# pi-agent-substrate

Use a persistent remote workspace from Pi or Oh My Pi through [Agent Substrate](https://github.com/agent-substrate/substrate). Keep the local conversation and terminal; run explicit remote file and process tools through the Substrate Environment API.

| Capability | Implementation / verification |
| --- | --- |
| Pi and Oh My Pi | Separate adapters, one shared client and workspace state machine |
| Remote files and shell | Verified against the real upstream guest in both harness loaders |
| Background processes | Start, status, paginated logs, termination, durable tracking |
| Environment lifecycle | Create, attach, status, suspend, confirmed deletion |
| Idle suspension | Opt-in; blocked during active calls, tracked jobs, or uncertain process state |
| Checkpoint/restore proof | Verified end-to-end on a self-hosted kind cluster (suspend to FULL-scope snapshot, resume with filesystem and same-process memory restored); see [Verification](#verification)[^verification] |

## Prerequisites

- Pi 0.86.1 with Node 22.19+ or Oh My Pi 18.2.6 with Bun 1.3.14+.[^versions]
- A private [Substrate cluster](https://github.com/agent-substrate/substrate) and deployed [Environment API](https://github.com/agent-substrate/env). Follow those upstream projects' deployment instructions.
- A working actor template containing `ate-env-guest` and `sh`.
- An operator-provided HTTPS endpoint or a loopback tunnel to `ate-env-api`. Use the root URL, without credentials, path, query, or fragment.

This package does not provision infrastructure. A standalone `ate-env-guest` supports the guest smoke test, but does not provide environment creation or suspension.

## Try the checkout

Run these commands from this package directory in **bash or zsh**:

```sh
bun install --frozen-lockfile

# Choose the installed harness:
pi -e ./src/pi.ts --substrate-endpoint http://127.0.0.1:17777
omp -e ./src/omp.ts --substrate-endpoint http://127.0.0.1:17777
```

Replace the endpoint with your private service or established tunnel. For an existing environment, add `--substrate-env my-workspace` on a new session. Otherwise, enter these commands inside Pi or OMP:

```text
/substrate create my-workspace
/substrate status
```

Ask the agent to use `substrate_*` tools for remote work. For example: “Use substrate_shell to inspect the remote workspace and substrate_write to create hello.txt there.”

To load the package on subsequent launches, install the local checkout using your chosen harness, then restart it:

```sh
# bash/zsh; from this package directory
pi install .
# Or:
omp install .
```

The package contains both `pi.extensions` and `omp.extensions` manifest entries. Do not load both adapters in the same harness. To distribute the local package without publishing it:

```sh
npm pack
```

## Commands

| In-session command | Use |
| --- | --- |
| `/substrate create <id> [template]` | Create and attach a new environment |
| `/substrate attach <id>` | Attach an existing environment |
| `/substrate status` | Inspect the environment and saved binding |
| `/substrate suspend` | Explicitly pause the environment, including background jobs |
| `/substrate detach` | Remove the session binding; retain remote files and processes |
| `/substrate delete <id>` | Permanently delete the attached environment after interactive confirmation |

Use lowercase DNS-label environment/template IDs, up to 63 characters. Deletion requires the exact attached ID and interactive confirmation; it is unavailable in headless mode.

For normal wake-up, perform the next remote file or process operation. The upstream service handles activation on guest traffic. Environment status inspection alone does not request wake-up.

## Tools

| Tool | Parameters |
| --- | --- |
| `substrate_read` | Remote `path`; optional `encoding`: `utf8` or `base64` |
| `substrate_write` | Remote `path`, complete `content`; optional `encoding` |
| `substrate_shell` | Shell `command`; optional remote `cwd`, `background`, `timeoutSeconds` |
| `substrate_process` | `processId`, `action`: `status`, `logs`, or `kill`; optional `stdoutOffset` and `stderrOffset` |

Use `substrate_shell` for remote editing, search, and Git. The remote shell is `sh`; no local shell execution or file synchronization takes place through these tools.

Files and collected output are bounded to **1 MiB**. Read binary content as base64. Foreground shell calls have a maximum duration of **120 seconds**. On foreground cancellation, timeout, output overflow, or output failure, the extension attempts to terminate the known remote process. On cleanup failure, inspect its reported process ID before continuing.

For longer jobs, use `background: true`, then inspect with `substrate_process`. For output larger than one page, use `action: "logs"` and the returned byte offsets to retrieve subsequent pages. Logs are UTF-8 text; page boundaries can split a multibyte character. A status or kill operation reconciles completed tracked jobs.

## Configuration

Flags override the corresponding environment variables. Set secrets through your normal environment/secret manager before starting the harness, never in a URL or command argument.

| Flag | Environment variable | Default |
| --- | --- | --- |
| `--substrate-endpoint` | `SUBSTRATE_ENDPOINT` | Required when using remote tools |
| `--substrate-atespace` | `SUBSTRATE_ATESPACE` | `ate-env` |
| `--substrate-env` | `SUBSTRATE_ENV` | No automatic attachment |
| `--substrate-idle-seconds` | `SUBSTRATE_IDLE_SECONDS` | `0`, disabled |
| None | `SUBSTRATE_TOKEN` | No bearer token |

Use HTTPS for non-loopback endpoints. Standard TLS certificate verification remains enabled. No token is stored in the session binding. The guest/API must be protected by your network and authentication boundary.[^security]

Idle suspension is an explicit opt-in for an environment dedicated to this session. Set a value between 0 and 86400 seconds. Active operations, tracked processes, or uncertain process starts prevent automatic suspension. Inspect completed processes to reconcile tracking. After a suspension attempt, no new attempt is scheduled until further workspace activity.

## Session and isolation boundaries

- Resume the same conversation with the same endpoint and atespace to restore its saved attachment and process tracking. This also works with a custom harness session directory.
- Forked/new sessions do not silently inherit their parent's remote attachment. Attach explicitly when sharing is intentional.
- Detach explicitly before switching away from tracked or uncertain processes. Detachment neither kills nor suspends the environment.
- A lost process-start response is ambiguous: the process may already be running. The extension retains uncertainty and does not replay the command. Inspect the remote environment before explicitly detaching and reattaching.
- Closing the harness does not delete or suspend the remote environment.
- Local built-in tools, local shell shortcuts, other extensions, and subagents remain outside this routing boundary. Use the explicit remote tools consistently. For full-harness isolation, run the harness itself inside the sandbox as a separate deployment.
- Idle tracking covers processes started through these tools. Shell-daemonized descendants and work started by other clients are not discoverable through the current guest API; keep automatic suspension disabled for shared or externally managed environments.

## Verification

Run from this checkout in **bash/zsh**:

```sh
bun run check
bun test
```

For an isolated real upstream guest, with the endpoint already available:

```sh
SUBSTRATE_GUEST_ENDPOINT=http://127.0.0.1:17779 bun run smoke:guest
```

The guest scenario writes disposable files, starts processes, checks output/exit codes and offsets, exercises cancellation and bounds, then removes its successful file fixture. It does not call a model.

For loader/tool verification, set `SUBSTRATE_HARNESS_LOADER` to the actual installed harness's `extensions/loader` module and `SUBSTRATE_GUEST_ENDPOINT` to the same guest. Run `node scripts/smoke-harness.ts pi` or `bun scripts/smoke-harness.ts omp`. This uses the real loader and live guest, with a synthetic session context to isolate the test from your normal sessions.

For the remaining full-cluster proof, configure `SUBSTRATE_ENDPOINT`, optional `SUBSTRATE_ATESPACE`, and optional `SUBSTRATE_TOKEN`, then run:

```sh
bun run smoke:cluster
```

The cluster scenario creates a unique environment, writes a file, and starts an in-memory shell counter. It suspends the environment, resumes through guest traffic, and checks the file plus a second increment in the same process. On success it deletes only that disposable environment. On failure it reports the environment ID and retains the environment for inspection.

Machine-readable results: [verification.json](verification.json). Reproduction scenarios: [scripts](scripts/). Protocol source: [agent-substrate/env at the pinned revision](https://github.com/agent-substrate/env/tree/ab40c7bfb2049af1a7aade9e7bf9c6cac925b5ca).

## License

Apache-2.0. Vendored upstream protocol notices are preserved; see [LICENSE](LICENSE) and [NOTICE](NOTICE).

[^verification]: Real guest file/process operations, harness loading, and the full-cluster suspend/resume proof were verified; the cluster proof ran against a self-hosted kind deployment of Substrate at commit `bb0effed` with the Environment API at the pinned revision. No suspension-latency or density claims are made.
[^versions]: Verification covers the listed Pi release and the recorded OMP source build, not every older or future release. Both extension APIs and the upstream Environment API can change.
[^security]: Upstream Substrate documents authentication without control-plane RBAC. Keep this deployment private and operator-controlled; a client-side namespace is not a tenant-authorization boundary.
