@copilotkit/runtime 1.55.1 → 1.55.2-next.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/CHANGELOG.md +13 -0
- package/dist/agent/converters/aisdk.cjs +215 -0
- package/dist/agent/converters/aisdk.cjs.map +1 -0
- package/dist/agent/converters/aisdk.d.cts +18 -0
- package/dist/agent/converters/aisdk.d.cts.map +1 -0
- package/dist/agent/converters/aisdk.d.mts +18 -0
- package/dist/agent/converters/aisdk.d.mts.map +1 -0
- package/dist/agent/converters/aisdk.mjs +214 -0
- package/dist/agent/converters/aisdk.mjs.map +1 -0
- package/dist/agent/converters/index.d.mts +3 -0
- package/dist/agent/converters/tanstack.cjs +180 -0
- package/dist/agent/converters/tanstack.cjs.map +1 -0
- package/dist/agent/converters/tanstack.d.cts +68 -0
- package/dist/agent/converters/tanstack.d.cts.map +1 -0
- package/dist/agent/converters/tanstack.d.mts +68 -0
- package/dist/agent/converters/tanstack.d.mts.map +1 -0
- package/dist/agent/converters/tanstack.mjs +178 -0
- package/dist/agent/converters/tanstack.mjs.map +1 -0
- package/dist/agent/index.cjs +111 -17
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +61 -4
- package/dist/agent/index.d.cts.map +1 -1
- package/dist/agent/index.d.mts +62 -4
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/agent/index.mjs +111 -17
- package/dist/agent/index.mjs.map +1 -1
- package/dist/lib/integrations/nextjs/pages-router.cjs.map +1 -1
- package/dist/lib/integrations/nextjs/pages-router.d.cts.map +1 -1
- package/dist/lib/integrations/nextjs/pages-router.d.mts.map +1 -1
- package/dist/lib/integrations/nextjs/pages-router.mjs.map +1 -1
- package/dist/lib/runtime/copilot-runtime.cjs +4 -2
- package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.mjs +4 -2
- package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
- package/dist/lib/runtime/mcp-tools-utils.cjs +1 -1
- package/dist/lib/runtime/mcp-tools-utils.cjs.map +1 -1
- package/dist/lib/runtime/mcp-tools-utils.mjs +1 -1
- package/dist/lib/runtime/mcp-tools-utils.mjs.map +1 -1
- package/dist/package.cjs +3 -2
- package/dist/package.mjs +3 -2
- package/dist/service-adapters/anthropic/utils.cjs +1 -1
- package/dist/service-adapters/anthropic/utils.cjs.map +1 -1
- package/dist/service-adapters/anthropic/utils.mjs +1 -1
- package/dist/service-adapters/anthropic/utils.mjs.map +1 -1
- package/dist/service-adapters/openai/utils.cjs +1 -1
- package/dist/service-adapters/openai/utils.cjs.map +1 -1
- package/dist/service-adapters/openai/utils.mjs +1 -1
- package/dist/service-adapters/openai/utils.mjs.map +1 -1
- package/dist/v2/index.cjs +5 -0
- package/dist/v2/index.d.cts +4 -2
- package/dist/v2/index.d.mts +4 -2
- package/dist/v2/index.mjs +3 -1
- package/package.json +4 -3
- package/src/agent/__tests__/agent-test-helpers.ts +446 -0
- package/src/agent/__tests__/agent.test.ts +593 -0
- package/src/agent/__tests__/converter-aisdk.test.ts +692 -0
- package/src/agent/__tests__/converter-custom.test.ts +319 -0
- package/src/agent/__tests__/converter-tanstack-input.test.ts +211 -0
- package/src/agent/__tests__/converter-tanstack.test.ts +314 -0
- package/src/agent/__tests__/mcp-servers-integration.test.ts +373 -0
- package/src/agent/__tests__/multimodal-tanstack.test.ts +284 -0
- package/src/agent/__tests__/test-helpers.ts +12 -8
- package/src/agent/converters/aisdk.ts +326 -0
- package/src/agent/converters/index.ts +7 -0
- package/src/agent/converters/tanstack.ts +286 -0
- package/src/agent/index.ts +245 -26
- package/src/lib/integrations/nextjs/pages-router.ts +1 -0
- package/src/lib/runtime/copilot-runtime.ts +21 -12
- package/src/lib/runtime/mcp-tools-utils.ts +1 -1
- package/src/service-adapters/anthropic/utils.ts +1 -1
- package/src/service-adapters/openai/utils.ts +1 -1
- package/src/v2/runtime/__tests__/mcp-apps-middleware-integration.test.ts +275 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @copilotkit/runtime
|
|
2
2
|
|
|
3
|
+
## 1.55.2-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5f4ca65: feat(runtime): add factory mode to BuiltInAgent for backend-agnostic LLM integration
|
|
8
|
+
- @copilotkit/shared@1.55.2-next.1
|
|
9
|
+
|
|
10
|
+
## 1.55.2-next.0
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- @copilotkit/shared@1.55.2-next.0
|
|
15
|
+
|
|
3
16
|
## 1.55.1
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
require("reflect-metadata");
|
|
2
|
+
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
let _copilotkit_shared = require("@copilotkit/shared");
|
|
4
|
+
let _ag_ui_client = require("@ag-ui/client");
|
|
5
|
+
|
|
6
|
+
//#region src/agent/converters/aisdk.ts
|
|
7
|
+
/**
|
|
8
|
+
* Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.
|
|
9
|
+
*
|
|
10
|
+
* This is a pure converter — it does NOT emit lifecycle events
|
|
11
|
+
* (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)
|
|
12
|
+
* is responsible for those.
|
|
13
|
+
*
|
|
14
|
+
* Terminal stream events (finish, error, abort) cause the generator to
|
|
15
|
+
* return so the caller can handle lifecycle appropriately.
|
|
16
|
+
*/
|
|
17
|
+
async function* convertAISDKStream(fullStream, abortSignal) {
|
|
18
|
+
let messageId = (0, _copilotkit_shared.randomUUID)();
|
|
19
|
+
let reasoningMessageId = (0, _copilotkit_shared.randomUUID)();
|
|
20
|
+
let isInReasoning = false;
|
|
21
|
+
const toolCallStates = /* @__PURE__ */ new Map();
|
|
22
|
+
const ensureToolCallState = (toolCallId) => {
|
|
23
|
+
let state = toolCallStates.get(toolCallId);
|
|
24
|
+
if (!state) {
|
|
25
|
+
state = {
|
|
26
|
+
started: false,
|
|
27
|
+
hasArgsDelta: false,
|
|
28
|
+
ended: false
|
|
29
|
+
};
|
|
30
|
+
toolCallStates.set(toolCallId, state);
|
|
31
|
+
}
|
|
32
|
+
return state;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Auto-close an open reasoning lifecycle.
|
|
36
|
+
* Some AI SDK providers (notably @ai-sdk/anthropic) never emit "reasoning-end",
|
|
37
|
+
* which leaves downstream state machines stuck. This helper emits the
|
|
38
|
+
* missing REASONING_MESSAGE_END + REASONING_END events so the stream
|
|
39
|
+
* can transition to text, tool-call, or finish phases.
|
|
40
|
+
*/
|
|
41
|
+
function* closeReasoningIfOpen() {
|
|
42
|
+
if (!isInReasoning) return;
|
|
43
|
+
isInReasoning = false;
|
|
44
|
+
yield {
|
|
45
|
+
type: _ag_ui_client.EventType.REASONING_MESSAGE_END,
|
|
46
|
+
messageId: reasoningMessageId
|
|
47
|
+
};
|
|
48
|
+
yield {
|
|
49
|
+
type: _ag_ui_client.EventType.REASONING_END,
|
|
50
|
+
messageId: reasoningMessageId
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
for await (const part of fullStream) {
|
|
55
|
+
const p = part;
|
|
56
|
+
if (p.type !== "reasoning-delta") yield* closeReasoningIfOpen();
|
|
57
|
+
switch (p.type) {
|
|
58
|
+
case "abort": return;
|
|
59
|
+
case "reasoning-start": {
|
|
60
|
+
const providedId = "id" in p ? p.id : void 0;
|
|
61
|
+
reasoningMessageId = providedId && providedId !== "0" ? providedId : (0, _copilotkit_shared.randomUUID)();
|
|
62
|
+
yield {
|
|
63
|
+
type: _ag_ui_client.EventType.REASONING_START,
|
|
64
|
+
messageId: reasoningMessageId
|
|
65
|
+
};
|
|
66
|
+
yield {
|
|
67
|
+
type: _ag_ui_client.EventType.REASONING_MESSAGE_START,
|
|
68
|
+
messageId: reasoningMessageId,
|
|
69
|
+
role: "reasoning"
|
|
70
|
+
};
|
|
71
|
+
isInReasoning = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case "reasoning-delta": {
|
|
75
|
+
const delta = p.text ?? "";
|
|
76
|
+
if (!delta) break;
|
|
77
|
+
yield {
|
|
78
|
+
type: _ag_ui_client.EventType.REASONING_MESSAGE_CONTENT,
|
|
79
|
+
messageId: reasoningMessageId,
|
|
80
|
+
delta
|
|
81
|
+
};
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case "reasoning-end": break;
|
|
85
|
+
case "tool-input-start": {
|
|
86
|
+
const toolCallId = p.id;
|
|
87
|
+
const state = ensureToolCallState(toolCallId);
|
|
88
|
+
state.toolName = p.toolName;
|
|
89
|
+
if (!state.started) {
|
|
90
|
+
state.started = true;
|
|
91
|
+
yield {
|
|
92
|
+
type: _ag_ui_client.EventType.TOOL_CALL_START,
|
|
93
|
+
parentMessageId: messageId,
|
|
94
|
+
toolCallId,
|
|
95
|
+
toolCallName: p.toolName
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case "tool-input-delta": {
|
|
101
|
+
const toolCallId = p.id;
|
|
102
|
+
const state = ensureToolCallState(toolCallId);
|
|
103
|
+
state.hasArgsDelta = true;
|
|
104
|
+
yield {
|
|
105
|
+
type: _ag_ui_client.EventType.TOOL_CALL_ARGS,
|
|
106
|
+
toolCallId,
|
|
107
|
+
delta: p.delta
|
|
108
|
+
};
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "tool-input-end": break;
|
|
112
|
+
case "text-start": {
|
|
113
|
+
const providedId = "id" in p ? p.id : void 0;
|
|
114
|
+
messageId = providedId && providedId !== "0" ? providedId : (0, _copilotkit_shared.randomUUID)();
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case "text-delta": {
|
|
118
|
+
const textDelta = "text" in p ? p.text : "";
|
|
119
|
+
yield {
|
|
120
|
+
type: _ag_ui_client.EventType.TEXT_MESSAGE_CHUNK,
|
|
121
|
+
role: "assistant",
|
|
122
|
+
messageId,
|
|
123
|
+
delta: textDelta
|
|
124
|
+
};
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "tool-call": {
|
|
128
|
+
const toolCallId = p.toolCallId;
|
|
129
|
+
const state = ensureToolCallState(toolCallId);
|
|
130
|
+
state.toolName = p.toolName ?? state.toolName;
|
|
131
|
+
if (!state.started) {
|
|
132
|
+
state.started = true;
|
|
133
|
+
yield {
|
|
134
|
+
type: _ag_ui_client.EventType.TOOL_CALL_START,
|
|
135
|
+
parentMessageId: messageId,
|
|
136
|
+
toolCallId,
|
|
137
|
+
toolCallName: p.toolName
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (!state.hasArgsDelta && "input" in p && p.input !== void 0) {
|
|
141
|
+
let serializedInput = "";
|
|
142
|
+
if (typeof p.input === "string") serializedInput = p.input;
|
|
143
|
+
else try {
|
|
144
|
+
serializedInput = JSON.stringify(p.input);
|
|
145
|
+
} catch {
|
|
146
|
+
serializedInput = String(p.input);
|
|
147
|
+
}
|
|
148
|
+
if (serializedInput.length > 0) {
|
|
149
|
+
yield {
|
|
150
|
+
type: _ag_ui_client.EventType.TOOL_CALL_ARGS,
|
|
151
|
+
toolCallId,
|
|
152
|
+
delta: serializedInput
|
|
153
|
+
};
|
|
154
|
+
state.hasArgsDelta = true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!state.ended) {
|
|
158
|
+
state.ended = true;
|
|
159
|
+
yield {
|
|
160
|
+
type: _ag_ui_client.EventType.TOOL_CALL_END,
|
|
161
|
+
toolCallId
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case "tool-result": {
|
|
167
|
+
const toolResult = "output" in p ? p.output : "result" in p ? p.result : null;
|
|
168
|
+
const toolName = "toolName" in p ? p.toolName : "";
|
|
169
|
+
toolCallStates.delete(p.toolCallId);
|
|
170
|
+
if (toolName === "AGUISendStateSnapshot" && toolResult && typeof toolResult === "object") {
|
|
171
|
+
const snapshot = toolResult.snapshot;
|
|
172
|
+
if (snapshot !== void 0) yield {
|
|
173
|
+
type: _ag_ui_client.EventType.STATE_SNAPSHOT,
|
|
174
|
+
snapshot
|
|
175
|
+
};
|
|
176
|
+
} else if (toolName === "AGUISendStateDelta" && toolResult && typeof toolResult === "object") {
|
|
177
|
+
const delta = toolResult.delta;
|
|
178
|
+
if (delta !== void 0) yield {
|
|
179
|
+
type: _ag_ui_client.EventType.STATE_DELTA,
|
|
180
|
+
delta
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
let serializedResult;
|
|
184
|
+
try {
|
|
185
|
+
serializedResult = JSON.stringify(toolResult);
|
|
186
|
+
} catch {
|
|
187
|
+
serializedResult = `[Unserializable tool result from ${toolName || "unknown tool"}]`;
|
|
188
|
+
}
|
|
189
|
+
yield {
|
|
190
|
+
type: _ag_ui_client.EventType.TOOL_CALL_RESULT,
|
|
191
|
+
role: "tool",
|
|
192
|
+
messageId: (0, _copilotkit_shared.randomUUID)(),
|
|
193
|
+
toolCallId: p.toolCallId,
|
|
194
|
+
content: serializedResult
|
|
195
|
+
};
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case "finish": return;
|
|
199
|
+
case "error": {
|
|
200
|
+
if (abortSignal.aborted) return;
|
|
201
|
+
const err = p.error ?? p.message ?? p.cause;
|
|
202
|
+
if (err instanceof Error) throw err;
|
|
203
|
+
throw new Error(typeof err === "string" ? err : `AI SDK stream error: ${JSON.stringify(p)}`);
|
|
204
|
+
}
|
|
205
|
+
default: break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} finally {
|
|
209
|
+
yield* closeReasoningIfOpen();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
//#endregion
|
|
214
|
+
exports.convertAISDKStream = convertAISDKStream;
|
|
215
|
+
//# sourceMappingURL=aisdk.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aisdk.cjs","names":["EventType"],"sources":["../../../src/agent/converters/aisdk.ts"],"sourcesContent":["import {\n BaseEvent,\n EventType,\n ReasoningEndEvent,\n ReasoningMessageContentEvent,\n ReasoningMessageEndEvent,\n ReasoningMessageStartEvent,\n ReasoningStartEvent,\n TextMessageChunkEvent,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolCallResultEvent,\n StateSnapshotEvent,\n StateDeltaEvent,\n} from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\n\n/**\n * Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.\n *\n * This is a pure converter — it does NOT emit lifecycle events\n * (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)\n * is responsible for those.\n *\n * Terminal stream events (finish, error, abort) cause the generator to\n * return so the caller can handle lifecycle appropriately.\n */\nexport async function* convertAISDKStream(\n fullStream: AsyncIterable<unknown>,\n abortSignal: AbortSignal,\n): AsyncGenerator<BaseEvent> {\n let messageId = randomUUID();\n let reasoningMessageId = randomUUID();\n let isInReasoning = false;\n\n const toolCallStates = new Map<\n string,\n {\n started: boolean;\n hasArgsDelta: boolean;\n ended: boolean;\n toolName?: string;\n }\n >();\n\n const ensureToolCallState = (toolCallId: string) => {\n let state = toolCallStates.get(toolCallId);\n if (!state) {\n state = { started: false, hasArgsDelta: false, ended: false };\n toolCallStates.set(toolCallId, state);\n }\n return state;\n };\n\n /**\n * Auto-close an open reasoning lifecycle.\n * Some AI SDK providers (notably @ai-sdk/anthropic) never emit \"reasoning-end\",\n * which leaves downstream state machines stuck. This helper emits the\n * missing REASONING_MESSAGE_END + REASONING_END events so the stream\n * can transition to text, tool-call, or finish phases.\n */\n function* closeReasoningIfOpen(): Generator<BaseEvent> {\n if (!isInReasoning) return;\n isInReasoning = false;\n const reasoningMsgEnd: ReasoningMessageEndEvent = {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n };\n yield reasoningMsgEnd;\n const reasoningEnd: ReasoningEndEvent = {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n };\n yield reasoningEnd;\n }\n\n try {\n for await (const part of fullStream) {\n const p = part as Record<string, unknown>;\n\n // Close any open reasoning lifecycle on every event except\n // reasoning-delta, which arrives mid-block and must not interrupt it.\n if (p.type !== \"reasoning-delta\") {\n yield* closeReasoningIfOpen();\n }\n\n switch (p.type) {\n case \"abort\": {\n // Terminal — let the caller handle lifecycle\n return;\n }\n\n case \"reasoning-start\": {\n // Use SDK-provided id, or generate a fresh UUID if id is falsy/\"0\"\n // to prevent consecutive reasoning blocks from sharing a messageId\n const providedId = \"id\" in p ? p.id : undefined;\n reasoningMessageId =\n providedId && providedId !== \"0\"\n ? (providedId as string)\n : randomUUID();\n const reasoningStartEvent: ReasoningStartEvent = {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n };\n yield reasoningStartEvent;\n const reasoningMessageStart: ReasoningMessageStartEvent = {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: \"reasoning\",\n };\n yield reasoningMessageStart;\n isInReasoning = true;\n break;\n }\n\n case \"reasoning-delta\": {\n const delta = (p.text as string) ?? \"\";\n if (!delta) break; // skip — @ag-ui/core schema requires delta to be non-empty\n const reasoningDeltaEvent: ReasoningMessageContentEvent = {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta,\n };\n yield reasoningDeltaEvent;\n break;\n }\n\n case \"reasoning-end\": {\n // closeReasoningIfOpen() already called before the switch — no-op here\n // if the SDK never emits this event (e.g. @ai-sdk/anthropic).\n break;\n }\n\n case \"tool-input-start\": {\n const toolCallId = p.id as string;\n const state = ensureToolCallState(toolCallId);\n state.toolName = p.toolName as string;\n if (!state.started) {\n state.started = true;\n const startEvent: ToolCallStartEvent = {\n type: EventType.TOOL_CALL_START,\n parentMessageId: messageId,\n toolCallId,\n toolCallName: p.toolName as string,\n };\n yield startEvent;\n }\n break;\n }\n\n case \"tool-input-delta\": {\n const toolCallId = p.id as string;\n const state = ensureToolCallState(toolCallId);\n state.hasArgsDelta = true;\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: p.delta as string,\n };\n yield argsEvent;\n break;\n }\n\n case \"tool-input-end\": {\n // No direct event – the subsequent \"tool-call\" part marks completion.\n break;\n }\n\n case \"text-start\": {\n // New text message starting - use the SDK-provided id\n // Use randomUUID() if part.id is falsy or \"0\" to prevent message merging issues\n const providedId = \"id\" in p ? p.id : undefined;\n messageId =\n providedId && providedId !== \"0\"\n ? (providedId as string)\n : randomUUID();\n break;\n }\n\n case \"text-delta\": {\n // AI SDK text-delta events use 'text' (not 'delta')\n const textDelta = \"text\" in p ? (p.text as string) : \"\";\n const textEvent: TextMessageChunkEvent = {\n type: EventType.TEXT_MESSAGE_CHUNK,\n role: \"assistant\",\n messageId,\n delta: textDelta,\n };\n yield textEvent;\n break;\n }\n\n case \"tool-call\": {\n const toolCallId = p.toolCallId as string;\n const state = ensureToolCallState(toolCallId);\n state.toolName = (p.toolName as string) ?? state.toolName;\n\n if (!state.started) {\n state.started = true;\n const startEvent: ToolCallStartEvent = {\n type: EventType.TOOL_CALL_START,\n parentMessageId: messageId,\n toolCallId,\n toolCallName: p.toolName as string,\n };\n yield startEvent;\n }\n\n if (!state.hasArgsDelta && \"input\" in p && p.input !== undefined) {\n let serializedInput = \"\";\n if (typeof p.input === \"string\") {\n serializedInput = p.input;\n } else {\n try {\n serializedInput = JSON.stringify(p.input);\n } catch {\n serializedInput = String(p.input);\n }\n }\n\n if (serializedInput.length > 0) {\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: serializedInput,\n };\n yield argsEvent;\n state.hasArgsDelta = true;\n }\n }\n\n if (!state.ended) {\n state.ended = true;\n const endEvent: ToolCallEndEvent = {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n };\n yield endEvent;\n }\n break;\n }\n\n case \"tool-result\": {\n // AI SDK tool-result uses \"output\"; older versions used \"result\" — check both\n const toolResult =\n \"output\" in p ? p.output : \"result\" in p ? p.result : null;\n const toolName = \"toolName\" in p ? (p.toolName as string) : \"\";\n toolCallStates.delete(p.toolCallId as string);\n\n // Check if this is a state update tool\n if (\n toolName === \"AGUISendStateSnapshot\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const snapshot = (toolResult as Record<string, unknown>).snapshot;\n if (snapshot !== undefined) {\n const stateSnapshotEvent: StateSnapshotEvent = {\n type: EventType.STATE_SNAPSHOT,\n snapshot,\n };\n yield stateSnapshotEvent;\n }\n } else if (\n toolName === \"AGUISendStateDelta\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const delta = (toolResult as Record<string, unknown>).delta;\n if (delta !== undefined) {\n const stateDeltaEvent: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n delta,\n };\n yield stateDeltaEvent;\n }\n }\n\n // Always emit the tool result event for the LLM\n let serializedResult: string;\n try {\n serializedResult = JSON.stringify(toolResult);\n } catch {\n serializedResult = `[Unserializable tool result from ${toolName || \"unknown tool\"}]`;\n }\n const resultEvent: ToolCallResultEvent = {\n type: EventType.TOOL_CALL_RESULT,\n role: \"tool\",\n messageId: randomUUID(),\n toolCallId: p.toolCallId as string,\n content: serializedResult,\n };\n yield resultEvent;\n break;\n }\n\n case \"finish\": {\n // Terminal — let the caller handle lifecycle\n return;\n }\n\n case \"error\": {\n if (abortSignal.aborted) {\n return;\n }\n // Re-throw so the caller can emit RUN_ERROR\n const err = p.error ?? p.message ?? p.cause;\n if (err instanceof Error) throw err;\n throw new Error(\n typeof err === \"string\"\n ? err\n : `AI SDK stream error: ${JSON.stringify(p)}`,\n );\n }\n\n default:\n // Unknown event types are silently ignored\n break;\n }\n }\n } finally {\n // Always close reasoning on exit (normal or exceptional)\n yield* closeReasoningIfOpen();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseEvent } from "@ag-ui/client";
|
|
3
|
+
|
|
4
|
+
//#region src/agent/converters/aisdk.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.
|
|
7
|
+
*
|
|
8
|
+
* This is a pure converter — it does NOT emit lifecycle events
|
|
9
|
+
* (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)
|
|
10
|
+
* is responsible for those.
|
|
11
|
+
*
|
|
12
|
+
* Terminal stream events (finish, error, abort) cause the generator to
|
|
13
|
+
* return so the caller can handle lifecycle appropriately.
|
|
14
|
+
*/
|
|
15
|
+
declare function convertAISDKStream(fullStream: AsyncIterable<unknown>, abortSignal: AbortSignal): AsyncGenerator<BaseEvent>;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { convertAISDKStream };
|
|
18
|
+
//# sourceMappingURL=aisdk.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aisdk.d.cts","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"mappings":";;;;;;AA4BA;;;;;;;;iBAAuB,kBAAA,CACrB,UAAA,EAAY,aAAA,WACZ,WAAA,EAAa,WAAA,GACZ,cAAA,CAAe,SAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { BaseEvent } from "@ag-ui/client";
|
|
3
|
+
|
|
4
|
+
//#region src/agent/converters/aisdk.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.
|
|
7
|
+
*
|
|
8
|
+
* This is a pure converter — it does NOT emit lifecycle events
|
|
9
|
+
* (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)
|
|
10
|
+
* is responsible for those.
|
|
11
|
+
*
|
|
12
|
+
* Terminal stream events (finish, error, abort) cause the generator to
|
|
13
|
+
* return so the caller can handle lifecycle appropriately.
|
|
14
|
+
*/
|
|
15
|
+
declare function convertAISDKStream(fullStream: AsyncIterable<unknown>, abortSignal: AbortSignal): AsyncGenerator<BaseEvent>;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { convertAISDKStream };
|
|
18
|
+
//# sourceMappingURL=aisdk.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aisdk.d.mts","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"mappings":";;;;;;AA4BA;;;;;;;;iBAAuB,kBAAA,CACrB,UAAA,EAAY,aAAA,WACZ,WAAA,EAAa,WAAA,GACZ,cAAA,CAAe,SAAA"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { randomUUID } from "@copilotkit/shared";
|
|
3
|
+
import { EventType } from "@ag-ui/client";
|
|
4
|
+
|
|
5
|
+
//#region src/agent/converters/aisdk.ts
|
|
6
|
+
/**
|
|
7
|
+
* Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.
|
|
8
|
+
*
|
|
9
|
+
* This is a pure converter — it does NOT emit lifecycle events
|
|
10
|
+
* (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)
|
|
11
|
+
* is responsible for those.
|
|
12
|
+
*
|
|
13
|
+
* Terminal stream events (finish, error, abort) cause the generator to
|
|
14
|
+
* return so the caller can handle lifecycle appropriately.
|
|
15
|
+
*/
|
|
16
|
+
async function* convertAISDKStream(fullStream, abortSignal) {
|
|
17
|
+
let messageId = randomUUID();
|
|
18
|
+
let reasoningMessageId = randomUUID();
|
|
19
|
+
let isInReasoning = false;
|
|
20
|
+
const toolCallStates = /* @__PURE__ */ new Map();
|
|
21
|
+
const ensureToolCallState = (toolCallId) => {
|
|
22
|
+
let state = toolCallStates.get(toolCallId);
|
|
23
|
+
if (!state) {
|
|
24
|
+
state = {
|
|
25
|
+
started: false,
|
|
26
|
+
hasArgsDelta: false,
|
|
27
|
+
ended: false
|
|
28
|
+
};
|
|
29
|
+
toolCallStates.set(toolCallId, state);
|
|
30
|
+
}
|
|
31
|
+
return state;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Auto-close an open reasoning lifecycle.
|
|
35
|
+
* Some AI SDK providers (notably @ai-sdk/anthropic) never emit "reasoning-end",
|
|
36
|
+
* which leaves downstream state machines stuck. This helper emits the
|
|
37
|
+
* missing REASONING_MESSAGE_END + REASONING_END events so the stream
|
|
38
|
+
* can transition to text, tool-call, or finish phases.
|
|
39
|
+
*/
|
|
40
|
+
function* closeReasoningIfOpen() {
|
|
41
|
+
if (!isInReasoning) return;
|
|
42
|
+
isInReasoning = false;
|
|
43
|
+
yield {
|
|
44
|
+
type: EventType.REASONING_MESSAGE_END,
|
|
45
|
+
messageId: reasoningMessageId
|
|
46
|
+
};
|
|
47
|
+
yield {
|
|
48
|
+
type: EventType.REASONING_END,
|
|
49
|
+
messageId: reasoningMessageId
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
for await (const part of fullStream) {
|
|
54
|
+
const p = part;
|
|
55
|
+
if (p.type !== "reasoning-delta") yield* closeReasoningIfOpen();
|
|
56
|
+
switch (p.type) {
|
|
57
|
+
case "abort": return;
|
|
58
|
+
case "reasoning-start": {
|
|
59
|
+
const providedId = "id" in p ? p.id : void 0;
|
|
60
|
+
reasoningMessageId = providedId && providedId !== "0" ? providedId : randomUUID();
|
|
61
|
+
yield {
|
|
62
|
+
type: EventType.REASONING_START,
|
|
63
|
+
messageId: reasoningMessageId
|
|
64
|
+
};
|
|
65
|
+
yield {
|
|
66
|
+
type: EventType.REASONING_MESSAGE_START,
|
|
67
|
+
messageId: reasoningMessageId,
|
|
68
|
+
role: "reasoning"
|
|
69
|
+
};
|
|
70
|
+
isInReasoning = true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "reasoning-delta": {
|
|
74
|
+
const delta = p.text ?? "";
|
|
75
|
+
if (!delta) break;
|
|
76
|
+
yield {
|
|
77
|
+
type: EventType.REASONING_MESSAGE_CONTENT,
|
|
78
|
+
messageId: reasoningMessageId,
|
|
79
|
+
delta
|
|
80
|
+
};
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "reasoning-end": break;
|
|
84
|
+
case "tool-input-start": {
|
|
85
|
+
const toolCallId = p.id;
|
|
86
|
+
const state = ensureToolCallState(toolCallId);
|
|
87
|
+
state.toolName = p.toolName;
|
|
88
|
+
if (!state.started) {
|
|
89
|
+
state.started = true;
|
|
90
|
+
yield {
|
|
91
|
+
type: EventType.TOOL_CALL_START,
|
|
92
|
+
parentMessageId: messageId,
|
|
93
|
+
toolCallId,
|
|
94
|
+
toolCallName: p.toolName
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "tool-input-delta": {
|
|
100
|
+
const toolCallId = p.id;
|
|
101
|
+
const state = ensureToolCallState(toolCallId);
|
|
102
|
+
state.hasArgsDelta = true;
|
|
103
|
+
yield {
|
|
104
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
105
|
+
toolCallId,
|
|
106
|
+
delta: p.delta
|
|
107
|
+
};
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case "tool-input-end": break;
|
|
111
|
+
case "text-start": {
|
|
112
|
+
const providedId = "id" in p ? p.id : void 0;
|
|
113
|
+
messageId = providedId && providedId !== "0" ? providedId : randomUUID();
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case "text-delta": {
|
|
117
|
+
const textDelta = "text" in p ? p.text : "";
|
|
118
|
+
yield {
|
|
119
|
+
type: EventType.TEXT_MESSAGE_CHUNK,
|
|
120
|
+
role: "assistant",
|
|
121
|
+
messageId,
|
|
122
|
+
delta: textDelta
|
|
123
|
+
};
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case "tool-call": {
|
|
127
|
+
const toolCallId = p.toolCallId;
|
|
128
|
+
const state = ensureToolCallState(toolCallId);
|
|
129
|
+
state.toolName = p.toolName ?? state.toolName;
|
|
130
|
+
if (!state.started) {
|
|
131
|
+
state.started = true;
|
|
132
|
+
yield {
|
|
133
|
+
type: EventType.TOOL_CALL_START,
|
|
134
|
+
parentMessageId: messageId,
|
|
135
|
+
toolCallId,
|
|
136
|
+
toolCallName: p.toolName
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (!state.hasArgsDelta && "input" in p && p.input !== void 0) {
|
|
140
|
+
let serializedInput = "";
|
|
141
|
+
if (typeof p.input === "string") serializedInput = p.input;
|
|
142
|
+
else try {
|
|
143
|
+
serializedInput = JSON.stringify(p.input);
|
|
144
|
+
} catch {
|
|
145
|
+
serializedInput = String(p.input);
|
|
146
|
+
}
|
|
147
|
+
if (serializedInput.length > 0) {
|
|
148
|
+
yield {
|
|
149
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
150
|
+
toolCallId,
|
|
151
|
+
delta: serializedInput
|
|
152
|
+
};
|
|
153
|
+
state.hasArgsDelta = true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (!state.ended) {
|
|
157
|
+
state.ended = true;
|
|
158
|
+
yield {
|
|
159
|
+
type: EventType.TOOL_CALL_END,
|
|
160
|
+
toolCallId
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case "tool-result": {
|
|
166
|
+
const toolResult = "output" in p ? p.output : "result" in p ? p.result : null;
|
|
167
|
+
const toolName = "toolName" in p ? p.toolName : "";
|
|
168
|
+
toolCallStates.delete(p.toolCallId);
|
|
169
|
+
if (toolName === "AGUISendStateSnapshot" && toolResult && typeof toolResult === "object") {
|
|
170
|
+
const snapshot = toolResult.snapshot;
|
|
171
|
+
if (snapshot !== void 0) yield {
|
|
172
|
+
type: EventType.STATE_SNAPSHOT,
|
|
173
|
+
snapshot
|
|
174
|
+
};
|
|
175
|
+
} else if (toolName === "AGUISendStateDelta" && toolResult && typeof toolResult === "object") {
|
|
176
|
+
const delta = toolResult.delta;
|
|
177
|
+
if (delta !== void 0) yield {
|
|
178
|
+
type: EventType.STATE_DELTA,
|
|
179
|
+
delta
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
let serializedResult;
|
|
183
|
+
try {
|
|
184
|
+
serializedResult = JSON.stringify(toolResult);
|
|
185
|
+
} catch {
|
|
186
|
+
serializedResult = `[Unserializable tool result from ${toolName || "unknown tool"}]`;
|
|
187
|
+
}
|
|
188
|
+
yield {
|
|
189
|
+
type: EventType.TOOL_CALL_RESULT,
|
|
190
|
+
role: "tool",
|
|
191
|
+
messageId: randomUUID(),
|
|
192
|
+
toolCallId: p.toolCallId,
|
|
193
|
+
content: serializedResult
|
|
194
|
+
};
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case "finish": return;
|
|
198
|
+
case "error": {
|
|
199
|
+
if (abortSignal.aborted) return;
|
|
200
|
+
const err = p.error ?? p.message ?? p.cause;
|
|
201
|
+
if (err instanceof Error) throw err;
|
|
202
|
+
throw new Error(typeof err === "string" ? err : `AI SDK stream error: ${JSON.stringify(p)}`);
|
|
203
|
+
}
|
|
204
|
+
default: break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} finally {
|
|
208
|
+
yield* closeReasoningIfOpen();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
export { convertAISDKStream };
|
|
214
|
+
//# sourceMappingURL=aisdk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aisdk.mjs","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"sourcesContent":["import {\n BaseEvent,\n EventType,\n ReasoningEndEvent,\n ReasoningMessageContentEvent,\n ReasoningMessageEndEvent,\n ReasoningMessageStartEvent,\n ReasoningStartEvent,\n TextMessageChunkEvent,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolCallResultEvent,\n StateSnapshotEvent,\n StateDeltaEvent,\n} from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\n\n/**\n * Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.\n *\n * This is a pure converter — it does NOT emit lifecycle events\n * (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)\n * is responsible for those.\n *\n * Terminal stream events (finish, error, abort) cause the generator to\n * return so the caller can handle lifecycle appropriately.\n */\nexport async function* convertAISDKStream(\n fullStream: AsyncIterable<unknown>,\n abortSignal: AbortSignal,\n): AsyncGenerator<BaseEvent> {\n let messageId = randomUUID();\n let reasoningMessageId = randomUUID();\n let isInReasoning = false;\n\n const toolCallStates = new Map<\n string,\n {\n started: boolean;\n hasArgsDelta: boolean;\n ended: boolean;\n toolName?: string;\n }\n >();\n\n const ensureToolCallState = (toolCallId: string) => {\n let state = toolCallStates.get(toolCallId);\n if (!state) {\n state = { started: false, hasArgsDelta: false, ended: false };\n toolCallStates.set(toolCallId, state);\n }\n return state;\n };\n\n /**\n * Auto-close an open reasoning lifecycle.\n * Some AI SDK providers (notably @ai-sdk/anthropic) never emit \"reasoning-end\",\n * which leaves downstream state machines stuck. This helper emits the\n * missing REASONING_MESSAGE_END + REASONING_END events so the stream\n * can transition to text, tool-call, or finish phases.\n */\n function* closeReasoningIfOpen(): Generator<BaseEvent> {\n if (!isInReasoning) return;\n isInReasoning = false;\n const reasoningMsgEnd: ReasoningMessageEndEvent = {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n };\n yield reasoningMsgEnd;\n const reasoningEnd: ReasoningEndEvent = {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n };\n yield reasoningEnd;\n }\n\n try {\n for await (const part of fullStream) {\n const p = part as Record<string, unknown>;\n\n // Close any open reasoning lifecycle on every event except\n // reasoning-delta, which arrives mid-block and must not interrupt it.\n if (p.type !== \"reasoning-delta\") {\n yield* closeReasoningIfOpen();\n }\n\n switch (p.type) {\n case \"abort\": {\n // Terminal — let the caller handle lifecycle\n return;\n }\n\n case \"reasoning-start\": {\n // Use SDK-provided id, or generate a fresh UUID if id is falsy/\"0\"\n // to prevent consecutive reasoning blocks from sharing a messageId\n const providedId = \"id\" in p ? p.id : undefined;\n reasoningMessageId =\n providedId && providedId !== \"0\"\n ? (providedId as string)\n : randomUUID();\n const reasoningStartEvent: ReasoningStartEvent = {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n };\n yield reasoningStartEvent;\n const reasoningMessageStart: ReasoningMessageStartEvent = {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: \"reasoning\",\n };\n yield reasoningMessageStart;\n isInReasoning = true;\n break;\n }\n\n case \"reasoning-delta\": {\n const delta = (p.text as string) ?? \"\";\n if (!delta) break; // skip — @ag-ui/core schema requires delta to be non-empty\n const reasoningDeltaEvent: ReasoningMessageContentEvent = {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta,\n };\n yield reasoningDeltaEvent;\n break;\n }\n\n case \"reasoning-end\": {\n // closeReasoningIfOpen() already called before the switch — no-op here\n // if the SDK never emits this event (e.g. @ai-sdk/anthropic).\n break;\n }\n\n case \"tool-input-start\": {\n const toolCallId = p.id as string;\n const state = ensureToolCallState(toolCallId);\n state.toolName = p.toolName as string;\n if (!state.started) {\n state.started = true;\n const startEvent: ToolCallStartEvent = {\n type: EventType.TOOL_CALL_START,\n parentMessageId: messageId,\n toolCallId,\n toolCallName: p.toolName as string,\n };\n yield startEvent;\n }\n break;\n }\n\n case \"tool-input-delta\": {\n const toolCallId = p.id as string;\n const state = ensureToolCallState(toolCallId);\n state.hasArgsDelta = true;\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: p.delta as string,\n };\n yield argsEvent;\n break;\n }\n\n case \"tool-input-end\": {\n // No direct event – the subsequent \"tool-call\" part marks completion.\n break;\n }\n\n case \"text-start\": {\n // New text message starting - use the SDK-provided id\n // Use randomUUID() if part.id is falsy or \"0\" to prevent message merging issues\n const providedId = \"id\" in p ? p.id : undefined;\n messageId =\n providedId && providedId !== \"0\"\n ? (providedId as string)\n : randomUUID();\n break;\n }\n\n case \"text-delta\": {\n // AI SDK text-delta events use 'text' (not 'delta')\n const textDelta = \"text\" in p ? (p.text as string) : \"\";\n const textEvent: TextMessageChunkEvent = {\n type: EventType.TEXT_MESSAGE_CHUNK,\n role: \"assistant\",\n messageId,\n delta: textDelta,\n };\n yield textEvent;\n break;\n }\n\n case \"tool-call\": {\n const toolCallId = p.toolCallId as string;\n const state = ensureToolCallState(toolCallId);\n state.toolName = (p.toolName as string) ?? state.toolName;\n\n if (!state.started) {\n state.started = true;\n const startEvent: ToolCallStartEvent = {\n type: EventType.TOOL_CALL_START,\n parentMessageId: messageId,\n toolCallId,\n toolCallName: p.toolName as string,\n };\n yield startEvent;\n }\n\n if (!state.hasArgsDelta && \"input\" in p && p.input !== undefined) {\n let serializedInput = \"\";\n if (typeof p.input === \"string\") {\n serializedInput = p.input;\n } else {\n try {\n serializedInput = JSON.stringify(p.input);\n } catch {\n serializedInput = String(p.input);\n }\n }\n\n if (serializedInput.length > 0) {\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: serializedInput,\n };\n yield argsEvent;\n state.hasArgsDelta = true;\n }\n }\n\n if (!state.ended) {\n state.ended = true;\n const endEvent: ToolCallEndEvent = {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n };\n yield endEvent;\n }\n break;\n }\n\n case \"tool-result\": {\n // AI SDK tool-result uses \"output\"; older versions used \"result\" — check both\n const toolResult =\n \"output\" in p ? p.output : \"result\" in p ? p.result : null;\n const toolName = \"toolName\" in p ? (p.toolName as string) : \"\";\n toolCallStates.delete(p.toolCallId as string);\n\n // Check if this is a state update tool\n if (\n toolName === \"AGUISendStateSnapshot\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const snapshot = (toolResult as Record<string, unknown>).snapshot;\n if (snapshot !== undefined) {\n const stateSnapshotEvent: StateSnapshotEvent = {\n type: EventType.STATE_SNAPSHOT,\n snapshot,\n };\n yield stateSnapshotEvent;\n }\n } else if (\n toolName === \"AGUISendStateDelta\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const delta = (toolResult as Record<string, unknown>).delta;\n if (delta !== undefined) {\n const stateDeltaEvent: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n delta,\n };\n yield stateDeltaEvent;\n }\n }\n\n // Always emit the tool result event for the LLM\n let serializedResult: string;\n try {\n serializedResult = JSON.stringify(toolResult);\n } catch {\n serializedResult = `[Unserializable tool result from ${toolName || \"unknown tool\"}]`;\n }\n const resultEvent: ToolCallResultEvent = {\n type: EventType.TOOL_CALL_RESULT,\n role: \"tool\",\n messageId: randomUUID(),\n toolCallId: p.toolCallId as string,\n content: serializedResult,\n };\n yield resultEvent;\n break;\n }\n\n case \"finish\": {\n // Terminal — let the caller handle lifecycle\n return;\n }\n\n case \"error\": {\n if (abortSignal.aborted) {\n return;\n }\n // Re-throw so the caller can emit RUN_ERROR\n const err = p.error ?? p.message ?? p.cause;\n if (err instanceof Error) throw err;\n throw new Error(\n typeof err === \"string\"\n ? err\n : `AI SDK stream error: ${JSON.stringify(p)}`,\n );\n }\n\n default:\n // Unknown event types are silently ignored\n break;\n }\n }\n } finally {\n // Always close reasoning on exit (normal or exceptional)\n yield* closeReasoningIfOpen();\n }\n}\n"],"mappings":"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"}
|