Rust workspace for a durable task dispatcher that can drive multiple coding-agent CLIs:
codexclaude codepicursor agent(agent)
The design target is not just "spawn a worker", but "persist enough state to recover and continue work later".
The project should be read as a general-purpose prompt + CLI tool:
- prompts define host and worker behavior
dispatch-cliexposes the durable command surface- optional host adapters can wrap the CLI for specific environments
- Persist task state to disk under
.dispatch/tasks/<task-id>/ - Persist backend session references so the same agent session can be resumed
- Keep an append-only event log for auditing and recovery
- Support backend-specific native session resume when available
- Fall back to external checkpoints when a backend has no validated native resume
- Persist execution permission mode explicitly per task
crates/dispatch-coreDurable task model, artifact layout, event log, and plan rendering.crates/dispatch-backendsBackend traits plus adapters forcodex,claude,pi, andcursor-agent.crates/dispatch-cliCLI forinit,run,status,resume, andevents.integrations/Optional host adapters. These are examples and convenience layers overdispatch-cli, not the core product boundary.promptsFirst-class dispatcher and worker prompt assets derived from the reference project.docsFlow specs and mapping notes that bring the full reference process into this repo. Includes a host-neutral integration contract for any calling agent.
Each task gets a dedicated directory:
<cwd>/.dispatch/
tasks/
<task-id>/
task.json
plan.md
output.md
context.md
events.jsonl
mailbox/
outputs/
task.json is the canonical state. events.jsonl is append-only. plan.md is a worker-owned working artifact, not the runtime source of truth.
Native session storage for backends that use explicit session files is runtime-owned and lives outside the task artifact tree:
~/.dispatch/runtime/sessions/<task-id>/
mailbox/ is the worker-to-user handoff channel:
mailbox/
001.question
001.answer
001.done
.done
outputs/ stores captured stdout/stderr for each execution attempt:
outputs/
attempt-000.stdout.log
attempt-000.stderr.log
attempt-001.stdout.log
attempt-001.stderr.log
In practice, task behavior is split three ways:
- the dispatcher prompt decides when to run, inspect, answer, or resume
- the worker prompt decides how to execute and keep
plan.mdmeaningful - the CLI persists state and remembers the exact backend session reference
-
Bootstrap default preference currently favors:
pifirst, thencodex, thenclaude, then other discovered targets. -
codexUses native session resume.automaps to--full-auto;dangermaps to--dangerously-bypass-approvals-and-sandbox. -
claudeUses a preallocated--session-idso the dispatcher can persist the session reference before execution.automaps to--permission-mode auto;dangermaps to--dangerously-skip-permissions. -
piUses a file-backed session in the runtime session store under~/.dispatch/runtime/sessions/<task-id>/via--session <path>so the dispatcher can remember the exact corresponding session. The current adapter records execution mode but does not add extra flags because the CLI help does not expose a separate approval-bypass switch. -
cursor-agentIncluded as a first-class backend target, but native resume is still marked unvalidated until the local CLI can be checked.autoanddangercurrently both map to--force. External task checkpoints still work.
Most operational commands support --json so a host prompt can treat dispatch as a stable command subsystem instead of scraping prose.
dispatch initCreate the root task store.dispatch templateGenerate aplan.mdtemplate that the user can fill in directly.dispatch readyLoad config and report dispatcher readiness without starting a task.dispatch config showInspect the current explicit backend/model/alias mapping.dispatch config add-backend|remove-backendManage backend command definitions.dispatch config add-model|remove-modelManage named model entries with explicit backend binding and optional scoped model id.dispatch config add-alias|remove-aliasManage user-facing aliases that point to a model and can prepend prompt guidance.dispatch config set-defaultChange the default model or alias.dispatch backendsShow installed status and capability flags for each backend.dispatch runPersist a task from an inline prompt,prompt.md, orplan.md, then execute it by default.dispatch listShow recent tasks in a compact summary view.dispatch inspectShow task state, pending mailbox questions, and recent events in one call.dispatch resumeReuse the persisted session reference for that task and execute the next turn by default.dispatch statusShow the canonical task record.dispatch eventsShow the append-only event log.dispatch questionsShow pending mailbox questions across tasks or for a specific task.dispatch answerWrite an answer into the task mailbox.
Hosts should think in task ids and durable state, not raw backend session handles.
Typical conversational behavior:
- start tasks with
run - inspect them with
inspect - answer worker questions with
answer - continue the same task with
resume
run now supports three task modes:
--mode directPush a clear prompt straight to the worker.--mode planUse or generate a checklist-drivenplan.md.--mode discussProduce a discussion draft and wait for user clarification.
When --mode auto is used:
plan.mdinputs route toplanprompt.mdinputs route todirect- inline prompts default to
directso the host prompt remains in control
Input sources:
--prompt "..."Inline prompt--from prompt.mdPrompt file--from plan.mdUser-authored plan file
Default task root is always cwd/.dispatch unless --root is set explicitly.
resume is task-centric: the host resumes a task id, and the runtime reuses the correct backend session handle underneath.
Native session forking may exist inside backend adapters, but it is not currently part of the main public CLI flow.
The dispatcher prompt is expected to behave like a host-side operator:
- interpret the user's intent and choose the next CLI command deliberately
- choose backend/model/mode deliberately
- inspect durable state instead of guessing
- ask the user focused follow-up questions when the worker is blocked
- report only operationally relevant status back to the user
The worker prompt is expected to behave like an execution agent:
- read and maintain
plan.mdas a working record - keep progress markers honest when using checklist form
- use
mailbox/only for worker-initiated clarifying questions - write
context.mdbefore stopping on timeout or blockage - leave
plan.md, output artifacts, and the completion marker in agreement
dispatch-cli is the primary tool surface.
Host integrations are optional wrappers that help a specific coding agent invoke the CLI more ergonomically.
The host-neutral integration contract is documented at host-integration-contract.md. Adapter-specific guidance lives in integrations/README.md.
Minimal generic flow:
dispatch --json ready
dispatch --json run --prompt "review auth flow" --mode direct
dispatch --json inspect <task-id>
dispatch --json questions <task-id>
dispatch --json answer <task-id> --message "..."
dispatch --json resume <task-id> --message "continue with the user's answer"
The repository currently includes one adapter example for pi.
The repository now includes a pi host extension at integrations/pi-dispatch-host/index.ts.
Development load:
pi -e /path/to/agent-dispatch/integrations/pi-dispatch-host/index.tsGlobal install:
mkdir -p ~/.pi/agent/extensions/dispatch
cp /path/to/agent-dispatch/integrations/pi-dispatch-host/index.ts ~/.pi/agent/extensions/dispatch/index.tsThe extension resolves the dispatcher in this order:
DISPATCH_BINDISPATCH_WORKSPACE- repo-local
target/debug/dispatch-cli cargo run -p dispatch-clidispatch-clionPATH
For a copied global extension, set one of these first:
export DISPATCH_WORKSPACE=/path/to/agent-dispatch
# or
export DISPATCH_BIN=/absolute/path/to/dispatch-cliSupported commands inside pi:
/dispatch <prompt>/dispatch --backend codex --model gpt-5.3-codex --mode direct <prompt>/dispatch --from plan.md --mode plan/dispatch template --kind audit --output plan.md/dispatch ready/dispatch config show/dispatch config set-default sonnet/dispatch list/dispatch inspect <task-id>/dispatch status [task-id]/dispatch questions [task-id]/dispatch events [task-id]/dispatch answer <task-id> <message...>/dispatch resume <task-id> <message...>/dispatch backends
The extension persists the last selected task in the pi session via appendEntry(), restores it on session start/tree navigation, and updates ctx.ui.setStatus() plus a small widget with the current task state.
Empty /dispatch requests still resolve to readiness or the last selected task, but free-form intent parsing is left to the calling model instead of the adapter.
Nothing in the core runtime depends on pi. Other hosts can integrate by invoking dispatch-cli with --json and treating it as a command subsystem.
The repository includes both unit tests and end-to-end tests:
- mailbox persistence and atomic answers
- runtime mode resolution and file-source parsing
- executor output capture and session extraction
- prompt-file direct execution
- user-authored
plan.mdexecution - discuss-mode draft creation
- background execution
- mailbox question -> answer -> resume roundtrip
- readiness reporting
- config mutation commands for explicit backend/model/alias mappings
The complete reference behavior from the upstream dispatch project is now landed in repository assets:
- prompts/dispatcher-system.md
- prompts/worker-template.md
- docs/dispatch-flows.md
- docs/dispatch-reference-map.md
- docs/host-integration-contract.md
These files are now part of runtime behavior:
- worker prompts are rendered from worker-template.md, not from an inline hardcoded string
- mailbox flow and recovery semantics are exercised by unit tests and e2e tests
- config is editable through explicit CLI commands instead of relying on heuristic model-family routing