llm-cli-gateway 1.16.0 → 1.16.2

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,30 @@ All notable changes to the llm-cli-gateway project.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [1.16.2] - 2026-05-29 — release formatting follow-up
8
+
9
+ Patch release that keeps the Mistral Vibe CLI contract fixes from `1.16.1`
10
+ and fixes the Prettier check failure on the release commit.
11
+
12
+ ### Fixed
13
+
14
+ - Formatted the new Vibe session-logging doctor tests so the repository CI
15
+ format gate passes.
16
+
17
+ ## [1.16.1] - 2026-05-29 — align Mistral Vibe CLI contract
18
+
19
+ Patch release for the current `mistral-vibe` CLI surface.
20
+
21
+ ### Fixed
22
+
23
+ - Updated Mistral Vibe requests to emit `--output text|json|streaming` instead
24
+ of the removed `--output-format` flag.
25
+ - Kept legacy MCP aliases working by mapping `plain` to `text` and
26
+ `stream-json` to `streaming`.
27
+ - Added `maxTokens` support for Mistral Vibe via `--max-tokens`.
28
+ - Updated Vibe install, upgrade, and doctor guidance for the current
29
+ `mistral-vibe` package and default-on session logging.
30
+
7
31
  ## [1.16.0] - 2026-05-29 — remove Redis session dependency
8
32
 
9
33
  Feature release that removes the optional Redis/ioredis layer from the
package/README.md CHANGED
@@ -253,13 +253,16 @@ grok login # OAuth flow, or set GROK_CODE_XAI_API_KEY
253
253
 
254
254
  ```bash
255
255
  # Pick one — the gateway's cli_upgrade auto-detects which one you used.
256
- pip install vibe-cli
257
- uv tool install vibe-cli
256
+ curl -LsSf https://mistral.ai/vibe/install.sh | bash
257
+ pip install mistral-vibe
258
+ uv tool install mistral-vibe
258
259
  brew install mistral-vibe
259
260
 
260
261
  vibe auth login
261
- # Required for `mistral_request --resume` / `--continue` to persist sessions:
262
- vibe config set session_logging.enabled true # or edit ~/.vibe/config.toml
262
+ # Current Vibe defaults session logging to enabled. If an older config disabled it,
263
+ # edit ~/.vibe/config.toml and set:
264
+ # [session_logging]
265
+ # enabled = true
263
266
  ```
264
267
 
265
268
  Vibe-specific notes:
@@ -736,7 +739,8 @@ Run a Mistral Vibe agentic coding request. Like `grok_request` in shape, but wit
736
739
  - `permissionMode`: `default | plan | accept-edits | auto-approve | chat | explore | lean` — emitted as `--agent <mode>`. Defaults to `auto-approve` in programmatic mode.
737
740
  - `allowedTools` (string[], optional): One `--enabled-tools <tool>` flag per entry (allow-list only).
738
741
  - `disallowedTools` (string[], optional): Accepted for parity with the other providers; ignored at the CLI boundary with a logged warning.
739
- - `sessionId` / `resumeLatest` / `createNewSession`: standard session controls. Continuity requires `[session_logging] enabled = true` in `~/.vibe/config.toml` `doctor --json` surfaces an actionable next-action when the toggle is missing.
742
+ - `outputFormat` (string, optional): Vibe 2.x values are `"text"`, `"json"`, or `"streaming"`; legacy aliases `"plain"` and `"stream-json"` are accepted and normalized before spawn.
743
+ - `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.
740
744
 
741
745
  ##### `claude_request_async` / `codex_request_async` / `gemini_request_async` / `grok_request_async` / `mistral_request_async`
742
746
 
@@ -1,6 +1,8 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { executeCli } from "./executor.js";
3
3
  import { getProviderRuntimeStatus } from "./provider-status.js";
4
+ const MISTRAL_VIBE_PACKAGE = "mistral-vibe";
5
+ const LEGACY_VIBE_PACKAGE = "vibe-cli";
4
6
  /**
5
7
  * Detect how Vibe was installed on this machine. Vibe does not self-update, so
6
8
  * cli_upgrade has to dispatch to the package manager that owns the binary.
@@ -16,15 +18,19 @@ export function detectMistralInstallMethod(exec = (cmd, args) => {
16
18
  stdout: result.stdout || "",
17
19
  };
18
20
  }) {
19
- const pip = exec("pip", ["show", "vibe-cli"]);
20
- if (pip.exitCode === 0 && /Name:\s*vibe-cli/i.test(pip.stdout)) {
21
+ const pip = exec("pip", ["show", MISTRAL_VIBE_PACKAGE]);
22
+ if (pip.exitCode === 0 && /Name:\s*mistral-vibe/i.test(pip.stdout)) {
23
+ return "pip";
24
+ }
25
+ const legacyPip = exec("pip", ["show", LEGACY_VIBE_PACKAGE]);
26
+ if (legacyPip.exitCode === 0 && /Name:\s*vibe-cli/i.test(legacyPip.stdout)) {
21
27
  return "pip";
22
28
  }
23
29
  const uv = exec("uv", ["tool", "list"]);
24
- if (uv.exitCode === 0 && /\bvibe(?:-cli)?\b/i.test(uv.stdout)) {
30
+ if (uv.exitCode === 0 && /\b(?:mistral-vibe|vibe-cli|vibe)\b/i.test(uv.stdout)) {
25
31
  return "uv";
26
32
  }
27
- const brew = exec("brew", ["list", "mistral-vibe"]);
33
+ const brew = exec("brew", ["list", MISTRAL_VIBE_PACKAGE]);
28
34
  if (brew.exitCode === 0) {
29
35
  return "brew";
30
36
  }
@@ -154,7 +160,9 @@ function buildMistralUpgradePlan(normalizedTarget, detectMistral) {
154
160
  // (we surface it as a no-op plan with `command: ""` so runCliUpgrade can
155
161
  // throw before spawning anything).
156
162
  if (method === "pip") {
157
- const pkg = normalizedTarget === "latest" ? "vibe-cli" : `vibe-cli==${normalizedTarget}`;
163
+ const pkg = normalizedTarget === "latest"
164
+ ? MISTRAL_VIBE_PACKAGE
165
+ : `${MISTRAL_VIBE_PACKAGE}==${normalizedTarget}`;
158
166
  return {
159
167
  cli: "mistral",
160
168
  target: normalizedTarget,
@@ -170,7 +178,7 @@ function buildMistralUpgradePlan(normalizedTarget, detectMistral) {
170
178
  cli: "mistral",
171
179
  target: normalizedTarget,
172
180
  command: "uv",
173
- args: ["tool", "upgrade", "vibe-cli"],
181
+ args: ["tool", "upgrade", MISTRAL_VIBE_PACKAGE],
174
182
  strategy: "uv-tool-upgrade",
175
183
  requiresNetwork: true,
176
184
  note: normalizedTarget === "latest"
@@ -183,7 +191,7 @@ function buildMistralUpgradePlan(normalizedTarget, detectMistral) {
183
191
  cli: "mistral",
184
192
  target: normalizedTarget,
185
193
  command: "brew",
186
- args: ["upgrade", "mistral-vibe"],
194
+ args: ["upgrade", MISTRAL_VIBE_PACKAGE],
187
195
  strategy: "brew-upgrade",
188
196
  requiresNetwork: true,
189
197
  note: normalizedTarget === "latest"
@@ -191,7 +199,7 @@ function buildMistralUpgradePlan(normalizedTarget, detectMistral) {
191
199
  : "brew upgrade does not honour explicit version targets; running upgrade to latest.",
192
200
  };
193
201
  }
194
- throw new Error("Could not detect how Mistral Vibe was installed. Install it via pip (`pip install vibe-cli`), uv (`uv tool install vibe-cli`), or Homebrew (`brew install mistral-vibe`) before running cli_upgrade.");
202
+ throw new Error("Could not detect how Mistral Vibe was installed. Install it via pip (`pip install mistral-vibe`), uv (`uv tool install mistral-vibe`), or Homebrew (`brew install mistral-vibe`) before running cli_upgrade.");
195
203
  }
196
204
  async function fallbackCliVersion(cli, args) {
197
205
  try {
package/dist/doctor.d.ts CHANGED
@@ -51,10 +51,10 @@ export interface GeminiConfigStatus {
51
51
  next_actions: string[];
52
52
  }
53
53
  /**
54
- * Probe ~/.vibe/config.toml to see whether session_logging is enabled. Vibe
55
- * persists session logs (which sessionId/--continue depends on) only when
56
- * `[session_logging] enabled = true` is set. The probe is read-only: the
57
- * gateway never mutates this file.
54
+ * Probe ~/.vibe/config.toml to see whether session_logging is enabled. Current
55
+ * Mistral Vibe defaults session logging to enabled; an explicit
56
+ * `[session_logging] enabled = false` disables `--continue` / `--resume`.
57
+ * The probe is read-only: the gateway never mutates this file.
58
58
  */
59
59
  export declare function checkVibeSessionLogging(home?: string): VibeSessionLoggingStatus;
60
60
  /**
package/dist/doctor.js CHANGED
@@ -10,10 +10,10 @@ import { loadCacheAwarenessConfig } from "./config.js";
10
10
  import { computeGlobalCacheStats } from "./cache-stats.js";
11
11
  import { FlightRecorder, resolveFlightRecorderDbPath } from "./flight-recorder.js";
12
12
  /**
13
- * Probe ~/.vibe/config.toml to see whether session_logging is enabled. Vibe
14
- * persists session logs (which sessionId/--continue depends on) only when
15
- * `[session_logging] enabled = true` is set. The probe is read-only: the
16
- * gateway never mutates this file.
13
+ * Probe ~/.vibe/config.toml to see whether session_logging is enabled. Current
14
+ * Mistral Vibe defaults session logging to enabled; an explicit
15
+ * `[session_logging] enabled = false` disables `--continue` / `--resume`.
16
+ * The probe is read-only: the gateway never mutates this file.
17
17
  */
18
18
  export function checkVibeSessionLogging(home = homedir()) {
19
19
  const configPath = join(home, ".vibe", "config.toml");
@@ -21,26 +21,28 @@ export function checkVibeSessionLogging(home = homedir()) {
21
21
  return {
22
22
  config_path: configPath,
23
23
  config_present: false,
24
- session_logging_enabled: false,
25
- note: "~/.vibe/config.toml not found. Run `vibe config set session_logging.enabled true` or create the file with a [session_logging]\\nenabled = true block.",
24
+ session_logging_enabled: true,
25
+ note: "~/.vibe/config.toml not found; current Vibe defaults session_logging.enabled to true. If resume fails, create ~/.vibe/config.toml with [session_logging]\\nenabled = true.",
26
26
  };
27
27
  }
28
28
  try {
29
29
  const text = readFileSync(configPath, "utf8");
30
30
  const enabled = parseVibeSessionLoggingEnabled(text);
31
- if (enabled) {
31
+ if (enabled !== false) {
32
32
  return {
33
33
  config_path: configPath,
34
34
  config_present: true,
35
35
  session_logging_enabled: true,
36
- note: "session_logging.enabled is true; --continue/--resume will work for mistral_request.",
36
+ note: enabled === true
37
+ ? "session_logging.enabled is true; --continue/--resume will work for mistral_request."
38
+ : "session_logging.enabled is not set; current Vibe defaults it to true.",
37
39
  };
38
40
  }
39
41
  return {
40
42
  config_path: configPath,
41
43
  config_present: true,
42
44
  session_logging_enabled: false,
43
- note: "[session_logging] enabled = false (or missing). Run `vibe config set session_logging.enabled true` or edit ~/.vibe/config.toml so mistral_request --resume / --continue can persist sessions.",
45
+ note: "[session_logging] enabled = false. Edit ~/.vibe/config.toml so the [session_logging] block sets enabled = true before using mistral_request --resume / --continue.",
44
46
  };
45
47
  }
46
48
  catch (err) {
@@ -54,7 +56,7 @@ export function checkVibeSessionLogging(home = homedir()) {
54
56
  }
55
57
  }
56
58
  /**
57
- * Tiny TOML probe focused on `[session_logging] enabled = true`. Avoids pulling
59
+ * Tiny TOML probe focused on `[session_logging] enabled = ...`. Avoids pulling
58
60
  * in the full `toml` parser when only one boolean is needed.
59
61
  */
60
62
  function parseVibeSessionLoggingEnabled(text) {
@@ -73,7 +75,11 @@ function parseVibeSessionLoggingEnabled(text) {
73
75
  const kv = line.match(/^enabled\s*=\s*(.+)$/);
74
76
  if (kv) {
75
77
  const value = kv[1].trim().toLowerCase();
76
- return value === "true";
78
+ if (value === "true")
79
+ return true;
80
+ if (value === "false")
81
+ return false;
82
+ return undefined;
77
83
  }
78
84
  }
79
85
  else {
@@ -81,11 +87,15 @@ function parseVibeSessionLoggingEnabled(text) {
81
87
  const dotted = line.match(/^session_logging\.enabled\s*=\s*(.+)$/);
82
88
  if (dotted) {
83
89
  const value = dotted[1].trim().toLowerCase();
84
- return value === "true";
90
+ if (value === "true")
91
+ return true;
92
+ if (value === "false")
93
+ return false;
94
+ return undefined;
85
95
  }
86
96
  }
87
97
  }
88
- return false;
98
+ return undefined;
89
99
  }
90
100
  /**
91
101
  * U27: Probe Gemini's project/user config locations.
package/dist/index.d.ts CHANGED
@@ -413,6 +413,8 @@ export declare function prepareMistralRequest(params: {
413
413
  maxTurns?: number;
414
414
  /** Phase 4 slice δ: Vibe `--max-price DOLLARS` cumulative-cost cap. */
415
415
  maxPrice?: number;
416
+ /** Vibe 2.x: `--max-tokens N` cumulative prompt + completion token cap. */
417
+ maxTokens?: number;
416
418
  /** Phase 4 slice ζ: Vibe `--workdir <DIR>` working-directory parity. */
417
419
  workingDir?: string;
418
420
  /** Phase 4 slice ζ: Vibe `--add-dir <DIR>` repeatable add-dir parity. */
@@ -429,7 +431,7 @@ export declare function prepareMistralRequest(params: {
429
431
  * through here, or a fresh-workspace / budgeted run can degrade on
430
432
  * the second attempt.
431
433
  */
432
- export declare function buildMistralRetryPrep(params: Pick<MistralRequestParams, "outputFormat" | "permissionMode" | "effort" | "reasoningEffort" | "allowedTools" | "disallowedTools" | "approvalStrategy" | "trust" | "maxTurns" | "maxPrice" | "workingDir" | "addDir"> & {
434
+ export declare function buildMistralRetryPrep(params: Pick<MistralRequestParams, "outputFormat" | "permissionMode" | "effort" | "reasoningEffort" | "allowedTools" | "disallowedTools" | "approvalStrategy" | "trust" | "maxTurns" | "maxPrice" | "maxTokens" | "workingDir" | "addDir"> & {
433
435
  effectivePrompt: string;
434
436
  }, recoveryModel: string): {
435
437
  args: string[];
@@ -557,6 +559,8 @@ export interface MistralRequestParams {
557
559
  maxTurns?: number;
558
560
  /** Phase 4 slice δ: Vibe `--max-price DOLLARS` cumulative-cost cap. */
559
561
  maxPrice?: number;
562
+ /** Vibe 2.x: `--max-tokens N` cumulative prompt + completion token cap. */
563
+ maxTokens?: number;
560
564
  /** Phase 4 slice ζ: Vibe `--workdir <DIR>` working-directory parity. */
561
565
  workingDir?: string;
562
566
  /** Phase 4 slice ζ: Vibe `--add-dir <DIR>` repeatable add-dir parity. */
package/dist/index.js CHANGED
@@ -165,7 +165,7 @@ Other: list_models, cli_versions, upstream_contracts, cli_upgrade, approval_list
165
165
 
166
166
  Key behaviors:
167
167
  - Sync auto-defers at ${SYNC_DEADLINE_MS}ms. Poll deferred jobs via llm_job_status/llm_job_result.
168
- - Sessions: Claude --continue, Gemini --resume, Grok --resume/--continue, Mistral --resume/--continue (requires session_logging.enabled=true in ~/.vibe/config.toml), Codex \`exec resume <ID>\` / \`exec resume --last\` (all real CLI continuity). For Codex, sessionId must be a real Codex UUID (from ~/.codex/sessions/); gateway-generated gw-* IDs are rejected.
168
+ - Sessions: Claude --continue, Gemini --resume, Grok --resume/--continue, Mistral --resume/--continue (current Vibe defaults session logging on; doctor flags explicit session_logging.enabled=false), Codex \`exec resume <ID>\` / \`exec resume --last\` (all real CLI continuity). For Codex, sessionId must be a real Codex UUID (from ~/.codex/sessions/); gateway-generated gw-* IDs are rejected.
169
169
  - Approval gates: opt-in via approvalStrategy:"mcp_managed".
170
170
  - Idle timeout kills stuck processes (default 10min, configurable via idleTimeoutMs).
171
171
 
@@ -1766,6 +1766,7 @@ export function prepareMistralRequest(params, runtime = resolveGatewayServerRunt
1766
1766
  trust: params.trust,
1767
1767
  maxTurns: params.maxTurns,
1768
1768
  maxPrice: params.maxPrice,
1769
+ maxTokens: params.maxTokens,
1769
1770
  workingDir: params.workingDir,
1770
1771
  addDir: params.addDir,
1771
1772
  });
@@ -1822,6 +1823,7 @@ export function buildMistralRetryPrep(params, recoveryModel) {
1822
1823
  trust: params.trust,
1823
1824
  maxTurns: params.maxTurns,
1824
1825
  maxPrice: params.maxPrice,
1826
+ maxTokens: params.maxTokens,
1825
1827
  workingDir: params.workingDir,
1826
1828
  addDir: params.addDir,
1827
1829
  });
@@ -2428,6 +2430,7 @@ export async function handleMistralRequest(deps, params) {
2428
2430
  trust: params.trust,
2429
2431
  maxTurns: params.maxTurns,
2430
2432
  maxPrice: params.maxPrice,
2433
+ maxTokens: params.maxTokens,
2431
2434
  workingDir: params.workingDir,
2432
2435
  addDir: params.addDir,
2433
2436
  }, runtime);
@@ -2578,6 +2581,7 @@ export async function handleMistralRequestAsync(deps, params) {
2578
2581
  trust: params.trust,
2579
2582
  maxTurns: params.maxTurns,
2580
2583
  maxPrice: params.maxPrice,
2584
+ maxTokens: params.maxTokens,
2581
2585
  workingDir: params.workingDir,
2582
2586
  addDir: params.addDir,
2583
2587
  }, runtime);
@@ -3761,13 +3765,13 @@ export function createGatewayServer(deps = {}) {
3761
3765
  .optional()
3762
3766
  .describe("Model alias (e.g. mistral-medium-3.5, latest). Resolved alias is injected via VIBE_ACTIVE_MODEL env var; Vibe has no --model flag."),
3763
3767
  outputFormat: z
3764
- .enum(["plain", "json", "stream-json"])
3768
+ .enum(["text", "plain", "json", "streaming", "stream-json"])
3765
3769
  .optional()
3766
- .describe("Output format (plain|json|stream-json). Vibe default is plain."),
3770
+ .describe("Output format for Vibe 2.x (text|json|streaming). Legacy aliases plain→text and stream-json→streaming are accepted."),
3767
3771
  sessionId: z
3768
3772
  .string()
3769
3773
  .optional()
3770
- .describe("Session ID (user-provided CLI handle for --resume). Requires [session_logging] enabled = true in ~/.vibe/config.toml."),
3774
+ .describe("Session ID (user-provided CLI handle for --resume). Current Vibe defaults session logging on; doctor flags explicit [session_logging] enabled = false."),
3771
3775
  resumeLatest: z
3772
3776
  .boolean()
3773
3777
  .default(false)
@@ -3822,6 +3826,7 @@ export function createGatewayServer(deps = {}) {
3822
3826
  .describe("Emit `--trust` so Vibe trusts the cwd for this invocation only (not persisted to trusted_folders.toml) and skips the interactive trust prompt (Phase 4 slice γ)."),
3823
3827
  maxTurns: MAX_TURNS_SCHEMA.optional().describe("Vibe `--max-turns N`: cap the agent-loop iteration count (programmatic mode only, Phase 4 slice δ). Bounded to safe integers ≤ 10000."),
3824
3828
  maxPrice: MAX_PRICE_SCHEMA.optional().describe("Vibe `--max-price DOLLARS`: interrupt the session when cumulative cost crosses this cap (programmatic mode only, Phase 4 slice δ). Bounded to finite values ≤ 10000 USD."),
3829
+ maxTokens: MAX_TURNS_SCHEMA.optional().describe("Vibe `--max-tokens N`: cap cumulative prompt + completion tokens for the session (programmatic mode only). Bounded to safe integers ≤ 10000."),
3825
3830
  // Phase 4 slice ζ — Vibe working-directory + additional-dirs parity.
3826
3831
  workingDir: z
3827
3832
  .string()
@@ -3833,7 +3838,7 @@ export function createGatewayServer(deps = {}) {
3833
3838
  .optional()
3834
3839
  .describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance (Vibe states this flag may be specified multiple times)."),
3835
3840
  worktree: WORKTREE_SCHEMA.optional(),
3836
- }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, workingDir, addDir, worktree, }) => {
3841
+ }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, maxTokens, workingDir, addDir, worktree, }) => {
3837
3842
  return handleMistralRequest({ sessionManager, logger, runtime }, {
3838
3843
  prompt,
3839
3844
  promptParts,
@@ -3858,6 +3863,7 @@ export function createGatewayServer(deps = {}) {
3858
3863
  trust,
3859
3864
  maxTurns,
3860
3865
  maxPrice,
3866
+ maxTokens,
3861
3867
  workingDir,
3862
3868
  addDir,
3863
3869
  worktree,
@@ -4482,13 +4488,13 @@ export function createGatewayServer(deps = {}) {
4482
4488
  .optional()
4483
4489
  .describe("Model alias (resolved into VIBE_ACTIVE_MODEL env var — Vibe has no --model flag)"),
4484
4490
  outputFormat: z
4485
- .enum(["plain", "json", "stream-json"])
4491
+ .enum(["text", "plain", "json", "streaming", "stream-json"])
4486
4492
  .optional()
4487
- .describe("Output format (plain|json|stream-json). Vibe default is plain."),
4493
+ .describe("Output format for Vibe 2.x (text|json|streaming). Legacy aliases plain→text and stream-json→streaming are accepted."),
4488
4494
  sessionId: z
4489
4495
  .string()
4490
4496
  .optional()
4491
- .describe("Session ID (user-provided CLI handle for --resume). Requires [session_logging] enabled = true in ~/.vibe/config.toml."),
4497
+ .describe("Session ID (user-provided CLI handle for --resume). Current Vibe defaults session logging on; doctor flags explicit [session_logging] enabled = false."),
4492
4498
  resumeLatest: z
4493
4499
  .boolean()
4494
4500
  .default(false)
@@ -4542,6 +4548,7 @@ export function createGatewayServer(deps = {}) {
4542
4548
  .describe("Emit `--trust` so Vibe trusts the cwd for this invocation only (not persisted to trusted_folders.toml) and skips the interactive trust prompt (Phase 4 slice γ)."),
4543
4549
  maxTurns: MAX_TURNS_SCHEMA.optional().describe("Vibe `--max-turns N`: cap the agent-loop iteration count (programmatic mode only, Phase 4 slice δ). Bounded to safe integers ≤ 10000."),
4544
4550
  maxPrice: MAX_PRICE_SCHEMA.optional().describe("Vibe `--max-price DOLLARS`: interrupt the session when cumulative cost crosses this cap (programmatic mode only, Phase 4 slice δ). Bounded to finite values ≤ 10000 USD."),
4551
+ maxTokens: MAX_TURNS_SCHEMA.optional().describe("Vibe `--max-tokens N`: cap cumulative prompt + completion tokens for the session (programmatic mode only). Bounded to safe integers ≤ 10000."),
4545
4552
  // Phase 4 slice ζ — Vibe working-directory + additional-dirs parity.
4546
4553
  workingDir: z
4547
4554
  .string()
@@ -4553,7 +4560,7 @@ export function createGatewayServer(deps = {}) {
4553
4560
  .optional()
4554
4561
  .describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance."),
4555
4562
  worktree: WORKTREE_SCHEMA.optional(),
4556
- }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, workingDir, addDir, worktree, }) => {
4563
+ }, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, maxTokens, workingDir, addDir, worktree, }) => {
4557
4564
  return handleMistralRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
4558
4565
  prompt,
4559
4566
  promptParts,
@@ -4577,6 +4584,7 @@ export function createGatewayServer(deps = {}) {
4577
4584
  trust,
4578
4585
  maxTurns,
4579
4586
  maxPrice,
4587
+ maxTokens,
4580
4588
  workingDir,
4581
4589
  addDir,
4582
4590
  worktree,
@@ -76,17 +76,22 @@ const GUIDANCE = {
76
76
  displayName: "Mistral Vibe CLI",
77
77
  install: {
78
78
  summary: "Install Mistral Vibe CLI via pip, uv, or Homebrew (Vibe does not self-update; cli_upgrade dispatches to the installer it detects).",
79
- commands: ["pip install vibe-cli", "uv tool install vibe-cli", "brew install mistral-vibe"],
80
- documentationUrl: "https://docs.mistral.ai/agents/vibe-cli",
79
+ commands: [
80
+ "curl -LsSf https://mistral.ai/vibe/install.sh | bash",
81
+ "pip install mistral-vibe",
82
+ "uv tool install mistral-vibe",
83
+ "brew install mistral-vibe",
84
+ ],
85
+ documentationUrl: "https://docs.mistral.ai/mistral-vibe/overview",
81
86
  },
82
87
  login: {
83
- summary: "Sign in through Mistral's official auth flow and enable session_logging in ~/.vibe/config.toml.",
84
- commands: ["vibe auth login", "vibe config set session_logging.enabled true"],
88
+ summary: "Sign in through Mistral's official auth flow. Current Vibe defaults session logging to enabled; if an older config disabled it, edit ~/.vibe/config.toml and set [session_logging] enabled = true.",
89
+ commands: ["vibe auth login"],
85
90
  credentialHandling: "Do not paste Mistral API keys, OAuth tokens, or ~/.vibe/credentials into the gateway or a remote chat.",
86
91
  },
87
92
  verification: {
88
93
  command: "vibe --version",
89
- expected: "Vibe CLI is installed; doctor additionally checks ~/.vibe/config.toml for session_logging.enabled=true",
94
+ expected: "Vibe CLI is installed; doctor checks ~/.vibe/config.toml for an explicit session_logging.enabled=false override",
90
95
  },
91
96
  },
92
97
  };
@@ -12,7 +12,7 @@ const VERSION_ARGS = {
12
12
  grok: ["--version"],
13
13
  mistral: ["--version"],
14
14
  };
15
- // Mistral Vibe ships as the `vibe` binary (PyPI package vibe-cli); the gateway
15
+ // Mistral Vibe ships as the `vibe` binary (PyPI package mistral-vibe); the gateway
16
16
  // uses `mistral` as the provider key but invokes `vibe` on the shell.
17
17
  export const PROVIDER_COMMANDS = {
18
18
  claude: "claude",
@@ -71,9 +71,9 @@ export declare function resolveGrokSessionArgs(opts: {
71
71
  /**
72
72
  * Mistral Vibe-specific resume args.
73
73
  *
74
- * Vibe persists sessions only when `[session_logging] enabled = true` is set in
75
- * `~/.vibe/config.toml`. The doctor checks for that toggle and surfaces an
76
- * actionable error when it is missing; this pure helper just emits the args.
74
+ * Current Vibe defaults session logging on; older configs can explicitly set
75
+ * `[session_logging] enabled = false`. The doctor checks that toggle before
76
+ * callers rely on session continuity; this pure helper just emits the args.
77
77
  *
78
78
  * The args shape mirrors Grok (`--continue` for latest, `--resume <id>` for a
79
79
  * specific session) because Vibe exposes the same surface for its session log.
@@ -125,6 +125,11 @@ export interface PrepareMistralRequestInput {
125
125
  * only).
126
126
  */
127
127
  maxPrice?: number;
128
+ /**
129
+ * Vibe 2.x supports `--max-tokens N` in programmatic mode, wired through to
130
+ * `run_programmatic(max_session_tokens=...)`.
131
+ */
132
+ maxTokens?: number;
128
133
  /**
129
134
  * Phase 4 slice ζ: emit `--workdir <DIR>` so Vibe changes into the named
130
135
  * directory before running. Single value (Vibe accepts one --workdir).
@@ -148,6 +153,8 @@ export interface PrepareMistralRequestResult {
148
153
  * - Model is selected via `VIBE_ACTIVE_MODEL` env var (NOT a `--model` flag).
149
154
  * - Permission mode emits `--agent <mode>` (defaults to `auto-approve` when unset).
150
155
  * - Allowed tools emit `--enabled-tools <tool>` once per tool (allowlist only).
156
+ * - Output format emits `--output <text|json|streaming>` (legacy gateway
157
+ * aliases `plain` and `stream-json` are normalized before spawn).
151
158
  * - Disallowed tools are accepted but ignored at the CLI boundary.
152
159
  */
153
160
  export declare function prepareMistralRequest(input: PrepareMistralRequestInput): PrepareMistralRequestResult;
@@ -100,9 +100,9 @@ export function resolveGrokSessionArgs(opts) {
100
100
  /**
101
101
  * Mistral Vibe-specific resume args.
102
102
  *
103
- * Vibe persists sessions only when `[session_logging] enabled = true` is set in
104
- * `~/.vibe/config.toml`. The doctor checks for that toggle and surfaces an
105
- * actionable error when it is missing; this pure helper just emits the args.
103
+ * Current Vibe defaults session logging on; older configs can explicitly set
104
+ * `[session_logging] enabled = false`. The doctor checks that toggle before
105
+ * callers rely on session continuity; this pure helper just emits the args.
106
106
  *
107
107
  * The args shape mirrors Grok (`--continue` for latest, `--resume <id>` for a
108
108
  * specific session) because Vibe exposes the same surface for its session log.
@@ -151,6 +151,8 @@ export const MISTRAL_DEFAULT_AGENT_MODE = "auto-approve";
151
151
  * - Model is selected via `VIBE_ACTIVE_MODEL` env var (NOT a `--model` flag).
152
152
  * - Permission mode emits `--agent <mode>` (defaults to `auto-approve` when unset).
153
153
  * - Allowed tools emit `--enabled-tools <tool>` once per tool (allowlist only).
154
+ * - Output format emits `--output <text|json|streaming>` (legacy gateway
155
+ * aliases `plain` and `stream-json` are normalized before spawn).
154
156
  * - Disallowed tools are accepted but ignored at the CLI boundary.
155
157
  */
156
158
  export function prepareMistralRequest(input) {
@@ -160,7 +162,7 @@ export function prepareMistralRequest(input) {
160
162
  env.VIBE_ACTIVE_MODEL = input.resolvedModel;
161
163
  }
162
164
  if (input.outputFormat) {
163
- args.push("--output-format", input.outputFormat);
165
+ args.push("--output", normalizeMistralOutputFormat(input.outputFormat));
164
166
  }
165
167
  const mode = input.permissionMode ?? MISTRAL_DEFAULT_AGENT_MODE;
166
168
  args.push("--agent", mode);
@@ -185,6 +187,9 @@ export function prepareMistralRequest(input) {
185
187
  if (input.maxPrice !== undefined) {
186
188
  args.push("--max-price", String(input.maxPrice));
187
189
  }
190
+ if (input.maxTokens !== undefined) {
191
+ args.push("--max-tokens", String(input.maxTokens));
192
+ }
188
193
  if (input.workingDir) {
189
194
  args.push("--workdir", input.workingDir);
190
195
  }
@@ -196,6 +201,13 @@ export function prepareMistralRequest(input) {
196
201
  const ignoredDisallowedTools = Boolean(input.disallowedTools && input.disallowedTools.length > 0);
197
202
  return { args, env, ignoredDisallowedTools };
198
203
  }
204
+ function normalizeMistralOutputFormat(format) {
205
+ if (format === "plain")
206
+ return "text";
207
+ if (format === "stream-json")
208
+ return "streaming";
209
+ return format;
210
+ }
199
211
  //──────────────────────────────────────────────────────────────────────────────
200
212
  // U24: Permission / approval mode parity helpers
201
213
  //──────────────────────────────────────────────────────────────────────────────
@@ -604,15 +604,16 @@ export const UPSTREAM_CLI_CONTRACTS = {
604
604
  // Phase 4 slice δ
605
605
  "maxTurns",
606
606
  "maxPrice",
607
+ "maxTokens",
607
608
  // Phase 4 slice ζ
608
609
  "workingDir",
609
610
  "addDir",
610
611
  ],
611
612
  flags: {
612
613
  "-p": { arity: "one", description: "Prompt text" },
613
- "--output-format": {
614
+ "--output": {
614
615
  arity: "one",
615
- values: ["plain", "json", "stream-json"],
616
+ values: ["text", "json", "streaming"],
616
617
  description: "Output format",
617
618
  },
618
619
  "--agent": {
@@ -641,6 +642,11 @@ export const UPSTREAM_CLI_CONTRACTS = {
641
642
  pattern: /^(0|[1-9][0-9]*)(\.[0-9]+)?$/,
642
643
  description: "Cumulative cost cap in USD (Phase 4 slice δ, programmatic mode only)",
643
644
  },
645
+ "--max-tokens": {
646
+ arity: "one",
647
+ pattern: /^[1-9][0-9]*$/,
648
+ description: "Cumulative prompt + completion token cap (Vibe 2.x programmatic mode)",
649
+ },
644
650
  "--workdir": {
645
651
  arity: "one",
646
652
  description: "Working directory for the invocation (Phase 4 slice ζ)",
@@ -686,6 +692,22 @@ export const UPSTREAM_CLI_CONTRACTS = {
686
692
  env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
687
693
  expect: "pass",
688
694
  },
695
+ {
696
+ id: "mistral-output-streaming-and-max-tokens",
697
+ description: "Vibe 2.x: --output streaming and --max-tokens are accepted",
698
+ args: [
699
+ "-p",
700
+ "hello",
701
+ "--agent",
702
+ "auto-approve",
703
+ "--output",
704
+ "streaming",
705
+ "--max-tokens",
706
+ "1000",
707
+ ],
708
+ env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
709
+ expect: "pass",
710
+ },
689
711
  {
690
712
  id: "mistral-max-price-scientific-notation",
691
713
  description: "Phase 4 slice δ: scientific-notation --max-price is rejected by contract pattern (matches MAX_PRICE_SCHEMA bounds)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "1.16.0",
3
+ "version": "1.16.2",
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",