llm-cli-gateway 2.13.2 → 2.14.0-rc.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +53 -26
  3. package/dist/acp/client.d.ts +29 -1
  4. package/dist/acp/client.js +78 -4
  5. package/dist/acp/errors.d.ts +9 -1
  6. package/dist/acp/errors.js +19 -0
  7. package/dist/acp/event-normalizer.d.ts +12 -0
  8. package/dist/acp/event-normalizer.js +16 -0
  9. package/dist/acp/flight-redaction.d.ts +3 -0
  10. package/dist/acp/flight-redaction.js +3 -0
  11. package/dist/acp/permission-bridge.js +11 -5
  12. package/dist/acp/process-manager.d.ts +2 -1
  13. package/dist/acp/process-manager.js +43 -4
  14. package/dist/acp/provider-registry.js +19 -11
  15. package/dist/acp/runtime.d.ts +8 -0
  16. package/dist/acp/runtime.js +47 -4
  17. package/dist/acp/types.d.ts +3083 -55
  18. package/dist/acp/types.js +242 -5
  19. package/dist/async-job-manager.d.ts +2 -0
  20. package/dist/async-job-manager.js +11 -0
  21. package/dist/codex-json-parser.d.ts +1 -0
  22. package/dist/codex-json-parser.js +6 -0
  23. package/dist/config.d.ts +8 -0
  24. package/dist/config.js +47 -0
  25. package/dist/flight-recorder.d.ts +2 -0
  26. package/dist/flight-recorder.js +20 -0
  27. package/dist/gemini-json-parser.d.ts +2 -0
  28. package/dist/gemini-json-parser.js +45 -8
  29. package/dist/grok-json-parser.d.ts +14 -0
  30. package/dist/grok-json-parser.js +156 -0
  31. package/dist/index.d.ts +47 -2
  32. package/dist/index.js +484 -119
  33. package/dist/job-store.d.ts +45 -7
  34. package/dist/job-store.js +138 -17
  35. package/dist/model-registry.d.ts +1 -0
  36. package/dist/model-registry.js +46 -0
  37. package/dist/oauth.js +2 -2
  38. package/dist/postgres-job-store-worker.d.ts +1 -0
  39. package/dist/postgres-job-store-worker.js +327 -0
  40. package/dist/pricing.d.ts +3 -2
  41. package/dist/provider-acp-capabilities.d.ts +52 -0
  42. package/dist/provider-acp-capabilities.js +101 -0
  43. package/dist/provider-admin-tools.d.ts +100 -0
  44. package/dist/provider-admin-tools.js +572 -0
  45. package/dist/provider-capability-cache.d.ts +46 -0
  46. package/dist/provider-capability-cache.js +248 -0
  47. package/dist/provider-capability-discovery.d.ts +85 -0
  48. package/dist/provider-capability-discovery.js +461 -0
  49. package/dist/provider-capability-resolver.d.ts +29 -0
  50. package/dist/provider-capability-resolver.js +92 -0
  51. package/dist/provider-definition-assertions.d.ts +9 -0
  52. package/dist/provider-definition-assertions.js +147 -0
  53. package/dist/provider-definitions.d.ts +127 -0
  54. package/dist/provider-definitions.js +758 -0
  55. package/dist/provider-help-parser.d.ts +34 -0
  56. package/dist/provider-help-parser.js +203 -0
  57. package/dist/provider-model-discovery.d.ts +30 -0
  58. package/dist/provider-model-discovery.js +229 -0
  59. package/dist/provider-output-metadata.d.ts +7 -0
  60. package/dist/provider-output-metadata.js +68 -0
  61. package/dist/provider-schema-builder.d.ts +22 -0
  62. package/dist/provider-schema-builder.js +55 -0
  63. package/dist/provider-surface-generator.d.ts +98 -0
  64. package/dist/provider-surface-generator.js +140 -0
  65. package/dist/provider-tool-capabilities.d.ts +3 -2
  66. package/dist/provider-tool-capabilities.js +77 -62
  67. package/dist/request-helpers.d.ts +37 -4
  68. package/dist/request-helpers.js +134 -0
  69. package/dist/resources.d.ts +6 -1
  70. package/dist/resources.js +64 -192
  71. package/dist/session-manager.js +18 -11
  72. package/dist/stream-json-parser.d.ts +1 -0
  73. package/dist/stream-json-parser.js +3 -0
  74. package/dist/upstream-contracts.d.ts +17 -1
  75. package/dist/upstream-contracts.js +209 -36
  76. package/npm-shrinkwrap.json +2 -2
  77. package/package.json +8 -3
package/dist/index.js CHANGED
@@ -10,15 +10,18 @@ import { z } from "zod/v3";
10
10
  import { executeCli, killAllProcessGroups, providerCommandName } from "./executor.js";
11
11
  import { parseStreamJson } from "./stream-json-parser.js";
12
12
  import { parseCodexJsonStream, codexDisplayText, codexFrResponse } from "./codex-json-parser.js";
13
+ import { grokDisplayText } from "./grok-json-parser.js";
14
+ import { extractProviderOutputMetadata } from "./provider-output-metadata.js";
13
15
  import { parseGeminiJson, parseGeminiStreamJson } from "./gemini-json-parser.js";
14
16
  import { parseVibeMetaJson } from "./mistral-meta-json-parser.js";
15
17
  import { homedir } from "os";
16
18
  import { CLI_TYPES, PROVIDER_TYPES, createSessionManager, callerIsRemote, remoteSafeSession, } from "./session-manager.js";
17
19
  import { createWorktree, createWorktreeSessionCleanupHook, } from "./worktree-manager.js";
18
20
  import { ResourceProvider } from "./resources.js";
21
+ import { generateResourceDescriptors, generateProviderAcpDescriptors, } from "./provider-surface-generator.js";
19
22
  import { PerformanceMetrics } from "./metrics.js";
20
23
  import { estimateTokens, optimizePrompt as optimizePromptText, optimizeResponse as optimizeResponseText, } from "./optimizer.js";
21
- import { loadConfig, loadPersistenceConfig, loadCacheAwarenessConfig, loadProvidersConfig, loadAcpConfig, loadLimitsConfig, defaultGatewayConfigPath, isXaiProviderEnabled, enabledApiProviders, minStableTokensForModel, } from "./config.js";
24
+ import { loadConfig, loadPersistenceConfig, loadCacheAwarenessConfig, loadProvidersConfig, loadAcpConfig, loadAdminConfig, loadLimitsConfig, defaultGatewayConfigPath, isXaiProviderEnabled, enabledApiProviders, minStableTokensForModel, } from "./config.js";
22
25
  import { runAcpRequest } from "./acp/runtime.js";
23
26
  import { isAcpError } from "./acp/errors.js";
24
27
  import { redactSecrets } from "./secret-redaction.js";
@@ -27,12 +30,15 @@ import { prepareApiRequest, apiProviderCatalogEntry, ApiModelNotAllowedError, }
27
30
  import { checkHealth } from "./health.js";
28
31
  import { clearModelRegistryCache, getAvailableCliInfo, getCliInfo, resolveModelAlias, } from "./model-registry.js";
29
32
  import { getProviderToolCapabilities } from "./provider-tool-capabilities.js";
33
+ import { getProviderDefinition, DEVIN_ACP_AGENT_TYPES, } from "./provider-definitions.js";
34
+ import { buildProviderDiscoveredView, peekProviderCapabilitySet, warmProviderCapabilities, } from "./provider-capability-resolver.js";
35
+ import { registerProviderAdminTools } from "./provider-admin-tools.js";
30
36
  import { AsyncJobManager, JobSaturationError, isAsyncJobInProgress, extractApiHttpStatus, extractApiErrorBody, } from "./async-job-manager.js";
31
37
  import { createJobStore } from "./job-store.js";
32
38
  import { ApprovalManager, bypassAllowedByOperator, } from "./approval-manager.js";
33
39
  import { checkReviewIntegrity } from "./review-integrity.js";
34
40
  import { buildClaudeMcpConfig, CLAUDE_MCP_SERVER_NAMES, } from "./claude-mcp-config.js";
35
- import { resolveGrokSessionArgs, resolveMistralSessionArgs, resolveCodexSessionArgs, sanitizeCliArgValues, prepareMistralRequest as buildMistralCliInvocation, GATEWAY_SESSION_PREFIX, resolveClaudePermissionFlags, resolveCodexSandboxFlags, CLAUDE_PERMISSION_MODES, GEMINI_APPROVAL_MODES, CODEX_SANDBOX_MODES, CODEX_ASK_FOR_APPROVAL_MODES, CLAUDE_EFFORT_LEVELS, prepareClaudeHighImpactFlags, validateClaudeAgentsMap, prepareCodexHighImpactFlags, prepareCodexForkRequest, CODEX_CONFIG_OVERRIDES_SCHEMA, resolveGeminiSessionPlan, GEMINI_HIGH_IMPACT_PARAMS_SCHEMA, } from "./request-helpers.js";
41
+ import { resolveGrokSessionArgs, resolveMistralSessionArgs, resolveCodexSessionArgs, sanitizeCliArgValues, prepareMistralRequest as buildMistralCliInvocation, GATEWAY_SESSION_PREFIX, resolveClaudePermissionFlags, resolveCodexSandboxFlags, CLAUDE_PERMISSION_MODES, GEMINI_APPROVAL_MODES, CODEX_SANDBOX_MODES, CODEX_ASK_FOR_APPROVAL_MODES, CODEX_LOCAL_PROVIDERS, CODEX_COLOR_MODES, CLAUDE_EFFORT_LEVELS, prepareClaudeHighImpactFlags, validateClaudeAgentsMap, prepareCodexHighImpactFlags, prepareCodexForkRequest, CODEX_CONFIG_OVERRIDES_SCHEMA, resolveGeminiSessionPlan, GEMINI_HIGH_IMPACT_PARAMS_SCHEMA, } from "./request-helpers.js";
36
42
  import { createFlightRecorder } from "./flight-recorder.js";
37
43
  import { resolvePromptInput, PromptPartsSchema, assembleClaudeCacheBlocks, } from "./prompt-parts.js";
38
44
  import { computeSessionCacheStats, computeTtlRemaining, readPersistedRequest, PERSISTED_REQUEST_DEFAULT_MAX_CHARS, } from "./cache-stats.js";
@@ -237,6 +243,7 @@ let persistenceConfig = null;
237
243
  let cacheAwarenessConfig = null;
238
244
  let providersConfig = null;
239
245
  let acpConfig = null;
246
+ let adminConfig = null;
240
247
  let limitsConfig = null;
241
248
  let jobStore = null;
242
249
  let jobStoreInitialized = false;
@@ -258,6 +265,10 @@ function getAcpConfig(runtimeLogger = logger) {
258
265
  acpConfig ??= loadAcpConfig(runtimeLogger);
259
266
  return acpConfig;
260
267
  }
268
+ function getAdminConfig(runtimeLogger = logger) {
269
+ adminConfig ??= loadAdminConfig(runtimeLogger);
270
+ return adminConfig;
271
+ }
261
272
  function getCacheAwarenessConfig(runtimeLogger = logger) {
262
273
  cacheAwarenessConfig ??= loadCacheAwarenessConfig(runtimeLogger);
263
274
  return cacheAwarenessConfig;
@@ -299,6 +310,91 @@ function mcpServerEnum() {
299
310
  }
300
311
  const CLI_TYPE_ENUM = z.enum(CLI_TYPES);
301
312
  export const MAX_TURNS_SCHEMA = z.number().int().positive().safe().max(10_000);
313
+ const CLAUDE_PART_A_FIELDS = {
314
+ includeHookEvents: z
315
+ .boolean()
316
+ .optional()
317
+ .describe("Claude --include-hook-events: include all hook lifecycle events in the output stream. Only takes effect with outputFormat=stream-json (the default)."),
318
+ replayUserMessages: z
319
+ .boolean()
320
+ .optional()
321
+ .describe("Claude --replay-user-messages: re-emit user messages from stdin back on stdout for acknowledgment. Only works with input-format=stream-json and outputFormat=stream-json (the cacheControl path)."),
322
+ systemPromptFile: z
323
+ .string()
324
+ .min(1)
325
+ .optional()
326
+ .describe("Claude --system-prompt-file: replace the system prompt from a file path (path variant of systemPrompt)."),
327
+ appendSystemPromptFile: z
328
+ .string()
329
+ .min(1)
330
+ .optional()
331
+ .describe("Claude --append-system-prompt-file: append a system prompt from a file path (path variant of appendSystemPrompt)."),
332
+ name: z
333
+ .string()
334
+ .min(1)
335
+ .optional()
336
+ .describe("Claude --name: display name for this session (shown in pickers/titles)."),
337
+ pluginDir: z
338
+ .array(z.string())
339
+ .optional()
340
+ .describe("Claude --plugin-dir: load a plugin from a directory or .zip for this session only. One --plugin-dir instance per entry."),
341
+ pluginUrl: z
342
+ .array(z.string())
343
+ .optional()
344
+ .describe("Claude --plugin-url: load a plugin .zip from a URL for this session only. One --plugin-url instance per entry."),
345
+ safeMode: z
346
+ .boolean()
347
+ .optional()
348
+ .describe("Claude --safe-mode: start with all customizations (CLAUDE.md, skills, plugins, hooks, MCP, commands, agents) disabled for troubleshooting. Explicit opt-in."),
349
+ bare: z
350
+ .boolean()
351
+ .optional()
352
+ .describe("Claude --bare: minimal mode (skip hooks, LSP, plugin sync, attribution, auto-memory, keychain reads, CLAUDE.md auto-discovery). Explicit opt-in."),
353
+ debug: z
354
+ .union([z.string(), z.boolean()])
355
+ .optional()
356
+ .describe('Claude -d/--debug: enable debug mode. true emits a bare --debug; a string emits --debug <filter> (e.g. "api,hooks"). Debug output goes to stderr only.'),
357
+ debugFile: z
358
+ .string()
359
+ .min(1)
360
+ .optional()
361
+ .describe("Claude --debug-file: write debug logs to a specific file path (enables debug mode)."),
362
+ };
363
+ const CODEX_PART_A_FIELDS = {
364
+ enable: z
365
+ .array(z.string())
366
+ .optional()
367
+ .describe("Codex --enable <FEATURE> (repeatable): enable a feature for this run (equivalent to -c features.<name>=true). Accepted on resume."),
368
+ disable: z
369
+ .array(z.string())
370
+ .optional()
371
+ .describe("Codex --disable <FEATURE> (repeatable): disable a feature for this run (equivalent to -c features.<name>=false). Accepted on resume."),
372
+ strictConfig: z
373
+ .boolean()
374
+ .optional()
375
+ .describe("Codex --strict-config: error out when config.toml contains fields not recognized by this Codex version. New sessions only."),
376
+ oss: z
377
+ .boolean()
378
+ .optional()
379
+ .describe("Codex --oss: use the open-source provider. New sessions only."),
380
+ localProvider: z
381
+ .enum(CODEX_LOCAL_PROVIDERS)
382
+ .optional()
383
+ .describe("Codex --local-provider: local OSS provider (lmstudio|ollama), used with oss. New sessions only."),
384
+ color: z
385
+ .enum(CODEX_COLOR_MODES)
386
+ .optional()
387
+ .describe("Codex --color: output color mode (always|never|auto). New sessions only."),
388
+ outputLastMessage: z
389
+ .string()
390
+ .min(1)
391
+ .optional()
392
+ .describe("Codex -o/--output-last-message <FILE>: write the agent's last message to a file. New sessions only."),
393
+ dangerouslyBypassHookTrust: z
394
+ .boolean()
395
+ .optional()
396
+ .describe("Codex --dangerously-bypass-hook-trust: run enabled hooks without persisted hook trust for this invocation. DANGEROUS. Explicit opt-in; never defaulted on."),
397
+ };
302
398
  const GROK_GENERATED_SHAPE = deriveZodShapeFromGeneration(UPSTREAM_CLI_CONTRACTS.grok, GROK_FLAG_GENERATION);
303
399
  export const MAX_TOKENS_SCHEMA = z.number().int().positive().safe().max(100_000_000);
304
400
  export const MAX_PRICE_SCHEMA = z.number().positive().finite().min(1e-6).max(10_000);
@@ -367,7 +463,7 @@ export function resolveGatewayServerRuntime(deps = {}, options = {}) {
367
463
  sessionManager: runtimeSessionManager,
368
464
  resourceProvider: deps.resourceProvider ??
369
465
  (options.isolateState
370
- ? new ResourceProvider(runtimeSessionManager, runtimePerformanceMetrics, runtimeFlightRecorder, deps.cacheAwareness ?? getCacheAwarenessConfig(runtimeLogger), deps.providers ?? getProvidersConfig(runtimeLogger))
466
+ ? new ResourceProvider(runtimeSessionManager, runtimePerformanceMetrics, runtimeFlightRecorder, deps.cacheAwareness ?? getCacheAwarenessConfig(runtimeLogger), deps.providers ?? getProvidersConfig(runtimeLogger), undefined, deps.acpConfig ?? getAcpConfig(runtimeLogger))
371
467
  : resourceProvider),
372
468
  db: "db" in deps ? (deps.db ?? null) : db,
373
469
  performanceMetrics: runtimePerformanceMetrics,
@@ -379,6 +475,7 @@ export function resolveGatewayServerRuntime(deps = {}, options = {}) {
379
475
  cacheAwareness: deps.cacheAwareness ?? getCacheAwarenessConfig(runtimeLogger),
380
476
  providers: deps.providers ?? getProvidersConfig(runtimeLogger),
381
477
  acpConfig: deps.acpConfig ?? getAcpConfig(runtimeLogger),
478
+ adminConfig: deps.adminConfig ?? getAdminConfig(runtimeLogger),
382
479
  workspaces: deps.workspaces ?? loadWorkspaceRegistry(runtimeLogger),
383
480
  };
384
481
  }
@@ -419,12 +516,28 @@ export async function runAcpTransport(deps, params) {
419
516
  model: params.model,
420
517
  sessionId: params.sessionId,
421
518
  correlationId: corrId,
519
+ agentType: params.agentType,
422
520
  });
521
+ const stop = result.stopReason;
522
+ const normalStop = stop === null || stop === "end_turn";
523
+ let warning = null;
524
+ if (!normalStop) {
525
+ warning =
526
+ `provider ended the turn with stop_reason=${stop}` +
527
+ (result.text.trim().length === 0 ? " and produced no output" : "") +
528
+ "; the response may be partial or refused.";
529
+ }
530
+ else if (result.text.trim().length === 0) {
531
+ warning =
532
+ "provider produced no assistant output (possible no-op, guardrail, or awaited input).";
533
+ }
534
+ const banner = `[gateway] transport=acp session=${result.gatewaySessionId}` +
535
+ (warning ? `\n[gateway] warning: ${warning}` : "");
423
536
  return {
424
537
  content: [
425
538
  {
426
539
  type: "text",
427
- text: `[gateway] transport=acp session=${result.gatewaySessionId}\n${result.text}`,
540
+ text: `${banner}\n${result.text}`,
428
541
  },
429
542
  ],
430
543
  };
@@ -1153,7 +1266,7 @@ function resolveClaudeMcpConfig(operation, correlationId, requestedMcpServers, s
1153
1266
  }
1154
1267
  return { config: mcpConfig };
1155
1268
  }
1156
- function registerBaseResources(server, runtime) {
1269
+ export function registerBaseResources(server, runtime) {
1157
1270
  for (const skill of loadedSkills) {
1158
1271
  server.registerResource(`skill-${skill.name}`, `skills://${skill.name}`, {
1159
1272
  title: skill.name,
@@ -1179,96 +1292,30 @@ function registerBaseResources(server, runtime) {
1179
1292
  const contents = await runtime.resourceProvider.readResource(uri.href);
1180
1293
  return { contents: contents ? [contents] : [] };
1181
1294
  });
1182
- server.registerResource("claude-sessions", "sessions://claude", {
1183
- title: "🤖 Claude Sessions",
1184
- description: "Claude conversation sessions",
1185
- mimeType: "application/json",
1186
- }, async (uri) => {
1187
- runtime.logger.debug("Reading Claude sessions resource");
1188
- const contents = await runtime.resourceProvider.readResource(uri.href);
1189
- return { contents: contents ? [contents] : [] };
1190
- });
1191
- server.registerResource("codex-sessions", "sessions://codex", {
1192
- title: "💻 Codex Sessions",
1193
- description: "Codex conversation sessions",
1194
- mimeType: "application/json",
1195
- }, async (uri) => {
1196
- runtime.logger.debug("Reading Codex sessions resource");
1197
- const contents = await runtime.resourceProvider.readResource(uri.href);
1198
- return { contents: contents ? [contents] : [] };
1199
- });
1200
- server.registerResource("gemini-sessions", "sessions://gemini", {
1201
- title: "✨ Gemini Sessions",
1202
- description: "Gemini conversation sessions",
1203
- mimeType: "application/json",
1204
- }, async (uri) => {
1205
- runtime.logger.debug("Reading Gemini sessions resource");
1206
- const contents = await runtime.resourceProvider.readResource(uri.href);
1207
- return { contents: contents ? [contents] : [] };
1208
- });
1209
- server.registerResource("grok-sessions", "sessions://grok", {
1210
- title: "⚡ Grok Sessions",
1211
- description: "Grok conversation sessions",
1212
- mimeType: "application/json",
1213
- }, async (uri) => {
1214
- runtime.logger.debug("Reading Grok sessions resource");
1215
- const contents = await runtime.resourceProvider.readResource(uri.href);
1216
- return { contents: contents ? [contents] : [] };
1217
- });
1218
- server.registerResource("mistral-sessions", "sessions://mistral", {
1219
- title: "🌬 Mistral Sessions",
1220
- description: "Mistral Vibe conversation sessions",
1221
- mimeType: "application/json",
1222
- }, async (uri) => {
1223
- runtime.logger.debug("Reading Mistral sessions resource");
1224
- const contents = await runtime.resourceProvider.readResource(uri.href);
1225
- return { contents: contents ? [contents] : [] };
1226
- });
1227
- server.registerResource("claude-models", "models://claude", {
1228
- title: "🧠 Claude Models",
1229
- description: "Claude models and capabilities",
1230
- mimeType: "application/json",
1231
- }, async (uri) => {
1232
- runtime.logger.debug("Reading Claude models resource");
1233
- const contents = await runtime.resourceProvider.readResource(uri.href);
1234
- return { contents: contents ? [contents] : [] };
1235
- });
1236
- server.registerResource("codex-models", "models://codex", {
1237
- title: "🔧 Codex Models",
1238
- description: "Codex models and capabilities",
1239
- mimeType: "application/json",
1240
- }, async (uri) => {
1241
- runtime.logger.debug("Reading Codex models resource");
1242
- const contents = await runtime.resourceProvider.readResource(uri.href);
1243
- return { contents: contents ? [contents] : [] };
1244
- });
1245
- server.registerResource("gemini-models", "models://gemini", {
1246
- title: "🌟 Gemini Models",
1247
- description: "Gemini models and capabilities",
1248
- mimeType: "application/json",
1249
- }, async (uri) => {
1250
- runtime.logger.debug("Reading Gemini models resource");
1251
- const contents = await runtime.resourceProvider.readResource(uri.href);
1252
- return { contents: contents ? [contents] : [] };
1253
- });
1254
- server.registerResource("grok-models", "models://grok", {
1255
- title: "⚡ Grok Models",
1256
- description: "Grok models and capabilities",
1257
- mimeType: "application/json",
1258
- }, async (uri) => {
1259
- runtime.logger.debug("Reading Grok models resource");
1260
- const contents = await runtime.resourceProvider.readResource(uri.href);
1261
- return { contents: contents ? [contents] : [] };
1262
- });
1263
- server.registerResource("mistral-models", "models://mistral", {
1264
- title: "🌬 Mistral Models",
1265
- description: "Mistral Vibe models and capabilities",
1266
- mimeType: "application/json",
1267
- }, async (uri) => {
1268
- runtime.logger.debug("Reading Mistral models resource");
1269
- const contents = await runtime.resourceProvider.readResource(uri.href);
1270
- return { contents: contents ? [contents] : [] };
1271
- });
1295
+ for (const descriptor of generateResourceDescriptors()) {
1296
+ if (descriptor.exposesSessionsResource) {
1297
+ server.registerResource(`${descriptor.provider}-sessions`, descriptor.sessionsUri, {
1298
+ title: `${descriptor.icon} ${descriptor.sessionLabel}s`,
1299
+ description: `${descriptor.displayName} conversation sessions`,
1300
+ mimeType: "application/json",
1301
+ }, async (uri) => {
1302
+ runtime.logger.debug(`Reading ${descriptor.sessionsUri} resource`);
1303
+ const contents = await runtime.resourceProvider.readResource(uri.href);
1304
+ return { contents: contents ? [contents] : [] };
1305
+ });
1306
+ }
1307
+ if (descriptor.exposesModelsResource) {
1308
+ server.registerResource(`${descriptor.provider}-models`, descriptor.modelsUri, {
1309
+ title: `${descriptor.icon} ${descriptor.displayName} Models & Capabilities`,
1310
+ description: `${descriptor.displayName} models and capabilities`,
1311
+ mimeType: "application/json",
1312
+ }, async (uri) => {
1313
+ runtime.logger.debug(`Reading ${descriptor.modelsUri} resource`);
1314
+ const contents = await runtime.resourceProvider.readResource(uri.href);
1315
+ return { contents: contents ? [contents] : [] };
1316
+ });
1317
+ }
1318
+ }
1272
1319
  server.registerResource("performance-metrics", "metrics://performance", {
1273
1320
  title: "📈 Performance Metrics",
1274
1321
  description: "Request counts, latency, success/failure rates",
@@ -1380,9 +1427,30 @@ function registerBaseResources(server, runtime) {
1380
1427
  const contents = await runtime.resourceProvider.readResource(uri.href);
1381
1428
  return { contents: contents ? [contents] : [] };
1382
1429
  });
1383
- const validationReceiptStore = runtime.persistence.backend === "sqlite"
1384
- ? runtime.asyncJobManager.getValidationRunStore()
1385
- : null;
1430
+ for (const descriptor of generateProviderAcpDescriptors()) {
1431
+ server.registerResource(`${descriptor.provider}-provider-acp`, descriptor.acpUri, {
1432
+ title: `${descriptor.icon} ${descriptor.displayName} ACP Capabilities`,
1433
+ description: `Native ACP entrypoint, negotiated capabilities, supported session methods, and host-service policy for ${descriptor.displayName}`,
1434
+ mimeType: "application/json",
1435
+ }, async (uri) => {
1436
+ runtime.logger.debug(`Reading ${descriptor.acpUri} resource`);
1437
+ const contents = await runtime.resourceProvider.readResource(uri.href);
1438
+ return { contents: contents ? [contents] : [] };
1439
+ });
1440
+ }
1441
+ server.registerResource("provider-acp", new ResourceTemplate("provider-acp://{provider}", { list: undefined }), {
1442
+ title: "Provider ACP Capabilities",
1443
+ description: "Read-only negotiated ACP capability record for one provider CLI (native: false for providers with no native ACP entrypoint)",
1444
+ mimeType: "application/json",
1445
+ }, async (uri, variables) => {
1446
+ const provider = Array.isArray(variables.provider)
1447
+ ? variables.provider[0]
1448
+ : variables.provider;
1449
+ runtime.logger.debug(`Reading provider-acp://${provider}`);
1450
+ const contents = await runtime.resourceProvider.readResource(uri.href);
1451
+ return { contents: contents ? [contents] : [] };
1452
+ });
1453
+ const validationReceiptStore = runtime.asyncJobManager?.getValidationRunStore();
1386
1454
  if (validationReceiptStore) {
1387
1455
  server.registerResource("validation-receipt", new ResourceTemplate("validation-receipt://{validationId}", { list: undefined }), {
1388
1456
  title: "Validation Receipt",
@@ -1428,10 +1496,33 @@ function resolvePromptOrPartsForPrep(args) {
1428
1496
  stablePrefixTokens: resolved.stablePrefixTokens,
1429
1497
  };
1430
1498
  }
1499
+ function remoteHostPathFieldError(operation, corrId, fields) {
1500
+ const ctx = getRequestContext();
1501
+ const isRemote = ctx?.transport === "http" || ctx?.authKind === "oauth";
1502
+ if (!isRemote)
1503
+ return null;
1504
+ const present = Object.entries(fields)
1505
+ .filter(([, v]) => (Array.isArray(v) ? v.length > 0 : v !== undefined && v !== null))
1506
+ .map(([k]) => k);
1507
+ if (present.length === 0)
1508
+ return null;
1509
+ return createErrorResponse(operation, 1, `Remote HTTP/OAuth requests may not use host-path or plugin fields: ${present.join(", ")}. ` +
1510
+ `These are restricted to local callers; supply content inline or reference a registered workspace.`, corrId);
1511
+ }
1431
1512
  export function prepareClaudeRequest(params, runtime = resolveGatewayServerRuntime()) {
1432
1513
  const corrId = params.correlationId || randomUUID();
1433
1514
  const cliInfo = getCliInfo();
1434
1515
  const resolvedModel = resolveModelAlias("claude", params.model, cliInfo);
1516
+ const remoteFieldErr = remoteHostPathFieldError(params.operation, corrId, {
1517
+ settings: params.settings,
1518
+ systemPromptFile: params.systemPromptFile,
1519
+ appendSystemPromptFile: params.appendSystemPromptFile,
1520
+ pluginDir: params.pluginDir,
1521
+ pluginUrl: params.pluginUrl,
1522
+ debugFile: params.debugFile,
1523
+ });
1524
+ if (remoteFieldErr)
1525
+ return remoteFieldErr;
1435
1526
  const inputResolution = resolvePromptOrPartsForPrep({
1436
1527
  prompt: params.prompt,
1437
1528
  promptParts: params.promptParts,
@@ -1651,6 +1742,17 @@ export function prepareClaudeRequest(params, runtime = resolveGatewayServerRunti
1651
1742
  settingSources: params.settingSources,
1652
1743
  settings: params.settings,
1653
1744
  tools: params.tools,
1745
+ includeHookEvents: params.includeHookEvents,
1746
+ replayUserMessages: params.replayUserMessages,
1747
+ systemPromptFile: params.systemPromptFile,
1748
+ appendSystemPromptFile: params.appendSystemPromptFile,
1749
+ name: params.name,
1750
+ pluginDir: params.pluginDir,
1751
+ pluginUrl: params.pluginUrl,
1752
+ safeMode: params.safeMode,
1753
+ bare: params.bare,
1754
+ debug: params.debug,
1755
+ debugFile: params.debugFile,
1654
1756
  }));
1655
1757
  return {
1656
1758
  corrId,
@@ -1673,6 +1775,13 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1673
1775
  const corrId = params.correlationId || randomUUID();
1674
1776
  const cliInfo = getCliInfo();
1675
1777
  const resolvedModel = resolveModelAlias("codex", params.model, cliInfo);
1778
+ const remoteFieldErr = remoteHostPathFieldError(params.operation, corrId, {
1779
+ outputSchema: typeof params.outputSchema === "string" ? params.outputSchema : undefined,
1780
+ images: params.images,
1781
+ outputLastMessage: params.outputLastMessage,
1782
+ });
1783
+ if (remoteFieldErr)
1784
+ return remoteFieldErr;
1676
1785
  const inputResolution = resolvePromptOrPartsForPrep({
1677
1786
  prompt: params.prompt,
1678
1787
  promptParts: params.promptParts,
@@ -1751,6 +1860,9 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1751
1860
  if (params.dangerouslyBypassApprovalsAndSandbox) {
1752
1861
  args.push("--dangerously-bypass-approvals-and-sandbox");
1753
1862
  }
1863
+ if (params.dangerouslyBypassHookTrust) {
1864
+ args.push("--dangerously-bypass-hook-trust");
1865
+ }
1754
1866
  args.push("--json");
1755
1867
  args.push("--skip-git-repo-check");
1756
1868
  let highImpactCleanup;
@@ -1763,6 +1875,21 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1763
1875
  args.push("--add-dir", dir);
1764
1876
  }
1765
1877
  }
1878
+ if (params.oss) {
1879
+ args.push("--oss");
1880
+ }
1881
+ if (params.localProvider) {
1882
+ args.push("--local-provider", params.localProvider);
1883
+ }
1884
+ if (params.strictConfig) {
1885
+ args.push("--strict-config");
1886
+ }
1887
+ if (params.color) {
1888
+ args.push("--color", params.color);
1889
+ }
1890
+ if (params.outputLastMessage !== undefined) {
1891
+ args.push("--output-last-message", params.outputLastMessage);
1892
+ }
1766
1893
  const high = prepareCodexHighImpactFlags({
1767
1894
  outputSchema: params.outputSchema,
1768
1895
  search: params.search,
@@ -1772,6 +1899,8 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1772
1899
  images: params.images,
1773
1900
  ignoreUserConfig: params.ignoreUserConfig,
1774
1901
  ignoreRules: params.ignoreRules,
1902
+ enable: params.enable,
1903
+ disable: params.disable,
1775
1904
  });
1776
1905
  if (high.missingImagePath) {
1777
1906
  return createErrorResponse(params.operation, 1, "", corrId, new Error(`images: path does not exist: ${high.missingImagePath}`));
@@ -1795,6 +1924,8 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1795
1924
  images: params.images,
1796
1925
  ignoreUserConfig: params.ignoreUserConfig,
1797
1926
  ignoreRules: params.ignoreRules,
1927
+ enable: params.enable,
1928
+ disable: params.disable,
1798
1929
  });
1799
1930
  if (high.missingImagePath) {
1800
1931
  return createErrorResponse(params.operation, 1, "", corrId, new Error(`images: path does not exist: ${high.missingImagePath}`));
@@ -1924,6 +2055,9 @@ export function prepareGeminiRequest(params, runtime = resolveGatewayServerRunti
1924
2055
  if (params.newProject) {
1925
2056
  args.push("--new-project");
1926
2057
  }
2058
+ if (params.printTimeout !== undefined && params.printTimeout !== "") {
2059
+ args.push("--print-timeout", params.printTimeout);
2060
+ }
1927
2061
  return {
1928
2062
  corrId,
1929
2063
  effectivePrompt,
@@ -1940,6 +2074,14 @@ export function prepareGrokRequest(params, runtime = resolveGatewayServerRuntime
1940
2074
  const corrId = params.correlationId || randomUUID();
1941
2075
  const cliInfo = getCliInfo();
1942
2076
  const resolvedModel = resolveModelAlias("grok", params.model, cliInfo);
2077
+ const remoteFieldErr = remoteHostPathFieldError(params.operation, corrId, {
2078
+ promptFile: params.promptFile,
2079
+ leaderSocket: params.leaderSocket,
2080
+ rules: params.rules,
2081
+ agent: params.agent,
2082
+ });
2083
+ if (remoteFieldErr)
2084
+ return remoteFieldErr;
1943
2085
  const inputResolution = resolvePromptOrPartsForPrep({
1944
2086
  prompt: params.prompt,
1945
2087
  promptParts: params.promptParts,
@@ -2202,6 +2344,9 @@ export function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionI
2202
2344
  if (cli === "codex" && outputFormat !== "json") {
2203
2345
  finalStdout = codexDisplayText(stdout);
2204
2346
  }
2347
+ if (cli === "grok") {
2348
+ finalStdout = grokDisplayText(outputFormat, stdout);
2349
+ }
2205
2350
  if (optimizeResponse && outputFormat !== "json") {
2206
2351
  const optimized = optimizeResponseText(finalStdout);
2207
2352
  logOptimizationTokens("response", corrId, finalStdout, optimized);
@@ -2217,11 +2362,11 @@ export function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionI
2217
2362
  }
2218
2363
  const derivedWarnings = [];
2219
2364
  const extraStructured = {};
2220
- let claudeResultError = false;
2365
+ let resultError = false;
2221
2366
  if (cli === "claude") {
2222
2367
  const parsedResult = parseStreamJson(stdout);
2223
2368
  if (parsedResult.isError) {
2224
- claudeResultError = true;
2369
+ resultError = true;
2225
2370
  extraStructured.resultIsError = true;
2226
2371
  derivedWarnings.push({
2227
2372
  code: "claude_result_error",
@@ -2234,8 +2379,27 @@ export function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionI
2234
2379
  if (parsedCodex.threadId) {
2235
2380
  extraStructured.codexSessionId = parsedCodex.threadId;
2236
2381
  }
2382
+ if (parsedCodex.error) {
2383
+ resultError = true;
2384
+ extraStructured.resultIsError = true;
2385
+ derivedWarnings.push({
2386
+ code: "codex_result_error",
2387
+ message: `Codex exited 0 but reported a failed turn: ${parsedCodex.error}. The returned text may be partial.`,
2388
+ });
2389
+ }
2237
2390
  }
2238
- if (!claudeResultError && finalStdout.trim().length === 0) {
2391
+ if (cli === "gemini") {
2392
+ const parsedGemini = outputFormat === "stream-json" ? parseGeminiStreamJson(stdout) : parseGeminiJson(stdout);
2393
+ if (parsedGemini?.stopReason === "error") {
2394
+ resultError = true;
2395
+ extraStructured.resultIsError = true;
2396
+ derivedWarnings.push({
2397
+ code: "gemini_result_error",
2398
+ message: "Gemini exited 0 but the result event reported status:error. The returned text may be partial.",
2399
+ });
2400
+ }
2401
+ }
2402
+ if (!resultError && finalStdout.trim().length === 0) {
2239
2403
  extraStructured.emptyOutput = true;
2240
2404
  derivedWarnings.push({
2241
2405
  code: "empty_output",
@@ -2973,6 +3137,7 @@ export async function handleGeminiRequest(deps, params) {
2973
3137
  yolo: params.yolo,
2974
3138
  project: params.project,
2975
3139
  newProject: params.newProject,
3140
+ printTimeout: params.printTimeout,
2976
3141
  }, runtime);
2977
3142
  if (!("args" in prep))
2978
3143
  return prep;
@@ -3061,6 +3226,7 @@ export async function handleGeminiRequest(deps, params) {
3061
3226
  }
3062
3227
  }
3063
3228
  const geminiUsage = extractUsageAndCost("gemini", stdout, params.outputFormat);
3229
+ const geminiMeta = extractProviderOutputMetadata("gemini", stdout, params.outputFormat);
3064
3230
  safeFlightComplete(corrId, {
3065
3231
  response: stdout,
3066
3232
  durationMs,
@@ -3075,6 +3241,8 @@ export async function handleGeminiRequest(deps, params) {
3075
3241
  cacheReadTokens: geminiUsage.cacheReadTokens,
3076
3242
  cacheCreationTokens: geminiUsage.cacheCreationTokens,
3077
3243
  costUsd: geminiUsage.costUsd,
3244
+ providerSessionId: geminiMeta.sessionId,
3245
+ stopReason: geminiMeta.stopReason,
3078
3246
  }, runtime);
3079
3247
  return response;
3080
3248
  }
@@ -3122,6 +3290,7 @@ export async function handleGeminiRequestAsync(deps, params) {
3122
3290
  yolo: params.yolo,
3123
3291
  project: params.project,
3124
3292
  newProject: params.newProject,
3293
+ printTimeout: params.printTimeout,
3125
3294
  }, runtime);
3126
3295
  if (!("args" in prep))
3127
3296
  return prep;
@@ -3343,6 +3512,7 @@ export async function handleGrokRequest(deps, params) {
3343
3512
  first.text = formatWorktreePrefix(worktreeResolution.worktreePath) + first.text;
3344
3513
  }
3345
3514
  }
3515
+ const grokMeta = extractProviderOutputMetadata("grok", stdout, params.outputFormat);
3346
3516
  safeFlightComplete(corrId, {
3347
3517
  response: stdout,
3348
3518
  durationMs,
@@ -3352,6 +3522,8 @@ export async function handleGrokRequest(deps, params) {
3352
3522
  optimizationApplied: params.optimizePrompt || (params.optimizeResponse ?? false),
3353
3523
  exitCode: 0,
3354
3524
  status: "completed",
3525
+ providerSessionId: grokMeta.sessionId,
3526
+ stopReason: grokMeta.stopReason,
3355
3527
  }, runtime);
3356
3528
  return response;
3357
3529
  }
@@ -3506,6 +3678,14 @@ export async function handleGrokRequestAsync(deps, params) {
3506
3678
  }
3507
3679
  export function prepareDevinRequest(params, _runtime) {
3508
3680
  const corrId = params.correlationId ?? randomUUID();
3681
+ const remoteFieldErr = remoteHostPathFieldError(params.operation, corrId, {
3682
+ promptFile: params.promptFile,
3683
+ config: params.config,
3684
+ agentConfig: params.agentConfig,
3685
+ exportSession: typeof params.exportSession === "string" ? params.exportSession : undefined,
3686
+ });
3687
+ if (remoteFieldErr)
3688
+ return remoteFieldErr;
3509
3689
  let prompt = (params.prompt ?? "").trim();
3510
3690
  if (!prompt) {
3511
3691
  return createErrorResponse(params.operation, 1, "prompt is required and cannot be empty", corrId);
@@ -3520,6 +3700,21 @@ export function prepareDevinRequest(params, _runtime) {
3520
3700
  args.push("--permission-mode", params.permissionMode);
3521
3701
  if (params.promptFile)
3522
3702
  args.push("--prompt-file", params.promptFile);
3703
+ if (params.config)
3704
+ args.push("--config", params.config);
3705
+ if (params.sandbox)
3706
+ args.push("--sandbox");
3707
+ if (typeof params.exportSession === "string") {
3708
+ args.push("--export", params.exportSession);
3709
+ }
3710
+ else if (params.exportSession === true) {
3711
+ args.push("--export");
3712
+ }
3713
+ if (params.respectWorkspaceTrust !== undefined) {
3714
+ args.push("--respect-workspace-trust", params.respectWorkspaceTrust ? "true" : "false");
3715
+ }
3716
+ if (params.agentConfig)
3717
+ args.push("--agent-config", params.agentConfig);
3523
3718
  return {
3524
3719
  corrId,
3525
3720
  effectivePrompt: prompt,
@@ -3539,6 +3734,7 @@ export async function handleDevinRequest(deps, params) {
3539
3734
  model: params.model,
3540
3735
  sessionId: params.sessionId,
3541
3736
  correlationId: params.correlationId,
3737
+ agentType: params.agentType,
3542
3738
  });
3543
3739
  }
3544
3740
  const runtime = resolveHandlerRuntime(deps);
@@ -3548,6 +3744,11 @@ export async function handleDevinRequest(deps, params) {
3548
3744
  model: params.model,
3549
3745
  permissionMode: params.permissionMode,
3550
3746
  promptFile: params.promptFile,
3747
+ config: params.config,
3748
+ sandbox: params.sandbox,
3749
+ exportSession: params.exportSession,
3750
+ respectWorkspaceTrust: params.respectWorkspaceTrust,
3751
+ agentConfig: params.agentConfig,
3551
3752
  correlationId: params.correlationId,
3552
3753
  optimizePrompt: params.optimizePrompt,
3553
3754
  operation: "devin_request",
@@ -3651,6 +3852,11 @@ export async function handleDevinRequestAsync(deps, params) {
3651
3852
  model: params.model,
3652
3853
  permissionMode: params.permissionMode,
3653
3854
  promptFile: params.promptFile,
3855
+ config: params.config,
3856
+ sandbox: params.sandbox,
3857
+ exportSession: params.exportSession,
3858
+ respectWorkspaceTrust: params.respectWorkspaceTrust,
3859
+ agentConfig: params.agentConfig,
3654
3860
  correlationId: params.correlationId,
3655
3861
  optimizePrompt: params.optimizePrompt,
3656
3862
  operation: "devin_request_async",
@@ -4355,6 +4561,14 @@ export async function handleCodexRequestAsync(deps, params) {
4355
4561
  ignoreRules: params.ignoreRules,
4356
4562
  workingDir: params.workingDir,
4357
4563
  addDir: params.addDir,
4564
+ enable: params.enable,
4565
+ disable: params.disable,
4566
+ strictConfig: params.strictConfig,
4567
+ oss: params.oss,
4568
+ localProvider: params.localProvider,
4569
+ color: params.color,
4570
+ outputLastMessage: params.outputLastMessage,
4571
+ dangerouslyBypassHookTrust: params.dangerouslyBypassHookTrust,
4358
4572
  }, runtime);
4359
4573
  if (!("args" in prep))
4360
4574
  return prep;
@@ -4459,9 +4673,14 @@ export function createGatewayServer(deps = {}) {
4459
4673
  registerValidationTools(server, {
4460
4674
  asyncJobManager,
4461
4675
  apiProviders: enabledApiProviders(providers),
4462
- validationRunStore: persistence.backend === "sqlite" ? asyncJobManager.getValidationRunStore() : null,
4676
+ validationRunStore: asyncJobManager.getValidationRunStore(),
4463
4677
  });
4464
4678
  registerWorkspaceTools(server, runtime);
4679
+ registerProviderAdminTools(server, {
4680
+ approvalManager: runtime.approvalManager,
4681
+ logger: runtime.logger,
4682
+ allowMutatingCliAdminOps: runtime.adminConfig.allowMutatingCliAdminOps,
4683
+ });
4465
4684
  const apiProviderTools = registerApiProviderTools(server, runtime, providers, asyncJobsEnabled);
4466
4685
  if (apiProviderTools.length > 0) {
4467
4686
  runtime.logger.info(`Registered API provider tools: ${apiProviderTools.join(", ")}`);
@@ -4650,6 +4869,7 @@ export function createGatewayServer(deps = {}) {
4650
4869
  .array(z.string())
4651
4870
  .optional()
4652
4871
  .describe('Claude --tools: restrict the available built-in tool set (distinct from allowedTools permission gating). Pass [""] to disable all tools.'),
4872
+ ...CLAUDE_PART_A_FIELDS,
4653
4873
  workspace: providerWorkspaceAliasSchema(),
4654
4874
  worktree: WORKTREE_SCHEMA.optional(),
4655
4875
  approvalStrategy: z
@@ -4685,7 +4905,7 @@ export function createGatewayServer(deps = {}) {
4685
4905
  destructiveHint: true,
4686
4906
  idempotentHint: false,
4687
4907
  openWorldHint: true,
4688
- }, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, addDir, noSessionPersistence, settingSources, settings, tools, workspace, worktree, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, }) => {
4908
+ }, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, addDir, noSessionPersistence, settingSources, settings, tools, includeHookEvents, replayUserMessages, systemPromptFile, appendSystemPromptFile, name, pluginDir, pluginUrl, safeMode, bare, debug, debugFile, workspace, worktree, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, }) => {
4689
4909
  const startTime = Date.now();
4690
4910
  if (systemPrompt !== undefined && appendSystemPrompt !== undefined) {
4691
4911
  return createErrorResponse("claude", 1, "", correlationId, new Error("systemPrompt and appendSystemPrompt are mutually exclusive; use one or the other (not both)."));
@@ -4722,6 +4942,17 @@ export function createGatewayServer(deps = {}) {
4722
4942
  settingSources,
4723
4943
  settings,
4724
4944
  tools,
4945
+ includeHookEvents,
4946
+ replayUserMessages,
4947
+ systemPromptFile,
4948
+ appendSystemPromptFile,
4949
+ name,
4950
+ pluginDir,
4951
+ pluginUrl,
4952
+ safeMode,
4953
+ bare,
4954
+ debug,
4955
+ debugFile,
4725
4956
  }, runtime);
4726
4957
  if (!("args" in prep))
4727
4958
  return prep;
@@ -4849,6 +5080,8 @@ export function createGatewayServer(deps = {}) {
4849
5080
  optimizationApplied: optimizePrompt || optimizeResponse,
4850
5081
  exitCode: 0,
4851
5082
  status: "completed",
5083
+ providerSessionId: parsed.sessionId ?? undefined,
5084
+ stopReason: parsed.stopReason ?? undefined,
4852
5085
  }, runtime);
4853
5086
  const streamResponse = buildCliResponse("claude", parsed.text, optimizeResponse, corrId, effectiveSessionId, prep, durationMs, undefined, outputFormat, warnings);
4854
5087
  if (worktreeResolution.worktreePath) {
@@ -4859,6 +5092,7 @@ export function createGatewayServer(deps = {}) {
4859
5092
  }
4860
5093
  return streamResponse;
4861
5094
  }
5095
+ const claudeMeta = extractProviderOutputMetadata("claude", stdout, outputFormat);
4862
5096
  safeFlightComplete(corrId, {
4863
5097
  response: stdout,
4864
5098
  durationMs,
@@ -4867,6 +5101,8 @@ export function createGatewayServer(deps = {}) {
4867
5101
  optimizationApplied: optimizePrompt || optimizeResponse,
4868
5102
  exitCode: 0,
4869
5103
  status: "completed",
5104
+ providerSessionId: claudeMeta.sessionId,
5105
+ stopReason: claudeMeta.stopReason,
4870
5106
  }, runtime);
4871
5107
  const nonStreamResponse = buildCliResponse("claude", stdout, optimizeResponse, corrId, effectiveSessionId, prep, durationMs, undefined, outputFormat, warnings);
4872
5108
  if (worktreeResolution.worktreePath) {
@@ -5005,6 +5241,7 @@ export function createGatewayServer(deps = {}) {
5005
5241
  .optional()
5006
5242
  .describe("Codex --add-dir <DIR>: additional writable workspace directories. Emitted once per entry on new sessions only; resume inherits the original session's writable-dir policy." +
5007
5243
  LOCAL_ADD_DIR_FIELD_SUFFIX),
5244
+ ...CODEX_PART_A_FIELDS,
5008
5245
  workspace: providerWorkspaceAliasSchema(),
5009
5246
  worktree: WORKTREE_SCHEMA.optional(),
5010
5247
  }, {
@@ -5013,7 +5250,7 @@ export function createGatewayServer(deps = {}) {
5013
5250
  destructiveHint: true,
5014
5251
  idempotentHint: false,
5015
5252
  openWorldHint: true,
5016
- }, async ({ prompt, promptParts, model, fullAuto, sandboxMode, askForApproval, useLegacyFullAutoFlag, dangerouslyBypassApprovalsAndSandbox, approvalStrategy, approvalPolicy, mcpServers, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, outputFormat, outputSchema, search, profile, configOverrides, ephemeral, images, ignoreUserConfig, ignoreRules, workingDir, addDir, workspace, worktree, }) => {
5253
+ }, async ({ prompt, promptParts, model, fullAuto, sandboxMode, askForApproval, useLegacyFullAutoFlag, dangerouslyBypassApprovalsAndSandbox, approvalStrategy, approvalPolicy, mcpServers, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, outputFormat, outputSchema, search, profile, configOverrides, ephemeral, images, ignoreUserConfig, ignoreRules, workingDir, addDir, enable, disable, strictConfig, oss, localProvider, color, outputLastMessage, dangerouslyBypassHookTrust, workspace, worktree, }) => {
5017
5254
  const startTime = Date.now();
5018
5255
  const prep = prepareCodexRequest({
5019
5256
  prompt,
@@ -5044,6 +5281,14 @@ export function createGatewayServer(deps = {}) {
5044
5281
  ignoreRules,
5045
5282
  workingDir,
5046
5283
  addDir,
5284
+ enable,
5285
+ disable,
5286
+ strictConfig,
5287
+ oss,
5288
+ localProvider,
5289
+ color,
5290
+ outputLastMessage,
5291
+ dangerouslyBypassHookTrust,
5047
5292
  }, runtime);
5048
5293
  if (!("args" in prep))
5049
5294
  return prep;
@@ -5102,7 +5347,12 @@ export function createGatewayServer(deps = {}) {
5102
5347
  errorMessage: stderr || `Exit code ${code}`,
5103
5348
  status: "failed",
5104
5349
  }, runtime);
5105
- const codexErrorDetail = stderr && stderr.trim().length > 0 ? stderr : codexDisplayText(stdout);
5350
+ const parsedCodexError = parseCodexJsonStream(stdout).error;
5351
+ const codexErrorDetail = stderr && stderr.trim().length > 0
5352
+ ? stderr
5353
+ : parsedCodexError && parsedCodexError.trim().length > 0
5354
+ ? parsedCodexError
5355
+ : codexDisplayText(stdout);
5106
5356
  const codexResumeHint = sessionId &&
5107
5357
  /not found|no such|unknown session|does not exist|invalid session/i.test(codexErrorDetail)
5108
5358
  ? `\n\nSession ${sessionId} could not be resumed. Codex session IDs are UUIDs under ~/.codex/sessions/; verify the id, omit sessionId, or set createNewSession:true.`
@@ -5130,6 +5380,7 @@ export function createGatewayServer(deps = {}) {
5130
5380
  }
5131
5381
  logger.info(`[${corrId}] codex_request completed successfully in ${durationMs}ms`);
5132
5382
  const codexUsage = extractUsageAndCost("codex", stdout, outputFormat);
5383
+ const codexMeta = extractProviderOutputMetadata("codex", stdout, outputFormat);
5133
5384
  safeFlightComplete(corrId, {
5134
5385
  response: codexFrResponse(outputFormat, stdout),
5135
5386
  durationMs,
@@ -5143,6 +5394,8 @@ export function createGatewayServer(deps = {}) {
5143
5394
  cacheReadTokens: codexUsage.cacheReadTokens,
5144
5395
  cacheCreationTokens: codexUsage.cacheCreationTokens,
5145
5396
  costUsd: codexUsage.costUsd,
5397
+ providerSessionId: codexMeta.sessionId,
5398
+ stopReason: codexMeta.stopReason,
5146
5399
  }, runtime);
5147
5400
  const codexResponse = buildCliResponse("codex", stdout, optimizeResponse, corrId, effectiveSessionId, prep, durationMs, undefined, outputFormat);
5148
5401
  if (worktreeResolution.worktreePath) {
@@ -5264,7 +5517,12 @@ export function createGatewayServer(deps = {}) {
5264
5517
  const { stdout, stderr, code } = result;
5265
5518
  durationMs = Math.max(0, Date.now() - startTime);
5266
5519
  if (code !== 0) {
5267
- const codexErrorDetail = stderr && stderr.trim().length > 0 ? stderr : codexDisplayText(stdout);
5520
+ const parsedCodexError = parseCodexJsonStream(stdout).error;
5521
+ const codexErrorDetail = stderr && stderr.trim().length > 0
5522
+ ? stderr
5523
+ : parsedCodexError && parsedCodexError.trim().length > 0
5524
+ ? parsedCodexError
5525
+ : codexDisplayText(stdout);
5268
5526
  const codexResumeHint = sessionId &&
5269
5527
  /not found|no such|unknown session|does not exist|invalid session/i.test(codexErrorDetail)
5270
5528
  ? `\n\nSession ${sessionId} could not be resumed. Codex session IDs are UUIDs under ~/.codex/sessions/; verify the id, omit sessionId, or set createNewSession:true.`
@@ -5368,6 +5626,11 @@ export function createGatewayServer(deps = {}) {
5368
5626
  .boolean()
5369
5627
  .optional()
5370
5628
  .describe("Antigravity 1.0.13 --new-project: create a new project for this session. Mutually exclusive with project."),
5629
+ printTimeout: z
5630
+ .string()
5631
+ .min(1)
5632
+ .optional()
5633
+ .describe("Antigravity --print-timeout <DURATION>: print-mode wait timeout as a Go duration string (e.g. '5m0s', '30s')."),
5371
5634
  workspace: providerWorkspaceAliasSchema(),
5372
5635
  worktree: WORKTREE_SCHEMA.optional(),
5373
5636
  }, {
@@ -5376,7 +5639,7 @@ export function createGatewayServer(deps = {}) {
5376
5639
  destructiveHint: true,
5377
5640
  idempotentHint: false,
5378
5641
  openWorldHint: true,
5379
- }, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, project, newProject, workspace, worktree, }) => {
5642
+ }, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, project, newProject, printTimeout, workspace, worktree, }) => {
5380
5643
  return handleGeminiRequest({ sessionManager, logger, runtime }, {
5381
5644
  prompt,
5382
5645
  promptParts,
@@ -5404,6 +5667,7 @@ export function createGatewayServer(deps = {}) {
5404
5667
  yolo,
5405
5668
  project,
5406
5669
  newProject,
5670
+ printTimeout,
5407
5671
  workspace,
5408
5672
  worktree,
5409
5673
  });
@@ -5577,6 +5841,27 @@ export function createGatewayServer(deps = {}) {
5577
5841
  .string()
5578
5842
  .optional()
5579
5843
  .describe("Load the initial prompt from a file (--prompt-file)"),
5844
+ config: z.string().optional().describe("Config file path (Devin --config <PATH>)"),
5845
+ sandbox: z
5846
+ .boolean()
5847
+ .optional()
5848
+ .describe("Run the Devin session in a sandbox (Devin --sandbox). Safety control: never defaulted on; pass true to opt in."),
5849
+ exportSession: z
5850
+ .union([z.boolean(), z.string()])
5851
+ .optional()
5852
+ .describe("Export the session (Devin --export [<PATH>]). true emits a bare --export; a string path emits --export <path>."),
5853
+ respectWorkspaceTrust: z
5854
+ .boolean()
5855
+ .optional()
5856
+ .describe("Respect workspace trust (Devin --respect-workspace-trust <bool>). Devin defaults true for interactive and false for print mode; set explicitly to override."),
5857
+ agentConfig: z
5858
+ .string()
5859
+ .optional()
5860
+ .describe("Agent config file path (Devin --agent-config <FILE>)"),
5861
+ agentType: z
5862
+ .enum(DEVIN_ACP_AGENT_TYPES)
5863
+ .optional()
5864
+ .describe("ACP agent variant for transport=acp (`devin acp --agent-type`): 'summarizer' (no tools, text summary) or 'review' (read-only + shell code-review). Ignored for the CLI transport."),
5580
5865
  sessionId: z
5581
5866
  .string()
5582
5867
  .optional()
@@ -5606,13 +5891,19 @@ export function createGatewayServer(deps = {}) {
5606
5891
  destructiveHint: true,
5607
5892
  idempotentHint: false,
5608
5893
  openWorldHint: true,
5609
- }, async ({ prompt, model, transport, permissionMode, promptFile, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, }) => {
5894
+ }, async ({ prompt, model, transport, permissionMode, promptFile, config, sandbox, exportSession, respectWorkspaceTrust, agentConfig, agentType, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, }) => {
5610
5895
  return handleDevinRequest({ sessionManager, logger, runtime }, {
5611
5896
  prompt,
5612
5897
  model,
5613
5898
  transport,
5614
5899
  permissionMode,
5615
5900
  promptFile,
5901
+ config,
5902
+ sandbox,
5903
+ exportSession,
5904
+ respectWorkspaceTrust,
5905
+ agentConfig,
5906
+ agentType,
5616
5907
  sessionId,
5617
5908
  resumeLatest,
5618
5909
  createNewSession,
@@ -5959,6 +6250,7 @@ export function createGatewayServer(deps = {}) {
5959
6250
  .array(z.string())
5960
6251
  .optional()
5961
6252
  .describe('Claude --tools: restrict the available built-in tool set (distinct from allowedTools permission gating). Pass [""] to disable all tools.'),
6253
+ ...CLAUDE_PART_A_FIELDS,
5962
6254
  workspace: providerWorkspaceAliasSchema(),
5963
6255
  worktree: WORKTREE_SCHEMA.optional(),
5964
6256
  approvalStrategy: z
@@ -5993,7 +6285,7 @@ export function createGatewayServer(deps = {}) {
5993
6285
  destructiveHint: true,
5994
6286
  idempotentHint: false,
5995
6287
  openWorldHint: true,
5996
- }, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, addDir, noSessionPersistence, settingSources, settings, tools, workspace, worktree, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, }) => {
6288
+ }, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, addDir, noSessionPersistence, settingSources, settings, tools, includeHookEvents, replayUserMessages, systemPromptFile, appendSystemPromptFile, name, pluginDir, pluginUrl, safeMode, bare, debug, debugFile, workspace, worktree, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, }) => {
5997
6289
  if (systemPrompt !== undefined && appendSystemPrompt !== undefined) {
5998
6290
  return createErrorResponse("claude", 1, "", correlationId, new Error("systemPrompt and appendSystemPrompt are mutually exclusive; use one or the other (not both)."));
5999
6291
  }
@@ -6029,6 +6321,17 @@ export function createGatewayServer(deps = {}) {
6029
6321
  settingSources,
6030
6322
  settings,
6031
6323
  tools,
6324
+ includeHookEvents,
6325
+ replayUserMessages,
6326
+ systemPromptFile,
6327
+ appendSystemPromptFile,
6328
+ name,
6329
+ pluginDir,
6330
+ pluginUrl,
6331
+ safeMode,
6332
+ bare,
6333
+ debug,
6334
+ debugFile,
6032
6335
  }, runtime);
6033
6336
  if (!("args" in prep))
6034
6337
  return prep;
@@ -6216,6 +6519,7 @@ export function createGatewayServer(deps = {}) {
6216
6519
  .optional()
6217
6520
  .describe("Codex --add-dir <DIR>: additional writable workspace directories (repeat per entry). New sessions only." +
6218
6521
  LOCAL_ADD_DIR_FIELD_SUFFIX),
6522
+ ...CODEX_PART_A_FIELDS,
6219
6523
  workspace: providerWorkspaceAliasSchema(),
6220
6524
  worktree: WORKTREE_SCHEMA.optional(),
6221
6525
  }, {
@@ -6224,7 +6528,7 @@ export function createGatewayServer(deps = {}) {
6224
6528
  destructiveHint: true,
6225
6529
  idempotentHint: false,
6226
6530
  openWorldHint: true,
6227
- }, async ({ prompt, promptParts, model, fullAuto, sandboxMode, askForApproval, useLegacyFullAutoFlag, dangerouslyBypassApprovalsAndSandbox, approvalStrategy, approvalPolicy, mcpServers, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, outputFormat, outputSchema, search, profile, configOverrides, ephemeral, images, ignoreUserConfig, ignoreRules, workingDir, addDir, workspace, worktree, }) => {
6531
+ }, async ({ prompt, promptParts, model, fullAuto, sandboxMode, askForApproval, useLegacyFullAutoFlag, dangerouslyBypassApprovalsAndSandbox, approvalStrategy, approvalPolicy, mcpServers, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, outputFormat, outputSchema, search, profile, configOverrides, ephemeral, images, ignoreUserConfig, ignoreRules, workingDir, addDir, enable, disable, strictConfig, oss, localProvider, color, outputLastMessage, dangerouslyBypassHookTrust, workspace, worktree, }) => {
6228
6532
  return handleCodexRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
6229
6533
  prompt,
6230
6534
  promptParts,
@@ -6255,6 +6559,14 @@ export function createGatewayServer(deps = {}) {
6255
6559
  ignoreRules,
6256
6560
  workingDir,
6257
6561
  addDir,
6562
+ enable,
6563
+ disable,
6564
+ strictConfig,
6565
+ oss,
6566
+ localProvider,
6567
+ color,
6568
+ outputLastMessage,
6569
+ dangerouslyBypassHookTrust,
6258
6570
  workspace,
6259
6571
  worktree,
6260
6572
  });
@@ -6343,6 +6655,11 @@ export function createGatewayServer(deps = {}) {
6343
6655
  .boolean()
6344
6656
  .optional()
6345
6657
  .describe("Antigravity 1.0.13 --new-project: create a new project for this session. Mutually exclusive with project."),
6658
+ printTimeout: z
6659
+ .string()
6660
+ .min(1)
6661
+ .optional()
6662
+ .describe("Antigravity --print-timeout <DURATION>: print-mode wait timeout as a Go duration string (e.g. '5m0s', '30s')."),
6346
6663
  workspace: providerWorkspaceAliasSchema(),
6347
6664
  worktree: WORKTREE_SCHEMA.optional(),
6348
6665
  }, {
@@ -6351,7 +6668,7 @@ export function createGatewayServer(deps = {}) {
6351
6668
  destructiveHint: true,
6352
6669
  idempotentHint: false,
6353
6670
  openWorldHint: true,
6354
- }, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, project, newProject, workspace, worktree, }) => {
6671
+ }, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, project, newProject, printTimeout, workspace, worktree, }) => {
6355
6672
  return handleGeminiRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
6356
6673
  prompt,
6357
6674
  promptParts,
@@ -6378,6 +6695,7 @@ export function createGatewayServer(deps = {}) {
6378
6695
  yolo,
6379
6696
  project,
6380
6697
  newProject,
6698
+ printTimeout,
6381
6699
  workspace,
6382
6700
  worktree,
6383
6701
  });
@@ -6653,6 +6971,23 @@ export function createGatewayServer(deps = {}) {
6653
6971
  .string()
6654
6972
  .optional()
6655
6973
  .describe("Load the initial prompt from a file (--prompt-file)"),
6974
+ config: z.string().optional().describe("Config file path (Devin --config <PATH>)"),
6975
+ sandbox: z
6976
+ .boolean()
6977
+ .optional()
6978
+ .describe("Run the Devin session in a sandbox (Devin --sandbox). Safety control: never defaulted on; pass true to opt in."),
6979
+ exportSession: z
6980
+ .union([z.boolean(), z.string()])
6981
+ .optional()
6982
+ .describe("Export the session (Devin --export [<PATH>]). true emits a bare --export; a string path emits --export <path>."),
6983
+ respectWorkspaceTrust: z
6984
+ .boolean()
6985
+ .optional()
6986
+ .describe("Respect workspace trust (Devin --respect-workspace-trust <bool>). Devin defaults true for interactive and false for print mode; set explicitly to override."),
6987
+ agentConfig: z
6988
+ .string()
6989
+ .optional()
6990
+ .describe("Agent config file path (Devin --agent-config <FILE>)"),
6656
6991
  sessionId: z
6657
6992
  .string()
6658
6993
  .optional()
@@ -6681,12 +7016,17 @@ export function createGatewayServer(deps = {}) {
6681
7016
  destructiveHint: true,
6682
7017
  idempotentHint: false,
6683
7018
  openWorldHint: true,
6684
- }, async ({ prompt, model, permissionMode, promptFile, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, }) => {
7019
+ }, async ({ prompt, model, permissionMode, promptFile, config, sandbox, exportSession, respectWorkspaceTrust, agentConfig, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, }) => {
6685
7020
  return handleDevinRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
6686
7021
  prompt,
6687
7022
  model,
6688
7023
  permissionMode,
6689
7024
  promptFile,
7025
+ config,
7026
+ sandbox,
7027
+ exportSession,
7028
+ respectWorkspaceTrust,
7029
+ agentConfig,
6690
7030
  sessionId,
6691
7031
  resumeLatest,
6692
7032
  createNewSession,
@@ -6989,6 +7329,18 @@ export function createGatewayServer(deps = {}) {
6989
7329
  result.stdout) {
6990
7330
  result.stdout = codexDisplayText(result.stdout);
6991
7331
  }
7332
+ if (callerIsRemote()) {
7333
+ const leakedId = result.providerSessionId;
7334
+ delete result.providerSessionId;
7335
+ if (leakedId) {
7336
+ if (result.stdout && result.stdout.includes(leakedId)) {
7337
+ result.stdout = result.stdout.split(leakedId).join("[redacted-session-id]");
7338
+ }
7339
+ if (result.stderr && result.stderr.includes(leakedId)) {
7340
+ result.stderr = result.stderr.split(leakedId).join("[redacted-session-id]");
7341
+ }
7342
+ }
7343
+ }
6992
7344
  return {
6993
7345
  content: [
6994
7346
  {
@@ -7232,10 +7584,7 @@ export function createGatewayServer(deps = {}) {
7232
7584
  .max(500)
7233
7585
  .default(50)
7234
7586
  .describe("Max number of approval records"),
7235
- cli: z
7236
- .enum(["claude", "codex", "gemini", "grok", "mistral"])
7237
- .optional()
7238
- .describe("Optional CLI filter"),
7587
+ cli: CLI_TYPE_ENUM.optional().describe("Optional CLI filter (any gateway CLI provider, derived from CLI_TYPES)"),
7239
7588
  }, {
7240
7589
  title: "Approval decisions",
7241
7590
  readOnlyHint: true,
@@ -7273,9 +7622,19 @@ export function createGatewayServer(deps = {}) {
7273
7622
  }, async ({ cli }) => {
7274
7623
  const cliInfo = getAvailableCliInfo();
7275
7624
  const apiRuntimes = enabledApiProviders(providers);
7625
+ const buildDiscoveredMap = (clis) => {
7626
+ const out = {};
7627
+ for (const c of clis) {
7628
+ out[c] = buildProviderDiscoveredView(getProviderDefinition(c), cliInfo[c], peekProviderCapabilitySet);
7629
+ }
7630
+ return out;
7631
+ };
7276
7632
  let result;
7277
7633
  if (cli && CLI_TYPES.includes(cli)) {
7278
- result = { [cli]: cliInfo[cli] };
7634
+ result = {
7635
+ [cli]: cliInfo[cli],
7636
+ discovered: buildDiscoveredMap([cli]),
7637
+ };
7279
7638
  }
7280
7639
  else if (cli) {
7281
7640
  const runtime = apiRuntimes.find(p => p.name === cli);
@@ -7283,7 +7642,11 @@ export function createGatewayServer(deps = {}) {
7283
7642
  }
7284
7643
  else {
7285
7644
  const apiProviders = apiRuntimes.map(apiProviderCatalogEntry);
7286
- result = { ...cliInfo, ...(apiProviders.length > 0 ? { apiProviders } : {}) };
7645
+ result = {
7646
+ ...cliInfo,
7647
+ discovered: buildDiscoveredMap(CLI_TYPES),
7648
+ ...(apiProviders.length > 0 ? { apiProviders } : {}),
7649
+ };
7287
7650
  }
7288
7651
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
7289
7652
  });
@@ -7330,6 +7693,7 @@ export function createGatewayServer(deps = {}) {
7330
7693
  includePaths,
7331
7694
  refresh,
7332
7695
  providersConfig: providers,
7696
+ acpConfig: runtime.acpConfig,
7333
7697
  });
7334
7698
  return { content: [{ type: "text", text: JSON.stringify(capabilities, null, 2) }] };
7335
7699
  });
@@ -7875,7 +8239,7 @@ async function initializeSessionManager() {
7875
8239
  });
7876
8240
  logger.info("File-based session manager initialized");
7877
8241
  }
7878
- resourceProvider = new ResourceProvider(sessionManager, performanceMetrics, getFlightRecorder(logger), getCacheAwarenessConfig(logger), getProvidersConfig(logger));
8242
+ resourceProvider = new ResourceProvider(sessionManager, performanceMetrics, getFlightRecorder(logger), getCacheAwarenessConfig(logger), getProvidersConfig(logger), undefined, getAcpConfig(logger));
7879
8243
  }
7880
8244
  function registerHealthResource(server) {
7881
8245
  if (db) {
@@ -8331,6 +8695,7 @@ async function main() {
8331
8695
  "stdio";
8332
8696
  logger.info(`Starting llm-cli-gateway MCP server with ${transportMode} transport`);
8333
8697
  await initializeSessionManager();
8698
+ void warmProviderCapabilities({ logger }).catch(() => undefined);
8334
8699
  const serverDeps = {
8335
8700
  sessionManager,
8336
8701
  resourceProvider,