@code-yeongyu/senpi 2026.9.27-4 → 2026.9.28-2

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 (294) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/bundle/chunks/{app-server-command-3GVJZFFH.js → app-server-command-H6LFICZ2.js} +1 -1
  3. package/dist/bundle/chunks/{azure-openai-responses-4SUOOZZ4.js → azure-openai-responses-FCZNBRWE.js} +1 -1
  4. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  5. package/dist/bundle/chunks/{chunk-PSCNHY7D.js → chunk-3GMFUEU7.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-HBG6SQU5.js → chunk-62XHQXCZ.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-EJKCBRYK.js → chunk-6BWCPOCV.js} +4 -4
  8. package/dist/bundle/chunks/{chunk-SLL375O7.js → chunk-6VD3EBQE.js} +1 -1
  9. package/dist/bundle/chunks/chunk-7YUGCTEV.js +15 -0
  10. package/dist/bundle/chunks/{chunk-FWBSGMAE.js → chunk-A4UMPXQF.js} +1 -1
  11. package/dist/bundle/chunks/chunk-BBK6SSYT.js +2 -0
  12. package/dist/bundle/chunks/{chunk-C2425O2Q.js → chunk-CNQ4JB45.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-27IFXX3B.js → chunk-DRN4FQCE.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-22MGY5JQ.js → chunk-EHSZ7UCW.js} +1 -1
  15. package/dist/bundle/chunks/chunk-FHJYIU3U.js +5 -0
  16. package/dist/bundle/chunks/chunk-GJUCSTAG.js +19 -0
  17. package/dist/bundle/chunks/chunk-KOPTADA3.js +8 -0
  18. package/dist/bundle/chunks/{chunk-VMKAYXHL.js → chunk-LSW7QSLV.js} +31 -26
  19. package/dist/bundle/chunks/{chunk-2BMD37YV.js → chunk-LUPPSEQW.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-NRGMT74E.js → chunk-QOVUQZO4.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-IKXPBYSO.js → chunk-SICBVLRH.js} +1 -1
  22. package/dist/bundle/chunks/chunk-SN4ZHPMN.js +135 -0
  23. package/dist/bundle/chunks/{chunk-MUTD2NOH.js → chunk-TFA3YAJK.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-4T4GFAGK.js → chunk-VFSXUT7K.js} +1 -1
  25. package/dist/bundle/chunks/{cli-main-66NFBKVH.js → cli-main-GYRTYPMG.js} +1 -1
  26. package/dist/bundle/chunks/{google-generative-ai-ZTGKZTGU.js → google-generative-ai-LFCBHG3Y.js} +1 -1
  27. package/dist/bundle/chunks/{google-vertex-CCZ376YK.js → google-vertex-C474FXU7.js} +1 -1
  28. package/dist/bundle/chunks/{help-fast-path-NZ3I2UEN.js → help-fast-path-3U3YDEZ6.js} +1 -1
  29. package/dist/bundle/chunks/{host-command-RORPAC5X.js → host-command-6CJ7YM6W.js} +1 -1
  30. package/dist/bundle/chunks/{host-lifecycle-UDMJGLOR.js → host-lifecycle-RZIMOFDM.js} +1 -1
  31. package/dist/bundle/chunks/host-lifecycle.js +6 -6
  32. package/dist/bundle/chunks/interactive-host-runtime-A3GEIFTU.js +4 -0
  33. package/dist/bundle/chunks/{interactive-mode-LOAADKLH.js → interactive-mode-LX665HTP.js} +1 -1
  34. package/dist/bundle/chunks/{list-tips-S5CANO5W.js → list-tips-S4U7TKER.js} +1 -1
  35. package/dist/bundle/chunks/multi-session-host-MFTUM6XQ.js +21 -0
  36. package/dist/bundle/chunks/{openai-codex-responses-YSGZZDD5.js → openai-codex-responses-2LDIFL6Z.js} +1 -1
  37. package/dist/bundle/chunks/{openai-completions-KWHRWCBN.js → openai-completions-7KZLPKYP.js} +1 -1
  38. package/dist/bundle/chunks/{openai-images-6U5FCN5S.js → openai-images-I7DR7ERP.js} +1 -1
  39. package/dist/bundle/chunks/{openai-responses-7PWMBGYX.js → openai-responses-LCHS3AF3.js} +1 -1
  40. package/dist/bundle/chunks/{openrouter-images-QIKWQZUD.js → openrouter-images-IPNDRK6C.js} +1 -1
  41. package/dist/bundle/chunks/{package-manager-cli-5EEDA35I.js → package-manager-cli-6IAE5WG3.js} +1 -1
  42. package/dist/bundle/chunks/{rotation-stream-KZG4OVR7.js → rotation-stream-ZVJEGA2G.js} +1 -1
  43. package/dist/bundle/chunks/{rpc-mode-HOEA5NGQ.js → rpc-mode-XN3HNZMH.js} +1 -1
  44. package/dist/bundle/chunks/session-picker-FPKB4DAC.js +2 -0
  45. package/dist/bundle/chunks/session-worker.js +318 -309
  46. package/dist/bundle/cli.js +1 -1
  47. package/dist/bundle/index.js +1 -1
  48. package/dist/bundle/rpc-entry.js +1 -1
  49. package/dist/cli/args.js +1 -1
  50. package/dist/cli/args.js.map +1 -1
  51. package/dist/cli/host-command.d.ts +8 -1
  52. package/dist/cli/host-command.d.ts.map +1 -1
  53. package/dist/cli/host-command.js +66 -14
  54. package/dist/cli/host-command.js.map +1 -1
  55. package/dist/core/agent-session.d.ts +18 -1
  56. package/dist/core/agent-session.d.ts.map +1 -1
  57. package/dist/core/agent-session.js +119 -3
  58. package/dist/core/agent-session.js.map +1 -1
  59. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +2 -2
  60. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +13 -5
  62. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
  63. package/dist/core/extensions/builtin/tool-search/native-search.d.ts.map +1 -1
  64. package/dist/core/extensions/builtin/tool-search/native-search.js +13 -1
  65. package/dist/core/extensions/builtin/tool-search/native-search.js.map +1 -1
  66. package/dist/core/retry-fallback/candidates.d.ts +26 -0
  67. package/dist/core/retry-fallback/candidates.d.ts.map +1 -0
  68. package/dist/core/retry-fallback/candidates.js +41 -0
  69. package/dist/core/retry-fallback/candidates.js.map +1 -0
  70. package/dist/core/retry-fallback/circuit-probes.d.ts +30 -0
  71. package/dist/core/retry-fallback/circuit-probes.d.ts.map +1 -0
  72. package/dist/core/retry-fallback/circuit-probes.js +60 -0
  73. package/dist/core/retry-fallback/circuit-probes.js.map +1 -0
  74. package/dist/core/retry-fallback/circuit.d.ts +101 -0
  75. package/dist/core/retry-fallback/circuit.d.ts.map +1 -0
  76. package/dist/core/retry-fallback/circuit.js +175 -0
  77. package/dist/core/retry-fallback/circuit.js.map +1 -0
  78. package/dist/core/retry-fallback/controller-types.d.ts +58 -0
  79. package/dist/core/retry-fallback/controller-types.d.ts.map +1 -0
  80. package/dist/core/retry-fallback/controller-types.js +2 -0
  81. package/dist/core/retry-fallback/controller-types.js.map +1 -0
  82. package/dist/core/retry-fallback/controller.d.ts +21 -49
  83. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  84. package/dist/core/retry-fallback/controller.js +88 -42
  85. package/dist/core/retry-fallback/controller.js.map +1 -1
  86. package/dist/core/sdk.d.ts.map +1 -1
  87. package/dist/core/sdk.js +25 -17
  88. package/dist/core/sdk.js.map +1 -1
  89. package/dist/core/session-failure-report.d.ts +33 -0
  90. package/dist/core/session-failure-report.d.ts.map +1 -0
  91. package/dist/core/session-failure-report.js +62 -0
  92. package/dist/core/session-failure-report.js.map +1 -0
  93. package/dist/core/settings-manager.d.ts +3 -0
  94. package/dist/core/settings-manager.d.ts.map +1 -1
  95. package/dist/core/settings-manager.js +4 -0
  96. package/dist/core/settings-manager.js.map +1 -1
  97. package/dist/core/slash-commands.d.ts.map +1 -1
  98. package/dist/core/slash-commands.js +1 -0
  99. package/dist/core/slash-commands.js.map +1 -1
  100. package/dist/index.d.ts +1 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +1 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/modes/index.d.ts +1 -0
  105. package/dist/modes/index.d.ts.map +1 -1
  106. package/dist/modes/index.js +2 -0
  107. package/dist/modes/index.js.map +1 -1
  108. package/dist/modes/interactive/help-content.d.ts.map +1 -1
  109. package/dist/modes/interactive/help-content.js +3 -0
  110. package/dist/modes/interactive/help-content.js.map +1 -1
  111. package/dist/modes/interactive/interactive-host-attach.d.ts +10 -0
  112. package/dist/modes/interactive/interactive-host-attach.d.ts.map +1 -0
  113. package/dist/modes/interactive/interactive-host-attach.js +14 -0
  114. package/dist/modes/interactive/interactive-host-attach.js.map +1 -0
  115. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  116. package/dist/modes/interactive/interactive-host-runtime.js +3 -6
  117. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  118. package/dist/modes/interactive/interactive-mode.d.ts +12 -0
  119. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  120. package/dist/modes/interactive/interactive-mode.js +95 -1
  121. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  122. package/dist/modes/interactive/session-failure-info.d.ts +3 -0
  123. package/dist/modes/interactive/session-failure-info.d.ts.map +1 -0
  124. package/dist/modes/interactive/session-failure-info.js +21 -0
  125. package/dist/modes/interactive/session-failure-info.js.map +1 -0
  126. package/dist/modes/interactive/tips/catalog/model-tips.d.ts +7 -7
  127. package/dist/modes/interactive/tips/catalog/model-tips.d.ts.map +1 -1
  128. package/dist/modes/interactive/tips/catalog/model-tips.js +7 -1
  129. package/dist/modes/interactive/tips/catalog/model-tips.js.map +1 -1
  130. package/dist/modes/interactive/tips/catalog/session-tips.d.ts.map +1 -1
  131. package/dist/modes/interactive/tips/catalog/session-tips.js +1 -1
  132. package/dist/modes/interactive/tips/catalog/session-tips.js.map +1 -1
  133. package/dist/modes/rpc/host-attach-hold.d.ts +16 -0
  134. package/dist/modes/rpc/host-attach-hold.d.ts.map +1 -0
  135. package/dist/modes/rpc/host-attach-hold.js +9 -0
  136. package/dist/modes/rpc/host-attach-hold.js.map +1 -0
  137. package/dist/modes/rpc/host-cleanup-paths.d.ts +20 -0
  138. package/dist/modes/rpc/host-cleanup-paths.d.ts.map +1 -0
  139. package/dist/modes/rpc/host-cleanup-paths.js +13 -0
  140. package/dist/modes/rpc/host-cleanup-paths.js.map +1 -0
  141. package/dist/modes/rpc/host-client-occupancy.d.ts +24 -0
  142. package/dist/modes/rpc/host-client-occupancy.d.ts.map +1 -0
  143. package/dist/modes/rpc/host-client-occupancy.js +53 -0
  144. package/dist/modes/rpc/host-client-occupancy.js.map +1 -0
  145. package/dist/modes/rpc/host-daemon-paths.d.ts +61 -8
  146. package/dist/modes/rpc/host-daemon-paths.d.ts.map +1 -1
  147. package/dist/modes/rpc/host-daemon-paths.js +128 -11
  148. package/dist/modes/rpc/host-daemon-paths.js.map +1 -1
  149. package/dist/modes/rpc/host-daemon-registration.d.ts +11 -1
  150. package/dist/modes/rpc/host-daemon-registration.d.ts.map +1 -1
  151. package/dist/modes/rpc/host-daemon-registration.js +28 -15
  152. package/dist/modes/rpc/host-daemon-registration.js.map +1 -1
  153. package/dist/modes/rpc/host-endpoints.d.ts +12 -0
  154. package/dist/modes/rpc/host-endpoints.d.ts.map +1 -0
  155. package/dist/modes/rpc/host-endpoints.js +59 -0
  156. package/dist/modes/rpc/host-endpoints.js.map +1 -0
  157. package/dist/modes/rpc/host-ensure-lock.d.ts +11 -0
  158. package/dist/modes/rpc/host-ensure-lock.d.ts.map +1 -0
  159. package/dist/modes/rpc/host-ensure-lock.js +49 -0
  160. package/dist/modes/rpc/host-ensure-lock.js.map +1 -0
  161. package/dist/modes/rpc/host-ensure.d.ts +7 -0
  162. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  163. package/dist/modes/rpc/host-ensure.js +42 -65
  164. package/dist/modes/rpc/host-ensure.js.map +1 -1
  165. package/dist/modes/rpc/host-gc-evidence.d.ts +18 -0
  166. package/dist/modes/rpc/host-gc-evidence.d.ts.map +1 -0
  167. package/dist/modes/rpc/host-gc-evidence.js +128 -0
  168. package/dist/modes/rpc/host-gc-evidence.js.map +1 -0
  169. package/dist/modes/rpc/host-gc.d.ts +28 -0
  170. package/dist/modes/rpc/host-gc.d.ts.map +1 -0
  171. package/dist/modes/rpc/host-gc.js +110 -0
  172. package/dist/modes/rpc/host-gc.js.map +1 -0
  173. package/dist/modes/rpc/host-generations.d.ts +10 -2
  174. package/dist/modes/rpc/host-generations.d.ts.map +1 -1
  175. package/dist/modes/rpc/host-generations.js +12 -7
  176. package/dist/modes/rpc/host-generations.js.map +1 -1
  177. package/dist/modes/rpc/host-handoff.d.ts +6 -0
  178. package/dist/modes/rpc/host-handoff.d.ts.map +1 -1
  179. package/dist/modes/rpc/host-handoff.js +31 -4
  180. package/dist/modes/rpc/host-handoff.js.map +1 -1
  181. package/dist/modes/rpc/host-lifecycle-policy.d.ts +53 -0
  182. package/dist/modes/rpc/host-lifecycle-policy.d.ts.map +1 -0
  183. package/dist/modes/rpc/host-lifecycle-policy.js +64 -0
  184. package/dist/modes/rpc/host-lifecycle-policy.js.map +1 -0
  185. package/dist/modes/rpc/host-lifecycle.d.ts +1 -44
  186. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  187. package/dist/modes/rpc/host-lifecycle.js +23 -85
  188. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  189. package/dist/modes/rpc/host-observe-request.d.ts +17 -0
  190. package/dist/modes/rpc/host-observe-request.d.ts.map +1 -0
  191. package/dist/modes/rpc/host-observe-request.js +30 -0
  192. package/dist/modes/rpc/host-observe-request.js.map +1 -0
  193. package/dist/modes/rpc/host-probe.d.ts +12 -0
  194. package/dist/modes/rpc/host-probe.d.ts.map +1 -1
  195. package/dist/modes/rpc/host-probe.js +28 -1
  196. package/dist/modes/rpc/host-probe.js.map +1 -1
  197. package/dist/modes/rpc/host-protocol-info.d.ts +2 -0
  198. package/dist/modes/rpc/host-protocol-info.d.ts.map +1 -1
  199. package/dist/modes/rpc/host-protocol-info.js +1 -0
  200. package/dist/modes/rpc/host-protocol-info.js.map +1 -1
  201. package/dist/modes/rpc/host-readiness.d.ts +22 -0
  202. package/dist/modes/rpc/host-readiness.d.ts.map +1 -0
  203. package/dist/modes/rpc/host-readiness.js +44 -0
  204. package/dist/modes/rpc/host-readiness.js.map +1 -0
  205. package/dist/modes/rpc/host-runner.d.ts +13 -0
  206. package/dist/modes/rpc/host-runner.d.ts.map +1 -1
  207. package/dist/modes/rpc/host-runner.js +23 -4
  208. package/dist/modes/rpc/host-runner.js.map +1 -1
  209. package/dist/modes/rpc/host-status-all.d.ts +31 -0
  210. package/dist/modes/rpc/host-status-all.d.ts.map +1 -0
  211. package/dist/modes/rpc/host-status-all.js +78 -0
  212. package/dist/modes/rpc/host-status-all.js.map +1 -0
  213. package/dist/modes/rpc/host-status-rows.d.ts +35 -0
  214. package/dist/modes/rpc/host-status-rows.d.ts.map +1 -0
  215. package/dist/modes/rpc/host-status-rows.js +42 -0
  216. package/dist/modes/rpc/host-status-rows.js.map +1 -0
  217. package/dist/modes/rpc/host-status.d.ts +32 -1
  218. package/dist/modes/rpc/host-status.d.ts.map +1 -1
  219. package/dist/modes/rpc/host-status.js +33 -27
  220. package/dist/modes/rpc/host-status.js.map +1 -1
  221. package/dist/modes/rpc/host-successor.d.ts +3 -1
  222. package/dist/modes/rpc/host-successor.d.ts.map +1 -1
  223. package/dist/modes/rpc/host-successor.js +105 -42
  224. package/dist/modes/rpc/host-successor.js.map +1 -1
  225. package/dist/modes/rpc/host-watchdog.d.ts +6 -0
  226. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  227. package/dist/modes/rpc/host-watchdog.js +12 -13
  228. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  229. package/dist/modes/rpc/loop-lag-watchdog.d.ts +16 -0
  230. package/dist/modes/rpc/loop-lag-watchdog.d.ts.map +1 -1
  231. package/dist/modes/rpc/loop-lag-watchdog.js +34 -2
  232. package/dist/modes/rpc/loop-lag-watchdog.js.map +1 -1
  233. package/dist/modes/rpc/multi-session-host.d.ts +2 -1
  234. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  235. package/dist/modes/rpc/multi-session-host.js +33 -6
  236. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  237. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  238. package/dist/modes/rpc/rpc-mode.js +2 -2
  239. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  240. package/dist/modes/rpc/rpc-types.d.ts +16 -0
  241. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  242. package/dist/modes/rpc/rpc-types.js.map +1 -1
  243. package/dist/modes/rpc/session-command-router.d.ts +9 -1
  244. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  245. package/dist/modes/rpc/session-command-router.js +4 -1
  246. package/dist/modes/rpc/session-command-router.js.map +1 -1
  247. package/dist/modes/rpc/session-path-key.d.ts +3 -1
  248. package/dist/modes/rpc/session-path-key.d.ts.map +1 -1
  249. package/dist/modes/rpc/session-path-key.js +3 -1
  250. package/dist/modes/rpc/session-path-key.js.map +1 -1
  251. package/docs/rpc.md +186 -19
  252. package/docs/settings.md +25 -1
  253. package/docs/usage.md +5 -2
  254. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
  255. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  256. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  257. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  258. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  259. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts +6 -0
  260. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts.map +1 -1
  261. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +36 -0
  262. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js.map +1 -1
  263. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +1 -0
  264. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +14 -1
  266. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  268. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  269. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  270. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -1
  271. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +6 -4
  272. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -1
  273. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +3 -0
  274. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  275. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +20 -0
  276. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  277. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts +2 -0
  278. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +1 -1
  279. package/node_modules/@earendil-works/pi-tui/dist/editor-component.js.map +1 -1
  280. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.d.ts +5 -0
  281. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.d.ts.map +1 -1
  282. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.js +7 -0
  283. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.js.map +1 -1
  284. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  285. package/package.json +7 -7
  286. package/dist/bundle/chunks/chunk-3DEXCYNN.js +0 -135
  287. package/dist/bundle/chunks/chunk-5X4KSPK5.js +0 -12
  288. package/dist/bundle/chunks/chunk-KMD5ZONU.js +0 -5
  289. package/dist/bundle/chunks/chunk-PEHZYGYA.js +0 -8
  290. package/dist/bundle/chunks/chunk-PRC5XIIH.js +0 -2
  291. package/dist/bundle/chunks/chunk-QKY33652.js +0 -18
  292. package/dist/bundle/chunks/interactive-host-runtime-TIFT2SKR.js +0 -4
  293. package/dist/bundle/chunks/multi-session-host-GRHKB7Q2.js +0 -21
  294. package/dist/bundle/chunks/session-picker-FTFCQYHH.js +0 -2
@@ -0,0 +1,101 @@
1
+ import type { FallbackLogger } from "./log.ts";
2
+ /**
3
+ * Fallback-chain circuit breaker. An entry that fails out of a configured chain
4
+ * opens a circuit keyed by its base selector (`provider/id`); while it is open,
5
+ * chain resolution skips the entry instead of spending its retry budget again.
6
+ *
7
+ * One breaker is shared by every session in the process that uses the same agent
8
+ * directory - the config that defines the providers and selectors - so a session
9
+ * started by `/new`, `/resume`, or `/fork` (each gets a fresh model runtime) and
10
+ * an in-process subagent do not re-discover a dead provider.
11
+ * The class is clock-free: every call receives `now`, keeping sessions' injected
12
+ * clocks authoritative and tests deterministic.
13
+ */
14
+ export declare const DEFAULT_CIRCUIT_COOLDOWN_MS = 60000;
15
+ export declare const DEFAULT_CIRCUIT_MAX_COOLDOWN_MS: number;
16
+ export interface FallbackCircuitSettings {
17
+ /** First cooldown after an entry fails out of a chain; doubles per consecutive open. 0 disables the breaker. */
18
+ circuitCooldownMs?: number;
19
+ circuitMaxCooldownMs?: number;
20
+ }
21
+ export interface ResolvedFallbackCircuitSettings {
22
+ cooldownMs: number;
23
+ maxCooldownMs: number;
24
+ }
25
+ export interface CircuitOpenRequest {
26
+ now: number;
27
+ cooldownMs: number;
28
+ maxCooldownMs: number;
29
+ retryAfterMs?: number;
30
+ }
31
+ export interface ProbeToken {
32
+ readonly selector: string;
33
+ readonly owner: string;
34
+ readonly generation: number;
35
+ }
36
+ export type CircuitAdmission = {
37
+ kind: "closed";
38
+ } | {
39
+ kind: "open";
40
+ } | {
41
+ kind: "probe";
42
+ token: ProbeToken;
43
+ };
44
+ export declare function resolveFallbackCircuitSettings(settings: FallbackCircuitSettings | undefined): ResolvedFallbackCircuitSettings;
45
+ export declare class FallbackCircuitBreaker {
46
+ private readonly circuits;
47
+ private generations;
48
+ get size(): number;
49
+ open(selector: string, request: CircuitOpenRequest): number;
50
+ isOpen(selector: string, now: number, owner: string): boolean;
51
+ /**
52
+ * Atomic admission: a closed selector passes, a cooling one or one whose probe
53
+ * another requester holds is refused, and a half-open one hands `owner` its
54
+ * single probe under a fresh generation. The probe stays exclusive until its
55
+ * token is released or the circuit settles - it never expires on a timer, so a
56
+ * slow probe cannot overlap a second one, and a stale token never releases a
57
+ * later acquisition.
58
+ */
59
+ admit(selector: string, now: number, owner: string): CircuitAdmission;
60
+ release(token: ProbeToken): void;
61
+ releaseOwnersWithPrefix(prefix: string): void;
62
+ close(selector: string): void;
63
+ has(selector: string): boolean;
64
+ sweep(now: number): void;
65
+ }
66
+ /**
67
+ * A session's requests are admitted per lane, not per session: the foreground
68
+ * turn is one lane, each background probe-back its own. Two lanes of one session
69
+ * never share a probe, so a probe-back in flight keeps the foreground turn off
70
+ * the entry exactly as it keeps a sibling session off.
71
+ */
72
+ export type CircuitLane = "turn" | `probe-back:${number}`;
73
+ export interface FallbackCircuitAccess {
74
+ noteFailure(selector: string, failure: {
75
+ retryAfterMs?: number;
76
+ errorMessage?: string;
77
+ }): void;
78
+ /** Whether the breaker holds any state for the selector, i.e. it, not a session cooldown, governs recovery. */
79
+ governs(selector: string): boolean;
80
+ isOpen(selector: string, lane?: CircuitLane): boolean;
81
+ admit(selector: string, lane?: CircuitLane): CircuitAdmission;
82
+ release(token: ProbeToken): void;
83
+ releaseAll(): void;
84
+ close(selector: string): void;
85
+ }
86
+ export interface FallbackCircuitAccessDeps {
87
+ breaker: FallbackCircuitBreaker;
88
+ owner(): string;
89
+ now(): number;
90
+ settings(): ResolvedFallbackCircuitSettings;
91
+ logger: FallbackLogger;
92
+ }
93
+ export declare function createFallbackCircuitAccess(deps: FallbackCircuitAccessDeps): FallbackCircuitAccess;
94
+ export declare function fallbackCircuitsFor(agentDir: string): FallbackCircuitBreaker;
95
+ /** Holds the agent dir's breaker for one session; `release` is idempotent. */
96
+ export declare function acquireFallbackCircuits(agentDir: string): {
97
+ breaker: FallbackCircuitBreaker;
98
+ release(): void;
99
+ };
100
+ export declare function monotonicNow(): number;
101
+ //# sourceMappingURL=circuit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit.d.ts","sourceRoot":"","sources":["../../../src/core/retry-fallback/circuit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ import { resolve } from "node:path";
2
+ import { parseRetryAfterMsMarker } from "@earendil-works/pi-ai/utils/retry-hint";
3
+ /**
4
+ * Fallback-chain circuit breaker. An entry that fails out of a configured chain
5
+ * opens a circuit keyed by its base selector (`provider/id`); while it is open,
6
+ * chain resolution skips the entry instead of spending its retry budget again.
7
+ *
8
+ * One breaker is shared by every session in the process that uses the same agent
9
+ * directory - the config that defines the providers and selectors - so a session
10
+ * started by `/new`, `/resume`, or `/fork` (each gets a fresh model runtime) and
11
+ * an in-process subagent do not re-discover a dead provider.
12
+ * The class is clock-free: every call receives `now`, keeping sessions' injected
13
+ * clocks authoritative and tests deterministic.
14
+ */
15
+ export const DEFAULT_CIRCUIT_COOLDOWN_MS = 60_000;
16
+ export const DEFAULT_CIRCUIT_MAX_COOLDOWN_MS = 30 * 60_000;
17
+ function nonNegativeMs(value, fallback) {
18
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
19
+ }
20
+ export function resolveFallbackCircuitSettings(settings) {
21
+ const cooldownMs = nonNegativeMs(settings?.circuitCooldownMs, DEFAULT_CIRCUIT_COOLDOWN_MS);
22
+ const maxCooldownMs = nonNegativeMs(settings?.circuitMaxCooldownMs, DEFAULT_CIRCUIT_MAX_COOLDOWN_MS);
23
+ return { cooldownMs, maxCooldownMs: Math.max(cooldownMs, maxCooldownMs) };
24
+ }
25
+ export class FallbackCircuitBreaker {
26
+ constructor() {
27
+ this.circuits = new Map();
28
+ this.generations = 0;
29
+ }
30
+ get size() {
31
+ return this.circuits.size;
32
+ }
33
+ open(selector, request) {
34
+ const previous = this.circuits.get(selector);
35
+ const consecutiveOpens = (previous?.consecutiveOpens ?? 0) + 1;
36
+ const cooldownMs = Math.min(request.cooldownMs * 2 ** (consecutiveOpens - 1), request.maxCooldownMs);
37
+ const retryAfterMs = request.retryAfterMs;
38
+ const hinted = retryAfterMs !== undefined && Number.isFinite(retryAfterMs) && retryAfterMs > 0
39
+ ? request.now + retryAfterMs
40
+ : 0;
41
+ const retryFloorUntil = Math.max(hinted, previous && previous.retryFloorUntil > request.now ? previous.retryFloorUntil : 0);
42
+ const openUntil = Math.max(request.now + cooldownMs, retryFloorUntil);
43
+ this.circuits.set(selector, {
44
+ openUntil,
45
+ retryFloorUntil,
46
+ consecutiveOpens,
47
+ cooldownMs,
48
+ maxCooldownMs: request.maxCooldownMs,
49
+ probe: undefined,
50
+ });
51
+ return openUntil;
52
+ }
53
+ isOpen(selector, now, owner) {
54
+ const circuit = this.circuits.get(selector);
55
+ if (!circuit)
56
+ return false;
57
+ if (now < circuit.openUntil)
58
+ return true;
59
+ return circuit.probe !== undefined && circuit.probe.owner !== owner;
60
+ }
61
+ /**
62
+ * Atomic admission: a closed selector passes, a cooling one or one whose probe
63
+ * another requester holds is refused, and a half-open one hands `owner` its
64
+ * single probe under a fresh generation. The probe stays exclusive until its
65
+ * token is released or the circuit settles - it never expires on a timer, so a
66
+ * slow probe cannot overlap a second one, and a stale token never releases a
67
+ * later acquisition.
68
+ */
69
+ admit(selector, now, owner) {
70
+ const circuit = this.circuits.get(selector);
71
+ if (!circuit)
72
+ return { kind: "closed" };
73
+ if (now < circuit.openUntil)
74
+ return { kind: "open" };
75
+ if (circuit.probe && circuit.probe.owner !== owner)
76
+ return { kind: "open" };
77
+ circuit.probe ??= { owner, generation: ++this.generations };
78
+ return { kind: "probe", token: { selector, owner, generation: circuit.probe.generation } };
79
+ }
80
+ release(token) {
81
+ const probe = this.circuits.get(token.selector)?.probe;
82
+ if (probe && probe.owner === token.owner && probe.generation === token.generation) {
83
+ const circuit = this.circuits.get(token.selector);
84
+ if (circuit)
85
+ circuit.probe = undefined;
86
+ }
87
+ }
88
+ releaseOwnersWithPrefix(prefix) {
89
+ for (const circuit of this.circuits.values()) {
90
+ if (circuit.probe?.owner.startsWith(prefix))
91
+ circuit.probe = undefined;
92
+ }
93
+ }
94
+ close(selector) {
95
+ this.circuits.delete(selector);
96
+ }
97
+ has(selector) {
98
+ return this.circuits.has(selector);
99
+ }
100
+ sweep(now) {
101
+ for (const [selector, circuit] of this.circuits) {
102
+ if (!circuit.probe && now >= circuit.openUntil + circuit.maxCooldownMs)
103
+ this.circuits.delete(selector);
104
+ }
105
+ }
106
+ }
107
+ export function createFallbackCircuitAccess(deps) {
108
+ const enabled = () => deps.settings().cooldownMs > 0;
109
+ return {
110
+ noteFailure(selector, failure) {
111
+ if (!enabled())
112
+ return;
113
+ const { cooldownMs, maxCooldownMs } = deps.settings();
114
+ const now = deps.now();
115
+ const retryAfterMs = failure.retryAfterMs ??
116
+ (failure.errorMessage === undefined ? undefined : parseRetryAfterMsMarker(failure.errorMessage));
117
+ deps.breaker.sweep(now);
118
+ const openUntil = deps.breaker.open(selector, { now, cooldownMs, maxCooldownMs, retryAfterMs });
119
+ deps.logger.info("circuit_opened", { selector, durationMs: openUntil - now, retryAfterMs });
120
+ },
121
+ governs: (selector) => enabled() && deps.breaker.has(selector),
122
+ isOpen: (selector, lane = "turn") => enabled() && deps.breaker.isOpen(selector, deps.now(), laneOwner(deps.owner(), lane)),
123
+ admit: (selector, lane = "turn") => enabled() ? deps.breaker.admit(selector, deps.now(), laneOwner(deps.owner(), lane)) : { kind: "closed" },
124
+ release: (token) => deps.breaker.release(token),
125
+ releaseAll: () => deps.breaker.releaseOwnersWithPrefix(`${deps.owner()}:`),
126
+ close: (selector) => deps.breaker.close(selector),
127
+ };
128
+ }
129
+ function laneOwner(session, lane) {
130
+ return `${session}:${lane}`;
131
+ }
132
+ const breakersByAgentDir = new Map();
133
+ /**
134
+ * An entry leaves the registry only when no live session holds it and it has no
135
+ * circuit left to share; a held breaker keeps its identity however empty it is.
136
+ */
137
+ function evictUnheldEmptyBreakers() {
138
+ for (const [key, entry] of breakersByAgentDir) {
139
+ if (entry.owners === 0 && entry.breaker.size === 0)
140
+ breakersByAgentDir.delete(key);
141
+ }
142
+ }
143
+ function entryFor(agentDir) {
144
+ const key = resolve(agentDir);
145
+ let entry = breakersByAgentDir.get(key);
146
+ if (!entry) {
147
+ evictUnheldEmptyBreakers();
148
+ entry = { breaker: new FallbackCircuitBreaker(), owners: 0 };
149
+ breakersByAgentDir.set(key, entry);
150
+ }
151
+ return entry;
152
+ }
153
+ export function fallbackCircuitsFor(agentDir) {
154
+ return entryFor(agentDir).breaker;
155
+ }
156
+ /** Holds the agent dir's breaker for one session; `release` is idempotent. */
157
+ export function acquireFallbackCircuits(agentDir) {
158
+ const entry = entryFor(agentDir);
159
+ entry.owners++;
160
+ let released = false;
161
+ return {
162
+ breaker: entry.breaker,
163
+ release() {
164
+ if (released)
165
+ return;
166
+ released = true;
167
+ entry.owners--;
168
+ evictUnheldEmptyBreakers();
169
+ },
170
+ };
171
+ }
172
+ export function monotonicNow() {
173
+ return performance.timeOrigin + performance.now();
174
+ }
175
+ //# sourceMappingURL=circuit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit.js","sourceRoot":"","sources":["../../../src/core/retry-fallback/circuit.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport { parseRetryAfterMsMarker } from \"@earendil-works/pi-ai/utils/retry-hint\";\nimport type { FallbackLogger } from \"./log.ts\";\n\n/**\n * Fallback-chain circuit breaker. An entry that fails out of a configured chain\n * opens a circuit keyed by its base selector (`provider/id`); while it is open,\n * chain resolution skips the entry instead of spending its retry budget again.\n *\n * One breaker is shared by every session in the process that uses the same agent\n * directory - the config that defines the providers and selectors - so a session\n * started by `/new`, `/resume`, or `/fork` (each gets a fresh model runtime) and\n * an in-process subagent do not re-discover a dead provider.\n * The class is clock-free: every call receives `now`, keeping sessions' injected\n * clocks authoritative and tests deterministic.\n */\n\nexport const DEFAULT_CIRCUIT_COOLDOWN_MS = 60_000;\nexport const DEFAULT_CIRCUIT_MAX_COOLDOWN_MS = 30 * 60_000;\n\nexport interface FallbackCircuitSettings {\n\t/** First cooldown after an entry fails out of a chain; doubles per consecutive open. 0 disables the breaker. */\n\tcircuitCooldownMs?: number;\n\tcircuitMaxCooldownMs?: number;\n}\n\nexport interface ResolvedFallbackCircuitSettings {\n\tcooldownMs: number;\n\tmaxCooldownMs: number;\n}\n\nexport interface CircuitOpenRequest {\n\tnow: number;\n\tcooldownMs: number;\n\tmaxCooldownMs: number;\n\tretryAfterMs?: number;\n}\n\nexport interface ProbeToken {\n\treadonly selector: string;\n\treadonly owner: string;\n\treadonly generation: number;\n}\n\nexport type CircuitAdmission = { kind: \"closed\" } | { kind: \"open\" } | { kind: \"probe\"; token: ProbeToken };\n\ninterface SelectorCircuit {\n\topenUntil: number;\n\tretryFloorUntil: number;\n\tconsecutiveOpens: number;\n\tcooldownMs: number;\n\tmaxCooldownMs: number;\n\tprobe: { owner: string; generation: number } | undefined;\n}\n\nfunction nonNegativeMs(value: unknown, fallback: number): number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\nexport function resolveFallbackCircuitSettings(\n\tsettings: FallbackCircuitSettings | undefined,\n): ResolvedFallbackCircuitSettings {\n\tconst cooldownMs = nonNegativeMs(settings?.circuitCooldownMs, DEFAULT_CIRCUIT_COOLDOWN_MS);\n\tconst maxCooldownMs = nonNegativeMs(settings?.circuitMaxCooldownMs, DEFAULT_CIRCUIT_MAX_COOLDOWN_MS);\n\treturn { cooldownMs, maxCooldownMs: Math.max(cooldownMs, maxCooldownMs) };\n}\n\nexport class FallbackCircuitBreaker {\n\tprivate readonly circuits = new Map<string, SelectorCircuit>();\n\tprivate generations = 0;\n\n\tget size(): number {\n\t\treturn this.circuits.size;\n\t}\n\n\topen(selector: string, request: CircuitOpenRequest): number {\n\t\tconst previous = this.circuits.get(selector);\n\t\tconst consecutiveOpens = (previous?.consecutiveOpens ?? 0) + 1;\n\t\tconst cooldownMs = Math.min(request.cooldownMs * 2 ** (consecutiveOpens - 1), request.maxCooldownMs);\n\t\tconst retryAfterMs = request.retryAfterMs;\n\t\tconst hinted =\n\t\t\tretryAfterMs !== undefined && Number.isFinite(retryAfterMs) && retryAfterMs > 0\n\t\t\t\t? request.now + retryAfterMs\n\t\t\t\t: 0;\n\t\tconst retryFloorUntil = Math.max(\n\t\t\thinted,\n\t\t\tprevious && previous.retryFloorUntil > request.now ? previous.retryFloorUntil : 0,\n\t\t);\n\t\tconst openUntil = Math.max(request.now + cooldownMs, retryFloorUntil);\n\t\tthis.circuits.set(selector, {\n\t\t\topenUntil,\n\t\t\tretryFloorUntil,\n\t\t\tconsecutiveOpens,\n\t\t\tcooldownMs,\n\t\t\tmaxCooldownMs: request.maxCooldownMs,\n\t\t\tprobe: undefined,\n\t\t});\n\t\treturn openUntil;\n\t}\n\n\tisOpen(selector: string, now: number, owner: string): boolean {\n\t\tconst circuit = this.circuits.get(selector);\n\t\tif (!circuit) return false;\n\t\tif (now < circuit.openUntil) return true;\n\t\treturn circuit.probe !== undefined && circuit.probe.owner !== owner;\n\t}\n\n\t/**\n\t * Atomic admission: a closed selector passes, a cooling one or one whose probe\n\t * another requester holds is refused, and a half-open one hands `owner` its\n\t * single probe under a fresh generation. The probe stays exclusive until its\n\t * token is released or the circuit settles - it never expires on a timer, so a\n\t * slow probe cannot overlap a second one, and a stale token never releases a\n\t * later acquisition.\n\t */\n\tadmit(selector: string, now: number, owner: string): CircuitAdmission {\n\t\tconst circuit = this.circuits.get(selector);\n\t\tif (!circuit) return { kind: \"closed\" };\n\t\tif (now < circuit.openUntil) return { kind: \"open\" };\n\t\tif (circuit.probe && circuit.probe.owner !== owner) return { kind: \"open\" };\n\t\tcircuit.probe ??= { owner, generation: ++this.generations };\n\t\treturn { kind: \"probe\", token: { selector, owner, generation: circuit.probe.generation } };\n\t}\n\n\trelease(token: ProbeToken): void {\n\t\tconst probe = this.circuits.get(token.selector)?.probe;\n\t\tif (probe && probe.owner === token.owner && probe.generation === token.generation) {\n\t\t\tconst circuit = this.circuits.get(token.selector);\n\t\t\tif (circuit) circuit.probe = undefined;\n\t\t}\n\t}\n\n\treleaseOwnersWithPrefix(prefix: string): void {\n\t\tfor (const circuit of this.circuits.values()) {\n\t\t\tif (circuit.probe?.owner.startsWith(prefix)) circuit.probe = undefined;\n\t\t}\n\t}\n\n\tclose(selector: string): void {\n\t\tthis.circuits.delete(selector);\n\t}\n\n\thas(selector: string): boolean {\n\t\treturn this.circuits.has(selector);\n\t}\n\n\tsweep(now: number): void {\n\t\tfor (const [selector, circuit] of this.circuits) {\n\t\t\tif (!circuit.probe && now >= circuit.openUntil + circuit.maxCooldownMs) this.circuits.delete(selector);\n\t\t}\n\t}\n}\n\n/**\n * A session's requests are admitted per lane, not per session: the foreground\n * turn is one lane, each background probe-back its own. Two lanes of one session\n * never share a probe, so a probe-back in flight keeps the foreground turn off\n * the entry exactly as it keeps a sibling session off.\n */\nexport type CircuitLane = \"turn\" | `probe-back:${number}`;\n\nexport interface FallbackCircuitAccess {\n\tnoteFailure(selector: string, failure: { retryAfterMs?: number; errorMessage?: string }): void;\n\t/** Whether the breaker holds any state for the selector, i.e. it, not a session cooldown, governs recovery. */\n\tgoverns(selector: string): boolean;\n\tisOpen(selector: string, lane?: CircuitLane): boolean;\n\tadmit(selector: string, lane?: CircuitLane): CircuitAdmission;\n\trelease(token: ProbeToken): void;\n\treleaseAll(): void;\n\tclose(selector: string): void;\n}\n\nexport interface FallbackCircuitAccessDeps {\n\tbreaker: FallbackCircuitBreaker;\n\towner(): string;\n\tnow(): number;\n\tsettings(): ResolvedFallbackCircuitSettings;\n\tlogger: FallbackLogger;\n}\n\nexport function createFallbackCircuitAccess(deps: FallbackCircuitAccessDeps): FallbackCircuitAccess {\n\tconst enabled = () => deps.settings().cooldownMs > 0;\n\treturn {\n\t\tnoteFailure(selector, failure) {\n\t\t\tif (!enabled()) return;\n\t\t\tconst { cooldownMs, maxCooldownMs } = deps.settings();\n\t\t\tconst now = deps.now();\n\t\t\tconst retryAfterMs =\n\t\t\t\tfailure.retryAfterMs ??\n\t\t\t\t(failure.errorMessage === undefined ? undefined : parseRetryAfterMsMarker(failure.errorMessage));\n\t\t\tdeps.breaker.sweep(now);\n\t\t\tconst openUntil = deps.breaker.open(selector, { now, cooldownMs, maxCooldownMs, retryAfterMs });\n\t\t\tdeps.logger.info(\"circuit_opened\", { selector, durationMs: openUntil - now, retryAfterMs });\n\t\t},\n\t\tgoverns: (selector) => enabled() && deps.breaker.has(selector),\n\t\tisOpen: (selector, lane = \"turn\") =>\n\t\t\tenabled() && deps.breaker.isOpen(selector, deps.now(), laneOwner(deps.owner(), lane)),\n\t\tadmit: (selector, lane = \"turn\") =>\n\t\t\tenabled() ? deps.breaker.admit(selector, deps.now(), laneOwner(deps.owner(), lane)) : { kind: \"closed\" },\n\t\trelease: (token) => deps.breaker.release(token),\n\t\treleaseAll: () => deps.breaker.releaseOwnersWithPrefix(`${deps.owner()}:`),\n\t\tclose: (selector) => deps.breaker.close(selector),\n\t};\n}\n\nfunction laneOwner(session: string, lane: CircuitLane): string {\n\treturn `${session}:${lane}`;\n}\n\ninterface BreakerEntry {\n\treadonly breaker: FallbackCircuitBreaker;\n\towners: number;\n}\n\nconst breakersByAgentDir = new Map<string, BreakerEntry>();\n\n/**\n * An entry leaves the registry only when no live session holds it and it has no\n * circuit left to share; a held breaker keeps its identity however empty it is.\n */\nfunction evictUnheldEmptyBreakers(): void {\n\tfor (const [key, entry] of breakersByAgentDir) {\n\t\tif (entry.owners === 0 && entry.breaker.size === 0) breakersByAgentDir.delete(key);\n\t}\n}\n\nfunction entryFor(agentDir: string): BreakerEntry {\n\tconst key = resolve(agentDir);\n\tlet entry = breakersByAgentDir.get(key);\n\tif (!entry) {\n\t\tevictUnheldEmptyBreakers();\n\t\tentry = { breaker: new FallbackCircuitBreaker(), owners: 0 };\n\t\tbreakersByAgentDir.set(key, entry);\n\t}\n\treturn entry;\n}\n\nexport function fallbackCircuitsFor(agentDir: string): FallbackCircuitBreaker {\n\treturn entryFor(agentDir).breaker;\n}\n\n/** Holds the agent dir's breaker for one session; `release` is idempotent. */\nexport function acquireFallbackCircuits(agentDir: string): { breaker: FallbackCircuitBreaker; release(): void } {\n\tconst entry = entryFor(agentDir);\n\tentry.owners++;\n\tlet released = false;\n\treturn {\n\t\tbreaker: entry.breaker,\n\t\trelease() {\n\t\t\tif (released) return;\n\t\t\treleased = true;\n\t\t\tentry.owners--;\n\t\t\tevictUnheldEmptyBreakers();\n\t\t},\n\t};\n}\n\nexport function monotonicNow(): number {\n\treturn performance.timeOrigin + performance.now();\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
+ import type { Api, Model } from "@earendil-works/pi-ai";
3
+ import type { FallbackCircuitAccess } from "./circuit.ts";
4
+ import type { SelectorCooldowns } from "./cooldown.ts";
5
+ import type { FallbackLogger } from "./log.ts";
6
+ export interface ActiveFallbackState {
7
+ chainKey: string;
8
+ originalSelector: string;
9
+ originalThinkingLevel?: ThinkingLevel;
10
+ lastAppliedThinkingLevel?: ThinkingLevel;
11
+ /** Pin provenance: refusal contributions release on compaction, billing never. */
12
+ pinnedByRefusal: boolean;
13
+ pinnedByBilling: boolean;
14
+ /** Derived OR of the two provenance flags - the only field consumers read. */
15
+ pinned: boolean;
16
+ }
17
+ export type FallbackReason = "transient" | "refusal" | "hard-error" | "billing";
18
+ export type CircuitFailure = {
19
+ errorMessage?: string;
20
+ retryAfterMs?: number;
21
+ };
22
+ export interface FallbackSettings {
23
+ modelFallback: boolean;
24
+ chains: Readonly<Record<string, readonly string[]>>;
25
+ }
26
+ export interface RetryFallbackControllerDeps {
27
+ getSettings(): FallbackSettings;
28
+ registry: {
29
+ find(provider: string, id: string): Model<Api> | undefined;
30
+ getAll(): Model<Api>[];
31
+ /** Ranks bare-selector expansion: OAuth-credential providers come first. */
32
+ isUsingOAuth?(model: Model<Api>): boolean;
33
+ /** Filters bare-selector expansion: a definitive `false` keeps a lane that can never serve out of the chain. */
34
+ isFallbackEligible?(model: Model<Api>): boolean;
35
+ };
36
+ cooldowns: SelectorCooldowns;
37
+ /** Chain-entry circuits shared with every session in the process on the same agent dir. */
38
+ circuits?: FallbackCircuitAccess;
39
+ logger: FallbackLogger;
40
+ switchModel(model: Model<Api>, thinking: ThinkingLevel, reason: "fallback" | "fallback-revert"): Promise<void>;
41
+ emit(event: {
42
+ type: "retry_fallback_applied";
43
+ from: string;
44
+ to: string;
45
+ chainKey: string;
46
+ reason: FallbackReason;
47
+ } | {
48
+ type: "retry_fallback_reverted";
49
+ from: string;
50
+ to: string;
51
+ }): void;
52
+ getCurrentSelector(): {
53
+ model: Model<Api>;
54
+ thinkingLevel?: ThinkingLevel;
55
+ } | undefined;
56
+ isAuthAvailable(provider: string): boolean;
57
+ }
58
+ //# sourceMappingURL=controller-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-types.d.ts","sourceRoot":"","sources":["../../../src/core/retry-fallback/controller-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,CAAC,EAAE,aAAa,CAAC;IACtC,wBAAwB,CAAC,EAAE,aAAa,CAAC;IACzC,kFAAkF;IAClF,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;AAChF,MAAM,MAAM,cAAc,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,2BAA2B;IAC3C,WAAW,IAAI,gBAAgB,CAAC;IAChC,QAAQ,EAAE;QACT,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,4EAA4E;QAC5E,YAAY,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;QAC1C,gHAAgH;QAChH,kBAAkB,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;KAChD,CAAC;IACF,SAAS,EAAE,iBAAiB,CAAC;IAC7B,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,GAAG,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/G,IAAI,CACH,KAAK,EACF;QACA,IAAI,EAAE,wBAAwB,CAAC;QAC/B,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,cAAc,CAAC;KACtB,GACD;QAAE,IAAI,EAAE,yBAAyB,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAC9D,IAAI,CAAC;IACR,kBAAkB,IAAI;QAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAAC,aAAa,CAAC,EAAE,aAAa,CAAA;KAAE,GAAG,SAAS,CAAC;IACvF,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controller-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-types.js","sourceRoot":"","sources":["../../../src/core/retry-fallback/controller-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type { FallbackCircuitAccess } from \"./circuit.ts\";\nimport type { SelectorCooldowns } from \"./cooldown.ts\";\nimport type { FallbackLogger } from \"./log.ts\";\n\nexport interface ActiveFallbackState {\n\tchainKey: string;\n\toriginalSelector: string;\n\toriginalThinkingLevel?: ThinkingLevel;\n\tlastAppliedThinkingLevel?: ThinkingLevel;\n\t/** Pin provenance: refusal contributions release on compaction, billing never. */\n\tpinnedByRefusal: boolean;\n\tpinnedByBilling: boolean;\n\t/** Derived OR of the two provenance flags - the only field consumers read. */\n\tpinned: boolean;\n}\n\nexport type FallbackReason = \"transient\" | \"refusal\" | \"hard-error\" | \"billing\";\nexport type CircuitFailure = { errorMessage?: string; retryAfterMs?: number };\n\nexport interface FallbackSettings {\n\tmodelFallback: boolean;\n\tchains: Readonly<Record<string, readonly string[]>>;\n}\n\nexport interface RetryFallbackControllerDeps {\n\tgetSettings(): FallbackSettings;\n\tregistry: {\n\t\tfind(provider: string, id: string): Model<Api> | undefined;\n\t\tgetAll(): Model<Api>[];\n\t\t/** Ranks bare-selector expansion: OAuth-credential providers come first. */\n\t\tisUsingOAuth?(model: Model<Api>): boolean;\n\t\t/** Filters bare-selector expansion: a definitive `false` keeps a lane that can never serve out of the chain. */\n\t\tisFallbackEligible?(model: Model<Api>): boolean;\n\t};\n\tcooldowns: SelectorCooldowns;\n\t/** Chain-entry circuits shared with every session in the process on the same agent dir. */\n\tcircuits?: FallbackCircuitAccess;\n\tlogger: FallbackLogger;\n\tswitchModel(model: Model<Api>, thinking: ThinkingLevel, reason: \"fallback\" | \"fallback-revert\"): Promise<void>;\n\temit(\n\t\tevent:\n\t\t\t| {\n\t\t\t\t\ttype: \"retry_fallback_applied\";\n\t\t\t\t\tfrom: string;\n\t\t\t\t\tto: string;\n\t\t\t\t\tchainKey: string;\n\t\t\t\t\treason: FallbackReason;\n\t\t\t }\n\t\t\t| { type: \"retry_fallback_reverted\"; from: string; to: string },\n\t): void;\n\tgetCurrentSelector(): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined;\n\tisAuthAvailable(provider: string): boolean;\n}\n"]}
@@ -1,58 +1,14 @@
1
1
  import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
2
  import { type Api, type Model } from "@earendil-works/pi-ai";
3
- import type { SelectorCooldowns } from "./cooldown.ts";
4
- import type { FallbackLogger } from "./log.ts";
5
- export interface ActiveFallbackState {
6
- chainKey: string;
7
- originalSelector: string;
8
- originalThinkingLevel?: ThinkingLevel;
9
- lastAppliedThinkingLevel?: ThinkingLevel;
10
- /** Pin provenance: refusal contributions release on compaction, billing never. */
11
- pinnedByRefusal: boolean;
12
- pinnedByBilling: boolean;
13
- /** Derived OR of the two provenance flags - the only field consumers read. */
14
- pinned: boolean;
15
- }
16
- type FallbackReason = "transient" | "refusal" | "hard-error" | "billing";
17
- interface FallbackSettings {
18
- modelFallback: boolean;
19
- chains: Readonly<Record<string, readonly string[]>>;
20
- }
21
- export interface RetryFallbackControllerDeps {
22
- getSettings(): FallbackSettings;
23
- registry: {
24
- find(provider: string, id: string): Model<Api> | undefined;
25
- getAll(): Model<Api>[];
26
- /** Ranks bare-selector expansion: OAuth-credential providers come first. */
27
- isUsingOAuth?(model: Model<Api>): boolean;
28
- /** Filters bare-selector expansion: a definitive `false` keeps a lane that can never serve out of the chain. */
29
- isFallbackEligible?(model: Model<Api>): boolean;
30
- };
31
- cooldowns: SelectorCooldowns;
32
- logger: FallbackLogger;
33
- switchModel(model: Model<Api>, thinking: ThinkingLevel, reason: "fallback" | "fallback-revert"): Promise<void>;
34
- emit(event: {
35
- type: "retry_fallback_applied";
36
- from: string;
37
- to: string;
38
- chainKey: string;
39
- reason: FallbackReason;
40
- } | {
41
- type: "retry_fallback_reverted";
42
- from: string;
43
- to: string;
44
- }): void;
45
- getCurrentSelector(): {
46
- model: Model<Api>;
47
- thinkingLevel?: ThinkingLevel;
48
- } | undefined;
49
- isAuthAvailable(provider: string): boolean;
50
- }
3
+ import { CircuitProbes } from "./circuit-probes.ts";
4
+ import type { ActiveFallbackState, CircuitFailure, FallbackReason, RetryFallbackControllerDeps } from "./controller-types.ts";
5
+ export type { ActiveFallbackState, RetryFallbackControllerDeps } from "./controller-types.ts";
51
6
  export declare class RetryFallbackController {
52
7
  private readonly deps;
53
8
  private readonly triedSelectors;
54
9
  private state;
55
10
  private lastExhaustedChainKey;
11
+ readonly probes: CircuitProbes;
56
12
  private canonicalCache;
57
13
  constructor(deps: RetryFallbackControllerDeps);
58
14
  get activeState(): Readonly<ActiveFallbackState> | undefined;
@@ -92,13 +48,29 @@ export declare class RetryFallbackController {
92
48
  notifyCompactionApplied(): boolean;
93
49
  /** A user-driven model change abandons the fallback window entirely. */
94
50
  clearForManualModelChange(model: Model<Api>): void;
51
+ /**
52
+ * Records a failure that makes the entry unusable for every session - billing
53
+ * or quota exhaustion, or a transient failure that spent its budget or failed a
54
+ * half-open probe - independent of whether a fallback candidate remains.
55
+ */
56
+ noteHealthFailure(model: Model<Api>, thinkingLevel: ThinkingLevel | undefined, failure: CircuitFailure): void;
57
+ /**
58
+ * Turn-boundary skip: when the current entry's circuit was opened by this or a
59
+ * sibling session, move to the next closed entry without spending a request on
60
+ * it. With no closed entry left the current one stays and probes, so the chain
61
+ * never refuses a turn; a half-open current entry is claimed for this session.
62
+ */
63
+ rerouteAroundOpenCircuit(): Promise<boolean>;
95
64
  tryFallback(reason: FallbackReason, failure: {
96
65
  errorMessage?: string;
97
66
  retryAfterMs?: number;
98
67
  }): Promise<boolean>;
68
+ private applyCandidate;
69
+ /** The chain governing `current`, when fallback is enabled; a model's own chain wins over an active one. */
70
+ private managedChainKey;
99
71
  private nextCandidate;
72
+ private switchOrRelease;
100
73
  private selectThinking;
101
74
  private skip;
102
75
  }
103
- export {};
104
76
  //# sourceMappingURL=controller.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/core/retry-fallback/controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/core/retry-fallback/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,KAAK,GAAG,EAAsB,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAWjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EACX,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,2BAA2B,EAC3B,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAE9F,qBAAa,uBAAuB;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA8B;IACnD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,qBAAqB,CAAqB;IAClD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAQ/B,OAAO,CAAC,cAAc,CAAsD;IAE5E,YAAY,IAAI,EAAE,2BAA2B,EAG5C;IAED,IAAI,WAAW,IAAI,QAAQ,CAAC,mBAAmB,CAAC,GAAG,SAAS,CAE3D;IAED,IAAI,iBAAiB,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED,SAAS,IAAI,IAAI,CAGhB;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,OAAO,CAAC,eAAe;IASvB,cAAc,IAAI,OAAO,CAExB;IAED;;;;;OAKG;IACH,kBAAkB,IAAI,OAAO,CAK5B;IAED;;;;;OAKG;IACG,mBAAmB,CAAC,YAAY,EAAE,iBAAiB,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CA4BrF;IAED;;;OAGG;IACH,uBAAuB,IAAI,IAAI,CAE9B;IAED;;;;;;;;OAQG;IACH,uBAAuB,IAAI,OAAO,CAajC;IAED,wEAAwE;IACxE,yBAAyB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAQjD;IAED;;;;OAIG;IACH,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,aAAa,GAAG,SAAS,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAG5G;IAED;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CAUjD;IAEK,WAAW,CAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC,OAAO,CAAC,CAelB;YAEa,cAAc;IA2B5B,4GAA4G;IAC5G,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;YA2BP,eAAe;IAa7B,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,IAAI;CAGZ"}