@mastra/client-js 0.0.0-extend-clickhouse-20250418135620 → 0.0.0-feat-support-ai-sdk-5-again-20250813225910

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 (83) hide show
  1. package/.turbo/turbo-build.log +18 -0
  2. package/CHANGELOG.md +1483 -2
  3. package/LICENSE.md +11 -42
  4. package/README.md +2 -1
  5. package/dist/adapters/agui.d.ts +22 -0
  6. package/dist/adapters/agui.d.ts.map +1 -0
  7. package/dist/client.d.ts +270 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/example.d.ts +2 -0
  10. package/dist/example.d.ts.map +1 -0
  11. package/dist/index.cjs +1812 -80
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.ts +3 -585
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +1807 -79
  16. package/dist/index.js.map +1 -0
  17. package/dist/resources/a2a.d.ts +41 -0
  18. package/dist/resources/a2a.d.ts.map +1 -0
  19. package/dist/resources/agent.d.ts +123 -0
  20. package/dist/resources/agent.d.ts.map +1 -0
  21. package/dist/resources/base.d.ts +13 -0
  22. package/dist/resources/base.d.ts.map +1 -0
  23. package/dist/resources/index.d.ts +11 -0
  24. package/dist/resources/index.d.ts.map +1 -0
  25. package/dist/resources/legacy-workflow.d.ts +87 -0
  26. package/dist/resources/legacy-workflow.d.ts.map +1 -0
  27. package/dist/resources/mcp-tool.d.ts +27 -0
  28. package/dist/resources/mcp-tool.d.ts.map +1 -0
  29. package/dist/resources/memory-thread.d.ts +53 -0
  30. package/dist/resources/memory-thread.d.ts.map +1 -0
  31. package/dist/resources/network-memory-thread.d.ts +47 -0
  32. package/dist/resources/network-memory-thread.d.ts.map +1 -0
  33. package/dist/resources/network.d.ts +30 -0
  34. package/dist/resources/network.d.ts.map +1 -0
  35. package/dist/resources/tool.d.ts +23 -0
  36. package/dist/resources/tool.d.ts.map +1 -0
  37. package/dist/resources/vNextNetwork.d.ts +42 -0
  38. package/dist/resources/vNextNetwork.d.ts.map +1 -0
  39. package/dist/resources/vector.d.ts +48 -0
  40. package/dist/resources/vector.d.ts.map +1 -0
  41. package/dist/resources/workflow.d.ts +154 -0
  42. package/dist/resources/workflow.d.ts.map +1 -0
  43. package/dist/types.d.ts +427 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/utils/index.d.ts +3 -0
  46. package/dist/utils/index.d.ts.map +1 -0
  47. package/dist/utils/process-client-tools.d.ts +3 -0
  48. package/dist/utils/process-client-tools.d.ts.map +1 -0
  49. package/dist/utils/zod-to-json-schema.d.ts +105 -0
  50. package/dist/utils/zod-to-json-schema.d.ts.map +1 -0
  51. package/integration-tests/agui-adapter.test.ts +122 -0
  52. package/integration-tests/package.json +17 -0
  53. package/integration-tests/src/mastra/index.ts +38 -0
  54. package/integration-tests/vitest.config.ts +9 -0
  55. package/package.json +33 -18
  56. package/src/adapters/agui.test.ts +322 -0
  57. package/src/adapters/agui.ts +263 -0
  58. package/src/client.ts +422 -14
  59. package/src/example.ts +59 -29
  60. package/src/index.test.ts +526 -10
  61. package/src/resources/a2a.ts +98 -0
  62. package/src/resources/agent.ts +637 -49
  63. package/src/resources/base.ts +9 -2
  64. package/src/resources/index.ts +4 -1
  65. package/src/resources/legacy-workflow.ts +242 -0
  66. package/src/resources/mcp-tool.ts +48 -0
  67. package/src/resources/memory-thread.test.ts +285 -0
  68. package/src/resources/memory-thread.ts +45 -5
  69. package/src/resources/network-memory-thread.test.ts +269 -0
  70. package/src/resources/network-memory-thread.ts +81 -0
  71. package/src/resources/network.ts +11 -17
  72. package/src/resources/tool.ts +16 -3
  73. package/src/resources/vNextNetwork.ts +194 -0
  74. package/src/resources/workflow.ts +289 -94
  75. package/src/types.ts +306 -24
  76. package/src/utils/index.ts +11 -0
  77. package/src/utils/process-client-tools.ts +32 -0
  78. package/src/utils/zod-to-json-schema.ts +10 -0
  79. package/src/v2-messages.test.ts +180 -0
  80. package/tsconfig.build.json +9 -0
  81. package/tsconfig.json +1 -1
  82. package/tsup.config.ts +17 -0
  83. package/dist/index.d.cts +0 -585
@@ -1,8 +1,11 @@
1
- import type { GenerateReturn } from '@mastra/core';
1
+ import { processDataStream, type JSONValue, type ToolInvocation, type UIMessage } from 'ai';
2
+ import { Tool, type CoreMessage, type MastraLanguageModel } from '@mastra/core';
3
+ import { type GenerateReturn } from '@mastra/core/llm';
2
4
  import type { JSONSchema7 } from 'json-schema';
3
5
  import { ZodSchema } from 'zod';
4
- import { zodToJsonSchema } from 'zod-to-json-schema';
5
- import { processDataStream } from '@ai-sdk/ui-utils';
6
+ import { zodToJsonSchema } from '../utils/zod-to-json-schema';
7
+ import { processClientTools } from '../utils/process-client-tools';
8
+ import { v4 as uuid } from '@lukeed/uuid';
6
9
 
7
10
  import type {
8
11
  GenerateParams,
@@ -14,29 +17,9 @@ import type {
14
17
  } from '../types';
15
18
 
16
19
  import { BaseResource } from './base';
17
-
18
- export class AgentTool extends BaseResource {
19
- constructor(
20
- options: ClientOptions,
21
- private agentId: string,
22
- private toolId: string,
23
- ) {
24
- super(options);
25
- }
26
-
27
- /**
28
- * Executes a specific tool for an agent
29
- * @param params - Parameters required for tool execution
30
- * @returns Promise containing tool execution results
31
- */
32
- /** @deprecated use CreateRun/startRun */
33
- execute(params: { data: any }): Promise<any> {
34
- return this.request(`/api/agents/${this.agentId}/tools/${this.toolId}/execute`, {
35
- method: 'POST',
36
- body: params,
37
- });
38
- }
39
- }
20
+ import type { RuntimeContext } from '@mastra/core/runtime-context';
21
+ import { parseClientRuntimeContext } from '../utils';
22
+ import { MessageList } from '@mastra/core/agent';
40
23
 
41
24
  export class AgentVoice extends BaseResource {
42
25
  constructor(
@@ -70,7 +53,7 @@ export class AgentVoice extends BaseResource {
70
53
  * @param options - Optional provider-specific options
71
54
  * @returns Promise containing the transcribed text
72
55
  */
73
- listen(audio: Blob, options?: Record<string, any>): Promise<Response> {
56
+ listen(audio: Blob, options?: Record<string, any>): Promise<{ text: string }> {
74
57
  const formData = new FormData();
75
58
  formData.append('audio', audio);
76
59
 
@@ -91,6 +74,14 @@ export class AgentVoice extends BaseResource {
91
74
  getSpeakers(): Promise<Array<{ voiceId: string; [key: string]: any }>> {
92
75
  return this.request(`/api/agents/${this.agentId}/voice/speakers`);
93
76
  }
77
+
78
+ /**
79
+ * Get the listener configuration for the agent's voice provider
80
+ * @returns Promise containing a check if the agent has listening capabilities
81
+ */
82
+ getListener(): Promise<{ enabled: boolean }> {
83
+ return this.request(`/api/agents/${this.agentId}/voice/listener`);
84
+ }
94
85
  }
95
86
 
96
87
  export class Agent extends BaseResource {
@@ -117,22 +108,440 @@ export class Agent extends BaseResource {
117
108
  * @param params - Generation parameters including prompt
118
109
  * @returns Promise containing the generated response
119
110
  */
120
- generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
121
- params: GenerateParams<T>,
122
- ): Promise<GenerateReturn<T>> {
111
+ async generate(
112
+ params: GenerateParams<undefined> & { output?: never; experimental_output?: never },
113
+ ): Promise<GenerateReturn<any, undefined, undefined>>;
114
+ async generate<Output extends JSONSchema7 | ZodSchema>(
115
+ params: GenerateParams<Output> & { output: Output; experimental_output?: never },
116
+ ): Promise<GenerateReturn<any, Output, undefined>>;
117
+ async generate<StructuredOutput extends JSONSchema7 | ZodSchema>(
118
+ params: GenerateParams<StructuredOutput> & { output?: never; experimental_output: StructuredOutput },
119
+ ): Promise<GenerateReturn<any, undefined, StructuredOutput>>;
120
+ async generate<
121
+ Output extends JSONSchema7 | ZodSchema | undefined = undefined,
122
+ StructuredOutput extends JSONSchema7 | ZodSchema | undefined = undefined,
123
+ >(params: GenerateParams<Output>): Promise<GenerateReturn<any, Output, StructuredOutput>> {
123
124
  const processedParams = {
124
125
  ...params,
125
- output: params.output instanceof ZodSchema ? zodToJsonSchema(params.output) : params.output,
126
- experimental_output:
127
- params.experimental_output instanceof ZodSchema
128
- ? zodToJsonSchema(params.experimental_output)
129
- : params.experimental_output,
126
+ output: params.output ? zodToJsonSchema(params.output) : undefined,
127
+ experimental_output: params.experimental_output ? zodToJsonSchema(params.experimental_output) : undefined,
128
+ runtimeContext: parseClientRuntimeContext(params.runtimeContext),
129
+ clientTools: processClientTools(params.clientTools),
130
130
  };
131
131
 
132
- return this.request(`/api/agents/${this.agentId}/generate`, {
133
- method: 'POST',
134
- body: processedParams,
132
+ const { runId, resourceId, threadId, runtimeContext } = processedParams as GenerateParams;
133
+
134
+ const response: GenerateReturn<any, Output, StructuredOutput> = await this.request(
135
+ `/api/agents/${this.agentId}/generate`,
136
+ {
137
+ method: 'POST',
138
+ body: processedParams,
139
+ },
140
+ );
141
+
142
+ if (response.finishReason === 'tool-calls') {
143
+ const toolCalls = (
144
+ response as unknown as {
145
+ toolCalls: { toolName: string; args: any; toolCallId: string }[];
146
+ messages: CoreMessage[];
147
+ }
148
+ ).toolCalls;
149
+
150
+ if (!toolCalls || !Array.isArray(toolCalls)) {
151
+ return response;
152
+ }
153
+
154
+ for (const toolCall of toolCalls) {
155
+ const clientTool = params.clientTools?.[toolCall.toolName] as Tool;
156
+
157
+ if (clientTool && clientTool.execute) {
158
+ const result = await clientTool.execute(
159
+ { context: toolCall?.args, runId, resourceId, threadId, runtimeContext: runtimeContext as RuntimeContext },
160
+ {
161
+ messages: (response as unknown as { messages: CoreMessage[] }).messages,
162
+ toolCallId: toolCall?.toolCallId,
163
+ },
164
+ );
165
+
166
+ const updatedMessages = [
167
+ {
168
+ role: 'user',
169
+ content: params.messages,
170
+ },
171
+ ...(response.response as unknown as { messages: CoreMessage[] }).messages,
172
+ {
173
+ role: 'tool',
174
+ content: [
175
+ {
176
+ type: 'tool-result',
177
+ toolCallId: toolCall.toolCallId,
178
+ toolName: toolCall.toolName,
179
+ result,
180
+ },
181
+ ],
182
+ },
183
+ ];
184
+ // @ts-ignore
185
+ return this.generate({
186
+ ...params,
187
+ messages: updatedMessages,
188
+ });
189
+ }
190
+ }
191
+ }
192
+
193
+ return response;
194
+ }
195
+
196
+ private async processChatResponse({
197
+ stream,
198
+ update,
199
+ onToolCall,
200
+ onFinish,
201
+ getCurrentDate = () => new Date(),
202
+ lastMessage,
203
+ }: {
204
+ stream: ReadableStream<Uint8Array>;
205
+ update: (options: { message: UIMessage; data: JSONValue[] | undefined; replaceLastMessage: boolean }) => void;
206
+ onToolCall?: any;
207
+ onFinish?: (options: { message: UIMessage | undefined; finishReason: string; usage: string }) => void;
208
+ generateId?: () => string;
209
+ getCurrentDate?: () => Date;
210
+ lastMessage: UIMessage | undefined;
211
+ }) {
212
+ const replaceLastMessage = lastMessage?.role === 'assistant';
213
+ let step = replaceLastMessage
214
+ ? 1 +
215
+ // find max step in existing tool invocations:
216
+ (lastMessage.toolInvocations?.reduce((max, toolInvocation) => {
217
+ return Math.max(max, toolInvocation.step ?? 0);
218
+ }, 0) ?? 0)
219
+ : 0;
220
+
221
+ const message: UIMessage = replaceLastMessage
222
+ ? structuredClone(lastMessage)
223
+ : {
224
+ id: uuid(),
225
+ createdAt: getCurrentDate(),
226
+ role: 'assistant',
227
+ content: '',
228
+ parts: [],
229
+ };
230
+
231
+ let currentTextPart: any | undefined = undefined;
232
+ let currentReasoningPart: any | undefined = undefined;
233
+ let currentReasoningTextDetail: { type: 'text'; text: string; signature?: string } | undefined = undefined;
234
+
235
+ function updateToolInvocationPart(toolCallId: string, invocation: ToolInvocation) {
236
+ const part = message.parts.find(
237
+ part => part.type === 'tool-invocation' && part.toolInvocation.toolCallId === toolCallId,
238
+ ) as any | undefined;
239
+
240
+ if (part != null) {
241
+ part.toolInvocation = invocation;
242
+ } else {
243
+ message.parts.push({
244
+ type: 'tool-invocation',
245
+ toolInvocation: invocation,
246
+ });
247
+ }
248
+ }
249
+
250
+ const data: JSONValue[] = [];
251
+
252
+ // keep list of current message annotations for message
253
+ let messageAnnotations: JSONValue[] | undefined = replaceLastMessage ? lastMessage?.annotations : undefined;
254
+
255
+ // keep track of partial tool calls
256
+ const partialToolCalls: Record<string, { text: string; step: number; index: number; toolName: string }> = {};
257
+
258
+ let usage: any = {
259
+ completionTokens: NaN,
260
+ promptTokens: NaN,
261
+ totalTokens: NaN,
262
+ };
263
+ let finishReason: string = 'unknown';
264
+
265
+ function execUpdate() {
266
+ // make a copy of the data array to ensure UI is updated (SWR)
267
+ const copiedData = [...data];
268
+
269
+ // keeps the currentMessage up to date with the latest annotations,
270
+ // even if annotations preceded the message creation
271
+ if (messageAnnotations?.length) {
272
+ message.annotations = messageAnnotations;
273
+ }
274
+
275
+ const copiedMessage = {
276
+ // deep copy the message to ensure that deep changes (msg attachments) are updated
277
+ // with SolidJS. SolidJS uses referential integration of sub-objects to detect changes.
278
+ ...structuredClone(message),
279
+ // add a revision id to ensure that the message is updated with SWR. SWR uses a
280
+ // hashing approach by default to detect changes, but it only works for shallow
281
+ // changes. This is why we need to add a revision id to ensure that the message
282
+ // is updated with SWR (without it, the changes get stuck in SWR and are not
283
+ // forwarded to rendering):
284
+ revisionId: uuid(),
285
+ } as UIMessage;
286
+
287
+ update({
288
+ message: copiedMessage,
289
+ data: copiedData,
290
+ replaceLastMessage,
291
+ });
292
+ }
293
+
294
+ await processDataStream({
295
+ stream,
296
+ onTextPart(value) {
297
+ if (currentTextPart == null) {
298
+ currentTextPart = {
299
+ type: 'text',
300
+ text: value,
301
+ };
302
+ message.parts.push(currentTextPart);
303
+ } else {
304
+ currentTextPart.text += value;
305
+ }
306
+
307
+ message.content += value;
308
+ execUpdate();
309
+ },
310
+ onReasoningPart(value) {
311
+ if (currentReasoningTextDetail == null) {
312
+ currentReasoningTextDetail = { type: 'text', text: value };
313
+ if (currentReasoningPart != null) {
314
+ currentReasoningPart.details.push(currentReasoningTextDetail);
315
+ }
316
+ } else {
317
+ currentReasoningTextDetail.text += value;
318
+ }
319
+
320
+ if (currentReasoningPart == null) {
321
+ currentReasoningPart = {
322
+ type: 'reasoning',
323
+ reasoning: value,
324
+ details: [currentReasoningTextDetail],
325
+ };
326
+ message.parts.push(currentReasoningPart);
327
+ } else {
328
+ currentReasoningPart.reasoning += value;
329
+ }
330
+
331
+ message.reasoning = (message.reasoning ?? '') + value;
332
+
333
+ execUpdate();
334
+ },
335
+ onReasoningSignaturePart(value) {
336
+ if (currentReasoningTextDetail != null) {
337
+ currentReasoningTextDetail.signature = value.signature;
338
+ }
339
+ },
340
+ onRedactedReasoningPart(value) {
341
+ if (currentReasoningPart == null) {
342
+ currentReasoningPart = {
343
+ type: 'reasoning',
344
+ reasoning: '',
345
+ details: [],
346
+ };
347
+ message.parts.push(currentReasoningPart);
348
+ }
349
+
350
+ currentReasoningPart.details.push({
351
+ type: 'redacted',
352
+ data: value.data,
353
+ });
354
+
355
+ currentReasoningTextDetail = undefined;
356
+
357
+ execUpdate();
358
+ },
359
+ onFilePart(value) {
360
+ message.parts.push({
361
+ type: 'file',
362
+ mimeType: value.mimeType,
363
+ data: value.data,
364
+ });
365
+
366
+ execUpdate();
367
+ },
368
+ onSourcePart(value) {
369
+ message.parts.push({
370
+ type: 'source',
371
+ source: value,
372
+ });
373
+
374
+ execUpdate();
375
+ },
376
+ onToolCallStreamingStartPart(value) {
377
+ if (message.toolInvocations == null) {
378
+ message.toolInvocations = [];
379
+ }
380
+
381
+ // add the partial tool call to the map
382
+ partialToolCalls[value.toolCallId] = {
383
+ text: '',
384
+ step,
385
+ toolName: value.toolName,
386
+ index: message.toolInvocations.length,
387
+ };
388
+
389
+ const invocation = {
390
+ state: 'partial-call',
391
+ step,
392
+ toolCallId: value.toolCallId,
393
+ toolName: value.toolName,
394
+ args: undefined,
395
+ } as const;
396
+
397
+ message.toolInvocations.push(invocation);
398
+
399
+ updateToolInvocationPart(value.toolCallId, invocation);
400
+
401
+ execUpdate();
402
+ },
403
+ onToolCallDeltaPart(value) {
404
+ const partialToolCall = partialToolCalls[value.toolCallId];
405
+
406
+ partialToolCall!.text += value.argsTextDelta;
407
+
408
+ // In v4, we don't have parsePartialJson, so we'll try to parse it directly
409
+ let partialArgs;
410
+ try {
411
+ partialArgs = JSON.parse(partialToolCall!.text);
412
+ } catch {
413
+ partialArgs = undefined;
414
+ }
415
+
416
+ const invocation = {
417
+ state: 'partial-call',
418
+ step: partialToolCall!.step,
419
+ toolCallId: value.toolCallId,
420
+ toolName: partialToolCall!.toolName,
421
+ args: partialArgs,
422
+ } as const;
423
+
424
+ message.toolInvocations![partialToolCall!.index] = invocation;
425
+
426
+ updateToolInvocationPart(value.toolCallId, invocation);
427
+
428
+ execUpdate();
429
+ },
430
+ async onToolCallPart(value) {
431
+ const invocation = {
432
+ state: 'call',
433
+ step,
434
+ ...value,
435
+ } as const;
436
+
437
+ if (partialToolCalls[value.toolCallId] != null) {
438
+ // change the partial tool call to a full tool call
439
+ message.toolInvocations![partialToolCalls[value.toolCallId]!.index] = invocation;
440
+ } else {
441
+ if (message.toolInvocations == null) {
442
+ message.toolInvocations = [];
443
+ }
444
+
445
+ message.toolInvocations.push(invocation);
446
+ }
447
+
448
+ updateToolInvocationPart(value.toolCallId, invocation);
449
+
450
+ execUpdate();
451
+
452
+ // invoke the onToolCall callback if it exists. This is blocking.
453
+ // In the future we should make this non-blocking, which
454
+ // requires additional state management for error handling etc.
455
+ if (onToolCall) {
456
+ const result = await onToolCall({ toolCall: value });
457
+ if (result != null) {
458
+ const invocation = {
459
+ state: 'result',
460
+ step,
461
+ ...value,
462
+ result,
463
+ } as const;
464
+
465
+ // store the result in the tool invocation
466
+ message.toolInvocations![message.toolInvocations!.length - 1] = invocation;
467
+
468
+ updateToolInvocationPart(value.toolCallId, invocation);
469
+
470
+ execUpdate();
471
+ }
472
+ }
473
+ },
474
+ onToolResultPart(value) {
475
+ const toolInvocations = message.toolInvocations;
476
+
477
+ if (toolInvocations == null) {
478
+ throw new Error('tool_result must be preceded by a tool_call');
479
+ }
480
+
481
+ // find if there is any tool invocation with the same toolCallId
482
+ // and replace it with the result
483
+ const toolInvocationIndex = toolInvocations.findIndex(invocation => invocation.toolCallId === value.toolCallId);
484
+
485
+ if (toolInvocationIndex === -1) {
486
+ throw new Error('tool_result must be preceded by a tool_call with the same toolCallId');
487
+ }
488
+
489
+ const invocation = {
490
+ ...toolInvocations[toolInvocationIndex],
491
+ state: 'result' as const,
492
+ ...value,
493
+ } as const;
494
+
495
+ toolInvocations[toolInvocationIndex] = invocation as ToolInvocation;
496
+
497
+ updateToolInvocationPart(value.toolCallId, invocation as ToolInvocation);
498
+
499
+ execUpdate();
500
+ },
501
+ onDataPart(value) {
502
+ data.push(...value);
503
+ execUpdate();
504
+ },
505
+ onMessageAnnotationsPart(value) {
506
+ if (messageAnnotations == null) {
507
+ messageAnnotations = [...value];
508
+ } else {
509
+ messageAnnotations.push(...value);
510
+ }
511
+
512
+ execUpdate();
513
+ },
514
+ onFinishStepPart(value) {
515
+ step += 1;
516
+
517
+ // reset the current text and reasoning parts
518
+ currentTextPart = value.isContinued ? currentTextPart : undefined;
519
+ currentReasoningPart = undefined;
520
+ currentReasoningTextDetail = undefined;
521
+ },
522
+ onStartStepPart(value) {
523
+ // keep message id stable when we are updating an existing message:
524
+ if (!replaceLastMessage) {
525
+ message.id = value.messageId;
526
+ }
527
+
528
+ // add a step boundary part to the message
529
+ message.parts.push({ type: 'step-start' });
530
+ execUpdate();
531
+ },
532
+ onFinishMessagePart(value) {
533
+ finishReason = value.finishReason;
534
+ if (value.usage != null) {
535
+ // usage = calculateLanguageModelUsage(value.usage);
536
+ usage = value.usage;
537
+ }
538
+ },
539
+ onErrorPart(error) {
540
+ throw new Error(error);
541
+ },
135
542
  });
543
+
544
+ onFinish?.({ message, finishReason, usage });
136
545
  }
137
546
 
138
547
  /**
@@ -149,13 +558,42 @@ export class Agent extends BaseResource {
149
558
  > {
150
559
  const processedParams = {
151
560
  ...params,
152
- output: params.output instanceof ZodSchema ? zodToJsonSchema(params.output) : params.output,
153
- experimental_output:
154
- params.experimental_output instanceof ZodSchema
155
- ? zodToJsonSchema(params.experimental_output)
156
- : params.experimental_output,
561
+ output: params.output ? zodToJsonSchema(params.output) : undefined,
562
+ experimental_output: params.experimental_output ? zodToJsonSchema(params.experimental_output) : undefined,
563
+ runtimeContext: parseClientRuntimeContext(params.runtimeContext),
564
+ clientTools: processClientTools(params.clientTools),
565
+ };
566
+
567
+ // Create a readable stream that will handle the response processing
568
+ const { readable, writable } = new TransformStream<Uint8Array, Uint8Array>();
569
+
570
+ // Start processing the response in the background
571
+ const response = await this.processStreamResponse(processedParams, writable);
572
+
573
+ // Create a new response with the readable stream
574
+ const streamResponse = new Response(readable, {
575
+ status: response.status,
576
+ statusText: response.statusText,
577
+ headers: response.headers,
578
+ }) as Response & {
579
+ processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
580
+ };
581
+
582
+ // Add the processDataStream method to the response
583
+ streamResponse.processDataStream = async (options = {}) => {
584
+ await processDataStream({
585
+ stream: streamResponse.body as ReadableStream<Uint8Array>,
586
+ ...options,
587
+ });
157
588
  };
158
589
 
590
+ return streamResponse;
591
+ }
592
+
593
+ /**
594
+ * Processes the stream response and handles tool calls
595
+ */
596
+ private async processStreamResponse(processedParams: any, writable: WritableStream<Uint8Array>) {
159
597
  const response: Response & {
160
598
  processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
161
599
  } = await this.request(`/api/agents/${this.agentId}/stream`, {
@@ -168,13 +606,134 @@ export class Agent extends BaseResource {
168
606
  throw new Error('No response body');
169
607
  }
170
608
 
171
- response.processDataStream = async (options = {}) => {
172
- await processDataStream({
173
- stream: response.body as ReadableStream<Uint8Array>,
174
- ...options,
175
- });
176
- };
609
+ try {
610
+ let toolCalls: ToolInvocation[] = [];
611
+ let finishReasonToolCalls = false;
612
+ let messages: UIMessage[] = [];
613
+ let hasProcessedToolCalls = false;
614
+
615
+ // Use tee() to split the stream into two branches
616
+ const [streamForWritable, streamForProcessing] = response.body.tee();
617
+
618
+ // Pipe one branch to the writable stream
619
+ streamForWritable
620
+ .pipeTo(writable, {
621
+ preventClose: true,
622
+ })
623
+ .catch(error => {
624
+ console.error('Error piping to writable stream:', error);
625
+ });
626
+
627
+ // Process the other branch for chat response handling
628
+ this.processChatResponse({
629
+ stream: streamForProcessing,
630
+ update: ({ message }) => {
631
+ const existingIndex = messages.findIndex(m => m.id === message.id);
632
+
633
+ if (existingIndex !== -1) {
634
+ messages[existingIndex] = message;
635
+ } else {
636
+ messages.push(message);
637
+ }
638
+ },
639
+ onFinish: async ({ finishReason, message }) => {
640
+ if (finishReason === 'tool-calls') {
641
+ const toolCall = [...(message?.parts ?? [])]
642
+ .reverse()
643
+ .find(part => part.type === 'tool-invocation')?.toolInvocation;
644
+ if (toolCall) {
645
+ toolCalls.push(toolCall);
646
+ }
647
+
648
+ // Handle tool calls if needed
649
+ for (const toolCall of toolCalls) {
650
+ const clientTool = processedParams.clientTools?.[toolCall.toolName] as Tool;
651
+ if (clientTool && clientTool.execute) {
652
+ const result = await clientTool.execute(
653
+ {
654
+ context: toolCall?.args,
655
+ runId: processedParams.runId,
656
+ resourceId: processedParams.resourceId,
657
+ threadId: processedParams.threadId,
658
+ runtimeContext: processedParams.runtimeContext as RuntimeContext,
659
+ },
660
+ {
661
+ messages: (response as unknown as { messages: CoreMessage[] }).messages,
662
+ toolCallId: toolCall?.toolCallId,
663
+ },
664
+ );
665
+
666
+ const lastMessage: UIMessage = JSON.parse(JSON.stringify(messages[messages.length - 1]));
667
+
668
+ const toolInvocationPart = lastMessage?.parts?.find(
669
+ part => part.type === 'tool-invocation' && part.toolInvocation?.toolCallId === toolCall.toolCallId,
670
+ ) as any | undefined;
671
+
672
+ if (toolInvocationPart) {
673
+ toolInvocationPart.toolInvocation = {
674
+ ...toolInvocationPart.toolInvocation,
675
+ state: 'result',
676
+ result,
677
+ };
678
+ }
679
+
680
+ const toolInvocation = lastMessage?.toolInvocations?.find(
681
+ toolInvocation => toolInvocation.toolCallId === toolCall.toolCallId,
682
+ ) as ToolInvocation | undefined;
683
+
684
+ if (toolInvocation) {
685
+ toolInvocation.state = 'result';
686
+ // @ts-ignore
687
+ toolInvocation.result = result;
688
+ }
689
+
690
+ // write the tool result part to the stream
691
+ const writer = writable.getWriter();
177
692
 
693
+ try {
694
+ await writer.write(
695
+ new TextEncoder().encode(
696
+ 'a:' +
697
+ JSON.stringify({
698
+ toolCallId: toolCall.toolCallId,
699
+ result,
700
+ }) +
701
+ '\n',
702
+ ),
703
+ );
704
+ } finally {
705
+ writer.releaseLock();
706
+ }
707
+
708
+ // Convert messages to the correct format for the recursive call
709
+ const originalMessages = processedParams.messages;
710
+ const messageArray = Array.isArray(originalMessages) ? originalMessages : [originalMessages];
711
+
712
+ // Recursively call stream with updated messages
713
+ this.processStreamResponse(
714
+ {
715
+ ...processedParams,
716
+ messages: [...messageArray, ...messages.filter(m => m.id !== lastMessage.id), lastMessage],
717
+ },
718
+ writable,
719
+ ).catch(error => {
720
+ console.error('Error processing stream response:', error);
721
+ });
722
+ }
723
+ }
724
+ } else {
725
+ setTimeout(() => {
726
+ writable.close();
727
+ }, 0);
728
+ }
729
+ },
730
+ lastMessage: undefined,
731
+ }).catch(error => {
732
+ console.error('Error processing stream response:', error);
733
+ });
734
+ } catch (error) {
735
+ console.error('Error processing stream response:', error);
736
+ }
178
737
  return response;
179
738
  }
180
739
 
@@ -187,6 +746,23 @@ export class Agent extends BaseResource {
187
746
  return this.request(`/api/agents/${this.agentId}/tools/${toolId}`);
188
747
  }
189
748
 
749
+ /**
750
+ * Executes a tool for the agent
751
+ * @param toolId - ID of the tool to execute
752
+ * @param params - Parameters required for tool execution
753
+ * @returns Promise containing the tool execution results
754
+ */
755
+ executeTool(toolId: string, params: { data: any; runtimeContext?: RuntimeContext }): Promise<any> {
756
+ const body = {
757
+ data: params.data,
758
+ runtimeContext: params.runtimeContext ? Object.fromEntries(params.runtimeContext.entries()) : undefined,
759
+ };
760
+ return this.request(`/api/agents/${this.agentId}/tools/${toolId}/execute`, {
761
+ method: 'POST',
762
+ body,
763
+ });
764
+ }
765
+
190
766
  /**
191
767
  * Retrieves evaluation results for the agent
192
768
  * @returns Promise containing agent evaluations
@@ -202,4 +778,16 @@ export class Agent extends BaseResource {
202
778
  liveEvals(): Promise<GetEvalsByAgentIdResponse> {
203
779
  return this.request(`/api/agents/${this.agentId}/evals/live`);
204
780
  }
781
+
782
+ /**
783
+ * Updates the model for the agent
784
+ * @param params - Parameters for updating the model
785
+ * @returns Promise containing the updated model
786
+ */
787
+ updateModel(params: { model: MastraLanguageModel }): Promise<{ message: string }> {
788
+ return this.request(`/api/agents/${this.agentId}/model`, {
789
+ method: 'POST',
790
+ body: params,
791
+ });
792
+ }
205
793
  }