fix(core): reject approval resumes of a running turn before freezing it - #511
Conversation
An approval or tool-response input can only answer the required actions of a completed turn. Check the previous turn state read-only before the cancelled-for-next-turn freeze, so an invalid resume such as a duplicate approval no longer cancels the turn it races. Fixes truefoundry#508
🦋 Changeset detectedLatest commit: 2a56d1a The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 87ed621. Configure here.
There was a problem hiding this comment.
Pull request overview
Fixes a turn-lifecycle bug in trueforge-core where SessionHandle.createTurn() could cancel an in-flight turn before validating a new turn request, causing a duplicate approval/tool-response submission to truncate the running turn with no successor created.
Changes:
- Add a pre-freeze, read-only guard in
SessionHandle.createTurn()that rejects approval/tool-response-only inputs when the previous turn is stillrunning. - Add a regression test asserting approval-only resumes don’t cancel a running turn, while user-message barge-in still cancels with
cancelled-for-next-turn.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/trueforge-core/src/agent-session/SessionHandle.ts | Adds a pre-freeze validation to prevent approval/tool-response resumes from cancelling a still-running previous turn. |
| packages/trueforge-core/tests/agent-session/sessions.test.ts | Adds coverage for the “approval-only resume vs user-message barge-in” behavior around running turns. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Verified end to end in the UI as well: ran the server from this branch, went through a full approval flow, and double clicked Allow at the pause. The duplicate is rejected and the turn streams to completion with all post-approval output intact. The same double click on the published 0.1.4 build cancels the turn mid-stream as described in #508. |

Fixes #508.
SessionHandle.createTurnfreezes the previous turn withcancelled-for-next-turnbefore the new request is validated, and there is no rollback. A request that later fails validation therefore kills the in-flight turn and leaves no successor. The issue has a live repro: a duplicate Allow click sends a seconduser.tool_approvalturn, the server rejects it with 422, and the turn that is executing the approved tool is cancelled anyway, after the tool's side effect already ran.The fix leans on an existing invariant: approval and tool-response items answer the required actions of a completed turn, and a running turn cannot have any. So when the input consists only of approval or tool-response items,
createTurnnow peeks at the previous turn read-only and throwsInvalidAgentSendInputErrorif it is still running, before the freeze. The running turn is untouched. Inputs containing a user message keep today's barge-in behavior, and valid resumes are unaffected because their previous turn is always terminal.Added a test covering both sides: an approval-only resume of a running turn is rejected and the turn stays running, while a user message still cancels it with
cancelled-for-next-turn. Fulltrueforge-corejest suite (389 passing), tsc, and eslint are clean.On process: I know CONTRIBUTING asks community PRs to wait for maintainer approval. Opening this anyway since it is small and directly tied to the repro in #508; happy to close it or rework the approach if you prefer to handle it differently.
Note
Medium Risk
Changes turn-lifecycle ordering in a core session path; behavior is narrowly scoped to approval/tool-response resumes but affects concurrency and cancellation semantics.
Overview
Fixes a race where
SessionHandle.createTurnfroze the previous turn withcancelled-for-next-turnbefore validating the new input, so a rejected duplicate approval (e.g. second Allow click → 422) could cancel an in-flight turn after the tool had already run (#508).createTurnnow, for input that is only approval or client tool-response messages, does a read-only check on the previous turn and throwsInvalidAgentSendInputErrorif it is stillrunning, before any freeze. Invalid resumes no longer touch the executing turn; user-message barge-in and valid resumes (previous turn already terminal) behave as before.Adds a session test that an approval-only resume against a running turn is rejected and stays running, while a user message still cancels with
cancelled-for-next-turn. Patch changeset for@truefoundry/trueforge/trueforge-core.Reviewed by Cursor Bugbot for commit 2a56d1a. Bugbot is set up for automated code reviews on this repo. Configure here.