SessionMode gains 'grok', a first-class backend alongside Claude Code,
shell, OpenCode, Codex, Gemini, Antigravity and Pi: its own PTY, tmux
session, charcoal tab identity ('gk' badge), welcome button, run-mode
entry, cron agentType, Docker and remote-SSH command defaults, and
clone-repo Brain option. Flag surface verified live against grok 1.0.5.
Grok mixes two existing shapes and the wiring follows from that:
- Codex-shaped on permissions: the bypass switch is GrokConfig.alwaysApprove
(--always-approve, grok's bypassPermissions mode; config-level deny rules
still apply on top). The Run button sends it true, like runAntigravity(),
and clampExternalCliBypassForOwner() puts grok in the only-if-sent branch:
a bare grok spawn is grok's own ask-mode default, which is already safe,
so only a sent config needs the flag forced off. Cron needs nothing for
the same reason.
- OpenCode-shaped on rendering: grok is a fullscreen alternate-screen TUI
with mouse support, so it stays OUT of isAltScreenStripMode() and lands
on the narrow tmux-attach strip and the 'buffer' local-echo fallthrough
(unmeasured against an authenticated composer; documented fallback is the
'off' branch).
- Pi-shaped on resolution: 'grok' has npm squatters (@vibe-kit/grok-cli
also installs a grok bin), so grok-cli-resolver.ts version-probes every
candidate (grok --version, killSignal SIGKILL, VITEST-gated) and
GET /api/grok/status surfaces path AND version; GROK_VERSION_REGEX is
shared with the dependency registry so doctor and run mode cannot drift.
Env allowlist gains GROK_* plus the XAI_* vendor namespace (XAI_API_KEY is
grok's documented headless auth var), the same narrow-vendor reasoning as
GOOGLE_* for gemini. Resume is id-regexed on purpose: grok's own --resume
also matches session titles, which are arbitrary user strings that must
never reach the bash -c spawn line.
Docker: grok is not on npm, so the agent image installs it in its own step
(xAI's installer has no --dir override; the binary is copied to
/usr/local/bin and root's ~/.grok dropped in the same layer), and
credentials are seeded per-file (auth.json, config.toml, pager.toml; the
dir also holds sessions/, memory/ and the ~160MB binary). Remote SSH routes
through the login-shell wrapper like the other agent CLIs.
Verified end to end on an isolated CODEMAN_INSTANCE with grok 1.0.5
installed: /api/grok/status resolves and reports the probed version,
quick-start spawns a pane whose command line ends in 'grok
--always-approve', the real TUI renders (OAuth device screen on an
unauthenticated box), and grokConfig round-trips through state.json.
Docs: docs/grok-integration.md (user guide) + docs/grok-integration-plan.md
(decisions, verification record, follow-ups).
Tests: test/grok-mode.test.ts, test/grok-cli-resolver.test.ts, plus
extended clamp/system-routes/render-index-html/run-mode-ui/mobile-overview/
local-echo-gating coverage. npm test (the CI gate) green: 5910 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9.6 KiB
Grok Build (xAI) integration plan
Status: Executed. This document records the plan, the decision behind each wiring point, and what was and was not verified. The user-facing guide is
grok-integration.md; the per-decision invariants live inarchitecture-invariants.md#external-cli-modes-opencode-codex-gemini-antigravity-pi-grok. Template: the pi integration (c5b5963,pi-integration-plan.md), which was itself calibrated against the four follow-up commits the antigravity integration needed. All of grok's facts below were verified against grok 1.0.5 (grok 1.0.5 (5115b46bc9)), installed live during the work.
1. What Grok Build is
xai-org/grok-build is xAI's coding agent: a
Rust fullscreen-TUI binary named grok, installed by
curl -fsSL https://x.ai/cli/install.sh | bash into ~/.grok/bin (with symlinks into
~/.local/bin; the installer also ships an agent alias). Config lives in
~/.grok/config.toml, TUI appearance in ~/.grok/pager.toml, credentials in
~/.grok/auth.json (0600), sessions under ~/.grok/sessions/. Auth is browser OAuth
on first launch, grok login --device-auth for SSH boxes, or XAI_API_KEY for
headless use. It has Claude-style permission modes (default/acceptEdits/auto/
dontAsk/bypassPermissions/plan), allow/deny rules, hooks, MCP, subagents, and a
headless -p mode.
2. Shape decisions (why grok is wired the way it is)
Grok is a seventh run mode, alongside Claude Code, shell, OpenCode, Codex, Gemini, Antigravity and Pi. Never a location overlay, never a web tab. Its wiring mixes two existing shapes:
| Question | Decision | Why |
|---|---|---|
| Permission bypass | GrokConfig.alwaysApprove -> --always-approve |
Grok's real flag (verified via --help): "Auto-approve all tool executions", i.e. its bypassPermissions mode. Config-level deny rules still apply on top. The Run button sends true, matching runAntigravity() and Claude's own --dangerously-skip-permissions default: Codeman sessions exist for autonomous work. |
| Multi-user clamp branch | only-if-sent (codex/antigravity branch) | A bare grok spawn is grok's own ask-mode default, which is already safe, so the clamp only needs to force a SENT alwaysApprove off. Contrast pi, whose absent default is an answerable prompt and therefore needs the materialize branch. Cron needs nothing for grok for the same reason (clampCronExternalCliConfigs). |
| Alt-screen strip | OUT of isAltScreenStripMode() |
Grok is a fullscreen alternate-screen TUI with mouse support (its own scrollback pane, pager.toml [terminal] alt_screen), i.e. the opencode case, not the Ink repaint case. It falls through to the narrow tmux-attach strip like opencode/antigravity/pi. |
| Resolver | version probe, like pi | grok has npm squatters (the unrelated @vibe-kit/grok-cli installs a grok bin). Candidates must pass grok --version; GROK_VERSION_REGEX is exported and shared with the dependency registry so doctor and run mode cannot disagree. The probe cannot tell two version-printing groks apart, so GET /api/grok/status surfaces path AND version. Search dirs: ~/.grok/bin first (installer target), then ~/.local/bin, /usr/local/bin, ~/bin. |
| Env allowlist | GROK_* + XAI_* prefixes |
GROK_* covers grok's documented inputs (GROK_HOME, GROK_CONFIG/GROK_CONFIG_PATH, GROK_MEMORY, GROK_WORKFLOWS, GROK_SANDBOX, GROK_OIDC_*, GROK_AUTH_PROVIDER_COMMAND). XAI_* is xAI's vendor namespace and carries XAI_API_KEY, grok's documented headless auth var: the same narrow-vendor-namespace reasoning that admitted GOOGLE_* for gemini. Foreign provider keys stay out, as always. |
| Resume | --resume <id> / --continue, id-regexed |
Grok's --resume also matches session TITLES (arbitrary user strings, case-insensitive). The ^[a-zA-Z0-9._-]+$ regex doubles as the no-titles rule, so nothing free-form can reach the bash -c spawn line. A valid explicit id wins over -c, mirroring pi. |
| Local echo | 'buffer' via the _updateLocalEchoState fallthrough |
UNMEASURED against an authenticated session (see §4). If grok's composer turns out per-keystroke reactive like codex's, the fallback is one 'off' branch; teaching PredictiveEchoAddon grok's composer row is the larger follow-up. |
| Truecolor | COLORTERM=truecolor + unset NO_COLOR |
Rust TUI with themes; joins the codex/gemini/antigravity/pi list in buildEnvExports() and buildMuxAttachEnv(). |
| Docker credentials | per-file seed: auth.json, config.toml, pager.toml |
~/.grok also holds sessions/, memory/, completions/, docs/ and the ~160MB binary under downloads/; a whole-dir seed would copy all of it on every container start. Same trade-off as pi: in-container sessions are invisible host-side, so grok -c in a Docker case sees only that container's history. |
| Docker install | own Dockerfile step | Not an npm package. xAI's installer has no --dir override, so the step copies /root/.grok/bin/grok (through the symlink, cp -L) into /usr/local/bin and removes root's ~/.grok in the same layer. |
| Remote SSH | exec "$SHELL" -i -l -c 'grok' |
sshd's remote-command PATH does not include ~/.grok/bin; same login-shell fix as every other agent CLI. |
| What is NOT wired | --permission-mode, --allow/--deny, -p headless, --worktree, --sandbox, --reasoning-effort, -s/--session-id, --fork-session, --agent, --output-format |
Follow-ups. The flag surface is kept minimal on purpose; grok is pre-1.0-style fast-moving and every flag added is a flag validated forever. |
3. Touch points (the checklist)
Backend: types/session.ts (SessionMode + GrokConfig + SessionState), utils/grok-cli-resolver.ts (new)
- barrel,
tmux-manager.ts(buildGrokCommand, dispatch, resume flag, PATH export, truecolor, availability error, plumbing),session.ts(external-mode gate, label, config plumbing, tmux-required error, attach env),mux-interface.ts,schemas.ts(prefixes,GrokConfigSchema, both mode enums, remote command overrides, cron agentType),session-routes.ts(clamp + both create paths),system-routes.ts(GET /api/grok/status),server.ts(availability inject + mux restore),docker-hosts.ts,remote-hosts.ts,config/dependency-registry.ts,cron/cron-service.ts(comment),response-viewer-transcript.ts,tui/tui-client.ts+tui-app.ts.
Frontend: index.html (welcome button, run-mode entry, cron option, clone Brain option),
session-ui.js (runGrok(), dispatch, availability, "Run GK" label, external-CLI gates,
runMode setter), app.js (label, gk tab badge, kill-menu), settings-ui.js,
mobile-overview.js, home-sessions.js, panels-ui.js, i18n.js, styles.css +
mobile.css (charcoal monochrome identity; the non-og skin block and the mobile
!important pair are both load-bearing, see the pi plan's §2.9 cascade trap).
Meta: docker/agent.Dockerfile, install.sh, package.json keyword, changeset,
skills/codeman/reference/*, CLAUDE.md, READMEs, architecture-invariants.md,
remote-sessions.md, security-architecture.md, docker-cases.md, cron-guide.md.
Tests: test/grok-mode.test.ts + test/grok-cli-resolver.test.ts (new);
external-cli-bypass-clamp, system-routes, render-index-html, run-mode-ui,
mobile-overview, local-echo-codex-gating (extended).
4. Verification performed
On this box, with grok 1.0.5 really installed and an isolated
CODEMAN_INSTANCE=grokwt server (own data dir, own tmux socket, port 5077):
npm test(the CI gate): green, 5900+ tests.typecheck,lint,format:check,check:frontend-syntax,check:public-assets,check:lockfile: green.GET /api/grok/status->{available: true, path: "/home/arkon/.local/bin", version: "1.0.5"}through the real resolver and probe.POST /api/quick-start {mode: "grok", grokConfig: {alwaysApprove: true}}-> session created, tmux pane spawned, real spawn line verified to end ingrok --always-approve, and the actual grok TUI rendered its OAuth device-approval screen in the pane (unauthenticated box, so sign-in is exactly where a first run lands).grokConfigpersisted into the instance'sstate.json.- Session deleted by exact id; instance data dir and throwaway case removed.
Not verified (honest gaps, all requiring an xAI account or more hardware):
an authenticated conversation end to end; the local-echo buffer policy against grok's
real composer (§2); scrollback/repaint behavior of the fullscreen TUI under the narrow
strip during a long session; a Docker case with mode: 'grok' (needs a --no-cache
agent-image rebuild); a remote-SSH grok case; cron readiness degradation (expected:
same slow-start-then-send as pi, documented in cron-guide.md).
5. Follow-ups
- Idle/completion signal: grok has a hooks system (user-guide
10-hooks.md); a hook POSTing to/api/hook-eventcould give grok sessions real idle detection instead of output-stabilization. Highest-value follow-up, same slot as pi'sagent_settledidea. - Response viewer: sessions are ACP JSONL under
~/.grok/sessions/<encoded-cwd>/<id>/updates.jsonl;grok -p ... --output-format json | jq -r '.sessionId'exists for correlation. - Permission-mode picker (
--permission-mode,--allow/--deny) in Session Options. - Measure the local-echo policy and the fullscreen-TUI scrollback behavior against an
authenticated session; pin the result in
local-echo-codex-gatingthe way pi did. grok doctoris a built-in terminal-support check worth pointing users at when a pane renders oddly.