Grok Build provider¶
This page describes how SASE integrates Grok Build for agent invocations.
Grok Build Integration¶
The GrokProvider invokes xAI's Grok Build CLI (grok).
Selection¶
Grok publishes llm_autodetect_cli_name but deliberately no llm_autodetect_priority,
so it never appears in autodetect candidates: grok is a generic executable name shared
with a stale community CLI (grok-dev, which also uses ~/.grok/). Select the provider
with llm_provider.provider: grok or %model:grok/<model> (see the generated
Built-in Model Catalog for current names); set
SASE_GROK_PATH when you also need to choose the executable. Separately, whichever
shipped size aliases currently target Grok can select it whenever a grok executable is
available (see the generated
shipped size-alias defaults). Routing checks
executable presence only; it does not verify the binary's identity. Run sase doctor
before launching: its grok --version probe reports a distinct wrong-binary advisory,
after which you should point SASE_GROK_PATH at the @xai-official/grok binary.
Grok's provider short name is grk, which enables foo.grk agent naming.
Command Construction¶
grok --prompt-file /dev/stdin --output-format streaming-messages-json \
--permission-mode bypassPermissions --model <model> --cwd <cwd> \
--session-id <uuid> --no-plan --no-ask-user --no-auto-update --no-leader \
--rules <directive[+AGENTS.md]> [--effort <level>] [extra_args...]
The prompt is written to process.stdin, exactly as Claude's provider does, so there is
no temp file to leak or clean up on interrupt and no argv exposure of prompt text.
Decisions inside that command:
--permission-mode bypassPermissions, not the undocumented--yolo. No sandbox profile is set, matching what SASE already does for Codex, OpenCode, and Muse.--no-auto-updateis not optional. Without it Grok may replace its own ~166 MB binary mid-run; update it throughsase agent-cli update grokinstead.--no-planand--no-ask-user./sase_planowns planning handoffs and/sase_questionsowns asking, so Grok's native planning and asking are disabled. Both flags are undocumented ingrok --help, so a parse-probe test pins them.--no-leaderis passed explicitly, even though leader mode is off by default, because it is opt-in via a user's own[cli] use_leader = trueand SASE runs many agents concurrently against one shared backend socket — explicit beats inherited.--session-idis generated by SASE, matching Claude's convention.--rulescarries the single-turn directive and the project instructions. Every invocation sends the SASE single-turn directive plus the project rootAGENTS.mdtext exactly once in SASE-managed projects (the directive alone elsewhere); the home layer andCLAUDE.mdare never included. Payloads over 120 KiB raise an actionable error before exec. There is deliberately no--trustand noGROK_CLAUDE_AGENTS_ENABLED. The directive names Grok's own wait primitives (block_until_ms,get_command_or_subagent_output,spawn_subagentwithbackground: false), becauserun_terminal_commandbackgrounds anything slower than its 30-second default.- Subagents stay enabled. Subagent usage can set Grok's internal
usage_is_incompleteflag, which degrades usage telemetry only — SASE treats token counts as telemetry, not as text or tool-call fidelity, so--no-subagentsis not passed.
Model Mapping¶
| Tier | Model |
|---|---|
large |
grok-4.7 |
small |
grok-4.6 |
The tier table names the current models; the generated shipped size-alias defaults show which size aliases route to them.
Grok Reasoning Effort¶
Grok models accept only --effort low|medium|high|xhigh; none, minimal, and max
are rejected by the CLI with a nonzero exit. SASE declares exactly the four supported
levels, so an explicit %effort:max/none/minimal raises a clean
LLMInvocationError instead of a Grok process crash, and a config-derived default at
one of those levels is logged and skipped. See
Reasoning Effort below — the generated
shipped size-alias defaults show the effort each
alias target carries, and the best-effort max-is-logged-and-skipped caveat below
applies to a user-configured target that pairs a provider with an alias-borne max it
does not support.
The Event Stream¶
Grok shares Claude's generalized Anthropic-Messages stream reader
(stream_and_parse_messages_json_output in _subprocess_claude.py), parameterized with
runtime="grok", the Grok tool-call writer, and a thinking sink — Claude's own behavior
is unchanged by this generalization. A no-tool turn emits system/init, one
assistant message whose message.content[] holds thinking and text blocks, and a
terminal result; a tool-using turn adds assistant messages with tool_use blocks
and user messages with tool_result blocks. result.usage carries the same four keys
initial_usage_totals() accumulates, plus a nested server_tool_use SASE's accumulator
ignores harmlessly.
Grok's failure frames carry detail in errors[] only — no top-level error,
message, or result field. The shared error-detail extraction folds errors[] in
when those are absent:
detail = event.get("error") or event.get("message") or event.get("result", "")
if not detail:
errors = event.get("errors")
if isinstance(errors, list):
detail = "\n".join(str(item) for item in errors if item)
This is safe by construction for Claude, which never emits errors[] and whose
append_error_events returns early on a success exit, so a success-path result.result
is never mistaken for an error.
Grok's thinking content blocks are routed into the same codex_thinking.jsonl sidecar
Codex writes reasoning summaries to (the filename is kept as-is because sase's TUI
read_codex_thinking reads that exact path), so Grok's reasoning renders in sase's TUI
thinking pane instead of being silently discarded the way non-text Claude blocks are.
Grok Tool-Call Capture¶
SASE captures Grok tool calls from the streaming-messages-json event stream; it does
not install Grok hooks. When SASE_ARTIFACTS_DIR is present, normalized records are
appended to $SASE_ARTIFACTS_DIR/tool_calls.jsonl with runtime: "grok" and
source: "stream" for sase's TUI
Agents Tab LLM Calls Panel.
Grok's native tool names are mapped onto SASE's canonical display names so the shared
summarizers in _tool_call_common.py produce rich previews instead of falling through
to a generic {"input_keys": [...]} row:
| Grok tool | Canonical display name |
|---|---|
run_terminal_command |
Bash |
read_file |
Read |
write |
Write |
search_replace |
Edit |
grep |
Grep |
list_dir |
Glob |
web_fetch |
WebFetch |
web_search |
WebSearch |
spawn_subagent |
Task |
todo_write |
TodoWrite |
An unmapped tool name survives under its own name rather than being dropped.
Result envelope decoding. Grok's tool_result blocks carry content as a
JSON-encoded string, not text, decoding to a bespoke tagged shape (for example
{"type": "Bash", "output": [...], "output_for_prompt": "exit: 0\n...", "exit_code": 0, ...}
for a shell command, or {"type": "SearchReplace", "EditsApplied": {...}} for an edit).
SASE decodes it and prefers output_for_prompt / tool_output_for_prompt for previews
— the human-readable projection Grok itself uses — maps exit_code through so Bash
rows show exit status, and absolute_path through so edit rows show the file. output
is a byte array, not a string, and is never previewed raw. A content string that is
not valid JSON degrades to the existing plain-text preview path rather than raising.
Grok's user messages carry no top-level tool_use_result envelope, so the decoded
content is the only structured source; Grok's tool-call ids are
call-<uuid>-<n>-shaped, which pair correctly through the existing id-based logic.
Token Usage¶
Grok's result.usage carries the same four keys Claude's does (input_tokens,
output_tokens, cache_creation_input_tokens, cache_read_input_tokens), so token
accounting reuses the same accumulator. Usage is best-effort: Grok's
streaming-messages-json output is a projection of its native usage ledger that drops
the internal "usage incomplete" marker, so subagent turns and interrupted turns can
under-count or zero out. Text and tool records are unaffected. total_cost_usd and a
per-model modelUsage ledger are populated on the OAuth subscription path.
Interrupts and Retries¶
Interrupt handling reuses Claude's interrupt/continue loop: start_interrupt_monitor
watches for an interrupt, and a continuation prompt carrying accumulated work is
relaunched on the same session mechanics as Claude. GrokProvider declares
llm_default_retry_config() with xAI-specific error_patterns ("xAI API error",
"xAI rate limit", "xAI server error", "xAI upstream request failed") kept
deliberately narrow so they cannot collide with Codex's ownership of generic 429 /
Too Many Requests wording; see
Provider-Supplied Retry Defaults.
Skills and Instruction File¶
skill_deploy_subpaths() defaults to f".{provider}" with no hook override, so Grok
skills deploy to ~/.grok/skills/<skill>/SKILL.md, rendered with
provider_name: "Grok", provider_tool_name: "Grok Build", and
provider_native_ask_tool: "ask_user_question". Grok's [compat.claude] cells default
to on, so a Grok run also sees ~/.claude/skills/; this is benign because a native
~/.grok/skills/<name> shadows a same-named Claude-compat skill entirely, and
sase init skills deploys every SASE skill to every registered provider's subpath, so
SASE skills are always shadowed by their correctly-rendered Grok copies.
Grok reads AGENTS.md natively, so there is no GROK.md provider shim — but SASE runs
Grok in untrusted workspaces, so no native files load and SASE instead delivers the
complete memory-built root bundle exactly once through --rules on explicit-delivery
runs (see Command Construction). The
grok_explicit_instructions sunset flag restores the directive-plus-project AGENTS.md
payload; per-invocation delivery failures also fall back to it. CLAUDE.md is never
sent through that channel.
Environment Variables¶
| Variable | Description |
|---|---|
SASE_LLM_LARGE_ARGS |
Extra CLI args for large tier (generic, preferred) |
SASE_LLM_SMALL_ARGS |
Extra CLI args for small tier (generic, preferred) |
SASE_GROK_PATH |
Path to the Grok Build CLI binary (default: grok on PATH) |
SASE_GROK_LARGE_ARGS |
Extra CLI args for large tier (Grok-specific fallback) |
SASE_GROK_SMALL_ARGS |
Extra CLI args for small tier (Grok-specific fallback) |
The generic SASE_LLM_*_ARGS variables take precedence over SASE_GROK_*_ARGS.
Timer Display¶
While waiting for a response, a provider_timer("Waiting for Grok") spinner is shown
(unless suppress_output is True).
Related¶
- Providers and models — the provider integration overview.
- Provider usage — subscription usage reporting.
_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.