@@ -100,6 +100,7 @@ export class StreamingMessageAggregator {
100100 // (or the user retries) so retry UI/backoff logic doesn't misfire on send failures.
101101
102102 private pendingStreamStartTime : number | null = null ;
103+ private pendingScriptExecutions = new Set < string > ( ) ;
103104
104105 // Workspace creation timestamp (used for recency calculation)
105106 // REQUIRED: Backend guarantees every workspace has createdAt via config.ts
@@ -214,6 +215,7 @@ export class StreamingMessageAggregator {
214215
215216 // Just store the message - backend assigns historySequence
216217 this . messages . set ( message . id , message ) ;
218+ this . syncScriptExecutionState ( message ) ;
217219 this . invalidateCache ( ) ;
218220 }
219221
@@ -228,6 +230,7 @@ export class StreamingMessageAggregator {
228230 // First, add all messages to the map
229231 for ( const message of messages ) {
230232 this . messages . set ( message . id , message ) ;
233+ this . syncScriptExecutionState ( message ) ;
231234 }
232235
233236 // Then, reconstruct derived state from the most recent assistant message
@@ -277,6 +280,22 @@ export class StreamingMessageAggregator {
277280 private setPendingStreamStartTime ( time : number | null ) : void {
278281 this . pendingStreamStartTime = time ;
279282 }
283+ hasPendingScriptExecution ( ) : boolean {
284+ return this . pendingScriptExecutions . size > 0 ;
285+ }
286+
287+ private syncScriptExecutionState ( message : MuxMessage ) : void {
288+ const muxMetadata = message . metadata ?. muxMetadata ;
289+ if ( muxMetadata ?. type === "script-execution" && muxMetadata . result === undefined ) {
290+ this . pendingScriptExecutions . add ( message . id ) ;
291+ } else {
292+ this . pendingScriptExecutions . delete ( message . id ) ;
293+ }
294+ }
295+
296+ private clearScriptExecutionState ( messageId : string ) : void {
297+ this . pendingScriptExecutions . delete ( messageId ) ;
298+ }
280299
281300 getActiveStreams ( ) : StreamingContext [ ] {
282301 return Array . from ( this . activeStreams . values ( ) ) ;
@@ -324,6 +343,7 @@ export class StreamingMessageAggregator {
324343 clear ( ) : void {
325344 this . messages . clear ( ) ;
326345 this . activeStreams . clear ( ) ;
346+ this . pendingScriptExecutions . clear ( ) ;
327347 this . streamSequenceCounter = 0 ;
328348 this . invalidateCache ( ) ;
329349 }
@@ -339,6 +359,7 @@ export class StreamingMessageAggregator {
339359 const historySeq = message . metadata ?. historySequence ;
340360 if ( historySeq !== undefined && sequencesToDelete . has ( historySeq ) ) {
341361 this . messages . delete ( messageId ) ;
362+ this . clearScriptExecutionState ( messageId ) ;
342363 }
343364 }
344365
@@ -377,6 +398,7 @@ export class StreamingMessageAggregator {
377398 } ) ;
378399
379400 this . messages . set ( data . messageId , streamingMessage ) ;
401+ this . syncScriptExecutionState ( streamingMessage ) ;
380402 this . invalidateCache ( ) ;
381403 }
382404
@@ -454,6 +476,7 @@ export class StreamingMessageAggregator {
454476 } ;
455477
456478 this . messages . set ( data . messageId , message ) ;
479+ this . syncScriptExecutionState ( message ) ;
457480
458481 // Clean up stream-scoped state (active stream tracking, TODOs)
459482 this . cleanupStreamState ( data . messageId ) ;
@@ -702,6 +725,7 @@ export class StreamingMessageAggregator {
702725 }
703726 for ( const removeId of messagesToRemove ) {
704727 this . messages . delete ( removeId ) ;
728+ this . clearScriptExecutionState ( removeId ) ;
705729 }
706730 break ; // Found and handled the conflict
707731 }
0 commit comments