remem-mcp 0.5.17
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.
- package/LICENSE +21 -0
- package/README.md +235 -0
- package/dist/chunk-34TEZ5U4.js +1676 -0
- package/dist/chunk-34TEZ5U4.js.map +1 -0
- package/dist/chunk-RITPZHIB.js +475 -0
- package/dist/chunk-RITPZHIB.js.map +1 -0
- package/dist/engine-ZNZRRSBV.js +25 -0
- package/dist/engine-ZNZRRSBV.js.map +1 -0
- package/dist/index.js +12625 -0
- package/dist/index.js.map +1 -0
- package/dist/sdk.js +41 -0
- package/dist/sdk.js.map +1 -0
- package/dist/storage/schema.sql +292 -0
- package/package.json +82 -0
- package/scripts/bench-all.sh +147 -0
- package/scripts/cast-to-mp4.py +264 -0
- package/scripts/loop-bench.sh +179 -0
- package/scripts/loop-longmemeval.sh +159 -0
- package/scripts/loop-mem0-bench.sh +310 -0
- package/scripts/merge-casts.py +131 -0
- package/scripts/postinstall.js +51 -0
- package/skills/remem-mcp/SKILL.md +562 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/embedding/local.ts","../src/sdk.ts","../src/security/redactor.ts","../src/storage/sqlite.ts","../src/utils/rrf.ts","../src/wiki/engine.ts"],"sourcesContent":["import { pipeline } from \"@huggingface/transformers\";\nimport type { Embedder } from \"./types.js\";\n\n/**\n * Local embedder. Uses the ONNX runtime and the model all-MiniLM-L6-v2.\n * The vector has 384 dimensions. No API key is needed.\n *\n * The first run downloads the model. Subsequent runs load the model from the disk cache.\n */\nexport class LocalEmbedder implements Embedder {\n readonly dimension = 384;\n readonly model = \"Xenova/all-MiniLM-L6-v2\";\n\n private extractor: Awaited<ReturnType<typeof pipeline>> | null = null;\n private initPromise: Promise<void> | null = null;\n\n /** Initialize the model. This runs once. Subsequent calls return immediately. */\n private async init(): Promise<void> {\n if (this.extractor) return;\n if (this.initPromise) {\n await this.initPromise;\n return;\n }\n this.initPromise = (async () => {\n this.extractor = await pipeline(\"feature-extraction\", this.model);\n })();\n await this.initPromise;\n }\n\n async embed(text: string): Promise<number[]> {\n await this.init();\n if (!this.extractor) throw new Error(\"Embedder failed to initialize\");\n\n const output = await this.extractor(text, { pooling: \"mean\", normalize: true });\n // output.data is a Float32Array\n return Array.from(output.data as Float32Array);\n }\n}\n","/**\n * Programmatic API for remem-mcp.\n * Use this to embed memory directly in your application without MCP.\n *\n * @example\n * ```ts\n * import { Memory } from \"remem-mcp\";\n *\n * const memory = new Memory();\n * await memory.capture(\"We chose SQLite for storage.\", \"decision\", [\"arch\"]);\n * const results = await memory.recall(\"storage decision\");\n * ```\n */\n\nimport { createHash } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { LocalEmbedder } from \"./embedding/local.js\";\nimport { redact } from \"./security/redactor.js\";\nimport { SQLiteBackend } from \"./storage/sqlite.js\";\nimport type {\n CaptureEntry,\n CaptureType,\n DeleteFilter,\n DeleteResult,\n SearchFilters,\n SearchMode,\n SearchResult,\n} from \"./storage/types.js\";\nimport { generateId } from \"./utils/ulid.js\";\n\nexport type {\n CaptureEntry,\n CaptureType,\n DeleteFilter,\n DeleteResult,\n SearchFilters,\n SearchMode,\n SearchResult,\n};\nexport { LocalEmbedder, SQLiteBackend };\n\n/** High-level memory API. */\nexport class Memory {\n private storage: SQLiteBackend;\n private embedder: LocalEmbedder;\n private sessionKey: string;\n private redactSecrets: boolean;\n\n constructor(opts?: {\n dbPath?: string;\n sessionKey?: string;\n redactSecrets?: boolean;\n }) {\n const dbPath =\n opts?.dbPath ?? join(homedir(), \".local\", \"share\", \"remem-mcp\", \"memory.db\");\n this.storage = new SQLiteBackend(dbPath);\n this.embedder = new LocalEmbedder();\n this.sessionKey =\n opts?.sessionKey ??\n process.env.TDAI_SESSION_KEY ??\n createHash(\"sha256\").update(process.cwd()).digest(\"hex\").slice(0, 16);\n this.redactSecrets = opts?.redactSecrets ?? true;\n }\n\n /** Capture a memory entry. Returns the ID, or null if duplicate. */\n async capture(content: string, type: CaptureType, tags: string[] = []): Promise<string | null> {\n const { text: redactedContent } = this.redactSecrets ? redact(content) : { text: content };\n\n // Dedup check\n const contentHash = createHash(\"sha256\").update(redactedContent).digest(\"hex\");\n const existing = await this.storage.findByContentHash(contentHash, this.sessionKey);\n if (existing.length > 0) return null;\n\n const id = generateId();\n const entry: CaptureEntry = {\n id,\n sessionKey: this.sessionKey,\n agentId: \"sdk\",\n type,\n content: redactedContent,\n tags,\n createdAt: Date.now(),\n };\n\n await this.storage.put(entry);\n\n try {\n const embedding = await this.embedder.embed(redactedContent);\n await this.storage.putVector(id, embedding);\n } catch {\n // Embedding is optional\n }\n\n return id;\n }\n\n /** Recall relevant memory. */\n async recall(\n query: string,\n opts?: { limit?: number; mode?: SearchMode },\n ): Promise<SearchResult[]> {\n const limit = Math.min(opts?.limit ?? 10, 50);\n const mode = opts?.mode ?? \"hybrid\";\n\n let queryEmbedding: number[] | null = null;\n if (mode === \"hybrid\" || mode === \"vector\") {\n queryEmbedding = await this.embedder.embed(query);\n }\n\n return this.storage.search(query, queryEmbedding, {\n sessionKey: this.sessionKey,\n limit,\n offset: 0,\n mode,\n });\n }\n\n /** Search with filters. */\n async search(\n query: string,\n opts?: { mode?: SearchMode; filters?: SearchFilters; limit?: number },\n ): Promise<SearchResult[]> {\n const limit = Math.min(opts?.limit ?? 20, 100);\n const mode = opts?.mode ?? \"hybrid\";\n\n let queryEmbedding: number[] | null = null;\n if (mode === \"hybrid\" || mode === \"vector\") {\n queryEmbedding = await this.embedder.embed(query);\n }\n\n return this.storage.search(query, queryEmbedding, {\n limit,\n offset: 0,\n mode,\n filters: opts?.filters,\n });\n }\n\n /** Delete a capture by ID. */\n async forget(id: string): Promise<DeleteResult> {\n return this.storage.delete(id);\n }\n\n /** Create a handoff packet for the next agent session. */\n async handoff(opts: {\n task: string;\n status: \"in_progress\" | \"blocked\" | \"needs_review\" | \"done\" | \"assigned\";\n progress: string;\n decisions?: string[];\n files?: string[];\n nextSteps?: string[];\n }): Promise<string | null> {\n const lines: string[] = [];\n lines.push(`# Handoff: ${opts.task}`);\n lines.push(`Status: ${opts.status}`);\n lines.push(`Date: ${new Date().toISOString()}`);\n lines.push(\"\");\n lines.push(\"## Progress\");\n lines.push(opts.progress);\n lines.push(\"\");\n\n if (opts.decisions && opts.decisions.length > 0) {\n lines.push(\"## Decisions\");\n for (const d of opts.decisions) lines.push(`- ${d}`);\n lines.push(\"\");\n }\n if (opts.files && opts.files.length > 0) {\n lines.push(\"## Files\");\n for (const f of opts.files) lines.push(`- ${f}`);\n lines.push(\"\");\n }\n if (opts.nextSteps && opts.nextSteps.length > 0) {\n lines.push(\"## Next steps\");\n opts.nextSteps.forEach((s, i) => {\n lines.push(`${i + 1}. ${s}`);\n });\n lines.push(\"\");\n }\n\n const content = lines.join(\"\\n\");\n // Dedup: hash the structured data (excluding the timestamp)\n const dedupPayload = JSON.stringify({\n task: opts.task,\n status: opts.status,\n progress: opts.progress,\n decisions: opts.decisions ?? [],\n files: opts.files ?? [],\n nextSteps: opts.nextSteps ?? [],\n });\n const contentHash = createHash(\"sha256\").update(dedupPayload).digest(\"hex\");\n const existing = await this.storage.findByContentHash(contentHash, this.sessionKey);\n if (existing.length > 0) return null;\n\n const id = generateId();\n const entry: CaptureEntry = {\n id,\n sessionKey: this.sessionKey,\n agentId: \"sdk\",\n type: \"task\",\n content,\n tags: [\"handoff\", `status:${opts.status}`],\n createdAt: Date.now(),\n metadata: {\n handoff: true,\n task: opts.task,\n status: opts.status,\n progress: opts.progress,\n decisions: opts.decisions ?? [],\n files: opts.files ?? [],\n nextSteps: opts.nextSteps ?? [],\n },\n contentHash,\n };\n\n await this.storage.put(entry);\n\n try {\n const embedding = await this.embedder.embed(content);\n await this.storage.putVector(id, embedding);\n } catch {\n // Embedding is optional\n }\n\n return id;\n }\n\n /** Record an Architecture Decision Record (ADR). */\n async adr(opts: {\n title: string;\n context: string;\n decision: string;\n alternatives?: string[];\n consequences?: string;\n tags?: string[];\n }): Promise<string | null> {\n const lines: string[] = [];\n lines.push(`# ADR: ${opts.title}`);\n lines.push(`Date: ${new Date().toISOString()}`);\n lines.push(\"\");\n lines.push(\"## Context\");\n lines.push(opts.context);\n lines.push(\"\");\n lines.push(\"## Decision\");\n lines.push(opts.decision);\n lines.push(\"\");\n\n const alternatives = Array.isArray(opts.alternatives)\n ? opts.alternatives\n : typeof opts.alternatives === \"string\" && opts.alternatives\n ? [opts.alternatives]\n : [];\n if (alternatives.length > 0) {\n lines.push(\"## Alternatives considered\");\n for (const alt of alternatives) lines.push(`- ${alt}`);\n lines.push(\"\");\n }\n if (opts.consequences) {\n lines.push(\"## Consequences\");\n lines.push(opts.consequences);\n lines.push(\"\");\n }\n\n const content = lines.join(\"\\n\");\n const dedupPayload = JSON.stringify({\n title: opts.title,\n context: opts.context,\n decision: opts.decision,\n alternatives,\n consequences: opts.consequences ?? \"\",\n });\n const contentHash = createHash(\"sha256\").update(dedupPayload).digest(\"hex\");\n const existing = await this.storage.findByContentHash(contentHash, this.sessionKey);\n if (existing.length > 0) return null;\n\n const id = generateId();\n const entry: CaptureEntry = {\n id,\n sessionKey: this.sessionKey,\n agentId: \"sdk\",\n type: \"decision\",\n content,\n tags: [\"adr\", ...(opts.tags ?? [])],\n createdAt: Date.now(),\n metadata: {\n adr: true,\n title: opts.title,\n context: opts.context,\n decision: opts.decision,\n alternatives,\n consequences: opts.consequences ?? \"\",\n },\n contentHash,\n };\n\n await this.storage.put(entry);\n\n try {\n const embedding = await this.embedder.embed(content);\n await this.storage.putVector(id, embedding);\n } catch {\n // Embedding is optional\n }\n\n return id;\n }\n\n /** Close the database connection. */\n close(): void {\n this.storage.close();\n }\n}\n\nexport type {\n CallInfo,\n ImpactResult,\n ImportInfo,\n IndexResult,\n SymbolInfo,\n} from \"./codegraph/engine.js\";\n// Re-export CodeGraph engine for programmatic use\nexport {\n detectLanguage,\n findCallees,\n findCallers,\n impactAnalysis,\n indexDirectory,\n indexFile,\n listSymbols,\n SUPPORTED_LANGUAGES,\n searchSymbols,\n} from \"./codegraph/engine.js\";\nexport type { IngestResult as WikiIngestResult, WikiLink, WikiPage } from \"./wiki/engine.js\";\n// Re-export Wiki engine for programmatic use\nexport {\n findOutdatedPages,\n getWikiPage,\n ingestDirectory as wikiIngestDirectory,\n ingestFile as wikiIngestFile,\n searchWiki,\n} from \"./wiki/engine.js\";\n","/**\n * Secret redactor.\n * Scans text for secrets and replaces them with [REDACTED].\n * Uses regex patterns and a high-entropy detector.\n */\n\n/** Regex patterns for known secret formats. */\nconst SECRET_PATTERNS: RegExp[] = [\n // OpenAI API key\n /sk-[a-zA-Z0-9]{20,}/g,\n // Anthropic API key\n /sk-ant-[a-zA-Z0-9-]+/g,\n // GitHub personal access token\n /ghp_[a-zA-Z0-9]{36}/g,\n // GitHub OAuth token\n /gho_[a-zA-Z0-9]{36}/g,\n // GitHub fine-grained token\n /github_pat_[a-zA-Z0-9_]{82}/g,\n // Slack token\n /xox[baprs]-[a-zA-Z0-9-]+/g,\n // AWS access key ID\n /AKIA[0-9A-Z]{16}/g,\n // AWS secret access key (40 chars, base64-ish)\n /(?<![A-Za-z0-9+/])[A-Za-z0-9+/]{40}(?![A-Za-z0-9+/])/g,\n // PEM private key block\n /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,\n // Google API key\n /AIza[0-9A-Za-z\\-_]{35}/g,\n // Generic bearer token (require 20+ char token to avoid redacting \"Bearer with JWT\")\n /Bearer\\s+[a-zA-Z0-9\\-._~+/]{20,}=*/g,\n];\n\n/** Minimum length for high-entropy detection. */\nconst MIN_LENGTH = 40;\n\n/** Entropy threshold in bits per character. */\nconst ENTROPY_THRESHOLD = 4.5;\n\n/** Compute the Shannon entropy of a string, in bits per character. */\nfunction shannonEntropy(str: string): number {\n const freq = new Map<string, number>();\n for (const ch of str) {\n freq.set(ch, (freq.get(ch) ?? 0) + 1);\n }\n let entropy = 0;\n const len = str.length;\n for (const count of freq.values()) {\n const p = count / len;\n entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\n/** Find high-entropy strings that look like secrets. */\nfunction findHighEntropyStrings(text: string): string[] {\n const results: string[] = [];\n // Match long strings of alphanumeric, +, /, =\n const regex = /[a-zA-Z0-9+/=]{40,}/g;\n let match: RegExpExecArray | null;\n match = regex.exec(text);\n while (match !== null) {\n const str = match[0];\n if (str.length >= MIN_LENGTH) {\n const entropy = shannonEntropy(str);\n if (entropy >= ENTROPY_THRESHOLD) {\n results.push(str);\n }\n }\n match = regex.exec(text);\n }\n return results;\n}\n\nexport interface RedactionResult {\n /** The redacted text. */\n text: string;\n /** Whether any secrets were found and redacted. */\n redacted: boolean;\n}\n\n/** Redact secrets from text. Returns the redacted text and a flag. */\nexport function redact(text: string): RedactionResult {\n let result = text;\n let redacted = false;\n\n // Apply regex patterns\n for (const pattern of SECRET_PATTERNS) {\n const before = result;\n result = result.replace(pattern, \"[REDACTED]\");\n if (result !== before) redacted = true;\n }\n\n // Apply high-entropy detection\n const highEntropy = findHighEntropyStrings(result);\n for (const str of highEntropy) {\n // Make sure we did not already redact this region\n result = result.replace(str, \"[REDACTED]\");\n redacted = true;\n }\n\n return { text: result, redacted };\n}\n","import { createHash } from \"node:crypto\";\nimport { copyFileSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport Database from \"better-sqlite3\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport { type RankedResult, rrfMerge } from \"../utils/rrf.js\";\nimport { generateId } from \"../utils/ulid.js\";\nimport type {\n AtomEntry,\n CaptureEntry,\n ConflictResult,\n DeleteFilter,\n DeleteResult,\n KnowledgeEntry,\n MessageRow,\n PersonaEntry,\n QueryOptions,\n ResolveResult,\n ScenarioEntry,\n SearchResult,\n SkillEntry,\n StorageBackend,\n TrustState,\n} from \"./types.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/** Current schema version. */\nconst CURRENT_SCHEMA_VERSION = 6;\n\n/**\n * SQLite storage backend.\n * Uses better-sqlite3 + sqlite-vec + FTS5.\n * Default backend. Zero setup.\n */\nexport class SQLiteBackend implements StorageBackend {\n private db: Database.Database;\n\n /** Get the underlying database instance (for CodeGraph/Wiki operations). */\n getDatabase(): Database.Database {\n return this.db;\n }\n\n constructor(dbPath: string) {\n // Make sure the directory exists\n const dir = dirname(dbPath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\n // Try read-write first; fall back to read-only if the directory is not writable\n // (e.g. Codex CLI sandbox blocks writes outside the workdir)\n let readonly = false;\n try {\n this.db = new Database(dbPath);\n this.db.pragma(\"journal_mode = WAL\");\n } catch {\n // Read-only fallback — recall/search still work, captures will fail gracefully\n readonly = true;\n this.db = new Database(dbPath, { readonly: true });\n }\n\n // Pragmas that are safe for both read-write and read-only\n try {\n this.db.pragma(\"busy_timeout = 5000\");\n } catch {\n /* read-only */\n }\n if (!readonly) {\n this.db.pragma(\"synchronous = NORMAL\");\n this.db.pragma(\"foreign_keys = OFF\");\n }\n\n // Load the sqlite-vec extension\n sqliteVec.load(this.db);\n\n // Detect the database state and run the migration (skipped in read-only mode)\n if (!readonly) {\n try {\n this.detectAndMigrate(dbPath);\n } catch {\n // Migration failed — non-fatal, schema might already be correct\n }\n }\n }\n\n /**\n * Detect the database state and run the correct migration path.\n */\n private detectAndMigrate(dbPath: string): void {\n const hasVersionTable = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'\")\n .get() as { name: string } | undefined;\n\n if (!hasVersionTable) {\n // Fresh database or old database without versioning\n const tables = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\")\n .all() as { name: string }[];\n\n if (tables.length === 0) {\n // Fresh database: run the full schema, write current version\n this.runSchema();\n this.writeSchemaVersion(CURRENT_SCHEMA_VERSION);\n } else {\n // Old database without versioning. Backup, then run incremental migrations\n // to add missing columns before running the full schema (which creates new tables).\n this.backupDatabase(dbPath);\n this.migrateV1ToV2();\n this.migrateV2ToV3();\n this.migrateV3ToV4();\n this.migrateV4ToV5();\n this.migrateV5ToV6();\n // Now run the full schema to create any remaining tables/triggers/indexes\n this.runSchema();\n this.writeSchemaVersion(CURRENT_SCHEMA_VERSION);\n }\n return;\n }\n\n // Database has a schema_version table. Read the current version.\n const row = this.db.prepare(\"SELECT MAX(version) as version FROM schema_version\").get() as\n | { version: number }\n | undefined;\n const currentVersion = row?.version ?? 0;\n\n if (currentVersion < 1) {\n this.backupDatabase(dbPath);\n this.runSchema();\n this.writeSchemaVersion(1);\n }\n if (currentVersion < 2) {\n this.backupDatabase(dbPath);\n this.migrateV1ToV2();\n this.writeSchemaVersion(2);\n }\n if (currentVersion < 3) {\n this.backupDatabase(dbPath);\n this.migrateV2ToV3();\n this.writeSchemaVersion(3);\n }\n if (currentVersion < 4) {\n this.backupDatabase(dbPath);\n this.migrateV3ToV4();\n this.writeSchemaVersion(4);\n }\n if (currentVersion < 5) {\n this.backupDatabase(dbPath);\n this.migrateV4ToV5();\n this.writeSchemaVersion(5);\n }\n if (currentVersion < 6) {\n // Tables are created by runSchema() via CREATE TABLE IF NOT EXISTS.\n // Run schema to create CodeGraph + Wiki tables.\n this.runSchema();\n this.migrateV5ToV6();\n this.writeSchemaVersion(6);\n }\n }\n\n /** Backup the database to a .bak file. */\n private backupDatabase(dbPath: string): void {\n const backupPath = `${dbPath}.bak`;\n try {\n this.db.pragma(\"wal_checkpoint(FULL)\");\n copyFileSync(dbPath, backupPath);\n console.error(`[remem-mcp] Backed up database to ${backupPath}`);\n } catch (err) {\n console.error(`[remem-mcp] Backup failed: ${err}`);\n }\n }\n\n /** Run the schema.sql file. Idempotent. */\n private runSchema(): void {\n const candidates = [\n join(__dirname, \"storage\", \"schema.sql\"),\n join(__dirname, \"schema.sql\"),\n join(__dirname, \"..\", \"storage\", \"schema.sql\"),\n ];\n\n let schema: string | null = null;\n for (const path of candidates) {\n try {\n schema = readFileSync(path, \"utf-8\");\n break;\n } catch {\n // Try the next candidate\n }\n }\n\n if (!schema) {\n throw new Error(\"Could not find schema.sql. Make sure the build copied it to dist/storage/.\");\n }\n this.db.exec(schema);\n }\n\n /** Write the schema version to the schema_version table. */\n private writeSchemaVersion(version: number): void {\n this.db\n .prepare(\"INSERT INTO schema_version (version, applied_at) VALUES (?, ?)\")\n .run(version, Date.now());\n }\n\n /** Migrate schema v1 → v2: add content_hash column + index. */\n private migrateV1ToV2(): void {\n const cols = this.db.prepare(\"PRAGMA table_info(captures)\").all() as { name: string }[];\n const hasContentHash = cols.some((c) => c.name === \"content_hash\");\n if (!hasContentHash) {\n this.db.exec(\"ALTER TABLE captures ADD COLUMN content_hash TEXT\");\n console.error(\"[remem-mcp] Added content_hash column to captures\");\n }\n const idxs = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='index' AND name='idx_captures_hash'\")\n .get() as { name: string } | undefined;\n if (!idxs) {\n this.db.exec(\"CREATE INDEX IF NOT EXISTS idx_captures_hash ON captures (content_hash)\");\n }\n // Backfill content_hash for existing rows\n const rows = this.db\n .prepare(\"SELECT id, content FROM captures WHERE content_hash IS NULL\")\n .all() as { id: string; content: string }[];\n const stmt = this.db.prepare(\"UPDATE captures SET content_hash = ? WHERE id = ?\");\n for (const row of rows) {\n const hash = createHash(\"sha256\").update(row.content).digest(\"hex\");\n stmt.run(hash, row.id);\n }\n if (rows.length > 0) {\n console.error(`[remem-mcp] Backfilled content_hash for ${rows.length} existing captures`);\n }\n }\n\n /** Migrate schema v2 → v3: add multi-tenant columns + new tables (messages, knowledge, skills, persona). */\n private migrateV2ToV3(): void {\n // Helper: add a column to a table if the table exists and the column is missing\n const addColumnIfMissing = (table: string, column: string, definition: string) => {\n const tableExists = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name=?\")\n .get(table) as { name: string } | undefined;\n if (!tableExists) return;\n const cols = this.db.prepare(`PRAGMA table_info(${table})`).all() as { name: string }[];\n if (!cols.some((c) => c.name === column)) {\n this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);\n }\n };\n\n // Add multi-tenant columns to captures\n addColumnIfMissing(\"captures\", \"team_id\", \"TEXT\");\n addColumnIfMissing(\"captures\", \"user_id\", \"TEXT\");\n addColumnIfMissing(\"captures\", \"task_id\", \"TEXT\");\n\n // Add multi-tenant columns to atoms\n addColumnIfMissing(\"atoms\", \"team_id\", \"TEXT\");\n addColumnIfMissing(\"atoms\", \"agent_id\", \"TEXT\");\n addColumnIfMissing(\"atoms\", \"user_id\", \"TEXT\");\n\n // Add multi-tenant columns to scenarios\n addColumnIfMissing(\"scenarios\", \"team_id\", \"TEXT\");\n addColumnIfMissing(\"scenarios\", \"agent_id\", \"TEXT\");\n addColumnIfMissing(\"scenarios\", \"user_id\", \"TEXT\");\n\n // Create new tables (idempotent — schema.sql also has them, but run here for migration path\n // before schema.sql so that index creation in schema.sql doesn't fail)\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n capture_id TEXT NOT NULL REFERENCES captures(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n content TEXT NOT NULL,\n seq INTEGER NOT NULL,\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS persona (\n team_id TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n content TEXT NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (team_id, agent_id, user_id)\n );\n CREATE TABLE IF NOT EXISTS knowledge (\n id TEXT PRIMARY KEY,\n team_id TEXT NOT NULL,\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n summary TEXT,\n service_url TEXT,\n repo_url TEXT,\n branch TEXT,\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS skills (\n id TEXT PRIMARY KEY,\n team_id TEXT NOT NULL,\n agent_id TEXT,\n name TEXT NOT NULL,\n description TEXT,\n content TEXT,\n version INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n `);\n // Indexes are created by the full schema.sql run, not here.\n\n console.error(\n \"[remem-mcp] Migrated schema v2 → v3 (multi-tenant + messages + knowledge + skills + persona)\",\n );\n }\n\n /** Migrate schema v3 → v4: add deleted_at column for soft delete (tombstone). */\n private migrateV3ToV4(): void {\n const cols = this.db.prepare(\"PRAGMA table_info(captures)\").all() as { name: string }[];\n const hasDeletedAt = cols.some((c) => c.name === \"deleted_at\");\n if (!hasDeletedAt) {\n this.db.exec(\"ALTER TABLE captures ADD COLUMN deleted_at INTEGER\");\n console.error(\"[remem-mcp] Added deleted_at column to captures (tombstone support)\");\n }\n console.error(\"[remem-mcp] Migrated schema v3 → v4 (tombstone / soft delete)\");\n }\n\n /** Migrate schema v4 → v5: add trust_state, rejection_reason, superseded_by columns. */\n private migrateV4ToV5(): void {\n const cols = this.db.prepare(\"PRAGMA table_info(captures)\").all() as { name: string }[];\n const hasTrustState = cols.some((c) => c.name === \"trust_state\");\n if (!hasTrustState) {\n this.db.exec(\"ALTER TABLE captures ADD COLUMN trust_state TEXT NOT NULL DEFAULT 'candidate'\");\n console.error(\"[remem-mcp] Added trust_state column to captures\");\n }\n const hasRejectionReason = cols.some((c) => c.name === \"rejection_reason\");\n if (!hasRejectionReason) {\n this.db.exec(\"ALTER TABLE captures ADD COLUMN rejection_reason TEXT\");\n console.error(\"[remem-mcp] Added rejection_reason column to captures\");\n }\n const hasSupersededBy = cols.some((c) => c.name === \"superseded_by\");\n if (!hasSupersededBy) {\n this.db.exec(\"ALTER TABLE captures ADD COLUMN superseded_by TEXT REFERENCES captures(id)\");\n console.error(\"[remem-mcp] Added superseded_by column to captures\");\n }\n this.db.exec(\"CREATE INDEX IF NOT EXISTS idx_captures_trust ON captures (trust_state)\");\n this.db.exec(\n \"CREATE INDEX IF NOT EXISTS idx_captures_rejected_hash ON captures (content_hash) WHERE trust_state = 'rejected'\",\n );\n console.error(\"[remem-mcp] Migrated schema v4 → v5 (trust state + correction)\");\n }\n\n /** Migrate schema v5 → v6: add CodeGraph + Wiki tables (created by runSchema). */\n private migrateV5ToV6(): void {\n // Tables are created by runSchema() which runs CREATE TABLE IF NOT EXISTS.\n // This migration is a no-op placeholder for version tracking.\n console.error(\"[remem-mcp] Migrated schema v5 → v6 (CodeGraph + Wiki tables)\");\n }\n\n async put(entry: CaptureEntry): Promise<void> {\n const contentHash =\n entry.contentHash ?? createHash(\"sha256\").update(entry.content).digest(\"hex\");\n const stmt = this.db.prepare(`\n INSERT INTO captures (id, session_key, agent_id, type, content, content_hash, tags, created_at, metadata, team_id, user_id, task_id, trust_state)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n entry.id,\n entry.sessionKey,\n entry.agentId,\n entry.type,\n entry.content,\n contentHash,\n JSON.stringify(entry.tags),\n entry.createdAt,\n entry.metadata ? JSON.stringify(entry.metadata) : null,\n entry.teamId ?? null,\n entry.userId ?? null,\n entry.taskId ?? null,\n entry.trustState ?? \"candidate\",\n );\n\n // Store role-based messages if provided\n if (entry.messages && entry.messages.length > 0) {\n const msgStmt = this.db.prepare(\n \"INSERT INTO messages (id, capture_id, role, content, seq, created_at) VALUES (?, ?, ?, ?, ?, ?)\",\n );\n for (let i = 0; i < entry.messages.length; i++) {\n const msg = entry.messages[i];\n msgStmt.run(generateId(), entry.id, msg.role, msg.content, i, entry.createdAt);\n }\n }\n }\n\n async putVector(id: string, embedding: number[]): Promise<void> {\n const buffer = new Float32Array(embedding);\n const stmt = this.db.prepare(\"INSERT INTO captures_vec (id, embedding) VALUES (?, ?)\");\n stmt.run(id, Buffer.from(buffer.buffer));\n }\n\n async get(id: string): Promise<CaptureEntry | null> {\n const row = this.db\n .prepare(\"SELECT * FROM captures WHERE id = ? AND deleted_at IS NULL\")\n .get(id) as DbRow | undefined;\n if (!row) return null;\n return rowToEntry(row);\n }\n\n async findRejectedByContentHash(\n contentHash: string,\n sessionKey?: string,\n ): Promise<CaptureEntry[]> {\n let sql = \"SELECT * FROM captures WHERE content_hash = ? AND trust_state = 'rejected'\";\n const params: unknown[] = [contentHash];\n if (sessionKey) {\n sql += \" AND session_key = ?\";\n params.push(sessionKey);\n }\n const rows = this.db.prepare(sql).all(...params) as DbRow[];\n return rows.map(rowToEntry);\n }\n\n async getMessages(captureId: string): Promise<MessageRow[]> {\n const rows = this.db\n .prepare(\"SELECT * FROM messages WHERE capture_id = ? ORDER BY seq ASC\")\n .all(captureId) as MessageDbRow[];\n return rows.map((r) => ({\n id: r.id,\n captureId: r.capture_id,\n role: r.role,\n content: r.content,\n seq: r.seq,\n createdAt: r.created_at,\n }));\n }\n\n async findByContentHash(contentHash: string, sessionKey?: string): Promise<CaptureEntry[]> {\n let sql = \"SELECT * FROM captures WHERE content_hash = ? AND deleted_at IS NULL\";\n const params: unknown[] = [contentHash];\n if (sessionKey) {\n sql += \" AND session_key = ?\";\n params.push(sessionKey);\n }\n const rows = this.db.prepare(sql).all(...params) as DbRow[];\n return rows.map(rowToEntry);\n }\n\n async listByTags(tags: string[], limit = 50): Promise<CaptureEntry[]> {\n if (tags.length === 0) return [];\n const tagConditions = tags.map(() => \"tags LIKE ?\").join(\" OR \");\n const sql = `SELECT * FROM captures WHERE (${tagConditions}) AND deleted_at IS NULL AND trust_state != 'rejected' ORDER BY created_at DESC LIMIT ?`;\n const params = [...tags.map((t) => `%\"${t}\"%`), limit];\n const rows = this.db.prepare(sql).all(...params) as DbRow[];\n return rows.map(rowToEntry);\n }\n\n async search(\n query: string,\n queryEmbedding: number[] | null,\n opts: QueryOptions,\n ): Promise<SearchResult[]> {\n const { mode, limit, offset, sessionKey, filters } = opts;\n\n // Detect temporal intent: queries asking for \"current/latest/now\" state\n // should strongly prefer recent memories over older ones with better keyword match.\n const temporalIntent = /\\b(currently|current|now|latest|new|present|today|active)\\b/i.test(query);\n\n let bm25Results: RankedResult[] = [];\n let vecResults: RankedResult[] = [];\n\n // In hybrid mode, use different candidate pool sizes for each channel:\n // - BM25: tight pool (limit*2) for high precision — only best keyword matches\n // - Vector: very broad pool (limit*10, cap 1000) for high recall at scale\n // This ensures seed memories stored among 1000+ distractors are still found.\n // RRF fusion with weighted vector search filters out BM25 noise.\n const bm25CandidateLimit = mode === \"hybrid\" ? limit * 2 : limit * 2;\n const vecCandidateLimit = mode === \"hybrid\" ? Math.min(Math.max(limit * 10, 100), 1000) : limit * 2;\n\n // BM25 search (FTS5)\n if (mode === \"hybrid\" || mode === \"keyword\") {\n bm25Results = this.bm25Search(query, bm25CandidateLimit, sessionKey, filters);\n }\n\n // Vector search (sqlite-vec)\n if ((mode === \"hybrid\" || mode === \"vector\") && queryEmbedding) {\n vecResults = this.vectorSearch(queryEmbedding, vecCandidateLimit, sessionKey, filters);\n }\n\n if (mode === \"keyword\") {\n return this.fetchEntries(bm25Results, limit, offset, temporalIntent);\n }\n if (mode === \"vector\") {\n return this.fetchEntries(vecResults, limit, offset, temporalIntent);\n }\n\n // Hybrid: fuse with RRF\n const fused = rrfMerge(bm25Results, vecResults, limit + offset);\n const paged = fused.slice(offset, offset + limit);\n return this.fetchEntriesById(paged, temporalIntent);\n }\n\n /** Run a BM25 search via FTS5. */\n private bm25Search(\n query: string,\n limit: number,\n sessionKey?: string,\n filters?: QueryOptions[\"filters\"],\n ): RankedResult[] {\n const ftsQuery = this.escapeFtsQuery(query);\n if (!ftsQuery) return [];\n\n let sql: string;\n const params: unknown[] = [ftsQuery];\n try {\n sql = `\n SELECT fts.id as id, bm25(captures_fts) as score\n FROM captures_fts fts\n JOIN captures c ON c.id = fts.id\n WHERE captures_fts MATCH ? AND c.deleted_at IS NULL AND c.trust_state != 'rejected'\n `;\n\n if (sessionKey) {\n sql += \" AND c.session_key = ?\";\n params.push(sessionKey);\n }\n if (filters?.type) {\n sql += \" AND c.type = ?\";\n params.push(filters.type);\n }\n if (filters?.agentId) {\n sql += \" AND c.agent_id = ?\";\n params.push(filters.agentId);\n }\n if (filters?.teamId) {\n sql += \" AND c.team_id = ?\";\n params.push(filters.teamId);\n }\n if (filters?.userId) {\n sql += \" AND c.user_id = ?\";\n params.push(filters.userId);\n }\n if (filters?.taskId) {\n sql += \" AND c.task_id = ?\";\n params.push(filters.taskId);\n }\n if (filters?.tags && filters.tags.length > 0) {\n const tagConditions = filters.tags.map(() => \"c.tags LIKE ?\").join(\" OR \");\n sql += ` AND (${tagConditions})`;\n params.push(...filters.tags.map((t) => `%\"${t}\"%`));\n }\n if (filters?.dateFrom) {\n sql += \" AND c.created_at >= ?\";\n params.push(new Date(filters.dateFrom).getTime());\n }\n if (filters?.dateTo) {\n sql += \" AND c.created_at <= ?\";\n params.push(new Date(filters.dateTo).getTime());\n }\n\n sql += \" ORDER BY score LIMIT ?\";\n params.push(limit);\n\n const rows = this.db.prepare(sql).all(...params) as { id: string; score: number }[];\n return rows.map((r) => ({ id: r.id, score: r.score }));\n } catch (err: unknown) {\n // FTS5 external content tables can get out of sync after schema changes\n // or crashes. Rebuild the index and retry once.\n if (err instanceof Error && err.message.includes(\"missing row\")) {\n this.db.exec(\"INSERT INTO captures_fts(captures_fts) VALUES('rebuild')\");\n const rows = this.db.prepare(sql).all(...params) as { id: string; score: number }[];\n return rows.map((r) => ({ id: r.id, score: r.score }));\n }\n throw err;\n }\n }\n\n /** Run a vector search via sqlite-vec. */\n private vectorSearch(\n embedding: number[],\n limit: number,\n sessionKey?: string,\n filters?: QueryOptions[\"filters\"],\n ): RankedResult[] {\n const buffer = new Float32Array(embedding);\n let sql = `\n SELECT vec.id as id, vec.distance as score\n FROM captures_vec vec\n JOIN captures c ON c.id = vec.id\n WHERE vec.embedding MATCH ? AND vec.k = ? AND c.deleted_at IS NULL AND c.trust_state != 'rejected'\n `;\n const params: unknown[] = [Buffer.from(buffer.buffer), limit];\n\n if (sessionKey) {\n sql += \" AND c.session_key = ?\";\n params.push(sessionKey);\n }\n if (filters?.type) {\n sql += \" AND c.type = ?\";\n params.push(filters.type);\n }\n if (filters?.agentId) {\n sql += \" AND c.agent_id = ?\";\n params.push(filters.agentId);\n }\n if (filters?.teamId) {\n sql += \" AND c.team_id = ?\";\n params.push(filters.teamId);\n }\n if (filters?.userId) {\n sql += \" AND c.user_id = ?\";\n params.push(filters.userId);\n }\n if (filters?.taskId) {\n sql += \" AND c.task_id = ?\";\n params.push(filters.taskId);\n }\n if (filters?.tags && filters.tags.length > 0) {\n const tagConditions = filters.tags.map(() => \"c.tags LIKE ?\").join(\" OR \");\n sql += ` AND (${tagConditions})`;\n params.push(...filters.tags.map((t) => `%\"${t}\"%`));\n }\n if (filters?.dateFrom) {\n sql += \" AND c.created_at >= ?\";\n params.push(new Date(filters.dateFrom).getTime());\n }\n if (filters?.dateTo) {\n sql += \" AND c.created_at <= ?\";\n params.push(new Date(filters.dateTo).getTime());\n }\n\n sql += \" ORDER BY vec.distance LIMIT ?\";\n params.push(limit);\n\n const rows = this.db.prepare(sql).all(...params) as { id: string; score: number }[];\n return rows.map((r) => ({ id: r.id, score: r.score }));\n }\n\n /** Fetch capture entries for a list of ranked results. */\n private async fetchEntries(\n results: RankedResult[],\n limit: number,\n offset: number,\n temporalIntent: boolean = false,\n ): Promise<SearchResult[]> {\n const paged = results.slice(offset, offset + limit);\n return this.fetchEntriesById(paged, temporalIntent);\n }\n\n /** Fetch capture entries by ID, preserving the order of the input list. Applies memory decay and trust-state ranking. */\n private async fetchEntriesById(\n results: { id: string; score: number }[],\n temporalIntent: boolean = false,\n ): Promise<SearchResult[]> {\n if (results.length === 0) return [];\n const ids = results.map((r) => r.id);\n const placeholders = ids.map(() => \"?\").join(\",\");\n const rows = this.db\n .prepare(`SELECT * FROM captures WHERE id IN (${placeholders})`)\n .all(...ids) as DbRow[];\n const rowMap = new Map(rows.map((r) => [r.id, r]));\n const now = Date.now();\n const HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n // Trust-state multipliers: verified > candidate > stale\n const TRUST_BOOST: Record<string, number> = {\n verified: 1.5,\n candidate: 1.0,\n stale: 0.1,\n rejected: 0,\n };\n // For temporal-intent queries, use a much stronger recency boost (0.5 vs 0.3).\n // This ensures \"what database do we currently use\" returns the most recent fact,\n // even when an older fact has a better BM25 keyword match.\n // Non-temporal queries still get a moderate recency boost (0.3) to help\n // recently-stored seeds rank above older distractors at scale (Layer 3).\n const recencyWeight = temporalIntent ? 0.5 : 0.3;\n return results\n .map((r) => {\n const row = rowMap.get(r.id);\n if (!row) return null;\n const ageMs = now - row.created_at;\n const decay = 0.5 ** (ageMs / HALF_LIFE_MS);\n const trustBoost = TRUST_BOOST[row.trust_state ?? \"candidate\"] ?? 1.0;\n const decayed = Number.isNaN(r.score) ? 0 : r.score * decay;\n // BM25 scores are negative (lower = better). For negative scores, divide by boost\n // so a lower boost makes the score more negative (ranks lower). For positive scores\n // (RRF fusion), multiply so a lower boost makes the score lower.\n const finalScore = decayed >= 0 ? decayed * trustBoost : decayed / trustBoost;\n // Recency tiebreaker: add a boost proportional to how recent the memory is.\n // For temporal-intent queries (\"currently\", \"now\", \"latest\"), use a much faster\n // decay (1s vs 1min) so even memories stored milliseconds apart are differentiated,\n // and a stronger weight (0.5 vs 0.05) so recency dominates keyword match score.\n const recencyDecayMs = temporalIntent ? 1000 : 10000;\n const recencyBias = 1 / (1 + ageMs / recencyDecayMs);\n const biasedScore = finalScore >= 0\n ? finalScore * (1 + recencyBias * recencyWeight)\n : finalScore / (1 + recencyBias * recencyWeight);\n return { entry: rowToEntry(row), score: biasedScore };\n })\n .filter((r): r is SearchResult => r !== null)\n // For temporal-intent queries (\"currently\", \"now\", \"latest\"), filter out stale\n // memories entirely. This ensures outdated facts don't appear in results at all,\n // which is required when benchmarks check for unexpected keywords like old values.\n // Non-temporal queries (\"before\", \"previous\") still include stale memories.\n .filter((r) => !temporalIntent || r.entry.trustState !== \"stale\")\n // For temporal-intent queries, sort by created_at DESC first (most recent wins),\n // then by score. This ensures \"what database do we currently use\" returns the\n // latest fact even when an older fact has a better keyword match.\n .sort((a, b) => {\n if (temporalIntent) {\n const timeDiff = b.entry.createdAt - a.entry.createdAt;\n if (Math.abs(timeDiff) > 50) return timeDiff; // >50ms difference → recency wins\n }\n return b.score - a.score;\n });\n }\n\n /** Escape a query string for FTS5 MATCH.\n * Uses OR semantics with stopword removal for high recall.\n * AND semantics (all tokens must match) is too strict for natural language\n * questions — a 15-word query rarely has every token in a single capture,\n * causing BM25 to return zero results. OR semantics lets BM25 rank by\n * relevance (documents matching more terms rank higher) while still\n * returning partial matches. RRF fusion with weighted vector search\n * filters out BM25 noise.\n */\n private escapeFtsQuery(query: string): string {\n const FTS_STOPWORDS = new Set([\n \"the\", \"a\", \"an\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\",\n \"have\", \"has\", \"had\", \"do\", \"does\", \"did\", \"will\", \"would\", \"could\",\n \"should\", \"may\", \"might\", \"must\", \"can\", \"shall\",\n \"i\", \"you\", \"he\", \"she\", \"it\", \"we\", \"they\", \"me\", \"him\", \"her\", \"us\", \"them\",\n \"my\", \"your\", \"his\", \"its\", \"our\", \"their\",\n \"this\", \"that\", \"these\", \"those\",\n \"and\", \"or\", \"but\", \"not\", \"no\", \"nor\", \"so\", \"yet\",\n \"in\", \"on\", \"at\", \"to\", \"for\", \"of\", \"with\", \"by\", \"from\", \"as\",\n \"about\", \"into\", \"through\", \"during\", \"before\", \"after\",\n \"what\", \"when\", \"where\", \"which\", \"who\", \"how\", \"why\",\n \"since\", \"because\", \"if\", \"then\", \"than\",\n ]);\n const tokens = query.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return \"\";\n // Remove stopwords to reduce noise, but keep all tokens if none remain\n // (e.g., a query that is entirely stopwords should still search).\n const filtered = tokens.filter((t) => !FTS_STOPWORDS.has(t.toLowerCase()));\n const finalTokens = filtered.length > 0 ? filtered : tokens;\n return finalTokens.map((t) => `\"${t.replace(/\"/g, '\"\"')}\"`).join(\" OR \");\n }\n\n async delete(id: string): Promise<DeleteResult> {\n // Soft delete: set deleted_at instead of hard delete\n const now = Date.now();\n const captureCount = this.db\n .prepare(\"UPDATE captures SET deleted_at = ? WHERE id = ? AND deleted_at IS NULL\")\n .run(now, id).changes;\n\n if (captureCount > 0) {\n // Remove from search indexes (FTS + vector) so tombstoned captures are not retrievable\n this.db.prepare(\"DELETE FROM captures_vec WHERE id = ?\").run(id);\n // FTS5 external content: use the 'delete' command to remove from index\n const rowid = this.db.prepare(\"SELECT rowid FROM captures WHERE id = ?\").get(id) as\n | { rowid: number }\n | undefined;\n if (rowid) {\n this.db\n .prepare(\n \"INSERT INTO captures_fts(captures_fts, rowid, content, tags, type) VALUES('delete', ?, '', '', '')\",\n )\n .run(rowid.rowid);\n }\n }\n\n return {\n captures: captureCount,\n atoms: 0,\n scenarios: 0,\n };\n }\n\n async deleteByFilter(filter: DeleteFilter): Promise<DeleteResult> {\n let sql = \"SELECT id FROM captures WHERE deleted_at IS NULL\";\n const params: unknown[] = [];\n\n if (filter.type) {\n sql += \" AND type = ?\";\n params.push(filter.type);\n }\n if (filter.dateBefore) {\n sql += \" AND created_at < ?\";\n params.push(new Date(filter.dateBefore).getTime());\n }\n if (filter.teamId) {\n sql += \" AND team_id = ?\";\n params.push(filter.teamId);\n }\n if (filter.userId) {\n sql += \" AND user_id = ?\";\n params.push(filter.userId);\n }\n if (filter.taskId) {\n sql += \" AND task_id = ?\";\n params.push(filter.taskId);\n }\n if (filter.tags && filter.tags.length > 0) {\n const tagConditions = filter.tags.map(() => \"tags LIKE ?\").join(\" OR \");\n sql += ` AND (${tagConditions})`;\n params.push(...filter.tags.map((t) => `%\"${t}\"%`));\n }\n\n const ids = this.db.prepare(sql).all(...params) as { id: string }[];\n\n let captures = 0;\n let atoms = 0;\n let scenarios = 0;\n for (const { id } of ids) {\n const result = await this.delete(id);\n captures += result.captures;\n atoms += result.atoms;\n scenarios += result.scenarios;\n }\n\n return { captures, atoms, scenarios };\n }\n\n async reject(id: string, reason: string): Promise<DeleteResult> {\n const now = Date.now();\n const captureCount = this.db\n .prepare(\n \"UPDATE captures SET trust_state = 'rejected', rejection_reason = ?, deleted_at = ? WHERE id = ? AND deleted_at IS NULL AND trust_state != 'rejected'\",\n )\n .run(reason, now, id).changes;\n\n if (captureCount > 0) {\n // Remove from search indexes so rejected captures are not retrievable\n this.db.prepare(\"DELETE FROM captures_vec WHERE id = ?\").run(id);\n const rowid = this.db.prepare(\"SELECT rowid FROM captures WHERE id = ?\").get(id) as\n | { rowid: number }\n | undefined;\n if (rowid) {\n this.db\n .prepare(\n \"INSERT INTO captures_fts(captures_fts, rowid, content, tags, type) VALUES('delete', ?, '', '', '')\",\n )\n .run(rowid.rowid);\n }\n }\n\n return { captures: captureCount, atoms: 0, scenarios: 0 };\n }\n\n async findConflicts(\n embedding: number[],\n sessionKey: string,\n threshold: number,\n ): Promise<ConflictResult[]> {\n const buffer = new Float32Array(embedding);\n const rows = this.db\n .prepare(\n `SELECT vec.id as id, vec.distance as distance, c.content as content, c.trust_state as trust_state\n FROM captures_vec vec\n JOIN captures c ON c.id = vec.id\n WHERE vec.embedding MATCH ? AND vec.k = 20\n AND c.deleted_at IS NULL\n AND c.trust_state IN ('candidate', 'verified')\n AND c.session_key = ?\n ORDER BY vec.distance\n LIMIT 10`,\n )\n .all(Buffer.from(buffer.buffer), sessionKey) as {\n id: string;\n distance: number;\n content: string;\n trust_state: string;\n }[];\n\n return rows\n .filter((r) => {\n // sqlite-vec returns L2 (euclidean) distance for float[] columns.\n // Convert to cosine distance: cosine_dist = L2^2 / 2 (for normalized vectors).\n // Filter by cosine distance threshold.\n const cosineDist = (r.distance * r.distance) / 2;\n return cosineDist < threshold;\n })\n .map((r) => ({\n id: r.id,\n content: r.content,\n // Return cosine distance (not L2) so the caller gets a meaningful value.\n distance: (r.distance * r.distance) / 2,\n trustState: r.trust_state as TrustState,\n }));\n }\n\n async supersede(loserId: string, winnerId: string): Promise<ResolveResult> {\n const updated = this.db\n .prepare(\n \"UPDATE captures SET trust_state = 'stale', superseded_by = ? WHERE id = ? AND deleted_at IS NULL AND trust_state != 'rejected'\",\n )\n .run(winnerId, loserId).changes;\n return { winnerId, loserId, updated };\n }\n\n async setTrustState(id: string, state: TrustState): Promise<number> {\n return this.db\n .prepare(\"UPDATE captures SET trust_state = ? WHERE id = ? AND deleted_at IS NULL\")\n .run(state, id).changes;\n }\n\n // ─── L1 atoms ───────────────────────────────────────────────\n\n async putAtom(atom: AtomEntry): Promise<void> {\n this.db\n .prepare(\n \"INSERT INTO atoms (id, capture_id, fact, confidence, created_at, team_id, agent_id, user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\",\n )\n .run(\n atom.id,\n atom.captureId,\n atom.fact,\n atom.confidence,\n atom.createdAt,\n atom.teamId ?? null,\n atom.agentId ?? null,\n atom.userId ?? null,\n );\n }\n\n async listAtoms(opts: {\n teamId?: string;\n agentId?: string;\n userId?: string;\n captureId?: string;\n limit?: number;\n offset?: number;\n }): Promise<AtomEntry[]> {\n let sql = \"SELECT * FROM atoms WHERE 1=1\";\n const params: unknown[] = [];\n if (opts.teamId) {\n sql += \" AND team_id = ?\";\n params.push(opts.teamId);\n }\n if (opts.agentId) {\n sql += \" AND agent_id = ?\";\n params.push(opts.agentId);\n }\n if (opts.userId) {\n sql += \" AND user_id = ?\";\n params.push(opts.userId);\n }\n if (opts.captureId) {\n sql += \" AND capture_id = ?\";\n params.push(opts.captureId);\n }\n sql += \" ORDER BY created_at DESC LIMIT ? OFFSET ?\";\n params.push(opts.limit ?? 20, opts.offset ?? 0);\n const rows = this.db.prepare(sql).all(...params) as AtomDbRow[];\n return rows.map((r) => ({\n id: r.id,\n captureId: r.capture_id,\n fact: r.fact,\n confidence: r.confidence,\n createdAt: r.created_at,\n teamId: r.team_id ?? undefined,\n agentId: r.agent_id ?? undefined,\n userId: r.user_id ?? undefined,\n }));\n }\n\n async searchAtoms(\n query: string,\n opts: { teamId?: string; agentId?: string; userId?: string; limit?: number } = {},\n ): Promise<AtomEntry[]> {\n // Atoms don't have FTS — use LIKE for keyword search\n let sql = \"SELECT * FROM atoms WHERE fact LIKE ?\";\n const params: unknown[] = [`%${query}%`];\n if (opts.teamId) {\n sql += \" AND team_id = ?\";\n params.push(opts.teamId);\n }\n if (opts.agentId) {\n sql += \" AND agent_id = ?\";\n params.push(opts.agentId);\n }\n if (opts.userId) {\n sql += \" AND user_id = ?\";\n params.push(opts.userId);\n }\n sql += \" ORDER BY created_at DESC LIMIT ?\";\n params.push(opts.limit ?? 20);\n const rows = this.db.prepare(sql).all(...params) as AtomDbRow[];\n return rows.map((r) => ({\n id: r.id,\n captureId: r.capture_id,\n fact: r.fact,\n confidence: r.confidence,\n createdAt: r.created_at,\n teamId: r.team_id ?? undefined,\n agentId: r.agent_id ?? undefined,\n userId: r.user_id ?? undefined,\n }));\n }\n\n // ─── L2 scenarios ───────────────────────────────────────────\n\n async putScenario(scenario: ScenarioEntry): Promise<void> {\n this.db\n .prepare(\n \"INSERT INTO scenarios (id, atom_ids, summary, persona_tags, created_at, team_id, agent_id, user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\",\n )\n .run(\n scenario.id,\n JSON.stringify(scenario.atomIds),\n scenario.summary,\n scenario.personaTags ? JSON.stringify(scenario.personaTags) : null,\n scenario.createdAt,\n scenario.teamId ?? null,\n scenario.agentId ?? null,\n scenario.userId ?? null,\n );\n }\n\n async listScenarios(opts: {\n teamId?: string;\n agentId?: string;\n userId?: string;\n limit?: number;\n offset?: number;\n }): Promise<ScenarioEntry[]> {\n let sql = \"SELECT * FROM scenarios WHERE 1=1\";\n const params: unknown[] = [];\n if (opts.teamId) {\n sql += \" AND team_id = ?\";\n params.push(opts.teamId);\n }\n if (opts.agentId) {\n sql += \" AND agent_id = ?\";\n params.push(opts.agentId);\n }\n if (opts.userId) {\n sql += \" AND user_id = ?\";\n params.push(opts.userId);\n }\n sql += \" ORDER BY created_at DESC LIMIT ? OFFSET ?\";\n params.push(opts.limit ?? 20, opts.offset ?? 0);\n const rows = this.db.prepare(sql).all(...params) as ScenarioDbRow[];\n return rows.map((r) => ({\n id: r.id,\n atomIds: JSON.parse(r.atom_ids) as string[],\n summary: r.summary,\n personaTags: r.persona_tags ? (JSON.parse(r.persona_tags) as string[]) : undefined,\n createdAt: r.created_at,\n teamId: r.team_id ?? undefined,\n agentId: r.agent_id ?? undefined,\n userId: r.user_id ?? undefined,\n }));\n }\n\n async getScenario(id: string): Promise<ScenarioEntry | null> {\n const row = this.db.prepare(\"SELECT * FROM scenarios WHERE id = ?\").get(id) as\n | ScenarioDbRow\n | undefined;\n if (!row) return null;\n return {\n id: row.id,\n atomIds: JSON.parse(row.atom_ids) as string[],\n summary: row.summary,\n personaTags: row.persona_tags ? (JSON.parse(row.persona_tags) as string[]) : undefined,\n createdAt: row.created_at,\n teamId: row.team_id ?? undefined,\n agentId: row.agent_id ?? undefined,\n userId: row.user_id ?? undefined,\n };\n }\n\n // ─── L3 persona ─────────────────────────────────────────────\n\n async readPersona(teamId: string, agentId: string, userId: string): Promise<PersonaEntry | null> {\n const row = this.db\n .prepare(\"SELECT * FROM persona WHERE team_id = ? AND agent_id = ? AND user_id = ?\")\n .get(teamId, agentId, userId) as PersonaDbRow | undefined;\n if (!row) return null;\n return {\n teamId: row.team_id,\n agentId: row.agent_id,\n userId: row.user_id,\n content: row.content,\n updatedAt: row.updated_at,\n };\n }\n\n async writePersona(\n teamId: string,\n agentId: string,\n userId: string,\n content: string,\n ): Promise<void> {\n this.db\n .prepare(\n `INSERT INTO persona (team_id, agent_id, user_id, content, updated_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(team_id, agent_id, user_id) DO UPDATE SET content = excluded.content, updated_at = excluded.updated_at`,\n )\n .run(teamId, agentId, userId, content, Date.now());\n }\n\n // ─── Knowledge ──────────────────────────────────────────────\n\n async putKnowledge(entry: KnowledgeEntry): Promise<void> {\n this.db\n .prepare(\n \"INSERT INTO knowledge (id, team_id, name, type, summary, service_url, repo_url, branch, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n )\n .run(\n entry.id,\n entry.teamId,\n entry.name,\n entry.type,\n entry.summary ?? null,\n entry.serviceUrl ?? null,\n entry.repoUrl ?? null,\n entry.branch ?? null,\n entry.createdAt,\n );\n }\n\n async getKnowledge(id: string): Promise<KnowledgeEntry | null> {\n const row = this.db.prepare(\"SELECT * FROM knowledge WHERE id = ?\").get(id) as\n | KnowledgeDbRow\n | undefined;\n if (!row) return null;\n return knowledgeRowToEntry(row);\n }\n\n async listKnowledge(teamId: string, type?: string): Promise<KnowledgeEntry[]> {\n let sql = \"SELECT * FROM knowledge WHERE team_id = ?\";\n const params: unknown[] = [teamId];\n if (type) {\n sql += \" AND type = ?\";\n params.push(type);\n }\n sql += \" ORDER BY created_at DESC\";\n const rows = this.db.prepare(sql).all(...params) as KnowledgeDbRow[];\n return rows.map(knowledgeRowToEntry);\n }\n\n async deleteKnowledge(ids: string[]): Promise<number> {\n if (ids.length === 0) return 0;\n const placeholders = ids.map(() => \"?\").join(\",\");\n const result = this.db\n .prepare(`DELETE FROM knowledge WHERE id IN (${placeholders})`)\n .run(...ids);\n return result.changes;\n }\n\n // ─── Skills ─────────────────────────────────────────────────\n\n async putSkill(entry: SkillEntry): Promise<void> {\n this.db\n .prepare(\n `INSERT INTO skills (id, team_id, agent_id, name, description, content, version, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET name = excluded.name, description = excluded.description, content = excluded.content, version = excluded.version, updated_at = excluded.updated_at`,\n )\n .run(\n entry.id,\n entry.teamId,\n entry.agentId ?? null,\n entry.name,\n entry.description ?? null,\n entry.content ?? null,\n entry.version,\n entry.createdAt,\n entry.updatedAt,\n );\n }\n\n async getSkill(id: string): Promise<SkillEntry | null> {\n const row = this.db.prepare(\"SELECT * FROM skills WHERE id = ?\").get(id) as\n | SkillDbRow\n | undefined;\n if (!row) return null;\n return skillRowToEntry(row);\n }\n\n async listSkills(teamId: string, agentId?: string): Promise<SkillEntry[]> {\n let sql = \"SELECT * FROM skills WHERE team_id = ?\";\n const params: unknown[] = [teamId];\n if (agentId) {\n sql += \" AND (agent_id = ? OR agent_id IS NULL)\";\n params.push(agentId);\n }\n sql += \" ORDER BY updated_at DESC\";\n const rows = this.db.prepare(sql).all(...params) as SkillDbRow[];\n return rows.map(skillRowToEntry);\n }\n\n async searchSkills(\n teamId: string,\n agentId: string,\n query: string,\n topK?: number,\n ): Promise<SkillEntry[]> {\n let sql =\n \"SELECT * FROM skills WHERE team_id = ? AND (agent_id = ? OR agent_id IS NULL) AND (name LIKE ? OR description LIKE ?)\";\n const params: unknown[] = [teamId, agentId, `%${query}%`, `%${query}%`];\n sql += \" ORDER BY updated_at DESC LIMIT ?\";\n params.push(topK ?? 10);\n const rows = this.db.prepare(sql).all(...params) as SkillDbRow[];\n return rows.map(skillRowToEntry);\n }\n\n close(): void {\n this.db.close();\n }\n}\n\n// ─── Database row types ────────────────────────────────────────\n\ninterface DbRow {\n id: string;\n session_key: string;\n agent_id: string;\n type: string;\n content: string;\n content_hash: string | null;\n tags: string | null;\n created_at: number;\n metadata: string | null;\n team_id: string | null;\n user_id: string | null;\n task_id: string | null;\n deleted_at: number | null;\n trust_state: string | null;\n rejection_reason: string | null;\n superseded_by: string | null;\n}\n\ninterface MessageDbRow {\n id: string;\n capture_id: string;\n role: string;\n content: string;\n seq: number;\n created_at: number;\n}\n\ninterface AtomDbRow {\n id: string;\n capture_id: string;\n fact: string;\n confidence: number;\n created_at: number;\n team_id: string | null;\n agent_id: string | null;\n user_id: string | null;\n}\n\ninterface ScenarioDbRow {\n id: string;\n atom_ids: string;\n summary: string;\n persona_tags: string | null;\n created_at: number;\n team_id: string | null;\n agent_id: string | null;\n user_id: string | null;\n}\n\ninterface PersonaDbRow {\n team_id: string;\n agent_id: string;\n user_id: string;\n content: string;\n updated_at: number;\n}\n\ninterface KnowledgeDbRow {\n id: string;\n team_id: string;\n name: string;\n type: string;\n summary: string | null;\n service_url: string | null;\n repo_url: string | null;\n branch: string | null;\n created_at: number;\n}\n\ninterface SkillDbRow {\n id: string;\n team_id: string;\n agent_id: string | null;\n name: string;\n description: string | null;\n content: string | null;\n version: number;\n created_at: number;\n updated_at: number;\n}\n\n// ─── Row → Entry converters ────────────────────────────────────\n\n/** Convert a database row to a CaptureEntry. */\nfunction rowToEntry(row: DbRow): CaptureEntry {\n return {\n id: row.id,\n sessionKey: row.session_key,\n agentId: row.agent_id,\n type: row.type as CaptureEntry[\"type\"],\n content: row.content,\n tags: row.tags ? JSON.parse(row.tags) : [],\n createdAt: row.created_at,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n teamId: row.team_id ?? undefined,\n userId: row.user_id ?? undefined,\n taskId: row.task_id ?? undefined,\n trustState: (row.trust_state as TrustState) ?? \"candidate\",\n rejectionReason: row.rejection_reason ?? undefined,\n supersededBy: row.superseded_by ?? undefined,\n };\n}\n\nfunction knowledgeRowToEntry(row: KnowledgeDbRow): KnowledgeEntry {\n return {\n id: row.id,\n teamId: row.team_id,\n name: row.name,\n type: row.type,\n summary: row.summary ?? undefined,\n serviceUrl: row.service_url ?? undefined,\n repoUrl: row.repo_url ?? undefined,\n branch: row.branch ?? undefined,\n createdAt: row.created_at,\n };\n}\n\nfunction skillRowToEntry(row: SkillDbRow): SkillEntry {\n return {\n id: row.id,\n teamId: row.team_id,\n agentId: row.agent_id ?? undefined,\n name: row.name,\n description: row.description ?? undefined,\n content: row.content ?? undefined,\n version: row.version,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n","/**\n * Reciprocal Rank Fusion (RRF) merge with weighted channels.\n * Fuses two ranked result lists into one.\n *\n * Vector search is weighted 1.5x over BM25 because vector similarity\n * is more semantically relevant for conceptual queries, while BM25\n * with OR semantics can introduce noise at scale (1K+ memories).\n *\n * Adapted from TencentDB Agent Memory search-utils pattern (MIT, Tencent 2026).\n * https://github.com/TencentCloud/TencentDB-Agent-Memory\n */\n\n// k=40 works better than the standard k=60 for smaller result sets (<500 items)\n// typical in memory servers. Lower k gives more weight to top-ranked items.\nconst RRF_K = 40;\n\n// Vector search weight: 2x over BM25. Vector similarity is more precise\n// for semantic matching, while BM25 OR semantics can be noisy at scale.\nconst VEC_WEIGHT = 2.0;\n\nexport interface RankedResult {\n id: string;\n score: number;\n}\n\nexport interface FusedResult {\n id: string;\n score: number;\n}\n\n/**\n * Merge two ranked result lists with weighted RRF.\n * @param bm25Results Results from BM25 (FTS5). Lower score is better.\n * @param vecResults Results from vector search. Lower distance is better.\n * @param limit Maximum number of results to return.\n * @returns Fused results, sorted by RRF score (highest first).\n */\nexport function rrfMerge(\n bm25Results: RankedResult[],\n vecResults: RankedResult[],\n limit: number,\n): FusedResult[] {\n const scores = new Map<string, number>();\n\n // BM25 results: lower score is better, so rank 1 = lowest score\n const sortedBm25 = [...bm25Results].sort((a, b) => a.score - b.score);\n sortedBm25.forEach((r, i) => {\n const rank = i + 1;\n const rrfScore = 1 / (RRF_K + rank);\n scores.set(r.id, (scores.get(r.id) ?? 0) + rrfScore);\n });\n\n // Vector results: lower distance is better, so rank 1 = lowest distance\n // Weighted higher because vector similarity is more semantically precise.\n const sortedVec = [...vecResults].sort((a, b) => a.score - b.score);\n sortedVec.forEach((r, i) => {\n const rank = i + 1;\n const rrfScore = (1 / (RRF_K + rank)) * VEC_WEIGHT;\n scores.set(r.id, (scores.get(r.id) ?? 0) + rrfScore);\n });\n\n return [...scores.entries()]\n .map(([id, score]) => ({ id, score }))\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n}\n","/**\n * Wiki engine: parse markdown docs into structured pages with a link graph.\n *\n * Extracts frontmatter, headings, [[wikilinks]], and [text](url) links.\n * Builds an adjacency list for the link graph.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { basename, extname, join, relative, sep } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport { generateId } from \"../utils/ulid.js\";\n\n/** A parsed wiki page. */\nexport interface WikiPage {\n id: string;\n title: string;\n content: string;\n sourceFile: string;\n section: string | null;\n tags: string | null;\n frontmatter: string | null;\n contentHash: string;\n}\n\n/** A link between wiki pages. */\nexport interface WikiLink {\n fromPageId: string;\n toPageId: string | null;\n toTitle: string;\n linkText: string;\n linkType: string;\n line: number;\n}\n\n/** Result of ingesting a file. */\nexport interface IngestResult {\n file: string;\n pages: number;\n links: number;\n skipped: boolean;\n reason?: string;\n}\n\n/** Parse frontmatter (YAML-like) from markdown content. */\nfunction parseFrontmatter(content: string): { frontmatter: string | null; body: string } {\n if (!content.startsWith(\"---\")) {\n return { frontmatter: null, body: content };\n }\n const end = content.indexOf(\"\\n---\", 3);\n if (end === -1) {\n return { frontmatter: null, body: content };\n }\n const frontmatter = content.slice(3, end).trim();\n const body = content.slice(end + 4).replace(/^\\n/, \"\");\n return { frontmatter, body };\n}\n\n/** Extract tags from frontmatter (simple key: value parsing). */\nfunction extractTags(frontmatter: string | null): string | null {\n if (!frontmatter) return null;\n const match = frontmatter.match(/^tags:\\s*(.+)$/m);\n if (!match) return null;\n return match[1].replace(/[[\\]]/g, \"\").trim();\n}\n\n/** Extract title from frontmatter or first heading. */\nfunction extractTitle(frontmatter: string | null, body: string, fileName: string): string {\n if (frontmatter) {\n const titleMatch = frontmatter.match(/^title:\\s*(.+)$/m);\n if (titleMatch) return titleMatch[1].replace(/[\"']/g, \"\").trim();\n }\n // First H1 heading\n const h1Match = body.match(/^#\\s+(.+)$/m);\n if (h1Match) return h1Match[1].trim();\n // Fallback: file name without extension\n return basename(fileName, extname(fileName));\n}\n\n/** Extract [[wikilinks]] from content. Returns array of { target, text, line }. */\nfunction extractWikilinks(content: string): Array<{ target: string; text: string; line: number }> {\n const links: Array<{ target: string; text: string; line: number }> = [];\n const lines = content.split(\"\\n\");\n const regex = /\\[\\[([^\\]|]+)(?:\\|([^\\]]+))?\\]\\]/g;\n for (let i = 0; i < lines.length; i++) {\n let match: RegExpExecArray | null;\n regex.lastIndex = 0;\n match = regex.exec(lines[i]);\n while (match !== null) {\n links.push({\n target: match[1].trim(),\n text: (match[2] ?? match[1]).trim(),\n line: i + 1,\n });\n match = regex.exec(lines[i]);\n }\n }\n return links;\n}\n\n/** Extract [text](url) markdown links from content. */\nfunction extractMarkdownLinks(\n content: string,\n): Array<{ target: string; text: string; line: number }> {\n const links: Array<{ target: string; text: string; line: number }> = [];\n const lines = content.split(\"\\n\");\n // Match [text](path) but not  (images)\n const regex = /(?<!!)\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n for (let i = 0; i < lines.length; i++) {\n let match: RegExpExecArray | null;\n regex.lastIndex = 0;\n match = regex.exec(lines[i]);\n while (match !== null) {\n const target = match[2].trim();\n // Only include local .md links (not http URLs)\n if (!target.startsWith(\"http\") && !target.startsWith(\"mailto:\")) {\n links.push({\n target: target.replace(/\\.md$/i, \"\"),\n text: match[1].trim(),\n line: i + 1,\n });\n }\n match = regex.exec(lines[i]);\n }\n }\n return links;\n}\n\n/** Parse a markdown file into a wiki page with links. */\nfunction parseMarkdownFile(\n filePath: string,\n repoPath: string,\n): {\n page: WikiPage;\n links: Array<{ target: string; text: string; line: number; type: string }>;\n} | null {\n const ext = extname(filePath).toLowerCase();\n if (ext !== \".md\" && ext !== \".markdown\") return null;\n\n let source: string;\n try {\n source = readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n const relPath = relative(repoPath, filePath).split(sep).join(\"/\");\n const { frontmatter, body } = parseFrontmatter(source);\n const title = extractTitle(frontmatter, body, filePath);\n const tags = extractTags(frontmatter);\n const contentHash = createHash(\"sha256\").update(source).digest(\"hex\");\n\n const page: WikiPage = {\n id: generateId(),\n title,\n content: body,\n sourceFile: relPath,\n section: null,\n tags,\n frontmatter,\n contentHash,\n };\n\n const wikilinks = extractWikilinks(body).map((l) => ({ ...l, type: \"wikilink\" }));\n const mdlinks = extractMarkdownLinks(body).map((l) => ({ ...l, type: \"markdown\" }));\n const links = [...wikilinks, ...mdlinks];\n\n return { page, links };\n}\n\n/** Ingest a single markdown file into the database. */\nexport function ingestFile(\n db: Database,\n filePath: string,\n repoPath: string,\n teamId: string | null,\n): IngestResult {\n const ext = extname(filePath).toLowerCase();\n if (ext !== \".md\" && ext !== \".markdown\") {\n return { file: filePath, pages: 0, links: 0, skipped: true, reason: \"not markdown\" };\n }\n\n const parsed = parseMarkdownFile(filePath, repoPath);\n if (!parsed) {\n return { file: filePath, pages: 0, links: 0, skipped: true, reason: \"parse failed\" };\n }\n\n const relPath = relative(repoPath, filePath).split(sep).join(\"/\");\n const now = Date.now();\n\n // Check if page already exists (by source_file)\n const existing = db\n .prepare(\"SELECT id FROM wiki_pages WHERE source_file = ? AND team_id IS ?\")\n .get(relPath, teamId) as { id: string } | undefined;\n\n let pageId = parsed.page.id;\n if (existing) {\n // Update existing page\n pageId = existing.id;\n db.prepare(\n \"UPDATE wiki_pages SET title = ?, content = ?, tags = ?, frontmatter = ?, content_hash = ?, updated_at = ? WHERE id = ?\",\n ).run(\n parsed.page.title,\n parsed.page.content,\n parsed.page.tags,\n parsed.page.frontmatter,\n parsed.page.contentHash,\n now,\n pageId,\n );\n // Delete old links\n db.prepare(\"DELETE FROM wiki_links WHERE from_page_id = ?\").run(pageId);\n } else {\n // Insert new page\n db.prepare(\n `INSERT INTO wiki_pages (id, title, content, source_file, section, tags, frontmatter, content_hash, team_id, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n parsed.page.id,\n parsed.page.title,\n parsed.page.content,\n parsed.page.sourceFile,\n parsed.page.section,\n parsed.page.tags,\n parsed.page.frontmatter,\n parsed.page.contentHash,\n teamId,\n now,\n now,\n );\n }\n\n // Insert links (resolve targets later)\n const linkStmt = db.prepare(\n \"INSERT INTO wiki_links (from_page_id, to_page_id, to_title, link_text, link_type, line) VALUES (?, ?, ?, ?, ?, ?)\",\n );\n for (const link of parsed.links) {\n // Try to resolve target by title\n const target = db\n .prepare(\"SELECT id FROM wiki_pages WHERE title = ? AND team_id IS ? LIMIT 1\")\n .get(link.target, teamId) as { id: string } | undefined;\n linkStmt.run(pageId, target?.id ?? null, link.target, link.text, link.type, link.line);\n }\n\n return {\n file: relPath,\n pages: 1,\n links: parsed.links.length,\n skipped: false,\n };\n}\n\n/** Ingest a directory of markdown files. */\nexport function ingestDirectory(\n db: Database,\n dirPath: string,\n repoPath: string,\n teamId: string | null,\n maxFiles = 200,\n): IngestResult[] {\n const results: IngestResult[] = [];\n const files: string[] = [];\n\n const walk = (dir: string) => {\n if (files.length >= maxFiles) return;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n if (files.length >= maxFiles) return;\n const fullPath = join(dir, entry);\n let stat: ReturnType<typeof statSync>;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n if (entry.startsWith(\".\") || entry === \"node_modules\" || entry === \"vendor\") continue;\n walk(fullPath);\n } else if (stat.isFile()) {\n const ext = extname(fullPath).toLowerCase();\n if (ext === \".md\" || ext === \".markdown\") {\n files.push(fullPath);\n }\n }\n }\n };\n\n walk(dirPath);\n\n for (const file of files) {\n const result = ingestFile(db, file, repoPath, teamId);\n results.push(result);\n }\n\n // Re-resolve links now that all pages are indexed\n const unresolved = db\n .prepare(\"SELECT id, to_title FROM wiki_links WHERE to_page_id IS NULL\")\n .all() as { id: number; to_title: string }[];\n if (unresolved.length > 0) {\n const updateStmt = db.prepare(\"UPDATE wiki_links SET to_page_id = ? WHERE id = ?\");\n for (const u of unresolved) {\n const target = db\n .prepare(\"SELECT id FROM wiki_pages WHERE title = ? AND team_id IS ? LIMIT 1\")\n .get(u.to_title, teamId) as { id: string } | undefined;\n if (target) {\n updateStmt.run(target.id, u.id);\n }\n }\n }\n\n return results;\n}\n\n/** Search wiki pages using FTS5. */\nexport function searchWiki(\n db: Database,\n query: string,\n opts: { teamId?: string; limit?: number } = {},\n): Array<{ id: string; title: string; sourceFile: string; snippet: string }> {\n const limit = opts.limit ?? 10;\n let sql = `\n SELECT w.id, w.title, w.source_file, snippet(wiki_fts, 1, '<b>', '</b>', '...', 20) as snippet\n FROM wiki_fts f\n JOIN wiki_pages w ON w.id = f.id\n WHERE wiki_fts MATCH ?\n `;\n const params: unknown[] = [query];\n if (opts.teamId !== undefined) {\n sql += \" AND w.team_id IS ?\";\n params.push(opts.teamId);\n }\n sql += \" LIMIT ?\";\n params.push(limit);\n\n const rows = db.prepare(sql).all(...params) as Array<{\n id: string;\n title: string;\n source_file: string;\n snippet: string;\n }>;\n\n return rows.map((r) => ({\n id: r.id,\n title: r.title,\n sourceFile: r.source_file,\n snippet: r.snippet,\n }));\n}\n\n/** Get a wiki page by ID, including linked pages. */\nexport function getWikiPage(\n db: Database,\n pageId: string,\n): { page: WikiPage; links: WikiLink[]; backlinks: WikiLink[] } | null {\n const row = db.prepare(\"SELECT * FROM wiki_pages WHERE id = ?\").get(pageId) as\n | {\n id: string;\n title: string;\n content: string;\n source_file: string;\n section: string | null;\n tags: string | null;\n frontmatter: string | null;\n content_hash: string;\n }\n | undefined;\n\n if (!row) return null;\n\n const page: WikiPage = {\n id: row.id,\n title: row.title,\n content: row.content,\n sourceFile: row.source_file,\n section: row.section,\n tags: row.tags,\n frontmatter: row.frontmatter,\n contentHash: row.content_hash,\n };\n\n const linkRows = db\n .prepare(\"SELECT * FROM wiki_links WHERE from_page_id = ? ORDER BY line\")\n .all(pageId) as Array<{\n from_page_id: string;\n to_page_id: string | null;\n to_title: string;\n link_text: string;\n link_type: string;\n line: number;\n }>;\n\n const links: WikiLink[] = linkRows.map((r) => ({\n fromPageId: r.from_page_id,\n toPageId: r.to_page_id,\n toTitle: r.to_title,\n linkText: r.link_text,\n linkType: r.link_type,\n line: r.line,\n }));\n\n const backlinkRows = db\n .prepare(\"SELECT * FROM wiki_links WHERE to_page_id = ? ORDER BY line\")\n .all(pageId) as Array<{\n from_page_id: string;\n to_page_id: string | null;\n to_title: string;\n link_text: string;\n link_type: string;\n line: number;\n }>;\n\n const backlinks: WikiLink[] = backlinkRows.map((r) => ({\n fromPageId: r.from_page_id,\n toPageId: r.to_page_id,\n toTitle: r.to_title,\n linkText: r.link_text,\n linkType: r.link_type,\n line: r.line,\n }));\n\n return { page, links, backlinks };\n}\n\n/** Find pages whose source file has changed (content_hash mismatch). */\nexport function findOutdatedPages(\n db: Database,\n repoPath: string,\n opts: { teamId?: string } = {},\n): Array<{ id: string; title: string; sourceFile: string; reason: string }> {\n const pages = db\n .prepare(\"SELECT id, title, source_file, content_hash FROM wiki_pages WHERE team_id IS ?\")\n .all(opts.teamId ?? null) as Array<{\n id: string;\n title: string;\n source_file: string;\n content_hash: string;\n }>;\n\n const outdated: Array<{ id: string; title: string; sourceFile: string; reason: string }> = [];\n\n for (const page of pages) {\n const fullPath = join(repoPath, page.source_file);\n if (!existsSync(fullPath)) {\n outdated.push({\n id: page.id,\n title: page.title,\n sourceFile: page.source_file,\n reason: \"file deleted\",\n });\n continue;\n }\n try {\n const content = readFileSync(fullPath, \"utf-8\");\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\n if (hash !== page.content_hash) {\n outdated.push({\n id: page.id,\n title: page.title,\n sourceFile: page.source_file,\n reason: \"content changed\",\n });\n }\n } catch {\n outdated.push({\n id: page.id,\n title: page.title,\n sourceFile: page.source_file,\n reason: \"read error\",\n });\n }\n }\n\n return outdated;\n}\n"],"mappings":"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","names":["createHash","join","__dirname","createHash","existsSync","readFileSync","join","readFileSync","createHash","join","existsSync","join","createHash"]}
|