@tanstack/ai 0.42.0 → 0.43.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/README.md +15 -1
- package/dist/esm/activities/chat/adapter.js +23 -16
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/agent-loop-strategies.d.ts +5 -36
- package/dist/esm/activities/chat/agent-loop-strategies.js +75 -21
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
- package/dist/esm/activities/chat/cancel.d.ts +40 -0
- package/dist/esm/activities/chat/cancel.js +54 -0
- package/dist/esm/activities/chat/cancel.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +28 -21
- package/dist/esm/activities/chat/index.js +2100 -1813
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
- package/dist/esm/activities/chat/mcp/manager.js +90 -77
- package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
- package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
- package/dist/esm/activities/chat/messages.js +397 -346
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.js +17 -15
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
- package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
- package/dist/esm/activities/chat/middleware/compose.js +623 -531
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.js +12 -5
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
- package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
- package/dist/esm/activities/chat/middleware/locks.js +71 -0
- package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
- package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
- package/dist/esm/activities/chat/middleware/run-store.js +176 -0
- package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
- package/dist/esm/activities/chat/middleware/validate.js +23 -28
- package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
- package/dist/esm/activities/chat/stream/json-parser.js +39 -25
- package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
- package/dist/esm/activities/chat/stream/processor.js +1341 -1542
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/stream/strategies.js +69 -53
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
- package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
- package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
- package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
- package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
- package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/error-payload.js +85 -47
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/activities/generateAudio/adapter.js +22 -15
- package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.d.ts +4 -0
- package/dist/esm/activities/generateAudio/index.js +141 -105
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.js +22 -15
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +4 -0
- package/dist/esm/activities/generateImage/index.js +155 -111
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js +22 -15
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
- package/dist/esm/activities/generateSpeech/index.js +159 -110
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js +22 -15
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
- package/dist/esm/activities/generateTranscription/index.js +159 -100
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.js +36 -29
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +143 -19
- package/dist/esm/activities/generateVideo/index.js +456 -279
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.js +60 -48
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/index.js +8 -34
- package/dist/esm/activities/middleware/index.d.ts +1 -1
- package/dist/esm/activities/middleware/run.d.ts +10 -0
- package/dist/esm/activities/middleware/run.js +53 -29
- package/dist/esm/activities/middleware/run.js.map +1 -1
- package/dist/esm/activities/middleware/types.d.ts +44 -6
- package/dist/esm/activities/stream-generation-result.d.ts +4 -1
- package/dist/esm/activities/stream-generation-result.js +79 -44
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/adapter.js +22 -15
- package/dist/esm/activities/summarize/adapter.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/activities/summarize/index.d.ts +27 -0
- package/dist/esm/activities/summarize/index.js +268 -102
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -1
- package/dist/esm/adapter-internals.js +4 -11
- package/dist/esm/client.d.ts +25 -3
- package/dist/esm/client.js +131 -64
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/custom-events.d.ts +76 -0
- package/dist/esm/custom-events.js +37 -0
- package/dist/esm/custom-events.js.map +1 -0
- package/dist/esm/delivery-detach.d.ts +50 -0
- package/dist/esm/delivery-detach.js +71 -0
- package/dist/esm/delivery-detach.js.map +1 -0
- package/dist/esm/delivery-disconnect.d.ts +62 -0
- package/dist/esm/delivery-disconnect.js +81 -0
- package/dist/esm/delivery-disconnect.js.map +1 -0
- package/dist/esm/extend-adapter.js +19 -17
- package/dist/esm/extend-adapter.js.map +1 -1
- package/dist/esm/index.d.ts +24 -6
- package/dist/esm/index.js +30 -98
- package/dist/esm/interrupt-resume.d.ts +71 -0
- package/dist/esm/interrupt-resume.js +438 -0
- package/dist/esm/interrupt-resume.js.map +1 -0
- package/dist/esm/interrupt-serialization.d.ts +12 -0
- package/dist/esm/interrupt-serialization.js +178 -0
- package/dist/esm/interrupt-serialization.js.map +1 -0
- package/dist/esm/interrupts.d.ts +84 -0
- package/dist/esm/interrupts.js +31 -0
- package/dist/esm/interrupts.js.map +1 -0
- package/dist/esm/locks.d.ts +10 -0
- package/dist/esm/locks.js +2 -0
- package/dist/esm/logger/console-logger.js +101 -78
- package/dist/esm/logger/console-logger.js.map +1 -1
- package/dist/esm/logger/internal-logger.js +104 -89
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +54 -49
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/logger/types.d.ts +1 -1
- package/dist/esm/middlewares/content-guard.js +142 -148
- package/dist/esm/middlewares/content-guard.js.map +1 -1
- package/dist/esm/middlewares/index.js +2 -6
- package/dist/esm/middlewares/otel.d.ts +3 -1
- package/dist/esm/middlewares/otel.js +599 -732
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/middlewares/usage-attributes.js +47 -40
- package/dist/esm/middlewares/usage-attributes.js.map +1 -1
- package/dist/esm/realtime/event-emitter.js +24 -25
- package/dist/esm/realtime/event-emitter.js.map +1 -1
- package/dist/esm/realtime/index.d.ts +5 -9
- package/dist/esm/realtime/index.js +29 -6
- package/dist/esm/realtime/index.js.map +1 -1
- package/dist/esm/scope.d.ts +47 -0
- package/dist/esm/stream-durability.d.ts +171 -0
- package/dist/esm/stream-durability.js +295 -0
- package/dist/esm/stream-durability.js.map +1 -0
- package/dist/esm/stream-to-response.d.ts +178 -13
- package/dist/esm/stream-to-response.js +663 -115
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.js +30 -16
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/system-prompts.js +27 -21
- package/dist/esm/system-prompts.js.map +1 -1
- package/dist/esm/tool-registry.js +72 -45
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/tools/provider-tool.js +14 -5
- package/dist/esm/tools/provider-tool.js.map +1 -1
- package/dist/esm/types.d.ts +321 -42
- package/dist/esm/types.js +2 -0
- package/dist/esm/utilities/ag-ui-wire.js +79 -93
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +26 -4
- package/dist/esm/utilities/chat-params.js +218 -92
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/errors.js +28 -18
- package/dist/esm/utilities/errors.js.map +1 -1
- package/dist/esm/utilities/media-prompt.js +46 -41
- package/dist/esm/utilities/media-prompt.js.map +1 -1
- package/dist/esm/utilities/numbers.js +13 -10
- package/dist/esm/utilities/numbers.js.map +1 -1
- package/dist/esm/utilities/provider-executed.js +20 -11
- package/dist/esm/utilities/provider-executed.js.map +1 -1
- package/dist/esm/utilities/sampling-keys.js +31 -19
- package/dist/esm/utilities/sampling-keys.js.map +1 -1
- package/dist/esm/utilities/tool-result.js +42 -30
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/dist/esm/utilities/usage.js +27 -9
- package/dist/esm/utilities/usage.js.map +1 -1
- package/dist/esm/utils.js +26 -18
- package/dist/esm/utils.js.map +1 -1
- package/package.json +10 -6
- package/skills/ai-core/SKILL.md +69 -18
- package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
- package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
- package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
- package/skills/ai-core/chat-experience/SKILL.md +98 -11
- package/skills/ai-core/client-persistence/SKILL.md +277 -0
- package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
- package/skills/ai-core/debug-logging/SKILL.md +1 -1
- package/skills/ai-core/locks/SKILL.md +143 -0
- package/skills/ai-core/media-generation/SKILL.md +144 -12
- package/skills/ai-core/middleware/SKILL.md +258 -33
- package/skills/ai-core/structured-outputs/SKILL.md +1 -1
- package/skills/ai-core/tool-calling/SKILL.md +54 -61
- package/src/activities/chat/agent-loop-strategies.ts +5 -39
- package/src/activities/chat/cancel.ts +81 -0
- package/src/activities/chat/index.ts +1091 -200
- package/src/activities/chat/mcp/manager.ts +4 -4
- package/src/activities/chat/mcp/types.ts +2 -2
- package/src/activities/chat/messages.ts +5 -3
- package/src/activities/chat/middleware/builder.ts +1 -1
- package/src/activities/chat/middleware/compose.ts +186 -9
- package/src/activities/chat/middleware/index.ts +26 -0
- package/src/activities/chat/middleware/locks.ts +102 -0
- package/src/activities/chat/middleware/pending-turn.ts +47 -0
- package/src/activities/chat/middleware/run-disconnect.ts +62 -0
- package/src/activities/chat/middleware/run-store.ts +412 -0
- package/src/activities/chat/middleware/types.ts +62 -1
- package/src/activities/chat/stream/processor.ts +189 -5
- package/src/activities/chat/tools/approval-schema.ts +205 -0
- package/src/activities/chat/tools/tool-calls.ts +106 -13
- package/src/activities/chat/tools/tool-definition.ts +210 -39
- package/src/activities/generateAudio/index.ts +20 -3
- package/src/activities/generateImage/index.ts +20 -3
- package/src/activities/generateSpeech/index.ts +25 -3
- package/src/activities/generateTranscription/index.ts +26 -3
- package/src/activities/generateVideo/index.ts +345 -82
- package/src/activities/middleware/index.ts +2 -0
- package/src/activities/middleware/run.ts +31 -0
- package/src/activities/middleware/types.ts +49 -5
- package/src/activities/stream-generation-result.ts +30 -2
- package/src/activities/summarize/chat-stream-summarize.ts +5 -0
- package/src/activities/summarize/index.ts +200 -10
- package/src/adapter-internals.ts +10 -1
- package/src/client.ts +244 -0
- package/src/custom-events.ts +107 -0
- package/src/delivery-detach.ts +72 -0
- package/src/delivery-disconnect.ts +84 -0
- package/src/index.ts +138 -1
- package/src/interrupt-resume.ts +824 -0
- package/src/interrupt-serialization.ts +183 -0
- package/src/interrupts.ts +146 -0
- package/src/locks.ts +17 -0
- package/src/logger/types.ts +1 -1
- package/src/middlewares/otel.ts +23 -5
- package/src/realtime/index.ts +5 -9
- package/src/scope.ts +47 -0
- package/src/stream-durability.ts +598 -0
- package/src/stream-to-response.ts +1051 -95
- package/src/strip-to-spec-middleware.ts +3 -3
- package/src/types.ts +405 -45
- package/src/utilities/chat-params.ts +245 -55
- package/dist/esm/activities/index.js.map +0 -1
- package/dist/esm/adapter-internals.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/middlewares/index.js.map +0 -1
|
@@ -1,548 +1,539 @@
|
|
|
1
|
-
import { normalizeToolResult } from "../../../utilities/tool-result.js";
|
|
2
1
|
import { isStandardSchema, parseWithStandardSchema } from "./schema-converter.js";
|
|
2
|
+
import { normalizeToolResult } from "../../../utilities/tool-result.js";
|
|
3
|
+
//#region src/activities/chat/tools/tool-calls.ts
|
|
3
4
|
function safeJsonParse(value) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(value);
|
|
7
|
+
} catch {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
9
10
|
}
|
|
10
11
|
function readMcpAppMeta(tool) {
|
|
11
|
-
|
|
12
|
-
return meta;
|
|
12
|
+
return tool.metadata?.mcp;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Eagerly read a tool's linked `ui://` resource (MCP Apps) and emit a
|
|
16
|
+
* `ui-resource` CUSTOM event so the client can render the widget. The model
|
|
17
|
+
* still receives the normal text tool-result; the widget rides alongside and
|
|
18
|
+
* never enters model input.
|
|
19
|
+
*
|
|
20
|
+
* Fail-soft: any read error logs a warning and emits nothing — it never throws,
|
|
21
|
+
* so the normal tool-result still flows and a broken widget cannot break the run.
|
|
22
|
+
*/
|
|
14
23
|
async function emitUiResourceIfLinked(tool, context) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
toolName: mcp.serverToolName ?? tool.name,
|
|
41
|
-
meta: void 0
|
|
42
|
-
});
|
|
24
|
+
const mcp = readMcpAppMeta(tool);
|
|
25
|
+
const uiUri = mcp?.uiResourceUri;
|
|
26
|
+
if (!uiUri || !mcp.readResource) return;
|
|
27
|
+
let matched;
|
|
28
|
+
try {
|
|
29
|
+
matched = (await mcp.readResource(uiUri)).contents.find((c) => c.uri === uiUri);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.warn(`[mcp-apps] failed to read ui resource ${uiUri}:`, err);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (!matched) {
|
|
35
|
+
console.warn(`[mcp-apps] ui resource ${uiUri} returned no content matching that uri; not emitting`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
context.emitCustomEvent("ui-resource", {
|
|
39
|
+
resource: {
|
|
40
|
+
uri: matched.uri,
|
|
41
|
+
mimeType: matched.mimeType ?? "text/html",
|
|
42
|
+
text: matched.text,
|
|
43
|
+
blob: matched.blob
|
|
44
|
+
},
|
|
45
|
+
serverId: mcp.serverId,
|
|
46
|
+
toolName: mcp.serverToolName ?? tool.name,
|
|
47
|
+
meta: void 0
|
|
48
|
+
});
|
|
43
49
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Error thrown when middleware decides to abort the chat run during tool execution.
|
|
52
|
+
*/
|
|
53
|
+
var MiddlewareAbortError = class extends Error {
|
|
54
|
+
constructor(reason) {
|
|
55
|
+
super(reason);
|
|
56
|
+
this.name = "MiddlewareAbortError";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.
|
|
61
|
+
*
|
|
62
|
+
* Responsibilities:
|
|
63
|
+
* - Accumulates streaming tool call events (ID, name, arguments)
|
|
64
|
+
* - Validates tool calls (filters out incomplete ones)
|
|
65
|
+
* - Executes tool `execute` functions with parsed arguments
|
|
66
|
+
* - Emits `TOOL_CALL_END` events for client visibility
|
|
67
|
+
* - Returns tool result messages for conversation history
|
|
68
|
+
*
|
|
69
|
+
* This class is used internally by the AI.chat() method to handle the automatic
|
|
70
|
+
* tool execution loop. It can also be used independently for custom tool execution logic.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const manager = new ToolCallManager(tools);
|
|
75
|
+
*
|
|
76
|
+
* // During streaming, accumulate tool calls
|
|
77
|
+
* for await (const chunk of stream) {
|
|
78
|
+
* if (chunk.type === 'TOOL_CALL_START') {
|
|
79
|
+
* manager.addToolCallStartEvent(chunk);
|
|
80
|
+
* } else if (chunk.type === 'TOOL_CALL_ARGS') {
|
|
81
|
+
* manager.addToolCallArgsEvent(chunk);
|
|
82
|
+
* }
|
|
83
|
+
* }
|
|
84
|
+
*
|
|
85
|
+
* // After stream completes, execute tools
|
|
86
|
+
* if (manager.hasToolCalls()) {
|
|
87
|
+
* const toolResults = yield* manager.executeTools(finishEvent);
|
|
88
|
+
* messages = [...messages, ...toolResults];
|
|
89
|
+
* manager.clear();
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
var ToolCallManager = class {
|
|
94
|
+
toolCallsMap = /* @__PURE__ */ new Map();
|
|
95
|
+
tools;
|
|
96
|
+
constructor(tools) {
|
|
97
|
+
this.tools = tools;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)
|
|
101
|
+
*/
|
|
102
|
+
addToolCallStartEvent(event) {
|
|
103
|
+
const index = event.index ?? this.toolCallsMap.size;
|
|
104
|
+
const runtimeEvent = event;
|
|
105
|
+
const name = runtimeEvent.toolCallName ?? runtimeEvent.toolName;
|
|
106
|
+
this.toolCallsMap.set(index, {
|
|
107
|
+
id: event.toolCallId,
|
|
108
|
+
type: "function",
|
|
109
|
+
function: {
|
|
110
|
+
name,
|
|
111
|
+
arguments: ""
|
|
112
|
+
},
|
|
113
|
+
...event.metadata !== void 0 && { metadata: event.metadata }
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)
|
|
118
|
+
*/
|
|
119
|
+
addToolCallArgsEvent(event) {
|
|
120
|
+
for (const [, toolCall] of this.toolCallsMap.entries()) if (toolCall.id === event.toolCallId) {
|
|
121
|
+
toolCall.function.arguments += event.delta;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Complete a tool call with its final input
|
|
127
|
+
* Called when TOOL_CALL_END is received
|
|
128
|
+
*/
|
|
129
|
+
completeToolCall(event) {
|
|
130
|
+
for (const [, toolCall] of this.toolCallsMap.entries()) if (toolCall.id === event.toolCallId) {
|
|
131
|
+
if (event.input !== void 0) {
|
|
132
|
+
const normalized = event.input && typeof event.input === "object" ? event.input : {};
|
|
133
|
+
toolCall.function.arguments = JSON.stringify(normalized);
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Check if there are any complete tool calls to execute
|
|
140
|
+
*/
|
|
141
|
+
hasToolCalls() {
|
|
142
|
+
return this.getToolCalls().length > 0;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get all complete tool calls (filtered for valid ID and name)
|
|
146
|
+
*/
|
|
147
|
+
getToolCalls() {
|
|
148
|
+
return Array.from(this.toolCallsMap.values()).filter((tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Execute all tool calls and return tool result messages
|
|
152
|
+
* Yields TOOL_CALL_END events for streaming
|
|
153
|
+
* @param finishEvent - RUN_FINISHED event from the stream
|
|
154
|
+
*/
|
|
155
|
+
async *executeTools(finishEvent, ...contextArgs) {
|
|
156
|
+
const toolCallsArray = this.getToolCalls();
|
|
157
|
+
const toolResults = [];
|
|
158
|
+
const hasRuntimeContext = contextArgs.length > 0;
|
|
159
|
+
const userContext = contextArgs[0];
|
|
160
|
+
for (const toolCall of toolCallsArray) {
|
|
161
|
+
const tool = this.tools.find((t) => t.name === toolCall.function.name);
|
|
162
|
+
let toolResultContent;
|
|
163
|
+
let toolResultState;
|
|
164
|
+
let toolOutput;
|
|
165
|
+
if (tool?.execute) try {
|
|
166
|
+
let args;
|
|
167
|
+
try {
|
|
168
|
+
const argsString = toolCall.function.arguments.trim() || "{}";
|
|
169
|
+
const parsed = JSON.parse(argsString);
|
|
170
|
+
args = parsed && typeof parsed === "object" ? parsed : {};
|
|
171
|
+
} catch (parseError) {
|
|
172
|
+
throw new Error(`Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`);
|
|
173
|
+
}
|
|
174
|
+
if (tool.inputSchema && isStandardSchema(tool.inputSchema)) try {
|
|
175
|
+
args = parseWithStandardSchema(tool.inputSchema, args);
|
|
176
|
+
} catch (validationError) {
|
|
177
|
+
const message = validationError instanceof Error ? validationError.message : "Validation failed";
|
|
178
|
+
throw new Error(`Input validation failed for tool ${tool.name}: ${message}`);
|
|
179
|
+
}
|
|
180
|
+
const executionContext = {
|
|
181
|
+
toolCallId: toolCall.id,
|
|
182
|
+
context: userContext,
|
|
183
|
+
emitCustomEvent: () => {}
|
|
184
|
+
};
|
|
185
|
+
let result = hasRuntimeContext ? await tool.execute(args, executionContext) : await tool.execute(args);
|
|
186
|
+
if (tool.outputSchema && isStandardSchema(tool.outputSchema)) try {
|
|
187
|
+
result = parseWithStandardSchema(tool.outputSchema, result);
|
|
188
|
+
} catch (validationError) {
|
|
189
|
+
const message = validationError instanceof Error ? validationError.message : "Validation failed";
|
|
190
|
+
throw new Error(`Output validation failed for tool ${tool.name}: ${message}`);
|
|
191
|
+
}
|
|
192
|
+
toolOutput = result;
|
|
193
|
+
toolResultContent = normalizeToolResult(result);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
toolResultContent = `Error executing tool: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
196
|
+
toolResultState = "output-error";
|
|
197
|
+
}
|
|
198
|
+
else toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`;
|
|
199
|
+
yield {
|
|
200
|
+
type: "TOOL_CALL_END",
|
|
201
|
+
toolCallId: toolCall.id,
|
|
202
|
+
toolCallName: toolCall.function.name,
|
|
203
|
+
toolName: toolCall.function.name,
|
|
204
|
+
model: finishEvent.model,
|
|
205
|
+
timestamp: Date.now(),
|
|
206
|
+
...toolOutput !== void 0 ? { output: toolOutput } : {},
|
|
207
|
+
result: toolResultContent,
|
|
208
|
+
...toolResultState !== void 0 && { state: toolResultState }
|
|
209
|
+
};
|
|
210
|
+
toolResults.push({
|
|
211
|
+
role: "tool",
|
|
212
|
+
content: toolResultContent,
|
|
213
|
+
toolCallId: toolCall.id
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return toolResults;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Clear the tool calls map for the next iteration
|
|
220
|
+
*/
|
|
221
|
+
clear() {
|
|
222
|
+
this.toolCallsMap.clear();
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
function approvalResolution(approvals, toolCallId) {
|
|
226
|
+
return approvals.get(toolCallId) ?? approvals.get(`approval_${toolCallId}`);
|
|
49
227
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
tools;
|
|
53
|
-
constructor(tools) {
|
|
54
|
-
this.tools = tools;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)
|
|
58
|
-
*/
|
|
59
|
-
addToolCallStartEvent(event) {
|
|
60
|
-
const index = event.index ?? this.toolCallsMap.size;
|
|
61
|
-
const runtimeEvent = event;
|
|
62
|
-
const name = runtimeEvent.toolCallName ?? runtimeEvent.toolName;
|
|
63
|
-
this.toolCallsMap.set(index, {
|
|
64
|
-
id: event.toolCallId,
|
|
65
|
-
type: "function",
|
|
66
|
-
function: {
|
|
67
|
-
name,
|
|
68
|
-
arguments: ""
|
|
69
|
-
},
|
|
70
|
-
...event.metadata !== void 0 && { metadata: event.metadata }
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)
|
|
75
|
-
*/
|
|
76
|
-
addToolCallArgsEvent(event) {
|
|
77
|
-
for (const [, toolCall] of this.toolCallsMap.entries()) {
|
|
78
|
-
if (toolCall.id === event.toolCallId) {
|
|
79
|
-
toolCall.function.arguments += event.delta;
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Complete a tool call with its final input
|
|
86
|
-
* Called when TOOL_CALL_END is received
|
|
87
|
-
*/
|
|
88
|
-
completeToolCall(event) {
|
|
89
|
-
for (const [, toolCall] of this.toolCallsMap.entries()) {
|
|
90
|
-
if (toolCall.id === event.toolCallId) {
|
|
91
|
-
if (event.input !== void 0) {
|
|
92
|
-
const normalized = event.input && typeof event.input === "object" ? event.input : {};
|
|
93
|
-
toolCall.function.arguments = JSON.stringify(normalized);
|
|
94
|
-
}
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Check if there are any complete tool calls to execute
|
|
101
|
-
*/
|
|
102
|
-
hasToolCalls() {
|
|
103
|
-
return this.getToolCalls().length > 0;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Get all complete tool calls (filtered for valid ID and name)
|
|
107
|
-
*/
|
|
108
|
-
getToolCalls() {
|
|
109
|
-
return Array.from(this.toolCallsMap.values()).filter(
|
|
110
|
-
(tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Execute all tool calls and return tool result messages
|
|
115
|
-
* Yields TOOL_CALL_END events for streaming
|
|
116
|
-
* @param finishEvent - RUN_FINISHED event from the stream
|
|
117
|
-
*/
|
|
118
|
-
async *executeTools(finishEvent, ...contextArgs) {
|
|
119
|
-
const toolCallsArray = this.getToolCalls();
|
|
120
|
-
const toolResults = [];
|
|
121
|
-
const hasRuntimeContext = contextArgs.length > 0;
|
|
122
|
-
const userContext = contextArgs[0];
|
|
123
|
-
for (const toolCall of toolCallsArray) {
|
|
124
|
-
const tool = this.tools.find((t) => t.name === toolCall.function.name);
|
|
125
|
-
let toolResultContent;
|
|
126
|
-
let toolResultState;
|
|
127
|
-
if (tool?.execute) {
|
|
128
|
-
try {
|
|
129
|
-
let args;
|
|
130
|
-
try {
|
|
131
|
-
const argsString = toolCall.function.arguments.trim() || "{}";
|
|
132
|
-
const parsed = JSON.parse(argsString);
|
|
133
|
-
args = parsed && typeof parsed === "object" ? parsed : {};
|
|
134
|
-
} catch (parseError) {
|
|
135
|
-
throw new Error(
|
|
136
|
-
`Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {
|
|
140
|
-
try {
|
|
141
|
-
args = parseWithStandardSchema(tool.inputSchema, args);
|
|
142
|
-
} catch (validationError) {
|
|
143
|
-
const message = validationError instanceof Error ? validationError.message : "Validation failed";
|
|
144
|
-
throw new Error(
|
|
145
|
-
`Input validation failed for tool ${tool.name}: ${message}`
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
const executionContext = {
|
|
150
|
-
toolCallId: toolCall.id,
|
|
151
|
-
context: userContext,
|
|
152
|
-
emitCustomEvent: () => {
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
let result = hasRuntimeContext ? await tool.execute(args, executionContext) : await tool.execute(args);
|
|
156
|
-
if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {
|
|
157
|
-
try {
|
|
158
|
-
result = parseWithStandardSchema(tool.outputSchema, result);
|
|
159
|
-
} catch (validationError) {
|
|
160
|
-
const message = validationError instanceof Error ? validationError.message : "Validation failed";
|
|
161
|
-
throw new Error(
|
|
162
|
-
`Output validation failed for tool ${tool.name}: ${message}`
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
toolResultContent = normalizeToolResult(result);
|
|
167
|
-
} catch (error) {
|
|
168
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
169
|
-
toolResultContent = `Error executing tool: ${message}`;
|
|
170
|
-
toolResultState = "output-error";
|
|
171
|
-
}
|
|
172
|
-
} else {
|
|
173
|
-
toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`;
|
|
174
|
-
}
|
|
175
|
-
yield {
|
|
176
|
-
type: "TOOL_CALL_END",
|
|
177
|
-
toolCallId: toolCall.id,
|
|
178
|
-
toolCallName: toolCall.function.name,
|
|
179
|
-
toolName: toolCall.function.name,
|
|
180
|
-
model: finishEvent.model,
|
|
181
|
-
timestamp: Date.now(),
|
|
182
|
-
result: toolResultContent,
|
|
183
|
-
...toolResultState !== void 0 && { state: toolResultState }
|
|
184
|
-
};
|
|
185
|
-
toolResults.push({
|
|
186
|
-
role: "tool",
|
|
187
|
-
content: toolResultContent,
|
|
188
|
-
toolCallId: toolCall.id
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
return toolResults;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Clear the tool calls map for the next iteration
|
|
195
|
-
*/
|
|
196
|
-
clear() {
|
|
197
|
-
this.toolCallsMap.clear();
|
|
198
|
-
}
|
|
228
|
+
function isApproved(resolution) {
|
|
229
|
+
return typeof resolution === "boolean" ? resolution : resolution.approved;
|
|
199
230
|
}
|
|
231
|
+
function editedApprovalArgs(resolution) {
|
|
232
|
+
return typeof resolution === "object" && resolution.approved ? resolution.editedArgs : void 0;
|
|
233
|
+
}
|
|
234
|
+
function deniedApprovalResult(resolution) {
|
|
235
|
+
return typeof resolution === "object" && !resolution.approved ? resolution.payload ?? { error: "User declined tool execution" } : { error: "User declined tool execution" };
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Helper that runs a tool execution promise while polling for pending custom events.
|
|
239
|
+
* Yields any custom events that are emitted during execution, then returns the
|
|
240
|
+
* execution result.
|
|
241
|
+
*/
|
|
200
242
|
async function* executeWithEventPolling(executionPromise, pendingEvents) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
while ((event = pendingEvents.shift()) !== void 0) {
|
|
219
|
-
yield event;
|
|
220
|
-
}
|
|
221
|
-
return state.result;
|
|
243
|
+
const state = {
|
|
244
|
+
done: false,
|
|
245
|
+
result: void 0
|
|
246
|
+
};
|
|
247
|
+
const executionWithFlag = executionPromise.then((r) => {
|
|
248
|
+
state.done = true;
|
|
249
|
+
state.result = r;
|
|
250
|
+
return r;
|
|
251
|
+
});
|
|
252
|
+
while (!state.done) {
|
|
253
|
+
await Promise.race([executionWithFlag, new Promise((resolve) => setTimeout(resolve, 10))]);
|
|
254
|
+
let event;
|
|
255
|
+
while ((event = pendingEvents.shift()) !== void 0) yield event;
|
|
256
|
+
}
|
|
257
|
+
let event;
|
|
258
|
+
while ((event = pendingEvents.shift()) !== void 0) yield event;
|
|
259
|
+
return state.result;
|
|
222
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Apply a middleware onBeforeToolCall decision.
|
|
263
|
+
* Returns the (possibly transformed) input if execution should proceed,
|
|
264
|
+
* or undefined if the tool call was skipped (result already pushed).
|
|
265
|
+
* Throws MiddlewareAbortError if the decision is 'abort'.
|
|
266
|
+
*/
|
|
223
267
|
async function applyBeforeToolCallDecision(toolCall, tool, input, toolName, middlewareHooks, results) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
268
|
+
if (!middlewareHooks.onBeforeToolCall) return {
|
|
269
|
+
proceed: true,
|
|
270
|
+
input
|
|
271
|
+
};
|
|
272
|
+
const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input);
|
|
273
|
+
if (!decision) return {
|
|
274
|
+
proceed: true,
|
|
275
|
+
input
|
|
276
|
+
};
|
|
277
|
+
if (decision.type === "abort") throw new MiddlewareAbortError(decision.reason || "Aborted by middleware");
|
|
278
|
+
if (decision.type === "skip") {
|
|
279
|
+
const skipResult = decision.result;
|
|
280
|
+
results.push({
|
|
281
|
+
toolCallId: toolCall.id,
|
|
282
|
+
toolName,
|
|
283
|
+
result: typeof skipResult === "string" ? safeJsonParse(skipResult) : skipResult ?? null,
|
|
284
|
+
duration: 0
|
|
285
|
+
});
|
|
286
|
+
if (middlewareHooks.onAfterToolCall) await middlewareHooks.onAfterToolCall({
|
|
287
|
+
toolCall,
|
|
288
|
+
tool,
|
|
289
|
+
toolName,
|
|
290
|
+
toolCallId: toolCall.id,
|
|
291
|
+
ok: true,
|
|
292
|
+
duration: 0,
|
|
293
|
+
result: skipResult
|
|
294
|
+
});
|
|
295
|
+
return { proceed: false };
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
proceed: true,
|
|
299
|
+
input: decision.args
|
|
300
|
+
};
|
|
256
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Execute a server-side tool with event polling, output validation, and middleware hooks.
|
|
304
|
+
* Yields CustomEvent chunks during execution and pushes the result to the results array.
|
|
305
|
+
*/
|
|
257
306
|
async function* executeServerTool(toolCall, tool, toolName, input, context, pendingEvents, results, middlewareHooks) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (middlewareHooks?.onAfterToolCall) {
|
|
310
|
-
await middlewareHooks.onAfterToolCall({
|
|
311
|
-
toolCall,
|
|
312
|
-
tool,
|
|
313
|
-
toolName,
|
|
314
|
-
toolCallId: toolCall.id,
|
|
315
|
-
ok: false,
|
|
316
|
-
duration,
|
|
317
|
-
error
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
307
|
+
const startTime = Date.now();
|
|
308
|
+
try {
|
|
309
|
+
if (!tool.execute) throw new Error(`Tool ${toolName} has no execute() implementation`);
|
|
310
|
+
let result = yield* executeWithEventPolling(Promise.resolve(tool.execute(input, context)), pendingEvents);
|
|
311
|
+
const duration = Date.now() - startTime;
|
|
312
|
+
await emitUiResourceIfLinked(tool, context);
|
|
313
|
+
let pendingEvent;
|
|
314
|
+
while ((pendingEvent = pendingEvents.shift()) !== void 0) yield pendingEvent;
|
|
315
|
+
if (tool.outputSchema && isStandardSchema(tool.outputSchema)) result = parseWithStandardSchema(tool.outputSchema, result);
|
|
316
|
+
const finalResult = typeof result === "string" ? safeJsonParse(result) : result ?? null;
|
|
317
|
+
results.push({
|
|
318
|
+
toolCallId: toolCall.id,
|
|
319
|
+
toolName,
|
|
320
|
+
result: finalResult,
|
|
321
|
+
input,
|
|
322
|
+
output: finalResult,
|
|
323
|
+
duration
|
|
324
|
+
});
|
|
325
|
+
if (middlewareHooks?.onAfterToolCall) await middlewareHooks.onAfterToolCall({
|
|
326
|
+
toolCall,
|
|
327
|
+
tool,
|
|
328
|
+
toolName,
|
|
329
|
+
toolCallId: toolCall.id,
|
|
330
|
+
ok: true,
|
|
331
|
+
duration,
|
|
332
|
+
result: finalResult
|
|
333
|
+
});
|
|
334
|
+
} catch (error) {
|
|
335
|
+
const duration = Date.now() - startTime;
|
|
336
|
+
let pendingEvent;
|
|
337
|
+
while ((pendingEvent = pendingEvents.shift()) !== void 0) yield pendingEvent;
|
|
338
|
+
if (error instanceof MiddlewareAbortError) throw error;
|
|
339
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
340
|
+
results.push({
|
|
341
|
+
toolCallId: toolCall.id,
|
|
342
|
+
toolName,
|
|
343
|
+
result: { error: message },
|
|
344
|
+
input,
|
|
345
|
+
state: "output-error",
|
|
346
|
+
duration
|
|
347
|
+
});
|
|
348
|
+
if (middlewareHooks?.onAfterToolCall) await middlewareHooks.onAfterToolCall({
|
|
349
|
+
toolCall,
|
|
350
|
+
tool,
|
|
351
|
+
toolName,
|
|
352
|
+
toolCallId: toolCall.id,
|
|
353
|
+
ok: false,
|
|
354
|
+
duration,
|
|
355
|
+
error
|
|
356
|
+
});
|
|
357
|
+
}
|
|
321
358
|
}
|
|
322
|
-
function buildClientToolResult(toolCallId, toolName, tool, rawResult) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
359
|
+
function buildClientToolResult(toolCallId, toolName, tool, rawResult, input) {
|
|
360
|
+
try {
|
|
361
|
+
let result = rawResult;
|
|
362
|
+
if (tool.outputSchema && isStandardSchema(tool.outputSchema)) result = parseWithStandardSchema(tool.outputSchema, result);
|
|
363
|
+
const parsed = typeof result === "string" ? safeJsonParse(result) : result ?? null;
|
|
364
|
+
return {
|
|
365
|
+
toolCallId,
|
|
366
|
+
toolName,
|
|
367
|
+
result: parsed,
|
|
368
|
+
input,
|
|
369
|
+
output: parsed
|
|
370
|
+
};
|
|
371
|
+
} catch (error) {
|
|
372
|
+
return {
|
|
373
|
+
toolCallId,
|
|
374
|
+
toolName,
|
|
375
|
+
result: { error: error instanceof Error ? error.message : "Validation failed" },
|
|
376
|
+
input,
|
|
377
|
+
state: "output-error"
|
|
378
|
+
};
|
|
379
|
+
}
|
|
342
380
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
middlewareHooks
|
|
498
|
-
);
|
|
499
|
-
} else {
|
|
500
|
-
results.push({
|
|
501
|
-
toolCallId: toolCall.id,
|
|
502
|
-
toolName,
|
|
503
|
-
result: { error: "User declined tool execution" },
|
|
504
|
-
state: "output-error"
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
} else {
|
|
508
|
-
needsApproval.push({
|
|
509
|
-
toolCallId: toolCall.id,
|
|
510
|
-
toolName,
|
|
511
|
-
input,
|
|
512
|
-
approvalId
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
if (middlewareHooks) {
|
|
518
|
-
const decision = await applyBeforeToolCallDecision(
|
|
519
|
-
toolCall,
|
|
520
|
-
tool,
|
|
521
|
-
input,
|
|
522
|
-
toolName,
|
|
523
|
-
middlewareHooks,
|
|
524
|
-
results
|
|
525
|
-
);
|
|
526
|
-
if (!decision.proceed) continue;
|
|
527
|
-
input = decision.input;
|
|
528
|
-
}
|
|
529
|
-
yield* executeServerTool(
|
|
530
|
-
toolCall,
|
|
531
|
-
tool,
|
|
532
|
-
toolName,
|
|
533
|
-
input,
|
|
534
|
-
context,
|
|
535
|
-
pendingEvents,
|
|
536
|
-
results,
|
|
537
|
-
middlewareHooks
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
return { results, needsApproval, needsClientExecution };
|
|
381
|
+
/**
|
|
382
|
+
* Execute tool calls based on their configuration.
|
|
383
|
+
* Yields CustomEvent chunks during tool execution for real-time progress updates.
|
|
384
|
+
*
|
|
385
|
+
* Handles three cases:
|
|
386
|
+
* 1. Client tools (no execute) - request client to execute
|
|
387
|
+
* 2. Server tools with approval - check approval before executing
|
|
388
|
+
* 3. Normal server tools - execute immediately
|
|
389
|
+
*
|
|
390
|
+
* @param toolCalls - Tool calls from the LLM
|
|
391
|
+
* @param tools - Available tools with their configurations
|
|
392
|
+
* @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution
|
|
393
|
+
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
394
|
+
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
395
|
+
*/
|
|
396
|
+
async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ new Map(), clientResults = /* @__PURE__ */ new Map(), createCustomEventChunk, middlewareHooks, userContext, abortSignal, resumeState) {
|
|
397
|
+
const results = [];
|
|
398
|
+
const needsApproval = [];
|
|
399
|
+
const needsClientExecution = [];
|
|
400
|
+
const toolMap = /* @__PURE__ */ new Map();
|
|
401
|
+
for (const tool of tools) toolMap.set(tool.name, tool);
|
|
402
|
+
const hasPendingApprovals = toolCalls.some((tc) => {
|
|
403
|
+
return toolMap.get(tc.function.name)?.needsApproval && approvalResolution(approvals, tc.id) === void 0 && !resumeState?.cancelledToolCallIds?.has(tc.id);
|
|
404
|
+
});
|
|
405
|
+
for (const toolCall of toolCalls) {
|
|
406
|
+
const tool = toolMap.get(toolCall.function.name);
|
|
407
|
+
const toolName = toolCall.function.name;
|
|
408
|
+
if (!tool) {
|
|
409
|
+
results.push({
|
|
410
|
+
toolCallId: toolCall.id,
|
|
411
|
+
toolName,
|
|
412
|
+
result: { error: `Unknown tool: ${toolName}` },
|
|
413
|
+
state: "output-error"
|
|
414
|
+
});
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
if (hasPendingApprovals) {
|
|
418
|
+
const isPendingApproval = tool.needsApproval && approvalResolution(approvals, toolCall.id) === void 0;
|
|
419
|
+
const isPlainClientRequest = !tool.needsApproval && !tool.execute;
|
|
420
|
+
if (!isPendingApproval && !isPlainClientRequest) continue;
|
|
421
|
+
}
|
|
422
|
+
if (resumeState?.cancelledToolCallIds?.has(toolCall.id)) {
|
|
423
|
+
results.push({
|
|
424
|
+
toolCallId: toolCall.id,
|
|
425
|
+
toolName,
|
|
426
|
+
result: { error: "Tool execution cancelled" },
|
|
427
|
+
state: "output-error"
|
|
428
|
+
});
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
let input = {};
|
|
432
|
+
const argsStr = toolCall.function.arguments.trim() || "{}";
|
|
433
|
+
if (argsStr) try {
|
|
434
|
+
const parsed = JSON.parse(argsStr);
|
|
435
|
+
input = parsed && typeof parsed === "object" ? parsed : {};
|
|
436
|
+
} catch (parseError) {
|
|
437
|
+
throw new Error(`Failed to parse tool arguments as JSON: ${argsStr}`);
|
|
438
|
+
}
|
|
439
|
+
if (tool.inputSchema && isStandardSchema(tool.inputSchema)) try {
|
|
440
|
+
input = parseWithStandardSchema(tool.inputSchema, input);
|
|
441
|
+
} catch (validationError) {
|
|
442
|
+
const message = validationError instanceof Error ? validationError.message : "Validation failed";
|
|
443
|
+
results.push({
|
|
444
|
+
toolCallId: toolCall.id,
|
|
445
|
+
toolName,
|
|
446
|
+
result: { error: `Input validation failed for tool ${tool.name}: ${message}` },
|
|
447
|
+
input,
|
|
448
|
+
state: "output-error"
|
|
449
|
+
});
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const pendingEvents = [];
|
|
453
|
+
const context = {
|
|
454
|
+
toolCallId: toolCall.id,
|
|
455
|
+
context: userContext,
|
|
456
|
+
abortSignal,
|
|
457
|
+
emitCustomEvent: (eventName, value) => {
|
|
458
|
+
if (createCustomEventChunk) pendingEvents.push(createCustomEventChunk(eventName, {
|
|
459
|
+
...value,
|
|
460
|
+
toolCallId: toolCall.id
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
if (!tool.execute) {
|
|
465
|
+
if (tool.needsApproval) {
|
|
466
|
+
const approvalId = `approval_${toolCall.id}`;
|
|
467
|
+
const resolution = approvalResolution(approvals, toolCall.id);
|
|
468
|
+
if (resolution !== void 0) if (isApproved(resolution)) {
|
|
469
|
+
input = editedApprovalArgs(resolution) ?? input;
|
|
470
|
+
if (clientResults.has(toolCall.id)) results.push(buildClientToolResult(toolCall.id, toolName, tool, clientResults.get(toolCall.id), input));
|
|
471
|
+
else needsClientExecution.push({
|
|
472
|
+
toolCallId: toolCall.id,
|
|
473
|
+
toolName,
|
|
474
|
+
input
|
|
475
|
+
});
|
|
476
|
+
} else results.push({
|
|
477
|
+
toolCallId: toolCall.id,
|
|
478
|
+
toolName,
|
|
479
|
+
result: resumeState?.deniedToolResults?.get(toolCall.id) ?? deniedApprovalResult(resolution),
|
|
480
|
+
input,
|
|
481
|
+
state: "output-error"
|
|
482
|
+
});
|
|
483
|
+
else needsApproval.push({
|
|
484
|
+
toolCallId: toolCall.id,
|
|
485
|
+
toolName: toolCall.function.name,
|
|
486
|
+
input,
|
|
487
|
+
approvalId
|
|
488
|
+
});
|
|
489
|
+
} else if (clientResults.has(toolCall.id)) results.push(buildClientToolResult(toolCall.id, toolName, tool, clientResults.get(toolCall.id), input));
|
|
490
|
+
else needsClientExecution.push({
|
|
491
|
+
toolCallId: toolCall.id,
|
|
492
|
+
toolName,
|
|
493
|
+
input
|
|
494
|
+
});
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (tool.needsApproval) {
|
|
498
|
+
const approvalId = `approval_${toolCall.id}`;
|
|
499
|
+
const resolution = approvalResolution(approvals, toolCall.id);
|
|
500
|
+
if (resolution !== void 0) if (isApproved(resolution)) {
|
|
501
|
+
input = editedApprovalArgs(resolution) ?? input;
|
|
502
|
+
if (middlewareHooks) {
|
|
503
|
+
const decision = await applyBeforeToolCallDecision(toolCall, tool, input, toolName, middlewareHooks, results);
|
|
504
|
+
if (!decision.proceed) continue;
|
|
505
|
+
input = decision.input;
|
|
506
|
+
}
|
|
507
|
+
yield* executeServerTool(toolCall, tool, toolName, input, context, pendingEvents, results, middlewareHooks);
|
|
508
|
+
} else results.push({
|
|
509
|
+
toolCallId: toolCall.id,
|
|
510
|
+
toolName,
|
|
511
|
+
result: resumeState?.deniedToolResults?.get(toolCall.id) ?? deniedApprovalResult(resolution),
|
|
512
|
+
input,
|
|
513
|
+
state: "output-error"
|
|
514
|
+
});
|
|
515
|
+
else needsApproval.push({
|
|
516
|
+
toolCallId: toolCall.id,
|
|
517
|
+
toolName,
|
|
518
|
+
input,
|
|
519
|
+
approvalId
|
|
520
|
+
});
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (middlewareHooks) {
|
|
524
|
+
const decision = await applyBeforeToolCallDecision(toolCall, tool, input, toolName, middlewareHooks, results);
|
|
525
|
+
if (!decision.proceed) continue;
|
|
526
|
+
input = decision.input;
|
|
527
|
+
}
|
|
528
|
+
yield* executeServerTool(toolCall, tool, toolName, input, context, pendingEvents, results, middlewareHooks);
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
results,
|
|
532
|
+
needsApproval,
|
|
533
|
+
needsClientExecution
|
|
534
|
+
};
|
|
541
535
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
executeToolCalls
|
|
547
|
-
};
|
|
548
|
-
//# sourceMappingURL=tool-calls.js.map
|
|
536
|
+
//#endregion
|
|
537
|
+
export { MiddlewareAbortError, ToolCallManager, executeServerTool, executeToolCalls };
|
|
538
|
+
|
|
539
|
+
//# sourceMappingURL=tool-calls.js.map
|