@bastani/atomic 0.9.32-alpha.1 → 0.9.33-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 (56) hide show
  1. package/CHANGELOG.md +13 -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 +12 -0
  7. package/dist/builtin/workflows/builtin/{chunk-bnz4knhy.js → chunk-0d0pd4fz.js} +1 -1
  8. package/dist/builtin/workflows/builtin/{chunk-v4scsq3y.js → chunk-119gt64q.js} +54 -16
  9. package/dist/builtin/workflows/builtin/{chunk-9ywkdsa9.js → chunk-s7y3hw0n.js} +1 -1
  10. package/dist/builtin/workflows/builtin/{chunk-dx5sjaxe.js → chunk-y68758re.js} +1 -1
  11. package/dist/builtin/workflows/builtin/goal.js +2 -2
  12. package/dist/builtin/workflows/builtin/index.js +3 -3
  13. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  14. package/dist/builtin/workflows/package.json +1 -1
  15. package/dist/builtin/workflows/src/extension/index.bundle.mjs +78 -20
  16. package/dist/builtin/workflows/src/index.js +53 -15
  17. package/dist/modes/interactive/interactive-agent-events.js +9 -1
  18. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  19. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  20. package/dist/modes/interactive/interactive-mode-base.js +1 -1
  21. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  22. package/dist/modes/interactive/program-status-reporter.d.ts +14 -0
  23. package/dist/modes/interactive/program-status-reporter.d.ts.map +1 -1
  24. package/dist/modes/interactive/program-status-reporter.js +92 -1
  25. package/dist/modes/interactive/program-status-reporter.js.map +1 -1
  26. package/dist/modes/interactive-engine/engine-workflow-activity.d.ts +23 -0
  27. package/dist/modes/interactive-engine/engine-workflow-activity.d.ts.map +1 -0
  28. package/dist/modes/interactive-engine/engine-workflow-activity.js +80 -0
  29. package/dist/modes/interactive-engine/engine-workflow-activity.js.map +1 -0
  30. package/dist/modes/interactive-engine/extension-ui-bridge.d.ts +8 -1
  31. package/dist/modes/interactive-engine/extension-ui-bridge.d.ts.map +1 -1
  32. package/dist/modes/interactive-engine/extension-ui-bridge.js +31 -1
  33. package/dist/modes/interactive-engine/extension-ui-bridge.js.map +1 -1
  34. package/dist/modes/interactive-engine/isolated-runtime.d.ts +3 -0
  35. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  36. package/dist/modes/interactive-engine/isolated-runtime.js +4 -0
  37. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  38. package/dist/modes/interactive-engine/protocol.d.ts +6 -0
  39. package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
  40. package/dist/modes/interactive-engine/protocol.js +63 -0
  41. package/dist/modes/interactive-engine/protocol.js.map +1 -1
  42. package/dist/modes/rpc/rpc-client.d.ts +7 -0
  43. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  44. package/dist/modes/rpc/rpc-client.js +24 -0
  45. package/dist/modes/rpc/rpc-client.js.map +1 -1
  46. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  47. package/dist/modes/rpc/rpc-mode.js +1 -0
  48. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  49. package/dist/modes/rpc/rpc-session-binding.d.ts +5 -1
  50. package/dist/modes/rpc/rpc-session-binding.d.ts.map +1 -1
  51. package/dist/modes/rpc/rpc-session-binding.js +10 -1
  52. package/dist/modes/rpc/rpc-session-binding.js.map +1 -1
  53. package/docs/terminal-setup.md +6 -4
  54. package/docs/workflows/operations.md +1 -1
  55. package/npm-shrinkwrap.json +32 -32
  56. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.33-alpha.1] - 2026-10-10
6
+
7
+ ### Fixed
8
+
9
+ - Fixed workflows falling back to a non-durable in-memory backend after a macOS reboot because the managed PostgreSQL cluster was refused with `Managed Postgres cluster identity mismatch` when only the data volume's device number changed. The cluster now attaches and its ownership record is updated when the data directory inode, path, cluster ID, major version, and PostgreSQL system identifier still match; genuine identity mismatches name the fields that differ ([#3555](https://github.com/bastani-inc/atomic/issues/3555)).
10
+ - Fixed terminal program status (OSC 7501) ignoring workflow runs: it now reports `working` while a workflow run executes, `blocked` while a run or stage waits for your input, `error` when a run fails (held until your next message, even after the run-end notice turn settles), and `done` once the agent and all runs finish ([#3556](https://github.com/bastani-inc/atomic/issues/3556)).
11
+
12
+ ## [0.9.32] - 2026-10-10
13
+
14
+ ### Fixed
15
+
16
+ - Fixed workflow stages in SDK-hosted sessions losing extensions supplied through `additionalExtensionPaths` and `extensionFactories`, and using the host process directory instead of the owning session's working directory when no stage `cwd` is set. Stage reports use that same resolved directory ([#3551](https://github.com/bastani-inc/atomic/issues/3551)).
17
+
5
18
  ## [0.9.32-alpha.1] - 2026-10-10
6
19
 
7
20
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.32-alpha.1",
3
+ "version": "0.9.33-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.32-alpha.1",
3
+ "version": "0.9.33-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic's pi-compatible MCP integration for server connections, OAuth, tools, resources, and terminal management.",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.32-alpha.1",
3
+ "version": "0.9.33-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.32-alpha.1",
3
+ "version": "0.9.33-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.33-alpha.1] - 2026-10-10
10
+
11
+ ### Fixed
12
+
13
+ - Fixed the managed PostgreSQL cluster being refused with `Managed Postgres cluster identity mismatch` after a macOS reboot changed the data volume's device number. Atomic now attaches the cluster and updates its ownership record when the data directory inode, path, cluster ID, major version, and PostgreSQL system identifier still match, and genuine identity mismatches name the fields that differ ([#3555](https://github.com/bastani-inc/atomic/issues/3555)).
14
+
15
+ ## [0.9.32] - 2026-10-10
16
+
17
+ ### Fixed
18
+
19
+ - Fixed stages without an explicit `cwd` running in the host process directory instead of the workflow invocation directory in SDK-hosted sessions. Stage reports now use the same resolved directory as their sessions, including adapter and session-manager overrides ([#3551](https://github.com/bastani-inc/atomic/issues/3551)).
20
+
9
21
  ## [0.9.32-alpha.1] - 2026-10-10
10
22
 
11
23
  ### Fixed
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getDurableBackend2,
3
3
  getAvailableDurableBackend2
4
- } from "./chunk-v4scsq3y.js";
4
+ } from "./chunk-119gt64q.js";
5
5
  export {
6
6
  getAvailableDurableBackend2 as getAvailableDurableBackend,
7
7
  getDurableBackend2 as getDurableBackend
@@ -6753,7 +6753,6 @@ class PostgresHealth {
6753
6753
  import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
6754
6754
  import { createRequire as createRequire2 } from "node:module";
6755
6755
  import { createServer } from "node:net";
6756
- import { endianness } from "node:os";
6757
6756
  import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3 } from "node:path";
6758
6757
 
6759
6758
  // dist/builtin/workflows/src/durable/dbos-postgres-ownership.ts
@@ -6768,6 +6767,7 @@ import {
6768
6767
  rmSync,
6769
6768
  writeFileSync
6770
6769
  } from "node:fs";
6770
+ import { endianness } from "node:os";
6771
6771
  import { join as join2 } from "node:path";
6772
6772
  function trustedPath(path, directory = false) {
6773
6773
  const stat = lstatSync(path);
@@ -6778,6 +6778,24 @@ function trustedPath(path, directory = false) {
6778
6778
  function postgresOwnershipDirectory(baseDir, major) {
6779
6779
  return join2(baseDir, `v${major}.shared`);
6780
6780
  }
6781
+ function controlFileSystemIdentifier(dataDir) {
6782
+ const controlPath = join2(dataDir, "global", "pg_control");
6783
+ if (!lstatSync(controlPath).isFile())
6784
+ throw new Error("Untrusted managed Postgres control file.");
6785
+ const control = readFileSync(controlPath);
6786
+ return (endianness() === "LE" ? control.readBigUInt64LE(0) : control.readBigUInt64BE(0)).toString();
6787
+ }
6788
+ var directoryInode = (identity) => /^\d+:(\d+)$/.exec(identity)?.[1];
6789
+ function managedPostgresIdentityMismatches(expected, current, compareSystemIdentifier = true) {
6790
+ const inode = directoryInode(expected.directoryIdentity);
6791
+ return [
6792
+ current.clusterId !== expected.clusterId && "clusterId",
6793
+ current.dataDir !== expected.dataDir && "dataDir",
6794
+ (inode === undefined || inode !== directoryInode(current.directoryIdentity)) && "inode",
6795
+ current.major !== expected.major && "major",
6796
+ compareSystemIdentifier && current.server?.systemIdentifier !== expected.server?.systemIdentifier && "systemIdentifier"
6797
+ ].filter((field) => field !== false);
6798
+ }
6781
6799
  function managedPostgresMetadata(baseDir, major, create) {
6782
6800
  if (!lstatSync(join2(baseDir, `v${major}`)).isDirectory()) {
6783
6801
  throw new Error("Managed Postgres data must be a real directory, not a link.");
@@ -6811,10 +6829,30 @@ function managedPostgresMetadata(baseDir, major, create) {
6811
6829
  }
6812
6830
  trustedPath(path);
6813
6831
  const record = JSON.parse(readFileSync(path, "utf8"));
6814
- if (record?.version !== 1 || typeof record.clusterId !== "string" || !/^[0-9a-f-]{36}$/.test(record.clusterId) || record.dataDir !== dataDir || record.directoryIdentity !== directoryIdentity || record.major !== major || record.server !== undefined && (!Number.isInteger(record.server?.port) || record.server.port < 1 || record.server.port > 65535 || !Number.isSafeInteger(record.server.pid) || record.server.pid <= 0 || !Number.isSafeInteger(record.server.started) || record.server.started <= 0 || !/^\d+$/.test(record.server.systemIdentifier))) {
6815
- throw new Error(`Managed Postgres cluster identity mismatch: ${path}. Preserve the data and ownership records.`);
6832
+ const mismatches = [
6833
+ ...record?.version !== 1 ? ["version"] : [],
6834
+ ...typeof record?.clusterId !== "string" || !/^[0-9a-f-]{36}$/.test(record.clusterId) ? ["clusterId"] : [],
6835
+ ...record?.dataDir !== dataDir ? ["dataDir"] : [],
6836
+ ...typeof record?.directoryIdentity !== "string" || directoryInode(record.directoryIdentity) !== String(stat.ino) ? ["inode"] : [],
6837
+ ...record?.major !== major ? ["major"] : [],
6838
+ ...record?.server !== undefined && (!Number.isInteger(record.server?.port) || record.server.port < 1 || record.server.port > 65535 || !Number.isSafeInteger(record.server.pid) || record.server.pid <= 0 || !Number.isSafeInteger(record.server.started) || record.server.started <= 0 || !/^\d+$/.test(record.server.systemIdentifier)) ? ["server"] : []
6839
+ ];
6840
+ const mismatch = (fields) => new Error(`Managed Postgres cluster identity mismatch (${fields.join(", ")}): ${path}. Preserve the data and ownership records.`);
6841
+ if (record === null || mismatches.length > 0)
6842
+ throw mismatch(mismatches);
6843
+ if (record.directoryIdentity === directoryIdentity)
6844
+ return record;
6845
+ if (record.server && controlSystemIdentifierOrUndefined(dataDir) !== record.server.systemIdentifier) {
6846
+ throw mismatch(["systemIdentifier"]);
6847
+ }
6848
+ return { ...record, directoryIdentity };
6849
+ }
6850
+ function controlSystemIdentifierOrUndefined(dataDir) {
6851
+ try {
6852
+ return controlFileSystemIdentifier(dataDir);
6853
+ } catch {
6854
+ return;
6816
6855
  }
6817
- return record;
6818
6856
  }
6819
6857
  function publishPostgresServer(baseDir, metadata, server) {
6820
6858
  const current = managedPostgresMetadata(baseDir, metadata.major, false);
@@ -6965,11 +7003,7 @@ function managedPostmaster(metadata) {
6965
7003
  return { pid, started, port, systemIdentifier };
6966
7004
  }
6967
7005
  function managedSystemIdentifier(metadata) {
6968
- const controlPath = join3(metadata.dataDir, "global", "pg_control");
6969
- if (!lstatSync2(controlPath).isFile())
6970
- throw new Error("Untrusted managed Postgres control file.");
6971
- const control = readFileSync2(controlPath);
6972
- const systemIdentifier = (endianness() === "LE" ? control.readBigUInt64LE(0) : control.readBigUInt64BE(0)).toString();
7006
+ const systemIdentifier = controlFileSystemIdentifier(metadata.dataDir);
6973
7007
  if (metadata.server && metadata.server.systemIdentifier !== systemIdentifier) {
6974
7008
  throw new Error("Managed Postgres system identity mismatch. Preserve the existing data.");
6975
7009
  }
@@ -7179,8 +7213,9 @@ async function ensureCluster(options = {}) {
7179
7213
  adoptedRegistry = postgresOwnershipDirectory(root, EMBEDDED_PG_MAJOR);
7180
7214
  if (options.recovery && !metadata)
7181
7215
  throw new Error("Managed Postgres recovery requires existing ownership records.");
7182
- if (options.recovery && (metadata?.clusterId !== options.recovery.clusterId || metadata.directoryIdentity !== options.recovery.directoryIdentity || metadata.server?.systemIdentifier !== options.recovery.server?.systemIdentifier)) {
7183
- throw new Error("Managed Postgres recovery identity changed while waiting for ownership.");
7216
+ const recoveryMismatches = options.recovery && metadata ? managedPostgresIdentityMismatches(options.recovery, metadata) : [];
7217
+ if (recoveryMismatches.length > 0) {
7218
+ throw new Error(`Managed Postgres recovery identity changed (${recoveryMismatches.join(", ")}) while waiting for ownership.`);
7184
7219
  }
7185
7220
  const existing = metadata && managedPostmaster(metadata);
7186
7221
  let port = existing?.port ?? metadata?.server?.port ?? preferredPort;
@@ -7395,8 +7430,9 @@ async function ensureCluster(options = {}) {
7395
7430
  };
7396
7431
  const inspect = async (probe = options.probeIdentity) => {
7397
7432
  const current = managedPostgresMetadata(root, EMBEDDED_PG_MAJOR, false);
7398
- if (current.clusterId !== pinned.clusterId || current.directoryIdentity !== pinned.directoryIdentity || current.server?.systemIdentifier !== pinned.server.systemIdentifier) {
7399
- throw new Error("Managed Postgres health identity mismatch. Preserve the data and ownership records.");
7433
+ const mismatches = managedPostgresIdentityMismatches(pinned, current);
7434
+ if (mismatches.length > 0) {
7435
+ throw new Error(`Managed Postgres health identity mismatch (${mismatches.join(", ")}). Preserve the data and ownership records.`);
7400
7436
  }
7401
7437
  const server = current.server;
7402
7438
  if (!server)
@@ -7456,8 +7492,9 @@ async function ensureCluster(options = {}) {
7456
7492
  async function stopBrokenManagedPostmaster(metadata, verified, pgCtl, context, lease, wait = delay, platform = process.platform) {
7457
7493
  const check = () => {
7458
7494
  const current = managedPostgresMetadata(context.baseDir, metadata.major, false);
7459
- if (current.clusterId !== metadata.clusterId || current.directoryIdentity !== metadata.directoryIdentity) {
7460
- throw new Error("Managed Postgres restart identity changed. Preserve the server and data directory.");
7495
+ const mismatches = managedPostgresIdentityMismatches(metadata, current, false);
7496
+ if (mismatches.length > 0) {
7497
+ throw new Error(`Managed Postgres restart identity changed (${mismatches.join(", ")}). Preserve the server and data directory.`);
7461
7498
  }
7462
7499
  return { current, process: verifyManagedPostmasterProcess(current, verified) };
7463
7500
  };
@@ -9279,6 +9316,7 @@ function reciprocalWorkflowRootRunId(runById, runId) {
9279
9316
  function workflowActivityNodeKey(runId, nodeId) {
9280
9317
  return `${runId}:${nodeId}`;
9281
9318
  }
9319
+ var endedStatuses = new Set(["completed", "cached", "failed", "cancelled"]);
9282
9320
 
9283
9321
  // dist/builtin/workflows/src/shared/workflow-observation-runtime.ts
9284
9322
  class WorkflowObservationRuntime {
@@ -9922,7 +9960,7 @@ function createPendingStageDeliveryStoreMethods(context) {
9922
9960
  const run = context.findRun(input.runId);
9923
9961
  if (run === undefined)
9924
9962
  return;
9925
- const { getAvailableDurableBackend } = await import("./chunk-bnz4knhy.js");
9963
+ const { getAvailableDurableBackend } = await import("./chunk-0d0pd4fz.js");
9926
9964
  const availableBackend = getAvailableDurableBackend();
9927
9965
  const backend = availableBackend === undefined ? undefined : durableBackendForRun(availableBackend, context.state.runs, input.runId);
9928
9966
  const receipt = await backend?.readSettledPendingStageMessage?.(input.runId, input.message.id);
@@ -37,7 +37,7 @@ import {
37
37
  parseFailureDiagnostics,
38
38
  summarizeReviewConvergence,
39
39
  reverify_consolidated_batch
40
- } from "./chunk-v4scsq3y.js";
40
+ } from "./chunk-119gt64q.js";
41
41
  import {
42
42
  VERIFICATION_SCALE
43
43
  } from "./chunk-n1910xc4.js";
@@ -39,7 +39,7 @@ import {
39
39
  reviewerFailureText,
40
40
  summarizeReviewConvergence,
41
41
  reverify_consolidated_batch
42
- } from "./chunk-v4scsq3y.js";
42
+ } from "./chunk-119gt64q.js";
43
43
  import {
44
44
  fold_usage
45
45
  } from "./chunk-tgt0s5e5.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  goal_default
3
- } from "./chunk-9ywkdsa9.js";
4
- import"./chunk-v4scsq3y.js";
3
+ } from "./chunk-s7y3hw0n.js";
4
+ import"./chunk-119gt64q.js";
5
5
  import"./chunk-2377r57g.js";
6
6
  import"./chunk-z0d03ktm.js";
7
7
  import"./chunk-n1910xc4.js";
@@ -16,8 +16,8 @@ import {
16
16
  } from "./chunk-kb7s3gx2.js";
17
17
  import {
18
18
  goal_default
19
- } from "./chunk-9ywkdsa9.js";
20
- import"./chunk-v4scsq3y.js";
19
+ } from "./chunk-s7y3hw0n.js";
20
+ import"./chunk-119gt64q.js";
21
21
  import"./chunk-z0d03ktm.js";
22
22
  import {
23
23
  open_claude_design_default
@@ -27,7 +27,7 @@ import {
27
27
  } from "./chunk-94f8zfz1.js";
28
28
  import {
29
29
  ralph_default
30
- } from "./chunk-dx5sjaxe.js";
30
+ } from "./chunk-y68758re.js";
31
31
  import {
32
32
  tournament_default
33
33
  } from "./chunk-wf876n16.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ralph_default
3
- } from "./chunk-dx5sjaxe.js";
4
- import"./chunk-v4scsq3y.js";
3
+ } from "./chunk-y68758re.js";
4
+ import"./chunk-119gt64q.js";
5
5
  import"./chunk-2377r57g.js";
6
6
  import"./chunk-z0d03ktm.js";
7
7
  import"./chunk-n1910xc4.js";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.32-alpha.1",
3
+ "version": "0.9.33-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [
@@ -38545,6 +38545,7 @@ import {
38545
38545
  rmSync,
38546
38546
  writeFileSync
38547
38547
  } from "node:fs";
38548
+ import { endianness } from "node:os";
38548
38549
  import { join as join3 } from "node:path";
38549
38550
  function trustedPath(path, directory = false) {
38550
38551
  const stat = lstatSync(path);
@@ -38555,6 +38556,23 @@ function trustedPath(path, directory = false) {
38555
38556
  function postgresOwnershipDirectory(baseDir, major) {
38556
38557
  return join3(baseDir, `v${major}.shared`);
38557
38558
  }
38559
+ function controlFileSystemIdentifier(dataDir) {
38560
+ const controlPath = join3(dataDir, "global", "pg_control");
38561
+ if (!lstatSync(controlPath).isFile())
38562
+ throw new Error("Untrusted managed Postgres control file.");
38563
+ const control = readFileSync2(controlPath);
38564
+ return (endianness() === "LE" ? control.readBigUInt64LE(0) : control.readBigUInt64BE(0)).toString();
38565
+ }
38566
+ function managedPostgresIdentityMismatches(expected, current, compareSystemIdentifier = true) {
38567
+ const inode = directoryInode(expected.directoryIdentity);
38568
+ return [
38569
+ current.clusterId !== expected.clusterId && "clusterId",
38570
+ current.dataDir !== expected.dataDir && "dataDir",
38571
+ (inode === undefined || inode !== directoryInode(current.directoryIdentity)) && "inode",
38572
+ current.major !== expected.major && "major",
38573
+ compareSystemIdentifier && current.server?.systemIdentifier !== expected.server?.systemIdentifier && "systemIdentifier"
38574
+ ].filter((field) => field !== false);
38575
+ }
38558
38576
  function managedPostgresMetadata(baseDir, major, create) {
38559
38577
  if (!lstatSync(join3(baseDir, `v${major}`)).isDirectory()) {
38560
38578
  throw new Error("Managed Postgres data must be a real directory, not a link.");
@@ -38588,10 +38606,30 @@ function managedPostgresMetadata(baseDir, major, create) {
38588
38606
  }
38589
38607
  trustedPath(path);
38590
38608
  const record = JSON.parse(readFileSync2(path, "utf8"));
38591
- if (record?.version !== 1 || typeof record.clusterId !== "string" || !/^[0-9a-f-]{36}$/.test(record.clusterId) || record.dataDir !== dataDir || record.directoryIdentity !== directoryIdentity || record.major !== major || record.server !== undefined && (!Number.isInteger(record.server?.port) || record.server.port < 1 || record.server.port > 65535 || !Number.isSafeInteger(record.server.pid) || record.server.pid <= 0 || !Number.isSafeInteger(record.server.started) || record.server.started <= 0 || !/^\d+$/.test(record.server.systemIdentifier))) {
38592
- throw new Error(`Managed Postgres cluster identity mismatch: ${path}. Preserve the data and ownership records.`);
38609
+ const mismatches = [
38610
+ ...record?.version !== 1 ? ["version"] : [],
38611
+ ...typeof record?.clusterId !== "string" || !/^[0-9a-f-]{36}$/.test(record.clusterId) ? ["clusterId"] : [],
38612
+ ...record?.dataDir !== dataDir ? ["dataDir"] : [],
38613
+ ...typeof record?.directoryIdentity !== "string" || directoryInode(record.directoryIdentity) !== String(stat.ino) ? ["inode"] : [],
38614
+ ...record?.major !== major ? ["major"] : [],
38615
+ ...record?.server !== undefined && (!Number.isInteger(record.server?.port) || record.server.port < 1 || record.server.port > 65535 || !Number.isSafeInteger(record.server.pid) || record.server.pid <= 0 || !Number.isSafeInteger(record.server.started) || record.server.started <= 0 || !/^\d+$/.test(record.server.systemIdentifier)) ? ["server"] : []
38616
+ ];
38617
+ const mismatch = (fields) => new Error(`Managed Postgres cluster identity mismatch (${fields.join(", ")}): ${path}. Preserve the data and ownership records.`);
38618
+ if (record === null || mismatches.length > 0)
38619
+ throw mismatch(mismatches);
38620
+ if (record.directoryIdentity === directoryIdentity)
38621
+ return record;
38622
+ if (record.server && controlSystemIdentifierOrUndefined(dataDir) !== record.server.systemIdentifier) {
38623
+ throw mismatch(["systemIdentifier"]);
38624
+ }
38625
+ return { ...record, directoryIdentity };
38626
+ }
38627
+ function controlSystemIdentifierOrUndefined(dataDir) {
38628
+ try {
38629
+ return controlFileSystemIdentifier(dataDir);
38630
+ } catch {
38631
+ return;
38593
38632
  }
38594
- return record;
38595
38633
  }
38596
38634
  function publishPostgresServer(baseDir, metadata, server) {
38597
38635
  const current = managedPostgresMetadata(baseDir, metadata.major, false);
@@ -38664,13 +38702,13 @@ function assertManagedPostmaster(metadata, port) {
38664
38702
  throw new Error("Managed Postgres postmaster identity does not match the shared data directory and port.");
38665
38703
  }
38666
38704
  }
38705
+ var directoryInode = (identity) => /^\d+:(\d+)$/.exec(identity)?.[1];
38667
38706
  var init_dbos_postgres_ownership = () => {};
38668
38707
 
38669
38708
  // dist/builtin/workflows/src/durable/dbos-postgres-identity.ts
38670
38709
  import { existsSync as existsSync3, lstatSync as lstatSync2, readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
38671
38710
  import { createRequire as createRequire2 } from "node:module";
38672
38711
  import { createServer } from "node:net";
38673
- import { endianness } from "node:os";
38674
38712
  import { dirname as dirname3, isAbsolute as isAbsolute2, join as join4 } from "node:path";
38675
38713
  function preferredPostgresPort(value2 = process.env.ATOMIC_POSTGRES_PORT) {
38676
38714
  if (value2 === undefined)
@@ -38748,11 +38786,7 @@ function managedPostmaster(metadata) {
38748
38786
  return { pid, started, port, systemIdentifier };
38749
38787
  }
38750
38788
  function managedSystemIdentifier(metadata) {
38751
- const controlPath = join4(metadata.dataDir, "global", "pg_control");
38752
- if (!lstatSync2(controlPath).isFile())
38753
- throw new Error("Untrusted managed Postgres control file.");
38754
- const control = readFileSync3(controlPath);
38755
- const systemIdentifier = (endianness() === "LE" ? control.readBigUInt64LE(0) : control.readBigUInt64BE(0)).toString();
38789
+ const systemIdentifier = controlFileSystemIdentifier(metadata.dataDir);
38756
38790
  if (metadata.server && metadata.server.systemIdentifier !== systemIdentifier) {
38757
38791
  throw new Error("Managed Postgres system identity mismatch. Preserve the existing data.");
38758
38792
  }
@@ -38963,8 +38997,9 @@ async function ensureCluster(options = {}) {
38963
38997
  adoptedRegistry = postgresOwnershipDirectory(root, EMBEDDED_PG_MAJOR);
38964
38998
  if (options.recovery && !metadata)
38965
38999
  throw new Error("Managed Postgres recovery requires existing ownership records.");
38966
- if (options.recovery && (metadata?.clusterId !== options.recovery.clusterId || metadata.directoryIdentity !== options.recovery.directoryIdentity || metadata.server?.systemIdentifier !== options.recovery.server?.systemIdentifier)) {
38967
- throw new Error("Managed Postgres recovery identity changed while waiting for ownership.");
39000
+ const recoveryMismatches = options.recovery && metadata ? managedPostgresIdentityMismatches(options.recovery, metadata) : [];
39001
+ if (recoveryMismatches.length > 0) {
39002
+ throw new Error(`Managed Postgres recovery identity changed (${recoveryMismatches.join(", ")}) while waiting for ownership.`);
38968
39003
  }
38969
39004
  const existing = metadata && managedPostmaster(metadata);
38970
39005
  let port = existing?.port ?? metadata?.server?.port ?? preferredPort;
@@ -39179,8 +39214,9 @@ async function ensureCluster(options = {}) {
39179
39214
  };
39180
39215
  const inspect = async (probe = options.probeIdentity) => {
39181
39216
  const current = managedPostgresMetadata(root, EMBEDDED_PG_MAJOR, false);
39182
- if (current.clusterId !== pinned.clusterId || current.directoryIdentity !== pinned.directoryIdentity || current.server?.systemIdentifier !== pinned.server.systemIdentifier) {
39183
- throw new Error("Managed Postgres health identity mismatch. Preserve the data and ownership records.");
39217
+ const mismatches = managedPostgresIdentityMismatches(pinned, current);
39218
+ if (mismatches.length > 0) {
39219
+ throw new Error(`Managed Postgres health identity mismatch (${mismatches.join(", ")}). Preserve the data and ownership records.`);
39184
39220
  }
39185
39221
  const server = current.server;
39186
39222
  if (!server)
@@ -39240,8 +39276,9 @@ async function ensureCluster(options = {}) {
39240
39276
  async function stopBrokenManagedPostmaster(metadata, verified, pgCtl, context, lease, wait = delay, platform = process.platform) {
39241
39277
  const check = () => {
39242
39278
  const current = managedPostgresMetadata(context.baseDir, metadata.major, false);
39243
- if (current.clusterId !== metadata.clusterId || current.directoryIdentity !== metadata.directoryIdentity) {
39244
- throw new Error("Managed Postgres restart identity changed. Preserve the server and data directory.");
39279
+ const mismatches = managedPostgresIdentityMismatches(metadata, current, false);
39280
+ if (mismatches.length > 0) {
39281
+ throw new Error(`Managed Postgres restart identity changed (${mismatches.join(", ")}). Preserve the server and data directory.`);
39245
39282
  }
39246
39283
  return { current, process: verifyManagedPostmasterProcess(current, verified) };
39247
39284
  };
@@ -77738,6 +77775,7 @@ function isStoppingRun(run, rootRunId, runs, stoppingRunIds) {
77738
77775
  }
77739
77776
  return stoppingRunIds.has(rootRunId);
77740
77777
  }
77778
+ var endedStatuses = new Set(["completed", "cached", "failed", "cancelled"]);
77741
77779
  function projectRoot(rootRunId, runs, runById, ownership) {
77742
77780
  let activeExecutionCount = 0;
77743
77781
  let humanWaits = 0;
@@ -77748,6 +77786,7 @@ function projectRoot(rootRunId, runs, runById, ownership) {
77748
77786
  let paused = false;
77749
77787
  let stoppingExecutionCount = 0;
77750
77788
  let independentContinuation = false;
77789
+ let childContinuation = false;
77751
77790
  for (const run of runs) {
77752
77791
  const runStopping = isStoppingRun(run, rootRunId, runById, ownership.stoppingRunIds);
77753
77792
  let runExecutionCount = (run.toolNodes ?? []).filter((tool) => ownership.executingToolNodeIds.has(workflowActivityNodeKey(run.id, tool.id))).length;
@@ -77762,7 +77801,17 @@ function projectRoot(rootRunId, runs, runById, ownership) {
77762
77801
  else
77763
77802
  manualWaits++;
77764
77803
  }
77765
- const statuses = new Map([...run.stages, ...run.toolNodes ?? []].map((node) => [node.id, node.status]));
77804
+ const statuses = new Map([...run.stages, ...run.toolNodes ?? []].map((node) => [
77805
+ node.id,
77806
+ endedStatuses.has(node.status) ? "ended" : node.status
77807
+ ]));
77808
+ const live = ownership.liveRunIds?.has(run.id) === true;
77809
+ for (const tool of run.toolNodes ?? []) {
77810
+ if (live && run.status === "running" && !runStopping && tool.status === "pending" && tool.parentIds.every((id) => statuses.get(id) === "ended")) {
77811
+ runnable = true;
77812
+ independentContinuation = true;
77813
+ }
77814
+ }
77766
77815
  for (const stage of run.stages) {
77767
77816
  if (run.status === "running" && stage.status === "paused")
77768
77817
  paused = true;
@@ -77779,20 +77828,29 @@ function projectRoot(rootRunId, runs, runById, ownership) {
77779
77828
  if (!runStopping)
77780
77829
  independentContinuation = true;
77781
77830
  }
77782
- if (run.status === "running" && !runStopping && stage.status === "pending" && stage.parentIds.every((id) => statuses.get(id) === "completed")) {
77831
+ if (run.status === "running" && !runStopping && stage.status === "pending" && stage.parentIds.every((id) => statuses.get(id) === "ended")) {
77783
77832
  runnable = true;
77784
77833
  independentContinuation = true;
77785
77834
  }
77786
77835
  }
77787
- if (ownership.liveRunIds?.has(run.id) && run.status === "running" && !runStopping && !run.pendingPrompt && run.blockedAt === undefined && run.failureDisposition !== "active_blocked" && run.stages.every((stage) => !stage.pendingPrompt && (stage.status === "completed" || stage.status === "skipped")) && (run.toolNodes ?? []).every((tool) => tool.status === "completed")) {
77788
- runnable = true;
77789
- independentContinuation = true;
77836
+ const childBoundary = (stage) => stage.status === "running" && stage.workflowChildRun !== undefined;
77837
+ if (live && run.status === "running" && !runStopping && !run.pendingPrompt && run.blockedAt === undefined && run.failureDisposition !== "active_blocked" && run.stages.every((stage) => !stage.pendingPrompt && (stage.status === "completed" || stage.status === "failed" || stage.status === "skipped" || childBoundary(stage))) && (run.toolNodes ?? []).every((tool) => endedStatuses.has(tool.status))) {
77838
+ if (run.stages.some(childBoundary))
77839
+ childContinuation = true;
77840
+ else {
77841
+ runnable = true;
77842
+ independentContinuation = true;
77843
+ }
77790
77844
  }
77791
77845
  activeExecutionCount += runExecutionCount;
77792
77846
  if (runStopping)
77793
77847
  stoppingExecutionCount += runExecutionCount;
77794
77848
  }
77795
77849
  const actionableBlockCount = humanWaits + manualWaits;
77850
+ if (childContinuation && actionableBlockCount === 0 && !paused) {
77851
+ runnable = true;
77852
+ independentContinuation = true;
77853
+ }
77796
77854
  let state = "idle";
77797
77855
  let reason = paused ? "paused" : "quiescent";
77798
77856
  if (activeExecutionCount > 0 || retrying || runnable) {
@@ -37030,6 +37030,7 @@ import {
37030
37030
  rmSync,
37031
37031
  writeFileSync
37032
37032
  } from "node:fs";
37033
+ import { endianness } from "node:os";
37033
37034
  import { join as join2 } from "node:path";
37034
37035
  function trustedPath(path, directory = false) {
37035
37036
  const stat = lstatSync(path);
@@ -37040,6 +37041,23 @@ function trustedPath(path, directory = false) {
37040
37041
  function postgresOwnershipDirectory(baseDir, major) {
37041
37042
  return join2(baseDir, `v${major}.shared`);
37042
37043
  }
37044
+ function controlFileSystemIdentifier(dataDir) {
37045
+ const controlPath = join2(dataDir, "global", "pg_control");
37046
+ if (!lstatSync(controlPath).isFile())
37047
+ throw new Error("Untrusted managed Postgres control file.");
37048
+ const control = readFileSync(controlPath);
37049
+ return (endianness() === "LE" ? control.readBigUInt64LE(0) : control.readBigUInt64BE(0)).toString();
37050
+ }
37051
+ function managedPostgresIdentityMismatches(expected, current, compareSystemIdentifier = true) {
37052
+ const inode = directoryInode(expected.directoryIdentity);
37053
+ return [
37054
+ current.clusterId !== expected.clusterId && "clusterId",
37055
+ current.dataDir !== expected.dataDir && "dataDir",
37056
+ (inode === undefined || inode !== directoryInode(current.directoryIdentity)) && "inode",
37057
+ current.major !== expected.major && "major",
37058
+ compareSystemIdentifier && current.server?.systemIdentifier !== expected.server?.systemIdentifier && "systemIdentifier"
37059
+ ].filter((field) => field !== false);
37060
+ }
37043
37061
  function managedPostgresMetadata(baseDir, major, create) {
37044
37062
  if (!lstatSync(join2(baseDir, `v${major}`)).isDirectory()) {
37045
37063
  throw new Error("Managed Postgres data must be a real directory, not a link.");
@@ -37073,10 +37091,30 @@ function managedPostgresMetadata(baseDir, major, create) {
37073
37091
  }
37074
37092
  trustedPath(path);
37075
37093
  const record = JSON.parse(readFileSync(path, "utf8"));
37076
- if (record?.version !== 1 || typeof record.clusterId !== "string" || !/^[0-9a-f-]{36}$/.test(record.clusterId) || record.dataDir !== dataDir || record.directoryIdentity !== directoryIdentity || record.major !== major || record.server !== undefined && (!Number.isInteger(record.server?.port) || record.server.port < 1 || record.server.port > 65535 || !Number.isSafeInteger(record.server.pid) || record.server.pid <= 0 || !Number.isSafeInteger(record.server.started) || record.server.started <= 0 || !/^\d+$/.test(record.server.systemIdentifier))) {
37077
- throw new Error(`Managed Postgres cluster identity mismatch: ${path}. Preserve the data and ownership records.`);
37094
+ const mismatches = [
37095
+ ...record?.version !== 1 ? ["version"] : [],
37096
+ ...typeof record?.clusterId !== "string" || !/^[0-9a-f-]{36}$/.test(record.clusterId) ? ["clusterId"] : [],
37097
+ ...record?.dataDir !== dataDir ? ["dataDir"] : [],
37098
+ ...typeof record?.directoryIdentity !== "string" || directoryInode(record.directoryIdentity) !== String(stat.ino) ? ["inode"] : [],
37099
+ ...record?.major !== major ? ["major"] : [],
37100
+ ...record?.server !== undefined && (!Number.isInteger(record.server?.port) || record.server.port < 1 || record.server.port > 65535 || !Number.isSafeInteger(record.server.pid) || record.server.pid <= 0 || !Number.isSafeInteger(record.server.started) || record.server.started <= 0 || !/^\d+$/.test(record.server.systemIdentifier)) ? ["server"] : []
37101
+ ];
37102
+ const mismatch = (fields) => new Error(`Managed Postgres cluster identity mismatch (${fields.join(", ")}): ${path}. Preserve the data and ownership records.`);
37103
+ if (record === null || mismatches.length > 0)
37104
+ throw mismatch(mismatches);
37105
+ if (record.directoryIdentity === directoryIdentity)
37106
+ return record;
37107
+ if (record.server && controlSystemIdentifierOrUndefined(dataDir) !== record.server.systemIdentifier) {
37108
+ throw mismatch(["systemIdentifier"]);
37109
+ }
37110
+ return { ...record, directoryIdentity };
37111
+ }
37112
+ function controlSystemIdentifierOrUndefined(dataDir) {
37113
+ try {
37114
+ return controlFileSystemIdentifier(dataDir);
37115
+ } catch {
37116
+ return;
37078
37117
  }
37079
- return record;
37080
37118
  }
37081
37119
  function publishPostgresServer(baseDir, metadata, server) {
37082
37120
  const current = managedPostgresMetadata(baseDir, metadata.major, false);
@@ -37149,13 +37187,13 @@ function assertManagedPostmaster(metadata, port) {
37149
37187
  throw new Error("Managed Postgres postmaster identity does not match the shared data directory and port.");
37150
37188
  }
37151
37189
  }
37190
+ var directoryInode = (identity) => /^\d+:(\d+)$/.exec(identity)?.[1];
37152
37191
  var init_dbos_postgres_ownership = () => {};
37153
37192
 
37154
37193
  // dist/builtin/workflows/src/durable/dbos-postgres-identity.ts
37155
37194
  import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
37156
37195
  import { createRequire as createRequire2 } from "node:module";
37157
37196
  import { createServer } from "node:net";
37158
- import { endianness } from "node:os";
37159
37197
  import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3 } from "node:path";
37160
37198
  function preferredPostgresPort(value = process.env.ATOMIC_POSTGRES_PORT) {
37161
37199
  if (value === undefined)
@@ -37233,11 +37271,7 @@ function managedPostmaster(metadata) {
37233
37271
  return { pid, started, port, systemIdentifier };
37234
37272
  }
37235
37273
  function managedSystemIdentifier(metadata) {
37236
- const controlPath = join3(metadata.dataDir, "global", "pg_control");
37237
- if (!lstatSync2(controlPath).isFile())
37238
- throw new Error("Untrusted managed Postgres control file.");
37239
- const control = readFileSync2(controlPath);
37240
- const systemIdentifier = (endianness() === "LE" ? control.readBigUInt64LE(0) : control.readBigUInt64BE(0)).toString();
37274
+ const systemIdentifier = controlFileSystemIdentifier(metadata.dataDir);
37241
37275
  if (metadata.server && metadata.server.systemIdentifier !== systemIdentifier) {
37242
37276
  throw new Error("Managed Postgres system identity mismatch. Preserve the existing data.");
37243
37277
  }
@@ -37448,8 +37482,9 @@ async function ensureCluster(options = {}) {
37448
37482
  adoptedRegistry = postgresOwnershipDirectory(root, EMBEDDED_PG_MAJOR);
37449
37483
  if (options.recovery && !metadata)
37450
37484
  throw new Error("Managed Postgres recovery requires existing ownership records.");
37451
- if (options.recovery && (metadata?.clusterId !== options.recovery.clusterId || metadata.directoryIdentity !== options.recovery.directoryIdentity || metadata.server?.systemIdentifier !== options.recovery.server?.systemIdentifier)) {
37452
- throw new Error("Managed Postgres recovery identity changed while waiting for ownership.");
37485
+ const recoveryMismatches = options.recovery && metadata ? managedPostgresIdentityMismatches(options.recovery, metadata) : [];
37486
+ if (recoveryMismatches.length > 0) {
37487
+ throw new Error(`Managed Postgres recovery identity changed (${recoveryMismatches.join(", ")}) while waiting for ownership.`);
37453
37488
  }
37454
37489
  const existing = metadata && managedPostmaster(metadata);
37455
37490
  let port = existing?.port ?? metadata?.server?.port ?? preferredPort;
@@ -37664,8 +37699,9 @@ async function ensureCluster(options = {}) {
37664
37699
  };
37665
37700
  const inspect = async (probe = options.probeIdentity) => {
37666
37701
  const current = managedPostgresMetadata(root, EMBEDDED_PG_MAJOR, false);
37667
- if (current.clusterId !== pinned.clusterId || current.directoryIdentity !== pinned.directoryIdentity || current.server?.systemIdentifier !== pinned.server.systemIdentifier) {
37668
- throw new Error("Managed Postgres health identity mismatch. Preserve the data and ownership records.");
37702
+ const mismatches = managedPostgresIdentityMismatches(pinned, current);
37703
+ if (mismatches.length > 0) {
37704
+ throw new Error(`Managed Postgres health identity mismatch (${mismatches.join(", ")}). Preserve the data and ownership records.`);
37669
37705
  }
37670
37706
  const server = current.server;
37671
37707
  if (!server)
@@ -37725,8 +37761,9 @@ async function ensureCluster(options = {}) {
37725
37761
  async function stopBrokenManagedPostmaster(metadata, verified, pgCtl, context, lease, wait = delay, platform = process.platform) {
37726
37762
  const check = () => {
37727
37763
  const current = managedPostgresMetadata(context.baseDir, metadata.major, false);
37728
- if (current.clusterId !== metadata.clusterId || current.directoryIdentity !== metadata.directoryIdentity) {
37729
- throw new Error("Managed Postgres restart identity changed. Preserve the server and data directory.");
37764
+ const mismatches = managedPostgresIdentityMismatches(metadata, current, false);
37765
+ if (mismatches.length > 0) {
37766
+ throw new Error(`Managed Postgres restart identity changed (${mismatches.join(", ")}). Preserve the server and data directory.`);
37730
37767
  }
37731
37768
  return { current, process: verifyManagedPostmasterProcess(current, verified) };
37732
37769
  };
@@ -40069,6 +40106,7 @@ function reciprocalWorkflowRootRunId(runById, runId) {
40069
40106
  function workflowActivityNodeKey(runId, nodeId) {
40070
40107
  return `${runId}:${nodeId}`;
40071
40108
  }
40109
+ var endedStatuses = new Set(["completed", "cached", "failed", "cancelled"]);
40072
40110
 
40073
40111
  // dist/builtin/workflows/src/shared/workflow-observation-runtime.ts
40074
40112
  class WorkflowObservationRuntime {