@ulrichc1/sparn 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/daemon/index.ts","../../src/daemon/session-watcher.ts","../../src/utils/context-parser.ts","../../src/utils/hash.ts","../../src/utils/tokenizer.ts","../../src/core/engram-scorer.ts","../../src/core/budget-pruner.ts","../../src/core/metrics.ts","../../src/core/incremental-optimizer.ts","../../src/core/context-pipeline.ts","../../src/daemon/file-tracker.ts"],"sourcesContent":["/**\n * Daemon Entry Point - Background process main loop\n *\n * This file is executed as a forked child process by daemon-process.ts.\n * It runs the session watcher and handles cleanup on exit.\n */\n\nimport { appendFileSync, existsSync, unlinkSync } from 'node:fs';\nimport type { SparnConfig } from '../types/config.js';\nimport { createSessionWatcher } from './session-watcher.js';\n\n// Parse config from environment\nconst configJson = process.env['SPARN_CONFIG'];\nconst pidFile = process.env['SPARN_PID_FILE'];\nconst logFile = process.env['SPARN_LOG_FILE'];\n\nif (!configJson || !pidFile || !logFile) {\n console.error('Daemon: Missing required environment variables');\n process.exit(1);\n}\n\nconst config: SparnConfig = JSON.parse(configJson);\n\n// Log helper\nfunction log(message: string): void {\n const timestamp = new Date().toISOString();\n const logMessage = `[${timestamp}] ${message}\\n`;\n\n // logFile is guaranteed to be defined after env check\n if (logFile) {\n try {\n appendFileSync(logFile, logMessage, 'utf-8');\n } catch {\n // Fail silently if can't write log\n }\n }\n}\n\n// Cleanup on exit\nfunction cleanup(): void {\n log('Daemon shutting down');\n\n // Remove PID file (pidFile is guaranteed to be defined after env check)\n if (pidFile && existsSync(pidFile)) {\n try {\n unlinkSync(pidFile);\n } catch {\n // Fail silently\n }\n }\n\n // Stop watcher\n watcher.stop();\n\n process.exit(0);\n}\n\n// Signal handlers\nprocess.on('SIGTERM', cleanup);\nprocess.on('SIGINT', cleanup);\nprocess.on('SIGHUP', cleanup);\n\n// Uncaught exception handler\nprocess.on('uncaughtException', (error) => {\n log(`Uncaught exception: ${error.message}`);\n cleanup();\n});\n\nprocess.on('unhandledRejection', (reason) => {\n log(`Unhandled rejection: ${reason}`);\n cleanup();\n});\n\n// Create and start session watcher\nlog('Daemon starting');\n\nconst watcher = createSessionWatcher({\n config,\n onOptimize: (sessionId, stats) => {\n log(\n `Optimized session ${sessionId}: ${stats.optimizedTokens} tokens (${Math.round(stats.reduction * 100)}% reduction)`,\n );\n },\n onError: (error) => {\n log(`Error: ${error.message}`);\n },\n});\n\n// Start watching\nwatcher\n .start()\n .then(() => {\n log('Daemon ready - watching Claude Code sessions');\n })\n .catch((error) => {\n log(`Failed to start: ${error.message}`);\n cleanup();\n });\n\n// Keep process alive\nsetInterval(() => {\n // Heartbeat (could be used for health checks)\n}, 60000); // Every minute\n","/**\n * Session Watcher - Monitor Claude Code session files for changes\n *\n * Uses Node.js fs.watch to monitor ~/.claude/projects/**\\/*.jsonl files.\n * Debounces events and triggers optimization when token threshold exceeded.\n * Maintains per-session ContextPipeline instances.\n */\n\nimport { type FSWatcher, readdirSync, statSync, watch } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { type ContextPipeline, createContextPipeline } from '../core/context-pipeline.js';\nimport { getMetrics } from '../core/metrics.js';\nimport type { SparnConfig } from '../types/config.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport { createFileTracker } from './file-tracker.js';\n\nexport interface SessionWatcherConfig {\n /** Sparn configuration */\n config: SparnConfig;\n /** Callback when optimization triggered */\n onOptimize?: (sessionId: string, stats: SessionStats) => void;\n /** Callback on error */\n onError?: (error: Error) => void;\n}\n\nexport interface SessionStats {\n /** Session ID */\n sessionId: string;\n /** Total tokens ingested */\n totalTokens: number;\n /** Current optimized tokens */\n optimizedTokens: number;\n /** Reduction percentage */\n reduction: number;\n /** Entry count */\n entryCount: number;\n /** Budget utilization */\n budgetUtilization: number;\n}\n\nexport interface SessionWatcher {\n /**\n * Start watching Claude Code session files\n * @returns Promise that resolves when watcher is ready\n */\n start(): Promise<void>;\n\n /**\n * Stop watching and cleanup\n */\n stop(): void;\n\n /**\n * Get statistics for all sessions\n * @returns Array of session stats\n */\n getStats(): SessionStats[];\n\n /**\n * Get statistics for a specific session\n * @param sessionId - Session ID\n * @returns Session stats or null if not found\n */\n getSessionStats(sessionId: string): SessionStats | null;\n\n /**\n * Manually trigger optimization for a session\n * @param sessionId - Session ID\n */\n optimizeSession(sessionId: string): void;\n}\n\n/**\n * Create a session watcher instance\n * @param config - Watcher configuration\n * @returns SessionWatcher instance\n */\nexport function createSessionWatcher(config: SessionWatcherConfig): SessionWatcher {\n const { config: sparnConfig, onOptimize, onError } = config;\n const { realtime, decay, states } = sparnConfig;\n\n // Per-session pipelines and trackers\n const pipelines = new Map<string, ContextPipeline>();\n const fileTracker = createFileTracker();\n\n // File system watchers\n const watchers: FSWatcher[] = [];\n\n // Debounce timers per file\n const debounceTimers = new Map<string, NodeJS.Timeout>();\n\n /**\n * Get Claude Code projects directory\n */\n function getProjectsDir(): string {\n return join(homedir(), '.claude', 'projects');\n }\n\n /**\n * Extract session ID from file path\n * Example: ~/.claude/projects/my-project/abc123.jsonl -> abc123\n */\n function getSessionId(filePath: string): string {\n const filename = filePath.split(/[/\\\\]/).pop() || '';\n return filename.replace(/\\.jsonl$/, '');\n }\n\n /**\n * Get or create pipeline for session\n */\n function getPipeline(sessionId: string): ContextPipeline {\n let pipeline = pipelines.get(sessionId);\n\n if (!pipeline) {\n pipeline = createContextPipeline({\n tokenBudget: realtime.tokenBudget,\n decay,\n states,\n windowSize: realtime.windowSize,\n fullOptimizationInterval: 50, // Full re-optimization every 50 incremental updates\n });\n pipelines.set(sessionId, pipeline);\n }\n\n return pipeline;\n }\n\n /**\n * Handle file change event (debounced)\n */\n function handleFileChange(filePath: string): void {\n // Clear existing timer\n const existingTimer = debounceTimers.get(filePath);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Set new debounced timer\n const timer = setTimeout(() => {\n try {\n // Read new lines from file\n const newLines = fileTracker.readNewLines(filePath);\n\n if (newLines.length === 0) return;\n\n // Parse JSONL content\n const content = newLines.join('\\n');\n const sessionId = getSessionId(filePath);\n const pipeline = getPipeline(sessionId);\n\n // Ingest into pipeline\n pipeline.ingest(content, { sessionId, filePath });\n\n // Check if we should trigger optimization\n const stats = pipeline.getStats();\n if (stats.currentTokens >= realtime.autoOptimizeThreshold) {\n // Update daemon metrics\n getMetrics().updateDaemon({\n sessionsWatched: pipelines.size,\n memoryUsage: process.memoryUsage().heapUsed,\n });\n\n // Trigger optimization callback\n if (onOptimize) {\n const sessionStats = computeSessionStats(sessionId, pipeline);\n onOptimize(sessionId, sessionStats);\n }\n }\n } catch (error) {\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n } finally {\n debounceTimers.delete(filePath);\n }\n }, realtime.debounceMs);\n\n debounceTimers.set(filePath, timer);\n }\n\n /**\n * Recursively find all JSONL files in directory\n */\n function findJsonlFiles(dir: string): string[] {\n const files: string[] = [];\n\n try {\n const entries = readdirSync(dir);\n\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n // Recurse into subdirectories\n files.push(...findJsonlFiles(fullPath));\n } else if (entry.endsWith('.jsonl')) {\n // Match pattern\n const matches = realtime.watchPatterns.some((pattern) => {\n // Simple glob matching (supports **/*.jsonl)\n const regex = new RegExp(\n pattern.replace(/\\*\\*/g, '.*').replace(/\\*/g, '[^/\\\\\\\\]*').replace(/\\./g, '\\\\.'),\n );\n return regex.test(fullPath);\n });\n\n if (matches) {\n files.push(fullPath);\n }\n }\n }\n } catch (_error) {\n // Ignore errors (directory might not exist yet)\n }\n\n return files;\n }\n\n /**\n * Compute session statistics\n */\n function computeSessionStats(sessionId: string, pipeline: ContextPipeline): SessionStats {\n const stats = pipeline.getStats();\n const entries = pipeline.getEntries();\n const totalTokens = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n return {\n sessionId,\n totalTokens: stats.totalIngested,\n optimizedTokens: stats.currentTokens,\n reduction: totalTokens > 0 ? (totalTokens - stats.currentTokens) / totalTokens : 0,\n entryCount: stats.currentEntries,\n budgetUtilization: stats.budgetUtilization,\n };\n }\n\n async function start(): Promise<void> {\n const projectsDir = getProjectsDir();\n\n // Find all existing JSONL files\n const jsonlFiles = findJsonlFiles(projectsDir);\n\n // Watch each file's parent directory (fs.watch is directory-based)\n const watchedDirs = new Set<string>();\n\n for (const file of jsonlFiles) {\n const dir = dirname(file);\n\n if (!watchedDirs.has(dir)) {\n const watcher = watch(dir, { recursive: false }, (_eventType, filename) => {\n if (filename?.endsWith('.jsonl')) {\n const fullPath = join(dir, filename);\n handleFileChange(fullPath);\n }\n });\n\n watchers.push(watcher);\n watchedDirs.add(dir);\n }\n }\n\n // Also watch projects directory for new subdirectories\n const projectsWatcher = watch(projectsDir, { recursive: true }, (_eventType, filename) => {\n if (filename?.endsWith('.jsonl')) {\n const fullPath = join(projectsDir, filename);\n handleFileChange(fullPath);\n }\n });\n\n watchers.push(projectsWatcher);\n\n // Update daemon metrics\n getMetrics().updateDaemon({\n startTime: Date.now(),\n sessionsWatched: jsonlFiles.length,\n memoryUsage: process.memoryUsage().heapUsed,\n });\n }\n\n function stop(): void {\n // Close all watchers\n for (const watcher of watchers) {\n watcher.close();\n }\n watchers.length = 0;\n\n // Clear all timers\n for (const timer of debounceTimers.values()) {\n clearTimeout(timer);\n }\n debounceTimers.clear();\n\n // Clear pipelines\n pipelines.clear();\n\n // Clear file tracker\n fileTracker.clearAll();\n }\n\n function getStats(): SessionStats[] {\n const stats: SessionStats[] = [];\n\n for (const [sessionId, pipeline] of pipelines.entries()) {\n stats.push(computeSessionStats(sessionId, pipeline));\n }\n\n return stats;\n }\n\n function getSessionStats(sessionId: string): SessionStats | null {\n const pipeline = pipelines.get(sessionId);\n if (!pipeline) return null;\n\n return computeSessionStats(sessionId, pipeline);\n }\n\n function optimizeSession(sessionId: string): void {\n const pipeline = pipelines.get(sessionId);\n if (!pipeline) return;\n\n // Get entries and force full optimization\n const entries = pipeline.getEntries();\n pipeline.clear();\n pipeline.ingest(entries.map((e) => e.content).join('\\n\\n'));\n\n // Trigger callback\n if (onOptimize) {\n const stats = computeSessionStats(sessionId, pipeline);\n onOptimize(sessionId, stats);\n }\n }\n\n return {\n start,\n stop,\n getStats,\n getSessionStats,\n optimizeSession,\n };\n}\n","/**\n * Context Parser - Shared utilities for parsing agent contexts into memory entries\n *\n * Extracted from claude-code adapter to enable reuse across:\n * - Adapters (claude-code, generic)\n * - Real-time pipeline (streaming context)\n * - Hooks (pre-prompt, post-tool-result)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { MemoryEntry } from '../types/memory.js';\nimport { hashContent } from './hash.js';\n\n/**\n * Block type classification for Claude Code context\n */\nexport type BlockType = 'conversation' | 'tool' | 'result' | 'other';\n\n/**\n * Parse Claude Code context into memory entries\n * Handles conversation turns, tool uses, and results\n * @param context - Raw context string\n * @returns Array of memory entries\n */\nexport function parseClaudeCodeContext(context: string): MemoryEntry[] {\n const entries: MemoryEntry[] = [];\n const now = Date.now();\n\n // Split by conversation turns and tool boundaries\n const lines = context.split('\\n');\n let currentBlock: string[] = [];\n let blockType: BlockType = 'other';\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Detect conversation turns\n if (trimmed.startsWith('User:') || trimmed.startsWith('Assistant:')) {\n if (currentBlock.length > 0) {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n currentBlock = [];\n }\n blockType = 'conversation';\n currentBlock.push(line);\n }\n // Detect tool calls\n else if (\n trimmed.includes('<function_calls>') ||\n trimmed.includes('<invoke>') ||\n trimmed.includes('<tool_use>')\n ) {\n if (currentBlock.length > 0) {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n currentBlock = [];\n }\n blockType = 'tool';\n currentBlock.push(line);\n }\n // Detect tool results\n else if (trimmed.includes('<function_results>') || trimmed.includes('</function_results>')) {\n if (currentBlock.length > 0 && blockType !== 'result') {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n currentBlock = [];\n }\n blockType = 'result';\n currentBlock.push(line);\n }\n // Continue current block\n else if (currentBlock.length > 0) {\n currentBlock.push(line);\n }\n // Start new block if line has content\n else if (trimmed.length > 0) {\n currentBlock.push(line);\n blockType = 'other';\n }\n }\n\n // Add final block\n if (currentBlock.length > 0) {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n }\n\n return entries.filter((e) => e.content.trim().length > 0);\n}\n\n/**\n * Create a memory entry from a content block\n * @param content - Block content\n * @param type - Block type\n * @param baseTime - Base timestamp\n * @returns Memory entry\n */\nexport function createEntry(content: string, type: BlockType, baseTime: number): MemoryEntry {\n const tags: string[] = [type];\n\n // Assign initial score based on type\n let initialScore = 0.5;\n if (type === 'conversation') initialScore = 0.8; // Prioritize conversation\n if (type === 'tool') initialScore = 0.7; // Tool calls are important\n if (type === 'result') initialScore = 0.4; // Results can be verbose\n\n return {\n id: randomUUID(),\n content,\n hash: hashContent(content),\n timestamp: baseTime,\n score: initialScore,\n state: initialScore > 0.7 ? 'active' : initialScore > 0.3 ? 'ready' : 'silent',\n ttl: 24 * 3600, // 24 hours default\n accessCount: 0,\n tags,\n metadata: { type },\n isBTSP: false,\n };\n}\n\n/**\n * Parse generic context (fallback for non-Claude-Code agents)\n * Splits on double newlines, treats as paragraphs\n * @param context - Raw context string\n * @returns Array of memory entries\n */\nexport function parseGenericContext(context: string): MemoryEntry[] {\n const entries: MemoryEntry[] = [];\n const now = Date.now();\n\n // Split on double newlines (paragraph boundaries)\n const blocks = context.split(/\\n\\n+/);\n\n for (const block of blocks) {\n const trimmed = block.trim();\n if (trimmed.length === 0) continue;\n\n entries.push(createEntry(trimmed, 'other', now));\n }\n\n return entries;\n}\n","/**\n * Content hashing utilities.\n * Uses SHA-256 for deduplication.\n */\n\nimport { createHash } from 'node:crypto';\n\n/**\n * Generate SHA-256 hash of content for deduplication.\n *\n * @param content - Content to hash\n * @returns 64-character hex string (SHA-256)\n *\n * @example\n * ```typescript\n * const hash = hashContent('Hello world');\n * console.log(hash.length); // 64\n * ```\n */\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n","/**\n * Token estimation utilities.\n * Uses whitespace heuristic (~90% accuracy vs GPT tokenizer).\n */\n\n/**\n * Estimate token count for text using heuristic.\n *\n * Approximation: 1 token ≈ 4 chars or 0.75 words\n * Provides ~90% accuracy compared to GPT tokenizer, sufficient for optimization heuristics.\n *\n * @param text - Text to count\n * @returns Estimated token count\n *\n * @example\n * ```typescript\n * const tokens = estimateTokens('Hello world');\n * console.log(tokens); // ~2\n * ```\n */\nexport function estimateTokens(text: string): number {\n if (!text || text.length === 0) {\n return 0;\n }\n\n // Split on whitespace to get words\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n const wordCount = words.length;\n\n // Character-based estimate\n const charCount = text.length;\n const charEstimate = Math.ceil(charCount / 4);\n\n // Word-based estimate\n const wordEstimate = Math.ceil(wordCount * 0.75);\n\n // Return the maximum of both estimates (more conservative)\n return Math.max(wordEstimate, charEstimate);\n}\n","/**\n * Engram Scorer - Implements engram theory (memory decay)\n *\n * Neuroscience: Memories fade over time without reinforcement.\n * Application: Apply exponential decay formula to memory scores based on age and access count.\n *\n * Formula: decay = 1 - e^(-age/TTL)\n * Score adjustment: score_new = score_old * (1 - decay) + (accessCount bonus)\n */\n\nimport type { MemoryEntry } from '../types/memory.js';\n\nexport interface EngramScorerConfig {\n /** Default TTL in hours for new entries */\n defaultTTL: number;\n /** Decay threshold (0.0-1.0) above which entries are marked for pruning */\n decayThreshold: number;\n}\n\nexport interface EngramScorer {\n /**\n * Calculate current score for an entry based on decay and access count\n * @param entry - Memory entry to score\n * @param currentTime - Current timestamp in milliseconds (for testing)\n * @returns Updated score (0.0-1.0)\n */\n calculateScore(entry: MemoryEntry, currentTime?: number): number;\n\n /**\n * Refresh TTL to default value\n * @param entry - Entry to refresh\n * @returns Entry with refreshed TTL and timestamp\n */\n refreshTTL(entry: MemoryEntry): MemoryEntry;\n\n /**\n * Calculate decay factor (0.0-1.0) based on age and TTL\n * @param ageInSeconds - Age of entry in seconds\n * @param ttlInSeconds - TTL in seconds\n * @returns Decay factor (0.0 = fresh, 1.0 = fully decayed)\n */\n calculateDecay(ageInSeconds: number, ttlInSeconds: number): number;\n}\n\n/**\n * Create an engram scorer instance\n * @param config - Scorer configuration\n * @returns EngramScorer instance\n */\nexport function createEngramScorer(config: EngramScorerConfig): EngramScorer {\n const { defaultTTL } = config;\n\n function calculateDecay(ageInSeconds: number, ttlInSeconds: number): number {\n if (ttlInSeconds === 0) return 1.0; // Instant decay\n if (ageInSeconds <= 0) return 0.0; // Fresh entry\n\n // Exponential decay: 1 - e^(-age/TTL)\n const ratio = ageInSeconds / ttlInSeconds;\n const decay = 1 - Math.exp(-ratio);\n\n // Clamp to [0.0, 1.0]\n return Math.max(0, Math.min(1, decay));\n }\n\n function calculateScore(entry: MemoryEntry, currentTime: number = Date.now()): number {\n // Calculate age in seconds\n const ageInMilliseconds = currentTime - entry.timestamp;\n const ageInSeconds = Math.max(0, ageInMilliseconds / 1000);\n\n // Calculate decay factor\n const decay = calculateDecay(ageInSeconds, entry.ttl);\n\n // Base score reduced by decay\n let score = entry.score * (1 - decay);\n\n // Access count bonus (diminishing returns via log)\n if (entry.accessCount > 0) {\n const accessBonus = Math.log(entry.accessCount + 1) * 0.1;\n score = Math.min(1.0, score + accessBonus);\n }\n\n // BTSP entries maintain high score\n if (entry.isBTSP) {\n score = Math.max(score, 0.9);\n }\n\n return Math.max(0, Math.min(1, score));\n }\n\n function refreshTTL(entry: MemoryEntry): MemoryEntry {\n return {\n ...entry,\n ttl: defaultTTL * 3600, // Convert hours to seconds\n timestamp: Date.now(),\n };\n }\n\n return {\n calculateScore,\n refreshTTL,\n calculateDecay,\n };\n}\n","/**\n * Budget-Aware Pruner - Token budget optimization\n *\n * Unlike SparsePruner which keeps top N% entries, BudgetPruner fits entries\n * within a target token budget using priority scoring that combines:\n * - TF-IDF relevance\n * - Engram decay\n * - Confidence state multipliers\n * - BTSP bypass (always included)\n *\n * Target use case: Real-time optimization for Opus model (~50K token budget)\n */\n\nimport type { RealtimeConfig } from '../types/config.js';\nimport type { MemoryEntry } from '../types/memory.js';\nimport type { PruneResult } from '../types/pruner.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport { createEngramScorer } from './engram-scorer.js';\n\nexport interface BudgetPrunerConfig {\n /** Target token budget */\n tokenBudget: number;\n /** Decay configuration */\n decay: {\n defaultTTL: number;\n decayThreshold: number;\n };\n /** State multipliers */\n states: {\n activeThreshold: number;\n readyThreshold: number;\n };\n}\n\nexport interface BudgetPruner {\n /**\n * Prune entries to fit within token budget\n * @param entries - Memory entries to prune\n * @param budget - Optional override budget (uses config default if not provided)\n * @returns Result with kept/removed entries and budget utilization\n */\n pruneToFit(entries: MemoryEntry[], budget?: number): PruneResult & { budgetUtilization: number };\n\n /**\n * Calculate priority score for an entry\n * @param entry - Entry to score\n * @param allEntries - All entries for TF-IDF calculation\n * @returns Priority score (higher = more important)\n */\n priorityScore(entry: MemoryEntry, allEntries: MemoryEntry[]): number;\n}\n\n/**\n * Create a budget-aware pruner instance\n * @param config - Pruner configuration\n * @returns BudgetPruner instance\n */\nexport function createBudgetPruner(config: BudgetPrunerConfig): BudgetPruner {\n const { tokenBudget, decay } = config;\n const engramScorer = createEngramScorer(decay);\n\n function tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length > 0);\n }\n\n function calculateTF(term: string, tokens: string[]): number {\n const count = tokens.filter((t) => t === term).length;\n // Sqrt capping to prevent common words from dominating\n return Math.sqrt(count);\n }\n\n function calculateIDF(term: string, allEntries: MemoryEntry[]): number {\n const totalDocs = allEntries.length;\n const docsWithTerm = allEntries.filter((entry) => {\n const tokens = tokenize(entry.content);\n return tokens.includes(term);\n }).length;\n\n if (docsWithTerm === 0) return 0;\n\n return Math.log(totalDocs / docsWithTerm);\n }\n\n function calculateTFIDF(entry: MemoryEntry, allEntries: MemoryEntry[]): number {\n const tokens = tokenize(entry.content);\n if (tokens.length === 0) return 0;\n\n const uniqueTerms = [...new Set(tokens)];\n let totalScore = 0;\n\n for (const term of uniqueTerms) {\n const tf = calculateTF(term, tokens);\n const idf = calculateIDF(term, allEntries);\n totalScore += tf * idf;\n }\n\n // Normalize by entry length\n return totalScore / tokens.length;\n }\n\n function getStateMultiplier(entry: MemoryEntry): number {\n // BTSP entries get max priority (handled separately, but keep high multiplier)\n if (entry.isBTSP) return 2.0;\n\n // State-based multipliers\n switch (entry.state) {\n case 'active':\n return 2.0;\n case 'ready':\n return 1.0;\n case 'silent':\n return 0.5;\n default:\n return 1.0;\n }\n }\n\n function priorityScore(entry: MemoryEntry, allEntries: MemoryEntry[]): number {\n const tfidf = calculateTFIDF(entry, allEntries);\n const currentScore = engramScorer.calculateScore(entry);\n const engramDecay = 1 - currentScore; // Lower decay = higher priority\n const stateMultiplier = getStateMultiplier(entry);\n\n // Priority = TF-IDF * (1 - decay) * state_multiplier\n // This balances relevance, recency, and confidence state\n return tfidf * (1 - engramDecay) * stateMultiplier;\n }\n\n function pruneToFit(\n entries: MemoryEntry[],\n budget: number = tokenBudget,\n ): PruneResult & { budgetUtilization: number } {\n if (entries.length === 0) {\n return {\n kept: [],\n removed: [],\n originalTokens: 0,\n prunedTokens: 0,\n budgetUtilization: 0,\n };\n }\n\n // Calculate original token count\n const originalTokens = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Step 1: Separate BTSP entries (always included, bypass budget)\n const btspEntries = entries.filter((e) => e.isBTSP);\n const regularEntries = entries.filter((e) => !e.isBTSP);\n\n const btspTokens = btspEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Step 2: Score regular entries\n const scored = regularEntries.map((entry) => ({\n entry,\n score: priorityScore(entry, entries),\n tokens: estimateTokens(entry.content),\n }));\n\n // Step 3: Sort by priority score descending\n scored.sort((a, b) => b.score - a.score);\n\n // Step 4: Greedy fill until budget exceeded\n const kept: MemoryEntry[] = [...btspEntries];\n const removed: MemoryEntry[] = [];\n let currentTokens = btspTokens;\n\n for (const item of scored) {\n if (currentTokens + item.tokens <= budget) {\n kept.push(item.entry);\n currentTokens += item.tokens;\n } else {\n removed.push(item.entry);\n }\n }\n\n const budgetUtilization = budget > 0 ? currentTokens / budget : 0;\n\n return {\n kept,\n removed,\n originalTokens,\n prunedTokens: currentTokens,\n budgetUtilization,\n };\n }\n\n return {\n pruneToFit,\n priorityScore,\n };\n}\n\n/**\n * Helper to create budget pruner from RealtimeConfig\n * @param realtimeConfig - Realtime configuration\n * @param decayConfig - Decay configuration\n * @param statesConfig - States configuration\n * @returns BudgetPruner instance\n */\nexport function createBudgetPrunerFromConfig(\n realtimeConfig: RealtimeConfig,\n decayConfig: { defaultTTL: number; decayThreshold: number },\n statesConfig: { activeThreshold: number; readyThreshold: number },\n): BudgetPruner {\n return createBudgetPruner({\n tokenBudget: realtimeConfig.tokenBudget,\n decay: decayConfig,\n states: statesConfig,\n });\n}\n","/**\n * Metrics and Telemetry System\n *\n * Tracks performance metrics and optimization statistics:\n * - Optimization duration and throughput\n * - Token savings and reduction rates\n * - Memory usage and cache hit rates\n * - Daemon uptime and session counts\n */\n\nexport interface OptimizationMetric {\n timestamp: number;\n duration: number;\n tokensBefore: number;\n tokensAfter: number;\n entriesProcessed: number;\n entriesKept: number;\n cacheHitRate: number;\n memoryUsage: number;\n}\n\nexport interface DaemonMetric {\n startTime: number;\n sessionsWatched: number;\n totalOptimizations: number;\n totalTokensSaved: number;\n averageLatency: number;\n memoryUsage: number;\n}\n\nexport interface MetricsSnapshot {\n timestamp: number;\n optimization: {\n totalRuns: number;\n totalDuration: number;\n totalTokensSaved: number;\n averageReduction: number;\n p50Latency: number;\n p95Latency: number;\n p99Latency: number;\n };\n cache: {\n hitRate: number;\n totalHits: number;\n totalMisses: number;\n size: number;\n };\n daemon: {\n uptime: number;\n sessionsWatched: number;\n memoryUsage: number;\n };\n}\n\nexport interface MetricsCollector {\n /**\n * Record an optimization metric\n */\n recordOptimization(metric: OptimizationMetric): void;\n\n /**\n * Update daemon metrics\n */\n updateDaemon(metric: Partial<DaemonMetric>): void;\n\n /**\n * Get current metrics snapshot\n */\n getSnapshot(): MetricsSnapshot;\n\n /**\n * Export metrics as JSON\n */\n export(): string;\n\n /**\n * Reset all metrics\n */\n reset(): void;\n}\n\n/**\n * Create a metrics collector instance\n */\nexport function createMetricsCollector(): MetricsCollector {\n const optimizations: OptimizationMetric[] = [];\n let daemonMetrics: DaemonMetric = {\n startTime: Date.now(),\n sessionsWatched: 0,\n totalOptimizations: 0,\n totalTokensSaved: 0,\n averageLatency: 0,\n memoryUsage: 0,\n };\n\n let cacheHits = 0;\n let cacheMisses = 0;\n\n function recordOptimization(metric: OptimizationMetric): void {\n optimizations.push(metric);\n\n // Update daemon totals\n daemonMetrics.totalOptimizations++;\n daemonMetrics.totalTokensSaved += metric.tokensBefore - metric.tokensAfter;\n\n // Update cache stats\n if (metric.cacheHitRate > 0) {\n const hits = Math.round(metric.entriesProcessed * metric.cacheHitRate);\n cacheHits += hits;\n cacheMisses += metric.entriesProcessed - hits;\n }\n\n // Update average latency (moving average)\n daemonMetrics.averageLatency =\n (daemonMetrics.averageLatency * (daemonMetrics.totalOptimizations - 1) + metric.duration) /\n daemonMetrics.totalOptimizations;\n\n // Keep only last 1000 metrics in memory\n if (optimizations.length > 1000) {\n optimizations.shift();\n }\n }\n\n function updateDaemon(metric: Partial<DaemonMetric>): void {\n daemonMetrics = {\n ...daemonMetrics,\n ...metric,\n };\n }\n\n function calculatePercentile(values: number[], percentile: number): number {\n if (values.length === 0) return 0;\n\n const sorted = [...values].sort((a, b) => a - b);\n const index = Math.ceil((percentile / 100) * sorted.length) - 1;\n return sorted[index] || 0;\n }\n\n function getSnapshot(): MetricsSnapshot {\n const totalRuns = optimizations.length;\n const totalDuration = optimizations.reduce((sum, m) => sum + m.duration, 0);\n const totalTokensSaved = optimizations.reduce(\n (sum, m) => sum + (m.tokensBefore - m.tokensAfter),\n 0,\n );\n\n const totalTokensBefore = optimizations.reduce((sum, m) => sum + m.tokensBefore, 0);\n const averageReduction = totalTokensBefore > 0 ? totalTokensSaved / totalTokensBefore : 0;\n\n const durations = optimizations.map((m) => m.duration);\n\n const totalCacheQueries = cacheHits + cacheMisses;\n const hitRate = totalCacheQueries > 0 ? cacheHits / totalCacheQueries : 0;\n\n return {\n timestamp: Date.now(),\n optimization: {\n totalRuns,\n totalDuration,\n totalTokensSaved,\n averageReduction,\n p50Latency: calculatePercentile(durations, 50),\n p95Latency: calculatePercentile(durations, 95),\n p99Latency: calculatePercentile(durations, 99),\n },\n cache: {\n hitRate,\n totalHits: cacheHits,\n totalMisses: cacheMisses,\n size: optimizations.reduce((sum, m) => sum + m.entriesKept, 0),\n },\n daemon: {\n uptime: Date.now() - daemonMetrics.startTime,\n sessionsWatched: daemonMetrics.sessionsWatched,\n memoryUsage: daemonMetrics.memoryUsage,\n },\n };\n }\n\n function exportMetrics(): string {\n return JSON.stringify(getSnapshot(), null, 2);\n }\n\n function reset(): void {\n optimizations.length = 0;\n cacheHits = 0;\n cacheMisses = 0;\n daemonMetrics = {\n startTime: Date.now(),\n sessionsWatched: 0,\n totalOptimizations: 0,\n totalTokensSaved: 0,\n averageLatency: 0,\n memoryUsage: 0,\n };\n }\n\n return {\n recordOptimization,\n updateDaemon,\n getSnapshot,\n export: exportMetrics,\n reset,\n };\n}\n\n// Global metrics instance\nlet globalMetrics: MetricsCollector | null = null;\n\n/**\n * Get or create the global metrics collector\n */\nexport function getMetrics(): MetricsCollector {\n if (!globalMetrics) {\n globalMetrics = createMetricsCollector();\n }\n return globalMetrics;\n}\n","/**\n * Incremental Optimizer - Cache-based delta processing\n *\n * Optimizes performance for real-time scenarios by:\n * - Caching entry scores by content hash\n * - Only recomputing scores for new/changed entries\n * - Pre-computing and caching document frequency tables\n * - Periodically forcing full re-optimization to prevent drift\n *\n * Target: <50ms for incremental updates on 100K token contexts\n */\n\nimport type { MemoryEntry } from '../types/memory.js';\nimport type { PruneResult } from '../types/pruner.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport { type BudgetPrunerConfig, createBudgetPruner } from './budget-pruner.js';\nimport { getMetrics } from './metrics.js';\n\nexport interface IncrementalOptimizerConfig extends BudgetPrunerConfig {\n /** Force full re-optimization every N incremental updates */\n fullOptimizationInterval: number;\n}\n\nexport interface IncrementalOptimizerState {\n /** Entry cache keyed by content hash */\n entryCache: Map<string, { entry: MemoryEntry; score: number; timestamp: number }>;\n /** Document frequency table for IDF calculation */\n documentFrequency: Map<string, number>;\n /** Total document count for IDF */\n totalDocuments: number;\n /** Incremental update counter */\n updateCount: number;\n /** Last full optimization timestamp */\n lastFullOptimization: number;\n}\n\nexport interface IncrementalOptimizer {\n /**\n * Optimize incrementally (only process new/changed entries)\n * @param newEntries - New entries to add\n * @param budget - Optional budget override\n * @returns Prune result with budget utilization\n */\n optimizeIncremental(\n newEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number };\n\n /**\n * Optimize fully (recompute all scores)\n * @param allEntries - All entries to optimize\n * @param budget - Optional budget override\n * @returns Prune result with budget utilization\n */\n optimizeFull(\n allEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number };\n\n /**\n * Get current optimizer state (for serialization)\n * @returns Serializable state object\n */\n getState(): IncrementalOptimizerState;\n\n /**\n * Restore optimizer state (from serialization)\n * @param state - State to restore\n */\n restoreState(state: IncrementalOptimizerState): void;\n\n /**\n * Reset optimizer state (clear all caches)\n */\n reset(): void;\n\n /**\n * Get cache statistics\n * @returns Cache stats\n */\n getStats(): {\n cachedEntries: number;\n uniqueTerms: number;\n totalDocuments: number;\n updateCount: number;\n lastFullOptimization: number;\n };\n}\n\n/**\n * Create an incremental optimizer instance\n * @param config - Optimizer configuration\n * @returns IncrementalOptimizer instance\n */\nexport function createIncrementalOptimizer(\n config: IncrementalOptimizerConfig,\n): IncrementalOptimizer {\n const pruner = createBudgetPruner(config);\n const { fullOptimizationInterval } = config;\n\n // Internal state\n let state: IncrementalOptimizerState = {\n entryCache: new Map(),\n documentFrequency: new Map(),\n totalDocuments: 0,\n updateCount: 0,\n lastFullOptimization: Date.now(),\n };\n\n function tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length > 0);\n }\n\n /**\n * Update document frequency table incrementally\n */\n function updateDocumentFrequency(entries: MemoryEntry[], remove = false): void {\n for (const entry of entries) {\n const tokens = tokenize(entry.content);\n const uniqueTerms = [...new Set(tokens)];\n\n for (const term of uniqueTerms) {\n const current = state.documentFrequency.get(term) || 0;\n const updated = remove ? Math.max(0, current - 1) : current + 1;\n\n if (updated === 0) {\n state.documentFrequency.delete(term);\n } else {\n state.documentFrequency.set(term, updated);\n }\n }\n }\n\n state.totalDocuments += remove ? -entries.length : entries.length;\n state.totalDocuments = Math.max(0, state.totalDocuments);\n }\n\n /**\n * Check if entry is cached and still valid\n */\n function getCachedEntry(hash: string): MemoryEntry | null {\n const cached = state.entryCache.get(hash);\n if (!cached) return null;\n\n // Entry is valid if found in cache\n return cached.entry;\n }\n\n /**\n * Cache entry with score\n */\n function cacheEntry(entry: MemoryEntry, score: number): void {\n state.entryCache.set(entry.hash, {\n entry,\n score,\n timestamp: Date.now(),\n });\n }\n\n function optimizeIncremental(\n newEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number } {\n const startTime = Date.now();\n state.updateCount++;\n\n // Force full optimization if interval reached\n if (state.updateCount >= fullOptimizationInterval) {\n // Get all cached entries\n const allEntries = Array.from(state.entryCache.values()).map((c) => c.entry);\n return optimizeFull([...allEntries, ...newEntries], budget);\n }\n\n // Filter out already-cached entries\n const uncachedEntries: MemoryEntry[] = [];\n const cachedEntries: MemoryEntry[] = [];\n\n for (const entry of newEntries) {\n const cached = getCachedEntry(entry.hash);\n if (cached) {\n cachedEntries.push(cached);\n } else {\n uncachedEntries.push(entry);\n }\n }\n\n // Update document frequency for new entries only\n if (uncachedEntries.length > 0) {\n updateDocumentFrequency(uncachedEntries, false);\n }\n\n // Combine with cached entries for scoring context\n const allEntries = [...cachedEntries, ...uncachedEntries];\n\n // Score only uncached entries (reuse cached scores)\n for (const entry of uncachedEntries) {\n const score = pruner.priorityScore(entry, allEntries);\n cacheEntry(entry, score);\n }\n\n // Get all current entries (from cache + new)\n const currentEntries = Array.from(state.entryCache.values()).map((c) => c.entry);\n\n // Calculate tokens before\n const tokensBefore = currentEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Prune to fit budget\n const result = pruner.pruneToFit(currentEntries, budget);\n\n // Calculate tokens after\n const tokensAfter = result.kept.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Update cache: remove pruned entries\n for (const removed of result.removed) {\n state.entryCache.delete(removed.hash);\n }\n\n // Update document frequency to reflect removal\n if (result.removed.length > 0) {\n updateDocumentFrequency(result.removed, true);\n }\n\n // Record metrics\n const duration = Date.now() - startTime;\n const cacheHitRate = newEntries.length > 0 ? cachedEntries.length / newEntries.length : 0;\n\n getMetrics().recordOptimization({\n timestamp: Date.now(),\n duration,\n tokensBefore,\n tokensAfter,\n entriesProcessed: newEntries.length,\n entriesKept: result.kept.length,\n cacheHitRate,\n memoryUsage: process.memoryUsage().heapUsed,\n });\n\n return result;\n }\n\n function optimizeFull(\n allEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number } {\n const startTime = Date.now();\n\n // Calculate tokens before\n const tokensBefore = allEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Reset state\n state.entryCache.clear();\n state.documentFrequency.clear();\n state.totalDocuments = 0;\n state.updateCount = 0;\n state.lastFullOptimization = Date.now();\n\n // Rebuild document frequency table\n updateDocumentFrequency(allEntries, false);\n\n // Score and cache all entries\n for (const entry of allEntries) {\n const score = pruner.priorityScore(entry, allEntries);\n cacheEntry(entry, score);\n }\n\n // Prune to fit budget\n const result = pruner.pruneToFit(allEntries, budget);\n\n // Calculate tokens after\n const tokensAfter = result.kept.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Update cache: remove pruned entries\n for (const removed of result.removed) {\n state.entryCache.delete(removed.hash);\n }\n\n // Update document frequency to reflect removal\n if (result.removed.length > 0) {\n updateDocumentFrequency(result.removed, true);\n }\n\n // Record metrics\n const duration = Date.now() - startTime;\n\n getMetrics().recordOptimization({\n timestamp: Date.now(),\n duration,\n tokensBefore,\n tokensAfter,\n entriesProcessed: allEntries.length,\n entriesKept: result.kept.length,\n cacheHitRate: 0, // Full optimization has no cache hits\n memoryUsage: process.memoryUsage().heapUsed,\n });\n\n return result;\n }\n\n function getState(): IncrementalOptimizerState {\n return {\n entryCache: new Map(state.entryCache),\n documentFrequency: new Map(state.documentFrequency),\n totalDocuments: state.totalDocuments,\n updateCount: state.updateCount,\n lastFullOptimization: state.lastFullOptimization,\n };\n }\n\n function restoreState(restoredState: IncrementalOptimizerState): void {\n state = {\n entryCache: new Map(restoredState.entryCache),\n documentFrequency: new Map(restoredState.documentFrequency),\n totalDocuments: restoredState.totalDocuments,\n updateCount: restoredState.updateCount,\n lastFullOptimization: restoredState.lastFullOptimization,\n };\n }\n\n function reset(): void {\n state = {\n entryCache: new Map(),\n documentFrequency: new Map(),\n totalDocuments: 0,\n updateCount: 0,\n lastFullOptimization: Date.now(),\n };\n }\n\n function getStats() {\n return {\n cachedEntries: state.entryCache.size,\n uniqueTerms: state.documentFrequency.size,\n totalDocuments: state.totalDocuments,\n updateCount: state.updateCount,\n lastFullOptimization: state.lastFullOptimization,\n };\n }\n\n return {\n optimizeIncremental,\n optimizeFull,\n getState,\n restoreState,\n reset,\n getStats,\n };\n}\n","/**\n * Streaming Context Pipeline - Real-time sliding window buffer\n *\n * Maintains an optimized context in real-time by:\n * - Ingesting new content as it arrives\n * - Storing entries by priority internally (for eviction decisions)\n * - Outputting in chronological order (for conversation coherence)\n * - Evicting lowest-priority entries when budget exceeded\n * - Using IncrementalOptimizer for fast delta processing\n */\n\nimport type { MemoryEntry } from '../types/memory.js';\nimport { parseClaudeCodeContext } from '../utils/context-parser.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport {\n createIncrementalOptimizer,\n type IncrementalOptimizerConfig,\n} from './incremental-optimizer.js';\n\nexport interface ContextPipelineConfig extends IncrementalOptimizerConfig {\n /** Sliding window size (max entries to keep) */\n windowSize: number;\n}\n\nexport interface ContextPipelineStats {\n /** Total entries ingested */\n totalIngested: number;\n /** Current entry count */\n currentEntries: number;\n /** Current token count */\n currentTokens: number;\n /** Budget utilization (0.0-1.0) */\n budgetUtilization: number;\n /** Evicted entry count */\n evictedEntries: number;\n /** Optimizer stats */\n optimizer: {\n cachedEntries: number;\n uniqueTerms: number;\n updateCount: number;\n };\n}\n\nexport interface ContextPipeline {\n /**\n * Ingest new content into the pipeline\n * @param content - Raw content string\n * @param metadata - Optional metadata to attach to entries\n * @returns Number of entries ingested\n */\n ingest(content: string, metadata?: Record<string, unknown>): number;\n\n /**\n * Get current optimized context (chronologically ordered)\n * @returns Optimized context string\n */\n getContext(): string;\n\n /**\n * Get current entries (chronologically ordered)\n * @returns Array of memory entries\n */\n getEntries(): MemoryEntry[];\n\n /**\n * Get pipeline statistics\n * @returns Pipeline stats\n */\n getStats(): ContextPipelineStats;\n\n /**\n * Clear all entries and reset state\n */\n clear(): void;\n}\n\n/**\n * Create a context pipeline instance\n * @param config - Pipeline configuration\n * @returns ContextPipeline instance\n */\nexport function createContextPipeline(config: ContextPipelineConfig): ContextPipeline {\n const optimizer = createIncrementalOptimizer(config);\n const { windowSize, tokenBudget } = config;\n\n // Internal state\n let totalIngested = 0;\n let evictedEntries = 0;\n let currentEntries: MemoryEntry[] = [];\n let budgetUtilization = 0;\n\n function ingest(content: string, metadata: Record<string, unknown> = {}): number {\n // Parse content into entries\n const newEntries = parseClaudeCodeContext(content);\n\n if (newEntries.length === 0) return 0;\n\n // Attach metadata to entries\n const entriesWithMetadata = newEntries.map((entry) => ({\n ...entry,\n metadata: { ...entry.metadata, ...metadata },\n }));\n\n // Optimize incrementally\n const result = optimizer.optimizeIncremental(entriesWithMetadata, tokenBudget);\n\n // Update statistics\n totalIngested += newEntries.length;\n evictedEntries += result.removed.length;\n currentEntries = result.kept;\n budgetUtilization = result.budgetUtilization;\n\n // Enforce window size limit (keep most recent if exceeded)\n if (currentEntries.length > windowSize) {\n // Sort by timestamp descending (newest first)\n const sorted = [...currentEntries].sort((a, b) => b.timestamp - a.timestamp);\n const toKeep = sorted.slice(0, windowSize);\n const toRemove = sorted.slice(windowSize);\n\n currentEntries = toKeep;\n evictedEntries += toRemove.length;\n }\n\n return newEntries.length;\n }\n\n function getContext(): string {\n // Sort entries chronologically (oldest first)\n const sorted = [...currentEntries].sort((a, b) => a.timestamp - b.timestamp);\n return sorted.map((e) => e.content).join('\\n\\n');\n }\n\n function getEntries(): MemoryEntry[] {\n // Return entries chronologically (oldest first)\n return [...currentEntries].sort((a, b) => a.timestamp - b.timestamp);\n }\n\n function getStats(): ContextPipelineStats {\n const optimizerStats = optimizer.getStats();\n const currentTokens = currentEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n return {\n totalIngested,\n currentEntries: currentEntries.length,\n currentTokens,\n budgetUtilization,\n evictedEntries,\n optimizer: {\n cachedEntries: optimizerStats.cachedEntries,\n uniqueTerms: optimizerStats.uniqueTerms,\n updateCount: optimizerStats.updateCount,\n },\n };\n }\n\n function clear(): void {\n totalIngested = 0;\n evictedEntries = 0;\n currentEntries = [];\n budgetUtilization = 0;\n optimizer.reset();\n }\n\n return {\n ingest,\n getContext,\n getEntries,\n getStats,\n clear,\n };\n}\n","/**\n * File Tracker - Incremental file reading with byte position tracking\n *\n * Tracks read positions for files to enable efficient incremental reading.\n * Handles JSONL partial line buffering for incomplete writes.\n *\n * Use case: Monitor Claude Code session JSONL files and only read new lines\n * as they're appended, without re-reading the entire file.\n */\n\nimport { readFileSync, statSync } from 'node:fs';\n\nexport interface FilePosition {\n /** File path */\n path: string;\n /** Last read byte position */\n position: number;\n /** Partial line buffer (for JSONL incomplete writes) */\n partialLine: string;\n /** Last modification time */\n lastModified: number;\n /** File size at last read */\n lastSize: number;\n}\n\nexport interface FileTracker {\n /**\n * Read new content from file since last read\n * @param filePath - File to read\n * @returns New content as array of lines (empty if no new content)\n */\n readNewLines(filePath: string): string[];\n\n /**\n * Get current position for a file\n * @param filePath - File path\n * @returns File position or null if not tracked\n */\n getPosition(filePath: string): FilePosition | null;\n\n /**\n * Reset position for a file (start from beginning on next read)\n * @param filePath - File path\n */\n resetPosition(filePath: string): void;\n\n /**\n * Clear all tracked positions\n */\n clearAll(): void;\n\n /**\n * Get all tracked file paths\n * @returns Array of tracked file paths\n */\n getTrackedFiles(): string[];\n}\n\n/**\n * Create a file tracker instance\n * @returns FileTracker instance\n */\nexport function createFileTracker(): FileTracker {\n // Track positions by file path\n const positions = new Map<string, FilePosition>();\n\n function readNewLines(filePath: string): string[] {\n try {\n // Get current file stats\n const stats = statSync(filePath);\n const currentSize = stats.size;\n const currentModified = stats.mtimeMs;\n\n // Get or initialize position\n let pos = positions.get(filePath);\n\n if (!pos) {\n // First read: start from beginning\n pos = {\n path: filePath,\n position: 0,\n partialLine: '',\n lastModified: currentModified,\n lastSize: 0,\n };\n positions.set(filePath, pos);\n }\n\n // Check if file was truncated or is same size\n if (currentSize < pos.lastSize || currentSize === pos.position) {\n // File truncated or no new content\n if (currentSize < pos.lastSize) {\n // Reset position if truncated\n pos.position = 0;\n pos.partialLine = '';\n }\n return [];\n }\n\n // Read new content from last position\n const buffer = Buffer.alloc(currentSize - pos.position);\n const fd = readFileSync(filePath);\n fd.copy(buffer, 0, pos.position, currentSize);\n\n // Convert to string and combine with partial line\n const newContent = (pos.partialLine + buffer.toString('utf-8')).split('\\n');\n\n // Last element might be incomplete (no trailing newline yet)\n const partialLine = newContent.pop() || '';\n\n // Update position\n pos.position = currentSize;\n pos.partialLine = partialLine;\n pos.lastModified = currentModified;\n pos.lastSize = currentSize;\n\n // Return complete lines (filter empty)\n return newContent.filter((line) => line.trim().length > 0);\n } catch (_error) {\n // File doesn't exist or can't be read\n // Return empty array (fail silently for watcher use case)\n return [];\n }\n }\n\n function getPosition(filePath: string): FilePosition | null {\n return positions.get(filePath) || null;\n }\n\n function resetPosition(filePath: string): void {\n positions.delete(filePath);\n }\n\n function clearAll(): void {\n positions.clear();\n }\n\n function getTrackedFiles(): string[] {\n return Array.from(positions.keys());\n }\n\n return {\n readNewLines,\n getPosition,\n resetPosition,\n clearAll,\n getTrackedFiles,\n };\n}\n"],"mappings":"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","names":["statSync","config","config","config","allEntries","config","config","statSync","watcher"]}
1
+ {"version":3,"sources":["../../src/daemon/index.ts","../../src/core/kv-memory.ts","../../src/daemon/consolidation-scheduler.ts","../../src/core/engram-scorer.ts","../../src/core/sleep-compressor.ts","../../src/cli/commands/consolidate.ts","../../src/core/metrics.ts","../../src/daemon/session-watcher.ts","../../src/utils/context-parser.ts","../../src/utils/hash.ts","../../src/utils/tokenizer.ts","../../src/core/budget-pruner.ts","../../src/core/incremental-optimizer.ts","../../src/core/context-pipeline.ts","../../src/daemon/file-tracker.ts"],"sourcesContent":["/**\n * Daemon Entry Point - Background process main loop\n *\n * This file is executed as a forked child process by daemon-process.ts.\n * It runs the session watcher and handles cleanup on exit.\n */\n\nimport { appendFileSync, existsSync, unlinkSync } from 'node:fs';\nimport { createKVMemory } from '../core/kv-memory.js';\nimport type { SparnConfig } from '../types/config.js';\nimport { createConsolidationScheduler } from './consolidation-scheduler.js';\nimport { createSessionWatcher } from './session-watcher.js';\n\n// Parse config from environment\nconst configJson = process.env['SPARN_CONFIG'];\nconst pidFile = process.env['SPARN_PID_FILE'];\nconst logFile = process.env['SPARN_LOG_FILE'];\n\nif (!configJson || !pidFile || !logFile) {\n console.error('Daemon: Missing required environment variables');\n process.exit(1);\n}\n\nconst config: SparnConfig = JSON.parse(configJson);\n\n// Log helper\nfunction log(message: string): void {\n const timestamp = new Date().toISOString();\n const logMessage = `[${timestamp}] ${message}\\n`;\n\n // logFile is guaranteed to be defined after env check\n if (logFile) {\n try {\n appendFileSync(logFile, logMessage, 'utf-8');\n } catch {\n // Fail silently if can't write log\n }\n }\n}\n\n// Cleanup on exit\nfunction cleanup(): void {\n log('Daemon shutting down');\n\n // Remove PID file (pidFile is guaranteed to be defined after env check)\n if (pidFile && existsSync(pidFile)) {\n try {\n unlinkSync(pidFile);\n } catch {\n // Fail silently\n }\n }\n\n // Stop scheduler\n if (scheduler) {\n scheduler.stop();\n }\n\n // Stop watcher\n watcher.stop();\n\n // Close memory connection\n if (memory) {\n void memory.close();\n }\n\n process.exit(0);\n}\n\n// Signal handlers\nprocess.on('SIGTERM', cleanup);\nprocess.on('SIGINT', cleanup);\nprocess.on('SIGHUP', cleanup);\n\n// Uncaught exception handler\nprocess.on('uncaughtException', (error) => {\n log(`Uncaught exception: ${error.message}`);\n cleanup();\n});\n\nprocess.on('unhandledRejection', (reason) => {\n log(`Unhandled rejection: ${reason}`);\n cleanup();\n});\n\n// Create memory instance for consolidation scheduler\n// Using a default path - could be made configurable in future\nlet memory: Awaited<ReturnType<typeof createKVMemory>> | null = null;\nlet scheduler: ReturnType<typeof createConsolidationScheduler> | null = null;\n\n// Create and start session watcher\nlog('Daemon starting');\n\nconst watcher = createSessionWatcher({\n config,\n onOptimize: (sessionId, stats) => {\n log(\n `Optimized session ${sessionId}: ${stats.optimizedTokens} tokens (${Math.round(stats.reduction * 100)}% reduction)`,\n );\n },\n onError: (error) => {\n log(`Error: ${error.message}`);\n },\n});\n\n// Start watching\nwatcher\n .start()\n .then(async () => {\n log('Daemon ready - watching Claude Code sessions');\n\n // Initialize consolidation scheduler if enabled\n if (\n config.realtime.consolidationInterval !== null &&\n config.realtime.consolidationInterval > 0\n ) {\n try {\n // Create memory instance\n memory = await createKVMemory('.sparn/memory.db');\n\n // Create and start scheduler\n scheduler = createConsolidationScheduler({\n memory,\n config,\n logFile,\n });\n\n scheduler.start();\n log(\n `Consolidation scheduler started (interval: ${config.realtime.consolidationInterval}h)`,\n );\n } catch (error) {\n log(\n `Failed to start consolidation scheduler: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n })\n .catch((error) => {\n log(`Failed to start: ${error.message}`);\n cleanup();\n });\n\n// Keep process alive\nsetInterval(() => {\n // Heartbeat (could be used for health checks)\n}, 60000); // Every minute\n","/**\n * KV Memory Store Module\n * Implements hippocampal key-value storage with dual index/value tables.\n * Maps to: Hippocampal Key-Value — the hippocampus separates what to store from how to retrieve it.\n */\n\nimport { copyFileSync, existsSync } from 'node:fs';\nimport Database from 'better-sqlite3';\nimport type { MemoryEntry, MemoryQueryFilters } from '../types/memory.js';\n\n/**\n * Optimization statistics record.\n */\nexport interface OptimizationStats {\n id: number;\n timestamp: number;\n tokens_before: number;\n tokens_after: number;\n entries_pruned: number;\n duration_ms: number;\n}\n\n/**\n * KV Memory interface.\n */\nexport interface KVMemory {\n /** Store a memory entry */\n put(entry: MemoryEntry): Promise<void>;\n\n /** Retrieve a memory entry by ID */\n get(id: string): Promise<MemoryEntry | null>;\n\n /** Query entries by filters */\n query(filters: MemoryQueryFilters): Promise<MemoryEntry[]>;\n\n /** Delete a memory entry */\n delete(id: string): Promise<void>;\n\n /** List all entry IDs */\n list(): Promise<string[]>;\n\n /** Compact database (remove expired entries) */\n compact(): Promise<number>;\n\n /** Close database connection */\n close(): Promise<void>;\n\n /** Record optimization statistics */\n recordOptimization(stats: Omit<OptimizationStats, 'id'>): Promise<void>;\n\n /** Get all optimization statistics */\n getOptimizationStats(): Promise<OptimizationStats[]>;\n\n /** Clear all optimization statistics */\n clearOptimizationStats(): Promise<void>;\n}\n\n/**\n * Create a timestamped backup of the database\n * @param dbPath - Path to database file\n * @returns Path to backup file\n */\nfunction createBackup(dbPath: string): string {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const backupPath = `${dbPath}.backup-${timestamp}`;\n\n try {\n copyFileSync(dbPath, backupPath);\n console.log(`✓ Database backed up to: ${backupPath}`);\n return backupPath;\n } catch (error) {\n console.error(`Warning: Could not create backup: ${error}`);\n return '';\n }\n}\n\n/**\n * Create KV Memory store with SQLite backend.\n *\n * Initializes database with dual table schema:\n * - entries_index: Fast lookups (id, hash, timestamp, score, ttl, state, accessCount, isBTSP)\n * - entries_value: Content storage (id, content, tags, metadata)\n *\n * @param dbPath - Path to SQLite database file\n * @returns KVMemory instance\n */\nexport async function createKVMemory(dbPath: string): Promise<KVMemory> {\n // Detect database corruption and create backup\n let db: Database.Database;\n try {\n db = new Database(dbPath);\n\n // Quick integrity check\n const integrityCheck = db.pragma('quick_check', { simple: true });\n if (integrityCheck !== 'ok') {\n console.error('⚠ Database corruption detected!');\n\n // Create backup before attempting recovery\n if (existsSync(dbPath)) {\n const backupPath = createBackup(dbPath);\n if (backupPath) {\n console.log(`Backup created at: ${backupPath}`);\n }\n }\n\n // Try to recover\n console.log('Attempting database recovery...');\n db.close();\n db = new Database(dbPath);\n }\n } catch (error) {\n console.error('⚠ Database error detected:', error);\n\n // Create backup if database exists\n if (existsSync(dbPath)) {\n createBackup(dbPath);\n console.log('Creating new database...');\n }\n\n db = new Database(dbPath);\n }\n\n // Enable WAL mode for better concurrency\n db.pragma('journal_mode = WAL');\n\n // Create entries_index table\n db.exec(`\n CREATE TABLE IF NOT EXISTS entries_index (\n id TEXT PRIMARY KEY NOT NULL,\n hash TEXT UNIQUE NOT NULL,\n timestamp INTEGER NOT NULL,\n score REAL NOT NULL DEFAULT 0.0 CHECK(score >= 0.0 AND score <= 1.0),\n ttl INTEGER NOT NULL CHECK(ttl >= 0),\n state TEXT NOT NULL CHECK(state IN ('silent', 'ready', 'active')),\n accessCount INTEGER NOT NULL DEFAULT 0 CHECK(accessCount >= 0),\n isBTSP INTEGER NOT NULL DEFAULT 0 CHECK(isBTSP IN (0, 1)),\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n `);\n\n // Create entries_value table\n db.exec(`\n CREATE TABLE IF NOT EXISTS entries_value (\n id TEXT PRIMARY KEY NOT NULL,\n content TEXT NOT NULL,\n tags TEXT,\n metadata TEXT,\n FOREIGN KEY (id) REFERENCES entries_index(id) ON DELETE CASCADE\n );\n `);\n\n // Create optimization_stats table\n db.exec(`\n CREATE TABLE IF NOT EXISTS optimization_stats (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n tokens_before INTEGER NOT NULL,\n tokens_after INTEGER NOT NULL,\n entries_pruned INTEGER NOT NULL,\n duration_ms INTEGER NOT NULL\n );\n `);\n\n // Create indexes\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_entries_state ON entries_index(state);\n CREATE INDEX IF NOT EXISTS idx_entries_score ON entries_index(score DESC);\n CREATE INDEX IF NOT EXISTS idx_entries_hash ON entries_index(hash);\n CREATE INDEX IF NOT EXISTS idx_entries_timestamp ON entries_index(timestamp DESC);\n CREATE INDEX IF NOT EXISTS idx_stats_timestamp ON optimization_stats(timestamp DESC);\n `);\n\n // Prepare statements for better performance\n const putIndexStmt = db.prepare(`\n INSERT OR REPLACE INTO entries_index\n (id, hash, timestamp, score, ttl, state, accessCount, isBTSP)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const putValueStmt = db.prepare(`\n INSERT OR REPLACE INTO entries_value\n (id, content, tags, metadata)\n VALUES (?, ?, ?, ?)\n `);\n\n const getStmt = db.prepare(`\n SELECT\n i.id, i.hash, i.timestamp, i.score, i.ttl, i.state, i.accessCount, i.isBTSP,\n v.content, v.tags, v.metadata\n FROM entries_index i\n JOIN entries_value v ON i.id = v.id\n WHERE i.id = ?\n `);\n\n const deleteIndexStmt = db.prepare('DELETE FROM entries_index WHERE id = ?');\n const deleteValueStmt = db.prepare('DELETE FROM entries_value WHERE id = ?');\n\n return {\n async put(entry: MemoryEntry): Promise<void> {\n const transaction = db.transaction(() => {\n putIndexStmt.run(\n entry.id,\n entry.hash,\n entry.timestamp,\n entry.score,\n entry.ttl,\n entry.state,\n entry.accessCount,\n entry.isBTSP ? 1 : 0,\n );\n\n putValueStmt.run(\n entry.id,\n entry.content,\n JSON.stringify(entry.tags),\n JSON.stringify(entry.metadata),\n );\n });\n\n transaction();\n },\n\n async get(id: string): Promise<MemoryEntry | null> {\n const row = getStmt.get(id) as unknown;\n\n if (!row) {\n return null;\n }\n\n const r = row as {\n id: string;\n hash: string;\n timestamp: number;\n score: number;\n ttl: number;\n state: string;\n accessCount: number;\n isBTSP: number;\n content: string;\n tags: string | null;\n metadata: string | null;\n };\n\n return {\n id: r.id,\n content: r.content,\n hash: r.hash,\n timestamp: r.timestamp,\n score: r.score,\n ttl: r.ttl,\n state: r.state as 'silent' | 'ready' | 'active',\n accessCount: r.accessCount,\n tags: r.tags ? JSON.parse(r.tags) : [],\n metadata: r.metadata ? JSON.parse(r.metadata) : {},\n isBTSP: r.isBTSP === 1,\n };\n },\n\n async query(filters: MemoryQueryFilters): Promise<MemoryEntry[]> {\n let sql = `\n SELECT\n i.id, i.hash, i.timestamp, i.score, i.ttl, i.state, i.accessCount, i.isBTSP,\n v.content, v.tags, v.metadata\n FROM entries_index i\n JOIN entries_value v ON i.id = v.id\n WHERE 1=1\n `;\n\n const params: unknown[] = [];\n\n if (filters.state) {\n sql += ' AND i.state = ?';\n params.push(filters.state);\n }\n\n if (filters.minScore !== undefined) {\n sql += ' AND i.score >= ?';\n params.push(filters.minScore);\n }\n\n if (filters.maxScore !== undefined) {\n sql += ' AND i.score <= ?';\n params.push(filters.maxScore);\n }\n\n if (filters.isBTSP !== undefined) {\n sql += ' AND i.isBTSP = ?';\n params.push(filters.isBTSP ? 1 : 0);\n }\n\n sql += ' ORDER BY i.score DESC';\n\n if (filters.limit) {\n sql += ' LIMIT ?';\n params.push(filters.limit);\n }\n\n if (filters.offset) {\n sql += ' OFFSET ?';\n params.push(filters.offset);\n }\n\n const stmt = db.prepare(sql);\n const rows = stmt.all(...params) as unknown[];\n\n return rows.map((row) => {\n const r = row as {\n id: string;\n hash: string;\n timestamp: number;\n score: number;\n ttl: number;\n state: string;\n accessCount: number;\n isBTSP: number;\n content: string;\n tags: string | null;\n metadata: string | null;\n };\n\n return {\n id: r.id,\n content: r.content,\n hash: r.hash,\n timestamp: r.timestamp,\n score: r.score,\n ttl: r.ttl,\n state: r.state as 'silent' | 'ready' | 'active',\n accessCount: r.accessCount,\n tags: r.tags ? JSON.parse(r.tags) : [],\n metadata: r.metadata ? JSON.parse(r.metadata) : {},\n isBTSP: r.isBTSP === 1,\n };\n });\n },\n\n async delete(id: string): Promise<void> {\n const transaction = db.transaction(() => {\n deleteIndexStmt.run(id);\n deleteValueStmt.run(id);\n });\n\n transaction();\n },\n\n async list(): Promise<string[]> {\n const stmt = db.prepare('SELECT id FROM entries_index');\n const rows = stmt.all() as { id: string }[];\n return rows.map((r) => r.id);\n },\n\n async compact(): Promise<number> {\n const before = db.prepare('SELECT COUNT(*) as count FROM entries_index').get() as {\n count: number;\n };\n\n // Remove fully decayed entries (this will be enhanced in sleep-compressor)\n db.exec('DELETE FROM entries_index WHERE ttl <= 0');\n\n db.exec('VACUUM');\n\n const after = db.prepare('SELECT COUNT(*) as count FROM entries_index').get() as {\n count: number;\n };\n\n return before.count - after.count;\n },\n\n async close(): Promise<void> {\n db.close();\n },\n\n async recordOptimization(stats: Omit<OptimizationStats, 'id'>): Promise<void> {\n const stmt = db.prepare(`\n INSERT INTO optimization_stats (timestamp, tokens_before, tokens_after, entries_pruned, duration_ms)\n VALUES (?, ?, ?, ?, ?)\n `);\n\n stmt.run(\n stats.timestamp,\n stats.tokens_before,\n stats.tokens_after,\n stats.entries_pruned,\n stats.duration_ms,\n );\n },\n\n async getOptimizationStats(): Promise<OptimizationStats[]> {\n const stmt = db.prepare(`\n SELECT id, timestamp, tokens_before, tokens_after, entries_pruned, duration_ms\n FROM optimization_stats\n ORDER BY timestamp DESC\n `);\n\n const rows = stmt.all() as OptimizationStats[];\n return rows;\n },\n\n async clearOptimizationStats(): Promise<void> {\n db.exec('DELETE FROM optimization_stats');\n },\n };\n}\n","/**\n * Consolidation Scheduler - Periodic memory consolidation\n *\n * Handles:\n * - Scheduled consolidation using setInterval\n * - Integration with existing consolidate command\n * - Logging to daemon log file\n * - Metrics tracking for consolidation runs\n */\n\nimport { appendFileSync } from 'node:fs';\nimport { consolidateCommand } from '../cli/commands/consolidate.js';\nimport type { KVMemory } from '../core/kv-memory.js';\nimport { getMetrics } from '../core/metrics.js';\nimport type { SparnConfig } from '../types/config.js';\n\nexport interface ConsolidationScheduler {\n /**\n * Start the scheduler\n */\n start(): void;\n\n /**\n * Stop the scheduler\n */\n stop(): void;\n\n /**\n * Get scheduler status\n */\n getStatus(): ConsolidationSchedulerStatus;\n}\n\nexport interface ConsolidationSchedulerStatus {\n /** Is the scheduler running */\n running: boolean;\n /** Interval in hours */\n intervalHours: number | null;\n /** Next run timestamp (null if not running) */\n nextRun: number | null;\n /** Total consolidations run */\n totalRuns: number;\n /** Last run timestamp (null if never run) */\n lastRun: number | null;\n /** Last run result (null if never run) */\n lastResult: ConsolidationResult | null;\n}\n\nexport interface ConsolidationResult {\n /** Timestamp of consolidation */\n timestamp: number;\n /** Entries before consolidation */\n entriesBefore: number;\n /** Entries after consolidation */\n entriesAfter: number;\n /** Decayed entries removed */\n decayedRemoved: number;\n /** Duplicate entries merged */\n duplicatesRemoved: number;\n /** Compression ratio (0.0-1.0) */\n compressionRatio: number;\n /** Duration in milliseconds */\n durationMs: number;\n /** Success flag */\n success: boolean;\n /** Error message if failed */\n error?: string;\n}\n\nexport interface ConsolidationSchedulerOptions {\n /** Memory store instance */\n memory: KVMemory;\n /** Sparn configuration */\n config: SparnConfig;\n /** Log file path (optional, defaults to daemon log) */\n logFile?: string;\n}\n\n/**\n * Create a consolidation scheduler instance\n * @param options - Scheduler options\n * @returns ConsolidationScheduler instance\n */\nexport function createConsolidationScheduler(\n options: ConsolidationSchedulerOptions,\n): ConsolidationScheduler {\n const { memory, config, logFile } = options;\n const intervalHours = config.realtime.consolidationInterval;\n\n let timerId: NodeJS.Timeout | null = null;\n let totalRuns = 0;\n let lastRun: number | null = null;\n let lastResult: ConsolidationResult | null = null;\n let nextRun: number | null = null;\n\n /**\n * Log helper\n */\n function log(message: string): void {\n const timestamp = new Date().toISOString();\n const logMessage = `[${timestamp}] [Consolidation] ${message}\\n`;\n\n // Log to daemon log file if available\n const logPath = logFile || config.realtime.logFile;\n if (logPath) {\n try {\n appendFileSync(logPath, logMessage, 'utf-8');\n } catch {\n // Fail silently if can't write log\n }\n }\n }\n\n /**\n * Run consolidation\n */\n async function runConsolidation(): Promise<void> {\n const startTime = Date.now();\n log('Starting scheduled consolidation');\n\n try {\n const result = await consolidateCommand({ memory });\n\n // Record success\n lastRun = startTime;\n totalRuns++;\n lastResult = {\n timestamp: startTime,\n entriesBefore: result.entriesBefore,\n entriesAfter: result.entriesAfter,\n decayedRemoved: result.decayedRemoved,\n duplicatesRemoved: result.duplicatesRemoved,\n compressionRatio: result.compressionRatio,\n durationMs: result.durationMs,\n success: true,\n };\n\n // Log result\n log(\n `Consolidation completed: ${result.entriesBefore} -> ${result.entriesAfter} entries ` +\n `(${result.decayedRemoved} decayed, ${result.duplicatesRemoved} duplicates, ` +\n `${Math.round(result.compressionRatio * 100)}% compression) in ${result.durationMs}ms`,\n );\n\n // Update metrics\n const metrics = getMetrics();\n metrics.recordOptimization({\n timestamp: startTime,\n duration: result.durationMs,\n tokensBefore: result.entriesBefore * 100, // Rough estimate\n tokensAfter: result.entriesAfter * 100, // Rough estimate\n entriesProcessed: result.entriesBefore,\n entriesKept: result.entriesAfter,\n cacheHitRate: 0, // N/A for consolidation\n memoryUsage: process.memoryUsage().heapUsed,\n });\n } catch (error) {\n // Record failure\n lastRun = startTime;\n totalRuns++;\n lastResult = {\n timestamp: startTime,\n entriesBefore: 0,\n entriesAfter: 0,\n decayedRemoved: 0,\n duplicatesRemoved: 0,\n compressionRatio: 0,\n durationMs: Date.now() - startTime,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n\n log(`Consolidation failed: ${lastResult.error}`);\n }\n\n // Update next run time\n if (intervalHours !== null && intervalHours > 0) {\n nextRun = Date.now() + intervalHours * 60 * 60 * 1000;\n }\n }\n\n function start(): void {\n // Check if interval is configured\n if (intervalHours === null || intervalHours <= 0) {\n log('Consolidation scheduler disabled (consolidationInterval not set)');\n return;\n }\n\n // Check if already running\n if (timerId !== null) {\n log('Consolidation scheduler already running');\n return;\n }\n\n // Calculate interval in milliseconds\n const intervalMs = intervalHours * 60 * 60 * 1000;\n\n // Set up periodic timer\n timerId = setInterval(() => {\n void runConsolidation();\n }, intervalMs);\n\n // Calculate next run\n nextRun = Date.now() + intervalMs;\n\n log(\n `Consolidation scheduler started (interval: ${intervalHours}h, next run: ${new Date(nextRun).toISOString()})`,\n );\n }\n\n function stop(): void {\n if (timerId !== null) {\n clearInterval(timerId);\n timerId = null;\n nextRun = null;\n log('Consolidation scheduler stopped');\n }\n }\n\n function getStatus(): ConsolidationSchedulerStatus {\n return {\n running: timerId !== null,\n intervalHours,\n nextRun,\n totalRuns,\n lastRun,\n lastResult,\n };\n }\n\n return {\n start,\n stop,\n getStatus,\n };\n}\n","/**\n * Engram Scorer - Implements engram theory (memory decay)\n *\n * Neuroscience: Memories fade over time without reinforcement.\n * Application: Apply exponential decay formula to memory scores based on age and access count.\n *\n * Formula: decay = 1 - e^(-age/TTL)\n * Score adjustment: score_new = score_old * (1 - decay) + (accessCount bonus)\n */\n\nimport type { MemoryEntry } from '../types/memory.js';\n\nexport interface EngramScorerConfig {\n /** Default TTL in hours for new entries */\n defaultTTL: number;\n /** Decay threshold (0.0-1.0) above which entries are marked for pruning */\n decayThreshold: number;\n}\n\nexport interface EngramScorer {\n /**\n * Calculate current score for an entry based on decay and access count\n * @param entry - Memory entry to score\n * @param currentTime - Current timestamp in milliseconds (for testing)\n * @returns Updated score (0.0-1.0)\n */\n calculateScore(entry: MemoryEntry, currentTime?: number): number;\n\n /**\n * Refresh TTL to default value\n * @param entry - Entry to refresh\n * @returns Entry with refreshed TTL and timestamp\n */\n refreshTTL(entry: MemoryEntry): MemoryEntry;\n\n /**\n * Calculate decay factor (0.0-1.0) based on age and TTL\n * @param ageInSeconds - Age of entry in seconds\n * @param ttlInSeconds - TTL in seconds\n * @returns Decay factor (0.0 = fresh, 1.0 = fully decayed)\n */\n calculateDecay(ageInSeconds: number, ttlInSeconds: number): number;\n}\n\n/**\n * Create an engram scorer instance\n * @param config - Scorer configuration\n * @returns EngramScorer instance\n */\nexport function createEngramScorer(config: EngramScorerConfig): EngramScorer {\n const { defaultTTL } = config;\n\n function calculateDecay(ageInSeconds: number, ttlInSeconds: number): number {\n if (ttlInSeconds === 0) return 1.0; // Instant decay\n if (ageInSeconds <= 0) return 0.0; // Fresh entry\n\n // Exponential decay: 1 - e^(-age/TTL)\n const ratio = ageInSeconds / ttlInSeconds;\n const decay = 1 - Math.exp(-ratio);\n\n // Clamp to [0.0, 1.0]\n return Math.max(0, Math.min(1, decay));\n }\n\n function calculateScore(entry: MemoryEntry, currentTime: number = Date.now()): number {\n // Calculate age in seconds\n const ageInMilliseconds = currentTime - entry.timestamp;\n const ageInSeconds = Math.max(0, ageInMilliseconds / 1000);\n\n // Calculate decay factor\n const decay = calculateDecay(ageInSeconds, entry.ttl);\n\n // Base score reduced by decay\n let score = entry.score * (1 - decay);\n\n // Access count bonus (diminishing returns via log)\n if (entry.accessCount > 0) {\n const accessBonus = Math.log(entry.accessCount + 1) * 0.1;\n score = Math.min(1.0, score + accessBonus);\n }\n\n // BTSP entries maintain high score\n if (entry.isBTSP) {\n score = Math.max(score, 0.9);\n }\n\n return Math.max(0, Math.min(1, score));\n }\n\n function refreshTTL(entry: MemoryEntry): MemoryEntry {\n return {\n ...entry,\n ttl: defaultTTL * 3600, // Convert hours to seconds\n timestamp: Date.now(),\n };\n }\n\n return {\n calculateScore,\n refreshTTL,\n calculateDecay,\n };\n}\n","/**\n * Sleep Compressor - Implements sleep replay principle\n *\n * Neuroscience: During sleep, the brain consolidates memories by replaying important ones\n * and discarding irrelevant information.\n * Application: Periodic consolidation removes decayed entries and merges duplicates.\n */\n\nimport type { ConsolidateResult, DuplicateGroup } from '../types/consolidate.js';\nimport type { MemoryEntry } from '../types/memory.js';\nimport { createEngramScorer } from './engram-scorer.js';\n\nexport interface SleepCompressor {\n /**\n * Consolidate entries: remove decayed, merge duplicates\n * @param entries - All memory entries\n * @returns Consolidation result\n */\n consolidate(entries: MemoryEntry[]): ConsolidateResult;\n\n /**\n * Find duplicate entries (exact hash or near-duplicate by similarity)\n * @param entries - Memory entries\n * @returns Groups of duplicates\n */\n findDuplicates(entries: MemoryEntry[]): DuplicateGroup[];\n\n /**\n * Merge duplicate entries, keeping highest score\n * @param groups - Duplicate groups\n * @returns Merged entries\n */\n mergeDuplicates(groups: DuplicateGroup[]): MemoryEntry[];\n}\n\n/**\n * Create a sleep compressor instance\n * @returns SleepCompressor instance\n */\nexport function createSleepCompressor(): SleepCompressor {\n const scorer = createEngramScorer({ defaultTTL: 24, decayThreshold: 0.95 });\n\n function consolidate(entries: MemoryEntry[]): ConsolidateResult {\n const startTime = Date.now();\n const originalCount = entries.length;\n\n // Step 1: Remove fully decayed entries (decay ≥ 0.95)\n const now = Date.now();\n const nonDecayed = entries.filter((entry) => {\n const ageInSeconds = (now - entry.timestamp) / 1000;\n const decay = scorer.calculateDecay(ageInSeconds, entry.ttl);\n return decay < 0.95; // Keep entries with decay < 0.95\n });\n\n const decayedRemoved = originalCount - nonDecayed.length;\n\n // Step 2: Find and merge duplicates\n const duplicateGroups = findDuplicates(nonDecayed);\n const merged = mergeDuplicates(duplicateGroups);\n\n // Step 3: Keep non-duplicates\n const duplicateIds = new Set(duplicateGroups.flatMap((g) => g.entries.map((e) => e.id)));\n const nonDuplicates = nonDecayed.filter((e) => !duplicateIds.has(e.id));\n\n // Combine merged duplicates with non-duplicates\n const kept = [...merged, ...nonDuplicates];\n const removed = entries.filter((e) => !kept.some((k) => k.id === e.id));\n\n const duplicatesRemoved = duplicateGroups.reduce((sum, g) => sum + (g.entries.length - 1), 0);\n\n return {\n kept,\n removed,\n entriesBefore: originalCount,\n entriesAfter: kept.length,\n decayedRemoved,\n duplicatesRemoved,\n compressionRatio: originalCount > 0 ? kept.length / originalCount : 0,\n durationMs: Date.now() - startTime,\n };\n }\n\n function findDuplicates(entries: MemoryEntry[]): DuplicateGroup[] {\n const groups: DuplicateGroup[] = [];\n const processed = new Set<string>();\n\n // Find exact hash matches\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n if (!entry || processed.has(entry.id)) continue;\n\n const duplicates = entries.filter((e, idx) => idx !== i && e.hash === entry.hash);\n\n if (duplicates.length > 0) {\n const group: DuplicateGroup = {\n entries: [entry, ...duplicates],\n similarity: 1.0, // Exact match\n };\n groups.push(group);\n\n // Mark as processed\n processed.add(entry.id);\n for (const dup of duplicates) {\n processed.add(dup.id);\n }\n }\n }\n\n // Find near-duplicates (cosine similarity ≥ 0.85)\n for (let i = 0; i < entries.length; i++) {\n const entryI = entries[i];\n if (!entryI || processed.has(entryI.id)) continue;\n\n for (let j = i + 1; j < entries.length; j++) {\n const entryJ = entries[j];\n if (!entryJ || processed.has(entryJ.id)) continue;\n\n const similarity = cosineSimilarity(entryI.content, entryJ.content);\n\n if (similarity >= 0.85) {\n const group: DuplicateGroup = {\n entries: [entryI, entryJ],\n similarity,\n };\n groups.push(group);\n\n processed.add(entryI.id);\n processed.add(entryJ.id);\n break; // Move to next i\n }\n }\n }\n\n return groups;\n }\n\n function mergeDuplicates(groups: DuplicateGroup[]): MemoryEntry[] {\n const merged: MemoryEntry[] = [];\n\n for (const group of groups) {\n // Keep entry with highest score\n const sorted = [...group.entries].sort((a, b) => b.score - a.score);\n const best = sorted[0];\n if (!best) continue; // Skip empty groups\n\n // Sum access counts\n const totalAccessCount = group.entries.reduce((sum, e) => sum + e.accessCount, 0);\n\n // Merge tags\n const allTags = new Set(group.entries.flatMap((e) => e.tags));\n\n merged.push({\n ...best,\n accessCount: totalAccessCount,\n tags: Array.from(allTags),\n });\n }\n\n return merged;\n }\n\n /**\n * Calculate cosine similarity between two text strings\n * @param text1 - First text\n * @param text2 - Second text\n * @returns Similarity score (0.0-1.0)\n */\n function cosineSimilarity(text1: string, text2: string): number {\n const words1 = tokenize(text1);\n const words2 = tokenize(text2);\n\n // Build vocabulary\n const vocab = new Set([...words1, ...words2]);\n\n // Build word frequency vectors\n const vec1: Record<string, number> = {};\n const vec2: Record<string, number> = {};\n\n for (const word of vocab) {\n vec1[word] = words1.filter((w) => w === word).length;\n vec2[word] = words2.filter((w) => w === word).length;\n }\n\n // Calculate dot product and magnitudes\n let dotProduct = 0;\n let mag1 = 0;\n let mag2 = 0;\n\n for (const word of vocab) {\n const count1 = vec1[word] ?? 0;\n const count2 = vec2[word] ?? 0;\n dotProduct += count1 * count2;\n mag1 += count1 * count1;\n mag2 += count2 * count2;\n }\n\n mag1 = Math.sqrt(mag1);\n mag2 = Math.sqrt(mag2);\n\n if (mag1 === 0 || mag2 === 0) return 0;\n\n return dotProduct / (mag1 * mag2);\n }\n\n function tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length > 0);\n }\n\n return {\n consolidate,\n findDuplicates,\n mergeDuplicates,\n };\n}\n","/**\n * Consolidate Command - Periodic memory consolidation\n */\n\nimport type { KVMemory } from '../../core/kv-memory.js';\nimport { createSleepCompressor } from '../../core/sleep-compressor.js';\n\nexport interface ConsolidateCommandOptions {\n /** Memory store instance */\n memory: KVMemory;\n}\n\nexport interface ConsolidateCommandResult {\n /** Entries before consolidation */\n entriesBefore: number;\n /** Entries after consolidation */\n entriesAfter: number;\n /** Decayed entries removed */\n decayedRemoved: number;\n /** Duplicate entries merged */\n duplicatesRemoved: number;\n /** Compression ratio (0.0-1.0) */\n compressionRatio: number;\n /** Duration in milliseconds */\n durationMs: number;\n /** VACUUM completed */\n vacuumCompleted: boolean;\n}\n\n/**\n * Execute the consolidate command\n * @param options - Command options\n * @returns Consolidation result\n */\nexport async function consolidateCommand(\n options: ConsolidateCommandOptions,\n): Promise<ConsolidateCommandResult> {\n const { memory } = options;\n\n // Get all entries from memory\n const allIds = await memory.list();\n const allEntries = await Promise.all(\n allIds.map(async (id) => {\n const entry = await memory.get(id);\n return entry;\n }),\n );\n\n // Filter out nulls\n const entries = allEntries.filter((e) => e !== null);\n\n // Run consolidation\n const compressor = createSleepCompressor();\n const result = compressor.consolidate(entries);\n\n // Update memory: remove old entries, keep consolidated ones\n for (const removed of result.removed) {\n await memory.delete(removed.id);\n }\n\n for (const kept of result.kept) {\n await memory.put(kept);\n }\n\n // Run VACUUM to reclaim space\n await memory.compact();\n\n return {\n entriesBefore: result.entriesBefore,\n entriesAfter: result.entriesAfter,\n decayedRemoved: result.decayedRemoved,\n duplicatesRemoved: result.duplicatesRemoved,\n compressionRatio: result.compressionRatio,\n durationMs: result.durationMs,\n vacuumCompleted: true,\n };\n}\n","/**\n * Metrics and Telemetry System\n *\n * Tracks performance metrics and optimization statistics:\n * - Optimization duration and throughput\n * - Token savings and reduction rates\n * - Memory usage and cache hit rates\n * - Daemon uptime and session counts\n */\n\nexport interface OptimizationMetric {\n timestamp: number;\n duration: number;\n tokensBefore: number;\n tokensAfter: number;\n entriesProcessed: number;\n entriesKept: number;\n cacheHitRate: number;\n memoryUsage: number;\n}\n\nexport interface DaemonMetric {\n startTime: number;\n sessionsWatched: number;\n totalOptimizations: number;\n totalTokensSaved: number;\n averageLatency: number;\n memoryUsage: number;\n}\n\nexport interface MetricsSnapshot {\n timestamp: number;\n optimization: {\n totalRuns: number;\n totalDuration: number;\n totalTokensSaved: number;\n averageReduction: number;\n p50Latency: number;\n p95Latency: number;\n p99Latency: number;\n };\n cache: {\n hitRate: number;\n totalHits: number;\n totalMisses: number;\n size: number;\n };\n daemon: {\n uptime: number;\n sessionsWatched: number;\n memoryUsage: number;\n };\n}\n\nexport interface MetricsCollector {\n /**\n * Record an optimization metric\n */\n recordOptimization(metric: OptimizationMetric): void;\n\n /**\n * Update daemon metrics\n */\n updateDaemon(metric: Partial<DaemonMetric>): void;\n\n /**\n * Get current metrics snapshot\n */\n getSnapshot(): MetricsSnapshot;\n\n /**\n * Export metrics as JSON\n */\n export(): string;\n\n /**\n * Reset all metrics\n */\n reset(): void;\n}\n\n/**\n * Create a metrics collector instance\n */\nexport function createMetricsCollector(): MetricsCollector {\n const optimizations: OptimizationMetric[] = [];\n let daemonMetrics: DaemonMetric = {\n startTime: Date.now(),\n sessionsWatched: 0,\n totalOptimizations: 0,\n totalTokensSaved: 0,\n averageLatency: 0,\n memoryUsage: 0,\n };\n\n let cacheHits = 0;\n let cacheMisses = 0;\n\n function recordOptimization(metric: OptimizationMetric): void {\n optimizations.push(metric);\n\n // Update daemon totals\n daemonMetrics.totalOptimizations++;\n daemonMetrics.totalTokensSaved += metric.tokensBefore - metric.tokensAfter;\n\n // Update cache stats\n if (metric.cacheHitRate > 0) {\n const hits = Math.round(metric.entriesProcessed * metric.cacheHitRate);\n cacheHits += hits;\n cacheMisses += metric.entriesProcessed - hits;\n }\n\n // Update average latency (moving average)\n daemonMetrics.averageLatency =\n (daemonMetrics.averageLatency * (daemonMetrics.totalOptimizations - 1) + metric.duration) /\n daemonMetrics.totalOptimizations;\n\n // Keep only last 1000 metrics in memory\n if (optimizations.length > 1000) {\n optimizations.shift();\n }\n }\n\n function updateDaemon(metric: Partial<DaemonMetric>): void {\n daemonMetrics = {\n ...daemonMetrics,\n ...metric,\n };\n }\n\n function calculatePercentile(values: number[], percentile: number): number {\n if (values.length === 0) return 0;\n\n const sorted = [...values].sort((a, b) => a - b);\n const index = Math.ceil((percentile / 100) * sorted.length) - 1;\n return sorted[index] || 0;\n }\n\n function getSnapshot(): MetricsSnapshot {\n const totalRuns = optimizations.length;\n const totalDuration = optimizations.reduce((sum, m) => sum + m.duration, 0);\n const totalTokensSaved = optimizations.reduce(\n (sum, m) => sum + (m.tokensBefore - m.tokensAfter),\n 0,\n );\n\n const totalTokensBefore = optimizations.reduce((sum, m) => sum + m.tokensBefore, 0);\n const averageReduction = totalTokensBefore > 0 ? totalTokensSaved / totalTokensBefore : 0;\n\n const durations = optimizations.map((m) => m.duration);\n\n const totalCacheQueries = cacheHits + cacheMisses;\n const hitRate = totalCacheQueries > 0 ? cacheHits / totalCacheQueries : 0;\n\n return {\n timestamp: Date.now(),\n optimization: {\n totalRuns,\n totalDuration,\n totalTokensSaved,\n averageReduction,\n p50Latency: calculatePercentile(durations, 50),\n p95Latency: calculatePercentile(durations, 95),\n p99Latency: calculatePercentile(durations, 99),\n },\n cache: {\n hitRate,\n totalHits: cacheHits,\n totalMisses: cacheMisses,\n size: optimizations.reduce((sum, m) => sum + m.entriesKept, 0),\n },\n daemon: {\n uptime: Date.now() - daemonMetrics.startTime,\n sessionsWatched: daemonMetrics.sessionsWatched,\n memoryUsage: daemonMetrics.memoryUsage,\n },\n };\n }\n\n function exportMetrics(): string {\n return JSON.stringify(getSnapshot(), null, 2);\n }\n\n function reset(): void {\n optimizations.length = 0;\n cacheHits = 0;\n cacheMisses = 0;\n daemonMetrics = {\n startTime: Date.now(),\n sessionsWatched: 0,\n totalOptimizations: 0,\n totalTokensSaved: 0,\n averageLatency: 0,\n memoryUsage: 0,\n };\n }\n\n return {\n recordOptimization,\n updateDaemon,\n getSnapshot,\n export: exportMetrics,\n reset,\n };\n}\n\n// Global metrics instance\nlet globalMetrics: MetricsCollector | null = null;\n\n/**\n * Get or create the global metrics collector\n */\nexport function getMetrics(): MetricsCollector {\n if (!globalMetrics) {\n globalMetrics = createMetricsCollector();\n }\n return globalMetrics;\n}\n","/**\n * Session Watcher - Monitor Claude Code session files for changes\n *\n * Uses Node.js fs.watch to monitor ~/.claude/projects/**\\/*.jsonl files.\n * Debounces events and triggers optimization when token threshold exceeded.\n * Maintains per-session ContextPipeline instances.\n */\n\nimport { type FSWatcher, readdirSync, statSync, watch } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { type ContextPipeline, createContextPipeline } from '../core/context-pipeline.js';\nimport { getMetrics } from '../core/metrics.js';\nimport type { SparnConfig } from '../types/config.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport { createFileTracker } from './file-tracker.js';\n\nexport interface SessionWatcherConfig {\n /** Sparn configuration */\n config: SparnConfig;\n /** Callback when optimization triggered */\n onOptimize?: (sessionId: string, stats: SessionStats) => void;\n /** Callback on error */\n onError?: (error: Error) => void;\n}\n\nexport interface SessionStats {\n /** Session ID */\n sessionId: string;\n /** Total tokens ingested */\n totalTokens: number;\n /** Current optimized tokens */\n optimizedTokens: number;\n /** Reduction percentage */\n reduction: number;\n /** Entry count */\n entryCount: number;\n /** Budget utilization */\n budgetUtilization: number;\n}\n\nexport interface SessionWatcher {\n /**\n * Start watching Claude Code session files\n * @returns Promise that resolves when watcher is ready\n */\n start(): Promise<void>;\n\n /**\n * Stop watching and cleanup\n */\n stop(): void;\n\n /**\n * Get statistics for all sessions\n * @returns Array of session stats\n */\n getStats(): SessionStats[];\n\n /**\n * Get statistics for a specific session\n * @param sessionId - Session ID\n * @returns Session stats or null if not found\n */\n getSessionStats(sessionId: string): SessionStats | null;\n\n /**\n * Manually trigger optimization for a session\n * @param sessionId - Session ID\n */\n optimizeSession(sessionId: string): void;\n}\n\n/**\n * Create a session watcher instance\n * @param config - Watcher configuration\n * @returns SessionWatcher instance\n */\nexport function createSessionWatcher(config: SessionWatcherConfig): SessionWatcher {\n const { config: sparnConfig, onOptimize, onError } = config;\n const { realtime, decay, states } = sparnConfig;\n\n // Per-session pipelines and trackers\n const pipelines = new Map<string, ContextPipeline>();\n const fileTracker = createFileTracker();\n\n // File system watchers\n const watchers: FSWatcher[] = [];\n\n // Debounce timers per file\n const debounceTimers = new Map<string, NodeJS.Timeout>();\n\n /**\n * Get Claude Code projects directory\n */\n function getProjectsDir(): string {\n return join(homedir(), '.claude', 'projects');\n }\n\n /**\n * Extract session ID from file path\n * Example: ~/.claude/projects/my-project/abc123.jsonl -> abc123\n */\n function getSessionId(filePath: string): string {\n const filename = filePath.split(/[/\\\\]/).pop() || '';\n return filename.replace(/\\.jsonl$/, '');\n }\n\n /**\n * Get or create pipeline for session\n */\n function getPipeline(sessionId: string): ContextPipeline {\n let pipeline = pipelines.get(sessionId);\n\n if (!pipeline) {\n pipeline = createContextPipeline({\n tokenBudget: realtime.tokenBudget,\n decay,\n states,\n windowSize: realtime.windowSize,\n fullOptimizationInterval: 50, // Full re-optimization every 50 incremental updates\n });\n pipelines.set(sessionId, pipeline);\n }\n\n return pipeline;\n }\n\n /**\n * Handle file change event (debounced)\n */\n function handleFileChange(filePath: string): void {\n // Clear existing timer\n const existingTimer = debounceTimers.get(filePath);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Set new debounced timer\n const timer = setTimeout(() => {\n try {\n // Read new lines from file\n const newLines = fileTracker.readNewLines(filePath);\n\n if (newLines.length === 0) return;\n\n // Parse JSONL content\n const content = newLines.join('\\n');\n const sessionId = getSessionId(filePath);\n const pipeline = getPipeline(sessionId);\n\n // Ingest into pipeline\n pipeline.ingest(content, { sessionId, filePath });\n\n // Check if we should trigger optimization\n const stats = pipeline.getStats();\n if (stats.currentTokens >= realtime.autoOptimizeThreshold) {\n // Update daemon metrics\n getMetrics().updateDaemon({\n sessionsWatched: pipelines.size,\n memoryUsage: process.memoryUsage().heapUsed,\n });\n\n // Trigger optimization callback\n if (onOptimize) {\n const sessionStats = computeSessionStats(sessionId, pipeline);\n onOptimize(sessionId, sessionStats);\n }\n }\n } catch (error) {\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n } finally {\n debounceTimers.delete(filePath);\n }\n }, realtime.debounceMs);\n\n debounceTimers.set(filePath, timer);\n }\n\n /**\n * Recursively find all JSONL files in directory\n */\n function findJsonlFiles(dir: string): string[] {\n const files: string[] = [];\n\n try {\n const entries = readdirSync(dir);\n\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n // Recurse into subdirectories\n files.push(...findJsonlFiles(fullPath));\n } else if (entry.endsWith('.jsonl')) {\n // Match pattern\n const matches = realtime.watchPatterns.some((pattern) => {\n // Simple glob matching (supports **/*.jsonl)\n const regex = new RegExp(\n pattern.replace(/\\*\\*/g, '.*').replace(/\\*/g, '[^/\\\\\\\\]*').replace(/\\./g, '\\\\.'),\n );\n return regex.test(fullPath);\n });\n\n if (matches) {\n files.push(fullPath);\n }\n }\n }\n } catch (_error) {\n // Ignore errors (directory might not exist yet)\n }\n\n return files;\n }\n\n /**\n * Compute session statistics\n */\n function computeSessionStats(sessionId: string, pipeline: ContextPipeline): SessionStats {\n const stats = pipeline.getStats();\n const entries = pipeline.getEntries();\n const totalTokens = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n return {\n sessionId,\n totalTokens: stats.totalIngested,\n optimizedTokens: stats.currentTokens,\n reduction: totalTokens > 0 ? (totalTokens - stats.currentTokens) / totalTokens : 0,\n entryCount: stats.currentEntries,\n budgetUtilization: stats.budgetUtilization,\n };\n }\n\n async function start(): Promise<void> {\n const projectsDir = getProjectsDir();\n\n // Find all existing JSONL files\n const jsonlFiles = findJsonlFiles(projectsDir);\n\n // Watch each file's parent directory (fs.watch is directory-based)\n const watchedDirs = new Set<string>();\n\n for (const file of jsonlFiles) {\n const dir = dirname(file);\n\n if (!watchedDirs.has(dir)) {\n const watcher = watch(dir, { recursive: false }, (_eventType, filename) => {\n if (filename?.endsWith('.jsonl')) {\n const fullPath = join(dir, filename);\n handleFileChange(fullPath);\n }\n });\n\n watchers.push(watcher);\n watchedDirs.add(dir);\n }\n }\n\n // Also watch projects directory for new subdirectories\n const projectsWatcher = watch(projectsDir, { recursive: true }, (_eventType, filename) => {\n if (filename?.endsWith('.jsonl')) {\n const fullPath = join(projectsDir, filename);\n handleFileChange(fullPath);\n }\n });\n\n watchers.push(projectsWatcher);\n\n // Update daemon metrics\n getMetrics().updateDaemon({\n startTime: Date.now(),\n sessionsWatched: jsonlFiles.length,\n memoryUsage: process.memoryUsage().heapUsed,\n });\n }\n\n function stop(): void {\n // Close all watchers\n for (const watcher of watchers) {\n watcher.close();\n }\n watchers.length = 0;\n\n // Clear all timers\n for (const timer of debounceTimers.values()) {\n clearTimeout(timer);\n }\n debounceTimers.clear();\n\n // Clear pipelines\n pipelines.clear();\n\n // Clear file tracker\n fileTracker.clearAll();\n }\n\n function getStats(): SessionStats[] {\n const stats: SessionStats[] = [];\n\n for (const [sessionId, pipeline] of pipelines.entries()) {\n stats.push(computeSessionStats(sessionId, pipeline));\n }\n\n return stats;\n }\n\n function getSessionStats(sessionId: string): SessionStats | null {\n const pipeline = pipelines.get(sessionId);\n if (!pipeline) return null;\n\n return computeSessionStats(sessionId, pipeline);\n }\n\n function optimizeSession(sessionId: string): void {\n const pipeline = pipelines.get(sessionId);\n if (!pipeline) return;\n\n // Get entries and force full optimization\n const entries = pipeline.getEntries();\n pipeline.clear();\n pipeline.ingest(entries.map((e) => e.content).join('\\n\\n'));\n\n // Trigger callback\n if (onOptimize) {\n const stats = computeSessionStats(sessionId, pipeline);\n onOptimize(sessionId, stats);\n }\n }\n\n return {\n start,\n stop,\n getStats,\n getSessionStats,\n optimizeSession,\n };\n}\n","/**\n * Context Parser - Shared utilities for parsing agent contexts into memory entries\n *\n * Extracted from claude-code adapter to enable reuse across:\n * - Adapters (claude-code, generic)\n * - Real-time pipeline (streaming context)\n * - Hooks (pre-prompt, post-tool-result)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { MemoryEntry } from '../types/memory.js';\nimport { hashContent } from './hash.js';\n\n/**\n * Block type classification for Claude Code context\n */\nexport type BlockType = 'conversation' | 'tool' | 'result' | 'other';\n\n/**\n * Parse Claude Code context into memory entries\n * Handles conversation turns, tool uses, and results\n * @param context - Raw context string\n * @returns Array of memory entries\n */\nexport function parseClaudeCodeContext(context: string): MemoryEntry[] {\n const entries: MemoryEntry[] = [];\n const now = Date.now();\n\n // Split by conversation turns and tool boundaries\n const lines = context.split('\\n');\n let currentBlock: string[] = [];\n let blockType: BlockType = 'other';\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Detect conversation turns\n if (trimmed.startsWith('User:') || trimmed.startsWith('Assistant:')) {\n if (currentBlock.length > 0) {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n currentBlock = [];\n }\n blockType = 'conversation';\n currentBlock.push(line);\n }\n // Detect tool calls\n else if (\n trimmed.includes('<function_calls>') ||\n trimmed.includes('<invoke>') ||\n trimmed.includes('<tool_use>')\n ) {\n if (currentBlock.length > 0) {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n currentBlock = [];\n }\n blockType = 'tool';\n currentBlock.push(line);\n }\n // Detect tool results\n else if (trimmed.includes('<function_results>') || trimmed.includes('</function_results>')) {\n if (currentBlock.length > 0 && blockType !== 'result') {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n currentBlock = [];\n }\n blockType = 'result';\n currentBlock.push(line);\n }\n // Continue current block\n else if (currentBlock.length > 0) {\n currentBlock.push(line);\n }\n // Start new block if line has content\n else if (trimmed.length > 0) {\n currentBlock.push(line);\n blockType = 'other';\n }\n }\n\n // Add final block\n if (currentBlock.length > 0) {\n entries.push(createEntry(currentBlock.join('\\n'), blockType, now));\n }\n\n return entries.filter((e) => e.content.trim().length > 0);\n}\n\n/**\n * Create a memory entry from a content block\n * @param content - Block content\n * @param type - Block type\n * @param baseTime - Base timestamp\n * @returns Memory entry\n */\nexport function createEntry(content: string, type: BlockType, baseTime: number): MemoryEntry {\n const tags: string[] = [type];\n\n // Assign initial score based on type\n let initialScore = 0.5;\n if (type === 'conversation') initialScore = 0.8; // Prioritize conversation\n if (type === 'tool') initialScore = 0.7; // Tool calls are important\n if (type === 'result') initialScore = 0.4; // Results can be verbose\n\n return {\n id: randomUUID(),\n content,\n hash: hashContent(content),\n timestamp: baseTime,\n score: initialScore,\n state: initialScore > 0.7 ? 'active' : initialScore > 0.3 ? 'ready' : 'silent',\n ttl: 24 * 3600, // 24 hours default\n accessCount: 0,\n tags,\n metadata: { type },\n isBTSP: false,\n };\n}\n\n/**\n * Parse generic context (fallback for non-Claude-Code agents)\n * Splits on double newlines, treats as paragraphs\n * @param context - Raw context string\n * @returns Array of memory entries\n */\nexport function parseGenericContext(context: string): MemoryEntry[] {\n const entries: MemoryEntry[] = [];\n const now = Date.now();\n\n // Split on double newlines (paragraph boundaries)\n const blocks = context.split(/\\n\\n+/);\n\n for (const block of blocks) {\n const trimmed = block.trim();\n if (trimmed.length === 0) continue;\n\n entries.push(createEntry(trimmed, 'other', now));\n }\n\n return entries;\n}\n","/**\n * Content hashing utilities.\n * Uses SHA-256 for deduplication.\n */\n\nimport { createHash } from 'node:crypto';\n\n/**\n * Generate SHA-256 hash of content for deduplication.\n *\n * @param content - Content to hash\n * @returns 64-character hex string (SHA-256)\n *\n * @example\n * ```typescript\n * const hash = hashContent('Hello world');\n * console.log(hash.length); // 64\n * ```\n */\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n","/**\n * Token estimation utilities.\n * Uses whitespace heuristic (~90% accuracy vs GPT tokenizer).\n */\n\n/**\n * Estimate token count for text using heuristic.\n *\n * Approximation: 1 token ≈ 4 chars or 0.75 words\n * Provides ~90% accuracy compared to GPT tokenizer, sufficient for optimization heuristics.\n *\n * @param text - Text to count\n * @returns Estimated token count\n *\n * @example\n * ```typescript\n * const tokens = estimateTokens('Hello world');\n * console.log(tokens); // ~2\n * ```\n */\nexport function estimateTokens(text: string): number {\n if (!text || text.length === 0) {\n return 0;\n }\n\n // Split on whitespace to get words\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n const wordCount = words.length;\n\n // Character-based estimate\n const charCount = text.length;\n const charEstimate = Math.ceil(charCount / 4);\n\n // Word-based estimate\n const wordEstimate = Math.ceil(wordCount * 0.75);\n\n // Return the maximum of both estimates (more conservative)\n return Math.max(wordEstimate, charEstimate);\n}\n","/**\n * Budget-Aware Pruner - Token budget optimization\n *\n * Unlike SparsePruner which keeps top N% entries, BudgetPruner fits entries\n * within a target token budget using priority scoring that combines:\n * - TF-IDF relevance\n * - Engram decay\n * - Confidence state multipliers\n * - BTSP bypass (always included)\n *\n * Target use case: Real-time optimization for Opus model (~50K token budget)\n */\n\nimport type { RealtimeConfig } from '../types/config.js';\nimport type { MemoryEntry } from '../types/memory.js';\nimport type { PruneResult } from '../types/pruner.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport { createEngramScorer } from './engram-scorer.js';\n\nexport interface BudgetPrunerConfig {\n /** Target token budget */\n tokenBudget: number;\n /** Decay configuration */\n decay: {\n defaultTTL: number;\n decayThreshold: number;\n };\n /** State multipliers */\n states: {\n activeThreshold: number;\n readyThreshold: number;\n };\n}\n\nexport interface BudgetPruner {\n /**\n * Prune entries to fit within token budget\n * @param entries - Memory entries to prune\n * @param budget - Optional override budget (uses config default if not provided)\n * @returns Result with kept/removed entries and budget utilization\n */\n pruneToFit(entries: MemoryEntry[], budget?: number): PruneResult & { budgetUtilization: number };\n\n /**\n * Calculate priority score for an entry\n * @param entry - Entry to score\n * @param allEntries - All entries for TF-IDF calculation\n * @returns Priority score (higher = more important)\n */\n priorityScore(entry: MemoryEntry, allEntries: MemoryEntry[]): number;\n}\n\n/**\n * Create a budget-aware pruner instance\n * @param config - Pruner configuration\n * @returns BudgetPruner instance\n */\nexport function createBudgetPruner(config: BudgetPrunerConfig): BudgetPruner {\n const { tokenBudget, decay } = config;\n const engramScorer = createEngramScorer(decay);\n\n function tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length > 0);\n }\n\n function calculateTF(term: string, tokens: string[]): number {\n const count = tokens.filter((t) => t === term).length;\n // Sqrt capping to prevent common words from dominating\n return Math.sqrt(count);\n }\n\n function calculateIDF(term: string, allEntries: MemoryEntry[]): number {\n const totalDocs = allEntries.length;\n const docsWithTerm = allEntries.filter((entry) => {\n const tokens = tokenize(entry.content);\n return tokens.includes(term);\n }).length;\n\n if (docsWithTerm === 0) return 0;\n\n return Math.log(totalDocs / docsWithTerm);\n }\n\n function calculateTFIDF(entry: MemoryEntry, allEntries: MemoryEntry[]): number {\n const tokens = tokenize(entry.content);\n if (tokens.length === 0) return 0;\n\n const uniqueTerms = [...new Set(tokens)];\n let totalScore = 0;\n\n for (const term of uniqueTerms) {\n const tf = calculateTF(term, tokens);\n const idf = calculateIDF(term, allEntries);\n totalScore += tf * idf;\n }\n\n // Normalize by entry length\n return totalScore / tokens.length;\n }\n\n function getStateMultiplier(entry: MemoryEntry): number {\n // BTSP entries get max priority (handled separately, but keep high multiplier)\n if (entry.isBTSP) return 2.0;\n\n // State-based multipliers\n switch (entry.state) {\n case 'active':\n return 2.0;\n case 'ready':\n return 1.0;\n case 'silent':\n return 0.5;\n default:\n return 1.0;\n }\n }\n\n function priorityScore(entry: MemoryEntry, allEntries: MemoryEntry[]): number {\n const tfidf = calculateTFIDF(entry, allEntries);\n const currentScore = engramScorer.calculateScore(entry);\n const engramDecay = 1 - currentScore; // Lower decay = higher priority\n const stateMultiplier = getStateMultiplier(entry);\n\n // Priority = TF-IDF * (1 - decay) * state_multiplier\n // This balances relevance, recency, and confidence state\n return tfidf * (1 - engramDecay) * stateMultiplier;\n }\n\n function pruneToFit(\n entries: MemoryEntry[],\n budget: number = tokenBudget,\n ): PruneResult & { budgetUtilization: number } {\n if (entries.length === 0) {\n return {\n kept: [],\n removed: [],\n originalTokens: 0,\n prunedTokens: 0,\n budgetUtilization: 0,\n };\n }\n\n // Calculate original token count\n const originalTokens = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Step 1: Separate BTSP entries (always included, bypass budget)\n const btspEntries = entries.filter((e) => e.isBTSP);\n const regularEntries = entries.filter((e) => !e.isBTSP);\n\n const btspTokens = btspEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Step 2: Score regular entries\n const scored = regularEntries.map((entry) => ({\n entry,\n score: priorityScore(entry, entries),\n tokens: estimateTokens(entry.content),\n }));\n\n // Step 3: Sort by priority score descending\n scored.sort((a, b) => b.score - a.score);\n\n // Step 4: Greedy fill until budget exceeded\n const kept: MemoryEntry[] = [...btspEntries];\n const removed: MemoryEntry[] = [];\n let currentTokens = btspTokens;\n\n for (const item of scored) {\n if (currentTokens + item.tokens <= budget) {\n kept.push(item.entry);\n currentTokens += item.tokens;\n } else {\n removed.push(item.entry);\n }\n }\n\n const budgetUtilization = budget > 0 ? currentTokens / budget : 0;\n\n return {\n kept,\n removed,\n originalTokens,\n prunedTokens: currentTokens,\n budgetUtilization,\n };\n }\n\n return {\n pruneToFit,\n priorityScore,\n };\n}\n\n/**\n * Helper to create budget pruner from RealtimeConfig\n * @param realtimeConfig - Realtime configuration\n * @param decayConfig - Decay configuration\n * @param statesConfig - States configuration\n * @returns BudgetPruner instance\n */\nexport function createBudgetPrunerFromConfig(\n realtimeConfig: RealtimeConfig,\n decayConfig: { defaultTTL: number; decayThreshold: number },\n statesConfig: { activeThreshold: number; readyThreshold: number },\n): BudgetPruner {\n return createBudgetPruner({\n tokenBudget: realtimeConfig.tokenBudget,\n decay: decayConfig,\n states: statesConfig,\n });\n}\n","/**\n * Incremental Optimizer - Cache-based delta processing\n *\n * Optimizes performance for real-time scenarios by:\n * - Caching entry scores by content hash\n * - Only recomputing scores for new/changed entries\n * - Pre-computing and caching document frequency tables\n * - Periodically forcing full re-optimization to prevent drift\n *\n * Target: <50ms for incremental updates on 100K token contexts\n */\n\nimport type { MemoryEntry } from '../types/memory.js';\nimport type { PruneResult } from '../types/pruner.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport { type BudgetPrunerConfig, createBudgetPruner } from './budget-pruner.js';\nimport { getMetrics } from './metrics.js';\n\nexport interface IncrementalOptimizerConfig extends BudgetPrunerConfig {\n /** Force full re-optimization every N incremental updates */\n fullOptimizationInterval: number;\n}\n\nexport interface IncrementalOptimizerState {\n /** Entry cache keyed by content hash */\n entryCache: Map<string, { entry: MemoryEntry; score: number; timestamp: number }>;\n /** Document frequency table for IDF calculation */\n documentFrequency: Map<string, number>;\n /** Total document count for IDF */\n totalDocuments: number;\n /** Incremental update counter */\n updateCount: number;\n /** Last full optimization timestamp */\n lastFullOptimization: number;\n}\n\nexport interface IncrementalOptimizer {\n /**\n * Optimize incrementally (only process new/changed entries)\n * @param newEntries - New entries to add\n * @param budget - Optional budget override\n * @returns Prune result with budget utilization\n */\n optimizeIncremental(\n newEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number };\n\n /**\n * Optimize fully (recompute all scores)\n * @param allEntries - All entries to optimize\n * @param budget - Optional budget override\n * @returns Prune result with budget utilization\n */\n optimizeFull(\n allEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number };\n\n /**\n * Get current optimizer state (for serialization)\n * @returns Serializable state object\n */\n getState(): IncrementalOptimizerState;\n\n /**\n * Restore optimizer state (from serialization)\n * @param state - State to restore\n */\n restoreState(state: IncrementalOptimizerState): void;\n\n /**\n * Reset optimizer state (clear all caches)\n */\n reset(): void;\n\n /**\n * Get cache statistics\n * @returns Cache stats\n */\n getStats(): {\n cachedEntries: number;\n uniqueTerms: number;\n totalDocuments: number;\n updateCount: number;\n lastFullOptimization: number;\n };\n}\n\n/**\n * Create an incremental optimizer instance\n * @param config - Optimizer configuration\n * @returns IncrementalOptimizer instance\n */\nexport function createIncrementalOptimizer(\n config: IncrementalOptimizerConfig,\n): IncrementalOptimizer {\n const pruner = createBudgetPruner(config);\n const { fullOptimizationInterval } = config;\n\n // Internal state\n let state: IncrementalOptimizerState = {\n entryCache: new Map(),\n documentFrequency: new Map(),\n totalDocuments: 0,\n updateCount: 0,\n lastFullOptimization: Date.now(),\n };\n\n function tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length > 0);\n }\n\n /**\n * Update document frequency table incrementally\n */\n function updateDocumentFrequency(entries: MemoryEntry[], remove = false): void {\n for (const entry of entries) {\n const tokens = tokenize(entry.content);\n const uniqueTerms = [...new Set(tokens)];\n\n for (const term of uniqueTerms) {\n const current = state.documentFrequency.get(term) || 0;\n const updated = remove ? Math.max(0, current - 1) : current + 1;\n\n if (updated === 0) {\n state.documentFrequency.delete(term);\n } else {\n state.documentFrequency.set(term, updated);\n }\n }\n }\n\n state.totalDocuments += remove ? -entries.length : entries.length;\n state.totalDocuments = Math.max(0, state.totalDocuments);\n }\n\n /**\n * Check if entry is cached and still valid\n */\n function getCachedEntry(hash: string): MemoryEntry | null {\n const cached = state.entryCache.get(hash);\n if (!cached) return null;\n\n // Entry is valid if found in cache\n return cached.entry;\n }\n\n /**\n * Cache entry with score\n */\n function cacheEntry(entry: MemoryEntry, score: number): void {\n state.entryCache.set(entry.hash, {\n entry,\n score,\n timestamp: Date.now(),\n });\n }\n\n function optimizeIncremental(\n newEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number } {\n const startTime = Date.now();\n state.updateCount++;\n\n // Force full optimization if interval reached\n if (state.updateCount >= fullOptimizationInterval) {\n // Get all cached entries\n const allEntries = Array.from(state.entryCache.values()).map((c) => c.entry);\n return optimizeFull([...allEntries, ...newEntries], budget);\n }\n\n // Filter out already-cached entries\n const uncachedEntries: MemoryEntry[] = [];\n const cachedEntries: MemoryEntry[] = [];\n\n for (const entry of newEntries) {\n const cached = getCachedEntry(entry.hash);\n if (cached) {\n cachedEntries.push(cached);\n } else {\n uncachedEntries.push(entry);\n }\n }\n\n // Update document frequency for new entries only\n if (uncachedEntries.length > 0) {\n updateDocumentFrequency(uncachedEntries, false);\n }\n\n // Combine with cached entries for scoring context\n const allEntries = [...cachedEntries, ...uncachedEntries];\n\n // Score only uncached entries (reuse cached scores)\n for (const entry of uncachedEntries) {\n const score = pruner.priorityScore(entry, allEntries);\n cacheEntry(entry, score);\n }\n\n // Get all current entries (from cache + new)\n const currentEntries = Array.from(state.entryCache.values()).map((c) => c.entry);\n\n // Calculate tokens before\n const tokensBefore = currentEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Prune to fit budget\n const result = pruner.pruneToFit(currentEntries, budget);\n\n // Calculate tokens after\n const tokensAfter = result.kept.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Update cache: remove pruned entries\n for (const removed of result.removed) {\n state.entryCache.delete(removed.hash);\n }\n\n // Update document frequency to reflect removal\n if (result.removed.length > 0) {\n updateDocumentFrequency(result.removed, true);\n }\n\n // Record metrics\n const duration = Date.now() - startTime;\n const cacheHitRate = newEntries.length > 0 ? cachedEntries.length / newEntries.length : 0;\n\n getMetrics().recordOptimization({\n timestamp: Date.now(),\n duration,\n tokensBefore,\n tokensAfter,\n entriesProcessed: newEntries.length,\n entriesKept: result.kept.length,\n cacheHitRate,\n memoryUsage: process.memoryUsage().heapUsed,\n });\n\n return result;\n }\n\n function optimizeFull(\n allEntries: MemoryEntry[],\n budget?: number,\n ): PruneResult & { budgetUtilization: number } {\n const startTime = Date.now();\n\n // Calculate tokens before\n const tokensBefore = allEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Reset state\n state.entryCache.clear();\n state.documentFrequency.clear();\n state.totalDocuments = 0;\n state.updateCount = 0;\n state.lastFullOptimization = Date.now();\n\n // Rebuild document frequency table\n updateDocumentFrequency(allEntries, false);\n\n // Score and cache all entries\n for (const entry of allEntries) {\n const score = pruner.priorityScore(entry, allEntries);\n cacheEntry(entry, score);\n }\n\n // Prune to fit budget\n const result = pruner.pruneToFit(allEntries, budget);\n\n // Calculate tokens after\n const tokensAfter = result.kept.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n // Update cache: remove pruned entries\n for (const removed of result.removed) {\n state.entryCache.delete(removed.hash);\n }\n\n // Update document frequency to reflect removal\n if (result.removed.length > 0) {\n updateDocumentFrequency(result.removed, true);\n }\n\n // Record metrics\n const duration = Date.now() - startTime;\n\n getMetrics().recordOptimization({\n timestamp: Date.now(),\n duration,\n tokensBefore,\n tokensAfter,\n entriesProcessed: allEntries.length,\n entriesKept: result.kept.length,\n cacheHitRate: 0, // Full optimization has no cache hits\n memoryUsage: process.memoryUsage().heapUsed,\n });\n\n return result;\n }\n\n function getState(): IncrementalOptimizerState {\n return {\n entryCache: new Map(state.entryCache),\n documentFrequency: new Map(state.documentFrequency),\n totalDocuments: state.totalDocuments,\n updateCount: state.updateCount,\n lastFullOptimization: state.lastFullOptimization,\n };\n }\n\n function restoreState(restoredState: IncrementalOptimizerState): void {\n state = {\n entryCache: new Map(restoredState.entryCache),\n documentFrequency: new Map(restoredState.documentFrequency),\n totalDocuments: restoredState.totalDocuments,\n updateCount: restoredState.updateCount,\n lastFullOptimization: restoredState.lastFullOptimization,\n };\n }\n\n function reset(): void {\n state = {\n entryCache: new Map(),\n documentFrequency: new Map(),\n totalDocuments: 0,\n updateCount: 0,\n lastFullOptimization: Date.now(),\n };\n }\n\n function getStats() {\n return {\n cachedEntries: state.entryCache.size,\n uniqueTerms: state.documentFrequency.size,\n totalDocuments: state.totalDocuments,\n updateCount: state.updateCount,\n lastFullOptimization: state.lastFullOptimization,\n };\n }\n\n return {\n optimizeIncremental,\n optimizeFull,\n getState,\n restoreState,\n reset,\n getStats,\n };\n}\n","/**\n * Streaming Context Pipeline - Real-time sliding window buffer\n *\n * Maintains an optimized context in real-time by:\n * - Ingesting new content as it arrives\n * - Storing entries by priority internally (for eviction decisions)\n * - Outputting in chronological order (for conversation coherence)\n * - Evicting lowest-priority entries when budget exceeded\n * - Using IncrementalOptimizer for fast delta processing\n */\n\nimport type { MemoryEntry } from '../types/memory.js';\nimport { parseClaudeCodeContext } from '../utils/context-parser.js';\nimport { estimateTokens } from '../utils/tokenizer.js';\nimport {\n createIncrementalOptimizer,\n type IncrementalOptimizerConfig,\n} from './incremental-optimizer.js';\n\nexport interface ContextPipelineConfig extends IncrementalOptimizerConfig {\n /** Sliding window size (max entries to keep) */\n windowSize: number;\n}\n\nexport interface ContextPipelineStats {\n /** Total entries ingested */\n totalIngested: number;\n /** Current entry count */\n currentEntries: number;\n /** Current token count */\n currentTokens: number;\n /** Budget utilization (0.0-1.0) */\n budgetUtilization: number;\n /** Evicted entry count */\n evictedEntries: number;\n /** Optimizer stats */\n optimizer: {\n cachedEntries: number;\n uniqueTerms: number;\n updateCount: number;\n };\n}\n\nexport interface ContextPipeline {\n /**\n * Ingest new content into the pipeline\n * @param content - Raw content string\n * @param metadata - Optional metadata to attach to entries\n * @returns Number of entries ingested\n */\n ingest(content: string, metadata?: Record<string, unknown>): number;\n\n /**\n * Get current optimized context (chronologically ordered)\n * @returns Optimized context string\n */\n getContext(): string;\n\n /**\n * Get current entries (chronologically ordered)\n * @returns Array of memory entries\n */\n getEntries(): MemoryEntry[];\n\n /**\n * Get pipeline statistics\n * @returns Pipeline stats\n */\n getStats(): ContextPipelineStats;\n\n /**\n * Clear all entries and reset state\n */\n clear(): void;\n}\n\n/**\n * Create a context pipeline instance\n * @param config - Pipeline configuration\n * @returns ContextPipeline instance\n */\nexport function createContextPipeline(config: ContextPipelineConfig): ContextPipeline {\n const optimizer = createIncrementalOptimizer(config);\n const { windowSize, tokenBudget } = config;\n\n // Internal state\n let totalIngested = 0;\n let evictedEntries = 0;\n let currentEntries: MemoryEntry[] = [];\n let budgetUtilization = 0;\n\n function ingest(content: string, metadata: Record<string, unknown> = {}): number {\n // Parse content into entries\n const newEntries = parseClaudeCodeContext(content);\n\n if (newEntries.length === 0) return 0;\n\n // Attach metadata to entries\n const entriesWithMetadata = newEntries.map((entry) => ({\n ...entry,\n metadata: { ...entry.metadata, ...metadata },\n }));\n\n // Optimize incrementally\n const result = optimizer.optimizeIncremental(entriesWithMetadata, tokenBudget);\n\n // Update statistics\n totalIngested += newEntries.length;\n evictedEntries += result.removed.length;\n currentEntries = result.kept;\n budgetUtilization = result.budgetUtilization;\n\n // Enforce window size limit (keep most recent if exceeded)\n if (currentEntries.length > windowSize) {\n // Sort by timestamp descending (newest first)\n const sorted = [...currentEntries].sort((a, b) => b.timestamp - a.timestamp);\n const toKeep = sorted.slice(0, windowSize);\n const toRemove = sorted.slice(windowSize);\n\n currentEntries = toKeep;\n evictedEntries += toRemove.length;\n }\n\n return newEntries.length;\n }\n\n function getContext(): string {\n // Sort entries chronologically (oldest first)\n const sorted = [...currentEntries].sort((a, b) => a.timestamp - b.timestamp);\n return sorted.map((e) => e.content).join('\\n\\n');\n }\n\n function getEntries(): MemoryEntry[] {\n // Return entries chronologically (oldest first)\n return [...currentEntries].sort((a, b) => a.timestamp - b.timestamp);\n }\n\n function getStats(): ContextPipelineStats {\n const optimizerStats = optimizer.getStats();\n const currentTokens = currentEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);\n\n return {\n totalIngested,\n currentEntries: currentEntries.length,\n currentTokens,\n budgetUtilization,\n evictedEntries,\n optimizer: {\n cachedEntries: optimizerStats.cachedEntries,\n uniqueTerms: optimizerStats.uniqueTerms,\n updateCount: optimizerStats.updateCount,\n },\n };\n }\n\n function clear(): void {\n totalIngested = 0;\n evictedEntries = 0;\n currentEntries = [];\n budgetUtilization = 0;\n optimizer.reset();\n }\n\n return {\n ingest,\n getContext,\n getEntries,\n getStats,\n clear,\n };\n}\n","/**\n * File Tracker - Incremental file reading with byte position tracking\n *\n * Tracks read positions for files to enable efficient incremental reading.\n * Handles JSONL partial line buffering for incomplete writes.\n *\n * Use case: Monitor Claude Code session JSONL files and only read new lines\n * as they're appended, without re-reading the entire file.\n */\n\nimport { readFileSync, statSync } from 'node:fs';\n\nexport interface FilePosition {\n /** File path */\n path: string;\n /** Last read byte position */\n position: number;\n /** Partial line buffer (for JSONL incomplete writes) */\n partialLine: string;\n /** Last modification time */\n lastModified: number;\n /** File size at last read */\n lastSize: number;\n}\n\nexport interface FileTracker {\n /**\n * Read new content from file since last read\n * @param filePath - File to read\n * @returns New content as array of lines (empty if no new content)\n */\n readNewLines(filePath: string): string[];\n\n /**\n * Get current position for a file\n * @param filePath - File path\n * @returns File position or null if not tracked\n */\n getPosition(filePath: string): FilePosition | null;\n\n /**\n * Reset position for a file (start from beginning on next read)\n * @param filePath - File path\n */\n resetPosition(filePath: string): void;\n\n /**\n * Clear all tracked positions\n */\n clearAll(): void;\n\n /**\n * Get all tracked file paths\n * @returns Array of tracked file paths\n */\n getTrackedFiles(): string[];\n}\n\n/**\n * Create a file tracker instance\n * @returns FileTracker instance\n */\nexport function createFileTracker(): FileTracker {\n // Track positions by file path\n const positions = new Map<string, FilePosition>();\n\n function readNewLines(filePath: string): string[] {\n try {\n // Get current file stats\n const stats = statSync(filePath);\n const currentSize = stats.size;\n const currentModified = stats.mtimeMs;\n\n // Get or initialize position\n let pos = positions.get(filePath);\n\n if (!pos) {\n // First read: start from beginning\n pos = {\n path: filePath,\n position: 0,\n partialLine: '',\n lastModified: currentModified,\n lastSize: 0,\n };\n positions.set(filePath, pos);\n }\n\n // Check if file was truncated or is same size\n if (currentSize < pos.lastSize || currentSize === pos.position) {\n // File truncated or no new content\n if (currentSize < pos.lastSize) {\n // Reset position if truncated\n pos.position = 0;\n pos.partialLine = '';\n }\n return [];\n }\n\n // Read new content from last position\n const buffer = Buffer.alloc(currentSize - pos.position);\n const fd = readFileSync(filePath);\n fd.copy(buffer, 0, pos.position, currentSize);\n\n // Convert to string and combine with partial line\n const newContent = (pos.partialLine + buffer.toString('utf-8')).split('\\n');\n\n // Last element might be incomplete (no trailing newline yet)\n const partialLine = newContent.pop() || '';\n\n // Update position\n pos.position = currentSize;\n pos.partialLine = partialLine;\n pos.lastModified = currentModified;\n pos.lastSize = currentSize;\n\n // Return complete lines (filter empty)\n return newContent.filter((line) => line.trim().length > 0);\n } catch (_error) {\n // File doesn't exist or can't be read\n // Return empty array (fail silently for watcher use case)\n return [];\n }\n }\n\n function getPosition(filePath: string): FilePosition | null {\n return positions.get(filePath) || null;\n }\n\n function resetPosition(filePath: string): void {\n positions.delete(filePath);\n }\n\n function clearAll(): void {\n positions.clear();\n }\n\n function getTrackedFiles(): string[] {\n return Array.from(positions.keys());\n }\n\n return {\n readNewLines,\n getPosition,\n resetPosition,\n clearAll,\n getTrackedFiles,\n };\n}\n"],"mappings":"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","names":["appendFileSync","existsSync","config","memory","memory","config","logFile","log","statSync","config","config","allEntries","config","config","statSync","watcher","appendFileSync","existsSync"]}