Skip to content

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-update is not optional. Without it Grok may replace its own ~166 MB binary mid-run; update it through sase agent-cli update grok instead.
  • --no-plan and --no-ask-user. /sase_plan owns planning handoffs and /sase_questions owns asking, so Grok's native planning and asking are disabled. Both flags are undocumented in grok --help, so a parse-probe test pins them.
  • --no-leader is passed explicitly, even though leader mode is off by default, because it is opt-in via a user's own [cli] use_leader = true and SASE runs many agents concurrently against one shared backend socket — explicit beats inherited.
  • --session-id is generated by SASE, matching Claude's convention.
  • --rules carries the single-turn directive and the project instructions. Every invocation sends the SASE single-turn directive plus the project root AGENTS.md text exactly once in SASE-managed projects (the directive alone elsewhere); the home layer and CLAUDE.md are never included. Payloads over 120 KiB raise an actionable error before exec. There is deliberately no --trust and no GROK_CLAUDE_AGENTS_ENABLED. The directive names Grok's own wait primitives (block_until_ms, get_command_or_subagent_output, spawn_subagent with background: false), because run_terminal_command backgrounds anything slower than its 30-second default.
  • Subagents stay enabled. Subagent usage can set Grok's internal usage_is_incomplete flag, which degrades usage telemetry only — SASE treats token counts as telemetry, not as text or tool-call fidelity, so --no-subagents is 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).

_[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.