@mcoda/codali 0.1.133 → 0.1.135-codali.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 +37 -0
- package/ORCHESTRATION.md +387 -0
- package/README.md +103 -1
- package/config/alternate-local.example.json +49 -0
- package/config/docdex-runtime.json +21 -0
- package/config/orchestration.schema.json +548 -0
- package/config/raijin-suku.example.json +172 -0
- package/config/terminal-long-scenarios.v1.json +70 -0
- package/dist/agents/RoleResolution.d.ts.map +1 -1
- package/dist/agents/RoleResolution.js +4 -4
- package/dist/api/CodaliApi.d.ts +16 -0
- package/dist/api/CodaliApi.d.ts.map +1 -1
- package/dist/api/CodaliApi.js +39 -10
- package/dist/cli/AskCommand.d.ts +3 -0
- package/dist/cli/AskCommand.d.ts.map +1 -1
- package/dist/cli/AskCommand.js +76 -13
- package/dist/cli/RunCommand.d.ts +4 -0
- package/dist/cli/RunCommand.d.ts.map +1 -1
- package/dist/cli/RunCommand.js +110 -24
- package/dist/cli/TerminalCommand.d.ts +35 -0
- package/dist/cli/TerminalCommand.d.ts.map +1 -0
- package/dist/cli/TerminalCommand.js +616 -0
- package/dist/cli/TerminalCredentials.d.ts +3 -0
- package/dist/cli/TerminalCredentials.d.ts.map +1 -0
- package/dist/cli/TerminalCredentials.js +89 -0
- package/dist/cli/TerminalInput.d.ts +24 -0
- package/dist/cli/TerminalInput.d.ts.map +1 -0
- package/dist/cli/TerminalInput.js +98 -0
- package/dist/cli/TerminalText.d.ts +10 -0
- package/dist/cli/TerminalText.d.ts.map +1 -0
- package/dist/cli/TerminalText.js +53 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +33 -4
- package/dist/cognitive/ConfiguredPipeline.d.ts +34 -0
- package/dist/cognitive/ConfiguredPipeline.d.ts.map +1 -0
- package/dist/cognitive/ConfiguredPipeline.js +50 -0
- package/dist/cognitive/ValidationRunner.d.ts +4 -0
- package/dist/cognitive/ValidationRunner.d.ts.map +1 -1
- package/dist/cognitive/ValidationRunner.js +18 -41
- package/dist/config/NodeOrchestration.d.ts +21 -0
- package/dist/config/NodeOrchestration.d.ts.map +1 -0
- package/dist/config/NodeOrchestration.js +54 -0
- package/dist/config/OrchestrationConfig.d.ts +100 -0
- package/dist/config/OrchestrationConfig.d.ts.map +1 -0
- package/dist/config/OrchestrationConfig.js +245 -0
- package/dist/connectors/mcp/McpClient.d.ts +3 -1
- package/dist/connectors/mcp/McpClient.d.ts.map +1 -1
- package/dist/connectors/mcp/McpClient.js +3 -2
- package/dist/connectors/mcp/McpServerRegistry.d.ts +2 -2
- package/dist/connectors/mcp/McpServerRegistry.d.ts.map +1 -1
- package/dist/connectors/mcp/McpServerRegistry.js +39 -16
- package/dist/connectors/mcp/McpToolAdapter.js +2 -2
- package/dist/connectors/media/ConfiguredMediaTools.d.ts +6 -0
- package/dist/connectors/media/ConfiguredMediaTools.d.ts.map +1 -0
- package/dist/connectors/media/ConfiguredMediaTools.js +41 -0
- package/dist/connectors/media/ImageGenerationTool.js +3 -3
- package/dist/connectors/media/MediaArtifactReceipt.d.ts +5 -0
- package/dist/connectors/media/MediaArtifactReceipt.d.ts.map +1 -0
- package/dist/connectors/media/MediaArtifactReceipt.js +65 -0
- package/dist/connectors/media/MediaJobStore.d.ts +68 -0
- package/dist/connectors/media/MediaJobStore.d.ts.map +1 -0
- package/dist/connectors/media/MediaJobStore.js +199 -0
- package/dist/connectors/media/MediaJobWorker.d.ts +2 -0
- package/dist/connectors/media/MediaJobWorker.d.ts.map +1 -0
- package/dist/connectors/media/MediaJobWorker.js +65 -0
- package/dist/connectors/media/RemoteMediaJob.d.ts +6 -0
- package/dist/connectors/media/RemoteMediaJob.d.ts.map +1 -0
- package/dist/connectors/media/RemoteMediaJob.js +80 -0
- package/dist/docdex/DocdexClient.d.ts +18 -1
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +135 -19
- package/dist/docdex/ManagedDocdex.d.ts +29 -0
- package/dist/docdex/ManagedDocdex.d.ts.map +1 -0
- package/dist/docdex/ManagedDocdex.js +215 -0
- package/dist/docdex/ManagedRuntime.d.ts +28 -0
- package/dist/docdex/ManagedRuntime.d.ts.map +1 -0
- package/dist/docdex/ManagedRuntime.js +212 -0
- package/dist/eval/EvaluationProcess.d.ts +13 -0
- package/dist/eval/EvaluationProcess.d.ts.map +1 -0
- package/dist/eval/EvaluationProcess.js +76 -0
- package/dist/eval/LongWorkflowJourney.d.ts +34 -0
- package/dist/eval/LongWorkflowJourney.d.ts.map +1 -0
- package/dist/eval/LongWorkflowJourney.js +243 -0
- package/dist/eval/PlanningCorpus.d.ts +45 -0
- package/dist/eval/PlanningCorpus.d.ts.map +1 -0
- package/dist/eval/PlanningCorpus.js +189 -0
- package/dist/eval/PlanningCorpusRunner.d.ts +13 -0
- package/dist/eval/PlanningCorpusRunner.d.ts.map +1 -0
- package/dist/eval/PlanningCorpusRunner.js +89 -0
- package/dist/eval/TerminalCorpusRunner.d.ts +36 -0
- package/dist/eval/TerminalCorpusRunner.d.ts.map +1 -0
- package/dist/eval/TerminalCorpusRunner.js +222 -0
- package/dist/eval/TerminalTaskCorpus.d.ts +26 -0
- package/dist/eval/TerminalTaskCorpus.d.ts.map +1 -0
- package/dist/eval/TerminalTaskCorpus.js +105 -0
- package/dist/eval/TerminalWorkflowCorpus.d.ts +11 -0
- package/dist/eval/TerminalWorkflowCorpus.d.ts.map +1 -0
- package/dist/eval/TerminalWorkflowCorpus.js +116 -0
- package/dist/eval/fixtures/TerminalRecoveryJourney.d.ts +4 -0
- package/dist/eval/fixtures/TerminalRecoveryJourney.d.ts.map +1 -0
- package/dist/eval/fixtures/TerminalRecoveryJourney.js +132 -0
- package/dist/gateway/CodaliGateway.d.ts +2 -0
- package/dist/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/gateway/CodaliGateway.js +16 -4
- package/dist/gateway/ConfiguredGateway.d.ts +23 -0
- package/dist/gateway/ConfiguredGateway.d.ts.map +1 -0
- package/dist/gateway/ConfiguredGateway.js +44 -0
- package/dist/gateway/GatewayTracer.d.ts +1 -1
- package/dist/gateway/GatewayTracer.d.ts.map +1 -1
- package/dist/gateway/GatewayTracer.js +2 -27
- package/dist/gateway/LocalGatewayTaskRunner.d.ts.map +1 -1
- package/dist/gateway/LocalGatewayTaskRunner.js +13 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/permissions/PermissionGate.d.ts +44 -0
- package/dist/permissions/PermissionGate.d.ts.map +1 -0
- package/dist/permissions/PermissionGate.js +97 -0
- package/dist/providers/ConfiguredEmbeddings.d.ts +43 -0
- package/dist/providers/ConfiguredEmbeddings.d.ts.map +1 -0
- package/dist/providers/ConfiguredEmbeddings.js +87 -0
- package/dist/providers/ConfiguredModelScope.d.ts +69 -0
- package/dist/providers/ConfiguredModelScope.d.ts.map +1 -0
- package/dist/providers/ConfiguredModelScope.js +113 -0
- package/dist/providers/ConfiguredProvider.d.ts +79 -0
- package/dist/providers/ConfiguredProvider.d.ts.map +1 -0
- package/dist/providers/ConfiguredProvider.js +238 -0
- package/dist/providers/ModelServiceError.d.ts +10 -0
- package/dist/providers/ModelServiceError.d.ts.map +1 -0
- package/dist/providers/ModelServiceError.js +45 -0
- package/dist/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/providers/MswarmWorkerProvider.js +4 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAiCompatibleProvider.js +134 -52
- package/dist/providers/ProviderTypes.d.ts +14 -0
- package/dist/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/runtime/CodaliJobRuntime.d.ts +6 -0
- package/dist/runtime/CodaliJobRuntime.d.ts.map +1 -1
- package/dist/runtime/CodaliJobRuntime.js +36 -1
- package/dist/runtime/CodaliRuntime.d.ts +30 -0
- package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
- package/dist/runtime/CodaliRuntime.js +71 -1
- package/dist/runtime/CompletionReport.d.ts +5 -0
- package/dist/runtime/CompletionReport.d.ts.map +1 -0
- package/dist/runtime/CompletionReport.js +12 -0
- package/dist/runtime/ProgressTracker.d.ts +17 -0
- package/dist/runtime/ProgressTracker.d.ts.map +1 -0
- package/dist/runtime/ProgressTracker.js +27 -0
- package/dist/runtime/RequestedTestEvidence.d.ts +12 -0
- package/dist/runtime/RequestedTestEvidence.d.ts.map +1 -0
- package/dist/runtime/RequestedTestEvidence.js +16 -0
- package/dist/runtime/RequiredRetrieval.d.ts +13 -0
- package/dist/runtime/RequiredRetrieval.d.ts.map +1 -0
- package/dist/runtime/RequiredRetrieval.js +25 -0
- package/dist/runtime/ReviewSource.d.ts +6 -0
- package/dist/runtime/ReviewSource.d.ts.map +1 -0
- package/dist/runtime/ReviewSource.js +38 -0
- package/dist/runtime/Runner.d.ts +13 -0
- package/dist/runtime/Runner.d.ts.map +1 -1
- package/dist/runtime/Runner.js +220 -158
- package/dist/runtime/SecretRedaction.d.ts +11 -0
- package/dist/runtime/SecretRedaction.d.ts.map +1 -0
- package/dist/runtime/SecretRedaction.js +85 -0
- package/dist/runtime/SessionController.d.ts +148 -0
- package/dist/runtime/SessionController.d.ts.map +1 -0
- package/dist/runtime/SessionController.js +1356 -0
- package/dist/runtime/StructuralEvidence.d.ts +22 -0
- package/dist/runtime/StructuralEvidence.d.ts.map +1 -0
- package/dist/runtime/StructuralEvidence.js +87 -0
- package/dist/runtime/TaskReviewContext.d.ts +34 -0
- package/dist/runtime/TaskReviewContext.d.ts.map +1 -0
- package/dist/runtime/TaskReviewContext.js +34 -0
- package/dist/runtime/ValidationOutput.d.ts +5 -0
- package/dist/runtime/ValidationOutput.d.ts.map +1 -0
- package/dist/runtime/ValidationOutput.js +32 -0
- package/dist/runtime/VerificationCommand.d.ts +3 -0
- package/dist/runtime/VerificationCommand.d.ts.map +1 -0
- package/dist/runtime/VerificationCommand.js +48 -0
- package/dist/runtime/WorkStatusPage.d.ts +15 -0
- package/dist/runtime/WorkStatusPage.d.ts.map +1 -0
- package/dist/runtime/WorkStatusPage.js +58 -0
- package/dist/runtime/WorkspaceLedger.d.ts +13 -0
- package/dist/runtime/WorkspaceLedger.d.ts.map +1 -0
- package/dist/runtime/WorkspaceLedger.js +145 -0
- package/dist/runtime/WorkspaceLock.d.ts.map +1 -1
- package/dist/runtime/WorkspaceLock.js +20 -17
- package/dist/runtime/WorkspaceSnapshot.d.ts +13 -0
- package/dist/runtime/WorkspaceSnapshot.d.ts.map +1 -0
- package/dist/runtime/WorkspaceSnapshot.js +88 -0
- package/dist/session/AtomicState.d.ts +13 -0
- package/dist/session/AtomicState.d.ts.map +1 -0
- package/dist/session/AtomicState.js +109 -0
- package/dist/session/SessionStoragePath.d.ts +15 -0
- package/dist/session/SessionStoragePath.d.ts.map +1 -0
- package/dist/session/SessionStoragePath.js +89 -0
- package/dist/session/SessionStore.d.ts +9 -0
- package/dist/session/SessionStore.d.ts.map +1 -1
- package/dist/session/SessionStore.js +181 -47
- package/dist/session/WorkState.d.ts +23 -0
- package/dist/session/WorkState.d.ts.map +1 -0
- package/dist/session/WorkState.js +367 -0
- package/dist/session/WorkTypes.d.ts +94 -0
- package/dist/session/WorkTypes.d.ts.map +1 -0
- package/dist/session/WorkTypes.js +1 -0
- package/dist/tools/ToolRegistry.d.ts +2 -0
- package/dist/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.js +12 -2
- package/dist/tools/ToolTypes.d.ts +16 -0
- package/dist/tools/ToolTypes.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.d.ts +2 -0
- package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.js +17 -2
- package/dist/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/tools/filesystem/FileTools.js +30 -10
- package/dist/tools/shell/AsyncProcess.d.ts +16 -0
- package/dist/tools/shell/AsyncProcess.d.ts.map +1 -0
- package/dist/tools/shell/AsyncProcess.js +94 -0
- package/dist/tools/shell/DarwinRuntimeLibraries.d.ts +13 -0
- package/dist/tools/shell/DarwinRuntimeLibraries.d.ts.map +1 -0
- package/dist/tools/shell/DarwinRuntimeLibraries.js +130 -0
- package/dist/tools/shell/HostToolchain.d.ts +19 -0
- package/dist/tools/shell/HostToolchain.d.ts.map +1 -0
- package/dist/tools/shell/HostToolchain.js +264 -0
- package/dist/tools/shell/IsolatedExecutor.d.ts +15 -0
- package/dist/tools/shell/IsolatedExecutor.d.ts.map +1 -0
- package/dist/tools/shell/IsolatedExecutor.js +134 -0
- package/dist/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/tools/shell/ShellTool.js +9 -29
- package/dist/tools/shell/ShellWorkingDirectory.d.ts +3 -0
- package/dist/tools/shell/ShellWorkingDirectory.d.ts.map +1 -0
- package/dist/tools/shell/ShellWorkingDirectory.js +30 -0
- package/package.json +27 -24
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { loadOrchestrationConfig, orchestrationHash } from "./OrchestrationConfig.js";
|
|
3
|
+
/** The node may serve only the model already admitted by its registry/job boundary. */
|
|
4
|
+
export async function loadNodeOrchestrationConfig(file, approved, nativeReasoning = {}) {
|
|
5
|
+
if (Object.keys(nativeReasoning).some(key => !["reasoning_effort", "reasoning_budget_tokens"].includes(key)))
|
|
6
|
+
throw new Error("Unsupported native reasoning field");
|
|
7
|
+
if (nativeReasoning.reasoning_effort !== undefined && (typeof nativeReasoning.reasoning_effort !== "string" || !/^[a-z][a-z0-9_-]{0,31}$/.test(nativeReasoning.reasoning_effort)))
|
|
8
|
+
throw new Error("Invalid reasoning_effort");
|
|
9
|
+
if (nativeReasoning.reasoning_budget_tokens !== undefined && (!Number.isSafeInteger(nativeReasoning.reasoning_budget_tokens) || nativeReasoning.reasoning_budget_tokens < 0 || nativeReasoning.reasoning_budget_tokens > 1000000))
|
|
10
|
+
throw new Error("Invalid reasoning_budget_tokens");
|
|
11
|
+
if (!path.isAbsolute(file))
|
|
12
|
+
throw new Error("Node orchestration config path must be absolute");
|
|
13
|
+
if (!approved.baseUrl)
|
|
14
|
+
throw new Error("Configured node orchestration requires an approved local runner");
|
|
15
|
+
const endpoint = new URL(approved.baseUrl);
|
|
16
|
+
if (!["127.0.0.1", "localhost", "[::1]"].includes(endpoint.hostname))
|
|
17
|
+
throw new Error("Configured node orchestration requires a loopback runner");
|
|
18
|
+
const normalize = (value) => new URL(value).toString().replace(/\/$/, "");
|
|
19
|
+
const expectedCatalog = approved.modelsPath
|
|
20
|
+
? new URL(approved.modelsPath, `${normalize(endpoint.toString())}/`).toString()
|
|
21
|
+
: `${normalize(endpoint.toString())}/models`;
|
|
22
|
+
const resolved = await loadOrchestrationConfig(file);
|
|
23
|
+
for (const provider of Object.values(resolved.config.providers)) {
|
|
24
|
+
if (provider.adapter !== "openai-compatible" || normalize(provider.baseUrl) !== normalize(endpoint.toString())) {
|
|
25
|
+
throw new Error("Node orchestration provider must match the approved runner; public gateway recursion is forbidden");
|
|
26
|
+
}
|
|
27
|
+
if (normalize(provider.catalogUrl ?? `${normalize(provider.baseUrl)}/models`) !== normalize(expectedCatalog)
|
|
28
|
+
|| provider.usageUrl || provider.mediaJobsUrl)
|
|
29
|
+
throw new Error("Node orchestration catalog/operations differ from the approved runner");
|
|
30
|
+
}
|
|
31
|
+
for (const model of Object.values(resolved.config.models)) {
|
|
32
|
+
if (model.catalogId !== approved.model || model.operations.some(operation => operation !== "chat.completions")) {
|
|
33
|
+
throw new Error("Node orchestration cannot change the admitted model or operation");
|
|
34
|
+
}
|
|
35
|
+
if (approved.modelDigest && model.revision !== approved.modelDigest)
|
|
36
|
+
throw new Error("Node orchestration model revision differs from the approved artifact");
|
|
37
|
+
if (model.supportsTools && !approved.supportsTools || model.supportsStreaming && !approved.supportsStreaming)
|
|
38
|
+
throw new Error("Node orchestration exceeds approved model capabilities");
|
|
39
|
+
if (approved.contextWindow && model.contextWindow > approved.contextWindow
|
|
40
|
+
|| approved.maxOutputTokens && model.maxOutputTokens > approved.maxOutputTokens)
|
|
41
|
+
throw new Error("Node orchestration exceeds approved model limits");
|
|
42
|
+
}
|
|
43
|
+
const sourceHash = resolved.hash;
|
|
44
|
+
if (Object.values(nativeReasoning).some(value => value !== undefined)) {
|
|
45
|
+
for (const model of Object.values(resolved.config.models)) {
|
|
46
|
+
// The caller can select native reasoning, but cannot change the admitted
|
|
47
|
+
// model, endpoint, tools or shared execution budget. All nested roles use
|
|
48
|
+
// the same pinned effective configuration for this invocation.
|
|
49
|
+
model.reasoning = Object.fromEntries(["low", "medium", "high", "max"].map(level => [level, { ...model.reasoning?.[level], ...nativeReasoning }]));
|
|
50
|
+
}
|
|
51
|
+
resolved.hash = orchestrationHash(resolved.config);
|
|
52
|
+
}
|
|
53
|
+
return { ...resolved, sourceHash };
|
|
54
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export type EffortLevel = "low" | "medium" | "high" | "max";
|
|
2
|
+
export type ModelOperation = "chat.completions" | "embeddings" | "images.generations" | "audio.generations" | "videos.generations";
|
|
3
|
+
export interface EffortBudget {
|
|
4
|
+
maxSteps: number;
|
|
5
|
+
maxToolCalls: number;
|
|
6
|
+
maxModelCalls: number;
|
|
7
|
+
maxTotalTokens: number;
|
|
8
|
+
maxOutputTokens: number;
|
|
9
|
+
timeoutMs: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const EFFORT_BUDGETS: Readonly<Record<EffortLevel, Readonly<EffortBudget>>>;
|
|
12
|
+
export interface OrchestrationProvider {
|
|
13
|
+
adapter: "mswarm" | "openai-compatible";
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
credentialRef?: string;
|
|
16
|
+
catalogUrl?: string;
|
|
17
|
+
usageUrl?: string;
|
|
18
|
+
/** Opt-in mswarm-media-jobs-v1 contract; synchronous adapters may omit it. */
|
|
19
|
+
mediaJobsUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface OrchestrationModel {
|
|
22
|
+
provider: string;
|
|
23
|
+
catalogId: string;
|
|
24
|
+
revision: string;
|
|
25
|
+
operations: ModelOperation[];
|
|
26
|
+
backend?: string;
|
|
27
|
+
host?: string;
|
|
28
|
+
resourcePool?: string;
|
|
29
|
+
contextWindow: number;
|
|
30
|
+
maxOutputTokens: number;
|
|
31
|
+
supportsTools?: boolean;
|
|
32
|
+
supportsStreaming?: boolean;
|
|
33
|
+
embeddingDimensions?: number;
|
|
34
|
+
embedding?: {
|
|
35
|
+
queryPrefix: string;
|
|
36
|
+
documentPrefix: string;
|
|
37
|
+
normalization: "none" | "l2";
|
|
38
|
+
pooling: string;
|
|
39
|
+
};
|
|
40
|
+
reasoning?: Partial<Record<EffortLevel, Record<string, unknown>>>;
|
|
41
|
+
}
|
|
42
|
+
export interface ModelBinding {
|
|
43
|
+
candidates?: string[];
|
|
44
|
+
useBinding?: string;
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
embeddingSpace?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface TaskControlConfig {
|
|
49
|
+
maxWritingTasksPerWorktree: 1;
|
|
50
|
+
maxConcurrentHelpers: number;
|
|
51
|
+
helpers: "read-only-or-draft";
|
|
52
|
+
repeatedFailureThreshold: number;
|
|
53
|
+
repairAttemptsBeforeReplan: number;
|
|
54
|
+
checkpointOn: string[];
|
|
55
|
+
reviewPolicy: "risk-and-evidence";
|
|
56
|
+
}
|
|
57
|
+
export interface OrchestrationConfig {
|
|
58
|
+
schemaVersion: 1;
|
|
59
|
+
profile: string;
|
|
60
|
+
providers: Record<string, OrchestrationProvider>;
|
|
61
|
+
models: Record<string, OrchestrationModel>;
|
|
62
|
+
bindings: Record<string, ModelBinding>;
|
|
63
|
+
routing?: {
|
|
64
|
+
strategy: "capability-and-capacity";
|
|
65
|
+
requiredBackends?: Record<string, string[]>;
|
|
66
|
+
};
|
|
67
|
+
effort?: Partial<Record<EffortLevel, Partial<EffortBudget>>>;
|
|
68
|
+
taskControl?: TaskControlConfig;
|
|
69
|
+
docdex?: {
|
|
70
|
+
binary?: string;
|
|
71
|
+
binarySha256?: string;
|
|
72
|
+
startupTimeoutMs?: number;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export interface ResolvedOrchestrationConfig {
|
|
76
|
+
config: OrchestrationConfig;
|
|
77
|
+
hash: string;
|
|
78
|
+
source: string;
|
|
79
|
+
}
|
|
80
|
+
export declare const pinOrchestrationConfig: (resolved: ResolvedOrchestrationConfig) => ResolvedOrchestrationConfig;
|
|
81
|
+
export declare const orchestrationHash: (config: OrchestrationConfig) => string;
|
|
82
|
+
export declare const validateOrchestrationConfig: (input: unknown) => OrchestrationConfig;
|
|
83
|
+
/** Every candidate in a failover binding must produce the same vector space. */
|
|
84
|
+
export declare const configuredEmbeddingSpace: (config: OrchestrationConfig, binding?: string) => {
|
|
85
|
+
id: string;
|
|
86
|
+
queryPrefix: string;
|
|
87
|
+
documentPrefix: string;
|
|
88
|
+
normalization: "none" | "l2";
|
|
89
|
+
pooling: string;
|
|
90
|
+
revision: string;
|
|
91
|
+
dimensions: number;
|
|
92
|
+
label: string;
|
|
93
|
+
};
|
|
94
|
+
export declare const resolveBinding: (config: OrchestrationConfig, name: string, operation?: ModelOperation) => Array<{
|
|
95
|
+
id: string;
|
|
96
|
+
model: OrchestrationModel;
|
|
97
|
+
}>;
|
|
98
|
+
export declare const loadOrchestrationConfig: (file?: string) => Promise<ResolvedOrchestrationConfig>;
|
|
99
|
+
export declare const effortBudget: (config: OrchestrationConfig, level: EffortLevel) => EffortBudget;
|
|
100
|
+
//# sourceMappingURL=OrchestrationConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrchestrationConfig.d.ts","sourceRoot":"","sources":["../../src/config/OrchestrationConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
export const EFFORT_BUDGETS = Object.freeze({
|
|
6
|
+
low: Object.freeze({ maxSteps: 12, maxToolCalls: 12, maxModelCalls: 12, maxTotalTokens: 32000, maxOutputTokens: 2048, timeoutMs: 300000 }),
|
|
7
|
+
medium: Object.freeze({ maxSteps: 32, maxToolCalls: 48, maxModelCalls: 36, maxTotalTokens: 128000, maxOutputTokens: 4096, timeoutMs: 900000 }),
|
|
8
|
+
high: Object.freeze({ maxSteps: 64, maxToolCalls: 128, maxModelCalls: 72, maxTotalTokens: 320000, maxOutputTokens: 8192, timeoutMs: 1800000 }),
|
|
9
|
+
max: Object.freeze({ maxSteps: 128, maxToolCalls: 256, maxModelCalls: 144, maxTotalTokens: 640000, maxOutputTokens: 8192, timeoutMs: 3600000 }),
|
|
10
|
+
});
|
|
11
|
+
export const pinOrchestrationConfig = (resolved) => {
|
|
12
|
+
const config = validateOrchestrationConfig(structuredClone(resolved.config));
|
|
13
|
+
if (orchestrationHash(config) !== resolved.hash)
|
|
14
|
+
throw new Error("Orchestration configuration hash does not match its contents");
|
|
15
|
+
return { config, hash: resolved.hash, source: resolved.source };
|
|
16
|
+
};
|
|
17
|
+
const operations = new Set(["chat.completions", "embeddings", "images.generations", "audio.generations", "videos.generations"]);
|
|
18
|
+
const isRecord = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
19
|
+
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
20
|
+
const object = (v, label) => {
|
|
21
|
+
if (!isRecord(v))
|
|
22
|
+
throw new Error(`${label} must be an object`);
|
|
23
|
+
return v;
|
|
24
|
+
};
|
|
25
|
+
const fields = (v, allowed, label) => {
|
|
26
|
+
for (const key of Object.keys(v))
|
|
27
|
+
if (!allowed.includes(key))
|
|
28
|
+
throw new Error(`${label}: unknown field ${key}`);
|
|
29
|
+
};
|
|
30
|
+
const nonempty = (v, label) => {
|
|
31
|
+
if (typeof v !== "string" || !v.trim() || /[\x00-\x1f]/.test(v))
|
|
32
|
+
throw new Error(`${label} must be a nonempty string`);
|
|
33
|
+
return v;
|
|
34
|
+
};
|
|
35
|
+
const positive = (v, label) => {
|
|
36
|
+
if (!Number.isSafeInteger(v) || v <= 0)
|
|
37
|
+
throw new Error(`${label} must be a positive integer`);
|
|
38
|
+
};
|
|
39
|
+
const trustedUrl = (v, label) => {
|
|
40
|
+
const url = new URL(nonempty(v, label));
|
|
41
|
+
if (url.username || url.password || url.search || url.hash)
|
|
42
|
+
throw new Error(`${label} must not contain credentials, query or fragment`);
|
|
43
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) {
|
|
44
|
+
throw new Error(`${label} requires HTTPS (HTTP is permitted only for loopback development)`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const canonical = (v) => Array.isArray(v) ? v.map(canonical) : isRecord(v)
|
|
48
|
+
? Object.fromEntries(Object.keys(v).sort().map(k => [k, canonical(v[k])])) : v;
|
|
49
|
+
export const orchestrationHash = (config) => createHash("sha256").update(JSON.stringify(canonical(config))).digest("hex");
|
|
50
|
+
export const validateOrchestrationConfig = (input) => {
|
|
51
|
+
const root = object(input, "orchestration");
|
|
52
|
+
fields(root, ["schemaVersion", "profile", "providers", "models", "bindings", "routing", "effort", "taskControl", "docdex"], "orchestration");
|
|
53
|
+
if (root.schemaVersion !== 1)
|
|
54
|
+
throw new Error("Unsupported orchestration schemaVersion");
|
|
55
|
+
nonempty(root.profile, "profile");
|
|
56
|
+
const providers = object(root.providers, "providers");
|
|
57
|
+
for (const [id, entry] of Object.entries(providers)) {
|
|
58
|
+
const p = object(entry, `providers.${id}`);
|
|
59
|
+
fields(p, ["adapter", "baseUrl", "credentialRef", "catalogUrl", "usageUrl", "mediaJobsUrl"], `providers.${id}`);
|
|
60
|
+
if (!["mswarm", "openai-compatible"].includes(String(p.adapter)))
|
|
61
|
+
throw new Error(`Unsupported adapter: ${id}`);
|
|
62
|
+
trustedUrl(p.baseUrl, `${id}.baseUrl`);
|
|
63
|
+
if (p.credentialRef !== undefined && !/^env:[A-Za-z_][A-Za-z0-9_]*$/.test(String(p.credentialRef)))
|
|
64
|
+
throw new Error(`${id}: credentialRef must be env:NAME`);
|
|
65
|
+
if (p.adapter === "mswarm" && !p.credentialRef)
|
|
66
|
+
throw new Error(`${id}: mSwarm requires credentialRef`);
|
|
67
|
+
for (const key of ["catalogUrl", "usageUrl", "mediaJobsUrl"])
|
|
68
|
+
if (p[key] !== undefined) {
|
|
69
|
+
trustedUrl(p[key], `${id}.${key}`);
|
|
70
|
+
if (new URL(String(p[key])).origin !== new URL(String(p.baseUrl)).origin)
|
|
71
|
+
throw new Error(`${id}.${key} must use the provider origin`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const models = object(root.models, "models");
|
|
75
|
+
for (const [id, entry] of Object.entries(models)) {
|
|
76
|
+
const m = object(entry, `models.${id}`);
|
|
77
|
+
fields(m, ["provider", "catalogId", "revision", "operations", "backend", "host", "resourcePool", "contextWindow", "maxOutputTokens", "supportsTools", "supportsStreaming", "embeddingDimensions", "embedding", "reasoning"], `models.${id}`);
|
|
78
|
+
if (!hasOwn(providers, String(m.provider)))
|
|
79
|
+
throw new Error(`${id}: unknown provider`);
|
|
80
|
+
nonempty(m.catalogId, `${id}.catalogId`);
|
|
81
|
+
nonempty(m.revision, `${id}.revision`);
|
|
82
|
+
positive(m.contextWindow, `${id}.contextWindow`);
|
|
83
|
+
positive(m.maxOutputTokens, `${id}.maxOutputTokens`);
|
|
84
|
+
if (Number(m.maxOutputTokens) > Number(m.contextWindow))
|
|
85
|
+
throw new Error(`${id}: output exceeds context window`);
|
|
86
|
+
if (!Array.isArray(m.operations) || !m.operations.length || m.operations.some(o => !operations.has(o)))
|
|
87
|
+
throw new Error(`${id}: invalid operations`);
|
|
88
|
+
for (const key of ["supportsTools", "supportsStreaming"])
|
|
89
|
+
if (m[key] !== undefined && typeof m[key] !== "boolean")
|
|
90
|
+
throw new Error(`${id}.${key} must be boolean`);
|
|
91
|
+
for (const key of ["host", "backend", "resourcePool"])
|
|
92
|
+
if (m[key] !== undefined)
|
|
93
|
+
nonempty(m[key], `${id}.${key}`);
|
|
94
|
+
if (m.embeddingDimensions !== undefined)
|
|
95
|
+
positive(m.embeddingDimensions, `${id}.embeddingDimensions`);
|
|
96
|
+
if (m.embedding !== undefined) {
|
|
97
|
+
const e = object(m.embedding, `${id}.embedding`);
|
|
98
|
+
fields(e, ["queryPrefix", "documentPrefix", "normalization", "pooling"], `${id}.embedding`);
|
|
99
|
+
for (const key of ["queryPrefix", "documentPrefix"])
|
|
100
|
+
if (typeof e[key] !== "string" || String(e[key]).length > 256 || /[\x00-\x1f]/.test(String(e[key])))
|
|
101
|
+
throw new Error(`${id}.embedding.${key} must be a bounded string`);
|
|
102
|
+
if (!["none", "l2"].includes(String(e.normalization)))
|
|
103
|
+
throw new Error(`${id}: invalid embedding normalization`);
|
|
104
|
+
nonempty(e.pooling, `${id}.embedding.pooling`);
|
|
105
|
+
}
|
|
106
|
+
if (m.reasoning !== undefined) {
|
|
107
|
+
const mapping = object(m.reasoning, `${id}.reasoning`);
|
|
108
|
+
fields(mapping, ["low", "medium", "high", "max"], `${id}.reasoning`);
|
|
109
|
+
for (const [level, settings] of Object.entries(mapping)) {
|
|
110
|
+
const values = object(settings, `${id}.reasoning.${level}`);
|
|
111
|
+
fields(values, ["reasoning_effort", "reasoning_budget", "reasoning_budget_tokens", "enable_thinking"], `${id}.reasoning.${level}`);
|
|
112
|
+
if (values.reasoning_effort !== undefined)
|
|
113
|
+
nonempty(values.reasoning_effort, `${id}.reasoning.${level}.reasoning_effort`);
|
|
114
|
+
if (values.reasoning_budget !== undefined)
|
|
115
|
+
positive(values.reasoning_budget, `${id}.reasoning.${level}.reasoning_budget`);
|
|
116
|
+
if (values.reasoning_budget_tokens !== undefined && (!Number.isSafeInteger(values.reasoning_budget_tokens) || Number(values.reasoning_budget_tokens) < 0 || Number(values.reasoning_budget_tokens) > 1000000))
|
|
117
|
+
throw new Error(`${id}.reasoning.${level}.reasoning_budget_tokens must be an integer from 0 through 1000000`);
|
|
118
|
+
if (values.enable_thinking !== undefined && typeof values.enable_thinking !== "boolean")
|
|
119
|
+
throw new Error(`${id}.reasoning.${level}.enable_thinking must be boolean`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const bindings = object(root.bindings, "bindings");
|
|
124
|
+
if (!hasOwn(bindings, "lead"))
|
|
125
|
+
throw new Error("Missing lead binding");
|
|
126
|
+
for (const [name, entry] of Object.entries(bindings)) {
|
|
127
|
+
const b = object(entry, `bindings.${name}`);
|
|
128
|
+
fields(b, ["candidates", "useBinding", "enabled", "embeddingSpace"], `bindings.${name}`);
|
|
129
|
+
if (b.enabled !== undefined && typeof b.enabled !== "boolean")
|
|
130
|
+
throw new Error(`${name}.enabled must be boolean`);
|
|
131
|
+
if (b.enabled === false) {
|
|
132
|
+
if (name === "lead")
|
|
133
|
+
throw new Error("Lead cannot be disabled");
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if ((b.candidates !== undefined) === (b.useBinding !== undefined))
|
|
137
|
+
throw new Error(`${name}: specify candidates or useBinding`);
|
|
138
|
+
if (b.useBinding !== undefined && !hasOwn(bindings, String(b.useBinding)))
|
|
139
|
+
throw new Error(`${name}: unknown binding alias`);
|
|
140
|
+
if (b.candidates !== undefined && (!Array.isArray(b.candidates) || !b.candidates.length || b.candidates.some(id => typeof id !== "string" || !hasOwn(models, id)) || new Set(b.candidates).size !== b.candidates.length))
|
|
141
|
+
throw new Error(`${name}: invalid/duplicate/undeclared candidates`);
|
|
142
|
+
if (b.embeddingSpace !== undefined)
|
|
143
|
+
nonempty(b.embeddingSpace, `${name}.embeddingSpace`);
|
|
144
|
+
}
|
|
145
|
+
if (root.routing !== undefined) {
|
|
146
|
+
const r = object(root.routing, "routing");
|
|
147
|
+
fields(r, ["strategy", "requiredBackends"], "routing");
|
|
148
|
+
if (r.strategy !== "capability-and-capacity")
|
|
149
|
+
throw new Error("Unsupported routing strategy");
|
|
150
|
+
for (const [role, values] of Object.entries(object(r.requiredBackends ?? {}, "requiredBackends"))) {
|
|
151
|
+
if (!hasOwn(bindings, role) || !Array.isArray(values) || !values.length || values.some(x => typeof x !== "string" || !x))
|
|
152
|
+
throw new Error(`Invalid requiredBackends: ${role}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (root.effort !== undefined) {
|
|
156
|
+
const profiles = object(root.effort, "effort");
|
|
157
|
+
fields(profiles, ["low", "medium", "high", "max"], "effort");
|
|
158
|
+
for (const [level, entry] of Object.entries(profiles)) {
|
|
159
|
+
const p = object(entry, `effort.${level}`);
|
|
160
|
+
fields(p, Object.keys(EFFORT_BUDGETS.low), `effort.${level}`);
|
|
161
|
+
for (const [key, value] of Object.entries(p))
|
|
162
|
+
positive(value, `effort.${level}.${key}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (root.taskControl !== undefined) {
|
|
166
|
+
const t = object(root.taskControl, "taskControl");
|
|
167
|
+
fields(t, ["maxWritingTasksPerWorktree", "maxConcurrentHelpers", "helpers", "repeatedFailureThreshold", "repairAttemptsBeforeReplan", "checkpointOn", "reviewPolicy"], "taskControl");
|
|
168
|
+
if (t.maxWritingTasksPerWorktree !== 1 || t.helpers !== "read-only-or-draft" || t.reviewPolicy !== "risk-and-evidence")
|
|
169
|
+
throw new Error("Invalid task-control safety policy");
|
|
170
|
+
for (const key of ["maxConcurrentHelpers", "repeatedFailureThreshold", "repairAttemptsBeforeReplan"])
|
|
171
|
+
positive(t[key], `taskControl.${key}`);
|
|
172
|
+
if (!Array.isArray(t.checkpointOn) || t.checkpointOn.some(v => !["task-transition", "before-compaction", "before-exit"].includes(String(v))))
|
|
173
|
+
throw new Error("Invalid checkpointOn policy");
|
|
174
|
+
}
|
|
175
|
+
if (root.docdex !== undefined) {
|
|
176
|
+
const d = object(root.docdex, "docdex");
|
|
177
|
+
fields(d, ["binary", "binarySha256", "startupTimeoutMs"], "docdex");
|
|
178
|
+
if (d.binary !== undefined)
|
|
179
|
+
nonempty(d.binary, "docdex.binary");
|
|
180
|
+
if (d.binarySha256 !== undefined && (typeof d.binarySha256 !== "string" || !/^[a-f0-9]{64}$/.test(d.binarySha256) || d.binary === undefined))
|
|
181
|
+
throw new Error("docdex.binarySha256 requires an explicit binary and a SHA-256 digest");
|
|
182
|
+
if (d.startupTimeoutMs !== undefined)
|
|
183
|
+
positive(d.startupTimeoutMs, "docdex.startupTimeoutMs");
|
|
184
|
+
}
|
|
185
|
+
const config = structuredClone(root);
|
|
186
|
+
for (const name of Object.keys(config.bindings))
|
|
187
|
+
if (config.bindings[name].enabled !== false)
|
|
188
|
+
resolveBinding(config, name);
|
|
189
|
+
if (config.bindings.embedding && config.bindings.embedding.enabled !== false)
|
|
190
|
+
configuredEmbeddingSpace(config);
|
|
191
|
+
return config;
|
|
192
|
+
};
|
|
193
|
+
/** Every candidate in a failover binding must produce the same vector space. */
|
|
194
|
+
export const configuredEmbeddingSpace = (config, binding = "embedding") => {
|
|
195
|
+
let key = binding;
|
|
196
|
+
const seen = new Set();
|
|
197
|
+
let label;
|
|
198
|
+
while (config.bindings[key] && !seen.has(key)) {
|
|
199
|
+
seen.add(key);
|
|
200
|
+
label ?? (label = config.bindings[key].embeddingSpace);
|
|
201
|
+
if (!config.bindings[key].useBinding)
|
|
202
|
+
break;
|
|
203
|
+
key = config.bindings[key].useBinding;
|
|
204
|
+
}
|
|
205
|
+
if (!label)
|
|
206
|
+
throw new Error("Enabled embedding binding requires embeddingSpace");
|
|
207
|
+
const candidates = resolveBinding(config, binding, "embeddings");
|
|
208
|
+
const identities = candidates.map(({ id, model }) => {
|
|
209
|
+
if (!model.embeddingDimensions || model.embeddingDimensions > 4096 || !model.embedding || /^(catalog-unversioned|latest|unknown)$/.test(model.revision)) {
|
|
210
|
+
throw new Error(`${id}: embeddings require a pinned revision, dimensions and explicit preprocessing`);
|
|
211
|
+
}
|
|
212
|
+
return { revision: model.revision, dimensions: model.embeddingDimensions, ...model.embedding };
|
|
213
|
+
});
|
|
214
|
+
const signature = JSON.stringify(canonical(identities[0]));
|
|
215
|
+
if (identities.some(identity => JSON.stringify(canonical(identity)) !== signature))
|
|
216
|
+
throw new Error("Embedding failover candidates have incompatible vector spaces");
|
|
217
|
+
return { label, ...identities[0], id: createHash("sha256").update(JSON.stringify([label, signature])).digest("hex") };
|
|
218
|
+
};
|
|
219
|
+
export const resolveBinding = (config, name, operation) => {
|
|
220
|
+
const seen = new Set();
|
|
221
|
+
let key = name;
|
|
222
|
+
while (true) {
|
|
223
|
+
if (seen.has(key))
|
|
224
|
+
throw new Error(`Binding alias cycle: ${name}`);
|
|
225
|
+
seen.add(key);
|
|
226
|
+
const binding = config.bindings[key];
|
|
227
|
+
if (!binding || binding.enabled === false)
|
|
228
|
+
throw new Error(`Binding unavailable: ${key}`);
|
|
229
|
+
if (binding.useBinding) {
|
|
230
|
+
key = binding.useBinding;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const requiredBackends = [...seen].map(role => config.routing?.requiredBackends?.[role]).filter((v) => !!v);
|
|
234
|
+
const candidates = (binding.candidates ?? []).map(id => ({ id, model: config.models[id] })).filter(({ model }) => model && (!operation || model.operations.includes(operation)) && requiredBackends.every(allowed => allowed.includes(model.backend ?? "")));
|
|
235
|
+
if (!candidates.length)
|
|
236
|
+
throw new Error(`No compatible configured candidates for ${name}${operation ? ` (${operation})` : ""}`);
|
|
237
|
+
return candidates;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
export const loadOrchestrationConfig = async (file = process.env.CODALI_ORCHESTRATION_CONFIG ?? path.join(homedir(), ".codali", "orchestration.json")) => {
|
|
241
|
+
const source = path.resolve(file);
|
|
242
|
+
const config = validateOrchestrationConfig(JSON.parse(await readFile(source, "utf8")));
|
|
243
|
+
return { config, source, hash: orchestrationHash(config) };
|
|
244
|
+
};
|
|
245
|
+
export const effortBudget = (config, level) => ({ ...EFFORT_BUDGETS[level], ...config.effort?.[level] });
|
|
@@ -103,6 +103,7 @@ export interface McpClientOptions {
|
|
|
103
103
|
export interface McpTransportClient {
|
|
104
104
|
listTools(params?: Record<string, unknown>, options?: {
|
|
105
105
|
timeout?: number;
|
|
106
|
+
signal?: AbortSignal;
|
|
106
107
|
}): Promise<{
|
|
107
108
|
tools: unknown[];
|
|
108
109
|
}>;
|
|
@@ -111,6 +112,7 @@ export interface McpTransportClient {
|
|
|
111
112
|
arguments?: Record<string, unknown>;
|
|
112
113
|
}, resultSchema?: unknown, options?: {
|
|
113
114
|
timeout?: number;
|
|
115
|
+
signal?: AbortSignal;
|
|
114
116
|
}): Promise<unknown>;
|
|
115
117
|
close(): Promise<void>;
|
|
116
118
|
}
|
|
@@ -125,7 +127,7 @@ export declare class McpClient {
|
|
|
125
127
|
private ensureClient;
|
|
126
128
|
private connect;
|
|
127
129
|
listTools(): Promise<McpToolDefinition[]>;
|
|
128
|
-
callTool(name: string, args: unknown): Promise<McpCallResult>;
|
|
130
|
+
callTool(name: string, args: unknown, signal?: AbortSignal): Promise<McpCallResult>;
|
|
129
131
|
close(): Promise<void>;
|
|
130
132
|
}
|
|
131
133
|
export declare const createMcpClient: (options: McpClientOptions) => McpClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpClient.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpClient.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AAEH;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,eAAe,CAAC;AAkCxD,eAAO,MAAM,sBAAsB;;;CAGzB,CAAC;AAIX,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhD,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,kBAAkB,GAAG,uBAAuB,GAAG,sBAAsB,CAAC;AAElF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC;CACd;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAG1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAWzD;AAKD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAS,OAAO,KAAG,MAkBpD,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,mBAAmB,CAAC;IAChC,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAClD;AAED,2DAA2D;AAC3D,MAAM,WAAW,kBAAkB;IACjC,SAAS,CACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,
|
|
1
|
+
{"version":3,"file":"McpClient.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -176,11 +176,12 @@ export class McpClient {
|
|
|
176
176
|
}
|
|
177
177
|
return tools;
|
|
178
178
|
}
|
|
179
|
-
async callTool(name, args) {
|
|
179
|
+
async callTool(name, args, signal) {
|
|
180
|
+
signal?.throwIfAborted();
|
|
180
181
|
const client = await this.ensureClient();
|
|
181
182
|
let raw;
|
|
182
183
|
try {
|
|
183
|
-
raw = await client.callTool({ name, arguments: isRecord(args) ? args : {} }, undefined, { timeout: this.timeoutMs });
|
|
184
|
+
raw = await client.callTool({ name, arguments: isRecord(args) ? args : {} }, undefined, { timeout: this.timeoutMs, ...(signal ? { signal } : {}) });
|
|
184
185
|
}
|
|
185
186
|
catch (error) {
|
|
186
187
|
throw new McpClientError("mcp_call_tool_failed", this.name, `tools/call ${name} failed on "${this.name}": ${error instanceof Error ? error.message : String(error)}`, { retryable: true, cause: error });
|
|
@@ -59,8 +59,8 @@ export declare class McpServerRegistry {
|
|
|
59
59
|
discoverTools(): Promise<ToolDefinition[]>;
|
|
60
60
|
/**
|
|
61
61
|
* Wraps a client so every tool call is bounded by the concurrency budget and
|
|
62
|
-
* retried once on a transport failure
|
|
63
|
-
*
|
|
62
|
+
* retried once on a transport failure only for operator-declared read-only
|
|
63
|
+
* servers. A lost write response leaves an uncertain remote outcome.
|
|
64
64
|
*/
|
|
65
65
|
private wrapClient;
|
|
66
66
|
healthReport(): McpServerHealth[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServerRegistry.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpServerRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EACL,SAAS,EAET,KAAK,mBAAmB,EAEzB,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;;;;;;GAYG;AAEH,yDAAyD;AACzD,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAK9C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,SAAS,CAAC;IAC9D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"McpServerRegistry.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpServerRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EACL,SAAS,EAET,KAAK,mBAAmB,EAEzB,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;;;;;;GAYG;AAEH,yDAAyD;AACzD,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAK9C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,SAAS,CAAC;IAC9D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAuC/D,qBAAa,iBAAiB;IAMhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,UAAU,CAAS;gBAEE,OAAO,EAAE,wBAAwB;IAM9D;;;;;OAKG;IACG,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAmFhD;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA8BlB,YAAY,IAAI,eAAe,EAAE;IAIjC,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAED,eAAO,MAAM,uBAAuB,GAClC,SAAS,wBAAwB,KAChC,iBAAmD,CAAC"}
|
|
@@ -24,21 +24,42 @@ class Semaphore {
|
|
|
24
24
|
this.limit = limit;
|
|
25
25
|
this.active = 0;
|
|
26
26
|
this.waiting = [];
|
|
27
|
+
if (!Number.isSafeInteger(limit) || limit < 1)
|
|
28
|
+
throw new Error("MCP concurrency limit must be a positive integer");
|
|
27
29
|
}
|
|
28
|
-
async acquire() {
|
|
30
|
+
async acquire(signal) {
|
|
31
|
+
signal?.throwIfAborted();
|
|
29
32
|
if (this.active < this.limit) {
|
|
30
33
|
this.active += 1;
|
|
31
|
-
return () => this.release();
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
else {
|
|
36
|
+
await new Promise((resolve, reject) => {
|
|
37
|
+
const grant = () => { signal?.removeEventListener("abort", abort); resolve(); };
|
|
38
|
+
const abort = () => {
|
|
39
|
+
const index = this.waiting.indexOf(grant);
|
|
40
|
+
if (index < 0)
|
|
41
|
+
return;
|
|
42
|
+
this.waiting.splice(index, 1);
|
|
43
|
+
reject(signal?.reason ?? new Error("MCP wait aborted"));
|
|
44
|
+
};
|
|
45
|
+
this.waiting.push(grant);
|
|
46
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
let released = false;
|
|
50
|
+
return () => { if (!released) {
|
|
51
|
+
released = true;
|
|
52
|
+
this.release();
|
|
53
|
+
} };
|
|
36
54
|
}
|
|
37
55
|
release() {
|
|
38
|
-
this.active -= 1;
|
|
39
56
|
const next = this.waiting.shift();
|
|
57
|
+
// Transfer the occupied slot before waking its waiter; a new caller cannot
|
|
58
|
+
// steal it between promise resolution and the waiter's continuation.
|
|
40
59
|
if (next)
|
|
41
60
|
next();
|
|
61
|
+
else
|
|
62
|
+
this.active -= 1;
|
|
42
63
|
}
|
|
43
64
|
}
|
|
44
65
|
export class McpServerRegistry {
|
|
@@ -116,7 +137,7 @@ export class McpServerRegistry {
|
|
|
116
137
|
durationMs,
|
|
117
138
|
});
|
|
118
139
|
return mcpToolsToDefinitions(tools, {
|
|
119
|
-
client: this.wrapClient(client),
|
|
140
|
+
client: this.wrapClient(client, definition.readOnly === true),
|
|
120
141
|
server: definition.name,
|
|
121
142
|
capability: definition.name,
|
|
122
143
|
// Operator declaration only. Claiming read-only for an unvetted
|
|
@@ -132,28 +153,30 @@ export class McpServerRegistry {
|
|
|
132
153
|
}
|
|
133
154
|
/**
|
|
134
155
|
* Wraps a client so every tool call is bounded by the concurrency budget and
|
|
135
|
-
* retried once on a transport failure
|
|
136
|
-
*
|
|
156
|
+
* retried once on a transport failure only for operator-declared read-only
|
|
157
|
+
* servers. A lost write response leaves an uncertain remote outcome.
|
|
137
158
|
*/
|
|
138
|
-
wrapClient(client) {
|
|
159
|
+
wrapClient(client, readOnly) {
|
|
139
160
|
const semaphore = this.semaphore;
|
|
140
161
|
const wrapped = Object.create(client);
|
|
141
|
-
wrapped.callTool = async (name, args) => {
|
|
142
|
-
const release = await semaphore.acquire();
|
|
162
|
+
wrapped.callTool = async (name, args, signal) => {
|
|
163
|
+
const release = await semaphore.acquire(signal);
|
|
143
164
|
try {
|
|
165
|
+
signal?.throwIfAborted();
|
|
144
166
|
let lastError;
|
|
145
|
-
|
|
167
|
+
const attempts = readOnly ? MAX_CALL_ATTEMPTS : 1;
|
|
168
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
146
169
|
try {
|
|
147
|
-
return await client.callTool(name, args);
|
|
170
|
+
return await client.callTool(name, args, signal);
|
|
148
171
|
}
|
|
149
172
|
catch (error) {
|
|
150
173
|
lastError = error;
|
|
151
174
|
const retryable = error instanceof McpClientError && error.retryable;
|
|
152
|
-
if (!retryable || attempt ===
|
|
175
|
+
if (!retryable || signal?.aborted || attempt === attempts)
|
|
153
176
|
break;
|
|
154
177
|
}
|
|
155
178
|
}
|
|
156
|
-
throw new ToolExecutionError("tool_execution_failed", lastError instanceof Error ? lastError.message : String(lastError), { retryable: false, details: { tool: name } });
|
|
179
|
+
throw new ToolExecutionError("tool_execution_failed", lastError instanceof Error ? lastError.message : String(lastError), { retryable: false, details: { tool: name, uncertainOutcome: !readOnly } });
|
|
157
180
|
}
|
|
158
181
|
finally {
|
|
159
182
|
release();
|
|
@@ -74,8 +74,8 @@ export const mcpToolToDefinition = (tool, options) => {
|
|
|
74
74
|
outputSchema: tool.outputSchema,
|
|
75
75
|
readOnly: options.readOnly,
|
|
76
76
|
capability: options.capability ?? options.server,
|
|
77
|
-
handler: async (args) => {
|
|
78
|
-
const result = await options.client.callTool(tool.name, args);
|
|
77
|
+
handler: async (args, context) => {
|
|
78
|
+
const result = await options.client.callTool(tool.name, args, context.signal);
|
|
79
79
|
if (!result.ok) {
|
|
80
80
|
// Surface as a tool failure so the runner records it and the model is
|
|
81
81
|
// told, rather than passing an error string off as a result.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ResolvedOrchestrationConfig } from "../../config/OrchestrationConfig.js";
|
|
2
|
+
import type { ToolDefinition } from "../../tools/ToolTypes.js";
|
|
3
|
+
import { MediaJobStore, type MediaJob } from "./MediaJobStore.js";
|
|
4
|
+
export declare const mediaJobSummary: (job: MediaJob) => Record<string, unknown>;
|
|
5
|
+
export declare const createConfiguredMediaTools: (resolved: ResolvedOrchestrationConfig, store: () => MediaJobStore) => ToolDefinition[];
|
|
6
|
+
//# sourceMappingURL=ConfiguredMediaTools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfiguredMediaTools.d.ts","sourceRoot":"","sources":["../../../src/connectors/media/ConfiguredMediaTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAkB,MAAM,qCAAqC,CAAC;AACvG,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,0BAA0B,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGlE,eAAO,MAAM,eAAe,GAAI,KAAK,QAAQ,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAuN,CAAC;AAC9R,eAAO,MAAM,0BAA0B,GAAI,UAAU,2BAA2B,EAAE,OAAO,MAAM,aAAa,KAAG,cAAc,EA+B5H,CAAC"}
|