@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,362 @@
1
+ import { createHash, createHmac } from "node:crypto";
2
+ import { realpath } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { SWARM_PARENT_RECEIPT_SCHEMA_VERSION, SWARM_SCHEMA_VERSION, computeSwarmLivePlanHash, } from "../../contracts/index.js";
5
+ import { SWARM_EVIDENCE_INDEX_SCHEMA_VERSION, SWARM_HOSTED_SCHEMA_VERSION, canonicalSwarmHostedTransportBytes, computeSwarmHostedEnvelopeIdentity, validateSwarmHostedEnvelope, } from "../../contracts/swarm-hosted.js";
6
+ import { readPrivateIntegrityKey } from "../persistence/integrity.js";
7
+ import { readSwarmReceiptProjection } from "./receipt-projection.js";
8
+ import { assertSwarmPathIdentifier } from "./workspaces.js";
9
+ import { readSwarmOperationalState } from "./operations.js";
10
+ export const SWARM_HOSTED_TRANSPORT_DOMAIN = "martin.swarm-hosted.v1";
11
+ async function deriveSwarmHostedTransportKeyMaterialFromReceipt(input) {
12
+ assertSwarmPathIdentifier(input.swarmId, "swarm ID");
13
+ const runsRoot = await realpath(resolve(input.runsRoot));
14
+ const receiptSecret = await readPrivateIntegrityKey(runsRoot, `swarm-${input.swarmId}.key`);
15
+ if (!receiptSecret)
16
+ throw new Error("SWARM_SEAL_KEY_UNAVAILABLE");
17
+ const secret = createHmac("sha256", receiptSecret)
18
+ .update(`${input.domain}\n${input.swarmId}\n`)
19
+ .digest("hex");
20
+ return { secret, keyId: `hosted-${sha256(secret).slice(0, 16)}` };
21
+ }
22
+ const productionDependencies = {
23
+ readOperationalState: (input) => readSwarmOperationalState(input),
24
+ readReceiptProjection: (input) => readSwarmReceiptProjection(input),
25
+ ensureHostedTransportKey: deriveHostedTransportKeyMaterial,
26
+ };
27
+ /** @internal Direct-module seam for proving derivation never creates another secret file. */
28
+ export async function deriveHostedTransportKeyMaterial(input) {
29
+ try {
30
+ return await deriveSwarmHostedTransportKeyMaterialFromReceipt({
31
+ ...input,
32
+ domain: SWARM_HOSTED_TRANSPORT_DOMAIN,
33
+ });
34
+ }
35
+ catch {
36
+ throw hostedError("SWARM_HOSTED_KEY_UNAVAILABLE", "Authenticated receipt key material is unavailable for hosted derivation.");
37
+ }
38
+ }
39
+ export async function buildSwarmHostedEnvelope(input) {
40
+ return buildSwarmHostedEnvelopeWithDependencies(input, productionDependencies);
41
+ }
42
+ export async function buildSwarmHostedEnvelopeWithDependencies(input, dependencies) {
43
+ const selector = { runsRoot: input.runsRoot, swarmId: input.swarmId };
44
+ const [operational, projection] = await Promise.all([
45
+ dependencies.readOperationalState(selector),
46
+ dependencies.readReceiptProjection(selector),
47
+ ]);
48
+ assertAuthenticatedSource(input, operational, projection);
49
+ const unsigned = projectEnvelope(input.runtimeVersion, operational, projection);
50
+ const key = await dependencies.ensureHostedTransportKey(selector);
51
+ const keyLocatorHash = sha256(`${SWARM_HOSTED_TRANSPORT_DOMAIN}\n${input.swarmId}\n${key.keyId}\n`);
52
+ const envelopeWithLocator = {
53
+ ...unsigned,
54
+ transportSignature: {
55
+ algorithm: "hmac-sha256",
56
+ keyId: key.keyId,
57
+ keyLocatorHash,
58
+ signatureHmacSha256: "0".repeat(64),
59
+ },
60
+ };
61
+ envelopeWithLocator.envelopeId = computeSwarmHostedEnvelopeIdentity(envelopeWithLocator);
62
+ const signatureHmacSha256 = createHmac("sha256", key.secret)
63
+ .update(`${SWARM_HOSTED_TRANSPORT_DOMAIN}\n${canonicalSwarmHostedTransportBytes(envelopeWithLocator)}`)
64
+ .digest("hex");
65
+ const envelope = {
66
+ ...envelopeWithLocator,
67
+ transportSignature: { ...envelopeWithLocator.transportSignature, signatureHmacSha256 },
68
+ };
69
+ const validation = validateSwarmHostedEnvelope(envelope);
70
+ if (!validation.ok) {
71
+ throw hostedError("SWARM_HOSTED_PROJECTION_INVALID", `Hosted swarm projection failed validation: ${validation.errors.map((error) => `${error.code}:${error.path}`).join(",")}`);
72
+ }
73
+ const canonicalBody = `${JSON.stringify(envelope)}\n`;
74
+ return {
75
+ envelope,
76
+ canonicalBody,
77
+ payloadSha256: sha256(canonicalBody),
78
+ envelopeIdentity: envelope.envelopeId,
79
+ transportKey: { keyId: key.keyId, keyLocatorHash, domain: SWARM_HOSTED_TRANSPORT_DOMAIN },
80
+ };
81
+ }
82
+ function projectEnvelope(runtimeVersion, operational, projection) {
83
+ const { plan } = operational;
84
+ const { receipt, integrity } = projection;
85
+ const childReceiptById = new Map(receipt.childReceipts.map((child) => [child.childRunId, child]));
86
+ const events = receipt.events.map((event) => projectEvent(event, childReceiptById));
87
+ const eventById = new Map(events.map((event) => [event.eventId, event]));
88
+ const plannedTaskAgent = new Map(plan.tasks.map((task) => [task.taskId, task.assignedAgentId]));
89
+ const effectiveTaskAgent = new Map(receipt.tasks.map((task) => [task.taskId, task.assignedAgentId]));
90
+ const agentTasks = new Map(receipt.agents.map((agent) => [agent.agentId, new Set(agent.contract.taskIds)]));
91
+ for (const task of receipt.tasks) {
92
+ for (const agentId of [plannedTaskAgent.get(task.taskId), effectiveTaskAgent.get(task.taskId)]) {
93
+ if (agentId)
94
+ agentTasks.get(agentId)?.add(task.taskId);
95
+ }
96
+ }
97
+ const agentById = new Map(receipt.agents.map((agent) => [agent.agentId, agent]));
98
+ const parentEvent = findSingleEvent(events, parentTerminalType(receipt.parentOutcome.state), undefined);
99
+ const globalEvent = receipt.globalVerificationId
100
+ ? findSingleEvent(events, receipt.taskVerificationState === "passed" ? "GLOBAL_VERIFIER_PASSED" : "GLOBAL_VERIFIER_FAILED", receipt.globalVerificationId)
101
+ : undefined;
102
+ const envelope = {
103
+ schemaVersion: SWARM_HOSTED_SCHEMA_VERSION,
104
+ envelopeId: "0".repeat(64),
105
+ sourceSchemas: {
106
+ swarm: SWARM_SCHEMA_VERSION,
107
+ receipt: SWARM_PARENT_RECEIPT_SCHEMA_VERSION,
108
+ evidenceIndex: SWARM_EVIDENCE_INDEX_SCHEMA_VERSION,
109
+ },
110
+ sourceIdentities: {
111
+ receiptId: receipt.receiptId,
112
+ receiptSha256: receipt.receiptSha256,
113
+ evidenceIndexSha256: receipt.evidenceIndexSha256,
114
+ eventChainSha256: integrity.eventChainHead,
115
+ },
116
+ runtimeVersion,
117
+ createdAt: receipt.sealedAt,
118
+ swarm: {
119
+ swarmId: receipt.swarmId,
120
+ workspaceId: plan.workspaceId,
121
+ projectId: plan.projectId,
122
+ planHash: receipt.planHash,
123
+ baselineCommit: receipt.baselineCommit,
124
+ },
125
+ topology: {
126
+ tasks: receipt.tasks.map((task) => ({
127
+ taskId: task.taskId,
128
+ required: task.required,
129
+ dependsOn: [...task.dependsOn],
130
+ ...(plannedTaskAgent.get(task.taskId) ? { plannedAgentId: plannedTaskAgent.get(task.taskId) } : {}),
131
+ ...(task.assignedAgentId ? { effectiveAgentId: task.assignedAgentId } : {}),
132
+ status: task.status,
133
+ })),
134
+ agents: receipt.agents.map((agent) => ({
135
+ agentId: agent.agentId,
136
+ role: agent.role,
137
+ status: agent.status,
138
+ taskIds: [...(agentTasks.get(agent.agentId) ?? [])].sort(),
139
+ ...(agent.childRunId ? { childRunId: agent.childRunId } : {}),
140
+ })),
141
+ children: receipt.childReceipts.map((child) => {
142
+ const agent = agentById.get(child.agentId);
143
+ const evidence = events
144
+ .filter((event) => event.childRunId === child.childRunId
145
+ && event.agentId === child.agentId
146
+ && event.attemptId === child.attemptId
147
+ && (event.taskId === undefined || child.taskIds.includes(event.taskId)))
148
+ .map(eventReference);
149
+ return {
150
+ childRunId: child.childRunId,
151
+ agentId: child.agentId,
152
+ attemptId: child.attemptId,
153
+ taskIds: [...child.taskIds],
154
+ status: agent?.status ?? "needs_review",
155
+ receiptIntegrityState: "verified",
156
+ evidence,
157
+ };
158
+ }),
159
+ },
160
+ budget: {
161
+ capUsd: receipt.budgetLedger.capUsd,
162
+ ...(receipt.budgetLedger.capTokens !== undefined ? { capTokens: receipt.budgetLedger.capTokens } : {}),
163
+ settledUsd: receipt.budgetLedger.settledUsd,
164
+ settledTokens: receipt.budgetLedger.settledTokens,
165
+ },
166
+ interventions: {
167
+ blockedActions: receipt.blockedActions.map((action) => ({
168
+ action: action.action,
169
+ reasonCode: action.reason,
170
+ taskId: action.taskId,
171
+ agentId: action.agentId,
172
+ attemptId: action.attemptId,
173
+ evidence: [{ eventId: action.eventId, sequence: action.sequence }],
174
+ })),
175
+ reassignments: receipt.reassignments.map((reassignment) => ({
176
+ taskId: reassignment.taskId,
177
+ fromAgentId: reassignment.fromAgentId,
178
+ toAgentId: reassignment.toAgentId,
179
+ fromAttemptId: reassignment.fromAttemptId,
180
+ toAttemptId: reassignment.toAttemptId,
181
+ reasonCode: reassignment.reason,
182
+ evidence: [{ eventId: reassignment.eventId, sequence: reassignment.sequence }],
183
+ })),
184
+ },
185
+ integration: {
186
+ admissions: integrationDecisions(events, "CHILD_PATCH_ADMITTED", "admitted"),
187
+ rejections: integrationDecisions(events, "CHILD_PATCH_REJECTED", "rejected"),
188
+ conflicts: integrationConflicts(events),
189
+ ...(receipt.integratedTreeHash ? { integratedTreeHash: receipt.integratedTreeHash } : {}),
190
+ },
191
+ events,
192
+ globalVerification: globalEvent ? {
193
+ state: receipt.taskVerificationState === "passed" ? "passed" : "failed",
194
+ verificationId: receipt.globalVerificationId,
195
+ ...(receipt.integratedTreeHash ? { integratedTreeHash: receipt.integratedTreeHash } : {}),
196
+ evidence: [eventReference(globalEvent)],
197
+ } : {
198
+ state: "unknown",
199
+ evidence: [],
200
+ },
201
+ parentOutcome: {
202
+ state: receipt.parentOutcome.state,
203
+ source: "sealed_parent_receipt",
204
+ evidence: [eventReference(parentEvent)],
205
+ },
206
+ taskVerificationState: receipt.taskVerificationState,
207
+ receiptIntegrityState: "verified",
208
+ transportSignature: {
209
+ algorithm: "hmac-sha256",
210
+ keyId: "pending",
211
+ keyLocatorHash: "0".repeat(64),
212
+ signatureHmacSha256: "0".repeat(64),
213
+ },
214
+ };
215
+ for (const reference of allEvidenceReferences(envelope)) {
216
+ if (!eventById.has(reference.eventId)) {
217
+ throw hostedError("SWARM_HOSTED_SOURCE_MISMATCH", "Hosted evidence reference does not resolve to an authenticated event.");
218
+ }
219
+ }
220
+ return envelope;
221
+ }
222
+ function assertAuthenticatedSource(input, operational, projection) {
223
+ const { plan, snapshot, events } = operational;
224
+ const { receipt, integrity, seal } = projection;
225
+ if (integrity.state !== "verified"
226
+ || !integrity.receiptSha256
227
+ || !integrity.evidenceIndexSha256
228
+ || !integrity.eventChainHead) {
229
+ throw hostedError("SWARM_HOSTED_INTEGRITY_NOT_VERIFIED", "Committed swarm receipt integrity is not verified.");
230
+ }
231
+ if (plan.schemaVersion !== SWARM_SCHEMA_VERSION
232
+ || receipt.schemaVersion !== SWARM_PARENT_RECEIPT_SCHEMA_VERSION
233
+ || seal.schemaVersion !== "martin.swarm-receipt-seal.v1") {
234
+ throw hostedError("SWARM_HOSTED_UNSUPPORTED_SOURCE", "Swarm hosted export source schema is unsupported.");
235
+ }
236
+ if (input.swarmId !== plan.swarmId
237
+ || computeSwarmLivePlanHash(plan) !== plan.planHash
238
+ || receipt.swarmId !== plan.swarmId
239
+ || seal.swarmId !== plan.swarmId
240
+ || receipt.planHash !== plan.planHash
241
+ || seal.planHash !== plan.planHash
242
+ || receipt.baselineCommit !== plan.baselineCommit
243
+ || receipt.evidenceIndexSha256 !== integrity.evidenceIndexSha256
244
+ || receipt.receiptSha256 !== integrity.receiptSha256
245
+ || integrity.taskVerificationState !== receipt.taskVerificationState
246
+ || seal.evidenceIndexSha256 !== receipt.evidenceIndexSha256
247
+ || seal.committedAt !== receipt.sealedAt
248
+ || snapshot.outcome.state !== receipt.parentOutcome.state
249
+ || stableJson(events) !== stableJson(receipt.events)) {
250
+ throw hostedError("SWARM_HOSTED_SOURCE_MISMATCH", "Committed swarm source identities disagree.");
251
+ }
252
+ }
253
+ function projectEvent(event, childReceiptById) {
254
+ const payload = event.payload;
255
+ const hasExplicitAttemptId = Object.prototype.hasOwnProperty.call(payload, "attemptId");
256
+ if (hasExplicitAttemptId
257
+ && (typeof payload.attemptId !== "string" || payload.attemptId.length === 0)) {
258
+ throw hostedError("SWARM_HOSTED_SOURCE_MISMATCH", "Present child attempt identity must be a non-empty string; only true omission may use receipt linkage.");
259
+ }
260
+ const explicitAttemptId = text(payload.attemptId)
261
+ ?? (event.type === "TASK_REASSIGNED" ? text(payload.fromAttemptId) : undefined);
262
+ let attemptId = explicitAttemptId;
263
+ if (event.childRunId && event.type.startsWith("CHILD_")) {
264
+ const child = childReceiptById.get(event.childRunId);
265
+ if (!child
266
+ || event.agentId !== child.agentId
267
+ || (event.taskId !== undefined && !child.taskIds.includes(event.taskId))
268
+ || (explicitAttemptId !== undefined && explicitAttemptId !== child.attemptId)) {
269
+ throw hostedError("SWARM_HOSTED_SOURCE_MISMATCH", "Child lifecycle event identities contradict authenticated child receipt linkage.");
270
+ }
271
+ attemptId = child.attemptId;
272
+ }
273
+ const relatedAttemptId = text(payload.toAttemptId);
274
+ const toAgentId = text(payload.toAgentId);
275
+ const candidateId = text(payload.candidateId);
276
+ const verificationId = text(payload.verificationId);
277
+ return {
278
+ eventId: event.idempotencyKey,
279
+ sourceIdempotencyKey: event.idempotencyKey,
280
+ sequence: event.sequence,
281
+ type: event.type,
282
+ timestamp: event.timestamp,
283
+ ...(event.taskId ? { taskId: event.taskId } : {}),
284
+ ...(event.agentId ? { agentId: event.type === "TASK_REASSIGNED" ? text(payload.fromAgentId) ?? event.agentId : event.agentId } : {}),
285
+ ...(event.childRunId ? { childRunId: event.childRunId } : {}),
286
+ ...(attemptId ? { attemptId } : {}),
287
+ ...(relatedAttemptId ? { relatedAttemptId } : {}),
288
+ ...(toAgentId ? { toAgentId } : {}),
289
+ ...(candidateId ? { candidateId } : {}),
290
+ ...(verificationId ? { verificationId } : {}),
291
+ ...(event.failureClass ? { failureClass: event.failureClass } : {}),
292
+ };
293
+ }
294
+ function integrationDecisions(events, type, state) {
295
+ return events.filter((event) => event.type === type).map((event) => {
296
+ if (!event.candidateId || !event.taskId || !event.agentId || !event.childRunId) {
297
+ throw hostedError("SWARM_HOSTED_SOURCE_MISMATCH", "Integration event lacks authenticated hosted identities.");
298
+ }
299
+ return {
300
+ candidateId: event.candidateId,
301
+ taskId: event.taskId,
302
+ agentId: event.agentId,
303
+ childRunId: event.childRunId,
304
+ state,
305
+ evidence: [eventReference(event)],
306
+ };
307
+ });
308
+ }
309
+ function integrationConflicts(events) {
310
+ return events.filter((event) => event.type === "INTEGRATION_CONFLICT").map((event) => ({
311
+ conflictId: event.candidateId ?? event.eventId,
312
+ taskIds: event.taskId ? [event.taskId] : [],
313
+ state: "blocking",
314
+ evidence: [eventReference(event)],
315
+ }));
316
+ }
317
+ function findSingleEvent(events, type, identity) {
318
+ const matches = events.filter((event) => event.type === type
319
+ && (identity === undefined || event.verificationId === identity));
320
+ if (matches.length !== 1) {
321
+ throw hostedError("SWARM_HOSTED_SOURCE_MISMATCH", `Expected one authoritative ${type} event.`);
322
+ }
323
+ return matches[0];
324
+ }
325
+ function parentTerminalType(state) {
326
+ return state === "verified" ? "SWARM_VERIFIED" : state === "stopped" ? "SWARM_STOPPED" : "SWARM_NEEDS_REVIEW";
327
+ }
328
+ function eventReference(event) {
329
+ return { eventId: event.eventId, sequence: event.sequence };
330
+ }
331
+ function allEvidenceReferences(envelope) {
332
+ return [
333
+ ...envelope.topology.children.flatMap((child) => child.evidence),
334
+ ...envelope.interventions.blockedActions.flatMap((action) => action.evidence),
335
+ ...envelope.interventions.reassignments.flatMap((reassignment) => reassignment.evidence),
336
+ ...envelope.integration.admissions.flatMap((decision) => decision.evidence),
337
+ ...envelope.integration.rejections.flatMap((decision) => decision.evidence),
338
+ ...envelope.integration.conflicts.flatMap((conflict) => conflict.evidence),
339
+ ...envelope.globalVerification.evidence,
340
+ ...envelope.parentOutcome.evidence,
341
+ ];
342
+ }
343
+ function text(value) {
344
+ return typeof value === "string" && value.length > 0 ? value : undefined;
345
+ }
346
+ function stableJson(value) {
347
+ if (Array.isArray(value))
348
+ return `[${value.map(stableJson).join(",")}]`;
349
+ if (value !== null && typeof value === "object") {
350
+ return `{${Object.entries(value)
351
+ .sort(([left], [right]) => left.localeCompare(right))
352
+ .map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`)
353
+ .join(",")}}`;
354
+ }
355
+ return JSON.stringify(value);
356
+ }
357
+ function sha256(value) {
358
+ return createHash("sha256").update(value, "utf8").digest("hex");
359
+ }
360
+ function hostedError(code, message) {
361
+ return Object.assign(new Error(`${code}: ${message}`), { code });
362
+ }
@@ -0,0 +1,5 @@
1
+ import { type SwarmAtlasView, type SwarmDashboardView, type SwarmSansaView } from "../../contracts/swarm-hosted.js";
2
+ export type { SwarmAtlasView, SwarmDashboardView, SwarmSansaView, SwarmTraceFact } from "../../contracts/swarm-hosted.js";
3
+ export declare function projectSwarmAtlasView(value: unknown): SwarmAtlasView;
4
+ export declare function projectSwarmTraceFacts(value: unknown): SwarmSansaView;
5
+ export declare function projectSwarmDashboardView(value: unknown): SwarmDashboardView;
@@ -0,0 +1,258 @@
1
+ import { validateSwarmHostedEnvelope, } from "../../contracts/swarm-hosted.js";
2
+ const FAILURE_EVENT_TYPES = new Set([
3
+ "CHILD_STOPPED",
4
+ "CHILD_NEEDS_REVIEW",
5
+ "CHILD_PATCH_REJECTED",
6
+ ]);
7
+ const TERMINAL_EVENT_TYPES = {
8
+ stopped: "CHILD_STOPPED",
9
+ needs_review: "CHILD_NEEDS_REVIEW",
10
+ rejected: "CHILD_PATCH_REJECTED",
11
+ };
12
+ export function projectSwarmAtlasView(value) {
13
+ const envelope = validatedEnvelope(value);
14
+ return {
15
+ schemaVersion: "martin.swarm-atlas-view.v1",
16
+ envelopeId: envelope.envelopeId,
17
+ sourceIdentities: { ...envelope.sourceIdentities },
18
+ swarm: { ...envelope.swarm },
19
+ tasks: envelope.topology.tasks.map(cloneTask),
20
+ dependencyEdges: envelope.topology.tasks
21
+ .flatMap((task) => task.dependsOn.map((dependencyTaskId) => ({
22
+ fromTaskId: task.taskId,
23
+ toTaskId: dependencyTaskId,
24
+ })))
25
+ .sort(compareDependencyEdges),
26
+ agents: envelope.topology.agents.map((agent) => ({ ...agent, taskIds: [...agent.taskIds] })),
27
+ children: envelope.topology.children.map((child) => ({
28
+ ...child,
29
+ taskIds: [...child.taskIds],
30
+ evidence: cloneEvidence(child.evidence),
31
+ })),
32
+ budget: { ...envelope.budget },
33
+ interventions: {
34
+ blockedActions: envelope.interventions.blockedActions.map((action) => ({ ...action, evidence: cloneEvidence(action.evidence) })),
35
+ reassignments: envelope.interventions.reassignments.map((reassignment) => ({ ...reassignment, evidence: cloneEvidence(reassignment.evidence) })),
36
+ },
37
+ integration: {
38
+ ...envelope.integration,
39
+ admissions: envelope.integration.admissions.map((decision) => ({ ...decision, evidence: cloneEvidence(decision.evidence) })),
40
+ rejections: envelope.integration.rejections.map((decision) => ({ ...decision, evidence: cloneEvidence(decision.evidence) })),
41
+ conflicts: envelope.integration.conflicts.map((conflict) => ({ ...conflict, taskIds: [...conflict.taskIds], evidence: cloneEvidence(conflict.evidence) })),
42
+ },
43
+ globalVerification: { ...envelope.globalVerification, evidence: cloneEvidence(envelope.globalVerification.evidence) },
44
+ parentOutcome: { ...envelope.parentOutcome, evidence: cloneEvidence(envelope.parentOutcome.evidence) },
45
+ taskVerificationState: envelope.taskVerificationState,
46
+ receiptIntegrityState: envelope.receiptIntegrityState,
47
+ };
48
+ }
49
+ export function projectSwarmTraceFacts(value) {
50
+ const envelope = validatedEnvelope(value);
51
+ const facts = [
52
+ ...observedFacts(envelope),
53
+ ...recoveryFacts(envelope),
54
+ ...blockingDependencyFacts(envelope),
55
+ ].sort(compareFacts);
56
+ return {
57
+ schemaVersion: "martin.swarm-sansa-view.v1",
58
+ envelopeId: envelope.envelopeId,
59
+ sourceIdentities: { ...envelope.sourceIdentities },
60
+ facts,
61
+ parentOutcome: { ...envelope.parentOutcome, evidence: cloneEvidence(envelope.parentOutcome.evidence) },
62
+ globalVerification: { ...envelope.globalVerification, evidence: cloneEvidence(envelope.globalVerification.evidence) },
63
+ };
64
+ }
65
+ export function projectSwarmDashboardView(value) {
66
+ const envelope = validatedEnvelope(value);
67
+ return {
68
+ schemaVersion: "martin.swarm-dashboard-view.v1",
69
+ envelopeId: envelope.envelopeId,
70
+ sourceIdentities: { ...envelope.sourceIdentities },
71
+ swarmId: envelope.swarm.swarmId,
72
+ children: envelope.topology.children.map((child) => ({
73
+ ...child,
74
+ taskIds: [...child.taskIds],
75
+ evidence: cloneEvidence(child.evidence),
76
+ })),
77
+ parentOutcome: { ...envelope.parentOutcome, evidence: cloneEvidence(envelope.parentOutcome.evidence) },
78
+ globalVerification: { ...envelope.globalVerification, evidence: cloneEvidence(envelope.globalVerification.evidence) },
79
+ taskVerificationState: envelope.taskVerificationState,
80
+ receiptIntegrityState: envelope.receiptIntegrityState,
81
+ budget: { ...envelope.budget },
82
+ };
83
+ }
84
+ function validatedEnvelope(value) {
85
+ const validation = validateSwarmHostedEnvelope(value);
86
+ if (!validation.ok) {
87
+ const error = new Error(`INVALID_SWARM_HOSTED_ENVELOPE: ${validation.errors.map((item) => `${item.code}:${item.path}`).join(",")}`);
88
+ Object.assign(error, { code: "INVALID_SWARM_HOSTED_ENVELOPE" });
89
+ throw error;
90
+ }
91
+ return value;
92
+ }
93
+ function observedFacts(envelope) {
94
+ return envelope.events.flatMap((event) => {
95
+ if (FAILURE_EVENT_TYPES.has(event.type))
96
+ return [factFromEvent("failure", event)];
97
+ if (event.type === "TASK_REASSIGNED")
98
+ return [factFromEvent("reassignment", event)];
99
+ if (event.type === "GLOBAL_VERIFIER_PASSED" || event.type === "GLOBAL_VERIFIER_FAILED") {
100
+ return [factFromEvent("verification_boundary", event)];
101
+ }
102
+ return [];
103
+ });
104
+ }
105
+ function recoveryFacts(envelope) {
106
+ const facts = [];
107
+ for (const reassignment of envelope.interventions.reassignments) {
108
+ const task = envelope.topology.tasks.find((item) => item.taskId === reassignment.taskId);
109
+ if (task?.status !== "accepted" || task.effectiveAgentId !== reassignment.toAgentId)
110
+ continue;
111
+ const reassigned = referencedEvent(envelope.events, reassignment.evidence, "TASK_REASSIGNED");
112
+ if (!reassigned
113
+ || reassigned.taskId !== reassignment.taskId
114
+ || reassigned.agentId !== reassignment.fromAgentId
115
+ || reassigned.toAgentId !== reassignment.toAgentId
116
+ || reassigned.attemptId !== reassignment.fromAttemptId
117
+ || reassigned.relatedAttemptId !== reassignment.toAttemptId)
118
+ continue;
119
+ const failure = latestBefore(envelope.events, reassigned.sequence, (event) => FAILURE_EVENT_TYPES.has(event.type)
120
+ && event.taskId === reassignment.taskId
121
+ && event.agentId === reassignment.fromAgentId
122
+ && event.attemptId === reassignment.fromAttemptId);
123
+ const start = firstAfter(envelope.events, reassigned.sequence, (event) => event.type === "CHILD_STARTED"
124
+ && event.taskId === reassignment.taskId
125
+ && event.agentId === reassignment.toAgentId
126
+ && event.attemptId === reassignment.toAttemptId);
127
+ const completed = start && firstAfter(envelope.events, start.sequence, (event) => event.type === "CHILD_VERIFIED"
128
+ && event.taskId === reassignment.taskId
129
+ && event.agentId === reassignment.toAgentId
130
+ && event.attemptId === reassignment.toAttemptId
131
+ && event.childRunId === start.childRunId);
132
+ const admitted = completed && firstAfter(envelope.events, completed.sequence, (event) => event.type === "CHILD_PATCH_ADMITTED"
133
+ && event.taskId === reassignment.taskId
134
+ && event.agentId === reassignment.toAgentId
135
+ && event.attemptId === reassignment.toAttemptId
136
+ && event.childRunId === completed.childRunId);
137
+ const admission = admitted && envelope.integration.admissions.find((decision) => decision.taskId === reassignment.taskId
138
+ && decision.agentId === reassignment.toAgentId
139
+ && decision.childRunId === admitted.childRunId
140
+ && decision.candidateId === admitted.candidateId
141
+ && evidenceIncludes(decision.evidence, admitted));
142
+ if (!failure || !start || !completed || !admitted || !admission)
143
+ continue;
144
+ facts.push({
145
+ factId: `derived:recovery:${reassignment.taskId}:${reassignment.fromAttemptId}:${reassignment.toAttemptId}`,
146
+ kind: "recovery",
147
+ derivationKind: "deterministic_graph_derivation",
148
+ taskIds: [reassignment.taskId],
149
+ agentIds: [reassignment.fromAgentId, reassignment.toAgentId],
150
+ childRunIds: compactUnique([failure.childRunId, completed.childRunId]),
151
+ attemptIds: [reassignment.fromAttemptId, reassignment.toAttemptId],
152
+ verificationIds: [],
153
+ evidence: [failure, reassigned, start, completed, admitted].map(eventReference),
154
+ });
155
+ }
156
+ return facts;
157
+ }
158
+ function blockingDependencyFacts(envelope) {
159
+ const tasks = new Map(envelope.topology.tasks.map((task) => [task.taskId, task]));
160
+ const facts = [];
161
+ for (const task of [...envelope.topology.tasks].sort((left, right) => left.taskId.localeCompare(right.taskId))) {
162
+ if (!new Set(["queued", "ready", "running"]).has(task.status))
163
+ continue;
164
+ for (const path of terminalDependencyPaths(task.taskId, tasks)) {
165
+ const blocking = tasks.get(path.at(-1));
166
+ const terminalType = blocking && TERMINAL_EVENT_TYPES[blocking.status];
167
+ if (!blocking || !terminalType)
168
+ continue;
169
+ const terminal = [...envelope.events].reverse().find((event) => event.type === terminalType && event.taskId === blocking.taskId);
170
+ if (!terminal)
171
+ continue;
172
+ facts.push({
173
+ factId: `derived:blocking:${path.join("->")}`,
174
+ kind: "blocking_dependency",
175
+ derivationKind: "deterministic_graph_derivation",
176
+ taskIds: path,
177
+ agentIds: compactUnique([terminal.agentId]),
178
+ childRunIds: compactUnique([terminal.childRunId]),
179
+ attemptIds: compactUnique([terminal.attemptId]),
180
+ verificationIds: [],
181
+ evidence: [eventReference(terminal)],
182
+ });
183
+ }
184
+ }
185
+ return facts;
186
+ }
187
+ function terminalDependencyPaths(startTaskId, tasks) {
188
+ const paths = [];
189
+ const visit = (currentId, path, seen) => {
190
+ const current = tasks.get(currentId);
191
+ if (!current)
192
+ return;
193
+ for (const dependencyId of [...current.dependsOn].sort()) {
194
+ if (seen.has(dependencyId))
195
+ continue;
196
+ const dependency = tasks.get(dependencyId);
197
+ if (!dependency)
198
+ continue;
199
+ const next = [...path, dependencyId];
200
+ if (TERMINAL_EVENT_TYPES[dependency.status])
201
+ paths.push(next);
202
+ else
203
+ visit(dependencyId, next, new Set([...seen, dependencyId]));
204
+ }
205
+ };
206
+ visit(startTaskId, [startTaskId], new Set([startTaskId]));
207
+ return paths;
208
+ }
209
+ function factFromEvent(kind, event) {
210
+ return {
211
+ factId: `observed:${event.sequence}:${event.eventId}`,
212
+ kind,
213
+ derivationKind: "observed",
214
+ taskIds: compactUnique([event.taskId]),
215
+ agentIds: compactUnique([event.agentId, event.toAgentId]),
216
+ childRunIds: compactUnique([event.childRunId]),
217
+ attemptIds: compactUnique([event.attemptId, event.relatedAttemptId]),
218
+ verificationIds: compactUnique([event.verificationId]),
219
+ evidence: [eventReference(event)],
220
+ };
221
+ }
222
+ function referencedEvent(events, evidence, type) {
223
+ return evidence
224
+ .map((reference) => events.find((event) => event.eventId === reference.eventId && event.sequence === reference.sequence))
225
+ .find((event) => event?.type === type);
226
+ }
227
+ function latestBefore(events, sequence, predicate) {
228
+ return [...events].reverse().find((event) => event.sequence < sequence && predicate(event));
229
+ }
230
+ function firstAfter(events, sequence, predicate) {
231
+ return events.find((event) => event.sequence > sequence && predicate(event));
232
+ }
233
+ function evidenceIncludes(evidence, event) {
234
+ return evidence.some((reference) => reference.eventId === event.eventId && reference.sequence === event.sequence);
235
+ }
236
+ function eventReference(event) {
237
+ return { eventId: event.eventId, sequence: event.sequence };
238
+ }
239
+ function cloneEvidence(evidence) {
240
+ return evidence.map((reference) => ({ ...reference }));
241
+ }
242
+ function cloneTask(task) {
243
+ return { ...task, dependsOn: [...task.dependsOn] };
244
+ }
245
+ function compactUnique(values) {
246
+ return [...new Set(values.filter((value) => typeof value === "string"))];
247
+ }
248
+ function compareDependencyEdges(left, right) {
249
+ return left.fromTaskId.localeCompare(right.fromTaskId) || left.toTaskId.localeCompare(right.toTaskId);
250
+ }
251
+ function compareFacts(left, right) {
252
+ const leftSequence = left.evidence.at(-1)?.sequence ?? Number.MAX_SAFE_INTEGER;
253
+ const rightSequence = right.evidence.at(-1)?.sequence ?? Number.MAX_SAFE_INTEGER;
254
+ const derivationOrder = (fact) => fact.derivationKind === "observed" ? 0 : 1;
255
+ return leftSequence - rightSequence
256
+ || derivationOrder(left) - derivationOrder(right)
257
+ || left.factId.localeCompare(right.factId);
258
+ }