Plugin SDK reference
Plugin SDK events and hook semantics
The typed lifecycle hook registrars and the decision semantics core applies to each hook result. Part of the Plugin SDK overview.
Events and lifecycle
| Method | What it does |
|---|---|
api.on(hookName, handler, opts?) |
Typed lifecycle hook |
api.onConversationBindingResolved(handler) |
Conversation binding callback |
See Plugin hooks for examples, common hook names, and guard semantics.
Hook decision semantics
before_install is a plugin-runtime lifecycle hook, not the operator install
policy surface. Use security.installPolicy when an allow/warn/block decision must
cover CLI and Gateway-backed install or update paths.
before_tool_call: returning{ block: true }is terminal. Once any handler sets it, lower-priority handlers are skipped.before_tool_call: returning{ block: false }is treated as no decision (same as omittingblock), not as an override.before_install: returning{ block: true }is terminal. Once any handler sets it, lower-priority handlers are skipped.before_install: returning{ block: false }is treated as no decision (same as omittingblock), not as an override.reply_dispatch: returning{ handled: true, ... }is terminal. Once any handler claims dispatch, lower-priority handlers and the default model dispatch path are skipped.message_sending: returning{ cancel: true }is terminal. Once any handler sets it, lower-priority handlers are skipped.message_sending: returning{ cancel: false }is treated as no decision (same as omittingcancel), not as an override.message_received: use the typedthreadIdfield when you need inbound thread/topic routing. Keepmetadatafor channel-specific extras.message_sending: use typedreplyToId/threadIdrouting fields before falling back to channel-specificmetadata.gateway_start: usectx.config,ctx.workspaceDir, andctx.getCron?.()for gateway-owned startup state instead of relying on internalgateway:startuphooks. Cron may still be loading at this point.cron_reconciled: rebuild a full external cron projection after startup or scheduler reload. It includesreasonand the effectiveenabledstate, includingenabled: false, whilectx.getCron?.()returns the exact reconciled scheduler. Passctx.abortSignalinto durable projection work; it aborts when that scheduler snapshot is superseded or the Gateway closes.cron_changed: observe gateway-owned cron lifecycle changes.scheduledandremovedevents are post-commit reconciliation hints, not an ordered delta log. A scheduled event'sevent.nextRunAtMsis absent when the job has no next wake; a removed event still carries the deleted job snapshot.
External wake schedulers should debounce or coalesce cron_changed events,
then reread the full durable view from the scheduler last captured by
cron_reconciled. Do not adopt the scheduler from a cron_changed context: a
detached hint from an older scheduler can overlap a later reload.
Use cron_reconciled as the full-snapshot trigger for durable state loaded at
Gateway startup or scheduler replacement. It is not replayed for a plugin-only
hot reload. Observation handlers run in parallel, and fire-and-forget
dispatches can overlap, so consumers must not depend on event completion order.
Keep OpenClaw as the source of truth for due checks and execution.
For a single-flight adapter with durable replacement, retry/backoff, and clean shutdown, see Safe external cron projection.