@copilotkit/runtime 1.75.1 → 1.76.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/agent/converters/aisdk.cjs +68 -10
- package/dist/agent/converters/aisdk.cjs.map +1 -1
- package/dist/agent/converters/aisdk.d.cts.map +1 -1
- package/dist/agent/converters/aisdk.d.mts.map +1 -1
- package/dist/agent/converters/aisdk.mjs +67 -11
- package/dist/agent/converters/aisdk.mjs.map +1 -1
- package/dist/agent/converters/tanstack.cjs +8 -6
- package/dist/agent/converters/tanstack.cjs.map +1 -1
- package/dist/agent/converters/tanstack.d.cts.map +1 -1
- package/dist/agent/converters/tanstack.d.mts.map +1 -1
- package/dist/agent/converters/tanstack.mjs +9 -7
- package/dist/agent/converters/tanstack.mjs.map +1 -1
- package/dist/agent/converters/usage.cjs +8 -2
- package/dist/agent/converters/usage.cjs.map +1 -1
- package/dist/agent/converters/usage.d.cts +3 -1
- package/dist/agent/converters/usage.d.cts.map +1 -1
- package/dist/agent/converters/usage.d.mts +3 -1
- package/dist/agent/converters/usage.d.mts.map +1 -1
- package/dist/agent/converters/usage.mjs +8 -2
- package/dist/agent/converters/usage.mjs.map +1 -1
- package/dist/agent/index.cjs +164 -34
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +18 -6
- package/dist/agent/index.d.cts.map +1 -1
- package/dist/agent/index.d.mts +18 -6
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/agent/index.mjs +166 -36
- package/dist/agent/index.mjs.map +1 -1
- package/dist/agent/mcp-tool-names.cjs +84 -0
- package/dist/agent/mcp-tool-names.cjs.map +1 -0
- package/dist/agent/mcp-tool-names.mjs +81 -0
- package/dist/agent/mcp-tool-names.mjs.map +1 -0
- package/dist/lib/runtime/copilot-runtime.cjs +20 -9
- 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 +20 -9
- package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
- package/dist/package.cjs +6 -7
- package/dist/package.mjs +6 -7
- package/dist/v2/runtime/handlers/intelligence/connect.cjs +1 -0
- package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.mjs +1 -0
- package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs +90 -4
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs +90 -4
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
- package/dist/v2/runtime/runner/in-memory.cjs +16 -1
- package/dist/v2/runtime/runner/in-memory.cjs.map +1 -1
- package/dist/v2/runtime/runner/in-memory.d.cts.map +1 -1
- package/dist/v2/runtime/runner/in-memory.d.mts.map +1 -1
- package/dist/v2/runtime/runner/in-memory.mjs +16 -1
- package/dist/v2/runtime/runner/in-memory.mjs.map +1 -1
- package/dist/v2/runtime/runner/intelligence.cjs +5 -2
- package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.mjs +6 -3
- package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
- package/package.json +7 -10
|
@@ -12,7 +12,7 @@ let _ag_ui_client = require("@ag-ui/client");
|
|
|
12
12
|
*/
|
|
13
13
|
function getAISDKRunFinishedDetails(part, identity = {}) {
|
|
14
14
|
const details = {};
|
|
15
|
-
if (typeof part.finishReason === "string") details.
|
|
15
|
+
if (typeof part.finishReason === "string") details.metadata = { finishReason: part.finishReason };
|
|
16
16
|
if (part.totalUsage === null || typeof part.totalUsage !== "object" || Array.isArray(part.totalUsage)) return details;
|
|
17
17
|
const totalUsage = part.totalUsage;
|
|
18
18
|
const counts = {};
|
|
@@ -26,6 +26,33 @@ function getAISDKRunFinishedDetails(part, identity = {}) {
|
|
|
26
26
|
}]);
|
|
27
27
|
return details;
|
|
28
28
|
}
|
|
29
|
+
function formatToolError(error) {
|
|
30
|
+
if (error instanceof Error) return error.message;
|
|
31
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
32
|
+
if (typeof error === "string") return error;
|
|
33
|
+
if (error === void 0 || error === null) return "Unknown tool error";
|
|
34
|
+
try {
|
|
35
|
+
const serialized = JSON.stringify(error);
|
|
36
|
+
return serialized === void 0 ? String(error) : serialized;
|
|
37
|
+
} catch {
|
|
38
|
+
return String(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns the AG-UI message id for an AI SDK `text-start` / `reasoning-start`
|
|
43
|
+
* part.
|
|
44
|
+
*
|
|
45
|
+
* Several providers number stream parts per response instead of giving them
|
|
46
|
+
* a unique id: @ai-sdk/anthropic and @ai-sdk/google use a block counter
|
|
47
|
+
* ("0", "1", ...) and @ai-sdk/openai-compatible always uses "txt-0" /
|
|
48
|
+
* "reasoning-0". Those ids repeat on every step and every run, so reusing
|
|
49
|
+
* them would append later replies to an earlier message. Only ids that look
|
|
50
|
+
* unique are kept.
|
|
51
|
+
*/
|
|
52
|
+
function resolveStreamPartMessageId(providedId) {
|
|
53
|
+
if (typeof providedId !== "string" || providedId === "" || /^(\d+|(txt|reasoning|msg)-0)$/.test(providedId)) return (0, _copilotkit_shared.randomUUID)();
|
|
54
|
+
return providedId;
|
|
55
|
+
}
|
|
29
56
|
/**
|
|
30
57
|
* Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.
|
|
31
58
|
*
|
|
@@ -78,14 +105,14 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
78
105
|
};
|
|
79
106
|
}
|
|
80
107
|
try {
|
|
108
|
+
const warnedUnknownPartTypes = /* @__PURE__ */ new Set();
|
|
81
109
|
for await (const part of fullStream) {
|
|
82
110
|
const p = part;
|
|
83
111
|
if (p.type !== "reasoning-delta") yield* closeReasoningIfOpen();
|
|
84
112
|
switch (p.type) {
|
|
85
113
|
case "abort": return;
|
|
86
|
-
case "reasoning-start":
|
|
87
|
-
|
|
88
|
-
reasoningMessageId = providedId && providedId !== "0" ? providedId : (0, _copilotkit_shared.randomUUID)();
|
|
114
|
+
case "reasoning-start":
|
|
115
|
+
reasoningMessageId = resolveStreamPartMessageId("id" in p ? p.id : void 0);
|
|
89
116
|
yield {
|
|
90
117
|
type: _ag_ui_client.EventType.REASONING_START,
|
|
91
118
|
messageId: reasoningMessageId
|
|
@@ -97,7 +124,6 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
97
124
|
};
|
|
98
125
|
isInReasoning = true;
|
|
99
126
|
break;
|
|
100
|
-
}
|
|
101
127
|
case "reasoning-delta": {
|
|
102
128
|
const delta = p.text ?? "";
|
|
103
129
|
if (!delta) break;
|
|
@@ -136,11 +162,9 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
136
162
|
break;
|
|
137
163
|
}
|
|
138
164
|
case "tool-input-end": break;
|
|
139
|
-
case "text-start":
|
|
140
|
-
|
|
141
|
-
messageId = providedId && providedId !== "0" ? providedId : (0, _copilotkit_shared.randomUUID)();
|
|
165
|
+
case "text-start":
|
|
166
|
+
messageId = resolveStreamPartMessageId("id" in p ? p.id : void 0);
|
|
142
167
|
break;
|
|
143
|
-
}
|
|
144
168
|
case "text-delta": {
|
|
145
169
|
const textDelta = "text" in p ? p.text : "";
|
|
146
170
|
yield {
|
|
@@ -237,6 +261,23 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
237
261
|
});
|
|
238
262
|
break;
|
|
239
263
|
}
|
|
264
|
+
case "tool-error": {
|
|
265
|
+
const toolCallId = p.toolCallId;
|
|
266
|
+
if (!toolCallId) throw new Error("AI SDK tool-error is missing toolCallId");
|
|
267
|
+
if (pendingInterrupts?.some((interrupt) => interrupt.toolCallId === toolCallId)) {
|
|
268
|
+
toolCallStates.delete(toolCallId);
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
toolCallStates.delete(toolCallId);
|
|
272
|
+
yield {
|
|
273
|
+
type: _ag_ui_client.EventType.TOOL_CALL_RESULT,
|
|
274
|
+
role: "tool",
|
|
275
|
+
messageId: (0, _copilotkit_shared.randomUUID)(),
|
|
276
|
+
toolCallId,
|
|
277
|
+
content: `Error: ${formatToolError(p.error)}`
|
|
278
|
+
};
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
240
281
|
case "tool-result": {
|
|
241
282
|
const toolResult = "output" in p ? p.output : "result" in p ? p.result : null;
|
|
242
283
|
const toolName = "toolName" in p ? p.toolName : "";
|
|
@@ -284,7 +325,22 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
284
325
|
if (err instanceof Error) throw err;
|
|
285
326
|
throw new Error(typeof err === "string" ? err : `AI SDK stream error: ${JSON.stringify(p)}`);
|
|
286
327
|
}
|
|
287
|
-
|
|
328
|
+
case "start":
|
|
329
|
+
case "start-step":
|
|
330
|
+
case "finish-step":
|
|
331
|
+
case "text-end":
|
|
332
|
+
case "source":
|
|
333
|
+
case "file":
|
|
334
|
+
case "tool-output-denied":
|
|
335
|
+
case "raw": break;
|
|
336
|
+
default: {
|
|
337
|
+
const unknownType = String(p.type);
|
|
338
|
+
if (!warnedUnknownPartTypes.has(unknownType)) {
|
|
339
|
+
warnedUnknownPartTypes.add(unknownType);
|
|
340
|
+
console.warn(`[convertAISDKStream] Ignoring unhandled AI SDK stream part: ${unknownType}`);
|
|
341
|
+
}
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
288
344
|
}
|
|
289
345
|
}
|
|
290
346
|
} finally {
|
|
@@ -294,5 +350,7 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
294
350
|
|
|
295
351
|
//#endregion
|
|
296
352
|
exports.convertAISDKStream = convertAISDKStream;
|
|
353
|
+
exports.formatToolError = formatToolError;
|
|
297
354
|
exports.getAISDKRunFinishedDetails = getAISDKRunFinishedDetails;
|
|
355
|
+
exports.resolveStreamPartMessageId = resolveStreamPartMessageId;
|
|
298
356
|
//# sourceMappingURL=aisdk.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aisdk.cjs","names":["tokenCountKeys","getTokenCount","createStateEventNormalizer","EventType"],"sources":["../../../src/agent/converters/aisdk.ts"],"sourcesContent":["import type {\n BaseEvent,\n Interrupt,\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 { EventType } from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { createStateEventNormalizer } from \"../state-delta\";\nimport { aggregateRunUsage, getTokenCount, tokenCountKeys } from \"./usage\";\nimport type { AgentRunFinishedDetails, AgentRunUsage } from \"./usage\";\n\n/**\n * Reads aggregate usage from an AI SDK finish part without inventing values\n * when a provider omits a token count.\n */\nexport function getAISDKRunFinishedDetails(\n part: Record<string, unknown>,\n identity: { provider?: string; model?: string } = {},\n): AgentRunFinishedDetails {\n const details: AgentRunFinishedDetails = {};\n\n if (typeof part.finishReason === \"string\") {\n details.finishReason = part.finishReason;\n }\n\n if (\n part.totalUsage === null ||\n typeof part.totalUsage !== \"object\" ||\n Array.isArray(part.totalUsage)\n ) {\n return details;\n }\n\n const totalUsage = part.totalUsage as Record<string, unknown>;\n const counts: AgentRunUsage = {};\n\n for (const key of tokenCountKeys) {\n const value = getTokenCount(totalUsage[key]);\n if (value !== undefined) {\n counts[key] = value;\n }\n }\n\n aggregateRunUsage(details, [{ ...identity, ...counts }]);\n\n return details;\n}\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 *\n * `pendingInterrupts`, when provided, is filled with one AG-UI Interrupt per\n * AI SDK `tool-approval-request` part (a tool declared `needsApproval: true`).\n * The caller turns a non-empty array into a RUN_FINISHED `outcome:interrupt`.\n */\nexport async function* convertAISDKStream(\n fullStream: AsyncIterable<unknown>,\n abortSignal: AbortSignal,\n pendingInterrupts?: Interrupt[],\n initialState?: unknown,\n runFinishedDetails?: AgentRunFinishedDetails,\n): AsyncGenerator<BaseEvent> {\n let messageId = randomUUID();\n let reasoningMessageId = randomUUID();\n let isInReasoning = false;\n const normalizeStateEvent = createStateEventNormalizer(initialState);\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-approval-request\": {\n // AI SDK native human-in-the-loop: a tool declared `needsApproval`\n // was called. The preceding \"tool-call\" part already streamed the\n // tool name + args; this part carries the toolCallId to pause on.\n // (Some shapes nest the call under `toolCall` — read both.)\n const nested = (p.toolCall ?? {}) as {\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n };\n const toolCallId =\n (p.toolCallId as string | undefined) ?? nested.toolCallId;\n if (!toolCallId) {\n throw new Error(\n \"AI SDK tool-approval-request is missing toolCallId\",\n );\n }\n const state = ensureToolCallState(toolCallId);\n const toolName = state.toolName ?? nested.toolName;\n\n // Defensive: emit the tool-call lifecycle if the \"tool-call\" part\n // didn't precede this one, so the assistant tool-call is in history\n // for the resume run (which keys the injected result by toolCallId).\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: toolName ?? \"\",\n };\n yield startEvent;\n }\n if (!state.hasArgsDelta && nested.input !== undefined) {\n let serialized = \"\";\n try {\n serialized =\n typeof nested.input === \"string\"\n ? nested.input\n : JSON.stringify(nested.input);\n } catch {\n serialized = String(nested.input);\n }\n if (serialized.length > 0) {\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: serialized,\n };\n yield argsEvent;\n state.hasArgsDelta = true;\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\n pendingInterrupts?.push({\n id: toolCallId,\n toolCallId,\n reason: \"tool_approval\",\n message: toolName ? `Approve \"${toolName}\"?` : undefined,\n ...(toolName ? { metadata: { toolName } } : {}),\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 for (const event of normalizeStateEvent(stateSnapshotEvent)) {\n yield event;\n }\n }\n } else if (\n toolName === \"AGUISendStateDelta\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const delta = (toolResult as { delta?: StateDeltaEvent[\"delta\"] })\n .delta;\n if (delta !== undefined) {\n const stateDeltaEvent: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n delta,\n };\n for (const event of normalizeStateEvent(stateDeltaEvent)) {\n yield event;\n }\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 if (runFinishedDetails) {\n Object.assign(runFinishedDetails, getAISDKRunFinishedDetails(p));\n }\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":";;;;;;;;;;;;AA0BA,SAAgB,2BACd,MACA,WAAkD,EAAE,EAC3B;CACzB,MAAM,UAAmC,EAAE;AAE3C,KAAI,OAAO,KAAK,iBAAiB,SAC/B,SAAQ,eAAe,KAAK;AAG9B,KACE,KAAK,eAAe,QACpB,OAAO,KAAK,eAAe,YAC3B,MAAM,QAAQ,KAAK,WAAW,CAE9B,QAAO;CAGT,MAAM,aAAa,KAAK;CACxB,MAAM,SAAwB,EAAE;AAEhC,MAAK,MAAM,OAAOA,8BAAgB;EAChC,MAAM,QAAQC,4BAAc,WAAW,KAAK;AAC5C,MAAI,UAAU,OACZ,QAAO,OAAO;;AAIlB,iCAAkB,SAAS,CAAC;EAAE,GAAG;EAAU,GAAG;EAAQ,CAAC,CAAC;AAExD,QAAO;;;;;;;;;;;;;;;;AAiBT,gBAAuB,mBACrB,YACA,aACA,mBACA,cACA,oBAC2B;CAC3B,IAAI,gDAAwB;CAC5B,IAAI,yDAAiC;CACrC,IAAI,gBAAgB;CACpB,MAAM,sBAAsBC,+CAA2B,aAAa;CAEpE,MAAM,iCAAiB,IAAI,KAQxB;CAEH,MAAM,uBAAuB,eAAuB;EAClD,IAAI,QAAQ,eAAe,IAAI,WAAW;AAC1C,MAAI,CAAC,OAAO;AACV,WAAQ;IAAE,SAAS;IAAO,cAAc;IAAO,OAAO;IAAO;AAC7D,kBAAe,IAAI,YAAY,MAAM;;AAEvC,SAAO;;;;;;;;;CAUT,UAAU,uBAA6C;AACrD,MAAI,CAAC,cAAe;AACpB,kBAAgB;AAKhB,QAJkD;GAChD,MAAMC,wBAAU;GAChB,WAAW;GACZ;AAMD,QAJwC;GACtC,MAAMA,wBAAU;GAChB,WAAW;GACZ;;AAIH,KAAI;AACF,aAAW,MAAM,QAAQ,YAAY;GACnC,MAAM,IAAI;AAIV,OAAI,EAAE,SAAS,kBACb,QAAO,sBAAsB;AAG/B,WAAQ,EAAE,MAAV;IACE,KAAK,QAEH;IAGF,KAAK,mBAAmB;KAGtB,MAAM,aAAa,QAAQ,IAAI,EAAE,KAAK;AACtC,0BACE,cAAc,eAAe,MACxB,iDACW;AAKlB,WAJiD;MAC/C,MAAMA,wBAAU;MAChB,WAAW;MACZ;AAOD,WAL0D;MACxD,MAAMA,wBAAU;MAChB,WAAW;MACX,MAAM;MACP;AAED,qBAAgB;AAChB;;IAGF,KAAK,mBAAmB;KACtB,MAAM,QAAS,EAAE,QAAmB;AACpC,SAAI,CAAC,MAAO;AAMZ,WAL0D;MACxD,MAAMA,wBAAU;MAChB,WAAW;MACX;MACD;AAED;;IAGF,KAAK,gBAGH;IAGF,KAAK,oBAAoB;KACvB,MAAM,aAAa,EAAE;KACrB,MAAM,QAAQ,oBAAoB,WAAW;AAC7C,WAAM,WAAW,EAAE;AACnB,SAAI,CAAC,MAAM,SAAS;AAClB,YAAM,UAAU;AAOhB,YANuC;OACrC,MAAMA,wBAAU;OAChB,iBAAiB;OACjB;OACA,cAAc,EAAE;OACjB;;AAGH;;IAGF,KAAK,oBAAoB;KACvB,MAAM,aAAa,EAAE;KACrB,MAAM,QAAQ,oBAAoB,WAAW;AAC7C,WAAM,eAAe;AAMrB,WALqC;MACnC,MAAMA,wBAAU;MAChB;MACA,OAAO,EAAE;MACV;AAED;;IAGF,KAAK,iBAEH;IAGF,KAAK,cAAc;KAGjB,MAAM,aAAa,QAAQ,IAAI,EAAE,KAAK;AACtC,iBACE,cAAc,eAAe,MACxB,iDACW;AAClB;;IAGF,KAAK,cAAc;KAEjB,MAAM,YAAY,UAAU,IAAK,EAAE,OAAkB;AAOrD,WANyC;MACvC,MAAMA,wBAAU;MAChB,MAAM;MACN;MACA,OAAO;MACR;AAED;;IAGF,KAAK,aAAa;KAChB,MAAM,aAAa,EAAE;KACrB,MAAM,QAAQ,oBAAoB,WAAW;AAC7C,WAAM,WAAY,EAAE,YAAuB,MAAM;AAEjD,SAAI,CAAC,MAAM,SAAS;AAClB,YAAM,UAAU;AAOhB,YANuC;OACrC,MAAMA,wBAAU;OAChB,iBAAiB;OACjB;OACA,cAAc,EAAE;OACjB;;AAIH,SAAI,CAAC,MAAM,gBAAgB,WAAW,KAAK,EAAE,UAAU,QAAW;MAChE,IAAI,kBAAkB;AACtB,UAAI,OAAO,EAAE,UAAU,SACrB,mBAAkB,EAAE;UAEpB,KAAI;AACF,yBAAkB,KAAK,UAAU,EAAE,MAAM;cACnC;AACN,yBAAkB,OAAO,EAAE,MAAM;;AAIrC,UAAI,gBAAgB,SAAS,GAAG;AAM9B,aALqC;QACnC,MAAMA,wBAAU;QAChB;QACA,OAAO;QACR;AAED,aAAM,eAAe;;;AAIzB,SAAI,CAAC,MAAM,OAAO;AAChB,YAAM,QAAQ;AAKd,YAJmC;OACjC,MAAMA,wBAAU;OAChB;OACD;;AAGH;;IAGF,KAAK,yBAAyB;KAK5B,MAAM,SAAU,EAAE,YAAY,EAAE;KAKhC,MAAM,aACH,EAAE,cAAqC,OAAO;AACjD,SAAI,CAAC,WACH,OAAM,IAAI,MACR,qDACD;KAEH,MAAM,QAAQ,oBAAoB,WAAW;KAC7C,MAAM,WAAW,MAAM,YAAY,OAAO;AAK1C,SAAI,CAAC,MAAM,SAAS;AAClB,YAAM,UAAU;AAOhB,YANuC;OACrC,MAAMA,wBAAU;OAChB,iBAAiB;OACjB;OACA,cAAc,YAAY;OAC3B;;AAGH,SAAI,CAAC,MAAM,gBAAgB,OAAO,UAAU,QAAW;MACrD,IAAI,aAAa;AACjB,UAAI;AACF,oBACE,OAAO,OAAO,UAAU,WACpB,OAAO,QACP,KAAK,UAAU,OAAO,MAAM;cAC5B;AACN,oBAAa,OAAO,OAAO,MAAM;;AAEnC,UAAI,WAAW,SAAS,GAAG;AAMzB,aALqC;QACnC,MAAMA,wBAAU;QAChB;QACA,OAAO;QACR;AAED,aAAM,eAAe;;;AAGzB,SAAI,CAAC,MAAM,OAAO;AAChB,YAAM,QAAQ;AAKd,YAJmC;OACjC,MAAMA,wBAAU;OAChB;OACD;;AAIH,wBAAmB,KAAK;MACtB,IAAI;MACJ;MACA,QAAQ;MACR,SAAS,WAAW,YAAY,SAAS,MAAM;MAC/C,GAAI,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE;MAC/C,CAAC;AACF;;IAGF,KAAK,eAAe;KAElB,MAAM,aACJ,YAAY,IAAI,EAAE,SAAS,YAAY,IAAI,EAAE,SAAS;KACxD,MAAM,WAAW,cAAc,IAAK,EAAE,WAAsB;AAC5D,oBAAe,OAAO,EAAE,WAAqB;AAG7C,SACE,aAAa,2BACb,cACA,OAAO,eAAe,UACtB;MACA,MAAM,WAAY,WAAuC;AACzD,UAAI,aAAa,QAAW;OAC1B,MAAM,qBAAyC;QAC7C,MAAMA,wBAAU;QAChB;QACD;AACD,YAAK,MAAM,SAAS,oBAAoB,mBAAmB,CACzD,OAAM;;gBAIV,aAAa,wBACb,cACA,OAAO,eAAe,UACtB;MACA,MAAM,QAAS,WACZ;AACH,UAAI,UAAU,QAAW;OACvB,MAAM,kBAAmC;QACvC,MAAMA,wBAAU;QAChB;QACD;AACD,YAAK,MAAM,SAAS,oBAAoB,gBAAgB,CACtD,OAAM;;;KAMZ,IAAI;AACJ,SAAI;AACF,yBAAmB,KAAK,UAAU,WAAW;aACvC;AACN,yBAAmB,oCAAoC,YAAY,eAAe;;AASpF,WAPyC;MACvC,MAAMA,wBAAU;MAChB,MAAM;MACN,+CAAuB;MACvB,YAAY,EAAE;MACd,SAAS;MACV;AAED;;IAGF,KAAK;AACH,SAAI,mBACF,QAAO,OAAO,oBAAoB,2BAA2B,EAAE,CAAC;AAGlE;IAGF,KAAK,SAAS;AACZ,SAAI,YAAY,QACd;KAGF,MAAM,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE;AACtC,SAAI,eAAe,MAAO,OAAM;AAChC,WAAM,IAAI,MACR,OAAO,QAAQ,WACX,MACA,wBAAwB,KAAK,UAAU,EAAE,GAC9C;;IAGH,QAEE;;;WAGE;AAER,SAAO,sBAAsB"}
|
|
1
|
+
{"version":3,"file":"aisdk.cjs","names":["tokenCountKeys","getTokenCount","createStateEventNormalizer","EventType"],"sources":["../../../src/agent/converters/aisdk.ts"],"sourcesContent":["import type {\n BaseEvent,\n Interrupt,\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 { EventType } from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { createStateEventNormalizer } from \"../state-delta\";\nimport { aggregateRunUsage, getTokenCount, tokenCountKeys } from \"./usage\";\nimport type { AgentRunFinishedDetails, AgentRunUsage } from \"./usage\";\n\n/**\n * Reads aggregate usage from an AI SDK finish part without inventing values\n * when a provider omits a token count.\n */\nexport function getAISDKRunFinishedDetails(\n part: Record<string, unknown>,\n identity: { provider?: string; model?: string } = {},\n): AgentRunFinishedDetails {\n const details: AgentRunFinishedDetails = {};\n\n if (typeof part.finishReason === \"string\") {\n details.metadata = { finishReason: part.finishReason };\n }\n\n if (\n part.totalUsage === null ||\n typeof part.totalUsage !== \"object\" ||\n Array.isArray(part.totalUsage)\n ) {\n return details;\n }\n\n const totalUsage = part.totalUsage as Record<string, unknown>;\n const counts: AgentRunUsage = {};\n\n for (const key of tokenCountKeys) {\n const value = getTokenCount(totalUsage[key]);\n if (value !== undefined) {\n counts[key] = value;\n }\n }\n\n aggregateRunUsage(details, [{ ...identity, ...counts }]);\n\n return details;\n}\n\nexport function formatToolError(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (\n error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\"\n ) {\n return error.message;\n }\n if (typeof error === \"string\") return error;\n if (error === undefined || error === null) return \"Unknown tool error\";\n\n try {\n const serialized = JSON.stringify(error);\n return serialized === undefined ? String(error) : serialized;\n } catch {\n return String(error);\n }\n}\n\n/**\n * Returns the AG-UI message id for an AI SDK `text-start` / `reasoning-start`\n * part.\n *\n * Several providers number stream parts per response instead of giving them\n * a unique id: @ai-sdk/anthropic and @ai-sdk/google use a block counter\n * (\"0\", \"1\", ...) and @ai-sdk/openai-compatible always uses \"txt-0\" /\n * \"reasoning-0\". Those ids repeat on every step and every run, so reusing\n * them would append later replies to an earlier message. Only ids that look\n * unique are kept.\n */\nexport function resolveStreamPartMessageId(providedId: unknown): string {\n if (\n typeof providedId !== \"string\" ||\n providedId === \"\" ||\n /^(\\d+|(txt|reasoning|msg)-0)$/.test(providedId)\n ) {\n return randomUUID();\n }\n return providedId;\n}\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 *\n * `pendingInterrupts`, when provided, is filled with one AG-UI Interrupt per\n * AI SDK `tool-approval-request` part (a tool declared `needsApproval: true`).\n * The caller turns a non-empty array into a RUN_FINISHED `outcome:interrupt`.\n */\nexport async function* convertAISDKStream(\n fullStream: AsyncIterable<unknown>,\n abortSignal: AbortSignal,\n pendingInterrupts?: Interrupt[],\n initialState?: unknown,\n runFinishedDetails?: AgentRunFinishedDetails,\n): AsyncGenerator<BaseEvent> {\n let messageId = randomUUID();\n let reasoningMessageId = randomUUID();\n let isInReasoning = false;\n const normalizeStateEvent = createStateEventNormalizer(initialState);\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 const warnedUnknownPartTypes = new Set<string>();\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 reasoningMessageId = resolveStreamPartMessageId(\n \"id\" in p ? p.id : undefined,\n );\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 messageId = resolveStreamPartMessageId(\"id\" in p ? p.id : undefined);\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-approval-request\": {\n // AI SDK native human-in-the-loop: a tool declared `needsApproval`\n // was called. The preceding \"tool-call\" part already streamed the\n // tool name + args; this part carries the toolCallId to pause on.\n // (Some shapes nest the call under `toolCall` — read both.)\n const nested = (p.toolCall ?? {}) as {\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n };\n const toolCallId =\n (p.toolCallId as string | undefined) ?? nested.toolCallId;\n if (!toolCallId) {\n throw new Error(\n \"AI SDK tool-approval-request is missing toolCallId\",\n );\n }\n const state = ensureToolCallState(toolCallId);\n const toolName = state.toolName ?? nested.toolName;\n\n // Defensive: emit the tool-call lifecycle if the \"tool-call\" part\n // didn't precede this one, so the assistant tool-call is in history\n // for the resume run (which keys the injected result by toolCallId).\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: toolName ?? \"\",\n };\n yield startEvent;\n }\n if (!state.hasArgsDelta && nested.input !== undefined) {\n let serialized = \"\";\n try {\n serialized =\n typeof nested.input === \"string\"\n ? nested.input\n : JSON.stringify(nested.input);\n } catch {\n serialized = String(nested.input);\n }\n if (serialized.length > 0) {\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: serialized,\n };\n yield argsEvent;\n state.hasArgsDelta = true;\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\n pendingInterrupts?.push({\n id: toolCallId,\n toolCallId,\n reason: \"tool_approval\",\n message: toolName ? `Approve \"${toolName}\"?` : undefined,\n ...(toolName ? { metadata: { toolName } } : {}),\n });\n break;\n }\n\n case \"tool-error\": {\n const toolCallId = p.toolCallId as string | undefined;\n if (!toolCallId) {\n throw new Error(\"AI SDK tool-error is missing toolCallId\");\n }\n\n // Interrupt tools do not execute on the server. Their result is\n // supplied by the human on the resume run, so suppress this part.\n if (\n pendingInterrupts?.some(\n (interrupt) => interrupt.toolCallId === toolCallId,\n )\n ) {\n toolCallStates.delete(toolCallId);\n break;\n }\n\n toolCallStates.delete(toolCallId);\n const resultEvent: ToolCallResultEvent = {\n type: EventType.TOOL_CALL_RESULT,\n role: \"tool\",\n messageId: randomUUID(),\n toolCallId,\n // A tool exception is a tool result from the model's perspective.\n // Keeping the Error prefix consistent with the core tool runner\n // lets both the client and the next model step see the failure.\n content: `Error: ${formatToolError(p.error)}`,\n };\n yield resultEvent;\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 for (const event of normalizeStateEvent(stateSnapshotEvent)) {\n yield event;\n }\n }\n } else if (\n toolName === \"AGUISendStateDelta\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const delta = (toolResult as { delta?: StateDeltaEvent[\"delta\"] })\n .delta;\n if (delta !== undefined) {\n const stateDeltaEvent: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n delta,\n };\n for (const event of normalizeStateEvent(stateDeltaEvent)) {\n yield event;\n }\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 if (runFinishedDetails) {\n Object.assign(runFinishedDetails, getAISDKRunFinishedDetails(p));\n }\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 // These AI SDK fullStream parts carry metadata that has no AG-UI event\n // equivalent. They are known and intentionally ignored; listing them\n // keeps the default's warning for genuinely new parts.\n case \"start\":\n case \"start-step\":\n case \"finish-step\":\n case \"text-end\":\n case \"source\":\n case \"file\":\n case \"tool-output-denied\":\n case \"raw\":\n break;\n\n default: {\n // Parts come from the caller's own `ai` install, which can be newer\n // than ours. Warn once per type and keep the run alive.\n const unknownType = String(p.type);\n if (!warnedUnknownPartTypes.has(unknownType)) {\n warnedUnknownPartTypes.add(unknownType);\n console.warn(\n `[convertAISDKStream] Ignoring unhandled AI SDK stream part: ${unknownType}`,\n );\n }\n break;\n }\n }\n }\n } finally {\n // Always close reasoning on exit (normal or exceptional)\n yield* closeReasoningIfOpen();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aisdk.d.cts","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"aisdk.d.cts","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"mappings":";;;;;;;;;;;;;;;;;;;iBAoHuB,kBAAA,CACrB,UAAA,EAAY,aAAA,WACZ,WAAA,EAAa,WAAA,EACb,iBAAA,GAAoB,SAAA,IACpB,YAAA,YACA,kBAAA,GAAqB,uBAAA,GACpB,cAAA,CAAe,SAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aisdk.d.mts","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"aisdk.d.mts","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"mappings":";;;;;;;;;;;;;;;;;;;iBAoHuB,kBAAA,CACrB,UAAA,EAAY,aAAA,WACZ,WAAA,EAAa,WAAA,EACb,iBAAA,GAAoB,SAAA,IACpB,YAAA,YACA,kBAAA,GAAqB,uBAAA,GACpB,cAAA,CAAe,SAAA"}
|
|
@@ -11,7 +11,7 @@ import { EventType } from "@ag-ui/client";
|
|
|
11
11
|
*/
|
|
12
12
|
function getAISDKRunFinishedDetails(part, identity = {}) {
|
|
13
13
|
const details = {};
|
|
14
|
-
if (typeof part.finishReason === "string") details.
|
|
14
|
+
if (typeof part.finishReason === "string") details.metadata = { finishReason: part.finishReason };
|
|
15
15
|
if (part.totalUsage === null || typeof part.totalUsage !== "object" || Array.isArray(part.totalUsage)) return details;
|
|
16
16
|
const totalUsage = part.totalUsage;
|
|
17
17
|
const counts = {};
|
|
@@ -25,6 +25,33 @@ function getAISDKRunFinishedDetails(part, identity = {}) {
|
|
|
25
25
|
}]);
|
|
26
26
|
return details;
|
|
27
27
|
}
|
|
28
|
+
function formatToolError(error) {
|
|
29
|
+
if (error instanceof Error) return error.message;
|
|
30
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
31
|
+
if (typeof error === "string") return error;
|
|
32
|
+
if (error === void 0 || error === null) return "Unknown tool error";
|
|
33
|
+
try {
|
|
34
|
+
const serialized = JSON.stringify(error);
|
|
35
|
+
return serialized === void 0 ? String(error) : serialized;
|
|
36
|
+
} catch {
|
|
37
|
+
return String(error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the AG-UI message id for an AI SDK `text-start` / `reasoning-start`
|
|
42
|
+
* part.
|
|
43
|
+
*
|
|
44
|
+
* Several providers number stream parts per response instead of giving them
|
|
45
|
+
* a unique id: @ai-sdk/anthropic and @ai-sdk/google use a block counter
|
|
46
|
+
* ("0", "1", ...) and @ai-sdk/openai-compatible always uses "txt-0" /
|
|
47
|
+
* "reasoning-0". Those ids repeat on every step and every run, so reusing
|
|
48
|
+
* them would append later replies to an earlier message. Only ids that look
|
|
49
|
+
* unique are kept.
|
|
50
|
+
*/
|
|
51
|
+
function resolveStreamPartMessageId(providedId) {
|
|
52
|
+
if (typeof providedId !== "string" || providedId === "" || /^(\d+|(txt|reasoning|msg)-0)$/.test(providedId)) return randomUUID();
|
|
53
|
+
return providedId;
|
|
54
|
+
}
|
|
28
55
|
/**
|
|
29
56
|
* Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.
|
|
30
57
|
*
|
|
@@ -77,14 +104,14 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
77
104
|
};
|
|
78
105
|
}
|
|
79
106
|
try {
|
|
107
|
+
const warnedUnknownPartTypes = /* @__PURE__ */ new Set();
|
|
80
108
|
for await (const part of fullStream) {
|
|
81
109
|
const p = part;
|
|
82
110
|
if (p.type !== "reasoning-delta") yield* closeReasoningIfOpen();
|
|
83
111
|
switch (p.type) {
|
|
84
112
|
case "abort": return;
|
|
85
|
-
case "reasoning-start":
|
|
86
|
-
|
|
87
|
-
reasoningMessageId = providedId && providedId !== "0" ? providedId : randomUUID();
|
|
113
|
+
case "reasoning-start":
|
|
114
|
+
reasoningMessageId = resolveStreamPartMessageId("id" in p ? p.id : void 0);
|
|
88
115
|
yield {
|
|
89
116
|
type: EventType.REASONING_START,
|
|
90
117
|
messageId: reasoningMessageId
|
|
@@ -96,7 +123,6 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
96
123
|
};
|
|
97
124
|
isInReasoning = true;
|
|
98
125
|
break;
|
|
99
|
-
}
|
|
100
126
|
case "reasoning-delta": {
|
|
101
127
|
const delta = p.text ?? "";
|
|
102
128
|
if (!delta) break;
|
|
@@ -135,11 +161,9 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
135
161
|
break;
|
|
136
162
|
}
|
|
137
163
|
case "tool-input-end": break;
|
|
138
|
-
case "text-start":
|
|
139
|
-
|
|
140
|
-
messageId = providedId && providedId !== "0" ? providedId : randomUUID();
|
|
164
|
+
case "text-start":
|
|
165
|
+
messageId = resolveStreamPartMessageId("id" in p ? p.id : void 0);
|
|
141
166
|
break;
|
|
142
|
-
}
|
|
143
167
|
case "text-delta": {
|
|
144
168
|
const textDelta = "text" in p ? p.text : "";
|
|
145
169
|
yield {
|
|
@@ -236,6 +260,23 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
236
260
|
});
|
|
237
261
|
break;
|
|
238
262
|
}
|
|
263
|
+
case "tool-error": {
|
|
264
|
+
const toolCallId = p.toolCallId;
|
|
265
|
+
if (!toolCallId) throw new Error("AI SDK tool-error is missing toolCallId");
|
|
266
|
+
if (pendingInterrupts?.some((interrupt) => interrupt.toolCallId === toolCallId)) {
|
|
267
|
+
toolCallStates.delete(toolCallId);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
toolCallStates.delete(toolCallId);
|
|
271
|
+
yield {
|
|
272
|
+
type: EventType.TOOL_CALL_RESULT,
|
|
273
|
+
role: "tool",
|
|
274
|
+
messageId: randomUUID(),
|
|
275
|
+
toolCallId,
|
|
276
|
+
content: `Error: ${formatToolError(p.error)}`
|
|
277
|
+
};
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
239
280
|
case "tool-result": {
|
|
240
281
|
const toolResult = "output" in p ? p.output : "result" in p ? p.result : null;
|
|
241
282
|
const toolName = "toolName" in p ? p.toolName : "";
|
|
@@ -283,7 +324,22 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
283
324
|
if (err instanceof Error) throw err;
|
|
284
325
|
throw new Error(typeof err === "string" ? err : `AI SDK stream error: ${JSON.stringify(p)}`);
|
|
285
326
|
}
|
|
286
|
-
|
|
327
|
+
case "start":
|
|
328
|
+
case "start-step":
|
|
329
|
+
case "finish-step":
|
|
330
|
+
case "text-end":
|
|
331
|
+
case "source":
|
|
332
|
+
case "file":
|
|
333
|
+
case "tool-output-denied":
|
|
334
|
+
case "raw": break;
|
|
335
|
+
default: {
|
|
336
|
+
const unknownType = String(p.type);
|
|
337
|
+
if (!warnedUnknownPartTypes.has(unknownType)) {
|
|
338
|
+
warnedUnknownPartTypes.add(unknownType);
|
|
339
|
+
console.warn(`[convertAISDKStream] Ignoring unhandled AI SDK stream part: ${unknownType}`);
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
287
343
|
}
|
|
288
344
|
}
|
|
289
345
|
} finally {
|
|
@@ -292,5 +348,5 @@ async function* convertAISDKStream(fullStream, abortSignal, pendingInterrupts, i
|
|
|
292
348
|
}
|
|
293
349
|
|
|
294
350
|
//#endregion
|
|
295
|
-
export { convertAISDKStream, getAISDKRunFinishedDetails };
|
|
351
|
+
export { convertAISDKStream, formatToolError, getAISDKRunFinishedDetails, resolveStreamPartMessageId };
|
|
296
352
|
//# sourceMappingURL=aisdk.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aisdk.mjs","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"sourcesContent":["import type {\n BaseEvent,\n Interrupt,\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 { EventType } from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { createStateEventNormalizer } from \"../state-delta\";\nimport { aggregateRunUsage, getTokenCount, tokenCountKeys } from \"./usage\";\nimport type { AgentRunFinishedDetails, AgentRunUsage } from \"./usage\";\n\n/**\n * Reads aggregate usage from an AI SDK finish part without inventing values\n * when a provider omits a token count.\n */\nexport function getAISDKRunFinishedDetails(\n part: Record<string, unknown>,\n identity: { provider?: string; model?: string } = {},\n): AgentRunFinishedDetails {\n const details: AgentRunFinishedDetails = {};\n\n if (typeof part.finishReason === \"string\") {\n details.finishReason = part.finishReason;\n }\n\n if (\n part.totalUsage === null ||\n typeof part.totalUsage !== \"object\" ||\n Array.isArray(part.totalUsage)\n ) {\n return details;\n }\n\n const totalUsage = part.totalUsage as Record<string, unknown>;\n const counts: AgentRunUsage = {};\n\n for (const key of tokenCountKeys) {\n const value = getTokenCount(totalUsage[key]);\n if (value !== undefined) {\n counts[key] = value;\n }\n }\n\n aggregateRunUsage(details, [{ ...identity, ...counts }]);\n\n return details;\n}\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 *\n * `pendingInterrupts`, when provided, is filled with one AG-UI Interrupt per\n * AI SDK `tool-approval-request` part (a tool declared `needsApproval: true`).\n * The caller turns a non-empty array into a RUN_FINISHED `outcome:interrupt`.\n */\nexport async function* convertAISDKStream(\n fullStream: AsyncIterable<unknown>,\n abortSignal: AbortSignal,\n pendingInterrupts?: Interrupt[],\n initialState?: unknown,\n runFinishedDetails?: AgentRunFinishedDetails,\n): AsyncGenerator<BaseEvent> {\n let messageId = randomUUID();\n let reasoningMessageId = randomUUID();\n let isInReasoning = false;\n const normalizeStateEvent = createStateEventNormalizer(initialState);\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-approval-request\": {\n // AI SDK native human-in-the-loop: a tool declared `needsApproval`\n // was called. The preceding \"tool-call\" part already streamed the\n // tool name + args; this part carries the toolCallId to pause on.\n // (Some shapes nest the call under `toolCall` — read both.)\n const nested = (p.toolCall ?? {}) as {\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n };\n const toolCallId =\n (p.toolCallId as string | undefined) ?? nested.toolCallId;\n if (!toolCallId) {\n throw new Error(\n \"AI SDK tool-approval-request is missing toolCallId\",\n );\n }\n const state = ensureToolCallState(toolCallId);\n const toolName = state.toolName ?? nested.toolName;\n\n // Defensive: emit the tool-call lifecycle if the \"tool-call\" part\n // didn't precede this one, so the assistant tool-call is in history\n // for the resume run (which keys the injected result by toolCallId).\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: toolName ?? \"\",\n };\n yield startEvent;\n }\n if (!state.hasArgsDelta && nested.input !== undefined) {\n let serialized = \"\";\n try {\n serialized =\n typeof nested.input === \"string\"\n ? nested.input\n : JSON.stringify(nested.input);\n } catch {\n serialized = String(nested.input);\n }\n if (serialized.length > 0) {\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: serialized,\n };\n yield argsEvent;\n state.hasArgsDelta = true;\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\n pendingInterrupts?.push({\n id: toolCallId,\n toolCallId,\n reason: \"tool_approval\",\n message: toolName ? `Approve \"${toolName}\"?` : undefined,\n ...(toolName ? { metadata: { toolName } } : {}),\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 for (const event of normalizeStateEvent(stateSnapshotEvent)) {\n yield event;\n }\n }\n } else if (\n toolName === \"AGUISendStateDelta\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const delta = (toolResult as { delta?: StateDeltaEvent[\"delta\"] })\n .delta;\n if (delta !== undefined) {\n const stateDeltaEvent: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n delta,\n };\n for (const event of normalizeStateEvent(stateDeltaEvent)) {\n yield event;\n }\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 if (runFinishedDetails) {\n Object.assign(runFinishedDetails, getAISDKRunFinishedDetails(p));\n }\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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"}
|
|
1
|
+
{"version":3,"file":"aisdk.mjs","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"sourcesContent":["import type {\n BaseEvent,\n Interrupt,\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 { EventType } from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { createStateEventNormalizer } from \"../state-delta\";\nimport { aggregateRunUsage, getTokenCount, tokenCountKeys } from \"./usage\";\nimport type { AgentRunFinishedDetails, AgentRunUsage } from \"./usage\";\n\n/**\n * Reads aggregate usage from an AI SDK finish part without inventing values\n * when a provider omits a token count.\n */\nexport function getAISDKRunFinishedDetails(\n part: Record<string, unknown>,\n identity: { provider?: string; model?: string } = {},\n): AgentRunFinishedDetails {\n const details: AgentRunFinishedDetails = {};\n\n if (typeof part.finishReason === \"string\") {\n details.metadata = { finishReason: part.finishReason };\n }\n\n if (\n part.totalUsage === null ||\n typeof part.totalUsage !== \"object\" ||\n Array.isArray(part.totalUsage)\n ) {\n return details;\n }\n\n const totalUsage = part.totalUsage as Record<string, unknown>;\n const counts: AgentRunUsage = {};\n\n for (const key of tokenCountKeys) {\n const value = getTokenCount(totalUsage[key]);\n if (value !== undefined) {\n counts[key] = value;\n }\n }\n\n aggregateRunUsage(details, [{ ...identity, ...counts }]);\n\n return details;\n}\n\nexport function formatToolError(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (\n error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\"\n ) {\n return error.message;\n }\n if (typeof error === \"string\") return error;\n if (error === undefined || error === null) return \"Unknown tool error\";\n\n try {\n const serialized = JSON.stringify(error);\n return serialized === undefined ? String(error) : serialized;\n } catch {\n return String(error);\n }\n}\n\n/**\n * Returns the AG-UI message id for an AI SDK `text-start` / `reasoning-start`\n * part.\n *\n * Several providers number stream parts per response instead of giving them\n * a unique id: @ai-sdk/anthropic and @ai-sdk/google use a block counter\n * (\"0\", \"1\", ...) and @ai-sdk/openai-compatible always uses \"txt-0\" /\n * \"reasoning-0\". Those ids repeat on every step and every run, so reusing\n * them would append later replies to an earlier message. Only ids that look\n * unique are kept.\n */\nexport function resolveStreamPartMessageId(providedId: unknown): string {\n if (\n typeof providedId !== \"string\" ||\n providedId === \"\" ||\n /^(\\d+|(txt|reasoning|msg)-0)$/.test(providedId)\n ) {\n return randomUUID();\n }\n return providedId;\n}\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 *\n * `pendingInterrupts`, when provided, is filled with one AG-UI Interrupt per\n * AI SDK `tool-approval-request` part (a tool declared `needsApproval: true`).\n * The caller turns a non-empty array into a RUN_FINISHED `outcome:interrupt`.\n */\nexport async function* convertAISDKStream(\n fullStream: AsyncIterable<unknown>,\n abortSignal: AbortSignal,\n pendingInterrupts?: Interrupt[],\n initialState?: unknown,\n runFinishedDetails?: AgentRunFinishedDetails,\n): AsyncGenerator<BaseEvent> {\n let messageId = randomUUID();\n let reasoningMessageId = randomUUID();\n let isInReasoning = false;\n const normalizeStateEvent = createStateEventNormalizer(initialState);\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 const warnedUnknownPartTypes = new Set<string>();\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 reasoningMessageId = resolveStreamPartMessageId(\n \"id\" in p ? p.id : undefined,\n );\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 messageId = resolveStreamPartMessageId(\"id\" in p ? p.id : undefined);\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-approval-request\": {\n // AI SDK native human-in-the-loop: a tool declared `needsApproval`\n // was called. The preceding \"tool-call\" part already streamed the\n // tool name + args; this part carries the toolCallId to pause on.\n // (Some shapes nest the call under `toolCall` — read both.)\n const nested = (p.toolCall ?? {}) as {\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n };\n const toolCallId =\n (p.toolCallId as string | undefined) ?? nested.toolCallId;\n if (!toolCallId) {\n throw new Error(\n \"AI SDK tool-approval-request is missing toolCallId\",\n );\n }\n const state = ensureToolCallState(toolCallId);\n const toolName = state.toolName ?? nested.toolName;\n\n // Defensive: emit the tool-call lifecycle if the \"tool-call\" part\n // didn't precede this one, so the assistant tool-call is in history\n // for the resume run (which keys the injected result by toolCallId).\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: toolName ?? \"\",\n };\n yield startEvent;\n }\n if (!state.hasArgsDelta && nested.input !== undefined) {\n let serialized = \"\";\n try {\n serialized =\n typeof nested.input === \"string\"\n ? nested.input\n : JSON.stringify(nested.input);\n } catch {\n serialized = String(nested.input);\n }\n if (serialized.length > 0) {\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n delta: serialized,\n };\n yield argsEvent;\n state.hasArgsDelta = true;\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\n pendingInterrupts?.push({\n id: toolCallId,\n toolCallId,\n reason: \"tool_approval\",\n message: toolName ? `Approve \"${toolName}\"?` : undefined,\n ...(toolName ? { metadata: { toolName } } : {}),\n });\n break;\n }\n\n case \"tool-error\": {\n const toolCallId = p.toolCallId as string | undefined;\n if (!toolCallId) {\n throw new Error(\"AI SDK tool-error is missing toolCallId\");\n }\n\n // Interrupt tools do not execute on the server. Their result is\n // supplied by the human on the resume run, so suppress this part.\n if (\n pendingInterrupts?.some(\n (interrupt) => interrupt.toolCallId === toolCallId,\n )\n ) {\n toolCallStates.delete(toolCallId);\n break;\n }\n\n toolCallStates.delete(toolCallId);\n const resultEvent: ToolCallResultEvent = {\n type: EventType.TOOL_CALL_RESULT,\n role: \"tool\",\n messageId: randomUUID(),\n toolCallId,\n // A tool exception is a tool result from the model's perspective.\n // Keeping the Error prefix consistent with the core tool runner\n // lets both the client and the next model step see the failure.\n content: `Error: ${formatToolError(p.error)}`,\n };\n yield resultEvent;\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 for (const event of normalizeStateEvent(stateSnapshotEvent)) {\n yield event;\n }\n }\n } else if (\n toolName === \"AGUISendStateDelta\" &&\n toolResult &&\n typeof toolResult === \"object\"\n ) {\n const delta = (toolResult as { delta?: StateDeltaEvent[\"delta\"] })\n .delta;\n if (delta !== undefined) {\n const stateDeltaEvent: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n delta,\n };\n for (const event of normalizeStateEvent(stateDeltaEvent)) {\n yield event;\n }\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 if (runFinishedDetails) {\n Object.assign(runFinishedDetails, getAISDKRunFinishedDetails(p));\n }\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 // These AI SDK fullStream parts carry metadata that has no AG-UI event\n // equivalent. They are known and intentionally ignored; listing them\n // keeps the default's warning for genuinely new parts.\n case \"start\":\n case \"start-step\":\n case \"finish-step\":\n case \"text-end\":\n case \"source\":\n case \"file\":\n case \"tool-output-denied\":\n case \"raw\":\n break;\n\n default: {\n // Parts come from the caller's own `ai` install, which can be newer\n // than ours. Warn once per type and keep the run alive.\n const unknownType = String(p.type);\n if (!warnedUnknownPartTypes.has(unknownType)) {\n warnedUnknownPartTypes.add(unknownType);\n console.warn(\n `[convertAISDKStream] Ignoring unhandled AI SDK stream part: ${unknownType}`,\n );\n }\n break;\n }\n }\n }\n } finally {\n // Always close reasoning on exit (normal or exceptional)\n yield* closeReasoningIfOpen();\n }\n}\n"],"mappings":"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"}
|
|
@@ -43,6 +43,7 @@ function convertUserContent(content) {
|
|
|
43
43
|
mimeType: source.mimeType
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
|
+
else if (source.type === "file") console.warn(`[CopilotKit] Dropping a ${partType} part that references a provider file handle: it is not a URL or inline data, so it cannot be sent to the model here.`);
|
|
46
47
|
else if (source.type === "url") parts.push({
|
|
47
48
|
type: partType,
|
|
48
49
|
source: {
|
|
@@ -133,7 +134,7 @@ function convertInputToTanStackAI(input) {
|
|
|
133
134
|
const messages = input.messages.filter((m) => chatRoles.has(m.role)).map((m) => {
|
|
134
135
|
const msg = {
|
|
135
136
|
role: m.role,
|
|
136
|
-
content: m.role === "user" ? convertUserContent(m.content) : typeof m.content === "string" ? m.content : null
|
|
137
|
+
content: m.role === "user" ? convertUserContent(m.content) : m.role === "tool" ? (0, _ag_ui_client.contentToText)(m.content) : typeof m.content === "string" ? m.content : null
|
|
137
138
|
};
|
|
138
139
|
if (m.role === "assistant" && "toolCalls" in m && m.toolCalls) msg.toolCalls = m.toolCalls.map((tc) => ({
|
|
139
140
|
id: tc.id,
|
|
@@ -337,16 +338,17 @@ async function* convertTanStackStream(stream, abortSignal, pendingInterrupts, in
|
|
|
337
338
|
/** Normalizes legacy and standard TanStack usage into AG-UI token usage. */
|
|
338
339
|
function collectTanStackRunFinishedDetails(event, details) {
|
|
339
340
|
if (!details) return;
|
|
340
|
-
if (typeof event.finishReason === "string") details.finishReason = event.finishReason;
|
|
341
341
|
const fallbackIdentity = {
|
|
342
342
|
provider: require_usage.getNonEmptyString(event.provider),
|
|
343
343
|
model: require_usage.getNonEmptyString(event.model)
|
|
344
344
|
};
|
|
345
|
+
require_usage.collectStandardRunFinishedDetails(event, details, fallbackIdentity);
|
|
346
|
+
if (typeof event.finishReason === "string") details.metadata = {
|
|
347
|
+
...details.metadata,
|
|
348
|
+
finishReason: event.finishReason
|
|
349
|
+
};
|
|
345
350
|
const usage = event.usage;
|
|
346
|
-
if (Array.isArray(usage))
|
|
347
|
-
require_usage.collectStandardRunFinishedDetails(event, details, fallbackIdentity);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
351
|
+
if (Array.isArray(usage)) return;
|
|
350
352
|
if (!require_usage.isRecord(usage)) return;
|
|
351
353
|
const promptDetails = require_usage.isRecord(usage.promptTokensDetails) ? usage.promptTokensDetails : {};
|
|
352
354
|
const completionDetails = require_usage.isRecord(usage.completionTokensDetails) ? usage.completionTokensDetails : {};
|