fix(session): discard a failed attempt's parts before retrying and stop retrying past an executed tool - #205
Merged
Merged
Conversation
…op retrying past an executed tool `Effect.retry(SessionRetry.policy)` wraps the whole stream consumption, but the parts a failed attempt had already persisted (text, reasoning, step-start, tool parts) were left in the assistant message, so the retried stream appended duplicates next to them. Worse, the retry replayed the same request, so a tool the model had already executed in the failed attempt (a gateway closing with `finish_reason: network_error` after a completed edit) was requested and run a second time. The processor now remembers the part ids it mints during an attempt and, as soon as the retry policy decides to retry, removes them through `Session.removePart` (projected to a DB delete) and settles the pending tool calls, before the wait starts. A failure after any tool call reached running skips the retry layer and goes straight to `halt`, so the turn ends with the error visible and no tool executes twice. Two regression tests cover this: a midstream server_error after a text chunk leaves exactly one text part and one step-start after the retry; a completed tool call followed by `network_error` makes no second provider request, keeps the tool at completed, and ends the message with an error. Claude-Session: https://claude.ai/code/session_01KAcoL6wpgVEs2ebUrUGpHS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Effect.retry(SessionRetry.policy)inpackages/redcode/src/session/processor.tswraps the whole stream consumption. The parts a failed attempt had already persisted (text, reasoning, step-start, tool parts) stayed in the assistant message, so the retried stream appended duplicates next to them. Worse, the retry replayed the unchangedstreamInput, so a tool the model had already executed in the failed attempt (a gateway ending withfinish_reason: network_errorafter a completed edit, seellm/ai-sdk.ts) was requested and executed a second time. The existing retry test only checked in-memory reasoning state, not what was persisted.Fix
nextPartID(), which records them inctx.attemptPartsfor the attempt in flight (reasoning, tool, step-start, step-finish, patch, text).sethook, before the wait starts),discardAttemptsettles the pending tool calls and removes the recorded parts throughSession.removePart, which the core projector turns into a DB delete. The message never shows the failed attempt's output next to the retry's.updateToolCallflagsctx.attemptExecutedonce a tool part leavespending. A failure after that skips the retry layer and goes straight tohalt, so the turn ends with the error visible and no tool executes twice. The check happens before the schedule so no misleadingretrystatus is published.Test
Two regression tests in
packages/redcode/test/session/processor-effect.test.ts, both failing onmainand passing here:discard the failed attempt's parts before retrying: a text chunk then a midstreamserver_error, followed by a clean reply. Before:["", "two"]text parts and two step-starts. After: exactly["two"], one step-start, two provider calls, no error.do not retry after a tool call already ran: a completed tool call thenfinish_reason: network_error. Before:continue, a second provider request. After:stop, one provider call, tool executed once and stillcompleted,message.errorset.mise x -- bun test --timeout 30000 test/session: 514 pass, 0 fail.mise x -- bun run typecheck: clean.https://claude.ai/code/session_01KAcoL6wpgVEs2ebUrUGpHS
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.