diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index 20aa8a8404d8..7d40216df3f3 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -536,6 +536,26 @@ const layer = Layer.effect( } }) + // Finalize partially streamed parts so a failed attempt does not leave + // unclosed reasoning/text blocks behind before the stream is retried. + const sealPartialParts = Effect.fn("SessionProcessor.sealPartialParts")(function* () { + if (ctx.currentText) { + const end = Date.now() + ctx.currentText.time = { start: ctx.currentText.time?.start ?? end, end } + yield* session.updatePart(ctx.currentText) + ctx.currentText = undefined + } + + for (const part of Object.values(ctx.reasoningMap)) { + const end = Date.now() + yield* session.updatePart({ + ...part, + time: { start: part.time.start ?? end, end }, + }) + } + ctx.reasoningMap = {} + }) + const cleanup = Effect.fn("SessionProcessor.cleanup")(function* () { if (ctx.snapshot) { const patch = yield* snapshot.patch(ctx.snapshot) @@ -552,21 +572,7 @@ const layer = Layer.effect( ctx.snapshot = undefined } - if (ctx.currentText) { - const end = Date.now() - ctx.currentText.time = { start: ctx.currentText.time?.start ?? end, end } - yield* session.updatePart(ctx.currentText) - ctx.currentText = undefined - } - - for (const part of Object.values(ctx.reasoningMap)) { - const end = Date.now() - yield* session.updatePart({ - ...part, - time: { start: part.time.start ?? end, end }, - }) - } - ctx.reasoningMap = {} + yield* sealPartialParts() yield* Effect.forEach( Object.values(ctx.toolcalls), @@ -634,8 +640,9 @@ const layer = Layer.effect( return yield* Effect.gen(function* () { yield* Effect.gen(function* () { - ctx.currentText = undefined - ctx.reasoningMap = {} + // A retried attempt replays the request from scratch, so close out + // any reasoning/text parts the failed attempt left dangling. + yield* sealPartialParts() yield* status.set(ctx.sessionID, { type: "busy" }) const stream = llm.stream(streamInput)