55// this store is the dedicated child record the enter-session UI reads.
66
77import type { ReactorEmittedEvent } from "@intx/inference" ;
8+ import { toolCallPreview } from "./tool-preview.js" ;
89
910export type SubAgentSessionStatus = "running" | "done" | "failed" | "cancelled" ;
1011
@@ -21,6 +22,13 @@ export type OutstandingToolCall = {
2122 callId : string ;
2223 name : string ;
2324 startedAt : number ;
25+ /**
26+ * Bounded one-line subject of the call (command, path, pattern…), or null
27+ * when the args have nothing useful to show. Derived from the same raw
28+ * arguments the transcript stores so the lane and the body cannot disagree
29+ * about what is running (CL-5765).
30+ */
31+ preview : string | null ;
2432} ;
2533
2634export type SubAgentSession = {
@@ -30,14 +38,17 @@ export type SubAgentSession = {
3038 brief : string ;
3139 status : SubAgentSessionStatus ;
3240 toolNames : string [ ] ;
33- // Name and start clock of the OLDEST outstanding call — the one that
34- // explains the longest silence. Both are derived from `outstandingTools`;
35- // never assign them directly. Null when nothing is in flight.
41+ // Name, preview, and start clock of the OLDEST outstanding call — the one
42+ // that explains the longest silence. All three are derived from
43+ // `outstandingTools`; never assign them directly. Null when nothing is in
44+ // flight.
3645 //
3746 // A worker inside one long tool emits no events for the whole execution, so
3847 // silence alone cannot tell "wedged" from "running a ten-minute test suite".
39- // The start clock is the fact that separates them.
48+ // The start clock is the fact that separates them. The preview is what lets
49+ // an operator tell six shell commands apart on a fleet board.
4050 currentToolName : string | null ;
51+ currentToolPreview : string | null ;
4152 currentToolStartedAt : number | null ;
4253 // Calls the reactor has started and not yet reported a result for. The
4354 // reactor runs parallel calls concurrently, so this cannot collapse to one
@@ -116,39 +127,65 @@ function defaultCreateId(): string {
116127}
117128
118129/**
119- * The one place the displayed pair is produced, so a name can never be shown
120- * beside another call's clock. Called after every change to `outstandingTools`.
130+ * The one place the displayed triple is produced, so a name / preview can never
131+ * be shown beside another call's clock. Called after every change to
132+ * `outstandingTools`.
121133 */
122134function syncCurrentTool ( session : SubAgentSession ) : void {
123135 let oldest : OutstandingToolCall | undefined ;
124136 for ( const call of session . outstandingTools ) {
125137 if ( oldest === undefined || call . startedAt < oldest . startedAt ) oldest = call ;
126138 }
127139 session . currentToolName = oldest ?. name ?? null ;
140+ session . currentToolPreview = oldest ?. preview ?? null ;
128141 session . currentToolStartedAt = oldest ?. startedAt ?? null ;
129142}
130143
131144/**
132145 * `restartClock` marks the execution boundary: argument streaming already
133146 * registered the call, and the figure worth showing is time spent running it.
147+ * `rawArgs`, when known, refreshes the lane preview from the same payload the
148+ * transcript stores.
134149 */
135150function beginToolCall (
136151 session : SubAgentSession ,
137152 callId : string ,
138153 name : string ,
139154 nowMs : number ,
140155 restartClock = false ,
156+ rawArgs ?: string ,
141157) : void {
142158 const existing = session . outstandingTools . find ( ( c ) => c . callId === callId ) ;
159+ const preview =
160+ rawArgs !== undefined ? toolCallPreview ( name , rawArgs ) : ( existing ?. preview ?? null ) ;
143161 if ( existing !== undefined ) {
144162 existing . name = name ;
145163 if ( restartClock ) existing . startedAt = nowMs ;
164+ if ( rawArgs !== undefined ) existing . preview = preview ;
146165 } else {
147- session . outstandingTools . push ( { callId, name, startedAt : nowMs } ) ;
166+ session . outstandingTools . push ( {
167+ callId,
168+ name,
169+ startedAt : nowMs ,
170+ preview,
171+ } ) ;
148172 }
149173 syncCurrentTool ( session ) ;
150174}
151175
176+ /** Refresh the outstanding call's preview once more of its arguments stream in. */
177+ function refreshToolPreview (
178+ session : SubAgentSession ,
179+ callId : string ,
180+ name : string ,
181+ rawArgs : string ,
182+ ) : void {
183+ const existing = session . outstandingTools . find ( ( c ) => c . callId === callId ) ;
184+ if ( existing === undefined ) return ;
185+ existing . preview = toolCallPreview ( name , rawArgs ) ;
186+ syncCurrentTool ( session ) ;
187+ }
188+
152189/**
153190 * Retires exactly the call that finished. A result carrying an id we never saw
154191 * start retires nothing, rather than silently clearing a live sibling's clock.
@@ -338,6 +375,7 @@ export function createSubAgentSessionStore(
338375 status : "running" ,
339376 toolNames : [ ] ,
340377 currentToolName : null ,
378+ currentToolPreview : null ,
341379 currentToolStartedAt : null ,
342380 outstandingTools : [ ] ,
343381 entries : [ ] ,
@@ -398,6 +436,9 @@ export function createSubAgentSessionStore(
398436 if ( entry ?. kind !== "tool" ) continue ;
399437 if ( callId !== null && entry . callId !== callId ) continue ;
400438 entry . arguments = appendCapped ( entry . arguments , fragment , maxEntryChars ) ;
439+ // Preview tracks the same args the transcript holds so the lane
440+ // and the body never disagree about what is running.
441+ refreshToolPreview ( session , entry . callId , entry . name , entry . arguments ) ;
401442 return ;
402443 }
403444 return ;
@@ -418,14 +459,21 @@ export function createSubAgentSessionStore(
418459 if ( args !== null && args . length > 0 ) entry . arguments = args ;
419460 // Arguments finished streaming; the call itself is still in
420461 // flight, so this renames it rather than restarting its clock.
421- beginToolCall ( session , entry . callId , entry . name , now ( ) ) ;
462+ beginToolCall (
463+ session ,
464+ entry . callId ,
465+ entry . name ,
466+ now ( ) ,
467+ false ,
468+ entry . arguments ,
469+ ) ;
422470 return ;
423471 }
424472 // No matching start — record a complete tool entry.
425473 if ( name !== null ) {
426474 const idForEntry = callId ?? `${ name } -${ session . entries . length } ` ;
427475 if ( ! session . toolNames . includes ( name ) ) session . toolNames . push ( name ) ;
428- beginToolCall ( session , idForEntry , name , now ( ) ) ;
476+ beginToolCall ( session , idForEntry , name , now ( ) , false , args ?? "" ) ;
429477 pushEntry ( session , {
430478 kind : "tool" ,
431479 callId : idForEntry ,
@@ -438,14 +486,22 @@ export function createSubAgentSessionStore(
438486 case "tool.start" : {
439487 // tool.start is the execution-time counterpart of inference.tool_call.
440488 // Prefer inference events for the transcript; only fill gaps.
441- const call = ( event as { data ?: { call ?: { name ?: unknown ; id ?: unknown } } } ) . data ?. call ;
489+ const call = ( event as {
490+ data ?: { call ?: { name ?: unknown ; id ?: unknown ; arguments ?: unknown } } ;
491+ } ) . data ?. call ;
442492 const name = typeof call ?. name === "string" ? call . name : null ;
443493 if ( name === null ) return ;
444494 const callId = typeof call ?. id === "string" ? call . id : null ;
495+ const rawArgs =
496+ call ?. arguments !== undefined
497+ ? capText ( stringifyUnknown ( call . arguments ) , maxEntryChars )
498+ : undefined ;
445499 // Without an id there is no way to tell which of several parallel
446500 // calls this starts, and guessing would retime the wrong one. The
447501 // inference-side start already registered it, so leave it alone.
448- if ( callId !== null ) beginToolCall ( session , callId , name , now ( ) , true ) ;
502+ if ( callId !== null ) {
503+ beginToolCall ( session , callId , name , now ( ) , true , rawArgs ) ;
504+ }
449505 if ( ! session . toolNames . includes ( name ) ) session . toolNames . push ( name ) ;
450506 return ;
451507 }
@@ -553,6 +609,7 @@ function cloneSession(session: SubAgentSession): SubAgentSession {
553609 status : session . status ,
554610 toolNames : [ ...session . toolNames ] ,
555611 currentToolName : session . currentToolName ,
612+ currentToolPreview : session . currentToolPreview ,
556613 currentToolStartedAt : session . currentToolStartedAt ,
557614 outstandingTools : session . outstandingTools . map ( ( c ) => ( { ...c } ) ) ,
558615 entries : session . entries . map ( cloneEntry ) ,
0 commit comments