@bastani/atomic 0.9.25-alpha.2 → 0.9.25-alpha.3

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 (118) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/builtin/intercom/package.json +2 -2
  3. package/dist/builtin/mcp/CHANGELOG.md +13 -0
  4. package/dist/builtin/mcp/index.bundle.mjs +489 -208
  5. package/dist/builtin/mcp/package.json +2 -2
  6. package/dist/builtin/subagents/package.json +3 -3
  7. package/dist/builtin/web-access/package.json +2 -2
  8. package/dist/builtin/workflows/CHANGELOG.md +12 -0
  9. package/dist/builtin/workflows/builtin/{chunk-e1254zvd.js → chunk-kfsnh3w7.js} +40 -12
  10. package/dist/builtin/workflows/builtin/{chunk-cpw5gtzg.js → chunk-rqmasd3d.js} +1 -1
  11. package/dist/builtin/workflows/builtin/{chunk-qy6g0baf.js → chunk-wz783pvd.js} +1 -1
  12. package/dist/builtin/workflows/builtin/goal.js +2 -2
  13. package/dist/builtin/workflows/builtin/index.js +3 -3
  14. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  15. package/dist/builtin/workflows/package.json +2 -2
  16. package/dist/builtin/workflows/src/extension/index.bundle.mjs +472 -88
  17. package/dist/builtin/workflows/src/index.js +65 -28
  18. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  19. package/dist/core/agent-session-accessors.js +7 -0
  20. package/dist/core/agent-session-accessors.js.map +1 -1
  21. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  22. package/dist/core/agent-session-extension-bindings.js +16 -2
  23. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  24. package/dist/core/agent-session-methods.d.ts +7 -1
  25. package/dist/core/agent-session-methods.d.ts.map +1 -1
  26. package/dist/core/agent-session-methods.js.map +1 -1
  27. package/dist/core/agent-session-state.d.ts.map +1 -1
  28. package/dist/core/agent-session-state.js +3 -2
  29. package/dist/core/agent-session-state.js.map +1 -1
  30. package/dist/core/agent-session-types.d.ts +1 -0
  31. package/dist/core/agent-session-types.d.ts.map +1 -1
  32. package/dist/core/agent-session-types.js.map +1 -1
  33. package/dist/core/agent-session.d.ts +3 -0
  34. package/dist/core/agent-session.d.ts.map +1 -1
  35. package/dist/core/agent-session.js +6 -0
  36. package/dist/core/agent-session.js.map +1 -1
  37. package/dist/core/extensions/api-types.d.ts +3 -0
  38. package/dist/core/extensions/api-types.d.ts.map +1 -1
  39. package/dist/core/extensions/api-types.js.map +1 -1
  40. package/dist/core/extensions/index.d.ts +2 -0
  41. package/dist/core/extensions/index.d.ts.map +1 -1
  42. package/dist/core/extensions/index.js +1 -0
  43. package/dist/core/extensions/index.js.map +1 -1
  44. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  45. package/dist/core/extensions/loader-api.js +11 -0
  46. package/dist/core/extensions/loader-api.js.map +1 -1
  47. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  48. package/dist/core/extensions/loader-runtime.js +3 -0
  49. package/dist/core/extensions/loader-runtime.js.map +1 -1
  50. package/dist/core/extensions/mcp-server-registry.d.ts.map +1 -1
  51. package/dist/core/extensions/mcp-server-registry.js +4 -0
  52. package/dist/core/extensions/mcp-server-registry.js.map +1 -1
  53. package/dist/core/extensions/runner.d.ts +3 -0
  54. package/dist/core/extensions/runner.d.ts.map +1 -1
  55. package/dist/core/extensions/runner.js +4 -0
  56. package/dist/core/extensions/runner.js.map +1 -1
  57. package/dist/core/extensions/runtime-types.d.ts +3 -0
  58. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  59. package/dist/core/extensions/runtime-types.js.map +1 -1
  60. package/dist/core/extensions/tool-types.d.ts +1 -0
  61. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  62. package/dist/core/extensions/tool-types.js.map +1 -1
  63. package/dist/core/extensions/workflow-run-control-hub.d.ts +23 -0
  64. package/dist/core/extensions/workflow-run-control-hub.d.ts.map +1 -0
  65. package/dist/core/extensions/workflow-run-control-hub.js +57 -0
  66. package/dist/core/extensions/workflow-run-control-hub.js.map +1 -0
  67. package/dist/core/extensions/workflow-run-control.d.ts +163 -0
  68. package/dist/core/extensions/workflow-run-control.d.ts.map +1 -0
  69. package/dist/core/extensions/workflow-run-control.js +73 -0
  70. package/dist/core/extensions/workflow-run-control.js.map +1 -0
  71. package/dist/core/mcp-servers.d.ts +4 -1
  72. package/dist/core/mcp-servers.d.ts.map +1 -1
  73. package/dist/core/mcp-servers.js.map +1 -1
  74. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  75. package/dist/core/remote-catalog-provider.js +4 -9
  76. package/dist/core/remote-catalog-provider.js.map +1 -1
  77. package/dist/core/sdk.js +1 -0
  78. package/dist/core/sdk.js.map +1 -1
  79. package/dist/core/tool-loadout.d.ts +1 -0
  80. package/dist/core/tool-loadout.d.ts.map +1 -1
  81. package/dist/core/tool-loadout.js +3 -0
  82. package/dist/core/tool-loadout.js.map +1 -1
  83. package/dist/core/tools/bash.d.ts.map +1 -1
  84. package/dist/core/tools/bash.js +11 -30
  85. package/dist/core/tools/bash.js.map +1 -1
  86. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  87. package/dist/extensions/codemode/execute.js +26 -0
  88. package/dist/extensions/codemode/execute.js.map +1 -1
  89. package/dist/extensions/codemode/renderer.d.ts.map +1 -1
  90. package/dist/extensions/codemode/renderer.js +38 -23
  91. package/dist/extensions/codemode/renderer.js.map +1 -1
  92. package/dist/extensions/codemode/tool.d.ts.map +1 -1
  93. package/dist/extensions/codemode/tool.js +22 -10
  94. package/dist/extensions/codemode/tool.js.map +1 -1
  95. package/dist/extensions/tool-search/tool.d.ts +1 -1
  96. package/dist/extensions/tool-search/tool.d.ts.map +1 -1
  97. package/dist/extensions/tool-search/tool.js +3 -13
  98. package/dist/extensions/tool-search/tool.js.map +1 -1
  99. package/dist/index-extensions.d.ts +2 -1
  100. package/dist/index-extensions.d.ts.map +1 -1
  101. package/dist/index-extensions.js +1 -1
  102. package/dist/index-extensions.js.map +1 -1
  103. package/dist/modes/interactive/components/visual-truncate.d.ts +27 -3
  104. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  105. package/dist/modes/interactive/components/visual-truncate.js +39 -6
  106. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  107. package/docs/extensions/events.md +2 -0
  108. package/docs/mcp-servers.md +47 -0
  109. package/docs/providers.md +8 -0
  110. package/docs/sdk/reference.md +16 -0
  111. package/docs/sdk.md +67 -1
  112. package/docs/settings.md +2 -0
  113. package/docs/tools.md +6 -0
  114. package/docs/workflows/operations.md +3 -1
  115. package/examples/extensions/built-in-tool-renderer.ts +16 -40
  116. package/examples/extensions/minimal-mode.ts +28 -108
  117. package/npm-shrinkwrap.json +69 -69
  118. package/package.json +8 -8
@@ -30558,7 +30558,7 @@ function createRecoverablePostgresPool(initialUrl, options = {}) {
30558
30558
  release(true);
30559
30559
  for (const { client } of held) {
30560
30560
  if (client.listenerCount("error") > 0) {
30561
- client.emit("error", new Error("Postgres dependency invalidated"));
30561
+ client.emit("error", new DbosDependencyError("Postgres dependency invalidated"));
30562
30562
  }
30563
30563
  }
30564
30564
  }
@@ -30674,6 +30674,7 @@ function createRecoverablePostgresPool(initialUrl, options = {}) {
30674
30674
  var import_datasource;
30675
30675
  var init_dbos_recoverable_pool = __esm(() => {
30676
30676
  init_esm();
30677
+ init_dbos_admission();
30677
30678
  import_datasource = __toESM(require_datasource(), 1);
30678
30679
  });
30679
30680
 
@@ -87745,6 +87746,9 @@ function isMonitoringConnectionFailure(error) {
87745
87746
  return ["ECONNREFUSED", "ECONNRESET", "EPIPE", "ETIMEDOUT", "57P01", "57P02", "57P03"].includes(String(error.code));
87746
87747
  return error.message === "timeout expired" || error.message === "Connection terminated due to connection timeout";
87747
87748
  }
87749
+ function isQueryReadTimeout(error) {
87750
+ return error instanceof Error && error.message === "Query read timeout";
87751
+ }
87748
87752
 
87749
87753
  class PostgresHealth {
87750
87754
  operations;
@@ -87777,11 +87781,20 @@ class PostgresHealth {
87777
87781
  try {
87778
87782
  if (this.stopped)
87779
87783
  throw new DbosDependencyError;
87780
- await this.operations.validate?.(client);
87784
+ try {
87785
+ await this.operations.validate?.(client);
87786
+ } catch (error) {
87787
+ if (!isQueryReadTimeout(error) || this.stopped)
87788
+ throw error;
87789
+ await this.operations.validate?.(client);
87790
+ }
87781
87791
  } catch (error) {
87782
87792
  this.failure = error instanceof Error ? error : new Error(String(error));
87783
- this.invalidate();
87784
- throw error;
87793
+ if (!isQueryReadTimeout(error)) {
87794
+ this.invalidate();
87795
+ throw error;
87796
+ }
87797
+ throw new DbosDependencyError("Managed PostgreSQL did not answer a connection identity check in time.");
87785
87798
  }
87786
87799
  }
87787
87800
  check() {
@@ -87815,7 +87828,7 @@ class PostgresHealth {
87815
87828
  try {
87816
87829
  identity = await this.operations.probe();
87817
87830
  } catch (error) {
87818
- if (!isMonitoringConnectionFailure(error) || this.stopped)
87831
+ if (!(isMonitoringConnectionFailure(error) || isQueryReadTimeout(error)) || this.stopped)
87819
87832
  throw error;
87820
87833
  try {
87821
87834
  identity = await this.operations.probe();
@@ -87845,6 +87858,10 @@ class PostgresHealth {
87845
87858
  if (ready !== undefined)
87846
87859
  return ready;
87847
87860
  } catch (error) {
87861
+ if (isQueryReadTimeout(error)) {
87862
+ this.failure = new DbosDependencyError("Managed PostgreSQL did not answer a health check in time.");
87863
+ throw this.failure;
87864
+ }
87848
87865
  this.failure = error instanceof Error ? error : new Error(String(error));
87849
87866
  if (!(error instanceof Error && ("code" in error) && error.code === "53300"))
87850
87867
  this.invalidate();
@@ -88108,6 +88125,7 @@ var POSTGRES_IDENTITY_SQL = `SELECT current_setting('data_directory') AS data_di
88108
88125
  split_part(pg_read_file('postmaster.pid'), E'\\n', 3) AS started,
88109
88126
  system_identifier::text, current_setting('server_version') AS server_version FROM pg_control_system()`;
88110
88127
  var POSTGRES_TIMEZONE_SQL = "SELECT set_config('timezone_abbreviations', 'Default', true)";
88128
+ var POSTGRES_HEALTH_QUERY_TIMEOUT_MS = 3000;
88111
88129
  async function probePostgresTimezoneData(port) {
88112
88130
  const client = new Client({
88113
88131
  host: "127.0.0.1",
@@ -88117,8 +88135,8 @@ async function probePostgresTimezoneData(port) {
88117
88135
  database: "postgres",
88118
88136
  ssl: false,
88119
88137
  connectionTimeoutMillis: 1000,
88120
- query_timeout: 1000,
88121
- statement_timeout: 1000
88138
+ query_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS,
88139
+ statement_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS
88122
88140
  });
88123
88141
  client.on("error", () => {});
88124
88142
  try {
@@ -88137,8 +88155,8 @@ async function probePostgresIdentity(port) {
88137
88155
  database: "postgres",
88138
88156
  ssl: false,
88139
88157
  connectionTimeoutMillis: 1000,
88140
- query_timeout: 1000,
88141
- statement_timeout: 1000
88158
+ query_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS,
88159
+ statement_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS
88142
88160
  });
88143
88161
  client.on("error", () => {});
88144
88162
  try {
@@ -88146,6 +88164,8 @@ async function probePostgresIdentity(port) {
88146
88164
  const result = await client.query(POSTGRES_IDENTITY_SQL);
88147
88165
  return result.rows[0];
88148
88166
  } catch (error) {
88167
+ if (isQueryReadTimeout(error))
88168
+ throw error;
88149
88169
  const code = error instanceof Error && "code" in error ? error.code : undefined;
88150
88170
  if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "57P01" || code === "57P02" || code === "57P03" || error instanceof Error && /timeout|timed out|connection terminated/i.test(error.message))
88151
88171
  return;
@@ -88313,6 +88333,15 @@ async function ensureCluster(options = {}) {
88313
88333
  }
88314
88334
  let verified;
88315
88335
  const existingServerProbe = () => options.probeIdentity ?? probePostgresIdentity;
88336
+ const startupServerProbe = async (probePort) => {
88337
+ try {
88338
+ return await existingServerProbe()(probePort);
88339
+ } catch (error) {
88340
+ if (isQueryReadTimeout(error))
88341
+ return;
88342
+ throw error;
88343
+ }
88344
+ };
88316
88345
  let prepared = options.prepared ? options.binaries : undefined;
88317
88346
  let selectedIdentity = options.runtimeIdentity;
88318
88347
  let reservedLiveGeneration;
@@ -88433,7 +88462,7 @@ async function ensureCluster(options = {}) {
88433
88462
  }
88434
88463
  } else if (existing) {
88435
88464
  await waitForClusterReadiness(logFile, undefined, async () => {
88436
- verified = await verifyPostgresIdentity(metadata, port, existing.pid, existingServerProbe());
88465
+ verified = await verifyPostgresIdentity(metadata, port, existing.pid, startupServerProbe);
88437
88466
  return verified !== undefined || managedPostmaster(metadata) === undefined;
88438
88467
  }, READY_ATTEMPTS, delay, port, dataDir);
88439
88468
  }
@@ -88465,7 +88494,7 @@ async function ensureCluster(options = {}) {
88465
88494
  startedCluster = { lease };
88466
88495
  activeCluster = startedCluster;
88467
88496
  await waitForClusterReadiness(logFile, startedCluster, async () => {
88468
- verified = await verifyPostgresIdentity(metadata, port, lease.pid, options.probeIdentity);
88497
+ verified = await verifyPostgresIdentity(metadata, port, lease.pid, startupServerProbe);
88469
88498
  return verified !== undefined;
88470
88499
  }, READY_ATTEMPTS, delay, port, dataDir);
88471
88500
  break;
@@ -88534,14 +88563,14 @@ async function ensureCluster(options = {}) {
88534
88563
  probe: () => inspect(),
88535
88564
  validate: async (client) => {
88536
88565
  const identity = await inspect(async () => {
88537
- const query = { text: POSTGRES_IDENTITY_SQL, query_timeout: 1000 };
88566
+ const query = { text: POSTGRES_IDENTITY_SQL, query_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS };
88538
88567
  const result = await client.query(query);
88539
88568
  return result.rows[0];
88540
88569
  });
88541
88570
  if (!identity)
88542
88571
  throw new DbosDependencyError;
88543
88572
  if (options.probeIdentity === undefined) {
88544
- const query = { text: POSTGRES_TIMEZONE_SQL, query_timeout: 1000 };
88573
+ const query = { text: POSTGRES_TIMEZONE_SQL, query_timeout: POSTGRES_HEALTH_QUERY_TIMEOUT_MS };
88545
88574
  await client.query(query);
88546
88575
  }
88547
88576
  },
@@ -92489,6 +92518,11 @@ async function resumeRun(runId, opts) {
92489
92518
  ...acknowledgementMessage !== undefined ? { message: acknowledgementMessage } : {}
92490
92519
  };
92491
92520
  }
92521
+ function isRunFullyPaused(activeStore, runId, controlRunIds) {
92522
+ const runs = activeStore.runs();
92523
+ const hasLiveStage = controlRunIds.some((controlRunId) => runs.find((candidate) => candidate.id === controlRunId)?.stages.some((stage) => stage.status === "running" || stage.status === "pending") === true);
92524
+ return !hasLiveStage && runs.find((candidate) => candidate.id === runId)?.status === "paused";
92525
+ }
92492
92526
  async function pauseRunWithAction(runId, opts, action = "pause") {
92493
92527
  const activeStore = opts?.store ?? store;
92494
92528
  const registry = opts?.stageControlRegistry ?? stageControlRegistry;
@@ -92531,7 +92565,11 @@ async function pauseRunWithAction(runId, opts, action = "pause") {
92531
92565
  message: `Run ${runId} paused (${run.controlPersistence ?? "observed"}). ${run.controlPersistence === "durable" ? "Database persistence confirmed." : "Pause observed locally, not confirmed persisted."} Resume with /workflow resume on this live process; untracked initialization or workflow code may still finish, but further workflow steps and completion wait for resume. Cross-process resume requires durable checkpoint or pending prompt progress.`
92532
92566
  };
92533
92567
  }
92534
- return { ok: false, runId, reason: "no_active_stages" };
92568
+ return {
92569
+ ok: false,
92570
+ runId,
92571
+ reason: isRunFullyPaused(activeStore, runId, controlRunIds) ? "already_paused" : "no_active_stages"
92572
+ };
92535
92573
  }
92536
92574
  const paused = [];
92537
92575
  const pausedRunIds = new Set;
@@ -92574,12 +92612,22 @@ async function pauseRun(runId, opts) {
92574
92612
  async function pauseAllRuns(opts) {
92575
92613
  const activeStore = opts?.store ?? store;
92576
92614
  const inFlight = topLevelWorkflowRuns(activeStore.runs()).filter((run) => run.endedAt === undefined);
92577
- return Promise.all(inFlight.map((run) => pauseRun(run.id, {
92615
+ const settled = await Promise.allSettled(inFlight.map((run) => pauseRun(run.id, {
92578
92616
  store: activeStore,
92579
92617
  stageControlRegistry: opts?.stageControlRegistry,
92580
92618
  toolControlRegistry: opts?.toolControlRegistry,
92581
92619
  jobs: opts?.jobs
92582
92620
  })));
92621
+ return settled.map((result, index) => {
92622
+ if (result.status === "fulfilled")
92623
+ return result.value;
92624
+ return {
92625
+ ok: false,
92626
+ runId: inFlight[index].id,
92627
+ reason: "pause_failed",
92628
+ message: result.reason instanceof Error ? result.reason.message : String(result.reason)
92629
+ };
92630
+ });
92583
92631
  }
92584
92632
 
92585
92633
  // dist/builtin/workflows/src/shared/persistence-compaction-policy.ts
@@ -93598,6 +93646,8 @@ function stageFailureMessage(runId, resultReason, action) {
93598
93646
  return `Run not found: ${runId}`;
93599
93647
  case "already_ended":
93600
93648
  return `Run already ended: ${runId}`;
93649
+ case "already_paused":
93650
+ return `Run ${runId} is already paused.`;
93601
93651
  case "stage_not_found":
93602
93652
  return `Stage not found for run: ${runId}`;
93603
93653
  default:
@@ -93688,7 +93738,7 @@ function resolveStageTarget(runId, stageTarget, activeStore = store) {
93688
93738
  return ambiguousUuidStageTarget(target, uuidMatches);
93689
93739
  const matches = graph.stages.filter((stage) => stageMatchesExpandedIdentifier(stage, target));
93690
93740
  if (matches.length === 0)
93691
- return { ok: false, message: `Stage not found in run ${runId}: ${target}` };
93741
+ return { ok: false, message: `Stage not found in run ${runId}: ${target}`, code: "stage_not_found" };
93692
93742
  if (matches.length > 1)
93693
93743
  return ambiguousStageTarget(target, matches);
93694
93744
  return resolvedStageTarget(matches[0]);
@@ -93711,12 +93761,14 @@ function matchingUuidStages(stages, target) {
93711
93761
  function ambiguousUuidStageTarget(target, stages) {
93712
93762
  return {
93713
93763
  ok: false,
93764
+ code: "stage_ambiguous",
93714
93765
  message: `Ambiguous stage UUID prefix "${target}" matches: ${stages.map((stage) => stage.workflowGraphTarget.stageId).join(", ")}. Use the full 36-character UUID.`
93715
93766
  };
93716
93767
  }
93717
93768
  function ambiguousStageTarget(target, stages) {
93718
93769
  return {
93719
93770
  ok: false,
93771
+ code: "stage_ambiguous",
93720
93772
  message: `Ambiguous stage identifier "${target}" matches: ${stages.map(expandedStageLabel).join(", ")}`
93721
93773
  };
93722
93774
  }
@@ -93742,10 +93794,11 @@ function resolveControlNodeTarget(runId, stageTarget, activeStore = store) {
93742
93794
  if (matches.length > 1)
93743
93795
  return {
93744
93796
  ok: false,
93797
+ code: "stage_ambiguous",
93745
93798
  message: `Ambiguous stage identifier "${target}" matches: ${matches.map(expandedStageLabel).join(", ")}`
93746
93799
  };
93747
93800
  }
93748
- return { ok: false, message: `Stage not found in run ${runId}: ${target}` };
93801
+ return { ok: false, message: `Stage not found in run ${runId}: ${target}`, code: "stage_not_found" };
93749
93802
  }
93750
93803
  function resolvedControlNodeTarget(node) {
93751
93804
  const graphTarget = node.workflowGraphTarget;
@@ -106960,7 +107013,7 @@ async function resumeDurableWorkflowClaimed(workflowId, deps, catalog) {
106960
107013
  function foreignRunningResult(name, workflowId) {
106961
107014
  return {
106962
107015
  ok: false,
106963
- reason: "not_resumable",
107016
+ reason: "owned_elsewhere",
106964
107017
  message: `Workflow "${name}" (${workflowId}) is actively running in another Atomic session. ` + "Control it from that session; it becomes resumable here only after that session pauses, quits, or crashes."
106965
107018
  };
106966
107019
  }
@@ -114012,7 +114065,8 @@ async function run(def, inputs, opts = {}) {
114012
114065
  let admissionControlError;
114013
114066
  let pausePersistence;
114014
114067
  let resumePersistence;
114015
- let controlAttempt;
114068
+ let controlSettlements = Promise.resolve();
114069
+ let latestControl = 0;
114016
114070
  let pauseGeneration = 0;
114017
114071
  const persistRunControl = async (status) => {
114018
114072
  ownController.signal.throwIfAborted();
@@ -114026,27 +114080,30 @@ async function run(def, inputs, opts = {}) {
114026
114080
  }
114027
114081
  if (durableBackend.getWorkflow(runId) === undefined)
114028
114082
  return;
114029
- controlAttempt?.abort(new Error("Workflow control superseded"));
114030
- const attempt = new AbortController;
114031
- controlAttempt = attempt;
114083
+ const previous = controlSettlements;
114084
+ const control = ++latestControl;
114032
114085
  const settlement = (async () => {
114033
114086
  try {
114034
- await boundedAdmission((signal) => dbosAdmissionContext.run(signal, async () => {
114035
- if (!await transitionDurableWorkflowStatus(durableBackend, runId, ["running", "paused"], status, undefined, true)) {
114036
- throw new Error(`Workflow ${runId} refused the durable ${status} transition`);
114037
- }
114038
- signal.throwIfAborted();
114039
- recordRunTimingCheckpoint(durableBackend, runSnapshot);
114040
- await durableBackend.flush(runId);
114041
- }), AbortSignal.any([ownController.signal, attempt.signal]), DBOS_ADMISSION_TIMEOUT_MS);
114042
- attempt.signal.throwIfAborted();
114087
+ await boundedAdmission(async (signal) => {
114088
+ await raceAbort(previous, signal);
114089
+ await dbosAdmissionContext.run(signal, async () => {
114090
+ if (!await transitionDurableWorkflowStatus(durableBackend, runId, ["running", "paused"], status, undefined, true)) {
114091
+ throw new Error(`Workflow ${runId} refused the durable ${status} transition`);
114092
+ }
114093
+ signal.throwIfAborted();
114094
+ recordRunTimingCheckpoint(durableBackend, runSnapshot);
114095
+ await durableBackend.flush(runId);
114096
+ });
114097
+ }, ownController.signal, DBOS_ADMISSION_TIMEOUT_MS);
114043
114098
  ownController.signal.throwIfAborted();
114099
+ if (control !== latestControl)
114100
+ return;
114044
114101
  activeStore.recordRunExecutionState(runId, {
114045
114102
  controlPersistence: durableBackend.persistent ? "durable" : "observed",
114046
114103
  ...durableRootAdmitted ? { phase: "executing", dependencyError: undefined } : {}
114047
114104
  });
114048
114105
  } catch (error) {
114049
- if (!attempt.signal.aborted && !ownController.signal.aborted && isDbosDependencyError(error)) {
114106
+ if (control === latestControl && !ownController.signal.aborted && isDbosDependencyError(error)) {
114050
114107
  activeStore.recordRunExecutionState(runId, {
114051
114108
  phase: "blocked_dependency",
114052
114109
  dependencyError: `Workflow database unavailable during ${status === "paused" ? "pause" : "resume"}; persistence not confirmed.`
@@ -114057,6 +114114,11 @@ async function run(def, inputs, opts = {}) {
114057
114114
  throw error;
114058
114115
  }
114059
114116
  })();
114117
+ controlSettlements = settlement.then(() => {
114118
+ return;
114119
+ }, () => {
114120
+ return;
114121
+ });
114060
114122
  if (status === "running")
114061
114123
  return settlement;
114062
114124
  let acknowledgementTimer;
@@ -114085,7 +114147,6 @@ async function run(def, inputs, opts = {}) {
114085
114147
  pause: () => {
114086
114148
  ownController.signal.throwIfAborted();
114087
114149
  pauseGeneration++;
114088
- controlAttempt?.abort(new Error("Workflow control superseded by pause"));
114089
114150
  scheduler.pauseRun();
114090
114151
  activeStore.recordRunPaused(runId, undefined, { resumable: true });
114091
114152
  pausePersistence = persistRunControl("paused");
@@ -115625,7 +115686,7 @@ function createExtensionRuntime(opts = {}) {
115625
115686
  if (!resumed.ok)
115626
115687
  return {
115627
115688
  ok: false,
115628
- reason: resumed.reason === "workflow_not_found" ? "workflow_not_found" : resumed.reason === "not_resumable" || resumed.reason === "stale" ? "not_resumable" : "insufficient_state",
115689
+ reason: resumed.reason === "workflow_not_found" ? "workflow_not_found" : resumed.reason === "owned_elsewhere" ? "owned_elsewhere" : resumed.reason === "not_resumable" || resumed.reason === "stale" ? "not_resumable" : "insufficient_state",
115629
115690
  message: resumed.message
115630
115691
  };
115631
115692
  return {
@@ -119459,6 +119520,33 @@ function openWorkflowResumeSelector(ui, liveRuns, hydrate, options = {}) {
119459
119520
  });
119460
119521
  }
119461
119522
 
119523
+ // dist/builtin/workflows/src/extension/workflow-bulk-control.ts
119524
+ function isAlreadyPaused(result) {
119525
+ return !result.ok && result.reason === "already_paused";
119526
+ }
119527
+ function leavesRunActive(result) {
119528
+ return !result.ok && result.reason !== "already_paused" && result.reason !== "already_ended";
119529
+ }
119530
+ function bulkFailedRuns(results) {
119531
+ return results.filter(leavesRunActive).map((result) => ({
119532
+ runId: result.runId,
119533
+ reason: result.reason,
119534
+ ...result.message === undefined ? {} : { message: result.message }
119535
+ }));
119536
+ }
119537
+ function bulkAlreadyPausedCount(results) {
119538
+ return results.filter(isAlreadyPaused).length;
119539
+ }
119540
+ function bulkUnstoppedStatus(results) {
119541
+ return results.some((result) => result.ok) ? { status: "partial" } : { status: "noop", code: "control_failed" };
119542
+ }
119543
+ function bulkFailureMessage(verb, action, results, describeSuccess) {
119544
+ const successes = results.filter((result) => result.ok).length;
119545
+ const failures = results.filter(leavesRunActive).length;
119546
+ const outcomes = results.map((result) => result.ok ? describeSuccess(result) : `${result.runId}: ${result.reason}${result.message === undefined ? "" : ` (${result.message})`}`).join(", ");
119547
+ return `${successes > 0 ? `${verb} ${successes} run(s); ` : ""}failed to ${action} ${failures} run(s); outcomes: ${outcomes}.`;
119548
+ }
119549
+
119462
119550
  // dist/builtin/workflows/src/durable/resume-catalog.ts
119463
119551
  function formatResumableWorkflowList(entries) {
119464
119552
  if (entries.length === 0)
@@ -119819,12 +119907,11 @@ ${renderSessionList(store.runs(), { theme, includeAll: true })}`);
119819
119907
  }
119820
119908
  }
119821
119909
  const results = action === "quit" ? await quitAllRuns({ ...owner, actor: "user" }) : await pauseAllRuns(owner);
119822
- const successes = results.filter((result) => result.ok);
119910
+ const successes = results.flatMap((result) => result.ok ? [result] : []);
119823
119911
  const changed = successes.length;
119824
- const failures = results.filter((result) => !result.ok);
119825
- if (action === "quit" && failures.length > 0) {
119826
- const outcomes = results.map((result) => result.ok ? result.message ?? `${result.runId}: quit` : `${result.runId}: ${result.reason}${"message" in result ? ` (${result.message})` : ""}`).join(", ");
119827
- const message = `${changed > 0 ? `Quit ${changed} run(s); ` : ""}failed to quit ${failures.length} run(s); outcomes: ${outcomes}.`;
119912
+ const alreadyPaused = bulkAlreadyPausedCount(results);
119913
+ if (bulkFailedRuns(results).length > 0) {
119914
+ const message = bulkFailureMessage(action === "quit" ? "Quit" : "Paused", action, results, (result) => result.message ?? `${result.runId}: ${action === "quit" ? "quit" : "paused"}`);
119828
119915
  if (changed > 0)
119829
119916
  print(message);
119830
119917
  else
@@ -119832,6 +119919,8 @@ ${renderSessionList(store.runs(), { theme, includeAll: true })}`);
119832
119919
  } else if (changed > 0) {
119833
119920
  print(successes.some((result) => result.message !== undefined) ? successes.map((result) => result.message ?? `Run ${result.runId} stopped.`).join(`
119834
119921
  `) : action === "quit" ? `Quit ${changed} run(s); resume with /workflow resume.` : `Paused ${changed} run(s).`);
119922
+ } else if (alreadyPaused > 0) {
119923
+ print(`${alreadyPaused} in-flight run(s) already paused.`);
119835
119924
  } else {
119836
119925
  fail(`No in-flight runs to ${action}.`);
119837
119926
  }
@@ -119878,6 +119967,8 @@ ${renderSessionList(store.runs(), { theme, includeAll: true })}`);
119878
119967
  const result = await pauseRun(resolved.runId, owner);
119879
119968
  if (result.ok)
119880
119969
  print(result.message ?? `Run ${result.runId} paused and can be resumed.`);
119970
+ else if (result.reason === "already_paused")
119971
+ print(`Run ${result.runId} is already paused.`);
119881
119972
  else
119882
119973
  fail(result.reason === "not_found" ? `Run not found: ${target}` : result.reason === "already_ended" ? `Run already ended: ${target}` : result.reason === "stage_not_found" ? `Stage not found for run ${resolved.runId}.` : `No active stages to pause on run ${resolved.runId}.`);
119883
119974
  } catch (error) {
@@ -120401,11 +120492,31 @@ function createWorkflowObservation(store, publisher, ownerSessionId) {
120401
120492
  };
120402
120493
  }
120403
120494
 
120404
- // dist/builtin/workflows/src/extension/workflow-tool.ts
120405
- import { getSupportedThinkingLevels } from "@bastani/pi-ai/compat";
120495
+ // dist/builtin/workflows/src/extension/workflow-session-run-control.ts
120496
+ import {
120497
+ WorkflowRunControlError,
120498
+ WorkflowRunControlUnavailableError,
120499
+ WorkflowRunDatabaseError,
120500
+ WorkflowRunNotFoundError,
120501
+ WorkflowRunNotResumableError,
120502
+ WorkflowRunOwnershipError,
120503
+ WorkflowStageAmbiguousError,
120504
+ WorkflowStageNotFoundError,
120505
+ WorkflowStageResumeUnsupportedError
120506
+ } from "@bastani/atomic";
120507
+
120508
+ // dist/builtin/workflows/src/extension/workflow-control-failure.ts
120509
+ init_dbos_admission();
120406
120510
 
120407
120511
  // dist/builtin/workflows/src/extension/workflow-instance-owner.ts
120408
120512
  var anonymousCallers = new WeakMap;
120513
+
120514
+ class WorkflowInstanceOwnershipError extends Error {
120515
+ constructor(message) {
120516
+ super(message);
120517
+ this.name = "WorkflowInstanceOwnershipError";
120518
+ }
120519
+ }
120409
120520
  function workflowCaller(ctx) {
120410
120521
  const sessionId = ctx.sessionId ?? ctx.sessionManager?.getSessionId?.();
120411
120522
  if (sessionId)
@@ -120431,9 +120542,9 @@ function assertWorkflowInstanceOwner(id, ctx, store) {
120431
120542
  const modelOwner = durable?.modelOwner ?? local?.modelOwner;
120432
120543
  if (modelOwner !== undefined) {
120433
120544
  if (modelOwner !== workflowCaller(ctx))
120434
- throw new Error("Workflow instance belongs to another caller/session.");
120545
+ throw new WorkflowInstanceOwnershipError("Workflow instance belongs to another caller/session.");
120435
120546
  } else if ((durable?.origin ?? local?.origin) === "agent") {
120436
- throw new Error("Workflow instance ownership is unavailable; another caller cannot assume authority.");
120547
+ throw new WorkflowInstanceOwnershipError("Workflow instance ownership is unavailable; another caller cannot assume authority.");
120437
120548
  }
120438
120549
  const parentId = durable?.rootWorkflowId ?? local?.parentRunId;
120439
120550
  if (parentId !== undefined)
@@ -120451,6 +120562,176 @@ function assertWorkflowInstanceOwner(id, ctx, store) {
120451
120562
  }
120452
120563
  }
120453
120564
 
120565
+ // dist/builtin/workflows/src/extension/workflow-control-failure.ts
120566
+ function classifyControlError(error) {
120567
+ if (error instanceof WorkflowInstanceOwnershipError)
120568
+ return "owned_elsewhere";
120569
+ if (error instanceof DbosDurabilityError || isDbosDependencyError(error))
120570
+ return "database_unavailable";
120571
+ return "control_failed";
120572
+ }
120573
+ function resumeFailureCode(reason) {
120574
+ switch (reason) {
120575
+ case "not_resumable":
120576
+ case "stale":
120577
+ return "not_resumable";
120578
+ case "owned_elsewhere":
120579
+ return "owned_elsewhere";
120580
+ case "not_registered":
120581
+ case "run_not_found":
120582
+ return "run_not_found";
120583
+ case "workflow_not_found":
120584
+ case "invalid_inputs":
120585
+ case "startup_failed":
120586
+ case "insufficient_state":
120587
+ return "control_failed";
120588
+ }
120589
+ }
120590
+
120591
+ // dist/builtin/workflows/src/extension/workflow-session-run-control.ts
120592
+ function executeContext(ctx) {
120593
+ if (ctx === undefined)
120594
+ throw new WorkflowRunControlUnavailableError;
120595
+ return {
120596
+ sessionId: ctx.sessionManager?.getSessionId?.(),
120597
+ sessionManager: ctx.sessionManager,
120598
+ ui: ctx.ui,
120599
+ hasUI: ctx.hasUI,
120600
+ hasHumanInput: ctx.hasHumanInput,
120601
+ model: ctx.model,
120602
+ modelRegistry: ctx.modelRegistry,
120603
+ cwd: ctx.cwd
120604
+ };
120605
+ }
120606
+ function errorOptions(runId, cause, failedRuns) {
120607
+ return {
120608
+ ...runId === undefined || runId === "--all" ? {} : { runId },
120609
+ ...cause === undefined ? {} : { cause },
120610
+ ...failedRuns === undefined ? {} : { failedRuns }
120611
+ };
120612
+ }
120613
+ function failure3(code, message, runId, cause, failedRuns) {
120614
+ const options = errorOptions(runId, cause, failedRuns);
120615
+ switch (code) {
120616
+ case "run_not_found":
120617
+ return new WorkflowRunNotFoundError(message, options);
120618
+ case "not_resumable":
120619
+ return new WorkflowRunNotResumableError(message, options);
120620
+ case "owned_elsewhere":
120621
+ return new WorkflowRunOwnershipError(message, options);
120622
+ case "stage_not_found":
120623
+ return new WorkflowStageNotFoundError(message, options);
120624
+ case "stage_ambiguous":
120625
+ return new WorkflowStageAmbiguousError(message, options);
120626
+ case "stage_resume_unsupported":
120627
+ return new WorkflowStageResumeUnsupportedError(message, options);
120628
+ case "database_unavailable":
120629
+ return new WorkflowRunDatabaseError(message, options);
120630
+ case "control_failed":
120631
+ return new WorkflowRunControlError("WORKFLOW_RUN_CONTROL_FAILED", message, options);
120632
+ }
120633
+ }
120634
+ function translateThrown(error, runId) {
120635
+ const code = classifyControlError(error);
120636
+ return code === "owned_elsewhere" || code === "database_unavailable" ? failure3(code, error.message, runId, error) : error;
120637
+ }
120638
+ function controlStatus(status, runId) {
120639
+ switch (status) {
120640
+ case "ok":
120641
+ case "running":
120642
+ case "paused":
120643
+ case "partial":
120644
+ case "noop":
120645
+ case "cancelled":
120646
+ return status;
120647
+ default:
120648
+ throw failure3("control_failed", `Unexpected workflow control status: ${status}`, runId);
120649
+ }
120650
+ }
120651
+ function controlOutcome(result) {
120652
+ if (result.action !== "pause" && result.action !== "quit" && result.action !== "resume") {
120653
+ throw failure3("control_failed", `Unexpected workflow result: ${result.action}`, undefined);
120654
+ }
120655
+ if ("code" in result && result.code !== undefined) {
120656
+ throw failure3(result.code, result.message, result.runId, undefined, "failedRuns" in result ? result.failedRuns : undefined);
120657
+ }
120658
+ return { ...result, status: controlStatus(result.status, result.runId) };
120659
+ }
120660
+ function rejectUnknownRunPrefix(store, runId) {
120661
+ const target = runId.trim();
120662
+ if (!isRunIdPrefix(target))
120663
+ return;
120664
+ const resolution = resolveRunIdTarget(target, store.runs().map((run) => run.id));
120665
+ if (resolution.kind === "not_found") {
120666
+ throw failure3("run_not_found", `Run not found: ${target} (no run in this session has that prefix; pass the full run id for a run recorded elsewhere).`, runId);
120667
+ }
120668
+ }
120669
+ function createSessionRunControl(host) {
120670
+ const request = async (args) => {
120671
+ try {
120672
+ const ctx = executeContext(host.context());
120673
+ if (args.runId !== undefined && (args.action === "status" || args.action === "stages"))
120674
+ rejectUnknownRunPrefix(host.store, args.runId);
120675
+ return await host.execute(args, ctx);
120676
+ } catch (error) {
120677
+ throw error instanceof Error ? translateThrown(error, args.runId) : error;
120678
+ }
120679
+ };
120680
+ const target = (runId) => typeof runId === "string" ? { runId } : { all: true };
120681
+ return {
120682
+ async listRuns(filter) {
120683
+ const result = await request({
120684
+ action: "status",
120685
+ ...filter?.status === undefined ? {} : { statusFilter: filter.status }
120686
+ });
120687
+ if (result.action !== "status")
120688
+ throw failure3("control_failed", `Unexpected workflow result: ${result.action}`, undefined);
120689
+ return result.runs;
120690
+ },
120691
+ async getRun(runId) {
120692
+ const result = await request({ action: "status", runId });
120693
+ if (result.action !== "statusDetail")
120694
+ throw failure3("control_failed", `Unexpected workflow result: ${result.action}`, runId);
120695
+ if ("error" in result)
120696
+ throw failure3("run_not_found", result.error, runId);
120697
+ return result.detail;
120698
+ },
120699
+ async getStages(runId, filter) {
120700
+ const result = await request({
120701
+ action: "stages",
120702
+ runId,
120703
+ ...filter?.status === undefined ? {} : { statusFilter: filter.status }
120704
+ });
120705
+ if (result.action !== "stages")
120706
+ throw failure3("control_failed", `Unexpected workflow result: ${result.action}`, runId);
120707
+ if (result.error !== undefined)
120708
+ throw failure3("run_not_found", result.error, runId);
120709
+ return result.stages;
120710
+ },
120711
+ async pause(runId, options) {
120712
+ return controlOutcome(await request({
120713
+ action: "pause",
120714
+ ...target(runId),
120715
+ ...options?.stageId === undefined ? {} : { stageId: options.stageId }
120716
+ }));
120717
+ },
120718
+ async quit(runId) {
120719
+ return controlOutcome(await request({ action: "quit", ...target(runId) }));
120720
+ },
120721
+ async resume(runId, options) {
120722
+ return controlOutcome(await request({
120723
+ action: "resume",
120724
+ runId,
120725
+ ...options?.stageId === undefined ? {} : { stageId: options.stageId },
120726
+ ...options?.message === undefined ? {} : { message: options.message }
120727
+ }));
120728
+ }
120729
+ };
120730
+ }
120731
+
120732
+ // dist/builtin/workflows/src/extension/workflow-tool.ts
120733
+ import { getSupportedThinkingLevels } from "@bastani/pi-ai/compat";
120734
+
120454
120735
  // dist/builtin/workflows/src/extension/workflow-status-summary.ts
120455
120736
  var workflowStatusRenderRuns = new WeakMap;
120456
120737
  function setWorkflowStatusRenderRuns(result, runs) {
@@ -121054,12 +121335,41 @@ async function abortToolNode(runId, nodeId, opts) {
121054
121335
  }
121055
121336
 
121056
121337
  // dist/builtin/workflows/src/extension/workflow-tool-control.ts
121057
- function controlFailure(action, runId, error) {
121338
+ async function foreignRunFailure(action, target, guard) {
121339
+ if (guard === undefined || !isFullRunId(target))
121340
+ return;
121341
+ let durable;
121342
+ try {
121343
+ durable = await guard.getRuntime().inspectDurableWorkflow(target);
121344
+ } catch (error) {
121345
+ return controlFailure(action, target, error, "database_unavailable");
121346
+ }
121347
+ if (durable.kind !== "found")
121348
+ return;
121349
+ const runId = durable.detail.runId;
121350
+ const foreign = (message) => ({
121351
+ action,
121352
+ runId,
121353
+ status: "noop",
121354
+ message,
121355
+ code: "owned_elsewhere"
121356
+ });
121357
+ try {
121358
+ guard.authorize?.(runId);
121359
+ } catch (error) {
121360
+ if (error instanceof WorkflowInstanceOwnershipError)
121361
+ return foreign(error.message);
121362
+ throw error;
121363
+ }
121364
+ return durable.detail.ownerActiveElsewhere === true ? foreign(`Workflow ${runId} is actively running in another Atomic session. Control it from that session; it can be ${action === "pause" ? "paused" : "quit"} only by its owner.`) : undefined;
121365
+ }
121366
+ function controlFailure(action, runId, error, code = classifyControlError(error)) {
121058
121367
  return {
121059
121368
  action,
121060
121369
  runId,
121061
121370
  status: "noop",
121062
- message: `Failed to ${action} run ${runId}: ${error instanceof Error ? error.message : String(error)}`
121371
+ message: `Failed to ${action} run ${runId}: ${error instanceof Error ? error.message : String(error)}`,
121372
+ code
121063
121373
  };
121064
121374
  }
121065
121375
  function resumeControlFailure(runId, error, store) {
@@ -121070,7 +121380,8 @@ function resumeControlFailure(runId, error, store) {
121070
121380
  action: "resume",
121071
121381
  runId,
121072
121382
  status: visiblyRunning ? "partial" : "noop",
121073
- message: `Failed to resume run ${runId}: ${detail}`
121383
+ message: `Failed to resume run ${runId}: ${detail}`,
121384
+ ...visiblyRunning ? {} : { code: classifyControlError(error) }
121074
121385
  };
121075
121386
  }
121076
121387
  async function workflowReloadAction(args, deps) {
@@ -121096,12 +121407,6 @@ async function workflowReloadAction(args, deps) {
121096
121407
  };
121097
121408
  }
121098
121409
  }
121099
- function bulkQuitFailureMessage(results) {
121100
- const successes = results.filter((result) => result.ok).length;
121101
- const failures = results.length - successes;
121102
- const outcomes = results.map((result) => result.ok ? result.message ?? `${result.runId}: quit${abandonedToolSuffix(result.abandonedTools)}` : `${result.runId}: ${result.reason}${"message" in result ? ` (${result.message})` : ""}`).join(", ");
121103
- return `${successes > 0 ? `Quit ${successes} run(s); ` : ""}failed to quit ${failures} run(s); outcomes: ${outcomes}.`;
121104
- }
121105
121410
  function abandonedToolSuffix(abandonedTools) {
121106
121411
  return abandonedTools.length === 0 ? "" : ` (abandoned tool node(s): ${abandonedTools.map(formatToolNodeIdentity).join(", ")})`;
121107
121412
  }
@@ -121140,7 +121445,7 @@ async function quitToolNodeAction(runId, nodeId, action, owner) {
121140
121445
  message: `Cancelled ctx.tool ${aborted.name} (${nodeId}) on run ${runId}.${abandonedNote}` + ` Sibling work was not aborted. Current workflow status: ${workflowStatus}.` + " If workflow code awaited this call without catching cancellation, the workflow may fail." + " A later resume re-runs this call."
121141
121446
  };
121142
121447
  }
121143
- async function workflowQuitAction(args, owner = captureWorkflowOwnerResources()) {
121448
+ async function workflowQuitAction(args, owner = captureWorkflowOwnerResources(), guard) {
121144
121449
  const { store } = owner;
121145
121450
  const target = resolveToolRunTarget(args, "No in-flight runs to quit.", store);
121146
121451
  const action = "quit";
@@ -121151,21 +121456,32 @@ async function workflowQuitAction(args, owner = captureWorkflowOwnerResources())
121151
121456
  const results = await quitAllRuns({ ...owner, actor: "agent" });
121152
121457
  const successes = results.filter((result) => result.ok);
121153
121458
  const quitCount = successes.length;
121154
- const failures = results.filter((result) => !result.ok);
121459
+ const failedRuns = bulkFailedRuns(results);
121460
+ if (failedRuns.length > 0) {
121461
+ return {
121462
+ action,
121463
+ runId: "--all",
121464
+ ...bulkUnstoppedStatus(results),
121465
+ message: bulkFailureMessage("Quit", action, results, (result) => result.message ?? `${result.runId}: quit${abandonedToolSuffix(result.abandonedTools)}`),
121466
+ failedRuns
121467
+ };
121468
+ }
121155
121469
  return {
121156
121470
  action,
121157
121471
  runId: "--all",
121158
- status: failures.length > 0 ? quitCount > 0 ? "partial" : "noop" : quitCount > 0 ? "paused" : "noop",
121159
- message: failures.length > 0 ? bulkQuitFailureMessage(results) : quitCount > 0 ? successes.some((result) => result.message !== undefined) ? successes.map((result) => result.message ?? `Run ${result.runId} quit.`).join(`
121472
+ status: quitCount > 0 ? "paused" : "noop",
121473
+ message: quitCount > 0 ? successes.some((result) => result.message !== undefined) ? successes.map((result) => result.message ?? `Run ${result.runId} quit.`).join(`
121160
121474
  `) : `Quit ${quitCount} run(s); resume with /workflow resume.` : "No in-flight runs to quit."
121161
121475
  };
121162
121476
  }
121163
121477
  if (target.kind === "malformed" || target.kind === "not_found") {
121164
- return { action, runId: target.target, status: "noop", message: target.message };
121478
+ const foreign = target.kind === "not_found" ? await foreignRunFailure(action, target.target, guard) : undefined;
121479
+ return foreign ?? { action, runId: target.target, status: "noop", message: target.message, code: "run_not_found" };
121165
121480
  }
121166
121481
  const controlNode = resolveControlNodeTarget(target.runId, args.stageId, store);
121167
- if (!controlNode.ok)
121168
- return { action, runId: target.runId, status: "noop", message: controlNode.message };
121482
+ if (!controlNode.ok) {
121483
+ return { action, runId: target.runId, status: "noop", message: controlNode.message, code: controlNode.code };
121484
+ }
121169
121485
  if (controlNode.kind === "tool")
121170
121486
  return quitToolNodeAction(controlNode.runId, controlNode.nodeId, action, owner);
121171
121487
  try {
@@ -121178,17 +121494,19 @@ async function workflowQuitAction(args, owner = captureWorkflowOwnerResources())
121178
121494
  message: result.message ?? `Run ${result.runId} quit and can be resumed with /workflow resume.${cancelledToolSummary(result)}`
121179
121495
  };
121180
121496
  }
121497
+ const benign = result.reason === "already_ended" || result.reason === "no_active_stages";
121181
121498
  return {
121182
121499
  action,
121183
121500
  runId: target.runId,
121184
121501
  status: "noop",
121185
- message: result.reason === "already_ended" ? `Run ${target.runId} already ended.` : result.reason === "no_active_stages" ? `No controllable stages on run ${target.runId}; the run remains active.` : `Run not found: ${target.runId}`
121502
+ message: result.reason === "already_ended" ? `Run ${target.runId} already ended.` : result.reason === "no_active_stages" ? `No controllable stages on run ${target.runId}; the run remains active.` : `Run not found: ${target.runId}`,
121503
+ ...benign ? {} : { code: "run_not_found" }
121186
121504
  };
121187
121505
  } catch (error) {
121188
121506
  return controlFailure(action, target.runId, error);
121189
121507
  }
121190
121508
  }
121191
- async function workflowPauseAction(args, owner = captureWorkflowOwnerResources()) {
121509
+ async function workflowPauseAction(args, owner = captureWorkflowOwnerResources(), guard) {
121192
121510
  const { store } = owner;
121193
121511
  const target = resolveToolRunTarget(args, "No in-flight runs to pause.", store);
121194
121512
  const action = "pause";
@@ -121199,12 +121517,24 @@ async function workflowPauseAction(args, owner = captureWorkflowOwnerResources()
121199
121517
  try {
121200
121518
  const results = await pauseAllRuns(owner);
121201
121519
  const paused = results.filter((result) => result.ok).length;
121520
+ const failedRuns = bulkFailedRuns(results);
121521
+ if (failedRuns.length > 0) {
121522
+ return {
121523
+ action,
121524
+ runId: "--all",
121525
+ ...bulkUnstoppedStatus(results),
121526
+ message: bulkFailureMessage("Paused", action, results, (result) => result.message ?? `${result.runId}: paused`),
121527
+ failedRuns
121528
+ };
121529
+ }
121530
+ const alreadyPaused = bulkAlreadyPausedCount(results);
121531
+ const summary = paused > 0 ? `Paused ${paused} run(s)${alreadyPaused > 0 ? `; ${alreadyPaused} already paused` : ""}.` : alreadyPaused > 0 ? `${alreadyPaused} in-flight run(s) already paused.` : "No in-flight runs to pause.";
121202
121532
  return {
121203
121533
  action,
121204
121534
  runId: "--all",
121205
121535
  status: paused > 0 ? "paused" : "noop",
121206
121536
  message: [
121207
- paused > 0 ? `Paused ${paused} run(s).` : "No in-flight runs to pause.",
121537
+ summary,
121208
121538
  ...results.flatMap((result) => result.ok && result.message !== undefined ? [result.message] : [])
121209
121539
  ].join(`
121210
121540
  `)
@@ -121213,16 +121543,19 @@ async function workflowPauseAction(args, owner = captureWorkflowOwnerResources()
121213
121543
  return controlFailure(action, "--all", error);
121214
121544
  }
121215
121545
  }
121216
- if (target.kind === "malformed" || target.kind === "not_found")
121217
- return { action, runId: target.target, status: "noop", message: target.message };
121546
+ if (target.kind === "malformed" || target.kind === "not_found") {
121547
+ const foreign = target.kind === "not_found" ? await foreignRunFailure(action, target.target, guard) : undefined;
121548
+ return foreign ?? { action, runId: target.target, status: "noop", message: target.message, code: "run_not_found" };
121549
+ }
121218
121550
  const controlNode = resolveControlNodeTarget(target.runId, args.stageId, store);
121219
- if (!controlNode.ok)
121220
- return { action, runId: target.runId, status: "noop", message: controlNode.message };
121551
+ if (!controlNode.ok) {
121552
+ return { action, runId: target.runId, status: "noop", message: controlNode.message, code: controlNode.code };
121553
+ }
121221
121554
  if (controlNode.kind === "tool")
121222
121555
  return quitToolNodeAction(controlNode.runId, controlNode.nodeId, action, owner);
121223
121556
  const stage = resolveToolStageTarget(target.runId, args.stageId, store);
121224
121557
  if (!stage.ok)
121225
- return { action, runId: target.runId, status: "noop", message: stage.message };
121558
+ return { action, runId: target.runId, status: "noop", message: stage.message, code: stage.code };
121226
121559
  const stageRunId = stage.runId ?? target.runId;
121227
121560
  try {
121228
121561
  const result = await pauseRun(stageRunId, { ...owner, stageId: stage.stageId });
@@ -121238,7 +121571,9 @@ async function workflowPauseAction(args, owner = captureWorkflowOwnerResources()
121238
121571
  action,
121239
121572
  runId: stageRunId,
121240
121573
  status: "noop",
121241
- message: stageFailureMessage(stageRunId, result.reason, "pause")
121574
+ message: stageFailureMessage(stageRunId, result.reason, "pause"),
121575
+ ...result.reason === "not_found" ? { code: "run_not_found" } : {},
121576
+ ...result.reason === "stage_not_found" ? { code: "stage_not_found" } : {}
121242
121577
  };
121243
121578
  } catch (error) {
121244
121579
  return controlFailure(action, stageRunId, error);
@@ -121273,7 +121608,8 @@ ${resumed.message}`;
121273
121608
  action: "resume",
121274
121609
  runId: resumed.ok ? resumed.runId : runId,
121275
121610
  status: resumed.ok ? "running" : "noop",
121276
- message
121611
+ message,
121612
+ ...resumed.ok ? {} : { code: resumeFailureCode(resumed.reason) }
121277
121613
  };
121278
121614
  }
121279
121615
  async function resumePreparedDurableTarget(runId, deps, budget) {
@@ -121291,7 +121627,8 @@ async function resumePreparedDurableTarget(runId, deps, budget) {
121291
121627
  action: "resume",
121292
121628
  runId: resumed.ok ? resumed.runId : runId,
121293
121629
  status: resumed.ok ? "running" : "noop",
121294
- message: resumed.message
121630
+ message: resumed.message,
121631
+ ...resumed.ok ? {} : { code: resumeFailureCode(resumed.reason) }
121295
121632
  };
121296
121633
  } catch (error) {
121297
121634
  return controlFailure("resume", runId, error);
@@ -121305,7 +121642,8 @@ function refuseStageScopedDurableResume(runId, args) {
121305
121642
  action: "resume",
121306
121643
  runId,
121307
121644
  status: "noop",
121308
- message: stageScopedDurableResumeMessage(runId)
121645
+ message: stageScopedDurableResumeMessage(runId),
121646
+ code: "stage_resume_unsupported"
121309
121647
  };
121310
121648
  }
121311
121649
  async function resolveExplicitDurableTarget(target, args, deps, liveRuns = []) {
@@ -121331,7 +121669,7 @@ async function resolveExplicitDurableTarget(target, args, deps, liveRuns = []) {
121331
121669
  deps.signal?.throwIfAborted();
121332
121670
  const resolved = resolveWorkflowResumeTarget(target, liveRuns, durable, completed);
121333
121671
  if (resolved.kind === "malformed" || resolved.kind === "ambiguous") {
121334
- return { action: "resume", runId: target, status: "noop", message: resolved.message };
121672
+ return { action: "resume", runId: target, status: "noop", message: resolved.message, code: "run_not_found" };
121335
121673
  }
121336
121674
  if (resolved.kind === "durable" || resolved.kind === "live" || resolved.kind === "completed")
121337
121675
  deps.authorize?.(resolved.workflowId);
@@ -121349,7 +121687,8 @@ async function resolveExplicitDurableTarget(target, args, deps, liveRuns = []) {
121349
121687
  action: "resume",
121350
121688
  runId: resolved.workflowId,
121351
121689
  status: "noop",
121352
- message: `Workflow ${resolved.workflowId} is completed, not resumable.`
121690
+ message: `Workflow ${resolved.workflowId} is completed, not resumable.`,
121691
+ code: "not_resumable"
121353
121692
  };
121354
121693
  }
121355
121694
  const durableHandle = getDurableBackend().getWorkflow(target);
@@ -121361,7 +121700,8 @@ async function resolveExplicitDurableTarget(target, args, deps, liveRuns = []) {
121361
121700
  action: "resume",
121362
121701
  runId: target,
121363
121702
  status: "noop",
121364
- message: `Workflow ${target} has no durable checkpoint or pending prompt progress and is not resumable.`
121703
+ message: `Workflow ${target} has no durable checkpoint or pending prompt progress and is not resumable.`,
121704
+ code: "not_resumable"
121365
121705
  };
121366
121706
  }
121367
121707
  if (durableHandle !== undefined) {
@@ -121370,7 +121710,13 @@ async function resolveExplicitDurableTarget(target, args, deps, liveRuns = []) {
121370
121710
  return refusal;
121371
121711
  return resumePreparedDurableTarget(target, deps, args.budget);
121372
121712
  }
121373
- return { action: "resume", runId: target, status: "noop", message: `Run not found: ${target}` };
121713
+ return {
121714
+ action: "resume",
121715
+ runId: target,
121716
+ status: "noop",
121717
+ message: `Run not found: ${target}`,
121718
+ code: "run_not_found"
121719
+ };
121374
121720
  }
121375
121721
  async function workflowResumeAction(args, deps) {
121376
121722
  const owner = deps.owner ?? captureWorkflowOwnerResources();
@@ -121385,18 +121731,25 @@ async function workflowResumeAction(args, deps) {
121385
121731
  if (target.kind === "all")
121386
121732
  return { action: "resume", runId: "--all", status: "noop", message: "Resume does not support --all." };
121387
121733
  if (target.kind === "malformed") {
121388
- return { action: "resume", runId: target.target, status: "noop", message: target.message };
121734
+ return { action: "resume", runId: target.target, status: "noop", message: target.message, code: "run_not_found" };
121389
121735
  }
121390
121736
  if (target.kind === "not_found") {
121391
121737
  if (explicitTarget !== undefined && explicitTarget.length > 0) {
121392
121738
  return resolveExplicitDurableTarget(explicitTarget, args, deps);
121393
121739
  }
121394
- return { action: "resume", runId: target.target, status: "noop", message: target.message };
121740
+ return { action: "resume", runId: target.target, status: "noop", message: target.message, code: "run_not_found" };
121395
121741
  }
121396
121742
  deps.authorize?.(target.runId);
121397
121743
  const requestedStage = resolveToolStageTarget(target.runId, args.stageId, store);
121398
- if (!requestedStage.ok)
121399
- return { action: "resume", runId: target.runId, status: "noop", message: requestedStage.message };
121744
+ if (!requestedStage.ok) {
121745
+ return {
121746
+ action: "resume",
121747
+ runId: target.runId,
121748
+ status: "noop",
121749
+ message: requestedStage.message,
121750
+ code: requestedStage.code
121751
+ };
121752
+ }
121400
121753
  const backend = getDurableBackend();
121401
121754
  const exact = store.runs().find((run) => run.id === target.runId);
121402
121755
  const shadow = exact === undefined ? "not_shadow" : classifyDurableResumeShadow(exact, store, {
@@ -121416,7 +121769,8 @@ async function workflowResumeAction(args, deps) {
121416
121769
  action: "resume",
121417
121770
  runId: target.runId,
121418
121771
  status: "noop",
121419
- message: `Workflow ${target.runId} has no durable checkpoint or pending prompt progress and is not resumable.`
121772
+ message: `Workflow ${target.runId} has no durable checkpoint or pending prompt progress and is not resumable.`,
121773
+ code: "not_resumable"
121420
121774
  };
121421
121775
  }
121422
121776
  if (toolControlRegistry.runControl(target.runId) === undefined && !backend.isWorkflowLoadable(target.runId)) {
@@ -121432,16 +121786,24 @@ async function workflowResumeAction(args, deps) {
121432
121786
  deps.signal?.throwIfAborted();
121433
121787
  if (!backend.isWorkflowLoadable(target.runId)) {
121434
121788
  store.removeRun(target.runId);
121435
- return { action: "resume", runId: target.runId, status: "noop", message: `Run not found: ${target.runId}` };
121789
+ return {
121790
+ action: "resume",
121791
+ runId: target.runId,
121792
+ status: "noop",
121793
+ message: `Run not found: ${target.runId}`,
121794
+ code: "run_not_found"
121795
+ };
121436
121796
  }
121437
121797
  }
121438
121798
  let warning;
121439
121799
  const stage = resolveToolStageTarget(target.runId, args.stageId, store);
121440
- if (!stage.ok)
121441
- return { action: "resume", runId: target.runId, status: "noop", message: stage.message };
121800
+ if (!stage.ok) {
121801
+ return { action: "resume", runId: target.runId, status: "noop", message: stage.message, code: stage.code };
121802
+ }
121442
121803
  const stageRunId = stage.runId ?? target.runId;
121443
121804
  const run = store.runs().find((candidate) => candidate.id === stageRunId);
121444
121805
  const hadPausedRunState = run?.status === "paused";
121806
+ const endedBeforeResume = run?.endedAt !== undefined;
121445
121807
  const hadPausedStageState = run !== undefined && workflowHasPausedStages(store, stageRunId);
121446
121808
  const isPaused = run !== undefined && workflowHasPausedState(store, stageRunId);
121447
121809
  const isDurableAuthorExit = run?.exited === true && run.status === "failed" && run.resumable === true;
@@ -121473,7 +121835,8 @@ ${continuation.message}`;
121473
121835
  action: "resume",
121474
121836
  runId: continuation.ok ? continuation.runId : stageRunId,
121475
121837
  status: continuation.ok ? "running" : "noop",
121476
- message
121838
+ message,
121839
+ ...continuation.ok ? {} : { code: resumeFailureCode(continuation.reason) }
121477
121840
  };
121478
121841
  }
121479
121842
  try {
@@ -121489,9 +121852,22 @@ ${continuation.message}`;
121489
121852
  const message = result.message ?? (isPaused ? result.resumed.length === 0 ? runLevelResumed ? `Resumed run ${result.runId}.` : `No paused stages on run ${result.runId}.` : `Resumed ${result.resumed.length} stage(s) on run ${result.runId}${args.message ? ` with message: "${args.message}"` : ""}.` : `Snapshot available: run ${result.runId} (${result.snapshot.name}) — status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}`);
121490
121853
  const noContinuation = result.mode === "not_resumable" || result.mode === "snapshot" && result.snapshot.status === "blocked";
121491
121854
  const status = result.mode === "partial" ? "partial" : noContinuation || noPausedProgress ? "noop" : "ok";
121492
- return { action: "resume", runId: result.runId, status, message };
121855
+ const notResumable = result.mode === "not_resumable" || endedBeforeResume && result.mode === "snapshot" && result.snapshot.resumable !== true;
121856
+ return {
121857
+ action: "resume",
121858
+ runId: result.runId,
121859
+ status,
121860
+ message,
121861
+ ...notResumable ? { code: "not_resumable" } : {}
121862
+ };
121493
121863
  }
121494
- return { action: "resume", runId: stageRunId, status: "noop", message: `Run not found: ${stageRunId}` };
121864
+ return {
121865
+ action: "resume",
121866
+ runId: stageRunId,
121867
+ status: "noop",
121868
+ message: `Run not found: ${stageRunId}`,
121869
+ code: "run_not_found"
121870
+ };
121495
121871
  } catch (error) {
121496
121872
  return resumeControlFailure(stageRunId, error, store);
121497
121873
  }
@@ -121957,11 +122333,11 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
121957
122333
  case "answer":
121958
122334
  return awaitRequest(workflowAnswerAction(args, owner));
121959
122335
  case "pause":
121960
- return awaitRequest(workflowPauseAction(args, owner));
122336
+ return awaitRequest(workflowPauseAction(args, owner, { getRuntime, authorize }));
121961
122337
  case "reload":
121962
122338
  return awaitRequest(workflowReloadAction(args, { reloadWorkflowResources }));
121963
122339
  case "quit":
121964
- return awaitRequest(workflowQuitAction(args, owner));
122340
+ return awaitRequest(workflowQuitAction(args, owner, { getRuntime, authorize }));
121965
122341
  case "resume":
121966
122342
  return awaitRequest(workflowResumeAction(args, {
121967
122343
  getRuntime,
@@ -122587,6 +122963,14 @@ function factory(pi) {
122587
122963
  const storeWidgetRef = { current: null };
122588
122964
  const intercomControlRef = { current: null };
122589
122965
  const executeWorkflowTool = makeExecuteWorkflowTool((ctx) => runtimeState.runtimeForContext(ctx), runtimeState.reloadWorkflowResources, runtimeState.ensureWorkflowResourcesLoaded, owner);
122966
+ let sessionContext;
122967
+ pi.on?.("session_start", (_event, ctx) => {
122968
+ sessionContext = ctx;
122969
+ });
122970
+ pi.on?.("session_shutdown", () => {
122971
+ sessionContext = undefined;
122972
+ });
122973
+ pi.registerWorkflowRunControl?.(createSessionRunControl({ execute: executeWorkflowTool, context: () => sessionContext, store: owner.store }));
122590
122974
  const executeWorkflowToolWithAutoAttach = async (args, ctx, signal, onRunAccepted) => {
122591
122975
  const result = await executeWorkflowTool(args, ctx, signal, onRunAccepted);
122592
122976
  if (workflowPolicyFromContext(ctx).allowInputPicker && result.action === "run" && result.status === "running" && result.runId.length > 0 && result.name !== undefined && runtimeState.runtimeProxy.registry.get(result.name)?.autoAttach === true) {