@iowarp/clio-coder 0.4.2 → 0.4.4

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 (523) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/CONTRIBUTING.md +86 -19
  3. package/README.md +35 -6
  4. package/dist/{acp-TMDQZDIG.js → acp-WNAYYF4F.js} +12 -13
  5. package/dist/{agents-5N5NG3XG.js → agents-3OKXHLOI.js} +60 -57
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-Z5CCBXKQ.js → auth-VKNNMGPU.js} +21 -19
  8. package/dist/{builtins-K6TNDT24.js → builtins-WGALA46I.js} +9 -4
  9. package/dist/{chunk-XE3PCIXH.js → chunk-23L32XTI.js} +12 -9
  10. package/dist/{chunk-I64IFBLB.js → chunk-25QBEXRS.js} +18 -11
  11. package/dist/{chunk-CDNVLKUX.js → chunk-26QSH3EJ.js} +13 -7
  12. package/dist/{chunk-QQLGQY2A.js → chunk-2ASED4PZ.js} +22 -22
  13. package/dist/{chunk-MCEPRMZW.js → chunk-2CU2H6KE.js} +2 -2
  14. package/dist/chunk-2DSOYNFC.js +108 -0
  15. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  16. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  17. package/dist/{chunk-O3YUNJZ2.js → chunk-2ZSONWVL.js} +82 -25
  18. package/dist/{chunk-2NHR3NAY.js → chunk-36CT5VVL.js} +331 -42
  19. package/dist/{chunk-2X4RYJTJ.js → chunk-3GY4F45V.js} +3 -3
  20. package/dist/{chunk-ZW55JB7N.js → chunk-3ODX73FK.js} +4 -6
  21. package/dist/{chunk-PBP4B7XR.js → chunk-3UNOLWNZ.js} +3 -3
  22. package/dist/{chunk-4JDLP6ZS.js → chunk-3UUXNFEX.js} +14 -10
  23. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  24. package/dist/{chunk-ZW4HH5JJ.js → chunk-4M6Z5QVF.js} +6 -6
  25. package/dist/{chunk-K6BSR66V.js → chunk-4NSRCOYP.js} +4 -1
  26. package/dist/{chunk-M2DAX4F6.js → chunk-4WR7VSYB.js} +2 -2
  27. package/dist/{chunk-FSP7CMNU.js → chunk-54X7T7DK.js} +61 -6
  28. package/dist/{chunk-54ODD65L.js → chunk-5636DCO5.js} +4 -4
  29. package/dist/chunk-57XXR6DR.js +3763 -0
  30. package/dist/{chunk-3KIPBMUA.js → chunk-5ICU3EUH.js} +2 -2
  31. package/dist/{chunk-77QIVUZB.js → chunk-5MEZN6CB.js} +4 -4
  32. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  33. package/dist/{chunk-YJISEZKC.js → chunk-5TUB6SLS.js} +6 -6
  34. package/dist/{chunk-IMXMHHMQ.js → chunk-6OSVSQL5.js} +341 -57
  35. package/dist/{chunk-Q4XWMHX6.js → chunk-6PAZTBPA.js} +14 -2
  36. package/dist/{chunk-FVDGR2ZL.js → chunk-6Q3CYFD3.js} +112 -39
  37. package/dist/{chunk-IDNA72AH.js → chunk-6QOTUPRG.js} +155 -36
  38. package/dist/{chunk-X7IARSHT.js → chunk-6UINWWS6.js} +16 -10
  39. package/dist/{chunk-CYZW7JHJ.js → chunk-72YIHOZQ.js} +9 -9
  40. package/dist/{chunk-IKSLQ4XV.js → chunk-75W7L2E2.js} +752 -861
  41. package/dist/{chunk-CRFOIAX3.js → chunk-7UGL4MB5.js} +6 -6
  42. package/dist/{chunk-HIICAHCJ.js → chunk-AUPNRN7C.js} +2 -2
  43. package/dist/{chunk-7BHIY2MW.js → chunk-BJVFZO5U.js} +8 -50
  44. package/dist/{chunk-B74PXLU7.js → chunk-CUSRQKPU.js} +65 -3
  45. package/dist/chunk-DQOVN6KV.js +386 -0
  46. package/dist/{chunk-E7GT7O5N.js → chunk-DT3LWJOB.js} +7 -4
  47. package/dist/chunk-DXKJURES.js +671 -0
  48. package/dist/{chunk-JBCS7CRR.js → chunk-EL24TAU4.js} +10 -10
  49. package/dist/{chunk-TPEQIQIE.js → chunk-ELWDPP3Y.js} +8 -8
  50. package/dist/{chunk-NDINPTJ4.js → chunk-ELZVTCGV.js} +5 -4
  51. package/dist/chunk-EXLD33WO.js +381 -0
  52. package/dist/chunk-FEAXX7B6.js +101 -0
  53. package/dist/{chunk-RLYRBIYQ.js → chunk-FFUPXJC4.js} +90 -331
  54. package/dist/{chunk-5PFYMY2V.js → chunk-FTMGRKEF.js} +2 -2
  55. package/dist/{chunk-34BHNEE3.js → chunk-GHS5EBTQ.js} +58 -7
  56. package/dist/{chunk-DYHAXKHD.js → chunk-GWZNEVM2.js} +12 -8
  57. package/dist/chunk-GX5WYQO4.js +59 -0
  58. package/dist/chunk-GYV6VZOC.js +26 -0
  59. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  60. package/dist/chunk-I2DWJ4GM.js +390 -0
  61. package/dist/{chunk-TXOTCRLG.js → chunk-I5FWO7L5.js} +5 -5
  62. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  63. package/dist/{chunk-XQRY4DTA.js → chunk-IGLP3ODT.js} +10 -10
  64. package/dist/chunk-IRXAATOX.js +539 -0
  65. package/dist/chunk-IXIY2H4R.js +44 -0
  66. package/dist/{chunk-SSEYRH53.js → chunk-IZXGRF7P.js} +92 -147
  67. package/dist/{chunk-5TSRNF4G.js → chunk-JCI2ROMZ.js} +164 -6
  68. package/dist/{chunk-JWJGP5DQ.js → chunk-JEIYHLOR.js} +7 -7
  69. package/dist/{chunk-F2I26BDK.js → chunk-JQLNNIKT.js} +4 -4
  70. package/dist/{chunk-BYMNWQ7O.js → chunk-JSD46VO2.js} +315 -63
  71. package/dist/{chunk-AK5XEFVZ.js → chunk-JT2RFCC5.js} +64 -14
  72. package/dist/{chunk-MCMZMDAC.js → chunk-K6T2ZAMZ.js} +168 -6
  73. package/dist/{chunk-PGF63K6I.js → chunk-KFV5L5SK.js} +73 -4
  74. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  75. package/dist/{chunk-PJX3WQUQ.js → chunk-LLXSDWXS.js} +3 -3
  76. package/dist/{chunk-DZAW46HP.js → chunk-LTIKRKFL.js} +3 -3
  77. package/dist/{chunk-DZEK6CJN.js → chunk-N56KALIC.js} +21 -21
  78. package/dist/{chunk-B7HM5Z7T.js → chunk-NAI6ZFCY.js} +9 -5
  79. package/dist/{chunk-I66ZTYNP.js → chunk-NRO2BJRH.js} +2656 -2213
  80. package/dist/{chunk-ZGNYYXQ6.js → chunk-NXIMQY5W.js} +3 -3
  81. package/dist/{chunk-IKOZFYBN.js → chunk-NXYCB2VD.js} +149 -106
  82. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  83. package/dist/{chunk-462T4EGZ.js → chunk-O5CVSAG5.js} +2 -2
  84. package/dist/chunk-ODGTEFFI.js +50 -0
  85. package/dist/{chunk-3F7VUY77.js → chunk-OEJSLEPW.js} +2 -2
  86. package/dist/{chunk-KKOJXO6R.js → chunk-OMQNJVKW.js} +4 -2
  87. package/dist/{chunk-5KW52TEP.js → chunk-Q4WO54TA.js} +132 -77
  88. package/dist/{chunk-W6NIE6OW.js → chunk-QUFRYSWI.js} +13 -7
  89. package/dist/{chunk-42FMPA75.js → chunk-QZWQA4DE.js} +2 -2
  90. package/dist/chunk-R6Q67RJH.js +134 -0
  91. package/dist/{chunk-W4YEMFBX.js → chunk-RAY4OVGZ.js} +3 -3
  92. package/dist/{chunk-ZNT2M6TG.js → chunk-RQCKCSRL.js} +17 -17
  93. package/dist/{chunk-LJID3DYZ.js → chunk-RXTN6AKH.js} +3 -3
  94. package/dist/{chunk-P75RZCJW.js → chunk-RZDWV63N.js} +3 -3
  95. package/dist/{chunk-UH632ZYL.js → chunk-S6PYF2XF.js} +2 -2
  96. package/dist/{chunk-HJWWJ6IL.js → chunk-TOIVGRUX.js} +17 -5
  97. package/dist/{chunk-HLAFFSEK.js → chunk-TQAHXW6Y.js} +2 -2
  98. package/dist/{chunk-JIEGK6UF.js → chunk-U6TMQNSI.js} +48 -4
  99. package/dist/{chunk-2HFQNRV3.js → chunk-UEPWCCTY.js} +12 -12
  100. package/dist/chunk-UOIZ7DA4.js +41 -0
  101. package/dist/{chunk-UH347SHR.js → chunk-USR47QNF.js} +11 -11
  102. package/dist/{chunk-AZ4WMN4W.js → chunk-V6HJFQZE.js} +2 -2
  103. package/dist/chunk-V76WTFTW.js +318 -0
  104. package/dist/{chunk-NMJXSHBJ.js → chunk-W54I7H25.js} +2 -2
  105. package/dist/{chunk-KPXDY6QF.js → chunk-XRZT5WY5.js} +2 -2
  106. package/dist/{chunk-UBRFI4HS.js → chunk-XULDXHTN.js} +142 -50
  107. package/dist/chunk-XXYSBZIQ.js +283 -0
  108. package/dist/{chunk-HKMD33FO.js → chunk-Y55JBDO5.js} +405 -122
  109. package/dist/{chunk-XOXV5GKE.js → chunk-YD5GIKET.js} +17 -8
  110. package/dist/{chunk-XGDPUNND.js → chunk-YECAMM3D.js} +2 -2
  111. package/dist/{chunk-BO7Y52RY.js → chunk-YNFKXPEC.js} +7 -7
  112. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  113. package/dist/{chunk-M2WXEHER.js → chunk-ZA4VCIGV.js} +2 -2
  114. package/dist/cli/index.js +42 -40
  115. package/dist/{clio-7VB377CC.js → clio-QLICPCF5.js} +7 -7
  116. package/dist/{code-nav-YVLCYA7V.js → code-nav-IJR2DBPR.js} +9 -9
  117. package/dist/{components-UBWCQSRW.js → components-2TGAI2RC.js} +5 -6
  118. package/dist/{config-4HVOS65E.js → config-IUA6OYNS.js} +88 -81
  119. package/dist/{configure-PIWO7B24.js → configure-VEPX4NMX.js} +26 -25
  120. package/dist/{context-KQYIWPWT.js → context-2DKHWH2T.js} +60 -45
  121. package/dist/{context-IYEHL3WQ.js → context-4MPR7WKB.js} +78 -69
  122. package/dist/{context-N6ZE3LGJ.js → context-BOYF5EJM.js} +15 -11
  123. package/dist/{context-clear-G4OGZJDS.js → context-clear-S4ZJCQUX.js} +73 -65
  124. package/dist/context-map-COB37XXN.js +505 -0
  125. package/dist/{context-working-set-BWLF6LJP.js → context-working-set-3I3FYX6Y.js} +18 -17
  126. package/dist/detail-A7JAVSIG.js +98 -0
  127. package/dist/{dispatch-runner-2QQAITS3.js → dispatch-runner-RJ5I2F2O.js} +99 -75
  128. package/dist/{docs-PD3EXDKU.js → docs-SPOV3BAN.js} +3 -5
  129. package/dist/{doctor-LHBD36VU.js → doctor-DKICC2SN.js} +71 -48
  130. package/dist/{eval-C45FYRJ6.js → eval-OQOQUDHK.js} +308 -146
  131. package/dist/{eval-inventory-6DEJPLBF.js → eval-inventory-Y6QRFOH5.js} +4 -4
  132. package/dist/{evidence-6SHONYAF.js → evidence-4DQ25GUQ.js} +79 -175
  133. package/dist/evidence-4F5USFKH.js +208 -0
  134. package/dist/{evolve-KRKMV72X.js → evolve-GSS52E5J.js} +71 -67
  135. package/dist/{extensions-KPZ2UHBB.js → extensions-G7MFLYHT.js} +8 -9
  136. package/dist/{fleet-IVTCKDHT.js → fleet-Q37YHHAQ.js} +126 -118
  137. package/dist/{fleet-commands-EDWL3IT7.js → fleet-commands-G7E4N7SM.js} +16 -13
  138. package/dist/{fleet-decisions-YP3YEFGK.js → fleet-decisions-O7M6QBA2.js} +9 -8
  139. package/dist/{fleet-graph-ZFWKHY2M.js → fleet-graph-TOUBW6OW.js} +21 -20
  140. package/dist/{fleet-inspect-FVUNCBML.js → fleet-inspect-SW33JJNI.js} +65 -60
  141. package/dist/{fleet-preflight-UN5XED4R.js → fleet-preflight-CV2655TW.js} +4 -5
  142. package/dist/{fleet-validate-XOWC4HSX.js → fleet-validate-KESZX2YH.js} +25 -24
  143. package/dist/{fleet-verify-UN3SODEL.js → fleet-verify-UQPMTVE3.js} +66 -61
  144. package/dist/{fleet-view-TWHJKCN6.js → fleet-view-MN2VG4MR.js} +65 -60
  145. package/dist/{init-T2QORQ3Y.js → init-PXEXQSBF.js} +90 -82
  146. package/dist/{interop-IN5I2A66.js → interop-ZG5T62U3.js} +12 -13
  147. package/dist/inventory-C26CFDRR.js +101 -0
  148. package/dist/{library-LSCATDLZ.js → library-B2W4N74O.js} +29 -29
  149. package/dist/{memory-HYOKAGGJ.js → memory-YCANYS5A.js} +73 -69
  150. package/dist/{models-2GPMFYCM.js → models-GERTU3YI.js} +51 -47
  151. package/dist/{monitor-E4ASVUJH.js → monitor-CPNIUULB.js} +74 -67
  152. package/dist/{orchestrator-DDMPR3PY.js → orchestrator-J4BSH4WQ.js} +1288 -1606
  153. package/dist/{panes-E3RUXOW5.js → panes-BOHAEGYC.js} +4 -4
  154. package/dist/{panes-IXKLOKA2.js → panes-NXSLDQZ2.js} +10 -11
  155. package/dist/{paths-L7LGY6RN.js → paths-VSUWNC22.js} +6 -7
  156. package/dist/reset-TNWTB5LU.js +343 -0
  157. package/dist/{resources-OTRSN34L.js → resources-4PXNMD5G.js} +29 -22
  158. package/dist/{run-5DEYH5QK.js → run-D6XJ34CN.js} +132 -132
  159. package/dist/{share-IHWTLO3M.js → share-2NWMJJEE.js} +27 -27
  160. package/dist/{skills-IYMXMKW4.js → skills-KR7WON5G.js} +40 -33
  161. package/dist/{skills-eval-DROHSJAR.js → skills-eval-O2ZNOLDS.js} +81 -77
  162. package/dist/{skills-inventory-D7X4L4ZX.js → skills-inventory-ZZOUBK7O.js} +23 -22
  163. package/dist/{slash-commands-QBM7UZ3B.js → slash-commands-ZXPJD64J.js} +47 -37
  164. package/dist/{steer-Z5DO23FJ.js → steer-XA25PSCS.js} +4 -4
  165. package/dist/{support-U7QOWY26.js → support-7EMVWYG2.js} +6 -6
  166. package/dist/{targets-P2FUC4IL.js → targets-OMH2XCSN.js} +50 -49
  167. package/dist/tasks-IPAGMEIX.js +36 -0
  168. package/dist/{terminal-lease-YREJ3JX2.js → terminal-lease-C2J3JYRE.js} +4 -4
  169. package/dist/{tools-5B7RO6MV.js → tools-EFFEAIDP.js} +8 -9
  170. package/dist/{trace-YMGMUM6A.js → trace-FXMXUZUF.js} +7 -7
  171. package/dist/uninstall-HALS6BLF.js +407 -0
  172. package/dist/upgrade-MS72RJEP.js +306 -0
  173. package/dist/{usage-ME5MPXGX.js → usage-NHG6MCJM.js} +162 -108
  174. package/dist/{verifiers-BVZ7IWOO.js → verifiers-7AUNVXDY.js} +155 -22
  175. package/dist/{verify-5K7ZKQFC.js → verify-FWYGPKMR.js} +14 -12
  176. package/dist/{web-fetch-MPARV2K7.js → web-fetch-V4FKSDAV.js} +4 -4
  177. package/dist/{wiki-generate-F5W5QTYY.js → wiki-generate-743CIGJW.js} +99 -90
  178. package/dist/{with-panes-BYOJCLAM.js → with-panes-BDQEWBRT.js} +10 -10
  179. package/dist/worker/entry.js +72 -68
  180. package/docs/README.md +3 -2
  181. package/docs/architecture/acp.md +17 -0
  182. package/docs/architecture/artifact-placement.md +1 -0
  183. package/docs/architecture/artifact-versions.md +2 -2
  184. package/docs/architecture/context-engine.md +4 -0
  185. package/docs/architecture/dispatch-typed-intent.md +1 -1
  186. package/docs/architecture/evidence-and-memory.md +1 -1
  187. package/docs/architecture/middleware-and-components.md +1 -1
  188. package/docs/architecture/model-catalog.md +21 -10
  189. package/docs/architecture/observability.md +19 -2
  190. package/docs/architecture/prompt-envelope-and-tools.md +17 -5
  191. package/docs/architecture/provider-adapter-cookbook.md +63 -0
  192. package/docs/architecture/safety-model.md +25 -22
  193. package/docs/architecture/tui-design.md +1 -1
  194. package/docs/guide/built-in-agents.md +25 -11
  195. package/docs/guide/commands-and-modes.md +18 -3
  196. package/docs/guide/configuration-and-targets.md +100 -10
  197. package/docs/guide/configuration-reference.md +17 -7
  198. package/docs/guide/environment-variables.md +4 -2
  199. package/docs/guide/installation-and-lifecycle.md +37 -4
  200. package/docs/guide/proactive-memory.md +66 -55
  201. package/docs/guide/skills-marketplace.md +18 -0
  202. package/docs/guide/tool-usage.md +78 -3
  203. package/docs/history/config-knobs-audit.md +2 -2
  204. package/docs/process/development-pipeline.md +40 -2
  205. package/docs/process/eval-runner.md +67 -3
  206. package/docs/process/git-commit-provenance.md +15 -0
  207. package/docs/process/release-cut-checklist.md +207 -0
  208. package/docs/process/scientific-validation.md +18 -17
  209. package/evals/behavioral-machinery-support.ts +1 -0
  210. package/evals/behavioral-machinery.yaml +1 -1
  211. package/evals/behavioral-model.yaml +3 -2
  212. package/package.json +2 -2
  213. package/skills/README.md +7 -5
  214. package/skills/coding/ast-grep/SKILL.md +101 -30
  215. package/skills/coding/ast-grep/evals.md +26 -0
  216. package/skills/coding/coding-standards/SKILL.md +47 -2
  217. package/skills/coding/coding-standards/evals.md +23 -0
  218. package/skills/coding/prototype/SKILL.md +87 -28
  219. package/skills/coding/prototype/evals.md +19 -0
  220. package/skills/coding/tdd/SKILL.md +80 -53
  221. package/skills/coding/tdd/evals.md +20 -0
  222. package/skills/context/context-handoff/SKILL.md +43 -2
  223. package/skills/context/context-handoff/evals.md +44 -0
  224. package/skills/context/context-prime/SKILL.md +45 -15
  225. package/skills/context/context-prime/evals.md +45 -0
  226. package/skills/git/branch-closeout/SKILL.md +132 -0
  227. package/skills/git/branch-closeout/evals.md +133 -0
  228. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  229. package/skills/git/file-ticket/SKILL.md +77 -63
  230. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  231. package/skills/git/file-ticket/evals.md +31 -26
  232. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  233. package/skills/git/fix-issue/SKILL.md +87 -64
  234. package/skills/git/fix-issue/evals.md +35 -31
  235. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  236. package/skills/git/resolve-merge-conflicts/SKILL.md +100 -51
  237. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  238. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  239. package/skills/git/ship/SKILL.md +103 -67
  240. package/skills/git/ship/assets/pr-template.md +21 -0
  241. package/skills/git/ship/evals.md +44 -28
  242. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  243. package/skills/git/worktree-create/SKILL.md +80 -50
  244. package/skills/git/worktree-create/evals.md +40 -33
  245. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  246. package/skills/git/worktree-merge/SKILL.md +112 -65
  247. package/skills/git/worktree-merge/evals.md +42 -34
  248. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  249. package/skills/planning/archify/SKILL.md +196 -0
  250. package/skills/planning/archify/evals.md +65 -0
  251. package/skills/planning/architecture/SKILL.md +61 -12
  252. package/skills/planning/architecture/evals.md +65 -0
  253. package/skills/planning/backlog/SKILL.md +130 -14
  254. package/skills/planning/backlog/evals.md +142 -0
  255. package/skills/planning/prd/SKILL.md +47 -6
  256. package/skills/planning/prd/evals.md +54 -0
  257. package/skills/planning/product-intent/SKILL.md +57 -2
  258. package/skills/planning/product-intent/evals.md +70 -0
  259. package/skills/planning/tech-spec/SKILL.md +53 -2
  260. package/skills/planning/tech-spec/evals.md +73 -0
  261. package/skills/registry.yaml +58 -50
  262. package/skills/remote.yaml +13 -0
  263. package/skills/research/arxiv-literature/SKILL.md +76 -18
  264. package/skills/research/arxiv-literature/evals.md +50 -0
  265. package/skills/research/experiment-protocol/SKILL.md +20 -1
  266. package/skills/research/experiment-protocol/evals.md +23 -0
  267. package/skills/research/scientific-debugging/SKILL.md +24 -1
  268. package/skills/research/scientific-debugging/evals.md +18 -0
  269. package/skills/research/scientific-modernization/SKILL.md +26 -1
  270. package/skills/research/scientific-modernization/evals.md +27 -0
  271. package/skills/skill-marketplace.json +63 -28
  272. package/skills/workflow/cut-it/SKILL.md +64 -5
  273. package/skills/workflow/cut-it/evals.md +101 -0
  274. package/skills/workflow/design-council/SKILL.md +112 -27
  275. package/skills/workflow/design-council/evals.md +161 -0
  276. package/skills/workflow/grill-me/SKILL.md +85 -10
  277. package/skills/workflow/grill-me/evals.md +153 -0
  278. package/skills/workflow/workflow-distiller/SKILL.md +76 -17
  279. package/skills/workflow/workflow-distiller/evals.md +118 -0
  280. package/src/cli/args.ts +0 -8
  281. package/src/cli/configure-interop.ts +105 -13
  282. package/src/cli/configure-oauth.ts +57 -0
  283. package/src/cli/configure-onboarding.ts +980 -0
  284. package/src/cli/configure-target.ts +594 -0
  285. package/src/cli/configure.ts +1084 -529
  286. package/src/cli/context-map.ts +114 -0
  287. package/src/cli/context.ts +4 -0
  288. package/src/cli/doctor-state-size.ts +1 -12
  289. package/src/cli/doctor-validation-contract.ts +28 -0
  290. package/src/cli/doctor.ts +5 -0
  291. package/src/cli/evidence-detail.ts +1 -75
  292. package/src/cli/evidence-inventory.ts +1 -167
  293. package/src/cli/index.ts +3 -0
  294. package/src/cli/lifecycle-presenter.ts +436 -0
  295. package/src/cli/models.ts +10 -2
  296. package/src/cli/modes/print.ts +5 -1
  297. package/src/cli/reset.ts +228 -106
  298. package/src/cli/run.ts +7 -4
  299. package/src/cli/select.ts +664 -0
  300. package/src/cli/skills.ts +9 -2
  301. package/src/cli/targets.ts +3 -0
  302. package/src/cli/tasks.ts +84 -0
  303. package/src/cli/uninstall.ts +233 -165
  304. package/src/cli/upgrade.ts +210 -150
  305. package/src/cli/usage.ts +92 -27
  306. package/src/cli/validate-model.ts +3 -3
  307. package/src/cli/verifiers.ts +147 -1
  308. package/src/cli/wiki-generate.ts +1 -0
  309. package/src/core/commit-attribution.ts +41 -1
  310. package/src/core/config.ts +56 -0
  311. package/src/core/external-diagnostic.ts +44 -0
  312. package/src/core/gateway-routing.ts +157 -0
  313. package/src/core/git-commit-attribution.ts +46 -3
  314. package/src/core/run-overrides.ts +0 -5
  315. package/src/core/safe-exec.ts +17 -2
  316. package/src/core/skill-activation.ts +92 -2
  317. package/src/core/tool-names.ts +5 -2
  318. package/src/domains/agents/builtins/architect.md +1 -1
  319. package/src/domains/agents/builtins/coder.md +1 -1
  320. package/src/domains/agents/builtins/documenter.md +1 -1
  321. package/src/domains/agents/builtins/git-master.md +1 -1
  322. package/src/domains/agents/builtins/provenance.md +7 -7
  323. package/src/domains/agents/builtins/tester.md +1 -1
  324. package/src/domains/agents/builtins/verifier.md +2 -2
  325. package/src/domains/agents/builtins/wiki-writer.md +4 -3
  326. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  327. package/src/domains/agents/catalog.ts +1 -1
  328. package/src/domains/agents/result-contract.ts +70 -0
  329. package/src/domains/context/extension.ts +31 -7
  330. package/src/domains/context/refresh.ts +3 -0
  331. package/src/domains/context/wiki/frontmatter.ts +5 -2
  332. package/src/domains/context/wiki/generate.ts +6 -0
  333. package/src/domains/context/wiki/map-seed.ts +589 -0
  334. package/src/domains/context/wiki/plan.ts +2 -2
  335. package/src/domains/context/wiki/prompts.ts +43 -0
  336. package/src/domains/dispatch/active-route-planner.ts +4 -0
  337. package/src/domains/dispatch/admission.ts +29 -0
  338. package/src/domains/dispatch/agent-candidates.ts +10 -0
  339. package/src/domains/dispatch/budget-envelope.ts +86 -1
  340. package/src/domains/dispatch/capability-match.ts +1 -0
  341. package/src/domains/dispatch/capacity-lease.ts +17 -0
  342. package/src/domains/dispatch/code-step.ts +11 -4
  343. package/src/domains/dispatch/contract.ts +42 -8
  344. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  345. package/src/domains/dispatch/extension.ts +184 -56
  346. package/src/domains/dispatch/fleet-commit-attribution.ts +5 -0
  347. package/src/domains/dispatch/fleet-run.ts +1 -0
  348. package/src/domains/dispatch/host-verification.ts +114 -13
  349. package/src/domains/dispatch/intent.ts +28 -18
  350. package/src/domains/dispatch/orphan-recovery.ts +2 -0
  351. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  352. package/src/domains/dispatch/reservation-store.ts +5 -3
  353. package/src/domains/dispatch/state.ts +15 -2
  354. package/src/domains/dispatch/types.ts +20 -2
  355. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  356. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  357. package/src/domains/eval/metrics/token-stream.ts +201 -31
  358. package/src/domains/eval/metrics/tracked.ts +40 -4
  359. package/src/domains/eval/runners/clio-run.ts +17 -11
  360. package/src/domains/eval/runners/context-index.ts +2 -7
  361. package/src/domains/eval/runners/context-init.ts +3 -6
  362. package/src/domains/eval/runners/external-command.ts +29 -11
  363. package/src/domains/eval/schema/suite.ts +28 -0
  364. package/src/domains/eval/schema/verdict.ts +2 -2
  365. package/src/domains/eval/suites/resolve.ts +13 -1
  366. package/src/domains/eval/suites/run.ts +24 -3
  367. package/src/domains/evidence/build.ts +102 -15
  368. package/src/domains/evidence/detail.ts +69 -0
  369. package/src/domains/evidence/eval.ts +13 -1
  370. package/src/domains/evidence/finish-contract-map.ts +5 -1
  371. package/src/domains/evidence/inventory.ts +167 -0
  372. package/src/domains/evidence/store.ts +16 -0
  373. package/src/domains/evidence/types.ts +12 -0
  374. package/src/domains/extensions/resources.ts +7 -0
  375. package/src/domains/interop/registry.ts +6 -2
  376. package/src/domains/interop/types.ts +4 -0
  377. package/src/domains/lifecycle/migrations/index.ts +4 -0
  378. package/src/domains/memory/task-memory-policy.ts +70 -26
  379. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  380. package/src/domains/middleware/index.ts +0 -1
  381. package/src/domains/middleware/marketplace-offer.ts +22 -35
  382. package/src/domains/middleware/memory-intervention.ts +127 -32
  383. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  384. package/src/domains/middleware/runtime.ts +7 -3
  385. package/src/domains/middleware/skills-reminder.ts +31 -2
  386. package/src/domains/mux/detect.ts +3 -6
  387. package/src/domains/observability/accountability.ts +15 -1
  388. package/src/domains/observability/compaction-usage.ts +118 -0
  389. package/src/domains/observability/contract.ts +52 -7
  390. package/src/domains/observability/cost.ts +1 -1
  391. package/src/domains/observability/evidence-index.ts +10 -0
  392. package/src/domains/observability/extension.ts +15 -6
  393. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  394. package/src/domains/observability/projection.ts +394 -45
  395. package/src/{interactive → domains/observability}/worker-progress.ts +3 -3
  396. package/src/domains/prompts/fragments/operating/contract.md +2 -0
  397. package/src/domains/prompts/fragments/wiki/page.md +8 -0
  398. package/src/domains/providers/contract.ts +4 -1
  399. package/src/domains/providers/extension.ts +40 -9
  400. package/src/domains/providers/model-capabilities.ts +9 -0
  401. package/src/domains/providers/model-discovery.ts +3 -4
  402. package/src/domains/providers/model-runtime-capabilities.ts +15 -5
  403. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +48 -26
  404. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  405. package/src/domains/providers/runtimes/claude/claude-code.ts +9 -0
  406. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  407. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  408. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  409. package/src/domains/providers/support.ts +11 -5
  410. package/src/domains/providers/target-model-cache.ts +25 -2
  411. package/src/domains/providers/types/capability-flags.ts +2 -0
  412. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  413. package/src/domains/providers/types/target-descriptor.ts +19 -0
  414. package/src/domains/resources/index.ts +3 -0
  415. package/src/domains/resources/skills/install.ts +72 -7
  416. package/src/domains/resources/skills/loader.ts +7 -0
  417. package/src/domains/resources/skills/marketplace.ts +63 -11
  418. package/src/domains/safety/action-classifier.ts +7 -0
  419. package/src/domains/safety/autonomy.ts +15 -0
  420. package/src/domains/safety/default-path-policy.ts +2 -0
  421. package/src/domains/safety/finish-contract-registration.ts +29 -14
  422. package/src/domains/safety/finish-contract.ts +252 -40
  423. package/src/domains/safety/index.ts +21 -1
  424. package/src/domains/safety/path-policy.ts +1 -1
  425. package/src/domains/safety/policy-engine.ts +60 -17
  426. package/src/domains/safety/protected-artifacts.ts +191 -88
  427. package/src/domains/safety/rigor.ts +53 -39
  428. package/src/domains/safety/run-effects.ts +2 -22
  429. package/src/domains/safety/skill-authority.ts +55 -0
  430. package/src/domains/safety/validation-contract.ts +388 -0
  431. package/src/domains/session/archive-readers.ts +10 -1
  432. package/src/domains/session/compaction/compact.ts +72 -22
  433. package/src/domains/session/decision-board.ts +101 -2
  434. package/src/domains/session/entries.ts +50 -7
  435. package/src/domains/session/extension.ts +4 -4
  436. package/src/domains/session/handoff.ts +2 -1
  437. package/src/domains/session/manager.ts +2 -3
  438. package/src/domains/session/task-board.ts +14 -1
  439. package/src/domains/session/tree/fork.ts +1 -2
  440. package/src/domains/session/tree/navigator.ts +1 -1
  441. package/src/domains/session/usage.ts +3 -3
  442. package/src/domains/user-tasks/acceptance.ts +56 -0
  443. package/src/domains/user-tasks/active-acceptance.ts +40 -0
  444. package/src/domains/user-tasks/store.ts +34 -3
  445. package/src/engine/acp/adapter.ts +24 -6
  446. package/src/engine/acp/server.ts +21 -4
  447. package/src/engine/acp/transport.ts +53 -8
  448. package/src/engine/acp/types.ts +4 -0
  449. package/src/engine/agent.ts +13 -3
  450. package/src/engine/ai.ts +26 -8
  451. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  452. package/src/engine/api-registry.ts +3 -0
  453. package/src/engine/apis/ollama-native.ts +15 -0
  454. package/src/engine/apis/openai-completions.ts +117 -14
  455. package/src/engine/claude/subprocess-runtime.ts +107 -60
  456. package/src/engine/external-subprocess.ts +122 -6
  457. package/src/entry/background-model-metadata.ts +18 -0
  458. package/src/entry/compaction-prompt.ts +57 -0
  459. package/src/entry/orchestrator.ts +416 -218
  460. package/src/entry/task-memory-lifecycle.ts +35 -0
  461. package/src/interactive/chat-loop-messages.ts +13 -4
  462. package/src/interactive/chat-loop.ts +65 -2
  463. package/src/interactive/chat-renderer.ts +1 -0
  464. package/src/interactive/cost-overlay.ts +26 -2
  465. package/src/interactive/dispatch-board.ts +46 -717
  466. package/src/interactive/fleet-run-preview.ts +2 -1
  467. package/src/interactive/interactive-application.ts +3 -2
  468. package/src/interactive/interactive-presentation.ts +55 -12
  469. package/src/interactive/interactive-slash-runtime.ts +4 -2
  470. package/src/interactive/oracle.ts +5 -2
  471. package/src/interactive/overlays/fleet-run-approval.ts +3 -2
  472. package/src/interactive/overlays/message-picker.ts +2 -2
  473. package/src/interactive/overlays/settings.ts +2 -2
  474. package/src/interactive/overlays/tree-selector.ts +2 -2
  475. package/src/interactive/renderers/branch-summary.ts +1 -1
  476. package/src/interactive/renderers/worker-entry.ts +32 -0
  477. package/src/interactive/slash-autocomplete.ts +4 -6
  478. package/src/interactive/slash-commands.ts +49 -45
  479. package/src/interactive/slash-spec.ts +28 -0
  480. package/src/interactive/theme/labels.ts +19 -13
  481. package/src/interactive/turn-context.ts +9 -5
  482. package/src/interactive/turn-recovery.ts +8 -0
  483. package/src/interactive/turn-runtime.ts +27 -11
  484. package/src/interactive/turn-state.ts +7 -0
  485. package/src/interactive/view/artifacts.ts +2 -0
  486. package/src/interactive/worker-receipts.ts +1 -0
  487. package/src/interactive/worker-stream.ts +13 -4
  488. package/src/tools/bootstrap.ts +4 -0
  489. package/src/tools/builtin-tool-catalog.ts +31 -0
  490. package/src/tools/compete-worktrees.ts +7 -1
  491. package/src/tools/context/index.ts +30 -9
  492. package/src/tools/core-bootstrap.ts +16 -0
  493. package/src/tools/decide.ts +136 -0
  494. package/src/tools/dispatch-admission.ts +21 -0
  495. package/src/tools/dispatch-arguments.ts +1 -0
  496. package/src/tools/dispatch-event-text.ts +10 -0
  497. package/src/tools/dispatch-plan.ts +6 -2
  498. package/src/tools/dispatch-runner.ts +27 -1
  499. package/src/tools/dispatch-types.ts +6 -0
  500. package/src/tools/evidence.ts +96 -0
  501. package/src/tools/limitation.ts +76 -0
  502. package/src/tools/policy.ts +9 -0
  503. package/src/tools/presentation.ts +3 -0
  504. package/src/tools/registry.ts +11 -5
  505. package/src/tools/result-shaping.ts +17 -5
  506. package/src/tools/task-worktree.ts +13 -3
  507. package/src/tools/tasks.ts +10 -1
  508. package/src/tools/verify/authoring.ts +170 -83
  509. package/src/tools/verify/catalog.ts +122 -5
  510. package/src/tools/verify/index.ts +2 -1
  511. package/src/tools/verify/numeric.ts +298 -0
  512. package/src/tools/verify/perf.ts +143 -0
  513. package/src/tools/verify/scripts.ts +229 -2
  514. package/src/tools/worker-evidence.ts +3 -1
  515. package/src/worker/spec-contract.ts +4 -0
  516. package/dist/chunk-2Z2IKEXI.js +0 -1554
  517. package/dist/chunk-RVG5JXAL.js +0 -41
  518. package/dist/chunk-T56WDKA5.js +0 -183
  519. package/dist/chunk-VN3SHNBN.js +0 -313
  520. package/dist/chunk-VPKWYKEY.js +0 -169
  521. package/dist/reset-OAQP3W4O.js +0 -230
  522. package/dist/uninstall-N34PCTGJ.js +0 -331
  523. package/dist/upgrade-PXK3S2YM.js +0 -325
@@ -17,6 +17,11 @@ import {
17
17
  } from "@earendil-works/pi-ai";
18
18
  import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
19
19
  import type { BackendCompletionTimings, BackendTimingsSource } from "../../core/cache-telemetry.js";
20
+ import {
21
+ type GatewayRoutingObservation,
22
+ liteLLMGatewayRoutingFromHeaders,
23
+ liteLLMRouteFailureMessage,
24
+ } from "../../core/gateway-routing.js";
20
25
  import type { ResponseModelIdObservation } from "../../core/response-model-id.js";
21
26
  import {
22
27
  type AppliedThinking,
@@ -27,7 +32,7 @@ import {
27
32
  import { lmStudioReasoningEffort } from "../../domains/providers/runtimes/common/lmstudio-http.js";
28
33
  import type { ThinkingLevel } from "../../domains/providers/types/capability-flags.js";
29
34
  import type { LocalModelQuirks, SamplingProfile } from "../../domains/providers/types/local-model-quirks.js";
30
- import type { LmStudioTargetSettings } from "../../domains/providers/types/target-descriptor.js";
35
+ import type { LiteLLMTargetSettings, LmStudioTargetSettings } from "../../domains/providers/types/target-descriptor.js";
31
36
  import { filterGemmaChannelStream, usesGemmaChannelMarkers } from "../gemma-channel-filter.js";
32
37
  import { HarmonyResponseParser } from "../harmony-response.js";
33
38
  import { createSentinelStripper, stripTokenizerSentinels } from "../strip-tokenizer-sentinels.js";
@@ -42,6 +47,8 @@ declare module "@earendil-works/pi-ai" {
42
47
  interface AssistantMessage {
43
48
  /** Direct observation of model-id presence in an OpenAI-compatible response. */
44
49
  responseModelIdObservation?: ResponseModelIdObservation;
50
+ /** Route, fallback, retry, and proxy timing facts reported by LiteLLM. */
51
+ gatewayRouting?: GatewayRoutingObservation;
45
52
  /** Backend-reported prefill and prediction timings when available. */
46
53
  backendTimings?: BackendCompletionTimings;
47
54
  }
@@ -69,6 +76,7 @@ interface ClioRuntimeMetadata {
69
76
  quirks?: LocalModelQuirks;
70
77
  chatTemplateKwargsUnsupported?: boolean;
71
78
  lmstudio?: LmStudioTargetSettings;
79
+ litellm?: LiteLLMTargetSettings;
72
80
  lmstudioReasoningOptions?: ReadonlyArray<string>;
73
81
  lmstudioDefaultModel?: string;
74
82
  };
@@ -101,6 +109,7 @@ interface ResponseModelIdCapture {
101
109
  modelIdDone: boolean;
102
110
  backendTimings: BackendCompletionTimings | null;
103
111
  backendTimingsSource: BackendTimingsSource | null;
112
+ gatewayRouting: GatewayRoutingObservation | null;
104
113
  buffer: string;
105
114
  decoder: TextDecoder | null;
106
115
  }
@@ -199,7 +208,10 @@ function observeResponseModelIdBytes(
199
208
  if (captureCanStopEarly(capture) || flush) capture.decoder = null;
200
209
  }
201
210
 
202
- function captureResponseModelId(response: Response, capture: ResponseModelIdCapture): Response {
211
+ function captureResponseModelId(response: Response, capture: ResponseModelIdCapture, model: Model<Api>): Response {
212
+ if (runtimeMetadata(model)?.runtimeId === "litellm") {
213
+ capture.gatewayRouting = liteLLMGatewayRoutingFromHeaders(response.headers);
214
+ }
203
215
  if (!response.body || !response.headers.get("content-type")?.toLowerCase().includes("text/event-stream")) {
204
216
  return response;
205
217
  }
@@ -234,6 +246,7 @@ function withResponseModelIdCapture<TOptions extends StreamOptions>(
234
246
  modelIdDone: false,
235
247
  backendTimings: null,
236
248
  backendTimingsSource: backendTimingsSourceForModel(model),
249
+ gatewayRouting: null,
237
250
  buffer: "",
238
251
  decoder: new TextDecoder(),
239
252
  };
@@ -241,7 +254,7 @@ function withResponseModelIdCapture<TOptions extends StreamOptions>(
241
254
  options.fetch ?? ((input, init) => globalThis.fetch(input, init));
242
255
  const capturedOptions = {
243
256
  ...options,
244
- fetch: async (input, init) => captureResponseModelId(await fetchImpl(input, init), capture),
257
+ fetch: async (input, init) => captureResponseModelId(await fetchImpl(input, init), capture, model),
245
258
  } as TOptions;
246
259
  const source = sourceFactory(capturedOptions);
247
260
  const annotated = createAssistantMessageEventStream();
@@ -255,9 +268,11 @@ function withResponseModelIdCapture<TOptions extends StreamOptions>(
255
268
  : { state: "not-observed" };
256
269
  if (event.type === "done") {
257
270
  event.message.responseModelIdObservation = observation;
271
+ if (capture.gatewayRouting !== null) event.message.gatewayRouting = capture.gatewayRouting;
258
272
  if (capture.backendTimings !== null) event.message.backendTimings = capture.backendTimings;
259
273
  } else if (event.type === "error") {
260
274
  event.error.responseModelIdObservation = observation;
275
+ if (capture.gatewayRouting !== null) event.error.gatewayRouting = capture.gatewayRouting;
261
276
  if (capture.backendTimings !== null) event.error.backendTimings = capture.backendTimings;
262
277
  }
263
278
  annotated.push(event as AssistantMessageEvent);
@@ -271,6 +286,31 @@ function withResponseModelIdCapture<TOptions extends StreamOptions>(
271
286
  return annotated;
272
287
  }
273
288
 
289
+ /** Make a failed physical gateway route final and useful to an operator. */
290
+ function withLiteLLMRouteFailureAdvice(
291
+ model: Model<"openai-completions">,
292
+ source: AssistantMessageEventStream,
293
+ ): AssistantMessageEventStream {
294
+ const metadata = runtimeMetadata(model);
295
+ if (metadata?.runtimeId !== "litellm") return source;
296
+ const advised = createAssistantMessageEventStream();
297
+ (async () => {
298
+ try {
299
+ for await (const event of source) {
300
+ if (event.type === "error") {
301
+ event.error.errorMessage = liteLLMRouteFailureMessage(event.error.errorMessage, metadata.targetId, model.id);
302
+ }
303
+ advised.push(event as AssistantMessageEvent);
304
+ }
305
+ advised.end();
306
+ } catch (err) {
307
+ const message = err instanceof Error ? err.message : String(err);
308
+ throw new Error(liteLLMRouteFailureMessage(message, metadata.targetId, model.id));
309
+ }
310
+ })();
311
+ return advised;
312
+ }
313
+
274
314
  function samplingParamsFromProfile(profile: SamplingProfile): Record<string, unknown> {
275
315
  return {
276
316
  ...(profile.topP !== undefined ? { top_p: profile.topP } : {}),
@@ -395,6 +435,17 @@ function applyThinkingPayload(
395
435
  ) {
396
436
  next.reasoning_effort = resolved.request.reasoningEffort;
397
437
  }
438
+ // LiteLLM generic openai/<local model> routes otherwise drop this standard
439
+ // parameter. Allow only the effort this model/runtime actually resolved;
440
+ // unknown off controls and unrelated caller parameters gain no allowance.
441
+ if (
442
+ resolved.runtimeId === "litellm" &&
443
+ resolved.request.reasoningEffort &&
444
+ next.reasoning_effort === resolved.request.reasoningEffort
445
+ ) {
446
+ const allowed = Array.isArray(next.allowed_openai_params) ? next.allowed_openai_params : [];
447
+ next.allowed_openai_params = [...new Set([...allowed, "reasoning_effort"])];
448
+ }
398
449
  if (resolved.request.chatTemplateKwargs && !chatTemplateKwargsUnsupported(model)) {
399
450
  const existing = isPlainRecord(next.chat_template_kwargs) ? next.chat_template_kwargs : {};
400
451
  next.chat_template_kwargs = { ...existing, ...resolved.request.chatTemplateKwargs };
@@ -433,15 +484,15 @@ function applyLmStudioPayload(
433
484
  if (request?.ttlSeconds !== undefined) next.ttl = request.ttlSeconds;
434
485
  if (request?.draftModel !== undefined) next.draft_model = request.draftModel;
435
486
  if (resolved.thinking.mechanism === "none" || resolved.thinking.mechanism === "always-on") return next;
436
- const resolvedEffort =
437
- resolved.request.reasoningEffort ??
438
- lmStudioReasoningEffort(resolved.thinking.effectiveLevel, runtimeMetadata(model)?.lmstudioReasoningOptions);
487
+ const resolvedEffort = runtimeMetadata(model)?.lmstudioReasoningOptions
488
+ ? lmStudioReasoningEffort(resolved.thinking.effectiveLevel, runtimeMetadata(model)?.lmstudioReasoningOptions)
489
+ : (resolved.request.reasoningEffort ?? lmStudioReasoningEffort(resolved.thinking.effectiveLevel));
439
490
  switch (request?.reasoning) {
440
491
  case "off":
441
- next.reasoning_effort = "none";
492
+ next.reasoning_effort = lmStudioReasoningEffort("off", runtimeMetadata(model)?.lmstudioReasoningOptions);
442
493
  break;
443
494
  case "on":
444
- next.reasoning_effort = "low";
495
+ next.reasoning_effort = lmStudioReasoningEffort("low", runtimeMetadata(model)?.lmstudioReasoningOptions);
445
496
  break;
446
497
  case "low":
447
498
  case "medium":
@@ -584,6 +635,50 @@ function withRemainingContextBudget<TOptions extends StreamOptions>(
584
635
  } as TOptions;
585
636
  }
586
637
 
638
+ function hasHeader(headers: Readonly<Record<string, unknown>> | undefined, name: string): boolean {
639
+ if (!headers) return false;
640
+ const wanted = name.toLowerCase();
641
+ return Object.keys(headers).some((key) => key.toLowerCase() === wanted);
642
+ }
643
+
644
+ /**
645
+ * Apply LiteLLM's request-control headers at the final transport boundary.
646
+ *
647
+ * The OpenAI SDK otherwise retries twice below Clio's visible recovery loop and
648
+ * below LiteLLM's own router, multiplying a single failure into several hidden
649
+ * attempts. Gateway requests use zero client retries by default; optional
650
+ * `numRetries` controls the proxy router itself, where any configured attempts
651
+ * remain observable. A physical-routing gateway should leave it at zero.
652
+ */
653
+ function withLiteLLMRequestOptions<TOptions extends StreamOptions>(
654
+ model: Model<"openai-completions">,
655
+ options: TOptions,
656
+ ): TOptions {
657
+ const metadata = runtimeMetadata(model);
658
+ if (metadata?.runtimeId !== "litellm") return options;
659
+ const request = metadata.litellm?.request;
660
+ const modelHeaders = model.headers as Readonly<Record<string, unknown>> | undefined;
661
+ const optionHeaders = options.headers as Readonly<Record<string, unknown>> | undefined;
662
+ const headers: Record<string, string | null> = { ...(options.headers ?? {}) };
663
+ const setDefault = (name: string, value: string | undefined): void => {
664
+ if (value === undefined || hasHeader(modelHeaders, name) || hasHeader(optionHeaders, name)) return;
665
+ headers[name] = value;
666
+ };
667
+ setDefault("x-litellm-tags", ["clio-coder", ...(request?.tags ?? [])].join(","));
668
+ if (request?.sendSessionId !== false) setDefault("x-litellm-session-id", options.sessionId);
669
+ setDefault("x-litellm-timeout", request?.timeoutSeconds?.toString());
670
+ setDefault("x-litellm-stream-timeout", request?.streamTimeoutSeconds?.toString());
671
+ setDefault("x-litellm-num-retries", request?.numRetries?.toString());
672
+ return {
673
+ ...options,
674
+ headers,
675
+ // Never hide duplicate attempts inside the OpenAI SDK beneath Clio's
676
+ // operator-visible failure. The gateway may still be configured to retry,
677
+ // but physical-routing deployments should keep that policy at zero too.
678
+ maxRetries: options.maxRetries ?? 0,
679
+ } as TOptions;
680
+ }
681
+
587
682
  function requiredToolArguments(tool: Tool): ReadonlyArray<string> {
588
683
  const schema = tool.parameters as unknown;
589
684
  if (schema === null || typeof schema !== "object" || Array.isArray(schema)) return [];
@@ -921,15 +1016,18 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
921
1016
  const resolved = resolvedCapabilitiesForModel(model, defaultLevel);
922
1017
  const effectiveContext = stripsThinking(resolved) ? stripThinkingFromContext(context) : context;
923
1018
  const withSamplers = withSamplingOverrides(model, options, resolved);
1019
+ const withGatewayOptions = withLiteLLMRequestOptions(
1020
+ model,
1021
+ withRemainingContextBudget(model, effectiveContext, withSamplers),
1022
+ );
924
1023
  return guardMalformedToolCalls(
925
1024
  withReasoningTokenEstimate(
926
1025
  stripSentinelsFromStream(
927
1026
  stripNeverReasoningFromStream(
928
1027
  filterGemmaChannelStream(
929
- withResponseModelIdCapture(
1028
+ withLiteLLMRouteFailureAdvice(
930
1029
  model,
931
- withRemainingContextBudget(model, effectiveContext, withSamplers),
932
- (capturedOptions) =>
1030
+ withResponseModelIdCapture(model, withGatewayOptions, (capturedOptions) =>
933
1031
  withLocalResidency(
934
1032
  model,
935
1033
  {
@@ -938,6 +1036,7 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
938
1036
  },
939
1037
  (requestModel) => piOpenAICompletions.stream(requestModel, effectiveContext, capturedOptions),
940
1038
  ),
1039
+ ),
941
1040
  ),
942
1041
  usesGemmaChannelMarkers(resolved.modelId),
943
1042
  ),
@@ -954,15 +1053,18 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
954
1053
  const resolved = resolvedCapabilitiesForModel(model, thinkingLevelFromSimple(options));
955
1054
  const effectiveContext = stripsThinking(resolved) ? stripThinkingFromContext(context) : context;
956
1055
  const withSamplers = withSamplingOverrides(model, options, resolved);
1056
+ const withGatewayOptions = withLiteLLMRequestOptions(
1057
+ model,
1058
+ withRemainingContextBudget(model, effectiveContext, withSamplers),
1059
+ );
957
1060
  return guardMalformedToolCalls(
958
1061
  withReasoningTokenEstimate(
959
1062
  stripSentinelsFromStream(
960
1063
  stripNeverReasoningFromStream(
961
1064
  filterGemmaChannelStream(
962
- withResponseModelIdCapture(
1065
+ withLiteLLMRouteFailureAdvice(
963
1066
  model,
964
- withRemainingContextBudget(model, effectiveContext, withSamplers),
965
- (capturedOptions) =>
1067
+ withResponseModelIdCapture(model, withGatewayOptions, (capturedOptions) =>
966
1068
  withLocalResidency(
967
1069
  model,
968
1070
  {
@@ -971,6 +1073,7 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
971
1073
  },
972
1074
  (requestModel) => piOpenAICompletions.streamSimple(requestModel, effectiveContext, capturedOptions),
973
1075
  ),
1076
+ ),
974
1077
  ),
975
1078
  usesGemmaChannelMarkers(resolved.modelId),
976
1079
  ),
@@ -1,17 +1,30 @@
1
1
  import { type ChildProcessByStdio, spawn } from "node:child_process";
2
- import { createInterface } from "node:readline";
3
- import type { Readable } from "node:stream";
2
+ import type { Readable, Writable } from "node:stream";
4
3
 
4
+ import { boundedExternalDiagnostic } from "../../core/external-diagnostic.js";
5
+ import { buildSafeToolEnv, resolveSafeCwd } from "../../core/safe-exec.js";
5
6
  import type { AutonomyLevel } from "../../domains/safety/autonomy.js";
6
7
  import { assertToolProfileEnforceable } from "../../tools/profiles.js";
7
- import { readStderr, waitForClose } from "../external-subprocess.js";
8
+ import { createProcessTreeTerminator, readBoundedLines, readStderr, waitForClose } from "../external-subprocess.js";
8
9
  import type { AgentEvent, AgentMessage, Usage } from "../types.js";
9
10
  import type { WorkerEventEmit, WorkerRunHandle, WorkerRunInput, WorkerRunResult } from "../worker-runtime.js";
10
11
  import { isClaudeCodeSessionId } from "./session-id.js";
11
12
 
12
13
  const READ_ONLY_CLAUDE_TOOLS = ["Read", "Grep", "Glob", "LS", "WebFetch", "WebSearch"] as const;
13
14
 
14
- type ClaudeChildProcess = ChildProcessByStdio<null, Readable, Readable>;
15
+ /** Official CLI binary, resolved from the operator's PATH. */
16
+ const CLAUDE_BINARY = "claude";
17
+ export const CLAUDE_MAX_STREAM_LINE_BYTES = 1024 * 1024;
18
+ export const CLAUDE_MAX_STREAM_BYTES = 8 * 1024 * 1024;
19
+
20
+ type ClaudeChildProcess = ChildProcessByStdio<Writable, Readable, Readable>;
21
+
22
+ export interface ClaudeRuntimeDependencies {
23
+ binary?: string;
24
+ workspaceRoot?: string;
25
+ environment?: NodeJS.ProcessEnv;
26
+ killGraceMs?: number;
27
+ }
15
28
 
16
29
  export interface ClaudeSubprocessPermissionConfig {
17
30
  permissionMode: "plan" | "dontAsk" | "acceptEdits" | "default" | "bypassPermissions";
@@ -48,15 +61,20 @@ export function claudeSubprocessPermissionConfigForAutonomy(
48
61
  return { permissionMode: "default", extraArgs: [], dangerousBypass: false };
49
62
  }
50
63
 
51
- function buildClaudeCodePrompt(input: WorkerRunInput): string {
64
+ export function buildClaudeCodePrompt(input: WorkerRunInput): string {
52
65
  const parts = (input.dynamicPromptMessages ?? []).map((message) => message.body.trim()).filter(Boolean);
53
66
  parts.push(input.task);
54
67
  return parts.join("\n\n");
55
68
  }
56
69
 
57
- function buildClaudeCodeArgs(input: WorkerRunInput): string[] {
70
+ /**
71
+ * `claude -p` reads the user prompt from stdin when no positional prompt is
72
+ * given, so the prompt never appears in argv. The system prompt stays on
73
+ * `--append-system-prompt`: stdin carries only the user turn.
74
+ */
75
+ export function buildClaudeCodeArgs(input: WorkerRunInput, gateEnv: NodeJS.ProcessEnv = process.env): string[] {
58
76
  assertToolProfileEnforceable(input.toolProfile, "claude-code");
59
- const permission = claudeSubprocessPermissionConfigForAutonomy(input.autonomy);
77
+ const permission = claudeSubprocessPermissionConfigForAutonomy(input.autonomy, gateEnv);
60
78
  const args = [
61
79
  "-p",
62
80
  "--output-format",
@@ -71,7 +89,6 @@ function buildClaudeCodeArgs(input: WorkerRunInput): string[] {
71
89
  const systemPrompt = input.systemPrompt.trim();
72
90
  if (systemPrompt.length > 0) args.push("--append-system-prompt", systemPrompt);
73
91
  if (isClaudeCodeSessionId(input.sessionId)) args.push("--session-id", input.sessionId.trim());
74
- args.push(buildClaudeCodePrompt(input));
75
92
  return args;
76
93
  }
77
94
 
@@ -170,8 +187,11 @@ function buildAssistantMessage(input: {
170
187
  exitCode: number;
171
188
  aborted: boolean;
172
189
  stderr: string;
190
+ transportError?: string;
173
191
  }): AgentMessage & { role: "assistant" } {
174
- const errorMessage = input.exitCode === 0 ? "" : resultError(input.result, input.stderr);
192
+ const transportError = input.transportError ?? "";
193
+ const failed = input.exitCode !== 0 || transportError.length > 0;
194
+ const errorMessage = !failed ? "" : transportError || resultError(input.result, input.stderr);
175
195
  const message: AgentMessage & { role: "assistant" } = {
176
196
  role: "assistant",
177
197
  content: [{ type: "text", text: input.text }],
@@ -179,12 +199,12 @@ function buildAssistantMessage(input: {
179
199
  provider: "anthropic",
180
200
  model: input.model,
181
201
  usage: normalizeUsage(input.result?.usage, finite(input.result?.total_cost_usd)),
182
- stopReason: input.aborted ? "aborted" : input.exitCode === 0 ? "stop" : "error",
202
+ stopReason: input.aborted ? "aborted" : failed ? "error" : "stop",
183
203
  timestamp: Date.now(),
184
204
  } as AgentMessage & { role: "assistant" };
185
205
  if (typeof input.result?.request_id === "string") message.responseId = input.result.request_id;
186
206
  if (typeof input.result?.model === "string") message.responseModel = input.result.model;
187
- if (errorMessage.length > 0) message.errorMessage = errorMessage;
207
+ if (errorMessage.length > 0) message.errorMessage = boundedExternalDiagnostic(errorMessage);
188
208
  return message;
189
209
  }
190
210
 
@@ -224,9 +244,14 @@ async function readJsonLines(
224
244
  emit: WorkerEventEmit,
225
245
  state: { started: boolean; text: string; model: string; result: Record<string, unknown> | null },
226
246
  ): Promise<void> {
227
- const rl = createInterface({ input: child.stdout });
228
- for await (const line of rl) {
229
- const trimmed = line.trim();
247
+ for await (const bounded of readBoundedLines(child.stdout, {
248
+ maxLineBytes: CLAUDE_MAX_STREAM_LINE_BYTES,
249
+ maxTotalBytes: CLAUDE_MAX_STREAM_BYTES,
250
+ })) {
251
+ if (bounded.kind === "oversized") {
252
+ throw new Error(`claude stream-json line exceeded ${CLAUDE_MAX_STREAM_LINE_BYTES} bytes`);
253
+ }
254
+ const trimmed = bounded.line.trim();
230
255
  if (trimmed.length === 0) continue;
231
256
  let parsed: unknown;
232
257
  try {
@@ -250,12 +275,21 @@ async function readJsonLines(
250
275
  }
251
276
  }
252
277
 
253
- export function startClaudeCodeWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): WorkerRunHandle {
254
- const args = buildClaudeCodeArgs(input);
255
- const child = spawn("claude", args, {
256
- cwd: process.cwd(),
257
- env: process.env,
258
- stdio: ["ignore", "pipe", "pipe"],
278
+ export function startClaudeCodeWorkerRun(
279
+ input: WorkerRunInput,
280
+ emit: WorkerEventEmit,
281
+ dependencies: ClaudeRuntimeDependencies = {},
282
+ ): WorkerRunHandle {
283
+ const sourceEnv = dependencies.environment ?? process.env;
284
+ const args = buildClaudeCodeArgs(input, sourceEnv);
285
+ const prompt = buildClaudeCodePrompt(input);
286
+ const workspaceRoot = dependencies.workspaceRoot ?? process.cwd();
287
+ const cwd = resolveSafeCwd(input.cwd, workspaceRoot);
288
+ const child: ClaudeChildProcess = spawn(dependencies.binary ?? CLAUDE_BINARY, args, {
289
+ cwd,
290
+ env: buildSafeToolEnv({}, sourceEnv),
291
+ detached: process.platform !== "win32",
292
+ stdio: ["pipe", "pipe", "pipe"],
259
293
  });
260
294
  const streamState = {
261
295
  started: false,
@@ -264,53 +298,66 @@ export function startClaudeCodeWorkerRun(input: WorkerRunInput, emit: WorkerEven
264
298
  result: null as Record<string, unknown> | null,
265
299
  };
266
300
  let aborted = false;
267
- let killTimer: NodeJS.Timeout | null = null;
268
-
301
+ let settled = false;
302
+ let transportError = "";
303
+ const terminator = createProcessTreeTerminator(child, dependencies.killGraceMs ?? 1500);
269
304
  const abort = (): void => {
305
+ if (settled) return;
270
306
  aborted = true;
271
- if (child.killed) return;
272
- child.kill("SIGTERM");
273
- killTimer = setTimeout(() => {
274
- if (!child.killed) child.kill("SIGKILL");
275
- }, 1500);
307
+ terminator.terminate();
276
308
  };
277
- if (input.signal) {
278
- if (input.signal.aborted) abort();
279
- else input.signal.addEventListener("abort", abort, { once: true });
280
- }
309
+ const onAbort = (): void => abort();
310
+ if (input.signal?.aborted) abort();
311
+ else input.signal?.addEventListener("abort", onAbort, { once: true });
312
+ child.once("error", (cause) => {
313
+ transportError ||=
314
+ (cause as NodeJS.ErrnoException).code === "ENOENT"
315
+ ? "Claude Code CLI (`claude`) is not installed or not on PATH."
316
+ : cause.message;
317
+ });
318
+ child.stdin.on("error", (cause) => {
319
+ if (!aborted) transportError ||= `could not send prompt to Claude Code CLI: ${cause.message}`;
320
+ });
321
+ child.stdin.end(prompt);
281
322
 
282
323
  const promise = (async (): Promise<WorkerRunResult> => {
283
324
  emit({ type: "agent_start" } as AgentEvent);
284
- const stderrPromise = readStderr(child);
285
- const stdoutPromise = readJsonLines(child, emit, streamState);
286
- const exitCode = await waitForClose(child);
287
- await stdoutPromise.catch(() => {});
288
- const stderr = await stderrPromise.catch(() => "");
289
- if (killTimer) clearTimeout(killTimer);
290
- const finalText =
291
- streamState.text ||
292
- resultText(streamState.result) ||
293
- (exitCode === 0 ? "" : resultError(streamState.result, stderr));
294
- const finalMessage = buildAssistantMessage({
295
- model: streamState.model,
296
- text: finalText,
297
- result: streamState.result,
298
- exitCode,
299
- aborted,
300
- stderr,
301
- });
302
- if (!streamState.started) emit({ type: "message_start", message: finalMessage } as AgentEvent);
303
- emit({ type: "message_end", message: finalMessage } as AgentEvent);
304
- const messages: AgentMessage[] = [finalMessage];
305
- emit({ type: "agent_end", messages } as AgentEvent);
306
- if (exitCode !== 0 && stderr.trim().length > 0 && !aborted) {
307
- process.stderr.write(`[worker:claude-code] ${stderr.trim()}\n`);
325
+ try {
326
+ const stderrPromise = readStderr(child);
327
+ const stdoutPromise = readJsonLines(child, emit, streamState).catch((cause) => {
328
+ transportError ||= cause instanceof Error ? cause.message : String(cause);
329
+ terminator.terminate();
330
+ });
331
+ const exitCode = await waitForClose(child);
332
+ await stdoutPromise;
333
+ const stderr = await stderrPromise.catch(() => "");
334
+ const finalText =
335
+ streamState.text ||
336
+ resultText(streamState.result) ||
337
+ (exitCode === 0 ? "" : resultError(streamState.result, stderr));
338
+ const finalMessage = buildAssistantMessage({
339
+ model: streamState.model,
340
+ text: finalText,
341
+ result: streamState.result,
342
+ exitCode,
343
+ aborted,
344
+ stderr,
345
+ transportError,
346
+ });
347
+ if (!streamState.started) emit({ type: "message_start", message: finalMessage } as AgentEvent);
348
+ emit({ type: "message_end", message: finalMessage } as AgentEvent);
349
+ const messages: AgentMessage[] = [finalMessage];
350
+ emit({ type: "agent_end", messages } as AgentEvent);
351
+ if (finalMessage.stopReason === "error" && finalMessage.errorMessage && !aborted) {
352
+ process.stderr.write(`[worker:claude-code] ${finalMessage.errorMessage}\n`);
353
+ }
354
+ return { messages, exitCode: finalMessage.stopReason === "stop" ? 0 : 1 };
355
+ } finally {
356
+ settled = true;
357
+ terminator.cleanup();
358
+ input.signal?.removeEventListener("abort", onAbort);
308
359
  }
309
- return { messages, exitCode: exitCode === 0 ? 0 : 1 };
310
360
  })();
311
361
 
312
- return {
313
- promise,
314
- abort,
315
- };
362
+ return { promise, abort };
316
363
  }
@@ -1,15 +1,125 @@
1
1
  import type { ChildProcessByStdio } from "node:child_process";
2
2
  import type { Readable } from "node:stream";
3
3
 
4
+ import { boundedExternalDiagnostic } from "../core/external-diagnostic.js";
5
+
4
6
  export type SubprocessWithStdio = ChildProcessByStdio<null, Readable, Readable>;
5
7
 
8
+ export type BoundedLine = { kind: "line"; line: string } | { kind: "oversized" };
9
+
10
+ /**
11
+ * Split a byte stream without ever retaining more than one bounded line.
12
+ * Node has already allocated each incoming chunk, but an unterminated provider
13
+ * line cannot make this decoder accumulate without limit.
14
+ */
15
+ export async function* readBoundedLines(
16
+ readable: Readable,
17
+ options: { maxLineBytes: number; maxTotalBytes: number },
18
+ ): AsyncGenerator<BoundedLine> {
19
+ let segments: Buffer[] = [];
20
+ let lineBytes = 0;
21
+ let totalBytes = 0;
22
+ let discarding = false;
23
+ const finishLine = (): BoundedLine | null => {
24
+ if (discarding) {
25
+ segments = [];
26
+ lineBytes = 0;
27
+ discarding = false;
28
+ return { kind: "oversized" };
29
+ }
30
+ if (lineBytes === 0) return { kind: "line", line: "" };
31
+ const joined = Buffer.concat(segments, lineBytes);
32
+ segments = [];
33
+ lineBytes = 0;
34
+ const end = joined.at(-1) === 0x0d ? joined.length - 1 : joined.length;
35
+ return { kind: "line", line: joined.subarray(0, end).toString("utf8") };
36
+ };
37
+
38
+ for await (const raw of readable) {
39
+ const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(String(raw));
40
+ totalBytes += chunk.byteLength;
41
+ if (totalBytes > options.maxTotalBytes) throw new Error("external stdout exceeded the cumulative output limit");
42
+ let offset = 0;
43
+ while (offset < chunk.length) {
44
+ const newline = chunk.indexOf(0x0a, offset);
45
+ const end = newline === -1 ? chunk.length : newline;
46
+ const piece = chunk.subarray(offset, end);
47
+ if (!discarding && piece.length > 0) {
48
+ if (lineBytes + piece.length > options.maxLineBytes) {
49
+ segments = [];
50
+ lineBytes = 0;
51
+ discarding = true;
52
+ } else {
53
+ segments.push(piece);
54
+ lineBytes += piece.length;
55
+ }
56
+ }
57
+ if (newline === -1) break;
58
+ const line = finishLine();
59
+ if (line) yield line;
60
+ offset = newline + 1;
61
+ }
62
+ }
63
+ if (discarding || lineBytes > 0) {
64
+ const line = finishLine();
65
+ if (line) yield line;
66
+ }
67
+ }
68
+
6
69
  export async function readStderr(child: { stderr: Readable }): Promise<string> {
7
- let stderr = "";
70
+ let tail = Buffer.alloc(0);
8
71
  for await (const chunk of child.stderr) {
9
- stderr += String(chunk);
10
- if (stderr.length > 8192) stderr = stderr.slice(-8192);
72
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
73
+ tail = Buffer.concat([tail, bytes]);
74
+ if (tail.byteLength > 8192) tail = tail.subarray(tail.byteLength - 8192);
11
75
  }
12
- return stderr;
76
+ return boundedExternalDiagnostic(tail.toString("utf8"));
77
+ }
78
+
79
+ export interface ProcessTreeTerminator {
80
+ terminate(): void;
81
+ cleanup(): void;
82
+ }
83
+
84
+ /** POSIX process-group termination with a direct-child fallback; Windows is direct-child only. */
85
+ export function createProcessTreeTerminator(
86
+ child: { pid?: number | undefined; exitCode: number | null; kill(signal?: NodeJS.Signals): boolean },
87
+ graceMs = 1500,
88
+ ): ProcessTreeTerminator {
89
+ let sent = false;
90
+ let timer: ReturnType<typeof setTimeout> | null = null;
91
+ const signal = (name: NodeJS.Signals): void => {
92
+ if (child.pid && process.platform !== "win32") {
93
+ try {
94
+ process.kill(-child.pid, name);
95
+ return;
96
+ } catch {
97
+ // The child may not have established its group yet.
98
+ }
99
+ }
100
+ if (child.exitCode !== null) return;
101
+ child.kill(name);
102
+ };
103
+ return {
104
+ terminate() {
105
+ if (sent) return;
106
+ sent = true;
107
+ signal("SIGTERM");
108
+ timer = setTimeout(() => {
109
+ timer = null;
110
+ signal("SIGKILL");
111
+ }, graceMs);
112
+ },
113
+ cleanup() {
114
+ if (timer) {
115
+ clearTimeout(timer);
116
+ timer = null;
117
+ // A closed leader can leave descendants with independent stdio.
118
+ // Finish the requested cancellation before releasing its escalation.
119
+ signal("SIGKILL");
120
+ }
121
+ },
122
+ };
13
123
  }
14
124
 
15
125
  export function waitForClose(child: {
@@ -17,7 +127,13 @@ export function waitForClose(child: {
17
127
  once(event: "close", listener: (code: number | null) => void): unknown;
18
128
  }): Promise<number> {
19
129
  return new Promise((resolve) => {
20
- child.once("error", () => resolve(1));
21
- child.once("close", (code: number | null) => resolve(code ?? 1));
130
+ let settled = false;
131
+ const finish = (code: number): void => {
132
+ if (settled) return;
133
+ settled = true;
134
+ resolve(code);
135
+ };
136
+ child.once("error", () => finish(1));
137
+ child.once("close", (code: number | null) => finish(code ?? 1));
22
138
  });
23
139
  }
@@ -0,0 +1,18 @@
1
+ import type { ProvidersContract } from "../domains/providers/contract.js";
2
+
3
+ /** Cold gateway roles need their own declared model controls before synthesis.
4
+ * Read metadata only; an unknown/mixed successful probe remains authoritative.
5
+ * Memory supplies its existing deadline/generation signal, including auth.
6
+ */
7
+ export async function prepareBackgroundModelMetadata(
8
+ providers: ProvidersContract,
9
+ targetId: string,
10
+ signal?: AbortSignal,
11
+ ): Promise<void> {
12
+ signal?.throwIfAborted();
13
+ const status = providers.list().find((entry) => entry.target.id === targetId);
14
+ if (status?.target.runtime === "litellm" && status.health.lastCheckAt === null) {
15
+ await providers.probeTarget(targetId, { reasoning: false, ...(signal ? { signal } : {}) });
16
+ }
17
+ signal?.throwIfAborted();
18
+ }