@convex-dev/agent 0.2.10-alpha.0 → 0.2.11-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 (65) hide show
  1. package/dist/UIMessages.d.ts.map +1 -1
  2. package/dist/UIMessages.js +3 -8
  3. package/dist/UIMessages.js.map +1 -1
  4. package/dist/client/definePlaygroundAPI.d.ts +42 -42
  5. package/dist/client/files.d.ts +2 -2
  6. package/dist/client/files.d.ts.map +1 -1
  7. package/dist/client/files.js.map +1 -1
  8. package/dist/client/index.d.ts +58 -58
  9. package/dist/client/index.d.ts.map +1 -1
  10. package/dist/client/index.js +1 -1
  11. package/dist/client/index.js.map +1 -1
  12. package/dist/client/messages.d.ts +15 -15
  13. package/dist/client/messages.d.ts.map +1 -1
  14. package/dist/client/messages.js.map +1 -1
  15. package/dist/client/saveInputMessages.d.ts +2 -2
  16. package/dist/client/saveInputMessages.d.ts.map +1 -1
  17. package/dist/client/saveInputMessages.js.map +1 -1
  18. package/dist/client/search.d.ts +17 -17
  19. package/dist/client/search.d.ts.map +1 -1
  20. package/dist/client/search.js +4 -13
  21. package/dist/client/search.js.map +1 -1
  22. package/dist/client/streaming.d.ts +95 -95
  23. package/dist/client/streaming.d.ts.map +1 -1
  24. package/dist/client/streaming.js.map +1 -1
  25. package/dist/client/threads.d.ts +5 -5
  26. package/dist/client/threads.d.ts.map +1 -1
  27. package/dist/client/threads.js.map +1 -1
  28. package/dist/client/types.d.ts +6 -8
  29. package/dist/client/types.d.ts.map +1 -1
  30. package/dist/component/messages.d.ts +113 -113
  31. package/dist/component/streams.d.ts +7 -7
  32. package/dist/component/threads.d.ts +7 -7
  33. package/dist/component/users.d.ts +3 -3
  34. package/dist/deltas.d.ts.map +1 -1
  35. package/dist/deltas.js +6 -17
  36. package/dist/deltas.js.map +1 -1
  37. package/dist/mapping.d.ts +5 -4
  38. package/dist/mapping.d.ts.map +1 -1
  39. package/dist/mapping.js +7 -0
  40. package/dist/mapping.js.map +1 -1
  41. package/dist/shared.d.ts +4 -2
  42. package/dist/shared.d.ts.map +1 -1
  43. package/dist/shared.js +10 -10
  44. package/dist/shared.js.map +1 -1
  45. package/dist/validators.d.ts +1 -0
  46. package/dist/validators.d.ts.map +1 -1
  47. package/dist/validators.js.map +1 -1
  48. package/package.json +2 -2
  49. package/src/UIMessages.ts +9 -9
  50. package/src/client/files.ts +2 -2
  51. package/src/client/index.ts +22 -22
  52. package/src/client/messages.ts +10 -5
  53. package/src/client/saveInputMessages.test.ts +13 -5
  54. package/src/client/saveInputMessages.ts +3 -3
  55. package/src/client/search.test.ts +19 -15
  56. package/src/client/search.ts +20 -28
  57. package/src/client/streaming.ts +7 -6
  58. package/src/client/threads.ts +10 -5
  59. package/src/client/types.ts +6 -8
  60. package/src/deltas.test.ts +24 -14
  61. package/src/deltas.ts +6 -17
  62. package/src/mapping.ts +12 -3
  63. package/src/shared.ts +37 -12
  64. package/src/toUIMessages.test.ts +80 -11
  65. package/src/validators.ts +10 -0
@@ -19,9 +19,10 @@ import {
19
19
  type StreamMessage,
20
20
  } from "../validators.js";
21
21
  import type {
22
+ ActionCtx,
22
23
  AgentComponent,
23
- RunMutationCtx,
24
- RunQueryCtx,
24
+ MutationCtx,
25
+ QueryCtx,
25
26
  SyncStreamsReturnValue,
26
27
  } from "./types.js";
27
28
 
@@ -45,7 +46,7 @@ export const vStreamMessagesReturnValue = v.object({
45
46
  * @returns The deltas for each stream from their existing cursor.
46
47
  */
47
48
  export async function syncStreams(
48
- ctx: RunQueryCtx,
49
+ ctx: QueryCtx | MutationCtx | ActionCtx,
49
50
  component: AgentComponent,
50
51
  {
51
52
  threadId,
@@ -80,7 +81,7 @@ export async function syncStreams(
80
81
  }
81
82
 
82
83
  export async function abortStream(
83
- ctx: RunMutationCtx,
84
+ ctx: MutationCtx,
84
85
  component: AgentComponent,
85
86
  args: { reason: string } & (
86
87
  | { streamId: string }
@@ -111,7 +112,7 @@ export async function abortStream(
111
112
  * @returns The streams for the thread.
112
113
  */
113
114
  export async function listStreams(
114
- ctx: RunQueryCtx,
115
+ ctx: QueryCtx | MutationCtx | ActionCtx,
115
116
  component: AgentComponent,
116
117
  {
117
118
  threadId,
@@ -212,7 +213,7 @@ export class DeltaStreamer<T> {
212
213
 
213
214
  constructor(
214
215
  public readonly component: AgentComponent,
215
- public readonly ctx: RunMutationCtx,
216
+ public readonly ctx: MutationCtx,
216
217
  config: {
217
218
  throttleMs: number | undefined;
218
219
  onAsyncAbort: (reason: string) => Promise<void>;
@@ -1,6 +1,11 @@
1
1
  import type { WithoutSystemFields } from "convex/server";
2
2
  import type { ThreadDoc } from "../validators.js";
3
- import type { AgentComponent, RunMutationCtx, RunQueryCtx } from "./types.js";
3
+ import type {
4
+ ActionCtx,
5
+ AgentComponent,
6
+ MutationCtx,
7
+ QueryCtx,
8
+ } from "./types.js";
4
9
 
5
10
  /**
6
11
  * Create a thread to store messages with an Agent.
@@ -10,7 +15,7 @@ import type { AgentComponent, RunMutationCtx, RunQueryCtx } from "./types.js";
10
15
  * @returns The id of the created thread.
11
16
  */
12
17
  export async function createThread(
13
- ctx: RunMutationCtx,
18
+ ctx: MutationCtx,
14
19
  component: AgentComponent,
15
20
  args?: { userId?: string | null; title?: string; summary?: string },
16
21
  ) {
@@ -32,7 +37,7 @@ export async function createThread(
32
37
  * @returns The metadata for the thread.
33
38
  */
34
39
  export async function getThreadMetadata(
35
- ctx: RunQueryCtx,
40
+ ctx: QueryCtx | MutationCtx | ActionCtx,
36
41
  component: AgentComponent,
37
42
  args: { threadId: string },
38
43
  ): Promise<ThreadDoc> {
@@ -46,7 +51,7 @@ export async function getThreadMetadata(
46
51
  }
47
52
 
48
53
  export async function updateThreadMetadata(
49
- ctx: RunMutationCtx,
54
+ ctx: MutationCtx,
50
55
  component: AgentComponent,
51
56
  args: { threadId: string; patch: Partial<WithoutSystemFields<ThreadDoc>> },
52
57
  ) {
@@ -62,7 +67,7 @@ export async function updateThreadMetadata(
62
67
  * @returns The threads matching the search, paginated.
63
68
  */
64
69
  export async function searchThreadTitles(
65
- ctx: RunQueryCtx,
70
+ ctx: QueryCtx | MutationCtx | ActionCtx,
66
71
  component: AgentComponent,
67
72
  {
68
73
  userId,
@@ -223,7 +223,7 @@ export type GenerationOutputMetadata = {
223
223
  };
224
224
 
225
225
  export type UsageHandler = (
226
- ctx: RunActionCtx,
226
+ ctx: ActionCtx,
227
227
  args: {
228
228
  userId: string | undefined;
229
229
  threadId: string | undefined;
@@ -242,7 +242,7 @@ export type UsageHandler = (
242
242
  * out, add in, or reorder messages.
243
243
  */
244
244
  export type ContextHandler = (
245
- ctx: RunActionCtx,
245
+ ctx: ActionCtx,
246
246
  args: {
247
247
  /**
248
248
  * All messages in the default order.
@@ -727,26 +727,24 @@ export type SyncStreamsReturnValue =
727
727
  | undefined;
728
728
 
729
729
  /* Type utils follow */
730
- export type RunQueryCtx = {
730
+ type RunQueryCtx = {
731
731
  runQuery: <Query extends FunctionReference<"query", "internal">>(
732
732
  query: Query,
733
733
  args: FunctionArgs<Query>,
734
734
  ) => Promise<FunctionReturnType<Query>>;
735
735
  };
736
- export type RunMutationCtx = RunQueryCtx & {
736
+ export type MutationCtx = RunQueryCtx & {
737
737
  runMutation: <Mutation extends FunctionReference<"mutation", "internal">>(
738
738
  mutation: Mutation,
739
739
  args: FunctionArgs<Mutation>,
740
740
  ) => Promise<FunctionReturnType<Mutation>>;
741
741
  };
742
- export type RunActionCtx = RunMutationCtx & {
742
+ export type UserActionCtx = GenericActionCtx<GenericDataModel>;
743
+ export type ActionCtx = MutationCtx & {
743
744
  runAction<Action extends FunctionReference<"action", "internal">>(
744
745
  action: Action,
745
746
  args: FunctionArgs<Action>,
746
747
  ): Promise<FunctionReturnType<Action>>;
747
- };
748
- export type UserActionCtx = GenericActionCtx<GenericDataModel>;
749
- export type ActionCtx = RunActionCtx & {
750
748
  auth: Auth;
751
749
  storage: StorageActionWriter;
752
750
  };
@@ -100,7 +100,7 @@ describe("UIMessageChunks", () => {
100
100
  { type: "finish" },
101
101
  ]);
102
102
  expect(updatedMessage.text).toBe(
103
- "Hey ho.The best ice cream flavor is vanilla.",
103
+ "Hey ho. The best ice cream flavor is vanilla.",
104
104
  );
105
105
  const expectedParts = [
106
106
  {
@@ -590,7 +590,9 @@ describe("combineUIMessages", () => {
590
590
  expect(combined[0].text).toBe("The result is 5.");
591
591
  expect(combined[0].parts).toHaveLength(2);
592
592
 
593
- const toolPart = combined[0].parts.find(p => p.type === "tool-calculator");
593
+ const toolPart = combined[0].parts.find(
594
+ (p) => p.type === "tool-calculator",
595
+ );
594
596
  expect(toolPart).toMatchObject({
595
597
  type: "tool-calculator",
596
598
  state: "output-available",
@@ -599,7 +601,7 @@ describe("combineUIMessages", () => {
599
601
  output: { result: 5 },
600
602
  });
601
603
 
602
- const textPart = combined[0].parts.find(p => p.type === "text");
604
+ const textPart = combined[0].parts.find((p) => p.type === "text");
603
605
  expect(textPart).toMatchObject({
604
606
  type: "text",
605
607
  text: "The result is 5.",
@@ -629,7 +631,13 @@ describe("combineUIMessages", () => {
629
631
  status: "success" as const,
630
632
  role: "assistant" as const,
631
633
  text: "The result is 5.",
632
- parts: [{ type: "text" as const, text: "The result is 5.", state: "done" as const }],
634
+ parts: [
635
+ {
636
+ type: "text" as const,
637
+ text: "The result is 5.",
638
+ state: "done" as const,
639
+ },
640
+ ],
633
641
  };
634
642
 
635
643
  const combined = combineUIMessages([userMessage, assistantMessage]);
@@ -706,8 +714,11 @@ describe("combineUIMessages", () => {
706
714
  expect(combined[0].text).toBe("The formatted result is: 5");
707
715
  expect(combined[0].parts).toHaveLength(3);
708
716
 
709
- const calculatorPart = combined[0].parts.find(p =>
710
- p.type === "tool-calculator" && "toolCallId" in p && p.toolCallId === "call_1"
717
+ const calculatorPart = combined[0].parts.find(
718
+ (p) =>
719
+ p.type === "tool-calculator" &&
720
+ "toolCallId" in p &&
721
+ p.toolCallId === "call_1",
711
722
  );
712
723
  expect(calculatorPart).toMatchObject({
713
724
  type: "tool-calculator",
@@ -717,8 +728,11 @@ describe("combineUIMessages", () => {
717
728
  output: { result: 5 },
718
729
  });
719
730
 
720
- const formatterPart = combined[0].parts.find(p =>
721
- p.type === "tool-formatter" && "toolCallId" in p && p.toolCallId === "call_2"
731
+ const formatterPart = combined[0].parts.find(
732
+ (p) =>
733
+ p.type === "tool-formatter" &&
734
+ "toolCallId" in p &&
735
+ p.toolCallId === "call_2",
722
736
  );
723
737
  expect(formatterPart).toMatchObject({
724
738
  type: "tool-formatter",
@@ -775,9 +789,7 @@ describe("combineUIMessages", () => {
775
789
  expect(combined[0].text).toBe("Still processing...");
776
790
  expect(combined[0].parts).toHaveLength(2);
777
791
 
778
- const toolPart = combined[0].parts.find(p =>
779
- p.type === "dynamic-tool"
780
- );
792
+ const toolPart = combined[0].parts.find((p) => p.type === "dynamic-tool");
781
793
  expect(toolPart).toMatchObject({
782
794
  type: "dynamic-tool",
783
795
  state: "input-available",
@@ -786,9 +798,7 @@ describe("combineUIMessages", () => {
786
798
  input: { operation: "add", a: 2, b: 3 },
787
799
  });
788
800
 
789
- const textPart = combined[0].parts.find(p =>
790
- p.type === "text"
791
- );
801
+ const textPart = combined[0].parts.find((p) => p.type === "text");
792
802
  expect(textPart).toMatchObject({
793
803
  type: "text",
794
804
  text: "Still processing...",
package/src/deltas.ts CHANGED
@@ -11,12 +11,13 @@ import {
11
11
  } from "ai";
12
12
  import { assert, pick } from "convex-helpers";
13
13
  import { type UIMessage } from "./UIMessages.js";
14
- import { sorted } from "./shared.js";
14
+ import { joinText, sorted } from "./shared.js";
15
15
  import {
16
16
  type MessageStatus,
17
17
  type StreamDelta,
18
18
  type StreamMessage,
19
19
  } from "./validators.js";
20
+ import { getErrorMessage } from "@ai-sdk/provider-utils";
20
21
 
21
22
  export function blankUIMessage<METADATA = unknown>(
22
23
  streamMessage: StreamMessage & { metadata?: METADATA },
@@ -85,10 +86,7 @@ export async function updateFromUIMessageChunks(
85
86
  if (failed) {
86
87
  message.status = "failed";
87
88
  }
88
- message.text = message.parts
89
- .filter((p) => p.type === "text")
90
- .map((p) => p.text)
91
- .join("");
89
+ message.text = joinText(message.parts);
92
90
  return message;
93
91
  }
94
92
 
@@ -470,10 +468,7 @@ export function updateFromTextStreamParts(
470
468
  case "tool-error": {
471
469
  const toolPart = toolPartsById.get(part.toolCallId);
472
470
  if (toolPart) {
473
- toolPart.errorText =
474
- part.error instanceof Error
475
- ? part.error.message.toString()
476
- : String(part.error);
471
+ toolPart.errorText = getErrorMessage(part.error);
477
472
  }
478
473
  break;
479
474
  }
@@ -501,10 +496,7 @@ export function updateFromTextStreamParts(
501
496
  part.state = "done";
502
497
  }
503
498
  }
504
- message.text = message.parts
505
- .filter((p) => p.type === "text")
506
- .map((p) => p.text)
507
- .join("");
499
+ message.text = joinText(message.parts);
508
500
  return [
509
501
  {
510
502
  streamId: streamMessage.streamId,
@@ -571,10 +563,7 @@ export function combineUIMessages(messages: UIMessage[]): UIMessage[] {
571
563
  ...previous,
572
564
  ...pick(message, ["status", "metadata", "agentName"]),
573
565
  parts: newParts,
574
- text: newParts
575
- .filter((p) => p.type === "text")
576
- .map((p) => p.text)
577
- .join(""),
566
+ text: joinText(newParts),
578
567
  });
579
568
  return acc;
580
569
  }, [] as UIMessage[]);
package/src/mapping.ts CHANGED
@@ -35,9 +35,10 @@ import {
35
35
  type vToolResultPart,
36
36
  type SourcePart,
37
37
  vToolResultOutput,
38
+ type MessageDoc,
38
39
  } from "./validators.js";
39
40
  import type { ActionCtx, AgentComponent } from "./client/types.js";
40
- import type { RunMutationCtx } from "./client/types.js";
41
+ import type { MutationCtx } from "./client/types.js";
41
42
  import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
42
43
  import type { Infer } from "convex/values";
43
44
  import {
@@ -70,7 +71,7 @@ export type SerializedContent = Message["content"];
70
71
  export type SerializedMessage = Message;
71
72
 
72
73
  export async function serializeMessage(
73
- ctx: ActionCtx | RunMutationCtx,
74
+ ctx: ActionCtx | MutationCtx,
74
75
  component: AgentComponent,
75
76
  message: ModelMessage | Message,
76
77
  ): Promise<{ message: SerializedMessage; fileIds?: string[] }> {
@@ -132,6 +133,14 @@ export function toModelMessage(
132
133
  } as ModelMessage;
133
134
  }
134
135
 
136
+ export function docsToModelMessages(messages: MessageDoc[]): ModelMessage[] {
137
+ return messages
138
+ .map((m) => m.message)
139
+ .filter((m) => !!m)
140
+ .filter((m) => !!m.content.length)
141
+ .map(toModelMessage);
142
+ }
143
+
135
144
  export function serializeUsage(usage: LanguageModelUsage): Usage {
136
145
  return {
137
146
  promptTokens: usage.inputTokens ?? 0,
@@ -254,7 +263,7 @@ function getMimeOrMediaType(part: { mediaType?: string; mimeType?: string }) {
254
263
  }
255
264
 
256
265
  export async function serializeContent(
257
- ctx: ActionCtx | RunMutationCtx,
266
+ ctx: ActionCtx | MutationCtx,
258
267
  component: AgentComponent,
259
268
  content: Content | Message["content"],
260
269
  ): Promise<{ content: SerializedContent; fileIds?: string[] }> {
package/src/shared.ts CHANGED
@@ -1,5 +1,18 @@
1
- import type { ModelMessage } from "ai";
2
- import type { Message } from "./validators.js";
1
+ import type {
2
+ FilePart,
3
+ ImagePart,
4
+ ReasoningPart,
5
+ ToolCallPart,
6
+ ToolResultPart,
7
+ } from "@ai-sdk/provider-utils";
8
+ import type {
9
+ ModelMessage,
10
+ TextPart,
11
+ UIDataTypes,
12
+ UIMessagePart,
13
+ UITools,
14
+ } from "ai";
15
+ import type { Message, MessageContentParts } from "./validators.js";
3
16
 
4
17
  export const DEFAULT_RECENT_MESSAGES = 100;
5
18
 
@@ -18,19 +31,12 @@ export function extractText(message: Message | ModelMessage) {
18
31
  if (typeof message.content === "string") {
19
32
  return message.content;
20
33
  }
21
- return message.content
22
- .filter((c) => c.type === "text")
23
- .map((c) => c.text)
24
- .join("");
34
+ return joinText(message.content);
25
35
  case "assistant":
26
36
  if (typeof message.content === "string") {
27
37
  return message.content;
28
38
  } else {
29
- const textParts = message.content.filter((c) => c.type === "text");
30
- if (!textParts.length) {
31
- return undefined;
32
- }
33
- return textParts.map((c) => c.text).join("");
39
+ return joinText(message.content) || undefined;
34
40
  }
35
41
  case "system":
36
42
  return message.content;
@@ -39,6 +45,25 @@ export function extractText(message: Message | ModelMessage) {
39
45
  return undefined;
40
46
  }
41
47
 
48
+ export function joinText(
49
+ parts: (
50
+ | UIMessagePart<UIDataTypes, UITools>
51
+ | TextPart
52
+ | ImagePart
53
+ | FilePart
54
+ | ReasoningPart
55
+ | ToolCallPart
56
+ | ToolResultPart
57
+ | MessageContentParts
58
+ )[],
59
+ ) {
60
+ return parts
61
+ .filter((p) => p.type === "text")
62
+ .map((p) => p.text)
63
+ .filter(Boolean)
64
+ .join(" ");
65
+ }
66
+
42
67
  export function extractReasoning(message: Message | ModelMessage) {
43
68
  if (typeof message.content === "string") {
44
69
  return undefined;
@@ -46,7 +71,7 @@ export function extractReasoning(message: Message | ModelMessage) {
46
71
  return message.content
47
72
  .filter((c) => c.type === "reasoning")
48
73
  .map((c) => c.text)
49
- .join("");
74
+ .join(" ");
50
75
  }
51
76
 
52
77
  export const DEFAULT_MESSAGE_RANGE = { before: 2, after: 1 };
@@ -132,10 +132,7 @@ describe("toUIMessages", () => {
132
132
  type: "tool-myTool",
133
133
  toolCallId: "call1",
134
134
  state: "output-available",
135
- output: {
136
- type: "text",
137
- value: "42",
138
- },
135
+ output: "42",
139
136
  });
140
137
  });
141
138
 
@@ -163,14 +160,14 @@ describe("toUIMessages", () => {
163
160
  },
164
161
  ],
165
162
  },
166
- reasoning: "I'm thinking...I'm thinking...",
163
+ reasoning: "I'm thinking... I'm thinking...",
167
164
  text: "Here's one idea. Here's another idea.",
168
165
  }),
169
166
  ];
170
167
  const uiMessages = toUIMessages(messages);
171
168
  expect(uiMessages).toHaveLength(1);
172
169
  expect(uiMessages[0].role).toBe("assistant");
173
- expect(uiMessages[0].text).toBe("Here's one idea.Here's another idea.");
170
+ expect(uiMessages[0].text).toBe("Here's one idea. Here's another idea.");
174
171
  expect(uiMessages[0].parts.filter((p) => p.type === "reasoning")).toEqual([
175
172
  {
176
173
  providerOptions: undefined,
@@ -206,6 +203,81 @@ describe("toUIMessages", () => {
206
203
  );
207
204
  });
208
205
 
206
+ it("combines text from between messages", () => {
207
+ const messages = [
208
+ baseMessageDoc({
209
+ message: {
210
+ role: "assistant",
211
+ content: [
212
+ {
213
+ type: "reasoning",
214
+ text: "I'm thinking...",
215
+ },
216
+ {
217
+ type: "text",
218
+ text: "I'm going to ask a question.",
219
+ },
220
+ {
221
+ type: "tool-call",
222
+ args: "What's the meaning of life?",
223
+ toolCallId: "call1",
224
+ toolName: "myTool",
225
+ },
226
+ ],
227
+ },
228
+ reasoning: "I'm thinking...",
229
+ text: "Here's one idea.",
230
+ tool: true,
231
+ order: 1,
232
+ stepOrder: 1,
233
+ }),
234
+ baseMessageDoc({
235
+ message: {
236
+ role: "tool",
237
+ content: [
238
+ {
239
+ type: "tool-result",
240
+ toolCallId: "call1",
241
+ toolName: "myTool",
242
+ output: {
243
+ type: "text",
244
+ value: "42",
245
+ },
246
+ },
247
+ ],
248
+ },
249
+ text: "",
250
+ tool: true,
251
+ order: 1,
252
+ stepOrder: 2,
253
+ }),
254
+ baseMessageDoc({
255
+ message: {
256
+ role: "assistant",
257
+ content: [
258
+ {
259
+ type: "reasoning",
260
+ text: "Thinking again...",
261
+ },
262
+ {
263
+ type: "text",
264
+ text: "Ok now I know.",
265
+ },
266
+ ],
267
+ },
268
+ text: "One last thing.",
269
+ order: 1,
270
+ stepOrder: 3,
271
+ }),
272
+ ];
273
+ const uiMessages = toUIMessages(messages);
274
+ expect(uiMessages).toHaveLength(1);
275
+ expect(uiMessages[0].role).toBe("assistant");
276
+ expect(uiMessages[0].text).toBe(
277
+ "I'm going to ask a question. Ok now I know.",
278
+ );
279
+ });
280
+
209
281
  it("handles system message", () => {
210
282
  const messages = [
211
283
  baseMessageDoc({
@@ -252,7 +324,7 @@ describe("toUIMessages", () => {
252
324
  type: "tool-result",
253
325
  toolName: "myTool",
254
326
  toolCallId: "call1",
255
- result: {
327
+ output: {
256
328
  type: "json",
257
329
  value: { data: "wrapped result", success: true },
258
330
  },
@@ -648,10 +720,7 @@ describe("toUIMessages", () => {
648
720
  toolCallId: "call1",
649
721
  state: "output-available",
650
722
  input: { operation: "add", a: 40, b: 2 },
651
- output: {
652
- type: "text",
653
- value: "42",
654
- },
723
+ output: "42",
655
724
  });
656
725
 
657
726
  // Should also have text part
package/src/validators.ts CHANGED
@@ -221,6 +221,16 @@ export const vMessage = v.union(
221
221
  );
222
222
  export type Message = Infer<typeof vMessage>;
223
223
 
224
+ export type MessageContentParts =
225
+ | Infer<typeof vTextPart>
226
+ | Infer<typeof vImagePart>
227
+ | Infer<typeof vFilePart>
228
+ | Infer<typeof vReasoningPart>
229
+ | Infer<typeof vRedactedReasoningPart>
230
+ | Infer<typeof vToolCallPart>
231
+ | Infer<typeof vToolResultPart>
232
+ | Infer<typeof vSourcePart>;
233
+
224
234
  export const vSource = v.union(
225
235
  v.object({
226
236
  type: v.optional(v.literal("source")),