@martinloop/mcp 0.7.0 → 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,76 @@
1
+ import { type SwarmConflictRecord, type SwarmCleanupRecord, type SwarmEvent, type SwarmPatchAdmission, type SwarmRunRecord } from "../../contracts/index.js";
2
+ import { type PersistedSwarmCandidateArtifact } from "./candidates.js";
3
+ import type { SwarmWorkspaceManager, SwarmWorkspaceRuntimeHandle } from "./workspaces.js";
4
+ export interface SwarmIntegrationGitCommand {
5
+ readonly cwd: string;
6
+ readonly args: readonly string[];
7
+ readonly stdin?: Buffer;
8
+ }
9
+ export interface SwarmIntegrationGitResult {
10
+ readonly stdout: Buffer;
11
+ readonly stderr: Buffer;
12
+ }
13
+ export type SwarmIntegrationGitRunner = (command: SwarmIntegrationGitCommand) => Promise<SwarmIntegrationGitResult>;
14
+ export interface SwarmIntegrationCandidate {
15
+ readonly candidateId: string;
16
+ }
17
+ export interface SwarmIntegrationReconstruction {
18
+ readonly reconstructionId: string;
19
+ readonly swarmId: string;
20
+ readonly failedCandidateId: string;
21
+ readonly previousWorkspaceId: string;
22
+ readonly replacementWorkspaceId: string;
23
+ readonly baselineCommit: string;
24
+ readonly replayedCandidateIds: readonly string[];
25
+ readonly expectedTreeHash: string;
26
+ readonly actualTreeHash: string;
27
+ readonly state: "completed" | "failed";
28
+ readonly recordedAt: string;
29
+ readonly cleanup?: SwarmCleanupRecord;
30
+ readonly diagnostic?: string;
31
+ }
32
+ export interface SwarmIntegrationEvidenceBundle {
33
+ readonly decision: SwarmPatchAdmission;
34
+ readonly event: SwarmEvent;
35
+ readonly conflict?: SwarmConflictRecord;
36
+ readonly reconstruction?: SwarmIntegrationReconstruction;
37
+ }
38
+ export interface SwarmIntegrationEvidenceStore {
39
+ loadPersistedCandidate(candidateId: string): Promise<PersistedSwarmCandidateArtifact | undefined>;
40
+ loadPersistedAdmission(candidateId: string): Promise<SwarmPatchAdmission | undefined>;
41
+ /** Atomically claims the swarm/candidate pair; identity is retained as evidence, not as replay-key entropy. */
42
+ claimCandidateIntegration(input: {
43
+ swarmId: string;
44
+ candidateId: string;
45
+ identitySha256: string;
46
+ }): Promise<"claimed" | "already_claimed">;
47
+ persistOutcome(bundle: SwarmIntegrationEvidenceBundle): Promise<void>;
48
+ persistReconstruction(record: SwarmIntegrationReconstruction): Promise<void>;
49
+ }
50
+ export interface IntegrateSwarmCandidatesInput {
51
+ readonly swarmRecord: SwarmRunRecord;
52
+ readonly workspaceManager: SwarmWorkspaceManager;
53
+ readonly integrationWorkspaceId: string;
54
+ readonly candidates: readonly SwarmIntegrationCandidate[];
55
+ readonly evidenceStore: SwarmIntegrationEvidenceStore;
56
+ readonly conflictPolicy?: "stop" | "continue";
57
+ readonly gitRunner?: SwarmIntegrationGitRunner;
58
+ readonly now?: () => string;
59
+ readonly createId?: (prefix: string) => string;
60
+ readonly createReconstructionWorkspaceId?: (sequence: number) => string;
61
+ /** Internal lifecycle seam used to retain exact manager authority on exceptional paths. */
62
+ readonly onWorkspaceCreated?: (workspace: SwarmWorkspaceRuntimeHandle) => void;
63
+ }
64
+ export interface SwarmIntegrationResult {
65
+ readonly completed: boolean;
66
+ readonly workspace: SwarmWorkspaceRuntimeHandle;
67
+ readonly finalTreeHash: string;
68
+ readonly admittedCandidateIds: readonly string[];
69
+ readonly decisions: readonly SwarmPatchAdmission[];
70
+ readonly events: readonly SwarmEvent[];
71
+ readonly conflicts: readonly SwarmConflictRecord[];
72
+ readonly reconstructions: readonly SwarmIntegrationReconstruction[];
73
+ readonly cleanupAuthorized: boolean;
74
+ }
75
+ export declare function integrateSwarmCandidates(input: IntegrateSwarmCandidatesInput): Promise<SwarmIntegrationResult>;
76
+ export declare function hashSwarmIntegrationPatch(patch: Buffer): string;
@@ -0,0 +1,615 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { SWARM_SCHEMA_VERSION, swarmConcretePathMatchesPattern, validateSwarmChildContract, validateSwarmRunRecord } from "../../contracts/index.js";
4
+ import { verifyPersistedSwarmCandidateArtifact } from "./candidates.js";
5
+ import { validateSwarmTaskGraph } from "./scheduler.js";
6
+ const MAX_GIT_OUTPUT_BYTES = 64 * 1024 * 1024;
7
+ const MAX_DIAGNOSTIC_LENGTH = 512;
8
+ export async function integrateSwarmCandidates(input) {
9
+ const gitRunner = input.gitRunner ?? runGit;
10
+ const now = input.now ?? (() => new Date().toISOString());
11
+ const createId = input.createId ?? ((prefix) => `${prefix}-${randomUUID()}`);
12
+ const createReconstructionWorkspaceId = input.createReconstructionWorkspaceId
13
+ ?? ((sequence) => `integration-recovery-${String(sequence)}`);
14
+ const conflictPolicy = input.conflictPolicy ?? "stop";
15
+ assertIntegrationAuthority(input);
16
+ assertCandidateReferencesUnique(input.candidates);
17
+ const persistedCandidates = [];
18
+ for (const reference of input.candidates) {
19
+ const stored = await input.evidenceStore.loadPersistedCandidate(reference.candidateId);
20
+ if (!stored || stored.candidate.candidateId !== reference.candidateId) {
21
+ throw new Error(`Persisted swarm candidate is missing: ${reference.candidateId}`);
22
+ }
23
+ persistedCandidates.push({ artifact: snapshotCandidateArtifact(stored) });
24
+ }
25
+ const ordered = orderCandidates(input.swarmRecord.tasks, persistedCandidates);
26
+ assertCandidateAliasesUnique(ordered);
27
+ let workspace = await input.workspaceManager.createWorkspace({
28
+ kind: "integration",
29
+ workspaceId: input.integrationWorkspaceId
30
+ });
31
+ input.onWorkspaceCreated?.(workspace);
32
+ assertManagerIntegrationWorkspace(input, workspace, input.integrationWorkspaceId);
33
+ let expectedTreeHash = await baselineTreeHash(gitRunner, workspace.path, input.workspaceManager.baselineCommit);
34
+ const initialTree = await writeTree(gitRunner, workspace.path);
35
+ if (initialTree !== expectedTreeHash) {
36
+ throw new Error("Integration workspace index does not match the immutable baseline tree.");
37
+ }
38
+ await assertNoUnstagedOrUntracked(gitRunner, workspace.path);
39
+ const admitted = [];
40
+ const decisions = [];
41
+ const events = [];
42
+ const conflicts = [];
43
+ const reconstructions = [];
44
+ let cleanupAuthorized = true;
45
+ for (const entry of ordered) {
46
+ const artifact = entry.artifact;
47
+ const persistedAdmission = await input.evidenceStore.loadPersistedAdmission(artifact.candidate.candidateId);
48
+ const preconditionFailure = validateIntegrationPrecondition(input, artifact, persistedAdmission, expectedTreeHash);
49
+ if (preconditionFailure) {
50
+ const bundle = createRejectedBundle(input, artifact, {
51
+ reasonCode: preconditionFailure.reasonCode,
52
+ diagnostic: preconditionFailure.diagnostic,
53
+ preTreeHash: expectedTreeHash,
54
+ now: now(),
55
+ createId
56
+ });
57
+ cleanupAuthorized = await persistBundle(input.evidenceStore, bundle);
58
+ decisions.push(bundle.decision);
59
+ events.push(bundle.event);
60
+ if (!cleanupAuthorized || conflictPolicy === "stop")
61
+ break;
62
+ continue;
63
+ }
64
+ const claim = await input.evidenceStore.claimCandidateIntegration({
65
+ swarmId: input.swarmRecord.swarmId,
66
+ candidateId: artifact.candidate.candidateId,
67
+ identitySha256: artifact.identitySha256
68
+ });
69
+ if (claim === "already_claimed") {
70
+ const bundle = createRejectedBundle(input, artifact, {
71
+ reasonCode: "integration_precondition_failed",
72
+ diagnostic: "Candidate integration claim already exists.",
73
+ preTreeHash: expectedTreeHash,
74
+ now: now(),
75
+ createId
76
+ });
77
+ cleanupAuthorized = await persistBundle(input.evidenceStore, bundle);
78
+ decisions.push(bundle.decision);
79
+ events.push(bundle.event);
80
+ break;
81
+ }
82
+ const actualPreTree = await writeTree(gitRunner, workspace.path);
83
+ if (actualPreTree !== expectedTreeHash) {
84
+ const bundle = createRejectedBundle(input, artifact, {
85
+ reasonCode: "integration_precondition_failed",
86
+ diagnostic: "Integration tree changed before candidate application.",
87
+ preTreeHash: actualPreTree,
88
+ now: now(),
89
+ createId
90
+ });
91
+ cleanupAuthorized = await persistBundle(input.evidenceStore, bundle);
92
+ decisions.push(bundle.decision);
93
+ events.push(bundle.event);
94
+ break;
95
+ }
96
+ await assertNoUnstagedOrUntracked(gitRunner, workspace.path);
97
+ try {
98
+ await gitRunner({
99
+ cwd: workspace.path,
100
+ args: ["-c", "core.autocrlf=false", "apply", "--check", "--index", "-"],
101
+ stdin: artifact.patch
102
+ });
103
+ }
104
+ catch (error) {
105
+ const postFailureTree = await writeTree(gitRunner, workspace.path);
106
+ if (postFailureTree !== expectedTreeHash) {
107
+ throw new Error("Git apply --check mutated the integration index.");
108
+ }
109
+ const conflict = createConflict(input, artifact, expectedTreeHash, error, now(), createId);
110
+ const bundle = createRejectedBundle(input, artifact, {
111
+ reasonCode: "integration_conflict",
112
+ diagnostic: conflict.diagnostic ?? "Candidate conflicts with the integrated tree.",
113
+ preTreeHash: expectedTreeHash,
114
+ now: now(),
115
+ createId,
116
+ conflict
117
+ });
118
+ cleanupAuthorized = await persistBundle(input.evidenceStore, bundle);
119
+ decisions.push(bundle.decision);
120
+ events.push(bundle.event);
121
+ conflicts.push(conflict);
122
+ if (!cleanupAuthorized || conflictPolicy === "stop")
123
+ break;
124
+ continue;
125
+ }
126
+ try {
127
+ await gitRunner({
128
+ cwd: workspace.path,
129
+ args: ["-c", "core.autocrlf=false", "apply", "--index", "-"],
130
+ stdin: artifact.patch
131
+ });
132
+ }
133
+ catch (error) {
134
+ const failedBundle = createRejectedBundle(input, artifact, {
135
+ reasonCode: "integration_apply_failed",
136
+ diagnostic: sanitizeDiagnostic(error),
137
+ preTreeHash: expectedTreeHash,
138
+ now: now(),
139
+ createId
140
+ });
141
+ const failurePersisted = await persistBundle(input.evidenceStore, failedBundle);
142
+ decisions.push(failedBundle.decision);
143
+ events.push(failedBundle.event);
144
+ if (!failurePersisted) {
145
+ cleanupAuthorized = false;
146
+ break;
147
+ }
148
+ const reconstructed = await reconstructIntegration({
149
+ input,
150
+ gitRunner,
151
+ currentWorkspace: workspace,
152
+ admitted,
153
+ failedArtifact: artifact,
154
+ expectedTreeHash,
155
+ sequence: reconstructions.length + 1,
156
+ now,
157
+ createId,
158
+ createReconstructionWorkspaceId
159
+ });
160
+ workspace = reconstructed.workspace;
161
+ reconstructions.push(reconstructed.record);
162
+ try {
163
+ await input.evidenceStore.persistReconstruction(reconstructed.record);
164
+ cleanupAuthorized = true;
165
+ }
166
+ catch {
167
+ cleanupAuthorized = false;
168
+ }
169
+ if (!reconstructed.ok || !cleanupAuthorized)
170
+ break;
171
+ continue;
172
+ }
173
+ await assertNoUnstagedOrUntracked(gitRunner, workspace.path);
174
+ const postTreeHash = await writeTree(gitRunner, workspace.path);
175
+ const admittedBundle = createAdmittedBundle(input, artifact, expectedTreeHash, postTreeHash, now(), createId);
176
+ try {
177
+ await input.evidenceStore.persistOutcome(admittedBundle);
178
+ }
179
+ catch (error) {
180
+ const reconstructed = await reconstructIntegration({
181
+ input,
182
+ gitRunner,
183
+ currentWorkspace: workspace,
184
+ admitted,
185
+ failedArtifact: artifact,
186
+ expectedTreeHash,
187
+ sequence: reconstructions.length + 1,
188
+ now,
189
+ createId,
190
+ createReconstructionWorkspaceId,
191
+ preserveFailedWorkspace: true,
192
+ diagnostic: sanitizeDiagnostic(error)
193
+ });
194
+ workspace = reconstructed.workspace;
195
+ reconstructions.push(reconstructed.record);
196
+ const failureBundle = createRejectedBundle(input, artifact, {
197
+ reasonCode: "artifact_persistence_failed",
198
+ diagnostic: sanitizeDiagnostic(error),
199
+ preTreeHash: expectedTreeHash,
200
+ now: now(),
201
+ createId,
202
+ reconstruction: reconstructed.record
203
+ });
204
+ cleanupAuthorized = await persistBundle(input.evidenceStore, failureBundle);
205
+ cleanupAuthorized = false;
206
+ decisions.push(failureBundle.decision);
207
+ events.push(failureBundle.event);
208
+ break;
209
+ }
210
+ admitted.push({ artifact, treeHash: postTreeHash });
211
+ expectedTreeHash = postTreeHash;
212
+ decisions.push(admittedBundle.decision);
213
+ events.push(admittedBundle.event);
214
+ }
215
+ const allAdmitted = admitted.length === ordered.length;
216
+ return Object.freeze({
217
+ completed: allAdmitted,
218
+ workspace,
219
+ finalTreeHash: expectedTreeHash,
220
+ admittedCandidateIds: Object.freeze(admitted.map(({ artifact }) => artifact.candidate.candidateId)),
221
+ decisions: Object.freeze(decisions),
222
+ events: Object.freeze(events),
223
+ conflicts: Object.freeze(conflicts),
224
+ reconstructions: Object.freeze(reconstructions),
225
+ cleanupAuthorized
226
+ });
227
+ }
228
+ function assertIntegrationAuthority(input) {
229
+ const graph = validateSwarmTaskGraph(input.swarmRecord.tasks);
230
+ if (!graph.ok)
231
+ throw new Error("Swarm task graph is invalid for deterministic integration.");
232
+ if (!validateSwarmRunRecord(input.swarmRecord).ok
233
+ || input.swarmRecord.outcome.state !== "running"
234
+ || input.integrationWorkspaceId.trim().length === 0) {
235
+ throw new Error("Integration requires current parent authority and the manager baseline.");
236
+ }
237
+ }
238
+ function assertManagerIntegrationWorkspace(input, workspace, expectedWorkspaceId) {
239
+ if (workspace.record.kind !== "integration"
240
+ || workspace.record.workspaceId !== expectedWorkspaceId
241
+ || workspace.record.swarmId !== input.swarmRecord.swarmId
242
+ || workspace.record.baselineCommit !== input.workspaceManager.baselineCommit
243
+ || workspace.record.state !== "active") {
244
+ throw new Error("Workspace manager returned an integration handle outside current parent authority.");
245
+ }
246
+ }
247
+ function validateIntegrationPrecondition(input, artifact, persisted, expectedTreeHash) {
248
+ if (!verifyPersistedSwarmCandidateArtifact(artifact)) {
249
+ return { reasonCode: "patch_hash_mismatch", diagnostic: "Persisted candidate bytes or identity hash do not match." };
250
+ }
251
+ const candidate = artifact.candidate;
252
+ if (candidate.swarmId !== input.swarmRecord.swarmId
253
+ || candidate.baselineCommit !== input.workspaceManager.baselineCommit
254
+ || !persisted
255
+ || persisted.schemaVersion !== SWARM_SCHEMA_VERSION
256
+ || persisted.admissionId.trim().length === 0
257
+ || persisted.state !== "admitted"
258
+ || persisted.reasonCode !== "admitted"
259
+ || persisted.receiptIntegrity !== "verified"
260
+ || !admissionMatchesCandidate(persisted, artifact)
261
+ || (persisted.preIntegrationTreeHash !== undefined && persisted.preIntegrationTreeHash !== expectedTreeHash)) {
262
+ return {
263
+ reasonCode: "integration_precondition_failed",
264
+ diagnostic: "Candidate identity, baseline, or persisted admission does not match integration authority."
265
+ };
266
+ }
267
+ const agent = input.swarmRecord.agents.find((entry) => entry.agentId === candidate.agentId);
268
+ if (!agent
269
+ || agent.childRunId !== candidate.childRunId
270
+ || agent.status !== "verified"
271
+ || !validateSwarmChildContract(input.swarmRecord.parentContract, agent.contract).ok
272
+ || candidate.taskIds.length !== 1
273
+ || !sameStrings(candidate.taskIds, agent.contract.taskIds)) {
274
+ return { reasonCode: "integration_precondition_failed", diagnostic: "Candidate agent/task binding is stale." };
275
+ }
276
+ const task = input.swarmRecord.tasks.find((entry) => entry.taskId === candidate.taskIds[0]);
277
+ if (!task
278
+ || task.status !== "accepted"
279
+ || task.assignedAgentId !== candidate.agentId
280
+ || candidate.changedPaths.some((path) => (input.swarmRecord.parentContract.scope.deniedPaths.some((pattern) => swarmConcretePathMatchesPattern(path, pattern))
281
+ || agent.contract.scope.deniedPaths.some((pattern) => swarmConcretePathMatchesPattern(path, pattern))
282
+ || !input.swarmRecord.parentContract.scope.allowedPaths.some((pattern) => swarmConcretePathMatchesPattern(path, pattern))
283
+ || !agent.contract.scope.allowedPaths.some((pattern) => swarmConcretePathMatchesPattern(path, pattern))
284
+ || !task.writeScope.some((pattern) => swarmConcretePathMatchesPattern(path, pattern))))) {
285
+ return { reasonCode: "integration_precondition_failed", diagnostic: "Candidate task or scope authority is stale." };
286
+ }
287
+ return undefined;
288
+ }
289
+ function admissionMatchesCandidate(admission, artifact) {
290
+ const candidate = artifact.candidate;
291
+ return admission.candidateId === candidate.candidateId
292
+ && admission.swarmId === candidate.swarmId
293
+ && admission.workspaceId === candidate.workspaceId
294
+ && admission.childRunId === candidate.childRunId
295
+ && admission.agentId === candidate.agentId
296
+ && admission.baselineCommit === candidate.baselineCommit
297
+ && sameStrings(admission.taskIds, candidate.taskIds)
298
+ && sameStrings(admission.changedPaths, candidate.changedPaths);
299
+ }
300
+ function orderCandidates(tasks, candidates) {
301
+ const order = stableTopologicalOrder(tasks);
302
+ const rank = new Map(order.map((taskId, index) => [taskId, index]));
303
+ return [...candidates].sort((left, right) => {
304
+ const leftRanks = left.artifact.candidate.taskIds.map((taskId) => rank.get(taskId) ?? Number.MAX_SAFE_INTEGER);
305
+ const rightRanks = right.artifact.candidate.taskIds.map((taskId) => rank.get(taskId) ?? Number.MAX_SAFE_INTEGER);
306
+ const byDependency = Math.max(...leftRanks) - Math.max(...rightRanks);
307
+ if (byDependency !== 0)
308
+ return byDependency;
309
+ const byTasks = [...left.artifact.candidate.taskIds].sort().join("\0")
310
+ .localeCompare([...right.artifact.candidate.taskIds].sort().join("\0"));
311
+ if (byTasks !== 0)
312
+ return byTasks;
313
+ return left.artifact.candidate.candidateId.localeCompare(right.artifact.candidate.candidateId);
314
+ });
315
+ }
316
+ function stableTopologicalOrder(tasks) {
317
+ const byId = new Map(tasks.map((task) => [task.taskId, task]));
318
+ const indegree = new Map(tasks.map((task) => [task.taskId, task.dependsOn.length]));
319
+ const dependents = new Map();
320
+ for (const task of tasks) {
321
+ for (const dependency of task.dependsOn) {
322
+ const list = dependents.get(dependency) ?? [];
323
+ list.push(task.taskId);
324
+ dependents.set(dependency, list);
325
+ }
326
+ }
327
+ const ready = tasks.filter((task) => indegree.get(task.taskId) === 0)
328
+ .map((task) => task.taskId)
329
+ .sort();
330
+ const ordered = [];
331
+ while (ready.length > 0) {
332
+ const taskId = ready.shift();
333
+ if (!taskId || !byId.has(taskId))
334
+ continue;
335
+ ordered.push(taskId);
336
+ for (const dependent of (dependents.get(taskId) ?? []).sort()) {
337
+ const next = (indegree.get(dependent) ?? 0) - 1;
338
+ indegree.set(dependent, next);
339
+ if (next === 0) {
340
+ ready.push(dependent);
341
+ ready.sort();
342
+ }
343
+ }
344
+ }
345
+ if (ordered.length !== tasks.length)
346
+ throw new Error("Swarm task graph contains a cycle.");
347
+ return ordered;
348
+ }
349
+ function assertCandidateReferencesUnique(candidates) {
350
+ const ids = new Set();
351
+ for (const candidate of candidates) {
352
+ if (candidate.candidateId.trim().length === 0 || ids.has(candidate.candidateId)) {
353
+ throw new Error("Duplicate or empty swarm candidate references are forbidden.");
354
+ }
355
+ ids.add(candidate.candidateId);
356
+ }
357
+ }
358
+ function assertCandidateAliasesUnique(candidates) {
359
+ const candidateIds = new Set();
360
+ const identities = new Set();
361
+ for (const { artifact } of candidates) {
362
+ if (candidateIds.has(artifact.candidate.candidateId)
363
+ || identities.has(artifact.identitySha256)) {
364
+ throw new Error("Duplicate or aliased swarm candidates are forbidden.");
365
+ }
366
+ candidateIds.add(artifact.candidate.candidateId);
367
+ identities.add(artifact.identitySha256);
368
+ }
369
+ }
370
+ async function reconstructIntegration(context) {
371
+ let cleanup;
372
+ if (!context.preserveFailedWorkspace) {
373
+ cleanup = await context.input.workspaceManager.removeWorkspace(context.currentWorkspace, {
374
+ evidencePersisted: true,
375
+ processTreeClosed: true,
376
+ force: true
377
+ });
378
+ if (cleanup.state !== "completed") {
379
+ const actualTreeHash = await writeTree(context.gitRunner, context.currentWorkspace.path);
380
+ const record = Object.freeze({
381
+ reconstructionId: context.createId("reconstruction"),
382
+ swarmId: context.input.swarmRecord.swarmId,
383
+ failedCandidateId: context.failedArtifact.candidate.candidateId,
384
+ previousWorkspaceId: context.currentWorkspace.record.workspaceId,
385
+ replacementWorkspaceId: context.currentWorkspace.record.workspaceId,
386
+ baselineCommit: context.input.workspaceManager.baselineCommit,
387
+ replayedCandidateIds: Object.freeze(context.admitted.map(({ artifact }) => artifact.candidate.candidateId)),
388
+ expectedTreeHash: context.expectedTreeHash,
389
+ actualTreeHash,
390
+ state: "failed",
391
+ recordedAt: context.now(),
392
+ cleanup,
393
+ diagnostic: "Integration workspace cleanup did not complete; reconstruction was not started."
394
+ });
395
+ return { ok: false, workspace: context.currentWorkspace, record };
396
+ }
397
+ }
398
+ const replacementWorkspaceId = context.createReconstructionWorkspaceId(context.sequence);
399
+ const workspace = await context.input.workspaceManager.createWorkspace({
400
+ kind: "integration",
401
+ workspaceId: replacementWorkspaceId
402
+ });
403
+ context.input.onWorkspaceCreated?.(workspace);
404
+ assertManagerIntegrationWorkspace(context.input, workspace, replacementWorkspaceId);
405
+ let actualTreeHash = await writeTree(context.gitRunner, workspace.path);
406
+ let diagnostic = context.diagnostic;
407
+ let ok = true;
408
+ for (const replay of context.admitted) {
409
+ try {
410
+ await applyPatch(context.gitRunner, workspace.path, replay.artifact.patch);
411
+ actualTreeHash = await writeTree(context.gitRunner, workspace.path);
412
+ if (actualTreeHash !== replay.treeHash) {
413
+ throw new Error("Replayed candidate tree hash does not match persisted integration evidence.");
414
+ }
415
+ }
416
+ catch (error) {
417
+ ok = false;
418
+ diagnostic = sanitizeDiagnostic(error);
419
+ break;
420
+ }
421
+ }
422
+ if (actualTreeHash !== context.expectedTreeHash)
423
+ ok = false;
424
+ const record = Object.freeze({
425
+ reconstructionId: context.createId("reconstruction"),
426
+ swarmId: context.input.swarmRecord.swarmId,
427
+ failedCandidateId: context.failedArtifact.candidate.candidateId,
428
+ previousWorkspaceId: context.currentWorkspace.record.workspaceId,
429
+ replacementWorkspaceId: workspace.record.workspaceId,
430
+ baselineCommit: context.input.workspaceManager.baselineCommit,
431
+ replayedCandidateIds: Object.freeze(context.admitted.map(({ artifact }) => artifact.candidate.candidateId)),
432
+ expectedTreeHash: context.expectedTreeHash,
433
+ actualTreeHash,
434
+ state: ok ? "completed" : "failed",
435
+ recordedAt: context.now(),
436
+ ...(cleanup ? { cleanup } : {}),
437
+ ...(diagnostic ? { diagnostic } : {})
438
+ });
439
+ return { ok, workspace, record };
440
+ }
441
+ function snapshotCandidateArtifact(artifact) {
442
+ return {
443
+ candidate: {
444
+ ...artifact.candidate,
445
+ taskIds: [...artifact.candidate.taskIds],
446
+ changedPaths: [...artifact.candidate.changedPaths]
447
+ },
448
+ attemptId: artifact.attemptId,
449
+ proposalId: artifact.proposalId,
450
+ identitySha256: artifact.identitySha256,
451
+ candidateTreeHash: artifact.candidateTreeHash,
452
+ patch: Buffer.from(artifact.patch),
453
+ manifest: Buffer.from(artifact.manifest),
454
+ manifestSha256: artifact.manifestSha256,
455
+ receiptIntegrity: { ...artifact.receiptIntegrity }
456
+ };
457
+ }
458
+ async function applyPatch(gitRunner, cwd, patch) {
459
+ await gitRunner({ cwd, args: ["-c", "core.autocrlf=false", "apply", "--check", "--index", "-"], stdin: patch });
460
+ await gitRunner({ cwd, args: ["-c", "core.autocrlf=false", "apply", "--index", "-"], stdin: patch });
461
+ }
462
+ function createAdmittedBundle(input, artifact, preTreeHash, postTreeHash, decidedAt, createId) {
463
+ return {
464
+ decision: createDecision(input, artifact, {
465
+ state: "admitted",
466
+ reasonCode: "admitted",
467
+ preTreeHash,
468
+ postTreeHash,
469
+ decidedAt,
470
+ createId
471
+ }),
472
+ event: createEvent(input, artifact, "CHILD_PATCH_ADMITTED", decidedAt, {
473
+ candidateId: artifact.candidate.candidateId,
474
+ patchSha256: artifact.candidate.patchSha256,
475
+ preIntegrationTreeHash: preTreeHash,
476
+ postIntegrationTreeHash: postTreeHash
477
+ })
478
+ };
479
+ }
480
+ function createRejectedBundle(input, artifact, context) {
481
+ return {
482
+ decision: createDecision(input, artifact, {
483
+ state: "rejected",
484
+ reasonCode: context.reasonCode,
485
+ preTreeHash: context.preTreeHash,
486
+ decidedAt: context.now,
487
+ diagnostic: context.diagnostic,
488
+ createId: context.createId
489
+ }),
490
+ event: createEvent(input, artifact, context.conflict ? "INTEGRATION_CONFLICT" : "CHILD_PATCH_REJECTED", context.now, {
491
+ candidateId: artifact.candidate.candidateId,
492
+ reasonCode: context.reasonCode,
493
+ preIntegrationTreeHash: context.preTreeHash,
494
+ diagnostic: context.diagnostic
495
+ }),
496
+ ...(context.conflict ? { conflict: context.conflict } : {}),
497
+ ...(context.reconstruction ? { reconstruction: context.reconstruction } : {})
498
+ };
499
+ }
500
+ function createDecision(input, artifact, context) {
501
+ const candidate = artifact.candidate;
502
+ return Object.freeze({
503
+ schemaVersion: SWARM_SCHEMA_VERSION,
504
+ admissionId: context.createId("integration-admission"),
505
+ candidateId: candidate.candidateId,
506
+ swarmId: input.swarmRecord.swarmId,
507
+ workspaceId: candidate.workspaceId,
508
+ childRunId: candidate.childRunId,
509
+ agentId: candidate.agentId,
510
+ taskIds: Object.freeze([...candidate.taskIds]),
511
+ baselineCommit: candidate.baselineCommit,
512
+ changedPaths: Object.freeze([...candidate.changedPaths]),
513
+ state: context.state,
514
+ reasonCode: context.reasonCode,
515
+ receiptIntegrity: artifact.receiptIntegrity.state,
516
+ decidedAt: context.decidedAt,
517
+ preIntegrationTreeHash: context.preTreeHash,
518
+ ...(context.postTreeHash ? { postIntegrationTreeHash: context.postTreeHash } : {}),
519
+ ...(context.diagnostic ? { diagnostic: context.diagnostic } : {})
520
+ });
521
+ }
522
+ function createEvent(input, artifact, type, timestamp, payload) {
523
+ return Object.freeze({
524
+ type,
525
+ swarmId: input.swarmRecord.swarmId,
526
+ timestamp,
527
+ parentPolicyVersion: input.swarmRecord.parentContract.policyVersion,
528
+ taskId: artifact.candidate.taskIds[0],
529
+ agentId: artifact.candidate.agentId,
530
+ childRunId: artifact.candidate.childRunId,
531
+ payload: Object.freeze(payload)
532
+ });
533
+ }
534
+ function createConflict(input, artifact, preTreeHash, error, recordedAt, createId) {
535
+ return Object.freeze({
536
+ conflictId: createId("conflict"),
537
+ taskIds: [...artifact.candidate.taskIds],
538
+ paths: [...artifact.candidate.changedPaths],
539
+ state: "blocking",
540
+ schemaVersion: SWARM_SCHEMA_VERSION,
541
+ candidateId: artifact.candidate.candidateId,
542
+ childRunId: artifact.candidate.childRunId,
543
+ agentId: artifact.candidate.agentId,
544
+ preIntegrationTreeHash: preTreeHash,
545
+ recordedAt,
546
+ diagnostic: sanitizeDiagnostic(error)
547
+ });
548
+ }
549
+ async function persistBundle(store, bundle) {
550
+ try {
551
+ await store.persistOutcome(bundle);
552
+ return true;
553
+ }
554
+ catch {
555
+ return false;
556
+ }
557
+ }
558
+ async function baselineTreeHash(gitRunner, cwd, baselineCommit) {
559
+ return gitScalar(gitRunner, cwd, ["rev-parse", `${baselineCommit}^{tree}`]);
560
+ }
561
+ async function writeTree(gitRunner, cwd) {
562
+ return gitScalar(gitRunner, cwd, ["write-tree"]);
563
+ }
564
+ async function assertNoUnstagedOrUntracked(gitRunner, cwd) {
565
+ await gitRunner({ cwd, args: ["diff", "--quiet", "--no-ext-diff", "--"] });
566
+ const untracked = await gitRunner({
567
+ cwd,
568
+ args: ["ls-files", "--others", "--exclude-standard", "-z"]
569
+ });
570
+ if (untracked.stdout.length !== 0) {
571
+ throw new Error("Integration workspace contains untracked mutation.");
572
+ }
573
+ }
574
+ async function gitScalar(gitRunner, cwd, args) {
575
+ const result = await gitRunner({ cwd, args });
576
+ return result.stdout.toString("utf8").trim();
577
+ }
578
+ function runGit(command) {
579
+ return new Promise((resolvePromise, rejectPromise) => {
580
+ const child = execFile("git", [...command.args], {
581
+ cwd: command.cwd,
582
+ encoding: null,
583
+ windowsHide: true,
584
+ maxBuffer: MAX_GIT_OUTPUT_BYTES
585
+ }, (error, stdout, stderr) => {
586
+ if (error) {
587
+ rejectPromise(Object.assign(error, { stderr }));
588
+ return;
589
+ }
590
+ resolvePromise({
591
+ stdout: Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout),
592
+ stderr: Buffer.isBuffer(stderr) ? stderr : Buffer.from(stderr)
593
+ });
594
+ });
595
+ if (command.stdin)
596
+ child.stdin?.end(command.stdin);
597
+ });
598
+ }
599
+ function sameStrings(left, right) {
600
+ if (left.length !== right.length)
601
+ return false;
602
+ const a = [...left].sort();
603
+ const b = [...right].sort();
604
+ return a.every((value, index) => value === b[index]);
605
+ }
606
+ function sanitizeDiagnostic(error) {
607
+ const raw = error instanceof Error
608
+ ? (error.stderr?.toString() || error.message)
609
+ : String(error);
610
+ return raw.replace(/[\u0000-\u001f\u007f]+/gu, " ").replace(/\s+/gu, " ").trim()
611
+ .slice(0, MAX_DIAGNOSTIC_LENGTH);
612
+ }
613
+ export function hashSwarmIntegrationPatch(patch) {
614
+ return createHash("sha256").update(patch).digest("hex");
615
+ }