Skip to main content
Tasks are durable units of work owned by the gateway. They can be created by clients, scheduled by triggers, or created by an agent through task tools. A task may execute as an agent subtask by using an agent spec. Tasks exist because not all assistant work should be trapped inside one foreground turn. Some work should run later, retry, report progress, coordinate child threads, or be detached from the parent conversation. The task runtime gives Pioneer a durable shape for that work.
Read this page together with Agent Loop. The agent loop exposes task tools; the task runtime owns the durable task tree and subagent execution.

Why Tasks Are Separate From Threads

A thread is a conversation timeline. A task is a unit of work with lifecycle, trigger, retry policy, dependencies, delivery, and optional child execution. Sometimes a task creates a hidden thread to do its work, but the task is still the owner of scheduling and result semantics. Keeping tasks separate from threads allows Pioneer to answer questions that a transcript alone cannot answer: is this work scheduled again, did it retry, which child run produced the result, is delivery still pending, did a write lock block execution, and what should happen if the gateway restarts?

Runtime Structure

TaskRuntime in crates/tasks/src/service.rs owns:
  • TaskService for mutations and query-oriented operations
  • TaskScheduler for due triggers and queued runs
  • TaskEventBus for in-process task event subscriptions
  • TaskExecutorRegistry for executor implementations
  • TaskStartupReconciler for startup repair
The gateway creates TaskRuntime, registers TaskAgentExecutor, starts the scheduler, and bridges task events into JSON-RPC notifications.

Durable Task Events

Task mutations append TaskEventPayload values and project them into read-model tables. Common events include task created, trigger created, task scheduled, task queued, run created, run started, progress, run completed, run failed, task completed, task failed, task cancelled, write-lock changes, delivery queued, child thread linked, and tree changes. The TaskProjector keeps task, trigger, run, dependency, delivery, write-lock, and agent-spec tables consistent with the event stream.

Triggers And Scheduling

Tasks can be immediate, scheduled for a time, interval-based, or cron-like. TaskTriggerCalculator validates trigger specs and computes initial and next fire times. TaskScheduler wakes from three sources: explicit notification, task event bus activity, and sleep until the next due item. Each pass processes due active triggers, due retry runs, and queued runs whose ready time has arrived. Concurrency policy controls whether a task can have multiple active runs. For recurring triggers, the scheduler computes the next fire time after each run is queued. For one-shot triggers, the trigger becomes exhausted.

Executors

Executors implement TaskExecutor. The current gateway registers an agent executor that can run task work in hidden child threads. The executor receives TaskExecutionContext, TaskRun, and a TaskExecutionHandle. The handle is the executor’s durable output channel. It can mark a run started, emit progress, link a child thread, complete a run, fail a run, cancel a run, release write locks, and queue delivery.

Subagents

Subagents are task-backed child turns. When a task has TaskAgentSpec, TaskAgentExecutor creates or restores a hidden task thread, creates a hidden turn, records child-thread lineage and run/thread bindings, and starts the agent loop with the spec’s model, provider, prompt, context policy, tool policy, and result contract. Context policy controls what the child receives from the parent: empty context, custom context, recent parent context, summary-style context, or inherited context depending on the spec. When the child turn completes, its output is extracted into a result candidate. That candidate is then accepted, revised, cancelled, or auto-accepted according to the review policy and task lifecycle. Thread ancestry is stored separately from task execution state. thread_lineage answers only “which thread came from which thread”: child thread, parent thread, root thread, depth, origin kind, creating thread/turn, and creation time. It does not own task ids, run ids, or child turn ids. Task execution state is stored in task-specific tables:
TablePurpose
task_run_executionExecutor lifecycle for one task run: reserved, starting, running, waiting for review, succeeded, failed, cancelled, or timed out.
task_run_thread_bindingWhich thread is the execution context for a run, usually primary_executor, with room for reviewer and recovery roles.
task_run_turnEach child turn inside the run: initial, revision, recovery, or review.
task_result_candidateA proposed output from one child turn. It can be pending review, accepted, rejected, superseded, extraction-failed, or cancelled.
task_result_review_eventReview/audit history for candidates, including parent-agent, user, review-agent, runtime-auto, and system decisions.
This is why subagents are not just “spawn another chat.” They are task runs with durable lineage. The child thread gives the agent a normal execution environment, while the task run gives the parent system progress, retries, cancellation, and result reconciliation.

Parent Review And Revision State

When review is enabled for an attached subagent task, child completion creates a result candidate rather than immediately completing the run. The execution enters waiting_review, and task/wait can return a reviewRequired item to the parent agent. The parent has three normal choices:
  • accept the candidate with task/accept;
  • request another child turn with task/revise;
  • cancel the task when the candidate should not be used.
Accepting records a TaskResultReviewEvent decision, marks the candidate accepted, finalizes the run result, completes the task when appropriate, and queues delivery/artifact handoff. Requesting a revision records a review event with concrete feedback, marks the current candidate rejected, creates a new task_run_turn in the same child thread with kind = revision, and dispatches the child agent again with the review feedback and any additional instructions. The next child turn produces another candidate. That candidate can be accepted or revised again until maxRevisionRounds is reached. The review event log is intentionally separate from the candidate row. That lets Pioneer support multiple reviewers, advisory reviews, user approvals, overrides, and dispute history without overloading a single mutable result field. A candidate can have many review events, but only a final decision event resolves it. Foreground attached subagent work uses parent-agent review by default. Scheduled and detached tasks must not wait forever for a foreground parent turn, so they can use runtime auto-accept behavior instead of entering the parent review loop.

Task Tools In The Agent Loop

When task orchestration is available, the agent materializes task tools into the normal tool router. The task domain is lazy-visible: preflight may reveal concrete task tools before the first main round, or the model can call request_tools with the task domain to reveal them for the next round. The prompt compiler includes the task orchestration policy when task tools are materialized. The model can create attached tasks, wait for task sets, cancel tasks, detach tasks, and observe results. The parent turn is not allowed to silently finish while attached tasks created by that turn are still active. The agent loop injects a system observation telling the model to call exactly one of wait, cancel, or detach. When task/wait returns review-required candidates, the task tool provider exposes task_accept and task_revise to the parent agent. task_accept finalizes the candidate. task_revise requires explicit feedback and keeps the parent turn active while the child revision runs. Child agent turns run through the same agent loop as foreground agent turns, including turn preflight before the child main provider request. They receive a task runtime hook context so memory policy and post-turn extraction can distinguish task-owned context from ordinary user-thread context.

Retries, Locks, And Deliveries

Retry policy can schedule retry runs after failures. Write locks prevent unsafe concurrent writes across task scopes. Delivery policy controls where task results go after terminal state, and delivery processing is run by the gateway background worker. Startup reconciliation repairs tasks and runs that were active when the gateway stopped. This is why task state is stored durably and projected rather than kept only in runtime memory.
  • Gateway explains how the gateway starts the task runtime and bridges task events.
  • Agent Loop explains how task tools are exposed during a turn.
  • Prompt And Context explains the task orchestration prompt section.
  • Persistence Layer explains task events and task projectors.