openclaw/extensions
Josh Avant 6ade9c474c
feat(hooks): add async requireApproval to before_tool_call (#55339)
* Plugins: add native ask dialog for before_tool_call hooks

Extend the before_tool_call plugin hook with a requireApproval return field
that pauses agent execution and waits for real user approval via channels
(Telegram, Discord, /approve command) instead of relying on the agent to
cooperate with a soft block.

- Add requireApproval field to PluginHookBeforeToolCallResult with id, title,
  description, severity, timeout, and timeoutBehavior options
- Extend runModifyingHook merge callback to receive hook registration so
  mergers can stamp pluginId; always invoke merger even for the first result
- Make ExecApprovalManager generic so it can be reused for plugin approvals
- Add plugin.approval.request/waitDecision/resolve gateway methods with
  schemas, scope guards, and broadcast events
- Handle requireApproval in pi-tools via two-phase gateway RPC with fallback
  to soft block when the gateway is unavailable
- Extend the exec approval forwarder with plugin approval message builders
  and forwarding methods
- Update /approve command to fall back to plugin.approval.resolve when exec
  approval lookup fails
- Document before_tool_call requireApproval in hooks docs and unified
  /approve behavior in exec-approvals docs

* Plugins: simplify plugin approval code

- Extract mergeParamsWithApprovalOverrides helper to deduplicate param
  merge logic in before_tool_call hook handling
- Use idiomatic conditional spread syntax in toolContext construction
- Extract callApprovalMethod helper in /approve command to eliminate
  duplicated callGateway calls
- Simplify plugin approval schema by removing unnecessary Type.Union
  with Type.Null on optional fields
- Extract normalizeTrimmedString helper for turn source field trimming

* Tests: add plugin approval wiring and /approve fallback coverage

Fix 3 broken assertions expecting old "Exec approval" message text.
Add tests for the /approve command's exec→plugin fallback path,
plugin approval method registration and scope authorization, and
handler factory key verification.

* UI: wire plugin approval events into the exec approval overlay

Handle plugin.approval.requested and plugin.approval.resolved gateway
events by extending the existing exec approval queue with a kind
discriminator. Plugin approvals reuse the same overlay, queue management,
and expiry timer, with branched rendering for plugin-specific content
(title, description, severity). The decision handler routes resolve calls
to the correct gateway method based on kind.

* fix: read plugin approval fields from nested request payload

The gateway broadcasts plugin approval payloads with title, description,
severity, pluginId, agentId, and sessionKey nested inside the request
object (PluginApprovalRequestPayload), not at the top level. Fix the
parser to read from the correct location so the overlay actually appears.

* feat: invoke plugin onResolution callback after approval decision

Adds onResolution to the requireApproval type and invokes it after
the user resolves the approval dialog, enabling plugins to react to
allow-always vs allow-once decisions.

* docs: add onResolution callback to requireApproval hook documentation

* test: fix /approve assertion for unified approval response text

* docs: regenerate plugin SDK API baseline

* docs: add changelog entry for plugin approval hooks

* fix: harden plugin approval hook reliability

- Add APPROVAL_NOT_FOUND error code so /approve fallback uses structured
  matching instead of fragile string comparison
- Check block before requireApproval so higher-priority plugin blocks
  cannot be overridden by a lower-priority approval
- Race waitDecision against abort signal so users are not stuck waiting
  for the full approval timeout after cancelling a run
- Use null consistently for missing pluginDescription instead of
  converting to undefined
- Add comments explaining the +10s timeout buffer on gateway RPCs

* docs: document block > requireApproval precedence in hooks

* fix: address Phase 1 critical correctness issues for plugin approval hooks

- Fix timeout-allow param bug: return merged hook params instead of
  original params when timeoutBehavior is "allow", preventing security
  plugins from having their parameter rewrites silently discarded.

- Host-generate approval IDs: remove plugin-provided id field from the
  requireApproval type, gateway request, and protocol schema. Server
  always generates IDs via randomUUID() to prevent forged/predictable
  ID attacks.

- Define onResolution semantics: add PluginApprovalResolutions constants
  and PluginApprovalResolution type. onResolution callback now fires on
  every exit path (allow, deny, timeout, abort, gateway error, no-ID).
  Decision branching uses constants instead of hard-coded strings.

- Fix pre-existing test infrastructure issues: bypass CJS mock cache for
  getGlobalHookRunner global singleton, reset gateway mock between tests,
  fix hook merger priority ordering in block+requireApproval test.

* fix: tighten plugin approval schema and add kind-prefixed IDs

Harden the plugin approval request schema: restrict severity to
enum (info|warning|critical), cap timeoutMs at 600s, limit title
to 80 chars and description to 256 chars. Prefix plugin approval
IDs with `plugin:` so /approve routing can distinguish them from
exec approvals deterministically instead of relying on fallback.

* fix: address remaining PR feedback (Phases 1-3 source changes)

* chore: regenerate baselines and protocol artifacts

* fix: exclude requesting connection from approval-client availability check

hasExecApprovalClients() counted the backend connection that issued
the plugin.approval.request RPC as an approval client, preventing
the no-approval-route fast path from firing in headless setups and
causing 120s stalls. Pass the caller's connId so it is skipped.
Applied to both plugin and exec approval handlers.

* Approvals: complete Discord parity and compatibility fallback

* Hooks: make plugin approval onResolution non-blocking

* Hooks: freeze params after approval owner is selected

* Gateway: harden plugin approval request/decision flow

* Discord/Telegram: fix plugin approval delivery parity

* Approvals: fix Telegram plugin approval edge cases

* Auto-reply: enforce Telegram plugin approval approvers

* Approvals: harden Telegram and plugin resolve policies

* Agents: static-import gateway approval call and fix e2e mock loading

* Auto-reply: restore /approve Telegram import boundary

* Approvals: fail closed on no-route and neutralize Discord mentions

* docs: refresh generated config and plugin API baselines

---------

Co-authored-by: Václav Belák <vaclav.belak@gendigital.com>
2026-03-27 09:06:40 -07:00
..
acpx refactor(plugin-sdk): expose zod subpath 2026-03-27 09:55:47 +05:30
amazon-bedrock chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
anthropic chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
anthropic-vertex feat: add anthropic-vertex provider for Claude via GCP Vertex AI (#43356) 2026-03-20 18:48:42 -04:00
bluebubbles fix(ci): restore boundary and test seams 2026-03-27 15:08:33 +00:00
brave test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
browser refactor: route plugin sdk through extension barrels 2026-03-27 13:46:16 +00:00
byteplus chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
chutes chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
cloudflare-ai-gateway chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
copilot-proxy chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
deepgram test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
deepseek chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
device-pair test: collapse provider plugin suites 2026-03-25 04:25:02 +00:00
diagnostics-otel build: refresh deps and vitest cache lanes 2026-03-27 02:26:07 +00:00
diffs refactor(plugin-sdk): expose zod subpath 2026-03-27 09:55:47 +05:30
discord feat(hooks): add async requireApproval to before_tool_call (#55339) 2026-03-27 09:06:40 -07:00
duckduckgo refactor: shortcut bundled provider contract fixtures 2026-03-27 04:44:43 +00:00
elevenlabs test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
exa refactor: shortcut bundled provider contract fixtures 2026-03-27 04:44:43 +00:00
fal chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
feishu fix(ci): restore plugin runtime boundaries 2026-03-27 14:38:40 +00:00
firecrawl refactor: shortcut bundled provider contract fixtures 2026-03-27 04:44:43 +00:00
github-copilot chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
google fix(ci): route extension tests through public test bridges 2026-03-27 15:20:01 +00:00
googlechat refactor: migrate more boundary parsing to zod 2026-03-27 05:07:50 +00:00
groq chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
huggingface chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
imessage fix(ci): restore runtime-api guardrails 2026-03-27 15:56:54 +00:00
irc refactor(plugin-sdk): expose zod subpath 2026-03-27 09:55:47 +05:30
kilocode fix(ci): route extension tests through public test bridges 2026-03-27 15:20:01 +00:00
kimi-coding chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
line refactor: remove onboarding gateway compat shims 2026-03-27 13:46:16 +00:00
litellm test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
llm-task chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
lobster chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
matrix fix(ci): restore runtime-api guardrails 2026-03-27 15:56:54 +00:00
mattermost refactor: expose dm policy test seams 2026-03-27 13:46:17 +00:00
memory-core style: normalize test and sdk formatting 2026-03-27 15:15:04 +00:00
memory-lancedb build: refresh deps and vitest cache lanes 2026-03-27 02:26:07 +00:00
microsoft test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
microsoft-foundry chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
minimax test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
mistral test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
modelstudio chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
moonshot fix(ci): route extension tests through public test bridges 2026-03-27 15:20:01 +00:00
msteams fix(ci): restore plugin runtime boundaries 2026-03-27 14:38:40 +00:00
nextcloud-talk fix(ci): restore runtime-api guardrails 2026-03-27 15:56:54 +00:00
nostr refactor: migrate more boundary parsing to zod 2026-03-27 05:07:50 +00:00
nvidia chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
ollama test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
open-prose chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
openai test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
opencode test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
opencode-go test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
openrouter test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
openshell test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
perplexity fix(ci): route extension tests through public test bridges 2026-03-27 15:20:01 +00:00
phone-control refactor: dedupe test and runtime seams 2026-03-24 23:33:30 +00:00
qianfan chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
sglang chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
shared refactor: share matrix and telegram dedupe helpers 2026-03-26 15:08:45 +00:00
signal test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
slack fix(ci): restore runtime-api guardrails 2026-03-27 15:56:54 +00:00
synology-chat refactor: migrate more boundary parsing to zod 2026-03-27 05:07:50 +00:00
synthetic test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
talk-voice test: dedupe extension channel fixtures 2026-03-26 19:47:27 +00:00
tavily refactor: shortcut bundled provider contract fixtures 2026-03-27 04:44:43 +00:00
telegram feat(hooks): add async requireApproval to before_tool_call (#55339) 2026-03-27 09:06:40 -07:00
thread-ownership fix(extensions): route fetch calls through fetchWithSsrFGuard (#53929) 2026-03-26 02:04:54 -07:00
tlon refactor(plugin-sdk): expose zod subpath 2026-03-27 09:55:47 +05:30
together chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
twitch refactor(plugin-sdk): expose zod subpath 2026-03-27 09:55:47 +05:30
venice chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
vercel-ai-gateway chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
vllm refactor: move provider runtime into extensions 2026-03-27 05:38:58 +00:00
voice-call refactor: add runtime-boundary plugin test seams 2026-03-27 13:46:17 +00:00
volcengine chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
whatsapp refactor: shrink remaining test seam reach-ins 2026-03-27 13:46:17 +00:00
xai fix(ci): route extension tests through public test bridges 2026-03-27 15:20:01 +00:00
xiaomi test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
zai test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
zalo refactor: route outbound payload tests through extension test seams 2026-03-27 13:46:17 +00:00
zalouser refactor: route zalouser payload mocks through test api 2026-03-27 13:46:17 +00:00
.npmignore