Narrative: the streaming-json headless contract intermittently emitted only thought events then stopReason:Cancelled with zero text, leaving Chat replies silently empty; the adapter now spawns grok with --output-format json, buffers stdout, and parses the single JSON response on process close, with streaming-json parsing kept only as a diagnostic fallback.
- Change grok CLI invocation from --output-format streaming-json to --output-format json (cli-stream.ts)
- Add GrokCliJsonResponse type ({text, stopReason, sessionId, requestId, thought}) and parseJsonOutput() to stream-parser.ts, keeping legacy NDJSON line parsing for fallback/diagnostics
- Rework runtime-adapter.ts to buffer full stdout, parse it via parsePromptOutput (JSON object first, NDJSON fallback), and surface a formatTerminalNoTextDiagnostic when a non-EndTurn stopReason yields no assistant text
- Rename first-line/inactivity timeout bookkeeping from line-based to output/chunk-based (FIRST_OUTPUT_TIMEOUT_MS, firstOutputReceived, firstStdoutChunk) since stdout is no longer consumed via readline
- Update cli-stream/runtime-adapter/stream-parser tests to cover the JSON response path and the Cancelled/no-text diagnostic
- Update docs/grok-cli-contract.md and plugin README to document the json output-format contract and diagnostics
- Add changeset fn-7796-grok-cli-reliable-headless.md (patch, fix)
Files changed:
.changeset/fn-7796-grok-cli-reliable-headless.md | 7 +
docs/grok-cli-contract.md | 108 ++++++++-----
plugins/fusion-plugin-grok-runtime/README.md | 16 +-
.../src/__tests__/cli-stream.test.ts | 4 +-
.../src/__tests__/runtime-adapter.test.ts | 73 ++++++++-
.../src/__tests__/stream-parser.test.ts | 80 +++++----
.../fusion-plugin-grok-runtime/src/cli-stream.ts | 14 +-
.../src/runtime-adapter.ts | 180 ++++++++++++---------
.../src/stream-parser.ts | 65 ++++++--
plugins/fusion-plugin-grok-runtime/src/types.ts | 13 +-
10 files changed, 373 insertions(+), 187 deletions(-)
Fusion-Task-Id: FN-7796
Fusion-Task-Lineage: c920fcf0-98f8-42ec-867a-7f76c0aca1b7
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
fusion-plugin-grok-runtime
Grok CLI-backed provider/runtime plugin for Fusion.
Install
This plugin ships bundled with Fusion and is auto-installed like the other
built-in runtime plugins. It shells out to an operator-installed grok
binary on PATH — Fusion never downloads or bundles the CLI itself.
- Canonical upstream: xAI official Grok CLI / Grok Build TUI (
grok --versionobserved asgrok 0.2.93 (f00f96316d4b)). - Docs / homepage: https://grok.com/, https://docs.x.ai/, and
grok --help/grok agent --helpfor exact flags. - Release / download: operator-installed; Fusion resolves
grokfrom PATH orgrokCliBinaryPath. - Binary name:
grok. - Checksum:
upstream-pending-verificationbecause Fusion does not download or pin the operator's binary.
The previously assumed superagent-ai/grok-cli contract is a different product that shares the grok binary name. This plugin targets xAI's official CLI contract.
Contract summary
- Provider ID:
grok-cli - Binary probe:
grok --version - Auth model — the
grokCLI owns its own authentication; Fusion does not require a Fusion-visible API key to enable/use it (FN-7716). Fusion additionally probes theGROK_API_KEYenv var and~/.grok/user-settings.json→{ "apiKey": "..." }purely as a non-blocking informational hint (apiKeyDetected); it never gates Enable or the authenticated state. The direct xAI OpenAI-compatible streaming path (base URLhttps://api.x.ai/v1) still uses$GROK_API_KEYwhen present, independent of the CLI provider. - Model discovery:
grok models(plain text). The observed xAI shape isDefault model: <id>, thenAvailable models:, then* <id> (default)/- <id>bullet rows.
CLI headless execution path (FN-7790 / FN-7796)
The plugin's GrokRuntimeAdapter returns a real Grok response through xAI's reliable single-object CLI output:
grok -p "<text>" --output-format json
# with optional model/cwd:
grok -p "<text>" --output-format json -m "grok-4.5" --cwd "/path/to/project"
-p, --single <PROMPT>runs a single prompt and exits; it does not require interactive stdin.--output-format jsonemits one object with{ text, stopReason, sessionId, requestId, thought }. Fusion buffers stdout until subprocessclose, then bridgesthoughttoonThinking,texttoonTextand persisted assistant content, and storessessionIdwhen present.- xAI's
--output-format streaming-jsonmode is not used for the primary headless path because livegrok 0.2.93testing found it can intermittently endstopReason:"Cancelled"with zerotextevents. A non-EndTurnstop reason with empty text now surfaces a concrete diagnostic instead of a blank assistant response; a parseableEndTurnwith empty text remains a legitimate silent response. - A wrong-binary/wrong-flag run that emits no parseable JSON surfaces a concrete diagnostic instead of a blank assistant response.
- Auth implication: because the
grokbinary resolves its own credentials for this path, a CLI-routed selection needs no Fusion-visibleGROK_API_KEY— unlike the direct xAI OpenAI-compatible streaming path.
See docs/grok-cli-contract.md for the full contract, live captures, and the reason Fusion no longer uses the old grok --prompt <text> --format json / step_* schema or the flaky streaming-json prompt path.
Routing Grok through the CLI runtime (FN-7725 / FN-7753 / FN-7790)
By default, selecting a grok-cli/* model for an agent/task routes through
the direct xAI OpenAI-compatible endpoint (https://api.x.ai/v1,
FN-7711/FN-7714) whenever Fusion can see a GROK_API_KEY (environment or
~/.grok/user-settings.json apiKey). If no Fusion-visible key resolves and
the Grok Runtime plugin is registered, Fusion automatically routes that session
through the grok CLI runtime instead, letting the CLI own auth end-to-end.
To route a specific agent's execution through the grok CLI runtime explicitly:
- Open the agent in the dashboard (New Agent or an existing agent's detail view).
- Under Runtime Source, choose Runtime instead of Built-in Model.
- Select Grok Runtime from the runtime dropdown (sourced from
GET /api/plugins/runtimes). - Save. The agent's
runtimeConfig.runtimeHintis now"grok"; every session that agent drives resolves through this plugin'sGrokRuntimeAdapterinstead of the default pi runtime.
Automatic fallback precedence (FN-7753): explicit runtime hint > Fusion-visible key/direct endpoint > automatic CLI fallback. The fallback is only derived when no explicit runtime hint is set, the provider is grok-cli, no Fusion-visible key resolves, and runtime id "grok" is registered. The selected model is normalized from grok-cli/<id> (or grok/<id>) to <id> and sent as -m <id>.
Known limitation: explicit Runtime-mode is still model-agnostic — it does not carry a specific grok-cli/* model id through to the adapter, so GrokRuntimeAdapter.createSession() falls back to "grok/default" and omits -m. Built-in Model selections preserve the model either through the direct endpoint (when a key is visible) or through the FN-7753 automatic CLI fallback (when no key is visible).
Enable via Settings → Authentication
- Install the
grokCLI and authenticate it by any method it supports — Fusion does not need to see the key. - Open Settings → Authentication in the Fusion dashboard.
- The "Grok — via Grok CLI" card shows probe status. Click Enable once the binary is available; a non-blocking hint appears only if Fusion did not detect a key, noting the direct xAI streaming path uses
GROK_API_KEYwhen present. - Discovered Grok models (via
grok models) then merge into the model picker under thegrok-cliprovider id.
Notes
Do not invent a grok status/whoami JSON auth contract — readiness is derived from binary availability, mirroring the Cursor CLI provider. See AGENTS.md's "External-integration evidence" policy for why the release/checksum fields above stay at upstream-pending-verification.