@bastani/atomic 0.9.16-alpha.6 → 0.9.16-alpha.8

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 +19 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +4 -4
  4. package/dist/builtin/intercom/broker/broker.ts +617 -22
  5. package/dist/builtin/intercom/broker/client-message-validation.ts +9 -0
  6. package/dist/builtin/intercom/broker/client.ts +200 -10
  7. package/dist/builtin/intercom/broker/pending-send-registry.ts +4 -0
  8. package/dist/builtin/intercom/broker/send-handler.ts +90 -36
  9. package/dist/builtin/intercom/broker/send-signature.ts +20 -8
  10. package/dist/builtin/intercom/group.ts +85 -0
  11. package/dist/builtin/intercom/index.bundle.mjs +680 -53
  12. package/dist/builtin/intercom/package.json +1 -1
  13. package/dist/builtin/intercom/session-target.ts +54 -0
  14. package/dist/builtin/intercom/skills/intercom/SKILL.md +18 -3
  15. package/dist/builtin/intercom/source-ownership.ts +31 -0
  16. package/dist/builtin/intercom/types.ts +115 -0
  17. package/dist/builtin/mcp/index.bundle.mjs +109 -35
  18. package/dist/builtin/mcp/package.json +1 -1
  19. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  20. package/dist/builtin/subagents/package.json +1 -1
  21. package/dist/builtin/subagents/skills/qlty/SKILL.md +123 -0
  22. package/dist/builtin/subagents/skills/qlty/references/coding-with-ai-agents.md +64 -0
  23. package/dist/builtin/subagents/skills/qlty/references/commands.md +291 -0
  24. package/dist/builtin/subagents/skills/qlty/references/plugins-and-extensions.md +228 -0
  25. package/dist/builtin/subagents/skills/qlty/references/quickstart.md +110 -0
  26. package/dist/builtin/subagents/src/extension/index.bundle.mjs +173 -51
  27. package/dist/builtin/web-access/index.bundle.mjs +109 -35
  28. package/dist/builtin/web-access/package.json +1 -1
  29. package/dist/builtin/workflows/CHANGELOG.md +23 -0
  30. package/dist/builtin/workflows/README.md +40 -37
  31. package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
  32. package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +137 -133
  33. package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
  34. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  35. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  36. package/dist/builtin/workflows/builtin/chunk-5n10x7j2.js +69730 -0
  37. package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
  38. package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
  39. package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
  40. package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
  41. package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
  42. package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
  43. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  44. package/dist/builtin/workflows/builtin/chunk-cdtd3m3w.js +529 -0
  45. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  46. package/dist/builtin/workflows/builtin/chunk-hdpj1dkw.js +2447 -0
  47. package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
  48. package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
  49. package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
  50. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  51. package/dist/builtin/workflows/builtin/chunk-v04c5qmd.js +1207 -0
  52. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  53. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  54. package/dist/builtin/workflows/builtin/chunk-zf28603f.js +1080 -0
  55. package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
  56. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
  57. package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
  58. package/dist/builtin/workflows/builtin/goal.js +16 -0
  59. package/dist/builtin/workflows/builtin/index.js +49 -0
  60. package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
  61. package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
  62. package/dist/builtin/workflows/builtin/ralph.js +16 -0
  63. package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
  64. package/dist/builtin/workflows/builtin/steering-context.js +7 -0
  65. package/dist/builtin/workflows/builtin/tournament.js +13 -0
  66. package/dist/builtin/workflows/package.json +10 -7
  67. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  68. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1250 -428
  69. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +956 -427
  70. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
  71. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
  72. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  73. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
  74. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  75. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  76. package/dist/core/agent-session-custom-message-commit.js +12 -5
  77. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  78. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  79. package/dist/core/agent-session-persistent-custom-messages.js +2 -0
  80. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  81. package/dist/core/agent-session.d.ts.map +1 -1
  82. package/dist/core/agent-session.js +2 -1
  83. package/dist/core/agent-session.js.map +1 -1
  84. package/dist/core/auth-storage.d.ts +2 -0
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +4 -0
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/builtin-install-layout.d.ts +1 -1
  89. package/dist/core/builtin-install-layout.d.ts.map +1 -1
  90. package/dist/core/builtin-install-layout.js +1 -1
  91. package/dist/core/builtin-install-layout.js.map +1 -1
  92. package/dist/core/codex-fast-mode.d.ts +10 -7
  93. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  94. package/dist/core/codex-fast-mode.js +31 -13
  95. package/dist/core/codex-fast-mode.js.map +1 -1
  96. package/dist/core/extensions/context-types.d.ts +35 -0
  97. package/dist/core/extensions/context-types.d.ts.map +1 -1
  98. package/dist/core/extensions/context-types.js.map +1 -1
  99. package/dist/core/extensions/index.d.ts +1 -1
  100. package/dist/core/extensions/index.d.ts.map +1 -1
  101. package/dist/core/extensions/index.js.map +1 -1
  102. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  104. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  105. package/dist/core/model-runtime.d.ts +2 -0
  106. package/dist/core/model-runtime.d.ts.map +1 -1
  107. package/dist/core/model-runtime.js +4 -0
  108. package/dist/core/model-runtime.js.map +1 -1
  109. package/dist/core/runtime-credentials.d.ts +1 -0
  110. package/dist/core/runtime-credentials.d.ts.map +1 -1
  111. package/dist/core/runtime-credentials.js +8 -0
  112. package/dist/core/runtime-credentials.js.map +1 -1
  113. package/dist/core/sdk.d.ts.map +1 -1
  114. package/dist/core/sdk.js +5 -5
  115. package/dist/core/sdk.js.map +1 -1
  116. package/dist/core/session-manager-storage.d.ts +2 -0
  117. package/dist/core/session-manager-storage.d.ts.map +1 -1
  118. package/dist/core/session-manager-storage.js +4 -2
  119. package/dist/core/session-manager-storage.js.map +1 -1
  120. package/dist/core/slash-commands.js +1 -1
  121. package/dist/core/slash-commands.js.map +1 -1
  122. package/dist/core/workflow-stage-admission.d.ts +6 -1
  123. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  124. package/dist/core/workflow-stage-admission.js +8 -9
  125. package/dist/core/workflow-stage-admission.js.map +1 -1
  126. package/dist/index.d.ts +0 -1
  127. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/footer.js +1 -1
  129. package/dist/modes/interactive/components/footer.js.map +1 -1
  130. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  131. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  132. package/dist/modes/interactive/interactive-startup.js +1 -1
  133. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  134. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  135. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  136. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  137. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  138. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  139. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  140. package/dist/modes/rpc/rpc-client-api.js +33 -5
  141. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  142. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  144. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  145. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  146. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-types.js.map +1 -1
  148. package/docs/intercom.md +15 -6
  149. package/docs/packages.md +2 -2
  150. package/docs/providers.md +8 -3
  151. package/docs/quickstart.md +3 -2
  152. package/docs/rpc.md +21 -4
  153. package/docs/settings.md +6 -4
  154. package/docs/skills.md +4 -0
  155. package/docs/subagents.md +1 -1
  156. package/docs/usage.md +1 -1
  157. package/docs/workflows.md +112 -239
  158. package/npm-shrinkwrap.json +32 -32
  159. package/package.json +9 -13
  160. package/dist/builtin/workflows/ambient.d.ts +0 -61
  161. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  162. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  163. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  164. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  165. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  166. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  167. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  168. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  169. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  170. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  171. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  172. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  173. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  174. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  175. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  176. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  177. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  178. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -211
  179. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  180. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  181. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  182. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  183. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  184. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  185. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  186. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  187. package/dist/builtin/workflows/builtin/index.ts +0 -16
  188. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  189. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  190. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  191. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  192. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  193. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  194. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  195. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  196. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  197. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  198. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  199. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  200. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  201. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  202. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  203. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -100
  204. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -455
  205. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  206. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  207. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  208. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  209. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  210. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  211. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  212. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  213. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  214. package/dist/builtin/workflows/builtin/verification-usage.ts +0 -44
@@ -6,9 +6,16 @@ import { writeFileSync, unlinkSync, mkdirSync } from "fs";
6
6
  import { randomUUID } from "crypto";
7
7
  import { writeMessage, createMessageReader } from "./framing.js";
8
8
  import { getBrokerPidPath, getBrokerSocketPath, getIntercomDirPath } from "./paths.js";
9
- import type { SessionInfo, BrokerMessage, SupervisorRegistration } from "../types.js";
9
+ import type { SessionInfo, Message, BrokerMessage, SupervisorRegistration } from "../types.js";
10
10
  import { DeliveredMessageCache } from "./delivered-message-cache.js";
11
- import { handleBrokerSend, type BrokerConnectedSession } from "./send-handler.js";
11
+ import { isMessage } from "./client-message-validation.js";
12
+ import { buildMessageSendSignature } from "./send-signature.js";
13
+ import {
14
+ handleBrokerSend,
15
+ parsePendingStageTarget,
16
+ type BrokerConnectedSession,
17
+ type PendingStageRoute,
18
+ } from "./send-handler.js";
12
19
  import { SupervisorChannelCache } from "./supervisor-channel.js";
13
20
  import { normalizeGroup } from "../group.js";
14
21
  import { handleBrokerPresence } from "./presence-handler.js";
@@ -18,6 +25,49 @@ const INTERCOM_DIR = getIntercomDirPath();
18
25
  const SOCKET_PATH = getBrokerSocketPath();
19
26
  const PID_PATH = getBrokerPidPath();
20
27
 
28
+ const PENDING_STAGE_MESSAGE_TIMEOUT_MS = 10_000;
29
+
30
+ interface PendingStageRouteRegistration {
31
+ readonly sessionId: string;
32
+ readonly group: string;
33
+ readonly capability: string;
34
+ }
35
+
36
+ interface PendingStageAcknowledgment {
37
+ readonly ownerSessionId: string;
38
+ readonly senderSocket: net.Socket;
39
+ readonly messageId: string;
40
+ readonly attemptId?: string;
41
+ readonly runId: string;
42
+ readonly stageKey: string;
43
+ readonly senderSessionId: string;
44
+ readonly sender: SessionInfo;
45
+ readonly message: Message;
46
+ readonly liveTargetId?: string;
47
+ readonly signature?: string;
48
+ readonly timeout: NodeJS.Timeout;
49
+ }
50
+
51
+ interface PendingStageNotificationAcknowledgment {
52
+ readonly ownerSessionId: string;
53
+ readonly recipientSessionId: string;
54
+ readonly senderSocket: net.Socket;
55
+ readonly messageId: string;
56
+ readonly attemptId?: string;
57
+ readonly signature: string;
58
+ readonly timeout: NodeJS.Timeout;
59
+ }
60
+
61
+ interface LiveWorkflowStageRouteRegistration {
62
+ readonly sessionId: string;
63
+ readonly capability: string;
64
+ }
65
+
66
+ interface LiveWorkflowStageRouteActivation {
67
+ readonly sessionId: string;
68
+ readonly pendingRequestIds: Set<string>;
69
+ }
70
+
21
71
  type ConnectedSession = BrokerConnectedSession;
22
72
 
23
73
  function isSessionRegistration(value: unknown): value is Omit<SessionInfo, "id"> {
@@ -62,6 +112,11 @@ class IntercomBroker {
62
112
  private deliveredMessages = new DeliveredMessageCache();
63
113
  private supervisorChannel = new SupervisorChannelCache();
64
114
  private pendingQuestions = new PendingQuestionIndex();
115
+ private pendingStageRoutes = new Map<string, PendingStageRouteRegistration>();
116
+ private pendingStageAcknowledgments = new Map<string, PendingStageAcknowledgment>();
117
+ private pendingStageNotificationAcknowledgments = new Map<string, PendingStageNotificationAcknowledgment>();
118
+ private liveWorkflowStageRoutes = new Map<string, LiveWorkflowStageRouteRegistration>();
119
+ private liveWorkflowStageRouteActivations = new Map<string, LiveWorkflowStageRouteActivation>();
65
120
 
66
121
  constructor() {
67
122
  mkdirSync(INTERCOM_DIR, { recursive: true });
@@ -88,6 +143,7 @@ class IntercomBroker {
88
143
  let sessionId: string | null = null;
89
144
 
90
145
  const reader = createMessageReader((msg) => {
146
+ if (socket.destroyed || socket.writableEnded) return;
91
147
  this.handleMessage(socket, msg, sessionId, (id) => {
92
148
  sessionId = id;
93
149
  });
@@ -120,6 +176,375 @@ class IntercomBroker {
120
176
  }
121
177
  }, 5000);
122
178
  }
179
+ private resolveLiveWorkflowStage = (target: string): ConnectedSession | undefined => {
180
+ const registration = this.liveWorkflowStageRoutes.get(target);
181
+ if (registration === undefined) return undefined;
182
+ const parsedTarget = parsePendingStageTarget(target);
183
+ const currentOwner = parsedTarget === undefined ? undefined : this.pendingStageRoutes.get(parsedTarget.runId);
184
+ if (currentOwner === undefined) return undefined;
185
+ if (currentOwner.capability !== registration.capability) {
186
+ this.liveWorkflowStageRoutes.delete(target);
187
+ return undefined;
188
+ }
189
+ const session = this.sessions.get(registration.sessionId);
190
+ if (session !== undefined) return session;
191
+ this.liveWorkflowStageRoutes.delete(target);
192
+ return undefined;
193
+ };
194
+
195
+ private acknowledgeLiveWorkflowStageRoute(requestId: string): void {
196
+ const activation = this.liveWorkflowStageRouteActivations.get(requestId);
197
+ if (activation === undefined || activation.pendingRequestIds.size > 0) return;
198
+ this.liveWorkflowStageRouteActivations.delete(requestId);
199
+ const stage = this.sessions.get(activation.sessionId);
200
+ if (stage !== undefined) writeMessage(stage.socket, { type: "live_workflow_stage_route_registered", requestId });
201
+ }
202
+
203
+ private releasePendingStageAcknowledgment(requestId: string): void {
204
+ for (const [activationId, activation] of this.liveWorkflowStageRouteActivations) {
205
+ if (!activation.pendingRequestIds.delete(requestId)) continue;
206
+ this.acknowledgeLiveWorkflowStageRoute(activationId);
207
+ }
208
+ }
209
+
210
+ private registerLiveWorkflowStageRoute(
211
+ currentId: string,
212
+ requestId: string,
213
+ runId: string,
214
+ stageKeys: readonly string[],
215
+ capability: string,
216
+ ): boolean {
217
+ const uniqueStageKeys = [...new Set(stageKeys)];
218
+ const targets = uniqueStageKeys.map((stageKey) => `${runId}:${stageKey}`);
219
+ for (const target of targets) {
220
+ const existing = this.liveWorkflowStageRoutes.get(target);
221
+ if (
222
+ existing !== undefined &&
223
+ existing.sessionId !== currentId &&
224
+ this.sessions.has(existing.sessionId)
225
+ ) {
226
+ return false;
227
+ }
228
+ }
229
+ for (const target of targets) {
230
+ this.liveWorkflowStageRoutes.set(target, { sessionId: currentId, capability });
231
+ }
232
+ const pendingRequestIds = new Set(
233
+ [...this.pendingStageAcknowledgments]
234
+ .filter(([, pending]) => pending.runId === runId && uniqueStageKeys.includes(pending.stageKey))
235
+ .map(([pendingRequestId]) => pendingRequestId),
236
+ );
237
+ this.liveWorkflowStageRouteActivations.set(requestId, { sessionId: currentId, pendingRequestIds });
238
+ this.acknowledgeLiveWorkflowStageRoute(requestId);
239
+ return true;
240
+ }
241
+
242
+ private routePendingStage = (route: PendingStageRoute): boolean => {
243
+ const ownerRegistration = this.pendingStageRoutes.get(route.runId);
244
+ if (ownerRegistration === undefined) return false;
245
+ const owner = this.sessions.get(ownerRegistration.sessionId);
246
+ if (owner === undefined) {
247
+ this.pendingStageRoutes.delete(route.runId);
248
+ return false;
249
+ }
250
+ if (route.liveTargetId === undefined && normalizeGroup(route.from.info.group) !== normalizeGroup(ownerRegistration.group)) {
251
+ writeMessage(route.socket, {
252
+ type: "delivery_failed",
253
+ messageId: route.message.id,
254
+ ...(route.attemptId ? { attemptId: route.attemptId } : {}),
255
+ reason: "Target workflow run is in a different intercom group",
256
+ });
257
+ return true;
258
+ }
259
+ const requestId = randomUUID();
260
+ const timeout = setTimeout(() => {
261
+ const pending = this.pendingStageAcknowledgments.get(requestId);
262
+ if (pending === undefined) return;
263
+ this.pendingStageAcknowledgments.delete(requestId);
264
+ this.releasePendingStageAcknowledgment(requestId);
265
+ writeMessage(pending.senderSocket, {
266
+ type: "delivery_failed",
267
+ messageId: pending.messageId,
268
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
269
+ reason: "Pending-stage route did not acknowledge the message",
270
+ });
271
+ }, PENDING_STAGE_MESSAGE_TIMEOUT_MS);
272
+ this.pendingStageAcknowledgments.set(requestId, {
273
+ ownerSessionId: ownerRegistration.sessionId,
274
+ senderSocket: route.socket,
275
+ messageId: route.message.id,
276
+ ...(route.attemptId ? { attemptId: route.attemptId } : {}),
277
+ runId: route.runId,
278
+ stageKey: route.stageKey,
279
+ senderSessionId: route.from.info.id,
280
+ sender: { ...route.from.info },
281
+ message: route.message,
282
+ ...(route.liveTargetId === undefined ? {} : { liveTargetId: route.liveTargetId }),
283
+ ...(route.signature === undefined ? {} : { signature: route.signature }),
284
+ timeout,
285
+ });
286
+ writeMessage(owner.socket, {
287
+ type: "pending_stage_message",
288
+ requestId,
289
+ from: route.from.info,
290
+ ...(route.from.registrationName === undefined ? {} : { senderRegistrationName: route.from.registrationName }),
291
+ ...(route.from.registrationReturnAddress === undefined
292
+ ? {}
293
+ : { senderReturnAddress: route.from.registrationReturnAddress }),
294
+ runId: route.runId,
295
+ stageKey: route.stageKey,
296
+ message: route.message,
297
+ ...(route.liveTargetId === undefined ? {} : { live: true }),
298
+ });
299
+ return true;
300
+ };
301
+
302
+ private settlePendingStageMessageRequest(
303
+ currentId: string,
304
+ requestId: string,
305
+ outcome: "queued" | "delivered" | "forward" | "refused",
306
+ position: number | undefined,
307
+ reason: string | undefined,
308
+ reasonCode: "message_id_conflict" | undefined,
309
+ ): void {
310
+ const pending = this.pendingStageAcknowledgments.get(requestId);
311
+ if (pending === undefined || pending.ownerSessionId !== currentId) return;
312
+ clearTimeout(pending.timeout);
313
+ this.pendingStageAcknowledgments.delete(requestId);
314
+ this.releasePendingStageAcknowledgment(requestId);
315
+ if (outcome === "forward") {
316
+ this.forwardValidatedLiveStageMessage(pending);
317
+ return;
318
+ }
319
+ if (outcome === "delivered") {
320
+ writeMessage(pending.senderSocket, {
321
+ type: "delivered",
322
+ messageId: pending.messageId,
323
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
324
+ });
325
+ return;
326
+ }
327
+ if (outcome === "queued" && typeof position === "number" && position > 0) {
328
+ writeMessage(pending.senderSocket, {
329
+ type: "queued",
330
+ messageId: pending.messageId,
331
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
332
+ runId: pending.runId,
333
+ stageKey: pending.stageKey,
334
+ position,
335
+ });
336
+ return;
337
+ }
338
+ writeMessage(pending.senderSocket, {
339
+ type: "delivery_failed",
340
+ messageId: pending.messageId,
341
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
342
+ reason: reason ?? "Pending-stage delivery was refused",
343
+ ...(reasonCode === undefined ? {} : { reasonCode }),
344
+ });
345
+ }
346
+
347
+ private forwardValidatedLiveStageMessage(pending: PendingStageAcknowledgment): void {
348
+ const from = this.sessions.get(pending.senderSessionId);
349
+ const target = this.resolveLiveWorkflowStage(`${pending.runId}:${pending.stageKey}`);
350
+ if (
351
+ from === undefined ||
352
+ target === undefined ||
353
+ target.info.id !== pending.liveTargetId ||
354
+ pending.signature === undefined
355
+ ) {
356
+ writeMessage(pending.senderSocket, {
357
+ type: "delivery_failed",
358
+ messageId: pending.messageId,
359
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
360
+ reason: "Session not found",
361
+ });
362
+ return;
363
+ }
364
+ const deliveredMatch = this.deliveredMessages.lookup(pending.messageId, pending.signature);
365
+ if (deliveredMatch === "conflict") {
366
+ writeMessage(pending.senderSocket, {
367
+ type: "delivery_failed",
368
+ messageId: pending.messageId,
369
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
370
+ reason: `Intercom message ID '${pending.messageId}' was already delivered with a different target or payload`,
371
+ reasonCode: "message_id_conflict",
372
+ });
373
+ return;
374
+ }
375
+ if (deliveredMatch === "miss") {
376
+ writeMessage(target.socket, { type: "message", from: pending.sender, message: pending.message });
377
+ this.deliveredMessages.record(pending.messageId, pending.signature);
378
+ if (pending.message.expectsReply === true) {
379
+ this.pendingQuestions.record(from.info.id, target.info.id, pending.messageId);
380
+ }
381
+ if (pending.message.replyTo !== undefined) {
382
+ this.pendingQuestions.clearReply(from.info.id, target.info.id, pending.message.replyTo);
383
+ }
384
+ }
385
+ writeMessage(pending.senderSocket, {
386
+ type: "delivered",
387
+ messageId: pending.messageId,
388
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
389
+ });
390
+ }
391
+
392
+
393
+ private failPendingStageNotification(
394
+ socket: net.Socket,
395
+ messageId: string,
396
+ attemptId: string | undefined,
397
+ reason: string,
398
+ reasonCode?: "message_id_conflict",
399
+ ): void {
400
+ writeMessage(socket, {
401
+ type: "delivery_failed",
402
+ messageId,
403
+ ...(attemptId === undefined ? {} : { attemptId }),
404
+ reason,
405
+ ...(reasonCode === undefined ? {} : { reasonCode }),
406
+ });
407
+ }
408
+
409
+ private handlePendingStageNotificationSend(
410
+ socket: net.Socket,
411
+ clientMessage: Record<string, unknown>,
412
+ currentId: string,
413
+ ): void {
414
+ const message = clientMessage.message;
415
+ const messageId = typeof message === "object" && message !== null && typeof (message as { id?: unknown }).id === "string"
416
+ ? (message as { id: string }).id
417
+ : "unknown";
418
+ const attemptId = typeof clientMessage.attemptId === "string" ? clientMessage.attemptId : undefined;
419
+ if (
420
+ typeof clientMessage.runId !== "string" ||
421
+ typeof clientMessage.capability !== "string" ||
422
+ typeof clientMessage.to !== "string" ||
423
+ (clientMessage.senderRegistrationName !== undefined && typeof clientMessage.senderRegistrationName !== "string") ||
424
+ (clientMessage.senderReturnAddress !== undefined &&
425
+ (typeof clientMessage.senderReturnAddress !== "string" || clientMessage.senderReturnAddress.length === 0)) ||
426
+ (clientMessage.attemptId !== undefined && typeof clientMessage.attemptId !== "string") ||
427
+ !isMessage(message)
428
+ ) {
429
+ this.failPendingStageNotification(socket, messageId, attemptId, "Invalid pending-stage notification format");
430
+ return;
431
+ }
432
+ const ownerRegistration = this.pendingStageRoutes.get(clientMessage.runId);
433
+ const owner = this.sessions.get(currentId);
434
+ const routeGroup = normalizeGroup(ownerRegistration?.group);
435
+ if (
436
+ ownerRegistration === undefined ||
437
+ owner === undefined ||
438
+ ownerRegistration.sessionId !== currentId ||
439
+ ownerRegistration.capability !== clientMessage.capability ||
440
+ normalizeGroup(owner.registrationGroup) !== routeGroup
441
+ ) {
442
+ this.failPendingStageNotification(socket, message.id, attemptId, "Pending-stage notification is not authorized");
443
+ return;
444
+ }
445
+ const logicalTarget = [
446
+ clientMessage.runId,
447
+ clientMessage.to,
448
+ clientMessage.senderReturnAddress ?? "",
449
+ clientMessage.senderRegistrationName ?? "",
450
+ routeGroup,
451
+ ].join(":");
452
+ const signature = buildMessageSendSignature(logicalTarget, message, clientMessage.capability);
453
+ const deliveredMatch = this.deliveredMessages.lookup(message.id, signature);
454
+ if (deliveredMatch === "match") {
455
+ writeMessage(socket, { type: "delivered", messageId: message.id, ...(attemptId === undefined ? {} : { attemptId }) });
456
+ return;
457
+ }
458
+ if (deliveredMatch === "conflict") {
459
+ this.failPendingStageNotification(
460
+ socket,
461
+ message.id,
462
+ attemptId,
463
+ `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
464
+ "message_id_conflict",
465
+ );
466
+ return;
467
+ }
468
+
469
+ let target = this.sessions.get(clientMessage.to);
470
+ if (target === undefined && clientMessage.senderReturnAddress !== undefined) {
471
+ const matches = [...this.sessions.values()].filter(
472
+ (session) =>
473
+ session.registrationReturnAddress === clientMessage.senderReturnAddress &&
474
+ normalizeGroup(session.registrationGroup) === routeGroup,
475
+ );
476
+ if (matches.length === 1) target = matches[0];
477
+ } else if (target === undefined && clientMessage.senderRegistrationName !== undefined) {
478
+ const alias = clientMessage.senderRegistrationName.trim().toLowerCase();
479
+ if (alias.length > 0) {
480
+ // Legacy durable records can only use the immutable registration name/group fallback.
481
+ const matches = [...this.sessions.values()].filter(
482
+ (session) =>
483
+ session.registrationName?.toLowerCase() === alias &&
484
+ normalizeGroup(session.registrationGroup) === routeGroup,
485
+ );
486
+ if (matches.length === 1) target = matches[0];
487
+ }
488
+ }
489
+ if (
490
+ target === undefined ||
491
+ target.info.id === currentId ||
492
+ normalizeGroup(target.registrationGroup) !== routeGroup ||
493
+ normalizeGroup(target.info.group) !== routeGroup
494
+ ) {
495
+ this.failPendingStageNotification(socket, message.id, attemptId, "Session not found");
496
+ return;
497
+ }
498
+
499
+ const requestId = randomUUID();
500
+ const timeout = setTimeout(() => {
501
+ const pending = this.pendingStageNotificationAcknowledgments.get(requestId);
502
+ if (pending === undefined) return;
503
+ this.pendingStageNotificationAcknowledgments.delete(requestId);
504
+ this.failPendingStageNotification(
505
+ pending.senderSocket,
506
+ pending.messageId,
507
+ pending.attemptId,
508
+ "Recipient did not acknowledge the pending-stage notification",
509
+ );
510
+ }, PENDING_STAGE_MESSAGE_TIMEOUT_MS);
511
+ this.pendingStageNotificationAcknowledgments.set(requestId, {
512
+ ownerSessionId: currentId,
513
+ recipientSessionId: target.info.id,
514
+ senderSocket: socket,
515
+ messageId: message.id,
516
+ ...(attemptId === undefined ? {} : { attemptId }),
517
+ signature,
518
+ timeout,
519
+ });
520
+ writeMessage(target.socket, { type: "pending_stage_notification", requestId, from: owner.info, message });
521
+ }
522
+
523
+ private settlePendingStageNotification(
524
+ currentId: string,
525
+ requestId: string,
526
+ delivered: boolean,
527
+ ): void {
528
+ const pending = this.pendingStageNotificationAcknowledgments.get(requestId);
529
+ if (pending === undefined || pending.recipientSessionId !== currentId) return;
530
+ clearTimeout(pending.timeout);
531
+ this.pendingStageNotificationAcknowledgments.delete(requestId);
532
+ if (!delivered) {
533
+ this.failPendingStageNotification(
534
+ pending.senderSocket,
535
+ pending.messageId,
536
+ pending.attemptId,
537
+ "Recipient did not admit the pending-stage notification",
538
+ );
539
+ return;
540
+ }
541
+ this.deliveredMessages.record(pending.messageId, pending.signature);
542
+ writeMessage(pending.senderSocket, {
543
+ type: "delivered",
544
+ messageId: pending.messageId,
545
+ ...(pending.attemptId === undefined ? {} : { attemptId: pending.attemptId }),
546
+ });
547
+ }
123
548
 
124
549
  private handleMessage(
125
550
  socket: net.Socket,
@@ -142,10 +567,14 @@ class IntercomBroker {
142
567
  if (!isSessionRegistration(clientMessage.session)) {
143
568
  throw new Error("Invalid register message");
144
569
  }
145
- if (clientMessage.supervisorOwnerToken !== undefined
146
- && (typeof clientMessage.supervisorOwnerToken !== "string" || !clientMessage.supervisorOwnerToken)) {
147
- throw new Error("Invalid supervisor owner token");
148
- }
570
+ if (clientMessage.returnAddress !== undefined &&
571
+ (typeof clientMessage.returnAddress !== "string" || clientMessage.returnAddress.length === 0)) {
572
+ throw new Error("Invalid return address");
573
+ }
574
+ if (clientMessage.supervisorOwnerToken !== undefined
575
+ && (typeof clientMessage.supervisorOwnerToken !== "string" || !clientMessage.supervisorOwnerToken)) {
576
+ throw new Error("Invalid supervisor owner token");
577
+ }
149
578
 
150
579
  if (currentId) {
151
580
  throw new Error("Received duplicate register message");
@@ -175,6 +604,13 @@ class IntercomBroker {
175
604
  this.sessions.set(id, {
176
605
  socket,
177
606
  info,
607
+ registrationGroup: info.group,
608
+ ...(typeof info.name === "string" && info.name.trim().length > 0
609
+ ? { registrationName: info.name.trim() }
610
+ : {}),
611
+ ...(typeof clientMessage.returnAddress === "string"
612
+ ? { registrationReturnAddress: clientMessage.returnAddress }
613
+ : {}),
178
614
  ...(supervisorId ? { supervisorId } : {}),
179
615
  ...(typeof clientMessage.supervisorOwnerToken === "string"
180
616
  ? { supervisorOwnerToken: clientMessage.supervisorOwnerToken }
@@ -243,21 +679,142 @@ class IntercomBroker {
243
679
  break;
244
680
  }
245
681
 
246
- case "send":
247
- case "supervisor_send": {
248
- handleBrokerSend(
249
- socket,
250
- clientMessage,
682
+ case "register_pending_stage_route": {
683
+ if (
684
+ typeof clientMessage.runId !== "string" ||
685
+ !clientMessage.runId ||
686
+ typeof clientMessage.group !== "string" ||
687
+ typeof clientMessage.capability !== "string" ||
688
+ !clientMessage.capability
689
+ ) {
690
+ throw new Error("Invalid pending-stage route registration");
691
+ }
692
+ const owner = this.sessions.get(currentId);
693
+ const existing = this.pendingStageRoutes.get(clientMessage.runId);
694
+ const ownerGroup = normalizeGroup(owner?.registrationGroup);
695
+ const activeExisting = existing !== undefined && this.sessions.has(existing.sessionId) ? existing : undefined;
696
+ if (
697
+ owner === undefined ||
698
+ normalizeGroup(clientMessage.group) !== ownerGroup ||
699
+ (activeExisting !== undefined &&
700
+ (activeExisting.capability !== clientMessage.capability || activeExisting.group !== ownerGroup))
701
+ ) {
702
+ writeMessage(socket, { type: "registration_failed", reason: "Pending-stage route is not authorized" });
703
+ socket.end();
704
+ return;
705
+ }
706
+ if (activeExisting !== undefined && activeExisting.sessionId !== currentId) {
707
+ // A stage replays the process-shared owner announcement before
708
+ // registering its live aliases. Authenticate it without replacing
709
+ // the workflow owner that handles pending delivery.
710
+ break;
711
+ }
712
+ this.pendingStageRoutes.set(clientMessage.runId, {
713
+ sessionId: currentId,
714
+ group: ownerGroup,
715
+ capability: clientMessage.capability,
716
+ });
717
+ break;
718
+ }
719
+
720
+ case "register_live_workflow_stage_route": {
721
+ if (
722
+ typeof clientMessage.requestId !== "string" ||
723
+ typeof clientMessage.runId !== "string" ||
724
+ typeof clientMessage.capability !== "string" ||
725
+ !clientMessage.capability ||
726
+ !Array.isArray(clientMessage.stageKeys) ||
727
+ clientMessage.stageKeys.length < 1 ||
728
+ clientMessage.stageKeys.length > 2 ||
729
+ !clientMessage.stageKeys.every(
730
+ (stageKey) =>
731
+ typeof stageKey === "string" &&
732
+ stageKey.length > 0 &&
733
+ parsePendingStageTarget(`${clientMessage.runId}:${stageKey}`) !== undefined,
734
+ )
735
+ ) {
736
+ throw new Error("Invalid live workflow-stage route registration");
737
+ }
738
+ const ownerRegistration = this.pendingStageRoutes.get(clientMessage.runId);
739
+ const registeringSession = this.sessions.get(currentId);
740
+ if (
741
+ ownerRegistration === undefined ||
742
+ registeringSession === undefined ||
743
+ ownerRegistration.capability !== clientMessage.capability ||
744
+ normalizeGroup(ownerRegistration.group) !==
745
+ normalizeGroup(registeringSession.registrationGroup ?? registeringSession.info.group) ||
746
+ !this.registerLiveWorkflowStageRoute(
747
+ currentId,
748
+ clientMessage.requestId,
749
+ clientMessage.runId,
750
+ clientMessage.stageKeys,
751
+ clientMessage.capability,
752
+ )
753
+ ) {
754
+ writeMessage(socket, {
755
+ type: "registration_failed",
756
+ reason: "Live workflow-stage route is owned by another active session",
757
+ });
758
+ socket.end();
759
+ return;
760
+ }
761
+ break;
762
+ }
763
+
764
+ case "pending_stage_message_result": {
765
+ if (
766
+ typeof clientMessage.requestId !== "string" ||
767
+ (clientMessage.outcome !== "queued" &&
768
+ clientMessage.outcome !== "delivered" &&
769
+ clientMessage.outcome !== "forward" &&
770
+ clientMessage.outcome !== "refused") ||
771
+ (clientMessage.position !== undefined && typeof clientMessage.position !== "number") ||
772
+ (clientMessage.reason !== undefined && typeof clientMessage.reason !== "string") ||
773
+ (clientMessage.reasonCode !== undefined && clientMessage.reasonCode !== "message_id_conflict")
774
+ ) {
775
+ throw new Error("Invalid pending-stage message result");
776
+ }
777
+ this.settlePendingStageMessageRequest(
251
778
  currentId,
252
- this.sessions,
253
- this.deliveredMessages,
254
- writeMessage,
255
- this.supervisorChannel,
256
- this.pendingQuestions,
779
+ clientMessage.requestId,
780
+ clientMessage.outcome,
781
+ clientMessage.position,
782
+ clientMessage.reason,
783
+ clientMessage.reasonCode,
257
784
  );
258
785
  break;
259
786
  }
260
787
 
788
+ case "send":
789
+ case "supervisor_send": {
790
+ handleBrokerSend(
791
+ socket,
792
+ clientMessage,
793
+ currentId,
794
+ this.sessions,
795
+ this.deliveredMessages,
796
+ writeMessage,
797
+ this.supervisorChannel,
798
+ this.pendingQuestions,
799
+ this.routePendingStage,
800
+ this.resolveLiveWorkflowStage,
801
+ );
802
+ break;
803
+ }
804
+
805
+ case "send_pending_stage_notification": {
806
+ this.handlePendingStageNotificationSend(socket, clientMessage, currentId);
807
+ break;
808
+ }
809
+
810
+ case "pending_stage_notification_result": {
811
+ if (typeof clientMessage.requestId !== "string" || typeof clientMessage.delivered !== "boolean") {
812
+ throw new Error("Invalid pending-stage notification result");
813
+ }
814
+ this.settlePendingStageNotification(currentId, clientMessage.requestId, clientMessage.delivered);
815
+ break;
816
+ }
817
+
261
818
  case "presence": {
262
819
  handleBrokerPresence(
263
820
  socket,
@@ -290,10 +847,42 @@ class IntercomBroker {
290
847
  });
291
848
  }
292
849
 
293
- this.sessions.delete(sessionId);
294
- this.broadcastToGroup({ type: "session_left", sessionId }, departed.info.group, sessionId);
295
- }
296
-
850
+ for (const [runId, owner] of this.pendingStageRoutes) {
851
+ if (owner.sessionId === sessionId) this.pendingStageRoutes.delete(runId);
852
+ }
853
+ for (const [requestId, pending] of this.pendingStageAcknowledgments) {
854
+ if (pending.ownerSessionId !== sessionId) continue;
855
+ clearTimeout(pending.timeout);
856
+ this.pendingStageAcknowledgments.delete(requestId);
857
+ this.releasePendingStageAcknowledgment(requestId);
858
+ writeMessage(pending.senderSocket, {
859
+ type: "delivery_failed",
860
+ messageId: pending.messageId,
861
+ ...(pending.attemptId ? { attemptId: pending.attemptId } : {}),
862
+ reason: "Pending-stage route disconnected before acknowledging the message",
863
+ });
864
+ }
865
+ for (const [requestId, pending] of this.pendingStageNotificationAcknowledgments) {
866
+ if (pending.ownerSessionId !== sessionId && pending.recipientSessionId !== sessionId) continue;
867
+ clearTimeout(pending.timeout);
868
+ this.pendingStageNotificationAcknowledgments.delete(requestId);
869
+ if (pending.ownerSessionId === sessionId) continue;
870
+ this.failPendingStageNotification(
871
+ pending.senderSocket,
872
+ pending.messageId,
873
+ pending.attemptId,
874
+ "Recipient disconnected before acknowledging the pending-stage notification",
875
+ );
876
+ }
877
+ for (const [target, registration] of this.liveWorkflowStageRoutes) {
878
+ if (registration.sessionId === sessionId) this.liveWorkflowStageRoutes.delete(target);
879
+ }
880
+ for (const [requestId, activation] of this.liveWorkflowStageRouteActivations) {
881
+ if (activation.sessionId === sessionId) this.liveWorkflowStageRouteActivations.delete(requestId);
882
+ }
883
+ this.sessions.delete(sessionId);
884
+ this.broadcastToGroup({ type: "session_left", sessionId }, departed.info.group, sessionId);
885
+ }
297
886
  /** Deliver a broadcast only to sessions in the given (normalized) group. */
298
887
  private broadcastToGroup(msg: BrokerMessage, group: string | undefined, exclude?: string): void {
299
888
  const target = normalizeGroup(group);
@@ -311,6 +900,12 @@ class IntercomBroker {
311
900
  session.socket.end();
312
901
  }
313
902
  this.sessions.clear();
903
+ this.pendingStageRoutes.clear();
904
+ this.pendingStageAcknowledgments.clear();
905
+ this.liveWorkflowStageRoutes.clear();
906
+ this.liveWorkflowStageRouteActivations.clear();
907
+ for (const pending of this.pendingStageNotificationAcknowledgments.values()) clearTimeout(pending.timeout);
908
+ this.pendingStageNotificationAcknowledgments.clear();
314
909
  if (process.platform !== "win32") {
315
910
  try {
316
911
  unlinkSync(SOCKET_PATH);
@@ -318,8 +913,8 @@ class IntercomBroker {
318
913
  // The socket may already be gone if shutdown started after a disconnect.
319
914
  }
320
915
  }
321
- try {
322
- unlinkSync(PID_PATH);
916
+ try {
917
+ unlinkSync(PID_PATH);
323
918
  } catch {
324
919
  // The PID file may already be gone if startup never completed.
325
920
  }