@bastani/atomic 0.9.54-alpha.1 → 0.9.55-alpha.1

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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +14 -0
  3. package/dist/builtin/intercom/README.md +2 -1
  4. package/dist/builtin/intercom/broker/announce-on-import.ts +5 -0
  5. package/dist/builtin/intercom/broker/broker.ts +28 -0
  6. package/dist/builtin/intercom/broker/client.ts +48 -3
  7. package/dist/builtin/intercom/broker/paths.ts +9 -0
  8. package/dist/builtin/intercom/broker/socket-writes.ts +5 -0
  9. package/dist/builtin/intercom/broker/spawn.ts +58 -6
  10. package/dist/builtin/intercom/broker/start-announcement.ts +77 -0
  11. package/dist/builtin/intercom/broker/startup-timeout.ts +23 -0
  12. package/dist/builtin/intercom/index.bundle.mjs +184 -73
  13. package/dist/builtin/intercom/package.json +1 -1
  14. package/dist/builtin/intercom/recoverable-disconnect.ts +38 -2
  15. package/dist/builtin/mcp/package.json +1 -1
  16. package/dist/builtin/subagents/package.json +1 -1
  17. package/dist/builtin/web-access/package.json +1 -1
  18. package/dist/builtin/workflows/CHANGELOG.md +26 -0
  19. package/dist/builtin/workflows/package.json +1 -1
  20. package/dist/builtin/workflows/src/durable/journal/resolution.d.ts +7 -0
  21. package/dist/builtin/workflows/src/extension/index.bundle.mjs +8458 -7874
  22. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +687 -111
  23. package/dist/builtin/workflows/src/index.js +834 -264
  24. package/dist/builtin/workflows/src/local-worker-entry.mjs +687 -111
  25. package/dist/builtin/workflows/src/workers/public-options.d.ts +8 -0
  26. package/dist/core/extension-startup-failure.d.ts.map +1 -1
  27. package/dist/core/extension-startup-failure.js +1 -0
  28. package/dist/core/extension-startup-failure.js.map +1 -1
  29. package/dist/core/storage/session/jsonl-session-store.d.ts +27 -0
  30. package/dist/core/storage/session/jsonl-session-store.d.ts.map +1 -1
  31. package/dist/core/storage/session/jsonl-session-store.js +99 -7
  32. package/dist/core/storage/session/jsonl-session-store.js.map +1 -1
  33. package/docs/settings.md +2 -2
  34. package/docs/storage.md +2 -0
  35. package/docs/workflows/api-reference.md +3 -3
  36. package/docs/workflows/operations.md +3 -3
  37. package/npm-shrinkwrap.json +32 -32
  38. package/package.json +3 -3
@@ -153,6 +153,8 @@ function isRecoverableIntercomDisconnect(error) {
153
153
  return true;
154
154
  if (current instanceof IntercomBrokerTimeoutError)
155
155
  return true;
156
+ if (current instanceof IntercomBrokerRetiringError)
157
+ return true;
156
158
  if (current instanceof IntercomLiveRouteRefusedError)
157
159
  return current.transient;
158
160
  current = current.cause;
@@ -170,7 +172,18 @@ function isIntercomBrokerTimeout(error) {
170
172
  }
171
173
  return false;
172
174
  }
173
- var RECOVERABLE_DISCONNECT_MESSAGE = "Client disconnected", MAX_CAUSE_DEPTH = 8, IntercomClientDisconnectedError, IntercomBrokerTimeoutError;
175
+ function isIntercomBrokerRetiring(error) {
176
+ let current = error;
177
+ for (let depth = 0;depth <= MAX_CAUSE_DEPTH; depth += 1) {
178
+ if (!(current instanceof Error))
179
+ return false;
180
+ if (current instanceof IntercomBrokerRetiringError)
181
+ return true;
182
+ current = current.cause;
183
+ }
184
+ return false;
185
+ }
186
+ var RECOVERABLE_DISCONNECT_MESSAGE = "Client disconnected", MAX_CAUSE_DEPTH = 8, IntercomClientDisconnectedError, IntercomBrokerTimeoutError, BROKER_RETIRING_CODE = "broker_retiring", BROKER_RETIRING_REASON = "Intercom broker is retiring; connect to its replacement", IntercomBrokerRetiringError;
174
187
  var init_recoverable_disconnect = __esm(() => {
175
188
  init_live_route_refusal();
176
189
  IntercomClientDisconnectedError = class IntercomClientDisconnectedError extends Error {
@@ -187,6 +200,13 @@ var init_recoverable_disconnect = __esm(() => {
187
200
  this.name = "IntercomBrokerTimeoutError";
188
201
  }
189
202
  };
203
+ IntercomBrokerRetiringError = class IntercomBrokerRetiringError extends Error {
204
+ intercomRecoverableDisconnect = true;
205
+ constructor(message = BROKER_RETIRING_REASON, options) {
206
+ super(message, options);
207
+ this.name = "IntercomBrokerRetiringError";
208
+ }
209
+ };
190
210
  });
191
211
 
192
212
  // dist/builtin/intercom/retry-policy.ts
@@ -208,7 +228,10 @@ function jitteredReconnectDelayMs(attempt, random = Math.random) {
208
228
  const ceiling = reconnectDelayMs(attempt);
209
229
  return Math.round(ceiling / 2 + ceiling / 2 * random());
210
230
  }
211
- var BACKOFF_MS;
231
+ function brokerRetiringRetryDelayMs(attempt) {
232
+ return Math.min(100 * 2 ** attempt, 1000);
233
+ }
234
+ var BACKOFF_MS, BROKER_RETIRING_RETRY_WINDOW_MS = 1e4;
212
235
  var init_reconnect_backoff = __esm(() => {
213
236
  BACKOFF_MS = [1000, 2000, 5000, 1e4, 30000];
214
237
  });
@@ -702,6 +725,9 @@ function getBrokerSpawnLockPath(agentDir = getAgentDir()) {
702
725
  function getBrokerLogPath(agentDir = getAgentDir()) {
703
726
  return join(getIntercomDirPath(agentDir), "broker.log");
704
727
  }
728
+ function getBrokerStartAnnouncementPath(agentDir = getAgentDir()) {
729
+ return join(getIntercomDirPath(agentDir), "broker.starting");
730
+ }
705
731
  function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir()) {
706
732
  if (platform === "win32") {
707
733
  return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
@@ -940,9 +966,40 @@ var init_group = __esm(() => {
940
966
  RESERVED_RUNTIME_GROUPS = new Set(["true", "auto"]);
941
967
  });
942
968
 
969
+ // dist/builtin/intercom/broker/socket-liveness.ts
970
+ import net from "net";
971
+ function isSocketAnswering(socketPath, timeoutMs = SOCKET_PROBE_TIMEOUT_MS) {
972
+ return new Promise((resolve) => {
973
+ const socket = net.connect(socketPath);
974
+ const finish = (isConnected) => {
975
+ clearTimeout(timeout);
976
+ socket.off("connect", onConnect);
977
+ socket.off("error", onError);
978
+ resolve(isConnected);
979
+ };
980
+ const onConnect = () => {
981
+ socket.on("error", () => {});
982
+ socket.end();
983
+ finish(true);
984
+ };
985
+ const onError = () => {
986
+ socket.destroy();
987
+ finish(false);
988
+ };
989
+ socket.on("connect", onConnect);
990
+ socket.on("error", onError);
991
+ const timeout = setTimeout(() => {
992
+ socket.destroy();
993
+ finish(false);
994
+ }, timeoutMs);
995
+ });
996
+ }
997
+ var SOCKET_PROBE_TIMEOUT_MS = 1000;
998
+ var init_socket_liveness = () => {};
999
+
943
1000
  // dist/builtin/intercom/broker/client.ts
944
1001
  import { EventEmitter } from "events";
945
- import net from "net";
1002
+ import net2 from "net";
946
1003
  import { randomUUID as randomUUID2 } from "crypto";
947
1004
  function connectAborted() {
948
1005
  return new IntercomClientDisconnectedError({ cause: new Error("Disconnected before the broker connection registered") });
@@ -967,6 +1024,19 @@ function isBacklogRefusal(error) {
967
1024
  const code = error.code;
968
1025
  return code === "ECONNREFUSED" || code === "EAGAIN";
969
1026
  }
1027
+ function isLostBrokerSignature(error) {
1028
+ if (error instanceof ConnectionClosedBeforeRegistrationError)
1029
+ return true;
1030
+ const code = error.code;
1031
+ return code === "ENOENT" || code === "ECONNRESET" || code === "EPIPE";
1032
+ }
1033
+ async function classifyRegistrationFailure(error) {
1034
+ if (!isLostBrokerSignature(error) || await isSocketAnswering(BROKER_SOCKET))
1035
+ return error;
1036
+ return new IntercomBrokerRetiringError(`Intercom broker went away before it registered this session: ${error.message}`, {
1037
+ cause: error
1038
+ });
1039
+ }
970
1040
  function toError(error) {
971
1041
  return error instanceof Error ? error : new Error(String(error));
972
1042
  }
@@ -976,15 +1046,22 @@ function isWorkflowFutureStageRosterEntries(value) {
976
1046
  function isWorkflowStageRosterEntries(value) {
977
1047
  return Array.isArray(value) && value.every((entry) => typeof entry === "object" && entry !== null && entry.kind === "workflow-stage" && typeof entry.runId === "string" && typeof entry.stageId === "string" && typeof entry.stageName === "string" && typeof entry.target === "string" && (entry.lifecycle === "pending" || entry.lifecycle === "running") && typeof entry.group === "string" && (entry.sessionId === undefined || typeof entry.sessionId === "string"));
978
1048
  }
979
- var BROKER_SOCKET, BROKER_CONNECT_TIMEOUT_MS = 1e4, PENDING_STAGE_ROUTE_ACK_TIMEOUT_MS = 1e4, GROUP_REQUEST_TIMEOUT_MS = 5000, PRESENCE_ACK_TIMEOUT_MS = 5000, RESTORING_RECIPIENT_REQUEST_TIMEOUT_MS = 5000, BROKER_BACKLOG_RETRIES = 6, BROKER_BACKLOG_RETRY_BASE_MS = 25, HELD_SEND_GRACE_MS = 5000, IntercomPendingStageRouteRefusedError, PENDING_STAGE_ROUTE_REFUSAL_CODES, IntercomClient;
1049
+ var BROKER_SOCKET, BROKER_CONNECT_TIMEOUT_MS = 1e4, PENDING_STAGE_ROUTE_ACK_TIMEOUT_MS = 1e4, GROUP_REQUEST_TIMEOUT_MS = 5000, PRESENCE_ACK_TIMEOUT_MS = 5000, RESTORING_RECIPIENT_REQUEST_TIMEOUT_MS = 5000, BROKER_BACKLOG_RETRIES = 6, BROKER_BACKLOG_RETRY_BASE_MS = 25, ConnectionClosedBeforeRegistrationError, HELD_SEND_GRACE_MS = 5000, IntercomPendingStageRouteRefusedError, PENDING_STAGE_ROUTE_REFUSAL_CODES, IntercomClient;
980
1050
  var init_client = __esm(() => {
981
1051
  init_paths();
982
1052
  init_pending_send_registry();
983
1053
  init_group();
984
1054
  init_recoverable_disconnect();
1055
+ init_socket_liveness();
985
1056
  init_live_route_refusal();
986
1057
  init_retry_policy();
987
1058
  BROKER_SOCKET = getBrokerSocketPath();
1059
+ ConnectionClosedBeforeRegistrationError = class ConnectionClosedBeforeRegistrationError extends Error {
1060
+ constructor() {
1061
+ super("Connection closed before registration");
1062
+ this.name = "ConnectionClosedBeforeRegistrationError";
1063
+ }
1064
+ };
988
1065
  IntercomPendingStageRouteRefusedError = class IntercomPendingStageRouteRefusedError extends Error {
989
1066
  connectionKept;
990
1067
  code;
@@ -1152,8 +1229,14 @@ var init_client = __esm(() => {
1152
1229
  } catch (error) {
1153
1230
  if (attempt.signal.aborted)
1154
1231
  throw connectAborted();
1155
- if (!(error instanceof Error) || !isBacklogRefusal(error) || retry >= BROKER_BACKLOG_RETRIES)
1232
+ if (!(error instanceof Error))
1156
1233
  throw error;
1234
+ if (!isBacklogRefusal(error) || retry >= BROKER_BACKLOG_RETRIES) {
1235
+ const classified = await classifyRegistrationFailure(error);
1236
+ if (attempt.signal.aborted)
1237
+ throw connectAborted();
1238
+ throw classified;
1239
+ }
1157
1240
  const backoff = BROKER_BACKLOG_RETRY_BASE_MS * 2 ** retry;
1158
1241
  await abortableDelay(backoff * (0.5 + Math.random() / 2), attempt.signal);
1159
1242
  }
@@ -1170,7 +1253,7 @@ var init_client = __esm(() => {
1170
1253
  this._messageSource = messageSource ?? readSubagentMessageSource();
1171
1254
  this._groups = normalizeGroups(session.groups, session.group);
1172
1255
  return new Promise((resolve, reject) => {
1173
- const socket = net.connect(BROKER_SOCKET);
1256
+ const socket = net2.connect(BROKER_SOCKET);
1174
1257
  this.socket = socket;
1175
1258
  this.disconnectError = null;
1176
1259
  let settled = false;
@@ -1228,7 +1311,7 @@ var init_client = __esm(() => {
1228
1311
  this.emit("disconnected", disconnectError);
1229
1312
  }
1230
1313
  if (wasConnecting) {
1231
- reject(new Error("Connection closed before registration"));
1314
+ reject(new ConnectionClosedBeforeRegistrationError);
1232
1315
  }
1233
1316
  };
1234
1317
  const onSocketError = (err) => {
@@ -1330,7 +1413,7 @@ var init_client = __esm(() => {
1330
1413
  case "registration_failed": {
1331
1414
  if (typeof brokerMessage.reason !== "string")
1332
1415
  throw new Error("Invalid registration_failed message");
1333
- const refusal = isLiveWorkflowStageRouteRefusalCode(brokerMessage.code) ? new IntercomLiveRouteRefusedError(brokerMessage.code, brokerMessage.reason) : new Error(brokerMessage.reason);
1416
+ const refusal = isLiveWorkflowStageRouteRefusalCode(brokerMessage.code) ? new IntercomLiveRouteRefusedError(brokerMessage.code, brokerMessage.reason) : brokerMessage.code === BROKER_RETIRING_CODE ? new IntercomBrokerRetiringError(brokerMessage.reason) : new Error(brokerMessage.reason);
1334
1417
  if (this._sessionId === null) {
1335
1418
  this.emit("_registration_failed", refusal);
1336
1419
  break;
@@ -2225,40 +2308,34 @@ var init_client = __esm(() => {
2225
2308
  };
2226
2309
  });
2227
2310
 
2228
- // dist/builtin/intercom/broker/socket-liveness.ts
2229
- import net2 from "net";
2230
- function isSocketAnswering(socketPath, timeoutMs = SOCKET_PROBE_TIMEOUT_MS) {
2231
- return new Promise((resolve) => {
2232
- const socket = net2.connect(socketPath);
2233
- const finish = (isConnected) => {
2234
- clearTimeout(timeout);
2235
- socket.off("connect", onConnect);
2236
- socket.off("error", onError);
2237
- resolve(isConnected);
2238
- };
2239
- const onConnect = () => {
2240
- socket.on("error", () => {});
2241
- socket.end();
2242
- finish(true);
2243
- };
2244
- const onError = () => {
2245
- socket.destroy();
2246
- finish(false);
2247
- };
2248
- socket.on("connect", onConnect);
2249
- socket.on("error", onError);
2250
- const timeout = setTimeout(() => {
2251
- socket.destroy();
2252
- finish(false);
2253
- }, timeoutMs);
2254
- });
2311
+ // dist/builtin/intercom/broker/startup-timeout.ts
2312
+ var BROKER_READY_TIMEOUT_MS = 30000, MIN_BROKER_STARTUP_TIMEOUT_MS = 1000;
2313
+
2314
+ // dist/builtin/intercom/broker/start-announcement.ts
2315
+ import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
2316
+ function readBrokerStartAnnouncement(announcement) {
2317
+ let text;
2318
+ try {
2319
+ text = readFileSync(announcement.path, "utf-8");
2320
+ } catch {
2321
+ return;
2322
+ }
2323
+ if (!text.endsWith(`
2324
+ `))
2325
+ return;
2326
+ const [token, pid, ...rest] = text.trim().split(/\s+/u);
2327
+ if (token !== announcement.token || rest.length > 0)
2328
+ return;
2329
+ const parsed = Number(pid);
2330
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
2255
2331
  }
2256
- var SOCKET_PROBE_TIMEOUT_MS = 1000;
2257
- var init_socket_liveness = () => {};
2332
+ var BROKER_ANNOUNCE_PATH_ENV = "ATOMIC_INTERCOM_BROKER_ANNOUNCE_PATH", BROKER_ANNOUNCE_TOKEN_ENV = "ATOMIC_INTERCOM_BROKER_ANNOUNCE_TOKEN";
2333
+ var init_start_announcement = () => {};
2258
2334
 
2259
2335
  // dist/builtin/intercom/broker/spawn.ts
2260
2336
  import { spawn } from "child_process";
2261
- import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
2337
+ import { randomUUID as randomUUID3 } from "crypto";
2338
+ import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync as readFileSync2, readSync, statSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
2262
2339
  import { join as join2, dirname } from "path";
2263
2340
  import { fileURLToPath } from "url";
2264
2341
  import { createRequire } from "module";
@@ -2347,7 +2424,7 @@ function getWindowsHiddenLauncherScript(commandLine) {
2347
2424
  }
2348
2425
  function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
2349
2426
  ensurePrivateDirectory(dirname(launcherPath));
2350
- writeFileSync(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
2427
+ writeFileSync2(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
2351
2428
  return launcherPath;
2352
2429
  }
2353
2430
  function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir = EXTENSION_DIR, platform = process.platform, intercomDir = INTERCOM_DIR, nodePath = process.execPath, runtime = getCurrentBrokerRuntime(), logPath = BROKER_LOG) {
@@ -2375,7 +2452,7 @@ function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir
2375
2452
  args: [...brokerArgs, brokerPath]
2376
2453
  };
2377
2454
  }
2378
- function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore") {
2455
+ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore", announcement) {
2379
2456
  const storage = processSharedStorageSelection();
2380
2457
  return {
2381
2458
  detached: true,
@@ -2383,7 +2460,8 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore")
2383
2460
  cwd: extensionDir,
2384
2461
  env: createChildProcessEnvironment({
2385
2462
  NODE_NO_WARNINGS: "1",
2386
- ...storage === undefined ? {} : { [BROKER_STORAGE_ENV]: storage }
2463
+ ...storage === undefined ? {} : { [BROKER_STORAGE_ENV]: storage },
2464
+ ...announcement === undefined ? {} : { [BROKER_ANNOUNCE_PATH_ENV]: announcement.path, [BROKER_ANNOUNCE_TOKEN_ENV]: announcement.token }
2387
2465
  }),
2388
2466
  windowsHide: true
2389
2467
  };
@@ -2396,7 +2474,7 @@ function resetBrokerLog(logPath = BROKER_LOG) {
2396
2474
  if (previous.length > 0) {
2397
2475
  try {
2398
2476
  const previousPath = `${logPath}.previous`;
2399
- writeFileSync(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
2477
+ writeFileSync2(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
2400
2478
  if (process.platform !== "win32")
2401
2479
  chmodSync(previousPath, 384);
2402
2480
  } catch {}
@@ -2458,6 +2536,12 @@ async function spawnBrokerIfNeeded(brokerCommand, brokerArgs, readyTimeoutMs = B
2458
2536
  }
2459
2537
  const brokerPath = resolveBrokerEntrypoint(extensionDir);
2460
2538
  const launch = getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir);
2539
+ const announcement = launch.kind === "windows-launcher" ? { path: BROKER_START_ANNOUNCEMENT, token: randomUUID3() } : undefined;
2540
+ if (announcement !== undefined) {
2541
+ try {
2542
+ unlinkSync2(announcement.path);
2543
+ } catch {}
2544
+ }
2461
2545
  if (launch.kind === "windows-launcher") {
2462
2546
  writeWindowsHiddenLauncher(launch.launcherCommandLine, launch.launcherPath);
2463
2547
  }
@@ -2468,7 +2552,7 @@ async function spawnBrokerIfNeeded(brokerCommand, brokerArgs, readyTimeoutMs = B
2468
2552
  const logFd = launch.kind === "direct" ? openSync(BROKER_LOG, "a") : undefined;
2469
2553
  let child;
2470
2554
  try {
2471
- child = spawn(launch.command, launch.args, getBrokerSpawnOptions(extensionDir, logFd ?? "ignore"));
2555
+ child = spawn(launch.command, launch.args, getBrokerSpawnOptions(extensionDir, logFd ?? "ignore", announcement));
2472
2556
  } finally {
2473
2557
  if (logFd !== undefined)
2474
2558
  closeSync(logFd);
@@ -2504,7 +2588,7 @@ ${describeBrokerLog()}`);
2504
2588
  };
2505
2589
  child.once("error", onError);
2506
2590
  child.once("exit", onExit);
2507
- waitForBroker(readyTimeoutMs).then(() => {
2591
+ waitForBroker(readyTimeoutMs, announcement === undefined ? undefined : () => readBrokerStartAnnouncement(announcement)).then(() => {
2508
2592
  cleanup();
2509
2593
  resolve();
2510
2594
  }, (error) => {
@@ -2525,7 +2609,7 @@ async function isBrokerRunning() {
2525
2609
  if (!existsSync(BROKER_PID))
2526
2610
  return false;
2527
2611
  try {
2528
- const pid = parseInt(readFileSync(BROKER_PID, "utf-8").trim(), 10);
2612
+ const pid = parseInt(readFileSync2(BROKER_PID, "utf-8").trim(), 10);
2529
2613
  if (!Number.isFinite(pid))
2530
2614
  return false;
2531
2615
  process.kill(pid, 0);
@@ -2550,7 +2634,7 @@ function handSpawnLockToBroker(pid, owner) {
2550
2634
  }
2551
2635
  function ownsSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
2552
2636
  try {
2553
- return readFileSync(lockPath, "utf-8").startsWith(owner);
2637
+ return readFileSync2(lockPath, "utf-8").startsWith(owner);
2554
2638
  } catch {
2555
2639
  return false;
2556
2640
  }
@@ -2562,7 +2646,7 @@ function acquireSpawnLock() {
2562
2646
  ${Date.now()}
2563
2647
  `;
2564
2648
  try {
2565
- writeFileSync(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
2649
+ writeFileSync2(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
2566
2650
  return owner;
2567
2651
  } catch (error) {
2568
2652
  if (!(error instanceof Error) || error.code !== "EEXIST") {
@@ -2570,7 +2654,7 @@ ${Date.now()}
2570
2654
  }
2571
2655
  if (isSpawnLockStale()) {
2572
2656
  try {
2573
- unlinkSync(BROKER_SPAWN_LOCK);
2657
+ unlinkSync2(BROKER_SPAWN_LOCK);
2574
2658
  } catch {}
2575
2659
  continue;
2576
2660
  }
@@ -2592,7 +2676,7 @@ function isSpawnLockStale(lockPath = BROKER_SPAWN_LOCK, nowMs = Date.now()) {
2592
2676
  return false;
2593
2677
  }
2594
2678
  try {
2595
- const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync(lockPath, "utf-8").trim().split(`
2679
+ const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync2(lockPath, "utf-8").trim().split(`
2596
2680
  `);
2597
2681
  const spawnerPid = Number.parseInt(spawnerLine, 10);
2598
2682
  const createdAt = Number.parseInt(createdAtLine, 10);
@@ -2610,25 +2694,34 @@ function releaseSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
2610
2694
  if (!ownsSpawnLock(owner, lockPath))
2611
2695
  return;
2612
2696
  try {
2613
- unlinkSync(lockPath);
2697
+ unlinkSync2(lockPath);
2614
2698
  } catch {}
2615
2699
  }
2616
- async function waitForBroker(timeoutMs = BROKER_READY_TIMEOUT_MS) {
2700
+ async function waitForBroker(timeoutMs = BROKER_READY_TIMEOUT_MS, announcedPid) {
2617
2701
  const start = Date.now();
2618
2702
  let interval = BROKER_POLL_INITIAL_INTERVAL_MS;
2619
2703
  while (Date.now() - start < timeoutMs) {
2620
2704
  if (await checkSocketConnectable()) {
2621
2705
  return;
2622
2706
  }
2707
+ const pid = announcedPid?.();
2708
+ if (pid !== undefined && !isProcessAlive(pid)) {
2709
+ if (await checkSocketConnectable())
2710
+ return;
2711
+ throw new Error(`Intercom broker exited before startup (pid ${pid})
2712
+ ${describeBrokerLog()}`);
2713
+ }
2623
2714
  await sleep(interval);
2624
2715
  interval = Math.min(interval * 2, BROKER_POLL_MAX_INTERVAL_MS);
2625
2716
  }
2626
- throw new Error(`Broker failed to start within timeout
2717
+ throw new Error(`Broker failed to start within timeout (${timeoutMs} ms)
2718
+ ` + "The broker did not answer on its socket in time; it may still be starting, which a busy machine or an empty jiti " + "cache slows. Try again, or raise `brokerStartupTimeoutMs` in the Intercom config.json (default " + `${BROKER_READY_TIMEOUT_MS} ms).
2627
2719
  ${describeBrokerLog()}`);
2628
2720
  }
2629
- var INTERCOM_DIR, EXTENSION_DIR, BROKER_SOCKET2, BROKER_PID, BROKER_SPAWN_LOCK, BROKER_LOG, BROKER_LOG_TAIL_BYTES, BROKER_POLL_INITIAL_INTERVAL_MS = 10, BROKER_POLL_MAX_INTERVAL_MS = 100, BROKER_READY_TIMEOUT_MS = 5000, BROKER_SPAWN_LOCK_MAX_AGE_MS = 60000, INTERNAL_INTERCOM_BROKER_ARG = "--atomic-internal-intercom-broker";
2721
+ var INTERCOM_DIR, EXTENSION_DIR, BROKER_SOCKET2, BROKER_PID, BROKER_SPAWN_LOCK, BROKER_LOG, BROKER_START_ANNOUNCEMENT, BROKER_LOG_TAIL_BYTES, BROKER_POLL_INITIAL_INTERVAL_MS = 10, BROKER_POLL_MAX_INTERVAL_MS = 100, BROKER_SPAWN_LOCK_MAX_AGE_MS = 60000, INTERNAL_INTERCOM_BROKER_ARG = "--atomic-internal-intercom-broker";
2630
2722
  var init_spawn = __esm(() => {
2631
2723
  init_socket_liveness();
2724
+ init_start_announcement();
2632
2725
  init_paths();
2633
2726
  INTERCOM_DIR = getIntercomDirPath();
2634
2727
  EXTENSION_DIR = resolveIntercomPackageRoot(import.meta.url);
@@ -2636,6 +2729,7 @@ var init_spawn = __esm(() => {
2636
2729
  BROKER_PID = getBrokerPidPath();
2637
2730
  BROKER_SPAWN_LOCK = getBrokerSpawnLockPath();
2638
2731
  BROKER_LOG = getBrokerLogPath();
2732
+ BROKER_START_ANNOUNCEMENT = getBrokerStartAnnouncementPath();
2639
2733
  BROKER_LOG_TAIL_BYTES = 8 * 1024;
2640
2734
  });
2641
2735
 
@@ -2792,14 +2886,14 @@ var init_reply_waiter = __esm(() => {
2792
2886
  });
2793
2887
 
2794
2888
  // dist/builtin/intercom/config.ts
2795
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2889
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
2796
2890
  import { getAgentConfigPaths } from "@bastani/atomic";
2797
2891
  function loadConfig() {
2798
2892
  if (!existsSync2(CONFIG_PATH)) {
2799
2893
  return { ...defaults };
2800
2894
  }
2801
2895
  try {
2802
- const raw = readFileSync2(CONFIG_PATH, "utf-8");
2896
+ const raw = readFileSync3(CONFIG_PATH, "utf-8");
2803
2897
  const parsed = JSON.parse(raw);
2804
2898
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2805
2899
  throw new Error("Config must be a JSON object");
@@ -2829,6 +2923,12 @@ function loadConfig() {
2829
2923
  }
2830
2924
  config.brokerArgs = brokerArgs;
2831
2925
  }
2926
+ if (Object.hasOwn(parsedConfig, "brokerStartupTimeoutMs")) {
2927
+ if (!Number.isInteger(parsedConfig.brokerStartupTimeoutMs) || Number(parsedConfig.brokerStartupTimeoutMs) < MIN_BROKER_STARTUP_TIMEOUT_MS) {
2928
+ throw new Error(`"brokerStartupTimeoutMs" must be an integer of at least ${MIN_BROKER_STARTUP_TIMEOUT_MS}`);
2929
+ }
2930
+ config.brokerStartupTimeoutMs = Number(parsedConfig.brokerStartupTimeoutMs);
2931
+ }
2832
2932
  if (Object.hasOwn(parsedConfig, "confirmSend")) {
2833
2933
  if (typeof parsedConfig.confirmSend !== "boolean") {
2834
2934
  throw new Error(`"confirmSend" must be a boolean`);
@@ -2876,6 +2976,7 @@ var init_config = __esm(() => {
2876
2976
  defaults = {
2877
2977
  brokerCommand: DEFAULT_BROKER_COMMAND,
2878
2978
  brokerArgs: [...DEFAULT_BROKER_ARGS],
2979
+ brokerStartupTimeoutMs: BROKER_READY_TIMEOUT_MS,
2879
2980
  confirmSend: false,
2880
2981
  maxPendingAsks: DEFAULT_MAX_PENDING_REPLY_WAITS,
2881
2982
  replyHint: true
@@ -3475,7 +3576,7 @@ var init_intercom_utils = __esm(() => {
3475
3576
  });
3476
3577
 
3477
3578
  // dist/builtin/intercom/contact-supervisor-tool.ts
3478
- import { randomUUID as randomUUID3 } from "crypto";
3579
+ import { randomUUID as randomUUID4 } from "crypto";
3479
3580
  import { Type } from "typebox";
3480
3581
  import { Text as Text2 } from "@earendil-works/pi-tui";
3481
3582
  function registerContactSupervisorTool(pi, deps) {
@@ -3675,7 +3776,7 @@ function registerContactSupervisorTool(pi, deps) {
3675
3776
  }
3676
3777
  let wait = null;
3677
3778
  try {
3678
- const questionId = randomUUID3();
3779
+ const questionId = randomUUID4();
3679
3780
  const admission = beginReplyWait(sendTo, questionId, signal);
3680
3781
  if (!admission.ok) {
3681
3782
  return {
@@ -3797,7 +3898,7 @@ var init_workflow_stage_target = __esm(() => {
3797
3898
  });
3798
3899
 
3799
3900
  // dist/builtin/intercom/retry-identity.ts
3800
- import { randomUUID as randomUUID4 } from "node:crypto";
3901
+ import { randomUUID as randomUUID5 } from "node:crypto";
3801
3902
  function operationKey(input) {
3802
3903
  return JSON.stringify([
3803
3904
  input.sessionId,
@@ -3825,8 +3926,8 @@ class RetryIdentityReservations {
3825
3926
  this.maxEntries = options.maxEntries ?? RETRY_IDENTITY_MAX_ENTRIES;
3826
3927
  this.maxReuses = options.maxReuses ?? RETRY_IDENTITY_MAX_REUSES;
3827
3928
  this.now = options.now ?? Date.now;
3828
- this.createId = options.createId ?? randomUUID4;
3829
- this.createToken = options.createToken ?? randomUUID4;
3929
+ this.createId = options.createId ?? randomUUID5;
3930
+ this.createToken = options.createToken ?? randomUUID5;
3830
3931
  }
3831
3932
  begin(input, retryToken) {
3832
3933
  const now = this.now();
@@ -5799,8 +5900,8 @@ import {
5799
5900
  existsSync as existsSync3,
5800
5901
  mkdirSync as mkdirSync2,
5801
5902
  openSync as openSync2,
5802
- readFileSync as readFileSync3,
5803
- writeFileSync as writeFileSync2
5903
+ readFileSync as readFileSync4,
5904
+ writeFileSync as writeFileSync3
5804
5905
  } from "node:fs";
5805
5906
  import { dirname as dirname2 } from "node:path";
5806
5907
  import { DatabaseSync } from "node:sqlite";
@@ -5865,13 +5966,13 @@ function prepareDurableAuthority(storagePath) {
5865
5966
  return;
5866
5967
  if (!databaseExists) {
5867
5968
  const key = randomBytes(AUTHORITY_KEY_BYTES);
5868
- writeFileSync2(keyPath, key, { flag: "wx", mode: 384 });
5969
+ writeFileSync3(keyPath, key, { flag: "wx", mode: 384 });
5869
5970
  const databaseDescriptor = openSync2(storagePath, "wx", 384);
5870
5971
  closeSync2(databaseDescriptor);
5871
5972
  restrictExistingAuthorityArtifacts(storagePath, keyPath);
5872
5973
  return key;
5873
5974
  }
5874
- const key = readFileSync3(keyPath);
5975
+ const key = readFileSync4(keyPath);
5875
5976
  if (key.length !== AUTHORITY_KEY_BYTES)
5876
5977
  return;
5877
5978
  return key;
@@ -6433,7 +6534,7 @@ var init_terminal_ordering_barrier = __esm(() => {
6433
6534
 
6434
6535
  // dist/builtin/intercom/subagent-relay.ts
6435
6536
  import { isStaleExtensionContextError as isStaleExtensionContextError4 } from "@bastani/atomic";
6436
- import { randomUUID as randomUUID5 } from "crypto";
6537
+ import { randomUUID as randomUUID6 } from "crypto";
6437
6538
  async function dispatchRelayFallback(pi, prefix, terminalMessage, stageAdmissionKey) {
6438
6539
  let delivered = 0;
6439
6540
  try {
@@ -6465,7 +6566,7 @@ function registerSubagentRelay(pi, deps) {
6465
6566
  status,
6466
6567
  group: deps.homeGroup?.()
6467
6568
  },
6468
- message: { id: randomUUID5(), timestamp: now, content: { text: messageText } },
6569
+ message: { id: randomUUID6(), timestamp: now, content: { text: messageText } },
6469
6570
  bodyText: messageText
6470
6571
  };
6471
6572
  let dispatched = false;
@@ -7705,11 +7806,11 @@ var init_released_stage_peers = __esm(() => {
7705
7806
  });
7706
7807
 
7707
7808
  // dist/builtin/intercom/supervisor-authorization-registry.ts
7708
- import { randomUUID as randomUUID6 } from "node:crypto";
7809
+ import { randomUUID as randomUUID7 } from "node:crypto";
7709
7810
 
7710
7811
  class SupervisorAuthorizationRegistry {
7711
7812
  byCapability = new Map;
7712
- ownerToken = randomUUID6();
7813
+ ownerToken = randomUUID7();
7713
7814
  async authorize(childName, connect) {
7714
7815
  const client = await connect();
7715
7816
  const authorization = await client.authorizeSupervisorChild(childName.trim());
@@ -7731,7 +7832,7 @@ __export(exports_index_heavy, {
7731
7832
  default: () => piIntercomExtension
7732
7833
  });
7733
7834
  import { APP_NAME, sessionScopedExtensionState } from "@bastani/atomic";
7734
- import { randomUUID as randomUUID7 } from "node:crypto";
7835
+ import { randomUUID as randomUUID8 } from "node:crypto";
7735
7836
  import { appendFileSync as appendFileSync2 } from "node:fs";
7736
7837
  import { setTimeout as delay3 } from "node:timers/promises";
7737
7838
  function isPendingStageUndeliverableEvent(value) {
@@ -7808,7 +7909,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
7808
7909
  const launcherCapabilityFor = (runId) => {
7809
7910
  let capability = launcherCapabilities.get(runId);
7810
7911
  if (capability === undefined) {
7811
- capability = randomUUID7();
7912
+ capability = randomUUID8();
7812
7913
  launcherCapabilities.set(runId, capability);
7813
7914
  }
7814
7915
  return capability;
@@ -7819,7 +7920,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
7819
7920
  const routeClientConnectGate = createConcurrencyGate(ROUTE_CLIENT_CONNECT_CONCURRENCY);
7820
7921
  const supervisorAuthorizations = new SupervisorAuthorizationRegistry;
7821
7922
  const releasedStagePeers = new ReleasedStagePeers({
7822
- prepare: () => spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs),
7923
+ prepare: () => spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs, config.brokerStartupTimeoutMs),
7823
7924
  createClient: (returnAddress) => new IntercomClient(returnAddress),
7824
7925
  cwd: () => getLiveContext()?.cwd ?? process.cwd(),
7825
7926
  relay: (from, message, channel) => {
@@ -8490,7 +8591,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
8490
8591
  return connection.promise;
8491
8592
  const owner = connection;
8492
8593
  const promise = routeClientConnectGate.run(async () => {
8493
- await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
8594
+ await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs, config.brokerStartupTimeoutMs);
8494
8595
  if (pendingStageRouteConnections.get(key) !== owner)
8495
8596
  throw new IntercomClientDisconnectedError;
8496
8597
  const routeReturnAddress = `${currentSessionId}:pending-stage-route:${key}`;
@@ -8697,6 +8798,8 @@ function piIntercomExtension(pi, testOverrides = {}) {
8697
8798
  let connectFailed = false;
8698
8799
  await ownershipRecorded.promise;
8699
8800
  const ownerWaitDeadline = Date.now() + LIVE_ROUTE_OWNER_WAIT_MS;
8801
+ let retiringRetries = 0;
8802
+ let retiringDeadline;
8700
8803
  try {
8701
8804
  for (let ownerWait = 0;; ownerWait += 1) {
8702
8805
  const nextClient = new IntercomClient(sessionIdAtStart, {
@@ -8708,7 +8811,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
8708
8811
  attachClientHandlers(nextClient);
8709
8812
  try {
8710
8813
  await testOverrides.beforeConnectAttempt?.(reason);
8711
- await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
8814
+ await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs, config.brokerStartupTimeoutMs);
8712
8815
  const childMetadata = currentChildOrchestratorMetadata();
8713
8816
  await nextClient.connect(buildRegistration(), childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy), resolveSessionHomeGroup());
8714
8817
  clientRegistrationGroup = resolveSessionHomeGroup();
@@ -8756,6 +8859,14 @@ function piIntercomExtension(pi, testOverrides = {}) {
8756
8859
  await delay3(liveRouteOwnerRetryDelayMs(ownerWait));
8757
8860
  continue;
8758
8861
  }
8862
+ if (isIntercomBrokerRetiring(error)) {
8863
+ retiringDeadline ??= Date.now() + BROKER_RETIRING_RETRY_WINDOW_MS;
8864
+ if (Date.now() < retiringDeadline && !disposed && !shuttingDown && getLiveContext(contextAtStart, generationAtStart)) {
8865
+ await delay3(brokerRetiringRetryDelayMs(retiringRetries++));
8866
+ if (getLiveContext(contextAtStart, generationAtStart))
8867
+ continue;
8868
+ }
8869
+ }
8759
8870
  connectFailed = true;
8760
8871
  throw toError3(error);
8761
8872
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.54-alpha.1",
3
+ "version": "0.9.55-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [