llm-cli-gateway 2.10.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/README.md +7 -5
- package/dist/acp/event-normalizer.d.ts +42 -0
- package/dist/acp/event-normalizer.js +71 -0
- package/dist/acp/flight-redaction.d.ts +25 -0
- package/dist/acp/flight-redaction.js +40 -0
- package/dist/acp/host-services.d.ts +16 -0
- package/dist/acp/host-services.js +29 -0
- package/dist/acp/permission-bridge.d.ts +15 -0
- package/dist/acp/permission-bridge.js +90 -0
- package/dist/acp/process-manager.js +7 -1
- package/dist/acp/provider-registry.d.ts +1 -1
- package/dist/acp/provider-registry.js +13 -0
- package/dist/acp/runtime.d.ts +35 -0
- package/dist/acp/runtime.js +125 -0
- package/dist/acp/session-map.d.ts +42 -0
- package/dist/acp/session-map.js +67 -0
- package/dist/acp/smoke-harness.d.ts +28 -0
- package/dist/acp/smoke-harness.js +90 -0
- package/dist/api-http.d.ts +18 -0
- package/dist/api-http.js +122 -0
- package/dist/api-provider.d.ts +83 -0
- package/dist/api-provider.js +258 -0
- package/dist/api-request.d.ts +30 -0
- package/dist/api-request.js +51 -0
- package/dist/approval-manager.d.ts +1 -1
- package/dist/approval-manager.js +6 -7
- package/dist/async-job-manager.d.ts +19 -4
- package/dist/async-job-manager.js +211 -35
- package/dist/claude-mcp-config.d.ts +2 -2
- package/dist/claude-mcp-config.js +42 -52
- package/dist/cli-updater.js +16 -1
- package/dist/config.d.ts +20 -0
- package/dist/config.js +93 -35
- package/dist/doctor.d.ts +1 -1
- package/dist/flight-recorder.d.ts +1 -0
- package/dist/flight-recorder.js +11 -0
- package/dist/index.d.ts +56 -5
- package/dist/index.js +639 -38
- package/dist/job-store.d.ts +15 -0
- package/dist/job-store.js +39 -5
- package/dist/mcp-registry.d.ts +17 -0
- package/dist/mcp-registry.js +5 -0
- package/dist/metrics.js +7 -2
- package/dist/model-registry.js +11 -0
- package/dist/prompt-parts.d.ts +6 -6
- package/dist/provider-login-guidance.js +21 -0
- package/dist/provider-status.js +4 -1
- package/dist/provider-tool-capabilities.d.ts +4 -3
- package/dist/provider-tool-capabilities.js +93 -6
- package/dist/request-helpers.d.ts +6 -6
- package/dist/request-helpers.js +1 -4
- package/dist/session-manager-pg.js +2 -9
- package/dist/session-manager.d.ts +9 -4
- package/dist/session-manager.js +13 -4
- package/dist/upstream-contracts.js +112 -2
- package/dist/validation-normalizer.d.ts +2 -2
- package/dist/validation-orchestrator.d.ts +2 -0
- package/dist/validation-orchestrator.js +28 -7
- package/dist/validation-tools.d.ts +61 -0
- package/dist/validation-tools.js +36 -21
- package/migrations/005_provider_type_open_api_names.sql +28 -0
- package/npm-shrinkwrap.json +4 -3
- package/package.json +12 -9
package/dist/index.js
CHANGED
|
@@ -18,7 +18,11 @@ import { createWorktree, createWorktreeSessionCleanupHook, } from "./worktree-ma
|
|
|
18
18
|
import { ResourceProvider } from "./resources.js";
|
|
19
19
|
import { PerformanceMetrics } from "./metrics.js";
|
|
20
20
|
import { estimateTokens, optimizePrompt as optimizePromptText, optimizeResponse as optimizeResponseText, } from "./optimizer.js";
|
|
21
|
-
import { loadConfig, loadPersistenceConfig, loadCacheAwarenessConfig, loadProvidersConfig, defaultGatewayConfigPath, isXaiProviderEnabled, minStableTokensForModel, } from "./config.js";
|
|
21
|
+
import { loadConfig, loadPersistenceConfig, loadCacheAwarenessConfig, loadProvidersConfig, loadAcpConfig, defaultGatewayConfigPath, isXaiProviderEnabled, enabledApiProviders, minStableTokensForModel, } from "./config.js";
|
|
22
|
+
import { runAcpRequest } from "./acp/runtime.js";
|
|
23
|
+
import { isAcpError } from "./acp/errors.js";
|
|
24
|
+
import { createApiProvider, runApiRequest, apiProviderBreakerState, } from "./api-provider.js";
|
|
25
|
+
import { prepareApiRequest, apiProviderCatalogEntry, ApiModelNotAllowedError, } from "./api-request.js";
|
|
22
26
|
import { createXaiResponse, XaiApiError, } from "./xai-api-provider.js";
|
|
23
27
|
import { checkHealth } from "./health.js";
|
|
24
28
|
import { clearModelRegistryCache, getAvailableCliInfo, getCliInfo, resolveModelAlias, } from "./model-registry.js";
|
|
@@ -28,7 +32,7 @@ import { createJobStore } from "./job-store.js";
|
|
|
28
32
|
import { ApprovalManager, bypassAllowedByOperator, } from "./approval-manager.js";
|
|
29
33
|
import { checkReviewIntegrity } from "./review-integrity.js";
|
|
30
34
|
import { buildClaudeMcpConfig, CLAUDE_MCP_SERVER_NAMES, } from "./claude-mcp-config.js";
|
|
31
|
-
import { resolveGrokSessionArgs, resolveMistralSessionArgs, resolveCodexSessionArgs, sanitizeCliArgValues, prepareMistralRequest as buildMistralCliInvocation,
|
|
35
|
+
import { resolveGrokSessionArgs, resolveMistralSessionArgs, resolveCodexSessionArgs, sanitizeCliArgValues, prepareMistralRequest as buildMistralCliInvocation, GATEWAY_SESSION_PREFIX, resolveClaudePermissionFlags, resolveCodexSandboxFlags, CLAUDE_PERMISSION_MODES, GEMINI_APPROVAL_MODES, CODEX_SANDBOX_MODES, CODEX_ASK_FOR_APPROVAL_MODES, CLAUDE_EFFORT_LEVELS, prepareClaudeHighImpactFlags, validateClaudeAgentsMap, prepareCodexHighImpactFlags, prepareCodexForkRequest, CODEX_CONFIG_OVERRIDES_SCHEMA, resolveGeminiSessionPlan, GEMINI_HIGH_IMPACT_PARAMS_SCHEMA, } from "./request-helpers.js";
|
|
32
36
|
import { createFlightRecorder } from "./flight-recorder.js";
|
|
33
37
|
import { resolvePromptInput, PromptPartsSchema, assembleClaudeCacheBlocks, } from "./prompt-parts.js";
|
|
34
38
|
import { computeSessionCacheStats, computeTtlRemaining, readPersistedRequest, PERSISTED_REQUEST_DEFAULT_MAX_CHARS, } from "./cache-stats.js";
|
|
@@ -183,6 +187,7 @@ let flightRecorder = null;
|
|
|
183
187
|
let persistenceConfig = null;
|
|
184
188
|
let cacheAwarenessConfig = null;
|
|
185
189
|
let providersConfig = null;
|
|
190
|
+
let acpConfig = null;
|
|
186
191
|
let jobStore = null;
|
|
187
192
|
let jobStoreInitialized = false;
|
|
188
193
|
let asyncJobManager = null;
|
|
@@ -195,6 +200,10 @@ function getPersistenceConfig(runtimeLogger = logger) {
|
|
|
195
200
|
persistenceConfig ??= loadPersistenceConfig(runtimeLogger);
|
|
196
201
|
return persistenceConfig;
|
|
197
202
|
}
|
|
203
|
+
function getAcpConfig(runtimeLogger = logger) {
|
|
204
|
+
acpConfig ??= loadAcpConfig(runtimeLogger);
|
|
205
|
+
return acpConfig;
|
|
206
|
+
}
|
|
198
207
|
function getCacheAwarenessConfig(runtimeLogger = logger) {
|
|
199
208
|
cacheAwarenessConfig ??= loadCacheAwarenessConfig(runtimeLogger);
|
|
200
209
|
return cacheAwarenessConfig;
|
|
@@ -229,7 +238,11 @@ function getApprovalManager(runtimeLogger = logger) {
|
|
|
229
238
|
approvalManager ??= new ApprovalManager(undefined, runtimeLogger);
|
|
230
239
|
return approvalManager;
|
|
231
240
|
}
|
|
232
|
-
|
|
241
|
+
function mcpServerEnum() {
|
|
242
|
+
return CLAUDE_MCP_SERVER_NAMES.length > 0
|
|
243
|
+
? z.enum(CLAUDE_MCP_SERVER_NAMES)
|
|
244
|
+
: z.string();
|
|
245
|
+
}
|
|
233
246
|
const CLI_TYPE_ENUM = z.enum(CLI_TYPES);
|
|
234
247
|
export const MAX_TURNS_SCHEMA = z.number().int().positive().safe().max(10_000);
|
|
235
248
|
const GROK_GENERATED_SHAPE = deriveZodShapeFromGeneration(UPSTREAM_CLI_CONTRACTS.grok, GROK_FLAG_GENERATION);
|
|
@@ -299,6 +312,7 @@ export function resolveGatewayServerRuntime(deps = {}, options = {}) {
|
|
|
299
312
|
persistence: deps.persistence ?? getPersistenceConfig(runtimeLogger),
|
|
300
313
|
cacheAwareness: deps.cacheAwareness ?? getCacheAwarenessConfig(runtimeLogger),
|
|
301
314
|
providers: deps.providers ?? getProvidersConfig(runtimeLogger),
|
|
315
|
+
acpConfig: deps.acpConfig ?? getAcpConfig(runtimeLogger),
|
|
302
316
|
workspaces: deps.workspaces ?? loadWorkspaceRegistry(runtimeLogger),
|
|
303
317
|
};
|
|
304
318
|
}
|
|
@@ -317,6 +331,44 @@ function resolveIdleTimeout(cli, override) {
|
|
|
317
331
|
return override;
|
|
318
332
|
return CLI_IDLE_TIMEOUTS[cli];
|
|
319
333
|
}
|
|
334
|
+
export async function runAcpTransport(deps, params) {
|
|
335
|
+
const runtime = resolveHandlerRuntime(deps);
|
|
336
|
+
const operation = `${params.provider}_request`;
|
|
337
|
+
const corrId = params.correlationId ?? randomUUID();
|
|
338
|
+
const prompt = (params.prompt ?? "").trim();
|
|
339
|
+
if (!prompt) {
|
|
340
|
+
return createErrorResponse(operation, 1, "prompt is required and cannot be empty", corrId);
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
const result = await runAcpRequest({
|
|
344
|
+
config: runtime.acpConfig,
|
|
345
|
+
sessionManager: runtime.sessionManager,
|
|
346
|
+
approvalManager: runtime.approvalManager,
|
|
347
|
+
flightRecorder: runtime.flightRecorder,
|
|
348
|
+
logger: runtime.logger,
|
|
349
|
+
}, {
|
|
350
|
+
provider: params.provider,
|
|
351
|
+
prompt,
|
|
352
|
+
model: params.model,
|
|
353
|
+
sessionId: params.sessionId,
|
|
354
|
+
correlationId: corrId,
|
|
355
|
+
});
|
|
356
|
+
return {
|
|
357
|
+
content: [
|
|
358
|
+
{
|
|
359
|
+
type: "text",
|
|
360
|
+
text: `[gateway] transport=acp session=${result.gatewaySessionId}\n${result.text}`,
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
if (isAcpError(err)) {
|
|
367
|
+
return createErrorResponse(operation, 1, err.userMessage, corrId);
|
|
368
|
+
}
|
|
369
|
+
return createErrorResponse(operation, 1, "", corrId, err);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
320
372
|
const SYNC_POLL_INTERVAL_MS = 1_000;
|
|
321
373
|
async function awaitJobOrDefer(cli, args, corrId, idleTimeoutMs, outputFormat, forceRefresh, runtime = resolveGatewayServerRuntime(), env, onComplete, flightRecorderEntry, extractUsage, stdin, cwd) {
|
|
322
374
|
let onCompleteOwnedByCaller = onComplete !== undefined;
|
|
@@ -406,6 +458,79 @@ async function awaitJobOrDefer(cli, args, corrId, idleTimeoutMs, outputFormat, f
|
|
|
406
458
|
message: `Execution exceeded sync deadline (${SYNC_DEADLINE_MS}ms). Poll with llm_job_status, collect with llm_job_result.`,
|
|
407
459
|
};
|
|
408
460
|
}
|
|
461
|
+
async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolveGatewayServerRuntime(), onComplete, flightRecorderEntry, extractUsage) {
|
|
462
|
+
let onCompleteOwnedByCaller = onComplete !== undefined;
|
|
463
|
+
const consumeOnComplete = () => {
|
|
464
|
+
if (!onCompleteOwnedByCaller || !onComplete)
|
|
465
|
+
return;
|
|
466
|
+
onCompleteOwnedByCaller = false;
|
|
467
|
+
try {
|
|
468
|
+
onComplete();
|
|
469
|
+
}
|
|
470
|
+
catch (err) {
|
|
471
|
+
runtime.logger.error(`awaitApiJobOrDefer onComplete (${provider.name}) threw`, err);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
const deferralAvailable = runtime.persistence.backend !== "none" &&
|
|
475
|
+
runtime.persistence.asyncJobsEnabled &&
|
|
476
|
+
runtime.asyncJobManager.hasStore();
|
|
477
|
+
if (SYNC_DEADLINE_MS === 0 || !deferralAvailable) {
|
|
478
|
+
try {
|
|
479
|
+
const result = await runApiRequest(provider, apiRequest, runtime.logger);
|
|
480
|
+
return { stdout: result.text, stderr: "", code: 0 };
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
return { stdout: "", stderr: err.message, code: 1 };
|
|
484
|
+
}
|
|
485
|
+
finally {
|
|
486
|
+
consumeOnComplete();
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
let outcome;
|
|
490
|
+
try {
|
|
491
|
+
outcome = runtime.asyncJobManager.startHttpJob({
|
|
492
|
+
provider,
|
|
493
|
+
apiRequest,
|
|
494
|
+
correlationId: corrId,
|
|
495
|
+
onComplete,
|
|
496
|
+
flightRecorderEntry,
|
|
497
|
+
extractUsage,
|
|
498
|
+
});
|
|
499
|
+
onCompleteOwnedByCaller = false;
|
|
500
|
+
}
|
|
501
|
+
catch (err) {
|
|
502
|
+
consumeOnComplete();
|
|
503
|
+
throw err;
|
|
504
|
+
}
|
|
505
|
+
const job = outcome.snapshot;
|
|
506
|
+
if (outcome.deduped) {
|
|
507
|
+
runtime.logger.info(`[${corrId}] api request deduped onto job ${job.id} (original corrId=${outcome.originalCorrelationId})`);
|
|
508
|
+
}
|
|
509
|
+
const deadline = Date.now() + SYNC_DEADLINE_MS;
|
|
510
|
+
while (Date.now() < deadline) {
|
|
511
|
+
const snapshot = runtime.asyncJobManager.getJobSnapshot(job.id);
|
|
512
|
+
if (snapshot && snapshot.status !== "running") {
|
|
513
|
+
const result = runtime.asyncJobManager.getJobResult(job.id);
|
|
514
|
+
if (!result)
|
|
515
|
+
return { stdout: "", stderr: "Job result unavailable", code: 1 };
|
|
516
|
+
return {
|
|
517
|
+
stdout: result.stdout,
|
|
518
|
+
stderr: result.stderr || result.error || "",
|
|
519
|
+
code: result.exitCode ?? 1,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
await new Promise(resolve => setTimeout(resolve, SYNC_POLL_INTERVAL_MS));
|
|
523
|
+
}
|
|
524
|
+
runtime.asyncJobManager.armFlightCompleteForDeferral(job.id);
|
|
525
|
+
runtime.logger.info(`[${corrId}] ${provider.name} sync deadline exceeded (${SYNC_DEADLINE_MS}ms), deferring to async job ${job.id}`);
|
|
526
|
+
return {
|
|
527
|
+
deferred: true,
|
|
528
|
+
jobId: job.id,
|
|
529
|
+
cli: provider.name,
|
|
530
|
+
correlationId: corrId,
|
|
531
|
+
message: `Execution exceeded sync deadline (${SYNC_DEADLINE_MS}ms). Poll with llm_job_status, collect with llm_job_result.`,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
409
534
|
function isDeferredResponse(result) {
|
|
410
535
|
return "deferred" in result && result.deferred === true;
|
|
411
536
|
}
|
|
@@ -795,7 +920,7 @@ function createApprovalDeniedResponse(operation, decision) {
|
|
|
795
920
|
}
|
|
796
921
|
function normalizeMcpServers(mcpServers) {
|
|
797
922
|
if (!mcpServers || mcpServers.length === 0) {
|
|
798
|
-
return [
|
|
923
|
+
return [];
|
|
799
924
|
}
|
|
800
925
|
return [...new Set(mcpServers)];
|
|
801
926
|
}
|
|
@@ -1552,9 +1677,6 @@ export function prepareGeminiRequest(params, runtime = resolveGatewayServerRunti
|
|
|
1552
1677
|
if (params.allowedTools && params.allowedTools.length > 0) {
|
|
1553
1678
|
return unsupported("allowedTools", "agy has no non-interactive allowed-tools flag");
|
|
1554
1679
|
}
|
|
1555
|
-
if (requestedMcpServers.length > 0) {
|
|
1556
|
-
return unsupported("mcpServers", "agy has no non-interactive allowed MCP server allowlist flag");
|
|
1557
|
-
}
|
|
1558
1680
|
if (params.outputFormat && params.outputFormat !== "text") {
|
|
1559
1681
|
return unsupported("outputFormat", "agy print mode currently emits text only");
|
|
1560
1682
|
}
|
|
@@ -2182,6 +2304,120 @@ export async function handleGrokApiRequest(deps, params) {
|
|
|
2182
2304
|
runtime.performanceMetrics.recordRequest("grok-api", durationMs || Math.max(0, Date.now() - startTime), wasSuccessful);
|
|
2183
2305
|
}
|
|
2184
2306
|
}
|
|
2307
|
+
function buildApiProviderCall(providerRuntime, params) {
|
|
2308
|
+
const apiRequest = prepareApiRequest(providerRuntime, {
|
|
2309
|
+
prompt: params.prompt ?? "",
|
|
2310
|
+
system: params.system,
|
|
2311
|
+
model: params.model,
|
|
2312
|
+
maxOutputTokens: params.maxOutputTokens,
|
|
2313
|
+
temperature: params.temperature,
|
|
2314
|
+
topP: params.topP,
|
|
2315
|
+
reasoningEffort: params.reasoningEffort,
|
|
2316
|
+
timeoutMs: params.timeoutMs,
|
|
2317
|
+
});
|
|
2318
|
+
const provider = createApiProvider(providerRuntime.name, providerRuntime.kind);
|
|
2319
|
+
return { provider, apiRequest };
|
|
2320
|
+
}
|
|
2321
|
+
function buildApiSuccessResponse(text, corrId, providerName) {
|
|
2322
|
+
return {
|
|
2323
|
+
content: [{ type: "text", text }],
|
|
2324
|
+
structuredContent: {
|
|
2325
|
+
response: text,
|
|
2326
|
+
correlationId: corrId,
|
|
2327
|
+
cli: providerName,
|
|
2328
|
+
exitCode: 0,
|
|
2329
|
+
},
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2332
|
+
export async function handleApiProviderRequest(runtimeArg, providerRuntime, params) {
|
|
2333
|
+
const toolName = `api_${providerRuntime.name}_request`;
|
|
2334
|
+
const corrId = params.correlationId ?? randomUUID();
|
|
2335
|
+
const startTime = Date.now();
|
|
2336
|
+
let wasSuccessful = false;
|
|
2337
|
+
try {
|
|
2338
|
+
if (!params.prompt || params.prompt.trim().length === 0) {
|
|
2339
|
+
return createErrorResponse(toolName, 1, "prompt is required and cannot be empty", corrId);
|
|
2340
|
+
}
|
|
2341
|
+
const { provider, apiRequest } = buildApiProviderCall(providerRuntime, params);
|
|
2342
|
+
const result = await awaitApiJobOrDefer(provider, apiRequest, corrId, runtimeArg);
|
|
2343
|
+
if (isDeferredResponse(result))
|
|
2344
|
+
return buildDeferredToolResponse(result);
|
|
2345
|
+
if (result.code !== 0) {
|
|
2346
|
+
return createErrorResponse(toolName, result.code, result.stderr, corrId);
|
|
2347
|
+
}
|
|
2348
|
+
wasSuccessful = true;
|
|
2349
|
+
return buildApiSuccessResponse(result.stdout, corrId, providerRuntime.name);
|
|
2350
|
+
}
|
|
2351
|
+
catch (err) {
|
|
2352
|
+
if (err instanceof ApiModelNotAllowedError) {
|
|
2353
|
+
return createErrorResponse(toolName, 1, err.message, corrId, err);
|
|
2354
|
+
}
|
|
2355
|
+
return createErrorResponse(toolName, 1, "", corrId, err);
|
|
2356
|
+
}
|
|
2357
|
+
finally {
|
|
2358
|
+
runtimeArg.performanceMetrics.recordRequest(providerRuntime.name, Math.max(0, Date.now() - startTime), wasSuccessful);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
export function handleApiProviderRequestAsync(runtimeArg, providerRuntime, params) {
|
|
2362
|
+
const toolName = `api_${providerRuntime.name}_request_async`;
|
|
2363
|
+
const corrId = params.correlationId ?? randomUUID();
|
|
2364
|
+
try {
|
|
2365
|
+
if (!params.prompt || params.prompt.trim().length === 0) {
|
|
2366
|
+
return createErrorResponse(toolName, 1, "prompt is required and cannot be empty", corrId);
|
|
2367
|
+
}
|
|
2368
|
+
const { provider, apiRequest } = buildApiProviderCall(providerRuntime, params);
|
|
2369
|
+
const outcome = runtimeArg.asyncJobManager.startHttpJob({
|
|
2370
|
+
provider,
|
|
2371
|
+
apiRequest,
|
|
2372
|
+
correlationId: corrId,
|
|
2373
|
+
writeFlightStart: true,
|
|
2374
|
+
});
|
|
2375
|
+
return buildDeferredToolResponse({
|
|
2376
|
+
deferred: true,
|
|
2377
|
+
jobId: outcome.snapshot.id,
|
|
2378
|
+
cli: providerRuntime.name,
|
|
2379
|
+
correlationId: corrId,
|
|
2380
|
+
message: outcome.deduped
|
|
2381
|
+
? `Deduped onto existing job ${outcome.snapshot.id}. Poll with llm_job_status.`
|
|
2382
|
+
: `Started async job ${outcome.snapshot.id}. Poll with llm_job_status, collect with llm_job_result.`,
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
catch (err) {
|
|
2386
|
+
if (err instanceof ApiModelNotAllowedError) {
|
|
2387
|
+
return createErrorResponse(toolName, 1, err.message, corrId, err);
|
|
2388
|
+
}
|
|
2389
|
+
return createErrorResponse(toolName, 1, "", corrId, err);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
const ApiReasoningEffortSchema = z.enum(["none", "low", "medium", "high"]);
|
|
2393
|
+
export function registerApiProviderTools(server, runtime, providers, asyncJobsEnabled) {
|
|
2394
|
+
const registered = [];
|
|
2395
|
+
const inputSchema = {
|
|
2396
|
+
prompt: z.string().min(1).max(100000).optional().describe("Prompt text for the API provider"),
|
|
2397
|
+
system: z.string().max(100000).optional().describe("Optional system instruction"),
|
|
2398
|
+
model: z
|
|
2399
|
+
.string()
|
|
2400
|
+
.min(1)
|
|
2401
|
+
.optional()
|
|
2402
|
+
.describe("Model id; defaults to the provider default_model"),
|
|
2403
|
+
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
2404
|
+
maxOutputTokens: z.number().int().positive().max(100000000).optional(),
|
|
2405
|
+
temperature: z.number().finite().min(0).max(2).optional(),
|
|
2406
|
+
topP: z.number().finite().min(0).max(1).optional(),
|
|
2407
|
+
reasoningEffort: ApiReasoningEffortSchema.optional(),
|
|
2408
|
+
timeoutMs: z.number().int().min(30_000).max(3_600_000).optional(),
|
|
2409
|
+
};
|
|
2410
|
+
for (const providerRuntime of enabledApiProviders(providers)) {
|
|
2411
|
+
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));
|
|
2413
|
+
registered.push(`api_${name}_request`);
|
|
2414
|
+
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));
|
|
2416
|
+
registered.push(`api_${name}_request_async`);
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
return registered;
|
|
2420
|
+
}
|
|
2185
2421
|
function maybeBuildCacheTtlWarning(args) {
|
|
2186
2422
|
if (args.cli !== "claude")
|
|
2187
2423
|
return null;
|
|
@@ -2467,6 +2703,15 @@ export async function handleGeminiRequestAsync(deps, params) {
|
|
|
2467
2703
|
}
|
|
2468
2704
|
}
|
|
2469
2705
|
export async function handleGrokRequest(deps, params) {
|
|
2706
|
+
if (params.transport === "acp") {
|
|
2707
|
+
return runAcpTransport(deps, {
|
|
2708
|
+
provider: "grok",
|
|
2709
|
+
prompt: params.prompt,
|
|
2710
|
+
model: params.model,
|
|
2711
|
+
sessionId: params.sessionId,
|
|
2712
|
+
correlationId: params.correlationId,
|
|
2713
|
+
});
|
|
2714
|
+
}
|
|
2470
2715
|
const runtime = resolveHandlerRuntime(deps);
|
|
2471
2716
|
const startTime = Date.now();
|
|
2472
2717
|
const prep = prepareGrokRequest({
|
|
@@ -2761,7 +3006,222 @@ export async function handleGrokRequestAsync(deps, params) {
|
|
|
2761
3006
|
return createErrorResponse("grok_request_async", 1, "", corrId, error);
|
|
2762
3007
|
}
|
|
2763
3008
|
}
|
|
3009
|
+
export function prepareDevinRequest(params, _runtime) {
|
|
3010
|
+
const corrId = params.correlationId ?? randomUUID();
|
|
3011
|
+
let prompt = (params.prompt ?? "").trim();
|
|
3012
|
+
if (!prompt) {
|
|
3013
|
+
return createErrorResponse(params.operation, 1, "prompt is required and cannot be empty", corrId);
|
|
3014
|
+
}
|
|
3015
|
+
if (params.optimizePrompt)
|
|
3016
|
+
prompt = optimizePromptText(prompt);
|
|
3017
|
+
const resolvedModel = resolveModelAlias("devin", params.model, getCliInfo());
|
|
3018
|
+
const args = ["-p", prompt];
|
|
3019
|
+
if (resolvedModel)
|
|
3020
|
+
args.push("--model", resolvedModel);
|
|
3021
|
+
if (params.permissionMode)
|
|
3022
|
+
args.push("--permission-mode", params.permissionMode);
|
|
3023
|
+
if (params.promptFile)
|
|
3024
|
+
args.push("--prompt-file", params.promptFile);
|
|
3025
|
+
return {
|
|
3026
|
+
corrId,
|
|
3027
|
+
effectivePrompt: prompt,
|
|
3028
|
+
resolvedModel,
|
|
3029
|
+
requestedMcpServers: [],
|
|
3030
|
+
approvalDecision: null,
|
|
3031
|
+
args,
|
|
3032
|
+
stablePrefixHash: null,
|
|
3033
|
+
stablePrefixTokens: null,
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
export async function handleDevinRequest(deps, params) {
|
|
3037
|
+
if (params.transport === "acp") {
|
|
3038
|
+
return runAcpTransport(deps, {
|
|
3039
|
+
provider: "devin",
|
|
3040
|
+
prompt: params.prompt,
|
|
3041
|
+
model: params.model,
|
|
3042
|
+
sessionId: params.sessionId,
|
|
3043
|
+
correlationId: params.correlationId,
|
|
3044
|
+
});
|
|
3045
|
+
}
|
|
3046
|
+
const runtime = resolveHandlerRuntime(deps);
|
|
3047
|
+
const startTime = Date.now();
|
|
3048
|
+
const prep = prepareDevinRequest({
|
|
3049
|
+
prompt: params.prompt,
|
|
3050
|
+
model: params.model,
|
|
3051
|
+
permissionMode: params.permissionMode,
|
|
3052
|
+
promptFile: params.promptFile,
|
|
3053
|
+
correlationId: params.correlationId,
|
|
3054
|
+
optimizePrompt: params.optimizePrompt,
|
|
3055
|
+
operation: "devin_request",
|
|
3056
|
+
}, runtime);
|
|
3057
|
+
if (!("args" in prep))
|
|
3058
|
+
return prep;
|
|
3059
|
+
const { corrId, args } = prep;
|
|
3060
|
+
let durationMs = 0;
|
|
3061
|
+
let wasSuccessful = false;
|
|
3062
|
+
safeFlightStart({
|
|
3063
|
+
correlationId: corrId,
|
|
3064
|
+
cli: "devin",
|
|
3065
|
+
model: prep.resolvedModel || "default",
|
|
3066
|
+
prompt: prep.effectivePrompt,
|
|
3067
|
+
sessionId: params.sessionId,
|
|
3068
|
+
}, runtime);
|
|
3069
|
+
try {
|
|
3070
|
+
const sessionResult = resolveGrokSessionArgs({
|
|
3071
|
+
sessionId: params.sessionId,
|
|
3072
|
+
resumeLatest: params.resumeLatest,
|
|
3073
|
+
createNewSession: params.createNewSession,
|
|
3074
|
+
});
|
|
3075
|
+
if (sessionResult.userProvidedSession) {
|
|
3076
|
+
await getExistingSessionForProvider(deps.sessionManager, sessionResult.effectiveSessionId, "devin");
|
|
3077
|
+
}
|
|
3078
|
+
args.push(...sessionResult.resumeArgs);
|
|
3079
|
+
const devinFrHandoff = buildAsyncFlightRecorderHandoff("devin", prep, params.sessionId, undefined);
|
|
3080
|
+
const result = await awaitJobOrDefer("devin", args, corrId, resolveIdleTimeout("devin", params.idleTimeoutMs), undefined, params.forceRefresh, runtime, undefined, undefined, devinFrHandoff.flightRecorderEntry, devinFrHandoff.extractUsage);
|
|
3081
|
+
if (isDeferredResponse(result)) {
|
|
3082
|
+
return buildDeferredToolResponse(result, sessionResult.effectiveSessionId);
|
|
3083
|
+
}
|
|
3084
|
+
const { stdout, stderr, code } = result;
|
|
3085
|
+
durationMs = Math.max(0, Date.now() - startTime);
|
|
3086
|
+
if (code !== 0) {
|
|
3087
|
+
safeFlightComplete(corrId, {
|
|
3088
|
+
response: stderr || "",
|
|
3089
|
+
durationMs,
|
|
3090
|
+
retryCount: 0,
|
|
3091
|
+
circuitBreakerState: "closed",
|
|
3092
|
+
optimizationApplied: false,
|
|
3093
|
+
exitCode: code,
|
|
3094
|
+
errorMessage: stderr || `Exit code ${code}`,
|
|
3095
|
+
status: "failed",
|
|
3096
|
+
}, runtime);
|
|
3097
|
+
return createErrorResponse("devin", code, stderr, corrId);
|
|
3098
|
+
}
|
|
3099
|
+
wasSuccessful = true;
|
|
3100
|
+
let effectiveSessionId = sessionResult.effectiveSessionId;
|
|
3101
|
+
if (sessionResult.userProvidedSession && effectiveSessionId) {
|
|
3102
|
+
const existing = await deps.sessionManager.getSession(effectiveSessionId);
|
|
3103
|
+
if (!existing) {
|
|
3104
|
+
try {
|
|
3105
|
+
await deps.sessionManager.createSession("devin", "Devin Session", effectiveSessionId);
|
|
3106
|
+
}
|
|
3107
|
+
catch {
|
|
3108
|
+
const rechecked = await deps.sessionManager.getSession(effectiveSessionId);
|
|
3109
|
+
if (!rechecked)
|
|
3110
|
+
throw new Error(`Failed to create or find session ${effectiveSessionId}`);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
await deps.sessionManager.updateSessionUsage(effectiveSessionId);
|
|
3114
|
+
}
|
|
3115
|
+
else if (!params.createNewSession && !effectiveSessionId) {
|
|
3116
|
+
const newSession = await deps.sessionManager.createSession("devin", "Devin Session", `${GATEWAY_SESSION_PREFIX}${randomUUID()}`);
|
|
3117
|
+
effectiveSessionId = newSession.id;
|
|
3118
|
+
}
|
|
3119
|
+
const response = buildCliResponse("devin", stdout, params.optimizeResponse ?? false, corrId, effectiveSessionId, prep, durationMs, sessionResult.userProvidedSession);
|
|
3120
|
+
safeFlightComplete(corrId, {
|
|
3121
|
+
response: stdout,
|
|
3122
|
+
durationMs,
|
|
3123
|
+
retryCount: 0,
|
|
3124
|
+
circuitBreakerState: "closed",
|
|
3125
|
+
optimizationApplied: params.optimizePrompt || (params.optimizeResponse ?? false),
|
|
3126
|
+
exitCode: 0,
|
|
3127
|
+
status: "completed",
|
|
3128
|
+
}, runtime);
|
|
3129
|
+
return response;
|
|
3130
|
+
}
|
|
3131
|
+
catch (error) {
|
|
3132
|
+
const elapsedMs = Math.max(0, Date.now() - startTime);
|
|
3133
|
+
safeFlightComplete(corrId, {
|
|
3134
|
+
response: "",
|
|
3135
|
+
durationMs: elapsedMs,
|
|
3136
|
+
retryCount: 0,
|
|
3137
|
+
circuitBreakerState: "closed",
|
|
3138
|
+
optimizationApplied: false,
|
|
3139
|
+
exitCode: 1,
|
|
3140
|
+
errorMessage: error.message,
|
|
3141
|
+
status: "failed",
|
|
3142
|
+
}, runtime);
|
|
3143
|
+
return createErrorResponse("devin", 1, "", corrId, error);
|
|
3144
|
+
}
|
|
3145
|
+
finally {
|
|
3146
|
+
runtime.performanceMetrics.recordRequest("devin", Math.max(0, durationMs || Date.now() - startTime), wasSuccessful);
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
export async function handleDevinRequestAsync(deps, params) {
|
|
3150
|
+
const runtime = resolveHandlerRuntime(deps);
|
|
3151
|
+
const prep = prepareDevinRequest({
|
|
3152
|
+
prompt: params.prompt,
|
|
3153
|
+
model: params.model,
|
|
3154
|
+
permissionMode: params.permissionMode,
|
|
3155
|
+
promptFile: params.promptFile,
|
|
3156
|
+
correlationId: params.correlationId,
|
|
3157
|
+
optimizePrompt: params.optimizePrompt,
|
|
3158
|
+
operation: "devin_request_async",
|
|
3159
|
+
}, runtime);
|
|
3160
|
+
if (!("args" in prep))
|
|
3161
|
+
return prep;
|
|
3162
|
+
const { corrId, args } = prep;
|
|
3163
|
+
try {
|
|
3164
|
+
const sessionResult = resolveGrokSessionArgs({
|
|
3165
|
+
sessionId: params.sessionId,
|
|
3166
|
+
resumeLatest: params.resumeLatest,
|
|
3167
|
+
createNewSession: params.createNewSession,
|
|
3168
|
+
});
|
|
3169
|
+
if (sessionResult.userProvidedSession) {
|
|
3170
|
+
await getExistingSessionForProvider(deps.sessionManager, sessionResult.effectiveSessionId, "devin");
|
|
3171
|
+
}
|
|
3172
|
+
args.push(...sessionResult.resumeArgs);
|
|
3173
|
+
let effectiveSessionId = sessionResult.effectiveSessionId;
|
|
3174
|
+
if (sessionResult.userProvidedSession && effectiveSessionId) {
|
|
3175
|
+
const existing = await deps.sessionManager.getSession(effectiveSessionId);
|
|
3176
|
+
if (!existing) {
|
|
3177
|
+
try {
|
|
3178
|
+
await deps.sessionManager.createSession("devin", "Devin Session", effectiveSessionId);
|
|
3179
|
+
}
|
|
3180
|
+
catch {
|
|
3181
|
+
const rechecked = await deps.sessionManager.getSession(effectiveSessionId);
|
|
3182
|
+
if (!rechecked)
|
|
3183
|
+
throw new Error(`Failed to create or find session ${effectiveSessionId}`);
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
await deps.sessionManager.updateSessionUsage(effectiveSessionId);
|
|
3187
|
+
}
|
|
3188
|
+
else if (!params.createNewSession && !effectiveSessionId) {
|
|
3189
|
+
const newSession = await deps.sessionManager.createSession("devin", "Devin Session", `${GATEWAY_SESSION_PREFIX}${randomUUID()}`);
|
|
3190
|
+
effectiveSessionId = newSession.id;
|
|
3191
|
+
}
|
|
3192
|
+
assertUpstreamCliArgs("devin", args);
|
|
3193
|
+
assertUpstreamCliEnv("devin", undefined);
|
|
3194
|
+
const devinAsyncFrHandoff = buildAsyncFlightRecorderHandoff("devin", prep, effectiveSessionId, undefined);
|
|
3195
|
+
const job = deps.asyncJobManager.startJob("devin", args, corrId, undefined, resolveIdleTimeout("devin", params.idleTimeoutMs), undefined, params.forceRefresh, undefined, undefined, devinAsyncFrHandoff.flightRecorderEntry, devinAsyncFrHandoff.extractUsage, true);
|
|
3196
|
+
deps.logger.info(`[${corrId}] devin_request_async started job ${job.id}`);
|
|
3197
|
+
return {
|
|
3198
|
+
content: [
|
|
3199
|
+
{
|
|
3200
|
+
type: "text",
|
|
3201
|
+
text: JSON.stringify({
|
|
3202
|
+
success: true,
|
|
3203
|
+
job,
|
|
3204
|
+
sessionId: effectiveSessionId || null,
|
|
3205
|
+
resumable: sessionResult.userProvidedSession,
|
|
3206
|
+
}, null, 2),
|
|
3207
|
+
},
|
|
3208
|
+
],
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
catch (error) {
|
|
3212
|
+
return createErrorResponse("devin_request_async", 1, "", corrId, error);
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
2764
3215
|
export async function handleMistralRequest(deps, params) {
|
|
3216
|
+
if (params.transport === "acp") {
|
|
3217
|
+
return runAcpTransport(deps, {
|
|
3218
|
+
provider: "mistral",
|
|
3219
|
+
prompt: params.prompt,
|
|
3220
|
+
model: params.model,
|
|
3221
|
+
sessionId: params.sessionId,
|
|
3222
|
+
correlationId: params.correlationId,
|
|
3223
|
+
});
|
|
3224
|
+
}
|
|
2765
3225
|
const runtime = resolveHandlerRuntime(deps);
|
|
2766
3226
|
const startTime = Date.now();
|
|
2767
3227
|
const prep = prepareMistralRequest({
|
|
@@ -3157,8 +3617,15 @@ export function createGatewayServer(deps = {}) {
|
|
|
3157
3617
|
const asyncJobsEnabled = persistence.backend !== "none" && persistence.asyncJobsEnabled && asyncJobManager.hasStore();
|
|
3158
3618
|
const server = newGatewayMcpServer(asyncJobsEnabled, grokApiToolsEnabled);
|
|
3159
3619
|
registerBaseResources(server, runtime);
|
|
3160
|
-
registerValidationTools(server, {
|
|
3620
|
+
registerValidationTools(server, {
|
|
3621
|
+
asyncJobManager,
|
|
3622
|
+
apiProviders: enabledApiProviders(providers),
|
|
3623
|
+
});
|
|
3161
3624
|
registerWorkspaceTools(server, runtime);
|
|
3625
|
+
const apiProviderTools = registerApiProviderTools(server, runtime, providers, asyncJobsEnabled);
|
|
3626
|
+
if (apiProviderTools.length > 0) {
|
|
3627
|
+
runtime.logger.info(`Registered API provider tools: ${apiProviderTools.join(", ")}`);
|
|
3628
|
+
}
|
|
3162
3629
|
if (grokApiToolsEnabled) {
|
|
3163
3630
|
server.tool("grok_api_request", "Run an xAI Grok API request synchronously through the Responses API. Requires exactly one of prompt or promptParts. Registered only when [providers.xai] is configured and its API-key env var is present.", {
|
|
3164
3631
|
prompt: z
|
|
@@ -3352,10 +3819,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3352
3819
|
.enum(["strict", "balanced", "permissive"])
|
|
3353
3820
|
.optional()
|
|
3354
3821
|
.describe("Approval policy override"),
|
|
3355
|
-
mcpServers: z
|
|
3356
|
-
.array(MCP_SERVER_ENUM)
|
|
3357
|
-
.default(["sqry"])
|
|
3358
|
-
.describe("MCP servers exposed to Claude"),
|
|
3822
|
+
mcpServers: z.array(mcpServerEnum()).default([]).describe("MCP servers exposed to Claude"),
|
|
3359
3823
|
strictMcpConfig: z
|
|
3360
3824
|
.boolean()
|
|
3361
3825
|
.default(false)
|
|
@@ -3626,8 +4090,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
3626
4090
|
.optional()
|
|
3627
4091
|
.describe("Approval policy override"),
|
|
3628
4092
|
mcpServers: z
|
|
3629
|
-
.array(
|
|
3630
|
-
.default([
|
|
4093
|
+
.array(mcpServerEnum())
|
|
4094
|
+
.default([])
|
|
3631
4095
|
.describe("MCP server names for approval tracking (Codex manages its own MCP config)"),
|
|
3632
4096
|
sessionId: z
|
|
3633
4097
|
.string()
|
|
@@ -3994,9 +4458,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
3994
4458
|
.optional()
|
|
3995
4459
|
.describe("Approval policy override"),
|
|
3996
4460
|
mcpServers: z
|
|
3997
|
-
.array(
|
|
4461
|
+
.array(mcpServerEnum())
|
|
3998
4462
|
.default([])
|
|
3999
|
-
.describe("
|
|
4463
|
+
.describe("MCP server names accepted for approval tracking only; Antigravity manages its own MCP configuration."),
|
|
4000
4464
|
allowedTools: z
|
|
4001
4465
|
.array(z.string())
|
|
4002
4466
|
.optional()
|
|
@@ -4082,6 +4546,10 @@ export function createGatewayServer(deps = {}) {
|
|
|
4082
4546
|
.describe("Prompt text for Grok (mutually exclusive with promptParts)"),
|
|
4083
4547
|
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."),
|
|
4084
4548
|
model: z.string().optional().describe("Model name or alias (e.g. grok-build, latest)"),
|
|
4549
|
+
transport: z
|
|
4550
|
+
.enum(["cli", "acp"])
|
|
4551
|
+
.default("cli")
|
|
4552
|
+
.describe("Transport: 'cli' (default) runs the Grok CLI; 'acp' routes through `grok agent stdio` when [acp].enabled and the provider's runtime_enabled are set (fails closed otherwise)."),
|
|
4085
4553
|
...GROK_GENERATED_SHAPE,
|
|
4086
4554
|
sessionId: z
|
|
4087
4555
|
.string()
|
|
@@ -4109,8 +4577,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
4109
4577
|
.optional()
|
|
4110
4578
|
.describe("Approval policy override"),
|
|
4111
4579
|
mcpServers: z
|
|
4112
|
-
.array(
|
|
4113
|
-
.default([
|
|
4580
|
+
.array(mcpServerEnum())
|
|
4581
|
+
.default([])
|
|
4114
4582
|
.describe("MCP server names for approval tracking (Grok manages its own MCP config via `grok mcp`)"),
|
|
4115
4583
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
4116
4584
|
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
@@ -4146,11 +4614,12 @@ export function createGatewayServer(deps = {}) {
|
|
|
4146
4614
|
destructiveHint: true,
|
|
4147
4615
|
idempotentHint: false,
|
|
4148
4616
|
openWorldHint: true,
|
|
4149
|
-
}, async ({ prompt, promptParts, model, 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, }) => {
|
|
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, }) => {
|
|
4150
4618
|
return handleGrokRequest({ sessionManager, logger, runtime }, {
|
|
4151
4619
|
prompt,
|
|
4152
4620
|
promptParts,
|
|
4153
4621
|
model,
|
|
4622
|
+
transport,
|
|
4154
4623
|
outputFormat,
|
|
4155
4624
|
sessionId,
|
|
4156
4625
|
resumeLatest,
|
|
@@ -4201,6 +4670,72 @@ export function createGatewayServer(deps = {}) {
|
|
|
4201
4670
|
worktree,
|
|
4202
4671
|
});
|
|
4203
4672
|
});
|
|
4673
|
+
server.tool("devin_request", "Run a Cognition Devin CLI request synchronously (auto-defers to a pollable job past the sync deadline when async jobs are enabled; otherwise runs to completion). Headless print mode (`devin -p`).", {
|
|
4674
|
+
prompt: z
|
|
4675
|
+
.string()
|
|
4676
|
+
.min(1, "Prompt cannot be empty")
|
|
4677
|
+
.max(100000, "Prompt too long (max 100k chars)")
|
|
4678
|
+
.optional()
|
|
4679
|
+
.describe("Prompt text for Devin CLI"),
|
|
4680
|
+
model: z.string().optional().describe("Model name or alias (e.g. opus, latest)"),
|
|
4681
|
+
transport: z
|
|
4682
|
+
.enum(["cli", "acp"])
|
|
4683
|
+
.default("cli")
|
|
4684
|
+
.describe("Transport: 'cli' (default) runs the Devin CLI; 'acp' routes through `devin acp` when [acp].enabled and the provider's runtime_enabled are set (fails closed otherwise)."),
|
|
4685
|
+
permissionMode: z
|
|
4686
|
+
.enum(["normal", "auto", "dangerous", "yolo", "bypass"])
|
|
4687
|
+
.optional()
|
|
4688
|
+
.describe("Devin CLI permission mode (--permission-mode). normal (alias auto) auto-approves read-only tools; dangerous (aliases yolo, bypass) auto-approves all."),
|
|
4689
|
+
promptFile: z
|
|
4690
|
+
.string()
|
|
4691
|
+
.optional()
|
|
4692
|
+
.describe("Load the initial prompt from a file (--prompt-file)"),
|
|
4693
|
+
sessionId: z
|
|
4694
|
+
.string()
|
|
4695
|
+
.optional()
|
|
4696
|
+
.describe("Devin session ID to resume (emits --resume <id>; use resumeLatest for --continue)"),
|
|
4697
|
+
resumeLatest: z
|
|
4698
|
+
.boolean()
|
|
4699
|
+
.default(false)
|
|
4700
|
+
.describe("Resume the most recent Devin session in cwd (--continue)"),
|
|
4701
|
+
createNewSession: z.boolean().default(false).describe("Force a new session"),
|
|
4702
|
+
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
4703
|
+
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
4704
|
+
optimizeResponse: z.boolean().default(false).describe("Optimize response output"),
|
|
4705
|
+
idleTimeoutMs: z
|
|
4706
|
+
.number()
|
|
4707
|
+
.int()
|
|
4708
|
+
.min(30_000)
|
|
4709
|
+
.max(3_600_000)
|
|
4710
|
+
.optional()
|
|
4711
|
+
.describe("Idle timeout in ms (min 30s, max 1h, omit=CLI default)"),
|
|
4712
|
+
forceRefresh: z
|
|
4713
|
+
.boolean()
|
|
4714
|
+
.default(false)
|
|
4715
|
+
.describe("Bypass dedup and force a fresh CLI run even if a recent identical request exists"),
|
|
4716
|
+
}, {
|
|
4717
|
+
title: "Devin CLI request",
|
|
4718
|
+
readOnlyHint: false,
|
|
4719
|
+
destructiveHint: true,
|
|
4720
|
+
idempotentHint: false,
|
|
4721
|
+
openWorldHint: true,
|
|
4722
|
+
}, async ({ prompt, model, transport, permissionMode, promptFile, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, }) => {
|
|
4723
|
+
return handleDevinRequest({ sessionManager, logger, runtime }, {
|
|
4724
|
+
prompt,
|
|
4725
|
+
model,
|
|
4726
|
+
transport,
|
|
4727
|
+
permissionMode,
|
|
4728
|
+
promptFile,
|
|
4729
|
+
sessionId,
|
|
4730
|
+
resumeLatest,
|
|
4731
|
+
createNewSession,
|
|
4732
|
+
correlationId,
|
|
4733
|
+
optimizePrompt,
|
|
4734
|
+
optimizeResponse,
|
|
4735
|
+
idleTimeoutMs,
|
|
4736
|
+
forceRefresh,
|
|
4737
|
+
});
|
|
4738
|
+
});
|
|
4204
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.", {
|
|
4205
4740
|
prompt: z
|
|
4206
4741
|
.string()
|
|
@@ -4213,6 +4748,10 @@ export function createGatewayServer(deps = {}) {
|
|
|
4213
4748
|
.string()
|
|
4214
4749
|
.optional()
|
|
4215
4750
|
.describe("Model alias (e.g. mistral-medium-3.5, latest). Resolved alias is injected via VIBE_ACTIVE_MODEL env var; Vibe has no --model flag."),
|
|
4751
|
+
transport: z
|
|
4752
|
+
.enum(["cli", "acp"])
|
|
4753
|
+
.default("cli")
|
|
4754
|
+
.describe("Transport: 'cli' (default) runs the Vibe CLI; 'acp' routes through `vibe-acp` when [acp].enabled and the provider's runtime_enabled are set (fails closed otherwise)."),
|
|
4216
4755
|
outputFormat: z
|
|
4217
4756
|
.enum(["text", "plain", "json", "streaming", "stream-json"])
|
|
4218
4757
|
.optional()
|
|
@@ -4227,9 +4766,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
4227
4766
|
.describe("Resume most recent Vibe session in cwd (--continue)"),
|
|
4228
4767
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
4229
4768
|
permissionMode: z
|
|
4230
|
-
.
|
|
4769
|
+
.string()
|
|
4231
4770
|
.optional()
|
|
4232
|
-
.describe("Vibe agent
|
|
4771
|
+
.describe("Vibe --agent name. Builtins: default|plan|accept-edits|auto-approve; Vibe also accepts install-gated builtins (e.g. lean) and custom agents from ~/.vibe/agents, so any name is passed through. Defaults to auto-approve for programmatic use."),
|
|
4233
4772
|
approvalStrategy: z
|
|
4234
4773
|
.enum(["legacy", "mcp_managed"])
|
|
4235
4774
|
.default("legacy")
|
|
@@ -4239,8 +4778,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
4239
4778
|
.optional()
|
|
4240
4779
|
.describe("Approval policy override"),
|
|
4241
4780
|
mcpServers: z
|
|
4242
|
-
.array(
|
|
4243
|
-
.default([
|
|
4781
|
+
.array(mcpServerEnum())
|
|
4782
|
+
.default([])
|
|
4244
4783
|
.describe("MCP server names for approval tracking (Vibe manages its own MCP config via `vibe mcp`)"),
|
|
4245
4784
|
allowedTools: z
|
|
4246
4785
|
.array(z.string())
|
|
@@ -4288,11 +4827,12 @@ export function createGatewayServer(deps = {}) {
|
|
|
4288
4827
|
destructiveHint: true,
|
|
4289
4828
|
idempotentHint: false,
|
|
4290
4829
|
openWorldHint: true,
|
|
4291
|
-
}, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, maxTokens, workingDir, addDir, workspace, worktree, }) => {
|
|
4830
|
+
}, async ({ prompt, promptParts, model, transport, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, maxTokens, workingDir, addDir, workspace, worktree, }) => {
|
|
4292
4831
|
return handleMistralRequest({ sessionManager, logger, runtime }, {
|
|
4293
4832
|
prompt,
|
|
4294
4833
|
promptParts,
|
|
4295
4834
|
model,
|
|
4835
|
+
transport,
|
|
4296
4836
|
outputFormat,
|
|
4297
4837
|
sessionId,
|
|
4298
4838
|
resumeLatest,
|
|
@@ -4437,10 +4977,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4437
4977
|
.enum(["strict", "balanced", "permissive"])
|
|
4438
4978
|
.optional()
|
|
4439
4979
|
.describe("Approval policy override"),
|
|
4440
|
-
mcpServers: z
|
|
4441
|
-
.array(MCP_SERVER_ENUM)
|
|
4442
|
-
.default(["sqry"])
|
|
4443
|
-
.describe("MCP servers exposed to Claude"),
|
|
4980
|
+
mcpServers: z.array(mcpServerEnum()).default([]).describe("MCP servers exposed to Claude"),
|
|
4444
4981
|
strictMcpConfig: z
|
|
4445
4982
|
.boolean()
|
|
4446
4983
|
.default(false)
|
|
@@ -4630,8 +5167,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
4630
5167
|
.optional()
|
|
4631
5168
|
.describe("Approval policy override"),
|
|
4632
5169
|
mcpServers: z
|
|
4633
|
-
.array(
|
|
4634
|
-
.default([
|
|
5170
|
+
.array(mcpServerEnum())
|
|
5171
|
+
.default([])
|
|
4635
5172
|
.describe("MCP server names for approval tracking (Codex manages its own MCP config)"),
|
|
4636
5173
|
sessionId: z
|
|
4637
5174
|
.string()
|
|
@@ -4756,9 +5293,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
4756
5293
|
.optional()
|
|
4757
5294
|
.describe("Approval policy override"),
|
|
4758
5295
|
mcpServers: z
|
|
4759
|
-
.array(
|
|
5296
|
+
.array(mcpServerEnum())
|
|
4760
5297
|
.default([])
|
|
4761
|
-
.describe("
|
|
5298
|
+
.describe("MCP server names accepted for approval tracking only; Antigravity manages its own MCP configuration."),
|
|
4762
5299
|
allowedTools: z
|
|
4763
5300
|
.array(z.string())
|
|
4764
5301
|
.optional()
|
|
@@ -4877,8 +5414,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
4877
5414
|
.optional()
|
|
4878
5415
|
.describe("Approval policy override"),
|
|
4879
5416
|
mcpServers: z
|
|
4880
|
-
.array(
|
|
4881
|
-
.default([
|
|
5417
|
+
.array(mcpServerEnum())
|
|
5418
|
+
.default([])
|
|
4882
5419
|
.describe("MCP server names for approval tracking (Grok manages its own MCP config via `grok mcp`)"),
|
|
4883
5420
|
allowedTools: z
|
|
4884
5421
|
.array(z.string())
|
|
@@ -5071,6 +5608,65 @@ export function createGatewayServer(deps = {}) {
|
|
|
5071
5608
|
worktree,
|
|
5072
5609
|
});
|
|
5073
5610
|
});
|
|
5611
|
+
server.tool("devin_request_async", "Start a Cognition Devin CLI request as a durable background job. Poll with llm_job_status, collect with llm_job_result.", {
|
|
5612
|
+
prompt: z
|
|
5613
|
+
.string()
|
|
5614
|
+
.min(1, "Prompt cannot be empty")
|
|
5615
|
+
.max(100000, "Prompt too long (max 100k chars)")
|
|
5616
|
+
.optional()
|
|
5617
|
+
.describe("Prompt text for Devin CLI"),
|
|
5618
|
+
model: z.string().optional().describe("Model name or alias (e.g. opus, latest)"),
|
|
5619
|
+
permissionMode: z
|
|
5620
|
+
.enum(["normal", "dangerous", "bypass"])
|
|
5621
|
+
.optional()
|
|
5622
|
+
.describe("Devin CLI permission mode (--permission-mode)"),
|
|
5623
|
+
promptFile: z
|
|
5624
|
+
.string()
|
|
5625
|
+
.optional()
|
|
5626
|
+
.describe("Load the initial prompt from a file (--prompt-file)"),
|
|
5627
|
+
sessionId: z
|
|
5628
|
+
.string()
|
|
5629
|
+
.optional()
|
|
5630
|
+
.describe("Devin session ID to resume (--resume <id>; use resumeLatest for --continue)"),
|
|
5631
|
+
resumeLatest: z
|
|
5632
|
+
.boolean()
|
|
5633
|
+
.default(false)
|
|
5634
|
+
.describe("Resume the most recent Devin session in cwd (--continue)"),
|
|
5635
|
+
createNewSession: z.boolean().default(false).describe("Force a new session"),
|
|
5636
|
+
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
5637
|
+
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
5638
|
+
idleTimeoutMs: z
|
|
5639
|
+
.number()
|
|
5640
|
+
.int()
|
|
5641
|
+
.min(30_000)
|
|
5642
|
+
.max(3_600_000)
|
|
5643
|
+
.optional()
|
|
5644
|
+
.describe("Idle timeout in ms (min 30s, max 1h, omit=CLI default)"),
|
|
5645
|
+
forceRefresh: z
|
|
5646
|
+
.boolean()
|
|
5647
|
+
.default(false)
|
|
5648
|
+
.describe("Bypass dedup and force a fresh CLI run even if a recent identical request exists"),
|
|
5649
|
+
}, {
|
|
5650
|
+
title: "Devin CLI request (async)",
|
|
5651
|
+
readOnlyHint: false,
|
|
5652
|
+
destructiveHint: true,
|
|
5653
|
+
idempotentHint: false,
|
|
5654
|
+
openWorldHint: true,
|
|
5655
|
+
}, async ({ prompt, model, permissionMode, promptFile, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, }) => {
|
|
5656
|
+
return handleDevinRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
|
|
5657
|
+
prompt,
|
|
5658
|
+
model,
|
|
5659
|
+
permissionMode,
|
|
5660
|
+
promptFile,
|
|
5661
|
+
sessionId,
|
|
5662
|
+
resumeLatest,
|
|
5663
|
+
createNewSession,
|
|
5664
|
+
correlationId,
|
|
5665
|
+
optimizePrompt,
|
|
5666
|
+
idleTimeoutMs,
|
|
5667
|
+
forceRefresh,
|
|
5668
|
+
});
|
|
5669
|
+
});
|
|
5074
5670
|
server.tool("mistral_request_async", "Start a Mistral Vibe CLI request as a durable background job. Poll with llm_job_status, collect with llm_job_result.", {
|
|
5075
5671
|
prompt: z
|
|
5076
5672
|
.string()
|
|
@@ -5097,9 +5693,9 @@ export function createGatewayServer(deps = {}) {
|
|
|
5097
5693
|
.describe("Resume most recent Vibe session in cwd (--continue)"),
|
|
5098
5694
|
createNewSession: z.boolean().default(false).describe("Force new session"),
|
|
5099
5695
|
permissionMode: z
|
|
5100
|
-
.
|
|
5696
|
+
.string()
|
|
5101
5697
|
.optional()
|
|
5102
|
-
.describe("Vibe agent
|
|
5698
|
+
.describe("Vibe --agent name. Builtins: default|plan|accept-edits|auto-approve; Vibe also accepts install-gated builtins (e.g. lean) and custom agents from ~/.vibe/agents, so any name is passed through. Defaults to auto-approve for programmatic use."),
|
|
5103
5699
|
approvalStrategy: z
|
|
5104
5700
|
.enum(["legacy", "mcp_managed"])
|
|
5105
5701
|
.default("legacy")
|
|
@@ -5109,8 +5705,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
5109
5705
|
.optional()
|
|
5110
5706
|
.describe("Approval policy override"),
|
|
5111
5707
|
mcpServers: z
|
|
5112
|
-
.array(
|
|
5113
|
-
.default([
|
|
5708
|
+
.array(mcpServerEnum())
|
|
5709
|
+
.default([])
|
|
5114
5710
|
.describe("MCP server names for approval tracking (Vibe manages its own MCP config via `vibe mcp`)"),
|
|
5115
5711
|
allowedTools: z
|
|
5116
5712
|
.array(z.string())
|
|
@@ -5439,6 +6035,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
5439
6035
|
defaultModel: null,
|
|
5440
6036
|
mode: "disabled",
|
|
5441
6037
|
},
|
|
6038
|
+
apiProviders: enabledApiProviders(providers).map(p => ({
|
|
6039
|
+
...apiProviderCatalogEntry(p),
|
|
6040
|
+
baseUrl: p.baseUrl,
|
|
6041
|
+
breakerState: apiProviderBreakerState(p.name),
|
|
6042
|
+
})),
|
|
5442
6043
|
sources: providers.sources,
|
|
5443
6044
|
};
|
|
5444
6045
|
return {
|