Background work namespaces
api.runtime.hooks
api.runtime.hooks
Dispatch isolated agent turns for untrusted external-content triggers, such
as an email watcher. Unlike
api.runtime.subagent.run(...), hook dispatch
wraps external content, serializes runs for the same session, and reports
completion through the Gateway. Plugin turns share the cron execution
budget without requiring the HTTP hooks endpoint. When HTTP hooks are
enabled, one slot in that shared budget remains reserved for HTTP work.agentId is required, and sessionKey must begin with hook: and contain
no whitespace or control characters. externalContentSource currently
accepts only "email"; external-content wrapping cannot be disabled. Set
deliver to false to record completion without announcing it. Successful
admission returns { ok: true, runId }; rejected admission returns
{ ok: false, reason }.This capability is available only to bundled plugins and trusted official
plugin installations. It does not require enabling or configuring the HTTP
hooks endpoint.api.runtime.subagent
api.runtime.subagent
Launch and manage background subagent runs.For a tool-free completion that needs no retained session or reply delivery,
use Gateway-backed runs return the canonical accepted
complete(...):agentId and message are required. extraSystemPrompt, model,
timeoutMs, and signal are optional. The selected agent supplies its
configured default model and credential owner when model is omitted.
The result is { text: string }; no session creation, message polling,
deletion, or completion delivery is needed. The configured runtime must
support fresh, tool-free isolated inference; unsupported runtimes fail
before inference.Completions use the shared background queue,
with up to three runs per plugin within the three-run total budget.
Cancellation removes queued work immediately. Running work keeps its slot
until underlying runtime cleanup finishes, then rejects; late output is not
returned after cancellation, timeout, or runtime retirement. Calls require
a live Gateway binding and plugin identity. Request-scoped calls retain the
caller’s operator scopes and agent access; completions started inside an
operator tool invocation are cancelled when that invocation ends.
Model overrides retain the
existing subagent authorization and allowedModels policy below.Use run(...) when you need a session or an agent tool surface:sessionKey alongside runId. The field is optional in the TypeScript result only so explicit custom runtimes remain compatible.waitForRun(...) returns the canonical Gateway wait result. status is "ok", "error", "timeout", or "pending"; pending is a normal nonterminal observation, not an exception. Optional error, startedAt, endedAt, stopReason, livenessState, yielded, pendingError, timeoutPhase, providerStarted, and terminalReply metadata is preserved so callers can distinguish observation timeouts from terminal outcomes. timeoutMs bounds the wait call; it does not cancel the run.plugins.entries.<id>.subagent.allowedModels can restrict overrides to
canonical provider/model targets. The same policy applies to complete;
request-scoped calls retain their authenticated client’s override authority.
The check uses the destination agent’s model configuration, including exact
configured model IDs, and applies to the plugin’s initial override. Configured
defaults, operator-installed model routing hooks, and automatic model fallbacks
retain their own selection policies.toolsAlsoAllow adds exact, uniquely owned tools registered by the calling plugin to the worker’s normal tool surface. The runtime rejects core tools and names shared with another plugin. Profiles and operator tool policies still apply, including explicit allowlists and denies.promptMode: "minimal" selects the bounded subagent prompt instead of the full conversation prompt. The plugin runtime exposes only this mode; omission keeps the full prompt. Use disableTools: true as well when the run must have an exact empty tool surface.completionDelivery: "current-requester" is default-off and is only available while a before_dispatch hook is handling an authenticated inbound request. OpenClaw captures the canonical requester session and delivery route before invoking the plugin, then delivers the subagent completion through the normal announce path. Plugins cannot provide or override requester lineage or destination fields. Calls outside that requester-bound hook context are rejected.deleteSession(...) can delete sessions created by the same plugin through api.runtime.subagent.run(...). Deleting arbitrary user or operator sessions still requires an admin-scoped Gateway request.api.runtime.tasks
api.runtime.tasks
Prefer The async
api.runtime.tasks.async for reads, managed-flow state changes, and child-task linkage. It has runs, flows, and
managedFlows namespaces with the same synchronous bindSession(...) and
fromToolContext(...) factories. Await get, list, findLatest, and
resolve; both flow namespaces also provide awaited getTaskSummary.managedFlows binding also provides createManaged,
tryCreateManaged, setWaiting, resume, finish, fail, and
requestCancel, plus runTask for linking existing work. Await creation before starting work and await each mutation
before reporting its result. Updates check the owner, managed mode, and
expected revision together inside the worker’s SQLite transaction.On an ordinary persistence rejection, createManaged throws
TaskFlow persistence failed. with the original error in cause;
tryCreateManaged returns null. Both methods propagate preparation and
input-validation errors.Results preserve the corresponding synchronous payloads and owner scope.
Reads query persisted SQLite records in the shared database worker, without
overwriting the process registry. Cold restoration also reads through the
worker and installs a complete snapshot before exposing the registry. Concurrent
callers share restoration, and newer synchronous writes take precedence over
delayed snapshots. Access checks for bare owner keys await any required
runtime configuration, plugin metadata, and consent preparation.
Committed writes reconcile the relevant process task and flow registries before
publication; a failed reconciliation leaves that projection dirty without
changing the durable write result. A result describes its operation snapshot
and may be superseded by a later mutation.The shared worker uses the canonical database opener and preserves classified
schema and ownership errors. Closing the shared database waits for in-flight
worker results and native cleanup before releasing its connection ownership.Lists sort newest first. Equal task timestamps sort by task ID descending;
equal flow timestamps sort by flow ID ascending. Run-ID lookup retains its
runtime preference and oldest-first selection, then uses task ID ascending
for ties. When an ACP run ID is reused, lookup excludes superseded backing
generations before applying that ordering. Backing details stay internal and
are not included in task views. Legacy synchronous methods
keep their existing insertion-order tie behavior.The synchronous read methods and corresponding managed-flow state mutations
remain supported but are deprecated in
favor of this opt-in surface. Their removal requires a supported external
plugin migration and an explicitly approved Plugin SDK major release.
Native cancellation continues through the existing namespaces. An awaited read
does not authorize a later write: retain revision checks. Async runTask
rereads the canonical flow and backing inside its write admission and refuses
a new active link when that backing has already completed. Existing terminal
projections can still receive metadata updates without restarting work.
For duplicate reuse, async runTask uses persisted creation-time/task-ID
order, then the existing ACP preference. Deprecated sync runTask preserves
its insertion-order tie behavior.
A worker error with code outcome-unknown can follow a committed write.
The code may appear directly, in a cause, or in AggregateError.errors when
cleanup also fails. Creation and child linkage propagate these errors; reread
current state before deciding whether to retry that operation.Bind Task Flow and Task Run state to a trusted, existing OpenClaw owner session.managedFlowscreates and mutates managed flow records. Bind withfromToolContext(ctx)orbindSession({ sessionKey, requesterOrigin })using host-resolved context, never raw user input.flowsandrunsprovide owner-scoped DTO lookups (get,list,findLatest,resolve).flowsalso exposesgetTaskSummary;runs.cancelcancels an existing task.managedFlows.get(flowId)returns the record with its revision. The read-onlyflowsDTO is not the revision-bearing mutation record.
api or register a plugin. For operator/agent
workflows, use managed Lobster execution.
The following contract is for actual plugin/controller code.Launching and linking a childrunTask records a link to existing work; it never launches ACP/subagent
execution. The backing task must already exist with the same owner,
canonical run/session identities and task runtime. Arbitrary IDs or a
status: "running" declaration cannot establish that authority.- Create a managed flow bound to the real requester session. Handle creation failure before launching work. Binding state access does not grant subagent requester authority.
- Inside an active requester-bound
before_dispatchhook for an authenticated inbound request, callapi.runtime.subagent.runwith a unique agent-qualified child session key, the task message andcompletionDelivery: "current-requester". The Gateway captures the requester and delivery route; retain the returned canonicalrunIdandsessionKey. Missing identities or a rejected launch are failures, not permission to fabricate a task. Ordinary runs withoutcurrent-requesterhavenot_applicablecompletion delivery and lack the mirrored backing needed for this link. - Resolve the canonical task with the owner-bound
await runs.resolve(runId). Verify its owner, run id, child session key and task runtime. Use its actualsourceId, queued/running status and available timing facts inawait managedFlows.runTask(...), alongside the managed flow id and task description. Do not confuse the launch result’s harness/provider metadata with the task DTO’sruntime. The async link checks current backing again before writing; checkcreatedbefore proceeding. Controllers retaining the deprecated sync API must keep their final backing read/check and link synchronous, with no interveningawait. - Observe completion through
subagent.waitForRunand the canonical task. A bounded wait returningpendingor an observation timeout is not a terminal child failure and does not cancel the run. Interpret results only after actual completion. On failure, record a failed/blocked flow outcome and report it; never insert a replacement child declaration to hide launch/link refusal. - Reload the managed record after awaited work. Stop for terminal state or cancellation intent; use the latest revision for the next state transition. Check every
appliedresult, includingfinish/fail, and checkcancelledfor cancellation. On revision conflict, reread and reconcile rather than blindly retrying side effects.
completionDelivery: "current-requester" is available only within the
genuine hook invocation. Do not retain that authority after the hook ends
or call private requester-context/registry helpers. See api.runtime.subagent
above for the public launch and wait contract. ACP linkage likewise requires
an existing owner-backed ACP launch, not a standalone runTask declaration.State without a childFor inline work, await createManaged on the async managed-flow binding,
then await and check setWaiting, resume, finish or fail transitions
as appropriate; no runTask is needed.
Keep stateJson and waitJson bounded. Waiting metadata records the reason
and correlation, but the controller must register the real event listener.Records persist in SQLite; arbitrary JavaScript is not replayed after
restart. Reload with the same trusted owner binding and explicitly resume
from current state. Task Flow is not a scheduler: use Automations or
api.session.workflow.scheduleSessionTurn(...) for future wakeups. See
Task Flow for durability and cancellation.Harness task execution ownership
createAgentHarnessTaskRuntime(...) from
openclaw/plugin-sdk/agent-harness-task-runtime accepts an optional
executionPid for the local process that executes the harness’s tasks. The SDK
captures its host and process start identity once when creating the scoped
runtime. Task records keep that identity so a successor Gateway can settle
running tasks whose recorded process is verifiably gone, without waiting for
their normal reconciliation grace period.
Pass only a local PID reported by the harness transport. Codex’s stdio transport
provides one; its WebSocket and Unix-socket transports do not. Omit executionPid
for remote or unidentified owners, including Copilot, whose SDK does not expose
its process identity. The SDK never substitutes the Gateway PID. Records without
an identity retain the existing grace period, including records written before
execution ownership was available.
Bundled harnesses delivering a completion can pass
isSourceSessionAdmissionAllowed to deliverAgentHarnessTaskCompletion(...).
Keep this callback bound to the current parent and task ownership. The delivery
owner rechecks it after asynchronous routing and immediately before a new Gateway
turn or message injection is accepted. Work already accepted keeps its own
lifecycle and can finish after the source retires. Use signal when the caller
also intends to cancel accepted work.