@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,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SPAWN_PATH_SEP,
|
|
3
|
+
MAX_NESTING_DEPTH,
|
|
4
|
+
buildSpawnPath,
|
|
5
|
+
spawnPathDepth,
|
|
6
|
+
parentSpawnPath,
|
|
7
|
+
spawnPathParts,
|
|
8
|
+
leafSpawnKey,
|
|
9
|
+
isAncestorSpawnPath,
|
|
10
|
+
} from '../spawnPath';
|
|
11
|
+
|
|
12
|
+
describe('spawnPath utilities', () => {
|
|
13
|
+
describe('constants', () => {
|
|
14
|
+
it('exports a slash separator', () => {
|
|
15
|
+
expect(SPAWN_PATH_SEP).toBe('/');
|
|
16
|
+
});
|
|
17
|
+
it('default max nesting depth is 5', () => {
|
|
18
|
+
expect(MAX_NESTING_DEPTH).toBe(5);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('buildSpawnPath', () => {
|
|
23
|
+
it('returns key as-is when parent is empty/null/undefined', () => {
|
|
24
|
+
expect(buildSpawnPath('', 'a')).toBe('a');
|
|
25
|
+
expect(buildSpawnPath(null, 'a')).toBe('a');
|
|
26
|
+
expect(buildSpawnPath(undefined, 'a')).toBe('a');
|
|
27
|
+
});
|
|
28
|
+
it('appends key to parent with separator', () => {
|
|
29
|
+
expect(buildSpawnPath('a', 'b')).toBe('a/b');
|
|
30
|
+
expect(buildSpawnPath('a/b', 'c')).toBe('a/b/c');
|
|
31
|
+
});
|
|
32
|
+
it('throws on empty key', () => {
|
|
33
|
+
expect(() => buildSpawnPath('a', '')).toThrow(/empty key/);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('spawnPathDepth', () => {
|
|
38
|
+
it('root is depth 0', () => {
|
|
39
|
+
expect(spawnPathDepth('')).toBe(0);
|
|
40
|
+
expect(spawnPathDepth(null)).toBe(0);
|
|
41
|
+
expect(spawnPathDepth(undefined)).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
it('single segment is depth 1', () => {
|
|
44
|
+
expect(spawnPathDepth('a')).toBe(1);
|
|
45
|
+
});
|
|
46
|
+
it('multi segment counts segments', () => {
|
|
47
|
+
expect(spawnPathDepth('a/b')).toBe(2);
|
|
48
|
+
expect(spawnPathDepth('a/b/c/d')).toBe(4);
|
|
49
|
+
});
|
|
50
|
+
it('ignores empty segments from stray separators', () => {
|
|
51
|
+
expect(spawnPathDepth('a//b')).toBe(2);
|
|
52
|
+
expect(spawnPathDepth('/a/b')).toBe(2);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('parentSpawnPath', () => {
|
|
57
|
+
it('root returns null', () => {
|
|
58
|
+
expect(parentSpawnPath('')).toBeNull();
|
|
59
|
+
expect(parentSpawnPath(null)).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
it('single segment returns empty (root)', () => {
|
|
62
|
+
expect(parentSpawnPath('a')).toBe('');
|
|
63
|
+
});
|
|
64
|
+
it('multi segment drops last', () => {
|
|
65
|
+
expect(parentSpawnPath('a/b')).toBe('a');
|
|
66
|
+
expect(parentSpawnPath('a/b/c')).toBe('a/b');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('spawnPathParts', () => {
|
|
71
|
+
it('empty path returns empty array', () => {
|
|
72
|
+
expect(spawnPathParts('')).toEqual([]);
|
|
73
|
+
expect(spawnPathParts(null)).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
it('splits segments', () => {
|
|
76
|
+
expect(spawnPathParts('a/b/c')).toEqual(['a', 'b', 'c']);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('leafSpawnKey', () => {
|
|
81
|
+
it('root returns null', () => {
|
|
82
|
+
expect(leafSpawnKey('')).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
it('returns last segment', () => {
|
|
85
|
+
expect(leafSpawnKey('a')).toBe('a');
|
|
86
|
+
expect(leafSpawnKey('a/b/c')).toBe('c');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('isAncestorSpawnPath', () => {
|
|
91
|
+
it('root is ancestor of any non-root path', () => {
|
|
92
|
+
expect(isAncestorSpawnPath('', 'a')).toBe(true);
|
|
93
|
+
expect(isAncestorSpawnPath('', 'a/b')).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it('root is not ancestor of root', () => {
|
|
96
|
+
expect(isAncestorSpawnPath('', '')).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
it('strict ancestor detection', () => {
|
|
99
|
+
expect(isAncestorSpawnPath('a', 'a/b')).toBe(true);
|
|
100
|
+
expect(isAncestorSpawnPath('a/b', 'a/b/c')).toBe(true);
|
|
101
|
+
expect(isAncestorSpawnPath('a', 'a')).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
it('sibling is not ancestor', () => {
|
|
104
|
+
expect(isAncestorSpawnPath('a/b', 'a/c')).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it('partial-prefix is not ancestor (must respect segment boundary)', () => {
|
|
107
|
+
expect(isAncestorSpawnPath('ab', 'abc')).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
package/src/common/index.ts
CHANGED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spawnPath — hierarchical invocation identity for nested multi-agent orchestration.
|
|
3
|
+
*
|
|
4
|
+
* A spawnPath is a slash-separated chain of spawnKeys from the root of the current
|
|
5
|
+
* agent invocation to the current spawn. The root agent has an empty spawnPath;
|
|
6
|
+
* each handoff/transfer/sequence that spawns a new subgraph appends a new spawnKey.
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* "" → primary agent (no spawn)
|
|
10
|
+
* "call_abc" → first-level handoff child
|
|
11
|
+
* "call_abc/call_def" → grandchild (depth 2)
|
|
12
|
+
* "call_abc/call_def/call_ghi" → depth 3
|
|
13
|
+
*
|
|
14
|
+
* These utilities are the single source of truth for path manipulation across:
|
|
15
|
+
* - @illuma-ai/agents (MultiAgentGraph, HandoffRegistry, callbacks)
|
|
16
|
+
* - host api (initialize.js, callbacks.js, ExecutionTrace writes)
|
|
17
|
+
* - host client (subagent store, sidebar rendering)
|
|
18
|
+
*
|
|
19
|
+
* See docs/multi-agent-nesting-architecture.md for the full design.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** Separator between spawnKeys in a spawnPath. Chosen so that the path looks
|
|
23
|
+
* like a filesystem/URL path, which makes it easy to read in logs and traces. */
|
|
24
|
+
export const SPAWN_PATH_SEP = '/';
|
|
25
|
+
|
|
26
|
+
/** Hard cap on nested multi-agent invocations. Prevents runaway recursion.
|
|
27
|
+
* Can be overridden at the host api layer via MULTI_AGENT_MAX_NESTING_DEPTH. */
|
|
28
|
+
export const MAX_NESTING_DEPTH = 5;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Append a spawnKey to a parent spawnPath.
|
|
32
|
+
*
|
|
33
|
+
* @param parent - Parent spawnPath (may be undefined/null/empty for root)
|
|
34
|
+
* @param key - spawnKey to append
|
|
35
|
+
* @returns New spawnPath string
|
|
36
|
+
*/
|
|
37
|
+
export function buildSpawnPath(
|
|
38
|
+
parent: string | undefined | null,
|
|
39
|
+
key: string
|
|
40
|
+
): string {
|
|
41
|
+
if (!key) {
|
|
42
|
+
throw new Error('[spawnPath] buildSpawnPath called with empty key');
|
|
43
|
+
}
|
|
44
|
+
if (parent == null || parent === '') return key;
|
|
45
|
+
return `${parent}${SPAWN_PATH_SEP}${key}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compute the depth of a spawnPath.
|
|
50
|
+
* Root (empty) → 0; single-segment → 1; etc.
|
|
51
|
+
*/
|
|
52
|
+
export function spawnPathDepth(path: string | undefined | null): number {
|
|
53
|
+
if (path == null || path === '') return 0;
|
|
54
|
+
return path.split(SPAWN_PATH_SEP).filter(Boolean).length;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Return the parent spawnPath, or null if the input is already root.
|
|
59
|
+
*
|
|
60
|
+
* - parentSpawnPath("a/b/c") === "a/b"
|
|
61
|
+
* - parentSpawnPath("a") === ""
|
|
62
|
+
* - parentSpawnPath("") === null
|
|
63
|
+
*/
|
|
64
|
+
export function parentSpawnPath(
|
|
65
|
+
path: string | undefined | null
|
|
66
|
+
): string | null {
|
|
67
|
+
if (path == null || path === '') return null;
|
|
68
|
+
const parts = path.split(SPAWN_PATH_SEP).filter(Boolean);
|
|
69
|
+
if (parts.length <= 1) return '';
|
|
70
|
+
return parts.slice(0, -1).join(SPAWN_PATH_SEP);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Split a spawnPath into its constituent spawnKey segments. */
|
|
74
|
+
export function spawnPathParts(path: string | undefined | null): string[] {
|
|
75
|
+
if (path == null || path === '') return [];
|
|
76
|
+
return path.split(SPAWN_PATH_SEP).filter(Boolean);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Return the last spawnKey in a spawnPath (the "current" spawn).
|
|
81
|
+
* Returns null for root.
|
|
82
|
+
*/
|
|
83
|
+
export function leafSpawnKey(path: string | undefined | null): string | null {
|
|
84
|
+
const parts = spawnPathParts(path);
|
|
85
|
+
return parts.length === 0 ? null : parts[parts.length - 1];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* True if `ancestor` is a strict ancestor of `descendant`. Root ("") is
|
|
90
|
+
* ancestor of everything except itself.
|
|
91
|
+
*/
|
|
92
|
+
export function isAncestorSpawnPath(
|
|
93
|
+
ancestor: string | undefined | null,
|
|
94
|
+
descendant: string | undefined | null
|
|
95
|
+
): boolean {
|
|
96
|
+
const a = ancestor ?? '';
|
|
97
|
+
const d = descendant ?? '';
|
|
98
|
+
if (a === d) return false;
|
|
99
|
+
if (a === '') return d !== '';
|
|
100
|
+
return d.startsWith(a + SPAWN_PATH_SEP);
|
|
101
|
+
}
|
package/src/graphs/Graph.ts
CHANGED
|
@@ -79,6 +79,10 @@ import { getChatModelClass, manualToolStreamProviders } from '@/llm/providers';
|
|
|
79
79
|
import { ToolNode as CustomToolNode, toolsCondition } from '@/tools/ToolNode';
|
|
80
80
|
import { ChatOpenAI, AzureChatOpenAI } from '@/llm/openai';
|
|
81
81
|
import { safeDispatchCustomEvent } from '@/utils/events';
|
|
82
|
+
import { mlog, mwarn } from '@/utils/logging';
|
|
83
|
+
import { normalizeMessageToolCalls } from '@/utils/toolCallNormalization';
|
|
84
|
+
import { isTruncationReason } from '@/utils/finishReasons';
|
|
85
|
+
import { isLikelyContextOverflowError } from '@/utils/errors';
|
|
82
86
|
import {
|
|
83
87
|
detectDocuments,
|
|
84
88
|
shouldInjectMultiDocHint,
|
|
@@ -1144,10 +1148,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1144
1148
|
const resolved = agentContext.resolveStructuredOutputMode();
|
|
1145
1149
|
method = resolved.method;
|
|
1146
1150
|
if (resolved.warnings.length > 0) {
|
|
1147
|
-
|
|
1148
|
-
'[Graph] Structured output mode warnings:',
|
|
1149
|
-
resolved.warnings
|
|
1150
|
-
);
|
|
1151
|
+
mwarn('[Graph] Structured output mode warnings:', resolved.warnings);
|
|
1151
1152
|
}
|
|
1152
1153
|
} else {
|
|
1153
1154
|
// Legacy fallback: use the old mode-based resolution
|
|
@@ -1172,7 +1173,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1172
1173
|
);
|
|
1173
1174
|
preparedSchema = prepared;
|
|
1174
1175
|
if (warnings.length > 0) {
|
|
1175
|
-
|
|
1176
|
+
mwarn('[Graph] Schema preparation warnings:', warnings);
|
|
1176
1177
|
}
|
|
1177
1178
|
}
|
|
1178
1179
|
|
|
@@ -1264,7 +1265,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1264
1265
|
? handleErrors
|
|
1265
1266
|
: `The response did not match the expected schema. Error: ${lastError.message}. Please try again with a valid response.`;
|
|
1266
1267
|
|
|
1267
|
-
|
|
1268
|
+
mwarn(
|
|
1268
1269
|
`[Graph] Structured output attempt ${attempts} failed: ${lastError.message}. Retrying...`
|
|
1269
1270
|
);
|
|
1270
1271
|
|
|
@@ -1467,7 +1468,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1467
1468
|
this._toolDiscoveryCache.getNewDiscoveries(messages);
|
|
1468
1469
|
if (cachedDiscoveries.length > 0) {
|
|
1469
1470
|
agentContext.markToolsAsDiscovered(cachedDiscoveries);
|
|
1470
|
-
|
|
1471
|
+
mlog(
|
|
1471
1472
|
`[Graph:ToolDiscovery] Cached ${cachedDiscoveries.length} new tools (total: ${this._toolDiscoveryCache.size})`
|
|
1472
1473
|
);
|
|
1473
1474
|
}
|
|
@@ -1498,11 +1499,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1498
1499
|
});
|
|
1499
1500
|
|
|
1500
1501
|
// DEBUG: Log which model and tools each agent uses during handoff
|
|
1501
|
-
|
|
1502
|
+
mlog(
|
|
1502
1503
|
`[createCallModel] Agent "${agentId}" invoking LLM | provider=${agentContext.provider} | ` +
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1504
|
+
`model=${(effectiveClientOptions as Record<string, unknown>).model ?? 'default'} | ` +
|
|
1505
|
+
`toolsForBinding=${toolsForBinding?.length ?? 0} | ` +
|
|
1506
|
+
`toolNames=[${(toolsForBinding ?? []).map((t) => (t as { name?: string }).name ?? 'unknown').join(', ')}]`
|
|
1506
1507
|
);
|
|
1507
1508
|
|
|
1508
1509
|
if (agentContext.systemRunnable) {
|
|
@@ -1515,7 +1516,15 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1515
1516
|
if (!config.signal) {
|
|
1516
1517
|
config.signal = this.signal;
|
|
1517
1518
|
}
|
|
1518
|
-
this.config
|
|
1519
|
+
// First-writer-wins: `this.config` is used ONLY as a "has a run started"
|
|
1520
|
+
// existence flag by the dispatch* methods (they never read its value —
|
|
1521
|
+
// they read the current RunnableConfig from LangChain AsyncLocalStorage).
|
|
1522
|
+
// Unconditionally reassigning here races across concurrent child
|
|
1523
|
+
// subgraph.invoke() calls under parallel multi-agent handoffs; the last
|
|
1524
|
+
// writer wins, and any dispatch firing between writes would historically
|
|
1525
|
+
// have been tagged with the wrong child's metadata. Keeping the first
|
|
1526
|
+
// write pinned makes this a true flag, eliminating the race.
|
|
1527
|
+
this.config ??= config;
|
|
1519
1528
|
|
|
1520
1529
|
let messagesToUse = messages;
|
|
1521
1530
|
|
|
@@ -1619,7 +1628,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1619
1628
|
|
|
1620
1629
|
if (oldMessages.length > 0) {
|
|
1621
1630
|
this._summaryInFlight = true;
|
|
1622
|
-
|
|
1631
|
+
mlog(
|
|
1623
1632
|
`[Graph:ProactiveSummary] Context at ${utilization.toFixed(1)}% (threshold ${threshold}%) — summarizing ${oldMessages.length} older msgs in background`
|
|
1624
1633
|
);
|
|
1625
1634
|
|
|
@@ -1628,7 +1637,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1628
1637
|
.then((updated) => {
|
|
1629
1638
|
if (updated != null && updated !== '') {
|
|
1630
1639
|
this._cachedRunSummary = updated;
|
|
1631
|
-
|
|
1640
|
+
mlog(
|
|
1632
1641
|
`[Graph:ProactiveSummary] Background summary ready (len=${updated.length})`
|
|
1633
1642
|
);
|
|
1634
1643
|
}
|
|
@@ -1838,7 +1847,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1838
1847
|
}
|
|
1839
1848
|
agentContext.indexTokenCountMap = viewTokenMap;
|
|
1840
1849
|
|
|
1841
|
-
|
|
1850
|
+
mlog(
|
|
1842
1851
|
`[Graph:Compaction] ${messages.length}→${viewParts.length} msgs | ` +
|
|
1843
1852
|
`compacted=${compactedMessages.length} window=${recentMessages.length} | ` +
|
|
1844
1853
|
`summary=${summarySource} | budget=${usedTokens}/${recentBudget}` +
|
|
@@ -1862,7 +1871,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1862
1871
|
if (shouldSummarize) {
|
|
1863
1872
|
if (this._summaryInFlight) {
|
|
1864
1873
|
this._pendingMessagesToRefine.push(...compactedMessages);
|
|
1865
|
-
|
|
1874
|
+
mlog(
|
|
1866
1875
|
`[Graph:Compaction] Summary in-flight, queued ${compactedMessages.length} msgs (pending=${this._pendingMessagesToRefine.length})`
|
|
1867
1876
|
);
|
|
1868
1877
|
} else {
|
|
@@ -1915,7 +1924,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1915
1924
|
deduplicateSystemMessages(messagesToUse);
|
|
1916
1925
|
if (removedCount > 0) {
|
|
1917
1926
|
messagesToUse = dedupedMessages;
|
|
1918
|
-
|
|
1927
|
+
mlog(
|
|
1919
1928
|
`[Graph:Dedup] Removed ${removedCount} duplicate system message(s)`
|
|
1920
1929
|
);
|
|
1921
1930
|
}
|
|
@@ -2039,7 +2048,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
2039
2048
|
);
|
|
2040
2049
|
}
|
|
2041
2050
|
|
|
2042
|
-
|
|
2043
2051
|
// Get model info for analytics
|
|
2044
2052
|
const bedrockOpts = agentContext.clientOptions as
|
|
2045
2053
|
| t.BedrockAnthropicClientOptions
|
|
@@ -2141,23 +2149,16 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
2141
2149
|
config
|
|
2142
2150
|
);
|
|
2143
2151
|
} catch (primaryError) {
|
|
2144
|
-
|
|
2145
|
-
const
|
|
2146
|
-
const isInputTooLongError =
|
|
2147
|
-
errorMessage.includes('too long') ||
|
|
2148
|
-
errorMessage.includes('input is too long') ||
|
|
2149
|
-
errorMessage.includes('context length') ||
|
|
2150
|
-
errorMessage.includes('maximum context') ||
|
|
2151
|
-
errorMessage.includes('validationexception') ||
|
|
2152
|
-
errorMessage.includes('prompt is too long');
|
|
2152
|
+
const errorMessage = (primaryError as Error).message;
|
|
2153
|
+
const isInputTooLongError = isLikelyContextOverflowError(errorMessage);
|
|
2153
2154
|
|
|
2154
2155
|
// Log when we detect the error
|
|
2155
2156
|
if (isInputTooLongError) {
|
|
2156
|
-
|
|
2157
|
+
mwarn(
|
|
2157
2158
|
'[Graph] Detected input too long error:',
|
|
2158
2159
|
errorMessage.substring(0, 200)
|
|
2159
2160
|
);
|
|
2160
|
-
|
|
2161
|
+
mwarn('[Graph] Checking emergency pruning conditions:', {
|
|
2161
2162
|
hasPruneMessages: !!agentContext.pruneMessages,
|
|
2162
2163
|
hasTokenCounter: !!agentContext.tokenCounter,
|
|
2163
2164
|
maxContextTokens: agentContext.maxContextTokens,
|
|
@@ -2182,7 +2183,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
2182
2183
|
const reducedMaxTokens = Math.floor(
|
|
2183
2184
|
agentContext.maxContextTokens! * reductionFactor
|
|
2184
2185
|
);
|
|
2185
|
-
|
|
2186
|
+
mwarn(
|
|
2186
2187
|
`[Graph] Input too long. Retrying with ${reductionFactor * 100}% context (${reducedMaxTokens} tokens)...`
|
|
2187
2188
|
);
|
|
2188
2189
|
|
|
@@ -2190,7 +2191,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
2190
2191
|
// This is needed when messages were dynamically added without updating the token map
|
|
2191
2192
|
let tokenMapForPruning = agentContext.indexTokenCountMap;
|
|
2192
2193
|
if (Object.keys(tokenMapForPruning).length < messages.length) {
|
|
2193
|
-
|
|
2194
|
+
mwarn(
|
|
2194
2195
|
'[Graph] Building fresh token count map for emergency pruning...'
|
|
2195
2196
|
);
|
|
2196
2197
|
tokenMapForPruning = {};
|
|
@@ -2215,7 +2216,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
2215
2216
|
|
|
2216
2217
|
// Skip if we can't fit any messages
|
|
2217
2218
|
if (reducedMessages.length === 0) {
|
|
2218
|
-
|
|
2219
|
+
mwarn(
|
|
2219
2220
|
`[Graph] Cannot fit any messages at ${reductionFactor * 100}% reduction, trying next level...`
|
|
2220
2221
|
);
|
|
2221
2222
|
continue;
|
|
@@ -2291,14 +2292,11 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2291
2292
|
`[Graph] ✅ Retry successful at ${reductionFactor * 100}% with ${reducedMessages.length} messages (reduced from ${finalMessages.length})`
|
|
2292
2293
|
);
|
|
2293
2294
|
} catch (retryError) {
|
|
2294
|
-
const retryErrorMsg = (retryError as Error).message
|
|
2295
|
-
const stillTooLong =
|
|
2296
|
-
retryErrorMsg.includes('too long') ||
|
|
2297
|
-
retryErrorMsg.includes('context length') ||
|
|
2298
|
-
retryErrorMsg.includes('validationexception');
|
|
2295
|
+
const retryErrorMsg = (retryError as Error).message;
|
|
2296
|
+
const stillTooLong = isLikelyContextOverflowError(retryErrorMsg);
|
|
2299
2297
|
|
|
2300
2298
|
if (stillTooLong && reductionFactor > 0.1) {
|
|
2301
|
-
|
|
2299
|
+
mwarn(
|
|
2302
2300
|
`[Graph] Still too long at ${reductionFactor * 100}%, trying more aggressive pruning...`
|
|
2303
2301
|
);
|
|
2304
2302
|
} else {
|
|
@@ -2370,6 +2368,27 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2370
2368
|
* handled everything — both paths become no-ops.
|
|
2371
2369
|
*/
|
|
2372
2370
|
const responseMessage = result.messages?.[0];
|
|
2371
|
+
|
|
2372
|
+
// Tool-call name normalization — catches LLM output that names tools
|
|
2373
|
+
// with wrong delimiters (outlook/operations), prefixes
|
|
2374
|
+
// (functions.outlook_operations), case drift, counter suffixes, or
|
|
2375
|
+
// empty names recoverable from the tool_call id. Mutates in place so
|
|
2376
|
+
// the downstream ToolNode dispatch sees the corrected names.
|
|
2377
|
+
if (responseMessage && agentContext.toolMap) {
|
|
2378
|
+
const allowedNames = new Set(Object.keys(agentContext.toolMap));
|
|
2379
|
+
if (allowedNames.size > 0) {
|
|
2380
|
+
const rewrote = normalizeMessageToolCalls(
|
|
2381
|
+
responseMessage,
|
|
2382
|
+
allowedNames
|
|
2383
|
+
);
|
|
2384
|
+
if (rewrote) {
|
|
2385
|
+
mlog(
|
|
2386
|
+
`[Graph] normalized tool_call names on agent "${agentId}" response`
|
|
2387
|
+
);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2373
2392
|
const toolCalls = (responseMessage as AIMessageChunk | undefined)
|
|
2374
2393
|
?.tool_calls;
|
|
2375
2394
|
const hasToolCalls = Array.isArray(toolCalls) && toolCalls.length > 0;
|
|
@@ -2485,12 +2504,22 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2485
2504
|
const messageStop = meta.messageStop as
|
|
2486
2505
|
| Record<string, unknown>
|
|
2487
2506
|
| undefined;
|
|
2488
|
-
|
|
2507
|
+
const nextReason =
|
|
2489
2508
|
(meta.finish_reason as string | undefined) ?? // OpenAI/Azure
|
|
2490
2509
|
(meta.stop_reason as string | undefined) ?? // Anthropic direct API
|
|
2491
2510
|
(meta.stopReason as string | undefined) ?? // Bedrock invoke (non-streaming)
|
|
2492
2511
|
(messageStop?.stopReason as string | undefined) ?? // Bedrock streaming
|
|
2493
2512
|
(meta.finishReason as string | undefined); // VertexAI/Google
|
|
2513
|
+
|
|
2514
|
+
// Sticky on truncation: a single Graph instance is reused across
|
|
2515
|
+
// every scoped-subgraph inner node invocation (see MultiAgentGraph
|
|
2516
|
+
// buildScopedSubgraph). If an earlier inner node hit max_tokens
|
|
2517
|
+
// but a later inner node finished cleanly, the host's continuation layer
|
|
2518
|
+
// would miss the truncation signal unless we preserve it. Keep the
|
|
2519
|
+
// truncation reason pinned so the outer caller can retry.
|
|
2520
|
+
if (!isTruncationReason(this.lastFinishReason)) {
|
|
2521
|
+
this.lastFinishReason = nextReason;
|
|
2522
|
+
}
|
|
2494
2523
|
}
|
|
2495
2524
|
|
|
2496
2525
|
this.cleanupSignalListener();
|
|
@@ -2553,7 +2582,7 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2553
2582
|
'[Graph] Deferred structured output failed after successful tool use:',
|
|
2554
2583
|
structuredError
|
|
2555
2584
|
);
|
|
2556
|
-
|
|
2585
|
+
mwarn(
|
|
2557
2586
|
'[Graph] Falling back to unstructured response from tool-use phase'
|
|
2558
2587
|
);
|
|
2559
2588
|
return result;
|
|
@@ -2578,7 +2607,10 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2578
2607
|
state: t.BaseGraphState,
|
|
2579
2608
|
config?: RunnableConfig
|
|
2580
2609
|
): string => {
|
|
2581
|
-
this.config
|
|
2610
|
+
// First-writer-wins — see note in createCallModel. `this.config` is an
|
|
2611
|
+
// existence flag only; assigning unconditionally would race under
|
|
2612
|
+
// parallel child subgraph.invoke().
|
|
2613
|
+
this.config ??= config;
|
|
2582
2614
|
return toolsCondition(state, toolNode, this.invokedToolIds);
|
|
2583
2615
|
};
|
|
2584
2616
|
|
|
@@ -2623,10 +2655,16 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2623
2655
|
default: () => [],
|
|
2624
2656
|
}),
|
|
2625
2657
|
});
|
|
2658
|
+
// Pass compileOptions (including the HITL checkpointer) to the OUTER
|
|
2659
|
+
// workflow — not just the inner agent subgraph. hasInterrupts() calls
|
|
2660
|
+
// getState() on the outer compiled graph; without a checkpointer here,
|
|
2661
|
+
// getState reports zero tasks and the HITL interrupt/resume loop breaks
|
|
2662
|
+
// out immediately even though interrupt() fired correctly inside the
|
|
2663
|
+
// agent subgraph.
|
|
2626
2664
|
const workflow = new StateGraph(StateAnnotation)
|
|
2627
2665
|
.addNode(this.defaultAgentId, agentNode, { ends: [END] })
|
|
2628
2666
|
.addEdge(START, this.defaultAgentId)
|
|
2629
|
-
.compile();
|
|
2667
|
+
.compile(this.compileOptions as unknown as never);
|
|
2630
2668
|
|
|
2631
2669
|
return workflow;
|
|
2632
2670
|
}
|
|
@@ -2709,7 +2747,7 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2709
2747
|
}
|
|
2710
2748
|
} catch (_e) {
|
|
2711
2749
|
/** If we can't get agent context, that's okay - agentId remains undefined */
|
|
2712
|
-
|
|
2750
|
+
mlog(
|
|
2713
2751
|
`[dispatchRunStep] Could not resolve agentId from metadata.langgraph_node="${(metadata as Record<string, unknown>).langgraph_node}": ${(_e as Error).message}`
|
|
2714
2752
|
);
|
|
2715
2753
|
}
|
|
@@ -2717,11 +2755,11 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2717
2755
|
|
|
2718
2756
|
this.contentData.push(runStep);
|
|
2719
2757
|
this.contentIndexMap.set(stepId, runStep.index);
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
);
|
|
2758
|
+
// Pass undefined so safeDispatchCustomEvent resolves the runnable config
|
|
2759
|
+
// from LangChain's AsyncLocalStorage. Using the shared `this.config` would
|
|
2760
|
+
// race across concurrent child subgraph.invoke calls under parallel
|
|
2761
|
+
// multi-agent handoffs and tag events with the wrong child's spawnKey.
|
|
2762
|
+
await safeDispatchCustomEvent(GraphEvents.ON_RUN_STEP, runStep);
|
|
2725
2763
|
return stepId;
|
|
2726
2764
|
}
|
|
2727
2765
|
|
|
@@ -2862,7 +2900,7 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2862
2900
|
}
|
|
2863
2901
|
|
|
2864
2902
|
if (!data.id) {
|
|
2865
|
-
|
|
2903
|
+
mwarn('No Tool ID provided for Tool Error');
|
|
2866
2904
|
return;
|
|
2867
2905
|
}
|
|
2868
2906
|
|
|
@@ -2927,11 +2965,10 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2927
2965
|
id,
|
|
2928
2966
|
delta,
|
|
2929
2967
|
};
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
);
|
|
2968
|
+
// See dispatchRunStep note: do not pass `this.config`. The implicit
|
|
2969
|
+
// AsyncLocalStorage config is the correct per-async-branch source under
|
|
2970
|
+
// parallel handoffs.
|
|
2971
|
+
await safeDispatchCustomEvent(GraphEvents.ON_RUN_STEP_DELTA, runStepDelta);
|
|
2935
2972
|
}
|
|
2936
2973
|
|
|
2937
2974
|
async dispatchMessageDelta(id: string, delta: t.MessageDelta): Promise<void> {
|
|
@@ -2942,11 +2979,8 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2942
2979
|
id,
|
|
2943
2980
|
delta,
|
|
2944
2981
|
};
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
messageDelta,
|
|
2948
|
-
this.config
|
|
2949
|
-
);
|
|
2982
|
+
// See dispatchRunStep note.
|
|
2983
|
+
await safeDispatchCustomEvent(GraphEvents.ON_MESSAGE_DELTA, messageDelta);
|
|
2950
2984
|
}
|
|
2951
2985
|
|
|
2952
2986
|
dispatchReasoningDelta = async (
|
|
@@ -2960,10 +2994,10 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
2960
2994
|
id: stepId,
|
|
2961
2995
|
delta,
|
|
2962
2996
|
};
|
|
2997
|
+
// See dispatchRunStep note.
|
|
2963
2998
|
await safeDispatchCustomEvent(
|
|
2964
2999
|
GraphEvents.ON_REASONING_DELTA,
|
|
2965
|
-
reasoningDelta
|
|
2966
|
-
this.config
|
|
3000
|
+
reasoningDelta
|
|
2967
3001
|
);
|
|
2968
3002
|
};
|
|
2969
3003
|
}
|