- name
- dispatch
- description
- Use when operating dispatch, creating or attaching Codex lanes, sending/steering/context-injecting/stopping through dispatch, checking provider capacity, managing triggers, checking daemon status/logs, or configuring the dispatch MCP/plugin surface. Not for changing dispatch source code; use AGENTS.md for implementation work.
- metadata
- {"short-description":"Operate the dispatch control plane"}
# dispatch
Use `$dispatch` to operate the local dispatch control plane. dispatch owns one
App Server connection through a daemon and exposes one authored op registry
through CLI and MCP surfaces. The connection owns a stdio App Server by default;
an explicitly configured local Unix socket attaches without owning server
lifecycle.
For source changes, read the repo-root `AGENTS.md` instead. This skill is for
using the tool.
## Command Surface
When you are in this repo, prefer the in-tree command:
```bash
uv run dispatch --help
```
The current canonical operator grammar is:
- health: `doctor`
- daemon process: `up`, `down`
- daemon reads: `daemon status`, `daemon log`
- registry recovery: `registry migrate`
- model catalog: `models`
- permission profiles: `permissions`
- provider capacity: `usage`
- statusline capture lifecycle (daemon-free): `usage-capture install`,
`usage-capture run`, `usage-capture status`, `usage-capture remove`
- thread lifecycle/read/search: `new`, `attach`, `list`, `list --unmanaged`,
`get`, `sync`, `tail`, `history`, `watch`, `search`, `query`
- thread actions: `rename`, `archive`, `restore`
- message verbs: `send`, `stop`
- goals: `goal status`, `goal set`, `goal clear`
- inbox/subscriptions: `subscribe`, `subscriptions`, `unsubscribe`, `inbox list`,
`inbox read`, `inbox ack`
- triggers: `trigger add`, `trigger list`, `trigger rm`, `trigger pause`,
`trigger resume`
- schemas/MCP: `schema <command>`, `mcp`
Successful CLI output is JSON-shaped. Use `--json` in scripts when you want the
machine-output contract to be explicit.
## Start Or Inspect The Daemon
```bash
uv run dispatch doctor --no-app-server
uv run dispatch up --json
uv run dispatch daemon status
uv run dispatch daemon log --limit 10
```
If a command fails because the running daemon does not support a current CLI op,
dispatch treats that as daemon/client skew. It restarts an idle daemon and retries
once. If the daemon has active work, it refuses to restart automatically and only
blocks the mismatched ops — commands the daemon still agrees on (`daemon status`,
`roster`, `stop`, ...) keep working, so inspect or drain the work, then run
`uv run dispatch down` and `uv run dispatch up --json` when it is safe.
Use `uv run dispatch doctor` before relying on live thread operations in a new or
untrusted environment. It checks PATH visibility, Codex CLI/auth footprint,
daemon socket/pidfile state, registry schema/integrity, packaged skills/plugin
assets, and a low-risk Codex App Server initialize smoke. Use `--no-app-server`
when you only need local install/runtime diagnostics.
Advanced shared-daemon experiments may set the absolute
`DISPATCH_APP_SERVER_SOCKET` path or `[app_server].socket_path` in the local
Dispatch config. `doctor` must report `transport: unix` before relying on that
topology. An explicit socket fails closed, and `dispatch down` closes only
Dispatch's connection. Do not use shared transport as authority for concurrent
Desktop/Dispatch writes; attached-lane write locks remain unchanged.
If doctor reports an old registry schema, run `uv run dispatch down`, then
`uv run dispatch registry migrate`, then `uv run dispatch up --json`.
Stop only when it is clearly your daemon/session to stop:
```bash
uv run dispatch down
```
Runtime state defaults to `~/.dispatch`. Use `DISPATCH_HOME` for isolation when
testing. Do not point tests at the user's live `~/.codex`; the repo integration
suite uses an isolated `CODEX_HOME`.
## Shell Completions
Use the derived completion command when setting up an operator shell:
```bash
uv run dispatch completion bash
uv run dispatch completion zsh
uv run dispatch completion fish
```
Evaluate the generated script for ad hoc use, or write it to the shell's
completion directory for durable installs.
## Thread Selectors And Lane Rules
Every managed thread has a stored dispatch-local `ref`. Prefer refs for command
arguments. The full Codex thread id is always accepted. Titles and `@handles`
are mutable convenience labels; use them only when a unique human label is more
useful than a ref.
Owned lanes are created by dispatch and are writable. Prefer `new` for a
configured managed thread; it applies `.dispatch/config.toml`, presets, name
prefixes, and can send an initial turn:
```bash
uv run dispatch new --name my-lane --cwd /path/to/project --text "Do the bounded thing."
uv run dispatch new --name my-lane --goal "Loop until green." --text "Start with tests."
uv run dispatch new --name visual-review --text "Review this state." --image ./screen.png
uv run dispatch new --name my-lane --preset reviewer --no-send
```
For rich initial input, repeat `--image PATH` and `--image-url HTTPS_URL`; add `--image-detail auto|low|high|original` when the default detail is not appropriate. Local images must be PNG, JPEG, GIF, or WebP and at most 20 MiB. Remote images must use HTTPS and resolve publicly; Dispatch fetches them under a shared 15-second deadline into ephemeral App Server inputs and never stores the bytes.
`new` selects the execution provider with `--provider codex|claude|hermes`, plus
CLI-only boolean shorthands `--codex`, `--claude`, and `--hermes` that marshal to
the same canonical input. Config `[defaults]` and `[presets.*]` accept the
canonical `provider` key only (no shorthands); CLI flags win over presets, which
win over defaults. Omitting all selectors launches a Codex lane. Provider
selectors are mutually exclusive — any combination of two, even the redundant
`--claude --provider claude`, is rejected before any lane work. This execution
provider is distinct from `--model-provider`, and the Claude execution provider is
not launchable yet: resolving to it — from a flag, preset, or config default —
fails with a validation error at launch, never with a silent Codex fallback.
Hermes is an opt-in provider for dedicated local sessions. It requires a configured
owned stdio gateway and the explicit upstream capability contract
`prompt_submit_if_idle_v1` plus `prompt_turn_correlation_v1`; the stock Hermes
`939e45c`/`0.21.2` runtime does not advertise the latter, so Dispatch must refuse
durable Hermes sends rather than infer completion. The capability patch is a local,
unpublished dependency until it is separately verified and adopted upstream.
Configure the binding in global `~/.dispatch/config.toml`:
```toml
[providers.hermes]
hermes_home = "/Users/me/.hermes"
source_root = "/Users/me/src/hermes-agent"
interpreter = "/Users/me/src/hermes-agent/.venv/bin/python"
profile = "default"
```
All paths must be absolute. The worker starts the configured interpreter as
`python -m tui_gateway.entry` with `HERMES_HOME` and the configured source root,
and owns only that child. It does not discover or stop Desktop, serve, or Runs
processes. Launch with an existing directory and plain text:
```bash
uv run dispatch doctor
uv run dispatch new --name hermes-review --provider hermes \
--cwd /path/to/project --text "Review the current changes." \
--idempotency-key hermes:review:1 --json
uv run dispatch send <dispatch-ref> "Address the highest priority finding." \
--idempotency-key hermes:review:2 --json
```
The first Hermes slice rejects goals, images or structured content, output
schemas, custom instructions, staging, workspace/worktree setup, subscriptions,
and Codex-only model or permission overrides before provider I/O. It does not
attach to arbitrary Desktop sessions or fall back to HTTP Runs. When supplied,
reuse an exact launch/send key and request to replay the local record; changing the request is
`delivery_conflict`. Keep unknown or ambiguous receipts held and inspect them
with `delivery get` or `delivery reconcile`; never resend to repair a lost
acknowledgment. A generation change fences existing Hermes sessions, while exact
local receipt replay remains available.
Omit permission-profile, sandbox, approval, model, and service-tier settings when Codex defaults are
acceptable. `dispatch new` omits unset policy/model fields from `thread/start`
and initial `turn/start`, allowing Codex/App Server global, profile, and
project-local configuration to apply. Add explicit values only when the lane
needs Dispatch-owned overrides.
Use `--goal` for a native App Server goal before the initial turn. Do not put
`/goal ...` in `--text`; dispatch treats slash commands as plain text and rejects
that shape so agents do not create a thread that only looks goal-driven.
For durable or parallel launches, drive `new` from a **launch packet** directory
(`goal.md`, `prompt.md`, `output.schema.json`, `base.md`, `developer.md`,
`dispatch.toml`, plus staged-only `hooks/` and `codex/`) or from explicit files:
```bash
uv run dispatch new --name lane-a --cwd /repo --packet ./packet
uv run dispatch new --name lane-a --cwd /repo --goal-file goal.md --input-file prompt.md
printf 'goal text' | uv run dispatch new --name lane-a --goal-file - --input-file prompt.md
uv run dispatch new --name lane-a --cwd /repo --packet ./packet --dry-run --json
uv run dispatch new --name lane-a --cwd /repo --packet ./packet --stage all
```
Use `--input-file` for the prompt file (the file form of `--text`). Precedence
per slot is inline flag > explicit file > packet > repo config. Only one input may
read stdin (`-`).
`--dry-run` resolves and prints the plan (sources with byte/SHA-256, effective
settings, staged parts) without mutating any state. `--stage all|<parts>` writes
durable twins to `.agents/sessions/<ref>/` (with `--inline <parts>` to exclude
some); dispatch stages `hooks/`/`codex/` but never executes hooks. The current
App Server exposes no native worktree request; Dispatch's `--worktree create`
helper is a vanilla git preflight, not a Codex protocol feature.
For worktree-backed lanes, treat the launched runtime as the source of truth.
Dispatch should be given the exact `--cwd`; it should not assume fixed Codex
worktree paths such as `.codex/worktrees/<run>/<lane>` or
`${CODEX_HOME:-$HOME/.codex}/worktrees/<name>`. Codex-managed worktrees may be detached or
unnamed, so an empty `git branch --show-current` is not automatically a failure.
Verify identity with `pwd`, `git rev-parse --show-toplevel`,
`git rev-parse --short HEAD`, `git status --short`, and any repo-provided runtime
or workspace doctor command. A branch name is useful metadata, not proof of
correctness unless the coordinator explicitly required a named branch.
If the repo provides `.codex/environments/environment.toml`, setup/teardown
hooks, or workspace bootstrap scripts, let repo-local tooling own those
semantics. Dispatch may stage the packet, hook files, and Codex config files so
the lane can inspect or run them, but Dispatch should not execute arbitrary hooks
or apply trust-sensitive config on the repo's behalf.
Use `--workspace` when Dispatch should resolve repo-local workspace metadata
before creating the thread:
```bash
uv run dispatch new --name lane-a --cwd /repo --packet ./packet --workspace auto --dry-run --json
uv run dispatch new --name lane-a --cwd /repo --packet ./packet --workspace auto --stage all
uv run dispatch new --name lane-a --cwd /repo --packet ./packet --workspace none
```
`--workspace none` preserves the exact cwd path. `--workspace auto` discovers
`.codex/environments/environment.toml`, reports environment name/version,
setup/cleanup scripts, repo root, and effective cwd, and no-ops with
`state="not_found"` when no supported metadata exists. Dry runs never execute
setup. Setup scripts run only with explicit `--workspace-setup run` or local
daemon policy `[policy] allow_workspace_setup = true`; packet-local config is
not enough to grant setup execution.
Use `--worktree create` when Dispatch should create a vanilla git worktree before
launch:
```bash
uv run dispatch new --name lane-a --cwd /repo --worktree create --dry-run --json
uv run dispatch new --name lane-a --cwd /repo --worktree create --worktree-branch dispatch/lane-a
uv run dispatch new --name lane-a --cwd /repo --worktree create --worktree-path /tmp/lane-a
```
The default root is `~/.dispatch/worktrees/<repo>/<lane>/`, not a repo-local
`.dispatch/worktrees/` directory. `DISPATCH_WORKTREE_ROOT` can override the root.
Do not assume or mimic Claude/Codex private worktree path schemes; Dispatch
reports the exact path/branch/base/head it created. If a branch is already
checked out elsewhere, launch fails before thread creation and names the owning
worktree.
Workspace config can carry worktree defaults, with CLI flags winning:
```toml
[workspace]
default = "auto"
worktree = "create"
worktree_branch = "dispatch/default"
worktree_base = "HEAD"
[workspace.presets.athena]
mode = "auto"
worktree = "create"
worktree_branch = "dispatch/athena"
```
`new` returns `message_accepted`, not proof of assistant completion. After launch,
use `get` to check `latest_turn`, `tail` for persisted history, or `watch` for a
bounded live sample.
Before choosing explicit `--model`, `--model-provider`, or `--service-tier`
values, ask dispatch for the live catalog:
```bash
uv run dispatch models
uv run dispatch models --no-refresh
uv run dispatch schema models
```
Omit model/tier values when Codex defaults are acceptable. If a preset uses a
user-facing tier such as `fast`, Dispatch resolves it through `model/list`
service tiers before starting the thread. The catalog also reports model-defined
reasoning efforts, input modalities, personality support, and upgrade targets.
Do not guess current model ids or effort names from memory; use the catalog
output and its `aliases` field.
Before selecting a named Codex permission profile, query the cwd-aware catalog:
```bash
uv run dispatch permissions --cwd /path/to/repo
uv run dispatch permissions --cwd /path/to/repo --include-disallowed
uv run dispatch schema permissions
```
Use `--permission-profile <id>` on `new`, or set `permission_profile` in global
or repo defaults/presets. Do not combine it with sandbox, approval-policy, or
approval-reviewer overrides. Omit all of them when Codex defaults should apply.
This is distinct from Dispatch `[policy]`, which governs how the daemon answers
inbound interactive requests and does not select a Codex profile.
Use the redacted provider inventory before routing optional work by capacity:
```bash
uv run dispatch usage
uv run dispatch usage --no-refresh --json
uv run dispatch usage --provider codex --host local
uv run dispatch usage --provider claude --host local
uv run dispatch usage --all-hosts --no-refresh
uv run dispatch usage --include-daily --stale-after-seconds 300
uv run dispatch schema usage
```
Default `usage` refreshes local Codex and Claude independently and omits daily
buckets. Claude uses the read-only `claude auth status --json` and `claude
agents --json` surfaces; Dispatch stores aggregate state counts, never roster
cwd/name/session/id fields or raw command output. The observation also records
the bounded semantic version from `claude --version`. Use `--no-refresh` for a
database-only read and `--include-daily` only when historical detail is needed.
The default host is `local`; use `--all-hosts` for mesh inventory. Treat `stale:
true`, `partial`, `signed_out`, `disabled`, `unsupported`, and `unavailable` as
explicit routing constraints. Runtime, capacity, account, usage, and each
window have independent freshness. Output is masked/fingerprinted and never
includes raw email or organization ids, auth material, balances, or
reset-credit mutation ids. Claude account/runtime can be ready before supported
statusline capacity snapshots exist.
Claude capacity snapshots are opt-in via the daemon-free `usage-capture`
lifecycle:
```bash
uv run dispatch usage-capture install --provider claude --dry-run
uv run dispatch usage-capture install --provider claude --yes
uv run dispatch usage-capture status --provider claude --json
uv run dispatch usage-capture remove --provider claude --yes
uv run dispatch usage-capture remove --provider claude --keep-current --yes
```
`install` preserves the operator's complete `statusLine` object in a
restoration record under `~/.dispatch/claude/`, writes the capture wrapper,
then swaps only `statusLine.command` in Claude settings — record, wrapper,
settings, in that crash-safe order. It requires confirmation before touching
Claude settings (`--yes` when non-interactive), is idempotent on rerun, never
records the Dispatch wrapper as the original, and reports malformed settings,
`disableAllHooks`, higher-precedence project/local overrides, and a missing
`dispatch` on `PATH` instead of silently succeeding. On each refresh,
`usage-capture run` reads the statusline JSON from stdin, atomically writes
bounded normalized rate-limit facts beneath `DISPATCH_HOME`, then delegates
the same stdin to the original renderer verbatim; with no original renderer it
emits nothing and Claude keeps its built-in footer.
`dispatch-claude-statusline` is a deprecated alias of the run path.
`status` reports one bounded state — `not_installed`, `prepared`, `installed`,
`drifted`, `broken`, or `disabled` — plus wrapper/record health and last
capture freshness, and never exposes the original command string. `remove`
restores the exact original `statusLine` (or deletes the key when none
existed) before deleting Dispatch artifacts; if settings drifted to something
newer, both `install` and `remove` refuse by default so the restoration
record is never overwritten with the drifted value — run
`remove --provider claude --keep-current` to clean up the artifacts while
preserving the newer setting, then reinstall to adopt it as the new original. `rate_limits` appears only for supported
Claude.ai subscriber sessions after the first API response; missing or stale
snapshots must not erase the last valid capacity windows. Never use the
private OAuth usage endpoint as a fallback.
Attached lanes are existing desktop Codex threads registered by raw thread id:
```bash
uv run dispatch attach <codex-thread-id>
uv run dispatch attach <codex-thread-id> --sync
```
Attached lanes are managed by dispatch but turn-writing/history-mutating
operations such as send, stop, goal mutation, fork, rollback, or compact are
Auf GitHub ansehen