diff --git a/docs/cli/agent.md b/docs/cli/agent.md index 04b106013..972f29f9e 100644 --- a/docs/cli/agent.md +++ b/docs/cli/agent.md @@ -99,7 +99,7 @@ fullsend agent set triage --fullsend-dir .fullsend --model xai-vertex/xai/grok-4 | `--model` | Model for this agent — an alias, a model id, or `provider/id` on pi | | `--effort` | Effort level for this agent (`low`, `medium`, `high`, `xhigh`, `max`) | -See [Runtimes — per-agent settings](../runtimes.md#per-agent-runtime-model-and-effort-in-configyaml) for precedence. +See [Runtimes — per-agent settings](../runtimes.md#per-agent-runtime-model-and-effort) for precedence. ## `agent remove` diff --git a/docs/cli/run.md b/docs/cli/run.md index aacb99984..7390b3ebd 100644 --- a/docs/cli/run.md +++ b/docs/cli/run.md @@ -48,7 +48,7 @@ The **Runtime** line shows which runtime was selected and the config source it w ## Runtime selection -The runtime for a run is resolved once, in this order: `--runtime` flag, `FULLSEND_RUNTIME`, `runtime:` on the agent's `agents:` entry in `config.yaml` / `.fullsend/config.yaml`, the repo-wide `runtime:` there, then the built-in `claude`. The same order applies to the model (`--model`, `FULLSEND_MODEL`, `model:` on the agent's `agents:` entry, harness `model:`, agent frontmatter; `FULLSEND_PI_MODEL` is a lower-precedence alias on pi) and to effort (`--effort`, `FULLSEND_EFFORT`, `effort:` on the agent's `agents:` entry, harness `effort:`). `` is the name given to `fullsend run` (`triage`, `code`, …); see [Runtimes — per-agent settings](../runtimes.md#per-agent-runtime-model-and-effort-in-configyaml). `FULLSEND_FALLBACK_MODELS=a,b` becomes Claude Code's `--fallback-model`; pi ignores it with a warning. +The runtime for a run is resolved once, in this order: `--runtime` flag, `FULLSEND_RUNTIME`, `runtime:` on the agent's `agents:` entry in `config.yaml` / `.fullsend/config.yaml`, the repo-wide `runtime:` there, then the built-in `claude`. The same order applies to the model (`--model`, `FULLSEND_MODEL`, `model:` on the agent's `agents:` entry, harness `model:`, agent frontmatter; `FULLSEND_PI_MODEL` is a lower-precedence alias on pi) and to effort (`--effort`, `FULLSEND_EFFORT`, `effort:` on the agent's `agents:` entry, harness `effort:`). `` is the name given to `fullsend run` (`triage`, `code`, …); see [Runtimes — per-agent settings](../runtimes.md#per-agent-runtime-model-and-effort). `FULLSEND_FALLBACK_MODELS=a,b` becomes Claude Code's `--fallback-model`; pi ignores it with a warning. The plan block prints `Runtime: (from )` and, when an override applied, `Model: (from )`; stderr carries `runtime: selected "" from ` (and `model: requested "" from `) for scripts. A value from the config file is labelled with the file path, suffixed ` agents.` when the agent's entry decided. An invalid override (unknown runtime, unknown effort level, an `agents:` entry that names no agent) fails before the sandbox is created. diff --git a/docs/guides/getting-started/choosing-a-runtime.md b/docs/guides/getting-started/choosing-a-runtime.md index dc614efb3..d4a3f446f 100644 --- a/docs/guides/getting-started/choosing-a-runtime.md +++ b/docs/guides/getting-started/choosing-a-runtime.md @@ -20,7 +20,7 @@ Fullsend supports multiple agent runtimes. A runtime is the program that runs in ## When and how the runtime is selected 1. **Next step — Configuring GitHub.** `fullsend github setup ` asks which runtime to use when run from a terminal; press Enter to keep `claude`. Passing `--runtime` skips the prompt. The setup PR it opens records the choice in `.fullsend/config.yaml` and describes how to change it. Nothing runs on this page — continue with [Configuring GitHub](configuring-github.md). -2. **Later — changing it.** Edit `runtime:` in the repo's `.fullsend/config.yaml` (the setup PR shows the key), or re-run `fullsend github setup --runtime `. To put one agent on a different runtime or model than the rest — say `code` on Claude Code while `triage` runs Grok on pi — set `runtime:` on that agent's `agents:` entry in the same file (`fullsend agent set code --runtime claude`); see [Runtimes — per-agent settings](../../runtimes.md#per-agent-runtime-model-and-effort-in-configyaml). Fleets managed through `repos.yaml` set `defaults.runtime` (or a per-entry `runtime`) — `fullsend repos set-default defaults.runtime pi` — and run `fullsend repos install`; see [fullsend repos](../../cli/repos.md). +2. **Later — changing it.** Edit `runtime:` in the repo's `.fullsend/config.yaml` (the setup PR shows the key), or re-run `fullsend github setup --runtime `. To put one agent on a different runtime or model than the rest — say `code` on Claude Code while `triage` runs Grok on pi — set `runtime:` on that agent's `agents:` entry in the same file (`fullsend agent set code --runtime claude`); see [Runtimes — per-agent settings](../../runtimes.md#per-agent-runtime-model-and-effort). Fleets managed through `repos.yaml` set `defaults.runtime` (or a per-entry `runtime`) — `fullsend repos set-default defaults.runtime pi` — and run `fullsend repos install`; see [fullsend repos](../../cli/repos.md). 3. **Per run — trying without changing the repo.** `fullsend run --runtime pi --model google-vertex/gemini-2.5-flash`, or the `FULLSEND_RUNTIME` / `FULLSEND_MODEL` / `FULLSEND_EFFORT` environment variables (flag beats environment beats the agent's `agents:` entry beats repo-wide config). In CI the same names work as repository variables. Reference: [fullsend run](../../cli/run.md) and [Runtimes — selecting and overriding](../../runtimes.md#selecting-a-runtime-and-model). ## Where to see what ran diff --git a/docs/guides/user/running-agents-locally.md b/docs/guides/user/running-agents-locally.md index a611c0aad..14c6abb38 100644 --- a/docs/guides/user/running-agents-locally.md +++ b/docs/guides/user/running-agents-locally.md @@ -202,6 +202,29 @@ fullsend run code \ --env-file fullsend-code.env ``` +### Choosing the runtime + + + +Every example above runs on **Claude Code**, the default runtime. Fullsend +also has an opt-in **pi** runtime, and any example on this page runs on it +by adding one flag to the same command: + +```bash +fullsend run triage \ + --fullsend-dir /tmp/fullsend-agents/ \ + --target-repo /tmp/target-repo/ \ + --env-file fullsend-gcp.env \ + --env-file fullsend-triage.env \ + --runtime pi +``` + +Everything else about runtimes lives in one place: [Agent +runtimes](../../runtimes.md) for selecting and overriding the runtime, +model and effort — per run, or per agent in `config.yaml` — and +[Pi › Running it locally](../../runtimes/pi.md#running-it-locally) for +what a local pi run needs, its models and its troubleshooting. + ### Remote resource flags When your harness references URL-based skills with transitive dependencies, @@ -272,362 +295,6 @@ For GitLab repositories, use `--forge gitlab` instead of `--mint-url`. The agent Status comment behavior is configured via `status_notifications` in `config.yaml`. See [Status Notifications](customizing-agents.md#status-notifications). -## Run a minimal agent on the pi runtime - -The pi runtime (`runtime: pi`) lets you run agents using -[pi](https://github.com/earendil-works/pi) instead of Claude Code inside the -sandbox. You can run a minimal pi agent locally without cloning the -`fullsend-ai/agents` fleet repo — `fullsend run --fullsend-dir` resolves a -config-registered agent to a local harness directory. - -> For background on the pi runtime, its security posture, and known -> constraints, see [Agent runtimes — Pi-specific known -> constraints](../../runtimes/pi.md). - -### Prerequisites (pi-specific) - -In addition to the general [prerequisites](#prerequisites) above, you need: - -| Requirement | Details | -|-------------|---------| -| Sandbox image with pi | `ghcr.io/fullsend-ai/fullsend-sandbox:latest` (must include `PI_VERSION`). Pull the latest to avoid stale cached images — see [Troubleshooting](#troubleshooting-pi-runtime) | -| GCP credentials | A service account key or `gcloud` ADC (`application_default_credentials.json`). The existing [GCP credentials](#get-google-cloud-platform-credentials) section applies — the pi Vertex provider reads the same variables | - -### Directory layout - -A working `--fullsend-dir` for the pi runtime needs the harness, a -`config.yaml` that both registers the agent and selects the runtime, and -supporting files for sandbox credentials and network policy. A bare -`config.yaml` + `harness/` is **not sufficient** — the agent starts but -fails when the sandbox has no credentials or Vertex egress: - -``` -pi-hello/ -├── config.yaml # registers the agent and selects runtime: pi -├── harness/ -│ └── pi-smoke.yaml # agent harness: image, model, host_files, policy -├── agents/ -│ └── pi-smoke.md # agent definition (frontmatter + task prompt) -├── policies/ -│ └── base.yaml # OpenShell sandbox policy (Vertex egress) -├── profiles/ -│ └── fullsend-vertex-ai.yaml # OpenShell egress allowlist for Vertex -├── providers/ -│ └── vertex-ai.yaml # OpenShell inference provider -└── env/ - └── gcp-vertex.env # sandbox-side GCP env vars (expand: true) -``` - -You can copy `policies/`, `profiles/`, `providers/` and `env/` from a -`fullsend-ai/agents` clone, or write them yourself — all four are short, -and their contents are given below so this example stays fleet-free. - -#### `config.yaml` - -The config must both register the harness **and** set `defaults.runtime: pi`. -The two are checked in different places and fail differently: - -- **No `agents:` entry** — placing `harness/pi-smoke.yaml` on disk is not - enough. `resolveAgentSource` looks the agent up in the config and, finding - nothing, fails with `resolving agent "pi-smoke": no config and agents-repo - fallback unavailable`. -- **No `defaults.runtime: pi`** — the run *succeeds* and silently uses the - default `claude` runtime (`backendFromConfigFile` → `ResolveFromConfig` - for this org-style config; a per-repo config resolves through - `ResolveForAgent`, which also honours `runtime:` on the agent's `agents:` entry). - The give-away is the `runtime: selected "claude"` line; pi is never - started. - -```yaml -version: "1" -agents: - - source: harness/pi-smoke.yaml -defaults: - runtime: pi -``` - -#### `harness/pi-smoke.yaml` - -The harness must include `host_files` to deliver GCP credentials into the -sandbox, and reference OpenShell profiles/providers for Vertex egress. -The `--env-file` flag sets the **runner** environment only — sandbox -environment comes from the harness via `env.sandbox` and `host_files` -([ADR 0055](../../ADRs/0055-unified-env-var-delivery.md)): - -```yaml -agent: agents/pi-smoke.md -policy: policies/base.yaml -openshell: - profiles: - - profiles/fullsend-vertex-ai.yaml -providers: - - providers/vertex-ai.yaml - -role: triage -slug: fullsend-ai-pi-smoke -model: haiku -image: ghcr.io/fullsend-ai/fullsend-sandbox:latest - -host_files: - - src: env/gcp-vertex.env - dest: /sandbox/workspace/.env.d/gcp-vertex.env - expand: true - - src: ${GOOGLE_APPLICATION_CREDENTIALS} - dest: /tmp/.gcp-credentials.json -``` - -#### `agents/pi-smoke.md` - -A minimal agent definition with a deterministic task: - -```markdown ---- -name: pi-smoke -description: Minimal smoke-test agent for the pi runtime. -tools: Bash(ls), Write -model: haiku ---- - -You are a smoke-test agent. Do exactly this, then stop: run `ls .` with the -bash tool, then use the write tool to create -`/sandbox/workspace/output/agent-result.json` containing exactly: - -{"action": "sufficient", "reasoning": "Smoke run: pi executed a tool call and wrote this file.", "comment": "pi runtime smoke test - no action needed."} - -Do not read or modify anything else. -``` - -The payload matches the `triage` result schema (`action`, `reasoning`, -`comment`) because the harness declares `role: triage`. This example passes -`--no-post-script`, so nothing validates it — but writing a valid result -keeps the example composable if you drop that flag or reuse the harness for -a real agent. - -#### `env/gcp-vertex.env` - -Sandbox-side GCP environment — these variables reach pi inside the -sandbox: - -```bash -export ANTHROPIC_VERTEX_PROJECT_ID={project-id} -export GOOGLE_CLOUD_PROJECT={project-id} -export CLOUD_ML_REGION=global -export GOOGLE_APPLICATION_CREDENTIALS=/tmp/.gcp-credentials.json -``` - -Two details this file depends on: - -- **`export` is required.** The sandbox sources `.env.d/*.env` with plain `.` - and no `set -a` (`internal/cli/run.go`), so a bare `KEY=value` becomes a - shell variable that pi — a child process — never sees. The symptom is the - Vertex extension disabling itself, or a credentials error, with the file - plainly present in the sandbox. -- **The filename must end in `.env`** — the sourcing loop globs `*.env`, so - `gcp-vertex.conf` would be copied and silently ignored. -- `GOOGLE_APPLICATION_CREDENTIALS` here is the **sandbox** path, matching the - `host_files` `dest` above — not the path on your machine. The `host_files` - entry uses `${GOOGLE_APPLICATION_CREDENTIALS}` from your *runner* shell to - find the key locally. - -#### `policies/base.yaml` - -The sandbox policy. Note the `read_only`/`read_write` prefixes — anything the -agent must read has to sit under one of them, which is why the pi Vertex -extension lives under `/usr/local/share` and not `/opt` (fullsend#6504): - -```yaml ---- -version: 1 -filesystem_policy: - include_workdir: true - read_only: [/usr, /lib, /proc, /dev/urandom, /app, /etc, /var/log] - read_write: [/sandbox, /tmp, /dev/null] -landlock: - compatibility: best_effort -process: - run_as_user: sandbox - run_as_group: sandbox -``` - -#### `profiles/fullsend-vertex-ai.yaml` - -The egress allowlist. Without it the sandbox blocks the inference call and pi -reports a model-not-found error rather than a network error. `**/node` is the -entry that matters for pi; `**/claude` serves the Claude Code runtime: - -```yaml ---- -id: fullsend-vertex-ai -display_name: Fullsend Vertex AI -description: Google Cloud APIs for Vertex AI inference -category: inference -endpoints: - - host: "*.googleapis.com" - port: 443 - protocol: rest - access: read-write - enforcement: enforce -binaries: - - "**/claude" - - "**/node" -``` - -#### `providers/vertex-ai.yaml` - -Binds that profile to the sandbox as an OpenShell provider: - -```yaml ---- -name: vertex-ai -type: fullsend-vertex-ai -credentials: - _NOOP_VERTEX_AI: "" -``` - -### Running the agent - -```bash -fullsend run pi-smoke \ - --fullsend-dir ./pi-hello \ - --target-repo /tmp/target-repo \ - --env-file fullsend-gcp.env \ - --no-post-script \ - --output-dir /tmp/fullsend-out -``` - -On a successful run, you see output like: - -``` -runtime: selected "pi" from ./pi-hello/config.yaml -→ Agent: claude-haiku-4-5 (v0.84.2) -→ Result: stop - Turns: 2 - Tokens: in=5169 out=372 reasoning=140 cache_create=0 cache_read=0 - ✓ Agent exited with code 0 (5.5s) -``` - -The `runtime: selected "pi"` line confirms the pi backend was used. - -Add `--keep-sandbox` when a run fails and you want to inspect the sandbox -afterwards — but delete it when you are done (`openshell sandbox delete -`), since kept sandboxes are not cleaned up for you. - -### Run artifacts - -After a successful run, the output directory contains: - -``` -/tmp/fullsend-out// -├── logs/ -│ ├── openshell-sandbox.log # OCSF events (network, policy decisions) -│ └── openshell-gateway.log -├── iteration-1/ -│ ├── output/ -│ │ └── agent-result.json # whatever the agent wrote to output/ -│ ├── output.jsonl # the agent's raw event stream -│ └── transcripts/ -│ └── -_.jsonl # pi session transcript -├── metrics.json # includes "runtime": "pi" -├── run-telemetry.jsonl -└── security/ # findings.jsonl appears only when a - # security hook actually reports something -``` - -Everything lives under a per-run directory named after the sandbox -(`fs--`), so `--output-dir` accumulates one subdirectory per run -rather than being overwritten. A clean run leaves `security/` empty — that is -the expected result, not a missing artifact. - -Key artifacts to verify: - -- **`metrics.json`** — check `"runtime": "pi"` to confirm the pi backend - was used -- **Session transcript** — the `.jsonl` file under `transcripts/` contains - pi's session events; look for `toolCall` / `toolResult` entries -- **`pi-debug.log`** — appears when `--debug='*'` is passed (note the `=` - syntax — see [Troubleshooting](#troubleshooting-pi-runtime)) - -Use the `analyze-transcript` skill to inspect the session: - -```bash -python3 skills/analyze-transcript/analyze-transcript.py summary \ - /tmp/fullsend-out//iteration-1/transcripts/.jsonl -``` - -``` -Agent: pi-smoke -Model: claude-haiku-4-5 -Messages: 7 (4 user, 3 assistant) -Tokens: 5485 in / 531 out / 0 cache-read / 0 cache-create - -Tool calls: - bash 2 - write 1 - -Stop reasons: toolUse=2, stop=1 -``` - -`tools` and `conversation` are the other two subcommands worth knowing — -`tools` for a per-call table, `conversation` for the readable flow. - -### Pi runtime knobs - -| Variable | Description | -|----------|-------------| -| `FULLSEND_MODEL` (or `fullsend run --model`) | Override the model for the run on any runtime; `FULLSEND_PI_MODEL` is kept as a pi-only alias | -| `FULLSEND_RUNTIME` (or `--runtime`) | Override the runtime selected by `config.yaml` | -| `FULLSEND_EFFORT` (or `--effort`) | Override the harness effort level | -| `FULLSEND_PI_PROVIDER` | Override the inference provider (runner env) | -| `FULLSEND_PI_BASH_ALLOWLIST` | Set to `enforce` to make the Bash first-token allowlist block instead of warn | - -### Security hooks - -Security hooks are enabled by default on pi. The run refuses to start -(exit 97) without the hook adapter — this is intentional (fail-closed). -The runner checks the adapter's SHA-256 before sourcing the agent-writable -`.env`, so a tampered adapter is rejected. - -A planted `.pi/extensions/evil.js` in the target repo is **not** loaded -when `--no-approve` is set (the default in fullsend runs). Pi's -`defaultProjectTrust: never` setting in the sandbox config prevents -repo-owned extensions, skills, and settings from loading. - -### Troubleshooting pi runtime - -**`pi preflight: pi --version exited 127: sh: 1: pi: not found`** -- The sandbox image is stale and predates the pi layers. Pull the latest: - ```bash - podman pull ghcr.io/fullsend-ai/fullsend-sandbox:latest - ``` - -**`[pi-anthropic-vertex] disabled: set GOOGLE_CLOUD_PROJECT ...`** -- The harness is missing `host_files` and/or the OpenShell egress profile. - Sandbox environment comes from the harness, not from `--env-file`. See - the [harness layout](#directory-layout) above. - -**`--debug "..."` fails with `accepts 1 arg(s), received 2`** -- `--debug` is an optional-value flag. Use `--debug='*'` (with `=`), not - `--debug "*"`. - -**Agent fails silently — check `pi-debug.log`** -- When a custom harness is missing `host_files` or the OpenShell profile, - the failure appears in `pi-debug.log` inside the run directory, not in - the runner's terminal output. - -### Platform notes (pi) - -**Linux (Fedora, rootless Podman):** verified end-to-end. The general -[Linux platform notes](#linux) apply. - -**macOS (Apple Silicon):** sandbox creation, the pi bootstrap and preflight, -loading the Vertex extension, and model-id translation are verified on -`darwin/arm64` (macOS 26.5.2, podman machine, `openshell` from Homebrew). -The general [macOS platform notes](#macos) apply. In particular: -- Use `/private/tmp/...` for bind mounts (not `/tmp/...`) -- If the sandbox image architecture differs from the host, set - `FULLSEND_SANDBOX_ARCH` and provide a Linux binary with - `--fullsend-binary` - ## Run from a container Instead of downloading the fullsend binary and installing its host-side @@ -702,10 +369,6 @@ approach. ## Platform notes -> Running the **pi runtime**? Its platform-specific notes live with the rest -> of the pi walkthrough: [Platform notes -> (pi)](#platform-notes-pi). - ### macOS - **Podman machine**: ensure the Podman machine is running (`podman machine start`) before invoking fullsend. The CLI does not start it automatically. @@ -890,3 +553,10 @@ curl -sf https://api.example.com/healthz diff <(grep DENIED run-1/logs/openshell-sandbox.log) \ <(grep DENIED run-2/logs/openshell-sandbox.log) ``` + +## See also + +- [Agent runtimes](../../runtimes.md) — choosing a runtime and overriding runtime, model and effort per run or per agent +- [Pi › Running it locally](../../runtimes/pi.md#running-it-locally) — what a local pi run needs, its models and troubleshooting +- [fullsend run](../../cli/run.md) — the full flag reference +- [Configuring agent behavior](customizing-agents.md) — harness configuration and `base:` composition diff --git a/docs/runtimes.md b/docs/runtimes.md index 649610373..473beb5d2 100644 --- a/docs/runtimes.md +++ b/docs/runtimes.md @@ -91,7 +91,7 @@ In CI these are repository variables of the same name, plain or role-prefixed the agent's `agents:` entry in `.fullsend/config.yaml` instead. Harness `env.runner` does **not** reach the `fullsend` process. -### Per-agent runtime, model and effort in config.yaml +### Per-agent runtime, model and effort The `agents:` list is the per-agent place in `config.yaml`: an entry names an agent and can set its `runtime`, `model` and `effort`. A built-in agent (`triage`, `code`, `review`, `fix`, `retro`, @@ -111,7 +111,19 @@ agents: model: haiku ``` -Or from the CLI: `fullsend agent set code --runtime claude --model sonnet --effort high`. +Or from the CLI, which validates the entry before writing it: + +1. `fullsend agent set code --fullsend-dir .fullsend --runtime claude --model sonnet --effort high` +2. `fullsend agent list --fullsend-dir .fullsend` shows the settings next to each agent — + `code (built-in) [runtime=claude model=sonnet effort=high]`, or the `source:` path for a custom + agent. +3. The next `fullsend run code` names the entry as the source — + `Runtime: claude (from agents.code)` — and a `--runtime`/`--model` flag on that + run still wins. + +An invalid value is refused before the write — `invalid effort "turbo": must be one of low, medium, +high, xhigh, max` — and the same check runs on every `fullsend run`, so a hand-edited entry fails the +run before a sandbox starts rather than being skipped. A `source:` entry needs no `name:` — the agent's name is derived from the source file (`harness/lint.yaml` → `lint`, ADR 0058), and that is the name the settings, `fullsend run lint` diff --git a/docs/runtimes/pi.md b/docs/runtimes/pi.md index d5c7059e7..7843d84a3 100644 --- a/docs/runtimes/pi.md +++ b/docs/runtimes/pi.md @@ -70,9 +70,65 @@ endpoints answer `FAILED_PRECONDITION` — so region variables are deliberately | Extra knobs | `FULLSEND_PI_PROVIDER` (prefix for bare ids), `FULLSEND_PI_BASH_ALLOWLIST=enforce` | | Not supported | Sub-agents, fallback chains, `plugins:`, Bedrock/Azure providers | -**Running it locally?** See [Run a minimal agent on the pi -runtime](../guides/user/running-agents-locally.md#run-a-minimal-agent-on-the-pi-runtime) — no fleet repo -required. +## Running it locally + +Complete [Running agents locally](../guides/user/running-agents-locally.md) first — the CLI, +OpenShell, credentials and the fleet clone are the same. Every example there runs on pi by adding +`--runtime pi` to the same command: + +```bash +fullsend run triage \ + --fullsend-dir /tmp/fullsend-agents/ \ + --target-repo /tmp/target-repo/ \ + --env-file fullsend-gcp.env \ + --env-file fullsend-triage.env \ + --runtime pi +``` + +The plan block confirms the selection — overridden values carry their source, harness defaults +print bare — and `metrics.json` records the same (`runtime`, `runtime_source`, `requested_model`, +`override_source`): + +``` + Model: opus + Effort: high + Runtime: pi (from --runtime flag) +... +runtime: selected "pi" from --runtime flag +... +→ Agent: claude-opus-4-6 (v0.84.2) +→ Result: stop + ✓ Agent exited with code 0 (131.9s) +``` + +Pick a model the same way — on pi the model name is also the provider choice, and the same Vertex +credentials cover Gemini: + +```bash +fullsend run triage ... --runtime pi --model google-vertex/gemini-3.7-flash +``` + +To keep an agent on pi (or off it) without passing flags every time, set `runtime:`/`model:` on +its `agents:` entry in `config.yaml` — see [per-agent settings](../runtimes.md#per-agent-runtime-model-and-effort). + +What a local pi run needs, beyond the guide: + +- **fullsend v0.37.0+** — the first release that carries the pi runtime; the release download + and the container image both work as-is. +- **A sandbox image that includes pi** — `ghcr.io/fullsend-ai/fullsend-sandbox` v0.37.0+ (the image + bakes `PI_VERSION`). A stale image fails preflight with `pi preflight: pi --version exited 127`; + `podman pull ghcr.io/fullsend-ai/fullsend-sandbox:latest` fixes it. +- **Platforms** — verified end to end on macOS Apple Silicon (podman machine, Homebrew `openshell`) + and Fedora with rootless Podman; the guide's platform notes apply unchanged. +- **`review` and `retro`** complete with schema-valid results but in a single context — pi has no + sub-agent tool, so the parallel reviewer roster is not exercised (see [Not yet exercised](#not-yet-exercised)). +- **Knobs** — `FULLSEND_PI_PROVIDER` sets the provider for bare model ids (default + `anthropic-vertex`); `FULLSEND_PI_BASH_ALLOWLIST=enforce` makes the Bash first-token allowlist + block instead of warn. +- **Security hooks are fail-closed** — a missing or modified hook adapter stops the run with exit + 97 by design; repo-owned `.pi/` content is never loaded. +- **Debugging** — `--debug='*'` (the `=` is required); sandbox-side failures land in `pi-debug.log` + inside the run directory, next to the transcripts, not in the runner's output. ## Behaviour differences worth knowing @@ -92,9 +148,10 @@ required. ## Not yet exercised `runtime: pi` is selectable and has been run end to end, but no **fleet lifecycle** run on Vertex is -recorded yet. Pilot on a disposable org with `triage`/`prioritize` before `code`/`fix`. `review` and -`retro` are unsupported — they need sub-agents, and would run in a single context without per-persona -models. `extension_error` events are not mapped. +recorded yet. Pilot on a disposable repo with `triage`/`prioritize` before `code`/`fix`. `review` and +`retro` run to schema-valid results, but in a **single context**: pi has no sub-agent tool, so the +parallel persona roster and its per-persona models are never exercised — treat them as unsupported +for that purpose. `extension_error` events are not mapped. ## Troubleshooting @@ -109,6 +166,22 @@ table above, not a shared one. **403 `PERMISSION_DENIED` on a Vertex call.** The credentials work but the model is not enabled in that project's Model Garden, or the provider resolved a different project than you expect. +**`[pi-anthropic-vertex] disabled: set GOOGLE_CLOUD_PROJECT ...`.** The sandbox environment comes +from the harness (`host_files`, `env.sandbox`), not from `--env-file`, which only reaches the runner +process (ADR 0055). Files sourced from `.env.d/` need `export` on each line. The fleet harnesses +already wire this; a custom harness must too. + +**The run used Claude instead of pi.** The runtime falls back to `claude` when neither the config's +`runtime:` (repo-wide or on the agent's `agents:` entry) nor `--runtime`/`FULLSEND_RUNTIME` selects +pi; the plan block's `Runtime:` line and stderr's `runtime: selected ...` show which one ran and why. + +**`--debug "..."` fails with `accepts 1 arg(s)`.** `--debug` takes an optional value: write +`--debug='*'` (with `=`). + +**The agent fails with nothing in the terminal.** Sandbox-side pi failures land in `pi-debug.log` +inside the run directory, next to the transcripts; kept sandboxes must be removed manually +(`openshell sandbox delete `). + **The model says it is a different model than you selected.** Do not trust the reply — a model asked about itself will often repeat whatever the conversation history said. `metrics.json` records the model that actually served the run, and the session JSONL under `transcripts/` records the @@ -117,5 +190,5 @@ provider and model per message. ## See also - [Agent runtimes](../runtimes.md) — choosing and selecting a runtime -- [Running agents locally](../guides/user/running-agents-locally.md#run-a-minimal-agent-on-the-pi-runtime) — a local pi run, no fleet repo required +- [Running agents locally](../guides/user/running-agents-locally.md) — the local-run flow that [Running it locally](#running-it-locally) builds on - [pi runtime internals](../contributing/runtime-implementation.md#pi-runtime-internals-6464) — verification provenance and what to re-check on a version bump