llm-cli-gateway 2.6.3 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.md +55 -9
- package/dist/acp/client.d.ts +78 -0
- package/dist/acp/client.js +201 -0
- package/dist/acp/errors.d.ts +63 -0
- package/dist/acp/errors.js +139 -0
- package/dist/acp/json-rpc-stdio.d.ts +71 -0
- package/dist/acp/json-rpc-stdio.js +375 -0
- package/dist/acp/process-manager.d.ts +66 -0
- package/dist/acp/process-manager.js +364 -0
- package/dist/acp/provider-registry.d.ts +24 -0
- package/dist/acp/provider-registry.js +82 -0
- package/dist/acp/types.d.ts +557 -0
- package/dist/acp/types.js +335 -0
- package/dist/async-job-manager.d.ts +2 -0
- package/dist/async-job-manager.js +45 -16
- package/dist/cache-stats.js +17 -10
- package/dist/codex-json-parser.d.ts +3 -0
- package/dist/codex-json-parser.js +17 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +119 -0
- package/dist/doctor.d.ts +22 -0
- package/dist/doctor.js +45 -0
- package/dist/http-transport.js +2 -1
- package/dist/index.js +78 -15
- package/dist/pricing.d.ts +1 -1
- package/dist/pricing.js +67 -2
- package/dist/provider-tool-capabilities.d.ts +135 -0
- package/dist/provider-tool-capabilities.js +1280 -0
- package/dist/request-context.d.ts +1 -0
- package/dist/request-helpers.d.ts +4 -4
- package/dist/resources.js +51 -0
- package/dist/upstream-contracts.d.ts +27 -0
- package/dist/upstream-contracts.js +131 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/setup/status.schema.json +67 -6
- package/socket.yml +25 -2
package/dist/config.js
CHANGED
|
@@ -348,6 +348,125 @@ export function isXaiProviderEnabled(config, env = process.env) {
|
|
|
348
348
|
return false;
|
|
349
349
|
return typeof env[keyEnv] === "string" && env[keyEnv].trim().length > 0;
|
|
350
350
|
}
|
|
351
|
+
export const ACP_TRANSPORTS = ["cli", "acp"];
|
|
352
|
+
export const DEFAULT_ACP_PROCESS_IDLE_TIMEOUT_MS = 600000;
|
|
353
|
+
export const DEFAULT_ACP_INITIALIZE_TIMEOUT_MS = 10000;
|
|
354
|
+
export const DEFAULT_ACP_SESSION_NEW_TIMEOUT_MS = 10000;
|
|
355
|
+
export const DEFAULT_ACP_PROMPT_TIMEOUT_MS = 600000;
|
|
356
|
+
const SHELL_METACHARACTERS = /[\s|&;<>(){}$`"'\\*?[\]~#!]/;
|
|
357
|
+
function isSafeExecutable(value) {
|
|
358
|
+
if (value.length === 0)
|
|
359
|
+
return false;
|
|
360
|
+
return !SHELL_METACHARACTERS.test(value);
|
|
361
|
+
}
|
|
362
|
+
const SafeExecutableSchema = z
|
|
363
|
+
.string()
|
|
364
|
+
.min(1)
|
|
365
|
+
.refine(isSafeExecutable, {
|
|
366
|
+
message: "ACP provider command must be a bare executable name or path with no shell metacharacters " +
|
|
367
|
+
"(no spaces, quotes, pipes, redirects, globs, or command substitution); pass arguments via 'args'",
|
|
368
|
+
});
|
|
369
|
+
const SafeArgSchema = z.string();
|
|
370
|
+
const AcpProviderSchema = z
|
|
371
|
+
.object({
|
|
372
|
+
enabled: z.boolean().default(false),
|
|
373
|
+
command: SafeExecutableSchema,
|
|
374
|
+
args: z.array(SafeArgSchema).default([]),
|
|
375
|
+
runtime_enabled: z.boolean().default(false),
|
|
376
|
+
isolated_leader_socket: z.boolean().default(false),
|
|
377
|
+
})
|
|
378
|
+
.strict();
|
|
379
|
+
const AcpConfigSchema = z
|
|
380
|
+
.object({
|
|
381
|
+
enabled: z.boolean().default(false),
|
|
382
|
+
default_transport: z.enum(ACP_TRANSPORTS).default("cli"),
|
|
383
|
+
smoke_on_startup: z.boolean().default(false),
|
|
384
|
+
process_idle_timeout_ms: z
|
|
385
|
+
.number()
|
|
386
|
+
.int()
|
|
387
|
+
.positive()
|
|
388
|
+
.default(DEFAULT_ACP_PROCESS_IDLE_TIMEOUT_MS),
|
|
389
|
+
initialize_timeout_ms: z.number().int().positive().default(DEFAULT_ACP_INITIALIZE_TIMEOUT_MS),
|
|
390
|
+
session_new_timeout_ms: z.number().int().positive().default(DEFAULT_ACP_SESSION_NEW_TIMEOUT_MS),
|
|
391
|
+
prompt_timeout_ms: z.number().int().positive().default(DEFAULT_ACP_PROMPT_TIMEOUT_MS),
|
|
392
|
+
allow_write_host_services: z.boolean().default(false),
|
|
393
|
+
allow_terminal_host_services: z.boolean().default(false),
|
|
394
|
+
fallback_to_cli_when_unhealthy: z.boolean().default(true),
|
|
395
|
+
providers: z.record(z.string(), AcpProviderSchema).default({}),
|
|
396
|
+
})
|
|
397
|
+
.strict();
|
|
398
|
+
function defaultAcpConfig(sourcePath) {
|
|
399
|
+
return {
|
|
400
|
+
enabled: false,
|
|
401
|
+
defaultTransport: "cli",
|
|
402
|
+
smokeOnStartup: false,
|
|
403
|
+
processIdleTimeoutMs: DEFAULT_ACP_PROCESS_IDLE_TIMEOUT_MS,
|
|
404
|
+
initializeTimeoutMs: DEFAULT_ACP_INITIALIZE_TIMEOUT_MS,
|
|
405
|
+
sessionNewTimeoutMs: DEFAULT_ACP_SESSION_NEW_TIMEOUT_MS,
|
|
406
|
+
promptTimeoutMs: DEFAULT_ACP_PROMPT_TIMEOUT_MS,
|
|
407
|
+
allowWriteHostServices: false,
|
|
408
|
+
allowTerminalHostServices: false,
|
|
409
|
+
fallbackToCliWhenUnhealthy: true,
|
|
410
|
+
providers: {},
|
|
411
|
+
sources: { configFile: sourcePath },
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function readAcpFile(configPath, logger) {
|
|
415
|
+
if (!existsSync(configPath)) {
|
|
416
|
+
return { raw: undefined, sourcePath: null };
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
const require = createRequire(import.meta.url);
|
|
420
|
+
const TOML = require("smol-toml");
|
|
421
|
+
const text = readFileSync(configPath, "utf-8");
|
|
422
|
+
const parsed = TOML.parse(text);
|
|
423
|
+
return { raw: parsed?.acp, sourcePath: configPath };
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
logger.error(`Failed to parse gateway config at ${configPath}; using acp defaults`, err);
|
|
427
|
+
return { raw: undefined, sourcePath: null };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
export function loadAcpConfig(logger = noopLogger) {
|
|
431
|
+
const configPath = defaultGatewayConfigPath();
|
|
432
|
+
const { raw, sourcePath } = readAcpFile(configPath, logger);
|
|
433
|
+
if (raw === undefined) {
|
|
434
|
+
return defaultAcpConfig(sourcePath);
|
|
435
|
+
}
|
|
436
|
+
let parsed;
|
|
437
|
+
try {
|
|
438
|
+
parsed = AcpConfigSchema.parse(raw);
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
throw new Error(`Invalid [acp] config: ${err instanceof Error ? err.message : String(err)}`, {
|
|
442
|
+
cause: err,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
const providers = {};
|
|
446
|
+
for (const [name, p] of Object.entries(parsed.providers)) {
|
|
447
|
+
providers[name] = {
|
|
448
|
+
enabled: p.enabled,
|
|
449
|
+
command: p.command,
|
|
450
|
+
args: p.args,
|
|
451
|
+
runtimeEnabled: p.runtime_enabled,
|
|
452
|
+
isolatedLeaderSocket: p.isolated_leader_socket,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
enabled: parsed.enabled,
|
|
457
|
+
defaultTransport: parsed.default_transport,
|
|
458
|
+
smokeOnStartup: parsed.smoke_on_startup,
|
|
459
|
+
processIdleTimeoutMs: parsed.process_idle_timeout_ms,
|
|
460
|
+
initializeTimeoutMs: parsed.initialize_timeout_ms,
|
|
461
|
+
sessionNewTimeoutMs: parsed.session_new_timeout_ms,
|
|
462
|
+
promptTimeoutMs: parsed.prompt_timeout_ms,
|
|
463
|
+
allowWriteHostServices: parsed.allow_write_host_services,
|
|
464
|
+
allowTerminalHostServices: parsed.allow_terminal_host_services,
|
|
465
|
+
fallbackToCliWhenUnhealthy: parsed.fallback_to_cli_when_unhealthy,
|
|
466
|
+
providers,
|
|
467
|
+
sources: { configFile: sourcePath },
|
|
468
|
+
};
|
|
469
|
+
}
|
|
351
470
|
const OAuthRegistrationPolicySchema = z.enum(["static_clients", "shared_secret", "open_dev"]);
|
|
352
471
|
const OAuthClientSchema = z
|
|
353
472
|
.object({
|
package/dist/doctor.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type EndpointExposureReport } from "./endpoint-exposure.js";
|
|
|
2
2
|
import { type ProviderLoginStatus } from "./provider-status.js";
|
|
3
3
|
import type { FlightRecorderQuery } from "./flight-recorder.js";
|
|
4
4
|
import { type CacheAwarenessConfig } from "./config.js";
|
|
5
|
+
import { type ProviderCapabilityId, type ProviderKind } from "./provider-tool-capabilities.js";
|
|
5
6
|
export type CliType = "claude" | "codex" | "gemini" | "grok" | "mistral";
|
|
6
7
|
export interface CacheAwarenessReport {
|
|
7
8
|
enabled_features: Array<"anthropic_cache_control" | "ttl_warnings">;
|
|
@@ -17,6 +18,26 @@ export interface CacheAwarenessReport {
|
|
|
17
18
|
total_cache_read_tokens: number;
|
|
18
19
|
}>>;
|
|
19
20
|
}
|
|
21
|
+
export interface ProviderCapabilitySummaryReport {
|
|
22
|
+
schema_version: "provider-tool-capabilities.v2";
|
|
23
|
+
tool: "provider_tool_capabilities";
|
|
24
|
+
resources: {
|
|
25
|
+
catalog: "provider-tools://catalog";
|
|
26
|
+
providers: Record<ProviderCapabilityId, string>;
|
|
27
|
+
};
|
|
28
|
+
cache_ttl_ms: number;
|
|
29
|
+
providers: Record<ProviderCapabilityId, {
|
|
30
|
+
provider_kind: ProviderKind;
|
|
31
|
+
cli_available: boolean;
|
|
32
|
+
gateway_request_tools: string[];
|
|
33
|
+
supported_features: string[];
|
|
34
|
+
unsupported_inputs: string[];
|
|
35
|
+
config_surface_count: number;
|
|
36
|
+
discovered_skill_count: number;
|
|
37
|
+
discovered_provider_tool_count: number;
|
|
38
|
+
warnings: string[];
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
20
41
|
export interface VibeSessionLoggingStatus {
|
|
21
42
|
config_path: string;
|
|
22
43
|
config_present: boolean;
|
|
@@ -116,6 +137,7 @@ export interface DoctorReport {
|
|
|
116
137
|
vibe_session_logging: VibeSessionLoggingStatus;
|
|
117
138
|
};
|
|
118
139
|
cache_awareness: CacheAwarenessReport;
|
|
140
|
+
provider_capabilities: ProviderCapabilitySummaryReport;
|
|
119
141
|
upstream: {
|
|
120
142
|
note: string;
|
|
121
143
|
recommendation: string;
|
package/dist/doctor.js
CHANGED
|
@@ -11,6 +11,7 @@ import { loadWorkspaceRegistry } from "./workspace-registry.js";
|
|
|
11
11
|
import { computeGlobalCacheStats } from "./cache-stats.js";
|
|
12
12
|
import { FlightRecorder, resolveFlightRecorderDbPath } from "./flight-recorder.js";
|
|
13
13
|
import { buildUpstreamContractReport } from "./upstream-contracts.js";
|
|
14
|
+
import { getProviderToolCapabilities, providerCapabilityIds, } from "./provider-tool-capabilities.js";
|
|
14
15
|
export function checkVibeSessionLogging(home = homedir()) {
|
|
15
16
|
const configPath = join(home, ".vibe", "config.toml");
|
|
16
17
|
if (!existsSync(configPath)) {
|
|
@@ -226,6 +227,49 @@ function buildCacheAwarenessReport(opts) {
|
|
|
226
227
|
per_cli: perCli,
|
|
227
228
|
};
|
|
228
229
|
}
|
|
230
|
+
function buildProviderCapabilitySummary(providerStatuses) {
|
|
231
|
+
const capabilities = getProviderToolCapabilities({
|
|
232
|
+
includeSkills: true,
|
|
233
|
+
includeProviderTools: true,
|
|
234
|
+
includeUnsupported: true,
|
|
235
|
+
includePaths: false,
|
|
236
|
+
});
|
|
237
|
+
const providers = Object.fromEntries(providerCapabilityIds().map(provider => {
|
|
238
|
+
const capability = capabilities[provider];
|
|
239
|
+
if (!capability) {
|
|
240
|
+
throw new Error(`Missing provider capability record for ${provider}`);
|
|
241
|
+
}
|
|
242
|
+
const cliAvailable = provider === "grok_api"
|
|
243
|
+
? capability.gatewayRequestTools.includes("grok_api_request")
|
|
244
|
+
: providerStatuses[provider].installed;
|
|
245
|
+
return [
|
|
246
|
+
provider,
|
|
247
|
+
{
|
|
248
|
+
provider_kind: capability.providerKind,
|
|
249
|
+
cli_available: cliAvailable,
|
|
250
|
+
gateway_request_tools: capability.gatewayRequestTools,
|
|
251
|
+
supported_features: Object.entries(capability.features)
|
|
252
|
+
.filter(([, feature]) => feature.supported)
|
|
253
|
+
.map(([name]) => name),
|
|
254
|
+
unsupported_inputs: capability.unsupportedInputs.map(input => input.input),
|
|
255
|
+
config_surface_count: capability.configSurfaces.length,
|
|
256
|
+
discovered_skill_count: capability.discoveredSkills.length,
|
|
257
|
+
discovered_provider_tool_count: capability.discoveredProviderTools.length,
|
|
258
|
+
warnings: capability.warnings,
|
|
259
|
+
},
|
|
260
|
+
];
|
|
261
|
+
}));
|
|
262
|
+
return {
|
|
263
|
+
schema_version: "provider-tool-capabilities.v2",
|
|
264
|
+
tool: "provider_tool_capabilities",
|
|
265
|
+
resources: {
|
|
266
|
+
catalog: "provider-tools://catalog",
|
|
267
|
+
providers: Object.fromEntries(providerCapabilityIds().map(provider => [provider, `provider-tools://${provider}`])),
|
|
268
|
+
},
|
|
269
|
+
cache_ttl_ms: 60_000,
|
|
270
|
+
providers,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
229
273
|
export function createDoctorReport(envOrOptions = process.env) {
|
|
230
274
|
const opts = isCreateDoctorReportOptions(envOrOptions)
|
|
231
275
|
? envOrOptions
|
|
@@ -316,6 +360,7 @@ export function createDoctorReport(envOrOptions = process.env) {
|
|
|
316
360
|
endpoint_exposure: endpointExposure,
|
|
317
361
|
client_config: clientConfigStatus(),
|
|
318
362
|
cache_awareness: buildCacheAwarenessReport(opts),
|
|
363
|
+
provider_capabilities: buildProviderCapabilitySummary(providerStatuses),
|
|
319
364
|
upstream,
|
|
320
365
|
next_actions: [],
|
|
321
366
|
};
|
package/dist/http-transport.js
CHANGED
|
@@ -155,7 +155,7 @@ export async function startHttpGateway(options) {
|
|
|
155
155
|
jsonError(res, 404, "Not found");
|
|
156
156
|
return;
|
|
157
157
|
}
|
|
158
|
-
let requestContext = { authScopes: [] };
|
|
158
|
+
let requestContext = { authScopes: [], transport: "http" };
|
|
159
159
|
if (!noAuthPath) {
|
|
160
160
|
const auth = authorizeBearerRequest(req, token);
|
|
161
161
|
if (!auth.ok) {
|
|
@@ -163,6 +163,7 @@ export async function startHttpGateway(options) {
|
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
requestContext = {
|
|
166
|
+
transport: "http",
|
|
166
167
|
authKind: auth.kind,
|
|
167
168
|
authScopes: auth.scopes ?? [],
|
|
168
169
|
authClientId: auth.clientId,
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from "url";
|
|
|
9
9
|
import { z } from "zod/v3";
|
|
10
10
|
import { executeCli, killAllProcessGroups, providerCommandName } from "./executor.js";
|
|
11
11
|
import { parseStreamJson } from "./stream-json-parser.js";
|
|
12
|
-
import { parseCodexJsonStream } from "./codex-json-parser.js";
|
|
12
|
+
import { parseCodexJsonStream, codexDisplayText, codexFrResponse } from "./codex-json-parser.js";
|
|
13
13
|
import { parseGeminiJson, parseGeminiStreamJson } from "./gemini-json-parser.js";
|
|
14
14
|
import { parseVibeMetaJson } from "./mistral-meta-json-parser.js";
|
|
15
15
|
import { homedir } from "os";
|
|
@@ -22,6 +22,7 @@ import { loadConfig, loadPersistenceConfig, loadCacheAwarenessConfig, loadProvid
|
|
|
22
22
|
import { createXaiResponse, XaiApiError, } from "./xai-api-provider.js";
|
|
23
23
|
import { checkHealth } from "./health.js";
|
|
24
24
|
import { clearModelRegistryCache, getAvailableCliInfo, getCliInfo, resolveModelAlias, } from "./model-registry.js";
|
|
25
|
+
import { getProviderToolCapabilities } from "./provider-tool-capabilities.js";
|
|
25
26
|
import { AsyncJobManager, } from "./async-job-manager.js";
|
|
26
27
|
import { createJobStore } from "./job-store.js";
|
|
27
28
|
import { ApprovalManager } from "./approval-manager.js";
|
|
@@ -488,13 +489,10 @@ async function resolveWorkspaceAndWorktreeForRequest(args) {
|
|
|
488
489
|
else if (isGatewayAppDirCwd()) {
|
|
489
490
|
throw new Error("No workspace selected. Configure [workspaces].default or pass a registered workspace alias.");
|
|
490
491
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
(args.workingDir || (args.addDir?.length ?? 0) > 0) &&
|
|
496
|
-
!args.runtime.workspaces.allowUnregisteredWorkingDir) {
|
|
497
|
-
throw new Error("workingDir/addDir require a registered workspace alias unless [workspaces].allow_unregistered_working_dir is explicitly enabled.");
|
|
492
|
+
const requestContext = getRequestContext();
|
|
493
|
+
const isRemoteTransport = requestContext?.transport === "http" || requestContext?.authKind === "oauth";
|
|
494
|
+
if (!workspace && isRemoteTransport) {
|
|
495
|
+
throw new Error("Remote HTTP provider requests require a registered workspace alias, session workspace, or [workspaces].default.");
|
|
498
496
|
}
|
|
499
497
|
if (workspace) {
|
|
500
498
|
if (args.workingDir) {
|
|
@@ -719,7 +717,7 @@ export function extractUsageAndCost(cli, output, outputFormat, ctx) {
|
|
|
719
717
|
costUsd: parsed.costUsd ?? undefined,
|
|
720
718
|
};
|
|
721
719
|
}
|
|
722
|
-
if (cli === "codex"
|
|
720
|
+
if (cli === "codex") {
|
|
723
721
|
const parsed = parseCodexJsonStream(output);
|
|
724
722
|
if (!parsed.usage) {
|
|
725
723
|
return {};
|
|
@@ -1049,6 +1047,27 @@ function registerBaseResources(server, runtime) {
|
|
|
1049
1047
|
const contents = await runtime.resourceProvider.readResource(uri.href);
|
|
1050
1048
|
return { contents: contents ? [contents] : [] };
|
|
1051
1049
|
});
|
|
1050
|
+
server.registerResource("provider-tools-catalog", "provider-tools://catalog", {
|
|
1051
|
+
title: "Provider Tool Capabilities Catalog",
|
|
1052
|
+
description: "Read-only catalog of gateway tool controls and discovered provider skills",
|
|
1053
|
+
mimeType: "application/json",
|
|
1054
|
+
}, async (uri) => {
|
|
1055
|
+
runtime.logger.debug("Reading provider-tools://catalog resource");
|
|
1056
|
+
const contents = await runtime.resourceProvider.readResource(uri.href);
|
|
1057
|
+
return { contents: contents ? [contents] : [] };
|
|
1058
|
+
});
|
|
1059
|
+
server.registerResource("provider-tools", new ResourceTemplate("provider-tools://{provider}", { list: undefined }), {
|
|
1060
|
+
title: "Provider Tool Capabilities",
|
|
1061
|
+
description: "Read-only gateway tool controls and discovered local skills for one provider CLI",
|
|
1062
|
+
mimeType: "application/json",
|
|
1063
|
+
}, async (uri, variables) => {
|
|
1064
|
+
const provider = Array.isArray(variables.provider)
|
|
1065
|
+
? variables.provider[0]
|
|
1066
|
+
: variables.provider;
|
|
1067
|
+
runtime.logger.debug(`Reading provider-tools://${provider}`);
|
|
1068
|
+
const contents = await runtime.resourceProvider.readResource(uri.href);
|
|
1069
|
+
return { contents: contents ? [contents] : [] };
|
|
1070
|
+
});
|
|
1052
1071
|
}
|
|
1053
1072
|
function resolvePromptOrPartsForPrep(args) {
|
|
1054
1073
|
const hasPrompt = typeof args.prompt === "string" && args.prompt.length > 0;
|
|
@@ -1398,9 +1417,7 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
|
|
|
1398
1417
|
if (params.dangerouslyBypassApprovalsAndSandbox) {
|
|
1399
1418
|
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
1400
1419
|
}
|
|
1401
|
-
|
|
1402
|
-
args.push("--json");
|
|
1403
|
-
}
|
|
1420
|
+
args.push("--json");
|
|
1404
1421
|
args.push("--skip-git-repo-check");
|
|
1405
1422
|
let highImpactCleanup;
|
|
1406
1423
|
if (sessionPlan.mode === "new") {
|
|
@@ -1808,6 +1825,9 @@ export function buildMistralRetryPrep(params, recoveryModel) {
|
|
|
1808
1825
|
}
|
|
1809
1826
|
function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionId, prep, durationMs, resumable, outputFormat, warnings) {
|
|
1810
1827
|
let finalStdout = stdout;
|
|
1828
|
+
if (cli === "codex" && outputFormat !== "json") {
|
|
1829
|
+
finalStdout = codexDisplayText(stdout);
|
|
1830
|
+
}
|
|
1811
1831
|
if (optimizeResponse && outputFormat !== "json") {
|
|
1812
1832
|
const optimized = optimizeResponseText(finalStdout);
|
|
1813
1833
|
logOptimizationTokens("response", corrId, finalStdout, optimized);
|
|
@@ -3597,7 +3617,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3597
3617
|
outputFormat: z
|
|
3598
3618
|
.enum(["text", "json"])
|
|
3599
3619
|
.default("text")
|
|
3600
|
-
.describe("Codex output format.
|
|
3620
|
+
.describe("Codex caller-facing output format. Token/cache usage is recorded in the flight recorder regardless. `text` (default) returns the plain reply; `json` returns the raw `--json` JSONL event stream."),
|
|
3601
3621
|
outputSchema: z
|
|
3602
3622
|
.union([z.string(), z.record(z.string(), z.unknown())])
|
|
3603
3623
|
.optional()
|
|
@@ -3757,7 +3777,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3757
3777
|
logger.info(`[${corrId}] codex_request completed successfully in ${durationMs}ms`);
|
|
3758
3778
|
const codexUsage = extractUsageAndCost("codex", stdout, outputFormat);
|
|
3759
3779
|
safeFlightComplete(corrId, {
|
|
3760
|
-
response: stdout,
|
|
3780
|
+
response: codexFrResponse(outputFormat, stdout),
|
|
3761
3781
|
durationMs,
|
|
3762
3782
|
retryCount: 0,
|
|
3763
3783
|
circuitBreakerState: "closed",
|
|
@@ -4600,7 +4620,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4600
4620
|
outputFormat: z
|
|
4601
4621
|
.enum(["text", "json"])
|
|
4602
4622
|
.default("text")
|
|
4603
|
-
.describe("Codex output format. `
|
|
4623
|
+
.describe("Codex caller-facing output format. Token/cache usage is recorded in the flight recorder regardless. `text` (default) returns the plain reply; `json` returns the raw `--json` JSONL event stream."),
|
|
4604
4624
|
outputSchema: z
|
|
4605
4625
|
.union([z.string(), z.record(z.string(), z.unknown())])
|
|
4606
4626
|
.optional()
|
|
@@ -5202,6 +5222,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5202
5222
|
if (outputFormat === "stream-json" && result.stdout) {
|
|
5203
5223
|
parsed = parseStreamJson(result.stdout);
|
|
5204
5224
|
}
|
|
5225
|
+
if (asyncJobManager.getJobCli(jobId) === "codex" &&
|
|
5226
|
+
outputFormat !== "json" &&
|
|
5227
|
+
result.stdout) {
|
|
5228
|
+
result.stdout = codexDisplayText(result.stdout);
|
|
5229
|
+
}
|
|
5205
5230
|
return {
|
|
5206
5231
|
content: [
|
|
5207
5232
|
{
|
|
@@ -5415,6 +5440,44 @@ export function createGatewayServer(deps = {}) {
|
|
|
5415
5440
|
const result = cli ? { [cli]: cliInfo[cli] } : cliInfo;
|
|
5416
5441
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
5417
5442
|
});
|
|
5443
|
+
server.tool("provider_tool_capabilities", "Report provider tool/feature capabilities and discovered local skill/tool integrations for claude|codex|gemini|grok|grok_api|mistral.", {
|
|
5444
|
+
cli: z
|
|
5445
|
+
.preprocess(value => (value === "" || value === null ? undefined : value), z.enum(["claude", "codex", "gemini", "grok", "grok_api", "mistral"]).optional())
|
|
5446
|
+
.describe("Provider filter (claude|codex|gemini|grok|grok_api|mistral)"),
|
|
5447
|
+
includeSkills: z
|
|
5448
|
+
.boolean()
|
|
5449
|
+
.default(true)
|
|
5450
|
+
.describe("Include bounded local skill discovery results"),
|
|
5451
|
+
includeProviderTools: z
|
|
5452
|
+
.boolean()
|
|
5453
|
+
.default(true)
|
|
5454
|
+
.describe("Include provider-native tools extracted from local skills"),
|
|
5455
|
+
includeUnsupported: z
|
|
5456
|
+
.boolean()
|
|
5457
|
+
.default(true)
|
|
5458
|
+
.describe("Include explicit unsupported/degraded input records"),
|
|
5459
|
+
includePaths: z
|
|
5460
|
+
.boolean()
|
|
5461
|
+
.default(false)
|
|
5462
|
+
.describe("Include raw local filesystem paths in discovery output"),
|
|
5463
|
+
refresh: z.boolean().default(false).describe("Bypass the short-lived capability cache"),
|
|
5464
|
+
}, {
|
|
5465
|
+
title: "Provider tool capabilities",
|
|
5466
|
+
readOnlyHint: true,
|
|
5467
|
+
destructiveHint: false,
|
|
5468
|
+
idempotentHint: true,
|
|
5469
|
+
openWorldHint: false,
|
|
5470
|
+
}, async ({ cli, includeSkills, includeProviderTools, includeUnsupported, includePaths, refresh, }) => {
|
|
5471
|
+
const capabilities = getProviderToolCapabilities({
|
|
5472
|
+
cli,
|
|
5473
|
+
includeSkills,
|
|
5474
|
+
includeProviderTools,
|
|
5475
|
+
includeUnsupported,
|
|
5476
|
+
includePaths,
|
|
5477
|
+
refresh,
|
|
5478
|
+
});
|
|
5479
|
+
return { content: [{ type: "text", text: JSON.stringify(capabilities, null, 2) }] };
|
|
5480
|
+
});
|
|
5418
5481
|
server.tool("cli_versions", "Report installed provider CLI versions, availability, and login status for all five providers or one.", {
|
|
5419
5482
|
cli: z
|
|
5420
5483
|
.preprocess(value => (value === "" || value === null ? undefined : value), z.enum(["claude", "codex", "gemini", "grok", "mistral"]).optional())
|
package/dist/pricing.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ export interface PricePerMillion {
|
|
|
3
3
|
outputUsd: number;
|
|
4
4
|
cacheReadMultiplier: number;
|
|
5
5
|
}
|
|
6
|
-
export declare const PRICING_AS_OF = "2026-
|
|
6
|
+
export declare const PRICING_AS_OF = "2026-06-13";
|
|
7
7
|
export declare function getPricing(cli: "claude" | "codex" | "gemini" | "grok" | "mistral", model: string): PricePerMillion;
|
|
8
8
|
export declare function estimateCacheSavingsUsd(cli: "claude" | "codex" | "gemini" | "grok" | "mistral", model: string, cacheReadTokens: number): number;
|
package/dist/pricing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const PRICING_AS_OF = "2026-
|
|
1
|
+
export const PRICING_AS_OF = "2026-06-13";
|
|
2
2
|
const ANTHROPIC_SONNET = {
|
|
3
3
|
inputUsd: 3,
|
|
4
4
|
outputUsd: 15,
|
|
@@ -19,6 +19,41 @@ const OPENAI_GPT5 = {
|
|
|
19
19
|
outputUsd: 10,
|
|
20
20
|
cacheReadMultiplier: 0.5,
|
|
21
21
|
};
|
|
22
|
+
const GEMINI_25_PRO = {
|
|
23
|
+
inputUsd: 1.25,
|
|
24
|
+
outputUsd: 10,
|
|
25
|
+
cacheReadMultiplier: 0.1,
|
|
26
|
+
};
|
|
27
|
+
const GEMINI_FLASH = {
|
|
28
|
+
inputUsd: 0.3,
|
|
29
|
+
outputUsd: 2.5,
|
|
30
|
+
cacheReadMultiplier: 0.1,
|
|
31
|
+
};
|
|
32
|
+
const GEMINI_3_PRO = {
|
|
33
|
+
inputUsd: 2,
|
|
34
|
+
outputUsd: 12,
|
|
35
|
+
cacheReadMultiplier: 0.1,
|
|
36
|
+
};
|
|
37
|
+
const GROK_4 = {
|
|
38
|
+
inputUsd: 1.25,
|
|
39
|
+
outputUsd: 2.5,
|
|
40
|
+
cacheReadMultiplier: 0.16,
|
|
41
|
+
};
|
|
42
|
+
const GROK_BUILD = {
|
|
43
|
+
inputUsd: 1,
|
|
44
|
+
outputUsd: 2,
|
|
45
|
+
cacheReadMultiplier: 0.2,
|
|
46
|
+
};
|
|
47
|
+
const MISTRAL_MEDIUM = {
|
|
48
|
+
inputUsd: 1.5,
|
|
49
|
+
outputUsd: 7.5,
|
|
50
|
+
cacheReadMultiplier: 0.1,
|
|
51
|
+
};
|
|
52
|
+
const MISTRAL_DEVSTRAL = {
|
|
53
|
+
inputUsd: 0.1,
|
|
54
|
+
outputUsd: 0.3,
|
|
55
|
+
cacheReadMultiplier: 0.1,
|
|
56
|
+
};
|
|
22
57
|
const ZERO = {
|
|
23
58
|
inputUsd: 0,
|
|
24
59
|
outputUsd: 0,
|
|
@@ -33,13 +68,43 @@ export function getPricing(cli, model) {
|
|
|
33
68
|
return ANTHROPIC_OPUS;
|
|
34
69
|
if (lower.includes("haiku"))
|
|
35
70
|
return ANTHROPIC_HAIKU;
|
|
36
|
-
return
|
|
71
|
+
return ANTHROPIC_SONNET;
|
|
37
72
|
}
|
|
38
73
|
if (cli === "codex") {
|
|
39
74
|
if (lower.includes("gpt-5") || lower.includes("o3"))
|
|
40
75
|
return OPENAI_GPT5;
|
|
41
76
|
return ZERO;
|
|
42
77
|
}
|
|
78
|
+
if (cli === "gemini") {
|
|
79
|
+
const specialtyVariant = lower.includes("lite") ||
|
|
80
|
+
lower.includes("image") ||
|
|
81
|
+
lower.includes("audio") ||
|
|
82
|
+
lower.includes("tts");
|
|
83
|
+
if (!specialtyVariant) {
|
|
84
|
+
if (lower.includes("2.5-flash"))
|
|
85
|
+
return GEMINI_FLASH;
|
|
86
|
+
if (lower.includes("2.5-pro"))
|
|
87
|
+
return GEMINI_25_PRO;
|
|
88
|
+
if (lower.includes("gemini-3") && lower.includes("pro"))
|
|
89
|
+
return GEMINI_3_PRO;
|
|
90
|
+
}
|
|
91
|
+
return ZERO;
|
|
92
|
+
}
|
|
93
|
+
if (cli === "grok") {
|
|
94
|
+
if (lower.includes("grok-build") || lower.includes("grok-code"))
|
|
95
|
+
return GROK_BUILD;
|
|
96
|
+
if (lower.includes("grok-4") || lower.includes("grok-3") || lower.includes("grok-latest")) {
|
|
97
|
+
return GROK_4;
|
|
98
|
+
}
|
|
99
|
+
return ZERO;
|
|
100
|
+
}
|
|
101
|
+
if (cli === "mistral") {
|
|
102
|
+
if (lower.includes("devstral-small"))
|
|
103
|
+
return MISTRAL_DEVSTRAL;
|
|
104
|
+
if (lower.includes("mistral-medium-3.5"))
|
|
105
|
+
return MISTRAL_MEDIUM;
|
|
106
|
+
return ZERO;
|
|
107
|
+
}
|
|
43
108
|
return ZERO;
|
|
44
109
|
}
|
|
45
110
|
export function estimateCacheSavingsUsd(cli, model, cacheReadTokens) {
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type CliInfo } from "./model-registry.js";
|
|
2
|
+
import { type CliType } from "./session-manager.js";
|
|
3
|
+
export interface ProviderToolControl {
|
|
4
|
+
name?: string;
|
|
5
|
+
supported: boolean;
|
|
6
|
+
requestField?: string;
|
|
7
|
+
cliFlag?: string;
|
|
8
|
+
behavior: string;
|
|
9
|
+
}
|
|
10
|
+
export type ProviderCapabilityId = CliType | "grok_api";
|
|
11
|
+
export type ProviderKind = "cli" | "api";
|
|
12
|
+
export type UnsupportedInputBehavior = "reject" | "ignored" | "not_supported" | "approval_tracking_only" | "deprecated";
|
|
13
|
+
export type ProviderToolConfidence = "high" | "medium" | "low";
|
|
14
|
+
export type ProviderToolExtractionReason = "exact-tool-section" | "known-tool-name" | "backtick-heuristic" | "low-confidence";
|
|
15
|
+
export interface ProviderSkillCapability {
|
|
16
|
+
name: string;
|
|
17
|
+
source: "user" | "bundled";
|
|
18
|
+
path?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
declaredTools: string[];
|
|
21
|
+
declaredToolReasons?: Partial<Record<string, ProviderToolExtractionReason>>;
|
|
22
|
+
}
|
|
23
|
+
export interface ProviderNativeToolCapability {
|
|
24
|
+
name: string;
|
|
25
|
+
source: string;
|
|
26
|
+
skillName?: string;
|
|
27
|
+
path?: string;
|
|
28
|
+
confidence: ProviderToolConfidence;
|
|
29
|
+
reason: ProviderToolExtractionReason;
|
|
30
|
+
}
|
|
31
|
+
export interface ProviderConfigSurface {
|
|
32
|
+
name: string;
|
|
33
|
+
kind: "file" | "directory" | "env" | "gateway" | "provider";
|
|
34
|
+
present: boolean;
|
|
35
|
+
path?: string;
|
|
36
|
+
entries?: string[];
|
|
37
|
+
details?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ProviderUnsupportedInput {
|
|
40
|
+
input: string;
|
|
41
|
+
behavior: UnsupportedInputBehavior;
|
|
42
|
+
details: string;
|
|
43
|
+
}
|
|
44
|
+
export interface ProviderFeatureCapability {
|
|
45
|
+
supported: boolean;
|
|
46
|
+
details?: string;
|
|
47
|
+
values?: string[];
|
|
48
|
+
}
|
|
49
|
+
export type ProviderAcpStatus = "native_smoke_passed" | "native_candidate" | "adapter_mediated_deferred" | "absent_watchlist" | "not_applicable";
|
|
50
|
+
export type ProviderAcpMediation = "native" | "adapter_mediated" | "none";
|
|
51
|
+
export type ProviderAcpSmokeStatus = "passed" | "not_run" | "unsupported";
|
|
52
|
+
export interface ProviderAcpCapability {
|
|
53
|
+
status: ProviderAcpStatus;
|
|
54
|
+
mediation: ProviderAcpMediation;
|
|
55
|
+
targetVersion: string;
|
|
56
|
+
entrypoint: {
|
|
57
|
+
command: string;
|
|
58
|
+
args: string[];
|
|
59
|
+
} | null;
|
|
60
|
+
runtimeEnabled: boolean;
|
|
61
|
+
smokeSupported: boolean;
|
|
62
|
+
smokeStatus: ProviderAcpSmokeStatus;
|
|
63
|
+
caveats: string[];
|
|
64
|
+
docs: string;
|
|
65
|
+
}
|
|
66
|
+
export type ProviderFeatureMap = Record<string, ProviderFeatureCapability>;
|
|
67
|
+
export interface ProviderCapabilityControls {
|
|
68
|
+
allowlist: ProviderToolControl;
|
|
69
|
+
denylist: ProviderToolControl;
|
|
70
|
+
mcpServers: ProviderToolControl;
|
|
71
|
+
nativeSkills: ProviderToolControl;
|
|
72
|
+
[name: string]: ProviderToolControl;
|
|
73
|
+
}
|
|
74
|
+
export type AcpFrozenClassification = "native_candidate" | "adapter_mediated_deferred" | "absent_watchlist";
|
|
75
|
+
export interface AcpProviderContract {
|
|
76
|
+
classification: AcpFrozenClassification;
|
|
77
|
+
summary: string;
|
|
78
|
+
}
|
|
79
|
+
export interface AcpContractMetadata {
|
|
80
|
+
protocol: "Agent Client Protocol";
|
|
81
|
+
outOfScope: "Agent Communication Protocol";
|
|
82
|
+
mcpFrontendRemains: true;
|
|
83
|
+
acpIsInternalProviderTransport: true;
|
|
84
|
+
defaultTransport: "cli";
|
|
85
|
+
hostServicesDenyByDefault: true;
|
|
86
|
+
noRawAcpJsonRpcTool: true;
|
|
87
|
+
adapterSupportIsNotNative: true;
|
|
88
|
+
contractDoc: "docs/acp-contract.md";
|
|
89
|
+
nonGoals: readonly string[];
|
|
90
|
+
providers: Readonly<Record<ProviderCapabilityId, AcpProviderContract>>;
|
|
91
|
+
}
|
|
92
|
+
export declare const ACP_CONTRACT: AcpContractMetadata;
|
|
93
|
+
export interface ProviderToolCapabilities {
|
|
94
|
+
schemaVersion: "provider-tool-capabilities.v2";
|
|
95
|
+
generatedAt: string;
|
|
96
|
+
cli: ProviderCapabilityId;
|
|
97
|
+
providerKind: ProviderKind;
|
|
98
|
+
gatewayRequestTools: string[];
|
|
99
|
+
modelInfo: CliInfo | GrokApiModelInfo;
|
|
100
|
+
summary: string;
|
|
101
|
+
acpContract: AcpProviderContract;
|
|
102
|
+
acp: ProviderAcpCapability;
|
|
103
|
+
controls: ProviderCapabilityControls;
|
|
104
|
+
features: ProviderFeatureMap;
|
|
105
|
+
discoveredSkills: ProviderSkillCapability[];
|
|
106
|
+
discoveredProviderTools: ProviderNativeToolCapability[];
|
|
107
|
+
configSurfaces: ProviderConfigSurface[];
|
|
108
|
+
unsupportedInputs: ProviderUnsupportedInput[];
|
|
109
|
+
warnings: string[];
|
|
110
|
+
metadata: {
|
|
111
|
+
deprecatedFields?: Record<string, string>;
|
|
112
|
+
cacheTtlMs: number;
|
|
113
|
+
};
|
|
114
|
+
gatewayRequestTool: string;
|
|
115
|
+
}
|
|
116
|
+
export interface GrokApiModelInfo {
|
|
117
|
+
description: string;
|
|
118
|
+
models: Record<string, string>;
|
|
119
|
+
defaultModel?: string;
|
|
120
|
+
defaultModelSource?: string;
|
|
121
|
+
warnings?: string[];
|
|
122
|
+
}
|
|
123
|
+
export interface ProviderCapabilityQuery {
|
|
124
|
+
cli?: ProviderCapabilityId;
|
|
125
|
+
includeSkills?: boolean;
|
|
126
|
+
includeProviderTools?: boolean;
|
|
127
|
+
includeUnsupported?: boolean;
|
|
128
|
+
includePaths?: boolean;
|
|
129
|
+
refresh?: boolean;
|
|
130
|
+
}
|
|
131
|
+
export type ProviderToolCapabilitiesMap = Partial<Record<ProviderCapabilityId, ProviderToolCapabilities>>;
|
|
132
|
+
export declare function getProviderToolCapabilities(queryOrCli?: ProviderCapabilityQuery | ProviderCapabilityId): ProviderToolCapabilitiesMap;
|
|
133
|
+
export declare function getOneProviderToolCapabilities(cli: ProviderCapabilityId, queryOrCli?: ProviderCapabilityQuery | ProviderCapabilityId): ProviderToolCapabilities;
|
|
134
|
+
export declare function clearProviderToolCapabilitiesCache(): void;
|
|
135
|
+
export declare function providerCapabilityIds(): readonly ProviderCapabilityId[];
|