Repository navigation
Steer Copilot, OpenCode and Cursor turns by stopping them and sending the message next - #1015
Merged
Merged
Conversation
sak0a
force-pushed
the
port/steer-by-restart
branch
28 times, most recently
from
October 31, 2026 09:48
ec1087f to
e52e250
Compare
sak0a
force-pushed
the
port/steer-by-restart
branch
26 times, most recently
from
October 31, 2026 14:57
9de8d7d to
df40d83
Compare
… the message next Steering a running turn worked only for Claude and Codex; on GitHub Copilot, OpenCode and Cursor the composer said "steering unavailable" and the message waited for the turn to end. Those providers can't add a message to a running turn, but they can stop it. They now report turnSteeringMode "interrupt-restart" (adapter capability turnSteering "interrupt-restart"), and a steer stops the turn and defers: the message stays in the node's follow-up queue and is sent as the next turn. A node's own stop holds no queue, only a person's Stop does. If the turn cannot be stopped, the message stays queued and the activity says so. The composer and a queued message's Steer button say "Stop this turn and send this next". The status field is a plain string, so a mode an older client does not know reads as no steering instead of failing the provider decode. ACP registry agents keep queueing, as in T3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e phone too Review findings on restart steering: - A deferred steer stayed in its queue position, so with other follow-ups queued the stopped turn was followed by an earlier message, not the steered one. The deferral now carries restartedTurn (an optional flag, like deliveryUncertain), and the node's queue moves that message to its head. - The phone's queued-message Steer button and web's aria-label kept "into the active turn" wording; both now say the turn is stopped and the message sent next. - Docs: a message sent while approvals wait still redirects on these providers, which declines the approvals and then stops the turn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sak0a
force-pushed
the
port/steer-by-restart
branch
from
October 31, 2026 15:03
df40d83 to
70f2e24
Compare
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.
What Changed
turnSteering: "interrupt-restart". For them,ProviderService.steerTurninterrupts the expected turn and then defers the steer (ProviderTurnNotSteerableError, reasonrestarting). The message goes back into the node's follow-up queue and is sent as the next turn once the stopped turn settles.restartedTurn(an optional flag), and the node's queue moves that message to its head, so the stopped turn is followed by the steered message, not an earlier follow-up.turnSteeringMode: "interrupt-restart". It is a plain string, so a mode an older client does not know reads as no steering instead of failing the provider decode. A sharedproviderSteering()in client-runtime reads it for web and mobile.docs/providers/approval-response-policy.md→ "Steering providers without native steering".Why
Port batch 9c, item "capability-driven adapter degradation". The report's recommendation was to give every provider steer semantics through an interrupt-and-restart steering mode, rather than rewrite capabilities into T3's 11 groups. This follows T3's policy (
docs/orchestration-v2/provider-capability-system.md: "active steering unsupported → interrupt active turn and restart the run";CommandPolicy.tsdecideSteeringExecution; adapters withsupportsSteeringByInterruptRestart: truefor Cursor, OpenCode and ACP,falsefor the ACP registry), adapted to Ryco's server-owned follow-up queue.T3's
ForwardCompatibleArray/Optionaldecoding was not ported wholesale. The new status field uses a plain string, the same forward-compatible pattern Ryco already uses for lineage relationships.Validation
bun run checkandbun run check:changedpass. Server boot +931 B.ProviderService.test.ts: a Cursor-shaped adapter ininterrupt-restartmode has the expected turn interrupted, gets a deferred steer with the restart wording and never hassteerTurncalled. A failed interrupt defers with "could not be stopped. The message stays queued."logic.test.ts(client-runtime):providerSteeringreads native, restart, an unknown future mode (no steering) and absent status.composerFollowUp.test.ts: the restart wording for both follow-up settings.decider.followUpQueue.test.ts: a steer deferred withrestartedTurnmoves to the head of a queue with earlier follow-ups;turnSteerFailure.test.ts: only therestartingreason sets the flag.🤖 Generated with Claude Code