Grok Build: a close reading of SpaceXAI's agent harness
This post picks a current open-source agent harness and reads it closely. I started with the name I half-remembered, "PiAgent", and could not find it: nothing notable exists under that exact name on GitHub, npm, or arXiv, just a small Unity package, an npm bot, and a VS Code extension. So I read the most prominent harness that actually exists in 2026: Grok Build, which its README describes as SpaceXAI's terminal-based AI coding agent (SpaceXAI's, per the README; the repo lives in the xai-org GitHub org), open-sourced on 2026-07-14 under Apache 2.0. The Rust repo had already passed 23.6k stars and 4.5k forks when I checked the GitHub API, so it clears the notable bar.
Framing first: this is a desk analysis. I read the repository README, the crate layout, and the user guide that ships in the repo; the docs live at docs.x.ai/build/overview. I did not run the tool, and I will not claim I did. I read it against three questions I bring to every harness: how does it sandbox the agent, how does it gate the tool interface, and how does it handle state?
The field map
Agent harnesses in 2026 sort into three families.
Coding harnesses run the agent on your machine against a real repo. Aider and the earlier terminal coding agents started the line, Claude Code made it the mainstream shape, and Grok Build, openai/codex, and sst/opencode now populate it. The families are visibly converging: Grok Build's third-party notices credit in-tree ports of tool implementations from codex and opencode, and it reads Claude's settings format for permission rules.
Eval harnesses score agents instead of shipping them. Inspect, from the UK AI Security Institute, is the reference here: tool use, sandboxed execution, and a typed event log are all first-class.
Research platforms sit in between. OpenHands is the best documented: a platform for agents that write code, run commands, and browse the web, with sandboxed code execution and multi-agent coordination built in, released under MIT.
One more layer: the Agent Client Protocol standardizes agent-to-editor communication over JSON-RPC, reusing MCP's JSON shapes where it can; Grok Build embeds in editors as an ACP server. It is the boring, important bet underneath all three families: the harness as a service; the GUI is a client.
Grok Build is the most thoroughly documented of these: its sandboxing, permission gate, and state model are all first-party, documented, and open.
Sandboxing: kernel-level and whole-process
The first thing I checked: does the sandbox wrap each command, or the whole agent? Grok Build applies the sandbox to the entire process at startup using OS primitives: Landlock on Linux (kernel 5.13+) and Seatbelt on macOS. Every tool operation is covered, including child processes spawned by bash. It is not per-command wrapping. The sandboxing chapter documents four built-in profiles, plus off, which is the default:
workspace: read anywhere, write the current directory plus~/.grok/and temp dirs, network allowedread-only: read anywhere, write only~/.grok/and temp dirs, child network blocked on Linuxstrict: read only the current directory and system paths, limited writes, child network blocked on Linuxdevbox: for disposable dev VMs, writes everywhere except/data
Custom profiles extend a base and add restrict_network, read_only and read_write paths, and a deny list. The deny list deserves a close read. It is kernel-enforced for read and write/rename, with gitignore-style globs: **/.env and **/*.pem are the documented examples. On macOS each glob becomes a Seatbelt regex applied at runtime, so files created after startup are denied too. On Linux the globs are expanded at launch and bound over, which the docs admit is best-effort: name exact paths for anything that must be airtight there. And the failure mode for custom profiles is fail-closed: a malformed glob, a missing bubblewrap, or a custom profile that cannot be applied makes Grok refuse to start rather than run under-enforced.
Built-in profiles fail the other way, and this is the sharper criticism. If a built-in profile fails to apply, Grok warns and continues without enforcement; it still refuses the leader so tools are not delegated elsewhere, but the agent runs unsandboxed after you asked for a sandbox. A warning in the startup scroll is a weak substitute for a refusal.
Two decisions I would copy. First, the sandbox profile is fixed for the life of a session: resuming with a different profile is refused, because widening a confined session is a footgun. Second, the sandbox is irreversible once applied; the agent cannot relax it at runtime.
The details show real attack-model thinking. The state directory stays writable for session files, but the kernel write-denies the paths used as global hook sources, and a symlinked GROK_HOME is refused at startup so the deny set cannot be retargeted. A shell environment policy controls what child processes inherit: all, core, or none, with built-in drops for variable names matching KEY, SECRET, or TOKEN once a policy is configured (out of the box the environment is left untouched). Sandbox events, including violations, append to ~/.grok/sandbox-events.jsonl. That last one is small and very right: enforcement you cannot observe is enforcement you cannot debug.
What I would not copy: sandbox is off by default, and on macOS the child-network blocking is a no-op, which makes the strict profile meaningfully weaker on the platform most readers use. And the sandbox protects the host from the agent. It does not give the agent a place to run untrusted things safely. That is a different axis; I come back to it below.
The tool interface: a layered gate
The tool set is compact: read_file, search_replace, list_dir, bash, grep, web_search, web_fetch, ask_user_question, todo_write, plus MCP servers and ACP. The crate boundary is xai-grok-tools; the interface is where the design work lives, and it is a pipeline, not a flag. The permissions and safety chapter spells out the order:
- PreToolUse hooks run first and can deny any call.
- Permission rules from config and CLI apply, with deny beating ask beating allow, across every scope.
- Remembered grants from earlier interactive approvals apply, scoped per project.
- Built-in auto-approvals cover read-only tools and a fixed list of read-only shell commands.
- The permission mode sets the prompt policy: default ask, acceptEdits, auto, dontAsk, or bypassPermissions, the always-approve mode.
Deny always wins; the docs say so plainly. Interactive grants are stored per project and never written into the repo, while declarative rules in .grok/config.toml are meant to be committed and reviewed. That split between personal grants and reviewable policy is the right shape.
Now the edges, which are where a harness earns or loses trust.
Hooks fail open. If a hook script crashes, times out, or is missing, the call proceeds as if allowed. The docs explicitly warn that a hook used as a security boundary must handle its own errors. I would flip the default.
The read-only command list is a heuristic, and the docs say so: tee is excluded because it can write anywhere, cargo check is excluded because build.rs runs code. The guidance is literally to treat the list as a convenience; it is no security boundary.
Chained commands are checked segment by segment for deny and ask, but allow rules match the whole string. The docs' own example: Bash(git *) auto-approves git status && rm -rf /. A narrow allow rule is a wide door unless paired with deny rules.
This matches the research on agent-computer interfaces: SWE-agent argued that agents are a new category of end user who need interfaces built for their modality rather than for humans. A small, structured tool vocabulary with explicit results, gated by layered policy, is the production version of that idea. The ports from codex and opencode suggest the industry is converging on the same vocabulary.
State handling: sessions as event logs
Every conversation is saved automatically as a session under ~/.grok/sessions/, one directory per session, grouped by encoded working directory. The session-management chapter makes the layout the architecture:
updates.jsonl: the authoritative log: one ACP session-update event per line, append-only, drives resume and restorechat_history.jsonl: raw messages sent to the modelplan.json: TODO/task staterewind_points.jsonl: file snapshots taken at each user promptsignals.json: token usage and tool/turn counterssummary.json: the index entry
Append-only JSONL is the right call: incremental writes, streaming reads, and every line is valid JSON you can debug. /rewind restores actual file snapshots rather than asking the agent to reconstruct earlier state. Compaction checkpoints preserve the conversation under compression. Forking a session can create an isolated git worktree per session through the x.ai/git/worktree extension, so parallel branches of the same repo do not collide. ACP clients get session/new and session/load, and the sandbox profile travels with the session.
What is missing is the same thing that is missing in most harness logs: the events are not typed for evaluation. There is no score event, no structured approval decision, no branch marker. signals.json keeps counters, not semantics.
Compare Inspect's event log, where events are typed classes: ModelEvent, ToolEvent with arguments, result, error, and cancelled, SandboxEvent for exec and file I/O, ApprovalEvent recording the decision and approver, ScoreEvent landing intermediate and final scores, BranchEvent marking where a branched trajectory diverges, plus spans and timeline tooling so a trajectory can be replayed, filtered, and rendered. That is what "event log as trajectory" looks like when the log is the product instead of the plumbing. OpenHands, on the research side, makes the same bet: the platform's value is that agents, sandboxes, and benchmarks all coordinate over one action and observation stream.
What I would adopt, what I would reject
Adopt: kernel-first whole-process sandboxing; fail-closed custom-profile, glob, and bubblewrap errors; session-pinned, immutable sandbox profiles; kernel-enforced deny globs; the environment variable policy; append-only session JSONL with file-snapshot rewind; declarative, committable permission rules with deny-wins semantics; and plan mode as a product pattern.
Reject or fix: sandbox off by default; built-in profiles that warn and continue without enforcement when they fail to apply, which is worse than failing closed because the user believes the sandbox is on; macOS network enforcement as a no-op; hooks failing open; allow rules that match whole command strings; and plan mode's edit gate being tool-level.
On that last one, the plan-mode docs are admirably honest about the gaps. Plan mode makes plan.md the only editable file, in every permission mode, including always-approve. Then: bash commands are not inspected for file writes, so shell redirection sidesteps the gate. And each subagent starts with a fresh plan-mode tracker, so a write-capable subagent can edit files while the parent is still planning, inheriting the parent's permission mode. The gate checks which tool was called, not what the world changed. Any gate on tool names will be bypassed through the shell; any gate that wants to survive subagents has to look at effects.
The alternative I would ship: gate on the event log. Approve proposals, not calls; check invariants over the stream of actual side effects; fail closed when a profile cannot be enforced; and keep the log typed enough to score and replay.
What this means for my own harness work
Three patterns I work with daily show up here in production form.
Proposal-first review. PiPlan.ai's pipeline reviews a proposal before anything executes. Grok Build's plan mode is the same instinct: explore, write the plan to the single writable file, present it for approval with inline comments, and keep the world read-only until then. What I would take is the enforcement. What I would fix is the bypass: the review has to gate the effects, and the subagents have to be inside the gate. That is exactly the problem my SafeRoutes build notes wrestle with: invariant gating that holds no matter which agent or tool is in play.
Simulation sandbox. Grok Build's sandbox protects the host from the agent. The other direction is protecting the world from the agent's actions, and the research anchor is ToolEmu, which emulates tool execution so you can surface failures before a real run; human review confirmed 68.8% of the failures it identified would be valid real-world failures. The simulation sandbox at PiPlan.ai is the same shape: rehearse the proposal's effects before they touch anything real. The harness needs both layers, and most have only the first.
Event log as trajectories. Session JSONL is a good start, but a trajectory earns its keep when it is typed and scorable: model, tool, approval, sandbox, score. That is the difference between a log you can debug and a log you can evaluate. The trajectory is not the conversation; it is the event stream with semantics.
I would not have known to look at Grok Build a month ago. The correct answer to "which harness" carries a timestamp. As of mid-2026, this is the one worth reading.
Sources linked in this post were fetched and verified.