@martinloop/mcp 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/package-version.d.ts +1 -1
  2. package/dist/package-version.js +1 -1
  3. package/dist/vendor/adapters/claude-cli.d.ts +11 -1
  4. package/dist/vendor/adapters/claude-cli.js +285 -71
  5. package/dist/vendor/adapters/cli-bridge.d.ts +12 -3
  6. package/dist/vendor/adapters/cli-bridge.js +129 -149
  7. package/dist/vendor/adapters/codex-cli.d.ts +3 -3
  8. package/dist/vendor/adapters/codex-cli.js +10 -24
  9. package/dist/vendor/adapters/codex-host.js +127 -132
  10. package/dist/vendor/adapters/index.d.ts +2 -1
  11. package/dist/vendor/adapters/index.js +2 -1
  12. package/dist/vendor/adapters/openai-compatible.js +88 -6
  13. package/dist/vendor/adapters/process-supervisor.d.ts +60 -0
  14. package/dist/vendor/adapters/process-supervisor.js +541 -0
  15. package/dist/vendor/adapters/verifier-only.d.ts +10 -1
  16. package/dist/vendor/adapters/verifier-only.js +43 -1
  17. package/dist/vendor/contracts/external-outcome.d.ts +102 -0
  18. package/dist/vendor/contracts/external-outcome.js +226 -0
  19. package/dist/vendor/contracts/index.d.ts +8 -1
  20. package/dist/vendor/contracts/index.js +2 -0
  21. package/dist/vendor/contracts/swarm-hosted.d.ts +204 -0
  22. package/dist/vendor/contracts/swarm-hosted.js +629 -0
  23. package/dist/vendor/contracts/swarm.d.ts +449 -0
  24. package/dist/vendor/contracts/swarm.js +550 -0
  25. package/dist/vendor/core/external-outcome.d.ts +34 -0
  26. package/dist/vendor/core/external-outcome.js +515 -0
  27. package/dist/vendor/core/grounding.js +1 -1
  28. package/dist/vendor/core/index.d.ts +47 -1
  29. package/dist/vendor/core/index.js +53 -3
  30. package/dist/vendor/core/leash.d.ts +1 -0
  31. package/dist/vendor/core/leash.js +13 -0
  32. package/dist/vendor/core/persistence/integrity.d.ts +4 -0
  33. package/dist/vendor/core/persistence/integrity.js +19 -6
  34. package/dist/vendor/core/persistence/swarm-integrity.d.ts +63 -0
  35. package/dist/vendor/core/persistence/swarm-integrity.js +309 -0
  36. package/dist/vendor/core/swarm/candidates.d.ts +78 -0
  37. package/dist/vendor/core/swarm/candidates.js +810 -0
  38. package/dist/vendor/core/swarm/evidence-projection.d.ts +87 -0
  39. package/dist/vendor/core/swarm/evidence-projection.js +128 -0
  40. package/dist/vendor/core/swarm/evidence.d.ts +40 -0
  41. package/dist/vendor/core/swarm/evidence.js +575 -0
  42. package/dist/vendor/core/swarm/hosted-export.d.ts +48 -0
  43. package/dist/vendor/core/swarm/hosted-export.js +362 -0
  44. package/dist/vendor/core/swarm/hosted-projection.d.ts +5 -0
  45. package/dist/vendor/core/swarm/hosted-projection.js +258 -0
  46. package/dist/vendor/core/swarm/index.d.ts +110 -0
  47. package/dist/vendor/core/swarm/index.js +1060 -0
  48. package/dist/vendor/core/swarm/integration.d.ts +76 -0
  49. package/dist/vendor/core/swarm/integration.js +615 -0
  50. package/dist/vendor/core/swarm/lifecycle.d.ts +72 -0
  51. package/dist/vendor/core/swarm/lifecycle.js +252 -0
  52. package/dist/vendor/core/swarm/live-runtime.d.ts +78 -0
  53. package/dist/vendor/core/swarm/live-runtime.js +1482 -0
  54. package/dist/vendor/core/swarm/live-store.d.ts +99 -0
  55. package/dist/vendor/core/swarm/live-store.js +860 -0
  56. package/dist/vendor/core/swarm/operational-reader.d.ts +17 -0
  57. package/dist/vendor/core/swarm/operational-reader.js +96 -0
  58. package/dist/vendor/core/swarm/operations.d.ts +29 -0
  59. package/dist/vendor/core/swarm/operations.js +128 -0
  60. package/dist/vendor/core/swarm/parent-receipt.d.ts +13 -0
  61. package/dist/vendor/core/swarm/parent-receipt.js +489 -0
  62. package/dist/vendor/core/swarm/receipt-projection.d.ts +26 -0
  63. package/dist/vendor/core/swarm/receipt-projection.js +120 -0
  64. package/dist/vendor/core/swarm/scheduler.d.ts +92 -0
  65. package/dist/vendor/core/swarm/scheduler.js +555 -0
  66. package/dist/vendor/core/swarm/verification.d.ts +70 -0
  67. package/dist/vendor/core/swarm/verification.js +295 -0
  68. package/dist/vendor/core/swarm/workspaces.d.ts +77 -0
  69. package/dist/vendor/core/swarm/workspaces.js +488 -0
  70. package/dist/vendor/core/verified-handoff.d.ts +4 -0
  71. package/dist/vendor/core/verified-handoff.js +4 -0
  72. package/package.json +1 -1
  73. package/server.json +2 -2
@@ -0,0 +1,92 @@
1
+ import type { SwarmBudgetLedger, SwarmBudgetLease, SwarmDependencyWaiver, SwarmParentContract, SwarmTaskNode } from "../../contracts/index.js";
2
+ export interface SwarmSchedulerError {
3
+ code: string;
4
+ taskId?: string;
5
+ message: string;
6
+ }
7
+ export type SwarmGraphValidationResult = {
8
+ ok: true;
9
+ errors: [];
10
+ } | {
11
+ ok: false;
12
+ errors: SwarmSchedulerError[];
13
+ };
14
+ export type SwarmBatchResult = {
15
+ ok: true;
16
+ tasks: SwarmTaskNode[];
17
+ errors: [];
18
+ } | {
19
+ ok: false;
20
+ tasks: [];
21
+ errors: SwarmSchedulerError[];
22
+ };
23
+ export type SwarmBudgetTransitionResult = {
24
+ ok: true;
25
+ ledger: SwarmBudgetLedger;
26
+ lease: SwarmBudgetLease;
27
+ errors: [];
28
+ } | {
29
+ ok: false;
30
+ ledger: SwarmBudgetLedger;
31
+ errors: SwarmSchedulerError[];
32
+ };
33
+ export interface SwarmBudgetProtectedUsage {
34
+ readonly usd: number;
35
+ readonly tokens: number;
36
+ }
37
+ export type ParentDependencyWaiverRegistry = Readonly<{
38
+ kind: "parent_dependency_waiver_registry";
39
+ }>;
40
+ export type IssuedDependencyWaiverCapability = Readonly<{
41
+ kind: "issued_dependency_waiver";
42
+ }>;
43
+ export type DependencyWaiverIssuanceResult = {
44
+ ok: true;
45
+ capability: IssuedDependencyWaiverCapability;
46
+ evidence: SwarmDependencyWaiver;
47
+ errors: [];
48
+ } | {
49
+ ok: false;
50
+ errors: SwarmSchedulerError[];
51
+ };
52
+ export declare function createParentDependencyWaiverRegistry(input: {
53
+ swarmId: string;
54
+ parentPolicyVersion: string;
55
+ dependencyWaiversAllowed: boolean;
56
+ }): ParentDependencyWaiverRegistry;
57
+ export declare function issueParentDependencyWaiver(registry: ParentDependencyWaiverRegistry, evidence: SwarmDependencyWaiver): DependencyWaiverIssuanceResult;
58
+ export declare function createSwarmBudgetLedger(input: {
59
+ capUsd: number;
60
+ capTokens?: number;
61
+ }): SwarmBudgetLedger;
62
+ export declare function reserveSwarmBudgetLease(ledger: SwarmBudgetLedger, input: Omit<SwarmBudgetLease, "status" | "actualUsage">): SwarmBudgetTransitionResult;
63
+ /**
64
+ * Extends one live lease by atomically reclaiming only the unobserved portion
65
+ * of sibling reservations. This keeps initial fair-share reservations intact
66
+ * until a child actually needs more capacity, while ensuring every live
67
+ * observation remains protected and total granted capacity never exceeds the
68
+ * parent cap.
69
+ */
70
+ export declare function extendSwarmBudgetLease(ledger: SwarmBudgetLedger, input: {
71
+ leaseId: string;
72
+ requiredUsd: number;
73
+ requiredTokens: number;
74
+ protectedUsage: Readonly<Record<string, SwarmBudgetProtectedUsage>>;
75
+ }): SwarmBudgetTransitionResult;
76
+ export declare function settleSwarmBudgetLease(ledger: SwarmBudgetLedger, input: {
77
+ leaseId: string;
78
+ actualUsd: number;
79
+ actualTokens: number;
80
+ provenance?: string;
81
+ }): SwarmBudgetTransitionResult;
82
+ export declare function releaseSwarmBudgetLease(ledger: SwarmBudgetLedger, leaseId: string): SwarmBudgetTransitionResult;
83
+ export declare function validateSwarmTaskGraph(tasks: readonly SwarmTaskNode[]): SwarmGraphValidationResult;
84
+ export declare function selectNextSwarmBatch(input: {
85
+ swarmId: string;
86
+ tasks: readonly SwarmTaskNode[];
87
+ maxConcurrency: number;
88
+ parentContract: Pick<SwarmParentContract, "policyVersion" | "recoveryPolicy" | "maxConcurrency">;
89
+ dependencyWaiverRegistry?: ParentDependencyWaiverRegistry;
90
+ dependencyWaiverCapabilities?: readonly IssuedDependencyWaiverCapability[];
91
+ }): SwarmBatchResult;
92
+ export declare function tasksHaveScopeCollision(left: SwarmTaskNode, right: SwarmTaskNode): boolean;
@@ -0,0 +1,555 @@
1
+ import { normalizeSwarmPathPattern, swarmPathPatternsOverlap } from "../../contracts/index.js";
2
+ const parentWaiverRegistries = new WeakMap();
3
+ const issuedWaiverCapabilities = new WeakMap();
4
+ export function createParentDependencyWaiverRegistry(input) {
5
+ const registry = Object.freeze({
6
+ kind: "parent_dependency_waiver_registry"
7
+ });
8
+ parentWaiverRegistries.set(registry, { ...input });
9
+ return registry;
10
+ }
11
+ export function issueParentDependencyWaiver(registry, evidence) {
12
+ const registryState = parentWaiverRegistries.get(registry);
13
+ if (!registryState) {
14
+ return {
15
+ ok: false,
16
+ errors: [{
17
+ code: "UNISSUED_PARENT_WAIVER_REGISTRY",
18
+ message: "Dependency waivers require an internally issued parent registry."
19
+ }]
20
+ };
21
+ }
22
+ if (!registryState.dependencyWaiversAllowed) {
23
+ return {
24
+ ok: false,
25
+ errors: [{
26
+ code: "DEPENDENCY_WAIVERS_DISABLED",
27
+ message: "Parent recovery policy does not allow dependency waivers."
28
+ }]
29
+ };
30
+ }
31
+ if (evidence.parentPolicyVersion !== registryState.parentPolicyVersion) {
32
+ return {
33
+ ok: false,
34
+ errors: [{
35
+ code: "WAIVER_POLICY_VERSION_MISMATCH",
36
+ taskId: evidence.taskId,
37
+ message: "Dependency waiver is not bound to the issuing parent policy version."
38
+ }]
39
+ };
40
+ }
41
+ if (!hasText(evidence.taskId)
42
+ || !hasText(evidence.dependencyTaskId)
43
+ || !hasText(evidence.approvedBy)
44
+ || !hasText(evidence.approvedAt)
45
+ || Number.isNaN(Date.parse(evidence.approvedAt))) {
46
+ return {
47
+ ok: false,
48
+ errors: [{
49
+ code: "INVALID_DEPENDENCY_WAIVER",
50
+ taskId: evidence.taskId,
51
+ message: "Dependency waiver evidence requires task, dependency, approval identity, and approval time."
52
+ }]
53
+ };
54
+ }
55
+ const capability = Object.freeze({
56
+ kind: "issued_dependency_waiver"
57
+ });
58
+ const clonedEvidence = { ...evidence };
59
+ issuedWaiverCapabilities.set(capability, { registry, evidence: clonedEvidence });
60
+ return { ok: true, capability, evidence: clonedEvidence, errors: [] };
61
+ }
62
+ export function createSwarmBudgetLedger(input) {
63
+ return {
64
+ capUsd: input.capUsd,
65
+ settledUsd: 0,
66
+ settledTokens: 0,
67
+ leases: [],
68
+ ...(input.capTokens !== undefined ? { capTokens: input.capTokens } : {})
69
+ };
70
+ }
71
+ export function reserveSwarmBudgetLease(ledger, input) {
72
+ if (!validateBudgetLedger(ledger)) {
73
+ return budgetError(ledger, "INVALID_BUDGET_LEDGER", "Budget caps and settled totals must be nonnegative finite values within their caps.");
74
+ }
75
+ if (!hasText(input.leaseId) || !hasText(input.agentId) || !hasText(input.taskId)) {
76
+ return budgetError(ledger, "INVALID_LEASE_IDENTITY", "Lease, agent, and task identifiers must be nonempty.");
77
+ }
78
+ if (!isNonnegativeFinite(input.reservedUsd) || !isNonnegativeInteger(input.reservedTokens)) {
79
+ return budgetError(ledger, "INVALID_LEASE_AMOUNT", "Lease reservations must be nonnegative finite values.");
80
+ }
81
+ if (ledger.leases.some((lease) => lease.leaseId === input.leaseId)) {
82
+ return budgetError(ledger, "DUPLICATE_LEASE_ID", `Lease ${input.leaseId} already exists.`);
83
+ }
84
+ const active = activeReservations(ledger);
85
+ if (ledger.settledUsd + active.usd + input.reservedUsd > ledger.capUsd
86
+ || (ledger.capTokens !== undefined
87
+ && ledger.settledTokens + active.tokens + input.reservedTokens > ledger.capTokens)) {
88
+ return budgetError(ledger, "GLOBAL_BUDGET_EXCEEDED", "The reservation would exceed the parent budget cap.");
89
+ }
90
+ const lease = { ...input, status: "reserved" };
91
+ return {
92
+ ok: true,
93
+ ledger: { ...ledger, leases: [...ledger.leases.map(cloneLease), lease] },
94
+ lease,
95
+ errors: []
96
+ };
97
+ }
98
+ /**
99
+ * Extends one live lease by atomically reclaiming only the unobserved portion
100
+ * of sibling reservations. This keeps initial fair-share reservations intact
101
+ * until a child actually needs more capacity, while ensuring every live
102
+ * observation remains protected and total granted capacity never exceeds the
103
+ * parent cap.
104
+ */
105
+ export function extendSwarmBudgetLease(ledger, input) {
106
+ if (!validateBudgetLedger(ledger)) {
107
+ return budgetError(ledger, "INVALID_BUDGET_LEDGER", "Budget caps and settled totals must be nonnegative finite values within their caps.");
108
+ }
109
+ const target = ledger.leases.find((lease) => lease.leaseId === input.leaseId);
110
+ if (!target)
111
+ return budgetError(ledger, "LEASE_NOT_FOUND", `Lease ${input.leaseId} does not exist.`);
112
+ if (target.status !== "reserved") {
113
+ return budgetError(ledger, "LEASE_ALREADY_FINAL", `Lease ${input.leaseId} is already ${target.status}.`);
114
+ }
115
+ if (!isNonnegativeFinite(input.requiredUsd) || !isNonnegativeInteger(input.requiredTokens)) {
116
+ return budgetError(ledger, "INVALID_LEASE_AMOUNT", "Required live usage must use nonnegative finite values.");
117
+ }
118
+ for (const [leaseId, usage] of Object.entries(input.protectedUsage)) {
119
+ const lease = ledger.leases.find((candidate) => candidate.leaseId === leaseId);
120
+ if (!lease || lease.status !== "reserved") {
121
+ return budgetError(ledger, "INVALID_PROTECTED_USAGE", `Protected usage references non-active lease ${leaseId}.`);
122
+ }
123
+ if (!isNonnegativeFinite(usage.usd) || !isNonnegativeInteger(usage.tokens)) {
124
+ return budgetError(ledger, "INVALID_PROTECTED_USAGE", `Protected usage for lease ${leaseId} is invalid.`);
125
+ }
126
+ }
127
+ const protectedTarget = input.protectedUsage[input.leaseId];
128
+ const requiredUsd = Math.max(input.requiredUsd, protectedTarget?.usd ?? 0, target.reservedUsd);
129
+ const requiredTokens = Math.max(input.requiredTokens, protectedTarget?.tokens ?? 0, target.reservedTokens);
130
+ if (requiredUsd > ledger.capUsd || (ledger.capTokens !== undefined && requiredTokens > ledger.capTokens)) {
131
+ return budgetError(ledger, "GLOBAL_BUDGET_EXCEEDED", "The live usage observation exceeds the parent budget cap.");
132
+ }
133
+ const nextLeases = ledger.leases.map(cloneLease);
134
+ let usdNeeded = requiredUsd - target.reservedUsd;
135
+ let tokensNeeded = requiredTokens - target.reservedTokens;
136
+ const active = activeReservations(ledger);
137
+ usdNeeded = Math.max(0, usdNeeded - Math.max(0, ledger.capUsd - ledger.settledUsd - active.usd));
138
+ tokensNeeded = Math.max(0, tokensNeeded - Math.max(0, (ledger.capTokens ?? Number.MAX_SAFE_INTEGER) - ledger.settledTokens - active.tokens));
139
+ for (const sibling of [...nextLeases]
140
+ .filter((lease) => lease.status === "reserved" && lease.leaseId !== input.leaseId)
141
+ .sort((left, right) => left.leaseId.localeCompare(right.leaseId))) {
142
+ const floor = input.protectedUsage[sibling.leaseId] ?? { usd: 0, tokens: 0 };
143
+ const reclaimedUsd = Math.min(usdNeeded, Math.max(0, sibling.reservedUsd - floor.usd));
144
+ const reclaimedTokens = Math.min(tokensNeeded, Math.max(0, sibling.reservedTokens - floor.tokens));
145
+ sibling.reservedUsd -= reclaimedUsd;
146
+ sibling.reservedTokens -= reclaimedTokens;
147
+ usdNeeded -= reclaimedUsd;
148
+ tokensNeeded -= reclaimedTokens;
149
+ if (nearlyEqual(usdNeeded, 0) && tokensNeeded === 0)
150
+ break;
151
+ }
152
+ if (!nearlyEqual(usdNeeded, 0) || tokensNeeded !== 0) {
153
+ return budgetError(ledger, "GLOBAL_BUDGET_EXCEEDED", "Observed live usage cannot be granted without exceeding the parent budget cap.");
154
+ }
155
+ const nextTarget = nextLeases.find((lease) => lease.leaseId === input.leaseId);
156
+ nextTarget.reservedUsd = requiredUsd;
157
+ nextTarget.reservedTokens = requiredTokens;
158
+ const nextLedger = { ...ledger, leases: nextLeases };
159
+ if (!validateBudgetLedger(nextLedger)) {
160
+ return budgetError(ledger, "GLOBAL_BUDGET_EXCEEDED", "The elastic lease transition would violate the parent budget cap.");
161
+ }
162
+ return { ok: true, ledger: nextLedger, lease: cloneLease(nextTarget), errors: [] };
163
+ }
164
+ export function settleSwarmBudgetLease(ledger, input) {
165
+ if (!validateBudgetLedger(ledger)) {
166
+ return budgetError(ledger, "INVALID_BUDGET_LEDGER", "Budget caps and settled totals must be nonnegative finite values within their caps.");
167
+ }
168
+ const lease = ledger.leases.find((candidate) => candidate.leaseId === input.leaseId);
169
+ if (!lease)
170
+ return budgetError(ledger, "LEASE_NOT_FOUND", `Lease ${input.leaseId} does not exist.`);
171
+ if (lease.status !== "reserved") {
172
+ return budgetError(ledger, "LEASE_ALREADY_FINAL", `Lease ${input.leaseId} is already ${lease.status}.`);
173
+ }
174
+ if (!isNonnegativeFinite(input.actualUsd) || !isNonnegativeInteger(input.actualTokens)) {
175
+ return budgetError(ledger, "INVALID_SETTLEMENT_AMOUNT", "Actual usage must use nonnegative finite values.");
176
+ }
177
+ const leaseOverspent = input.actualUsd > lease.reservedUsd || input.actualTokens > lease.reservedTokens;
178
+ const settledLease = {
179
+ ...lease,
180
+ status: leaseOverspent ? "overspent" : "settled",
181
+ actualUsage: {
182
+ usd: input.actualUsd,
183
+ tokens: input.actualTokens,
184
+ ...(input.provenance !== undefined ? { provenance: input.provenance } : {})
185
+ }
186
+ };
187
+ const nextLedger = {
188
+ ...ledger,
189
+ settledUsd: ledger.settledUsd + input.actualUsd,
190
+ settledTokens: ledger.settledTokens + input.actualTokens,
191
+ leases: ledger.leases.map((candidate) => candidate.leaseId === input.leaseId ? settledLease : cloneLease(candidate))
192
+ };
193
+ if (leaseOverspent) {
194
+ return budgetError(nextLedger, "LEASE_OVERSPEND", "Actual usage exceeded the reserved lease and was recorded as terminal spend.");
195
+ }
196
+ return { ok: true, ledger: nextLedger, lease: settledLease, errors: [] };
197
+ }
198
+ export function releaseSwarmBudgetLease(ledger, leaseId) {
199
+ if (!validateBudgetLedger(ledger)) {
200
+ return budgetError(ledger, "INVALID_BUDGET_LEDGER", "Budget caps and settled totals must be nonnegative finite values within their caps.");
201
+ }
202
+ const lease = ledger.leases.find((candidate) => candidate.leaseId === leaseId);
203
+ if (!lease)
204
+ return budgetError(ledger, "LEASE_NOT_FOUND", `Lease ${leaseId} does not exist.`);
205
+ if (lease.status !== "reserved") {
206
+ return budgetError(ledger, "LEASE_ALREADY_FINAL", `Lease ${leaseId} is already ${lease.status}.`);
207
+ }
208
+ const releasedLease = { ...lease, status: "released" };
209
+ return {
210
+ ok: true,
211
+ ledger: {
212
+ ...ledger,
213
+ leases: ledger.leases.map((candidate) => candidate.leaseId === leaseId ? releasedLease : cloneLease(candidate))
214
+ },
215
+ lease: releasedLease,
216
+ errors: []
217
+ };
218
+ }
219
+ export function validateSwarmTaskGraph(tasks) {
220
+ const errors = [];
221
+ const byId = new Map();
222
+ for (const task of tasks) {
223
+ if (byId.has(task.taskId)) {
224
+ errors.push({
225
+ code: "DUPLICATE_TASK_ID",
226
+ taskId: task.taskId,
227
+ message: `Duplicate task identifier: ${task.taskId}`
228
+ });
229
+ }
230
+ else {
231
+ byId.set(task.taskId, task);
232
+ }
233
+ if (task.mutationMode === "read_only" && task.writeScope.length > 0) {
234
+ errors.push({
235
+ code: "READ_ONLY_SCOPE_CONFLICT",
236
+ taskId: task.taskId,
237
+ message: "Read-only tasks cannot declare write scope."
238
+ });
239
+ }
240
+ if (task.mutationMode !== "read_only" && task.mutationMode !== "write") {
241
+ errors.push({
242
+ code: "INVALID_MUTATION_MODE",
243
+ taskId: task.taskId,
244
+ message: "Tasks must explicitly declare read_only or write mutation mode."
245
+ });
246
+ }
247
+ else if (task.mutationMode === "write" && task.writeScope.length === 0) {
248
+ errors.push({
249
+ code: "WRITE_SCOPE_REQUIRED",
250
+ taskId: task.taskId,
251
+ message: "Write tasks must declare at least one write scope pattern."
252
+ });
253
+ }
254
+ if (task.writeScope.some((pattern) => !normalizeSwarmPathPattern(pattern).ok)) {
255
+ errors.push({
256
+ code: "INVALID_WRITE_SCOPE",
257
+ taskId: task.taskId,
258
+ message: "Task write scope contains an unsafe or malformed repository path pattern."
259
+ });
260
+ }
261
+ }
262
+ for (const task of tasks) {
263
+ for (const dependencyId of task.dependsOn) {
264
+ if (dependencyId === task.taskId) {
265
+ errors.push({
266
+ code: "SELF_DEPENDENCY",
267
+ taskId: task.taskId,
268
+ message: `Task ${task.taskId} cannot depend on itself.`
269
+ });
270
+ }
271
+ else if (!byId.has(dependencyId)) {
272
+ errors.push({
273
+ code: "MISSING_DEPENDENCY",
274
+ taskId: task.taskId,
275
+ message: `Task ${task.taskId} depends on missing task ${dependencyId}.`
276
+ });
277
+ }
278
+ }
279
+ }
280
+ if (!errors.some((error) => error.code === "DUPLICATE_TASK_ID" || error.code === "MISSING_DEPENDENCY")) {
281
+ const visiting = new Set();
282
+ const visited = new Set();
283
+ const visit = (taskId) => {
284
+ if (visiting.has(taskId))
285
+ return true;
286
+ if (visited.has(taskId))
287
+ return false;
288
+ visiting.add(taskId);
289
+ for (const dependencyId of byId.get(taskId)?.dependsOn ?? []) {
290
+ if (visit(dependencyId))
291
+ return true;
292
+ }
293
+ visiting.delete(taskId);
294
+ visited.add(taskId);
295
+ return false;
296
+ };
297
+ for (const taskId of [...byId.keys()].sort()) {
298
+ if (visit(taskId)) {
299
+ errors.push({
300
+ code: "TASK_CYCLE",
301
+ taskId,
302
+ message: `Task graph contains a dependency cycle involving ${taskId}.`
303
+ });
304
+ break;
305
+ }
306
+ }
307
+ }
308
+ return errors.length === 0 ? { ok: true, errors: [] } : { ok: false, errors };
309
+ }
310
+ export function selectNextSwarmBatch(input) {
311
+ const graph = validateSwarmTaskGraph(input.tasks);
312
+ const errors = [...graph.errors];
313
+ if (!hasText(input.swarmId)) {
314
+ errors.push({
315
+ code: "INVALID_SWARM_ID",
316
+ message: "Active swarmId is required for scheduler authority binding."
317
+ });
318
+ }
319
+ if (!Number.isInteger(input.maxConcurrency) || input.maxConcurrency <= 0) {
320
+ errors.push({
321
+ code: "INVALID_CONCURRENCY",
322
+ message: "maxConcurrency must be a positive integer."
323
+ });
324
+ }
325
+ if (!Number.isInteger(input.parentContract.maxConcurrency) || input.parentContract.maxConcurrency <= 0) {
326
+ errors.push({
327
+ code: "INVALID_PARENT_CONCURRENCY",
328
+ message: "Parent maxConcurrency must be a positive integer."
329
+ });
330
+ }
331
+ const waiverValidation = validateDependencyWaiverCapabilities(input.swarmId, input.tasks, input.parentContract, input.dependencyWaiverRegistry, input.dependencyWaiverCapabilities ?? []);
332
+ errors.push(...waiverValidation.errors);
333
+ if (errors.length > 0)
334
+ return { ok: false, tasks: [], errors };
335
+ const byId = new Map(input.tasks.map((task) => [task.taskId, task]));
336
+ const running = input.tasks.filter((task) => task.status === "running");
337
+ if (running.length > input.parentContract.maxConcurrency) {
338
+ return {
339
+ ok: false,
340
+ tasks: [],
341
+ errors: [{
342
+ code: "PARENT_CONCURRENCY_EXCEEDED",
343
+ message: "Active work already exceeds the parent concurrency cap."
344
+ }]
345
+ };
346
+ }
347
+ const effectiveConcurrency = Math.min(input.maxConcurrency, input.parentContract.maxConcurrency);
348
+ const availableSlots = Math.max(effectiveConcurrency - running.length, 0);
349
+ if (availableSlots === 0)
350
+ return { ok: true, tasks: [], errors: [] };
351
+ const ready = input.tasks
352
+ .filter((task) => task.status === "queued" || task.status === "ready")
353
+ .filter((task) => task.dependsOn.every((dependencyId) => {
354
+ if (waiverValidation.evidence.some((waiver) => waiver.taskId === task.taskId
355
+ && waiver.dependencyTaskId === dependencyId
356
+ && waiver.parentPolicyVersion === input.parentContract.policyVersion))
357
+ return true;
358
+ const dependency = byId.get(dependencyId);
359
+ return dependency?.status === "accepted";
360
+ }))
361
+ .sort((left, right) => left.taskId.localeCompare(right.taskId));
362
+ const admitted = [];
363
+ const activeMutations = running.filter(isMutatingTask);
364
+ for (const candidate of ready) {
365
+ if (admitted.length >= availableSlots)
366
+ break;
367
+ if (isMutatingTask(candidate)
368
+ && [...activeMutations, ...admitted.filter(isMutatingTask)].some((active) => tasksHaveScopeCollision(candidate, active))) {
369
+ continue;
370
+ }
371
+ admitted.push(candidate);
372
+ }
373
+ return { ok: true, tasks: admitted, errors: [] };
374
+ }
375
+ export function tasksHaveScopeCollision(left, right) {
376
+ if (!isMutatingTask(left) || !isMutatingTask(right))
377
+ return false;
378
+ return left.writeScope.some((leftPath) => right.writeScope.some((rightPath) => swarmPathPatternsOverlap(leftPath, rightPath)));
379
+ }
380
+ function isMutatingTask(task) {
381
+ return task.mutationMode === "write";
382
+ }
383
+ function activeReservations(ledger) {
384
+ return ledger.leases.reduce((total, lease) => lease.status === "reserved"
385
+ ? { usd: total.usd + lease.reservedUsd, tokens: total.tokens + lease.reservedTokens }
386
+ : total, { usd: 0, tokens: 0 });
387
+ }
388
+ function budgetError(ledger, code, message) {
389
+ return {
390
+ ok: false,
391
+ ledger: cloneLedger(ledger),
392
+ errors: [{ code, message }]
393
+ };
394
+ }
395
+ function cloneLedger(ledger) {
396
+ return { ...ledger, leases: ledger.leases.map(cloneLease) };
397
+ }
398
+ function cloneLease(lease) {
399
+ return {
400
+ ...lease,
401
+ ...(lease.actualUsage ? { actualUsage: { ...lease.actualUsage } } : {})
402
+ };
403
+ }
404
+ function isNonnegativeFinite(value) {
405
+ return Number.isFinite(value) && value >= 0;
406
+ }
407
+ function validateBudgetLedger(ledger) {
408
+ if (!isNonnegativeFinite(ledger.capUsd)
409
+ || (ledger.capTokens !== undefined && !isNonnegativeInteger(ledger.capTokens))
410
+ || !isNonnegativeFinite(ledger.settledUsd)
411
+ || !isNonnegativeInteger(ledger.settledTokens)) {
412
+ return false;
413
+ }
414
+ const ids = new Set();
415
+ let settledUsd = 0;
416
+ let settledTokens = 0;
417
+ let reservedUsd = 0;
418
+ let reservedTokens = 0;
419
+ let hasOverspentLease = false;
420
+ for (const lease of ledger.leases) {
421
+ if (!hasText(lease.leaseId) || ids.has(lease.leaseId) || !hasText(lease.agentId) || !hasText(lease.taskId)) {
422
+ return false;
423
+ }
424
+ ids.add(lease.leaseId);
425
+ if (!["reserved", "settled", "overspent", "released"].includes(lease.status))
426
+ return false;
427
+ if (!isNonnegativeFinite(lease.reservedUsd) || !isNonnegativeInteger(lease.reservedTokens))
428
+ return false;
429
+ if (lease.status === "settled" || lease.status === "overspent") {
430
+ if (!lease.actualUsage
431
+ || !isNonnegativeFinite(lease.actualUsage.usd)
432
+ || !isNonnegativeInteger(lease.actualUsage.tokens)) {
433
+ return false;
434
+ }
435
+ const exceededReservation = lease.actualUsage.usd > lease.reservedUsd
436
+ || lease.actualUsage.tokens > lease.reservedTokens;
437
+ if ((lease.status === "settled" && exceededReservation)
438
+ || (lease.status === "overspent" && !exceededReservation)) {
439
+ return false;
440
+ }
441
+ settledUsd += lease.actualUsage.usd;
442
+ settledTokens += lease.actualUsage.tokens;
443
+ if (lease.status === "overspent")
444
+ hasOverspentLease = true;
445
+ }
446
+ else if (lease.actualUsage !== undefined) {
447
+ return false;
448
+ }
449
+ if (lease.status === "reserved") {
450
+ reservedUsd += lease.reservedUsd;
451
+ reservedTokens += lease.reservedTokens;
452
+ }
453
+ }
454
+ return nearlyEqual(ledger.settledUsd, settledUsd)
455
+ && ledger.settledTokens === settledTokens
456
+ // Once a provider reports an overage, the spend is irreversible. Keep the
457
+ // ledger structurally valid so every already-running lease can settle its
458
+ // actual usage and terminal evidence can seal. Admission remains closed:
459
+ // reserveSwarmBudgetLease still rejects any further reservation whose
460
+ // settled + reserved total exceeds the hard cap.
461
+ && (hasOverspentLease || settledUsd + reservedUsd <= ledger.capUsd)
462
+ && (ledger.capTokens === undefined
463
+ || hasOverspentLease
464
+ || settledTokens + reservedTokens <= ledger.capTokens);
465
+ }
466
+ function isNonnegativeInteger(value) {
467
+ return Number.isInteger(value) && value >= 0;
468
+ }
469
+ function nearlyEqual(left, right) {
470
+ return Math.abs(left - right) <= Number.EPSILON * Math.max(1, Math.abs(left), Math.abs(right));
471
+ }
472
+ function hasText(value) {
473
+ return value.trim().length > 0;
474
+ }
475
+ function validateDependencyWaiverCapabilities(activeSwarmId, tasks, parent, registry, capabilities) {
476
+ if (capabilities.length === 0)
477
+ return { errors: [], evidence: [] };
478
+ if (!parent.recoveryPolicy.dependencyWaiversAllowed) {
479
+ return {
480
+ errors: [{
481
+ code: "DEPENDENCY_WAIVERS_DISABLED",
482
+ message: "Parent recovery policy does not allow dependency waivers."
483
+ }],
484
+ evidence: []
485
+ };
486
+ }
487
+ const registryState = registry ? parentWaiverRegistries.get(registry) : undefined;
488
+ if (!registry || !registryState) {
489
+ return {
490
+ errors: [{
491
+ code: "UNISSUED_DEPENDENCY_WAIVER",
492
+ message: "Dependency waiver capabilities require an internally issued parent registry."
493
+ }],
494
+ evidence: []
495
+ };
496
+ }
497
+ if (registryState.swarmId !== activeSwarmId) {
498
+ return {
499
+ errors: [{
500
+ code: "WAIVER_SWARM_MISMATCH",
501
+ message: "Parent waiver registry is not bound to the active swarm."
502
+ }],
503
+ evidence: []
504
+ };
505
+ }
506
+ if (registryState.parentPolicyVersion !== parent.policyVersion) {
507
+ return {
508
+ errors: [{
509
+ code: "WAIVER_POLICY_VERSION_MISMATCH",
510
+ message: "Parent waiver registry is not bound to the active policy version."
511
+ }],
512
+ evidence: []
513
+ };
514
+ }
515
+ const byId = new Map(tasks.map((task) => [task.taskId, task]));
516
+ const errors = [];
517
+ const evidence = [];
518
+ for (const capability of capabilities) {
519
+ const issued = isObject(capability) ? issuedWaiverCapabilities.get(capability) : undefined;
520
+ if (!issued || issued.registry !== registry) {
521
+ errors.push({
522
+ code: "UNISSUED_DEPENDENCY_WAIVER",
523
+ message: "A structurally valid object is not an internally issued waiver capability."
524
+ });
525
+ continue;
526
+ }
527
+ const waiver = issued.evidence;
528
+ if (waiver.parentPolicyVersion !== parent.policyVersion) {
529
+ errors.push({
530
+ code: "WAIVER_POLICY_VERSION_MISMATCH",
531
+ taskId: waiver.taskId,
532
+ message: "Dependency waiver is not bound to the active parent policy version."
533
+ });
534
+ continue;
535
+ }
536
+ const task = byId.get(waiver.taskId);
537
+ if (!task
538
+ || !task.dependsOn.includes(waiver.dependencyTaskId)
539
+ || !hasText(waiver.approvedBy)
540
+ || !hasText(waiver.approvedAt)
541
+ || Number.isNaN(Date.parse(waiver.approvedAt))) {
542
+ errors.push({
543
+ code: "INVALID_DEPENDENCY_WAIVER",
544
+ taskId: waiver.taskId,
545
+ message: "Dependency waiver must bind an existing dependency and include approval identity and time."
546
+ });
547
+ continue;
548
+ }
549
+ evidence.push({ ...waiver });
550
+ }
551
+ return { errors, evidence: errors.length === 0 ? evidence : [] };
552
+ }
553
+ function isObject(value) {
554
+ return (typeof value === "object" && value !== null) || typeof value === "function";
555
+ }