@@ -347,25 +347,24 @@ function applyToolResult(
347347/**
348348 * Folds one wire envelope into the model. Pure accumulator: it mutates and
349349 * returns the same `model` (the streaming hot path keeps one model per turn).
350- * Idempotent by wire `seq` so reconnect replay over a populated model is a
351- * no-op for already-applied events, and replay into a fresh model rebuilds the
352- * identical tree.
350+ * `seq` is the monotonic wire cursor — the contract guarantees it is always a
351+ * finite number — so it is the sole ordering and idempotency key: an event at
352+ * or below the applied high-water mark is a replay and no-ops (reconnect replay
353+ * over a populated model is a no-op; replay into a fresh model rebuilds the
354+ * identical tree).
353355 */
354356export function reduceEvent ( model : TurnModel , envelope : PersistedStreamEventEnvelope ) : TurnModel {
355- const seq = typeof envelope . seq === 'number' ? envelope . seq : undefined
356- if ( seq !== undefined ) {
357- if ( seq <= model . lastSeq ) return model
358- model . lastSeq = seq
359- }
360- const seqNum = seq ?? model . order . length + 1
357+ const seq = envelope . seq
358+ if ( seq <= model . lastSeq ) return model
359+ model . lastSeq = seq
361360 const tsMs = tsToMs ( envelope . ts )
362361 const scope = envelope . scope
363362 const spanId = scope ?. spanId ?? MAIN_SPAN
364363
365364 switch ( envelope . type ) {
366365 case MothershipStreamV1EventType . text : {
367366 const payload = envelope . payload
368- appendText ( model , spanId , payload . channel as TextChannel , payload . text , seqNum , tsMs )
367+ appendText ( model , spanId , payload . channel as TextChannel , payload . text , seq , tsMs )
369368 break
370369 }
371370 case MothershipStreamV1EventType . tool : {
@@ -406,7 +405,7 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
406405 resolveToolId ( model , rawToolCallId ) ,
407406 spanId ,
408407 toolName ,
409- seqNum ,
408+ seq ,
410409 tsMs
411410 )
412411 if ( isRecord ( payload . arguments ) ) node . args = payload . arguments
@@ -420,7 +419,7 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
420419 resolveToolId ( model , rawToolCallId ) ,
421420 spanId ,
422421 toolName ,
423- seqNum ,
422+ seq ,
424423 tsMs
425424 )
426425 const delta = asString ( payload . argumentsDelta )
@@ -445,7 +444,7 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
445444 scope ?. parentToolCallId ?? asString ( data ?. tool_call_id ) ?? asString ( data ?. toolCallId )
446445 const agentId = asString ( payload . agent ) ?? scope ?. agentId ?? ''
447446 const resolvedSpanId =
448- scope ?. spanId ?? ( triggerToolCallId ? `span:${ triggerToolCallId } ` : `span:${ seqNum } ` )
447+ scope ?. spanId ?? ( triggerToolCallId ? `span:${ triggerToolCallId } ` : `span:${ seq } ` )
449448 const parentSpanId = scope ?. parentSpanId ?? MAIN_SPAN
450449
451450 if ( payload . event === MothershipStreamV1SpanLifecycleEvent . start ) {
@@ -459,7 +458,7 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
459458 parentSpanId,
460459 agentId,
461460 status : 'running' ,
462- seq : seqNum ,
461+ seq : seq ,
463462 ...( tsMs !== undefined ? { startedAtMs : tsMs } : { } ) ,
464463 ...( triggerToolCallId ? { triggerToolCallId } : { } ) ,
465464 }
@@ -473,7 +472,7 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
473472 const node = model . nodes . get ( resolvedSpanId )
474473 if ( node && node . kind === 'agent' && ! isNodeTerminal ( node . status ) ) {
475474 node . status = data && asString ( data . error ) ? 'error' : 'success'
476- node . endSeq = seqNum
475+ node . endSeq = seq
477476 }
478477 }
479478 break
@@ -484,10 +483,10 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
484483 if ( kind === MothershipStreamV1RunKind . compaction_start ) {
485484 const node = upsertToolNode (
486485 model ,
487- `compaction:${ seqNum } ` ,
486+ `compaction:${ seq } ` ,
488487 spanId ,
489488 'context_compaction' ,
490- seqNum ,
489+ seq ,
491490 tsMs
492491 )
493492 node . uiTitle = 'Compacting context...'
@@ -509,10 +508,10 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
509508 if ( ! finalized ) {
510509 const node = upsertToolNode (
511510 model ,
512- `compaction:${ seqNum } ` ,
511+ `compaction:${ seq } ` ,
513512 spanId ,
514513 'context_compaction' ,
515- seqNum ,
514+ seq ,
516515 tsMs
517516 )
518517 node . status = 'success'
@@ -535,7 +534,7 @@ export function reduceEvent(model: TurnModel, envelope: PersistedStreamEventEnve
535534 open . text += prefix + tag
536535 }
537536 } else {
538- appendText ( model , spanId , 'assistant' , tag , seqNum , tsMs )
537+ appendText ( model , spanId , 'assistant' , tag , seq , tsMs )
539538 }
540539 break
541540 }
0 commit comments