Harnesses
A harness is the underlying CLI that drives a workspace agent — the binary
that runs in tmux. The model is what the binary calls. They are picked
independently per spawn.
Supported today: claude-code (default) and pi. For the wider field of
coding-agent harnesses Panopticon could adopt next — and how each one handles
skills, MCP, and AGENTS.md — see the Harness Landscape.
| Harness | Binary | Default for | Notes |
|---|
claude-code | Anthropic Claude Code | All workflows | Default if you don’t pick. Required for Anthropic-subscription users. |
pi | Pi Coding Agent | Multi-provider work, large workspaces | Adds Pi-style RPC over a named pipe; reads ~/.claude/skills for skill parity. |
Supported harnesses
Claude Code (default)
- Shipped by Anthropic; Panopticon installs no special integration — drop in
claude and run.
- Works with both subscription auth (Claude Code OAuth) and API-key auth.
- All role runs (
plan, work, review, test, ship) default here.
Pi (alternative)
- Adds RPC mode (
pi --mode rpc) so Panopticon can write structured commands to
Pi’s stdin via a named pipe (mkfifo at ~/.panopticon/agents/<agentId>/rpc.in).
- Vendored extension at
packages/pi-extension/ reports lifecycle events:
session_start writes a ready.json, tool_execution_end updates a
heartbeat, and a /pan-done slash command writes a completion marker.
- Multi-provider — drives Anthropic, OpenAI, Google, OpenRouter, Minimax,
DashScope through Pi’s own provider routing.
Installing Pi
Pi is not auto-installed. Install it once, then run pan doctor to confirm.
npm install -g @mariozechner/pi-coding-agent
# or — install from the pi-mono repo:
# git clone https://github.com/badlogic/pi-mono && cd pi-mono && ./install.sh
pan doctor # confirms `pi` is on PATH and reports the version
pan doctor --strict # exits non-zero if Pi is missing (good for CI)
pan doctor also checks that packages/pi-extension/dist/index.js exists in the
Panopticon workspace. If it doesn’t, run:
cd packages/pi-extension && npm run build
The minimum supported Pi version is pinned in src/cli/commands/doctor.ts
(SUPPORTED_PI_VERSION_MIN); upgrade Pi if pan doctor reports it as too old.
After installing Pi, run pan sync once. Panopticon writes
~/.pi/agent/settings.json with a skills array pointing at ~/.claude/skills
so Pi loads the same skill tree Claude Code does. Existing keys in
settings.json are preserved.
Provider authentication
Panopticon bridges configured API keys into Pi’s environment at launch time,
so you do not need to configure auth separately in Pi for most providers.
When you spawn a Pi agent with a Kimi, MiniMax, Z.AI, MiMo, OpenRouter, Nous,
or DashScope model, Panopticon injects the native provider env var
(KIMI_API_KEY, MINIMAX_API_KEY, etc.) automatically from your dashboard
Settings or ~/.panopticon.env.
Providers that rely on OAuth / subscription auth (Anthropic, OpenAI Codex)
still require you to log in through Pi directly (/login inside a Pi session)
or via Panopticon’s dedicated subscription flows (Claude Code OAuth, Codex
CLIProxy auth).
Where you pick the harness
The harness is chosen per spawn at four user-initiated surfaces:
| Surface | How |
|---|
| Plan kickoff (start-planning) | Harness/model picker in the kickoff dialog. |
| Work agent start (CLI) | pan start <ISSUE> --harness pi --model <id>. Default is claude-code. |
| Work agent start (Dashboard) | “Start” button menu lets you pick harness + model before launch. |
| Conversation panel | Harness/model selector at the top of the panel. |
There is no per-issue lock — an issue planned with Pi can have a Claude Code
review agent on the same PR, and vice versa.
Role runs (plan / work / review / test / ship)
Pipeline-spawned roles do not prompt at runtime. They read per-role
harness + model defaults from the dashboard Settings page. A harness selector
sits next to each role’s model dropdown to mix and match. Sub-roles such as
work.inspect and review.security inherit from the parent role unless
configured more specifically.
The Settings page is also where you wire up tracker API keys and project
configuration, so harness routing for autonomous role runs lives alongside the
rest of the orchestrator’s defaults — set the harness once per role here and
every pipeline spawn for that role inherits it.
ToS rules
There is exactly one blocked combination, gated by
canUseHarness(harness, model, authMode) in src/lib/harness-policy.ts:
Blocked: pi + Anthropic model + Anthropic auth = subscription
This is required by the Claude Code subscription terms — only the claude-code
binary may invoke Anthropic models when you’re authenticated via the Claude Code
OAuth subscription. Everything else is allowed:
| Harness | Model provider | Anthropic auth mode | Allowed? |
|---|
| claude-code | any | any | ✅ |
| pi | non-Anthropic (OpenAI/Google/OpenRouter/Minimax/DashScope/…) | any | ✅ |
| pi | Anthropic | API key | ✅ |
| pi | Anthropic | subscription | 🚫 |
| pi | Anthropic | unset (no Anthropic auth) | ✅ |
The gate is evaluated at every spawn entry point and at every picker UI so a
stale Settings selection cannot bypass it. When the pipeline routes a role run
into the blocked cell, it falls back to claude-code and emits a console.warn
rather than failing the whole pipeline.
Auth mode is exclusive
Anthropic auth is exclusively subscription or API key, never both. If you log
into the Claude Code subscription, Panopticon ignores any ANTHROPIC_API_KEY in
the environment. Set one or the other, not both.
Troubleshooting
”Pi not on PATH” or wrong version
Run pan doctor. The Pi check reports OK / missing / too-old with the resolved
version. The fix message includes the install/upgrade command.
Pi spawns but the agent never reaches “ready”
Check ~/.panopticon/agents/<id>/ready.json — the vendored extension writes this
on session_start. If it never appears:
- Confirm
packages/pi-extension/dist/index.js exists (pan doctor will warn).
- Tail the tmux session:
tmux -L panopticon attach -t agent-<id>. Pi prints
stdout to the pane; structural errors show up there.
- Verify the named pipe exists at
~/.panopticon/agents/<id>/rpc.in. If a stale
regular file is in its place, createPiFifo replaces it on next spawn.
Pi heartbeat is stale but the agent is working
PiRuntime.getHeartbeat() walks three sources in priority order: active
heartbeat (<60s old) → JSONL session mtime → tmux session created timestamp.
If your dashboard shows a stale heartbeat, check
~/.panopticon/heartbeats/<id>.json — Pi writes there on every
tool_execution_end.
Tradeoffs
RPC over named pipe vs tmux paste-buffer
Pi’s harness uses a per-agent mkfifo (~/.panopticon/agents/<id>/rpc.in) for
command delivery. Claude Code uses tmux’s load-buffer + paste-buffer
pattern. The fifo path:
- skips the 300ms paste-render wait Claude Code needs;
- gives Pi its own structured RPC channel separate from the visible tmux pane;
- keeps the agent inside tmux for crash isolation and visual attach (Pi’s stdout
still flows into the pane).
The downside: a reader-less fifo throws ENXIO immediately, so writePiCommand
returns a typed PiNotReady error and the runtime adapter recycles the agent
rather than blocking on the open call.
Multi-provider via Pi vs CLIProxy
Both Pi (with native multi-provider routing) and Claude Code (via the CLIProxy
auth shim) can drive non-Anthropic models. Pi gives you provider routing without
an extra middleware process; CLIProxy gives you Claude Code’s tooling/UX with
non-Anthropic providers. Pick by which UX you want — there is no correctness
difference today.
When to pick which
- Default to
claude-code for Anthropic-subscription users and for roles
whose instructions rely on Claude Code agent definitions.
- Switch to
pi when you need a non-Anthropic provider, when you want Pi’s
compact-context behavior, or when you’re driving a long-running session where
the named-pipe RPC is materially faster than paste-buffer delivery.
- Mix per role in Settings if you want, e.g., a Pi work role feeding a
Claude Code review role.
Implementation
src/lib/runtimes/pi.ts — PiRuntime (the AgentRuntime adapter for Pi)
src/lib/runtimes/pi-fifo.ts — createPiFifo, writePiCommand, PiNotReady
src/lib/cost-parsers/pi-parser.ts — Pi v3 JSONL active-branch walker
src/lib/harness-policy.ts — canUseHarness ToS gate
packages/pi-extension/ — vendored lifecycle extension Pi loads via --extension
See also