llm-cli-gateway 2.7.0 → 2.8.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/dist/config.js CHANGED
@@ -348,6 +348,125 @@ export function isXaiProviderEnabled(config, env = process.env) {
348
348
  return false;
349
349
  return typeof env[keyEnv] === "string" && env[keyEnv].trim().length > 0;
350
350
  }
351
+ export const ACP_TRANSPORTS = ["cli", "acp"];
352
+ export const DEFAULT_ACP_PROCESS_IDLE_TIMEOUT_MS = 600000;
353
+ export const DEFAULT_ACP_INITIALIZE_TIMEOUT_MS = 10000;
354
+ export const DEFAULT_ACP_SESSION_NEW_TIMEOUT_MS = 10000;
355
+ export const DEFAULT_ACP_PROMPT_TIMEOUT_MS = 600000;
356
+ const SHELL_METACHARACTERS = /[\s|&;<>(){}$`"'\\*?[\]~#!]/;
357
+ function isSafeExecutable(value) {
358
+ if (value.length === 0)
359
+ return false;
360
+ return !SHELL_METACHARACTERS.test(value);
361
+ }
362
+ const SafeExecutableSchema = z
363
+ .string()
364
+ .min(1)
365
+ .refine(isSafeExecutable, {
366
+ message: "ACP provider command must be a bare executable name or path with no shell metacharacters " +
367
+ "(no spaces, quotes, pipes, redirects, globs, or command substitution); pass arguments via 'args'",
368
+ });
369
+ const SafeArgSchema = z.string();
370
+ const AcpProviderSchema = z
371
+ .object({
372
+ enabled: z.boolean().default(false),
373
+ command: SafeExecutableSchema,
374
+ args: z.array(SafeArgSchema).default([]),
375
+ runtime_enabled: z.boolean().default(false),
376
+ isolated_leader_socket: z.boolean().default(false),
377
+ })
378
+ .strict();
379
+ const AcpConfigSchema = z
380
+ .object({
381
+ enabled: z.boolean().default(false),
382
+ default_transport: z.enum(ACP_TRANSPORTS).default("cli"),
383
+ smoke_on_startup: z.boolean().default(false),
384
+ process_idle_timeout_ms: z
385
+ .number()
386
+ .int()
387
+ .positive()
388
+ .default(DEFAULT_ACP_PROCESS_IDLE_TIMEOUT_MS),
389
+ initialize_timeout_ms: z.number().int().positive().default(DEFAULT_ACP_INITIALIZE_TIMEOUT_MS),
390
+ session_new_timeout_ms: z.number().int().positive().default(DEFAULT_ACP_SESSION_NEW_TIMEOUT_MS),
391
+ prompt_timeout_ms: z.number().int().positive().default(DEFAULT_ACP_PROMPT_TIMEOUT_MS),
392
+ allow_write_host_services: z.boolean().default(false),
393
+ allow_terminal_host_services: z.boolean().default(false),
394
+ fallback_to_cli_when_unhealthy: z.boolean().default(true),
395
+ providers: z.record(z.string(), AcpProviderSchema).default({}),
396
+ })
397
+ .strict();
398
+ function defaultAcpConfig(sourcePath) {
399
+ return {
400
+ enabled: false,
401
+ defaultTransport: "cli",
402
+ smokeOnStartup: false,
403
+ processIdleTimeoutMs: DEFAULT_ACP_PROCESS_IDLE_TIMEOUT_MS,
404
+ initializeTimeoutMs: DEFAULT_ACP_INITIALIZE_TIMEOUT_MS,
405
+ sessionNewTimeoutMs: DEFAULT_ACP_SESSION_NEW_TIMEOUT_MS,
406
+ promptTimeoutMs: DEFAULT_ACP_PROMPT_TIMEOUT_MS,
407
+ allowWriteHostServices: false,
408
+ allowTerminalHostServices: false,
409
+ fallbackToCliWhenUnhealthy: true,
410
+ providers: {},
411
+ sources: { configFile: sourcePath },
412
+ };
413
+ }
414
+ function readAcpFile(configPath, logger) {
415
+ if (!existsSync(configPath)) {
416
+ return { raw: undefined, sourcePath: null };
417
+ }
418
+ try {
419
+ const require = createRequire(import.meta.url);
420
+ const TOML = require("smol-toml");
421
+ const text = readFileSync(configPath, "utf-8");
422
+ const parsed = TOML.parse(text);
423
+ return { raw: parsed?.acp, sourcePath: configPath };
424
+ }
425
+ catch (err) {
426
+ logger.error(`Failed to parse gateway config at ${configPath}; using acp defaults`, err);
427
+ return { raw: undefined, sourcePath: null };
428
+ }
429
+ }
430
+ export function loadAcpConfig(logger = noopLogger) {
431
+ const configPath = defaultGatewayConfigPath();
432
+ const { raw, sourcePath } = readAcpFile(configPath, logger);
433
+ if (raw === undefined) {
434
+ return defaultAcpConfig(sourcePath);
435
+ }
436
+ let parsed;
437
+ try {
438
+ parsed = AcpConfigSchema.parse(raw);
439
+ }
440
+ catch (err) {
441
+ throw new Error(`Invalid [acp] config: ${err instanceof Error ? err.message : String(err)}`, {
442
+ cause: err,
443
+ });
444
+ }
445
+ const providers = {};
446
+ for (const [name, p] of Object.entries(parsed.providers)) {
447
+ providers[name] = {
448
+ enabled: p.enabled,
449
+ command: p.command,
450
+ args: p.args,
451
+ runtimeEnabled: p.runtime_enabled,
452
+ isolatedLeaderSocket: p.isolated_leader_socket,
453
+ };
454
+ }
455
+ return {
456
+ enabled: parsed.enabled,
457
+ defaultTransport: parsed.default_transport,
458
+ smokeOnStartup: parsed.smoke_on_startup,
459
+ processIdleTimeoutMs: parsed.process_idle_timeout_ms,
460
+ initializeTimeoutMs: parsed.initialize_timeout_ms,
461
+ sessionNewTimeoutMs: parsed.session_new_timeout_ms,
462
+ promptTimeoutMs: parsed.prompt_timeout_ms,
463
+ allowWriteHostServices: parsed.allow_write_host_services,
464
+ allowTerminalHostServices: parsed.allow_terminal_host_services,
465
+ fallbackToCliWhenUnhealthy: parsed.fallback_to_cli_when_unhealthy,
466
+ providers,
467
+ sources: { configFile: sourcePath },
468
+ };
469
+ }
351
470
  const OAuthRegistrationPolicySchema = z.enum(["static_clients", "shared_secret", "open_dev"]);
352
471
  const OAuthClientSchema = z
353
472
  .object({
@@ -155,7 +155,7 @@ export async function startHttpGateway(options) {
155
155
  jsonError(res, 404, "Not found");
156
156
  return;
157
157
  }
158
- let requestContext = { authScopes: [] };
158
+ let requestContext = { authScopes: [], transport: "http" };
159
159
  if (!noAuthPath) {
160
160
  const auth = authorizeBearerRequest(req, token);
161
161
  if (!auth.ok) {
@@ -163,6 +163,7 @@ export async function startHttpGateway(options) {
163
163
  return;
164
164
  }
165
165
  requestContext = {
166
+ transport: "http",
166
167
  authKind: auth.kind,
167
168
  authScopes: auth.scopes ?? [],
168
169
  authClientId: auth.clientId,
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { fileURLToPath } from "url";
9
9
  import { z } from "zod/v3";
10
10
  import { executeCli, killAllProcessGroups, providerCommandName } from "./executor.js";
11
11
  import { parseStreamJson } from "./stream-json-parser.js";
12
- import { parseCodexJsonStream } from "./codex-json-parser.js";
12
+ import { parseCodexJsonStream, codexDisplayText, codexFrResponse } from "./codex-json-parser.js";
13
13
  import { parseGeminiJson, parseGeminiStreamJson } from "./gemini-json-parser.js";
14
14
  import { parseVibeMetaJson } from "./mistral-meta-json-parser.js";
15
15
  import { homedir } from "os";
@@ -489,13 +489,10 @@ async function resolveWorkspaceAndWorktreeForRequest(args) {
489
489
  else if (isGatewayAppDirCwd()) {
490
490
  throw new Error("No workspace selected. Configure [workspaces].default or pass a registered workspace alias.");
491
491
  }
492
- if (!workspace && getRequestContext()?.authKind === "oauth") {
493
- throw new Error("Remote OAuth provider requests require a registered workspace alias or [workspaces].default.");
494
- }
495
- if (!workspace &&
496
- (args.workingDir || (args.addDir?.length ?? 0) > 0) &&
497
- !args.runtime.workspaces.allowUnregisteredWorkingDir) {
498
- throw new Error("workingDir/addDir require a registered workspace alias unless [workspaces].allow_unregistered_working_dir is explicitly enabled.");
492
+ const requestContext = getRequestContext();
493
+ const isRemoteTransport = requestContext?.transport === "http" || requestContext?.authKind === "oauth";
494
+ if (!workspace && isRemoteTransport) {
495
+ throw new Error("Remote HTTP provider requests require a registered workspace alias, session workspace, or [workspaces].default.");
499
496
  }
500
497
  if (workspace) {
501
498
  if (args.workingDir) {
@@ -720,7 +717,7 @@ export function extractUsageAndCost(cli, output, outputFormat, ctx) {
720
717
  costUsd: parsed.costUsd ?? undefined,
721
718
  };
722
719
  }
723
- if (cli === "codex" && outputFormat === "json") {
720
+ if (cli === "codex") {
724
721
  const parsed = parseCodexJsonStream(output);
725
722
  if (!parsed.usage) {
726
723
  return {};
@@ -1420,9 +1417,7 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1420
1417
  if (params.dangerouslyBypassApprovalsAndSandbox) {
1421
1418
  args.push("--dangerously-bypass-approvals-and-sandbox");
1422
1419
  }
1423
- if (params.outputFormat === "json") {
1424
- args.push("--json");
1425
- }
1420
+ args.push("--json");
1426
1421
  args.push("--skip-git-repo-check");
1427
1422
  let highImpactCleanup;
1428
1423
  if (sessionPlan.mode === "new") {
@@ -1830,6 +1825,9 @@ export function buildMistralRetryPrep(params, recoveryModel) {
1830
1825
  }
1831
1826
  function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionId, prep, durationMs, resumable, outputFormat, warnings) {
1832
1827
  let finalStdout = stdout;
1828
+ if (cli === "codex" && outputFormat !== "json") {
1829
+ finalStdout = codexDisplayText(stdout);
1830
+ }
1833
1831
  if (optimizeResponse && outputFormat !== "json") {
1834
1832
  const optimized = optimizeResponseText(finalStdout);
1835
1833
  logOptimizationTokens("response", corrId, finalStdout, optimized);
@@ -3619,7 +3617,7 @@ export function createGatewayServer(deps = {}) {
3619
3617
  outputFormat: z
3620
3618
  .enum(["text", "json"])
3621
3619
  .default("text")
3622
- .describe("Codex output format. `json` emits --json (JSONL events) so token usage and cost are parsed and reported in the flight recorder. `text` is the default."),
3620
+ .describe("Codex caller-facing output format. Token/cache usage is recorded in the flight recorder regardless. `text` (default) returns the plain reply; `json` returns the raw `--json` JSONL event stream."),
3623
3621
  outputSchema: z
3624
3622
  .union([z.string(), z.record(z.string(), z.unknown())])
3625
3623
  .optional()
@@ -3779,7 +3777,7 @@ export function createGatewayServer(deps = {}) {
3779
3777
  logger.info(`[${corrId}] codex_request completed successfully in ${durationMs}ms`);
3780
3778
  const codexUsage = extractUsageAndCost("codex", stdout, outputFormat);
3781
3779
  safeFlightComplete(corrId, {
3782
- response: stdout,
3780
+ response: codexFrResponse(outputFormat, stdout),
3783
3781
  durationMs,
3784
3782
  retryCount: 0,
3785
3783
  circuitBreakerState: "closed",
@@ -4622,7 +4620,7 @@ export function createGatewayServer(deps = {}) {
4622
4620
  outputFormat: z
4623
4621
  .enum(["text", "json"])
4624
4622
  .default("text")
4625
- .describe("Codex output format. `json` emits --json (JSONL events) for token usage extraction."),
4623
+ .describe("Codex caller-facing output format. Token/cache usage is recorded in the flight recorder regardless. `text` (default) returns the plain reply; `json` returns the raw `--json` JSONL event stream."),
4626
4624
  outputSchema: z
4627
4625
  .union([z.string(), z.record(z.string(), z.unknown())])
4628
4626
  .optional()
@@ -5224,6 +5222,11 @@ export function createGatewayServer(deps = {}) {
5224
5222
  if (outputFormat === "stream-json" && result.stdout) {
5225
5223
  parsed = parseStreamJson(result.stdout);
5226
5224
  }
5225
+ if (asyncJobManager.getJobCli(jobId) === "codex" &&
5226
+ outputFormat !== "json" &&
5227
+ result.stdout) {
5228
+ result.stdout = codexDisplayText(result.stdout);
5229
+ }
5227
5230
  return {
5228
5231
  content: [
5229
5232
  {
package/dist/pricing.d.ts CHANGED
@@ -3,6 +3,6 @@ export interface PricePerMillion {
3
3
  outputUsd: number;
4
4
  cacheReadMultiplier: number;
5
5
  }
6
- export declare const PRICING_AS_OF = "2026-05-26";
6
+ export declare const PRICING_AS_OF = "2026-06-13";
7
7
  export declare function getPricing(cli: "claude" | "codex" | "gemini" | "grok" | "mistral", model: string): PricePerMillion;
8
8
  export declare function estimateCacheSavingsUsd(cli: "claude" | "codex" | "gemini" | "grok" | "mistral", model: string, cacheReadTokens: number): number;
package/dist/pricing.js CHANGED
@@ -1,4 +1,4 @@
1
- export const PRICING_AS_OF = "2026-05-26";
1
+ export const PRICING_AS_OF = "2026-06-13";
2
2
  const ANTHROPIC_SONNET = {
3
3
  inputUsd: 3,
4
4
  outputUsd: 15,
@@ -19,6 +19,41 @@ const OPENAI_GPT5 = {
19
19
  outputUsd: 10,
20
20
  cacheReadMultiplier: 0.5,
21
21
  };
22
+ const GEMINI_25_PRO = {
23
+ inputUsd: 1.25,
24
+ outputUsd: 10,
25
+ cacheReadMultiplier: 0.1,
26
+ };
27
+ const GEMINI_FLASH = {
28
+ inputUsd: 0.3,
29
+ outputUsd: 2.5,
30
+ cacheReadMultiplier: 0.1,
31
+ };
32
+ const GEMINI_3_PRO = {
33
+ inputUsd: 2,
34
+ outputUsd: 12,
35
+ cacheReadMultiplier: 0.1,
36
+ };
37
+ const GROK_4 = {
38
+ inputUsd: 1.25,
39
+ outputUsd: 2.5,
40
+ cacheReadMultiplier: 0.16,
41
+ };
42
+ const GROK_BUILD = {
43
+ inputUsd: 1,
44
+ outputUsd: 2,
45
+ cacheReadMultiplier: 0.2,
46
+ };
47
+ const MISTRAL_MEDIUM = {
48
+ inputUsd: 1.5,
49
+ outputUsd: 7.5,
50
+ cacheReadMultiplier: 0.1,
51
+ };
52
+ const MISTRAL_DEVSTRAL = {
53
+ inputUsd: 0.1,
54
+ outputUsd: 0.3,
55
+ cacheReadMultiplier: 0.1,
56
+ };
22
57
  const ZERO = {
23
58
  inputUsd: 0,
24
59
  outputUsd: 0,
@@ -33,13 +68,43 @@ export function getPricing(cli, model) {
33
68
  return ANTHROPIC_OPUS;
34
69
  if (lower.includes("haiku"))
35
70
  return ANTHROPIC_HAIKU;
36
- return ZERO;
71
+ return ANTHROPIC_SONNET;
37
72
  }
38
73
  if (cli === "codex") {
39
74
  if (lower.includes("gpt-5") || lower.includes("o3"))
40
75
  return OPENAI_GPT5;
41
76
  return ZERO;
42
77
  }
78
+ if (cli === "gemini") {
79
+ const specialtyVariant = lower.includes("lite") ||
80
+ lower.includes("image") ||
81
+ lower.includes("audio") ||
82
+ lower.includes("tts");
83
+ if (!specialtyVariant) {
84
+ if (lower.includes("2.5-flash"))
85
+ return GEMINI_FLASH;
86
+ if (lower.includes("2.5-pro"))
87
+ return GEMINI_25_PRO;
88
+ if (lower.includes("gemini-3") && lower.includes("pro"))
89
+ return GEMINI_3_PRO;
90
+ }
91
+ return ZERO;
92
+ }
93
+ if (cli === "grok") {
94
+ if (lower.includes("grok-build") || lower.includes("grok-code"))
95
+ return GROK_BUILD;
96
+ if (lower.includes("grok-4") || lower.includes("grok-3") || lower.includes("grok-latest")) {
97
+ return GROK_4;
98
+ }
99
+ return ZERO;
100
+ }
101
+ if (cli === "mistral") {
102
+ if (lower.includes("devstral-small"))
103
+ return MISTRAL_DEVSTRAL;
104
+ if (lower.includes("mistral-medium-3.5"))
105
+ return MISTRAL_MEDIUM;
106
+ return ZERO;
107
+ }
43
108
  return ZERO;
44
109
  }
45
110
  export function estimateCacheSavingsUsd(cli, model, cacheReadTokens) {
@@ -46,6 +46,23 @@ export interface ProviderFeatureCapability {
46
46
  details?: string;
47
47
  values?: string[];
48
48
  }
49
+ export type ProviderAcpStatus = "native_smoke_passed" | "native_candidate" | "adapter_mediated_deferred" | "absent_watchlist" | "not_applicable";
50
+ export type ProviderAcpMediation = "native" | "adapter_mediated" | "none";
51
+ export type ProviderAcpSmokeStatus = "passed" | "not_run" | "unsupported";
52
+ export interface ProviderAcpCapability {
53
+ status: ProviderAcpStatus;
54
+ mediation: ProviderAcpMediation;
55
+ targetVersion: string;
56
+ entrypoint: {
57
+ command: string;
58
+ args: string[];
59
+ } | null;
60
+ runtimeEnabled: boolean;
61
+ smokeSupported: boolean;
62
+ smokeStatus: ProviderAcpSmokeStatus;
63
+ caveats: string[];
64
+ docs: string;
65
+ }
49
66
  export type ProviderFeatureMap = Record<string, ProviderFeatureCapability>;
50
67
  export interface ProviderCapabilityControls {
51
68
  allowlist: ProviderToolControl;
@@ -54,6 +71,25 @@ export interface ProviderCapabilityControls {
54
71
  nativeSkills: ProviderToolControl;
55
72
  [name: string]: ProviderToolControl;
56
73
  }
74
+ export type AcpFrozenClassification = "native_candidate" | "adapter_mediated_deferred" | "absent_watchlist";
75
+ export interface AcpProviderContract {
76
+ classification: AcpFrozenClassification;
77
+ summary: string;
78
+ }
79
+ export interface AcpContractMetadata {
80
+ protocol: "Agent Client Protocol";
81
+ outOfScope: "Agent Communication Protocol";
82
+ mcpFrontendRemains: true;
83
+ acpIsInternalProviderTransport: true;
84
+ defaultTransport: "cli";
85
+ hostServicesDenyByDefault: true;
86
+ noRawAcpJsonRpcTool: true;
87
+ adapterSupportIsNotNative: true;
88
+ contractDoc: "docs/acp-contract.md";
89
+ nonGoals: readonly string[];
90
+ providers: Readonly<Record<ProviderCapabilityId, AcpProviderContract>>;
91
+ }
92
+ export declare const ACP_CONTRACT: AcpContractMetadata;
57
93
  export interface ProviderToolCapabilities {
58
94
  schemaVersion: "provider-tool-capabilities.v2";
59
95
  generatedAt: string;
@@ -62,6 +98,8 @@ export interface ProviderToolCapabilities {
62
98
  gatewayRequestTools: string[];
63
99
  modelInfo: CliInfo | GrokApiModelInfo;
64
100
  summary: string;
101
+ acpContract: AcpProviderContract;
102
+ acp: ProviderAcpCapability;
65
103
  controls: ProviderCapabilityControls;
66
104
  features: ProviderFeatureMap;
67
105
  discoveredSkills: ProviderSkillCapability[];
@@ -11,6 +11,146 @@ const MAX_SKILL_BYTES = 64 * 1024;
11
11
  const MAX_CONFIG_BYTES = 128 * 1024;
12
12
  const MAX_PROVIDER_TOOLS_PER_SKILL = 50;
13
13
  const CAPABILITY_CACHE_TTL_MS = 60 * 1000;
14
+ export const ACP_CONTRACT = {
15
+ protocol: "Agent Client Protocol",
16
+ outOfScope: "Agent Communication Protocol",
17
+ mcpFrontendRemains: true,
18
+ acpIsInternalProviderTransport: true,
19
+ defaultTransport: "cli",
20
+ hostServicesDenyByDefault: true,
21
+ noRawAcpJsonRpcTool: true,
22
+ adapterSupportIsNotNative: true,
23
+ contractDoc: "docs/acp-contract.md",
24
+ nonGoals: [
25
+ "Replace the MCP server.",
26
+ "Ship an outbound ACP server or frontend in this slice.",
27
+ "Wrap every provider immediately.",
28
+ "Run adapter-mediated providers by default.",
29
+ "Grant write or terminal HostServices by default.",
30
+ "Expose raw ACP JSON-RPC to agents.",
31
+ "Implement any agent-to-agent Agent Communication Protocol layer.",
32
+ ],
33
+ providers: {
34
+ mistral: {
35
+ classification: "native_candidate",
36
+ summary: "Mistral Vibe exposes native ACP via vibe-acp; first runtime pilot candidate.",
37
+ },
38
+ grok: {
39
+ classification: "native_candidate",
40
+ summary: "xAI Grok CLI exposes native ACP via grok agent stdio; second runtime pilot candidate.",
41
+ },
42
+ codex: {
43
+ classification: "adapter_mediated_deferred",
44
+ summary: "OpenAI Codex CLI has no native ACP entrypoint at the target version; adapter-mediated and deferred.",
45
+ },
46
+ claude: {
47
+ classification: "adapter_mediated_deferred",
48
+ summary: "Anthropic Claude Code has no native ACP entrypoint at the target version; adapter-mediated and deferred.",
49
+ },
50
+ gemini: {
51
+ classification: "absent_watchlist",
52
+ summary: "Google Antigravity agy 1.0.7 has no ACP surface; watchlist item only.",
53
+ },
54
+ grok_api: {
55
+ classification: "absent_watchlist",
56
+ summary: "Grok API is an HTTP provider with no ACP process transport; watchlist item only.",
57
+ },
58
+ },
59
+ };
60
+ const ACP_DOCS_REFERENCE = "docs/plans/first-class-acp-gateway-extension.dag.toml";
61
+ const ACP_CAPABILITIES = {
62
+ mistral: {
63
+ status: "native_smoke_passed",
64
+ mediation: "native",
65
+ targetVersion: "vibe 2.14.1",
66
+ entrypoint: { command: "vibe-acp", args: [] },
67
+ runtimeEnabled: false,
68
+ smokeSupported: true,
69
+ smokeStatus: "passed",
70
+ caveats: [
71
+ "Native ACP via the provider-scoped vibe-acp executable; first runtime pilot.",
72
+ "Runtime routing stays disabled until ACP is enabled in gateway config.",
73
+ ],
74
+ docs: ACP_DOCS_REFERENCE,
75
+ },
76
+ grok: {
77
+ status: "native_smoke_passed",
78
+ mediation: "native",
79
+ targetVersion: "grok 0.2.50 (cadf94855)",
80
+ entrypoint: { command: "grok", args: ["agent", "stdio"] },
81
+ runtimeEnabled: false,
82
+ smokeSupported: true,
83
+ smokeStatus: "passed",
84
+ caveats: [
85
+ "Native ACP via grok agent stdio; second runtime pilot.",
86
+ "Credential lookup is owned by the installed CLI; empty-env smoke is not expected to pass.",
87
+ "Runtime routing stays disabled until ACP is enabled in gateway config.",
88
+ ],
89
+ docs: ACP_DOCS_REFERENCE,
90
+ },
91
+ codex: {
92
+ status: "adapter_mediated_deferred",
93
+ mediation: "adapter_mediated",
94
+ targetVersion: "codex-cli 0.139.0",
95
+ entrypoint: null,
96
+ runtimeEnabled: false,
97
+ smokeSupported: false,
98
+ smokeStatus: "unsupported",
99
+ caveats: [
100
+ "No native ACP entrypoint at the target version; ACP would be adapter-mediated.",
101
+ "Adapter support requires a separate threat model and is never labelled native gateway ACP support.",
102
+ ],
103
+ docs: ACP_DOCS_REFERENCE,
104
+ },
105
+ claude: {
106
+ status: "adapter_mediated_deferred",
107
+ mediation: "adapter_mediated",
108
+ targetVersion: "claude 2.1.175",
109
+ entrypoint: null,
110
+ runtimeEnabled: false,
111
+ smokeSupported: false,
112
+ smokeStatus: "unsupported",
113
+ caveats: [
114
+ "No native Claude Code CLI ACP entrypoint at the target version; ACP would be adapter-mediated.",
115
+ "Adapter ownership, permission bridging, and install story must be specified before runtime support.",
116
+ ],
117
+ docs: ACP_DOCS_REFERENCE,
118
+ },
119
+ gemini: {
120
+ status: "absent_watchlist",
121
+ mediation: "none",
122
+ targetVersion: "agy 1.0.7",
123
+ entrypoint: null,
124
+ runtimeEnabled: false,
125
+ smokeSupported: false,
126
+ smokeStatus: "unsupported",
127
+ caveats: [
128
+ "Antigravity agy 1.0.7 has no ACP flag or subcommand.",
129
+ "Legacy Gemini CLI ACP evidence does not transfer to agy; kept on the upstream drift watchlist.",
130
+ ],
131
+ docs: ACP_DOCS_REFERENCE,
132
+ },
133
+ grok_api: {
134
+ status: "not_applicable",
135
+ mediation: "none",
136
+ targetVersion: "xAI Responses API",
137
+ entrypoint: null,
138
+ runtimeEnabled: false,
139
+ smokeSupported: false,
140
+ smokeStatus: "unsupported",
141
+ caveats: ["ACP is a CLI-stdio transport; the HTTP API provider has no ACP surface."],
142
+ docs: ACP_DOCS_REFERENCE,
143
+ },
144
+ };
145
+ function cloneAcpCapability(acp) {
146
+ return {
147
+ ...acp,
148
+ entrypoint: acp.entrypoint
149
+ ? { command: acp.entrypoint.command, args: [...acp.entrypoint.args] }
150
+ : null,
151
+ caveats: [...acp.caveats],
152
+ };
153
+ }
14
154
  const PROVIDER_CAPABILITY_IDS = [...CLI_TYPES, "grok_api"];
15
155
  const KNOWN_PROVIDER_TOOLS = {
16
156
  grok: [
@@ -640,6 +780,8 @@ function buildOneProviderToolCapabilities(cli, query) {
640
780
  gatewayRequestTool: gatewayRequestTools[0] ?? definition.gatewayRequestTools[0],
641
781
  modelInfo: getModelInfo(cli, query.refresh),
642
782
  summary: definition.summary,
783
+ acpContract: { ...ACP_CONTRACT.providers[cli] },
784
+ acp: cloneAcpCapability(ACP_CAPABILITIES[cli]),
643
785
  controls: cloneControls(definition.controls),
644
786
  features,
645
787
  discoveredSkills,
@@ -1,4 +1,5 @@
1
1
  export interface GatewayRequestContext {
2
+ transport?: "stdio" | "http";
2
3
  authKind?: "disabled" | "gateway_bearer" | "oauth";
3
4
  authScopes: string[];
4
5
  authClientId?: string;
@@ -98,8 +98,8 @@ export declare const CLAUDE_HIGH_IMPACT_PARAMS_SCHEMA: z.ZodEffects<z.ZodObject<
98
98
  effort: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "xhigh", "max"]>>;
99
99
  excludeDynamicSystemPromptSections: z.ZodOptional<z.ZodBoolean>;
100
100
  }, "strip", z.ZodTypeAny, {
101
- agents?: Record<string, Record<string, unknown>> | undefined;
102
101
  agent?: string | undefined;
102
+ agents?: Record<string, Record<string, unknown>> | undefined;
103
103
  forkSession?: boolean | undefined;
104
104
  systemPrompt?: string | undefined;
105
105
  appendSystemPrompt?: string | undefined;
@@ -108,8 +108,8 @@ export declare const CLAUDE_HIGH_IMPACT_PARAMS_SCHEMA: z.ZodEffects<z.ZodObject<
108
108
  effort?: "medium" | "low" | "high" | "xhigh" | "max" | undefined;
109
109
  excludeDynamicSystemPromptSections?: boolean | undefined;
110
110
  }, {
111
- agents?: Record<string, Record<string, unknown>> | undefined;
112
111
  agent?: string | undefined;
112
+ agents?: Record<string, Record<string, unknown>> | undefined;
113
113
  forkSession?: boolean | undefined;
114
114
  systemPrompt?: string | undefined;
115
115
  appendSystemPrompt?: string | undefined;
@@ -118,8 +118,8 @@ export declare const CLAUDE_HIGH_IMPACT_PARAMS_SCHEMA: z.ZodEffects<z.ZodObject<
118
118
  effort?: "medium" | "low" | "high" | "xhigh" | "max" | undefined;
119
119
  excludeDynamicSystemPromptSections?: boolean | undefined;
120
120
  }>, {
121
- agents?: Record<string, Record<string, unknown>> | undefined;
122
121
  agent?: string | undefined;
122
+ agents?: Record<string, Record<string, unknown>> | undefined;
123
123
  forkSession?: boolean | undefined;
124
124
  systemPrompt?: string | undefined;
125
125
  appendSystemPrompt?: string | undefined;
@@ -128,8 +128,8 @@ export declare const CLAUDE_HIGH_IMPACT_PARAMS_SCHEMA: z.ZodEffects<z.ZodObject<
128
128
  effort?: "medium" | "low" | "high" | "xhigh" | "max" | undefined;
129
129
  excludeDynamicSystemPromptSections?: boolean | undefined;
130
130
  }, {
131
- agents?: Record<string, Record<string, unknown>> | undefined;
132
131
  agent?: string | undefined;
132
+ agents?: Record<string, Record<string, unknown>> | undefined;
133
133
  forkSession?: boolean | undefined;
134
134
  systemPrompt?: string | undefined;
135
135
  appendSystemPrompt?: string | undefined;
@@ -107,6 +107,20 @@ export interface ProviderSubcommandCompactCatalog {
107
107
  ];
108
108
  rows: readonly (readonly string[])[];
109
109
  }
110
+ export type AcpEntrypointStatus = "native" | "adapter_mediated_deferred" | "absent_watchlist";
111
+ export interface AcpEntrypointContract {
112
+ cli: CliType;
113
+ displayName: string;
114
+ status: AcpEntrypointStatus;
115
+ executable: string;
116
+ entrypointArgs: readonly string[];
117
+ targetVersion: string;
118
+ probeArgs: readonly (readonly string[])[];
119
+ adapterCandidates?: readonly string[];
120
+ evidence: string;
121
+ docsRef: string;
122
+ }
123
+ export declare const ACP_ENTRYPOINT_CONTRACTS: Record<CliType, AcpEntrypointContract>;
110
124
  export declare const UPSTREAM_CLI_CONTRACTS: Record<CliType, CliContract>;
111
125
  export declare function validateUpstreamCliArgs(cli: CliType, args: readonly string[]): ContractValidationResult;
112
126
  export declare function assertUpstreamCliArgs(cli: CliType, args: readonly string[]): void;
@@ -167,6 +181,19 @@ export interface InstalledCliContractProbe {
167
181
  warnings: string[];
168
182
  }
169
183
  export declare function probeInstalledCliContract(cli: CliType, timeoutMs?: number): InstalledCliContractProbe;
184
+ export interface InstalledAcpEntrypointProbe {
185
+ cli: CliType;
186
+ status: AcpEntrypointStatus;
187
+ executable: string;
188
+ entrypointArgs: readonly string[];
189
+ targetVersion: string;
190
+ checkedProbeCommands: readonly (readonly string[])[];
191
+ available: boolean | null;
192
+ entrypointDrift: boolean;
193
+ warnings: string[];
194
+ probedAt: string;
195
+ }
196
+ export declare function probeInstalledAcpEntrypoint(cli: CliType, timeoutMs?: number): InstalledAcpEntrypointProbe;
170
197
  export declare function buildUpstreamContractReport(options?: {
171
198
  cli?: CliType;
172
199
  probeInstalled?: boolean;