@joshuaswarren/openclaw-engram 8.3.88 → 8.3.89
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/index.js +400 -144
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +38 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -136,6 +136,7 @@ function parseConfig(raw) {
|
|
|
136
136
|
}
|
|
137
137
|
const sharedCrossSignalSemanticEnabled = cfg.sharedCrossSignalSemanticEnabled === true || cfg.crossSignalsSemanticEnabled === true;
|
|
138
138
|
const sharedCrossSignalSemanticTimeoutMs = typeof cfg.sharedCrossSignalSemanticTimeoutMs === "number" ? Math.max(1, Math.floor(cfg.sharedCrossSignalSemanticTimeoutMs)) : typeof cfg.crossSignalsSemanticTimeoutMs === "number" ? Math.max(1, Math.floor(cfg.crossSignalsSemanticTimeoutMs)) : 4e3;
|
|
139
|
+
const recallPipelineConfig = buildRecallPipelineConfig(cfg);
|
|
139
140
|
return {
|
|
140
141
|
openaiApiKey: apiKey,
|
|
141
142
|
openaiBaseUrl: baseUrl,
|
|
@@ -367,6 +368,8 @@ function parseConfig(raw) {
|
|
|
367
368
|
knowledgeIndexEnabled: cfg.knowledgeIndexEnabled !== false,
|
|
368
369
|
knowledgeIndexMaxEntities: typeof cfg.knowledgeIndexMaxEntities === "number" ? cfg.knowledgeIndexMaxEntities : 40,
|
|
369
370
|
knowledgeIndexMaxChars: typeof cfg.knowledgeIndexMaxChars === "number" ? cfg.knowledgeIndexMaxChars : 4e3,
|
|
371
|
+
recallBudgetChars: recallPipelineConfig.recallBudgetChars,
|
|
372
|
+
recallPipeline: recallPipelineConfig.pipeline,
|
|
370
373
|
entityRelationshipsEnabled: cfg.entityRelationshipsEnabled !== false,
|
|
371
374
|
entityActivityLogEnabled: cfg.entityActivityLogEnabled !== false,
|
|
372
375
|
entityActivityLogMaxEntries: typeof cfg.entityActivityLogMaxEntries === "number" ? cfg.entityActivityLogMaxEntries : 20,
|
|
@@ -454,6 +457,100 @@ function parseConfig(raw) {
|
|
|
454
457
|
tmtSummaryMaxTokens: typeof cfg.tmtSummaryMaxTokens === "number" ? cfg.tmtSummaryMaxTokens : 300
|
|
455
458
|
};
|
|
456
459
|
}
|
|
460
|
+
function clampNonNegativeNumber(value) {
|
|
461
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
462
|
+
return Math.max(0, Math.floor(value));
|
|
463
|
+
}
|
|
464
|
+
function parseRecallSectionEntry(raw) {
|
|
465
|
+
const entry = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
466
|
+
return {
|
|
467
|
+
id: typeof entry.id === "string" ? entry.id.trim() : "",
|
|
468
|
+
enabled: entry.enabled !== false,
|
|
469
|
+
maxChars: entry.maxChars === null ? null : clampNonNegativeNumber(entry.maxChars),
|
|
470
|
+
consolidateTriggerLines: clampNonNegativeNumber(entry.consolidateTriggerLines),
|
|
471
|
+
consolidateTargetLines: clampNonNegativeNumber(entry.consolidateTargetLines),
|
|
472
|
+
maxEntities: clampNonNegativeNumber(entry.maxEntities),
|
|
473
|
+
maxResults: clampNonNegativeNumber(entry.maxResults),
|
|
474
|
+
maxTurns: clampNonNegativeNumber(entry.maxTurns),
|
|
475
|
+
maxTokens: clampNonNegativeNumber(entry.maxTokens),
|
|
476
|
+
lookbackHours: clampNonNegativeNumber(entry.lookbackHours),
|
|
477
|
+
maxCount: clampNonNegativeNumber(entry.maxCount),
|
|
478
|
+
topK: clampNonNegativeNumber(entry.topK),
|
|
479
|
+
timeoutMs: clampNonNegativeNumber(entry.timeoutMs),
|
|
480
|
+
maxPatterns: clampNonNegativeNumber(entry.maxPatterns)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function buildDefaultRecallPipeline(cfg) {
|
|
484
|
+
return [
|
|
485
|
+
{
|
|
486
|
+
id: "shared-context",
|
|
487
|
+
enabled: cfg.sharedContextEnabled === true,
|
|
488
|
+
maxChars: typeof cfg.sharedContextMaxInjectChars === "number" ? Math.max(0, Math.floor(cfg.sharedContextMaxInjectChars)) : 4e3
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
id: "profile",
|
|
492
|
+
enabled: true,
|
|
493
|
+
consolidateTriggerLines: 100,
|
|
494
|
+
consolidateTargetLines: 50
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
id: "identity-continuity",
|
|
498
|
+
enabled: cfg.identityContinuityEnabled === true
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
id: "knowledge-index",
|
|
502
|
+
enabled: cfg.knowledgeIndexEnabled !== false,
|
|
503
|
+
maxChars: typeof cfg.knowledgeIndexMaxChars === "number" ? Math.max(0, Math.floor(cfg.knowledgeIndexMaxChars)) : 4e3,
|
|
504
|
+
maxEntities: typeof cfg.knowledgeIndexMaxEntities === "number" ? Math.max(0, Math.floor(cfg.knowledgeIndexMaxEntities)) : 40
|
|
505
|
+
},
|
|
506
|
+
{ id: "verbatim-artifacts", enabled: cfg.verbatimArtifactsEnabled === true },
|
|
507
|
+
{ id: "memory-boxes", enabled: cfg.memoryBoxesEnabled === true },
|
|
508
|
+
{ id: "temporal-memory-tree", enabled: cfg.temporalMemoryTreeEnabled === true },
|
|
509
|
+
{
|
|
510
|
+
id: "memories",
|
|
511
|
+
enabled: true,
|
|
512
|
+
maxResults: typeof cfg.qmdMaxResults === "number" ? Math.max(0, Math.floor(cfg.qmdMaxResults)) : 8
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
id: "compression-guidelines",
|
|
516
|
+
enabled: cfg.compressionGuidelineLearningEnabled === true
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
id: "transcript",
|
|
520
|
+
enabled: cfg.transcriptEnabled !== false,
|
|
521
|
+
maxTurns: typeof cfg.maxTranscriptTurns === "number" ? Math.max(0, Math.floor(cfg.maxTranscriptTurns)) : 50,
|
|
522
|
+
maxTokens: typeof cfg.maxTranscriptTokens === "number" ? Math.max(0, Math.floor(cfg.maxTranscriptTokens)) : 1e3,
|
|
523
|
+
lookbackHours: typeof cfg.transcriptRecallHours === "number" ? Math.max(0, Math.floor(cfg.transcriptRecallHours)) : 12
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
id: "summaries",
|
|
527
|
+
enabled: cfg.hourlySummariesEnabled !== false,
|
|
528
|
+
maxCount: typeof cfg.maxSummaryCount === "number" ? Math.max(0, Math.floor(cfg.maxSummaryCount)) : 6,
|
|
529
|
+
lookbackHours: typeof cfg.summaryRecallHours === "number" ? Math.max(0, Math.floor(cfg.summaryRecallHours)) : 24
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
id: "conversation-recall",
|
|
533
|
+
enabled: cfg.conversationIndexEnabled === true,
|
|
534
|
+
topK: typeof cfg.conversationRecallTopK === "number" ? Math.max(0, Math.floor(cfg.conversationRecallTopK)) : 3,
|
|
535
|
+
maxChars: typeof cfg.conversationRecallMaxChars === "number" ? Math.max(0, Math.floor(cfg.conversationRecallMaxChars)) : 2500,
|
|
536
|
+
timeoutMs: typeof cfg.conversationRecallTimeoutMs === "number" ? Math.max(0, Math.floor(cfg.conversationRecallTimeoutMs)) : 800
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
id: "compounding",
|
|
540
|
+
enabled: cfg.compoundingEnabled === true && cfg.compoundingInjectEnabled !== false,
|
|
541
|
+
maxPatterns: 40
|
|
542
|
+
},
|
|
543
|
+
{ id: "questions", enabled: cfg.injectQuestions === true }
|
|
544
|
+
];
|
|
545
|
+
}
|
|
546
|
+
function buildRecallPipelineConfig(cfg) {
|
|
547
|
+
const maxMemoryTokens = typeof cfg.maxMemoryTokens === "number" ? Math.max(0, Math.floor(cfg.maxMemoryTokens)) : 2e3;
|
|
548
|
+
const recallBudgetCharsRaw = clampNonNegativeNumber(cfg.recallBudgetChars);
|
|
549
|
+
const recallBudgetChars = recallBudgetCharsRaw ?? maxMemoryTokens * 4;
|
|
550
|
+
const rawPipeline = cfg.recallPipeline;
|
|
551
|
+
const pipeline2 = Array.isArray(rawPipeline) ? rawPipeline.map(parseRecallSectionEntry).filter((entry) => entry.id.length > 0) : buildDefaultRecallPipeline(cfg);
|
|
552
|
+
return { recallBudgetChars, pipeline: pipeline2 };
|
|
553
|
+
}
|
|
457
554
|
|
|
458
555
|
// src/orchestrator.ts
|
|
459
556
|
import path28 from "path";
|
|
@@ -1713,13 +1810,16 @@ var ConsolidationResultSchema = z.object({
|
|
|
1713
1810
|
profileUpdates: z.array(z.string()).describe("New profile statements to add or update"),
|
|
1714
1811
|
entityUpdates: z.array(EntityMentionSchema).describe("Entity updates from consolidation analysis")
|
|
1715
1812
|
});
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1813
|
+
function buildProfileConsolidationResultSchema(targetLines) {
|
|
1814
|
+
return z.object({
|
|
1815
|
+
consolidatedProfile: z.string().describe(
|
|
1816
|
+
`The full consolidated profile as markdown. Preserve all ## section headers. Merge duplicate or near-duplicate bullets into single clear statements. Remove stale or superseded information. Keep the most important and durable observations. Target roughly ${targetLines} lines.`
|
|
1817
|
+
),
|
|
1818
|
+
removedCount: z.number().describe("Number of bullets removed or merged during consolidation"),
|
|
1819
|
+
summary: z.string().describe("Brief summary of what was consolidated")
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
var ProfileConsolidationResultSchema = buildProfileConsolidationResultSchema(50);
|
|
1723
1823
|
var IdentityConsolidationResultSchema = z.object({
|
|
1724
1824
|
learnedPatterns: z.array(z.string()).describe(
|
|
1725
1825
|
"Consolidated behavioral patterns and lessons learned, each a concise standalone statement"
|
|
@@ -2870,13 +2970,13 @@ Respond with valid JSON matching this schema:
|
|
|
2870
2970
|
* The LLM merges duplicates, removes stale info, and preserves section structure.
|
|
2871
2971
|
* Returns the consolidated markdown or null on failure.
|
|
2872
2972
|
*/
|
|
2873
|
-
async consolidateProfile(fullProfileContent) {
|
|
2973
|
+
async consolidateProfile(fullProfileContent, targetLines = 50) {
|
|
2874
2974
|
const pTraceId = crypto.randomUUID();
|
|
2875
2975
|
this.emit({ kind: "llm_start", traceId: pTraceId, model: this.config.model, operation: "profile_consolidation", input: fullProfileContent.slice(0, 2e3) });
|
|
2876
2976
|
const pStartTime = Date.now();
|
|
2877
2977
|
if (this.config.localLlmEnabled) {
|
|
2878
2978
|
try {
|
|
2879
|
-
const localResult = await this.consolidateProfileWithLocalLlm(fullProfileContent);
|
|
2979
|
+
const localResult = await this.consolidateProfileWithLocalLlm(fullProfileContent, targetLines);
|
|
2880
2980
|
if (localResult) {
|
|
2881
2981
|
const durationMs = Date.now() - pStartTime;
|
|
2882
2982
|
this.emit({ kind: "llm_end", traceId: pTraceId, model: this.config.localLlmModel, operation: "profile_consolidation", durationMs });
|
|
@@ -2900,7 +3000,7 @@ Respond with valid JSON matching this schema:
|
|
|
2900
3000
|
pTraceId,
|
|
2901
3001
|
"profile_consolidation",
|
|
2902
3002
|
pStartTime,
|
|
2903
|
-
|
|
3003
|
+
buildProfileConsolidationResultSchema(targetLines),
|
|
2904
3004
|
[
|
|
2905
3005
|
{
|
|
2906
3006
|
role: "system",
|
|
@@ -2911,7 +3011,7 @@ Respond with valid JSON matching this schema:
|
|
|
2911
3011
|
3. REMOVES stale information that has been superseded by newer bullets
|
|
2912
3012
|
4. REMOVES trivial or overly specific operational details that won't be useful across sessions
|
|
2913
3013
|
5. KEEPS the most important, durable observations about the user's preferences, habits, identity, and working style
|
|
2914
|
-
6. Target roughly
|
|
3014
|
+
6. Target roughly ${targetLines} lines \u2014 this is a soft target, prioritize quality over length
|
|
2915
3015
|
7. Write in the same style as the existing profile \u2014 concise bullets, no fluff
|
|
2916
3016
|
|
|
2917
3017
|
The output should be the COMPLETE consolidated profile as valid markdown, starting with "# Behavioral Profile".`
|
|
@@ -2942,13 +3042,13 @@ The output should be the COMPLETE consolidated profile as valid markdown, starti
|
|
|
2942
3042
|
3. REMOVES stale information that has been superseded by newer bullets
|
|
2943
3043
|
4. REMOVES trivial or overly specific operational details that won't be useful across sessions
|
|
2944
3044
|
5. KEEPS the most important, durable observations about the user's preferences, habits, identity, and working style
|
|
2945
|
-
6. Target roughly
|
|
3045
|
+
6. Target roughly ${targetLines} lines \u2014 this is a soft target, prioritize quality over length
|
|
2946
3046
|
7. Write in the same style as the existing profile \u2014 concise bullets, no fluff
|
|
2947
3047
|
|
|
2948
3048
|
The output should be the COMPLETE consolidated profile as valid markdown, starting with "# Behavioral Profile".`,
|
|
2949
3049
|
input: fullProfileContent,
|
|
2950
3050
|
text: {
|
|
2951
|
-
format: zodTextFormat(
|
|
3051
|
+
format: zodTextFormat(buildProfileConsolidationResultSchema(targetLines), "profile_consolidation_result")
|
|
2952
3052
|
}
|
|
2953
3053
|
});
|
|
2954
3054
|
const pDurationMs = Date.now() - pStartTime;
|
|
@@ -2986,7 +3086,7 @@ The output should be the COMPLETE consolidated profile as valid markdown, starti
|
|
|
2986
3086
|
/**
|
|
2987
3087
|
* Consolidate profile using local LLM.
|
|
2988
3088
|
*/
|
|
2989
|
-
async consolidateProfileWithLocalLlm(fullProfileContent) {
|
|
3089
|
+
async consolidateProfileWithLocalLlm(fullProfileContent, targetLines = 50) {
|
|
2990
3090
|
const contextSizes = this.modelRegistry.calculateContextSizes(
|
|
2991
3091
|
this.config.localLlmModel,
|
|
2992
3092
|
this.config.localLlmMaxContext
|
|
@@ -2999,7 +3099,7 @@ The output should be the COMPLETE consolidated profile as valid markdown, starti
|
|
|
2999
3099
|
3. REMOVES stale information that has been superseded by newer bullets
|
|
3000
3100
|
4. REMOVES trivial or overly specific operational details that won't be useful across sessions
|
|
3001
3101
|
5. KEEPS the most important, durable observations about the user's preferences, habits, identity, and working style
|
|
3002
|
-
6. Target roughly
|
|
3102
|
+
6. Target roughly ${targetLines} lines \u2014 this is a soft target, prioritize quality over length
|
|
3003
3103
|
7. Write in the same style as the existing profile \u2014 concise bullets, no fluff
|
|
3004
3104
|
|
|
3005
3105
|
Profile to consolidate:
|
|
@@ -5612,11 +5712,12 @@ ${sanitized.text}
|
|
|
5612
5712
|
}
|
|
5613
5713
|
}
|
|
5614
5714
|
/** Check if profile.md exceeds the max line cap and needs LLM consolidation */
|
|
5615
|
-
async profileNeedsConsolidation() {
|
|
5715
|
+
async profileNeedsConsolidation(triggerLines) {
|
|
5616
5716
|
const profile = await this.readProfile();
|
|
5617
5717
|
if (!profile) return false;
|
|
5618
5718
|
const lineCount = profile.split("\n").length;
|
|
5619
|
-
|
|
5719
|
+
const threshold = typeof triggerLines === "number" ? Math.max(0, Math.floor(triggerLines)) : _StorageManager.PROFILE_MAX_LINES;
|
|
5720
|
+
return lineCount > threshold;
|
|
5620
5721
|
}
|
|
5621
5722
|
async readAllMemories() {
|
|
5622
5723
|
const memories = [];
|
|
@@ -6616,13 +6717,14 @@ ${reflection}
|
|
|
6616
6717
|
* Build the Knowledge Index: a compact markdown table of top-scored entities.
|
|
6617
6718
|
* Respects maxEntities and maxChars limits from config.
|
|
6618
6719
|
*/
|
|
6619
|
-
async buildKnowledgeIndex(config) {
|
|
6620
|
-
|
|
6720
|
+
async buildKnowledgeIndex(config, overrides) {
|
|
6721
|
+
const useDefaultLimits = overrides?.maxEntities === void 0 && overrides?.maxChars === void 0;
|
|
6722
|
+
if (useDefaultLimits && this.knowledgeIndexCache && Date.now() - this.knowledgeIndexCache.builtAt < _StorageManager.KNOWLEDGE_INDEX_CACHE_TTL_MS) {
|
|
6621
6723
|
return { result: this.knowledgeIndexCache.result, cached: true };
|
|
6622
6724
|
}
|
|
6623
6725
|
const entities = await this.readAllEntityFiles();
|
|
6624
6726
|
if (entities.length === 0) {
|
|
6625
|
-
this.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
6727
|
+
if (useDefaultLimits) this.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
6626
6728
|
return { result: "", cached: false };
|
|
6627
6729
|
}
|
|
6628
6730
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -6635,26 +6737,28 @@ ${reflection}
|
|
|
6635
6737
|
topRelationships: e.relationships.slice(0, 3).map((r) => r.target)
|
|
6636
6738
|
}));
|
|
6637
6739
|
scored.sort((a, b) => b.score - a.score);
|
|
6638
|
-
const
|
|
6740
|
+
const maxEntities = typeof overrides?.maxEntities === "number" ? Math.max(0, Math.floor(overrides.maxEntities)) : config.knowledgeIndexMaxEntities;
|
|
6741
|
+
const topN = scored.slice(0, maxEntities);
|
|
6639
6742
|
if (topN.length === 0) {
|
|
6640
|
-
this.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
6743
|
+
if (useDefaultLimits) this.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
6641
6744
|
return { result: "", cached: false };
|
|
6642
6745
|
}
|
|
6643
6746
|
const header = "## Knowledge Index\n\n| Entity | Type | Summary | Connected to |\n|--------|------|---------|-------------|";
|
|
6644
6747
|
const rows = [];
|
|
6645
6748
|
let totalChars = header.length;
|
|
6749
|
+
const maxChars = typeof overrides?.maxChars === "number" ? Math.max(0, Math.floor(overrides.maxChars)) : config.knowledgeIndexMaxChars;
|
|
6646
6750
|
for (const entity of topN) {
|
|
6647
6751
|
const summary = entity.summary || `${entity.factCount} facts`;
|
|
6648
6752
|
const connected = entity.topRelationships.length > 0 ? entity.topRelationships.join(", ") : "\u2014";
|
|
6649
6753
|
const row = `| ${entity.name} | ${entity.type} | ${summary} | ${connected} |`;
|
|
6650
|
-
if (totalChars + row.length + 1 >
|
|
6754
|
+
if (totalChars + row.length + 1 > maxChars) break;
|
|
6651
6755
|
rows.push(row);
|
|
6652
6756
|
totalChars += row.length + 1;
|
|
6653
6757
|
}
|
|
6654
6758
|
const result = rows.length === 0 ? "" : `${header}
|
|
6655
6759
|
${rows.join("\n")}
|
|
6656
6760
|
`;
|
|
6657
|
-
this.knowledgeIndexCache = { result, builtAt: Date.now() };
|
|
6761
|
+
if (useDefaultLimits) this.knowledgeIndexCache = { result, builtAt: Date.now() };
|
|
6658
6762
|
return { result, cached: false };
|
|
6659
6763
|
}
|
|
6660
6764
|
/** Invalidate the Knowledge Index cache (call after entity mutations). */
|
|
@@ -15483,11 +15587,69 @@ ${r.snippet.trim()}
|
|
|
15483
15587
|
log.debug(`last graph recall write failed: ${err}`);
|
|
15484
15588
|
}
|
|
15485
15589
|
}
|
|
15590
|
+
getRecallSectionEntry(sectionId) {
|
|
15591
|
+
const pipeline2 = Array.isArray(this.config.recallPipeline) ? this.config.recallPipeline : [];
|
|
15592
|
+
return pipeline2.find((entry) => entry.id === sectionId);
|
|
15593
|
+
}
|
|
15594
|
+
isRecallSectionEnabled(sectionId, defaultEnabled = true) {
|
|
15595
|
+
const entry = this.getRecallSectionEntry(sectionId);
|
|
15596
|
+
if (!entry) return defaultEnabled;
|
|
15597
|
+
return entry.enabled !== false;
|
|
15598
|
+
}
|
|
15599
|
+
getRecallSectionMaxChars(sectionId) {
|
|
15600
|
+
const entry = this.getRecallSectionEntry(sectionId);
|
|
15601
|
+
if (!entry) return void 0;
|
|
15602
|
+
if (entry.maxChars === null) return null;
|
|
15603
|
+
if (typeof entry.maxChars !== "number") return void 0;
|
|
15604
|
+
return Math.max(0, Math.floor(entry.maxChars));
|
|
15605
|
+
}
|
|
15606
|
+
getRecallSectionNumber(sectionId, key) {
|
|
15607
|
+
const entry = this.getRecallSectionEntry(sectionId);
|
|
15608
|
+
if (!entry) return void 0;
|
|
15609
|
+
const value = entry[key];
|
|
15610
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
15611
|
+
return Math.max(0, Math.floor(value));
|
|
15612
|
+
}
|
|
15613
|
+
appendRecallSection(sectionBuckets, sectionId, content) {
|
|
15614
|
+
if (!this.isRecallSectionEnabled(sectionId)) return;
|
|
15615
|
+
const trimmed = content.trim();
|
|
15616
|
+
if (trimmed.length === 0) return;
|
|
15617
|
+
const maxChars = this.getRecallSectionMaxChars(sectionId);
|
|
15618
|
+
let finalContent = trimmed;
|
|
15619
|
+
if (maxChars === 0) return;
|
|
15620
|
+
if (typeof maxChars === "number" && finalContent.length > maxChars) {
|
|
15621
|
+
finalContent = `${finalContent.slice(0, maxChars)}
|
|
15622
|
+
|
|
15623
|
+
...(trimmed)
|
|
15624
|
+
`;
|
|
15625
|
+
}
|
|
15626
|
+
const existing = sectionBuckets.get(sectionId) ?? [];
|
|
15627
|
+
existing.push(finalContent);
|
|
15628
|
+
sectionBuckets.set(sectionId, existing);
|
|
15629
|
+
}
|
|
15630
|
+
assembleRecallSections(sectionBuckets) {
|
|
15631
|
+
const ordered = [];
|
|
15632
|
+
const pipeline2 = Array.isArray(this.config.recallPipeline) ? this.config.recallPipeline : [];
|
|
15633
|
+
const orderedIds = pipeline2.filter((entry) => entry.enabled !== false).map((entry) => entry.id);
|
|
15634
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15635
|
+
for (const id of orderedIds) {
|
|
15636
|
+
const chunks = sectionBuckets.get(id);
|
|
15637
|
+
if (!chunks || chunks.length === 0) continue;
|
|
15638
|
+
ordered.push(chunks.join("\n\n"));
|
|
15639
|
+
seen.add(id);
|
|
15640
|
+
}
|
|
15641
|
+
for (const [id, chunks] of sectionBuckets.entries()) {
|
|
15642
|
+
if (seen.has(id)) continue;
|
|
15643
|
+
if (chunks.length === 0) continue;
|
|
15644
|
+
ordered.push(chunks.join("\n\n"));
|
|
15645
|
+
}
|
|
15646
|
+
return ordered;
|
|
15647
|
+
}
|
|
15486
15648
|
async recallInternal(prompt, sessionKey) {
|
|
15487
15649
|
const recallStart = Date.now();
|
|
15488
15650
|
const timings = {};
|
|
15489
15651
|
const promptHash = createHash6("sha256").update(prompt).digest("hex");
|
|
15490
|
-
const
|
|
15652
|
+
const sectionBuckets = /* @__PURE__ */ new Map();
|
|
15491
15653
|
const queryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
|
|
15492
15654
|
cronRecallPolicyEnabled: this.config.cronRecallPolicyEnabled,
|
|
15493
15655
|
cronRecallNormalizedQueryMaxChars: this.config.cronRecallNormalizedQueryMaxChars,
|
|
@@ -15517,7 +15679,10 @@ ${r.snippet.trim()}
|
|
|
15517
15679
|
0,
|
|
15518
15680
|
Math.min(this.config.qmdMaxResults, this.config.recallPlannerMaxQmdResultsMinimal)
|
|
15519
15681
|
);
|
|
15520
|
-
const
|
|
15682
|
+
const baseRecallResultLimit = recallMode !== "no_recall" && queryPolicy.retrievalBudgetMode === "minimal" ? Math.min(plannerRecallResultLimit, policyMinimalLimit) : plannerRecallResultLimit;
|
|
15683
|
+
const memoriesSectionEnabled = this.isRecallSectionEnabled("memories");
|
|
15684
|
+
const memorySectionMaxResults = this.getRecallSectionNumber("memories", "maxResults");
|
|
15685
|
+
const recallResultLimit = memoriesSectionEnabled ? memorySectionMaxResults !== void 0 ? Math.min(baseRecallResultLimit, memorySectionMaxResults) : baseRecallResultLimit : 0;
|
|
15521
15686
|
const recallHeadroom = this.config.verbatimArtifactsEnabled ? Math.max(12, this.config.verbatimArtifactsMaxRecall * 4) : 12;
|
|
15522
15687
|
const computedFetchLimit = recallResultLimit === 0 ? 0 : Math.max(recallResultLimit, Math.min(200, recallResultLimit + recallHeadroom));
|
|
15523
15688
|
const qmdFetchLimit = computedFetchLimit;
|
|
@@ -15561,6 +15726,7 @@ ${r.snippet.trim()}
|
|
|
15561
15726
|
const recallNamespaces = recallNamespacesForPrincipal(principal, this.config);
|
|
15562
15727
|
const profileStorage = await this.storageRouter.storageFor(selfNamespace);
|
|
15563
15728
|
const sharedContextPromise = (async () => {
|
|
15729
|
+
if (!this.isRecallSectionEnabled("shared-context", this.config.sharedContextEnabled === true)) return null;
|
|
15564
15730
|
if (!this.sharedContext) return null;
|
|
15565
15731
|
const t0 = Date.now();
|
|
15566
15732
|
const [priorities, roundtable] = await Promise.all([
|
|
@@ -15579,12 +15745,14 @@ ${r.snippet.trim()}
|
|
|
15579
15745
|
return trimmed.trim().length > 0 ? trimmed : null;
|
|
15580
15746
|
})();
|
|
15581
15747
|
const profilePromise = (async () => {
|
|
15748
|
+
if (!this.isRecallSectionEnabled("profile")) return null;
|
|
15582
15749
|
const t0 = Date.now();
|
|
15583
15750
|
const profile2 = await profileStorage.readProfile();
|
|
15584
15751
|
timings.profile = `${Date.now() - t0}ms`;
|
|
15585
15752
|
return profile2 || null;
|
|
15586
15753
|
})();
|
|
15587
15754
|
const identityContinuityPromise = (async () => {
|
|
15755
|
+
if (!this.isRecallSectionEnabled("identity-continuity", this.config.identityContinuityEnabled === true)) return null;
|
|
15588
15756
|
const t0 = Date.now();
|
|
15589
15757
|
const section = await this.buildIdentityContinuitySection({
|
|
15590
15758
|
storage: profileStorage,
|
|
@@ -15595,10 +15763,14 @@ ${r.snippet.trim()}
|
|
|
15595
15763
|
return section;
|
|
15596
15764
|
})();
|
|
15597
15765
|
const knowledgeIndexPromise = (async () => {
|
|
15766
|
+
if (!this.isRecallSectionEnabled("knowledge-index", this.config.knowledgeIndexEnabled)) return null;
|
|
15598
15767
|
if (!this.config.knowledgeIndexEnabled) return null;
|
|
15599
15768
|
const t0 = Date.now();
|
|
15600
15769
|
try {
|
|
15601
|
-
const ki = await this.storage.buildKnowledgeIndex(this.config
|
|
15770
|
+
const ki = await this.storage.buildKnowledgeIndex(this.config, {
|
|
15771
|
+
maxEntities: this.getRecallSectionNumber("knowledge-index", "maxEntities"),
|
|
15772
|
+
maxChars: this.getRecallSectionNumber("knowledge-index", "maxChars")
|
|
15773
|
+
});
|
|
15602
15774
|
timings.ki = `${Date.now() - t0}ms${ki.cached ? " (cached)" : ""}`;
|
|
15603
15775
|
return ki.result ? ki : null;
|
|
15604
15776
|
} catch (err) {
|
|
@@ -15608,6 +15780,7 @@ ${r.snippet.trim()}
|
|
|
15608
15780
|
}
|
|
15609
15781
|
})();
|
|
15610
15782
|
const artifactsPromise = (async () => {
|
|
15783
|
+
if (!this.isRecallSectionEnabled("verbatim-artifacts", this.config.verbatimArtifactsEnabled === true)) return [];
|
|
15611
15784
|
if (!this.config.verbatimArtifactsEnabled) return [];
|
|
15612
15785
|
const t0 = Date.now();
|
|
15613
15786
|
const targetCount = computeArtifactRecallLimit(
|
|
@@ -15651,26 +15824,162 @@ ${r.snippet.trim()}
|
|
|
15651
15824
|
timings.qmd = `${Date.now() - t0}ms`;
|
|
15652
15825
|
return { memoryResultsLists: [filteredResults], globalResults: [] };
|
|
15653
15826
|
})();
|
|
15654
|
-
const
|
|
15827
|
+
const transcriptPromise = (async () => {
|
|
15828
|
+
const t0 = Date.now();
|
|
15829
|
+
if (!this.config.transcriptEnabled || !this.isRecallSectionEnabled("transcript", true)) {
|
|
15830
|
+
timings.transcript = "skip";
|
|
15831
|
+
return null;
|
|
15832
|
+
}
|
|
15833
|
+
const transcriptMaxTokens = this.getRecallSectionNumber("transcript", "maxTokens") ?? this.config.maxTranscriptTokens;
|
|
15834
|
+
const transcriptMaxTurns = this.getRecallSectionNumber("transcript", "maxTurns") ?? this.config.maxTranscriptTurns;
|
|
15835
|
+
const transcriptLookbackHours = this.getRecallSectionNumber("transcript", "lookbackHours") ?? this.config.transcriptRecallHours;
|
|
15836
|
+
if (transcriptMaxTokens === 0 || transcriptMaxTurns === 0 || transcriptLookbackHours === 0) {
|
|
15837
|
+
timings.transcript = "skip(limit=0)";
|
|
15838
|
+
return null;
|
|
15839
|
+
}
|
|
15840
|
+
let section = null;
|
|
15841
|
+
let checkpointInjected = false;
|
|
15842
|
+
if (this.config.checkpointEnabled) {
|
|
15843
|
+
const checkpoint = await this.transcript.loadCheckpoint(sessionKey);
|
|
15844
|
+
log.debug(`recall: checkpoint loaded, turns=${checkpoint?.turns?.length ?? 0}`);
|
|
15845
|
+
if (checkpoint && checkpoint.turns.length > 0) {
|
|
15846
|
+
const formatted = this.transcript.formatForRecall(checkpoint.turns, transcriptMaxTokens);
|
|
15847
|
+
if (formatted) {
|
|
15848
|
+
section = `## Working Context (Recovered)
|
|
15849
|
+
|
|
15850
|
+
${formatted}`;
|
|
15851
|
+
checkpointInjected = true;
|
|
15852
|
+
await this.transcript.clearCheckpoint();
|
|
15853
|
+
}
|
|
15854
|
+
}
|
|
15855
|
+
}
|
|
15856
|
+
if (!checkpointInjected) {
|
|
15857
|
+
const entries = await this.transcript.readRecent(transcriptLookbackHours, sessionKey);
|
|
15858
|
+
log.debug(`recall: read ${entries.length} transcript entries for sessionKey=${sessionKey}`);
|
|
15859
|
+
const cappedEntries = entries.slice(-transcriptMaxTurns);
|
|
15860
|
+
if (cappedEntries.length > 0) {
|
|
15861
|
+
log.debug(`recall: injecting ${cappedEntries.length} transcript entries`);
|
|
15862
|
+
const formatted = this.transcript.formatForRecall(cappedEntries, transcriptMaxTokens);
|
|
15863
|
+
if (formatted) section = formatted;
|
|
15864
|
+
}
|
|
15865
|
+
}
|
|
15866
|
+
timings.transcript = `${Date.now() - t0}ms`;
|
|
15867
|
+
return section;
|
|
15868
|
+
})();
|
|
15869
|
+
const summariesPromise = (async () => {
|
|
15870
|
+
const t0 = Date.now();
|
|
15871
|
+
if (!this.config.hourlySummariesEnabled || !sessionKey || !this.isRecallSectionEnabled("summaries", true)) {
|
|
15872
|
+
timings.summaries = "skip";
|
|
15873
|
+
return null;
|
|
15874
|
+
}
|
|
15875
|
+
const summariesLookbackHours = this.getRecallSectionNumber("summaries", "lookbackHours") ?? this.config.summaryRecallHours;
|
|
15876
|
+
const summariesMaxCount = this.getRecallSectionNumber("summaries", "maxCount") ?? this.config.maxSummaryCount;
|
|
15877
|
+
if (summariesLookbackHours <= 0 || summariesMaxCount <= 0) {
|
|
15878
|
+
timings.summaries = "skip(limit=0)";
|
|
15879
|
+
return null;
|
|
15880
|
+
}
|
|
15881
|
+
const summaries = await this.summarizer.readRecent(sessionKey, summariesLookbackHours);
|
|
15882
|
+
const cappedSummaries = summaries.slice(0, summariesMaxCount);
|
|
15883
|
+
const section = cappedSummaries.length > 0 ? this.summarizer.formatForRecall(cappedSummaries, summariesMaxCount) : null;
|
|
15884
|
+
timings.summaries = `${Date.now() - t0}ms`;
|
|
15885
|
+
return section;
|
|
15886
|
+
})();
|
|
15887
|
+
const conversationRecallPromise = (async () => {
|
|
15888
|
+
const t0 = Date.now();
|
|
15889
|
+
if (!this.config.conversationIndexEnabled || queryPolicy.skipConversationRecall || !this.isRecallSectionEnabled("conversation-recall", true)) {
|
|
15890
|
+
timings.convRecall = "skip";
|
|
15891
|
+
return null;
|
|
15892
|
+
}
|
|
15893
|
+
const topKOverride = this.getRecallSectionNumber("conversation-recall", "topK");
|
|
15894
|
+
if (topKOverride === 0) {
|
|
15895
|
+
timings.convRecall = "skip(topK=0)";
|
|
15896
|
+
return null;
|
|
15897
|
+
}
|
|
15898
|
+
const startedAtMs = Date.now();
|
|
15899
|
+
const timeoutMs = Math.max(
|
|
15900
|
+
200,
|
|
15901
|
+
this.getRecallSectionNumber("conversation-recall", "timeoutMs") ?? this.config.conversationRecallTimeoutMs
|
|
15902
|
+
);
|
|
15903
|
+
const topK = Math.max(
|
|
15904
|
+
1,
|
|
15905
|
+
topKOverride ?? this.config.conversationRecallTopK
|
|
15906
|
+
);
|
|
15907
|
+
const maxChars = Math.max(
|
|
15908
|
+
400,
|
|
15909
|
+
this.getRecallSectionNumber("conversation-recall", "maxChars") ?? this.config.conversationRecallMaxChars
|
|
15910
|
+
);
|
|
15911
|
+
const results = await Promise.race([
|
|
15912
|
+
this.searchConversationRecallResults(retrievalQuery, topK),
|
|
15913
|
+
new Promise((resolve) => setTimeout(() => resolve([]), timeoutMs))
|
|
15914
|
+
]).catch(() => []);
|
|
15915
|
+
const durationMs = Date.now() - startedAtMs;
|
|
15916
|
+
if (durationMs >= timeoutMs) {
|
|
15917
|
+
log.debug(`conversation recall: timed out after ${timeoutMs}ms`);
|
|
15918
|
+
}
|
|
15919
|
+
const section = this.formatConversationRecallSection(results, maxChars);
|
|
15920
|
+
timings.convRecall = `${Date.now() - t0}ms`;
|
|
15921
|
+
return section;
|
|
15922
|
+
})();
|
|
15923
|
+
const compoundingPromise = (async () => {
|
|
15924
|
+
const t0 = Date.now();
|
|
15925
|
+
if (!this.compounding || !this.config.compoundingInjectEnabled || !this.isRecallSectionEnabled("compounding", true)) {
|
|
15926
|
+
timings.compounding = "skip";
|
|
15927
|
+
return null;
|
|
15928
|
+
}
|
|
15929
|
+
const mistakes = await this.compounding.readMistakes();
|
|
15930
|
+
if (!mistakes || !Array.isArray(mistakes.patterns) || mistakes.patterns.length === 0) {
|
|
15931
|
+
timings.compounding = `${Date.now() - t0}ms`;
|
|
15932
|
+
return null;
|
|
15933
|
+
}
|
|
15934
|
+
const maxPatterns = this.getRecallSectionNumber("compounding", "maxPatterns") ?? 40;
|
|
15935
|
+
if (maxPatterns === 0) {
|
|
15936
|
+
timings.compounding = "skip(limit=0)";
|
|
15937
|
+
return null;
|
|
15938
|
+
}
|
|
15939
|
+
const lines = [
|
|
15940
|
+
"## Institutional Learning (Compounded)",
|
|
15941
|
+
"",
|
|
15942
|
+
"Avoid repeating these patterns:",
|
|
15943
|
+
...mistakes.patterns.slice(0, maxPatterns).map((p) => `- ${p}`)
|
|
15944
|
+
];
|
|
15945
|
+
timings.compounding = `${Date.now() - t0}ms`;
|
|
15946
|
+
return lines.join("\n");
|
|
15947
|
+
})();
|
|
15948
|
+
const [
|
|
15949
|
+
sharedCtx,
|
|
15950
|
+
profile,
|
|
15951
|
+
identityContinuity,
|
|
15952
|
+
kiResult,
|
|
15953
|
+
artifacts,
|
|
15954
|
+
qmdResult,
|
|
15955
|
+
transcriptSection,
|
|
15956
|
+
summariesSection,
|
|
15957
|
+
conversationRecallSection,
|
|
15958
|
+
compoundingSection
|
|
15959
|
+
] = await Promise.all([
|
|
15655
15960
|
sharedContextPromise,
|
|
15656
15961
|
profilePromise,
|
|
15657
15962
|
identityContinuityPromise,
|
|
15658
15963
|
knowledgeIndexPromise,
|
|
15659
15964
|
artifactsPromise,
|
|
15660
|
-
qmdPromise
|
|
15965
|
+
qmdPromise,
|
|
15966
|
+
transcriptPromise,
|
|
15967
|
+
summariesPromise,
|
|
15968
|
+
conversationRecallPromise,
|
|
15969
|
+
compoundingPromise
|
|
15661
15970
|
]);
|
|
15662
|
-
if (sharedCtx)
|
|
15663
|
-
if (profile)
|
|
15971
|
+
if (sharedCtx) this.appendRecallSection(sectionBuckets, "shared-context", sharedCtx);
|
|
15972
|
+
if (profile) this.appendRecallSection(sectionBuckets, "profile", `## User Profile
|
|
15664
15973
|
|
|
15665
15974
|
${profile}`);
|
|
15666
15975
|
if (identityContinuity) {
|
|
15667
|
-
|
|
15976
|
+
this.appendRecallSection(sectionBuckets, "identity-continuity", identityContinuity.section);
|
|
15668
15977
|
identityInjectionModeUsed = identityContinuity.mode;
|
|
15669
15978
|
identityInjectedChars = identityContinuity.injectedChars;
|
|
15670
15979
|
identityInjectionTruncated = identityContinuity.truncated;
|
|
15671
15980
|
}
|
|
15672
15981
|
if (kiResult?.result) {
|
|
15673
|
-
|
|
15982
|
+
this.appendRecallSection(sectionBuckets, "knowledge-index", kiResult.result);
|
|
15674
15983
|
log.debug(`Knowledge Index: ${kiResult.result.split("\n").length - 4} entities, ${kiResult.result.length} chars${kiResult.cached ? " (cached)" : ""}`);
|
|
15675
15984
|
}
|
|
15676
15985
|
if (artifacts.length > 0) {
|
|
@@ -15680,11 +15989,11 @@ ${profile}`);
|
|
|
15680
15989
|
const created = createdRaw ? createdRaw.slice(0, 19).replace("T", " ") : "unknown-time";
|
|
15681
15990
|
return `- [${artifactType}] "${this.truncateArtifactForRecall(a.content)}" (${created})`;
|
|
15682
15991
|
});
|
|
15683
|
-
|
|
15992
|
+
this.appendRecallSection(sectionBuckets, "verbatim-artifacts", `## Verbatim Artifacts
|
|
15684
15993
|
|
|
15685
15994
|
${lines.join("\n")}`);
|
|
15686
15995
|
}
|
|
15687
|
-
if (this.config.memoryBoxesEnabled && this.config.boxRecallDays > 0) {
|
|
15996
|
+
if (this.isRecallSectionEnabled("memory-boxes", this.config.memoryBoxesEnabled === true) && this.config.memoryBoxesEnabled && this.config.boxRecallDays > 0) {
|
|
15688
15997
|
const recentBoxes = await this.boxBuilderFor(profileStorage).readRecentBoxes(this.config.boxRecallDays).catch(() => []);
|
|
15689
15998
|
if (recentBoxes.length > 0) {
|
|
15690
15999
|
const boxLines = recentBoxes.slice(0, 5).map((b) => {
|
|
@@ -15692,16 +16001,16 @@ ${lines.join("\n")}`);
|
|
|
15692
16001
|
const traceNote = b.traceId ? ` [trace: ${b.traceId.slice(0, 12)}]` : "";
|
|
15693
16002
|
return `- [${sealedDate}${traceNote}] Topics: ${b.topics.join(", ")} (${b.memoryIds.length} memories)`;
|
|
15694
16003
|
});
|
|
15695
|
-
|
|
16004
|
+
this.appendRecallSection(sectionBuckets, "memory-boxes", `## Recent Topic Windows
|
|
15696
16005
|
|
|
15697
16006
|
${boxLines.join("\n")}`);
|
|
15698
16007
|
}
|
|
15699
16008
|
}
|
|
15700
|
-
if (this.config.temporalMemoryTreeEnabled && recallMode !== "minimal" && recallMode !== "no_recall") {
|
|
16009
|
+
if (this.isRecallSectionEnabled("temporal-memory-tree", this.config.temporalMemoryTreeEnabled === true) && this.config.temporalMemoryTreeEnabled && recallMode !== "minimal" && recallMode !== "no_recall") {
|
|
15701
16010
|
const tmtNode = await this.tmtBuilder.getMostRelevantNode();
|
|
15702
16011
|
if (tmtNode) {
|
|
15703
16012
|
const levelLabel = tmtNode.level.charAt(0).toUpperCase() + tmtNode.level.slice(1);
|
|
15704
|
-
|
|
16013
|
+
this.appendRecallSection(sectionBuckets, "temporal-memory-tree", `## Memory Timeline (${levelLabel})
|
|
15705
16014
|
|
|
15706
16015
|
${tmtNode.summary}`);
|
|
15707
16016
|
}
|
|
@@ -15789,7 +16098,7 @@ ${tmtNode.summary}`);
|
|
|
15789
16098
|
this.publishRecallResults({
|
|
15790
16099
|
title: "Relevant Memories",
|
|
15791
16100
|
results: memoryResults,
|
|
15792
|
-
|
|
16101
|
+
sectionBuckets,
|
|
15793
16102
|
retrievalQuery,
|
|
15794
16103
|
sessionKey,
|
|
15795
16104
|
identityInjection: {
|
|
@@ -15817,7 +16126,7 @@ ${tmtNode.summary}`);
|
|
|
15817
16126
|
this.publishRecallResults({
|
|
15818
16127
|
title: "Relevant Memories",
|
|
15819
16128
|
results: scoped,
|
|
15820
|
-
|
|
16129
|
+
sectionBuckets,
|
|
15821
16130
|
retrievalQuery,
|
|
15822
16131
|
sessionKey,
|
|
15823
16132
|
identityInjection: {
|
|
@@ -15840,7 +16149,7 @@ ${tmtNode.summary}`);
|
|
|
15840
16149
|
this.publishRecallResults({
|
|
15841
16150
|
title: "Long-Term Memories (Fallback)",
|
|
15842
16151
|
results: longTerm,
|
|
15843
|
-
|
|
16152
|
+
sectionBuckets,
|
|
15844
16153
|
retrievalQuery,
|
|
15845
16154
|
sessionKey,
|
|
15846
16155
|
identityInjection: {
|
|
@@ -15854,13 +16163,17 @@ ${tmtNode.summary}`);
|
|
|
15854
16163
|
}
|
|
15855
16164
|
}
|
|
15856
16165
|
if (globalResults.length > 0) {
|
|
15857
|
-
|
|
16166
|
+
this.appendRecallSection(
|
|
16167
|
+
sectionBuckets,
|
|
16168
|
+
"workspace-context",
|
|
15858
16169
|
this.formatQmdResults("Workspace Context", globalResults)
|
|
15859
16170
|
);
|
|
15860
16171
|
}
|
|
15861
16172
|
timings.qmdPost = `${Date.now() - t0}ms`;
|
|
15862
16173
|
if (isDisagreementPrompt(prompt)) {
|
|
15863
|
-
|
|
16174
|
+
this.appendRecallSection(
|
|
16175
|
+
sectionBuckets,
|
|
16176
|
+
"memories",
|
|
15864
16177
|
[
|
|
15865
16178
|
"## Retrieval Feedback Helper",
|
|
15866
16179
|
"",
|
|
@@ -15891,7 +16204,7 @@ ${tmtNode.summary}`);
|
|
|
15891
16204
|
this.publishRecallResults({
|
|
15892
16205
|
title: "Relevant Memories",
|
|
15893
16206
|
results: scoped,
|
|
15894
|
-
|
|
16207
|
+
sectionBuckets,
|
|
15895
16208
|
retrievalQuery,
|
|
15896
16209
|
sessionKey,
|
|
15897
16210
|
identityInjection: {
|
|
@@ -15931,7 +16244,7 @@ ${tmtNode.summary}`);
|
|
|
15931
16244
|
this.publishRecallResults({
|
|
15932
16245
|
title: "Recent Memories",
|
|
15933
16246
|
results: recent,
|
|
15934
|
-
|
|
16247
|
+
sectionBuckets,
|
|
15935
16248
|
retrievalQuery,
|
|
15936
16249
|
sessionKey,
|
|
15937
16250
|
identityInjection: {
|
|
@@ -15954,7 +16267,7 @@ ${tmtNode.summary}`);
|
|
|
15954
16267
|
this.publishRecallResults({
|
|
15955
16268
|
title: "Long-Term Memories (Fallback)",
|
|
15956
16269
|
results: longTerm,
|
|
15957
|
-
|
|
16270
|
+
sectionBuckets,
|
|
15958
16271
|
retrievalQuery,
|
|
15959
16272
|
sessionKey,
|
|
15960
16273
|
identityInjection: {
|
|
@@ -15979,7 +16292,7 @@ ${tmtNode.summary}`);
|
|
|
15979
16292
|
this.publishRecallResults({
|
|
15980
16293
|
title: "Long-Term Memories (Fallback)",
|
|
15981
16294
|
results: longTerm,
|
|
15982
|
-
|
|
16295
|
+
sectionBuckets,
|
|
15983
16296
|
retrievalQuery,
|
|
15984
16297
|
sessionKey,
|
|
15985
16298
|
identityInjection: {
|
|
@@ -15993,7 +16306,9 @@ ${tmtNode.summary}`);
|
|
|
15993
16306
|
}
|
|
15994
16307
|
}
|
|
15995
16308
|
if (isDisagreementPrompt(prompt)) {
|
|
15996
|
-
|
|
16309
|
+
this.appendRecallSection(
|
|
16310
|
+
sectionBuckets,
|
|
16311
|
+
"memories",
|
|
15997
16312
|
[
|
|
15998
16313
|
"## Retrieval Feedback Helper",
|
|
15999
16314
|
"",
|
|
@@ -16007,108 +16322,37 @@ ${tmtNode.summary}`);
|
|
|
16007
16322
|
);
|
|
16008
16323
|
}
|
|
16009
16324
|
}
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
const transcriptT0 = Date.now();
|
|
16015
|
-
log.debug(`recall: transcriptEnabled=${this.config.transcriptEnabled}, sessionKey=${sessionKey}`);
|
|
16016
|
-
if (this.config.transcriptEnabled) {
|
|
16017
|
-
let checkpointInjected = false;
|
|
16018
|
-
if (this.config.checkpointEnabled) {
|
|
16019
|
-
const checkpoint = await this.transcript.loadCheckpoint(sessionKey);
|
|
16020
|
-
log.debug(`recall: checkpoint loaded, turns=${checkpoint?.turns?.length ?? 0}`);
|
|
16021
|
-
if (checkpoint && checkpoint.turns.length > 0) {
|
|
16022
|
-
const formatted = this.transcript.formatForRecall(
|
|
16023
|
-
checkpoint.turns,
|
|
16024
|
-
this.config.maxTranscriptTokens
|
|
16025
|
-
);
|
|
16026
|
-
if (formatted) {
|
|
16027
|
-
sections.push(`## Working Context (Recovered)
|
|
16028
|
-
|
|
16029
|
-
${formatted}`);
|
|
16030
|
-
checkpointInjected = true;
|
|
16031
|
-
await this.transcript.clearCheckpoint();
|
|
16032
|
-
}
|
|
16033
|
-
}
|
|
16034
|
-
}
|
|
16035
|
-
if (!checkpointInjected) {
|
|
16036
|
-
const entries = await this.transcript.readRecent(
|
|
16037
|
-
this.config.transcriptRecallHours,
|
|
16038
|
-
sessionKey
|
|
16039
|
-
);
|
|
16040
|
-
log.debug(`recall: read ${entries.length} transcript entries for sessionKey=${sessionKey}`);
|
|
16041
|
-
const cappedEntries = entries.slice(-this.config.maxTranscriptTurns);
|
|
16042
|
-
if (cappedEntries.length > 0) {
|
|
16043
|
-
log.debug(`recall: injecting ${cappedEntries.length} transcript entries`);
|
|
16044
|
-
const formatted = this.transcript.formatForRecall(
|
|
16045
|
-
cappedEntries,
|
|
16046
|
-
this.config.maxTranscriptTokens
|
|
16047
|
-
);
|
|
16048
|
-
if (formatted) {
|
|
16049
|
-
sections.push(formatted);
|
|
16050
|
-
}
|
|
16051
|
-
}
|
|
16325
|
+
if (this.isRecallSectionEnabled("compression-guidelines", this.config.compressionGuidelineLearningEnabled === true)) {
|
|
16326
|
+
const compressionGuidelineSection = await this.buildCompressionGuidelineRecallSection();
|
|
16327
|
+
if (compressionGuidelineSection) {
|
|
16328
|
+
this.appendRecallSection(sectionBuckets, "compression-guidelines", compressionGuidelineSection);
|
|
16052
16329
|
}
|
|
16053
16330
|
}
|
|
16054
|
-
|
|
16055
|
-
|
|
16056
|
-
if (this.config.hourlySummariesEnabled && sessionKey) {
|
|
16057
|
-
const summaries = await this.summarizer.readRecent(
|
|
16058
|
-
sessionKey,
|
|
16059
|
-
this.config.summaryRecallHours
|
|
16060
|
-
);
|
|
16061
|
-
const cappedSummaries = summaries.slice(0, this.config.maxSummaryCount);
|
|
16062
|
-
if (cappedSummaries.length > 0) {
|
|
16063
|
-
const formatted = this.summarizer.formatForRecall(
|
|
16064
|
-
cappedSummaries,
|
|
16065
|
-
this.config.maxSummaryCount
|
|
16066
|
-
);
|
|
16067
|
-
sections.push(formatted);
|
|
16068
|
-
}
|
|
16331
|
+
if (transcriptSection) {
|
|
16332
|
+
this.appendRecallSection(sectionBuckets, "transcript", transcriptSection);
|
|
16069
16333
|
}
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
if (this.config.conversationIndexEnabled && !queryPolicy.skipConversationRecall) {
|
|
16073
|
-
const startedAtMs = Date.now();
|
|
16074
|
-
const timeoutMs = Math.max(200, this.config.conversationRecallTimeoutMs);
|
|
16075
|
-
const topK = Math.max(1, this.config.conversationRecallTopK);
|
|
16076
|
-
const maxChars = Math.max(400, this.config.conversationRecallMaxChars);
|
|
16077
|
-
const results = await Promise.race([
|
|
16078
|
-
this.searchConversationRecallResults(retrievalQuery, topK),
|
|
16079
|
-
new Promise((resolve) => setTimeout(() => resolve([]), timeoutMs))
|
|
16080
|
-
]).catch(() => []);
|
|
16081
|
-
const durationMs = Date.now() - startedAtMs;
|
|
16082
|
-
if (durationMs >= timeoutMs) {
|
|
16083
|
-
log.debug(`conversation recall: timed out after ${timeoutMs}ms`);
|
|
16084
|
-
}
|
|
16085
|
-
const formattedConversationRecall = this.formatConversationRecallSection(results, maxChars);
|
|
16086
|
-
if (formattedConversationRecall) {
|
|
16087
|
-
sections.push(formattedConversationRecall);
|
|
16088
|
-
}
|
|
16334
|
+
if (summariesSection) {
|
|
16335
|
+
this.appendRecallSection(sectionBuckets, "summaries", summariesSection);
|
|
16089
16336
|
}
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
const mistakes = await this.compounding.readMistakes();
|
|
16093
|
-
if (mistakes && Array.isArray(mistakes.patterns) && mistakes.patterns.length > 0) {
|
|
16094
|
-
const lines = [
|
|
16095
|
-
"## Institutional Learning (Compounded)",
|
|
16096
|
-
"",
|
|
16097
|
-
"Avoid repeating these patterns:",
|
|
16098
|
-
...mistakes.patterns.slice(0, 40).map((p) => `- ${p}`)
|
|
16099
|
-
];
|
|
16100
|
-
sections.push(lines.join("\n"));
|
|
16101
|
-
}
|
|
16337
|
+
if (conversationRecallSection) {
|
|
16338
|
+
this.appendRecallSection(sectionBuckets, "conversation-recall", conversationRecallSection);
|
|
16102
16339
|
}
|
|
16103
|
-
if (
|
|
16340
|
+
if (compoundingSection) {
|
|
16341
|
+
this.appendRecallSection(sectionBuckets, "compounding", compoundingSection);
|
|
16342
|
+
}
|
|
16343
|
+
if (this.config.injectQuestions && this.isRecallSectionEnabled("questions", true)) {
|
|
16104
16344
|
const questions = await profileStorage.readQuestions({ unresolvedOnly: true });
|
|
16105
16345
|
if (questions.length > 0) {
|
|
16106
16346
|
const topQuestion = questions[0];
|
|
16107
|
-
|
|
16347
|
+
this.appendRecallSection(
|
|
16348
|
+
sectionBuckets,
|
|
16349
|
+
"questions",
|
|
16350
|
+
`## Open Question
|
|
16108
16351
|
|
|
16109
16352
|
Something I've been curious about: ${topQuestion.question}
|
|
16110
16353
|
|
|
16111
|
-
_Context: ${topQuestion.context}_`
|
|
16354
|
+
_Context: ${topQuestion.context}_`
|
|
16355
|
+
);
|
|
16112
16356
|
}
|
|
16113
16357
|
}
|
|
16114
16358
|
timings.total = `${Date.now() - recallStart}ms`;
|
|
@@ -16127,7 +16371,8 @@ _Context: ${topQuestion.context}_`);
|
|
|
16127
16371
|
}
|
|
16128
16372
|
}).catch((err) => log.debug(`last recall record failed: ${err}`));
|
|
16129
16373
|
}
|
|
16130
|
-
const
|
|
16374
|
+
const orderedSections = this.assembleRecallSections(sectionBuckets);
|
|
16375
|
+
const context = orderedSections.length === 0 ? "" : orderedSections.join("\n\n---\n\n");
|
|
16131
16376
|
this.emitTrace({
|
|
16132
16377
|
kind: "recall_summary",
|
|
16133
16378
|
traceId: createHash6("sha256").update(`${sessionKey ?? "default"}:${Date.now()}:${promptHash}`).digest("hex").slice(0, 16),
|
|
@@ -17333,11 +17578,17 @@ _Context: ${topQuestion.context}_`);
|
|
|
17333
17578
|
if (this.config.identityEnabled) {
|
|
17334
17579
|
await this.autoConsolidateIdentity();
|
|
17335
17580
|
}
|
|
17336
|
-
|
|
17581
|
+
const profileSection = this.getRecallSectionEntry("profile");
|
|
17582
|
+
const profileConsolidationTriggerLines = typeof profileSection?.consolidateTriggerLines === "number" ? Math.max(0, Math.floor(profileSection.consolidateTriggerLines)) : void 0;
|
|
17583
|
+
const profileConsolidationTargetLines = typeof profileSection?.consolidateTargetLines === "number" ? Math.max(0, Math.floor(profileSection.consolidateTargetLines)) : 50;
|
|
17584
|
+
if (await this.storage.profileNeedsConsolidation(profileConsolidationTriggerLines)) {
|
|
17337
17585
|
log.info("profile.md exceeds max lines \u2014 running smart consolidation");
|
|
17338
17586
|
const currentProfile = await this.storage.readProfile();
|
|
17339
17587
|
if (currentProfile) {
|
|
17340
|
-
const profileResult = await this.extraction.consolidateProfile(
|
|
17588
|
+
const profileResult = await this.extraction.consolidateProfile(
|
|
17589
|
+
currentProfile,
|
|
17590
|
+
profileConsolidationTargetLines
|
|
17591
|
+
);
|
|
17341
17592
|
if (profileResult) {
|
|
17342
17593
|
await this.storage.writeProfile(profileResult.consolidatedProfile);
|
|
17343
17594
|
log.info(`profile.md consolidated: removed ${profileResult.removedCount} items \u2014 ${profileResult.summary}`);
|
|
@@ -17901,7 +18152,11 @@ ${lines.join("\n\n")}`;
|
|
|
17901
18152
|
identityInjection: options.identityInjection
|
|
17902
18153
|
}).catch((err) => log.debug(`last recall record failed: ${err}`));
|
|
17903
18154
|
}
|
|
17904
|
-
|
|
18155
|
+
this.appendRecallSection(
|
|
18156
|
+
options.sectionBuckets,
|
|
18157
|
+
"memories",
|
|
18158
|
+
this.formatQmdResults(options.title, options.results)
|
|
18159
|
+
);
|
|
17905
18160
|
}
|
|
17906
18161
|
async searchEmbeddingFallback(query, limit) {
|
|
17907
18162
|
if (!this.config.embeddingFallbackEnabled) return [];
|
|
@@ -25644,7 +25899,8 @@ var index_default = {
|
|
|
25644
25899
|
const context = await orchestrator.recall(prompt, sessionKey);
|
|
25645
25900
|
log.debug(`before_agent_start: recall returned ${context?.length ?? 0} chars`);
|
|
25646
25901
|
if (!context) return;
|
|
25647
|
-
const maxChars = cfg.
|
|
25902
|
+
const maxChars = cfg.recallBudgetChars;
|
|
25903
|
+
if (maxChars === 0) return;
|
|
25648
25904
|
const trimmed = context.length > maxChars ? context.slice(0, maxChars) + "\n\n...(memory context trimmed)" : context;
|
|
25649
25905
|
log.debug(`before_agent_start: returning system prompt with ${trimmed.length} chars`);
|
|
25650
25906
|
return {
|