@bastani/atomic 0.9.44 → 0.9.45
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.
- package/CHANGELOG.md +34 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +17 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/journal/internal.d.ts +6 -37
- package/dist/builtin/workflows/src/durable/journal/resolution.d.ts +2 -0
- package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +5 -4
- package/dist/builtin/workflows/src/durable/journal/types.d.ts +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +40393 -40289
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +55675 -55570
- package/dist/builtin/workflows/src/index.js +126 -22
- package/dist/builtin/workflows/src/local-worker-entry.mjs +55675 -55570
- package/dist/core/settings-schema.d.ts.map +1 -1
- package/dist/core/settings-schema.js +1 -1
- package/dist/core/settings-schema.js.map +1 -1
- package/dist/core/storage/provider.d.ts +6 -3
- package/dist/core/storage/provider.d.ts.map +1 -1
- package/dist/core/storage/provider.js +7 -5
- package/dist/core/storage/provider.js.map +1 -1
- package/dist/core/storage/run-journal.d.ts +34 -0
- package/dist/core/storage/run-journal.d.ts.map +1 -1
- package/dist/core/storage/run-journal.js.map +1 -1
- package/docs/settings.md +1 -1
- package/docs/storage.md +3 -1
- package/docs/workflows/api-reference.md +8 -1
- package/docs/workflows/operations.md +1 -1
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- 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
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
-
|
|
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,
|
|
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 ?
|
|
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 ??
|
|
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,
|
|
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
|
|