@illuma-ai/agents 1.1.28 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/spawnPath.cjs +104 -0
- package/dist/cjs/common/spawnPath.cjs.map +1 -0
- package/dist/cjs/graphs/Graph.cjs +89 -45
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/HandoffRegistry.cjs +47 -8
- package/dist/cjs/graphs/HandoffRegistry.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +493 -267
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/graphs/phases/flushLoop.cjs +214 -0
- package/dist/cjs/graphs/phases/flushLoop.cjs.map +1 -0
- package/dist/cjs/graphs/phases/memoryFlushPhase.cjs +102 -0
- package/dist/cjs/graphs/phases/memoryFlushPhase.cjs.map +1 -0
- package/dist/cjs/llm/bedrock/index.cjs +4 -3
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +117 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/memory/citations.cjs +69 -0
- package/dist/cjs/memory/citations.cjs.map +1 -0
- package/dist/cjs/memory/compositeBackend.cjs +60 -0
- package/dist/cjs/memory/compositeBackend.cjs.map +1 -0
- package/dist/cjs/memory/constants.cjs +232 -0
- package/dist/cjs/memory/constants.cjs.map +1 -0
- package/dist/cjs/memory/embeddings.cjs +151 -0
- package/dist/cjs/memory/embeddings.cjs.map +1 -0
- package/dist/cjs/memory/factory.cjs +95 -0
- package/dist/cjs/memory/factory.cjs.map +1 -0
- package/dist/cjs/memory/migrate.cjs +81 -0
- package/dist/cjs/memory/migrate.cjs.map +1 -0
- package/dist/cjs/memory/mmr.cjs +138 -0
- package/dist/cjs/memory/mmr.cjs.map +1 -0
- package/dist/cjs/memory/paths.cjs +217 -0
- package/dist/cjs/memory/paths.cjs.map +1 -0
- package/dist/cjs/memory/pgvectorStore.cjs +225 -0
- package/dist/cjs/memory/pgvectorStore.cjs.map +1 -0
- package/dist/cjs/memory/recallTracking.cjs +98 -0
- package/dist/cjs/memory/recallTracking.cjs.map +1 -0
- package/dist/cjs/memory/schema.sql +51 -0
- package/dist/cjs/memory/temporalDecay.cjs +118 -0
- package/dist/cjs/memory/temporalDecay.cjs.map +1 -0
- package/dist/cjs/nodes/ApprovalGateNode.cjs +1 -1
- package/dist/cjs/nodes/ApprovalGateNode.cjs.map +1 -1
- package/dist/cjs/prompts/memoryFlushPrompt.cjs +49 -0
- package/dist/cjs/prompts/memoryFlushPrompt.cjs.map +1 -0
- package/dist/cjs/run.cjs +16 -3
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/AskUser.cjs +6 -1
- package/dist/cjs/tools/AskUser.cjs.map +1 -1
- package/dist/cjs/tools/BrowserTools.cjs +1 -1
- package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +127 -10
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/approval/constants.cjs +2 -2
- package/dist/cjs/tools/approval/constants.cjs.map +1 -1
- package/dist/cjs/tools/memory/index.cjs +58 -0
- package/dist/cjs/tools/memory/index.cjs.map +1 -0
- package/dist/cjs/tools/memory/memoryAppendTool.cjs +69 -0
- package/dist/cjs/tools/memory/memoryAppendTool.cjs.map +1 -0
- package/dist/cjs/tools/memory/memoryGetTool.cjs +49 -0
- package/dist/cjs/tools/memory/memoryGetTool.cjs.map +1 -0
- package/dist/cjs/tools/memory/memorySearchTool.cjs +65 -0
- package/dist/cjs/tools/memory/memorySearchTool.cjs.map +1 -0
- package/dist/cjs/tools/memory/shared.cjs +106 -0
- package/dist/cjs/tools/memory/shared.cjs.map +1 -0
- package/dist/cjs/types/graph.cjs.map +1 -1
- package/dist/cjs/utils/childAgentContext.cjs +242 -0
- package/dist/cjs/utils/childAgentContext.cjs.map +1 -0
- package/dist/cjs/utils/errors.cjs +113 -0
- package/dist/cjs/utils/errors.cjs.map +1 -0
- package/dist/cjs/utils/events.cjs +36 -7
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/finishReasons.cjs +44 -0
- package/dist/cjs/utils/finishReasons.cjs.map +1 -0
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/logging.cjs +34 -0
- package/dist/cjs/utils/logging.cjs.map +1 -0
- package/dist/cjs/utils/toolCallNormalization.cjs +250 -0
- package/dist/cjs/utils/toolCallNormalization.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/spawnPath.mjs +95 -0
- package/dist/esm/common/spawnPath.mjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +89 -45
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/HandoffRegistry.mjs +47 -8
- package/dist/esm/graphs/HandoffRegistry.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +493 -267
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/graphs/phases/flushLoop.mjs +209 -0
- package/dist/esm/graphs/phases/flushLoop.mjs.map +1 -0
- package/dist/esm/graphs/phases/memoryFlushPhase.mjs +99 -0
- package/dist/esm/graphs/phases/memoryFlushPhase.mjs.map +1 -0
- package/dist/esm/llm/bedrock/index.mjs +4 -3
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/main.mjs +21 -0
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/memory/citations.mjs +64 -0
- package/dist/esm/memory/citations.mjs.map +1 -0
- package/dist/esm/memory/compositeBackend.mjs +58 -0
- package/dist/esm/memory/compositeBackend.mjs.map +1 -0
- package/dist/esm/memory/constants.mjs +198 -0
- package/dist/esm/memory/constants.mjs.map +1 -0
- package/dist/esm/memory/embeddings.mjs +148 -0
- package/dist/esm/memory/embeddings.mjs.map +1 -0
- package/dist/esm/memory/factory.mjs +93 -0
- package/dist/esm/memory/factory.mjs.map +1 -0
- package/dist/esm/memory/migrate.mjs +78 -0
- package/dist/esm/memory/migrate.mjs.map +1 -0
- package/dist/esm/memory/mmr.mjs +130 -0
- package/dist/esm/memory/mmr.mjs.map +1 -0
- package/dist/esm/memory/paths.mjs +207 -0
- package/dist/esm/memory/paths.mjs.map +1 -0
- package/dist/esm/memory/pgvectorStore.mjs +223 -0
- package/dist/esm/memory/pgvectorStore.mjs.map +1 -0
- package/dist/esm/memory/recallTracking.mjs +94 -0
- package/dist/esm/memory/recallTracking.mjs.map +1 -0
- package/dist/esm/memory/schema.sql +51 -0
- package/dist/esm/memory/temporalDecay.mjs +110 -0
- package/dist/esm/memory/temporalDecay.mjs.map +1 -0
- package/dist/esm/nodes/ApprovalGateNode.mjs +1 -1
- package/dist/esm/nodes/ApprovalGateNode.mjs.map +1 -1
- package/dist/esm/prompts/memoryFlushPrompt.mjs +44 -0
- package/dist/esm/prompts/memoryFlushPrompt.mjs.map +1 -0
- package/dist/esm/run.mjs +16 -3
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/AskUser.mjs +6 -1
- package/dist/esm/tools/AskUser.mjs.map +1 -1
- package/dist/esm/tools/BrowserTools.mjs +1 -1
- package/dist/esm/tools/BrowserTools.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +128 -11
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/approval/constants.mjs +2 -2
- package/dist/esm/tools/approval/constants.mjs.map +1 -1
- package/dist/esm/tools/memory/index.mjs +46 -0
- package/dist/esm/tools/memory/index.mjs.map +1 -0
- package/dist/esm/tools/memory/memoryAppendTool.mjs +67 -0
- package/dist/esm/tools/memory/memoryAppendTool.mjs.map +1 -0
- package/dist/esm/tools/memory/memoryGetTool.mjs +47 -0
- package/dist/esm/tools/memory/memoryGetTool.mjs.map +1 -0
- package/dist/esm/tools/memory/memorySearchTool.mjs +63 -0
- package/dist/esm/tools/memory/memorySearchTool.mjs.map +1 -0
- package/dist/esm/tools/memory/shared.mjs +98 -0
- package/dist/esm/tools/memory/shared.mjs.map +1 -0
- package/dist/esm/types/graph.mjs.map +1 -1
- package/dist/esm/utils/childAgentContext.mjs +237 -0
- package/dist/esm/utils/childAgentContext.mjs.map +1 -0
- package/dist/esm/utils/errors.mjs +109 -0
- package/dist/esm/utils/errors.mjs.map +1 -0
- package/dist/esm/utils/events.mjs +36 -8
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/finishReasons.mjs +41 -0
- package/dist/esm/utils/finishReasons.mjs.map +1 -0
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/logging.mjs +31 -0
- package/dist/esm/utils/logging.mjs.map +1 -0
- package/dist/esm/utils/toolCallNormalization.mjs +247 -0
- package/dist/esm/utils/toolCallNormalization.mjs.map +1 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/common/spawnPath.d.ts +59 -0
- package/dist/types/graphs/HandoffRegistry.d.ts +24 -7
- package/dist/types/graphs/MultiAgentGraph.d.ts +43 -23
- package/dist/types/graphs/phases/flushLoop.d.ts +106 -0
- package/dist/types/graphs/phases/memoryFlushPhase.d.ts +100 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/memory/__tests__/mockBackend.d.ts +40 -0
- package/dist/types/memory/citations.d.ts +39 -0
- package/dist/types/memory/compositeBackend.d.ts +30 -0
- package/dist/types/memory/constants.d.ts +121 -0
- package/dist/types/memory/embeddings.d.ts +15 -0
- package/dist/types/memory/factory.d.ts +23 -0
- package/dist/types/memory/index.d.ts +21 -0
- package/dist/types/memory/migrate.d.ts +14 -0
- package/dist/types/memory/mmr.d.ts +50 -0
- package/dist/types/memory/paths.d.ts +107 -0
- package/dist/types/memory/pgvectorStore.d.ts +56 -0
- package/dist/types/memory/recallTracking.d.ts +30 -0
- package/dist/types/memory/temporalDecay.d.ts +53 -0
- package/dist/types/memory/types.d.ts +182 -0
- package/dist/types/prompts/memoryFlushPrompt.d.ts +54 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/tools/AskUser.d.ts +1 -1
- package/dist/types/tools/BrowserTools.d.ts +2 -2
- package/dist/types/tools/approval/constants.d.ts +2 -2
- package/dist/types/tools/memory/index.d.ts +39 -0
- package/dist/types/tools/memory/memoryAppendTool.d.ts +27 -0
- package/dist/types/tools/memory/memoryGetTool.d.ts +22 -0
- package/dist/types/tools/memory/memorySearchTool.d.ts +22 -0
- package/dist/types/tools/memory/shared.d.ts +106 -0
- package/dist/types/types/graph.d.ts +10 -3
- package/dist/types/utils/childAgentContext.d.ts +99 -0
- package/dist/types/utils/errors.d.ts +37 -0
- package/dist/types/utils/events.d.ts +21 -0
- package/dist/types/utils/finishReasons.d.ts +32 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/logging.d.ts +2 -0
- package/dist/types/utils/toolCallNormalization.d.ts +44 -0
- package/package.json +6 -4
- package/src/agents/AgentContext.ts +12 -4
- package/src/common/__tests__/enum.test.ts +4 -2
- package/src/common/__tests__/spawnPath.test.ts +110 -0
- package/src/common/index.ts +1 -0
- package/src/common/spawnPath.ts +101 -0
- package/src/graphs/Graph.ts +95 -61
- package/src/graphs/HandoffRegistry.ts +48 -17
- package/src/graphs/MultiAgentGraph.ts +588 -327
- package/src/graphs/__tests__/HandoffRegistry.test.ts +4 -1
- package/src/graphs/__tests__/multi-agent-delegate.test.ts +61 -16
- package/src/graphs/__tests__/multi-agent-edges.test.ts +4 -2
- package/src/graphs/__tests__/multi-agent-nested-subgraph.test.ts +221 -0
- package/src/graphs/__tests__/structured-output.integration.test.ts +212 -118
- package/src/graphs/contextManagement.e2e.test.ts +1 -1
- package/src/graphs/phases/__tests__/flushLoop.test.ts +264 -0
- package/src/graphs/phases/__tests__/memoryFlushPhase.test.ts +37 -0
- package/src/graphs/phases/__tests__/runMemoryFlush.test.ts +150 -0
- package/src/graphs/phases/flushLoop.ts +303 -0
- package/src/graphs/phases/memoryFlushPhase.ts +209 -0
- package/src/index.ts +30 -1
- package/src/llm/bedrock/index.ts +4 -5
- package/src/memory/__tests__/citations.test.ts +61 -0
- package/src/memory/__tests__/compositeBackend.test.ts +79 -0
- package/src/memory/__tests__/isolation.test.ts +206 -0
- package/src/memory/__tests__/mmr.test.ts +148 -0
- package/src/memory/__tests__/mockBackend.ts +161 -0
- package/src/memory/__tests__/paths.test.ts +168 -0
- package/src/memory/__tests__/recallTracking.test.ts +96 -0
- package/src/memory/__tests__/temporalDecay.test.ts +151 -0
- package/src/memory/citations.ts +80 -0
- package/src/memory/compositeBackend.ts +99 -0
- package/src/memory/constants.ts +229 -0
- package/src/memory/embeddings.ts +188 -0
- package/src/memory/factory.ts +111 -0
- package/src/memory/index.ts +46 -0
- package/src/memory/migrate.ts +116 -0
- package/src/memory/mmr.ts +161 -0
- package/src/memory/paths.ts +258 -0
- package/src/memory/pgvectorStore.ts +324 -0
- package/src/memory/recallTracking.ts +127 -0
- package/src/memory/schema.sql +51 -0
- package/src/memory/temporalDecay.ts +134 -0
- package/src/memory/types.ts +185 -0
- package/src/nodes/ApprovalGateNode.ts +4 -10
- package/src/nodes/__tests__/ApprovalGateNode.test.ts +11 -20
- package/src/prompts/memoryFlushPrompt.ts +78 -0
- package/src/run.ts +17 -6
- package/src/scripts/test-bedrock-handoff-autonomous.ts +56 -20
- package/src/specs/agent-handoffs-bedrock.integration.test.ts +8 -5
- package/src/specs/agent-handoffs.test.ts +8 -2
- package/src/tools/AskUser.ts +7 -2
- package/src/tools/BrowserTools.ts +3 -5
- package/src/tools/ToolNode.ts +150 -13
- package/src/tools/__tests__/ToolApproval.test.ts +22 -9
- package/src/tools/approval/__tests__/constants.test.ts +1 -1
- package/src/tools/approval/constants.ts +2 -2
- package/src/tools/memory/__tests__/memoryTools.test.ts +205 -0
- package/src/tools/memory/index.ts +96 -0
- package/src/tools/memory/memoryAppendTool.ts +101 -0
- package/src/tools/memory/memoryGetTool.ts +53 -0
- package/src/tools/memory/memorySearchTool.ts +80 -0
- package/src/tools/memory/shared.ts +169 -0
- package/src/tools/search/search.test.ts +6 -1
- package/src/types/graph.ts +10 -3
- package/src/utils/__tests__/childAgentContext.test.ts +217 -0
- package/src/utils/__tests__/errors.test.ts +136 -0
- package/src/utils/__tests__/finishReasons.test.ts +55 -0
- package/src/utils/__tests__/toolCallNormalization.test.ts +181 -0
- package/src/utils/childAgentContext.ts +259 -0
- package/src/utils/errors.ts +115 -0
- package/src/utils/events.ts +37 -7
- package/src/utils/finishReasons.ts +40 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/llm.ts +0 -1
- package/src/utils/logging.ts +45 -8
- package/src/utils/toolCallNormalization.ts +271 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximal Marginal Relevance (MMR) re-ranking — Phase 2.
|
|
3
|
+
*
|
|
4
|
+
* Ported from upstream `extensions/memory-core/src/memory/mmr.ts` with
|
|
5
|
+
* minor adaptation for our `MemoryEntry` shape (content vs snippet, id vs
|
|
6
|
+
* path+startLine). Behavior is identical: normalize scores, iteratively
|
|
7
|
+
* pick the item that maximizes `λ * relevance - (1-λ) * max_similarity`
|
|
8
|
+
* using Jaccard on tokenized content.
|
|
9
|
+
*
|
|
10
|
+
* @see Carbonell & Goldstein, "The Use of MMR, Diversity-Based Reranking" (1998)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface MMRConfig {
|
|
14
|
+
/** Opt-in. Upstream default is false. */
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
/** 0 = max diversity, 1 = max relevance. Upstream default 0.7. */
|
|
17
|
+
lambda: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const DEFAULT_MMR_CONFIG: MMRConfig = {
|
|
21
|
+
enabled: false,
|
|
22
|
+
lambda: 0.7,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* CJK Unified Ideographs + Extension A, Hiragana/Katakana, Hangul.
|
|
27
|
+
* These lack whitespace boundaries so we must tokenize them differently.
|
|
28
|
+
*/
|
|
29
|
+
const CJK_RE =
|
|
30
|
+
/[\u3040-\u309f\u30a0-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uac00-\ud7af\u1100-\u11ff]/;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Tokenize content into a set for Jaccard similarity.
|
|
34
|
+
*
|
|
35
|
+
* ASCII: alphanumeric + underscore runs, lowercased.
|
|
36
|
+
* CJK: each char becomes a unigram; consecutive pairs become a bigram.
|
|
37
|
+
* Non-adjacent CJK chars (e.g. `我a好`) do NOT form a bigram.
|
|
38
|
+
*/
|
|
39
|
+
export function tokenize(text: string): Set<string> {
|
|
40
|
+
const lower = (text ?? '').toLowerCase();
|
|
41
|
+
const ascii = lower.match(/[a-z0-9_]+/g) ?? [];
|
|
42
|
+
|
|
43
|
+
const chars = Array.from(lower);
|
|
44
|
+
const cjkData: Array<{ char: string; index: number }> = [];
|
|
45
|
+
for (let i = 0; i < chars.length; i++) {
|
|
46
|
+
if (CJK_RE.test(chars[i])) cjkData.push({ char: chars[i], index: i });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const bigrams: string[] = [];
|
|
50
|
+
for (let i = 0; i < cjkData.length - 1; i++) {
|
|
51
|
+
if (cjkData[i + 1].index === cjkData[i].index + 1) {
|
|
52
|
+
bigrams.push(cjkData[i].char + cjkData[i + 1].char);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return new Set([...ascii, ...bigrams, ...cjkData.map((d) => d.char)]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function jaccardSimilarity(a: Set<string>, b: Set<string>): number {
|
|
60
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
61
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
62
|
+
|
|
63
|
+
const [smaller, larger] = a.size <= b.size ? [a, b] : [b, a];
|
|
64
|
+
let intersection = 0;
|
|
65
|
+
for (const t of smaller) if (larger.has(t)) intersection++;
|
|
66
|
+
const union = a.size + b.size - intersection;
|
|
67
|
+
return union === 0 ? 0 : intersection / union;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function textSimilarity(a: string, b: string): number {
|
|
71
|
+
return jaccardSimilarity(tokenize(a), tokenize(b));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function computeMMRScore(
|
|
75
|
+
relevance: number,
|
|
76
|
+
maxSimilarity: number,
|
|
77
|
+
lambda: number
|
|
78
|
+
): number {
|
|
79
|
+
return lambda * relevance - (1 - lambda) * maxSimilarity;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface MMRItem {
|
|
83
|
+
id: string;
|
|
84
|
+
score: number;
|
|
85
|
+
content: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Re-rank items using MMR. Returns a new array in MMR order.
|
|
90
|
+
*/
|
|
91
|
+
export function mmrRerank<T extends MMRItem>(
|
|
92
|
+
items: T[],
|
|
93
|
+
config: Partial<MMRConfig> = {}
|
|
94
|
+
): T[] {
|
|
95
|
+
const enabled = config.enabled ?? DEFAULT_MMR_CONFIG.enabled;
|
|
96
|
+
const rawLambda = config.lambda ?? DEFAULT_MMR_CONFIG.lambda;
|
|
97
|
+
|
|
98
|
+
if (!enabled || items.length <= 1) return [...items];
|
|
99
|
+
|
|
100
|
+
const lambda = Math.max(0, Math.min(1, rawLambda));
|
|
101
|
+
if (lambda === 1) return [...items].sort((a, b) => b.score - a.score);
|
|
102
|
+
|
|
103
|
+
const tokenCache = new Map<string, Set<string>>();
|
|
104
|
+
for (const item of items) tokenCache.set(item.id, tokenize(item.content));
|
|
105
|
+
|
|
106
|
+
const scores = items.map((i) => i.score);
|
|
107
|
+
const maxScore = Math.max(...scores);
|
|
108
|
+
const minScore = Math.min(...scores);
|
|
109
|
+
const range = maxScore - minScore;
|
|
110
|
+
const normalize = (s: number): number =>
|
|
111
|
+
range === 0 ? 1 : (s - minScore) / range;
|
|
112
|
+
|
|
113
|
+
const selected: T[] = [];
|
|
114
|
+
const remaining = new Set(items);
|
|
115
|
+
|
|
116
|
+
while (remaining.size > 0) {
|
|
117
|
+
let best: T | null = null;
|
|
118
|
+
let bestMMR = -Infinity;
|
|
119
|
+
for (const cand of remaining) {
|
|
120
|
+
const rel = normalize(cand.score);
|
|
121
|
+
const candTokens = tokenCache.get(cand.id)!;
|
|
122
|
+
let maxSim = 0;
|
|
123
|
+
for (const sel of selected) {
|
|
124
|
+
const sim = jaccardSimilarity(candTokens, tokenCache.get(sel.id)!);
|
|
125
|
+
if (sim > maxSim) maxSim = sim;
|
|
126
|
+
}
|
|
127
|
+
const mmr = computeMMRScore(rel, maxSim, lambda);
|
|
128
|
+
if (
|
|
129
|
+
mmr > bestMMR ||
|
|
130
|
+
(mmr === bestMMR && cand.score > (best?.score ?? -Infinity))
|
|
131
|
+
) {
|
|
132
|
+
bestMMR = mmr;
|
|
133
|
+
best = cand;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!best) break;
|
|
137
|
+
selected.push(best);
|
|
138
|
+
remaining.delete(best);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return selected;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Adapter: apply MMR to an array of MemoryEntry-shaped hits.
|
|
146
|
+
*
|
|
147
|
+
* Uses (path|id|index) as the stable ID so two hits from the same file at
|
|
148
|
+
* different content still get distinct MMR identities.
|
|
149
|
+
*/
|
|
150
|
+
export function applyMMRToMemoryHits<
|
|
151
|
+
T extends { id: string; path: string; content: string; score: number },
|
|
152
|
+
>(results: T[], config: Partial<MMRConfig> = {}): T[] {
|
|
153
|
+
if (results.length <= 1) return results;
|
|
154
|
+
const byId = new Map<string, T>();
|
|
155
|
+
const items: MMRItem[] = results.map((r, i) => {
|
|
156
|
+
const id = `${r.path}#${r.id}#${i}`;
|
|
157
|
+
byId.set(id, r);
|
|
158
|
+
return { id, score: r.score, content: r.content };
|
|
159
|
+
});
|
|
160
|
+
return mmrRerank(items, config).map((m) => byId.get(m.id)!);
|
|
161
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous memory — canonical path whitelist + tier utilities.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the 8 stable memory documents. Every write
|
|
5
|
+
* goes through {@link assertWritablePath}; every reader can ask
|
|
6
|
+
* {@link getTierForPath} what a row belongs to. Adding or removing a
|
|
7
|
+
* path means editing this file — and exactly this file.
|
|
8
|
+
*
|
|
9
|
+
* ## Why a whitelist?
|
|
10
|
+
*
|
|
11
|
+
* Earlier upstream-faithful designs used date-keyed files
|
|
12
|
+
* (`memory/YYYY-MM-DD.md`), which have three problems for a persistent
|
|
13
|
+
* multi-user agent:
|
|
14
|
+
*
|
|
15
|
+
* 1. **Unbounded growth** — one row per day per agent per user, forever.
|
|
16
|
+
* 2. **LLM routing ambiguity** — "which date file do I read?" has no
|
|
17
|
+
* good answer, so the model reads (and ranks against) all of them.
|
|
18
|
+
* 3. **No natural deduplication** — the same fact written across three
|
|
19
|
+
* days returns three near-identical hits.
|
|
20
|
+
*
|
|
21
|
+
* With 8 stable canonical documents the LLM knows *exactly* where to
|
|
22
|
+
* write ("is this a preference? → `memory/user/preferences.md`") and
|
|
23
|
+
* each row accumulates via UPSERT instead of piling up new rows.
|
|
24
|
+
*
|
|
25
|
+
* ## Two tiers
|
|
26
|
+
*
|
|
27
|
+
* - **Agent tier** (`memory/agent/*`) — operational knowledge that
|
|
28
|
+
* every caller of the agent benefits from. Stored with `user_id = NULL`.
|
|
29
|
+
* Shared across all users of a collaborative agent; the only tier that
|
|
30
|
+
* exists for isolated/autonomous agents with no invoker.
|
|
31
|
+
*
|
|
32
|
+
* - **User tier** (`memory/user/*`) — per-invoker personalization.
|
|
33
|
+
* Stored with `user_id = <caller>`. Read path filters so User A never
|
|
34
|
+
* sees User B's rows, even for the same agent. Not writable unless the
|
|
35
|
+
* flush scope carries a non-empty `userId`.
|
|
36
|
+
*
|
|
37
|
+
* The tier of a path is a function of its prefix (`memory/agent/` vs
|
|
38
|
+
* `memory/user/`), so adding a new document is one line here + the
|
|
39
|
+
* constant array entry; no store or route code has to change.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { MEMORY_PATH_PREFIX } from './constants';
|
|
43
|
+
import type { MemoryScope } from './types';
|
|
44
|
+
|
|
45
|
+
/** Tier discriminator — the two kinds of memory a row can belong to. */
|
|
46
|
+
export type MemoryTier = 'agent' | 'user';
|
|
47
|
+
|
|
48
|
+
/** Every canonical document the flush phase is allowed to write. */
|
|
49
|
+
export interface MemoryPathDescriptor {
|
|
50
|
+
/** Full path including the `memory/` prefix. */
|
|
51
|
+
path: string;
|
|
52
|
+
/** Which tier the path lives under. */
|
|
53
|
+
tier: MemoryTier;
|
|
54
|
+
/** Short label shown in UI tier badges and the flush prompt rubric. */
|
|
55
|
+
label: string;
|
|
56
|
+
/** One-line description — fed to the LLM to make routing unambiguous. */
|
|
57
|
+
description: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Agent-tier documents — shared across all users of the agent. */
|
|
61
|
+
export const MEMORY_AGENT_PATHS: readonly MemoryPathDescriptor[] =
|
|
62
|
+
Object.freeze([
|
|
63
|
+
Object.freeze({
|
|
64
|
+
path: 'memory/agent/playbook.md',
|
|
65
|
+
tier: 'agent' as const,
|
|
66
|
+
label: 'Playbook',
|
|
67
|
+
description:
|
|
68
|
+
'Successful task patterns and workflows that have worked for this agent — ' +
|
|
69
|
+
'reusable recipes, known-good tool sequences, approaches that consistently ' +
|
|
70
|
+
'produce the right result.',
|
|
71
|
+
}),
|
|
72
|
+
Object.freeze({
|
|
73
|
+
path: 'memory/agent/pitfalls.md',
|
|
74
|
+
tier: 'agent' as const,
|
|
75
|
+
label: 'Pitfalls',
|
|
76
|
+
description:
|
|
77
|
+
'Tool failures, error recoveries, schema mistakes, and invalid argument ' +
|
|
78
|
+
'shapes the agent has seen — so the same mistake is not repeated on future ' +
|
|
79
|
+
'turns. One note per distinct failure mode.',
|
|
80
|
+
}),
|
|
81
|
+
Object.freeze({
|
|
82
|
+
path: 'memory/agent/domain.md',
|
|
83
|
+
tier: 'agent' as const,
|
|
84
|
+
label: 'Domain',
|
|
85
|
+
description:
|
|
86
|
+
'Durable facts about the systems, APIs, data models, and business rules ' +
|
|
87
|
+
'this agent operates on — things that are true independent of any one user ' +
|
|
88
|
+
'and that the agent repeatedly needs to know.',
|
|
89
|
+
}),
|
|
90
|
+
Object.freeze({
|
|
91
|
+
path: 'memory/agent/style.md',
|
|
92
|
+
tier: 'agent' as const,
|
|
93
|
+
label: 'Style',
|
|
94
|
+
description:
|
|
95
|
+
'Tone, formatting, and response-shape conventions the agent has converged ' +
|
|
96
|
+
'on across the whole user base. Do NOT write user-specific preferences here ' +
|
|
97
|
+
'(those belong under the user tier).',
|
|
98
|
+
}),
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
/** User-tier documents — per-invoker, never shared across users. */
|
|
102
|
+
export const MEMORY_USER_PATHS: readonly MemoryPathDescriptor[] = Object.freeze(
|
|
103
|
+
[
|
|
104
|
+
Object.freeze({
|
|
105
|
+
path: 'memory/user/profile.md',
|
|
106
|
+
tier: 'user' as const,
|
|
107
|
+
label: 'Profile',
|
|
108
|
+
description:
|
|
109
|
+
"This specific user's identity — name, role, team, sign-off name, " +
|
|
110
|
+
'responsibilities, stable facts about who they are. Only facts ' +
|
|
111
|
+
'volunteered by the user themselves.',
|
|
112
|
+
}),
|
|
113
|
+
Object.freeze({
|
|
114
|
+
path: 'memory/user/preferences.md',
|
|
115
|
+
tier: 'user' as const,
|
|
116
|
+
label: 'Preferences',
|
|
117
|
+
description:
|
|
118
|
+
'How THIS user wants things done — preferred formats, verbosity, tone, ' +
|
|
119
|
+
'cadence, output length, language conventions. Explicit corrections they ' +
|
|
120
|
+
'have given count as preferences.',
|
|
121
|
+
}),
|
|
122
|
+
Object.freeze({
|
|
123
|
+
path: 'memory/user/projects.md',
|
|
124
|
+
tier: 'user' as const,
|
|
125
|
+
label: 'Projects',
|
|
126
|
+
description:
|
|
127
|
+
"This user's current initiatives, ongoing work, deadlines, and active " +
|
|
128
|
+
'project context. Include dates in the prose when relevant. Retire entries ' +
|
|
129
|
+
'by overwriting them on future flushes when the user indicates a project is done.',
|
|
130
|
+
}),
|
|
131
|
+
Object.freeze({
|
|
132
|
+
path: 'memory/user/references.md',
|
|
133
|
+
tier: 'user' as const,
|
|
134
|
+
label: 'References',
|
|
135
|
+
description:
|
|
136
|
+
'External systems, dashboards, accounts, links, channels, and resources ' +
|
|
137
|
+
"THIS user has pointed the agent at — the 'where to look' pointers that " +
|
|
138
|
+
'make later turns more efficient.',
|
|
139
|
+
}),
|
|
140
|
+
]
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
/** All 8 canonical documents in display order: agent tier first, user tier second. */
|
|
144
|
+
export const MEMORY_ALL_PATHS: readonly MemoryPathDescriptor[] = Object.freeze([
|
|
145
|
+
...MEMORY_AGENT_PATHS,
|
|
146
|
+
...MEMORY_USER_PATHS,
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
/** Fast O(1) lookup map: path → descriptor. */
|
|
150
|
+
const PATH_INDEX: ReadonlyMap<string, MemoryPathDescriptor> = new Map(
|
|
151
|
+
MEMORY_ALL_PATHS.map((p) => [p.path, p])
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
/** Set of all whitelisted paths — for `has()` lookups. */
|
|
155
|
+
export const MEMORY_WRITABLE_PATHS: ReadonlySet<string> = new Set(
|
|
156
|
+
MEMORY_ALL_PATHS.map((p) => p.path)
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
/** Returns the descriptor for a canonical path, or `undefined` if not on the whitelist. */
|
|
160
|
+
export function getPathDescriptor(
|
|
161
|
+
path: string
|
|
162
|
+
): MemoryPathDescriptor | undefined {
|
|
163
|
+
return PATH_INDEX.get(path);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Returns the tier a given path belongs to.
|
|
168
|
+
*
|
|
169
|
+
* For unknown paths (e.g. legacy date-keyed rows that predate this schema),
|
|
170
|
+
* falls back to inspecting the `memory/agent/` or `memory/user/` prefix.
|
|
171
|
+
* Anything that matches neither is classified as `agent` — it will surface
|
|
172
|
+
* in the admin UI under the agent-tier header and stay read-only there.
|
|
173
|
+
*/
|
|
174
|
+
export function getTierForPath(path: string): MemoryTier {
|
|
175
|
+
const descriptor = PATH_INDEX.get(path);
|
|
176
|
+
if (descriptor) return descriptor.tier;
|
|
177
|
+
if (path.startsWith('memory/user/')) return 'user';
|
|
178
|
+
return 'agent';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Paths that are legal to write **for this caller's scope**.
|
|
183
|
+
*
|
|
184
|
+
* If `scope.userId` is absent (isolated/autonomous agent), the user-tier
|
|
185
|
+
* paths are dropped. This is the list the flush prompt renders into its
|
|
186
|
+
* rubric — the LLM never sees paths it cannot write to.
|
|
187
|
+
*/
|
|
188
|
+
export function getWritablePathsForScope(
|
|
189
|
+
scope: Pick<MemoryScope, 'userId'>
|
|
190
|
+
): readonly MemoryPathDescriptor[] {
|
|
191
|
+
if (scope.userId == null || scope.userId === '') {
|
|
192
|
+
return MEMORY_AGENT_PATHS;
|
|
193
|
+
}
|
|
194
|
+
return MEMORY_ALL_PATHS;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Validates a write path against the whitelist AND the caller's scope.
|
|
199
|
+
*
|
|
200
|
+
* Throws with an actionable message when:
|
|
201
|
+
* - the path is missing the `memory/` prefix
|
|
202
|
+
* - the path is not on the 8-doc whitelist
|
|
203
|
+
* - the path is user-tier but `scope.userId` is missing
|
|
204
|
+
*
|
|
205
|
+
* The store's append() is the single call site; other callers should
|
|
206
|
+
* never hit this directly. Kept as a standalone function for testability.
|
|
207
|
+
*/
|
|
208
|
+
export function assertWritablePath(
|
|
209
|
+
path: string,
|
|
210
|
+
scope: Pick<MemoryScope, 'userId'>
|
|
211
|
+
): MemoryPathDescriptor {
|
|
212
|
+
if (!path || !path.startsWith(MEMORY_PATH_PREFIX)) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`memory_append path must start with "${MEMORY_PATH_PREFIX}" (received "${path}")`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const descriptor = PATH_INDEX.get(path);
|
|
218
|
+
if (!descriptor) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`memory_append path "${path}" is not on the canonical whitelist. ` +
|
|
221
|
+
`Allowed: ${MEMORY_ALL_PATHS.map((p) => p.path).join(', ')}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (
|
|
225
|
+
descriptor.tier === 'user' &&
|
|
226
|
+
(scope.userId == null || scope.userId === '')
|
|
227
|
+
) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`memory_append to user-tier path "${path}" requires a caller userId in scope, ` +
|
|
230
|
+
`but scope.userId is missing. This path is private to a specific user and ` +
|
|
231
|
+
`cannot be written from an isolated/autonomous context.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return descriptor;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Renders the whitelist as a compact bullet list, used inside the flush
|
|
239
|
+
* prompt so the LLM sees the authoritative rubric at inference time.
|
|
240
|
+
*
|
|
241
|
+
* Output shape:
|
|
242
|
+
* - memory/agent/playbook.md [Playbook, shared] — <description>
|
|
243
|
+
* - memory/agent/pitfalls.md [Pitfalls, shared] — <description>
|
|
244
|
+
* ...
|
|
245
|
+
*
|
|
246
|
+
* The `[Label, shared|private]` tag tells the model whether the doc is
|
|
247
|
+
* visible to all users (agent tier) or only to the caller (user tier).
|
|
248
|
+
* Filtered to only the paths writable for the given scope.
|
|
249
|
+
*/
|
|
250
|
+
export function renderPathsRubric(scope: Pick<MemoryScope, 'userId'>): string {
|
|
251
|
+
const writable = getWritablePathsForScope(scope);
|
|
252
|
+
return writable
|
|
253
|
+
.map((p) => {
|
|
254
|
+
const visibility = p.tier === 'agent' ? 'shared' : 'private';
|
|
255
|
+
return `- ${p.path} [${p.label}, ${visibility}] — ${p.description}`;
|
|
256
|
+
})
|
|
257
|
+
.join('\n');
|
|
258
|
+
}
|