refactor(server): ACP client fs and terminals are opt-in per flavor - #13623
Conversation
|
Macroscope skipped reviewing this pull request. Per-review cost limit exceeded (workspace setting). This review would cost an estimated $40.87, which exceeds your per-review limit of $15.00. The top 3 files driving up this estimate:
Tip To get this pull request reviewed, you can:
|
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR changes production defaults for ACP filesystem and terminal capabilities, shifting existing agents between T3-mediated and agent-owned file and shell execution. The added coverage reduces uncertainty, but the default behavior change requires human review. Not approved because:
Review your spending limits in Billing settings, or comment |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
0d4f03f to
2f2a96e
Compare
e5a3e33 to
dcb5ce1
Compare
| @@ -5329,25 +5333,23 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV | |||
| Effect.succeed(request), | |||
| requestContext.requestId, | |||
There was a problem hiding this comment.
This changes the no-clientFileSystem path to leave both handlers unregistered, but the existing filesystem tests now explicitly opt in, so they no longer exercise that path. Could you add a focused adapter test that omits clientFileSystem and verifies read/write requests are rejected without touching disk?
Posted via Macroscope — Effect Service Conventions
23fff73 to
6cb5ed0
Compare
0abd34b to
bae8e22
Compare
Every ACP agent was offered client fs, so agents like Grok routed their file reads and writes through T3 instead of their own permission model. Now only Antigravity (its own workspace-contained handlers) gets client fs and only Devin gets client terminals; everyone else reads, writes and runs commands itself and asks through session/request_permission. The unconfined generic fs handlers are gone, so a stray fs request gets method-not-found. The Grok recorder pins the clientCapabilities T3 advertises, and the two Grok fixtures that carried fs frames are re-recorded live against grok 1.0.41: Grok asks once for its own write and sends no fs or terminal requests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The mock agent can now send fs/write_text_file and fs/read_text_file on every prompt regardless of what the client advertised. A generic flavor without clientFileSystem answers both with method-not-found and nothing is written. A registry test pins client terminals to Devin and client fs to no registry agent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
6cb5ed0 to
c8ec40f
Compare
V2 offered client
fs(read and write) to every ACP agent, and client terminals to every registry agent. Agents that honor those capabilities route their file and shell work through T3, where T3 runs it with the server's own privileges and a policy guard of its own, not through the agent's permission model. Grok is one: it usesAcpSessionFsonly when the client advertises both fs capabilities and otherwise falls back to its ownLocalFs(agent_ops.rs:4160,:4193-4205), and does the same for terminals (:4209-4224,AcpTerminalRunnervsTerminalRunner).Layer 2 of the ACP stack ("rely on the agent's own sandboxes and permission models rather than implementing our own, like Claude and Codex"). Stacked on #13613.
What changed
clientCapabilities.fsfollows the flavor'sclientFileSystem(fix(server): Antigravity keeps its workspace containment #13613), and without it no fs handler is registered, so effect-acp answers a strayfs/*request with method-not-found. OpenCode and Kilo send one after approved edits whatever the capability says. Only Antigravity setsclientFileSystem.AcpRegistryAdapterV2passesclientTerminalsonly for Devin. Grok and Antigravity never had them.AcpClientFs.tsand its test are deleted, since nothing uses them now. The runtime-policy guards stay in front of Antigravity's handlers and Devin's terminals. Removing them is a later layer.clientCapabilitiesT3 advertises instead of writing<any>, so a transcript records whether Grok was offered fs or terminals.tool_call_read_only_on_requestandtodo_listfor Grok were the only Grok fixtures withfs/*frames. I re-recorded both live againstgrok 1.0.41. Grok now reads and writes the workspace itself:todo_listhas no permission requests, andtool_call_read_only_on_requestasks once (session/request_permission,kind: "edit") for its own write. A new shared assertion,assertNoAcpClientFileOrTerminalRequests, checks that the initialize advertises neither capability and that nofs/*orterminal/*frame occurs. The old Grok assertion ("T3 must serve Grok's client-mediated read") is replaced by these checks.Known risks
fs/write_text_fileafter an approved edit whatever the capability says, as an un-awaited promise with no catch (opencode/src/acp/permission.tswriteProposedEdit,void this.input.connection.writeTextFile(...); Kilopackages/opencode/src/acp/permission.ts:121). T3 now answers method-not-found, so that promise rejects unhandled inside the agent process. Whether Bun kills the process on that is unverified: neither agent's ACP mode is installed here. Before this change the same write ran with the server's privileges.Decisions (override if you disagree)
Overlap
v2/grok-gates) also editsrecord-grok-acp-replay-fixture.tsandfixtures/shared.ts, in different hunks. Its newgrok_monitorrecording has nofs/*frames and keepsclientCapabilities: "<any>", which still matches.--permission-mode defaultfor this read-only on-request policy).Verification
All commands ran in
apps/serverwithTMPDIRunder /home:vp test run src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts: 87/87 passed, including the two re-recorded Grok fixtures. Before re-recording, the old transcripts failed as expected (the replay agent waited forfs/*responses T3 no longer serves).vp test run src/orchestration-v2/testkit/OrchestratorReplayFixtures.contract.test.ts: passed.vp test run src/orchestration-v2/Adapters/{AcpAdapterV2,AcpRegistryAdapterV2,AntigravityAdapterV2,GrokAdapterV2}.test.ts src/provider/Drivers/AntigravityDriver.test.ts: 153 passed. The two adapter tests for the fs policy guard now opt in through a testclientFileSystem. New tests:AcpAdapterV2 > answers fs requests method-not-found when the flavor does not opt into client fs: the ACP mock agent (newT3_ACP_CLIENT_FS_PROBE_PATHbehavior) sendsfs/write_text_filethenfs/read_text_fileon a prompt whatever the client advertised. The initialize carriesfs: false, terminal: false, both requests get-32601, and no file is written. With the old always-on generic handlers put back, the test fails.AcpRegistryAdapterV2 > offers client terminals to Devin only and client fs to no registry agent: Devin advertisesterminal: true, geminiterminal: false, bothfs: false. Giving every registry agent terminals fails it.node scripts/record-grok-acp-replay-fixture.ts --scenario tool_call_read_only_on_requestand--scenario todo_listwithT3_GROK_BIN=~/.local/bin/grok, re-run after rebasing onto fix(server): V2 Grok launches in the thread's permission mode #13616. Both passed their live assertions. One earliertodo_listtake (before the rebase) wrote the todo list once, already completed, and failed the fixture's "two plan updates" check; that was the model's choice, not this change.vp exec tsc --noEmit -p .: clean.vp run knip:check: clean.vp linton the touched files: only warnings that already exist on the base branch.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code