feat(approvals): human-in-the-loop tool-call confirmation (#124 Tier 1+2)#517
Open
clemenshelm wants to merge 9 commits into
Open
feat(approvals): human-in-the-loop tool-call confirmation (#124 Tier 1+2)#517clemenshelm wants to merge 9 commits into
clemenshelm wants to merge 9 commits into
Conversation
added 9 commits
June 16, 2026 16:39
Durable, server-enforced confirmation record for #124 Tier 1+2. Bound to (agentId, requesterId, argsDigest, sessionKey), consumed once. tier enum reserves 'escalate' for the deferred four-eyes tier.
- computeArgsDigest: stable canonical sha256 binding one exact tool call - decideGate: consume-once (FOR UPDATE SKIP LOCKED) or create pending; fail-closed on expiry - resolveDecision/expireStale; self-confirm authorization - defaultConfirmTools: powerful-tool auto-default from tool-registry - timestamptz columns; AgentPluginConfig.pinchy-approvals.confirmTools Real-DB integration tested.
- POST /api/internal/approvals/gate-check (gateway-token): the server-side security boundary; consume-or-block + audits approval.requested/consumed - GET /api/approvals: requester's pending confirmations for the chat card - POST /api/approvals/[id]/decision: self-confirm approve/deny + audit - approval.* audit family (PII-free: argsDigest only; summary stays operational) - decideGate gains a created flag to keep approval.requested idempotent Real-DB integration tested (132 integration, 5962 unit, tsc clean).
…Config Lets the agents PATCH route persist the per-agent confirm-tool policy.
- New internal hook-only plugin: before_tool_call gate calls gate-check for every tool and fails closed if the approval service is unreachable - gate-check now short-circuits ungated tools server-side (always-fresh policy, no plugin cache); plugin needs zero policy state - Wiring: KNOWN/INTERNAL_PLUGINS, manifest map, regenerateOpenClawConfig emission (always enabled), Dockerfile.pinchy, entrypoint.sh expected list - Drift guards updated (schema-sync classification, plugins.allow order) - E2E: gated tool blocked → approval.requested → user grants → approval.granted
New 'Require confirmation before' section in the agent permissions tab (admin-only): per-tool checkboxes + a 'Use recommended' button that selects the agent's powerful tools. Persists to pluginConfig['pinchy-approvals'].
A polling ApprovalsInbox (mounted in the app shell) lists the acting user's pending tool-call confirmations with Approve/Deny. After approving, the user asks the agent to proceed and the gate consumes the ticket. Deferred (follow-up): native requireApproval inline-in-chat card + auto proceed-injection (needs an openclaw-node extension).
Covers enabling per-agent confirmation, the approve/deny flow, the audit lifecycle, and the server-side enforcement guarantee. Pinchy voice.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements Tier 1+2 of #124 — human-in-the-loop tool-call confirmation. An admin marks tools as "require confirmation"; the agent then pauses and asks the acting user before running them. The user approves their own action in seconds — no admin bottleneck.
This follows the reframe in #124: the validated, market-standard need is confirmation (the acting user confirms, so the agent can't act autonomously), not an admin approval queue. Four-eyes (a different approver) stays documented in the issue as a demand-pulled extension and is not built here.
How
A two-layer, server-enforced design:
pinchy-approvalsregisters abefore_tool_callhook that asks Pinchy'sgate-checkendpoint for every tool. The endpoint is the security boundary; it fails closed if unreachable.(agentId, requesting user / senderId, args digest, originating session)and consumed exactly once (FOR UPDATE SKIP LOCKED). Changed args ⇒ new confirmation. Never shared across users of a shared agent. Pending confirmations expire (15 min) and fail closed.Surfaces
powerfultools).ApprovalsInboxthat lists the user's pending confirmations.approval.requested → granted/denied → consumed/expiredlifecycle. PII-free — only the args digest is logged; the human-readable summary stays on the operational row.Tested
approval.requested→ user grants →approval.granted(runs in the integration E2E CI job; satisfies the internal-plugin coverage contract).pnpm build,tsc --noEmit,eslint(0 errors), and the docs build all pass. All Plugin-Integration-Contract drift guards updated.Deferred (documented, not built)
requireApprovalinline-in-chat card + automatic proceed-injection (would also unlock Slack/Telegram approval routing) — needs anopenclaw-nodeextension. Interim UX: approve in the inbox, then ask the agent to proceed.Refs #124.