# pi-agi

Long-horizon orchestrator mode for [pi](https://pi.dev). 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:

```bash
# 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 --agi
```

This 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:

```bash
# 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 --agi
```

If `pi-agi` is already installed, update that package and confirm the registry
version before starting Pi:

```bash
npm view pi-agi version
pi update npm:pi-agi
pi --agi
```

### Configure 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`:

```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`:

```bash
export MY_MODEL_API_KEY="your-key"
pi --model my-endpoint/my-model --agi
```

You 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:

```json
"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:

```bash
pi -e /absolute/path/to/pi-agi
```

For a repeatable local TUI in an owned tmux session:

```bash
npm run live -- start
npm run live -- attach
```

Inside 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:

```bash
pi --agi
```

Commands:

- `/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` — archive `goal.md`, `plan.md`, and `notes/` without parsing them; preserves `memory/` and runtime records
- `agi_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 session
- `agi_delegate` — spawn one detached worker, end the main turn, and wait for it automatically
- `agi_workers` — list named agents and their latest states
- `agi_worker` — inspect one named agent's concise status or final report
- `agi_steer` — send a durable mid-run instruction with exact acknowledgment
- `wait_for_agent` — end the turn and wait only for the active worker's completion, attention, or five-minute review
- `agi_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

```bash
npm install --ignore-scripts
npm run check
npm test
```

`npm 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/`](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:

```bash
npm run release:all
```

The package is retained under `.release/`. The benchmark itself is intentionally
not started by default because it consumes model budget. Start it only when
intended:

```bash
npm run release:all -- --run-arena
```

Future 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:

```bash
npm run release:all -- --publish-npm
```

Use `--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:

```bash
npm run live -- start
```

The 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:

```bash
npm run live -- attach
```

Then type `/agi` inside Pi. Alternatively, after the TUI has started:

```bash
npm run live -- enable
```

Interactive 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:

```text
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:

```text
/agi-workers
```

Or press `Ctrl+Alt+W`.

Test exact-ack steering while a worker is running:

```text
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:

```bash
npm run live -- status
```

Send a prompt without attaching:

```bash
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:

```bash
npm run live -- capture
npm run live -- capture /tmp/pi-agi-live-tui.txt
```

The `.pi/agi/` state, worker runtime files, and Pi sessions live inside the
workspace printed by `start`. For example:

```bash
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"
done
```

Inspect the concise worker trace:

```bash
for file in "$workspace"/.pi/agi/.runtime/agents/*/trace.log; do
  echo "=== $file ==="
  tail -n 80 "$file"
done
```

Inspect raw worker events for diagnostics:

```bash
for file in "$workspace"/.pi/agi/.runtime/agents/*/events.jsonl; do
  echo "=== $file ==="
  jq -c . "$file"
done
```

Inspect durable steering acknowledgments:

```bash
find -L "$workspace"/.pi/agi/.runtime/agents \
  -path '*/control/steer-ack/*.json' \
  -print -exec jq . {} \;
```

Stop only the session created by this checkout:

```bash
npm run live -- stop
```

The 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`:

```bash
PI_AGI_TMUX_SESSION=pi-agi-second npm run live -- start /tmp/my-agi-test
```

Requires 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:

```bash
npm run sandbox:mission -- preflight
```

On 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:

```bash
PI_AGI_SANDBOX_AGENT_DIR="$HOME/.pi/agent" \
  npm run sandbox:mission -- start
```

The 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:

```bash
npm run sandbox:mission -- attach
npm run sandbox:mission -- run
npm run sandbox:mission -- status
npm run sandbox:mission -- monitor 30
```

If 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:

```bash
npm run sandbox:mission -- recover-session
```

The 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:

```bash
npm run sandbox:mission -- capture /tmp/open-mission-tui.txt
npm run sandbox:mission -- export /tmp/open-mission-artifacts
npm run sandbox:mission -- stop
```

Each 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:

```bash
PI_AGI_SANDBOX_AGENT_DIR=/tmp/pi-agi-sandbox-agent \
  npm run sandbox:mission:local -- start
```
