Bracket agent-initiated ACP turns so unprompted work renders - #2220
Open
SawyerHood wants to merge 1 commit into
Open
Bracket agent-initiated ACP turns so unprompted work renders#2220SawyerHood wants to merge 1 commit into
SawyerHood wants to merge 1 commit into
Conversation
When an ACP agent streams work with no session/prompt in flight (OMP's async-job delivery), the bridge forwarded the updates without opening a turn, so the runtime assembler demoted each one to a hidden thread-scoped provider/unhandled row and the user saw nothing. The bridge now folds agent-initiated work into its open-turn mirror (activePromptKind: "turn" | "compaction" | "agent" | null). A work-kind update arriving idle opens a turn; a 5 s quiet window ends it; the next turn/start settles it first; thread/stop interrupts it; an agent exit fails it through the settling error. Permission requests inside an agent turn are handled like prompted ones instead of auto-cancelled. Co-Authored-By: Claude <noreply@anthropic.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.
What was wrong
When an ACP agent streams work with no
session/promptin flight (the concrete case is OMP's async-job auto-delivery),handleAgentNotificationin the provider-acp bridge forwarded everysession/updateto the translator without opening a turn. ACP has no turn bracket for agent-initiated work, the translator is context-free, and the runtime's delta assembler refuses to let item/stream deltas open a turn, so each unprompted chunk was demoted to a thread-scopedprovider/unhandledrow. Those rows are hidden unlessshowUnhandledProviderEventsis on, so the thread simply never answered. Root cause and live repro: https://get-bb.github.io/reports/issues/2122.html.#2123 (thanks @bradhallett) correctly identified the layer and the shape (a bridge-emitted
turn.open, per provider-bridge-protocol turn lifecycle rule 3). It was reviewed REQUEST CHANGES because it tracked the vouched turn in a parallelspontaneousTurnOpenboolean, so the existing readers ofactivePromptKindkept their old semantics: an agent exit left the turn open forever (thread hangs in Working), permission requests inside the turn were auto-cancelled even in full mode, and the 120 s quiet window buffered the last message and reported "Worked for 2m".What changed
All in
plugins/provider-acp(bridge-local; no wire shape changes, so noHOST_DAEMON_PROTOCOL_VERSIONbump).src/bridge/bridge.ts: agent-initiated work is folded into the bridge's existing open-turn mirror,activePromptKind: "turn" | "compaction" | "agent" | null, instead of a second flag, so every reader had to decide what an agent turn means:handleAgentNotificationopens the turn (ACP_TURN_STARTED_METHOD) whenactivePromptKind === nulland the update is a work kind, and re-arms a 5 s quiet timer per work update. The timer does not close the turn while a permission is pending.turn/startsettles a still-open agent turn (end_turn) before the check for an active turn, so the next user prompt or compaction never sees "A turn is already active";thread/stop(interrupt) settles it ascancelled; agent exit emits the settling error throughemitSessionErrorwhile the turn is still marked open, then clears the mirror, so the assembler closes the turn asfailed; a release stop detaches without fabricating a terminal state (Release agent runtimes on thread stop #1584), same as a prompted turn.handlePermissionRequesttreats"agent"like"turn": full mode auto-allows, other modes forward to the user.turn/steerduring an agent turn still returnsNO_ACTIVE_TURN; the runtime already maps that to a freshturn/start, which settles the agent turn first.src/visibility.ts: the work-kind set is derived from the visibility metadata (AGENT_WORK_ACP_UPDATE_KINDS= normalized kinds minususage_update, exported asisAgentWorkAcpUpdateKind) rather than a second hand-maintained map.src/bridge/fake-acp-agent.mjs: newagent-initiatedprompt behavior (OMP wire shape: echoeduser_message_chunk, agent chunks, a tool call that completes) with:permission,:exit, and:noisevariants.Differences from #2123: one mirror instead of two; settled on agent exit and on release; permission requests inside the turn are handled; 5 s quiet window instead of 120 s; the quiet window never closes a turn with an unanswered permission; no test-only timeout hook (the tests run against the real window).
How you verified
src/bridge/bridge.test.tsunder "agent-initiated turns (provider-acp silently drops agent-initiated turns (unprompted session updates, e.g. OMP async-job delivery) #2122)". With the source change stashed, 6 of 7 fail (the unprompted text never becomes an agent message; no second turn); with it, all 7 pass:turn/started, 0provider/unhandled, tool item rendered, exactly oneinput.accepted) and closes itcompletedafter the quiet windowthread/stopissue-2122.repro.test.tsand both hostile probes inissue-2122.pr-edges.test.ts(agent exit, permission during vouched turn), which fail on Vouch agent-initiated ACP turns so async job delivery renders #2123, pass on this branch.pnpm exec turbo run test typecheck lint --filter=bb-plugin-provider-acp --force: 7/7 tasks green, 15 test files / 203 tests passed.scripts/bb-dev-app current), registering the fake agent viacustomAcpAgents:thread spawn --provider acp-fakeacp --prompt agent-initiatedproduced turn t2 with the agent message, afileReaditem, and the closing message,turn/completed completed4.98 s after the last chunk, noprovider/unhandledrows; the timeline API shows the turn and the final assistant message.--prompt agent-initiated:exitproducedprovider/error+turn/completed failedon turn t4 within 70 ms of the exit and the thread status iserror, not a hung Working state.Fixes #2122