kirograph 1.1.1 → 1.2.1

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.
Files changed (40) hide show
  1. package/README.md +3 -2
  2. package/dist/bin/commands/memory.js +12 -2
  3. package/dist/bin/commands/memory.js.map +2 -2
  4. package/dist/bin/commands/wiki.js +3 -2
  5. package/dist/bin/commands/wiki.js.map +2 -2
  6. package/dist/bin/kirograph.js +1 -1
  7. package/dist/config.js +8 -0
  8. package/dist/config.js.map +2 -2
  9. package/dist/db/memory-schema.sql +2 -1
  10. package/dist/mcp/handlers/memory.js +2 -1
  11. package/dist/mcp/handlers/memory.js.map +2 -2
  12. package/dist/mcp/handlers/wiki.js +4 -2
  13. package/dist/mcp/handlers/wiki.js.map +2 -2
  14. package/dist/mcp/tools.js +2 -1
  15. package/dist/mcp/tools.js.map +2 -2
  16. package/dist/memory/database.js +17 -4
  17. package/dist/memory/database.js.map +2 -2
  18. package/dist/memory/index.js +18 -1
  19. package/dist/memory/index.js.map +2 -2
  20. package/dist/memory/schema.js +35 -0
  21. package/dist/memory/schema.js.map +2 -2
  22. package/dist/memory/types.js.map +1 -1
  23. package/dist/patterns/loader.js +2 -146
  24. package/dist/patterns/loader.js.map +3 -3
  25. package/dist/security/manifest/plugins/maven.js +33 -3
  26. package/dist/security/manifest/plugins/maven.js.map +2 -2
  27. package/dist/security/manifest/plugins/npm.js +32 -3
  28. package/dist/security/manifest/plugins/npm.js.map +2 -2
  29. package/dist/shared/json-schema.js +136 -0
  30. package/dist/shared/json-schema.js.map +7 -0
  31. package/dist/shared/yaml.js +174 -0
  32. package/dist/shared/yaml.js.map +7 -0
  33. package/dist/wiki/index.js +3 -1
  34. package/dist/wiki/index.js.map +2 -2
  35. package/dist/wiki/lint.js +14 -1
  36. package/dist/wiki/lint.js.map +2 -2
  37. package/dist/wiki/typed-pages.js +67 -0
  38. package/dist/wiki/typed-pages.js.map +7 -0
  39. package/dist/wiki/types.js.map +1 -1
  40. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/database.ts"],
4
- "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Database layer\n *\n * Operates on mem_* tables within the same SQLite database as the code graph.\n * Completely isolated: no FK to core nodes/edges tables.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport type {\n MemSession,\n MemObservation,\n MemObservationInput,\n MemLink,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n MemRelation,\n MemRelationInput,\n MemPrompt,\n RelationType,\n} from './types';\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash('sha256').update(content).digest('hex');\n}\n\n// \u2500\u2500 MemoryDatabase \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryDatabase {\n private db: any;\n private initialized = false;\n\n constructor(db: any) {\n this.db = db;\n }\n\n /**\n * Apply memory schema. Called once when enableMemory is true.\n */\n initialize(): void {\n if (this.initialized) return;\n const schemaPath = path.join(__dirname, '../db/memory-schema.sql');\n const sql = fs.readFileSync(schemaPath, 'utf8');\n this.db.exec(sql);\n\n // Migration: add temporal columns if they don't exist (for existing databases)\n this.migrateTemporalColumns();\n this.migrateRelationsAndPrompts();\n\n this.initialized = true;\n }\n\n /**\n * Add temporal fact columns to mem_observations if missing.\n * Safe to run multiple times (uses IF NOT EXISTS pattern via try/catch).\n */\n private migrateTemporalColumns(): void {\n const columnsToAdd = [\n { name: 'valid_from', type: 'INTEGER' },\n { name: 'valid_until', type: 'INTEGER' },\n { name: 'superseded_by', type: 'TEXT' },\n { name: 'fact_type', type: \"TEXT DEFAULT 'observation'\" },\n { name: 'topic_key', type: 'TEXT' },\n { name: 'review_after', type: 'INTEGER' },\n ];\n\n for (const col of columnsToAdd) {\n try {\n this.db.exec(`ALTER TABLE mem_observations ADD COLUMN ${col.name} ${col.type}`);\n } catch {\n // Column already exists \u2014 ignore\n }\n }\n }\n\n private migrateRelationsAndPrompts(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS mem_relations (\n id TEXT PRIMARY KEY,\n observation_a TEXT NOT NULL REFERENCES mem_observations(id) ON DELETE CASCADE,\n observation_b TEXT NOT NULL REFERENCES mem_observations(id) ON DELETE CASCADE,\n relation TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 1.0,\n reason TEXT,\n evidence TEXT,\n judgment_status TEXT NOT NULL DEFAULT 'pending',\n judged_at INTEGER,\n created_at INTEGER NOT NULL,\n UNIQUE(observation_a, observation_b)\n );\n CREATE INDEX IF NOT EXISTS idx_mem_rel_a ON mem_relations(observation_a);\n CREATE INDEX IF NOT EXISTS idx_mem_rel_b ON mem_relations(observation_b);\n CREATE INDEX IF NOT EXISTS idx_mem_rel_status ON mem_relations(judgment_status);\n CREATE TABLE IF NOT EXISTS mem_prompts (\n id TEXT PRIMARY KEY,\n session_id TEXT REFERENCES mem_sessions(id) ON DELETE CASCADE,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_mem_prompts_session ON mem_prompts(session_id);\n `);\n }\n\n // \u2500\u2500 Watchmen \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Count non-summary observations created after the most recent summary.\n * Used by WatchmenChecker to decide if synthesis should trigger.\n */\n countSinceLastSummary(): number {\n const lastSummary = this.db.get(\n `SELECT created_at FROM mem_observations WHERE kind = 'summary' ORDER BY created_at DESC LIMIT 1`\n ) as { created_at: number } | undefined;\n\n const since = lastSummary?.created_at ?? 0;\n const row = this.db.get(\n `SELECT COUNT(*) as count FROM mem_observations WHERE kind != 'summary' AND created_at > ?`,\n [since]\n ) as { count: number };\n\n return row?.count ?? 0;\n }\n\n getObservationsSinceLastSummary(limit = 50): MemObservation[] {\n const lastSummary = this.db.get(\n `SELECT created_at FROM mem_observations WHERE kind = 'summary' ORDER BY created_at DESC LIMIT 1`\n ) as { created_at: number } | undefined;\n\n const since = lastSummary?.created_at ?? 0;\n const rows = this.db.all(\n `SELECT * FROM mem_observations WHERE kind != 'summary' AND created_at > ?\n ORDER BY created_at DESC LIMIT ?`,\n [since, limit]\n ) as any[];\n\n return rows.map(row => ({\n id: row.id,\n sessionId: row.session_id ?? undefined,\n content: row.content,\n contentRaw: row.content_raw ?? undefined,\n contentHash: row.content_hash,\n kind: row.kind,\n source: row.source,\n tags: row.tags ? JSON.parse(row.tags) : undefined,\n createdAt: row.created_at,\n }));\n }\n\n // \u2500\u2500 Sessions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get or create an active session. Auto-creates if no active session exists\n * within the timeout window.\n */\n getOrCreateSession(ide: string, cwd: string, timeoutSeconds = 7200): string {\n const cutoff = Date.now() - (timeoutSeconds * 1000);\n\n // Look for an active session (no ended_at, same ide, started within timeout)\n const existing = this.db.get(\n `SELECT id FROM mem_sessions\n WHERE ended_at IS NULL AND ide = ? AND started_at > ?\n ORDER BY started_at DESC LIMIT 1`,\n [ide, cutoff]\n );\n\n if (existing) return existing.id;\n\n // Auto-close any stale sessions for this IDE\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND ide = ?`,\n [Date.now(), ide]\n );\n\n // Create new session\n const id = generateId();\n this.db.run(\n `INSERT INTO mem_sessions (id, ide, cwd, started_at) VALUES (?, ?, ?, ?)`,\n [id, ide, cwd, Date.now()]\n );\n return id;\n }\n\n endSession(sessionId: string): void {\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE id = ? AND ended_at IS NULL`,\n [Date.now(), sessionId]\n );\n }\n\n getSession(sessionId: string): MemSession | null {\n const row = this.db.get('SELECT * FROM mem_sessions WHERE id = ?', [sessionId]);\n return row ? this.rowToSession(row) : null;\n }\n\n listSessions(limit = 10): MemSession[] {\n return this.db.all(\n 'SELECT * FROM mem_sessions ORDER BY started_at DESC LIMIT ?',\n [limit]\n ).map(this.rowToSession);\n }\n\n // \u2500\u2500 Observations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert an observation. Returns the ID, or null if a duplicate (same content_hash).\n */\n insertObservation(\n content: string,\n opts: {\n contentRaw?: string;\n kind?: string;\n source?: string;\n tags?: string[];\n sessionId?: string;\n topicKey?: string;\n reviewAfter?: number;\n } = {}\n ): string | null {\n const id = generateId();\n const contentHash = hashContent(content);\n const kind = opts.kind ?? 'note';\n const source = opts.source ?? 'manual';\n const tags = opts.tags ? JSON.stringify(opts.tags) : null;\n const now = Date.now();\n\n try {\n this.db.run(\n `INSERT INTO mem_observations (id, session_id, content, content_raw, content_hash, kind, source, tags, created_at, topic_key, review_after)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, opts.sessionId ?? null, content, opts.contentRaw ?? null, contentHash, kind, source, tags, now, opts.topicKey ?? null, opts.reviewAfter ?? null]\n );\n } catch (err: any) {\n // Unique constraint on content_hash \u2014 duplicate, skip silently\n if (err.message?.includes('UNIQUE constraint failed') || err.message?.includes('unique')) {\n return null;\n }\n throw err;\n }\n\n // Keep FTS in sync\n this.db.run(\n `INSERT INTO mem_fts (id, content, kind) VALUES (?, ?, ?)`,\n [id, content, kind]\n );\n\n return id;\n }\n\n getObservation(id: string): MemObservation | null {\n const row = this.db.get('SELECT * FROM mem_observations WHERE id = ?', [id]);\n return row ? this.rowToObservation(row) : null;\n }\n\n getObservationsBySession(sessionId: string, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE session_id = ? ORDER BY created_at DESC LIMIT ?',\n [sessionId, limit]\n ).map(this.rowToObservation);\n }\n\n getObservationsByTimeRange(from: number, to: number, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?',\n [from, to, limit]\n ).map(this.rowToObservation);\n }\n\n // \u2500\u2500 FTS Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n searchFTS(query: string, opts: MemSearchOptions = {}): ScoredObservation[] {\n const { limit = 10, kind, sessionId, asOf } = opts;\n\n // Sanitize for FTS5\n const safe = query\n .replace(/\\b(AND|OR|NOT)\\b/g, ' ')\n .replace(/['\"*()?\\-+^~:{}\\\\\\.\\/,\\[\\]]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (!safe) return [];\n\n const ftsQuery = safe + '*';\n const safeLimit = Math.max(1, Math.floor(Number(limit)));\n\n const conditions: string[] = [];\n const params: any[] = [];\n\n if (kind) {\n conditions.push('o.kind = ?');\n params.push(kind);\n }\n if (sessionId) {\n conditions.push('o.session_id = ?');\n params.push(sessionId);\n }\n\n // Temporal filtering: exclude expired and superseded facts\n if (asOf) {\n conditions.push('(o.valid_from IS NULL OR o.valid_from <= ?)');\n params.push(asOf);\n conditions.push('(o.valid_until IS NULL OR o.valid_until > ?)');\n params.push(asOf);\n conditions.push('o.superseded_by IS NULL');\n }\n\n const where = conditions.length > 0 ? `AND ${conditions.join(' AND ')}` : '';\n\n const rows = this.db.all(\n `SELECT o.*, rank FROM mem_fts f\n JOIN mem_observations o ON o.id = f.id\n WHERE mem_fts MATCH '${ftsQuery}' ${where}\n ORDER BY rank\n LIMIT ${safeLimit}`,\n params\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: Math.abs(row.rank ?? 0),\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n linkToSymbol(observationId: string, qualifiedName: string, relevance = 1.0): void {\n this.db.run(\n `INSERT OR IGNORE INTO mem_links (observation_id, qualified_name, relevance)\n VALUES (?, ?, ?)`,\n [observationId, qualifiedName, relevance]\n );\n }\n\n linkToSymbols(observationId: string, qualifiedNames: string[]): void {\n for (const qn of qualifiedNames) {\n this.linkToSymbol(observationId, qn);\n }\n }\n\n /**\n * Get observations linked to a symbol by qualified_name.\n * Resolves through mem_links \u2014 no dependency on node IDs.\n */\n getLinkedObservations(qualifiedName: string, limit = 10): ScoredObservation[] {\n const rows = this.db.all(\n `SELECT o.*, l.relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name = ?\n ORDER BY l.relevance DESC, o.created_at DESC\n LIMIT ?`,\n [qualifiedName, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n /**\n * Get observations linked to any of the given qualified names.\n * Used by kirograph_context and kirograph_impact to surface memory.\n */\n getLinkedObservationsForSymbols(qualifiedNames: string[], limit = 5): ScoredObservation[] {\n if (qualifiedNames.length === 0) return [];\n const placeholders = qualifiedNames.map(() => '?').join(',');\n const rows = this.db.all(\n `SELECT DISTINCT o.*, MAX(l.relevance) as relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name IN (${placeholders})\n GROUP BY o.id\n ORDER BY relevance DESC, o.created_at DESC\n LIMIT ?`,\n [...qualifiedNames, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Vectors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertVector(observationId: string, embedding: Buffer, model: string): void {\n this.db.run(\n `INSERT OR REPLACE INTO mem_vectors (observation_id, embedding, model, created_at)\n VALUES (?, ?, ?, ?)`,\n [observationId, embedding, model, Date.now()]\n );\n }\n\n getVector(observationId: string): { embedding: Buffer; model: string } | null {\n const row = this.db.get(\n 'SELECT embedding, model FROM mem_vectors WHERE observation_id = ?',\n [observationId]\n );\n return row ? { embedding: row.embedding, model: row.model } : null;\n }\n\n getVectorCount(): number {\n const row = this.db.get('SELECT COUNT(*) as count FROM mem_vectors');\n return row?.count ?? 0;\n }\n\n getVectorModelMismatch(currentModel: string): number {\n const row = this.db.get(\n 'SELECT COUNT(*) as count FROM mem_vectors WHERE model != ?',\n [currentModel]\n );\n return row?.count ?? 0;\n }\n\n getAllVectors(model?: string): Array<{ observationId: string; embedding: Buffer }> {\n const sql = model\n ? 'SELECT observation_id, embedding FROM mem_vectors WHERE model = ?'\n : 'SELECT observation_id, embedding FROM mem_vectors';\n const params = model ? [model] : [];\n return this.db.all(sql, params).map((row: any) => ({\n observationId: row.observation_id,\n embedding: row.embedding,\n }));\n }\n\n deleteVectors(): void {\n this.db.run('DELETE FROM mem_vectors');\n }\n\n // \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertRelation(input: MemRelationInput): string {\n const id = generateId();\n this.db.run(\n `INSERT OR REPLACE INTO mem_relations (id, observation_a, observation_b, relation, confidence, reason, evidence, judgment_status, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?)`,\n [id, input.observationA, input.observationB, input.relation, input.confidence ?? 1.0, input.reason ?? null, input.evidence ?? null, Date.now()]\n );\n return id;\n }\n\n judgeRelation(id: string, relation: RelationType, confidence: number, reason?: string, evidence?: string): void {\n this.db.run(\n `UPDATE mem_relations SET relation = ?, confidence = ?, reason = ?, evidence = ?, judgment_status = 'judged', judged_at = ? WHERE id = ?`,\n [relation, confidence, reason ?? null, evidence ?? null, Date.now(), id]\n );\n }\n\n ignoreRelation(id: string): void {\n this.db.run(`UPDATE mem_relations SET judgment_status = 'ignored' WHERE id = ?`, [id]);\n }\n\n getPendingRelations(limit = 20): MemRelation[] {\n return this.db.all(\n `SELECT * FROM mem_relations WHERE judgment_status = 'pending' ORDER BY created_at DESC LIMIT ?`,\n [limit]\n ).map((row: any) => this.rowToRelation(row));\n }\n\n getRelationsForObservations(ids: string[]): Map<string, MemRelation[]> {\n if (ids.length === 0) return new Map();\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db.all(\n `SELECT * FROM mem_relations WHERE observation_a IN (${placeholders}) OR observation_b IN (${placeholders})`,\n [...ids, ...ids]\n ) as any[];\n const map = new Map<string, MemRelation[]>();\n for (const row of rows) {\n const rel = this.rowToRelation(row);\n for (const obsId of [row.observation_a, row.observation_b]) {\n if (ids.includes(obsId)) {\n if (!map.has(obsId)) map.set(obsId, []);\n map.get(obsId)!.push(rel);\n }\n }\n }\n return map;\n }\n\n resolveObservationByTopicKey(topicKey: string): MemObservation | null {\n const row = this.db.get('SELECT * FROM mem_observations WHERE topic_key = ? LIMIT 1', [topicKey]);\n return row ? this.rowToObservation(row) : null;\n }\n\n private rowToRelation(row: any): MemRelation {\n return {\n id: row.id,\n observationA: row.observation_a,\n observationB: row.observation_b,\n relation: row.relation,\n confidence: row.confidence,\n reason: row.reason ?? undefined,\n evidence: row.evidence ?? undefined,\n judgmentStatus: row.judgment_status,\n judgedAt: row.judged_at ?? undefined,\n createdAt: row.created_at,\n };\n }\n\n // \u2500\u2500 Review \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getObservationsForReview(limit = 20): MemObservation[] {\n const now = Date.now();\n return this.db.all(\n `SELECT * FROM mem_observations WHERE review_after IS NOT NULL AND review_after < ? AND superseded_by IS NULL ORDER BY review_after ASC LIMIT ?`,\n [now, limit]\n ).map((row: any) => this.rowToObservation(row));\n }\n\n markReviewed(id: string): void {\n this.db.run(`UPDATE mem_observations SET review_after = NULL WHERE id = ?`, [id]);\n }\n\n // \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertPrompt(sessionId: string | undefined, content: string): string {\n const id = generateId();\n this.db.run(\n `INSERT INTO mem_prompts (id, session_id, content, created_at) VALUES (?, ?, ?, ?)`,\n [id, sessionId ?? null, content, Date.now()]\n );\n return id;\n }\n\n getPromptsBySession(sessionId: string, limit = 20): MemPrompt[] {\n return this.db.all(\n `SELECT * FROM mem_prompts WHERE session_id = ? ORDER BY created_at DESC LIMIT ?`,\n [sessionId, limit]\n ).map((row: any) => ({ id: row.id, sessionId: row.session_id ?? undefined, content: row.content, createdAt: row.created_at }));\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(currentModel?: string): MemStats {\n const sessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions')?.c ?? 0;\n const activeSessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions WHERE ended_at IS NULL')?.c ?? 0;\n const observations = this.db.get('SELECT COUNT(*) as c FROM mem_observations')?.c ?? 0;\n const links = this.db.get('SELECT COUNT(*) as c FROM mem_links')?.c ?? 0;\n const vectors = this.db.get('SELECT COUNT(*) as c FROM mem_vectors')?.c ?? 0;\n const mismatch = currentModel ? this.getVectorModelMismatch(currentModel) : 0;\n const relations = this.db.get('SELECT COUNT(*) as c FROM mem_relations')?.c ?? 0;\n const pendingConflicts = this.db.get(\"SELECT COUNT(*) as c FROM mem_relations WHERE judgment_status = 'pending'\")?.c ?? 0;\n\n return {\n sessions,\n activeSessions,\n observations,\n links,\n vectors,\n embeddableCount: observations,\n modelMismatch: mismatch > 0,\n currentModel,\n relations,\n pendingConflicts,\n };\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Delete observations older than the given timestamp.\n * Cascades to mem_links and mem_vectors via FK.\n */\n prune(olderThan: number): number {\n // Get IDs to delete (for FTS cleanup)\n const ids = this.db.all(\n 'SELECT id FROM mem_observations WHERE created_at < ?',\n [olderThan]\n ).map((r: any) => r.id);\n\n if (ids.length === 0) return 0;\n\n const placeholders = ids.map(() => '?').join(',');\n this.db.run(`DELETE FROM mem_fts WHERE id IN (${placeholders})`, ids);\n this.db.run(`DELETE FROM mem_observations WHERE created_at < ?`, [olderThan]);\n\n // Clean up empty sessions\n this.db.run(\n `DELETE FROM mem_sessions WHERE id NOT IN (SELECT DISTINCT session_id FROM mem_observations WHERE session_id IS NOT NULL)`\n );\n\n return ids.length;\n }\n\n /**\n * Find stale links where qualified_name no longer exists in the nodes table.\n */\n findStaleLinks(): MemLink[] {\n const rows = this.db.all(\n `SELECT l.* FROM mem_links l\n LEFT JOIN nodes n ON n.qualified_name = l.qualified_name\n WHERE n.id IS NULL`\n );\n return rows.map((row: any) => ({\n observationId: row.observation_id,\n qualifiedName: row.qualified_name,\n relevance: row.relevance,\n }));\n }\n\n /**\n * Remove stale links (where qualified_name no longer resolves).\n */\n removeStaleLinks(): number {\n const result = this.db.run(\n `DELETE FROM mem_links WHERE qualified_name NOT IN (SELECT DISTINCT qualified_name FROM nodes)`\n );\n return result?.changes ?? 0;\n }\n\n /**\n * Close stale sessions (started but never ended, older than timeout).\n */\n closeStaleSessionsOlderThan(timeoutMs: number): number {\n const cutoff = Date.now() - timeoutMs;\n const result = this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND started_at < ?`,\n [Date.now(), cutoff]\n );\n return result?.changes ?? 0;\n }\n\n // \u2500\u2500 Privacy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Strip <private>...</private> blocks from text.\n */\n static stripPrivate(text: string): string {\n return text.replace(/<private>[\\s\\S]*?<\\/private>/gi, '').trim();\n }\n\n // \u2500\u2500 Row mappers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private rowToSession(row: any): MemSession {\n return {\n id: row.id,\n ide: row.ide ?? undefined,\n cwd: row.cwd ?? undefined,\n startedAt: row.started_at,\n endedAt: row.ended_at ?? undefined,\n };\n }\n\n private rowToObservation(row: any): MemObservation {\n return {\n id: row.id,\n sessionId: row.session_id ?? undefined,\n content: row.content,\n contentRaw: row.content_raw ?? undefined,\n contentHash: row.content_hash,\n kind: row.kind,\n source: row.source,\n tags: row.tags ? JSON.parse(row.tags) : undefined,\n createdAt: row.created_at,\n validFrom: row.valid_from ?? undefined,\n validUntil: row.valid_until ?? undefined,\n supersededBy: row.superseded_by ?? undefined,\n factType: row.fact_type ?? undefined,\n topicKey: row.topic_key ?? undefined,\n reviewAfter: row.review_after ?? undefined,\n };\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Database layer\n *\n * Operates on mem_* tables within the same SQLite database as the code graph.\n * Completely isolated: no FK to core nodes/edges tables.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport type {\n MemSession,\n MemObservation,\n MemObservationInput,\n MemLink,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n MemRelation,\n MemRelationInput,\n MemPrompt,\n RelationType,\n} from './types';\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash('sha256').update(content).digest('hex');\n}\n\n// \u2500\u2500 MemoryDatabase \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryDatabase {\n private db: any;\n private initialized = false;\n\n constructor(db: any) {\n this.db = db;\n }\n\n /**\n * Apply memory schema. Called once when enableMemory is true.\n */\n initialize(): void {\n if (this.initialized) return;\n const schemaPath = path.join(__dirname, '../db/memory-schema.sql');\n const sql = fs.readFileSync(schemaPath, 'utf8');\n this.db.exec(sql);\n\n // Migration: add temporal columns if they don't exist (for existing databases)\n this.migrateTemporalColumns();\n this.migrateRelationsAndPrompts();\n this.migrateFieldsColumn();\n\n this.initialized = true;\n }\n\n /**\n * Add temporal fact columns to mem_observations if missing.\n * Safe to run multiple times (uses IF NOT EXISTS pattern via try/catch).\n */\n private migrateTemporalColumns(): void {\n const columnsToAdd = [\n { name: 'valid_from', type: 'INTEGER' },\n { name: 'valid_until', type: 'INTEGER' },\n { name: 'superseded_by', type: 'TEXT' },\n { name: 'fact_type', type: \"TEXT DEFAULT 'observation'\" },\n { name: 'topic_key', type: 'TEXT' },\n { name: 'review_after', type: 'INTEGER' },\n ];\n\n for (const col of columnsToAdd) {\n try {\n this.db.exec(`ALTER TABLE mem_observations ADD COLUMN ${col.name} ${col.type}`);\n } catch {\n // Column already exists \u2014 ignore\n }\n }\n }\n\n /**\n * Add the `fields` column (structured extra fields, JSON-serialized) to\n * mem_observations if missing. Backs memorySchemaValidation.\n */\n private migrateFieldsColumn(): void {\n try {\n this.db.exec(`ALTER TABLE mem_observations ADD COLUMN fields TEXT`);\n } catch {\n // Column already exists \u2014 ignore\n }\n }\n\n private migrateRelationsAndPrompts(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS mem_relations (\n id TEXT PRIMARY KEY,\n observation_a TEXT NOT NULL REFERENCES mem_observations(id) ON DELETE CASCADE,\n observation_b TEXT NOT NULL REFERENCES mem_observations(id) ON DELETE CASCADE,\n relation TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 1.0,\n reason TEXT,\n evidence TEXT,\n judgment_status TEXT NOT NULL DEFAULT 'pending',\n judged_at INTEGER,\n created_at INTEGER NOT NULL,\n UNIQUE(observation_a, observation_b)\n );\n CREATE INDEX IF NOT EXISTS idx_mem_rel_a ON mem_relations(observation_a);\n CREATE INDEX IF NOT EXISTS idx_mem_rel_b ON mem_relations(observation_b);\n CREATE INDEX IF NOT EXISTS idx_mem_rel_status ON mem_relations(judgment_status);\n CREATE TABLE IF NOT EXISTS mem_prompts (\n id TEXT PRIMARY KEY,\n session_id TEXT REFERENCES mem_sessions(id) ON DELETE CASCADE,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_mem_prompts_session ON mem_prompts(session_id);\n `);\n }\n\n // \u2500\u2500 Watchmen \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Count non-summary observations created after the most recent summary.\n * Used by WatchmenChecker to decide if synthesis should trigger.\n */\n countSinceLastSummary(): number {\n const lastSummary = this.db.get(\n `SELECT created_at FROM mem_observations WHERE kind = 'summary' ORDER BY created_at DESC LIMIT 1`\n ) as { created_at: number } | undefined;\n\n const since = lastSummary?.created_at ?? 0;\n const row = this.db.get(\n `SELECT COUNT(*) as count FROM mem_observations WHERE kind != 'summary' AND created_at > ?`,\n [since]\n ) as { count: number };\n\n return row?.count ?? 0;\n }\n\n getObservationsSinceLastSummary(limit = 50): MemObservation[] {\n const lastSummary = this.db.get(\n `SELECT created_at FROM mem_observations WHERE kind = 'summary' ORDER BY created_at DESC LIMIT 1`\n ) as { created_at: number } | undefined;\n\n const since = lastSummary?.created_at ?? 0;\n const rows = this.db.all(\n `SELECT * FROM mem_observations WHERE kind != 'summary' AND created_at > ?\n ORDER BY created_at DESC LIMIT ?`,\n [since, limit]\n ) as any[];\n\n return rows.map(row => ({\n id: row.id,\n sessionId: row.session_id ?? undefined,\n content: row.content,\n contentRaw: row.content_raw ?? undefined,\n contentHash: row.content_hash,\n kind: row.kind,\n source: row.source,\n tags: row.tags ? JSON.parse(row.tags) : undefined,\n createdAt: row.created_at,\n }));\n }\n\n // \u2500\u2500 Sessions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get or create an active session. Auto-creates if no active session exists\n * within the timeout window.\n */\n getOrCreateSession(ide: string, cwd: string, timeoutSeconds = 7200): string {\n const cutoff = Date.now() - (timeoutSeconds * 1000);\n\n // Look for an active session (no ended_at, same ide, started within timeout)\n const existing = this.db.get(\n `SELECT id FROM mem_sessions\n WHERE ended_at IS NULL AND ide = ? AND started_at > ?\n ORDER BY started_at DESC LIMIT 1`,\n [ide, cutoff]\n );\n\n if (existing) return existing.id;\n\n // Auto-close any stale sessions for this IDE\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND ide = ?`,\n [Date.now(), ide]\n );\n\n // Create new session\n const id = generateId();\n this.db.run(\n `INSERT INTO mem_sessions (id, ide, cwd, started_at) VALUES (?, ?, ?, ?)`,\n [id, ide, cwd, Date.now()]\n );\n return id;\n }\n\n endSession(sessionId: string): void {\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE id = ? AND ended_at IS NULL`,\n [Date.now(), sessionId]\n );\n }\n\n getSession(sessionId: string): MemSession | null {\n const row = this.db.get('SELECT * FROM mem_sessions WHERE id = ?', [sessionId]);\n return row ? this.rowToSession(row) : null;\n }\n\n listSessions(limit = 10): MemSession[] {\n return this.db.all(\n 'SELECT * FROM mem_sessions ORDER BY started_at DESC LIMIT ?',\n [limit]\n ).map(this.rowToSession);\n }\n\n // \u2500\u2500 Observations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert an observation. Returns the ID, or null if a duplicate (same content_hash).\n */\n insertObservation(\n content: string,\n opts: {\n contentRaw?: string;\n kind?: string;\n source?: string;\n tags?: string[];\n sessionId?: string;\n topicKey?: string;\n reviewAfter?: number;\n fields?: Record<string, unknown>;\n } = {}\n ): string | null {\n const id = generateId();\n const contentHash = hashContent(content);\n const kind = opts.kind ?? 'note';\n const source = opts.source ?? 'manual';\n const tags = opts.tags ? JSON.stringify(opts.tags) : null;\n const fields = opts.fields ? JSON.stringify(opts.fields) : null;\n const now = Date.now();\n\n try {\n this.db.run(\n `INSERT INTO mem_observations (id, session_id, content, content_raw, content_hash, kind, source, tags, created_at, topic_key, review_after, fields)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, opts.sessionId ?? null, content, opts.contentRaw ?? null, contentHash, kind, source, tags, now, opts.topicKey ?? null, opts.reviewAfter ?? null, fields]\n );\n } catch (err: any) {\n // Unique constraint on content_hash \u2014 duplicate, skip silently\n if (err.message?.includes('UNIQUE constraint failed') || err.message?.includes('unique')) {\n return null;\n }\n throw err;\n }\n\n // Keep FTS in sync\n this.db.run(\n `INSERT INTO mem_fts (id, content, kind) VALUES (?, ?, ?)`,\n [id, content, kind]\n );\n\n return id;\n }\n\n getObservation(id: string): MemObservation | null {\n const row = this.db.get('SELECT * FROM mem_observations WHERE id = ?', [id]);\n return row ? this.rowToObservation(row) : null;\n }\n\n getObservationsBySession(sessionId: string, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE session_id = ? ORDER BY created_at DESC LIMIT ?',\n [sessionId, limit]\n ).map(this.rowToObservation);\n }\n\n getObservationsByTimeRange(from: number, to: number, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?',\n [from, to, limit]\n ).map(this.rowToObservation);\n }\n\n // \u2500\u2500 FTS Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n searchFTS(query: string, opts: MemSearchOptions = {}): ScoredObservation[] {\n const { limit = 10, kind, sessionId, asOf } = opts;\n\n // Sanitize for FTS5\n const safe = query\n .replace(/\\b(AND|OR|NOT)\\b/g, ' ')\n .replace(/['\"*()?\\-+^~:{}\\\\\\.\\/,\\[\\]]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (!safe) return [];\n\n const ftsQuery = safe + '*';\n const safeLimit = Math.max(1, Math.floor(Number(limit)));\n\n const conditions: string[] = [];\n const params: any[] = [];\n\n if (kind) {\n conditions.push('o.kind = ?');\n params.push(kind);\n }\n if (sessionId) {\n conditions.push('o.session_id = ?');\n params.push(sessionId);\n }\n\n // Temporal filtering: exclude expired and superseded facts\n if (asOf) {\n conditions.push('(o.valid_from IS NULL OR o.valid_from <= ?)');\n params.push(asOf);\n conditions.push('(o.valid_until IS NULL OR o.valid_until > ?)');\n params.push(asOf);\n conditions.push('o.superseded_by IS NULL');\n }\n\n const where = conditions.length > 0 ? `AND ${conditions.join(' AND ')}` : '';\n\n const rows = this.db.all(\n `SELECT o.*, rank FROM mem_fts f\n JOIN mem_observations o ON o.id = f.id\n WHERE mem_fts MATCH '${ftsQuery}' ${where}\n ORDER BY rank\n LIMIT ${safeLimit}`,\n params\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: Math.abs(row.rank ?? 0),\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n linkToSymbol(observationId: string, qualifiedName: string, relevance = 1.0): void {\n this.db.run(\n `INSERT OR IGNORE INTO mem_links (observation_id, qualified_name, relevance)\n VALUES (?, ?, ?)`,\n [observationId, qualifiedName, relevance]\n );\n }\n\n linkToSymbols(observationId: string, qualifiedNames: string[]): void {\n for (const qn of qualifiedNames) {\n this.linkToSymbol(observationId, qn);\n }\n }\n\n /**\n * Get observations linked to a symbol by qualified_name.\n * Resolves through mem_links \u2014 no dependency on node IDs.\n */\n getLinkedObservations(qualifiedName: string, limit = 10): ScoredObservation[] {\n const rows = this.db.all(\n `SELECT o.*, l.relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name = ?\n ORDER BY l.relevance DESC, o.created_at DESC\n LIMIT ?`,\n [qualifiedName, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n /**\n * Get observations linked to any of the given qualified names.\n * Used by kirograph_context and kirograph_impact to surface memory.\n */\n getLinkedObservationsForSymbols(qualifiedNames: string[], limit = 5): ScoredObservation[] {\n if (qualifiedNames.length === 0) return [];\n const placeholders = qualifiedNames.map(() => '?').join(',');\n const rows = this.db.all(\n `SELECT DISTINCT o.*, MAX(l.relevance) as relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name IN (${placeholders})\n GROUP BY o.id\n ORDER BY relevance DESC, o.created_at DESC\n LIMIT ?`,\n [...qualifiedNames, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Vectors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertVector(observationId: string, embedding: Buffer, model: string): void {\n this.db.run(\n `INSERT OR REPLACE INTO mem_vectors (observation_id, embedding, model, created_at)\n VALUES (?, ?, ?, ?)`,\n [observationId, embedding, model, Date.now()]\n );\n }\n\n getVector(observationId: string): { embedding: Buffer; model: string } | null {\n const row = this.db.get(\n 'SELECT embedding, model FROM mem_vectors WHERE observation_id = ?',\n [observationId]\n );\n return row ? { embedding: row.embedding, model: row.model } : null;\n }\n\n getVectorCount(): number {\n const row = this.db.get('SELECT COUNT(*) as count FROM mem_vectors');\n return row?.count ?? 0;\n }\n\n getVectorModelMismatch(currentModel: string): number {\n const row = this.db.get(\n 'SELECT COUNT(*) as count FROM mem_vectors WHERE model != ?',\n [currentModel]\n );\n return row?.count ?? 0;\n }\n\n getAllVectors(model?: string): Array<{ observationId: string; embedding: Buffer }> {\n const sql = model\n ? 'SELECT observation_id, embedding FROM mem_vectors WHERE model = ?'\n : 'SELECT observation_id, embedding FROM mem_vectors';\n const params = model ? [model] : [];\n return this.db.all(sql, params).map((row: any) => ({\n observationId: row.observation_id,\n embedding: row.embedding,\n }));\n }\n\n deleteVectors(): void {\n this.db.run('DELETE FROM mem_vectors');\n }\n\n // \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertRelation(input: MemRelationInput): string {\n const id = generateId();\n this.db.run(\n `INSERT OR REPLACE INTO mem_relations (id, observation_a, observation_b, relation, confidence, reason, evidence, judgment_status, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?)`,\n [id, input.observationA, input.observationB, input.relation, input.confidence ?? 1.0, input.reason ?? null, input.evidence ?? null, Date.now()]\n );\n return id;\n }\n\n judgeRelation(id: string, relation: RelationType, confidence: number, reason?: string, evidence?: string): void {\n this.db.run(\n `UPDATE mem_relations SET relation = ?, confidence = ?, reason = ?, evidence = ?, judgment_status = 'judged', judged_at = ? WHERE id = ?`,\n [relation, confidence, reason ?? null, evidence ?? null, Date.now(), id]\n );\n }\n\n ignoreRelation(id: string): void {\n this.db.run(`UPDATE mem_relations SET judgment_status = 'ignored' WHERE id = ?`, [id]);\n }\n\n getPendingRelations(limit = 20): MemRelation[] {\n return this.db.all(\n `SELECT * FROM mem_relations WHERE judgment_status = 'pending' ORDER BY created_at DESC LIMIT ?`,\n [limit]\n ).map((row: any) => this.rowToRelation(row));\n }\n\n getRelationsForObservations(ids: string[]): Map<string, MemRelation[]> {\n if (ids.length === 0) return new Map();\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db.all(\n `SELECT * FROM mem_relations WHERE observation_a IN (${placeholders}) OR observation_b IN (${placeholders})`,\n [...ids, ...ids]\n ) as any[];\n const map = new Map<string, MemRelation[]>();\n for (const row of rows) {\n const rel = this.rowToRelation(row);\n for (const obsId of [row.observation_a, row.observation_b]) {\n if (ids.includes(obsId)) {\n if (!map.has(obsId)) map.set(obsId, []);\n map.get(obsId)!.push(rel);\n }\n }\n }\n return map;\n }\n\n resolveObservationByTopicKey(topicKey: string): MemObservation | null {\n const row = this.db.get('SELECT * FROM mem_observations WHERE topic_key = ? LIMIT 1', [topicKey]);\n return row ? this.rowToObservation(row) : null;\n }\n\n private rowToRelation(row: any): MemRelation {\n return {\n id: row.id,\n observationA: row.observation_a,\n observationB: row.observation_b,\n relation: row.relation,\n confidence: row.confidence,\n reason: row.reason ?? undefined,\n evidence: row.evidence ?? undefined,\n judgmentStatus: row.judgment_status,\n judgedAt: row.judged_at ?? undefined,\n createdAt: row.created_at,\n };\n }\n\n // \u2500\u2500 Review \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getObservationsForReview(limit = 20): MemObservation[] {\n const now = Date.now();\n return this.db.all(\n `SELECT * FROM mem_observations WHERE review_after IS NOT NULL AND review_after < ? AND superseded_by IS NULL ORDER BY review_after ASC LIMIT ?`,\n [now, limit]\n ).map((row: any) => this.rowToObservation(row));\n }\n\n markReviewed(id: string): void {\n this.db.run(`UPDATE mem_observations SET review_after = NULL WHERE id = ?`, [id]);\n }\n\n // \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertPrompt(sessionId: string | undefined, content: string): string {\n const id = generateId();\n this.db.run(\n `INSERT INTO mem_prompts (id, session_id, content, created_at) VALUES (?, ?, ?, ?)`,\n [id, sessionId ?? null, content, Date.now()]\n );\n return id;\n }\n\n getPromptsBySession(sessionId: string, limit = 20): MemPrompt[] {\n return this.db.all(\n `SELECT * FROM mem_prompts WHERE session_id = ? ORDER BY created_at DESC LIMIT ?`,\n [sessionId, limit]\n ).map((row: any) => ({ id: row.id, sessionId: row.session_id ?? undefined, content: row.content, createdAt: row.created_at }));\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(currentModel?: string): MemStats {\n const sessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions')?.c ?? 0;\n const activeSessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions WHERE ended_at IS NULL')?.c ?? 0;\n const observations = this.db.get('SELECT COUNT(*) as c FROM mem_observations')?.c ?? 0;\n const links = this.db.get('SELECT COUNT(*) as c FROM mem_links')?.c ?? 0;\n const vectors = this.db.get('SELECT COUNT(*) as c FROM mem_vectors')?.c ?? 0;\n const mismatch = currentModel ? this.getVectorModelMismatch(currentModel) : 0;\n const relations = this.db.get('SELECT COUNT(*) as c FROM mem_relations')?.c ?? 0;\n const pendingConflicts = this.db.get(\"SELECT COUNT(*) as c FROM mem_relations WHERE judgment_status = 'pending'\")?.c ?? 0;\n\n return {\n sessions,\n activeSessions,\n observations,\n links,\n vectors,\n embeddableCount: observations,\n modelMismatch: mismatch > 0,\n currentModel,\n relations,\n pendingConflicts,\n };\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Delete observations older than the given timestamp.\n * Cascades to mem_links and mem_vectors via FK.\n */\n prune(olderThan: number): number {\n // Get IDs to delete (for FTS cleanup)\n const ids = this.db.all(\n 'SELECT id FROM mem_observations WHERE created_at < ?',\n [olderThan]\n ).map((r: any) => r.id);\n\n if (ids.length === 0) return 0;\n\n const placeholders = ids.map(() => '?').join(',');\n this.db.run(`DELETE FROM mem_fts WHERE id IN (${placeholders})`, ids);\n this.db.run(`DELETE FROM mem_observations WHERE created_at < ?`, [olderThan]);\n\n // Clean up empty sessions\n this.db.run(\n `DELETE FROM mem_sessions WHERE id NOT IN (SELECT DISTINCT session_id FROM mem_observations WHERE session_id IS NOT NULL)`\n );\n\n return ids.length;\n }\n\n /**\n * Find stale links where qualified_name no longer exists in the nodes table.\n */\n findStaleLinks(): MemLink[] {\n const rows = this.db.all(\n `SELECT l.* FROM mem_links l\n LEFT JOIN nodes n ON n.qualified_name = l.qualified_name\n WHERE n.id IS NULL`\n );\n return rows.map((row: any) => ({\n observationId: row.observation_id,\n qualifiedName: row.qualified_name,\n relevance: row.relevance,\n }));\n }\n\n /**\n * Remove stale links (where qualified_name no longer resolves).\n */\n removeStaleLinks(): number {\n const result = this.db.run(\n `DELETE FROM mem_links WHERE qualified_name NOT IN (SELECT DISTINCT qualified_name FROM nodes)`\n );\n return result?.changes ?? 0;\n }\n\n /**\n * Close stale sessions (started but never ended, older than timeout).\n */\n closeStaleSessionsOlderThan(timeoutMs: number): number {\n const cutoff = Date.now() - timeoutMs;\n const result = this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND started_at < ?`,\n [Date.now(), cutoff]\n );\n return result?.changes ?? 0;\n }\n\n // \u2500\u2500 Privacy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Strip <private>...</private> blocks from text.\n */\n static stripPrivate(text: string): string {\n return text.replace(/<private>[\\s\\S]*?<\\/private>/gi, '').trim();\n }\n\n // \u2500\u2500 Row mappers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private rowToSession(row: any): MemSession {\n return {\n id: row.id,\n ide: row.ide ?? undefined,\n cwd: row.cwd ?? undefined,\n startedAt: row.started_at,\n endedAt: row.ended_at ?? undefined,\n };\n }\n\n private rowToObservation(row: any): MemObservation {\n return {\n id: row.id,\n sessionId: row.session_id ?? undefined,\n content: row.content,\n contentRaw: row.content_raw ?? undefined,\n contentHash: row.content_hash,\n kind: row.kind,\n source: row.source,\n tags: row.tags ? JSON.parse(row.tags) : undefined,\n createdAt: row.created_at,\n validFrom: row.valid_from ?? undefined,\n validUntil: row.valid_until ?? undefined,\n supersededBy: row.superseded_by ?? undefined,\n factType: row.fact_type ?? undefined,\n topicKey: row.topic_key ?? undefined,\n reviewAfter: row.review_after ?? undefined,\n fields: row.fields ? JSON.parse(row.fields) : undefined,\n };\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  return to;
17
19
  };
18
20
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
  var memory_exports = {};
21
31
  __export(memory_exports, {
@@ -27,6 +37,7 @@ __export(memory_exports, {
27
37
  compressObservation: () => import_compress2.compressObservation,
28
38
  detectSymbols: () => import_symbols2.detectSymbols,
29
39
  extractIdentifiers: () => import_compress2.extractIdentifiers,
40
+ validateObservationFields: () => import_schema2.validateObservationFields,
30
41
  validateObservationInput: () => import_schema2.validateObservationInput
31
42
  });
32
43
  module.exports = __toCommonJS(memory_exports);
@@ -37,6 +48,7 @@ var import_vectors = require("./vectors");
37
48
  var import_watchmen = require("../watchmen");
38
49
  var import_errors = require("../errors");
39
50
  var import_schema = require("./schema");
51
+ var path = __toESM(require("path"));
40
52
  var import_database2 = require("./database");
41
53
  var import_compress2 = require("./compress");
42
54
  var import_symbols2 = require("./symbols");
@@ -85,6 +97,9 @@ class MemoryManager {
85
97
  if (this.config.memoryStrictWrites) {
86
98
  (0, import_schema.validateObservationInput)(input);
87
99
  }
100
+ if (this.config.memorySchemaValidation) {
101
+ (0, import_schema.validateObservationFields)(input, path.join(this.projectRoot, ".kirograph", "memory-schemas"));
102
+ }
88
103
  let text = input.content;
89
104
  text = import_database.MemoryDatabase.stripPrivate(text);
90
105
  if (!text.trim()) return null;
@@ -105,7 +120,8 @@ class MemoryManager {
105
120
  tags: input.tags,
106
121
  sessionId,
107
122
  topicKey: input.topicKey,
108
- reviewAfter: input.reviewAfter
123
+ reviewAfter: input.reviewAfter,
124
+ fields: input.fields
109
125
  });
110
126
  if (!id) {
111
127
  (0, import_errors.logDebug)("Memory: duplicate observation skipped");
@@ -382,6 +398,7 @@ class MemoryManager {
382
398
  compressObservation,
383
399
  detectSymbols,
384
400
  extractIdentifiers,
401
+ validateObservationFields,
385
402
  validateObservationInput,
386
403
  ...require("./types")
387
404
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/index.ts"],
4
- "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Public API\n *\n * MemoryManager is the single entry point for all memory operations.\n * Coordinates compression, storage, symbol detection, and embedding.\n */\n\nimport type { KiroGraphConfig } from '../config';\nimport type {\n MemObservation,\n MemObservationInput,\n MemSession,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n CompressResult,\n WatchmenReadyResult,\n MemRelation,\n MemRelationInput,\n RelationType,\n MemPrompt,\n} from './types';\nimport { MemoryDatabase } from './database';\nimport { compressObservation, type CavemanMode } from './compress';\nimport { detectSymbols } from './symbols';\nimport { MemoryVectorManager } from './vectors';\nimport { WatchmenChecker } from '../watchmen';\nimport { logDebug } from '../errors';\nimport { validateObservationInput } from './schema';\n\nexport { MemoryDatabase } from './database';\nexport { compressObservation, extractIdentifiers, type CavemanMode } from './compress';\nexport { detectSymbols } from './symbols';\nexport { MemoryVectorManager } from './vectors';\nexport { MemorySchemaError, OBSERVATION_KINDS, validateObservationInput } from './schema';\nexport * from './types';\n\n// \u2500\u2500 MemoryManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryManager {\n private memDb: MemoryDatabase;\n private vectorMgr: MemoryVectorManager;\n private config: KiroGraphConfig;\n private db: any; // raw sqlite handle for symbol detection\n private cavemanMode: CavemanMode;\n private excludePatterns: string[];\n private watchmenChecker: WatchmenChecker | null;\n private projectRoot: string;\n\n constructor(config: KiroGraphConfig, db: any, projectRoot = process.cwd()) {\n this.config = config;\n this.db = db;\n this.projectRoot = projectRoot;\n this.memDb = new MemoryDatabase(db);\n const kirographDir = require('path').join(projectRoot, '.kirograph');\n this.vectorMgr = new MemoryVectorManager(config, this.memDb, kirographDir);\n this.cavemanMode = (config as any).cavemanMode ?? 'off';\n this.excludePatterns = (config as any).memoryExcludePatterns ?? [];\n this.watchmenChecker = config.enableWatchmen\n ? new WatchmenChecker(config.watchmenThreshold)\n : null;\n }\n\n /**\n * Initialize memory tables. Must be called before any operations.\n */\n initialize(): void {\n this.memDb.initialize();\n }\n\n /**\n * Check if memory is enabled in config.\n */\n static isEnabled(config: KiroGraphConfig): boolean {\n return !!(config as any).enableMemory;\n }\n\n // \u2500\u2500 Store \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Store an observation. Handles:\n * 1. Privacy stripping (<private> blocks)\n * 2. Exclude pattern checking\n * 3. Caveman compression (if enabled)\n * 4. Deduplication (content hash)\n * 5. Symbol detection and linking\n * 6. Embedding (if enabled)\n * 7. Watchmen threshold check (if enabled) \u2014 returns WatchmenReadyResult when synthesis should run\n *\n * Returns the observation ID, a WatchmenReadyResult, or null if skipped (duplicate or excluded).\n * @throws MemorySchemaError if `memoryStrictWrites` is enabled and the input doesn't conform.\n */\n async store(input: MemObservationInput, ide = 'kiro'): Promise<string | WatchmenReadyResult | null> {\n // 0. Opt-in schema validation \u2014 rejects instead of silently corrupting the store.\n if ((this.config as any).memoryStrictWrites) {\n validateObservationInput(input);\n }\n\n let text = input.content;\n\n // 1. Strip <private> blocks\n text = MemoryDatabase.stripPrivate(text);\n if (!text.trim()) return null;\n\n // 2. Check exclude patterns\n if (this.shouldExclude(text)) return null;\n\n // 3. Compress (if caveman is on)\n let contentRaw: string | undefined;\n if (this.cavemanMode !== 'off') {\n const result = compressObservation(text, this.cavemanMode);\n contentRaw = text;\n text = result.compressed;\n }\n\n // 4. Get or create session\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n\n // 5. Insert observation (deduplicates via content_hash)\n const id = this.memDb.insertObservation(text, {\n contentRaw: this.cavemanMode !== 'off' ? contentRaw : undefined,\n kind: input.kind ?? 'note',\n source: input.source ?? 'manual',\n tags: input.tags,\n sessionId,\n topicKey: input.topicKey,\n reviewAfter: input.reviewAfter,\n });\n\n if (!id) {\n logDebug('Memory: duplicate observation skipped');\n return null;\n }\n\n // 6. Detect and link symbols\n const symbols = detectSymbols(text, this.db);\n if (symbols.length > 0) {\n this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n }\n\n // Also detect in raw text if different\n if (contentRaw) {\n const rawSymbols = detectSymbols(contentRaw, this.db);\n const extraSymbols = rawSymbols.filter(\n s => !symbols.some(existing => existing.qualifiedName === s.qualifiedName)\n );\n if (extraSymbols.length > 0) {\n this.memDb.linkToSymbols(id, extraSymbols.map(s => s.qualifiedName));\n }\n }\n\n // 7. Embed (async, non-blocking for the store operation)\n const obs = this.memDb.getObservation(id);\n if (obs) {\n await this.vectorMgr.embedObservation(obs);\n }\n\n // 8. Watchmen threshold check\n if (this.watchmenChecker && input.kind !== 'summary') {\n const check = this.watchmenChecker.shouldSynthesize(this.memDb);\n if (check.ready) {\n return this.watchmenChecker.buildReadyResponse(id, check.pendingCount, this.projectRoot);\n }\n }\n\n return id;\n }\n\n // \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Hybrid search: combines FTS5 and vector search with configurable alpha.\n */\n async search(query: string, opts: MemSearchOptions = {}): Promise<ScoredObservation[]> {\n const limit = opts.limit ?? 10;\n const alpha = opts.alpha ?? (this.config as any).memorySearchAlpha ?? 0.5;\n\n // FTS search\n const ftsResults = this.memDb.searchFTS(query, { ...opts, limit: limit * 2 });\n\n // Vector search (if enabled and no model mismatch)\n let vectorResults: ScoredObservation[] = [];\n if (this.vectorMgr.isEnabled() && !this.vectorMgr.hasModelMismatch()) {\n vectorResults = await this.vectorMgr.search(query, limit * 2);\n }\n\n // If only one source has results, return it directly\n if (vectorResults.length === 0) {\n const sliced = ftsResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n if (ftsResults.length === 0) {\n const sliced = vectorResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n\n // Hybrid merge: normalize scores and blend\n const merged = this.mergeResults(ftsResults, vectorResults, alpha);\n const sliced = merged.slice(0, limit);\n\n // Enrich with relations\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n\n return sliced;\n }\n\n /**\n * Get observations linked to specific symbols.\n * Used by kirograph_context and kirograph_impact.\n */\n getLinkedObservations(qualifiedNames: string[], limit = 3, threshold = 0.3): ScoredObservation[] {\n const results = this.memDb.getLinkedObservationsForSymbols(qualifiedNames, limit * 2);\n return results.filter(r => r.score >= threshold).slice(0, limit);\n }\n\n // \u2500\u2500 Timeline \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * List sessions with their observation counts.\n */\n timeline(opts: MemTimelineOptions = {}): { sessions: MemSession[]; observations: Map<string, MemObservation[]> } {\n const limit = opts.limit ?? 5;\n\n if (opts.sessionId) {\n const session = this.memDb.getSession(opts.sessionId);\n if (!session) return { sessions: [], observations: new Map() };\n const obs = this.memDb.getObservationsBySession(opts.sessionId);\n const map = new Map<string, MemObservation[]>();\n map.set(opts.sessionId, obs);\n return { sessions: [session], observations: map };\n }\n\n const sessions = this.memDb.listSessions(limit);\n const observations = new Map<string, MemObservation[]>();\n for (const session of sessions) {\n const obs = this.memDb.getObservationsBySession(session.id, 10);\n observations.set(session.id, obs);\n }\n return { sessions, observations };\n }\n\n // \u2500\u2500 Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): MemStats {\n const modelName = (this.config as any).embeddingModel ?? 'nomic-ai/nomic-embed-text-v1.5';\n return this.memDb.getStats(modelName);\n }\n\n // \u2500\u2500 Session management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n endCurrentSession(ide = 'kiro'): void {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n // Find active session and close it\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n this.memDb.endSession(sessionId);\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n prune(olderThanMs: number): number {\n const cutoff = Date.now() - olderThanMs;\n return this.memDb.prune(cutoff);\n }\n\n async reembed(batchSize = 32): Promise<number> {\n return this.vectorMgr.reembed(batchSize);\n }\n\n lint(): { staleLinks: number; modelMismatch: boolean; staleSessions: number } {\n const staleLinks = this.memDb.findStaleLinks().length;\n const modelMismatch = this.vectorMgr.hasModelMismatch();\n const sessionTimeout = ((this.config as any).memorySessionTimeout ?? 7200) * 1000;\n const staleSessions = this.memDb.closeStaleSessionsOlderThan(sessionTimeout);\n return { staleLinks, modelMismatch, staleSessions };\n }\n\n removeStaleLinks(): number {\n return this.memDb.removeStaleLinks();\n }\n\n // \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n compareObservations(input: MemRelationInput): string {\n // Resolve by topic_key if not found by ID\n let obsA = input.observationA;\n let obsB = input.observationB;\n if (!this.memDb.getObservation(obsA)) {\n const byKey = this.memDb.resolveObservationByTopicKey(obsA);\n if (byKey) obsA = byKey.id;\n }\n if (!this.memDb.getObservation(obsB)) {\n const byKey = this.memDb.resolveObservationByTopicKey(obsB);\n if (byKey) obsB = byKey.id;\n }\n return this.memDb.insertRelation({ ...input, observationA: obsA, observationB: obsB });\n }\n\n judgeRelation(relationId: string, relation: RelationType, confidence: number, reason?: string, evidence?: string): void {\n this.memDb.judgeRelation(relationId, relation, confidence, reason, evidence);\n }\n\n ignoreRelation(relationId: string): void {\n this.memDb.ignoreRelation(relationId);\n }\n\n getPendingRelations(limit = 20): MemRelation[] {\n return this.memDb.getPendingRelations(limit);\n }\n\n // \u2500\u2500 Review \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getObservationsForReview(limit = 20): MemObservation[] {\n return this.memDb.getObservationsForReview(limit);\n }\n\n markReviewed(id: string): void {\n this.memDb.markReviewed(id);\n }\n\n // \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async savePrompt(content: string, ide = 'kiro'): Promise<string> {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n return this.memDb.insertPrompt(sessionId, content);\n }\n\n // \u2500\u2500 Passive capture \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n capturePassive(content: string, sessionId?: string): Array<{ id: string | null; kind: string; content: string }> {\n const kindMap: Record<string, string> = {\n 'key learnings': 'pattern',\n 'learnings': 'pattern',\n 'observations': 'note',\n 'decisions': 'decision',\n 'key changes': 'pattern',\n 'errors': 'error',\n 'patterns': 'pattern',\n };\n\n const results: Array<{ id: string | null; kind: string; content: string }> = [];\n const lines = content.split('\\n');\n let currentKind: string | null = null;\n\n for (const line of lines) {\n const headingMatch = line.match(/^#{1,3}\\s+(.+)/);\n if (headingMatch) {\n const heading = headingMatch[1].toLowerCase().trim();\n currentKind = kindMap[heading] ?? null;\n continue;\n }\n if (!currentKind) continue;\n const bulletMatch = line.match(/^[\\s]*[-*\u2022]\\s+(.+)|^[\\s]*\\d+[.)]\\s+(.+)/);\n if (bulletMatch) {\n const text = (bulletMatch[1] || bulletMatch[2]).trim();\n if (text.length < 5) continue;\n const id = this.memDb.insertObservation(text, { kind: currentKind, source: 'passive', sessionId });\n const symbols = detectSymbols(text, this.db);\n if (id && symbols.length > 0) this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n results.push({ id, kind: currentKind, content: text });\n }\n }\n\n return results;\n }\n\n // \u2500\u2500 Topic key \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n suggestTopicKey(kind: string, title: string): string {\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .trim()\n .split(/\\s+/)\n .slice(0, 6)\n .join('-');\n return `${kind}/${slug}`;\n }\n\n // \u2500\u2500 Conflict scan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n scanConflicts(limit = 50): Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> {\n const recent = this.memDb.getObservationsByTimeRange(0, Date.now(), limit);\n const candidates: Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> = [];\n const seen = new Set<string>();\n\n for (const obs of recent) {\n if (!obs.content || obs.content.length < 20) continue;\n const words = obs.content.split(/\\s+/).slice(0, 8).join(' ');\n const similar = this.memDb.searchFTS(words, { limit: 5 });\n for (const match of similar) {\n if (match.observation.id === obs.id) continue;\n const pairKey = [obs.id, match.observation.id].sort().join('|');\n if (seen.has(pairKey)) continue;\n seen.add(pairKey);\n const existing = this.memDb.getRelationsForObservations([obs.id]);\n const alreadyLinked = existing.get(obs.id)?.some(r => r.observationA === match.observation.id || r.observationB === match.observation.id);\n if (!alreadyLinked && match.score > 0.3) {\n candidates.push({ observationA: obs, observationB: match.observation, similarity: match.score });\n }\n }\n }\n\n return candidates.slice(0, 20);\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private shouldExclude(text: string): boolean {\n if (this.excludePatterns.length === 0) return false;\n\n // Simple check: if any exclude pattern appears as a substring in the text\n // This is a basic implementation; could use picomatch for glob matching\n for (const pattern of this.excludePatterns) {\n const normalized = pattern.replace(/\\*/g, '');\n if (normalized && text.includes(normalized)) return true;\n }\n return false;\n }\n\n private mergeResults(\n ftsResults: ScoredObservation[],\n vectorResults: ScoredObservation[],\n alpha: number\n ): ScoredObservation[] {\n // Normalize FTS scores (rank is negative in FTS5, lower = better)\n const maxFts = Math.max(...ftsResults.map(r => r.score), 1);\n const normalizedFts = new Map<string, number>();\n for (const r of ftsResults) {\n normalizedFts.set(r.observation.id, r.score / maxFts);\n }\n\n // Vector scores are already 0-1 (cosine similarity)\n const normalizedVec = new Map<string, number>();\n for (const r of vectorResults) {\n normalizedVec.set(r.observation.id, r.score);\n }\n\n // Merge: collect all unique observation IDs\n const allIds = new Set([...normalizedFts.keys(), ...normalizedVec.keys()]);\n const merged: ScoredObservation[] = [];\n\n // Build a lookup for observations\n const obsMap = new Map<string, MemObservation>();\n for (const r of ftsResults) obsMap.set(r.observation.id, r.observation);\n for (const r of vectorResults) obsMap.set(r.observation.id, r.observation);\n\n for (const id of allIds) {\n const ftsScore = normalizedFts.get(id) ?? 0;\n const vecScore = normalizedVec.get(id) ?? 0;\n const hybridScore = (1 - alpha) * ftsScore + alpha * vecScore;\n const obs = obsMap.get(id)!;\n merged.push({ observation: obs, score: hybridScore, scoreSource: 'hybrid' });\n }\n\n merged.sort((a, b) => b.score - a.score);\n return merged;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Public API\n *\n * MemoryManager is the single entry point for all memory operations.\n * Coordinates compression, storage, symbol detection, and embedding.\n */\n\nimport type { KiroGraphConfig } from '../config';\nimport type {\n MemObservation,\n MemObservationInput,\n MemSession,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n CompressResult,\n WatchmenReadyResult,\n MemRelation,\n MemRelationInput,\n RelationType,\n MemPrompt,\n} from './types';\nimport { MemoryDatabase } from './database';\nimport { compressObservation, type CavemanMode } from './compress';\nimport { detectSymbols } from './symbols';\nimport { MemoryVectorManager } from './vectors';\nimport { WatchmenChecker } from '../watchmen';\nimport { logDebug } from '../errors';\nimport { validateObservationInput, validateObservationFields } from './schema';\nimport * as path from 'path';\n\nexport { MemoryDatabase } from './database';\nexport { compressObservation, extractIdentifiers, type CavemanMode } from './compress';\nexport { detectSymbols } from './symbols';\nexport { MemoryVectorManager } from './vectors';\nexport { MemorySchemaError, OBSERVATION_KINDS, validateObservationInput, validateObservationFields } from './schema';\nexport * from './types';\n\n// \u2500\u2500 MemoryManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryManager {\n private memDb: MemoryDatabase;\n private vectorMgr: MemoryVectorManager;\n private config: KiroGraphConfig;\n private db: any; // raw sqlite handle for symbol detection\n private cavemanMode: CavemanMode;\n private excludePatterns: string[];\n private watchmenChecker: WatchmenChecker | null;\n private projectRoot: string;\n\n constructor(config: KiroGraphConfig, db: any, projectRoot = process.cwd()) {\n this.config = config;\n this.db = db;\n this.projectRoot = projectRoot;\n this.memDb = new MemoryDatabase(db);\n const kirographDir = require('path').join(projectRoot, '.kirograph');\n this.vectorMgr = new MemoryVectorManager(config, this.memDb, kirographDir);\n this.cavemanMode = (config as any).cavemanMode ?? 'off';\n this.excludePatterns = (config as any).memoryExcludePatterns ?? [];\n this.watchmenChecker = config.enableWatchmen\n ? new WatchmenChecker(config.watchmenThreshold)\n : null;\n }\n\n /**\n * Initialize memory tables. Must be called before any operations.\n */\n initialize(): void {\n this.memDb.initialize();\n }\n\n /**\n * Check if memory is enabled in config.\n */\n static isEnabled(config: KiroGraphConfig): boolean {\n return !!(config as any).enableMemory;\n }\n\n // \u2500\u2500 Store \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Store an observation. Handles:\n * 1. Privacy stripping (<private> blocks)\n * 2. Exclude pattern checking\n * 3. Caveman compression (if enabled)\n * 4. Deduplication (content hash)\n * 5. Symbol detection and linking\n * 6. Embedding (if enabled)\n * 7. Watchmen threshold check (if enabled) \u2014 returns WatchmenReadyResult when synthesis should run\n *\n * Returns the observation ID, a WatchmenReadyResult, or null if skipped (duplicate or excluded).\n * @throws MemorySchemaError if `memoryStrictWrites` is enabled and the input doesn't conform.\n */\n async store(input: MemObservationInput, ide = 'kiro'): Promise<string | WatchmenReadyResult | null> {\n // 0. Opt-in schema validation \u2014 rejects instead of silently corrupting the store.\n if ((this.config as any).memoryStrictWrites) {\n validateObservationInput(input);\n }\n if ((this.config as any).memorySchemaValidation) {\n validateObservationFields(input, path.join(this.projectRoot, '.kirograph', 'memory-schemas'));\n }\n\n let text = input.content;\n\n // 1. Strip <private> blocks\n text = MemoryDatabase.stripPrivate(text);\n if (!text.trim()) return null;\n\n // 2. Check exclude patterns\n if (this.shouldExclude(text)) return null;\n\n // 3. Compress (if caveman is on)\n let contentRaw: string | undefined;\n if (this.cavemanMode !== 'off') {\n const result = compressObservation(text, this.cavemanMode);\n contentRaw = text;\n text = result.compressed;\n }\n\n // 4. Get or create session\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n\n // 5. Insert observation (deduplicates via content_hash)\n const id = this.memDb.insertObservation(text, {\n contentRaw: this.cavemanMode !== 'off' ? contentRaw : undefined,\n kind: input.kind ?? 'note',\n source: input.source ?? 'manual',\n tags: input.tags,\n sessionId,\n topicKey: input.topicKey,\n reviewAfter: input.reviewAfter,\n fields: input.fields,\n });\n\n if (!id) {\n logDebug('Memory: duplicate observation skipped');\n return null;\n }\n\n // 6. Detect and link symbols\n const symbols = detectSymbols(text, this.db);\n if (symbols.length > 0) {\n this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n }\n\n // Also detect in raw text if different\n if (contentRaw) {\n const rawSymbols = detectSymbols(contentRaw, this.db);\n const extraSymbols = rawSymbols.filter(\n s => !symbols.some(existing => existing.qualifiedName === s.qualifiedName)\n );\n if (extraSymbols.length > 0) {\n this.memDb.linkToSymbols(id, extraSymbols.map(s => s.qualifiedName));\n }\n }\n\n // 7. Embed (async, non-blocking for the store operation)\n const obs = this.memDb.getObservation(id);\n if (obs) {\n await this.vectorMgr.embedObservation(obs);\n }\n\n // 8. Watchmen threshold check\n if (this.watchmenChecker && input.kind !== 'summary') {\n const check = this.watchmenChecker.shouldSynthesize(this.memDb);\n if (check.ready) {\n return this.watchmenChecker.buildReadyResponse(id, check.pendingCount, this.projectRoot);\n }\n }\n\n return id;\n }\n\n // \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Hybrid search: combines FTS5 and vector search with configurable alpha.\n */\n async search(query: string, opts: MemSearchOptions = {}): Promise<ScoredObservation[]> {\n const limit = opts.limit ?? 10;\n const alpha = opts.alpha ?? (this.config as any).memorySearchAlpha ?? 0.5;\n\n // FTS search\n const ftsResults = this.memDb.searchFTS(query, { ...opts, limit: limit * 2 });\n\n // Vector search (if enabled and no model mismatch)\n let vectorResults: ScoredObservation[] = [];\n if (this.vectorMgr.isEnabled() && !this.vectorMgr.hasModelMismatch()) {\n vectorResults = await this.vectorMgr.search(query, limit * 2);\n }\n\n // If only one source has results, return it directly\n if (vectorResults.length === 0) {\n const sliced = ftsResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n if (ftsResults.length === 0) {\n const sliced = vectorResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n\n // Hybrid merge: normalize scores and blend\n const merged = this.mergeResults(ftsResults, vectorResults, alpha);\n const sliced = merged.slice(0, limit);\n\n // Enrich with relations\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n\n return sliced;\n }\n\n /**\n * Get observations linked to specific symbols.\n * Used by kirograph_context and kirograph_impact.\n */\n getLinkedObservations(qualifiedNames: string[], limit = 3, threshold = 0.3): ScoredObservation[] {\n const results = this.memDb.getLinkedObservationsForSymbols(qualifiedNames, limit * 2);\n return results.filter(r => r.score >= threshold).slice(0, limit);\n }\n\n // \u2500\u2500 Timeline \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * List sessions with their observation counts.\n */\n timeline(opts: MemTimelineOptions = {}): { sessions: MemSession[]; observations: Map<string, MemObservation[]> } {\n const limit = opts.limit ?? 5;\n\n if (opts.sessionId) {\n const session = this.memDb.getSession(opts.sessionId);\n if (!session) return { sessions: [], observations: new Map() };\n const obs = this.memDb.getObservationsBySession(opts.sessionId);\n const map = new Map<string, MemObservation[]>();\n map.set(opts.sessionId, obs);\n return { sessions: [session], observations: map };\n }\n\n const sessions = this.memDb.listSessions(limit);\n const observations = new Map<string, MemObservation[]>();\n for (const session of sessions) {\n const obs = this.memDb.getObservationsBySession(session.id, 10);\n observations.set(session.id, obs);\n }\n return { sessions, observations };\n }\n\n // \u2500\u2500 Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): MemStats {\n const modelName = (this.config as any).embeddingModel ?? 'nomic-ai/nomic-embed-text-v1.5';\n return this.memDb.getStats(modelName);\n }\n\n // \u2500\u2500 Session management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n endCurrentSession(ide = 'kiro'): void {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n // Find active session and close it\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n this.memDb.endSession(sessionId);\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n prune(olderThanMs: number): number {\n const cutoff = Date.now() - olderThanMs;\n return this.memDb.prune(cutoff);\n }\n\n async reembed(batchSize = 32): Promise<number> {\n return this.vectorMgr.reembed(batchSize);\n }\n\n lint(): { staleLinks: number; modelMismatch: boolean; staleSessions: number } {\n const staleLinks = this.memDb.findStaleLinks().length;\n const modelMismatch = this.vectorMgr.hasModelMismatch();\n const sessionTimeout = ((this.config as any).memorySessionTimeout ?? 7200) * 1000;\n const staleSessions = this.memDb.closeStaleSessionsOlderThan(sessionTimeout);\n return { staleLinks, modelMismatch, staleSessions };\n }\n\n removeStaleLinks(): number {\n return this.memDb.removeStaleLinks();\n }\n\n // \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n compareObservations(input: MemRelationInput): string {\n // Resolve by topic_key if not found by ID\n let obsA = input.observationA;\n let obsB = input.observationB;\n if (!this.memDb.getObservation(obsA)) {\n const byKey = this.memDb.resolveObservationByTopicKey(obsA);\n if (byKey) obsA = byKey.id;\n }\n if (!this.memDb.getObservation(obsB)) {\n const byKey = this.memDb.resolveObservationByTopicKey(obsB);\n if (byKey) obsB = byKey.id;\n }\n return this.memDb.insertRelation({ ...input, observationA: obsA, observationB: obsB });\n }\n\n judgeRelation(relationId: string, relation: RelationType, confidence: number, reason?: string, evidence?: string): void {\n this.memDb.judgeRelation(relationId, relation, confidence, reason, evidence);\n }\n\n ignoreRelation(relationId: string): void {\n this.memDb.ignoreRelation(relationId);\n }\n\n getPendingRelations(limit = 20): MemRelation[] {\n return this.memDb.getPendingRelations(limit);\n }\n\n // \u2500\u2500 Review \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getObservationsForReview(limit = 20): MemObservation[] {\n return this.memDb.getObservationsForReview(limit);\n }\n\n markReviewed(id: string): void {\n this.memDb.markReviewed(id);\n }\n\n // \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async savePrompt(content: string, ide = 'kiro'): Promise<string> {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n return this.memDb.insertPrompt(sessionId, content);\n }\n\n // \u2500\u2500 Passive capture \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n capturePassive(content: string, sessionId?: string): Array<{ id: string | null; kind: string; content: string }> {\n const kindMap: Record<string, string> = {\n 'key learnings': 'pattern',\n 'learnings': 'pattern',\n 'observations': 'note',\n 'decisions': 'decision',\n 'key changes': 'pattern',\n 'errors': 'error',\n 'patterns': 'pattern',\n };\n\n const results: Array<{ id: string | null; kind: string; content: string }> = [];\n const lines = content.split('\\n');\n let currentKind: string | null = null;\n\n for (const line of lines) {\n const headingMatch = line.match(/^#{1,3}\\s+(.+)/);\n if (headingMatch) {\n const heading = headingMatch[1].toLowerCase().trim();\n currentKind = kindMap[heading] ?? null;\n continue;\n }\n if (!currentKind) continue;\n const bulletMatch = line.match(/^[\\s]*[-*\u2022]\\s+(.+)|^[\\s]*\\d+[.)]\\s+(.+)/);\n if (bulletMatch) {\n const text = (bulletMatch[1] || bulletMatch[2]).trim();\n if (text.length < 5) continue;\n const id = this.memDb.insertObservation(text, { kind: currentKind, source: 'passive', sessionId });\n const symbols = detectSymbols(text, this.db);\n if (id && symbols.length > 0) this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n results.push({ id, kind: currentKind, content: text });\n }\n }\n\n return results;\n }\n\n // \u2500\u2500 Topic key \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n suggestTopicKey(kind: string, title: string): string {\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .trim()\n .split(/\\s+/)\n .slice(0, 6)\n .join('-');\n return `${kind}/${slug}`;\n }\n\n // \u2500\u2500 Conflict scan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n scanConflicts(limit = 50): Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> {\n const recent = this.memDb.getObservationsByTimeRange(0, Date.now(), limit);\n const candidates: Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> = [];\n const seen = new Set<string>();\n\n for (const obs of recent) {\n if (!obs.content || obs.content.length < 20) continue;\n const words = obs.content.split(/\\s+/).slice(0, 8).join(' ');\n const similar = this.memDb.searchFTS(words, { limit: 5 });\n for (const match of similar) {\n if (match.observation.id === obs.id) continue;\n const pairKey = [obs.id, match.observation.id].sort().join('|');\n if (seen.has(pairKey)) continue;\n seen.add(pairKey);\n const existing = this.memDb.getRelationsForObservations([obs.id]);\n const alreadyLinked = existing.get(obs.id)?.some(r => r.observationA === match.observation.id || r.observationB === match.observation.id);\n if (!alreadyLinked && match.score > 0.3) {\n candidates.push({ observationA: obs, observationB: match.observation, similarity: match.score });\n }\n }\n }\n\n return candidates.slice(0, 20);\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private shouldExclude(text: string): boolean {\n if (this.excludePatterns.length === 0) return false;\n\n // Simple check: if any exclude pattern appears as a substring in the text\n // This is a basic implementation; could use picomatch for glob matching\n for (const pattern of this.excludePatterns) {\n const normalized = pattern.replace(/\\*/g, '');\n if (normalized && text.includes(normalized)) return true;\n }\n return false;\n }\n\n private mergeResults(\n ftsResults: ScoredObservation[],\n vectorResults: ScoredObservation[],\n alpha: number\n ): ScoredObservation[] {\n // Normalize FTS scores (rank is negative in FTS5, lower = better)\n const maxFts = Math.max(...ftsResults.map(r => r.score), 1);\n const normalizedFts = new Map<string, number>();\n for (const r of ftsResults) {\n normalizedFts.set(r.observation.id, r.score / maxFts);\n }\n\n // Vector scores are already 0-1 (cosine similarity)\n const normalizedVec = new Map<string, number>();\n for (const r of vectorResults) {\n normalizedVec.set(r.observation.id, r.score);\n }\n\n // Merge: collect all unique observation IDs\n const allIds = new Set([...normalizedFts.keys(), ...normalizedVec.keys()]);\n const merged: ScoredObservation[] = [];\n\n // Build a lookup for observations\n const obsMap = new Map<string, MemObservation>();\n for (const r of ftsResults) obsMap.set(r.observation.id, r.observation);\n for (const r of vectorResults) obsMap.set(r.observation.id, r.observation);\n\n for (const id of allIds) {\n const ftsScore = normalizedFts.get(id) ?? 0;\n const vecScore = normalizedVec.get(id) ?? 0;\n const hybridScore = (1 - alpha) * ftsScore + alpha * vecScore;\n const obs = obsMap.get(id)!;\n merged.push({ observation: obs, score: hybridScore, scoreSource: 'hybrid' });\n }\n\n merged.sort((a, b) => b.score - a.score);\n return merged;\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBA,sBAA+B;AAC/B,sBAAsD;AACtD,qBAA8B;AAC9B,qBAAoC;AACpC,sBAAgC;AAChC,oBAAyB;AACzB,oBAAoE;AACpE,WAAsB;AAEtB,IAAAA,mBAA+B;AAC/B,IAAAC,mBAA0E;AAC1E,IAAAC,kBAA8B;AAC9B,IAAAC,kBAAoC;AACpC,IAAAC,iBAA0G;AAC1G,2BAAc,oBArCd;AAyCO,MAAM,cAAc;AAAA,EAUzB,YAAY,QAAyB,IAAS,cAAc,QAAQ,IAAI,GAAG;AACzE,SAAK,SAAS;AACd,SAAK,KAAK;AACV,SAAK,cAAc;AACnB,SAAK,QAAQ,IAAI,+BAAe,EAAE;AAClC,UAAM,eAAe,QAAQ,MAAM,EAAE,KAAK,aAAa,YAAY;AACnE,SAAK,YAAY,IAAI,mCAAoB,QAAQ,KAAK,OAAO,YAAY;AACzE,SAAK,cAAe,OAAe,eAAe;AAClD,SAAK,kBAAmB,OAAe,yBAAyB,CAAC;AACjE,SAAK,kBAAkB,OAAO,iBAC1B,IAAI,gCAAgB,OAAO,iBAAiB,IAC5C;AAAA,EACN;AAAA;AAAA;AAAA;AAAA,EAKA,aAAmB;AACjB,SAAK,MAAM,WAAW;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,UAAU,QAAkC;AACjD,WAAO,CAAC,CAAE,OAAe;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,MAAM,OAA4B,MAAM,QAAsD;AAElG,QAAK,KAAK,OAAe,oBAAoB;AAC3C,kDAAyB,KAAK;AAAA,IAChC;AACA,QAAK,KAAK,OAAe,wBAAwB;AAC/C,mDAA0B,OAAO,KAAK,KAAK,KAAK,aAAa,cAAc,gBAAgB,CAAC;AAAA,IAC9F;AAEA,QAAI,OAAO,MAAM;AAGjB,WAAO,+BAAe,aAAa,IAAI;AACvC,QAAI,CAAC,KAAK,KAAK,EAAG,QAAO;AAGzB,QAAI,KAAK,cAAc,IAAI,EAAG,QAAO;AAGrC,QAAI;AACJ,QAAI,KAAK,gBAAgB,OAAO;AAC9B,YAAM,aAAS,qCAAoB,MAAM,KAAK,WAAW;AACzD,mBAAa;AACb,aAAO,OAAO;AAAA,IAChB;AAGA,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,iBAAkB,KAAK,OAAe,wBAAwB;AACpE,UAAM,YAAY,KAAK,MAAM,mBAAmB,KAAK,KAAK,cAAc;AAGxE,UAAM,KAAK,KAAK,MAAM,kBAAkB,MAAM;AAAA,MAC5C,YAAY,KAAK,gBAAgB,QAAQ,aAAa;AAAA,MACtD,MAAM,MAAM,QAAQ;AAAA,MACpB,QAAQ,MAAM,UAAU;AAAA,MACxB,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB,QAAQ,MAAM;AAAA,IAChB,CAAC;AAED,QAAI,CAAC,IAAI;AACP,kCAAS,uCAAuC;AAChD,aAAO;AAAA,IACT;AAGA,UAAM,cAAU,8BAAc,MAAM,KAAK,EAAE;AAC3C,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,MAAM,cAAc,IAAI,QAAQ,IAAI,OAAK,EAAE,aAAa,CAAC;AAAA,IAChE;AAGA,QAAI,YAAY;AACd,YAAM,iBAAa,8BAAc,YAAY,KAAK,EAAE;AACpD,YAAM,eAAe,WAAW;AAAA,QAC9B,OAAK,CAAC,QAAQ,KAAK,cAAY,SAAS,kBAAkB,EAAE,aAAa;AAAA,MAC3E;AACA,UAAI,aAAa,SAAS,GAAG;AAC3B,aAAK,MAAM,cAAc,IAAI,aAAa,IAAI,OAAK,EAAE,aAAa,CAAC;AAAA,MACrE;AAAA,IACF;AAGA,UAAM,MAAM,KAAK,MAAM,eAAe,EAAE;AACxC,QAAI,KAAK;AACP,YAAM,KAAK,UAAU,iBAAiB,GAAG;AAAA,IAC3C;AAGA,QAAI,KAAK,mBAAmB,MAAM,SAAS,WAAW;AACpD,YAAM,QAAQ,KAAK,gBAAgB,iBAAiB,KAAK,KAAK;AAC9D,UAAI,MAAM,OAAO;AACf,eAAO,KAAK,gBAAgB,mBAAmB,IAAI,MAAM,cAAc,KAAK,WAAW;AAAA,MACzF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,OAAe,OAAyB,CAAC,GAAiC;AACrF,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,QAAQ,KAAK,SAAU,KAAK,OAAe,qBAAqB;AAGtE,UAAM,aAAa,KAAK,MAAM,UAAU,OAAO,EAAE,GAAG,MAAM,OAAO,QAAQ,EAAE,CAAC;AAG5E,QAAI,gBAAqC,CAAC;AAC1C,QAAI,KAAK,UAAU,UAAU,KAAK,CAAC,KAAK,UAAU,iBAAiB,GAAG;AACpE,sBAAgB,MAAM,KAAK,UAAU,OAAO,OAAO,QAAQ,CAAC;AAAA,IAC9D;AAGA,QAAI,cAAc,WAAW,GAAG;AAC9B,YAAMC,UAAS,WAAW,MAAM,GAAG,KAAK;AACxC,YAAMC,OAAMD,QAAO,IAAI,OAAK,EAAE,YAAY,EAAE;AAC5C,YAAME,UAAS,KAAK,MAAM,4BAA4BD,IAAG;AACzD,iBAAW,KAAKD,SAAQ;AACtB,cAAM,OAAOE,QAAO,IAAI,EAAE,YAAY,EAAE;AACxC,YAAI,QAAQ,KAAK,SAAS,EAAG,GAAE,YAAY;AAAA,MAC7C;AACA,aAAOF;AAAA,IACT;AACA,QAAI,WAAW,WAAW,GAAG;AAC3B,YAAMA,UAAS,cAAc,MAAM,GAAG,KAAK;AAC3C,YAAMC,OAAMD,QAAO,IAAI,OAAK,EAAE,YAAY,EAAE;AAC5C,YAAME,UAAS,KAAK,MAAM,4BAA4BD,IAAG;AACzD,iBAAW,KAAKD,SAAQ;AACtB,cAAM,OAAOE,QAAO,IAAI,EAAE,YAAY,EAAE;AACxC,YAAI,QAAQ,KAAK,SAAS,EAAG,GAAE,YAAY;AAAA,MAC7C;AACA,aAAOF;AAAA,IACT;AAGA,UAAM,SAAS,KAAK,aAAa,YAAY,eAAe,KAAK;AACjE,UAAM,SAAS,OAAO,MAAM,GAAG,KAAK;AAGpC,UAAM,MAAM,OAAO,IAAI,OAAK,EAAE,YAAY,EAAE;AAC5C,UAAM,SAAS,KAAK,MAAM,4BAA4B,GAAG;AACzD,eAAW,KAAK,QAAQ;AACtB,YAAM,OAAO,OAAO,IAAI,EAAE,YAAY,EAAE;AACxC,UAAI,QAAQ,KAAK,SAAS,EAAG,GAAE,YAAY;AAAA,IAC7C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,gBAA0B,QAAQ,GAAG,YAAY,KAA0B;AAC/F,UAAM,UAAU,KAAK,MAAM,gCAAgC,gBAAgB,QAAQ,CAAC;AACpF,WAAO,QAAQ,OAAO,OAAK,EAAE,SAAS,SAAS,EAAE,MAAM,GAAG,KAAK;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,OAA2B,CAAC,GAA4E;AAC/G,UAAM,QAAQ,KAAK,SAAS;AAE5B,QAAI,KAAK,WAAW;AAClB,YAAM,UAAU,KAAK,MAAM,WAAW,KAAK,SAAS;AACpD,UAAI,CAAC,QAAS,QAAO,EAAE,UAAU,CAAC,GAAG,cAAc,oBAAI,IAAI,EAAE;AAC7D,YAAM,MAAM,KAAK,MAAM,yBAAyB,KAAK,SAAS;AAC9D,YAAM,MAAM,oBAAI,IAA8B;AAC9C,UAAI,IAAI,KAAK,WAAW,GAAG;AAC3B,aAAO,EAAE,UAAU,CAAC,OAAO,GAAG,cAAc,IAAI;AAAA,IAClD;AAEA,UAAM,WAAW,KAAK,MAAM,aAAa,KAAK;AAC9C,UAAM,eAAe,oBAAI,IAA8B;AACvD,eAAW,WAAW,UAAU;AAC9B,YAAM,MAAM,KAAK,MAAM,yBAAyB,QAAQ,IAAI,EAAE;AAC9D,mBAAa,IAAI,QAAQ,IAAI,GAAG;AAAA,IAClC;AACA,WAAO,EAAE,UAAU,aAAa;AAAA,EAClC;AAAA;AAAA,EAIA,WAAqB;AACnB,UAAM,YAAa,KAAK,OAAe,kBAAkB;AACzD,WAAO,KAAK,MAAM,SAAS,SAAS;AAAA,EACtC;AAAA;AAAA,EAIA,kBAAkB,MAAM,QAAc;AACpC,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,iBAAkB,KAAK,OAAe,wBAAwB;AAEpE,UAAM,YAAY,KAAK,MAAM,mBAAmB,KAAK,KAAK,cAAc;AACxE,SAAK,MAAM,WAAW,SAAS;AAAA,EACjC;AAAA;AAAA,EAIA,MAAM,aAA6B;AACjC,UAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,WAAO,KAAK,MAAM,MAAM,MAAM;AAAA,EAChC;AAAA,EAEA,MAAM,QAAQ,YAAY,IAAqB;AAC7C,WAAO,KAAK,UAAU,QAAQ,SAAS;AAAA,EACzC;AAAA,EAEA,OAA8E;AAC5E,UAAM,aAAa,KAAK,MAAM,eAAe,EAAE;AAC/C,UAAM,gBAAgB,KAAK,UAAU,iBAAiB;AACtD,UAAM,kBAAmB,KAAK,OAAe,wBAAwB,QAAQ;AAC7E,UAAM,gBAAgB,KAAK,MAAM,4BAA4B,cAAc;AAC3E,WAAO,EAAE,YAAY,eAAe,cAAc;AAAA,EACpD;AAAA,EAEA,mBAA2B;AACzB,WAAO,KAAK,MAAM,iBAAiB;AAAA,EACrC;AAAA;AAAA,EAIA,oBAAoB,OAAiC;AAEnD,QAAI,OAAO,MAAM;AACjB,QAAI,OAAO,MAAM;AACjB,QAAI,CAAC,KAAK,MAAM,eAAe,IAAI,GAAG;AACpC,YAAM,QAAQ,KAAK,MAAM,6BAA6B,IAAI;AAC1D,UAAI,MAAO,QAAO,MAAM;AAAA,IAC1B;AACA,QAAI,CAAC,KAAK,MAAM,eAAe,IAAI,GAAG;AACpC,YAAM,QAAQ,KAAK,MAAM,6BAA6B,IAAI;AAC1D,UAAI,MAAO,QAAO,MAAM;AAAA,IAC1B;AACA,WAAO,KAAK,MAAM,eAAe,EAAE,GAAG,OAAO,cAAc,MAAM,cAAc,KAAK,CAAC;AAAA,EACvF;AAAA,EAEA,cAAc,YAAoB,UAAwB,YAAoB,QAAiB,UAAyB;AACtH,SAAK,MAAM,cAAc,YAAY,UAAU,YAAY,QAAQ,QAAQ;AAAA,EAC7E;AAAA,EAEA,eAAe,YAA0B;AACvC,SAAK,MAAM,eAAe,UAAU;AAAA,EACtC;AAAA,EAEA,oBAAoB,QAAQ,IAAmB;AAC7C,WAAO,KAAK,MAAM,oBAAoB,KAAK;AAAA,EAC7C;AAAA;AAAA,EAIA,yBAAyB,QAAQ,IAAsB;AACrD,WAAO,KAAK,MAAM,yBAAyB,KAAK;AAAA,EAClD;AAAA,EAEA,aAAa,IAAkB;AAC7B,SAAK,MAAM,aAAa,EAAE;AAAA,EAC5B;AAAA;AAAA,EAIA,MAAM,WAAW,SAAiB,MAAM,QAAyB;AAC/D,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,iBAAkB,KAAK,OAAe,wBAAwB;AACpE,UAAM,YAAY,KAAK,MAAM,mBAAmB,KAAK,KAAK,cAAc;AACxE,WAAO,KAAK,MAAM,aAAa,WAAW,OAAO;AAAA,EACnD;AAAA;AAAA,EAIA,eAAe,SAAiB,WAAiF;AAC/G,UAAM,UAAkC;AAAA,MACtC,iBAAiB;AAAA,MACjB,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,eAAe;AAAA,MACf,UAAU;AAAA,MACV,YAAY;AAAA,IACd;AAEA,UAAM,UAAuE,CAAC;AAC9E,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAI,cAA6B;AAEjC,eAAW,QAAQ,OAAO;AACxB,YAAM,eAAe,KAAK,MAAM,gBAAgB;AAChD,UAAI,cAAc;AAChB,cAAM,UAAU,aAAa,CAAC,EAAE,YAAY,EAAE,KAAK;AACnD,sBAAc,QAAQ,OAAO,KAAK;AAClC;AAAA,MACF;AACA,UAAI,CAAC,YAAa;AAClB,YAAM,cAAc,KAAK,MAAM,yCAAyC;AACxE,UAAI,aAAa;AACf,cAAM,QAAQ,YAAY,CAAC,KAAK,YAAY,CAAC,GAAG,KAAK;AACrD,YAAI,KAAK,SAAS,EAAG;AACrB,cAAM,KAAK,KAAK,MAAM,kBAAkB,MAAM,EAAE,MAAM,aAAa,QAAQ,WAAW,UAAU,CAAC;AACjG,cAAM,cAAU,8BAAc,MAAM,KAAK,EAAE;AAC3C,YAAI,MAAM,QAAQ,SAAS,EAAG,MAAK,MAAM,cAAc,IAAI,QAAQ,IAAI,OAAK,EAAE,aAAa,CAAC;AAC5F,gBAAQ,KAAK,EAAE,IAAI,MAAM,aAAa,SAAS,KAAK,CAAC;AAAA,MACvD;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,gBAAgB,MAAc,OAAuB;AACnD,UAAM,OAAO,MACV,YAAY,EACZ,QAAQ,iBAAiB,EAAE,EAC3B,KAAK,EACL,MAAM,KAAK,EACX,MAAM,GAAG,CAAC,EACV,KAAK,GAAG;AACX,WAAO,GAAG,IAAI,IAAI,IAAI;AAAA,EACxB;AAAA;AAAA,EAIA,cAAc,QAAQ,IAA+F;AACnH,UAAM,SAAS,KAAK,MAAM,2BAA2B,GAAG,KAAK,IAAI,GAAG,KAAK;AACzE,UAAM,aAAwG,CAAC;AAC/G,UAAM,OAAO,oBAAI,IAAY;AAE7B,eAAW,OAAO,QAAQ;AACxB,UAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,SAAS,GAAI;AAC7C,YAAM,QAAQ,IAAI,QAAQ,MAAM,KAAK,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC3D,YAAM,UAAU,KAAK,MAAM,UAAU,OAAO,EAAE,OAAO,EAAE,CAAC;AACxD,iBAAW,SAAS,SAAS;AAC3B,YAAI,MAAM,YAAY,OAAO,IAAI,GAAI;AACrC,cAAM,UAAU,CAAC,IAAI,IAAI,MAAM,YAAY,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG;AAC9D,YAAI,KAAK,IAAI,OAAO,EAAG;AACvB,aAAK,IAAI,OAAO;AAChB,cAAM,WAAW,KAAK,MAAM,4BAA4B,CAAC,IAAI,EAAE,CAAC;AAChE,cAAM,gBAAgB,SAAS,IAAI,IAAI,EAAE,GAAG,KAAK,OAAK,EAAE,iBAAiB,MAAM,YAAY,MAAM,EAAE,iBAAiB,MAAM,YAAY,EAAE;AACxI,YAAI,CAAC,iBAAiB,MAAM,QAAQ,KAAK;AACvC,qBAAW,KAAK,EAAE,cAAc,KAAK,cAAc,MAAM,aAAa,YAAY,MAAM,MAAM,CAAC;AAAA,QACjG;AAAA,MACF;AAAA,IACF;AAEA,WAAO,WAAW,MAAM,GAAG,EAAE;AAAA,EAC/B;AAAA;AAAA,EAIQ,cAAc,MAAuB;AAC3C,QAAI,KAAK,gBAAgB,WAAW,EAAG,QAAO;AAI9C,eAAW,WAAW,KAAK,iBAAiB;AAC1C,YAAM,aAAa,QAAQ,QAAQ,OAAO,EAAE;AAC5C,UAAI,cAAc,KAAK,SAAS,UAAU,EAAG,QAAO;AAAA,IACtD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,aACN,YACA,eACA,OACqB;AAErB,UAAM,SAAS,KAAK,IAAI,GAAG,WAAW,IAAI,OAAK,EAAE,KAAK,GAAG,CAAC;AAC1D,UAAM,gBAAgB,oBAAI,IAAoB;AAC9C,eAAW,KAAK,YAAY;AAC1B,oBAAc,IAAI,EAAE,YAAY,IAAI,EAAE,QAAQ,MAAM;AAAA,IACtD;AAGA,UAAM,gBAAgB,oBAAI,IAAoB;AAC9C,eAAW,KAAK,eAAe;AAC7B,oBAAc,IAAI,EAAE,YAAY,IAAI,EAAE,KAAK;AAAA,IAC7C;AAGA,UAAM,SAAS,oBAAI,IAAI,CAAC,GAAG,cAAc,KAAK,GAAG,GAAG,cAAc,KAAK,CAAC,CAAC;AACzE,UAAM,SAA8B,CAAC;AAGrC,UAAM,SAAS,oBAAI,IAA4B;AAC/C,eAAW,KAAK,WAAY,QAAO,IAAI,EAAE,YAAY,IAAI,EAAE,WAAW;AACtE,eAAW,KAAK,cAAe,QAAO,IAAI,EAAE,YAAY,IAAI,EAAE,WAAW;AAEzE,eAAW,MAAM,QAAQ;AACvB,YAAM,WAAW,cAAc,IAAI,EAAE,KAAK;AAC1C,YAAM,WAAW,cAAc,IAAI,EAAE,KAAK;AAC1C,YAAM,eAAe,IAAI,SAAS,WAAW,QAAQ;AACrD,YAAM,MAAM,OAAO,IAAI,EAAE;AACzB,aAAO,KAAK,EAAE,aAAa,KAAK,OAAO,aAAa,aAAa,SAAS,CAAC;AAAA,IAC7E;AAEA,WAAO,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACvC,WAAO;AAAA,EACT;AACF;",
6
6
  "names": ["import_database", "import_compress", "import_symbols", "import_vectors", "import_schema", "sliced", "ids", "relMap"]
7
7
  }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,14 +17,25 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var schema_exports = {};
20
30
  __export(schema_exports, {
21
31
  MemorySchemaError: () => MemorySchemaError,
22
32
  OBSERVATION_KINDS: () => OBSERVATION_KINDS,
33
+ validateObservationFields: () => validateObservationFields,
23
34
  validateObservationInput: () => validateObservationInput
24
35
  });
25
36
  module.exports = __toCommonJS(schema_exports);
37
+ var path = __toESM(require("path"));
38
+ var import_json_schema = require("../shared/json-schema");
26
39
  const OBSERVATION_KINDS = [
27
40
  "decision",
28
41
  "error",
@@ -48,10 +61,32 @@ function validateObservationInput(input) {
48
61
  throw new MemorySchemaError("Observation tags must be non-empty strings");
49
62
  }
50
63
  }
64
+ function validateObservationFields(input, memorySchemasDir) {
65
+ const kind = input.kind ?? "note";
66
+ const schemaPath = path.join(memorySchemasDir, `${kind}.schema.json`);
67
+ let schema;
68
+ try {
69
+ schema = (0, import_json_schema.loadJsonSchema)(schemaPath);
70
+ } catch (err) {
71
+ if (err instanceof import_json_schema.SchemaLoadError) {
72
+ throw new MemorySchemaError(err.message);
73
+ }
74
+ throw err;
75
+ }
76
+ if (!schema) return;
77
+ const violations = (0, import_json_schema.validateAgainstSchema)(input.fields ?? {}, schema);
78
+ if (violations.length > 0) {
79
+ throw new MemorySchemaError(
80
+ `Observation fields for kind "${kind}" failed schema validation:
81
+ - ${violations.join("\n - ")}`
82
+ );
83
+ }
84
+ }
51
85
  // Annotate the CommonJS export names for ESM import in node:
52
86
  0 && (module.exports = {
53
87
  MemorySchemaError,
54
88
  OBSERVATION_KINDS,
89
+ validateObservationFields,
55
90
  validateObservationInput
56
91
  });
57
92
  //# sourceMappingURL=schema.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/schema.ts"],
4
- "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Opt-in write validation\n *\n * Off by default (config `memoryStrictWrites`): MemoryManager.store() has\n * never validated its input \u2014 `kind` is typed as a fixed union in\n * MemObservationInput, but every call site (CLI `--kind`, the\n * `kirograph_mem_store` MCP tool, direct API use) casts through `any` with\n * no runtime check, so an arbitrary string silently lands in the `kind`\n * column and breaks every downstream consumer that pattern-matches against\n * the known set (search filters, watchmen's passive-capture kind map,\n * timeline rendering). When `memoryStrictWrites` is enabled, writes that\n * don't conform are rejected with the violation named instead of silently\n * corrupting the store.\n */\nimport type { MemObservationInput, ObservationKind } from './types';\n\nexport const OBSERVATION_KINDS: readonly ObservationKind[] = [\n 'decision', 'error', 'pattern', 'architecture', 'summary', 'note',\n];\n\nexport class MemorySchemaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MemorySchemaError';\n }\n}\n\n/**\n * Validate an observation write against KiroGraph's memory model.\n * Throws MemorySchemaError naming the violation; callers only invoke this\n * when `memoryStrictWrites` is on.\n */\nexport function validateObservationInput(input: MemObservationInput): void {\n const kind = input.kind ?? 'note';\n if (!OBSERVATION_KINDS.includes(kind)) {\n throw new MemorySchemaError(\n `Invalid observation kind \"${kind}\" \u2014 must be one of: ${OBSERVATION_KINDS.join(', ')}`,\n );\n }\n\n if (input.tags?.some(t => typeof t !== 'string' || t.trim() === '')) {\n throw new MemorySchemaError('Observation tags must be non-empty strings');\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBO,MAAM,oBAAgD;AAAA,EAC3D;AAAA,EAAY;AAAA,EAAS;AAAA,EAAW;AAAA,EAAgB;AAAA,EAAW;AAC7D;AAEO,MAAM,0BAA0B,MAAM;AAAA,EAC3C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAOO,SAAS,yBAAyB,OAAkC;AACzE,QAAM,OAAO,MAAM,QAAQ;AAC3B,MAAI,CAAC,kBAAkB,SAAS,IAAI,GAAG;AACrC,UAAM,IAAI;AAAA,MACR,6BAA6B,IAAI,4BAAuB,kBAAkB,KAAK,IAAI,CAAC;AAAA,IACtF;AAAA,EACF;AAEA,MAAI,MAAM,MAAM,KAAK,OAAK,OAAO,MAAM,YAAY,EAAE,KAAK,MAAM,EAAE,GAAG;AACnE,UAAM,IAAI,kBAAkB,4CAA4C;AAAA,EAC1E;AACF;",
4
+ "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Opt-in write validation\n *\n * Off by default (config `memoryStrictWrites`): MemoryManager.store() has\n * never validated its input \u2014 `kind` is typed as a fixed union in\n * MemObservationInput, but every call site (CLI `--kind`, the\n * `kirograph_mem_store` MCP tool, direct API use) casts through `any` with\n * no runtime check, so an arbitrary string silently lands in the `kind`\n * column and breaks every downstream consumer that pattern-matches against\n * the known set (search filters, watchmen's passive-capture kind map,\n * timeline rendering). When `memoryStrictWrites` is enabled, writes that\n * don't conform are rejected with the violation named instead of silently\n * corrupting the store.\n *\n * `memorySchemaValidation` (separate, also off by default) goes further:\n * it validates `input.fields` \u2014 free-form structured data beyond `content`\n * \u2014 against a JSON Schema registered per kind at\n * `.kirograph/memory-schemas/<kind>.schema.json`. A kind with no schema\n * file is never validated (schemas are opt-in per kind, not required), so\n * enabling this flag with zero schemas present is a no-op.\n */\nimport * as path from 'path';\nimport type { MemObservationInput, ObservationKind } from './types';\nimport { loadJsonSchema, validateAgainstSchema, SchemaLoadError } from '../shared/json-schema';\n\nexport const OBSERVATION_KINDS: readonly ObservationKind[] = [\n 'decision', 'error', 'pattern', 'architecture', 'summary', 'note',\n];\n\nexport class MemorySchemaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MemorySchemaError';\n }\n}\n\n/**\n * Validate an observation write against KiroGraph's memory model.\n * Throws MemorySchemaError naming the violation; callers only invoke this\n * when `memoryStrictWrites` is on.\n */\nexport function validateObservationInput(input: MemObservationInput): void {\n const kind = input.kind ?? 'note';\n if (!OBSERVATION_KINDS.includes(kind)) {\n throw new MemorySchemaError(\n `Invalid observation kind \"${kind}\" \u2014 must be one of: ${OBSERVATION_KINDS.join(', ')}`,\n );\n }\n\n if (input.tags?.some(t => typeof t !== 'string' || t.trim() === '')) {\n throw new MemorySchemaError('Observation tags must be non-empty strings');\n }\n}\n\n/**\n * Validate `input.fields` against `.kirograph/memory-schemas/<kind>.schema.json`\n * when a schema is registered for the observation's kind. Only invoked when\n * `memorySchemaValidation` is enabled; throws MemorySchemaError naming every\n * violation when the schema exists and fields don't conform.\n */\nexport function validateObservationFields(input: MemObservationInput, memorySchemasDir: string): void {\n const kind = input.kind ?? 'note';\n const schemaPath = path.join(memorySchemasDir, `${kind}.schema.json`);\n\n let schema;\n try {\n schema = loadJsonSchema(schemaPath);\n } catch (err) {\n if (err instanceof SchemaLoadError) {\n throw new MemorySchemaError(err.message);\n }\n throw err;\n }\n if (!schema) return; // no schema registered for this kind \u2014 nothing to validate\n\n const violations = validateAgainstSchema(input.fields ?? {}, schema);\n if (violations.length > 0) {\n throw new MemorySchemaError(\n `Observation fields for kind \"${kind}\" failed schema validation:\\n - ${violations.join('\\n - ')}`,\n );\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBA,WAAsB;AAEtB,yBAAuE;AAEhE,MAAM,oBAAgD;AAAA,EAC3D;AAAA,EAAY;AAAA,EAAS;AAAA,EAAW;AAAA,EAAgB;AAAA,EAAW;AAC7D;AAEO,MAAM,0BAA0B,MAAM;AAAA,EAC3C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAOO,SAAS,yBAAyB,OAAkC;AACzE,QAAM,OAAO,MAAM,QAAQ;AAC3B,MAAI,CAAC,kBAAkB,SAAS,IAAI,GAAG;AACrC,UAAM,IAAI;AAAA,MACR,6BAA6B,IAAI,4BAAuB,kBAAkB,KAAK,IAAI,CAAC;AAAA,IACtF;AAAA,EACF;AAEA,MAAI,MAAM,MAAM,KAAK,OAAK,OAAO,MAAM,YAAY,EAAE,KAAK,MAAM,EAAE,GAAG;AACnE,UAAM,IAAI,kBAAkB,4CAA4C;AAAA,EAC1E;AACF;AAQO,SAAS,0BAA0B,OAA4B,kBAAgC;AACpG,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,aAAa,KAAK,KAAK,kBAAkB,GAAG,IAAI,cAAc;AAEpE,MAAI;AACJ,MAAI;AACF,iBAAS,mCAAe,UAAU;AAAA,EACpC,SAAS,KAAK;AACZ,QAAI,eAAe,oCAAiB;AAClC,YAAM,IAAI,kBAAkB,IAAI,OAAO;AAAA,IACzC;AACA,UAAM;AAAA,EACR;AACA,MAAI,CAAC,OAAQ;AAEb,QAAM,iBAAa,0CAAsB,MAAM,UAAU,CAAC,GAAG,MAAM;AACnE,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,IAAI;AAAA,MACR,gCAAgC,IAAI;AAAA,MAAoC,WAAW,KAAK,QAAQ,CAAC;AAAA,IACnG;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/types.ts"],
4
- "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Type definitions\n */\n\n// \u2500\u2500 Session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemSession {\n id: string;\n ide?: string;\n cwd?: string;\n startedAt: number;\n endedAt?: number;\n}\n\n// \u2500\u2500 Observation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ObservationKind = 'decision' | 'error' | 'pattern' | 'architecture' | 'summary' | 'note';\nexport type ObservationSource = 'hook' | 'manual' | 'agent';\n\nexport interface MemObservation {\n id: string;\n sessionId?: string;\n content: string;\n contentRaw?: string;\n contentHash: string;\n kind: ObservationKind;\n source: ObservationSource;\n tags?: string[];\n createdAt: number;\n /** Temporal validity: when this fact became valid (epoch ms). */\n validFrom?: number;\n /** Temporal validity: when this fact expires (epoch ms). Null = no expiry. */\n validUntil?: number;\n /** ID of the observation that supersedes this one. */\n supersededBy?: string;\n /** Fact type for temporal classification. */\n factType?: 'observation' | 'decision' | 'procedure' | 'constraint';\n topicKey?: string;\n reviewAfter?: number;\n}\n\nexport interface MemObservationInput {\n content: string;\n kind?: ObservationKind;\n source?: ObservationSource;\n tags?: string[];\n topicKey?: string;\n reviewAfter?: number;\n}\n\n// \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemLink {\n observationId: string;\n qualifiedName: string;\n relevance: number;\n}\n\n// \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ScoredObservation {\n observation: MemObservation;\n score: number;\n /** Source of the score: 'fts', 'vector', or 'hybrid' */\n scoreSource: 'fts' | 'vector' | 'hybrid';\n /** Active relations for this observation (populated by search) */\n relations?: MemRelation[];\n}\n\nexport interface MemSearchOptions {\n limit?: number;\n kind?: ObservationKind;\n sessionId?: string;\n /** FTS/vector blend: 0 = FTS only, 1 = vector only, 0.5 = equal blend */\n alpha?: number;\n /** Query facts valid at this timestamp (epoch ms). Filters out expired/superseded observations. */\n asOf?: number;\n}\n\nexport interface MemTimelineOptions {\n limit?: number;\n sessionId?: string;\n}\n\n// \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemStats {\n sessions: number;\n activeSessions: number;\n observations: number;\n links: number;\n vectors: number;\n embeddableCount: number;\n modelMismatch: boolean;\n currentModel?: string;\n relations: number;\n pendingConflicts: number;\n}\n\n// \u2500\u2500 Watchmen \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface WatchmenReadyResult {\n id: string;\n watchmenReady: true;\n pendingCount: number;\n /** Synthesis instructions for the active agent. */\n message: string;\n /** Project-relative paths to write the workspace brief to. */\n targetFiles: string[];\n /**\n * Present when Kiro is detected (.kiro/ exists). Path to the steering\n * directory where individual skill files should be written as separate\n * `inclusion: manual` files (e.g. `.kiro/steering/watchmen-<slug>.md`).\n * Absent for all other tools \u2014 embed procedures in the brief instead.\n */\n skillTargetDir?: string;\n}\n\n// \u2500\u2500 Compress \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface CompressResult {\n compressed: string;\n originalLength: number;\n compressedLength: number;\n detectedSymbols: string[];\n}\n\n// \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type RelationType =\n | 'supersedes'\n | 'conflicts_with'\n | 'compatible'\n | 'scoped'\n | 'related'\n | 'not_conflict';\n\nexport type JudgmentStatus = 'pending' | 'judged' | 'ignored';\n\nexport interface MemRelation {\n id: string;\n observationA: string;\n observationB: string;\n relation: RelationType;\n confidence: number;\n reason?: string;\n evidence?: string;\n judgmentStatus: JudgmentStatus;\n judgedAt?: number;\n createdAt: number;\n}\n\nexport interface MemRelationInput {\n observationA: string;\n observationB: string;\n relation: RelationType;\n confidence?: number;\n reason?: string;\n evidence?: string;\n}\n\n// \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemPrompt {\n id: string;\n sessionId?: string;\n content: string;\n createdAt: number;\n}\n"],
4
+ "sourcesContent": ["/**\n * KiroGraph Memory \u2014 Type definitions\n */\n\n// \u2500\u2500 Session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemSession {\n id: string;\n ide?: string;\n cwd?: string;\n startedAt: number;\n endedAt?: number;\n}\n\n// \u2500\u2500 Observation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ObservationKind = 'decision' | 'error' | 'pattern' | 'architecture' | 'summary' | 'note';\nexport type ObservationSource = 'hook' | 'manual' | 'agent';\n\nexport interface MemObservation {\n id: string;\n sessionId?: string;\n content: string;\n contentRaw?: string;\n contentHash: string;\n kind: ObservationKind;\n source: ObservationSource;\n tags?: string[];\n createdAt: number;\n /** Temporal validity: when this fact became valid (epoch ms). */\n validFrom?: number;\n /** Temporal validity: when this fact expires (epoch ms). Null = no expiry. */\n validUntil?: number;\n /** ID of the observation that supersedes this one. */\n supersededBy?: string;\n /** Fact type for temporal classification. */\n factType?: 'observation' | 'decision' | 'procedure' | 'constraint';\n topicKey?: string;\n reviewAfter?: number;\n /**\n * Structured extra fields beyond `content`, validated per-`kind` against\n * `.kirograph/memory-schemas/<kind>.schema.json` when memorySchemaValidation\n * is enabled. Always freely settable \u2014 validation is opt-in.\n */\n fields?: Record<string, unknown>;\n}\n\nexport interface MemObservationInput {\n content: string;\n kind?: ObservationKind;\n source?: ObservationSource;\n tags?: string[];\n topicKey?: string;\n reviewAfter?: number;\n fields?: Record<string, unknown>;\n}\n\n// \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemLink {\n observationId: string;\n qualifiedName: string;\n relevance: number;\n}\n\n// \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ScoredObservation {\n observation: MemObservation;\n score: number;\n /** Source of the score: 'fts', 'vector', or 'hybrid' */\n scoreSource: 'fts' | 'vector' | 'hybrid';\n /** Active relations for this observation (populated by search) */\n relations?: MemRelation[];\n}\n\nexport interface MemSearchOptions {\n limit?: number;\n kind?: ObservationKind;\n sessionId?: string;\n /** FTS/vector blend: 0 = FTS only, 1 = vector only, 0.5 = equal blend */\n alpha?: number;\n /** Query facts valid at this timestamp (epoch ms). Filters out expired/superseded observations. */\n asOf?: number;\n}\n\nexport interface MemTimelineOptions {\n limit?: number;\n sessionId?: string;\n}\n\n// \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemStats {\n sessions: number;\n activeSessions: number;\n observations: number;\n links: number;\n vectors: number;\n embeddableCount: number;\n modelMismatch: boolean;\n currentModel?: string;\n relations: number;\n pendingConflicts: number;\n}\n\n// \u2500\u2500 Watchmen \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface WatchmenReadyResult {\n id: string;\n watchmenReady: true;\n pendingCount: number;\n /** Synthesis instructions for the active agent. */\n message: string;\n /** Project-relative paths to write the workspace brief to. */\n targetFiles: string[];\n /**\n * Present when Kiro is detected (.kiro/ exists). Path to the steering\n * directory where individual skill files should be written as separate\n * `inclusion: manual` files (e.g. `.kiro/steering/watchmen-<slug>.md`).\n * Absent for all other tools \u2014 embed procedures in the brief instead.\n */\n skillTargetDir?: string;\n}\n\n// \u2500\u2500 Compress \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface CompressResult {\n compressed: string;\n originalLength: number;\n compressedLength: number;\n detectedSymbols: string[];\n}\n\n// \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type RelationType =\n | 'supersedes'\n | 'conflicts_with'\n | 'compatible'\n | 'scoped'\n | 'related'\n | 'not_conflict';\n\nexport type JudgmentStatus = 'pending' | 'judged' | 'ignored';\n\nexport interface MemRelation {\n id: string;\n observationA: string;\n observationB: string;\n relation: RelationType;\n confidence: number;\n reason?: string;\n evidence?: string;\n judgmentStatus: JudgmentStatus;\n judgedAt?: number;\n createdAt: number;\n}\n\nexport interface MemRelationInput {\n observationA: string;\n observationB: string;\n relation: RelationType;\n confidence?: number;\n reason?: string;\n evidence?: string;\n}\n\n// \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemPrompt {\n id: string;\n sessionId?: string;\n content: string;\n createdAt: number;\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }