Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.blobhub.io/llms.txt

Use this file to discover all available pages before exploring further.

A thread is handed to the worker and returned through its lifecycle state machine. The user drives the thread by setting instance.state = pending; the worker takes over, runs the agent, and reports progress back through the worker activity log. This page covers the dynamics — the states, the transitions, and what the worker does in each. The static envelope shape and field-ownership rules live on the Job Session Object page. The control channel is instance.state on the thread envelope (value.thread.metadata), with the values pending, active, completed, and failed. The user owns the workspace and agent settings and sets instance.state to pending (hand off) or completed (clean stop), and resets failed → pending to retry. The worker writes only instance.state, and only the values active and failed.

State machine

The user-driven states are pending (hand off), completed (clean stop), and the failed → pending reset. The worker-driven states are active (activation succeeded) and failed (a thread-level error).

pending → active checklist (worker side)

When the worker sees a thread envelope with instance.state == "pending", it runs this sequence with respect to the envelope:
  1. Validate workspace.work_folder:
    • must be absolute → else WORK_FOLDER_NOT_ABSOLUTE
    • must exist → else WORK_FOLDER_NOT_FOUND
    • must be a directory → else WORK_FOLDER_NOT_A_DIR
    • must be readable → else WORK_FOLDER_NOT_READABLE
  2. Validate agent.type is in the supported set (claude_code, codex) → else AGENT_TYPE_UNSUPPORTED.
  3. Validate the resolved permissions is autonomous or approval → else PERMISSIONS_UNSUPPORTED.
  4. Reserve a slot from the concurrency.max_agents semaphore. If saturated, the thread stays in pending until a slot frees.
  5. Spawn the configured agent with its working directory set to work_folder. Record the resumable agent_session_id returned by the SDK adapter (local-only).
  6. Catch up: fetch list_session_thread_items(created_since = items.last_consumed.created_at, ascending=true), drop self items, and feed the remaining user items in created_at order to the agent’s first turn.
  7. Write instance.state = "active" on the envelope; persist thread.yaml (recording the local agent_session_id and clearing the local error).
  8. Post a thread_active activity-log item on the worker thread.
Validation failures (steps 1–3) transition straight to failed with the corresponding error code; the semaphore slot is not reserved.

Active behavior

While the thread is active:
  • Inbound — new thread items posted by other users (not the worker’s own user) are queued in memory. Between turns the worker drains the queue, coalesces all queued items in created_at order into one prompt, and runs the next agent turn. The agent is never interrupted mid-turn.
  • Outbound — every agent emission becomes a thread item with a brief content[0].text and structured metadata. See Thread items.
  • Interactive prompts — when the agent asks a question, the turn blocks and the worker posts a pending_prompt item. See Interactive prompts.
  • Cursorsitems.last_consumed advances only at a completed turn boundary, so a crash mid-turn replays the items the resumed agent hasn’t yet folded in. items.last_posted advances per successful post.

active → completed (clean stop)

The user stops a thread cleanly by setting instance.state = "completed" on the envelope. The worker observes the change, tears down the live turn loop, releases the concurrency slot, records the state locally so a later restart does not try to recover the thread, and posts a thread_completed activity-log item on the worker thread. completed is user-set — the worker never writes it.

active → failed triggers

TriggerCode
Agent process exited unexpectedlyAGENT_CRASHED
Agent executable missing on PATHAGENT_EXECUTABLE_NOT_FOUND
Persistent 4xx posting itemsTHREAD_POST_FAILED
Item exceeds cap even after truncationTHREAD_ITEM_TOO_LARGE
A failed session resume surfaces as AGENT_CRASHED — there is no separate resume error code. On transition to failed, the worker writes instance.state = "failed" on the envelope, records the error in the local thread.yaml, releases the concurrency slot, cancels any active prompt, and posts a thread_failed activity item that carries the error code and message. The error itself is not placed on the envelope — it lives in thread.yaml locally and surfaces server-side only through the thread_failed activity item.

failed → pending reset

To retry a failed thread, the user updates the envelope to set instance.state = "pending". The worker observes the change, clears its local agent block, and re-runs the pending → active activation. The worker never auto-retries a failed thread.

See also