Files
Codeman/.changeset/ba4bc996.md
T
Codeman maintainer f4dcfbe6ca fix(pi): close four mode-list gaps in the pi run mode
Review follow-ups on #282. All four are the same failure shape: a list that
enumerates run modes, missed by the sweep that added 'pi'.

1. Cron ignored pi's project-trust clamp. The PR widened CronJobBaseSchema's
   agentType to accept 'pi' but not the matching clamp beside gemini's, so a
   non-granted multi-user owner's cron pi job spawned bare `pi` (pi's own
   defaultProjectTrust, an interactive prompt they can answer "yes" to, which
   loads and EXECUTES repo-local .pi/extensions TypeScript) while the same
   user's UI/API launch was forced to --no-approve. The clamp is now a pure
   exported helper, clampCronExternalCliConfigs(), so both it and gemini's
   previously untested materialization are pinned.

2. POST /api/sessions/:id/interactive auto-enabled the Ralph tracker for pi:
   its denylist covered opencode/codex/gemini/antigravity only. The tracker is
   never fed for an external CLI (_processExpensiveParsers returns early), so a
   pi session reported ralphEnabled and Ralph UI state no sibling backend shows.

3. REMOTE_CLI_BIN had no pi entry, so buildRemoteCliVersionProbeCommand()
   returned null and Session.cliVersion stayed blank for every remote-SSH pi
   session, even though the PR wired the remote launch command and the
   per-mode override schema field.

4. The desktop home rail's badge map had no pi entry, and its lookup falls back
   to '', which is what claude renders. A pi session read as Claude there while
   the tab strip and phone overview badged it correctly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 17:23:27 +02:00

3.6 KiB

aicodeman
aicodeman
minor

Add Pi (pi.dev) as a sixth CLI run mode (#206).

SessionMode gains 'pi', a first-class backend alongside Claude Code, OpenCode, Codex, Gemini and Antigravity: its own PTY, tmux session, rose tab identity, welcome button, run-mode entry, cron agentType, Docker and remote-SSH command defaults, and clone-repo Brain option.

  • New resolver src/utils/pi-cli-resolver.ts. Unlike the sibling resolvers it sanity-probes pi --version and requires semver-shaped output, because pi is a short generic name that a stray binary on $PATH can shadow; the rejected path is logged. GET /api/pi/status returns { available, path, version } so a misresolution is diagnosable.
  • PiConfig maps to --model (accepts provider/id and a :thinking suffix), --provider, --thinking, --session/-c, and the tri-state --approve / --no-approve. Every value is regex-allowlisted and dropped on failure. --api-key is deliberately never wired: it would put a provider secret on the spawn command line.
  • No bypass flag. Pi has no permission prompts and no sandbox, so there is no --dangerously-skip-permissions analog. Its privilege-shaped knob is approveProjectTrust, which makes pi load and execute repo-local .pi/extensions TypeScript and install missing project packages. clampExternalCliBypassForOwner() therefore puts pi in the materialize branch: a non-granted multi-user owner gets --no-approve even when no config was sent, because pi's own default is an interactive prompt the session user could answer themselves. The same materialization applies to cron-fired jobs (clampCronExternalCliConfigs), which carry no per-CLI config and would otherwise launch on pi's own default. Both helpers had no test coverage at all; they now do, for every CLI.
  • Env allowlist gains only the PI_* prefix. Pi's ~34 provider key vars share no prefix and ALLOWED_ENV_PREFIXES is one global list with no mode context, so admitting them would widen the allowlist for every mode at once. Users authenticate via pi's /login or the server process's own environment.
  • Pi stays out of isAltScreenStripMode(). Its default TUI renders into the main screen with terminal-owned scrollback, and since 0.84.0 the user can flip to a fullscreen TUI at runtime via /settings — verified to switch the pane into the alt screen, which the strip would have corrupted.
  • Docker: pi installs in its own --ignore-scripts step so that flag cannot affect the other four CLIs, and its credentials are seeded per-file (auth.json, settings.json, trust.json, models.json, models-store.json) rather than whole-dir, since ~/.pi/agent also holds sessions, extensions and installed package trees.
  • Local echo: pi lands on the buffer overlay. Verified that codex's per-keystroke starvation does not reproduce — pi's slash picker re-filters on the whole composer content, so a one-shot flush behaves identically to per-keystroke typing.
  • Mode-list parity: pi is excluded from the Ralph tracker auto-enable on POST /api/sessions/:id/interactive (like every other external CLI, whose output the tracker never parses), carries a REMOTE_CLI_BIN entry so a remote-SSH pi session reports its CLI version, and gets its own badge in the desktop home rail instead of rendering like Claude.
  • Installer detection, docs (docs/pi-integration.md), READMEs, and the architecture invariants are updated. Tests: test/pi-mode.test.ts and test/routes/external-cli-bypass-clamp.test.ts, plus extensions to the run-mode, mobile-overview, render-index-html, system-routes and local-echo suites.