@bastani/atomic 0.9.45 → 0.9.46

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 (214) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +22 -0
  3. package/dist/builtin/intercom/broker/broker.ts +124 -10
  4. package/dist/builtin/intercom/broker/client.ts +177 -25
  5. package/dist/builtin/intercom/broker/multinode-router.ts +228 -27
  6. package/dist/builtin/intercom/broker/multinode-state-store.ts +10 -0
  7. package/dist/builtin/intercom/broker/recipient-receipts.ts +38 -4
  8. package/dist/builtin/intercom/broker/send-handler.ts +9 -2
  9. package/dist/builtin/intercom/broker/sql-outbox-store.ts +29 -4
  10. package/dist/builtin/intercom/group.ts +25 -0
  11. package/dist/builtin/intercom/index.bundle.mjs +177 -35
  12. package/dist/builtin/intercom/package.json +1 -1
  13. package/dist/builtin/intercom/retry-policy.ts +7 -0
  14. package/dist/builtin/intercom/types.ts +21 -0
  15. package/dist/builtin/mcp/package.json +1 -1
  16. package/dist/builtin/subagents/CHANGELOG.md +21 -0
  17. package/dist/builtin/subagents/package.json +1 -1
  18. package/dist/builtin/subagents/src/extension/index.bundle.mjs +152 -56
  19. package/dist/builtin/web-access/package.json +1 -1
  20. package/dist/builtin/workflows/CHANGELOG.md +79 -0
  21. package/dist/builtin/workflows/builtin/{chunk-6901nsd6.js → chunk-6jpwc09k.js} +1 -1
  22. package/dist/builtin/workflows/builtin/{chunk-vwx023ge.js → chunk-qhm35ntx.js} +1 -1
  23. package/dist/builtin/workflows/builtin/{chunk-jvna28mq.js → chunk-zhyqnf5g.js} +331 -68
  24. package/dist/builtin/workflows/builtin/goal.js +2 -2
  25. package/dist/builtin/workflows/builtin/index.js +3 -3
  26. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  27. package/dist/builtin/workflows/package.json +1 -1
  28. package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +1 -0
  29. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6.md +2 -2
  30. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_1_sol.md +2 -2
  31. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_1_terra.md +77 -0
  32. package/dist/builtin/workflows/src/authoring.d.ts +1 -1
  33. package/dist/builtin/workflows/src/durable/journal/internal.d.ts +2 -0
  34. package/dist/builtin/workflows/src/extension/index.bundle.mjs +7104 -3387
  35. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +11105 -7264
  36. package/dist/builtin/workflows/src/index.js +6796 -3606
  37. package/dist/builtin/workflows/src/local-worker-entry.mjs +11105 -7264
  38. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +19 -0
  39. package/dist/core/agent-session-boundaries.d.ts.map +1 -1
  40. package/dist/core/agent-session-boundaries.js +1 -0
  41. package/dist/core/agent-session-boundaries.js.map +1 -1
  42. package/dist/core/agent-session-events.d.ts.map +1 -1
  43. package/dist/core/agent-session-events.js +2 -1
  44. package/dist/core/agent-session-events.js.map +1 -1
  45. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  46. package/dist/core/agent-session-extension-bindings.js +15 -2
  47. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  48. package/dist/core/agent-session-methods.d.ts +3 -2
  49. package/dist/core/agent-session-methods.d.ts.map +1 -1
  50. package/dist/core/agent-session-methods.js.map +1 -1
  51. package/dist/core/agent-session-prompt.d.ts +4 -2
  52. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  53. package/dist/core/agent-session-prompt.js +13 -8
  54. package/dist/core/agent-session-prompt.js.map +1 -1
  55. package/dist/core/agent-session-recovery.d.ts.map +1 -1
  56. package/dist/core/agent-session-recovery.js +92 -1
  57. package/dist/core/agent-session-recovery.js.map +1 -1
  58. package/dist/core/agent-session-tasks.d.ts.map +1 -1
  59. package/dist/core/agent-session-tasks.js +8 -1
  60. package/dist/core/agent-session-tasks.js.map +1 -1
  61. package/dist/core/agent-session-transfer.d.ts.map +1 -1
  62. package/dist/core/agent-session-transfer.js +5 -0
  63. package/dist/core/agent-session-transfer.js.map +1 -1
  64. package/dist/core/agent-session.d.ts +2 -0
  65. package/dist/core/agent-session.d.ts.map +1 -1
  66. package/dist/core/agent-session.js +2 -0
  67. package/dist/core/agent-session.js.map +1 -1
  68. package/dist/core/copilot-errors.d.ts +6 -0
  69. package/dist/core/copilot-errors.d.ts.map +1 -0
  70. package/dist/core/copilot-errors.js +17 -0
  71. package/dist/core/copilot-errors.js.map +1 -0
  72. package/dist/core/execution-model-router.d.ts.map +1 -1
  73. package/dist/core/execution-model-router.js +12 -2
  74. package/dist/core/execution-model-router.js.map +1 -1
  75. package/dist/core/extensions/context-types.d.ts +7 -0
  76. package/dist/core/extensions/context-types.d.ts.map +1 -1
  77. package/dist/core/extensions/context-types.js.map +1 -1
  78. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  79. package/dist/core/extensions/runner-context.js +6 -0
  80. package/dist/core/extensions/runner-context.js.map +1 -1
  81. package/dist/core/extensions/ui-types.d.ts +9 -1
  82. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  83. package/dist/core/extensions/ui-types.js.map +1 -1
  84. package/dist/core/extensions/workflow-activity-hub.d.ts +4 -0
  85. package/dist/core/extensions/workflow-activity-hub.d.ts.map +1 -1
  86. package/dist/core/extensions/workflow-activity-hub.js +26 -5
  87. package/dist/core/extensions/workflow-activity-hub.js.map +1 -1
  88. package/dist/core/extensions/workflow-events.d.ts +5 -0
  89. package/dist/core/extensions/workflow-events.d.ts.map +1 -1
  90. package/dist/core/extensions/workflow-events.js.map +1 -1
  91. package/dist/core/messages.d.ts +5 -0
  92. package/dist/core/messages.d.ts.map +1 -1
  93. package/dist/core/messages.js +1 -1
  94. package/dist/core/messages.js.map +1 -1
  95. package/dist/core/model-fallback-failures.d.ts +1 -1
  96. package/dist/core/model-fallback-failures.d.ts.map +1 -1
  97. package/dist/core/model-fallback-failures.js +37 -3
  98. package/dist/core/model-fallback-failures.js.map +1 -1
  99. package/dist/core/model-resolver-defaults.js +2 -2
  100. package/dist/core/model-resolver-defaults.js.map +1 -1
  101. package/dist/core/model-runtime-streaming.js +2 -2
  102. package/dist/core/model-runtime-streaming.js.map +1 -1
  103. package/dist/core/package-manager-npm.d.ts.map +1 -1
  104. package/dist/core/package-manager-npm.js +7 -3
  105. package/dist/core/package-manager-npm.js.map +1 -1
  106. package/dist/core/provider-composer.d.ts.map +1 -1
  107. package/dist/core/provider-composer.js +19 -11
  108. package/dist/core/provider-composer.js.map +1 -1
  109. package/dist/core/provider-rate-limits.d.ts +55 -6
  110. package/dist/core/provider-rate-limits.d.ts.map +1 -1
  111. package/dist/core/provider-rate-limits.js +182 -29
  112. package/dist/core/provider-rate-limits.js.map +1 -1
  113. package/dist/core/provider-request-admission.d.ts.map +1 -1
  114. package/dist/core/provider-request-admission.js +42 -18
  115. package/dist/core/provider-request-admission.js.map +1 -1
  116. package/dist/core/storage/node-sqlite.d.ts.map +1 -1
  117. package/dist/core/storage/node-sqlite.js +176 -19
  118. package/dist/core/storage/node-sqlite.js.map +1 -1
  119. package/dist/core/storage/provider.d.ts +4 -2
  120. package/dist/core/storage/provider.d.ts.map +1 -1
  121. package/dist/core/storage/provider.js +94 -5
  122. package/dist/core/storage/provider.js.map +1 -1
  123. package/dist/core/structured-output/index.d.ts.map +1 -1
  124. package/dist/core/structured-output/index.js +15 -8
  125. package/dist/core/structured-output/index.js.map +1 -1
  126. package/dist/core/structured-output/provider-failure.d.ts +17 -0
  127. package/dist/core/structured-output/provider-failure.d.ts.map +1 -0
  128. package/dist/core/structured-output/provider-failure.js +28 -0
  129. package/dist/core/structured-output/provider-failure.js.map +1 -0
  130. package/dist/core/tasks/completion.d.ts +6 -1
  131. package/dist/core/tasks/completion.d.ts.map +1 -1
  132. package/dist/core/tasks/completion.js +33 -18
  133. package/dist/core/tasks/completion.js.map +1 -1
  134. package/dist/core/tasks/pause.d.ts +2 -1
  135. package/dist/core/tasks/pause.d.ts.map +1 -1
  136. package/dist/core/tasks/pause.js +3 -2
  137. package/dist/core/tasks/pause.js.map +1 -1
  138. package/dist/core/tools/bash.d.ts.map +1 -1
  139. package/dist/core/tools/bash.js +13 -7
  140. package/dist/core/tools/bash.js.map +1 -1
  141. package/dist/core/tools/grep.d.ts.map +1 -1
  142. package/dist/core/tools/grep.js +6 -4
  143. package/dist/core/tools/grep.js.map +1 -1
  144. package/dist/core/tools/shell-task-render.d.ts +1 -0
  145. package/dist/core/tools/shell-task-render.d.ts.map +1 -1
  146. package/dist/core/tools/shell-task-render.js +7 -2
  147. package/dist/core/tools/shell-task-render.js.map +1 -1
  148. package/dist/core/tools/shell-task-status.d.ts.map +1 -1
  149. package/dist/core/tools/shell-task-status.js +2 -1
  150. package/dist/core/tools/shell-task-status.js.map +1 -1
  151. package/dist/extensions/tool-search/tool.d.ts +5 -0
  152. package/dist/extensions/tool-search/tool.d.ts.map +1 -1
  153. package/dist/extensions/tool-search/tool.js +38 -5
  154. package/dist/extensions/tool-search/tool.js.map +1 -1
  155. package/dist/index.d.ts +1 -1
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +1 -1
  158. package/dist/index.js.map +1 -1
  159. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  160. package/dist/modes/interactive/components/assistant-message.js +20 -12
  161. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  162. package/dist/modes/interactive/components/chat-session-host-actions.js +3 -2
  163. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  164. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  165. package/dist/modes/interactive/components/login-dialog.js +42 -14
  166. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  167. package/dist/modes/interactive/interactive-extension-context.js +2 -1
  168. package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
  169. package/dist/modes/interactive/interactive-extension-custom-ui.js +1 -1
  170. package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
  171. package/dist/modes/interactive/interactive-extension-dialogs.js +10 -4
  172. package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
  173. package/dist/modes/interactive/interactive-mode-surface.d.ts +1 -0
  174. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  175. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  176. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  177. package/dist/modes/interactive/interactive-startup.js +5 -6
  178. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  179. package/dist/modes/interactive-engine/engine-custom-ui.d.ts +3 -0
  180. package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
  181. package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
  182. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  183. package/dist/modes/interactive-engine/protocol.d.ts +6 -0
  184. package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
  185. package/dist/modes/interactive-engine/protocol.js +9 -0
  186. package/dist/modes/interactive-engine/protocol.js.map +1 -1
  187. package/dist/modes/interactive-engine/remote-component.d.ts.map +1 -1
  188. package/dist/modes/interactive-engine/remote-component.js +3 -2
  189. package/dist/modes/interactive-engine/remote-component.js.map +1 -1
  190. package/dist/modes/rpc/rpc-client-process.d.ts.map +1 -1
  191. package/dist/modes/rpc/rpc-client-process.js +8 -1
  192. package/dist/modes/rpc/rpc-client-process.js.map +1 -1
  193. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  194. package/dist/modes/rpc/rpc-client.js +7 -1
  195. package/dist/modes/rpc/rpc-client.js.map +1 -1
  196. package/dist/utils/ansi.d.ts +2 -0
  197. package/dist/utils/ansi.d.ts.map +1 -1
  198. package/dist/utils/ansi.js +5 -0
  199. package/dist/utils/ansi.js.map +1 -1
  200. package/docs/codemode.md +1 -1
  201. package/docs/intercom/reference.md +3 -3
  202. package/docs/llama-cpp.md +25 -14
  203. package/docs/models/evals.md +31 -12
  204. package/docs/models/reference.md +23 -51
  205. package/docs/models.md +3 -4
  206. package/docs/providers.md +5 -15
  207. package/docs/settings.md +1 -1
  208. package/docs/storage.md +2 -2
  209. package/docs/tools.md +1 -1
  210. package/docs/tui.md +2 -0
  211. package/docs/workflows/api-reference.md +20 -1
  212. package/docs/workflows/operations.md +17 -4
  213. package/npm-shrinkwrap.json +64 -44
  214. package/package.json +4 -4
@@ -36,6 +36,34 @@ export const PENDING_STAGE_ROUTE_ACK_TIMEOUT_MS = 10_000;
36
36
  const GROUP_REQUEST_TIMEOUT_MS = 5000;
37
37
  const PRESENCE_ACK_TIMEOUT_MS = 5000;
38
38
  const RESTORING_RECIPIENT_REQUEST_TIMEOUT_MS = 5000;
39
+ /** #3904: retries of a connection the kernel refused before it reached the broker (a full listen backlog). */
40
+ const BROKER_BACKLOG_RETRIES = 6;
41
+ const BROKER_BACKLOG_RETRY_BASE_MS = 25;
42
+
43
+ /** The connect attempt was cancelled by `disconnect()` before it registered. */
44
+ function connectAborted(): IntercomClientDisconnectedError {
45
+ return new IntercomClientDisconnectedError({ cause: new Error("Disconnected before the broker connection registered") });
46
+ }
47
+
48
+ function abortableDelay(ms: number, signal: AbortSignal): Promise<void> {
49
+ return new Promise((resolveDelay, rejectDelay) => {
50
+ const timer = setTimeout(() => {
51
+ signal.removeEventListener("abort", onAbort);
52
+ resolveDelay();
53
+ }, ms);
54
+ const onAbort = () => {
55
+ clearTimeout(timer);
56
+ rejectDelay(connectAborted());
57
+ };
58
+ if (signal.aborted) onAbort();
59
+ else signal.addEventListener("abort", onAbort, { once: true });
60
+ });
61
+ }
62
+
63
+ function isBacklogRefusal(error: Error): boolean {
64
+ const code = (error as NodeJS.ErrnoException).code;
65
+ return code === "ECONNREFUSED" || code === "EAGAIN";
66
+ }
39
67
  /** Extra wait past a broker hold, so the broker's own outcome for a held send always arrives first (#3760). */
40
68
  const HELD_SEND_GRACE_MS = 5000;
41
69
 
@@ -93,6 +121,8 @@ export interface PendingStageMessageRequest {
93
121
  readonly senderRegistrationName?: string;
94
122
  readonly senderReturnAddress?: string;
95
123
  readonly live?: boolean;
124
+ /** #4006: the broker authorized the sender as the session that launched the run, not by membership. */
125
+ readonly invocationControl?: true;
96
126
  }
97
127
 
98
128
  /** Broker → route owner: the live stage targets a sticky broadcast was actually written to. */
@@ -212,6 +242,12 @@ export class IntercomClient extends EventEmitter {
212
242
  /** #3906: the broker of the current registration refuses a pending-stage route without ending the connection. */
213
243
  private brokerRefusesPendingStageRoutesInPlace = false;
214
244
  private brokerAnswersStatus = false;
245
+ /** #4006: the broker of the current registration accepts `claim_workflow_invocation`. */
246
+ private brokerClaimsWorkflowInvocations = false;
247
+ /** #3904: the connect attempt in progress, which `disconnect()` cancels. */
248
+ private connectAttempt: AbortController | undefined;
249
+ /** #3983: the broker of the current registration accepts `saving`. */
250
+ private brokerExtendsSaves = false;
215
251
  private socket: net.Socket | null = null;
216
252
  private _sessionId: string | null = null;
217
253
  private _supervisorSessionId: string | null = null;
@@ -231,6 +267,7 @@ export class IntercomClient extends EventEmitter {
231
267
  private pendingBrokerStatus = new Map<string, { resolve: (report: BrokerStatusReport) => void; reject: (error: Error) => void }>();
232
268
  private _groups = normalizeGroups();
233
269
  private pendingLiveWorkflowStageRoutes = new Map<string, { resolve(): void; reject(error: Error): void }>();
270
+ private pendingWorkflowInvocationClaims = new Map<string, { resolve(claimed: boolean): void; reject(error: Error): void }>();
234
271
  private pendingStageRouteRegistrations = new Map<string, { resolve(): void; reject(error: Error): void }>();
235
272
 
236
273
  private pendingSupervisorAuthorizations = new Map<string, {
@@ -297,6 +334,7 @@ export class IntercomClient extends EventEmitter {
297
334
  for (const pending of this.pendingPresence.values()) pending.reject(error);
298
335
  for (const pending of this.pendingMemberships.values()) pending.reject(error);
299
336
  for (const pending of this.pendingLiveWorkflowStageRoutes.values()) pending.reject(error);
337
+ for (const pending of this.pendingWorkflowInvocationClaims.values()) pending.reject(error);
300
338
  for (const pending of this.pendingStageRouteRegistrations.values()) pending.reject(error);
301
339
  for (const pending of this.pendingBrokerStatus.values()) pending.reject(error);
302
340
  this.pendingLists.clear();
@@ -306,6 +344,7 @@ export class IntercomClient extends EventEmitter {
306
344
  this.pendingPresence.clear();
307
345
  this.pendingMemberships.clear();
308
346
  this.pendingLiveWorkflowStageRoutes.clear();
347
+ this.pendingWorkflowInvocationClaims.clear();
309
348
  this.pendingStageRouteRegistrations.clear();
310
349
  this.pendingBrokerStatus.clear();
311
350
  }
@@ -345,7 +384,43 @@ export class IntercomClient extends EventEmitter {
345
384
  return socket;
346
385
  }
347
386
 
348
- connect(
387
+ /**
388
+ * Connect and register. A connection the operating system refuses before it reaches the broker (`ECONNREFUSED`,
389
+ * `EAGAIN`) is retried a few times with a short jittered backoff: when many sessions start at once (a large pooled
390
+ * fan-out) the broker socket's listen backlog fills and the kernel refuses connections the broker would accept a
391
+ * moment later (#3904). Every other failure, and a refusal that persists past the retries, rejects as before.
392
+ */
393
+ async connect(
394
+ session: Omit<SessionInfo, "id">,
395
+ supervisor?: SupervisorRegistration,
396
+ supervisorOwnerToken?: string,
397
+ messageSource?: Message["source"],
398
+ registrationGroup?: string,
399
+ ): Promise<void> {
400
+ // #3904: `disconnect()` aborts this attempt, its retry delay and any later retry, so a session that finished
401
+ // its cleanup never ends up connected and registered by a retry still in flight.
402
+ const attempt = new AbortController();
403
+ this.connectAttempt = attempt;
404
+ try {
405
+ for (let retry = 0; ; retry++) {
406
+ if (attempt.signal.aborted) throw connectAborted();
407
+ try {
408
+ await this.connectOnce(attempt.signal, session, supervisor, supervisorOwnerToken, messageSource, registrationGroup);
409
+ return;
410
+ } catch (error) {
411
+ if (attempt.signal.aborted) throw connectAborted();
412
+ if (!(error instanceof Error) || !isBacklogRefusal(error) || retry >= BROKER_BACKLOG_RETRIES) throw error;
413
+ const backoff = BROKER_BACKLOG_RETRY_BASE_MS * 2 ** retry;
414
+ await abortableDelay(backoff * (0.5 + Math.random() / 2), attempt.signal);
415
+ }
416
+ }
417
+ } finally {
418
+ if (this.connectAttempt === attempt) this.connectAttempt = undefined;
419
+ }
420
+ }
421
+
422
+ private connectOnce(
423
+ signal: AbortSignal,
349
424
  session: Omit<SessionInfo, "id">,
350
425
  supervisor?: SupervisorRegistration,
351
426
  supervisorOwnerToken?: string,
@@ -413,6 +488,8 @@ export class IntercomClient extends EventEmitter {
413
488
  this.brokerAcknowledgesPendingStageRoutes = false;
414
489
  this.brokerRefusesPendingStageRoutesInPlace = false;
415
490
  this.brokerAnswersStatus = false;
491
+ this.brokerClaimsWorkflowInvocations = false;
492
+ this.brokerExtendsSaves = false;
416
493
  this._messageSource = undefined;
417
494
  this.disconnectError = null;
418
495
  if (connectionEstablished && !wasDisconnecting) {
@@ -459,7 +536,38 @@ export class IntercomClient extends EventEmitter {
459
536
  this.handleBrokerMessage(msg);
460
537
  }, onReaderError);
461
538
 
539
+ const onConnect = (): void => {
540
+ if (signal.aborted) {
541
+ onAbort();
542
+ return;
543
+ }
544
+ try {
545
+ writeMessage(socket, {
546
+ type: "register",
547
+ session,
548
+ ...(registrationGroup !== undefined ? { registrationGroup } : {}),
549
+ returnAddress: this.returnAddress,
550
+ ...(this.preferredSessionId === undefined ? {} : { preferredSessionId: this.preferredSessionId }),
551
+ ...(this.acknowledgesReceipt ? { acknowledgesReceipt: true as const } : {}),
552
+ ...(supervisor ? { supervisor } : {}),
553
+ ...(supervisorOwnerToken ? { supervisorOwnerToken } : {}),
554
+ });
555
+ } catch (error) {
556
+ cleanupConnectionAttempt();
557
+ cleanupSocketListeners();
558
+ if (this.socket === socket) {
559
+ this.socket = null;
560
+ }
561
+ socket.destroy();
562
+ reject(toError(error));
563
+ }
564
+ };
565
+
566
+ const onAbort = () => onError(connectAborted());
567
+
462
568
  const cleanupConnectionAttempt = () => {
569
+ signal.removeEventListener("abort", onAbort);
570
+ socket.off("connect", onConnect);
463
571
  this.off("_registered", onRegistered);
464
572
  this.off("_registration_failed", onRegistrationFailed);
465
573
  socket.off("error", onError);
@@ -480,26 +588,9 @@ export class IntercomClient extends EventEmitter {
480
588
  this.once("_registered", onRegistered);
481
589
  this.once("_registration_failed", onRegistrationFailed);
482
590
 
483
- try {
484
- writeMessage(socket, {
485
- type: "register",
486
- session,
487
- ...(registrationGroup !== undefined ? { registrationGroup } : {}),
488
- returnAddress: this.returnAddress,
489
- ...(this.preferredSessionId === undefined ? {} : { preferredSessionId: this.preferredSessionId }),
490
- ...(this.acknowledgesReceipt ? { acknowledgesReceipt: true as const } : {}),
491
- ...(supervisor ? { supervisor } : {}),
492
- ...(supervisorOwnerToken ? { supervisorOwnerToken } : {}),
493
- });
494
- } catch (error) {
495
- cleanupConnectionAttempt();
496
- cleanupSocketListeners();
497
- if (this.socket === socket) {
498
- this.socket = null;
499
- }
500
- socket.destroy();
501
- reject(toError(error));
502
- }
591
+ // Register only once connected, and only if the attempt was not cancelled meanwhile (#3904).
592
+ signal.addEventListener("abort", onAbort, { once: true });
593
+ socket.once("connect", onConnect);
503
594
  });
504
595
  }
505
596
 
@@ -523,7 +614,9 @@ export class IntercomClient extends EventEmitter {
523
614
  || (brokerMessage.acknowledgesReceipt !== undefined && brokerMessage.acknowledgesReceipt !== true)
524
615
  || (brokerMessage.acknowledgesPendingStageRoutes !== undefined && brokerMessage.acknowledgesPendingStageRoutes !== true)
525
616
  || (brokerMessage.refusesPendingStageRoutesInPlace !== undefined && brokerMessage.refusesPendingStageRoutesInPlace !== true)
526
- || (brokerMessage.brokerStatus !== undefined && brokerMessage.brokerStatus !== true)) {
617
+ || (brokerMessage.brokerStatus !== undefined && brokerMessage.brokerStatus !== true)
618
+ || (brokerMessage.claimsWorkflowInvocations !== undefined && brokerMessage.claimsWorkflowInvocations !== true)
619
+ || (brokerMessage.extendsSaves !== undefined && brokerMessage.extendsSaves !== true)) {
527
620
  throw new Error("Invalid registered message");
528
621
  }
529
622
 
@@ -539,6 +632,8 @@ export class IntercomClient extends EventEmitter {
539
632
  this.brokerAcknowledgesPendingStageRoutes = brokerMessage.acknowledgesPendingStageRoutes === true;
540
633
  this.brokerRefusesPendingStageRoutesInPlace = brokerMessage.refusesPendingStageRoutesInPlace === true;
541
634
  this.brokerAnswersStatus = brokerMessage.brokerStatus === true;
635
+ this.brokerClaimsWorkflowInvocations = brokerMessage.claimsWorkflowInvocations === true;
636
+ this.brokerExtendsSaves = this.brokerAcknowledgesReceipt && brokerMessage.extendsSaves === true;
542
637
  this.emit("_registered", { type: "registered", sessionId: brokerMessage.sessionId });
543
638
  break;
544
639
  }
@@ -661,7 +756,8 @@ export class IntercomClient extends EventEmitter {
661
756
  break;
662
757
  }
663
758
  case "pending_stage_message": {
664
- const { requestId, from, senderRegistrationName, senderReturnAddress, runId, target, message, live } = brokerMessage;
759
+ const { requestId, from, senderRegistrationName, senderReturnAddress, runId, target, message, live, invocationControl } =
760
+ brokerMessage;
665
761
  if (
666
762
  typeof requestId !== "string" ||
667
763
  !isSessionInfo(from) ||
@@ -670,7 +766,8 @@ export class IntercomClient extends EventEmitter {
670
766
  typeof runId !== "string" ||
671
767
  typeof target !== "string" ||
672
768
  !isMessage(message) ||
673
- (live !== undefined && typeof live !== "boolean")
769
+ (live !== undefined && typeof live !== "boolean") ||
770
+ (invocationControl !== undefined && invocationControl !== true)
674
771
  ) {
675
772
  throw new Error("Invalid pending-stage message event");
676
773
  }
@@ -683,6 +780,7 @@ export class IntercomClient extends EventEmitter {
683
780
  target,
684
781
  message,
685
782
  ...(live === true ? { live: true } : {}),
783
+ ...(invocationControl === true ? { invocationControl: true } : {}),
686
784
  } satisfies PendingStageMessageRequest);
687
785
  break;
688
786
  }
@@ -753,6 +851,15 @@ export class IntercomClient extends EventEmitter {
753
851
  pending.resolve();
754
852
  break;
755
853
  }
854
+ case "workflow_invocation_claimed": {
855
+ const { requestId, claimed } = brokerMessage;
856
+ if (typeof requestId !== "string" || typeof claimed !== "boolean") throw new Error("Invalid workflow invocation claim answer");
857
+ const pending = this.pendingWorkflowInvocationClaims.get(requestId);
858
+ if (!pending) return;
859
+ this.pendingWorkflowInvocationClaims.delete(requestId);
860
+ pending.resolve(claimed);
861
+ break;
862
+ }
756
863
  case "live_workflow_stage_route_registered": {
757
864
  const { requestId } = brokerMessage;
758
865
  if (typeof requestId !== "string") throw new Error("Invalid live workflow-stage route registration");
@@ -931,6 +1038,12 @@ export class IntercomClient extends EventEmitter {
931
1038
  }
932
1039
  }
933
1040
  async disconnect(): Promise<void> {
1041
+ // A connect still in progress (or waiting to retry) is cancelled: it closes any socket it opened and never
1042
+ // registers (#3904).
1043
+ if (this.connectAttempt !== undefined && this._sessionId === null) {
1044
+ this.connectAttempt.abort();
1045
+ return;
1046
+ }
934
1047
  const socket = this.socket;
935
1048
  if (!socket) {
936
1049
  return;
@@ -1194,6 +1307,7 @@ export class IntercomClient extends EventEmitter {
1194
1307
  possibleStages?: WorkflowPossibleStageAnnouncement[],
1195
1308
  parent?: WorkflowRunParentAnnouncement,
1196
1309
  dormant?: boolean,
1310
+ launcherCapability?: string,
1197
1311
  ): void {
1198
1312
  writeMessage(this.requireActiveSocket(), {
1199
1313
  type: "register_pending_stage_route",
@@ -1204,6 +1318,7 @@ export class IntercomClient extends EventEmitter {
1204
1318
  possibleStages,
1205
1319
  ...(parent === undefined ? {} : { parent }),
1206
1320
  ...(dormant === true ? { dormant: true as const } : {}),
1321
+ ...(launcherCapability === undefined || !this.brokerClaimsWorkflowInvocations ? {} : { launcherCapability }),
1207
1322
  });
1208
1323
  }
1209
1324
 
@@ -1220,6 +1335,8 @@ export class IntercomClient extends EventEmitter {
1220
1335
  possibleStages?: WorkflowPossibleStageAnnouncement[],
1221
1336
  parent?: WorkflowRunParentAnnouncement,
1222
1337
  dormant?: boolean,
1338
+ /** #4006: the launcher-only secret, held by the process that launched the run and never by its stages. */
1339
+ launcherCapability?: string,
1223
1340
  ): Promise<void> {
1224
1341
  let socket: net.Socket;
1225
1342
  try {
@@ -1229,7 +1346,7 @@ export class IntercomClient extends EventEmitter {
1229
1346
  }
1230
1347
  if (!this.brokerAcknowledgesPendingStageRoutes) {
1231
1348
  try {
1232
- this.registerPendingStageRoute(runId, group, capability, stages, possibleStages, parent, dormant);
1349
+ this.registerPendingStageRoute(runId, group, capability, stages, possibleStages, parent, dormant, launcherCapability);
1233
1350
  } catch (error) {
1234
1351
  return Promise.reject(toError(error));
1235
1352
  }
@@ -1262,6 +1379,7 @@ export class IntercomClient extends EventEmitter {
1262
1379
  possibleStages,
1263
1380
  ...(parent === undefined ? {} : { parent }),
1264
1381
  ...(dormant === true ? { dormant: true as const } : {}),
1382
+ ...(launcherCapability === undefined || !this.brokerClaimsWorkflowInvocations ? {} : { launcherCapability }),
1265
1383
  ...(this.multiplexesPendingStageRoutes && this.brokerRefusesPendingStageRoutesInPlace
1266
1384
  ? { refusalKeepsConnection: true as const }
1267
1385
  : {}),
@@ -1274,6 +1392,31 @@ export class IntercomClient extends EventEmitter {
1274
1392
  });
1275
1393
  }
1276
1394
 
1395
+ /**
1396
+ * #4006: prove with the run's launcher capability (registered with the run's route, never given to its
1397
+ * stages) that this session launched `runId`, so it may send to the run's stages without joining
1398
+ * `workflow:<rootRunId>`. Resolves `false` when the broker refused the claim or predates it.
1399
+ */
1400
+ claimWorkflowInvocation(runId: string, launcherCapability: string): Promise<boolean> {
1401
+ let socket: net.Socket;
1402
+ try {
1403
+ socket = this.requireActiveSocket();
1404
+ } catch (error) {
1405
+ return Promise.reject(toError(error));
1406
+ }
1407
+ if (!this.brokerClaimsWorkflowInvocations) return Promise.resolve(false);
1408
+ const requestId = randomUUID();
1409
+ return new Promise((resolve, reject) => {
1410
+ this.pendingWorkflowInvocationClaims.set(requestId, { resolve, reject });
1411
+ try {
1412
+ writeMessage(socket, { type: "claim_workflow_invocation", requestId, runId, capability: launcherCapability });
1413
+ } catch (error) {
1414
+ this.pendingWorkflowInvocationClaims.delete(requestId);
1415
+ reject(toError(error));
1416
+ }
1417
+ });
1418
+ }
1419
+
1277
1420
  registerLiveWorkflowStageRoute(runId: string, stageKeys: readonly string[], capability: string): Promise<void> {
1278
1421
  let socket: net.Socket;
1279
1422
  try {
@@ -1343,6 +1486,15 @@ export class IntercomClient extends EventEmitter {
1343
1486
  writeMessage(this.requireActiveSocket(), { type: "received", messageId });
1344
1487
  }
1345
1488
 
1489
+ /**
1490
+ * #3983: tell the broker this message is still being saved, so its sender keeps waiting instead of hearing the
1491
+ * outcome is uncertain. A no-op on a broker that did not advertise `extendsSaves`, or while disconnected.
1492
+ */
1493
+ reportSaving(messageId: string): void {
1494
+ if (!this.brokerExtendsSaves || !this.socket || this.socket.destroyed) return;
1495
+ writeMessage(this.socket, { type: "saving", messageId });
1496
+ }
1497
+
1346
1498
  sendPendingStageNotification(
1347
1499
  runId: string,
1348
1500
  capability: string,