pi-agi
v0.8.0
Published
Long-horizon orchestrator mode for pi. Toggle with /agi.
Maintainers
Readme
pi-agi
Long-horizon orchestrator mode for pi. The main session keeps the goal, plan, and memory, and can delegate work to one isolated worker at a time for context economy and independent verification.
Install
pi-agi is a pi extension, not a standalone application. Install pi first:
# Requires Node.js 20 or newer.
npm install -g @earendil-works/pi-coding-agent
# Install the extension through pi.
pi install npm:pi-agi
# Start with AGI mode enabled.
pi --agiThis installs from the public npm registry. No GitHub account or token is required.
You can also start pi normally and enable the extension with /agi.
The activation banner includes the loaded extension version, for example
pi-agi v0.7.1 enabled.
Do not use npm install -g pi-agi: pi packages must be installed with pi install so pi can register and load the extension. Configure a model provider in pi before using workers.
Test the npm release on macOS
Install Node.js 20 or newer, then install Pi and this extension:
# With Homebrew, if Node.js is not already installed.
brew install node
npm install -g @earendil-works/pi-coding-agent
pi install npm:pi-agi
pi --agiIf pi-agi is already installed, update that package and confirm the registry
version before starting Pi:
npm view pi-agi version
pi update npm:pi-agi
pi --agiConfigure a custom model endpoint
Pi owns model configuration, and pi-agi workers inherit the model selected by
the parent Pi session. Add an OpenAI-compatible endpoint to
~/.pi/agent/models.json:
{
"providers": {
"my-endpoint": {
"baseUrl": "https://models.example.com/v1",
"api": "openai-completions",
"apiKey": "$MY_MODEL_API_KEY",
"models": [
{
"id": "my-model",
"name": "My model",
"contextWindow": 200000,
"maxTokens": 64000
}
]
}
}
}Set the referenced key and select the model as provider/model:
export MY_MODEL_API_KEY="your-key"
pi --model my-endpoint/my-model --agiYou can also start Pi normally and use /model; opening the model picker
reloads models.json. For a keyless local endpoint such as Ollama, use its URL
(for example http://localhost:11434/v1) and a dummy literal apiKey such as
"ollama". If the endpoint rejects the OpenAI developer role or
reasoning_effort, add this provider-level compatibility block:
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false
}Supported api values include openai-completions, openai-responses,
anthropic-messages, and google-generative-ai. Keep secrets out of the JSON
by referencing environment variables as shown above; a plain MY_MODEL_API_KEY
without the leading $ is treated as a literal value.
For local development:
pi -e /absolute/path/to/pi-agiFor a repeatable local TUI in an owned tmux session:
npm run live -- start
npm run live -- attachInside Pi, type /agi. The extension is loaded directly from src/index.ts;
nothing is installed or deployed.
Use
Toggle with /agi or Ctrl+Alt+G, or start enabled:
pi --agiCommands:
/agi— toggle orchestrator mode/agi-status— show mode and state-file status/agi-goal,/agi-memory— edit raw goal and memory files/agi-workers— open the live worker inspector/agi-stop— interrupt the entire AGI run after confirmation: main turn, owned workers, sleeps, and wakes
Tools:
agi_archive— archivegoal.md,plan.md, andnotes/without parsing them; preservesmemory/and runtime recordsagi_note— emit one short user-facing progress note per turn (never enters model context)agi_control— steer live replacement decisions through interrupt/inspection, stop queued or paused work, stop-all when no owned worker is live, and resume the same sessionagi_delegate— spawn one detached worker, end the main turn, and wait for it automaticallyagi_workers— list named agents and their latest statesagi_worker— inspect one named agent's concise status or final reportagi_steer— send a durable mid-run instruction with exact acknowledgmentwait_for_agent— end the turn and wait only for the active worker's completion, attention, or five-minute reviewagi_sleep— end the turn until a worker, tick, user message, or capped timer wakes it
Durable files
goal.md, plan.md, notes/*, and memory/* are ordinary model-owned files under .pi/agi/. The model reads and changes them with Pi's normal read, edit, and write tools. user-requests.md is an automatic verbatim journal of real user turns. Any Markdown structure is valid; frontmatter and task schemas are optional.
The harness injects raw goal.md, plan.md, and optional memory/index.md content on ordinary AGI turns, plus sorted note and memory filenames with best-effort headings. The context is deterministically truncated at 12,000 characters. Missing, empty, malformed, and legacy files remain valid and never block direct work, delegation, sleeping, recovery, or worker completion.
These files are working context only. They do not authorize execution or schedule turns. plan.md never creates automatic ticks: timed re-checks require an explicit agi_sleep, while delegation waits and worker events wake automatically.
Workers
agi_delegate({name, prompt}) spawns one detached pi --mode rpc process for one named agent, sends the main agent's chosen brief verbatim, terminates the main turn, and enters a worker wait. Names match ^[a-z0-9][a-z0-9_]{0,63}$. One name owns one saved conversation lineage: resume that name to continue it, or choose another name for a fresh zero-context agent. A used name cannot be delegated again. The main agent decides which user wording, observations, decisions, and working-file details are useful to include. Completion or attention wakes the orchestrator immediately. If the worker is still running after five minutes, the wake contains only its current bounded activity record. Each lineage continuously exposes a concise, redacted, sequence-numbered .pi/agi/.runtime/agents/<name>/trace.log. The orchestrator uses normal filesystem tools to read a bounded first tail, then only lines above the highest sequence it has already seen. If the worker is on track, wait_for_agent ends the turn and rearms the wait. A model may steer live work or interrupt it for a resumable checkpoint, but terminal stop requires the worker to be queued or durably paused with the interrupted process fully exited. Explicit user and lifecycle stop controls remain immediate.
Workers inherit the parent session's resolved provider/model. Delegated workers have no harness wall-clock deadline, turn cap, token budget, or tool-call budget. They continue until they finish, fail naturally, are explicitly interrupted or stopped, the owning lifecycle shuts them down, or an external platform such as Arena ends the enclosing job. Agent profiles cannot add hidden worker budgets. Model-facing worker status and trace.log use concise sanitized projections, while full timestamps, counters, provider errors, and other diagnostics remain in the raw files and human inspector.
A user message interrupts the worker wait immediately so the orchestrator can answer. If the worker remains active afterward, settling returns to the same five-minute wait. Esc asks whether to interrupt the entire AGI run, with Yes selected first; /agi-stop uses the same whole-run stop path. A user-stopped run never resumes automatically.
Each worker is configured by an agent profile — a Markdown file with YAML frontmatter, resolved from .pi/agi/agents/ (project, requires trust), then ~/.pi/agent/agi/agents/ (user), then the four built-ins:
| Profile | Purpose |
| --- | --- |
| worker | Default implementation persona. |
| explore | Reconnaissance persona for the next investigation step. |
| review | Critique persona for a diff or plan. |
| verify | Verification persona that runs tests and builds. |
The built-ins are written to the user directory on first activation and never overwritten, so they are examples you can edit.
Workers end with a concise useful report in whatever clear format fits the task. Any non-empty final assistant text is a valid result; Markdown headings and outcome keywords are optional. The orchestrator verifies consequential claims against the repository instead of relying on a heading-derived clean-tree heuristic.
Concurrency is fixed at one active worker total. Profiles are behavioral prompts, not capability or security boundaries. Every worker receives Pi's normal tools, extension discovery, explicit -e extensions, plugins, skills, and inherited network mode. Legacy profile fields model, fallbackModels, tools, excludeTools, and extensions are ignored with a one-time warning.
Workers always use the parent orchestrator's exact provider/model; agi_delegate exposes no model selector. The exact value is passed to the child, so a fuzzy bare model id cannot be resolved differently by a detached process.
Workers cannot delegate: PI_AGI_ROLE=worker means the orchestrator tools are never registered, so the depth cap is structural rather than a counter.
Operational telemetry stays out of model context. UTC timestamps and fleet dumps remain in internal state, logs, or purpose-built TUI diagnostics instead of being injected every turn. Internal wait-cycle/streak metadata may remain in durable diagnostics and logs for continuity, but is never exposed to the model or TUI; the wait indicator shows only the concise reason and next wake.
Scheduler-triggered turns receive only the current actionable wake delta, not another copy of the standing goal/plan/notes digest. Completion and review wakes identify agents by name, and named external waits retain the note the orchestrator supplied. Older wake messages remain in the raw session but are removed from later model context.
Known limitation on pi 0.83: a detached worker does not outlive its orchestrator. pi --mode rpc shuts down on stdin EOF, and stdin has to stay open so dialogs from inherited extensions can be answered — an unanswered dialog would block the worker forever. When an orchestrator exits, its in-flight runs are reported orphaned on the next start rather than silently lost. Adoption is implemented and takes over the moment pi can be told not to exit on EOF.
While AGI mode is active, the orchestrator retains the full tool set, including edit, write, and ordinary bash commands. Delegation is encouraged by the system prompt rather than enforced by capability restrictions. The orchestrator can work directly, delegate, or mix both approaches according to the task.
Environment
| Variable | Effect |
| --- | --- |
| PI_AGI_DISABLE=1 | Disable the extension entirely |
| PI_AGI_ROLE=worker | Mark the process a worker; no orchestrator tools are registered |
| PI_AGI_ROLE=off | Inert, same as disabled |
| PI_AGI_PI_BINARY | Path to the pi executable used to spawn workers, when auto-resolution picks the wrong one |
Workers additionally receive the public PI_AGI_AGENT_NAME plus private runtime variables PI_AGI_RUN_ID, PI_AGI_RUN_DIR, PI_AGI_DEPTH, and PI_AGI_MAX_DEPTH. Optional Laminar correlation uses LMNR_TRACE_AGENT_NAME. Any other PI_AGI_* variable is stripped from a worker's environment, so a value in the parent shell cannot reach a child.
Development
npm install --ignore-scripts
npm run check
npm testnpm test runs every suite serially, including integration cases that spawn a real pi --mode rpc child against a localhost stub provider — no provider credentials and no paid tokens, but it takes a few minutes. Those cases need a pi checkout: they default to a sibling ../pi directory and honour PI_ROOT.
Design notes and the implementation spec are in docs/agi/.
One-command release and deployment
The release runner validates the checkout, creates one versioned npm tarball,
installs this checkout into local Pi without leaving the old npm:pi-agi
package enabled, deploys that same package to the production VPS sandbox, and
refreshes plus rebuilds the pi-agi Arena image with exact artifact provenance:
npm run release:allThe package is retained under .release/. The benchmark itself is intentionally
not started by default because it consumes model budget. Start it only when
intended:
npm run release:all -- --run-arenaFuture benchmark runs use gpt-5.6-luna at high with a 1,800-second
ceiling. The release builds task-specific Pi-AGI images for Sokoban, Super
Mario, Snake Maze Campaign, and 2048 before a benchmark can start.
Publishing to the public npm registry is also explicit and requires a clean checkout whose package version is ready to publish:
npm run release:all -- --publish-npmUse --skip-tests only after the exact checkout has already passed the full
suite, and --skip-vps-preflight only when the isolated VPS preflight has just
passed. Run npm run release:all -- --help for target-path overrides.
Local Tmux End-to-End Test
This workflow runs the current checkout directly. It does not publish, install,
or deploy pi-agi. Pi still uses your configured model provider, so model calls
may consume API tokens.
Start a clean temporary Git repository and a detached tmux TUI:
npm run live -- startThe command prints the generated /tmp/pi-agi-live.* workspace and retains it
after the session ends. It loads only this extension with pi -ne -e
./src/index.ts; other discovered extensions are disabled.
Attach and enable AGI mode:
npm run live -- attachThen type /agi inside Pi. Alternatively, after the TUI has started:
npm run live -- enableInteractive tests intentionally start Pi normally and enable /agi afterward.
This exercises the same TUI toggle users see and avoids relying on startup-flag
behavior while debugging it.
Paste this prompt into pi:
Live-test AGI mode end to end.
Delegate a named agent `hello` with the worker profile. No goal or plan setup is required. The worker must create hello.txt containing exactly:
hello from pi-agi worker
Delegation waits automatically. If the five-minute trajectory check wakes you while the worker is still running, inspect its latest progress and use agi_sleep until worker again when no intervention is needed. When completion wakes you, verify hello.txt directly from disk and report the result. Do not create the file yourself.Open the worker inspector while the worker runs:
/agi-workersOr press Ctrl+Alt+W.
Test exact-ack steering while a worker is running:
Steer the active worker with this exact instruction: also create steered.txt containing exactly "steering delivered". Wait for durable acknowledgment.Inspect the exact owned process and workspace from another terminal:
npm run live -- statusSend a prompt without attaching:
npm run live -- send "Run one delegated worker task, verify it, then mark the goal met."For inspector, steering, interrupt, stop, and same-session resume coverage, use
the prompts from the earlier end-to-end flow while watching /agi-workers.
Because the tmux session persists, the control-plane behavior can be exercised
without relaunching or deploying the extension.
Capture the complete tmux scrollback:
npm run live -- capture
npm run live -- capture /tmp/pi-agi-live-tui.txtThe .pi/agi/ state, worker runtime files, and Pi sessions live inside the
workspace printed by start. For example:
workspace=/tmp/pi-agi-live.REPLACE_ME
for file in "$workspace"/.pi/agi/.runtime/agents/*/status.json; do
echo "=== $file ==="
jq '{name, state, pid, steering, error}' "$file"
doneInspect the concise worker trace:
for file in "$workspace"/.pi/agi/.runtime/agents/*/trace.log; do
echo "=== $file ==="
tail -n 80 "$file"
doneInspect raw worker events for diagnostics:
for file in "$workspace"/.pi/agi/.runtime/agents/*/events.jsonl; do
echo "=== $file ==="
jq -c . "$file"
doneInspect durable steering acknowledgments:
find -L "$workspace"/.pi/agi/.runtime/agents \
-path '*/control/steer-ack/*.json' \
-print -exec jq . {} \;Stop only the session created by this checkout:
npm run live -- stopThe stop command checks the session ownership marker, pane PID, command, owner, and working directory before sending graceful EOF. It never kills the tmux server or a pattern of processes. The workspace is not deleted.
Use PI_AGI_TMUX_SESSION=name to run a second isolated session, or pass an
explicit workspace to start:
PI_AGI_TMUX_SESSION=pi-agi-second npm run live -- start /tmp/my-agi-testRequires pi 0.83.0 or newer and tmux.
Sandboxed Real Open Mission
The open-mission harness exercises discovery, self-provisioned browser and shell use, delegation, durable waiting, and completion after a genuine external result. It does not contain a synthetic directory, mailbox, reply generator, preselected address, dedicated email API, or hidden success path.
By default, the runner uploads the current checkout over SSH and executes the
Docker sandbox on the kortix-prod VPS. Override it with PI_AGI_VPS_HOST or
PI_AGI_VPS_ROOT. The optional local path remains available as
npm run sandbox:mission:local -- ....
First build the images and verify the isolation, generic internet, and container-root package-installation capabilities:
npm run sandbox:mission -- preflightOn the VPS, preflight installs the Ubuntu docker-compose-v2 package if Compose
is missing, then proves that public HTTPS and container-root package installation work
through the filtered forward proxy, direct agent egress fails, private and
metadata destinations are rejected, and the host workspace and Docker socket
are absent. It uses a unique Compose project and removes its containers,
networks, temporary volume, and locally tagged images when the check finishes.
For a live VPS model run, select a Pi config source. The uploader copies only
auth.json, models.json, and settings.json; it never copies extensions,
browser data, trust/history, run history, or package caches:
PI_AGI_SANDBOX_AGENT_DIR="$HOME/.pi/agent" \
npm run sandbox:mission -- startThe runner rejects a source without model/auth data. The VPS runner copies only
the allowlisted model files over SSH into the private release directory. The
container receives it read-only, copies it to tmpfs, and does not mount the
host workspace, Docker socket, browser profile, cookies, or mailbox. The agent
attaches only to an internal Docker network. A generic proxy attached to a
separate egress network resolves destinations itself and accepts public unicast
targets only. The agent is root only inside the container, with a writable
container layer so it can install its own browser or other tools. All Linux
capabilities are dropped except the five identity/file capabilities required by
package managers; no-new-privileges remains enabled.
Configured Pi packages are removed from the copied settings, so only this
checkout's AGI extension loads.
start returns only after the TUI is visibly ready; startup failures retain the
dead pane and scrollback for diagnosis.
Run and observe the mission:
npm run sandbox:mission -- attach
npm run sandbox:mission -- run
npm run sandbox:mission -- status
npm run sandbox:mission -- monitor 30If provider-incompatible conversation content makes every subsequent turn fail, recover only the Pi conversation while preserving the active durable goal, workspace, sessions, and external-action evidence. Recovery also re-enables AGI mode before submitting the continuation message:
npm run sandbox:mission -- recover-sessionThe pasted mission is an ordinary user prompt. Sandbox-level instructions state environment facts only and do not add task-specific rules. The core scheduler provides delegation and durable waiting; the sandbox deliberately supplies no synthetic completion path, and retains the trace and workspace so claimed evidence can be inspected. Capture the complete TUI scrollback or export the retained Docker workspace:
npm run sandbox:mission -- capture /tmp/open-mission-tui.txt
npm run sandbox:mission -- export /tmp/open-mission-artifacts
npm run sandbox:mission -- stopEach VPS upload is an immutable source release. The runner retains the newest
six releases by default and always protects the active current target. Set
PI_AGI_VPS_RELEASE_KEEP to an integer from 1 to 50 to change the retention.
stop verifies and stops only the exact Compose project and owned tmux session.
The named workspace volume is retained. Use PI_AGI_SANDBOX_PROJECT and
PI_AGI_SANDBOX_TMUX_SESSION for parallel isolated runs.
To run the same harness locally instead:
PI_AGI_SANDBOX_AGENT_DIR=/tmp/pi-agi-sandbox-agent \
npm run sandbox:mission:local -- start