@@ -40,8 +40,8 @@ export interface MastraAgentConfig extends AgentConfig {
4040interface MastraAgentStreamOptions {
4141 onTextPart ?: ( text : string ) => void ;
4242 onFinishMessagePart ?: ( ) => void ;
43- onToolCallPart ?: ( streamPart : { toolCallId : string ; toolName : string ; args : any } ) => void ;
44- onToolResultPart ?: ( streamPart : { toolCallId : string ; result : any } ) => void ;
43+ onToolCallPart ?: ( streamPart : { toolCallId : string ; toolName : string ; args : unknown } ) => void ;
44+ onToolResultPart ?: ( streamPart : { toolCallId : string ; output : unknown } ) => void ;
4545 onError ?: ( error : Error ) => void ;
4646 onRunFinished ?: ( ) => Promise < void > ;
4747}
@@ -145,7 +145,7 @@ export class MastraAgent extends AbstractAgent {
145145 const toolCallResultEvent : ToolCallResultEvent = {
146146 type : EventType . TOOL_CALL_RESULT ,
147147 toolCallId : streamPart . toolCallId ,
148- content : JSON . stringify ( streamPart . result ) ,
148+ content : safeStringify ( streamPart . output ) ,
149149 messageId : randomUUID ( ) ,
150150 role : "tool" ,
151151 } ;
@@ -268,37 +268,13 @@ export class MastraAgent extends AbstractAgent {
268268 // Process it using the agent's built-in streaming mechanism
269269 if ( response && typeof response === "object" ) {
270270 for await ( const chunk of response . fullStream ) {
271- switch ( chunk . type ) {
272- case "text-delta" : {
273- onTextPart ?.( chunk . payload . text ) ;
274- break ;
275- }
276- case "tool-call" : {
277- onToolCallPart ?.( {
278- toolCallId : chunk . payload . toolCallId ,
279- toolName : chunk . payload . toolName ,
280- args : chunk . payload . args ,
281- } ) ;
282- break ;
283- }
284- case "tool-result" : {
285- onToolResultPart ?.( {
286- toolCallId : chunk . payload . toolCallId ,
287- result : chunk . payload . result ,
288- } ) ;
289- break ;
290- }
291-
292- case "error" : {
293- onError ?.( new Error ( chunk . payload . error as string ) ) ;
294- break ;
295- }
296-
297- case "finish" : {
298- onFinishMessagePart ?.( ) ;
299- break ;
300- }
301- }
271+ handleStreamChunk ( chunk , {
272+ onTextPart,
273+ onToolCallPart,
274+ onToolResultPart,
275+ onFinishMessagePart,
276+ onError,
277+ } ) ;
302278 }
303279
304280 await onRunFinished ?.( ) ;
@@ -323,35 +299,28 @@ export class MastraAgent extends AbstractAgent {
323299 if ( response && typeof response . processDataStream === "function" ) {
324300 await response . processDataStream ( {
325301 onChunk : async ( chunk ) => {
326- switch ( chunk . type ) {
327- case "text-delta" : {
328- onTextPart ?.( chunk . payload . text ) ;
329- break ;
330- }
331- case "tool-call" : {
332- onToolCallPart ?.( {
333- toolCallId : chunk . payload . toolCallId ,
334- toolName : chunk . payload . toolName ,
335- args : chunk . payload . args ,
336- } ) ;
337- break ;
338- }
339- case "tool-result" : {
340- onToolResultPart ?.( {
341- toolCallId : chunk . payload . toolCallId ,
342- result : chunk . payload . result ,
343- } ) ;
344- break ;
345- }
346-
347- case "finish" : {
348- onFinishMessagePart ?.( ) ;
349- break ;
350- }
351- }
302+ handleStreamChunk ( chunk , {
303+ onTextPart,
304+ onToolCallPart,
305+ onToolResultPart,
306+ onFinishMessagePart,
307+ onError,
308+ } ) ;
352309 } ,
353310 } ) ;
354311 await onRunFinished ?.( ) ;
312+ } else if ( response && typeof ( response as { fullStream ?: unknown } ) . fullStream === "object" ) {
313+ const fullStream = ( response as unknown as { fullStream : AsyncIterable < unknown > } ) . fullStream ;
314+ for await ( const chunk of fullStream ) {
315+ handleStreamChunk ( chunk , {
316+ onTextPart,
317+ onToolCallPart,
318+ onToolResultPart,
319+ onFinishMessagePart,
320+ onError,
321+ } ) ;
322+ }
323+ await onRunFinished ?.( ) ;
355324 } else {
356325 throw new Error ( "Invalid response from remote agent" ) ;
357326 }
@@ -379,3 +348,170 @@ export class MastraAgent extends AbstractAgent {
379348 return getNetwork ( options ) ;
380349 }
381350}
351+
352+ function handleStreamChunk (
353+ chunk : unknown ,
354+ handlers : {
355+ onTextPart ?: ( text : string ) => void ;
356+ onToolCallPart ?: ( streamPart : { toolCallId : string ; toolName : string ; args : unknown } ) => void ;
357+ onToolResultPart ?: ( streamPart : { toolCallId : string ; output : unknown } ) => void ;
358+ onFinishMessagePart ?: ( ) => void ;
359+ onError ?: ( error : Error ) => void ;
360+ } ,
361+ ) {
362+ if ( ! chunk || typeof chunk !== "object" || ! ( "type" in chunk ) ) {
363+ return ;
364+ }
365+
366+ const type = ( chunk as { type : string } ) . type ;
367+
368+ switch ( type ) {
369+ case "text-delta" : {
370+ const text = extractTextDelta ( chunk ) ;
371+ if ( typeof text === "string" && text . length > 0 ) {
372+ handlers . onTextPart ?.( text ) ;
373+ }
374+ break ;
375+ }
376+
377+ case "tool-call" : {
378+ const toolCall = extractToolCall ( chunk ) ;
379+ if ( toolCall ) {
380+ handlers . onToolCallPart ?.( toolCall ) ;
381+ }
382+ break ;
383+ }
384+
385+ case "tool-result" : {
386+ const toolResult = extractToolResult ( chunk ) ;
387+ if ( toolResult ) {
388+ handlers . onToolResultPart ?.( toolResult ) ;
389+ }
390+ break ;
391+ }
392+
393+ case "tool-error" : {
394+ handlers . onError ?.(
395+ new Error (
396+ safeStringify ( ( chunk as { error ?: unknown } ) . error ?? "Tool execution failed" ) ,
397+ ) ,
398+ ) ;
399+ break ;
400+ }
401+
402+ case "error" : {
403+ handlers . onError ?.( extractError ( chunk ) ?? new Error ( "Stream error" ) ) ;
404+ break ;
405+ }
406+
407+ case "finish" : {
408+ handlers . onFinishMessagePart ?.( ) ;
409+ break ;
410+ }
411+
412+ default :
413+ break ;
414+ }
415+ }
416+
417+ function extractTextDelta ( chunk : unknown ) : string | undefined {
418+ if ( ! chunk || typeof chunk !== "object" ) {
419+ return undefined ;
420+ }
421+
422+ const candidate = chunk as Record < string , unknown > ;
423+ if ( typeof candidate . text === "string" ) {
424+ return candidate . text ;
425+ }
426+ if ( typeof candidate . delta === "string" ) {
427+ return candidate . delta ;
428+ }
429+ const payload = candidate . payload as Record < string , unknown > | undefined ;
430+ if ( payload ) {
431+ if ( typeof payload . text === "string" ) {
432+ return payload . text ;
433+ }
434+ if ( typeof payload . delta === "string" ) {
435+ return payload . delta ;
436+ }
437+ }
438+ return undefined ;
439+ }
440+
441+ function extractToolCall ( chunk : unknown ) :
442+ | { toolCallId : string ; toolName : string ; args : unknown }
443+ | undefined {
444+ if ( ! chunk || typeof chunk !== "object" ) {
445+ return undefined ;
446+ }
447+
448+ const candidate = chunk as Record < string , unknown > ;
449+ const toolCallId = getString ( candidate . toolCallId ) ?? getString ( candidate . tool_call_id ) ;
450+ const toolName = getString ( candidate . toolName ) ?? getString ( candidate . tool_name ) ;
451+ const input = candidate . input ?? candidate . args ;
452+ const payload = candidate . payload as Record < string , unknown > | undefined ;
453+
454+ return toolCallId && toolName
455+ ? {
456+ toolCallId,
457+ toolName,
458+ args : input ?? payload ?. input ?? payload ?. args ,
459+ }
460+ : undefined ;
461+ }
462+
463+ function extractToolResult ( chunk : unknown ) :
464+ | { toolCallId : string ; output : unknown }
465+ | undefined {
466+ if ( ! chunk || typeof chunk !== "object" ) {
467+ return undefined ;
468+ }
469+
470+ const candidate = chunk as Record < string , unknown > ;
471+ const toolCallId = getString ( candidate . toolCallId ) ?? getString ( candidate . tool_call_id ) ;
472+ const output = candidate . output ?? candidate . result ;
473+ const payload = candidate . payload as Record < string , unknown > | undefined ;
474+
475+ return toolCallId
476+ ? {
477+ toolCallId,
478+ output : output ?? payload ?. output ?? payload ?. result ,
479+ }
480+ : undefined ;
481+ }
482+
483+ function extractError ( chunk : unknown ) : Error | undefined {
484+ if ( ! chunk || typeof chunk !== "object" ) {
485+ return undefined ;
486+ }
487+
488+ const candidate = chunk as Record < string , unknown > ;
489+ const payload = candidate . payload as Record < string , unknown > | undefined ;
490+ const errorValue = candidate . error ?? payload ?. error ?? payload ?. message ;
491+
492+ if ( ! errorValue ) {
493+ return undefined ;
494+ }
495+
496+ if ( errorValue instanceof Error ) {
497+ return errorValue ;
498+ }
499+
500+ const message = typeof errorValue === "string" ? errorValue : safeStringify ( errorValue ) ;
501+ return new Error ( message ) ;
502+ }
503+
504+ function safeStringify ( value : unknown ) : string {
505+ if ( typeof value === "string" ) {
506+ return value ;
507+ }
508+ try {
509+ return JSON . stringify ( value ?? { } ) ;
510+ } catch {
511+ return String ( value ) ;
512+ }
513+ }
514+
515+ function getString ( value : unknown ) : string | undefined {
516+ return typeof value === "string" ? value : undefined ;
517+ }
0 commit comments