@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.
Files changed (232) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/ORCHESTRATION.md +387 -0
  3. package/README.md +103 -1
  4. package/config/alternate-local.example.json +49 -0
  5. package/config/docdex-runtime.json +21 -0
  6. package/config/orchestration.schema.json +548 -0
  7. package/config/raijin-suku.example.json +172 -0
  8. package/config/terminal-long-scenarios.v1.json +70 -0
  9. package/dist/agents/RoleResolution.d.ts.map +1 -1
  10. package/dist/agents/RoleResolution.js +4 -4
  11. package/dist/api/CodaliApi.d.ts +16 -0
  12. package/dist/api/CodaliApi.d.ts.map +1 -1
  13. package/dist/api/CodaliApi.js +39 -10
  14. package/dist/cli/AskCommand.d.ts +3 -0
  15. package/dist/cli/AskCommand.d.ts.map +1 -1
  16. package/dist/cli/AskCommand.js +76 -13
  17. package/dist/cli/RunCommand.d.ts +4 -0
  18. package/dist/cli/RunCommand.d.ts.map +1 -1
  19. package/dist/cli/RunCommand.js +110 -24
  20. package/dist/cli/TerminalCommand.d.ts +35 -0
  21. package/dist/cli/TerminalCommand.d.ts.map +1 -0
  22. package/dist/cli/TerminalCommand.js +616 -0
  23. package/dist/cli/TerminalCredentials.d.ts +3 -0
  24. package/dist/cli/TerminalCredentials.d.ts.map +1 -0
  25. package/dist/cli/TerminalCredentials.js +89 -0
  26. package/dist/cli/TerminalInput.d.ts +24 -0
  27. package/dist/cli/TerminalInput.d.ts.map +1 -0
  28. package/dist/cli/TerminalInput.js +98 -0
  29. package/dist/cli/TerminalText.d.ts +10 -0
  30. package/dist/cli/TerminalText.d.ts.map +1 -0
  31. package/dist/cli/TerminalText.js +53 -0
  32. package/dist/cli.d.ts.map +1 -1
  33. package/dist/cli.js +33 -4
  34. package/dist/cognitive/ConfiguredPipeline.d.ts +34 -0
  35. package/dist/cognitive/ConfiguredPipeline.d.ts.map +1 -0
  36. package/dist/cognitive/ConfiguredPipeline.js +50 -0
  37. package/dist/cognitive/ValidationRunner.d.ts +4 -0
  38. package/dist/cognitive/ValidationRunner.d.ts.map +1 -1
  39. package/dist/cognitive/ValidationRunner.js +18 -41
  40. package/dist/config/NodeOrchestration.d.ts +21 -0
  41. package/dist/config/NodeOrchestration.d.ts.map +1 -0
  42. package/dist/config/NodeOrchestration.js +54 -0
  43. package/dist/config/OrchestrationConfig.d.ts +100 -0
  44. package/dist/config/OrchestrationConfig.d.ts.map +1 -0
  45. package/dist/config/OrchestrationConfig.js +245 -0
  46. package/dist/connectors/mcp/McpClient.d.ts +3 -1
  47. package/dist/connectors/mcp/McpClient.d.ts.map +1 -1
  48. package/dist/connectors/mcp/McpClient.js +3 -2
  49. package/dist/connectors/mcp/McpServerRegistry.d.ts +2 -2
  50. package/dist/connectors/mcp/McpServerRegistry.d.ts.map +1 -1
  51. package/dist/connectors/mcp/McpServerRegistry.js +39 -16
  52. package/dist/connectors/mcp/McpToolAdapter.js +2 -2
  53. package/dist/connectors/media/ConfiguredMediaTools.d.ts +6 -0
  54. package/dist/connectors/media/ConfiguredMediaTools.d.ts.map +1 -0
  55. package/dist/connectors/media/ConfiguredMediaTools.js +41 -0
  56. package/dist/connectors/media/ImageGenerationTool.js +3 -3
  57. package/dist/connectors/media/MediaArtifactReceipt.d.ts +5 -0
  58. package/dist/connectors/media/MediaArtifactReceipt.d.ts.map +1 -0
  59. package/dist/connectors/media/MediaArtifactReceipt.js +65 -0
  60. package/dist/connectors/media/MediaJobStore.d.ts +68 -0
  61. package/dist/connectors/media/MediaJobStore.d.ts.map +1 -0
  62. package/dist/connectors/media/MediaJobStore.js +199 -0
  63. package/dist/connectors/media/MediaJobWorker.d.ts +2 -0
  64. package/dist/connectors/media/MediaJobWorker.d.ts.map +1 -0
  65. package/dist/connectors/media/MediaJobWorker.js +65 -0
  66. package/dist/connectors/media/RemoteMediaJob.d.ts +6 -0
  67. package/dist/connectors/media/RemoteMediaJob.d.ts.map +1 -0
  68. package/dist/connectors/media/RemoteMediaJob.js +80 -0
  69. package/dist/docdex/DocdexClient.d.ts +18 -1
  70. package/dist/docdex/DocdexClient.d.ts.map +1 -1
  71. package/dist/docdex/DocdexClient.js +135 -19
  72. package/dist/docdex/ManagedDocdex.d.ts +29 -0
  73. package/dist/docdex/ManagedDocdex.d.ts.map +1 -0
  74. package/dist/docdex/ManagedDocdex.js +215 -0
  75. package/dist/docdex/ManagedRuntime.d.ts +28 -0
  76. package/dist/docdex/ManagedRuntime.d.ts.map +1 -0
  77. package/dist/docdex/ManagedRuntime.js +212 -0
  78. package/dist/eval/EvaluationProcess.d.ts +13 -0
  79. package/dist/eval/EvaluationProcess.d.ts.map +1 -0
  80. package/dist/eval/EvaluationProcess.js +76 -0
  81. package/dist/eval/LongWorkflowJourney.d.ts +34 -0
  82. package/dist/eval/LongWorkflowJourney.d.ts.map +1 -0
  83. package/dist/eval/LongWorkflowJourney.js +243 -0
  84. package/dist/eval/PlanningCorpus.d.ts +45 -0
  85. package/dist/eval/PlanningCorpus.d.ts.map +1 -0
  86. package/dist/eval/PlanningCorpus.js +189 -0
  87. package/dist/eval/PlanningCorpusRunner.d.ts +13 -0
  88. package/dist/eval/PlanningCorpusRunner.d.ts.map +1 -0
  89. package/dist/eval/PlanningCorpusRunner.js +89 -0
  90. package/dist/eval/TerminalCorpusRunner.d.ts +36 -0
  91. package/dist/eval/TerminalCorpusRunner.d.ts.map +1 -0
  92. package/dist/eval/TerminalCorpusRunner.js +222 -0
  93. package/dist/eval/TerminalTaskCorpus.d.ts +26 -0
  94. package/dist/eval/TerminalTaskCorpus.d.ts.map +1 -0
  95. package/dist/eval/TerminalTaskCorpus.js +105 -0
  96. package/dist/eval/TerminalWorkflowCorpus.d.ts +11 -0
  97. package/dist/eval/TerminalWorkflowCorpus.d.ts.map +1 -0
  98. package/dist/eval/TerminalWorkflowCorpus.js +116 -0
  99. package/dist/eval/fixtures/TerminalRecoveryJourney.d.ts +4 -0
  100. package/dist/eval/fixtures/TerminalRecoveryJourney.d.ts.map +1 -0
  101. package/dist/eval/fixtures/TerminalRecoveryJourney.js +132 -0
  102. package/dist/gateway/CodaliGateway.d.ts +2 -0
  103. package/dist/gateway/CodaliGateway.d.ts.map +1 -1
  104. package/dist/gateway/CodaliGateway.js +16 -4
  105. package/dist/gateway/ConfiguredGateway.d.ts +23 -0
  106. package/dist/gateway/ConfiguredGateway.d.ts.map +1 -0
  107. package/dist/gateway/ConfiguredGateway.js +44 -0
  108. package/dist/gateway/GatewayTracer.d.ts +1 -1
  109. package/dist/gateway/GatewayTracer.d.ts.map +1 -1
  110. package/dist/gateway/GatewayTracer.js +2 -27
  111. package/dist/gateway/LocalGatewayTaskRunner.d.ts.map +1 -1
  112. package/dist/gateway/LocalGatewayTaskRunner.js +13 -1
  113. package/dist/index.d.ts +23 -0
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +12 -0
  116. package/dist/permissions/PermissionGate.d.ts +44 -0
  117. package/dist/permissions/PermissionGate.d.ts.map +1 -0
  118. package/dist/permissions/PermissionGate.js +97 -0
  119. package/dist/providers/ConfiguredEmbeddings.d.ts +43 -0
  120. package/dist/providers/ConfiguredEmbeddings.d.ts.map +1 -0
  121. package/dist/providers/ConfiguredEmbeddings.js +87 -0
  122. package/dist/providers/ConfiguredModelScope.d.ts +69 -0
  123. package/dist/providers/ConfiguredModelScope.d.ts.map +1 -0
  124. package/dist/providers/ConfiguredModelScope.js +113 -0
  125. package/dist/providers/ConfiguredProvider.d.ts +79 -0
  126. package/dist/providers/ConfiguredProvider.d.ts.map +1 -0
  127. package/dist/providers/ConfiguredProvider.js +238 -0
  128. package/dist/providers/ModelServiceError.d.ts +10 -0
  129. package/dist/providers/ModelServiceError.d.ts.map +1 -0
  130. package/dist/providers/ModelServiceError.js +45 -0
  131. package/dist/providers/MswarmWorkerProvider.d.ts.map +1 -1
  132. package/dist/providers/MswarmWorkerProvider.js +4 -0
  133. package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
  134. package/dist/providers/OpenAiCompatibleProvider.js +134 -52
  135. package/dist/providers/ProviderTypes.d.ts +14 -0
  136. package/dist/providers/ProviderTypes.d.ts.map +1 -1
  137. package/dist/runtime/CodaliJobRuntime.d.ts +6 -0
  138. package/dist/runtime/CodaliJobRuntime.d.ts.map +1 -1
  139. package/dist/runtime/CodaliJobRuntime.js +36 -1
  140. package/dist/runtime/CodaliRuntime.d.ts +30 -0
  141. package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
  142. package/dist/runtime/CodaliRuntime.js +71 -1
  143. package/dist/runtime/CompletionReport.d.ts +5 -0
  144. package/dist/runtime/CompletionReport.d.ts.map +1 -0
  145. package/dist/runtime/CompletionReport.js +12 -0
  146. package/dist/runtime/ProgressTracker.d.ts +17 -0
  147. package/dist/runtime/ProgressTracker.d.ts.map +1 -0
  148. package/dist/runtime/ProgressTracker.js +27 -0
  149. package/dist/runtime/RequestedTestEvidence.d.ts +12 -0
  150. package/dist/runtime/RequestedTestEvidence.d.ts.map +1 -0
  151. package/dist/runtime/RequestedTestEvidence.js +16 -0
  152. package/dist/runtime/RequiredRetrieval.d.ts +13 -0
  153. package/dist/runtime/RequiredRetrieval.d.ts.map +1 -0
  154. package/dist/runtime/RequiredRetrieval.js +25 -0
  155. package/dist/runtime/ReviewSource.d.ts +6 -0
  156. package/dist/runtime/ReviewSource.d.ts.map +1 -0
  157. package/dist/runtime/ReviewSource.js +38 -0
  158. package/dist/runtime/Runner.d.ts +13 -0
  159. package/dist/runtime/Runner.d.ts.map +1 -1
  160. package/dist/runtime/Runner.js +220 -158
  161. package/dist/runtime/SecretRedaction.d.ts +11 -0
  162. package/dist/runtime/SecretRedaction.d.ts.map +1 -0
  163. package/dist/runtime/SecretRedaction.js +85 -0
  164. package/dist/runtime/SessionController.d.ts +148 -0
  165. package/dist/runtime/SessionController.d.ts.map +1 -0
  166. package/dist/runtime/SessionController.js +1356 -0
  167. package/dist/runtime/StructuralEvidence.d.ts +22 -0
  168. package/dist/runtime/StructuralEvidence.d.ts.map +1 -0
  169. package/dist/runtime/StructuralEvidence.js +87 -0
  170. package/dist/runtime/TaskReviewContext.d.ts +34 -0
  171. package/dist/runtime/TaskReviewContext.d.ts.map +1 -0
  172. package/dist/runtime/TaskReviewContext.js +34 -0
  173. package/dist/runtime/ValidationOutput.d.ts +5 -0
  174. package/dist/runtime/ValidationOutput.d.ts.map +1 -0
  175. package/dist/runtime/ValidationOutput.js +32 -0
  176. package/dist/runtime/VerificationCommand.d.ts +3 -0
  177. package/dist/runtime/VerificationCommand.d.ts.map +1 -0
  178. package/dist/runtime/VerificationCommand.js +48 -0
  179. package/dist/runtime/WorkStatusPage.d.ts +15 -0
  180. package/dist/runtime/WorkStatusPage.d.ts.map +1 -0
  181. package/dist/runtime/WorkStatusPage.js +58 -0
  182. package/dist/runtime/WorkspaceLedger.d.ts +13 -0
  183. package/dist/runtime/WorkspaceLedger.d.ts.map +1 -0
  184. package/dist/runtime/WorkspaceLedger.js +145 -0
  185. package/dist/runtime/WorkspaceLock.d.ts.map +1 -1
  186. package/dist/runtime/WorkspaceLock.js +20 -17
  187. package/dist/runtime/WorkspaceSnapshot.d.ts +13 -0
  188. package/dist/runtime/WorkspaceSnapshot.d.ts.map +1 -0
  189. package/dist/runtime/WorkspaceSnapshot.js +88 -0
  190. package/dist/session/AtomicState.d.ts +13 -0
  191. package/dist/session/AtomicState.d.ts.map +1 -0
  192. package/dist/session/AtomicState.js +109 -0
  193. package/dist/session/SessionStoragePath.d.ts +15 -0
  194. package/dist/session/SessionStoragePath.d.ts.map +1 -0
  195. package/dist/session/SessionStoragePath.js +89 -0
  196. package/dist/session/SessionStore.d.ts +9 -0
  197. package/dist/session/SessionStore.d.ts.map +1 -1
  198. package/dist/session/SessionStore.js +181 -47
  199. package/dist/session/WorkState.d.ts +23 -0
  200. package/dist/session/WorkState.d.ts.map +1 -0
  201. package/dist/session/WorkState.js +367 -0
  202. package/dist/session/WorkTypes.d.ts +94 -0
  203. package/dist/session/WorkTypes.d.ts.map +1 -0
  204. package/dist/session/WorkTypes.js +1 -0
  205. package/dist/tools/ToolRegistry.d.ts +2 -0
  206. package/dist/tools/ToolRegistry.d.ts.map +1 -1
  207. package/dist/tools/ToolRegistry.js +12 -2
  208. package/dist/tools/ToolTypes.d.ts +16 -0
  209. package/dist/tools/ToolTypes.d.ts.map +1 -1
  210. package/dist/tools/docdex/DocdexTools.d.ts +2 -0
  211. package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
  212. package/dist/tools/docdex/DocdexTools.js +17 -2
  213. package/dist/tools/filesystem/FileTools.d.ts.map +1 -1
  214. package/dist/tools/filesystem/FileTools.js +30 -10
  215. package/dist/tools/shell/AsyncProcess.d.ts +16 -0
  216. package/dist/tools/shell/AsyncProcess.d.ts.map +1 -0
  217. package/dist/tools/shell/AsyncProcess.js +94 -0
  218. package/dist/tools/shell/DarwinRuntimeLibraries.d.ts +13 -0
  219. package/dist/tools/shell/DarwinRuntimeLibraries.d.ts.map +1 -0
  220. package/dist/tools/shell/DarwinRuntimeLibraries.js +130 -0
  221. package/dist/tools/shell/HostToolchain.d.ts +19 -0
  222. package/dist/tools/shell/HostToolchain.d.ts.map +1 -0
  223. package/dist/tools/shell/HostToolchain.js +264 -0
  224. package/dist/tools/shell/IsolatedExecutor.d.ts +15 -0
  225. package/dist/tools/shell/IsolatedExecutor.d.ts.map +1 -0
  226. package/dist/tools/shell/IsolatedExecutor.js +134 -0
  227. package/dist/tools/shell/ShellTool.d.ts.map +1 -1
  228. package/dist/tools/shell/ShellTool.js +9 -29
  229. package/dist/tools/shell/ShellWorkingDirectory.d.ts +3 -0
  230. package/dist/tools/shell/ShellWorkingDirectory.d.ts.map +1 -0
  231. package/dist/tools/shell/ShellWorkingDirectory.js +30 -0
  232. 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":"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"}
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. Retrying is safe here because Phase 2
63
- * connectors are read-only.
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;AA0B/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;IA4BlB,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"}
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
- await new Promise((resolve) => this.waiting.push(resolve));
34
- this.active += 1;
35
- return () => this.release();
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. Retrying is safe here because Phase 2
136
- * connectors are read-only.
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
- for (let attempt = 1; attempt <= MAX_CALL_ATTEMPTS; attempt += 1) {
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 === MAX_CALL_ATTEMPTS)
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"}