@mnemoai/core 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +7 -0
  4. package/dist/cli.js.map +7 -0
  5. package/dist/index.d.ts +128 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/{index.ts → dist/index.js} +526 -1333
  8. package/dist/index.js.map +7 -0
  9. package/dist/src/access-tracker.d.ts +97 -0
  10. package/dist/src/access-tracker.d.ts.map +1 -0
  11. package/dist/src/access-tracker.js +184 -0
  12. package/dist/src/access-tracker.js.map +7 -0
  13. package/dist/src/adapters/chroma.d.ts +31 -0
  14. package/dist/src/adapters/chroma.d.ts.map +1 -0
  15. package/{src/adapters/chroma.ts → dist/src/adapters/chroma.js} +45 -107
  16. package/dist/src/adapters/chroma.js.map +7 -0
  17. package/dist/src/adapters/lancedb.d.ts +29 -0
  18. package/dist/src/adapters/lancedb.d.ts.map +1 -0
  19. package/{src/adapters/lancedb.ts → dist/src/adapters/lancedb.js} +41 -109
  20. package/dist/src/adapters/lancedb.js.map +7 -0
  21. package/dist/src/adapters/pgvector.d.ts +33 -0
  22. package/dist/src/adapters/pgvector.d.ts.map +1 -0
  23. package/{src/adapters/pgvector.ts → dist/src/adapters/pgvector.js} +42 -104
  24. package/dist/src/adapters/pgvector.js.map +7 -0
  25. package/dist/src/adapters/qdrant.d.ts +34 -0
  26. package/dist/src/adapters/qdrant.d.ts.map +1 -0
  27. package/dist/src/adapters/qdrant.js +132 -0
  28. package/dist/src/adapters/qdrant.js.map +7 -0
  29. package/dist/src/adaptive-retrieval.d.ts +14 -0
  30. package/dist/src/adaptive-retrieval.d.ts.map +1 -0
  31. package/dist/src/adaptive-retrieval.js +52 -0
  32. package/dist/src/adaptive-retrieval.js.map +7 -0
  33. package/dist/src/audit-log.d.ts +56 -0
  34. package/dist/src/audit-log.d.ts.map +1 -0
  35. package/dist/src/audit-log.js +139 -0
  36. package/dist/src/audit-log.js.map +7 -0
  37. package/dist/src/chunker.d.ts +45 -0
  38. package/dist/src/chunker.d.ts.map +1 -0
  39. package/dist/src/chunker.js +157 -0
  40. package/dist/src/chunker.js.map +7 -0
  41. package/dist/src/config.d.ts +70 -0
  42. package/dist/src/config.d.ts.map +1 -0
  43. package/dist/src/config.js +142 -0
  44. package/dist/src/config.js.map +7 -0
  45. package/dist/src/decay-engine.d.ts +73 -0
  46. package/dist/src/decay-engine.d.ts.map +1 -0
  47. package/dist/src/decay-engine.js +119 -0
  48. package/dist/src/decay-engine.js.map +7 -0
  49. package/dist/src/embedder.d.ts +94 -0
  50. package/dist/src/embedder.d.ts.map +1 -0
  51. package/{src/embedder.ts → dist/src/embedder.js} +119 -317
  52. package/dist/src/embedder.js.map +7 -0
  53. package/dist/src/extraction-prompts.d.ts +12 -0
  54. package/dist/src/extraction-prompts.d.ts.map +1 -0
  55. package/dist/src/extraction-prompts.js +311 -0
  56. package/dist/src/extraction-prompts.js.map +7 -0
  57. package/dist/src/license.d.ts +29 -0
  58. package/dist/src/license.d.ts.map +1 -0
  59. package/{src/license.ts → dist/src/license.js} +42 -113
  60. package/dist/src/license.js.map +7 -0
  61. package/dist/src/llm-client.d.ts +23 -0
  62. package/dist/src/llm-client.d.ts.map +1 -0
  63. package/{src/llm-client.ts → dist/src/llm-client.js} +22 -55
  64. package/dist/src/llm-client.js.map +7 -0
  65. package/dist/src/logger.d.ts +33 -0
  66. package/dist/src/logger.d.ts.map +1 -0
  67. package/dist/src/logger.js +35 -0
  68. package/dist/src/logger.js.map +7 -0
  69. package/dist/src/mcp-server.d.ts +16 -0
  70. package/dist/src/mcp-server.d.ts.map +1 -0
  71. package/{src/mcp-server.ts → dist/src/mcp-server.js} +81 -181
  72. package/dist/src/mcp-server.js.map +7 -0
  73. package/dist/src/memory-categories.d.ts +40 -0
  74. package/dist/src/memory-categories.d.ts.map +1 -0
  75. package/dist/src/memory-categories.js +33 -0
  76. package/dist/src/memory-categories.js.map +7 -0
  77. package/dist/src/memory-upgrader.d.ts +71 -0
  78. package/dist/src/memory-upgrader.d.ts.map +1 -0
  79. package/dist/src/memory-upgrader.js +238 -0
  80. package/dist/src/memory-upgrader.js.map +7 -0
  81. package/dist/src/migrate.d.ts +47 -0
  82. package/dist/src/migrate.d.ts.map +1 -0
  83. package/{src/migrate.ts → dist/src/migrate.js} +57 -165
  84. package/dist/src/migrate.js.map +7 -0
  85. package/dist/src/mnemo.d.ts +67 -0
  86. package/dist/src/mnemo.d.ts.map +1 -0
  87. package/dist/src/mnemo.js +66 -0
  88. package/dist/src/mnemo.js.map +7 -0
  89. package/dist/src/noise-filter.d.ts +23 -0
  90. package/dist/src/noise-filter.d.ts.map +1 -0
  91. package/dist/src/noise-filter.js +62 -0
  92. package/dist/src/noise-filter.js.map +7 -0
  93. package/dist/src/noise-prototypes.d.ts +40 -0
  94. package/dist/src/noise-prototypes.d.ts.map +1 -0
  95. package/dist/src/noise-prototypes.js +116 -0
  96. package/dist/src/noise-prototypes.js.map +7 -0
  97. package/dist/src/observability.d.ts +16 -0
  98. package/dist/src/observability.d.ts.map +1 -0
  99. package/dist/src/observability.js +53 -0
  100. package/dist/src/observability.js.map +7 -0
  101. package/dist/src/query-tracker.d.ts +27 -0
  102. package/dist/src/query-tracker.d.ts.map +1 -0
  103. package/dist/src/query-tracker.js +32 -0
  104. package/dist/src/query-tracker.js.map +7 -0
  105. package/dist/src/reflection-event-store.d.ts +44 -0
  106. package/dist/src/reflection-event-store.d.ts.map +1 -0
  107. package/dist/src/reflection-event-store.js +50 -0
  108. package/dist/src/reflection-event-store.js.map +7 -0
  109. package/dist/src/reflection-item-store.d.ts +58 -0
  110. package/dist/src/reflection-item-store.d.ts.map +1 -0
  111. package/dist/src/reflection-item-store.js +69 -0
  112. package/dist/src/reflection-item-store.js.map +7 -0
  113. package/dist/src/reflection-mapped-metadata.d.ts +47 -0
  114. package/dist/src/reflection-mapped-metadata.d.ts.map +1 -0
  115. package/dist/src/reflection-mapped-metadata.js +40 -0
  116. package/dist/src/reflection-mapped-metadata.js.map +7 -0
  117. package/dist/src/reflection-metadata.d.ts +11 -0
  118. package/dist/src/reflection-metadata.d.ts.map +1 -0
  119. package/dist/src/reflection-metadata.js +24 -0
  120. package/dist/src/reflection-metadata.js.map +7 -0
  121. package/dist/src/reflection-ranking.d.ts +13 -0
  122. package/dist/src/reflection-ranking.d.ts.map +1 -0
  123. package/{src/reflection-ranking.ts → dist/src/reflection-ranking.js} +12 -21
  124. package/dist/src/reflection-ranking.js.map +7 -0
  125. package/dist/src/reflection-retry.d.ts +30 -0
  126. package/dist/src/reflection-retry.d.ts.map +1 -0
  127. package/{src/reflection-retry.ts → dist/src/reflection-retry.js} +24 -64
  128. package/dist/src/reflection-retry.js.map +7 -0
  129. package/dist/src/reflection-slices.d.ts +42 -0
  130. package/dist/src/reflection-slices.d.ts.map +1 -0
  131. package/{src/reflection-slices.ts → dist/src/reflection-slices.js} +60 -136
  132. package/dist/src/reflection-slices.js.map +7 -0
  133. package/dist/src/reflection-store.d.ts +85 -0
  134. package/dist/src/reflection-store.d.ts.map +1 -0
  135. package/dist/src/reflection-store.js +407 -0
  136. package/dist/src/reflection-store.js.map +7 -0
  137. package/dist/src/resonance-state.d.ts +19 -0
  138. package/dist/src/resonance-state.d.ts.map +1 -0
  139. package/{src/resonance-state.ts → dist/src/resonance-state.js} +13 -42
  140. package/dist/src/resonance-state.js.map +7 -0
  141. package/dist/src/retriever.d.ts +228 -0
  142. package/dist/src/retriever.d.ts.map +1 -0
  143. package/dist/src/retriever.js +1006 -0
  144. package/dist/src/retriever.js.map +7 -0
  145. package/dist/src/scopes.d.ts +58 -0
  146. package/dist/src/scopes.d.ts.map +1 -0
  147. package/dist/src/scopes.js +252 -0
  148. package/dist/src/scopes.js.map +7 -0
  149. package/dist/src/self-improvement-files.d.ts +20 -0
  150. package/dist/src/self-improvement-files.d.ts.map +1 -0
  151. package/{src/self-improvement-files.ts → dist/src/self-improvement-files.js} +24 -49
  152. package/dist/src/self-improvement-files.js.map +7 -0
  153. package/dist/src/semantic-gate.d.ts +24 -0
  154. package/dist/src/semantic-gate.d.ts.map +1 -0
  155. package/dist/src/semantic-gate.js +86 -0
  156. package/dist/src/semantic-gate.js.map +7 -0
  157. package/dist/src/session-recovery.d.ts +9 -0
  158. package/dist/src/session-recovery.d.ts.map +1 -0
  159. package/{src/session-recovery.ts → dist/src/session-recovery.js} +40 -57
  160. package/dist/src/session-recovery.js.map +7 -0
  161. package/dist/src/smart-extractor.d.ts +107 -0
  162. package/dist/src/smart-extractor.d.ts.map +1 -0
  163. package/{src/smart-extractor.ts → dist/src/smart-extractor.js} +130 -383
  164. package/dist/src/smart-extractor.js.map +7 -0
  165. package/dist/src/smart-metadata.d.ts +103 -0
  166. package/dist/src/smart-metadata.d.ts.map +1 -0
  167. package/dist/src/smart-metadata.js +361 -0
  168. package/dist/src/smart-metadata.js.map +7 -0
  169. package/dist/src/storage-adapter.d.ts +102 -0
  170. package/dist/src/storage-adapter.d.ts.map +1 -0
  171. package/dist/src/storage-adapter.js +22 -0
  172. package/dist/src/storage-adapter.js.map +7 -0
  173. package/dist/src/store.d.ts +108 -0
  174. package/dist/src/store.d.ts.map +1 -0
  175. package/dist/src/store.js +939 -0
  176. package/dist/src/store.js.map +7 -0
  177. package/dist/src/tier-manager.d.ts +57 -0
  178. package/dist/src/tier-manager.d.ts.map +1 -0
  179. package/dist/src/tier-manager.js +80 -0
  180. package/dist/src/tier-manager.js.map +7 -0
  181. package/dist/src/tools.d.ts +43 -0
  182. package/dist/src/tools.d.ts.map +1 -0
  183. package/dist/src/tools.js +1075 -0
  184. package/dist/src/tools.js.map +7 -0
  185. package/dist/src/wal-recovery.d.ts +30 -0
  186. package/dist/src/wal-recovery.d.ts.map +1 -0
  187. package/{src/wal-recovery.ts → dist/src/wal-recovery.js} +26 -79
  188. package/dist/src/wal-recovery.js.map +7 -0
  189. package/package.json +21 -2
  190. package/openclaw.plugin.json +0 -815
  191. package/src/access-tracker.ts +0 -341
  192. package/src/adapters/README.md +0 -78
  193. package/src/adapters/qdrant.ts +0 -191
  194. package/src/adaptive-retrieval.ts +0 -90
  195. package/src/audit-log.ts +0 -238
  196. package/src/chunker.ts +0 -254
  197. package/src/config.ts +0 -271
  198. package/src/decay-engine.ts +0 -238
  199. package/src/extraction-prompts.ts +0 -339
  200. package/src/memory-categories.ts +0 -71
  201. package/src/memory-upgrader.ts +0 -388
  202. package/src/mnemo.ts +0 -142
  203. package/src/noise-filter.ts +0 -97
  204. package/src/noise-prototypes.ts +0 -164
  205. package/src/observability.ts +0 -81
  206. package/src/query-tracker.ts +0 -57
  207. package/src/reflection-event-store.ts +0 -98
  208. package/src/reflection-item-store.ts +0 -112
  209. package/src/reflection-mapped-metadata.ts +0 -84
  210. package/src/reflection-metadata.ts +0 -23
  211. package/src/reflection-store.ts +0 -602
  212. package/src/retriever.ts +0 -1510
  213. package/src/scopes.ts +0 -375
  214. package/src/semantic-gate.ts +0 -121
  215. package/src/smart-metadata.ts +0 -561
  216. package/src/storage-adapter.ts +0 -153
  217. package/src/store.ts +0 -1330
  218. package/src/tier-manager.ts +0 -189
  219. package/src/tools.ts +0 -1292
  220. package/test/core.test.mjs +0 -301
@@ -0,0 +1,7 @@
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
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Prompt templates for intelligent memory extraction.
3
+ * Three mandatory prompts:
4
+ * - buildExtractionPrompt: 6-category L0/L1/L2 extraction with few-shot
5
+ * - buildDedupPrompt: CREATE/MERGE/SKIP dedup decision
6
+ * - buildMergePrompt: Memory merge with three-level structure
7
+ */
8
+ export declare function buildExtractionPrompt(conversationText: string, user: string): string;
9
+ export declare function buildChineseExtractionPrompt(conversationText: string, user: string): string;
10
+ export declare function buildDedupPrompt(candidateAbstract: string, candidateOverview: string, candidateContent: string, existingMemories: string): string;
11
+ export declare function buildMergePrompt(existingAbstract: string, existingOverview: string, existingContent: string, newAbstract: string, newOverview: string, newContent: string, category: string): string;
12
+ //# sourceMappingURL=extraction-prompts.d.ts.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,311 @@
1
+ function buildExtractionPrompt(conversationText, user) {
2
+ return `Analyze the following session context and extract memories worth long-term preservation.
3
+
4
+ User: ${user}
5
+
6
+ Target Output Language: auto (detect from recent messages)
7
+
8
+ ## Recent Conversation
9
+ ${conversationText}
10
+
11
+ # Memory Extraction Criteria
12
+
13
+ ## What is worth remembering?
14
+ - Personalized information: Information specific to this user, not general domain knowledge
15
+ - Long-term validity: Information that will still be useful in future sessions
16
+ - Specific and clear: Has concrete details, not vague generalizations
17
+
18
+ ## What is NOT worth remembering?
19
+ - General knowledge that anyone would know
20
+ - Temporary information: One-time questions or conversations
21
+ - Vague information: "User has questions about a feature" (no specific details)
22
+ - Tool output, error logs, or boilerplate
23
+ - 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
24
+ - Degraded or incomplete references: If the user mentions something vaguely ("that thing I said"), do NOT invent details or create a hollow memory
25
+
26
+ # Memory Classification
27
+
28
+ ## Core Decision Logic
29
+
30
+ | Question | Answer | Category |
31
+ |----------|--------|----------|
32
+ | Who is the user? | Identity, attributes | profile |
33
+ | What does the user prefer? | Preferences, habits | preferences |
34
+ | What is this thing? | Person, project, organization | entities |
35
+ | What happened? | Decision, milestone | events |
36
+ | How was it solved? | Problem + solution | cases |
37
+ | What is the process? | Reusable steps | patterns |
38
+
39
+ ## Precise Definition
40
+
41
+ **profile** - User identity (static attributes). Test: "User is..."
42
+ **preferences** - User preferences (tendencies). Test: "User prefers/likes..."
43
+ **entities** - Continuously existing nouns. Test: "XXX's state is..."
44
+ **events** - Things that happened. Test: "XXX did/completed..."
45
+ **cases** - Problem + solution pairs. Test: Contains "problem -> solution"
46
+ **patterns** - Reusable processes. Test: Can be used in "similar situations"
47
+
48
+ ## Common Confusion
49
+ - "Plan to do X" -> events (action, not entity)
50
+ - "Project X status: Y" -> entities (describes entity)
51
+ - "User prefers X" -> preferences (not profile)
52
+ - "Encountered problem A, used solution B" -> cases (not events)
53
+ - "General process for handling certain problems" -> patterns (not cases)
54
+
55
+ # Three-Level Structure
56
+
57
+ Each memory contains three levels:
58
+
59
+ **abstract (L0)**: One-liner index
60
+ - Merge types (preferences/entities/profile/patterns): \`[Merge key]: [Description]\`
61
+ - Independent types (events/cases): Specific description
62
+
63
+ **overview (L1)**: Structured Markdown summary with category-specific headings
64
+
65
+ **content (L2)**: Full narrative with background and details
66
+
67
+ # Few-shot Examples
68
+
69
+ ## profile
70
+ \`\`\`json
71
+ {
72
+ "category": "profile",
73
+ "abstract": "User basic info: AI development engineer, 3 years LLM experience",
74
+ "overview": "## Background\\n- Occupation: AI development engineer\\n- Experience: 3 years LLM development\\n- Tech stack: Python, LangChain",
75
+ "content": "User is an AI development engineer with 3 years of LLM application development experience."
76
+ }
77
+ \`\`\`
78
+
79
+ ## preferences
80
+ \`\`\`json
81
+ {
82
+ "category": "preferences",
83
+ "abstract": "Python code style: No type hints, concise and direct",
84
+ "overview": "## Preference Domain\\n- Language: Python\\n- Topic: Code style\\n\\n## Details\\n- No type hints\\n- Concise function comments\\n- Direct implementation",
85
+ "content": "User prefers Python code without type hints, with concise function comments."
86
+ }
87
+ \`\`\`
88
+
89
+ ## cases
90
+ \`\`\`json
91
+ {
92
+ "category": "cases",
93
+ "abstract": "LanceDB BigInt error -> Use Number() coercion before arithmetic",
94
+ "overview": "## Problem\\nLanceDB 0.26+ returns BigInt for numeric columns\\n\\n## Solution\\nCoerce values with Number(...) before arithmetic",
95
+ "content": "When LanceDB returns BigInt values, wrap them with Number() before doing arithmetic operations."
96
+ }
97
+ \`\`\`
98
+
99
+ # Output Format
100
+
101
+ Return JSON:
102
+ {
103
+ "memories": [
104
+ {
105
+ "category": "profile|preferences|entities|events|cases|patterns",
106
+ "abstract": "One-line index",
107
+ "overview": "Structured Markdown summary",
108
+ "content": "Full narrative"
109
+ }
110
+ ]
111
+ }
112
+
113
+ Notes:
114
+ - Output language should match the dominant language in the conversation
115
+ - Only extract truly valuable personalized information
116
+ - If nothing worth recording, return {"memories": []}
117
+ - Maximum 5 memories per extraction
118
+ - Preferences should be aggregated by topic`;
119
+ }
120
+ function buildChineseExtractionPrompt(conversationText, user) {
121
+ return `\u5206\u6790\u4EE5\u4E0B\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u63D0\u53D6\u503C\u5F97\u957F\u671F\u4FDD\u5B58\u7684\u8BB0\u5FC6\u3002
122
+
123
+ \u7528\u6237: ${user}
124
+
125
+ ## \u6700\u8FD1\u5BF9\u8BDD
126
+ ${conversationText}
127
+
128
+ # \u8BB0\u5FC6\u63D0\u53D6\u6807\u51C6
129
+
130
+ ## \u4EC0\u4E48\u503C\u5F97\u8BB0\u4F4F\uFF1F
131
+ - \u4E2A\u6027\u5316\u4FE1\u606F\uFF1A\u4E13\u5C5E\u4E8E\u8BE5\u7528\u6237\u7684\u4FE1\u606F\uFF0C\u800C\u975E\u901A\u7528\u9886\u57DF\u77E5\u8BC6
132
+ - \u957F\u671F\u6709\u6548\u6027\uFF1A\u5728\u672A\u6765\u4F1A\u8BDD\u4E2D\u4ECD\u7136\u6709\u7528\u7684\u4FE1\u606F
133
+ - \u5177\u4F53\u660E\u786E\uFF1A\u6709\u5177\u4F53\u7EC6\u8282\uFF0C\u800C\u975E\u6A21\u7CCA\u7684\u6982\u62EC
134
+
135
+ ## \u4EC0\u4E48\u4E0D\u503C\u5F97\u8BB0\u4F4F\uFF1F
136
+ - \u4EFB\u4F55\u4EBA\u90FD\u77E5\u9053\u7684\u5E38\u8BC6
137
+ - \u4E34\u65F6\u4FE1\u606F\uFF1A\u4E00\u6B21\u6027\u7684\u63D0\u95EE\u6216\u5BF9\u8BDD
138
+ - \u6A21\u7CCA\u4FE1\u606F\uFF1A"\u7528\u6237\u5BF9\u67D0\u529F\u80FD\u6709\u7591\u95EE"\uFF08\u6CA1\u6709\u5177\u4F53\u7EC6\u8282\uFF09
139
+ - \u5DE5\u5177\u8F93\u51FA\u3001\u9519\u8BEF\u65E5\u5FD7\u6216\u6A21\u677F\u6587\u5B57
140
+ - \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
141
+ - \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
142
+
143
+ # \u8BB0\u5FC6\u5206\u7C7B
144
+
145
+ ## \u6838\u5FC3\u5224\u65AD\u903B\u8F91
146
+
147
+ | \u95EE\u9898 | \u7B54\u6848 | \u5206\u7C7B |
148
+ |------|------|------|
149
+ | \u7528\u6237\u662F\u8C01\uFF1F | \u8EAB\u4EFD\u3001\u5C5E\u6027 | profile |
150
+ | \u7528\u6237\u504F\u597D\u4EC0\u4E48\uFF1F | \u504F\u597D\u3001\u4E60\u60EF | preferences |
151
+ | \u8FD9\u4E2A\u4E1C\u897F\u662F\u4EC0\u4E48\uFF1F | \u4EBA\u7269\u3001\u9879\u76EE\u3001\u7EC4\u7EC7 | entities |
152
+ | \u53D1\u751F\u4E86\u4EC0\u4E48\uFF1F | \u51B3\u7B56\u3001\u91CC\u7A0B\u7891 | events |
153
+ | \u600E\u4E48\u89E3\u51B3\u7684\uFF1F | \u95EE\u9898+\u65B9\u6848 | cases |
154
+ | \u6D41\u7A0B\u662F\u4EC0\u4E48\uFF1F | \u53EF\u590D\u7528\u6B65\u9AA4 | patterns |
155
+
156
+ ## \u7CBE\u786E\u5B9A\u4E49
157
+
158
+ **profile** - \u7528\u6237\u8EAB\u4EFD\uFF08\u9759\u6001\u5C5E\u6027\uFF09\u3002\u5224\u65AD\u6807\u51C6\uFF1A"\u7528\u6237\u662F..."
159
+ **preferences** - \u7528\u6237\u504F\u597D\uFF08\u503E\u5411\u6027\uFF09\u3002\u5224\u65AD\u6807\u51C6\uFF1A"\u7528\u6237\u504F\u597D/\u559C\u6B22..."
160
+ **entities** - \u6301\u7EED\u5B58\u5728\u7684\u540D\u8BCD\u5B9E\u4F53\u3002\u5224\u65AD\u6807\u51C6\uFF1A"XXX\u7684\u72B6\u6001\u662F..."
161
+ **events** - \u53D1\u751F\u8FC7\u7684\u4E8B\u4EF6\u3002\u5224\u65AD\u6807\u51C6\uFF1A"XXX\u505A\u4E86/\u5B8C\u6210\u4E86..."
162
+ **cases** - \u95EE\u9898+\u89E3\u51B3\u65B9\u6848\u5BF9\u3002\u5224\u65AD\u6807\u51C6\uFF1A\u5305\u542B"\u95EE\u9898->\u65B9\u6848"
163
+ **patterns** - \u53EF\u590D\u7528\u6D41\u7A0B\u3002\u5224\u65AD\u6807\u51C6\uFF1A\u53EF\u7528\u4E8E"\u7C7B\u4F3C\u573A\u666F"
164
+
165
+ ## \u5E38\u89C1\u6DF7\u6DC6
166
+ - "\u8BA1\u5212\u505AX" -> events\uFF08\u884C\u52A8\uFF0C\u975E\u5B9E\u4F53\uFF09
167
+ - "\u9879\u76EEX\u7684\u72B6\u6001\uFF1AY" -> entities\uFF08\u63CF\u8FF0\u5B9E\u4F53\uFF09
168
+ - "\u7528\u6237\u504F\u597DX" -> preferences\uFF08\u4E0D\u662F profile\uFF09
169
+ - "\u9047\u5230\u95EE\u9898A\uFF0C\u7528\u4E86\u65B9\u6848B" -> cases\uFF08\u4E0D\u662F events\uFF09
170
+ - "\u5904\u7406\u67D0\u7C7B\u95EE\u9898\u7684\u901A\u7528\u6D41\u7A0B" -> patterns\uFF08\u4E0D\u662F cases\uFF09
171
+
172
+ # \u4E09\u7EA7\u7ED3\u6784
173
+
174
+ \u6BCF\u6761\u8BB0\u5FC6\u5305\u542B\u4E09\u4E2A\u5C42\u7EA7\uFF1A
175
+
176
+ **abstract (L0)**\uFF1A\u4E00\u884C\u7D22\u5F15
177
+ - \u5408\u5E76\u7C7B\u578B\uFF08preferences/entities/profile/patterns\uFF09\uFF1A\`[\u5408\u5E76\u952E]: [\u63CF\u8FF0]\`
178
+ - \u72EC\u7ACB\u7C7B\u578B\uFF08events/cases\uFF09\uFF1A\u5177\u4F53\u63CF\u8FF0
179
+
180
+ **overview (L1)**\uFF1A\u7ED3\u6784\u5316 Markdown \u6458\u8981\uFF0C\u4F7F\u7528\u5206\u7C7B\u7279\u5B9A\u6807\u9898
181
+
182
+ **content (L2)**\uFF1A\u5305\u542B\u80CC\u666F\u548C\u7EC6\u8282\u7684\u5B8C\u6574\u53D9\u8FF0
183
+
184
+ # \u5C11\u6837\u672C\u793A\u4F8B
185
+
186
+ ## profile
187
+ \`\`\`json
188
+ {
189
+ "category": "profile",
190
+ "abstract": "\u7528\u6237\u57FA\u672C\u4FE1\u606F\uFF1AAI\u5F00\u53D1\u5DE5\u7A0B\u5E08\uFF0C3\u5E74LLM\u7ECF\u9A8C",
191
+ "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",
192
+ "content": "\u7528\u6237\u662F\u4E00\u540DAI\u5F00\u53D1\u5DE5\u7A0B\u5E08\uFF0C\u67093\u5E74LLM\u5E94\u7528\u5F00\u53D1\u7ECF\u9A8C\u3002"
193
+ }
194
+ \`\`\`
195
+
196
+ ## preferences
197
+ \`\`\`json
198
+ {
199
+ "category": "preferences",
200
+ "abstract": "Python\u4EE3\u7801\u98CE\u683C\uFF1A\u4E0D\u52A0\u7C7B\u578B\u6CE8\u89E3\uFF0C\u7B80\u6D01\u76F4\u63A5",
201
+ "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",
202
+ "content": "\u7528\u6237\u504F\u597D\u4E0D\u5E26\u7C7B\u578B\u6CE8\u89E3\u7684Python\u4EE3\u7801\uFF0C\u51FD\u6570\u6CE8\u91CA\u8981\u7B80\u6D01\u3002"
203
+ }
204
+ \`\`\`
205
+
206
+ ## cases
207
+ \`\`\`json
208
+ {
209
+ "category": "cases",
210
+ "abstract": "LanceDB BigInt\u9519\u8BEF -> \u5728\u8FD0\u7B97\u524D\u4F7F\u7528Number()\u8F6C\u6362",
211
+ "overview": "## \u95EE\u9898\\nLanceDB 0.26+\u5BF9\u6570\u503C\u5217\u8FD4\u56DEBigInt\\n\\n## \u65B9\u6848\\n\u5728\u8FD0\u7B97\u524D\u7528Number(...)\u8F6C\u6362",
212
+ "content": "\u5F53LanceDB\u8FD4\u56DEBigInt\u503C\u65F6\uFF0C\u5728\u505A\u7B97\u672F\u8FD0\u7B97\u524D\u7528Number()\u5305\u88F9\u3002"
213
+ }
214
+ \`\`\`
215
+
216
+ # \u8F93\u51FA\u683C\u5F0F
217
+
218
+ \u8FD4\u56DE JSON:
219
+ {
220
+ "memories": [
221
+ {
222
+ "category": "profile|preferences|entities|events|cases|patterns",
223
+ "abstract": "\u4E00\u884C\u7D22\u5F15",
224
+ "overview": "\u7ED3\u6784\u5316Markdown\u6458\u8981",
225
+ "content": "\u5B8C\u6574\u53D9\u8FF0"
226
+ }
227
+ ]
228
+ }
229
+
230
+ \u6CE8\u610F\uFF1A
231
+ - \u8F93\u51FA\u8BED\u8A00\u5E94\u4E0E\u5BF9\u8BDD\u4E2D\u7684\u4E3B\u8981\u8BED\u8A00\u4E00\u81F4
232
+ - \u53EA\u63D0\u53D6\u771F\u6B63\u6709\u4EF7\u503C\u7684\u4E2A\u6027\u5316\u4FE1\u606F
233
+ - \u5982\u679C\u6CA1\u6709\u503C\u5F97\u8BB0\u5F55\u7684\u5185\u5BB9\uFF0C\u8FD4\u56DE {"memories": []}
234
+ - \u6BCF\u6B21\u63D0\u53D6\u6700\u591A5\u6761\u8BB0\u5FC6
235
+ - \u504F\u597D\u5E94\u6309\u4E3B\u9898\u805A\u5408`;
236
+ }
237
+ function buildDedupPrompt(candidateAbstract, candidateOverview, candidateContent, existingMemories) {
238
+ return `Determine how to handle this candidate memory.
239
+
240
+ **Candidate Memory**:
241
+ Abstract: ${candidateAbstract}
242
+ Overview: ${candidateOverview}
243
+ Content: ${candidateContent}
244
+
245
+ **Existing Similar Memories**:
246
+ ${existingMemories}
247
+
248
+ Please decide:
249
+ - 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")
250
+ - CREATE: This is completely new information not covered by any existing memory, should be created
251
+ - MERGE: Candidate memory adds genuinely NEW details to an existing memory and should be merged
252
+ - SUPPORT: Candidate reinforces/confirms an existing memory in a specific context (e.g. "still prefers tea in the evening")
253
+ - 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)
254
+ - CONTRADICT: Candidate directly contradicts an existing memory in a specific context (e.g. existing: "runs on weekends", candidate: "stopped running on weekends")
255
+
256
+ IMPORTANT:
257
+ - "events" and "cases" categories are independent records \u2014 they do NOT support MERGE/SUPPORT/CONTEXTUALIZE/CONTRADICT. For these categories, only use SKIP or CREATE.
258
+ - 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.
259
+ - A candidate with less information than an existing memory on the same topic should NEVER be CREATED or MERGED \u2014 always SKIP.
260
+ - For SUPPORT/CONTEXTUALIZE/CONTRADICT, you MUST provide a context_label from this vocabulary: general, morning, evening, night, weekday, weekend, work, leisure, summer, winter, travel.
261
+
262
+ Return JSON format:
263
+ {
264
+ "decision": "skip|create|merge|support|contextualize|contradict",
265
+ "match_index": 1,
266
+ "reason": "Decision reason",
267
+ "context_label": "evening"
268
+ }
269
+
270
+ - If decision is "merge"/"support"/"contextualize"/"contradict", set "match_index" to the number of the existing memory (1-based).
271
+ - Only include "context_label" for support/contextualize/contradict decisions.`;
272
+ }
273
+ function buildMergePrompt(existingAbstract, existingOverview, existingContent, newAbstract, newOverview, newContent, category) {
274
+ return `Merge the following memory into a single coherent record with all three levels.
275
+
276
+ ** Category **: ${category}
277
+
278
+ ** Existing Memory:**
279
+ Abstract: ${existingAbstract}
280
+ Overview:
281
+ ${existingOverview}
282
+ Content:
283
+ ${existingContent}
284
+
285
+ ** New Information:**
286
+ Abstract: ${newAbstract}
287
+ Overview:
288
+ ${newOverview}
289
+ Content:
290
+ ${newContent}
291
+
292
+ Requirements:
293
+ - Remove duplicate information
294
+ - Keep the most up - to - date details
295
+ - Maintain a coherent narrative
296
+ - Keep code identifiers / URIs / model names unchanged when they are proper nouns
297
+
298
+ Return JSON:
299
+ {
300
+ "abstract": "Merged one-line abstract",
301
+ "overview": "Merged structured Markdown overview",
302
+ "content": "Merged full content"
303
+ } `;
304
+ }
305
+ export {
306
+ buildChineseExtractionPrompt,
307
+ buildDedupPrompt,
308
+ buildExtractionPrompt,
309
+ buildMergePrompt
310
+ };
311
+ //# sourceMappingURL=extraction-prompts.js.map
@@ -0,0 +1,7 @@
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
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Mnemo Pro License Validation
3
+ *
4
+ * Two modes:
5
+ * 1. MNEMO_PRO_KEY — pre-activated key (offline, machine-bound)
6
+ * 2. MNEMO_LICENSE_TOKEN — auto-activate on first run (online, one-time)
7
+ *
8
+ * Machine fingerprint: SHA-256(hostname + arch + cpuModel + platform)
9
+ * Indie keys are bound to one machine. Team/Enterprise keys are per-seat.
10
+ */
11
+ export interface LicensePayload {
12
+ licensee: string;
13
+ email: string;
14
+ plan: "indie" | "team" | "enterprise";
15
+ issued: string;
16
+ expires: string;
17
+ machine_id?: string;
18
+ }
19
+ export declare function getMachineFingerprint(): string;
20
+ export declare function isProLicensed(): boolean;
21
+ /**
22
+ * Async version — waits for activation to complete if token is present.
23
+ * Use this during plugin initialization.
24
+ */
25
+ export declare function ensureProLicense(): Promise<boolean>;
26
+ export declare function getLicenseInfo(): LicensePayload | null;
27
+ export declare function requirePro(featureName: string): boolean;
28
+ export declare function _resetLicenseCache(): void;
29
+ //# sourceMappingURL=license.d.ts.map
@@ -0,0 +1 @@
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"}