@mnemoai/core 1.1.2 → 1.1.3

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 (191) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +321 -0
  3. package/dist/cli.d.ts +0 -1
  4. package/dist/cli.js +0 -1
  5. package/dist/index.d.ts +0 -115
  6. package/dist/index.js +1 -6
  7. package/dist/src/access-tracker.d.ts +0 -1
  8. package/dist/src/access-tracker.js +0 -1
  9. package/dist/src/adapters/chroma.d.ts +0 -1
  10. package/dist/src/adapters/chroma.js +0 -1
  11. package/dist/src/adapters/lancedb.d.ts +0 -1
  12. package/dist/src/adapters/lancedb.js +0 -1
  13. package/dist/src/adapters/pgvector.d.ts +0 -1
  14. package/dist/src/adapters/pgvector.js +0 -1
  15. package/dist/src/adapters/qdrant.d.ts +0 -1
  16. package/dist/src/adapters/qdrant.js +0 -1
  17. package/dist/src/adaptive-retrieval.d.ts +0 -1
  18. package/dist/src/adaptive-retrieval.js +0 -1
  19. package/dist/src/audit-log.d.ts +0 -1
  20. package/dist/src/audit-log.js +0 -1
  21. package/dist/src/chunker.d.ts +0 -1
  22. package/dist/src/chunker.js +0 -1
  23. package/dist/src/config.d.ts +0 -1
  24. package/dist/src/config.js +0 -1
  25. package/dist/src/decay-engine.d.ts +0 -1
  26. package/dist/src/decay-engine.js +0 -1
  27. package/dist/src/embedder.d.ts +0 -1
  28. package/dist/src/embedder.js +0 -1
  29. package/dist/src/extraction-prompts.d.ts +0 -1
  30. package/dist/src/extraction-prompts.js +0 -1
  31. package/dist/src/license.d.ts +0 -1
  32. package/dist/src/license.js +2 -6
  33. package/dist/src/llm-client.d.ts +0 -1
  34. package/dist/src/llm-client.js +0 -1
  35. package/dist/src/logger.d.ts +0 -1
  36. package/dist/src/logger.js +0 -1
  37. package/dist/src/mcp-server.d.ts +0 -1
  38. package/dist/src/mcp-server.js +0 -1
  39. package/dist/src/memory-categories.d.ts +0 -1
  40. package/dist/src/memory-categories.js +0 -1
  41. package/dist/src/memory-upgrader.d.ts +0 -1
  42. package/dist/src/memory-upgrader.js +0 -1
  43. package/dist/src/migrate.d.ts +0 -1
  44. package/dist/src/migrate.js +0 -1
  45. package/dist/src/mnemo.d.ts +7 -1
  46. package/dist/src/mnemo.js +19 -7
  47. package/dist/src/noise-filter.d.ts +0 -1
  48. package/dist/src/noise-filter.js +0 -1
  49. package/dist/src/noise-prototypes.d.ts +0 -1
  50. package/dist/src/noise-prototypes.js +0 -1
  51. package/dist/src/observability.d.ts +0 -1
  52. package/dist/src/observability.js +0 -1
  53. package/dist/src/query-tracker.d.ts +0 -1
  54. package/dist/src/query-tracker.js +0 -1
  55. package/dist/src/reflection-event-store.d.ts +0 -1
  56. package/dist/src/reflection-event-store.js +0 -1
  57. package/dist/src/reflection-item-store.d.ts +0 -1
  58. package/dist/src/reflection-item-store.js +0 -1
  59. package/dist/src/reflection-mapped-metadata.d.ts +0 -1
  60. package/dist/src/reflection-mapped-metadata.js +0 -1
  61. package/dist/src/reflection-metadata.d.ts +0 -1
  62. package/dist/src/reflection-metadata.js +0 -1
  63. package/dist/src/reflection-ranking.d.ts +0 -1
  64. package/dist/src/reflection-ranking.js +0 -1
  65. package/dist/src/reflection-retry.d.ts +0 -1
  66. package/dist/src/reflection-retry.js +0 -1
  67. package/dist/src/reflection-slices.d.ts +0 -1
  68. package/dist/src/reflection-slices.js +0 -1
  69. package/dist/src/reflection-store.d.ts +0 -1
  70. package/dist/src/reflection-store.js +0 -1
  71. package/dist/src/resonance-state.d.ts +0 -1
  72. package/dist/src/resonance-state.js +0 -1
  73. package/dist/src/retriever.d.ts +0 -1
  74. package/dist/src/retriever.js +0 -1
  75. package/dist/src/scopes.d.ts +0 -1
  76. package/dist/src/scopes.js +0 -1
  77. package/dist/src/self-improvement-files.d.ts +0 -1
  78. package/dist/src/self-improvement-files.js +0 -1
  79. package/dist/src/semantic-gate.d.ts +0 -1
  80. package/dist/src/semantic-gate.js +0 -1
  81. package/dist/src/session-recovery.d.ts +0 -1
  82. package/dist/src/session-recovery.js +0 -1
  83. package/dist/src/smart-extractor.d.ts +0 -1
  84. package/dist/src/smart-extractor.js +0 -1
  85. package/dist/src/smart-metadata.d.ts +0 -1
  86. package/dist/src/smart-metadata.js +0 -1
  87. package/dist/src/storage-adapter.d.ts +0 -1
  88. package/dist/src/storage-adapter.js +0 -1
  89. package/dist/src/store.d.ts +0 -1
  90. package/dist/src/store.js +0 -1
  91. package/dist/src/tier-manager.d.ts +0 -1
  92. package/dist/src/tier-manager.js +0 -1
  93. package/dist/src/tools.d.ts +0 -1
  94. package/dist/src/tools.js +0 -1
  95. package/dist/src/wal-recovery.d.ts +0 -1
  96. package/dist/src/wal-recovery.js +0 -1
  97. package/package.json +1 -1
  98. package/dist/cli.d.ts.map +0 -1
  99. package/dist/cli.js.map +0 -7
  100. package/dist/index.d.ts.map +0 -1
  101. package/dist/index.js.map +0 -7
  102. package/dist/src/access-tracker.d.ts.map +0 -1
  103. package/dist/src/access-tracker.js.map +0 -7
  104. package/dist/src/adapters/chroma.d.ts.map +0 -1
  105. package/dist/src/adapters/chroma.js.map +0 -7
  106. package/dist/src/adapters/lancedb.d.ts.map +0 -1
  107. package/dist/src/adapters/lancedb.js.map +0 -7
  108. package/dist/src/adapters/pgvector.d.ts.map +0 -1
  109. package/dist/src/adapters/pgvector.js.map +0 -7
  110. package/dist/src/adapters/qdrant.d.ts.map +0 -1
  111. package/dist/src/adapters/qdrant.js.map +0 -7
  112. package/dist/src/adaptive-retrieval.d.ts.map +0 -1
  113. package/dist/src/adaptive-retrieval.js.map +0 -7
  114. package/dist/src/audit-log.d.ts.map +0 -1
  115. package/dist/src/audit-log.js.map +0 -7
  116. package/dist/src/chunker.d.ts.map +0 -1
  117. package/dist/src/chunker.js.map +0 -7
  118. package/dist/src/config.d.ts.map +0 -1
  119. package/dist/src/config.js.map +0 -7
  120. package/dist/src/decay-engine.d.ts.map +0 -1
  121. package/dist/src/decay-engine.js.map +0 -7
  122. package/dist/src/embedder.d.ts.map +0 -1
  123. package/dist/src/embedder.js.map +0 -7
  124. package/dist/src/extraction-prompts.d.ts.map +0 -1
  125. package/dist/src/extraction-prompts.js.map +0 -7
  126. package/dist/src/license.d.ts.map +0 -1
  127. package/dist/src/license.js.map +0 -7
  128. package/dist/src/llm-client.d.ts.map +0 -1
  129. package/dist/src/llm-client.js.map +0 -7
  130. package/dist/src/logger.d.ts.map +0 -1
  131. package/dist/src/logger.js.map +0 -7
  132. package/dist/src/mcp-server.d.ts.map +0 -1
  133. package/dist/src/mcp-server.js.map +0 -7
  134. package/dist/src/memory-categories.d.ts.map +0 -1
  135. package/dist/src/memory-categories.js.map +0 -7
  136. package/dist/src/memory-upgrader.d.ts.map +0 -1
  137. package/dist/src/memory-upgrader.js.map +0 -7
  138. package/dist/src/migrate.d.ts.map +0 -1
  139. package/dist/src/migrate.js.map +0 -7
  140. package/dist/src/mnemo.d.ts.map +0 -1
  141. package/dist/src/mnemo.js.map +0 -7
  142. package/dist/src/noise-filter.d.ts.map +0 -1
  143. package/dist/src/noise-filter.js.map +0 -7
  144. package/dist/src/noise-prototypes.d.ts.map +0 -1
  145. package/dist/src/noise-prototypes.js.map +0 -7
  146. package/dist/src/observability.d.ts.map +0 -1
  147. package/dist/src/observability.js.map +0 -7
  148. package/dist/src/query-tracker.d.ts.map +0 -1
  149. package/dist/src/query-tracker.js.map +0 -7
  150. package/dist/src/reflection-event-store.d.ts.map +0 -1
  151. package/dist/src/reflection-event-store.js.map +0 -7
  152. package/dist/src/reflection-item-store.d.ts.map +0 -1
  153. package/dist/src/reflection-item-store.js.map +0 -7
  154. package/dist/src/reflection-mapped-metadata.d.ts.map +0 -1
  155. package/dist/src/reflection-mapped-metadata.js.map +0 -7
  156. package/dist/src/reflection-metadata.d.ts.map +0 -1
  157. package/dist/src/reflection-metadata.js.map +0 -7
  158. package/dist/src/reflection-ranking.d.ts.map +0 -1
  159. package/dist/src/reflection-ranking.js.map +0 -7
  160. package/dist/src/reflection-retry.d.ts.map +0 -1
  161. package/dist/src/reflection-retry.js.map +0 -7
  162. package/dist/src/reflection-slices.d.ts.map +0 -1
  163. package/dist/src/reflection-slices.js.map +0 -7
  164. package/dist/src/reflection-store.d.ts.map +0 -1
  165. package/dist/src/reflection-store.js.map +0 -7
  166. package/dist/src/resonance-state.d.ts.map +0 -1
  167. package/dist/src/resonance-state.js.map +0 -7
  168. package/dist/src/retriever.d.ts.map +0 -1
  169. package/dist/src/retriever.js.map +0 -7
  170. package/dist/src/scopes.d.ts.map +0 -1
  171. package/dist/src/scopes.js.map +0 -7
  172. package/dist/src/self-improvement-files.d.ts.map +0 -1
  173. package/dist/src/self-improvement-files.js.map +0 -7
  174. package/dist/src/semantic-gate.d.ts.map +0 -1
  175. package/dist/src/semantic-gate.js.map +0 -7
  176. package/dist/src/session-recovery.d.ts.map +0 -1
  177. package/dist/src/session-recovery.js.map +0 -7
  178. package/dist/src/smart-extractor.d.ts.map +0 -1
  179. package/dist/src/smart-extractor.js.map +0 -7
  180. package/dist/src/smart-metadata.d.ts.map +0 -1
  181. package/dist/src/smart-metadata.js.map +0 -7
  182. package/dist/src/storage-adapter.d.ts.map +0 -1
  183. package/dist/src/storage-adapter.js.map +0 -7
  184. package/dist/src/store.d.ts.map +0 -1
  185. package/dist/src/store.js.map +0 -7
  186. package/dist/src/tier-manager.d.ts.map +0 -1
  187. package/dist/src/tier-manager.js.map +0 -7
  188. package/dist/src/tools.d.ts.map +0 -1
  189. package/dist/src/tools.js.map +0 -7
  190. package/dist/src/wal-recovery.d.ts.map +0 -1
  191. package/dist/src/wal-recovery.js.map +0 -7
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/adapters/lancedb.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * LanceDB Storage Adapter \u2014 Default backend for Mnemo.\n *\n * Implements StorageAdapter using @lancedb/lancedb.\n * This is the reference implementation; other backends should\n * produce equivalent behavior.\n */\n\nimport type {\n StorageAdapter,\n MemoryRecord,\n SearchResult,\n QueryOptions,\n} from \"../storage-adapter.js\";\nimport { registerAdapter } from \"../storage-adapter.js\";\n\n/** Strict allowlist sanitizer \u2014 prevents SQL injection in LanceDB filters */\nfunction sanitize(value: string): string {\n if (typeof value !== \"string\") return \"\";\n return value.replace(/[^a-zA-Z0-9\\-_.:@ \\u4e00-\\u9fff\\u3400-\\u4dbf]/g, \"\");\n}\n\n// Dynamic import to avoid hard dependency at module level\nlet _lancedb: typeof import(\"@lancedb/lancedb\") | null = null;\n\nasync function loadLanceDB() {\n if (!_lancedb) {\n _lancedb = await import(\"@lancedb/lancedb\");\n }\n return _lancedb;\n}\n\nconst TABLE_NAME = \"memories\";\n\nexport class LanceDBAdapter implements StorageAdapter {\n readonly name = \"lancedb\";\n\n private db: any = null;\n private table: any = null;\n private ftsReady = false;\n private vectorDim = 0;\n\n async connect(dbPath: string): Promise<void> {\n const lancedb = await loadLanceDB();\n this.db = await lancedb.connect(dbPath);\n }\n\n async ensureTable(vectorDimensions: number): Promise<void> {\n this.vectorDim = vectorDimensions;\n\n try {\n this.table = await this.db.openTable(TABLE_NAME);\n } catch {\n // Table doesn't exist \u2014 create with schema\n const lancedb = await loadLanceDB();\n const schemaEntry: MemoryRecord = {\n id: \"__schema__\",\n text: \"\",\n vector: new Array(vectorDimensions).fill(0),\n timestamp: 0,\n scope: \"global\",\n importance: 0,\n category: \"other\",\n metadata: \"{}\",\n };\n\n try {\n this.table = await this.db.createTable(TABLE_NAME, [schemaEntry]);\n await this.table.delete('id = \"__schema__\"');\n } catch (err) {\n if (String(err).includes(\"already exists\")) {\n this.table = await this.db.openTable(TABLE_NAME);\n } else {\n throw err;\n }\n }\n }\n\n // Validate dimensions\n const sample = await this.table.query().limit(1).toArray();\n if (sample.length > 0 && sample[0]?.vector?.length) {\n const existing = sample[0].vector.length;\n if (existing !== vectorDimensions) {\n throw new Error(\n `Vector dimension mismatch: table=${existing}, config=${vectorDimensions}`\n );\n }\n }\n\n // Create FTS index\n await this.ensureFullTextIndex();\n }\n\n async add(records: MemoryRecord[]): Promise<void> {\n if (!this.table) throw new Error(\"Table not initialized\");\n await this.table.add(records);\n }\n\n async update(id: string, record: MemoryRecord): Promise<void> {\n if (!this.table) throw new Error(\"Table not initialized\");\n await this.table.delete(`id = '${sanitize(id)}'`);\n await this.table.add([record]);\n }\n\n async delete(filter: string): Promise<void> {\n if (!this.table) throw new Error(\"Table not initialized\");\n await this.table.delete(filter);\n }\n\n async vectorSearch(\n vector: number[],\n limit: number,\n minScore = 0,\n scopeFilter?: string[],\n ): Promise<SearchResult[]> {\n if (!this.table) throw new Error(\"Table not initialized\");\n\n let query = this.table.vectorSearch(vector).distanceType(\"cosine\").limit(limit * 3);\n\n if (scopeFilter?.length) {\n const scopeExpr = scopeFilter.map((s) => `'${sanitize(s)}'`).join(\", \");\n query = query.where(`scope IN (${scopeExpr})`);\n }\n\n const raw = await query.toArray();\n\n return raw\n .map((row: any) => {\n const distance = row._distance ?? row.distance ?? 1;\n const score = 1 / (1 + distance);\n return { record: this.toRecord(row), score };\n })\n .filter((r: SearchResult) => r.score >= minScore)\n .slice(0, limit);\n }\n\n async fullTextSearch(\n queryText: string,\n limit: number,\n scopeFilter?: string[],\n ): Promise<SearchResult[]> {\n if (!this.table || !this.ftsReady) return [];\n\n let query = this.table.search(queryText, \"fts\").limit(limit * 2);\n\n if (scopeFilter?.length) {\n const scopeExpr = scopeFilter.map((s) => `'${sanitize(s)}'`).join(\", \");\n query = query.where(`scope IN (${scopeExpr})`);\n }\n\n const raw = await query.toArray();\n\n return raw\n .map((row: any) => {\n const score = row._relevance_score ?? row.score ?? 0.5;\n return { record: this.toRecord(row), score };\n })\n .slice(0, limit);\n }\n\n async query(options: QueryOptions): Promise<MemoryRecord[]> {\n if (!this.table) throw new Error(\"Table not initialized\");\n\n let q = this.table.query();\n\n if (options.select?.length) {\n q = q.select(options.select);\n }\n if (options.where) {\n q = q.where(options.where);\n }\n if (options.limit) {\n q = q.limit(options.limit);\n }\n\n const raw = await q.toArray();\n return raw.map((row: any) => this.toRecord(row));\n }\n\n async count(filter?: string): Promise<number> {\n if (!this.table) throw new Error(\"Table not initialized\");\n let q = this.table.query();\n if (filter) q = q.where(filter);\n const rows = await q.toArray();\n return rows.length;\n }\n\n async ensureFullTextIndex(): Promise<void> {\n if (!this.table) return;\n\n try {\n const indices = await this.table.listIndices();\n const hasFts = indices?.some(\n (idx: any) => idx.indexType === \"FTS\" || idx.columns?.includes(\"text\"),\n );\n\n if (!hasFts) {\n const lancedb = await loadLanceDB();\n await this.table.createIndex(\"text\", {\n config: (lancedb as any).Index.fts(),\n });\n }\n this.ftsReady = true;\n } catch {\n this.ftsReady = false;\n }\n }\n\n hasFullTextSearch(): boolean {\n return this.ftsReady;\n }\n\n async close(): Promise<void> {\n this.table = null;\n this.db = null;\n }\n\n // \u2500\u2500 Helpers \u2500\u2500\n\n private toRecord(row: any): MemoryRecord {\n return {\n id: row.id,\n text: row.text,\n vector: row.vector ? Array.from(row.vector) : [],\n timestamp: row.timestamp ?? 0,\n scope: row.scope ?? \"global\",\n importance: row.importance ?? 0.5,\n category: row.category ?? \"other\",\n metadata: row.metadata ?? \"{}\",\n ...row, // preserve extra fields\n };\n }\n}\n\n// \u2500\u2500 Auto-register \u2500\u2500\nregisterAdapter(\"lancedb\", () => new LanceDBAdapter());\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"pgvector.d.ts","sourceRoot":"","sources":["../../../src/adapters/pgvector.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,uBAAuB,CAAC;AAK/B,qBAAa,eAAgB,YAAW,cAAc;IACpD,QAAQ,CAAC,IAAI,cAAc;IAE3B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,gBAAgB,CAAS;gBAErB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAKtC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtC,WAAW,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCpD,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3C,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrC,YAAY,CAChB,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,SAAI,EACZ,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC;IAyBpB,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC;IAyBpB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IASrD,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOvC,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1C,iBAAiB,IAAI,OAAO;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,QAAQ;CAYjB"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/adapters/pgvector.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * PGVector Storage Adapter for Mnemo\n *\n * Requirements:\n * npm install pg pgvector\n * PostgreSQL with pgvector extension enabled\n *\n * Config:\n * storage: \"pgvector\"\n * storageConfig: { connectionString: \"postgres://user:pass@localhost:5432/mnemo\" }\n */\n\nimport type {\n StorageAdapter,\n MemoryRecord,\n SearchResult,\n QueryOptions,\n} from \"../storage-adapter.js\";\nimport { registerAdapter } from \"../storage-adapter.js\";\n\nconst TABLE = \"mnemo_memories\";\n\nexport class PGVectorAdapter implements StorageAdapter {\n readonly name = \"pgvector\";\n\n private pool: any = null;\n private vectorDim = 0;\n private connectionString: string;\n\n constructor(config?: Record<string, unknown>) {\n this.connectionString = (config?.connectionString as string) ||\n \"postgres://localhost:5432/mnemo\";\n }\n\n async connect(dbPath: string): Promise<void> {\n const { Pool } = await import(\"pg\");\n this.pool = new Pool({\n connectionString: dbPath || this.connectionString,\n });\n\n // Enable pgvector extension\n await this.pool.query(\"CREATE EXTENSION IF NOT EXISTS vector\");\n }\n\n async ensureTable(vectorDimensions: number): Promise<void> {\n this.vectorDim = vectorDimensions;\n\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${TABLE} (\n id TEXT PRIMARY KEY,\n text TEXT NOT NULL,\n vector vector(${vectorDimensions}),\n timestamp BIGINT DEFAULT 0,\n scope TEXT DEFAULT 'global',\n importance REAL DEFAULT 0.5,\n category TEXT DEFAULT 'other',\n metadata JSONB DEFAULT '{}'::jsonb\n )\n `);\n\n // Create HNSW index for vector search\n await this.pool.query(`\n CREATE INDEX IF NOT EXISTS ${TABLE}_vector_idx\n ON ${TABLE} USING hnsw (vector vector_cosine_ops)\n `).catch(() => {}); // ignore if already exists\n\n // Create GIN index for full-text search\n await this.pool.query(`\n CREATE INDEX IF NOT EXISTS ${TABLE}_text_idx\n ON ${TABLE} USING gin (to_tsvector('simple', text))\n `).catch(() => {});\n\n // Create index on scope for filtering\n await this.pool.query(`\n CREATE INDEX IF NOT EXISTS ${TABLE}_scope_idx ON ${TABLE} (scope)\n `).catch(() => {});\n }\n\n async add(records: MemoryRecord[]): Promise<void> {\n for (const r of records) {\n await this.pool.query(\n `INSERT INTO ${TABLE} (id, text, vector, timestamp, scope, importance, category, metadata)\n VALUES ($1, $2, $3::vector, $4, $5, $6, $7, $8)\n ON CONFLICT (id) DO UPDATE SET\n text = EXCLUDED.text,\n vector = EXCLUDED.vector,\n timestamp = EXCLUDED.timestamp,\n scope = EXCLUDED.scope,\n importance = EXCLUDED.importance,\n category = EXCLUDED.category,\n metadata = EXCLUDED.metadata`,\n [r.id, r.text, `[${r.vector.join(\",\")}]`, r.timestamp, r.scope, r.importance, r.category, r.metadata],\n );\n }\n }\n\n async update(id: string, record: MemoryRecord): Promise<void> {\n await this.add([record]);\n }\n\n async delete(filter: string): Promise<void> {\n const idMatch = filter.match(/id\\s*=\\s*'([^']+)'/);\n if (idMatch) {\n await this.pool.query(`DELETE FROM ${TABLE} WHERE id = $1`, [idMatch[1]]);\n } else {\n // Pass filter as-is for simple SQL WHERE clauses\n await this.pool.query(`DELETE FROM ${TABLE} WHERE ${filter}`);\n }\n }\n\n async vectorSearch(\n vector: number[],\n limit: number,\n minScore = 0,\n scopeFilter?: string[],\n ): Promise<SearchResult[]> {\n const vectorStr = `[${vector.join(\",\")}]`;\n let query = `\n SELECT *, 1 - (vector <=> $1::vector) AS score\n FROM ${TABLE}\n WHERE 1 - (vector <=> $1::vector) >= $2\n `;\n const params: any[] = [vectorStr, minScore];\n\n if (scopeFilter?.length) {\n query += ` AND scope = ANY($3)`;\n params.push(scopeFilter);\n }\n\n query += ` ORDER BY vector <=> $1::vector LIMIT $${params.length + 1}`;\n params.push(limit);\n\n const result = await this.pool.query(query, params);\n\n return result.rows.map((row: any) => ({\n record: this.toRecord(row),\n score: parseFloat(row.score),\n }));\n }\n\n async fullTextSearch(\n queryText: string,\n limit: number,\n scopeFilter?: string[],\n ): Promise<SearchResult[]> {\n // Use PostgreSQL full-text search with ts_rank\n let query = `\n SELECT *, ts_rank(to_tsvector('simple', text), plainto_tsquery('simple', $1)) AS score\n FROM ${TABLE}\n WHERE to_tsvector('simple', text) @@ plainto_tsquery('simple', $1)\n `;\n const params: any[] = [queryText];\n\n if (scopeFilter?.length) {\n query += ` AND scope = ANY($2)`;\n params.push(scopeFilter);\n }\n\n query += ` ORDER BY score DESC LIMIT $${params.length + 1}`;\n params.push(limit);\n\n const result = await this.pool.query(query, params);\n\n return result.rows.map((row: any) => ({\n record: this.toRecord(row),\n score: parseFloat(row.score),\n }));\n }\n\n async query(options: QueryOptions): Promise<MemoryRecord[]> {\n let query = `SELECT * FROM ${TABLE}`;\n if (options.where) query += ` WHERE ${options.where}`;\n query += ` LIMIT ${options.limit || 100}`;\n\n const result = await this.pool.query(query);\n return result.rows.map((row: any) => this.toRecord(row));\n }\n\n async count(filter?: string): Promise<number> {\n let query = `SELECT COUNT(*) FROM ${TABLE}`;\n if (filter) query += ` WHERE ${filter}`;\n const result = await this.pool.query(query);\n return parseInt(result.rows[0].count);\n }\n\n async ensureFullTextIndex(): Promise<void> {\n // Created in ensureTable\n }\n\n hasFullTextSearch(): boolean {\n return true; // PostgreSQL has native full-text search\n }\n\n async close(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n this.pool = null;\n }\n }\n\n // \u2500\u2500 Helpers \u2500\u2500\n\n private toRecord(row: any): MemoryRecord {\n return {\n id: row.id,\n text: row.text,\n vector: row.vector ? (typeof row.vector === \"string\" ? JSON.parse(row.vector) : Array.from(row.vector)) : [],\n timestamp: parseInt(row.timestamp) || 0,\n scope: row.scope ?? \"global\",\n importance: parseFloat(row.importance) || 0.5,\n category: row.category ?? \"other\",\n metadata: typeof row.metadata === \"object\" ? JSON.stringify(row.metadata) : (row.metadata ?? \"{}\"),\n };\n }\n}\n\nregisterAdapter(\"pgvector\", (config) => new PGVectorAdapter(config));\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"qdrant.d.ts","sourceRoot":"","sources":["../../../src/adapters/qdrant.ts"],"names":[],"mappings":"AACA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,uBAAuB,CAAC;AAK/B,qBAAa,aAAc,YAAW,cAAc;IAClD,QAAQ,CAAC,IAAI,YAAY;IAEzB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,GAAG,CAAmC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAAK;gBAEV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAKtC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQxB,WAAW,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBpD,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB3C,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrC,YAAY,CAChB,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,SAAI,EACZ,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC;IAmBpB,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC;IAMpB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAerD,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQvC,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAK1C,iBAAiB,IAAI,OAAO;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,WAAW;CASpB"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/adapters/qdrant.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Qdrant Storage Adapter for Mnemo\n *\n * Requirements:\n * npm install @qdrant/js-client-rest\n *\n * Config:\n * storage: \"qdrant\"\n * storageConfig: { url: \"http://localhost:6333\", apiKey?: \"...\" }\n */\n\nimport type {\n StorageAdapter,\n MemoryRecord,\n SearchResult,\n QueryOptions,\n} from \"../storage-adapter.js\";\nimport { registerAdapter } from \"../storage-adapter.js\";\n\nconst COLLECTION = \"mnemo_memories\";\n\nexport class QdrantAdapter implements StorageAdapter {\n readonly name = \"qdrant\";\n\n private client: any = null;\n private url: string = \"http://localhost:6333\";\n private apiKey?: string;\n private vectorDim = 0;\n\n constructor(config?: Record<string, unknown>) {\n if (config?.url) this.url = config.url as string;\n if (config?.apiKey) this.apiKey = config.apiKey as string;\n }\n\n async connect(): Promise<void> {\n const { QdrantClient } = await import(\"@qdrant/js-client-rest\");\n this.client = new QdrantClient({\n url: this.url,\n ...(this.apiKey ? { apiKey: this.apiKey } : {}),\n });\n }\n\n async ensureTable(vectorDimensions: number): Promise<void> {\n this.vectorDim = vectorDimensions;\n const collections = await this.client.getCollections();\n const exists = collections.collections.some((c: any) => c.name === COLLECTION);\n\n if (!exists) {\n await this.client.createCollection(COLLECTION, {\n vectors: { size: vectorDimensions, distance: \"Cosine\" },\n });\n // Create payload indices for filtering\n await this.client.createPayloadIndex(COLLECTION, {\n field_name: \"scope\",\n field_schema: \"keyword\",\n });\n await this.client.createPayloadIndex(COLLECTION, {\n field_name: \"category\",\n field_schema: \"keyword\",\n });\n }\n }\n\n async add(records: MemoryRecord[]): Promise<void> {\n const points = records.map((r) => ({\n id: r.id,\n vector: r.vector,\n payload: {\n text: r.text,\n timestamp: r.timestamp,\n scope: r.scope,\n importance: r.importance,\n category: r.category,\n metadata: r.metadata,\n },\n }));\n await this.client.upsert(COLLECTION, { points });\n }\n\n async update(id: string, record: MemoryRecord): Promise<void> {\n await this.add([record]);\n }\n\n async delete(filter: string): Promise<void> {\n // Parse simple \"id = 'xxx'\" filter\n const idMatch = filter.match(/id\\s*=\\s*'([^']+)'/);\n if (idMatch) {\n await this.client.delete(COLLECTION, {\n points: [idMatch[1]],\n });\n }\n }\n\n async vectorSearch(\n vector: number[],\n limit: number,\n minScore = 0,\n scopeFilter?: string[],\n ): Promise<SearchResult[]> {\n const filter = scopeFilter?.length\n ? { must: [{ key: \"scope\", match: { any: scopeFilter } }] }\n : undefined;\n\n const results = await this.client.search(COLLECTION, {\n vector,\n limit,\n with_payload: true,\n score_threshold: minScore,\n ...(filter ? { filter } : {}),\n });\n\n return results.map((r: any) => ({\n record: this.toRecord(r.id, r.payload),\n score: r.score,\n }));\n }\n\n async fullTextSearch(\n _query: string,\n _limit: number,\n _scopeFilter?: string[],\n ): Promise<SearchResult[]> {\n // Qdrant doesn't have native BM25 \u2014 fall back to empty\n // Users should pair with a separate FTS engine or use vector search\n return [];\n }\n\n async query(options: QueryOptions): Promise<MemoryRecord[]> {\n const filter = options.where\n ? this.parseFilter(options.where)\n : undefined;\n\n const result = await this.client.scroll(COLLECTION, {\n limit: options.limit || 100,\n with_payload: true,\n with_vectors: true,\n ...(filter ? { filter } : {}),\n });\n\n return result.points.map((p: any) => this.toRecord(p.id, p.payload, p.vector));\n }\n\n async count(filter?: string): Promise<number> {\n const result = await this.client.count(COLLECTION, {\n ...(filter ? { filter: this.parseFilter(filter) } : {}),\n exact: true,\n });\n return result.count;\n }\n\n async ensureFullTextIndex(): Promise<void> {\n // Qdrant uses payload indices, not FTS indices\n // Text search via Qdrant requires external FTS or payload keyword match\n }\n\n hasFullTextSearch(): boolean {\n return false; // Qdrant doesn't have native BM25\n }\n\n async close(): Promise<void> {\n this.client = null;\n }\n\n // \u2500\u2500 Helpers \u2500\u2500\n\n private toRecord(id: string, payload: any, vector?: number[]): MemoryRecord {\n return {\n id,\n text: payload.text ?? \"\",\n vector: vector ? Array.from(vector) : [],\n timestamp: payload.timestamp ?? 0,\n scope: payload.scope ?? \"global\",\n importance: payload.importance ?? 0.5,\n category: payload.category ?? \"other\",\n metadata: payload.metadata ?? \"{}\",\n };\n }\n\n private parseFilter(where: string): any {\n // Simple parser for common filters\n const scopeMatch = where.match(/scope\\s+IN\\s*\\(([^)]+)\\)/i);\n if (scopeMatch) {\n const scopes = scopeMatch[1].split(\",\").map((s) => s.trim().replace(/'/g, \"\"));\n return { must: [{ key: \"scope\", match: { any: scopes } }] };\n }\n return undefined;\n }\n}\n\nregisterAdapter(\"qdrant\", (config) => new QdrantAdapter(config));\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"adaptive-retrieval.d.ts","sourceRoot":"","sources":["../../src/adaptive-retrieval.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAkDH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CA2B9E"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/adaptive-retrieval.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Adaptive Retrieval\n * Determines whether a query needs memory retrieval at all.\n * Skips retrieval for greetings, commands, simple instructions, and system messages.\n * Saves embedding API calls and reduces noise injection.\n */\n\n// Queries that are clearly NOT memory-retrieval candidates\nconst SKIP_PATTERNS = [\n // Greetings & pleasantries\n /^(hi|hello|hey|good\\s*(morning|afternoon|evening|night)|greetings|yo|sup|howdy|what'?s up)\\b/i,\n // System/bot commands\n /^\\//, // slash commands\n /^(run|build|test|ls|cd|git|npm|pip|docker|curl|cat|grep|find|make|sudo)\\b/i,\n // Simple affirmations/negations\n /^(yes|no|yep|nope|ok|okay|sure|fine|thanks|thank you|thx|ty|got it|understood|cool|nice|great|good|perfect|awesome)\\s*[.!]?$/i,\n // Continuation prompts\n /^(go ahead|continue|proceed|do it|start|begin|next)\\s*[.!]?$/i,\n // Pure emoji\n /^[\\p{Emoji}\\s]+$/u,\n // Heartbeat/system (match anywhere, not just at start, to handle prefixed formats)\n /HEARTBEAT/i,\n /^\\[System/i,\n // Single-word utility pings\n /^(ping|pong|test|debug)\\s*[.!?]?$/i,\n];\n\n// Queries that SHOULD trigger retrieval even if short\nconst FORCE_RETRIEVE_PATTERNS = [\n /\\b(remember|recall|forgot|memory|memories)\\b/i,\n /\\b(last time|before|previously|earlier|yesterday|ago)\\b/i,\n /\\b(my (name|email|phone|address|birthday|preference))\\b/i,\n /\\b(what did (i|we)|did i (tell|say|mention))\\b/i,\n];\n\n/**\n * Normalize the raw prompt before applying skip/force rules.\n */\nfunction normalizeQuery(query: string): string {\n let s = query.trim();\n\n // 1. Strip injected metadata headers.\n const metadataPattern = /^(Conversation info|Sender) \\(untrusted metadata\\):[\\s\\S]*?\\n\\s*\\n/gim;\n s = s.replace(metadataPattern, \"\");\n\n // 2. Strip cron wrapper prefix.\n s = s.trim().replace(/^\\[cron:[^\\]]+\\]\\s*/i, \"\");\n\n // 3. Strip timestamp prefix [Mon 2026-03-02 04:21 GMT+8].\n s = s.trim().replace(/^\\[[A-Za-z]{3}\\s\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}\\s[^\\]]+\\]\\s*/, \"\");\n\n const result = s.trim();\n return result;\n}\n\n/**\n * Determine if a query should skip memory retrieval.\n * Returns true if retrieval should be skipped.\n * @param query The raw prompt text\n * @param minLength Optional minimum length override (if set, overrides built-in thresholds)\n */\nexport function shouldSkipRetrieval(query: string, minLength?: number): boolean {\n const trimmed = normalizeQuery(query);\n\n // Force retrieve if query has memory-related intent (checked FIRST,\n // before length check, so short CJK queries aren't skipped)\n if (FORCE_RETRIEVE_PATTERNS.some(p => p.test(trimmed))) return false;\n\n // Too short to be meaningful\n if (trimmed.length < 5) return true;\n\n // Skip if matches any skip pattern\n if (SKIP_PATTERNS.some(p => p.test(trimmed))) return true;\n\n // If caller provides a custom minimum length, use it\n if (minLength !== undefined && minLength > 0) {\n if (trimmed.length < minLength && !trimmed.includes('?')) return true;\n return false;\n }\n\n // Skip very short non-question messages (likely commands or affirmations)\n // CJK characters carry more meaning per character, so use a lower threshold\n const hasCJK = /[\\u4e00-\\u9fff\\u3040-\\u309f\\u30a0-\\u30ff\\uac00-\\ud7af]/.test(trimmed);\n const defaultMinLength = hasCJK ? 6 : 15;\n if (trimmed.length < defaultMinLength && !trimmed.includes('?')) return true;\n\n // Default: do retrieve\n return false;\n}\n"],
5
- "mappings": "AASA,MAAM,gBAAgB;AAAA;AAAA,EAEpB;AAAA;AAAA,EAEA;AAAA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AACF;AAGA,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,SAAS,eAAe,OAAuB;AAC7C,MAAI,IAAI,MAAM,KAAK;AAGnB,QAAM,kBAAkB;AACxB,MAAI,EAAE,QAAQ,iBAAiB,EAAE;AAGjC,MAAI,EAAE,KAAK,EAAE,QAAQ,wBAAwB,EAAE;AAG/C,MAAI,EAAE,KAAK,EAAE,QAAQ,+DAA+D,EAAE;AAEtF,QAAM,SAAS,EAAE,KAAK;AACtB,SAAO;AACT;AAQO,SAAS,oBAAoB,OAAe,WAA6B;AAC9E,QAAM,UAAU,eAAe,KAAK;AAIpC,MAAI,wBAAwB,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,EAAG,QAAO;AAG/D,MAAI,QAAQ,SAAS,EAAG,QAAO;AAG/B,MAAI,cAAc,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,EAAG,QAAO;AAGrD,MAAI,cAAc,UAAa,YAAY,GAAG;AAC5C,QAAI,QAAQ,SAAS,aAAa,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AACjE,WAAO;AAAA,EACT;AAIA,QAAM,SAAS,yDAAyD,KAAK,OAAO;AACpF,QAAM,mBAAmB,SAAS,IAAI;AACtC,MAAI,QAAQ,SAAS,oBAAoB,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAGxE,SAAO;AACT;",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"audit-log.d.ts","sourceRoot":"","sources":["../../src/audit-log.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AASH,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAyBD;;GAEG;AACH,wBAAsB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC5D;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAUN;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,IAAI,CAQN;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CASN;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CASN;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CASN;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,EAAE,CAAC,CA4BvB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEtD"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/audit-log.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: LicenseRef-Mnemo-Pro\n/**\n * Mnemo Audit Log \u2014 GDPR/EU AI Act compliance\n *\n * Records all memory CRUD operations with:\n * - WHO: agent/user identity\n * - WHAT: operation type + affected memory IDs\n * - WHEN: ISO timestamp\n * - WHY: source/trigger (auto-capture, manual, contradiction, etc.)\n *\n * Stored as append-only JSONL file. Supports retention policies.\n */\n\nimport { appendFile, mkdir, readFile, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst AUDIT_DIR = join(homedir(), \".mnemo\", \"audit\");\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB per file, then rotate\n\nexport type AuditAction =\n | \"create\"\n | \"update\"\n | \"delete\"\n | \"bulk_delete\"\n | \"expire\"\n | \"merge\"\n | \"recall\"\n | \"export\";\n\nexport interface AuditEntry {\n timestamp: string;\n action: AuditAction;\n actor: string; // agent ID, user ID, or \"system\"\n memoryIds: string[]; // affected memory IDs\n scope?: string;\n reason?: string; // \"auto-capture\", \"contradiction\", \"user-request\", \"decay\", etc.\n details?: string; // additional context (text preview, old\u2192new value, etc.)\n ip?: string; // for API-based access\n}\n\nlet _initialized = false;\nlet _currentFile = \"\";\nlet _enabled = true;\n\n/**\n * Initialize the audit log directory.\n */\nasync function ensureDir(): Promise<void> {\n if (_initialized) return;\n try {\n await mkdir(AUDIT_DIR, { recursive: true });\n _currentFile = getLogFileName();\n _initialized = true;\n } catch {\n _enabled = false;\n }\n}\n\nfunction getLogFileName(): string {\n const date = new Date().toISOString().slice(0, 10); // YYYY-MM-DD\n return join(AUDIT_DIR, `audit-${date}.jsonl`);\n}\n\n/**\n * Append an audit entry. Fire-and-forget \u2014 never blocks the main flow.\n */\nexport async function audit(entry: AuditEntry): Promise<void> {\n if (!_enabled) return;\n\n try {\n await ensureDir();\n\n // Rotate file daily\n const expectedFile = getLogFileName();\n if (expectedFile !== _currentFile) {\n _currentFile = expectedFile;\n }\n\n // Check file size for rotation\n try {\n const stats = await stat(_currentFile);\n if (stats.size > MAX_FILE_SIZE) {\n const rotatedName = _currentFile.replace(\".jsonl\", `-${Date.now()}.jsonl`);\n _currentFile = rotatedName;\n }\n } catch {\n // File doesn't exist yet, that's fine\n }\n\n const line = JSON.stringify({\n ...entry,\n timestamp: entry.timestamp || new Date().toISOString(),\n }) + \"\\n\";\n\n await appendFile(_currentFile, line);\n } catch {\n // Audit log failure should never break the main flow\n }\n}\n\n/**\n * Convenience: log a memory creation.\n */\nexport function auditCreate(\n memoryId: string,\n actor: string,\n scope: string,\n reason: string,\n textPreview?: string,\n): void {\n audit({\n timestamp: new Date().toISOString(),\n action: \"create\",\n actor,\n memoryIds: [memoryId],\n scope,\n reason,\n details: textPreview ? textPreview.slice(0, 200) : undefined,\n }).catch(() => {});\n}\n\n/**\n * Convenience: log a memory deletion.\n */\nexport function auditDelete(\n memoryIds: string[],\n actor: string,\n reason: string,\n): void {\n audit({\n timestamp: new Date().toISOString(),\n action: memoryIds.length > 1 ? \"bulk_delete\" : \"delete\",\n actor,\n memoryIds,\n reason,\n }).catch(() => {});\n}\n\n/**\n * Convenience: log a memory update (e.g., importance change, tier change).\n */\nexport function auditUpdate(\n memoryId: string,\n actor: string,\n reason: string,\n details?: string,\n): void {\n audit({\n timestamp: new Date().toISOString(),\n action: \"update\",\n actor,\n memoryIds: [memoryId],\n reason,\n details,\n }).catch(() => {});\n}\n\n/**\n * Convenience: log a memory expiration (contradiction resolution).\n */\nexport function auditExpire(\n memoryId: string,\n actor: string,\n reason: string,\n details?: string,\n): void {\n audit({\n timestamp: new Date().toISOString(),\n action: \"expire\",\n actor,\n memoryIds: [memoryId],\n reason,\n details,\n }).catch(() => {});\n}\n\n/**\n * Convenience: log a memory recall (for access audit trail).\n */\nexport function auditRecall(\n memoryIds: string[],\n actor: string,\n query?: string,\n): void {\n audit({\n timestamp: new Date().toISOString(),\n action: \"recall\",\n actor,\n memoryIds,\n reason: \"retrieval\",\n details: query ? query.slice(0, 200) : undefined,\n }).catch(() => {});\n}\n\n/**\n * Read audit log entries for a date range.\n * Useful for compliance exports.\n */\nexport async function readAuditLog(\n startDate: string,\n endDate: string,\n): Promise<AuditEntry[]> {\n await ensureDir();\n const entries: AuditEntry[] = [];\n\n const start = new Date(startDate);\n const end = new Date(endDate);\n const current = new Date(start);\n\n while (current <= end) {\n const dateStr = current.toISOString().slice(0, 10);\n const filePath = join(AUDIT_DIR, `audit-${dateStr}.jsonl`);\n\n try {\n const content = await readFile(filePath, \"utf8\");\n const lines = content.trim().split(\"\\n\").filter(Boolean);\n for (const line of lines) {\n try {\n entries.push(JSON.parse(line));\n } catch { /* skip malformed */ }\n }\n } catch {\n // File doesn't exist for this date, skip\n }\n\n current.setDate(current.getDate() + 1);\n }\n\n return entries;\n}\n\n/**\n * Enable or disable audit logging.\n */\nexport function setAuditEnabled(enabled: boolean): void {\n _enabled = enabled;\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../../src/chunker.ts"],"names":[],"mappings":"AACA;;;;;;;;;GASG;AAMH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,aAAa,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAID,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiB3D,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,aAMpC,CAAC;AAoGF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,aAAsC,GAAG,WAAW,CA6DvG;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,WAAW,CAa5E;AAED,eAAe,aAAa,CAAC"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/chunker.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Long Context Chunking System\n *\n * Goal: split documents that exceed embedding model context limits into smaller,\n * semantically coherent chunks with overlap.\n *\n * Notes:\n * - We use *character counts* as a conservative proxy for tokens.\n * - The embedder triggers this only after a provider throws a context-length error.\n */\n\n// ============================================================================\n// Types & Constants\n// ============================================================================\n\nexport interface ChunkMetadata {\n startIndex: number;\n endIndex: number;\n length: number;\n}\n\nexport interface ChunkResult {\n chunks: string[];\n metadatas: ChunkMetadata[];\n totalOriginalLength: number;\n chunkCount: number;\n}\n\nexport interface ChunkerConfig {\n /** Maximum characters per chunk. */\n maxChunkSize: number;\n /** Overlap between chunks in characters. */\n overlapSize: number;\n /** Minimum chunk size (except the final chunk). */\n minChunkSize: number;\n /** Attempt to split on sentence boundaries for better semantic coherence. */\n semanticSplit: boolean;\n /** Max lines per chunk before we try to split earlier on a line boundary. */\n maxLinesPerChunk: number;\n}\n\n// Common embedding context limits (provider/model specific). These are typically\n// token limits, but we treat them as inputs to a conservative char-based heuristic.\nexport const EMBEDDING_CONTEXT_LIMITS: Record<string, number> = {\n // Jina v5\n \"jina-embeddings-v5-text-small\": 8192,\n \"jina-embeddings-v5-text-nano\": 8192,\n\n // OpenAI\n \"text-embedding-3-small\": 8192,\n \"text-embedding-3-large\": 8192,\n\n // Google\n \"text-embedding-004\": 8192,\n \"gemini-embedding-001\": 2048,\n\n // Local/common\n \"nomic-embed-text\": 8192,\n \"all-MiniLM-L6-v2\": 512,\n \"all-mpnet-base-v2\": 512,\n};\n\nexport const DEFAULT_CHUNKER_CONFIG: ChunkerConfig = {\n maxChunkSize: 4000,\n overlapSize: 200,\n minChunkSize: 200,\n semanticSplit: true,\n maxLinesPerChunk: 50,\n};\n\n// Sentence ending patterns (English + CJK-ish punctuation)\nconst SENTENCE_ENDING = /[.!?\u3002\uFF01\uFF1F]/;\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n\nfunction countLines(s: string): number {\n // Count \\n (treat CRLF as one line break)\n return s.split(/\\r\\n|\\n|\\r/).length;\n}\n\nfunction findLastIndexWithin(text: string, re: RegExp, start: number, end: number): number {\n // Find last match start index for regex within [start, end).\n // NOTE: `re` must NOT be global; we will scan manually.\n let last = -1;\n for (let i = end - 1; i >= start; i--) {\n if (re.test(text[i])) return i;\n }\n return last;\n}\n\nfunction findSplitEnd(text: string, start: number, maxEnd: number, minEnd: number, config: ChunkerConfig): number {\n const safeMinEnd = clamp(minEnd, start + 1, maxEnd);\n const safeMaxEnd = clamp(maxEnd, safeMinEnd, text.length);\n\n // Respect line limit: if we exceed maxLinesPerChunk, force earlier split at a line break.\n if (config.maxLinesPerChunk > 0) {\n const candidate = text.slice(start, safeMaxEnd);\n if (countLines(candidate) > config.maxLinesPerChunk) {\n // Find the position of the Nth line break.\n let breaks = 0;\n for (let i = start; i < safeMaxEnd; i++) {\n const ch = text[i];\n if (ch === \"\\n\") {\n breaks++;\n if (breaks >= config.maxLinesPerChunk) {\n // Split right after this newline.\n return Math.max(i + 1, safeMinEnd);\n }\n }\n }\n }\n }\n\n if (config.semanticSplit) {\n // Prefer a sentence boundary near the end.\n // Scan backward from safeMaxEnd to safeMinEnd.\n for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {\n if (SENTENCE_ENDING.test(text[i])) {\n // Include trailing whitespace after punctuation.\n let j = i + 1;\n while (j < safeMaxEnd && /\\s/.test(text[j])) j++;\n return j;\n }\n }\n\n // Next best: newline boundary.\n for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {\n if (text[i] === \"\\n\") return i + 1;\n }\n }\n\n // Fallback: last whitespace boundary.\n for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {\n if (/\\s/.test(text[i])) return i;\n }\n\n return safeMaxEnd;\n}\n\nfunction sliceTrimWithIndices(text: string, start: number, end: number): { chunk: string; meta: ChunkMetadata } {\n const raw = text.slice(start, end);\n const leading = raw.match(/^\\s*/)?.[0]?.length ?? 0;\n const trailing = raw.match(/\\s*$/)?.[0]?.length ?? 0;\n const chunk = raw.trim();\n\n const trimmedStart = start + leading;\n const trimmedEnd = end - trailing;\n\n return {\n chunk,\n meta: {\n startIndex: trimmedStart,\n endIndex: Math.max(trimmedStart, trimmedEnd),\n length: chunk.length,\n },\n };\n}\n\n// ============================================================================\n// Chunking Core\n// ============================================================================\n\nexport function chunkDocument(text: string, config: ChunkerConfig = DEFAULT_CHUNKER_CONFIG): ChunkResult {\n if (!text || text.trim().length === 0) {\n return { chunks: [], metadatas: [], totalOriginalLength: 0, chunkCount: 0 };\n }\n\n const totalOriginalLength = text.length;\n const chunks: string[] = [];\n const metadatas: ChunkMetadata[] = [];\n\n let pos = 0;\n const maxGuard = Math.max(4, Math.ceil(text.length / Math.max(1, config.maxChunkSize - config.overlapSize)) + 5);\n let guard = 0;\n\n while (pos < text.length && guard < maxGuard) {\n guard++;\n\n const remaining = text.length - pos;\n if (remaining <= config.maxChunkSize) {\n const { chunk, meta } = sliceTrimWithIndices(text, pos, text.length);\n if (chunk.length > 0) {\n chunks.push(chunk);\n metadatas.push(meta);\n }\n break;\n }\n\n const maxEnd = Math.min(pos + config.maxChunkSize, text.length);\n const minEnd = Math.min(pos + config.minChunkSize, maxEnd);\n\n const end = findSplitEnd(text, pos, maxEnd, minEnd, config);\n const { chunk, meta } = sliceTrimWithIndices(text, pos, end);\n\n // If trimming made it too small, fall back to a hard split.\n if (chunk.length < config.minChunkSize) {\n const hardEnd = Math.min(pos + config.maxChunkSize, text.length);\n const hard = sliceTrimWithIndices(text, pos, hardEnd);\n if (hard.chunk.length > 0) {\n chunks.push(hard.chunk);\n metadatas.push(hard.meta);\n }\n if (hardEnd >= text.length) break;\n pos = Math.max(hardEnd - config.overlapSize, pos + 1);\n continue;\n }\n\n chunks.push(chunk);\n metadatas.push(meta);\n\n if (end >= text.length) break;\n\n // Move forward with overlap.\n const nextPos = Math.max(end - config.overlapSize, pos + 1);\n pos = nextPos;\n }\n\n return {\n chunks,\n metadatas,\n totalOriginalLength,\n chunkCount: chunks.length,\n };\n}\n\n/**\n * Smart chunker that adapts to model context limits.\n *\n * We intentionally pick conservative char limits (70% of the reported limit)\n * since token/char ratios vary.\n */\nexport function smartChunk(text: string, embedderModel?: string): ChunkResult {\n const limit = embedderModel ? EMBEDDING_CONTEXT_LIMITS[embedderModel] : undefined;\n const base = limit ?? 8192;\n\n const config: ChunkerConfig = {\n maxChunkSize: Math.max(1000, Math.floor(base * 0.7)),\n overlapSize: Math.max(0, Math.floor(base * 0.05)),\n minChunkSize: Math.max(100, Math.floor(base * 0.1)),\n semanticSplit: true,\n maxLinesPerChunk: 50,\n };\n\n return chunkDocument(text, config);\n}\n\nexport default chunkDocument;\n"],
5
- "mappings": "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",
6
- "names": ["chunk", "meta"]
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AACA;;;GAGG;AAUH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC;QAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,eAAe,GAAG,aAAa,GAAG,MAAM,CAAC;QAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;QAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;IACF,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACtD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;KACxC,CAAC;IACF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,QAAQ,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,cAAc,CAAC,EAAE;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAMD,wBAAgB,gBAAgB,IAAI,MAAM,CAWzC;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAYnE;AAMD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAuH9D;AAOD,wBAAgB,sBAAsB,IAAI,YAAY,CAgCrD"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/config.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Shared Configuration Helpers\n * Extracted from index.ts for reuse by MCP server and gateway plugin.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\nexport interface PluginConfig {\n embedding: {\n provider: \"openai-compatible\";\n apiKey: string | string[];\n model?: string;\n baseURL?: string;\n dimensions?: number;\n taskQuery?: string;\n taskPassage?: string;\n normalized?: boolean;\n };\n dbPath?: string;\n autoCapture?: boolean;\n autoRecall?: boolean;\n autoRecallMinLength?: number;\n autoRecallMinRepeated?: number;\n captureAssistant?: boolean;\n retrieval?: {\n mode?: \"hybrid\" | \"vector\";\n vectorWeight?: number;\n bm25Weight?: number;\n minScore?: number;\n rerank?: \"cross-encoder\" | \"lightweight\" | \"none\";\n candidatePoolSize?: number;\n rerankApiKey?: string;\n rerankModel?: string;\n rerankEndpoint?: string;\n rerankProvider?: \"jina\" | \"siliconflow\" | \"voyage\" | \"pinecone\";\n recencyHalfLifeDays?: number;\n recencyWeight?: number;\n filterNoise?: boolean;\n lengthNormAnchor?: number;\n hardMinScore?: number;\n timeDecayHalfLifeDays?: number;\n reinforcementFactor?: number;\n maxHalfLifeMultiplier?: number;\n };\n scopes?: {\n default?: string;\n definitions?: Record<string, { description: string }>;\n agentAccess?: Record<string, string[]>;\n };\n enableManagementTools?: boolean;\n sessionMemory?: { enabled?: boolean; messageCount?: number };\n mdMirror?: { enabled?: boolean; dir?: string };\n autoCaptureLLM?: {\n enabled?: boolean;\n endpoint?: string;\n model?: string;\n timeoutMs?: number;\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nexport function getDefaultDbPath(): string {\n // Prefer MNEMO_DB_PATH env, then ~/.mnemo/memory-db (open source default),\n // then ~/.openclaw/memory/lancedb-pro (OpenClaw integration fallback)\n if (process.env.MNEMO_DB_PATH) return process.env.MNEMO_DB_PATH;\n const mnemoPath = join(homedir(), \".mnemo\", \"memory-db\");\n const openclawPath = join(homedir(), \".openclaw\", \"memory\", \"lancedb-pro\");\n try {\n const { existsSync } = require(\"fs\");\n if (existsSync(openclawPath)) return openclawPath;\n } catch {}\n return mnemoPath;\n}\n\nexport function resolveEnvVars(value: string): string {\n return value.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\n const envValue = process.env[envVar];\n if (!envValue) {\n throw new Error(`Environment variable ${envVar} is not set`);\n }\n return envValue;\n });\n}\n\nexport function parsePositiveInt(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value) && value > 0) {\n return Math.floor(value);\n }\n if (typeof value === \"string\") {\n const s = value.trim();\n if (!s) return undefined;\n const resolved = resolveEnvVars(s);\n const n = Number(resolved);\n if (Number.isFinite(n) && n > 0) return Math.floor(n);\n }\n return undefined;\n}\n\n// ============================================================================\n// Config Parser\n// ============================================================================\n\nexport function parsePluginConfig(value: unknown): PluginConfig {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(\"mnemo config required\");\n }\n const cfg = value as Record<string, unknown>;\n\n const embedding = cfg.embedding as Record<string, unknown> | undefined;\n if (!embedding) {\n throw new Error(\"embedding config is required\");\n }\n\n // Accept single key (string) or array of keys for round-robin rotation\n let apiKey: string | string[];\n if (typeof embedding.apiKey === \"string\") {\n apiKey = embedding.apiKey;\n } else if (Array.isArray(embedding.apiKey) && embedding.apiKey.length > 0) {\n const invalid = embedding.apiKey.findIndex(\n (k: unknown) => typeof k !== \"string\" || (k as string).trim().length === 0,\n );\n if (invalid !== -1) {\n throw new Error(\n `embedding.apiKey[${invalid}] is invalid: expected non-empty string`,\n );\n }\n apiKey = embedding.apiKey as string[];\n } else if (embedding.apiKey !== undefined) {\n throw new Error(\"embedding.apiKey must be a string or non-empty array of strings\");\n } else {\n apiKey = process.env.OPENAI_API_KEY || \"\";\n }\n\n if (!apiKey || (Array.isArray(apiKey) && apiKey.length === 0)) {\n throw new Error(\"embedding.apiKey is required (set directly or via OPENAI_API_KEY env var)\");\n }\n\n return {\n embedding: {\n provider: \"openai-compatible\",\n apiKey,\n model:\n typeof embedding.model === \"string\"\n ? embedding.model\n : \"text-embedding-3-small\",\n baseURL:\n typeof embedding.baseURL === \"string\"\n ? resolveEnvVars(embedding.baseURL)\n : undefined,\n dimensions: parsePositiveInt(embedding.dimensions ?? cfg.dimensions),\n taskQuery:\n typeof embedding.taskQuery === \"string\"\n ? embedding.taskQuery\n : undefined,\n taskPassage:\n typeof embedding.taskPassage === \"string\"\n ? embedding.taskPassage\n : undefined,\n normalized:\n typeof embedding.normalized === \"boolean\"\n ? embedding.normalized\n : undefined,\n },\n dbPath: typeof cfg.dbPath === \"string\" ? cfg.dbPath : undefined,\n autoCapture: cfg.autoCapture !== false,\n autoRecall: cfg.autoRecall === true,\n autoRecallMinLength: parsePositiveInt(cfg.autoRecallMinLength),\n autoRecallMinRepeated: parsePositiveInt(cfg.autoRecallMinRepeated),\n captureAssistant: cfg.captureAssistant === true,\n retrieval:\n typeof cfg.retrieval === \"object\" && cfg.retrieval !== null\n ? (cfg.retrieval as any)\n : undefined,\n scopes:\n typeof cfg.scopes === \"object\" && cfg.scopes !== null\n ? (cfg.scopes as any)\n : undefined,\n enableManagementTools: cfg.enableManagementTools === true,\n sessionMemory:\n typeof cfg.sessionMemory === \"object\" && cfg.sessionMemory !== null\n ? {\n enabled:\n (cfg.sessionMemory as Record<string, unknown>).enabled !== false,\n messageCount:\n typeof (cfg.sessionMemory as Record<string, unknown>)\n .messageCount === \"number\"\n ? ((cfg.sessionMemory as Record<string, unknown>)\n .messageCount as number)\n : undefined,\n }\n : undefined,\n mdMirror:\n typeof cfg.mdMirror === \"object\" && cfg.mdMirror !== null\n ? {\n enabled:\n (cfg.mdMirror as Record<string, unknown>).enabled === true,\n dir:\n typeof (cfg.mdMirror as Record<string, unknown>).dir === \"string\"\n ? ((cfg.mdMirror as Record<string, unknown>).dir as string)\n : undefined,\n }\n : undefined,\n autoCaptureLLM:\n typeof cfg.autoCaptureLLM === \"object\" && cfg.autoCaptureLLM !== null\n ? {\n enabled:\n (cfg.autoCaptureLLM as Record<string, unknown>).enabled === true,\n endpoint:\n typeof (cfg.autoCaptureLLM as Record<string, unknown>).endpoint === \"string\"\n ? ((cfg.autoCaptureLLM as Record<string, unknown>).endpoint as string)\n : undefined,\n model:\n typeof (cfg.autoCaptureLLM as Record<string, unknown>).model === \"string\"\n ? ((cfg.autoCaptureLLM as Record<string, unknown>).model as string)\n : undefined,\n timeoutMs: parsePositiveInt(\n (cfg.autoCaptureLLM as Record<string, unknown>).timeoutMs,\n ),\n }\n : undefined,\n };\n}\n\n// ============================================================================\n// Load config from config file\n// Checks: MNEMO_CONFIG env \u2192 ~/.mnemo/mnemo.json \u2192 ~/.openclaw/openclaw.json\n// ============================================================================\n\nexport function loadConfigFromOpenClaw(): PluginConfig {\n const { existsSync } = require(\"fs\");\n const envPath = process.env.MNEMO_CONFIG;\n const mnemoPath = join(homedir(), \".mnemo\", \"mnemo.json\");\n const openclawPath = join(homedir(), \".openclaw\", \"openclaw.json\");\n const configPath = envPath || (existsSync(mnemoPath) ? mnemoPath : openclawPath);\n let raw: string;\n try {\n raw = readFileSync(configPath, \"utf8\");\n } catch (err) {\n throw new Error(\n `Failed to read ${configPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n let json: any;\n try {\n json = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `Failed to parse ${configPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const pluginConfig = json?.plugins?.entries?.[\"memory-lancedb-pro\"]?.config;\n if (!pluginConfig) {\n throw new Error(\n `No config found at plugins.entries[\"memory-lancedb-pro\"].config in ${configPath}`,\n );\n }\n\n return parsePluginConfig(pluginConfig);\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"decay-engine.d.ts","sourceRoot":"","sources":["../../src/decay-engine.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAQzD,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,eAAe,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAGD,eAAO,MAAM,oBAAoB,EAAE,WAclC,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,gDAAgD;IAChD,KAAK,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACzD,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC;IAClE,2EAA2E;IAC3E,gBAAgB,CACd,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAC1D,GAAG,CAAC,EAAE,MAAM,GACX,IAAI,CAAC;IACR,sDAAsD;IACtD,gBAAgB,CACd,QAAQ,EAAE,eAAe,EAAE,EAC3B,GAAG,CAAC,EAAE,MAAM,GACX,UAAU,EAAE,CAAC;CACjB;AAMD,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,WAAkC,GACzC,WAAW,CAgIb"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/decay-engine.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Decay Engine \u2014 Weibull stretched-exponential decay model\n *\n * Composite score = recencyWeight * recency + frequencyWeight * frequency + intrinsicWeight * intrinsic\n *\n * - Recency: Weibull decay with importance-modulated half-life and tier-specific beta\n * - Frequency: Logarithmic saturation with time-weighted access pattern bonus\n * - Intrinsic: importance \u00D7 confidence\n */\n\nimport type { MemoryTier } from \"./memory-categories.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nconst MS_PER_DAY = 86_400_000;\n\nexport interface DecayConfig {\n /** Days until recency score halves (default: 30) */\n recencyHalfLifeDays: number;\n /** Weight of recency in composite (default: 0.4) */\n recencyWeight: number;\n /** Weight of access frequency (default: 0.3) */\n frequencyWeight: number;\n /** Weight of importance \u00D7 confidence (default: 0.3) */\n intrinsicWeight: number;\n /** Below this composite = stale (default: 0.3) */\n staleThreshold: number;\n /** Minimum search boost (default: 0.3) */\n searchBoostMin: number;\n /** Importance modulation coefficient for half-life (default: 1.5) */\n importanceModulation: number;\n /** Weibull beta for Core tier \u2014 sub-exponential (default: 0.8) */\n betaCore: number;\n /** Weibull beta for Working tier \u2014 standard exponential (default: 1.0) */\n betaWorking: number;\n /** Weibull beta for Peripheral tier \u2014 super-exponential (default: 1.3) */\n betaPeripheral: number;\n /** Decay floor for Core memories (default: 0.9) */\n coreDecayFloor: number;\n /** Decay floor for Working memories (default: 0.7) */\n workingDecayFloor: number;\n /** Decay floor for Peripheral memories (default: 0.5) */\n peripheralDecayFloor: number;\n}\n\n// NOTE: These weights are initial values pending grid-search optimization.\nexport const DEFAULT_DECAY_CONFIG: DecayConfig = {\n recencyHalfLifeDays: 30,\n recencyWeight: 0.4, // initial \u2014 pending optimization\n frequencyWeight: 0.3, // initial \u2014 pending optimization\n intrinsicWeight: 0.3, // initial \u2014 pending optimization\n staleThreshold: 0.3,\n searchBoostMin: 0.3,\n importanceModulation: 1.5,\n betaCore: 0.8,\n betaWorking: 1.0,\n betaPeripheral: 1.3,\n coreDecayFloor: 0.9,\n workingDecayFloor: 0.7,\n peripheralDecayFloor: 0.5,\n};\n\nexport interface DecayScore {\n memoryId: string;\n recency: number;\n frequency: number;\n intrinsic: number;\n composite: number;\n}\n\n/** Minimal memory fields needed for decay calculation. */\nexport interface DecayableMemory {\n id: string;\n importance: number;\n confidence: number;\n tier: MemoryTier;\n accessCount: number;\n createdAt: number;\n lastAccessedAt: number;\n /** Emotional salience (0-1). Modulates decay: high salience = slower decay. */\n emotionalSalience: number;\n}\n\nexport interface DecayEngine {\n /** Calculate decay score for a single memory */\n score(memory: DecayableMemory, now?: number): DecayScore;\n /** Calculate decay scores for multiple memories */\n scoreAll(memories: DecayableMemory[], now?: number): DecayScore[];\n /** Apply decay boost to search results (multiplies each score by boost) */\n applySearchBoost(\n results: Array<{ memory: DecayableMemory; score: number }>,\n now?: number,\n ): void;\n /** Find stale memories (composite below threshold) */\n getStaleMemories(\n memories: DecayableMemory[],\n now?: number,\n ): DecayScore[];\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport function createDecayEngine(\n config: DecayConfig = DEFAULT_DECAY_CONFIG,\n): DecayEngine {\n const {\n recencyHalfLifeDays: halfLife,\n recencyWeight: rw,\n frequencyWeight: fw,\n intrinsicWeight: iw,\n staleThreshold,\n searchBoostMin: boostMin,\n importanceModulation: mu,\n betaCore,\n betaWorking,\n betaPeripheral,\n coreDecayFloor,\n workingDecayFloor,\n peripheralDecayFloor,\n } = config;\n\n function getTierBeta(tier: MemoryTier): number {\n switch (tier) {\n case \"core\":\n return betaCore;\n case \"working\":\n return betaWorking;\n case \"peripheral\":\n return betaPeripheral;\n }\n }\n\n function getTierFloor(tier: MemoryTier): number {\n switch (tier) {\n case \"core\":\n return coreDecayFloor;\n case \"working\":\n return workingDecayFloor;\n case \"peripheral\":\n return peripheralDecayFloor;\n }\n }\n\n /**\n * Recency: Weibull stretched-exponential decay with importance + salience modulated half-life.\n * effectiveHL = halfLife * exp(mu * importance) * (1 + salience * 0.5)\n *\n * Salience effect: a memory with salience=1.0 gets 1.5x the half-life.\n * This models the human brain's amygdala tagging: emotionally charged\n * memories decay slower regardless of their \"importance\" category.\n */\n function recency(memory: DecayableMemory, now: number): number {\n const lastActive =\n memory.accessCount > 0 ? memory.lastAccessedAt : memory.createdAt;\n const daysSince = Math.max(0, (now - lastActive) / MS_PER_DAY);\n const salience = memory.emotionalSalience ?? 0.3;\n const effectiveHL = halfLife * Math.exp(mu * memory.importance) * (1 + salience * 0.5);\n const lambda = Math.LN2 / effectiveHL;\n const beta = getTierBeta(memory.tier);\n return Math.exp(-lambda * Math.pow(daysSince, beta));\n }\n\n /**\n * Frequency: logarithmic saturation curve with time-weighted access pattern bonus.\n * base = 1 - exp(-accessCount / 5)\n * For memories with >1 access, a recentness bonus is applied.\n */\n function frequency(memory: DecayableMemory): number {\n const base = 1 - Math.exp(-memory.accessCount / 5);\n if (memory.accessCount <= 1) return base;\n\n const lastActive =\n memory.accessCount > 0 ? memory.lastAccessedAt : memory.createdAt;\n const accessSpanDays = Math.max(\n 1,\n (lastActive - memory.createdAt) / MS_PER_DAY,\n );\n const avgGapDays = accessSpanDays / Math.max(memory.accessCount - 1, 1);\n const recentnessBonus = Math.exp(-avgGapDays / 30);\n return base * (0.5 + 0.5 * recentnessBonus);\n }\n\n /**\n * Intrinsic value: importance \u00D7 confidence \u00D7 (1 + salience * 0.3).\n * Salience gives a mild intrinsic boost \u2014 emotionally significant memories\n * are inherently more valuable even if importance score is moderate.\n */\n function intrinsic(memory: DecayableMemory): number {\n const salience = memory.emotionalSalience ?? 0.3;\n return memory.importance * memory.confidence * (1 + salience * 0.3);\n }\n\n function scoreOne(memory: DecayableMemory, now: number): DecayScore {\n const r = recency(memory, now);\n const f = frequency(memory);\n const i = intrinsic(memory);\n const composite = rw * r + fw * f + iw * i;\n\n return {\n memoryId: memory.id,\n recency: r,\n frequency: f,\n intrinsic: i,\n composite,\n };\n }\n\n return {\n score(memory, now = Date.now()) {\n return scoreOne(memory, now);\n },\n\n scoreAll(memories, now = Date.now()) {\n return memories.map((m) => scoreOne(m, now));\n },\n\n applySearchBoost(results, now = Date.now()) {\n for (const r of results) {\n const ds = scoreOne(r.memory, now);\n const tierFloor = Math.max(getTierFloor(r.memory.tier), ds.composite);\n const multiplier = boostMin + ((1 - boostMin) * tierFloor);\n r.score *= Math.min(1, Math.max(boostMin, multiplier));\n }\n },\n\n getStaleMemories(memories, now = Date.now()) {\n const scores = memories.map((m) => scoreOne(m, now));\n return scores\n .filter((s) => s.composite < staleThreshold)\n .sort((a, b) => a.composite - b.composite);\n },\n };\n}\n"],
5
- "mappings": "AAiBA,MAAM,aAAa;AAgCZ,MAAM,uBAAoC;AAAA,EAC/C,qBAAqB;AAAA,EACrB,eAAe;AAAA;AAAA,EACf,iBAAiB;AAAA;AAAA,EACjB,iBAAiB;AAAA;AAAA,EACjB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,UAAU;AAAA,EACV,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,sBAAsB;AACxB;AA4CO,SAAS,kBACd,SAAsB,sBACT;AACb,QAAM;AAAA,IACJ,qBAAqB;AAAA,IACrB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB;AAAA,IACA,gBAAgB;AAAA,IAChB,sBAAsB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,WAAS,YAAY,MAA0B;AAC7C,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,IACX;AAAA,EACF;AAEA,WAAS,aAAa,MAA0B;AAC9C,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,IACX;AAAA,EACF;AAUA,WAAS,QAAQ,QAAyB,KAAqB;AAC7D,UAAM,aACJ,OAAO,cAAc,IAAI,OAAO,iBAAiB,OAAO;AAC1D,UAAM,YAAY,KAAK,IAAI,IAAI,MAAM,cAAc,UAAU;AAC7D,UAAM,WAAW,OAAO,qBAAqB;AAC7C,UAAM,cAAc,WAAW,KAAK,IAAI,KAAK,OAAO,UAAU,KAAK,IAAI,WAAW;AAClF,UAAM,SAAS,KAAK,MAAM;AAC1B,UAAM,OAAO,YAAY,OAAO,IAAI;AACpC,WAAO,KAAK,IAAI,CAAC,SAAS,KAAK,IAAI,WAAW,IAAI,CAAC;AAAA,EACrD;AAOA,WAAS,UAAU,QAAiC;AAClD,UAAM,OAAO,IAAI,KAAK,IAAI,CAAC,OAAO,cAAc,CAAC;AACjD,QAAI,OAAO,eAAe,EAAG,QAAO;AAEpC,UAAM,aACJ,OAAO,cAAc,IAAI,OAAO,iBAAiB,OAAO;AAC1D,UAAM,iBAAiB,KAAK;AAAA,MAC1B;AAAA,OACC,aAAa,OAAO,aAAa;AAAA,IACpC;AACA,UAAM,aAAa,iBAAiB,KAAK,IAAI,OAAO,cAAc,GAAG,CAAC;AACtE,UAAM,kBAAkB,KAAK,IAAI,CAAC,aAAa,EAAE;AACjD,WAAO,QAAQ,MAAM,MAAM;AAAA,EAC7B;AAOA,WAAS,UAAU,QAAiC;AAClD,UAAM,WAAW,OAAO,qBAAqB;AAC7C,WAAO,OAAO,aAAa,OAAO,cAAc,IAAI,WAAW;AAAA,EACjE;AAEA,WAAS,SAAS,QAAyB,KAAyB;AAClE,UAAM,IAAI,QAAQ,QAAQ,GAAG;AAC7B,UAAM,IAAI,UAAU,MAAM;AAC1B,UAAM,IAAI,UAAU,MAAM;AAC1B,UAAM,YAAY,KAAK,IAAI,KAAK,IAAI,KAAK;AAEzC,WAAO;AAAA,MACL,UAAU,OAAO;AAAA,MACjB,SAAS;AAAA,MACT,WAAW;AAAA,MACX,WAAW;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM,KAAK,IAAI,GAAG;AAC9B,aAAO,SAAS,QAAQ,GAAG;AAAA,IAC7B;AAAA,IAEA,SAAS,UAAU,MAAM,KAAK,IAAI,GAAG;AACnC,aAAO,SAAS,IAAI,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAAA,IAC7C;AAAA,IAEA,iBAAiB,SAAS,MAAM,KAAK,IAAI,GAAG;AAC1C,iBAAW,KAAK,SAAS;AACvB,cAAM,KAAK,SAAS,EAAE,QAAQ,GAAG;AACjC,cAAM,YAAY,KAAK,IAAI,aAAa,EAAE,OAAO,IAAI,GAAG,GAAG,SAAS;AACpE,cAAM,aAAa,YAAa,IAAI,YAAY;AAChD,UAAE,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,UAAU,UAAU,CAAC;AAAA,MACvD;AAAA,IACF;AAAA,IAEA,iBAAiB,UAAU,MAAM,KAAK,IAAI,GAAG;AAC3C,YAAM,SAAS,SAAS,IAAI,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AACnD,aAAO,OACJ,OAAO,CAAC,MAAM,EAAE,YAAY,cAAc,EAC1C,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAAA,IAC7C;AAAA,EACF;AACF;",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"embedder.d.ts","sourceRoot":"","sources":["../../src/embedder.ts"],"names":[],"mappings":"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"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/embedder.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Embedding Abstraction Layer\n * OpenAI-compatible API for various embedding providers.\n * Supports automatic chunking for documents exceeding embedding context limits.\n *\n * Note: Some providers (e.g. Jina) support extra parameters like `task` and\n * `normalized` on the embeddings endpoint. The OpenAI SDK types do not include\n * these fields, so we pass them via a narrow `any` cast.\n */\n\nimport OpenAI from \"openai\";\nimport { createHash } from \"node:crypto\";\nimport { smartChunk } from \"./chunker.js\";\nimport { log } from \"./logger.js\";\n\n// ============================================================================\n// Embedding Cache (LRU with TTL)\n// ============================================================================\n\ninterface CacheEntry {\n vector: number[];\n createdAt: number;\n}\n\nclass EmbeddingCache {\n private cache = new Map<string, CacheEntry>();\n private readonly maxSize: number;\n private readonly ttlMs: number;\n public hits = 0;\n public misses = 0;\n\n constructor(maxSize = 256, ttlMinutes = 30) {\n this.maxSize = maxSize;\n this.ttlMs = ttlMinutes * 60_000;\n }\n\n private key(text: string, task?: string): string {\n const hash = createHash(\"sha256\").update(`${task || \"\"}:${text}`).digest(\"hex\").slice(0, 24);\n return hash;\n }\n\n get(text: string, task?: string): number[] | undefined {\n const k = this.key(text, task);\n const entry = this.cache.get(k);\n if (!entry) {\n this.misses++;\n return undefined;\n }\n if (Date.now() - entry.createdAt > this.ttlMs) {\n this.cache.delete(k);\n this.misses++;\n return undefined;\n }\n // Move to end (most recently used)\n this.cache.delete(k);\n this.cache.set(k, entry);\n this.hits++;\n return entry.vector;\n }\n\n set(text: string, task: string | undefined, vector: number[]): void {\n const k = this.key(text, task);\n // Evict oldest if full\n if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) this.cache.delete(firstKey);\n }\n this.cache.set(k, { vector, createdAt: Date.now() });\n }\n\n get size(): number { return this.cache.size; }\n get stats(): { size: number; hits: number; misses: number; hitRate: string } {\n const total = this.hits + this.misses;\n return {\n size: this.cache.size,\n hits: this.hits,\n misses: this.misses,\n hitRate: total > 0 ? `${((this.hits / total) * 100).toFixed(1)}%` : \"N/A\",\n };\n }\n}\n\n// ============================================================================\n// Types & Configuration\n// ============================================================================\n\nexport interface EmbeddingConfig {\n provider: \"openai-compatible\";\n /** Single API key or array of keys for round-robin rotation with failover. */\n apiKey: string | string[];\n model: string;\n baseURL?: string;\n dimensions?: number;\n\n /** Optional task type for query embeddings (e.g. \"retrieval.query\") */\n taskQuery?: string;\n /** Optional task type for passage/document embeddings (e.g. \"retrieval.passage\") */\n taskPassage?: string;\n /** Optional flag to request normalized embeddings (provider-dependent, e.g. Jina v5) */\n normalized?: boolean;\n /** Enable automatic chunking for documents exceeding context limits (default: true) */\n chunking?: boolean;\n}\n\n// Known embedding model dimensions\nconst EMBEDDING_DIMENSIONS: Record<string, number> = {\n \"text-embedding-3-small\": 1536,\n \"text-embedding-3-large\": 3072,\n \"text-embedding-004\": 768,\n \"gemini-embedding-001\": 3072,\n \"nomic-embed-text\": 768,\n \"mxbai-embed-large\": 1024,\n \"BAAI/bge-m3\": 1024,\n \"all-MiniLM-L6-v2\": 384,\n \"all-mpnet-base-v2\": 512,\n\n // Jina v5\n \"jina-embeddings-v5-text-small\": 1024,\n \"jina-embeddings-v5-text-nano\": 768,\n};\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\nfunction resolveEnvVars(value: string): string {\n return value.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\n const envValue = process.env[envVar];\n if (!envValue) {\n throw new Error(`Environment variable ${envVar} is not set`);\n }\n return envValue;\n });\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction getErrorStatus(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- error introspection requires flexible access\n const err = error as Record<string, any>;\n if (typeof err.status === \"number\") return err.status;\n if (typeof err.statusCode === \"number\") return err.statusCode;\n if (err.error && typeof err.error === \"object\") {\n if (typeof err.error.status === \"number\") return err.error.status;\n if (typeof err.error.statusCode === \"number\") return err.error.statusCode;\n }\n return undefined;\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- error introspection requires flexible access\n const err = error as Record<string, any>;\n if (typeof err.code === \"string\") return err.code;\n if (err.error && typeof err.error === \"object\" && typeof err.error.code === \"string\") {\n return err.error.code;\n }\n return undefined;\n}\n\nfunction getProviderLabel(baseURL: string | undefined, model: string): string {\n const base = baseURL || \"\";\n\n if (base) {\n if (/api\\.jina\\.ai/i.test(base)) return \"Jina\";\n if (/localhost:11434|127\\.0\\.0\\.1:11434|\\/ollama\\b/i.test(base)) return \"Ollama\";\n if (/api\\.openai\\.com/i.test(base)) return \"OpenAI\";\n\n try {\n return new URL(base).host;\n } catch {\n return base;\n }\n }\n\n if (/^jina-/i.test(model)) return \"Jina\";\n\n return \"embedding provider\";\n}\n\nfunction isAuthError(error: unknown): boolean {\n const status = getErrorStatus(error);\n if (status === 401 || status === 403) return true;\n\n const code = getErrorCode(error);\n if (code && /invalid.*key|auth|forbidden|unauthorized/i.test(code)) return true;\n\n const msg = getErrorMessage(error);\n return /\\b401\\b|\\b403\\b|invalid api key|api key expired|expired api key|forbidden|unauthorized|authentication failed|access denied/i.test(msg);\n}\n\nfunction isNetworkError(error: unknown): boolean {\n const code = getErrorCode(error);\n if (code && /ECONNREFUSED|ECONNRESET|ENOTFOUND|EHOSTUNREACH|ETIMEDOUT/i.test(code)) {\n return true;\n }\n\n const msg = getErrorMessage(error);\n return /ECONNREFUSED|ECONNRESET|ENOTFOUND|EHOSTUNREACH|ETIMEDOUT|fetch failed|network error|socket hang up|connection refused|getaddrinfo/i.test(msg);\n}\n\nexport function formatEmbeddingProviderError(\n error: unknown,\n opts: { baseURL?: string; model: string; mode?: \"single\" | \"batch\" },\n): string {\n const raw = getErrorMessage(error).trim();\n if (\n raw.startsWith(\"Embedding provider authentication failed\") ||\n raw.startsWith(\"Embedding provider unreachable\") ||\n raw.startsWith(\"Failed to generate embedding from \") ||\n raw.startsWith(\"Failed to generate batch embeddings from \")\n ) {\n return raw;\n }\n\n const status = getErrorStatus(error);\n const code = getErrorCode(error);\n const provider = getProviderLabel(opts.baseURL, opts.model);\n const detail = raw.length > 0 ? raw : \"unknown error\";\n const suffix = [status, code].filter(Boolean).join(\" \");\n const detailText = suffix ? `${suffix}: ${detail}` : detail;\n const genericPrefix =\n opts.mode === \"batch\"\n ? `Failed to generate batch embeddings from ${provider}: `\n : `Failed to generate embedding from ${provider}: `;\n\n if (isAuthError(error)) {\n let hint = `Check embedding.apiKey and endpoint for ${provider}.`;\n if (provider === \"Jina\") {\n hint +=\n \" If your Jina key expired or lost access, replace the key or switch to a local OpenAI-compatible endpoint such as Ollama (for example baseURL http://127.0.0.1:11434/v1, with a matching model and embedding.dimensions).\";\n } else if (provider === \"Ollama\") {\n hint +=\n \" Ollama usually works with a dummy apiKey; verify the local server is running, the model is pulled, and embedding.dimensions matches the model output.\";\n }\n return `Embedding provider authentication failed (${detailText}). ${hint}`;\n }\n\n if (isNetworkError(error)) {\n let hint = `Verify the endpoint is reachable`;\n if (opts.baseURL) {\n hint += ` at ${opts.baseURL}`;\n }\n hint += ` and that model \\\"${opts.model}\\\" is available.`;\n return `Embedding provider unreachable (${detailText}). ${hint}`;\n }\n\n return `${genericPrefix}${detailText}`;\n}\n\nexport function getVectorDimensions(model: string, overrideDims?: number): number {\n if (overrideDims && overrideDims > 0) {\n return overrideDims;\n }\n\n const dims = EMBEDDING_DIMENSIONS[model];\n if (!dims) {\n throw new Error(\n `Unsupported embedding model: ${model}. Either add it to EMBEDDING_DIMENSIONS or set embedding.dimensions in config.`\n );\n }\n\n return dims;\n}\n\n// ============================================================================\n// Embedder Class\n// ============================================================================\n\nexport class Embedder {\n /** Pool of OpenAI clients \u2014 one per API key for round-robin rotation. */\n private clients: OpenAI[];\n /** Round-robin index for client rotation. */\n private _clientIndex: number = 0;\n\n public readonly dimensions: number;\n private readonly _cache: EmbeddingCache;\n\n private readonly _model: string;\n private readonly _baseURL?: string;\n private readonly _taskQuery?: string;\n private readonly _taskPassage?: string;\n private readonly _normalized?: boolean;\n\n /** Optional requested dimensions to pass through to the embedding provider (OpenAI-compatible). */\n private readonly _requestDimensions?: number;\n /** Enable automatic chunking for long documents (default: true) */\n private readonly _autoChunk: boolean;\n\n constructor(config: EmbeddingConfig & { chunking?: boolean }) {\n // Normalize apiKey to array and resolve environment variables\n const apiKeys = Array.isArray(config.apiKey) ? config.apiKey : [config.apiKey];\n const resolvedKeys = apiKeys.map(k => resolveEnvVars(k));\n\n this._model = config.model;\n this._baseURL = config.baseURL;\n this._taskQuery = config.taskQuery;\n this._taskPassage = config.taskPassage;\n this._normalized = config.normalized;\n this._requestDimensions = config.dimensions;\n // Enable auto-chunking by default for better handling of long documents\n this._autoChunk = config.chunking !== false;\n\n // Create a client pool \u2014 one OpenAI client per key\n this.clients = resolvedKeys.map(key => new OpenAI({\n apiKey: key,\n ...(config.baseURL ? { baseURL: config.baseURL } : {}),\n }));\n\n if (this.clients.length > 1) {\n log.info(`Initialized ${this.clients.length} API keys for round-robin rotation`);\n }\n\n this.dimensions = getVectorDimensions(config.model, config.dimensions);\n this._cache = new EmbeddingCache(256, 30); // 256 entries, 30 min TTL\n }\n\n // --------------------------------------------------------------------------\n // Multi-key rotation helpers\n // --------------------------------------------------------------------------\n\n /** Return the next client in round-robin order. */\n private nextClient(): OpenAI {\n const client = this.clients[this._clientIndex % this.clients.length];\n this._clientIndex = (this._clientIndex + 1) % this.clients.length;\n return client;\n }\n\n /** Check whether an error is a rate-limit / quota-exceeded / overload error. */\n private isRateLimitError(error: unknown): boolean {\n if (!error || typeof error !== \"object\") return false;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- error introspection requires flexible access\n const err = error as Record<string, any>;\n\n // HTTP status: 429 (rate limit) or 503 (service overload)\n if (err.status === 429 || err.status === 503) return true;\n\n // OpenAI SDK structured error code\n if (err.code === \"rate_limit_exceeded\" || err.code === \"insufficient_quota\") return true;\n\n // Nested error object (some providers)\n const nested = err.error;\n if (nested && typeof nested === \"object\") {\n if (nested.type === \"rate_limit_exceeded\" || nested.type === \"insufficient_quota\") return true;\n if (nested.code === \"rate_limit_exceeded\" || nested.code === \"insufficient_quota\") return true;\n }\n\n // Fallback: message text matching\n const msg = error instanceof Error ? error.message : String(error);\n return /rate.limit|quota|too many requests|insufficient.*credit|429|503.*overload/i.test(msg);\n }\n\n /**\n * Call embeddings.create with automatic key rotation on rate-limit errors.\n * Tries each key in the pool at most once before giving up.\n */\n // TODO: type payload as OpenAI.EmbeddingCreateParams & extra provider fields; type return as CreateEmbeddingResponse\n private async embedWithRetry(payload: any): Promise<any> {\n const maxAttempts = this.clients.length;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const client = this.nextClient();\n try {\n return await client.embeddings.create(payload);\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n if (this.isRateLimitError(error) && attempt < maxAttempts - 1) {\n log.info(\n `Attempt ${attempt + 1}/${maxAttempts} hit rate limit, rotating to next key...`\n );\n continue;\n }\n\n // Non-rate-limit error \u2192 don't retry, let caller handle (e.g. chunking)\n if (!this.isRateLimitError(error)) {\n throw error;\n }\n }\n }\n\n // All keys exhausted with rate-limit errors\n throw new Error(\n `All ${maxAttempts} API keys exhausted (rate limited). Last error: ${lastError?.message || \"unknown\"}`,\n { cause: lastError }\n );\n }\n\n /** Number of API keys in the rotation pool. */\n get keyCount(): number {\n return this.clients.length;\n }\n\n // --------------------------------------------------------------------------\n // Backward-compatible API\n // --------------------------------------------------------------------------\n\n /**\n * Backward-compatible embedding API.\n *\n * Historically the plugin used a single `embed()` method for both query and\n * passage embeddings. With task-aware providers we treat this as passage.\n */\n async embed(text: string): Promise<number[]> {\n return this.embedPassage(text);\n }\n\n /** Backward-compatible batch embedding API (treated as passage). */\n async embedBatch(texts: string[]): Promise<number[][]> {\n return this.embedBatchPassage(texts);\n }\n\n // --------------------------------------------------------------------------\n // Task-aware API\n // --------------------------------------------------------------------------\n\n async embedQuery(text: string): Promise<number[]> {\n return this.embedSingle(text, this._taskQuery);\n }\n\n async embedPassage(text: string): Promise<number[]> {\n return this.embedSingle(text, this._taskPassage);\n }\n\n async embedBatchQuery(texts: string[]): Promise<number[][]> {\n return this.embedMany(texts, this._taskQuery);\n }\n\n async embedBatchPassage(texts: string[]): Promise<number[][]> {\n return this.embedMany(texts, this._taskPassage);\n }\n\n // --------------------------------------------------------------------------\n // Internals\n // --------------------------------------------------------------------------\n\n private validateEmbedding(embedding: number[]): void {\n if (!Array.isArray(embedding)) {\n throw new Error(`Embedding is not an array (got ${typeof embedding})`);\n }\n if (embedding.length !== this.dimensions) {\n throw new Error(\n `Embedding dimension mismatch: expected ${this.dimensions}, got ${embedding.length}`\n );\n }\n }\n\n // TODO: type return as OpenAI.EmbeddingCreateParams & provider-specific fields\n private buildPayload(input: string | string[], task?: string): Record<string, unknown> {\n const payload: Record<string, unknown> = {\n model: this.model,\n input,\n };\n\n // Force float output to avoid SDK default base64 decoding path.\n // Skip for providers that reject this field (e.g. Voyage).\n const isVoyage = this._baseURL?.includes(\"voyageai.com\");\n if (!isVoyage) {\n payload.encoding_format = \"float\";\n }\n\n // Voyage uses \"input_type\" instead of \"task\"\n if (task && isVoyage) {\n // Map taskQuery/taskPassage to Voyage input_type\n if (task.includes(\"query\")) payload.input_type = \"query\";\n else if (task.includes(\"passage\") || task.includes(\"document\")) payload.input_type = \"document\";\n else payload.input_type = task;\n } else if (task) {\n payload.task = task;\n }\n if (this._normalized !== undefined) payload.normalized = this._normalized;\n\n // Some OpenAI-compatible providers support requesting a specific vector size.\n // We only pass it through when explicitly configured to avoid breaking providers\n // that reject unknown fields.\n if (this._requestDimensions && this._requestDimensions > 0 && !isVoyage) {\n payload.dimensions = this._requestDimensions;\n }\n\n\n\n return payload;\n }\n\n private async embedSingle(text: string, task?: string): Promise<number[]> {\n if (!text || text.trim().length === 0) {\n throw new Error(\"Cannot embed empty text\");\n }\n\n // Check cache first\n const cached = this._cache.get(text, task);\n if (cached) return cached;\n\n try {\n const response = await this.embedWithRetry(this.buildPayload(text, task));\n const embedding = response.data[0]?.embedding as number[] | undefined;\n if (!embedding) {\n throw new Error(\"No embedding returned from provider\");\n }\n\n this.validateEmbedding(embedding);\n this._cache.set(text, task, embedding);\n return embedding;\n } catch (error) {\n // Check if this is a context length exceeded error and try chunking\n const errorMsg = error instanceof Error ? error.message : String(error);\n const isContextError = /context|too long|exceed|length/i.test(errorMsg);\n\n if (isContextError && this._autoChunk) {\n try {\n log.info(`Document exceeded context limit (${errorMsg}), attempting chunking...`);\n const chunkResult = smartChunk(text, this._model);\n\n if (chunkResult.chunks.length === 0) {\n throw new Error(`Failed to chunk document: ${errorMsg}`);\n }\n\n // Embed all chunks in parallel\n log.info(`Split document into ${chunkResult.chunkCount} chunks for embedding`);\n const chunkEmbeddings = await Promise.all(\n chunkResult.chunks.map(async (chunk, idx) => {\n try {\n const embedding = await this.embedSingle(chunk, task);\n return { embedding };\n } catch (chunkError) {\n log.warn(`Failed to embed chunk ${idx}:`, chunkError);\n throw chunkError;\n }\n })\n );\n\n // Compute average embedding across chunks\n const avgEmbedding = chunkEmbeddings.reduce(\n (sum, { embedding }) => {\n for (let i = 0; i < embedding.length; i++) {\n sum[i] += embedding[i];\n }\n return sum;\n },\n new Array(this.dimensions).fill(0)\n );\n\n const finalEmbedding = avgEmbedding.map(v => v / chunkEmbeddings.length);\n\n // Cache the result for the original text (using its hash)\n this._cache.set(text, task, finalEmbedding);\n log.info(`Successfully embedded long document as ${chunkEmbeddings.length} averaged chunks`);\n\n return finalEmbedding;\n } catch (chunkError) {\n // If chunking fails, throw the original error\n log.warn(`Chunking failed, using original error:`, chunkError);\n const friendly = formatEmbeddingProviderError(error, {\n baseURL: this._baseURL,\n model: this._model,\n mode: \"single\",\n });\n throw new Error(friendly, { cause: error });\n }\n }\n\n const friendly = formatEmbeddingProviderError(error, {\n baseURL: this._baseURL,\n model: this._model,\n mode: \"single\",\n });\n throw new Error(friendly, { cause: error instanceof Error ? error : undefined });\n }\n }\n\n private async embedMany(texts: string[], task?: string): Promise<number[][]> {\n if (!texts || texts.length === 0) {\n return [];\n }\n\n // Filter out empty texts and track indices\n const validTexts: string[] = [];\n const validIndices: number[] = [];\n\n texts.forEach((text, index) => {\n if (text && text.trim().length > 0) {\n validTexts.push(text);\n validIndices.push(index);\n }\n });\n\n if (validTexts.length === 0) {\n return texts.map(() => []);\n }\n\n try {\n const response = await this.embedWithRetry(\n this.buildPayload(validTexts, task)\n );\n\n // Create result array with proper length\n const results: number[][] = new Array(texts.length);\n\n // Fill in embeddings for valid texts\n response.data.forEach((item, idx) => {\n const originalIndex = validIndices[idx];\n const embedding = item.embedding as number[];\n\n this.validateEmbedding(embedding);\n results[originalIndex] = embedding;\n });\n\n // Fill empty arrays for invalid texts\n for (let i = 0; i < texts.length; i++) {\n if (!results[i]) {\n results[i] = [];\n }\n }\n\n return results;\n } catch (error) {\n // Check if this is a context length exceeded error and try chunking each text\n const errorMsg = error instanceof Error ? error.message : String(error);\n const isContextError = /context|too long|exceed|length/i.test(errorMsg);\n\n if (isContextError && this._autoChunk) {\n try {\n log.info(`Batch embedding failed with context error, attempting chunking...`);\n\n const chunkResults = await Promise.all(\n validTexts.map(async (text, idx) => {\n const chunkResult = smartChunk(text, this._model);\n if (chunkResult.chunks.length === 0) {\n throw new Error(\"Chunker produced no chunks\");\n }\n\n // Embed all chunks in parallel, then average.\n const embeddings = await Promise.all(\n chunkResult.chunks.map((chunk) => this.embedSingle(chunk, task))\n );\n\n const avgEmbedding = embeddings.reduce(\n (sum, emb) => {\n for (let i = 0; i < emb.length; i++) {\n sum[i] += emb[i];\n }\n return sum;\n },\n new Array(this.dimensions).fill(0)\n );\n\n const finalEmbedding = avgEmbedding.map((v) => v / embeddings.length);\n\n // Cache the averaged embedding for the original (long) text.\n this._cache.set(text, task, finalEmbedding);\n\n return { embedding: finalEmbedding, index: validIndices[idx] };\n })\n );\n\n log.info(`Successfully chunked and embedded ${chunkResults.length} long documents`);\n\n // Build results array\n const results: number[][] = new Array(texts.length);\n chunkResults.forEach(({ embedding, index }) => {\n if (embedding.length > 0) {\n this.validateEmbedding(embedding);\n results[index] = embedding;\n } else {\n results[index] = [];\n }\n });\n\n // Fill empty arrays for invalid texts\n for (let i = 0; i < texts.length; i++) {\n if (!results[i]) {\n results[i] = [];\n }\n }\n\n return results;\n } catch (chunkError) {\n const friendly = formatEmbeddingProviderError(error, {\n baseURL: this._baseURL,\n model: this._model,\n mode: \"batch\",\n });\n throw new Error(`Failed to embed documents after chunking attempt: ${friendly}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n }\n\n const friendly = formatEmbeddingProviderError(error, {\n baseURL: this._baseURL,\n model: this._model,\n mode: \"batch\",\n });\n throw new Error(friendly, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n }\n\n get model(): string {\n return this._model;\n }\n\n // Test connection and validate configuration\n async test(): Promise<{ success: boolean; error?: string; dimensions?: number }> {\n try {\n const testEmbedding = await this.embedPassage(\"test\");\n return {\n success: true,\n dimensions: testEmbedding.length,\n };\n } catch (error) {\n\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n get cacheStats() {\n return {\n ...this._cache.stats,\n keyCount: this.clients.length,\n };\n }\n}\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\nexport function createEmbedder(config: EmbeddingConfig): Embedder {\n return new Embedder(config);\n}\n"],
5
- "mappings": "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",
6
- "names": ["friendly"]
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"extraction-prompts.d.ts","sourceRoot":"","sources":["../../src/extraction-prompts.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AAEH,wBAAgB,qBAAqB,CACnC,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,MAAM,GACX,MAAM,CAsHR;AAMD,wBAAgB,4BAA4B,CAC1C,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,MAAM,GACX,MAAM,CAoHR;AAED,wBAAgB,gBAAgB,CAC9B,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAmCR;AAED,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CA+BR"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/extraction-prompts.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Prompt templates for intelligent memory extraction.\n * Three mandatory prompts:\n * - buildExtractionPrompt: 6-category L0/L1/L2 extraction with few-shot\n * - buildDedupPrompt: CREATE/MERGE/SKIP dedup decision\n * - buildMergePrompt: Memory merge with three-level structure\n */\n\nexport function buildExtractionPrompt(\n conversationText: string,\n user: string,\n): string {\n return `Analyze the following session context and extract memories worth long-term preservation.\n\nUser: ${user}\n\nTarget Output Language: auto (detect from recent messages)\n\n## Recent Conversation\n${conversationText}\n\n# Memory Extraction Criteria\n\n## What is worth remembering?\n- Personalized information: Information specific to this user, not general domain knowledge\n- Long-term validity: Information that will still be useful in future sessions\n- Specific and clear: Has concrete details, not vague generalizations\n\n## What is NOT worth remembering?\n- General knowledge that anyone would know\n- Temporary information: One-time questions or conversations\n- Vague information: \"User has questions about a feature\" (no specific details)\n- Tool output, error logs, or boilerplate\n- Recall queries / meta-questions: \"Do you remember X?\", \"\u4F60\u8FD8\u8BB0\u5F97X\u5417?\", \"\u4F60\u77E5\u9053\u6211\u559C\u6B22\u4EC0\u4E48\u5417\" \u2014 these are retrieval requests, NOT new information to store\n- Degraded or incomplete references: If the user mentions something vaguely (\"that thing I said\"), do NOT invent details or create a hollow memory\n\n# Memory Classification\n\n## Core Decision Logic\n\n| Question | Answer | Category |\n|----------|--------|----------|\n| Who is the user? | Identity, attributes | profile |\n| What does the user prefer? | Preferences, habits | preferences |\n| What is this thing? | Person, project, organization | entities |\n| What happened? | Decision, milestone | events |\n| How was it solved? | Problem + solution | cases |\n| What is the process? | Reusable steps | patterns |\n\n## Precise Definition\n\n**profile** - User identity (static attributes). Test: \"User is...\"\n**preferences** - User preferences (tendencies). Test: \"User prefers/likes...\"\n**entities** - Continuously existing nouns. Test: \"XXX's state is...\"\n**events** - Things that happened. Test: \"XXX did/completed...\"\n**cases** - Problem + solution pairs. Test: Contains \"problem -> solution\"\n**patterns** - Reusable processes. Test: Can be used in \"similar situations\"\n\n## Common Confusion\n- \"Plan to do X\" -> events (action, not entity)\n- \"Project X status: Y\" -> entities (describes entity)\n- \"User prefers X\" -> preferences (not profile)\n- \"Encountered problem A, used solution B\" -> cases (not events)\n- \"General process for handling certain problems\" -> patterns (not cases)\n\n# Three-Level Structure\n\nEach memory contains three levels:\n\n**abstract (L0)**: One-liner index\n- Merge types (preferences/entities/profile/patterns): \\`[Merge key]: [Description]\\`\n- Independent types (events/cases): Specific description\n\n**overview (L1)**: Structured Markdown summary with category-specific headings\n\n**content (L2)**: Full narrative with background and details\n\n# Few-shot Examples\n\n## profile\n\\`\\`\\`json\n{\n \"category\": \"profile\",\n \"abstract\": \"User basic info: AI development engineer, 3 years LLM experience\",\n \"overview\": \"## Background\\\\n- Occupation: AI development engineer\\\\n- Experience: 3 years LLM development\\\\n- Tech stack: Python, LangChain\",\n \"content\": \"User is an AI development engineer with 3 years of LLM application development experience.\"\n}\n\\`\\`\\`\n\n## preferences\n\\`\\`\\`json\n{\n \"category\": \"preferences\",\n \"abstract\": \"Python code style: No type hints, concise and direct\",\n \"overview\": \"## Preference Domain\\\\n- Language: Python\\\\n- Topic: Code style\\\\n\\\\n## Details\\\\n- No type hints\\\\n- Concise function comments\\\\n- Direct implementation\",\n \"content\": \"User prefers Python code without type hints, with concise function comments.\"\n}\n\\`\\`\\`\n\n## cases\n\\`\\`\\`json\n{\n \"category\": \"cases\",\n \"abstract\": \"LanceDB BigInt error -> Use Number() coercion before arithmetic\",\n \"overview\": \"## Problem\\\\nLanceDB 0.26+ returns BigInt for numeric columns\\\\n\\\\n## Solution\\\\nCoerce values with Number(...) before arithmetic\",\n \"content\": \"When LanceDB returns BigInt values, wrap them with Number() before doing arithmetic operations.\"\n}\n\\`\\`\\`\n\n# Output Format\n\nReturn JSON:\n{\n \"memories\": [\n {\n \"category\": \"profile|preferences|entities|events|cases|patterns\",\n \"abstract\": \"One-line index\",\n \"overview\": \"Structured Markdown summary\",\n \"content\": \"Full narrative\"\n }\n ]\n}\n\nNotes:\n- Output language should match the dominant language in the conversation\n- Only extract truly valuable personalized information\n- If nothing worth recording, return {\"memories\": []}\n- Maximum 5 memories per extraction\n- Preferences should be aggregated by topic`;\n}\n\n// ============================================================================\n// Chinese Extraction Prompt\n// ============================================================================\n\nexport function buildChineseExtractionPrompt(\n conversationText: string,\n user: string,\n): string {\n return `\u5206\u6790\u4EE5\u4E0B\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u63D0\u53D6\u503C\u5F97\u957F\u671F\u4FDD\u5B58\u7684\u8BB0\u5FC6\u3002\n\n\u7528\u6237: ${user}\n\n## \u6700\u8FD1\u5BF9\u8BDD\n${conversationText}\n\n# \u8BB0\u5FC6\u63D0\u53D6\u6807\u51C6\n\n## \u4EC0\u4E48\u503C\u5F97\u8BB0\u4F4F\uFF1F\n- \u4E2A\u6027\u5316\u4FE1\u606F\uFF1A\u4E13\u5C5E\u4E8E\u8BE5\u7528\u6237\u7684\u4FE1\u606F\uFF0C\u800C\u975E\u901A\u7528\u9886\u57DF\u77E5\u8BC6\n- \u957F\u671F\u6709\u6548\u6027\uFF1A\u5728\u672A\u6765\u4F1A\u8BDD\u4E2D\u4ECD\u7136\u6709\u7528\u7684\u4FE1\u606F\n- \u5177\u4F53\u660E\u786E\uFF1A\u6709\u5177\u4F53\u7EC6\u8282\uFF0C\u800C\u975E\u6A21\u7CCA\u7684\u6982\u62EC\n\n## \u4EC0\u4E48\u4E0D\u503C\u5F97\u8BB0\u4F4F\uFF1F\n- \u4EFB\u4F55\u4EBA\u90FD\u77E5\u9053\u7684\u5E38\u8BC6\n- \u4E34\u65F6\u4FE1\u606F\uFF1A\u4E00\u6B21\u6027\u7684\u63D0\u95EE\u6216\u5BF9\u8BDD\n- \u6A21\u7CCA\u4FE1\u606F\uFF1A\"\u7528\u6237\u5BF9\u67D0\u529F\u80FD\u6709\u7591\u95EE\"\uFF08\u6CA1\u6709\u5177\u4F53\u7EC6\u8282\uFF09\n- \u5DE5\u5177\u8F93\u51FA\u3001\u9519\u8BEF\u65E5\u5FD7\u6216\u6A21\u677F\u6587\u5B57\n- \u56DE\u5FC6\u67E5\u8BE2/\u5143\u95EE\u9898\uFF1A\"\u4F60\u8FD8\u8BB0\u5F97X\u5417\uFF1F\"\u3001\"\u597D\u7684\"\u3001\"\u6536\u5230\"\u3001\"\u55EF\u55EF\"\u2014\u2014\u8FD9\u4E9B\u662F\u68C0\u7D22\u8BF7\u6C42\u6216\u5E94\u7B54\u788E\u7247\uFF0C\u4E0D\u662F\u65B0\u4FE1\u606F\n- \u964D\u7EA7\u6216\u4E0D\u5B8C\u6574\u5F15\u7528\uFF1A\u5982\u679C\u7528\u6237\u6A21\u7CCA\u63D0\u53CA\u67D0\u4E8B\uFF08\"\u4E4B\u524D\u8BF4\u7684\u90A3\u4E2A\"\uFF09\uFF0C\u4E0D\u8981\u7F16\u9020\u7EC6\u8282\u6216\u521B\u5EFA\u7A7A\u6D1E\u8BB0\u5FC6\n\n# \u8BB0\u5FC6\u5206\u7C7B\n\n## \u6838\u5FC3\u5224\u65AD\u903B\u8F91\n\n| \u95EE\u9898 | \u7B54\u6848 | \u5206\u7C7B |\n|------|------|------|\n| \u7528\u6237\u662F\u8C01\uFF1F | \u8EAB\u4EFD\u3001\u5C5E\u6027 | profile |\n| \u7528\u6237\u504F\u597D\u4EC0\u4E48\uFF1F | \u504F\u597D\u3001\u4E60\u60EF | preferences |\n| \u8FD9\u4E2A\u4E1C\u897F\u662F\u4EC0\u4E48\uFF1F | \u4EBA\u7269\u3001\u9879\u76EE\u3001\u7EC4\u7EC7 | entities |\n| \u53D1\u751F\u4E86\u4EC0\u4E48\uFF1F | \u51B3\u7B56\u3001\u91CC\u7A0B\u7891 | events |\n| \u600E\u4E48\u89E3\u51B3\u7684\uFF1F | \u95EE\u9898+\u65B9\u6848 | cases |\n| \u6D41\u7A0B\u662F\u4EC0\u4E48\uFF1F | \u53EF\u590D\u7528\u6B65\u9AA4 | patterns |\n\n## \u7CBE\u786E\u5B9A\u4E49\n\n**profile** - \u7528\u6237\u8EAB\u4EFD\uFF08\u9759\u6001\u5C5E\u6027\uFF09\u3002\u5224\u65AD\u6807\u51C6\uFF1A\"\u7528\u6237\u662F...\"\n**preferences** - \u7528\u6237\u504F\u597D\uFF08\u503E\u5411\u6027\uFF09\u3002\u5224\u65AD\u6807\u51C6\uFF1A\"\u7528\u6237\u504F\u597D/\u559C\u6B22...\"\n**entities** - \u6301\u7EED\u5B58\u5728\u7684\u540D\u8BCD\u5B9E\u4F53\u3002\u5224\u65AD\u6807\u51C6\uFF1A\"XXX\u7684\u72B6\u6001\u662F...\"\n**events** - \u53D1\u751F\u8FC7\u7684\u4E8B\u4EF6\u3002\u5224\u65AD\u6807\u51C6\uFF1A\"XXX\u505A\u4E86/\u5B8C\u6210\u4E86...\"\n**cases** - \u95EE\u9898+\u89E3\u51B3\u65B9\u6848\u5BF9\u3002\u5224\u65AD\u6807\u51C6\uFF1A\u5305\u542B\"\u95EE\u9898->\u65B9\u6848\"\n**patterns** - \u53EF\u590D\u7528\u6D41\u7A0B\u3002\u5224\u65AD\u6807\u51C6\uFF1A\u53EF\u7528\u4E8E\"\u7C7B\u4F3C\u573A\u666F\"\n\n## \u5E38\u89C1\u6DF7\u6DC6\n- \"\u8BA1\u5212\u505AX\" -> events\uFF08\u884C\u52A8\uFF0C\u975E\u5B9E\u4F53\uFF09\n- \"\u9879\u76EEX\u7684\u72B6\u6001\uFF1AY\" -> entities\uFF08\u63CF\u8FF0\u5B9E\u4F53\uFF09\n- \"\u7528\u6237\u504F\u597DX\" -> preferences\uFF08\u4E0D\u662F profile\uFF09\n- \"\u9047\u5230\u95EE\u9898A\uFF0C\u7528\u4E86\u65B9\u6848B\" -> cases\uFF08\u4E0D\u662F events\uFF09\n- \"\u5904\u7406\u67D0\u7C7B\u95EE\u9898\u7684\u901A\u7528\u6D41\u7A0B\" -> patterns\uFF08\u4E0D\u662F cases\uFF09\n\n# \u4E09\u7EA7\u7ED3\u6784\n\n\u6BCF\u6761\u8BB0\u5FC6\u5305\u542B\u4E09\u4E2A\u5C42\u7EA7\uFF1A\n\n**abstract (L0)**\uFF1A\u4E00\u884C\u7D22\u5F15\n- \u5408\u5E76\u7C7B\u578B\uFF08preferences/entities/profile/patterns\uFF09\uFF1A\\`[\u5408\u5E76\u952E]: [\u63CF\u8FF0]\\`\n- \u72EC\u7ACB\u7C7B\u578B\uFF08events/cases\uFF09\uFF1A\u5177\u4F53\u63CF\u8FF0\n\n**overview (L1)**\uFF1A\u7ED3\u6784\u5316 Markdown \u6458\u8981\uFF0C\u4F7F\u7528\u5206\u7C7B\u7279\u5B9A\u6807\u9898\n\n**content (L2)**\uFF1A\u5305\u542B\u80CC\u666F\u548C\u7EC6\u8282\u7684\u5B8C\u6574\u53D9\u8FF0\n\n# \u5C11\u6837\u672C\u793A\u4F8B\n\n## profile\n\\`\\`\\`json\n{\n \"category\": \"profile\",\n \"abstract\": \"\u7528\u6237\u57FA\u672C\u4FE1\u606F\uFF1AAI\u5F00\u53D1\u5DE5\u7A0B\u5E08\uFF0C3\u5E74LLM\u7ECF\u9A8C\",\n \"overview\": \"## \u80CC\u666F\\\\n- \u804C\u4E1A\uFF1AAI\u5F00\u53D1\u5DE5\u7A0B\u5E08\\\\n- \u7ECF\u9A8C\uFF1A3\u5E74LLM\u5E94\u7528\u5F00\u53D1\\\\n- \u6280\u672F\u6808\uFF1APython, LangChain\",\n \"content\": \"\u7528\u6237\u662F\u4E00\u540DAI\u5F00\u53D1\u5DE5\u7A0B\u5E08\uFF0C\u67093\u5E74LLM\u5E94\u7528\u5F00\u53D1\u7ECF\u9A8C\u3002\"\n}\n\\`\\`\\`\n\n## preferences\n\\`\\`\\`json\n{\n \"category\": \"preferences\",\n \"abstract\": \"Python\u4EE3\u7801\u98CE\u683C\uFF1A\u4E0D\u52A0\u7C7B\u578B\u6CE8\u89E3\uFF0C\u7B80\u6D01\u76F4\u63A5\",\n \"overview\": \"## \u504F\u597D\u9886\u57DF\\\\n- \u8BED\u8A00\uFF1APython\\\\n- \u4E3B\u9898\uFF1A\u4EE3\u7801\u98CE\u683C\\\\n\\\\n## \u8BE6\u60C5\\\\n- \u4E0D\u52A0\u7C7B\u578B\u6CE8\u89E3\\\\n- \u7B80\u6D01\u7684\u51FD\u6570\u6CE8\u91CA\\\\n- \u76F4\u63A5\u5B9E\u73B0\",\n \"content\": \"\u7528\u6237\u504F\u597D\u4E0D\u5E26\u7C7B\u578B\u6CE8\u89E3\u7684Python\u4EE3\u7801\uFF0C\u51FD\u6570\u6CE8\u91CA\u8981\u7B80\u6D01\u3002\"\n}\n\\`\\`\\`\n\n## cases\n\\`\\`\\`json\n{\n \"category\": \"cases\",\n \"abstract\": \"LanceDB BigInt\u9519\u8BEF -> \u5728\u8FD0\u7B97\u524D\u4F7F\u7528Number()\u8F6C\u6362\",\n \"overview\": \"## \u95EE\u9898\\\\nLanceDB 0.26+\u5BF9\u6570\u503C\u5217\u8FD4\u56DEBigInt\\\\n\\\\n## \u65B9\u6848\\\\n\u5728\u8FD0\u7B97\u524D\u7528Number(...)\u8F6C\u6362\",\n \"content\": \"\u5F53LanceDB\u8FD4\u56DEBigInt\u503C\u65F6\uFF0C\u5728\u505A\u7B97\u672F\u8FD0\u7B97\u524D\u7528Number()\u5305\u88F9\u3002\"\n}\n\\`\\`\\`\n\n# \u8F93\u51FA\u683C\u5F0F\n\n\u8FD4\u56DE JSON:\n{\n \"memories\": [\n {\n \"category\": \"profile|preferences|entities|events|cases|patterns\",\n \"abstract\": \"\u4E00\u884C\u7D22\u5F15\",\n \"overview\": \"\u7ED3\u6784\u5316Markdown\u6458\u8981\",\n \"content\": \"\u5B8C\u6574\u53D9\u8FF0\"\n }\n ]\n}\n\n\u6CE8\u610F\uFF1A\n- \u8F93\u51FA\u8BED\u8A00\u5E94\u4E0E\u5BF9\u8BDD\u4E2D\u7684\u4E3B\u8981\u8BED\u8A00\u4E00\u81F4\n- \u53EA\u63D0\u53D6\u771F\u6B63\u6709\u4EF7\u503C\u7684\u4E2A\u6027\u5316\u4FE1\u606F\n- \u5982\u679C\u6CA1\u6709\u503C\u5F97\u8BB0\u5F55\u7684\u5185\u5BB9\uFF0C\u8FD4\u56DE {\"memories\": []}\n- \u6BCF\u6B21\u63D0\u53D6\u6700\u591A5\u6761\u8BB0\u5FC6\n- \u504F\u597D\u5E94\u6309\u4E3B\u9898\u805A\u5408`;\n}\n\nexport function buildDedupPrompt(\n candidateAbstract: string,\n candidateOverview: string,\n candidateContent: string,\n existingMemories: string,\n): string {\n return `Determine how to handle this candidate memory.\n\n**Candidate Memory**:\nAbstract: ${candidateAbstract}\nOverview: ${candidateOverview}\nContent: ${candidateContent}\n\n**Existing Similar Memories**:\n${existingMemories}\n\nPlease decide:\n- SKIP: Candidate memory duplicates existing memories, no need to save. Also SKIP if the candidate contains LESS information than an existing memory on the same topic (information degradation \u2014 e.g., candidate says \"programming language preference\" but existing memory already says \"programming language preference: Python, TypeScript\")\n- CREATE: This is completely new information not covered by any existing memory, should be created\n- MERGE: Candidate memory adds genuinely NEW details to an existing memory and should be merged\n- SUPPORT: Candidate reinforces/confirms an existing memory in a specific context (e.g. \"still prefers tea in the evening\")\n- CONTEXTUALIZE: Candidate adds a situational nuance to an existing memory (e.g. existing: \"likes coffee\", candidate: \"prefers tea at night\" \u2014 different context, same topic)\n- CONTRADICT: Candidate directly contradicts an existing memory in a specific context (e.g. existing: \"runs on weekends\", candidate: \"stopped running on weekends\")\n\nIMPORTANT:\n- \"events\" and \"cases\" categories are independent records \u2014 they do NOT support MERGE/SUPPORT/CONTEXTUALIZE/CONTRADICT. For these categories, only use SKIP or CREATE.\n- If the candidate appears to be derived from a recall question (e.g., \"Do you remember X?\" / \"\u4F60\u8BB0\u5F97X\u5417\uFF1F\") and an existing memory already covers topic X with equal or more detail, you MUST choose SKIP.\n- A candidate with less information than an existing memory on the same topic should NEVER be CREATED or MERGED \u2014 always SKIP.\n- For SUPPORT/CONTEXTUALIZE/CONTRADICT, you MUST provide a context_label from this vocabulary: general, morning, evening, night, weekday, weekend, work, leisure, summer, winter, travel.\n\nReturn JSON format:\n{\n \"decision\": \"skip|create|merge|support|contextualize|contradict\",\n \"match_index\": 1,\n \"reason\": \"Decision reason\",\n \"context_label\": \"evening\"\n}\n\n- If decision is \"merge\"/\"support\"/\"contextualize\"/\"contradict\", set \"match_index\" to the number of the existing memory (1-based).\n- Only include \"context_label\" for support/contextualize/contradict decisions.`;\n}\n\nexport function buildMergePrompt(\n existingAbstract: string,\n existingOverview: string,\n existingContent: string,\n newAbstract: string,\n newOverview: string,\n newContent: string,\n category: string,\n): string {\n return `Merge the following memory into a single coherent record with all three levels.\n\n** Category **: ${category}\n\n** Existing Memory:**\n Abstract: ${existingAbstract}\n Overview:\n${existingOverview}\n Content:\n${existingContent}\n\n** New Information:**\n Abstract: ${newAbstract}\n Overview:\n${newOverview}\n Content:\n${newContent}\n\n Requirements:\n - Remove duplicate information\n - Keep the most up - to - date details\n - Maintain a coherent narrative\n - Keep code identifiers / URIs / model names unchanged when they are proper nouns\n\nReturn JSON:\n {\n \"abstract\": \"Merged one-line abstract\",\n \"overview\": \"Merged structured Markdown overview\",\n \"content\": \"Merged full content\"\n } `;\n}\n"],
5
- "mappings": "AASO,SAAS,sBACd,kBACA,MACQ;AACR,SAAO;AAAA;AAAA,QAED,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAKV,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8GlB;AAMO,SAAS,6BACd,kBACA,MACQ;AACR,SAAO;AAAA;AAAA,gBAEH,IAAI;AAAA;AAAA;AAAA,EAGR,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8GlB;AAEO,SAAS,iBACd,mBACA,mBACA,kBACA,kBACQ;AACR,SAAO;AAAA;AAAA;AAAA,YAGG,iBAAiB;AAAA,YACjB,iBAAiB;AAAA,WAClB,gBAAgB;AAAA;AAAA;AAAA,EAGzB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BlB;AAEO,SAAS,iBACd,kBACA,kBACA,iBACA,aACA,aACA,YACA,UACQ;AACR,SAAO;AAAA;AAAA,kBAES,QAAQ;AAAA;AAAA;AAAA,gBAGV,gBAAgB;AAAA;AAAA,EAE9B,gBAAgB;AAAA;AAAA,EAEhB,eAAe;AAAA;AAAA;AAAA,gBAGD,WAAW;AAAA;AAAA,EAEzB,WAAW;AAAA;AAAA,EAEX,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcZ;",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../../src/license.ts"],"names":[],"mappings":"AACA;;;;;;;;;GASG;AAsBH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C;AA6FD,wBAAgB,aAAa,IAAI,OAAO,CA2CvC;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CA0CzD;AAED,wBAAgB,cAAc,IAAI,cAAc,GAAG,IAAI,CAGtD;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAcvD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/license.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Mnemo Pro License Validation\n *\n * Two modes:\n * 1. MNEMO_PRO_KEY \u2014 pre-activated key (offline, machine-bound)\n * 2. MNEMO_LICENSE_TOKEN \u2014 auto-activate on first run (online, one-time)\n *\n * Machine fingerprint: SHA-256(hostname + arch + cpuModel + platform)\n * Indie keys are bound to one machine. Team/Enterprise keys are per-seat.\n */\n\nimport { verify, createPublicKey, createHash } from \"node:crypto\";\nimport { hostname, arch, cpus, platform } from \"node:os\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { log } from \"./logger.js\";\n\n// Ed25519 public key (DER/SPKI, base64) \u2014 safe to publish\nconst PUBLIC_KEY_B64 =\n \"MCowBQYDK2VwAyEAe8cshR0FAlDoILPw0aW1AyUNGbQXSOZaQKEZ7T2mXV8=\";\n\nconst ACTIVATION_URL =\n process.env.MNEMO_ACTIVATION_URL || \"https://activation.m-nemo.ai\";\n\nconst KEY_CACHE_PATH = join(homedir(), \".mnemo\", \"pro-key.json\");\n\nlet _cachedResult: boolean | null = null;\nlet _cachedPayload: LicensePayload | null = null;\nlet _warnedOnce = false;\n\nexport interface LicensePayload {\n licensee: string;\n email: string;\n plan: \"indie\" | \"team\" | \"enterprise\";\n issued: string;\n expires: string;\n machine_id?: string;\n}\n\n// \u2500\u2500 Machine Fingerprint \u2500\u2500\n\nexport function getMachineFingerprint(): string {\n const cpu = cpus()[0]?.model || \"unknown\";\n const raw = `${hostname()}:${arch()}:${cpu}:${platform()}`;\n return createHash(\"sha256\").update(raw).digest(\"hex\");\n}\n\n// \u2500\u2500 Key Verification (offline) \u2500\u2500\n\nfunction verifyKey(key: string): LicensePayload | null {\n const dotIdx = key.indexOf(\".\");\n if (dotIdx < 1) return null;\n\n try {\n const payloadBuf = Buffer.from(key.slice(0, dotIdx), \"base64\");\n const signatureBuf = Buffer.from(key.slice(dotIdx + 1), \"base64\");\n\n const pubKeyObj = createPublicKey({\n key: Buffer.from(PUBLIC_KEY_B64, \"base64\"),\n format: \"der\",\n type: \"spki\",\n });\n\n const valid = verify(null, payloadBuf, pubKeyObj, signatureBuf);\n if (!valid) return null;\n\n const payload: LicensePayload = JSON.parse(payloadBuf.toString(\"utf8\"));\n\n // Check expiry\n if (payload.expires) {\n if (new Date(payload.expires).getTime() < Date.now()) return null;\n }\n\n // Check machine binding (if present in payload)\n if (payload.machine_id) {\n const localFP = getMachineFingerprint();\n if (payload.machine_id !== localFP) return null;\n }\n\n return payload;\n } catch {\n return null;\n }\n}\n\n// \u2500\u2500 Auto-Activation (online, one-time) \u2500\u2500\n\nasync function autoActivate(token: string): Promise<string | null> {\n try {\n const machine_id = getMachineFingerprint();\n const resp = await fetch(`${ACTIVATION_URL}/activate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token, machine_id }),\n signal: AbortSignal.timeout(10000),\n });\n\n if (!resp.ok) {\n const err = await resp.json().catch(() => ({})) as any;\n if (resp.status === 409) {\n log.warn(\n `License token already activated on another device. ` +\n `Visit https://m-nemo.ai/pro/migrate to transfer.`\n );\n } else {\n log.warn(`Activation failed: ${err.error || resp.status}`);\n }\n return null;\n }\n\n const { key } = await resp.json() as { key: string };\n\n // Cache the activated key locally\n try {\n mkdirSync(join(homedir(), \".mnemo\"), { recursive: true });\n writeFileSync(KEY_CACHE_PATH, JSON.stringify({ key, token, activated: new Date().toISOString() }));\n } catch { /* non-fatal */ }\n\n return key;\n } catch (err) {\n log.warn(`Activation request failed (offline?): ${err}`);\n return null;\n }\n}\n\n// \u2500\u2500 Load cached key from disk \u2500\u2500\n\nfunction loadCachedKey(): string | null {\n try {\n const data = JSON.parse(readFileSync(KEY_CACHE_PATH, \"utf8\"));\n return data.key || null;\n } catch {\n return null;\n }\n}\n\n// \u2500\u2500 Main entry point \u2500\u2500\n\nexport function isProLicensed(): boolean {\n if (_cachedResult !== null) return _cachedResult;\n\n // Priority 1: explicit MNEMO_PRO_KEY env var\n const explicitKey = process.env.MNEMO_PRO_KEY?.trim();\n if (explicitKey) {\n const payload = verifyKey(explicitKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Priority 2: cached key from previous activation\n const cachedKey = loadCachedKey();\n if (cachedKey) {\n const payload = verifyKey(cachedKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Priority 3: auto-activate with token (async \u2014 won't block first call)\n const token = process.env.MNEMO_LICENSE_TOKEN?.trim();\n if (token) {\n // Fire and forget \u2014 next process start will pick up cached key\n autoActivate(token).then((key) => {\n if (key) {\n const payload = verifyKey(key);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n log.info(`Pro license activated for ${payload.licensee} (${payload.plan})`);\n }\n }\n }).catch(() => {});\n }\n\n _cachedResult = false;\n return false;\n}\n\n/**\n * Async version \u2014 waits for activation to complete if token is present.\n * Use this during plugin initialization.\n */\nexport async function ensureProLicense(): Promise<boolean> {\n if (_cachedResult !== null) return _cachedResult;\n\n // Check explicit key\n const explicitKey = process.env.MNEMO_PRO_KEY?.trim();\n if (explicitKey) {\n const payload = verifyKey(explicitKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Check cached key\n const cachedKey = loadCachedKey();\n if (cachedKey) {\n const payload = verifyKey(cachedKey);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n return true;\n }\n }\n\n // Try auto-activate with token\n const token = process.env.MNEMO_LICENSE_TOKEN?.trim();\n if (token) {\n const key = await autoActivate(token);\n if (key) {\n const payload = verifyKey(key);\n if (payload) {\n _cachedPayload = payload;\n _cachedResult = true;\n log.info(`Pro license activated for ${payload.licensee} (${payload.plan})`);\n return true;\n }\n }\n }\n\n _cachedResult = false;\n return false;\n}\n\nexport function getLicenseInfo(): LicensePayload | null {\n isProLicensed();\n return _cachedPayload;\n}\n\nexport function requirePro(featureName: string): boolean {\n if (isProLicensed()) return true;\n\n if (!_warnedOnce) {\n log.warn(\n `Pro features disabled \u2014 set MNEMO_PRO_KEY or MNEMO_LICENSE_TOKEN to enable. ` +\n `Core functionality is fully available. https://m-nemo.ai/pro`,\n );\n _warnedOnce = true;\n }\n if (process.env.MNEMO_DEBUG) {\n log.debug(`Pro feature skipped: ${featureName}`);\n }\n return false;\n}\n\nexport function _resetLicenseCache(): void {\n _cachedResult = null;\n _cachedPayload = null;\n _warnedOnce = false;\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/llm-client.ts"],"names":[],"mappings":"AACA;;;GAGG;AAIH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACpE;AAED;;;GAGG;AACH,iBAAS,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B5D;AAQD,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA+DlE;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/llm-client.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * LLM Client for memory extraction and dedup decisions.\n * Uses OpenAI-compatible API (reuses the embedding provider config).\n */\n\nimport OpenAI from \"openai\";\n\nexport interface LlmClientConfig {\n apiKey: string;\n model: string;\n baseURL?: string;\n timeoutMs?: number;\n log?: (msg: string) => void;\n}\n\nexport interface LlmClient {\n /** Send a prompt and parse the JSON response. Returns null on failure. */\n completeJson<T>(prompt: string, label?: string): Promise<T | null>;\n}\n\n/**\n * Extract JSON from an LLM response that may be wrapped in markdown fences\n * or contain surrounding text.\n */\nfunction extractJsonFromResponse(text: string): string | null {\n // Try markdown code fence first (```json ... ``` or ``` ... ```)\n const fenceMatch = text.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)```/);\n if (fenceMatch) {\n return fenceMatch[1].trim();\n }\n\n // Try balanced brace extraction\n const firstBrace = text.indexOf(\"{\");\n if (firstBrace === -1) return null;\n\n let depth = 0;\n let lastBrace = -1;\n for (let i = firstBrace; i < text.length; i++) {\n if (text[i] === \"{\") depth++;\n else if (text[i] === \"}\") {\n depth--;\n if (depth === 0) {\n lastBrace = i;\n break;\n }\n }\n }\n\n if (lastBrace === -1) return null;\n return text.substring(firstBrace, lastBrace + 1);\n}\n\nfunction previewText(value: string, maxLen = 200): string {\n const normalized = value.replace(/\\s+/g, \" \").trim();\n if (normalized.length <= maxLen) return normalized;\n return `${normalized.slice(0, maxLen - 3)}...`;\n}\n\nexport function createLlmClient(config: LlmClientConfig): LlmClient {\n const client = new OpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n timeout: config.timeoutMs ?? 30000,\n });\n const log = config.log ?? (() => {});\n\n return {\n async completeJson<T>(prompt: string, label = \"generic\"): Promise<T | null> {\n try {\n const response = await client.chat.completions.create({\n model: config.model,\n messages: [\n {\n role: \"system\",\n content:\n \"You are a memory extraction assistant. Always respond with valid JSON only.\",\n },\n { role: \"user\", content: prompt },\n ],\n temperature: 0.1,\n });\n\n const raw = response.choices?.[0]?.message?.content;\n if (!raw) {\n log(\n `mnemo: llm-client [${label}] empty response content from model ${config.model}`,\n );\n return null;\n }\n if (typeof raw !== \"string\") {\n log(\n `mnemo: llm-client [${label}] non-string response content type=${Array.isArray(raw) ? \"array\" : typeof raw} from model ${config.model}`,\n );\n return null;\n }\n\n const jsonStr = extractJsonFromResponse(raw);\n if (!jsonStr) {\n log(\n `mnemo: llm-client [${label}] no JSON object found (chars=${raw.length}, preview=${JSON.stringify(previewText(raw))})`,\n );\n return null;\n }\n\n try {\n return JSON.parse(jsonStr) as T;\n } catch (err) {\n log(\n `mnemo: llm-client [${label}] JSON.parse failed: ${err instanceof Error ? err.message : String(err)} (jsonChars=${jsonStr.length}, jsonPreview=${JSON.stringify(previewText(jsonStr))})`,\n );\n return null;\n }\n } catch (err) {\n // Graceful degradation \u2014 return null so caller can fall back\n log(\n `mnemo: llm-client [${label}] request failed for model ${config.model}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return null;\n }\n },\n };\n}\n\nexport { extractJsonFromResponse };\n"],
5
- "mappings": "AAMA,OAAO,YAAY;AAmBnB,SAAS,wBAAwB,MAA6B;AAE5D,QAAM,aAAa,KAAK,MAAM,iCAAiC;AAC/D,MAAI,YAAY;AACd,WAAO,WAAW,CAAC,EAAE,KAAK;AAAA,EAC5B;AAGA,QAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,MAAI,eAAe,GAAI,QAAO;AAE9B,MAAI,QAAQ;AACZ,MAAI,YAAY;AAChB,WAAS,IAAI,YAAY,IAAI,KAAK,QAAQ,KAAK;AAC7C,QAAI,KAAK,CAAC,MAAM,IAAK;AAAA,aACZ,KAAK,CAAC,MAAM,KAAK;AACxB;AACA,UAAI,UAAU,GAAG;AACf,oBAAY;AACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,GAAI,QAAO;AAC7B,SAAO,KAAK,UAAU,YAAY,YAAY,CAAC;AACjD;AAEA,SAAS,YAAY,OAAe,SAAS,KAAa;AACxD,QAAM,aAAa,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACnD,MAAI,WAAW,UAAU,OAAQ,QAAO;AACxC,SAAO,GAAG,WAAW,MAAM,GAAG,SAAS,CAAC,CAAC;AAC3C;AAEO,SAAS,gBAAgB,QAAoC;AAClE,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO,aAAa;AAAA,EAC/B,CAAC;AACD,QAAM,MAAM,OAAO,QAAQ,MAAM;AAAA,EAAC;AAElC,SAAO;AAAA,IACL,MAAM,aAAgB,QAAgB,QAAQ,WAA8B;AAC1E,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,UACpD,OAAO,OAAO;AAAA,UACd,UAAU;AAAA,YACR;AAAA,cACE,MAAM;AAAA,cACN,SACE;AAAA,YACJ;AAAA,YACA,EAAE,MAAM,QAAQ,SAAS,OAAO;AAAA,UAClC;AAAA,UACA,aAAa;AAAA,QACf,CAAC;AAED,cAAM,MAAM,SAAS,UAAU,CAAC,GAAG,SAAS;AAC5C,YAAI,CAAC,KAAK;AACR;AAAA,YACE,sBAAsB,KAAK,uCAAuC,OAAO,KAAK;AAAA,UAChF;AACA,iBAAO;AAAA,QACT;AACA,YAAI,OAAO,QAAQ,UAAU;AAC3B;AAAA,YACE,sBAAsB,KAAK,sCAAsC,MAAM,QAAQ,GAAG,IAAI,UAAU,OAAO,GAAG,eAAe,OAAO,KAAK;AAAA,UACvI;AACA,iBAAO;AAAA,QACT;AAEA,cAAM,UAAU,wBAAwB,GAAG;AAC3C,YAAI,CAAC,SAAS;AACZ;AAAA,YACE,sBAAsB,KAAK,iCAAiC,IAAI,MAAM,aAAa,KAAK,UAAU,YAAY,GAAG,CAAC,CAAC;AAAA,UACrH;AACA,iBAAO;AAAA,QACT;AAEA,YAAI;AACF,iBAAO,KAAK,MAAM,OAAO;AAAA,QAC3B,SAAS,KAAK;AACZ;AAAA,YACE,sBAAsB,KAAK,wBAAwB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,eAAe,QAAQ,MAAM,iBAAiB,KAAK,UAAU,YAAY,OAAO,CAAC,CAAC;AAAA,UACvL;AACA,iBAAO;AAAA,QACT;AAAA,MACF,SAAS,KAAK;AAEZ;AAAA,UACE,sBAAsB,KAAK,8BAA8B,OAAO,KAAK,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC5H;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAC9C;AAwBD;;GAEG;AACH,eAAO,MAAM,GAAG,EAAE,MAKjB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/logger.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Mnemo Logger \u2014 Unified logging interface\n *\n * Replaces scattered console.log/warn/error with a structured logger.\n * Supports log levels, prefixes, and external logger injection.\n *\n * Usage:\n * import { log } from \"./logger.js\";\n * log.info(\"message\");\n * log.warn(\"something wrong\");\n * log.debug(\"verbose detail\"); // only when MNEMO_DEBUG=1\n */\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface Logger {\n debug(msg: string, ...args: unknown[]): void;\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n}\n\nconst PREFIX = \"[mnemo]\";\n\nconst isDebug = () => !!process.env.MNEMO_DEBUG;\n\n/** Default console-based logger */\nconst defaultLogger: Logger = {\n debug(msg, ...args) {\n if (isDebug()) console.debug(PREFIX, msg, ...args);\n },\n info(msg, ...args) {\n console.log(PREFIX, msg, ...args);\n },\n warn(msg, ...args) {\n console.warn(PREFIX, msg, ...args);\n },\n error(msg, ...args) {\n console.error(PREFIX, msg, ...args);\n },\n};\n\nlet _logger: Logger = defaultLogger;\n\n/**\n * Get the current logger instance.\n */\nexport const log: Logger = {\n debug: (msg, ...args) => _logger.debug(msg, ...args),\n info: (msg, ...args) => _logger.info(msg, ...args),\n warn: (msg, ...args) => _logger.warn(msg, ...args),\n error: (msg, ...args) => _logger.error(msg, ...args),\n};\n\n/**\n * Replace the default logger with a custom implementation.\n * Useful for integrating with OpenClaw's api.logger or external logging services.\n */\nexport function setLogger(logger: Logger): void {\n _logger = logger;\n}\n\n/**\n * Reset to the default console logger.\n */\nexport function resetLogger(): void {\n _logger = defaultLogger;\n}\n"],
5
- "mappings": "AAuBA,MAAM,SAAS;AAEf,MAAM,UAAU,MAAM,CAAC,CAAC,QAAQ,IAAI;AAGpC,MAAM,gBAAwB;AAAA,EAC5B,MAAM,QAAQ,MAAM;AAClB,QAAI,QAAQ,EAAG,SAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,EACnD;AAAA,EACA,KAAK,QAAQ,MAAM;AACjB,YAAQ,IAAI,QAAQ,KAAK,GAAG,IAAI;AAAA,EAClC;AAAA,EACA,KAAK,QAAQ,MAAM;AACjB,YAAQ,KAAK,QAAQ,KAAK,GAAG,IAAI;AAAA,EACnC;AAAA,EACA,MAAM,QAAQ,MAAM;AAClB,YAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,EACpC;AACF;AAEA,IAAI,UAAkB;AAKf,MAAM,MAAc;AAAA,EACzB,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,KAAK,GAAG,IAAI;AAAA,EACnD,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,KAAK,GAAG,IAAI;AAAA,EACjD,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,KAAK,GAAG,IAAI;AAAA,EACjD,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,KAAK,GAAG,IAAI;AACrD;AAMO,SAAS,UAAU,QAAsB;AAC9C,YAAU;AACZ;AAKO,SAAS,cAAoB;AAClC,YAAU;AACZ;",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/mcp-server.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;GAaG"}