Skip to content

pi-dispatch 5.0 FOOM

Choose a tag to compare

@edgehero edgehero released this 05 Nov 19:20
8faf67c
pi-dispatch-5.0-readme.mp4

One minute, with sound. The screens show sample data.

Working with a coding agent usually means sitting next to it. You type, it works, you check, you type again. That is the right way to start, but one person can only watch so much.

I want pi-dispatch to help people get up from that chair at their own pace, one kind of work at a time, from in the loop to near the loop to out of the loop. Where this is heading is what I call dark engineering. A dark factory runs with the lights off because nobody needs to stand on the floor, and I think software work can go the same way, with you looking after a swarm of agents rather than watching one.

I started where most people start, reading every step the agent took. The first thing I stopped watching was small, repetitive work, because I could check afterwards what each run had loaded and what it cost, and the result came back as a change I could read before it shipped.

5.0 makes that whole trip possible from one web page, which 4.x did not have. Early on you watch a job live, and with steering on for its trigger you can send it a message while it works. Once a kind of work is going well, it runs by itself and stops to ask when it needs you. The question waits for two weeks by default, and you can answer one on an issue from the page, on your phone too. After that, jobs start from a schedule or an issue, a second agent can review what came back, and you look at the result. The Zero touch page shows how often each project's runs needed a person, so you step back when the number says you can, and nothing moves a project for you.

The console's overview page: one question and two things to fix, five jobs waiting and why, four running, 15 dollars spent of a 25 dollar daily limit, and a timeline of every project's runs with the jobs in line after the now line

The overview page, with sample data: what needs you, what is waiting and why, what is running, and what it has cost today.

The spending limits hold with nobody watching, and every job stays in its own sealed box. On a GitHub or GitLab project the work stops today at a proposed change. pi-dispatch never merges it, but a job's token could, so require a person's review in your branch protection and what ships stays your decision.

It is free and open source, and it runs on your own computer or server. Install it here.

What else is new

The web console is the biggest change, and every page of it fits a phone. A run's page now says what that run actually loaded, so a flow that was never found or an extension that failed no longer hides behind a clean exit. A trigger's page says what its jobs get on each of your machines, and you can change it right there with a preview in words and one Confirm, down to leaving one extension or one tool out of that trigger's jobs.

Runs got sturdier too. With run.recover, a crashed run can continue from its checkpoint instead of starting over. On an issue whose trigger keeps its session, you can fork a run from an earlier point, on another model if you like, and an agent can stop to ask permission for a named action and wait for a person.

For running many at once, a job can name what it needs (a planner, an editor) and you choose the model per project, with fallbacks for when one is rate limited. Watch rules turn findings in your logs and alerts into a read-only investigation or an issue. The Updates page shows what has a newer version: a new pi arrives through a release, and a person can try a new extension version on one job and then adopt it. Sandboxes open from the console, and there is an optional gateway for reaching their terminal in a browser. OpenShell joins Docker and Podman as a place to run jobs, and jobs now run on pi 1.5.0, with Radius supported under a cost cap.

Upgrading from 4.x: read this first

Follow the upgrade guide. In short: pause, wait for running jobs, then move the job image, every worker, the receivers, the console and every /dispatch, and resume. Install one exact version everywhere.

Why this is a major version

  • /dispatch insights no longer writes insights.html, so its --no-open and --full-paths and the setting PI_GRAPH_DIR are gone. A leftover PI_GRAPH_DIR is silently ignored. The command now prints the console's address.
  • PI_MODEL, PI_PROVIDER and the overlay's model and provider are shape-checked. A value that used to be accepted now refuses at boot or refuses jobs.
  • Every job publishes a redacted live transcript to Valkey for up to 24 hours (PI_LIVE_TRANSCRIPTS=0 turns it off), and run progress keys (PI_RUN_PROGRESS).
  • The console binds a port, and with run.recover a retry can continue a run.
  • PI_REPORT_PROGRESS, PI_RECOVERING and PI_STEER are reserved, so a trigger's env that used them is refused.
  • The receiver needs the new worker, and a console answer needs a worker of the same release.

Before you upgrade

  • A top-level version key that earlier releases ignored, in settings.json, the pause windows file or the triggers file, is now read. A hand-added one that is not an integer of 1 or more stops the worker or refuses jobs. Above 1 in settings.json holds every job as newer-release, and above 1 in the pause windows file or above 2 in the triggers file stops the worker at its next start. Remove a hand-added version.
  • Cron ids over 176 characters are refused. Shorten them first.
  • An Updates canary queued from the console runs as the trigger's ordinary paid job on a 4.x worker. Upgrade every worker before using the Updates page.
  • run.excludeExtensions and run.excludeExtensionTools need every worker, receiver and console on 5.0.0. A 4.x worker drops both and runs the job wider than the file says, on a clean record. A 4.x panel, /dispatch or one-shot disarm strips the file's stamp, and a 5.0.0 receiver or worker then refuses that file until a write from the 5.0.0 console or /dispatch puts the stamp back. The console refuses the change while a live worker cannot read them. A hand edit is not stopped, though doctor warns.
  • An up-made Valkey container keeps :8 until it is recreated. Doctor's state.older names it, with the fix. A Podman unit moves with service install --force.

What changes for jobs you already run

  • A chained child inherits packages. A follow-up job of a "packages": false trigger now loses the staged packages it used to load.
  • Jobs move from pi 1.2.0 to 1.5.0. pi 1.5.0 converts tool arguments: every tool the runner defines refuses a wrong type first, and MCP tools receive the converted value.
  • OpenRouter costs are billed totals, so a job on a BYOK key counts upstream inference against its cap.
  • 129 catalog models are gone since 4.3.1 (123 chat, 2 image, 4 classifier) and 192 are new. Check triggers and agents naming a removed model, among them Radius gpt-5.5, gpt-5.4, gpt-5.4-mini or gpt-5.3-codex, or OpenRouter openai/o1, openai/o3-mini, openai/o4-mini, openai/gpt-3.5-turbo or openai/gpt-4-turbo. A trigger whose watch.decide names a removed model refuses the triggers file.
  • 4.3.1's GitLab and Forgejo docs said those forges send a delivery answered 503 again. GitHub, GitLab and Forgejo never do that on their own, and the docs now say where to send one again. The receiver answers 503 when it did before. Past one replica, its enqueue_failed line carries queued and replicas.
  • A forge without an action list gets a refusal that names the trigger and the forge, where it would have thrown a TypeError. No configuration reaches this today.
  • Capacity insights no longer count an older job's slot time as a new job's under a reused job id.
  • pi-dispatch service restart --drain now also pauses, waits for and resumes the host's own queue when PI_WORKER_NAME is set. A queue paused before the drain stays paused. It refuses before pausing anything when this shell and the deployment .env disagree on PI_WORKER_NAME.

Rolling back to 4.3.1

  • Take run.excludeExtensions and run.excludeExtensionTools off every trigger first. 4.x runs those triggers' jobs with what the keys took away.
  • 4.3.1 drops unknown settings keys (secretBundles included) when its /dispatch panel writes settings.json. It also writes the triggers file as triggers alone.
  • Run record .tmp crash leftovers stay in the logs folder. Delete them by hand.
  • A rollback ends with pi-dispatch resume, since the pause lives in Valkey.
  • 4.3.1 never touches console-audit/: it has no web console, and its sweep reads only the top of the logs folder.

By package

  • @edgehero/pi-dispatch: many new subpaths, among them ./job-data-version, ./retention, ./bullmq-internals and ./model-attempts. A new dependency, @earendil-works/pi-tui, pinned exactly. New commands chat and steer. New trigger fields run.progress, run.statusComment, run.recover and run.steer, among others. New settings: PI_LIVE_TRANSCRIPTS, PI_RUN_PROGRESS, PI_CHECKPOINTS_DIR (checkpoints hold transcripts and trees for up to 7 days), PI_CONSOLE_AUDIT_RETENTION_DAYS (defaults to PI_LOG_RETENTION_DAYS, and 0 keeps it forever), PI_RUN_MIRROR_MAX_RUNS (5,000 by default, 1,000 to 20,000, and in a fleet the lowest wins), and the shell-only PI_CONSOLE_TOKEN and PI_CONSOLE_URL. New defer reason newer-release. New doctor ids settings.unknown-keys, fleet.store-versions, fleet.versions, dollars.window-reset (it counts only records from this release on) and state.older. Run records gain v, dollars.reservedIn, jobCreatedAt and loaded. Valkey is pinned to valkey/valkey:8.1.
  • @edgehero/pi-dispatch-receiver: the worker range is ^5.0.0. An older receiver drops the new run fields and the secretBundles key, and the new worker refuses those jobs as trigger-skew.
  • @edgehero/pi-dispatch-admin: the pi-dispatch-console command and its PI_CONSOLE_* settings, /dispatch console, /dispatch chat, the PARKED view, t and T, and pi-dispatch service install --console. On stored state from a newer release: an audit row or a token role this console does not know is kept and marked, or signs in nowhere; a newer run record shows by its core facts; a job with a dataVersion it cannot read cannot be re-run or answered here; a retained workspace with a newer manifest reads manifest-newer and is not reopenable. It also names a scheduler whose id is 177 to 200 characters long, so you can remove it.
  • @edgehero/pi-dispatch-sandbox-gateway: its first release. With PI_LOGS_DIR unset, it writes under ~/.pi-dispatch/logs, so run it as the worker's account.
  • The job image, ghcr.io/edgehero/pi-job:5.0.0: new capabilities, among them reportProgress, recover, steer, sandboxLayout, observableRunner and loadExclusions, and the label dev.pi-dispatch.net-binaries. An older image is refused before it spends (job-image-*-unsupported) where a job needs one of them. A job with run.excludeTools in a scope with a role map needs this image even if it never starts a child. If you build your own image from ours, rebuild it from this release. @earendil-works/pi-agent-core is pinned exactly. :latest carries this runner.
  • OpenShell: PI_OPENSHELL_ENGINE and PI_OPENSHELL_GATEWAY, and the console and panel show its runs. The venue is refused unless the host's ptrace_scope is exactly 1, and an older image is refused before it spends.

Security

GHSA-4qvc-f352-mm29, a console issue fixed in #729. 5.0.0 is its patched version, and no earlier release contains the console.

Full changelog: v4.3.1...v5.0.0

Upgrading: https://github.com/edgehero/pi-dispatch/blob/v5.0.0/docs/upgrading.md