llm-cli-gateway 2.11.1 → 2.12.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/async-job-orchestration/SKILL.md +288 -0
- package/.agents/skills/implement-review-fix/SKILL.md +154 -0
- package/.agents/skills/multi-llm-review/SKILL.md +174 -0
- package/.agents/skills/public-demo-session/SKILL.md +100 -0
- package/.agents/skills/secure-orchestration/SKILL.md +227 -0
- package/.agents/skills/session-workflow/SKILL.md +271 -0
- package/CHANGELOG.md +40 -0
- package/README.md +48 -19
- package/dist/acp/provider-registry.js +5 -5
- package/dist/api-provider.d.ts +7 -0
- package/dist/api-provider.js +7 -0
- package/dist/api-request.js +1 -0
- package/dist/async-job-manager.d.ts +11 -1
- package/dist/async-job-manager.js +44 -3
- package/dist/config.d.ts +2 -0
- package/dist/config.js +3 -0
- package/dist/index.d.ts +38 -2
- package/dist/index.js +609 -156
- package/dist/job-store.d.ts +48 -1
- package/dist/job-store.js +184 -0
- package/dist/provider-codegen.js +3 -0
- package/dist/provider-tool-capabilities.js +7 -7
- package/dist/upstream-contracts.d.ts +1 -0
- package/dist/upstream-contracts.js +128 -21
- package/dist/validation-orchestrator.d.ts +5 -2
- package/dist/validation-orchestrator.js +71 -5
- package/dist/validation-receipt.d.ts +68 -0
- package/dist/validation-receipt.js +245 -0
- package/dist/validation-report.d.ts +4 -2
- package/dist/validation-report.js +18 -1
- package/dist/validation-tools.js +58 -9
- package/npm-shrinkwrap.json +5 -5
- package/package.json +10 -4
- package/dist/xai-api-provider.d.ts +0 -43
- package/dist/xai-api-provider.js +0 -191
package/dist/index.js
CHANGED
|
@@ -23,11 +23,10 @@ import { runAcpRequest } from "./acp/runtime.js";
|
|
|
23
23
|
import { isAcpError } from "./acp/errors.js";
|
|
24
24
|
import { createApiProvider, runApiRequest, apiProviderBreakerState, } from "./api-provider.js";
|
|
25
25
|
import { prepareApiRequest, apiProviderCatalogEntry, ApiModelNotAllowedError, } from "./api-request.js";
|
|
26
|
-
import { createXaiResponse, XaiApiError, } from "./xai-api-provider.js";
|
|
27
26
|
import { checkHealth } from "./health.js";
|
|
28
27
|
import { clearModelRegistryCache, getAvailableCliInfo, getCliInfo, resolveModelAlias, } from "./model-registry.js";
|
|
29
28
|
import { getProviderToolCapabilities } from "./provider-tool-capabilities.js";
|
|
30
|
-
import { AsyncJobManager, } from "./async-job-manager.js";
|
|
29
|
+
import { AsyncJobManager, extractApiHttpStatus, extractApiErrorBody, } from "./async-job-manager.js";
|
|
31
30
|
import { createJobStore } from "./job-store.js";
|
|
32
31
|
import { ApprovalManager, bypassAllowedByOperator, } from "./approval-manager.js";
|
|
33
32
|
import { checkReviewIntegrity } from "./review-integrity.js";
|
|
@@ -43,6 +42,7 @@ import { printDoctorJson } from "./doctor.js";
|
|
|
43
42
|
import { createWorkspace, describeWorkspace, getWorkspace, loadWorkspaceRegistry, registerExistingWorkspace, resolveWorkspaceForProvider, validatePathInsideWorkspace, } from "./workspace-registry.js";
|
|
44
43
|
import { generateSecret, hashSecret } from "./oauth.js";
|
|
45
44
|
import { registerValidationTools } from "./validation-tools.js";
|
|
45
|
+
import { currentCaller, resolveValidationReceipt } from "./validation-receipt.js";
|
|
46
46
|
import { assertUpstreamCliArgs, assertUpstreamCliEnv, buildProviderSubcommandsCompactCatalog, buildUpstreamContractReport, getCliSubcommandContract, probeInstalledCliContract, serializeCliSubcommandContract, UPSTREAM_CLI_CONTRACTS, } from "./upstream-contracts.js";
|
|
47
47
|
import { buildArgvFromGeneration, deriveZodShapeFromGeneration, GROK_FLAG_GENERATION, GROK_GEN_OUTPUT_FORMAT, GROK_GEN_MAIN, GROK_GEN_PROMPT_FILE, GROK_GEN_SINGLE, GROK_GEN_TAIL, } from "./provider-codegen.js";
|
|
48
48
|
import { entrypointFileURL } from "./entrypoint-url.js";
|
|
@@ -161,14 +161,15 @@ export function buildServerInstructions(asyncJobsEnabled, grokApiToolsEnabled =
|
|
|
161
161
|
: '- Async jobs are DISABLED (persistence.backend = "none"): *_request_async and llm_job_* tools are not registered, and sync requests run to completion (no auto-deferral).';
|
|
162
162
|
return `llm-cli-gateway: Multi-LLM orchestration via MCP.
|
|
163
163
|
|
|
164
|
-
Tools: claude_request, codex_request, gemini_request, grok_request, mistral_request${apiToolsNote} (sync)${asyncToolsNote} | codex_fork_session (fork a Codex session into a new branch)
|
|
164
|
+
Tools: claude_request, codex_request, gemini_request, grok_request, mistral_request, devin_request${apiToolsNote} (sync)${asyncToolsNote} | codex_fork_session (fork a Codex session into a new branch)
|
|
165
165
|
Validation: validate_with_models, second_opinion, compare_answers, red_team_review, consensus_check, ask_model, synthesize_validation, list_available_models | job_status/job_result (validation jobs)
|
|
166
166
|
${jobsLine}Sessions: session_create, session_list, session_set_active, session_get, session_delete, session_clear_all
|
|
167
|
-
Other: list_models, cli_versions, upstream_contracts, provider_subcommands_* (read-only subcommand contract/drift introspection), cli_upgrade, approval_list, llm_process_health, llm_request_result (read back any persisted request
|
|
167
|
+
Other: list_models, provider_tool_capabilities, cli_versions, upstream_contracts, provider_subcommands_* (read-only subcommand contract/drift introspection), cli_upgrade, approval_list, llm_process_health, llm_request_result (read back any persisted request, sync or async, by correlationId)
|
|
168
|
+
Workspaces: workspace_create, workspace_list, workspace_get, workspace_register_existing_repo
|
|
168
169
|
|
|
169
170
|
Key behaviors:
|
|
170
171
|
${deferralLine}
|
|
171
|
-
- Sessions: Claude --continue, Gemini (Antigravity) --conversation <id>/--continue, Grok --resume/--continue, Mistral --resume/--continue (current Vibe defaults session logging on; doctor flags explicit session_logging.enabled=false), Codex \`exec resume <ID>\` / \`exec resume --last
|
|
172
|
+
- Sessions: Claude --continue, Gemini (Antigravity) --conversation <id>/--continue, Grok --resume/--continue, Mistral --resume/--continue (current Vibe defaults session logging on; doctor flags explicit session_logging.enabled=false), Codex \`exec resume <ID>\` / \`exec resume --last\`, Devin --resume <id>/--continue (all real CLI continuity). For Codex, sessionId must be a real Codex UUID (from ~/.codex/sessions/); gateway-generated gw-* IDs are rejected.
|
|
172
173
|
- Approval gates: opt-in via approvalStrategy:"mcp_managed".
|
|
173
174
|
- Upstream drift detection: After upgrading any provider CLI (especially grok), use upstream_contracts with probeInstalled:true and provider_subcommand_drift for declared subcommand help surfaces. Probes are safe, read-only --help checks.
|
|
174
175
|
- Idle timeout kills stuck processes (default 10min, configurable via idleTimeoutMs).
|
|
@@ -280,6 +281,11 @@ export const WORKSPACE_ALIAS_SCHEMA = z
|
|
|
280
281
|
.describe("Registered workspace alias. Remote clients use aliases, not absolute paths.");
|
|
281
282
|
export const SESSION_PROVIDER_VALUES = PROVIDER_TYPES;
|
|
282
283
|
export const SESSION_PROVIDER_ENUM = z.enum(SESSION_PROVIDER_VALUES);
|
|
284
|
+
export function sessionProviderValuesFor(providers) {
|
|
285
|
+
return [
|
|
286
|
+
...new Set([...SESSION_PROVIDER_VALUES, ...enabledApiProviders(providers).map(p => p.name)]),
|
|
287
|
+
];
|
|
288
|
+
}
|
|
283
289
|
let activeServer = null;
|
|
284
290
|
let activeHttpGateway = null;
|
|
285
291
|
export function resolveGatewayServerRuntime(deps = {}, options = {}) {
|
|
@@ -458,7 +464,7 @@ async function awaitJobOrDefer(cli, args, corrId, idleTimeoutMs, outputFormat, f
|
|
|
458
464
|
message: `Execution exceeded sync deadline (${SYNC_DEADLINE_MS}ms). Poll with llm_job_status, collect with llm_job_result.`,
|
|
459
465
|
};
|
|
460
466
|
}
|
|
461
|
-
async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolveGatewayServerRuntime(), onComplete, flightRecorderEntry, extractUsage) {
|
|
467
|
+
async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolveGatewayServerRuntime(), onComplete, flightRecorderEntry, extractUsage, forceRefresh, forceInline) {
|
|
462
468
|
let onCompleteOwnedByCaller = onComplete !== undefined;
|
|
463
469
|
const consumeOnComplete = () => {
|
|
464
470
|
if (!onCompleteOwnedByCaller || !onComplete)
|
|
@@ -474,13 +480,28 @@ async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolv
|
|
|
474
480
|
const deferralAvailable = runtime.persistence.backend !== "none" &&
|
|
475
481
|
runtime.persistence.asyncJobsEnabled &&
|
|
476
482
|
runtime.asyncJobManager.hasStore();
|
|
477
|
-
if (SYNC_DEADLINE_MS === 0 || !deferralAvailable) {
|
|
483
|
+
if (SYNC_DEADLINE_MS === 0 || !deferralAvailable || forceInline) {
|
|
478
484
|
try {
|
|
479
485
|
const result = await runApiRequest(provider, apiRequest, runtime.logger);
|
|
480
|
-
return {
|
|
486
|
+
return {
|
|
487
|
+
stdout: result.text,
|
|
488
|
+
stderr: "",
|
|
489
|
+
code: 0,
|
|
490
|
+
usage: result.usage,
|
|
491
|
+
httpStatus: result.httpStatus,
|
|
492
|
+
responseId: result.responseId ?? null,
|
|
493
|
+
model: result.model,
|
|
494
|
+
status: result.status ?? null,
|
|
495
|
+
};
|
|
481
496
|
}
|
|
482
497
|
catch (err) {
|
|
483
|
-
return {
|
|
498
|
+
return {
|
|
499
|
+
stdout: "",
|
|
500
|
+
stderr: err.message,
|
|
501
|
+
code: 1,
|
|
502
|
+
httpStatus: extractApiHttpStatus(err) ?? undefined,
|
|
503
|
+
errorBody: extractApiErrorBody(err),
|
|
504
|
+
};
|
|
484
505
|
}
|
|
485
506
|
finally {
|
|
486
507
|
consumeOnComplete();
|
|
@@ -495,6 +516,7 @@ async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolv
|
|
|
495
516
|
onComplete,
|
|
496
517
|
flightRecorderEntry,
|
|
497
518
|
extractUsage,
|
|
519
|
+
forceRefresh,
|
|
498
520
|
});
|
|
499
521
|
onCompleteOwnedByCaller = false;
|
|
500
522
|
}
|
|
@@ -517,6 +539,11 @@ async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolv
|
|
|
517
539
|
stdout: result.stdout,
|
|
518
540
|
stderr: result.stderr || result.error || "",
|
|
519
541
|
code: result.exitCode ?? 1,
|
|
542
|
+
usage: result.apiUsage,
|
|
543
|
+
httpStatus: result.httpStatus,
|
|
544
|
+
responseId: result.responseId,
|
|
545
|
+
model: result.model,
|
|
546
|
+
errorBody: result.errorBody,
|
|
520
547
|
};
|
|
521
548
|
}
|
|
522
549
|
await new Promise(resolve => setTimeout(resolve, SYNC_POLL_INTERVAL_MS));
|
|
@@ -780,22 +807,41 @@ function registerWorkspaceTools(server, runtime) {
|
|
|
780
807
|
}
|
|
781
808
|
});
|
|
782
809
|
}
|
|
783
|
-
function createErrorResponse(cli, code, stderr, correlationId, error) {
|
|
810
|
+
export function createErrorResponse(cli, code, stderr, correlationId, error, apiError) {
|
|
784
811
|
let errorMessage = `Error executing ${cli} CLI`;
|
|
785
812
|
const isLaunchExit = code === 127 || code === -4058;
|
|
813
|
+
const isOutputOverflow = Boolean(error) && /Output exceeded maximum size/i.test(error.message);
|
|
814
|
+
const authProbe = `${error?.message ?? ""}\n${stderr ?? ""}`;
|
|
815
|
+
const isAuthError = /not logged in|please run .*login|unauthorized|\b401\b|authentication failed|invalid api key|no api key|expired token|re-?authenticate/i.test(authProbe);
|
|
816
|
+
const authRemediation = {
|
|
817
|
+
claude: "Run `claude login` (or set ANTHROPIC_API_KEY) and retry.",
|
|
818
|
+
codex: "Run `codex login` and retry.",
|
|
819
|
+
gemini: "Re-authenticate the Antigravity `agy` CLI and retry.",
|
|
820
|
+
grok: "Run `grok login` (or re-authenticate) and retry.",
|
|
821
|
+
mistral: "Run `vibe --setup` (or re-authenticate) and retry.",
|
|
822
|
+
devin: "Run `devin auth login` (or set WINDSURF_API_KEY) and retry.",
|
|
823
|
+
};
|
|
786
824
|
if (error) {
|
|
787
825
|
errorMessage += `:\n${error.message}`;
|
|
788
826
|
if (error.message.includes("ENOENT")) {
|
|
789
827
|
errorMessage += `\n\nThe '${cli}' command was not found. Please ensure ${cli} CLI is installed and in your PATH.`;
|
|
790
828
|
}
|
|
829
|
+
else if (/EACCES|EPERM/.test(error.message)) {
|
|
830
|
+
errorMessage += `\n\nThe '${cli}' binary is not executable or permission was denied (EACCES/EPERM). Check its file permissions and PATH.`;
|
|
831
|
+
}
|
|
832
|
+
else if (isOutputOverflow) {
|
|
833
|
+
errorMessage += `\n\nThe ${cli} output exceeded the 50MB cap. Narrow the request, ask for structured/JSON output, or split the work into smaller calls.`;
|
|
834
|
+
}
|
|
791
835
|
logger.error(`[${correlationId || "unknown"}] ${cli} CLI execution failed:`, error.message);
|
|
792
836
|
}
|
|
793
837
|
else if (code === 124) {
|
|
794
838
|
errorMessage += `: Command timed out\n${stderr}`;
|
|
839
|
+
errorMessage += `\n\nIncrease timeoutMs, or use the *_request_async variant and poll with llm_job_status / llm_job_result.`;
|
|
795
840
|
logger.error(`[${correlationId || "unknown"}] ${cli} CLI timed out`);
|
|
796
841
|
}
|
|
797
842
|
else if (code === 125) {
|
|
798
843
|
errorMessage += `: Process killed due to inactivity\n${stderr}`;
|
|
844
|
+
errorMessage += `\n\nThe CLI may have been awaiting interactive approval. Pass a non-interactive permission/approval option, or raise idleTimeoutMs.`;
|
|
799
845
|
logger.error(`[${correlationId || "unknown"}] ${cli} CLI killed due to inactivity`);
|
|
800
846
|
}
|
|
801
847
|
else if (isLaunchExit) {
|
|
@@ -804,8 +850,14 @@ function createErrorResponse(cli, code, stderr, correlationId, error) {
|
|
|
804
850
|
}
|
|
805
851
|
else if (code !== 0) {
|
|
806
852
|
errorMessage += ` (exit code ${code}):\n${stderr}`;
|
|
853
|
+
if (!stderr || stderr.trim().length === 0) {
|
|
854
|
+
errorMessage += `\n\nNo error output was captured. The CLI may not be authenticated, may be awaiting interactive input, or may have rejected an unsupported flag. Re-run with DEBUG=1 for more detail.`;
|
|
855
|
+
}
|
|
807
856
|
logger.error(`[${correlationId || "unknown"}] ${cli} CLI failed with exit code ${code}`);
|
|
808
857
|
}
|
|
858
|
+
if (isAuthError) {
|
|
859
|
+
errorMessage += `\n\n${authRemediation[cli] ?? `Re-authenticate the ${cli} CLI and retry.`}`;
|
|
860
|
+
}
|
|
809
861
|
return {
|
|
810
862
|
content: [{ type: "text", text: errorMessage }],
|
|
811
863
|
isError: true,
|
|
@@ -814,20 +866,30 @@ function createErrorResponse(cli, code, stderr, correlationId, error) {
|
|
|
814
866
|
correlationId: correlationId || null,
|
|
815
867
|
cli,
|
|
816
868
|
exitCode: code,
|
|
817
|
-
errorCategory:
|
|
818
|
-
? "
|
|
819
|
-
: code ===
|
|
820
|
-
? "
|
|
821
|
-
:
|
|
822
|
-
? "
|
|
823
|
-
:
|
|
824
|
-
? "
|
|
825
|
-
:
|
|
869
|
+
errorCategory: isAuthError
|
|
870
|
+
? "auth_error"
|
|
871
|
+
: code === 124
|
|
872
|
+
? "timeout"
|
|
873
|
+
: code === 125
|
|
874
|
+
? "idle_timeout"
|
|
875
|
+
: isOutputOverflow
|
|
876
|
+
? "output_overflow"
|
|
877
|
+
: error
|
|
878
|
+
? "spawn_error"
|
|
879
|
+
: isLaunchExit
|
|
880
|
+
? "spawn_error"
|
|
881
|
+
: "cli_error",
|
|
882
|
+
...(apiError
|
|
883
|
+
? {
|
|
884
|
+
httpStatus: apiError.httpStatus ?? undefined,
|
|
885
|
+
responseBody: apiError.responseBody,
|
|
886
|
+
}
|
|
887
|
+
: {}),
|
|
826
888
|
},
|
|
827
889
|
};
|
|
828
890
|
}
|
|
829
891
|
export function extractUsageAndCost(cli, output, outputFormat, ctx) {
|
|
830
|
-
if (cli === "claude" && outputFormat === "stream-json") {
|
|
892
|
+
if (cli === "claude" && (outputFormat === "stream-json" || outputFormat === "json")) {
|
|
831
893
|
const parsed = parseStreamJson(output);
|
|
832
894
|
if (!parsed.usage) {
|
|
833
895
|
return { costUsd: parsed.costUsd ?? undefined };
|
|
@@ -1191,9 +1253,30 @@ function registerBaseResources(server, runtime) {
|
|
|
1191
1253
|
const contents = await runtime.resourceProvider.readResource(uri.href);
|
|
1192
1254
|
return { contents: contents ? [contents] : [] };
|
|
1193
1255
|
});
|
|
1256
|
+
const validationReceiptStore = runtime.persistence.backend === "sqlite"
|
|
1257
|
+
? runtime.asyncJobManager.getValidationRunStore()
|
|
1258
|
+
: null;
|
|
1259
|
+
if (validationReceiptStore) {
|
|
1260
|
+
server.registerResource("validation-receipt", new ResourceTemplate("validation-receipt://{validationId}", { list: undefined }), {
|
|
1261
|
+
title: "Validation Receipt",
|
|
1262
|
+
description: "Immutable receipt of a terminal cross-LLM validation run (own-or-not-found).",
|
|
1263
|
+
mimeType: "application/json",
|
|
1264
|
+
}, async (uri, variables) => {
|
|
1265
|
+
const validationId = Array.isArray(variables.validationId)
|
|
1266
|
+
? variables.validationId[0]
|
|
1267
|
+
: variables.validationId;
|
|
1268
|
+
runtime.logger.debug(`Reading validation-receipt://${validationId}`);
|
|
1269
|
+
const result = resolveValidationReceipt({ asyncJobManager: runtime.asyncJobManager, validationRunStore: validationReceiptStore }, String(validationId), { caller: currentCaller() });
|
|
1270
|
+
return {
|
|
1271
|
+
contents: [
|
|
1272
|
+
{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(result, null, 2) },
|
|
1273
|
+
],
|
|
1274
|
+
};
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1194
1277
|
}
|
|
1195
1278
|
function resolvePromptOrPartsForPrep(args) {
|
|
1196
|
-
const hasPrompt = typeof args.prompt === "string" && args.prompt.length > 0;
|
|
1279
|
+
const hasPrompt = typeof args.prompt === "string" && args.prompt.trim().length > 0;
|
|
1197
1280
|
const hasParts = args.promptParts !== undefined;
|
|
1198
1281
|
if (hasPrompt && hasParts) {
|
|
1199
1282
|
return {
|
|
@@ -1705,6 +1788,15 @@ export function prepareGeminiRequest(params, runtime = resolveGatewayServerRunti
|
|
|
1705
1788
|
if (params.yolo || effectiveApprovalMode === "yolo") {
|
|
1706
1789
|
args.push("--dangerously-skip-permissions");
|
|
1707
1790
|
}
|
|
1791
|
+
if (params.project !== undefined && params.newProject) {
|
|
1792
|
+
return createErrorResponse(params.operation, 1, "", corrId, new Error("project and newProject are mutually exclusive"));
|
|
1793
|
+
}
|
|
1794
|
+
if (params.project !== undefined && params.project !== "") {
|
|
1795
|
+
args.push("--project", params.project);
|
|
1796
|
+
}
|
|
1797
|
+
if (params.newProject) {
|
|
1798
|
+
args.push("--new-project");
|
|
1799
|
+
}
|
|
1708
1800
|
return {
|
|
1709
1801
|
corrId,
|
|
1710
1802
|
effectivePrompt,
|
|
@@ -1818,12 +1910,30 @@ export function prepareGrokRequest(params, runtime = resolveGatewayServerRuntime
|
|
|
1818
1910
|
}
|
|
1819
1911
|
args.push(...buildArgvFromGeneration(grokContract, GROK_GEN_SINGLE, genParams));
|
|
1820
1912
|
args.push(...buildArgvFromGeneration(grokContract, GROK_GEN_TAIL, genParams));
|
|
1913
|
+
const hasNativeWorktree = params.nativeWorktree === true ||
|
|
1914
|
+
(typeof params.nativeWorktree === "string" && params.nativeWorktree.length > 0);
|
|
1821
1915
|
if (params.nativeWorktree === true) {
|
|
1822
1916
|
args.push("--worktree");
|
|
1823
1917
|
}
|
|
1824
1918
|
else if (typeof params.nativeWorktree === "string" && params.nativeWorktree.length > 0) {
|
|
1825
1919
|
args.push("--worktree", params.nativeWorktree);
|
|
1826
1920
|
}
|
|
1921
|
+
if (params.worktreeRef !== undefined && params.worktreeRef !== "") {
|
|
1922
|
+
if (!hasNativeWorktree) {
|
|
1923
|
+
return createErrorResponse(params.operation, 1, "", corrId, new Error("worktreeRef: requires nativeWorktree (grok --worktree-ref needs --worktree)"));
|
|
1924
|
+
}
|
|
1925
|
+
args.push("--worktree-ref", params.worktreeRef);
|
|
1926
|
+
}
|
|
1927
|
+
if (params.forkSession) {
|
|
1928
|
+
args.push("--fork-session");
|
|
1929
|
+
}
|
|
1930
|
+
if (params.jsonSchema !== undefined) {
|
|
1931
|
+
const schemaArg = typeof params.jsonSchema === "string" ? params.jsonSchema : JSON.stringify(params.jsonSchema);
|
|
1932
|
+
if (!schemaArg.trim()) {
|
|
1933
|
+
return createErrorResponse(params.operation, 1, "", corrId, new Error("jsonSchema: must be a non-empty JSON Schema string or object"));
|
|
1934
|
+
}
|
|
1935
|
+
args.push("--json-schema", schemaArg);
|
|
1936
|
+
}
|
|
1827
1937
|
return {
|
|
1828
1938
|
corrId,
|
|
1829
1939
|
effectivePrompt,
|
|
@@ -1960,7 +2070,7 @@ export function buildMistralRetryPrep(params, recoveryModel) {
|
|
|
1960
2070
|
addDir: params.addDir,
|
|
1961
2071
|
});
|
|
1962
2072
|
}
|
|
1963
|
-
function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionId, prep, durationMs, resumable, outputFormat, warnings) {
|
|
2073
|
+
export function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionId, prep, durationMs, resumable, outputFormat, warnings) {
|
|
1964
2074
|
let finalStdout = stdout;
|
|
1965
2075
|
if (cli === "codex" && outputFormat !== "json") {
|
|
1966
2076
|
finalStdout = codexDisplayText(stdout);
|
|
@@ -1978,9 +2088,37 @@ function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionId, prep
|
|
|
1978
2088
|
.join("\n");
|
|
1979
2089
|
finalStdout += `\n\n⚠️ Review Integrity Warnings (score: ${prep.reviewIntegrity.totalScore}):\n${warnings}`;
|
|
1980
2090
|
}
|
|
2091
|
+
const derivedWarnings = [];
|
|
2092
|
+
const extraStructured = {};
|
|
2093
|
+
let claudeResultError = false;
|
|
2094
|
+
if (cli === "claude") {
|
|
2095
|
+
const parsedResult = parseStreamJson(stdout);
|
|
2096
|
+
if (parsedResult.isError) {
|
|
2097
|
+
claudeResultError = true;
|
|
2098
|
+
extraStructured.resultIsError = true;
|
|
2099
|
+
derivedWarnings.push({
|
|
2100
|
+
code: "claude_result_error",
|
|
2101
|
+
message: "Claude exited 0 but the result event reported is_error:true (e.g. max-turns or mid-run error). The returned text may be partial.",
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
if (cli === "codex") {
|
|
2106
|
+
const parsedCodex = parseCodexJsonStream(stdout);
|
|
2107
|
+
if (parsedCodex.threadId) {
|
|
2108
|
+
extraStructured.codexSessionId = parsedCodex.threadId;
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
if (!claudeResultError && finalStdout.trim().length === 0) {
|
|
2112
|
+
extraStructured.emptyOutput = true;
|
|
2113
|
+
derivedWarnings.push({
|
|
2114
|
+
code: "empty_output",
|
|
2115
|
+
message: `${cli} exited 0 but produced no assistant output. The request may have been a no-op, hit a guardrail, or awaited input.`,
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
1981
2118
|
const response = {
|
|
1982
2119
|
content: [{ type: "text", text: finalStdout }],
|
|
1983
2120
|
structuredContent: {
|
|
2121
|
+
...extraStructured,
|
|
1984
2122
|
response: finalStdout,
|
|
1985
2123
|
model: prep.resolvedModel || "default",
|
|
1986
2124
|
cli,
|
|
@@ -2011,8 +2149,9 @@ function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionId, prep
|
|
|
2011
2149
|
if (prep.reviewIntegrity && prep.reviewIntegrity.violations.length > 0) {
|
|
2012
2150
|
response.reviewIntegrity = prep.reviewIntegrity;
|
|
2013
2151
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2152
|
+
const allWarnings = [...(warnings ?? []), ...derivedWarnings];
|
|
2153
|
+
if (allWarnings.length > 0) {
|
|
2154
|
+
response.warnings = allWarnings;
|
|
2016
2155
|
}
|
|
2017
2156
|
return response;
|
|
2018
2157
|
}
|
|
@@ -2123,11 +2262,18 @@ async function getExistingSessionForProvider(sessionManager, sessionId, provider
|
|
|
2123
2262
|
}
|
|
2124
2263
|
return existing;
|
|
2125
2264
|
}
|
|
2126
|
-
function
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2265
|
+
function grokInputToMessages(input, instructions) {
|
|
2266
|
+
const messages = [];
|
|
2267
|
+
if (instructions && instructions.length > 0) {
|
|
2268
|
+
messages.push({ role: "system", content: instructions });
|
|
2269
|
+
}
|
|
2270
|
+
if (typeof input === "string") {
|
|
2271
|
+
messages.push({ role: "user", content: input });
|
|
2272
|
+
}
|
|
2273
|
+
else {
|
|
2274
|
+
messages.push(...input);
|
|
2275
|
+
}
|
|
2276
|
+
return messages;
|
|
2131
2277
|
}
|
|
2132
2278
|
function buildGrokApiToolResponse(args) {
|
|
2133
2279
|
let text = args.result.text;
|
|
@@ -2221,12 +2367,12 @@ export async function handleGrokApiRequest(deps, params) {
|
|
|
2221
2367
|
const session = await resolveGrokApiSession(params, runtime);
|
|
2222
2368
|
sessionId = session.sessionId;
|
|
2223
2369
|
previousResponseId = session.previousResponseId;
|
|
2224
|
-
const
|
|
2370
|
+
const grokProvider = createApiProvider("grok-api", "xai-responses");
|
|
2371
|
+
const call = (prev) => runApiRequest(grokProvider, {
|
|
2225
2372
|
baseUrl: xaiConfig.baseUrl,
|
|
2226
2373
|
apiKey,
|
|
2227
2374
|
model: prep.resolvedModel,
|
|
2228
|
-
|
|
2229
|
-
instructions: prep.instructions,
|
|
2375
|
+
messages: grokInputToMessages(prep.input, prep.instructions),
|
|
2230
2376
|
previousResponseId: prev,
|
|
2231
2377
|
maxOutputTokens: params.maxOutputTokens,
|
|
2232
2378
|
temperature: params.temperature,
|
|
@@ -2239,8 +2385,7 @@ export async function handleGrokApiRequest(deps, params) {
|
|
|
2239
2385
|
result = await call(previousResponseId);
|
|
2240
2386
|
}
|
|
2241
2387
|
catch (error) {
|
|
2242
|
-
|
|
2243
|
-
if (xaiError?.status === 404 && previousResponseId) {
|
|
2388
|
+
if (extractApiHttpStatus(error) === 404 && previousResponseId) {
|
|
2244
2389
|
runtime.logger.warn(`[${corrId}] xAI previous_response_id was rejected; clearing stale session metadata and retrying fresh`);
|
|
2245
2390
|
await runtime.sessionManager.updateSessionMetadata(sessionId, {
|
|
2246
2391
|
xaiPreviousResponseId: null,
|
|
@@ -2286,10 +2431,9 @@ export async function handleGrokApiRequest(deps, params) {
|
|
|
2286
2431
|
catch (error) {
|
|
2287
2432
|
durationMs = Math.max(0, Date.now() - startTime);
|
|
2288
2433
|
const err = error;
|
|
2289
|
-
const xaiError = asXaiApiError(error);
|
|
2290
2434
|
runtime.logger.error(`[${corrId}] grok_api_request failed`, err.message);
|
|
2291
2435
|
safeFlightComplete(corrId, {
|
|
2292
|
-
response:
|
|
2436
|
+
response: extractApiErrorBody(error) ?? "",
|
|
2293
2437
|
durationMs,
|
|
2294
2438
|
retryCount: 0,
|
|
2295
2439
|
circuitBreakerState: "closed",
|
|
@@ -2304,10 +2448,63 @@ export async function handleGrokApiRequest(deps, params) {
|
|
|
2304
2448
|
runtime.performanceMetrics.recordRequest("grok-api", durationMs || Math.max(0, Date.now() - startTime), wasSuccessful);
|
|
2305
2449
|
}
|
|
2306
2450
|
}
|
|
2307
|
-
function
|
|
2451
|
+
function resolveApiProviderInput(params, toolName, corrId) {
|
|
2452
|
+
const inputResolution = resolvePromptOrPartsForPrep({
|
|
2453
|
+
prompt: params.prompt,
|
|
2454
|
+
promptParts: params.promptParts,
|
|
2455
|
+
operation: toolName,
|
|
2456
|
+
correlationId: corrId,
|
|
2457
|
+
});
|
|
2458
|
+
if (!inputResolution.ok)
|
|
2459
|
+
return { ok: false, error: inputResolution.error };
|
|
2460
|
+
const system = params.promptParts?.system && params.promptParts.system.length > 0
|
|
2461
|
+
? params.promptParts.system
|
|
2462
|
+
: params.system;
|
|
2463
|
+
let userContent = params.promptParts
|
|
2464
|
+
? buildXaiPromptPartsUserContent(params.promptParts)
|
|
2465
|
+
: (params.prompt ?? "");
|
|
2466
|
+
let optimizationApplied = false;
|
|
2467
|
+
if (params.optimizePrompt) {
|
|
2468
|
+
const optimized = optimizePromptText(userContent);
|
|
2469
|
+
logOptimizationTokens("prompt", corrId, userContent, optimized);
|
|
2470
|
+
userContent = optimized;
|
|
2471
|
+
optimizationApplied = true;
|
|
2472
|
+
}
|
|
2473
|
+
return {
|
|
2474
|
+
ok: true,
|
|
2475
|
+
userContent,
|
|
2476
|
+
system,
|
|
2477
|
+
effectivePrompt: buildXaiPromptPartsEffectivePrompt(system, userContent),
|
|
2478
|
+
optimizationApplied,
|
|
2479
|
+
stablePrefixHash: inputResolution.stablePrefixHash,
|
|
2480
|
+
stablePrefixTokens: inputResolution.stablePrefixTokens,
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
async function resolveApiSession(providerName, continuity, params, runtime) {
|
|
2484
|
+
const label = `${providerName} API Session`;
|
|
2485
|
+
const readPrev = (s) => continuity === "server-side-id" &&
|
|
2486
|
+
!params.createNewSession &&
|
|
2487
|
+
typeof s.metadata?.apiPreviousResponseId === "string"
|
|
2488
|
+
? s.metadata.apiPreviousResponseId
|
|
2489
|
+
: undefined;
|
|
2490
|
+
if (params.sessionId) {
|
|
2491
|
+
const existing = await getExistingSessionForProvider(runtime.sessionManager, params.sessionId, providerName);
|
|
2492
|
+
const session = existing ??
|
|
2493
|
+
(await runtime.sessionManager.createSession(providerName, label, params.sessionId));
|
|
2494
|
+
return { sessionId: session.id, previousResponseId: readPrev(session) };
|
|
2495
|
+
}
|
|
2496
|
+
if (continuity === "server-side-id" && !params.createNewSession) {
|
|
2497
|
+
const active = await getCallerOwnedActiveSession(runtime.sessionManager, providerName);
|
|
2498
|
+
if (active)
|
|
2499
|
+
return { sessionId: active.id, previousResponseId: readPrev(active) };
|
|
2500
|
+
}
|
|
2501
|
+
const session = await runtime.sessionManager.createSession(providerName, label, `${GATEWAY_SESSION_PREFIX}${randomUUID()}`);
|
|
2502
|
+
return { sessionId: session.id };
|
|
2503
|
+
}
|
|
2504
|
+
function buildApiProviderCall(providerRuntime, params, userContent, system) {
|
|
2308
2505
|
const apiRequest = prepareApiRequest(providerRuntime, {
|
|
2309
|
-
prompt:
|
|
2310
|
-
system
|
|
2506
|
+
prompt: userContent,
|
|
2507
|
+
system,
|
|
2311
2508
|
model: params.model,
|
|
2312
2509
|
maxOutputTokens: params.maxOutputTokens,
|
|
2313
2510
|
temperature: params.temperature,
|
|
@@ -2318,16 +2515,32 @@ function buildApiProviderCall(providerRuntime, params) {
|
|
|
2318
2515
|
const provider = createApiProvider(providerRuntime.name, providerRuntime.kind);
|
|
2319
2516
|
return { provider, apiRequest };
|
|
2320
2517
|
}
|
|
2321
|
-
function buildApiSuccessResponse(text, corrId, providerName) {
|
|
2322
|
-
|
|
2518
|
+
function buildApiSuccessResponse(text, corrId, providerName, telemetry) {
|
|
2519
|
+
const usage = telemetry?.usage;
|
|
2520
|
+
const response = {
|
|
2323
2521
|
content: [{ type: "text", text }],
|
|
2324
2522
|
structuredContent: {
|
|
2325
2523
|
response: text,
|
|
2326
|
-
|
|
2524
|
+
provider: providerName,
|
|
2327
2525
|
cli: providerName,
|
|
2526
|
+
model: telemetry?.model,
|
|
2527
|
+
correlationId: corrId,
|
|
2528
|
+
sessionId: telemetry?.sessionId ?? null,
|
|
2529
|
+
responseId: telemetry?.responseId ?? null,
|
|
2530
|
+
previousResponseId: telemetry?.previousResponseId ?? null,
|
|
2531
|
+
stalePreviousResponseCleared: telemetry?.stalePreviousResponseCleared ?? false,
|
|
2532
|
+
status: telemetry?.status ?? undefined,
|
|
2533
|
+
httpStatus: telemetry?.httpStatus ?? undefined,
|
|
2534
|
+
inputTokens: usage?.inputTokens,
|
|
2535
|
+
outputTokens: usage?.outputTokens,
|
|
2536
|
+
cacheReadTokens: usage?.cacheReadTokens,
|
|
2537
|
+
costUsd: usage?.costUsd,
|
|
2328
2538
|
exitCode: 0,
|
|
2329
2539
|
},
|
|
2330
2540
|
};
|
|
2541
|
+
if (telemetry?.sessionId)
|
|
2542
|
+
response.sessionId = telemetry.sessionId;
|
|
2543
|
+
return response;
|
|
2331
2544
|
}
|
|
2332
2545
|
export async function handleApiProviderRequest(runtimeArg, providerRuntime, params) {
|
|
2333
2546
|
const toolName = `api_${providerRuntime.name}_request`;
|
|
@@ -2335,18 +2548,115 @@ export async function handleApiProviderRequest(runtimeArg, providerRuntime, para
|
|
|
2335
2548
|
const startTime = Date.now();
|
|
2336
2549
|
let wasSuccessful = false;
|
|
2337
2550
|
try {
|
|
2338
|
-
|
|
2339
|
-
|
|
2551
|
+
const resolved = resolveApiProviderInput(params, toolName, corrId);
|
|
2552
|
+
if (!resolved.ok)
|
|
2553
|
+
return resolved.error;
|
|
2554
|
+
const { provider, apiRequest } = buildApiProviderCall(providerRuntime, params, resolved.userContent, resolved.system);
|
|
2555
|
+
const serverSide = provider.continuity === "server-side-id";
|
|
2556
|
+
const wantsSession = serverSide || params.sessionId !== undefined || params.createNewSession === true;
|
|
2557
|
+
let sessionId;
|
|
2558
|
+
let previousResponseId;
|
|
2559
|
+
if (wantsSession) {
|
|
2560
|
+
const session = await resolveApiSession(providerRuntime.name, provider.continuity, params, runtimeArg);
|
|
2561
|
+
sessionId = session.sessionId;
|
|
2562
|
+
previousResponseId = session.previousResponseId;
|
|
2563
|
+
await runtimeArg.sessionManager.updateSessionUsage(sessionId);
|
|
2564
|
+
if (previousResponseId)
|
|
2565
|
+
apiRequest.previousResponseId = previousResponseId;
|
|
2566
|
+
}
|
|
2567
|
+
const flightRecorderEntry = {
|
|
2568
|
+
model: apiRequest.model,
|
|
2569
|
+
prompt: resolved.effectivePrompt,
|
|
2570
|
+
sessionId,
|
|
2571
|
+
stablePrefixHash: resolved.stablePrefixHash ?? undefined,
|
|
2572
|
+
stablePrefixTokens: resolved.stablePrefixTokens ?? undefined,
|
|
2573
|
+
};
|
|
2574
|
+
safeFlightStart({
|
|
2575
|
+
correlationId: corrId,
|
|
2576
|
+
cli: providerRuntime.name,
|
|
2577
|
+
model: apiRequest.model,
|
|
2578
|
+
prompt: resolved.effectivePrompt,
|
|
2579
|
+
system: resolved.system,
|
|
2580
|
+
sessionId,
|
|
2581
|
+
stablePrefixHash: resolved.stablePrefixHash ?? undefined,
|
|
2582
|
+
stablePrefixTokens: resolved.stablePrefixTokens ?? undefined,
|
|
2583
|
+
}, runtimeArg);
|
|
2584
|
+
let result = await awaitApiJobOrDefer(provider, apiRequest, corrId, runtimeArg, undefined, flightRecorderEntry, undefined, params.forceRefresh, serverSide);
|
|
2585
|
+
let stalePreviousResponseCleared = false;
|
|
2586
|
+
if (serverSide &&
|
|
2587
|
+
!isDeferredResponse(result) &&
|
|
2588
|
+
result.code !== 0 &&
|
|
2589
|
+
result.httpStatus === 404 &&
|
|
2590
|
+
sessionId &&
|
|
2591
|
+
previousResponseId) {
|
|
2592
|
+
await runtimeArg.sessionManager.updateSessionMetadata(sessionId, {
|
|
2593
|
+
apiPreviousResponseId: null,
|
|
2594
|
+
apiResponseCreatedAt: null,
|
|
2595
|
+
});
|
|
2596
|
+
apiRequest.previousResponseId = undefined;
|
|
2597
|
+
previousResponseId = undefined;
|
|
2598
|
+
stalePreviousResponseCleared = true;
|
|
2599
|
+
const retry = await awaitApiJobOrDefer(provider, apiRequest, corrId, runtimeArg, undefined, flightRecorderEntry, undefined, true, serverSide);
|
|
2600
|
+
if (!isDeferredResponse(retry))
|
|
2601
|
+
result = retry;
|
|
2340
2602
|
}
|
|
2341
|
-
const { provider, apiRequest } = buildApiProviderCall(providerRuntime, params);
|
|
2342
|
-
const result = await awaitApiJobOrDefer(provider, apiRequest, corrId, runtimeArg);
|
|
2343
2603
|
if (isDeferredResponse(result))
|
|
2344
|
-
return buildDeferredToolResponse(result);
|
|
2604
|
+
return buildDeferredToolResponse(result, sessionId);
|
|
2605
|
+
const durationMs = Math.max(0, Date.now() - startTime);
|
|
2345
2606
|
if (result.code !== 0) {
|
|
2346
|
-
|
|
2607
|
+
safeFlightComplete(corrId, {
|
|
2608
|
+
response: result.errorBody ?? "",
|
|
2609
|
+
durationMs,
|
|
2610
|
+
retryCount: 0,
|
|
2611
|
+
circuitBreakerState: "closed",
|
|
2612
|
+
optimizationApplied: resolved.optimizationApplied,
|
|
2613
|
+
exitCode: result.code,
|
|
2614
|
+
httpStatus: result.httpStatus ?? undefined,
|
|
2615
|
+
errorMessage: result.stderr,
|
|
2616
|
+
status: "failed",
|
|
2617
|
+
}, runtimeArg);
|
|
2618
|
+
return createErrorResponse(toolName, result.code, result.stderr, corrId, undefined, {
|
|
2619
|
+
httpStatus: result.httpStatus,
|
|
2620
|
+
responseBody: result.errorBody,
|
|
2621
|
+
});
|
|
2347
2622
|
}
|
|
2348
2623
|
wasSuccessful = true;
|
|
2349
|
-
|
|
2624
|
+
if (serverSide && sessionId && result.responseId) {
|
|
2625
|
+
await runtimeArg.sessionManager.updateSessionMetadata(sessionId, {
|
|
2626
|
+
apiPreviousResponseId: result.responseId,
|
|
2627
|
+
apiResponseCreatedAt: new Date().toISOString(),
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
let text = result.stdout;
|
|
2631
|
+
if (params.optimizeResponse) {
|
|
2632
|
+
const optimized = optimizeResponseText(text);
|
|
2633
|
+
logOptimizationTokens("response", corrId, text, optimized);
|
|
2634
|
+
text = optimized;
|
|
2635
|
+
}
|
|
2636
|
+
safeFlightComplete(corrId, {
|
|
2637
|
+
response: text,
|
|
2638
|
+
durationMs,
|
|
2639
|
+
retryCount: 0,
|
|
2640
|
+
circuitBreakerState: "closed",
|
|
2641
|
+
optimizationApplied: resolved.optimizationApplied || (params.optimizeResponse ?? false),
|
|
2642
|
+
exitCode: 0,
|
|
2643
|
+
httpStatus: result.httpStatus ?? undefined,
|
|
2644
|
+
status: "completed",
|
|
2645
|
+
inputTokens: result.usage?.inputTokens,
|
|
2646
|
+
outputTokens: result.usage?.outputTokens,
|
|
2647
|
+
cacheReadTokens: result.usage?.cacheReadTokens,
|
|
2648
|
+
costUsd: result.usage?.costUsd,
|
|
2649
|
+
}, runtimeArg);
|
|
2650
|
+
return buildApiSuccessResponse(text, corrId, providerRuntime.name, {
|
|
2651
|
+
model: result.model,
|
|
2652
|
+
httpStatus: result.httpStatus,
|
|
2653
|
+
responseId: result.responseId,
|
|
2654
|
+
usage: result.usage,
|
|
2655
|
+
sessionId,
|
|
2656
|
+
previousResponseId,
|
|
2657
|
+
stalePreviousResponseCleared,
|
|
2658
|
+
status: result.status,
|
|
2659
|
+
});
|
|
2350
2660
|
}
|
|
2351
2661
|
catch (err) {
|
|
2352
2662
|
if (err instanceof ApiModelNotAllowedError) {
|
|
@@ -2362,15 +2672,22 @@ export function handleApiProviderRequestAsync(runtimeArg, providerRuntime, param
|
|
|
2362
2672
|
const toolName = `api_${providerRuntime.name}_request_async`;
|
|
2363
2673
|
const corrId = params.correlationId ?? randomUUID();
|
|
2364
2674
|
try {
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
const { provider, apiRequest } = buildApiProviderCall(providerRuntime, params);
|
|
2675
|
+
const resolved = resolveApiProviderInput(params, toolName, corrId);
|
|
2676
|
+
if (!resolved.ok)
|
|
2677
|
+
return resolved.error;
|
|
2678
|
+
const { provider, apiRequest } = buildApiProviderCall(providerRuntime, params, resolved.userContent, resolved.system);
|
|
2369
2679
|
const outcome = runtimeArg.asyncJobManager.startHttpJob({
|
|
2370
2680
|
provider,
|
|
2371
2681
|
apiRequest,
|
|
2372
2682
|
correlationId: corrId,
|
|
2373
2683
|
writeFlightStart: true,
|
|
2684
|
+
forceRefresh: params.forceRefresh,
|
|
2685
|
+
flightRecorderEntry: {
|
|
2686
|
+
model: apiRequest.model,
|
|
2687
|
+
prompt: resolved.effectivePrompt,
|
|
2688
|
+
stablePrefixHash: resolved.stablePrefixHash ?? undefined,
|
|
2689
|
+
stablePrefixTokens: resolved.stablePrefixTokens ?? undefined,
|
|
2690
|
+
},
|
|
2374
2691
|
});
|
|
2375
2692
|
return buildDeferredToolResponse({
|
|
2376
2693
|
deferred: true,
|
|
@@ -2393,7 +2710,13 @@ const ApiReasoningEffortSchema = z.enum(["none", "low", "medium", "high"]);
|
|
|
2393
2710
|
export function registerApiProviderTools(server, runtime, providers, asyncJobsEnabled) {
|
|
2394
2711
|
const registered = [];
|
|
2395
2712
|
const inputSchema = {
|
|
2396
|
-
prompt: z
|
|
2713
|
+
prompt: z
|
|
2714
|
+
.string()
|
|
2715
|
+
.min(1)
|
|
2716
|
+
.max(100000)
|
|
2717
|
+
.optional()
|
|
2718
|
+
.describe("Prompt text (mutually exclusive with promptParts)"),
|
|
2719
|
+
promptParts: PromptPartsSchema.optional().describe("Cache-aware structured prompt { system?, tools?, context?, task }; mutually exclusive with prompt"),
|
|
2397
2720
|
system: z.string().max(100000).optional().describe("Optional system instruction"),
|
|
2398
2721
|
model: z
|
|
2399
2722
|
.string()
|
|
@@ -2401,18 +2724,56 @@ export function registerApiProviderTools(server, runtime, providers, asyncJobsEn
|
|
|
2401
2724
|
.optional()
|
|
2402
2725
|
.describe("Model id; defaults to the provider default_model"),
|
|
2403
2726
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
2404
|
-
maxOutputTokens: z
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2727
|
+
maxOutputTokens: z
|
|
2728
|
+
.number()
|
|
2729
|
+
.int()
|
|
2730
|
+
.positive()
|
|
2731
|
+
.max(100000000)
|
|
2732
|
+
.optional()
|
|
2733
|
+
.describe("Max output tokens. Bounded to safe integers <= 100000000. Anthropic-kind api providers default max output to 4096 tokens unless maxOutputTokens is set."),
|
|
2734
|
+
temperature: z
|
|
2735
|
+
.number()
|
|
2736
|
+
.finite()
|
|
2737
|
+
.min(0)
|
|
2738
|
+
.max(2)
|
|
2739
|
+
.optional()
|
|
2740
|
+
.describe("Sampling temperature passed to the provider API"),
|
|
2741
|
+
topP: z
|
|
2742
|
+
.number()
|
|
2743
|
+
.finite()
|
|
2744
|
+
.min(0)
|
|
2745
|
+
.max(1)
|
|
2746
|
+
.optional()
|
|
2747
|
+
.describe("Nucleus sampling top_p passed to the provider API"),
|
|
2748
|
+
reasoningEffort: ApiReasoningEffortSchema.optional().describe("Reasoning effort (none|low|medium|high). Only forwarded by the xai adapter; ignored by other provider kinds."),
|
|
2749
|
+
timeoutMs: z
|
|
2750
|
+
.number()
|
|
2751
|
+
.int()
|
|
2752
|
+
.min(30_000)
|
|
2753
|
+
.max(3_600_000)
|
|
2754
|
+
.optional()
|
|
2755
|
+
.describe("HTTP request timeout in ms (min 30s, max 1h, default 10m)"),
|
|
2756
|
+
optimizePrompt: z.boolean().optional().describe("Optimize the prompt before sending"),
|
|
2757
|
+
optimizeResponse: z
|
|
2758
|
+
.boolean()
|
|
2759
|
+
.optional()
|
|
2760
|
+
.describe("Optimize the response text (sync path only)"),
|
|
2761
|
+
forceRefresh: z.boolean().optional().describe("Bypass dedup and force a fresh request"),
|
|
2762
|
+
sessionId: z
|
|
2763
|
+
.string()
|
|
2764
|
+
.optional()
|
|
2765
|
+
.describe("Gateway session to resolve/track (sync tool; reuse keeps it alive). On the async api handler sessionId and createNewSession are currently inert."),
|
|
2766
|
+
createNewSession: z
|
|
2767
|
+
.boolean()
|
|
2768
|
+
.optional()
|
|
2769
|
+
.describe("Force a fresh session instead of reusing the active one. On the async api handler sessionId and createNewSession are currently inert."),
|
|
2409
2770
|
};
|
|
2410
2771
|
for (const providerRuntime of enabledApiProviders(providers)) {
|
|
2411
2772
|
const name = providerRuntime.name;
|
|
2412
|
-
server.tool(`api_${name}_request`, `Run a request against the "${name}" API provider (kind: ${providerRuntime.kind}) synchronously. Registered only when [providers.${name}] is configured and enabled.`, inputSchema, { title: `${name} API request`, readOnlyHint: false, openWorldHint: true }, async (params) => handleApiProviderRequest(runtime, providerRuntime, params));
|
|
2773
|
+
server.tool(`api_${name}_request`, `Run a request against the "${name}" API provider (kind: ${providerRuntime.kind}) synchronously. Registered only when [providers.${name}] is configured and enabled. OpenRouter-kind providers require usage_include=true in their provider config for cost reporting.`, inputSchema, { title: `${name} API request`, readOnlyHint: false, openWorldHint: true }, async (params) => handleApiProviderRequest(runtime, providerRuntime, params));
|
|
2413
2774
|
registered.push(`api_${name}_request`);
|
|
2414
2775
|
if (asyncJobsEnabled) {
|
|
2415
|
-
server.tool(`api_${name}_request_async`, `Start an async request against the "${name}" API provider; returns a jobId to poll with llm_job_status.`, inputSchema, { title: `${name} API request (async)`, readOnlyHint: false, openWorldHint: true }, async (params) => handleApiProviderRequestAsync(runtime, providerRuntime, params));
|
|
2776
|
+
server.tool(`api_${name}_request_async`, `Start an async request against the "${name}" API provider; returns a jobId to poll with llm_job_status. OpenRouter-kind providers require usage_include=true in their provider config for cost reporting.`, inputSchema, { title: `${name} API request (async)`, readOnlyHint: false, openWorldHint: true }, async (params) => handleApiProviderRequestAsync(runtime, providerRuntime, params));
|
|
2416
2777
|
registered.push(`api_${name}_request_async`);
|
|
2417
2778
|
}
|
|
2418
2779
|
}
|
|
@@ -2483,6 +2844,8 @@ export async function handleGeminiRequest(deps, params) {
|
|
|
2483
2844
|
attachments: params.attachments,
|
|
2484
2845
|
skipTrust: params.skipTrust,
|
|
2485
2846
|
yolo: params.yolo,
|
|
2847
|
+
project: params.project,
|
|
2848
|
+
newProject: params.newProject,
|
|
2486
2849
|
}, runtime);
|
|
2487
2850
|
if (!("args" in prep))
|
|
2488
2851
|
return prep;
|
|
@@ -2630,6 +2993,8 @@ export async function handleGeminiRequestAsync(deps, params) {
|
|
|
2630
2993
|
attachments: params.attachments,
|
|
2631
2994
|
skipTrust: params.skipTrust,
|
|
2632
2995
|
yolo: params.yolo,
|
|
2996
|
+
project: params.project,
|
|
2997
|
+
newProject: params.newProject,
|
|
2633
2998
|
}, runtime);
|
|
2634
2999
|
if (!("args" in prep))
|
|
2635
3000
|
return prep;
|
|
@@ -2759,6 +3124,9 @@ export async function handleGrokRequest(deps, params) {
|
|
|
2759
3124
|
restoreCode: params.restoreCode,
|
|
2760
3125
|
leaderSocket: params.leaderSocket,
|
|
2761
3126
|
nativeWorktree: params.nativeWorktree,
|
|
3127
|
+
worktreeRef: params.worktreeRef,
|
|
3128
|
+
forkSession: params.forkSession,
|
|
3129
|
+
jsonSchema: params.jsonSchema,
|
|
2762
3130
|
}, runtime);
|
|
2763
3131
|
if (!("args" in prep))
|
|
2764
3132
|
return prep;
|
|
@@ -2927,6 +3295,9 @@ export async function handleGrokRequestAsync(deps, params) {
|
|
|
2927
3295
|
restoreCode: params.restoreCode,
|
|
2928
3296
|
leaderSocket: params.leaderSocket,
|
|
2929
3297
|
nativeWorktree: params.nativeWorktree,
|
|
3298
|
+
worktreeRef: params.worktreeRef,
|
|
3299
|
+
forkSession: params.forkSession,
|
|
3300
|
+
jsonSchema: params.jsonSchema,
|
|
2930
3301
|
}, runtime);
|
|
2931
3302
|
if (!("args" in prep))
|
|
2932
3303
|
return prep;
|
|
@@ -3620,6 +3991,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3620
3991
|
registerValidationTools(server, {
|
|
3621
3992
|
asyncJobManager,
|
|
3622
3993
|
apiProviders: enabledApiProviders(providers),
|
|
3994
|
+
validationRunStore: persistence.backend === "sqlite" ? asyncJobManager.getValidationRunStore() : null,
|
|
3623
3995
|
});
|
|
3624
3996
|
registerWorkspaceTools(server, runtime);
|
|
3625
3997
|
const apiProviderTools = registerApiProviderTools(server, runtime, providers, asyncJobsEnabled);
|
|
@@ -3720,7 +4092,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3720
4092
|
sessionId: z
|
|
3721
4093
|
.string()
|
|
3722
4094
|
.optional()
|
|
3723
|
-
.describe("
|
|
4095
|
+
.describe("On a fresh request this id is emitted as Claude --session-id <uuid> (must be a valid UUID that does not already exist). Resume the latest cwd conversation with continueSession:true. gw-* ids are not valid Claude --session-id values."),
|
|
3724
4096
|
continueSession: z
|
|
3725
4097
|
.boolean()
|
|
3726
4098
|
.default(false)
|
|
@@ -3754,11 +4126,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
3754
4126
|
systemPrompt: z
|
|
3755
4127
|
.string()
|
|
3756
4128
|
.optional()
|
|
3757
|
-
.describe("Claude --system-prompt: replace the system prompt entirely."),
|
|
4129
|
+
.describe("Claude --system-prompt: replace the system prompt entirely. Mutually exclusive with appendSystemPrompt."),
|
|
3758
4130
|
appendSystemPrompt: z
|
|
3759
4131
|
.string()
|
|
3760
4132
|
.optional()
|
|
3761
|
-
.describe("Claude --append-system-prompt: append to the existing system prompt."),
|
|
4133
|
+
.describe("Claude --append-system-prompt: append to the existing system prompt. Mutually exclusive with systemPrompt."),
|
|
3762
4134
|
maxBudgetUsd: z
|
|
3763
4135
|
.number()
|
|
3764
4136
|
.positive()
|
|
@@ -3786,7 +4158,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3786
4158
|
jsonSchema: z
|
|
3787
4159
|
.union([z.string(), z.record(z.string(), z.unknown())])
|
|
3788
4160
|
.optional()
|
|
3789
|
-
.describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'."),
|
|
4161
|
+
.describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'. Set outputFormat:json so the gateway treats the reply as structured output (skips response optimization and warning injection); the default output format is not json, so pass it explicitly."),
|
|
3790
4162
|
addDir: z
|
|
3791
4163
|
.array(z.string())
|
|
3792
4164
|
.optional()
|
|
@@ -3814,11 +4186,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
3814
4186
|
approvalStrategy: z
|
|
3815
4187
|
.enum(["legacy", "mcp_managed"])
|
|
3816
4188
|
.default("legacy")
|
|
3817
|
-
.describe("Approval strategy"),
|
|
4189
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
3818
4190
|
approvalPolicy: z
|
|
3819
4191
|
.enum(["strict", "balanced", "permissive"])
|
|
3820
4192
|
.optional()
|
|
3821
|
-
.describe("Approval policy
|
|
4193
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
3822
4194
|
mcpServers: z.array(mcpServerEnum()).default([]).describe("MCP servers exposed to Claude"),
|
|
3823
4195
|
strictMcpConfig: z
|
|
3824
4196
|
.boolean()
|
|
@@ -3833,7 +4205,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3833
4205
|
.min(30_000)
|
|
3834
4206
|
.max(3_600_000)
|
|
3835
4207
|
.optional()
|
|
3836
|
-
.describe("Idle timeout in ms (min 30s, max 1h, omit=CLI default)"),
|
|
4208
|
+
.describe("Idle timeout in ms (min 30s, max 1h, omit=CLI default). Idle enforcement applies only when outputFormat is stream-json; it is ignored for text/json."),
|
|
3837
4209
|
forceRefresh: z
|
|
3838
4210
|
.boolean()
|
|
3839
4211
|
.default(false)
|
|
@@ -4060,7 +4432,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4060
4432
|
.optional()
|
|
4061
4433
|
.describe("Prompt text for Codex (mutually exclusive with promptParts)"),
|
|
4062
4434
|
promptParts: PromptPartsSchema.optional().describe("Cache-aware structured prompt: { system?, tools?, context?, task }. Mutually exclusive with prompt. Stable parts hash into cache_state for prefix-discipline tracking."),
|
|
4063
|
-
model: z.string().optional().describe("Model name or alias (e.g. gpt-5.4, latest)"),
|
|
4435
|
+
model: z.string().optional().describe("Model name or alias (e.g. gpt-5.5, gpt-5.4, latest)"),
|
|
4064
4436
|
fullAuto: z
|
|
4065
4437
|
.boolean()
|
|
4066
4438
|
.default(false)
|
|
@@ -4068,7 +4440,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4068
4440
|
sandboxMode: z
|
|
4069
4441
|
.enum(CODEX_SANDBOX_MODES)
|
|
4070
4442
|
.optional()
|
|
4071
|
-
.describe("Codex --sandbox
|
|
4443
|
+
.describe("Codex --sandbox. Omit = Codex exec built-in default (read-only; cannot write files). Pass workspace-write to let Codex edit files in the working dir, or danger-full-access for unrestricted access."),
|
|
4072
4444
|
askForApproval: z
|
|
4073
4445
|
.enum(CODEX_ASK_FOR_APPROVAL_MODES)
|
|
4074
4446
|
.optional()
|
|
@@ -4084,11 +4456,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4084
4456
|
approvalStrategy: z
|
|
4085
4457
|
.enum(["legacy", "mcp_managed"])
|
|
4086
4458
|
.default("legacy")
|
|
4087
|
-
.describe("Approval strategy"),
|
|
4459
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
4088
4460
|
approvalPolicy: z
|
|
4089
4461
|
.enum(["strict", "balanced", "permissive"])
|
|
4090
4462
|
.optional()
|
|
4091
|
-
.describe("Approval policy
|
|
4463
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
4092
4464
|
mcpServers: z
|
|
4093
4465
|
.array(mcpServerEnum())
|
|
4094
4466
|
.default([])
|
|
@@ -4096,11 +4468,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4096
4468
|
sessionId: z
|
|
4097
4469
|
.string()
|
|
4098
4470
|
.optional()
|
|
4099
|
-
.describe("Codex session UUID to resume via `codex exec resume <ID>`. Must be a real Codex session ID (from `~/.codex/sessions/` or the `codex resume` picker). Gateway-generated `gw-*` IDs are rejected."),
|
|
4471
|
+
.describe("Codex session UUID to resume via `codex exec resume <ID>`. Must be a real Codex session ID (from `~/.codex/sessions/` or the `codex resume` picker). Gateway-generated `gw-*` IDs are rejected. For a brand-new session no resumable sessionId is returned; continue with resumeLatest:true or a real Codex UUID."),
|
|
4100
4472
|
resumeLatest: z
|
|
4101
4473
|
.boolean()
|
|
4102
4474
|
.default(false)
|
|
4103
|
-
.describe("Resume the most recent Codex session
|
|
4475
|
+
.describe("Resume the most recent Codex session via `codex exec resume --last`. Ignored if sessionId is set. A brand-new session returns no resumable sessionId; continue with resumeLatest:true or a real Codex UUID."),
|
|
4104
4476
|
createNewSession: z.boolean().default(false).describe("Force a fresh session (no resume)"),
|
|
4105
4477
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
4106
4478
|
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
@@ -4255,7 +4627,12 @@ export function createGatewayServer(deps = {}) {
|
|
|
4255
4627
|
errorMessage: stderr || `Exit code ${code}`,
|
|
4256
4628
|
status: "failed",
|
|
4257
4629
|
}, runtime);
|
|
4258
|
-
|
|
4630
|
+
const codexErrorDetail = stderr && stderr.trim().length > 0 ? stderr : codexDisplayText(stdout);
|
|
4631
|
+
const codexResumeHint = sessionId &&
|
|
4632
|
+
/not found|no such|unknown session|does not exist|invalid session/i.test(codexErrorDetail)
|
|
4633
|
+
? `\n\nSession ${sessionId} could not be resumed. Codex session IDs are UUIDs under ~/.codex/sessions/; verify the id, omit sessionId, or set createNewSession:true.`
|
|
4634
|
+
: "";
|
|
4635
|
+
return createErrorResponse("codex", code, `${codexErrorDetail}${codexResumeHint}`, corrId);
|
|
4259
4636
|
}
|
|
4260
4637
|
wasSuccessful = true;
|
|
4261
4638
|
let effectiveSessionId = sessionId;
|
|
@@ -4339,7 +4716,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4339
4716
|
sandboxMode: z
|
|
4340
4717
|
.enum(CODEX_SANDBOX_MODES)
|
|
4341
4718
|
.optional()
|
|
4342
|
-
.describe("Codex --sandbox
|
|
4719
|
+
.describe("Codex --sandbox. Omit = Codex exec built-in default (read-only; cannot write files). Pass workspace-write to let Codex edit files in the working dir, or danger-full-access for unrestricted access."),
|
|
4343
4720
|
askForApproval: z
|
|
4344
4721
|
.enum(CODEX_ASK_FOR_APPROVAL_MODES)
|
|
4345
4722
|
.optional()
|
|
@@ -4412,7 +4789,12 @@ export function createGatewayServer(deps = {}) {
|
|
|
4412
4789
|
const { stdout, stderr, code } = result;
|
|
4413
4790
|
durationMs = Math.max(0, Date.now() - startTime);
|
|
4414
4791
|
if (code !== 0) {
|
|
4415
|
-
|
|
4792
|
+
const codexErrorDetail = stderr && stderr.trim().length > 0 ? stderr : codexDisplayText(stdout);
|
|
4793
|
+
const codexResumeHint = sessionId &&
|
|
4794
|
+
/not found|no such|unknown session|does not exist|invalid session/i.test(codexErrorDetail)
|
|
4795
|
+
? `\n\nSession ${sessionId} could not be resumed. Codex session IDs are UUIDs under ~/.codex/sessions/; verify the id, omit sessionId, or set createNewSession:true.`
|
|
4796
|
+
: "";
|
|
4797
|
+
return createErrorResponse("codex", code, `${codexErrorDetail}${codexResumeHint}`, corrId);
|
|
4416
4798
|
}
|
|
4417
4799
|
wasSuccessful = true;
|
|
4418
4800
|
return {
|
|
@@ -4442,21 +4824,24 @@ export function createGatewayServer(deps = {}) {
|
|
|
4442
4824
|
sessionId: z
|
|
4443
4825
|
.string()
|
|
4444
4826
|
.optional()
|
|
4445
|
-
.describe("Antigravity conversation ID to resume (emits --conversation <id>)"),
|
|
4446
|
-
resumeLatest: z
|
|
4827
|
+
.describe("Antigravity conversation ID to resume (emits --conversation <id>). agy owns conversation ids; a fresh request returns no resumable sessionId, continue via resumeLatest:true."),
|
|
4828
|
+
resumeLatest: z
|
|
4829
|
+
.boolean()
|
|
4830
|
+
.default(false)
|
|
4831
|
+
.describe("Continue the most recent conversation. agy owns conversation ids; a fresh request returns no resumable sessionId, continue via resumeLatest:true."),
|
|
4447
4832
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
4448
4833
|
approvalMode: z
|
|
4449
4834
|
.enum(GEMINI_APPROVAL_MODES)
|
|
4450
4835
|
.optional()
|
|
4451
|
-
.describe("Approval
|
|
4836
|
+
.describe("Approval mode. Only default (prompted) and yolo (auto-approve all, emits --dangerously-skip-permissions) work with the Antigravity agy headless path; auto_edit and plan are rejected at request time (agy has no headless accept-edits/plan mode)."),
|
|
4452
4837
|
approvalStrategy: z
|
|
4453
4838
|
.enum(["legacy", "mcp_managed"])
|
|
4454
4839
|
.default("legacy")
|
|
4455
|
-
.describe("Approval strategy"),
|
|
4840
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
4456
4841
|
approvalPolicy: z
|
|
4457
4842
|
.enum(["strict", "balanced", "permissive"])
|
|
4458
4843
|
.optional()
|
|
4459
|
-
.describe("Approval policy
|
|
4844
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
4460
4845
|
mcpServers: z
|
|
4461
4846
|
.array(mcpServerEnum())
|
|
4462
4847
|
.default([])
|
|
@@ -4486,7 +4871,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4486
4871
|
outputFormat: z
|
|
4487
4872
|
.enum(["text", "json", "stream-json"])
|
|
4488
4873
|
.default("text")
|
|
4489
|
-
.describe("
|
|
4874
|
+
.describe("Output format. The Antigravity agy headless path emits text only; json and stream-json are rejected at request time. Per-request token usage and cost are therefore not available for gemini."),
|
|
4490
4875
|
sandbox: GEMINI_HIGH_IMPACT_PARAMS_SCHEMA.shape.sandbox.describe("Run Antigravity in sandbox mode (--sandbox)"),
|
|
4491
4876
|
policyFiles: GEMINI_HIGH_IMPACT_PARAMS_SCHEMA.shape.policyFiles.describe("Unsupported for Antigravity CLI; non-empty values are rejected."),
|
|
4492
4877
|
adminPolicyFiles: GEMINI_HIGH_IMPACT_PARAMS_SCHEMA.shape.adminPolicyFiles.describe("Unsupported for Antigravity CLI; non-empty values are rejected."),
|
|
@@ -4499,6 +4884,15 @@ export function createGatewayServer(deps = {}) {
|
|
|
4499
4884
|
.boolean()
|
|
4500
4885
|
.optional()
|
|
4501
4886
|
.describe("Emit `--dangerously-skip-permissions` to auto-approve all actions. Routed through the same approval gate. Under mcp_managed the gate still decides."),
|
|
4887
|
+
project: z
|
|
4888
|
+
.string()
|
|
4889
|
+
.min(1)
|
|
4890
|
+
.optional()
|
|
4891
|
+
.describe("Antigravity 1.0.13 --project <ID>: select the project for this session. Mutually exclusive with newProject."),
|
|
4892
|
+
newProject: z
|
|
4893
|
+
.boolean()
|
|
4894
|
+
.optional()
|
|
4895
|
+
.describe("Antigravity 1.0.13 --new-project: create a new project for this session. Mutually exclusive with project."),
|
|
4502
4896
|
workspace: WORKSPACE_ALIAS_SCHEMA.optional(),
|
|
4503
4897
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
4504
4898
|
}, {
|
|
@@ -4507,7 +4901,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4507
4901
|
destructiveHint: true,
|
|
4508
4902
|
idempotentHint: false,
|
|
4509
4903
|
openWorldHint: true,
|
|
4510
|
-
}, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, workspace, worktree, }) => {
|
|
4904
|
+
}, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, project, newProject, workspace, worktree, }) => {
|
|
4511
4905
|
return handleGeminiRequest({ sessionManager, logger, runtime }, {
|
|
4512
4906
|
prompt,
|
|
4513
4907
|
promptParts,
|
|
@@ -4533,6 +4927,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
4533
4927
|
attachments,
|
|
4534
4928
|
skipTrust,
|
|
4535
4929
|
yolo,
|
|
4930
|
+
project,
|
|
4931
|
+
newProject,
|
|
4536
4932
|
workspace,
|
|
4537
4933
|
worktree,
|
|
4538
4934
|
});
|
|
@@ -4554,11 +4950,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4554
4950
|
sessionId: z
|
|
4555
4951
|
.string()
|
|
4556
4952
|
.optional()
|
|
4557
|
-
.describe("Provider-native session ID to resume (emits --resume <id>; use resumeLatest for --continue)"),
|
|
4953
|
+
.describe("Provider-native session ID to resume (emits --resume <id>; use resumeLatest for --continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
4558
4954
|
resumeLatest: z
|
|
4559
4955
|
.boolean()
|
|
4560
4956
|
.default(false)
|
|
4561
|
-
.describe("Resume most recent Grok session in cwd (--continue)"),
|
|
4957
|
+
.describe("Resume most recent Grok session in cwd (--continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
4562
4958
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
4563
4959
|
alwaysApprove: z
|
|
4564
4960
|
.boolean()
|
|
@@ -4567,15 +4963,15 @@ export function createGatewayServer(deps = {}) {
|
|
|
4567
4963
|
permissionMode: z
|
|
4568
4964
|
.enum(["default", "acceptEdits", "auto", "dontAsk", "bypassPermissions", "plan"])
|
|
4569
4965
|
.optional()
|
|
4570
|
-
.describe("Grok permission mode"),
|
|
4966
|
+
.describe("Grok permission mode: default|acceptEdits|auto|dontAsk|bypassPermissions|plan."),
|
|
4571
4967
|
approvalStrategy: z
|
|
4572
4968
|
.enum(["legacy", "mcp_managed"])
|
|
4573
4969
|
.default("legacy")
|
|
4574
|
-
.describe("Approval strategy"),
|
|
4970
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
4575
4971
|
approvalPolicy: z
|
|
4576
4972
|
.enum(["strict", "balanced", "permissive"])
|
|
4577
4973
|
.optional()
|
|
4578
|
-
.describe("Approval policy
|
|
4974
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
4579
4975
|
mcpServers: z
|
|
4580
4976
|
.array(mcpServerEnum())
|
|
4581
4977
|
.default([])
|
|
@@ -4589,7 +4985,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4589
4985
|
.min(30_000)
|
|
4590
4986
|
.max(3_600_000)
|
|
4591
4987
|
.optional()
|
|
4592
|
-
.describe("Idle timeout in ms (min 30s, max 1h
|
|
4988
|
+
.describe("Idle timeout in ms (min 30s, max 1h). Omit = gateway default of 600000ms (10 min) with no output before the process is killed."),
|
|
4593
4989
|
forceRefresh: z
|
|
4594
4990
|
.boolean()
|
|
4595
4991
|
.default(false)
|
|
@@ -4606,6 +5002,19 @@ export function createGatewayServer(deps = {}) {
|
|
|
4606
5002
|
.union([z.boolean(), z.string().min(1)])
|
|
4607
5003
|
.optional()
|
|
4608
5004
|
.describe("Grok -w/--worktree: native CLI worktree flag (`true` → bare `--worktree`, string → named). NOT gateway slice λ `worktree`."),
|
|
5005
|
+
worktreeRef: z
|
|
5006
|
+
.string()
|
|
5007
|
+
.min(1)
|
|
5008
|
+
.optional()
|
|
5009
|
+
.describe("Grok 0.2.73 --worktree-ref <REF>: git ref (branch/tag/commit) to base the native worktree on. Requires nativeWorktree."),
|
|
5010
|
+
forkSession: z
|
|
5011
|
+
.boolean()
|
|
5012
|
+
.optional()
|
|
5013
|
+
.describe("Grok 0.2.73 --fork-session: when resuming (--resume/--continue), fork into a new session ID instead of reusing the original."),
|
|
5014
|
+
jsonSchema: z
|
|
5015
|
+
.union([z.string().min(1), z.record(z.string(), z.unknown())])
|
|
5016
|
+
.optional()
|
|
5017
|
+
.describe("Grok 0.2.73 --json-schema: constrain output to a JSON Schema (string literal or object; implies json output). Set outputFormat:json so the gateway treats the reply as structured output (skips response optimization and warning injection); the default output format is not json, so pass it explicitly."),
|
|
4609
5018
|
workspace: WORKSPACE_ALIAS_SCHEMA.optional(),
|
|
4610
5019
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
4611
5020
|
}, {
|
|
@@ -4614,7 +5023,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4614
5023
|
destructiveHint: true,
|
|
4615
5024
|
idempotentHint: false,
|
|
4616
5025
|
openWorldHint: true,
|
|
4617
|
-
}, async ({ prompt, promptParts, model, transport, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, leaderSocket, nativeWorktree, workspace, worktree, }) => {
|
|
5026
|
+
}, async ({ prompt, promptParts, model, transport, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, leaderSocket, nativeWorktree, worktreeRef, forkSession, jsonSchema, workspace, worktree, }) => {
|
|
4618
5027
|
return handleGrokRequest({ sessionManager, logger, runtime }, {
|
|
4619
5028
|
prompt,
|
|
4620
5029
|
promptParts,
|
|
@@ -4666,6 +5075,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
4666
5075
|
restoreCode,
|
|
4667
5076
|
leaderSocket,
|
|
4668
5077
|
nativeWorktree,
|
|
5078
|
+
worktreeRef,
|
|
5079
|
+
forkSession,
|
|
5080
|
+
jsonSchema,
|
|
4669
5081
|
workspace,
|
|
4670
5082
|
worktree,
|
|
4671
5083
|
});
|
|
@@ -4676,7 +5088,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4676
5088
|
.min(1, "Prompt cannot be empty")
|
|
4677
5089
|
.max(100000, "Prompt too long (max 100k chars)")
|
|
4678
5090
|
.optional()
|
|
4679
|
-
.describe("Prompt text for Devin CLI"),
|
|
5091
|
+
.describe("Prompt text for Devin CLI. Required in practice; promptFile is additive (loads an initial prompt from a file)."),
|
|
4680
5092
|
model: z.string().optional().describe("Model name or alias (e.g. opus, latest)"),
|
|
4681
5093
|
transport: z
|
|
4682
5094
|
.enum(["cli", "acp"])
|
|
@@ -4685,7 +5097,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4685
5097
|
permissionMode: z
|
|
4686
5098
|
.enum(["auto", "smart", "dangerous"])
|
|
4687
5099
|
.optional()
|
|
4688
|
-
.describe("Devin CLI permission mode (--permission-mode). auto auto-approves read-only tools; smart additionally auto-runs actions a fast model judges safe; dangerous auto-approves all."),
|
|
5100
|
+
.describe("Devin CLI permission mode (--permission-mode). auto auto-approves read-only tools; smart additionally auto-runs actions a fast model judges safe; dangerous auto-approves all. When omitted, Devin uses its own headless default; pass auto or smart for unattended runs."),
|
|
4689
5101
|
promptFile: z
|
|
4690
5102
|
.string()
|
|
4691
5103
|
.optional()
|
|
@@ -4693,11 +5105,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4693
5105
|
sessionId: z
|
|
4694
5106
|
.string()
|
|
4695
5107
|
.optional()
|
|
4696
|
-
.describe("Devin session ID to resume (emits --resume <id>; use resumeLatest for --continue)"),
|
|
5108
|
+
.describe("Devin session ID to resume (emits --resume <id>; use resumeLatest for --continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
4697
5109
|
resumeLatest: z
|
|
4698
5110
|
.boolean()
|
|
4699
5111
|
.default(false)
|
|
4700
|
-
.describe("Resume the most recent Devin session in cwd (--continue)"),
|
|
5112
|
+
.describe("Resume the most recent Devin session in cwd (--continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
4701
5113
|
createNewSession: z.boolean().default(false).describe("Force a new session"),
|
|
4702
5114
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
4703
5115
|
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
@@ -4736,7 +5148,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4736
5148
|
forceRefresh,
|
|
4737
5149
|
});
|
|
4738
5150
|
});
|
|
4739
|
-
server.tool("mistral_request", "Run a Mistral Vibe CLI request synchronously (when async jobs are enabled, auto-defers to a pollable job past the sync deadline; otherwise runs to completion). Requires exactly one of prompt or promptParts.", {
|
|
5151
|
+
server.tool("mistral_request", "Run a Mistral Vibe CLI request synchronously (when async jobs are enabled, auto-defers to a pollable job past the sync deadline; otherwise runs to completion). Requires exactly one of prompt or promptParts. Defaults to --agent auto-approve (unattended tool execution); pass permissionMode plan or accept-edits for safer runs.", {
|
|
4740
5152
|
prompt: z
|
|
4741
5153
|
.string()
|
|
4742
5154
|
.min(1, "Prompt cannot be empty")
|
|
@@ -4759,11 +5171,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4759
5171
|
sessionId: z
|
|
4760
5172
|
.string()
|
|
4761
5173
|
.optional()
|
|
4762
|
-
.describe("Session ID (user-provided CLI handle for --resume). Current Vibe defaults session logging on; doctor flags explicit [session_logging] enabled = false."),
|
|
5174
|
+
.describe("Session ID (user-provided CLI handle for --resume). Current Vibe defaults session logging on; doctor flags explicit [session_logging] enabled = false. Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
4763
5175
|
resumeLatest: z
|
|
4764
5176
|
.boolean()
|
|
4765
5177
|
.default(false)
|
|
4766
|
-
.describe("Resume most recent Vibe session in cwd (--continue)"),
|
|
5178
|
+
.describe("Resume most recent Vibe session in cwd (--continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
4767
5179
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
4768
5180
|
permissionMode: z
|
|
4769
5181
|
.string()
|
|
@@ -4772,11 +5184,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4772
5184
|
approvalStrategy: z
|
|
4773
5185
|
.enum(["legacy", "mcp_managed"])
|
|
4774
5186
|
.default("legacy")
|
|
4775
|
-
.describe("Approval strategy"),
|
|
5187
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
4776
5188
|
approvalPolicy: z
|
|
4777
5189
|
.enum(["strict", "balanced", "permissive"])
|
|
4778
5190
|
.optional()
|
|
4779
|
-
.describe("Approval policy
|
|
5191
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
4780
5192
|
mcpServers: z
|
|
4781
5193
|
.array(mcpServerEnum())
|
|
4782
5194
|
.default([])
|
|
@@ -4878,7 +5290,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4878
5290
|
sessionId: z
|
|
4879
5291
|
.string()
|
|
4880
5292
|
.optional()
|
|
4881
|
-
.describe("
|
|
5293
|
+
.describe("On a fresh request this id is emitted as Claude --session-id <uuid> (must be a valid UUID that does not already exist). Resume the latest cwd conversation with continueSession:true. gw-* ids are not valid Claude --session-id values."),
|
|
4882
5294
|
continueSession: z
|
|
4883
5295
|
.boolean()
|
|
4884
5296
|
.default(false)
|
|
@@ -4912,11 +5324,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4912
5324
|
systemPrompt: z
|
|
4913
5325
|
.string()
|
|
4914
5326
|
.optional()
|
|
4915
|
-
.describe("Claude --system-prompt: replace the system prompt entirely."),
|
|
5327
|
+
.describe("Claude --system-prompt: replace the system prompt entirely. Mutually exclusive with appendSystemPrompt."),
|
|
4916
5328
|
appendSystemPrompt: z
|
|
4917
5329
|
.string()
|
|
4918
5330
|
.optional()
|
|
4919
|
-
.describe("Claude --append-system-prompt: append to the existing system prompt."),
|
|
5331
|
+
.describe("Claude --append-system-prompt: append to the existing system prompt. Mutually exclusive with systemPrompt."),
|
|
4920
5332
|
maxBudgetUsd: z
|
|
4921
5333
|
.number()
|
|
4922
5334
|
.positive()
|
|
@@ -4944,7 +5356,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4944
5356
|
jsonSchema: z
|
|
4945
5357
|
.union([z.string(), z.record(z.string(), z.unknown())])
|
|
4946
5358
|
.optional()
|
|
4947
|
-
.describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'."),
|
|
5359
|
+
.describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'. Set outputFormat:json so the gateway treats the reply as structured output (skips response optimization and warning injection); the default output format is not json, so pass it explicitly."),
|
|
4948
5360
|
addDir: z
|
|
4949
5361
|
.array(z.string())
|
|
4950
5362
|
.optional()
|
|
@@ -4972,11 +5384,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
4972
5384
|
approvalStrategy: z
|
|
4973
5385
|
.enum(["legacy", "mcp_managed"])
|
|
4974
5386
|
.default("legacy")
|
|
4975
|
-
.describe("Approval strategy"),
|
|
5387
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
4976
5388
|
approvalPolicy: z
|
|
4977
5389
|
.enum(["strict", "balanced", "permissive"])
|
|
4978
5390
|
.optional()
|
|
4979
|
-
.describe("Approval policy
|
|
5391
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
4980
5392
|
mcpServers: z.array(mcpServerEnum()).default([]).describe("MCP servers exposed to Claude"),
|
|
4981
5393
|
strictMcpConfig: z
|
|
4982
5394
|
.boolean()
|
|
@@ -4990,7 +5402,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4990
5402
|
.min(30_000)
|
|
4991
5403
|
.max(3_600_000)
|
|
4992
5404
|
.optional()
|
|
4993
|
-
.describe("Idle timeout in ms (min 30s, max 1h, omit=CLI default)"),
|
|
5405
|
+
.describe("Idle timeout in ms (min 30s, max 1h, omit=CLI default). Idle enforcement applies only when outputFormat is stream-json; it is ignored for text/json."),
|
|
4994
5406
|
forceRefresh: z
|
|
4995
5407
|
.boolean()
|
|
4996
5408
|
.default(false)
|
|
@@ -5137,7 +5549,10 @@ export function createGatewayServer(deps = {}) {
|
|
|
5137
5549
|
.optional()
|
|
5138
5550
|
.describe("Prompt text for Codex (mutually exclusive with promptParts)"),
|
|
5139
5551
|
promptParts: PromptPartsSchema.optional().describe("Cache-aware structured prompt: { system?, tools?, context?, task }. Mutually exclusive with prompt. Stable parts hash into cache_state for prefix-discipline tracking."),
|
|
5140
|
-
model: z
|
|
5552
|
+
model: z
|
|
5553
|
+
.string()
|
|
5554
|
+
.optional()
|
|
5555
|
+
.describe("Model name or alias (e.g. gpt-5.5, gpt-5.4, latest)"),
|
|
5141
5556
|
fullAuto: z
|
|
5142
5557
|
.boolean()
|
|
5143
5558
|
.default(false)
|
|
@@ -5145,7 +5560,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5145
5560
|
sandboxMode: z
|
|
5146
5561
|
.enum(CODEX_SANDBOX_MODES)
|
|
5147
5562
|
.optional()
|
|
5148
|
-
.describe("Codex --sandbox
|
|
5563
|
+
.describe("Codex --sandbox. Omit = Codex exec built-in default (read-only; cannot write files). Pass workspace-write to let Codex edit files in the working dir, or danger-full-access for unrestricted access."),
|
|
5149
5564
|
askForApproval: z
|
|
5150
5565
|
.enum(CODEX_ASK_FOR_APPROVAL_MODES)
|
|
5151
5566
|
.optional()
|
|
@@ -5161,11 +5576,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5161
5576
|
approvalStrategy: z
|
|
5162
5577
|
.enum(["legacy", "mcp_managed"])
|
|
5163
5578
|
.default("legacy")
|
|
5164
|
-
.describe("Approval strategy"),
|
|
5579
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
5165
5580
|
approvalPolicy: z
|
|
5166
5581
|
.enum(["strict", "balanced", "permissive"])
|
|
5167
5582
|
.optional()
|
|
5168
|
-
.describe("Approval policy
|
|
5583
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
5169
5584
|
mcpServers: z
|
|
5170
5585
|
.array(mcpServerEnum())
|
|
5171
5586
|
.default([])
|
|
@@ -5173,11 +5588,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5173
5588
|
sessionId: z
|
|
5174
5589
|
.string()
|
|
5175
5590
|
.optional()
|
|
5176
|
-
.describe("Codex session UUID to resume via `codex exec resume <ID>`. Must be a real Codex session ID (from `~/.codex/sessions/` or the `codex resume` picker). Gateway-generated `gw-*` IDs are rejected."),
|
|
5591
|
+
.describe("Codex session UUID to resume via `codex exec resume <ID>`. Must be a real Codex session ID (from `~/.codex/sessions/` or the `codex resume` picker). Gateway-generated `gw-*` IDs are rejected. For a brand-new session no resumable sessionId is returned; continue with resumeLatest:true or a real Codex UUID."),
|
|
5177
5592
|
resumeLatest: z
|
|
5178
5593
|
.boolean()
|
|
5179
5594
|
.default(false)
|
|
5180
|
-
.describe("Resume the most recent Codex session
|
|
5595
|
+
.describe("Resume the most recent Codex session via `codex exec resume --last`. Ignored if sessionId is set. A brand-new session returns no resumable sessionId; continue with resumeLatest:true or a real Codex UUID."),
|
|
5181
5596
|
createNewSession: z.boolean().default(false).describe("Force a fresh session (no resume)"),
|
|
5182
5597
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
5183
5598
|
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
@@ -5277,21 +5692,24 @@ export function createGatewayServer(deps = {}) {
|
|
|
5277
5692
|
sessionId: z
|
|
5278
5693
|
.string()
|
|
5279
5694
|
.optional()
|
|
5280
|
-
.describe("Antigravity conversation ID to resume (emits --conversation <id>)"),
|
|
5281
|
-
resumeLatest: z
|
|
5695
|
+
.describe("Antigravity conversation ID to resume (emits --conversation <id>). agy owns conversation ids; a fresh request returns no resumable sessionId, continue via resumeLatest:true."),
|
|
5696
|
+
resumeLatest: z
|
|
5697
|
+
.boolean()
|
|
5698
|
+
.default(false)
|
|
5699
|
+
.describe("Continue the most recent conversation. agy owns conversation ids; a fresh request returns no resumable sessionId, continue via resumeLatest:true."),
|
|
5282
5700
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
5283
5701
|
approvalMode: z
|
|
5284
5702
|
.enum(GEMINI_APPROVAL_MODES)
|
|
5285
5703
|
.optional()
|
|
5286
|
-
.describe("Approval
|
|
5704
|
+
.describe("Approval mode. Only default (prompted) and yolo (auto-approve all, emits --dangerously-skip-permissions) work with the Antigravity agy headless path; auto_edit and plan are rejected at request time (agy has no headless accept-edits/plan mode)."),
|
|
5287
5705
|
approvalStrategy: z
|
|
5288
5706
|
.enum(["legacy", "mcp_managed"])
|
|
5289
5707
|
.default("legacy")
|
|
5290
|
-
.describe("Approval strategy"),
|
|
5708
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
5291
5709
|
approvalPolicy: z
|
|
5292
5710
|
.enum(["strict", "balanced", "permissive"])
|
|
5293
5711
|
.optional()
|
|
5294
|
-
.describe("Approval policy
|
|
5712
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
5295
5713
|
mcpServers: z
|
|
5296
5714
|
.array(mcpServerEnum())
|
|
5297
5715
|
.default([])
|
|
@@ -5320,7 +5738,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5320
5738
|
outputFormat: z
|
|
5321
5739
|
.enum(["text", "json", "stream-json"])
|
|
5322
5740
|
.default("text")
|
|
5323
|
-
.describe("
|
|
5741
|
+
.describe("Output format. The Antigravity agy headless path emits text only; json and stream-json are rejected at request time. Per-request token usage and cost are therefore not available for gemini."),
|
|
5324
5742
|
sandbox: GEMINI_HIGH_IMPACT_PARAMS_SCHEMA.shape.sandbox.describe("Run Antigravity in sandbox mode (--sandbox)"),
|
|
5325
5743
|
policyFiles: GEMINI_HIGH_IMPACT_PARAMS_SCHEMA.shape.policyFiles.describe("Unsupported for Antigravity CLI; non-empty values are rejected."),
|
|
5326
5744
|
adminPolicyFiles: GEMINI_HIGH_IMPACT_PARAMS_SCHEMA.shape.adminPolicyFiles.describe("Unsupported for Antigravity CLI; non-empty values are rejected."),
|
|
@@ -5333,6 +5751,15 @@ export function createGatewayServer(deps = {}) {
|
|
|
5333
5751
|
.boolean()
|
|
5334
5752
|
.optional()
|
|
5335
5753
|
.describe("Emit `--dangerously-skip-permissions` to auto-approve all actions. Routed through the same approval gate. Under mcp_managed the gate still decides."),
|
|
5754
|
+
project: z
|
|
5755
|
+
.string()
|
|
5756
|
+
.min(1)
|
|
5757
|
+
.optional()
|
|
5758
|
+
.describe("Antigravity 1.0.13 --project <ID>: select the project for this session. Mutually exclusive with newProject."),
|
|
5759
|
+
newProject: z
|
|
5760
|
+
.boolean()
|
|
5761
|
+
.optional()
|
|
5762
|
+
.describe("Antigravity 1.0.13 --new-project: create a new project for this session. Mutually exclusive with project."),
|
|
5336
5763
|
workspace: WORKSPACE_ALIAS_SCHEMA.optional(),
|
|
5337
5764
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5338
5765
|
}, {
|
|
@@ -5341,7 +5768,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5341
5768
|
destructiveHint: true,
|
|
5342
5769
|
idempotentHint: false,
|
|
5343
5770
|
openWorldHint: true,
|
|
5344
|
-
}, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, workspace, worktree, }) => {
|
|
5771
|
+
}, async ({ prompt, promptParts, model, sessionId, resumeLatest, createNewSession, approvalMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, includeDirs, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, outputFormat, sandbox, policyFiles, adminPolicyFiles, attachments, skipTrust, yolo, project, newProject, workspace, worktree, }) => {
|
|
5345
5772
|
return handleGeminiRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
|
|
5346
5773
|
prompt,
|
|
5347
5774
|
promptParts,
|
|
@@ -5366,6 +5793,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
5366
5793
|
attachments,
|
|
5367
5794
|
skipTrust,
|
|
5368
5795
|
yolo,
|
|
5796
|
+
project,
|
|
5797
|
+
newProject,
|
|
5369
5798
|
workspace,
|
|
5370
5799
|
worktree,
|
|
5371
5800
|
});
|
|
@@ -5386,11 +5815,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5386
5815
|
sessionId: z
|
|
5387
5816
|
.string()
|
|
5388
5817
|
.optional()
|
|
5389
|
-
.describe("Provider-native session ID to resume (emits --resume <id>; use resumeLatest for --continue)"),
|
|
5818
|
+
.describe("Provider-native session ID to resume (emits --resume <id>; use resumeLatest for --continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
5390
5819
|
resumeLatest: z
|
|
5391
5820
|
.boolean()
|
|
5392
5821
|
.default(false)
|
|
5393
|
-
.describe("Resume most recent Grok session in cwd (--continue)"),
|
|
5822
|
+
.describe("Resume most recent Grok session in cwd (--continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
5394
5823
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
5395
5824
|
alwaysApprove: z
|
|
5396
5825
|
.boolean()
|
|
@@ -5399,7 +5828,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5399
5828
|
permissionMode: z
|
|
5400
5829
|
.enum(["default", "acceptEdits", "auto", "dontAsk", "bypassPermissions", "plan"])
|
|
5401
5830
|
.optional()
|
|
5402
|
-
.describe("Grok permission mode"),
|
|
5831
|
+
.describe("Grok permission mode: default|acceptEdits|auto|dontAsk|bypassPermissions|plan."),
|
|
5403
5832
|
effort: z
|
|
5404
5833
|
.enum(["low", "medium", "high", "xhigh", "max"])
|
|
5405
5834
|
.optional()
|
|
@@ -5408,11 +5837,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5408
5837
|
approvalStrategy: z
|
|
5409
5838
|
.enum(["legacy", "mcp_managed"])
|
|
5410
5839
|
.default("legacy")
|
|
5411
|
-
.describe("Approval strategy"),
|
|
5840
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
5412
5841
|
approvalPolicy: z
|
|
5413
5842
|
.enum(["strict", "balanced", "permissive"])
|
|
5414
5843
|
.optional()
|
|
5415
|
-
.describe("Approval policy
|
|
5844
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
5416
5845
|
mcpServers: z
|
|
5417
5846
|
.array(mcpServerEnum())
|
|
5418
5847
|
.default([])
|
|
@@ -5433,7 +5862,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5433
5862
|
.min(30_000)
|
|
5434
5863
|
.max(3_600_000)
|
|
5435
5864
|
.optional()
|
|
5436
|
-
.describe("Idle timeout in ms (min 30s, max 1h
|
|
5865
|
+
.describe("Idle timeout in ms (min 30s, max 1h). Omit = gateway default of 600000ms (10 min) with no output before the process is killed."),
|
|
5437
5866
|
forceRefresh: z
|
|
5438
5867
|
.boolean()
|
|
5439
5868
|
.default(false)
|
|
@@ -5546,6 +5975,19 @@ export function createGatewayServer(deps = {}) {
|
|
|
5546
5975
|
.union([z.boolean(), z.string().min(1)])
|
|
5547
5976
|
.optional()
|
|
5548
5977
|
.describe("Grok -w/--worktree: native CLI worktree flag (`true` → bare `--worktree`, string → named). NOT gateway slice λ `worktree`."),
|
|
5978
|
+
worktreeRef: z
|
|
5979
|
+
.string()
|
|
5980
|
+
.min(1)
|
|
5981
|
+
.optional()
|
|
5982
|
+
.describe("Grok 0.2.73 --worktree-ref <REF>: git ref (branch/tag/commit) to base the native worktree on. Requires nativeWorktree."),
|
|
5983
|
+
forkSession: z
|
|
5984
|
+
.boolean()
|
|
5985
|
+
.optional()
|
|
5986
|
+
.describe("Grok 0.2.73 --fork-session: when resuming (--resume/--continue), fork into a new session ID instead of reusing the original."),
|
|
5987
|
+
jsonSchema: z
|
|
5988
|
+
.union([z.string().min(1), z.record(z.string(), z.unknown())])
|
|
5989
|
+
.optional()
|
|
5990
|
+
.describe("Grok 0.2.73 --json-schema: constrain output to a JSON Schema (string literal or object; implies json output). Set outputFormat:json so the gateway treats the reply as structured output (skips response optimization and warning injection); the default output format is not json, so pass it explicitly."),
|
|
5549
5991
|
workspace: WORKSPACE_ALIAS_SCHEMA.optional(),
|
|
5550
5992
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5551
5993
|
}, {
|
|
@@ -5554,7 +5996,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5554
5996
|
destructiveHint: true,
|
|
5555
5997
|
idempotentHint: false,
|
|
5556
5998
|
openWorldHint: true,
|
|
5557
|
-
}, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, leaderSocket, nativeWorktree, workspace, worktree, }) => {
|
|
5999
|
+
}, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, maxTurns, workingDir, sandbox, rules, systemPromptOverride, allow, deny, compactionMode, compactionDetail, agent, bestOfN, check, disableWebSearch, todoGate, verbatim, agents, promptFile, promptJson, single, experimentalMemory, noAltScreen, noMemory, noPlan, noSubagents, oauth, restoreCode, leaderSocket, nativeWorktree, worktreeRef, forkSession, jsonSchema, workspace, worktree, }) => {
|
|
5558
6000
|
return handleGrokRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
|
|
5559
6001
|
prompt,
|
|
5560
6002
|
promptParts,
|
|
@@ -5604,6 +6046,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
5604
6046
|
restoreCode,
|
|
5605
6047
|
leaderSocket,
|
|
5606
6048
|
nativeWorktree,
|
|
6049
|
+
worktreeRef,
|
|
6050
|
+
forkSession,
|
|
6051
|
+
jsonSchema,
|
|
5607
6052
|
workspace,
|
|
5608
6053
|
worktree,
|
|
5609
6054
|
});
|
|
@@ -5614,12 +6059,12 @@ export function createGatewayServer(deps = {}) {
|
|
|
5614
6059
|
.min(1, "Prompt cannot be empty")
|
|
5615
6060
|
.max(100000, "Prompt too long (max 100k chars)")
|
|
5616
6061
|
.optional()
|
|
5617
|
-
.describe("Prompt text for Devin CLI"),
|
|
6062
|
+
.describe("Prompt text for Devin CLI. Required in practice; promptFile is additive (loads an initial prompt from a file)."),
|
|
5618
6063
|
model: z.string().optional().describe("Model name or alias (e.g. opus, latest)"),
|
|
5619
6064
|
permissionMode: z
|
|
5620
6065
|
.enum(["auto", "smart", "dangerous"])
|
|
5621
6066
|
.optional()
|
|
5622
|
-
.describe("Devin CLI permission mode (--permission-mode). auto, smart, or dangerous."),
|
|
6067
|
+
.describe("Devin CLI permission mode (--permission-mode). auto, smart, or dangerous. When omitted, Devin uses its own headless default; pass auto or smart for unattended runs."),
|
|
5623
6068
|
promptFile: z
|
|
5624
6069
|
.string()
|
|
5625
6070
|
.optional()
|
|
@@ -5627,11 +6072,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5627
6072
|
sessionId: z
|
|
5628
6073
|
.string()
|
|
5629
6074
|
.optional()
|
|
5630
|
-
.describe("Devin session ID to resume (--resume <id>; use resumeLatest for --continue)"),
|
|
6075
|
+
.describe("Devin session ID to resume (--resume <id>; use resumeLatest for --continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
5631
6076
|
resumeLatest: z
|
|
5632
6077
|
.boolean()
|
|
5633
6078
|
.default(false)
|
|
5634
|
-
.describe("Resume the most recent Devin session in cwd (--continue)"),
|
|
6079
|
+
.describe("Resume the most recent Devin session in cwd (--continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
5635
6080
|
createNewSession: z.boolean().default(false).describe("Force a new session"),
|
|
5636
6081
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
5637
6082
|
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
@@ -5686,11 +6131,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5686
6131
|
sessionId: z
|
|
5687
6132
|
.string()
|
|
5688
6133
|
.optional()
|
|
5689
|
-
.describe("Session ID (user-provided CLI handle for --resume). Current Vibe defaults session logging on; doctor flags explicit [session_logging] enabled = false."),
|
|
6134
|
+
.describe("Session ID (user-provided CLI handle for --resume). Current Vibe defaults session logging on; doctor flags explicit [session_logging] enabled = false. Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
5690
6135
|
resumeLatest: z
|
|
5691
6136
|
.boolean()
|
|
5692
6137
|
.default(false)
|
|
5693
|
-
.describe("Resume most recent Vibe session in cwd (--continue)"),
|
|
6138
|
+
.describe("Resume most recent Vibe session in cwd (--continue). Note: the gw-* id minted for a brand-new session is not resumable via sessionId; continue with resumeLatest:true."),
|
|
5694
6139
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
5695
6140
|
permissionMode: z
|
|
5696
6141
|
.string()
|
|
@@ -5699,11 +6144,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5699
6144
|
approvalStrategy: z
|
|
5700
6145
|
.enum(["legacy", "mcp_managed"])
|
|
5701
6146
|
.default("legacy")
|
|
5702
|
-
.describe("Approval strategy"),
|
|
6147
|
+
.describe("Approval strategy: legacy (default) lets the provider CLI's own flags decide; mcp_managed routes the run through the gateway approval gate (required for approvalPolicy and approval_list). Under mcp_managed the caller's permissionMode/alwaysApprove may be overridden by the gate."),
|
|
5703
6148
|
approvalPolicy: z
|
|
5704
6149
|
.enum(["strict", "balanced", "permissive"])
|
|
5705
6150
|
.optional()
|
|
5706
|
-
.describe("Approval policy
|
|
6151
|
+
.describe("Approval policy when approvalStrategy is mcp_managed: strict|balanced|permissive (default balanced). Ignored under legacy strategy."),
|
|
5707
6152
|
mcpServers: z
|
|
5708
6153
|
.array(mcpServerEnum())
|
|
5709
6154
|
.default([])
|
|
@@ -6084,10 +6529,10 @@ export function createGatewayServer(deps = {}) {
|
|
|
6084
6529
|
],
|
|
6085
6530
|
};
|
|
6086
6531
|
});
|
|
6087
|
-
server.tool("list_models", "List models, aliases, and defaults for one provider CLI (claude|codex|gemini|grok|mistral).", {
|
|
6532
|
+
server.tool("list_models", "List models, aliases, and defaults for one provider CLI (claude|codex|gemini|grok|mistral|devin), or omit cli to list all providers.", {
|
|
6088
6533
|
cli: z
|
|
6089
|
-
.preprocess(value => (value === "" || value === null ? undefined : value),
|
|
6090
|
-
.describe("CLI filter (claude|codex|gemini|grok|mistral)"),
|
|
6534
|
+
.preprocess(value => (value === "" || value === null ? undefined : value), CLI_TYPE_ENUM.optional())
|
|
6535
|
+
.describe("CLI filter (claude|codex|gemini|grok|mistral|devin)"),
|
|
6091
6536
|
}, {
|
|
6092
6537
|
title: "Provider models",
|
|
6093
6538
|
readOnlyHint: true,
|
|
@@ -6099,10 +6544,10 @@ export function createGatewayServer(deps = {}) {
|
|
|
6099
6544
|
const result = cli ? { [cli]: cliInfo[cli] } : cliInfo;
|
|
6100
6545
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
6101
6546
|
});
|
|
6102
|
-
server.tool("provider_tool_capabilities", "Report provider tool/feature capabilities and discovered local skill/tool integrations for claude|codex|gemini|grok|grok_api
|
|
6547
|
+
server.tool("provider_tool_capabilities", "Report provider tool/feature capabilities and discovered local skill/tool integrations for claude|codex|gemini|grok|mistral|devin|grok_api.", {
|
|
6103
6548
|
cli: z
|
|
6104
|
-
.preprocess(value => (value === "" || value === null ? undefined : value), z.enum(["claude", "codex", "gemini", "grok", "
|
|
6105
|
-
.describe("Provider filter (claude|codex|gemini|grok|grok_api
|
|
6549
|
+
.preprocess(value => (value === "" || value === null ? undefined : value), z.enum(["claude", "codex", "gemini", "grok", "mistral", "devin", "grok_api"]).optional())
|
|
6550
|
+
.describe("Provider filter (claude|codex|gemini|grok|mistral|devin|grok_api)"),
|
|
6106
6551
|
includeSkills: z
|
|
6107
6552
|
.boolean()
|
|
6108
6553
|
.default(true)
|
|
@@ -6137,10 +6582,10 @@ export function createGatewayServer(deps = {}) {
|
|
|
6137
6582
|
});
|
|
6138
6583
|
return { content: [{ type: "text", text: JSON.stringify(capabilities, null, 2) }] };
|
|
6139
6584
|
});
|
|
6140
|
-
server.tool("cli_versions", "Report installed provider CLI versions, availability, and login status for all
|
|
6585
|
+
server.tool("cli_versions", "Report installed provider CLI versions, availability, and login status for all six providers (claude|codex|gemini|grok|mistral|devin) or one.", {
|
|
6141
6586
|
cli: z
|
|
6142
|
-
.preprocess(value => (value === "" || value === null ? undefined : value),
|
|
6143
|
-
.describe("CLI filter (claude|codex|gemini|grok|mistral)"),
|
|
6587
|
+
.preprocess(value => (value === "" || value === null ? undefined : value), CLI_TYPE_ENUM.optional())
|
|
6588
|
+
.describe("CLI filter (claude|codex|gemini|grok|mistral|devin)"),
|
|
6144
6589
|
}, {
|
|
6145
6590
|
title: "Provider CLI versions",
|
|
6146
6591
|
readOnlyHint: true,
|
|
@@ -6154,7 +6599,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6154
6599
|
server.tool("upstream_contracts", "Return the gateway's declared provider CLI contracts; with probeInstalled true, diff against installed --help surfaces to detect flag drift.", {
|
|
6155
6600
|
cli: z
|
|
6156
6601
|
.preprocess(value => (value === "" || value === null ? undefined : value), CLI_TYPE_ENUM.optional())
|
|
6157
|
-
.describe("CLI filter (claude|codex|gemini|grok|mistral)"),
|
|
6602
|
+
.describe("CLI filter (claude|codex|gemini|grok|mistral|devin)"),
|
|
6158
6603
|
probeInstalled: z
|
|
6159
6604
|
.boolean()
|
|
6160
6605
|
.default(false)
|
|
@@ -6172,7 +6617,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6172
6617
|
server.tool("provider_subcommands_list", "Return a compact, filterable read-only catalog of declared provider CLI subcommands without flags or raw help.", {
|
|
6173
6618
|
provider: z
|
|
6174
6619
|
.preprocess(value => (value === "" || value === null ? undefined : value), CLI_TYPE_ENUM.optional())
|
|
6175
|
-
.describe("Optional provider filter (claude|codex|gemini|grok|mistral)"),
|
|
6620
|
+
.describe("Optional provider filter (claude|codex|gemini|grok|mistral|devin)"),
|
|
6176
6621
|
tier: z
|
|
6177
6622
|
.enum(["catalog", "inspect", "execute_candidate", "diagnostic"])
|
|
6178
6623
|
.optional()
|
|
@@ -6222,7 +6667,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6222
6667
|
};
|
|
6223
6668
|
});
|
|
6224
6669
|
server.tool("provider_subcommand_contract", "Return the detailed read-only contract for exactly one declared provider CLI subcommand.", {
|
|
6225
|
-
provider: CLI_TYPE_ENUM.describe("Provider (claude|codex|gemini|grok|mistral)"),
|
|
6670
|
+
provider: CLI_TYPE_ENUM.describe("Provider (claude|codex|gemini|grok|mistral|devin)"),
|
|
6226
6671
|
commandPath: z.array(z.string().min(1)).min(1).describe("Command path segments"),
|
|
6227
6672
|
}, {
|
|
6228
6673
|
title: "Provider subcommand contract",
|
|
@@ -6246,7 +6691,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6246
6691
|
server.tool("provider_subcommand_drift", "Probe declared provider subcommand --help surfaces and return compact drift rows without raw help output.", {
|
|
6247
6692
|
provider: z
|
|
6248
6693
|
.preprocess(value => (value === "" || value === null ? undefined : value), CLI_TYPE_ENUM.optional())
|
|
6249
|
-
.describe("Optional provider filter (claude|codex|gemini|grok|mistral)"),
|
|
6694
|
+
.describe("Optional provider filter (claude|codex|gemini|grok|mistral|devin)"),
|
|
6250
6695
|
includeClean: z
|
|
6251
6696
|
.boolean()
|
|
6252
6697
|
.default(false)
|
|
@@ -6297,7 +6742,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6297
6742
|
};
|
|
6298
6743
|
});
|
|
6299
6744
|
server.tool("cli_upgrade", "Plan (dryRun, default true) or execute an upgrade for one provider CLI using its native update mechanism.", {
|
|
6300
|
-
cli:
|
|
6745
|
+
cli: CLI_TYPE_ENUM.describe("CLI to upgrade (claude|codex|gemini|grok|mistral|devin)"),
|
|
6301
6746
|
target: z
|
|
6302
6747
|
.string()
|
|
6303
6748
|
.min(1)
|
|
@@ -6351,8 +6796,12 @@ export function createGatewayServer(deps = {}) {
|
|
|
6351
6796
|
};
|
|
6352
6797
|
}
|
|
6353
6798
|
});
|
|
6354
|
-
|
|
6355
|
-
|
|
6799
|
+
const sessionProviderValues = sessionProviderValuesFor(providers);
|
|
6800
|
+
const sessionProviderEnum = sessionProviderValues.length > SESSION_PROVIDER_VALUES.length
|
|
6801
|
+
? z.enum(sessionProviderValues)
|
|
6802
|
+
: SESSION_PROVIDER_ENUM;
|
|
6803
|
+
server.tool("session_create", "Create a gateway session record for a provider. NOTE: this is gateway bookkeeping (a plain UUID), not a provider-native session; Codex resume needs a real Codex UUID.", {
|
|
6804
|
+
cli: sessionProviderEnum.describe("Provider type (claude|codex|gemini|grok|mistral|grok-api)"),
|
|
6356
6805
|
description: z.string().optional().describe("Session description"),
|
|
6357
6806
|
setAsActive: z.boolean().default(true).describe("Set as active session"),
|
|
6358
6807
|
}, {
|
|
@@ -6391,7 +6840,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
6391
6840
|
}
|
|
6392
6841
|
});
|
|
6393
6842
|
server.tool("session_list", "List gateway session records and the active session per provider, optionally filtered by provider.", {
|
|
6394
|
-
cli:
|
|
6843
|
+
cli: sessionProviderEnum
|
|
6844
|
+
.optional()
|
|
6845
|
+
.describe("Provider filter (claude|codex|gemini|grok|mistral|grok-api)"),
|
|
6395
6846
|
}, {
|
|
6396
6847
|
title: "List sessions",
|
|
6397
6848
|
readOnlyHint: true,
|
|
@@ -6402,7 +6853,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6402
6853
|
try {
|
|
6403
6854
|
const caller = resolveOwnerPrincipal(getRequestContext());
|
|
6404
6855
|
const sessions = (await sessionManager.listSessions(cli)).filter(s => principalCanAccess(s.ownerPrincipal, caller));
|
|
6405
|
-
const activeSessions = Object.fromEntries(await Promise.all(
|
|
6856
|
+
const activeSessions = Object.fromEntries(await Promise.all(sessionProviderValues.map(async (provider) => {
|
|
6406
6857
|
const active = await sessionManager.getActiveSession(provider);
|
|
6407
6858
|
return [
|
|
6408
6859
|
provider,
|
|
@@ -6424,7 +6875,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6424
6875
|
text: JSON.stringify({
|
|
6425
6876
|
total: sessionList.length,
|
|
6426
6877
|
sessions: sessionList,
|
|
6427
|
-
activeSessions: Object.fromEntries(
|
|
6878
|
+
activeSessions: Object.fromEntries(sessionProviderValues.map(provider => [
|
|
6428
6879
|
provider,
|
|
6429
6880
|
activeSessions[provider]?.id || null,
|
|
6430
6881
|
])),
|
|
@@ -6438,7 +6889,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6438
6889
|
}
|
|
6439
6890
|
});
|
|
6440
6891
|
server.tool("session_set_active", "Set or clear the active session for a provider; the active session is used when a request omits sessionId.", {
|
|
6441
|
-
cli:
|
|
6892
|
+
cli: sessionProviderEnum.describe("Provider type (claude|codex|gemini|grok|mistral|grok-api)"),
|
|
6442
6893
|
sessionId: z.string().nullable().describe("Session ID (null to clear)"),
|
|
6443
6894
|
}, {
|
|
6444
6895
|
title: "Set active session",
|
|
@@ -6618,7 +7069,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
6618
7069
|
}
|
|
6619
7070
|
});
|
|
6620
7071
|
server.tool("session_clear_all", "Delete all gateway session records, optionally scoped to one provider.", {
|
|
6621
|
-
cli:
|
|
7072
|
+
cli: sessionProviderEnum
|
|
7073
|
+
.optional()
|
|
7074
|
+
.describe("Provider filter (claude|codex|gemini|grok|mistral|grok-api)"),
|
|
6622
7075
|
}, {
|
|
6623
7076
|
title: "Clear sessions",
|
|
6624
7077
|
readOnlyHint: false,
|