@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +12 -0
  3. package/dist/builtin/intercom/broker/broker.ts +13 -1
  4. package/dist/builtin/intercom/index.bundle.mjs +9 -1
  5. package/dist/builtin/intercom/package.json +1 -1
  6. package/dist/builtin/intercom/types.ts +7 -0
  7. package/dist/builtin/intercom/workflow-delivery-states.ts +48 -0
  8. package/dist/builtin/mcp/package.json +1 -1
  9. package/dist/builtin/subagents/package.json +1 -1
  10. package/dist/builtin/web-access/package.json +1 -1
  11. package/dist/builtin/workflows/CHANGELOG.md +26 -0
  12. package/dist/builtin/workflows/builtin/{chunk-zjecnjxx.js → chunk-6901nsd6.js} +1 -1
  13. package/dist/builtin/workflows/builtin/{chunk-k8d7ehjx.js → chunk-jvna28mq.js} +381 -130
  14. package/dist/builtin/workflows/builtin/{chunk-qxpymnaf.js → chunk-vwx023ge.js} +1 -1
  15. package/dist/builtin/workflows/builtin/goal.js +2 -2
  16. package/dist/builtin/workflows/builtin/index.js +3 -3
  17. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  18. package/dist/builtin/workflows/package.json +1 -1
  19. package/dist/builtin/workflows/src/durable/journal/internal.d.ts +15 -1
  20. package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +11 -0
  21. package/dist/builtin/workflows/src/extension/index.bundle.mjs +2457 -736
  22. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +2329 -730
  23. package/dist/builtin/workflows/src/index.js +2328 -730
  24. package/dist/builtin/workflows/src/local-worker-entry.mjs +2329 -730
  25. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  26. package/dist/core/agent-session-custom-message-commit.js +16 -0
  27. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  28. package/dist/core/storage/errors.d.ts +5 -1
  29. package/dist/core/storage/errors.d.ts.map +1 -1
  30. package/dist/core/storage/errors.js +8 -2
  31. package/dist/core/storage/errors.js.map +1 -1
  32. package/dist/core/storage/node-sqlite.d.ts +2 -1
  33. package/dist/core/storage/node-sqlite.d.ts.map +1 -1
  34. package/dist/core/storage/node-sqlite.js +213 -20
  35. package/dist/core/storage/node-sqlite.js.map +1 -1
  36. package/docs/intercom/reference.md +2 -0
  37. package/docs/workflows/operations.md +1 -1
  38. package/npm-shrinkwrap.json +32 -32
  39. 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((entry) => entry.status === "delivered" || entry.status === "undeliverable" && entry.undeliverableNotifiedAt !== undefined);
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 transitions = new Map;
1488
- const serialize = async (runId, transition) => {
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 serialize(input.runId, async () => {
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.pendingStageMessages ?? [] : [...run.pendingStageMessages ?? [], receipt], input, senderGroup, runGroup, stageIdentity);
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
- const messages = await persistTransition(backend, input.runId, result.messages, context);
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).pendingStageMessages ?? [] } : result;
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 serialize(input.runId, async () => {
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.pendingStageMessages ?? [] : [...run.pendingStageMessages ?? [], receipt], input, undefined, undefined, resolvePendingStageIdentity(run, input.stageKey));
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 serialize(runId, async () => {
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.pendingStageMessages ?? [];
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
- const messages = await persistTransition(backend, runId, next, context);
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 serialize(runId, async () => {
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.pendingStageMessages ?? [];
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
- const messages = await persistTransition(backend, runId, next, context);
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 serialize(runId, async () => {
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.pendingStageMessages ?? [];
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
- const messages = await persistTransition(backend, runId, next, context);
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 serialize(input.runId, async () => {
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.pendingStageMessages ?? [] : [...run.pendingStageMessages ?? [], receipt], input, senderGroup, runGroup);
1762
+ const result = queueStickyStageMessage(receipt === undefined ? batch.messages(run) : [...batch.messages(run), receipt], input, senderGroup, runGroup);
1607
1763
  if (result.ok && !result.deduplicated) {
1608
- const messages = await persistTransition(backend, input.runId, result.messages, context);
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).pendingStageMessages ?? [] } : result;
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 serialize(runId, async () => {
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 = reconcileDeliveryCounts(backend, runId, run.pendingStageMessages ?? []);
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 = reconcileDeliveryCounts(backend, runId, current);
1632
- if (current !== run.pendingStageMessages) {
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
- const messages = await persistTransition(backend, runId, next, context);
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 serialize(runId, async () => {
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 = reconcileDeliveryCounts(backend, runId, run.pendingStageMessages ?? []);
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
- const messages = await persistTransition(backend, runId, next, context);
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
- async function persistTransition(backend, runId, messages, context) {
1678
- let retained = reconcileDeliveryCounts(backend, runId, messages);
1679
- try {
1680
- if (backend.archivePendingStageDeliveryReceipt !== undefined && backend.hasPendingStageDeliveryReceipt !== undefined) {
1681
- const compacted = [];
1682
- for (const entry of retained) {
1683
- if (entry.sticky !== true || entry.deliveries === undefined) {
1684
- compacted.push(entry);
1685
- continue;
1686
- }
1687
- const latest = new Map;
1688
- entry.deliveries.forEach((delivery, index) => {
1689
- latest.set(JSON.stringify([delivery.runId, delivery.stageId, delivery.admission]), index);
1690
- });
1691
- const deliveries = [];
1692
- let archivedThrough = -1;
1693
- for (let index = 0;index < entry.deliveries.length; index++) {
1694
- const delivery = entry.deliveries[index];
1695
- const stage = context.state.runs.find((run) => run.id === delivery.runId)?.stages.find((stage) => stage.id === delivery.stageId);
1696
- if (entry.status !== "queued" || stage?.status === "completed" || stage?.status === "skipped" || stage?.status === "failed" || latest.get(JSON.stringify([delivery.runId, delivery.stageId, delivery.admission])) !== index && delivery.sessionId !== undefined) {
1697
- while (archivedThrough < index) {
1698
- const prefixIndex = archivedThrough + 1;
1699
- await backend.archivePendingStageDeliveryReceipt(runId, entry.id, entry.deliveries[prefixIndex], {
1700
- messageRunId: entry.runId,
1701
- deliveryCount: (entry.deliveryCount ?? entry.deliveries.length) - entry.deliveries.length + prefixIndex + 1
1702
- });
1703
- archivedThrough = prefixIndex;
1704
- }
1705
- } else {
1706
- deliveries.push(delivery);
1707
- }
1708
- }
1709
- compacted.push(deliveries.length === entry.deliveries.length ? entry : { ...entry, deliveries });
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
- retained = compactStickyStageMessageDeliveries(retained, context.state.runs);
1714
- if (backend.archivePendingStageMessage !== undefined && backend.readSettledPendingStageMessage !== undefined) {
1715
- retained = await retainPendingStageMessages(retained, (entry) => backend.archivePendingStageMessage(runId, entry));
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
- if (!await backend.persistPendingStageMessages(runId, retained)) {
1718
- throw new Error(`atomic-workflows: durable workflow ${runId} is unavailable for pending-stage persistence`);
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
- return retained;
1721
- } finally {
1722
- const run = context.findRun(runId);
1723
- if (run !== undefined) {
1724
- const current = run.pendingStageMessages ?? [];
1725
- const recovered = reconcileDeliveryCounts(backend, runId, current);
1726
- if (recovered !== current) {
1727
- run.pendingStageMessages = [...recovered];
1728
- context.bumpAndNotify();
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
- function reconcileDeliveryCounts(backend, runId, messages) {
1734
- let changed = false;
1735
- const recovered = messages.map((entry) => {
1736
- if (entry.sticky !== true)
1737
- return entry;
1738
- const count = backend.getPendingStageMessageDeliveryCount?.(runId, entry.id, entry.runId) ?? 0;
1739
- if (count <= (entry.deliveryCount ?? entry.deliveries?.length ?? 0))
1740
- return entry;
1741
- changed = true;
1742
- return { ...entry, deliveryCount: count };
1743
- });
1744
- return changed ? recovered : messages;
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
@@ -37,7 +37,7 @@ import {
37
37
  parseFailureDiagnostics,
38
38
  summarizeReviewConvergence,
39
39
  reverify_consolidated_batch
40
- } from "./chunk-k8d7ehjx.js";
40
+ } from "./chunk-jvna28mq.js";
41
41
  import {
42
42
  VERIFICATION_SCALE
43
43
  } from "./chunk-n1910xc4.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  goal_default
3
- } from "./chunk-qxpymnaf.js";
4
- import"./chunk-k8d7ehjx.js";
3
+ } from "./chunk-vwx023ge.js";
4
+ import"./chunk-jvna28mq.js";
5
5
  import"./chunk-2377r57g.js";
6
6
  import"./chunk-z0d03ktm.js";
7
7
  import"./chunk-n1910xc4.js";