Skip to content

Render sandbox command approvals as terminal confirmations - #333883

Merged
Osvaldo Ortega (osortega) merged 3 commits into
mainfrom
osortega/agents/sandbox-permission-prompt-review
Sep 2, 2026
Merged

Render sandbox command approvals as terminal confirmations#333883
Osvaldo Ortega (osortega) merged 3 commits into
mainfrom
osortega/agents/sandbox-permission-prompt-review

Conversation

@osortega

@osortega Osvaldo Ortega (osortega) commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Sandbox command approvals render as a plain text card instead of the terminal confirmation, so the user approves a command they cannot see.

Before / after

A shell-command permission request from a cloud sandbox session:

Before Title "Run command", body showing the agent's stated intention (e.g. "Find copilot CLI sandbox builder"). The command itself is never shown.
After The standard terminal confirmation card, with the command rendered as shellscript.

Why it happens

isTerminalToolCall has three ways to recognize a terminal call, but two of them only apply once the tool is Running/Completed. At confirmation time — the moment that matters — the only live signal is _meta.toolKind === 'terminal'.

toolKind is a VS Code-private rendering hint, not part of the Agent Host Protocol. Our own getToolKind says so: "This is not part of the protocol and is injected by the agent adapter." Our in-process adapters (mapSessionEvents.ts, claudeToolDisplay.ts) stamp it; a remote agent host is a separate implementation and has no reason to know about it. With no toolKind, the call falls through to the generic tool card.

Note the contrast with subagents: isSubagentTool already has a tool-name fallback for exactly this "server didn't send _meta" case. Terminal had none.

The change

getToolKind falls back to the permission metadata a remote host does provide on a pending confirmation, mapping it to the rendering hint:

  • a shell-command request → terminal
  • a file-read request → read

Everything downstream is untouched: the existing terminal confirmation renderer takes over, reading the command from toolInput, which was already available. Putting the fallback in getToolKind rather than in isTerminalToolCall means all eight call sites agree — confirmation, live rendering, and history replay.

Path-batched requests are deliberately not mapped to terminal. Their subject is a list of paths rather than a command line, so rendering one as a command would misdescribe what is being approved. Covered by a regression test.

Why this isn't temporary

The Agent Host Protocol has no field that identifies a pending tool call as a shell command — not toolName, intention, confirmationTitle, or anything else on ToolCallPendingConfirmationState. The only in-protocol signal is a Terminal content block, which does not exist until the tool is already running, i.e. after approval. So this gap is not something a host-side change alone can close today, and the fallback is expected to be long-lived.

An earlier revision of this PR also adapted file-write permissions (restating the target file as a markdown link so the card and sessions list showed a file pill). That has been removed: it duplicated information a host can supply directly, so it would have become dead code rather than a lasting fix. Tracked separately with the agent host team.

Testing

Three unit tests in stateToProgressAdapter.test.ts, built from a real pending-confirmation payload:

  • a command permission renders the terminal confirmation with the real command
  • the alternate payload shape is honoured when the primary one is absent (older hosts send only the raw form)
  • a path-batched permission stays on the generic card

Verified the first two fail without the change. Full suite: 814 passing across the adapter, chat contribution, client tools, confirmations, approval model and tool-display suites; typecheck and layer checks clean.

Risk

The fallback only fires when _meta.toolKind is absent, which is only ever a remote host — local sessions cannot reach it. No shared VS Code rendering code is modified; the adapter's own diff is a documentation comment plus the delegation to getToolKind.

Copilot AI balanced review requested due to automatic review settings September 1, 2026 22:10

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Remote write labels and cross-platform paths remain incorrect, and newly editable inputs are silently ignored.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Balanced
Findings: 3 Medium severity

New issues introduced by this change (3)
Severity Finding
Medium severity src/​vs/​workbench/​contrib/​chat/​browser/​agentSessions/​agentHost/​stateToProgressAdapter.ts — The normalized write data does not update the invocation label. AgentSessionApprovalModel reads…
Medium severity src/​vs/​workbench/​contrib/​chat/​browser/​agentSessions/​agentHost/​stateToProgressAdapter.ts — This converts a remote filesystem path with URI.file inside getEditFileMessage, whose separator…
Medium severity src/​vs/​workbench/​contrib/​chat/​browser/​widget/​chatContentParts/​toolInvocationParts/​chatToolConfirmationSubPart.ts — This makes the JSON editor appear for plain-string Agent Host confirmations, but that editor is…
What changed in this PR

Improves Agent Host sandbox permission prompts so command and file approvals use specialized renderers.

Changes:

  • Infers rendering metadata from remote permission requests.
  • Normalizes write-permission messages and inputs.
  • Adds regression tests for command, path, and write prompts.
File Description
stateToProgressAdapter.test.ts Tests remote permission rendering.
chatToolConfirmationSubPart.ts Renders structured input alongside plain messages.
stateToProgressAdapter.ts Adapts command and write permission metadata.
copilotToolDisplay.ts Reuses shared edit-message formatting.
streamingToolCallDisplay.ts Adds shared edit-message helper.
sessionReducers.ts Infers tool kinds from permission metadata.
agentPermissionRequestMeta.ts Parses remote permission metadata.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Translate the write permission's invocationMessage, not just the
confirmation message. AgentSessionApprovalModel reads
invocationMessage for non-terminal approvals, so the sessions list
showed the host's generic "Edit file" while the confirmation card
showed the file. Both now derive from the same value.

Normalize a Windows host path before formatting the link. URI.file
only rewrites separators when the client runs Windows, so a Windows
host paired with a non-Windows client collapsed C:\repo\file.ts into
a single-segment basename and an unresolvable URI.

Revert the chatToolConfirmationSubPart change. It surfaced the raw
input for plain-string confirmations, but that editor is writable
while agent-host approval dispatch never sends editedToolInput, so
edits were silently discarded. Translating the message removes the
need for it, and this no longer touches shared rendering code.

Also stop emitting the raw input for writes: it repeated the file the
message already names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@osortega Osvaldo Ortega (osortega) changed the title Improve sandbox permission prompt rendering Render sandbox command approvals as terminal confirmations Sep 1, 2026
@osortega
Osvaldo Ortega (osortega) marked this pull request as ready for review September 1, 2026 23:22
The write path restated the host's file name as a markdown link so the
confirmation card and sessions list would show a file pill. That
duplicates information a host can supply directly, so it would have
become dead code rather than a lasting fix. Tracked separately with
the agent host team.

The command path stays. It is not compensating for a host deviation:
the Agent Host Protocol has no field that identifies a pending tool
call as a shell command, so `_meta.toolKind` (a VS Code-private hint a
remote host has no reason to set) is the only signal, and without the
fallback a sandbox command approval shows the agent's intention
instead of the command being approved.

Reverts the getEditFileMessage extraction, the write branch and its
Windows path normalization, the write fields on the permission meta
reader, and the three write tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@osortega
Osvaldo Ortega (osortega) force-pushed the osortega/agents/sandbox-permission-prompt-review branch from 4bec2ea to 8a642e4 Compare September 1, 2026 23:24
@osortega
Osvaldo Ortega (osortega) merged commit 98807d2 into main Sep 2, 2026
40 checks passed
@osortega
Osvaldo Ortega (osortega) deleted the osortega/agents/sandbox-permission-prompt-review branch September 2, 2026 02:02
@vs-code-engineering vs-code-engineering Bot added this to the 1.137.0 milestone Sep 2, 2026
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.

3 participants