OpenClaw tasks engineering guide
Overview
The OpenClaw task subsystem is the shared background-run ledger for detached work. It records ACP sessions, native subagents, scheduled-job executions, gateway-backed CLI runs, and async media jobs. It persists task state, exposes operator and client views, delivers completion notifications, supports best-effort cancellation, and runs maintenance to reconcile stale records. It does not schedule or execute the underlying work.
Mental model
producer runtime
-> detached-task-runtime facade
-> task-executor orchestration
-> task-registry singleton
-> SQLite store + in-memory indexes
-> CLI / Gateway RPC / chat / status / plugin runtime views
gateway startup
-> task-registry maintenance sweeper
-> runtime-aware reconciliation, cleanup stamping, pruning
Think of tasks as durable records with lifecycle transitions. The owner runtime remains responsible for doing the work. Task Flow sits one level above tasks when background work needs parent flow state, multi-step control, or mirrored flow status.
Repository layout
| Path | Use it for |
|---|---|
src/tasks/task-registry.ts | Core task state, indexes, lifecycle updates, lookups, delivery, cancellation, summaries, and restore. |
src/tasks/task-registry.types.ts | Canonical task record, runtime, status, delivery, notify, event, and summary types. |
src/tasks/task-executor.ts | Producer-facing orchestration: create queued/running tasks, finalize by run id, retry blocked flows, cancel tasks/flows. |
src/tasks/detached-task-runtime*.ts | Facade and optional runtime registration contract for detached task lifecycle ownership. |
src/tasks/task-registry.store*.ts | Store abstraction and SQLite-backed persistence for task records and delivery state. |
src/tasks/task-registry.maintenance.ts | Runtime-aware reconciliation, lost detection, cron recovery, cleanup stamping, pruning, and sweeper timer. |
src/tasks/task-registry.audit.ts | Health findings for stale, lost, delivery-failed, missing-cleanup, and inconsistent records. |
src/tasks/task-flow-*.ts | Task Flow registry, store, maintenance, audit, owner access, and internal runtime exports. |
src/tasks/task-status.ts | Cleanup-aware task snapshots and sanitized user-visible status formatting. |
src/commands/tasks.ts, src/commands/flows.ts | CLI implementations for task and flow operator commands. |
src/gateway/server-methods/tasks.ts | Gateway protocol handlers for task list/get/cancel. |
src/auto-reply/reply/commands-tasks.ts | Chat-native /tasks board. |
Runtime architecture
The registry is a module-level singleton backed by SQLite and in-memory indexes. ensureTaskRegistryReady() restores from the store once and subscribes to agent lifecycle events. Writes update the in-memory record, persist through TaskRegistryStore, sync parent flow state, and emit best-effort observer events.
Gateway startup imports task-registry.maintenance.ts, configures the cron store path, marks the gateway as authoritative for cron active jobs, and starts a deferred sweep plus a 60-second interval sweep. Minimal test gateways skip this runtime loop.
Runtime-heavy dependencies are behind narrow lazy modules. Delivery uses task-registry-delivery-runtime.ts to reach outbound messaging. Cancellation uses task-registry-control.runtime.ts to reach ACP session control and subagent control.
Major subsystems
Task registry
- Responsibility: Own task records, indexes, lifecycle writes, delivery state, lookups, summaries, and cancellation fallback.
- Key files/symbols:
task-registry.ts,createTaskRecord,updateTaskStateByRunId,maybeDeliverTaskTerminalUpdate,cancelTaskById,listTaskRecords. - External interface: Re-exported through
runtime-internal.tsfor internal consumers; higher-level callers should preferdetached-task-runtime.tsortask-executor.ts. - Collaborators: task store, task flow registry, agent events, outbound message delivery, system events, heartbeat wake, ACP/subagent control.
- State:
tasks,taskDeliveryStates, indexes by run id, owner key, parent flow id, and related session key, plus a pending-delivery guard set. - Tests:
src/tasks/task-registry.test.ts,src/tasks/task-registry.store.test.ts. - Extension pattern: Add new lifecycle metadata to
TaskRecord, SQLite row mapping, store tests, domain views, CLI/Gateway mappers, and status sanitization as needed.
Detached task runtime facade
- Responsibility: Provide producer-safe lifecycle calls and allow a registered runtime to own lifecycle and cancellation.
- Key files/symbols:
detached-task-runtime.ts,detached-task-runtime-contract.ts,registerDetachedTaskRuntime,createRunningTaskRun,finalizeTaskRunByRunId. - External interface: Producers call this facade to avoid reaching into registry internals.
- Collaborators:
task-executor.ts, optional registered runtime, maintenance recovery hook. - State: Runtime registration state in
detached-task-runtime-state.ts. - Tests:
src/tasks/detached-task-runtime.test.ts. - Extension pattern: Register a runtime only when it genuinely owns execution/cancellation semantics; return
found: falsefor tasks it does not own so core fallback can proceed.
Task executor
- Responsibility: Bridge producer intent to registry writes and Task Flow behavior.
- Key files/symbols:
task-executor.ts,createQueuedTaskRun,createRunningTaskRun,completeTaskRunByRunId,failTaskRunByRunId,cancelDetachedTaskRunById. - External interface: Used by the default detached runtime and some direct internal call sites.
- Collaborators: task registry, task flow registry, owner access, registered detached runtime.
- State: No durable state of its own; writes through registry and flow registry.
- Tests:
src/tasks/task-executor.test.ts,src/tasks/task-executor-policy.test.ts. - Extension pattern: Add orchestration-level behavior here when it must apply across producers, not in individual producer call sites.
Persistence stores
- Responsibility: Persist task and flow snapshots and incremental upserts/deletes.
- Key files/symbols:
task-registry.store.ts,task-registry.store.sqlite.ts,task-flow-registry.store.ts,task-flow-registry.store.sqlite.ts. - External interface: Store interfaces expose load/save/upsert/delete/close and are replaceable in tests.
- Collaborators: path resolvers, SQLite WAL maintenance, delivery-context normalization.
- State:
task_runs,task_delivery_state, andflow_runstables under the OpenClaw state directory. - Tests: store tests and path tests in
src/tasks. - Extension pattern: Keep row-to-record and bind functions symmetric; add migration logic for new columns; update index coverage for common lookup paths.
Maintenance and audit
- Responsibility: Detect stale/lost records, recover cron results, stamp cleanup, prune expired rows, and produce health findings.
- Key files/symbols:
task-registry.maintenance.ts,runTaskRegistryMaintenance,previewTaskRegistryMaintenance,startTaskRegistryMaintenance,listTaskAuditFindings. - External interface: CLI
tasks audit/tasks maintenance, gateway startup sweeper, status health. - Collaborators: ACP session metadata, session stores, cron active jobs, cron store/logs, agent run contexts, task registry, plugin state sweeps.
- State: Maintenance timers and configured runtime dependencies.
- Tests:
task-registry.maintenance.issue-60299.test.ts,task-registry.audit.test.ts,commands/tasks.test.ts. - Extension pattern: Add runtime-aware reconciliation in maintenance, then include operator diagnostics and audit coverage so stale cases are explainable.
Task Flow
- Responsibility: Track flow-level state above one or more tasks, with managed and task-mirrored modes.
- Key files/symbols:
task-flow-registry.ts,TaskFlowRecord,createManagedTaskFlow,syncFlowFromTask,updateFlowRecordByIdExpectedRevision,cancelFlowById. - External interface: CLI
openclaw tasks flow, plugin runtime flow bindings, internal one-task auto-sync. - Collaborators: task executor, task registry, task flow store, flow audit/maintenance.
- State:
flow_runsrows with revision, state JSON, wait JSON, blocking task, and cancel request. - Tests:
task-flow-registry.test.ts,task-flow-registry.audit.test.ts,task-flow-registry.maintenance.test.ts. - Extension pattern: Use managed mode when the flow owns child task scheduling; use mirrored mode when the flow only reflects externally created tasks.
Operator and client surfaces
- Responsibility: Let humans and clients inspect, audit, maintain, and cancel task/flow records.
- Key files/symbols:
commands/tasks.ts,commands/flows.ts,server-methods/tasks.ts,commands-tasks.ts,task-status.ts. - External interface: CLI, Gateway RPC, chat
/tasks, status surfaces, plugin runtime task APIs. - Collaborators: registry, maintenance, audit, flow registry, status snapshot helpers.
- State: Read-only except cancellation, notification policy changes, and maintenance apply.
- Tests: CLI, gateway, chat, and status tests listed above.
- Extension pattern: Keep public mappers sanitized and stable; add schema updates for Gateway changes; add JSON and text-mode CLI tests.
Core workflows
Create a background task
- Trigger: A producer starts work and calls
createQueuedTaskRunorcreateRunningTaskRun. - Path:
detached-task-runtime.ts->task-executor.ts->task-registry.ts:createTaskRecord-> store upsert. - Data: runtime, owner key, requester session, child session, run id, task text, delivery context, notification policy, timing.
- Side effects: task row and delivery row are persisted; indexes update; eligible ACP/subagent records can get a one-task flow.
- Failure behavior: create path throws for invalid owner/flow invariants; producers often catch and log so task tracking does not block the underlying work.
- Tests:
task-registry.test.ts, producer tests, andcommands-tasks.test.ts.
Finalize background work
- Trigger: Producer reports success, failure, timeout, or cancellation by run id.
- Path:
completeTaskRunByRunId/failTaskRunByRunId/finalizeTaskRunByRunId->updateTaskStateByRunId->updateTask->syncFlowFromTask-> optional delivery. - Data: terminal status, ended time, error, progress summary, terminal summary, terminal outcome.
- Side effects: status/timestamps update, cleanup timing is added, flow state mirrors terminal result, delivery may be sent or queued.
- Failure behavior: invalid weaker status updates are ignored; delivery failures are logged and recorded.
- Tests:
task-registry.test.ts,subagent-registry-lifecycletests, gateway task tests.
Deliver a terminal update
- Trigger: A task becomes terminal and policy allows auto-delivery.
- Path:
maybeDeliverTaskTerminalUpdate-> policy checks intask-executor-policy.ts-> direct outbound send or session event queue. - Data: requester origin, owner session, task title, status, terminal summary, idempotency key.
- Side effects: outbound message, mirrored session event, heartbeat wake, delivery status update.
- Failure behavior: direct-send failure logs warning, attempts session fallback, and marks delivery failed.
- Tests: delivery cases in
task-registry.test.ts.
Cancel a task
- Trigger: CLI, Gateway RPC, or plugin runtime requests cancellation.
- Path: operator surface ->
cancelDetachedTaskRunById-> registered runtime if present ->cancelTaskById. - Data: task id, runtime config, optional reason.
- Side effects: ACP session cancellation, subagent kill request, or CLI record cancellation; terminal delivery may run.
- Failure behavior: missing and already-terminal tasks return structured non-cancelled results; unsupported runtimes return a reason.
- Tests:
gateway/server-methods/tasks.test.ts, cancellation cases intask-registry.test.ts.
Run maintenance
- Trigger: Gateway sweeper,
openclaw tasks maintenance, or tests. - Path:
runTaskRegistryMaintenance-> runtime-specific backing checks -> cron recovery -> lost marking -> cleanup stamping -> pruning. - Data: task snapshots, session stores, ACP metadata, cron active jobs, cron run logs, agent run contexts.
- Side effects: terminal recovery, lost marking, cleanupAfter writes, expired row deletion, optional terminal delivery.
- Failure behavior: registered recovery hooks are caught and warned; cleanup failures do not mask task outcome.
- Tests:
task-registry.maintenance.issue-60299.test.ts,commands/tasks.test.ts.
Inspect tasks from chat and clients
- Trigger:
/tasks,tasks.list,tasks.get, oropenclaw tasks list/show. - Path: chat uses
buildTaskStatusSnapshot; Gateway usesmapTaskSummary; CLI uses inspectable reconciliation helpers. - Data: task records filtered by session, agent, status, runtime, cursor, or lookup token.
- Side effects: read-only except CLI inspectable reconciliation can project state for display.
- Failure behavior: invalid Gateway params return protocol errors; missing CLI lookups exit non-zero; chat rejects unauthorized senders.
- Tests:
commands-tasks.test.ts,server-methods/tasks.test.ts,commands/tasks.test.ts.
Coordinate a Task Flow
- Trigger: task executor auto-creates one-task flows or a controller creates a managed flow.
- Path: create/update flow record -> link tasks via
parentFlowId->syncFlowFromTaskor managed flow updates -> CLI/plugin view. - Data: flow id, owner, sync mode, controller id, revision, status, current step, blocking task, JSON state/wait blobs, linked tasks.
- Side effects: flow row persisted, task rows linked, flow status mirrors or controls child tasks.
- Failure behavior: expected-revision conflicts reject updates; cancel intent is sticky; audit flags stale or inconsistent flow state.
- Tests:
task-flow-registry.test.ts,task-flow-registry.audit.test.ts,commands/tasks.test.ts.
Data model and state
TaskRecord is the central unit. It combines identity (taskId, runId, sourceId), ownership (requesterSessionKey, ownerKey, scopeKind, agentId), relationships (childSessionKey, parentFlowId, parentTaskId), lifecycle (status, timestamps, cleanup), delivery (deliveryStatus, notifyPolicy), and user summaries.
TaskFlowRecord adds parent-level orchestration state: syncMode, revision, goal, currentStep, JSON state/wait payloads, blocking task data, and sticky cancellation.
Persistence uses two SQLite registries under the OpenClaw state directory: task runs and task flows. Both stores set restrictive file permissions and maintain WAL sidecars. Store interfaces are injectable for tests.
External interfaces
- CLI:
openclaw tasks,tasks list,show,notify,cancel,audit,maintenance, andtasks flow list/show/cancel. - Gateway RPC:
tasks.list,tasks.get, andtasks.cancelwith schemas insrc/gateway/protocol/schema/tasks.ts. - Chat:
/tasksfor current-session task board and same-agent aggregate fallback. - Status: status text and session-status tooling use cleanup-aware snapshots from
task-status.ts. - Plugin runtime:
createRuntimeTasksexposes owner-bound task and flow accessors. - Producer API:
detached-task-runtime.tslifecycle functions for background producers.
Cross-cutting patterns
- Validation: Gateway protocol handlers validate params before work. Registry creation asserts owner and parent-flow invariants.
- Error handling: lifecycle write errors usually propagate; delivery, observers, cleanup, and optional runtime recovery are best-effort with warnings.
- Logging: use
createSubsystemLoggerwith task-specific subsystem names. - Security: owner-scoped wrappers gate plugin access; task text is sanitized before external display; cancellation requires runtime config.
- Configuration: maintenance receives cron store path and cron runtime authority at gateway startup; task storage path follows
OPENCLAW_STATE_DIR. - Concurrency: module-level maps are synchronous; delivery uses a pending set to suppress duplicate sends; flow updates use revision checks.
- Formatting: terminal output uses theme helpers and simple tables; JSON output is stable for automation.
Testing strategy
Tests are colocated and behavior-oriented. Core task tests are broad, so prefer targeted files while changing a narrow surface.
pnpm test src/tasks/task-registry.test.tsfor lifecycle, delivery, cancellation, and registry behavior.pnpm test src/tasks/task-registry.store.test.tsfor persistence changes.pnpm test src/tasks/task-registry.audit.test.tsandpnpm test src/tasks/task-registry.maintenance.issue-60299.test.tsfor audit/maintenance changes.pnpm test src/tasks/task-flow-registry.test.tsand related flow audit/maintenance tests for Task Flow changes.pnpm test src/commands/tasks.test.tsandpnpm test src/commands/tasks-json.test.tsfor CLI behavior.pnpm test src/gateway/server-methods/tasks.test.tsfor Gateway RPC behavior.pnpm test src/auto-reply/reply/commands-tasks.test.tsfor chat board behavior.
Use repo wrappers such as pnpm test <path>, pnpm check:changed, and pnpm build when changes affect build output, dynamic imports, package surfaces, or gateway startup boundaries.
How to make common changes
Add a new task-producing runtime
- Add the runtime value to
TaskRuntimeonly if it is truly a new owner class rather than a task kind. - Update creation/finalization call sites to use
detached-task-runtime.ts. - Teach maintenance how to prove active backing state and recover or mark lost records.
- Update CLI filters, Gateway mapping if needed, docs, tests, and audit expectations.
Add a field to task records
- Update
TaskRecordand any derived public view types. - Add SQLite column migration, row mapping, and bind mapping.
- Update clone/snapshot and JSON output behavior if needed.
- Add store tests, registry tests, and Gateway/CLI mapper tests when externally visible.
Add a CLI task command
- Wire command syntax in
src/cli/program/register.status-health-sessions.ts. - Implement command behavior in
src/commands/tasks.tsorsrc/commands/flows.ts. - Support
--jsonwhen automation is likely. - Add text and JSON tests under
src/commands.
Add a Gateway task method
- Add or extend schemas in
src/gateway/protocol/schema/tasks.ts. - Implement validated handler behavior in
src/gateway/server-methods/tasks.ts. - Regenerate protocol artifacts if required by the changed schema.
- Add server-method tests and client-facing sanitization checks.
Change Task Flow behavior
- Decide whether the change applies to
task_mirrored,managed, or both. - Keep revision semantics intact for managed flow updates.
- Update flow audit/maintenance if new stale or blocked states are possible.
- Update CLI and plugin-domain views if new fields become operator-visible.
Operational concerns
- Startup: task maintenance starts during gateway early runtime startup unless running a minimal test gateway.
- Storage: task and flow registries are local SQLite files under the OpenClaw state directory. WAL maintenance is configured in the store modules.
- Cleanup: terminal task records receive
cleanupAfterand are pruned by maintenance. Lost tasks warn during retention and error after expiry. - Packaging: task cancellation depends on control runtime files being included in published builds; past changelog entries call out this packaging seam.
- Performance: maintenance avoids repeated broad session-store reads; issue-specific tests cover pass-local lookup reuse.
- Troubleshooting: use
openclaw tasks audit --jsonandopenclaw tasks maintenance --jsonfor diagnostic reasons before applying changes.
Known risks and open questions
- Registry singleton state requires disciplined test resets and can make hidden coupling easy.
- Producer metadata quality is critical for maintenance; incorrect
runId,childSessionKey, orownerKeycan break lookup, cancellation, or lost recovery. - Delivery failure retry policy appears intentionally audit-driven, not automatic; confirm before adding retries.
- Notification docs should be kept aligned with policy-gated delivery behavior.
- Task Flow is powerful but has two sync modes; maintainers should avoid blending managed ownership with mirrored observation in the same flow.
- Plugin runtime task controls are owner-scoped; do not bypass those wrappers from plugin-facing code.
- The fixed retention window may not satisfy operators who need longer audit history; this is a product decision, not just a code change.
- SQLite runtime support is assumed by default persistence; environment issues can block task storage.