llm-cli-gateway 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,45 @@ All notable changes to the llm-cli-gateway project.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [2.1.0] - 2026-06-07: Grok Build 0.2.32, probe drift acknowledgement, docs currency
8
+
9
+ ### Added
10
+
11
+ - Grok Build 0.2.32 support: new `leaderSocket` parameter on `grok_request` /
12
+ `grok_request_async` maps to the new `--leader-socket <PATH>` flag (isolated
13
+ leader process for local/branch Grok builds; default `~/.grok/leader.sock`).
14
+ Contract declares the flag with arity-one validation plus conformance
15
+ fixtures. The release's other changes (plugin slash commands in all
16
+ conversations, ordered rapid prompt submissions, faster grep on large
17
+ repos) are CLI-internal and inherited automatically. Probe at 0.2.32:
18
+ missingFlags/warnings clean.
19
+
20
+ ### Fixed
21
+
22
+ - Upstream-contract probe drift after the 2026-06 provider CLI upgrades
23
+ (gemini 0.45.2, grok 0.2.22, vibe 2.14.0): `CliFlagContract.hiddenFromHelp`
24
+ marks real flags hidden from a binary's `--help` (Claude `--max-turns`), and
25
+ `CliContract.acknowledgedUpstreamFlags` acknowledges upstream-only flags the
26
+ gateway never emits (29 Claude, 18 Gemini). Both are probe-only — the argv
27
+ allowlist is unchanged — with stale-marker warnings in both directions and a
28
+ new `acknowledgedExtraFlags` probe field. New pure `computeFlagDrift` plus
29
+ 7 unit tests.
30
+ - MCP server version now reports the real package version (was hardcoded
31
+ `1.0.0`).
32
+
33
+ ### Documentation
34
+
35
+ - Cross-LLM documentation currency review (Codex + Gemini + Grok + Mistral):
36
+ README tool reference gains `codex_fork_session`, `llm_request_result`,
37
+ `llm_process_health`, `upstream_contracts`, and `list_available_models`;
38
+ `claude_request` parameter list completed (`outputFormat` default is
39
+ `stream-json`); Codex `fullAuto` documented as deprecated in favour of
40
+ `sandboxMode`; Gemini approval modes include `plan`; grok/mistral upgrade
41
+ strategies documented; stale test counts, provider lists, and
42
+ `BEST_PRACTICES.md` path pointers corrected across README, AGENTS.md,
43
+ .cursorrules, CLAUDE.md, docs/guides, docs/personal-mcp (Mistral/Vibe row
44
+ added to the provider support matrix), and docs/upstream.
45
+
7
46
  ## [2.0.0] - 2026-06-04: node:sqlite migration — native module out of the prod graph
8
47
 
9
48
  Major release. Persistence moves from the native `better-sqlite3` binding to
package/README.md CHANGED
@@ -205,7 +205,7 @@ Opt-in flags (all default off) live under `[cache_awareness]` in `~/.llm-cli-gat
205
205
 
206
206
  ### Security & Quality
207
207
 
208
- - **Comprehensive Testing**: 900+ tests covering unit, integration, and regression scenarios with real CLI execution
208
+ - **Comprehensive Testing**: 1,000+ tests covering unit, integration, and regression scenarios with real CLI execution
209
209
  - **Input Validation**: Zod schemas prevent injection attacks
210
210
  - **No Secret Leakage**: Generic session descriptions only (file permissions 0o600)
211
211
  - **No ReDoS**: Bounded regex patterns prevent catastrophic backtracking
@@ -344,6 +344,7 @@ The personal-appliance surface exposes simplified validation tools for non-devel
344
344
  - `consensus_check`: check whether providers agree with a claim.
345
345
  - `ask_model`: ask one provider through the simplified surface.
346
346
  - `synthesize_validation`: run an explicit judge model after provider results have been collected.
347
+ - `list_available_models`: list the models each provider CLI exposes through the simplified surface.
347
348
  - `job_status` and `job_result`: poll and collect validation job outputs.
348
349
 
349
350
  The validation report preserves per-provider disagreement. Optional judge synthesis is explicit about which provider produced the judge job.
@@ -356,15 +357,29 @@ Execute a Claude Code request with optional session management.
356
357
 
357
358
  **Parameters:**
358
359
 
359
- - `prompt` (string, required): The prompt to send (1-100,000 chars)
360
+ - `prompt` (string, optional*): The prompt to send (1-100,000 chars). *Exactly one of `prompt` or `promptParts` is required (mutually exclusive)
360
361
  - `model` (string, optional): Model name or alias (use `list_models` for available values; supports `latest`)
361
- - `outputFormat` (string, optional): Output format ("text" or "json"), default: "text"
362
+ - `outputFormat` (string, optional): Output format (`text|json|stream-json`), default: `stream-json` — the gateway parses NDJSON usage events for token/cost observability; override to `text` only when you want unparsed stdout
362
363
  - `sessionId` (string, optional): Specific session ID to use
363
364
  - `continueSession` (boolean, optional): Continue the active session
364
365
  - `createNewSession` (boolean, optional): Always create a new session
366
+ - `forkSession` (boolean, optional): Fork the resumed session instead of appending to it
365
367
  - `allowedTools` (string[], optional): Restrict Claude tools to this allow-list
366
368
  - `disallowedTools` (string[], optional): Explicitly deny listed Claude tools
367
- - `dangerouslySkipPermissions` (boolean, optional): Request CLI-side permission bypass (legacy mode only)
369
+ - `permissionMode` (string, optional): Claude permission mode (`default|acceptEdits|plan|auto|dontAsk|bypassPermissions`); preferred over `dangerouslySkipPermissions`
370
+ - `dangerouslySkipPermissions` (boolean, optional): Deprecated — maps to `permissionMode: "bypassPermissions"`; `permissionMode` wins when both are set
371
+ - `agent` (string, optional): Named sub-agent to run as
372
+ - `agents` (string, optional): Inline agent definitions JSON
373
+ - `systemPrompt` / `appendSystemPrompt` (string, optional): Replace or extend the system prompt
374
+ - `maxBudgetUsd` (number, optional): Budget cap in USD for the request
375
+ - `maxTurns` (integer, optional): Agent-loop turn cap
376
+ - `effort` (string, optional): Reasoning effort (`low|medium|high|xhigh|max`)
377
+ - `fallbackModel` (string, optional): Auto-fallback model when the default is overloaded
378
+ - `jsonSchema` (string, optional): JSON Schema literal constraining structured output
379
+ - `addDir` (string[], optional): Additional workspace directories
380
+ - `noSessionPersistence` (boolean, optional): Ephemeral session (not persisted to disk)
381
+ - `settingSources` / `settings` / `tools` (optional): Setting sources to load, settings JSON path/literal, built-in tool restriction
382
+ - `excludeDynamicSystemPromptSections` (boolean, optional): Trim dynamic system prompt sections
368
383
  - `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
369
384
  - `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
370
385
  - `mcpServers` (string[], optional): Claude MCP servers to expose (default: `["sqry","exa","ref_tools"]`; `"trstr"` available as opt-in)
@@ -372,6 +387,10 @@ Execute a Claude Code request with optional session management.
372
387
  - `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency (44% reduction), default: false
373
388
  - `optimizeResponse` (boolean, optional): Optimize response for token efficiency (37% reduction), default: false
374
389
  - `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
390
+ - `idleTimeoutMs` (integer, optional): Kill a stuck process after output inactivity; 30,000 to 3,600,000 ms
391
+ - `worktree` (boolean|object, optional): Run inside a gateway-owned git worktree (slice λ)
392
+ - `promptParts` (object, optional): Cache-aware structured prompt `{ system?, tools?, context?, task }`; mutually exclusive with `prompt`
393
+ - `forceRefresh` (boolean, optional): Bypass dedup and force a fresh CLI run, default: false
375
394
 
376
395
  **Response extras:**
377
396
 
@@ -396,19 +415,33 @@ Execute a Codex request with optional session tracking.
396
415
 
397
416
  **Parameters:**
398
417
 
399
- - `prompt` (string, required): The prompt to send (1-100,000 chars)
400
- - `model` (string, optional): Model name or alias (use `list_models` for available values; supports `latest`, recommended: `gpt-5.4`)
401
- - `fullAuto` (boolean, optional): Enable full-auto mode, default: false
418
+ - `prompt` (string, optional*): The prompt to send (1-100,000 chars). *Exactly one of `prompt` or `promptParts` is required (mutually exclusive)
419
+ - `model` (string, optional): Model name or alias (use `list_models` for available values; supports `latest`, recommended: `gpt-5.5`)
420
+ - `fullAuto` (boolean, optional): Deprecated — expands to `--sandbox workspace-write` only (current Codex no longer accepts approval-policy flags); prefer `sandboxMode`
421
+ - `sandboxMode` (string, optional): Codex sandbox (`read-only|workspace-write|danger-full-access`)
402
422
  - `dangerouslyBypassApprovalsAndSandbox` (boolean, optional): Request Codex bypass flags
403
423
  - `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
404
424
  - `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
405
425
  - `mcpServers` (string[], optional): MCP servers expected for Codex execution context
406
426
  - `sessionId` (string, optional): Session identifier for tracking
427
+ - `resumeLatest` (boolean, optional): Resume the most recent Codex session in the current cwd (`codex exec resume --last`); ignored if `sessionId` is set
407
428
  - `createNewSession` (boolean, optional): Always create a new session
429
+ - `forceRefresh` (boolean, optional): Bypass dedup and force a fresh CLI run, default: false
430
+ - `outputFormat` (string, optional): `text` (default) or `json` (`--json` JSONL events for token usage extraction)
431
+ - `outputSchema` (string|object, optional): Codex `--output-schema` — path or inline JSON Schema
432
+ - `workingDir` (string, optional): Working root for this session (`-C`/`--cd`; new sessions only)
433
+ - `addDir` (string[], optional): Additional writable workspace directories (one `--add-dir` per entry; new sessions only)
434
+ - `ephemeral` (boolean, optional): Codex `--ephemeral` (no session persistence)
435
+ - `images` (string[], optional): Image attachments (one `-i <path>` per entry)
436
+ - `profile` (string, optional): Codex `--profile <name>` (new sessions only; ignored with a logged warning on resume)
437
+ - `configOverrides` (object, optional): Codex `-c key=value` overrides
438
+ - `ignoreRules` / `ignoreUserConfig` (boolean, optional): Codex `--ignore-rules` / `--ignore-user-config`
439
+ - `worktree` (boolean|object, optional): Run inside a gateway-owned git worktree (slice λ)
440
+ - `promptParts` (object, optional): Cache-aware structured prompt `{ system?, tools?, context?, task }`; mutually exclusive with `prompt`
408
441
  - `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency, default: false
409
442
  - `optimizeResponse` (boolean, optional): Optimize response for token efficiency, default: false
410
443
  - `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
411
- - `idleTimeoutMs` (number, optional): Kill a stuck Codex process after output inactivity; 30,000 to 3,600,000 ms
444
+ - `idleTimeoutMs` (integer, optional): Kill a stuck Codex process after output inactivity; 30,000 to 3,600,000 ms
412
445
 
413
446
  **Response extras:**
414
447
 
@@ -420,32 +453,56 @@ Execute a Codex request with optional session tracking.
420
453
  ```json
421
454
  {
422
455
  "prompt": "Create a REST API endpoint",
423
- "model": "gpt-5.4",
424
- "fullAuto": true,
456
+ "model": "gpt-5.5",
457
+ "sandboxMode": "workspace-write",
425
458
  "optimizePrompt": true
426
459
  }
427
460
  ```
428
461
 
462
+ ##### `codex_fork_session`
463
+
464
+ Fork an existing Codex session into a new branch (`codex fork <SESSION_ID|--last> <prompt>`), preserving the original session's history while the fork diverges.
465
+
466
+ **Parameters:**
467
+
468
+ - `prompt` (string, required): Prompt text for the forked session (1-100,000 chars)
469
+ - `sessionId` (string, optional): Codex session UUID to fork from (mutually exclusive with `forkLast`)
470
+ - `forkLast` (boolean, optional): Fork the most recent Codex session instead of naming one
471
+ - `model` (string, optional): Model name or alias (e.g. `gpt-5.5`, `latest`)
472
+ - `sandboxMode` (string, optional): Codex sandbox (`read-only|workspace-write|danger-full-access`)
473
+ - `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
474
+ - `idleTimeoutMs` (number, optional): Idle timeout in ms (30s-1h, omit for CLI default)
475
+
429
476
  ##### `gemini_request`
430
477
 
431
478
  Execute a Gemini CLI request with session support.
432
479
 
433
480
  **Parameters:**
434
481
 
435
- - `prompt` (string, required): The prompt to send (1-100,000 chars)
482
+ - `prompt` (string, optional*): The prompt to send (1-100,000 chars). *Exactly one of `prompt` or `promptParts` is required (mutually exclusive)
436
483
  - `model` (string, optional): Model name or alias (use `list_models` for available values; supports `latest`, `pro`, `flash`)
437
484
  - `sessionId` (string, optional): Session ID to resume
438
485
  - `resumeLatest` (boolean, optional): Resume the latest session automatically
439
486
  - `createNewSession` (boolean, optional): Always create a new session
440
- - `approvalMode` (string, optional): Gemini approval mode (`default|auto_edit|yolo`) in legacy mode
487
+ - `approvalMode` (string, optional): Gemini approval mode (`default|auto_edit|yolo|plan`) in legacy mode
441
488
  - `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
442
489
  - `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
443
490
  - `mcpServers` (string[], optional): Allowed Gemini MCP server names
444
491
  - `allowedTools` (string[], optional): Restrict Gemini tools to this allow-list
445
492
  - `includeDirs` (string[], optional): Additional workspace directories for Gemini
493
+ - `outputFormat` (string, optional): `text` (default), `json` (`-o json`), or `stream-json` (`-o stream-json`, NDJSON with usage extraction)
494
+ - `sandbox` (boolean, optional): Run Gemini in sandbox mode (`-s`)
495
+ - `policyFiles` / `adminPolicyFiles` (string[], optional): Policy / admin-policy file paths (one `--policy`/`--admin-policy` per file; paths must exist)
496
+ - `attachments` (string[], optional): Absolute file paths prepended as `@<path>` tokens to the prompt
497
+ - `skipTrust` (boolean, optional): Emit `--skip-trust` to trust the workspace for this session (required for headless runs in fresh workspaces)
498
+ - `yolo` (boolean, optional): Auto-approve all; equivalent to `approvalMode: "yolo"`. Emits `--yolo` only when `--approval-mode yolo` is not already being emitted (never both)
499
+ - `worktree` (boolean|object, optional): Run inside a gateway-owned git worktree (slice λ)
500
+ - `promptParts` (object, optional): Cache-aware structured prompt `{ system?, tools?, context?, task }`; mutually exclusive with `prompt`
446
501
  - `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency, default: false
447
502
  - `optimizeResponse` (boolean, optional): Optimize response for token efficiency, default: false
448
503
  - `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
504
+ - `idleTimeoutMs` (integer, optional): Kill a stuck process after output inactivity; 30,000 to 3,600,000 ms
505
+ - `forceRefresh` (boolean, optional): Bypass dedup and force a fresh CLI run, default: false
449
506
 
450
507
  **Response extras:**
451
508
 
@@ -469,7 +526,7 @@ Execute a Grok CLI (xAI) request with session support.
469
526
 
470
527
  **Parameters:**
471
528
 
472
- - `prompt` (string, required): The prompt to send (1-100,000 chars)
529
+ - `prompt` (string, optional*): The prompt to send (1-100,000 chars). *Exactly one of `prompt` or `promptParts` is required (mutually exclusive)
473
530
  - `model` (string, optional): Model name or alias (e.g. `grok-build`, `latest`)
474
531
  - `outputFormat` (string, optional): `"plain"` (default), `"json"`, or `"streaming-json"`
475
532
  - `sessionId` (string, optional): Session ID to resume (`--resume <id>`)
@@ -484,9 +541,35 @@ Execute a Grok CLI (xAI) request with session support.
484
541
  - `mcpServers` (string[], optional): MCP server names tracked for approvals (Grok manages its own MCP config via `grok mcp`)
485
542
  - `allowedTools` (string[], optional): Allowed built-in tools (passed as `--tools` comma list)
486
543
  - `disallowedTools` (string[], optional): Disallowed built-in tools (passed as `--disallowed-tools` comma list)
544
+ - `maxTurns` (integer, optional): Agent-loop iteration cap (`--max-turns`)
545
+ - `workingDir` (string, optional): Working directory for this invocation (`--cwd`)
546
+ - `sandbox` (string, optional): Sandbox profile for filesystem/network access (`--sandbox`, freeform; also via `GROK_SANDBOX`)
547
+ - `rules` (string, optional): Extra rules appended to the system prompt (`--rules`; supports `@file` prefix)
548
+ - `systemPromptOverride` (string, optional): Replace the agent's system prompt entirely
549
+ - `allow` / `deny` (string[], optional): Permission allow/deny rules (one `--allow`/`--deny` per entry)
550
+ - `compactionMode` (string, optional): `summary` (default) `|transcript|segments`
551
+ - `compactionDetail` (string, optional): `none|minimal|balanced|verbose` (segments mode only)
552
+ - `agent` (string, optional): Agent name or definition file path
553
+ - `agents` (string|object, optional): Inline subagent definitions JSON
554
+ - `bestOfN` (integer, optional): Run the task N ways in parallel and pick the best (headless only)
555
+ - `check` (boolean, optional): Append a self-verification loop (headless only)
556
+ - `disableWebSearch` (boolean, optional): Disable web search and remote retrieval tools
557
+ - `todoGate` (boolean, optional): Enable runtime turn-end TodoGate (session-scoped)
558
+ - `verbatim` (boolean, optional): Send the prompt exactly as given (also skips gateway prompt optimisation)
559
+ - `promptFile` / `promptJson` / `single` (optional): Single-turn prompt from a file / JSON blocks / literal
560
+ - `experimentalMemory` / `noMemory` (boolean, optional): Enable/disable cross-session memory
561
+ - `noAltScreen` / `noPlan` / `noSubagents` (boolean, optional): Disable alt screen / plan mode / subagent spawning
562
+ - `oauth` (boolean, optional): Use OAuth during authentication
563
+ - `restoreCode` (boolean, optional): Check out the original session commit when resuming
564
+ - `leaderSocket` (string, optional): Custom leader socket path (`--leader-socket`, Grok 0.2.32+; default `~/.grok/leader.sock`) — targets an isolated leader process, e.g. a local/branch Grok build
565
+ - `nativeWorktree` (boolean|string, optional): Grok's own `--worktree` flag (`true` → bare, string → named); distinct from the gateway `worktree` option
566
+ - `worktree` (boolean|object, optional): Run inside a gateway-owned git worktree (slice λ)
567
+ - `promptParts` (object, optional): Cache-aware structured prompt `{ system?, tools?, context?, task }`; mutually exclusive with `prompt`
487
568
  - `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency, default: false
488
569
  - `optimizeResponse` (boolean, optional): Optimize response for token efficiency, default: false
489
570
  - `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
571
+ - `idleTimeoutMs` (integer, optional): Kill a stuck process after output inactivity; 30,000 to 3,600,000 ms
572
+ - `forceRefresh` (boolean, optional): Bypass dedup and force a fresh CLI run, default: false
490
573
 
491
574
  **Example:**
492
575
 
@@ -740,6 +823,21 @@ Run a Mistral Vibe agentic coding request. Like `grok_request` in shape, but wit
740
823
  - `disallowedTools` (string[], optional): Accepted for parity with the other providers; ignored at the CLI boundary with a logged warning.
741
824
  - `outputFormat` (string, optional): Vibe 2.x values are `"text"`, `"json"`, or `"streaming"`; legacy aliases `"plain"` and `"stream-json"` are accepted and normalized before spawn.
742
825
  - `sessionId` / `resumeLatest` / `createNewSession`: standard session controls. Current Vibe defaults session logging to enabled; if an older config has `[session_logging] enabled = false`, `doctor --json` surfaces an actionable next-action.
826
+ - `trust` (boolean, optional): Emit `--trust` so Vibe trusts the cwd for this invocation only (not persisted; skips the interactive trust prompt)
827
+ - `maxTurns` (integer, optional): Agent-loop iteration cap (`--max-turns`, programmatic mode only)
828
+ - `maxPrice` (number, optional): Interrupt when cumulative cost crosses this USD cap (`--max-price`, programmatic mode only)
829
+ - `maxTokens` (integer, optional): Cap cumulative prompt + completion tokens (`--max-tokens`, programmatic mode only)
830
+ - `workingDir` (string, optional): Change to this directory before running (`--workdir`)
831
+ - `addDir` (string[], optional): Additional writable workspace directories (one `--add-dir` per entry)
832
+ - `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
833
+ - `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
834
+ - `mcpServers` (string[], optional): MCP server names tracked for approvals (Vibe manages its own MCP config via `vibe mcp`)
835
+ - `worktree` (boolean|object, optional): Run inside a gateway-owned git worktree (slice λ)
836
+ - `promptParts` (object, optional): Cache-aware structured prompt `{ system?, tools?, context?, task }`; mutually exclusive with `prompt`
837
+ - `optimizePrompt` / `optimizeResponse` (boolean, optional): Token-efficiency optimisation, default: false
838
+ - `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
839
+ - `idleTimeoutMs` (integer, optional): Kill a stuck process after output inactivity; 30,000 to 3,600,000 ms
840
+ - `forceRefresh` (boolean, optional): Bypass dedup and force a fresh CLI run, default: false
743
841
 
744
842
  ##### `claude_request_async` / `codex_request_async` / `gemini_request_async` / `grok_request_async` / `mistral_request_async`
745
843
 
@@ -778,10 +876,33 @@ List recent MCP-managed approval decisions recorded by the gateway.
778
876
  **Parameters:**
779
877
 
780
878
  - `limit` (number, optional): Max records (1-500), default: 50
781
- - `cli` (string, optional): Filter by `"claude"`, `"codex"`, or `"gemini"`
879
+ - `cli` (string, optional): Filter by `"claude"`, `"codex"`, `"gemini"`, `"grok"`, or `"mistral"`
782
880
 
783
881
  Approval records are persisted to `~/.llm-cli-gateway/approvals.jsonl`.
784
882
 
883
+ ##### `llm_request_result`
884
+
885
+ Read back any persisted request — sync or async — by its correlation ID. Every response echoes its ID in `structuredContent.correlationId`; pass it here to recover the persisted prompt/response after the inline result is gone. Reads the flight recorder, so it works independently of async-job persistence (returns "not found" when flight recording is disabled).
886
+
887
+ **Parameters:**
888
+
889
+ - `correlationId` (string, required): Correlation ID from a prior request
890
+ - `maxChars` (number, optional): Max chars of the persisted response to return (1,000-2,000,000)
891
+ - `includePrompt` (boolean, optional): Include the full persisted prompt text, default: false
892
+
893
+ ##### `llm_process_health`
894
+
895
+ Report gateway process health: async-job manager state plus the resolved persistence block (`backend`, `dbPath`, config sources). Use it to confirm which config file and SQLite paths the gateway is actually running under.
896
+
897
+ ##### `upstream_contracts`
898
+
899
+ Return the gateway's declared provider CLI contracts, optionally probing the installed binaries for drift.
900
+
901
+ **Parameters:**
902
+
903
+ - `cli` (string, optional): Filter (`claude|codex|gemini|grok|mistral`)
904
+ - `probeInstalled` (boolean, optional, default `false`): Run local `--help` probes and compare advertised flags against the declared contract — strongly recommended after any provider CLI upgrade. The probe reports `missingFlags`, `extraFlags`, `acknowledgedExtraFlags` (known upstream-only flags filtered from `extraFlags`), `discoveredFlags`, and stale-marker `warnings`.
905
+
785
906
  #### Session Management Tools
786
907
 
787
908
  ##### `session_create`
@@ -924,6 +1045,9 @@ Plan or run an upgrade for one CLI.
924
1045
  - Codex latest: `codex update`
925
1046
  - Codex explicit target: `npm install -g @openai/codex@<target>`
926
1047
  - Gemini: `npm install -g @google/gemini-cli@<target>`
1048
+ - Grok latest: `grok update`
1049
+ - Grok explicit target: `grok update --version <target>`
1050
+ - Mistral (Vibe): dispatches to the detected installer (`pip`/`uv`/`brew`); errors with guidance when none is detected (Vibe ships no self-update command)
927
1051
 
928
1052
  **Example dry run:**
929
1053
 
package/dist/index.d.ts CHANGED
@@ -251,6 +251,7 @@ export declare function prepareGrokRequest(params: {
251
251
  noSubagents?: boolean;
252
252
  oauth?: boolean;
253
253
  restoreCode?: boolean;
254
+ leaderSocket?: string;
254
255
  nativeWorktree?: boolean | string;
255
256
  }, runtime?: GatewayServerRuntime): CliRequestPrep | ExtendedToolResponse;
256
257
  export declare function prepareMistralRequest(params: {
@@ -376,6 +377,7 @@ export interface GrokRequestParams {
376
377
  noSubagents?: boolean;
377
378
  oauth?: boolean;
378
379
  restoreCode?: boolean;
380
+ leaderSocket?: string;
379
381
  nativeWorktree?: boolean | string;
380
382
  worktree?: boolean | {
381
383
  name?: string;
package/dist/index.js CHANGED
@@ -143,8 +143,8 @@ function loadSkills() {
143
143
  const loadedSkills = loadSkills();
144
144
  const SERVER_INSTRUCTIONS = `llm-cli-gateway: Multi-LLM orchestration via MCP.
145
145
 
146
- Tools: claude_request, codex_request, gemini_request, grok_request, mistral_request (sync) | *_request_async (async)
147
- Validation: validate_with_models, second_opinion, compare_answers, red_team_review, consensus_check, ask_model, synthesize_validation
146
+ Tools: claude_request, codex_request, gemini_request, grok_request, mistral_request (sync) | *_request_async (async) | codex_fork_session (fork a Codex session into a new branch)
147
+ Validation: validate_with_models, second_opinion, compare_answers, red_team_review, consensus_check, ask_model, synthesize_validation, list_available_models | job_status/job_result (validation jobs)
148
148
  Jobs: llm_job_status, llm_job_result, llm_job_cancel
149
149
  Sessions: session_create, session_list, session_set_active, session_get, session_delete, session_clear_all
150
150
  Other: list_models, cli_versions, upstream_contracts (use --probe-installed after CLI upgrades to detect drift), cli_upgrade, approval_list, llm_process_health, llm_request_result (read back any persisted request — sync or async — by correlationId)
@@ -159,7 +159,7 @@ Key behaviors:
159
159
  Skills (full docs via MCP resources):
160
160
  ${loadedSkills.map(s => `- skills://${s.name} — ${s.description}`).join("\n")}`;
161
161
  function newGatewayMcpServer() {
162
- return new McpServer({ name: "llm-cli-gateway", version: "1.0.0" }, { instructions: SERVER_INSTRUCTIONS });
162
+ return new McpServer({ name: "llm-cli-gateway", version: packageVersion() }, { instructions: SERVER_INSTRUCTIONS });
163
163
  }
164
164
  let sessionManager;
165
165
  let db = null;
@@ -1474,6 +1474,9 @@ export function prepareGrokRequest(params, runtime = resolveGatewayServerRuntime
1474
1474
  if (params.restoreCode) {
1475
1475
  args.push("--restore-code");
1476
1476
  }
1477
+ if (params.leaderSocket) {
1478
+ args.push("--leader-socket", params.leaderSocket);
1479
+ }
1477
1480
  if (params.nativeWorktree === true) {
1478
1481
  args.push("--worktree");
1479
1482
  }
@@ -1976,6 +1979,7 @@ export async function handleGrokRequest(deps, params) {
1976
1979
  noSubagents: params.noSubagents,
1977
1980
  oauth: params.oauth,
1978
1981
  restoreCode: params.restoreCode,
1982
+ leaderSocket: params.leaderSocket,
1979
1983
  nativeWorktree: params.nativeWorktree,
1980
1984
  }, runtime);
1981
1985
  if (!("args" in prep))
@@ -2133,6 +2137,7 @@ export async function handleGrokRequestAsync(deps, params) {
2133
2137
  noSubagents: params.noSubagents,
2134
2138
  oauth: params.oauth,
2135
2139
  restoreCode: params.restoreCode,
2140
+ leaderSocket: params.leaderSocket,
2136
2141
  nativeWorktree: params.nativeWorktree,
2137
2142
  }, runtime);
2138
2143
  if (!("args" in prep))
@@ -3488,12 +3493,17 @@ export function createGatewayServer(deps = {}) {
3488
3493
  .boolean()
3489
3494
  .optional()
3490
3495
  .describe("Grok --restore-code: check out the original session commit when resuming."),
3496
+ leaderSocket: z
3497
+ .string()
3498
+ .min(1)
3499
+ .optional()
3500
+ .describe("Grok 0.2.32+ --leader-socket <PATH>: custom leader socket path (default ~/.grok/leader.sock). Targets an isolated leader process, e.g. a local/branch Grok build; name it ~/.grok/leader-*.sock to keep `grok leader list/kill` discovery working."),
3491
3501
  nativeWorktree: z
3492
3502
  .union([z.boolean(), z.string().min(1)])
3493
3503
  .optional()
3494
3504
  .describe("Grok -w/--worktree: native CLI worktree flag (`true` → bare `--worktree`, string → named). NOT gateway slice λ `worktree`."),
3495
3505
  worktree: WORKTREE_SCHEMA.optional(),
3496
- }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, nativeWorktree, worktree, }) => {
3506
+ }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, leaderSocket, nativeWorktree, worktree, }) => {
3497
3507
  return handleGrokRequest({ sessionManager, logger, runtime }, {
3498
3508
  prompt,
3499
3509
  promptParts,
@@ -3542,6 +3552,7 @@ export function createGatewayServer(deps = {}) {
3542
3552
  noSubagents,
3543
3553
  oauth,
3544
3554
  restoreCode,
3555
+ leaderSocket,
3545
3556
  nativeWorktree,
3546
3557
  worktree,
3547
3558
  });
@@ -4298,12 +4309,17 @@ export function createGatewayServer(deps = {}) {
4298
4309
  .boolean()
4299
4310
  .optional()
4300
4311
  .describe("Grok --restore-code: check out the original session commit when resuming."),
4312
+ leaderSocket: z
4313
+ .string()
4314
+ .min(1)
4315
+ .optional()
4316
+ .describe("Grok 0.2.32+ --leader-socket <PATH>: custom leader socket path (default ~/.grok/leader.sock). Targets an isolated leader process, e.g. a local/branch Grok build; name it ~/.grok/leader-*.sock to keep `grok leader list/kill` discovery working."),
4301
4317
  nativeWorktree: z
4302
4318
  .union([z.boolean(), z.string().min(1)])
4303
4319
  .optional()
4304
4320
  .describe("Grok -w/--worktree: native CLI worktree flag (`true` → bare `--worktree`, string → named). NOT gateway slice λ `worktree`."),
4305
4321
  worktree: WORKTREE_SCHEMA.optional(),
4306
- }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, nativeWorktree, worktree, }) => {
4322
+ }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, leaderSocket, nativeWorktree, worktree, }) => {
4307
4323
  return handleGrokRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
4308
4324
  prompt,
4309
4325
  promptParts,
@@ -4351,6 +4367,7 @@ export function createGatewayServer(deps = {}) {
4351
4367
  noSubagents,
4352
4368
  oauth,
4353
4369
  restoreCode,
4370
+ leaderSocket,
4354
4371
  nativeWorktree,
4355
4372
  worktree,
4356
4373
  });
@@ -5,6 +5,7 @@ export interface CliFlagContract {
5
5
  values?: readonly string[];
6
6
  pattern?: RegExp;
7
7
  description: string;
8
+ hiddenFromHelp?: boolean;
8
9
  }
9
10
  export interface CliUpstreamMetadata {
10
11
  sourceUrls: readonly string[];
@@ -32,6 +33,7 @@ export interface CliContract {
32
33
  resumeMaxPositionals?: number;
33
34
  resumeOnlyFlags?: readonly string[];
34
35
  resumeForbiddenFlags?: readonly string[];
36
+ acknowledgedUpstreamFlags?: readonly string[];
35
37
  upstreamMetadata?: CliUpstreamMetadata;
36
38
  }
37
39
  export interface CliContractFixture {
@@ -57,6 +59,13 @@ export declare function assertUpstreamCliArgs(cli: CliType, args: readonly strin
57
59
  export declare function validateUpstreamCliEnv(cli: CliType, env: Record<string, string> | undefined): ContractValidationResult;
58
60
  export declare function assertUpstreamCliEnv(cli: CliType, env: Record<string, string> | undefined): void;
59
61
  export declare function extractDiscoveredFlags(helpText: string): readonly string[];
62
+ export interface FlagDriftResult {
63
+ missingFlags: string[];
64
+ extraFlags: readonly string[];
65
+ acknowledgedExtraFlags: readonly string[];
66
+ warnings: string[];
67
+ }
68
+ export declare function computeFlagDrift(contract: CliContract, helpText: string, discoveredFlags: readonly string[]): FlagDriftResult;
60
69
  export interface InstalledCliContractProbe {
61
70
  cli: CliType;
62
71
  executable: string;
@@ -66,6 +75,7 @@ export interface InstalledCliContractProbe {
66
75
  checkedHelpCommands: string[][];
67
76
  missingFlags: string[];
68
77
  extraFlags: readonly string[];
78
+ acknowledgedExtraFlags: readonly string[];
69
79
  discoveredFlags: readonly string[];
70
80
  helpHash?: string;
71
81
  versionHint?: string;
@@ -99,7 +99,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
99
99
  pattern: /^[0-9]+(?:\.[0-9]+)?$/,
100
100
  description: "Budget cap in USD",
101
101
  },
102
- "--max-turns": { arity: "one", pattern: /^[1-9][0-9]*$/, description: "Turn cap" },
102
+ "--max-turns": {
103
+ arity: "one",
104
+ pattern: /^[1-9][0-9]*$/,
105
+ description: "Turn cap",
106
+ hiddenFromHelp: true,
107
+ },
103
108
  "--effort": { arity: "one", values: EFFORT_LEVELS, description: "Reasoning effort" },
104
109
  "--exclude-dynamic-system-prompt-sections": {
105
110
  arity: "none",
@@ -136,6 +141,37 @@ export const UPSTREAM_CLI_CONTRACTS = {
136
141
  description: 'Restrict the available built-in tool set ("" disables all)',
137
142
  },
138
143
  },
144
+ acknowledgedUpstreamFlags: [
145
+ "--allow-dangerously-skip-permissions",
146
+ "--allowed",
147
+ "--bare",
148
+ "--betas",
149
+ "--brief",
150
+ "--chrome",
151
+ "--dangerously-skip-permissions",
152
+ "--debug",
153
+ "--debug-file",
154
+ "--disable-slash-commands",
155
+ "--disallowed",
156
+ "--file",
157
+ "--from-pr",
158
+ "--ide",
159
+ "--include-hook-events",
160
+ "--mcp-debug",
161
+ "--name",
162
+ "--no-chrome",
163
+ "--plugin-dir",
164
+ "--plugin-url",
165
+ "--print",
166
+ "--prompt-suggestions",
167
+ "--remote-control",
168
+ "--remote-control-session-name-prefix",
169
+ "--replay-user-messages",
170
+ "--resume",
171
+ "--tmux",
172
+ "--version",
173
+ "--worktree",
174
+ ],
139
175
  env: {},
140
176
  conformanceFixtures: [
141
177
  {
@@ -518,6 +554,26 @@ export const UPSTREAM_CLI_CONTRACTS = {
518
554
  description: "Auto-approve all actions (gemini -y/--yolo). Functionally equivalent to --approval-mode yolo; the gateway emits at most one of the two.",
519
555
  },
520
556
  },
557
+ acknowledgedUpstreamFlags: [
558
+ "--accept-raw-output-risk",
559
+ "--acp",
560
+ "--debug",
561
+ "--delete-session",
562
+ "--experimental-acp",
563
+ "--extensions",
564
+ "--list-extensions",
565
+ "--list-sessions",
566
+ "--output-format",
567
+ "--prompt",
568
+ "--prompt-interactive",
569
+ "--raw-output",
570
+ "--sandbox",
571
+ "--screen-reader",
572
+ "--session-file",
573
+ "--session-id",
574
+ "--version",
575
+ "--worktree",
576
+ ],
521
577
  env: {},
522
578
  conformanceFixtures: [
523
579
  {
@@ -612,6 +668,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
612
668
  "noSubagents",
613
669
  "oauth",
614
670
  "restoreCode",
671
+ "leaderSocket",
615
672
  "nativeWorktree",
616
673
  ],
617
674
  flags: {
@@ -693,6 +750,10 @@ export const UPSTREAM_CLI_CONTRACTS = {
693
750
  arity: "none",
694
751
  description: "Check out the original session commit when resuming",
695
752
  },
753
+ "--leader-socket": {
754
+ arity: "one",
755
+ description: "Custom leader socket path (isolated leader, Grok 0.2.32+)",
756
+ },
696
757
  "--single": { arity: "one", description: "Single-turn prompt" },
697
758
  "--todo-gate": { arity: "none", description: "Enable runtime turn-end TodoGate" },
698
759
  "--verbatim": { arity: "none", description: "Send prompt exactly as given" },
@@ -843,6 +904,18 @@ export const UPSTREAM_CLI_CONTRACTS = {
843
904
  ],
844
905
  expect: "pass",
845
906
  },
907
+ {
908
+ id: "grok-leader-socket",
909
+ description: "Grok 0.2.32: --leader-socket <PATH> is accepted",
910
+ args: ["-p", "hello", "--leader-socket", "/home/user/.grok/leader-branch.sock"],
911
+ expect: "pass",
912
+ },
913
+ {
914
+ id: "grok-leader-socket-missing-value",
915
+ description: "Grok 0.2.32: --leader-socket without a path is rejected (arity one)",
916
+ args: ["-p", "hello", "--leader-socket"],
917
+ expect: "fail",
918
+ },
846
919
  ],
847
920
  },
848
921
  mistral: {
@@ -1220,6 +1293,42 @@ export function extractDiscoveredFlags(helpText) {
1220
1293
  }
1221
1294
  return Array.from(discovered).sort();
1222
1295
  }
1296
+ export function computeFlagDrift(contract, helpText, discoveredFlags) {
1297
+ const warnings = [];
1298
+ const missingFlags = [];
1299
+ for (const [flag, spec] of Object.entries(contract.flags)) {
1300
+ const inHelp = helpText.includes(flag);
1301
+ if (spec.hiddenFromHelp) {
1302
+ if (inHelp) {
1303
+ warnings.push(`${flag} is marked hiddenFromHelp but now appears in ${contract.executable} help output; remove the hiddenFromHelp marker from the contract`);
1304
+ }
1305
+ continue;
1306
+ }
1307
+ if (!inHelp)
1308
+ missingFlags.push(flag);
1309
+ }
1310
+ const contractFlagSet = new Set(Object.keys(contract.flags));
1311
+ const acknowledged = new Set(contract.acknowledgedUpstreamFlags ?? []);
1312
+ const extraFlags = [];
1313
+ const acknowledgedExtraFlags = [];
1314
+ for (const flag of discoveredFlags) {
1315
+ if (contractFlagSet.has(flag))
1316
+ continue;
1317
+ if (acknowledged.has(flag)) {
1318
+ acknowledgedExtraFlags.push(flag);
1319
+ }
1320
+ else {
1321
+ extraFlags.push(flag);
1322
+ }
1323
+ }
1324
+ const discoveredSet = new Set(discoveredFlags);
1325
+ for (const flag of acknowledged) {
1326
+ if (!discoveredSet.has(flag)) {
1327
+ warnings.push(`acknowledged upstream flag ${flag} no longer appears in ${contract.executable} help output; remove it from acknowledgedUpstreamFlags`);
1328
+ }
1329
+ }
1330
+ return { missingFlags, extraFlags, acknowledgedExtraFlags, warnings };
1331
+ }
1223
1332
  export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
1224
1333
  const contract = UPSTREAM_CLI_CONTRACTS[cli];
1225
1334
  const outputs = [];
@@ -1252,6 +1361,7 @@ export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
1252
1361
  checkedHelpCommands: contract.helpArgs,
1253
1362
  missingFlags: [],
1254
1363
  extraFlags: [],
1364
+ acknowledgedExtraFlags: [],
1255
1365
  discoveredFlags: [],
1256
1366
  helpHash: undefined,
1257
1367
  versionHint: undefined,
@@ -1265,10 +1375,9 @@ export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
1265
1375
  }
1266
1376
  }
1267
1377
  const helpText = outputs.join("\n");
1268
- const missingFlags = Object.keys(contract.flags).filter(flag => !helpText.includes(flag));
1269
1378
  const discoveredFlags = extractDiscoveredFlags(helpText);
1270
- const contractFlagSet = new Set(Object.keys(contract.flags));
1271
- const extraFlags = discoveredFlags.filter(f => !contractFlagSet.has(f));
1379
+ const drift = computeFlagDrift(contract, helpText, discoveredFlags);
1380
+ warnings.push(...drift.warnings);
1272
1381
  const versionMatch = helpText.match(/^\s*(?:[A-Za-z][\w .-]+)?v?\d+\.\d+\S*/m);
1273
1382
  const versionHint = versionMatch ? versionMatch[0].trim().slice(0, 80) : undefined;
1274
1383
  const helpHash = createHash("sha256").update(helpText).digest("hex");
@@ -1279,8 +1388,9 @@ export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
1279
1388
  resolvedArgs,
1280
1389
  available: true,
1281
1390
  checkedHelpCommands: contract.helpArgs,
1282
- missingFlags,
1283
- extraFlags,
1391
+ missingFlags: drift.missingFlags,
1392
+ extraFlags: drift.extraFlags,
1393
+ acknowledgedExtraFlags: drift.acknowledgedExtraFlags,
1284
1394
  discoveredFlags,
1285
1395
  helpHash,
1286
1396
  versionHint,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "mcpName": "io.github.verivus-oss/llm-cli-gateway",
5
5
  "description": "MCP server providing unified access to Claude Code, Codex, Gemini, Grok, and Mistral Vibe CLIs with session management, retry logic, async job orchestration, durable job results, and cross-LLM validation.",
6
6
  "license": "MIT",