@assistant-ui/react-ag-ui 0.0.71 → 0.1.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/README.md +1 -1
- package/dist/runtime/{AgUiThreadRuntimeCore.d.ts → AgUiThreadController.d.ts} +6 -12
- package/dist/runtime/AgUiThreadController.d.ts.map +1 -0
- package/dist/runtime/{AgUiThreadRuntimeCore.js → AgUiThreadController.js} +246 -159
- package/dist/runtime/AgUiThreadController.js.map +1 -0
- package/dist/runtime/adapter/conversions.d.ts +4 -4
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +111 -33
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +3 -5
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +47 -61
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +0 -24
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/adapter/subscriber.js +1 -22
- package/dist/runtime/adapter/subscriber.js.map +1 -1
- package/dist/runtime/agUiThreadState.d.ts +60 -0
- package/dist/runtime/agUiThreadState.d.ts.map +1 -0
- package/dist/runtime/agUiThreadState.js +95 -0
- package/dist/runtime/agUiThreadState.js.map +1 -0
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +45 -48
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/mcp-tool-result.d.ts +3 -1
- package/dist/runtime/mcp-tool-result.d.ts.map +1 -1
- package/dist/runtime/mcp-tool-result.js +6 -3
- package/dist/runtime/mcp-tool-result.js.map +1 -1
- package/dist/runtime/types.d.ts +11 -35
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/useAgUiRuntime.js +2 -2
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +12 -11
- package/src/runtime/{AgUiThreadRuntimeCore.interrupt-cancel.test.ts → AgUiThreadController.interrupt-cancel.test.ts} +4 -4
- package/src/runtime/{AgUiThreadRuntimeCore.test.ts → AgUiThreadController.test.ts} +35 -6
- package/src/runtime/{AgUiThreadRuntimeCore.ts → AgUiThreadController.ts} +356 -171
- package/src/runtime/adapter/conversions.content.test.ts +37 -5
- package/src/runtime/adapter/conversions.tools.test.ts +2 -1
- package/src/runtime/adapter/conversions.ts +235 -58
- package/src/runtime/adapter/run-aggregator.ts +79 -114
- package/src/runtime/adapter/subscriber.ts +1 -44
- package/src/runtime/agUiThreadState.test.ts +205 -0
- package/src/runtime/agUiThreadState.ts +130 -0
- package/src/runtime/event-parser.ts +40 -58
- package/src/runtime/mcp-tool-result.ts +16 -2
- package/src/runtime/types.ts +16 -28
- package/src/useAgUiRuntime.fast-refresh.test.tsx +3 -3
- package/src/useAgUiRuntime.thread-switch.test.tsx +4 -4
- package/src/useAgUiRuntime.ts +3 -3
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +0 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { parseMcpToolCallResult, readMcpAppResourceUri } from "../mcp-tool-result.js";
|
|
2
|
+
import { parseMcpToolCallResult, readMcpAppResourceUri, toolContentText } from "../mcp-tool-result.js";
|
|
3
3
|
import { AG_UI_METADATA_NAMESPACE } from "../types.js";
|
|
4
4
|
import { projectAgUiToolApprovals } from "./tool-approval.js";
|
|
5
5
|
import { generateId } from "@assistant-ui/core";
|
|
@@ -21,11 +21,11 @@ function readLegacyAgUiNamespace(metadata) {
|
|
|
21
21
|
if (!isObject(namespaced)) return {};
|
|
22
22
|
return "interrupts" in namespaced ? { interrupts: namespaced.interrupts } : {};
|
|
23
23
|
}
|
|
24
|
-
function
|
|
24
|
+
function withoutPersistedStatusMetadata(wire) {
|
|
25
25
|
if (!isObject(wire.custom)) return wire;
|
|
26
26
|
const namespaced = wire.custom[AG_UI_METADATA_NAMESPACE];
|
|
27
|
-
if (!isObject(namespaced) || !("interrupts" in namespaced)) return wire;
|
|
28
|
-
const { interrupts: _interrupts, ...remainingNamespace } = namespaced;
|
|
27
|
+
if (!isObject(namespaced) || !("interrupts" in namespaced) && !("pendingToolCallIds" in namespaced)) return wire;
|
|
28
|
+
const { interrupts: _interrupts, pendingToolCallIds: _pendingToolCallIds, ...remainingNamespace } = namespaced;
|
|
29
29
|
const { [AG_UI_METADATA_NAMESPACE]: _namespace, ...remainingCustom } = wire.custom;
|
|
30
30
|
if (Object.keys(remainingNamespace).length > 0) remainingCustom[AG_UI_METADATA_NAMESPACE] = remainingNamespace;
|
|
31
31
|
const { custom: _custom, ...remainingWire } = wire;
|
|
@@ -35,10 +35,11 @@ function withoutPersistedInterrupts(wire) {
|
|
|
35
35
|
} : remainingWire;
|
|
36
36
|
}
|
|
37
37
|
function toSnapshotCustom(metadata, state = {}) {
|
|
38
|
-
const wire =
|
|
38
|
+
const wire = withoutPersistedStatusMetadata(toAgUiWireMetadata(metadata) ?? {});
|
|
39
39
|
const needsWireSlot = isObject(metadata) && (Object.keys(metadata).length === 0 || "agui" in wire);
|
|
40
40
|
const namespaced = {
|
|
41
41
|
...readLegacyAgUiNamespace(metadata),
|
|
42
|
+
...readPersistedPendingToolCallIds(metadata) !== void 0 ? { pendingToolCallIds: readPersistedPendingToolCallIds(metadata) } : {},
|
|
42
43
|
...state,
|
|
43
44
|
...needsWireSlot ? { messageMetadata: wire } : {}
|
|
44
45
|
};
|
|
@@ -89,14 +90,14 @@ const readAgUiReasoningMeta = (providerMetadata) => {
|
|
|
89
90
|
messageMetadata: toAgUiWireMetadata(namespaced.messageMetadata)
|
|
90
91
|
};
|
|
91
92
|
};
|
|
92
|
-
const
|
|
93
|
+
const buildPartMetadata = (part, filename) => {
|
|
93
94
|
const metadata = {
|
|
94
95
|
...readAgUiNamespace(part.providerMetadata),
|
|
95
96
|
...filename !== void 0 && { filename }
|
|
96
97
|
};
|
|
97
98
|
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
98
99
|
};
|
|
99
|
-
const
|
|
100
|
+
const readPartMetadata = (metadata) => {
|
|
100
101
|
if (!isObject(metadata)) return {};
|
|
101
102
|
const { filename: _filename, ...rest } = metadata;
|
|
102
103
|
return {
|
|
@@ -139,7 +140,7 @@ function mediaTypeForMime(mimeType) {
|
|
|
139
140
|
if (mimeType?.startsWith("video/")) return "video";
|
|
140
141
|
return "document";
|
|
141
142
|
}
|
|
142
|
-
function
|
|
143
|
+
function buildPartSource(value, declaredMimeType, sourceType) {
|
|
143
144
|
const source = resolveFilePartSource({
|
|
144
145
|
data: value,
|
|
145
146
|
mimeType: declaredMimeType ?? "application/octet-stream",
|
|
@@ -159,7 +160,7 @@ function buildInputSource(value, declaredMimeType, sourceType) {
|
|
|
159
160
|
mimeType: source.mimeType
|
|
160
161
|
};
|
|
161
162
|
}
|
|
162
|
-
function
|
|
163
|
+
function toContentPart(part, fallbackMimeType) {
|
|
163
164
|
if (!isObject(part)) return null;
|
|
164
165
|
const type = getString(part, "type");
|
|
165
166
|
if (type === "text") {
|
|
@@ -173,8 +174,8 @@ function toInputContent(part, fallbackMimeType) {
|
|
|
173
174
|
if (type === "image") {
|
|
174
175
|
const image = getString(part, "image");
|
|
175
176
|
if (image === void 0) return null;
|
|
176
|
-
const metadata =
|
|
177
|
-
const source =
|
|
177
|
+
const metadata = buildPartMetadata(part, getString(part, "filename"));
|
|
178
|
+
const source = buildPartSource(image, fallbackMimeType);
|
|
178
179
|
return {
|
|
179
180
|
type: "image",
|
|
180
181
|
source: source.type === "data" ? {
|
|
@@ -189,8 +190,8 @@ function toInputContent(part, fallbackMimeType) {
|
|
|
189
190
|
if (data === void 0) return null;
|
|
190
191
|
const declaredMimeType = getString(part, "mimeType") || fallbackMimeType;
|
|
191
192
|
const filename = getString(part, "filename");
|
|
192
|
-
const source =
|
|
193
|
-
const metadata =
|
|
193
|
+
const source = buildPartSource(data, declaredMimeType, getString(part, "sourceType"));
|
|
194
|
+
const metadata = buildPartMetadata(part, filename);
|
|
194
195
|
switch (mediaTypeForMime(source.mimeType)) {
|
|
195
196
|
case "image": return {
|
|
196
197
|
type: "image",
|
|
@@ -222,7 +223,7 @@ function toInputContent(part, fallbackMimeType) {
|
|
|
222
223
|
if (data === void 0 || format === void 0) return null;
|
|
223
224
|
return {
|
|
224
225
|
type: "audio",
|
|
225
|
-
source:
|
|
226
|
+
source: buildPartSource(parseDataUrl(data)?.data ?? data, `audio/${format}`)
|
|
226
227
|
};
|
|
227
228
|
}
|
|
228
229
|
return null;
|
|
@@ -233,7 +234,7 @@ const mediaInputTypes = /* @__PURE__ */ new Set([
|
|
|
233
234
|
"video",
|
|
234
235
|
"document"
|
|
235
236
|
]);
|
|
236
|
-
function
|
|
237
|
+
function partSourceToString(value) {
|
|
237
238
|
if (!isObject(value)) return null;
|
|
238
239
|
const sourceValue = getString(value, "value");
|
|
239
240
|
if (sourceValue === void 0) return null;
|
|
@@ -275,18 +276,51 @@ function upgradeBinaryInputPart(part) {
|
|
|
275
276
|
...filename && { metadata: { filename } }
|
|
276
277
|
};
|
|
277
278
|
}
|
|
279
|
+
function normalizeInputPart(rawPart) {
|
|
280
|
+
return getString(rawPart, "type") === "binary" ? upgradeBinaryInputPart(rawPart) : rawPart;
|
|
281
|
+
}
|
|
282
|
+
function toUserContentLayout(content) {
|
|
283
|
+
const fileSourceParts = [];
|
|
284
|
+
const textParts = [];
|
|
285
|
+
if (!Array.isArray(content)) return {
|
|
286
|
+
fileSourceParts,
|
|
287
|
+
textParts
|
|
288
|
+
};
|
|
289
|
+
let mediaIndex = 0;
|
|
290
|
+
for (const rawPart of content) {
|
|
291
|
+
if (!isObject(rawPart)) continue;
|
|
292
|
+
if (rawPart.type === "text" && typeof rawPart.text === "string") {
|
|
293
|
+
textParts.push({
|
|
294
|
+
index: textParts.length + mediaIndex,
|
|
295
|
+
text: rawPart.text
|
|
296
|
+
});
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
const part = normalizeInputPart(rawPart);
|
|
300
|
+
if (!part || !mediaInputTypes.has(getString(part, "type") ?? "")) continue;
|
|
301
|
+
if (isObject(part.source) && part.source.type === "file" && typeof part.source.value === "string") fileSourceParts.push({
|
|
302
|
+
index: mediaIndex++,
|
|
303
|
+
part
|
|
304
|
+
});
|
|
305
|
+
else if (partSourceToString(part.source)) mediaIndex++;
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
fileSourceParts,
|
|
309
|
+
textParts: textParts.length === 0 || textParts.length === 1 && textParts[0].index === 0 && textParts[0].text !== "" ? [] : textParts
|
|
310
|
+
};
|
|
311
|
+
}
|
|
278
312
|
function toSnapshotAttachments(content) {
|
|
279
313
|
if (!Array.isArray(content)) return [];
|
|
280
314
|
const attachments = [];
|
|
281
315
|
for (const rawPart of content) {
|
|
282
316
|
if (!isObject(rawPart)) continue;
|
|
283
|
-
const part =
|
|
317
|
+
const part = normalizeInputPart(rawPart);
|
|
284
318
|
if (!part) continue;
|
|
285
319
|
const type = getString(part, "type");
|
|
286
320
|
if (type === void 0 || !mediaInputTypes.has(type)) continue;
|
|
287
|
-
const source =
|
|
321
|
+
const source = partSourceToString(part.source);
|
|
288
322
|
if (!source) continue;
|
|
289
|
-
const { filename, providerMetadata } =
|
|
323
|
+
const { filename, providerMetadata } = readPartMetadata(part.metadata);
|
|
290
324
|
const id = attachments.length.toString();
|
|
291
325
|
if (type === "image") {
|
|
292
326
|
attachments.push({
|
|
@@ -323,6 +357,13 @@ function toSnapshotAttachments(content) {
|
|
|
323
357
|
}
|
|
324
358
|
return attachments;
|
|
325
359
|
}
|
|
360
|
+
function readContentSlots(value) {
|
|
361
|
+
if (!Array.isArray(value)) return [];
|
|
362
|
+
return value.flatMap((entry) => isObject(entry) && typeof entry.index === "number" && Number.isInteger(entry.index) && entry.index >= 0 ? [{
|
|
363
|
+
index: entry.index,
|
|
364
|
+
entry
|
|
365
|
+
}] : []);
|
|
366
|
+
}
|
|
326
367
|
function buildUserContent(message) {
|
|
327
368
|
const contentParts = Array.isArray(message.content) ? message.content : [];
|
|
328
369
|
const attachments = message.attachments ?? [];
|
|
@@ -332,7 +373,7 @@ function buildUserContent(message) {
|
|
|
332
373
|
text: message.content
|
|
333
374
|
});
|
|
334
375
|
for (const part of contentParts) {
|
|
335
|
-
const input =
|
|
376
|
+
const input = toContentPart(part, void 0);
|
|
336
377
|
if (input) converted.push(input);
|
|
337
378
|
}
|
|
338
379
|
for (const attachment of attachments) {
|
|
@@ -341,11 +382,31 @@ function buildUserContent(message) {
|
|
|
341
382
|
if (!Array.isArray(attachmentContent)) continue;
|
|
342
383
|
const fallbackMime = getString(attachment, "contentType");
|
|
343
384
|
for (const part of attachmentContent) {
|
|
344
|
-
const input =
|
|
385
|
+
const input = toContentPart(part, fallbackMime);
|
|
345
386
|
if (input) converted.push(input);
|
|
346
387
|
}
|
|
347
388
|
}
|
|
348
|
-
|
|
389
|
+
const namespaced = readNamespacedMetadata(message.metadata);
|
|
390
|
+
const textSlots = readContentSlots(namespaced.textParts).flatMap(({ index, entry }) => typeof entry.text === "string" ? [{
|
|
391
|
+
index,
|
|
392
|
+
part: {
|
|
393
|
+
type: "text",
|
|
394
|
+
text: entry.text
|
|
395
|
+
}
|
|
396
|
+
}] : []);
|
|
397
|
+
const mergedText = converted.flatMap((part) => part.type === "text" ? [part.text] : []).join("\n");
|
|
398
|
+
const restoresText = textSlots.length > 0 && textSlots.map(({ part }) => part.text).join("\n") === mergedText;
|
|
399
|
+
const fileSourceSlots = readContentSlots(namespaced.fileSourceParts).flatMap(({ index, entry: { part } }) => isObject(part) && mediaInputTypes.has(getString(part, "type") ?? "") && isObject(part.source) && part.source.type === "file" && typeof part.source.value === "string" ? [{
|
|
400
|
+
index,
|
|
401
|
+
part
|
|
402
|
+
}] : []);
|
|
403
|
+
const ordered = restoresText ? converted.filter((part) => part.type !== "text") : converted;
|
|
404
|
+
for (const { index, part } of fileSourceSlots.sort((a, b) => a.index - b.index)) {
|
|
405
|
+
const mediaPositions = ordered.flatMap((contentPart, position) => contentPart.type !== "text" ? [position] : []);
|
|
406
|
+
ordered.splice(mediaPositions[index] ?? ordered.length, 0, part);
|
|
407
|
+
}
|
|
408
|
+
if (restoresText) for (const { index, part } of textSlots.sort((a, b) => a.index - b.index)) ordered.splice(Math.min(index, ordered.length), 0, part);
|
|
409
|
+
if (restoresText || ordered.some((part) => part.type !== "text")) return ordered;
|
|
349
410
|
if (converted.length === 0) return extractText(message.content);
|
|
350
411
|
return converted.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
351
412
|
}
|
|
@@ -406,6 +467,10 @@ function readPersistedInterrupts(metadata) {
|
|
|
406
467
|
if (!isObject(namespaced)) return void 0;
|
|
407
468
|
return validateInterrupts(namespaced.interrupts);
|
|
408
469
|
}
|
|
470
|
+
function readPersistedPendingToolCallIds(metadata) {
|
|
471
|
+
const value = readNamespacedMetadata(metadata).pendingToolCallIds;
|
|
472
|
+
return Array.isArray(value) && value.length > 0 && value.every((id) => typeof id === "string") ? value : void 0;
|
|
473
|
+
}
|
|
409
474
|
function readOpaqueReasoning(metadata) {
|
|
410
475
|
if (!isObject(metadata)) return [];
|
|
411
476
|
const custom = metadata.custom;
|
|
@@ -480,7 +545,15 @@ function toAssistantSnapshotMessage(rawMessage) {
|
|
|
480
545
|
}
|
|
481
546
|
function toUserOrSystemSnapshotMessage(role, rawMessage) {
|
|
482
547
|
const messageName = getString(rawMessage, "name");
|
|
483
|
-
const
|
|
548
|
+
const { fileSourceParts, textParts } = role === "user" ? toUserContentLayout(rawMessage.content) : {
|
|
549
|
+
fileSourceParts: [],
|
|
550
|
+
textParts: []
|
|
551
|
+
};
|
|
552
|
+
const custom = toSnapshotCustom(rawMessage.metadata, {
|
|
553
|
+
...role === "developer" ? { role: "developer" } : {},
|
|
554
|
+
...fileSourceParts.length > 0 ? { fileSourceParts } : {},
|
|
555
|
+
...textParts.length > 0 ? { textParts } : {}
|
|
556
|
+
});
|
|
484
557
|
const attachments = role === "user" ? toSnapshotAttachments(rawMessage.content) : [];
|
|
485
558
|
return {
|
|
486
559
|
id: getString(rawMessage, "id") ?? generateId(),
|
|
@@ -603,13 +676,13 @@ function fromAgUiMessages(messages, options) {
|
|
|
603
676
|
flushPendingReasoning();
|
|
604
677
|
const toolCallId = getToolCallId(rawMessage) ?? `tool-${generateId()}`;
|
|
605
678
|
const toolMessageId = getString(rawMessage, "id");
|
|
606
|
-
const modelContent = extractText(rawMessage.content);
|
|
607
|
-
const mcpResult = parseMcpToolCallResult(rawMessage, modelContent);
|
|
608
|
-
const
|
|
679
|
+
const modelContent = Array.isArray(rawMessage.content) ? toolContentText(rawMessage.content) : extractText(rawMessage.content);
|
|
680
|
+
const mcpResult = parseMcpToolCallResult(rawMessage, Array.isArray(rawMessage.content) ? rawMessage.content : modelContent);
|
|
681
|
+
const toolModelContent = mcpResult ? [{
|
|
609
682
|
type: "text",
|
|
610
683
|
text: modelContent
|
|
611
684
|
}] : void 0;
|
|
612
|
-
const result = mcpResult ?? (rawMessage.result !== void 0 ? rawMessage.result : typeof rawMessage.content === "string" ? parseJSONText(rawMessage.content) : rawMessage.content);
|
|
685
|
+
const result = mcpResult ?? (Array.isArray(rawMessage.content) ? rawMessage.content : rawMessage.result !== void 0 ? rawMessage.result : typeof rawMessage.content === "string" ? parseJSONText(rawMessage.content) : rawMessage.content);
|
|
613
686
|
const isError = typeof rawMessage.error === "string" || rawMessage.isError === true || rawMessage.status === "error" ? true : rawMessage.isError === false ? false : void 0;
|
|
614
687
|
const mcpAppUri = readMcpAppResourceUri(mcpResult?._meta);
|
|
615
688
|
const mcpApp = mcpAppUri !== void 0 ? { resourceUri: mcpAppUri } : void 0;
|
|
@@ -622,10 +695,11 @@ function fromAgUiMessages(messages, options) {
|
|
|
622
695
|
const part = message.content[partIndex];
|
|
623
696
|
if (!isObject(part) || part.type !== "tool-call") continue;
|
|
624
697
|
if (getString(part, "toolCallId") !== toolCallId) continue;
|
|
698
|
+
const { modelContent: _modelContent, ...partWithoutModelContent } = part;
|
|
625
699
|
const updatedPart = {
|
|
626
|
-
...part,
|
|
700
|
+
...Array.isArray(rawMessage.content) && !mcpResult ? partWithoutModelContent : part,
|
|
627
701
|
result,
|
|
628
|
-
...
|
|
702
|
+
...toolModelContent ? { modelContent: toolModelContent } : {},
|
|
629
703
|
...isError !== void 0 ? { isError } : {},
|
|
630
704
|
...toolMessageId !== void 0 ? { unstable_toolMessageId: toolMessageId } : {},
|
|
631
705
|
...mcpApp ? { mcp: { app: mcpApp } } : {},
|
|
@@ -653,7 +727,7 @@ function fromAgUiMessages(messages, options) {
|
|
|
653
727
|
args: {},
|
|
654
728
|
argsText: "{}",
|
|
655
729
|
result,
|
|
656
|
-
...
|
|
730
|
+
...toolModelContent ? { modelContent: toolModelContent } : {},
|
|
657
731
|
...isError !== void 0 ? { isError } : {},
|
|
658
732
|
...toolMessageId !== void 0 ? { unstable_toolMessageId: toolMessageId } : {},
|
|
659
733
|
...mcpApp ? { mcp: { app: mcpApp } } : {},
|
|
@@ -803,7 +877,8 @@ function fromAgUiMessages(messages, options) {
|
|
|
803
877
|
const message = folded[i];
|
|
804
878
|
if (message.role !== "assistant") continue;
|
|
805
879
|
const hasInterrupt = readPersistedInterrupts(message.metadata) !== void 0;
|
|
806
|
-
const
|
|
880
|
+
const pendingToolCallIds = readPersistedPendingToolCallIds(message.metadata);
|
|
881
|
+
const hasPendingToolCall = Array.isArray(message.content) && message.content.some((part) => isObject(part) && part.type === "tool-call" && part.result === void 0 && (pendingToolCallIds === void 0 || pendingToolCallIds.includes(getString(part, "toolCallId") ?? "")));
|
|
807
882
|
if (hasInterrupt || hasPendingToolCall) folded[i] = {
|
|
808
883
|
...message,
|
|
809
884
|
status: getAutoStatus(false, false, hasInterrupt, hasPendingToolCall, void 0)
|
|
@@ -857,21 +932,24 @@ function convertAssistantMessage(message, converted) {
|
|
|
857
932
|
emitToolResult(toolCallId, part, converted);
|
|
858
933
|
}
|
|
859
934
|
}
|
|
935
|
+
function isToolContentParts(value) {
|
|
936
|
+
return Array.isArray(value) && value.length > 0 && value.every((part) => isObject(part) && (part.type === "text" ? typeof part.text === "string" : mediaInputTypes.has(getString(part, "type") ?? "") && isObject(part.source) && typeof part.source.value === "string"));
|
|
937
|
+
}
|
|
860
938
|
function emitToolResult(toolCallId, part, converted) {
|
|
861
939
|
if (part.result === void 0) return;
|
|
862
|
-
const resultContent = part.modelContent !== void 0 ? extractText(part.modelContent) : typeof part.result === "string" ? part.result : JSON.stringify(part.result);
|
|
940
|
+
const resultContent = part.modelContent !== void 0 ? extractText(part.modelContent) : isToolContentParts(part.result) ? part.result : typeof part.result === "string" ? part.result : JSON.stringify(part.result);
|
|
863
941
|
const messageMetadata = readAgUiPartMessageMetadata(part.providerMetadata);
|
|
864
942
|
converted.push({
|
|
865
943
|
id: part.unstable_toolMessageId ?? `${toolCallId}:tool`,
|
|
866
944
|
role: "tool",
|
|
867
945
|
content: resultContent,
|
|
868
946
|
toolCallId,
|
|
869
|
-
...part.isError ? { error: resultContent } : {},
|
|
947
|
+
...part.isError ? { error: Array.isArray(resultContent) ? toolContentText(resultContent) : resultContent } : {},
|
|
870
948
|
...messageMetadata ? { metadata: messageMetadata } : {}
|
|
871
949
|
});
|
|
872
950
|
}
|
|
873
951
|
function convertToolMessage(message, converted) {
|
|
874
|
-
const content = extractText(message.content);
|
|
952
|
+
const content = isToolContentParts(message.content) ? message.content : extractText(message.content);
|
|
875
953
|
const toolCallId = message.toolCallId ?? generateId();
|
|
876
954
|
converted.push({
|
|
877
955
|
id: message.id,
|