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

Stream protected evidence across agent runtimes - #2882

Merged
shellz-n-stuff merged 12 commits into
mainfrom
codex/protected-review-size
Nov 10, 2026
Merged

shellz-n-stuff merged 12 commits into
mainfrom
codex/protected-review-size

Conversation

@shellz-n-stuff

@shellz-n-stuff shellz-n-stuff commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

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.

Before: whole-request caps reject evidence before review. After: bounded streaming delivers complete evidence to a plugin that owns storage and review capacity.

Design

  • One required 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.
  • Codex, Goose, and Pi use the shared ordered chunk protocol. Claude reads immutable transcript snapshots, and its native watchdog spools large frames while preserving callback deadlines.
  • Review adapters accept replayable text. Process writes cannot interleave; a partial-write failure stops the worker.

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.

shellz-n-stuff and others added 3 commits November 6, 2026 14:30
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>
@shellz-n-stuff shellz-n-stuff changed the title Allow multi-megabyte protected agent reviews Let protection plugins declare their review budgets Nov 6, 2026
shellz-n-stuff and others added 2 commits November 6, 2026 16:15
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>
@shellz-n-stuff shellz-n-stuff changed the title Let protection plugins declare their review budgets Stream protected evidence across agent runtimes Nov 6, 2026
@shellz-n-stuff
shellz-n-stuff marked this pull request as ready for review November 6, 2026 22:46
@shellz-n-stuff
shellz-n-stuff requested a review from a team as a code owner November 6, 2026 22:46
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T02:46:11.191947Z 7cd080b Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

...[
...(Array.isArray(history) ? history : [history]),
...this.protectedEvents,
].flatMap((value) => protectionSources(value)),

P2 Badge Preserve per-record Claude transcript phases

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".

Comment on lines +256 to +259
!isTextSource(prompt.input) &&
!protection?.value.checkEvidence &&
new TextEncoder().encode(JSON.stringify(prompt)).length >
LEGACY_PROMPT_BYTES

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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>
@shellz-n-stuff
shellz-n-stuff marked this pull request as draft November 6, 2026 23:25
shellz-n-stuff and others added 5 commits November 6, 2026 17:29
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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +92 to +94
!["kind,session", "id,kind,session"].includes(
Object.keys(params).sort().join(),
) ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 kazemek21 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.

[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>
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>
@shellz-n-stuff
shellz-n-stuff merged commit e5096be into main Nov 10, 2026
60 checks passed
@shellz-n-stuff
shellz-n-stuff deleted the codex/protected-review-size branch November 10, 2026 00:37
gilgtm added a commit that referenced this pull request Nov 10, 2026
…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
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