@mingxy/cerebro 1.15.5 → 1.15.6
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/hooks.d.ts.map +1 -1
- package/dist/hooks.js +303 -242
- package/dist/hooks.js.map +1 -1
- package/dist/tools.d.ts +2 -2
- package/package.json +1 -1
- package/src/hooks.ts +178 -109
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,KAAK,gBAAgB,EAAsB,MAAM,aAAa,CAAC;AAqUxE,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM,EAAE,YAAY,CAAC,EAAE,MAAM,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,IAgB5K,OAAO;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,EAC3C,QAAQ;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,mBAuR/B;AAyBD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,MAAM,EAAE,EACvB,GAAG,EAAE,GAAG,EACR,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM,EACtC,YAAY,CAAC,EAAE,MAAM,MAAM,EAC3B,SAAS,CAAC,EAAE,MAAM,IAiBhB,OAAO;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,EAC/D,QAAQ;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks.js
CHANGED
|
@@ -632,20 +632,108 @@ export function memoryInjectionHook(client, containerTags, tui, config = {}, get
|
|
|
632
632
|
return stripped.length > 200 ? stripped.slice(0, 200) : stripped;
|
|
633
633
|
})
|
|
634
634
|
: undefined;
|
|
635
|
-
// ========== Phase A:
|
|
636
|
-
|
|
635
|
+
// ========== Phase A: unified data fetch + injection ==========
|
|
636
|
+
let shouldRecallRes;
|
|
637
637
|
let profileBlock = "";
|
|
638
638
|
let profileInjected = false;
|
|
639
639
|
let profileCountText = "";
|
|
640
|
+
let isCacheHit = false;
|
|
641
|
+
const cached = recallCache.get(input.sessionID);
|
|
640
642
|
if (cached) {
|
|
641
|
-
|
|
643
|
+
isCacheHit = true;
|
|
644
|
+
shouldRecallRes = cached.recallResult;
|
|
642
645
|
if (cached.profileBlock) {
|
|
643
646
|
profileBlock = cached.profileBlock;
|
|
644
647
|
profileInjected = true;
|
|
645
648
|
profileCountText = cached.profileData?.countText ?? "";
|
|
646
649
|
}
|
|
647
|
-
|
|
648
|
-
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
// cache miss: synchronous await (first message takes 5-8s, but gets injection)
|
|
653
|
+
const [profile, recallRes] = await Promise.all([
|
|
654
|
+
client.getProfile(),
|
|
655
|
+
client.shouldRecall(query_text, last_query_text, input.sessionID, similarityThreshold, maxRecallResults, projectTags.length > 0 ? projectTags : undefined, conversationContext && conversationContext.length > 0 ? conversationContext : undefined, {
|
|
656
|
+
fetch_multiplier: fetchMultiplier,
|
|
657
|
+
topk_cap_multiplier: topkCapMultiplier,
|
|
658
|
+
mmr_jaccard_threshold: mmrJaccardThreshold,
|
|
659
|
+
mmr_penalty_factor: mmrPenaltyFactor,
|
|
660
|
+
phase2_multiplier: phase2Multiplier,
|
|
661
|
+
llm_max_eval: llmMaxEval,
|
|
662
|
+
refine_strategy: refineStrategy,
|
|
663
|
+
refine_medium_chars: refineMediumChars,
|
|
664
|
+
}, directory || process.env.OMEM_PROJECT_DIR),
|
|
665
|
+
]);
|
|
666
|
+
if (!recallRes) {
|
|
667
|
+
showToast(tui, "🧠 Cerebro Service Unavailable", "Unable to reach memory API", "error", toastDelayMs);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
shouldRecallRes = recallRes;
|
|
671
|
+
// build profile block
|
|
672
|
+
if (profile) {
|
|
673
|
+
const built = buildProfileBlock(profile);
|
|
674
|
+
if (built) {
|
|
675
|
+
profileBlock = built.block;
|
|
676
|
+
profileCountText = built.countText;
|
|
677
|
+
profileInjected = true;
|
|
678
|
+
profileInjectedSessions.set(input.sessionID, Date.now());
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// write cache for next round
|
|
682
|
+
recallCache.set(input.sessionID, {
|
|
683
|
+
profileBlock,
|
|
684
|
+
recallResult: shouldRecallRes,
|
|
685
|
+
profileData: { countText: profileCountText },
|
|
686
|
+
timestamp: Date.now(),
|
|
687
|
+
});
|
|
688
|
+
// LRU eviction
|
|
689
|
+
if (recallCache.size > 50) {
|
|
690
|
+
let oldestKey = null;
|
|
691
|
+
let oldestTime = Infinity;
|
|
692
|
+
for (const [k, v] of recallCache) {
|
|
693
|
+
if (v.timestamp < oldestTime) {
|
|
694
|
+
oldestTime = v.timestamp;
|
|
695
|
+
oldestKey = k;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (oldestKey)
|
|
699
|
+
recallCache.delete(oldestKey);
|
|
700
|
+
}
|
|
701
|
+
// defensive check
|
|
702
|
+
if (shouldRecallRes.should_recall && !Array.isArray(shouldRecallRes.memories)) {
|
|
703
|
+
logErr("memoryInjectionHook shouldRecall returned incomplete data", { shouldRecall: shouldRecallRes.should_recall, hasMemories: !!shouldRecallRes.memories });
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
logDebug("memoryInjectionHook cache miss, fetched synchronously", { sessionId: input.sessionID, shouldRecall: shouldRecallRes.should_recall, memCount: shouldRecallRes.memories?.length ?? 0 });
|
|
707
|
+
}
|
|
708
|
+
// ========== unified injection logic (cache hit + cache miss share this) ==========
|
|
709
|
+
if (!shouldRecallRes.should_recall) {
|
|
710
|
+
// no-recall path: inject profile only
|
|
711
|
+
const partsToInject = [];
|
|
712
|
+
if (profileBlock)
|
|
713
|
+
partsToInject.push(profileBlock);
|
|
714
|
+
if (partsToInject.length > 0) {
|
|
715
|
+
const injectText = partsToInject.join("\n\n");
|
|
716
|
+
const contextPart = {
|
|
717
|
+
id: `prt_cerebro-context-${Date.now()}`,
|
|
718
|
+
sessionID: input.sessionID,
|
|
719
|
+
messageID: output.message.id,
|
|
720
|
+
type: "text",
|
|
721
|
+
text: injectText,
|
|
722
|
+
synthetic: true,
|
|
723
|
+
};
|
|
724
|
+
output.parts.unshift(contextPart);
|
|
725
|
+
logDebug("memoryInjectionHook profile injected (no-recall)", { sessionId: input.sessionID });
|
|
726
|
+
}
|
|
727
|
+
injectedSessions.add(input.sessionID);
|
|
728
|
+
showToast(tui, "🧠 Profile Injected", profileCountText ? `Profile: ${profileCountText} · no recall needed` : "No memory recall needed", "success", toastDelayMs);
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
const results = shouldRecallRes.memories ?? [];
|
|
732
|
+
const clustered = shouldRecallRes.clustered;
|
|
733
|
+
const existingIds = injectedMemoryIds.get(input.sessionID) ?? new Set();
|
|
734
|
+
const newResults = results.filter((r) => !existingIds.has(r.memory.id));
|
|
735
|
+
logDebug("memoryInjectionHook dedup", { totalResults: results.length, existingCount: existingIds.size, newCount: newResults.length });
|
|
736
|
+
if (newResults.length === 0) {
|
|
649
737
|
const partsToInject = [];
|
|
650
738
|
if (profileBlock)
|
|
651
739
|
partsToInject.push(profileBlock);
|
|
@@ -660,261 +748,234 @@ export function memoryInjectionHook(client, containerTags, tui, config = {}, get
|
|
|
660
748
|
synthetic: true,
|
|
661
749
|
};
|
|
662
750
|
output.parts.unshift(contextPart);
|
|
663
|
-
logDebug("memoryInjectionHook profile injected
|
|
751
|
+
logDebug("memoryInjectionHook profile injected (dedup)", { sessionId: input.sessionID });
|
|
664
752
|
}
|
|
665
753
|
injectedSessions.add(input.sessionID);
|
|
666
754
|
}
|
|
667
755
|
else {
|
|
668
|
-
const
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
756
|
+
const profileChars = profileInjected ? profileBlock.length : 0;
|
|
757
|
+
const budgetRemaining = maxContentChars - profileChars;
|
|
758
|
+
const itemCount = clustered
|
|
759
|
+
? (clustered.cluster_summaries.length + clustered.standalone_memories.length)
|
|
760
|
+
: newResults.length;
|
|
761
|
+
const dynamicMaxContentLength = itemCount > 0
|
|
762
|
+
? Math.min(maxContentLength, Math.max(MIN_ITEM_CONTENT_CHARS, Math.floor(budgetRemaining / itemCount)))
|
|
763
|
+
: maxContentLength;
|
|
764
|
+
const block = clustered
|
|
765
|
+
? buildClusteredContextBlock(clustered, dynamicMaxContentLength)
|
|
766
|
+
: buildContextBlock(newResults, dynamicMaxContentLength);
|
|
767
|
+
const partsToInject = [];
|
|
768
|
+
if (block)
|
|
769
|
+
partsToInject.push(block);
|
|
770
|
+
if (block)
|
|
771
|
+
partsToInject.push(FETCH_POLICY);
|
|
772
|
+
if (profileBlock)
|
|
773
|
+
partsToInject.push(profileBlock);
|
|
774
|
+
if (isSaveKeyword)
|
|
775
|
+
partsToInject.push(KEYWORD_NUDGE);
|
|
776
|
+
if (partsToInject.length > 0) {
|
|
777
|
+
const injectText = partsToInject.join("\n\n");
|
|
778
|
+
const contextPart = {
|
|
779
|
+
id: `prt_cerebro-context-${Date.now()}`,
|
|
780
|
+
sessionID: input.sessionID,
|
|
781
|
+
messageID: output.message.id,
|
|
782
|
+
type: "text",
|
|
783
|
+
text: injectText,
|
|
784
|
+
synthetic: true,
|
|
785
|
+
};
|
|
786
|
+
output.parts.unshift(contextPart);
|
|
787
|
+
logDebug("memoryInjectionHook block injected", {
|
|
788
|
+
sessionId: input.sessionID,
|
|
789
|
+
injectTextLen: injectText.length,
|
|
790
|
+
blockPreview: block?.slice(0, 200),
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
injectedSessions.add(input.sessionID);
|
|
794
|
+
if (isSaveKeyword) {
|
|
795
|
+
saveKeywordDetectedSessions.delete(input.sessionID);
|
|
796
|
+
}
|
|
797
|
+
const newIds = newResults.map((r) => r.memory.id);
|
|
798
|
+
injectedMemoryIds.set(input.sessionID, new Set([...existingIds, ...newIds]));
|
|
799
|
+
const memDynamic = newResults.filter((r) => r.memory.memory_type === "fact" || r.memory.memory_type === "event").length;
|
|
800
|
+
const memStatic = newResults.filter((r) => r.memory.memory_type === "pinned" || r.memory.memory_type === "preference").length;
|
|
801
|
+
const memOther = newResults.length - memDynamic - memStatic;
|
|
802
|
+
let memCountMsg = "";
|
|
803
|
+
if (memDynamic > 0)
|
|
804
|
+
memCountMsg += `Dynamic(${memDynamic}) `;
|
|
805
|
+
if (memStatic > 0)
|
|
806
|
+
memCountMsg += `Static(${memStatic}) `;
|
|
807
|
+
if (memOther > 0)
|
|
808
|
+
memCountMsg += `Other(${memOther}) `;
|
|
809
|
+
const categories = categorize(newResults);
|
|
810
|
+
const catSummary = Array.from(categories.entries())
|
|
811
|
+
.map(([label, items]) => `${label}(${items.length})`)
|
|
812
|
+
.join(" · ");
|
|
813
|
+
let toastTitle;
|
|
814
|
+
let toastMessage;
|
|
815
|
+
if (clustered) {
|
|
816
|
+
const clusterCount = clustered.cluster_summaries.length;
|
|
817
|
+
const standaloneCount = clustered.standalone_memories.length;
|
|
818
|
+
toastTitle = `🧠 Context Injected · ${clusterCount} clusters${standaloneCount > 0 ? ` · ${standaloneCount} standalone` : ""}`;
|
|
819
|
+
toastMessage = profileInjected
|
|
820
|
+
? `Profile: ${profileCountText} · Clustered memory display`
|
|
821
|
+
: `Clustered memory display`;
|
|
691
822
|
}
|
|
692
823
|
else {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
: newResults.length;
|
|
698
|
-
const dynamicMaxContentLength = itemCount > 0
|
|
699
|
-
? Math.min(maxContentLength, Math.max(MIN_ITEM_CONTENT_CHARS, Math.floor(budgetRemaining / itemCount)))
|
|
700
|
-
: maxContentLength;
|
|
701
|
-
const block = clustered
|
|
702
|
-
? buildClusteredContextBlock(clustered, dynamicMaxContentLength)
|
|
703
|
-
: buildContextBlock(newResults, dynamicMaxContentLength);
|
|
704
|
-
const partsToInject = [];
|
|
705
|
-
if (block)
|
|
706
|
-
partsToInject.push(block);
|
|
707
|
-
if (block)
|
|
708
|
-
partsToInject.push(FETCH_POLICY);
|
|
709
|
-
if (profileBlock)
|
|
710
|
-
partsToInject.push(profileBlock);
|
|
711
|
-
if (isSaveKeyword)
|
|
712
|
-
partsToInject.push(KEYWORD_NUDGE);
|
|
713
|
-
if (partsToInject.length > 0) {
|
|
714
|
-
const injectText = partsToInject.join("\n\n");
|
|
715
|
-
const contextPart = {
|
|
716
|
-
id: `prt_cerebro-context-${Date.now()}`,
|
|
717
|
-
sessionID: input.sessionID,
|
|
718
|
-
messageID: output.message.id,
|
|
719
|
-
type: "text",
|
|
720
|
-
text: injectText,
|
|
721
|
-
synthetic: true,
|
|
722
|
-
};
|
|
723
|
-
output.parts.unshift(contextPart);
|
|
724
|
-
logDebug("memoryInjectionHook block injected from cache", {
|
|
725
|
-
sessionId: input.sessionID,
|
|
726
|
-
injectTextLen: injectText.length,
|
|
727
|
-
blockPreview: block?.slice(0, 200),
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
injectedSessions.add(input.sessionID);
|
|
731
|
-
if (isSaveKeyword) {
|
|
732
|
-
saveKeywordDetectedSessions.delete(input.sessionID);
|
|
733
|
-
}
|
|
734
|
-
const newIds = newResults.map((r) => r.memory.id);
|
|
735
|
-
injectedMemoryIds.set(input.sessionID, new Set([...existingIds, ...newIds]));
|
|
736
|
-
const memDynamic = newResults.filter((r) => r.memory.memory_type === "fact" || r.memory.memory_type === "event").length;
|
|
737
|
-
const memStatic = newResults.filter((r) => r.memory.memory_type === "pinned" || r.memory.memory_type === "preference").length;
|
|
738
|
-
const memOther = newResults.length - memDynamic - memStatic;
|
|
739
|
-
let memCountMsg = "";
|
|
740
|
-
if (memDynamic > 0)
|
|
741
|
-
memCountMsg += `Dynamic(${memDynamic}) `;
|
|
742
|
-
if (memStatic > 0)
|
|
743
|
-
memCountMsg += `Static(${memStatic}) `;
|
|
744
|
-
if (memOther > 0)
|
|
745
|
-
memCountMsg += `Other(${memOther}) `;
|
|
746
|
-
const categories = categorize(newResults);
|
|
747
|
-
const catSummary = Array.from(categories.entries())
|
|
748
|
-
.map(([label, items]) => `${label}(${items.length})`)
|
|
749
|
-
.join(" · ");
|
|
750
|
-
let toastTitle;
|
|
751
|
-
let toastMessage;
|
|
752
|
-
if (clustered) {
|
|
753
|
-
const clusterCount = clustered.cluster_summaries.length;
|
|
754
|
-
const standaloneCount = clustered.standalone_memories.length;
|
|
755
|
-
toastTitle = `🧠 Context Injected · ${clusterCount} 主题簇${standaloneCount > 0 ? ` · ${standaloneCount} 补充` : ""}`;
|
|
756
|
-
toastMessage = profileInjected
|
|
757
|
-
? `Profile: ${profileCountText} · 聚合记忆展示`
|
|
758
|
-
: `聚合记忆展示`;
|
|
759
|
-
}
|
|
760
|
-
else {
|
|
761
|
-
toastTitle = `🧠 Context Injected · ${newResults.length} fragments`;
|
|
762
|
-
toastMessage = profileInjected
|
|
763
|
-
? `Profile: ${profileCountText} · Memories: ${memCountMsg.trim()}${catSummary ? ` · ${catSummary}` : ""}`
|
|
764
|
-
: `${memCountMsg.trim()}${catSummary ? ` · ${catSummary}` : ""}`;
|
|
765
|
-
}
|
|
766
|
-
showToast(tui, toastTitle, toastMessage, "success", toastDelayMs);
|
|
824
|
+
toastTitle = `🧠 Context Injected · ${newResults.length} fragments`;
|
|
825
|
+
toastMessage = profileInjected
|
|
826
|
+
? `Profile: ${profileCountText} · Memories: ${memCountMsg.trim()}${catSummary ? ` · ${catSummary}` : ""}`
|
|
827
|
+
: `${memCountMsg.trim()}${catSummary ? ` · ${catSummary}` : ""}`;
|
|
767
828
|
}
|
|
829
|
+
showToast(tui, toastTitle, toastMessage, "success", toastDelayMs);
|
|
768
830
|
}
|
|
769
|
-
logDebug("memoryInjectionHook cache hit, injection complete", { sessionId: input.sessionID });
|
|
770
831
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
sessionId: bgSessionId,
|
|
807
|
-
shouldRecall: shouldRecallRes.should_recall,
|
|
808
|
-
confidence: shouldRecallRes.confidence,
|
|
809
|
-
memCount: shouldRecallRes.memories?.length ?? 0,
|
|
810
|
-
});
|
|
811
|
-
if (shouldRecallRes.should_recall && !Array.isArray(shouldRecallRes.memories)) {
|
|
812
|
-
logErr("memoryInjectionHook shouldRecall returned incomplete data", {
|
|
832
|
+
logDebug("memoryInjectionHook injection complete", { sessionId: input.sessionID, isCacheHit });
|
|
833
|
+
// ========== Phase B: fire-and-forget async fetch for NEXT round (cache hit only) ==========
|
|
834
|
+
if (isCacheHit) {
|
|
835
|
+
const bgSessionId = input.sessionID;
|
|
836
|
+
const bgQueryText = query_text;
|
|
837
|
+
const bgLastQueryText = last_query_text;
|
|
838
|
+
const bgConversationContext = conversationContext;
|
|
839
|
+
const bgProjectTags = projectTags.length > 0 ? projectTags : undefined;
|
|
840
|
+
const bgDirectory = directory || process.env.OMEM_PROJECT_DIR;
|
|
841
|
+
Promise.allSettled([
|
|
842
|
+
client.getProfile(),
|
|
843
|
+
client.shouldRecall(bgQueryText, bgLastQueryText, bgSessionId, similarityThreshold, maxRecallResults, bgProjectTags, bgConversationContext && bgConversationContext.length > 0 ? bgConversationContext : undefined, {
|
|
844
|
+
fetch_multiplier: fetchMultiplier,
|
|
845
|
+
topk_cap_multiplier: topkCapMultiplier,
|
|
846
|
+
mmr_jaccard_threshold: mmrJaccardThreshold,
|
|
847
|
+
mmr_penalty_factor: mmrPenaltyFactor,
|
|
848
|
+
phase2_multiplier: phase2Multiplier,
|
|
849
|
+
llm_max_eval: llmMaxEval,
|
|
850
|
+
refine_strategy: refineStrategy,
|
|
851
|
+
refine_medium_chars: refineMediumChars,
|
|
852
|
+
}, bgDirectory),
|
|
853
|
+
])
|
|
854
|
+
.then(([profileRes, recallRes]) => {
|
|
855
|
+
if (recallRes.status === 'rejected') {
|
|
856
|
+
logErr("memoryInjectionHook shouldRecall failed", { error: String(recallRes.reason) });
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
const profile = profileRes.status === 'fulfilled' ? profileRes.value : null;
|
|
860
|
+
const shouldRecallRes = recallRes.value;
|
|
861
|
+
if (!shouldRecallRes) {
|
|
862
|
+
showToast(tui, "🧠 Cerebro Service Unavailable", "Unable to reach memory API · check connection", "error", toastDelayMs);
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
logDebug("memoryInjectionHook background fetch complete", {
|
|
866
|
+
sessionId: bgSessionId,
|
|
813
867
|
shouldRecall: shouldRecallRes.should_recall,
|
|
814
|
-
|
|
868
|
+
confidence: shouldRecallRes.confidence,
|
|
869
|
+
memCount: shouldRecallRes.memories?.length ?? 0,
|
|
815
870
|
});
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
871
|
+
if (shouldRecallRes.should_recall && !Array.isArray(shouldRecallRes.memories)) {
|
|
872
|
+
logErr("memoryInjectionHook shouldRecall returned incomplete data", {
|
|
873
|
+
shouldRecall: shouldRecallRes.should_recall,
|
|
874
|
+
hasMemories: !!shouldRecallRes.memories,
|
|
875
|
+
});
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
let bgProfileBlock = "";
|
|
879
|
+
let bgProfileCountText = "";
|
|
880
|
+
let bgProfileInjected = false;
|
|
881
|
+
if (profile) {
|
|
882
|
+
const lastInjected = profileInjectedSessions.get(bgSessionId);
|
|
883
|
+
const ttlExpired = !lastInjected || (Date.now() - lastInjected > 30 * 60 * 1000);
|
|
884
|
+
if (ttlExpired) {
|
|
885
|
+
const built = buildProfileBlock(profile);
|
|
886
|
+
if (built) {
|
|
887
|
+
bgProfileBlock = built.block;
|
|
888
|
+
bgProfileCountText = built.countText;
|
|
889
|
+
bgProfileInjected = true;
|
|
890
|
+
}
|
|
830
891
|
}
|
|
831
892
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
893
|
+
recallCache.set(bgSessionId, {
|
|
894
|
+
profileBlock: bgProfileBlock,
|
|
895
|
+
recallResult: shouldRecallRes,
|
|
896
|
+
profileData: { countText: bgProfileCountText },
|
|
897
|
+
timestamp: Date.now(),
|
|
898
|
+
});
|
|
899
|
+
if (recallCache.size > 50) {
|
|
900
|
+
let oldestKey = null;
|
|
901
|
+
let oldestTime = Infinity;
|
|
902
|
+
for (const [k, v] of recallCache) {
|
|
903
|
+
if (v.timestamp < oldestTime) {
|
|
904
|
+
oldestTime = v.timestamp;
|
|
905
|
+
oldestKey = k;
|
|
906
|
+
}
|
|
846
907
|
}
|
|
908
|
+
if (oldestKey)
|
|
909
|
+
recallCache.delete(oldestKey);
|
|
847
910
|
}
|
|
848
|
-
if (
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
|
|
911
|
+
if (shouldRecallRes.should_recall) {
|
|
912
|
+
const results = shouldRecallRes.memories ?? [];
|
|
913
|
+
const existingIds = injectedMemoryIds.get(bgSessionId) ?? new Set();
|
|
914
|
+
const newResults = results.filter((r) => !existingIds.has(r.memory.id));
|
|
915
|
+
if (newResults.length > 0) {
|
|
916
|
+
const newIds = newResults.map((r) => r.memory.id);
|
|
917
|
+
injectedMemoryIds.set(bgSessionId, new Set([...existingIds, ...newIds]));
|
|
918
|
+
}
|
|
919
|
+
const storedMemoryIds = shouldRecallRes.memories?.map((r) => r.memory.id) ?? [];
|
|
920
|
+
const storedDiscardedIds = shouldRecallRes.discarded?.map((d) => d.memory_id) ?? [];
|
|
921
|
+
const maxScore = storedMemoryIds.length > 0
|
|
922
|
+
? Math.max(...(shouldRecallRes.memories?.map((r) => r.score) ?? [0]))
|
|
923
|
+
: 0;
|
|
924
|
+
const bgBlock = shouldRecallRes.clustered
|
|
925
|
+
? buildClusteredContextBlock(shouldRecallRes.clustered, maxContentLength)
|
|
926
|
+
: buildContextBlock(newResults, maxContentLength);
|
|
927
|
+
const bgInjectedContent = bgBlock ?? undefined;
|
|
928
|
+
const items = [
|
|
929
|
+
...(shouldRecallRes.memories?.map((r) => ({
|
|
930
|
+
memory_id: r.memory.id,
|
|
931
|
+
score: r.score,
|
|
932
|
+
refine_relevance: r.refine_relevance,
|
|
933
|
+
refine_reasoning: r.refine_reasoning,
|
|
934
|
+
is_kept: true,
|
|
935
|
+
})) ?? []),
|
|
936
|
+
...(shouldRecallRes.discarded?.map((d) => ({
|
|
937
|
+
memory_id: d.memory_id,
|
|
938
|
+
score: d.score,
|
|
939
|
+
refine_relevance: d.refine_relevance,
|
|
940
|
+
refine_reasoning: d.refine_reasoning,
|
|
941
|
+
is_kept: false,
|
|
942
|
+
})) ?? []),
|
|
943
|
+
];
|
|
944
|
+
client.createRecallEvent({
|
|
945
|
+
session_id: bgSessionId,
|
|
946
|
+
recall_type: "auto",
|
|
947
|
+
query_text: bgQueryText,
|
|
948
|
+
max_score: maxScore,
|
|
949
|
+
llm_confidence: shouldRecallRes.confidence ?? 0,
|
|
950
|
+
profile_injected: bgProfileInjected,
|
|
951
|
+
kept_count: storedMemoryIds.length,
|
|
952
|
+
discarded_count: storedDiscardedIds.length,
|
|
953
|
+
injected_count: newResults.length,
|
|
954
|
+
profile_content: bgProfileInjected && bgProfileBlock ? bgProfileBlock : undefined,
|
|
955
|
+
injected_content: bgInjectedContent,
|
|
956
|
+
items: items.length > 0 ? items : undefined,
|
|
957
|
+
}).catch((e) => {
|
|
958
|
+
logErr("memoryInjectionHook background createRecallEvent failed", { error: String(e) });
|
|
959
|
+
});
|
|
858
960
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
score: r.score,
|
|
872
|
-
refine_relevance: r.refine_relevance,
|
|
873
|
-
refine_reasoning: r.refine_reasoning,
|
|
874
|
-
is_kept: true,
|
|
875
|
-
})) ?? []),
|
|
876
|
-
...(shouldRecallRes.discarded?.map((d) => ({
|
|
877
|
-
memory_id: d.memory_id,
|
|
878
|
-
score: d.score,
|
|
879
|
-
refine_relevance: d.refine_relevance,
|
|
880
|
-
refine_reasoning: d.refine_reasoning,
|
|
881
|
-
is_kept: false,
|
|
882
|
-
})) ?? []),
|
|
883
|
-
];
|
|
884
|
-
client.createRecallEvent({
|
|
885
|
-
session_id: bgSessionId,
|
|
886
|
-
recall_type: "auto",
|
|
887
|
-
query_text: bgQueryText,
|
|
888
|
-
max_score: maxScore,
|
|
889
|
-
llm_confidence: shouldRecallRes.confidence ?? 0,
|
|
890
|
-
profile_injected: bgProfileInjected,
|
|
891
|
-
kept_count: storedMemoryIds.length,
|
|
892
|
-
discarded_count: storedDiscardedIds.length,
|
|
893
|
-
injected_count: newResults.length,
|
|
894
|
-
profile_content: bgProfileInjected && bgProfileBlock ? bgProfileBlock : undefined,
|
|
895
|
-
injected_content: bgInjectedContent,
|
|
896
|
-
items: items.length > 0 ? items : undefined,
|
|
897
|
-
}).catch((e) => {
|
|
898
|
-
logErr("memoryInjectionHook background createRecallEvent failed", { error: String(e) });
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
})
|
|
902
|
-
.catch((err) => {
|
|
903
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
904
|
-
logErr("memoryInjectionHook background fetch failed", { error: errMsg });
|
|
905
|
-
if (errMsg.includes("[cerebro]")) {
|
|
906
|
-
const cleanMsg = errMsg.replace(/^\[cerebro\]\s*/, "");
|
|
907
|
-
if (cleanMsg.startsWith("500")) {
|
|
908
|
-
showToast(tui, "🧠 Cerebro Server Error", cleanMsg.substring(0, 200), "error");
|
|
961
|
+
})
|
|
962
|
+
.catch((err) => {
|
|
963
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
964
|
+
logErr("memoryInjectionHook background fetch failed", { error: errMsg });
|
|
965
|
+
if (errMsg.includes("[cerebro]")) {
|
|
966
|
+
const cleanMsg = errMsg.replace(/^\[cerebro\]\s*/, "");
|
|
967
|
+
if (cleanMsg.startsWith("500")) {
|
|
968
|
+
showToast(tui, "🧠 Cerebro Server Error", cleanMsg.substring(0, 200), "error");
|
|
969
|
+
}
|
|
970
|
+
else if (cleanMsg.includes("timed out")) {
|
|
971
|
+
showToast(tui, "🧠 Cerebro Service Timeout", cleanMsg.substring(0, 100), "error");
|
|
972
|
+
}
|
|
909
973
|
}
|
|
910
|
-
else if (
|
|
911
|
-
showToast(tui, "🧠 Cerebro Service
|
|
974
|
+
else if (errMsg.includes("fetch") || errMsg.includes("network")) {
|
|
975
|
+
showToast(tui, "🧠 Cerebro Service Unavailable", "Network error · check API connection", "error");
|
|
912
976
|
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
showToast(tui, "🧠 Cerebro Service Unavailable", "Network error · check API connection", "error");
|
|
916
|
-
}
|
|
917
|
-
});
|
|
977
|
+
});
|
|
978
|
+
}
|
|
918
979
|
}
|
|
919
980
|
catch (err) {
|
|
920
981
|
const errMsg = err instanceof Error ? err.message : String(err);
|