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

Cancel added tools when native work stops - #2972

Merged
dr0uleau merged 4 commits into
mainfrom
nexus/cancel-added-tool-calls-with-their-native-turn-f41a47a3
Nov 10, 2026
Merged

dr0uleau merged 4 commits into
mainfrom
nexus/cancel-added-tool-calls-with-their-native-turn-f41a47a3

Conversation

@dr0uleau

@dr0uleau dr0uleau commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Why

A plugin tool can wait for task data before it writes. Stop must cancel that exact call, even when a new native turn starts before the wait ends.

For example:

  1. An agent calls a plugin tool to update a task.
  2. The tool waits for task data.
  3. The owner presses Stop, then starts another request.
  4. The old tool resumes. Its captured signal is cancelled, so it must refuse the write.

Without a signal tied to the original call, the old tool can still write after Stop. A new active turn does not make that old call valid.

What

Expose the host signal as BuzzClient.signal for native Claude and Codex tools. Codex cancels tool calls when Stop starts, while its process cleanup continues.

How

The callback keeps the signal captured by its native request. Plugins pass it to waits and check it before external writes. Conversation activity does not set ownership. Existing client mutators and access checks remain in place. Hosts without request cancellation omit the property. Signals stay out of saved arguments and session archives.

Risk

This affects native tool cancellation. Stop cannot undo writes already sent. Plugin authors must use the supplied signal for external writes.

Testing

The required push checks pass on Blox for head 387ec05985088f4070cb9e5c2bacf357887d338f and base d52b32363ba02f554896388889b2bb9b5ca6a0f9 (command 28427047). Type checks, 2,969 related tests, design checks, and scoped lint pass. Four files and 14 tests skip. No Rust inputs change. The rebase keeps both independent Codex tests at the conflict.

The four new cancellation cases fail against the original base because no request signal reaches the callback. They pass with this change. They cover Stop during a wait, a new turn, normal completion, and refusal of late publication. No browser cases change. The file-reader fixture waits for its spawn gate before it checks replies. Its assertions and timeout stay unchanged.

Agent review completes with no findings. Native macOS acceptance passes at a905da432c6529cf6d2e357060f2f435405507dc on an isolated macOS arm64 VM (command 28417514). The real app Stop controls abort held plugin calls for both Claude and Codex. Each fresh turn writes while the old call stays held. Releasing the old call then refuses its write. The old files remain absent after the CLI response barrier. The same signed executable restores identity and plugin activation after restart. App controls remove test agents, the channel, and the plugin. Owned processes and listeners stop.

The native test uses generated keys, a local simulated signed relay, and scripted Claude/Codex CLI processes. It does not test provider inference or production relay policy. The rebase preserves the cancellation code and dispatch ownership. Its incoming protection-context and transcript changes do not affect the unprotected turns exercised here. Current-head unit checks cover the merged code. Human testing is explicitly waived by the operator.

buzz-review-completed (human testing waived by the operator)

Generated with Codex

@dr0uleau
dr0uleau marked this pull request as ready for review November 9, 2026 17:45
@dr0uleau
dr0uleau requested a review from a team as a code owner November 9, 2026 17:46
@dr0uleau
dr0uleau requested review from gilgtm and kazemek21 November 9, 2026 17:46
kazemek21 added a commit that referenced this pull request Nov 9, 2026
## TL;DR

Removes the six owner-session read tools that let a Buzz-hosted agent
read with its owner's own access: channel, member and DM lists, channel
details, messages and canvases. An agent's own reads and the channel
archive, unarchive and delete changes are unchanged. The tools are gone
from discovery on every harness, and a cached or direct call to one is
refused before anything is read with the owner's session.

Fixes NEXUS-4348 · Initiative:
https://blockcell.sqprod.co/sites/kylez-buzz-mcp-initiative/

## Scope

Kyle's request was to remove "the channel/member/dm list/get" endpoints
and leave the archive/delete channel changes for now. Removing
`owner_read` and the two Canvas tools as well is the initiative lead's
interpretation: they read through the same owner session and handle, and
leaving them would keep the owner-read capability this removes. If that
is wrong, the #2881 half can be restored on its own.

### Removed

From #2745 (owner channels and members):
- `owner_channel_list`, `owner_channel_get`, `owner_member_list`, their
hint text and discovery on Claude Code, Codex, Pi, the session RPC and
the harness kit.
- `src/features/agents/owner-access.ts`, the run handle's `ownerAccess`
field and the runner's host hook that built it for bundled-revision
runs.
- The `BUZZ_OWNER_MCP_COMMUNITIES` build setting (`vite.config.ts`). A
distributor build that still sets it now gets nothing from it;
`docs/configuration.md` lists it with the other retired inputs.
- The owner-tool sections in `docs/agents.md`, `docs/configuration.md`,
`docs/buzz-help.md` and `src/buzz-mcp/AGENTS.md`.

From #2881 (owner messages and canvases):
- `owner_read`, `owner_canvas_get`, `owner_canvas_history`,
`src/buzz-mcp/owner.ts` and the `Owner*` client types.
- The `docs/relay-queries.md` paragraph, and the exports of `tools.ts`
helpers that only `owner.ts` used. `edited()` keeps its author-only edit
selection, minus the owner parameter.

### Kept

- From #2745: the Stop and cancellation fences in the Claude Code, Goose
and ACP sessions (`workLifetime`, `current()`), which every Buzz tool
call now depends on, and the channel-lifecycle changes
(`channel-lifecycle.ts`, including its `records` parameter).
- `addTool` still refuses plugin tool names starting with `owner_`, so a
stale call to a retired name can never reach plugin code. There is no
other owner-prefixed restriction and no new permission layer.
- Ordinary agent-scoped reads, Codex per-thread/turn tool validation,
plugin and external isolation.

## Alternate routes checked

Nothing else reached these tools: no Rust, IPC, sidecar or RPC path, and
Mesh and external agents use the same run handle that no longer carries
owner access. Codex thread tool keys no longer carry the `:owner`
suffix, so threads created with owner tools are replaced instead of
reused with a stale tool list.

## Tests

The new tests list the six names as a fixed fixture rather than reading
them from the catalog:
- `session-tools.test.ts`: `sessionTools()` and `sessionHint()`, which
every harness lists from, name none of the six.
- `service.test.ts`: a real bundled run handle has no `ownerAccess`.
With the owner's session stubbed to return an `OWNER_ONLY` message for
an owner-only channel, each retired name is refused with `No tool
<name>` before any relay query. An ordinary `read` of that channel then
runs with the agent's own key, filtered to that channel, and returns `No
messages.`. The same client's `read` of the agent's own channel returns
its message, and no read through the owner's session ever names the
owner-only channel. External agents see none of the names and are
refused the same way.
- `codex/runtime.test.ts`: `dynamicTools` lists none of the names, each
call fails Codex's validation, a planted owner-access spy is never
called, and the thread's own `read` works.
- `app-client.test.ts`: a handle that still carries an owner capability
doesn't pass it to the tool client.
- The Claude Code and Goose Stop/timer tests now hold an ordinary
`mem_get` read instead of an owner tool.

On a Blox box with these commits applied to main: `tsc` passes, and the
12 affected Vitest files pass (claude, codex, goose, pi runtimes,
app-client, tools, session-tools, service, kit, ACP client,
channel-lifecycle). Full CI covers the rest. `git push --no-verify`
skipped the pre-push lanes (types-and-related-unit-tests, design-system,
rust-clippy); CI's `pnpm check`, sharded Vitest and clippy jobs repeat
all three.

Fail-before check: the removal tests in `service.test.ts` and
`codex/runtime.test.ts` were run against the pre-removal source
(0a5c190). Three of them fail on real assertions:
- Codex `dynamicTools` contains `owner_channel_list`.
- An external agent's call returns "Owner tools are not available to
this agent here; nothing was read." instead of `No tool
owner_channel_list`.
- The bundled handle has `ownerAccess`.

All three pass on this branch. Vitest strips types, so this check
doesn't cover `tsc`. The `session-tools.test.ts` discovery test also
passes on the old source, because that source only lists the tools when
the owner setting is on, so it doesn't count as a fail-before check.

## Not covered

- No native app run or live relay check. The removal is exercised
through the service, runner and harness tests above.
- A Buzz process already running a build with these tools keeps them
until it updates. This doesn't revoke anything remotely.

## Related open PRs

- Human work in overlapping files keeps its behavior but may need a
rebase: #2700, #2974, #3081, #2972 (cancel added tools on native Stop),
#2935, #1634, #2622, #3095, #2882.
- #3004, #3005, #3006, #3008 and #2980 build on the removed owner access
and are not part of this change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
(claude-opus-5-5) on behalf of kylez

---------

Signed-off-by: Kyle Zemek <kylez@squareup.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@dr0uleau
dr0uleau force-pushed the nexus/cancel-added-tool-calls-with-their-native-turn-f41a47a3 branch from dd5a899 to 65aec92 Compare November 9, 2026 21:57

@gilgtm gilgtm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 No P0/P1 findings after deep dual review of 65aec926a00e16354c4560556db930d9d7b6450d.

Posted by duel-review skill [driver: codex(high), mode: deep-dual, review synthesized by codex(high)]

@dr0uleau
dr0uleau force-pushed the nexus/cancel-added-tool-calls-with-their-native-turn-f41a47a3 branch from 65aec92 to a905da4 Compare November 10, 2026 00:21
dr0uleau and others added 4 commits November 10, 2026 10:40
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Daniel Rouleau <drouleau@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Daniel Rouleau <drouleau@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Daniel Rouleau <drouleau@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Daniel Rouleau <drouleau@squareup.com>
@dr0uleau
dr0uleau force-pushed the nexus/cancel-added-tool-calls-with-their-native-turn-f41a47a3 branch from a905da4 to 387ec05 Compare November 10, 2026 18:46
@dr0uleau
dr0uleau merged commit df93ecd into main Nov 10, 2026
64 checks passed
@dr0uleau
dr0uleau deleted the nexus/cancel-added-tool-calls-with-their-native-turn-f41a47a3 branch November 10, 2026 19:01
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