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.
Pi is a different shape of CLI from the other four, and three decisions
follow from that:
- It has NO permission prompts and no sandbox, so there is no
--dangerously-skip-permissions analog and none was invented. The
privilege-shaped knob is the tri-state 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
a prompt the session user could answer themselves. That helper had zero
test coverage; it now has coverage for all four CLIs.
- Only the PI_ prefix joins the env allowlist. 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. Auth goes through pi's /login or the server's own environment.
--api-key is deliberately never wired: it would put a provider secret on
the spawn command line.
- pi stays OUT of isAltScreenStripMode(). Its default TUI renders into the
main screen with terminal-owned scrollback, and its 0.84.0 fullscreen
mode is runtime-switchable via /settings; that flip was measured to put
the pane into the alt screen, which the strip would have corrupted.
pi-cli-resolver.ts additionally sanity-probes `pi --version` and requires
semver-shaped output, because `pi` is a short generic name a stray binary
can shadow; GET /api/pi/status surfaces path and version so a
misresolution is diagnosable rather than presenting as a broken mode.
Docker installs pi in its own --ignore-scripts step so that flag cannot
affect the other four CLIs, and seeds its credentials per-file rather than
whole-dir (~/.pi/agent also holds sessions, extensions and package trees).
Verified end to end against pi 0.84.1 on an isolated instance: resolver
search-dir fallback, flag construction, piConfig persistence across a full
server restart, the trust prompt and its --no-approve suppression, the
rose Run button on the default daylight-blue skin (the nested skin block
eats per-mode gradients unless the rule lives inside it), and the buffer
local-echo policy, which pi tolerates where codex did not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Antigravity (agy) was wired into the session layer but never propagated to
the surfaces around it, while Gemini CLI stayed documented as a consumer
product despite being enterprise-only since Google's cutover. Gemini keeps
full support; Antigravity now sits beside it everywhere.
Functional fixes:
- docker/agent.Dockerfile never installed agy, so a docker case with
mode 'antigravity' died on command-not-found. agy is not on npm, so it
gets its own installer step. --dir /usr/local/bin is load-bearing: the
default $HOME/.local/bin resolves to root's home at build time and is
unreachable by the `agent` user the container runs as. Verified inside
codeman/agent:base (v1.1.10, reachable as `agent`). Note the binary is
~190MB, the largest layer in the image.
- Welcome screen gained a Run Antigravity action, gated on agy being
present like the other CLI buttons, with a cyan identity matching the
toolbar run button and run-mode dot.
- install.sh now detects agy (search paths mirroring the resolver), counts
it as a satisfying AI CLI, and recommends it over Gemini in the install
hints. Detection only, no new auto-install path.
Docs corrected where they were factually wrong:
- architecture-invariants documented isExternalCliMode() as
opencode/codex/gemini when the code has included antigravity for a
while, said "all three modes", and omitted ANTIGRAVITY_ from the env
prefix allowlist row.
- cron-guide's agentType enum, cron-discovery's SessionMode, and
remote-sessions' RemoteCommandMode were all stale.
Also: README + README.zh-CN (five CLIs, Gemini marked enterprise-only),
package.json keyword, and comment drift in 8 places.
test/run-mode-ui.test.ts now covers the new welcome button; verified it
fails without the settings-ui wiring.
Antigravity nests its whole state under ~/.gemini/antigravity-cli/, not
~/.antigravity, so the existing .gemini docker credential seed already
covers it. Recorded as a comment so nobody adds dead config later.
isAltScreenStripMode() deliberately still excludes antigravity: whether
its TUI needs the alt-screen strip is a behavioural question that needs a
real agy session, not a guess.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The durable-launch section predated the #145 consolidation: owned launches use
the dedicated -L codeman-remote socket with codeman-ssh-<id8> names and
per-session set -t options (never -g). Discovery/attach (COD-105) genuinely
target the canonical -L codeman socket; the asymmetry is now called out.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
55f5ada (COD-105) added Phase 2 of the remote-tmux arc: discover codeman-*
sessions on a host and attach to non-owned ones, with detach-not-kill on close.
- Data model: SessionRemote.owned/remoteSessionName + RemoteSessionInfo;
toSessionRemote (owned:true) vs toAttachedSessionRemote (owned:false).
- New Ownership section: discovery (listRemoteCodemanSessions, the literal-\t
parse quirk, never-throws/VITEST), attach-vs-launch selection
(buildRemoteSessionCommand), and the killSession detach-not-kill guarantee.
- API: GET /api/remote-hosts/:hostId/sessions + the attachRemoteSession
create path.
- CLAUDE.md Remote Key Pattern notes discover/attach + detach-not-kill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit f321e1200a9a7c1e58c69ba936b680200fd53275)