@convex-dev/agent 0.0.16 → 0.0.17-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.
Files changed (108) hide show
  1. package/README.md +159 -98
  2. package/dist/commonjs/client/index.d.ts +806 -189
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +178 -28
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/commonjs/client/playground.d.ts +609 -0
  7. package/dist/commonjs/client/playground.d.ts.map +1 -0
  8. package/dist/commonjs/client/playground.js +200 -0
  9. package/dist/commonjs/client/playground.js.map +1 -0
  10. package/dist/commonjs/component/apiKeys.d.ts +11 -0
  11. package/dist/commonjs/component/apiKeys.d.ts.map +1 -0
  12. package/dist/commonjs/component/apiKeys.js +69 -0
  13. package/dist/commonjs/component/apiKeys.js.map +1 -0
  14. package/dist/commonjs/component/files.d.ts +31 -0
  15. package/dist/commonjs/component/files.d.ts.map +1 -0
  16. package/dist/commonjs/component/files.js +61 -0
  17. package/dist/commonjs/component/files.js.map +1 -0
  18. package/dist/commonjs/component/messages.d.ts +720 -676
  19. package/dist/commonjs/component/messages.d.ts.map +1 -1
  20. package/dist/commonjs/component/messages.js +99 -287
  21. package/dist/commonjs/component/messages.js.map +1 -1
  22. package/dist/commonjs/component/schema.d.ts +1124 -1324
  23. package/dist/commonjs/component/schema.d.ts.map +1 -1
  24. package/dist/commonjs/component/schema.js +26 -14
  25. package/dist/commonjs/component/schema.js.map +1 -1
  26. package/dist/commonjs/component/threads.d.ts +95 -0
  27. package/dist/commonjs/component/threads.d.ts.map +1 -0
  28. package/dist/commonjs/component/threads.js +151 -0
  29. package/dist/commonjs/component/threads.js.map +1 -0
  30. package/dist/commonjs/component/users.d.ts +37 -0
  31. package/dist/commonjs/component/users.d.ts.map +1 -0
  32. package/dist/commonjs/component/users.js +118 -0
  33. package/dist/commonjs/component/users.js.map +1 -0
  34. package/dist/commonjs/component/vector/index.d.ts.map +1 -1
  35. package/dist/commonjs/component/vector/index.js +4 -6
  36. package/dist/commonjs/component/vector/index.js.map +1 -1
  37. package/dist/commonjs/component/vector/tables.d.ts +10 -10
  38. package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
  39. package/dist/commonjs/component/vector/tables.js.map +1 -1
  40. package/dist/commonjs/mapping.d.ts +7 -1
  41. package/dist/commonjs/mapping.d.ts.map +1 -1
  42. package/dist/commonjs/mapping.js +39 -18
  43. package/dist/commonjs/mapping.js.map +1 -1
  44. package/dist/commonjs/validators.d.ts +1669 -2340
  45. package/dist/commonjs/validators.d.ts.map +1 -1
  46. package/dist/commonjs/validators.js +35 -26
  47. package/dist/commonjs/validators.js.map +1 -1
  48. package/dist/esm/client/index.d.ts +806 -189
  49. package/dist/esm/client/index.d.ts.map +1 -1
  50. package/dist/esm/client/index.js +178 -28
  51. package/dist/esm/client/index.js.map +1 -1
  52. package/dist/esm/client/playground.d.ts +609 -0
  53. package/dist/esm/client/playground.d.ts.map +1 -0
  54. package/dist/esm/client/playground.js +200 -0
  55. package/dist/esm/client/playground.js.map +1 -0
  56. package/dist/esm/component/apiKeys.d.ts +11 -0
  57. package/dist/esm/component/apiKeys.d.ts.map +1 -0
  58. package/dist/esm/component/apiKeys.js +69 -0
  59. package/dist/esm/component/apiKeys.js.map +1 -0
  60. package/dist/esm/component/files.d.ts +31 -0
  61. package/dist/esm/component/files.d.ts.map +1 -0
  62. package/dist/esm/component/files.js +61 -0
  63. package/dist/esm/component/files.js.map +1 -0
  64. package/dist/esm/component/messages.d.ts +720 -676
  65. package/dist/esm/component/messages.d.ts.map +1 -1
  66. package/dist/esm/component/messages.js +99 -287
  67. package/dist/esm/component/messages.js.map +1 -1
  68. package/dist/esm/component/schema.d.ts +1124 -1324
  69. package/dist/esm/component/schema.d.ts.map +1 -1
  70. package/dist/esm/component/schema.js +26 -14
  71. package/dist/esm/component/schema.js.map +1 -1
  72. package/dist/esm/component/threads.d.ts +95 -0
  73. package/dist/esm/component/threads.d.ts.map +1 -0
  74. package/dist/esm/component/threads.js +151 -0
  75. package/dist/esm/component/threads.js.map +1 -0
  76. package/dist/esm/component/users.d.ts +37 -0
  77. package/dist/esm/component/users.d.ts.map +1 -0
  78. package/dist/esm/component/users.js +118 -0
  79. package/dist/esm/component/users.js.map +1 -0
  80. package/dist/esm/component/vector/index.d.ts.map +1 -1
  81. package/dist/esm/component/vector/index.js +4 -6
  82. package/dist/esm/component/vector/index.js.map +1 -1
  83. package/dist/esm/component/vector/tables.d.ts +10 -10
  84. package/dist/esm/component/vector/tables.d.ts.map +1 -1
  85. package/dist/esm/component/vector/tables.js.map +1 -1
  86. package/dist/esm/mapping.d.ts +7 -1
  87. package/dist/esm/mapping.d.ts.map +1 -1
  88. package/dist/esm/mapping.js +39 -18
  89. package/dist/esm/mapping.js.map +1 -1
  90. package/dist/esm/validators.d.ts +1669 -2340
  91. package/dist/esm/validators.d.ts.map +1 -1
  92. package/dist/esm/validators.js +35 -26
  93. package/dist/esm/validators.js.map +1 -1
  94. package/package.json +8 -1
  95. package/src/client/index.ts +220 -59
  96. package/src/client/playground.ts +257 -0
  97. package/src/component/_generated/api.d.ts +601 -400
  98. package/src/component/apiKeys.ts +74 -0
  99. package/src/component/files.ts +72 -0
  100. package/src/component/messages.test.ts +23 -0
  101. package/src/component/messages.ts +127 -341
  102. package/src/component/schema.ts +32 -13
  103. package/src/component/threads.ts +184 -0
  104. package/src/component/users.ts +145 -0
  105. package/src/component/vector/index.ts +5 -6
  106. package/src/component/vector/tables.ts +15 -15
  107. package/src/mapping.ts +65 -32
  108. package/src/validators.ts +47 -28
@@ -15,7 +15,9 @@ import type {
15
15
  ToolChoice,
16
16
  ToolExecutionOptions,
17
17
  ToolSet,
18
+ UIMessage,
18
19
  } from "ai";
20
+ import type { ToolInvocationUIPart } from "@ai-sdk/ui-utils";
19
21
  import {
20
22
  generateObject,
21
23
  generateText,
@@ -40,6 +42,7 @@ import {
40
42
  serializeNewMessagesInStep,
41
43
  serializeObjectResult,
42
44
  serializeStep,
45
+ toUIFilePart,
43
46
  } from "../mapping.js";
44
47
  import {
45
48
  DEFAULT_MESSAGE_RANGE,
@@ -49,13 +52,15 @@ import {
49
52
  } from "../shared.js";
50
53
  import {
51
54
  type CallSettings,
52
- MessageWithMetadata,
55
+ type MessageWithMetadata as InnerMessageWithMetadata,
53
56
  type ProviderMetadata,
54
57
  type ProviderOptions,
55
58
  type SearchOptions,
56
59
  type Usage,
60
+ vFileWithStringId,
57
61
  vSafeObjectArgs,
58
62
  vTextArgs,
63
+ vThreadStatus,
59
64
  } from "../validators.js";
60
65
  import type {
61
66
  OpaqueIds,
@@ -66,38 +71,33 @@ import type {
66
71
  } from "./types.js";
67
72
  import schema from "../component/schema.js";
68
73
 
74
+ export { extractText, isTool };
69
75
  export type { Usage, ProviderMetadata };
70
76
  export {
77
+ paginationResultValidator,
78
+ vContextOptions,
71
79
  vUsage,
72
80
  vProviderMetadata,
73
81
  vUserMessage,
74
82
  vAssistantMessage,
75
83
  vToolMessage,
84
+ vStorageOptions,
76
85
  vSystemMessage,
77
86
  vMessage,
78
87
  } from "../validators.js";
79
88
 
80
- export type ThreadDoc = OpaqueIds<
81
- { _id: string; _creationTime: number } & Infer<
82
- typeof schema.tables.threads.validator
83
- >
84
- >;
85
- export type MessageDoc = OpaqueIds<
86
- { _id: string; _creationTime: number } & Infer<
87
- typeof schema.tables.messages.validator
88
- >
89
- >;
90
-
91
89
  /**
92
90
  * Options to configure what messages are fetched as context,
93
91
  * automatically with thread.generateText, or directly via search.
94
92
  */
95
93
  export type ContextOptions = {
94
+ /** @deprecated Use excludeToolMessages instead. */
95
+ includeToolCalls?: boolean;
96
96
  /**
97
97
  * Whether to include tool messages in the context.
98
98
  * By default, tool calls and results are not included.
99
99
  */
100
- includeToolCalls?: boolean;
100
+ excludeToolMessages?: boolean;
101
101
  /**
102
102
  * How many recent messages to include. These are added after the search
103
103
  * messages, and do not count against the search limit.
@@ -172,9 +172,11 @@ export type UsageHandler = (
172
172
  }
173
173
  ) => void | Promise<void>;
174
174
 
175
+ export type AgentComponent = UseApi<Mounts>;
176
+
175
177
  export class Agent<AgentTools extends ToolSet> {
176
178
  constructor(
177
- public component: UseApi<Mounts>,
179
+ public component: AgentComponent,
178
180
  public options: {
179
181
  /**
180
182
  * The name for the agent. This will be attributed on each message
@@ -334,7 +336,7 @@ export class Agent<AgentTools extends ToolSet> {
334
336
  thread?: Thread<ThreadTools extends undefined ? AgentTools : ThreadTools>;
335
337
  }> {
336
338
  const threadDoc = await ctx.runMutation(
337
- this.component.messages.createThread,
339
+ this.component.threads.createThread,
338
340
  {
339
341
  userId: args?.userId,
340
342
  title: args?.title,
@@ -415,10 +417,15 @@ export class Agent<AgentTools extends ToolSet> {
415
417
  userId: string | undefined;
416
418
  threadId: string | undefined;
417
419
  messages: CoreMessage[];
418
- parentMessageId?: string;
420
+ /**
421
+ * If provided, it will search for messages before this message.
422
+ * Note: if this is far in the past, the search results may be more
423
+ * limited, as it's post-filtering the results.
424
+ */
425
+ beforeMessageId?: string;
419
426
  contextOptions: ContextOptions | undefined;
420
427
  }
421
- ): Promise<CoreMessage[]> {
428
+ ): Promise<MessageDoc[]> {
422
429
  assert(args.userId || args.threadId, "Specify userId or threadId");
423
430
  // Fetch the latest messages from the thread
424
431
  const contextMessages: MessageDoc[] = [];
@@ -431,11 +438,9 @@ export class Agent<AgentTools extends ToolSet> {
431
438
  const searchMessages = await ctx.runAction(
432
439
  this.component.messages.searchMessages,
433
440
  {
434
- userId: args.contextOptions?.searchOtherThreads
435
- ? args.userId
436
- : undefined,
441
+ userId: opts?.searchOtherThreads ? args.userId : undefined,
437
442
  threadId: args.threadId,
438
- parentMessageId: args.parentMessageId,
443
+ beforeMessageId: args.beforeMessageId,
439
444
  ...(await this.searchOptionsWithDefaults(opts, args.messages)),
440
445
  }
441
446
  );
@@ -445,15 +450,16 @@ export class Agent<AgentTools extends ToolSet> {
445
450
  }
446
451
  if (args.threadId && opts.recentMessages !== 0) {
447
452
  const { page } = await ctx.runQuery(
448
- this.component.messages.getThreadMessages,
453
+ this.component.messages.listMessagesByThreadId,
449
454
  {
450
455
  threadId: args.threadId,
451
- isTool: opts.includeToolCalls ? undefined : false,
456
+ excludeToolMessages:
457
+ opts.includeToolCalls === true ? false : opts.excludeToolMessages,
452
458
  paginationOpts: {
453
459
  numItems: opts.recentMessages ?? DEFAULT_RECENT_MESSAGES,
454
460
  cursor: null,
455
461
  },
456
- parentMessageId: args.parentMessageId,
462
+ beforeMessageId: args.beforeMessageId,
457
463
  order: "desc",
458
464
  statuses: ["success"],
459
465
  }
@@ -463,15 +469,12 @@ export class Agent<AgentTools extends ToolSet> {
463
469
  ...page.filter((m) => !included?.has(m._id)).reverse()
464
470
  );
465
471
  }
466
-
467
- // Sort the raw MessageDocs by order and stepOrder
468
- const sortedDocs = contextMessages.sort((a, b) =>
469
- a.order === b.order ? a.stepOrder - b.stepOrder : a.order - b.order
470
- );
471
-
472
472
  // Ensure we don't include tool messages without a corresponding tool call
473
- return filterOutOrphanedToolMessages(sortedDocs).map((m) =>
474
- deserializeMessage(m.message!)
473
+ return filterOutOrphanedToolMessages(
474
+ contextMessages.sort((a, b) =>
475
+ // Sort the raw MessageDocs by order and stepOrder
476
+ a.order === b.order ? a.stepOrder - b.stepOrder : a.order - b.order
477
+ )
475
478
  );
476
479
  }
477
480
 
@@ -544,13 +547,8 @@ export class Agent<AgentTools extends ToolSet> {
544
547
  */
545
548
  pending?: boolean;
546
549
  /**
547
- * The message that this is responding to.
548
- */
549
- parentMessageId?: string;
550
- /**
551
- * Whether to mark all pending messages in the thread as failed.
552
- * This is used to recover from a failure via a retry that wipes the slate clean.
553
- * Defaults to true.
550
+ * If true, it will fail any pending steps.
551
+ * Defaults to false.
554
552
  */
555
553
  failPendingSteps?: boolean;
556
554
  }
@@ -575,9 +573,8 @@ export class Agent<AgentTools extends ToolSet> {
575
573
  message: serializeMessage(m),
576
574
  }) as MessageWithMetadata
577
575
  ),
578
- failPendingSteps: args.failPendingSteps ?? true,
576
+ failPendingSteps: args.failPendingSteps ?? false,
579
577
  pending: args.pending ?? false,
580
- parentMessageId: args.parentMessageId,
581
578
  });
582
579
  return {
583
580
  lastMessageId: result.messages.at(-1)!._id,
@@ -598,7 +595,7 @@ export class Agent<AgentTools extends ToolSet> {
598
595
  /**
599
596
  * The message this step is in response to.
600
597
  */
601
- messageId: string;
598
+ parentMessageId: string;
602
599
  /**
603
600
  * The step to save, possibly including multiple tool calls.
604
601
  */
@@ -635,7 +632,7 @@ export class Agent<AgentTools extends ToolSet> {
635
632
  await ctx.runMutation(this.component.messages.addStep, {
636
633
  userId: args.userId,
637
634
  threadId: args.threadId,
638
- messageId: args.messageId,
635
+ parentMessageId: args.parentMessageId,
639
636
  step: { step, messages },
640
637
  failPendingSteps: false,
641
638
  });
@@ -741,7 +738,7 @@ export class Agent<AgentTools extends ToolSet> {
741
738
  await this.saveStep(ctx, {
742
739
  userId,
743
740
  threadId,
744
- messageId,
741
+ parentMessageId: messageId,
745
742
  step,
746
743
  });
747
744
  }
@@ -863,7 +860,7 @@ export class Agent<AgentTools extends ToolSet> {
863
860
  await this.saveStep(ctx, {
864
861
  userId,
865
862
  threadId,
866
- messageId,
863
+ parentMessageId: messageId,
867
864
  step,
868
865
  });
869
866
  }
@@ -902,7 +899,6 @@ export class Agent<AgentTools extends ToolSet> {
902
899
  {
903
900
  userId,
904
901
  threadId,
905
- parentMessageId,
906
902
  contextOptions,
907
903
  storageOptions,
908
904
  }: {
@@ -920,7 +916,6 @@ export class Agent<AgentTools extends ToolSet> {
920
916
  userId,
921
917
  threadId,
922
918
  messages,
923
- parentMessageId,
924
919
  contextOptions,
925
920
  });
926
921
  let messageId: string | undefined;
@@ -933,9 +928,7 @@ export class Agent<AgentTools extends ToolSet> {
933
928
  messages: coreMessages,
934
929
  metadata: coreMessages.length === 1 ? [{ id: args.id }] : undefined,
935
930
  pending: true,
936
- // We should just fail if you pass in an ID for the message, fail those children
937
- // failPendingSteps: true,
938
- parentMessageId,
931
+ failPendingSteps: true,
939
932
  });
940
933
  messageId = saved.lastMessageId;
941
934
  }
@@ -944,7 +937,10 @@ export class Agent<AgentTools extends ToolSet> {
944
937
  args: {
945
938
  ...rest,
946
939
  system: args.system ?? this.options.instructions,
947
- messages: [...contextMessages, ...messages],
940
+ messages: [
941
+ ...contextMessages.map((m) => deserializeMessage(m.message!)),
942
+ ...messages,
943
+ ],
948
944
  } as T,
949
945
  messageId,
950
946
  };
@@ -993,7 +989,12 @@ export class Agent<AgentTools extends ToolSet> {
993
989
  } as any)) as GenerateObjectResult<T> & GenerationOutputMetadata;
994
990
 
995
991
  if (threadId && messageId && saveOutputMessages !== false) {
996
- await this.saveObject(ctx, { threadId, messageId, result, userId });
992
+ await this.saveObject(ctx, {
993
+ threadId,
994
+ parentMessageId: messageId,
995
+ result,
996
+ userId,
997
+ });
997
998
  }
998
999
  result.messageId = messageId;
999
1000
  if (trackUsage && result.usage) {
@@ -1070,7 +1071,7 @@ export class Agent<AgentTools extends ToolSet> {
1070
1071
  await this.saveObject(ctx, {
1071
1072
  userId,
1072
1073
  threadId,
1073
- messageId,
1074
+ parentMessageId: messageId,
1074
1075
  result: {
1075
1076
  object: result.object,
1076
1077
  finishReason: "stop",
@@ -1118,7 +1119,7 @@ export class Agent<AgentTools extends ToolSet> {
1118
1119
  args: {
1119
1120
  userId: string | undefined;
1120
1121
  threadId: string;
1121
- messageId: string;
1122
+ parentMessageId: string;
1122
1123
  result: GenerateObjectResult<unknown>;
1123
1124
  metadata?: Omit<MessageWithMetadata, "message">;
1124
1125
  }
@@ -1147,7 +1148,7 @@ export class Agent<AgentTools extends ToolSet> {
1147
1148
  await ctx.runMutation(this.component.messages.addStep, {
1148
1149
  userId: args.userId,
1149
1150
  threadId: args.threadId,
1150
- messageId: args.messageId,
1151
+ parentMessageId: args.parentMessageId,
1151
1152
  failPendingSteps: false,
1152
1153
  step: { step, messages },
1153
1154
  });
@@ -1442,10 +1443,6 @@ function wrapTools(
1442
1443
  }
1443
1444
 
1444
1445
  type Options = {
1445
- /**
1446
- * The parent message id to use for the tool calls.
1447
- */
1448
- parentMessageId?: string;
1449
1446
  /**
1450
1447
  * The context options to use for passing in message history to the LLM.
1451
1448
  */
@@ -1726,3 +1723,167 @@ interface Thread<DefaultTools extends ToolSet> {
1726
1723
  StreamObjectResult<DeepPartial<T>, T, never> & ThreadOutputMetadata
1727
1724
  >;
1728
1725
  }
1726
+
1727
+ export const vThreadDoc = v.object({
1728
+ _id: v.string(),
1729
+ _creationTime: v.number(),
1730
+ userId: v.optional(v.string()), // Unset for anonymous
1731
+ title: v.optional(v.string()),
1732
+ summary: v.optional(v.string()),
1733
+ status: vThreadStatus,
1734
+ });
1735
+ export type ThreadDoc = Infer<typeof vThreadDoc>;
1736
+
1737
+ export const vMessageDoc = v.object({
1738
+ _id: v.string(),
1739
+ _creationTime: v.number(),
1740
+ ...schema.tables.messages.validator.fields,
1741
+ // Overwrite all the types that have a v.id validator
1742
+ // Outside of the component, they are strings
1743
+ threadId: v.string(),
1744
+ parentMessageId: v.optional(v.string()),
1745
+ stepId: v.optional(v.string()),
1746
+ embeddingId: v.optional(v.string()),
1747
+ files: v.optional(v.array(vFileWithStringId)),
1748
+ });
1749
+ export type MessageDoc = Infer<typeof vMessageDoc>;
1750
+
1751
+ type MessageWithMetadata = OpaqueIds<InnerMessageWithMetadata>;
1752
+
1753
+ export function toUIMessages(messages: MessageDoc[]): UIMessage[] {
1754
+ const uiMessages: UIMessage[] = [];
1755
+ let assistantMessage: UIMessage | undefined;
1756
+ for (const message of messages) {
1757
+ const coreMessage = message.message && deserializeMessage(message.message);
1758
+ const text = message.text ?? "";
1759
+ const content = coreMessage?.content;
1760
+ const nonStringContent =
1761
+ content && typeof content !== "string" ? content : [];
1762
+ if (!coreMessage) continue;
1763
+ if (coreMessage.role === "system") {
1764
+ uiMessages.push({
1765
+ id: message.id ?? message._id,
1766
+ createdAt: new Date(message._creationTime),
1767
+ role: "system",
1768
+ content: text,
1769
+ parts: [{ type: "text", text }],
1770
+ });
1771
+ } else if (coreMessage.role === "user") {
1772
+ const parts: UIMessage["parts"] = [];
1773
+ if (text) {
1774
+ parts.push({ type: "text", text });
1775
+ }
1776
+ if (message.files) {
1777
+ parts.push(...message.files.map(toUIFilePart));
1778
+ }
1779
+ uiMessages.push({
1780
+ id: message.id ?? message._id,
1781
+ createdAt: new Date(message._creationTime),
1782
+ role: "user",
1783
+ content: message.text ?? "",
1784
+ parts,
1785
+ });
1786
+ } else {
1787
+ if (coreMessage.role === "tool" && !assistantMessage) {
1788
+ console.warn(
1789
+ "Tool message without preceding assistant message.. skipping",
1790
+ message
1791
+ );
1792
+ continue;
1793
+ }
1794
+ if (!assistantMessage) {
1795
+ assistantMessage = {
1796
+ id: message.id ?? message._id,
1797
+ createdAt: new Date(message._creationTime),
1798
+ role: "assistant",
1799
+ content: message.text ?? "",
1800
+ parts: [],
1801
+ };
1802
+ uiMessages.push(assistantMessage);
1803
+ }
1804
+ // update it to the last message's id
1805
+ assistantMessage.id = message.id ?? message._id;
1806
+ if (message.text) {
1807
+ assistantMessage.parts.push({
1808
+ type: "text",
1809
+ text: message.text,
1810
+ });
1811
+ assistantMessage.content += message.text;
1812
+ }
1813
+ if (message.reasoning) {
1814
+ assistantMessage.parts.push({
1815
+ type: "reasoning",
1816
+ reasoning: message.reasoning,
1817
+ details: message.reasoningDetails ?? [],
1818
+ });
1819
+ }
1820
+ for (const source of message.sources ?? []) {
1821
+ assistantMessage.parts.push({
1822
+ type: "source",
1823
+ source,
1824
+ });
1825
+ }
1826
+ for (const file of message.files ?? []) {
1827
+ assistantMessage.parts.push(toUIFilePart(file));
1828
+ }
1829
+ for (const contentPart of nonStringContent) {
1830
+ switch (contentPart.type) {
1831
+ case "tool-call":
1832
+ assistantMessage.parts.push({
1833
+ type: "step-start",
1834
+ });
1835
+ assistantMessage.parts.push({
1836
+ type: "tool-invocation",
1837
+ toolInvocation: {
1838
+ state: "call",
1839
+ step: assistantMessage.parts.filter(
1840
+ (part) => part.type === "tool-invocation"
1841
+ ).length,
1842
+ toolCallId: contentPart.toolCallId,
1843
+ toolName: contentPart.toolName,
1844
+ args: contentPart.args,
1845
+ },
1846
+ });
1847
+ break;
1848
+ case "tool-result": {
1849
+ const call = assistantMessage.parts.find(
1850
+ (part) =>
1851
+ part.type === "tool-invocation" &&
1852
+ part.toolInvocation.toolCallId === contentPart.toolCallId
1853
+ ) as ToolInvocationUIPart | undefined;
1854
+ const toolInvocation: ToolInvocationUIPart["toolInvocation"] = {
1855
+ state: "result",
1856
+ toolCallId: contentPart.toolCallId,
1857
+ toolName: contentPart.toolName,
1858
+ args: call?.toolInvocation.args,
1859
+ result: contentPart.result,
1860
+ step:
1861
+ call?.toolInvocation.step ??
1862
+ assistantMessage.parts.filter(
1863
+ (part) => part.type === "tool-invocation"
1864
+ ).length,
1865
+ };
1866
+ if (call) {
1867
+ (call as ToolInvocationUIPart).toolInvocation = toolInvocation;
1868
+ } else {
1869
+ console.warn(
1870
+ "Tool result without preceding tool call.. adding anyways",
1871
+ contentPart
1872
+ );
1873
+ assistantMessage.parts.push({
1874
+ type: "tool-invocation",
1875
+ toolInvocation,
1876
+ });
1877
+ }
1878
+ break;
1879
+ }
1880
+ }
1881
+ }
1882
+ if (!message.tool) {
1883
+ // Reset it so the next set of tool calls will create a new assistant message
1884
+ assistantMessage = undefined;
1885
+ }
1886
+ }
1887
+ }
1888
+ return uiMessages;
1889
+ }