@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
@@ -3824,10 +3824,16 @@ function selectRunJournal(options = {}) {
3824
3824
  async function resolveRunJournal(options = {}) {
3825
3825
  return openSelectedRunJournal(selectRunJournal(options), options.agentDir);
3826
3826
  }
3827
+ function runJournalOrigin(journal) {
3828
+ return journalOrigins.get(journal);
3829
+ }
3827
3830
  async function openSelectedRunJournal(selection, agentDir = getAgentDir()) {
3828
3831
  if ("createRun" in selection)
3829
3832
  return selection;
3830
- return withRunJournalConfigSource(selection, () => provider.openRunJournal(selection, { agentDir, domain: "journal" }));
3833
+ const journal = await withRunJournalConfigSource(selection, () => provider.openRunJournal(selection, { agentDir, domain: "journal" }));
3834
+ if (!journalOrigins.has(journal))
3835
+ journalOrigins.set(journal, { selection, agentDir });
3836
+ return journal;
3831
3837
  }
3832
3838
  async function withRunJournalConfigSource(selection, open) {
3833
3839
  if ("createRun" in selection)
@@ -3845,7 +3851,7 @@ async function withRunJournalConfigSource(selection, open) {
3845
3851
  throw error;
3846
3852
  }
3847
3853
  }
3848
- var hostModules, provider, origins;
3854
+ var hostModules, provider, origins, journalOrigins;
3849
3855
  var init_resolution = __esm(() => {
3850
3856
  init_memory_run_journal();
3851
3857
  init_sql_run_journal();
@@ -3858,6 +3864,7 @@ var init_resolution = __esm(() => {
3858
3864
  }
3859
3865
  });
3860
3866
  origins = new WeakMap;
3867
+ journalOrigins = new WeakMap;
3861
3868
  });
3862
3869
 
3863
3870
  // dist/builtin/workflows/src/authoring/keep-context.ts
@@ -17283,7 +17290,7 @@ async function admitWorkflowSessions(backend, opts, callback) {
17283
17290
  return callback(opts);
17284
17291
  if (!(rootBackend instanceof JournalDurableBackend))
17285
17292
  throw new Error("Resource admission requires a journal backend");
17286
- const admission = await SqlResourceAdmission.open(rootBackend.journal, {
17293
+ const admission = await SqlResourceAdmission.open(poolJournalFor(rootBackend.journal), {
17287
17294
  ...config,
17288
17295
  root: opts.parentRun?.rootRunId ?? opts.runId,
17289
17296
  holder: randomUUID7()
@@ -17301,6 +17308,7 @@ var init_admission_binding = __esm(() => {
17301
17308
  init_journal_backend();
17302
17309
  init_wiring();
17303
17310
  init_stage_runner_session_options();
17311
+ init_pool_journal();
17304
17312
  init_resource_admission();
17305
17313
  workflowResourceAdmission = new WeakMap;
17306
17314
  });
@@ -59185,6 +59193,84 @@ var init_host_options = __esm(() => {
59185
59193
  hostWorkerBindings = Symbol("host-worker-bindings");
59186
59194
  });
59187
59195
 
59196
+ // dist/builtin/workflows/src/durable/journal/pool-remote-journal.ts
59197
+ class PoolRemoteJournal {
59198
+ journal;
59199
+ storage;
59200
+ mirrored;
59201
+ mirroring;
59202
+ constructor(journal, storage) {
59203
+ this.journal = journal;
59204
+ this.storage = storage;
59205
+ }
59206
+ async mirror(lease) {
59207
+ const root = lease.runId;
59208
+ const sampledAt = performance.now();
59209
+ const [run, state] = await Promise.all([this.journal.getRun(root), this.journal.leaseState(root)]);
59210
+ if (run === undefined)
59211
+ throw new RunNotFoundError(root);
59212
+ const { runs, leases } = this.storage.tables;
59213
+ await this.storage.transaction(async (tx) => {
59214
+ await tx.run(`INSERT INTO ${runs}(run_id, name, cwd, created_at, status, header_json) VALUES (?, ?, ?, ?, ?, ?)
59215
+ ON CONFLICT(run_id) DO UPDATE SET status = excluded.status`, root, run.name, run.cwd, run.createdAt, run.status, serializeJson({ runId: root, name: run.name, cwd: run.cwd, createdAt: run.createdAt, status: run.status }));
59216
+ if (state === undefined)
59217
+ await tx.run(`DELETE FROM ${leases} WHERE run_id = ?`, root);
59218
+ else
59219
+ await tx.run(`INSERT INTO ${leases}(run_id, holder_id, epoch, renewed_at) VALUES (?, ?, ?, CAST(unixepoch('subsec') * 1000 AS INTEGER) - ?)
59220
+ ON CONFLICT(run_id) DO UPDATE SET holder_id = excluded.holder_id, epoch = excluded.epoch, renewed_at = excluded.renewed_at`, root, state.holderId, state.epoch, Math.ceil(Math.max(0, state.ageMs) + (performance.now() - sampledAt)));
59221
+ });
59222
+ if (state === undefined || state.holderId !== lease.holderId || state.epoch !== lease.epoch)
59223
+ throw new StaleLeaseError(root, lease.epoch, state?.epoch ?? 0);
59224
+ this.mirrored = { lease, at: sampledAt };
59225
+ }
59226
+ async fresh(lease, maxAgeMs = MIRROR_FRESH_MS) {
59227
+ const known = this.mirrored;
59228
+ if (known !== undefined && known.lease.holderId === lease.holderId && known.lease.epoch === lease.epoch && performance.now() - known.at < maxAgeMs)
59229
+ return;
59230
+ this.mirroring ??= this.mirror(lease).finally(() => {
59231
+ this.mirroring = undefined;
59232
+ });
59233
+ try {
59234
+ await this.mirroring;
59235
+ } catch (error) {
59236
+ if (error instanceof StaleLeaseError || error instanceof RunNotFoundError)
59237
+ throw error;
59238
+ }
59239
+ }
59240
+ entry(root, key) {
59241
+ return this.journal.readKey(root, key);
59242
+ }
59243
+ append(lease, key, checkpoint) {
59244
+ const event = { type: RUN_EVENT_TYPES.checkpointRecorded, payload: checkpoint };
59245
+ return this.journal.append(lease, event, { key });
59246
+ }
59247
+ events(root) {
59248
+ return (fromSeq) => this.journal.read(root, fromSeq);
59249
+ }
59250
+ }
59251
+ function isSameCheckpointValue(stored, next) {
59252
+ const hash = (value2) => durableHash(value2);
59253
+ return hash(stored) === hash(next);
59254
+ }
59255
+ function isRecordedCheckpointValue(stored, next) {
59256
+ const recorded = stored;
59257
+ if (isSameCheckpointValue(recorded, next))
59258
+ return true;
59259
+ const { completedAt, startedAt } = recorded;
59260
+ if (typeof completedAt !== "number")
59261
+ return false;
59262
+ const later = next;
59263
+ if (startedAt === undefined !== (later.startedAt === undefined))
59264
+ return false;
59265
+ return isSameCheckpointValue({ ...later, completedAt, ...startedAt === undefined ? {} : { startedAt } }, recorded);
59266
+ }
59267
+ var MIRROR_FRESH_MS = 2000;
59268
+ var init_pool_remote_journal = __esm(() => {
59269
+ init_durable_hash();
59270
+ init_types();
59271
+ init_values();
59272
+ });
59273
+
59188
59274
  // dist/builtin/workflows/src/durable/journal/pool-checkpoint-index.ts
59189
59275
  function push(map, key, value2) {
59190
59276
  if (key === undefined)
@@ -59252,6 +59338,11 @@ class PoolCheckpointRead {
59252
59338
  this.pending = apply;
59253
59339
  });
59254
59340
  }
59341
+ readEntries(stream) {
59342
+ return this.index.readEntries(this.root, stream, (apply) => {
59343
+ this.pending = apply;
59344
+ });
59345
+ }
59255
59346
  commit() {
59256
59347
  this.pending?.();
59257
59348
  this.pending = undefined;
@@ -59274,7 +59365,6 @@ class PoolCheckpointIndex {
59274
59365
  }
59275
59366
  const from = base?.seq ?? 0;
59276
59367
  const to = head ? Number(head.seq) : 0;
59277
- const delta = new Partitions;
59278
59368
  const added = [];
59279
59369
  if (to > from) {
59280
59370
  const rows = await tx.all(`SELECT seq, key, payload FROM ${events} WHERE run_id = ? AND seq > ? AND seq <= ? AND type = ? ORDER BY seq`, root, from, to, RUN_EVENT_TYPES.checkpointRecorded);
@@ -59282,15 +59372,57 @@ class PoolCheckpointIndex {
59282
59372
  const checkpoint = JSON.parse(String(row.payload));
59283
59373
  if (checkpoint.kind === "ui")
59284
59374
  continue;
59285
- const entry = {
59375
+ added.push({
59286
59376
  seq: Number(row.seq),
59287
59377
  order: row.key === null ? Buffer.alloc(0) : Buffer.from(row.key, "utf8"),
59288
59378
  checkpoint
59289
- };
59290
- delta.add(entry);
59291
- added.push(entry);
59379
+ });
59292
59380
  }
59293
59381
  }
59382
+ return this.view(root, current, base, from, to, head, added, onCommit);
59383
+ }
59384
+ async readEntries(root, stream, onCommit) {
59385
+ const current = this.roots.get(root);
59386
+ let base = current;
59387
+ for (;; ) {
59388
+ const mark = base !== undefined && base.seq > 0 ? { seq: base.seq, key: base.key, at: base.at } : undefined;
59389
+ let verified = mark === undefined;
59390
+ let mismatch = false;
59391
+ let head;
59392
+ const added = [];
59393
+ for await (const entry of stream(mark === undefined ? 1 : mark.seq)) {
59394
+ if (mark !== undefined && entry.seq === mark.seq) {
59395
+ if ((entry.key ?? null) !== mark.key || entry.at !== mark.at) {
59396
+ mismatch = true;
59397
+ break;
59398
+ }
59399
+ verified = true;
59400
+ head = { seq: entry.seq, key: entry.key ?? null, at: entry.at };
59401
+ continue;
59402
+ }
59403
+ head = { seq: entry.seq, key: entry.key ?? null, at: entry.at };
59404
+ if (entry.event.type !== RUN_EVENT_TYPES.checkpointRecorded)
59405
+ continue;
59406
+ const checkpoint = entry.event.payload;
59407
+ if (checkpoint.kind === "ui")
59408
+ continue;
59409
+ added.push({
59410
+ seq: entry.seq,
59411
+ order: entry.key === undefined ? Buffer.alloc(0) : Buffer.from(entry.key, "utf8"),
59412
+ checkpoint
59413
+ });
59414
+ }
59415
+ if (mark !== undefined && (mismatch || !verified)) {
59416
+ base = undefined;
59417
+ continue;
59418
+ }
59419
+ return this.view(root, current, base, mark?.seq ?? 0, head?.seq ?? 0, head, added, onCommit);
59420
+ }
59421
+ }
59422
+ view(root, current, base, from, to, head, added, onCommit) {
59423
+ const delta = new Partitions;
59424
+ for (const entry of added)
59425
+ delta.add(entry);
59294
59426
  onCommit(() => {
59295
59427
  const latest = this.roots.get(root);
59296
59428
  if (!base) {
@@ -59553,15 +59685,21 @@ function expiry(now, duration) {
59553
59685
 
59554
59686
  class SqlPoolQueue {
59555
59687
  storage;
59688
+ remote;
59556
59689
  meta;
59557
59690
  roots;
59558
59691
  work;
59559
59692
  operations;
59693
+ outbox;
59560
59694
  transaction;
59561
59695
  journal;
59562
59696
  checkpoints = new PoolCheckpointIndex;
59563
- constructor(storage) {
59697
+ committing = new Map;
59698
+ draining;
59699
+ outboxPending = false;
59700
+ constructor(storage, remote) {
59564
59701
  this.storage = storage;
59702
+ this.remote = remote;
59565
59703
  const { transaction, tables: journal } = storage;
59566
59704
  this.transaction = transaction;
59567
59705
  this.journal = journal;
@@ -59569,10 +59707,11 @@ class SqlPoolQueue {
59569
59707
  this.roots = `${journal.runs}_pool`;
59570
59708
  this.work = `${journal.runs}_work`;
59571
59709
  this.operations = `${journal.runs}_pool_operations`;
59710
+ this.outbox = `${journal.runs}_pool_outbox`;
59572
59711
  }
59573
- static async open(storage) {
59712
+ static async open(storage, remote) {
59574
59713
  const journal = storage.tables;
59575
- const pool = new SqlPoolQueue(storage);
59714
+ const pool = new SqlPoolQueue(storage, remote);
59576
59715
  await storage.transaction(async (tx) => {
59577
59716
  if ((await tx.get("PRAGMA foreign_keys"))?.foreign_keys !== 1)
59578
59717
  throw new Error("Pool requires SQLite foreign keys");
@@ -59615,6 +59754,15 @@ class SqlPoolQueue {
59615
59754
  state TEXT NOT NULL DEFAULT 'running', revision INTEGER NOT NULL DEFAULT 0
59616
59755
  )`);
59617
59756
  await tx.exec(poolUsageSchema(journal.runs));
59757
+ if (remote)
59758
+ await tx.exec(`CREATE TABLE IF NOT EXISTS ${pool.outbox}(
59759
+ work TEXT NOT NULL REFERENCES ${pool.work}(id) ON DELETE CASCADE,
59760
+ generation INTEGER NOT NULL, operation TEXT NOT NULL, request TEXT NOT NULL,
59761
+ event_key TEXT NOT NULL, payload TEXT NOT NULL, last_seq INTEGER,
59762
+ PRIMARY KEY(work, generation, operation)
59763
+ )`);
59764
+ if (remote)
59765
+ pool.outboxPending = await tx.get(`SELECT 1 AS held FROM ${pool.outbox} LIMIT 1`) !== undefined;
59618
59766
  if (version === 1)
59619
59767
  await pool.migrateFromVersion1(tx);
59620
59768
  });
@@ -59659,6 +59807,7 @@ class SqlPoolQueue {
59659
59807
  });
59660
59808
  }
59661
59809
  async setControl(lease, state) {
59810
+ await this.remote?.fresh(lease);
59662
59811
  return this.transaction(async (tx) => {
59663
59812
  await this.authority(tx, lease);
59664
59813
  await tx.run(`INSERT INTO ${this.roots}_control(root, state, revision) VALUES (?, ?, 1)
@@ -59670,7 +59819,7 @@ class SqlPoolQueue {
59670
59819
  withCurrentAppend(lease, token, assertSession, action) {
59671
59820
  lease = snapshotLease(lease);
59672
59821
  token = { ...token };
59673
- return this.storage.withAppendGuard(async (tx, runId) => {
59822
+ const guard = async (tx, runId) => {
59674
59823
  try {
59675
59824
  if (runId !== lease.runId)
59676
59825
  throw new Error("Pool delivery cannot write another root");
@@ -59688,7 +59837,10 @@ class SqlPoolQueue {
59688
59837
  throw error;
59689
59838
  throw new PoolAppendRefusedError(error.message, { cause: error });
59690
59839
  }
59691
- }, action);
59840
+ };
59841
+ if (this.remote)
59842
+ return this.transaction((tx) => guard(tx, lease.runId)).then(action);
59843
+ return this.storage.withAppendGuard(guard, action);
59692
59844
  }
59693
59845
  async assertCurrent(lease, token) {
59694
59846
  await this.transaction(async (tx) => {
@@ -59721,6 +59873,8 @@ class SqlPoolQueue {
59721
59873
  const payload = serializeJson(boundary);
59722
59874
  const fingerprint = durableHash({ descriptor: JSON.parse(json), boundary: JSON.parse(payload) });
59723
59875
  const key = JSON.stringify([RUN_EVENT_TYPES.checkpointRecorded, boundary.kind, boundary.checkpointId]);
59876
+ if (this.remote)
59877
+ await this.recordBoundaryStart(lease, key, boundary);
59724
59878
  return this.transaction(async (tx) => {
59725
59879
  const { now } = await this.authority(tx, lease);
59726
59880
  const previous = await tx.get(`SELECT * FROM ${this.work} WHERE root = ? AND (boundary = ? OR json_extract(descriptor, '$.scope') = ? OR json_extract(descriptor, '$.childRunId') = ?)`, lease.runId, boundaryId, scope, childRunId);
@@ -59737,21 +59891,47 @@ class SqlPoolQueue {
59737
59891
  throw new Error("Pool work scopes overlap");
59738
59892
  const id = randomUUID10();
59739
59893
  await tx.run(`INSERT INTO ${this.work}(id, root, boundary, descriptor, fingerprint, epoch, ready_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, id, lease.runId, boundaryId, json, fingerprint, lease.epoch, now);
59740
- const seq = await tx.get(`SELECT COALESCE(MAX(seq), 0) AS seq FROM ${this.journal.events} WHERE run_id = ?`, lease.runId);
59741
- const existing = await tx.get(`SELECT payload FROM ${this.journal.events} WHERE run_id = ? AND key = ?`, lease.runId, key);
59742
- if (existing && existing.payload !== payload)
59743
- throw new Error("Pool boundary identity conflict");
59744
- if (!existing)
59745
- await tx.run(`INSERT INTO ${this.journal.events} VALUES (?, ?, ?, ?, ?, ?, ?)`, lease.runId, journalCounter(lease.runId, "seq", Number(seq.seq) + 1, "overflow"), lease.epoch, key, RUN_EVENT_TYPES.checkpointRecorded, payload, now);
59894
+ if (!this.remote) {
59895
+ const seq = await tx.get(`SELECT COALESCE(MAX(seq), 0) AS seq FROM ${this.journal.events} WHERE run_id = ?`, lease.runId);
59896
+ const existing = await tx.get(`SELECT payload FROM ${this.journal.events} WHERE run_id = ? AND key = ?`, lease.runId, key);
59897
+ if (existing && existing.payload !== payload)
59898
+ throw new Error("Pool boundary identity conflict");
59899
+ if (!existing)
59900
+ await tx.run(`INSERT INTO ${this.journal.events} VALUES (?, ?, ?, ?, ?, ?, ?)`, lease.runId, journalCounter(lease.runId, "seq", Number(seq.seq) + 1, "overflow"), lease.epoch, key, RUN_EVENT_TYPES.checkpointRecorded, payload, now);
59901
+ }
59746
59902
  return fromRow(await tx.get(`SELECT * FROM ${this.work} WHERE id = ?`, id));
59747
59903
  });
59748
59904
  }
59905
+ async recordBoundaryStart(lease, key, boundary) {
59906
+ const remote = this.remote;
59907
+ await remote.fresh(lease);
59908
+ const existing = await remote.entry(lease.runId, key);
59909
+ if (existing) {
59910
+ if (!isSameCheckpointValue(existing.event.payload, boundary))
59911
+ throw new Error("Pool boundary identity conflict");
59912
+ return;
59913
+ }
59914
+ const outcome = await remote.append(lease, key, boundary);
59915
+ if (outcome.status === "exists" && !isSameCheckpointValue(outcome.entry.event.payload, boundary))
59916
+ throw new Error("Pool boundary identity conflict");
59917
+ }
59749
59918
  async claim(lease, worker, definitions, durationMs) {
59750
59919
  positive(durationMs);
59751
59920
  lease = snapshotLease(lease);
59752
59921
  const compatibleDefinitions = definitions.flatMap((d) => [d.definition, d.digest]);
59753
59922
  validateText("holderId", worker);
59754
59923
  const compatible = definitions.map(() => "(json_extract(descriptor, '$.definition') = ? AND json_extract(descriptor, '$.digest') = ?)").join(" OR ");
59924
+ if (this.remote) {
59925
+ await this.remote.fresh(lease);
59926
+ try {
59927
+ while (this.outboxPending || this.draining !== undefined)
59928
+ await this.drainOutbox(lease);
59929
+ } catch (error) {
59930
+ if (error instanceof StaleLeaseError)
59931
+ throw error;
59932
+ return;
59933
+ }
59934
+ }
59755
59935
  return this.transaction(async (tx) => {
59756
59936
  const { now, status } = await this.authority(tx, lease);
59757
59937
  const control = await tx.get(`SELECT state FROM ${this.roots}_control WHERE root = ?`, lease.runId);
@@ -59785,6 +59965,7 @@ class SqlPoolQueue {
59785
59965
  positive(durationMs);
59786
59966
  lease = snapshotLease(lease);
59787
59967
  token = { ...token };
59968
+ await this.remote?.fresh(lease);
59788
59969
  return this.transaction(async (tx) => {
59789
59970
  const { now } = await this.authority(tx, lease);
59790
59971
  const row = await this.current(tx, lease, token);
@@ -59803,6 +59984,8 @@ class SqlPoolQueue {
59803
59984
  const request = serializeJson({ epoch: token.epoch, worker: token.worker, mutation });
59804
59985
  const identity = requestIdentity(request);
59805
59986
  mutation = JSON.parse(request).mutation;
59987
+ if (this.remote && mutation.kind !== "session-append")
59988
+ return this.commitRemote(lease, token, operationId, mutation, identity);
59806
59989
  const checkpoints = this.checkpoints.begin(lease.runId);
59807
59990
  const receipt = await this.transaction(async (tx) => {
59808
59991
  const { now, status } = await this.authority(tx, lease);
@@ -59826,6 +60009,127 @@ class SqlPoolQueue {
59826
60009
  checkpoints.commit();
59827
60010
  return receipt;
59828
60011
  }
60012
+ commitRemote(lease, token, operationId, mutation, identity) {
60013
+ const flight = JSON.stringify([token.id, token.generation, operationId]);
60014
+ const running = this.committing.get(flight);
60015
+ if (running)
60016
+ return running;
60017
+ const commit = this.stageAndDeliver(lease, token, operationId, mutation, identity).finally(() => {
60018
+ this.committing.delete(flight);
60019
+ });
60020
+ this.committing.set(flight, commit);
60021
+ return commit;
60022
+ }
60023
+ async stageAndDeliver(lease, token, operationId, mutation, identity) {
60024
+ const remote = this.remote;
60025
+ await remote.fresh(lease);
60026
+ const checkpoint = mutation.checkpoint;
60027
+ const key = JSON.stringify([RUN_EVENT_TYPES.checkpointRecorded, checkpoint.kind, checkpoint.checkpointId]);
60028
+ const reads = this.checkpoints.begin(lease.runId);
60029
+ const [existing, view] = await Promise.all([
60030
+ remote.entry(lease.runId, key),
60031
+ checkpoint.kind === "ui" ? undefined : reads.readEntries(remote.events(lease.runId))
60032
+ ]);
60033
+ reads.commit();
60034
+ const source = {
60035
+ read: async () => view ?? Promise.reject(new Error("Pool checkpoint view is unavailable"))
60036
+ };
60037
+ const staged = await this.transaction(async (tx) => {
60038
+ const { now, status } = await this.authority(tx, lease);
60039
+ const row = await this.current(tx, lease, token);
60040
+ if (row.epoch !== lease.epoch)
60041
+ throw new Error(STALE_WORK_LEASE);
60042
+ const previous = await tx.get(`SELECT * FROM ${this.operations} WHERE work = ? AND generation = ? AND operation = ?`, token.id, token.generation, operationId);
60043
+ if (previous) {
60044
+ if (previous.request !== identity)
60045
+ throw new Error("Pool operation identity conflict");
60046
+ return { receipt: JSON.parse(previous.receipt) };
60047
+ }
60048
+ const held = await tx.get(`SELECT * FROM ${this.outbox} WHERE work = ? AND generation = ? AND operation = ?`, token.id, token.generation, operationId);
60049
+ if (held) {
60050
+ if (held.request !== identity)
60051
+ throw new Error("Pool operation identity conflict");
60052
+ return { held };
60053
+ }
60054
+ if (row.expires_at < now)
60055
+ throw new Error(STALE_WORK_LEASE);
60056
+ if (!await this.admitsAttemptWrites(tx, lease, status))
60057
+ throw new Error("Pool root is not running");
60058
+ await validatePoolMutation(tx, this.journal, lease.runId, fromRow(row), mutation, source);
60059
+ if (checkpoint.completedAt > Math.max(now, Date.now()) + MAX_WORKER_CLOCK_SKEW_MS)
60060
+ throw new Error("Pool checkpoint timestamp is in the future");
60061
+ assertWorkflowSerializableObject(checkpoint, "Pool checkpoint");
60062
+ if (existing && !isRecordedCheckpointValue(existing.event.payload, checkpoint))
60063
+ throw new Error("Pool checkpoint identity conflict");
60064
+ if (existing && mutation.kind !== "checkpoint")
60065
+ throw new Error("Pool session checkpoint identity conflict");
60066
+ const lastSeq = mutation.kind === "checkpoint" ? undefined : await applyPoolSession(tx, `${this.journal.runs}_pool_`, row.id, mutation);
60067
+ const billed = poolCheckpointUsage(checkpoint);
60068
+ if (billed && !existing)
60069
+ await upsertPoolUsage(tx, this.journal.runs, row.id, token.generation, billed.key, billed.usage);
60070
+ if (existing) {
60071
+ const receipt = { seq: existing.seq, at: existing.at };
60072
+ await tx.run(`INSERT INTO ${this.operations} VALUES (?, ?, ?, ?, ?)`, token.id, token.generation, operationId, identity, JSON.stringify(receipt));
60073
+ return { receipt };
60074
+ }
60075
+ const entry = {
60076
+ work: token.id,
60077
+ generation: token.generation,
60078
+ operation: operationId,
60079
+ request: identity,
60080
+ event_key: key,
60081
+ payload: serializeJson(checkpoint),
60082
+ last_seq: lastSeq ?? null
60083
+ };
60084
+ await tx.run(`INSERT INTO ${this.outbox} VALUES (?, ?, ?, ?, ?, ?, ?)`, entry.work, entry.generation, entry.operation, entry.request, entry.event_key, entry.payload, entry.last_seq);
60085
+ return { held: entry };
60086
+ });
60087
+ if (staged.receipt !== undefined)
60088
+ return staged.receipt;
60089
+ this.outboxPending = true;
60090
+ return this.deliver(lease, staged.held);
60091
+ }
60092
+ async deliver(lease, held) {
60093
+ const remote = this.remote;
60094
+ const checkpoint = JSON.parse(held.payload);
60095
+ const outcome = await remote.append(lease, held.event_key, checkpoint);
60096
+ const forget = (tx) => tx.run(`DELETE FROM ${this.outbox} WHERE work = ? AND generation = ? AND operation = ?`, held.work, held.generation, held.operation);
60097
+ if (outcome.status === "exists" && !isRecordedCheckpointValue(outcome.entry.event.payload, checkpoint)) {
60098
+ await this.transaction(forget);
60099
+ throw new Error("Pool checkpoint identity conflict");
60100
+ }
60101
+ const receipt = {
60102
+ seq: outcome.entry.seq,
60103
+ at: outcome.entry.at,
60104
+ ...held.last_seq === null ? {} : { lastSeq: Number(held.last_seq) }
60105
+ };
60106
+ await this.transaction(async (tx) => {
60107
+ await tx.run(`INSERT OR IGNORE INTO ${this.operations} VALUES (?, ?, ?, ?, ?)`, held.work, held.generation, held.operation, held.request, JSON.stringify(receipt));
60108
+ await forget(tx);
60109
+ });
60110
+ return receipt;
60111
+ }
60112
+ drainOutbox(lease) {
60113
+ if (!this.remote)
60114
+ return Promise.resolve(0);
60115
+ lease = snapshotLease(lease);
60116
+ this.draining ??= this.deliverAll(lease).finally(() => {
60117
+ this.draining = undefined;
60118
+ });
60119
+ return this.draining;
60120
+ }
60121
+ async deliverAll(lease) {
60122
+ this.outboxPending = false;
60123
+ const rows = await this.transaction((tx) => tx.all(`SELECT o.* FROM ${this.outbox} o JOIN ${this.work} w ON w.id = o.work WHERE w.root = ? ORDER BY o.rowid`, lease.runId));
60124
+ try {
60125
+ for (const row of rows)
60126
+ await this.deliver(lease, row);
60127
+ } catch (error) {
60128
+ this.outboxPending = true;
60129
+ throw error;
60130
+ }
60131
+ return rows.length;
60132
+ }
59829
60133
  async appendSession(tx, row, mutation, now) {
59830
60134
  return { at: now, lastSeq: await applyPoolSession(tx, `${this.journal.runs}_pool_`, row.id, mutation) };
59831
60135
  }
@@ -59861,6 +60165,7 @@ class SqlPoolQueue {
59861
60165
  token = { ...token };
59862
60166
  assertWorkflowSerializableObject(result, "Pool result");
59863
60167
  const payload = serializeJson(result);
60168
+ await this.remote?.fresh(lease);
59864
60169
  await this.transaction(async (tx) => {
59865
60170
  const { now, status } = await this.authority(tx, lease);
59866
60171
  const row = await this.current(tx, lease, token, true);
@@ -59876,6 +60181,8 @@ class SqlPoolQueue {
59876
60181
  throw new Error(STALE_WORK_LEASE);
59877
60182
  if (JSON.parse(payload).runId !== fromRow(row).childRunId)
59878
60183
  throw new Error("Pool result identity mismatch");
60184
+ if (this.remote && await tx.get(`SELECT 1 AS held FROM ${this.outbox} WHERE work = ? LIMIT 1`, token.id))
60185
+ throw new Error("Pool work has journal events not yet recorded");
59879
60186
  await tx.run(`INSERT INTO ${this.work}_results VALUES (?, ?)`, token.id, payload);
59880
60187
  await tx.run(`UPDATE ${this.work} SET state = 'completed' WHERE id = ?`, token.id);
59881
60188
  });
@@ -59902,6 +60209,7 @@ class SqlPoolQueue {
59902
60209
  positive(durationMs);
59903
60210
  lease = snapshotLease(lease);
59904
60211
  token = { ...token };
60212
+ await this.remote?.fresh(lease);
59905
60213
  return this.transaction(async (tx) => {
59906
60214
  const { now } = await this.authority(tx, lease);
59907
60215
  const row = await this.current(tx, lease, token);
@@ -59973,11 +60281,11 @@ function isRecordedCheckpoint(stored, payload) {
59973
60281
  return false;
59974
60282
  return serializeJson({ ...next, completedAt, ...startedAt === undefined ? {} : { startedAt } }) === stored;
59975
60283
  }
59976
- function openSqlPoolQueue(journal) {
60284
+ function openSqlPoolQueue(journal, remote) {
59977
60285
  const storage = sqlPoolStorage(journal);
59978
60286
  if (!storage)
59979
60287
  return Promise.reject(new Error("Worker pool requires a SQL run journal"));
59980
- return SqlPoolQueue.open(storage);
60288
+ return SqlPoolQueue.open(storage, remote);
59981
60289
  }
59982
60290
  var STORE_NOW2 = "CAST(unixepoch('subsec') * 1000 AS INTEGER)", MAX_WORKER_CLOCK_SKEW_MS = 1000, POOL_SCHEMA_VERSION = 2, STALE_WORK_LEASE = "Stale work lease", PoolAppendRefusedError;
59983
60291
  var init_sql_pool_queue = __esm(() => {
@@ -59987,6 +60295,7 @@ var init_sql_pool_queue = __esm(() => {
59987
60295
  init_internal();
59988
60296
  init_pool_checkpoint_index();
59989
60297
  init_pool_mutations();
60298
+ init_pool_remote_journal();
59990
60299
  init_pool_stage_usage();
59991
60300
  init_types();
59992
60301
  init_values();
@@ -61791,6 +62100,9 @@ function getBrokerSpawnLockPath(agentDir = getAgentDir5()) {
61791
62100
  function getBrokerLogPath(agentDir = getAgentDir5()) {
61792
62101
  return join26(getIntercomDirPath(agentDir), "broker.log");
61793
62102
  }
62103
+ function getBrokerStartAnnouncementPath(agentDir = getAgentDir5()) {
62104
+ return join26(getIntercomDirPath(agentDir), "broker.starting");
62105
+ }
61794
62106
  function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir5()) {
61795
62107
  if (platform === "win32") {
61796
62108
  return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
@@ -62097,7 +62409,7 @@ var init_live_route_refusal = __esm(() => {
62097
62409
  });
62098
62410
 
62099
62411
  // dist/builtin/intercom/recoverable-disconnect.ts
62100
- var RECOVERABLE_DISCONNECT_MESSAGE = "Client disconnected", IntercomClientDisconnectedError, IntercomBrokerTimeoutError;
62412
+ var RECOVERABLE_DISCONNECT_MESSAGE = "Client disconnected", IntercomClientDisconnectedError, IntercomBrokerTimeoutError, BROKER_RETIRING_CODE = "broker_retiring", BROKER_RETIRING_REASON = "Intercom broker is retiring; connect to its replacement", IntercomBrokerRetiringError;
62101
62413
  var init_recoverable_disconnect = __esm(() => {
62102
62414
  init_live_route_refusal();
62103
62415
  IntercomClientDisconnectedError = class IntercomClientDisconnectedError extends Error {
@@ -62114,8 +62426,46 @@ var init_recoverable_disconnect = __esm(() => {
62114
62426
  this.name = "IntercomBrokerTimeoutError";
62115
62427
  }
62116
62428
  };
62429
+ IntercomBrokerRetiringError = class IntercomBrokerRetiringError extends Error {
62430
+ intercomRecoverableDisconnect = true;
62431
+ constructor(message = BROKER_RETIRING_REASON, options) {
62432
+ super(message, options);
62433
+ this.name = "IntercomBrokerRetiringError";
62434
+ }
62435
+ };
62117
62436
  });
62118
62437
 
62438
+ // dist/builtin/intercom/broker/socket-liveness.ts
62439
+ import net from "net";
62440
+ function isSocketAnswering(socketPath, timeoutMs = SOCKET_PROBE_TIMEOUT_MS) {
62441
+ return new Promise((resolve) => {
62442
+ const socket = net.connect(socketPath);
62443
+ const finish = (isConnected) => {
62444
+ clearTimeout(timeout);
62445
+ socket.off("connect", onConnect);
62446
+ socket.off("error", onError);
62447
+ resolve(isConnected);
62448
+ };
62449
+ const onConnect = () => {
62450
+ socket.on("error", () => {});
62451
+ socket.end();
62452
+ finish(true);
62453
+ };
62454
+ const onError = () => {
62455
+ socket.destroy();
62456
+ finish(false);
62457
+ };
62458
+ socket.on("connect", onConnect);
62459
+ socket.on("error", onError);
62460
+ const timeout = setTimeout(() => {
62461
+ socket.destroy();
62462
+ finish(false);
62463
+ }, timeoutMs);
62464
+ });
62465
+ }
62466
+ var SOCKET_PROBE_TIMEOUT_MS = 1000;
62467
+ var init_socket_liveness = () => {};
62468
+
62119
62469
  // dist/builtin/intercom/retry-policy.ts
62120
62470
  var ASK_REPLY_TIMEOUT_MS, RETRY_IDENTITY_RETRY_OPPORTUNITY_MS, RETRY_IDENTITY_TTL_MS, DELIVERED_MESSAGE_TTL_MS, SEND_RESPONSE_TIMEOUT_MS = 1e4, RECIPIENT_RECEIPT_TIMEOUT_MS = 8000, RECIPIENT_SAVING_REPORT_INTERVAL_MS;
62121
62471
  var init_retry_policy = __esm(() => {
@@ -62128,7 +62478,7 @@ var init_retry_policy = __esm(() => {
62128
62478
 
62129
62479
  // dist/builtin/intercom/broker/client.ts
62130
62480
  import { EventEmitter } from "events";
62131
- import net from "net";
62481
+ import net2 from "net";
62132
62482
  import { randomUUID as randomUUID13 } from "crypto";
62133
62483
  function connectAborted() {
62134
62484
  return new IntercomClientDisconnectedError({ cause: new Error("Disconnected before the broker connection registered") });
@@ -62153,6 +62503,19 @@ function isBacklogRefusal(error) {
62153
62503
  const code = error.code;
62154
62504
  return code === "ECONNREFUSED" || code === "EAGAIN";
62155
62505
  }
62506
+ function isLostBrokerSignature(error) {
62507
+ if (error instanceof ConnectionClosedBeforeRegistrationError)
62508
+ return true;
62509
+ const code = error.code;
62510
+ return code === "ENOENT" || code === "ECONNRESET" || code === "EPIPE";
62511
+ }
62512
+ async function classifyRegistrationFailure(error) {
62513
+ if (!isLostBrokerSignature(error) || await isSocketAnswering(BROKER_SOCKET))
62514
+ return error;
62515
+ return new IntercomBrokerRetiringError(`Intercom broker went away before it registered this session: ${error.message}`, {
62516
+ cause: error
62517
+ });
62518
+ }
62156
62519
  function toError(error) {
62157
62520
  return error instanceof Error ? error : new Error(String(error));
62158
62521
  }
@@ -62162,15 +62525,22 @@ function isWorkflowFutureStageRosterEntries(value2) {
62162
62525
  function isWorkflowStageRosterEntries(value2) {
62163
62526
  return Array.isArray(value2) && value2.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"));
62164
62527
  }
62165
- 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;
62528
+ 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;
62166
62529
  var init_client = __esm(() => {
62167
62530
  init_paths();
62168
62531
  init_pending_send_registry();
62169
62532
  init_group();
62170
62533
  init_recoverable_disconnect();
62534
+ init_socket_liveness();
62171
62535
  init_live_route_refusal();
62172
62536
  init_retry_policy();
62173
62537
  BROKER_SOCKET = getBrokerSocketPath();
62538
+ ConnectionClosedBeforeRegistrationError = class ConnectionClosedBeforeRegistrationError extends Error {
62539
+ constructor() {
62540
+ super("Connection closed before registration");
62541
+ this.name = "ConnectionClosedBeforeRegistrationError";
62542
+ }
62543
+ };
62174
62544
  IntercomPendingStageRouteRefusedError = class IntercomPendingStageRouteRefusedError extends Error {
62175
62545
  connectionKept;
62176
62546
  code;
@@ -62338,8 +62708,14 @@ var init_client = __esm(() => {
62338
62708
  } catch (error) {
62339
62709
  if (attempt.signal.aborted)
62340
62710
  throw connectAborted();
62341
- if (!(error instanceof Error) || !isBacklogRefusal(error) || retry >= BROKER_BACKLOG_RETRIES)
62711
+ if (!(error instanceof Error))
62342
62712
  throw error;
62713
+ if (!isBacklogRefusal(error) || retry >= BROKER_BACKLOG_RETRIES) {
62714
+ const classified = await classifyRegistrationFailure(error);
62715
+ if (attempt.signal.aborted)
62716
+ throw connectAborted();
62717
+ throw classified;
62718
+ }
62343
62719
  const backoff = BROKER_BACKLOG_RETRY_BASE_MS * 2 ** retry;
62344
62720
  await abortableDelay(backoff * (0.5 + Math.random() / 2), attempt.signal);
62345
62721
  }
@@ -62356,7 +62732,7 @@ var init_client = __esm(() => {
62356
62732
  this._messageSource = messageSource ?? readSubagentMessageSource();
62357
62733
  this._groups = normalizeGroups(session.groups, session.group);
62358
62734
  return new Promise((resolve, reject) => {
62359
- const socket = net.connect(BROKER_SOCKET);
62735
+ const socket = net2.connect(BROKER_SOCKET);
62360
62736
  this.socket = socket;
62361
62737
  this.disconnectError = null;
62362
62738
  let settled = false;
@@ -62414,7 +62790,7 @@ var init_client = __esm(() => {
62414
62790
  this.emit("disconnected", disconnectError);
62415
62791
  }
62416
62792
  if (wasConnecting) {
62417
- reject(new Error("Connection closed before registration"));
62793
+ reject(new ConnectionClosedBeforeRegistrationError);
62418
62794
  }
62419
62795
  };
62420
62796
  const onSocketError = (err) => {
@@ -62516,7 +62892,7 @@ var init_client = __esm(() => {
62516
62892
  case "registration_failed": {
62517
62893
  if (typeof brokerMessage.reason !== "string")
62518
62894
  throw new Error("Invalid registration_failed message");
62519
- const refusal = isLiveWorkflowStageRouteRefusalCode(brokerMessage.code) ? new IntercomLiveRouteRefusedError(brokerMessage.code, brokerMessage.reason) : new Error(brokerMessage.reason);
62895
+ const refusal = isLiveWorkflowStageRouteRefusalCode(brokerMessage.code) ? new IntercomLiveRouteRefusedError(brokerMessage.code, brokerMessage.reason) : brokerMessage.code === BROKER_RETIRING_CODE ? new IntercomBrokerRetiringError(brokerMessage.reason) : new Error(brokerMessage.reason);
62520
62896
  if (this._sessionId === null) {
62521
62897
  this.emit("_registration_failed", refusal);
62522
62898
  break;
@@ -63411,40 +63787,34 @@ var init_client = __esm(() => {
63411
63787
  };
63412
63788
  });
63413
63789
 
63414
- // dist/builtin/intercom/broker/socket-liveness.ts
63415
- import net2 from "net";
63416
- function isSocketAnswering(socketPath, timeoutMs = SOCKET_PROBE_TIMEOUT_MS) {
63417
- return new Promise((resolve) => {
63418
- const socket = net2.connect(socketPath);
63419
- const finish = (isConnected) => {
63420
- clearTimeout(timeout);
63421
- socket.off("connect", onConnect);
63422
- socket.off("error", onError);
63423
- resolve(isConnected);
63424
- };
63425
- const onConnect = () => {
63426
- socket.on("error", () => {});
63427
- socket.end();
63428
- finish(true);
63429
- };
63430
- const onError = () => {
63431
- socket.destroy();
63432
- finish(false);
63433
- };
63434
- socket.on("connect", onConnect);
63435
- socket.on("error", onError);
63436
- const timeout = setTimeout(() => {
63437
- socket.destroy();
63438
- finish(false);
63439
- }, timeoutMs);
63440
- });
63790
+ // dist/builtin/intercom/broker/startup-timeout.ts
63791
+ var BROKER_READY_TIMEOUT_MS = 30000, MIN_BROKER_STARTUP_TIMEOUT_MS = 1000;
63792
+
63793
+ // dist/builtin/intercom/broker/start-announcement.ts
63794
+ import { readFileSync as readFileSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
63795
+ function readBrokerStartAnnouncement(announcement) {
63796
+ let text;
63797
+ try {
63798
+ text = readFileSync3(announcement.path, "utf-8");
63799
+ } catch {
63800
+ return;
63801
+ }
63802
+ if (!text.endsWith(`
63803
+ `))
63804
+ return;
63805
+ const [token, pid, ...rest] = text.trim().split(/\s+/u);
63806
+ if (token !== announcement.token || rest.length > 0)
63807
+ return;
63808
+ const parsed = Number(pid);
63809
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
63441
63810
  }
63442
- var SOCKET_PROBE_TIMEOUT_MS = 1000;
63443
- var init_socket_liveness = () => {};
63811
+ var BROKER_ANNOUNCE_PATH_ENV = "ATOMIC_INTERCOM_BROKER_ANNOUNCE_PATH", BROKER_ANNOUNCE_TOKEN_ENV = "ATOMIC_INTERCOM_BROKER_ANNOUNCE_TOKEN";
63812
+ var init_start_announcement = () => {};
63444
63813
 
63445
63814
  // dist/builtin/intercom/broker/spawn.ts
63446
63815
  import { spawn } from "child_process";
63447
- import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync3, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
63816
+ import { randomUUID as randomUUID14 } from "crypto";
63817
+ import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync4, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
63448
63818
  import { join as join27, dirname as dirname11 } from "path";
63449
63819
  import { fileURLToPath as fileURLToPath4 } from "url";
63450
63820
  import { createRequire as createRequire3 } from "module";
@@ -63533,7 +63903,7 @@ function getWindowsHiddenLauncherScript(commandLine) {
63533
63903
  }
63534
63904
  function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
63535
63905
  ensurePrivateDirectory(dirname11(launcherPath));
63536
- writeFileSync4(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
63906
+ writeFileSync5(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
63537
63907
  return launcherPath;
63538
63908
  }
63539
63909
  function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir = EXTENSION_DIR, platform = process.platform, intercomDir = INTERCOM_DIR, nodePath = process.execPath, runtime = getCurrentBrokerRuntime(), logPath = BROKER_LOG) {
@@ -63561,7 +63931,7 @@ function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir
63561
63931
  args: [...brokerArgs, brokerPath]
63562
63932
  };
63563
63933
  }
63564
- function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore") {
63934
+ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore", announcement) {
63565
63935
  const storage = processSharedStorageSelection();
63566
63936
  return {
63567
63937
  detached: true,
@@ -63569,7 +63939,8 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore")
63569
63939
  cwd: extensionDir,
63570
63940
  env: createChildProcessEnvironment4({
63571
63941
  NODE_NO_WARNINGS: "1",
63572
- ...storage === undefined ? {} : { [BROKER_STORAGE_ENV]: storage }
63942
+ ...storage === undefined ? {} : { [BROKER_STORAGE_ENV]: storage },
63943
+ ...announcement === undefined ? {} : { [BROKER_ANNOUNCE_PATH_ENV]: announcement.path, [BROKER_ANNOUNCE_TOKEN_ENV]: announcement.token }
63573
63944
  }),
63574
63945
  windowsHide: true
63575
63946
  };
@@ -63582,7 +63953,7 @@ function resetBrokerLog(logPath = BROKER_LOG) {
63582
63953
  if (previous.length > 0) {
63583
63954
  try {
63584
63955
  const previousPath = `${logPath}.previous`;
63585
- writeFileSync4(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
63956
+ writeFileSync5(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
63586
63957
  if (process.platform !== "win32")
63587
63958
  chmodSync(previousPath, 384);
63588
63959
  } catch {}
@@ -63644,6 +64015,12 @@ async function spawnBrokerIfNeeded(brokerCommand, brokerArgs, readyTimeoutMs = B
63644
64015
  }
63645
64016
  const brokerPath = resolveBrokerEntrypoint(extensionDir);
63646
64017
  const launch = getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir);
64018
+ const announcement = launch.kind === "windows-launcher" ? { path: BROKER_START_ANNOUNCEMENT, token: randomUUID14() } : undefined;
64019
+ if (announcement !== undefined) {
64020
+ try {
64021
+ unlinkSync4(announcement.path);
64022
+ } catch {}
64023
+ }
63647
64024
  if (launch.kind === "windows-launcher") {
63648
64025
  writeWindowsHiddenLauncher(launch.launcherCommandLine, launch.launcherPath);
63649
64026
  }
@@ -63654,7 +64031,7 @@ async function spawnBrokerIfNeeded(brokerCommand, brokerArgs, readyTimeoutMs = B
63654
64031
  const logFd = launch.kind === "direct" ? openSync3(BROKER_LOG, "a") : undefined;
63655
64032
  let child;
63656
64033
  try {
63657
- child = spawn(launch.command, launch.args, getBrokerSpawnOptions(extensionDir, logFd ?? "ignore"));
64034
+ child = spawn(launch.command, launch.args, getBrokerSpawnOptions(extensionDir, logFd ?? "ignore", announcement));
63658
64035
  } finally {
63659
64036
  if (logFd !== undefined)
63660
64037
  closeSync3(logFd);
@@ -63690,7 +64067,7 @@ ${describeBrokerLog()}`);
63690
64067
  };
63691
64068
  child.once("error", onError);
63692
64069
  child.once("exit", onExit);
63693
- waitForBroker(readyTimeoutMs).then(() => {
64070
+ waitForBroker(readyTimeoutMs, announcement === undefined ? undefined : () => readBrokerStartAnnouncement(announcement)).then(() => {
63694
64071
  cleanup();
63695
64072
  resolve();
63696
64073
  }, (error) => {
@@ -63711,7 +64088,7 @@ async function isBrokerRunning() {
63711
64088
  if (!existsSync5(BROKER_PID))
63712
64089
  return false;
63713
64090
  try {
63714
- const pid = parseInt(readFileSync3(BROKER_PID, "utf-8").trim(), 10);
64091
+ const pid = parseInt(readFileSync4(BROKER_PID, "utf-8").trim(), 10);
63715
64092
  if (!Number.isFinite(pid))
63716
64093
  return false;
63717
64094
  process.kill(pid, 0);
@@ -63736,7 +64113,7 @@ function handSpawnLockToBroker(pid, owner) {
63736
64113
  }
63737
64114
  function ownsSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
63738
64115
  try {
63739
- return readFileSync3(lockPath, "utf-8").startsWith(owner);
64116
+ return readFileSync4(lockPath, "utf-8").startsWith(owner);
63740
64117
  } catch {
63741
64118
  return false;
63742
64119
  }
@@ -63748,7 +64125,7 @@ function acquireSpawnLock() {
63748
64125
  ${Date.now()}
63749
64126
  `;
63750
64127
  try {
63751
- writeFileSync4(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
64128
+ writeFileSync5(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
63752
64129
  return owner;
63753
64130
  } catch (error) {
63754
64131
  if (!(error instanceof Error) || error.code !== "EEXIST") {
@@ -63756,7 +64133,7 @@ ${Date.now()}
63756
64133
  }
63757
64134
  if (isSpawnLockStale()) {
63758
64135
  try {
63759
- unlinkSync3(BROKER_SPAWN_LOCK);
64136
+ unlinkSync4(BROKER_SPAWN_LOCK);
63760
64137
  } catch {}
63761
64138
  continue;
63762
64139
  }
@@ -63778,7 +64155,7 @@ function isSpawnLockStale(lockPath = BROKER_SPAWN_LOCK, nowMs = Date.now()) {
63778
64155
  return false;
63779
64156
  }
63780
64157
  try {
63781
- const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync3(lockPath, "utf-8").trim().split(`
64158
+ const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync4(lockPath, "utf-8").trim().split(`
63782
64159
  `);
63783
64160
  const spawnerPid = Number.parseInt(spawnerLine, 10);
63784
64161
  const createdAt = Number.parseInt(createdAtLine, 10);
@@ -63796,25 +64173,34 @@ function releaseSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
63796
64173
  if (!ownsSpawnLock(owner, lockPath))
63797
64174
  return;
63798
64175
  try {
63799
- unlinkSync3(lockPath);
64176
+ unlinkSync4(lockPath);
63800
64177
  } catch {}
63801
64178
  }
63802
- async function waitForBroker(timeoutMs = BROKER_READY_TIMEOUT_MS) {
64179
+ async function waitForBroker(timeoutMs = BROKER_READY_TIMEOUT_MS, announcedPid) {
63803
64180
  const start = Date.now();
63804
64181
  let interval = BROKER_POLL_INITIAL_INTERVAL_MS;
63805
64182
  while (Date.now() - start < timeoutMs) {
63806
64183
  if (await checkSocketConnectable()) {
63807
64184
  return;
63808
64185
  }
64186
+ const pid = announcedPid?.();
64187
+ if (pid !== undefined && !isProcessAlive(pid)) {
64188
+ if (await checkSocketConnectable())
64189
+ return;
64190
+ throw new Error(`Intercom broker exited before startup (pid ${pid})
64191
+ ${describeBrokerLog()}`);
64192
+ }
63809
64193
  await sleep(interval);
63810
64194
  interval = Math.min(interval * 2, BROKER_POLL_MAX_INTERVAL_MS);
63811
64195
  }
63812
- throw new Error(`Broker failed to start within timeout
64196
+ throw new Error(`Broker failed to start within timeout (${timeoutMs} ms)
64197
+ ` + "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).
63813
64198
  ${describeBrokerLog()}`);
63814
64199
  }
63815
- 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";
64200
+ 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";
63816
64201
  var init_spawn = __esm(() => {
63817
64202
  init_socket_liveness();
64203
+ init_start_announcement();
63818
64204
  init_paths();
63819
64205
  INTERCOM_DIR = getIntercomDirPath();
63820
64206
  EXTENSION_DIR = resolveIntercomPackageRoot(import.meta.url);
@@ -63822,6 +64208,7 @@ var init_spawn = __esm(() => {
63822
64208
  BROKER_PID = getBrokerPidPath();
63823
64209
  BROKER_SPAWN_LOCK = getBrokerSpawnLockPath();
63824
64210
  BROKER_LOG = getBrokerLogPath();
64211
+ BROKER_START_ANNOUNCEMENT = getBrokerStartAnnouncementPath();
63825
64212
  BROKER_LOG_TAIL_BYTES = 8 * 1024;
63826
64213
  });
63827
64214
 
@@ -63846,14 +64233,14 @@ var init_reply_waiter = __esm(() => {
63846
64233
  });
63847
64234
 
63848
64235
  // dist/builtin/intercom/config.ts
63849
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
64236
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
63850
64237
  import { getAgentConfigPaths } from "@bastani/atomic";
63851
64238
  function loadConfig() {
63852
64239
  if (!existsSync6(CONFIG_PATH)) {
63853
64240
  return { ...defaults };
63854
64241
  }
63855
64242
  try {
63856
- const raw = readFileSync4(CONFIG_PATH, "utf-8");
64243
+ const raw = readFileSync5(CONFIG_PATH, "utf-8");
63857
64244
  const parsed = JSON.parse(raw);
63858
64245
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
63859
64246
  throw new Error("Config must be a JSON object");
@@ -63883,6 +64270,12 @@ function loadConfig() {
63883
64270
  }
63884
64271
  config.brokerArgs = brokerArgs;
63885
64272
  }
64273
+ if (Object.hasOwn(parsedConfig, "brokerStartupTimeoutMs")) {
64274
+ if (!Number.isInteger(parsedConfig.brokerStartupTimeoutMs) || Number(parsedConfig.brokerStartupTimeoutMs) < MIN_BROKER_STARTUP_TIMEOUT_MS) {
64275
+ throw new Error(`"brokerStartupTimeoutMs" must be an integer of at least ${MIN_BROKER_STARTUP_TIMEOUT_MS}`);
64276
+ }
64277
+ config.brokerStartupTimeoutMs = Number(parsedConfig.brokerStartupTimeoutMs);
64278
+ }
63886
64279
  if (Object.hasOwn(parsedConfig, "confirmSend")) {
63887
64280
  if (typeof parsedConfig.confirmSend !== "boolean") {
63888
64281
  throw new Error(`"confirmSend" must be a boolean`);
@@ -63930,6 +64323,7 @@ var init_config = __esm(() => {
63930
64323
  defaults = {
63931
64324
  brokerCommand: DEFAULT_BROKER_COMMAND,
63932
64325
  brokerArgs: [...DEFAULT_BROKER_ARGS],
64326
+ brokerStartupTimeoutMs: BROKER_READY_TIMEOUT_MS,
63933
64327
  confirmSend: false,
63934
64328
  maxPendingAsks: DEFAULT_MAX_PENDING_REPLY_WAITS,
63935
64329
  replyHint: true
@@ -63950,7 +64344,7 @@ var init_reconnect_backoff = __esm(() => {
63950
64344
  });
63951
64345
 
63952
64346
  // dist/builtin/intercom/workflow-route-owner.ts
63953
- import { randomUUID as randomUUID14 } from "node:crypto";
64347
+ import { randomUUID as randomUUID15 } from "node:crypto";
63954
64348
  import { createEventBus, getBuiltinPackagePaths } from "@bastani/atomic";
63955
64349
  function closedError() {
63956
64350
  return new Error("Workflow Intercom route owner closed");
@@ -63958,7 +64352,7 @@ function closedError() {
63958
64352
  function createWorkflowRouteOwner(cwd) {
63959
64353
  const events = createEventBus();
63960
64354
  const config = loadConfig();
63961
- const id = randomUUID14();
64355
+ const id = randomUUID15();
63962
64356
  const routes = new Map;
63963
64357
  const connections = new Map;
63964
64358
  const isCurrent = (state) => routes.get(state.route.runId) === state;
@@ -64022,7 +64416,7 @@ function createWorkflowRouteOwner(cwd) {
64022
64416
  });
64023
64417
  if (extensionDir === undefined)
64024
64418
  throw new Error("Intercom builtin package is unavailable");
64025
- await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs, undefined, extensionDir);
64419
+ await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs, config.brokerStartupTimeoutMs, extensionDir);
64026
64420
  if (connections.get(connection.key) !== connection)
64027
64421
  throw closedError();
64028
64422
  const client = new IntercomClient(`${id}:pending-stage-route:${connection.key}`, {
@@ -64666,7 +65060,7 @@ var init_startup_timeout = () => {};
64666
65060
 
64667
65061
  // dist/builtin/workflows/src/workers/pool-coordinator.ts
64668
65062
  import { fork } from "node:child_process";
64669
- import { randomUUID as randomUUID15 } from "node:crypto";
65063
+ import { randomUUID as randomUUID16 } from "node:crypto";
64670
65064
  import { createWriteStream } from "node:fs";
64671
65065
  import { mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
64672
65066
  import { dirname as dirname13, join as join29, resolve as resolve12 } from "node:path";
@@ -64891,6 +65285,9 @@ class PoolCoordinator {
64891
65285
  lease;
64892
65286
  options;
64893
65287
  journal;
65288
+ durable;
65289
+ remote;
65290
+ mirrorTimer;
64894
65291
  queue;
64895
65292
  usageLedger;
64896
65293
  usageTotal;
@@ -64923,6 +65320,7 @@ class PoolCoordinator {
64923
65320
  while (this.inflight.size > 0)
64924
65321
  await Promise.allSettled([...this.inflight]);
64925
65322
  await this.journal?.close().catch(() => {});
65323
+ await this.durable?.close().catch(() => {});
64926
65324
  }
64927
65325
  receive(message) {
64928
65326
  if (message.kind === "request-reply") {
@@ -65012,11 +65410,21 @@ class PoolCoordinator {
65012
65410
  this.desiredControl ??= control;
65013
65411
  configurePooledWatchTrace(options.journalPath, "coordinator");
65014
65412
  this.journal ??= await resolveRunJournal({ journal: { path: options.journalPath } });
65413
+ if (options.durable !== undefined && this.remote === undefined) {
65414
+ this.durable = await openDurableJournal(options.durable);
65415
+ this.remote = new PoolRemoteJournal(this.durable, sqlPoolStorage(this.journal));
65416
+ }
65417
+ await this.remote?.mirror(lease);
65015
65418
  this.usageLedger ??= await openSqlPoolUsage(this.journal);
65016
65419
  this.usageTotal = await this.usageLedger.total(root);
65017
65420
  this.lease = lease;
65018
- const queue = await openSqlPoolQueue(this.journal);
65421
+ const queue = await openSqlPoolQueue(this.journal, this.remote);
65019
65422
  await queue.initialize(lease, COORDINATOR_LEASE_MS);
65423
+ await queue.drainOutbox(lease);
65424
+ if (this.remote !== undefined) {
65425
+ const remote = this.remote;
65426
+ this.mirrorTimer = setInterval(() => void remote.fresh(lease, 0).catch(() => {}), MIRROR_INTERVAL_MS).unref();
65427
+ }
65020
65428
  const retained = [];
65021
65429
  for (const work of await queue.inspect(root))
65022
65430
  if (work.state === "leased" && work.epoch < lease.epoch && (await unconfirmedProcessGroups(this.journal, root, { attempt: work })).length > 0)
@@ -65149,7 +65557,7 @@ class PoolCoordinator {
65149
65557
  throw new Error("Worker exited before delivery acknowledgement");
65150
65558
  const { slot, workerRequest } = target;
65151
65559
  await new Promise((resolve, reject) => {
65152
- const id = randomUUID15();
65560
+ const id = randomUUID16();
65153
65561
  slot.deliveries.set(id, { resolve, reject });
65154
65562
  slot.child.send({ id: workerRequest, delivery: { id, from: payload.from, message: payload.message } }, (error) => {
65155
65563
  if (error) {
@@ -65490,12 +65898,16 @@ class PoolCoordinator {
65490
65898
  admission: options.admission,
65491
65899
  ...this.promptNodes ? { promptNodes: true } : {}
65492
65900
  })
65493
- ], { stdio: ["ignore", "ignore", "pipe", "ipc"], execArgv: [] });
65901
+ ], {
65902
+ stdio: ["ignore", "ignore", "pipe", "ipc"],
65903
+ execArgv: [],
65904
+ ...options.durable === undefined ? {} : { env: { ...process.env, [POOL_DURABLE_JOURNAL_ENV]: JSON.stringify(options.durable) } }
65905
+ });
65494
65906
  const disconnected = new Promise((resolve) => child.once("disconnect", () => resolve()));
65495
65907
  this.children.add(child);
65496
65908
  if (child.pid !== undefined)
65497
65909
  this.port.send({ kind: "spawned", pid: child.pid });
65498
- const worker = randomUUID15();
65910
+ const worker = randomUUID16();
65499
65911
  this.incarnations.set(worker, child);
65500
65912
  const slot = { worker, child, deliveries: new Map, runs: new Set, stageCommands: new Map };
65501
65913
  this.slots.add(slot);
@@ -65896,6 +66308,7 @@ class PoolCoordinator {
65896
66308
  this.closed = true;
65897
66309
  clearInterval(this.ticker);
65898
66310
  clearInterval(this.recoveryTimer);
66311
+ clearInterval(this.mirrorTimer);
65899
66312
  this.rateLimitOutbox.close();
65900
66313
  let failure;
65901
66314
  let stopping = [];
@@ -65974,8 +66387,10 @@ function runPoolCoordinator() {
65974
66387
  port.onClose(() => process.exit(0));
65975
66388
  return coordinator;
65976
66389
  }
65977
- var CONTROL_ACKNOWLEDGEMENT_TIMEOUT_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
66390
+ var CONTROL_ACKNOWLEDGEMENT_TIMEOUT_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, MIRROR_INTERVAL_MS = 1e4, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
65978
66391
  var init_pool_coordinator = __esm(() => {
66392
+ init_internal();
66393
+ init_pool_remote_journal();
65979
66394
  init_resolution();
65980
66395
  init_workflows_host_module();
65981
66396
  init_sql_pool_queue();
@@ -65986,6 +66401,7 @@ var init_pool_coordinator = __esm(() => {
65986
66401
  init_pending_stage_route_capability();
65987
66402
  init_pending_stage_route_readiness();
65988
66403
  init_store();
66404
+ init_pool_journal();
65989
66405
  init_pooled_watch_trace();
65990
66406
  init_resource_admission();
65991
66407
  init_stage_watch_coalesce();
@@ -66800,7 +67216,7 @@ class StageToolExecutionBuffer {
66800
67216
  }
66801
67217
 
66802
67218
  // dist/builtin/workflows/src/workers/pooled-stage-handle.ts
66803
- import { randomUUID as randomUUID16 } from "node:crypto";
67219
+ import { randomUUID as randomUUID17 } from "node:crypto";
66804
67220
 
66805
67221
  class PooledStageHandle {
66806
67222
  runId;
@@ -67118,7 +67534,7 @@ class PooledStageHandle {
67118
67534
  return Promise.reject(new PooledStageDeliveryError("StageNotLive", this.stageName));
67119
67535
  const outcome = Promise.withResolvers();
67120
67536
  const entry = {
67121
- op: randomUUID16(),
67537
+ op: randomUUID17(),
67122
67538
  text,
67123
67539
  intent,
67124
67540
  ...options === undefined ? {} : { options },
@@ -67576,7 +67992,7 @@ var init_pooled_stage_registry = __esm(() => {
67576
67992
 
67577
67993
  // dist/builtin/workflows/src/workers/local-pool.ts
67578
67994
  import { fork as fork2 } from "node:child_process";
67579
- import { randomUUID as randomUUID17 } from "node:crypto";
67995
+ import { randomUUID as randomUUID18 } from "node:crypto";
67580
67996
  import { resolve as resolve13 } from "node:path";
67581
67997
  import { setTimeout as delay3 } from "node:timers/promises";
67582
67998
  import { fileURLToPath as fileURLToPath5 } from "node:url";
@@ -67761,8 +68177,7 @@ class LocalWorkerPool {
67761
68177
  async prepare(root, store) {
67762
68178
  if (this.closed)
67763
68179
  throw new Error("Local worker pool is closed");
67764
- if (!(this.backend.journal instanceof SqlRunJournal) || resolve13(this.options.journalPath) !== resolve13(this.backend.journal.path) || sqlPoolStorage(this.backend.journal)?.tables.runs !== "wf_runs")
67765
- throw new Error("Local workers require the same default-prefix SQLite journal");
68180
+ const poolJournal = await this.poolJournal();
67766
68181
  if (this.root !== undefined && this.root !== root)
67767
68182
  throw new Error("Local worker pool belongs to another root");
67768
68183
  this.root = root;
@@ -67779,6 +68194,7 @@ class LocalWorkerPool {
67779
68194
  lease: this.lease,
67780
68195
  options: {
67781
68196
  journalPath: this.options.journalPath,
68197
+ ...this.options.durable === undefined ? {} : { durable: this.options.durable },
67782
68198
  workers: this.options.workers,
67783
68199
  leaseMs: this.leaseMs,
67784
68200
  startupTimeoutMs: this.options.startupTimeoutMs ?? defaultStartupTimeoutMs(this.options.workers),
@@ -67787,7 +68203,20 @@ class LocalWorkerPool {
67787
68203
  },
67788
68204
  ...this.desiredControl === undefined ? {} : { control: this.desiredControl }
67789
68205
  });
67790
- this.queue ??= await openSqlPoolQueue(this.backend.journal);
68206
+ this.queue ??= await openSqlPoolQueue(poolJournal, this.options.durable === undefined ? undefined : new PoolRemoteJournal(this.backend.journal, sqlPoolStorage(poolJournal)));
68207
+ }
68208
+ async poolJournal() {
68209
+ const journal = this.backend.journal;
68210
+ if (this.options.durable === undefined) {
68211
+ if (!(journal instanceof SqlRunJournal) || resolve13(this.options.journalPath) !== resolve13(journal.path) || sqlPoolStorage(journal)?.tables.runs !== "wf_runs")
68212
+ throw new Error("Local workers require the same default-prefix SQLite journal");
68213
+ return journal;
68214
+ }
68215
+ if (isLocalPoolJournal(journal))
68216
+ throw new Error("Local workers share a local SQLite journal as it is; a durable journal selection does not apply");
68217
+ const coordination = await openCoordinationJournal(this.options.journalPath);
68218
+ registerPoolJournal(journal, coordination);
68219
+ return coordination;
67791
68220
  }
67792
68221
  async control(state) {
67793
68222
  this.desiredControl = state;
@@ -67870,7 +68299,7 @@ class LocalWorkerPool {
67870
68299
  return { ok: false, refusal: "PoolClosed" };
67871
68300
  if (this.coordinator === undefined)
67872
68301
  return { ok: false, refusal: "StageNotOnWorker" };
67873
- const watch = randomUUID17();
68302
+ const watch = randomUUID18();
67874
68303
  this.stageWatches.set(watch, listener);
67875
68304
  try {
67876
68305
  const result = await this.command({ kind: "stage-watch", watch, ...ref });
@@ -68754,6 +69183,7 @@ var RECONNECT_ATTEMPT_PUBLICATIONS = 40, INTERNAL_WORKFLOW_WORKER_ARG = "--inter
68754
69183
  var init_local_pool = __esm(() => {
68755
69184
  init_internal();
68756
69185
  init_journal_backend();
69186
+ init_pool_remote_journal();
68757
69187
  init_sql_pool_queue();
68758
69188
  init_sql_run_journal();
68759
69189
  init_workflow_tool_abort();
@@ -68768,6 +69198,7 @@ var init_local_pool = __esm(() => {
68768
69198
  init_held_prompt_answers();
68769
69199
  init_host_options();
68770
69200
  init_pool_coordinator();
69201
+ init_pool_journal();
68771
69202
  init_pooled_stage_sources();
68772
69203
  init_pooled_watch_trace();
68773
69204
  init_stage_handoff();
@@ -68809,8 +69240,18 @@ async function activatePublicPool(backend, options) {
68809
69240
  throw new Error("Local worker containment is unavailable on Windows; pooled tool processes cannot be enabled");
68810
69241
  if (process.platform !== "linux" && process.platform !== "darwin" || !await hasNativeProcessIdentity() || !await admissionBootIdentity())
68811
69242
  throw new Error("Local worker containment requires native process identity support");
68812
- if (!(backend instanceof JournalDurableBackend) || !(backend.journal instanceof SqlRunJournal) || backend.journal.path === ":memory:" || sqlPoolStorage(backend.journal)?.tables.runs !== "wf_runs")
68813
- throw new Error("Local workers require a default-prefix local SQLite journal");
69243
+ if (!(backend instanceof JournalDurableBackend))
69244
+ throw new Error("Local workers require a journal backend");
69245
+ const local = isLocalPoolJournal(backend.journal);
69246
+ let durable;
69247
+ let poolJournal = backend.journal;
69248
+ let poolPath = local ? backend.journal.path : "";
69249
+ if (!local) {
69250
+ durable = poolDurableJournal(backend.journal);
69251
+ poolPath = coordinationPath(durable);
69252
+ poolJournal = await openCoordinationJournal(poolPath);
69253
+ registerPoolJournal(backend.journal, poolJournal);
69254
+ }
68814
69255
  const cwd = options.cwd ?? process.cwd();
68815
69256
  const localWorkers = options.localWorkers && {
68816
69257
  ...options.localWorkers,
@@ -68822,16 +69263,27 @@ async function activatePublicPool(backend, options) {
68822
69263
  ...options.admission,
68823
69264
  ...options.admission.userDatabase ? { userDatabase: resolve14(cwd, options.admission.userDatabase) } : {}
68824
69265
  };
69266
+ if (durable !== undefined && admission?.userLimits) {
69267
+ if (admission.userDatabase !== undefined && admission.userDatabase !== resolve14(poolPath))
69268
+ throw new Error(`admission.userDatabase must be omitted, or ${resolve14(poolPath)}, for a journal that is not a local SQLite file`);
69269
+ admission.userDatabase = resolve14(poolPath);
69270
+ }
68825
69271
  const sources = localWorkers && await Promise.all(localWorkers.definitions.map(async (source) => ({ ...source, digest: await definitionDigest(source.path) })));
68826
69272
  const resources = JSON.parse(JSON.stringify({
68827
69273
  ...localWorkers ? { localWorkers, definitions: sources } : {},
68828
- ...admission ? { admission } : {}
69274
+ ...admission ? { admission } : {},
69275
+ ...durable !== undefined && localWorkers ? { coordinator: { host: poolHostId(durable.agentDir), database: coordinationDatabase(durable) } } : {}
68829
69276
  }));
69277
+ const saved = options.effectiveResources?.coordinator;
69278
+ if (saved !== undefined && resources.coordinator !== undefined && saved.host !== resources.coordinator.host)
69279
+ throw new Error(`This root's local workers ran on another machine (${saved.host}). Their work queue and leases are in that machine's pool database, which this machine cannot see, and its workers may still be running children. Resume the root on that machine.`);
69280
+ if (saved !== undefined && resources.coordinator !== undefined && saved.host === resources.coordinator.host && saved.database !== resources.coordinator.database)
69281
+ throw new Error(`This root's local workers keep their work queue and leases in the pool database ${saved.database} on this machine, but the journal is now selected in a way that uses ${resources.coordinator.database}: its adapter, options, prefix or the agent directory changed. Resume the root with the journal selected as it was when the root started.`);
68830
69282
  if (options.effectiveResources && !isDeepStrictEqual2(options.effectiveResources, resources))
68831
69283
  throw new Error("Worker definition compatibility or immutable resource configuration changed");
68832
69284
  options.effectiveResources = resources;
68833
69285
  if (resources.admission)
68834
- await SqlResourceAdmission.open(backend.journal, {
69286
+ await SqlResourceAdmission.open(poolJournal, {
68835
69287
  ...resources.admission,
68836
69288
  root: options.runId,
68837
69289
  holder: "preflight"
@@ -68841,7 +69293,8 @@ async function activatePublicPool(backend, options) {
68841
69293
  const definitions = await Promise.all((sources ?? []).map(async (source) => ({ ...source, definition: await loadWorkerDefinition(source) })));
68842
69294
  const pool = new LocalWorkerPool(backend, {
68843
69295
  rootId: options.runId,
68844
- journalPath: backend.journal.path,
69296
+ journalPath: poolPath,
69297
+ ...durable === undefined ? {} : { durable },
68845
69298
  workers: localWorkers.count,
68846
69299
  definitions,
68847
69300
  admission: resources.admission ?? {},
@@ -68858,13 +69311,12 @@ async function activatePublicPool(backend, options) {
68858
69311
  }
68859
69312
  }
68860
69313
  var init_public_pool = __esm(() => {
68861
- init_internal();
68862
69314
  init_journal_backend();
68863
- init_sql_run_journal();
68864
69315
  init_admission_host();
68865
69316
  init_definitions();
68866
69317
  init_host_options();
68867
69318
  init_local_pool();
69319
+ init_pool_journal();
68868
69320
  init_resource_admission();
68869
69321
  });
68870
69322
 
@@ -69324,7 +69776,7 @@ var init_run_budget = __esm(() => {
69324
69776
  });
69325
69777
 
69326
69778
  // dist/builtin/workflows/src/engine/primitives/parallel.ts
69327
- import { randomUUID as randomUUID18 } from "node:crypto";
69779
+ import { randomUUID as randomUUID19 } from "node:crypto";
69328
69780
  function createParallelPrimitive(input) {
69329
69781
  return async (steps, options = {}) => {
69330
69782
  input.runtime.exit.throwIfWorkflowExitSelected();
@@ -69332,7 +69784,7 @@ function createParallelPrimitive(input) {
69332
69784
  const normalizedOptions = normalizeGroup(options);
69333
69785
  const normalizedSteps = steps.map(normalizeGroup);
69334
69786
  const needsAutoGroup = normalizedOptions.group === true || normalizedSteps.some((step) => step.group === true);
69335
- const autoGroup = needsAutoGroup ? randomUUID18() : undefined;
69787
+ const autoGroup = needsAutoGroup ? randomUUID19() : undefined;
69336
69788
  const resolveAutoGroup = (value2) => value2.group === true && autoGroup ? { ...value2, group: autoGroup } : value2;
69337
69789
  const resolvedOptions = resolveAutoGroup(normalizedOptions);
69338
69790
  const resolvedSteps = normalizedSteps.map(resolveAutoGroup);
@@ -70216,7 +70668,7 @@ function hasUsableContentBlock(block) {
70216
70668
  }
70217
70669
 
70218
70670
  // dist/builtin/workflows/src/shared/stage-session-store.ts
70219
- import { randomUUID as randomUUID19 } from "node:crypto";
70671
+ import { randomUUID as randomUUID20 } from "node:crypto";
70220
70672
  import { dirname as dirname14, resolve as resolve15 } from "node:path";
70221
70673
  import {
70222
70674
  JsonlSessionStore,
@@ -70238,7 +70690,7 @@ async function stageSessionStore(options, defaultSessionDir) {
70238
70690
  }
70239
70691
  async function forkStageSession(store, sessionId, cwd, options, destination = store) {
70240
70692
  const source = await store.load(sessionId);
70241
- const id = randomUUID19();
70693
+ const id = randomUUID20();
70242
70694
  await destination.create({
70243
70695
  id,
70244
70696
  version: 4,
@@ -70273,6 +70725,7 @@ var init_stage_session_store = () => {};
70273
70725
  // dist/builtin/workflows/src/workers/pool-session-store.ts
70274
70726
  var init_pool_session_store = __esm(() => {
70275
70727
  init_internal();
70728
+ init_pool_journal();
70276
70729
  });
70277
70730
 
70278
70731
  // dist/builtin/workflows/src/durable/completed-catalog-index.ts
@@ -78961,7 +79414,7 @@ var init_executor = __esm(() => {
78961
79414
  });
78962
79415
 
78963
79416
  // dist/builtin/workflows/src/runs/foreground/sdk-run.ts
78964
- import { randomUUID as randomUUID20 } from "node:crypto";
79417
+ import { randomUUID as randomUUID21 } from "node:crypto";
78965
79418
  async function run2(def, inputs, callerOpts = {}) {
78966
79419
  let opts = callerOpts;
78967
79420
  const store2 = opts.store === undefined || opts.store === store ? currentWorkflowStore() : opts.store;
@@ -78972,7 +79425,7 @@ async function run2(def, inputs, callerOpts = {}) {
78972
79425
  if (opts.journal === undefined && opts.durableBackend === undefined && ownedBackend === undefined) {
78973
79426
  bindStoreDurableBackend(store2, backend);
78974
79427
  }
78975
- const runId = opts.runId ?? randomUUID20();
79428
+ const runId = opts.runId ?? randomUUID21();
78976
79429
  if (sourceRunId !== undefined || opts.localWorkers || opts.admission) {
78977
79430
  opts = await restoreWorkerOptions(backend, { ...opts, runId });
78978
79431
  }
@@ -79142,10 +79595,128 @@ var init_workflows_host_module = __esm(() => {
79142
79595
  provideRunJournalHostModules({ "@bastani/atomic/workflows": exports_sdk_surface });
79143
79596
  });
79144
79597
 
79598
+ // dist/builtin/workflows/src/workers/pool-journal.ts
79599
+ import { createHash as createHash6, randomUUID as randomUUID22 } from "node:crypto";
79600
+ import { existsSync as existsSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
79601
+ import { basename as basename6, dirname as dirname16, join as join31, resolve as resolve17 } from "node:path";
79602
+ import { storageSelectionKey as storageSelectionKey2 } from "@bastani/atomic/storage";
79603
+ function encodeOption(value2) {
79604
+ if (typeof value2 === "bigint")
79605
+ return { $bigint: value2.toString() };
79606
+ if (value2 instanceof Uint8Array)
79607
+ return { $bytes: Buffer.from(value2).toString("base64") };
79608
+ return value2;
79609
+ }
79610
+ function decodeOption(value2) {
79611
+ if (value2 !== null && typeof value2 === "object") {
79612
+ if ("$bigint" in value2)
79613
+ return BigInt(value2.$bigint);
79614
+ return new Uint8Array(Buffer.from(value2.$bytes, "base64"));
79615
+ }
79616
+ return value2;
79617
+ }
79618
+ function isReopenableSelection(selection) {
79619
+ return selection.kind === "sqlite" || selection.kind === "adapter";
79620
+ }
79621
+ function encodeSelection(selection) {
79622
+ if (selection.kind === "sqlite")
79623
+ return JSON.stringify({ ...selection, path: resolve17(selection.path) });
79624
+ return JSON.stringify({
79625
+ ...selection,
79626
+ ...selection.options === undefined ? {} : {
79627
+ options: Object.fromEntries(Object.entries(selection.options).map(([key, value2]) => [key, encodeOption(value2)]))
79628
+ }
79629
+ });
79630
+ }
79631
+ function decodeSelection(text) {
79632
+ const value2 = JSON.parse(text);
79633
+ if (value2.kind === "sqlite" || value2.options === undefined)
79634
+ return value2;
79635
+ return {
79636
+ ...value2,
79637
+ options: Object.fromEntries(Object.entries(value2.options).map(([key, option]) => [key, decodeOption(option)]))
79638
+ };
79639
+ }
79640
+ function isLocalPoolJournal(journal) {
79641
+ return journal instanceof SqlRunJournal && journal.descriptor.adapter === "node-sqlite" && journal.path !== ":memory:" && sqlPoolStorage(journal)?.tables.runs === "wf_runs";
79642
+ }
79643
+ function poolDurableJournal(journal) {
79644
+ const origin = runJournalOrigin(journal);
79645
+ if (origin === undefined)
79646
+ throw new Error("Local workers cannot reopen a journal passed as an object; select the journal with workflows.journal, --storage or ATOMIC_WORKFLOWS_JOURNAL so every worker process can open it");
79647
+ if (!isReopenableSelection(origin.selection))
79648
+ throw new Error(`Local workers need a journal their processes can open by selection, a SQLite file or a storage adapter, not ${origin.selection.kind}`);
79649
+ return { selection: encodeSelection(origin.selection), agentDir: origin.agentDir };
79650
+ }
79651
+ function openDurableJournal(durable) {
79652
+ return openSelectedRunJournal(decodeSelection(durable.selection), durable.agentDir);
79653
+ }
79654
+ function poolStateDirectory(agentDir) {
79655
+ return join31(dirname16(agentDir), "workflows", "pool");
79656
+ }
79657
+ function databaseIdentity(selection, agentDir) {
79658
+ return storageSelectionKey2(selection.kind === "adapter" ? { ...selection, secrets: undefined } : selection, agentDir);
79659
+ }
79660
+ function coordinationPath(durable) {
79661
+ const selection = decodeSelection(durable.selection);
79662
+ const identity = createHash6("sha256").update(databaseIdentity(selection, durable.agentDir)).digest("hex");
79663
+ return join31(poolStateDirectory(durable.agentDir), `${identity.slice(0, 24)}.sqlite`);
79664
+ }
79665
+ function coordinationDatabase(durable) {
79666
+ return basename6(coordinationPath(durable), ".sqlite");
79667
+ }
79668
+ function openCoordinationJournal(path) {
79669
+ const file = resolve17(path);
79670
+ let opened = coordinationJournals.get(file);
79671
+ if (opened === undefined) {
79672
+ mkdirSync8(dirname16(file), { recursive: true });
79673
+ opened = resolveRunJournal({ journal: { path: file } });
79674
+ coordinationJournals.set(file, opened);
79675
+ opened.catch(() => {
79676
+ if (coordinationJournals.get(file) === opened)
79677
+ coordinationJournals.delete(file);
79678
+ });
79679
+ }
79680
+ return opened;
79681
+ }
79682
+ function registerPoolJournal(durable, coordination) {
79683
+ coordinations.set(durable, coordination);
79684
+ }
79685
+ function poolJournalFor(journal) {
79686
+ return coordinations.get(journal) ?? journal;
79687
+ }
79688
+ function poolHostId(agentDir) {
79689
+ const file = join31(poolStateDirectory(agentDir), "host-id");
79690
+ try {
79691
+ const existing = readFileSync6(file, "utf8").trim();
79692
+ if (existing !== "")
79693
+ return existing;
79694
+ } catch {}
79695
+ mkdirSync8(dirname16(file), { recursive: true });
79696
+ const id = randomUUID22();
79697
+ try {
79698
+ writeFileSync6(file, `${id}
79699
+ `, { flag: "wx" });
79700
+ return id;
79701
+ } catch {
79702
+ return readFileSync6(file, "utf8").trim();
79703
+ }
79704
+ }
79705
+ var POOL_DURABLE_JOURNAL_ENV = "ATOMIC_POOL_DURABLE_JOURNAL", coordinationJournals, coordinations;
79706
+ var init_pool_journal = __esm(() => {
79707
+ init_internal();
79708
+ init_workflows_host_module();
79709
+ init_resolution();
79710
+ init_sql_run_journal();
79711
+ coordinationJournals = new Map;
79712
+ coordinations = new WeakMap;
79713
+ });
79714
+
79145
79715
  // dist/builtin/workflows/src/workers/local-worker.ts
79146
79716
  init_internal();
79147
79717
  init_journal_backend();
79148
79718
  init_resolution();
79719
+ init_pool_journal();
79149
79720
  init_workflows_host_module();
79150
79721
  init_run();
79151
79722
  init_run_tool_control_registry();
@@ -79157,7 +79728,7 @@ init_stage_ui_broker();
79157
79728
  init_store();
79158
79729
  init_admission_binding();
79159
79730
  init_definitions();
79160
- import { randomUUID as randomUUID22 } from "node:crypto";
79731
+ import { randomUUID as randomUUID24 } from "node:crypto";
79161
79732
  import { setTimeout as delay4 } from "node:timers/promises";
79162
79733
  import {
79163
79734
  applyProviderRateLimitObservation as applyProviderRateLimitObservation2,
@@ -79183,7 +79754,7 @@ init_resource_admission();
79183
79754
  // dist/builtin/workflows/src/workers/session-store.ts
79184
79755
  init_stage_topology();
79185
79756
  init_run_durable_topology();
79186
- import { randomUUID as randomUUID21 } from "node:crypto";
79757
+ import { randomUUID as randomUUID23 } from "node:crypto";
79187
79758
  function workerSessionStore(input) {
79188
79759
  const checkpoint = (id, header) => {
79189
79760
  const owner = header.workflow;
@@ -79199,7 +79770,7 @@ function workerSessionStore(input) {
79199
79770
  ...durableStageCheckpointMetadata(stage, durableRunTopology(run), run.stages.indexOf(stage)),
79200
79771
  kind: "stage",
79201
79772
  workflowId: input.root,
79202
- checkpointId: `${prefix}:session:${id}:${randomUUID21()}`,
79773
+ checkpointId: `${prefix}:session:${id}:${randomUUID23()}`,
79203
79774
  replayKey: `${prefix}:${stage.replayKey}`,
79204
79775
  name: stage.name,
79205
79776
  sessionId: id,
@@ -79863,7 +80434,10 @@ async function runWorker(start) {
79863
80434
  });
79864
80435
  });
79865
80436
  const definitions = await Promise.all(start.definitions.map(loadWorkerDefinition));
79866
- const journal = await resolveRunJournal({ journal: { path: start.journalPath } });
80437
+ const poolJournal = await resolveRunJournal({ journal: { path: start.journalPath } });
80438
+ const durableSelection = process.env[POOL_DURABLE_JOURNAL_ENV];
80439
+ delete process.env[POOL_DURABLE_JOURNAL_ENV];
80440
+ const journal = durableSelection === undefined ? poolJournal : await openDurableJournal(JSON.parse(durableSelection));
79867
80441
  const journalReads = new Set(["descriptor", "getRun", "listRuns", "read", "readKey", "leaseState", "close"]);
79868
80442
  const readOnlyJournal = new Proxy(journal, {
79869
80443
  get(target, key) {
@@ -79877,9 +80451,9 @@ async function runWorker(start) {
79877
80451
  });
79878
80452
  const reader = await JournalDurableBackend.open(readOnlyJournal, { runs: [] });
79879
80453
  configurePooledWatchTrace(start.journalPath, `worker:${process.pid}`);
79880
- const sessions = await new StorageProvider3().openSessionStore({ kind: "sqlite", path: start.journalPath, prefix: `${sqlPoolStorage(journal).tables.runs}_pool_` }, { agentDir: "", domain: "sessions" });
80454
+ const sessions = await new StorageProvider3().openSessionStore({ kind: "sqlite", path: start.journalPath, prefix: `${sqlPoolStorage(poolJournal).tables.runs}_pool_` }, { agentDir: "", domain: "sessions" });
79881
80455
  const commit = poolWorkerCommit({
79882
- send: async (mutation) => await request({ kind: "commit", operation: randomUUID22(), mutation }),
80456
+ send: async (mutation) => await request({ kind: "commit", operation: randomUUID24(), mutation }),
79883
80457
  catchUp: () => reader.catchUpWorkflow(start.root)
79884
80458
  });
79885
80459
  const reads = new Set([
@@ -80024,7 +80598,7 @@ async function runWorker(start) {
80024
80598
  });
80025
80599
  };
80026
80600
  const unsubscribe = store.subscribeInvalidation(publish);
80027
- const admission = start.admission ? await SqlResourceAdmission.open(journal, {
80601
+ const admission = start.admission ? await SqlResourceAdmission.open(poolJournal, {
80028
80602
  ...start.admission,
80029
80603
  root: start.root,
80030
80604
  holder: work.worker,
@@ -80173,6 +80747,8 @@ async function runWorker(start) {
80173
80747
  } finally {
80174
80748
  await reader.close();
80175
80749
  await sessions.close();
80750
+ if (poolJournal !== journal)
80751
+ await poolJournal.close().catch(() => {});
80176
80752
  }
80177
80753
  if (rootLeaseLost)
80178
80754
  process.disconnect?.();