/** * Intelligence Layer - LocalStorage Compactor Fallback * * Provides rule-based compaction for browser/dev environment. */ import type { CompactableMessage, CompactionResult, CompactionCheck, CompactionConfig } from '../intelligence-backend'; export const fallbackCompactor = { async estimateTokens(text: string): Promise { // Simple heuristic: ~4 chars per token for English, ~1.5 for CJK const cjkChars = (text.match(/[\u4e00-\u9fff\u3040-\u30ff]/g) ?? []).length; const otherChars = text.length - cjkChars; return Math.ceil(cjkChars * 1.5 + otherChars / 4); }, async estimateMessagesTokens(messages: CompactableMessage[]): Promise { let total = 0; for (const m of messages) { total += await fallbackCompactor.estimateTokens(m.content); } return total; }, async checkThreshold( messages: CompactableMessage[], config?: CompactionConfig ): Promise { const threshold = config?.soft_threshold_tokens ?? 15000; const currentTokens = await fallbackCompactor.estimateMessagesTokens(messages); return { should_compact: currentTokens >= threshold, current_tokens: currentTokens, threshold, urgency: currentTokens >= (config?.hard_threshold_tokens ?? 20000) ? 'hard' : currentTokens >= threshold ? 'soft' : 'none', }; }, async compact( messages: CompactableMessage[], _agentId: string, _conversationId?: string, config?: CompactionConfig ): Promise { // Simple rule-based compaction: keep last N messages const keepRecent = config?.keep_recent_messages ?? 10; const retained = messages.slice(-keepRecent); return { compacted_messages: retained, summary: `[Compacted ${messages.length - retained.length} earlier messages]`, original_count: messages.length, retained_count: retained.length, flushed_memories: 0, tokens_before_compaction: await fallbackCompactor.estimateMessagesTokens(messages), tokens_after_compaction: await fallbackCompactor.estimateMessagesTokens(retained), }; }, };