@mnemoai/core 1.1.0 → 1.1.2

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 +136 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/{index.ts → dist/index.js} +537 -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/smart-extractor.ts"],
4
+ "sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Smart Memory Extractor \u2014 LLM-powered extraction pipeline\n * Replaces regex-triggered capture with intelligent 6-category extraction.\n *\n * Pipeline: conversation \u2192 LLM extract \u2192 candidates \u2192 dedup \u2192 persist\n *\n */\n\nimport type { MemoryStore, MemorySearchResult } from \"./store.js\";\nimport type { Embedder } from \"./embedder.js\";\nimport type { LlmClient } from \"./llm-client.js\";\nimport {\n buildExtractionPrompt,\n buildChineseExtractionPrompt,\n buildDedupPrompt,\n buildMergePrompt,\n} from \"./extraction-prompts.js\";\nimport {\n type CandidateMemory,\n type DedupDecision,\n type DedupResult,\n type ExtractionStats,\n type MemoryCategory,\n ALWAYS_MERGE_CATEGORIES,\n MERGE_SUPPORTED_CATEGORIES,\n MEMORY_CATEGORIES,\n normalizeCategory,\n} from \"./memory-categories.js\";\nimport { isNoise } from \"./noise-filter.js\";\nimport type { NoisePrototypeBank } from \"./noise-prototypes.js\";\nimport { buildSmartMetadata, parseSmartMetadata, stringifySmartMetadata, parseSupportInfo, updateSupportStats } from \"./smart-metadata.js\";\nimport { log as _log } from \"./logger.js\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst SIMILARITY_THRESHOLD = 0.7;\nconst MAX_SIMILAR_FOR_PROMPT = 3;\nconst MAX_MEMORIES_PER_EXTRACTION = 5;\nconst VALID_DECISIONS = new Set<string>([\"create\", \"merge\", \"skip\", \"support\", \"contextualize\", \"contradict\"]);\n\n// ============================================================================\n// CJK Detection\n// ============================================================================\n\n/**\n * Detect whether a text is predominantly CJK (Chinese/Japanese/Korean).\n * Returns true if CJK characters make up > 30% of non-whitespace characters.\n */\nfunction isCjkDominant(text: string): boolean {\n // CJK Unified Ideographs + CJK Extension A/B + CJK Compatibility + Kana + Hangul\n const cjkRegex = /[\\u4e00-\\u9fff\\u3400-\\u4dbf\\u3040-\\u309f\\u30a0-\\u30ff\\uac00-\\ud7af\\uf900-\\ufaff]/g;\n const nonWhitespace = text.replace(/\\s/g, \"\");\n if (nonWhitespace.length === 0) return false;\n const cjkMatches = nonWhitespace.match(cjkRegex);\n const cjkCount = cjkMatches ? cjkMatches.length : 0;\n return cjkCount / nonWhitespace.length > 0.3;\n}\n\n// ============================================================================\n// Smart Extractor\n// ============================================================================\n\nexport interface SmartExtractorConfig {\n /** User identifier for extraction prompt. */\n user?: string;\n /** Minimum conversation messages before extraction triggers. */\n extractMinMessages?: number;\n /** Maximum characters of conversation text to process. */\n extractMaxChars?: number;\n /** Default scope for new memories. */\n defaultScope?: string;\n /** Logger function. */\n log?: (msg: string) => void;\n /** Debug logger function. */\n debugLog?: (msg: string) => void;\n /** Optional embedding-based noise prototype bank for language-agnostic noise filtering. */\n noiseBank?: NoisePrototypeBank;\n}\n\nexport interface ExtractPersistOptions {\n /** Target scope for newly created memories. */\n scope?: string;\n /** Scopes visible to the current agent for dedup/merge. */\n scopeFilter?: string[];\n}\n\nexport class SmartExtractor {\n private log: (msg: string) => void;\n private debugLog: (msg: string) => void;\n\n constructor(\n private store: MemoryStore,\n private embedder: Embedder,\n private llm: LlmClient,\n private config: SmartExtractorConfig = {},\n ) {\n this.log = config.log ?? ((msg: string) => _log.info(msg));\n this.debugLog = config.debugLog ?? (() => { });\n }\n\n // --------------------------------------------------------------------------\n // Main entry point\n // --------------------------------------------------------------------------\n\n /**\n * Extract memories from a conversation text and persist them.\n * Returns extraction statistics.\n */\n async extractAndPersist(\n conversationText: string,\n sessionKey: string = \"unknown\",\n options: ExtractPersistOptions = {},\n ): Promise<ExtractionStats> {\n const stats: ExtractionStats = { created: 0, merged: 0, skipped: 0 };\n const targetScope = options.scope ?? this.config.defaultScope ?? \"global\";\n const scopeFilter =\n options.scopeFilter && options.scopeFilter.length > 0\n ? options.scopeFilter\n : [targetScope];\n\n // Step 1: LLM extraction\n const candidates = await this.extractCandidates(conversationText);\n\n if (candidates.length === 0) {\n this.log(\"memory-pro: smart-extractor: no memories extracted\");\n // LLM returned zero candidates \u2192 strongest noise signal \u2192 feedback to noise bank\n this.learnAsNoise(conversationText);\n return stats;\n }\n\n this.log(\n `memory-pro: smart-extractor: extracted ${candidates.length} candidate(s)`,\n );\n\n // Step 2: Process each candidate through dedup pipeline\n for (const candidate of candidates.slice(0, MAX_MEMORIES_PER_EXTRACTION)) {\n try {\n await this.processCandidate(\n candidate,\n sessionKey,\n stats,\n targetScope,\n scopeFilter,\n );\n } catch (err) {\n this.log(\n `memory-pro: smart-extractor: failed to process candidate [${candidate.category}]: ${String(err)}`,\n );\n }\n }\n\n return stats;\n }\n\n // --------------------------------------------------------------------------\n // Embedding Noise Pre-Filter\n // --------------------------------------------------------------------------\n\n /**\n * Filter out texts that match noise prototypes by embedding similarity.\n * Long texts (>300 chars) are passed through without checking.\n * Only active when noiseBank is configured and initialized.\n */\n async filterNoiseByEmbedding(texts: string[]): Promise<string[]> {\n const noiseBank = this.config.noiseBank;\n if (!noiseBank || !noiseBank.initialized) return texts;\n\n const result: string[] = [];\n for (const text of texts) {\n // Very short texts lack semantic signal \u2014 skip noise check to avoid false positives\n if (text.length <= 8) {\n result.push(text);\n continue;\n }\n // Long texts are unlikely to be pure noise queries\n if (text.length > 300) {\n result.push(text);\n continue;\n }\n try {\n const vec = await this.embedder.embed(text);\n if (!vec || vec.length === 0 || !noiseBank.isNoise(vec)) {\n result.push(text);\n } else {\n this.debugLog(\n `mnemo: smart-extractor: embedding noise filtered: ${text.slice(0, 80)}`,\n );\n }\n } catch {\n // Embedding failed \u2014 pass text through\n result.push(text);\n }\n }\n return result;\n }\n\n /**\n * Feed back conversation text to the noise prototype bank.\n * Called when LLM extraction returns zero candidates (strongest noise signal).\n */\n private async learnAsNoise(conversationText: string): Promise<void> {\n const noiseBank = this.config.noiseBank;\n if (!noiseBank || !noiseBank.initialized) return;\n\n try {\n const tail = conversationText.slice(-300);\n const vec = await this.embedder.embed(tail);\n if (vec && vec.length > 0) {\n noiseBank.learn(vec);\n this.debugLog(\"mnemo: smart-extractor: learned noise from zero-extraction\");\n }\n } catch {\n // Non-critical \u2014 silently skip\n }\n }\n\n // --------------------------------------------------------------------------\n // Step 1: LLM Extraction\n // --------------------------------------------------------------------------\n\n /**\n * Call LLM to extract candidate memories from conversation text.\n */\n private async extractCandidates(\n conversationText: string,\n ): Promise<CandidateMemory[]> {\n const maxChars = this.config.extractMaxChars ?? 8000;\n const truncated =\n conversationText.length > maxChars\n ? conversationText.slice(-maxChars)\n : conversationText;\n\n const user = this.config.user ?? \"User\";\n const prompt = isCjkDominant(truncated)\n ? buildChineseExtractionPrompt(truncated, user)\n : buildExtractionPrompt(truncated, user);\n\n const result = await this.llm.completeJson<{\n memories: Array<{\n category: string;\n abstract: string;\n overview: string;\n content: string;\n }>;\n }>(prompt, \"extract-candidates\");\n\n if (!result) {\n this.debugLog(\n \"mnemo: smart-extractor: extract-candidates returned null\",\n );\n return [];\n }\n if (!result.memories || !Array.isArray(result.memories)) {\n this.debugLog(\n `mnemo: smart-extractor: extract-candidates returned unexpected shape keys=${Object.keys(result).join(\",\") || \"(none)\"}`,\n );\n return [];\n }\n\n this.debugLog(\n `mnemo: smart-extractor: extract-candidates raw memories=${result.memories.length}`,\n );\n\n // Validate and normalize candidates\n const candidates: CandidateMemory[] = [];\n let invalidCategoryCount = 0;\n let shortAbstractCount = 0;\n let noiseAbstractCount = 0;\n for (const raw of result.memories) {\n const category = normalizeCategory(raw.category ?? \"\");\n if (!category) {\n invalidCategoryCount++;\n this.debugLog(\n `mnemo: smart-extractor: dropping candidate due to invalid category rawCategory=${JSON.stringify(raw.category ?? \"\")} abstract=${JSON.stringify((raw.abstract ?? \"\").trim().slice(0, 120))}`,\n );\n continue;\n }\n\n const abstract = (raw.abstract ?? \"\").trim();\n const overview = (raw.overview ?? \"\").trim();\n const content = (raw.content ?? \"\").trim();\n\n // Skip empty or noise\n if (!abstract || abstract.length < 5) {\n shortAbstractCount++;\n this.debugLog(\n `mnemo: smart-extractor: dropping candidate due to short abstract category=${category} abstract=${JSON.stringify(abstract)}`,\n );\n continue;\n }\n if (isNoise(abstract)) {\n noiseAbstractCount++;\n this.debugLog(\n `mnemo: smart-extractor: dropping candidate due to noise abstract category=${category} abstract=${JSON.stringify(abstract.slice(0, 120))}`,\n );\n continue;\n }\n\n candidates.push({ category, abstract, overview, content });\n }\n\n this.debugLog(\n `mnemo: smart-extractor: validation summary accepted=${candidates.length}, invalidCategory=${invalidCategoryCount}, shortAbstract=${shortAbstractCount}, noiseAbstract=${noiseAbstractCount}`,\n );\n\n return candidates;\n }\n\n // --------------------------------------------------------------------------\n // Step 2: Dedup + Persist\n // --------------------------------------------------------------------------\n\n /**\n * Process a single candidate memory: dedup \u2192 merge/create \u2192 store\n */\n private async processCandidate(\n candidate: CandidateMemory,\n sessionKey: string,\n stats: ExtractionStats,\n targetScope: string,\n scopeFilter: string[],\n ): Promise<void> {\n // Profile always merges (skip dedup)\n if (ALWAYS_MERGE_CATEGORIES.has(candidate.category)) {\n await this.handleProfileMerge(\n candidate,\n sessionKey,\n targetScope,\n scopeFilter,\n );\n stats.merged++;\n return;\n }\n\n // Embed the candidate for vector dedup\n const embeddingText = `${candidate.abstract} ${candidate.content}`;\n const vector = await this.embedder.embed(embeddingText);\n if (!vector || vector.length === 0) {\n this.log(\"memory-pro: smart-extractor: embedding failed, storing as-is\");\n await this.storeCandidate(candidate, vector || [], sessionKey, targetScope);\n stats.created++;\n return;\n }\n\n // Dedup pipeline\n const dedupResult = await this.deduplicate(candidate, vector, scopeFilter);\n\n switch (dedupResult.decision) {\n case \"create\":\n await this.storeCandidate(candidate, vector, sessionKey, targetScope);\n stats.created++;\n break;\n\n case \"merge\":\n if (\n dedupResult.matchId &&\n MERGE_SUPPORTED_CATEGORIES.has(candidate.category)\n ) {\n await this.handleMerge(\n candidate,\n dedupResult.matchId,\n scopeFilter,\n targetScope,\n dedupResult.contextLabel,\n );\n stats.merged++;\n } else {\n // Category doesn't support merge \u2192 create instead\n await this.storeCandidate(candidate, vector, sessionKey, targetScope);\n stats.created++;\n }\n break;\n\n case \"skip\":\n this.log(\n `memory-pro: smart-extractor: skipped [${candidate.category}] ${candidate.abstract.slice(0, 60)}`,\n );\n stats.skipped++;\n break;\n\n case \"support\":\n if (dedupResult.matchId) {\n await this.handleSupport(dedupResult.matchId, scopeFilter, { session: sessionKey, timestamp: Date.now() }, dedupResult.reason, dedupResult.contextLabel);\n stats.supported = (stats.supported ?? 0) + 1;\n } else {\n await this.storeCandidate(candidate, vector, sessionKey, targetScope);\n stats.created++;\n }\n break;\n\n case \"contextualize\":\n if (dedupResult.matchId) {\n await this.handleContextualize(candidate, vector, dedupResult.matchId, sessionKey, targetScope, scopeFilter, dedupResult.contextLabel);\n stats.created++;\n } else {\n await this.storeCandidate(candidate, vector, sessionKey, targetScope);\n stats.created++;\n }\n break;\n\n case \"contradict\":\n if (dedupResult.matchId) {\n await this.handleContradict(candidate, vector, dedupResult.matchId, sessionKey, targetScope, scopeFilter, dedupResult.contextLabel);\n stats.created++;\n } else {\n await this.storeCandidate(candidate, vector, sessionKey, targetScope);\n stats.created++;\n }\n break;\n }\n }\n\n // --------------------------------------------------------------------------\n // Dedup Pipeline (vector pre-filter + LLM decision)\n // --------------------------------------------------------------------------\n\n /**\n * Two-stage dedup: vector similarity search \u2192 LLM decision.\n */\n private async deduplicate(\n candidate: CandidateMemory,\n candidateVector: number[],\n scopeFilter: string[],\n ): Promise<DedupResult> {\n // Stage 1: Vector pre-filter \u2014 find similar memories\n const similar = await this.store.vectorSearch(\n candidateVector,\n 5,\n SIMILARITY_THRESHOLD,\n scopeFilter,\n );\n\n if (similar.length === 0) {\n return { decision: \"create\", reason: \"No similar memories found\" };\n }\n\n // Stage 2: LLM decision\n return this.llmDedupDecision(candidate, similar);\n }\n\n private async llmDedupDecision(\n candidate: CandidateMemory,\n similar: MemorySearchResult[],\n ): Promise<DedupResult> {\n const topSimilar = similar.slice(0, MAX_SIMILAR_FOR_PROMPT);\n const existingFormatted = topSimilar\n .map((r, i) => {\n // Extract L0 abstract from metadata if available, fallback to text\n let metaObj: Record<string, unknown> = {};\n try {\n metaObj = JSON.parse(r.entry.metadata || \"{}\");\n } catch { }\n const abstract = (metaObj.l0_abstract as string) || r.entry.text;\n const overview = (metaObj.l1_overview as string) || \"\";\n return `${i + 1}. [${(metaObj.memory_category as string) || r.entry.category}] ${abstract}\\n Overview: ${overview}\\n Score: ${r.score.toFixed(3)}`;\n })\n .join(\"\\n\");\n\n const prompt = buildDedupPrompt(\n candidate.abstract,\n candidate.overview,\n candidate.content,\n existingFormatted,\n );\n\n try {\n const data = await this.llm.completeJson<{\n decision: string;\n reason: string;\n match_index?: number;\n context_label?: string;\n }>(prompt, \"dedup-decision\");\n\n if (!data) {\n this.log(\n \"memory-pro: smart-extractor: dedup LLM returned unparseable response, defaulting to CREATE\",\n );\n return { decision: \"create\", reason: \"LLM response unparseable\" };\n }\n\n const decision = (data.decision?.toLowerCase() ??\n \"create\") as DedupDecision;\n if (!VALID_DECISIONS.has(decision)) {\n return {\n decision: \"create\",\n reason: `Unknown decision: ${data.decision}`,\n };\n }\n\n // Resolve merge target from LLM's match_index (1-based)\n const idx = data.match_index;\n const matchEntry =\n typeof idx === \"number\" && idx >= 1 && idx <= topSimilar.length\n ? topSimilar[idx - 1]\n : topSimilar[0];\n\n return {\n decision,\n reason: data.reason ?? \"\",\n matchId: [\"merge\", \"support\", \"contextualize\", \"contradict\"].includes(decision) ? matchEntry?.entry.id : undefined,\n contextLabel: typeof data.context_label === \"string\" ? data.context_label : undefined,\n };\n } catch (err) {\n this.log(\n `memory-pro: smart-extractor: dedup LLM failed: ${String(err)}`,\n );\n return { decision: \"create\", reason: `LLM failed: ${String(err)}` };\n }\n }\n\n // --------------------------------------------------------------------------\n // Merge Logic\n // --------------------------------------------------------------------------\n\n /**\n * Profile always-merge: read existing profile, merge with LLM, upsert.\n */\n private async handleProfileMerge(\n candidate: CandidateMemory,\n sessionKey: string,\n targetScope: string,\n scopeFilter: string[],\n ): Promise<void> {\n // Find existing profile memory by category\n const embeddingText = `${candidate.abstract} ${candidate.content}`;\n const vector = await this.embedder.embed(embeddingText);\n\n // Search for existing profile memories\n const existing = await this.store.vectorSearch(\n vector || [],\n 1,\n 0.3,\n scopeFilter,\n );\n const profileMatch = existing.find((r) => {\n try {\n const meta = JSON.parse(r.entry.metadata || \"{}\");\n return meta.memory_category === \"profile\";\n } catch {\n return false;\n }\n });\n\n if (profileMatch) {\n await this.handleMerge(\n candidate,\n profileMatch.entry.id,\n scopeFilter,\n targetScope,\n );\n } else {\n // No existing profile \u2014 create new\n await this.storeCandidate(candidate, vector || [], sessionKey, targetScope);\n }\n }\n\n /**\n * Merge a candidate into an existing memory using LLM.\n */\n private async handleMerge(\n candidate: CandidateMemory,\n matchId: string,\n scopeFilter: string[],\n targetScope: string,\n contextLabel?: string,\n ): Promise<void> {\n let existingAbstract = \"\";\n let existingOverview = \"\";\n let existingContent = \"\";\n\n try {\n const existing = await this.store.getById(matchId, scopeFilter);\n if (existing) {\n const meta = parseSmartMetadata(existing.metadata, existing);\n existingAbstract = meta.l0_abstract || existing.text;\n existingOverview = meta.l1_overview || \"\";\n existingContent = meta.l2_content || existing.text;\n }\n } catch {\n // Fallback: store as new\n this.log(\n `memory-pro: smart-extractor: could not read existing memory ${matchId}, storing as new`,\n );\n const vector = await this.embedder.embed(\n `${candidate.abstract} ${candidate.content}`,\n );\n await this.storeCandidate(\n candidate,\n vector || [],\n \"merge-fallback\",\n targetScope,\n );\n return;\n }\n\n // Call LLM to merge\n const prompt = buildMergePrompt(\n existingAbstract,\n existingOverview,\n existingContent,\n candidate.abstract,\n candidate.overview,\n candidate.content,\n candidate.category,\n );\n\n const merged = await this.llm.completeJson<{\n abstract: string;\n overview: string;\n content: string;\n }>(prompt, \"merge-memory\");\n\n if (!merged) {\n this.log(\"memory-pro: smart-extractor: merge LLM failed, skipping merge\");\n return;\n }\n\n // Re-embed the merged content\n const mergedText = `${merged.abstract} ${merged.content}`;\n const newVector = await this.embedder.embed(mergedText);\n\n // Update existing memory via store.update()\n const existing = await this.store.getById(matchId, scopeFilter);\n const metadata = stringifySmartMetadata(\n buildSmartMetadata(existing ?? { text: merged.abstract }, {\n l0_abstract: merged.abstract,\n l1_overview: merged.overview,\n l2_content: merged.content,\n memory_category: candidate.category,\n tier: \"working\",\n confidence: 0.8,\n }),\n );\n\n await this.store.update(\n matchId,\n {\n text: merged.abstract,\n vector: newVector,\n metadata,\n },\n scopeFilter,\n );\n\n // Update support stats on the merged memory\n try {\n const updatedEntry = await this.store.getById(matchId, scopeFilter);\n if (updatedEntry) {\n const meta = parseSmartMetadata(updatedEntry.metadata, updatedEntry);\n const supportInfo = parseSupportInfo(meta.support_info);\n const updated = updateSupportStats(supportInfo, contextLabel, \"support\");\n const finalMetadata = stringifySmartMetadata({ ...meta, support_info: updated });\n await this.store.update(matchId, { metadata: finalMetadata }, scopeFilter);\n }\n } catch {\n // Non-critical: merge succeeded, support stats update is best-effort\n }\n\n this.log(\n `memory-pro: smart-extractor: merged [${candidate.category}]${contextLabel ? ` [${contextLabel}]` : \"\"} into ${matchId.slice(0, 8)}`,\n );\n }\n\n // --------------------------------------------------------------------------\n // Context-Aware Handlers (support / contextualize / contradict)\n // --------------------------------------------------------------------------\n\n /**\n * Handle SUPPORT: update support stats on existing memory for a specific context.\n */\n private async handleSupport(\n matchId: string,\n scopeFilter: string[],\n source: { session: string; timestamp: number },\n reason: string,\n contextLabel?: string,\n ): Promise<void> {\n const existing = await this.store.getById(matchId, scopeFilter);\n if (!existing) return;\n\n const meta = parseSmartMetadata(existing.metadata, existing);\n const supportInfo = parseSupportInfo(meta.support_info);\n const updated = updateSupportStats(supportInfo, contextLabel, \"support\");\n meta.support_info = updated;\n\n await this.store.update(\n matchId,\n { metadata: stringifySmartMetadata(meta) },\n scopeFilter,\n );\n\n this.log(\n `memory-pro: smart-extractor: support [${contextLabel || \"general\"}] on ${matchId.slice(0, 8)} \u2014 ${reason}`,\n );\n }\n\n /**\n * Handle CONTEXTUALIZE: create a new entry that adds situational nuance,\n * linked to the original via a relation in metadata.\n */\n private async handleContextualize(\n candidate: CandidateMemory,\n vector: number[],\n matchId: string,\n sessionKey: string,\n targetScope: string,\n scopeFilter: string[],\n contextLabel?: string,\n ): Promise<void> {\n const storeCategory = this.mapToStoreCategory(candidate.category);\n const metadata = stringifySmartMetadata({\n l0_abstract: candidate.abstract,\n l1_overview: candidate.overview,\n l2_content: candidate.content,\n memory_category: candidate.category,\n tier: \"working\" as const,\n access_count: 0,\n confidence: 0.7,\n last_accessed_at: Date.now(),\n source_session: sessionKey,\n contexts: contextLabel ? [contextLabel] : [],\n relations: [{ type: \"contextualizes\", targetId: matchId }],\n });\n\n await this.store.store({\n text: candidate.abstract,\n vector,\n category: storeCategory,\n scope: targetScope,\n importance: this.getDefaultImportance(candidate.category),\n metadata,\n });\n\n this.log(\n `memory-pro: smart-extractor: contextualize [${contextLabel || \"general\"}] new entry linked to ${matchId.slice(0, 8)}`,\n );\n }\n\n /**\n * Handle CONTRADICT: create contradicting entry + record contradiction evidence\n * on the original memory's support stats.\n */\n private async handleContradict(\n candidate: CandidateMemory,\n vector: number[],\n matchId: string,\n sessionKey: string,\n targetScope: string,\n scopeFilter: string[],\n contextLabel?: string,\n ): Promise<void> {\n const now = Date.now();\n const nowIso = new Date(now).toISOString();\n\n // 1. Demote + expire the contradicted memory\n const existing = await this.store.getById(matchId, scopeFilter);\n if (existing) {\n const meta = parseSmartMetadata(existing.metadata, existing);\n const supportInfo = parseSupportInfo(meta.support_info);\n const updated = updateSupportStats(supportInfo, contextLabel, \"contradict\");\n meta.support_info = updated;\n meta.expired_at = nowIso;\n meta.expired_reason = `contradicted by: ${candidate.abstract.slice(0, 120)}`;\n meta.superseded_by_session = sessionKey;\n await this.store.update(\n matchId,\n {\n importance: Math.max(0.05, (existing.importance ?? 0.7) * 0.2),\n metadata: stringifySmartMetadata(meta),\n },\n scopeFilter,\n );\n\n // 2. Expire in Graphiti (fire-and-forget)\n if (process.env.GRAPHITI_ENABLED === \"true\") {\n const graphitiBase = process.env.GRAPHITI_BASE_URL || \"http://127.0.0.1:18799\";\n fetch(`${graphitiBase}/facts/expire`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n text: existing.text,\n expired_at: nowIso,\n reason: `contradicted: ${candidate.abstract.slice(0, 80)}`,\n }),\n signal: AbortSignal.timeout(5000),\n }).catch(() => {});\n }\n\n this.log(\n `memory-pro: smart-extractor: expired old memory ${matchId.slice(0, 8)} (imp ${(existing.importance ?? 0.7).toFixed(2)}\u2192${Math.max(0.05, (existing.importance ?? 0.7) * 0.2).toFixed(2)})`,\n );\n }\n\n // 3. Store the new (contradicting) entry with supersedes relation\n const storeCategory = this.mapToStoreCategory(candidate.category);\n const metadata = stringifySmartMetadata({\n l0_abstract: candidate.abstract,\n l1_overview: candidate.overview,\n l2_content: candidate.content,\n memory_category: candidate.category,\n tier: \"working\" as const,\n access_count: 0,\n confidence: 0.85,\n last_accessed_at: now,\n source_session: sessionKey,\n contexts: contextLabel ? [contextLabel] : [],\n relations: [\n { type: \"contradicts\", targetId: matchId },\n { type: \"supersedes\", targetId: matchId },\n ],\n valid_from: nowIso,\n });\n\n await this.store.store({\n text: candidate.abstract,\n vector,\n category: storeCategory,\n scope: targetScope,\n importance: Math.min(1.0, this.getDefaultImportance(candidate.category) + 0.1),\n metadata,\n });\n\n this.log(\n `memory-pro: smart-extractor: contradict [${contextLabel || \"general\"}] superseded ${matchId.slice(0, 8)} \u2192 new entry (imp ${(this.getDefaultImportance(candidate.category) + 0.1).toFixed(2)})`,\n );\n }\n\n // --------------------------------------------------------------------------\n // Store Helper\n // --------------------------------------------------------------------------\n\n /**\n * Store a candidate memory as a new entry with L0/L1/L2 metadata.\n */\n private async storeCandidate(\n candidate: CandidateMemory,\n vector: number[],\n sessionKey: string,\n targetScope: string,\n ): Promise<void> {\n // Map 6-category to existing store categories for backward compatibility\n const storeCategory = this.mapToStoreCategory(candidate.category);\n\n const metadata = stringifySmartMetadata(\n buildSmartMetadata(\n {\n text: candidate.abstract,\n category: this.mapToStoreCategory(candidate.category),\n },\n {\n l0_abstract: candidate.abstract,\n l1_overview: candidate.overview,\n l2_content: candidate.content,\n memory_category: candidate.category,\n tier: \"working\",\n access_count: 0,\n confidence: 0.7,\n source_session: sessionKey,\n },\n ),\n );\n\n await this.store.store({\n text: candidate.abstract, // L0 used as the searchable text\n vector,\n category: storeCategory,\n scope: targetScope,\n importance: this.getDefaultImportance(candidate.category),\n metadata,\n });\n\n this.log(\n `memory-pro: smart-extractor: created [${candidate.category}] ${candidate.abstract.slice(0, 60)}`,\n );\n }\n\n /**\n * Map 6-category to existing 5-category store type for backward compatibility.\n */\n private mapToStoreCategory(\n category: MemoryCategory,\n ): \"preference\" | \"fact\" | \"decision\" | \"entity\" | \"other\" {\n switch (category) {\n case \"profile\":\n return \"fact\";\n case \"preferences\":\n return \"preference\";\n case \"entities\":\n return \"entity\";\n case \"events\":\n return \"decision\";\n case \"cases\":\n return \"fact\";\n case \"patterns\":\n return \"other\";\n default:\n return \"other\";\n }\n }\n\n /**\n * Get default importance score by category.\n */\n private getDefaultImportance(category: MemoryCategory): number {\n switch (category) {\n case \"profile\":\n return 0.9; // Identity is very important\n case \"preferences\":\n return 0.8;\n case \"entities\":\n return 0.7;\n case \"events\":\n return 0.6;\n case \"cases\":\n return 0.8; // Problem-solution pairs are high value\n case \"patterns\":\n return 0.85; // Reusable processes are high value\n default:\n return 0.5;\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["existing"]
7
+ }
@@ -0,0 +1,103 @@
1
+ import type { MemoryCategory, MemoryTier } from "./memory-categories.js";
2
+ import type { DecayableMemory } from "./decay-engine.js";
3
+ type LegacyStoreCategory = "preference" | "fact" | "decision" | "entity" | "other" | "reflection";
4
+ type EntryLike = {
5
+ text?: string;
6
+ category?: LegacyStoreCategory;
7
+ importance?: number;
8
+ timestamp?: number;
9
+ metadata?: string;
10
+ };
11
+ export interface SmartMemoryMetadata {
12
+ l0_abstract: string;
13
+ l1_overview: string;
14
+ l2_content: string;
15
+ memory_category: MemoryCategory;
16
+ tier: MemoryTier;
17
+ access_count: number;
18
+ confidence: number;
19
+ last_accessed_at: number;
20
+ source_session?: string;
21
+ /** Emotional salience score (0-1). Higher = more emotionally significant = decays slower.
22
+ * Computed at store time via heuristic rules (zero LLM cost). */
23
+ emotional_salience: number;
24
+ [key: string]: unknown;
25
+ }
26
+ export interface LifecycleMemory {
27
+ id: string;
28
+ importance: number;
29
+ confidence: number;
30
+ tier: MemoryTier;
31
+ accessCount: number;
32
+ createdAt: number;
33
+ lastAccessedAt: number;
34
+ emotionalSalience: number;
35
+ }
36
+ /**
37
+ * Compute emotional salience from text + category.
38
+ * Returns 0-1. Higher = more emotionally charged / personally significant.
39
+ * Pure heuristic, no LLM call.
40
+ */
41
+ export declare function computeEmotionalSalience(text: string, category?: string, importance?: number): number;
42
+ /**
43
+ * Calibrate LLM-returned emotion valence with rule-based post-processing.
44
+ *
45
+ * Fixes two common LLM failure modes:
46
+ * 1. Strong emotional text scored as neutral (0.4-0.6) → push toward extremes
47
+ * 2. Pure factual/data text scored as emotional → compress toward neutral
48
+ *
49
+ * @param text - The memory text
50
+ * @param rawValence - LLM-returned valence (0-1, 0.5 = neutral)
51
+ * @returns Calibrated valence (0-1)
52
+ */
53
+ export declare function calibrateEmotion(text: string, rawValence: number): number;
54
+ export declare function reverseMapLegacyCategory(oldCategory: LegacyStoreCategory | undefined, text?: string): MemoryCategory;
55
+ export declare function parseSmartMetadata(rawMetadata: string | undefined, entry?: EntryLike): SmartMemoryMetadata;
56
+ export declare function buildSmartMetadata(entry: EntryLike, patch?: Partial<SmartMemoryMetadata>): SmartMemoryMetadata;
57
+ export declare function stringifySmartMetadata(metadata: SmartMemoryMetadata | Record<string, unknown>): string;
58
+ export declare function toLifecycleMemory(id: string, entry: EntryLike): LifecycleMemory;
59
+ /**
60
+ * Parse a memory entry into both a DecayableMemory (for the decay engine)
61
+ * and the raw SmartMemoryMetadata (for in-place mutation before write-back).
62
+ */
63
+ export declare function getDecayableFromEntry(entry: EntryLike & {
64
+ id?: string;
65
+ }): {
66
+ memory: DecayableMemory;
67
+ meta: SmartMemoryMetadata;
68
+ };
69
+ /** Predefined context vocabulary for support slices */
70
+ export declare const SUPPORT_CONTEXT_VOCABULARY: readonly ["general", "morning", "afternoon", "evening", "night", "weekday", "weekend", "work", "leisure", "summer", "winter", "travel"];
71
+ export type SupportContext = (typeof SUPPORT_CONTEXT_VOCABULARY)[number] | string;
72
+ /** Max number of context slices per memory to prevent metadata bloat */
73
+ export declare const MAX_SUPPORT_SLICES = 8;
74
+ /** A single context-specific support slice */
75
+ export interface ContextualSupport {
76
+ context: SupportContext;
77
+ confirmations: number;
78
+ contradictions: number;
79
+ strength: number;
80
+ last_observed_at: number;
81
+ }
82
+ /** V2 support info with per-context slices */
83
+ export interface SupportInfoV2 {
84
+ global_strength: number;
85
+ total_observations: number;
86
+ slices: ContextualSupport[];
87
+ }
88
+ /**
89
+ * Normalize a raw context label to a canonical context.
90
+ * Maps common variants and falls back to "general".
91
+ */
92
+ export declare function normalizeContext(raw: string | undefined): SupportContext;
93
+ /**
94
+ * Parse support_info from metadata JSON. Handles V1 (flat) → V2 (sliced) migration.
95
+ */
96
+ export declare function parseSupportInfo(raw: unknown): SupportInfoV2;
97
+ /**
98
+ * Update support stats for a specific context.
99
+ * Returns a new SupportInfoV2 with the updated slice.
100
+ */
101
+ export declare function updateSupportStats(existing: SupportInfoV2, contextLabel: string | undefined, event: "support" | "contradict"): SupportInfoV2;
102
+ export {};
103
+ //# sourceMappingURL=smart-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-metadata.d.ts","sourceRoot":"","sources":["../../src/smart-metadata.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,361 @@
1
+ function clamp01(value, fallback) {
2
+ const n = typeof value === "number" ? value : Number(value);
3
+ if (!Number.isFinite(n)) return fallback;
4
+ return Math.min(1, Math.max(0, n));
5
+ }
6
+ function clampCount(value, fallback = 0) {
7
+ const n = typeof value === "number" ? value : Number(value);
8
+ if (!Number.isFinite(n) || n < 0) return fallback;
9
+ return Math.floor(n);
10
+ }
11
+ const SALIENCE_BOOSTERS = [
12
+ // Decisions and commitments
13
+ { pattern: /\b(决定|决策|confirmed|decided|commit|approved|批了|拍板|定了)\b/i, boost: 0.3 },
14
+ // Strong emotions
15
+ { pattern: /\b(震惊|惊喜|愤怒|失望|兴奋|amazing|shocked|frustrated|excited|worried|担心)\b/i, boost: 0.25 },
16
+ // First-time events
17
+ { pattern: /\b(第一次|首次|first time|first ever|从未|never before)\b/i, boost: 0.25 },
18
+ // Financial significance
19
+ { pattern: /\b(\d+万|\d+亿|\$[\d,.]+[MBK]|估值|valuation|投资|持仓)\b/i, boost: 0.2 },
20
+ // People and relationships (user-configurable entity names)
21
+ { pattern: /\b(colleague|partner|mentor|friend|manager|teammate)\b/i, boost: 0.15 },
22
+ // Lessons learned / mistakes
23
+ { pattern: /\b(教训|踩坑|pitfall|lesson|mistake|bug|故障|挂了|崩了)\b/i, boost: 0.2 },
24
+ // Preferences and identity
25
+ { pattern: /\b(喜欢|讨厌|偏好|prefer|hate|love|always|never)\b/i, boost: 0.15 },
26
+ // Exclamation / emphasis (emotional weight)
27
+ { pattern: /[!!]{2,}|‼️|⚠️|🔴|💀/, boost: 0.1 }
28
+ ];
29
+ const SALIENCE_DAMPENERS = [
30
+ { pattern: /\b(heartbeat|HEARTBEAT_OK)\b/i, dampen: 0.2 },
31
+ { pattern: /\b(cron|restart|gateway|status)\b/i, dampen: 0.1 },
32
+ { pattern: /\b(debug|stack trace|npm|node_modules|\.tsx?|\.jsx?)\b/i, dampen: 0.1 },
33
+ { pattern: /^\[?(Updated|Added|Removed|Fixed|Set)\]?\s.{0,30}$/i, dampen: 0.15 }
34
+ // short auto-generated entries only
35
+ ];
36
+ function computeEmotionalSalience(text, category, importance) {
37
+ let score = 0.35;
38
+ if (category === "decision") score += 0.15;
39
+ if (category === "preference") score += 0.1;
40
+ if (category === "reflection") score += 0.1;
41
+ if (typeof importance === "number" && importance > 0.8) score += 0.1;
42
+ if (typeof importance === "number" && importance > 0.9) score += 0.05;
43
+ for (const { pattern, boost } of SALIENCE_BOOSTERS) {
44
+ if (pattern.test(text)) score += boost;
45
+ }
46
+ for (const { pattern, dampen } of SALIENCE_DAMPENERS) {
47
+ if (pattern.test(text)) score -= dampen;
48
+ }
49
+ return Math.min(1, Math.max(0, score));
50
+ }
51
+ const STRONG_EMOTION_PATTERNS = [
52
+ /太好了|太棒了|amazing|incredible|wonderful|fantastic|excellent/i,
53
+ /terrible|horrible|awful|disgusting|devastating/i,
54
+ /fuck|shit|damn|靠|卧槽|我去|妈的/i,
55
+ /哈哈哈|lol|lmao|rofl|😂|🤣/i,
56
+ /[!!]{3,}/,
57
+ /heartbroken|ecstatic|furious|thrilled|terrified/i,
58
+ /崩溃|暴怒|狂喜|绝望|震惊|兴奋死了/i
59
+ ];
60
+ const FACTUAL_PATTERNS = [
61
+ /估值|revenue|valuation|profit|loss|margin/i,
62
+ /[$¥€£]/,
63
+ /%/
64
+ ];
65
+ function calibrateEmotion(text, rawValence) {
66
+ if (!Number.isFinite(rawValence)) return 0.5;
67
+ const hasStrongEmotion = STRONG_EMOTION_PATTERNS.some((p) => p.test(text));
68
+ const digitChars = (text.match(/\d/g) || []).length;
69
+ const digitRatio = text.length > 0 ? digitChars / text.length : 0;
70
+ const hasFactualSignals = digitRatio > 0.3 || FACTUAL_PATTERNS.some((p) => p.test(text));
71
+ let calibrated = rawValence;
72
+ if (hasStrongEmotion && rawValence >= 0.4 && rawValence <= 0.6) {
73
+ calibrated = rawValence <= 0.5 ? 0.3 : 0.7;
74
+ }
75
+ if (hasFactualSignals && !hasStrongEmotion) {
76
+ if (calibrated < 0.45) calibrated = 0.45;
77
+ if (calibrated > 0.55) calibrated = 0.55;
78
+ }
79
+ return Math.min(1, Math.max(0, calibrated));
80
+ }
81
+ function normalizeTier(value) {
82
+ switch (value) {
83
+ case "core":
84
+ case "working":
85
+ case "peripheral":
86
+ return value;
87
+ default:
88
+ return "working";
89
+ }
90
+ }
91
+ function reverseMapLegacyCategory(oldCategory, text = "") {
92
+ switch (oldCategory) {
93
+ case "preference":
94
+ return "preferences";
95
+ case "entity":
96
+ return "entities";
97
+ case "decision":
98
+ return "events";
99
+ case "other":
100
+ return "patterns";
101
+ case "fact":
102
+ if (/\b(my |i am |i'm |name is |叫我|我的|我是)\b/i.test(text) && text.length < 200) {
103
+ return "profile";
104
+ }
105
+ return "cases";
106
+ default:
107
+ return "patterns";
108
+ }
109
+ }
110
+ function defaultOverview(text) {
111
+ return `- ${text}`;
112
+ }
113
+ function normalizeText(value, fallback) {
114
+ return typeof value === "string" && value.trim() ? value.trim() : fallback;
115
+ }
116
+ function parseSmartMetadata(rawMetadata, entry = {}) {
117
+ let parsed = {};
118
+ if (rawMetadata) {
119
+ try {
120
+ const obj = JSON.parse(rawMetadata);
121
+ if (obj && typeof obj === "object") {
122
+ parsed = obj;
123
+ }
124
+ } catch {
125
+ parsed = {};
126
+ }
127
+ }
128
+ const text = entry.text ?? "";
129
+ const timestamp = typeof entry.timestamp === "number" && Number.isFinite(entry.timestamp) ? entry.timestamp : Date.now();
130
+ const memoryCategory = reverseMapLegacyCategory(entry.category, text);
131
+ const l0 = normalizeText(parsed.l0_abstract, text);
132
+ const l2 = normalizeText(parsed.l2_content, text);
133
+ const normalized = {
134
+ ...parsed,
135
+ l0_abstract: l0,
136
+ l1_overview: normalizeText(parsed.l1_overview, defaultOverview(l0)),
137
+ l2_content: l2,
138
+ memory_category: typeof parsed.memory_category === "string" ? parsed.memory_category : memoryCategory,
139
+ tier: normalizeTier(parsed.tier),
140
+ access_count: clampCount(parsed.access_count, 0),
141
+ confidence: clamp01(parsed.confidence, 0.7),
142
+ last_accessed_at: clampCount(parsed.last_accessed_at, timestamp),
143
+ source_session: typeof parsed.source_session === "string" ? parsed.source_session : void 0,
144
+ emotional_salience: clamp01(
145
+ parsed.emotional_salience,
146
+ computeEmotionalSalience(text, entry.category, entry.importance)
147
+ )
148
+ };
149
+ return normalized;
150
+ }
151
+ function buildSmartMetadata(entry, patch = {}) {
152
+ const base = parseSmartMetadata(entry.metadata, entry);
153
+ const text = entry.text ?? "";
154
+ const rawSalience = clamp01(
155
+ patch.emotional_salience ?? base.emotional_salience,
156
+ base.emotional_salience
157
+ );
158
+ const calibratedSalience = calibrateEmotion(text, rawSalience);
159
+ return {
160
+ ...base,
161
+ ...patch,
162
+ l0_abstract: normalizeText(patch.l0_abstract, base.l0_abstract),
163
+ l1_overview: normalizeText(patch.l1_overview, base.l1_overview),
164
+ l2_content: normalizeText(patch.l2_content, base.l2_content),
165
+ memory_category: typeof patch.memory_category === "string" ? patch.memory_category : base.memory_category,
166
+ tier: normalizeTier(patch.tier ?? base.tier),
167
+ access_count: clampCount(patch.access_count, base.access_count),
168
+ confidence: clamp01(patch.confidence, base.confidence),
169
+ last_accessed_at: clampCount(
170
+ patch.last_accessed_at,
171
+ base.last_accessed_at || entry.timestamp || Date.now()
172
+ ),
173
+ source_session: typeof patch.source_session === "string" ? patch.source_session : base.source_session,
174
+ emotional_salience: calibratedSalience
175
+ };
176
+ }
177
+ const MAX_SOURCES = 20;
178
+ const MAX_HISTORY = 50;
179
+ const MAX_RELATIONS = 16;
180
+ function stringifySmartMetadata(metadata) {
181
+ const capped = { ...metadata };
182
+ if (Array.isArray(capped.sources) && capped.sources.length > MAX_SOURCES) {
183
+ capped.sources = capped.sources.slice(-MAX_SOURCES);
184
+ }
185
+ if (Array.isArray(capped.history) && capped.history.length > MAX_HISTORY) {
186
+ capped.history = capped.history.slice(-MAX_HISTORY);
187
+ }
188
+ if (Array.isArray(capped.relations) && capped.relations.length > MAX_RELATIONS) {
189
+ capped.relations = capped.relations.slice(0, MAX_RELATIONS);
190
+ }
191
+ return JSON.stringify(capped);
192
+ }
193
+ function toLifecycleMemory(id, entry) {
194
+ const metadata = parseSmartMetadata(entry.metadata, entry);
195
+ const createdAt = typeof entry.timestamp === "number" && Number.isFinite(entry.timestamp) ? entry.timestamp : Date.now();
196
+ return {
197
+ id,
198
+ importance: typeof entry.importance === "number" && Number.isFinite(entry.importance) ? entry.importance : 0.7,
199
+ confidence: metadata.confidence,
200
+ tier: metadata.tier,
201
+ accessCount: metadata.access_count,
202
+ createdAt,
203
+ lastAccessedAt: metadata.last_accessed_at || createdAt,
204
+ emotionalSalience: metadata.emotional_salience
205
+ };
206
+ }
207
+ function getDecayableFromEntry(entry) {
208
+ const meta = parseSmartMetadata(entry.metadata, entry);
209
+ const createdAt = typeof entry.timestamp === "number" && Number.isFinite(entry.timestamp) ? entry.timestamp : Date.now();
210
+ const memory = {
211
+ id: entry.id ?? "",
212
+ importance: typeof entry.importance === "number" && Number.isFinite(entry.importance) ? entry.importance : 0.7,
213
+ confidence: meta.confidence,
214
+ tier: meta.tier,
215
+ accessCount: meta.access_count,
216
+ createdAt,
217
+ lastAccessedAt: meta.last_accessed_at || createdAt,
218
+ emotionalSalience: meta.emotional_salience
219
+ };
220
+ return { memory, meta };
221
+ }
222
+ const SUPPORT_CONTEXT_VOCABULARY = [
223
+ "general",
224
+ "morning",
225
+ "afternoon",
226
+ "evening",
227
+ "night",
228
+ "weekday",
229
+ "weekend",
230
+ "work",
231
+ "leisure",
232
+ "summer",
233
+ "winter",
234
+ "travel"
235
+ ];
236
+ const MAX_SUPPORT_SLICES = 8;
237
+ function normalizeContext(raw) {
238
+ if (!raw || !raw.trim()) return "general";
239
+ const lower = raw.trim().toLowerCase();
240
+ if (SUPPORT_CONTEXT_VOCABULARY.includes(lower)) {
241
+ return lower;
242
+ }
243
+ const aliases = {
244
+ "\u65E9\u4E0A": "morning",
245
+ "\u4E0A\u5348": "morning",
246
+ "\u65E9\u6668": "morning",
247
+ "\u4E0B\u5348": "afternoon",
248
+ "\u508D\u665A": "evening",
249
+ "\u665A\u4E0A": "evening",
250
+ "\u6DF1\u591C": "night",
251
+ "\u591C\u665A": "night",
252
+ "\u51CC\u6668": "night",
253
+ "\u5DE5\u4F5C\u65E5": "weekday",
254
+ "\u5E73\u65F6": "weekday",
255
+ "\u5468\u672B": "weekend",
256
+ "\u5047\u65E5": "weekend",
257
+ "\u4F11\u606F\u65E5": "weekend",
258
+ "\u5DE5\u4F5C": "work",
259
+ "\u4E0A\u73ED": "work",
260
+ "\u529E\u516C": "work",
261
+ "\u4F11\u95F2": "leisure",
262
+ "\u653E\u677E": "leisure",
263
+ "\u4F11\u606F": "leisure",
264
+ "\u590F\u5929": "summer",
265
+ "\u590F\u5B63": "summer",
266
+ "\u51AC\u5929": "winter",
267
+ "\u51AC\u5B63": "winter",
268
+ "\u65C5\u884C": "travel",
269
+ "\u51FA\u5DEE": "travel",
270
+ "\u65C5\u6E38": "travel"
271
+ };
272
+ return aliases[lower] || lower;
273
+ }
274
+ function parseSupportInfo(raw) {
275
+ const defaultV2 = {
276
+ global_strength: 0.5,
277
+ total_observations: 0,
278
+ slices: []
279
+ };
280
+ if (!raw || typeof raw !== "object") return defaultV2;
281
+ const obj = raw;
282
+ if (Array.isArray(obj.slices)) {
283
+ return {
284
+ global_strength: typeof obj.global_strength === "number" ? obj.global_strength : 0.5,
285
+ total_observations: typeof obj.total_observations === "number" ? obj.total_observations : 0,
286
+ slices: obj.slices.filter(
287
+ (s) => s && typeof s.context === "string"
288
+ ).map((s) => ({
289
+ context: String(s.context),
290
+ confirmations: typeof s.confirmations === "number" && s.confirmations >= 0 ? s.confirmations : 0,
291
+ contradictions: typeof s.contradictions === "number" && s.contradictions >= 0 ? s.contradictions : 0,
292
+ strength: typeof s.strength === "number" && s.strength >= 0 && s.strength <= 1 ? s.strength : 0.5,
293
+ last_observed_at: typeof s.last_observed_at === "number" ? s.last_observed_at : Date.now()
294
+ }))
295
+ };
296
+ }
297
+ const conf = typeof obj.confirmations === "number" ? obj.confirmations : 0;
298
+ const contra = typeof obj.contradictions === "number" ? obj.contradictions : 0;
299
+ const total = conf + contra;
300
+ if (total === 0) return defaultV2;
301
+ return {
302
+ global_strength: total > 0 ? conf / total : 0.5,
303
+ total_observations: total,
304
+ slices: [{
305
+ context: "general",
306
+ confirmations: conf,
307
+ contradictions: contra,
308
+ strength: total > 0 ? conf / total : 0.5,
309
+ last_observed_at: Date.now()
310
+ }]
311
+ };
312
+ }
313
+ function updateSupportStats(existing, contextLabel, event) {
314
+ const ctx = normalizeContext(contextLabel);
315
+ const base = { ...existing, slices: [...existing.slices.map((s) => ({ ...s }))] };
316
+ let slice = base.slices.find((s) => s.context === ctx);
317
+ if (!slice) {
318
+ slice = { context: ctx, confirmations: 0, contradictions: 0, strength: 0.5, last_observed_at: Date.now() };
319
+ base.slices.push(slice);
320
+ }
321
+ if (event === "support") slice.confirmations++;
322
+ else slice.contradictions++;
323
+ const sliceTotal = slice.confirmations + slice.contradictions;
324
+ slice.strength = sliceTotal > 0 ? slice.confirmations / sliceTotal : 0.5;
325
+ slice.last_observed_at = Date.now();
326
+ let slices = base.slices;
327
+ let droppedConf = 0, droppedContra = 0;
328
+ if (slices.length > MAX_SUPPORT_SLICES) {
329
+ slices = slices.sort((a, b) => b.last_observed_at - a.last_observed_at);
330
+ const dropped = slices.slice(MAX_SUPPORT_SLICES);
331
+ for (const d of dropped) {
332
+ droppedConf += d.confirmations;
333
+ droppedContra += d.contradictions;
334
+ }
335
+ slices = slices.slice(0, MAX_SUPPORT_SLICES);
336
+ }
337
+ let totalConf = droppedConf, totalContra = droppedContra;
338
+ for (const s of slices) {
339
+ totalConf += s.confirmations;
340
+ totalContra += s.contradictions;
341
+ }
342
+ const totalObs = totalConf + totalContra;
343
+ const global_strength = totalObs > 0 ? totalConf / totalObs : 0.5;
344
+ return { global_strength, total_observations: totalObs, slices };
345
+ }
346
+ export {
347
+ MAX_SUPPORT_SLICES,
348
+ SUPPORT_CONTEXT_VOCABULARY,
349
+ buildSmartMetadata,
350
+ calibrateEmotion,
351
+ computeEmotionalSalience,
352
+ getDecayableFromEntry,
353
+ normalizeContext,
354
+ parseSmartMetadata,
355
+ parseSupportInfo,
356
+ reverseMapLegacyCategory,
357
+ stringifySmartMetadata,
358
+ toLifecycleMemory,
359
+ updateSupportStats
360
+ };
361
+ //# sourceMappingURL=smart-metadata.js.map