llm-cli-gateway 2.17.0 → 2.17.1

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,15 @@ All notable changes to the llm-cli-gateway project.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.17.1] - 2026-07-13
8
+
9
+ ### Fixed
10
+
11
+ - **Upstream CLI contract refresh.** Updated installed-provider contracts for
12
+ Claude, Codex, Antigravity, Grok, Mistral Vibe, Devin, and Cursor Agent.
13
+ Vibe now emits its supported `--disabled-tools` denylist control instead of
14
+ silently ignoring `disallowedTools`.
15
+
7
16
  ## [2.17.0] - 2026-07-13
8
17
 
9
18
  ### Added
package/README.md CHANGED
@@ -97,7 +97,7 @@ The next documentation focus is provider-specific skill and DAG-TOML pairs for e
97
97
  - CI runs build, lint, format, tests, package checks, and npm audit.
98
98
  - Security CI runs actionlint, zizmor, shellcheck, typos, osv-scanner, gitleaks, and lychee.
99
99
  - GitHub release installer artifacts are checksummed and signed with Sigstore keyless signing.
100
- - npm releases use a generated prod-only shrinkwrap and release security audit; the publish token is fetched at runtime from Azure Key Vault through GitHub OIDC to Entra.
100
+ - npm releases use a generated prod-only shrinkwrap and release security audit; GitHub Actions Trusted Publishing exchanges the job's OIDC identity for short-lived npm publish credentials.
101
101
  - The npm package intentionally ships a generated, prod-only `npm-shrinkwrap.json` so registry installs resolve the audited release tree. Release gates regenerate it from `package-lock.json`, compare for parity, and run a registry-fidelity consumer install before publishing.
102
102
  - Socket behavioural alerts are documented in [`socket.yml`](./socket.yml) and under "Security Considerations" below. `shellAccess` and `shrinkwrap` are reviewed package capabilities/configuration for this CLI appliance, not hidden install behaviour.
103
103
 
@@ -370,12 +370,12 @@ Vibe-specific notes:
370
370
  `default | plan | accept-edits | auto-approve`; Vibe also accepts install-gated
371
371
  builtins (e.g. `lean`) and custom agents from `~/.vibe/agents`, so any name is
372
372
  passed through and Vibe validates availability. The gateway's
373
- programmatic-mode default is `auto-approve`; pick a stricter mode
374
- explicitly if you need approval gates.
375
- - **`allowedTools` is allow-list only** the gateway emits one
376
- `--enabled-tools <tool>` flag per entry. `disallowedTools` is accepted in
377
- the schema for caller-side parity but is silently ignored at the CLI
378
- boundary (a `logger.info` warning records the no-op).
373
+ programmatic-mode default is `accept-edits`; use `auto-approve` only as an
374
+ explicit opt-in.
375
+ - **Tool controls use Vibe's native flags.** The gateway emits one
376
+ `--enabled-tools <tool>` flag per `allowedTools` entry and one
377
+ `--disabled-tools <tool>` flag per `disallowedTools` entry. Vibe applies
378
+ disabled tools after enabled-tool filtering.
379
379
  - **No self-update**: `cli_upgrade --cli mistral` detects whether you used
380
380
  pip / uv / brew and dispatches the matching upgrade command. Running
381
381
  `vibe update` is not a thing.
@@ -992,9 +992,9 @@ Run a Mistral Vibe agentic coding request. Like `grok_request` in shape, but wit
992
992
 
993
993
  - `model` (string, optional): Vibe model alias (for example `mistral-medium-3.5` or `latest`). The resolved value is injected via the `VIBE_ACTIVE_MODEL` environment variable; omit it to let the gateway discover Vibe config and avoid stale hardcoded defaults.
994
994
  - `transport` (string, optional): `"cli"` (default) runs the Vibe CLI; `"acp"` routes through Vibe's native `vibe-acp` transport when `[acp].enabled` and the provider's `runtime_enabled` are set (fails closed otherwise). Sync-only: `mistral_request_async` always runs the CLI transport and does not accept `transport`
995
- - `permissionMode`: the Vibe `--agent` name builtins `default | plan | accept-edits | auto-approve`, or any install-gated/custom agent. Emitted as `--agent <name>`. Defaults to `auto-approve` in programmatic mode.
996
- - `allowedTools` (string[], optional): One `--enabled-tools <tool>` flag per entry (allow-list only).
997
- - `disallowedTools` (string[], optional): Accepted for parity with the other providers; ignored at the CLI boundary with a logged warning.
995
+ - `permissionMode`: the Vibe `--agent` name: builtins `default | plan | accept-edits | auto-approve`, or any install-gated/custom agent. Emitted as `--agent <name>`. Defaults to `accept-edits` in programmatic mode; use `auto-approve` only as an explicit opt-in.
996
+ - `allowedTools` (string[], optional): One `--enabled-tools <tool>` flag per entry.
997
+ - `disallowedTools` (string[], optional): One `--disabled-tools <tool>` flag per entry, applied after the enabled-tool filter.
998
998
  - `outputFormat` (string, optional): Vibe 2.x values are `"text"`, `"json"`, or `"streaming"`; legacy aliases `"plain"` and `"stream-json"` are accepted and normalized before spawn.
999
999
  - `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.
1000
1000
  - `trust` (boolean, optional): Emit `--trust` so Vibe trusts the cwd for this invocation only (not persisted; skips the interactive trust prompt)
@@ -1023,7 +1023,7 @@ Run a Cognition Devin CLI request synchronously (headless print mode, `devin -p`
1023
1023
  - `model` (string, optional): Model name or alias (e.g. `opus`, `latest`)
1024
1024
  - `transport` (string, optional): `"cli"` (default) runs the Devin CLI; `"acp"` routes through Devin's native `devin acp` transport when `[acp].enabled` and the provider's `runtime_enabled` are set (fails closed otherwise). Sync-only: `devin_request_async` always runs the CLI transport and accepts neither `transport` nor `agentType`
1025
1025
  - `agentType` (string, optional): ACP agent variant for `transport: "acp"` (`devin acp --agent-type`): `"summarizer"` (no tools, text summary) or `"review"` (read-only plus shell code-review); ignored for the CLI transport
1026
- - `permissionMode` (string, optional): Devin CLI permission mode (`--permission-mode`): `auto` (auto-approves read-only tools), `smart` (also auto-runs actions a fast model judges safe), `dangerous` (auto-approves all). Omit to use Devin's headless default
1026
+ - `permissionMode` (string, optional): Devin CLI permission mode (`--permission-mode`): `auto` (auto-approves read-only tools), `accept-edits` (also auto-approves workspace edits), `smart` (also auto-runs actions a fast model judges safe), `dangerous` (auto-approves all). Omit to use Devin's headless default
1027
1027
  - `promptFile` (string, optional): Load the initial prompt from a file (`--prompt-file`)
1028
1028
  - `sessionId` (string, optional): Devin session ID to resume (`--resume <id>`). The `gw-*` id minted for a brand-new session is not resumable via `sessionId`; continue with `resumeLatest: true`
1029
1029
  - `resumeLatest` (boolean, optional): Resume the most recent Devin session in cwd (`--continue`)
@@ -1653,7 +1653,7 @@ The gateway supports concurrent requests across different CLIs. Each request spa
1653
1653
  - **Command Execution**: Uses `spawn` with separate arguments (not shell execution)
1654
1654
  - **No Eval**: No dynamic code evaluation in our source (see "Socket alerts" below for the transitive `ajv` codegen case)
1655
1655
  - **Sandboxing**: Consider running in containers for production use
1656
- - **npm publish control**: npm releases are gated by the generated prod-only shrinkwrap, release security audit, packed-consumer checks, and a publish token fetched at runtime from Azure Key Vault through GitHub OIDC to Entra
1656
+ - **npm publish control**: npm releases are gated by the generated prod-only shrinkwrap, release security audit, packed-consumer checks, and GitHub Actions Trusted Publishing with short-lived OIDC-derived npm publish credentials
1657
1657
  - **Release signing**: GitHub release installer artifacts are signed with Sigstore keyless signing; verify `SHA256SUMS.sigstore.json` before trusting the checksum file
1658
1658
 
1659
1659
  ### Socket alerts — context for reviewers
package/dist/index.d.ts CHANGED
@@ -386,7 +386,6 @@ export declare function buildMistralRetryPrep(params: Pick<MistralRequestParams,
386
386
  }, recoveryModel: string): {
387
387
  args: string[];
388
388
  env: Record<string, string>;
389
- ignoredDisallowedTools: boolean;
390
389
  };
391
390
  export declare function buildCliResponse(cli: CliType, stdout: string, optimizeResponse: boolean, corrId: string, sessionId: string | undefined, prep: CliRequestPrep, durationMs: number, resumable?: boolean, outputFormat?: string, warnings?: WarningEntry[], compressResponse?: boolean): ExtendedToolResponse;
392
391
  export interface GrokApiRequestParams {
@@ -542,7 +541,7 @@ export declare function handleGrokRequestAsync(deps: AsyncHandlerDeps, params: O
542
541
  export interface DevinRequestParams {
543
542
  prompt?: string;
544
543
  model?: string;
545
- permissionMode?: "auto" | "smart" | "dangerous";
544
+ permissionMode?: "auto" | "accept-edits" | "smart" | "dangerous";
546
545
  promptFile?: string;
547
546
  config?: string;
548
547
  sandbox?: boolean;
package/dist/index.js CHANGED
@@ -2348,9 +2348,6 @@ export function prepareMistralRequest(params, runtime = resolveGatewayServerRunt
2348
2348
  workingDir: params.workingDir,
2349
2349
  addDir: params.addDir,
2350
2350
  });
2351
- if (prep.ignoredDisallowedTools) {
2352
- runtime.logger.info(`[${corrId}] Mistral does not support disallowedTools; ignoring (caller passed ${params.disallowedTools?.length ?? 0} entries)`);
2353
- }
2354
2351
  return {
2355
2352
  corrId,
2356
2353
  effectivePrompt,
@@ -5446,7 +5443,7 @@ export function createGatewayServer(deps = {}) {
5446
5443
  permissionMode: z
5447
5444
  .enum(CLAUDE_PERMISSION_MODES)
5448
5445
  .optional()
5449
- .describe("Claude --permission-mode: default|acceptEdits|plan|auto|dontAsk|bypassPermissions. `default` is a no-op (no flag emitted)."),
5446
+ .describe("Claude --permission-mode: default|acceptEdits|auto|bypassPermissions|manual|dontAsk|plan. `default` is a no-op (no flag emitted)."),
5450
5447
  agent: z
5451
5448
  .string()
5452
5449
  .optional()
@@ -6522,9 +6519,9 @@ export function createGatewayServer(deps = {}) {
6522
6519
  .default("cli")
6523
6520
  .describe("Transport: 'cli' (default) runs the Devin CLI; 'acp' routes through `devin acp` when [acp].enabled and the provider's runtime_enabled are set (fails closed otherwise)."),
6524
6521
  permissionMode: z
6525
- .enum(["auto", "smart", "dangerous"])
6522
+ .enum(["auto", "accept-edits", "smart", "dangerous"])
6526
6523
  .optional()
6527
- .describe("Devin CLI permission mode (--permission-mode). auto auto-approves read-only tools; smart additionally auto-runs actions a fast model judges safe; dangerous auto-approves all. When omitted, Devin uses its own headless default; pass auto or smart for unattended runs."),
6524
+ .describe("Devin CLI permission mode (--permission-mode). auto auto-approves read-only tools; accept-edits also auto-approves workspace edits; smart additionally auto-runs actions a fast model judges safe; dangerous auto-approves all. When omitted, Devin uses its own headless default; choose an explicit mode for unattended runs."),
6528
6525
  promptFile: z
6529
6526
  .string()
6530
6527
  .optional()
@@ -6764,7 +6761,7 @@ export function createGatewayServer(deps = {}) {
6764
6761
  disallowedTools: z
6765
6762
  .array(z.string())
6766
6763
  .optional()
6767
- .describe("Accepted for caller parity; Vibe has no deny-list flag, so values are ignored (a warning is logged)."),
6764
+ .describe("Denylist of built-in tools, each emitted as a separate --disabled-tools <tool> flag"),
6768
6765
  correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
6769
6766
  optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
6770
6767
  optimizeResponse: z.boolean().default(false).describe("Optimize response output"),
@@ -6878,7 +6875,7 @@ export function createGatewayServer(deps = {}) {
6878
6875
  permissionMode: z
6879
6876
  .enum(CLAUDE_PERMISSION_MODES)
6880
6877
  .optional()
6881
- .describe("Claude --permission-mode: default|acceptEdits|plan|auto|dontAsk|bypassPermissions. `default` is a no-op."),
6878
+ .describe("Claude --permission-mode: default|acceptEdits|auto|bypassPermissions|manual|dontAsk|plan. `default` is a no-op."),
6882
6879
  agent: z
6883
6880
  .string()
6884
6881
  .optional()
@@ -7688,9 +7685,9 @@ export function createGatewayServer(deps = {}) {
7688
7685
  .describe("Prompt text for Devin CLI. Required in practice; promptFile is additive (loads an initial prompt from a file)."),
7689
7686
  model: z.string().optional().describe("Model name or alias (e.g. opus, latest)"),
7690
7687
  permissionMode: z
7691
- .enum(["auto", "smart", "dangerous"])
7688
+ .enum(["auto", "accept-edits", "smart", "dangerous"])
7692
7689
  .optional()
7693
- .describe("Devin CLI permission mode (--permission-mode). auto, smart, or dangerous. When omitted, Devin uses its own headless default; pass auto or smart for unattended runs."),
7690
+ .describe("Devin CLI permission mode (--permission-mode). auto, accept-edits, smart, or dangerous. When omitted, Devin uses its own headless default; choose an explicit mode for unattended runs."),
7694
7691
  promptFile: z
7695
7692
  .string()
7696
7693
  .optional()
@@ -7913,7 +7910,7 @@ export function createGatewayServer(deps = {}) {
7913
7910
  disallowedTools: z
7914
7911
  .array(z.string())
7915
7912
  .optional()
7916
- .describe("Accepted for caller parity; Vibe has no deny-list flag, so values are ignored (a warning is logged)."),
7913
+ .describe("Denylist of built-in tools, each emitted as a separate --disabled-tools <tool> flag"),
7917
7914
  correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
7918
7915
  optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
7919
7916
  compressResponse: z
@@ -122,6 +122,13 @@ export function familyBaseRisk(provider, fam) {
122
122
  return contract.risk;
123
123
  return fam.safety === "read-only" ? "read_only" : "writes_local_config";
124
124
  }
125
+ function contractAdminProjectionCeiling(provider, commandPath) {
126
+ const exactContract = getCliSubcommandContract(provider, commandPath);
127
+ return exactContract?.adminProjection === "not_exposed" ? "not_exposed" : null;
128
+ }
129
+ function projectedAdminExposure(provider, commandPath, risk) {
130
+ return contractAdminProjectionCeiling(provider, commandPath) ?? adminRiskToExposure(risk);
131
+ }
125
132
  function rootAdvertises(discovered, family) {
126
133
  const wanted = family.toLowerCase();
127
134
  return discovered.rootHelp.subcommands.some(s => s.name.toLowerCase() === wanted);
@@ -142,6 +149,7 @@ export function projectProviderAdminOperations(def, discovered) {
142
149
  if (adminSurfaceKind(fam) !== "cli-subcommand")
143
150
  continue;
144
151
  const baseRisk = familyBaseRisk(def.id, fam);
152
+ const familyExposure = projectedAdminExposure(def.id, [fam.family], baseRisk);
145
153
  if (!discovered) {
146
154
  ops.push({
147
155
  provider: def.id,
@@ -149,8 +157,8 @@ export function projectProviderAdminOperations(def, discovered) {
149
157
  operationId: fam.family,
150
158
  argv: [fam.family],
151
159
  risk: baseRisk,
152
- exposure: adminRiskToExposure(baseRisk),
153
- mutating: adminRiskToExposure(baseRisk) === "mcp_requires_approval",
160
+ exposure: familyExposure,
161
+ mutating: familyExposure === "mcp_requires_approval",
154
162
  available: false,
155
163
  discoverySource: "no-discovery",
156
164
  summary: fam.evidence,
@@ -165,7 +173,7 @@ export function projectProviderAdminOperations(def, discovered) {
165
173
  if (!isSafeAdminToken(sub.name))
166
174
  continue;
167
175
  const risk = classifyOperationRisk(sub.name, baseRisk, { isSubcommand: true });
168
- const exposure = adminRiskToExposure(risk);
176
+ const exposure = projectedAdminExposure(def.id, [fam.family, sub.name], risk);
169
177
  ops.push({
170
178
  provider: def.id,
171
179
  family: fam.family,
@@ -183,7 +191,7 @@ export function projectProviderAdminOperations(def, discovered) {
183
191
  }
184
192
  const baseExposure = adminRiskToExposure(baseRisk);
185
193
  const risk = baseExposure === "not_exposed" ? baseRisk : classifyOperationRisk(fam.family, baseRisk);
186
- const exposure = adminRiskToExposure(risk);
194
+ const exposure = projectedAdminExposure(def.id, [fam.family], risk);
187
195
  ops.push({
188
196
  provider: def.id,
189
197
  family: fam.family,
@@ -5,13 +5,13 @@ export function adminSurfaceKind(family) {
5
5
  }
6
6
  export const DEVIN_ACP_AGENT_TYPES = ["summarizer", "review"];
7
7
  export const PROVIDER_TARGET_VERSIONS = {
8
- claude: "claude 2.1.206",
9
- codex: "codex-cli 0.144.1",
10
- gemini: "agy 1.1.0",
11
- grok: "grok 0.2.93 (f00f96316d)",
8
+ claude: "claude 2.1.207",
9
+ codex: "codex-cli 0.144.3",
10
+ gemini: "agy 1.1.1",
11
+ grok: "grok 0.2.99 (b1b49ccb71)",
12
12
  mistral: "vibe 2.19.1",
13
13
  devin: "devin 3000.1.27 (0d4bf12e)",
14
- cursor: "cursor-agent 2026.07.09-c59fd9a",
14
+ cursor: "cursor-agent 2026.07.09-a3815c0",
15
15
  };
16
16
  const PROVIDER_DEFINITIONS = {
17
17
  claude: {
@@ -615,7 +615,7 @@ const TOOL_CONTROLS = {
615
615
  mistral: {
616
616
  providerKind: "cli",
617
617
  gatewayRequestTools: ["mistral_request", "mistral_request_async"],
618
- summary: "Mistral Vibe owns its runtime tool catalog; the gateway can pass Vibe enabled-tool controls and reports local skills if present.",
618
+ summary: "Mistral Vibe owns its runtime tool catalog; the gateway can pass Vibe enabled- and disabled-tool controls and reports local skills if present.",
619
619
  controls: {
620
620
  allowlist: {
621
621
  supported: true,
@@ -624,9 +624,10 @@ const TOOL_CONTROLS = {
624
624
  behavior: "Each entry is emitted as a separate Vibe enabled-tool flag.",
625
625
  },
626
626
  denylist: {
627
- supported: false,
627
+ supported: true,
628
628
  requestField: "disallowedTools",
629
- behavior: "Accepted for caller parity but ignored because Vibe has no deny-list flag.",
629
+ cliFlag: "--disabled-tools",
630
+ behavior: "Each entry is emitted as a separate Vibe disabled-tool flag.",
630
631
  },
631
632
  mcpServers: {
632
633
  supported: false,
@@ -675,15 +676,11 @@ const TOOL_CONTROLS = {
675
676
  approvalAndSandboxControls: true,
676
677
  costAndLoopControls: true,
677
678
  workspaceAndWorktreeControls: true,
679
+ toolAllowDenyControls: true,
678
680
  enabledToolAllowlist: true,
679
681
  trustControl: true,
680
682
  }),
681
683
  unsupportedInputs: [
682
- {
683
- input: "disallowedTools",
684
- behavior: "ignored",
685
- details: "Accepted for caller parity but ignored because Vibe has no deny-list flag.",
686
- },
687
684
  {
688
685
  input: "mcpServers",
689
686
  behavior: "approval_tracking_only",
@@ -798,7 +795,7 @@ const TOOL_CONTROLS = {
798
795
  supported: true,
799
796
  requestField: "permissionMode",
800
797
  cliFlag: "--permission-mode",
801
- behavior: "Maps to Devin CLI --permission-mode: auto auto-approves read-only tools; smart additionally auto-runs actions a fast model judges safe; dangerous auto-approves all.",
798
+ behavior: "Maps to Devin CLI --permission-mode: auto auto-approves read-only tools; accept-edits also auto-approves workspace edits; smart additionally auto-runs actions a fast model judges safe; dangerous auto-approves all.",
802
799
  },
803
800
  promptControl: {
804
801
  supported: true,
@@ -52,10 +52,9 @@ export interface PrepareMistralRequestInput {
52
52
  export interface PrepareMistralRequestResult {
53
53
  args: string[];
54
54
  env: Record<string, string>;
55
- ignoredDisallowedTools: boolean;
56
55
  }
57
56
  export declare function prepareMistralRequest(input: PrepareMistralRequestInput): PrepareMistralRequestResult;
58
- export declare const CLAUDE_PERMISSION_MODES: readonly ["default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions"];
57
+ export declare const CLAUDE_PERMISSION_MODES: readonly ["default", "acceptEdits", "auto", "bypassPermissions", "manual", "dontAsk", "plan"];
59
58
  export type ClaudePermissionMode = (typeof CLAUDE_PERMISSION_MODES)[number];
60
59
  export interface ClaudePermissionFlagsInput {
61
60
  permissionMode?: ClaudePermissionMode;
@@ -3,6 +3,7 @@ import { tmpdir } from "os";
3
3
  import { join, isAbsolute } from "path";
4
4
  import { randomUUID } from "crypto";
5
5
  import { z } from "zod/v3";
6
+ import { CLAUDE_WIRE_PERMISSION_MODES } from "./upstream-contracts.js";
6
7
  export const GATEWAY_SESSION_PREFIX = "gw-";
7
8
  export function validateSessionId(sessionId) {
8
9
  if (sessionId.startsWith(GATEWAY_SESSION_PREFIX)) {
@@ -117,6 +118,12 @@ export function prepareMistralRequest(input) {
117
118
  args.push("--enabled-tools", tool);
118
119
  }
119
120
  }
121
+ if (input.disallowedTools && input.disallowedTools.length > 0) {
122
+ sanitizeCliArgValues(input.disallowedTools, "disallowedTools");
123
+ for (const tool of input.disallowedTools) {
124
+ args.push("--disabled-tools", tool);
125
+ }
126
+ }
120
127
  if (input.trust) {
121
128
  args.push("--trust");
122
129
  }
@@ -137,8 +144,7 @@ export function prepareMistralRequest(input) {
137
144
  args.push("--add-dir", dir);
138
145
  }
139
146
  }
140
- const ignoredDisallowedTools = Boolean(input.disallowedTools && input.disallowedTools.length > 0);
141
- return { args, env, ignoredDisallowedTools };
147
+ return { args, env };
142
148
  }
143
149
  function normalizeMistralOutputFormat(format) {
144
150
  if (format === "plain")
@@ -147,14 +153,7 @@ function normalizeMistralOutputFormat(format) {
147
153
  return "streaming";
148
154
  return format;
149
155
  }
150
- export const CLAUDE_PERMISSION_MODES = [
151
- "default",
152
- "acceptEdits",
153
- "plan",
154
- "auto",
155
- "dontAsk",
156
- "bypassPermissions",
157
- ];
156
+ export const CLAUDE_PERMISSION_MODES = ["default", ...CLAUDE_WIRE_PERMISSION_MODES];
158
157
  export function resolveClaudePermissionFlags(input) {
159
158
  const { permissionMode, dangerouslySkipPermissions } = input;
160
159
  let warning;
@@ -1,5 +1,6 @@
1
1
  import { type CliType } from "./provider-definitions.js";
2
2
  export type CliFlagArity = "none" | "one" | "optional" | "variadic";
3
+ export type CliPositionalLimit = number | "variadic";
3
4
  export interface CliFlagContract {
4
5
  arity: CliFlagArity;
5
6
  values?: readonly string[];
@@ -52,12 +53,13 @@ export interface CliSubcommandContract {
52
53
  commandPath: readonly string[];
53
54
  helpArgs: readonly string[][];
54
55
  flags: Record<string, CliFlagContract>;
55
- maxPositionals: number;
56
+ maxPositionals: CliPositionalLimit;
56
57
  acknowledgedUpstreamFlags?: readonly string[];
57
58
  aliases?: readonly string[];
58
59
  children?: Record<string, CliSubcommandContract>;
59
60
  risk: CliSubcommandRisk;
60
61
  exposure: CliSubcommandExposure;
62
+ adminProjection?: "not_exposed";
61
63
  tier: CliSubcommandTier;
62
64
  tokenCost: CliSubcommandTokenCost;
63
65
  summary: string;
@@ -122,6 +124,7 @@ export interface AcpEntrypointContract {
122
124
  docsRef: string;
123
125
  }
124
126
  export declare const ACP_ENTRYPOINT_CONTRACTS: Record<CliType, AcpEntrypointContract>;
127
+ export declare const CLAUDE_WIRE_PERMISSION_MODES: readonly ["acceptEdits", "auto", "bypassPermissions", "manual", "dontAsk", "plan"];
125
128
  export declare const UPSTREAM_CLI_CONTRACTS: Record<CliType, CliContract>;
126
129
  export declare function validateUpstreamCliArgs(cli: CliType, args: readonly string[]): ContractValidationResult;
127
130
  export declare function assertUpstreamCliArgs(cli: CliType, args: readonly string[]): void;
@@ -67,7 +67,7 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
67
67
  executable: "devin",
68
68
  entrypointArgs: ["acp"],
69
69
  targetVersion: PROVIDER_TARGET_VERSIONS.devin,
70
- probeArgs: [["--version"]],
70
+ probeArgs: [["acp", "--help"]],
71
71
  evidence: 'Native ACP entrypoint `devin acp` (stdio JSON-RPC). Slice D1 manual initialize + session/new smoke passed (protocolVersion 1, agent "Affogato", session created). Third native runtime pilot; routing stays config-gated.',
72
72
  docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.devin",
73
73
  },
@@ -83,13 +83,21 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
83
83
  docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.cursor",
84
84
  },
85
85
  };
86
- const PERMISSION_MODES = [
87
- "default",
86
+ export const CLAUDE_WIRE_PERMISSION_MODES = [
88
87
  "acceptEdits",
88
+ "auto",
89
+ "bypassPermissions",
90
+ "manual",
91
+ "dontAsk",
89
92
  "plan",
93
+ ];
94
+ const GROK_PERMISSION_MODES = [
95
+ "default",
96
+ "acceptEdits",
90
97
  "auto",
91
98
  "dontAsk",
92
99
  "bypassPermissions",
100
+ "plan",
93
101
  ];
94
102
  const EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
95
103
  function scFlag(name, arity = "optional") {
@@ -114,6 +122,7 @@ function subcommand(commandPath, summary, risk, flags = [], options = {}) {
114
122
  children: options.children ?? {},
115
123
  risk,
116
124
  exposure: options.exposure ?? "tracked_only",
125
+ ...(options.adminProjection ? { adminProjection: options.adminProjection } : {}),
117
126
  tier: options.tier ?? "catalog",
118
127
  tokenCost: options.tokenCost ?? "small",
119
128
  summary,
@@ -150,6 +159,10 @@ export const UPSTREAM_CLI_CONTRACTS = {
150
159
  doctor: subcommand(["doctor"], "Run Claude Code diagnostic checks.", "read_only", [], {
151
160
  tier: "diagnostic",
152
161
  }),
162
+ gateway: subcommand(["gateway"], "Run Claude enterprise auth and telemetry gateway mode.", "starts_server", ["--config"], {
163
+ exposure: "not_exposed",
164
+ flagArities: { "--config": "one" },
165
+ }),
153
166
  mcp: subcommand(["mcp"], "Manage Claude MCP server configuration.", "writes_local_config"),
154
167
  plugin: subcommand(["plugin"], "Manage Claude plugins.", "writes_local_config", [], {
155
168
  aliases: ["plugins"],
@@ -244,7 +257,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
244
257
  },
245
258
  "--output-format": {
246
259
  arity: "one",
247
- values: ["json", "stream-json"],
260
+ values: ["text", "json", "stream-json"],
248
261
  description: "Machine-readable output format",
249
262
  },
250
263
  "--include-partial-messages": {
@@ -259,7 +272,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
259
272
  "--disallowed-tools": { arity: "variadic", description: "Disallowed tool names/patterns" },
260
273
  "--permission-mode": {
261
274
  arity: "one",
262
- values: PERMISSION_MODES,
275
+ values: CLAUDE_WIRE_PERMISSION_MODES,
263
276
  description: "Claude permission mode",
264
277
  },
265
278
  "--mcp-config": { arity: "one", description: "MCP config path" },
@@ -392,6 +405,18 @@ export const UPSTREAM_CLI_CONTRACTS = {
392
405
  args: ["-p", "hello"],
393
406
  expect: "pass",
394
407
  },
408
+ {
409
+ id: "claude-permission-mode-manual",
410
+ description: "Claude 2.1.207 accepts the manual permission mode literal",
411
+ args: ["-p", "hello", "--permission-mode", "manual"],
412
+ expect: "pass",
413
+ },
414
+ {
415
+ id: "claude-permission-mode-default-gateway-only",
416
+ description: "Gateway default is represented by omitting the upstream flag",
417
+ args: ["-p", "hello", "--permission-mode", "default"],
418
+ expect: "fail",
419
+ },
395
420
  {
396
421
  id: "claude-unsupported-flag",
397
422
  description: "Unsupported flag is rejected before spawn",
@@ -404,6 +429,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
404
429
  args: ["-p", "hello", "--fallback-model", "claude-haiku-4-5-20251001"],
405
430
  expect: "pass",
406
431
  },
432
+ {
433
+ id: "claude-output-format-text",
434
+ description: "Claude 2.1.207 accepts explicit text output format",
435
+ args: ["-p", "hello", "--output-format", "text"],
436
+ expect: "pass",
437
+ },
407
438
  {
408
439
  id: "claude-json-schema",
409
440
  description: "Phase 4 slice η: --json-schema accepts inline JSON literal",
@@ -508,8 +539,8 @@ export const UPSTREAM_CLI_CONTRACTS = {
508
539
  upstream: "OpenAI Codex CLI",
509
540
  upstreamMetadata: {
510
541
  sourceUrls: [
511
- "https://github.com/openai/codex/releases",
512
- "https://developers.openai.com/codex/changelog",
542
+ "https://api.github.com/repos/openai/codex/releases/latest",
543
+ "https://learn.chatgpt.com/docs/changelog/rss.xml",
513
544
  ],
514
545
  packageName: "@openai/codex",
515
546
  repo: "https://github.com/openai/codex",
@@ -668,6 +699,76 @@ export const UPSTREAM_CLI_CONTRACTS = {
668
699
  ], { exposure: "not_exposed" }),
669
700
  debug: subcommand(["debug"], "Run Codex debugging utilities.", "read_only", ["--config", "--disable", "--enable"], { tier: "diagnostic" }),
670
701
  apply: subcommand(["apply"], "Apply a Codex patch to the workspace.", "destructive", ["--config", "--disable", "--enable"], { exposure: "not_exposed" }),
702
+ resume: subcommand(["resume"], "Resume a saved interactive Codex session.", "executes_agent", [
703
+ "--add-dir",
704
+ "--all",
705
+ "--ask-for-approval",
706
+ "--cd",
707
+ "--config",
708
+ "--dangerously-bypass-approvals-and-sandbox",
709
+ "--dangerously-bypass-hook-trust",
710
+ "--disable",
711
+ "--enable",
712
+ "--image",
713
+ "--include-non-interactive",
714
+ "--last",
715
+ "--local-provider",
716
+ "--model",
717
+ "--no-alt-screen",
718
+ "--oss",
719
+ "--profile",
720
+ "--remote",
721
+ "--remote-auth-token-env",
722
+ "--sandbox",
723
+ "--search",
724
+ "--strict-config",
725
+ "--version",
726
+ ], {
727
+ exposure: "not_exposed",
728
+ maxPositionals: 2,
729
+ flagArities: {
730
+ "--all": "none",
731
+ "--dangerously-bypass-approvals-and-sandbox": "none",
732
+ "--dangerously-bypass-hook-trust": "none",
733
+ "--include-non-interactive": "none",
734
+ "--last": "none",
735
+ "--no-alt-screen": "none",
736
+ "--oss": "none",
737
+ "--search": "none",
738
+ "--strict-config": "none",
739
+ "--version": "none",
740
+ },
741
+ }),
742
+ delete: subcommand(["delete"], "Permanently delete a saved Codex session.", "destructive", [
743
+ "--add-dir",
744
+ "--cd",
745
+ "--config",
746
+ "--dangerously-bypass-approvals-and-sandbox",
747
+ "--dangerously-bypass-hook-trust",
748
+ "--disable",
749
+ "--enable",
750
+ "--force",
751
+ "--image",
752
+ "--local-provider",
753
+ "--model",
754
+ "--oss",
755
+ "--profile",
756
+ "--remote",
757
+ "--remote-auth-token-env",
758
+ "--sandbox",
759
+ "--strict-config",
760
+ ], {
761
+ exposure: "not_exposed",
762
+ adminProjection: "not_exposed",
763
+ maxPositionals: 1,
764
+ flagArities: {
765
+ "--dangerously-bypass-approvals-and-sandbox": "none",
766
+ "--dangerously-bypass-hook-trust": "none",
767
+ "--force": "none",
768
+ "--oss": "none",
769
+ "--strict-config": "none",
770
+ },
771
+ }),
671
772
  archive: subcommand(["archive"], "Archive Codex session state.", "writes_local_config", [
672
773
  "--add-dir",
673
774
  "--cd",
@@ -960,7 +1061,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
960
1061
  upstreamMetadata: {
961
1062
  sourceUrls: [
962
1063
  "https://antigravity.google/docs/cli-overview",
963
- "https://github.com/google-antigravity/antigravity-cli/releases",
1064
+ "https://api.github.com/repos/google-antigravity/antigravity-cli/releases/latest",
964
1065
  ],
965
1066
  repo: "https://github.com/google-antigravity/antigravity-cli",
966
1067
  installDocsUrl: "https://antigravity.google/docs/cli-getting-started",
@@ -969,6 +1070,11 @@ export const UPSTREAM_CLI_CONTRACTS = {
969
1070
  },
970
1071
  helpArgs: [["--help"]],
971
1072
  subcommands: {
1073
+ agent: subcommand(["agent"], "List available Antigravity agents.", "read_only", [], {
1074
+ aliases: ["agents"],
1075
+ tier: "inspect",
1076
+ tokenCost: "tiny",
1077
+ }),
972
1078
  changelog: subcommand(["changelog"], "Show Antigravity CLI changelog and release notes.", "read_only", [], { tokenCost: "small" }),
973
1079
  install: subcommand(["install"], "Configure Antigravity CLI environment paths and shell settings.", "writes_local_config", ["--dir", "--skip-aliases", "--skip-path"], {
974
1080
  flagArities: { "--dir": "one", "--skip-aliases": "none", "--skip-path": "none" },
@@ -1033,7 +1139,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
1033
1139
  description: "Print-mode wait timeout as a Go duration string (e.g. 5m0s)",
1034
1140
  },
1035
1141
  },
1036
- acknowledgedUpstreamFlags: ["--log-file", "--mode", "--prompt-interactive"],
1142
+ acknowledgedUpstreamFlags: ["--agent", "--log-file", "--mode", "--prompt-interactive"],
1037
1143
  env: {},
1038
1144
  conformanceFixtures: [
1039
1145
  {
@@ -1048,6 +1154,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
1048
1154
  args: ["--print", "hello", "--not-a-gemini-flag"],
1049
1155
  expect: "fail",
1050
1156
  },
1157
+ {
1158
+ id: "gemini-agent-selection-acknowledged-not-emitted",
1159
+ description: "Antigravity 1.1.1 advertises --agent, but gateway request argv stays closed until its security model is explicitly wired",
1160
+ args: ["--print", "hello", "--agent", "reviewer"],
1161
+ expect: "fail",
1162
+ },
1051
1163
  {
1052
1164
  id: "gemini-antigravity-workspace-flags",
1053
1165
  description: "Antigravity workspace and sandbox flags are accepted",
@@ -1181,49 +1293,9 @@ export const UPSTREAM_CLI_CONTRACTS = {
1181
1293
  sessions: subcommand(["sessions"], "Inspect Grok sessions.", "read_only", ["--leader-socket"], {
1182
1294
  tier: "inspect",
1183
1295
  }),
1184
- setup: subcommand(["setup"], "Configure Grok CLI local setup.", "writes_local_config", ["--leader-socket"], { exposure: "not_exposed" }),
1185
- ssh: subcommand(["ssh"], "Manage Grok SSH integration.", "network", ["--leader-socket"], {
1186
- acknowledgedUpstreamFlags: [
1187
- "--agent",
1188
- "--agents",
1189
- "--allow",
1190
- "--always-approve",
1191
- "--best-of-n",
1192
- "--check",
1193
- "--continue",
1194
- "--cwd",
1195
- "--deny",
1196
- "--disable-web-search",
1197
- "--disallowed-tools",
1198
- "--experimental-memory",
1199
- "--fork-session",
1200
- "--json-schema",
1201
- "--max-turns",
1202
- "--minimal",
1203
- "--model",
1204
- "--no-alt-screen",
1205
- "--no-memory",
1206
- "--no-plan",
1207
- "--no-subagents",
1208
- "--oauth",
1209
- "--output-format",
1210
- "--permission-mode",
1211
- "--prompt-file",
1212
- "--prompt-json",
1213
- "--reasoning-effort",
1214
- "--restore-code",
1215
- "--resume",
1216
- "--rules",
1217
- "--sandbox",
1218
- "--session-id",
1219
- "--single",
1220
- "--system-prompt-override",
1221
- "--tools",
1222
- "--verbatim",
1223
- "--version",
1224
- "--worktree",
1225
- "--worktree-ref",
1226
- ],
1296
+ setup: subcommand(["setup"], "Configure Grok CLI local setup.", "writes_local_config", ["--json", "--leader-socket"], {
1297
+ exposure: "not_exposed",
1298
+ flagArities: { "--json": "none", "--leader-socket": "one" },
1227
1299
  }),
1228
1300
  trace: subcommand(["trace"], "Inspect Grok trace data.", "read_only", ["--json", "--leader-socket", "--local", "--output"], { tier: "diagnostic" }),
1229
1301
  update: subcommand(["update"], "Update the Grok CLI binary.", "updates_binary", [
@@ -1237,9 +1309,20 @@ export const UPSTREAM_CLI_CONTRACTS = {
1237
1309
  ], { exposure: "not_exposed" }),
1238
1310
  version: subcommand(["version"], "Print Grok version information.", "read_only", ["--json", "--leader-socket"], { tier: "diagnostic" }),
1239
1311
  worktree: subcommand(["worktree"], "Manage Grok worktree sessions.", "writes_local_config", ["--leader-socket"]),
1312
+ wrap: subcommand(["wrap"], "Run an arbitrary command inside a local clipboard-forwarding PTY.", "destructive", ["--leader-socket"], {
1313
+ exposure: "not_exposed",
1314
+ tier: "execute_candidate",
1315
+ maxPositionals: "variadic",
1316
+ flagArities: { "--leader-socket": "one" },
1317
+ }),
1240
1318
  }, GROK_DEBUG_HELP_FLAGS),
1241
1319
  maxPositionals: 0,
1242
- acknowledgedUpstreamFlags: [...GROK_DEBUG_HELP_FLAGS, "--minimal", "--session-id"],
1320
+ acknowledgedUpstreamFlags: [
1321
+ ...GROK_DEBUG_HELP_FLAGS,
1322
+ "--fullscreen",
1323
+ "--minimal",
1324
+ "--session-id",
1325
+ ],
1243
1326
  mcpTools: ["grok_request", "grok_request_async"],
1244
1327
  mcpParameters: [
1245
1328
  "prompt",
@@ -1299,7 +1382,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
1299
1382
  "--always-approve": { arity: "none", description: "Approve tool use automatically" },
1300
1383
  "--permission-mode": {
1301
1384
  arity: "one",
1302
- values: PERMISSION_MODES,
1385
+ values: GROK_PERMISSION_MODES,
1303
1386
  description: "Permission mode",
1304
1387
  },
1305
1388
  "--effort": { arity: "one", values: EFFORT_LEVELS, description: "Reasoning effort" },
@@ -1648,6 +1731,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
1648
1731
  description: "Agent/permission mode (builtin, install-gated, or custom agent name)",
1649
1732
  },
1650
1733
  "--enabled-tools": { arity: "one", description: "Enabled tool" },
1734
+ "--disabled-tools": { arity: "one", description: "Disabled tool" },
1651
1735
  "--resume": {
1652
1736
  arity: "optional",
1653
1737
  description: "Resume session by ID, or interactive picker when omitted",
@@ -1681,13 +1765,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
1681
1765
  description: "Additional writable workspace directory (Phase 4 slice ζ; repeat once per directory)",
1682
1766
  },
1683
1767
  },
1684
- acknowledgedUpstreamFlags: [
1685
- "--auto-approve",
1686
- "--check-upgrade",
1687
- "--disabled-tools",
1688
- "--worktree",
1689
- "--yolo",
1690
- ],
1768
+ acknowledgedUpstreamFlags: ["--auto-approve", "--check-upgrade", "--worktree", "--yolo"],
1691
1769
  env: {
1692
1770
  VIBE_ACTIVE_MODEL: {
1693
1771
  arity: "one",
@@ -1813,11 +1891,11 @@ export const UPSTREAM_CLI_CONTRACTS = {
1813
1891
  expect: "fail",
1814
1892
  },
1815
1893
  {
1816
- id: "mistral-disabled-tools-rejected",
1817
- description: "Vibe 2.19.1 adds --disabled-tools (denylist counterpart to --enabled-tools); the gateway does not emit it (disallowedTools is accepted but ignored) and rejects raw --disabled-tools as caller argv",
1894
+ id: "mistral-disabled-tools",
1895
+ description: "Vibe 2.19.1 --disabled-tools denylist is accepted once per tool",
1818
1896
  args: ["-p", "hello", "--disabled-tools", "shell"],
1819
1897
  env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
1820
- expect: "fail",
1898
+ expect: "pass",
1821
1899
  },
1822
1900
  {
1823
1901
  id: "mistral-resume-bare",
@@ -1833,14 +1911,63 @@ export const UPSTREAM_CLI_CONTRACTS = {
1833
1911
  executable: "devin",
1834
1912
  upstream: "Cognition Devin CLI",
1835
1913
  upstreamMetadata: {
1836
- sourceUrls: ["https://cli.devin.ai/docs/reference/commands", "https://docs.devin.ai/cli"],
1914
+ sourceUrls: [
1915
+ "https://cli.devin.ai/docs/reference/commands",
1916
+ "https://docs.devin.ai/cli",
1917
+ "https://docs.devin.ai/cli/changelog/stable",
1918
+ ],
1837
1919
  packageName: "devin",
1838
1920
  installDocsUrl: "https://docs.devin.ai/cli",
1839
1921
  releaseChannel: "vendor",
1840
1922
  watchCategories: ["flags", "subcommands", "permission-modes", "acp-entrypoint"],
1841
1923
  },
1842
1924
  helpArgs: [["--help"]],
1843
- subcommands: {},
1925
+ subcommands: {
1926
+ acp: subcommand(["acp"], "Run the Devin Agent Client Protocol server over stdio.", "starts_server", ["--agent-type"], {
1927
+ exposure: "not_exposed",
1928
+ flagArities: { "--agent-type": "one" },
1929
+ }),
1930
+ auth: subcommand(["auth"], "Manage Devin authentication state.", "auth", [], {
1931
+ exposure: "not_exposed",
1932
+ maxPositionals: "variadic",
1933
+ }),
1934
+ cloud: subcommand(["cloud"], "Manage Devin Cloud environments, sandboxes, and builds.", "network", [], { exposure: "not_exposed", maxPositionals: "variadic" }),
1935
+ list: subcommand(["list"], "List Devin sessions in the current directory.", "read_only", ["--format"], {
1936
+ aliases: ["ls"],
1937
+ tier: "inspect",
1938
+ flagArities: { "--format": "one" },
1939
+ }),
1940
+ mcp: subcommand(["mcp"], "Manage Devin MCP server connections.", "writes_local_config", [], {
1941
+ exposure: "not_exposed",
1942
+ maxPositionals: "variadic",
1943
+ }),
1944
+ plugins: subcommand(["plugins"], "Manage Devin plugins.", "writes_local_config", [], {
1945
+ exposure: "not_exposed",
1946
+ maxPositionals: "variadic",
1947
+ }),
1948
+ rules: subcommand(["rules"], "Manage Devin always-on agent rules.", "writes_local_config", [], { exposure: "not_exposed", maxPositionals: "variadic" }),
1949
+ sandbox: subcommand(["sandbox"], "Manage Devin process sandboxing.", "writes_local_config", [], { exposure: "not_exposed", maxPositionals: "variadic" }),
1950
+ setup: subcommand(["setup"], "Run Devin interactive setup.", "writes_local_config", ["--force-manual-token-flow"], {
1951
+ exposure: "not_exposed",
1952
+ flagArities: { "--force-manual-token-flow": "none" },
1953
+ }),
1954
+ shell: subcommand(["shell"], "Integrate Devin with the local shell.", "writes_local_config", [], { exposure: "not_exposed", maxPositionals: "variadic" }),
1955
+ skills: subcommand(["skills"], "Manage Devin skills.", "writes_local_config", [], {
1956
+ exposure: "not_exposed",
1957
+ maxPositionals: "variadic",
1958
+ }),
1959
+ uninstall: subcommand(["uninstall"], "Uninstall Devin and remove local data.", "destructive", ["--clean", "--force"], {
1960
+ exposure: "not_exposed",
1961
+ flagArities: { "--clean": "none", "--force": "none" },
1962
+ }),
1963
+ update: subcommand(["update"], "Check for or install Devin CLI updates.", "updates_binary", ["--force"], {
1964
+ exposure: "not_exposed",
1965
+ flagArities: { "--force": "none" },
1966
+ }),
1967
+ version: subcommand(["version"], "Print Devin version information.", "read_only", [], {
1968
+ tier: "diagnostic",
1969
+ }),
1970
+ },
1844
1971
  maxPositionals: 0,
1845
1972
  mcpTools: ["devin_request", "devin_request_async"],
1846
1973
  mcpParameters: [
@@ -1865,8 +1992,8 @@ export const UPSTREAM_CLI_CONTRACTS = {
1865
1992
  "--model": { arity: "one", description: "AI model for this session" },
1866
1993
  "--permission-mode": {
1867
1994
  arity: "one",
1868
- values: ["auto", "smart", "dangerous"],
1869
- description: "Permission mode (auto = read-only auto-approve; smart = additionally auto-runs safe actions per fast model; dangerous = approve all)",
1995
+ values: ["auto", "accept-edits", "smart", "dangerous"],
1996
+ description: "Permission mode (auto = read-only auto-approve; accept-edits = also auto-approve workspace edits; smart = additionally auto-runs safe actions per fast model; dangerous = approve all)",
1870
1997
  },
1871
1998
  "--prompt-file": { arity: "one", description: "Load the initial prompt from a file" },
1872
1999
  "--config": { arity: "one", description: "Config file path" },
@@ -1917,6 +2044,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
1917
2044
  args: ["-p", "hello", "--permission-mode", "auto"],
1918
2045
  expect: "pass",
1919
2046
  },
2047
+ {
2048
+ id: "devin-permission-mode-accept-edits",
2049
+ description: "Valid --permission-mode 'accept-edits' accepted",
2050
+ args: ["-p", "hello", "--permission-mode", "accept-edits"],
2051
+ expect: "pass",
2052
+ },
1920
2053
  {
1921
2054
  id: "devin-permission-mode-smart",
1922
2055
  description: "Valid --permission-mode 'smart' accepted",
@@ -2007,7 +2140,57 @@ export const UPSTREAM_CLI_CONTRACTS = {
2007
2140
  ],
2008
2141
  },
2009
2142
  helpArgs: [["--help"]],
2010
- subcommands: {},
2143
+ subcommands: {
2144
+ about: subcommand(["about"], "Display Cursor Agent version, system, and account information.", "read_only", ["--format"], { tier: "diagnostic", flagArities: { "--format": "one" } }),
2145
+ agent: subcommand(["agent"], "Start an interactive Cursor Agent session.", "executes_agent", [], { exposure: "not_exposed", maxPositionals: "variadic" }),
2146
+ "create-chat": subcommand(["create-chat"], "Create a new Cursor chat and return its ID.", "network", [], { exposure: "not_exposed" }),
2147
+ "generate-rule": subcommand(["generate-rule"], "Generate a Cursor rule through interactive prompts.", "writes_local_config", [], { aliases: ["rule"], exposure: "not_exposed", maxPositionals: "variadic" }),
2148
+ "install-shell-integration": subcommand(["install-shell-integration"], "Install Cursor shell integration.", "writes_local_config", [], { exposure: "not_exposed" }),
2149
+ login: subcommand(["login"], "Authenticate with Cursor.", "auth", [], {
2150
+ exposure: "not_exposed",
2151
+ }),
2152
+ logout: subcommand(["logout"], "Clear Cursor authentication state.", "auth", [], {
2153
+ exposure: "not_exposed",
2154
+ }),
2155
+ ls: subcommand(["ls"], "Resume a Cursor chat session.", "executes_agent", [], {
2156
+ exposure: "not_exposed",
2157
+ maxPositionals: "variadic",
2158
+ }),
2159
+ mcp: subcommand(["mcp"], "Manage Cursor MCP server configuration.", "writes_local_config", [], { exposure: "not_exposed", maxPositionals: "variadic" }),
2160
+ models: subcommand(["models"], "List Cursor account models.", "read_only", [], {
2161
+ tier: "inspect",
2162
+ }),
2163
+ resume: subcommand(["resume"], "Resume the latest Cursor chat.", "executes_agent", [], {
2164
+ exposure: "not_exposed",
2165
+ maxPositionals: "variadic",
2166
+ }),
2167
+ status: subcommand(["status"], "View Cursor authentication status.", "read_only", ["--format"], {
2168
+ aliases: ["whoami"],
2169
+ tier: "inspect",
2170
+ flagArities: { "--format": "one" },
2171
+ }),
2172
+ "uninstall-shell-integration": subcommand(["uninstall-shell-integration"], "Remove Cursor shell integration.", "writes_local_config", [], { exposure: "not_exposed" }),
2173
+ update: subcommand(["update"], "Update Cursor Agent.", "updates_binary", [], {
2174
+ exposure: "not_exposed",
2175
+ }),
2176
+ worker: subcommand(["worker"], "Start a private Cursor cloud worker for local agent runs.", "starts_server", [
2177
+ "--auth-token-file",
2178
+ "--data-dir",
2179
+ "--debug",
2180
+ "--idle-release-timeout",
2181
+ "--label",
2182
+ "--labels-file",
2183
+ "--management-addr",
2184
+ "--name",
2185
+ "--pool",
2186
+ "--pool-name",
2187
+ "--single-use",
2188
+ "--worker-dir",
2189
+ ], {
2190
+ exposure: "not_exposed",
2191
+ flagArities: { "--debug": "none", "--single-use": "none" },
2192
+ }),
2193
+ },
2011
2194
  maxPositionals: 1,
2012
2195
  mcpTools: ["cursor_request", "cursor_request_async"],
2013
2196
  mcpParameters: [
@@ -2297,6 +2480,7 @@ export function serializeCliSubcommandContract(cli, contract) {
2297
2480
  })),
2298
2481
  risk: contract.risk,
2299
2482
  exposure: contract.exposure,
2483
+ adminProjection: contract.adminProjection ?? null,
2300
2484
  tier: contract.tier,
2301
2485
  tokenCost: contract.tokenCost,
2302
2486
  summary: contract.summary,
@@ -2441,7 +2625,7 @@ export function validateUpstreamCliSubcommandArgs(cli, commandPath, args) {
2441
2625
  });
2442
2626
  }
2443
2627
  }
2444
- if (positionals.length > contract.maxPositionals) {
2628
+ if (contract.maxPositionals !== "variadic" && positionals.length > contract.maxPositionals) {
2445
2629
  violations.push({
2446
2630
  cli,
2447
2631
  message: `${cli} subcommand "${subcommandKey(commandPath)}" has ${positionals.length} positional values; upstream subcommand contract allows ${contract.maxPositionals}`,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "2.17.0",
3
+ "version": "2.17.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "llm-cli-gateway",
9
- "version": "2.17.0",
9
+ "version": "2.17.1",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/sdk": "^1.29.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "2.17.0",
3
+ "version": "2.17.1",
4
4
  "mcpName": "io.github.verivus-oss/llm-cli-gateway",
5
5
  "description": "Secure local control plane for AI coding agents across supported MCP clients, with workspace-scoped remote access, approval gates, durable jobs, sessions, and audit receipts.",
6
6
  "license": "MIT",
@@ -151,5 +151,9 @@
151
151
  "directories": {
152
152
  "doc": "docs"
153
153
  },
154
- "types": "./dist/index.d.ts"
154
+ "types": "./dist/index.d.ts",
155
+ "allowScripts": {
156
+ "better-sqlite3@12.11.1": true,
157
+ "fsevents": false
158
+ }
155
159
  }