llm-cli-gateway 1.16.2 → 1.17.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,36 @@ All notable changes to the llm-cli-gateway project.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [1.17.0] - 2026-05-30 — upstream provider tracking
8
+
9
+ Feature release adding repeatable upstream-provider contract tracking for the
10
+ gateway's supported CLIs.
11
+
12
+ ### Added
13
+
14
+ - Added provider-specific maintenance skills for Claude Code, Codex, Gemini,
15
+ Grok, and Mistral Vibe.
16
+ - Added upstream source metadata to the CLI contract table and mirrored it into
17
+ `docs/upstream/provider-sources.dag.toml`.
18
+ - Added `scripts/upstream-scan.mjs` plus `npm run upstream:contracts` and
19
+ `npm run upstream:scan` for offline contract checks and advisory live source
20
+ scans.
21
+ - Added upstream source tests covering contract/TOML synchronization.
22
+
23
+ ### Changed
24
+
25
+ - Pointed Claude Code tracking at the markdown changelog, Codex tracking at the
26
+ GitHub releases feed plus product changelog, Gemini tracking at the Gemini CLI
27
+ changelog plus GitHub releases, and Grok tracking at the markdown xAI release
28
+ notes.
29
+ - Ignored local-only agent/worktree artifacts that should not enter source
30
+ control.
31
+
32
+ ### Fixed
33
+
34
+ - Fixed the `maxTokens` request schema so token budgets no longer reuse the
35
+ `maxTurns` limit.
36
+
7
37
  ## [1.16.2] - 2026-05-29 — release formatting follow-up
8
38
 
9
39
  Patch release that keeps the Mistral Vibe CLI contract fixes from `1.16.1`
package/dist/index.d.ts CHANGED
@@ -66,6 +66,7 @@ type GatewayLogger = typeof logger;
66
66
  * upper bound — no plausible single agent loop exceeds 10k turns or 10k USD.
67
67
  */
68
68
  export declare const MAX_TURNS_SCHEMA: z.ZodNumber;
69
+ export declare const MAX_TOKENS_SCHEMA: z.ZodNumber;
69
70
  export declare const MAX_PRICE_SCHEMA: z.ZodNumber;
70
71
  /**
71
72
  * Slice λ: shared worktree directive for all 10 `*_request` / `*_request_async`
package/dist/index.js CHANGED
@@ -242,6 +242,10 @@ const MCP_SERVER_ENUM = z.enum(CLAUDE_MCP_SERVER_NAMES);
242
242
  * upper bound — no plausible single agent loop exceeds 10k turns or 10k USD.
243
243
  */
244
244
  export const MAX_TURNS_SCHEMA = z.number().int().positive().safe().max(10_000);
245
+ // Token budgets can legitimately exceed the agent-turn cap by orders of
246
+ // magnitude. Keep a finite operational guardrail while avoiding the 10k turn
247
+ // ceiling that would make large-context Vibe sessions unusable.
248
+ export const MAX_TOKENS_SCHEMA = z.number().int().positive().safe().max(100_000_000);
245
249
  // `.min(1e-6)` keeps the value in JS's decimal-stringify range:
246
250
  // String(1e-6) === "0.000001" but String(1e-7) === "1e-7", which both
247
251
  // upstream CLIs would reject. 1µUSD per request is fine-grained enough
@@ -3826,7 +3830,7 @@ export function createGatewayServer(deps = {}) {
3826
3830
  .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 γ)."),
3827
3831
  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."),
3828
3832
  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."),
3833
+ maxTokens: MAX_TOKENS_SCHEMA.optional().describe("Vibe `--max-tokens N`: cap cumulative prompt + completion tokens for the session (programmatic mode only). Bounded to safe integers ≤ 100000000."),
3830
3834
  // Phase 4 slice ζ — Vibe working-directory + additional-dirs parity.
3831
3835
  workingDir: z
3832
3836
  .string()
@@ -4548,7 +4552,7 @@ export function createGatewayServer(deps = {}) {
4548
4552
  .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 γ)."),
4549
4553
  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."),
4550
4554
  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."),
4555
+ maxTokens: MAX_TOKENS_SCHEMA.optional().describe("Vibe `--max-tokens N`: cap cumulative prompt + completion tokens for the session (programmatic mode only). Bounded to safe integers ≤ 100000000."),
4552
4556
  // Phase 4 slice ζ — Vibe working-directory + additional-dirs parity.
4553
4557
  workingDir: z
4554
4558
  .string()
@@ -13,6 +13,43 @@ export interface CliFlagContract {
13
13
  pattern?: RegExp;
14
14
  description: string;
15
15
  }
16
+ /**
17
+ * Pure upstream-tracking metadata for a provider CLI.
18
+ *
19
+ * IMPORTANT — non-duplication invariant: nothing here encodes mechanical
20
+ * behaviour. Flags, output modes, session/resume rules, permission modes,
21
+ * forbidden flags, env contracts, and positional limits live ONLY in the
22
+ * surrounding {@link CliContract} and are validated ONLY by
23
+ * {@link validateUpstreamCliArgs} / {@link validateUpstreamCliEnv}. The fields
24
+ * below are descriptive pointers used by the upstream changelog scanner
25
+ * (`scripts/upstream-scan.mjs`) and surfaced in the contract report — they
26
+ * never drive argv/env enforcement.
27
+ *
28
+ * `docs/upstream/provider-sources.dag.toml` mirrors `sourceUrls` and
29
+ * `watchCategories` for the scanner's offline scan plan; a unit test
30
+ * (`upstream-sources.test.ts`) asserts the TOML stays in sync with these
31
+ * fields so the two cannot drift. The TypeScript values here are authoritative;
32
+ * the TOML is scanner input only and is never consulted for contract
33
+ * enforcement.
34
+ */
35
+ export interface CliUpstreamMetadata {
36
+ /** Canonical changelog / release-notes URLs the scanner fetches with --live. */
37
+ sourceUrls: readonly string[];
38
+ /** Distribution package identifier (npm package name, PyPI project, …). */
39
+ packageName?: string;
40
+ /** Source repository URL, when distinct from the changelog source. */
41
+ repo?: string;
42
+ /** Human-facing install / getting-started docs. */
43
+ installDocsUrl?: string;
44
+ /** Distribution channel the gateway expects the CLI to ship through. */
45
+ releaseChannel?: "npm" | "pypi" | "github-release" | "vendor";
46
+ /**
47
+ * Contract surfaces worth watching in upstream release notes (e.g. "flags",
48
+ * "output-formats", "session-resume"). Descriptive labels for the scanner and
49
+ * report ONLY — never a validation input.
50
+ */
51
+ watchCategories: readonly string[];
52
+ }
16
53
  export interface CliContract {
17
54
  cli: CliType;
18
55
  executable: string;
@@ -31,6 +68,8 @@ export interface CliContract {
31
68
  resumeMaxPositionals?: number;
32
69
  resumeOnlyFlags?: readonly string[];
33
70
  resumeForbiddenFlags?: readonly string[];
71
+ /** Non-mechanical upstream-tracking metadata. See {@link CliUpstreamMetadata}. */
72
+ upstreamMetadata?: CliUpstreamMetadata;
34
73
  }
35
74
  export interface CliContractFixture {
36
75
  id: string;
@@ -14,6 +14,13 @@ export const UPSTREAM_CLI_CONTRACTS = {
14
14
  cli: "claude",
15
15
  executable: "claude",
16
16
  upstream: "Claude Code CLI",
17
+ upstreamMetadata: {
18
+ sourceUrls: ["https://code.claude.com/docs/en/changelog.md"],
19
+ packageName: "@anthropic-ai/claude-code",
20
+ installDocsUrl: "https://code.claude.com/docs/en/overview",
21
+ releaseChannel: "npm",
22
+ watchCategories: ["flags", "output-formats", "permission-modes", "session-resume", "models"],
23
+ },
17
24
  helpArgs: [["--help"]],
18
25
  maxPositionals: 0,
19
26
  mcpTools: ["claude_request", "claude_request_async"],
@@ -197,6 +204,23 @@ export const UPSTREAM_CLI_CONTRACTS = {
197
204
  cli: "codex",
198
205
  executable: "codex",
199
206
  upstream: "OpenAI Codex CLI",
207
+ upstreamMetadata: {
208
+ sourceUrls: [
209
+ "https://github.com/openai/codex/releases",
210
+ "https://developers.openai.com/codex/changelog",
211
+ ],
212
+ packageName: "@openai/codex",
213
+ repo: "https://github.com/openai/codex",
214
+ installDocsUrl: "https://developers.openai.com/codex/cli",
215
+ releaseChannel: "npm",
216
+ watchCategories: [
217
+ "flags",
218
+ "sandbox-modes",
219
+ "approval-modes",
220
+ "session-resume",
221
+ "output-schema",
222
+ ],
223
+ },
200
224
  helpArgs: [
201
225
  ["exec", "--help"],
202
226
  ["exec", "resume", "--help"],
@@ -343,6 +367,17 @@ export const UPSTREAM_CLI_CONTRACTS = {
343
367
  cli: "gemini",
344
368
  executable: "gemini",
345
369
  upstream: "Google Gemini CLI",
370
+ upstreamMetadata: {
371
+ sourceUrls: [
372
+ "https://geminicli.com/docs/changelogs/",
373
+ "https://github.com/google-gemini/gemini-cli/releases",
374
+ ],
375
+ packageName: "@google/gemini-cli",
376
+ repo: "https://github.com/google-gemini/gemini-cli",
377
+ installDocsUrl: "https://geminicli.com/docs/",
378
+ releaseChannel: "npm",
379
+ watchCategories: ["flags", "approval-modes", "output-formats", "session-resume"],
380
+ },
346
381
  helpArgs: [["--help"]],
347
382
  maxPositionals: 0,
348
383
  mcpTools: ["gemini_request", "gemini_request_async"],
@@ -428,6 +463,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
428
463
  cli: "grok",
429
464
  executable: "grok",
430
465
  upstream: "xAI Grok CLI",
466
+ upstreamMetadata: {
467
+ sourceUrls: ["https://docs.x.ai/developers/release-notes.md"],
468
+ installDocsUrl: "https://docs.x.ai/build/overview",
469
+ releaseChannel: "vendor",
470
+ watchCategories: ["flags", "permission-modes", "session-resume", "sandbox", "output-formats"],
471
+ },
431
472
  helpArgs: [["--help"]],
432
473
  maxPositionals: 0,
433
474
  mcpTools: ["grok_request", "grok_request_async"],
@@ -582,6 +623,14 @@ export const UPSTREAM_CLI_CONTRACTS = {
582
623
  cli: "mistral",
583
624
  executable: "vibe",
584
625
  upstream: "Mistral Vibe CLI",
626
+ upstreamMetadata: {
627
+ sourceUrls: ["https://github.com/mistralai/mistral-vibe/releases"],
628
+ packageName: "mistral-vibe",
629
+ repo: "https://github.com/mistralai/mistral-vibe",
630
+ installDocsUrl: "https://github.com/mistralai/mistral-vibe#installation",
631
+ releaseChannel: "pypi",
632
+ watchCategories: ["flags", "agent-modes", "session-logging", "output-formats", "env-model"],
633
+ },
585
634
  helpArgs: [["--help"]],
586
635
  maxPositionals: 0,
587
636
  mcpTools: ["mistral_request", "mistral_request_async"],
@@ -960,6 +1009,19 @@ export function buildUpstreamContractReport(options = {}) {
960
1009
  {
961
1010
  executable: contract.executable,
962
1011
  upstream: contract.upstream,
1012
+ // Pure metadata pointers (changelog URLs, package name, watch
1013
+ // categories). Enriched from the CliContract — the single source of
1014
+ // truth — so report consumers and the scanner read the same values.
1015
+ upstreamMetadata: contract.upstreamMetadata
1016
+ ? {
1017
+ sourceUrls: contract.upstreamMetadata.sourceUrls,
1018
+ packageName: contract.upstreamMetadata.packageName ?? null,
1019
+ repo: contract.upstreamMetadata.repo ?? null,
1020
+ installDocsUrl: contract.upstreamMetadata.installDocsUrl ?? null,
1021
+ releaseChannel: contract.upstreamMetadata.releaseChannel ?? null,
1022
+ watchCategories: contract.upstreamMetadata.watchCategories,
1023
+ }
1024
+ : null,
963
1025
  command: contract.command ?? null,
964
1026
  helpArgs: contract.helpArgs,
965
1027
  mcpTools: contract.mcpTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "1.16.2",
3
+ "version": "1.17.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",
@@ -71,6 +71,8 @@
71
71
  "test:pg": "bash ./scripts/test-pg.sh",
72
72
  "test:all": "npm run test && npm run test:pg",
73
73
  "smoke:cache-control": "node docs/plans/slice-kappa-smoke-test.mjs",
74
+ "upstream:contracts": "node scripts/upstream-scan.mjs --contracts-check",
75
+ "upstream:scan": "node scripts/upstream-scan.mjs",
74
76
  "lint": "eslint src/**/*.ts",
75
77
  "lint:fix": "eslint src/**/*.ts --fix",
76
78
  "format": "prettier --write 'src/**/*.ts'",
package/socket.yml CHANGED
@@ -22,11 +22,36 @@ version: 2
22
22
  # (SQLite, sessions.json) or explicit local CLI process I/O.
23
23
  #
24
24
  # shellAccess
25
- # src/executor.ts uses child_process.spawn(cmd, args, { ... }) with a
26
- # fixed allow-list of CLI binaries (claude / codex / gemini / grok /
27
- # vibe). shell:true is never set; arguments are passed as an array, so
28
- # there is no shell interpolation path for user input. Spawning these
29
- # CLIs is the entire purpose of the package.
25
+ # This alert fires on every module that imports node:child_process, and
26
+ # because spawning provider CLIs and git is the entire purpose of the
27
+ # package it surfaces on every release BY DESIGN we keep it visible
28
+ # rather than silencing it. It is a capability description, not a finding.
29
+ #
30
+ # INVARIANT enforced across ALL sites below: arguments are always passed
31
+ # as an array and `shell: true` is NEVER set, so there is no shell
32
+ # interpolation path for user input. `scripts/release-security-audit.sh`
33
+ # guards the dynamic-execution surface. When you add a new module that
34
+ # imports child_process, add it to this list so the alert location set
35
+ # stays documented instead of reading like a regression.
36
+ #
37
+ # Current production child_process sites (dist/*.js mirrors of src/*.ts):
38
+ # - src/executor.ts spawn(cmd, args) — provider CLI allow-list
39
+ # (claude / codex / gemini / grok / vibe).
40
+ # - src/worktree-manager.ts spawn("git", args) — gateway-owned git
41
+ # worktree lifecycle (add / list / prune /
42
+ # remove / branch -D).
43
+ # - src/cli-updater.ts spawnSync(cmd, args) — provider CLI version
44
+ # + upgrade checks.
45
+ # - src/provider-status.ts spawnSync(cmd, args) — provider login-status
46
+ # probe.
47
+ # - src/upstream-contracts.ts spawnSync(cmd, ["--help"]) — optional
48
+ # installed-CLI contract probe.
49
+ # - src/endpoint-exposure.ts spawnSync(process.execPath, ["-e", …]) —
50
+ # out-of-process HEAD reachability probe
51
+ # (opt-in via the start:http path only).
52
+ # - src/async-job-manager.ts imports the ChildProcess *type* only for
53
+ # job lifecycle tracking; it spawns via
54
+ # executor.ts (spawnCliProcess), not directly.
30
55
  #
31
56
  # usesEval
32
57
  # Not in our source. Transitive via @modelcontextprotocol/sdk → ajv@8,
@@ -40,16 +65,6 @@ version: 2
40
65
  # gateway does not call db.pragma() from production code; SQLite setup
41
66
  # uses fixed literal db.exec("PRAGMA ...") statements, and the release
42
67
  # security audit fails future production `.pragma()` calls.
43
- #
44
- # ioredis obfuscated code / base64 strings
45
- # Socket may flag ioredis@5.10.1 built/constants/TLSProfiles.js because it
46
- # contains base64-looking strings. This is a reviewed false positive: the
47
- # strings are PEM-encoded Redis Cloud TLS CA certificates. The file exports
48
- # static TLS profile data only; it contains no decoder loop, dynamic eval,
49
- # network call, or hidden execution path. The same file is byte-for-byte
50
- # identical in ioredis@5.9.2. ioredis is not installed by the default
51
- # production dependency tree; it is an optional peer for PostgreSQL/Redis
52
- # session storage and a pinned dev dependency for tests.
53
68
 
54
69
  issueRules:
55
70
  # Defaults from Socket. Listed explicitly so future contributors see what