@mastra/client-js 0.0.0-separate-trace-data-from-component-20250501141108 → 0.0.0-share-agent-metadata-with-cloud-20250718123411

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