@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.
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.d.ts.map +1 -0
- package/{index.ts → dist/index.js} +526 -1333
- package/dist/index.js.map +7 -0
- package/dist/src/access-tracker.d.ts +97 -0
- package/dist/src/access-tracker.d.ts.map +1 -0
- package/dist/src/access-tracker.js +184 -0
- package/dist/src/access-tracker.js.map +7 -0
- package/dist/src/adapters/chroma.d.ts +31 -0
- package/dist/src/adapters/chroma.d.ts.map +1 -0
- package/{src/adapters/chroma.ts → dist/src/adapters/chroma.js} +45 -107
- package/dist/src/adapters/chroma.js.map +7 -0
- package/dist/src/adapters/lancedb.d.ts +29 -0
- package/dist/src/adapters/lancedb.d.ts.map +1 -0
- package/{src/adapters/lancedb.ts → dist/src/adapters/lancedb.js} +41 -109
- package/dist/src/adapters/lancedb.js.map +7 -0
- package/dist/src/adapters/pgvector.d.ts +33 -0
- package/dist/src/adapters/pgvector.d.ts.map +1 -0
- package/{src/adapters/pgvector.ts → dist/src/adapters/pgvector.js} +42 -104
- package/dist/src/adapters/pgvector.js.map +7 -0
- package/dist/src/adapters/qdrant.d.ts +34 -0
- package/dist/src/adapters/qdrant.d.ts.map +1 -0
- package/dist/src/adapters/qdrant.js +132 -0
- package/dist/src/adapters/qdrant.js.map +7 -0
- package/dist/src/adaptive-retrieval.d.ts +14 -0
- package/dist/src/adaptive-retrieval.d.ts.map +1 -0
- package/dist/src/adaptive-retrieval.js +52 -0
- package/dist/src/adaptive-retrieval.js.map +7 -0
- package/dist/src/audit-log.d.ts +56 -0
- package/dist/src/audit-log.d.ts.map +1 -0
- package/dist/src/audit-log.js +139 -0
- package/dist/src/audit-log.js.map +7 -0
- package/dist/src/chunker.d.ts +45 -0
- package/dist/src/chunker.d.ts.map +1 -0
- package/dist/src/chunker.js +157 -0
- package/dist/src/chunker.js.map +7 -0
- package/dist/src/config.d.ts +70 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +142 -0
- package/dist/src/config.js.map +7 -0
- package/dist/src/decay-engine.d.ts +73 -0
- package/dist/src/decay-engine.d.ts.map +1 -0
- package/dist/src/decay-engine.js +119 -0
- package/dist/src/decay-engine.js.map +7 -0
- package/dist/src/embedder.d.ts +94 -0
- package/dist/src/embedder.d.ts.map +1 -0
- package/{src/embedder.ts → dist/src/embedder.js} +119 -317
- package/dist/src/embedder.js.map +7 -0
- package/dist/src/extraction-prompts.d.ts +12 -0
- package/dist/src/extraction-prompts.d.ts.map +1 -0
- package/dist/src/extraction-prompts.js +311 -0
- package/dist/src/extraction-prompts.js.map +7 -0
- package/dist/src/license.d.ts +29 -0
- package/dist/src/license.d.ts.map +1 -0
- package/{src/license.ts → dist/src/license.js} +42 -113
- package/dist/src/license.js.map +7 -0
- package/dist/src/llm-client.d.ts +23 -0
- package/dist/src/llm-client.d.ts.map +1 -0
- package/{src/llm-client.ts → dist/src/llm-client.js} +22 -55
- package/dist/src/llm-client.js.map +7 -0
- package/dist/src/logger.d.ts +33 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +35 -0
- package/dist/src/logger.js.map +7 -0
- package/dist/src/mcp-server.d.ts +16 -0
- package/dist/src/mcp-server.d.ts.map +1 -0
- package/{src/mcp-server.ts → dist/src/mcp-server.js} +81 -181
- package/dist/src/mcp-server.js.map +7 -0
- package/dist/src/memory-categories.d.ts +40 -0
- package/dist/src/memory-categories.d.ts.map +1 -0
- package/dist/src/memory-categories.js +33 -0
- package/dist/src/memory-categories.js.map +7 -0
- package/dist/src/memory-upgrader.d.ts +71 -0
- package/dist/src/memory-upgrader.d.ts.map +1 -0
- package/dist/src/memory-upgrader.js +238 -0
- package/dist/src/memory-upgrader.js.map +7 -0
- package/dist/src/migrate.d.ts +47 -0
- package/dist/src/migrate.d.ts.map +1 -0
- package/{src/migrate.ts → dist/src/migrate.js} +57 -165
- package/dist/src/migrate.js.map +7 -0
- package/dist/src/mnemo.d.ts +67 -0
- package/dist/src/mnemo.d.ts.map +1 -0
- package/dist/src/mnemo.js +66 -0
- package/dist/src/mnemo.js.map +7 -0
- package/dist/src/noise-filter.d.ts +23 -0
- package/dist/src/noise-filter.d.ts.map +1 -0
- package/dist/src/noise-filter.js +62 -0
- package/dist/src/noise-filter.js.map +7 -0
- package/dist/src/noise-prototypes.d.ts +40 -0
- package/dist/src/noise-prototypes.d.ts.map +1 -0
- package/dist/src/noise-prototypes.js +116 -0
- package/dist/src/noise-prototypes.js.map +7 -0
- package/dist/src/observability.d.ts +16 -0
- package/dist/src/observability.d.ts.map +1 -0
- package/dist/src/observability.js +53 -0
- package/dist/src/observability.js.map +7 -0
- package/dist/src/query-tracker.d.ts +27 -0
- package/dist/src/query-tracker.d.ts.map +1 -0
- package/dist/src/query-tracker.js +32 -0
- package/dist/src/query-tracker.js.map +7 -0
- package/dist/src/reflection-event-store.d.ts +44 -0
- package/dist/src/reflection-event-store.d.ts.map +1 -0
- package/dist/src/reflection-event-store.js +50 -0
- package/dist/src/reflection-event-store.js.map +7 -0
- package/dist/src/reflection-item-store.d.ts +58 -0
- package/dist/src/reflection-item-store.d.ts.map +1 -0
- package/dist/src/reflection-item-store.js +69 -0
- package/dist/src/reflection-item-store.js.map +7 -0
- package/dist/src/reflection-mapped-metadata.d.ts +47 -0
- package/dist/src/reflection-mapped-metadata.d.ts.map +1 -0
- package/dist/src/reflection-mapped-metadata.js +40 -0
- package/dist/src/reflection-mapped-metadata.js.map +7 -0
- package/dist/src/reflection-metadata.d.ts +11 -0
- package/dist/src/reflection-metadata.d.ts.map +1 -0
- package/dist/src/reflection-metadata.js +24 -0
- package/dist/src/reflection-metadata.js.map +7 -0
- package/dist/src/reflection-ranking.d.ts +13 -0
- package/dist/src/reflection-ranking.d.ts.map +1 -0
- package/{src/reflection-ranking.ts → dist/src/reflection-ranking.js} +12 -21
- package/dist/src/reflection-ranking.js.map +7 -0
- package/dist/src/reflection-retry.d.ts +30 -0
- package/dist/src/reflection-retry.d.ts.map +1 -0
- package/{src/reflection-retry.ts → dist/src/reflection-retry.js} +24 -64
- package/dist/src/reflection-retry.js.map +7 -0
- package/dist/src/reflection-slices.d.ts +42 -0
- package/dist/src/reflection-slices.d.ts.map +1 -0
- package/{src/reflection-slices.ts → dist/src/reflection-slices.js} +60 -136
- package/dist/src/reflection-slices.js.map +7 -0
- package/dist/src/reflection-store.d.ts +85 -0
- package/dist/src/reflection-store.d.ts.map +1 -0
- package/dist/src/reflection-store.js +407 -0
- package/dist/src/reflection-store.js.map +7 -0
- package/dist/src/resonance-state.d.ts +19 -0
- package/dist/src/resonance-state.d.ts.map +1 -0
- package/{src/resonance-state.ts → dist/src/resonance-state.js} +13 -42
- package/dist/src/resonance-state.js.map +7 -0
- package/dist/src/retriever.d.ts +228 -0
- package/dist/src/retriever.d.ts.map +1 -0
- package/dist/src/retriever.js +1006 -0
- package/dist/src/retriever.js.map +7 -0
- package/dist/src/scopes.d.ts +58 -0
- package/dist/src/scopes.d.ts.map +1 -0
- package/dist/src/scopes.js +252 -0
- package/dist/src/scopes.js.map +7 -0
- package/dist/src/self-improvement-files.d.ts +20 -0
- package/dist/src/self-improvement-files.d.ts.map +1 -0
- package/{src/self-improvement-files.ts → dist/src/self-improvement-files.js} +24 -49
- package/dist/src/self-improvement-files.js.map +7 -0
- package/dist/src/semantic-gate.d.ts +24 -0
- package/dist/src/semantic-gate.d.ts.map +1 -0
- package/dist/src/semantic-gate.js +86 -0
- package/dist/src/semantic-gate.js.map +7 -0
- package/dist/src/session-recovery.d.ts +9 -0
- package/dist/src/session-recovery.d.ts.map +1 -0
- package/{src/session-recovery.ts → dist/src/session-recovery.js} +40 -57
- package/dist/src/session-recovery.js.map +7 -0
- package/dist/src/smart-extractor.d.ts +107 -0
- package/dist/src/smart-extractor.d.ts.map +1 -0
- package/{src/smart-extractor.ts → dist/src/smart-extractor.js} +130 -383
- package/dist/src/smart-extractor.js.map +7 -0
- package/dist/src/smart-metadata.d.ts +103 -0
- package/dist/src/smart-metadata.d.ts.map +1 -0
- package/dist/src/smart-metadata.js +361 -0
- package/dist/src/smart-metadata.js.map +7 -0
- package/dist/src/storage-adapter.d.ts +102 -0
- package/dist/src/storage-adapter.d.ts.map +1 -0
- package/dist/src/storage-adapter.js +22 -0
- package/dist/src/storage-adapter.js.map +7 -0
- package/dist/src/store.d.ts +108 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/store.js +939 -0
- package/dist/src/store.js.map +7 -0
- package/dist/src/tier-manager.d.ts +57 -0
- package/dist/src/tier-manager.d.ts.map +1 -0
- package/dist/src/tier-manager.js +80 -0
- package/dist/src/tier-manager.js.map +7 -0
- package/dist/src/tools.d.ts +43 -0
- package/dist/src/tools.d.ts.map +1 -0
- package/dist/src/tools.js +1075 -0
- package/dist/src/tools.js.map +7 -0
- package/dist/src/wal-recovery.d.ts +30 -0
- package/dist/src/wal-recovery.d.ts.map +1 -0
- package/{src/wal-recovery.ts → dist/src/wal-recovery.js} +26 -79
- package/dist/src/wal-recovery.js.map +7 -0
- package/package.json +21 -2
- package/openclaw.plugin.json +0 -815
- package/src/access-tracker.ts +0 -341
- package/src/adapters/README.md +0 -78
- package/src/adapters/qdrant.ts +0 -191
- package/src/adaptive-retrieval.ts +0 -90
- package/src/audit-log.ts +0 -238
- package/src/chunker.ts +0 -254
- package/src/config.ts +0 -271
- package/src/decay-engine.ts +0 -238
- package/src/extraction-prompts.ts +0 -339
- package/src/memory-categories.ts +0 -71
- package/src/memory-upgrader.ts +0 -388
- package/src/mnemo.ts +0 -142
- package/src/noise-filter.ts +0 -97
- package/src/noise-prototypes.ts +0 -164
- package/src/observability.ts +0 -81
- package/src/query-tracker.ts +0 -57
- package/src/reflection-event-store.ts +0 -98
- package/src/reflection-item-store.ts +0 -112
- package/src/reflection-mapped-metadata.ts +0 -84
- package/src/reflection-metadata.ts +0 -23
- package/src/reflection-store.ts +0 -602
- package/src/retriever.ts +0 -1510
- package/src/scopes.ts +0 -375
- package/src/semantic-gate.ts +0 -121
- package/src/smart-metadata.ts +0 -561
- package/src/storage-adapter.ts +0 -153
- package/src/store.ts +0 -1330
- package/src/tier-manager.ts +0 -189
- package/src/tools.ts +0 -1292
- 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
|