llm-cli-gateway 2.13.2 → 2.14.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +68 -29
  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 +38 -1
  20. package/dist/async-job-manager.js +287 -20
  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 +19 -0
  24. package/dist/config.js +84 -1
  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 +504 -122
  33. package/dist/job-store.d.ts +119 -11
  34. package/dist/job-store.js +372 -42
  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 +444 -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/sqlite-driver.d.ts +1 -1
  73. package/dist/sqlite-driver.js +2 -1
  74. package/dist/stream-json-parser.d.ts +1 -0
  75. package/dist/stream-json-parser.js +3 -0
  76. package/dist/upstream-contracts.d.ts +17 -1
  77. package/dist/upstream-contracts.js +209 -36
  78. package/npm-shrinkwrap.json +2 -2
  79. package/package.json +8 -3
@@ -176,6 +176,86 @@ export function resolveClaudePermissionFlags(input) {
176
176
  export const GEMINI_APPROVAL_MODES = ["default", "auto_edit", "yolo", "plan"];
177
177
  export const CODEX_SANDBOX_MODES = ["read-only", "workspace-write", "danger-full-access"];
178
178
  export const CODEX_ASK_FOR_APPROVAL_MODES = ["untrusted", "on-request", "never"];
179
+ export const CODEX_LOCAL_PROVIDERS = ["lmstudio", "ollama"];
180
+ export const CODEX_COLOR_MODES = ["always", "never", "auto"];
181
+ export const CLAUDE_UNEXPOSED_CLI_FLAGS = [
182
+ {
183
+ flag: "--tmux",
184
+ reason: "interactive-only",
185
+ detail: "Creates an attached tmux/iTerm2 session for a worktree (requires --worktree); incompatible with headless --print.",
186
+ },
187
+ {
188
+ flag: "--background",
189
+ reason: "gateway-managed",
190
+ detail: "Starts a detached background agent (managed via `claude agents`); conflicts with the gateway's own async-job model.",
191
+ },
192
+ {
193
+ flag: "--remote-control",
194
+ reason: "gateway-managed",
195
+ detail: "Starts an interactive Remote Control session; a stateful/remote surface deferred to the gateway-managed transport.",
196
+ },
197
+ {
198
+ flag: "--remote",
199
+ reason: "admin-deferred",
200
+ detail: "No bare --remote exists in installed Claude help; the only remote surface is --remote-control (gateway-managed).",
201
+ },
202
+ {
203
+ flag: "--worktree",
204
+ reason: "gateway-managed",
205
+ detail: "claude --help advertises -w, --worktree [name], but gateway worktrees are owned by slice λ (worktree-manager); the native --worktree is intentionally never emitted so the gateway owns the checkout lifecycle.",
206
+ },
207
+ {
208
+ flag: "--resume",
209
+ reason: "gateway-managed",
210
+ detail: "claude --help advertises -r, --resume [value] for interactive resume; the gateway session model maps continuity onto --continue (continueSession) and --session-id (sessionId), so the raw --resume passthrough is intentionally not wired.",
211
+ },
212
+ ];
213
+ export const CODEX_UNEXPOSED_CLI_FLAGS = [
214
+ {
215
+ flag: "--remote",
216
+ reason: "admin-deferred",
217
+ detail: "TUI-only (top-level `codex` help, not `codex exec`): connects the TUI to a remote app-server endpoint; not a headless exec flag.",
218
+ },
219
+ {
220
+ flag: "--remote-auth-token-env",
221
+ reason: "admin-deferred",
222
+ detail: "TUI-only companion to --remote (bearer-token env var for the remote app-server websocket); not a headless exec flag.",
223
+ },
224
+ {
225
+ flag: "--ask-for-approval",
226
+ reason: "admin-deferred",
227
+ detail: "Removed from the installed `codex exec` upstream: current codex exec no longer accepts --ask-for-approval. The askForApproval MCP input is retained for back-compat but warns and emits no argv (accepted-but-ignored); classified here because the flag is not present in the installed headless help.",
228
+ },
229
+ {
230
+ flag: "--search",
231
+ reason: "admin-deferred",
232
+ detail: "Removed from the installed `codex exec` upstream: current codex exec no longer accepts --search. The search MCP input is retained for back-compat but warns and emits no argv (accepted-but-ignored); classified here because the flag is not present in the installed headless help.",
233
+ },
234
+ ];
235
+ export const GEMINI_UNEXPOSED_CLI_FLAGS = [
236
+ {
237
+ flag: "--prompt-interactive",
238
+ reason: "interactive-only",
239
+ detail: "Runs an initial prompt then continues in an attached interactive session (short alias -i); incompatible with the headless --print run mode the gateway drives.",
240
+ },
241
+ {
242
+ flag: "--log-file",
243
+ reason: "admin-deferred",
244
+ detail: "Overrides agy's internal CLI log file path (a diagnostic/maintenance surface); the gateway flight recorder is the request-logging surface, so this is deferred to a later admin phase.",
245
+ },
246
+ ];
247
+ export const MISTRAL_UNEXPOSED_CLI_FLAGS = [
248
+ {
249
+ flag: "--setup",
250
+ reason: "admin-deferred",
251
+ detail: "Vibe `--setup` configures the API key and exits; a credential/setup maintenance op, not a headless run flag. Deferred to the phase-6 admin surface.",
252
+ },
253
+ {
254
+ flag: "--check-upgrade",
255
+ reason: "admin-deferred",
256
+ detail: "Vibe `--check-upgrade` checks for a Vibe update, prompts to install, and exits; a maintenance op, not a headless run flag. Deferred to the phase-6 admin surface.",
257
+ },
258
+ ];
179
259
  export function resolveCodexSandboxFlags(input) {
180
260
  const { sandboxMode, askForApproval, fullAuto, useLegacyFullAutoFlag } = input;
181
261
  const args = [];
@@ -324,6 +404,48 @@ export function prepareClaudeHighImpactFlags(input) {
324
404
  if (input.tools && input.tools.length > 0) {
325
405
  args.push("--tools", ...input.tools);
326
406
  }
407
+ if (input.includeHookEvents) {
408
+ args.push("--include-hook-events");
409
+ }
410
+ if (input.replayUserMessages) {
411
+ args.push("--replay-user-messages");
412
+ }
413
+ if (input.systemPromptFile !== undefined) {
414
+ args.push("--system-prompt-file", input.systemPromptFile);
415
+ }
416
+ if (input.appendSystemPromptFile !== undefined) {
417
+ args.push("--append-system-prompt-file", input.appendSystemPromptFile);
418
+ }
419
+ if (input.name !== undefined) {
420
+ args.push("--name", input.name);
421
+ }
422
+ if (input.pluginDir && input.pluginDir.length > 0) {
423
+ for (const dir of input.pluginDir) {
424
+ args.push("--plugin-dir", dir);
425
+ }
426
+ }
427
+ if (input.pluginUrl && input.pluginUrl.length > 0) {
428
+ for (const url of input.pluginUrl) {
429
+ args.push("--plugin-url", url);
430
+ }
431
+ }
432
+ if (input.safeMode) {
433
+ args.push("--safe-mode");
434
+ }
435
+ if (input.bare) {
436
+ args.push("--bare");
437
+ }
438
+ if (input.debug !== undefined && input.debug !== false) {
439
+ if (typeof input.debug === "string") {
440
+ args.push("--debug", input.debug);
441
+ }
442
+ else {
443
+ args.push("--debug");
444
+ }
445
+ }
446
+ if (input.debugFile !== undefined) {
447
+ args.push("--debug-file", input.debugFile);
448
+ }
327
449
  return args;
328
450
  }
329
451
  export const CODEX_CONFIG_OVERRIDES_SCHEMA = z
@@ -382,6 +504,8 @@ export const CODEX_HIGH_IMPACT_PARAMS_SCHEMA = z.object({
382
504
  images: z.array(z.string()).optional(),
383
505
  ignoreUserConfig: z.boolean().optional(),
384
506
  ignoreRules: z.boolean().optional(),
507
+ enable: z.array(z.string()).optional(),
508
+ disable: z.array(z.string()).optional(),
385
509
  });
386
510
  export function prepareCodexHighImpactFlags(input) {
387
511
  const missingImagePath = findMissingImagePath(input.images);
@@ -417,6 +541,16 @@ export function prepareCodexHighImpactFlags(input) {
417
541
  if (input.ignoreRules) {
418
542
  args.push("--ignore-rules");
419
543
  }
544
+ if (input.enable && input.enable.length > 0) {
545
+ for (const feature of input.enable) {
546
+ args.push("--enable", feature);
547
+ }
548
+ }
549
+ if (input.disable && input.disable.length > 0) {
550
+ for (const feature of input.disable) {
551
+ args.push("--disable", feature);
552
+ }
553
+ }
420
554
  return {
421
555
  args,
422
556
  cleanup,
@@ -1,8 +1,11 @@
1
1
  import { ISessionManager } from "./session-manager.js";
2
+ import { type CliType } from "./session-manager.js";
2
3
  import { PerformanceMetrics } from "./metrics.js";
3
4
  import { FlightRecorderQuery } from "./flight-recorder.js";
4
5
  import { type GlobalCacheStats, type PrefixCacheStats, type SessionCacheStats } from "./cache-stats.js";
5
6
  import { type CacheAwarenessConfig, type ProvidersConfig } from "./config.js";
7
+ import type { AcpConfig } from "./config.js";
8
+ import { type ResolvedProviderCapability } from "./provider-capability-resolver.js";
6
9
  export interface ResourceDefinition {
7
10
  uri: string;
8
11
  name: string;
@@ -26,7 +29,9 @@ export declare class ResourceProvider {
26
29
  private flightRecorder;
27
30
  private cacheAwareness;
28
31
  private providers;
29
- constructor(sessionManager: ISessionManager, performanceMetrics: PerformanceMetrics, flightRecorder?: FlightRecorderQuery, cacheAwareness?: CacheAwarenessConfig | null, providers?: ProvidersConfig | null);
32
+ private capabilityPeek;
33
+ private acpConfig;
34
+ constructor(sessionManager: ISessionManager, performanceMetrics: PerformanceMetrics, flightRecorder?: FlightRecorderQuery, cacheAwareness?: CacheAwarenessConfig | null, providers?: ProvidersConfig | null, capabilityPeek?: (id: CliType) => ResolvedProviderCapability | null, acpConfig?: AcpConfig | null);
30
35
  getFlightRecorderQuery(): FlightRecorderQuery;
31
36
  private apiRuntimes;
32
37
  private continuityTrackedApiRuntimes;
package/dist/resources.js CHANGED
@@ -8,18 +8,26 @@ import { apiContinuityForKind } from "./api-provider.js";
8
8
  import { apiProviderCatalogEntry } from "./api-request.js";
9
9
  import { buildProviderSubcommandsCompactCatalog, getCliSubcommandContract, serializeCliSubcommandContract, } from "./upstream-contracts.js";
10
10
  import { getOneProviderToolCapabilities, getProviderToolCapabilities, knownProviderCapabilityIds, providerCapabilityIds, } from "./provider-tool-capabilities.js";
11
+ import { generateProviderAcpDescriptors, generateResourceDescriptors, parseModelsResourceUri, parseProviderAcpResourceUri, parseSessionsResourceUri, } from "./provider-surface-generator.js";
12
+ import { getProviderDefinition } from "./provider-definitions.js";
13
+ import { buildProviderAcpCapabilityRecord } from "./provider-acp-capabilities.js";
14
+ import { buildProviderDiscoveredView, peekProviderCapabilitySet, } from "./provider-capability-resolver.js";
11
15
  export class ResourceProvider {
12
16
  sessionManager;
13
17
  performanceMetrics;
14
18
  flightRecorder;
15
19
  cacheAwareness;
16
20
  providers;
17
- constructor(sessionManager, performanceMetrics, flightRecorder = { queryRequests: () => [] }, cacheAwareness = null, providers = null) {
21
+ capabilityPeek;
22
+ acpConfig;
23
+ constructor(sessionManager, performanceMetrics, flightRecorder = { queryRequests: () => [] }, cacheAwareness = null, providers = null, capabilityPeek = peekProviderCapabilitySet, acpConfig = null) {
18
24
  this.sessionManager = sessionManager;
19
25
  this.performanceMetrics = performanceMetrics;
20
26
  this.flightRecorder = flightRecorder;
21
27
  this.cacheAwareness = cacheAwareness;
22
28
  this.providers = providers;
29
+ this.capabilityPeek = capabilityPeek;
30
+ this.acpConfig = acpConfig;
23
31
  }
24
32
  getFlightRecorderQuery() {
25
33
  return this.flightRecorder;
@@ -61,61 +69,19 @@ export class ResourceProvider {
61
69
  lastModified: new Date().toISOString(),
62
70
  },
63
71
  },
64
- {
65
- uri: "sessions://claude",
66
- name: "Claude Sessions",
67
- title: "🤖 Claude Sessions",
68
- description: "List of Claude conversation sessions",
69
- mimeType: "application/json",
70
- annotations: {
71
- audience: ["user", "assistant"],
72
- priority: 0.6,
73
- },
74
- },
75
- {
76
- uri: "sessions://codex",
77
- name: "Codex Sessions",
78
- title: "💻 Codex Sessions",
79
- description: "List of Codex conversation sessions",
80
- mimeType: "application/json",
81
- annotations: {
82
- audience: ["user", "assistant"],
83
- priority: 0.6,
84
- },
85
- },
86
- {
87
- uri: "sessions://gemini",
88
- name: "Gemini Sessions",
89
- title: "✨ Gemini Sessions",
90
- description: "List of Gemini conversation sessions",
91
- mimeType: "application/json",
92
- annotations: {
93
- audience: ["user", "assistant"],
94
- priority: 0.6,
95
- },
96
- },
97
- {
98
- uri: "sessions://grok",
99
- name: "Grok Sessions",
100
- title: "⚡ Grok Sessions",
101
- description: "List of Grok conversation sessions",
72
+ ...generateResourceDescriptors()
73
+ .filter(descriptor => descriptor.exposesSessionsResource)
74
+ .map(descriptor => ({
75
+ uri: descriptor.sessionsUri,
76
+ name: `${descriptor.sessionLabel}s`,
77
+ title: `${descriptor.icon} ${descriptor.sessionLabel}s`,
78
+ description: `List of ${descriptor.displayName} conversation sessions`,
102
79
  mimeType: "application/json",
103
80
  annotations: {
104
81
  audience: ["user", "assistant"],
105
82
  priority: 0.6,
106
83
  },
107
- },
108
- {
109
- uri: "sessions://mistral",
110
- name: "Mistral Sessions",
111
- title: "🌬 Mistral Sessions",
112
- description: "List of Mistral Vibe conversation sessions",
113
- mimeType: "application/json",
114
- annotations: {
115
- audience: ["user", "assistant"],
116
- priority: 0.6,
117
- },
118
- },
84
+ })),
119
85
  ...this.continuityTrackedApiRuntimes().map(rt => ({
120
86
  uri: `sessions://${rt.name}`,
121
87
  name: `${rt.name} Sessions`,
@@ -127,61 +93,19 @@ export class ResourceProvider {
127
93
  priority: 0.6,
128
94
  },
129
95
  })),
130
- {
131
- uri: "models://claude",
132
- name: "Claude Models",
133
- title: "🧠 Claude Models & Capabilities",
134
- description: "Available Claude models and their capabilities",
135
- mimeType: "application/json",
136
- annotations: {
137
- audience: ["user", "assistant"],
138
- priority: 0.8,
139
- },
140
- },
141
- {
142
- uri: "models://codex",
143
- name: "Codex Models",
144
- title: "🔧 Codex Models & Capabilities",
145
- description: "Available Codex models and their capabilities",
146
- mimeType: "application/json",
147
- annotations: {
148
- audience: ["user", "assistant"],
149
- priority: 0.8,
150
- },
151
- },
152
- {
153
- uri: "models://gemini",
154
- name: "Gemini Models",
155
- title: "🌟 Gemini Models & Capabilities",
156
- description: "Available Gemini models and their capabilities",
157
- mimeType: "application/json",
158
- annotations: {
159
- audience: ["user", "assistant"],
160
- priority: 0.8,
161
- },
162
- },
163
- {
164
- uri: "models://grok",
165
- name: "Grok Models",
166
- title: "⚡ Grok Models & Capabilities",
167
- description: "Available Grok models and their capabilities",
168
- mimeType: "application/json",
169
- annotations: {
170
- audience: ["user", "assistant"],
171
- priority: 0.8,
172
- },
173
- },
174
- {
175
- uri: "models://mistral",
176
- name: "Mistral Models",
177
- title: "🌬 Mistral Models & Capabilities",
178
- description: "Available Mistral Vibe models and their capabilities",
96
+ ...generateResourceDescriptors()
97
+ .filter(descriptor => descriptor.exposesModelsResource)
98
+ .map(descriptor => ({
99
+ uri: descriptor.modelsUri,
100
+ name: `${descriptor.displayName} Models`,
101
+ title: `${descriptor.icon} ${descriptor.displayName} Models & Capabilities`,
102
+ description: `Available ${descriptor.displayName} models and their capabilities`,
179
103
  mimeType: "application/json",
180
104
  annotations: {
181
105
  audience: ["user", "assistant"],
182
106
  priority: 0.8,
183
107
  },
184
- },
108
+ })),
185
109
  ...this.apiRuntimes().map(rt => ({
186
110
  uri: `models://${rt.name}`,
187
111
  name: `${rt.name} Models`,
@@ -237,6 +161,17 @@ export class ResourceProvider {
237
161
  priority: 0.8,
238
162
  },
239
163
  })),
164
+ ...generateProviderAcpDescriptors().map(descriptor => ({
165
+ uri: descriptor.acpUri,
166
+ name: `${descriptor.displayName} ACP Capabilities`,
167
+ title: `${descriptor.icon} ${descriptor.displayName} ACP Capabilities`,
168
+ description: `Native ACP entrypoint, negotiated capabilities, supported session methods, and host-service policy for ${descriptor.displayName}`,
169
+ mimeType: "application/json",
170
+ annotations: {
171
+ audience: ["user", "assistant"],
172
+ priority: 0.7,
173
+ },
174
+ })),
240
175
  ];
241
176
  }
242
177
  ownedSessions(sessions) {
@@ -271,109 +206,28 @@ export class ResourceProvider {
271
206
  }, null, 2),
272
207
  };
273
208
  }
274
- if (uri === "sessions://claude") {
275
- const sessions = this.ownedSessions(await this.sessionManager.listSessions("claude"));
209
+ const sessionProvider = parseSessionsResourceUri(uri);
210
+ if (sessionProvider) {
211
+ const sessions = this.ownedSessions(await this.sessionManager.listSessions(sessionProvider));
276
212
  return {
277
213
  uri,
278
214
  mimeType: "application/json",
279
215
  text: JSON.stringify({
280
- cli: "claude",
216
+ cli: sessionProvider,
281
217
  total: sessions.length,
282
218
  sessions,
283
- activeSession: await this.ownedActiveId("claude"),
219
+ activeSession: await this.ownedActiveId(sessionProvider),
284
220
  }, null, 2),
285
221
  };
286
222
  }
287
- if (uri === "sessions://codex") {
288
- const sessions = this.ownedSessions(await this.sessionManager.listSessions("codex"));
289
- return {
290
- uri,
291
- mimeType: "application/json",
292
- text: JSON.stringify({
293
- cli: "codex",
294
- total: sessions.length,
295
- sessions,
296
- activeSession: await this.ownedActiveId("codex"),
297
- }, null, 2),
298
- };
299
- }
300
- if (uri === "sessions://gemini") {
301
- const sessions = this.ownedSessions(await this.sessionManager.listSessions("gemini"));
302
- return {
303
- uri,
304
- mimeType: "application/json",
305
- text: JSON.stringify({
306
- cli: "gemini",
307
- total: sessions.length,
308
- sessions,
309
- activeSession: await this.ownedActiveId("gemini"),
310
- }, null, 2),
311
- };
312
- }
313
- if (uri === "sessions://grok") {
314
- const sessions = this.ownedSessions(await this.sessionManager.listSessions("grok"));
315
- return {
316
- uri,
317
- mimeType: "application/json",
318
- text: JSON.stringify({
319
- cli: "grok",
320
- total: sessions.length,
321
- sessions,
322
- activeSession: await this.ownedActiveId("grok"),
323
- }, null, 2),
324
- };
325
- }
326
- if (uri === "sessions://mistral") {
327
- const sessions = this.ownedSessions(await this.sessionManager.listSessions("mistral"));
328
- return {
329
- uri,
330
- mimeType: "application/json",
331
- text: JSON.stringify({
332
- cli: "mistral",
333
- total: sessions.length,
334
- sessions,
335
- activeSession: await this.ownedActiveId("mistral"),
336
- }, null, 2),
337
- };
338
- }
339
- if (uri === "models://claude") {
223
+ const modelProvider = parseModelsResourceUri(uri);
224
+ if (modelProvider) {
340
225
  const cliInfo = getAvailableCliInfo();
226
+ const discovered = buildProviderDiscoveredView(getProviderDefinition(modelProvider), cliInfo[modelProvider], this.capabilityPeek);
341
227
  return {
342
228
  uri,
343
229
  mimeType: "application/json",
344
- text: JSON.stringify(cliInfo.claude, null, 2),
345
- };
346
- }
347
- if (uri === "models://codex") {
348
- const cliInfo = getAvailableCliInfo();
349
- return {
350
- uri,
351
- mimeType: "application/json",
352
- text: JSON.stringify(cliInfo.codex, null, 2),
353
- };
354
- }
355
- if (uri === "models://gemini") {
356
- const cliInfo = getAvailableCliInfo();
357
- return {
358
- uri,
359
- mimeType: "application/json",
360
- text: JSON.stringify(cliInfo.gemini, null, 2),
361
- };
362
- }
363
- if (uri === "models://grok") {
364
- const cliInfo = getAvailableCliInfo();
365
- return {
366
- uri,
367
- mimeType: "application/json",
368
- text: JSON.stringify(cliInfo.grok, null, 2),
369
- };
370
- }
371
- if (uri === "models://mistral") {
372
- const cliInfo = getAvailableCliInfo();
373
- return {
374
- uri,
375
- mimeType: "application/json",
376
- text: JSON.stringify(cliInfo.mistral, null, 2),
230
+ text: JSON.stringify({ ...cliInfo[modelProvider], discovered }, null, 2),
377
231
  };
378
232
  }
379
233
  if (uri.startsWith("models://")) {
@@ -420,7 +274,10 @@ export class ResourceProvider {
420
274
  return {
421
275
  uri,
422
276
  mimeType: "application/json",
423
- text: JSON.stringify(getProviderToolCapabilities({ providersConfig: this.providers ?? undefined }), null, 2),
277
+ text: JSON.stringify(getProviderToolCapabilities({
278
+ providersConfig: this.providers ?? undefined,
279
+ acpConfig: this.acpConfig ?? undefined,
280
+ }), null, 2),
424
281
  };
425
282
  }
426
283
  const providerToolsResource = parseProviderToolsUri(uri, this.providerCapabilityIds());
@@ -430,9 +287,24 @@ export class ResourceProvider {
430
287
  mimeType: "application/json",
431
288
  text: JSON.stringify(getOneProviderToolCapabilities(providerToolsResource.provider, {
432
289
  providersConfig: this.providers ?? undefined,
290
+ acpConfig: this.acpConfig ?? undefined,
433
291
  }), null, 2),
434
292
  };
435
293
  }
294
+ const acpProvider = parseProviderAcpResourceUri(uri);
295
+ if (acpProvider) {
296
+ const resolved = this.capabilityPeek(acpProvider);
297
+ const record = buildProviderAcpCapabilityRecord(acpProvider, {
298
+ discovered: resolved?.set.acpInitialize ?? null,
299
+ discoveredDegraded: resolved?.degraded ?? false,
300
+ acpConfig: this.acpConfig,
301
+ });
302
+ return {
303
+ uri,
304
+ mimeType: "application/json",
305
+ text: JSON.stringify(record, null, 2),
306
+ };
307
+ }
436
308
  const subcommandResource = parseProviderSubcommandUri(uri);
437
309
  if (subcommandResource) {
438
310
  const contract = getCliSubcommandContract(subcommandResource.provider, subcommandResource.commandPath);
@@ -6,6 +6,7 @@ import { DEFAULT_SESSION_TTL_SECONDS } from "./config.js";
6
6
  import { noopLogger } from "./logger.js";
7
7
  import { getRequestContext, resolveOwnerPrincipal } from "./request-context.js";
8
8
  import { API_PROVIDER_TYPES, CLI_TYPES, } from "./provider-types.js";
9
+ import { getAllProviderDefinitions } from "./provider-definitions.js";
9
10
  export { API_PROVIDER_TYPES, CLI_TYPES };
10
11
  export const PROVIDER_TYPES = [...CLI_TYPES, ...API_PROVIDER_TYPES];
11
12
  export function isCliType(provider) {
@@ -15,13 +16,7 @@ export function providerKind(provider) {
15
16
  return isCliType(provider) ? "cli" : "api";
16
17
  }
17
18
  const KNOWN_SESSION_DESCRIPTIONS = {
18
- claude: "Claude Session",
19
- codex: "Codex Session",
20
- gemini: "Gemini Session",
21
- grok: "Grok Session",
22
- mistral: "Mistral Session",
23
- devin: "Devin Session",
24
- cursor: "Cursor Session",
19
+ ...Object.fromEntries(getAllProviderDefinitions().map(def => [def.id, def.sessionLabel])),
25
20
  "grok-api": "Grok API Session",
26
21
  };
27
22
  export function defaultSessionDescription(provider) {
@@ -33,11 +28,23 @@ export function remoteSafeSession(session) {
33
28
  if (!metadata)
34
29
  return session;
35
30
  const next = { ...metadata };
31
+ const root = typeof next.workspaceRoot === "string" ? next.workspaceRoot : undefined;
32
+ const reducePath = (absolute) => {
33
+ const rel = root ? pathRelative(root, absolute) : "";
34
+ return rel && !rel.startsWith("..") && !pathIsAbsolute(rel) ? rel : pathBasename(absolute);
35
+ };
36
36
  if (typeof next.worktreePath === "string") {
37
- const root = typeof next.workspaceRoot === "string" ? next.workspaceRoot : undefined;
38
- const rel = root ? pathRelative(root, next.worktreePath) : "";
39
- next.worktreePath =
40
- rel && !rel.startsWith("..") && !pathIsAbsolute(rel) ? rel : pathBasename(next.worktreePath);
37
+ next.worktreePath = reducePath(next.worktreePath);
38
+ }
39
+ if (next.acp && typeof next.acp === "object" && !Array.isArray(next.acp)) {
40
+ const acp = { ...next.acp };
41
+ if (typeof acp.cwd === "string")
42
+ acp.cwd = reducePath(acp.cwd);
43
+ if (typeof acp.worktreePath === "string")
44
+ acp.worktreePath = reducePath(acp.worktreePath);
45
+ if ("sessionId" in acp)
46
+ delete acp.sessionId;
47
+ next.acp = acp;
41
48
  }
42
49
  if (typeof next.workspaceRoot === "string")
43
50
  delete next.workspaceRoot;
@@ -9,7 +9,7 @@ export interface GatewayStatement {
9
9
  export interface GatewayDatabase {
10
10
  exec(sql: string): void;
11
11
  prepare(sql: string): GatewayStatement;
12
- withTransaction<A extends unknown[]>(fn: (...args: A) => void): (...args: A) => void;
12
+ withTransaction<A extends unknown[], R = void>(fn: (...args: A) => R): (...args: A) => R;
13
13
  close(): void;
14
14
  }
15
15
  export declare function openDatabase(dbPath: string): GatewayDatabase;
@@ -115,8 +115,9 @@ class GatewayDatabaseImpl {
115
115
  this.db.exec("BEGIN");
116
116
  this.inTransaction = true;
117
117
  try {
118
- fn(...args);
118
+ const result = fn(...args);
119
119
  this.db.exec("COMMIT");
120
+ return result;
120
121
  }
121
122
  catch (error) {
122
123
  try {
@@ -13,5 +13,6 @@ export interface StreamJsonResult {
13
13
  durationApiMs: number | null;
14
14
  isError: boolean;
15
15
  numTurns: number | null;
16
+ stopReason: string | null;
16
17
  }
17
18
  export declare function parseStreamJson(stdout: string): StreamJsonResult;
@@ -51,6 +51,7 @@ export function parseStreamJson(stdout) {
51
51
  durationApiMs: numberOrNull(resultEvent.duration_api_ms),
52
52
  isError: resultEvent.is_error === true,
53
53
  numTurns: numberOrNull(resultEvent.num_turns),
54
+ stopReason: stringOrNull(resultEvent.stop_reason) ?? stringOrNull(assistantEvent?.message?.stop_reason),
54
55
  };
55
56
  }
56
57
  if (assistantEvent) {
@@ -74,6 +75,7 @@ export function parseStreamJson(stdout) {
74
75
  durationApiMs: null,
75
76
  isError: false,
76
77
  numTurns: null,
78
+ stopReason: stringOrNull(message?.stop_reason),
77
79
  };
78
80
  }
79
81
  return {
@@ -85,5 +87,6 @@ export function parseStreamJson(stdout) {
85
87
  durationApiMs: null,
86
88
  isError: false,
87
89
  numTurns: null,
90
+ stopReason: null,
88
91
  };
89
92
  }
@@ -1,4 +1,4 @@
1
- import type { CliType } from "./session-manager.js";
1
+ import { type CliType } from "./provider-definitions.js";
2
2
  export type CliFlagArity = "none" | "one" | "optional" | "variadic";
3
3
  export interface CliFlagContract {
4
4
  arity: CliFlagArity;
@@ -199,3 +199,19 @@ export declare function buildUpstreamContractReport(options?: {
199
199
  cli?: CliType;
200
200
  probeInstalled?: boolean;
201
201
  }): Record<string, unknown>;
202
+ export interface DiscoveryContractDrift {
203
+ cli: CliType;
204
+ version: string;
205
+ contractTargetVersion: string;
206
+ versionMatchesContract: boolean;
207
+ missingContractFlags: string[];
208
+ newDiscoveredFlags: string[];
209
+ discoveredUnmappedCount: number;
210
+ status: "clean" | "drift" | "degraded";
211
+ }
212
+ export declare function computeDiscoveryContractDrift(cli: CliType, discovered: {
213
+ version: string;
214
+ discoveredFlagNames: readonly string[];
215
+ discoveredUnmappedCount: number;
216
+ status: "ok" | "degraded" | "error";
217
+ }): DiscoveryContractDrift;