dashboard: inline approval card — Approve/Deny with Y/N kbd, redacted preview, 5-min auto-deny, audit event #966

Closed
opened 2026-05-08 12:15:40 +00:00 by claude-desktop · 1 comment
Collaborator

User story

As an operator I want destructive tool calls (force-push, file delete, shell with rm / --force, secret access) to render an inline Approve/Deny card in the timeline that captures my decision in the audit log, so destructive ops never run silently and an idle session auto-denies after 5 min instead of sitting forever.

Acceptance criteria

Server

  • ApprovalGate middleware in the agent runner: a tool policy can mark a call as requiring approval; the runner pauses the call, emits a tool_call event with state: "approval-requested", and waits on a POST /agents/approvals/:call_id { decision } endpoint or 5-minute timeout (configurable).
  • Timeout = output-denied with reason: "auto-denied (timeout)".
  • Audit log: every approval/denial captured as a system event with operator id (from session cookie), decision, timestamp, redacted args.

Frontend

  • <ApprovalCard> renders inline at the matching event. Approve / Deny buttons via existing <Button> primitive (tone="success" / tone="error"). Keyboard Y / N while focused.
  • Args preview is redacted by default (env vars masked, paths shown, secrets stripped) — full args behind a "show full" toggle.
  • Counts down the 5-minute timer (visible on the card).

Tooling

  • Configurable list of approval-required tools per agent type. Default seeds: Bash matching rm -r|--force-with-lease|git push --force|gpg|cat .env, Delete, mcp__forgejo__delete_*, Write to .env*/secrets.*.
  • Settings page exposes the rules; existing tool-policy schema is extended.

Tests

  • Approve path: agent resumes, tool runs, output-available lands.
  • Deny path: agent receives "denied" tool result, run continues per agent prompt's deny handling.
  • Timeout path: 5-min timer fires, auto-deny, audit event recorded.

Out of scope

  • Approving from mobile / Slack / email — push-notification surface is a separate channel issue.
  • "Approve always for tool X" persistence — explicitly out; every gate is per-call.

Dependencies

  • Depends on <ToolCard> ticket — approval card composes into it.
  • Depends on the state-enum-on-wire ticket — uses the approval-requested state.

References

## User story As an operator I want destructive tool calls (force-push, file delete, shell with `rm` / `--force`, secret access) to render an inline Approve/Deny card in the timeline that captures my decision in the audit log, so destructive ops never run silently and an idle session auto-denies after 5 min instead of sitting forever. ## Acceptance criteria ### Server - [ ] `ApprovalGate` middleware in the agent runner: a tool policy can mark a call as requiring approval; the runner pauses the call, emits a `tool_call` event with `state: "approval-requested"`, and waits on a `POST /agents/approvals/:call_id { decision }` endpoint or 5-minute timeout (configurable). - [ ] Timeout = `output-denied` with `reason: "auto-denied (timeout)"`. - [ ] Audit log: every approval/denial captured as a `system` event with operator id (from session cookie), decision, timestamp, redacted args. ### Frontend - [ ] `<ApprovalCard>` renders inline at the matching event. Approve / Deny buttons via existing `<Button>` primitive (`tone="success"` / `tone="error"`). Keyboard `Y` / `N` while focused. - [ ] Args preview is redacted by default (env vars masked, paths shown, secrets stripped) — full args behind a "show full" toggle. - [ ] Counts down the 5-minute timer (visible on the card). ### Tooling - [ ] Configurable list of `approval-required` tools per agent type. Default seeds: `Bash` matching `rm -r|--force-with-lease|git push --force|gpg|cat .env`, `Delete`, `mcp__forgejo__delete_*`, `Write` to `.env*`/`secrets.*`. - [ ] Settings page exposes the rules; existing tool-policy schema is extended. ### Tests - [ ] Approve path: agent resumes, tool runs, `output-available` lands. - [ ] Deny path: agent receives "denied" tool result, run continues per agent prompt's deny handling. - [ ] Timeout path: 5-min timer fires, auto-deny, audit event recorded. ## Out of scope - Approving from mobile / Slack / email — push-notification surface is a separate channel issue. - "Approve always for tool X" persistence — explicitly out; every gate is per-call. ## Dependencies - Depends on `<ToolCard>` ticket — approval card composes into it. - Depends on the state-enum-on-wire ticket — uses the `approval-requested` state. ## References - assistant-ui inline approvals: https://github.com/Yonom/assistant-ui - Sourcegraph Cody Agentic Chat: https://sourcegraph.com/docs/cody/capabilities/agentic-chat
Collaborator

🤖 Auto-assigned to code-lead (heuristic: area:dashboard + body 2414 bytes (> 2 KB) — code-lead (heavy)). Reply /unassign to reroute.

🤖 Auto-assigned to **code-lead** (heuristic: area:dashboard + body 2414 bytes (> 2 KB) — code-lead (heavy)). Reply `/unassign` to reroute.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#966
No description provided.