@shapeshift-labs/frontier-swarm 0.5.23 → 0.5.25
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.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +291 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
}
|