[plugin sdk] Add generic plugin host-hook contracts (#72287)

Merged via squash.

Prepared head SHA: 68e5f2ce19
Co-authored-by: 100yenadmin <239388517+100yenadmin@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
EVA
2026-04-28 07:07:02 +07:00
committed by GitHub
parent ef1e97472f
commit 1adaa28dc8
96 changed files with 7248 additions and 283 deletions

View File

@@ -66,11 +66,13 @@ observation-only.
**Agent turn**
- `before_model_resolve` — override provider or model before session messages load
- `agent_turn_prepare` — consume queued plugin turn injections and add same-turn context before prompt hooks
- `before_prompt_build` — add dynamic context or system-prompt text before the model call
- `before_agent_start` — compatibility-only combined phase; prefer the two hooks above
- **`before_agent_reply`** — short-circuit the model turn with a synthetic reply or silence
- **`before_agent_finalize`** — inspect the natural final answer and request one more model pass
- `agent_end` — observe final messages, success state, and run duration
- `heartbeat_prompt_contribution` — add heartbeat-only context for background monitor and lifecycle plugins
**Conversation observation**
@@ -153,6 +155,13 @@ Rules:
- `onResolution` receives the resolved approval decision — `allow-once`,
`allow-always`, `deny`, `timeout`, or `cancelled`.
Bundled plugins that need host-level policy can register trusted tool policies
with `api.registerTrustedToolPolicy(...)`. These run before ordinary
`before_tool_call` hooks and before external plugin decisions. Use them only
for host-trusted gates such as workspace policy, budget enforcement, or
reserved workflow safety. External plugins should use normal `before_tool_call`
hooks.
### Tool result persistence
Tool results can include structured `details` for UI rendering, diagnostics,
@@ -174,9 +183,15 @@ Use the phase-specific hooks for new plugins:
- `before_model_resolve`: receives only the current prompt and attachment
metadata. Return `providerOverride` or `modelOverride`.
- `agent_turn_prepare`: receives the current prompt, prepared session messages,
and any exactly-once queued injections drained for this session. Return
`prependContext` or `appendContext`.
- `before_prompt_build`: receives the current prompt and session messages.
Return `prependContext`, `systemPrompt`, `prependSystemContext`, or
`appendSystemContext`.
Return `prependContext`, `appendContext`, `systemPrompt`,
`prependSystemContext`, or `appendSystemContext`.
- `heartbeat_prompt_contribution`: runs only for heartbeat turns and returns
`prependContext` or `appendContext`. It is intended for background monitors
that need to summarize current state without changing user-initiated turns.
`before_agent_start` remains for compatibility. Prefer the explicit hooks above
so your plugin does not depend on a legacy combined phase.
@@ -219,8 +234,31 @@ Non-bundled plugins that need `llm_input`, `llm_output`,
}
```
Prompt-mutating hooks can be disabled per plugin with
`plugins.entries.<id>.hooks.allowPromptInjection=false`.
Prompt-mutating hooks and durable next-turn injections can be disabled per plugin
with `plugins.entries.<id>.hooks.allowPromptInjection=false`.
### Session extensions and next-turn injections
Workflow plugins can persist small JSON-compatible session state with
`api.registerSessionExtension(...)` and update it through the Gateway
`sessions.pluginPatch` method. Session rows project registered extension state
through `pluginExtensions`, letting Control UI and other clients render
plugin-owned status without learning plugin internals.
Use `api.enqueueNextTurnInjection(...)` when a plugin needs durable context to
reach the next model turn exactly once. OpenClaw drains queued injections before
prompt hooks, drops expired injections, and deduplicates by `idempotencyKey`
per plugin. This is the right seam for approval resumes, policy summaries,
background monitor deltas, and command continuations that should be visible to
the model on the next turn but should not become permanent system prompt text.
Cleanup semantics are part of the contract. Session extension cleanup and
runtime lifecycle cleanup callbacks receive `reset`, `delete`, `disable`, or
`restart`. The host removes the owning plugin's persistent session extension
state and pending next-turn injections for reset/delete/disable; restart keeps
durable session state while cleanup callbacks let plugins release scheduler
jobs, run context, and other out-of-band resources for the old runtime
generation.
## Message hooks

View File

@@ -113,6 +113,49 @@ provider- or plugin-specific policy to core prompt builders.
| `api.registerMemoryPromptSupplement(builder)` | Additive memory-adjacent prompt section |
| `api.registerMemoryCorpusSupplement(adapter)` | Additive memory search/read corpus |
### Host hooks for workflow plugins
Host hooks are the SDK seams for plugins that need to participate in the host
lifecycle rather than only adding a provider, channel, or tool. They are
generic contracts; Plan Mode can use them, but so can approval workflows,
workspace policy gates, background monitors, setup wizards, and UI companion
plugins.
| Method | Contract it owns |
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| `api.registerSessionExtension(...)` | Plugin-owned, JSON-compatible session state projected through Gateway sessions |
| `api.enqueueNextTurnInjection(...)` | Durable exactly-once context injected into the next agent turn for one session |
| `api.registerTrustedToolPolicy(...)` | Bundled/trusted pre-plugin tool policy that can block or rewrite tool params |
| `api.registerToolMetadata(...)` | Tool catalog display metadata without changing the tool implementation |
| `api.registerCommand(...)` | Scoped plugin commands; command results can set `continueAgent: true` |
| `api.registerControlUiDescriptor(...)` | Control UI contribution descriptors for session, tool, run, or settings surfaces |
| `api.registerRuntimeLifecycle(...)` | Cleanup callbacks for plugin-owned runtime resources on reset/delete/reload paths |
| `api.registerAgentEventSubscription(...)` | Sanitized event subscriptions for workflow state and monitors |
| `api.setRunContext(...)` / `getRunContext(...)` / `clearRunContext(...)` | Per-run plugin scratch state cleared on terminal run lifecycle |
| `api.registerSessionSchedulerJob(...)` | Plugin-owned session scheduler job records with deterministic cleanup |
The contracts intentionally split authority:
- External plugins can own session extensions, UI descriptors, commands, tool
metadata, next-turn injections, and normal hooks.
- Trusted tool policies run before ordinary `before_tool_call` hooks and are
bundled-only because they participate in host safety policy.
- Reserved command ownership is bundled-only. External plugins should use their
own command names or aliases.
- `allowPromptInjection=false` disables prompt-mutating hooks including
`agent_turn_prepare`, `before_prompt_build`, `heartbeat_prompt_contribution`,
prompt fields from legacy `before_agent_start`, and
`enqueueNextTurnInjection`.
Examples of non-Plan consumers:
| Plugin archetype | Hooks used |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Approval workflow | Session extension, command continuation, next-turn injection, UI descriptor |
| Budget/workspace policy gate | Trusted tool policy, tool metadata, session projection |
| Background lifecycle monitor | Runtime lifecycle cleanup, agent event subscription, session scheduler ownership/cleanup, heartbeat prompt contribution, UI descriptor |
| Setup or onboarding wizard | Session extension, scoped commands, Control UI descriptor |
<Note>
Reserved core admin namespaces (`config.*`, `exec.approvals.*`, `wizard.*`,
`update.*`) always stay `operator.admin`, even if a plugin tries to assign a