llm-cli-gateway 2.15.0 → 2.17.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/.agents/skills/least-cost-routing/SKILL.md +123 -0
- package/CHANGELOG.md +78 -1
- package/dist/acp/client.js +5 -0
- package/dist/acp/flight-redaction.d.ts +2 -0
- package/dist/acp/flight-redaction.js +2 -0
- package/dist/acp/process-manager.js +2 -1
- package/dist/acp/provider-registry.js +8 -8
- package/dist/acp/runtime.d.ts +1 -0
- package/dist/acp/runtime.js +20 -0
- package/dist/acp/types.d.ts +52 -0
- package/dist/acp/types.js +8 -0
- package/dist/api-provider.d.ts +1 -0
- package/dist/api-provider.js +1 -0
- package/dist/async-job-manager.d.ts +20 -1
- package/dist/async-job-manager.js +85 -28
- package/dist/claude-mcp-config.js +1 -1
- package/dist/compressor/estimate.d.ts +5 -0
- package/dist/compressor/estimate.js +21 -0
- package/dist/compressor/index.d.ts +23 -0
- package/dist/compressor/index.js +77 -0
- package/dist/compressor/router.d.ts +2 -0
- package/dist/compressor/router.js +57 -0
- package/dist/compressor/transforms/ansi.d.ts +3 -0
- package/dist/compressor/transforms/ansi.js +99 -0
- package/dist/compressor/transforms/json.d.ts +1 -0
- package/dist/compressor/transforms/json.js +156 -0
- package/dist/compressor/transforms/log.d.ts +12 -0
- package/dist/compressor/transforms/log.js +55 -0
- package/dist/compressor/transforms/whitespace.d.ts +8 -0
- package/dist/compressor/transforms/whitespace.js +79 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.js +166 -6
- package/dist/db.js +4 -4
- package/dist/doctor.d.ts +34 -1
- package/dist/doctor.js +85 -3
- package/dist/executor.d.ts +6 -0
- package/dist/executor.js +16 -3
- package/dist/flight-recorder.d.ts +19 -0
- package/dist/flight-recorder.js +110 -2
- package/dist/http-transport.js +19 -21
- package/dist/index.d.ts +58 -2
- package/dist/index.js +929 -94
- package/dist/job-store.d.ts +14 -2
- package/dist/job-store.js +170 -43
- package/dist/lcr-priors.d.ts +60 -0
- package/dist/lcr-priors.js +190 -0
- package/dist/lcr-router-env.d.ts +20 -0
- package/dist/lcr-router-env.js +133 -0
- package/dist/lcr-telemetry.d.ts +2 -0
- package/dist/lcr-telemetry.js +17 -0
- package/dist/least-cost-router.d.ts +86 -0
- package/dist/least-cost-router.js +296 -0
- package/dist/least-cost-types.d.ts +34 -0
- package/dist/least-cost-types.js +1 -0
- package/dist/migrate-sessions.js +1 -1
- package/dist/migrate.js +1 -1
- package/dist/model-registry.js +1 -1
- package/dist/postgres-job-store-worker.js +56 -13
- package/dist/pricing.d.ts +17 -0
- package/dist/pricing.js +167 -0
- package/dist/provider-definitions.d.ts +5 -0
- package/dist/provider-definitions.js +56 -10
- package/dist/provider-tool-capabilities.js +3 -3
- package/dist/request-helpers.d.ts +2 -2
- package/dist/request-helpers.js +1 -1
- package/dist/resources.d.ts +37 -2
- package/dist/resources.js +96 -1
- package/dist/retry.d.ts +1 -0
- package/dist/retry.js +1 -1
- package/dist/spawn-env-isolation.d.ts +10 -0
- package/dist/spawn-env-isolation.js +55 -0
- package/dist/token-estimator.d.ts +6 -0
- package/dist/token-estimator.js +59 -0
- package/dist/upstream-contracts.js +48 -28
- package/dist/validation-receipt.js +1 -1
- package/dist/validation-tools.d.ts +6 -0
- package/dist/validation-tools.js +174 -54
- package/npm-shrinkwrap.json +2 -2
- package/package.json +15 -6
- package/setup/status.schema.json +68 -0
package/dist/resources.js
CHANGED
|
@@ -4,6 +4,9 @@ import { getRequestContext, principalCanAccess, resolveOwnerPrincipal } from "./
|
|
|
4
4
|
import { getAvailableCliInfo } from "./model-registry.js";
|
|
5
5
|
import { computeGlobalCacheStats, computePrefixCacheStats, computeSessionCacheStats, computeTtlRemaining, } from "./cache-stats.js";
|
|
6
6
|
import { enabledApiProviders, } from "./config.js";
|
|
7
|
+
import { computeLcrPriorsFromDb } from "./lcr-priors.js";
|
|
8
|
+
import { telemetryTierFor } from "./lcr-telemetry.js";
|
|
9
|
+
import { PRICING_AS_OF, API_CATALOG_AS_OF } from "./pricing.js";
|
|
7
10
|
import { apiContinuityForKind } from "./api-provider.js";
|
|
8
11
|
import { apiProviderCatalogEntry } from "./api-request.js";
|
|
9
12
|
import { buildProviderSubcommandsCompactCatalog, getCliSubcommandContract, serializeCliSubcommandContract, } from "./upstream-contracts.js";
|
|
@@ -20,7 +23,8 @@ export class ResourceProvider {
|
|
|
20
23
|
providers;
|
|
21
24
|
capabilityPeek;
|
|
22
25
|
acpConfig;
|
|
23
|
-
|
|
26
|
+
leastCost;
|
|
27
|
+
constructor(sessionManager, performanceMetrics, flightRecorder = { queryRequests: () => [] }, cacheAwareness = null, providers = null, capabilityPeek = peekProviderCapabilitySet, acpConfig = null, leastCost = undefined) {
|
|
24
28
|
this.sessionManager = sessionManager;
|
|
25
29
|
this.performanceMetrics = performanceMetrics;
|
|
26
30
|
this.flightRecorder = flightRecorder;
|
|
@@ -28,6 +32,10 @@ export class ResourceProvider {
|
|
|
28
32
|
this.providers = providers;
|
|
29
33
|
this.capabilityPeek = capabilityPeek;
|
|
30
34
|
this.acpConfig = acpConfig;
|
|
35
|
+
this.leastCost = leastCost;
|
|
36
|
+
}
|
|
37
|
+
routingResourcesEnabled() {
|
|
38
|
+
return this.leastCost?.enabled === true;
|
|
31
39
|
}
|
|
32
40
|
getFlightRecorderQuery() {
|
|
33
41
|
return this.flightRecorder;
|
|
@@ -172,8 +180,79 @@ export class ResourceProvider {
|
|
|
172
180
|
priority: 0.7,
|
|
173
181
|
},
|
|
174
182
|
})),
|
|
183
|
+
...(this.routingResourcesEnabled()
|
|
184
|
+
? [
|
|
185
|
+
{
|
|
186
|
+
uri: "routing://decisions",
|
|
187
|
+
name: "Routing Decisions",
|
|
188
|
+
title: "Least-Cost Routing Decisions",
|
|
189
|
+
description: "Recent redacted least-cost routing decisions (provider/model/tier/est cost/confidence)",
|
|
190
|
+
mimeType: "application/json",
|
|
191
|
+
annotations: {
|
|
192
|
+
audience: ["user", "assistant"],
|
|
193
|
+
priority: 0.7,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
uri: "routing://priors",
|
|
198
|
+
name: "Routing Priors",
|
|
199
|
+
title: "Least-Cost Routing Priors",
|
|
200
|
+
description: "Learned output-token priors + input-token calibration (k, samples, quality) + price asOf",
|
|
201
|
+
mimeType: "application/json",
|
|
202
|
+
annotations: {
|
|
203
|
+
audience: ["user", "assistant"],
|
|
204
|
+
priority: 0.7,
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
]
|
|
208
|
+
: []),
|
|
175
209
|
];
|
|
176
210
|
}
|
|
211
|
+
readRoutingDecisions() {
|
|
212
|
+
const rows = this.flightRecorder.queryRequests(`SELECT r.cli, r.model, r.datetime_utc, r.cost_basis,
|
|
213
|
+
m.route_est_cost_usd, m.route_est_confidence, m.route_reason,
|
|
214
|
+
m.route_considered, m.route_reroutes
|
|
215
|
+
FROM requests r
|
|
216
|
+
LEFT JOIN gateway_metadata m ON m.request_id = r.id
|
|
217
|
+
WHERE m.routed = 1
|
|
218
|
+
ORDER BY r.datetime_utc DESC
|
|
219
|
+
LIMIT 50`);
|
|
220
|
+
return rows.map(row => ({
|
|
221
|
+
provider: row.cli,
|
|
222
|
+
model: row.model,
|
|
223
|
+
tier: telemetryTierFor(row.cli),
|
|
224
|
+
estCostUsd: row.route_est_cost_usd ?? null,
|
|
225
|
+
costBasis: row.cost_basis ?? null,
|
|
226
|
+
confidence: row.route_est_confidence ?? null,
|
|
227
|
+
reason: row.route_reason ?? null,
|
|
228
|
+
considered: row.route_considered ?? null,
|
|
229
|
+
reroutes: row.route_reroutes ?? null,
|
|
230
|
+
at: row.datetime_utc,
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
readRoutingPriors() {
|
|
234
|
+
const scope = this.leastCost?.priorsScope ?? "off";
|
|
235
|
+
const priors = computeLcrPriorsFromDb(this.flightRecorder, {
|
|
236
|
+
priorsScope: scope,
|
|
237
|
+
ownerPrincipal: resolveOwnerPrincipal(getRequestContext()),
|
|
238
|
+
});
|
|
239
|
+
return {
|
|
240
|
+
priorsScope: scope,
|
|
241
|
+
priceAsOf: { table: PRICING_AS_OF, apiCatalog: API_CATALOG_AS_OF },
|
|
242
|
+
outputPriors: Array.from(priors.outputPriors, ([key, prior]) => ({
|
|
243
|
+
candidate: key,
|
|
244
|
+
median: prior.median,
|
|
245
|
+
p90: prior.p90,
|
|
246
|
+
samples: prior.samples,
|
|
247
|
+
})),
|
|
248
|
+
calibration: Array.from(priors.calibration, ([key, bucket]) => ({
|
|
249
|
+
bucket: key,
|
|
250
|
+
k: bucket.k,
|
|
251
|
+
samples: bucket.samples,
|
|
252
|
+
confidence: bucket.confidence,
|
|
253
|
+
})),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
177
256
|
ownedSessions(sessions) {
|
|
178
257
|
const caller = resolveOwnerPrincipal(getRequestContext());
|
|
179
258
|
const owned = sessions.filter(s => principalCanAccess(s.ownerPrincipal, caller));
|
|
@@ -256,6 +335,22 @@ export class ResourceProvider {
|
|
|
256
335
|
};
|
|
257
336
|
}
|
|
258
337
|
}
|
|
338
|
+
if (this.routingResourcesEnabled()) {
|
|
339
|
+
if (uri === "routing://decisions") {
|
|
340
|
+
return {
|
|
341
|
+
uri,
|
|
342
|
+
mimeType: "application/json",
|
|
343
|
+
text: JSON.stringify({ decisions: this.readRoutingDecisions() }, null, 2),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
if (uri === "routing://priors") {
|
|
347
|
+
return {
|
|
348
|
+
uri,
|
|
349
|
+
mimeType: "application/json",
|
|
350
|
+
text: JSON.stringify(this.readRoutingPriors(), null, 2),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
259
354
|
if (uri === "metrics://performance") {
|
|
260
355
|
return {
|
|
261
356
|
uri,
|
package/dist/retry.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface RetryOptions {
|
|
|
19
19
|
isTransient: (error: any) => boolean;
|
|
20
20
|
onRetry: (error: any, attempt: number, delay: number) => void;
|
|
21
21
|
}
|
|
22
|
+
export declare const isDefaultTransient: (error: any) => boolean;
|
|
22
23
|
export declare function createCircuitBreaker(options?: {
|
|
23
24
|
resetTimeout?: number;
|
|
24
25
|
failureThreshold?: number;
|
package/dist/retry.js
CHANGED
|
@@ -4,7 +4,7 @@ export var CircuitBreakerState;
|
|
|
4
4
|
CircuitBreakerState["OPEN"] = "OPEN";
|
|
5
5
|
CircuitBreakerState["HALF_OPEN"] = "HALF_OPEN";
|
|
6
6
|
})(CircuitBreakerState || (CircuitBreakerState = {}));
|
|
7
|
-
const isDefaultTransient = (error) => {
|
|
7
|
+
export const isDefaultTransient = (error) => {
|
|
8
8
|
if (!error) {
|
|
9
9
|
return false;
|
|
10
10
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Logger } from "./logger.js";
|
|
2
|
+
export declare function isRedirectionEnvKey(key: string): boolean;
|
|
3
|
+
export declare function isSpawnEnvIsolationEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
4
|
+
export interface SpawnEnvSanitizeResult {
|
|
5
|
+
env: NodeJS.ProcessEnv;
|
|
6
|
+
stripped: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function sanitizeSpawnEnv(baseEnv: NodeJS.ProcessEnv): SpawnEnvSanitizeResult;
|
|
9
|
+
export declare function resetSpawnEnvIsolationWarning(): void;
|
|
10
|
+
export declare function applySpawnEnvIsolation(finalEnv: NodeJS.ProcessEnv, logger?: Logger, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { logWarn } from "./logger.js";
|
|
2
|
+
const REDIRECTION_SUFFIXES = [
|
|
3
|
+
/_BASE_URL$/,
|
|
4
|
+
/_API_URL$/,
|
|
5
|
+
/_API_BASE$/,
|
|
6
|
+
/_ENDPOINT$/,
|
|
7
|
+
/_ENDPOINT_URL$/,
|
|
8
|
+
/_SERVER_URL$/,
|
|
9
|
+
];
|
|
10
|
+
const REDIRECTION_EXACT = new Set([
|
|
11
|
+
"ANTHROPIC_UNIX_SOCKET",
|
|
12
|
+
"GROK_LEADER_SOCKET",
|
|
13
|
+
]);
|
|
14
|
+
export function isRedirectionEnvKey(key) {
|
|
15
|
+
const upper = key.toUpperCase();
|
|
16
|
+
if (upper === "NO_PROXY")
|
|
17
|
+
return false;
|
|
18
|
+
if (upper.endsWith("_PROXY"))
|
|
19
|
+
return true;
|
|
20
|
+
if (REDIRECTION_EXACT.has(upper))
|
|
21
|
+
return true;
|
|
22
|
+
return REDIRECTION_SUFFIXES.some(re => re.test(upper));
|
|
23
|
+
}
|
|
24
|
+
export function isSpawnEnvIsolationEnabled(env = process.env) {
|
|
25
|
+
const raw = (env.LLM_GATEWAY_ISOLATE_SPAWN_ENV ?? "").trim().toLowerCase();
|
|
26
|
+
return raw === "1" || raw === "true" || raw === "on" || raw === "yes";
|
|
27
|
+
}
|
|
28
|
+
export function sanitizeSpawnEnv(baseEnv) {
|
|
29
|
+
const env = {};
|
|
30
|
+
const stripped = [];
|
|
31
|
+
for (const key of Object.keys(baseEnv)) {
|
|
32
|
+
if (isRedirectionEnvKey(key)) {
|
|
33
|
+
stripped.push(key);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
env[key] = baseEnv[key];
|
|
37
|
+
}
|
|
38
|
+
return { env, stripped };
|
|
39
|
+
}
|
|
40
|
+
let warnedOnce = false;
|
|
41
|
+
export function resetSpawnEnvIsolationWarning() {
|
|
42
|
+
warnedOnce = false;
|
|
43
|
+
}
|
|
44
|
+
export function applySpawnEnvIsolation(finalEnv, logger, env = process.env) {
|
|
45
|
+
if (!isSpawnEnvIsolationEnabled(env))
|
|
46
|
+
return finalEnv;
|
|
47
|
+
const { env: sanitized, stripped } = sanitizeSpawnEnv(finalEnv);
|
|
48
|
+
if (stripped.length > 0 && logger && !warnedOnce) {
|
|
49
|
+
warnedOnce = true;
|
|
50
|
+
logWarn(logger, `spawn-env isolation: withheld ${stripped.length} endpoint/proxy redirection variable(s) ` +
|
|
51
|
+
`from provider child processes (${stripped.join(", ")}). ` +
|
|
52
|
+
`Unset LLM_GATEWAY_ISOLATE_SPAWN_ENV to disable.`);
|
|
53
|
+
}
|
|
54
|
+
return sanitized;
|
|
55
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const CJK_RE = /[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff00-\uffef\uac00-\ud7af]/g;
|
|
2
|
+
const CODE_SYMBOL_RE = /[{}[\]()<>;=+\-*/\\|&%$#@`~]/g;
|
|
3
|
+
const CODE_KEYWORD_RE = /\b(function|const|let|var|return|import|export|class|def|public|private|static|void|for|while|switch|case)\b/;
|
|
4
|
+
const DIVISOR_BY_TYPE = new Map([
|
|
5
|
+
["prose", 4],
|
|
6
|
+
["code", 3],
|
|
7
|
+
["cjk", 1.5],
|
|
8
|
+
]);
|
|
9
|
+
const FAMILY_MULTIPLIERS = new Map([
|
|
10
|
+
["openai", 1.0],
|
|
11
|
+
["o200k", 1.0],
|
|
12
|
+
["cl100k", 1.02],
|
|
13
|
+
["claude", 1.08],
|
|
14
|
+
["gemini", 0.98],
|
|
15
|
+
["sentencepiece", 0.98],
|
|
16
|
+
["grok", 1.0],
|
|
17
|
+
["mistral", 1.05],
|
|
18
|
+
]);
|
|
19
|
+
export function classifyContent(text) {
|
|
20
|
+
if (!text)
|
|
21
|
+
return "prose";
|
|
22
|
+
const nonSpace = text.replace(/\s+/g, "").length;
|
|
23
|
+
if (nonSpace === 0)
|
|
24
|
+
return "prose";
|
|
25
|
+
const cjkMatches = text.match(CJK_RE);
|
|
26
|
+
const cjkCount = cjkMatches ? cjkMatches.length : 0;
|
|
27
|
+
if (cjkCount / nonSpace >= 0.2)
|
|
28
|
+
return "cjk";
|
|
29
|
+
const trimmed = text.trim();
|
|
30
|
+
const looksJson = /^[[{]/.test(trimmed) && /[}\]]/.test(trimmed) && /[:,]/.test(trimmed);
|
|
31
|
+
const symbolMatches = trimmed.match(CODE_SYMBOL_RE);
|
|
32
|
+
const symbolCount = symbolMatches ? symbolMatches.length : 0;
|
|
33
|
+
const symbolRatio = symbolCount / nonSpace;
|
|
34
|
+
const hasKeyword = CODE_KEYWORD_RE.test(trimmed);
|
|
35
|
+
if (looksJson || symbolRatio >= 0.08 || (hasKeyword && symbolRatio >= 0.03)) {
|
|
36
|
+
return "code";
|
|
37
|
+
}
|
|
38
|
+
return "prose";
|
|
39
|
+
}
|
|
40
|
+
function familyMultiplier(family) {
|
|
41
|
+
if (!family)
|
|
42
|
+
return 1;
|
|
43
|
+
const f = family.toLowerCase();
|
|
44
|
+
for (const [key, multiplier] of FAMILY_MULTIPLIERS) {
|
|
45
|
+
if (f === key || f.includes(key))
|
|
46
|
+
return multiplier;
|
|
47
|
+
}
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
export function estimateInputTokens(text, opts) {
|
|
51
|
+
if (!text)
|
|
52
|
+
return 0;
|
|
53
|
+
const type = classifyContent(text);
|
|
54
|
+
const divisor = DIVISOR_BY_TYPE.get(type) ?? 4;
|
|
55
|
+
const base = text.length / divisor;
|
|
56
|
+
const familyMult = familyMultiplier(opts?.family);
|
|
57
|
+
const k = opts?.calibrationK ?? 1;
|
|
58
|
+
return Math.ceil(base * familyMult * k);
|
|
59
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { getProviderDefinition } from "./provider-definitions.js";
|
|
3
|
+
import { getProviderDefinition, PROVIDER_TARGET_VERSIONS, } from "./provider-definitions.js";
|
|
4
4
|
import { envWithExtendedPath, getExtendedPath, resolveCommandForSpawn } from "./executor.js";
|
|
5
5
|
export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
6
6
|
mistral: {
|
|
@@ -9,7 +9,7 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
9
9
|
status: "native",
|
|
10
10
|
executable: "vibe-acp",
|
|
11
11
|
entrypointArgs: [],
|
|
12
|
-
targetVersion:
|
|
12
|
+
targetVersion: PROVIDER_TARGET_VERSIONS.mistral,
|
|
13
13
|
probeArgs: [["--version"], ["--help"]],
|
|
14
14
|
evidence: "Native ACP executable vibe-acp; manual initialize + session/new smoke passed. First runtime pilot.",
|
|
15
15
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.mistral",
|
|
@@ -20,9 +20,9 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
20
20
|
status: "native",
|
|
21
21
|
executable: "grok",
|
|
22
22
|
entrypointArgs: ["agent", "stdio"],
|
|
23
|
-
targetVersion:
|
|
23
|
+
targetVersion: PROVIDER_TARGET_VERSIONS.grok,
|
|
24
24
|
probeArgs: [["agent", "stdio", "--help"]],
|
|
25
|
-
evidence:
|
|
25
|
+
evidence: `Native ACP via \`grok agent stdio\`; initialize + session/new smoke passed with isolated leader socket. Second runtime pilot. Entrypoint re-probed clean at ${PROVIDER_TARGET_VERSIONS.grok}.`,
|
|
26
26
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.grok",
|
|
27
27
|
},
|
|
28
28
|
codex: {
|
|
@@ -31,10 +31,10 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
31
31
|
status: "adapter_mediated_deferred",
|
|
32
32
|
executable: "codex",
|
|
33
33
|
entrypointArgs: [],
|
|
34
|
-
targetVersion:
|
|
34
|
+
targetVersion: PROVIDER_TARGET_VERSIONS.codex,
|
|
35
35
|
probeArgs: [],
|
|
36
36
|
adapterCandidates: ["zed-industries/codex-acp", "agentclientprotocol/codex-acp"],
|
|
37
|
-
evidence:
|
|
37
|
+
evidence: `No native ACP entrypoint at ${PROVIDER_TARGET_VERSIONS.codex}. Adapter evidence tracked as documentation only; not native gateway ACP support.`,
|
|
38
38
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.codex",
|
|
39
39
|
},
|
|
40
40
|
claude: {
|
|
@@ -43,10 +43,10 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
43
43
|
status: "adapter_mediated_deferred",
|
|
44
44
|
executable: "claude",
|
|
45
45
|
entrypointArgs: [],
|
|
46
|
-
targetVersion:
|
|
46
|
+
targetVersion: PROVIDER_TARGET_VERSIONS.claude,
|
|
47
47
|
probeArgs: [],
|
|
48
48
|
adapterCandidates: ["Claude Agent SDK ACP adapter"],
|
|
49
|
-
evidence:
|
|
49
|
+
evidence: `No native Claude Code CLI ACP entrypoint at ${PROVIDER_TARGET_VERSIONS.claude}. Adapter ownership/permission bridging unresolved; deferred.`,
|
|
50
50
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.claude",
|
|
51
51
|
},
|
|
52
52
|
gemini: {
|
|
@@ -55,9 +55,9 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
55
55
|
status: "absent_watchlist",
|
|
56
56
|
executable: "agy",
|
|
57
57
|
entrypointArgs: [],
|
|
58
|
-
targetVersion:
|
|
58
|
+
targetVersion: PROVIDER_TARGET_VERSIONS.gemini,
|
|
59
59
|
probeArgs: [],
|
|
60
|
-
evidence:
|
|
60
|
+
evidence: `${PROVIDER_TARGET_VERSIONS.gemini} has no ACP flag or subcommand. Legacy Gemini CLI ACP evidence does not transfer. Watchlist item.`,
|
|
61
61
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.gemini",
|
|
62
62
|
},
|
|
63
63
|
devin: {
|
|
@@ -66,7 +66,7 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
66
66
|
status: "native",
|
|
67
67
|
executable: "devin",
|
|
68
68
|
entrypointArgs: ["acp"],
|
|
69
|
-
targetVersion:
|
|
69
|
+
targetVersion: PROVIDER_TARGET_VERSIONS.devin,
|
|
70
70
|
probeArgs: [["--version"]],
|
|
71
71
|
evidence: 'Native ACP entrypoint `devin acp` (stdio JSON-RPC). Slice D1 manual initialize + session/new smoke passed (protocolVersion 1, agent "Affogato", session created). Third native runtime pilot; routing stays config-gated.',
|
|
72
72
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.devin",
|
|
@@ -77,7 +77,7 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
77
77
|
status: "native",
|
|
78
78
|
executable: "cursor-agent",
|
|
79
79
|
entrypointArgs: ["acp"],
|
|
80
|
-
targetVersion:
|
|
80
|
+
targetVersion: PROVIDER_TARGET_VERSIONS.cursor,
|
|
81
81
|
probeArgs: [["acp", "--help"]],
|
|
82
82
|
evidence: "Native hidden ACP entrypoint `cursor-agent acp` (stdio JSON-RPC). `cursor-agent acp --help` was verified locally; manual initialize + session/new smoke passed locally (protocolVersion 1, session created; no agentInfo returned). Runtime routing stays config-gated.",
|
|
83
83
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.cursor",
|
|
@@ -326,10 +326,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
326
326
|
"--system-prompt-file": {
|
|
327
327
|
arity: "one",
|
|
328
328
|
description: "Replacement system prompt read from a file path",
|
|
329
|
+
hiddenFromHelp: true,
|
|
329
330
|
},
|
|
330
331
|
"--append-system-prompt-file": {
|
|
331
332
|
arity: "one",
|
|
332
333
|
description: "Appended system prompt read from a file path",
|
|
334
|
+
hiddenFromHelp: true,
|
|
333
335
|
},
|
|
334
336
|
"--name": { arity: "one", description: "Session name label" },
|
|
335
337
|
"--plugin-dir": {
|
|
@@ -494,7 +496,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
494
496
|
},
|
|
495
497
|
{
|
|
496
498
|
id: "claude-background-acknowledged-not-emitted",
|
|
497
|
-
description: "Claude 2.1.
|
|
499
|
+
description: "Claude 2.1.204 advertises --bg/--background (background agent), but the gateway acknowledges them without emitting; caller argv is rejected",
|
|
498
500
|
args: ["-p", "hello", "--background"],
|
|
499
501
|
expect: "fail",
|
|
500
502
|
},
|
|
@@ -658,8 +660,11 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
658
660
|
"--disable",
|
|
659
661
|
"--enable",
|
|
660
662
|
"--include-managed-config",
|
|
661
|
-
"--
|
|
663
|
+
"--permission-profile",
|
|
662
664
|
"--profile",
|
|
665
|
+
"--sandbox-state-disable-network",
|
|
666
|
+
"--sandbox-state-json",
|
|
667
|
+
"--sandbox-state-readable-root",
|
|
663
668
|
], { exposure: "not_exposed" }),
|
|
664
669
|
debug: subcommand(["debug"], "Run Codex debugging utilities.", "read_only", ["--config", "--disable", "--enable"], { tier: "diagnostic" }),
|
|
665
670
|
apply: subcommand(["apply"], "Apply a Codex patch to the workspace.", "destructive", ["--config", "--disable", "--enable"], { exposure: "not_exposed" }),
|
|
@@ -1028,7 +1033,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
1028
1033
|
description: "Print-mode wait timeout as a Go duration string (e.g. 5m0s)",
|
|
1029
1034
|
},
|
|
1030
1035
|
},
|
|
1031
|
-
acknowledgedUpstreamFlags: ["--log-file", "--prompt-interactive"],
|
|
1036
|
+
acknowledgedUpstreamFlags: ["--log-file", "--mode", "--prompt-interactive"],
|
|
1032
1037
|
env: {},
|
|
1033
1038
|
conformanceFixtures: [
|
|
1034
1039
|
{
|
|
@@ -1109,6 +1114,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
1109
1114
|
"--leader-socket",
|
|
1110
1115
|
"--model",
|
|
1111
1116
|
"--no-leader",
|
|
1117
|
+
"--plugin-dir",
|
|
1112
1118
|
"--reasoning-effort",
|
|
1113
1119
|
"--reauth",
|
|
1114
1120
|
"--xai-api-base-url",
|
|
@@ -1189,11 +1195,11 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
1189
1195
|
"--deny",
|
|
1190
1196
|
"--disable-web-search",
|
|
1191
1197
|
"--disallowed-tools",
|
|
1192
|
-
"--effort",
|
|
1193
1198
|
"--experimental-memory",
|
|
1194
1199
|
"--fork-session",
|
|
1195
1200
|
"--json-schema",
|
|
1196
1201
|
"--max-turns",
|
|
1202
|
+
"--minimal",
|
|
1197
1203
|
"--model",
|
|
1198
1204
|
"--no-alt-screen",
|
|
1199
1205
|
"--no-memory",
|
|
@@ -1233,7 +1239,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
1233
1239
|
worktree: subcommand(["worktree"], "Manage Grok worktree sessions.", "writes_local_config", ["--leader-socket"]),
|
|
1234
1240
|
}, GROK_DEBUG_HELP_FLAGS),
|
|
1235
1241
|
maxPositionals: 0,
|
|
1236
|
-
acknowledgedUpstreamFlags: [...GROK_DEBUG_HELP_FLAGS, "--session-id"],
|
|
1242
|
+
acknowledgedUpstreamFlags: [...GROK_DEBUG_HELP_FLAGS, "--minimal", "--session-id"],
|
|
1237
1243
|
mcpTools: ["grok_request", "grok_request_async"],
|
|
1238
1244
|
mcpParameters: [
|
|
1239
1245
|
"prompt",
|
|
@@ -1675,11 +1681,17 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
1675
1681
|
description: "Additional writable workspace directory (Phase 4 slice ζ; repeat once per directory)",
|
|
1676
1682
|
},
|
|
1677
1683
|
},
|
|
1678
|
-
acknowledgedUpstreamFlags: [
|
|
1684
|
+
acknowledgedUpstreamFlags: [
|
|
1685
|
+
"--auto-approve",
|
|
1686
|
+
"--check-upgrade",
|
|
1687
|
+
"--disabled-tools",
|
|
1688
|
+
"--worktree",
|
|
1689
|
+
"--yolo",
|
|
1690
|
+
],
|
|
1679
1691
|
env: {
|
|
1680
1692
|
VIBE_ACTIVE_MODEL: {
|
|
1681
1693
|
arity: "one",
|
|
1682
|
-
pattern: /^[^\s\
|
|
1694
|
+
pattern: /^[^\s\p{Cc}]+$/u,
|
|
1683
1695
|
description: "Active model selector; Vibe uses env instead of a --model flag",
|
|
1684
1696
|
},
|
|
1685
1697
|
},
|
|
@@ -1774,25 +1786,39 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
1774
1786
|
},
|
|
1775
1787
|
{
|
|
1776
1788
|
id: "mistral-current-help-surface",
|
|
1777
|
-
description: "Vibe 2.
|
|
1789
|
+
description: "Vibe 2.19.1 request-time help surface: --prompt, -v, --version, --setup accepted",
|
|
1778
1790
|
args: ["--prompt", "hello", "--agent", "auto-approve", "-v", "--version", "--setup"],
|
|
1779
1791
|
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
1780
1792
|
expect: "pass",
|
|
1781
1793
|
},
|
|
1782
1794
|
{
|
|
1783
1795
|
id: "mistral-yolo-shortcut-rejected",
|
|
1784
|
-
description: "Vibe 2.
|
|
1796
|
+
description: "Vibe 2.19.0 advertises --yolo as a shortcut, but the gateway keeps using explicit --agent auto-approve",
|
|
1785
1797
|
args: ["-p", "hello", "--yolo"],
|
|
1786
1798
|
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
1787
1799
|
expect: "fail",
|
|
1788
1800
|
},
|
|
1789
1801
|
{
|
|
1790
1802
|
id: "mistral-check-upgrade-rejected",
|
|
1791
|
-
description: "Vibe 2.
|
|
1803
|
+
description: "Vibe 2.19.0 advertises --check-upgrade, but gateway request validation rejects update-prompt flags",
|
|
1792
1804
|
args: ["--check-upgrade"],
|
|
1793
1805
|
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
1794
1806
|
expect: "fail",
|
|
1795
1807
|
},
|
|
1808
|
+
{
|
|
1809
|
+
id: "mistral-worktree-rejected",
|
|
1810
|
+
description: "Vibe 2.19.0 advertises --worktree (native worktree); the gateway uses slice-λ worktree (spawns with cwd) and rejects raw --worktree as caller argv",
|
|
1811
|
+
args: ["-p", "hello", "--worktree", "feature"],
|
|
1812
|
+
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
1813
|
+
expect: "fail",
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
id: "mistral-disabled-tools-rejected",
|
|
1817
|
+
description: "Vibe 2.19.1 adds --disabled-tools (denylist counterpart to --enabled-tools); the gateway does not emit it (disallowedTools is accepted but ignored) and rejects raw --disabled-tools as caller argv",
|
|
1818
|
+
args: ["-p", "hello", "--disabled-tools", "shell"],
|
|
1819
|
+
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
1820
|
+
expect: "fail",
|
|
1821
|
+
},
|
|
1796
1822
|
{
|
|
1797
1823
|
id: "mistral-resume-bare",
|
|
1798
1824
|
description: "Vibe --resume without session ID is accepted (optional arity)",
|
|
@@ -2031,22 +2057,16 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
2031
2057
|
acknowledgedUpstreamFlags: [
|
|
2032
2058
|
"--api-key",
|
|
2033
2059
|
"--header",
|
|
2034
|
-
"-H",
|
|
2035
|
-
"-p",
|
|
2036
2060
|
"--plan",
|
|
2037
2061
|
"--yolo",
|
|
2038
2062
|
"--approve-mcps",
|
|
2039
2063
|
"--plugin-dir",
|
|
2040
2064
|
"--worktree",
|
|
2041
|
-
"-w",
|
|
2042
2065
|
"--worktree-base",
|
|
2043
2066
|
"--skip-worktree-setup",
|
|
2044
2067
|
"--stream-partial-output",
|
|
2045
2068
|
"--list-models",
|
|
2046
2069
|
"--version",
|
|
2047
|
-
"-v",
|
|
2048
|
-
"--help",
|
|
2049
|
-
"-h",
|
|
2050
2070
|
],
|
|
2051
2071
|
env: {
|
|
2052
2072
|
CURSOR_API_KEY: {
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { z } from "zod/v3";
|
|
2
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import type { AsyncJobManager } from "./async-job-manager.js";
|
|
4
|
+
import { PerformanceMetrics } from "./metrics.js";
|
|
5
|
+
import { type LeastCostConfig } from "./config.js";
|
|
6
|
+
import type { FlightRecorderQuery } from "./flight-recorder.js";
|
|
4
7
|
import { type ValidationOrchestratorDeps } from "./validation-orchestrator.js";
|
|
5
8
|
export interface ValidationToolDeps extends ValidationOrchestratorDeps {
|
|
6
9
|
asyncJobManager: AsyncJobManager;
|
|
10
|
+
leastCost?: LeastCostConfig;
|
|
11
|
+
performanceMetrics?: PerformanceMetrics;
|
|
12
|
+
flightRecorder?: FlightRecorderQuery;
|
|
7
13
|
}
|
|
8
14
|
export declare function buildValidationSchemas(deps: ValidationToolDeps): {
|
|
9
15
|
providerSchema: z.ZodEnum<[string, ...string[]]>;
|