@joshuaswarren/openclaw-engram 9.1.6 → 9.1.8
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/README.md +49 -0
- package/dist/access-cli.js +4 -4
- package/dist/{calibration-LBE4XTN4.js → calibration-QBVYTFTQ.js} +2 -2
- package/dist/{causal-consolidation-5VSMA2L5.js → causal-consolidation-T7EEYKPA.js} +4 -4
- package/dist/{chunk-HPTDE747.js → chunk-5HN5NZMV.js} +271 -12
- package/dist/chunk-5HN5NZMV.js.map +1 -0
- package/dist/{chunk-BIVUV2GJ.js → chunk-UO4TIAAW.js} +4 -4
- package/dist/{chunk-V2E2ORE5.js → chunk-XCAYYSI7.js} +1 -1
- package/dist/{chunk-QX3ELIKM.js → chunk-YKWJUR3I.js} +1 -1
- package/dist/chunk-YKWJUR3I.js.map +1 -0
- package/dist/{engine-3TEQ54VW.js → engine-77ETPFGR.js} +3 -3
- package/dist/fallback-llm-KV6HAJ2N.js +9 -0
- package/dist/index.js +39 -8
- package/dist/index.js.map +1 -1
- package/dist/{storage-7BE5XQTN.js → storage-ZAI7Z232.js} +2 -2
- package/dist/storage-ZAI7Z232.js.map +1 -0
- package/openclaw.plugin.json +71 -0
- package/package.json +1 -1
- package/dist/chunk-HPTDE747.js.map +0 -1
- package/dist/chunk-QX3ELIKM.js.map +0 -1
- /package/dist/{calibration-LBE4XTN4.js.map → calibration-QBVYTFTQ.js.map} +0 -0
- /package/dist/{causal-consolidation-5VSMA2L5.js.map → causal-consolidation-T7EEYKPA.js.map} +0 -0
- /package/dist/{chunk-BIVUV2GJ.js.map → chunk-UO4TIAAW.js.map} +0 -0
- /package/dist/{chunk-V2E2ORE5.js.map → chunk-XCAYYSI7.js.map} +0 -0
- /package/dist/{engine-3TEQ54VW.js.map → engine-77ETPFGR.js.map} +0 -0
- /package/dist/{storage-7BE5XQTN.js.map → fallback-llm-KV6HAJ2N.js.map} +0 -0
package/README.md
CHANGED
|
@@ -266,6 +266,8 @@ These capabilities can be enabled progressively:
|
|
|
266
266
|
- **Shared Context** — Cross-agent memory sharing for multi-agent setups
|
|
267
267
|
- **Identity Continuity** — Consistent agent personality across sessions
|
|
268
268
|
- **Hot/Cold Tiering** — Automatic migration of aging memories to cold storage
|
|
269
|
+
- **Memory Cache** — Process-level singleton cache for `readAllMemories()` — turns 15s disk scans into <100ms cache hits, shared across all sessions
|
|
270
|
+
- **Semantic Consolidation** — Finds clusters of semantically similar memories, synthesizes canonical versions via LLM, archives originals to reduce bloat
|
|
269
271
|
- **Native Knowledge** — Search curated markdown (workspace docs, Obsidian vaults) without extracting into memory
|
|
270
272
|
- **Behavior Loop Tuning** — Runtime self-tuning of extraction and recall parameters
|
|
271
273
|
|
|
@@ -334,6 +336,45 @@ Enable it in your `openclaw.json`:
|
|
|
334
336
|
|
|
335
337
|
Set `parallelMaxResultsPerAgent: 0` to disable an individual agent's results without disabling the feature entirely.
|
|
336
338
|
|
|
339
|
+
### Semantic Consolidation (opt-in)
|
|
340
|
+
|
|
341
|
+
Over time, memory stores accumulate redundant facts — the same information extracted multiple times across sessions, expressed slightly differently. Semantic consolidation finds clusters of similar memories using token overlap, synthesizes a single canonical version via LLM, and archives the originals. This reduces storage bloat, speeds up recall, and improves memory quality.
|
|
342
|
+
|
|
343
|
+
- **Conservative by default** — Only merges when 80%+ token overlap is detected across 3+ memories
|
|
344
|
+
- **LLM synthesis** — Uses your configured model to combine unique information from all cluster members
|
|
345
|
+
- **Safe archival** — Originals are archived (not deleted) with full provenance tracking
|
|
346
|
+
- **Configurable** — Adjust threshold, cluster size, excluded categories, model, and schedule
|
|
347
|
+
- **Excluded categories** — Corrections and commitments are never consolidated (configurable)
|
|
348
|
+
|
|
349
|
+
Enable it in your `openclaw.json`:
|
|
350
|
+
|
|
351
|
+
```jsonc
|
|
352
|
+
{
|
|
353
|
+
"plugins": {
|
|
354
|
+
"entries": {
|
|
355
|
+
"openclaw-engram": {
|
|
356
|
+
"config": {
|
|
357
|
+
"semanticConsolidationEnabled": true
|
|
358
|
+
// Optional tuning:
|
|
359
|
+
// "semanticConsolidationThreshold": 0.8, // 0.8=conservative, 0.6=aggressive
|
|
360
|
+
// "semanticConsolidationModel": "fast", // "auto", "fast", or specific model
|
|
361
|
+
// "semanticConsolidationIntervalHours": 168, // weekly (default)
|
|
362
|
+
// "semanticConsolidationMaxPerRun": 100
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Run manually from the CLI:
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
openclaw engram semantic-consolidate --dry-run # Preview what would be merged
|
|
374
|
+
openclaw engram semantic-consolidate --verbose # Run with detailed output
|
|
375
|
+
openclaw engram semantic-consolidate --threshold 0.6 # Override threshold
|
|
376
|
+
```
|
|
377
|
+
|
|
337
378
|
### Advanced (opt-in)
|
|
338
379
|
|
|
339
380
|
- **Objective-State Recall** — Surfaces file/process/tool state snapshots alongside semantic memory
|
|
@@ -417,6 +458,11 @@ openclaw engram review-disposition <id> --status rejected # Operator review
|
|
|
417
458
|
openclaw engram benchmark recall # Benchmark status and validation
|
|
418
459
|
openclaw engram benchmark-ci-gate # CI gate for regressions
|
|
419
460
|
|
|
461
|
+
# Memory maintenance
|
|
462
|
+
openclaw engram consolidate # Run standard consolidation
|
|
463
|
+
openclaw engram semantic-consolidate # Run semantic dedup consolidation
|
|
464
|
+
openclaw engram semantic-consolidate --dry-run # Preview without changes
|
|
465
|
+
|
|
420
466
|
# Access layer
|
|
421
467
|
openclaw engram access http-serve --token "$TOKEN" # Start HTTP API
|
|
422
468
|
openclaw engram access mcp-serve # Start stdio MCP server
|
|
@@ -441,6 +487,9 @@ All settings live in `openclaw.json` under `plugins.entries.openclaw-engram.conf
|
|
|
441
487
|
| `memoryDir` | `~/.openclaw/workspace/memory/local` | Memory storage root |
|
|
442
488
|
| `memoryOsPreset` | unset | Quick config: `conservative`, `balanced`, `research-max`, `local-llm-heavy` |
|
|
443
489
|
| `lcmEnabled` | `false` | Enable Lossless Context Management (proactive session archive + summary DAG) |
|
|
490
|
+
| `semanticConsolidationEnabled` | `false` | Enable periodic semantic dedup of similar memories |
|
|
491
|
+
| `semanticConsolidationThreshold` | `0.8` | Token overlap threshold (0.8=conservative, 0.6=aggressive) |
|
|
492
|
+
| `semanticConsolidationModel` | `"auto"` | LLM for synthesis: `"auto"`, `"fast"`, or specific model |
|
|
444
493
|
|
|
445
494
|
**[See the full config reference for all 60+ settings](docs/config-reference.md)** including search backend configuration, namespace policies, Memory OS features, governance, evaluation harness, trust zones, causal trajectories, and more.
|
|
446
495
|
|
package/dist/access-cli.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
EngramAccessService,
|
|
4
4
|
Orchestrator,
|
|
5
5
|
parseConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-5HN5NZMV.js";
|
|
7
|
+
import "./chunk-UO4TIAAW.js";
|
|
8
8
|
import "./chunk-IMMYYNXG.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-YKWJUR3I.js";
|
|
10
10
|
import "./chunk-6KX4XLQJ.js";
|
|
11
|
+
import "./chunk-XCAYYSI7.js";
|
|
11
12
|
import "./chunk-MKM2BCQH.js";
|
|
12
|
-
import "./chunk-V2E2ORE5.js";
|
|
13
13
|
import "./chunk-DEIBZP3O.js";
|
|
14
14
|
import "./chunk-SSIIJJKA.js";
|
|
15
15
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// openclaw-engram: Local-first memory plugin
|
|
2
2
|
import {
|
|
3
3
|
FallbackLlmClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XCAYYSI7.js";
|
|
5
5
|
import {
|
|
6
6
|
listJsonFiles
|
|
7
7
|
} from "./chunk-DEIBZP3O.js";
|
|
@@ -233,4 +233,4 @@ export {
|
|
|
233
233
|
runCalibrationIfEnabled,
|
|
234
234
|
synthesizeCalibrationRules
|
|
235
235
|
};
|
|
236
|
-
//# sourceMappingURL=calibration-
|
|
236
|
+
//# sourceMappingURL=calibration-QBVYTFTQ.js.map
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// openclaw-engram: Local-first memory plugin
|
|
2
|
+
import {
|
|
3
|
+
FallbackLlmClient
|
|
4
|
+
} from "./chunk-XCAYYSI7.js";
|
|
2
5
|
import {
|
|
3
6
|
readChainIndex,
|
|
4
7
|
resolveChainsDir
|
|
@@ -6,9 +9,6 @@ import {
|
|
|
6
9
|
import {
|
|
7
10
|
isRecord
|
|
8
11
|
} from "./chunk-MKM2BCQH.js";
|
|
9
|
-
import {
|
|
10
|
-
FallbackLlmClient
|
|
11
|
-
} from "./chunk-V2E2ORE5.js";
|
|
12
12
|
import {
|
|
13
13
|
listJsonFiles,
|
|
14
14
|
readJsonFile
|
|
@@ -203,4 +203,4 @@ export {
|
|
|
203
203
|
deriveCausalPromotionCandidates,
|
|
204
204
|
synthesizeCausalPreferencesViaLlm
|
|
205
205
|
};
|
|
206
|
-
//# sourceMappingURL=causal-consolidation-
|
|
206
|
+
//# sourceMappingURL=causal-consolidation-T7EEYKPA.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
CompoundingEngine,
|
|
4
4
|
SharedContextManager,
|
|
5
5
|
defaultTierMigrationCycleBudget
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UO4TIAAW.js";
|
|
7
7
|
import {
|
|
8
8
|
searchCausalTrajectories
|
|
9
9
|
} from "./chunk-IMMYYNXG.js";
|
|
@@ -21,10 +21,16 @@ import {
|
|
|
21
21
|
rotateMarkdownFileToArchive,
|
|
22
22
|
sanitizeMemoryContent,
|
|
23
23
|
toMemoryPathRel
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-YKWJUR3I.js";
|
|
25
25
|
import {
|
|
26
26
|
GraphIndex
|
|
27
27
|
} from "./chunk-6KX4XLQJ.js";
|
|
28
|
+
import {
|
|
29
|
+
FallbackLlmClient,
|
|
30
|
+
buildChatCompletionTokenLimit,
|
|
31
|
+
extractJsonCandidates,
|
|
32
|
+
shouldAssumeOpenAiChatCompletions
|
|
33
|
+
} from "./chunk-XCAYYSI7.js";
|
|
28
34
|
import {
|
|
29
35
|
BoxBuilder,
|
|
30
36
|
assertIsoRecordedAt,
|
|
@@ -38,12 +44,6 @@ import {
|
|
|
38
44
|
recordStoreDay,
|
|
39
45
|
validateStringRecord
|
|
40
46
|
} from "./chunk-MKM2BCQH.js";
|
|
41
|
-
import {
|
|
42
|
-
FallbackLlmClient,
|
|
43
|
-
buildChatCompletionTokenLimit,
|
|
44
|
-
extractJsonCandidates,
|
|
45
|
-
shouldAssumeOpenAiChatCompletions
|
|
46
|
-
} from "./chunk-V2E2ORE5.js";
|
|
47
47
|
import {
|
|
48
48
|
listJsonFiles,
|
|
49
49
|
listNamedFiles,
|
|
@@ -545,6 +545,15 @@ function parseConfig(raw) {
|
|
|
545
545
|
verifiedRecallEnabled: cfg.verifiedRecallEnabled === true,
|
|
546
546
|
semanticRulePromotionEnabled: cfg.semanticRulePromotionEnabled === true,
|
|
547
547
|
semanticRuleVerificationEnabled: cfg.semanticRuleVerificationEnabled === true,
|
|
548
|
+
semanticConsolidationEnabled: cfg.semanticConsolidationEnabled === true,
|
|
549
|
+
semanticConsolidationModel: typeof cfg.semanticConsolidationModel === "string" && cfg.semanticConsolidationModel.length > 0 ? cfg.semanticConsolidationModel : "auto",
|
|
550
|
+
semanticConsolidationThreshold: typeof cfg.semanticConsolidationThreshold === "number" ? cfg.semanticConsolidationThreshold : 0.8,
|
|
551
|
+
semanticConsolidationMinClusterSize: typeof cfg.semanticConsolidationMinClusterSize === "number" ? Math.max(2, Math.floor(cfg.semanticConsolidationMinClusterSize)) : 3,
|
|
552
|
+
semanticConsolidationExcludeCategories: Array.isArray(cfg.semanticConsolidationExcludeCategories) ? cfg.semanticConsolidationExcludeCategories.filter(
|
|
553
|
+
(c) => typeof c === "string" && c.length > 0
|
|
554
|
+
) : ["correction", "commitment"],
|
|
555
|
+
semanticConsolidationIntervalHours: typeof cfg.semanticConsolidationIntervalHours === "number" ? Math.max(1, Math.floor(cfg.semanticConsolidationIntervalHours)) : 168,
|
|
556
|
+
semanticConsolidationMaxPerRun: typeof cfg.semanticConsolidationMaxPerRun === "number" ? Math.max(0, Math.floor(cfg.semanticConsolidationMaxPerRun)) : 100,
|
|
548
557
|
creationMemoryEnabled: cfg.creationMemoryEnabled === true,
|
|
549
558
|
memoryUtilityLearningEnabled: cfg.memoryUtilityLearningEnabled === true,
|
|
550
559
|
promotionByOutcomeEnabled: cfg.promotionByOutcomeEnabled === true,
|
|
@@ -18750,6 +18759,82 @@ function validateReplayTurn(turn, index) {
|
|
|
18750
18759
|
return issues;
|
|
18751
18760
|
}
|
|
18752
18761
|
|
|
18762
|
+
// src/semantic-consolidation.ts
|
|
18763
|
+
function findSimilarClusters(memories, config) {
|
|
18764
|
+
const excluded = new Set(config.excludeCategories);
|
|
18765
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
18766
|
+
for (const m of memories) {
|
|
18767
|
+
const cat = m.frontmatter.category;
|
|
18768
|
+
if (excluded.has(cat)) continue;
|
|
18769
|
+
if (m.frontmatter.status && m.frontmatter.status !== "active") continue;
|
|
18770
|
+
const list = byCategory.get(cat) ?? [];
|
|
18771
|
+
list.push(m);
|
|
18772
|
+
byCategory.set(cat, list);
|
|
18773
|
+
}
|
|
18774
|
+
const clusters = [];
|
|
18775
|
+
let totalCandidates = 0;
|
|
18776
|
+
for (const [category, mems] of byCategory) {
|
|
18777
|
+
if (totalCandidates >= config.maxPerRun) break;
|
|
18778
|
+
const tokenized = mems.map((m) => ({
|
|
18779
|
+
memory: m,
|
|
18780
|
+
tokens: new Set(normalizeRecallTokens(m.content, []))
|
|
18781
|
+
}));
|
|
18782
|
+
const clustered = /* @__PURE__ */ new Set();
|
|
18783
|
+
for (let i = 0; i < tokenized.length && totalCandidates < config.maxPerRun; i++) {
|
|
18784
|
+
if (clustered.has(tokenized[i].memory.frontmatter.id)) continue;
|
|
18785
|
+
const cluster = [tokenized[i].memory];
|
|
18786
|
+
let totalOverlap = 0;
|
|
18787
|
+
let comparisons = 0;
|
|
18788
|
+
for (let j = i + 1; j < tokenized.length; j++) {
|
|
18789
|
+
if (clustered.has(tokenized[j].memory.frontmatter.id)) continue;
|
|
18790
|
+
const aTokens = tokenized[i].tokens;
|
|
18791
|
+
const bTokens = tokenized[j].tokens;
|
|
18792
|
+
if (aTokens.size === 0 || bTokens.size === 0) continue;
|
|
18793
|
+
const overlap = countRecallTokenOverlap(aTokens, [...bTokens].join(" "));
|
|
18794
|
+
const maxTokens = Math.max(aTokens.size, bTokens.size);
|
|
18795
|
+
const score = maxTokens > 0 ? overlap / maxTokens : 0;
|
|
18796
|
+
if (score >= config.threshold) {
|
|
18797
|
+
cluster.push(tokenized[j].memory);
|
|
18798
|
+
totalOverlap += score;
|
|
18799
|
+
comparisons++;
|
|
18800
|
+
if (totalCandidates + cluster.length >= config.maxPerRun) break;
|
|
18801
|
+
}
|
|
18802
|
+
}
|
|
18803
|
+
if (cluster.length >= config.minClusterSize) {
|
|
18804
|
+
for (const m of cluster) clustered.add(m.frontmatter.id);
|
|
18805
|
+
clusters.push({
|
|
18806
|
+
category,
|
|
18807
|
+
memories: cluster,
|
|
18808
|
+
overlapScore: comparisons > 0 ? totalOverlap / comparisons : 0
|
|
18809
|
+
});
|
|
18810
|
+
totalCandidates += cluster.length;
|
|
18811
|
+
}
|
|
18812
|
+
}
|
|
18813
|
+
}
|
|
18814
|
+
return clusters;
|
|
18815
|
+
}
|
|
18816
|
+
function buildConsolidationPrompt(cluster) {
|
|
18817
|
+
const memoryTexts = cluster.memories.map(
|
|
18818
|
+
(m, i) => `Memory ${i + 1} (${m.frontmatter.id}, created ${m.frontmatter.created}):
|
|
18819
|
+
${m.content}`
|
|
18820
|
+
).join("\n\n");
|
|
18821
|
+
return `You are a memory consolidation system. The following ${cluster.memories.length} memories in the "${cluster.category}" category contain overlapping information.
|
|
18822
|
+
|
|
18823
|
+
Synthesize them into ONE canonical memory that:
|
|
18824
|
+
1. Preserves ALL unique information from every source memory
|
|
18825
|
+
2. Removes redundancy and repetition
|
|
18826
|
+
3. Uses clear, concise language
|
|
18827
|
+
4. Maintains the same category and tone
|
|
18828
|
+
5. Does NOT add information that isn't in the sources
|
|
18829
|
+
|
|
18830
|
+
${memoryTexts}
|
|
18831
|
+
|
|
18832
|
+
Write ONLY the consolidated memory content (no metadata, no explanation, no preamble):`;
|
|
18833
|
+
}
|
|
18834
|
+
function parseConsolidationResponse(response) {
|
|
18835
|
+
return response.trim();
|
|
18836
|
+
}
|
|
18837
|
+
|
|
18753
18838
|
// src/conversation-index/chunker.ts
|
|
18754
18839
|
function chunkTranscriptEntries(sessionKey, entries, opts) {
|
|
18755
18840
|
const maxChars = Math.max(500, opts.maxChars);
|
|
@@ -21098,6 +21183,140 @@ var Orchestrator = class _Orchestrator {
|
|
|
21098
21183
|
async runConsolidationNow() {
|
|
21099
21184
|
return this.runConsolidation();
|
|
21100
21185
|
}
|
|
21186
|
+
async runSemanticConsolidationNow(options) {
|
|
21187
|
+
return this.runSemanticConsolidation({ ...options, force: true });
|
|
21188
|
+
}
|
|
21189
|
+
async runSemanticConsolidation(options) {
|
|
21190
|
+
const result = {
|
|
21191
|
+
clustersFound: 0,
|
|
21192
|
+
memoriesConsolidated: 0,
|
|
21193
|
+
memoriesArchived: 0,
|
|
21194
|
+
errors: 0,
|
|
21195
|
+
clusters: []
|
|
21196
|
+
};
|
|
21197
|
+
if (!this.config.semanticConsolidationEnabled && !options?.force) {
|
|
21198
|
+
log.debug("[semantic-consolidation] disabled in config");
|
|
21199
|
+
return result;
|
|
21200
|
+
}
|
|
21201
|
+
log.info("[semantic-consolidation] starting run");
|
|
21202
|
+
const allMemories = await this.storage.readAllMemories();
|
|
21203
|
+
if (allMemories.length < 10) {
|
|
21204
|
+
log.debug("[semantic-consolidation] too few memories, skipping");
|
|
21205
|
+
return result;
|
|
21206
|
+
}
|
|
21207
|
+
const threshold = options?.thresholdOverride ?? this.config.semanticConsolidationThreshold;
|
|
21208
|
+
const clusters = findSimilarClusters(allMemories, {
|
|
21209
|
+
threshold,
|
|
21210
|
+
minClusterSize: this.config.semanticConsolidationMinClusterSize,
|
|
21211
|
+
excludeCategories: this.config.semanticConsolidationExcludeCategories,
|
|
21212
|
+
maxPerRun: this.config.semanticConsolidationMaxPerRun
|
|
21213
|
+
});
|
|
21214
|
+
result.clustersFound = clusters.length;
|
|
21215
|
+
result.clusters = clusters;
|
|
21216
|
+
if (clusters.length === 0) {
|
|
21217
|
+
log.info("[semantic-consolidation] no clusters found");
|
|
21218
|
+
return result;
|
|
21219
|
+
}
|
|
21220
|
+
log.info(`[semantic-consolidation] found ${clusters.length} cluster(s)`);
|
|
21221
|
+
if (options?.dryRun) {
|
|
21222
|
+
log.info("[semantic-consolidation] dry run \u2014 skipping LLM synthesis and archival");
|
|
21223
|
+
return result;
|
|
21224
|
+
}
|
|
21225
|
+
const { FallbackLlmClient: FallbackLlmClient2 } = await import("./fallback-llm-KV6HAJ2N.js");
|
|
21226
|
+
const modelSetting = this.config.semanticConsolidationModel;
|
|
21227
|
+
if (modelSetting === "fast" && this.fastLlm) {
|
|
21228
|
+
log.info("[semantic-consolidation] using fast local LLM for synthesis");
|
|
21229
|
+
}
|
|
21230
|
+
const llm = new FallbackLlmClient2(this.config.gatewayConfig);
|
|
21231
|
+
if (!llm.isAvailable() && !(modelSetting === "fast" && this.fastLlm)) {
|
|
21232
|
+
log.warn("[semantic-consolidation] no LLM available \u2014 skipping synthesis");
|
|
21233
|
+
return result;
|
|
21234
|
+
}
|
|
21235
|
+
for (const cluster of clusters) {
|
|
21236
|
+
try {
|
|
21237
|
+
const prompt = buildConsolidationPrompt(cluster);
|
|
21238
|
+
const messages = [
|
|
21239
|
+
{ role: "system", content: "You are a memory consolidation system. Output only the consolidated memory text." },
|
|
21240
|
+
{ role: "user", content: prompt }
|
|
21241
|
+
];
|
|
21242
|
+
const llmOpts = { temperature: 0.2, maxTokens: 2e3 };
|
|
21243
|
+
let response = null;
|
|
21244
|
+
if (modelSetting === "fast" && this.fastLlm) {
|
|
21245
|
+
const fastResult = await this.fastLlm.chatCompletion(messages, {
|
|
21246
|
+
operation: "semantic-consolidation",
|
|
21247
|
+
maxTokens: llmOpts.maxTokens,
|
|
21248
|
+
temperature: llmOpts.temperature
|
|
21249
|
+
});
|
|
21250
|
+
response = fastResult ? { content: fastResult.content } : null;
|
|
21251
|
+
} else {
|
|
21252
|
+
response = await llm.chatCompletion(messages, llmOpts);
|
|
21253
|
+
}
|
|
21254
|
+
if (!response?.content) {
|
|
21255
|
+
log.warn(`[semantic-consolidation] empty LLM response for cluster in "${cluster.category}"`);
|
|
21256
|
+
result.errors++;
|
|
21257
|
+
continue;
|
|
21258
|
+
}
|
|
21259
|
+
const canonicalContent = parseConsolidationResponse(response.content);
|
|
21260
|
+
cluster.canonicalContent = canonicalContent;
|
|
21261
|
+
const sorted = [...cluster.memories].sort(
|
|
21262
|
+
(a, b) => new Date(b.frontmatter.created).getTime() - new Date(a.frontmatter.created).getTime()
|
|
21263
|
+
);
|
|
21264
|
+
const newest = sorted[0];
|
|
21265
|
+
const lineageIds = cluster.memories.map((m) => m.frontmatter.id);
|
|
21266
|
+
const canonicalId = await this.storage.writeMemory(
|
|
21267
|
+
newest.frontmatter.category,
|
|
21268
|
+
canonicalContent,
|
|
21269
|
+
{
|
|
21270
|
+
actor: "semantic-consolidation",
|
|
21271
|
+
confidence: newest.frontmatter.confidence,
|
|
21272
|
+
tags: [...new Set(cluster.memories.flatMap((m) => m.frontmatter.tags ?? []))],
|
|
21273
|
+
source: "semantic-consolidation",
|
|
21274
|
+
lineage: lineageIds
|
|
21275
|
+
}
|
|
21276
|
+
);
|
|
21277
|
+
result.memoriesConsolidated++;
|
|
21278
|
+
for (const m of cluster.memories) {
|
|
21279
|
+
const archiveResult = await this.storage.archiveMemory(m, {
|
|
21280
|
+
actor: "semantic-consolidation",
|
|
21281
|
+
reasonCode: "semantic-consolidation",
|
|
21282
|
+
relatedMemoryIds: [canonicalId]
|
|
21283
|
+
});
|
|
21284
|
+
if (archiveResult) {
|
|
21285
|
+
if (this.contentHashIndex) {
|
|
21286
|
+
this.contentHashIndex.remove(m.content);
|
|
21287
|
+
}
|
|
21288
|
+
await this.embeddingFallback.removeFromIndex(m.frontmatter.id);
|
|
21289
|
+
if (this.config.queryAwareIndexingEnabled && m.path && m.frontmatter?.created) {
|
|
21290
|
+
deindexMemory(
|
|
21291
|
+
this.config.memoryDir,
|
|
21292
|
+
m.path,
|
|
21293
|
+
m.frontmatter.created,
|
|
21294
|
+
m.frontmatter.tags ?? []
|
|
21295
|
+
);
|
|
21296
|
+
}
|
|
21297
|
+
result.memoriesArchived++;
|
|
21298
|
+
}
|
|
21299
|
+
}
|
|
21300
|
+
log.info(
|
|
21301
|
+
`[semantic-consolidation] consolidated ${cluster.memories.length} memories \u2192 ${canonicalId}`
|
|
21302
|
+
);
|
|
21303
|
+
} catch (err) {
|
|
21304
|
+
log.warn(
|
|
21305
|
+
`[semantic-consolidation] cluster processing failed: ${err instanceof Error ? err.message : String(err)}`
|
|
21306
|
+
);
|
|
21307
|
+
result.errors++;
|
|
21308
|
+
}
|
|
21309
|
+
}
|
|
21310
|
+
if (result.memoriesArchived > 0 && this.contentHashIndex) {
|
|
21311
|
+
await this.contentHashIndex.save().catch(
|
|
21312
|
+
(err) => log.warn(`[semantic-consolidation] content-hash index save failed: ${err}`)
|
|
21313
|
+
);
|
|
21314
|
+
}
|
|
21315
|
+
log.info(
|
|
21316
|
+
`[semantic-consolidation] complete: clusters=${result.clustersFound}, consolidated=${result.memoriesConsolidated}, archived=${result.memoriesArchived}, errors=${result.errors}`
|
|
21317
|
+
);
|
|
21318
|
+
return result;
|
|
21319
|
+
}
|
|
21101
21320
|
async waitForExtractionIdle(timeoutMs = 6e4) {
|
|
21102
21321
|
const started = Date.now();
|
|
21103
21322
|
while (this.queueProcessing || this.extractionQueue.length > 0) {
|
|
@@ -22823,7 +23042,7 @@ ${trimmedBody}`;
|
|
|
22823
23042
|
return null;
|
|
22824
23043
|
}
|
|
22825
23044
|
try {
|
|
22826
|
-
const { getCalibrationRulesForRecall, buildCalibrationRecallSection } = await import("./calibration-
|
|
23045
|
+
const { getCalibrationRulesForRecall, buildCalibrationRecallSection } = await import("./calibration-QBVYTFTQ.js");
|
|
22827
23046
|
const rules = await getCalibrationRulesForRecall(this.config.memoryDir);
|
|
22828
23047
|
if (rules.length === 0) {
|
|
22829
23048
|
timings.calibrationRules = "skip(no-rules)";
|
|
@@ -25368,6 +25587,39 @@ _Context: ${topQuestion.context}_`
|
|
|
25368
25587
|
log.info(`archived ${archived} old low-importance facts`);
|
|
25369
25588
|
}
|
|
25370
25589
|
}
|
|
25590
|
+
if (this.config.semanticConsolidationEnabled) {
|
|
25591
|
+
try {
|
|
25592
|
+
const stateFilePath = path40.join(this.config.memoryDir, "state", "semantic-consolidation-last-run.json");
|
|
25593
|
+
let shouldRun = true;
|
|
25594
|
+
try {
|
|
25595
|
+
const stateRaw = await readFile22(stateFilePath, "utf-8");
|
|
25596
|
+
const stateData = JSON.parse(stateRaw);
|
|
25597
|
+
if (stateData.lastRunAt) {
|
|
25598
|
+
const lastRunMs = new Date(stateData.lastRunAt).getTime();
|
|
25599
|
+
const intervalMs = this.config.semanticConsolidationIntervalHours * 60 * 60 * 1e3;
|
|
25600
|
+
if (Date.now() - lastRunMs < intervalMs) {
|
|
25601
|
+
shouldRun = false;
|
|
25602
|
+
log.debug("[semantic-consolidation] skipping \u2014 not enough time since last run");
|
|
25603
|
+
}
|
|
25604
|
+
}
|
|
25605
|
+
} catch {
|
|
25606
|
+
}
|
|
25607
|
+
if (shouldRun) {
|
|
25608
|
+
const semResult = await this.runSemanticConsolidation();
|
|
25609
|
+
if (semResult.memoriesArchived > 0) {
|
|
25610
|
+
log.info(`[semantic-consolidation] archived ${semResult.memoriesArchived} memories during maintenance`);
|
|
25611
|
+
allMemories = await this.storage.readAllMemories();
|
|
25612
|
+
}
|
|
25613
|
+
if (semResult.errors === 0 || semResult.memoriesArchived > 0) {
|
|
25614
|
+
const stateDir2 = path40.join(this.config.memoryDir, "state");
|
|
25615
|
+
await mkdir27(stateDir2, { recursive: true });
|
|
25616
|
+
await writeFile26(stateFilePath, JSON.stringify({ lastRunAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
|
|
25617
|
+
}
|
|
25618
|
+
}
|
|
25619
|
+
} catch (err) {
|
|
25620
|
+
log.warn(`[semantic-consolidation] maintenance pass failed (non-fatal): ${err}`);
|
|
25621
|
+
}
|
|
25622
|
+
}
|
|
25371
25623
|
if (this.config.identityEnabled) {
|
|
25372
25624
|
await this.autoConsolidateIdentity();
|
|
25373
25625
|
}
|
|
@@ -28558,8 +28810,15 @@ var EngramAccessService = class {
|
|
|
28558
28810
|
const lcmSessionKey = namespace !== this.orchestrator.config.defaultNamespace ? `${namespace}:${request.sessionKey}` : request.sessionKey;
|
|
28559
28811
|
let lcmArchived = false;
|
|
28560
28812
|
if (this.orchestrator.lcmEngine && this.orchestrator.lcmEngine.enabled) {
|
|
28561
|
-
|
|
28562
|
-
|
|
28813
|
+
try {
|
|
28814
|
+
const lcmPromise = this.orchestrator.lcmEngine.observeMessages(lcmSessionKey, request.messages);
|
|
28815
|
+
lcmPromise.catch((err) => {
|
|
28816
|
+
log.error(`access-observe LCM archival failed: ${err}`);
|
|
28817
|
+
});
|
|
28818
|
+
lcmArchived = true;
|
|
28819
|
+
} catch (err) {
|
|
28820
|
+
log.error(`access-observe LCM enqueue failed: ${err}`);
|
|
28821
|
+
}
|
|
28563
28822
|
}
|
|
28564
28823
|
let extractionQueued = false;
|
|
28565
28824
|
if (request.skipExtraction !== true) {
|
|
@@ -28726,4 +28985,4 @@ export {
|
|
|
28726
28985
|
EngramAccessInputError,
|
|
28727
28986
|
EngramAccessService
|
|
28728
28987
|
};
|
|
28729
|
-
//# sourceMappingURL=chunk-
|
|
28988
|
+
//# sourceMappingURL=chunk-5HN5NZMV.js.map
|