Skip to content

Agent runtime, scheduling, and completion

This page answers the agent/task part of the cli.js analysis: how the agent system is designed, which agent families exist, how tasks are scheduled, which patterns are used, how completion is detected, what is unusual about the runtime, and whether timed tasks exist.

It complements Agents, tasks, and subagents, Slash commands and automation, and Agent and automation architecture. Those pages list individual tools/commands; this page focuses on scheduling and lifecycle mechanics.

Short answer

  • The agent system is orchestration over the existing session runtime, not a second runtime. Agents/subagents reuse the same context loop, tool permission boundary, hooks, JSONL session storage, and telemetry/debug surfaces.
  • The source-visible agent families are: inline custom agents (--agents <json>), the claude agents command family, subagents created through task/delegation tools, teammate/background-agent modes, hosted review agents (ultrareview), and automation helpers such as slash commands/skills/auto-mode.
  • Tasks are scheduled with a task store + task message queue + stream-frame patching pattern. TaskGet can block until a terminal status; task updates emit patch events.
  • Completion is detected primarily through task status. In the SDK/task protocol, terminal statuses are completed, failed, and cancelled; internal UI eviction also treats killed as terminal.
  • Timed tasks do exist. A Kairos/cron family exposes prompt scheduling with session-only or durable tasks, recurring/one-shot cron expressions, missed-task surfacing, jitter, lock files, and a CLAUDE_CODE_DISABLE_CRON kill switch.

Source anchors

Semantic aliasSourceApproximate locationString or symbolMeaning
AgentsCommandFamilycli.jsline ~19550, byte 0xdcc1acH.command("agents")Root command family for agents/background agents.
InlineAgentsFlagcli.jsline ~19525, byte 0xdc1fd4--agents <json>Inline custom-agent definitions at session start.
RemoteControlAgentCoordinationcli.jsline ~19550, byte 0xdcb6f2--remote-control [name]Remote Control can expose/coordinate running agent sessions.
UltraReviewCommandcli.jsline ~19550, byte 0xdcc963H.command("ultrareview [target]")Hosted multi-agent review command family.
AutoModeCommandcli.jsline ~19550, byte 0xdccb56H.command("auto-mode")Permission/automation classifier inspection command.
TaskCreateToolcli.jsline ~1091, byte 0x4804f6TaskCreateTask creation tool constant.
TaskGetToolcli.jsline ~1091, byte 0x480512TaskGetTask status/result retrieval tool constant.
TaskListToolcli.jsline ~1091, byte 0x480523TaskListTask listing tool constant.
TaskUpdateToolcli.jsline ~99, byte 0x97c03TaskUpdateTask update tool constant/request family.
TaskUpdateWaitercli.jsline ~99, byte 0x97bfb_waitForTaskUpdateBlocking wait path used by TaskGet.
TaskTerminalStatusPredicatecli.jsline ~95, byte 0x91cb6`function R5H(H){return H===“completed”
TerminalTaskCancelGuardcli.jsline ~99, byte 0x97ef1Cannot cancel task in terminal statusCancellation is rejected for terminal tasks.
TaskStartedFramecli.jsline ~1837, byte 0x503d86task_startedRuntime task registration stream frame.
TaskUpdatedFramecli.jsline ~1837, byte 0x503a13task_updatedPatch-style task update stream frame.
TaskProgressFramecli.jsline ~2004, byte 0x519283task_progressTask progress stream frame.
TaskNotificationContractcli.jsline ~185, byte 0x112031Each stdout line is delivered to the model as a <task_notification> eventLong-running monitor notification contract.
SubagentStartHookcli.jsline ~185, byte 0x10b75dSubagentStartSubagent lifecycle hook.
SubagentStopHookcli.jsline ~185, byte 0x10b76dSubagentStopSubagent lifecycle hook.
TaskCreatedHookcli.jsline ~185, byte 0x10b7d5TaskCreatedTask lifecycle hook.
TaskCompletedHookcli.jsline ~185, byte 0x10b7e3TaskCompletedTask lifecycle hook.
SubagentContextClassifiercli.jsline ~253, byte 0x2104c1agentType==="subagent"Runtime distinction for subagent context.
KairosCronGatecli.jsline ~1091, byte 0x480583isKairosCronEnabledScheduled-task feature gate.
DisableCronEnvcli.jsline ~1091, byte 0x4806fdCLAUDE_CODE_DISABLE_CRONScheduled-task kill switch.
CronSchedulerRuntimecli.jsline ~9564, byte 0xcd3ba4createCronSchedulerRuntime scheduled-task engine.
ScheduledTaskLockFilecli.jsline ~9564, byte 0xcd383a.claude/scheduled_tasks.lockScheduled-task lock file.
UltraReviewPreflightApicli.jsline ~6664, byte 0xab2665/v1/ultrareview/preflightHosted review preflight API.

Agent families visible in the bundle

cli.js does not expose one clean static roster of every shipped agent persona. Instead, it exposes agent families and loading mechanisms.

FamilySource-visible entryDesign role
Inline custom agents--agents <json>Session-scoped custom agent definitions, useful for scripted/headless runs.
Agent command familyH.command("agents")User-facing/background-agent management and dispatch.
SubagentsagentType==="subagent", SubagentStart, SubagentStopDelegated model contexts that run as projections of the same session runtime.
Task agentsTaskCreate, TaskUpdate, TaskGet, TaskListStructured tasks used by the model/runtime to plan, assign, wait, and report progress.
Teammate/background modes--agent-id, --agent-name, --team-name, --teammate-mode, --agent-typeMulti-agent/teammate coordination around the same CLI runtime.
Hosted review agentsultrareview [target], /v1/ultrareview/preflightExplicit hosted multi-agent review workflow.
Skills/slash automationSkill, slash command metadata, keybinding command:*Human/plugin/keybinding-triggered automation that can look agent-like but enters through commands/tools.
Auto-mode classifierauto-mode, hasAutoModeOptIn, tengu_auto_mode_configPermission/automation classifier; not an agent itself, but affects whether agents/tools can proceed without prompts.

The important design point: these families share the same session envelope, settings/policy, MCP/plugin registry, tool-permission boundary, and transcript system.

Task scheduling model

sequenceDiagram
participant Model
participant TaskTool as Task tools
participant Store as Task store
participant Queue as Task message queue
participant Stream as SDK/headless stream
participant Worker as Subagent/worker
Model->>TaskTool: TaskCreate(subject, description)
TaskTool->>Store: create pending task
Store-->>Stream: task_started / TaskCreated
Model->>TaskTool: TaskUpdate(status/owner/blocks)
TaskTool->>Store: mutate task record
Store-->>Stream: task_updated patch
Worker-->>Queue: response/error/progress messages
Model->>TaskTool: TaskGet(taskId)
TaskTool->>Queue: drain queued messages
TaskTool->>Store: read task status
alt non-terminal
TaskTool->>Store: _waitForTaskUpdate
Store-->>TaskTool: wake on update
else terminal
TaskTool->>Store: getTaskResult
TaskTool-->>Model: result + taskId metadata
end
ComponentBehavior
TaskCreateCreates a structured task with pending status and metadata; source prompt says to use it for complex multi-step work and plan mode.
TaskUpdateUpdates status/description/owner/dependencies/metadata; runtime emits patch-style task_updated frames.
TaskListLists current tasks with cursor support.
TaskGetRetrieves a task; if non-terminal, drains queued messages and waits for updates until terminal.
Task message queueCarries response/error/progress messages related to a task, then clears when the terminal result is returned.
Stream framestask_started, task_updated, task_progress, task_notification let UIs/SDK hosts display progress without polling raw files.
HooksTaskCreated and TaskCompleted are hook events; SubagentStart/SubagentStop are separate context lifecycle events.

Completion detection

There are several completion signals, depending on the layer.

LayerTerminal/completion ruleEvidence
SDK/task protocolcompleted, failed, cancelledTaskTerminalStatusPredicate returns true for exactly those strings.
Cancellation pathCannot cancel if already terminalCannot cancel task in terminal status: ${status}.
Internal UI evictioncompleted, failed, killed can be evicted after notification/retention checksInternal task-eviction snippet checks those statuses.
Hook layerTaskCompletedHook event emitted when task lifecycle completes.
Subagent layerSubagentStopRuntime-context completion, distinct from task-record completion.
Remote bash commandonCommandLifecycle(uuid,"completed") after output/error is enqueuedRemote bridge bash_command handling.
Scheduled one-shot taskFired prompt is removed/deleted after firecreateCronScheduler removes non-recurring fired tasks.
Scheduled recurring tasklastFiredAt persisted; task expires if aged outisRecurringTaskAged, tengu_scheduled_task_expired.

The hidden footgun is that task completion and subagent completion are not identical. A subagent can stop, a task record can complete, and a remote/host command can acknowledge completion through different frames.

Scheduling patterns

PatternHow it worksWhy it matters
Deferred tool resultTask tools can defer and wait (shouldDefer, _waitForTaskUpdate).Lets the model start long work and later request the result without blocking every frame.
Patch streamingtask_updated contains a minimal patch (status, description, end_time, error, etc.).UIs/SDK hosts can update state incrementally.
Queue draining before waitTaskGet drains task messages before checking terminal state.Prevents progress/errors from being stranded while a caller waits.
Dependency/ownership metadataTask prompts mention owner, blocks, and blockedBy.Supports multi-agent planning without a separate workflow engine.
Subagent projectionagentType==="subagent" changes runtime context inside the same loop.Avoids a second permission/model/session stack.
Long-running notification processEach stdout line becomes <task_notification>.External monitors can feed the model with a narrow text-line contract.
Worktree/tmux/in-process teammate modesCLI exposes teammate identity and mode flags.Enables multi-agent work without assuming one deployment topology.
Hosted preflightultrareview calls /v1/ultrareview/preflight before hosted work.Hosted runs are explicit and preflighted rather than silently triggered.
Cron prompt injectionScheduled tasks call onFire(prompt) or onFireTask(task).Timed automation is implemented as prompt/task injection into the existing session.

Timed tasks and cron

The scheduled-task family is source-visible and feature-gated.

SurfaceBehavior
isKairosCronEnabledReturns false when CLAUDE_CODE_DISABLE_CRON is set; otherwise checks a feature flag.
isDurableCronEnabledSeparate gate for durable scheduled tasks.
Cron create/list/delete toolsPrompt strings describe scheduling prompts for future times, list/delete operations, and durable vs session-only tasks.
Standard 5-field cronPrompt says minute/hour/day/month/day-of-week in the user’s local timezone.
One-shot tasksrecurring: false; fire once and auto-delete.
Recurring tasksrecurring: true; persist/update lastFiredAt; may age out if not permanent.
Jitter guidancePrompt explicitly tells the model to avoid :00 and :30 when approximate timing allows.
Durable storagePrompt says durable: true persists to .claude/scheduled_tasks.json; scheduler code uses .claude/scheduled_tasks.lock.
LockingScheduler lock records sessionId, pid, procStart, and acquiredAt; stale PID locks can be recovered.
Missed tasksMissed one-shot tasks are surfaced and then removed; telemetry includes tengu_scheduled_task_missed.
Fire telemetryRuntime emits tengu_scheduled_task_fire; aged recurring tasks emit tengu_scheduled_task_expired.

The scheduler is not a separate always-on daemon in the analyzed path. It is an in-session scheduler with locking, periodic checks, and optional durable files so future/parallel processes can coordinate.

Unique runtime design choices

  1. Agents are runtime projections. Subagents and background agents reuse session, model, tool, permission, hook, and telemetry infrastructure.
  2. Tasks are model-visible tools. The model can create/update/list/get structured tasks through normal tool pathways, which makes planning auditable in the transcript.
  3. Completion is status-driven. Waiting is implemented by TaskGet + _waitForTaskUpdate, not by guessing from text output.
  4. The task stream is patch-oriented. task_updated carries minimal changes, which is friendlier for SDK/TUI consumers than replaying full task lists.
  5. Cron fires prompts, not arbitrary code. Timed automation injects prompts/tasks into the same agent loop; tool execution still goes through permissions.
  6. Long-running monitors speak one line at a time. The <task_notification> stdout contract avoids embedding an arbitrary subprocess protocol inside the model loop.
  7. Remote and local use the same envelope. Remote Control can send commands/control responses, but task and permission semantics stay aligned with local runs.
  8. Hosted review is explicit. ultrareview is a command with preflight, not an ambient background service.
  9. Feature gates surround advanced automation. Cron, background agents, bridge behavior, auto-mode, and agent views all have feature/env/policy gates.

Diagnostics and telemetry for agents

SignalMeaning
task_started, task_updated, task_progress, task_notificationRuntime-visible progress frames.
TaskCreated, TaskCompleted, SubagentStart, SubagentStopHook-level lifecycle events.
tengu_scheduled_task_missed, tengu_scheduled_task_fire, tengu_scheduled_task_expiredScheduled-task telemetry.
tengu_auto_mode_*Auto-mode decision/fallback/denial telemetry family.
tengu_worktree_kept, tengu_worktree_removedWorktree teammate/task cleanup telemetry.
Debug logs (--debug, CLAUDE_CODE_DEBUG_LOGS_DIR)Low-level traces for scheduler locks, task updates, bridge state, and errors.

For the broader gates and observability story, see Feature gates reference and Telemetry and tracing.

Caveats

  • The source-visible agent list is a list of families/loading surfaces, not a guaranteed complete persona catalog. Some agent definitions can come from plugins, settings, marketplace data, or hosted services.
  • Status names differ by layer. Do not assume killed is part of the SDK terminal predicate; TaskTerminalStatusPredicate only includes completed, failed, and cancelled.
  • Cron behavior depends on feature gates and environment. If CLAUDE_CODE_DISABLE_CRON is set, scheduled-task creation should be treated as unavailable.
  • tengu_* names are opaque unless adjacent code explains them. This page only interprets names with nearby behavioral evidence.

Created and maintained by Yingting Huang.