Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CodeyBox.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<Project Path="src/CodeyBox.Agents.Copilot/CodeyBox.Agents.Copilot.csproj" />
<Project Path="src/CodeyBox.Agents.Antigravity/CodeyBox.Agents.Antigravity.csproj" />
<Project Path="src/CodeyBox.Agents.Crock/CodeyBox.Agents.Crock.csproj" />
<Project Path="src/CodeyBox.Agents.Pi/CodeyBox.Agents.Pi.csproj" />
<Project Path="src/CodeyBox.Agents/CodeyBox.Agents.csproj" />
<Project Path="src/CodeyBox.AdminSeed/CodeyBox.AdminSeed.csproj" />
<Project Path="src/CodeyBox.Api/CodeyBox.Api.csproj" />
Expand Down
3 changes: 3 additions & 0 deletions docs/concepts/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ tooling, not in the agent runner contract.
| `opencode` | `opencode` | `OPENCODE_AUTH_JSON`, written to `~/.local/share/opencode/auth.json` | `CODEYBOX_OPENCODE_AUTH_FILE` |
| `antigravity` | `agy` | `CODEYBOX_ANTIGRAVITY_OAUTH_CREDS_JSON` (OAuth bundle, written to `~/.gemini/antigravity-cli/antigravity-oauth-token`) | `CODEYBOX_ANTIGRAVITY_OAUTH_CREDS_JSON` |
| `crock` | `crock` | `CROCK_CONFIG_JSON` (file-materialised to `~/.crockcode/config.json`) | `CODEYBOX_CROCK_CONFIG_JSON` |
| `pi` | `pi` | `ANTHROPIC_API_KEY` (provider API key; other providers use their own variable from pi's provider table β€” see [Pi quirks](../reference/agent-quirks.md#pi-coding-agent-pi)) | `CODEYBOX_PI_API_KEY` |

The sandbox-side env name is what the agent CLI reads. The host-side name is
what the orchestrator looks up when building the credential bundle β€” for most
Expand Down Expand Up @@ -49,6 +50,7 @@ the most common cause of fresh-class dispatch failures.
| `cursor` | `curl -fsSL https://cursor.com/install \| bash` | Installs as `agent` (not `cursor-agent`). See [Cursor quirks](../reference/agent-quirks.md#cursor-cli-agent). |
| `opencode` | `curl -fsSL https://opencode.ai/install \| bash` | Plaintext stdout only β€” no structured stream. |
| `antigravity` | *operator-supplied β€” stage the `agy` binary on the host and ship it via `CodeyBox:MultipassExecutableProvisions` or `CodeyBox:Incus:ExecutableProvisions`, matching the selected provider* (see [Antigravity quirks](../reference/agent-quirks.md#google-antigravity-cli-agy)). Do not use `curl -fsSL https://antigravity.google/cli/install.sh \| bash`: that URL serves the landing page, not a script, and piping HTML into `bash` fails silently when the runcmd ends with `\|\| true`. | Installs the proprietary `agy` CLI on the non-login sandbox PATH. Multi-model gateway β€” each gateway model id is a separate quota bucket. Configure each accepted model as its own `AgentClass` member; the router gates per-model via the existing `(AgentKind, ModelId)` exhaustion key. |
| `pi` | `npm install -g --ignore-scripts @earendil-works/pi-coding-agent` | MIT-licensed; needs Node.js on the image. `--ignore-scripts` skips npm lifecycle scripts during install. See [Pi quirks](../reference/agent-quirks.md#pi-coding-agent-pi). |

Verify each command against its upstream install docs at the time of baking β€”
versions and install URLs change. Multipass and Incus keep independent bake
Expand Down Expand Up @@ -168,6 +170,7 @@ credentials before they waste expensive compute.
| `copilot` | *(no probe)* β€” always passes | β€” |
| `cursor` | *(no HTTP probe β€” Cursor exposes no public usage endpoint)* β€” verifies the credential bundle carries `CODEYBOX_CURSOR_AUTH_JSON`; real auth check happens on first CLI call | β€” |
| `opencode` | *(no network call)* β€” credential-presence check only | `OPENCODE_AUTH_JSON` |
| `pi` | *(no network call β€” pi fronts 30+ providers, so no single endpoint validates the credential)* β€” verifies the bundle carries `ANTHROPIC_API_KEY`; real auth check happens on first CLI call | `ANTHROPIC_API_KEY` |

Each probe sends the minimal possible request (`max_tokens=1`). A 2xx response
means the credential is valid. 401/403 is classified as `"auth"` failure.
Expand Down
1 change: 1 addition & 0 deletions docs/operating/costs.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ events.
| Agent | Bundled rates | Why |
|---|---|---|
| `claude`, `codex`, `gemini` | yes | the provider publishes per-token list prices |
| `pi` | yes, for the shipped Anthropic-backed member | pi is a multi-provider front; the bundled bucket mirrors Anthropic on-demand rates keyed by the bare `message.model` id. Operators fronting other providers add that provider's list prices under `CodeyBox:AgentPricing`. |
| `opencode` | yes, estimated | OpenCode Go is subscription-priced. Bundled rates are a single subscription-equivalent USD/M per model (same value for input, cached, and output), derived from the $12/5h budget, each model's requests-per-5h limit, and the token mix documented at [opencode.ai/docs/go](https://opencode.ai/docs/go). Keys are `opencode-go/<model-id>`. |
| `cursor`, `copilot` | no | flat-rate subscriptions with no published per-token price |

Expand Down
82 changes: 82 additions & 0 deletions docs/reference/agent-quirks.md
Original file line number Diff line number Diff line change
Expand Up @@ -669,3 +669,85 @@ root, so a misconfiguration fails as an Infrastructure error rather than a
catastrophic host mount. Only sandbox providers that preserve a live local Unix
socket support this fallback. The daemon owns the tunnel + MCP tools and, if
configured with its own key, is what bills the batch.

### Pi coding agent (`pi`)

**Install in the sandbox image** β€” add the install line to
`CodeyBox:MultipassExtraRuncmd` or `CodeyBox:Incus:ExtraRuncmd`, matching the
selected provider (verified against pi 0.85.1, 2026-09-14):

```sh
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
```

MIT-licensed ([repo](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent),
npm `@earendil-works/pi-coding-agent`). Lean terminal agent (Read/Write/Edit/Bash
tools) fronting 30+ providers through its pi-ai layer.

**Non-interactive invocation.** The runner drives `pi --mode json` with the
prompt on stdin and NO positional prompt argument:

```sh
pi --mode json --no-session --offline [--model <id>] [--thinking <level>]
```

`--mode json` was chosen over raw `-p` and `--mode rpc`: `-p` prints only the
final response text (usage, dispatch model, and terminal error shape would be
unrecoverable), while `--mode rpc` is a bidirectional prompt/response protocol
needing a driver loop for no extra signal on a one-shot run. `--mode json`
exits after the run like `-p` but emits one JSON event per stdout line, with
cumulative `usage {input, output, cacheRead, cacheWrite, totalTokens}` and
`model` on the assistant message frames. `--no-session` skips persisting
`~/.pi/agent/sessions` (the VM is ephemeral); `--offline` disables pi.dev
startup network (version checks, telemetry). The runner deliberately passes
NEITHER `--approve` NOR `--no-approve`: the sandbox tree is untrusted, so the
`ask` default (ignore project resources) is the safe posture.

**Exit-zero errors.** Pi exits 0 even when the run dies before producing
output (verified: missing API key and a provider 401 both exit 0 with the
cause only in the event stream). The runner lifts the terminal error
(`stopReason: "error"` + `errorMessage` on the message frames, or the
plaintext `No API key found for the selected model.`) into
`TerminalDiagnostic`, so the pipeline's no-changes branch parks quota/auth
give-ups instead of dead-lettering them as "produced no changes" β€” the same
shape `agy` has.

**Authentication.** Provider API keys from the environment (`ANTHROPIC_API_KEY`,
`OPENAI_API_KEY`, `GEMINI_API_KEY`, … β€” full table in pi's `providers.md`).
The shipped credential mapping wires host `CODEYBOX_PI_API_KEY` to
sandbox-side `ANTHROPIC_API_KEY`; operators fronting other providers add that
provider's variable to the mapping. Interactive `/login` state is not shipped
into sandboxes. Prefer `provider/id`-qualified `ModelId` values
(e.g. `anthropic/claude-haiku-4-5`): pi's default provider is google, so a bare
id can resolve against the wrong catalog.

**Reasoning effort.** `ReasoningMode` maps 1:1 onto `pi --thinking`
(`off|minimal|low|medium|high|xhigh|max`). Only exact allowlist members are
emitted; anything else is ignored rather than passed through to fail the CLI.

**Quota probe.** Ships as Unknown-only: pi has no meterable quota endpoint
(it fronts 30+ providers). The router's `QuotaUnknownPolicy` (default
`UseObservedFailures`) gates dispatch via observed failure history, and
`PiQuotaFailureDetector` classifies the relayed provider errors (401/auth
shapes β†’ Unauthorized; 429/rate-limit β†’ RateLimitExceeded; 402/billing β†’
LimitReached) with operator-extensible rows under
`CodeyBox:QuotaFailurePatterns:pi`.

**Smoke probes.** Host-side `PiSmokeProbe` is a credential-presence check only
(no network call β€” no single endpoint validates a multi-provider credential).
`PiInVmSmokeProbe` execs `pi --version` plus a `pi --help | grep -q -- --mode`
assertion, so a pi build that dropped the JSON event stream benches at smoke
time instead of failing first dispatch.

**Model-list probe.** `pi --list-models` needs an authenticated provider plus
network, so the host-side probe returns the curated `PiKnownModels` seed
instead of live-reading the catalog. Operator `ModelId` values absent from the
seed surface as a startup warning, never a hard reject (pi accepts fuzzy and
provider-qualified ids beyond the seed).

**Cost attribution.** `PiCostExtractor` takes the latest (cumulative) `usage`
frame and the bare `message.model` id (pi strips the `provider/` qualifier in
`message.model`). Bundled rates live in `agent-pricing-defaults.json` under the
`pi` bucket for the shipped Anthropic-backed member; operators fronting other
providers add that provider's list prices there (or under
`CodeyBox:AgentPricing`) keyed by the bare model id.
19 changes: 19 additions & 0 deletions src/CodeyBox.Agents.Pi/CodeyBox.Agents.Pi.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\CodeyBox.Agents\CodeyBox.Agents.csproj" />
<ProjectReference Include="..\CodeyBox.Core\CodeyBox.Core.csproj" />
<ProjectReference Include="..\CodeyBox.HostProcess\CodeyBox.HostProcess.csproj" />
</ItemGroup>

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="CodeyBox.Tests" />
</ItemGroup>

</Project>
Loading
Loading