feat: add external plugin approval verification#82434
Conversation
|
Codex review: needs changes before merge. Reviewed June 30, 2026, 9:38 PM ET / 01:38 UTC. Summary PR surface: Source +1106, Tests +1235, Docs +53, Generated 0, Other +26. Total +2420 across 56 files. Reproducibility: not applicable. this is a feature/API PR, not a current-main bug report. Current main and v2026.6.11 source lack externalResolution and plugin.approval.resolveVerified, so there is no existing behavior to reproduce. Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Mantis proof suggestion Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Security Review findings
Review detailsBest possible solution: Keep the plugin-owned external verification seam only if maintainers approve the public resolver contract, fix the resolved-retry validation gap, rebase, and keep the broader resolver design tracked separately. Do we have a high-confidence way to reproduce the issue? Not applicable; this is a feature/API PR, not a current-main bug report. Current main and v2026.6.11 source lack externalResolution and plugin.approval.resolveVerified, so there is no existing behavior to reproduce. Is this the best way to solve the issue? No, not yet. The implementation is a plausible narrow host seam, but it is not the best merge-ready solution until resolved retries preserve owner validation and maintainers choose this API over the broader resolver design. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against b3b51b0c9185. Label changesLabel justifications:
Evidence reviewedPR surface: Source +1106, Tests +1235, Docs +53, Generated 0, Other +26. Total +2420 across 56 files. View PR surface stats
Security concerns:
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
e3210b7 to
adc699b
Compare
e79f8f4 to
1e7ce2d
Compare
a341cf3 to
8f276f1
Compare
f53379e to
3e0f127
Compare
233585a to
dc3e1c7
Compare
dc3e1c7 to
da8c3cc
Compare
|
Having a discussion right now with some other maintainers about this because we are overhauling approvals and reviews with auto review. Will have more details in a few days |
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
|
quick update: rebased this on latest main and resolved the stale merge conflicts. The refreshed branch preserves the recent approval-runtime hardening from main and keeps this PR scoped to the external verified-resolution path. Local verification:
GitHub CI is running on the refreshed head now. |
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
|
quick keep-alive/status bump: this PR is still active and waiting on maintainer review while the approval/API discussion settles. The branch was refreshed on latest main yesterday, conflicts were resolved, and GitHub CI is green on the current head. No new action needed from my side right now, just keeping this from going stale while review is pending. |
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
2 similar comments
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
|
Updated after the RFC cleanup / Kevin feedback pass. What changed:
Latest local verification:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Follow-up: addressed the two CI failures from the latest push.
Local verification after the fix:
|
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
1 similar comment
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
|
Closing due to inactivity. |
Summary
This is the narrowed AgentKit host API slice after @kevinslin's feedback about avoiding a broad custom-action surface.
externalResolutionfor plugin approvals as a single command-template field, not arbitrary custom action arrays.plugin.approval.resolveVerifiedfor proof-backed allow resolution after external verification succeeds.operator.approvalsas the resolver authority;operator.adminstill satisfies it as the normal operator-scope superset.pluginIdas the pending approval owner selector, not an authenticated plugin principal.This replaces the earlier broader approach from #82431. The chat/status-card follow-up from #82471 is not required for the current AgentKit end-to-end path.
Review context
Design RFC: openclaw/rfcs#15
Tracking issue: #82336
Concrete plugin use case:
AgentKit needs to let a user complete external World verification before a protected OpenClaw tool continues, while keeping OpenClaw core as the approval owner.
Trust boundary
flowchart TB subgraph Core[OpenClaw core] A[Owns pending approval] B[Owns deny and timeout handling] C[Validates approval id] D[Checks pending approval pluginId] E[Checks decision came from externalResolution] F[Resolves approval and resumes tool] end subgraph Plugin[AgentKit plugin] G[Owns World verification] H[Shows QR/link] I[Submits proof-backed allow] end A --> G I --> D D --> E E --> FWhy this shape
The earlier approach exposed arbitrary
actions, which made every approval-capable client understand plugin-defined buttons. That was too broad for this use case.This PR keeps the core approval contract intact and adds only:
externalResolution: one verifier label plus command templates for external verification.plugin.approval.resolveVerified: an ownership-checked resolver so plugin X can resolve only approvals created by plugin X, and only for decisions exposed by that approval'sexternalResolution.Verify once,Verify and trust for session, orDenywithout typing slash commands.It does not add arbitrary approval actions, does not require durable status cards, does not require
chat.inject, and does not add a broad plugin-admin helper.Verification
Latest refresh after rebasing on
origin/main(2720ac06b7) and addressing CI failures:/Users/pabloguardiola/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node scripts/run-vitest.mjs src/plugins/contracts/session-actions.contract.test.ts src/gateway/server-methods/plugin-approval.test.ts src/tui/tui-plugin-approval-selector.test.ts src/tui/tui-event-handlers.test.ts src/tui/tui-command-handlers.test.ts src/tui/components/chat-log.test.tsPATH="/Users/pabloguardiola/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin:/Users/pabloguardiola/.cache/codex-runtimes/codex-primary-runtime/dependencies/bin:$PATH" pnpm check:test-types/Users/pabloguardiola/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node scripts/docs-list.jsgit diff --checkEarlier companion plugin proof against this host API branch:
pnpm test:hitlpnpm test:openclaw-hitlpnpm test:local-full-e2e -- --openclaw ../openclaw --skip-host-build --skip-linkpnpm pack:checkReal behavior proof
Behavior addressed: external HITL plugins can ask OpenClaw to show a narrow verification command for allow decisions while core keeps denial and pending approval ownership.
Real environment tested: local OpenClaw checkout with focused gateway/plugin approval tests, TUI selector/event tests, docs listing, test typecheck, and a companion AgentKit plugin gateway e2e run against this host API.
Exact steps or command run after this patch: ran the latest refresh commands listed above after rebasing on current
origin/main; earlier AgentKit wrapper proof was run against the linked host API branch and is preserved below.Evidence after fix: copied after-fix output from the AgentKit local OpenClaw gateway e2e run:
{ "ok": true, "denyApprovalCommands": [ "/agentkit approve plugin:5f073ff9-7ca7-45ba-bf57-d920909973ea allow-once", "/agentkit approve plugin:5f073ff9-7ca7-45ba-bf57-d920909973ea allow-always", "/approve plugin:5f073ff9-7ca7-45ba-bf57-d920909973ea deny" ], "deniedHookResult": { "blocked": true, "kind": "failure", "deniedReason": "plugin-approval", "reason": "Denied by user", "params": { "cmd": "echo agentkit" } }, "allowApprovalCommands": [ "/agentkit approve plugin:111f0504-d4d8-4ee5-bd80-57bce3e8525a allow-once", "/agentkit approve plugin:111f0504-d4d8-4ee5-bd80-57bce3e8525a allow-always", "/approve plugin:111f0504-d4d8-4ee5-bd80-57bce3e8525a deny" ], "allowedHookResult": { "blocked": false, "params": { "cmd": "echo agentkit-allow" }, "approvalResolution": "allow-once" } }Observed result after fix: plugin approval prompts render external verification as command text/selectable TUI actions, core deny remains available, verified allow resolution is approval-scoped and plugin-owned, and the companion AgentKit plugin can continue the protected call after proof-backed
allow-once.What was not tested: a fresh live World App QR scan from this exact OpenClaw PR head after today's rebase. The companion AgentKit branch includes
docs/manual-tui-e2e.mdfor that full user-facing TUI recording path.