镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

fix(goose): end the turn when a final send succeeds - #2997

Merged
baxen merged 3 commits into
mainfrom
mbot/goose-final-cancel
Nov 7, 2026
Merged

baxen merged 3 commits into
mainfrom
mbot/goose-final-cancel

Conversation

@baxen

@baxen baxen commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

What

With goose, a send (or reply_in_subthread) marked final: true now ends the goose turn, as it already does for Claude Code (continue: false) and Pi.

Until now AcpSession.finished() only settled the Buzz work. goose went on with one more model call, and Buzz never posted its text. In that call, agents that had put final on a message promising more work ("I'll start the PR…") noticed the mistake, decided the protocol made them stop, and dropped the work. Buzz also counted the work as done while goose was still running.

How

goose v1.57.0 has no way for a tool result to end a turn. PostToolUse hooks only notify, and Stop hooks can only block. ACP session/cancel does it cleanly:

  • goose finishes the step it is in and saves every tool result that completed. Unfinished calls in the batch are saved as interrupted (goose-agent/src/machine.rs finalize).
  • The prompt answers stopReason: "cancelled". AcpSession already handles that for a turn the owner did not stop.

Two races are handled in acp/session.ts:

  1. When to cancel. A cancel sent from finished() reaches goose before goose records the send result, so goose would save the send as interrupted and the model might resend it next turn. goose reports a batch's results together, once every call has one (ops_toolcalling.rs). So after finished(), the session cancels on the first tool_call_update that is completed or failed.
  2. Messages in between. A message that arrives between finished() and that update is queued as the next turn instead of steered into the turn being cancelled.

finished() keeps its existing guard: when another message is waiting for its answer, the turn is not ended.

Non-goals: no change to the final prompt or tool description, and nothing in goose.

Validation

  • src/bundled/goose/goose.test.ts: three new tests. The turn is cancelled only after the completed result arrives, and the next turn's results don't cancel it. A message arriving mid-ending runs as the next turn. No cancel while another message waits. The first two fail on origin/main and pass here.

  • vitest run src/bundled/goose src/features/agent-harness src/features/agents src/bundled/mesh-compute: 1984 passed. tsc --noEmit is clean.

  • Live goose-acp 1.57.0 (GOOSE_ACP_BIN=… vitest run src/bundled/goose/goose.live.test.ts). The new test "ends the turn after a final send, keeping the send's result" asserts:

    • exactly one session/cancel after the final send;
    • on the next turn, the model reports the send's actual result (Sent.), so goose did not save the send as interrupted.

    It fails on origin/main (no cancel) and passes here.

    • Three other live tests fail the same way on origin/main and on this branch: they expect { ok: true } where finished() returns { ok: true, answered: true }. That predates this change, and I left it alone.

Deferred

  • No independent agent review yet.
  • Not exercised in the running app. Steps to try: in a goose agent's chat, ask for a reply. The agent's activity indicator should clear right after its final message, with no trailing "Writing reply". Another message sent in that moment should get its own turn.

baxen and others added 2 commits November 7, 2026 11:09
Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
@tlongwell-block
tlongwell-block marked this pull request as ready for review November 7, 2026 20:57
@tlongwell-block
tlongwell-block requested a review from a team as a code owner November 7, 2026 20:57
…til the turn ends

The cancel now waits for the tool_call_update of the final send itself,
matched by the ACP tool-call id goose passes in the MCP call's
`_meta["agent-tool-call-request-id"]`, instead of the first completed or
failed update. An earlier call's late update can no longer cancel before
goose has the send's result. Without an id, nothing is cancelled and the
turn ends on its own, as before.

`ending` stays set until the cancelled prompt returns, so a message sent
while the cancel is in flight runs as the next turn instead of being
steered into the turn being cancelled.

Tests: a message sent during the cancel is queued; an unrelated late
completion does not cancel; a final send without an id ends on its own.
All three fail at f037cc7.

Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
@baxen
baxen merged commit 6858698 into main Nov 7, 2026
60 checks passed
@baxen
baxen deleted the mbot/goose-final-cancel branch November 7, 2026 23:41
tlongwell-block added a commit that referenced this pull request Nov 8, 2026
* origin/main: (47 commits)
  fix(relay): report a community's membership refusal once, not as raw 403s (#3012)
  test(browser): fix red main and two cross-PR flakes (#3015)
  feat(agent-activity): keep remote agent outcomes readable (#2913)
  test(solo): cover agent tagging behind a background read-state snapshot (#2941)
  fix(desktop): quit on SIGTERM, SIGINT and SIGHUP so agents stop with Buzz (#2905)
  feat(unread): opening a channel quiets its sidebar row (#2998)
  fix(goose): end the turn when a final send succeeds (#2997)
  fix(relay): report a community's refusal once, not as raw 403s (#2978)
  Give fast Builderlab models their own section and a bolt in the model picker (#2592)
  feat(dev): `just desktop me` keeps Keychain approvals across rebuilds (#3002)
  fix(relay): show one quiet reconnect toast after 30 s, above the composer (#2968)
  feat(memory): render rich markdown and navigable buzz links (#2981)
  fix(identity): never strand first run on a key another account holds (#2996)
  Let agent_create propose instructions (#2332)
  fix(unread): raise a local read floor over dropped marks, and halve save validation (#2977)
  Add Codex huddle auto-join and thinking presence (#2927)
  feat(claude): set up Claude from a button beside Harness (#2975)
  docs(plugins): document the decisions API in the authoring guide (#2994)
  fix(agent-harness): stop telling agents to use GitHub-flavored Markdown (#2639)
  feat(identity): brand federated sign-in and return to the app (#2982)
  ...

Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>

# Conflicts:
#	src/bundled/codex/voice.ts
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.

2 participants