@@ -96,16 +96,6 @@ export class ChatStore {
9696 /** Bound chat-ui transcript — set when the ChatTranscript component mounts. */
9797 private _transcript : TranscriptApi | null = null ;
9898
99- /**
100- * Accumulated thinking text keyed by thinking-id.
101- * upsertThinking() takes full text (not deltas), so we accumulate here then pass
102- * the growing string on each chunk.
103- */
104- private _thinkingTextMap = new Map < string , string > ( ) ;
105-
106- /** Epoch ms when the first thought chunk of the current turn arrived. */
107- private _thinkingStartedAt : number | null = null ;
108-
10999 constructor ( conversationId : string , projectId : string , taskId : string , initialModel ?: string ) {
110100 this . _conversationId = conversationId ;
111101 this . _projectId = projectId ;
@@ -156,8 +146,6 @@ export class ChatStore {
156146 if ( payload . phase === 'start' ) {
157147 this . items = [ ] ;
158148 this . _streamKeyMap . clear ( ) ;
159- this . _thinkingTextMap . clear ( ) ;
160- this . _thinkingStartedAt = null ;
161149 this . isClosed = false ;
162150 this . _transcript ?. reset ( ) ;
163151 } else {
@@ -202,8 +190,8 @@ export class ChatStore {
202190 text,
203191 streaming : false ,
204192 } ) ;
205- this . _transcript ?. appendMessageChunk ( ' user', userId , text ) ;
206- this . _transcript ?. finalizeTurn ( ) ;
193+ this . _transcript ?. dispatch ( { type : 'message_chunk' , role : ' user', id : userId , text } ) ;
194+ this . _transcript ?. dispatch ( { type : 'turn_done' } ) ;
207195
208196 void rpc . acp
209197 . prompt ( this . _conversationId , text )
@@ -274,26 +262,24 @@ export class ChatStore {
274262 const existing = this . _streamKeyMap . get ( streamKey ) ;
275263 if ( existing ) {
276264 existing . text += text ;
277- this . _transcript ?. appendMessageChunk ( role , messageId , text ) ;
278- return ;
265+ } else {
266+ const created = this . _pushMessage ( role , text ) ;
267+ this . _streamKeyMap . set ( streamKey , created ) ;
279268 }
280- const created = this . _pushMessage ( role , text ) ;
281- this . _streamKeyMap . set ( streamKey , created ) ;
282- this . _transcript ?. appendMessageChunk ( role , messageId , text ) ;
269+ this . _transcript ?. dispatch ( { type : 'message_chunk' , role, id : messageId , text } ) ;
283270 return ;
284271 }
285272
286273 // No messageId: append to the trailing bubble if it's the same role and still
287274 // streaming; otherwise start a new bubble.
288275 const last = this . items . at ( - 1 ) ;
289- const syntheticId = `${ role } -${ Date . now ( ) } -${ Math . random ( ) } ` ;
290276 if ( last && last . kind === 'message' && last . role === role && last . streaming ) {
291277 last . text += text ;
292- this . _transcript ?. appendMessageChunk ( role , last . id , text ) ;
278+ this . _transcript ?. dispatch ( { type : 'message_chunk' , role, id : last . id , text } ) ;
293279 return ;
294280 }
295- this . _pushMessage ( role , text ) ;
296- this . _transcript ?. appendMessageChunk ( role , syntheticId , text ) ;
281+ const created = this . _pushMessage ( role , text ) ;
282+ this . _transcript ?. dispatch ( { type : 'message_chunk' , role, id : created . id , text } ) ;
297283 }
298284
299285 private _appendThinkingChunk (
@@ -303,7 +289,7 @@ export class ChatStore {
303289 chunk . content . type === 'text' ? ( ( chunk . content as { text ?: string } ) . text ?? '' ) : '' ;
304290 const thinkingId = chunk . messageId ?? `thought-${ Date . now ( ) } ` ;
305291
306- // Mirror into items[] as a legacy thought message for hydration fallback .
292+ // Mirror into items[] as a legacy thought message for seed() hydration .
307293 const streamKey = `thought:${ thinkingId } ` ;
308294 const existing = this . _streamKeyMap . get ( streamKey ) ;
309295 if ( existing ) {
@@ -313,18 +299,8 @@ export class ChatStore {
313299 this . _streamKeyMap . set ( streamKey , created ) ;
314300 }
315301
316- // Drive TranscriptApi with full accumulated text (upsertThinking is not delta).
317- const accumulated = ( this . _thinkingTextMap . get ( thinkingId ) ?? '' ) + text ;
318- this . _thinkingTextMap . set ( thinkingId , accumulated ) ;
319- if ( this . _thinkingStartedAt === null ) {
320- this . _thinkingStartedAt = Date . now ( ) ;
321- }
322- this . _transcript ?. upsertThinking ( {
323- id : thinkingId ,
324- text : accumulated ,
325- status : 'thinking' ,
326- startedAt : this . _thinkingStartedAt ,
327- } ) ;
302+ // The transcript reducer owns startedAt and text accumulation — just dispatch the delta.
303+ this . _transcript ?. dispatch ( { type : 'thinking_chunk' , id : thinkingId , text } ) ;
328304 }
329305
330306 private _pushMessage (
@@ -358,6 +334,13 @@ export class ChatStore {
358334 if ( patch . toolName !== undefined ) existing . toolName = patch . toolName ;
359335 if ( patch . status !== undefined ) existing . status = patch . status ;
360336 if ( patch . inputSummary !== undefined ) existing . inputSummary = patch . inputSummary ;
337+ this . _transcript ?. dispatch ( {
338+ type : 'tool_update' ,
339+ id : patch . toolCallId ,
340+ status : patch . status ,
341+ name : patch . toolName ,
342+ inputSummary : patch . inputSummary ,
343+ } ) ;
361344 } else {
362345 this . items . push ( {
363346 kind : 'tool' ,
@@ -367,13 +350,13 @@ export class ChatStore {
367350 status : patch . status ?? 'running' ,
368351 inputSummary : patch . inputSummary ,
369352 } ) ;
353+ this . _transcript ?. dispatch ( {
354+ type : 'tool_start' ,
355+ id : patch . toolCallId ,
356+ name : patch . toolName ?? '(tool)' ,
357+ inputSummary : patch . inputSummary ,
358+ } ) ;
370359 }
371- this . _transcript ?. upsertTool ( {
372- id : patch . toolCallId ,
373- name : patch . toolName ?? '(tool)' ,
374- status : patch . status ?? 'running' ,
375- inputSummary : patch . inputSummary ,
376- } ) ;
377360 }
378361
379362 /** Marks all streaming message bubbles as settled and commits the active turn. */
@@ -382,9 +365,7 @@ export class ChatStore {
382365 if ( item . kind === 'message' ) item . streaming = false ;
383366 }
384367 this . _streamKeyMap . clear ( ) ;
385- this . _thinkingTextMap . clear ( ) ;
386- this . _thinkingStartedAt = null ;
387- this . _transcript ?. finalizeTurn ( ) ;
368+ this . _transcript ?. dispatch ( { type : 'turn_done' } ) ;
388369 }
389370
390371 private _summarizeInput ( input : unknown ) : string | undefined {
0 commit comments