open-multi-agent-kit 0.98.4 → 0.98.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 (252) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +3 -2
  3. package/dist/commands/run-command.d.ts +2 -1
  4. package/dist/commands/run-command.d.ts.map +1 -1
  5. package/dist/commands/run-command.js +9 -1
  6. package/dist/commands/run-command.js.map +1 -1
  7. package/dist/commands/verified-run-cli.d.ts +6 -0
  8. package/dist/commands/verified-run-cli.d.ts.map +1 -0
  9. package/dist/commands/verified-run-cli.js +190 -0
  10. package/dist/commands/verified-run-cli.js.map +1 -0
  11. package/dist/core/agent-session-services.d.ts +8 -1
  12. package/dist/core/agent-session-services.d.ts.map +1 -1
  13. package/dist/core/agent-session-services.js +41 -0
  14. package/dist/core/agent-session-services.js.map +1 -1
  15. package/dist/core/agent-session.d.ts +7 -8
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +61 -41
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/prompt-settlement.d.ts +4 -3
  20. package/dist/core/prompt-settlement.d.ts.map +1 -1
  21. package/dist/core/prompt-settlement.js.map +1 -1
  22. package/dist/core/run-budget-policy.d.ts +18 -0
  23. package/dist/core/run-budget-policy.d.ts.map +1 -0
  24. package/dist/core/run-budget-policy.js +52 -0
  25. package/dist/core/run-budget-policy.js.map +1 -0
  26. package/dist/core/run-budget.d.ts +33 -0
  27. package/dist/core/run-budget.d.ts.map +1 -0
  28. package/dist/core/run-budget.js +89 -0
  29. package/dist/core/run-budget.js.map +1 -0
  30. package/dist/core/run-execution-api.d.ts +15 -0
  31. package/dist/core/run-execution-api.d.ts.map +1 -0
  32. package/dist/core/run-execution-api.js +7 -0
  33. package/dist/core/run-execution-api.js.map +1 -0
  34. package/dist/core/run-journal.d.ts.map +1 -1
  35. package/dist/core/run-journal.js +30 -208
  36. package/dist/core/run-journal.js.map +1 -1
  37. package/dist/core/session-bash-service.d.ts +2 -2
  38. package/dist/core/session-bash-service.d.ts.map +1 -1
  39. package/dist/core/session-bash-service.js +21 -9
  40. package/dist/core/session-bash-service.js.map +1 -1
  41. package/dist/core/session-failure-cause.d.ts.map +1 -1
  42. package/dist/core/session-failure-cause.js +5 -0
  43. package/dist/core/session-failure-cause.js.map +1 -1
  44. package/dist/core/session-prompt-lifecycle.d.ts +25 -0
  45. package/dist/core/session-prompt-lifecycle.d.ts.map +1 -0
  46. package/dist/core/session-prompt-lifecycle.js +87 -0
  47. package/dist/core/session-prompt-lifecycle.js.map +1 -0
  48. package/dist/core/session-run-budget.d.ts +28 -0
  49. package/dist/core/session-run-budget.d.ts.map +1 -0
  50. package/dist/core/session-run-budget.js +127 -0
  51. package/dist/core/session-run-budget.js.map +1 -0
  52. package/dist/core/session-run-termination.d.ts.map +1 -1
  53. package/dist/core/session-run-termination.js +14 -3
  54. package/dist/core/session-run-termination.js.map +1 -1
  55. package/dist/core/session-termination-types.d.ts +97 -0
  56. package/dist/core/session-termination-types.d.ts.map +1 -0
  57. package/dist/core/session-termination-types.js +26 -0
  58. package/dist/core/session-termination-types.js.map +1 -0
  59. package/dist/core/session-termination.d.ts +3 -97
  60. package/dist/core/session-termination.d.ts.map +1 -1
  61. package/dist/core/session-termination.js +24 -28
  62. package/dist/core/session-termination.js.map +1 -1
  63. package/dist/core/slash-commands.d.ts.map +1 -1
  64. package/dist/core/slash-commands.js +1 -0
  65. package/dist/core/slash-commands.js.map +1 -1
  66. package/dist/core/subagent-lane-launcher.d.ts +3 -2
  67. package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
  68. package/dist/core/subagent-lane-launcher.js +24 -13
  69. package/dist/core/subagent-lane-launcher.js.map +1 -1
  70. package/dist/core/verified-run/broker.d.ts +26 -0
  71. package/dist/core/verified-run/broker.d.ts.map +1 -0
  72. package/dist/core/verified-run/broker.js +210 -0
  73. package/dist/core/verified-run/broker.js.map +1 -0
  74. package/dist/core/verified-run/candidate.d.ts +24 -0
  75. package/dist/core/verified-run/candidate.d.ts.map +1 -0
  76. package/dist/core/verified-run/candidate.js +167 -0
  77. package/dist/core/verified-run/candidate.js.map +1 -0
  78. package/dist/core/verified-run/check-receipt.d.ts +18 -0
  79. package/dist/core/verified-run/check-receipt.d.ts.map +1 -0
  80. package/dist/core/verified-run/check-receipt.js +92 -0
  81. package/dist/core/verified-run/check-receipt.js.map +1 -0
  82. package/dist/core/verified-run/coordinator.d.ts +39 -0
  83. package/dist/core/verified-run/coordinator.d.ts.map +1 -0
  84. package/dist/core/verified-run/coordinator.js +190 -0
  85. package/dist/core/verified-run/coordinator.js.map +1 -0
  86. package/dist/core/verified-run/dag-candidates.d.ts +8 -0
  87. package/dist/core/verified-run/dag-candidates.d.ts.map +1 -0
  88. package/dist/core/verified-run/dag-candidates.js +65 -0
  89. package/dist/core/verified-run/dag-candidates.js.map +1 -0
  90. package/dist/core/verified-run/dag-phase.d.ts +7 -0
  91. package/dist/core/verified-run/dag-phase.d.ts.map +1 -0
  92. package/dist/core/verified-run/dag-phase.js +92 -0
  93. package/dist/core/verified-run/dag-phase.js.map +1 -0
  94. package/dist/core/verified-run/dag-projection.d.ts +7 -0
  95. package/dist/core/verified-run/dag-projection.d.ts.map +1 -0
  96. package/dist/core/verified-run/dag-projection.js +90 -0
  97. package/dist/core/verified-run/dag-projection.js.map +1 -0
  98. package/dist/core/verified-run/dag-recovery.d.ts +13 -0
  99. package/dist/core/verified-run/dag-recovery.d.ts.map +1 -0
  100. package/dist/core/verified-run/dag-recovery.js +98 -0
  101. package/dist/core/verified-run/dag-recovery.js.map +1 -0
  102. package/dist/core/verified-run/dag-retry-projection.d.ts +9 -0
  103. package/dist/core/verified-run/dag-retry-projection.d.ts.map +1 -0
  104. package/dist/core/verified-run/dag-retry-projection.js +43 -0
  105. package/dist/core/verified-run/dag-retry-projection.js.map +1 -0
  106. package/dist/core/verified-run/dag-types.d.ts +47 -0
  107. package/dist/core/verified-run/dag-types.d.ts.map +1 -0
  108. package/dist/core/verified-run/dag-types.js +2 -0
  109. package/dist/core/verified-run/dag-types.js.map +1 -0
  110. package/dist/core/verified-run/event-parser.d.ts +3 -0
  111. package/dist/core/verified-run/event-parser.d.ts.map +1 -0
  112. package/dist/core/verified-run/event-parser.js +153 -0
  113. package/dist/core/verified-run/event-parser.js.map +1 -0
  114. package/dist/core/verified-run/events.d.ts +4 -0
  115. package/dist/core/verified-run/events.d.ts.map +1 -0
  116. package/dist/core/verified-run/events.js +3 -0
  117. package/dist/core/verified-run/events.js.map +1 -0
  118. package/dist/core/verified-run/evidence-binding.d.ts +18 -0
  119. package/dist/core/verified-run/evidence-binding.d.ts.map +1 -0
  120. package/dist/core/verified-run/evidence-binding.js +81 -0
  121. package/dist/core/verified-run/evidence-binding.js.map +1 -0
  122. package/dist/core/verified-run/evidence.d.ts +27 -0
  123. package/dist/core/verified-run/evidence.d.ts.map +1 -0
  124. package/dist/core/verified-run/evidence.js +100 -0
  125. package/dist/core/verified-run/evidence.js.map +1 -0
  126. package/dist/core/verified-run/journal.d.ts +32 -0
  127. package/dist/core/verified-run/journal.d.ts.map +1 -0
  128. package/dist/core/verified-run/journal.js +103 -0
  129. package/dist/core/verified-run/journal.js.map +1 -0
  130. package/dist/core/verified-run/namespace-identity.d.ts +11 -0
  131. package/dist/core/verified-run/namespace-identity.d.ts.map +1 -0
  132. package/dist/core/verified-run/namespace-identity.js +77 -0
  133. package/dist/core/verified-run/namespace-identity.js.map +1 -0
  134. package/dist/core/verified-run/owned-execution.d.ts +21 -0
  135. package/dist/core/verified-run/owned-execution.d.ts.map +1 -0
  136. package/dist/core/verified-run/owned-execution.js +43 -0
  137. package/dist/core/verified-run/owned-execution.js.map +1 -0
  138. package/dist/core/verified-run/phase-context.d.ts +9 -0
  139. package/dist/core/verified-run/phase-context.d.ts.map +1 -0
  140. package/dist/core/verified-run/phase-context.js +2 -0
  141. package/dist/core/verified-run/phase-context.js.map +1 -0
  142. package/dist/core/verified-run/process-gate.d.ts +5 -0
  143. package/dist/core/verified-run/process-gate.d.ts.map +1 -0
  144. package/dist/core/verified-run/process-gate.js +31 -0
  145. package/dist/core/verified-run/process-gate.js.map +1 -0
  146. package/dist/core/verified-run/projection.d.ts +4 -0
  147. package/dist/core/verified-run/projection.d.ts.map +1 -0
  148. package/dist/core/verified-run/projection.js +219 -0
  149. package/dist/core/verified-run/projection.js.map +1 -0
  150. package/dist/core/verified-run/recovery-clock.d.ts +19 -0
  151. package/dist/core/verified-run/recovery-clock.d.ts.map +1 -0
  152. package/dist/core/verified-run/recovery-clock.js +69 -0
  153. package/dist/core/verified-run/recovery-clock.js.map +1 -0
  154. package/dist/core/verified-run/recovery-command.d.ts +11 -0
  155. package/dist/core/verified-run/recovery-command.d.ts.map +1 -0
  156. package/dist/core/verified-run/recovery-command.js +73 -0
  157. package/dist/core/verified-run/recovery-command.js.map +1 -0
  158. package/dist/core/verified-run/recovery-projection.d.ts +12 -0
  159. package/dist/core/verified-run/recovery-projection.d.ts.map +1 -0
  160. package/dist/core/verified-run/recovery-projection.js +88 -0
  161. package/dist/core/verified-run/recovery-projection.js.map +1 -0
  162. package/dist/core/verified-run/recovery.d.ts +12 -0
  163. package/dist/core/verified-run/recovery.d.ts.map +1 -0
  164. package/dist/core/verified-run/recovery.js +115 -0
  165. package/dist/core/verified-run/recovery.js.map +1 -0
  166. package/dist/core/verified-run/run-types.d.ts +103 -0
  167. package/dist/core/verified-run/run-types.d.ts.map +1 -0
  168. package/dist/core/verified-run/run-types.js +2 -0
  169. package/dist/core/verified-run/run-types.js.map +1 -0
  170. package/dist/core/verified-run/scripted-writer.d.ts +15 -0
  171. package/dist/core/verified-run/scripted-writer.d.ts.map +1 -0
  172. package/dist/core/verified-run/scripted-writer.js +94 -0
  173. package/dist/core/verified-run/scripted-writer.js.map +1 -0
  174. package/dist/core/verified-run/session-port.d.ts +27 -0
  175. package/dist/core/verified-run/session-port.d.ts.map +1 -0
  176. package/dist/core/verified-run/session-port.js +2 -0
  177. package/dist/core/verified-run/session-port.js.map +1 -0
  178. package/dist/core/verified-run/storage.d.ts +15 -0
  179. package/dist/core/verified-run/storage.d.ts.map +1 -0
  180. package/dist/core/verified-run/storage.js +96 -0
  181. package/dist/core/verified-run/storage.js.map +1 -0
  182. package/dist/core/verified-run/verification-phase.d.ts +5 -0
  183. package/dist/core/verified-run/verification-phase.d.ts.map +1 -0
  184. package/dist/core/verified-run/verification-phase.js +56 -0
  185. package/dist/core/verified-run/verification-phase.js.map +1 -0
  186. package/dist/core/verified-run/work-recovery.d.ts +8 -0
  187. package/dist/core/verified-run/work-recovery.d.ts.map +1 -0
  188. package/dist/core/verified-run/work-recovery.js +44 -0
  189. package/dist/core/verified-run/work-recovery.js.map +1 -0
  190. package/dist/core/verified-run/writer-completion.d.ts +4 -0
  191. package/dist/core/verified-run/writer-completion.d.ts.map +1 -0
  192. package/dist/core/verified-run/writer-completion.js +27 -0
  193. package/dist/core/verified-run/writer-completion.js.map +1 -0
  194. package/dist/core/verified-run/writer-phase.d.ts +8 -0
  195. package/dist/core/verified-run/writer-phase.d.ts.map +1 -0
  196. package/dist/core/verified-run/writer-phase.js +76 -0
  197. package/dist/core/verified-run/writer-phase.js.map +1 -0
  198. package/dist/core/verified-run/writer-projection.d.ts +11 -0
  199. package/dist/core/verified-run/writer-projection.d.ts.map +1 -0
  200. package/dist/core/verified-run/writer-projection.js +70 -0
  201. package/dist/core/verified-run/writer-projection.js.map +1 -0
  202. package/dist/core/verified-run/writer-recovery.d.ts +16 -0
  203. package/dist/core/verified-run/writer-recovery.d.ts.map +1 -0
  204. package/dist/core/verified-run/writer-recovery.js +107 -0
  205. package/dist/core/verified-run/writer-recovery.js.map +1 -0
  206. package/dist/core/workload-permit-pool.d.ts +1 -3
  207. package/dist/core/workload-permit-pool.d.ts.map +1 -1
  208. package/dist/core/workload-permit-pool.js +11 -11
  209. package/dist/core/workload-permit-pool.js.map +1 -1
  210. package/dist/index.d.ts +3 -2
  211. package/dist/index.d.ts.map +1 -1
  212. package/dist/index.js +2 -2
  213. package/dist/index.js.map +1 -1
  214. package/dist/main.d.ts.map +1 -1
  215. package/dist/main.js +2 -8
  216. package/dist/main.js.map +1 -1
  217. package/dist/modes/interactive/components/session-failure.d.ts +13 -0
  218. package/dist/modes/interactive/components/session-failure.d.ts.map +1 -0
  219. package/dist/modes/interactive/components/session-failure.js +81 -0
  220. package/dist/modes/interactive/components/session-failure.js.map +1 -0
  221. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  222. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  223. package/dist/modes/interactive/interactive-mode.js +45 -31
  224. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  225. package/dist/modes/interactive/tui-diagnostics.d.ts +36 -0
  226. package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -0
  227. package/dist/modes/interactive/tui-diagnostics.js +105 -0
  228. package/dist/modes/interactive/tui-diagnostics.js.map +1 -0
  229. package/dist/modes/interactive/tui-runtime-info.d.ts +20 -0
  230. package/dist/modes/interactive/tui-runtime-info.d.ts.map +1 -0
  231. package/dist/modes/interactive/tui-runtime-info.js +76 -0
  232. package/dist/modes/interactive/tui-runtime-info.js.map +1 -0
  233. package/docs/development.md +10 -0
  234. package/docs/keybindings.md +1 -1
  235. package/docs/release-audit-0.98.5.md +84 -0
  236. package/docs/run-protocol.md +33 -1
  237. package/docs/runtime-algorithms.md +26 -0
  238. package/docs/sdk.md +193 -0
  239. package/docs/usage.md +54 -0
  240. package/docs/verified-run-testing.md +417 -0
  241. package/docs/verified-run.md +388 -0
  242. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  243. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  244. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  245. package/examples/extensions/gondolin/package-lock.json +2 -2
  246. package/examples/extensions/gondolin/package.json +1 -1
  247. package/examples/extensions/sandbox/package-lock.json +2 -2
  248. package/examples/extensions/sandbox/package.json +1 -1
  249. package/examples/extensions/with-deps/package-lock.json +2 -2
  250. package/examples/extensions/with-deps/package.json +1 -1
  251. package/npm-shrinkwrap.json +18 -18
  252. package/package.json +6 -6
@@ -103,6 +103,7 @@ import { resolveResourceGovernorSettings } from "./resource-governor-settings.js
103
103
  import { admissionObservationFacts, classificationObservationFacts, ResourceObservationJournal, settledObservationFacts, snapshotObservationFacts, soundObservationFacts, } from "./resource-observation-journal.js";
104
104
  import { decideResourceSafetyGate, RESOURCE_PRESSURE_REQUIRED_ACTION } from "./resource-safety-gate.js";
105
105
  import { appendRouterFeedbackRecord, ROUTER_FEEDBACK_LEVELS, } from "./router-feedback-collector.js";
106
+ import { RunBudgetExceededError, RunBudgetPolicyError } from "./run-budget-policy.js";
106
107
  import { RunJournalStore } from "./run-journal-store.js";
107
108
  import { RunResourceLeaseController } from "./run-resource-lease.js";
108
109
  import { SessionBashRuntime } from "./session-bash-runtime.js";
@@ -111,6 +112,8 @@ import { SessionCompactionService } from "./session-compaction-service.js";
111
112
  import { preflightFailureCause, runtimeFailureCause, terminationMessage } from "./session-failure-cause.js";
112
113
  import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
113
114
  import { acquireSessionOwnerLeaseSync } from "./session-owner-lease.js";
115
+ import { PromptExecutionBusyError, SessionPromptLifecycle } from "./session-prompt-lifecycle.js";
116
+ import { SessionRunBudget } from "./session-run-budget.js";
114
117
  import { classifyRunTermination } from "./session-run-termination.js";
115
118
  import { assembleSessionSystemPrompt } from "./session-system-prompt.js";
116
119
  import { classifySessionTermination, } from "./session-termination.js";
@@ -217,6 +220,11 @@ export class AgentSession {
217
220
  // Event subscription state
218
221
  _unsubscribeAgent;
219
222
  _eventListeners = [];
223
+ _runBudget;
224
+ _promptLifecycle = new SessionPromptLifecycle({
225
+ canSettle: () => !this.isStreaming && !this.agent.hasQueuedMessages(),
226
+ auditsLateSettlement: () => this.agent.toolExecutionPolicy?.lateSettlement !== "ignore",
227
+ });
220
228
  _messageEndReplacements = new WeakMap();
221
229
  /** Tracks pending steering messages for UI display. Removed when delivered. */
222
230
  _steeringMessages = [];
@@ -313,6 +321,21 @@ export class AgentSession {
313
321
  _promptCacheLastBreakReason;
314
322
  constructor(config) {
315
323
  this.agent = config.agent;
324
+ this._runBudget = new SessionRunBudget(this.agent, {
325
+ assertIdle: () => {
326
+ this._promptLifecycle.assertIdle();
327
+ if (this.isRetrying || this.isCompacting || this._branchSummaryAbortController)
328
+ throw new PromptExecutionBusyError();
329
+ },
330
+ stop: (error) => {
331
+ this._pendingRuntimeTerminationCause = { area: "budget", code: error.code };
332
+ this.agent.abort();
333
+ this.abortRetry();
334
+ this.abortCompaction();
335
+ this.abortBranchSummary();
336
+ },
337
+ reject: (error) => this._publishRuntimeFailure(error),
338
+ });
316
339
  this.sessionManager = config.sessionManager;
317
340
  this.settingsManager = config.settingsManager;
318
341
  this._scopedModels = config.scopedModels ?? [];
@@ -688,6 +711,7 @@ export class AgentSession {
688
711
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
689
712
  }
690
713
  async _getCompactionRequestAuth(model) {
714
+ this._runBudget.assertAdmission();
691
715
  const options = { minRemainingMs: COMPACTION_MIN_TOKEN_VALIDITY_MS };
692
716
  if (isBuiltinStreamFn(this.agent.streamFn)) {
693
717
  return this._getRequiredRequestAuth(model, options);
@@ -842,6 +866,8 @@ export class AgentSession {
842
866
  }
843
867
  }
844
868
  }
869
+ if (event.type === "tool_execution_late_settlement")
870
+ this._promptLifecycle.flush();
845
871
  };
846
872
  _willRetryAfterAgentEnd(event) {
847
873
  const settings = this.settingsManager.getRetrySettings();
@@ -1109,6 +1135,8 @@ export class AgentSession {
1109
1135
  * Call this when completely done with the session.
1110
1136
  */
1111
1137
  dispose() {
1138
+ this._promptLifecycle.dispose();
1139
+ this._runBudget.close();
1112
1140
  try {
1113
1141
  try {
1114
1142
  this.abortRetry();
@@ -1235,7 +1263,7 @@ export class AgentSession {
1235
1263
  for (const name of desiredToolNames) {
1236
1264
  const tool = this._toolRegistry.get(name);
1237
1265
  if (tool) {
1238
- tools.push(tool);
1266
+ tools.push(this._promptLifecycle.wrapTool(tool));
1239
1267
  validToolNames.push(name);
1240
1268
  }
1241
1269
  else if (lockedToolNames?.includes(name)) {
@@ -1460,13 +1488,14 @@ export class AgentSession {
1460
1488
  // One identity per top-level run (§16.2): internal retries and
1461
1489
  // continuations inside this call share it, as does the resource lease.
1462
1490
  const promptRunId = `prompt-run-${randomUUID()}`;
1463
- const startedAtEpochMs = Date.now();
1491
+ const ownedRun = this._promptLifecycle.begin(promptRunId);
1464
1492
  // Roadmap M2: the lease spans the whole run including internal retries
1465
1493
  // and continuations, so they share one admission decision (§8.3).
1466
1494
  const resourceLease = await this._beginResourceGovernedRun(promptRunId);
1467
1495
  const resourceObservations = this._resourceObservationJournals.get(promptRunId) ?? null;
1468
1496
  let outcome = "completed";
1469
1497
  try {
1498
+ this._runBudget.assertActive();
1470
1499
  await this.agent.prompt(messages);
1471
1500
  while (await this._handlePostAgentRun()) {
1472
1501
  await this.agent.continue();
@@ -1478,43 +1507,18 @@ export class AgentSession {
1478
1507
  throw error;
1479
1508
  }
1480
1509
  finally {
1510
+ if (this._runBudget.failure && this._lastTermination?.kind !== "budget_exhausted")
1511
+ this._publishRuntimeFailure(this._runBudget.failure);
1481
1512
  outcome = promptSettlement.resolvePromptSettlementOutcome(outcome, this._lastTermination?.kind);
1482
- if (resourceLease !== null) {
1483
- // Generation-safe: a stale release after another run acquired the
1484
- // lease is a no-op instead of clobbering the newer cap (§8.1).
1485
- this._resourceLeaseController?.release(resourceLease);
1486
- resourceObservations?.record("resource_lease_released_v1", { promptRunId });
1487
- }
1488
1513
  this._flushPendingBashMessages();
1489
- // §16.4 (M4): settle only after every in-run continuation drained.
1490
- this._emitPromptSettledIfReady({ promptRunId, startedAtEpochMs, outcome, resourceObservations });
1491
- }
1492
- }
1493
- /**
1494
- * Emit `prompt_settled` when the §16.4 conditions hold. Called exactly
1495
- * once per promptRunId (from `_runAgentPrompt`'s finally), so the same
1496
- * run can never emit twice; late-queued messages block emission
1497
- * conservatively — the continuation run settles under its own id.
1498
- * Settlement is a UX signal only and must never affect the run (§16.1).
1499
- */
1500
- _emitPromptSettledIfReady(input) {
1501
- try {
1502
- let state = promptSettlement.createPromptSettlementState(input.promptRunId, input.startedAtEpochMs);
1503
- state = promptSettlement.reducePromptSettlement(state, { kind: "terminal", outcome: input.outcome });
1504
- if (this.isStreaming) {
1505
- state = promptSettlement.reducePromptSettlement(state, { kind: "tool", delta: 1 });
1506
- }
1507
- if (this.agent.hasQueuedMessages()) {
1508
- state = promptSettlement.reducePromptSettlement(state, { kind: "continuation", delta: 1 });
1509
- }
1510
- const settled = promptSettlement.settlePromptIfReady(state, Date.now());
1511
- if (settled.event !== null) {
1512
- input.resourceObservations?.record("prompt_settled_v1", settledObservationFacts(settled.event));
1513
- this._emit(settled.event);
1514
- }
1515
- }
1516
- catch {
1517
- // Never let settlement bookkeeping break a run.
1514
+ ownedRun.finish(outcome, (event) => {
1515
+ if (resourceLease !== null) {
1516
+ this._resourceLeaseController?.release(resourceLease);
1517
+ resourceObservations?.record("resource_lease_released_v1", { promptRunId });
1518
+ }
1519
+ resourceObservations?.record("prompt_settled_v1", settledObservationFacts(event));
1520
+ this._emit(event);
1521
+ });
1518
1522
  }
1519
1523
  }
1520
1524
  /**
@@ -1526,7 +1530,7 @@ export class AgentSession {
1526
1530
  * delayed. The pending probe is bounded (~cpuSampleMs) and never rejects.
1527
1531
  * - `adaptive`/`strict`: bounded blocking probe (default 300 ms deadline),
1528
1532
  * then a generation-safe lease applies the effective tool cap for this
1529
- * run. The caller's `finally` releases it exactly once.
1533
+ * run. Prompt settlement releases it after owned tool promises terminate.
1530
1534
  *
1531
1535
  * Never throws: any probe or policy failure leaves this run ungoverned
1532
1536
  * (§2.1: probe failure must not crash or block the prompt).
@@ -1721,6 +1725,7 @@ export class AgentSession {
1721
1725
  }
1722
1726
  }
1723
1727
  async _handlePostAgentRun() {
1728
+ this._runBudget.assertActive();
1724
1729
  const msg = this._lastAssistantMessage;
1725
1730
  this._lastAssistantMessage = undefined;
1726
1731
  if (!msg) {
@@ -1755,7 +1760,15 @@ export class AgentSession {
1755
1760
  * @throws Error if streaming and no streamingBehavior specified
1756
1761
  * @throws Error if no model selected or no API key available (when not streaming)
1757
1762
  */
1758
- async prompt(text, options) {
1763
+ prompt(text, options) {
1764
+ if (options?.runBudget === undefined && (this.isStreaming || this.isRetrying))
1765
+ return this._prompt(text, options);
1766
+ return this._runBudget.execute(options?.runBudget, () => this._prompt(text, options), options?.preflightResult);
1767
+ }
1768
+ getRunBudgetSnapshot() {
1769
+ return this._runBudget.snapshot();
1770
+ }
1771
+ async _prompt(text, options) {
1759
1772
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
1760
1773
  const preflightResult = options?.preflightResult;
1761
1774
  let currentText = redactSensitiveText(text);
@@ -1820,6 +1833,7 @@ export class AgentSession {
1820
1833
  preflightResult?.(true);
1821
1834
  return;
1822
1835
  }
1836
+ this._promptLifecycle.assertIdle();
1823
1837
  // Flush any pending bash messages before the new prompt
1824
1838
  this._flushPendingBashMessages();
1825
1839
  // Validate model
@@ -1918,11 +1932,16 @@ export class AgentSession {
1918
1932
  }
1919
1933
  this._recordPromptCachePlan(result?.systemPrompt ? "extension-override" : "turn-plan");
1920
1934
  await this._checkProjectedCompaction(messages);
1935
+ this._runBudget.assertActive();
1921
1936
  }
1922
1937
  catch (error) {
1923
1938
  preflightResult?.(false);
1924
1939
  const rawMessage = error instanceof Error ? error.message : String(error);
1925
- const cause = preflightFailureCause(rawMessage, Boolean(this.model));
1940
+ const cause = error instanceof RunBudgetExceededError
1941
+ ? { area: "budget", code: error.code }
1942
+ : error instanceof RunBudgetPolicyError
1943
+ ? { area: "configuration", code: "invalid" }
1944
+ : preflightFailureCause(rawMessage, Boolean(this.model));
1926
1945
  const timestamp = new Date().toISOString();
1927
1946
  this._publishTermination(classifySessionTermination({
1928
1947
  sessionId: this.sessionId,
@@ -2172,6 +2191,7 @@ export class AgentSession {
2172
2191
  this._followUpMessages = [];
2173
2192
  this.agent.clearAllQueues();
2174
2193
  this._emitQueueUpdate();
2194
+ this._promptLifecycle.flush();
2175
2195
  return { steering, followUp };
2176
2196
  }
2177
2197
  /** Number of pending messages (includes both steering and follow-up) */
@@ -3938,7 +3958,7 @@ export class AgentSession {
3938
3958
  const failoverTo = await this._maybeFailoverFromSafetyStop(message);
3939
3959
  const switchedVia = failoverTo ? `failover ${failoverTo}` : rotatedTo ? `route ${rotatedTo}` : undefined;
3940
3960
  // Safety stops are usually immediate false positives — short delay after failover, full backoff otherwise.
3941
- const delayMs = computeRetryDelayMs(settings.baseDelayMs, attempt, switchedVia !== undefined);
3961
+ const delayMs = Math.min(computeRetryDelayMs(settings.baseDelayMs, attempt, switchedVia !== undefined), this._runBudget.remainingMs ?? Infinity);
3942
3962
  const errorMessage = switchedVia
3943
3963
  ? `${message.errorMessage || "content/safety stop"} → ${switchedVia}`
3944
3964
  : message.errorMessage || "Unknown error";