llm-cli-gateway 2.12.0 → 2.12.2
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 +29 -0
- package/README.md +132 -5
- package/dist/api-provider.d.ts +1 -0
- package/dist/api-provider.js +13 -0
- package/dist/async-job-manager.d.ts +34 -2
- package/dist/async-job-manager.js +344 -46
- package/dist/config.d.ts +34 -0
- package/dist/config.js +91 -0
- package/dist/doctor.d.ts +30 -2
- package/dist/doctor.js +78 -6
- package/dist/endpoint-exposure.js +15 -3
- package/dist/http-transport.d.ts +3 -0
- package/dist/http-transport.js +138 -8
- package/dist/index.d.ts +15 -0
- package/dist/index.js +221 -74
- package/dist/provider-login-guidance.d.ts +12 -0
- package/dist/provider-login-guidance.js +28 -0
- package/dist/provider-status.d.ts +12 -0
- package/dist/provider-status.js +14 -0
- package/dist/provider-tool-capabilities.d.ts +4 -1
- package/dist/provider-tool-capabilities.js +225 -11
- package/dist/resources.d.ts +6 -2
- package/dist/resources.js +72 -8
- package/dist/validation-normalizer.js +5 -4
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -1
- package/setup/status.schema.json +65 -0
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ 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, loadAcpConfig, defaultGatewayConfigPath, isXaiProviderEnabled, enabledApiProviders, minStableTokensForModel, } from "./config.js";
|
|
21
|
+
import { loadConfig, loadPersistenceConfig, loadCacheAwarenessConfig, loadProvidersConfig, loadAcpConfig, loadLimitsConfig, defaultGatewayConfigPath, isXaiProviderEnabled, enabledApiProviders, minStableTokensForModel, } from "./config.js";
|
|
22
22
|
import { runAcpRequest } from "./acp/runtime.js";
|
|
23
23
|
import { isAcpError } from "./acp/errors.js";
|
|
24
24
|
import { createApiProvider, runApiRequest, apiProviderBreakerState, } from "./api-provider.js";
|
|
@@ -26,7 +26,7 @@ import { prepareApiRequest, apiProviderCatalogEntry, ApiModelNotAllowedError, }
|
|
|
26
26
|
import { checkHealth } from "./health.js";
|
|
27
27
|
import { clearModelRegistryCache, getAvailableCliInfo, getCliInfo, resolveModelAlias, } from "./model-registry.js";
|
|
28
28
|
import { getProviderToolCapabilities } from "./provider-tool-capabilities.js";
|
|
29
|
-
import { AsyncJobManager, extractApiHttpStatus, extractApiErrorBody, } from "./async-job-manager.js";
|
|
29
|
+
import { AsyncJobManager, JobSaturationError, isAsyncJobInProgress, extractApiHttpStatus, extractApiErrorBody, } from "./async-job-manager.js";
|
|
30
30
|
import { createJobStore } from "./job-store.js";
|
|
31
31
|
import { ApprovalManager, bypassAllowedByOperator, } from "./approval-manager.js";
|
|
32
32
|
import { checkReviewIntegrity } from "./review-integrity.js";
|
|
@@ -39,6 +39,7 @@ import { getCliVersions, runCliUpgrade } from "./cli-updater.js";
|
|
|
39
39
|
import { startHttpGateway } from "./http-transport.js";
|
|
40
40
|
import { getRequestContext, resolveOwnerPrincipal, principalCanAccess } from "./request-context.js";
|
|
41
41
|
import { printDoctorJson } from "./doctor.js";
|
|
42
|
+
import { redactDiagnosticUrl } from "./endpoint-exposure.js";
|
|
42
43
|
import { createWorkspace, describeWorkspace, getWorkspace, loadWorkspaceRegistry, registerExistingWorkspace, resolveWorkspaceForProvider, validatePathInsideWorkspace, } from "./workspace-registry.js";
|
|
43
44
|
import { generateSecret, hashSecret } from "./oauth.js";
|
|
44
45
|
import { registerValidationTools } from "./validation-tools.js";
|
|
@@ -165,12 +166,13 @@ Tools: claude_request, codex_request, gemini_request, grok_request, mistral_requ
|
|
|
165
166
|
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
167
|
${jobsLine}Sessions: session_create, session_list, session_set_active, session_get, session_delete, session_clear_all
|
|
167
168
|
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
|
|
169
|
+
Workspaces: workspace_create, workspace_list, workspace_get, workspace_register_existing_repo (remote HTTP/OAuth workspace registry only; do not use workspace_* to fix stdio/local provider path access)
|
|
169
170
|
|
|
170
171
|
Key behaviors:
|
|
171
172
|
${deferralLine}
|
|
172
173
|
- 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.
|
|
173
174
|
- Approval gates: opt-in via approvalStrategy:"mcp_managed".
|
|
175
|
+
- Stdio/local provider calls may pass local workingDir/addDir/includeDirs directly. Workspace aliases are for remote HTTP/OAuth calls and must not be used as a fallback after a stdio provider path error.
|
|
174
176
|
- 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.
|
|
175
177
|
- Idle timeout kills stuck processes (default 10min, configurable via idleTimeoutMs).
|
|
176
178
|
|
|
@@ -189,6 +191,7 @@ let persistenceConfig = null;
|
|
|
189
191
|
let cacheAwarenessConfig = null;
|
|
190
192
|
let providersConfig = null;
|
|
191
193
|
let acpConfig = null;
|
|
194
|
+
let limitsConfig = null;
|
|
192
195
|
let jobStore = null;
|
|
193
196
|
let jobStoreInitialized = false;
|
|
194
197
|
let asyncJobManager = null;
|
|
@@ -201,6 +204,10 @@ function getPersistenceConfig(runtimeLogger = logger) {
|
|
|
201
204
|
persistenceConfig ??= loadPersistenceConfig(runtimeLogger);
|
|
202
205
|
return persistenceConfig;
|
|
203
206
|
}
|
|
207
|
+
function getLimitsConfig(runtimeLogger = logger) {
|
|
208
|
+
limitsConfig ??= loadLimitsConfig(runtimeLogger);
|
|
209
|
+
return limitsConfig;
|
|
210
|
+
}
|
|
204
211
|
function getAcpConfig(runtimeLogger = logger) {
|
|
205
212
|
acpConfig ??= loadAcpConfig(runtimeLogger);
|
|
206
213
|
return acpConfig;
|
|
@@ -229,7 +236,7 @@ function getJobStore(runtimeLogger = logger) {
|
|
|
229
236
|
function newAsyncJobManager(metrics, runtimeLogger, store = getJobStore(runtimeLogger), fr = getFlightRecorder(runtimeLogger)) {
|
|
230
237
|
return new AsyncJobManager(runtimeLogger, (cli, durationMs, success) => {
|
|
231
238
|
metrics.recordRequest(cli, durationMs, success);
|
|
232
|
-
}, store, fr);
|
|
239
|
+
}, store, fr, getLimitsConfig(runtimeLogger).jobs);
|
|
233
240
|
}
|
|
234
241
|
function getAsyncJobManager(runtimeLogger = logger) {
|
|
235
242
|
asyncJobManager ??= newAsyncJobManager(performanceMetrics, runtimeLogger);
|
|
@@ -278,7 +285,14 @@ export const WORKSPACE_ALIAS_SCHEMA = z
|
|
|
278
285
|
.min(1)
|
|
279
286
|
.max(64)
|
|
280
287
|
.regex(/^[A-Za-z][A-Za-z0-9._-]{0,63}$/)
|
|
281
|
-
.describe("Registered workspace alias.
|
|
288
|
+
.describe("Registered workspace alias for remote HTTP/OAuth provider calls. Stdio/local callers should not use workspace_* tools to fix provider path access; pass local workingDir/addDir/includeDirs directly.");
|
|
289
|
+
const PROVIDER_WORKSPACE_FIELD_DESCRIPTION = "Registered workspace alias for remote HTTP/OAuth provider calls. Do not use this field, workspace_list, or workspace_register_existing_repo as a fallback for stdio/local provider path access; pass workingDir/addDir/includeDirs directly instead.";
|
|
290
|
+
const LOCAL_WORKING_DIR_FIELD_SUFFIX = " Stdio/local callers may pass local paths directly. Remote HTTP/OAuth callers must use relative paths inside a selected registered workspace. Do not call workspace_* tools to fix stdio/local provider path access.";
|
|
291
|
+
const LOCAL_ADD_DIR_FIELD_SUFFIX = " Stdio/local callers may pass local paths directly. Remote HTTP/OAuth callers must use relative paths inside a selected registered workspace. Do not call workspace_* tools to fix stdio/local provider path access.";
|
|
292
|
+
const LOCAL_INCLUDE_DIRS_FIELD_SUFFIX = " Stdio/local callers may pass local paths directly. Remote HTTP/OAuth callers must use relative paths inside a selected registered workspace. Do not call workspace_* tools to fix stdio/local provider path access.";
|
|
293
|
+
function providerWorkspaceAliasSchema() {
|
|
294
|
+
return WORKSPACE_ALIAS_SCHEMA.describe(PROVIDER_WORKSPACE_FIELD_DESCRIPTION).optional();
|
|
295
|
+
}
|
|
282
296
|
export const SESSION_PROVIDER_VALUES = PROVIDER_TYPES;
|
|
283
297
|
export const SESSION_PROVIDER_ENUM = z.enum(SESSION_PROVIDER_VALUES);
|
|
284
298
|
export function sessionProviderValuesFor(providers) {
|
|
@@ -307,7 +321,7 @@ export function resolveGatewayServerRuntime(deps = {}, options = {}) {
|
|
|
307
321
|
sessionManager: runtimeSessionManager,
|
|
308
322
|
resourceProvider: deps.resourceProvider ??
|
|
309
323
|
(options.isolateState
|
|
310
|
-
? new ResourceProvider(runtimeSessionManager, runtimePerformanceMetrics, runtimeFlightRecorder, deps.cacheAwareness ?? getCacheAwarenessConfig(runtimeLogger))
|
|
324
|
+
? new ResourceProvider(runtimeSessionManager, runtimePerformanceMetrics, runtimeFlightRecorder, deps.cacheAwareness ?? getCacheAwarenessConfig(runtimeLogger), deps.providers ?? getProvidersConfig(runtimeLogger))
|
|
311
325
|
: resourceProvider),
|
|
312
326
|
db: "db" in deps ? (deps.db ?? null) : db,
|
|
313
327
|
performanceMetrics: runtimePerformanceMetrics,
|
|
@@ -402,6 +416,14 @@ async function awaitJobOrDefer(cli, args, corrId, idleTimeoutMs, outputFormat, f
|
|
|
402
416
|
runtime.asyncJobManager.hasStore();
|
|
403
417
|
if (SYNC_DEADLINE_MS === 0 || !deferralAvailable) {
|
|
404
418
|
const command = providerCommandName(cli);
|
|
419
|
+
let slot;
|
|
420
|
+
try {
|
|
421
|
+
slot = await runtime.asyncJobManager.acquireProcessSlot(cli);
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
consumeOnComplete();
|
|
425
|
+
throw err;
|
|
426
|
+
}
|
|
405
427
|
try {
|
|
406
428
|
return await executeCli(command, args, {
|
|
407
429
|
idleTimeout: idleTimeoutMs,
|
|
@@ -412,6 +434,7 @@ async function awaitJobOrDefer(cli, args, corrId, idleTimeoutMs, outputFormat, f
|
|
|
412
434
|
});
|
|
413
435
|
}
|
|
414
436
|
finally {
|
|
437
|
+
slot.release();
|
|
415
438
|
consumeOnComplete();
|
|
416
439
|
}
|
|
417
440
|
}
|
|
@@ -441,7 +464,7 @@ async function awaitJobOrDefer(cli, args, corrId, idleTimeoutMs, outputFormat, f
|
|
|
441
464
|
const deadline = Date.now() + SYNC_DEADLINE_MS;
|
|
442
465
|
while (Date.now() < deadline) {
|
|
443
466
|
const snapshot = runtime.asyncJobManager.getJobSnapshot(job.id);
|
|
444
|
-
if (snapshot && snapshot.status
|
|
467
|
+
if (snapshot && !isAsyncJobInProgress(snapshot.status)) {
|
|
445
468
|
const result = runtime.asyncJobManager.getJobResult(job.id);
|
|
446
469
|
if (!result) {
|
|
447
470
|
return { stdout: "", stderr: "Job result unavailable", code: 1 };
|
|
@@ -481,6 +504,14 @@ async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolv
|
|
|
481
504
|
runtime.persistence.asyncJobsEnabled &&
|
|
482
505
|
runtime.asyncJobManager.hasStore();
|
|
483
506
|
if (SYNC_DEADLINE_MS === 0 || !deferralAvailable || forceInline) {
|
|
507
|
+
let slot;
|
|
508
|
+
try {
|
|
509
|
+
slot = await runtime.asyncJobManager.acquireProcessSlot(provider.name);
|
|
510
|
+
}
|
|
511
|
+
catch (err) {
|
|
512
|
+
consumeOnComplete();
|
|
513
|
+
throw err;
|
|
514
|
+
}
|
|
484
515
|
try {
|
|
485
516
|
const result = await runApiRequest(provider, apiRequest, runtime.logger);
|
|
486
517
|
return {
|
|
@@ -504,6 +535,7 @@ async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolv
|
|
|
504
535
|
};
|
|
505
536
|
}
|
|
506
537
|
finally {
|
|
538
|
+
slot.release();
|
|
507
539
|
consumeOnComplete();
|
|
508
540
|
}
|
|
509
541
|
}
|
|
@@ -531,7 +563,7 @@ async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolv
|
|
|
531
563
|
const deadline = Date.now() + SYNC_DEADLINE_MS;
|
|
532
564
|
while (Date.now() < deadline) {
|
|
533
565
|
const snapshot = runtime.asyncJobManager.getJobSnapshot(job.id);
|
|
534
|
-
if (snapshot && snapshot.status
|
|
566
|
+
if (snapshot && !isAsyncJobInProgress(snapshot.status)) {
|
|
535
567
|
const result = runtime.asyncJobManager.getJobResult(job.id);
|
|
536
568
|
if (!result)
|
|
537
569
|
return { stdout: "", stderr: "Job result unavailable", code: 1 };
|
|
@@ -678,38 +710,50 @@ function workspaceAdminEnabled() {
|
|
|
678
710
|
const scopes = getRequestContext()?.authScopes ?? [];
|
|
679
711
|
return process.env.LLM_GATEWAY_WORKSPACE_ADMIN === "1" && scopes.includes("workspace:admin");
|
|
680
712
|
}
|
|
713
|
+
function assertWorkspaceToolCaller(toolName) {
|
|
714
|
+
const context = getRequestContext();
|
|
715
|
+
if (context?.transport === "http" || context?.authKind === "oauth")
|
|
716
|
+
return;
|
|
717
|
+
throw new Error(`${toolName} is only for remote HTTP/OAuth workspace clients. Stdio/local provider calls must not use workspace_* tools for path access; pass workingDir/addDir/includeDirs directly on the provider request instead.`);
|
|
718
|
+
}
|
|
681
719
|
function registerWorkspaceTools(server, runtime) {
|
|
682
|
-
server.tool("workspace_list", "List registered workspace aliases
|
|
720
|
+
server.tool("workspace_list", "List registered workspace aliases for remote HTTP/OAuth provider calls. Does not browse files. Stdio/local callers should not use workspace_* tools to fix provider path access; pass local workingDir/addDir/includeDirs directly.", {}, {
|
|
683
721
|
title: "List workspaces",
|
|
684
722
|
readOnlyHint: true,
|
|
685
723
|
destructiveHint: false,
|
|
686
724
|
idempotentHint: true,
|
|
687
725
|
openWorldHint: false,
|
|
688
726
|
}, async () => {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
727
|
+
try {
|
|
728
|
+
assertWorkspaceToolCaller("workspace_list");
|
|
729
|
+
const registry = loadWorkspaceRegistry(runtime.logger);
|
|
730
|
+
return {
|
|
731
|
+
content: [
|
|
732
|
+
{
|
|
733
|
+
type: "text",
|
|
734
|
+
text: JSON.stringify({
|
|
735
|
+
success: true,
|
|
736
|
+
enabled: registry.enabled,
|
|
737
|
+
default: registry.defaultAlias,
|
|
738
|
+
workspaces: registry.repos.map(describeWorkspace),
|
|
739
|
+
allowed_roots: registry.allowedRoots.map(root => ({
|
|
740
|
+
alias: root.alias,
|
|
741
|
+
path: root.path,
|
|
742
|
+
allow_register_existing_git_repos: root.allowRegisterExistingGitRepos,
|
|
743
|
+
allow_create_directories: root.allowCreateDirectories,
|
|
744
|
+
allow_init_git_repos: root.allowInitGitRepos,
|
|
745
|
+
max_create_depth: root.maxCreateDepth,
|
|
746
|
+
})),
|
|
747
|
+
}, null, 2),
|
|
748
|
+
},
|
|
749
|
+
],
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
catch (error) {
|
|
753
|
+
return createErrorResponse("workspace_list", 1, "", undefined, error);
|
|
754
|
+
}
|
|
711
755
|
});
|
|
712
|
-
server.tool("workspace_get", "Inspect a registered workspace alias. Does not list files.", { alias: WORKSPACE_ALIAS_SCHEMA }, {
|
|
756
|
+
server.tool("workspace_get", "Inspect a registered remote HTTP/OAuth workspace alias. Does not list files. Not needed for stdio/local provider calls; do not use workspace_* tools to fix local path access.", { alias: WORKSPACE_ALIAS_SCHEMA }, {
|
|
713
757
|
title: "Get workspace",
|
|
714
758
|
readOnlyHint: true,
|
|
715
759
|
destructiveHint: false,
|
|
@@ -717,6 +761,7 @@ function registerWorkspaceTools(server, runtime) {
|
|
|
717
761
|
openWorldHint: false,
|
|
718
762
|
}, async ({ alias }) => {
|
|
719
763
|
try {
|
|
764
|
+
assertWorkspaceToolCaller("workspace_get");
|
|
720
765
|
const registry = loadWorkspaceRegistry(runtime.logger);
|
|
721
766
|
return {
|
|
722
767
|
content: [
|
|
@@ -731,7 +776,7 @@ function registerWorkspaceTools(server, runtime) {
|
|
|
731
776
|
return createErrorResponse("workspace_get", 1, "", undefined, error);
|
|
732
777
|
}
|
|
733
778
|
});
|
|
734
|
-
server.tool("workspace_create", "Create a new local folder or git repo under a configured allowed root. Requires LLM_GATEWAY_WORKSPACE_ADMIN=1 and OAuth scope workspace:admin.", {
|
|
779
|
+
server.tool("workspace_create", "Create a remote HTTP/OAuth workspace alias by creating a new local folder or git repo under a configured allowed root. Not for stdio/local provider path access. Requires LLM_GATEWAY_WORKSPACE_ADMIN=1 and OAuth scope workspace:admin.", {
|
|
735
780
|
alias: WORKSPACE_ALIAS_SCHEMA,
|
|
736
781
|
root: WORKSPACE_ALIAS_SCHEMA.describe("Allowed-root alias from workspace_list."),
|
|
737
782
|
slug: z.string().min(1).max(255).describe("Safe relative path under the allowed root."),
|
|
@@ -745,6 +790,7 @@ function registerWorkspaceTools(server, runtime) {
|
|
|
745
790
|
openWorldHint: false,
|
|
746
791
|
}, async ({ alias, root, slug, kind, setDefault }) => {
|
|
747
792
|
try {
|
|
793
|
+
assertWorkspaceToolCaller("workspace_create");
|
|
748
794
|
if (!workspaceAdminEnabled()) {
|
|
749
795
|
throw new Error("workspace_create requires LLM_GATEWAY_WORKSPACE_ADMIN=1 and OAuth scope workspace:admin");
|
|
750
796
|
}
|
|
@@ -769,7 +815,7 @@ function registerWorkspaceTools(server, runtime) {
|
|
|
769
815
|
return createErrorResponse("workspace_create", 1, "", undefined, error);
|
|
770
816
|
}
|
|
771
817
|
});
|
|
772
|
-
server.tool("workspace_register_existing_repo", "Register an existing local Git repo
|
|
818
|
+
server.tool("workspace_register_existing_repo", "Register an existing local Git repo as a remote HTTP/OAuth workspace alias. Not for stdio/local provider path access. Requires LLM_GATEWAY_WORKSPACE_ADMIN=1 and OAuth scope workspace:admin.", {
|
|
773
819
|
alias: WORKSPACE_ALIAS_SCHEMA,
|
|
774
820
|
path: z
|
|
775
821
|
.string()
|
|
@@ -784,6 +830,7 @@ function registerWorkspaceTools(server, runtime) {
|
|
|
784
830
|
openWorldHint: false,
|
|
785
831
|
}, async ({ alias, path, setDefault }) => {
|
|
786
832
|
try {
|
|
833
|
+
assertWorkspaceToolCaller("workspace_register_existing_repo");
|
|
787
834
|
if (!workspaceAdminEnabled()) {
|
|
788
835
|
throw new Error("workspace_register_existing_repo requires LLM_GATEWAY_WORKSPACE_ADMIN=1 and OAuth scope workspace:admin");
|
|
789
836
|
}
|
|
@@ -808,6 +855,23 @@ function registerWorkspaceTools(server, runtime) {
|
|
|
808
855
|
});
|
|
809
856
|
}
|
|
810
857
|
export function createErrorResponse(cli, code, stderr, correlationId, error, apiError) {
|
|
858
|
+
if (error instanceof JobSaturationError) {
|
|
859
|
+
const satMessage = `${error.message}. The gateway is protecting the host from process/request ` +
|
|
860
|
+
`overload; this is transient. Retry after a short delay, or reduce concurrent requests.`;
|
|
861
|
+
logger.error(`[${correlationId || "unknown"}] ${cli} rejected: gateway at capacity`);
|
|
862
|
+
return {
|
|
863
|
+
content: [{ type: "text", text: satMessage }],
|
|
864
|
+
isError: true,
|
|
865
|
+
structuredContent: {
|
|
866
|
+
response: satMessage,
|
|
867
|
+
correlationId: correlationId || null,
|
|
868
|
+
cli,
|
|
869
|
+
exitCode: code,
|
|
870
|
+
errorCategory: "saturated",
|
|
871
|
+
retryable: true,
|
|
872
|
+
},
|
|
873
|
+
};
|
|
874
|
+
}
|
|
811
875
|
let errorMessage = `Error executing ${cli} CLI`;
|
|
812
876
|
const isLaunchExit = code === 127 || code === -4058;
|
|
813
877
|
const isOutputOverflow = Boolean(error) && /Output exceeded maximum size/i.test(error.message);
|
|
@@ -4162,7 +4226,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
4162
4226
|
addDir: z
|
|
4163
4227
|
.array(z.string())
|
|
4164
4228
|
.optional()
|
|
4165
|
-
.describe("Claude --add-dir: additional directories the CLI is allowed to read/write beyond the process cwd. Each entry is emitted as its own --add-dir instance."
|
|
4229
|
+
.describe("Claude --add-dir: additional directories the CLI is allowed to read/write beyond the process cwd. Each entry is emitted as its own --add-dir instance." +
|
|
4230
|
+
LOCAL_ADD_DIR_FIELD_SUFFIX),
|
|
4166
4231
|
noSessionPersistence: z
|
|
4167
4232
|
.boolean()
|
|
4168
4233
|
.optional()
|
|
@@ -4181,7 +4246,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4181
4246
|
.array(z.string())
|
|
4182
4247
|
.optional()
|
|
4183
4248
|
.describe('Claude --tools: restrict the available built-in tool set (distinct from allowedTools permission gating). Pass [""] to disable all tools.'),
|
|
4184
|
-
workspace:
|
|
4249
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
4185
4250
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
4186
4251
|
approvalStrategy: z
|
|
4187
4252
|
.enum(["legacy", "mcp_managed"])
|
|
@@ -4525,12 +4590,14 @@ export function createGatewayServer(deps = {}) {
|
|
|
4525
4590
|
.string()
|
|
4526
4591
|
.min(1)
|
|
4527
4592
|
.optional()
|
|
4528
|
-
.describe("Codex -C/--cd <DIR>: working root for this session. Emitted on new sessions only; resume inherits the original session's cwd via CODEX_RESUME_FILTERED_FLAGS."
|
|
4593
|
+
.describe("Codex -C/--cd <DIR>: working root for this session. Emitted on new sessions only; resume inherits the original session's cwd via CODEX_RESUME_FILTERED_FLAGS." +
|
|
4594
|
+
LOCAL_WORKING_DIR_FIELD_SUFFIX),
|
|
4529
4595
|
addDir: z
|
|
4530
4596
|
.array(z.string())
|
|
4531
4597
|
.optional()
|
|
4532
|
-
.describe("Codex --add-dir <DIR>: additional writable workspace directories. Emitted once per entry on new sessions only; resume inherits the original session's writable-dir policy."
|
|
4533
|
-
|
|
4598
|
+
.describe("Codex --add-dir <DIR>: additional writable workspace directories. Emitted once per entry on new sessions only; resume inherits the original session's writable-dir policy." +
|
|
4599
|
+
LOCAL_ADD_DIR_FIELD_SUFFIX),
|
|
4600
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
4534
4601
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
4535
4602
|
}, {
|
|
4536
4603
|
title: "Codex request",
|
|
@@ -4729,7 +4796,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4729
4796
|
.max(3_600_000)
|
|
4730
4797
|
.optional()
|
|
4731
4798
|
.describe("Idle timeout in ms (min 30s, max 1h, omit=CLI default)"),
|
|
4732
|
-
workspace:
|
|
4799
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
4733
4800
|
}, {
|
|
4734
4801
|
title: "Fork Codex session",
|
|
4735
4802
|
readOnlyHint: false,
|
|
@@ -4853,7 +4920,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4853
4920
|
includeDirs: z
|
|
4854
4921
|
.array(z.string())
|
|
4855
4922
|
.optional()
|
|
4856
|
-
.describe("Additional workspace directories passed as --add-dir"),
|
|
4923
|
+
.describe("Additional workspace directories passed as --add-dir." + LOCAL_INCLUDE_DIRS_FIELD_SUFFIX),
|
|
4857
4924
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
4858
4925
|
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
4859
4926
|
optimizeResponse: z.boolean().default(false).describe("Optimize response output"),
|
|
@@ -4893,7 +4960,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
4893
4960
|
.boolean()
|
|
4894
4961
|
.optional()
|
|
4895
4962
|
.describe("Antigravity 1.0.13 --new-project: create a new project for this session. Mutually exclusive with project."),
|
|
4896
|
-
workspace:
|
|
4963
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
4897
4964
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
4898
4965
|
}, {
|
|
4899
4966
|
title: "Gemini request",
|
|
@@ -5015,7 +5082,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5015
5082
|
.union([z.string().min(1), z.record(z.string(), z.unknown())])
|
|
5016
5083
|
.optional()
|
|
5017
5084
|
.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."),
|
|
5018
|
-
workspace:
|
|
5085
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
5019
5086
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5020
5087
|
}, {
|
|
5021
5088
|
title: "Grok request",
|
|
@@ -5226,12 +5293,14 @@ export function createGatewayServer(deps = {}) {
|
|
|
5226
5293
|
.string()
|
|
5227
5294
|
.min(1)
|
|
5228
5295
|
.optional()
|
|
5229
|
-
.describe("Vibe --workdir <DIR>: change to this directory before running. Single value (Vibe accepts one --workdir per invocation)."
|
|
5296
|
+
.describe("Vibe --workdir <DIR>: change to this directory before running. Single value (Vibe accepts one --workdir per invocation)." +
|
|
5297
|
+
LOCAL_WORKING_DIR_FIELD_SUFFIX),
|
|
5230
5298
|
addDir: z
|
|
5231
5299
|
.array(z.string())
|
|
5232
5300
|
.optional()
|
|
5233
|
-
.describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance (Vibe states this flag may be specified multiple times)."
|
|
5234
|
-
|
|
5301
|
+
.describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance (Vibe states this flag may be specified multiple times)." +
|
|
5302
|
+
LOCAL_ADD_DIR_FIELD_SUFFIX),
|
|
5303
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
5235
5304
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5236
5305
|
}, {
|
|
5237
5306
|
title: "Mistral Vibe request",
|
|
@@ -5360,7 +5429,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
5360
5429
|
addDir: z
|
|
5361
5430
|
.array(z.string())
|
|
5362
5431
|
.optional()
|
|
5363
|
-
.describe("Claude --add-dir: additional directories the CLI is allowed to read/write beyond the process cwd. Each entry is emitted as its own --add-dir instance."
|
|
5432
|
+
.describe("Claude --add-dir: additional directories the CLI is allowed to read/write beyond the process cwd. Each entry is emitted as its own --add-dir instance." +
|
|
5433
|
+
LOCAL_ADD_DIR_FIELD_SUFFIX),
|
|
5364
5434
|
noSessionPersistence: z
|
|
5365
5435
|
.boolean()
|
|
5366
5436
|
.optional()
|
|
@@ -5379,7 +5449,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5379
5449
|
.array(z.string())
|
|
5380
5450
|
.optional()
|
|
5381
5451
|
.describe('Claude --tools: restrict the available built-in tool set (distinct from allowedTools permission gating). Pass [""] to disable all tools.'),
|
|
5382
|
-
workspace:
|
|
5452
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
5383
5453
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5384
5454
|
approvalStrategy: z
|
|
5385
5455
|
.enum(["legacy", "mcp_managed"])
|
|
@@ -5629,12 +5699,14 @@ export function createGatewayServer(deps = {}) {
|
|
|
5629
5699
|
.string()
|
|
5630
5700
|
.min(1)
|
|
5631
5701
|
.optional()
|
|
5632
|
-
.describe("Codex -C/--cd <DIR>: working root for this session. New sessions only; resume inherits the original session's cwd."
|
|
5702
|
+
.describe("Codex -C/--cd <DIR>: working root for this session. New sessions only; resume inherits the original session's cwd." +
|
|
5703
|
+
LOCAL_WORKING_DIR_FIELD_SUFFIX),
|
|
5633
5704
|
addDir: z
|
|
5634
5705
|
.array(z.string())
|
|
5635
5706
|
.optional()
|
|
5636
|
-
.describe("Codex --add-dir <DIR>: additional writable workspace directories (repeat per entry). New sessions only."
|
|
5637
|
-
|
|
5707
|
+
.describe("Codex --add-dir <DIR>: additional writable workspace directories (repeat per entry). New sessions only." +
|
|
5708
|
+
LOCAL_ADD_DIR_FIELD_SUFFIX),
|
|
5709
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
5638
5710
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5639
5711
|
}, {
|
|
5640
5712
|
title: "Codex request (async job)",
|
|
@@ -5721,7 +5793,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
5721
5793
|
includeDirs: z
|
|
5722
5794
|
.array(z.string())
|
|
5723
5795
|
.optional()
|
|
5724
|
-
.describe("Additional workspace directories passed as --add-dir"
|
|
5796
|
+
.describe("Additional workspace directories passed as --add-dir." +
|
|
5797
|
+
LOCAL_INCLUDE_DIRS_FIELD_SUFFIX),
|
|
5725
5798
|
correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
|
|
5726
5799
|
optimizePrompt: z.boolean().default(false).describe("Optimize prompt before execution"),
|
|
5727
5800
|
idleTimeoutMs: z
|
|
@@ -5760,7 +5833,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5760
5833
|
.boolean()
|
|
5761
5834
|
.optional()
|
|
5762
5835
|
.describe("Antigravity 1.0.13 --new-project: create a new project for this session. Mutually exclusive with project."),
|
|
5763
|
-
workspace:
|
|
5836
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
5764
5837
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5765
5838
|
}, {
|
|
5766
5839
|
title: "Gemini request (async job)",
|
|
@@ -5872,7 +5945,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
5872
5945
|
.string()
|
|
5873
5946
|
.min(1)
|
|
5874
5947
|
.optional()
|
|
5875
|
-
.describe("Grok --cwd <DIR>: working directory for this invocation. Lets headless callers run Grok against a directory other than the gateway process's cwd."
|
|
5948
|
+
.describe("Grok --cwd <DIR>: working directory for this invocation. Lets headless callers run Grok against a directory other than the gateway process's cwd." +
|
|
5949
|
+
LOCAL_WORKING_DIR_FIELD_SUFFIX),
|
|
5876
5950
|
sandbox: z
|
|
5877
5951
|
.string()
|
|
5878
5952
|
.min(1)
|
|
@@ -5988,7 +6062,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
5988
6062
|
.union([z.string().min(1), z.record(z.string(), z.unknown())])
|
|
5989
6063
|
.optional()
|
|
5990
6064
|
.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."),
|
|
5991
|
-
workspace:
|
|
6065
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
5992
6066
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
5993
6067
|
}, {
|
|
5994
6068
|
title: "Grok request (async job)",
|
|
@@ -6185,12 +6259,14 @@ export function createGatewayServer(deps = {}) {
|
|
|
6185
6259
|
.string()
|
|
6186
6260
|
.min(1)
|
|
6187
6261
|
.optional()
|
|
6188
|
-
.describe("Vibe --workdir <DIR>: change to this directory before running. Single value per invocation."
|
|
6262
|
+
.describe("Vibe --workdir <DIR>: change to this directory before running. Single value per invocation." +
|
|
6263
|
+
LOCAL_WORKING_DIR_FIELD_SUFFIX),
|
|
6189
6264
|
addDir: z
|
|
6190
6265
|
.array(z.string())
|
|
6191
6266
|
.optional()
|
|
6192
|
-
.describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance."
|
|
6193
|
-
|
|
6267
|
+
.describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance." +
|
|
6268
|
+
LOCAL_ADD_DIR_FIELD_SUFFIX),
|
|
6269
|
+
workspace: providerWorkspaceAliasSchema(),
|
|
6194
6270
|
worktree: WORKTREE_SCHEMA.optional(),
|
|
6195
6271
|
}, {
|
|
6196
6272
|
title: "Mistral Vibe request (async job)",
|
|
@@ -6227,7 +6303,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6227
6303
|
worktree,
|
|
6228
6304
|
});
|
|
6229
6305
|
});
|
|
6230
|
-
server.tool("llm_job_status", "Check lifecycle status (running|completed|failed|canceled|orphaned) of a gateway async or deferred-sync job by jobId.", {
|
|
6306
|
+
server.tool("llm_job_status", "Check lifecycle status (queued|running|completed|failed|canceled|orphaned) of a gateway async or deferred-sync job by jobId.", {
|
|
6231
6307
|
jobId: z.string().describe("Async job ID from *_request_async"),
|
|
6232
6308
|
}, {
|
|
6233
6309
|
title: "Async job status",
|
|
@@ -6447,18 +6523,22 @@ export function createGatewayServer(deps = {}) {
|
|
|
6447
6523
|
openWorldHint: false,
|
|
6448
6524
|
}, async () => {
|
|
6449
6525
|
const health = asyncJobManager.getJobHealth();
|
|
6526
|
+
const storeAttached = asyncJobManager.hasStore();
|
|
6527
|
+
const asyncJobsEffective = persistence.backend !== "none" && persistence.asyncJobsEnabled && storeAttached;
|
|
6450
6528
|
const persistenceBlock = {
|
|
6451
6529
|
backend: persistence.backend,
|
|
6452
6530
|
dbPath: persistence.path,
|
|
6453
6531
|
dsn: persistence.dsn ? "[redacted]" : null,
|
|
6454
6532
|
retentionDays: persistence.retentionDays,
|
|
6455
6533
|
dedupWindowMs: persistence.dedupWindowMs,
|
|
6456
|
-
asyncJobsEnabled:
|
|
6534
|
+
asyncJobsEnabled: asyncJobsEffective,
|
|
6457
6535
|
acknowledgeEphemeral: persistence.acknowledgeEphemeral,
|
|
6458
6536
|
sources: persistence.sources,
|
|
6459
|
-
warning:
|
|
6537
|
+
warning: asyncJobsEffective
|
|
6460
6538
|
? null
|
|
6461
|
-
:
|
|
6539
|
+
: persistence.backend === "none"
|
|
6540
|
+
? "Async job persistence is disabled (backend = 'none'). *_request_async tools are NOT registered on this gateway. Set [persistence].backend = 'sqlite' (or 'memory' + acknowledgeEphemeral = true) to enable them."
|
|
6541
|
+
: `Async job persistence is configured (backend = '${persistence.backend}') but the durable job store failed to open, so *_request_async tools are NOT registered on this gateway. Check gateway startup logs for the store-open error.`,
|
|
6462
6542
|
};
|
|
6463
6543
|
const outboundProviders = {
|
|
6464
6544
|
xai: providers.xai
|
|
@@ -6467,7 +6547,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6467
6547
|
enabled: isXaiProviderEnabled(providers),
|
|
6468
6548
|
apiKeyEnv: providers.xai.apiKeyEnv,
|
|
6469
6549
|
apiKeyPresent: isXaiProviderEnabled(providers),
|
|
6470
|
-
baseUrl: providers.xai.baseUrl,
|
|
6550
|
+
baseUrl: redactDiagnosticUrl(providers.xai.baseUrl) ?? providers.xai.baseUrl,
|
|
6471
6551
|
defaultModel: providers.xai.defaultModel,
|
|
6472
6552
|
mode: isXaiProviderEnabled(providers) ? "sync" : "configured-missing-key",
|
|
6473
6553
|
}
|
|
@@ -6482,16 +6562,59 @@ export function createGatewayServer(deps = {}) {
|
|
|
6482
6562
|
},
|
|
6483
6563
|
apiProviders: enabledApiProviders(providers).map(p => ({
|
|
6484
6564
|
...apiProviderCatalogEntry(p),
|
|
6485
|
-
baseUrl: p.baseUrl,
|
|
6565
|
+
baseUrl: redactDiagnosticUrl(p.baseUrl) ?? p.baseUrl,
|
|
6486
6566
|
breakerState: apiProviderBreakerState(p.name),
|
|
6487
6567
|
})),
|
|
6488
6568
|
sources: providers.sources,
|
|
6489
6569
|
};
|
|
6570
|
+
const limiter = asyncJobManager.getLimiterSnapshot();
|
|
6571
|
+
const jobLimits = asyncJobManager.getConfiguredLimits();
|
|
6572
|
+
const httpLimits = getLimitsConfig().http;
|
|
6573
|
+
const mem = process.memoryUsage();
|
|
6574
|
+
const backpressure = {
|
|
6575
|
+
jobs: {
|
|
6576
|
+
running: limiter.running,
|
|
6577
|
+
queued: limiter.queued,
|
|
6578
|
+
runningByProvider: limiter.runningByProvider,
|
|
6579
|
+
queuedByProvider: limiter.queuedByProvider,
|
|
6580
|
+
maxRunning: limiter.maxRunning,
|
|
6581
|
+
maxRunningPerProvider: limiter.maxRunningPerProvider,
|
|
6582
|
+
maxQueued: limiter.maxQueued,
|
|
6583
|
+
rejected: limiter.rejected,
|
|
6584
|
+
timedOut: limiter.timedOut,
|
|
6585
|
+
saturated: limiter.saturated,
|
|
6586
|
+
completedJobMemoryTtlMs: jobLimits.completedJobMemoryTtlMs,
|
|
6587
|
+
maxJobOutputBytes: jobLimits.maxJobOutputBytes,
|
|
6588
|
+
},
|
|
6589
|
+
httpSessions: activeHttpGateway
|
|
6590
|
+
? { active: true, ...activeHttpGateway.sessionHealth() }
|
|
6591
|
+
: {
|
|
6592
|
+
active: false,
|
|
6593
|
+
current: 0,
|
|
6594
|
+
max: httpLimits.maxSessions,
|
|
6595
|
+
oldestAgeMs: 0,
|
|
6596
|
+
idleTtlMs: httpLimits.sessionIdleTtlMs,
|
|
6597
|
+
reaperIntervalMs: httpLimits.sessionReaperIntervalMs,
|
|
6598
|
+
saturated: false,
|
|
6599
|
+
},
|
|
6600
|
+
memory: {
|
|
6601
|
+
rss: mem.rss,
|
|
6602
|
+
heapUsed: mem.heapUsed,
|
|
6603
|
+
heapTotal: mem.heapTotal,
|
|
6604
|
+
external: mem.external,
|
|
6605
|
+
},
|
|
6606
|
+
};
|
|
6490
6607
|
return {
|
|
6491
6608
|
content: [
|
|
6492
6609
|
{
|
|
6493
6610
|
type: "text",
|
|
6494
|
-
text: JSON.stringify({
|
|
6611
|
+
text: JSON.stringify({
|
|
6612
|
+
success: true,
|
|
6613
|
+
...health,
|
|
6614
|
+
backpressure,
|
|
6615
|
+
persistence: persistenceBlock,
|
|
6616
|
+
outboundProviders,
|
|
6617
|
+
}, null, 2),
|
|
6495
6618
|
},
|
|
6496
6619
|
],
|
|
6497
6620
|
};
|
|
@@ -6529,10 +6652,13 @@ export function createGatewayServer(deps = {}) {
|
|
|
6529
6652
|
],
|
|
6530
6653
|
};
|
|
6531
6654
|
});
|
|
6532
|
-
|
|
6655
|
+
const listModelsFilterValues = [
|
|
6656
|
+
...new Set([...CLI_TYPES, ...enabledApiProviders(providers).map(p => p.name)]),
|
|
6657
|
+
];
|
|
6658
|
+
server.tool("list_models", "List models, aliases, and defaults for one provider (claude|codex|gemini|grok|mistral|devin, or an enabled API provider name), or omit cli to list all providers. API providers are returned under an `apiProviders` array.", {
|
|
6533
6659
|
cli: z
|
|
6534
|
-
.preprocess(value => (value === "" || value === null ? undefined : value),
|
|
6535
|
-
.describe("
|
|
6660
|
+
.preprocess(value => (value === "" || value === null ? undefined : value), z.enum(listModelsFilterValues).optional())
|
|
6661
|
+
.describe("Provider filter (claude|codex|gemini|grok|mistral|devin, or an enabled API provider name)"),
|
|
6536
6662
|
}, {
|
|
6537
6663
|
title: "Provider models",
|
|
6538
6664
|
readOnlyHint: true,
|
|
@@ -6541,13 +6667,32 @@ export function createGatewayServer(deps = {}) {
|
|
|
6541
6667
|
openWorldHint: false,
|
|
6542
6668
|
}, async ({ cli }) => {
|
|
6543
6669
|
const cliInfo = getAvailableCliInfo();
|
|
6544
|
-
const
|
|
6670
|
+
const apiRuntimes = enabledApiProviders(providers);
|
|
6671
|
+
let result;
|
|
6672
|
+
if (cli && CLI_TYPES.includes(cli)) {
|
|
6673
|
+
result = { [cli]: cliInfo[cli] };
|
|
6674
|
+
}
|
|
6675
|
+
else if (cli) {
|
|
6676
|
+
const runtime = apiRuntimes.find(p => p.name === cli);
|
|
6677
|
+
result = { apiProviders: runtime ? [apiProviderCatalogEntry(runtime)] : [] };
|
|
6678
|
+
}
|
|
6679
|
+
else {
|
|
6680
|
+
const apiProviders = apiRuntimes.map(apiProviderCatalogEntry);
|
|
6681
|
+
result = { ...cliInfo, ...(apiProviders.length > 0 ? { apiProviders } : {}) };
|
|
6682
|
+
}
|
|
6545
6683
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
6546
6684
|
});
|
|
6547
|
-
|
|
6685
|
+
const providerToolCapabilitiesFilterValues = [
|
|
6686
|
+
...new Set([
|
|
6687
|
+
...CLI_TYPES,
|
|
6688
|
+
"grok_api",
|
|
6689
|
+
...enabledApiProviders(providers).map(p => p.name),
|
|
6690
|
+
]),
|
|
6691
|
+
];
|
|
6692
|
+
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, or an enabled API provider name.", {
|
|
6548
6693
|
cli: z
|
|
6549
|
-
.preprocess(value => (value === "" || value === null ? undefined : value), z.enum(
|
|
6550
|
-
.describe("Provider filter (claude|codex|gemini|grok|mistral|devin|grok_api)"),
|
|
6694
|
+
.preprocess(value => (value === "" || value === null ? undefined : value), z.enum(providerToolCapabilitiesFilterValues).optional())
|
|
6695
|
+
.describe("Provider filter (claude|codex|gemini|grok|mistral|devin|grok_api, or an enabled API provider name)"),
|
|
6551
6696
|
includeSkills: z
|
|
6552
6697
|
.boolean()
|
|
6553
6698
|
.default(true)
|
|
@@ -6579,6 +6724,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
6579
6724
|
includeUnsupported,
|
|
6580
6725
|
includePaths,
|
|
6581
6726
|
refresh,
|
|
6727
|
+
providersConfig: providers,
|
|
6582
6728
|
});
|
|
6583
6729
|
return { content: [{ type: "text", text: JSON.stringify(capabilities, null, 2) }] };
|
|
6584
6730
|
});
|
|
@@ -7124,7 +7270,7 @@ async function initializeSessionManager() {
|
|
|
7124
7270
|
});
|
|
7125
7271
|
logger.info("File-based session manager initialized");
|
|
7126
7272
|
}
|
|
7127
|
-
resourceProvider = new ResourceProvider(sessionManager, performanceMetrics, getFlightRecorder(logger), getCacheAwarenessConfig(logger));
|
|
7273
|
+
resourceProvider = new ResourceProvider(sessionManager, performanceMetrics, getFlightRecorder(logger), getCacheAwarenessConfig(logger), getProvidersConfig(logger));
|
|
7128
7274
|
}
|
|
7129
7275
|
function registerHealthResource(server) {
|
|
7130
7276
|
if (db) {
|
|
@@ -7430,7 +7576,8 @@ async function main() {
|
|
|
7430
7576
|
if (args[0] === "doctor") {
|
|
7431
7577
|
if (args.includes("--json")) {
|
|
7432
7578
|
const probeUpstream = args.includes("--probe-upstream") || args.includes("--probe-installed");
|
|
7433
|
-
|
|
7579
|
+
const probeApiProviders = args.includes("--probe-api-providers");
|
|
7580
|
+
await printDoctorJson({ probeUpstream, probeApiProviders });
|
|
7434
7581
|
return;
|
|
7435
7582
|
}
|
|
7436
7583
|
process.stderr.write("Only doctor --json is supported in this layer.\n");
|