@bastani/atomic 0.9.26-alpha.4 → 0.9.26-alpha.5

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 (110) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/builtin/intercom/package.json +2 -2
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/package.json +3 -3
  5. package/dist/builtin/web-access/package.json +2 -2
  6. package/dist/builtin/workflows/CHANGELOG.md +10 -0
  7. package/dist/builtin/workflows/builtin/{chunk-73p2cm5s.js → chunk-613yaa53.js} +124 -43
  8. package/dist/builtin/workflows/builtin/{chunk-n95myjcg.js → chunk-6kqrk0pf.js} +1 -1
  9. package/dist/builtin/workflows/builtin/{chunk-ycvg8y8k.js → chunk-ea6nrbn3.js} +1 -1
  10. package/dist/builtin/workflows/builtin/goal.js +2 -2
  11. package/dist/builtin/workflows/builtin/index.js +3 -3
  12. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  13. package/dist/builtin/workflows/package.json +2 -2
  14. package/dist/builtin/workflows/src/extension/index.bundle.mjs +317 -62
  15. package/dist/builtin/workflows/src/index.js +176 -62
  16. package/dist/core/agent-session-export.js +1 -1
  17. package/dist/core/agent-session-export.js.map +1 -1
  18. package/dist/core/export-html/tool-renderer.d.ts +3 -3
  19. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  20. package/dist/core/export-html/tool-renderer.js +3 -3
  21. package/dist/core/export-html/tool-renderer.js.map +1 -1
  22. package/dist/core/extensions/api-types.d.ts +3 -1
  23. package/dist/core/extensions/api-types.d.ts.map +1 -1
  24. package/dist/core/extensions/api-types.js.map +1 -1
  25. package/dist/core/extensions/index.d.ts +1 -1
  26. package/dist/core/extensions/index.d.ts.map +1 -1
  27. package/dist/core/extensions/index.js.map +1 -1
  28. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  29. package/dist/core/extensions/loader-api.js +5 -0
  30. package/dist/core/extensions/loader-api.js.map +1 -1
  31. package/dist/core/extensions/loader-bindings.d.ts +1 -1
  32. package/dist/core/extensions/loader-bindings.d.ts.map +1 -1
  33. package/dist/core/extensions/loader-bindings.js +1 -0
  34. package/dist/core/extensions/loader-bindings.js.map +1 -1
  35. package/dist/core/extensions/runner.d.ts +3 -1
  36. package/dist/core/extensions/runner.d.ts.map +1 -1
  37. package/dist/core/extensions/runner.js +6 -0
  38. package/dist/core/extensions/runner.js.map +1 -1
  39. package/dist/core/extensions/runtime-types.d.ts +2 -1
  40. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  41. package/dist/core/extensions/runtime-types.js.map +1 -1
  42. package/dist/core/extensions/tool-types.d.ts +3 -0
  43. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  44. package/dist/core/extensions/tool-types.js.map +1 -1
  45. package/dist/core/extensions/ui-types.d.ts +2 -2
  46. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  47. package/dist/core/extensions/ui-types.js.map +1 -1
  48. package/dist/core/model-resolver-defaults.js +1 -1
  49. package/dist/core/model-resolver-defaults.js.map +1 -1
  50. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  51. package/dist/core/package-manager-auto-resources.js +6 -2
  52. package/dist/core/package-manager-auto-resources.js.map +1 -1
  53. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  54. package/dist/core/package-manager-resolver.js +4 -2
  55. package/dist/core/package-manager-resolver.js.map +1 -1
  56. package/dist/core/settings-manager-resource-accessors.js +7 -3
  57. package/dist/core/settings-manager-resource-accessors.js.map +1 -1
  58. package/dist/core/settings-merge.d.ts.map +1 -1
  59. package/dist/core/settings-merge.js +10 -0
  60. package/dist/core/settings-merge.js.map +1 -1
  61. package/dist/core/settings-types.d.ts +7 -1
  62. package/dist/core/settings-types.d.ts.map +1 -1
  63. package/dist/core/settings-types.js.map +1 -1
  64. package/dist/extensions/mcp/index.d.ts.map +1 -1
  65. package/dist/extensions/mcp/index.js +20 -1
  66. package/dist/extensions/mcp/index.js.map +1 -1
  67. package/dist/extensions/mcp/tools.d.ts +3 -1
  68. package/dist/extensions/mcp/tools.d.ts.map +1 -1
  69. package/dist/extensions/mcp/tools.js +19 -13
  70. package/dist/extensions/mcp/tools.js.map +1 -1
  71. package/dist/index.d.ts +2 -2
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js.map +1 -1
  74. package/dist/modes/interactive/components/chat-message-renderer.d.ts +2 -3
  75. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  76. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  77. package/dist/modes/interactive/components/config-selector-list.d.ts.map +1 -1
  78. package/dist/modes/interactive/components/config-selector-list.js +3 -1
  79. package/dist/modes/interactive/components/config-selector-list.js.map +1 -1
  80. package/dist/modes/interactive/components/config-selector-project-scope.d.ts.map +1 -1
  81. package/dist/modes/interactive/components/config-selector-project-scope.js +2 -1
  82. package/dist/modes/interactive/components/config-selector-project-scope.js.map +1 -1
  83. package/dist/modes/interactive/components/tool-execution.d.ts +2 -5
  84. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  85. package/dist/modes/interactive/components/tool-execution.js +14 -40
  86. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  87. package/dist/modes/interactive/interactive-mode-surface.d.ts +3 -3
  88. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  89. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  90. package/dist/modes/interactive/interactive-session-runtime.js +6 -1
  91. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  92. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  93. package/dist/modes/interactive/interactive-startup.js +5 -0
  94. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  95. package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
  96. package/dist/modes/interactive-engine/engine-custom-ui.js +9 -0
  97. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  98. package/dist/modes/interactive-engine/engine-render-service.d.ts.map +1 -1
  99. package/dist/modes/interactive-engine/engine-render-service.js +10 -1
  100. package/dist/modes/interactive-engine/engine-render-service.js.map +1 -1
  101. package/dist/utils/image-convert.d.ts +5 -0
  102. package/dist/utils/image-convert.d.ts.map +1 -1
  103. package/dist/utils/image-convert.js +36 -6
  104. package/dist/utils/image-convert.js.map +1 -1
  105. package/docs/extensions.md +6 -0
  106. package/docs/providers.md +10 -0
  107. package/docs/settings.md +28 -1
  108. package/docs/workflows/operations.md +25 -3
  109. package/npm-shrinkwrap.json +71 -92
  110. package/package.json +8 -8
@@ -76,6 +76,92 @@ function raceAbort(operation, signal) {
76
76
  });
77
77
  }
78
78
 
79
+ // dist/builtin/workflows/src/durable/dbos-database-diagnostics.ts
80
+ function redactedDatabaseEndpoint(url) {
81
+ try {
82
+ const parsed = new URL(url);
83
+ parsed.username = "";
84
+ parsed.password = "";
85
+ parsed.search = "";
86
+ parsed.hash = "";
87
+ return parsed.toString();
88
+ } catch {
89
+ return "unparseable PostgreSQL endpoint";
90
+ }
91
+ }
92
+ function redactedDatabaseMessage(message, url) {
93
+ let safe = message.replace(/postgres(?:ql)?:\/\/[^\s"'<>]+/gi, (value2) => redactedDatabaseEndpoint(value2));
94
+ let configuredSecrets = [];
95
+ if (url !== undefined) {
96
+ try {
97
+ const parsed = new URL(url);
98
+ configuredSecrets = [parsed.username, parsed.password, ...parsed.searchParams.values()].flatMap((secret) => {
99
+ try {
100
+ return [secret, decodeURIComponent(secret)];
101
+ } catch {
102
+ return [secret];
103
+ }
104
+ }).filter((secret) => secret.length > 0);
105
+ } catch {
106
+ return "PostgreSQL diagnostic could not be safely redacted; check the configured endpoint.";
107
+ }
108
+ }
109
+ safe = safe.replace(/\b(password|passwd|token|secret|sslpassword|user(?:name)?)\s*(?:([=:])\s*|((?:is|was)\s+)|\s+)(?:"([^"]*)"|'([^']*)'|([^\s,;]+))/gi, (match, key, separator, filler, doubleQuoted, singleQuoted, bare) => {
110
+ const value2 = doubleQuoted ?? singleQuoted ?? bare ?? "";
111
+ const isUserContext = /^user(?:name)?$/i.test(key);
112
+ if (separator === undefined && !isUserContext && !configuredSecrets.includes(value2))
113
+ return match;
114
+ const rendered = doubleQuoted !== undefined ? '"[redacted]"' : singleQuoted !== undefined ? "'[redacted]'" : "[redacted]";
115
+ return `${key}${separator ?? " "}${filler ?? ""}${rendered}`;
116
+ });
117
+ for (const secret of new Set(configuredSecrets)) {
118
+ if (secret.length < 4)
119
+ continue;
120
+ const escaped = secret.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
121
+ const expression = new RegExp(`(?<![\\p{L}\\p{N}_\\[])${escaped}(?![\\p{L}\\p{N}_\\]])`, "gu");
122
+ safe = safe.replace(expression, "[redacted]");
123
+ }
124
+ return safe;
125
+ }
126
+ function databaseDependencyMessage(message, diagnostics) {
127
+ const safe = redactedDatabaseMessage(message, diagnostics?.url);
128
+ if (diagnostics === undefined)
129
+ return safe;
130
+ const failure = diagnostics.failure === undefined ? "" : ` Last probe/recovery failure: ${redactedDatabaseMessage(diagnostics.failure, diagnostics.url)}`;
131
+ return `${safe} Provider: ${diagnostics.provider}; endpoint: ${redactedDatabaseEndpoint(diagnostics.url)}.${failure}`;
132
+ }
133
+
134
+ // dist/builtin/workflows/src/durable/dbos-process-owner.ts
135
+ function emptyOwner2() {
136
+ return {
137
+ version: 1,
138
+ state: "uninitialized",
139
+ configured: undefined,
140
+ active: undefined,
141
+ launchPromise: undefined,
142
+ shutdownPromise: undefined,
143
+ failure: undefined,
144
+ wrappers: undefined,
145
+ leases: new Set
146
+ };
147
+ }
148
+ function ownerBag2() {
149
+ return globalThis;
150
+ }
151
+ function getDbosProcessOwner() {
152
+ const bag = ownerBag2();
153
+ const existing = bag[DBOS_PROCESS_OWNER_KEY];
154
+ if (existing !== undefined && existing.version === 1)
155
+ return existing;
156
+ const created = emptyOwner2();
157
+ bag[DBOS_PROCESS_OWNER_KEY] = created;
158
+ return created;
159
+ }
160
+ var DBOS_PROCESS_OWNER_KEY;
161
+ var init_dbos_process_owner = __esm(() => {
162
+ DBOS_PROCESS_OWNER_KEY = Symbol.for("atomic-workflows/dbos-process-owner@1");
163
+ });
164
+
79
165
  // dist/builtin/workflows/src/durable/dbos-admission.ts
80
166
  import { AsyncLocalStorage } from "node:async_hooks";
81
167
  function isDbosDependencyError(error) {
@@ -94,6 +180,8 @@ async function boundedAdmission(operation, signal, timeoutMs = DBOS_ADMISSION_TI
94
180
  return await raceAbort(operation(controller.signal), controller.signal);
95
181
  } catch (error) {
96
182
  controller.abort(error);
183
+ if (isDbosDependencyError(error) && error.admissionDetail !== undefined)
184
+ throw new DbosDependencyError(error.admissionDetail, null);
97
185
  throw error;
98
186
  } finally {
99
187
  clearTimeout(timer);
@@ -102,10 +190,13 @@ async function boundedAdmission(operation, signal, timeoutMs = DBOS_ADMISSION_TI
102
190
  }
103
191
  var DBOS_ADMISSION_TIMEOUT_MS = 1e4, DbosDependencyError, key2, bag, dbosAdmissionContext;
104
192
  var init_dbos_admission = __esm(() => {
193
+ init_dbos_process_owner();
105
194
  DbosDependencyError = class DbosDependencyError extends Error {
106
195
  code = "ATOMIC_DBOS_DEPENDENCY";
107
- constructor(message = "Workflow database unavailable during admission. Restore PostgreSQL before retrying.") {
108
- super(message);
196
+ admissionDetail;
197
+ constructor(message = "Workflow database unavailable during admission. Restore PostgreSQL before retrying.", diagnostics = getDbosProcessOwner().databaseDiagnostics?.(), admissionDetail) {
198
+ super(databaseDependencyMessage(message, diagnostics ?? undefined));
199
+ this.admissionDetail = admissionDetail === undefined ? undefined : redactedDatabaseMessage(admissionDetail);
109
200
  this.name = "DbosDependencyError";
110
201
  }
111
202
  };
@@ -30276,11 +30367,11 @@ function fenceDbosAdmissionPool(pool) {
30276
30367
  const connect = pool.connect.bind(pool);
30277
30368
  async function acquire(signal) {
30278
30369
  if (signal.aborted)
30279
- throw new DbosDependencyError;
30370
+ throw sdkDependencyError();
30280
30371
  const pending = connect().then((client) => {
30281
30372
  if (signal.aborted) {
30282
30373
  client.release(true);
30283
- throw new DbosDependencyError;
30374
+ throw sdkDependencyError();
30284
30375
  }
30285
30376
  return fencedClient(client, signal);
30286
30377
  });
@@ -30324,7 +30415,7 @@ function fencedClient(client, signal) {
30324
30415
  const callback = typeof last === "function" ? values.pop() : undefined;
30325
30416
  const pending = (async () => {
30326
30417
  if (signal.aborted || released)
30327
- throw new DbosDependencyError;
30418
+ throw sdkDependencyError();
30328
30419
  try {
30329
30420
  return await raceAbort(new Promise((resolve, reject) => {
30330
30421
  Reflect.apply(target, client, [
@@ -30356,9 +30447,15 @@ function fencedClient(client, signal) {
30356
30447
  }
30357
30448
  });
30358
30449
  }
30450
+ function sdkDependencyError(message) {
30451
+ const reported = message === undefined ? new DbosDependencyError : new DbosDependencyError(message, null);
30452
+ return new DbosDependencyError(undefined, null, reported.message);
30453
+ }
30359
30454
  function databaseError(error, signal) {
30360
30455
  if (signal.aborted)
30361
- return new DbosDependencyError;
30456
+ return sdkDependencyError();
30457
+ if (isDbosDependencyError(error))
30458
+ return sdkDependencyError(error.admissionDetail ?? error.message);
30362
30459
  const pending = [error];
30363
30460
  const seen = new Set;
30364
30461
  while (pending.length > 0) {
@@ -30375,7 +30472,7 @@ function databaseError(error, signal) {
30375
30472
  const code = "code" in value2 && typeof value2.code === "string" ? value2.code : undefined;
30376
30473
  const message = value2 instanceof Error ? value2.message : "";
30377
30474
  if (code !== undefined && (/^(?:08|53|57)[0-9A-Z]{3}$|^40003$/.test(code) || connectionErrnos.has(code)) || code === undefined && connectionMessages.has(message)) {
30378
- return new DbosDependencyError;
30475
+ return sdkDependencyError();
30379
30476
  }
30380
30477
  }
30381
30478
  return error;
@@ -30398,7 +30495,9 @@ var init_dbos_admission_pool = __esm(() => {
30398
30495
  "Connection terminated unexpectedly",
30399
30496
  "Connection terminated due to connection timeout",
30400
30497
  "Client has encountered a connection error and is not queryable",
30401
- "timeout exceeded when trying to connect"
30498
+ "timeout exceeded when trying to connect",
30499
+ "Client was closed and is not queryable",
30500
+ "Postgres connection pool changed while acquiring a connection"
30402
30501
  ]);
30403
30502
  });
30404
30503
 
@@ -30686,6 +30785,11 @@ function defaultDatabaseUrl(name) {
30686
30785
  function configureAdmissionDatabase(sdk, config, launchLock = withDbosLaunchLock) {
30687
30786
  const systemDatabaseUrl = config.systemDatabaseUrl ?? defaultDatabaseUrl(config.name);
30688
30787
  const health = resolvedPostgresHealth(systemDatabaseUrl);
30788
+ getDbosProcessOwner().databaseDiagnostics = () => ({
30789
+ provider: health === undefined ? config.systemDatabaseUrl === undefined ? "docker" : "configured" : "managed",
30790
+ url: health?.endpoint ?? systemDatabaseUrl,
30791
+ failure: health?.lastFailure?.message
30792
+ });
30689
30793
  const createPool = () => {
30690
30794
  let unsubscribe;
30691
30795
  const managed = createRecoverablePostgresPool(systemDatabaseUrl, {
@@ -30723,6 +30827,7 @@ var import_datasource2;
30723
30827
  var init_dbos_admission_config = __esm(() => {
30724
30828
  init_dbos_admission_pool();
30725
30829
  init_dbos_launch_lock();
30830
+ init_dbos_process_owner();
30726
30831
  init_dbos_recoverable_pool();
30727
30832
  import_datasource2 = __toESM(require_datasource(), 1);
30728
30833
  });
@@ -85896,33 +86001,8 @@ function classifyDbosDeletionTombstone(records, workflowId) {
85896
86001
  return isCurrentDurableFormat(raw.version) ? "current" : "unknown";
85897
86002
  }
85898
86003
 
85899
- // dist/builtin/workflows/src/durable/dbos-process-owner.ts
85900
- var DBOS_PROCESS_OWNER_KEY = Symbol.for("atomic-workflows/dbos-process-owner@1");
85901
- function emptyOwner2() {
85902
- return {
85903
- version: 1,
85904
- state: "uninitialized",
85905
- configured: undefined,
85906
- active: undefined,
85907
- launchPromise: undefined,
85908
- shutdownPromise: undefined,
85909
- failure: undefined,
85910
- wrappers: undefined,
85911
- leases: new Set
85912
- };
85913
- }
85914
- function ownerBag2() {
85915
- return globalThis;
85916
- }
85917
- function getDbosProcessOwner() {
85918
- const bag = ownerBag2();
85919
- const existing = bag[DBOS_PROCESS_OWNER_KEY];
85920
- if (existing !== undefined && existing.version === 1)
85921
- return existing;
85922
- const created = emptyOwner2();
85923
- bag[DBOS_PROCESS_OWNER_KEY] = created;
85924
- return created;
85925
- }
86004
+ // dist/builtin/workflows/src/durable/dbos-backend.ts
86005
+ init_dbos_process_owner();
85926
86006
 
85927
86007
  // dist/builtin/workflows/src/durable/dbos-sdk-handle.ts
85928
86008
  var ATOMIC_EXECUTOR_ID = `atomic-${process.pid.toString(36)}-${crypto.randomUUID().slice(0, 8)}`;
@@ -86043,6 +86123,7 @@ function statusToInfo(status, fallbackId) {
86043
86123
  }
86044
86124
 
86045
86125
  // dist/builtin/workflows/src/durable/dbos-system-database-url.ts
86126
+ init_dbos_process_owner();
86046
86127
  function nonEmptyTrimmed(value2) {
86047
86128
  const trimmed = value2?.trim();
86048
86129
  return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;
@@ -86057,9 +86138,7 @@ class DbosSystemDatabaseConflictError extends Error {
86057
86138
  function redactDatabaseUrl(url) {
86058
86139
  try {
86059
86140
  const parsed = new URL(url);
86060
- if (parsed.password !== "")
86061
- parsed.password = "***";
86062
- return parsed.toString();
86141
+ return parsed.host;
86063
86142
  } catch {
86064
86143
  return "the requested database";
86065
86144
  }
@@ -87776,6 +87855,7 @@ class PostgresHealth {
87776
87855
  attempts = 0;
87777
87856
  nextRecoveryAt = 0;
87778
87857
  failure;
87858
+ healthySinceFailure = false;
87779
87859
  revision = 0;
87780
87860
  listeners = new Set;
87781
87861
  constructor(operations) {
@@ -87784,6 +87864,18 @@ class PostgresHealth {
87784
87864
  get lastFailure() {
87785
87865
  return this.failure;
87786
87866
  }
87867
+ get endpoint() {
87868
+ return this.operations.url?.();
87869
+ }
87870
+ retainFailure(error) {
87871
+ const message = error instanceof Error ? error.message : String(error);
87872
+ const safe = redactedDatabaseMessage(message, this.endpoint);
87873
+ this.failure = new Error(safe);
87874
+ this.healthySinceFailure = false;
87875
+ }
87876
+ dependencyFailure(message) {
87877
+ return new DbosDependencyError(message, this.endpoint === undefined ? undefined : { provider: "managed", url: this.endpoint, failure: this.failure?.message });
87878
+ }
87787
87879
  subscribe(listener) {
87788
87880
  this.listeners.add(listener);
87789
87881
  return () => this.listeners.delete(listener);
@@ -87806,12 +87898,15 @@ class PostgresHealth {
87806
87898
  await this.operations.validate?.(client);
87807
87899
  }
87808
87900
  } catch (error) {
87809
- this.failure = error instanceof Error ? error : new Error(String(error));
87901
+ this.retainFailure(error);
87810
87902
  if (!isQueryReadTimeout(error)) {
87811
87903
  this.invalidate();
87812
- throw error;
87904
+ if (this.endpoint === undefined)
87905
+ throw error;
87906
+ const failure = this.dependencyFailure("Managed PostgreSQL connection validation refused. Preserve its data and ownership records.");
87907
+ throw error instanceof DbosDependencyError ? failure : new Error(failure.message);
87813
87908
  }
87814
- throw new DbosDependencyError("Managed PostgreSQL did not answer a connection identity check in time.");
87909
+ throw this.dependencyFailure("Managed PostgreSQL did not answer a connection identity check in time.");
87815
87910
  }
87816
87911
  }
87817
87912
  check() {
@@ -87852,7 +87947,7 @@ class PostgresHealth {
87852
87947
  } catch (retryError) {
87853
87948
  if (!isMonitoringConnectionFailure(retryError))
87854
87949
  throw retryError;
87855
- this.failure = retryError;
87950
+ this.retainFailure(retryError);
87856
87951
  return;
87857
87952
  }
87858
87953
  }
@@ -87867,6 +87962,7 @@ class PostgresHealth {
87867
87962
  this.available = identity;
87868
87963
  this.attempts = 0;
87869
87964
  this.nextRecoveryAt = 0;
87965
+ this.healthySinceFailure = true;
87870
87966
  return identity.url;
87871
87967
  }
87872
87968
  async refresh() {
@@ -87876,19 +87972,21 @@ class PostgresHealth {
87876
87972
  return ready;
87877
87973
  } catch (error) {
87878
87974
  if (isQueryReadTimeout(error)) {
87879
- this.failure = new DbosDependencyError("Managed PostgreSQL did not answer a health check in time.");
87880
- throw this.failure;
87975
+ this.retainFailure(new Error("Managed PostgreSQL did not answer a health check in time."));
87976
+ throw this.dependencyFailure("Managed PostgreSQL did not answer a health check in time.");
87881
87977
  }
87882
- this.failure = error instanceof Error ? error : new Error(String(error));
87978
+ this.retainFailure(error);
87883
87979
  if (!(error instanceof Error && ("code" in error) && error.code === "53300"))
87884
87980
  this.invalidate();
87885
- throw error;
87981
+ throw this.endpoint === undefined ? error : this.dependencyFailure("Managed PostgreSQL recovery refused after a health probe failure. Preserve its data and ownership records.");
87982
+ }
87983
+ if (this.attempts === 0 && (this.failure === undefined || this.healthySinceFailure)) {
87984
+ this.retainFailure(new Error("Managed PostgreSQL failed its live health check."));
87985
+ this.healthySinceFailure = false;
87886
87986
  }
87887
- if (this.attempts === 0)
87888
- this.failure = new DbosDependencyError("Managed PostgreSQL failed its live health check.");
87889
87987
  this.invalidate();
87890
87988
  if ((this.operations.now ?? Date.now)() < this.nextRecoveryAt)
87891
- throw new DbosDependencyError("Managed Postgres recovery is cooling down after bounded attempts.");
87989
+ throw this.dependencyFailure("Managed Postgres recovery is cooling down after bounded attempts.");
87892
87990
  while (!this.stopped && this.attempts < RECOVERY_ATTEMPTS) {
87893
87991
  const attempt = this.attempts++;
87894
87992
  if (attempt > 0)
@@ -87901,12 +87999,12 @@ class PostgresHealth {
87901
87999
  if (ready !== undefined)
87902
88000
  return ready;
87903
88001
  } catch (error) {
87904
- this.failure = error instanceof Error ? error : new Error(String(error));
88002
+ this.retainFailure(error);
87905
88003
  }
87906
88004
  }
87907
88005
  this.attempts = 0;
87908
88006
  this.nextRecoveryAt = (this.operations.now ?? Date.now)() + HEALTH_INTERVAL_MS;
87909
- throw new DbosDependencyError("Managed Postgres is unavailable after bounded recovery. Preserve its data and ownership records.");
88007
+ throw this.dependencyFailure("Managed Postgres is unavailable after bounded recovery. Preserve its data and ownership records.");
87910
88008
  }
87911
88009
  }
87912
88010
 
@@ -88562,6 +88660,8 @@ async function ensureCluster(options = {}) {
88562
88660
  const server = current.server;
88563
88661
  if (!server)
88564
88662
  throw new Error("Managed Postgres published identity is missing.");
88663
+ if (managedPostmaster(current) === undefined)
88664
+ return;
88565
88665
  if (!managedPostgresRuntimeHealthy(current, server.port)) {
88566
88666
  verifyManagedPostmasterProcess(current, server);
88567
88667
  return;
@@ -88578,6 +88678,7 @@ async function ensureCluster(options = {}) {
88578
88678
  };
88579
88679
  health = new PostgresHealth({
88580
88680
  probe: () => inspect(),
88681
+ url: embeddedDbosSystemDatabaseUrl,
88581
88682
  validate: async (client) => {
88582
88683
  const identity = await inspect(async () => {
88583
88684
  const query = { text: POSTGRES_IDENTITY_SQL, query_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS };
@@ -89665,6 +89766,9 @@ async function requireDockerSuccess(action, args) {
89665
89766
  throw new Error(`Could not ${action}: ${commandFailureDetail(result)}`);
89666
89767
  }
89667
89768
 
89769
+ // dist/builtin/workflows/src/durable/dbos-lifecycle.ts
89770
+ init_dbos_process_owner();
89771
+
89668
89772
  // dist/builtin/workflows/src/durable/dbos-registration-diagnostics.ts
89669
89773
  var cachedErrorModule;
89670
89774
  var errorModuleLoad;
@@ -94385,6 +94489,9 @@ function registerWorkflowLifecycleHandlers(pi, deps) {
94385
94489
  });
94386
94490
  }
94387
94491
 
94492
+ // dist/builtin/workflows/src/extension/extension-runtime-state.ts
94493
+ init_dbos_process_owner();
94494
+
94388
94495
  // dist/builtin/workflows/src/durable/workflow-heartbeat-anchor.ts
94389
94496
  var WORKFLOW_HEARTBEAT_ANCHOR_CHECKPOINT_NAME = "workflow-heartbeat-anchor";
94390
94497
  function asPersistedAnchorOutput(output) {
@@ -102532,6 +102639,7 @@ function hasInvocationData(checkpoints, replayKey) {
102532
102639
 
102533
102640
  // dist/builtin/workflows/src/engine/run.ts
102534
102641
  init_dbos_admission();
102642
+ init_dbos_process_owner();
102535
102643
 
102536
102644
  // dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts
102537
102645
  import { isAbsolute as isAbsolute12, join as join28, resolve as resolve12 } from "node:path";
@@ -114202,7 +114310,7 @@ async function run(def, inputs, opts = {}) {
114202
114310
  if (control === latestControl && !ownController.signal.aborted && isDbosDependencyError(error)) {
114203
114311
  activeStore.recordRunExecutionState(runId, {
114204
114312
  phase: "blocked_dependency",
114205
- dependencyError: `Workflow database unavailable during ${status === "paused" ? "pause" : "resume"}; persistence not confirmed.`
114313
+ dependencyError: `Workflow database unavailable during ${status === "paused" ? "pause" : "resume"}; persistence not confirmed. ${error.message}`
114206
114314
  });
114207
114315
  if (status === "paused")
114208
114316
  return;
@@ -114249,7 +114357,7 @@ async function run(def, inputs, opts = {}) {
114249
114357
  if (!admission.admitting)
114250
114358
  return pausePersistence;
114251
114359
  backgroundAdmissionControl(durableBackend, runId, pausePersistence, (error, resumable) => {
114252
- admissionControlError = unknownErrorMessage(error);
114360
+ admissionControlError = databaseDependencyMessage(unknownErrorMessage(error), getDbosProcessOwner().databaseDiagnostics?.());
114253
114361
  runSnapshot.error = admissionControlError;
114254
114362
  activeStore.recordRunPaused(runId, undefined, { resumable });
114255
114363
  return runSnapshot.error;
@@ -114266,7 +114374,7 @@ async function run(def, inputs, opts = {}) {
114266
114374
  } catch (error) {
114267
114375
  if (admission.failed) {
114268
114376
  scheduler.releaseRun();
114269
- throw error;
114377
+ throw new Error(admissionControlError ?? databaseDependencyMessage(unknownErrorMessage(error), getDbosProcessOwner().databaseDiagnostics?.()));
114270
114378
  }
114271
114379
  }
114272
114380
  if (generation !== pauseGeneration)
@@ -114339,12 +114447,21 @@ async function run(def, inputs, opts = {}) {
114339
114447
  }).catch((error) => {
114340
114448
  const isTopologyError = typeof error === "object" && error !== null && "name" in error && error.name === "DurableNestedTopologyError";
114341
114449
  if (!isDbosDependencyError(error) && !isTopologyError) {
114342
- durableAdmissionFailure = { error };
114343
- classifiedFailures.set(error, classifyWorkflowFailure({
114450
+ const admissionDetail = unknownErrorMessage(error);
114451
+ const diagnostics = getDbosProcessOwner().databaseDiagnostics?.();
114452
+ const safeMessage = databaseDependencyMessage(admissionDetail, diagnostics);
114453
+ activeStore.recordRunExecutionState(runId, {
114454
+ phase: "blocked_dependency",
114455
+ dependencyError: safeMessage
114456
+ });
114457
+ const safeError = new Error(safeMessage);
114458
+ durableAdmissionFailure = { error: safeError };
114459
+ classifiedFailures.set(safeError, classifyWorkflowFailure({
114344
114460
  code: "ATOMIC_DURABLE_ADMISSION_REJECTED",
114345
- message: unknownErrorMessage(error),
114346
- cause: error
114461
+ message: safeMessage,
114462
+ cause: safeError
114347
114463
  }));
114464
+ throw safeError;
114348
114465
  }
114349
114466
  throw error;
114350
114467
  }), ownController.signal);
@@ -115654,7 +115771,11 @@ function createExtensionRuntime(opts = {}) {
115654
115771
  const resolvePossibleStageEntry = opts.resolvePossibleStageEntry;
115655
115772
  const beforeRestoreCompleted = opts.beforeRestoreCompleted;
115656
115773
  const ensureDbosReady = async () => {
115657
- return await initializeDurableBackend(durabilityWarningSink);
115774
+ if (opts.prepareDatabase !== undefined)
115775
+ await opts.prepareDatabase();
115776
+ const backend = await initializeDurableBackend(durabilityWarningSink);
115777
+ opts.databaseReady?.();
115778
+ return backend;
115658
115779
  };
115659
115780
  function runOptions(policy) {
115660
115781
  const defaultSessionDir = resolveDefaultStageSessionDir?.();
@@ -115925,6 +116046,95 @@ function createStatusWriter(store, config, opts = {}) {
115925
116046
  };
115926
116047
  }
115927
116048
 
116049
+ // dist/builtin/workflows/src/extension/workflow-database-settings.ts
116050
+ import { readFileSync as readFileSync7 } from "node:fs";
116051
+ import { readFile as readFile6 } from "node:fs/promises";
116052
+ import { homedir as homedir3 } from "node:os";
116053
+ import { join as join33, resolve as resolve15 } from "node:path";
116054
+ import { getAgentDir as getAgentDir3, getAgentDirs as getAgentDirs3, getProjectConfigDirs, SettingsManager } from "@bastani/atomic";
116055
+ var DATABASE_SETTING_KEYS = new Set(["systemDatabaseUrl", "systemDatabaseUrlFile"]);
116056
+ function databaseSettings(settings) {
116057
+ const workflows = settings.workflows;
116058
+ if (workflows === undefined || workflows === null || Array.isArray(workflows))
116059
+ return;
116060
+ if (typeof workflows !== "object")
116061
+ throw new TypeError("workflows must be an array of paths or an object");
116062
+ return workflows.durability;
116063
+ }
116064
+ function readSettingsFile(directory) {
116065
+ let parsed;
116066
+ try {
116067
+ parsed = JSON.parse(readFileSync7(join33(directory, "settings.json"), "utf8").replace(/^\uFEFF/, ""));
116068
+ } catch {
116069
+ return;
116070
+ }
116071
+ return parsed !== null && typeof parsed === "object" ? parsed : undefined;
116072
+ }
116073
+ function settingsDirectoryDeclaringDatabase(directories) {
116074
+ let declaring;
116075
+ for (const directory of directories) {
116076
+ const settings = readSettingsFile(directory);
116077
+ if (settings !== undefined && databaseSettings(settings) !== undefined)
116078
+ declaring ??= directory;
116079
+ }
116080
+ return declaring ?? directories[0];
116081
+ }
116082
+ async function resolveWorkflowDatabaseSettings(global2, project, directories) {
116083
+ const projectDatabase = databaseSettings(project);
116084
+ const database = projectDatabase !== undefined ? projectDatabase : databaseSettings(global2);
116085
+ if (database === undefined)
116086
+ return;
116087
+ if (database === null || typeof database !== "object" || Array.isArray(database)) {
116088
+ throw new TypeError("workflows.durability must be an object");
116089
+ }
116090
+ if (Object.keys(database).some((key) => !DATABASE_SETTING_KEYS.has(key))) {
116091
+ throw new TypeError("workflows.durability supports only systemDatabaseUrl or systemDatabaseUrlFile");
116092
+ }
116093
+ const { systemDatabaseUrl: url, systemDatabaseUrlFile: file } = database;
116094
+ if (url !== undefined && file !== undefined) {
116095
+ throw new TypeError("Set only one of workflows.durability.systemDatabaseUrl and systemDatabaseUrlFile");
116096
+ }
116097
+ let value2 = url;
116098
+ if (file !== undefined) {
116099
+ if (typeof file !== "string" || file.trim() === "") {
116100
+ throw new TypeError("workflows.durability.systemDatabaseUrlFile must be a non-empty path");
116101
+ }
116102
+ const path = file.startsWith("~/") || file.startsWith("~\\") ? join33(homedir3(), file.slice(2)) : resolve15(projectDatabase === undefined ? directories.global : directories.project, file);
116103
+ try {
116104
+ value2 = await readFile6(path, "utf8");
116105
+ } catch {
116106
+ throw new Error("Cannot read workflows.durability.systemDatabaseUrlFile; check its path and permissions");
116107
+ }
116108
+ }
116109
+ if (value2 === undefined)
116110
+ return;
116111
+ if (typeof value2 !== "string" || value2.trim() === "") {
116112
+ throw new TypeError("Workflow database selection must contain a non-empty Postgres URL");
116113
+ }
116114
+ try {
116115
+ const parsed = new URL(value2.trim());
116116
+ if (!["postgres:", "postgresql:"].includes(parsed.protocol) || parsed.hostname === "")
116117
+ throw new Error;
116118
+ } catch {
116119
+ throw new TypeError("Workflow database selection must contain a valid Postgres URL");
116120
+ }
116121
+ return value2.trim();
116122
+ }
116123
+ async function prepareWorkflowDatabaseSettings(cwd, projectTrusted) {
116124
+ if (process.env.DBOS_SYSTEM_DATABASE_URL?.trim())
116125
+ return;
116126
+ const manager = SettingsManager.create(cwd, getAgentDir3(), { projectTrusted });
116127
+ if (manager.drainErrors().length > 0)
116128
+ throw new Error("Cannot read workflow database settings; repair settings.json");
116129
+ const projectDirectories = getProjectConfigDirs(cwd);
116130
+ const url = await resolveWorkflowDatabaseSettings(manager.getGlobalSettings(), manager.getProjectSettings(), {
116131
+ global: settingsDirectoryDeclaringDatabase(getAgentDirs3()),
116132
+ project: projectTrusted ? settingsDirectoryDeclaringDatabase(projectDirectories) : projectDirectories[0]
116133
+ });
116134
+ if (url !== undefined)
116135
+ requestDbosSystemDatabaseUrl(url);
116136
+ }
116137
+
115928
116138
  // dist/builtin/workflows/src/extension/workflow-heartbeat-notice.ts
115929
116139
  var WORKFLOW_HEARTBEAT_TITLE = "WORKFLOW HEARTBEAT";
115930
116140
  var WORKFLOW_HEARTBEAT_GLYPH = "♥";
@@ -117076,6 +117286,45 @@ function createWorkflowExtensionRuntimeState(pi, adapters, options = {}) {
117076
117286
  const resolveDefaultStageSessionDir = () => hostStageSessionDir.current;
117077
117287
  const discoveryRef = { current: null };
117078
117288
  const resolvePossibleStageEntry = (normalizedName) => discoveryRef.current?.sources.find((source) => source.id === normalizedName)?.filePath ?? resolveBuiltinDefinitionSource(normalizedName);
117289
+ let databasePreparation;
117290
+ const prepareDatabase = () => {
117291
+ const cwd = resolveCwd();
117292
+ const trusted = pi.getResourceLoaderInheritanceSnapshot?.()?.projectTrusted ?? false;
117293
+ const key = JSON.stringify([cwd, trusted]);
117294
+ if (databasePreparation?.key === key)
117295
+ return databasePreparation.promise;
117296
+ const promise = prepareWorkflowDatabaseSettings(cwd, trusted).catch((error) => {
117297
+ if (databasePreparation?.promise === promise)
117298
+ databasePreparation = undefined;
117299
+ throw error;
117300
+ });
117301
+ databasePreparation = { key, promise };
117302
+ return promise;
117303
+ };
117304
+ const databaseReady = (ctx) => {
117305
+ const owner = getDbosProcessOwner();
117306
+ const describeDatabase = owner.databaseDiagnostics;
117307
+ if (owner.state !== "ready" || describeDatabase === undefined)
117308
+ return;
117309
+ if (owner.announcedDatabaseDiagnostics === describeDatabase)
117310
+ return;
117311
+ const diagnostics = describeDatabase();
117312
+ const dockerFallback = diagnostics.provider === "docker";
117313
+ let host = "unknown host";
117314
+ try {
117315
+ host = new URL(diagnostics.url).host;
117316
+ } catch {}
117317
+ const message = `Workflow database: ${dockerFallback ? "Docker fallback" : diagnostics.provider} (${host})`;
117318
+ if (ctx?.hasUI && ctx.ui?.notify) {
117319
+ try {
117320
+ ctx.ui.notify(message, "info");
117321
+ owner.announcedDatabaseDiagnostics = describeDatabase;
117322
+ } catch {}
117323
+ } else if (dockerFallback) {
117324
+ owner.announcedDatabaseDiagnostics = describeDatabase;
117325
+ console.error(message);
117326
+ }
117327
+ };
117079
117328
  const startupDiscovery = discoverStartupWorkflowsSync();
117080
117329
  const runtimeRef = {
117081
117330
  current: createExtensionRuntime({
@@ -117089,7 +117338,9 @@ function createWorkflowExtensionRuntimeState(pi, adapters, options = {}) {
117089
117338
  config: runtimeConfigRef.current,
117090
117339
  resolvePossibleStageEntry,
117091
117340
  resolveDefaultStageSessionDir,
117092
- beforeRestoreCompleted
117341
+ beforeRestoreCompleted,
117342
+ prepareDatabase,
117343
+ databaseReady
117093
117344
  })
117094
117345
  };
117095
117346
  const configLoadRef = { current: null };
@@ -117154,7 +117405,9 @@ function createWorkflowExtensionRuntimeState(pi, adapters, options = {}) {
117154
117405
  ...models === undefined ? {} : { models },
117155
117406
  ...durabilityWarningSink === undefined ? {} : { durabilityWarningSink },
117156
117407
  resolveDefaultStageSessionDir,
117157
- beforeRestoreCompleted
117408
+ beforeRestoreCompleted,
117409
+ prepareDatabase,
117410
+ databaseReady: () => databaseReady(ctx)
117158
117411
  });
117159
117412
  }
117160
117413
  let lazyDiscoveryPromise = null;
@@ -117193,7 +117446,9 @@ function createWorkflowExtensionRuntimeState(pi, adapters, options = {}) {
117193
117446
  config: runtimeConfigRef.current,
117194
117447
  resolvePossibleStageEntry,
117195
117448
  resolveDefaultStageSessionDir,
117196
- beforeRestoreCompleted
117449
+ beforeRestoreCompleted,
117450
+ prepareDatabase,
117451
+ databaseReady
117197
117452
  });
117198
117453
  }
117199
117454
  function isWorkflowDiscoveryCurrent(generation) {