Skip to content

agent: a permission request says what the agent would do - #19

Merged
foxnne merged 1 commit into
mainfrom
chat-approvals
Oct 19, 2026
Merged

foxnne merged 1 commit into
mainfrom
chat-approvals

Conversation

@foxnne

@foxnne foxnne commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Part of fizzyedit/plans AGENTS_ANYWHERE_PLAN (plans/AGENTS_ANYWHERE_PLAN.md), step 6: approvals you can judge.

What changes

An agent's session/request_permission showed only the tool call's title beside its options, so the person could not tell what they were allowing. Now:

  • The question says what the call would do, one line per tool kind (src/permission.zig, std-only):
    • execute: Run zig build test (plain text; every piece the agent wrote, command, path or URL, is cut to its first line and 100 bytes, with "… (more lines)", so it cannot add lines of its own beside the buttons)
    • edit: Edit src/a.zig: +2 −1, Create new.zig: +2 −0, Edit 3 files: +5 −2, or Edit <path> without a diff
    • read / delete / move: the path (and line, "and N more"), Move a to b
    • search: Search for <pattern> in src; fetch: Fetch <url> or Search the web for <query>; otherwise the title (on its own, without a step: The agent asks to <title>.)
    • paths inside the chat's folder are written relative to it, others in full
  • The detail is behind the step's disclosure: the request's diffs (open, as before) and locations, plus the whole command (as sh code) or, for a call that says nothing else about itself, its rawInput as JSON (capped at 4 KB on a UTF-8 boundary). One update per request; a step's earlier content (a diff from its tool_call) is never wiped by a request that gives none or an empty list.
  • A request about a call the chat has not seen becomes a step, so every request is on one; a later tool_call for it updates that step instead of adding a second. Each known call keeps its kind, since a ToolCallUpdate may leave it out.
  • Requests are cleared when they can no longer be answered: stopping the turn, the agent exiting, or a restart answers each waiting request cancelled (as ACP requires for a cancelled turn) and closes its question through a new chat command, chat.unask { chat, question }; the buttons give way to "Not answered". Closing the chat drops them without a call.
  • The agent's options (allow once / always, reject once / always) are as the agent offers them, unchanged.

Checked against the current ACP spec (protocol/tool-calls: ToolCallUpdate fields, diff with optional oldText, locations, permission outcomes).

Not here: flagging a pending approval when the chat is not in view (the plan's step lists it; left for a follow-up).

How it was checked

  • zig build test: new permission.zig tests for each kind's line and detail, the diff counts, and the waiting requests drained once (what exit/restart/cancel use); a Transcript test that cancelQuestion closes an open question and leaves an answered one.
  • A --quiet fizzy (main, macOS) with a scripted ACP agent asking three permissions (execute on a known step, an edit with a diff on an unseen step, an MCP call): each showed its line and detail; an answer through agent.chat_answer reached the agent as selected; when the agent exited, every open question turned to "Not answered" with "The agent stopped." and nothing written to the dead pipe.
  • code-review at medium: fixed the content wipe and double update, the detail missing on known steps, unsanitized paths/URLs, literal backticks, trailing-newline counts, the UTF-8 cut, the OOM path in draining requests (now pop, no allocation), the lost framing of a stepless question, and fetch queries.

🤖 Generated with Claude Code

The agent's `session/request_permission` showed only the tool call's title beside its options.
Now the question reads as one line per kind: the command for execute, the file and its +/- line
count for an edit (or Create for a new file, or N files), the path (and line) for read, delete
and move, the pattern for search, the URL for fetch, the title otherwise. The step's disclosure
holds the detail: the request's diffs and locations, the whole command, or the call's raw input
as JSON when nothing else says what it is. A request about a call the chat has not seen becomes
a step, so every request has one. The agent's options are unchanged.

A request still waiting when the turn is stopped, the agent exits, or it restarts is answered
`cancelled` (ACP) and its question closes through the new `chat.unask`, so no buttons are left
answering nothing.

Part of fizzyedit/plans AGENTS_ANYWHERE_PLAN, step 6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne marked this pull request as ready for review October 19, 2026 20:21
@foxnne
foxnne merged commit e18011f into main Oct 19, 2026
6 checks passed
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.

1 participant