Skip to content

fix(agent-core-v2): adopt the replayed turn outcome on restore - #215

Merged
elkaix merged 1 commit into
mainfrom
fix/restore-reconciles-turn-outcome
Aug 27, 2026
Merged

fix(agent-core-v2): adopt the replayed turn outcome on restore#215
elkaix merged 1 commit into
mainfrom
fix/restore-reconciles-turn-outcome

Conversation

@elkaix

@elkaix elkaix commented Aug 27, 2026

Copy link
Copy Markdown
Member

Related Issue

Follow-up to #213, from the review discussion on reconcileAfterRestore.

Problem

SessionOutcomeMirror persists 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 replayed lastEnded reason is unambiguous, adopt it with touchUpdatedAt: false and track its turn id for the undo range check: completedcompleted, failed / blockedfailed. A replayed cancelled is deliberately left alone: the wire carries no interruptReason, so it cannot distinguish a user stop from a programmatic abort, and only user stops are ever persisted.
  • Tests: stale cancelled replaced by replayed completed; replayed blocked maps to failed; replayed cancelled keeps the persisted completed; the adopted turn id feeds the undo range check.

Verification

  • pnpm --filter @pymodel/agent-core-v2 exec vitest run — 347 files, 5,723 tests passed
  • typecheck, tsgo, lint:imports, check-no-comments, oxlint on the changed files — all exit 0

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: the issue must have a maintainer's /approve).
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

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.
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 1 minute.

View limit details

Limit 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.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a4aa9113-8fd9-4e3a-8787-e3542a02f6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 0753f13 and 133808a.

📒 Files selected for processing (3)
  • .changeset/restore-reconciles-turn-outcome.md
  • packages/agent-core-v2/src/session/sessionActivity/sessionOutcomeMirrorService.ts
  • packages/agent-core-v2/test/session/sessionActivity/sessionOutcomeMirror.test.ts

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 27, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pymodel/pythinker-code@133808a
npx https://pkg.pr.new/@pymodel/pythinker-code@133808a

commit: 133808a

@elkaix
elkaix merged commit e4ed37f into main Aug 27, 2026
25 checks passed
@elkaix
elkaix deleted the fix/restore-reconciles-turn-outcome branch August 27, 2026 01:29
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant