fix(agent-core-v2): adopt the replayed turn outcome on restore - #215
Merged
Conversation
A session whose persisted outcome went stale (a write that never landed before shutdown) kept that outcome after restore whenever the replayed wire still had an ended turn, because the restore reconcile only cleared the outcome when no ended turn existed. Adopt the replayed outcome when it is unambiguous: completed stays completed, failed and blocked map to failed. A replayed cancellation is left alone, since the wire cannot tell a user stop from a programmatic abort and only user stops are persisted.
|
Warning Review limit reachedNext included review available in 1 minute. View limit detailsLimit details: You’ve used all 10 included reviews currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Comment |
commit: |
Merged
elkaix
pushed a commit
that referenced
this pull request
Aug 27, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @pymodel/pythinker-code@1.4.0 ### Minor Changes - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Remove the `--allow-remote-terminals` flag from `pythinker web`; PTY terminal routes now stay available on loopback binds only. - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Add `PYTHINKER_CODE_INFINITE_RETRY=1` to retry every failed model request indefinitely with backoff instead of failing the turn, for long unattended runs. - [#208](#208) [`bd0eadd`](bd0eadd) Thanks [@elkaix](https://github.com/elkaix)! - Add an option to reveal saved plan files in your file manager. Select Reveal in file manager from a saved plan. ### Patch Changes - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Silence the MaxListenersExceededWarning that could appear during long agent turns with many parallel tool calls. - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Fix messages sent from one web client not appearing on other clients connected to the same session. - [#207](#207) [`3a1d761`](3a1d761) Thanks [@elkaix](https://github.com/elkaix)! - Fix context usage updates in interactive clients. - [#214](#214) [`48510be`](48510be) Thanks [@elkaix](https://github.com/elkaix)! - Complete a Codex sign-in as soon as the browser tab becomes visible again, not only when the window regains focus. - [#201](#201) [`5f087d8`](5f087d8) Thanks [@elkaix](https://github.com/elkaix)! - Make the chat prompt anchor a compact line index that opens prompt and response previews. - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Persist a picked thinking effort as the default only up to the model's own default effort; a more expensive pick stays session-only. - [#204](#204) [`2d76cad`](2d76cad) Thanks [@elkaix](https://github.com/elkaix)! - Fix prompt anchor selection when a conversation has two prompts. - [#198](#198) [`6be446e`](6be446e) Thanks [@elkaix](https://github.com/elkaix)! - Wrap narrow Changes editor lines while keeping line numbers visible. - [#207](#207) [`3a1d761`](3a1d761) Thanks [@elkaix](https://github.com/elkaix)! - Fix session history after steering or undoing a turn. - [#211](#211) [`7040eed`](7040eed) Thanks [@elkaix](https://github.com/elkaix)! - Add a setting to pin every subagent to the selected model, use the dark banner in every sidebar, and show Pythinker desktop updates as one continuous download. - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Show the /plugins marketplace catalog as soon as it loads, with latest-version lookups running in the background. - [#192](#192) [`8b6cc70`](8b6cc70) Thanks [@elkaix](https://github.com/elkaix)! - Preserve subagent model aliases when provider models refresh. - [#215](#215) [`e4ed37f`](e4ed37f) Thanks [@elkaix](https://github.com/elkaix)! - Fix a resumed session showing a stale "manually stopped" or failed state when its last turn had actually completed. - [#198](#198) [`6be446e`](6be446e) Thanks [@elkaix](https://github.com/elkaix)! - Show available desktop updates as a sidebar button with release notes and update controls. - [#192](#192) [`8b6cc70`](8b6cc70) Thanks [@elkaix](https://github.com/elkaix)! - Stop goal turns when automatic context compaction is cancelled or fails. - [#192](#192) [`8b6cc70`](8b6cc70) Thanks [@elkaix](https://github.com/elkaix)! - Let subagents inherit the calling agent model from the Agent settings tab. - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Fix foreground subagents being reported as background tasks on the task list. - [#201](#201) [`5f087d8`](5f087d8) Thanks [@elkaix](https://github.com/elkaix)! - Use theme-matched Pythinker banners in the sidebar. - [#207](#207) [`3a1d761`](3a1d761) Thanks [@elkaix](https://github.com/elkaix)! - Fix loss of large tool outputs in long conversations. - [#213](#213) [`0753f13`](0753f13) Thanks [@elkaix](https://github.com/elkaix)! - Fix the "manually stopped" state lingering after undoing the interrupted turn. - [#198](#198) [`6be446e`](6be446e) Thanks [@elkaix](https://github.com/elkaix)! - Replace the sidebar robot icon with the Pythinker Code banner. ## pythinker@0.9.6 ### Patch Changes - [#188](#188) [`0097afc`](0097afc) Thanks [@elkaix](https://github.com/elkaix)! - Persist a picked thinking effort as the default only up to the model's own default effort; a more expensive pick stays session-only. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.
Related Issue
Follow-up to #213, from the review discussion on
reconcileAfterRestore.Problem
SessionOutcomeMirrorpersists the last turn outcome (completed/failed/cancelled) into the session metadata. On restore it only reconciled one stale case: a persisted outcome with no ended turn on the replayed wire. If the persisted value itself went stale — the async metadata write never landed before shutdown — a session whose last turn had actually completed still resumed showing "manually stopped" (or a failed badge), because the replayed wire had an ended turn and the reconcile kept whatever was on disk.What changed
sessionOutcomeMirrorService.ts: after restore, when the replayedlastEndedreason is unambiguous, adopt it withtouchUpdatedAt: falseand track its turn id for the undo range check:completed→completed,failed/blocked→failed. A replayedcancelledis deliberately left alone: the wire carries nointerruptReason, so it cannot distinguish a user stop from a programmatic abort, and only user stops are ever persisted.cancelledreplaced by replayedcompleted; replayedblockedmaps tofailed; replayedcancelledkeeps the persistedcompleted; the adopted turn id feeds the undo range check.Verification
pnpm --filter @pymodel/agent-core-v2 exec vitest run— 347 files, 5,723 tests passedtypecheck,tsgo,lint:imports,check-no-comments, oxlint on the changed files — all exit 0Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.