@illuma-ai/agents 1.1.28 → 1.3.0
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 +84 -33
- 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 +113 -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/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 +84 -33
- 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 +20 -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/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/events.d.ts +21 -0
- package/dist/types/utils/finishReasons.d.ts +32 -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 +90 -47
- 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__/finishReasons.test.ts +55 -0
- package/src/utils/__tests__/toolCallNormalization.test.ts +181 -0
- package/src/utils/childAgentContext.ts +259 -0
- package/src/utils/events.ts +37 -7
- package/src/utils/finishReasons.ts +40 -0
- package/src/utils/llm.ts +0 -1
- package/src/utils/logging.ts +45 -8
- package/src/utils/toolCallNormalization.ts +271 -0
package/src/utils/logging.ts
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import util from 'util';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Multi-agent debug gate. Controls chatty graph/handoff/transfer/sequence
|
|
7
|
+
* trace logs emitted from `MultiAgentGraph` and `Graph`. Off by default so the
|
|
8
|
+
* package stays quiet when embedded in host apps. Flip to
|
|
9
|
+
* `DEBUG_MULTIAGENT=true` in the host env to re-enable for testing.
|
|
10
|
+
*
|
|
11
|
+
* Exported as functions so runtime toggling via env reload works; the check
|
|
12
|
+
* is cheap (string equality) and only runs when a log site fires.
|
|
13
|
+
*/
|
|
14
|
+
function isMultiAgentDebugEnabled(): boolean {
|
|
15
|
+
return process.env.DEBUG_MULTIAGENT === 'true';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
export const mlog = (...args: any[]): void => {
|
|
20
|
+
if (isMultiAgentDebugEnabled()) {
|
|
21
|
+
console.debug(...args);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
export const mwarn = (...args: any[]): void => {
|
|
27
|
+
if (isMultiAgentDebugEnabled()) {
|
|
28
|
+
console.warn(...args);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
5
32
|
export function setupLogging(logFileName: string): void {
|
|
6
33
|
const logFile = fs.createWriteStream(logFileName, { flags: 'a' });
|
|
7
34
|
|
|
@@ -10,39 +37,49 @@ export function setupLogging(logFileName: string): void {
|
|
|
10
37
|
const originalStdoutWrite = process.stdout.write;
|
|
11
38
|
const originalStderrWrite = process.stderr.write;
|
|
12
39
|
|
|
13
|
-
console.log = function(...args): void {
|
|
40
|
+
console.log = function (...args): void {
|
|
14
41
|
logFile.write(util.format.apply(null, args) + ' ');
|
|
15
42
|
originalConsoleLog.apply(console, args);
|
|
16
43
|
};
|
|
17
44
|
|
|
18
|
-
console.error = function(...args): void {
|
|
45
|
+
console.error = function (...args): void {
|
|
19
46
|
logFile.write(util.format.apply(null, args) + ' ');
|
|
20
47
|
originalConsoleError.apply(console, args);
|
|
21
48
|
};
|
|
22
49
|
|
|
23
|
-
process.stdout.write = function(
|
|
50
|
+
process.stdout.write = function (
|
|
24
51
|
buffer: Uint8Array | string,
|
|
25
52
|
cb?: ((err?: Error) => void) | string,
|
|
26
|
-
fd?: (
|
|
53
|
+
fd?: (err?: Error) => void
|
|
27
54
|
): boolean {
|
|
28
55
|
if (typeof buffer === 'string') {
|
|
29
56
|
logFile.write(buffer);
|
|
30
57
|
} else {
|
|
31
58
|
logFile.write(buffer.toString());
|
|
32
59
|
}
|
|
33
|
-
return originalStdoutWrite.call(
|
|
60
|
+
return originalStdoutWrite.call(
|
|
61
|
+
process.stdout,
|
|
62
|
+
buffer,
|
|
63
|
+
cb as BufferEncoding | undefined,
|
|
64
|
+
fd
|
|
65
|
+
);
|
|
34
66
|
};
|
|
35
67
|
|
|
36
|
-
process.stderr.write = function(
|
|
68
|
+
process.stderr.write = function (
|
|
37
69
|
buffer: Uint8Array | string,
|
|
38
70
|
cb?: ((err?: Error) => void) | string,
|
|
39
|
-
fd?: (
|
|
71
|
+
fd?: (err?: Error) => void
|
|
40
72
|
): boolean {
|
|
41
73
|
if (typeof buffer === 'string') {
|
|
42
74
|
logFile.write(buffer);
|
|
43
75
|
} else {
|
|
44
76
|
logFile.write(buffer.toString());
|
|
45
77
|
}
|
|
46
|
-
return originalStderrWrite.call(
|
|
78
|
+
return originalStderrWrite.call(
|
|
79
|
+
process.stderr,
|
|
80
|
+
buffer,
|
|
81
|
+
cb as BufferEncoding | undefined,
|
|
82
|
+
fd
|
|
83
|
+
);
|
|
47
84
|
};
|
|
48
85
|
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-call name normalization for malformed LLM tool_use outputs.
|
|
3
|
+
*
|
|
4
|
+
* LLMs — especially smaller / faster models — frequently emit tool_use
|
|
5
|
+
* blocks with names that don't exactly match the registered tool name:
|
|
6
|
+
*
|
|
7
|
+
* - Wrong delimiter: `outlook/operations` vs `outlook_operations`
|
|
8
|
+
* - Function-prefix style: `functions.outlook_operations`
|
|
9
|
+
* - Case drift: `Outlook_Operations`
|
|
10
|
+
* - Counter suffix: `outlook_operations_1`
|
|
11
|
+
* - Missing name, only id: `{name: "", id: "tool_outlook_operations_42"}`
|
|
12
|
+
*
|
|
13
|
+
* Before this normalization layer, any of the above caused the LangGraph
|
|
14
|
+
* ToolNode to throw "Tool X not found" and the whole turn to fail. With
|
|
15
|
+
* normalization, we resolve to the correct registered name case-insensitively
|
|
16
|
+
* and transparently fix the tool_call in place.
|
|
17
|
+
*
|
|
18
|
+
* Resolution order:
|
|
19
|
+
* 1. Exact match
|
|
20
|
+
* 2. Normalized delimiter + exact
|
|
21
|
+
* 3. Case-insensitive match
|
|
22
|
+
* 4. Structured candidates (strip prefixes, take suffix segments)
|
|
23
|
+
* 5. Infer from tool_call id (strip trailing digits, function/tool prefix)
|
|
24
|
+
*
|
|
25
|
+
* Any unresolvable tool_call is left as-is — the downstream ToolNode will
|
|
26
|
+
* fail with its usual error and the auto-recovery path kicks in.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
function lowercaseOrEmpty(value: string): string {
|
|
30
|
+
return typeof value === 'string' ? value.toLowerCase() : '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Collapse common delimiters (space, dot, slash, dash) to `_` and fold case. */
|
|
34
|
+
function normalizeDelimiters(name: string): string {
|
|
35
|
+
return name
|
|
36
|
+
.trim()
|
|
37
|
+
.replace(/[\s./-]+/g, '_')
|
|
38
|
+
.replace(/_{2,}/g, '_')
|
|
39
|
+
.replace(/^_+|_+$/g, '');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resolveCaseInsensitive(
|
|
43
|
+
rawName: string,
|
|
44
|
+
allowed: Set<string>
|
|
45
|
+
): string | null {
|
|
46
|
+
if (allowed.size === 0) return null;
|
|
47
|
+
const folded = lowercaseOrEmpty(rawName);
|
|
48
|
+
let match: string | null = null;
|
|
49
|
+
for (const name of allowed) {
|
|
50
|
+
if (lowercaseOrEmpty(name) !== folded) continue;
|
|
51
|
+
// Ambiguous → fail closed
|
|
52
|
+
if (match && match !== name) return null;
|
|
53
|
+
match = name;
|
|
54
|
+
}
|
|
55
|
+
return match;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolveExact(rawName: string, allowed: Set<string>): string | null {
|
|
59
|
+
if (allowed.size === 0) return null;
|
|
60
|
+
if (allowed.has(rawName)) return rawName;
|
|
61
|
+
const normalized = normalizeDelimiters(rawName);
|
|
62
|
+
if (allowed.has(normalized)) return normalized;
|
|
63
|
+
return (
|
|
64
|
+
resolveCaseInsensitive(rawName, allowed) ??
|
|
65
|
+
resolveCaseInsensitive(normalized, allowed)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function buildStructuredCandidates(rawName: string): string[] {
|
|
70
|
+
const trimmed = rawName.trim();
|
|
71
|
+
if (!trimmed) return [];
|
|
72
|
+
const seen = new Set<string>();
|
|
73
|
+
const out: string[] = [];
|
|
74
|
+
const add = (value: string): void => {
|
|
75
|
+
const c = value.trim();
|
|
76
|
+
if (!c || seen.has(c)) return;
|
|
77
|
+
seen.add(c);
|
|
78
|
+
out.push(c);
|
|
79
|
+
};
|
|
80
|
+
add(trimmed);
|
|
81
|
+
add(normalizeDelimiters(trimmed));
|
|
82
|
+
// Also try dot-normalized, then split
|
|
83
|
+
const dotForm = trimmed.replace(/\//g, '.');
|
|
84
|
+
add(dotForm);
|
|
85
|
+
add(normalizeDelimiters(dotForm));
|
|
86
|
+
const segments = dotForm
|
|
87
|
+
.split('.')
|
|
88
|
+
.map((s) => s.trim())
|
|
89
|
+
.filter(Boolean);
|
|
90
|
+
if (segments.length > 1) {
|
|
91
|
+
for (let i = 1; i < segments.length; i++) {
|
|
92
|
+
const suffix = segments.slice(i).join('.');
|
|
93
|
+
add(suffix);
|
|
94
|
+
add(normalizeDelimiters(suffix));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolveStructured(
|
|
101
|
+
rawName: string,
|
|
102
|
+
allowed: Set<string>
|
|
103
|
+
): string | null {
|
|
104
|
+
if (allowed.size === 0) return null;
|
|
105
|
+
const candidates = buildStructuredCandidates(rawName);
|
|
106
|
+
for (const c of candidates) {
|
|
107
|
+
if (allowed.has(c)) return c;
|
|
108
|
+
}
|
|
109
|
+
for (const c of candidates) {
|
|
110
|
+
const ci = resolveCaseInsensitive(c, allowed);
|
|
111
|
+
if (ci) return ci;
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function inferFromToolCallId(
|
|
117
|
+
rawId: string | undefined,
|
|
118
|
+
allowed: Set<string>
|
|
119
|
+
): string | null {
|
|
120
|
+
if (!rawId || allowed.size === 0) return null;
|
|
121
|
+
const id = rawId.trim();
|
|
122
|
+
if (!id) return null;
|
|
123
|
+
|
|
124
|
+
const tokens = new Set<string>();
|
|
125
|
+
const addTokens = (value: string): void => {
|
|
126
|
+
const t = value.trim();
|
|
127
|
+
if (!t) return;
|
|
128
|
+
tokens.add(t);
|
|
129
|
+
tokens.add(t.replace(/[:._/-]\d+$/, ''));
|
|
130
|
+
tokens.add(t.replace(/\d+$/, ''));
|
|
131
|
+
const dotForm = t.replace(/\//g, '.');
|
|
132
|
+
tokens.add(dotForm);
|
|
133
|
+
tokens.add(dotForm.replace(/[:._-]\d+$/, ''));
|
|
134
|
+
tokens.add(dotForm.replace(/\d+$/, ''));
|
|
135
|
+
for (const prefix of [/^functions?[._-]?/i, /^tools?[._-]?/i]) {
|
|
136
|
+
const stripped = dotForm.replace(prefix, '');
|
|
137
|
+
if (stripped !== dotForm) {
|
|
138
|
+
tokens.add(stripped);
|
|
139
|
+
tokens.add(stripped.replace(/[:._-]\d+$/, ''));
|
|
140
|
+
tokens.add(stripped.replace(/\d+$/, ''));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const preColon = id.split(':')[0] ?? id;
|
|
146
|
+
addTokens(id);
|
|
147
|
+
addTokens(preColon);
|
|
148
|
+
|
|
149
|
+
let singleMatch: string | null = null;
|
|
150
|
+
for (const token of tokens) {
|
|
151
|
+
const matched = resolveStructured(token, allowed);
|
|
152
|
+
if (!matched) continue;
|
|
153
|
+
// Ambiguous → fail closed
|
|
154
|
+
if (singleMatch && singleMatch !== matched) return null;
|
|
155
|
+
singleMatch = matched;
|
|
156
|
+
}
|
|
157
|
+
if (singleMatch) return singleMatch;
|
|
158
|
+
|
|
159
|
+
// Substring fallback: ids frequently look like `call_<toolname>_<n>` or
|
|
160
|
+
// `toolu_01...<toolname>...`. Scan for any allowed name embedded in the
|
|
161
|
+
// delimiter-normalized id (case-insensitive) and fail closed if more than
|
|
162
|
+
// one distinct allowed name matches.
|
|
163
|
+
const haystack = lowercaseOrEmpty(normalizeDelimiters(id));
|
|
164
|
+
let substrMatch: string | null = null;
|
|
165
|
+
for (const allowedName of allowed) {
|
|
166
|
+
const needle = lowercaseOrEmpty(allowedName);
|
|
167
|
+
if (!needle) continue;
|
|
168
|
+
if (!haystack.includes(needle)) continue;
|
|
169
|
+
if (substrMatch && substrMatch !== allowedName) return null;
|
|
170
|
+
substrMatch = allowedName;
|
|
171
|
+
}
|
|
172
|
+
return substrMatch;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Produce the best allowed tool name for a raw LLM tool_use name.
|
|
177
|
+
* Returns the original name unchanged if no resolution is possible —
|
|
178
|
+
* the downstream executor will then fail with its normal error path.
|
|
179
|
+
*/
|
|
180
|
+
export function normalizeToolCallName(
|
|
181
|
+
rawName: string,
|
|
182
|
+
allowedToolNames: Set<string>,
|
|
183
|
+
rawToolCallId?: string
|
|
184
|
+
): string {
|
|
185
|
+
const trimmed = rawName.trim() ?? '';
|
|
186
|
+
if (!trimmed) {
|
|
187
|
+
return (
|
|
188
|
+
inferFromToolCallId(rawToolCallId, allowedToolNames) ?? rawName ?? ''
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (allowedToolNames.size === 0) return trimmed;
|
|
192
|
+
|
|
193
|
+
const exact = resolveExact(trimmed, allowedToolNames);
|
|
194
|
+
if (exact) return exact;
|
|
195
|
+
|
|
196
|
+
const structured = resolveStructured(trimmed, allowedToolNames);
|
|
197
|
+
if (structured) return structured;
|
|
198
|
+
|
|
199
|
+
// Last-resort: try the raw id (if supplied). Substring matching lives
|
|
200
|
+
// inside inferFromToolCallId — only safe to invoke when the caller
|
|
201
|
+
// passed a real provider id, not when the raw name was used above.
|
|
202
|
+
const inferred = inferFromToolCallId(rawToolCallId, allowedToolNames);
|
|
203
|
+
if (inferred) return inferred;
|
|
204
|
+
|
|
205
|
+
return trimmed;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* In-place normalization of all tool_calls on an AIMessage-like object.
|
|
210
|
+
*
|
|
211
|
+
* Applies `normalizeToolCallName` to each tool_call's `name` field using the
|
|
212
|
+
* provided allowed-tool set (derived from the agent's toolMap keys). Also
|
|
213
|
+
* rewrites the `name` field inside any content blocks of type `tool_use` so
|
|
214
|
+
* that downstream providers see the corrected name.
|
|
215
|
+
*
|
|
216
|
+
* Returns `true` if any name was rewritten.
|
|
217
|
+
*/
|
|
218
|
+
export function normalizeMessageToolCalls(
|
|
219
|
+
message: unknown,
|
|
220
|
+
allowedToolNames: Set<string>
|
|
221
|
+
): boolean {
|
|
222
|
+
if (!message || typeof message !== 'object') return false;
|
|
223
|
+
let changed = false;
|
|
224
|
+
|
|
225
|
+
// LangChain AIMessage.tool_calls (normalized form)
|
|
226
|
+
const msg = message as {
|
|
227
|
+
tool_calls?: Array<{ name?: string; id?: string }>;
|
|
228
|
+
content?: unknown;
|
|
229
|
+
};
|
|
230
|
+
if (Array.isArray(msg.tool_calls)) {
|
|
231
|
+
for (const tc of msg.tool_calls) {
|
|
232
|
+
if (!tc || typeof tc !== 'object') continue;
|
|
233
|
+
const rawName = typeof tc.name === 'string' ? tc.name : '';
|
|
234
|
+
const rawId = typeof tc.id === 'string' ? tc.id : undefined;
|
|
235
|
+
const normalized = normalizeToolCallName(
|
|
236
|
+
rawName,
|
|
237
|
+
allowedToolNames,
|
|
238
|
+
rawId
|
|
239
|
+
);
|
|
240
|
+
if (normalized && normalized !== rawName) {
|
|
241
|
+
tc.name = normalized;
|
|
242
|
+
changed = true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Anthropic-style content blocks with type: 'tool_use'
|
|
248
|
+
if (Array.isArray(msg.content)) {
|
|
249
|
+
for (const block of msg.content as Array<{
|
|
250
|
+
type?: unknown;
|
|
251
|
+
name?: unknown;
|
|
252
|
+
id?: unknown;
|
|
253
|
+
}>) {
|
|
254
|
+
if (!block || typeof block !== 'object') continue;
|
|
255
|
+
if (block.type !== 'tool_use') continue;
|
|
256
|
+
const rawName = typeof block.name === 'string' ? block.name : '';
|
|
257
|
+
const rawId = typeof block.id === 'string' ? block.id : undefined;
|
|
258
|
+
const normalized = normalizeToolCallName(
|
|
259
|
+
rawName,
|
|
260
|
+
allowedToolNames,
|
|
261
|
+
rawId
|
|
262
|
+
);
|
|
263
|
+
if (normalized && normalized !== rawName) {
|
|
264
|
+
block.name = normalized;
|
|
265
|
+
changed = true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return changed;
|
|
271
|
+
}
|