@convex-dev/agent 0.3.2 → 0.6.0-alpha.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/UIMessages.d.ts +3 -2
- package/dist/UIMessages.d.ts.map +1 -1
- package/dist/UIMessages.js +100 -19
- package/dist/UIMessages.js.map +1 -1
- package/dist/client/createTool.d.ts +129 -25
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +65 -8
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/definePlaygroundAPI.d.ts +560 -35
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +4 -2
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +462 -37
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +26 -7
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +114 -9
- package/dist/client/messages.d.ts.map +1 -1
- package/dist/client/mockModel.d.ts +17 -17
- package/dist/client/mockModel.d.ts.map +1 -1
- package/dist/client/mockModel.js +8 -6
- package/dist/client/mockModel.js.map +1 -1
- package/dist/client/saveInputMessages.d.ts +1 -1
- package/dist/client/saveInputMessages.d.ts.map +1 -1
- package/dist/client/saveInputMessages.js +1 -1
- package/dist/client/saveInputMessages.js.map +1 -1
- package/dist/client/search.d.ts +132 -14
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +66 -19
- package/dist/client/search.js.map +1 -1
- package/dist/client/streamText.d.ts +4 -4
- package/dist/client/streamText.d.ts.map +1 -1
- package/dist/client/streamText.js.map +1 -1
- package/dist/client/streaming.d.ts +1491 -87
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/types.d.ts +26 -6
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/component.d.ts +862 -19
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/files.d.ts +12 -6
- package/dist/component/files.d.ts.map +1 -1
- package/dist/component/files.js +10 -2
- package/dist/component/files.js.map +1 -1
- package/dist/component/messages.d.ts +1247 -92
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/schema.d.ts +2823 -217
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +3 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.js +1 -1
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +10 -10
- package/dist/component/vector/index.d.ts +1 -1
- package/dist/deltas.d.ts.map +1 -1
- package/dist/deltas.js +40 -5
- package/dist/deltas.js.map +1 -1
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +117 -25
- package/dist/mapping.js.map +1 -1
- package/dist/react/useThreadMessages.d.ts.map +1 -1
- package/dist/react/useThreadMessages.js +17 -9
- package/dist/react/useThreadMessages.js.map +1 -1
- package/dist/shared.d.ts +2 -2
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +12196 -624
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +152 -9
- package/dist/validators.js.map +1 -1
- package/package.json +24 -22
- package/src/UIMessages.combineUIMessages.test.ts +239 -0
- package/src/UIMessages.ts +184 -66
- package/src/client/createTool.ts +291 -67
- package/src/client/files.ts +4 -2
- package/src/client/index.test.ts +1 -0
- package/src/client/index.ts +46 -25
- package/src/client/mockModel.ts +36 -34
- package/src/client/saveInputMessages.ts +2 -2
- package/src/client/search.test.ts +166 -0
- package/src/client/search.ts +121 -38
- package/src/client/streamText.ts +5 -5
- package/src/client/types.ts +31 -18
- package/src/component/_generated/component.ts +2329 -338
- package/src/component/files.ts +11 -2
- package/src/component/messages.test.ts +7 -7
- package/src/component/schema.ts +3 -1
- package/src/component/setup.test.ts +7 -0
- package/src/component/streams.ts +1 -1
- package/src/deltas.test.ts +90 -0
- package/src/deltas.ts +47 -8
- package/src/fromUIMessages.test.ts +32 -27
- package/src/mapping.test.ts +48 -0
- package/src/mapping.ts +234 -142
- package/src/react/useThreadMessages.ts +21 -9
- package/src/shared.ts +2 -0
- package/src/toUIMessages.test.ts +539 -1
- package/src/validators.ts +179 -20
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
package/src/mapping.ts
CHANGED
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
type Message,
|
|
26
26
|
type MessageWithMetadata,
|
|
27
27
|
type Usage,
|
|
28
|
-
type vContent,
|
|
29
28
|
type vFilePart,
|
|
30
29
|
type vImagePart,
|
|
31
30
|
type vReasoningPart,
|
|
@@ -36,6 +35,8 @@ import {
|
|
|
36
35
|
type SourcePart,
|
|
37
36
|
vToolResultOutput,
|
|
38
37
|
type MessageDoc,
|
|
38
|
+
vToolApprovalRequest,
|
|
39
|
+
vToolApprovalResponse,
|
|
39
40
|
} from "./validators.js";
|
|
40
41
|
import type { ActionCtx, AgentComponent } from "./client/types.js";
|
|
41
42
|
import type { MutationCtx } from "./client/types.js";
|
|
@@ -155,6 +156,17 @@ export function toModelMessageUsage(usage: Usage): LanguageModelUsage {
|
|
|
155
156
|
totalTokens: usage.totalTokens,
|
|
156
157
|
reasoningTokens: usage.reasoningTokens,
|
|
157
158
|
cachedInputTokens: usage.cachedInputTokens,
|
|
159
|
+
// These detail fields are required by LanguageModelUsage type but we don't
|
|
160
|
+
// have the granular data, so we provide empty objects with undefined values.
|
|
161
|
+
inputTokenDetails: {
|
|
162
|
+
cacheReadTokens: undefined,
|
|
163
|
+
cacheWriteTokens: undefined,
|
|
164
|
+
noCacheTokens: undefined,
|
|
165
|
+
},
|
|
166
|
+
outputTokenDetails: {
|
|
167
|
+
textTokens: undefined,
|
|
168
|
+
reasoningTokens: undefined,
|
|
169
|
+
},
|
|
158
170
|
};
|
|
159
171
|
}
|
|
160
172
|
|
|
@@ -165,18 +177,33 @@ export function serializeWarnings(
|
|
|
165
177
|
return undefined;
|
|
166
178
|
}
|
|
167
179
|
return warnings.map((warning) => {
|
|
168
|
-
if (warning.type
|
|
169
|
-
return
|
|
180
|
+
if (warning.type === "compatibility") {
|
|
181
|
+
return {
|
|
182
|
+
type: "unsupported-setting",
|
|
183
|
+
setting: warning.feature,
|
|
184
|
+
details: warning.details,
|
|
185
|
+
};
|
|
170
186
|
}
|
|
171
|
-
return
|
|
172
|
-
});
|
|
187
|
+
return warning;
|
|
188
|
+
}) as any;
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
export function toModelMessageWarnings(
|
|
176
192
|
warnings: MessageWithMetadata["warnings"],
|
|
177
193
|
): CallWarning[] | undefined {
|
|
178
|
-
|
|
179
|
-
|
|
194
|
+
if (!warnings) {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
return warnings.map((warning) => {
|
|
198
|
+
if (warning.type === "unsupported-setting") {
|
|
199
|
+
return {
|
|
200
|
+
type: "compatibility",
|
|
201
|
+
feature: warning.setting,
|
|
202
|
+
details: warning.details,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return warning;
|
|
206
|
+
}) as any;
|
|
180
207
|
}
|
|
181
208
|
|
|
182
209
|
export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
@@ -307,7 +334,7 @@ export async function serializeContent(
|
|
|
307
334
|
}
|
|
308
335
|
return {
|
|
309
336
|
type: part.type,
|
|
310
|
-
|
|
337
|
+
mediaType: getMimeOrMediaType(part),
|
|
311
338
|
...metadata,
|
|
312
339
|
image,
|
|
313
340
|
} satisfies Infer<typeof vImagePart>;
|
|
@@ -327,15 +354,18 @@ export async function serializeContent(
|
|
|
327
354
|
type: part.type,
|
|
328
355
|
data,
|
|
329
356
|
filename: part.filename,
|
|
330
|
-
|
|
357
|
+
mediaType: getMimeOrMediaType(part)!,
|
|
331
358
|
...metadata,
|
|
332
359
|
} satisfies Infer<typeof vFilePart>;
|
|
333
360
|
}
|
|
334
361
|
case "tool-call": {
|
|
335
|
-
|
|
362
|
+
// Handle legacy data where only args field exists
|
|
363
|
+
const input = part.input ?? (part as any)?.args ?? {};
|
|
336
364
|
return {
|
|
337
365
|
type: part.type,
|
|
338
|
-
|
|
366
|
+
input,
|
|
367
|
+
/** @deprecated Use `input` instead. */
|
|
368
|
+
args: input,
|
|
339
369
|
toolCallId: part.toolCallId,
|
|
340
370
|
toolName: part.toolName,
|
|
341
371
|
providerExecuted: part.providerExecuted,
|
|
@@ -363,13 +393,31 @@ export async function serializeContent(
|
|
|
363
393
|
case "source": {
|
|
364
394
|
return part satisfies Infer<typeof vSourcePart>;
|
|
365
395
|
}
|
|
396
|
+
case "tool-approval-request": {
|
|
397
|
+
return {
|
|
398
|
+
type: part.type,
|
|
399
|
+
approvalId: part.approvalId,
|
|
400
|
+
toolCallId: part.toolCallId,
|
|
401
|
+
...metadata,
|
|
402
|
+
} satisfies Infer<typeof vToolApprovalRequest>;
|
|
403
|
+
}
|
|
404
|
+
case "tool-approval-response": {
|
|
405
|
+
return {
|
|
406
|
+
type: part.type,
|
|
407
|
+
approvalId: part.approvalId,
|
|
408
|
+
approved: part.approved,
|
|
409
|
+
reason: part.reason,
|
|
410
|
+
providerExecuted: part.providerExecuted,
|
|
411
|
+
...metadata,
|
|
412
|
+
} satisfies Infer<typeof vToolApprovalResponse>;
|
|
413
|
+
}
|
|
366
414
|
default:
|
|
367
|
-
return
|
|
415
|
+
return null;
|
|
368
416
|
}
|
|
369
417
|
}),
|
|
370
418
|
);
|
|
371
419
|
return {
|
|
372
|
-
content: serialized as SerializedContent,
|
|
420
|
+
content: serialized.filter((p) => p !== null) as SerializedContent,
|
|
373
421
|
fileIds: fileIds.length > 0 ? fileIds : undefined,
|
|
374
422
|
};
|
|
375
423
|
}
|
|
@@ -378,57 +426,78 @@ export function fromModelMessageContent(content: Content): Message["content"] {
|
|
|
378
426
|
if (typeof content === "string") {
|
|
379
427
|
return content;
|
|
380
428
|
}
|
|
381
|
-
return content
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
429
|
+
return content
|
|
430
|
+
.map((part) => {
|
|
431
|
+
const metadata: {
|
|
432
|
+
providerOptions?: ProviderOptions;
|
|
433
|
+
providerMetadata?: ProviderMetadata;
|
|
434
|
+
} = {};
|
|
435
|
+
if ("providerOptions" in part) {
|
|
436
|
+
metadata.providerOptions = part.providerOptions as ProviderOptions;
|
|
437
|
+
}
|
|
438
|
+
if ("providerMetadata" in part) {
|
|
439
|
+
metadata.providerMetadata = part.providerMetadata as ProviderMetadata;
|
|
440
|
+
}
|
|
441
|
+
switch (part.type) {
|
|
442
|
+
case "text":
|
|
443
|
+
return part satisfies Infer<typeof vTextPart>;
|
|
444
|
+
case "image":
|
|
445
|
+
return {
|
|
446
|
+
type: part.type,
|
|
447
|
+
mediaType: getMimeOrMediaType(part),
|
|
448
|
+
...metadata,
|
|
449
|
+
image: serializeDataOrUrl(part.image),
|
|
450
|
+
} satisfies Infer<typeof vImagePart>;
|
|
451
|
+
case "file":
|
|
452
|
+
return {
|
|
453
|
+
type: part.type,
|
|
454
|
+
data: serializeDataOrUrl(part.data),
|
|
455
|
+
filename: part.filename,
|
|
456
|
+
mediaType: getMimeOrMediaType(part)!,
|
|
457
|
+
...metadata,
|
|
458
|
+
} satisfies Infer<typeof vFilePart>;
|
|
459
|
+
case "tool-call":
|
|
460
|
+
// Handle legacy data where only args field exists
|
|
461
|
+
return {
|
|
462
|
+
type: part.type,
|
|
463
|
+
input: part.input ?? (part as any)?.args ?? {},
|
|
464
|
+
/** @deprecated Use `input` instead. */
|
|
465
|
+
args: part.input ?? (part as any)?.args ?? {},
|
|
466
|
+
toolCallId: part.toolCallId,
|
|
467
|
+
toolName: part.toolName,
|
|
468
|
+
providerExecuted: part.providerExecuted,
|
|
469
|
+
...metadata,
|
|
470
|
+
} satisfies Infer<typeof vToolCallPart>;
|
|
471
|
+
case "tool-result":
|
|
472
|
+
return normalizeToolResult(part, metadata);
|
|
473
|
+
case "reasoning":
|
|
474
|
+
return {
|
|
475
|
+
type: part.type,
|
|
476
|
+
text: part.text,
|
|
477
|
+
...metadata,
|
|
478
|
+
} satisfies Infer<typeof vReasoningPart>;
|
|
479
|
+
case "tool-approval-request":
|
|
480
|
+
return {
|
|
481
|
+
type: part.type,
|
|
482
|
+
approvalId: part.approvalId,
|
|
483
|
+
toolCallId: part.toolCallId,
|
|
484
|
+
...metadata,
|
|
485
|
+
} satisfies Infer<typeof vToolApprovalRequest>;
|
|
486
|
+
case "tool-approval-response":
|
|
487
|
+
return {
|
|
488
|
+
type: part.type,
|
|
489
|
+
approvalId: part.approvalId,
|
|
490
|
+
approved: part.approved,
|
|
491
|
+
reason: part.reason,
|
|
492
|
+
providerExecuted: part.providerExecuted,
|
|
493
|
+
...metadata,
|
|
494
|
+
} satisfies Infer<typeof vToolApprovalResponse>;
|
|
495
|
+
// Not in current generation output, but could be in historical messages
|
|
496
|
+
default:
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
})
|
|
500
|
+
.filter((p) => p !== null) as Message["content"];
|
|
432
501
|
}
|
|
433
502
|
|
|
434
503
|
export function toModelMessageContent(
|
|
@@ -437,84 +506,103 @@ export function toModelMessageContent(
|
|
|
437
506
|
if (typeof content === "string") {
|
|
438
507
|
return content;
|
|
439
508
|
}
|
|
440
|
-
return content
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if ("providerMetadata" in part) {
|
|
449
|
-
metadata.providerMetadata = part.providerMetadata;
|
|
450
|
-
}
|
|
451
|
-
switch (part.type) {
|
|
452
|
-
case "text":
|
|
453
|
-
return {
|
|
454
|
-
type: part.type,
|
|
455
|
-
text: part.text,
|
|
456
|
-
...metadata,
|
|
457
|
-
} satisfies TextPart;
|
|
458
|
-
case "image":
|
|
459
|
-
return {
|
|
460
|
-
type: part.type,
|
|
461
|
-
image: toModelMessageDataOrUrl(part.image),
|
|
462
|
-
mediaType: getMimeOrMediaType(part),
|
|
463
|
-
...metadata,
|
|
464
|
-
} satisfies ImagePart;
|
|
465
|
-
case "file":
|
|
466
|
-
return {
|
|
467
|
-
type: part.type,
|
|
468
|
-
data: toModelMessageDataOrUrl(part.data),
|
|
469
|
-
filename: part.filename,
|
|
470
|
-
mediaType: getMimeOrMediaType(part)!,
|
|
471
|
-
...metadata,
|
|
472
|
-
} satisfies FilePart;
|
|
473
|
-
case "tool-call": {
|
|
474
|
-
const input = "input" in part ? part.input : part.args;
|
|
475
|
-
return {
|
|
476
|
-
type: part.type,
|
|
477
|
-
input: input ?? null,
|
|
478
|
-
toolCallId: part.toolCallId,
|
|
479
|
-
toolName: part.toolName,
|
|
480
|
-
providerExecuted: part.providerExecuted,
|
|
481
|
-
...metadata,
|
|
482
|
-
} satisfies ToolCallPart;
|
|
509
|
+
return content
|
|
510
|
+
.map((part) => {
|
|
511
|
+
const metadata: {
|
|
512
|
+
providerOptions?: ProviderOptions;
|
|
513
|
+
providerMetadata?: ProviderMetadata;
|
|
514
|
+
} = {};
|
|
515
|
+
if ("providerOptions" in part) {
|
|
516
|
+
metadata.providerOptions = part.providerOptions;
|
|
483
517
|
}
|
|
484
|
-
|
|
485
|
-
|
|
518
|
+
if ("providerMetadata" in part) {
|
|
519
|
+
metadata.providerMetadata = part.providerMetadata;
|
|
486
520
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
521
|
+
switch (part.type) {
|
|
522
|
+
case "text":
|
|
523
|
+
return {
|
|
524
|
+
type: part.type,
|
|
525
|
+
text: part.text,
|
|
526
|
+
...metadata,
|
|
527
|
+
} satisfies TextPart;
|
|
528
|
+
case "image":
|
|
529
|
+
return {
|
|
530
|
+
type: part.type,
|
|
531
|
+
image: toModelMessageDataOrUrl(part.image),
|
|
532
|
+
mediaType: getMimeOrMediaType(part),
|
|
533
|
+
...metadata,
|
|
534
|
+
} satisfies ImagePart;
|
|
535
|
+
case "file":
|
|
536
|
+
return {
|
|
537
|
+
type: part.type,
|
|
538
|
+
data: toModelMessageDataOrUrl(part.data),
|
|
539
|
+
filename: part.filename,
|
|
540
|
+
mediaType: getMimeOrMediaType(part)!,
|
|
541
|
+
...metadata,
|
|
542
|
+
} satisfies FilePart;
|
|
543
|
+
case "tool-call": {
|
|
544
|
+
// Handle legacy data where only args field exists
|
|
545
|
+
const input = part.input ?? (part as any)?.args ?? {};
|
|
546
|
+
return {
|
|
547
|
+
type: part.type,
|
|
548
|
+
input,
|
|
549
|
+
toolCallId: part.toolCallId,
|
|
550
|
+
toolName: part.toolName,
|
|
551
|
+
providerExecuted: part.providerExecuted,
|
|
552
|
+
...metadata,
|
|
553
|
+
} satisfies ToolCallPart;
|
|
554
|
+
}
|
|
555
|
+
case "tool-result": {
|
|
556
|
+
return normalizeToolResult(part, metadata);
|
|
557
|
+
}
|
|
558
|
+
case "reasoning":
|
|
559
|
+
return {
|
|
560
|
+
type: part.type,
|
|
561
|
+
text: part.text,
|
|
562
|
+
...metadata,
|
|
563
|
+
} satisfies ReasoningPart;
|
|
564
|
+
case "redacted-reasoning":
|
|
565
|
+
// TODO: should we just drop this?
|
|
566
|
+
return {
|
|
567
|
+
type: "reasoning",
|
|
568
|
+
text: "",
|
|
569
|
+
...metadata,
|
|
570
|
+
providerOptions: metadata.providerOptions
|
|
571
|
+
? {
|
|
572
|
+
...Object.fromEntries(
|
|
573
|
+
Object.entries(metadata.providerOptions ?? {}).map(
|
|
574
|
+
([key, value]) => [
|
|
575
|
+
key,
|
|
576
|
+
{ ...value, redactedData: part.data },
|
|
577
|
+
],
|
|
578
|
+
),
|
|
507
579
|
),
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
580
|
+
}
|
|
581
|
+
: undefined,
|
|
582
|
+
} satisfies ReasoningPart;
|
|
583
|
+
case "source":
|
|
584
|
+
return part satisfies SourcePart;
|
|
585
|
+
case "tool-approval-request":
|
|
586
|
+
return {
|
|
587
|
+
type: part.type,
|
|
588
|
+
approvalId: part.approvalId,
|
|
589
|
+
toolCallId: part.toolCallId,
|
|
590
|
+
...metadata,
|
|
591
|
+
} satisfies Infer<typeof vToolApprovalRequest>;
|
|
592
|
+
case "tool-approval-response":
|
|
593
|
+
return {
|
|
594
|
+
type: part.type,
|
|
595
|
+
approvalId: part.approvalId,
|
|
596
|
+
approved: part.approved,
|
|
597
|
+
reason: part.reason,
|
|
598
|
+
providerExecuted: part.providerExecuted,
|
|
599
|
+
...metadata,
|
|
600
|
+
} satisfies Infer<typeof vToolApprovalResponse>;
|
|
601
|
+
default:
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
})
|
|
605
|
+
.filter((p) => p !== null) as Content;
|
|
518
606
|
}
|
|
519
607
|
|
|
520
608
|
export function normalizeToolOutput(
|
|
@@ -544,11 +632,15 @@ function normalizeToolResult(
|
|
|
544
632
|
): ToolResultPart & Infer<typeof vToolResultPart> {
|
|
545
633
|
return {
|
|
546
634
|
type: part.type,
|
|
547
|
-
output:
|
|
548
|
-
part.output
|
|
549
|
-
|
|
635
|
+
output: part.output
|
|
636
|
+
? validate(vToolResultOutput, part.output)
|
|
637
|
+
? (part.output as any)
|
|
638
|
+
: normalizeToolOutput(JSON.stringify(part.output))
|
|
639
|
+
: normalizeToolOutput("result" in part ? part.result : undefined),
|
|
550
640
|
toolCallId: part.toolCallId,
|
|
551
641
|
toolName: part.toolName,
|
|
642
|
+
// Preserve isError flag for error reporting
|
|
643
|
+
...("isError" in part && part.isError ? { isError: true } : {}),
|
|
552
644
|
...metadata,
|
|
553
645
|
} satisfies ToolResultPart;
|
|
554
646
|
}
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
PaginationOptions,
|
|
17
17
|
PaginationResult,
|
|
18
18
|
} from "convex/server";
|
|
19
|
-
import { useMemo } from "react";
|
|
19
|
+
import { useMemo, useState, useEffect, useRef } from "react";
|
|
20
20
|
import type { SyncStreamsReturnValue } from "../client/types.js";
|
|
21
21
|
import { sorted } from "../shared.js";
|
|
22
22
|
import { fromUIMessages } from "../UIMessages.js";
|
|
@@ -239,12 +239,24 @@ export function useStreamingThreadMessages<Query extends StreamQuery<any>>(
|
|
|
239
239
|
args === "skip" ? undefined : (args.startOrder ?? undefined);
|
|
240
240
|
const queryOptions = { startOrder, ...options };
|
|
241
241
|
const uiMessages = useStreamingUIMessages(query, queryArgs, queryOptions);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
242
|
+
const [messages, setMessages] = useState<Array<MessageDoc> | undefined>();
|
|
243
|
+
const generationRef = useRef(0);
|
|
244
|
+
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
if (args === "skip" || !uiMessages) {
|
|
247
|
+
setMessages(undefined);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const currentGeneration = ++generationRef.current;
|
|
251
|
+
(async () => {
|
|
252
|
+
const nested = await Promise.all(
|
|
253
|
+
uiMessages.map((m) => fromUIMessages([m], { threadId: args.threadId })),
|
|
254
|
+
);
|
|
255
|
+
if (generationRef.current === currentGeneration) {
|
|
256
|
+
setMessages(nested.flat());
|
|
257
|
+
}
|
|
258
|
+
})();
|
|
259
|
+
}, [uiMessages, args === "skip" ? undefined : args.threadId]);
|
|
260
|
+
|
|
261
|
+
return messages;
|
|
250
262
|
}
|
package/src/shared.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ReasoningPart,
|
|
5
5
|
ToolCallPart,
|
|
6
6
|
ToolResultPart,
|
|
7
|
+
ToolApprovalRequest,
|
|
7
8
|
} from "@ai-sdk/provider-utils";
|
|
8
9
|
import type {
|
|
9
10
|
ModelMessage,
|
|
@@ -55,6 +56,7 @@ export function joinText(
|
|
|
55
56
|
| ToolCallPart
|
|
56
57
|
| ToolResultPart
|
|
57
58
|
| MessageContentParts
|
|
59
|
+
| ToolApprovalRequest
|
|
58
60
|
)[],
|
|
59
61
|
) {
|
|
60
62
|
return parts
|