Repository navigation
Stream protected evidence across agent runtimes - #2882
Conversation
Raise the action transport ceiling to 8 MiB and coordinate prompt, Claude control-channel, loopback MCP, and opt-in command output bounds. Preserve complete evidence and existing denial behavior. Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Use the shared action-byte limit in the captured-file regression fixture so it still verifies refusal before model review or publication. Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Capture evidence and prompt limits at provider registration, validate them against host resource safeguards, and preserve legacy defaults. Cover plugin-selected limits, immutable declarations, invalid registrations and file-publication refusal. Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
buzz-app/src/bundled/claude-code/claude.ts
Lines 1017 to 1020 in 561b501
When protectionTranscript crosses its 64 KiB buffering threshold, history is a TextSource rather than an array. This branch therefore passes the source object to protectionSources once; it cannot inspect the streamed JSONL records and emits one default context source, so native tool_result records lose their tool-result phase and every record becomes one quoted JSON string. Consequently, a policy with context enabled and tool-result disabled still reviews disabled tool results whenever the transcript is large, while the same small transcript skips them. Project the stream record-by-record while preserving each record's source and phase.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| !isTextSource(prompt.input) && | ||
| !protection?.value.checkEvidence && | ||
| new TextEncoder().encode(JSON.stringify(prompt)).length > | ||
| LEGACY_PROMPT_BYTES |
There was a problem hiding this comment.
Keep the prompt cap for nonstreaming runtimes
When the calling protection plugin implements checkEvidence, this condition removes the 768 KiB limit from string prompts even if the selected model runtime did not declare streaming: true. A modern protection provider selecting a legacy or third-party nonstreaming runtime can therefore send an arbitrarily large materialized prompt through its buffered compatibility path, contrary to the documented legacy bound and potentially exhausting the process transport or model runtime. Base this exception on the selected runtime's provider.streaming capability rather than the caller's evidence-check implementation.
Useful? React with 👍 / 👎.
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Pi's host now sends protected actions only over the evidence protocol, so its single-message `security` handler had no sender. Remove it and port the Pi lifecycle and isolation tests to begin/chunk/finish/cancel. `checkEvidence` already denies unless the provider read every chunk and the signal is still live, so the bridge no longer repeats that check, and EvidenceTransfer drops its duplicate read-once flag, `consumed` field and unreachable byte-count guard. A settled check still fails the transfer so blocked chunk writers are released. The initialize result no longer advertises an evidence capability that neither sender reads. Tests: drop the Rust loopback test (no host MCP change), dead mocks and an always-true Codex assertion; cut the 40 MiB and 52 MiB fixtures to just past the thresholds they exercise; move the encoder test beside json-stream. Revert unrelated manifest formatting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7cd080b0c0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| !["kind,session", "id,kind,session"].includes( | ||
| Object.keys(params).sort().join(), | ||
| ) || |
There was a problem hiding this comment.
Preserve optional command metadata in streamed admission
When a native protected action supplies the documented optional top-level command, this allowlist rejects its begin request because only kind,session and id,kind,session are accepted. ProtectionAction still exposes command?: string, the provider API documents it as metadata, and the previous bridge validated and forwarded it, so affected Codex/Goose command actions now fail closed before reaching the checker. Accept a string command in the begin metadata and include it in the action passed to checkEvidence.
AGENTS.md reference: AGENTS.md:L94-L97
Useful? React with 👍 / 👎.
kazemek21
left a comment
There was a problem hiding this comment.
[P2] Preserve transcript phases across the streaming threshold
The existing phase concern still applies at this head. Large histories become one context source, so selective phase policies behave differently solely because history crossed the buffering threshold. Please preserve per-record phase routing while streaming, including both context-disabled and tool-result-disabled policies.
Kyle reviewed the PR and the Nexus review-tracker notes and approves with this non-blocking comment.
Posted by Codex (gpt-6.1-sol) on behalf of kylez
A transcript past the 64 KiB buffering threshold reached the gate as one context source, so tool results lost their tool-result phase. Policies that disable either phase then behaved differently by transcript size. Stream a large transcript as one source per phase, routing each record through the same projection a small history uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
## 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>
…nt-progress-rows * origin/main: (53 commits) fix(search): make Command F search the Bestie conversation (#3084) fix(plugins): keep failed update checks on the plugin's Settings row instead of a toast (#3088) Show single-agent Stop on the activity bubble (#3140) fix: support explicit hosted browser origins (#3149) fix: keep Codex tool namespace descriptions bounded (#3128) fix(messages): show "New messages" in channels longer than one page (#3071) Keep shared agent guidance generic (#3134) fix(typing): a thread reply also ends the agent's channel-level typing (#3148) Stream protected evidence across agent runtimes (#2882) feat(identity): consolidate key and account controls (#3069) Add portrait-colored foil and animated agent card reveal (#3144) NEXUS-4190: Disclose unavailable saved Solo choices (#2544) docs: mark the plugin API as preview and say which parts are steady (#3129) Open the app with a saved key; check the account's key in the background (#3064) fix(federated-identity): name only communities that require NIP-FI as affected by a switch (#3072) feat(pairing): signed in with Builderlab, say to get the app instead of pairing (#3039) Show Add agent button on shared agent previews (#3131) NEXUS-4224: Preserve peer unread intent during automatic reads (#2641) fix(plugins): review a pinned link to an installed plugin's source as an update (#3024) perf(profiles): resume verified-profile batches after a planned renewal (#2293) ... Signed-off-by: Gil Birman <gil@squareup.com> # Conflicts: # docs/buzz-help.md
Large actions and transcripts can fail with “Protected action is too large” before the protection plugin sees them. This removes the host's 256 KiB action and 768 KiB review-prompt caps. Plugins own evidence storage and model-capacity handling.
Design
checkEvidence(metadata, source, signal)contract. Identity and policy come from the host; an allow requires complete evidence consumption. No legacy provider fallback or streaming opt-in.Chunk sizes bound transfer buffering, not total evidence. Cancellation, ordering, metadata validation, and deadlines remain. Model context and plugin resources are finite; incomplete or failed inspection cannot authorize an action.
Validation
Real-process 40 MiB review and 52 MiB Claude transcript checks verify complete replay. Codex/Goose fixtures and Pi integration checks cover runtime delivery and enforcement. Regression coverage includes cancellation, incomplete consumption, snapshot lifetime, and write failure. The affected host suites pass, including the large-transcript and native-watchdog checks; independent review found no blockers. Required local push checks cover types, related tests, design checks, and Rust Clippy.
Remaining work: compatible plugin/runtime artifacts are built locally but unpublished. Packaged macOS integration and human acceptance remain outstanding. Existing browser-fixture approval checks do not establish packaged-app acceptance.