@iowarp/clio-coder 0.4.2 → 0.4.3

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 (369) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/CONTRIBUTING.md +86 -19
  3. package/README.md +35 -6
  4. package/dist/{acp-TMDQZDIG.js → acp-H2NGRPWO.js} +11 -11
  5. package/dist/{agents-5N5NG3XG.js → agents-TL5LLUQP.js} +54 -53
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-Z5CCBXKQ.js → auth-E5SW4HMS.js} +19 -16
  8. package/dist/{builtins-K6TNDT24.js → builtins-IA7V7FUC.js} +9 -4
  9. package/dist/{chunk-ZW4HH5JJ.js → chunk-2APPQIER.js} +6 -6
  10. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  11. package/dist/{chunk-UBRFI4HS.js → chunk-2UG5F4C5.js} +127 -47
  12. package/dist/{chunk-UH632ZYL.js → chunk-2UH2KFUP.js} +2 -2
  13. package/dist/{chunk-3F7VUY77.js → chunk-2VIKGWFZ.js} +2 -2
  14. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  15. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  16. package/dist/{chunk-2X4RYJTJ.js → chunk-4UVU7BJ5.js} +2 -2
  17. package/dist/{chunk-M2DAX4F6.js → chunk-4WR7VSYB.js} +2 -2
  18. package/dist/{chunk-5KW52TEP.js → chunk-54CBCGIR.js} +5 -5
  19. package/dist/{chunk-3KIPBMUA.js → chunk-5ICU3EUH.js} +2 -2
  20. package/dist/{chunk-77QIVUZB.js → chunk-5MEZN6CB.js} +4 -4
  21. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  22. package/dist/{chunk-LJID3DYZ.js → chunk-64I3JVYM.js} +2 -2
  23. package/dist/{chunk-4JDLP6ZS.js → chunk-6PTFB5VS.js} +7 -7
  24. package/dist/{chunk-VN3SHNBN.js → chunk-7DICMOS6.js} +2 -2
  25. package/dist/{chunk-HLAFFSEK.js → chunk-7DRAWPTZ.js} +2 -2
  26. package/dist/chunk-7E7I3WLS.js +3762 -0
  27. package/dist/{chunk-YJISEZKC.js → chunk-7ZYNNDKC.js} +6 -6
  28. package/dist/{chunk-I66ZTYNP.js → chunk-AF4YM7Z4.js} +236 -101
  29. package/dist/{chunk-2HFQNRV3.js → chunk-AX2THNSA.js} +12 -12
  30. package/dist/{chunk-PGF63K6I.js → chunk-B4OAX3SI.js} +65 -3
  31. package/dist/{chunk-W6NIE6OW.js → chunk-B4VEBZKF.js} +3 -3
  32. package/dist/{chunk-JBCS7CRR.js → chunk-BEPZRGGU.js} +10 -10
  33. package/dist/{chunk-XGDPUNND.js → chunk-CE5AX47J.js} +2 -2
  34. package/dist/{chunk-I64IFBLB.js → chunk-DWUOQKRU.js} +17 -10
  35. package/dist/{chunk-DZAW46HP.js → chunk-E3TPLWFX.js} +3 -3
  36. package/dist/{chunk-HIICAHCJ.js → chunk-EKCHAPYA.js} +2 -2
  37. package/dist/{chunk-XE3PCIXH.js → chunk-F5JHEYZM.js} +7 -7
  38. package/dist/{chunk-5PFYMY2V.js → chunk-FTMGRKEF.js} +2 -2
  39. package/dist/{chunk-ZNT2M6TG.js → chunk-G76U63X4.js} +17 -17
  40. package/dist/{chunk-34BHNEE3.js → chunk-GHS5EBTQ.js} +58 -7
  41. package/dist/{chunk-2NHR3NAY.js → chunk-GI7YYQ3F.js} +40 -34
  42. package/dist/{chunk-DYHAXKHD.js → chunk-GWZNEVM2.js} +12 -8
  43. package/dist/chunk-GYV6VZOC.js +26 -0
  44. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  45. package/dist/{chunk-W4YEMFBX.js → chunk-HEQY7ZFI.js} +2 -2
  46. package/dist/{chunk-IKOZFYBN.js → chunk-I7ZPNEJM.js} +145 -102
  47. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  48. package/dist/{chunk-XQRY4DTA.js → chunk-IGLP3ODT.js} +10 -10
  49. package/dist/chunk-IJNZMHLA.js +101 -0
  50. package/dist/{chunk-JWJGP5DQ.js → chunk-INY6HTFL.js} +7 -7
  51. package/dist/{chunk-PBP4B7XR.js → chunk-IUE3Y34X.js} +2 -2
  52. package/dist/{chunk-B74PXLU7.js → chunk-IWT4SF4R.js} +3 -3
  53. package/dist/{chunk-B7HM5Z7T.js → chunk-JDAY6FIL.js} +5 -5
  54. package/dist/{chunk-PJX3WQUQ.js → chunk-JEQ3XTHC.js} +2 -2
  55. package/dist/{chunk-FSP7CMNU.js → chunk-JGRC33J2.js} +50 -4
  56. package/dist/{chunk-X7IARSHT.js → chunk-JKKCYP3C.js} +9 -9
  57. package/dist/{chunk-HJWWJ6IL.js → chunk-JSC3U7TI.js} +16 -4
  58. package/dist/{chunk-SSEYRH53.js → chunk-KK4JZPBQ.js} +19 -140
  59. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  60. package/dist/{chunk-Q4XWMHX6.js → chunk-L47TF46W.js} +2 -2
  61. package/dist/{chunk-O3YUNJZ2.js → chunk-LDJG7DW3.js} +81 -24
  62. package/dist/{chunk-CDNVLKUX.js → chunk-LLDJM5XK.js} +13 -7
  63. package/dist/{chunk-F2I26BDK.js → chunk-MUW2BDDH.js} +4 -4
  64. package/dist/{chunk-HKMD33FO.js → chunk-MWUZBSAQ.js} +79 -76
  65. package/dist/{chunk-QQLGQY2A.js → chunk-N2Z7HLVY.js} +20 -20
  66. package/dist/{chunk-DZEK6CJN.js → chunk-NIQJ66N4.js} +19 -19
  67. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  68. package/dist/{chunk-462T4EGZ.js → chunk-O5CVSAG5.js} +2 -2
  69. package/dist/{chunk-TPEQIQIE.js → chunk-OML5D5V5.js} +8 -8
  70. package/dist/{chunk-IKSLQ4XV.js → chunk-PAJQJ7BS.js} +558 -216
  71. package/dist/{chunk-ZW55JB7N.js → chunk-PUVDKJ2Y.js} +2 -2
  72. package/dist/{chunk-UH347SHR.js → chunk-QWGDJJYJ.js} +11 -11
  73. package/dist/chunk-R6Q67RJH.js +134 -0
  74. package/dist/{chunk-CRFOIAX3.js → chunk-RRNP2ANY.js} +6 -6
  75. package/dist/{chunk-IDNA72AH.js → chunk-RSJ25QSL.js} +2 -2
  76. package/dist/chunk-SKHCAU7K.js +385 -0
  77. package/dist/{chunk-RLYRBIYQ.js → chunk-TM6LQDI3.js} +20 -12
  78. package/dist/chunk-UOIZ7DA4.js +41 -0
  79. package/dist/{chunk-P75RZCJW.js → chunk-UPZU6GE4.js} +3 -3
  80. package/dist/{chunk-MCMZMDAC.js → chunk-V2ANDPVT.js} +4 -4
  81. package/dist/{chunk-AK5XEFVZ.js → chunk-VA5FNYMT.js} +26 -13
  82. package/dist/{chunk-IMXMHHMQ.js → chunk-VW6DOEDG.js} +332 -57
  83. package/dist/{chunk-XOXV5GKE.js → chunk-W6RRQCPQ.js} +16 -7
  84. package/dist/{chunk-CYZW7JHJ.js → chunk-WBKFA554.js} +8 -8
  85. package/dist/{chunk-BO7Y52RY.js → chunk-WCXUNS7U.js} +7 -7
  86. package/dist/{chunk-ZGNYYXQ6.js → chunk-WRBAGUNF.js} +3 -3
  87. package/dist/{chunk-FVDGR2ZL.js → chunk-XIVNBFZS.js} +85 -30
  88. package/dist/{chunk-BYMNWQ7O.js → chunk-XPWWI35G.js} +299 -58
  89. package/dist/{chunk-KPXDY6QF.js → chunk-XRZT5WY5.js} +2 -2
  90. package/dist/{chunk-AZ4WMN4W.js → chunk-Y3CBHOR6.js} +2 -2
  91. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  92. package/dist/{chunk-54ODD65L.js → chunk-YQWYVTMC.js} +4 -4
  93. package/dist/{chunk-M2WXEHER.js → chunk-ZA4VCIGV.js} +2 -2
  94. package/dist/{chunk-7BHIY2MW.js → chunk-ZDN3Y73Y.js} +6 -6
  95. package/dist/{chunk-E7GT7O5N.js → chunk-ZWPRK62N.js} +7 -4
  96. package/dist/cli/index.js +38 -37
  97. package/dist/{clio-7VB377CC.js → clio-CMMK4KRR.js} +7 -7
  98. package/dist/{code-nav-YVLCYA7V.js → code-nav-MDZNQS33.js} +7 -7
  99. package/dist/{components-UBWCQSRW.js → components-UCUQ4QXW.js} +4 -4
  100. package/dist/{config-4HVOS65E.js → config-SVM5P5YI.js} +76 -74
  101. package/dist/{configure-PIWO7B24.js → configure-LE3IK2TJ.js} +26 -24
  102. package/dist/{context-IYEHL3WQ.js → context-2OHRKS42.js} +66 -63
  103. package/dist/{context-N6ZE3LGJ.js → context-E3VC7RX5.js} +15 -11
  104. package/dist/{context-KQYIWPWT.js → context-VNCR7KAG.js} +60 -45
  105. package/dist/{context-clear-G4OGZJDS.js → context-clear-BW4O37TG.js} +61 -59
  106. package/dist/context-map-COB37XXN.js +505 -0
  107. package/dist/{context-working-set-BWLF6LJP.js → context-working-set-VDS25HXZ.js} +17 -16
  108. package/dist/{dispatch-runner-2QQAITS3.js → dispatch-runner-5AHT53RF.js} +85 -74
  109. package/dist/{doctor-LHBD36VU.js → doctor-WNNVO6FY.js} +37 -37
  110. package/dist/{eval-C45FYRJ6.js → eval-7G7SGAYO.js} +285 -114
  111. package/dist/{eval-inventory-6DEJPLBF.js → eval-inventory-Y6QRFOH5.js} +4 -4
  112. package/dist/{evidence-6SHONYAF.js → evidence-VD6736FQ.js} +63 -62
  113. package/dist/{evolve-KRKMV72X.js → evolve-AL3NGVRL.js} +62 -61
  114. package/dist/{extensions-KPZ2UHBB.js → extensions-MOVJ32NM.js} +7 -7
  115. package/dist/{fleet-IVTCKDHT.js → fleet-QZHUMAGI.js} +110 -108
  116. package/dist/{fleet-commands-EDWL3IT7.js → fleet-commands-BAYT5FJZ.js} +10 -10
  117. package/dist/{fleet-decisions-YP3YEFGK.js → fleet-decisions-IREVMRU4.js} +7 -6
  118. package/dist/{fleet-graph-ZFWKHY2M.js → fleet-graph-YCTT3HTI.js} +19 -18
  119. package/dist/{fleet-inspect-FVUNCBML.js → fleet-inspect-QVJTDAVB.js} +55 -54
  120. package/dist/{fleet-preflight-UN5XED4R.js → fleet-preflight-25QAFPK4.js} +4 -4
  121. package/dist/{fleet-validate-XOWC4HSX.js → fleet-validate-5O57AAJ7.js} +23 -22
  122. package/dist/{fleet-verify-UN3SODEL.js → fleet-verify-CPH2W2T6.js} +56 -55
  123. package/dist/{fleet-view-TWHJKCN6.js → fleet-view-SWBR3VGQ.js} +55 -54
  124. package/dist/{init-T2QORQ3Y.js → init-J477LKZH.js} +78 -76
  125. package/dist/{interop-IN5I2A66.js → interop-3FCM6XLG.js} +11 -11
  126. package/dist/{library-LSCATDLZ.js → library-QUQEIUG6.js} +28 -27
  127. package/dist/{memory-HYOKAGGJ.js → memory-SGGSEP65.js} +64 -63
  128. package/dist/{models-2GPMFYCM.js → models-HEKUAXXK.js} +49 -43
  129. package/dist/{monitor-E4ASVUJH.js → monitor-HKU57TYQ.js} +61 -60
  130. package/dist/{orchestrator-DDMPR3PY.js → orchestrator-VDFAEFAI.js} +919 -546
  131. package/dist/{panes-E3RUXOW5.js → panes-DN2SSFOH.js} +3 -3
  132. package/dist/{panes-IXKLOKA2.js → panes-TALGNPZT.js} +8 -8
  133. package/dist/{paths-L7LGY6RN.js → paths-NBMFAIEZ.js} +5 -5
  134. package/dist/reset-EAJFFJVB.js +344 -0
  135. package/dist/{resources-OTRSN34L.js → resources-OVKSEFVE.js} +27 -20
  136. package/dist/{run-5DEYH5QK.js → run-7DP7ZF2J.js} +113 -109
  137. package/dist/{share-IHWTLO3M.js → share-WML67FT3.js} +26 -25
  138. package/dist/{skills-IYMXMKW4.js → skills-SG662R2K.js} +39 -31
  139. package/dist/{skills-eval-DROHSJAR.js → skills-eval-VVZEUU46.js} +74 -73
  140. package/dist/{skills-inventory-D7X4L4ZX.js → skills-inventory-I2E23GET.js} +21 -20
  141. package/dist/{slash-commands-QBM7UZ3B.js → slash-commands-S7MBJDQK.js} +35 -34
  142. package/dist/{steer-Z5DO23FJ.js → steer-2LQOMCPB.js} +3 -3
  143. package/dist/{support-U7QOWY26.js → support-CC2UJBJ6.js} +6 -6
  144. package/dist/{targets-P2FUC4IL.js → targets-4QC3HIEW.js} +48 -45
  145. package/dist/{terminal-lease-YREJ3JX2.js → terminal-lease-TUHIJ6Y2.js} +2 -2
  146. package/dist/{tools-5B7RO6MV.js → tools-TFGJICCU.js} +8 -8
  147. package/dist/{trace-YMGMUM6A.js → trace-FXMXUZUF.js} +7 -7
  148. package/dist/uninstall-5PEVOE5B.js +408 -0
  149. package/dist/upgrade-M4WXY6KN.js +303 -0
  150. package/dist/{usage-ME5MPXGX.js → usage-N7ZNVLEM.js} +147 -102
  151. package/dist/{verifiers-BVZ7IWOO.js → verifiers-DJTP4XX6.js} +15 -15
  152. package/dist/{verify-5K7ZKQFC.js → verify-RWE4PPEK.js} +9 -9
  153. package/dist/{wiki-generate-F5W5QTYY.js → wiki-generate-C7IQOXSP.js} +84 -82
  154. package/dist/{with-panes-BYOJCLAM.js → with-panes-4GCGSL7J.js} +9 -9
  155. package/dist/worker/entry.js +61 -60
  156. package/docs/architecture/artifact-placement.md +1 -0
  157. package/docs/architecture/artifact-versions.md +1 -1
  158. package/docs/architecture/context-engine.md +4 -0
  159. package/docs/architecture/middleware-and-components.md +1 -1
  160. package/docs/architecture/model-catalog.md +21 -10
  161. package/docs/architecture/observability.md +12 -1
  162. package/docs/architecture/prompt-envelope-and-tools.md +2 -0
  163. package/docs/architecture/provider-adapter-cookbook.md +63 -0
  164. package/docs/architecture/safety-model.md +15 -5
  165. package/docs/guide/built-in-agents.md +17 -3
  166. package/docs/guide/commands-and-modes.md +1 -1
  167. package/docs/guide/configuration-and-targets.md +97 -9
  168. package/docs/guide/configuration-reference.md +7 -2
  169. package/docs/guide/environment-variables.md +2 -0
  170. package/docs/guide/installation-and-lifecycle.md +37 -4
  171. package/docs/guide/proactive-memory.md +66 -55
  172. package/docs/guide/skills-marketplace.md +18 -0
  173. package/docs/process/development-pipeline.md +34 -1
  174. package/docs/process/eval-runner.md +67 -3
  175. package/evals/behavioral-model.yaml +3 -2
  176. package/package.json +2 -2
  177. package/skills/README.md +7 -5
  178. package/skills/coding/ast-grep/SKILL.md +101 -30
  179. package/skills/coding/ast-grep/evals.md +26 -0
  180. package/skills/coding/coding-standards/SKILL.md +40 -5
  181. package/skills/coding/coding-standards/evals.md +23 -0
  182. package/skills/coding/prototype/SKILL.md +87 -28
  183. package/skills/coding/prototype/evals.md +19 -0
  184. package/skills/coding/tdd/SKILL.md +80 -53
  185. package/skills/coding/tdd/evals.md +20 -0
  186. package/skills/context/context-handoff/SKILL.md +43 -2
  187. package/skills/context/context-handoff/evals.md +44 -0
  188. package/skills/context/context-prime/SKILL.md +45 -15
  189. package/skills/context/context-prime/evals.md +45 -0
  190. package/skills/git/branch-closeout/SKILL.md +132 -0
  191. package/skills/git/branch-closeout/evals.md +133 -0
  192. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  193. package/skills/git/file-ticket/SKILL.md +77 -63
  194. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  195. package/skills/git/file-ticket/evals.md +31 -26
  196. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  197. package/skills/git/fix-issue/SKILL.md +87 -64
  198. package/skills/git/fix-issue/evals.md +35 -31
  199. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  200. package/skills/git/resolve-merge-conflicts/SKILL.md +100 -51
  201. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  202. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  203. package/skills/git/ship/SKILL.md +103 -67
  204. package/skills/git/ship/assets/pr-template.md +21 -0
  205. package/skills/git/ship/evals.md +44 -28
  206. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  207. package/skills/git/worktree-create/SKILL.md +80 -50
  208. package/skills/git/worktree-create/evals.md +40 -33
  209. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  210. package/skills/git/worktree-merge/SKILL.md +112 -65
  211. package/skills/git/worktree-merge/evals.md +42 -34
  212. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  213. package/skills/planning/archify/SKILL.md +196 -0
  214. package/skills/planning/archify/evals.md +65 -0
  215. package/skills/planning/architecture/SKILL.md +61 -12
  216. package/skills/planning/architecture/evals.md +65 -0
  217. package/skills/planning/backlog/SKILL.md +130 -14
  218. package/skills/planning/backlog/evals.md +142 -0
  219. package/skills/planning/prd/SKILL.md +46 -6
  220. package/skills/planning/prd/evals.md +54 -0
  221. package/skills/planning/product-intent/SKILL.md +57 -2
  222. package/skills/planning/product-intent/evals.md +70 -0
  223. package/skills/planning/tech-spec/SKILL.md +53 -2
  224. package/skills/planning/tech-spec/evals.md +73 -0
  225. package/skills/registry.yaml +58 -50
  226. package/skills/remote.yaml +13 -0
  227. package/skills/research/arxiv-literature/SKILL.md +76 -18
  228. package/skills/research/arxiv-literature/evals.md +50 -0
  229. package/skills/research/experiment-protocol/SKILL.md +20 -1
  230. package/skills/research/experiment-protocol/evals.md +23 -0
  231. package/skills/research/scientific-debugging/SKILL.md +23 -1
  232. package/skills/research/scientific-debugging/evals.md +18 -0
  233. package/skills/research/scientific-modernization/SKILL.md +26 -1
  234. package/skills/research/scientific-modernization/evals.md +27 -0
  235. package/skills/skill-marketplace.json +63 -28
  236. package/skills/workflow/cut-it/SKILL.md +65 -5
  237. package/skills/workflow/cut-it/evals.md +101 -0
  238. package/skills/workflow/design-council/SKILL.md +117 -27
  239. package/skills/workflow/design-council/evals.md +161 -0
  240. package/skills/workflow/grill-me/SKILL.md +86 -10
  241. package/skills/workflow/grill-me/evals.md +153 -0
  242. package/skills/workflow/workflow-distiller/SKILL.md +76 -17
  243. package/skills/workflow/workflow-distiller/evals.md +118 -0
  244. package/src/cli/configure-interop.ts +105 -13
  245. package/src/cli/configure-oauth.ts +57 -0
  246. package/src/cli/configure-onboarding.ts +980 -0
  247. package/src/cli/configure-target.ts +594 -0
  248. package/src/cli/configure.ts +1082 -528
  249. package/src/cli/context-map.ts +114 -0
  250. package/src/cli/context.ts +4 -0
  251. package/src/cli/index.ts +1 -0
  252. package/src/cli/lifecycle-presenter.ts +436 -0
  253. package/src/cli/models.ts +10 -2
  254. package/src/cli/modes/print.ts +5 -1
  255. package/src/cli/reset.ts +228 -106
  256. package/src/cli/run.ts +7 -2
  257. package/src/cli/select.ts +664 -0
  258. package/src/cli/skills.ts +9 -2
  259. package/src/cli/targets.ts +3 -0
  260. package/src/cli/uninstall.ts +233 -165
  261. package/src/cli/upgrade.ts +204 -149
  262. package/src/cli/usage.ts +86 -27
  263. package/src/cli/validate-model.ts +3 -3
  264. package/src/core/config.ts +56 -0
  265. package/src/core/external-diagnostic.ts +44 -0
  266. package/src/core/gateway-routing.ts +157 -0
  267. package/src/core/safe-exec.ts +17 -2
  268. package/src/core/skill-activation.ts +89 -2
  269. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  270. package/src/domains/agents/catalog.ts +1 -1
  271. package/src/domains/agents/result-contract.ts +70 -0
  272. package/src/domains/context/wiki/map-seed.ts +589 -0
  273. package/src/domains/context/wiki/plan.ts +2 -2
  274. package/src/domains/dispatch/admission.ts +29 -0
  275. package/src/domains/dispatch/agent-candidates.ts +10 -0
  276. package/src/domains/dispatch/budget-envelope.ts +86 -1
  277. package/src/domains/dispatch/capability-match.ts +1 -0
  278. package/src/domains/dispatch/capacity-lease.ts +17 -0
  279. package/src/domains/dispatch/contract.ts +11 -1
  280. package/src/domains/dispatch/extension.ts +134 -29
  281. package/src/domains/dispatch/types.ts +3 -0
  282. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  283. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  284. package/src/domains/eval/metrics/token-stream.ts +201 -31
  285. package/src/domains/eval/metrics/tracked.ts +40 -4
  286. package/src/domains/eval/runners/clio-run.ts +5 -2
  287. package/src/domains/eval/schema/suite.ts +28 -0
  288. package/src/domains/eval/schema/verdict.ts +2 -2
  289. package/src/domains/eval/suites/resolve.ts +13 -1
  290. package/src/domains/eval/suites/run.ts +24 -3
  291. package/src/domains/interop/registry.ts +6 -2
  292. package/src/domains/interop/types.ts +4 -0
  293. package/src/domains/lifecycle/migrations/index.ts +4 -0
  294. package/src/domains/memory/task-memory-policy.ts +70 -26
  295. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  296. package/src/domains/middleware/index.ts +0 -1
  297. package/src/domains/middleware/marketplace-offer.ts +3 -35
  298. package/src/domains/middleware/memory-intervention.ts +127 -32
  299. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  300. package/src/domains/middleware/skills-reminder.ts +31 -2
  301. package/src/domains/observability/compaction-usage.ts +118 -0
  302. package/src/domains/observability/cost.ts +1 -1
  303. package/src/domains/observability/extension.ts +6 -1
  304. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  305. package/src/domains/providers/contract.ts +4 -1
  306. package/src/domains/providers/extension.ts +40 -9
  307. package/src/domains/providers/model-capabilities.ts +9 -0
  308. package/src/domains/providers/model-discovery.ts +2 -0
  309. package/src/domains/providers/model-runtime-capabilities.ts +15 -5
  310. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +32 -12
  311. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  312. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  313. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  314. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  315. package/src/domains/providers/support.ts +11 -5
  316. package/src/domains/providers/target-model-cache.ts +25 -2
  317. package/src/domains/providers/types/capability-flags.ts +2 -0
  318. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  319. package/src/domains/providers/types/target-descriptor.ts +19 -0
  320. package/src/domains/resources/index.ts +3 -0
  321. package/src/domains/resources/skills/install.ts +72 -7
  322. package/src/domains/resources/skills/loader.ts +7 -0
  323. package/src/domains/resources/skills/marketplace.ts +63 -11
  324. package/src/domains/safety/autonomy.ts +15 -0
  325. package/src/domains/safety/index.ts +1 -0
  326. package/src/domains/safety/path-policy.ts +1 -1
  327. package/src/domains/safety/policy-engine.ts +34 -11
  328. package/src/domains/safety/protected-artifacts.ts +191 -88
  329. package/src/domains/safety/run-effects.ts +2 -22
  330. package/src/domains/safety/skill-authority.ts +55 -0
  331. package/src/domains/session/compaction/compact.ts +72 -22
  332. package/src/domains/session/entries.ts +6 -0
  333. package/src/domains/session/usage.ts +3 -3
  334. package/src/engine/agent.ts +13 -3
  335. package/src/engine/ai.ts +26 -8
  336. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  337. package/src/engine/api-registry.ts +3 -0
  338. package/src/engine/apis/openai-completions.ts +117 -14
  339. package/src/engine/external-subprocess.ts +114 -6
  340. package/src/entry/background-model-metadata.ts +18 -0
  341. package/src/entry/compaction-prompt.ts +57 -0
  342. package/src/entry/orchestrator.ts +405 -216
  343. package/src/entry/task-memory-lifecycle.ts +35 -0
  344. package/src/interactive/chat-loop-messages.ts +13 -4
  345. package/src/interactive/chat-loop.ts +65 -2
  346. package/src/interactive/chat-renderer.ts +1 -0
  347. package/src/interactive/cost-overlay.ts +26 -2
  348. package/src/interactive/interactive-slash-runtime.ts +2 -1
  349. package/src/interactive/renderers/worker-entry.ts +32 -0
  350. package/src/interactive/slash-commands.ts +24 -6
  351. package/src/interactive/theme/labels.ts +19 -13
  352. package/src/interactive/turn-context.ts +9 -5
  353. package/src/interactive/turn-recovery.ts +8 -0
  354. package/src/interactive/turn-runtime.ts +27 -11
  355. package/src/interactive/turn-state.ts +7 -0
  356. package/src/interactive/worker-receipts.ts +1 -0
  357. package/src/interactive/worker-stream.ts +6 -1
  358. package/src/tools/context/index.ts +30 -9
  359. package/src/tools/dispatch-arguments.ts +1 -0
  360. package/src/tools/dispatch-event-text.ts +10 -0
  361. package/src/tools/dispatch-plan.ts +1 -0
  362. package/src/tools/dispatch-runner.ts +12 -0
  363. package/src/tools/registry.ts +11 -5
  364. package/src/tools/worker-evidence.ts +3 -1
  365. package/src/worker/spec-contract.ts +4 -0
  366. package/dist/chunk-2Z2IKEXI.js +0 -1554
  367. package/dist/reset-OAQP3W4O.js +0 -230
  368. package/dist/uninstall-N34PCTGJ.js +0 -331
  369. 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,15 +1,117 @@
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.exitCode !== null) return;
93
+ if (child.pid && process.platform !== "win32") {
94
+ try {
95
+ process.kill(-child.pid, name);
96
+ return;
97
+ } catch {
98
+ // The child may not have established its group yet.
99
+ }
100
+ }
101
+ child.kill(name);
102
+ };
103
+ return {
104
+ terminate() {
105
+ if (sent) return;
106
+ sent = true;
107
+ signal("SIGTERM");
108
+ timer = setTimeout(() => signal("SIGKILL"), graceMs);
109
+ },
110
+ cleanup() {
111
+ if (timer) clearTimeout(timer);
112
+ timer = null;
113
+ },
114
+ };
13
115
  }
14
116
 
15
117
  export function waitForClose(child: {
@@ -17,7 +119,13 @@ export function waitForClose(child: {
17
119
  once(event: "close", listener: (code: number | null) => void): unknown;
18
120
  }): Promise<number> {
19
121
  return new Promise((resolve) => {
20
- child.once("error", () => resolve(1));
21
- child.once("close", (code: number | null) => resolve(code ?? 1));
122
+ let settled = false;
123
+ const finish = (code: number): void => {
124
+ if (settled) return;
125
+ settled = true;
126
+ resolve(code);
127
+ };
128
+ child.once("error", () => finish(1));
129
+ child.once("close", (code: number | null) => finish(code ?? 1));
22
130
  });
23
131
  }
@@ -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
+ }
@@ -0,0 +1,57 @@
1
+ import { constants } from "node:fs";
2
+ import { open } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+
5
+ const MAX_PROMPT_BYTES = 64 * 1024;
6
+
7
+ /** Absolute paths remain supported; relative paths are based on the current session workspace. */
8
+ export async function readCompactionSystemPrompt(
9
+ path: string | null | undefined,
10
+ workspace: string,
11
+ ): Promise<string | undefined> {
12
+ if (path === undefined || path === null) return undefined;
13
+ // Quote the configured path, not file contents. Escape invisible directional
14
+ // formatting too, and bound diagnostics for an invalid oversized filename.
15
+ const quotedPath = JSON.stringify(path).replace(
16
+ /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu,
17
+ (character) => `\\u{${character.codePointAt(0)?.toString(16)}}`,
18
+ );
19
+ const displayPath = quotedPath.length <= 1024 ? quotedPath : `${quotedPath.slice(0, 1024)}...`;
20
+ const failure = (reason: string) =>
21
+ new Error(
22
+ `context.compaction.systemPrompt ${displayPath} ${reason}; set a readable, nonempty UTF-8 file of at most ${MAX_PROMPT_BYTES} bytes (relative to the session workspace), or unset the override`,
23
+ );
24
+ if (!path.trim()) throw failure("path is empty");
25
+ let file: Awaited<ReturnType<typeof open>>;
26
+ try {
27
+ // Nonblocking open prevents a named pipe from hanging compaction before stat.
28
+ file = await open(resolve(workspace, path), constants.O_RDONLY | constants.O_NONBLOCK);
29
+ } catch {
30
+ throw failure("cannot be opened");
31
+ }
32
+ try {
33
+ if (!(await file.stat()).isFile()) throw failure("must refer to a regular file");
34
+ // Read only limit + 1, even if the file grows after stat. No unbounded readFile.
35
+ const buffer = Buffer.alloc(MAX_PROMPT_BYTES + 1);
36
+ let length = 0;
37
+ while (length < buffer.length) {
38
+ const { bytesRead } = await file.read(buffer, length, buffer.length - length, null);
39
+ if (bytesRead === 0) break;
40
+ length += bytesRead;
41
+ }
42
+ if (length > MAX_PROMPT_BYTES) throw failure("file is oversized");
43
+ let text: string;
44
+ try {
45
+ text = new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, length));
46
+ } catch {
47
+ throw failure("file is not valid UTF-8");
48
+ }
49
+ if (!text.trim()) throw failure("file is empty");
50
+ return text;
51
+ } catch (error) {
52
+ if (error instanceof Error && error.message.startsWith("context.compaction.systemPrompt ")) throw error;
53
+ throw failure("file cannot be read");
54
+ } finally {
55
+ await file.close();
56
+ }
57
+ }