@@ -38,86 +38,91 @@ export function outputText(content: ReadonlyArray<{ type: string; text?: string
3838export function legacyTool ( input : {
3939 sessionID : string
4040 messageID : string
41- callID : string
42- name : string
43- state : SessionMessageAssistantTool [ "state" ]
44- time : SessionMessageAssistantTool [ "time" ]
45- executed ?: boolean
46- providerState ?: Record < string , unknown >
47- providerResultState ?: Record < string , unknown >
41+ tool : SessionMessageAssistantTool
4842} ) : ToolPart {
43+ const tool = input . tool
4944 const providerCall =
50- input . executed === undefined && input . providerState === undefined
45+ tool . executed === undefined && tool . providerState === undefined
5146 ? undefined
52- : { executed : input . executed , state : input . providerState }
47+ : { executed : tool . executed , state : tool . providerState }
5348 const providerResult =
54- input . executed === undefined && input . providerResultState === undefined
49+ tool . executed === undefined && tool . providerResultState === undefined
5550 ? undefined
56- : { executed : input . executed , state : input . providerResultState }
51+ : { executed : tool . executed , state : tool . providerResultState }
5752 const base = {
58- id : `prt_${ input . callID } ` ,
53+ id : `prt_${ tool . id } ` ,
5954 sessionID : input . sessionID ,
6055 messageID : input . messageID ,
6156 type : "tool" as const ,
62- callID : input . callID ,
63- tool : input . name ,
57+ callID : tool . id ,
58+ tool : tool . name ,
6459 }
65- if ( input . state . status === "pending" ) {
60+ if ( tool . state . status === "pending" ) {
6661 return {
6762 ...base ,
68- state : { status : "pending" , input : { } , raw : input . state . input } ,
63+ state : { status : "pending" , input : { } , raw : tool . state . input } ,
6964 }
7065 }
71- if ( input . state . status === "running" ) {
66+ if ( tool . state . status === "running" ) {
7267 return {
7368 ...base ,
7469 state : {
7570 status : "running" ,
76- input : input . state . input ,
77- title : input . name ,
78- metadata : { structured : input . state . structured , content : input . state . content , providerCall } ,
79- time : { start : input . time . ran ?? input . time . created } ,
71+ input : tool . state . input ,
72+ title : tool . name ,
73+ metadata : { structured : tool . state . structured , content : tool . state . content , providerCall } ,
74+ time : { start : tool . time . ran ?? tool . time . created } ,
8075 } ,
8176 }
8277 }
83- if ( input . state . status === "completed" ) {
78+ if ( tool . state . status === "completed" ) {
8479 return {
8580 ...base ,
8681 state : {
8782 status : "completed" ,
88- input : input . state . input ,
89- output : outputText ( input . state . content ) ,
90- title : input . name ,
83+ input : tool . state . input ,
84+ output : outputText ( tool . state . content ) ,
85+ title : tool . name ,
9186 metadata : {
92- structured : input . state . structured ,
93- content : input . state . content ,
94- outputPaths : input . state . outputPaths ,
95- result : input . state . result ,
87+ structured : tool . state . structured ,
88+ content : tool . state . content ,
89+ outputPaths : tool . state . outputPaths ,
90+ result : tool . state . result ,
9691 providerCall,
9792 providerResult,
9893 } ,
99- time : { start : input . time . ran ?? input . time . created , end : input . time . completed ?? input . time . created } ,
94+ time : { start : tool . time . ran ?? tool . time . created , end : tool . time . completed ?? tool . time . created } ,
10095 } ,
10196 }
10297 }
10398 return {
10499 ...base ,
105100 state : {
106101 status : "error" ,
107- input : input . state . input ,
108- error : input . state . error . message ,
102+ input : tool . state . input ,
103+ error : tool . state . error . message ,
109104 metadata : {
110- structured : input . state . structured ,
111- content : input . state . content ,
112- result : input . state . result ,
105+ structured : tool . state . structured ,
106+ content : tool . state . content ,
107+ result : tool . state . result ,
113108 providerCall,
114109 providerResult,
115110 } ,
116- time : { start : input . time . ran ?? input . time . created , end : input . time . completed ?? input . time . created } ,
111+ time : { start : tool . time . ran ?? tool . time . created , end : tool . time . completed ?? tool . time . created } ,
117112 } ,
118113 }
119114}
120115
116+ export function nextFragmentID ( kind : "text" | "reasoning" , ordinals : Map < string , number > , messageID : string ) {
117+ const ordinal = ordinals . get ( messageID ) ?? 0
118+ ordinals . set ( messageID , ordinal + 1 )
119+ return `${ kind } :${ ordinal } `
120+ }
121+
122+ export function currentFragmentID ( kind : "text" | "reasoning" , ordinals : Map < string , number > , messageID : string ) {
123+ return `${ kind } :${ Math . max ( 0 , ( ordinals . get ( messageID ) ?? 1 ) - 1 ) } `
124+ }
125+
121126export function toolCommit ( part : ToolPart , phase : "start" | "progress" | "final" ) : StreamCommit {
122127 const status = part . state . status
123128 const text =
@@ -153,7 +158,6 @@ type ToolTrack = {
153158 name : string
154159 input : Record < string , unknown >
155160 started : number
156- executed ?: boolean
157161 providerState ?: Record < string , unknown >
158162}
159163
@@ -173,8 +177,6 @@ type ChildState = {
173177 projectedReasoning : Map < string , string >
174178 textOrdinals : Map < string , number >
175179 reasoningOrdinals : Map < string , number >
176- activeText : Map < string , string >
177- activeReasoning : Map < string , string >
178180 tools : Map < string , ToolTrack >
179181 finishedTools : Set < string >
180182 messageIDs : Set < string >
@@ -235,7 +237,6 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
235237 // Live subagent tool calls in the parent, so tool.success structured output
236238 // can be joined with the call's input metadata.
237239 const pendingCalls = new Map < string , Record < string , unknown > > ( )
238- const subagentCalls = new Set < string > ( )
239240 // Foreign sessions already resolved through session.get. Non-children stay
240241 // cached so unrelated concurrent sessions are checked at most once.
241242 const checked = new Set < string > ( )
@@ -263,8 +264,6 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
263264 projectedReasoning : new Map ( ) ,
264265 textOrdinals : new Map ( ) ,
265266 reasoningOrdinals : new Map ( ) ,
266- activeText : new Map ( ) ,
267- activeReasoning : new Map ( ) ,
268267 tools : new Map ( ) ,
269268 finishedTools : new Set ( ) ,
270269 messageIDs : new Set ( ) ,
@@ -328,13 +327,7 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
328327 const part = legacyTool ( {
329328 sessionID : child . sessionID ,
330329 messageID,
331- callID : item . id ,
332- name : item . name ,
333- state : item . state ,
334- time : item . time ,
335- executed : item . executed ,
336- providerState : item . providerState ,
337- providerResultState : item . providerResultState ,
330+ tool : item ,
338331 } )
339332 if ( item . state . status === "pending" ) return
340333 child . callIDs . add ( item . id )
@@ -355,8 +348,6 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
355348 child . projectedReasoning . clear ( )
356349 child . textOrdinals . clear ( )
357350 child . reasoningOrdinals . clear ( )
358- child . activeText . clear ( )
359- child . activeReasoning . clear ( )
360351 child . finishedTools . clear ( )
361352 child . messageIDs . clear ( )
362353 child . callIDs . clear ( )
@@ -477,15 +468,11 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
477468 return
478469 }
479470 if ( event . type === "session.text.started" ) {
480- const ordinal = child . textOrdinals . get ( event . data . assistantMessageID ) ?? 0
481- child . textOrdinals . set ( event . data . assistantMessageID , ordinal + 1 )
482- child . activeText . set ( event . data . assistantMessageID , `text:${ ordinal } ` )
471+ nextFragmentID ( "text" , child . textOrdinals , event . data . assistantMessageID )
483472 return
484473 }
485474 if ( event . type === "session.text.delta" ) {
486- const id =
487- child . activeText . get ( event . data . assistantMessageID ) ??
488- `text:${ Math . max ( 0 , ( child . textOrdinals . get ( event . data . assistantMessageID ) ?? 1 ) - 1 ) } `
475+ const id = currentFragmentID ( "text" , child . textOrdinals , event . data . assistantMessageID )
489476 const key = fragmentKey ( event . data . assistantMessageID , id )
490477 const projected = child . projectedText . get ( key )
491478 const covered = projected ?. indexOf ( event . data . delta ) ?? - 1
@@ -508,13 +495,10 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
508495 return
509496 }
510497 if ( event . type === "session.text.ended" ) {
511- const id =
512- child . activeText . get ( event . data . assistantMessageID ) ??
513- `text:${ Math . max ( 0 , ( child . textOrdinals . get ( event . data . assistantMessageID ) ?? 1 ) - 1 ) } `
498+ const id = currentFragmentID ( "text" , child . textOrdinals , event . data . assistantMessageID )
514499 const key = fragmentKey ( event . data . assistantMessageID , id )
515500 child . text . set ( key , event . data . text )
516501 child . projectedText . delete ( key )
517- child . activeText . delete ( event . data . assistantMessageID )
518502 setFrame ( child , key , {
519503 kind : "assistant" ,
520504 source : "assistant" ,
@@ -528,15 +512,11 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
528512 return
529513 }
530514 if ( event . type === "session.reasoning.started" ) {
531- const ordinal = child . reasoningOrdinals . get ( event . data . assistantMessageID ) ?? 0
532- child . reasoningOrdinals . set ( event . data . assistantMessageID , ordinal + 1 )
533- child . activeReasoning . set ( event . data . assistantMessageID , `reasoning:${ ordinal } ` )
515+ nextFragmentID ( "reasoning" , child . reasoningOrdinals , event . data . assistantMessageID )
534516 return
535517 }
536518 if ( event . type === "session.reasoning.delta" ) {
537- const id =
538- child . activeReasoning . get ( event . data . assistantMessageID ) ??
539- `reasoning:${ Math . max ( 0 , ( child . reasoningOrdinals . get ( event . data . assistantMessageID ) ?? 1 ) - 1 ) } `
519+ const id = currentFragmentID ( "reasoning" , child . reasoningOrdinals , event . data . assistantMessageID )
540520 const key = fragmentKey ( event . data . assistantMessageID , id )
541521 const projected = child . projectedReasoning . get ( key )
542522 const covered = projected ?. indexOf ( event . data . delta ) ?? - 1
@@ -559,13 +539,10 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
559539 return
560540 }
561541 if ( event . type === "session.reasoning.ended" ) {
562- const id =
563- child . activeReasoning . get ( event . data . assistantMessageID ) ??
564- `reasoning:${ Math . max ( 0 , ( child . reasoningOrdinals . get ( event . data . assistantMessageID ) ?? 1 ) - 1 ) } `
542+ const id = currentFragmentID ( "reasoning" , child . reasoningOrdinals , event . data . assistantMessageID )
565543 const key = fragmentKey ( event . data . assistantMessageID , id )
566544 child . reasoning . set ( key , event . data . text )
567545 child . projectedReasoning . delete ( key )
568- child . activeReasoning . delete ( event . data . assistantMessageID )
569546 if ( ! input . thinking ) return
570547 setFrame ( child , key , {
571548 kind : "reasoning" ,
@@ -588,7 +565,6 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
588565 name : current ?. name ?? "tool" ,
589566 input : event . data . input ,
590567 started : current ?. started ?? event . created ,
591- executed : event . data . executed ,
592568 providerState : event . data . state ,
593569 } )
594570 childTool (
@@ -650,7 +626,14 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
650626 notifyDetail ( child )
651627 return
652628 }
629+ if ( event . type === "session.step.ended" ) {
630+ child . textOrdinals . delete ( event . data . assistantMessageID )
631+ child . reasoningOrdinals . delete ( event . data . assistantMessageID )
632+ return
633+ }
653634 if ( event . type === "session.step.failed" ) {
635+ child . textOrdinals . delete ( event . data . assistantMessageID )
636+ child . reasoningOrdinals . delete ( event . data . assistantMessageID )
654637 setFrame ( child , `error:step:${ event . data . assistantMessageID } ` , {
655638 kind : "error" ,
656639 source : "system" ,
@@ -687,22 +670,20 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac
687670 return {
688671 main ( event ) {
689672 if ( event . type === "session.tool.input.started" ) {
690- if ( event . data . name === "subagent" ) subagentCalls . add ( event . data . callID )
673+ if ( event . data . name === "subagent" ) pendingCalls . set ( event . data . callID , { } )
691674 return
692675 }
693676 if ( event . type === "session.tool.called" ) {
694- if ( subagentCalls . has ( event . data . callID ) ) pendingCalls . set ( event . data . callID , event . data . input )
677+ if ( pendingCalls . has ( event . data . callID ) ) pendingCalls . set ( event . data . callID , event . data . input )
695678 return
696679 }
697680 if ( event . type === "session.tool.failed" ) {
698681 pendingCalls . delete ( event . data . callID )
699- subagentCalls . delete ( event . data . callID )
700682 return
701683 }
702684 if ( event . type !== "session.tool.success" ) return
703685 const pending = pendingCalls . get ( event . data . callID )
704686 pendingCalls . delete ( event . data . callID )
705- subagentCalls . delete ( event . data . callID )
706687 const found = childSessionID ( record ( event . data . structured ) )
707688 if ( ! found ) return
708689 const child = ensureChild ( found . sessionID )
0 commit comments