Let a user stop an in-flight agent turn (CL-7201) - #513
Merged
TheGreatAxios merged 22 commits intoAug 31, 2026
Conversation
Contributor
Author
|
Ran a Critique pass against the full diff before marking this ready. It found three real bugs, all now fixed with regression tests:
Also worth noting for review: Critique confirmed All packages (chat, chat-ui, icons, hub) still green: typecheck + test + prettier + eslint. |
TheGreatAxios
force-pushed
the
cl-7201-let-a-user-stop-an-in-flight-agent-turn
branch
from
August 30, 2026 21:46
3e43b64 to
e6b8b06
Compare
TheGreatAxios
force-pushed
the
cl-7201-let-a-user-stop-an-in-flight-agent-turn
branch
2 times, most recently
from
August 31, 2026 04:03
7e6d495 to
c7a7873
Compare
TheGreatAxios
force-pushed
the
cl-7201-let-a-user-stop-an-in-flight-agent-turn
branch
2 times, most recently
from
August 31, 2026 07:05
303f5ca to
de4e52b
Compare
…lation cuts work short
Stop was keyed off the tokenless typing pulse, so it vanished the moment tokens streamed. Offer it for the whole awaiting phase, and settle a cancelled turn as replied so a late inference event cannot reopen that pulse.
A cancelled 1:1 turn still has exactly one membership and no running row, so the sole-membership fallback would post the late reply as an unattached message. Check the latest turn status before falling back.
TheGreatAxios
force-pushed
the
cl-7201-let-a-user-stop-an-in-flight-agent-turn
branch
from
August 31, 2026 07:12
de4e52b to
99cbe21
Compare
TheGreatAxios
marked this pull request as ready for review
August 31, 2026 07:22
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.
Summary
There was no cancel/abort/interrupt endpoint for a running agent turn anywhere in the chat routes — the only bound on a wedged turn was the multi-minute dispatch/wait-until-free timeouts. This adds one.
Stacked on
cl-7193-with-timeout-abort(#483, not yet merged) — it exports theAbortSignalseams (withTimeout's per-call signal,dispatchTurn's signal param,AgentTurnStore.finishTurn's compare-and-set) this cancellation is built on. Review against that base, notmain.withTimeout(packages/chat/src/with-timeout.ts) now accepts an optional externalAbortSignal, composed with its own timeout signal — a caller's own cancellation cutsworkshort exactly like a timeout does, without waiting for the timeout's clock.TurnCancelRegistry(packages/chat/src/turn-cancellation.ts) is the live abort seam a running turn is reachable through while still on our own call stack (dispatchTurnBatch'swaitUntilFreewait,dispatchTurn'ssendMailcall).AgentTurnStoregains a"cancelled"status andfindRunningTurns(plural, uncapped — the existinglistTurnspages and has no status filter, wrong primitive for a settle-everything sweep).cancelWorkbenchTurn(packages/chat/src/workbench-service.ts) runs two mechanisms together: aborting any live controller (reaches work still in flight) and sweeping every snapshotted running turn through the samefinishTurncompare-and-set (reaches a turn whose dispatch has already resolved and moved off our call stack — the agent is generating, or parked on a gate somewhere in the execution plane this package can't see into). Whichever wins a given row's CAS is the only one that settles it and posts the one honestturnCancellednotice for it.POST /workbenches/:id/turns/cancelroute, wired through the hub's chat/workflow/Slack send paths.running/onStopprops) shown whenever a turn is in flight, independent of the composer's own send state — a follow-up message can still be typed and queued while a turn runs.What cancellation actually stops vs. records (CL-7230)
@corbits/agent-lifecycle's wake and@corbits/folded-runs' mail send accept noAbortSignal. OncesendMailis in flight, cancellation cannot forcibly stop the underlying agent process — it can only interrupt bookkeeping/waiting phases still on our own call stack, and settles the turn row honestly (cancelled, never a false claim of a clean stop). A late reply that lands anyway simply finds norunningrow left to attach to (the same CAS guarantee CL-7193 already gives a timed-out turn against a late reply — tested here for the cancel outcome specifically).Question-gate scope
CL-7190/7191 (the actual
message_responseask_user gate, PRs #500/#501) are not merged to main and not stacked into this branch — the gate's own state lives entirely in unmerged vendored@intx/inference/packages/interaction-toolscode I have no access to from here. From this package's point of view, a gate-parked turn is just an ordinaryrunningAgentTurnrow, so the generic mechanism above settles it the same way. Verified (not just asserted) that a late answer arriving after cancellation — standing in for a delayed gate reply — cannot reopen an already-cancelled row, viafinishTurn's existing compare-and-set.Known, disclosed gaps
turn-queue.ts's ownpendingByWorkbenchqueue (queued behind another turn for the same agent, not yet dispatched) has nothing registered to abort yet — cancelling at that exact moment is not currently reachable by this mechanism. Real gap, not covered here.CancelledTurnStrip— the repo has no existing DOM-test precedent for either's sibling (the send button,FailedTurnStrip), so pure-function tests cover the logic instead.Test plan
cd packages/chat && bun run typecheck && bun test(794 tests, 0 fail)cd packages/chat-ui && bun run typecheck && bun test(821 tests, 0 fail)cd packages/icons && bun run typecheck && bun testcd apps/hub && bun run typecheck && bun test(207 tests, 0 fail)bunx prettier --checkon all touched filesbun run lint(root) — 0 errors, pre-existing unrelated warnings onlyDO NOT MERGE.