@@ -144,22 +144,29 @@ function projectCommandValue(data: Record<string, unknown>): unknown {
144144}
145145
146146function summarizeToolTextOutput ( value : string ) : string | null {
147- const lines : string [ ] = [ ] ;
148- for ( const rawLine of value . split ( / \r ? \n / u) ) {
149- const line = rawLine . replace ( / \s + / g, " " ) . trim ( ) ;
147+ let meaningfulLineCount = 0 ;
148+ let offset = 0 ;
149+
150+ while ( offset <= value . length ) {
151+ const newlineIndex = value . indexOf ( "\n" , offset ) ;
152+ const lineEnd = newlineIndex === - 1 ? value . length : newlineIndex ;
153+ const line = value . slice ( offset , lineEnd ) . replace ( / \s + / g, " " ) . trim ( ) ;
150154 if ( line . length > 0 ) {
151- lines . push ( line ) ;
155+ meaningfulLineCount += 1 ;
156+ if ( line !== "```" ) {
157+ const summary = line . length <= 84 ? line : `${ line . slice ( 0 , 83 ) . trimEnd ( ) } …` ;
158+ // V8 can retain the full tool output behind a short sliced string.
159+ // Join a tiny character array so the returned preview owns its bytes.
160+ return Array . from ( summary ) . join ( "" ) ;
161+ }
152162 }
163+ if ( newlineIndex === - 1 ) {
164+ break ;
165+ }
166+ offset = newlineIndex + 1 ;
153167 }
154168
155- const firstLine = lines . find ( ( line ) => line !== "```" ) ;
156- if ( firstLine ) {
157- return firstLine . length <= 84 ? firstLine : `${ firstLine . slice ( 0 , 83 ) . trimEnd ( ) } …` ;
158- }
159- if ( lines . length > 1 ) {
160- return `${ lines . length . toLocaleString ( ) } lines` ;
161- }
162- return null ;
169+ return meaningfulLineCount > 1 ? `${ meaningfulLineCount . toLocaleString ( ) } lines` : null ;
163170}
164171
165172/**
@@ -488,9 +495,6 @@ function toolLifecycleIdentity(activity: OrchestrationThreadActivity): string |
488495 * update within the turn — a later update belongs to a subsequent call that
489496 * reuses the same identity and is still in flight. Rows without a lifecycle
490497 * identity pass through, matching the clients, which never collapse them.
491- * Live `thread.activity-appended` events are untouched: updates still stream
492- * in real time and the completion supersedes them on the client as before.
493- *
494498 * Deliberate divergence from client collapse: clients fold only *adjacent*
495499 * lifecycle rows, so a superseded update separated from its completion by an
496500 * interleaved parallel call renders as its own row today, and this drop
@@ -517,7 +521,7 @@ function dropSupersededToolUpdatedActivities(
517521 if ( ! identity ) {
518522 continue ;
519523 }
520- const key = `${ activity . turnId ?? "" }
524+ const key = `${ activity . turnId ?? "" } \u0000 ${ identity } ` ;
521525 const indices = completionIndicesByKey . get ( key ) ;
522526 if ( indices ) {
523527 indices . push ( index ) ;
@@ -537,7 +541,7 @@ function dropSupersededToolUpdatedActivities(
537541 if ( ! identity ) {
538542 return true ;
539543 }
540- const indices = completionIndicesByKey . get ( `${ activity . turnId ?? "" }
544+ const indices = completionIndicesByKey . get ( `${ activity . turnId ?? "" } \u0000 ${ identity } ` ) ;
541545 return ! indices ?. some ( ( completionIndex ) => completionIndex > index ) ;
542546 } ) ;
543547}
0 commit comments