@bastani/atomic 0.9.44 → 0.9.45-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 (32) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/package.json +1 -1
  5. package/dist/builtin/web-access/package.json +1 -1
  6. package/dist/builtin/workflows/CHANGELOG.md +11 -0
  7. package/dist/builtin/workflows/package.json +1 -1
  8. package/dist/builtin/workflows/src/durable/journal/internal.d.ts +6 -37
  9. package/dist/builtin/workflows/src/durable/journal/resolution.d.ts +2 -0
  10. package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +5 -4
  11. package/dist/builtin/workflows/src/durable/journal/types.d.ts +1 -1
  12. package/dist/builtin/workflows/src/extension/index.bundle.mjs +40393 -40289
  13. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +55675 -55570
  14. package/dist/builtin/workflows/src/index.js +126 -22
  15. package/dist/builtin/workflows/src/local-worker-entry.mjs +55675 -55570
  16. package/dist/core/settings-schema.d.ts.map +1 -1
  17. package/dist/core/settings-schema.js +1 -1
  18. package/dist/core/settings-schema.js.map +1 -1
  19. package/dist/core/storage/provider.d.ts +6 -3
  20. package/dist/core/storage/provider.d.ts.map +1 -1
  21. package/dist/core/storage/provider.js +7 -5
  22. package/dist/core/storage/provider.js.map +1 -1
  23. package/dist/core/storage/run-journal.d.ts +34 -0
  24. package/dist/core/storage/run-journal.d.ts.map +1 -1
  25. package/dist/core/storage/run-journal.js.map +1 -1
  26. package/docs/settings.md +1 -1
  27. package/docs/storage.md +3 -1
  28. package/docs/workflows/api-reference.md +8 -1
  29. package/docs/workflows/operations.md +1 -1
  30. package/npm-shrinkwrap.json +32 -32
  31. package/package.json +3 -3
  32. package/schemas/settings.schema.json +1 -1
@@ -923,6 +923,78 @@ function registerRunJournalConformance(runner, name, open, observeRead) {
923
923
  assert.deepEqual(await entries(journal), []);
924
924
  });
925
925
  }
926
+ check("optional appendMany settles each append in order and fences a stale lease", async (journal) => {
927
+ if (journal.appendMany === undefined)
928
+ return;
929
+ await journal.createRun(header);
930
+ const held = await lease(journal);
931
+ const context = (action) => action();
932
+ const results = await journal.appendMany(held, [
933
+ { event: { type: "RunStarted", payload: null }, key: "start", context },
934
+ { event: { type: "StatusChanged", payload: { status: "waiting" } }, context },
935
+ { event: { type: "RunFinished", payload: null }, key: "start", context }
936
+ ]);
937
+ assert.deepEqual(results.map((result) => result.status === "fulfilled" ? [result.value.status, result.value.entry.seq] : []), [
938
+ ["appended", 1],
939
+ ["appended", 2],
940
+ ["exists", 1]
941
+ ]);
942
+ assert.deepEqual((await entries(journal)).map((entry) => entry.event.type), ["RunStarted", "StatusChanged"]);
943
+ assert.equal((await journal.getRun(header.runId))?.status, "waiting");
944
+ assert.equal((await journal.takeover(header.runId, "next", held.holderId)).status, "acquired");
945
+ await assert.rejects(journal.appendMany(held, [{ event: { type: "RunFinished", payload: null }, context }]), StaleLeaseError);
946
+ assert.equal((await entries(journal)).length, 2);
947
+ });
948
+ check("optional appendMany isolates a failed append from its valid neighbours", async (journal) => {
949
+ if (journal.appendMany === undefined)
950
+ return;
951
+ await journal.createRun(header);
952
+ const held = await lease(journal);
953
+ const context = (action) => action();
954
+ const results = await journal.appendMany(held, [
955
+ { event: { type: "RunStarted", payload: null }, context },
956
+ { event: { type: "StatusChanged", payload: { status: "bad\x00status" } }, context },
957
+ { event: { type: "StatusChanged", payload: { status: "waiting" } }, context }
958
+ ]);
959
+ assert.deepEqual(results.map((result) => result.status), ["fulfilled", "rejected", "fulfilled"]);
960
+ const [started, invalid, waiting] = results;
961
+ assert.ok(invalid?.status === "rejected" && invalid.reason instanceof JournalInvalidTextError);
962
+ assert.deepEqual([started, waiting].map((result) => result?.status === "fulfilled" ? result.value.entry.seq : undefined), [1, 2]);
963
+ assert.deepEqual((await entries(journal)).map((entry) => entry.event.type), ["RunStarted", "StatusChanged"]);
964
+ assert.equal((await journal.getRun(header.runId))?.status, "waiting");
965
+ });
966
+ check("optional change tracking reports run versions and snapshot tails", async (journal) => {
967
+ const members = [journal.changeToken, journal.runVersions, journal.snapshotTail];
968
+ if (members.every((member) => member === undefined))
969
+ return;
970
+ const { changeToken, runVersions, snapshotTail } = journal;
971
+ if (changeToken === undefined || runVersions === undefined || snapshotTail === undefined)
972
+ return assert.fail("changeToken, runVersions and snapshotTail must be implemented together");
973
+ const empty = await changeToken.call(journal);
974
+ assert.equal(await changeToken.call(journal), empty);
975
+ await journal.createRun(header);
976
+ const created = await changeToken.call(journal);
977
+ assert.notEqual(created, empty);
978
+ assert.deepEqual(await runVersions.call(journal), new Map([[header.runId, { seq: 0, at: 0 }]]));
979
+ assert.equal(await snapshotTail.call(journal, header.runId), undefined);
980
+ const held = await lease(journal);
981
+ const started = await journal.append(held, { type: "RunStarted", payload: null });
982
+ await journal.append(held, { type: "CheckpointRecorded", payload: { kind: "tool", completedAt: 5 } });
983
+ const checkpoint = await journal.append(held, {
984
+ type: "CheckpointRecorded",
985
+ payload: { kind: "tool", completedAt: 7 }
986
+ });
987
+ assert.notEqual(await changeToken.call(journal), created);
988
+ const latest = { seq: checkpoint.entry.seq, at: checkpoint.entry.at };
989
+ assert.deepEqual(await runVersions.call(journal), new Map([[header.runId, latest]]));
990
+ assert.deepEqual(await snapshotTail.call(journal, header.runId), {
991
+ version: latest,
992
+ snapshot: started.entry,
993
+ laterCheckpoints: 2,
994
+ lastCheckpointAt: 7
995
+ });
996
+ assert.equal(await snapshotTail.call(journal, "absent"), undefined);
997
+ });
926
998
  check("closed journals reject operations and started reads while close remains idempotent", async (journal) => {
927
999
  await journal.createRun(header);
928
1000
  const held = await lease(journal);
@@ -977,17 +1049,21 @@ var init_conformance = __esm(() => {
977
1049
  });
978
1050
 
979
1051
  // dist/builtin/workflows/src/durable/journal/internal.ts
980
- function registerRunJournalVersions(journal, versions) {
981
- versionReaders.set(journal, versions);
982
- }
983
1052
  function runJournalVersions(journal) {
984
- return versionReaders.get(journal);
985
- }
986
- function registerRunJournalAppendMany(journal, appendMany) {
987
- appendManyWriters.set(journal, appendMany);
1053
+ const { changeToken, runVersions, snapshotTail } = journal;
1054
+ if (typeof changeToken !== "function" || typeof runVersions !== "function" || typeof snapshotTail !== "function")
1055
+ return;
1056
+ return {
1057
+ changeToken: () => changeToken.call(journal),
1058
+ runVersions: () => runVersions.call(journal),
1059
+ snapshotTail: (runId) => snapshotTail.call(journal, runId)
1060
+ };
988
1061
  }
989
1062
  function runJournalAppendMany(journal) {
990
- return appendManyWriters.get(journal);
1063
+ const { appendMany } = journal;
1064
+ if (typeof appendMany !== "function")
1065
+ return;
1066
+ return (lease, appends) => appendMany.call(journal, lease, appends);
991
1067
  }
992
1068
  function registerSqlPoolStorage(journal, storage) {
993
1069
  poolStorage.set(journal, storage);
@@ -995,7 +1071,7 @@ function registerSqlPoolStorage(journal, storage) {
995
1071
  function sqlPoolStorage(journal) {
996
1072
  return poolStorage.get(journal);
997
1073
  }
998
- var JOURNAL_READ_PAGE_SIZE = 256, NON_SNAPSHOT_EVENT_TYPES, versionReaders, appendManyWriters, poolStorage;
1074
+ var JOURNAL_READ_PAGE_SIZE = 256, NON_SNAPSHOT_EVENT_TYPES, poolStorage;
999
1075
  var init_internal = __esm(() => {
1000
1076
  init_types();
1001
1077
  NON_SNAPSHOT_EVENT_TYPES = [
@@ -1003,8 +1079,6 @@ var init_internal = __esm(() => {
1003
1079
  RUN_EVENT_TYPES.messageSettled,
1004
1080
  RUN_EVENT_TYPES.messageDelivered
1005
1081
  ];
1006
- versionReaders = new WeakMap;
1007
- appendManyWriters = new WeakMap;
1008
1082
  poolStorage = new WeakMap;
1009
1083
  });
1010
1084
 
@@ -1383,12 +1457,6 @@ class SqlRunJournal {
1383
1457
  this.database = database;
1384
1458
  this.tables = tables;
1385
1459
  this.legacy = legacy;
1386
- registerRunJournalVersions(this, {
1387
- changeToken: () => this.changeToken(),
1388
- runVersions: () => this.runVersions(),
1389
- snapshotTail: (runId) => this.snapshotTail(runId)
1390
- });
1391
- registerRunJournalAppendMany(this, (lease, appends) => this.appendMany(lease, appends));
1392
1460
  registerSqlPoolStorage(this, {
1393
1461
  transaction: (body) => this.operation(() => this.database.transaction(body)),
1394
1462
  withAppendGuard: (guard, action) => this.appendGuard.run(guard, action),
@@ -1808,6 +1876,9 @@ import {
1808
1876
  StorageProvider,
1809
1877
  selectStorage
1810
1878
  } from "@bastani/atomic/storage";
1879
+ function provideRunJournalHostModules(modules) {
1880
+ hostModules = modules;
1881
+ }
1811
1882
  function selectRunJournal(options = {}) {
1812
1883
  const agentDir = options.agentDir ?? getAgentDir();
1813
1884
  const defaultPath = join(dirname(agentDir), "workflows", "journal.sqlite");
@@ -1876,13 +1947,17 @@ async function withRunJournalConfigSource(selection, open) {
1876
1947
  throw error;
1877
1948
  }
1878
1949
  }
1879
- var provider, origins;
1950
+ var hostModules, provider, origins;
1880
1951
  var init_resolution = __esm(() => {
1881
1952
  init_memory_run_journal();
1882
1953
  init_sql_run_journal();
1954
+ hostModules = {};
1883
1955
  provider = new StorageProvider({
1884
1956
  memory: (descriptor) => new MemoryRunJournal(descriptor),
1885
- sql: SqlRunJournal.open
1957
+ sql: SqlRunJournal.open,
1958
+ get hostModules() {
1959
+ return hostModules;
1960
+ }
1886
1961
  });
1887
1962
  origins = new WeakMap;
1888
1963
  });
@@ -11601,6 +11676,13 @@ var init_journal_backend = __esm(() => {
11601
11676
  entryJson = new WeakMap;
11602
11677
  });
11603
11678
 
11679
+ // dist/builtin/workflows/src/durable/journal/workflows-host-module.ts
11680
+ var init_workflows_host_module = __esm(() => {
11681
+ init_sdk_surface();
11682
+ init_resolution();
11683
+ provideRunJournalHostModules({ "@bastani/atomic/workflows": exports_sdk_surface });
11684
+ });
11685
+
11604
11686
  // dist/builtin/workflows/src/durable/factory.ts
11605
11687
  import { getAgentDir as getAgentDir3, SettingsManager } from "@bastani/atomic";
11606
11688
  import { storageSelectionKey } from "@bastani/atomic/storage";
@@ -11653,6 +11735,7 @@ var init_factory = __esm(() => {
11653
11735
  init_backend_process_owner();
11654
11736
  init_journal_backend();
11655
11737
  init_resolution();
11738
+ init_workflows_host_module();
11656
11739
  });
11657
11740
 
11658
11741
  // dist/builtin/workflows/src/durable/run-result.ts
@@ -63320,6 +63403,23 @@ var init_sql_pool_usage = __esm(() => {
63320
63403
  init_values();
63321
63404
  });
63322
63405
 
63406
+ // dist/builtin/workflows/src/workers/startup-timeout.ts
63407
+ import { availableParallelism, loadavg as loadavg2 } from "node:os";
63408
+ function currentHostCapacity() {
63409
+ return { cpus: availableParallelism(), load: loadavg2()[0] ?? 0 };
63410
+ }
63411
+ function defaultStartupTimeoutMs(workers, host = currentHostCapacity()) {
63412
+ const cores = Math.max(1, host.cpus);
63413
+ const demand = workers + Math.max(0, host.load);
63414
+ const oversubscription = Math.max(1, demand / cores);
63415
+ return Math.min(MAX_DEFAULT_STARTUP_TIMEOUT_MS, Math.ceil(BASE_STARTUP_TIMEOUT_MS * oversubscription));
63416
+ }
63417
+ function startupTimeoutMessage(startupTimeoutMs) {
63418
+ return `Local worker startup timed out after ${startupTimeoutMs} ms: workers were too slow to start, usually because the machine is busy or the pool is large for its cores. Raise workflows.localWorkers.startupTimeoutMs (localWorkers.startupTimeoutMs in the SDK) to allow more time`;
63419
+ }
63420
+ var BASE_STARTUP_TIMEOUT_MS = 30000, MAX_DEFAULT_STARTUP_TIMEOUT_MS = 600000;
63421
+ var init_startup_timeout = () => {};
63422
+
63323
63423
  // dist/builtin/workflows/src/workers/pool-coordinator.ts
63324
63424
  import { fork } from "node:child_process";
63325
63425
  import { randomUUID as randomUUID15 } from "node:crypto";
@@ -63860,7 +63960,7 @@ class PoolCoordinator {
63860
63960
  });
63861
63961
  const closed = new Promise((resolve) => child.once("close", () => resolve()));
63862
63962
  child.once("exit", () => {
63863
- Promise.race([closed, delay2(STDERR_DRAIN_GRACE_MS)]).then(() => reject(diagnosed(timedOut ? `Local worker startup timed out after ${startupTimeoutMs} ms` : "Local worker exited before readiness")));
63963
+ Promise.race([closed, delay2(STDERR_DRAIN_GRACE_MS)]).then(() => reject(diagnosed(timedOut ? startupTimeoutMessage(startupTimeoutMs) : "Local worker exited before readiness")));
63864
63964
  this.children.delete(child);
63865
63965
  this.logs.delete(child);
63866
63966
  if (child.pid !== undefined)
@@ -64149,6 +64249,7 @@ class PoolCoordinator {
64149
64249
  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, ROUTE_REGISTRATION_WAIT_MS = 120000, 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";
64150
64250
  var init_pool_coordinator = __esm(() => {
64151
64251
  init_resolution();
64252
+ init_workflows_host_module();
64152
64253
  init_sql_pool_queue();
64153
64254
  init_sql_pool_usage();
64154
64255
  init_pending_stage_intercom();
@@ -64157,6 +64258,7 @@ var init_pool_coordinator = __esm(() => {
64157
64258
  init_pending_stage_route_readiness();
64158
64259
  init_store();
64159
64260
  init_resource_admission();
64261
+ init_startup_timeout();
64160
64262
  COORDINATOR_LEASE_MS = 3 * STAGE_SESSION_HEARTBEAT_INTERVAL_MS;
64161
64263
  });
64162
64264
 
@@ -65013,7 +65115,7 @@ class LocalWorkerPool {
65013
65115
  journalPath: this.options.journalPath,
65014
65116
  workers: this.options.workers,
65015
65117
  leaseMs: this.leaseMs,
65016
- startupTimeoutMs: this.options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS,
65118
+ startupTimeoutMs: this.options.startupTimeoutMs ?? defaultStartupTimeoutMs(this.options.workers),
65017
65119
  ...this.options.admission === undefined ? {} : { admission: this.options.admission },
65018
65120
  ...workerEntry === undefined ? {} : { workerEntry }
65019
65121
  },
@@ -65434,7 +65536,7 @@ class LocalWorkerPool {
65434
65536
  throw failure;
65435
65537
  }
65436
65538
  }
65437
- var INTERNAL_WORKFLOW_WORKER_ARG = "--internal-workflow-worker", DEFAULT_WORK_LEASE_MS = 30000, MINIMUM_WORK_LEASE_MS, DEFAULT_STARTUP_TIMEOUT_MS = 30000, MAX_TIMER_DELAY_MS = 2147483647, COORDINATOR_STDERR_TAIL_CHARACTERS = 8192, COORDINATOR_EXIT_GRACE_MS = 5000;
65539
+ var INTERNAL_WORKFLOW_WORKER_ARG = "--internal-workflow-worker", DEFAULT_WORK_LEASE_MS = 30000, MINIMUM_WORK_LEASE_MS, MAX_TIMER_DELAY_MS = 2147483647, COORDINATOR_STDERR_TAIL_CHARACTERS = 8192, COORDINATOR_EXIT_GRACE_MS = 5000;
65438
65540
  var init_local_pool = __esm(() => {
65439
65541
  init_internal();
65440
65542
  init_sql_pool_queue();
@@ -65449,6 +65551,7 @@ var init_local_pool = __esm(() => {
65449
65551
  init_host_options();
65450
65552
  init_pool_coordinator();
65451
65553
  init_stage_handoff();
65554
+ init_startup_timeout();
65452
65555
  MINIMUM_WORK_LEASE_MS = 2 * WORKER_HEARTBEAT_INTERVAL_MS;
65453
65556
  });
65454
65557
 
@@ -75705,6 +75808,7 @@ var init_sdk_surface = __esm(() => {
75705
75808
  init_workflow_infrastructure_error();
75706
75809
  init_definitions();
75707
75810
  init_registry();
75811
+ init_workflows_host_module();
75708
75812
  init_types();
75709
75813
  });
75710
75814