@@ -20,8 +20,7 @@ import {
2020 debugError ,
2121 debugStart ,
2222 isDebugEnabled ,
23- popParent ,
24- pushParent ,
23+ runWithDebugParent ,
2524} from "../utils/debug" ;
2625
2726export interface TaskOutput {
@@ -209,73 +208,134 @@ export function createTaskTool(
209208 } )
210209 : "" ;
211210
212- // Push this task as parent context for child tool calls
213- if ( debugId ) pushParent ( debugId ) ;
214-
215- try {
216- const model = typeConfig . model || defaultModel ;
217- // Custom tools override the type's default tools, additionalTools are merged in
218- const tools = filterTools (
219- allTools ,
220- customTools ?? typeConfig . tools ,
221- typeConfig . additionalTools ,
222- ) ;
223- // Custom system_prompt overrides the type's default
224- const systemPrompt = system_prompt ?? typeConfig . systemPrompt ;
225-
226- // Merge budget stopWhen with existing stop conditions
227- const baseStopWhen =
228- typeConfig . stopWhen ?? defaultStopWhen ?? stepCountIs ( 15 ) ;
229- const effectiveStopWhen = budget
230- ? [ baseStopWhen , budget . stopWhen ] . flat ( )
231- : baseStopWhen ;
232-
233- // Common options for both generateText and streamText
234- const commonOptions = {
235- model,
236- tools,
237- system : systemPrompt ,
238- prompt,
239- stopWhen : effectiveStopWhen ,
240- prepareStep : typeConfig . prepareStep ,
241- } ;
242-
243- // Use streamText if streamWriter is provided, otherwise generateText
244- if ( streamWriter ) {
245- // Emit start event
246- const startId = generateEventId ( ) ;
247- streamWriter . write ( {
248- type : "data-subagent" ,
249- id : startId ,
250- data : {
251- event : "start" ,
211+ const executeTask = async ( ) : Promise < TaskOutput | TaskError > => {
212+ try {
213+ const model = typeConfig . model || defaultModel ;
214+ // Custom tools override the type's default tools, additionalTools are merged in
215+ const tools = filterTools (
216+ allTools ,
217+ customTools ?? typeConfig . tools ,
218+ typeConfig . additionalTools ,
219+ ) ;
220+ // Custom system_prompt overrides the type's default
221+ const systemPrompt = system_prompt ?? typeConfig . systemPrompt ;
222+
223+ // Merge budget stopWhen with existing stop conditions
224+ const baseStopWhen =
225+ typeConfig . stopWhen ?? defaultStopWhen ?? stepCountIs ( 15 ) ;
226+ const effectiveStopWhen = budget
227+ ? [ baseStopWhen , budget . stopWhen ] . flat ( )
228+ : baseStopWhen ;
229+
230+ // Common options for both generateText and streamText
231+ const commonOptions = {
232+ model,
233+ tools,
234+ system : systemPrompt ,
235+ prompt,
236+ stopWhen : effectiveStopWhen ,
237+ prepareStep : typeConfig . prepareStep ,
238+ } ;
239+
240+ // Use streamText if streamWriter is provided, otherwise generateText
241+ if ( streamWriter ) {
242+ // Emit start event
243+ const startId = generateEventId ( ) ;
244+ streamWriter . write ( {
245+ type : "data-subagent" ,
246+ id : startId ,
247+ data : {
248+ event : "start" ,
249+ subagent : subagent_type ,
250+ description,
251+ } satisfies SubagentEventData ,
252+ } ) ;
253+
254+ const result = streamText ( {
255+ ...commonOptions ,
256+ onStepFinish : async ( step ) => {
257+ // Track cost before anything else
258+ budget ?. onStepFinish ( step ) ;
259+ // Stream tool calls
260+ if ( step . toolCalls ?. length ) {
261+ for ( const tc of step . toolCalls ) {
262+ const eventId = generateEventId ( ) ;
263+ streamWriter . write ( {
264+ type : "data-subagent" ,
265+ id : eventId ,
266+ data : {
267+ event : "tool-call" ,
268+ subagent : subagent_type ,
269+ description,
270+ toolName : tc . toolName ,
271+ args : tc . input as Record < string , unknown > ,
272+ } satisfies SubagentEventData ,
273+ } ) ;
274+ }
275+ }
276+ // Call subagent-specific callback
277+ await typeConfig . onStepFinish ?.( step ) ;
278+ // Call default callback with subagent context
279+ await defaultOnStepFinish ?.( {
280+ subagentType : subagent_type ,
281+ description,
282+ step,
283+ } ) ;
284+ } ,
285+ } ) ;
286+
287+ // Wait for stream to complete
288+ const text = await result . text ;
289+ const usage = await result . usage ;
290+ const response = await result . response ;
291+
292+ // Emit done event
293+ streamWriter . write ( {
294+ type : "data-subagent" ,
295+ id : generateEventId ( ) ,
296+ data : {
297+ event : "done" ,
298+ subagent : subagent_type ,
299+ description,
300+ } satisfies SubagentEventData ,
301+ } ) ;
302+
303+ // Emit complete event with full messages for UI access
304+ streamWriter . write ( {
305+ type : "data-subagent" ,
306+ id : generateEventId ( ) ,
307+ data : {
308+ event : "complete" ,
309+ subagent : subagent_type ,
310+ description,
311+ messages : response . messages ,
312+ } satisfies SubagentEventData ,
313+ } ) ;
314+
315+ const durationMs = Math . round ( performance . now ( ) - startTime ) ;
316+
317+ return {
318+ result : text ,
319+ usage :
320+ usage . inputTokens !== undefined &&
321+ usage . outputTokens !== undefined
322+ ? {
323+ input_tokens : usage . inputTokens ,
324+ output_tokens : usage . outputTokens ,
325+ }
326+ : undefined ,
327+ duration_ms : durationMs ,
252328 subagent : subagent_type ,
253329 description,
254- } satisfies SubagentEventData ,
255- } ) ;
330+ } ;
331+ }
256332
257- const result = streamText ( {
333+ // Default: use generateText (no streaming)
334+ const result = await generateText ( {
258335 ...commonOptions ,
259336 onStepFinish : async ( step ) => {
260337 // Track cost before anything else
261338 budget ?. onStepFinish ( step ) ;
262- // Stream tool calls
263- if ( step . toolCalls ?. length ) {
264- for ( const tc of step . toolCalls ) {
265- const eventId = generateEventId ( ) ;
266- streamWriter . write ( {
267- type : "data-subagent" ,
268- id : eventId ,
269- data : {
270- event : "tool-call" ,
271- subagent : subagent_type ,
272- description,
273- toolName : tc . toolName ,
274- args : tc . input as Record < string , unknown > ,
275- } satisfies SubagentEventData ,
276- } ) ;
277- }
278- }
279339 // Call subagent-specific callback
280340 await typeConfig . onStepFinish ?.( step ) ;
281341 // Call default callback with subagent context
@@ -287,136 +347,63 @@ export function createTaskTool(
287347 } ,
288348 } ) ;
289349
290- // Wait for stream to complete
291- const text = await result . text ;
292- const usage = await result . usage ;
293- const response = await result . response ;
294-
295- // Emit done event
296- streamWriter . write ( {
297- type : "data-subagent" ,
298- id : generateEventId ( ) ,
299- data : {
300- event : "done" ,
301- subagent : subagent_type ,
302- description,
303- } satisfies SubagentEventData ,
304- } ) ;
305-
306- // Emit complete event with full messages for UI access
307- streamWriter . write ( {
308- type : "data-subagent" ,
309- id : generateEventId ( ) ,
310- data : {
311- event : "complete" ,
312- subagent : subagent_type ,
313- description,
314- messages : response . messages ,
315- } satisfies SubagentEventData ,
316- } ) ;
317-
318350 const durationMs = Math . round ( performance . now ( ) - startTime ) ;
319351
320- // Pop parent context and emit debug end
321- if ( debugId ) {
322- popParent ( ) ;
323- debugEnd ( debugId , "task" , {
324- summary : {
325- tokens : {
326- input : usage . inputTokens ,
327- output : usage . outputTokens ,
328- } ,
329- steps : response . messages ?. length ,
330- } ,
331- duration_ms : durationMs ,
332- } ) ;
333- }
352+ // Format usage
353+ const usage =
354+ result . usage . inputTokens !== undefined &&
355+ result . usage . outputTokens !== undefined
356+ ? {
357+ input_tokens : result . usage . inputTokens ,
358+ output_tokens : result . usage . outputTokens ,
359+ }
360+ : undefined ;
334361
335362 return {
336- result : text ,
337- usage :
338- usage . inputTokens !== undefined &&
339- usage . outputTokens !== undefined
340- ? {
341- input_tokens : usage . inputTokens ,
342- output_tokens : usage . outputTokens ,
343- }
344- : undefined ,
363+ result : result . text ,
364+ usage,
345365 duration_ms : durationMs ,
346366 subagent : subagent_type ,
347367 description,
348368 } ;
369+ } catch ( error ) {
370+ const errorMessage =
371+ error instanceof Error ? error . message : "Unknown error" ;
372+
373+ const durationMs = Math . round ( performance . now ( ) - startTime ) ;
374+ return {
375+ error : errorMessage ,
376+ subagent : subagent_type ,
377+ description,
378+ duration_ms : durationMs ,
379+ } ;
349380 }
381+ } ;
350382
351- // Default: use generateText (no streaming)
352- const result = await generateText ( {
353- ...commonOptions ,
354- onStepFinish : async ( step ) => {
355- // Track cost before anything else
356- budget ?. onStepFinish ( step ) ;
357- // Call subagent-specific callback
358- await typeConfig . onStepFinish ?.( step ) ;
359- // Call default callback with subagent context
360- await defaultOnStepFinish ?.( {
361- subagentType : subagent_type ,
362- description,
363- step,
364- } ) ;
365- } ,
366- } ) ;
367-
368- const durationMs = Math . round ( performance . now ( ) - startTime ) ;
369-
370- // Format usage
371- const usage =
372- result . usage . inputTokens !== undefined &&
373- result . usage . outputTokens !== undefined
374- ? {
375- input_tokens : result . usage . inputTokens ,
376- output_tokens : result . usage . outputTokens ,
377- }
378- : undefined ;
379-
380- // Pop parent context and emit debug end
381- if ( debugId ) {
382- popParent ( ) ;
383+ // Wrap in debug parent context so child tool calls are correctly attributed
384+ const result = await runWithDebugParent ( debugId , executeTask ) ;
385+
386+ // Emit debug end/error outside the parent context for correct indent level
387+ if ( debugId ) {
388+ if ( "error" in result ) {
389+ debugError ( debugId , "task" , result . error ) ;
390+ } else {
383391 debugEnd ( debugId , "task" , {
384392 summary : {
385- tokens : {
386- input : result . usage . inputTokens ,
387- output : result . usage . outputTokens ,
388- } ,
389- steps : result . steps ?. length ,
393+ tokens : result . usage
394+ ? {
395+ input : result . usage . input_tokens ,
396+ output : result . usage . output_tokens ,
397+ }
398+ : undefined ,
390399 } ,
391- duration_ms : durationMs ,
400+ duration_ms :
401+ result . duration_ms ?? Math . round ( performance . now ( ) - startTime ) ,
392402 } ) ;
393403 }
394-
395- return {
396- result : result . text ,
397- usage,
398- duration_ms : durationMs ,
399- subagent : subagent_type ,
400- description,
401- } ;
402- } catch ( error ) {
403- const errorMessage =
404- error instanceof Error ? error . message : "Unknown error" ;
405-
406- // Pop parent context and emit debug error
407- if ( debugId ) {
408- popParent ( ) ;
409- debugError ( debugId , "task" , errorMessage ) ;
410- }
411-
412- const durationMs = Math . round ( performance . now ( ) - startTime ) ;
413- return {
414- error : errorMessage ,
415- subagent : subagent_type ,
416- description,
417- duration_ms : durationMs ,
418- } ;
419404 }
405+
406+ return result ;
420407 } ,
421408 } ) ;
422409}
0 commit comments