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

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 (40) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +10 -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/send-signature.ts +4 -1
  6. package/dist/builtin/intercom/index.bundle.mjs +82 -14
  7. package/dist/builtin/intercom/package.json +1 -1
  8. package/dist/builtin/intercom/skills/intercom/SKILL.md +1 -0
  9. package/dist/builtin/intercom/types.ts +8 -0
  10. package/dist/builtin/mcp/package.json +1 -1
  11. package/dist/builtin/subagents/package.json +1 -1
  12. package/dist/builtin/web-access/package.json +1 -1
  13. package/dist/builtin/workflows/CHANGELOG.md +6 -0
  14. package/dist/builtin/workflows/package.json +1 -1
  15. package/dist/builtin/workflows/src/extension/index.bundle.mjs +94 -5
  16. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +94 -5
  17. package/dist/builtin/workflows/src/index.js +94 -5
  18. package/dist/builtin/workflows/src/local-worker-entry.mjs +94 -5
  19. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  20. package/dist/core/agent-session-custom-message-commit.js +59 -0
  21. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  22. package/dist/core/agent-session-methods.d.ts +1 -0
  23. package/dist/core/agent-session-methods.d.ts.map +1 -1
  24. package/dist/core/agent-session-methods.js.map +1 -1
  25. package/dist/core/agent-session-prompt.d.ts +4 -0
  26. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  27. package/dist/core/agent-session-prompt.js +10 -2
  28. package/dist/core/agent-session-prompt.js.map +1 -1
  29. package/dist/core/agent-session.d.ts +2 -0
  30. package/dist/core/agent-session.d.ts.map +1 -1
  31. package/dist/core/agent-session.js +9 -0
  32. package/dist/core/agent-session.js.map +1 -1
  33. package/dist/core/builtin-packages.d.ts.map +1 -1
  34. package/dist/core/builtin-packages.js +13 -13
  35. package/dist/core/builtin-packages.js.map +1 -1
  36. package/docs/intercom/operations.md +1 -1
  37. package/docs/intercom.md +11 -1
  38. package/docs/subagents.md +9 -1
  39. package/npm-shrinkwrap.json +32 -32
  40. package/package.json +3 -3
@@ -1661,6 +1661,13 @@ function removeProcessExitCleanup(cleanup) {
1661
1661
  if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
1662
1662
  process.off("exit", runProcessExitCleanups);
1663
1663
  }
1664
+ function snapshotNeedsHistory(tail) {
1665
+ const event = tail.snapshot.event;
1666
+ if (event.type !== RUN_EVENT_TYPES.messageQueued)
1667
+ return false;
1668
+ const payload = event.payload;
1669
+ return hasPendingStageDeliveryDelta(payload.pendingStageMessages);
1670
+ }
1664
1671
  function* invoke(action) {
1665
1672
  try {
1666
1673
  const result = action();
@@ -1776,8 +1783,11 @@ class JournalDurableBackend {
1776
1783
  this.projection.applyWorkflowSnapshot(event.payload.handle);
1777
1784
  this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
1778
1785
  } else {
1779
- const handle = event.payload;
1780
1786
  const previous = this.projection.getWorkflow(entry.runId);
1787
+ const handle = event.type === RUN_EVENT_TYPES.messageQueued && hasPendingStageDeliveryDelta(event.payload.pendingStageMessages) ? {
1788
+ ...event.payload,
1789
+ pendingStageMessages: decodePendingStageDeliveries(previous?.pendingStageMessages ?? [], event.payload.pendingStageMessages ?? [])
1790
+ } : event.payload;
1781
1791
  this.projection.applyWorkflowSnapshot(handle);
1782
1792
  if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
1783
1793
  this.prompts.delete(entry.runId);
@@ -2073,11 +2083,23 @@ class JournalDurableBackend {
2073
2083
  for (const message of messages)
2074
2084
  if (message.runId !== logicalRunId)
2075
2085
  throw new JournalMessageScopeError(logicalRunId, message.runId);
2076
- yield* this.snapshot({
2086
+ const next = {
2077
2087
  ...handle,
2078
2088
  pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
2079
2089
  updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
2080
- }, RUN_EVENT_TYPES.messageQueued);
2090
+ };
2091
+ const encoded = encodePendingStageDeliveries(handle.pendingStageMessages ?? [], next.pendingStageMessages ?? []);
2092
+ const base = this.sequences.get(runId);
2093
+ if (encoded !== undefined && base !== undefined) {
2094
+ try {
2095
+ yield* this.append(runId, { type: RUN_EVENT_TYPES.messageQueued, payload: { ...next, pendingStageMessages: encoded } }, { expectSeq: base });
2096
+ return true;
2097
+ } catch (error) {
2098
+ if (!(error instanceof JournalCasConflictError))
2099
+ throw error;
2100
+ }
2101
+ }
2102
+ yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
2081
2103
  return true;
2082
2104
  }
2083
2105
  async archivePendingStageMessage(runId, message) {
@@ -2171,7 +2193,7 @@ class JournalDurableBackend {
2171
2193
  if (summarized !== undefined && summarized.seq === version.seq && summarized.at === version.at)
2172
2194
  return;
2173
2195
  const tail = yield* invoke(() => journal.snapshotTail(runId));
2174
- if (tail !== undefined)
2196
+ if (tail !== undefined && !snapshotNeedsHistory(tail))
2175
2197
  return this.summarize(runId, tail);
2176
2198
  if (summarized === undefined && version.seq === 0)
2177
2199
  return;
@@ -2397,6 +2419,69 @@ class JournalDurableBackend {
2397
2419
  }
2398
2420
  }
2399
2421
  }
2422
+ function identity(message) {
2423
+ return JSON.stringify([message.runId, message.id]);
2424
+ }
2425
+ function uniqueByIdentity(messages) {
2426
+ const result = new Map;
2427
+ const repeated = new Set;
2428
+ for (const message of messages) {
2429
+ const key = identity(message);
2430
+ if (result.has(key))
2431
+ repeated.add(key);
2432
+ result.set(key, message);
2433
+ }
2434
+ for (const key of repeated)
2435
+ result.delete(key);
2436
+ return result;
2437
+ }
2438
+ function isDeltaEncoded(message) {
2439
+ return message.deliveryDelta !== undefined;
2440
+ }
2441
+ function encodePendingStageDeliveries(previous, next) {
2442
+ const bases = uniqueByIdentity(previous);
2443
+ const nextIdentities = uniqueByIdentity(next);
2444
+ let encodedAny = false;
2445
+ const encoded = next.map((message) => {
2446
+ const base = bases.get(identity(message))?.deliveries;
2447
+ const deliveries = message.deliveries;
2448
+ if (base === undefined || deliveries === undefined || !nextIdentities.has(identity(message)))
2449
+ return message;
2450
+ const drop = [];
2451
+ let kept = 0;
2452
+ base.forEach((delivery, index) => {
2453
+ if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
2454
+ kept++;
2455
+ else
2456
+ drop.push(index);
2457
+ });
2458
+ const add = deliveries.slice(kept);
2459
+ if (drop.length + add.length >= deliveries.length)
2460
+ return message;
2461
+ encodedAny = true;
2462
+ const { deliveries: _full, ...rest } = message;
2463
+ return { ...rest, deliveryDelta: drop.length === 0 ? { add } : { drop, add } };
2464
+ });
2465
+ return encodedAny ? encoded : undefined;
2466
+ }
2467
+ function decodePendingStageDeliveries(previous, messages) {
2468
+ if (!messages.some(isDeltaEncoded))
2469
+ return messages;
2470
+ const bases = uniqueByIdentity(previous);
2471
+ return messages.map((message) => {
2472
+ if (!isDeltaEncoded(message))
2473
+ return message;
2474
+ const base = bases.get(identity(message))?.deliveries;
2475
+ if (base === undefined)
2476
+ throw new Error(`atomic-workflows: journal delivery change for ${identity(message)} has no base entry`);
2477
+ const { deliveryDelta, ...rest } = message;
2478
+ const dropped = new Set(deliveryDelta.drop ?? []);
2479
+ return { ...rest, deliveries: [...base.filter((_, index) => !dropped.has(index)), ...deliveryDelta.add] };
2480
+ });
2481
+ }
2482
+ function hasPendingStageDeliveryDelta(messages) {
2483
+ return messages?.some(isDeltaEncoded) === true;
2484
+ }
2400
2485
  var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
2401
2486
  var init_journal_backend = __esm(() => {
2402
2487
  init_workflow_instance_ownership_error();
@@ -58403,7 +58488,8 @@ function buildSendSignature(to, options) {
58403
58488
  attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
58404
58489
  replyTo: options.replyTo ?? null,
58405
58490
  expectsReply: options.expectsReply ?? false,
58406
- replyError: options.replyError ?? null
58491
+ replyError: options.replyError ?? null,
58492
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery }
58407
58493
  });
58408
58494
  }
58409
58495
 
@@ -58589,6 +58675,8 @@ function isMessage(value2) {
58589
58675
  return false;
58590
58676
  if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean")
58591
58677
  return false;
58678
+ if (message.delivery !== undefined && message.delivery !== "steer" && message.delivery !== "followUp" && message.delivery !== "interrupt")
58679
+ return false;
58592
58680
  if (message.replyError !== undefined && typeof message.replyError !== "string")
58593
58681
  return false;
58594
58682
  if (message.source !== undefined && !isMessageSource(message.source))
@@ -59798,6 +59886,7 @@ var init_client = __esm(() => {
59798
59886
  timestamp: Date.now(),
59799
59887
  replyTo: options.replyTo,
59800
59888
  expectsReply: options.expectsReply,
59889
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery },
59801
59890
  replyError: options.replyError,
59802
59891
  source: this._messageSource,
59803
59892
  content: { text: options.text, attachments: options.attachments }
@@ -9870,6 +9870,13 @@ function removeProcessExitCleanup(cleanup) {
9870
9870
  if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
9871
9871
  process.off("exit", runProcessExitCleanups);
9872
9872
  }
9873
+ function snapshotNeedsHistory(tail) {
9874
+ const event = tail.snapshot.event;
9875
+ if (event.type !== RUN_EVENT_TYPES.messageQueued)
9876
+ return false;
9877
+ const payload = event.payload;
9878
+ return hasPendingStageDeliveryDelta(payload.pendingStageMessages);
9879
+ }
9873
9880
  function* invoke(action) {
9874
9881
  try {
9875
9882
  const result = action();
@@ -9985,8 +9992,11 @@ class JournalDurableBackend {
9985
9992
  this.projection.applyWorkflowSnapshot(event.payload.handle);
9986
9993
  this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
9987
9994
  } else {
9988
- const handle = event.payload;
9989
9995
  const previous = this.projection.getWorkflow(entry.runId);
9996
+ const handle = event.type === RUN_EVENT_TYPES.messageQueued && hasPendingStageDeliveryDelta(event.payload.pendingStageMessages) ? {
9997
+ ...event.payload,
9998
+ pendingStageMessages: decodePendingStageDeliveries(previous?.pendingStageMessages ?? [], event.payload.pendingStageMessages ?? [])
9999
+ } : event.payload;
9990
10000
  this.projection.applyWorkflowSnapshot(handle);
9991
10001
  if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
9992
10002
  this.prompts.delete(entry.runId);
@@ -10282,11 +10292,23 @@ class JournalDurableBackend {
10282
10292
  for (const message of messages)
10283
10293
  if (message.runId !== logicalRunId)
10284
10294
  throw new JournalMessageScopeError(logicalRunId, message.runId);
10285
- yield* this.snapshot({
10295
+ const next = {
10286
10296
  ...handle,
10287
10297
  pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
10288
10298
  updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
10289
- }, RUN_EVENT_TYPES.messageQueued);
10299
+ };
10300
+ const encoded = encodePendingStageDeliveries(handle.pendingStageMessages ?? [], next.pendingStageMessages ?? []);
10301
+ const base = this.sequences.get(runId);
10302
+ if (encoded !== undefined && base !== undefined) {
10303
+ try {
10304
+ yield* this.append(runId, { type: RUN_EVENT_TYPES.messageQueued, payload: { ...next, pendingStageMessages: encoded } }, { expectSeq: base });
10305
+ return true;
10306
+ } catch (error) {
10307
+ if (!(error instanceof JournalCasConflictError))
10308
+ throw error;
10309
+ }
10310
+ }
10311
+ yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
10290
10312
  return true;
10291
10313
  }
10292
10314
  async archivePendingStageMessage(runId, message) {
@@ -10380,7 +10402,7 @@ class JournalDurableBackend {
10380
10402
  if (summarized !== undefined && summarized.seq === version.seq && summarized.at === version.at)
10381
10403
  return;
10382
10404
  const tail = yield* invoke(() => journal.snapshotTail(runId));
10383
- if (tail !== undefined)
10405
+ if (tail !== undefined && !snapshotNeedsHistory(tail))
10384
10406
  return this.summarize(runId, tail);
10385
10407
  if (summarized === undefined && version.seq === 0)
10386
10408
  return;
@@ -10606,6 +10628,69 @@ class JournalDurableBackend {
10606
10628
  }
10607
10629
  }
10608
10630
  }
10631
+ function identity(message) {
10632
+ return JSON.stringify([message.runId, message.id]);
10633
+ }
10634
+ function uniqueByIdentity(messages) {
10635
+ const result = new Map;
10636
+ const repeated = new Set;
10637
+ for (const message of messages) {
10638
+ const key = identity(message);
10639
+ if (result.has(key))
10640
+ repeated.add(key);
10641
+ result.set(key, message);
10642
+ }
10643
+ for (const key of repeated)
10644
+ result.delete(key);
10645
+ return result;
10646
+ }
10647
+ function isDeltaEncoded(message) {
10648
+ return message.deliveryDelta !== undefined;
10649
+ }
10650
+ function encodePendingStageDeliveries(previous, next) {
10651
+ const bases = uniqueByIdentity(previous);
10652
+ const nextIdentities = uniqueByIdentity(next);
10653
+ let encodedAny = false;
10654
+ const encoded = next.map((message) => {
10655
+ const base = bases.get(identity(message))?.deliveries;
10656
+ const deliveries = message.deliveries;
10657
+ if (base === undefined || deliveries === undefined || !nextIdentities.has(identity(message)))
10658
+ return message;
10659
+ const drop = [];
10660
+ let kept = 0;
10661
+ base.forEach((delivery, index) => {
10662
+ if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
10663
+ kept++;
10664
+ else
10665
+ drop.push(index);
10666
+ });
10667
+ const add = deliveries.slice(kept);
10668
+ if (drop.length + add.length >= deliveries.length)
10669
+ return message;
10670
+ encodedAny = true;
10671
+ const { deliveries: _full, ...rest } = message;
10672
+ return { ...rest, deliveryDelta: drop.length === 0 ? { add } : { drop, add } };
10673
+ });
10674
+ return encodedAny ? encoded : undefined;
10675
+ }
10676
+ function decodePendingStageDeliveries(previous, messages) {
10677
+ if (!messages.some(isDeltaEncoded))
10678
+ return messages;
10679
+ const bases = uniqueByIdentity(previous);
10680
+ return messages.map((message) => {
10681
+ if (!isDeltaEncoded(message))
10682
+ return message;
10683
+ const base = bases.get(identity(message))?.deliveries;
10684
+ if (base === undefined)
10685
+ throw new Error(`atomic-workflows: journal delivery change for ${identity(message)} has no base entry`);
10686
+ const { deliveryDelta, ...rest } = message;
10687
+ const dropped = new Set(deliveryDelta.drop ?? []);
10688
+ return { ...rest, deliveries: [...base.filter((_, index) => !dropped.has(index)), ...deliveryDelta.add] };
10689
+ });
10690
+ }
10691
+ function hasPendingStageDeliveryDelta(messages) {
10692
+ return messages?.some(isDeltaEncoded) === true;
10693
+ }
10609
10694
  var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
10610
10695
  var init_journal_backend = __esm(() => {
10611
10696
  init_workflow_instance_ownership_error();
@@ -59086,7 +59171,8 @@ function buildSendSignature(to, options) {
59086
59171
  attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
59087
59172
  replyTo: options.replyTo ?? null,
59088
59173
  expectsReply: options.expectsReply ?? false,
59089
- replyError: options.replyError ?? null
59174
+ replyError: options.replyError ?? null,
59175
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery }
59090
59176
  });
59091
59177
  }
59092
59178
 
@@ -59272,6 +59358,8 @@ function isMessage(value2) {
59272
59358
  return false;
59273
59359
  if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean")
59274
59360
  return false;
59361
+ if (message.delivery !== undefined && message.delivery !== "steer" && message.delivery !== "followUp" && message.delivery !== "interrupt")
59362
+ return false;
59275
59363
  if (message.replyError !== undefined && typeof message.replyError !== "string")
59276
59364
  return false;
59277
59365
  if (message.source !== undefined && !isMessageSource(message.source))
@@ -60481,6 +60569,7 @@ var init_client = __esm(() => {
60481
60569
  timestamp: Date.now(),
60482
60570
  replyTo: options.replyTo,
60483
60571
  expectsReply: options.expectsReply,
60572
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery },
60484
60573
  replyError: options.replyError,
60485
60574
  source: this._messageSource,
60486
60575
  content: { text: options.text, attachments: options.attachments }
@@ -1661,6 +1661,13 @@ function removeProcessExitCleanup(cleanup) {
1661
1661
  if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
1662
1662
  process.off("exit", runProcessExitCleanups);
1663
1663
  }
1664
+ function snapshotNeedsHistory(tail) {
1665
+ const event = tail.snapshot.event;
1666
+ if (event.type !== RUN_EVENT_TYPES.messageQueued)
1667
+ return false;
1668
+ const payload = event.payload;
1669
+ return hasPendingStageDeliveryDelta(payload.pendingStageMessages);
1670
+ }
1664
1671
  function* invoke(action) {
1665
1672
  try {
1666
1673
  const result = action();
@@ -1776,8 +1783,11 @@ class JournalDurableBackend {
1776
1783
  this.projection.applyWorkflowSnapshot(event.payload.handle);
1777
1784
  this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
1778
1785
  } else {
1779
- const handle = event.payload;
1780
1786
  const previous = this.projection.getWorkflow(entry.runId);
1787
+ const handle = event.type === RUN_EVENT_TYPES.messageQueued && hasPendingStageDeliveryDelta(event.payload.pendingStageMessages) ? {
1788
+ ...event.payload,
1789
+ pendingStageMessages: decodePendingStageDeliveries(previous?.pendingStageMessages ?? [], event.payload.pendingStageMessages ?? [])
1790
+ } : event.payload;
1781
1791
  this.projection.applyWorkflowSnapshot(handle);
1782
1792
  if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
1783
1793
  this.prompts.delete(entry.runId);
@@ -2073,11 +2083,23 @@ class JournalDurableBackend {
2073
2083
  for (const message of messages)
2074
2084
  if (message.runId !== logicalRunId)
2075
2085
  throw new JournalMessageScopeError(logicalRunId, message.runId);
2076
- yield* this.snapshot({
2086
+ const next = {
2077
2087
  ...handle,
2078
2088
  pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
2079
2089
  updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
2080
- }, RUN_EVENT_TYPES.messageQueued);
2090
+ };
2091
+ const encoded = encodePendingStageDeliveries(handle.pendingStageMessages ?? [], next.pendingStageMessages ?? []);
2092
+ const base = this.sequences.get(runId);
2093
+ if (encoded !== undefined && base !== undefined) {
2094
+ try {
2095
+ yield* this.append(runId, { type: RUN_EVENT_TYPES.messageQueued, payload: { ...next, pendingStageMessages: encoded } }, { expectSeq: base });
2096
+ return true;
2097
+ } catch (error) {
2098
+ if (!(error instanceof JournalCasConflictError))
2099
+ throw error;
2100
+ }
2101
+ }
2102
+ yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
2081
2103
  return true;
2082
2104
  }
2083
2105
  async archivePendingStageMessage(runId, message) {
@@ -2171,7 +2193,7 @@ class JournalDurableBackend {
2171
2193
  if (summarized !== undefined && summarized.seq === version.seq && summarized.at === version.at)
2172
2194
  return;
2173
2195
  const tail = yield* invoke(() => journal.snapshotTail(runId));
2174
- if (tail !== undefined)
2196
+ if (tail !== undefined && !snapshotNeedsHistory(tail))
2175
2197
  return this.summarize(runId, tail);
2176
2198
  if (summarized === undefined && version.seq === 0)
2177
2199
  return;
@@ -2397,6 +2419,69 @@ class JournalDurableBackend {
2397
2419
  }
2398
2420
  }
2399
2421
  }
2422
+ function identity(message) {
2423
+ return JSON.stringify([message.runId, message.id]);
2424
+ }
2425
+ function uniqueByIdentity(messages) {
2426
+ const result = new Map;
2427
+ const repeated = new Set;
2428
+ for (const message of messages) {
2429
+ const key = identity(message);
2430
+ if (result.has(key))
2431
+ repeated.add(key);
2432
+ result.set(key, message);
2433
+ }
2434
+ for (const key of repeated)
2435
+ result.delete(key);
2436
+ return result;
2437
+ }
2438
+ function isDeltaEncoded(message) {
2439
+ return message.deliveryDelta !== undefined;
2440
+ }
2441
+ function encodePendingStageDeliveries(previous, next) {
2442
+ const bases = uniqueByIdentity(previous);
2443
+ const nextIdentities = uniqueByIdentity(next);
2444
+ let encodedAny = false;
2445
+ const encoded = next.map((message) => {
2446
+ const base = bases.get(identity(message))?.deliveries;
2447
+ const deliveries = message.deliveries;
2448
+ if (base === undefined || deliveries === undefined || !nextIdentities.has(identity(message)))
2449
+ return message;
2450
+ const drop = [];
2451
+ let kept = 0;
2452
+ base.forEach((delivery, index) => {
2453
+ if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
2454
+ kept++;
2455
+ else
2456
+ drop.push(index);
2457
+ });
2458
+ const add = deliveries.slice(kept);
2459
+ if (drop.length + add.length >= deliveries.length)
2460
+ return message;
2461
+ encodedAny = true;
2462
+ const { deliveries: _full, ...rest } = message;
2463
+ return { ...rest, deliveryDelta: drop.length === 0 ? { add } : { drop, add } };
2464
+ });
2465
+ return encodedAny ? encoded : undefined;
2466
+ }
2467
+ function decodePendingStageDeliveries(previous, messages) {
2468
+ if (!messages.some(isDeltaEncoded))
2469
+ return messages;
2470
+ const bases = uniqueByIdentity(previous);
2471
+ return messages.map((message) => {
2472
+ if (!isDeltaEncoded(message))
2473
+ return message;
2474
+ const base = bases.get(identity(message))?.deliveries;
2475
+ if (base === undefined)
2476
+ throw new Error(`atomic-workflows: journal delivery change for ${identity(message)} has no base entry`);
2477
+ const { deliveryDelta, ...rest } = message;
2478
+ const dropped = new Set(deliveryDelta.drop ?? []);
2479
+ return { ...rest, deliveries: [...base.filter((_, index) => !dropped.has(index)), ...deliveryDelta.add] };
2480
+ });
2481
+ }
2482
+ function hasPendingStageDeliveryDelta(messages) {
2483
+ return messages?.some(isDeltaEncoded) === true;
2484
+ }
2400
2485
  var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
2401
2486
  var init_journal_backend = __esm(() => {
2402
2487
  init_workflow_instance_ownership_error();
@@ -58403,7 +58488,8 @@ function buildSendSignature(to, options) {
58403
58488
  attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
58404
58489
  replyTo: options.replyTo ?? null,
58405
58490
  expectsReply: options.expectsReply ?? false,
58406
- replyError: options.replyError ?? null
58491
+ replyError: options.replyError ?? null,
58492
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery }
58407
58493
  });
58408
58494
  }
58409
58495
 
@@ -58589,6 +58675,8 @@ function isMessage(value2) {
58589
58675
  return false;
58590
58676
  if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean")
58591
58677
  return false;
58678
+ if (message.delivery !== undefined && message.delivery !== "steer" && message.delivery !== "followUp" && message.delivery !== "interrupt")
58679
+ return false;
58592
58680
  if (message.replyError !== undefined && typeof message.replyError !== "string")
58593
58681
  return false;
58594
58682
  if (message.source !== undefined && !isMessageSource(message.source))
@@ -59798,6 +59886,7 @@ var init_client = __esm(() => {
59798
59886
  timestamp: Date.now(),
59799
59887
  replyTo: options.replyTo,
59800
59888
  expectsReply: options.expectsReply,
59889
+ ...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery },
59801
59890
  replyError: options.replyError,
59802
59891
  source: this._messageSource,
59803
59892
  content: { text: options.text, attachments: options.attachments }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-custom-message-commit.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-custom-message-commit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAO9F,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA6BnD,6EAA6E;AAC7E,wBAAsB,2BAA2B,CAAC,CAAC,EAClD,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAmGf;AAED,wBAAgB,4BAA4B,CAAC,CAAC,EAC7C,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,+EAA+E;AAC/E,wBAAsB,4BAA4B,CAAC,CAAC,EACnD,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAgB,6BAA6B,CAAC,CAAC,EAC9C,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,eAAO,MAAM,sCAAsC;;;CAGlD,CAAC"}
1
+ {"version":3,"file":"agent-session-custom-message-commit.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-custom-message-commit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAQ9F,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAmEnD,6EAA6E;AAC7E,wBAAsB,2BAA2B,CAAC,CAAC,EAClD,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA+Gf;AAED,wBAAgB,4BAA4B,CAAC,CAAC,EAC7C,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,+EAA+E;AAC/E,wBAAsB,4BAA4B,CAAC,CAAC,EACnD,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA2Df;AAED,wBAAgB,6BAA6B,CAAC,CAAC,EAC9C,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,eAAO,MAAM,sCAAsC;;;CAGlD,CAAC"}
@@ -1,5 +1,7 @@
1
1
  import { resolveWorkflowStageDeliveryTarget } from "./agent-session-delivery-forwarding.js";
2
2
  import { admitProtectedStreamingCustomMessage, queueProtectedStreamingCustomMessage, queueProtectedStreamingCustomMessages, triggerProtectedStreamingCustomMessages, } from "./agent-session-persistent-custom-messages.js";
3
+ import { deliverQueuedInputIfIdle } from "./agent-session-prompt.js";
4
+ import { TASK_COMPLETION_MESSAGE_TYPE } from "./tasks/completion.js";
3
5
  async function appendDurableStageAdmission(session, message) {
4
6
  await session._appendCustomMessage(message);
5
7
  }
@@ -23,15 +25,54 @@ async function commitPriorityMessage(session, message) {
23
25
  void turn.catch(() => { });
24
26
  }
25
27
  }
28
+ /**
29
+ * #3919: before a fresh stage's own prompt is submitted, turn-starting input is that prompt's startup context.
30
+ * Once the prompt is in preflight, a run has started, or queued input is paused, the ordinary admission paths
31
+ * own ordering. The stage's own task completions follow its own work, so they never precede its prompt.
32
+ */
33
+ function holdsForFirstStagePrompt(session, message, options) {
34
+ return (session._workflowStageAwaitingFirstPrompt &&
35
+ session._activePromptCount === 0 &&
36
+ !session._queuedMessagesPaused &&
37
+ message.customType !== TASK_COMPLETION_MESSAGE_TYPE &&
38
+ options?.triggerTurn === true &&
39
+ options.excludeFromContext !== true &&
40
+ options.deliverAs !== "nextTurn");
41
+ }
42
+ /**
43
+ * #3919: once the stage prompt is submitted but before it owns a run (its input hooks), a turn-starting
44
+ * message must not start a competing turn. It queues behind the pending prompt; interrupts keep their own path.
45
+ */
46
+ function queuesBehindFirstStagePrompt(session, options) {
47
+ return (session._workflowStageAwaitingFirstPrompt &&
48
+ session._activePromptCount > 0 &&
49
+ !session._queuedMessagesPaused &&
50
+ options?.triggerTurn === true &&
51
+ options.excludeFromContext !== true &&
52
+ options.deliverAs !== "nextTurn" &&
53
+ options.deliverAs !== "interrupt");
54
+ }
26
55
  /** Commit one delivery whose source generation already granted admission. */
27
56
  export async function commitAdmittedCustomMessage(session, appMessage, options) {
28
57
  const owner = resolveWorkflowStageDeliveryTarget(session);
29
58
  if (owner !== session)
30
59
  return commitAdmittedCustomMessage(owner, appMessage, options);
31
60
  const self = session;
61
+ if (holdsForFirstStagePrompt(self, appMessage, options)) {
62
+ await appendDurableStageAdmission(self, appMessage);
63
+ return;
64
+ }
32
65
  const useProtectedReconciliation = options?.persistWhenStreaming === true &&
33
66
  options.excludeFromContext !== true &&
34
67
  (options.triggerTurn === true || options.stageAdmissionKey !== undefined);
68
+ if (queuesBehindFirstStagePrompt(self, options)) {
69
+ const delivery = options?.deliverAs === "followUp" ? "followUp" : "steer";
70
+ if (useProtectedReconciliation)
71
+ await queueProtectedStreamingCustomMessage(self, appMessage, delivery);
72
+ else
73
+ self._queueAgentMessage(appMessage, delivery);
74
+ return;
75
+ }
35
76
  if (useProtectedReconciliation &&
36
77
  options?.triggerTurn &&
37
78
  options.deliverAs === "interrupt" &&
@@ -78,6 +119,9 @@ export async function commitAdmittedCustomMessage(session, appMessage, options)
78
119
  }
79
120
  else if ((self.isStreaming || self._isPreparingRun) && useProtectedReconciliation) {
80
121
  await queueProtectedStreamingCustomMessage(self, appMessage, options?.deliverAs === "followUp" ? "followUp" : "steer");
122
+ // The durable card append can yield past the run's last queue drain.
123
+ if (!self.isStreaming)
124
+ deliverQueuedInputIfIdle(self);
81
125
  }
82
126
  else if ((self.isStreaming || self._isPreparingRun) && options?.persistWhenStreaming === true) {
83
127
  await appendDurableStageAdmission(self, appMessage);
@@ -130,8 +174,21 @@ export async function commitAdmittedCustomMessages(session, appMessages, options
130
174
  if (owner !== session)
131
175
  return commitAdmittedCustomMessages(owner, appMessages, options);
132
176
  const self = session;
177
+ if (appMessages.every((item) => holdsForFirstStagePrompt(self, item, options))) {
178
+ for (const item of appMessages)
179
+ await appendDurableStageAdmission(self, item);
180
+ return;
181
+ }
133
182
  const useProtectedReconciliation = options?.persistWhenStreaming === true && options.triggerTurn === true && options.excludeFromContext !== true;
134
183
  const delivery = options?.deliverAs === "followUp" ? "followUp" : "steer";
184
+ if (queuesBehindFirstStagePrompt(self, options)) {
185
+ if (useProtectedReconciliation)
186
+ await queueProtectedStreamingCustomMessages(self, appMessages, delivery);
187
+ else
188
+ for (const item of appMessages)
189
+ self._queueAgentMessage(item, delivery);
190
+ return;
191
+ }
135
192
  if (options?.deliverAs === "nextTurn") {
136
193
  self._pendingNextTurnMessages.push(...appMessages);
137
194
  }
@@ -166,6 +223,8 @@ export async function commitAdmittedCustomMessages(session, appMessages, options
166
223
  }
167
224
  else if ((self.isStreaming || self._isPreparingRun) && useProtectedReconciliation) {
168
225
  await queueProtectedStreamingCustomMessages(self, appMessages, delivery);
226
+ if (!self.isStreaming)
227
+ deliverQueuedInputIfIdle(self);
169
228
  }
170
229
  else if ((self.isStreaming || self._isPreparingRun) && options?.persistWhenStreaming === true) {
171
230
  for (const item of appMessages)