@bastani/atomic 0.9.44-alpha.1 → 0.9.44-alpha.3

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 (43) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +17 -0
  3. package/dist/builtin/intercom/broker/client-message-validation.ts +6 -0
  4. package/dist/builtin/intercom/broker/client.ts +4 -0
  5. package/dist/builtin/intercom/broker/framing.ts +18 -1
  6. package/dist/builtin/intercom/broker/send-signature.ts +4 -1
  7. package/dist/builtin/intercom/index.bundle.mjs +101 -18
  8. package/dist/builtin/intercom/package.json +1 -1
  9. package/dist/builtin/intercom/skills/intercom/SKILL.md +1 -0
  10. package/dist/builtin/intercom/types.ts +8 -0
  11. package/dist/builtin/mcp/package.json +1 -1
  12. package/dist/builtin/subagents/package.json +1 -1
  13. package/dist/builtin/web-access/index.bundle.mjs +2510 -1308
  14. package/dist/builtin/web-access/package.json +1 -1
  15. package/dist/builtin/workflows/CHANGELOG.md +13 -0
  16. package/dist/builtin/workflows/package.json +1 -1
  17. package/dist/builtin/workflows/src/extension/index.bundle.mjs +363 -37
  18. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +363 -37
  19. package/dist/builtin/workflows/src/index.js +363 -37
  20. package/dist/builtin/workflows/src/local-worker-entry.mjs +363 -37
  21. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  22. package/dist/core/agent-session-custom-message-commit.js +59 -0
  23. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  24. package/dist/core/agent-session-methods.d.ts +1 -0
  25. package/dist/core/agent-session-methods.d.ts.map +1 -1
  26. package/dist/core/agent-session-methods.js.map +1 -1
  27. package/dist/core/agent-session-prompt.d.ts +4 -0
  28. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  29. package/dist/core/agent-session-prompt.js +10 -2
  30. package/dist/core/agent-session-prompt.js.map +1 -1
  31. package/dist/core/agent-session.d.ts +2 -0
  32. package/dist/core/agent-session.d.ts.map +1 -1
  33. package/dist/core/agent-session.js +9 -0
  34. package/dist/core/agent-session.js.map +1 -1
  35. package/dist/core/builtin-packages.d.ts.map +1 -1
  36. package/dist/core/builtin-packages.js +13 -13
  37. package/dist/core/builtin-packages.js.map +1 -1
  38. package/docs/intercom/operations.md +1 -1
  39. package/docs/intercom.md +11 -1
  40. package/docs/subagents.md +9 -1
  41. package/examples/extensions/gondolin/package-lock.json +3 -3
  42. package/npm-shrinkwrap.json +67 -51
  43. package/package.json +4 -4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.44-alpha.1",
3
+ "version": "0.9.44-alpha.3",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.44-alpha.3] - 2026-10-25
10
+
11
+ ### Fixed
12
+
13
+ - A workflow whose stages send many Intercom messages to stage patterns (for example `workflow:<rootRunId>/child-*/**`) no longer saturates the workflow host. The host matched every queued pattern message against every stage on each change; it now indexes the patterns and each stage's paths once. In a 150-child fan-out test, delivered sends went from 566 of 900 to 900 of 900 and host event-loop lag p99 from about 1 s to under 200 ms ([#3931](https://github.com/bastani-inc/atomic/issues/3931))
14
+ - Workflow journal events no longer store a run's whole list of pending stage messages each time. Pattern sends stay queued until the root run ends, so every queued message rewrote the growing list and the journal grew quadratically with the number of sends. Each event now records only what changed; older journals still read as before ([#3929](https://github.com/bastani-inc/atomic/issues/3929))
15
+
16
+ ## [0.9.44-alpha.2] - 2026-10-24
17
+
18
+ ### Fixed
19
+
20
+ - A sticky Intercom broadcast, such as `workflow:<rootRunId>/**`, that reaches many workflow stages no longer makes the workflow journal grow quadratically. Recording one more delivery used to rewrite every stage's deliveries in the run's queued-message event; each event now stores only the deliveries that changed since the run's previous event. In a 160-worker run this shrinks those events from about 160 MB to about 9 MB. Journals written by earlier versions read and resume unchanged ([#3920](https://github.com/bastani-inc/atomic/issues/3920))
21
+
9
22
  ## [0.9.44-alpha.1] - 2026-10-24
10
23
 
11
24
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.44-alpha.1",
3
+ "version": "0.9.44-alpha.3",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [
@@ -2120,7 +2120,20 @@ function createWorkflowBoundarySegmentsResolver(runs) {
2120
2120
  };
2121
2121
  }
2122
2122
  function workflowBoundaryHops(runs, runId) {
2123
+ return boundaryHopsFrom(new Map(runs.map((run) => [run.id, run])), runId);
2124
+ }
2125
+ function createWorkflowBoundaryHopsResolver(runs) {
2123
2126
  const runById = new Map(runs.map((run) => [run.id, run]));
2127
+ const cache = new Map;
2128
+ return (runId) => {
2129
+ if (cache.has(runId))
2130
+ return cache.get(runId);
2131
+ const hops = boundaryHopsFrom(runById, runId);
2132
+ cache.set(runId, hops);
2133
+ return hops;
2134
+ };
2135
+ }
2136
+ function boundaryHopsFrom(runById, runId) {
2124
2137
  const hops = [];
2125
2138
  let current = runById.get(runId);
2126
2139
  while (current !== undefined && current.parentRunId !== undefined) {
@@ -2146,6 +2159,35 @@ function workflowStagePathVariants(hops, stageSegments) {
2146
2159
  function stageMatchesPathPattern(patternSegments, hops, stageSegments) {
2147
2160
  return workflowStagePathVariants(hops, stageSegments).some((candidate) => matchStagePathSegments(patternSegments, candidate));
2148
2161
  }
2162
+ function createStagePathPatternIndex(patterns) {
2163
+ const byFirstSegment = new Map;
2164
+ const globFirst = [];
2165
+ patterns.forEach((pattern, index) => {
2166
+ const first = pattern[0];
2167
+ if (first === undefined || first.includes("*")) {
2168
+ globFirst.push(index);
2169
+ return;
2170
+ }
2171
+ const bucket = byFirstSegment.get(first);
2172
+ if (bucket === undefined)
2173
+ byFirstSegment.set(first, [index]);
2174
+ else
2175
+ bucket.push(index);
2176
+ });
2177
+ return {
2178
+ matching(hops, stageSegments) {
2179
+ if (patterns.length === 0)
2180
+ return [];
2181
+ const variants = workflowStagePathVariants(hops, stageSegments);
2182
+ const candidates = new Set(globFirst);
2183
+ for (const variant of variants) {
2184
+ for (const index of byFirstSegment.get(variant[0]) ?? [])
2185
+ candidates.add(index);
2186
+ }
2187
+ return [...candidates].sort((left, right) => left - right).filter((index) => variants.some((variant) => matchStagePathSegments(patterns[index], variant)));
2188
+ }
2189
+ };
2190
+ }
2149
2191
  function pendingStageTarget(runId, stage) {
2150
2192
  return "workflowGraphTarget" in stage ? stage.workflowGraphTarget : { runId, stageId: stage.id };
2151
2193
  }
@@ -6808,6 +6850,17 @@ function removeProcessExitCleanup(cleanup) {
6808
6850
  if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
6809
6851
  process.off("exit", runProcessExitCleanups);
6810
6852
  }
6853
+ function writtenHandle(event) {
6854
+ if (event.type === RUN_EVENT_TYPES.promptAsked || event.type === RUN_EVENT_TYPES.promptReleased)
6855
+ return event.payload.handle;
6856
+ if (event.type === RUN_EVENT_TYPES.checkpointRecorded || event.type === RUN_EVENT_TYPES.messageSettled || event.type === RUN_EVENT_TYPES.messageDelivered)
6857
+ return;
6858
+ return event.payload;
6859
+ }
6860
+ function snapshotNeedsHistory(tail) {
6861
+ const handle = writtenHandle(tail.snapshot.event);
6862
+ return handle !== undefined && (handle.pendingStageMessageChange !== undefined || hasPendingStageDeliveryDelta(handle.pendingStageMessages));
6863
+ }
6811
6864
  function* invoke(action) {
6812
6865
  try {
6813
6866
  const result = action();
@@ -6920,11 +6973,11 @@ class JournalDurableBackend {
6920
6973
  const payload = event.payload;
6921
6974
  this.projection.archivePendingStageDeliveryReceipt(entry.runId, payload.messageId, payload.delivery, payload.accounting);
6922
6975
  } else if (event.type === RUN_EVENT_TYPES.promptAsked || event.type === RUN_EVENT_TYPES.promptReleased) {
6923
- this.projection.applyWorkflowSnapshot(event.payload.handle);
6976
+ this.projection.applyWorkflowSnapshot(decodeHandle(event.payload.handle, this.projection.getWorkflow(entry.runId)?.pendingStageMessages));
6924
6977
  this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
6925
6978
  } else {
6926
- const handle = event.payload;
6927
6979
  const previous = this.projection.getWorkflow(entry.runId);
6980
+ const handle = decodeHandle(event.payload, previous?.pendingStageMessages);
6928
6981
  this.projection.applyWorkflowSnapshot(handle);
6929
6982
  if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
6930
6983
  this.prompts.delete(entry.runId);
@@ -7044,6 +7097,20 @@ class JournalDurableBackend {
7044
7097
  }
7045
7098
  *append(runId, event, options) {
7046
7099
  const lease = yield* this.ownership(runId);
7100
+ const base = this.sequences.get(runId);
7101
+ const pinned = options?.expectSeq;
7102
+ const encoded = base !== undefined && (pinned === undefined || pinned === base) && event.type !== RUN_EVENT_TYPES.runStarted ? encodeHandleEvent(event, this.projection.getWorkflow(runId)?.pendingStageMessages) : undefined;
7103
+ if (encoded !== undefined) {
7104
+ try {
7105
+ return yield* this.write(runId, lease, encoded, { ...options, expectSeq: base });
7106
+ } catch (error) {
7107
+ if (!(error instanceof JournalCasConflictError) || pinned !== undefined)
7108
+ throw error;
7109
+ }
7110
+ }
7111
+ yield* this.write(runId, lease, event, options);
7112
+ }
7113
+ *write(runId, lease, event, options) {
7047
7114
  try {
7048
7115
  const outcome = yield* invoke(() => this.journal.append(lease, event, options));
7049
7116
  const nextSeq = (this.sequences.get(runId) ?? 0) + 1;
@@ -7220,11 +7287,12 @@ class JournalDurableBackend {
7220
7287
  for (const message of messages)
7221
7288
  if (message.runId !== logicalRunId)
7222
7289
  throw new JournalMessageScopeError(logicalRunId, message.runId);
7223
- yield* this.snapshot({
7290
+ const next = {
7224
7291
  ...handle,
7225
7292
  pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
7226
7293
  updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
7227
- }, RUN_EVENT_TYPES.messageQueued);
7294
+ };
7295
+ yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
7228
7296
  return true;
7229
7297
  }
7230
7298
  async archivePendingStageMessage(runId, message) {
@@ -7318,7 +7386,7 @@ class JournalDurableBackend {
7318
7386
  if (summarized !== undefined && summarized.seq === version.seq && summarized.at === version.at)
7319
7387
  return;
7320
7388
  const tail = yield* invoke(() => journal.snapshotTail(runId));
7321
- if (tail !== undefined)
7389
+ if (tail !== undefined && !snapshotNeedsHistory(tail))
7322
7390
  return this.summarize(runId, tail);
7323
7391
  if (summarized === undefined && version.seq === 0)
7324
7392
  return;
@@ -7544,7 +7612,182 @@ class JournalDurableBackend {
7544
7612
  }
7545
7613
  }
7546
7614
  }
7547
- var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
7615
+ function identity(message) {
7616
+ return JSON.stringify([message.runId, message.id]);
7617
+ }
7618
+ function uniqueByIdentity(messages) {
7619
+ const result = new Map;
7620
+ const repeated = new Set;
7621
+ for (const message of messages) {
7622
+ const key = identity(message);
7623
+ if (result.has(key))
7624
+ repeated.add(key);
7625
+ result.set(key, message);
7626
+ }
7627
+ for (const key of repeated)
7628
+ result.delete(key);
7629
+ return result;
7630
+ }
7631
+ function isDeltaEncoded(message) {
7632
+ return message.deliveryDelta !== undefined;
7633
+ }
7634
+ function deliveryDelta(base, deliveries) {
7635
+ const drop = [];
7636
+ let kept = 0;
7637
+ base.forEach((delivery, index) => {
7638
+ if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
7639
+ kept++;
7640
+ else
7641
+ drop.push(index);
7642
+ });
7643
+ const add = deliveries.slice(kept);
7644
+ return drop.length === 0 ? { add } : { drop, add };
7645
+ }
7646
+ function pendingStageFields(message) {
7647
+ return Object.keys(message);
7648
+ }
7649
+ function copyField(target, source, key) {
7650
+ target[key] = source[key];
7651
+ }
7652
+ function isRemovedField(message, key) {
7653
+ return message[key] === undefined;
7654
+ }
7655
+ function json(message) {
7656
+ let text = entryJson.get(message);
7657
+ if (text === undefined) {
7658
+ text = JSON.stringify(message);
7659
+ entryJson.set(message, text);
7660
+ }
7661
+ return text;
7662
+ }
7663
+ function patchEntry(index, base, message) {
7664
+ const set = {};
7665
+ const unset = pendingStageFields(base).filter((key) => base[key] !== undefined && isRemovedField(message, key));
7666
+ for (const key of pendingStageFields(message)) {
7667
+ if (message[key] === undefined || key === "deliveries")
7668
+ continue;
7669
+ if (JSON.stringify(message[key]) !== JSON.stringify(base[key]))
7670
+ copyField(set, message, key);
7671
+ }
7672
+ let delta;
7673
+ if (message.deliveries !== undefined) {
7674
+ if (base.deliveries === undefined)
7675
+ set.deliveries = message.deliveries;
7676
+ else if (JSON.stringify(base.deliveries) !== JSON.stringify(message.deliveries))
7677
+ delta = deliveryDelta(base.deliveries, message.deliveries);
7678
+ }
7679
+ return {
7680
+ patch: index,
7681
+ ...Object.keys(set).length === 0 ? {} : { set },
7682
+ ...unset.length === 0 ? {} : { unset },
7683
+ ...delta === undefined ? {} : { deliveryDelta: delta }
7684
+ };
7685
+ }
7686
+ function encodePendingStageList(previous, next) {
7687
+ const positions = new Map;
7688
+ const repeated = new Set;
7689
+ previous.forEach((message, index) => {
7690
+ const key = identity(message);
7691
+ if (positions.has(key))
7692
+ repeated.add(key);
7693
+ positions.set(key, index);
7694
+ });
7695
+ for (const key of repeated)
7696
+ positions.delete(key);
7697
+ const items = [];
7698
+ let reused = 0;
7699
+ for (const message of next) {
7700
+ const index = positions.get(identity(message));
7701
+ const base = index === undefined ? undefined : previous[index];
7702
+ if (base === undefined) {
7703
+ items.push(message);
7704
+ continue;
7705
+ }
7706
+ reused++;
7707
+ const patched = base === message || json(base) === json(message) ? undefined : patchEntry(index, base, message);
7708
+ if (patched === undefined || Object.keys(patched).length === 1) {
7709
+ const last = items.at(-1);
7710
+ if (Array.isArray(last) && last[0] + last[1] === index)
7711
+ last[1]++;
7712
+ else
7713
+ items.push([index, 1]);
7714
+ continue;
7715
+ }
7716
+ items.push(JSON.stringify(patched).length < json(message).length ? patched : message);
7717
+ }
7718
+ return reused > 0 ? items : undefined;
7719
+ }
7720
+ function decodePendingStageList(previous, items) {
7721
+ const base = (index) => {
7722
+ if (!Number.isInteger(index) || index < 0 || index >= previous.length)
7723
+ throw new Error(`atomic-workflows: journal pending-stage change names missing entry ${index}`);
7724
+ return previous[index];
7725
+ };
7726
+ const result = [];
7727
+ for (const item of items) {
7728
+ if (Array.isArray(item)) {
7729
+ const [start, count] = item;
7730
+ for (let index = start;index < start + count; index++)
7731
+ result.push(base(index));
7732
+ } else if (typeof item.patch === "number") {
7733
+ const { patch, set, unset, deliveryDelta: delta } = item;
7734
+ const entry = { ...base(patch), ...set };
7735
+ for (const key of unset ?? [])
7736
+ delete entry[key];
7737
+ if (delta !== undefined) {
7738
+ const dropped = new Set(delta.drop ?? []);
7739
+ const deliveries = base(patch).deliveries ?? [];
7740
+ entry.deliveries = [...deliveries.filter((_, index) => !dropped.has(index)), ...delta.add];
7741
+ }
7742
+ result.push(entry);
7743
+ } else
7744
+ result.push(item);
7745
+ }
7746
+ return result;
7747
+ }
7748
+ function encodeHandleEvent(event, previous) {
7749
+ const handle = writtenHandle(event);
7750
+ if (handle?.pendingStageMessages === undefined || previous === undefined)
7751
+ return;
7752
+ const change = encodePendingStageList(previous, handle.pendingStageMessages);
7753
+ if (change === undefined)
7754
+ return;
7755
+ const { pendingStageMessages: _full, ...rest } = handle;
7756
+ const encoded = { ...rest, pendingStageMessageChange: change };
7757
+ if (event.type === RUN_EVENT_TYPES.promptAsked || event.type === RUN_EVENT_TYPES.promptReleased)
7758
+ return { ...event, payload: { ...event.payload, handle: encoded } };
7759
+ return { ...event, payload: encoded };
7760
+ }
7761
+ function decodeHandle(handle, previous) {
7762
+ const { pendingStageMessageChange: change, ...rest } = handle;
7763
+ if (change !== undefined)
7764
+ return { ...rest, pendingStageMessages: decodePendingStageList(previous ?? [], change) };
7765
+ if (!hasPendingStageDeliveryDelta(handle.pendingStageMessages))
7766
+ return handle;
7767
+ return {
7768
+ ...handle,
7769
+ pendingStageMessages: decodePendingStageDeliveries(previous ?? [], handle.pendingStageMessages ?? [])
7770
+ };
7771
+ }
7772
+ function decodePendingStageDeliveries(previous, messages) {
7773
+ if (!messages.some(isDeltaEncoded))
7774
+ return messages;
7775
+ const bases = uniqueByIdentity(previous);
7776
+ return messages.map((message) => {
7777
+ if (!isDeltaEncoded(message))
7778
+ return message;
7779
+ const base = bases.get(identity(message))?.deliveries;
7780
+ if (base === undefined)
7781
+ throw new Error(`atomic-workflows: journal delivery change for ${identity(message)} has no base entry`);
7782
+ const { deliveryDelta, ...rest } = message;
7783
+ const dropped = new Set(deliveryDelta.drop ?? []);
7784
+ return { ...rest, deliveries: [...base.filter((_, index) => !dropped.has(index)), ...deliveryDelta.add] };
7785
+ });
7786
+ }
7787
+ function hasPendingStageDeliveryDelta(messages) {
7788
+ return messages?.some(isDeltaEncoded) === true;
7789
+ }
7790
+ var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError, entryJson;
7548
7791
  var init_journal_backend = __esm(() => {
7549
7792
  init_workflow_instance_ownership_error();
7550
7793
  init_backend();
@@ -7602,6 +7845,7 @@ var init_journal_backend = __esm(() => {
7602
7845
  this.name = "JournalMessageScopeError";
7603
7846
  }
7604
7847
  };
7848
+ entryJson = new WeakMap;
7605
7849
  });
7606
7850
 
7607
7851
  // dist/builtin/workflows/src/durable/journal/legacy-journal-tables.ts
@@ -60727,24 +60971,63 @@ function stickyStageDeliveryStatuses(runs, rootRunId, availableBackend) {
60727
60971
  const root = runs.find((run) => run.id === rootRunId);
60728
60972
  const entries = (root?.pendingStageMessages ?? []).filter((entry) => entry.sticky === true);
60729
60973
  const result = [];
60974
+ const patternByTarget = new Map;
60975
+ const patterns = [];
60976
+ const entriesByPattern = [];
60977
+ const ownEntries = [];
60978
+ entries.forEach((entry, index) => {
60979
+ const target = entry.targetPath ?? entry.stageKey;
60980
+ let pattern = patternByTarget.get(target);
60981
+ if (pattern === undefined) {
60982
+ const parsed = parseWorkflowStageTarget(target);
60983
+ pattern = parsed?.rootRunId === rootRunId ? patterns.push(parsed.segments) - 1 : -1;
60984
+ if (pattern >= 0)
60985
+ entriesByPattern.push([]);
60986
+ patternByTarget.set(target, pattern);
60987
+ }
60988
+ if (pattern < 0)
60989
+ return;
60990
+ entriesByPattern[pattern].push(index);
60991
+ ownEntries.push(index);
60992
+ });
60993
+ if (ownEntries.length === 0)
60994
+ return result;
60995
+ const patternIndex = createStagePathPatternIndex(patterns);
60996
+ const deliveriesByEntry = new Map;
60997
+ const entryReceipts = (index, runId, stageId) => {
60998
+ let byStage = deliveriesByEntry.get(index);
60999
+ if (byStage === undefined) {
61000
+ byStage = new Map;
61001
+ for (const delivery of entries[index].deliveries ?? []) {
61002
+ const key = `${delivery.runId}\x00${delivery.stageId}`;
61003
+ const list = byStage.get(key);
61004
+ if (list === undefined)
61005
+ byStage.set(key, [delivery]);
61006
+ else
61007
+ list.push(delivery);
61008
+ }
61009
+ deliveriesByEntry.set(index, byStage);
61010
+ }
61011
+ return byStage.get(`${runId}\x00${stageId}`) ?? [];
61012
+ };
60730
61013
  availableBackend = availableRunDurableBackend(runs, rootRunId) ?? availableBackend;
60731
61014
  const backend = availableBackend === undefined ? undefined : durableBackendForRun(availableBackend, runs, rootRunId);
61015
+ const runById = new Map(runs.map((run) => [run.id, run]));
61016
+ const hopsFor = createWorkflowBoundaryHopsResolver(runs);
60732
61017
  for (const run of runs) {
60733
- if (run.id !== rootRunId && run.rootRunId !== rootRunId && durableRootRunIdForRun(runs, run.id) !== rootRunId)
61018
+ const durableRootRunId = reciprocalWorkflowRootRunId(runById, run.id);
61019
+ if (run.id !== rootRunId && run.rootRunId !== rootRunId && durableRootRunId !== rootRunId)
60734
61020
  continue;
60735
- const resolved = durableRootRunIdForRun(runs, run.id) === rootRunId;
60736
- const hops = workflowBoundaryHops(runs, run.id);
61021
+ const resolved = durableRootRunId === rootRunId;
61022
+ const hops = hopsFor(run.id);
60737
61023
  for (const stage of run.stages) {
60738
61024
  if (stage.nodeKind === "tool" || stage.replayKey?.startsWith("prompt:") === true || stage.replayKey?.startsWith("workflow:") === true || stage.workflowChildRun !== undefined || stage.workflowChild !== undefined)
60739
61025
  continue;
60740
- for (const entry of entries) {
61026
+ const matchedEntries = resolved && hops ? patternIndex.matching(hops, [stage.id, stage.name]).flatMap((pattern) => entriesByPattern[pattern]).sort((left, right) => left - right) : ownEntries;
61027
+ for (const index of matchedEntries) {
61028
+ const entry = entries[index];
60741
61029
  const target = entry.targetPath ?? entry.stageKey;
60742
- const parsed = parseWorkflowStageTarget(target);
60743
- if (!parsed || parsed.rootRunId !== rootRunId)
60744
- continue;
60745
- if (resolved && hops && !stageMatchesPathPattern(parsed.segments, hops, [stage.id, stage.name]))
60746
- continue;
60747
- const receipts = (entry.deliveries ?? []).filter((delivery) => delivery.runId === run.id && delivery.stageId === stage.id);
61030
+ const receipts = entryReceipts(index, run.id, stage.id);
60748
61031
  const delivered = backend?.hasCachedPendingStageDeliveryReceipt?.(rootRunId, entry.id, {
60749
61032
  runId: run.id,
60750
61033
  stageId: stage.id,
@@ -61062,20 +61345,27 @@ function possibleStageRows(runs, rootRunId, rootRun, backend) {
61062
61345
  if (isTerminalRunStatus(rootRun.status))
61063
61346
  return [];
61064
61347
  const stickyQueued = (rootRun.pendingStageMessages ?? []).filter((entry) => entry.sticky === true && entry.status === "queued");
61348
+ const segmentsByTarget = new Map;
61065
61349
  const stickySegments = (entry) => {
61066
- const parsed = parseWorkflowStageTarget(entry.targetPath ?? entry.stageKey);
61067
- return parsed?.rootRunId === rootRunId ? parsed.segments : undefined;
61350
+ const target = entry.targetPath ?? entry.stageKey;
61351
+ if (segmentsByTarget.has(target))
61352
+ return segmentsByTarget.get(target);
61353
+ const parsed = parseWorkflowStageTarget(target);
61354
+ const segments = parsed?.rootRunId === rootRunId ? parsed.segments : undefined;
61355
+ segmentsByTarget.set(target, segments);
61356
+ return segments;
61068
61357
  };
61069
61358
  const scanEntries = stickyQueued.filter((entry) => {
61070
61359
  const segments = stickySegments(entry);
61071
61360
  return !(segments !== undefined && segments.length === 1 && segments[0] === "**");
61072
61361
  });
61362
+ const liveStageMatches = materializedStageMatcher(runs, rootRunId);
61073
61363
  const rows = [];
61074
61364
  for (const scanEntry of rootRun.possibleStages ?? []) {
61075
61365
  const rowSegments = splitStagePathSegments(scanEntry);
61076
61366
  if (rowSegments.length === 0 || rowSegments.some((segment) => segment.length === 0))
61077
61367
  continue;
61078
- if (!rowSegments.some((segment) => segment.includes("*")) && materializedStageMatches(runs, rootRunId, rowSegments)) {
61368
+ if (!rowSegments.some((segment) => segment.includes("*")) && liveStageMatches(rowSegments)) {
61079
61369
  continue;
61080
61370
  }
61081
61371
  rows.push({
@@ -61096,28 +61386,43 @@ function possibleStageRows(runs, rootRunId, rootRun, backend) {
61096
61386
  }).length
61097
61387
  });
61098
61388
  const statuses = stickyStageDeliveryStatuses(runs, rootRunId, backend);
61099
- for (const target of new Set(statuses.map((status) => status.target))) {
61100
- if (rows.some((row) => row.target === target))
61389
+ const statusesByTarget = new Map;
61390
+ for (const status of statuses) {
61391
+ const forTarget = statusesByTarget.get(status.target);
61392
+ if (forTarget === undefined)
61393
+ statusesByTarget.set(status.target, [status]);
61394
+ else
61395
+ forTarget.push(status);
61396
+ }
61397
+ const queuedByTarget = new Map;
61398
+ for (const entry of stickyQueued) {
61399
+ const target = entry.targetPath ?? entry.stageKey;
61400
+ queuedByTarget.set(target, (queuedByTarget.get(target) ?? 0) + 1);
61401
+ }
61402
+ const rowTargets = new Set(rows.map((row) => row.target));
61403
+ for (const target of statusesByTarget.keys()) {
61404
+ if (rowTargets.has(target))
61101
61405
  continue;
61102
- rows.push({
61103
- target,
61104
- queuedCount: stickyQueued.filter((entry) => (entry.targetPath ?? entry.stageKey) === target).length,
61105
- deliveryOnly: true
61106
- });
61406
+ rowTargets.add(target);
61407
+ rows.push({ target, queuedCount: queuedByTarget.get(target) ?? 0, deliveryOnly: true });
61107
61408
  }
61108
61409
  for (let index = 0;index < rows.length; index++) {
61109
61410
  const row = rows[index];
61110
- const deliveryStates = statuses.filter((status) => status.target === row.target);
61111
- if (deliveryStates.length > 0)
61411
+ const deliveryStates = statusesByTarget.get(row.target);
61412
+ if (deliveryStates !== undefined)
61112
61413
  rows[index] = { ...row, deliveryStates };
61113
61414
  }
61114
61415
  return rows;
61115
61416
  }
61116
- function materializedStageMatches(runs, rootRunId, entrySegments) {
61117
- for (const run of runs) {
61118
- if (durableRootRunIdForRun(runs, run.id) !== rootRunId)
61119
- continue;
61120
- const hops = workflowBoundaryHops(runs, run.id);
61417
+ function materializedStageMatcher(runs, rootRunId) {
61418
+ const runById = new Map(runs.map((run) => [run.id, run]));
61419
+ const hopsFor = createWorkflowBoundaryHopsResolver(runs);
61420
+ const invocationRuns = runs.filter((run) => reciprocalWorkflowRootRunId(runById, run.id) === rootRunId);
61421
+ return (entrySegments) => materializedStageMatches(invocationRuns, hopsFor, entrySegments);
61422
+ }
61423
+ function materializedStageMatches(invocationRuns, hopsFor, entrySegments) {
61424
+ for (const run of invocationRuns) {
61425
+ const hops = hopsFor(run.id);
61121
61426
  if (hops === undefined)
61122
61427
  continue;
61123
61428
  for (const stage of run.stages) {
@@ -61392,10 +61697,12 @@ async function recordConfirmedStickyDeliveries(activeStore, event) {
61392
61697
  }
61393
61698
  function matchingLiveStages(runs, rootRunId, patternSegments) {
61394
61699
  const matches = [];
61700
+ const runById = new Map(runs.map((run) => [run.id, run]));
61701
+ const hopsFor = createWorkflowBoundaryHopsResolver(runs);
61395
61702
  for (const run of runs) {
61396
- if (durableRootRunIdForRun(runs, run.id) !== rootRunId)
61703
+ if (reciprocalWorkflowRootRunId(runById, run.id) !== rootRunId)
61397
61704
  continue;
61398
- const hops = workflowBoundaryHops(runs, run.id);
61705
+ const hops = hopsFor(run.id);
61399
61706
  if (hops === undefined)
61400
61707
  continue;
61401
61708
  for (const stage of run.stages) {
@@ -61531,11 +61838,26 @@ function writeMessage(socket, msg, callback) {
61531
61838
  }
61532
61839
  function createMessageReader(onMessage, onError) {
61533
61840
  let buffer = Buffer.alloc(0);
61841
+ let pending = [];
61842
+ let pendingLength = 0;
61843
+ let awaitedLength = 0;
61534
61844
  return (data) => {
61535
- buffer = Buffer.concat([buffer, data]);
61845
+ if (pendingLength + data.length < awaitedLength) {
61846
+ pending.push(data);
61847
+ pendingLength += data.length;
61848
+ return;
61849
+ }
61850
+ buffer = pending.length === 0 ? Buffer.concat([buffer, data]) : Buffer.concat([...pending, data]);
61851
+ pending = [];
61852
+ pendingLength = 0;
61853
+ awaitedLength = 0;
61536
61854
  while (buffer.length >= 4) {
61537
61855
  const length = buffer.readUInt32BE(0);
61538
61856
  if (buffer.length < 4 + length) {
61857
+ pending = [buffer];
61858
+ pendingLength = buffer.length;
61859
+ awaitedLength = 4 + length;
61860
+ buffer = Buffer.alloc(0);
61539
61861
  break;
61540
61862
  }
61541
61863
  const payload = buffer.subarray(4, 4 + length);
@@ -61647,7 +61969,8 @@ function buildSendSignature(to, options) {
61647
61969
  attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
61648
61970
  replyTo: options.replyTo ?? null,
61649
61971
  expectsReply: options.expectsReply ?? false,
61650
- replyError: options.replyError ?? null
61972
+ replyError: options.replyError ?? null,
61973
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery }
61651
61974
  });
61652
61975
  }
61653
61976
 
@@ -61833,6 +62156,8 @@ function isMessage(value2) {
61833
62156
  return false;
61834
62157
  if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean")
61835
62158
  return false;
62159
+ if (message.delivery !== undefined && message.delivery !== "steer" && message.delivery !== "followUp" && message.delivery !== "interrupt")
62160
+ return false;
61836
62161
  if (message.replyError !== undefined && typeof message.replyError !== "string")
61837
62162
  return false;
61838
62163
  if (message.source !== undefined && !isMessageSource(message.source))
@@ -63042,6 +63367,7 @@ var init_client = __esm(() => {
63042
63367
  timestamp: Date.now(),
63043
63368
  replyTo: options.replyTo,
63044
63369
  expectsReply: options.expectsReply,
63370
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery },
63045
63371
  replyError: options.replyError,
63046
63372
  source: this._messageSource,
63047
63373
  content: { text: options.text, attachments: options.attachments }