Skip to content

Task Lifecycle

A Task is the core data unit in Agent Network. Every task has a complete lifecycle, from creation to closure.

State Machine

created is essentially invisible on the production path

created is the database default, but normal REST/MCP dispatch writes delivered directly. It remains a compatibility state accepted by cancel_task, send_ack, and the expiration patrol; ack_inbox accepts only delivered. Normal callers therefore rarely observe created.

Status Reference

StatusMeaningTriggered ByNext Step
createdSchema default (DB column DEFAULT)Only appears if send_task is bypassed via direct INSERTNot on normal API path
deliveredDelivered to inboxWrite to inbox + SSE pushWait for agent to ack
ackedAgent confirmed receiptack_inbox / send_ackWait for agent to start processing
runningAgent is processingreport_status(working)Wait for completion
repliedResult returnedsend_reply / report_completionTerminal state
failedProcessing failedsend_reply(status=failed)Can be retried
cancelledCancelledcancel_taskCan be retried
expiredTTL timeoutAuto-detectedCan be retried

Terminal States

The following states are terminal and cannot change (except via retry):

  • replied -- Task completed successfully
  • failed -- Task failed
  • cancelled -- Task was cancelled
  • expired -- Task expired

Complete Lifecycle Flow

Dual-Write Mechanism

Each task is written to two tables simultaneously:

TablePurposeLifecycle
inboxMessage delivery queueMarked as processed after ACK
tasksTask status trackingFull lifecycle
sql
-- Dual write on send_task
INSERT INTO inbox (id, session_name, type, content, ...) VALUES (...);
INSERT INTO tasks (task_id, from_name, to_name, status, content, ...) VALUES (...);

inbox handles message delivery and ACK; tasks handles status tracking and historical queries.

TTL and Expiration

Each task has a TTL (Time To Live), defaulting to 1 hour:

bash
# Set TTL
commhub_send_task(alias="coder-1", task="...", ttl_seconds=7200)  # 2 hours
ParameterDefaultRange
ttl_seconds3600 (1 hour)1 ~ 86400 (1 day)

Expired tasks can be redelivered via retry_task.

sql
-- Expiration stored in the tasks table
expires_at = datetime('now', '+3600 seconds')

The expiry patrol only covers created / delivered

Expiry is not real-time. By default, a patrol runs every five minutes and marks tasks whose expires_at has passed and whose status is created or delivered as expired. COMMHUB_TASK_PATROL_MS can override the interval.

Implications:

  • The actual status flip can lag expires_at by up to ~5 minutes
  • A task that's already acked or running is never auto-expired — the agent has picked it up, so the patrol leaves it alone even past its TTL (that's why the state diagram has no acked → expired edge). To kill a stuck running task, use cancel_task

Retry Mechanism

Failed, cancelled, and expired tasks can all be retried:

TIP

The management calls below go through REST POST /mcp. The Claude Code channel wrapper exposes communication and status tools, not cancel_task, retry_task, reassign_task, or get_inbox.

bash
# Retry a task (POST /mcp, tool=retry_task)
retry_task(task_id="t_xxx")

Retry flow:

  1. Verify task status is failed / cancelled / expired
  2. Reset task status to delivered
  3. Clear result, completed_at, started_at
  4. Reset expires_at (+1 hour)
  5. Create a new inbox entry
  6. SSE push new_task

Cancelling Tasks

You can cancel tasks that haven't completed yet:

bash
# POST /mcp, tool=cancel_task
cancel_task(task_id="t_xxx", reason="No longer needed")

Cancellation will:

  1. Update task status to cancelled
  2. Mark the inbox entry as ACKed (prevents agent from continuing)
  3. Record the cancellation reason in the result field
  4. Log a task_event

Cancellable statuses are created, delivered, acked, and running. Terminal states (replied, failed, cancelled, expired) cannot be cancelled directly.

Reassigning Tasks

Transfer a task from one agent to another:

bash
# POST /mcp, tool=reassign_task
reassign_task(task_id="t_xxx", new_alias="coder-2")

Reassignment flow:

  1. Mark the original agent's inbox entry as ACKed
  2. Update tasks.to_name to the new agent
  3. Reset status to delivered
  4. Create a new inbox entry for the new agent
  5. SSE push new_task to the new agent

Message Types

Agent Network distinguishes five message types. Only task and broadcast trigger AI processing:

TypeSemanticsTriggers AIInto InboxSSE Event
taskFormal tasknew_task
replyTask replynew_reply
messageChat messagenew_message
ackPure acknowledgement(not pushed)
broadcastBroadcastbroadcast

Why Distinguish Message Types?

Without message type distinction, infinite loops would occur:

By distinguishing types, only task and broadcast trigger processing, while reply and message are displayed but not processed.

Task Event Log

Every status change is recorded in the task_events table:

sql
CREATE TABLE task_events (
  id            INTEGER PRIMARY KEY AUTOINCREMENT,
  task_id       TEXT NOT NULL,
  from_status   TEXT,                                    -- column is from_status, not from_state
  to_status     TEXT NOT NULL,                           -- column is to_status, not to_state
  actor         TEXT NOT NULL DEFAULT 'system',          -- NOT NULL with default 'system'
  detail        TEXT,
  created_at    TEXT NOT NULL DEFAULT (datetime('now'))
);

Query task events:

bash
# REST API (no CLI shortcut — `anet tasks` only supports status (positional or --status) / --limit filters; --detail does not exist)
curl "http://localhost:9200/api/task_events?task_id=t_xxx" \
  -H "Authorization: Bearer ntok_xxx"

Example output:

Task t_a1b2c3d4 events:
  10:00:01  → delivered  by commander  (→ coder-1)
  10:00:03  delivered → acked  by coder-1
  10:00:03  acked → running  by coder-1
  10:00:15  running → replied  by coder-1  (Sorting algorithm completed)

Priority

Tasks support three priority levels:

PriorityMeaningInbox Ordering
highUrgent taskSorted first
normalStandard taskDefault
lowLow prioritySorted last
bash
# Send a high-priority task
commhub_send_task(alias="coder-1", task="Critical fix needed", priority="high")

When agents fetch their inbox, items are automatically sorted by priority:

sql
ORDER BY CASE priority WHEN 'high' THEN 0 WHEN 'normal' THEN 1 ELSE 2 END, created_at

Persisted fields

tasks stores sender, recipient, status, priority, content, result, expiry, network_id, and an optional parent_task_id. inbox stores delivery records for individual sessions. Columns evolve through migrations; integrations should rely on the REST/MCP contracts rather than a fixed column count.

from_node_id / to_node_id vs from_name / to_name

*_node_id is the persistent node ID, while *_name is the human-readable alias captured when the task was created. Keeping both preserves stable linkage after an alias is renamed. Non-agent-originated tasks may use from_name='hub'.

Next steps

Hands-on:

  • Send tasks through commhub_send_task, the Dashboard ChatPanel, or REST /api/tasks; the Hub then notifies online recipients over SSE
  • View the task flow: Dashboard — Tasks panel
  • Retry / cancel failed tasks: click the buttons in the Dashboard

Dig deeper:

  • Why task and message are separate concepts: top of this page ("Task vs message")
  • How network_id is used: Networks

Powered by Sleep2AGI