open-multi-agent-kit 0.98.3 → 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 (468) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +12 -2
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +14 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/help.d.ts.map +1 -1
  8. package/dist/cli/help.js +3 -0
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/mcp-attach.d.ts +3 -3
  11. package/dist/cli/mcp-attach.d.ts.map +1 -1
  12. package/dist/cli/mcp-attach.js +5 -4
  13. package/dist/cli/mcp-attach.js.map +1 -1
  14. package/dist/cli/model-contract.d.ts +5 -0
  15. package/dist/cli/model-contract.d.ts.map +1 -0
  16. package/dist/cli/model-contract.js +45 -0
  17. package/dist/cli/model-contract.js.map +1 -0
  18. package/dist/commands/adaptorch-doctor-cli.d.ts.map +1 -1
  19. package/dist/commands/adaptorch-doctor-cli.js +32 -10
  20. package/dist/commands/adaptorch-doctor-cli.js.map +1 -1
  21. package/dist/commands/init-cli.d.ts +12 -0
  22. package/dist/commands/init-cli.d.ts.map +1 -0
  23. package/dist/commands/init-cli.js +76 -0
  24. package/dist/commands/init-cli.js.map +1 -0
  25. package/dist/commands/provider-sync-cli.d.ts +27 -0
  26. package/dist/commands/provider-sync-cli.d.ts.map +1 -0
  27. package/dist/commands/provider-sync-cli.js +177 -0
  28. package/dist/commands/provider-sync-cli.js.map +1 -0
  29. package/dist/commands/run-command.d.ts +9 -0
  30. package/dist/commands/run-command.d.ts.map +1 -0
  31. package/dist/commands/run-command.js +34 -0
  32. package/dist/commands/run-command.js.map +1 -0
  33. package/dist/commands/verified-run-cli.d.ts +6 -0
  34. package/dist/commands/verified-run-cli.d.ts.map +1 -0
  35. package/dist/commands/verified-run-cli.js +190 -0
  36. package/dist/commands/verified-run-cli.js.map +1 -0
  37. package/dist/core/active-skill-state.d.ts +7 -0
  38. package/dist/core/active-skill-state.d.ts.map +1 -0
  39. package/dist/core/active-skill-state.js +25 -0
  40. package/dist/core/active-skill-state.js.map +1 -0
  41. package/dist/core/agent-session-services.d.ts +10 -1
  42. package/dist/core/agent-session-services.d.ts.map +1 -1
  43. package/dist/core/agent-session-services.js +43 -0
  44. package/dist/core/agent-session-services.js.map +1 -1
  45. package/dist/core/agent-session.d.ts +7 -9
  46. package/dist/core/agent-session.d.ts.map +1 -1
  47. package/dist/core/agent-session.js +91 -126
  48. package/dist/core/agent-session.js.map +1 -1
  49. package/dist/core/codex-chatgpt-web-bridge.d.ts +99 -0
  50. package/dist/core/codex-chatgpt-web-bridge.d.ts.map +1 -0
  51. package/dist/core/codex-chatgpt-web-bridge.js +192 -0
  52. package/dist/core/codex-chatgpt-web-bridge.js.map +1 -0
  53. package/dist/core/codex-chatgpt-web-sync.d.ts +50 -0
  54. package/dist/core/codex-chatgpt-web-sync.d.ts.map +1 -0
  55. package/dist/core/codex-chatgpt-web-sync.js +119 -0
  56. package/dist/core/codex-chatgpt-web-sync.js.map +1 -0
  57. package/dist/core/compaction/compaction-headroom.d.ts +30 -0
  58. package/dist/core/compaction/compaction-headroom.d.ts.map +1 -0
  59. package/dist/core/compaction/compaction-headroom.js +48 -0
  60. package/dist/core/compaction/compaction-headroom.js.map +1 -0
  61. package/dist/core/compaction/compaction.d.ts +2 -29
  62. package/dist/core/compaction/compaction.d.ts.map +1 -1
  63. package/dist/core/compaction/compaction.js +19 -47
  64. package/dist/core/compaction/compaction.js.map +1 -1
  65. package/dist/core/compaction/overflow-retry-guard.d.ts +20 -0
  66. package/dist/core/compaction/overflow-retry-guard.d.ts.map +1 -0
  67. package/dist/core/compaction/overflow-retry-guard.js +38 -0
  68. package/dist/core/compaction/overflow-retry-guard.js.map +1 -0
  69. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  70. package/dist/core/context-budget-headroom-types.js +1 -1
  71. package/dist/core/context-budget-headroom-types.js.map +1 -1
  72. package/dist/core/context-budget-token-counter-types.d.ts +27 -0
  73. package/dist/core/context-budget-token-counter-types.d.ts.map +1 -0
  74. package/dist/core/context-budget-token-counter-types.js +2 -0
  75. package/dist/core/context-budget-token-counter-types.js.map +1 -0
  76. package/dist/core/context-budget-token-counter.d.ts +2 -26
  77. package/dist/core/context-budget-token-counter.d.ts.map +1 -1
  78. package/dist/core/context-budget-token-counter.js +2 -0
  79. package/dist/core/context-budget-token-counter.js.map +1 -1
  80. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  81. package/dist/core/context-budget-v2-scoring.js +14 -10
  82. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  83. package/dist/core/domain-router.d.ts.map +1 -1
  84. package/dist/core/domain-router.js +1 -1
  85. package/dist/core/domain-router.js.map +1 -1
  86. package/dist/core/grok-harness-dispatch.d.ts +4 -0
  87. package/dist/core/grok-harness-dispatch.d.ts.map +1 -1
  88. package/dist/core/grok-harness-dispatch.js +20 -3
  89. package/dist/core/grok-harness-dispatch.js.map +1 -1
  90. package/dist/core/grok-harness.d.ts +15 -9
  91. package/dist/core/grok-harness.d.ts.map +1 -1
  92. package/dist/core/grok-harness.js +34 -8
  93. package/dist/core/grok-harness.js.map +1 -1
  94. package/dist/core/mcp/tools.d.ts +2 -2
  95. package/dist/core/mcp/tools.d.ts.map +1 -1
  96. package/dist/core/mcp/tools.js +29 -3
  97. package/dist/core/mcp/tools.js.map +1 -1
  98. package/dist/core/model-registry-schema.d.ts +1081 -0
  99. package/dist/core/model-registry-schema.d.ts.map +1 -0
  100. package/dist/core/model-registry-schema.js +148 -0
  101. package/dist/core/model-registry-schema.js.map +1 -0
  102. package/dist/core/model-registry.d.ts.map +1 -1
  103. package/dist/core/model-registry.js +1 -146
  104. package/dist/core/model-registry.js.map +1 -1
  105. package/dist/core/model-resolver.d.ts +2 -1
  106. package/dist/core/model-resolver.d.ts.map +1 -1
  107. package/dist/core/model-resolver.js +2 -1
  108. package/dist/core/model-resolver.js.map +1 -1
  109. package/dist/core/prompt-preset.d.ts +1 -1
  110. package/dist/core/prompt-preset.d.ts.map +1 -1
  111. package/dist/core/prompt-preset.js +16 -1
  112. package/dist/core/prompt-preset.js.map +1 -1
  113. package/dist/core/prompt-settlement.d.ts +4 -3
  114. package/dist/core/prompt-settlement.d.ts.map +1 -1
  115. package/dist/core/prompt-settlement.js.map +1 -1
  116. package/dist/core/provider-display-names.d.ts.map +1 -1
  117. package/dist/core/provider-display-names.js +1 -0
  118. package/dist/core/provider-display-names.js.map +1 -1
  119. package/dist/core/provider-resilience.d.ts +8 -0
  120. package/dist/core/provider-resilience.d.ts.map +1 -1
  121. package/dist/core/provider-resilience.js +20 -3
  122. package/dist/core/provider-resilience.js.map +1 -1
  123. package/dist/core/provider-usage-types.d.ts +42 -0
  124. package/dist/core/provider-usage-types.d.ts.map +1 -0
  125. package/dist/core/provider-usage-types.js +2 -0
  126. package/dist/core/provider-usage-types.js.map +1 -0
  127. package/dist/core/provider-usage.d.ts +2 -31
  128. package/dist/core/provider-usage.d.ts.map +1 -1
  129. package/dist/core/provider-usage.js +4 -0
  130. package/dist/core/provider-usage.js.map +1 -1
  131. package/dist/core/redaction.d.ts.map +1 -1
  132. package/dist/core/redaction.js +18 -2
  133. package/dist/core/redaction.js.map +1 -1
  134. package/dist/core/run-budget-policy.d.ts +18 -0
  135. package/dist/core/run-budget-policy.d.ts.map +1 -0
  136. package/dist/core/run-budget-policy.js +52 -0
  137. package/dist/core/run-budget-policy.js.map +1 -0
  138. package/dist/core/run-budget.d.ts +33 -0
  139. package/dist/core/run-budget.d.ts.map +1 -0
  140. package/dist/core/run-budget.js +89 -0
  141. package/dist/core/run-budget.js.map +1 -0
  142. package/dist/core/run-execution-api.d.ts +15 -0
  143. package/dist/core/run-execution-api.d.ts.map +1 -0
  144. package/dist/core/run-execution-api.js +7 -0
  145. package/dist/core/run-execution-api.js.map +1 -0
  146. package/dist/core/run-journal.d.ts.map +1 -1
  147. package/dist/core/run-journal.js +30 -208
  148. package/dist/core/run-journal.js.map +1 -1
  149. package/dist/core/sandbox/policy-merge.d.ts +6 -0
  150. package/dist/core/sandbox/policy-merge.d.ts.map +1 -0
  151. package/dist/core/sandbox/policy-merge.js +74 -0
  152. package/dist/core/sandbox/policy-merge.js.map +1 -0
  153. package/dist/core/sandbox/policy-paths.d.ts +4 -0
  154. package/dist/core/sandbox/policy-paths.d.ts.map +1 -0
  155. package/dist/core/sandbox/policy-paths.js +21 -0
  156. package/dist/core/sandbox/policy-paths.js.map +1 -0
  157. package/dist/core/sandbox/policy-types.d.ts +75 -0
  158. package/dist/core/sandbox/policy-types.d.ts.map +1 -0
  159. package/dist/core/sandbox/policy-types.js +2 -0
  160. package/dist/core/sandbox/policy-types.js.map +1 -0
  161. package/dist/core/sandbox/policy.d.ts +3 -77
  162. package/dist/core/sandbox/policy.d.ts.map +1 -1
  163. package/dist/core/sandbox/policy.js +3 -98
  164. package/dist/core/sandbox/policy.js.map +1 -1
  165. package/dist/core/sdk-provider-stream.d.ts +14 -0
  166. package/dist/core/sdk-provider-stream.d.ts.map +1 -0
  167. package/dist/core/sdk-provider-stream.js +38 -0
  168. package/dist/core/sdk-provider-stream.js.map +1 -0
  169. package/dist/core/sdk.d.ts +5 -1
  170. package/dist/core/sdk.d.ts.map +1 -1
  171. package/dist/core/sdk.js +17 -33
  172. package/dist/core/sdk.js.map +1 -1
  173. package/dist/core/session-bash-service.d.ts +2 -2
  174. package/dist/core/session-bash-service.d.ts.map +1 -1
  175. package/dist/core/session-bash-service.js +21 -9
  176. package/dist/core/session-bash-service.js.map +1 -1
  177. package/dist/core/session-failure-cause.d.ts.map +1 -1
  178. package/dist/core/session-failure-cause.js +15 -3
  179. package/dist/core/session-failure-cause.js.map +1 -1
  180. package/dist/core/session-prompt-lifecycle.d.ts +25 -0
  181. package/dist/core/session-prompt-lifecycle.d.ts.map +1 -0
  182. package/dist/core/session-prompt-lifecycle.js +87 -0
  183. package/dist/core/session-prompt-lifecycle.js.map +1 -0
  184. package/dist/core/session-run-budget.d.ts +28 -0
  185. package/dist/core/session-run-budget.d.ts.map +1 -0
  186. package/dist/core/session-run-budget.js +127 -0
  187. package/dist/core/session-run-budget.js.map +1 -0
  188. package/dist/core/session-run-termination.d.ts +18 -0
  189. package/dist/core/session-run-termination.d.ts.map +1 -0
  190. package/dist/core/session-run-termination.js +72 -0
  191. package/dist/core/session-run-termination.js.map +1 -0
  192. package/dist/core/session-termination-types.d.ts +97 -0
  193. package/dist/core/session-termination-types.d.ts.map +1 -0
  194. package/dist/core/session-termination-types.js +26 -0
  195. package/dist/core/session-termination-types.js.map +1 -0
  196. package/dist/core/session-termination.d.ts +3 -97
  197. package/dist/core/session-termination.d.ts.map +1 -1
  198. package/dist/core/session-termination.js +25 -29
  199. package/dist/core/session-termination.js.map +1 -1
  200. package/dist/core/skill-selector.d.ts +40 -0
  201. package/dist/core/skill-selector.d.ts.map +1 -0
  202. package/dist/core/skill-selector.js +102 -0
  203. package/dist/core/skill-selector.js.map +1 -0
  204. package/dist/core/slash-commands.d.ts.map +1 -1
  205. package/dist/core/slash-commands.js +1 -0
  206. package/dist/core/slash-commands.js.map +1 -1
  207. package/dist/core/subagent-lane-launcher.d.ts +3 -2
  208. package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
  209. package/dist/core/subagent-lane-launcher.js +24 -13
  210. package/dist/core/subagent-lane-launcher.js.map +1 -1
  211. package/dist/core/tools/render-utils.d.ts.map +1 -1
  212. package/dist/core/tools/render-utils.js +2 -4
  213. package/dist/core/tools/render-utils.js.map +1 -1
  214. package/dist/core/turn-metrics-record.d.ts +55 -0
  215. package/dist/core/turn-metrics-record.d.ts.map +1 -0
  216. package/dist/core/turn-metrics-record.js +149 -0
  217. package/dist/core/turn-metrics-record.js.map +1 -0
  218. package/dist/core/turn-metrics.d.ts +4 -56
  219. package/dist/core/turn-metrics.d.ts.map +1 -1
  220. package/dist/core/turn-metrics.js +12 -60
  221. package/dist/core/turn-metrics.js.map +1 -1
  222. package/dist/core/verified-run/broker.d.ts +26 -0
  223. package/dist/core/verified-run/broker.d.ts.map +1 -0
  224. package/dist/core/verified-run/broker.js +210 -0
  225. package/dist/core/verified-run/broker.js.map +1 -0
  226. package/dist/core/verified-run/candidate.d.ts +24 -0
  227. package/dist/core/verified-run/candidate.d.ts.map +1 -0
  228. package/dist/core/verified-run/candidate.js +167 -0
  229. package/dist/core/verified-run/candidate.js.map +1 -0
  230. package/dist/core/verified-run/check-receipt.d.ts +18 -0
  231. package/dist/core/verified-run/check-receipt.d.ts.map +1 -0
  232. package/dist/core/verified-run/check-receipt.js +92 -0
  233. package/dist/core/verified-run/check-receipt.js.map +1 -0
  234. package/dist/core/verified-run/coordinator.d.ts +39 -0
  235. package/dist/core/verified-run/coordinator.d.ts.map +1 -0
  236. package/dist/core/verified-run/coordinator.js +190 -0
  237. package/dist/core/verified-run/coordinator.js.map +1 -0
  238. package/dist/core/verified-run/dag-candidates.d.ts +8 -0
  239. package/dist/core/verified-run/dag-candidates.d.ts.map +1 -0
  240. package/dist/core/verified-run/dag-candidates.js +65 -0
  241. package/dist/core/verified-run/dag-candidates.js.map +1 -0
  242. package/dist/core/verified-run/dag-phase.d.ts +7 -0
  243. package/dist/core/verified-run/dag-phase.d.ts.map +1 -0
  244. package/dist/core/verified-run/dag-phase.js +92 -0
  245. package/dist/core/verified-run/dag-phase.js.map +1 -0
  246. package/dist/core/verified-run/dag-projection.d.ts +7 -0
  247. package/dist/core/verified-run/dag-projection.d.ts.map +1 -0
  248. package/dist/core/verified-run/dag-projection.js +90 -0
  249. package/dist/core/verified-run/dag-projection.js.map +1 -0
  250. package/dist/core/verified-run/dag-recovery.d.ts +13 -0
  251. package/dist/core/verified-run/dag-recovery.d.ts.map +1 -0
  252. package/dist/core/verified-run/dag-recovery.js +98 -0
  253. package/dist/core/verified-run/dag-recovery.js.map +1 -0
  254. package/dist/core/verified-run/dag-retry-projection.d.ts +9 -0
  255. package/dist/core/verified-run/dag-retry-projection.d.ts.map +1 -0
  256. package/dist/core/verified-run/dag-retry-projection.js +43 -0
  257. package/dist/core/verified-run/dag-retry-projection.js.map +1 -0
  258. package/dist/core/verified-run/dag-types.d.ts +47 -0
  259. package/dist/core/verified-run/dag-types.d.ts.map +1 -0
  260. package/dist/core/verified-run/dag-types.js +2 -0
  261. package/dist/core/verified-run/dag-types.js.map +1 -0
  262. package/dist/core/verified-run/event-parser.d.ts +3 -0
  263. package/dist/core/verified-run/event-parser.d.ts.map +1 -0
  264. package/dist/core/verified-run/event-parser.js +153 -0
  265. package/dist/core/verified-run/event-parser.js.map +1 -0
  266. package/dist/core/verified-run/events.d.ts +4 -0
  267. package/dist/core/verified-run/events.d.ts.map +1 -0
  268. package/dist/core/verified-run/events.js +3 -0
  269. package/dist/core/verified-run/events.js.map +1 -0
  270. package/dist/core/verified-run/evidence-binding.d.ts +18 -0
  271. package/dist/core/verified-run/evidence-binding.d.ts.map +1 -0
  272. package/dist/core/verified-run/evidence-binding.js +81 -0
  273. package/dist/core/verified-run/evidence-binding.js.map +1 -0
  274. package/dist/core/verified-run/evidence.d.ts +27 -0
  275. package/dist/core/verified-run/evidence.d.ts.map +1 -0
  276. package/dist/core/verified-run/evidence.js +100 -0
  277. package/dist/core/verified-run/evidence.js.map +1 -0
  278. package/dist/core/verified-run/journal.d.ts +32 -0
  279. package/dist/core/verified-run/journal.d.ts.map +1 -0
  280. package/dist/core/verified-run/journal.js +103 -0
  281. package/dist/core/verified-run/journal.js.map +1 -0
  282. package/dist/core/verified-run/namespace-identity.d.ts +11 -0
  283. package/dist/core/verified-run/namespace-identity.d.ts.map +1 -0
  284. package/dist/core/verified-run/namespace-identity.js +77 -0
  285. package/dist/core/verified-run/namespace-identity.js.map +1 -0
  286. package/dist/core/verified-run/owned-execution.d.ts +21 -0
  287. package/dist/core/verified-run/owned-execution.d.ts.map +1 -0
  288. package/dist/core/verified-run/owned-execution.js +43 -0
  289. package/dist/core/verified-run/owned-execution.js.map +1 -0
  290. package/dist/core/verified-run/phase-context.d.ts +9 -0
  291. package/dist/core/verified-run/phase-context.d.ts.map +1 -0
  292. package/dist/core/verified-run/phase-context.js +2 -0
  293. package/dist/core/verified-run/phase-context.js.map +1 -0
  294. package/dist/core/verified-run/process-gate.d.ts +5 -0
  295. package/dist/core/verified-run/process-gate.d.ts.map +1 -0
  296. package/dist/core/verified-run/process-gate.js +31 -0
  297. package/dist/core/verified-run/process-gate.js.map +1 -0
  298. package/dist/core/verified-run/projection.d.ts +4 -0
  299. package/dist/core/verified-run/projection.d.ts.map +1 -0
  300. package/dist/core/verified-run/projection.js +219 -0
  301. package/dist/core/verified-run/projection.js.map +1 -0
  302. package/dist/core/verified-run/recovery-clock.d.ts +19 -0
  303. package/dist/core/verified-run/recovery-clock.d.ts.map +1 -0
  304. package/dist/core/verified-run/recovery-clock.js +69 -0
  305. package/dist/core/verified-run/recovery-clock.js.map +1 -0
  306. package/dist/core/verified-run/recovery-command.d.ts +11 -0
  307. package/dist/core/verified-run/recovery-command.d.ts.map +1 -0
  308. package/dist/core/verified-run/recovery-command.js +73 -0
  309. package/dist/core/verified-run/recovery-command.js.map +1 -0
  310. package/dist/core/verified-run/recovery-projection.d.ts +12 -0
  311. package/dist/core/verified-run/recovery-projection.d.ts.map +1 -0
  312. package/dist/core/verified-run/recovery-projection.js +88 -0
  313. package/dist/core/verified-run/recovery-projection.js.map +1 -0
  314. package/dist/core/verified-run/recovery.d.ts +12 -0
  315. package/dist/core/verified-run/recovery.d.ts.map +1 -0
  316. package/dist/core/verified-run/recovery.js +115 -0
  317. package/dist/core/verified-run/recovery.js.map +1 -0
  318. package/dist/core/verified-run/run-types.d.ts +103 -0
  319. package/dist/core/verified-run/run-types.d.ts.map +1 -0
  320. package/dist/core/verified-run/run-types.js +2 -0
  321. package/dist/core/verified-run/run-types.js.map +1 -0
  322. package/dist/core/verified-run/scripted-writer.d.ts +15 -0
  323. package/dist/core/verified-run/scripted-writer.d.ts.map +1 -0
  324. package/dist/core/verified-run/scripted-writer.js +94 -0
  325. package/dist/core/verified-run/scripted-writer.js.map +1 -0
  326. package/dist/core/verified-run/session-port.d.ts +27 -0
  327. package/dist/core/verified-run/session-port.d.ts.map +1 -0
  328. package/dist/core/verified-run/session-port.js +2 -0
  329. package/dist/core/verified-run/session-port.js.map +1 -0
  330. package/dist/core/verified-run/storage.d.ts +15 -0
  331. package/dist/core/verified-run/storage.d.ts.map +1 -0
  332. package/dist/core/verified-run/storage.js +96 -0
  333. package/dist/core/verified-run/storage.js.map +1 -0
  334. package/dist/core/verified-run/verification-phase.d.ts +5 -0
  335. package/dist/core/verified-run/verification-phase.d.ts.map +1 -0
  336. package/dist/core/verified-run/verification-phase.js +56 -0
  337. package/dist/core/verified-run/verification-phase.js.map +1 -0
  338. package/dist/core/verified-run/work-recovery.d.ts +8 -0
  339. package/dist/core/verified-run/work-recovery.d.ts.map +1 -0
  340. package/dist/core/verified-run/work-recovery.js +44 -0
  341. package/dist/core/verified-run/work-recovery.js.map +1 -0
  342. package/dist/core/verified-run/writer-completion.d.ts +4 -0
  343. package/dist/core/verified-run/writer-completion.d.ts.map +1 -0
  344. package/dist/core/verified-run/writer-completion.js +27 -0
  345. package/dist/core/verified-run/writer-completion.js.map +1 -0
  346. package/dist/core/verified-run/writer-phase.d.ts +8 -0
  347. package/dist/core/verified-run/writer-phase.d.ts.map +1 -0
  348. package/dist/core/verified-run/writer-phase.js +76 -0
  349. package/dist/core/verified-run/writer-phase.js.map +1 -0
  350. package/dist/core/verified-run/writer-projection.d.ts +11 -0
  351. package/dist/core/verified-run/writer-projection.d.ts.map +1 -0
  352. package/dist/core/verified-run/writer-projection.js +70 -0
  353. package/dist/core/verified-run/writer-projection.js.map +1 -0
  354. package/dist/core/verified-run/writer-recovery.d.ts +16 -0
  355. package/dist/core/verified-run/writer-recovery.d.ts.map +1 -0
  356. package/dist/core/verified-run/writer-recovery.js +107 -0
  357. package/dist/core/verified-run/writer-recovery.js.map +1 -0
  358. package/dist/core/workload-permit-pool.d.ts +1 -3
  359. package/dist/core/workload-permit-pool.d.ts.map +1 -1
  360. package/dist/core/workload-permit-pool.js +11 -11
  361. package/dist/core/workload-permit-pool.js.map +1 -1
  362. package/dist/guardrails/evidence-system.d.ts +1 -1
  363. package/dist/guardrails/evidence-system.d.ts.map +1 -1
  364. package/dist/guardrails/evidence-system.js +18 -29
  365. package/dist/guardrails/evidence-system.js.map +1 -1
  366. package/dist/guardrails/merge-gate-result.d.ts +4 -0
  367. package/dist/guardrails/merge-gate-result.d.ts.map +1 -0
  368. package/dist/guardrails/merge-gate-result.js +32 -0
  369. package/dist/guardrails/merge-gate-result.js.map +1 -0
  370. package/dist/index.d.ts +3 -2
  371. package/dist/index.d.ts.map +1 -1
  372. package/dist/index.js +2 -2
  373. package/dist/index.js.map +1 -1
  374. package/dist/main.d.ts.map +1 -1
  375. package/dist/main.js +17 -23
  376. package/dist/main.js.map +1 -1
  377. package/dist/modes/interactive/components/session-failure.d.ts +13 -0
  378. package/dist/modes/interactive/components/session-failure.d.ts.map +1 -0
  379. package/dist/modes/interactive/components/session-failure.js +81 -0
  380. package/dist/modes/interactive/components/session-failure.js.map +1 -0
  381. package/dist/modes/interactive/interactive-login-options.d.ts +27 -0
  382. package/dist/modes/interactive/interactive-login-options.d.ts.map +1 -0
  383. package/dist/modes/interactive/interactive-login-options.js +54 -0
  384. package/dist/modes/interactive/interactive-login-options.js.map +1 -0
  385. package/dist/modes/interactive/interactive-mode.d.ts +8 -4
  386. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  387. package/dist/modes/interactive/interactive-mode.js +183 -171
  388. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  389. package/dist/modes/interactive/interactive-resume-command.d.ts +3 -0
  390. package/dist/modes/interactive/interactive-resume-command.d.ts.map +1 -0
  391. package/dist/modes/interactive/interactive-resume-command.js +24 -0
  392. package/dist/modes/interactive/interactive-resume-command.js.map +1 -0
  393. package/dist/modes/interactive/interactive-tool-result.d.ts +32 -0
  394. package/dist/modes/interactive/interactive-tool-result.d.ts.map +1 -0
  395. package/dist/modes/interactive/interactive-tool-result.js +59 -0
  396. package/dist/modes/interactive/interactive-tool-result.js.map +1 -0
  397. package/dist/modes/interactive/tui-diagnostics.d.ts +36 -0
  398. package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -0
  399. package/dist/modes/interactive/tui-diagnostics.js +105 -0
  400. package/dist/modes/interactive/tui-diagnostics.js.map +1 -0
  401. package/dist/modes/interactive/tui-runtime-info.d.ts +20 -0
  402. package/dist/modes/interactive/tui-runtime-info.d.ts.map +1 -0
  403. package/dist/modes/interactive/tui-runtime-info.js +76 -0
  404. package/dist/modes/interactive/tui-runtime-info.js.map +1 -0
  405. package/dist/modes/print-mode.d.ts.map +1 -1
  406. package/dist/modes/print-mode.js +22 -0
  407. package/dist/modes/print-mode.js.map +1 -1
  408. package/dist/utils/clipboard-image.d.ts.map +1 -1
  409. package/dist/utils/clipboard-image.js +10 -60
  410. package/dist/utils/clipboard-image.js.map +1 -1
  411. package/dist/utils/terminal-links.d.ts +13 -0
  412. package/dist/utils/terminal-links.d.ts.map +1 -0
  413. package/dist/utils/terminal-links.js +68 -0
  414. package/dist/utils/terminal-links.js.map +1 -0
  415. package/dist/utils/windows-clipboard-image.d.ts +16 -0
  416. package/dist/utils/windows-clipboard-image.d.ts.map +1 -0
  417. package/dist/utils/windows-clipboard-image.js +87 -0
  418. package/dist/utils/windows-clipboard-image.js.map +1 -0
  419. package/docs/adaptorch-onboarding.md +80 -0
  420. package/docs/containerization.md +12 -0
  421. package/docs/context-files.md +97 -0
  422. package/docs/correctness-wall.md +8 -0
  423. package/docs/custom-provider.md +35 -0
  424. package/docs/development.md +28 -3
  425. package/docs/environment-variables.md +2 -0
  426. package/docs/grok-harness.md +4 -0
  427. package/docs/harness-boundaries.md +154 -0
  428. package/docs/harness-improvements.md +90 -0
  429. package/docs/json.md +19 -0
  430. package/docs/keybindings.md +2 -2
  431. package/docs/mcp.md +22 -2
  432. package/docs/metrics.md +66 -16
  433. package/docs/model-catalog-refresh.md +190 -0
  434. package/docs/model-contract.md +168 -0
  435. package/docs/models.md +21 -4
  436. package/docs/provider-resilience.md +5 -3
  437. package/docs/providers.md +68 -1
  438. package/docs/quickstart.md +4 -0
  439. package/docs/release-audit-0.98.4.md +112 -0
  440. package/docs/release-audit-0.98.5.md +84 -0
  441. package/docs/review-bundle-followup.md +195 -0
  442. package/docs/run-protocol.md +33 -1
  443. package/docs/runtime-algorithms.md +45 -0
  444. package/docs/sdk.md +204 -2
  445. package/docs/sessions.md +1 -1
  446. package/docs/settings.md +17 -1
  447. package/docs/skills.md +2 -0
  448. package/docs/tb21-audit.md +131 -0
  449. package/docs/terminal-setup.md +20 -0
  450. package/docs/usage.md +77 -1
  451. package/docs/verified-run-testing.md +417 -0
  452. package/docs/verified-run.md +388 -0
  453. package/docs/windows.md +48 -0
  454. package/examples/README.md +4 -0
  455. package/examples/context/AGENTS.md +64 -0
  456. package/examples/context/CLAUDE.md +17 -0
  457. package/examples/context/INTERNET.md +42 -0
  458. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  459. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  460. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  461. package/examples/extensions/gondolin/package-lock.json +2 -2
  462. package/examples/extensions/gondolin/package.json +1 -1
  463. package/examples/extensions/sandbox/package-lock.json +2 -2
  464. package/examples/extensions/sandbox/package.json +1 -1
  465. package/examples/extensions/with-deps/package-lock.json +2 -2
  466. package/examples/extensions/with-deps/package.json +1 -1
  467. package/npm-shrinkwrap.json +18 -18
  468. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"session-bash-service.js","sourceRoot":"","sources":["../../src/core/session-bash-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAmB,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,+BAA+B,EAC/B,uBAAuB,GACvB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAItF,OAAO,EAAkD,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAoC5G,SAAS,oBAAoB,CAAC,KAAc,EAAkB;IAC7D,OAAO,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC;AAAA,CACnF;AAED,SAAS,4BAA4B,CAAC,MAAc,EAAc;IACjE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAAA,CAC3E;AAED,MAAM,OAAO,kBAAkB;IACtB,eAAe,CAA8B;IAC7C,mBAAmB,GAA2B,EAAE,CAAC;IACxC,IAAI,CAAyB;IAE9C,YAAY,IAA4B,EAAE;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;IAED,KAAK,CAAC,WAAW,CAChB,OAAe,EACf,OAAiC,EACjC,OAA4B,EACN;QACtB,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC;QACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,qBAAqB,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC;QAC1C,MAAM,eAAe,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QACnE,IAAI,aAAa,EAAE,CAAC;YACnB,mBAAmB,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,mBAAmB,CAAC,aAAa,EAAE,OAAO,CAAC,EAAE;gBAC7E,SAAS,EAAE,SAAS;gBACpB,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,eAAe;aACxB,CAAC,CAAC;QACJ,CAAC;QAED,yEAAyE;QACzE,yEAAyE;QACzE,2EAAuE;QACvE,IAAI,OAAO,EAAE,UAAU,KAAK,UAAU,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC;YACtE,MAAM,YAAY,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YACnD,IAAI,YAAY,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,yCAAwC,YAAY,CAAC,IAAI,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;YACtG,CAAC;QACF,CAAC;QAED,4EAA4E;QAC5E,8EAA8E;QAC9E,gFAAgF;QAChF,8EAA8E;QAC9E,wEAAwE;QACxE,IAAI,OAAO,EAAE,UAAU,KAAK,UAAU,EAAE,CAAC;YACxC,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,eAAe,EAAE;gBAC3D,KAAK,EAAE,KAAK;gBACZ,qBAAqB,EAAE,+BAA+B,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;aAC3E,CAAC,CAAC;YACH,IAAI,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACpB,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACxD,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBACjD,OAAO,OAAO,CAAC;YAChB,CAAC;QACF,CAAC;QAED,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC7C,IAAI,qBAA+C,CAAC;QAEpD,IAAI,CAAC;YACJ,yEAAwE;YACxE,mEAAmE;YACnE,IAAI,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBACrC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBAClG,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;oBACjC,MAAM,OAAO,GAAG,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBACtD,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;oBACjD,OAAO,OAAO,CAAC;gBAChB,CAAC;gBACD,qBAAqB,GAAG,KAAK,CAAC,OAAO,CAAC;YACvC,CAAC;YACD,IAAI,CAAC;gBACJ,MAAM,UAAU,GACf,OAAO,EAAE,UAAU;oBACnB,yBAAyB,CAAC;wBACzB,SAAS;wBACT,aAAa,EAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC;qBAC/D,CAAC,CAAC;gBACJ,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC/B,MAAM,cAAc,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC;oBACzC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;oBACjB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAAA,CAC1E,CAAC;gBAEF,yFAAyF;gBACzF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC;oBAC9C,eAAe;oBACf,GAAG;oBACH,SAAS;oBACT,UAAU;oBACV,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM;oBACnC,OAAO,EAAE,cAAc;iBACvB,CAAC,CAAC;gBACH,MAAM,MAAM,GACX,QAAQ;oBACR,CAAC,MAAM,yBAAyB,CAAC,eAAe,EAAE,GAAG,EAAE,UAAU,EAAE;wBAClE,OAAO,EAAE,cAAc;wBACvB,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM;qBACnC,CAAC,CAAC,CAAC;gBAEL,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;gBAChD,OAAO,MAAM,CAAC;YACf,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;oBAClC,MAAM,KAAK,CAAC;gBACb,CAAC;gBACD,MAAM,OAAO,GAAG,4BAA4B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC5D,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBACjD,OAAO,OAAO,CAAC;YAChB,CAAC;QACF,CAAC;gBAAS,CAAC;YACV,sEAAqE;YACrE,qBAAqB,EAAE,EAAE,CAAC;YAC1B,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QAClC,CAAC;IAAA,CACD;IAED;;;OAGG;IACH,gBAAgB,CAAC,OAAe,EAAE,MAAkB,EAAE,OAA0C,EAAQ;QACvG,MAAM,WAAW,GAAyB;YACzC,IAAI,EAAE,eAAe;YACrB,OAAO;YACP,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,kBAAkB,EAAE,OAAO,EAAE,kBAAkB;SAC/C,CAAC;QAEF,sFAAsF;QACtF,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;QACtC,CAAC;IAAA,CACD;IAED,mCAAmC;IACnC,SAAS,GAAS;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,CAAC;IAAA,CAC9B;IAED,IAAI,aAAa,GAAY;QAC5B,OAAO,IAAI,CAAC,eAAe,KAAK,SAAS,CAAC;IAAA,CAC1C;IAED,IAAI,sBAAsB,GAAY;QACrC,OAAO,IAAI,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC;IAAA,CAC3C;IAED,+EAA+E;IAC/E,YAAY,GAAS;QACpB,IAAI,IAAI,CAAC,mBAAmB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAClD,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACpD,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,CAAC,mBAAmB,GAAG,EAAE,CAAC;IAAA,CAC9B;CACD","sourcesContent":["/**\n * Session bash service: command execution, result recording, abort, and the\n * streaming-deferral queue. Extracted from AgentSession so the session class\n * coordinates lifecycle while this owns the bash surface end-to-end.\n *\n * Ordering contract (preserved from the session implementation):\n * - results recorded while the agent is streaming are queued and flushed on\n * turn end (`flushPending`) so tool_use/tool_result ordering never breaks;\n * - `recordBashResult` is idempotent-safe to call from executeBash and from\n * extensions that run bash themselves.\n */\nimport { type BashResult, executeBashWithOperations } from \"./bash-executor.ts\";\nimport { classifyShellCommand } from \"./command-safety.ts\";\nimport {\n\tbuildBlockedBashResult,\n\tevaluateCommandGate,\n\tisCommandSafetyAssumeYesEnabled,\n\tisCommandSafetyDisabled,\n} from \"./extensions/builtin/command-safety-gate.ts\";\nimport type { LoadoutAccessPolicy } from \"./loadout-access-policy.ts\";\nimport { assertLoadoutAccess, decideLoadoutAccess } from \"./loadout-access-policy.ts\";\nimport type { BashExecutionMessage } from \"./messages.ts\";\nimport type { SessionBashRuntime } from \"./session-bash-runtime.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\nimport { type BashOperations, type BashSandboxPreflight, createLocalBashOperations } from \"./tools/bash.ts\";\n\nexport interface ExecuteBashOptions {\n\texcludeFromContext?: boolean;\n\t/** Optional identifier echoed in `bash_execution_update` events (RPC request correlation). */\n\tid?: string;\n\toperations?: BashOperations;\n\tsafetyGate?: \"headless\";\n\t/** Trusted internal/test override for local bash sandboxing. */\n\tsandboxPolicy?: BashSandboxPreflight;\n}\n\n/** Resource gate grant (roadmap §11/M3): §11.3 blocked payload, permit release, or ungated. */\nexport interface BashResourcePermitGrant {\n\t/** §11.3 structured block JSON; when set the command must not spawn. */\n\treadonly blocked?: string;\n\treadonly release?: () => void;\n}\n\nexport interface SessionBashServiceDeps {\n\treadonly settings: SettingsManager;\n\treadonly runtime: SessionBashRuntime;\n\treadonly loadoutPolicy: LoadoutAccessPolicy | undefined;\n\treadonly getCwd: () => string;\n\treadonly emit: (event: { type: \"bash_execution_update\"; id?: string; delta: string }) => void;\n\treadonly isStreaming: () => boolean;\n\treadonly pushMessage: (message: BashExecutionMessage) => void;\n\treadonly appendMessage: (message: BashExecutionMessage) => void;\n\t/**\n\t * Optional resource governor gate, invoked AFTER command safety (§9.4\n\t * order) and before any shell spawns. The abort signal cancels a queued\n\t * permit wait. Implementations must never throw.\n\t */\n\treadonly acquireResourcePermit?: (command: string, signal: AbortSignal) => Promise<BashResourcePermitGrant>;\n}\n\nfunction isSandboxDeniedError(error: unknown): error is Error {\n\treturn error instanceof Error && error.message.startsWith(\"sandbox: shell denied\");\n}\n\nfunction buildSandboxDeniedBashResult(reason: string): BashResult {\n\treturn { output: reason, exitCode: 1, cancelled: false, truncated: false };\n}\n\nexport class SessionBashService {\n\tprivate abortController: AbortController | undefined;\n\tprivate pendingBashMessages: BashExecutionMessage[] = [];\n\tprivate readonly deps: SessionBashServiceDeps;\n\n\tconstructor(deps: SessionBashServiceDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync executeBash(\n\t\tcommand: string,\n\t\tonChunk?: (chunk: string) => void,\n\t\toptions?: ExecuteBashOptions,\n\t): Promise<BashResult> {\n\t\tconst { settings, runtime, loadoutPolicy } = this.deps;\n\t\tconst prefix = settings.getShellCommandPrefix();\n\t\tconst shellPath = settings.getShellPath();\n\t\tconst resolvedCommand = prefix ? `${prefix}\\n${command}` : command;\n\t\tif (loadoutPolicy) {\n\t\t\tassertLoadoutAccess((request) => decideLoadoutAccess(loadoutPolicy, request), {\n\t\t\t\toperation: \"execute\",\n\t\t\t\ttoolName: \"bash\",\n\t\t\t\tcommand: resolvedCommand,\n\t\t\t});\n\t\t}\n\n\t\t// Non-negotiable safety floor for headless callers (RPC bash): hard-deny\n\t\t// block-tier commands before any shell is spawned. YOLO mode opts out of\n\t\t// the entire gate — the floor included — so nothing prompts or denies.\n\t\tif (options?.safetyGate === \"headless\" && !isCommandSafetyDisabled()) {\n\t\t\tconst floorVerdict = classifyShellCommand(command);\n\t\t\tif (floorVerdict.risk === \"block\") {\n\t\t\t\tthrow new Error(`OMK §0.1 safety floor blocked bash: [${floorVerdict.rule}] ${floorVerdict.reason}`);\n\t\t\t}\n\t\t}\n\n\t\t// Command-safety parity for non-interactive callers (RPC bash). Interactive\n\t\t// `!`/`!!` bash is gated earlier through the user_bash extension event, which\n\t\t// keeps its prompt-based approval semantics, so it does not pass safetyGate and\n\t\t// is never double-prompted here. confirm/block-tier verdicts deny headlessly;\n\t\t// the EFFECTIVE command (after the shell command prefix) is classified.\n\t\tif (options?.safetyGate === \"headless\") {\n\t\t\tconst decision = await evaluateCommandGate(resolvedCommand, {\n\t\t\t\thasUI: false,\n\t\t\t\theadlessConfirmPolicy: isCommandSafetyAssumeYesEnabled() ? \"allow\" : \"deny\",\n\t\t\t});\n\t\t\tif (decision?.deny) {\n\t\t\t\tconst blocked = buildBlockedBashResult(decision.reason);\n\t\t\t\tthis.recordBashResult(command, blocked, options);\n\t\t\t\treturn blocked;\n\t\t\t}\n\t\t}\n\n\t\tthis.abortController = new AbortController();\n\t\tlet releaseResourcePermit: (() => void) | undefined;\n\n\t\ttry {\n\t\t\t// Resource safety gate (§9.4: command safety above stays authoritative;\n\t\t\t// pressure can only throttle or defer an already-allowed command).\n\t\t\tif (this.deps.acquireResourcePermit) {\n\t\t\t\tconst grant = await this.deps.acquireResourcePermit(resolvedCommand, this.abortController.signal);\n\t\t\t\tif (grant.blocked !== undefined) {\n\t\t\t\t\tconst blocked = buildBlockedBashResult(grant.blocked);\n\t\t\t\t\tthis.recordBashResult(command, blocked, options);\n\t\t\t\t\treturn blocked;\n\t\t\t\t}\n\t\t\t\treleaseResourcePermit = grant.release;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst operations =\n\t\t\t\t\toptions?.operations ??\n\t\t\t\t\tcreateLocalBashOperations({\n\t\t\t\t\t\tshellPath,\n\t\t\t\t\t\tsandboxPolicy: runtime.sandboxPreflight(options?.sandboxPolicy),\n\t\t\t\t\t});\n\t\t\t\tconst cwd = this.deps.getCwd();\n\t\t\t\tconst onChunkWrapped = (delta: string) => {\n\t\t\t\t\tonChunk?.(delta);\n\t\t\t\t\tthis.deps.emit({ type: \"bash_execution_update\", id: options?.id, delta });\n\t\t\t\t};\n\n\t\t\t\t// Default-on verified path when a session ledger exists; OMK_VERIFIED_BASH=0 rolls back.\n\t\t\t\tconst verified = await runtime.executeVerified({\n\t\t\t\t\tresolvedCommand,\n\t\t\t\t\tcwd,\n\t\t\t\t\tshellPath,\n\t\t\t\t\toperations,\n\t\t\t\t\tsignal: this.abortController.signal,\n\t\t\t\t\tonChunk: onChunkWrapped,\n\t\t\t\t});\n\t\t\t\tconst result: BashResult =\n\t\t\t\t\tverified ??\n\t\t\t\t\t(await executeBashWithOperations(resolvedCommand, cwd, operations, {\n\t\t\t\t\t\tonChunk: onChunkWrapped,\n\t\t\t\t\t\tsignal: this.abortController.signal,\n\t\t\t\t\t}));\n\n\t\t\t\tthis.recordBashResult(command, result, options);\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isSandboxDeniedError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst blocked = buildSandboxDeniedBashResult(error.message);\n\t\t\t\tthis.recordBashResult(command, blocked, options);\n\t\t\t\treturn blocked;\n\t\t\t}\n\t\t} finally {\n\t\t\t// §10.3 exactly-once: the pool treats a repeated release as a no-op.\n\t\t\treleaseResourcePermit?.();\n\t\t\tthis.abortController = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Record a bash execution result in session history.\n\t * Used by executeBash and by extensions that handle bash execution themselves.\n\t */\n\trecordBashResult(command: string, result: BashResult, options?: { excludeFromContext?: boolean }): void {\n\t\tconst bashMessage: BashExecutionMessage = {\n\t\t\trole: \"bashExecution\",\n\t\t\tcommand,\n\t\t\toutput: result.output,\n\t\t\texitCode: result.exitCode,\n\t\t\tcancelled: result.cancelled,\n\t\t\ttruncated: result.truncated,\n\t\t\tfullOutputPath: result.fullOutputPath,\n\t\t\ttimestamp: Date.now(),\n\t\t\texcludeFromContext: options?.excludeFromContext,\n\t\t};\n\n\t\t// If agent is streaming, defer adding to avoid breaking tool_use/tool_result ordering\n\t\tif (this.deps.isStreaming()) {\n\t\t\tthis.pendingBashMessages.push(bashMessage);\n\t\t} else {\n\t\t\tthis.deps.pushMessage(bashMessage);\n\t\t\tthis.deps.appendMessage(bashMessage);\n\t\t}\n\t}\n\n\t/** Cancel running bash command. */\n\tabortBash(): void {\n\t\tthis.abortController?.abort();\n\t}\n\n\tget isBashRunning(): boolean {\n\t\treturn this.abortController !== undefined;\n\t}\n\n\tget hasPendingBashMessages(): boolean {\n\t\treturn this.pendingBashMessages.length > 0;\n\t}\n\n\t/** Flush queued results after the agent turn completes (ordering contract). */\n\tflushPending(): void {\n\t\tif (this.pendingBashMessages.length === 0) return;\n\t\tfor (const bashMessage of this.pendingBashMessages) {\n\t\t\tthis.deps.pushMessage(bashMessage);\n\t\t\tthis.deps.appendMessage(bashMessage);\n\t\t}\n\t\tthis.pendingBashMessages = [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-bash-service.js","sourceRoot":"","sources":["../../src/core/session-bash-service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Session bash service: command execution, result recording, abort, and the\n * streaming-deferral queue. Extracted from AgentSession so the session class\n * coordinates lifecycle while this owns the bash surface end-to-end.\n *\n * Ordering contract (preserved from the session implementation):\n * - results recorded while the agent is streaming are queued and flushed on\n * turn end (`flushPending`) so tool_use/tool_result ordering never breaks;\n * - `recordBashResult` is idempotent-safe to call from executeBash and from\n * extensions that run bash themselves.\n */\nimport { type BashResult, executeBashWithOperations } from \"./bash-executor.ts\";\nimport { classifyShellCommand } from \"./command-safety.ts\";\nimport {\n\tbuildBlockedBashResult,\n\tevaluateCommandGate,\n\tisCommandSafetyAssumeYesEnabled,\n\tisCommandSafetyDisabled,\n} from \"./extensions/builtin/command-safety-gate.ts\";\nimport type { LoadoutAccessPolicy } from \"./loadout-access-policy.ts\";\nimport { assertLoadoutAccess, decideLoadoutAccess } from \"./loadout-access-policy.ts\";\nimport type { BashExecutionMessage } from \"./messages.ts\";\nimport type { SessionBashRuntime } from \"./session-bash-runtime.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\nimport { type BashOperations, type BashSandboxPreflight, createLocalBashOperations } from \"./tools/bash.ts\";\n\nexport interface ExecuteBashOptions {\n\texcludeFromContext?: boolean;\n\t/** Optional identifier echoed in `bash_execution_update` events (RPC request correlation). */\n\tid?: string;\n\toperations?: BashOperations;\n\tsafetyGate?: \"headless\";\n\t/** Trusted internal/test override for local bash sandboxing. */\n\tsandboxPolicy?: BashSandboxPreflight;\n}\n\n/** Resource gate grant (roadmap §11/M3): §11.3 blocked payload, permit release, or ungated. */\nexport interface BashResourcePermitGrant {\n\t/** §11.3 structured block JSON; when set the command must not spawn. */\n\treadonly blocked?: string;\n\treadonly release?: () => void;\n}\n\nexport interface SessionBashServiceDeps {\n\treadonly settings: SettingsManager;\n\treadonly runtime: SessionBashRuntime;\n\treadonly loadoutPolicy: LoadoutAccessPolicy | undefined;\n\treadonly getCwd: () => string;\n\treadonly emit: (event: { type: \"bash_execution_update\"; id?: string; delta: string }) => void;\n\treadonly isStreaming: () => boolean;\n\treadonly pushMessage: (message: BashExecutionMessage) => void;\n\treadonly appendMessage: (message: BashExecutionMessage) => void;\n\t/**\n\t * Optional resource governor gate, invoked AFTER command safety (§9.4\n\t * order) and before any shell spawns. The abort signal cancels a queued\n\t * permit wait. Implementations must never throw.\n\t */\n\treadonly acquireResourcePermit?: (command: string, signal: AbortSignal) => Promise<BashResourcePermitGrant>;\n}\n\nfunction isSandboxDeniedError(error: unknown): error is Error {\n\treturn error instanceof Error && error.message.startsWith(\"sandbox: shell denied\");\n}\n\nfunction buildSandboxDeniedBashResult(reason: string): BashResult {\n\treturn { output: reason, exitCode: 1, cancelled: false, truncated: false };\n}\n\nexport class SessionBashService {\n\tprivate readonly abortControllers = new Set<AbortController>();\n\tprivate pendingBashMessages: BashExecutionMessage[] = [];\n\tprivate readonly deps: SessionBashServiceDeps;\n\n\tconstructor(deps: SessionBashServiceDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync executeBash(\n\t\tcommand: string,\n\t\tonChunk?: (chunk: string) => void,\n\t\toptions?: ExecuteBashOptions,\n\t): Promise<BashResult> {\n\t\tconst { settings, runtime, loadoutPolicy } = this.deps;\n\t\tconst prefix = settings.getShellCommandPrefix();\n\t\tconst shellPath = settings.getShellPath();\n\t\tconst resolvedCommand = prefix ? `${prefix}\\n${command}` : command;\n\t\tif (loadoutPolicy) {\n\t\t\tassertLoadoutAccess((request) => decideLoadoutAccess(loadoutPolicy, request), {\n\t\t\t\toperation: \"execute\",\n\t\t\t\ttoolName: \"bash\",\n\t\t\t\tcommand: resolvedCommand,\n\t\t\t});\n\t\t}\n\n\t\t// Non-negotiable safety floor for headless callers (RPC bash): hard-deny\n\t\t// block-tier commands before any shell is spawned. YOLO mode opts out of\n\t\t// the entire gate — the floor included — so nothing prompts or denies.\n\t\tif (options?.safetyGate === \"headless\" && !isCommandSafetyDisabled()) {\n\t\t\tconst floorVerdict = classifyShellCommand(command);\n\t\t\tif (floorVerdict.risk === \"block\") {\n\t\t\t\tthrow new Error(`OMK §0.1 safety floor blocked bash: [${floorVerdict.rule}] ${floorVerdict.reason}`);\n\t\t\t}\n\t\t}\n\n\t\t// Command-safety parity for non-interactive callers (RPC bash). Interactive\n\t\t// `!`/`!!` bash is gated earlier through the user_bash extension event, which\n\t\t// keeps its prompt-based approval semantics, so it does not pass safetyGate and\n\t\t// is never double-prompted here. confirm/block-tier verdicts deny headlessly;\n\t\t// the EFFECTIVE command (after the shell command prefix) is classified.\n\t\tif (options?.safetyGate === \"headless\") {\n\t\t\tconst decision = await evaluateCommandGate(resolvedCommand, {\n\t\t\t\thasUI: false,\n\t\t\t\theadlessConfirmPolicy: isCommandSafetyAssumeYesEnabled() ? \"allow\" : \"deny\",\n\t\t\t});\n\t\t\tif (decision?.deny) {\n\t\t\t\tconst blocked = buildBlockedBashResult(decision.reason);\n\t\t\t\tthis.recordBashResult(command, blocked, options);\n\t\t\t\treturn blocked;\n\t\t\t}\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\t\tthis.abortControllers.add(abortController);\n\t\tlet releaseResourcePermit: (() => void) | undefined;\n\n\t\ttry {\n\t\t\t// Resource safety gate (§9.4: command safety above stays authoritative;\n\t\t\t// pressure can only throttle or defer an already-allowed command).\n\t\t\tif (this.deps.acquireResourcePermit) {\n\t\t\t\tconst grant = await this.deps.acquireResourcePermit(resolvedCommand, abortController.signal);\n\t\t\t\tif (grant.blocked !== undefined) {\n\t\t\t\t\tconst blocked = buildBlockedBashResult(grant.blocked);\n\t\t\t\t\tthis.recordBashResult(command, blocked, options);\n\t\t\t\t\treturn blocked;\n\t\t\t\t}\n\t\t\t\treleaseResourcePermit = grant.release;\n\t\t\t}\n\t\t\tif (abortController.signal.aborted) {\n\t\t\t\tconst cancelled: BashResult = {\n\t\t\t\t\toutput: \"Command aborted before execution.\",\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t};\n\t\t\t\tthis.recordBashResult(command, cancelled, options);\n\t\t\t\treturn cancelled;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst operations =\n\t\t\t\t\toptions?.operations ??\n\t\t\t\t\tcreateLocalBashOperations({\n\t\t\t\t\t\tshellPath,\n\t\t\t\t\t\tsandboxPolicy: runtime.sandboxPreflight(options?.sandboxPolicy),\n\t\t\t\t\t});\n\t\t\t\tconst cwd = this.deps.getCwd();\n\t\t\t\tconst onChunkWrapped = (delta: string) => {\n\t\t\t\t\tonChunk?.(delta);\n\t\t\t\t\tthis.deps.emit({ type: \"bash_execution_update\", id: options?.id, delta });\n\t\t\t\t};\n\n\t\t\t\t// Default-on verified path when a session ledger exists; OMK_VERIFIED_BASH=0 rolls back.\n\t\t\t\tconst verified = await runtime.executeVerified({\n\t\t\t\t\tresolvedCommand,\n\t\t\t\t\tcwd,\n\t\t\t\t\tshellPath,\n\t\t\t\t\toperations,\n\t\t\t\t\tsignal: abortController.signal,\n\t\t\t\t\tonChunk: onChunkWrapped,\n\t\t\t\t});\n\t\t\t\tconst result: BashResult =\n\t\t\t\t\tverified ??\n\t\t\t\t\t(await executeBashWithOperations(resolvedCommand, cwd, operations, {\n\t\t\t\t\t\tonChunk: onChunkWrapped,\n\t\t\t\t\t\tsignal: abortController.signal,\n\t\t\t\t\t}));\n\n\t\t\t\tthis.recordBashResult(command, result, options);\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isSandboxDeniedError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst blocked = buildSandboxDeniedBashResult(error.message);\n\t\t\t\tthis.recordBashResult(command, blocked, options);\n\t\t\t\treturn blocked;\n\t\t\t}\n\t\t} finally {\n\t\t\t// §10.3 exactly-once: the pool treats a repeated release as a no-op.\n\t\t\treleaseResourcePermit?.();\n\t\t\tthis.abortControllers.delete(abortController);\n\t\t}\n\t}\n\n\t/**\n\t * Record a bash execution result in session history.\n\t * Used by executeBash and by extensions that handle bash execution themselves.\n\t */\n\trecordBashResult(command: string, result: BashResult, options?: { excludeFromContext?: boolean }): void {\n\t\tconst bashMessage: BashExecutionMessage = {\n\t\t\trole: \"bashExecution\",\n\t\t\tcommand,\n\t\t\toutput: result.output,\n\t\t\texitCode: result.exitCode,\n\t\t\tcancelled: result.cancelled,\n\t\t\ttruncated: result.truncated,\n\t\t\tfullOutputPath: result.fullOutputPath,\n\t\t\ttimestamp: Date.now(),\n\t\t\texcludeFromContext: options?.excludeFromContext,\n\t\t};\n\n\t\t// If agent is streaming, defer adding to avoid breaking tool_use/tool_result ordering\n\t\tif (this.deps.isStreaming()) {\n\t\t\tthis.pendingBashMessages.push(bashMessage);\n\t\t} else {\n\t\t\tthis.deps.pushMessage(bashMessage);\n\t\t\tthis.deps.appendMessage(bashMessage);\n\t\t}\n\t}\n\n\t/** Request cancellation for every command still owned by this session. */\n\tabortBash(): void {\n\t\tfor (const controller of this.abortControllers) controller.abort();\n\t}\n\n\tget isBashRunning(): boolean {\n\t\treturn this.abortControllers.size > 0;\n\t}\n\n\tget hasPendingBashMessages(): boolean {\n\t\treturn this.pendingBashMessages.length > 0;\n\t}\n\n\t/** Flush queued results after the agent turn completes (ordering contract). */\n\tflushPending(): void {\n\t\tif (this.pendingBashMessages.length === 0) return;\n\t\tfor (const bashMessage of this.pendingBashMessages) {\n\t\t\tthis.deps.pushMessage(bashMessage);\n\t\t\tthis.deps.appendMessage(bashMessage);\n\t\t}\n\t\tthis.pendingBashMessages = [];\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-failure-cause.d.ts","sourceRoot":"","sources":["../../src/core/session-failure-cause.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,QAAQ,CAAC;AAOlE,OAAO,EAA0C,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAmBhH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKtF;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,uBAAuB,CA8C9G;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,uBAAuB,CA0BjG;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAW3E","sourcesContent":["/**\n * Failure-text to `SessionTerminationCause` classification.\n *\n * This is the layer *below* `session-termination.ts`: that module turns a cause\n * into a kind/phase/next-action record, while this one decides which cause a\n * provider error, a preflight rejection, or a thrown runtime error represents.\n *\n * The functions are pure so the ordering can be pinned by direct tests. Ordering\n * is load-bearing in two places that are easy to regress:\n *\n * - quota/billing exhaustion is matched *before* the generic 401/403 auth\n * patterns, because `403 ... usage limit for this billing cycle` is\n * transient per cycle and must fail over instead of terminating as auth;\n * - upstream 5xx/dropped streams are matched *before* the protocol fallback,\n * so guidance points at retry/model-switch rather than transcript sanitize.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"omk-ai\";\nimport {\n\tisClaudeCodeVersionTooOldMessage,\n\tisQuotaExhaustionMessage,\n\tisUpstreamUnavailableMessage,\n} from \"./provider-resilience.ts\";\nimport { redactSensitiveText } from \"./redaction.ts\";\nimport { MAX_SESSION_TERMINATION_MESSAGE_LENGTH, type SessionTerminationCause } from \"./session-termination.ts\";\n\n/**\n * Errno codes that mean a write could not be durably completed. Anything else\n * (a missing path, for example) is not evidence of a persistence fault.\n */\nconst PERSISTENCE_ERRNO_CODES: ReadonlySet<string> = new Set([\n\t\"EACCES\",\n\t\"EDQUOT\",\n\t\"EFBIG\",\n\t\"EIO\",\n\t\"EISDIR\",\n\t\"EMFILE\",\n\t\"ENFILE\",\n\t\"ENOSPC\",\n\t\"EPERM\",\n\t\"EROFS\",\n]);\n\n/**\n * Normalize a free-text failure message for the termination record: redact\n * secret-shaped content, drop NUL bytes, and bound the length.\n */\nexport function terminationMessage(value: string | undefined, fallback: string): string {\n\tconst redacted = redactSensitiveText(value?.trim() || fallback)\n\t\t.replace(/\\0/g, \"\")\n\t\t.slice(0, MAX_SESSION_TERMINATION_MESSAGE_LENGTH);\n\treturn redacted || fallback;\n}\n\n/** Classify a failed provider turn from the assistant message it produced. */\nexport function providerFailureCause(message: AssistantMessage, contextWindow: number): SessionTerminationCause {\n\tconst text = message.errorMessage ?? \"\";\n\tif (isContextOverflow(message, contextWindow)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\t// Fable/Claude often emit stop_reason=refusal on benign turns (false positive).\n\t// Match broadly so we never mislabel these as provider.protocol.\n\tif (\n\t\t/stop_reason\\s*=\\s*(refusal|sensitive)|content\\/safety stop|safety stop|provider\\.refusal|kind=provider_refusal/i.test(\n\t\t\ttext,\n\t\t)\n\t) {\n\t\treturn { area: \"provider\", code: \"refusal\" };\n\t}\n\t// A stale spoofed Claude Code version against a newer model's gate is a client\n\t// configuration fault, not a transcript-shape one: it must not inherit the\n\t// retryable protocol default at the bottom of this function.\n\tif (isClaudeCodeVersionTooOldMessage(text)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\t// Quota/billing exhaustion is checked BEFORE the generic 401/403 auth\n\t// patterns: \"403 ... usage limit for this billing cycle\" is transient per\n\t// cycle and must fail over, not terminate the turn as an auth error.\n\tif (isQuotaExhaustionMessage(text) || /rate.?limit|too many requests|429/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|unauthori[sz]ed|forbidden|invalid.?api.?key|no api key|401|403|\\/login/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\t// Gateway/upstream 5xx and dropped streams are transport failures, not\n\t// transcript-shape problems. Classify as network so the guidance points to\n\t// retry/model-switch instead of the protocol sanitize path.\n\tif (isUpstreamUnavailableMessage(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\t// Kimi/K3 + OpenAI-compat: orphan tool results after dropped error assistants.\n\t// Sanitize-and-retry (transform-messages drops orphans), not a hard tool fatal.\n\tif (/tool_call_id\\s+is\\s+not\\s+found|tool_call_id\\s+not\\s+found|unknown\\s+tool_call_id/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"protocol\" };\n\t}\n\tif (/tool.+timed? out|tool.+timeout/i.test(text)) return { area: \"tool\", code: \"timeout\" };\n\tif (/\\btool\\b/i.test(text) && !/tool_call_id/i.test(text)) return { area: \"tool\", code: \"fatal\" };\n\tif (/network|fetch failed|connection|socket|websocket|timed? out|timeout|dns|econn|^terminated$/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\treturn { area: \"provider\", code: \"protocol\" };\n}\n\n/**\n * Classify a preflight rejection raised before the provider was called.\n * `hasModel` is false when no model is selected for the session.\n */\nexport function preflightFailureCause(message: string, hasModel: boolean): SessionTerminationCause {\n\tif (!hasModel || /no model|model selected|model is required/i.test(message)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\tif (isQuotaExhaustionMessage(message)) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|api key|unauthori[sz]ed|forbidden|401|403|\\/login/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\tif (isUpstreamUnavailableMessage(message)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\tif (/context.+overflow|context window|too many tokens/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\tif (/duplicate.?result/i.test(message)) return { area: \"transcript\", code: \"duplicate_result\" };\n\tif (/orphan.?result/i.test(message)) return { area: \"transcript\", code: \"orphan_result\" };\n\tif (/duplicate.?call/i.test(message)) return { area: \"transcript\", code: \"duplicate_call_id\" };\n\tif (/transcript|missing.?result/i.test(message)) return { area: \"transcript\", code: \"missing_result\" };\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\tif (/fsync/i.test(message)) return { area: \"persistence\", code: \"fsync_failed\" };\n\tif (/lock/i.test(message)) return { area: \"persistence\", code: \"lock_failed\" };\n\tif (/append|persist|write/i.test(message)) return { area: \"persistence\", code: \"append_failed\" };\n\tif (/tool/i.test(message)) return { area: \"tool\", code: \"fatal\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n\n/** Classify a value thrown out of the session runtime itself. */\nexport function runtimeFailureCause(error: unknown): SessionTerminationCause {\n\tconst code = typeof error === \"object\" && error !== null && \"code\" in error ? Reflect.get(error, \"code\") : undefined;\n\tif (typeof code === \"string\" && PERSISTENCE_ERRNO_CODES.has(code)) {\n\t\treturn { area: \"persistence\", code: \"append_failed\" };\n\t}\n\tconst message = error instanceof Error ? error.message : String(error);\n\tif (/compaction.+stale|session changed during compaction/i.test(message)) {\n\t\treturn { area: \"compaction\", code: \"stale\" };\n\t}\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n"]}
1
+ {"version":3,"file":"session-failure-cause.d.ts","sourceRoot":"","sources":["../../src/core/session-failure-cause.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,QAAQ,CAAC;AASlE,OAAO,EAA0C,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAmBhH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKtF;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,uBAAuB,CAuD9G;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,uBAAuB,CA0BjG;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAa3E","sourcesContent":["/**\n * Failure-text to `SessionTerminationCause` classification.\n *\n * This is the layer *below* `session-termination.ts`: that module turns a cause\n * into a kind/phase/next-action record, while this one decides which cause a\n * provider error, a preflight rejection, or a thrown runtime error represents.\n *\n * The functions are pure so the ordering can be pinned by direct tests. Ordering\n * is load-bearing in two places that are easy to regress:\n *\n * - quota/billing exhaustion is matched *before* the generic 401/403 auth\n * patterns, because `403 ... usage limit for this billing cycle` is\n * transient per cycle and must fail over instead of terminating as auth;\n * - upstream 5xx/dropped streams are matched *before* the protocol fallback,\n * so guidance points at retry/model-switch rather than transcript sanitize.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"omk-ai\";\nimport {\n\tisClaudeCodeVersionTooOldMessage,\n\tisCodexChatgptAccountUnsupportedModelMessage,\n\tisQuotaExhaustionMessage,\n\tisUpstreamUnavailableMessage,\n} from \"./provider-resilience.ts\";\nimport { redactSensitiveText } from \"./redaction.ts\";\nimport { RunBudgetExceededError, RunBudgetPolicyError } from \"./run-budget-policy.ts\";\nimport { MAX_SESSION_TERMINATION_MESSAGE_LENGTH, type SessionTerminationCause } from \"./session-termination.ts\";\n\n/**\n * Errno codes that mean a write could not be durably completed. Anything else\n * (a missing path, for example) is not evidence of a persistence fault.\n */\nconst PERSISTENCE_ERRNO_CODES: ReadonlySet<string> = new Set([\n\t\"EACCES\",\n\t\"EDQUOT\",\n\t\"EFBIG\",\n\t\"EIO\",\n\t\"EISDIR\",\n\t\"EMFILE\",\n\t\"ENFILE\",\n\t\"ENOSPC\",\n\t\"EPERM\",\n\t\"EROFS\",\n]);\n\n/**\n * Normalize a free-text failure message for the termination record: redact\n * secret-shaped content, drop NUL bytes, and bound the length.\n */\nexport function terminationMessage(value: string | undefined, fallback: string): string {\n\tconst redacted = redactSensitiveText(value?.trim() || fallback)\n\t\t.replace(/\\0/g, \"\")\n\t\t.slice(0, MAX_SESSION_TERMINATION_MESSAGE_LENGTH);\n\treturn redacted || fallback;\n}\n\n/** Classify a failed provider turn from the assistant message it produced. */\nexport function providerFailureCause(message: AssistantMessage, contextWindow: number): SessionTerminationCause {\n\tconst text = message.errorMessage ?? \"\";\n\tif (isContextOverflow(message, contextWindow)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\t// Fable/Claude often emit stop_reason=refusal on benign turns (false positive).\n\t// Match broadly so we never mislabel these as provider.protocol.\n\tif (\n\t\t/stop_reason\\s*=\\s*(refusal|sensitive)|content\\/safety stop|safety stop|provider\\.refusal|kind=provider_refusal/i.test(\n\t\t\ttext,\n\t\t)\n\t) {\n\t\treturn { area: \"provider\", code: \"refusal\" };\n\t}\n\t// A stale spoofed Claude Code version against a newer model's gate is a client\n\t// configuration fault, not a transcript-shape one: it must not inherit the\n\t// retryable protocol default at the bottom of this function.\n\tif (isClaudeCodeVersionTooOldMessage(text) || isCodexChatgptAccountUnsupportedModelMessage(text)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\t// Quota/billing exhaustion is checked BEFORE the generic 401/403 auth\n\t// patterns: \"403 ... usage limit for this billing cycle\" is transient per\n\t// cycle and must fail over, not terminate the turn as an auth error.\n\t// Provider-capacity rejections are matched here too: they are transient waits,\n\t// not transcript faults, but their bodies carry no status or limit token, so\n\t// without these they reach the protocol fallback at the bottom and advertise\n\t// the orphan tool_call_id sanitize path. Observed 2026-09-03:\n\t// xai/grok-4.6 \"currently at capacity\" (HTTP 429) and\n\t// anthropic/claude-opus-5 `overloaded_error` / \"Overloaded\" (HTTP 529).\n\tif (\n\t\tisQuotaExhaustionMessage(text) ||\n\t\t/rate.?limit|too many requests|429|at capacity|high demand|overloaded/i.test(text)\n\t) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|unauthori[sz]ed|forbidden|invalid.?api.?key|no api key|401|403|\\/login/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\t// Gateway/upstream 5xx and dropped streams are transport failures, not\n\t// transcript-shape problems. Classify as network so the guidance points to\n\t// retry/model-switch instead of the protocol sanitize path.\n\tif (isUpstreamUnavailableMessage(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\t// Kimi/K3 + OpenAI-compat: orphan tool results after dropped error assistants.\n\t// Sanitize-and-retry (transform-messages drops orphans), not a hard tool fatal.\n\tif (/tool_call_id\\s+is\\s+not\\s+found|tool_call_id\\s+not\\s+found|unknown\\s+tool_call_id/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"protocol\" };\n\t}\n\tif (/tool.+timed? out|tool.+timeout/i.test(text)) return { area: \"tool\", code: \"timeout\" };\n\tif (/\\btool\\b/i.test(text) && !/tool_call_id/i.test(text)) return { area: \"tool\", code: \"fatal\" };\n\tif (/network|fetch failed|connection|socket|websocket|timed? out|timeout|dns|econn|^terminated$/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\treturn { area: \"provider\", code: \"protocol\" };\n}\n\n/**\n * Classify a preflight rejection raised before the provider was called.\n * `hasModel` is false when no model is selected for the session.\n */\nexport function preflightFailureCause(message: string, hasModel: boolean): SessionTerminationCause {\n\tif (!hasModel || /no model|model selected|model is required/i.test(message)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\tif (isQuotaExhaustionMessage(message)) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|api key|unauthori[sz]ed|forbidden|401|403|\\/login/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\tif (isUpstreamUnavailableMessage(message)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\tif (/context.+overflow|context window|too many tokens/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\tif (/duplicate.?result/i.test(message)) return { area: \"transcript\", code: \"duplicate_result\" };\n\tif (/orphan.?result/i.test(message)) return { area: \"transcript\", code: \"orphan_result\" };\n\tif (/duplicate.?call/i.test(message)) return { area: \"transcript\", code: \"duplicate_call_id\" };\n\tif (/transcript|missing.?result/i.test(message)) return { area: \"transcript\", code: \"missing_result\" };\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\tif (/fsync/i.test(message)) return { area: \"persistence\", code: \"fsync_failed\" };\n\tif (/lock/i.test(message)) return { area: \"persistence\", code: \"lock_failed\" };\n\tif (/append|persist|write/i.test(message)) return { area: \"persistence\", code: \"append_failed\" };\n\tif (/tool/i.test(message)) return { area: \"tool\", code: \"fatal\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n\n/** Classify a value thrown out of the session runtime itself. */\nexport function runtimeFailureCause(error: unknown): SessionTerminationCause {\n\tif (error instanceof RunBudgetExceededError) return { area: \"budget\", code: error.code };\n\tif (error instanceof RunBudgetPolicyError) return { area: \"configuration\", code: \"invalid\" };\n\tconst code = typeof error === \"object\" && error !== null && \"code\" in error ? Reflect.get(error, \"code\") : undefined;\n\tif (typeof code === \"string\" && PERSISTENCE_ERRNO_CODES.has(code)) {\n\t\treturn { area: \"persistence\", code: \"append_failed\" };\n\t}\n\tconst message = error instanceof Error ? error.message : String(error);\n\tif (/compaction.+stale|session changed during compaction/i.test(message)) {\n\t\treturn { area: \"compaction\", code: \"stale\" };\n\t}\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n"]}
@@ -15,8 +15,9 @@
15
15
  * so guidance points at retry/model-switch rather than transcript sanitize.
16
16
  */
17
17
  import { isContextOverflow } from "omk-ai";
18
- import { isClaudeCodeVersionTooOldMessage, isQuotaExhaustionMessage, isUpstreamUnavailableMessage, } from "./provider-resilience.js";
18
+ import { isClaudeCodeVersionTooOldMessage, isCodexChatgptAccountUnsupportedModelMessage, isQuotaExhaustionMessage, isUpstreamUnavailableMessage, } from "./provider-resilience.js";
19
19
  import { redactSensitiveText } from "./redaction.js";
20
+ import { RunBudgetExceededError, RunBudgetPolicyError } from "./run-budget-policy.js";
20
21
  import { MAX_SESSION_TERMINATION_MESSAGE_LENGTH } from "./session-termination.js";
21
22
  /**
22
23
  * Errno codes that mean a write could not be durably completed. Anything else
@@ -58,13 +59,20 @@ export function providerFailureCause(message, contextWindow) {
58
59
  // A stale spoofed Claude Code version against a newer model's gate is a client
59
60
  // configuration fault, not a transcript-shape one: it must not inherit the
60
61
  // retryable protocol default at the bottom of this function.
61
- if (isClaudeCodeVersionTooOldMessage(text)) {
62
+ if (isClaudeCodeVersionTooOldMessage(text) || isCodexChatgptAccountUnsupportedModelMessage(text)) {
62
63
  return { area: "configuration", code: "invalid" };
63
64
  }
64
65
  // Quota/billing exhaustion is checked BEFORE the generic 401/403 auth
65
66
  // patterns: "403 ... usage limit for this billing cycle" is transient per
66
67
  // cycle and must fail over, not terminate the turn as an auth error.
67
- if (isQuotaExhaustionMessage(text) || /rate.?limit|too many requests|429/i.test(text)) {
68
+ // Provider-capacity rejections are matched here too: they are transient waits,
69
+ // not transcript faults, but their bodies carry no status or limit token, so
70
+ // without these they reach the protocol fallback at the bottom and advertise
71
+ // the orphan tool_call_id sanitize path. Observed 2026-09-03:
72
+ // xai/grok-4.6 "currently at capacity" (HTTP 429) and
73
+ // anthropic/claude-opus-5 `overloaded_error` / "Overloaded" (HTTP 529).
74
+ if (isQuotaExhaustionMessage(text) ||
75
+ /rate.?limit|too many requests|429|at capacity|high demand|overloaded/i.test(text)) {
68
76
  return { area: "provider", code: "rate_limit" };
69
77
  }
70
78
  if (/auth|unauthori[sz]ed|forbidden|invalid.?api.?key|no api key|401|403|\/login/i.test(text)) {
@@ -132,6 +140,10 @@ export function preflightFailureCause(message, hasModel) {
132
140
  }
133
141
  /** Classify a value thrown out of the session runtime itself. */
134
142
  export function runtimeFailureCause(error) {
143
+ if (error instanceof RunBudgetExceededError)
144
+ return { area: "budget", code: error.code };
145
+ if (error instanceof RunBudgetPolicyError)
146
+ return { area: "configuration", code: "invalid" };
135
147
  const code = typeof error === "object" && error !== null && "code" in error ? Reflect.get(error, "code") : undefined;
136
148
  if (typeof code === "string" && PERSISTENCE_ERRNO_CODES.has(code)) {
137
149
  return { area: "persistence", code: "append_failed" };
@@ -1 +1 @@
1
- {"version":3,"file":"session-failure-cause.js","sourceRoot":"","sources":["../../src/core/session-failure-cause.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Failure-text to `SessionTerminationCause` classification.\n *\n * This is the layer *below* `session-termination.ts`: that module turns a cause\n * into a kind/phase/next-action record, while this one decides which cause a\n * provider error, a preflight rejection, or a thrown runtime error represents.\n *\n * The functions are pure so the ordering can be pinned by direct tests. Ordering\n * is load-bearing in two places that are easy to regress:\n *\n * - quota/billing exhaustion is matched *before* the generic 401/403 auth\n * patterns, because `403 ... usage limit for this billing cycle` is\n * transient per cycle and must fail over instead of terminating as auth;\n * - upstream 5xx/dropped streams are matched *before* the protocol fallback,\n * so guidance points at retry/model-switch rather than transcript sanitize.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"omk-ai\";\nimport {\n\tisClaudeCodeVersionTooOldMessage,\n\tisQuotaExhaustionMessage,\n\tisUpstreamUnavailableMessage,\n} from \"./provider-resilience.ts\";\nimport { redactSensitiveText } from \"./redaction.ts\";\nimport { MAX_SESSION_TERMINATION_MESSAGE_LENGTH, type SessionTerminationCause } from \"./session-termination.ts\";\n\n/**\n * Errno codes that mean a write could not be durably completed. Anything else\n * (a missing path, for example) is not evidence of a persistence fault.\n */\nconst PERSISTENCE_ERRNO_CODES: ReadonlySet<string> = new Set([\n\t\"EACCES\",\n\t\"EDQUOT\",\n\t\"EFBIG\",\n\t\"EIO\",\n\t\"EISDIR\",\n\t\"EMFILE\",\n\t\"ENFILE\",\n\t\"ENOSPC\",\n\t\"EPERM\",\n\t\"EROFS\",\n]);\n\n/**\n * Normalize a free-text failure message for the termination record: redact\n * secret-shaped content, drop NUL bytes, and bound the length.\n */\nexport function terminationMessage(value: string | undefined, fallback: string): string {\n\tconst redacted = redactSensitiveText(value?.trim() || fallback)\n\t\t.replace(/\\0/g, \"\")\n\t\t.slice(0, MAX_SESSION_TERMINATION_MESSAGE_LENGTH);\n\treturn redacted || fallback;\n}\n\n/** Classify a failed provider turn from the assistant message it produced. */\nexport function providerFailureCause(message: AssistantMessage, contextWindow: number): SessionTerminationCause {\n\tconst text = message.errorMessage ?? \"\";\n\tif (isContextOverflow(message, contextWindow)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\t// Fable/Claude often emit stop_reason=refusal on benign turns (false positive).\n\t// Match broadly so we never mislabel these as provider.protocol.\n\tif (\n\t\t/stop_reason\\s*=\\s*(refusal|sensitive)|content\\/safety stop|safety stop|provider\\.refusal|kind=provider_refusal/i.test(\n\t\t\ttext,\n\t\t)\n\t) {\n\t\treturn { area: \"provider\", code: \"refusal\" };\n\t}\n\t// A stale spoofed Claude Code version against a newer model's gate is a client\n\t// configuration fault, not a transcript-shape one: it must not inherit the\n\t// retryable protocol default at the bottom of this function.\n\tif (isClaudeCodeVersionTooOldMessage(text)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\t// Quota/billing exhaustion is checked BEFORE the generic 401/403 auth\n\t// patterns: \"403 ... usage limit for this billing cycle\" is transient per\n\t// cycle and must fail over, not terminate the turn as an auth error.\n\tif (isQuotaExhaustionMessage(text) || /rate.?limit|too many requests|429/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|unauthori[sz]ed|forbidden|invalid.?api.?key|no api key|401|403|\\/login/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\t// Gateway/upstream 5xx and dropped streams are transport failures, not\n\t// transcript-shape problems. Classify as network so the guidance points to\n\t// retry/model-switch instead of the protocol sanitize path.\n\tif (isUpstreamUnavailableMessage(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\t// Kimi/K3 + OpenAI-compat: orphan tool results after dropped error assistants.\n\t// Sanitize-and-retry (transform-messages drops orphans), not a hard tool fatal.\n\tif (/tool_call_id\\s+is\\s+not\\s+found|tool_call_id\\s+not\\s+found|unknown\\s+tool_call_id/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"protocol\" };\n\t}\n\tif (/tool.+timed? out|tool.+timeout/i.test(text)) return { area: \"tool\", code: \"timeout\" };\n\tif (/\\btool\\b/i.test(text) && !/tool_call_id/i.test(text)) return { area: \"tool\", code: \"fatal\" };\n\tif (/network|fetch failed|connection|socket|websocket|timed? out|timeout|dns|econn|^terminated$/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\treturn { area: \"provider\", code: \"protocol\" };\n}\n\n/**\n * Classify a preflight rejection raised before the provider was called.\n * `hasModel` is false when no model is selected for the session.\n */\nexport function preflightFailureCause(message: string, hasModel: boolean): SessionTerminationCause {\n\tif (!hasModel || /no model|model selected|model is required/i.test(message)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\tif (isQuotaExhaustionMessage(message)) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|api key|unauthori[sz]ed|forbidden|401|403|\\/login/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\tif (isUpstreamUnavailableMessage(message)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\tif (/context.+overflow|context window|too many tokens/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\tif (/duplicate.?result/i.test(message)) return { area: \"transcript\", code: \"duplicate_result\" };\n\tif (/orphan.?result/i.test(message)) return { area: \"transcript\", code: \"orphan_result\" };\n\tif (/duplicate.?call/i.test(message)) return { area: \"transcript\", code: \"duplicate_call_id\" };\n\tif (/transcript|missing.?result/i.test(message)) return { area: \"transcript\", code: \"missing_result\" };\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\tif (/fsync/i.test(message)) return { area: \"persistence\", code: \"fsync_failed\" };\n\tif (/lock/i.test(message)) return { area: \"persistence\", code: \"lock_failed\" };\n\tif (/append|persist|write/i.test(message)) return { area: \"persistence\", code: \"append_failed\" };\n\tif (/tool/i.test(message)) return { area: \"tool\", code: \"fatal\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n\n/** Classify a value thrown out of the session runtime itself. */\nexport function runtimeFailureCause(error: unknown): SessionTerminationCause {\n\tconst code = typeof error === \"object\" && error !== null && \"code\" in error ? Reflect.get(error, \"code\") : undefined;\n\tif (typeof code === \"string\" && PERSISTENCE_ERRNO_CODES.has(code)) {\n\t\treturn { area: \"persistence\", code: \"append_failed\" };\n\t}\n\tconst message = error instanceof Error ? error.message : String(error);\n\tif (/compaction.+stale|session changed during compaction/i.test(message)) {\n\t\treturn { area: \"compaction\", code: \"stale\" };\n\t}\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n"]}
1
+ {"version":3,"file":"session-failure-cause.js","sourceRoot":"","sources":["../../src/core/session-failure-cause.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Failure-text to `SessionTerminationCause` classification.\n *\n * This is the layer *below* `session-termination.ts`: that module turns a cause\n * into a kind/phase/next-action record, while this one decides which cause a\n * provider error, a preflight rejection, or a thrown runtime error represents.\n *\n * The functions are pure so the ordering can be pinned by direct tests. Ordering\n * is load-bearing in two places that are easy to regress:\n *\n * - quota/billing exhaustion is matched *before* the generic 401/403 auth\n * patterns, because `403 ... usage limit for this billing cycle` is\n * transient per cycle and must fail over instead of terminating as auth;\n * - upstream 5xx/dropped streams are matched *before* the protocol fallback,\n * so guidance points at retry/model-switch rather than transcript sanitize.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"omk-ai\";\nimport {\n\tisClaudeCodeVersionTooOldMessage,\n\tisCodexChatgptAccountUnsupportedModelMessage,\n\tisQuotaExhaustionMessage,\n\tisUpstreamUnavailableMessage,\n} from \"./provider-resilience.ts\";\nimport { redactSensitiveText } from \"./redaction.ts\";\nimport { RunBudgetExceededError, RunBudgetPolicyError } from \"./run-budget-policy.ts\";\nimport { MAX_SESSION_TERMINATION_MESSAGE_LENGTH, type SessionTerminationCause } from \"./session-termination.ts\";\n\n/**\n * Errno codes that mean a write could not be durably completed. Anything else\n * (a missing path, for example) is not evidence of a persistence fault.\n */\nconst PERSISTENCE_ERRNO_CODES: ReadonlySet<string> = new Set([\n\t\"EACCES\",\n\t\"EDQUOT\",\n\t\"EFBIG\",\n\t\"EIO\",\n\t\"EISDIR\",\n\t\"EMFILE\",\n\t\"ENFILE\",\n\t\"ENOSPC\",\n\t\"EPERM\",\n\t\"EROFS\",\n]);\n\n/**\n * Normalize a free-text failure message for the termination record: redact\n * secret-shaped content, drop NUL bytes, and bound the length.\n */\nexport function terminationMessage(value: string | undefined, fallback: string): string {\n\tconst redacted = redactSensitiveText(value?.trim() || fallback)\n\t\t.replace(/\\0/g, \"\")\n\t\t.slice(0, MAX_SESSION_TERMINATION_MESSAGE_LENGTH);\n\treturn redacted || fallback;\n}\n\n/** Classify a failed provider turn from the assistant message it produced. */\nexport function providerFailureCause(message: AssistantMessage, contextWindow: number): SessionTerminationCause {\n\tconst text = message.errorMessage ?? \"\";\n\tif (isContextOverflow(message, contextWindow)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\t// Fable/Claude often emit stop_reason=refusal on benign turns (false positive).\n\t// Match broadly so we never mislabel these as provider.protocol.\n\tif (\n\t\t/stop_reason\\s*=\\s*(refusal|sensitive)|content\\/safety stop|safety stop|provider\\.refusal|kind=provider_refusal/i.test(\n\t\t\ttext,\n\t\t)\n\t) {\n\t\treturn { area: \"provider\", code: \"refusal\" };\n\t}\n\t// A stale spoofed Claude Code version against a newer model's gate is a client\n\t// configuration fault, not a transcript-shape one: it must not inherit the\n\t// retryable protocol default at the bottom of this function.\n\tif (isClaudeCodeVersionTooOldMessage(text) || isCodexChatgptAccountUnsupportedModelMessage(text)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\t// Quota/billing exhaustion is checked BEFORE the generic 401/403 auth\n\t// patterns: \"403 ... usage limit for this billing cycle\" is transient per\n\t// cycle and must fail over, not terminate the turn as an auth error.\n\t// Provider-capacity rejections are matched here too: they are transient waits,\n\t// not transcript faults, but their bodies carry no status or limit token, so\n\t// without these they reach the protocol fallback at the bottom and advertise\n\t// the orphan tool_call_id sanitize path. Observed 2026-09-03:\n\t// xai/grok-4.6 \"currently at capacity\" (HTTP 429) and\n\t// anthropic/claude-opus-5 `overloaded_error` / \"Overloaded\" (HTTP 529).\n\tif (\n\t\tisQuotaExhaustionMessage(text) ||\n\t\t/rate.?limit|too many requests|429|at capacity|high demand|overloaded/i.test(text)\n\t) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|unauthori[sz]ed|forbidden|invalid.?api.?key|no api key|401|403|\\/login/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\t// Gateway/upstream 5xx and dropped streams are transport failures, not\n\t// transcript-shape problems. Classify as network so the guidance points to\n\t// retry/model-switch instead of the protocol sanitize path.\n\tif (isUpstreamUnavailableMessage(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\t// Kimi/K3 + OpenAI-compat: orphan tool results after dropped error assistants.\n\t// Sanitize-and-retry (transform-messages drops orphans), not a hard tool fatal.\n\tif (/tool_call_id\\s+is\\s+not\\s+found|tool_call_id\\s+not\\s+found|unknown\\s+tool_call_id/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"protocol\" };\n\t}\n\tif (/tool.+timed? out|tool.+timeout/i.test(text)) return { area: \"tool\", code: \"timeout\" };\n\tif (/\\btool\\b/i.test(text) && !/tool_call_id/i.test(text)) return { area: \"tool\", code: \"fatal\" };\n\tif (/network|fetch failed|connection|socket|websocket|timed? out|timeout|dns|econn|^terminated$/i.test(text)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\treturn { area: \"provider\", code: \"protocol\" };\n}\n\n/**\n * Classify a preflight rejection raised before the provider was called.\n * `hasModel` is false when no model is selected for the session.\n */\nexport function preflightFailureCause(message: string, hasModel: boolean): SessionTerminationCause {\n\tif (!hasModel || /no model|model selected|model is required/i.test(message)) {\n\t\treturn { area: \"configuration\", code: \"invalid\" };\n\t}\n\tif (isQuotaExhaustionMessage(message)) {\n\t\treturn { area: \"provider\", code: \"rate_limit\" };\n\t}\n\tif (/auth|api key|unauthori[sz]ed|forbidden|401|403|\\/login/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"auth\" };\n\t}\n\tif (isUpstreamUnavailableMessage(message)) {\n\t\treturn { area: \"provider\", code: \"network\" };\n\t}\n\tif (/context.+overflow|context window|too many tokens/i.test(message)) {\n\t\treturn { area: \"provider\", code: \"context_overflow\" };\n\t}\n\tif (/duplicate.?result/i.test(message)) return { area: \"transcript\", code: \"duplicate_result\" };\n\tif (/orphan.?result/i.test(message)) return { area: \"transcript\", code: \"orphan_result\" };\n\tif (/duplicate.?call/i.test(message)) return { area: \"transcript\", code: \"duplicate_call_id\" };\n\tif (/transcript|missing.?result/i.test(message)) return { area: \"transcript\", code: \"missing_result\" };\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\tif (/fsync/i.test(message)) return { area: \"persistence\", code: \"fsync_failed\" };\n\tif (/lock/i.test(message)) return { area: \"persistence\", code: \"lock_failed\" };\n\tif (/append|persist|write/i.test(message)) return { area: \"persistence\", code: \"append_failed\" };\n\tif (/tool/i.test(message)) return { area: \"tool\", code: \"fatal\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n\n/** Classify a value thrown out of the session runtime itself. */\nexport function runtimeFailureCause(error: unknown): SessionTerminationCause {\n\tif (error instanceof RunBudgetExceededError) return { area: \"budget\", code: error.code };\n\tif (error instanceof RunBudgetPolicyError) return { area: \"configuration\", code: \"invalid\" };\n\tconst code = typeof error === \"object\" && error !== null && \"code\" in error ? Reflect.get(error, \"code\") : undefined;\n\tif (typeof code === \"string\" && PERSISTENCE_ERRNO_CODES.has(code)) {\n\t\treturn { area: \"persistence\", code: \"append_failed\" };\n\t}\n\tconst message = error instanceof Error ? error.message : String(error);\n\tif (/compaction.+stale|session changed during compaction/i.test(message)) {\n\t\treturn { area: \"compaction\", code: \"stale\" };\n\t}\n\tif (/compaction/i.test(message)) return { area: \"compaction\", code: \"failed\" };\n\treturn { area: \"internal\", code: \"unclassified\" };\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import type { AgentTool } from "omk-agent-core";
2
+ import { type PromptSettledEvent, type PromptSettlementOutcome } from "./prompt-settlement.ts";
3
+ export declare class PromptExecutionBusyError extends Error {
4
+ constructor();
5
+ }
6
+ /** Session-local ownership, not a durable journal or a detached-process sandbox. */
7
+ export declare class SessionPromptLifecycle {
8
+ private owner;
9
+ private disposed;
10
+ private readonly canSettle;
11
+ private readonly auditsLateSettlement;
12
+ constructor(options?: {
13
+ canSettle?: () => boolean;
14
+ auditsLateSettlement?: () => boolean;
15
+ });
16
+ assertIdle(): void;
17
+ begin(promptRunId: string): {
18
+ finish: (outcome: PromptSettlementOutcome, notify: (event: PromptSettledEvent) => void) => void;
19
+ };
20
+ wrapTool(tool: AgentTool): AgentTool;
21
+ /** Called after the root producer drains, or after late-settlement event delivery. */
22
+ flush(): void;
23
+ dispose(): void;
24
+ }
25
+ //# sourceMappingURL=session-prompt-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-prompt-lifecycle.d.ts","sourceRoot":"","sources":["../../src/core/session-prompt-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAEN,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAG5B,MAAM,wBAAwB,CAAC;AAShC,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,cAGC;CACD;AAED,oFAAoF;AACpF,qBAAa,sBAAsB;IAClC,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAgB;IAErD,YAAY,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;QAAC,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAA;KAAO,EAG5F;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG;QAC3B,MAAM,EAAE,CAAC,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,KAAK,IAAI,CAAC;KAChG,CAWA;IAED,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CA6BnC;IAED,sFAAsF;IACtF,KAAK,IAAI,IAAI,CAUZ;IAED,OAAO,IAAI,IAAI,CAGd;CACD","sourcesContent":["import type { AgentTool } from \"omk-agent-core\";\nimport {\n\tcreatePromptSettlementState,\n\ttype PromptSettledEvent,\n\ttype PromptSettlementOutcome,\n\treducePromptSettlement,\n\tsettlePromptIfReady,\n} from \"./prompt-settlement.ts\";\n\ninterface PromptOwner {\n\treadonly promptRunId: string;\n\treadonly startedAt: number;\n\treadonly executions: Set<symbol>;\n\tterminal?: { readonly outcome: PromptSettlementOutcome; readonly notify: (event: PromptSettledEvent) => void };\n}\n\nexport class PromptExecutionBusyError extends Error {\n\tconstructor() {\n\t\tsuper(\"Agent is already processing or has unsettled tool execution. Wait for actual termination.\");\n\t\tthis.name = \"PromptExecutionBusyError\";\n\t}\n}\n\n/** Session-local ownership, not a durable journal or a detached-process sandbox. */\nexport class SessionPromptLifecycle {\n\tprivate owner: PromptOwner | undefined;\n\tprivate disposed = false;\n\tprivate readonly canSettle: () => boolean;\n\tprivate readonly auditsLateSettlement: () => boolean;\n\n\tconstructor(options: { canSettle?: () => boolean; auditsLateSettlement?: () => boolean } = {}) {\n\t\tthis.canSettle = options.canSettle ?? (() => true);\n\t\tthis.auditsLateSettlement = options.auditsLateSettlement ?? (() => true);\n\t}\n\n\tassertIdle(): void {\n\t\tif (this.owner !== undefined || this.disposed) throw new PromptExecutionBusyError();\n\t}\n\n\tbegin(promptRunId: string): {\n\t\tfinish: (outcome: PromptSettlementOutcome, notify: (event: PromptSettledEvent) => void) => void;\n\t} {\n\t\tthis.assertIdle();\n\t\tconst owner: PromptOwner = { promptRunId, startedAt: performance.now(), executions: new Set() };\n\t\tthis.owner = owner;\n\t\treturn {\n\t\t\tfinish: (outcome, notify) => {\n\t\t\t\tif (owner.terminal !== undefined) return;\n\t\t\t\towner.terminal = { outcome, notify };\n\t\t\t\tthis.flush();\n\t\t\t},\n\t\t};\n\t}\n\n\twrapTool(tool: AgentTool): AgentTool {\n\t\treturn {\n\t\t\tname: tool.name,\n\t\t\tlabel: tool.label,\n\t\t\tdescription: tool.description,\n\t\t\tparameters: tool.parameters,\n\t\t\tprepareArguments: tool.prepareArguments,\n\t\t\texecutionMode: tool.executionMode,\n\t\t\tresourceClaims: tool.resourceClaims,\n\t\t\t// A spread would evaluate and freeze the context-sensitive timeout too early.\n\t\t\tget timeoutMs() {\n\t\t\t\treturn tool.timeoutMs;\n\t\t\t},\n\t\t\texecute: async (...args) => {\n\t\t\t\tconst owner = this.owner;\n\t\t\t\tif (this.disposed || owner?.terminal !== undefined) throw new PromptExecutionBusyError();\n\t\t\t\t// Runtime-owned identities cannot collide with reused model toolCallIds.\n\t\t\t\tconst execution = Symbol();\n\t\t\t\tconst requiresAudit = this.auditsLateSettlement();\n\t\t\t\towner?.executions.add(execution);\n\t\t\t\ttry {\n\t\t\t\t\treturn await tool.execute(...args);\n\t\t\t\t} finally {\n\t\t\t\t\towner?.executions.delete(execution);\n\t\t\t\t\t// Audit mode flushes only after the late event has been delivered.\n\t\t\t\t\tif (!requiresAudit) this.flush();\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\t/** Called after the root producer drains, or after late-settlement event delivery. */\n\tflush(): void {\n\t\tconst owner = this.owner;\n\t\tif (!owner?.terminal || owner.executions.size > 0 || this.disposed || !this.canSettle()) return;\n\t\tconst state = reducePromptSettlement(createPromptSettlementState(owner.promptRunId, owner.startedAt), {\n\t\t\tkind: \"terminal\",\n\t\t\toutcome: owner.terminal.outcome,\n\t\t});\n\t\tconst { event } = settlePromptIfReady(state, performance.now());\n\t\tthis.owner = undefined;\n\t\tif (event !== null) owner.terminal.notify(event);\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tthis.owner = undefined;\n\t}\n}\n"]}
@@ -0,0 +1,87 @@
1
+ import { createPromptSettlementState, reducePromptSettlement, settlePromptIfReady, } from "./prompt-settlement.js";
2
+ export class PromptExecutionBusyError extends Error {
3
+ constructor() {
4
+ super("Agent is already processing or has unsettled tool execution. Wait for actual termination.");
5
+ this.name = "PromptExecutionBusyError";
6
+ }
7
+ }
8
+ /** Session-local ownership, not a durable journal or a detached-process sandbox. */
9
+ export class SessionPromptLifecycle {
10
+ owner;
11
+ disposed = false;
12
+ canSettle;
13
+ auditsLateSettlement;
14
+ constructor(options = {}) {
15
+ this.canSettle = options.canSettle ?? (() => true);
16
+ this.auditsLateSettlement = options.auditsLateSettlement ?? (() => true);
17
+ }
18
+ assertIdle() {
19
+ if (this.owner !== undefined || this.disposed)
20
+ throw new PromptExecutionBusyError();
21
+ }
22
+ begin(promptRunId) {
23
+ this.assertIdle();
24
+ const owner = { promptRunId, startedAt: performance.now(), executions: new Set() };
25
+ this.owner = owner;
26
+ return {
27
+ finish: (outcome, notify) => {
28
+ if (owner.terminal !== undefined)
29
+ return;
30
+ owner.terminal = { outcome, notify };
31
+ this.flush();
32
+ },
33
+ };
34
+ }
35
+ wrapTool(tool) {
36
+ return {
37
+ name: tool.name,
38
+ label: tool.label,
39
+ description: tool.description,
40
+ parameters: tool.parameters,
41
+ prepareArguments: tool.prepareArguments,
42
+ executionMode: tool.executionMode,
43
+ resourceClaims: tool.resourceClaims,
44
+ // A spread would evaluate and freeze the context-sensitive timeout too early.
45
+ get timeoutMs() {
46
+ return tool.timeoutMs;
47
+ },
48
+ execute: async (...args) => {
49
+ const owner = this.owner;
50
+ if (this.disposed || owner?.terminal !== undefined)
51
+ throw new PromptExecutionBusyError();
52
+ // Runtime-owned identities cannot collide with reused model toolCallIds.
53
+ const execution = Symbol();
54
+ const requiresAudit = this.auditsLateSettlement();
55
+ owner?.executions.add(execution);
56
+ try {
57
+ return await tool.execute(...args);
58
+ }
59
+ finally {
60
+ owner?.executions.delete(execution);
61
+ // Audit mode flushes only after the late event has been delivered.
62
+ if (!requiresAudit)
63
+ this.flush();
64
+ }
65
+ },
66
+ };
67
+ }
68
+ /** Called after the root producer drains, or after late-settlement event delivery. */
69
+ flush() {
70
+ const owner = this.owner;
71
+ if (!owner?.terminal || owner.executions.size > 0 || this.disposed || !this.canSettle())
72
+ return;
73
+ const state = reducePromptSettlement(createPromptSettlementState(owner.promptRunId, owner.startedAt), {
74
+ kind: "terminal",
75
+ outcome: owner.terminal.outcome,
76
+ });
77
+ const { event } = settlePromptIfReady(state, performance.now());
78
+ this.owner = undefined;
79
+ if (event !== null)
80
+ owner.terminal.notify(event);
81
+ }
82
+ dispose() {
83
+ this.disposed = true;
84
+ this.owner = undefined;
85
+ }
86
+ }
87
+ //# sourceMappingURL=session-prompt-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-prompt-lifecycle.js","sourceRoot":"","sources":["../../src/core/session-prompt-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"omk-agent-core\";\nimport {\n\tcreatePromptSettlementState,\n\ttype PromptSettledEvent,\n\ttype PromptSettlementOutcome,\n\treducePromptSettlement,\n\tsettlePromptIfReady,\n} from \"./prompt-settlement.ts\";\n\ninterface PromptOwner {\n\treadonly promptRunId: string;\n\treadonly startedAt: number;\n\treadonly executions: Set<symbol>;\n\tterminal?: { readonly outcome: PromptSettlementOutcome; readonly notify: (event: PromptSettledEvent) => void };\n}\n\nexport class PromptExecutionBusyError extends Error {\n\tconstructor() {\n\t\tsuper(\"Agent is already processing or has unsettled tool execution. Wait for actual termination.\");\n\t\tthis.name = \"PromptExecutionBusyError\";\n\t}\n}\n\n/** Session-local ownership, not a durable journal or a detached-process sandbox. */\nexport class SessionPromptLifecycle {\n\tprivate owner: PromptOwner | undefined;\n\tprivate disposed = false;\n\tprivate readonly canSettle: () => boolean;\n\tprivate readonly auditsLateSettlement: () => boolean;\n\n\tconstructor(options: { canSettle?: () => boolean; auditsLateSettlement?: () => boolean } = {}) {\n\t\tthis.canSettle = options.canSettle ?? (() => true);\n\t\tthis.auditsLateSettlement = options.auditsLateSettlement ?? (() => true);\n\t}\n\n\tassertIdle(): void {\n\t\tif (this.owner !== undefined || this.disposed) throw new PromptExecutionBusyError();\n\t}\n\n\tbegin(promptRunId: string): {\n\t\tfinish: (outcome: PromptSettlementOutcome, notify: (event: PromptSettledEvent) => void) => void;\n\t} {\n\t\tthis.assertIdle();\n\t\tconst owner: PromptOwner = { promptRunId, startedAt: performance.now(), executions: new Set() };\n\t\tthis.owner = owner;\n\t\treturn {\n\t\t\tfinish: (outcome, notify) => {\n\t\t\t\tif (owner.terminal !== undefined) return;\n\t\t\t\towner.terminal = { outcome, notify };\n\t\t\t\tthis.flush();\n\t\t\t},\n\t\t};\n\t}\n\n\twrapTool(tool: AgentTool): AgentTool {\n\t\treturn {\n\t\t\tname: tool.name,\n\t\t\tlabel: tool.label,\n\t\t\tdescription: tool.description,\n\t\t\tparameters: tool.parameters,\n\t\t\tprepareArguments: tool.prepareArguments,\n\t\t\texecutionMode: tool.executionMode,\n\t\t\tresourceClaims: tool.resourceClaims,\n\t\t\t// A spread would evaluate and freeze the context-sensitive timeout too early.\n\t\t\tget timeoutMs() {\n\t\t\t\treturn tool.timeoutMs;\n\t\t\t},\n\t\t\texecute: async (...args) => {\n\t\t\t\tconst owner = this.owner;\n\t\t\t\tif (this.disposed || owner?.terminal !== undefined) throw new PromptExecutionBusyError();\n\t\t\t\t// Runtime-owned identities cannot collide with reused model toolCallIds.\n\t\t\t\tconst execution = Symbol();\n\t\t\t\tconst requiresAudit = this.auditsLateSettlement();\n\t\t\t\towner?.executions.add(execution);\n\t\t\t\ttry {\n\t\t\t\t\treturn await tool.execute(...args);\n\t\t\t\t} finally {\n\t\t\t\t\towner?.executions.delete(execution);\n\t\t\t\t\t// Audit mode flushes only after the late event has been delivered.\n\t\t\t\t\tif (!requiresAudit) this.flush();\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\t/** Called after the root producer drains, or after late-settlement event delivery. */\n\tflush(): void {\n\t\tconst owner = this.owner;\n\t\tif (!owner?.terminal || owner.executions.size > 0 || this.disposed || !this.canSettle()) return;\n\t\tconst state = reducePromptSettlement(createPromptSettlementState(owner.promptRunId, owner.startedAt), {\n\t\t\tkind: \"terminal\",\n\t\t\toutcome: owner.terminal.outcome,\n\t\t});\n\t\tconst { event } = settlePromptIfReady(state, performance.now());\n\t\tthis.owner = undefined;\n\t\tif (event !== null) owner.terminal.notify(event);\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tthis.owner = undefined;\n\t}\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { Agent, StreamFn } from "omk-agent-core";
2
+ import { RunBudget, type RunBudgetSnapshot } from "./run-budget.ts";
3
+ import { RunBudgetExceededError, type RunBudgetLimits, RunBudgetPolicyError } from "./run-budget-policy.ts";
4
+ interface RunBudgetLifecycle {
5
+ readonly assertIdle: () => void;
6
+ readonly stop: (error: RunBudgetExceededError) => void;
7
+ readonly reject: (error: RunBudgetExceededError | RunBudgetPolicyError) => void;
8
+ }
9
+ /** Installs one scoped stream boundary; restores it without overwriting a later owner. */
10
+ export declare class SessionRunBudget {
11
+ private readonly agent;
12
+ private readonly lifecycle;
13
+ private current;
14
+ private previous;
15
+ private executing;
16
+ private disposed;
17
+ constructor(agent: Agent, lifecycle: RunBudgetLifecycle);
18
+ get failure(): RunBudgetExceededError | undefined;
19
+ get remainingMs(): number | undefined;
20
+ snapshot(): RunBudgetSnapshot | undefined;
21
+ assertActive(): void;
22
+ assertAdmission(): void;
23
+ close(): void;
24
+ execute(limits: RunBudgetLimits | undefined, operation: () => Promise<void>, preflightResult?: (accepted: boolean) => void): Promise<void>;
25
+ }
26
+ export declare function wrapBudgetStream(source: StreamFn, budget: RunBudget): StreamFn;
27
+ export {};
28
+ //# sourceMappingURL=session-run-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-run-budget.d.ts","sourceRoot":"","sources":["../../src/core/session-run-budget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,KAAK,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG5G,UAAU,kBAAkB;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,sBAAsB,GAAG,oBAAoB,KAAK,IAAI,CAAC;CAChF;AAED,0FAA0F;AAC1F,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAGtD;IAED,IAAI,OAAO,IAAI,sBAAsB,GAAG,SAAS,CAEhD;IACD,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAEpC;IACD,QAAQ,IAAI,iBAAiB,GAAG,SAAS,CAExC;IACD,YAAY,IAAI,IAAI,CAEnB;IACD,eAAe,IAAI,IAAI,CAEtB;IACD,KAAK,IAAI,IAAI,CAGZ;IAEK,OAAO,CACZ,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC9B,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,GAC3C,OAAO,CAAC,IAAI,CAAC,CAmDf;CACD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,CA6B9E","sourcesContent":["import type { Agent, StreamFn } from \"omk-agent-core\";\nimport { RunBudget, type RunBudgetSnapshot } from \"./run-budget.ts\";\nimport { RunBudgetExceededError, type RunBudgetLimits, RunBudgetPolicyError } from \"./run-budget-policy.ts\";\nimport { PromptExecutionBusyError } from \"./session-prompt-lifecycle.ts\";\n\ninterface RunBudgetLifecycle {\n\treadonly assertIdle: () => void;\n\treadonly stop: (error: RunBudgetExceededError) => void;\n\treadonly reject: (error: RunBudgetExceededError | RunBudgetPolicyError) => void;\n}\n\n/** Installs one scoped stream boundary; restores it without overwriting a later owner. */\nexport class SessionRunBudget {\n\tprivate readonly agent: Agent;\n\tprivate readonly lifecycle: RunBudgetLifecycle;\n\tprivate current: RunBudget | undefined;\n\tprivate previous: RunBudget | undefined;\n\tprivate executing = false;\n\tprivate disposed = false;\n\n\tconstructor(agent: Agent, lifecycle: RunBudgetLifecycle) {\n\t\tthis.agent = agent;\n\t\tthis.lifecycle = lifecycle;\n\t}\n\n\tget failure(): RunBudgetExceededError | undefined {\n\t\treturn this.current?.failure;\n\t}\n\tget remainingMs(): number | undefined {\n\t\treturn this.current?.remainingMs;\n\t}\n\tsnapshot(): RunBudgetSnapshot | undefined {\n\t\treturn (this.current ?? this.previous)?.snapshot();\n\t}\n\tassertActive(): void {\n\t\tthis.current?.assertActive();\n\t}\n\tassertAdmission(): void {\n\t\tthis.current?.assertAdmission();\n\t}\n\tclose(): void {\n\t\tthis.disposed = true;\n\t\tthis.current?.close();\n\t}\n\n\tasync execute(\n\t\tlimits: RunBudgetLimits | undefined,\n\t\toperation: () => Promise<void>,\n\t\tpreflightResult?: (accepted: boolean) => void,\n\t): Promise<void> {\n\t\tlet ownsScope = false;\n\t\tlet entered = false;\n\t\tlet budget: RunBudget | undefined;\n\t\tconst source = this.agent.streamFn;\n\t\tconst sourceAuth = this.agent.getApiKey;\n\t\tlet wrapped: StreamFn | undefined;\n\t\tlet wrappedAuth: Agent[\"getApiKey\"];\n\t\ttry {\n\t\t\tif (this.executing || this.disposed || (this.previous?.snapshot().activeRequests ?? 0) > 0) {\n\t\t\t\tthrow new PromptExecutionBusyError();\n\t\t\t}\n\t\t\tthis.executing = true;\n\t\t\townsScope = true;\n\t\t\tif (limits !== undefined) {\n\t\t\t\tif (this.agent.state.isStreaming) throw new PromptExecutionBusyError();\n\t\t\t\tthis.lifecycle.assertIdle();\n\t\t\t\tconst scopedBudget = new RunBudget(limits, this.lifecycle.stop);\n\t\t\t\tbudget = scopedBudget;\n\t\t\t\tthis.current = scopedBudget;\n\t\t\t\twrapped = wrapBudgetStream(source, scopedBudget);\n\t\t\t\twrappedAuth = async (provider) => {\n\t\t\t\t\tscopedBudget.assertAdmission();\n\t\t\t\t\tconst key = await sourceAuth?.(provider);\n\t\t\t\t\tscopedBudget.assertActive();\n\t\t\t\t\treturn key;\n\t\t\t\t};\n\t\t\t\tthis.agent.streamFn = wrapped;\n\t\t\t\tthis.agent.getApiKey = wrappedAuth;\n\t\t\t\tscopedBudget.assertAdmission();\n\t\t\t}\n\t\t\tentered = true;\n\t\t\tawait operation();\n\t\t\tbudget?.assertActive();\n\t\t} catch (error) {\n\t\t\tif (!entered) {\n\t\t\t\tpreflightResult?.(false);\n\t\t\t\tif (error instanceof RunBudgetExceededError || error instanceof RunBudgetPolicyError)\n\t\t\t\t\tthis.lifecycle.reject(error);\n\t\t\t}\n\t\t\tthrow budget?.failure ?? error;\n\t\t} finally {\n\t\t\tif (budget) {\n\t\t\t\tbudget.close();\n\t\t\t\tthis.previous = budget;\n\t\t\t\tif (this.current === budget) this.current = undefined;\n\t\t\t}\n\t\t\tif (wrapped !== undefined && this.agent.streamFn === wrapped) this.agent.streamFn = source;\n\t\t\tif (wrappedAuth !== undefined && this.agent.getApiKey === wrappedAuth) this.agent.getApiKey = sourceAuth;\n\t\t\tif (ownsScope) this.executing = false;\n\t\t}\n\t}\n}\n\nexport function wrapBudgetStream(source: StreamFn, budget: RunBudget): StreamFn {\n\tconst wrapped: StreamFn = async (model, context, options) => {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst release = budget.admit();\n\t\tconst remainingMs = budget.remainingMs;\n\t\ttry {\n\t\t\tconst stream = await source(model, context, {\n\t\t\t\t...options,\n\t\t\t\tsignal: options?.signal ? AbortSignal.any([options.signal, budget.signal]) : budget.signal,\n\t\t\t\tmaxRetries: 0,\n\t\t\t\t...(remainingMs === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { timeoutMs: Math.min(options?.timeoutMs ?? remainingMs, remainingMs) }),\n\t\t\t});\n\t\t\t// A returned stream is not yet a completed request. Missing terminal metadata\n\t\t\t// retains the reservation; abort alone never refunds it.\n\t\t\tvoid stream.result().then(release, release);\n\t\t\treturn stream;\n\t\t} catch (error) {\n\t\t\trelease();\n\t\t\tthrow error;\n\t\t}\n\t};\n\t// Preserve source-owned identity brands used by mandatory credential checks.\n\tfor (const key of Object.getOwnPropertySymbols(source)) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(source, key);\n\t\tif (descriptor) Object.defineProperty(wrapped, key, descriptor);\n\t}\n\treturn wrapped;\n}\n"]}
@@ -0,0 +1,127 @@
1
+ import { RunBudget } from "./run-budget.js";
2
+ import { RunBudgetExceededError, RunBudgetPolicyError } from "./run-budget-policy.js";
3
+ import { PromptExecutionBusyError } from "./session-prompt-lifecycle.js";
4
+ /** Installs one scoped stream boundary; restores it without overwriting a later owner. */
5
+ export class SessionRunBudget {
6
+ agent;
7
+ lifecycle;
8
+ current;
9
+ previous;
10
+ executing = false;
11
+ disposed = false;
12
+ constructor(agent, lifecycle) {
13
+ this.agent = agent;
14
+ this.lifecycle = lifecycle;
15
+ }
16
+ get failure() {
17
+ return this.current?.failure;
18
+ }
19
+ get remainingMs() {
20
+ return this.current?.remainingMs;
21
+ }
22
+ snapshot() {
23
+ return (this.current ?? this.previous)?.snapshot();
24
+ }
25
+ assertActive() {
26
+ this.current?.assertActive();
27
+ }
28
+ assertAdmission() {
29
+ this.current?.assertAdmission();
30
+ }
31
+ close() {
32
+ this.disposed = true;
33
+ this.current?.close();
34
+ }
35
+ async execute(limits, operation, preflightResult) {
36
+ let ownsScope = false;
37
+ let entered = false;
38
+ let budget;
39
+ const source = this.agent.streamFn;
40
+ const sourceAuth = this.agent.getApiKey;
41
+ let wrapped;
42
+ let wrappedAuth;
43
+ try {
44
+ if (this.executing || this.disposed || (this.previous?.snapshot().activeRequests ?? 0) > 0) {
45
+ throw new PromptExecutionBusyError();
46
+ }
47
+ this.executing = true;
48
+ ownsScope = true;
49
+ if (limits !== undefined) {
50
+ if (this.agent.state.isStreaming)
51
+ throw new PromptExecutionBusyError();
52
+ this.lifecycle.assertIdle();
53
+ const scopedBudget = new RunBudget(limits, this.lifecycle.stop);
54
+ budget = scopedBudget;
55
+ this.current = scopedBudget;
56
+ wrapped = wrapBudgetStream(source, scopedBudget);
57
+ wrappedAuth = async (provider) => {
58
+ scopedBudget.assertAdmission();
59
+ const key = await sourceAuth?.(provider);
60
+ scopedBudget.assertActive();
61
+ return key;
62
+ };
63
+ this.agent.streamFn = wrapped;
64
+ this.agent.getApiKey = wrappedAuth;
65
+ scopedBudget.assertAdmission();
66
+ }
67
+ entered = true;
68
+ await operation();
69
+ budget?.assertActive();
70
+ }
71
+ catch (error) {
72
+ if (!entered) {
73
+ preflightResult?.(false);
74
+ if (error instanceof RunBudgetExceededError || error instanceof RunBudgetPolicyError)
75
+ this.lifecycle.reject(error);
76
+ }
77
+ throw budget?.failure ?? error;
78
+ }
79
+ finally {
80
+ if (budget) {
81
+ budget.close();
82
+ this.previous = budget;
83
+ if (this.current === budget)
84
+ this.current = undefined;
85
+ }
86
+ if (wrapped !== undefined && this.agent.streamFn === wrapped)
87
+ this.agent.streamFn = source;
88
+ if (wrappedAuth !== undefined && this.agent.getApiKey === wrappedAuth)
89
+ this.agent.getApiKey = sourceAuth;
90
+ if (ownsScope)
91
+ this.executing = false;
92
+ }
93
+ }
94
+ }
95
+ export function wrapBudgetStream(source, budget) {
96
+ const wrapped = async (model, context, options) => {
97
+ options?.signal?.throwIfAborted();
98
+ const release = budget.admit();
99
+ const remainingMs = budget.remainingMs;
100
+ try {
101
+ const stream = await source(model, context, {
102
+ ...options,
103
+ signal: options?.signal ? AbortSignal.any([options.signal, budget.signal]) : budget.signal,
104
+ maxRetries: 0,
105
+ ...(remainingMs === undefined
106
+ ? {}
107
+ : { timeoutMs: Math.min(options?.timeoutMs ?? remainingMs, remainingMs) }),
108
+ });
109
+ // A returned stream is not yet a completed request. Missing terminal metadata
110
+ // retains the reservation; abort alone never refunds it.
111
+ void stream.result().then(release, release);
112
+ return stream;
113
+ }
114
+ catch (error) {
115
+ release();
116
+ throw error;
117
+ }
118
+ };
119
+ // Preserve source-owned identity brands used by mandatory credential checks.
120
+ for (const key of Object.getOwnPropertySymbols(source)) {
121
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
122
+ if (descriptor)
123
+ Object.defineProperty(wrapped, key, descriptor);
124
+ }
125
+ return wrapped;
126
+ }
127
+ //# sourceMappingURL=session-run-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-run-budget.js","sourceRoot":"","sources":["../../src/core/session-run-budget.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, StreamFn } from \"omk-agent-core\";\nimport { RunBudget, type RunBudgetSnapshot } from \"./run-budget.ts\";\nimport { RunBudgetExceededError, type RunBudgetLimits, RunBudgetPolicyError } from \"./run-budget-policy.ts\";\nimport { PromptExecutionBusyError } from \"./session-prompt-lifecycle.ts\";\n\ninterface RunBudgetLifecycle {\n\treadonly assertIdle: () => void;\n\treadonly stop: (error: RunBudgetExceededError) => void;\n\treadonly reject: (error: RunBudgetExceededError | RunBudgetPolicyError) => void;\n}\n\n/** Installs one scoped stream boundary; restores it without overwriting a later owner. */\nexport class SessionRunBudget {\n\tprivate readonly agent: Agent;\n\tprivate readonly lifecycle: RunBudgetLifecycle;\n\tprivate current: RunBudget | undefined;\n\tprivate previous: RunBudget | undefined;\n\tprivate executing = false;\n\tprivate disposed = false;\n\n\tconstructor(agent: Agent, lifecycle: RunBudgetLifecycle) {\n\t\tthis.agent = agent;\n\t\tthis.lifecycle = lifecycle;\n\t}\n\n\tget failure(): RunBudgetExceededError | undefined {\n\t\treturn this.current?.failure;\n\t}\n\tget remainingMs(): number | undefined {\n\t\treturn this.current?.remainingMs;\n\t}\n\tsnapshot(): RunBudgetSnapshot | undefined {\n\t\treturn (this.current ?? this.previous)?.snapshot();\n\t}\n\tassertActive(): void {\n\t\tthis.current?.assertActive();\n\t}\n\tassertAdmission(): void {\n\t\tthis.current?.assertAdmission();\n\t}\n\tclose(): void {\n\t\tthis.disposed = true;\n\t\tthis.current?.close();\n\t}\n\n\tasync execute(\n\t\tlimits: RunBudgetLimits | undefined,\n\t\toperation: () => Promise<void>,\n\t\tpreflightResult?: (accepted: boolean) => void,\n\t): Promise<void> {\n\t\tlet ownsScope = false;\n\t\tlet entered = false;\n\t\tlet budget: RunBudget | undefined;\n\t\tconst source = this.agent.streamFn;\n\t\tconst sourceAuth = this.agent.getApiKey;\n\t\tlet wrapped: StreamFn | undefined;\n\t\tlet wrappedAuth: Agent[\"getApiKey\"];\n\t\ttry {\n\t\t\tif (this.executing || this.disposed || (this.previous?.snapshot().activeRequests ?? 0) > 0) {\n\t\t\t\tthrow new PromptExecutionBusyError();\n\t\t\t}\n\t\t\tthis.executing = true;\n\t\t\townsScope = true;\n\t\t\tif (limits !== undefined) {\n\t\t\t\tif (this.agent.state.isStreaming) throw new PromptExecutionBusyError();\n\t\t\t\tthis.lifecycle.assertIdle();\n\t\t\t\tconst scopedBudget = new RunBudget(limits, this.lifecycle.stop);\n\t\t\t\tbudget = scopedBudget;\n\t\t\t\tthis.current = scopedBudget;\n\t\t\t\twrapped = wrapBudgetStream(source, scopedBudget);\n\t\t\t\twrappedAuth = async (provider) => {\n\t\t\t\t\tscopedBudget.assertAdmission();\n\t\t\t\t\tconst key = await sourceAuth?.(provider);\n\t\t\t\t\tscopedBudget.assertActive();\n\t\t\t\t\treturn key;\n\t\t\t\t};\n\t\t\t\tthis.agent.streamFn = wrapped;\n\t\t\t\tthis.agent.getApiKey = wrappedAuth;\n\t\t\t\tscopedBudget.assertAdmission();\n\t\t\t}\n\t\t\tentered = true;\n\t\t\tawait operation();\n\t\t\tbudget?.assertActive();\n\t\t} catch (error) {\n\t\t\tif (!entered) {\n\t\t\t\tpreflightResult?.(false);\n\t\t\t\tif (error instanceof RunBudgetExceededError || error instanceof RunBudgetPolicyError)\n\t\t\t\t\tthis.lifecycle.reject(error);\n\t\t\t}\n\t\t\tthrow budget?.failure ?? error;\n\t\t} finally {\n\t\t\tif (budget) {\n\t\t\t\tbudget.close();\n\t\t\t\tthis.previous = budget;\n\t\t\t\tif (this.current === budget) this.current = undefined;\n\t\t\t}\n\t\t\tif (wrapped !== undefined && this.agent.streamFn === wrapped) this.agent.streamFn = source;\n\t\t\tif (wrappedAuth !== undefined && this.agent.getApiKey === wrappedAuth) this.agent.getApiKey = sourceAuth;\n\t\t\tif (ownsScope) this.executing = false;\n\t\t}\n\t}\n}\n\nexport function wrapBudgetStream(source: StreamFn, budget: RunBudget): StreamFn {\n\tconst wrapped: StreamFn = async (model, context, options) => {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst release = budget.admit();\n\t\tconst remainingMs = budget.remainingMs;\n\t\ttry {\n\t\t\tconst stream = await source(model, context, {\n\t\t\t\t...options,\n\t\t\t\tsignal: options?.signal ? AbortSignal.any([options.signal, budget.signal]) : budget.signal,\n\t\t\t\tmaxRetries: 0,\n\t\t\t\t...(remainingMs === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { timeoutMs: Math.min(options?.timeoutMs ?? remainingMs, remainingMs) }),\n\t\t\t});\n\t\t\t// A returned stream is not yet a completed request. Missing terminal metadata\n\t\t\t// retains the reservation; abort alone never refunds it.\n\t\t\tvoid stream.result().then(release, release);\n\t\t\treturn stream;\n\t\t} catch (error) {\n\t\t\trelease();\n\t\t\tthrow error;\n\t\t}\n\t};\n\t// Preserve source-owned identity brands used by mandatory credential checks.\n\tfor (const key of Object.getOwnPropertySymbols(source)) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(source, key);\n\t\tif (descriptor) Object.defineProperty(wrapped, key, descriptor);\n\t}\n\treturn wrapped;\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { AgentMessage } from "omk-agent-core";
2
+ import type { Api, Model } from "omk-ai";
3
+ import { type ClassifySessionTerminationInput, type SessionTermination, type SessionTerminationCause } from "./session-termination.ts";
4
+ interface RunTerminationContext extends Pick<ClassifySessionTerminationInput, "sessionId" | "runId" | "timestamp"> {
5
+ readonly model?: Pick<Model<Api>, "provider" | "id" | "contextWindow">;
6
+ readonly elevatedRisk: boolean;
7
+ readonly userAbortRequested: boolean;
8
+ readonly pendingCause?: SessionTerminationCause;
9
+ readonly toolTimeout?: {
10
+ readonly toolCallId: string;
11
+ readonly toolName: string;
12
+ readonly executionStarted: boolean;
13
+ };
14
+ }
15
+ /** Translate one settled core run into a cause; lifecycle persistence stays with AgentSession. */
16
+ export declare function classifyRunTermination(messages: readonly AgentMessage[], context: RunTerminationContext): SessionTermination;
17
+ export {};
18
+ //# sourceMappingURL=session-run-termination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-run-termination.d.ts","sourceRoot":"","sources":["../../src/core/session-run-termination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,GAAG,EAAoB,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE3D,OAAO,EACN,KAAK,+BAA+B,EAEpC,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,MAAM,0BAA0B,CAAC;AAElC,UAAU,qBAAsB,SAAQ,IAAI,CAAC,+BAA+B,EAAE,WAAW,GAAG,OAAO,GAAG,WAAW,CAAC;IACjH,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,eAAe,CAAC,CAAC;IACvE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,uBAAuB,CAAC;IAChD,QAAQ,CAAC,WAAW,CAAC,EAAE;QACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;KACnC,CAAC;CACF;AAED,kGAAkG;AAClG,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,OAAO,EAAE,qBAAqB,GAC5B,kBAAkB,CA4DpB","sourcesContent":["import type { AgentMessage } from \"omk-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"omk-ai\";\nimport { providerFailureCause, terminationMessage } from \"./session-failure-cause.ts\";\nimport {\n\ttype ClassifySessionTerminationInput,\n\tclassifySessionTermination,\n\ttype SessionTermination,\n\ttype SessionTerminationCause,\n} from \"./session-termination.ts\";\n\ninterface RunTerminationContext extends Pick<ClassifySessionTerminationInput, \"sessionId\" | \"runId\" | \"timestamp\"> {\n\treadonly model?: Pick<Model<Api>, \"provider\" | \"id\" | \"contextWindow\">;\n\treadonly elevatedRisk: boolean;\n\treadonly userAbortRequested: boolean;\n\treadonly pendingCause?: SessionTerminationCause;\n\treadonly toolTimeout?: {\n\t\treadonly toolCallId: string;\n\t\treadonly toolName: string;\n\t\treadonly executionStarted: boolean;\n\t};\n}\n\n/** Translate one settled core run into a cause; lifecycle persistence stays with AgentSession. */\nexport function classifyRunTermination(\n\tmessages: readonly AgentMessage[],\n\tcontext: RunTerminationContext,\n): SessionTermination {\n\tlet assistant: AssistantMessage | undefined;\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst candidate = messages[index];\n\t\tif (candidate?.role === \"assistant\") {\n\t\t\tassistant = candidate;\n\t\t\tbreak;\n\t\t}\n\t}\n\tlet cause: SessionTerminationCause;\n\tlet message: string;\n\tlet sideEffects: \"none\" | \"possible\" = context.elevatedRisk ? \"possible\" : \"none\";\n\tlet toolCallId: string | undefined;\n\tlet toolName: string | undefined;\n\n\tif (context.pendingCause?.area === \"budget\") {\n\t\tcause = context.pendingCause;\n\t\tmessage = `Run budget exhausted: ${context.pendingCause.code}`;\n\t\tsideEffects = \"possible\";\n\t} else if (context.toolTimeout) {\n\t\tcause = { area: \"tool\", code: \"timeout\" };\n\t\tmessage = `Tool ${context.toolTimeout.toolName} timed out.`;\n\t\ttoolCallId = context.toolTimeout.toolCallId;\n\t\ttoolName = context.toolTimeout.toolName;\n\t\tsideEffects = context.toolTimeout.executionStarted ? \"possible\" : sideEffects;\n\t} else if (context.pendingCause?.area === \"configuration\") {\n\t\tcause = context.pendingCause;\n\t\tmessage = \"Model dispatch was rejected by the configured model contract.\";\n\t} else if (context.userAbortRequested) {\n\t\tcause = { area: \"user\", code: \"abort\" };\n\t\tmessage = \"The user aborted the run.\";\n\t\tif (messages.some((entry) => entry.role === \"toolResult\")) sideEffects = \"possible\";\n\t} else if (!assistant) {\n\t\tcause = { area: \"internal\", code: \"unclassified\" };\n\t\tmessage = \"Agent run ended without an assistant result.\";\n\t} else if (assistant.stopReason === \"aborted\") {\n\t\tcause = { area: \"provider\", code: \"abort\" };\n\t\tmessage = terminationMessage(assistant.errorMessage, \"The provider aborted the run.\");\n\t} else if (assistant.stopReason === \"error\") {\n\t\tcause = providerFailureCause(assistant, context.model?.contextWindow ?? 0);\n\t\tmessage = terminationMessage(assistant.errorMessage, \"The provider request failed.\");\n\t} else {\n\t\tcause = { area: \"completed\" };\n\t\tmessage = \"Run completed.\";\n\t}\n\tconst provider = assistant?.provider || context.model?.provider;\n\tconst model = assistant?.model || context.model?.id;\n\treturn classifySessionTermination({\n\t\tsessionId: context.sessionId,\n\t\trunId: context.runId,\n\t\ttimestamp: context.timestamp,\n\t\tsource: \"observed\",\n\t\tmessage,\n\t\tcause,\n\t\tsideEffects,\n\t\t...(provider ? { provider } : {}),\n\t\t...(model ? { model } : {}),\n\t\t...(toolCallId ? { toolCallId } : {}),\n\t\t...(toolName ? { toolName } : {}),\n\t});\n}\n"]}