diff --git a/packages/ui/src/sync/event-pipeline.ts b/packages/ui/src/sync/event-pipeline.ts index 331ef9e6..f63e69d8 100644 --- a/packages/ui/src/sync/event-pipeline.ts +++ b/packages/ui/src/sync/event-pipeline.ts @@ -314,9 +314,13 @@ export function createEventPipeline(input: EventPipelineInput) { } else { d.queue[i] = normalizedPayload if (normalizedPayload.type === "message.part.updated") { - const part = (normalizedPayload.properties as { part: { messageID: string; id: string } }).part - d.staleDeltas.add(deltaKey(part.messageID, part.id, "text")) - d.staleDeltas.add(deltaKey(part.messageID, part.id, "output")) + const part = (normalizedPayload.properties as { part: Record & { messageID: string; id: string } }).part + for (const field of ["text", "output"] as const) { + const value = part[field] + if (typeof value === "string" && value.length > 0) { + d.staleDeltas.add(deltaKey(part.messageID, part.id, field)) + } + } } } syncDebug.pipeline.coalesced(normalizedPayload.type, k) diff --git a/packages/ui/src/sync/sync-context.tsx b/packages/ui/src/sync/sync-context.tsx index 3a76dc2b..22d34160 100644 --- a/packages/ui/src/sync/sync-context.tsx +++ b/packages/ui/src/sync/sync-context.tsx @@ -1227,12 +1227,12 @@ function handleEvent( const messageID = getMessageIdFromPayload(payload) ?? undefined syncDebug.dispatch.eventNoChange(payload.type, sessionID, messageID) - // Parts-gap recovery: if a part event was dropped because the parts array - // was missing (message not yet inserted or parts lost), trigger a repair - // fetch for the session. - if (sessionID && messageID && ( - payload.type === "message.part.delta" || payload.type === "message.part.updated" - )) { + // Parts-gap recovery: if a delta event was dropped because the parts array + // was missing or the partID was not found, trigger a repair fetch for the + // session. message.part.updated never needs repair — it only returns false + // for intentionally skipped types (step-start, step-finish, patch) or when + // preserving an existing finished tool part, neither of which indicates missing data. + if (sessionID && messageID && payload.type === "message.part.delta") { enqueuePartsRepair(resolvedDirectory, sessionID, childStores) } }