Grok Build · 55 min

Headless mode, ACP, and Grok Bot

Specialist rung. If you do not write software or run agents, you can stop after C4 and still have reached repo level.

Open Grok Build docs
  • Run grok -p for a one-shot prompt and parse --output-format json
  • Resume a headless session with -r, and not confuse -s with resume
  • Describe ACP as JSON-RPC (initialize, session/new, session/prompt, session/update)
  • Separate Grok Build automation from Grok Bot (cloud teammates on one computer)

Why this matters

The TUI is how you learn. It is not how CI, an editor, or a scheduled job talks to Grok Build. Those surfaces have different contracts. If you use the wrong one, you will wait for a permission prompt that nobody will click, or you will paste TUI folklore into a JSON-RPC stream and wonder why the process hangs.

Specialist rung. If you do not write software or run agents, you can stop after C4 and still have reached repo level.

Three names, three jobs:

NameWhat it isWhen you use it
Headless (grok -p)One prompt, tools run, process prints and exitsScripts, CI, a hook that needs a single answer
ACP (grok agent stdio / serve)Long-lived agent. Clients speak JSON-RPCIDEs, SDKs, a custom app that streams turns
Grok BotA different product. Named teammates on a persistent cloud computerAlways-on work in apps and sites, not this CLI

This module is the first two as Grok Build, and the third only so you stop mixing them. The artifact is a real grok -p JSON object with a sessionId you resumed, plus a short note Grok Build wrote that uses the words JSON-RPC and “shared cloud computer” correctly.

Teach

Headless: grok -p

grok -p "What files are in this directory? Do not edit."
grok -p "Explain the architecture" --output-format json
grok -p "Explain the architecture" --output-format streaming-json

-p / --single is the usual trigger. --prompt-file and --prompt-json also start headless. The process has full tools. It exits when the turn is done. Exit 0 is success, 1 is an error, 130 is Ctrl+C, 143 is SIGTERM.

Headless does not read piped stdin as the prompt. This fails as a brief:

git diff --staged | grok -p "write a commit message"

Do this instead:

grok -p "Write a concise commit message for:`n`n$(git diff --staged)"
grok --prompt-file .\prompt.txt

Output formats:

  • plain (default) — text on stdout
  • json — one object at the end: text, stopReason, sessionId, spend fields when the prompt reached the model
  • streaming-json — one typed event per line (text, tool_call, end, …). Switch on type. end is last.
  • streaming-messages-json — Messages-API shaped stream for clients that already parse that wire

Automation reads json or streaming-json. Keep stdout clean: --no-auto-update, and do not print your own logs to stdout.

Sessions. Each grok -p is a new session by default. Carry context with:

# capture id
grok -p "Remember the practice word is cedar." --output-format json
# resume THAT id — scripts should pass the id, not a title
grok -p "What was the practice word?" --resume "<sessionId>" --output-format json

# most recent session in this directory
grok -p "Continue." -c

-s / --session-id creates a new session with a UUID. It errors if the id exists or is not a UUID. It does not resume. That old upsert behavior is gone. -s with -r/-c only when you also pass --fork-session.

Other flags you will actually set:

FlagRole
--cwd <path>Working directory. Grok walks up to .git from here. Point at the subproject, not a giant monorepo root, if startup is slow.
--always-approveAlias --yolo, same as --permission-mode bypassPermissions. No interactive permission prompts. Deny rules and hooks still apply.
--allow / --denyRepeatable. Deny wins.
--tools / --disallowed-toolsHeadless only. Allowlist / denylist of built-in tool ids (run_terminal_cmd, not “bash”).
--max-turns <n>Headless only. Stop after N agentic turns.
--worktree=nameRun the one-shot in a new git worktree (C8).

CI without a browser: XAI_API_KEY from console.x.ai, or grok login --device-auth on that machine once.

--always-approve is for unattended runs. It is not “turn the safety off.” C10 is the rest of that sentence. Do not add it to a TUI habit because a prompt annoyed you.

ACP is JSON-RPC

ACP (Agent Client Protocol) is how an editor or your own process talks to a long-lived Grok Build agent. The wire is JSON-RPC 2.0. stdio is the common local path: newline-delimited JSON on stdin/stdout.

grok agent --always-approve stdio
grok agent --always-approve serve --bind 127.0.0.1:2419 --secret <token>

serve is a WebSocket server you run. Grok’s hosted cloud sandboxes do not run grok agent serve.

Lifecycle:

  1. Client → initialize (protocol version, client capabilities)
  2. Client → session/new (cwd, mcpServers, optional _meta.yoloMode)
  3. Client → session/prompt (sessionId, prompt: [{ type: "text", text: "…" }])
  4. Agent → session/update notifications (agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update, plan)
  5. Permissions, unless always-approve / yoloMode

session/prompt returning is not the same as “the answer is in result.” The assistant text arrives as session/update chunks. If you only read the RPC result, you will think the agent said nothing.

Grok adds x.ai/* extension methods (fs, git, worktree apply, terminal, session fork). Treat that list as non-exhaustive; discover methods from initialize. Official client SDKs exist (TypeScript @agentclientprotocol/sdk, Rust, Python, Go, Kotlin). Compatible editors include Zed, Neovim, Emacs. JetBrains is listed as coming.

Headless grok -p is the wrong tool for an IDE. ACP is the wrong tool for a one-line CI review. Pick once.

Grok Bot is not Grok Build

Grok Bot is a sibling product: named, persistent teammates. You message a Bot like a colleague. Bots have a cloud computer (browser, filesystem, terminal), connectors/MCP where available, and computer-use for sites without a clean API.

All of your Bots share one cloud computer, isolated to the account, not to each Bot. A login or file on that machine is available to every Bot you have. Separate Bots are not a security boundary. Each Bot has its own screen so they can drive tools in parallel.

That is not grok -p. It is not grok agent stdio. It does not share this pathway’s session directory. Do not file a Bot transcript as a C9 headless artifact.

If you have Bot access, the safety habits still look like C10: require approval for send, publish, purchase, delete, production. Enter passwords yourself on the computer; do not paste them into chat. If you do not have Bot access, you still need the distinction written down so you do not chase the wrong docs.

Worked example

Practice repo.

Set-Location $env:USERPROFILE\grok-build-practice
grok -p "Count the top-level files in this directory. Reply with one integer and the names. Do not edit." --output-format json --no-auto-update

Save the sessionId. Then:

grok -p "What integer did you report last turn? Reply with only that integer." --resume "<paste-id>" --output-format json --no-auto-update

The second text should be that integer. If it is not, you resumed the wrong id, or you used -s thinking it would resume.

Optional ACP smoke (you are not building an IDE today):

grok agent --help

You should see stdio and serve. A real handshake is initialize then session/new — JSON-RPC objects with "jsonrpc":"2.0", "id", "method". You do not type those into the TUI prompt.

Then open the TUI in the same repo and have Grok write notes/c9-surfaces.md from your session id and the three-row table in this lesson. It must say ACP is JSON-RPC and Grok Bot shares one cloud computer per account.

Your turn

Do c9-l01-e1 in Grok Build: a headless JSON run you resume, then a TUI-written distinction note. grok.com does not count. A Bot chat does not count.

Common failure

Piping stdin into -p. The diff never arrives. Use $(git diff) or --prompt-file.

-s to continue. You create a collision or a fresh session and lose the word you stored. -r / --resume with the id from JSON.

Always-approve as a lifestyle. You add --yolo to every TUI launch. Unattended automation only, with deny rules you already wrote. Next module is that control plane.

Calling Bot “headless Grok.” Different product, different computer, shared across every Bot on the account. If the file lives on the Bot VM, every Bot can see it.

ACP client that ignores session/update. You print result and get an empty string. Stream the chunks.

What’s next

C10 is permissions and review: ask versus always-approve, deny still winning, sandbox on Linux/macOS, and the review habit you will use in the capstone. No shortcuts around those gates.

Your turn

Grok Build

Resume a grok -p session, then name the three surfaces

Copy-ready prompt
I am on Grok Mastery Pathway C, module C9, part B.

Write notes/c9-surfaces.md in this repo (create notes/ if needed). Do not
edit any other file.

The file must contain exactly these headings and constraints:

# C9 surfaces
## Headless
- One paragraph: grok -p runs one prompt and exits.
- Include this session id I already used in headless: PASTE_SESSION_ID
- State that --output-format json is how I got that id.
## ACP
- One paragraph: grok agent stdio speaks JSON-RPC 2.0.
- Name these methods in this order: initialize, session/new, session/prompt.
- State that assistant text arrives as session/update chunks, not only as
  the session/prompt result.
## Grok Bot
- One paragraph: Grok Bot is a different product.
- State that all of my Bots share one cloud computer isolated to the
  account, not to each Bot.
## What I ran
- The exact first grok -p command (redact nothing except an API key if you
  see one — there should be none).

Do not claim I connected ACP unless I did. Do not claim I have a Grok Bot.
Artifact

1. First `grok -p` JSON (at least sessionId, stopReason, text). 2. Second JSON `text` — your practice token only. 3. Full `notes/c9-surfaces.md` as written by Grok Build.

Read the lesson if you want. Submitting an artifact and marking complete needs a seat.