@shapeshift-labs/frontier-swarm 0.5.23 → 0.5.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2295,6 +2295,45 @@ export function createRunProjectionFromSwarmRunEvents(events, options = {}) {
2295
2295
  metadata: toJsonObject(options.metadata)
2296
2296
  });
2297
2297
  }
2298
+ export function createSwarmRunFromRunProjection(projection, options) {
2299
+ const plan = options.plan;
2300
+ const results = plan.jobs
2301
+ .map((job) => deriveSwarmResultFromRunProjection(projection, job))
2302
+ .filter((result) => !!result);
2303
+ const run = createSwarmRun({
2304
+ id: options.id ?? projection.run.id ?? plan.runId,
2305
+ plan,
2306
+ startedAt: options.startedAt ?? parseRunTimeMs(projection.run.createdAt, plan.createdAt),
2307
+ status: options.status ?? deriveSwarmRunStatusFromResults(plan.jobs, results),
2308
+ events: createSwarmEventsFromRunProjection(projection),
2309
+ results,
2310
+ metadata: mergeSwarmMetadata([
2311
+ projection.run.metadata,
2312
+ options.metadata,
2313
+ { source: 'frontier-run.projection', frontierRunId: projection.run.id }
2314
+ ])
2315
+ });
2316
+ const finishedAt = latestSwarmResultFinishedAt(run.results);
2317
+ return finishedAt === undefined ? run : { ...run, finishedAt };
2318
+ }
2319
+ export function createSwarmQueueOverlayFromRunProjection(projection, options) {
2320
+ const run = createSwarmRunFromRunProjection(projection, {
2321
+ plan: options.plan,
2322
+ id: projection.run.id,
2323
+ startedAt: parseRunTimeMs(projection.run.createdAt, options.plan.createdAt),
2324
+ metadata: options.metadata
2325
+ });
2326
+ return createSwarmQueueOverlay({
2327
+ id: options.id,
2328
+ runId: run.id,
2329
+ results: run.results,
2330
+ generatedAt: options.generatedAt,
2331
+ metadata: mergeSwarmMetadata([
2332
+ options.metadata,
2333
+ { source: 'frontier-run.projection', frontierRunId: projection.run.id }
2334
+ ])
2335
+ });
2336
+ }
2298
2337
  export function createRunDashboardFromSwarmRun(input, options = {}) {
2299
2338
  const events = isFrontierRunEventList(input) ? input : createRunEventsFromSwarmRun(input, options);
2300
2339
  return createRunDashboardSnapshot(createRunProjectionFromSwarmRunEvents(events, options));
@@ -10452,6 +10491,258 @@ function swarmRunEventTime(options, fallback) {
10452
10491
  const value = options.now ?? fallback;
10453
10492
  return new Date(Number.isFinite(value) ? value : Date.now()).toISOString();
10454
10493
  }
10494
+ function createSwarmEventsFromRunProjection(projection) {
10495
+ return projection.events.map((event) => ({
10496
+ id: `frontier-run:${event.id}`,
10497
+ type: event.type,
10498
+ runId: projection.run.id,
10499
+ at: parseRunTimeMs(event.time, Date.now()),
10500
+ message: event.type,
10501
+ data: event.payload,
10502
+ metadata: pruneUndefinedJsonObject({
10503
+ source: 'frontier-run.event',
10504
+ eventId: event.id,
10505
+ actorId: event.actorId,
10506
+ actorSeq: event.actorSeq,
10507
+ parents: event.parents
10508
+ })
10509
+ }));
10510
+ }
10511
+ function deriveSwarmResultFromRunProjection(projection, job) {
10512
+ const graph = projection.run.graph;
10513
+ const nodes = Object.values(graph.nodes);
10514
+ const edges = Object.values(graph.edges);
10515
+ const attempt = nodes.find((node) => node.kind === 'attempt' && runNodeMatchesJob(node, job));
10516
+ const attemptId = attempt?.id ?? swarmRunAttemptNodeId(job.id);
10517
+ const patchNodes = nodes.filter((node) => node.kind === 'patch' && runNodeMatchesJob(node, job));
10518
+ const patchIds = new Set(patchNodes.map((node) => node.id));
10519
+ const verificationNodeIds = new Set(edges
10520
+ .filter((edge) => edge.from === attemptId && edge.type === 'verified-by')
10521
+ .map((edge) => edge.to));
10522
+ const verificationNodes = nodes.filter((node) => (node.kind === 'verification'
10523
+ && (runNodeMatchesJob(node, job) || verificationNodeIds.has(node.id))));
10524
+ const decisionNodes = nodes.filter((node) => (node.kind === 'decision'
10525
+ && (runNodeMatchesJob(node, job) || runDecisionSubjectsJob(node, job, patchIds))));
10526
+ const evidenceNodes = nodes.filter((node) => (node.kind === 'evidence'
10527
+ && (runNodeMatchesJob(node, job) || runEvidenceLinkedToJob(node, attemptId, patchIds, edges))));
10528
+ const hasProjectionResult = !!attempt || patchNodes.length > 0 || verificationNodes.length > 0 || evidenceNodes.length > 0 || decisionNodes.length > 0;
10529
+ if (!hasProjectionResult)
10530
+ return undefined;
10531
+ const attemptMetadata = attempt ? runNodeMetadata(attempt) : undefined;
10532
+ const patchMetadata = patchNodes.map(runNodeMetadata);
10533
+ const decisionMetadata = decisionNodes.map(runNodeMetadata);
10534
+ const mergedMetadata = mergeSwarmMetadata([
10535
+ attemptMetadata,
10536
+ ...patchMetadata,
10537
+ ...decisionMetadata,
10538
+ { source: 'frontier-run.projection', frontierRunId: projection.run.id }
10539
+ ]);
10540
+ const changedPaths = uniqueStrings(patchNodes.flatMap((node) => node.kind === 'patch' ? node.changedPaths : []));
10541
+ const changedRegions = uniqueStrings(patchMetadata.flatMap((metadata) => jsonStringArray(metadata?.changedRegions)));
10542
+ const ownershipViolations = uniqueStrings(patchMetadata.flatMap((metadata) => jsonStringArray(metadata?.ownershipViolations)));
10543
+ const evidencePaths = deriveRunEvidencePaths(nodes, edges, attemptId, patchIds, evidenceNodes);
10544
+ const patchPath = deriveRunPatchPath(nodes, edges, patchNodes);
10545
+ const status = deriveSwarmResultStatusFromRunAttempt(attempt, verificationNodes, patchNodes);
10546
+ const error = status === 'failed' || status === 'blocked'
10547
+ ? (runMetadataString(attemptMetadata, 'reason') ?? (attempt?.kind === 'attempt' ? attempt.reason : undefined))
10548
+ : undefined;
10549
+ const startedAt = attempt?.kind === 'attempt' ? parseOptionalRunTimeMs(attempt.startedAt) : undefined;
10550
+ const finishedAt = attempt?.kind === 'attempt' ? parseOptionalRunTimeMs(attempt.endedAt) : undefined;
10551
+ return {
10552
+ jobId: job.id,
10553
+ status,
10554
+ mergeReadiness: readSwarmMergeReadiness(attemptMetadata, patchMetadata, decisionMetadata),
10555
+ ...(startedAt !== undefined ? { startedAt } : {}),
10556
+ ...(finishedAt !== undefined ? { finishedAt } : {}),
10557
+ ...(runMetadataNumber(attemptMetadata, 'exitCode') !== undefined ? { exitCode: runMetadataNumber(attemptMetadata, 'exitCode') } : {}),
10558
+ ...(runMetadataString(attemptMetadata, 'signal') ? { signal: runMetadataString(attemptMetadata, 'signal') } : {}),
10559
+ changedPaths,
10560
+ changedRegions,
10561
+ ownershipViolations,
10562
+ evidencePaths,
10563
+ ...(patchPath ? { patchPath } : {}),
10564
+ queueItemIds: uniqueStrings([
10565
+ ...jsonStringArray(attemptMetadata?.queueItemIds),
10566
+ ...patchMetadata.flatMap((metadata) => jsonStringArray(metadata?.queueItemIds)),
10567
+ ...decisionMetadata.flatMap((metadata) => jsonStringArray(metadata?.queueItemIds))
10568
+ ]),
10569
+ riskLevel: readSwarmRiskLevel(attemptMetadata, patchMetadata),
10570
+ mergeDisposition: readSwarmMergeDisposition(attemptMetadata, patchMetadata, decisionMetadata),
10571
+ verification: verificationNodes.map((node) => runVerificationNodeToSwarmResult(node)),
10572
+ ...(attemptMetadata?.semanticImport !== undefined ? { semanticImport: cloneJsonValue(attemptMetadata.semanticImport) } : {}),
10573
+ ...(patchNodes[0]?.kind === 'patch' && patchNodes[0].summary ? { lastMessage: patchNodes[0].summary } : {}),
10574
+ ...(error ? { error } : {}),
10575
+ ...(mergedMetadata ? { metadata: mergedMetadata } : {})
10576
+ };
10577
+ }
10578
+ function runNodeMatchesJob(node, job) {
10579
+ const metadata = runNodeMetadata(node);
10580
+ const metadataJobId = runMetadataString(metadata, 'jobId');
10581
+ if (metadataJobId === job.id)
10582
+ return true;
10583
+ if (node.kind === 'attempt' && node.taskId === swarmRunTaskNodeId(job.taskId))
10584
+ return true;
10585
+ if (node.kind === 'task' && node.id === swarmRunTaskNodeId(job.taskId))
10586
+ return true;
10587
+ if ((node.kind === 'attempt' || node.kind === 'patch') && stripRunNodePrefix(node.id, node.kind) === job.id)
10588
+ return true;
10589
+ return false;
10590
+ }
10591
+ function runDecisionSubjectsJob(node, job, patchIds) {
10592
+ if (node.kind !== 'decision')
10593
+ return false;
10594
+ const taskId = swarmRunTaskNodeId(job.taskId);
10595
+ return node.subjectIds.some((subjectId) => subjectId === taskId || patchIds.has(subjectId));
10596
+ }
10597
+ function runEvidenceLinkedToJob(node, attemptId, patchIds, edges) {
10598
+ if (node.kind !== 'evidence')
10599
+ return false;
10600
+ return edges.some((edge) => edge.to === node.id && (edge.from === attemptId || patchIds.has(edge.from)));
10601
+ }
10602
+ function deriveRunEvidencePaths(nodes, edges, attemptId, patchIds, evidenceNodes) {
10603
+ const artifacts = new Map(nodes
10604
+ .filter((node) => node.kind === 'artifact')
10605
+ .map((node) => [node.id, node]));
10606
+ const subjectIds = new Set([attemptId, ...patchIds]);
10607
+ const linkedArtifactIds = edges
10608
+ .filter((edge) => edge.type === 'produces-artifact' && subjectIds.has(edge.from))
10609
+ .map((edge) => edge.to);
10610
+ return uniqueStrings([
10611
+ ...evidenceNodes.flatMap((node) => node.kind === 'evidence' ? (node.artifactIds ?? []) : [])
10612
+ .map((artifactId) => runArtifactPath(artifacts.get(artifactId)))
10613
+ .filter((file) => !!file),
10614
+ ...linkedArtifactIds
10615
+ .map((artifactId) => artifacts.get(artifactId))
10616
+ .filter((node) => !!node && node.kind === 'artifact' && node.artifactType !== 'patch')
10617
+ .map(runArtifactPath)
10618
+ .filter((file) => !!file)
10619
+ ]);
10620
+ }
10621
+ function deriveRunPatchPath(nodes, edges, patchNodes) {
10622
+ const artifacts = new Map(nodes
10623
+ .filter((node) => node.kind === 'artifact')
10624
+ .map((node) => [node.id, node]));
10625
+ for (const patch of patchNodes) {
10626
+ if (patch.kind !== 'patch')
10627
+ continue;
10628
+ const metadataPatchPath = runMetadataString(runNodeMetadata(patch), 'patchPath');
10629
+ if (metadataPatchPath)
10630
+ return metadataPatchPath;
10631
+ if (patch.artifactId) {
10632
+ const artifactPath = runArtifactPath(artifacts.get(patch.artifactId));
10633
+ if (artifactPath)
10634
+ return artifactPath;
10635
+ }
10636
+ const attached = edges
10637
+ .filter((edge) => edge.from === patch.id && edge.type === 'produces-artifact')
10638
+ .map((edge) => artifacts.get(edge.to))
10639
+ .find((node) => node?.kind === 'artifact' && node.artifactType === 'patch');
10640
+ const attachedPath = runArtifactPath(attached);
10641
+ if (attachedPath)
10642
+ return attachedPath;
10643
+ }
10644
+ return undefined;
10645
+ }
10646
+ function runVerificationNodeToSwarmResult(node) {
10647
+ if (node.kind !== 'verification')
10648
+ return { name: node.id };
10649
+ const metadata = runNodeMetadata(node);
10650
+ const command = node.command ? [node.command, ...(node.args ?? [])] : [];
10651
+ return {
10652
+ name: node.title ?? node.id,
10653
+ command,
10654
+ ...(command.length ? { commandLine: command.join(' ') } : {}),
10655
+ ...(node.cwd ? { cwd: node.cwd } : {}),
10656
+ ...(node.exitCode !== undefined ? { status: node.exitCode } : { status: node.status === 'passed' ? 0 : node.status === 'failed' ? 1 : undefined }),
10657
+ ...(runMetadataNumber(metadata, 'durationMs') !== undefined ? { durationMs: runMetadataNumber(metadata, 'durationMs') } : {}),
10658
+ stdoutTail: jsonStringArray(metadata?.stdoutTail),
10659
+ stderrTail: jsonStringArray(metadata?.stderrTail),
10660
+ required: node.required ?? true,
10661
+ ...(runMetadataString(metadata, 'category') ? { category: runMetadataString(metadata, 'category') } : {}),
10662
+ ...(metadata ? { metadata } : {})
10663
+ };
10664
+ }
10665
+ function deriveSwarmResultStatusFromRunAttempt(attempt, verificationNodes, patchNodes) {
10666
+ if (attempt?.kind === 'attempt') {
10667
+ if (attempt.status === 'completed')
10668
+ return 'completed';
10669
+ if (attempt.status === 'running')
10670
+ return 'running';
10671
+ if (attempt.status === 'queued')
10672
+ return 'scheduled';
10673
+ if (attempt.status === 'cancelled' || attempt.status === 'timed-out' || attempt.status === 'failed')
10674
+ return 'failed';
10675
+ }
10676
+ if (verificationNodes.some((node) => node.kind === 'verification' && node.required !== false && node.status === 'failed'))
10677
+ return 'failed';
10678
+ if (patchNodes.length > 0 || verificationNodes.length > 0)
10679
+ return 'completed';
10680
+ return 'planned';
10681
+ }
10682
+ function deriveSwarmRunStatusFromResults(jobs, results) {
10683
+ if (results.length === 0)
10684
+ return 'planned';
10685
+ if (results.some((result) => result.status === 'failed'))
10686
+ return 'failed';
10687
+ if (results.some((result) => result.status === 'blocked'))
10688
+ return 'blocked';
10689
+ if (results.some((result) => result.status === 'running'))
10690
+ return 'running';
10691
+ if (results.length >= jobs.length && results.every((result) => result.status === 'completed' || result.status === 'verified'))
10692
+ return 'completed';
10693
+ return 'running';
10694
+ }
10695
+ function latestSwarmResultFinishedAt(results) {
10696
+ const finishedAt = results
10697
+ .map((result) => result.finishedAt)
10698
+ .filter((value) => value !== undefined);
10699
+ return finishedAt.length ? Math.max(...finishedAt) : undefined;
10700
+ }
10701
+ function readSwarmMergeReadiness(attemptMetadata, patchMetadata, decisionMetadata) {
10702
+ return readFirstMetadataString('mergeReadiness', [attemptMetadata, ...patchMetadata, ...decisionMetadata]);
10703
+ }
10704
+ function readSwarmMergeDisposition(attemptMetadata, patchMetadata, decisionMetadata) {
10705
+ return readFirstMetadataString('mergeDisposition', [attemptMetadata])
10706
+ ?? readFirstMetadataString('disposition', [...patchMetadata, ...decisionMetadata]);
10707
+ }
10708
+ function readSwarmRiskLevel(attemptMetadata, patchMetadata) {
10709
+ return readFirstMetadataString('riskLevel', [attemptMetadata, ...patchMetadata]);
10710
+ }
10711
+ function readFirstMetadataString(key, metadata) {
10712
+ for (const entry of metadata) {
10713
+ const value = runMetadataString(entry, key);
10714
+ if (value)
10715
+ return value;
10716
+ }
10717
+ return undefined;
10718
+ }
10719
+ function runNodeMetadata(node) {
10720
+ return toJsonObject(node.metadata);
10721
+ }
10722
+ function runMetadataString(metadata, key) {
10723
+ const value = metadata?.[key];
10724
+ return typeof value === 'string' && value ? value : undefined;
10725
+ }
10726
+ function runMetadataNumber(metadata, key) {
10727
+ const value = metadata?.[key];
10728
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
10729
+ }
10730
+ function runArtifactPath(node) {
10731
+ if (!node || node.kind !== 'artifact')
10732
+ return undefined;
10733
+ return node.path ?? node.uri ?? node.title;
10734
+ }
10735
+ function stripRunNodePrefix(id, prefix) {
10736
+ return id.startsWith(`${prefix}:`) ? id.slice(prefix.length + 1) : undefined;
10737
+ }
10738
+ function parseRunTimeMs(value, fallback) {
10739
+ const parsed = value ? Date.parse(value) : NaN;
10740
+ return Number.isFinite(parsed) ? parsed : fallback;
10741
+ }
10742
+ function parseOptionalRunTimeMs(value) {
10743
+ const parsed = value ? Date.parse(value) : NaN;
10744
+ return Number.isFinite(parsed) ? parsed : undefined;
10745
+ }
10455
10746
  function isFrontierRunEventList(input) {
10456
10747
  return Array.isArray(input);
10457
10748
  }