Originally created by: imshaikot
The side panel runs on whichever agent CLI you already have logged in — Claude Code, Codex, Antigravity, with Mistral Vibe next. xAI ships one too now: Grok Build, a first-party, open-source (xai-org/grok-build) Rust CLI with a documented headless mode.
It is worth adding, and not only for coverage. It is the first candidate runner whose containment can be scoped to a single run in both directions — tool permissions and an OS sandbox — which no runner except Claude Code currently manages.
| What a runner needs | Grok Build offers |
|---|---|
| Per-run MCP server | [mcp_servers.browsentic] in a project .grok/config.toml, with command / args / env |
| Per-run tool allowlist | [permission] allow = ["MCPTool(browsentic__*)"] plus a deny list, also honoured project-level. Claude Code's model, not Antigravity's soft-deny |
| A real sandbox | [profiles.…] in a project .grok/sandbox.toml, selected with --sandbox <name>: Seatbelt on macOS, Landlock on Linux, restrict_network and path deny globs |
| Streaming | --output-format streaming-json — ACP-shaped session/update notifications (agent_message_chunk, params.update.content.text) |
| Resume | --session-id <ID> creates a named session, --resume <ID> continues it |
| Auth | browser login, or XAI_API_KEY |
Cursor was the other candidate. It configures MCP from files too, but has no sandbox flag, so it lands in the same host containment class as Antigravity — a sealed environment and nothing else. Grok is strictly the better first move.
Runner in src/daemon/agent/runners/types.ts is the whole contract: stream, reader, json, answer, plus optional hint, check and grant. Nothing outside a runner file knows what a CLI's flags look like.AGENT_KINDS drives everything downstream — readAgentConfig loops it, agentState probes each kind, and the popup's picker renders straight from AGENT_LIST. The extension needs no changes at all.scripts/check-security.mjs sweeps vetPlan over every runner × both modes, so a new kind is vetted with no fixture to add. A missing CONTAINMENT entry fails tsc rather than shipping uncontained.mcpTools on StreamContext and endsOnExit on Runner — are very likely what this runner wants too. Cheapest to land right after Vibe.So the work is: a catalog entry, src/daemon/agent/runners/grok.ts, one line in RUNNERS, a CONTAINMENT entry, and docs.
Two files written into the run's cwd — the Antigravity Plan.files pattern, but with teeth:
# .grok/config.toml
[mcp_servers.browsentic]
command = "<node>"
args = ["<cli.js>", "mcp"]
env = { BROWSENTIC_AGENT_RUN = "<runId>" }
[permission]
allow = ["MCPTool(browsentic__*)"]
deny = ["Bash(*)", "Edit(**)", "Read(**)", "WebFetch(*)"]
:::toml
# .grok/sandbox.toml
[profiles.browsentic]
extends = "workspace"
restrict_network = true
deny = ["**/.env", "**/*.pem", "**/.ssh/**"]
grok -p <instruction> --output-format streaming-json --session-id <runSession>
--sandbox browsentic --always-approve [-m <model>]
--resume <id> on later turns of the same conversation. task mode drops the MCP server entirely, as the other runners' json plans do.
Catalog entry: label Grok Build, vendor xAI, bin grok, keepsEnv: ['XAI_', 'GROK_'], models starting from grok-4.6.
--session-id is a small structural win. Every existing runner scrapes a session id out of its stream and threads it through sink.session(); here the daemon can mint it and know the resume key before the first byte arrives.
--always-approve must never travel aloneHeadless runs need --always-approve, which on its own hands the CLI the machine. Paired with a restrict_network sandbox profile and the deny rules above it is exactly Codex's bargain (--ask-for-approval never + --sandbox read-only) — but the pairing has to be enforced, not assumed.
FORBIDDEN in src/daemon/guardrails/spawn.ts matches nothing like --always-approve today, so the CONTAINMENT entry should require the pair: vetPlan fails a plan carrying --always-approve without --sandbox <profile>, and fails one whose files list is missing either TOML file. That is the assertion worth writing the check-security cases around.
One smaller wrinkle: localTools has three values and Grok is the first runner that honestly has two of them (a per-run allowlist and a sandbox). Either it claims allowlist and the note explains the sandbox, or the field becomes a set.
.grok/config.toml contributes but not the precedence. If [mcp_servers] merges, a task-mode run cannot assert zero MCP servers; if [permission] merges, a user's own allow rules could widen ours. grok inspect prints which configs are picked up.agent_message_chunk. Tool calls, usage and end-of-turn are not documented. If there is no closing event, endsOnExit covers it.MCPTool(browsentic__*) glob? If it needs exact names, mcpTools on StreamContext already supplies them.hint() rather than reading as "not installed".grok-4.6.Driving Browsentic from Grok over MCP is a separate direction and already works — that is MCP clients. This is about what runs the side panel.
Sources: Grok Build overview · Headless & Scripting · Sandbox · Permissions · Settings Reference
Tickets: #22
Tickets: #25
Tickets: #26
Tickets: #27
Tickets: #28
Tickets: #34
Tickets: #9
Ticket changed by: imshaikot