open-multi-agent-kit 0.96.2 → 0.98.0

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 (368) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +43 -9
  3. package/dist/cli/args.d.ts +2 -3
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +13 -189
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/help.d.ts +3 -0
  8. package/dist/cli/help.d.ts.map +1 -0
  9. package/dist/cli/help.js +194 -0
  10. package/dist/cli/help.js.map +1 -0
  11. package/dist/cli/initial-message.d.ts.map +1 -1
  12. package/dist/cli/initial-message.js +2 -1
  13. package/dist/cli/initial-message.js.map +1 -1
  14. package/dist/cli/resource-paths.d.ts +4 -0
  15. package/dist/cli/resource-paths.d.ts.map +1 -0
  16. package/dist/cli/resource-paths.js +59 -0
  17. package/dist/cli/resource-paths.js.map +1 -0
  18. package/dist/commands/adaptorch-doctor-cli.d.ts +13 -0
  19. package/dist/commands/adaptorch-doctor-cli.d.ts.map +1 -0
  20. package/dist/commands/adaptorch-doctor-cli.js +115 -0
  21. package/dist/commands/adaptorch-doctor-cli.js.map +1 -0
  22. package/dist/commands/resource-doctor-cli.d.ts +4 -0
  23. package/dist/commands/resource-doctor-cli.d.ts.map +1 -1
  24. package/dist/commands/resource-doctor-cli.js +35 -5
  25. package/dist/commands/resource-doctor-cli.js.map +1 -1
  26. package/dist/core/adaptorch-bridge.d.ts +11 -13
  27. package/dist/core/adaptorch-bridge.d.ts.map +1 -1
  28. package/dist/core/adaptorch-bridge.js +11 -13
  29. package/dist/core/adaptorch-bridge.js.map +1 -1
  30. package/dist/core/agent-session-snapshot.d.ts +16 -0
  31. package/dist/core/agent-session-snapshot.d.ts.map +1 -0
  32. package/dist/core/agent-session-snapshot.js +111 -0
  33. package/dist/core/agent-session-snapshot.js.map +1 -0
  34. package/dist/core/agent-session.d.ts +14 -10
  35. package/dist/core/agent-session.d.ts.map +1 -1
  36. package/dist/core/agent-session.js +102 -217
  37. package/dist/core/agent-session.js.map +1 -1
  38. package/dist/core/agent-tool-settings.d.ts +10 -0
  39. package/dist/core/agent-tool-settings.d.ts.map +1 -1
  40. package/dist/core/agent-tool-settings.js +10 -1
  41. package/dist/core/agent-tool-settings.js.map +1 -1
  42. package/dist/core/attachment-store.d.ts +39 -0
  43. package/dist/core/attachment-store.d.ts.map +1 -0
  44. package/dist/core/attachment-store.js +81 -0
  45. package/dist/core/attachment-store.js.map +1 -0
  46. package/dist/core/command-safety.d.ts +8 -0
  47. package/dist/core/command-safety.d.ts.map +1 -1
  48. package/dist/core/command-safety.js +140 -14
  49. package/dist/core/command-safety.js.map +1 -1
  50. package/dist/core/compaction/compaction.d.ts +13 -1
  51. package/dist/core/compaction/compaction.d.ts.map +1 -1
  52. package/dist/core/compaction/compaction.js +60 -62
  53. package/dist/core/compaction/compaction.js.map +1 -1
  54. package/dist/core/compaction/index.d.ts +1 -0
  55. package/dist/core/compaction/index.d.ts.map +1 -1
  56. package/dist/core/compaction/index.js +1 -0
  57. package/dist/core/compaction/index.js.map +1 -1
  58. package/dist/core/compaction/knowledge-triage-format.d.ts +9 -0
  59. package/dist/core/compaction/knowledge-triage-format.d.ts.map +1 -0
  60. package/dist/core/compaction/knowledge-triage-format.js +71 -0
  61. package/dist/core/compaction/knowledge-triage-format.js.map +1 -0
  62. package/dist/core/compaction/knowledge-triage-types.d.ts +7 -0
  63. package/dist/core/compaction/knowledge-triage-types.d.ts.map +1 -0
  64. package/dist/core/compaction/knowledge-triage-types.js +2 -0
  65. package/dist/core/compaction/knowledge-triage-types.js.map +1 -0
  66. package/dist/core/compaction/knowledge-triage.d.ts +29 -0
  67. package/dist/core/compaction/knowledge-triage.d.ts.map +1 -0
  68. package/dist/core/compaction/knowledge-triage.js +189 -0
  69. package/dist/core/compaction/knowledge-triage.js.map +1 -0
  70. package/dist/core/compaction/transaction.d.ts.map +1 -1
  71. package/dist/core/compaction/transaction.js +1 -1
  72. package/dist/core/compaction/transaction.js.map +1 -1
  73. package/dist/core/compaction/utils.d.ts +4 -0
  74. package/dist/core/compaction/utils.d.ts.map +1 -1
  75. package/dist/core/compaction/utils.js +37 -0
  76. package/dist/core/compaction/utils.js.map +1 -1
  77. package/dist/core/completion-sound-io.d.ts +14 -0
  78. package/dist/core/completion-sound-io.d.ts.map +1 -0
  79. package/dist/core/completion-sound-io.js +64 -0
  80. package/dist/core/completion-sound-io.js.map +1 -0
  81. package/dist/core/completion-sound.d.ts +4 -10
  82. package/dist/core/completion-sound.d.ts.map +1 -1
  83. package/dist/core/completion-sound.js +28 -58
  84. package/dist/core/completion-sound.js.map +1 -1
  85. package/dist/core/context-budget-governor-v2.d.ts +1 -1
  86. package/dist/core/context-budget-governor-v2.d.ts.map +1 -1
  87. package/dist/core/context-budget-governor-v2.js +1 -1
  88. package/dist/core/context-budget-governor-v2.js.map +1 -1
  89. package/dist/core/context-budget-relevance.d.ts +15 -2
  90. package/dist/core/context-budget-relevance.d.ts.map +1 -1
  91. package/dist/core/context-budget-relevance.js +21 -9
  92. package/dist/core/context-budget-relevance.js.map +1 -1
  93. package/dist/core/context-budget-system-prompt-items.d.ts +1 -1
  94. package/dist/core/context-budget-system-prompt-items.d.ts.map +1 -1
  95. package/dist/core/context-budget-system-prompt-items.js.map +1 -1
  96. package/dist/core/context-budget-system-prompt.d.ts +1 -1
  97. package/dist/core/context-budget-system-prompt.d.ts.map +1 -1
  98. package/dist/core/context-budget-system-prompt.js.map +1 -1
  99. package/dist/core/context-budget-v2-cache-keys.d.ts +3 -0
  100. package/dist/core/context-budget-v2-cache-keys.d.ts.map +1 -1
  101. package/dist/core/context-budget-v2-cache-keys.js +2 -0
  102. package/dist/core/context-budget-v2-cache-keys.js.map +1 -1
  103. package/dist/core/context-budget-v2-plan-cache-keys.d.ts.map +1 -1
  104. package/dist/core/context-budget-v2-plan-cache-keys.js +3 -0
  105. package/dist/core/context-budget-v2-plan-cache-keys.js.map +1 -1
  106. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  107. package/dist/core/context-budget-v2-planner.js +2 -1
  108. package/dist/core/context-budget-v2-planner.js.map +1 -1
  109. package/dist/core/context-budget-v2-scoring.d.ts +17 -0
  110. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  111. package/dist/core/context-budget-v2-scoring.js +25 -8
  112. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  113. package/dist/core/context-budget-v2-selection.d.ts +2 -2
  114. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  115. package/dist/core/context-budget-v2-selection.js +18 -1
  116. package/dist/core/context-budget-v2-selection.js.map +1 -1
  117. package/dist/core/context-budget-v2-types.d.ts +17 -0
  118. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  119. package/dist/core/context-budget-v2-types.js +15 -0
  120. package/dist/core/context-budget-v2-types.js.map +1 -1
  121. package/dist/core/context-file.d.ts +23 -0
  122. package/dist/core/context-file.d.ts.map +1 -0
  123. package/dist/core/context-file.js +17 -0
  124. package/dist/core/context-file.js.map +1 -0
  125. package/dist/core/extensions/builtin/command-safety-gate.d.ts +3 -1
  126. package/dist/core/extensions/builtin/command-safety-gate.d.ts.map +1 -1
  127. package/dist/core/extensions/builtin/command-safety-gate.js +7 -0
  128. package/dist/core/extensions/builtin/command-safety-gate.js.map +1 -1
  129. package/dist/core/extensions/types.d.ts +12 -1
  130. package/dist/core/extensions/types.d.ts.map +1 -1
  131. package/dist/core/extensions/types.js.map +1 -1
  132. package/dist/core/keybindings.d.ts +1 -1
  133. package/dist/core/keybindings.d.ts.map +1 -1
  134. package/dist/core/keybindings.js +6 -1
  135. package/dist/core/keybindings.js.map +1 -1
  136. package/dist/core/model-prompt-policy.d.ts +1 -1
  137. package/dist/core/model-prompt-policy.d.ts.map +1 -1
  138. package/dist/core/model-prompt-policy.js.map +1 -1
  139. package/dist/core/model-registry.d.ts +8 -3
  140. package/dist/core/model-registry.d.ts.map +1 -1
  141. package/dist/core/model-registry.js +71 -1
  142. package/dist/core/model-registry.js.map +1 -1
  143. package/dist/core/package-doctor-source-scan.d.ts +9 -0
  144. package/dist/core/package-doctor-source-scan.d.ts.map +1 -1
  145. package/dist/core/package-doctor-source-scan.js +77 -4
  146. package/dist/core/package-doctor-source-scan.js.map +1 -1
  147. package/dist/core/prompt-attachment.d.ts +51 -0
  148. package/dist/core/prompt-attachment.d.ts.map +1 -0
  149. package/dist/core/prompt-attachment.js +186 -0
  150. package/dist/core/prompt-attachment.js.map +1 -0
  151. package/dist/core/prompt-settlement.d.ts +6 -3
  152. package/dist/core/prompt-settlement.d.ts.map +1 -1
  153. package/dist/core/prompt-settlement.js +10 -14
  154. package/dist/core/prompt-settlement.js.map +1 -1
  155. package/dist/core/provider-resilience.d.ts +28 -0
  156. package/dist/core/provider-resilience.d.ts.map +1 -1
  157. package/dist/core/provider-resilience.js +50 -0
  158. package/dist/core/provider-resilience.js.map +1 -1
  159. package/dist/core/provider-retry.d.ts +1 -1
  160. package/dist/core/provider-retry.d.ts.map +1 -1
  161. package/dist/core/provider-retry.js +1 -1
  162. package/dist/core/provider-retry.js.map +1 -1
  163. package/dist/core/provider-usage.d.ts.map +1 -1
  164. package/dist/core/provider-usage.js +68 -2
  165. package/dist/core/provider-usage.js.map +1 -1
  166. package/dist/core/reasoning-router-promotion.d.ts +124 -0
  167. package/dist/core/reasoning-router-promotion.d.ts.map +1 -0
  168. package/dist/core/reasoning-router-promotion.js +121 -0
  169. package/dist/core/reasoning-router-promotion.js.map +1 -0
  170. package/dist/core/reasoning-router-replay-stability.d.ts +65 -0
  171. package/dist/core/reasoning-router-replay-stability.d.ts.map +1 -0
  172. package/dist/core/reasoning-router-replay-stability.js +67 -0
  173. package/dist/core/reasoning-router-replay-stability.js.map +1 -0
  174. package/dist/core/reasoning-router-v4-weights.d.ts +6 -6
  175. package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
  176. package/dist/core/reasoning-router-v4-weights.js +3 -3
  177. package/dist/core/reasoning-router-v4-weights.js.map +1 -1
  178. package/dist/core/reasoning-router-v4.d.ts.map +1 -1
  179. package/dist/core/reasoning-router-v4.js +6 -6
  180. package/dist/core/reasoning-router-v4.js.map +1 -1
  181. package/dist/core/redaction.d.ts.map +1 -1
  182. package/dist/core/redaction.js +1 -1
  183. package/dist/core/redaction.js.map +1 -1
  184. package/dist/core/resource-admission.d.ts +2 -1
  185. package/dist/core/resource-admission.d.ts.map +1 -1
  186. package/dist/core/resource-admission.js +11 -0
  187. package/dist/core/resource-admission.js.map +1 -1
  188. package/dist/core/resource-governor-format.d.ts +1 -1
  189. package/dist/core/resource-governor-format.d.ts.map +1 -1
  190. package/dist/core/resource-governor-format.js.map +1 -1
  191. package/dist/core/resource-governor-settings.d.ts +6 -12
  192. package/dist/core/resource-governor-settings.d.ts.map +1 -1
  193. package/dist/core/resource-governor-settings.js.map +1 -1
  194. package/dist/core/resource-governor-types.d.ts +24 -0
  195. package/dist/core/resource-governor-types.d.ts.map +1 -0
  196. package/dist/core/resource-governor-types.js +15 -0
  197. package/dist/core/resource-governor-types.js.map +1 -0
  198. package/dist/core/resource-loader.d.ts +1 -7
  199. package/dist/core/resource-loader.d.ts.map +1 -1
  200. package/dist/core/resource-loader.js.map +1 -1
  201. package/dist/core/resource-observation-journal.d.ts +2 -1
  202. package/dist/core/resource-observation-journal.d.ts.map +1 -1
  203. package/dist/core/resource-observation-journal.js +4 -3
  204. package/dist/core/resource-observation-journal.js.map +1 -1
  205. package/dist/core/resource-observation-report-reader.d.ts +7 -0
  206. package/dist/core/resource-observation-report-reader.d.ts.map +1 -0
  207. package/dist/core/resource-observation-report-reader.js +157 -0
  208. package/dist/core/resource-observation-report-reader.js.map +1 -0
  209. package/dist/core/resource-observation-report.d.ts +24 -0
  210. package/dist/core/resource-observation-report.d.ts.map +1 -0
  211. package/dist/core/resource-observation-report.js +101 -0
  212. package/dist/core/resource-observation-report.js.map +1 -0
  213. package/dist/core/router-feedback-collector.d.ts +1 -1
  214. package/dist/core/router-feedback-collector.d.ts.map +1 -1
  215. package/dist/core/router-feedback-collector.js.map +1 -1
  216. package/dist/core/session-bash-service.d.ts.map +1 -1
  217. package/dist/core/session-bash-service.js +4 -3
  218. package/dist/core/session-bash-service.js.map +1 -1
  219. package/dist/core/session-failure-cause.d.ts +33 -0
  220. package/dist/core/session-failure-cause.d.ts.map +1 -0
  221. package/dist/core/session-failure-cause.js +141 -0
  222. package/dist/core/session-failure-cause.js.map +1 -0
  223. package/dist/core/session-system-prompt.d.ts +1 -1
  224. package/dist/core/session-system-prompt.d.ts.map +1 -1
  225. package/dist/core/session-system-prompt.js.map +1 -1
  226. package/dist/core/session-termination.d.ts +1 -1
  227. package/dist/core/session-termination.d.ts.map +1 -1
  228. package/dist/core/session-termination.js +11 -2
  229. package/dist/core/session-termination.js.map +1 -1
  230. package/dist/core/settings-manager.d.ts +1 -1
  231. package/dist/core/settings-manager.d.ts.map +1 -1
  232. package/dist/core/settings-manager.js.map +1 -1
  233. package/dist/core/system-prompt.d.ts +1 -1
  234. package/dist/core/system-prompt.d.ts.map +1 -1
  235. package/dist/core/system-prompt.js.map +1 -1
  236. package/dist/core/tools/bash.d.ts.map +1 -1
  237. package/dist/core/tools/bash.js +10 -14
  238. package/dist/core/tools/bash.js.map +1 -1
  239. package/dist/core/tools/grep.d.ts.map +1 -1
  240. package/dist/core/tools/grep.js +13 -48
  241. package/dist/core/tools/grep.js.map +1 -1
  242. package/dist/core/tools/read.d.ts.map +1 -1
  243. package/dist/core/tools/read.js +1 -34
  244. package/dist/core/tools/read.js.map +1 -1
  245. package/dist/core/workload-classifier.d.ts +2 -10
  246. package/dist/core/workload-classifier.d.ts.map +1 -1
  247. package/dist/core/workload-classifier.js +17 -0
  248. package/dist/core/workload-classifier.js.map +1 -1
  249. package/dist/core/workload-family-matcher.d.ts +1 -1
  250. package/dist/core/workload-family-matcher.d.ts.map +1 -1
  251. package/dist/core/workload-family-matcher.js.map +1 -1
  252. package/dist/core/workload-shard-execution-types.d.ts +40 -0
  253. package/dist/core/workload-shard-execution-types.d.ts.map +1 -0
  254. package/dist/core/workload-shard-execution-types.js +14 -0
  255. package/dist/core/workload-shard-execution-types.js.map +1 -0
  256. package/dist/core/workload-shard-executor.d.ts +3 -27
  257. package/dist/core/workload-shard-executor.d.ts.map +1 -1
  258. package/dist/core/workload-shard-executor.js.map +1 -1
  259. package/dist/core/workload-shard-runner.d.ts +1 -1
  260. package/dist/core/workload-shard-runner.d.ts.map +1 -1
  261. package/dist/core/workload-shard-runner.js.map +1 -1
  262. package/dist/core/workload-types.d.ts +24 -0
  263. package/dist/core/workload-types.d.ts.map +1 -0
  264. package/dist/core/workload-types.js +15 -0
  265. package/dist/core/workload-types.js.map +1 -0
  266. package/dist/index.d.ts +1 -1
  267. package/dist/index.d.ts.map +1 -1
  268. package/dist/index.js +1 -1
  269. package/dist/index.js.map +1 -1
  270. package/dist/main.d.ts +2 -0
  271. package/dist/main.d.ts.map +1 -1
  272. package/dist/main.js +27 -47
  273. package/dist/main.js.map +1 -1
  274. package/dist/modes/interactive/components/attachment-strip.d.ts +21 -0
  275. package/dist/modes/interactive/components/attachment-strip.d.ts.map +1 -0
  276. package/dist/modes/interactive/components/attachment-strip.js +136 -0
  277. package/dist/modes/interactive/components/attachment-strip.js.map +1 -0
  278. package/dist/modes/interactive/components/footer.d.ts +6 -0
  279. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/footer.js +26 -1
  281. package/dist/modes/interactive/components/footer.js.map +1 -1
  282. package/dist/modes/interactive/components/status-sidebar.d.ts.map +1 -1
  283. package/dist/modes/interactive/components/status-sidebar.js +12 -1
  284. package/dist/modes/interactive/components/status-sidebar.js.map +1 -1
  285. package/dist/modes/interactive/interactive-mode.d.ts +23 -4
  286. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  287. package/dist/modes/interactive/interactive-mode.js +174 -53
  288. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  289. package/dist/modes/interactive/theme/omk-control-panel.json +1 -1
  290. package/dist/utils/clipboard-image.d.ts.map +1 -1
  291. package/dist/utils/clipboard-image.js +42 -33
  292. package/dist/utils/clipboard-image.js.map +1 -1
  293. package/dist/utils/clipboard.d.ts.map +1 -1
  294. package/dist/utils/clipboard.js +14 -2
  295. package/dist/utils/clipboard.js.map +1 -1
  296. package/docs/adaptorch-preview-spec.md +20 -18
  297. package/docs/adaptorch-preview.md +9 -9
  298. package/docs/compaction.md +25 -4
  299. package/docs/correctness-wall.md +10 -10
  300. package/docs/docs.json +12 -0
  301. package/docs/environment-variables.md +9 -2
  302. package/docs/extensions.md +12 -3
  303. package/docs/index.md +7 -1
  304. package/docs/metrics.md +74 -2
  305. package/docs/provider-resilience.md +11 -0
  306. package/docs/runtime-algorithms.md +359 -0
  307. package/docs/sdk.md +2 -2
  308. package/docs/settings.md +11 -10
  309. package/docs/usage.md +19 -3
  310. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  311. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  312. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  313. package/examples/extensions/gondolin/package-lock.json +2 -2
  314. package/examples/extensions/gondolin/package.json +1 -1
  315. package/examples/extensions/sandbox/package-lock.json +2 -2
  316. package/examples/extensions/sandbox/package.json +1 -1
  317. package/examples/extensions/with-deps/package-lock.json +2 -2
  318. package/examples/extensions/with-deps/package.json +1 -1
  319. package/npm-shrinkwrap.json +18 -18
  320. package/package.json +7 -7
  321. package/dist/core/context-budget-governor.d.ts +0 -67
  322. package/dist/core/context-budget-governor.d.ts.map +0 -1
  323. package/dist/core/context-budget-governor.js +0 -224
  324. package/dist/core/context-budget-governor.js.map +0 -1
  325. package/dist/core/lean-ctx.d.ts +0 -99
  326. package/dist/core/lean-ctx.d.ts.map +0 -1
  327. package/dist/core/lean-ctx.js +0 -225
  328. package/dist/core/lean-ctx.js.map +0 -1
  329. package/dist/core/read-anchors.d.ts +0 -110
  330. package/dist/core/read-anchors.d.ts.map +0 -1
  331. package/dist/core/read-anchors.js +0 -193
  332. package/dist/core/read-anchors.js.map +0 -1
  333. package/dist/core/recovery-checkpoint.d.ts +0 -196
  334. package/dist/core/recovery-checkpoint.d.ts.map +0 -1
  335. package/dist/core/recovery-checkpoint.js +0 -382
  336. package/dist/core/recovery-checkpoint.js.map +0 -1
  337. package/dist/core/sandbox-policy-v2.d.ts +0 -49
  338. package/dist/core/sandbox-policy-v2.d.ts.map +0 -1
  339. package/dist/core/sandbox-policy-v2.js +0 -368
  340. package/dist/core/sandbox-policy-v2.js.map +0 -1
  341. package/dist/core/tools/omp-pure-seams.d.ts +0 -41
  342. package/dist/core/tools/omp-pure-seams.d.ts.map +0 -1
  343. package/dist/core/tools/omp-pure-seams.js +0 -142
  344. package/dist/core/tools/omp-pure-seams.js.map +0 -1
  345. package/dist/core/tools/omp-seam-runtime.d.ts +0 -100
  346. package/dist/core/tools/omp-seam-runtime.d.ts.map +0 -1
  347. package/dist/core/tools/omp-seam-runtime.js +0 -40
  348. package/dist/core/tools/omp-seam-runtime.js.map +0 -1
  349. package/dist/guardrails/audit-logger.d.ts +0 -4
  350. package/dist/guardrails/audit-logger.d.ts.map +0 -1
  351. package/dist/guardrails/audit-logger.js +0 -28
  352. package/dist/guardrails/audit-logger.js.map +0 -1
  353. package/dist/guardrails/freedomd-adapter.d.ts +0 -16
  354. package/dist/guardrails/freedomd-adapter.d.ts.map +0 -1
  355. package/dist/guardrails/freedomd-adapter.js +0 -87
  356. package/dist/guardrails/freedomd-adapter.js.map +0 -1
  357. package/dist/orchestration/lane-grant-auditor.d.ts +0 -4
  358. package/dist/orchestration/lane-grant-auditor.d.ts.map +0 -1
  359. package/dist/orchestration/lane-grant-auditor.js +0 -87
  360. package/dist/orchestration/lane-grant-auditor.js.map +0 -1
  361. package/dist/types/guardrails.d.ts +0 -61
  362. package/dist/types/guardrails.d.ts.map +0 -1
  363. package/dist/types/guardrails.js +0 -9
  364. package/dist/types/guardrails.js.map +0 -1
  365. package/dist/types/lane-grant.d.ts +0 -58
  366. package/dist/types/lane-grant.d.ts.map +0 -1
  367. package/dist/types/lane-grant.js +0 -9
  368. package/dist/types/lane-grant.js.map +0 -1
@@ -17,7 +17,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
17
  import { basename, dirname, join as joinPath } from "node:path";
18
18
  import { isDeepStrictEqual } from "node:util";
19
19
  import { getVisionRouteModel } from "omk-agent-core";
20
- import { clampThinkingLevel, cleanupSessionResources, deriveContextPromptCacheKey, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "omk-ai";
20
+ import { clampThinkingLevel, cleanupSessionResources, deriveContextPromptCacheKey, getSupportedThinkingLevels, isBuiltinStreamFn, modelsAreEqual, resetApiProviders, } from "omk-ai";
21
21
  import { APP_NAME, VERSION } from "../config.js";
22
22
  import { theme } from "../modes/interactive/theme/theme.js";
23
23
  import { stripFrontmatter } from "../utils/frontmatter.js";
@@ -72,14 +72,15 @@ function firstTextContent(result) {
72
72
  }
73
73
  return undefined;
74
74
  }
75
+ import { createImmutableMessageSnapshot } from "./agent-session-snapshot.js";
75
76
  import { redactCredentialShapedContent } from "./compaction/transaction.js";
76
77
  import { selectContextFilesForModel } from "./model-prompt-policy.js";
77
78
  import { findExactModelReferenceMatch } from "./model-resolver.js";
78
79
  import { computePromptTokenBudget } from "./prompt-budget.js";
79
80
  import { classifyPromptCacheTransition } from "./prompt-cache.js";
80
- import { createPromptSettlementState, reducePromptSettlement, settlePromptIfReady, } from "./prompt-settlement.js";
81
+ import * as promptSettlement from "./prompt-settlement.js";
81
82
  import { expandPromptTemplate } from "./prompt-templates.js";
82
- import { isQuotaExhaustionMessage, isStickySafetyModel, pickFailoverCandidate, resolveProviderResilience, shouldEjectStickySafetyModel, shouldHonorSafetyFailover, stickySafetyBlockMessage, } from "./provider-resilience.js";
83
+ import { isQuotaExhaustionMessage, isStickySafetyModel, isUpstreamUnavailableMessage, pickFailoverCandidate, resolveFailoverCandidates, resolveProviderResilience, sameModelRouteCandidates, shouldEjectStickySafetyModel, shouldHonorSafetyFailover, stickySafetyBlockMessage, } from "./provider-resilience.js";
83
84
  import { computeRetryDelayMs, failoverModelKey, isFailoverTriggerError, isRetryableAssistantError, nextRetryAttempt, retryBudgetForAssistantError, } from "./provider-retry.js";
84
85
  import { getBiasStepsForCell, parseRouterBiasSnapshot } from "./reasoning-router-bias.js";
85
86
  import { classifyTaskV4, deriveRouterFeedbackFeaturesV4, resolveThinkingLevelV4WithUncertainty, TASK_CLASS_THINKING_LEVELS_V4, } from "./reasoning-router-v4.js";
@@ -95,6 +96,7 @@ import { RunResourceLeaseController } from "./run-resource-lease.js";
95
96
  import { SessionBashRuntime } from "./session-bash-runtime.js";
96
97
  import { SessionBashService } from "./session-bash-service.js";
97
98
  import { SessionCompactionService } from "./session-compaction-service.js";
99
+ import { preflightFailureCause, providerFailureCause, runtimeFailureCause, terminationMessage, } from "./session-failure-cause.js";
98
100
  import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
99
101
  import { acquireSessionOwnerLeaseSync } from "./session-owner-lease.js";
100
102
  import { assembleSessionSystemPrompt } from "./session-system-prompt.js";
@@ -171,103 +173,6 @@ function toolNameSetsEqual(left, right) {
171
173
  return (normalizedLeft.length === normalizedRight.length &&
172
174
  normalizedLeft.every((toolName, index) => toolName === normalizedRight[index]));
173
175
  }
174
- function snapshotContractError(reason) {
175
- return new Error(`Finalized message replacement must be a plain serializable snapshot: ${reason}`);
176
- }
177
- /**
178
- * Clone a finalized message replacement into the JSON-compatible snapshot
179
- * persisted by SessionManager. Undefined remains allowed as ordinary optional
180
- * message data: JSON omits object properties with undefined values and writes
181
- * undefined array elements as null.
182
- */
183
- function clonePlainSnapshot(value, ancestors = new WeakSet(), copies = new WeakMap()) {
184
- if (value === null)
185
- return value;
186
- switch (typeof value) {
187
- case "string":
188
- case "boolean":
189
- case "undefined":
190
- return value;
191
- case "number":
192
- if (!Number.isFinite(value)) {
193
- throw snapshotContractError("non-finite number values are not allowed");
194
- }
195
- return value;
196
- case "bigint":
197
- throw snapshotContractError("bigint values are not allowed");
198
- case "function":
199
- case "symbol":
200
- throw snapshotContractError(`${typeof value} values are not allowed`);
201
- case "object":
202
- break;
203
- default:
204
- throw snapshotContractError(`${typeof value} values are not allowed`);
205
- }
206
- if (ancestors.has(value)) {
207
- throw snapshotContractError("cyclic values are not allowed");
208
- }
209
- if (copies.has(value)) {
210
- return copies.get(value);
211
- }
212
- ancestors.add(value);
213
- try {
214
- if (Array.isArray(value)) {
215
- const copy = [];
216
- copy.length = value.length;
217
- copies.set(value, copy);
218
- for (const key of Reflect.ownKeys(value)) {
219
- if (key === "length")
220
- continue;
221
- if (typeof key !== "string" || !/^(0|[1-9]\d*)$/.test(key) || Number(key) >= value.length) {
222
- throw snapshotContractError("arrays may only contain indexed values");
223
- }
224
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
225
- if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
226
- throw snapshotContractError("accessor or non-enumerable properties are not allowed");
227
- }
228
- copy[Number(key)] = clonePlainSnapshot(descriptor.value, ancestors, copies);
229
- }
230
- return copy;
231
- }
232
- const prototype = Object.getPrototypeOf(value);
233
- if (prototype !== Object.prototype && prototype !== null) {
234
- throw snapshotContractError("non-plain objects are not allowed");
235
- }
236
- const copy = Object.create(prototype);
237
- copies.set(value, copy);
238
- for (const key of Reflect.ownKeys(value)) {
239
- if (typeof key !== "string") {
240
- throw snapshotContractError("symbol-keyed properties are not allowed");
241
- }
242
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
243
- if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
244
- throw snapshotContractError("accessor or non-enumerable properties are not allowed");
245
- }
246
- Object.defineProperty(copy, key, {
247
- value: clonePlainSnapshot(descriptor.value, ancestors, copies),
248
- enumerable: true,
249
- configurable: true,
250
- writable: true,
251
- });
252
- }
253
- return copy;
254
- }
255
- finally {
256
- ancestors.delete(value);
257
- }
258
- }
259
- function freezeSnapshot(value, seen = new WeakSet()) {
260
- if (value === null || typeof value !== "object" || seen.has(value))
261
- return value;
262
- seen.add(value);
263
- for (const child of Object.values(value)) {
264
- freezeSnapshot(child, seen);
265
- }
266
- return Object.freeze(value);
267
- }
268
- function createImmutableMessageSnapshot(message) {
269
- return freezeSnapshot(clonePlainSnapshot(message));
270
- }
271
176
  // ============================================================================
272
177
  // AgentSession Class
273
178
  // ============================================================================
@@ -387,7 +292,7 @@ export class AgentSession {
387
292
  _workspaceMutationCount = 0;
388
293
  // Model registry for API key resolution
389
294
  _modelRegistry;
390
- /** Lazy, in-memory only; never shared across sessions or persisted. */
295
+ /** Lazy provider: representations persist per workspace by default; plans stay session-memory-only. */
391
296
  _contextBudgetCacheProvider;
392
297
  _contextCacheInvalidationSnapshot;
393
298
  // Tool registry for extension getTools/setTools
@@ -599,78 +504,6 @@ export class AgentSession {
599
504
  });
600
505
  this._appendReplayEvent(event, details);
601
506
  }
602
- _terminationMessage(value, fallback) {
603
- const redacted = redactSensitiveText(value?.trim() || fallback)
604
- .replace(/\0/g, "")
605
- .slice(0, 512);
606
- return redacted || fallback;
607
- }
608
- _providerFailureCause(message) {
609
- const text = message.errorMessage ?? "";
610
- if (isContextOverflow(message, this.model?.contextWindow ?? 0)) {
611
- return { area: "provider", code: "context_overflow" };
612
- }
613
- // Fable/Claude often emit stop_reason=refusal on benign turns (false positive).
614
- // Match broadly so we never mislabel these as provider.protocol.
615
- if (/stop_reason\s*=\s*(refusal|sensitive)|content\/safety stop|safety stop|provider\.refusal|kind=provider_refusal/i.test(text)) {
616
- return { area: "provider", code: "refusal" };
617
- }
618
- // Quota/billing exhaustion is checked BEFORE the generic 401/403 auth
619
- // patterns: "403 ... usage limit for this billing cycle" is transient per
620
- // cycle and must fail over, not terminate the turn as an auth error.
621
- if (isQuotaExhaustionMessage(text) || /rate.?limit|too many requests|429/i.test(text)) {
622
- return { area: "provider", code: "rate_limit" };
623
- }
624
- if (/auth|unauthori[sz]ed|forbidden|invalid.?api.?key|no api key|401|403|\/login/i.test(text)) {
625
- return { area: "provider", code: "auth" };
626
- }
627
- // Kimi/K3 + OpenAI-compat: orphan tool results after dropped error assistants.
628
- // Sanitize-and-retry (transform-messages drops orphans), not a hard tool fatal.
629
- if (/tool_call_id\s+is\s+not\s+found|tool_call_id\s+not\s+found|unknown\s+tool_call_id/i.test(text)) {
630
- return { area: "provider", code: "protocol" };
631
- }
632
- if (/tool.+timed? out|tool.+timeout/i.test(text))
633
- return { area: "tool", code: "timeout" };
634
- if (/\btool\b/i.test(text) && !/tool_call_id/i.test(text))
635
- return { area: "tool", code: "fatal" };
636
- if (/network|fetch failed|connection|socket|websocket|timed? out|timeout|dns|econn|^terminated$/i.test(text)) {
637
- return { area: "provider", code: "network" };
638
- }
639
- return { area: "provider", code: "protocol" };
640
- }
641
- _classifyPreflightCause(message) {
642
- if (!this.model || /no model|model selected|model is required/i.test(message)) {
643
- return { area: "configuration", code: "invalid" };
644
- }
645
- if (isQuotaExhaustionMessage(message)) {
646
- return { area: "provider", code: "rate_limit" };
647
- }
648
- if (/auth|api key|unauthori[sz]ed|forbidden|401|403|\/login/i.test(message)) {
649
- return { area: "provider", code: "auth" };
650
- }
651
- if (/context.+overflow|context window|too many tokens/i.test(message)) {
652
- return { area: "provider", code: "context_overflow" };
653
- }
654
- if (/duplicate.?result/i.test(message))
655
- return { area: "transcript", code: "duplicate_result" };
656
- if (/orphan.?result/i.test(message))
657
- return { area: "transcript", code: "orphan_result" };
658
- if (/duplicate.?call/i.test(message))
659
- return { area: "transcript", code: "duplicate_call_id" };
660
- if (/transcript|missing.?result/i.test(message))
661
- return { area: "transcript", code: "missing_result" };
662
- if (/compaction/i.test(message))
663
- return { area: "compaction", code: "failed" };
664
- if (/fsync/i.test(message))
665
- return { area: "persistence", code: "fsync_failed" };
666
- if (/lock/i.test(message))
667
- return { area: "persistence", code: "lock_failed" };
668
- if (/append|persist|write/i.test(message))
669
- return { area: "persistence", code: "append_failed" };
670
- if (/tool/i.test(message))
671
- return { area: "tool", code: "fatal" };
672
- return { area: "internal", code: "unclassified" };
673
- }
674
507
  _classifyRunTermination(runId, event) {
675
508
  const timestamp = new Date().toISOString();
676
509
  let assistant;
@@ -699,11 +532,11 @@ export class AgentSession {
699
532
  }
700
533
  else if (assistant.stopReason === "aborted") {
701
534
  cause = this._userAbortRequested ? { area: "user", code: "abort" } : { area: "provider", code: "abort" };
702
- message = this._terminationMessage(assistant.errorMessage, this._userAbortRequested ? "The user aborted the run." : "The provider aborted the run.");
535
+ message = terminationMessage(assistant.errorMessage, this._userAbortRequested ? "The user aborted the run." : "The provider aborted the run.");
703
536
  }
704
537
  else if (assistant.stopReason === "error") {
705
- cause = this._providerFailureCause(assistant);
706
- message = this._terminationMessage(assistant.errorMessage, "The provider request failed.");
538
+ cause = providerFailureCause(assistant, this.model?.contextWindow ?? 0);
539
+ message = terminationMessage(assistant.errorMessage, "The provider request failed.");
707
540
  }
708
541
  else {
709
542
  cause = { area: "completed" };
@@ -733,24 +566,8 @@ export class AgentSession {
733
566
  this._lastTermination = termination;
734
567
  this._emit({ type: "session_termination", termination });
735
568
  }
736
- _runtimeFailureCause(error) {
737
- if (this._pendingRuntimeTerminationCause)
738
- return this._pendingRuntimeTerminationCause;
739
- const code = typeof error === "object" && error !== null && "code" in error ? Reflect.get(error, "code") : undefined;
740
- if (typeof code === "string" &&
741
- new Set(["EACCES", "EDQUOT", "EFBIG", "EIO", "EISDIR", "EMFILE", "ENFILE", "ENOSPC", "EPERM", "EROFS"]).has(code)) {
742
- return { area: "persistence", code: "append_failed" };
743
- }
744
- const message = error instanceof Error ? error.message : String(error);
745
- if (/compaction.+stale|session changed during compaction/i.test(message)) {
746
- return { area: "compaction", code: "stale" };
747
- }
748
- if (/compaction/i.test(message))
749
- return { area: "compaction", code: "failed" };
750
- return { area: "internal", code: "unclassified" };
751
- }
752
569
  _publishRuntimeFailure(error) {
753
- const cause = this._runtimeFailureCause(error);
570
+ const cause = this._pendingRuntimeTerminationCause ?? runtimeFailureCause(error);
754
571
  const runId = this._activeRunId ?? `runtime-${randomUUID()}`;
755
572
  const timestamp = new Date().toISOString();
756
573
  let message = "The AgentSession runtime failed before completing the run.";
@@ -915,7 +732,7 @@ export class AgentSession {
915
732
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
916
733
  }
917
734
  async _getCompactionRequestAuth(model) {
918
- if (this.agent.streamFn === streamSimple) {
735
+ if (isBuiltinStreamFn(this.agent.streamFn)) {
919
736
  return this._getRequiredRequestAuth(model);
920
737
  }
921
738
  const result = await this._modelRegistry.getApiKeyAndHeaders(model);
@@ -1130,6 +947,11 @@ export class AgentSession {
1130
947
  }
1131
948
  else if (event.type === "agent_end") {
1132
949
  await this._extensionRunner.emit({ type: "agent_end", messages: event.messages });
950
+ // `agent_end` fires once per attempt. Extensions that release per-run
951
+ // state need the boundary no retry follows, so mark it explicitly.
952
+ if (!this._willRetryAfterAgentEnd(event)) {
953
+ await this._extensionRunner.emit({ type: "agent_settled", messages: event.messages });
954
+ }
1133
955
  }
1134
956
  else if (event.type === "turn_start") {
1135
957
  this._turnMetricsState = {
@@ -1694,12 +1516,12 @@ export class AgentSession {
1694
1516
  }
1695
1517
  }
1696
1518
  catch (error) {
1697
- outcome =
1698
- this._userAbortRequested || (error instanceof Error && /abort/i.test(error.message)) ? "aborted" : "failed";
1519
+ outcome = "failed";
1699
1520
  this._publishRuntimeFailure(error);
1700
1521
  throw error;
1701
1522
  }
1702
1523
  finally {
1524
+ outcome = promptSettlement.resolvePromptSettlementOutcome(outcome, this._lastTermination?.kind);
1703
1525
  if (resourceLease !== null) {
1704
1526
  // Generation-safe: a stale release after another run acquired the
1705
1527
  // lease is a no-op instead of clobbering the newer cap (§8.1).
@@ -1720,15 +1542,15 @@ export class AgentSession {
1720
1542
  */
1721
1543
  _emitPromptSettledIfReady(input) {
1722
1544
  try {
1723
- let state = createPromptSettlementState(input.promptRunId, input.startedAtEpochMs);
1724
- state = reducePromptSettlement(state, { kind: "terminal", outcome: input.outcome });
1545
+ let state = promptSettlement.createPromptSettlementState(input.promptRunId, input.startedAtEpochMs);
1546
+ state = promptSettlement.reducePromptSettlement(state, { kind: "terminal", outcome: input.outcome });
1725
1547
  if (this.isStreaming) {
1726
- state = reducePromptSettlement(state, { kind: "tool", delta: 1 });
1548
+ state = promptSettlement.reducePromptSettlement(state, { kind: "tool", delta: 1 });
1727
1549
  }
1728
1550
  if (this.agent.hasQueuedMessages()) {
1729
- state = reducePromptSettlement(state, { kind: "continuation", delta: 1 });
1551
+ state = promptSettlement.reducePromptSettlement(state, { kind: "continuation", delta: 1 });
1730
1552
  }
1731
- const settled = settlePromptIfReady(state, Date.now());
1553
+ const settled = promptSettlement.settlePromptIfReady(state, Date.now());
1732
1554
  if (settled.event !== null) {
1733
1555
  input.resourceObservations?.record("prompt_settled_v1", settledObservationFacts(settled.event));
1734
1556
  this._emit(settled.event);
@@ -2017,7 +1839,7 @@ export class AgentSession {
2017
1839
  // Emit input event for extension interception (before skill/template expansion)
2018
1840
  let currentImages = options?.images;
2019
1841
  if (this._extensionRunner.hasHandlers("input")) {
2020
- const inputResult = await this._extensionRunner.emitInput(currentText, currentImages, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
1842
+ const inputResult = await this._extensionRunner.emitInput(currentText, currentImages, options?.source ?? "interactive", this.isStreaming || this.isRetrying ? options?.streamingBehavior : undefined);
2021
1843
  if (inputResult.action === "handled") {
2022
1844
  preflightResult?.(true);
2023
1845
  return;
@@ -2034,8 +1856,8 @@ export class AgentSession {
2034
1856
  expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
2035
1857
  }
2036
1858
  expandedText = redactSensitiveText(expandedText);
2037
- // If streaming, queue via steer() or followUp() based on option
2038
- if (this.isStreaming) {
1859
+ // While a run or its retry backoff owns the session, queue instead of starting a competing top-level prompt.
1860
+ if (this.isStreaming || this.isRetrying) {
2039
1861
  if (!options?.streamingBehavior) {
2040
1862
  throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
2041
1863
  }
@@ -2143,14 +1965,14 @@ export class AgentSession {
2143
1965
  catch (error) {
2144
1966
  preflightResult?.(false);
2145
1967
  const rawMessage = error instanceof Error ? error.message : String(error);
2146
- const cause = this._classifyPreflightCause(rawMessage);
1968
+ const cause = preflightFailureCause(rawMessage, Boolean(this.model));
2147
1969
  const timestamp = new Date().toISOString();
2148
1970
  this._publishTermination(classifySessionTermination({
2149
1971
  sessionId: this.sessionId,
2150
1972
  runId: `preflight-${randomUUID()}`,
2151
1973
  timestamp,
2152
1974
  source: "observed",
2153
- message: this._terminationMessage(rawMessage, "Prompt preflight failed."),
1975
+ message: terminationMessage(rawMessage, "Prompt preflight failed."),
2154
1976
  cause,
2155
1977
  sideEffects: "none",
2156
1978
  ...(this.model ? { provider: this.model.provider, model: this.model.id } : {}),
@@ -2532,7 +2354,7 @@ export class AgentSession {
2532
2354
  }
2533
2355
  async _cycleAvailableModel(direction) {
2534
2356
  const resilience = resolveProviderResilience(this.settingsManager.getProviderResilienceSettings());
2535
- const raw = await this._modelRegistry.getAvailable();
2357
+ const raw = this._modelRegistry.getAvailable();
2536
2358
  // Hard-kill sticky safety models from the cycle list (Fable must not re-enter via hotkey).
2537
2359
  const availableModels = resilience.blockStickySafetyModels
2538
2360
  ? raw.filter((m) => !isStickySafetyModel(m.id, m.provider))
@@ -2736,8 +2558,8 @@ export class AgentSession {
2736
2558
  /**
2737
2559
  * Context-pressure band 0..3 from the projected token estimate over the
2738
2560
  * model context window (reuses estimateProjectedContextTokens). 0..<0.5,
2739
- * 1..<0.75, 2..<0.9, 3..>=0.9. Inert under DEFAULT_WEIGHTS (no pressure
2740
- * coefficient); computed for future calibrated weight presets.
2561
+ * 1..<0.75, 2..<0.9, 3..>=0.9. The released default pressure coefficient
2562
+ * is 1 and participates only after the classifier has prompt evidence.
2741
2563
  */
2742
2564
  _computePressureBucket() {
2743
2565
  const contextWindow = this.model?.contextWindow ?? 0;
@@ -2986,6 +2808,25 @@ export class AgentSession {
2986
2808
  });
2987
2809
  return model;
2988
2810
  }
2811
+ /**
2812
+ * Authenticated failover candidates for compaction summarization, in
2813
+ * resilience-chain order, excluding the primary compaction model itself.
2814
+ * Used when the primary model's quota is exhausted (same-model retry is
2815
+ * useless until reset; another provider can still save the compaction).
2816
+ */
2817
+ _compactionFailoverModels(primary) {
2818
+ const candidates = resolveFailoverCandidates(this.settingsManager.getProviderResilienceSettings());
2819
+ const available = this._modelRegistry.getAvailable();
2820
+ const models = [];
2821
+ for (const candidate of candidates) {
2822
+ if (candidate.provider === primary.provider && candidate.id === primary.id)
2823
+ continue;
2824
+ const resolved = available.find((m) => m.provider === candidate.provider && m.id === candidate.id);
2825
+ if (resolved)
2826
+ models.push(resolved);
2827
+ }
2828
+ return models;
2829
+ }
2989
2830
  /**
2990
2831
  * Manually compact the session context.
2991
2832
  * Aborts current agent operation first.
@@ -3046,7 +2887,7 @@ export class AgentSession {
3046
2887
  }
3047
2888
  else {
3048
2889
  // Generate compaction result
3049
- const result = await compact(preparation, compactionModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }));
2890
+ const result = await compact(preparation, compactionModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }), this._compactionFailoverModels(compactionModel));
3050
2891
  summary = result.summary;
3051
2892
  firstKeptEntryId = result.firstKeptEntryId;
3052
2893
  tokensBefore = result.tokensBefore;
@@ -3089,13 +2930,15 @@ export class AgentSession {
3089
2930
  compactionCode = "aborted";
3090
2931
  else if (stale)
3091
2932
  compactionCode = "stale";
2933
+ else if (isQuotaExhaustionMessage(message))
2934
+ compactionCode = "quota_exhausted";
3092
2935
  const timestamp = new Date().toISOString();
3093
2936
  this._publishTermination(classifySessionTermination({
3094
2937
  sessionId: this.sessionId,
3095
2938
  runId: `compaction-${randomUUID()}`,
3096
2939
  timestamp,
3097
2940
  source: "observed",
3098
- message: this._terminationMessage(message, "Manual compaction failed."),
2941
+ message: terminationMessage(message, "Manual compaction failed."),
3099
2942
  cause: { area: "compaction", code: compactionCode },
3100
2943
  sideEffects: committedCompaction ? "confirmed" : "none",
3101
2944
  ...(this.model ? { provider: this.model.provider, model: this.model.id } : {}),
@@ -3142,7 +2985,7 @@ export class AgentSession {
3142
2985
  /**
3143
2986
  * Effective context window for the upcoming turn.
3144
2987
  * Image-bearing turns with a text-only session model are auto-routed to the
3145
- * vision model (gpt-5.6-luna, 400K) — that window is the real limit. Text-only
2988
+ * vision model (gpt-5.6-luna, 1M) — that window is the real limit. Text-only
3146
2989
  * turns keep the session model's (typically much larger) window.
3147
2990
  */
3148
2991
  _effectiveTurnContextWindow(pendingMessages, sessionWindow) {
@@ -3305,7 +3148,7 @@ export class AgentSession {
3305
3148
  const compactionModel = this._resolveCompactionModel(this.model);
3306
3149
  let apiKey;
3307
3150
  let headers;
3308
- if (this.agent.streamFn === streamSimple) {
3151
+ if (isBuiltinStreamFn(this.agent.streamFn)) {
3309
3152
  const authResult = await this._modelRegistry.getApiKeyAndHeaders(compactionModel);
3310
3153
  if (!authResult.ok || !authResult.apiKey) {
3311
3154
  const providerLabel = compactionModel.provider;
@@ -3377,7 +3220,7 @@ export class AgentSession {
3377
3220
  }
3378
3221
  else {
3379
3222
  // Generate compaction result
3380
- const compactResult = await compact(preparation, compactionModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }));
3223
+ const compactResult = await compact(preparation, compactionModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), this._compactionFailoverModels(compactionModel));
3381
3224
  summary = compactResult.summary;
3382
3225
  firstKeptEntryId = compactResult.firstKeptEntryId;
3383
3226
  tokensBefore = compactResult.tokensBefore;
@@ -3992,6 +3835,41 @@ export class AgentSession {
3992
3835
  // setModel itself blocks sticky targets, not sources — safe.
3993
3836
  await this.setModel(next);
3994
3837
  }
3838
+ /**
3839
+ * Rotate to another authenticated provider route serving the SAME model
3840
+ * family (e.g. openrouter/stealth/ox-alpha ↔ opencode-go/ox-alpha-free ↔
3841
+ * opencode/x-preview-f-free). Used when the current route fails with an
3842
+ * upstream availability error — same-model retry would hammer a dead
3843
+ * endpoint while the identical model is one route away. Visited and
3844
+ * unauthenticated routes are marked in {@link _refusedModels} so repeat
3845
+ * failures advance the rotation within the turn.
3846
+ */
3847
+ async _maybeRotateModelRoutes() {
3848
+ const current = this.model;
3849
+ if (!current)
3850
+ return undefined;
3851
+ const candidates = sameModelRouteCandidates(current, this._modelRegistry.getAvailable());
3852
+ for (const candidate of candidates) {
3853
+ const key = failoverModelKey(candidate.provider, candidate.id);
3854
+ if (this._refusedModels.has(key))
3855
+ continue;
3856
+ const next = this._modelRegistry.find(candidate.provider, candidate.id);
3857
+ if (!next || !this._modelRegistry.hasConfiguredAuth(next)) {
3858
+ this._refusedModels.add(key);
3859
+ continue;
3860
+ }
3861
+ try {
3862
+ await this.setModel(next);
3863
+ // The old route just failed upstream — keep it out of this turn's retries.
3864
+ this._refusedModels.add(failoverModelKey(current.provider, current.id));
3865
+ return `${next.provider}/${next.id}`;
3866
+ }
3867
+ catch {
3868
+ this._refusedModels.add(key);
3869
+ }
3870
+ }
3871
+ return undefined;
3872
+ }
3995
3873
  /**
3996
3874
  * Content/safety stops (stop_reason=refusal) are often false positives on coding turns
3997
3875
  * for Fable AND Claude Opus/Sonnet. Retrying the same model usually reprints the refusal —
@@ -4053,12 +3931,19 @@ export class AgentSession {
4053
3931
  if (attempt === undefined)
4054
3932
  return false;
4055
3933
  this._retryAttempt = attempt;
3934
+ // Upstream-unavailable (gateway 5xx / dropped stream): rotate to another
3935
+ // authenticated route of the SAME model family so the retry lands on a
3936
+ // live endpoint instead of hammering the dead one.
3937
+ const rotatedTo = isUpstreamUnavailableMessage(message.errorMessage)
3938
+ ? await this._maybeRotateModelRoutes()
3939
+ : undefined;
4056
3940
  // Content/safety stop (Fable/Opus/Sonnet): switch model BEFORE delay so retry is not same-model refusal.
4057
3941
  const failoverTo = await this._maybeFailoverFromSafetyStop(message);
3942
+ const switchedVia = failoverTo ? `failover ${failoverTo}` : rotatedTo ? `route ${rotatedTo}` : undefined;
4058
3943
  // Safety stops are usually immediate false positives — short delay after failover, full backoff otherwise.
4059
- const delayMs = computeRetryDelayMs(settings.baseDelayMs, attempt, failoverTo !== undefined);
4060
- const errorMessage = failoverTo
4061
- ? `${message.errorMessage || "content/safety stop"} → failover ${failoverTo}`
3944
+ const delayMs = computeRetryDelayMs(settings.baseDelayMs, attempt, switchedVia !== undefined);
3945
+ const errorMessage = switchedVia
3946
+ ? `${message.errorMessage || "content/safety stop"} → ${switchedVia}`
4062
3947
  : message.errorMessage || "Unknown error";
4063
3948
  this._emit({
4064
3949
  type: "auto_retry_start",