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

PathUse it for
src/tasks/task-registry.tsCore task state, indexes, lifecycle updates, lookups, delivery, cancellation, summaries, and restore.
src/tasks/task-registry.types.tsCanonical task record, runtime, status, delivery, notify, event, and summary types.
src/tasks/task-executor.tsProducer-facing orchestration: create queued/running tasks, finalize by run id, retry blocked flows, cancel tasks/flows.
src/tasks/detached-task-runtime*.tsFacade and optional runtime registration contract for detached task lifecycle ownership.
src/tasks/task-registry.store*.tsStore abstraction and SQLite-backed persistence for task records and delivery state.
src/tasks/task-registry.maintenance.tsRuntime-aware reconciliation, lost detection, cron recovery, cleanup stamping, pruning, and sweeper timer.
src/tasks/task-registry.audit.tsHealth findings for stale, lost, delivery-failed, missing-cleanup, and inconsistent records.
src/tasks/task-flow-*.tsTask Flow registry, store, maintenance, audit, owner access, and internal runtime exports.
src/tasks/task-status.tsCleanup-aware task snapshots and sanitized user-visible status formatting.
src/commands/tasks.ts, src/commands/flows.tsCLI implementations for task and flow operator commands.
src/gateway/server-methods/tasks.tsGateway protocol handlers for task list/get/cancel.
src/auto-reply/reply/commands-tasks.tsChat-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

Detached task runtime facade

Task executor

Persistence stores

Maintenance and audit

Task Flow

Operator and client surfaces

Core workflows

Create a background task

Finalize background work

Deliver a terminal update

Cancel a task

Run maintenance

Inspect tasks from chat and clients

Coordinate a Task Flow

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

Cross-cutting patterns

Testing strategy

Tests are colocated and behavior-oriented. Core task tests are broad, so prefer targeted files while changing a narrow surface.

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

  1. Add the runtime value to TaskRuntime only if it is truly a new owner class rather than a task kind.
  2. Update creation/finalization call sites to use detached-task-runtime.ts.
  3. Teach maintenance how to prove active backing state and recover or mark lost records.
  4. Update CLI filters, Gateway mapping if needed, docs, tests, and audit expectations.

Add a field to task records

  1. Update TaskRecord and any derived public view types.
  2. Add SQLite column migration, row mapping, and bind mapping.
  3. Update clone/snapshot and JSON output behavior if needed.
  4. Add store tests, registry tests, and Gateway/CLI mapper tests when externally visible.

Add a CLI task command

  1. Wire command syntax in src/cli/program/register.status-health-sessions.ts.
  2. Implement command behavior in src/commands/tasks.ts or src/commands/flows.ts.
  3. Support --json when automation is likely.
  4. Add text and JSON tests under src/commands.

Add a Gateway task method

  1. Add or extend schemas in src/gateway/protocol/schema/tasks.ts.
  2. Implement validated handler behavior in src/gateway/server-methods/tasks.ts.
  3. Regenerate protocol artifacts if required by the changed schema.
  4. Add server-method tests and client-facing sanitization checks.

Change Task Flow behavior

  1. Decide whether the change applies to task_mirrored, managed, or both.
  2. Keep revision semantics intact for managed flow updates.
  3. Update flow audit/maintenance if new stale or blocked states are possible.
  4. Update CLI and plugin-domain views if new fields become operator-visible.

Operational concerns

Known risks and open questions