Repository navigation
feat(agents): read owner messages and canvases over MCP - #2881
Merged
kazemek21 merged 1 commit intoNov 7, 2026
Merged
Conversation
kazemek21
force-pushed
the
kylez_NEXUS-4285_owner-read-completeness
branch
2 times, most recently
from
November 6, 2026 21:09
d74f9b6 to
a9d7c24
Compare
kazemek21
marked this pull request as ready for review
November 6, 2026 21:26
kazemek21
force-pushed
the
kylez_NEXUS-4285_owner-read-completeness
branch
5 times, most recently
from
November 6, 2026 23:16
52848df to
89b2fab
Compare
dr0uleau
reviewed
Nov 6, 2026
kazemek21
force-pushed
the
kylez_NEXUS-4285_owner-read-completeness
branch
2 times, most recently
from
November 7, 2026 00:18
cecda48 to
8286cfd
Compare
kazemek21
force-pushed
the
kylez_NEXUS-4285_owner-read-completeness
branch
3 times, most recently
from
November 7, 2026 02:09
63c43ea to
7074491
Compare
Add owner_read, owner_canvas_get and owner_canvas_history to the owner tools. Each is a typed, read-only OwnerAccess operation run on the owner's current binding and released only while it holds. Content comes from the relay's snapshot read, so no unread, membership or subscription state changes. External app connections no longer carry the bundled run's owner access. Reads, owner and ordinary, now take a message's newest eligible edit, so a newer edit by someone else no longer hides it. As in the app's fold, an owner read also counts a chat message's or forum post's edit by its author's verified owner in the same channel. Fixes NEXUS-4285 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kyle Zemek <kylez@squareup.com>
kazemek21
force-pushed
the
kylez_NEXUS-4285_owner-read-completeness
branch
from
November 7, 2026 02:44
7074491 to
158505b
Compare
dr0uleau
approved these changes
Nov 7, 2026
dr0uleau
left a comment
Contributor
There was a problem hiding this comment.
🤖 reviewed 158505b -- no P2 or worse findings remain. the owner-edit regression has test coverage, and CI passes for this commit. live relay and native checks remain unverified
tlongwell-block
added a commit
that referenced
this pull request
Nov 7, 2026
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> * origin/main: Build sidebar headings as one fluid design-system row (#2686) fix(messages): swap bubble fills for reactions you've made, drop the ring (#2949) fix(messages): align line-start mention capsules with each other (#2899) fix(relay): probe the live socket on wake, focus and network (#2958) fix(messages): size thread avatars like the timeline's (#2890) build(desktop): share the bundled buzz CLI build across worktrees (#2894) fix(messages): render review comment mentions as chips and open at the reader's place (#2965) feat(agents): read owner messages and canvases over MCP (#2881) Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
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>
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.
TL;DR
Completes the owner-context MCP reads that #2745 started. An agent's app-hosted run can now read a message, thread or search (
owner_read), the current Canvas (owner_canvas_get) and Canvas revision history (owner_canvas_history) with its signed-in owner's access, including in private channels the agent is not in. These reach the owner's session only through new typedOwnerAccessoperations, and they write nothing. It also stops external app connections from getting the owner's access. Saving the owner's attachments (owner_media_get) is left out; see Outstanding.Fixes NEXUS-4285 · Initiative status: https://blockcell.sqprod.co/sites/kylez-buzz-mcp-initiative/
How it reads
createOwnerAccessruns each new operation on the owner's current binding. Each answer is released only if that binding still holds after the protection review of the tool result. A sign-out, account switch, reconnect, Stop or turn end returns nothing. The host still decides the agent, owner and community, and every answer names them inexecutedAs.partialwhile that list is incomplete. Other channels' matches still fill the relay's page, soqtells the agent to narrow by channel or time when alimitedpage shows few results. One read fetches a page's edits, asreaddoes; when it fills the relay's 500 cap, the answer saysedits_limitedinstead of passing earlier text off as current.session.snapshotRead. That is the relay's own access-checked answer, which feeds no session projection. So owner reads change no unread or read state, membership, subscriptions or channel lists. A test asserts thatreadRemotesees only relay-signed channel records.readand the app's Canvas history. Reads use thebefore/before_idcursor, fetch limit+1 to detect an older page, and apply the author's newest edit. A thread page holds at most 499, not the ordinaryread's 500, because owner reads go through the relay reader, which caps a filter at 500 and the lookahead needs one. The first history page is read from the writer, as the app's history is, and so is an explicit revision, so an ID history just listed is found.edited()chose the newest edit before checking its author, so if an agent edited its message and its owner edited it later, bothreadandowner_readshowed the original. It now picks the newest eligible edit. Forowner_readthat also follows the app's fold (fold.tsbyAuthor, documented indocs/relay-queries.md): in a chat message's or forum post's channel, an edit by the author's owner counts when the owner's Buzz has verified that ownership from the author's signed profile. Without that verification only the author's own edits count.readpasses no owner map, so it keeps author-only edits.ownerAccesssince feat(agents): let app-hosted agents read their owner's channels and members #2745, so it listed and could call theowner_*tools.connectExternalnow dropsownerAccessfrom that handle, as the docs already promised.docs/agents.mdnow notes that, as its setup already warns of all tool output, members serving its model can read what they return.There is no new logger, signer, client or lifecycle. The ordinary tools' identity, schemas and errors are unchanged:
messageLinewas extracted fromtools.tswithout behavior changes.Tests
owner-access.test.tscovers:limitedandpartial;callSessionTool→appClient→createOwnerAccessfor the new tools.service.test.tsproves an external connection to a bundled harness's run lists noowner_*tool and refuses a guessedowner_channel_listcall, while that run's own handle does carry the owner's access. It fails without the fix.owner.test.tscovers the tools' arguments, limits, continuations and errors.app-client.test.tscovers the turn checks and signal for the new wrappers.Native gaps
Outstanding
owner_media_getis not in this PR. The nativeapp_agent_mediacommand saves a file where the agent's own tools can open it before the call's final checks run: the result protection review, and whether the owner's binding still holds after a download. No existing mechanism holds the file back until those pass, so the tool is neither listed nor callable. It needs a stage-then-publish step in the native media command first. The removed implementation is kept on the worker's preserve ref for that follow-up.🤖 Generated with Claude Code