@bastani/atomic 0.9.44-alpha.4 → 0.9.44-alpha.6
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/CHANGELOG.md +26 -0
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/broker/broker.ts +13 -1
- package/dist/builtin/intercom/index.bundle.mjs +9 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/types.ts +7 -0
- package/dist/builtin/intercom/workflow-delivery-states.ts +48 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +26 -0
- package/dist/builtin/workflows/builtin/{chunk-zjecnjxx.js → chunk-6901nsd6.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-k8d7ehjx.js → chunk-jvna28mq.js} +381 -130
- package/dist/builtin/workflows/builtin/{chunk-qxpymnaf.js → chunk-vwx023ge.js} +1 -1
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/journal/internal.d.ts +15 -1
- package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +11 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +2457 -736
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +2329 -730
- package/dist/builtin/workflows/src/index.js +2328 -730
- package/dist/builtin/workflows/src/local-worker-entry.mjs +2329 -730
- package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
- package/dist/core/agent-session-custom-message-commit.js +16 -0
- package/dist/core/agent-session-custom-message-commit.js.map +1 -1
- package/dist/core/storage/errors.d.ts +5 -1
- package/dist/core/storage/errors.d.ts.map +1 -1
- package/dist/core/storage/errors.js +8 -2
- package/dist/core/storage/errors.js.map +1 -1
- package/dist/core/storage/node-sqlite.d.ts +2 -1
- package/dist/core/storage/node-sqlite.d.ts.map +1 -1
- package/dist/core/storage/node-sqlite.js +213 -20
- package/dist/core/storage/node-sqlite.js.map +1 -1
- package/docs/intercom/reference.md +2 -0
- package/docs/workflows/operations.md +1 -1
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
|
@@ -122,6 +122,12 @@ class ScopedDurableBackend {
|
|
|
122
122
|
getPendingStageMessageDeliveryCount(workflowId, messageId, logicalRunId = workflowId) {
|
|
123
123
|
return this.inner.getPendingStageMessageDeliveryCount?.(this.scope.rootWorkflowId, messageId, logicalRunId) ?? 0;
|
|
124
124
|
}
|
|
125
|
+
pendingStageDeliveryReceiptChanges(_workflowId, cursor) {
|
|
126
|
+
return this.inner.pendingStageDeliveryReceiptChanges?.(this.scope.rootWorkflowId, cursor);
|
|
127
|
+
}
|
|
128
|
+
pendingStageDeliveryCountVersion(_workflowId) {
|
|
129
|
+
return this.inner.pendingStageDeliveryCountVersion?.(this.scope.rootWorkflowId);
|
|
130
|
+
}
|
|
125
131
|
async readPendingStageMessageDeliveryCount(workflowId, messageId, logicalRunId = workflowId) {
|
|
126
132
|
return this.inner.readPendingStageMessageDeliveryCount === undefined ? this.getPendingStageMessageDeliveryCount(workflowId, messageId, logicalRunId) : await this.inner.readPendingStageMessageDeliveryCount(this.scope.rootWorkflowId, messageId, logicalRunId);
|
|
127
133
|
}
|
|
@@ -1474,61 +1480,220 @@ function createStoreContext(state = createStoreState()) {
|
|
|
1474
1480
|
|
|
1475
1481
|
// dist/builtin/workflows/src/shared/pending-stage-retention.ts
|
|
1476
1482
|
var PENDING_STAGE_SETTLED_MESSAGE_LIMIT = 50;
|
|
1483
|
+
function isSettledPendingStageMessage(entry) {
|
|
1484
|
+
return entry.status === "delivered" || entry.status === "undeliverable" && entry.undeliverableNotifiedAt !== undefined;
|
|
1485
|
+
}
|
|
1477
1486
|
async function retainPendingStageMessages(messages, archive) {
|
|
1478
|
-
const settled = messages.filter(
|
|
1487
|
+
const settled = messages.filter(isSettledPendingStageMessage);
|
|
1479
1488
|
const expired = new Set(settled.slice(0, Math.max(0, settled.length - PENDING_STAGE_SETTLED_MESSAGE_LIMIT)));
|
|
1480
1489
|
for (const entry of expired)
|
|
1481
1490
|
await archive(entry);
|
|
1482
1491
|
return expired.size === 0 ? messages : messages.filter((entry) => !expired.has(entry));
|
|
1483
1492
|
}
|
|
1484
1493
|
|
|
1494
|
+
// dist/builtin/workflows/src/shared/pending-stage-transition-ledger.ts
|
|
1495
|
+
class PendingStageTransitionLedger {
|
|
1496
|
+
compacted = [];
|
|
1497
|
+
settled = 0;
|
|
1498
|
+
stages = new Map;
|
|
1499
|
+
fixed = new WeakSet;
|
|
1500
|
+
reconciled = [];
|
|
1501
|
+
reconciledVersion;
|
|
1502
|
+
reconcile(backend, runId, messages) {
|
|
1503
|
+
if (backend.getPendingStageMessageDeliveryCount === undefined)
|
|
1504
|
+
return messages;
|
|
1505
|
+
const version = backend.pendingStageDeliveryCountVersion?.(runId);
|
|
1506
|
+
const base = version !== undefined && version === this.reconciledVersion ? this.reconciled : undefined;
|
|
1507
|
+
let recovered;
|
|
1508
|
+
for (let index = 0;index < messages.length; index++) {
|
|
1509
|
+
const entry = messages[index];
|
|
1510
|
+
if (entry.sticky !== true || base !== undefined && base[index] === entry)
|
|
1511
|
+
continue;
|
|
1512
|
+
const count = backend.getPendingStageMessageDeliveryCount(runId, entry.id, entry.runId);
|
|
1513
|
+
if (count <= (entry.deliveryCount ?? entry.deliveries?.length ?? 0))
|
|
1514
|
+
continue;
|
|
1515
|
+
recovered ??= [...messages];
|
|
1516
|
+
recovered[index] = { ...entry, deliveryCount: count };
|
|
1517
|
+
}
|
|
1518
|
+
const result = recovered ?? messages;
|
|
1519
|
+
this.reconciled = result;
|
|
1520
|
+
this.reconciledVersion = version;
|
|
1521
|
+
return result;
|
|
1522
|
+
}
|
|
1523
|
+
async compact(backend, runId, messages, runs, archiveSettled) {
|
|
1524
|
+
const stageStatus = createStageStatusLookup(runs);
|
|
1525
|
+
const endedStages = [];
|
|
1526
|
+
const dirty = new Set;
|
|
1527
|
+
for (const [deliveryRunId, byStage] of this.stages) {
|
|
1528
|
+
for (const [stageId, entries] of byStage) {
|
|
1529
|
+
if (!isEndedStageStatus(stageStatus(deliveryRunId, stageId)))
|
|
1530
|
+
continue;
|
|
1531
|
+
endedStages.push([deliveryRunId, stageId]);
|
|
1532
|
+
for (const entry of entries)
|
|
1533
|
+
dirty.add(entry);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
const base = this.compacted;
|
|
1537
|
+
const output = [];
|
|
1538
|
+
const removed = [];
|
|
1539
|
+
const added = [];
|
|
1540
|
+
let settled = this.settled;
|
|
1541
|
+
const length = Math.max(base.length, messages.length);
|
|
1542
|
+
for (let index = 0;index < length; index++) {
|
|
1543
|
+
const previous = base[index];
|
|
1544
|
+
const entry = messages[index];
|
|
1545
|
+
if (previous === entry && !dirty.has(entry)) {
|
|
1546
|
+
output.push(entry);
|
|
1547
|
+
continue;
|
|
1548
|
+
}
|
|
1549
|
+
if (previous !== undefined) {
|
|
1550
|
+
removed.push(previous);
|
|
1551
|
+
if (isSettledPendingStageMessage(previous))
|
|
1552
|
+
settled--;
|
|
1553
|
+
}
|
|
1554
|
+
if (entry === undefined)
|
|
1555
|
+
continue;
|
|
1556
|
+
if (isSettledPendingStageMessage(entry))
|
|
1557
|
+
settled++;
|
|
1558
|
+
const next = this.fixed.has(entry) && !dirty.has(entry) ? entry : await compactEntryDeliveries(backend, runId, entry, stageStatus);
|
|
1559
|
+
output.push(next);
|
|
1560
|
+
added.push(next);
|
|
1561
|
+
}
|
|
1562
|
+
let retained = output;
|
|
1563
|
+
if (archiveSettled !== undefined && settled > PENDING_STAGE_SETTLED_MESSAGE_LIMIT) {
|
|
1564
|
+
retained = await retainPendingStageMessages(output, archiveSettled);
|
|
1565
|
+
settled = PENDING_STAGE_SETTLED_MESSAGE_LIMIT;
|
|
1566
|
+
}
|
|
1567
|
+
return {
|
|
1568
|
+
messages: retained,
|
|
1569
|
+
commit: () => {
|
|
1570
|
+
for (const [deliveryRunId, stageId] of endedStages) {
|
|
1571
|
+
const byStage = this.stages.get(deliveryRunId);
|
|
1572
|
+
byStage?.delete(stageId);
|
|
1573
|
+
if (byStage?.size === 0)
|
|
1574
|
+
this.stages.delete(deliveryRunId);
|
|
1575
|
+
}
|
|
1576
|
+
for (const entry of dirty)
|
|
1577
|
+
this.fixed.delete(entry);
|
|
1578
|
+
for (const entry of removed)
|
|
1579
|
+
this.unindex(entry);
|
|
1580
|
+
for (const entry of added)
|
|
1581
|
+
this.index(entry);
|
|
1582
|
+
this.compacted = retained;
|
|
1583
|
+
this.settled = settled;
|
|
1584
|
+
}
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
index(entry) {
|
|
1588
|
+
this.fixed.add(entry);
|
|
1589
|
+
for (const delivery of entry.deliveries ?? []) {
|
|
1590
|
+
let byStage = this.stages.get(delivery.runId);
|
|
1591
|
+
if (byStage === undefined) {
|
|
1592
|
+
byStage = new Map;
|
|
1593
|
+
this.stages.set(delivery.runId, byStage);
|
|
1594
|
+
}
|
|
1595
|
+
let entries = byStage.get(delivery.stageId);
|
|
1596
|
+
if (entries === undefined) {
|
|
1597
|
+
entries = new Set;
|
|
1598
|
+
byStage.set(delivery.stageId, entries);
|
|
1599
|
+
}
|
|
1600
|
+
entries.add(entry);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
unindex(entry) {
|
|
1604
|
+
this.fixed.delete(entry);
|
|
1605
|
+
for (const delivery of entry.deliveries ?? []) {
|
|
1606
|
+
const byStage = this.stages.get(delivery.runId);
|
|
1607
|
+
const entries = byStage?.get(delivery.stageId);
|
|
1608
|
+
if (entries === undefined)
|
|
1609
|
+
continue;
|
|
1610
|
+
entries.delete(entry);
|
|
1611
|
+
if (entries.size > 0)
|
|
1612
|
+
continue;
|
|
1613
|
+
byStage.delete(delivery.stageId);
|
|
1614
|
+
if (byStage.size === 0)
|
|
1615
|
+
this.stages.delete(delivery.runId);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
function isCompactingBackend(backend) {
|
|
1620
|
+
return backend.archivePendingStageDeliveryReceipt !== undefined && backend.hasPendingStageDeliveryReceipt !== undefined;
|
|
1621
|
+
}
|
|
1622
|
+
function createStageStatusLookup(runs) {
|
|
1623
|
+
let runById;
|
|
1624
|
+
return (runId, stageId) => {
|
|
1625
|
+
if (runById === undefined) {
|
|
1626
|
+
runById = new Map;
|
|
1627
|
+
for (const run of runs)
|
|
1628
|
+
if (!runById.has(run.id))
|
|
1629
|
+
runById.set(run.id, run);
|
|
1630
|
+
}
|
|
1631
|
+
return runById.get(runId)?.stages.find((stage) => stage.id === stageId)?.status;
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
function isEndedStageStatus(status) {
|
|
1635
|
+
return status === "completed" || status === "skipped" || status === "failed";
|
|
1636
|
+
}
|
|
1637
|
+
async function compactEntryDeliveries(backend, runId, entry, stageStatus) {
|
|
1638
|
+
if (entry.sticky !== true || entry.deliveries === undefined)
|
|
1639
|
+
return entry;
|
|
1640
|
+
const latest = new Map;
|
|
1641
|
+
entry.deliveries.forEach((delivery, index) => {
|
|
1642
|
+
latest.set(deliverySlot(delivery), index);
|
|
1643
|
+
});
|
|
1644
|
+
const deliveries = [];
|
|
1645
|
+
let archivedThrough = -1;
|
|
1646
|
+
for (let index = 0;index < entry.deliveries.length; index++) {
|
|
1647
|
+
const delivery = entry.deliveries[index];
|
|
1648
|
+
if (entry.status !== "queued" || isEndedStageStatus(stageStatus(delivery.runId, delivery.stageId)) || latest.get(deliverySlot(delivery)) !== index && delivery.sessionId !== undefined) {
|
|
1649
|
+
while (archivedThrough < index) {
|
|
1650
|
+
const prefixIndex = archivedThrough + 1;
|
|
1651
|
+
const receipt = entry.deliveries[prefixIndex];
|
|
1652
|
+
if (!await backend.hasPendingStageDeliveryReceipt(runId, entry.id, receipt, entry.runId))
|
|
1653
|
+
await backend.archivePendingStageDeliveryReceipt(runId, entry.id, receipt, {
|
|
1654
|
+
messageRunId: entry.runId,
|
|
1655
|
+
deliveryCount: (entry.deliveryCount ?? entry.deliveries.length) - entry.deliveries.length + prefixIndex + 1
|
|
1656
|
+
});
|
|
1657
|
+
archivedThrough = prefixIndex;
|
|
1658
|
+
}
|
|
1659
|
+
} else {
|
|
1660
|
+
deliveries.push(delivery);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
return deliveries.length === entry.deliveries.length ? entry : { ...entry, deliveries };
|
|
1664
|
+
}
|
|
1665
|
+
function deliverySlot(delivery) {
|
|
1666
|
+
return `${delivery.runId.length}:${delivery.runId}${delivery.stageId.length}:${delivery.stageId}${delivery.admission === undefined ? "-" : `+${delivery.admission}`}`;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1485
1669
|
// dist/builtin/workflows/src/shared/store-pending-stage-delivery-methods.ts
|
|
1486
1670
|
function createPendingStageDeliveryStoreMethods(context) {
|
|
1487
|
-
const
|
|
1488
|
-
const
|
|
1489
|
-
const previous = transitions.get(runId) ?? Promise.resolve();
|
|
1490
|
-
const result = previous.catch(() => {
|
|
1491
|
-
return;
|
|
1492
|
-
}).then(transition);
|
|
1493
|
-
const settled = result.then(() => {
|
|
1494
|
-
return;
|
|
1495
|
-
}, () => {
|
|
1496
|
-
return;
|
|
1497
|
-
});
|
|
1498
|
-
transitions.set(runId, settled);
|
|
1499
|
-
settled.finally(() => {
|
|
1500
|
-
if (transitions.get(runId) === settled)
|
|
1501
|
-
transitions.delete(runId);
|
|
1502
|
-
});
|
|
1503
|
-
return await result;
|
|
1504
|
-
};
|
|
1671
|
+
const ledgers = new PendingStageLedgers;
|
|
1672
|
+
const batches = new PendingStageTransitionBatches(context, ledgers);
|
|
1505
1673
|
return {
|
|
1506
1674
|
async queueStageMessage(input, senderGroup, runGroup, backend) {
|
|
1507
|
-
return await
|
|
1675
|
+
return await batches.run(input.runId, backend, async (batch) => {
|
|
1508
1676
|
const run = context.findRun(input.runId);
|
|
1509
1677
|
if (run === undefined)
|
|
1510
1678
|
return;
|
|
1511
1679
|
const stageIdentity = resolvePendingStageIdentity(run, input.stageKey);
|
|
1512
1680
|
const receipt = await backend.readSettledPendingStageMessage?.(input.runId, input.message.id);
|
|
1513
|
-
const result = queueStageMessage(receipt === undefined ? run
|
|
1681
|
+
const result = queueStageMessage(receipt === undefined ? batch.messages(run) : [...batch.messages(run), receipt], input, senderGroup, runGroup, stageIdentity);
|
|
1514
1682
|
if (result.ok && !result.deduplicated) {
|
|
1515
|
-
|
|
1516
|
-
const live = context.findRun(input.runId) ?? run;
|
|
1517
|
-
live.pendingStageMessages = [...messages];
|
|
1518
|
-
context.bumpAndNotify();
|
|
1683
|
+
await batch.apply(backend, input.runId, result.messages);
|
|
1519
1684
|
}
|
|
1520
|
-
return result.ok ? { ...result, messages: (context.findRun(input.runId) ?? run)
|
|
1685
|
+
return result.ok ? { ...result, messages: batch.messages(context.findRun(input.runId) ?? run) } : result;
|
|
1521
1686
|
});
|
|
1522
1687
|
},
|
|
1523
1688
|
async validateLiveStageMessage(input) {
|
|
1524
|
-
return await
|
|
1689
|
+
return await batches.run(input.runId, undefined, async (batch) => {
|
|
1525
1690
|
const run = context.findRun(input.runId);
|
|
1526
1691
|
if (run === undefined)
|
|
1527
1692
|
return;
|
|
1528
1693
|
const availableBackend = availableRunDurableBackend(context.state.runs, input.runId) ?? context.backend;
|
|
1529
1694
|
const backend = availableBackend === undefined ? undefined : durableBackendForRun(availableBackend, context.state.runs, input.runId);
|
|
1530
1695
|
const receipt = await backend?.readSettledPendingStageMessage?.(input.runId, input.message.id);
|
|
1531
|
-
const result = queueStageMessage(receipt === undefined ? run
|
|
1696
|
+
const result = queueStageMessage(receipt === undefined ? batch.messages(run) : [...batch.messages(run), receipt], input, undefined, undefined, resolvePendingStageIdentity(run, input.stageKey));
|
|
1532
1697
|
if (!result.ok) {
|
|
1533
1698
|
return result.reason === "message_id_conflict" ? { outcome: "message_id_conflict", messageId: result.messageId } : { outcome: "forward" };
|
|
1534
1699
|
}
|
|
@@ -1550,77 +1715,65 @@ function createPendingStageDeliveryStoreMethods(context) {
|
|
|
1550
1715
|
return pendingStageMessagesFor(run?.pendingStageMessages ?? [], runId, stageKey, run === undefined ? undefined : resolvePendingStageIdentity(run, stageKey));
|
|
1551
1716
|
},
|
|
1552
1717
|
async markPendingStageMessageDelivered(runId, stageKey, messageId, deliveredAt, backend) {
|
|
1553
|
-
return await
|
|
1718
|
+
return await batches.run(runId, backend, async (batch) => {
|
|
1554
1719
|
const run = context.findRun(runId);
|
|
1555
1720
|
if (run === undefined)
|
|
1556
1721
|
return false;
|
|
1557
|
-
const current = run
|
|
1722
|
+
const current = batch.messages(run);
|
|
1558
1723
|
const next = markPendingStageMessageDelivered(current, runId, stageKey, messageId, deliveredAt, resolvePendingStageIdentity(run, stageKey));
|
|
1559
1724
|
if (next === current)
|
|
1560
1725
|
return false;
|
|
1561
|
-
|
|
1562
|
-
const live = context.findRun(runId) ?? run;
|
|
1563
|
-
live.pendingStageMessages = [...messages];
|
|
1564
|
-
context.bumpAndNotify();
|
|
1726
|
+
await batch.apply(backend, runId, next);
|
|
1565
1727
|
return true;
|
|
1566
1728
|
});
|
|
1567
1729
|
},
|
|
1568
1730
|
async markPendingStageMessageUndeliverable(runId, stageKey, messageId, reason, backend) {
|
|
1569
|
-
return await
|
|
1731
|
+
return await batches.run(runId, backend, async (batch) => {
|
|
1570
1732
|
const run = context.findRun(runId);
|
|
1571
1733
|
if (run === undefined)
|
|
1572
1734
|
return false;
|
|
1573
|
-
const current = run
|
|
1735
|
+
const current = batch.messages(run);
|
|
1574
1736
|
const next = markPendingStageMessageUndeliverable(current, runId, stageKey, messageId, reason, resolvePendingStageIdentity(run, stageKey));
|
|
1575
1737
|
if (next === current)
|
|
1576
1738
|
return false;
|
|
1577
|
-
|
|
1578
|
-
const live = context.findRun(runId) ?? run;
|
|
1579
|
-
live.pendingStageMessages = [...messages];
|
|
1580
|
-
context.bumpAndNotify();
|
|
1739
|
+
await batch.apply(backend, runId, next);
|
|
1581
1740
|
return true;
|
|
1582
1741
|
});
|
|
1583
1742
|
},
|
|
1584
1743
|
async markPendingStageMessageUndeliverableNotified(runId, stageKey, messageId, notificationId, notifiedAt, backend) {
|
|
1585
|
-
return await
|
|
1744
|
+
return await batches.run(runId, backend, async (batch) => {
|
|
1586
1745
|
const run = context.findRun(runId);
|
|
1587
1746
|
if (run === undefined)
|
|
1588
1747
|
return false;
|
|
1589
|
-
const current = run
|
|
1748
|
+
const current = batch.messages(run);
|
|
1590
1749
|
const next = markPendingStageMessageUndeliverableNotified(current, runId, stageKey, messageId, notificationId, notifiedAt);
|
|
1591
1750
|
if (next === current)
|
|
1592
1751
|
return false;
|
|
1593
|
-
|
|
1594
|
-
const live = context.findRun(runId) ?? run;
|
|
1595
|
-
live.pendingStageMessages = [...messages];
|
|
1596
|
-
context.bumpAndNotify();
|
|
1752
|
+
await batch.apply(backend, runId, next);
|
|
1597
1753
|
return true;
|
|
1598
1754
|
});
|
|
1599
1755
|
},
|
|
1600
1756
|
async queueStickyStageMessage(input, senderGroup, runGroup, backend) {
|
|
1601
|
-
return await
|
|
1757
|
+
return await batches.run(input.runId, backend, async (batch) => {
|
|
1602
1758
|
const run = context.findRun(input.runId);
|
|
1603
1759
|
if (run === undefined)
|
|
1604
1760
|
return;
|
|
1605
1761
|
const receipt = await backend.readSettledPendingStageMessage?.(input.runId, input.message.id);
|
|
1606
|
-
const result = queueStickyStageMessage(receipt === undefined ? run
|
|
1762
|
+
const result = queueStickyStageMessage(receipt === undefined ? batch.messages(run) : [...batch.messages(run), receipt], input, senderGroup, runGroup);
|
|
1607
1763
|
if (result.ok && !result.deduplicated) {
|
|
1608
|
-
|
|
1609
|
-
const live = context.findRun(input.runId) ?? run;
|
|
1610
|
-
live.pendingStageMessages = [...messages];
|
|
1611
|
-
context.bumpAndNotify();
|
|
1764
|
+
await batch.apply(backend, input.runId, result.messages);
|
|
1612
1765
|
}
|
|
1613
|
-
return result.ok ? { ...result, messages: (context.findRun(input.runId) ?? run)
|
|
1766
|
+
return result.ok ? { ...result, messages: batch.messages(context.findRun(input.runId) ?? run) } : result;
|
|
1614
1767
|
});
|
|
1615
1768
|
},
|
|
1616
1769
|
async recordPendingStageMessageDeliveries(runId, messageId, records, deliveredAt, backend) {
|
|
1617
|
-
return await
|
|
1770
|
+
return await batches.run(runId, backend, async (batch) => {
|
|
1618
1771
|
const run = context.findRun(runId);
|
|
1619
1772
|
if (run === undefined)
|
|
1620
1773
|
return false;
|
|
1621
1774
|
if (records.length === 0)
|
|
1622
1775
|
await backend.readPendingStageMessageDeliveryCount?.(runId, messageId, runId);
|
|
1623
|
-
let current =
|
|
1776
|
+
let current = ledgers.for(run).reconcile(backend, runId, batch.messages(run));
|
|
1624
1777
|
const unconfirmedRecords = [];
|
|
1625
1778
|
for (const record of records) {
|
|
1626
1779
|
const contextReceipt = record.admission === "transport" ? { ...record, admission: "context" } : undefined;
|
|
@@ -1628,35 +1781,26 @@ function createPendingStageDeliveryStoreMethods(context) {
|
|
|
1628
1781
|
unconfirmedRecords.push(record);
|
|
1629
1782
|
}
|
|
1630
1783
|
}
|
|
1631
|
-
current =
|
|
1632
|
-
|
|
1633
|
-
run.pendingStageMessages = [...current];
|
|
1634
|
-
context.bumpAndNotify();
|
|
1635
|
-
}
|
|
1784
|
+
current = ledgers.for(run).reconcile(backend, runId, current);
|
|
1785
|
+
batch.reconciled(run, current);
|
|
1636
1786
|
const next = recordPendingStageMessageDeliveries(current, runId, messageId, unconfirmedRecords, deliveredAt);
|
|
1637
1787
|
if (next === current)
|
|
1638
1788
|
return false;
|
|
1639
|
-
|
|
1640
|
-
const live = context.findRun(runId) ?? run;
|
|
1641
|
-
live.pendingStageMessages = [...messages];
|
|
1642
|
-
context.bumpAndNotify();
|
|
1789
|
+
await batch.apply(backend, runId, next);
|
|
1643
1790
|
return true;
|
|
1644
1791
|
});
|
|
1645
1792
|
},
|
|
1646
1793
|
async settleStickyPendingStageMessageDelivered(runId, messageId, settledAt, backend) {
|
|
1647
|
-
return await
|
|
1794
|
+
return await batches.run(runId, backend, async (batch) => {
|
|
1648
1795
|
const run = context.findRun(runId);
|
|
1649
1796
|
if (run === undefined)
|
|
1650
1797
|
return false;
|
|
1651
1798
|
await backend.readPendingStageMessageDeliveryCount?.(runId, messageId, runId);
|
|
1652
|
-
const current =
|
|
1799
|
+
const current = ledgers.for(run).reconcile(backend, runId, batch.messages(run));
|
|
1653
1800
|
const next = settleStickyPendingStageMessageDelivered(current, runId, messageId, settledAt);
|
|
1654
1801
|
if (next === current)
|
|
1655
1802
|
return false;
|
|
1656
|
-
|
|
1657
|
-
const live = context.findRun(runId) ?? run;
|
|
1658
|
-
live.pendingStageMessages = [...messages];
|
|
1659
|
-
context.bumpAndNotify();
|
|
1803
|
+
await batch.apply(backend, runId, next);
|
|
1660
1804
|
return true;
|
|
1661
1805
|
});
|
|
1662
1806
|
}
|
|
@@ -1674,74 +1818,181 @@ function resolvePendingStageIdentity(run, stageKey) {
|
|
|
1674
1818
|
aliases: stage.id === stage.name ? [stage.id] : [stage.id, stage.name]
|
|
1675
1819
|
};
|
|
1676
1820
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1821
|
+
var PENDING_STAGE_TRANSITION_BATCH_LIMIT = 256;
|
|
1822
|
+
|
|
1823
|
+
class PendingStageTransitionBatches {
|
|
1824
|
+
queues = new Map;
|
|
1825
|
+
context;
|
|
1826
|
+
ledgers;
|
|
1827
|
+
constructor(context, ledgers) {
|
|
1828
|
+
this.context = context;
|
|
1829
|
+
this.ledgers = ledgers;
|
|
1830
|
+
}
|
|
1831
|
+
run(runId, backend, step) {
|
|
1832
|
+
return new Promise((resolve, reject) => {
|
|
1833
|
+
const transition = {
|
|
1834
|
+
backend,
|
|
1835
|
+
step,
|
|
1836
|
+
resolve: (value) => resolve(value),
|
|
1837
|
+
reject
|
|
1838
|
+
};
|
|
1839
|
+
const queue = this.queues.get(runId);
|
|
1840
|
+
if (queue !== undefined) {
|
|
1841
|
+
queue.push(transition);
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
const created = [transition];
|
|
1845
|
+
this.queues.set(runId, created);
|
|
1846
|
+
Promise.resolve().then(() => this.drain(runId, created));
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
async drain(runId, queue) {
|
|
1850
|
+
while (queue.length > 0)
|
|
1851
|
+
await this.runBatch(runId, takeBatch(queue));
|
|
1852
|
+
this.queues.delete(runId);
|
|
1853
|
+
}
|
|
1854
|
+
async runBatch(runId, transitions) {
|
|
1855
|
+
const batch = new PendingStageTransitionBatch(this.context, this.ledgers, runId);
|
|
1856
|
+
const outcomes = [];
|
|
1857
|
+
for (const transition of transitions) {
|
|
1858
|
+
const changedBefore = batch.changed;
|
|
1859
|
+
try {
|
|
1860
|
+
const value = await transition.step(batch);
|
|
1861
|
+
outcomes.push({ ok: true, value, uncommitted: changedBefore || batch.changed });
|
|
1862
|
+
} catch (error) {
|
|
1863
|
+
outcomes.push({ ok: false, error });
|
|
1710
1864
|
}
|
|
1711
|
-
retained = compacted;
|
|
1712
1865
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1866
|
+
let failure;
|
|
1867
|
+
try {
|
|
1868
|
+
await batch.persist();
|
|
1869
|
+
} catch (error) {
|
|
1870
|
+
failure = { error };
|
|
1871
|
+
}
|
|
1872
|
+
transitions.forEach((transition, index) => {
|
|
1873
|
+
const outcome = outcomes[index];
|
|
1874
|
+
if (!outcome.ok)
|
|
1875
|
+
transition.reject(outcome.error);
|
|
1876
|
+
else if (failure !== undefined && outcome.uncommitted)
|
|
1877
|
+
transition.reject(failure.error);
|
|
1878
|
+
else
|
|
1879
|
+
transition.resolve(outcome.value);
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
function takeBatch(queue) {
|
|
1884
|
+
let count = 1;
|
|
1885
|
+
let backend = queue[0].backend;
|
|
1886
|
+
while (count < queue.length && count < PENDING_STAGE_TRANSITION_BATCH_LIMIT) {
|
|
1887
|
+
const next = queue[count].backend;
|
|
1888
|
+
if (next !== undefined && backend !== undefined && next !== backend)
|
|
1889
|
+
break;
|
|
1890
|
+
backend ??= next;
|
|
1891
|
+
count++;
|
|
1892
|
+
}
|
|
1893
|
+
return queue.splice(0, count);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
class PendingStageTransitionBatch {
|
|
1897
|
+
changed = false;
|
|
1898
|
+
list;
|
|
1899
|
+
pending;
|
|
1900
|
+
context;
|
|
1901
|
+
ledgers;
|
|
1902
|
+
runId;
|
|
1903
|
+
constructor(context, ledgers, runId) {
|
|
1904
|
+
this.context = context;
|
|
1905
|
+
this.ledgers = ledgers;
|
|
1906
|
+
this.runId = runId;
|
|
1907
|
+
}
|
|
1908
|
+
messages(run) {
|
|
1909
|
+
return this.list ?? run.pendingStageMessages ?? [];
|
|
1910
|
+
}
|
|
1911
|
+
reconciled(run, messages) {
|
|
1912
|
+
if (this.changed) {
|
|
1913
|
+
this.list = messages;
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
if (messages !== run.pendingStageMessages) {
|
|
1917
|
+
run.pendingStageMessages = [...messages];
|
|
1918
|
+
this.context.bumpAndNotify();
|
|
1716
1919
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1920
|
+
}
|
|
1921
|
+
async apply(backend, runId, messages) {
|
|
1922
|
+
const run = this.context.findRun(runId);
|
|
1923
|
+
try {
|
|
1924
|
+
const compacted = await compactTransition(backend, runId, messages, this.context, this.ledgers, run);
|
|
1925
|
+
this.list = compacted.messages;
|
|
1926
|
+
this.changed = true;
|
|
1927
|
+
this.pending = { backend, messages: compacted.messages, commit: compacted.commit, run };
|
|
1928
|
+
return compacted.messages;
|
|
1929
|
+
} catch (error) {
|
|
1930
|
+
if (this.changed && this.list !== undefined) {
|
|
1931
|
+
const ledger = run === undefined ? new PendingStageTransitionLedger : this.ledgers.for(run);
|
|
1932
|
+
this.list = ledger.reconcile(backend, runId, this.list);
|
|
1933
|
+
} else
|
|
1934
|
+
reconcileLive(backend, runId, this.context, this.ledgers);
|
|
1935
|
+
throw error;
|
|
1719
1936
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1937
|
+
}
|
|
1938
|
+
async persist() {
|
|
1939
|
+
if (this.pending === undefined)
|
|
1940
|
+
return;
|
|
1941
|
+
const { backend, messages, commit, run } = this.pending;
|
|
1942
|
+
const published = this.list ?? messages;
|
|
1943
|
+
try {
|
|
1944
|
+
if (!await backend.persistPendingStageMessages(this.runId, messages)) {
|
|
1945
|
+
throw new Error(`atomic-workflows: durable workflow ${this.runId} is unavailable for pending-stage persistence`);
|
|
1729
1946
|
}
|
|
1947
|
+
commit?.();
|
|
1948
|
+
} finally {
|
|
1949
|
+
reconcileLive(backend, this.runId, this.context, this.ledgers);
|
|
1730
1950
|
}
|
|
1951
|
+
const live = this.context.findRun(this.runId) ?? run;
|
|
1952
|
+
if (live !== undefined)
|
|
1953
|
+
live.pendingStageMessages = [...published];
|
|
1954
|
+
this.context.bumpAndNotify();
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
async function compactTransition(backend, runId, messages, context, ledgers, run) {
|
|
1958
|
+
const ledger = run === undefined ? new PendingStageTransitionLedger : ledgers.for(run);
|
|
1959
|
+
const reconciled = ledger.reconcile(backend, runId, messages);
|
|
1960
|
+
const archiveSettled = backend.archivePendingStageMessage !== undefined && backend.readSettledPendingStageMessage !== undefined ? (entry) => backend.archivePendingStageMessage(runId, entry) : undefined;
|
|
1961
|
+
if (isCompactingBackend(backend)) {
|
|
1962
|
+
return await ledger.compact(backend, runId, reconciled, context.state.runs, archiveSettled);
|
|
1963
|
+
}
|
|
1964
|
+
let retained = compactStickyStageMessageDeliveries(reconciled, context.state.runs);
|
|
1965
|
+
if (archiveSettled !== undefined)
|
|
1966
|
+
retained = await retainPendingStageMessages(retained, archiveSettled);
|
|
1967
|
+
if (run !== undefined)
|
|
1968
|
+
ledgers.reset(run);
|
|
1969
|
+
return { messages: retained };
|
|
1970
|
+
}
|
|
1971
|
+
function reconcileLive(backend, runId, context, ledgers) {
|
|
1972
|
+
const current = context.findRun(runId);
|
|
1973
|
+
if (current === undefined)
|
|
1974
|
+
return;
|
|
1975
|
+
const messages = current.pendingStageMessages ?? [];
|
|
1976
|
+
const recovered = ledgers.for(current).reconcile(backend, runId, messages);
|
|
1977
|
+
if (recovered !== messages) {
|
|
1978
|
+
current.pendingStageMessages = [...recovered];
|
|
1979
|
+
context.bumpAndNotify();
|
|
1731
1980
|
}
|
|
1732
1981
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
return
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1982
|
+
|
|
1983
|
+
class PendingStageLedgers {
|
|
1984
|
+
ledgers = new WeakMap;
|
|
1985
|
+
for(run) {
|
|
1986
|
+
let ledger = this.ledgers.get(run);
|
|
1987
|
+
if (ledger === undefined) {
|
|
1988
|
+
ledger = new PendingStageTransitionLedger;
|
|
1989
|
+
this.ledgers.set(run, ledger);
|
|
1990
|
+
}
|
|
1991
|
+
return ledger;
|
|
1992
|
+
}
|
|
1993
|
+
reset(run) {
|
|
1994
|
+
this.ledgers.delete(run);
|
|
1995
|
+
}
|
|
1745
1996
|
}
|
|
1746
1997
|
|
|
1747
1998
|
// dist/builtin/workflows/src/shared/store-prompt-methods.ts
|