@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
@@ -63,6 +63,92 @@ function raceAbort2(operation, signal) {
63
63
  });
64
64
  }
65
65
 
66
+ // dist/builtin/workflows/src/durable/dbos-database-diagnostics.ts
67
+ function redactedDatabaseEndpoint(url) {
68
+ try {
69
+ const parsed = new URL(url);
70
+ parsed.username = "";
71
+ parsed.password = "";
72
+ parsed.search = "";
73
+ parsed.hash = "";
74
+ return parsed.toString();
75
+ } catch {
76
+ return "unparseable PostgreSQL endpoint";
77
+ }
78
+ }
79
+ function redactedDatabaseMessage(message, url) {
80
+ let safe = message.replace(/postgres(?:ql)?:\/\/[^\s"'<>]+/gi, (value) => redactedDatabaseEndpoint(value));
81
+ let configuredSecrets = [];
82
+ if (url !== undefined) {
83
+ try {
84
+ const parsed = new URL(url);
85
+ configuredSecrets = [parsed.username, parsed.password, ...parsed.searchParams.values()].flatMap((secret) => {
86
+ try {
87
+ return [secret, decodeURIComponent(secret)];
88
+ } catch {
89
+ return [secret];
90
+ }
91
+ }).filter((secret) => secret.length > 0);
92
+ } catch {
93
+ return "PostgreSQL diagnostic could not be safely redacted; check the configured endpoint.";
94
+ }
95
+ }
96
+ 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) => {
97
+ const value = doubleQuoted ?? singleQuoted ?? bare ?? "";
98
+ const isUserContext = /^user(?:name)?$/i.test(key);
99
+ if (separator === undefined && !isUserContext && !configuredSecrets.includes(value))
100
+ return match;
101
+ const rendered = doubleQuoted !== undefined ? '"[redacted]"' : singleQuoted !== undefined ? "'[redacted]'" : "[redacted]";
102
+ return `${key}${separator ?? " "}${filler ?? ""}${rendered}`;
103
+ });
104
+ for (const secret of new Set(configuredSecrets)) {
105
+ if (secret.length < 4)
106
+ continue;
107
+ const escaped = secret.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
108
+ const expression = new RegExp(`(?<![\\p{L}\\p{N}_\\[])${escaped}(?![\\p{L}\\p{N}_\\]])`, "gu");
109
+ safe = safe.replace(expression, "[redacted]");
110
+ }
111
+ return safe;
112
+ }
113
+ function databaseDependencyMessage(message, diagnostics) {
114
+ const safe = redactedDatabaseMessage(message, diagnostics?.url);
115
+ if (diagnostics === undefined)
116
+ return safe;
117
+ const failure = diagnostics.failure === undefined ? "" : ` Last probe/recovery failure: ${redactedDatabaseMessage(diagnostics.failure, diagnostics.url)}`;
118
+ return `${safe} Provider: ${diagnostics.provider}; endpoint: ${redactedDatabaseEndpoint(diagnostics.url)}.${failure}`;
119
+ }
120
+
121
+ // dist/builtin/workflows/src/durable/dbos-process-owner.ts
122
+ function emptyOwner() {
123
+ return {
124
+ version: 1,
125
+ state: "uninitialized",
126
+ configured: undefined,
127
+ active: undefined,
128
+ launchPromise: undefined,
129
+ shutdownPromise: undefined,
130
+ failure: undefined,
131
+ wrappers: undefined,
132
+ leases: new Set
133
+ };
134
+ }
135
+ function ownerBag() {
136
+ return globalThis;
137
+ }
138
+ function getDbosProcessOwner() {
139
+ const bag = ownerBag();
140
+ const existing = bag[DBOS_PROCESS_OWNER_KEY];
141
+ if (existing !== undefined && existing.version === 1)
142
+ return existing;
143
+ const created = emptyOwner();
144
+ bag[DBOS_PROCESS_OWNER_KEY] = created;
145
+ return created;
146
+ }
147
+ var DBOS_PROCESS_OWNER_KEY;
148
+ var init_dbos_process_owner = __esm(() => {
149
+ DBOS_PROCESS_OWNER_KEY = Symbol.for("atomic-workflows/dbos-process-owner@1");
150
+ });
151
+
66
152
  // dist/builtin/workflows/src/durable/dbos-admission.ts
67
153
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
68
154
  function isDbosDependencyError(error) {
@@ -81,6 +167,8 @@ async function boundedAdmission(operation, signal, timeoutMs = DBOS_ADMISSION_TI
81
167
  return await raceAbort2(operation(controller.signal), controller.signal);
82
168
  } catch (error) {
83
169
  controller.abort(error);
170
+ if (isDbosDependencyError(error) && error.admissionDetail !== undefined)
171
+ throw new DbosDependencyError(error.admissionDetail, null);
84
172
  throw error;
85
173
  } finally {
86
174
  clearTimeout(timer);
@@ -89,10 +177,13 @@ async function boundedAdmission(operation, signal, timeoutMs = DBOS_ADMISSION_TI
89
177
  }
90
178
  var DBOS_ADMISSION_TIMEOUT_MS = 1e4, DbosDependencyError, key2, bag, dbosAdmissionContext;
91
179
  var init_dbos_admission = __esm(() => {
180
+ init_dbos_process_owner();
92
181
  DbosDependencyError = class DbosDependencyError extends Error {
93
182
  code = "ATOMIC_DBOS_DEPENDENCY";
94
- constructor(message = "Workflow database unavailable during admission. Restore PostgreSQL before retrying.") {
95
- super(message);
183
+ admissionDetail;
184
+ constructor(message = "Workflow database unavailable during admission. Restore PostgreSQL before retrying.", diagnostics = getDbosProcessOwner().databaseDiagnostics?.(), admissionDetail) {
185
+ super(databaseDependencyMessage(message, diagnostics ?? undefined));
186
+ this.admissionDetail = admissionDetail === undefined ? undefined : redactedDatabaseMessage(admissionDetail);
96
187
  this.name = "DbosDependencyError";
97
188
  }
98
189
  };
@@ -30263,11 +30354,11 @@ function fenceDbosAdmissionPool(pool) {
30263
30354
  const connect = pool.connect.bind(pool);
30264
30355
  async function acquire(signal) {
30265
30356
  if (signal.aborted)
30266
- throw new DbosDependencyError;
30357
+ throw sdkDependencyError();
30267
30358
  const pending = connect().then((client) => {
30268
30359
  if (signal.aborted) {
30269
30360
  client.release(true);
30270
- throw new DbosDependencyError;
30361
+ throw sdkDependencyError();
30271
30362
  }
30272
30363
  return fencedClient(client, signal);
30273
30364
  });
@@ -30311,7 +30402,7 @@ function fencedClient(client, signal) {
30311
30402
  const callback = typeof last === "function" ? values.pop() : undefined;
30312
30403
  const pending = (async () => {
30313
30404
  if (signal.aborted || released)
30314
- throw new DbosDependencyError;
30405
+ throw sdkDependencyError();
30315
30406
  try {
30316
30407
  return await raceAbort2(new Promise((resolve, reject) => {
30317
30408
  Reflect.apply(target, client, [
@@ -30343,9 +30434,15 @@ function fencedClient(client, signal) {
30343
30434
  }
30344
30435
  });
30345
30436
  }
30437
+ function sdkDependencyError(message) {
30438
+ const reported = message === undefined ? new DbosDependencyError : new DbosDependencyError(message, null);
30439
+ return new DbosDependencyError(undefined, null, reported.message);
30440
+ }
30346
30441
  function databaseError(error, signal) {
30347
30442
  if (signal.aborted)
30348
- return new DbosDependencyError;
30443
+ return sdkDependencyError();
30444
+ if (isDbosDependencyError(error))
30445
+ return sdkDependencyError(error.admissionDetail ?? error.message);
30349
30446
  const pending = [error];
30350
30447
  const seen = new Set;
30351
30448
  while (pending.length > 0) {
@@ -30362,7 +30459,7 @@ function databaseError(error, signal) {
30362
30459
  const code = "code" in value && typeof value.code === "string" ? value.code : undefined;
30363
30460
  const message = value instanceof Error ? value.message : "";
30364
30461
  if (code !== undefined && (/^(?:08|53|57)[0-9A-Z]{3}$|^40003$/.test(code) || connectionErrnos.has(code)) || code === undefined && connectionMessages.has(message)) {
30365
- return new DbosDependencyError;
30462
+ return sdkDependencyError();
30366
30463
  }
30367
30464
  }
30368
30465
  return error;
@@ -30385,7 +30482,9 @@ var init_dbos_admission_pool = __esm(() => {
30385
30482
  "Connection terminated unexpectedly",
30386
30483
  "Connection terminated due to connection timeout",
30387
30484
  "Client has encountered a connection error and is not queryable",
30388
- "timeout exceeded when trying to connect"
30485
+ "timeout exceeded when trying to connect",
30486
+ "Client was closed and is not queryable",
30487
+ "Postgres connection pool changed while acquiring a connection"
30389
30488
  ]);
30390
30489
  });
30391
30490
 
@@ -30673,6 +30772,11 @@ function defaultDatabaseUrl(name) {
30673
30772
  function configureAdmissionDatabase(sdk, config, launchLock = withDbosLaunchLock) {
30674
30773
  const systemDatabaseUrl = config.systemDatabaseUrl ?? defaultDatabaseUrl(config.name);
30675
30774
  const health = resolvedPostgresHealth(systemDatabaseUrl);
30775
+ getDbosProcessOwner().databaseDiagnostics = () => ({
30776
+ provider: health === undefined ? config.systemDatabaseUrl === undefined ? "docker" : "configured" : "managed",
30777
+ url: health?.endpoint ?? systemDatabaseUrl,
30778
+ failure: health?.lastFailure?.message
30779
+ });
30676
30780
  const createPool = () => {
30677
30781
  let unsubscribe;
30678
30782
  const managed = createRecoverablePostgresPool(systemDatabaseUrl, {
@@ -30710,6 +30814,7 @@ var import_datasource2;
30710
30814
  var init_dbos_admission_config = __esm(() => {
30711
30815
  init_dbos_admission_pool();
30712
30816
  init_dbos_launch_lock();
30817
+ init_dbos_process_owner();
30713
30818
  init_dbos_recoverable_pool();
30714
30819
  import_datasource2 = __toESM(require_datasource(), 1);
30715
30820
  });
@@ -37791,10 +37896,11 @@ function hasInvocationData(checkpoints, replayKey) {
37791
37896
 
37792
37897
  // dist/builtin/workflows/src/engine/run.ts
37793
37898
  init_dbos_admission();
37899
+ init_dbos_process_owner();
37794
37900
 
37795
37901
  // dist/builtin/workflows/src/durable/backend-process-owner.ts
37796
37902
  var DURABLE_BACKEND_PROCESS_OWNER_KEY = Symbol.for("atomic-workflows/durable-backend-process-owner@1");
37797
- function emptyOwner() {
37903
+ function emptyOwner2() {
37798
37904
  return {
37799
37905
  version: 1,
37800
37906
  injectedBackend: undefined,
@@ -37804,15 +37910,15 @@ function emptyOwner() {
37804
37910
  warningReported: false
37805
37911
  };
37806
37912
  }
37807
- function ownerBag() {
37913
+ function ownerBag2() {
37808
37914
  return globalThis;
37809
37915
  }
37810
37916
  function getDurableBackendProcessOwner() {
37811
- const bag = ownerBag();
37917
+ const bag = ownerBag2();
37812
37918
  const existing = bag[DURABLE_BACKEND_PROCESS_OWNER_KEY];
37813
37919
  if (existing !== undefined && existing.version === 1)
37814
37920
  return existing;
37815
- const created = emptyOwner();
37921
+ const created = emptyOwner2();
37816
37922
  bag[DURABLE_BACKEND_PROCESS_OWNER_KEY] = created;
37817
37923
  return created;
37818
37924
  }
@@ -39024,33 +39130,8 @@ function classifyDbosDeletionTombstone(records, workflowId) {
39024
39130
  return isCurrentDurableFormat(raw.version) ? "current" : "unknown";
39025
39131
  }
39026
39132
 
39027
- // dist/builtin/workflows/src/durable/dbos-process-owner.ts
39028
- var DBOS_PROCESS_OWNER_KEY = Symbol.for("atomic-workflows/dbos-process-owner@1");
39029
- function emptyOwner2() {
39030
- return {
39031
- version: 1,
39032
- state: "uninitialized",
39033
- configured: undefined,
39034
- active: undefined,
39035
- launchPromise: undefined,
39036
- shutdownPromise: undefined,
39037
- failure: undefined,
39038
- wrappers: undefined,
39039
- leases: new Set
39040
- };
39041
- }
39042
- function ownerBag2() {
39043
- return globalThis;
39044
- }
39045
- function getDbosProcessOwner() {
39046
- const bag = ownerBag2();
39047
- const existing = bag[DBOS_PROCESS_OWNER_KEY];
39048
- if (existing !== undefined && existing.version === 1)
39049
- return existing;
39050
- const created = emptyOwner2();
39051
- bag[DBOS_PROCESS_OWNER_KEY] = created;
39052
- return created;
39053
- }
39133
+ // dist/builtin/workflows/src/durable/dbos-backend.ts
39134
+ init_dbos_process_owner();
39054
39135
 
39055
39136
  // dist/builtin/workflows/src/durable/dbos-sdk-handle.ts
39056
39137
  var ATOMIC_EXECUTOR_ID = `atomic-${process.pid.toString(36)}-${crypto.randomUUID().slice(0, 8)}`;
@@ -39171,6 +39252,7 @@ function statusToInfo(status, fallbackId) {
39171
39252
  }
39172
39253
 
39173
39254
  // dist/builtin/workflows/src/durable/dbos-system-database-url.ts
39255
+ init_dbos_process_owner();
39174
39256
  function nonEmptyTrimmed(value) {
39175
39257
  const trimmed = value?.trim();
39176
39258
  return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;
@@ -39185,9 +39267,7 @@ class DbosSystemDatabaseConflictError extends Error {
39185
39267
  function redactDatabaseUrl(url) {
39186
39268
  try {
39187
39269
  const parsed = new URL(url);
39188
- if (parsed.password !== "")
39189
- parsed.password = "***";
39190
- return parsed.toString();
39270
+ return parsed.host;
39191
39271
  } catch {
39192
39272
  return "the requested database";
39193
39273
  }
@@ -40904,6 +40984,7 @@ class PostgresHealth {
40904
40984
  attempts = 0;
40905
40985
  nextRecoveryAt = 0;
40906
40986
  failure;
40987
+ healthySinceFailure = false;
40907
40988
  revision = 0;
40908
40989
  listeners = new Set;
40909
40990
  constructor(operations) {
@@ -40912,6 +40993,18 @@ class PostgresHealth {
40912
40993
  get lastFailure() {
40913
40994
  return this.failure;
40914
40995
  }
40996
+ get endpoint() {
40997
+ return this.operations.url?.();
40998
+ }
40999
+ retainFailure(error) {
41000
+ const message = error instanceof Error ? error.message : String(error);
41001
+ const safe = redactedDatabaseMessage(message, this.endpoint);
41002
+ this.failure = new Error(safe);
41003
+ this.healthySinceFailure = false;
41004
+ }
41005
+ dependencyFailure(message) {
41006
+ return new DbosDependencyError(message, this.endpoint === undefined ? undefined : { provider: "managed", url: this.endpoint, failure: this.failure?.message });
41007
+ }
40915
41008
  subscribe(listener) {
40916
41009
  this.listeners.add(listener);
40917
41010
  return () => this.listeners.delete(listener);
@@ -40934,12 +41027,15 @@ class PostgresHealth {
40934
41027
  await this.operations.validate?.(client);
40935
41028
  }
40936
41029
  } catch (error) {
40937
- this.failure = error instanceof Error ? error : new Error(String(error));
41030
+ this.retainFailure(error);
40938
41031
  if (!isQueryReadTimeout(error)) {
40939
41032
  this.invalidate();
40940
- throw error;
41033
+ if (this.endpoint === undefined)
41034
+ throw error;
41035
+ const failure = this.dependencyFailure("Managed PostgreSQL connection validation refused. Preserve its data and ownership records.");
41036
+ throw error instanceof DbosDependencyError ? failure : new Error(failure.message);
40941
41037
  }
40942
- throw new DbosDependencyError("Managed PostgreSQL did not answer a connection identity check in time.");
41038
+ throw this.dependencyFailure("Managed PostgreSQL did not answer a connection identity check in time.");
40943
41039
  }
40944
41040
  }
40945
41041
  check() {
@@ -40980,7 +41076,7 @@ class PostgresHealth {
40980
41076
  } catch (retryError) {
40981
41077
  if (!isMonitoringConnectionFailure(retryError))
40982
41078
  throw retryError;
40983
- this.failure = retryError;
41079
+ this.retainFailure(retryError);
40984
41080
  return;
40985
41081
  }
40986
41082
  }
@@ -40995,6 +41091,7 @@ class PostgresHealth {
40995
41091
  this.available = identity;
40996
41092
  this.attempts = 0;
40997
41093
  this.nextRecoveryAt = 0;
41094
+ this.healthySinceFailure = true;
40998
41095
  return identity.url;
40999
41096
  }
41000
41097
  async refresh() {
@@ -41004,19 +41101,21 @@ class PostgresHealth {
41004
41101
  return ready;
41005
41102
  } catch (error) {
41006
41103
  if (isQueryReadTimeout(error)) {
41007
- this.failure = new DbosDependencyError("Managed PostgreSQL did not answer a health check in time.");
41008
- throw this.failure;
41104
+ this.retainFailure(new Error("Managed PostgreSQL did not answer a health check in time."));
41105
+ throw this.dependencyFailure("Managed PostgreSQL did not answer a health check in time.");
41009
41106
  }
41010
- this.failure = error instanceof Error ? error : new Error(String(error));
41107
+ this.retainFailure(error);
41011
41108
  if (!(error instanceof Error && ("code" in error) && error.code === "53300"))
41012
41109
  this.invalidate();
41013
- throw error;
41110
+ throw this.endpoint === undefined ? error : this.dependencyFailure("Managed PostgreSQL recovery refused after a health probe failure. Preserve its data and ownership records.");
41111
+ }
41112
+ if (this.attempts === 0 && (this.failure === undefined || this.healthySinceFailure)) {
41113
+ this.retainFailure(new Error("Managed PostgreSQL failed its live health check."));
41114
+ this.healthySinceFailure = false;
41014
41115
  }
41015
- if (this.attempts === 0)
41016
- this.failure = new DbosDependencyError("Managed PostgreSQL failed its live health check.");
41017
41116
  this.invalidate();
41018
41117
  if ((this.operations.now ?? Date.now)() < this.nextRecoveryAt)
41019
- throw new DbosDependencyError("Managed Postgres recovery is cooling down after bounded attempts.");
41118
+ throw this.dependencyFailure("Managed Postgres recovery is cooling down after bounded attempts.");
41020
41119
  while (!this.stopped && this.attempts < RECOVERY_ATTEMPTS) {
41021
41120
  const attempt = this.attempts++;
41022
41121
  if (attempt > 0)
@@ -41029,12 +41128,12 @@ class PostgresHealth {
41029
41128
  if (ready !== undefined)
41030
41129
  return ready;
41031
41130
  } catch (error) {
41032
- this.failure = error instanceof Error ? error : new Error(String(error));
41131
+ this.retainFailure(error);
41033
41132
  }
41034
41133
  }
41035
41134
  this.attempts = 0;
41036
41135
  this.nextRecoveryAt = (this.operations.now ?? Date.now)() + HEALTH_INTERVAL_MS;
41037
- throw new DbosDependencyError("Managed Postgres is unavailable after bounded recovery. Preserve its data and ownership records.");
41136
+ throw this.dependencyFailure("Managed Postgres is unavailable after bounded recovery. Preserve its data and ownership records.");
41038
41137
  }
41039
41138
  }
41040
41139
 
@@ -41690,6 +41789,8 @@ async function ensureCluster(options = {}) {
41690
41789
  const server = current.server;
41691
41790
  if (!server)
41692
41791
  throw new Error("Managed Postgres published identity is missing.");
41792
+ if (managedPostmaster(current) === undefined)
41793
+ return;
41693
41794
  if (!managedPostgresRuntimeHealthy(current, server.port)) {
41694
41795
  verifyManagedPostmasterProcess(current, server);
41695
41796
  return;
@@ -41706,6 +41807,7 @@ async function ensureCluster(options = {}) {
41706
41807
  };
41707
41808
  health = new PostgresHealth({
41708
41809
  probe: () => inspect(),
41810
+ url: embeddedDbosSystemDatabaseUrl,
41709
41811
  validate: async (client) => {
41710
41812
  const identity = await inspect(async () => {
41711
41813
  const query = { text: POSTGRES_IDENTITY_SQL, query_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS };
@@ -42793,6 +42895,9 @@ async function requireDockerSuccess(action, args) {
42793
42895
  throw new Error(`Could not ${action}: ${commandFailureDetail(result)}`);
42794
42896
  }
42795
42897
 
42898
+ // dist/builtin/workflows/src/durable/dbos-lifecycle.ts
42899
+ init_dbos_process_owner();
42900
+
42796
42901
  // dist/builtin/workflows/src/durable/dbos-registration-diagnostics.ts
42797
42902
  var cachedErrorModule;
42798
42903
  var errorModuleLoad;
@@ -55743,7 +55848,7 @@ async function run(def, inputs, opts = {}) {
55743
55848
  if (control === latestControl && !ownController.signal.aborted && isDbosDependencyError(error)) {
55744
55849
  activeStore.recordRunExecutionState(runId, {
55745
55850
  phase: "blocked_dependency",
55746
- dependencyError: `Workflow database unavailable during ${status === "paused" ? "pause" : "resume"}; persistence not confirmed.`
55851
+ dependencyError: `Workflow database unavailable during ${status === "paused" ? "pause" : "resume"}; persistence not confirmed. ${error.message}`
55747
55852
  });
55748
55853
  if (status === "paused")
55749
55854
  return;
@@ -55790,7 +55895,7 @@ async function run(def, inputs, opts = {}) {
55790
55895
  if (!admission.admitting)
55791
55896
  return pausePersistence;
55792
55897
  backgroundAdmissionControl(durableBackend, runId, pausePersistence, (error, resumable) => {
55793
- admissionControlError = unknownErrorMessage(error);
55898
+ admissionControlError = databaseDependencyMessage(unknownErrorMessage(error), getDbosProcessOwner().databaseDiagnostics?.());
55794
55899
  runSnapshot.error = admissionControlError;
55795
55900
  activeStore.recordRunPaused(runId, undefined, { resumable });
55796
55901
  return runSnapshot.error;
@@ -55807,7 +55912,7 @@ async function run(def, inputs, opts = {}) {
55807
55912
  } catch (error) {
55808
55913
  if (admission.failed) {
55809
55914
  scheduler.releaseRun();
55810
- throw error;
55915
+ throw new Error(admissionControlError ?? databaseDependencyMessage(unknownErrorMessage(error), getDbosProcessOwner().databaseDiagnostics?.()));
55811
55916
  }
55812
55917
  }
55813
55918
  if (generation !== pauseGeneration)
@@ -55880,12 +55985,21 @@ async function run(def, inputs, opts = {}) {
55880
55985
  }).catch((error) => {
55881
55986
  const isTopologyError = typeof error === "object" && error !== null && "name" in error && error.name === "DurableNestedTopologyError";
55882
55987
  if (!isDbosDependencyError(error) && !isTopologyError) {
55883
- durableAdmissionFailure = { error };
55884
- classifiedFailures.set(error, classifyWorkflowFailure({
55988
+ const admissionDetail = unknownErrorMessage(error);
55989
+ const diagnostics = getDbosProcessOwner().databaseDiagnostics?.();
55990
+ const safeMessage = databaseDependencyMessage(admissionDetail, diagnostics);
55991
+ activeStore.recordRunExecutionState(runId, {
55992
+ phase: "blocked_dependency",
55993
+ dependencyError: safeMessage
55994
+ });
55995
+ const safeError = new Error(safeMessage);
55996
+ durableAdmissionFailure = { error: safeError };
55997
+ classifiedFailures.set(safeError, classifyWorkflowFailure({
55885
55998
  code: "ATOMIC_DURABLE_ADMISSION_REJECTED",
55886
- message: unknownErrorMessage(error),
55887
- cause: error
55999
+ message: safeMessage,
56000
+ cause: safeError
55888
56001
  }));
56002
+ throw safeError;
55889
56003
  }
55890
56004
  throw error;
55891
56005
  }), ownController.signal);
@@ -114,7 +114,7 @@ export async function exportToHtml(outputPath, options = {}) {
114
114
  ]);
115
115
  // Create tool renderer if we have an extension runner (for custom tool HTML rendering)
116
116
  const toolRenderer = createToolHtmlRenderer({
117
- getToolDefinition: (name) => this.getToolDefinition(name),
117
+ getToolRenderers: (name) => this.extensionRunner.resolveToolRenderers(name, () => this.getToolDefinition(name)),
118
118
  theme,
119
119
  cwd: this.sessionManager.getCwd(),
120
120
  });
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-export.js","sourceRoot":"","sources":["../../src/core/agent-session-export.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { AssistantMessage, Usage } from \"@bastani/pi-ai/compat\";\nimport { getThemeByName, theme } from \"../modes/interactive/theme/theme.js\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { SessionStats } from \"./agent-session-types.js\";\nimport { calculateContextTokens, estimateProjectedContextTokens } from \"./compaction/index.ts\";\nimport type { ToolHtmlRenderer } from \"./export-html/index.ts\";\nimport type { ContextUsage, ReplacedSessionContext } from \"./extensions/index.js\";\nimport { CURRENT_SESSION_VERSION, getLatestCompactionBoundaryEntry, type SessionHeader } from \"./session-manager.ts\";\nimport { addUsageToTotals, createUsageTotals } from \"./usage-totals.ts\";\n\nexport function getSessionStats(this: AgentSession): SessionStats {\n\tlet userMessages = 0;\n\tlet assistantMessages = 0;\n\tlet toolResults = 0;\n\tlet totalMessages = 0;\n\tlet toolCalls = 0;\n\tconst totals = createUsageTotals();\n\tlet latestAssistantUsage: Usage | undefined;\n\tfor (const entry of this.sessionManager.getEntries()) {\n\t\tif (\n\t\t\t(entry.type === \"usage\" ||\n\t\t\t\tentry.type === \"branch_summary\" ||\n\t\t\t\tentry.type === \"session_summary\" ||\n\t\t\t\tentry.type === \"compaction\") &&\n\t\t\tentry.usage\n\t\t) {\n\t\t\taddUsageToTotals(totals, entry.usage);\n\t\t}\n\t\tif (entry.type !== \"message\") continue;\n\t\ttotalMessages++;\n\t\tconst message = entry.message;\n\t\tif (message.role === \"user\") userMessages++;\n\t\telse if (message.role === \"toolResult\") {\n\t\t\ttoolResults++;\n\t\t\tif (\"usage\" in message && message.usage) addUsageToTotals(totals, message.usage);\n\t\t} else if (message.role === \"assistant\") {\n\t\t\tassistantMessages++;\n\t\t\tconst assistant = message as AssistantMessage;\n\t\t\ttoolCalls += assistant.content.filter((content) => content.type === \"toolCall\").length;\n\t\t\taddUsageToTotals(totals, assistant.usage);\n\t\t\tlatestAssistantUsage = assistant.usage;\n\t\t}\n\t}\n\n\treturn {\n\t\tsessionFile: this.sessionFile,\n\t\tsessionId: this.sessionId,\n\t\tuserMessages,\n\t\tassistantMessages,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\ttotalMessages,\n\t\ttokens: {\n\t\t\tinput: totals.input,\n\t\t\toutput: totals.output,\n\t\t\tcacheRead: totals.cacheRead,\n\t\t\tcacheWrite: totals.cacheWrite,\n\t\t\ttotal: totals.input + totals.output + totals.cacheRead + totals.cacheWrite,\n\t\t},\n\t\tcost: totals.cost,\n\t\tcontextUsage: this.getContextUsage(),\n\t\tlatestAssistantUsage,\n\t};\n}\n\nexport function getContextUsage(this: AgentSession): ContextUsage | undefined {\n\tconst model = this.model;\n\tif (!model) return undefined;\n\n\tconst contextWindow = model.contextWindow ?? 0;\n\tif (contextWindow <= 0) return undefined;\n\n\t// After compaction, the last assistant usage reflects pre-compaction context size.\n\t// We can only trust usage from an assistant that responded after the latest compaction.\n\t// If no such assistant exists, context token count is unknown until the next LLM response.\n\tconst projection = this.sessionManager.buildSessionProjection();\n\tconst branchEntries = this.sessionManager.getBranch();\n\tconst latestCompactionBoundary = getLatestCompactionBoundaryEntry(branchEntries);\n\n\tif (latestCompactionBoundary) {\n\t\t// Only a projected (not omitted) assistant that responded after the boundary\n\t\t// carries trustworthy usage.\n\t\tconst projectedAssistants = new Set(\n\t\t\tprojection.entries.flatMap((entry) =>\n\t\t\t\tentry.messages.some(\n\t\t\t\t\t(message) =>\n\t\t\t\t\t\tmessage.role === \"assistant\" &&\n\t\t\t\t\t\tmessage.stopReason !== \"aborted\" &&\n\t\t\t\t\t\tmessage.stopReason !== \"error\" &&\n\t\t\t\t\t\tcalculateContextTokens(message.usage, message.api) > 0,\n\t\t\t\t)\n\t\t\t\t\t? [entry.sourceEntry.id]\n\t\t\t\t\t: [],\n\t\t\t),\n\t\t);\n\t\tconst compactionIndex = branchEntries.findIndex((entry) => entry.id === latestCompactionBoundary.id);\n\t\tconst hasPostCompactionUsage = branchEntries\n\t\t\t.slice(compactionIndex + 1)\n\t\t\t.some((entry) => projectedAssistants.has(entry.id));\n\t\tif (!hasPostCompactionUsage) return { tokens: null, contextWindow, percent: null };\n\t}\n\n\tconst estimate = estimateProjectedContextTokens(projection, branchEntries);\n\tconst percent = (estimate.tokens / contextWindow) * 100;\n\n\treturn {\n\t\ttokens: estimate.tokens,\n\t\tcontextWindow,\n\t\tpercent,\n\t};\n}\n\n/**\n * Export session to HTML.\n * @param outputPath Optional output path (defaults to session directory)\n * @param options Optional export presentation settings; `themeName` overrides the\n * saved theme setting (e.g. with this run's --use-theme selection) when it names a\n * registered theme.\n * @returns Path to exported file\n */\n\nexport async function exportToHtml(\n\tthis: AgentSession,\n\toutputPath?: string,\n\toptions: { themeName?: string } = {},\n): Promise<string> {\n\tconst themeName = [options.themeName, this.settingsManager.getTheme()].find(\n\t\t(candidate) => candidate !== undefined && getThemeByName(candidate) !== undefined,\n\t);\n\tconst [{ exportSessionToHtml }, { createToolHtmlRenderer }] = await Promise.all([\n\t\timport(\"./export-html/index.ts\"),\n\t\timport(\"./export-html/tool-renderer.ts\"),\n\t]);\n\n\t// Create tool renderer if we have an extension runner (for custom tool HTML rendering)\n\tconst toolRenderer: ToolHtmlRenderer = createToolHtmlRenderer({\n\t\tgetToolDefinition: (name) => this.getToolDefinition(name),\n\t\ttheme,\n\t\tcwd: this.sessionManager.getCwd(),\n\t});\n\n\treturn await exportSessionToHtml(this.sessionManager, this.state, {\n\t\toutputPath,\n\t\tthemeName,\n\t\ttoolRenderer,\n\t});\n}\n\n/**\n * Export the current session branch to a JSONL file.\n * Writes the session header followed by all entries on the current branch path.\n * @param outputPath Target file path. If omitted, generates a timestamped file in cwd.\n * @returns The resolved output file path.\n */\n\nexport function exportToJsonl(\n\tthis: AgentSession,\n\toutputPath?: string,\n\toptions: { includeShareContext?: boolean } = {},\n): string {\n\tconst filePath = resolvePath(\n\t\toutputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, \"-\")}.jsonl`,\n\t\tprocess.cwd(),\n\t);\n\tconst dir = dirname(filePath);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\tconst header: SessionHeader = {\n\t\ttype: \"session\",\n\t\tversion: CURRENT_SESSION_VERSION,\n\t\tid: this.sessionManager.getSessionId(),\n\t\ttimestamp: new Date().toISOString(),\n\t\tcwd: this.sessionManager.getCwd(),\n\t};\n\n\tconst branchEntries = this.sessionManager.getBranch();\n\tconst lines = [JSON.stringify(header)];\n\n\t// Re-chain parentIds to form a linear sequence\n\tlet prevId: string | null = null;\n\tfor (const entry of branchEntries) {\n\t\tconst linear = { ...entry, parentId: prevId };\n\t\tlines.push(JSON.stringify(linear));\n\t\tprevId = entry.id;\n\t}\n\tif (options.includeShareContext) {\n\t\tconst shareEntry = {\n\t\t\ttype: \"custom\",\n\t\t\tid: randomUUID(),\n\t\t\tparentId: prevId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType: \"atomic.share\",\n\t\t\tdata: {\n\t\t\t\tsystemPrompt: this.systemPrompt,\n\t\t\t\ttools: this.agent.state.tools.map(({ name, description, parameters }) => ({\n\t\t\t\t\tname,\n\t\t\t\t\tdescription,\n\t\t\t\t\tparameters,\n\t\t\t\t})),\n\t\t\t},\n\t\t};\n\t\tlines.push(JSON.stringify(shareEntry));\n\t}\n\n\twriteFileSync(filePath, `${lines.join(\"\\n\")}\\n`);\n\treturn filePath;\n}\n\n// =========================================================================\n// Utilities\n// =========================================================================\n\n/**\n * Get text content of last assistant message.\n * Useful for /copy command.\n * @returns Text content, or undefined if no assistant message exists\n */\n\nexport function getLastAssistantText(this: AgentSession): string | undefined {\n\tfor (const message of this.messages.slice().reverse()) {\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst assistant = message as AssistantMessage;\n\t\t// Skip aborted messages with no content\n\t\tif (assistant.stopReason === \"aborted\" && assistant.content.length === 0) continue;\n\t\tlet text = \"\";\n\t\tfor (const content of assistant.content) {\n\t\t\tif (content.type === \"text\") text += content.text;\n\t\t}\n\t\tconst trimmed = text.trim();\n\t\tif (trimmed) return trimmed;\n\t}\n\treturn undefined;\n}\n\n// =========================================================================\n// Extension System\n// =========================================================================\n\nexport function createReplacedSessionContext(this: AgentSession): ReplacedSessionContext {\n\tconst context = Object.defineProperties(\n\t\t{},\n\t\tObject.getOwnPropertyDescriptors(this._extensionRunner.createCommandContext()),\n\t) as ReplacedSessionContext;\n\tcontext.sendMessage = (message, options) => this.sendCustomMessage(message, options);\n\tcontext.sendUserMessage = (content, options) => this.sendUserMessage(content, options);\n\treturn context;\n}\n\n/**\n * Check if extensions have handlers for a specific event type.\n */\n\nexport function hasExtensionHandlers(this: AgentSession, eventType: string): boolean {\n\treturn this._extensionRunner.hasHandlers(eventType);\n}\n\n/**\n * Get the extension runner (for setting UI context and error handlers).\n */\n\nexport const agentSessionExportMethods = {\n\tgetSessionStats,\n\tgetContextUsage,\n\texportToHtml,\n\texportToJsonl,\n\tgetLastAssistantText,\n\tcreateReplacedSessionContext,\n\thasExtensionHandlers,\n};\n"]}
1
+ {"version":3,"file":"agent-session-export.js","sourceRoot":"","sources":["../../src/core/agent-session-export.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { AssistantMessage, Usage } from \"@bastani/pi-ai/compat\";\nimport { getThemeByName, theme } from \"../modes/interactive/theme/theme.js\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { SessionStats } from \"./agent-session-types.js\";\nimport { calculateContextTokens, estimateProjectedContextTokens } from \"./compaction/index.ts\";\nimport type { ToolHtmlRenderer } from \"./export-html/index.ts\";\nimport type { ContextUsage, ReplacedSessionContext } from \"./extensions/index.js\";\nimport { CURRENT_SESSION_VERSION, getLatestCompactionBoundaryEntry, type SessionHeader } from \"./session-manager.ts\";\nimport { addUsageToTotals, createUsageTotals } from \"./usage-totals.ts\";\n\nexport function getSessionStats(this: AgentSession): SessionStats {\n\tlet userMessages = 0;\n\tlet assistantMessages = 0;\n\tlet toolResults = 0;\n\tlet totalMessages = 0;\n\tlet toolCalls = 0;\n\tconst totals = createUsageTotals();\n\tlet latestAssistantUsage: Usage | undefined;\n\tfor (const entry of this.sessionManager.getEntries()) {\n\t\tif (\n\t\t\t(entry.type === \"usage\" ||\n\t\t\t\tentry.type === \"branch_summary\" ||\n\t\t\t\tentry.type === \"session_summary\" ||\n\t\t\t\tentry.type === \"compaction\") &&\n\t\t\tentry.usage\n\t\t) {\n\t\t\taddUsageToTotals(totals, entry.usage);\n\t\t}\n\t\tif (entry.type !== \"message\") continue;\n\t\ttotalMessages++;\n\t\tconst message = entry.message;\n\t\tif (message.role === \"user\") userMessages++;\n\t\telse if (message.role === \"toolResult\") {\n\t\t\ttoolResults++;\n\t\t\tif (\"usage\" in message && message.usage) addUsageToTotals(totals, message.usage);\n\t\t} else if (message.role === \"assistant\") {\n\t\t\tassistantMessages++;\n\t\t\tconst assistant = message as AssistantMessage;\n\t\t\ttoolCalls += assistant.content.filter((content) => content.type === \"toolCall\").length;\n\t\t\taddUsageToTotals(totals, assistant.usage);\n\t\t\tlatestAssistantUsage = assistant.usage;\n\t\t}\n\t}\n\n\treturn {\n\t\tsessionFile: this.sessionFile,\n\t\tsessionId: this.sessionId,\n\t\tuserMessages,\n\t\tassistantMessages,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\ttotalMessages,\n\t\ttokens: {\n\t\t\tinput: totals.input,\n\t\t\toutput: totals.output,\n\t\t\tcacheRead: totals.cacheRead,\n\t\t\tcacheWrite: totals.cacheWrite,\n\t\t\ttotal: totals.input + totals.output + totals.cacheRead + totals.cacheWrite,\n\t\t},\n\t\tcost: totals.cost,\n\t\tcontextUsage: this.getContextUsage(),\n\t\tlatestAssistantUsage,\n\t};\n}\n\nexport function getContextUsage(this: AgentSession): ContextUsage | undefined {\n\tconst model = this.model;\n\tif (!model) return undefined;\n\n\tconst contextWindow = model.contextWindow ?? 0;\n\tif (contextWindow <= 0) return undefined;\n\n\t// After compaction, the last assistant usage reflects pre-compaction context size.\n\t// We can only trust usage from an assistant that responded after the latest compaction.\n\t// If no such assistant exists, context token count is unknown until the next LLM response.\n\tconst projection = this.sessionManager.buildSessionProjection();\n\tconst branchEntries = this.sessionManager.getBranch();\n\tconst latestCompactionBoundary = getLatestCompactionBoundaryEntry(branchEntries);\n\n\tif (latestCompactionBoundary) {\n\t\t// Only a projected (not omitted) assistant that responded after the boundary\n\t\t// carries trustworthy usage.\n\t\tconst projectedAssistants = new Set(\n\t\t\tprojection.entries.flatMap((entry) =>\n\t\t\t\tentry.messages.some(\n\t\t\t\t\t(message) =>\n\t\t\t\t\t\tmessage.role === \"assistant\" &&\n\t\t\t\t\t\tmessage.stopReason !== \"aborted\" &&\n\t\t\t\t\t\tmessage.stopReason !== \"error\" &&\n\t\t\t\t\t\tcalculateContextTokens(message.usage, message.api) > 0,\n\t\t\t\t)\n\t\t\t\t\t? [entry.sourceEntry.id]\n\t\t\t\t\t: [],\n\t\t\t),\n\t\t);\n\t\tconst compactionIndex = branchEntries.findIndex((entry) => entry.id === latestCompactionBoundary.id);\n\t\tconst hasPostCompactionUsage = branchEntries\n\t\t\t.slice(compactionIndex + 1)\n\t\t\t.some((entry) => projectedAssistants.has(entry.id));\n\t\tif (!hasPostCompactionUsage) return { tokens: null, contextWindow, percent: null };\n\t}\n\n\tconst estimate = estimateProjectedContextTokens(projection, branchEntries);\n\tconst percent = (estimate.tokens / contextWindow) * 100;\n\n\treturn {\n\t\ttokens: estimate.tokens,\n\t\tcontextWindow,\n\t\tpercent,\n\t};\n}\n\n/**\n * Export session to HTML.\n * @param outputPath Optional output path (defaults to session directory)\n * @param options Optional export presentation settings; `themeName` overrides the\n * saved theme setting (e.g. with this run's --use-theme selection) when it names a\n * registered theme.\n * @returns Path to exported file\n */\n\nexport async function exportToHtml(\n\tthis: AgentSession,\n\toutputPath?: string,\n\toptions: { themeName?: string } = {},\n): Promise<string> {\n\tconst themeName = [options.themeName, this.settingsManager.getTheme()].find(\n\t\t(candidate) => candidate !== undefined && getThemeByName(candidate) !== undefined,\n\t);\n\tconst [{ exportSessionToHtml }, { createToolHtmlRenderer }] = await Promise.all([\n\t\timport(\"./export-html/index.ts\"),\n\t\timport(\"./export-html/tool-renderer.ts\"),\n\t]);\n\n\t// Create tool renderer if we have an extension runner (for custom tool HTML rendering)\n\tconst toolRenderer: ToolHtmlRenderer = createToolHtmlRenderer({\n\t\tgetToolRenderers: (name) => this.extensionRunner.resolveToolRenderers(name, () => this.getToolDefinition(name)),\n\t\ttheme,\n\t\tcwd: this.sessionManager.getCwd(),\n\t});\n\n\treturn await exportSessionToHtml(this.sessionManager, this.state, {\n\t\toutputPath,\n\t\tthemeName,\n\t\ttoolRenderer,\n\t});\n}\n\n/**\n * Export the current session branch to a JSONL file.\n * Writes the session header followed by all entries on the current branch path.\n * @param outputPath Target file path. If omitted, generates a timestamped file in cwd.\n * @returns The resolved output file path.\n */\n\nexport function exportToJsonl(\n\tthis: AgentSession,\n\toutputPath?: string,\n\toptions: { includeShareContext?: boolean } = {},\n): string {\n\tconst filePath = resolvePath(\n\t\toutputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, \"-\")}.jsonl`,\n\t\tprocess.cwd(),\n\t);\n\tconst dir = dirname(filePath);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\tconst header: SessionHeader = {\n\t\ttype: \"session\",\n\t\tversion: CURRENT_SESSION_VERSION,\n\t\tid: this.sessionManager.getSessionId(),\n\t\ttimestamp: new Date().toISOString(),\n\t\tcwd: this.sessionManager.getCwd(),\n\t};\n\n\tconst branchEntries = this.sessionManager.getBranch();\n\tconst lines = [JSON.stringify(header)];\n\n\t// Re-chain parentIds to form a linear sequence\n\tlet prevId: string | null = null;\n\tfor (const entry of branchEntries) {\n\t\tconst linear = { ...entry, parentId: prevId };\n\t\tlines.push(JSON.stringify(linear));\n\t\tprevId = entry.id;\n\t}\n\tif (options.includeShareContext) {\n\t\tconst shareEntry = {\n\t\t\ttype: \"custom\",\n\t\t\tid: randomUUID(),\n\t\t\tparentId: prevId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType: \"atomic.share\",\n\t\t\tdata: {\n\t\t\t\tsystemPrompt: this.systemPrompt,\n\t\t\t\ttools: this.agent.state.tools.map(({ name, description, parameters }) => ({\n\t\t\t\t\tname,\n\t\t\t\t\tdescription,\n\t\t\t\t\tparameters,\n\t\t\t\t})),\n\t\t\t},\n\t\t};\n\t\tlines.push(JSON.stringify(shareEntry));\n\t}\n\n\twriteFileSync(filePath, `${lines.join(\"\\n\")}\\n`);\n\treturn filePath;\n}\n\n// =========================================================================\n// Utilities\n// =========================================================================\n\n/**\n * Get text content of last assistant message.\n * Useful for /copy command.\n * @returns Text content, or undefined if no assistant message exists\n */\n\nexport function getLastAssistantText(this: AgentSession): string | undefined {\n\tfor (const message of this.messages.slice().reverse()) {\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst assistant = message as AssistantMessage;\n\t\t// Skip aborted messages with no content\n\t\tif (assistant.stopReason === \"aborted\" && assistant.content.length === 0) continue;\n\t\tlet text = \"\";\n\t\tfor (const content of assistant.content) {\n\t\t\tif (content.type === \"text\") text += content.text;\n\t\t}\n\t\tconst trimmed = text.trim();\n\t\tif (trimmed) return trimmed;\n\t}\n\treturn undefined;\n}\n\n// =========================================================================\n// Extension System\n// =========================================================================\n\nexport function createReplacedSessionContext(this: AgentSession): ReplacedSessionContext {\n\tconst context = Object.defineProperties(\n\t\t{},\n\t\tObject.getOwnPropertyDescriptors(this._extensionRunner.createCommandContext()),\n\t) as ReplacedSessionContext;\n\tcontext.sendMessage = (message, options) => this.sendCustomMessage(message, options);\n\tcontext.sendUserMessage = (content, options) => this.sendUserMessage(content, options);\n\treturn context;\n}\n\n/**\n * Check if extensions have handlers for a specific event type.\n */\n\nexport function hasExtensionHandlers(this: AgentSession, eventType: string): boolean {\n\treturn this._extensionRunner.hasHandlers(eventType);\n}\n\n/**\n * Get the extension runner (for setting UI context and error handlers).\n */\n\nexport const agentSessionExportMethods = {\n\tgetSessionStats,\n\tgetContextUsage,\n\texportToHtml,\n\texportToJsonl,\n\tgetLastAssistantText,\n\tcreateReplacedSessionContext,\n\thasExtensionHandlers,\n};\n"]}
@@ -5,10 +5,10 @@
5
5
  * and converting the ANSI output to HTML.
6
6
  */
7
7
  import type { Theme } from "../../modes/interactive/theme/theme.js";
8
- import type { ToolDefinition } from "../extensions/types.ts";
8
+ import type { ToolRenderers } from "../extensions/types.ts";
9
9
  export interface ToolHtmlRendererDeps {
10
- /** Function to look up tool definition by name */
11
- getToolDefinition: (name: string) => ToolDefinition | undefined;
10
+ /** Renderers of calls to a tool, resolved by extensions and the registered tool. */
11
+ getToolRenderers: (name: string) => ToolRenderers | undefined;
12
12
  /** Theme for styling */
13
13
  theme: Theme;
14
14
  /** Working directory for render context */
@@ -1 +1 @@
1
- {"version":3,"file":"tool-renderer.d.ts","sourceRoot":"","sources":["../../../src/core/export-html/tool-renderer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,wBAAwB,CAAC;AAGhF,MAAM,WAAW,oBAAoB;IACpC,kDAAkD;IAClD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,GAAG,SAAS,CAAC;IAChE,wBAAwB;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAChC,oFAAoF;IACpF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACpF,yGAAyG;IACzG,YAAY,CACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAChF,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,GACd;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACzD;AAsBD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAkHnF"}
1
+ {"version":3,"file":"tool-renderer.d.ts","sourceRoot":"","sources":["../../../src/core/export-html/tool-renderer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,KAAK,EAAqB,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAG/E,MAAM,WAAW,oBAAoB;IACpC,oFAAoF;IACpF,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;IAC9D,wBAAwB;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAChC,oFAAoF;IACpF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACpF,yGAAyG;IACzG,YAAY,CACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAChF,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,GACd;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACzD;AAsBD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAkHnF"}
@@ -25,7 +25,7 @@ function trimRenderedResultLines(lines) {
25
25
  return lines.slice(start, end);
26
26
  }
27
27
  export function createToolHtmlRenderer(deps) {
28
- const { getToolDefinition, theme, cwd, width = 100 } = deps;
28
+ const { getToolRenderers, theme, cwd, width = 100 } = deps;
29
29
  const renderedCallComponents = new Map();
30
30
  const renderedResultComponents = new Map();
31
31
  const renderedStates = new Map();
@@ -58,7 +58,7 @@ export function createToolHtmlRenderer(deps) {
58
58
  renderCall(toolCallId, toolName, args) {
59
59
  try {
60
60
  renderedArgs.set(toolCallId, args);
61
- const toolDef = getToolDefinition(toolName);
61
+ const toolDef = getToolRenderers(toolName);
62
62
  if (!toolDef?.renderCall) {
63
63
  return undefined;
64
64
  }
@@ -74,7 +74,7 @@ export function createToolHtmlRenderer(deps) {
74
74
  },
75
75
  renderResult(toolCallId, toolName, result, details, isError) {
76
76
  try {
77
- const toolDef = getToolDefinition(toolName);
77
+ const toolDef = getToolRenderers(toolName);
78
78
  if (!toolDef?.renderResult) {
79
79
  return undefined;
80
80
  }