@iowarp/clio-coder 0.4.1 → 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 (604) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/CONTRIBUTING.md +142 -52
  3. package/README.md +434 -473
  4. package/SECURITY.md +2 -1
  5. package/dist/{acp-ZILU3AUO.js → acp-H2NGRPWO.js} +12 -12
  6. package/dist/{agents-HYWGBGQR.js → agents-TL5LLUQP.js} +56 -55
  7. package/dist/assets/codewiki.json +1 -1
  8. package/dist/{auth-N3QT7CBO.js → auth-E5SW4HMS.js} +23 -21
  9. package/dist/builtins-IA7V7FUC.js +22 -0
  10. package/dist/{chunk-7RY5VZPH.js → chunk-2APPQIER.js} +8 -8
  11. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  12. package/dist/{chunk-JA5QWE4Z.js → chunk-2UG5F4C5.js} +1973 -1664
  13. package/dist/{chunk-5YHDIDBP.js → chunk-2UH2KFUP.js} +2 -2
  14. package/dist/{chunk-CTJ4RNAA.js → chunk-2VIKGWFZ.js} +2 -2
  15. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  16. package/dist/{chunk-GIZNH63R.js → chunk-35MSIRKH.js} +9 -4
  17. package/dist/chunk-3EBYEESD.js +314 -0
  18. package/dist/{chunk-J5LZHVIT.js → chunk-3M6DQK6S.js} +113 -35
  19. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  20. package/dist/{chunk-6FN3E6KX.js → chunk-4O6MANBS.js} +2 -2
  21. package/dist/chunk-4UVU7BJ5.js +39 -0
  22. package/dist/{chunk-VKRH2TCS.js → chunk-4WR7VSYB.js} +2 -2
  23. package/dist/{chunk-BBTJOK6Y.js → chunk-54CBCGIR.js} +5 -5
  24. package/dist/{chunk-AP73CFDC.js → chunk-5ICU3EUH.js} +2 -2
  25. package/dist/chunk-5MEZN6CB.js +1334 -0
  26. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  27. package/dist/{chunk-ABLSQ6JX.js → chunk-64I3JVYM.js} +8 -2
  28. package/dist/{chunk-AFKWHWXF.js → chunk-6PTFB5VS.js} +39 -22
  29. package/dist/{chunk-VN3SHNBN.js → chunk-7DICMOS6.js} +2 -2
  30. package/dist/chunk-7DRAWPTZ.js +360 -0
  31. package/dist/chunk-7E7I3WLS.js +3762 -0
  32. package/dist/{chunk-BJGUKIG4.js → chunk-7ZYNNDKC.js} +7 -7
  33. package/dist/{chunk-XKA2ICR3.js → chunk-AF4YM7Z4.js} +652 -252
  34. package/dist/{chunk-GVQJ5CCZ.js → chunk-AX2THNSA.js} +12 -12
  35. package/dist/{chunk-IG7BCQBA.js → chunk-B4OAX3SI.js} +65 -3
  36. package/dist/{chunk-TD3PGPQA.js → chunk-B4VEBZKF.js} +3 -3
  37. package/dist/{chunk-74YWRRU5.js → chunk-BEPZRGGU.js} +10 -10
  38. package/dist/{chunk-FEFIFZTL.js → chunk-CE5AX47J.js} +2 -2
  39. package/dist/{chunk-UAPGZHYC.js → chunk-DWUOQKRU.js} +25 -11
  40. package/dist/{chunk-THYWACCR.js → chunk-E3TPLWFX.js} +3 -3
  41. package/dist/{chunk-7EPLI7VL.js → chunk-EKCHAPYA.js} +2 -2
  42. package/dist/{chunk-HLW2MRKE.js → chunk-F4EKGO4N.js} +3 -1
  43. package/dist/{chunk-PJJ6MY27.js → chunk-F5JHEYZM.js} +7 -7
  44. package/dist/{chunk-6CCS4G3W.js → chunk-FTMGRKEF.js} +3 -3
  45. package/dist/{chunk-SINK3QR6.js → chunk-G76U63X4.js} +17 -17
  46. package/dist/{chunk-EIMVLWB3.js → chunk-GHS5EBTQ.js} +64 -9
  47. package/dist/{chunk-QMXC4JB7.js → chunk-GI7YYQ3F.js} +187 -1419
  48. package/dist/{chunk-TZSKNMZG.js → chunk-GTUD2WMY.js} +2 -1
  49. package/dist/{chunk-6HMJX2VU.js → chunk-GWZNEVM2.js} +44 -12
  50. package/dist/chunk-GYV6VZOC.js +26 -0
  51. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  52. package/dist/{chunk-UXN6JT4W.js → chunk-HEQY7ZFI.js} +3 -3
  53. package/dist/{chunk-7PWAODYW.js → chunk-I7XBWTYH.js} +2 -2
  54. package/dist/{chunk-GCSMB2KY.js → chunk-I7ZPNEJM.js} +145 -102
  55. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  56. package/dist/{chunk-QTFGO774.js → chunk-IGLP3ODT.js} +29 -16
  57. package/dist/chunk-IJNZMHLA.js +101 -0
  58. package/dist/{chunk-BDPT6GTK.js → chunk-INY6HTFL.js} +7 -7
  59. package/dist/{chunk-PBP4B7XR.js → chunk-IUE3Y34X.js} +2 -2
  60. package/dist/{chunk-6NJQITNH.js → chunk-IWT4SF4R.js} +6 -3
  61. package/dist/{chunk-R23Z6K6I.js → chunk-JDAY6FIL.js} +19 -19
  62. package/dist/chunk-JEQ3XTHC.js +42 -0
  63. package/dist/{chunk-FSP7CMNU.js → chunk-JGRC33J2.js} +50 -4
  64. package/dist/{chunk-TVH4ONAM.js → chunk-JKKCYP3C.js} +10 -10
  65. package/dist/{chunk-HJWWJ6IL.js → chunk-JSC3U7TI.js} +16 -4
  66. package/dist/{chunk-C537JADH.js → chunk-KK4JZPBQ.js} +19 -141
  67. package/dist/{chunk-K6BF4U2H.js → chunk-KKOJXO6R.js} +62 -14
  68. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  69. package/dist/{chunk-6DWBAZ5U.js → chunk-L47TF46W.js} +5 -7
  70. package/dist/{chunk-HUAS7ITX.js → chunk-LDJG7DW3.js} +91 -42
  71. package/dist/{chunk-CDNVLKUX.js → chunk-LLDJM5XK.js} +13 -7
  72. package/dist/{chunk-YPI3QQCF.js → chunk-MCEPRMZW.js} +2 -4
  73. package/dist/{chunk-Y4CAGMM6.js → chunk-MNJGS2IN.js} +5 -6
  74. package/dist/{chunk-VKFQTNDV.js → chunk-MUW2BDDH.js} +4 -4
  75. package/dist/{chunk-E67WX76H.js → chunk-MWUZBSAQ.js} +104 -152
  76. package/dist/{chunk-OJTRZGR3.js → chunk-N2Z7HLVY.js} +21 -21
  77. package/dist/{chunk-TVHHYFHE.js → chunk-NEDJ26B5.js} +2 -2
  78. package/dist/{chunk-FYUN5KZ3.js → chunk-NIQJ66N4.js} +21 -21
  79. package/dist/{chunk-U2WB7TZS.js → chunk-NMJXSHBJ.js} +97 -85
  80. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  81. package/dist/{chunk-VEGN6WIQ.js → chunk-O5CVSAG5.js} +3 -3
  82. package/dist/{chunk-MOPSG2X7.js → chunk-OML5D5V5.js} +8 -8
  83. package/dist/{chunk-2VG7KLYV.js → chunk-PAJQJ7BS.js} +5816 -3255
  84. package/dist/{chunk-ZW55JB7N.js → chunk-PUVDKJ2Y.js} +2 -2
  85. package/dist/{chunk-BTGG6BG2.js → chunk-QWGDJJYJ.js} +158 -19
  86. package/dist/chunk-R6Q67RJH.js +134 -0
  87. package/dist/{chunk-ZJLUDYFY.js → chunk-RRNP2ANY.js} +6 -6
  88. package/dist/{chunk-PVAMAVBB.js → chunk-RSJ25QSL.js} +102 -2
  89. package/dist/{chunk-NLFAQR7Z.js → chunk-S66XZJOF.js} +3 -23
  90. package/dist/chunk-SKHCAU7K.js +385 -0
  91. package/dist/chunk-SZAA6XDG.js +30 -0
  92. package/dist/{chunk-J4HBWF6Y.js → chunk-TM6LQDI3.js} +131 -28
  93. package/dist/chunk-UOIZ7DA4.js +41 -0
  94. package/dist/{chunk-MA3H6DM5.js → chunk-UPZU6GE4.js} +25 -3
  95. package/dist/{chunk-BWW4HLO4.js → chunk-UXCU4E3T.js} +8 -6
  96. package/dist/{chunk-N5UK64DP.js → chunk-V2ANDPVT.js} +4 -4
  97. package/dist/{chunk-AK5XEFVZ.js → chunk-VA5FNYMT.js} +26 -13
  98. package/dist/{chunk-6VC4OV3Z.js → chunk-VIA6RFQZ.js} +3 -11
  99. package/dist/{chunk-ZAZB4JMW.js → chunk-VKPAQYEB.js} +27 -8
  100. package/dist/{chunk-QKIFBZKT.js → chunk-VW6DOEDG.js} +497 -81
  101. package/dist/{chunk-SCYB3HA4.js → chunk-W6RRQCPQ.js} +63 -19
  102. package/dist/{chunk-2NM363SV.js → chunk-WBKFA554.js} +10 -10
  103. package/dist/{chunk-R32CLGZ6.js → chunk-WCXUNS7U.js} +82 -21
  104. package/dist/{chunk-GPPB3JBE.js → chunk-WRBAGUNF.js} +3 -3
  105. package/dist/{chunk-IXJT6DCX.js → chunk-XIVNBFZS.js} +85 -30
  106. package/dist/{chunk-UEDMSP56.js → chunk-XPWWI35G.js} +417 -201
  107. package/dist/chunk-XRZT5WY5.js +47 -0
  108. package/dist/{chunk-3QSOM6PA.js → chunk-Y3CBHOR6.js} +2 -2
  109. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  110. package/dist/{chunk-AKB4GYDL.js → chunk-YQWYVTMC.js} +5 -5
  111. package/dist/{chunk-6I5ILFOF.js → chunk-ZA4VCIGV.js} +3 -3
  112. package/dist/{chunk-7OBGU7UB.js → chunk-ZDN3Y73Y.js} +12 -18
  113. package/dist/{chunk-3I5NY75V.js → chunk-ZWPRK62N.js} +8 -5
  114. package/dist/cli/index.js +41 -39
  115. package/dist/{clio-IT3G3VQH.js → clio-CMMK4KRR.js} +9 -9
  116. package/dist/{code-nav-RK6S7F6E.js → code-nav-MDZNQS33.js} +89 -21
  117. package/dist/{components-UBWCQSRW.js → components-UCUQ4QXW.js} +4 -4
  118. package/dist/{config-3QZRWZJF.js → config-SVM5P5YI.js} +131 -84
  119. package/dist/{configure-FL7Y3KJF.js → configure-LE3IK2TJ.js} +28 -26
  120. package/dist/{context-5HE7ODYK.js → context-2OHRKS42.js} +69 -64
  121. package/dist/{context-KYQFRVDC.js → context-E3VC7RX5.js} +15 -11
  122. package/dist/{context-XNHL75JV.js → context-VNCR7KAG.js} +93 -65
  123. package/dist/{context-clear-N545L53A.js → context-clear-BW4O37TG.js} +64 -60
  124. package/dist/context-map-COB37XXN.js +505 -0
  125. package/dist/{context-working-set-QHKXSV2F.js → context-working-set-VDS25HXZ.js} +19 -18
  126. package/dist/{dispatch-runner-RGIE5PCT.js → dispatch-runner-5AHT53RF.js} +93 -82
  127. package/dist/{docs-5NAF6AU7.js → docs-PD3EXDKU.js} +21 -20
  128. package/dist/{doctor-ZGPEGHIP.js → doctor-WNNVO6FY.js} +48 -47
  129. package/dist/{eval-GXLL44RD.js → eval-7G7SGAYO.js} +287 -115
  130. package/dist/{eval-inventory-HBWSWQOK.js → eval-inventory-Y6QRFOH5.js} +4 -4
  131. package/dist/{evidence-HWLBRH3Q.js → evidence-VD6736FQ.js} +67 -64
  132. package/dist/{evolve-FTZBMNVW.js → evolve-AL3NGVRL.js} +65 -62
  133. package/dist/{extensions-VHRBEID7.js → extensions-MOVJ32NM.js} +9 -7
  134. package/dist/{fleet-CKZHJWZJ.js → fleet-QZHUMAGI.js} +114 -111
  135. package/dist/{fleet-commands-EXDXBMV6.js → fleet-commands-BAYT5FJZ.js} +10 -10
  136. package/dist/{fleet-decisions-OTHB6KRL.js → fleet-decisions-IREVMRU4.js} +7 -6
  137. package/dist/{fleet-graph-YTEZUCUT.js → fleet-graph-YCTT3HTI.js} +22 -19
  138. package/dist/{fleet-inspect-SS6YMDCK.js → fleet-inspect-QVJTDAVB.js} +58 -55
  139. package/dist/{fleet-preflight-PBY4VYOM.js → fleet-preflight-25QAFPK4.js} +4 -4
  140. package/dist/{fleet-validate-KMEM5L3S.js → fleet-validate-5O57AAJ7.js} +26 -23
  141. package/dist/{fleet-verify-QD5M7E7Q.js → fleet-verify-CPH2W2T6.js} +59 -56
  142. package/dist/{fleet-view-WAMJYNDT.js → fleet-view-SWBR3VGQ.js} +58 -55
  143. package/dist/{init-5XQRBOFV.js → init-J477LKZH.js} +82 -79
  144. package/dist/{interop-34TVO25M.js → interop-3FCM6XLG.js} +11 -11
  145. package/dist/{library-3QY6KF57.js → library-QUQEIUG6.js} +30 -27
  146. package/dist/{memory-L4UTIIIW.js → memory-SGGSEP65.js} +67 -64
  147. package/dist/{models-ZVX3QOWE.js → models-HEKUAXXK.js} +53 -46
  148. package/dist/{monitor-CEKVSYTS.js → monitor-HKU57TYQ.js} +63 -60
  149. package/dist/{orchestrator-77BAP6BC.js → orchestrator-VDFAEFAI.js} +1831 -1057
  150. package/dist/{panes-7STHOAUJ.js → panes-DN2SSFOH.js} +5 -5
  151. package/dist/{panes-SHAUIRXY.js → panes-TALGNPZT.js} +29 -14
  152. package/dist/{paths-L7LGY6RN.js → paths-NBMFAIEZ.js} +5 -5
  153. package/dist/reset-EAJFFJVB.js +344 -0
  154. package/dist/{resources-74GKTLSF.js → resources-OVKSEFVE.js} +29 -20
  155. package/dist/{run-HBAUJNNZ.js → run-7DP7ZF2J.js} +120 -115
  156. package/dist/{share-G3APVLVP.js → share-WML67FT3.js} +32 -27
  157. package/dist/{skills-35HHUKCR.js → skills-SG662R2K.js} +41 -31
  158. package/dist/{skills-eval-QN4HSHDC.js → skills-eval-VVZEUU46.js} +78 -77
  159. package/dist/{skills-inventory-J357J34F.js → skills-inventory-I2E23GET.js} +23 -20
  160. package/dist/{slash-commands-JZZCQA32.js → slash-commands-S7MBJDQK.js} +40 -36
  161. package/dist/{steer-XAVHJM22.js → steer-2LQOMCPB.js} +3 -3
  162. package/dist/{support-U7QOWY26.js → support-CC2UJBJ6.js} +6 -6
  163. package/dist/{targets-DSM6CY3M.js → targets-4QC3HIEW.js} +54 -54
  164. package/dist/{terminal-lease-JOPFUVEM.js → terminal-lease-TUHIJ6Y2.js} +5 -5
  165. package/dist/{tools-MKNWVPBH.js → tools-TFGJICCU.js} +10 -10
  166. package/dist/{trace-ECQ7TIYZ.js → trace-FXMXUZUF.js} +55 -7
  167. package/dist/uninstall-5PEVOE5B.js +408 -0
  168. package/dist/upgrade-M4WXY6KN.js +303 -0
  169. package/dist/{usage-X52N3IDJ.js → usage-N7ZNVLEM.js} +151 -104
  170. package/dist/{verifiers-EJTVVSMA.js → verifiers-DJTP4XX6.js} +15 -15
  171. package/dist/{verify-YJL6XET2.js → verify-RWE4PPEK.js} +9 -9
  172. package/dist/{web-fetch-MPIFL3LL.js → web-fetch-MPARV2K7.js} +2 -2
  173. package/dist/{wiki-generate-4NDZTQ4B.js → wiki-generate-C7IQOXSP.js} +89 -86
  174. package/dist/{with-panes-OBOBFIIR.js → with-panes-4GCGSL7J.js} +53 -257
  175. package/dist/worker/entry.js +90 -74
  176. package/docs/README.md +176 -81
  177. package/docs/{acp.md → architecture/acp.md} +36 -20
  178. package/docs/{alcf-provider.md → architecture/alcf-provider.md} +8 -5
  179. package/docs/{architecture.md → architecture/architecture.md} +43 -22
  180. package/docs/{artifact-placement.md → architecture/artifact-placement.md} +27 -23
  181. package/docs/architecture/artifact-versions.md +90 -0
  182. package/docs/{capacity-and-scheduling.md → architecture/capacity-and-scheduling.md} +26 -13
  183. package/docs/{context-engine.md → architecture/context-engine.md} +29 -25
  184. package/docs/{context-working-set.md → architecture/context-working-set.md} +13 -10
  185. package/docs/{dispatch-architecture-rationale.md → architecture/dispatch-architecture-rationale.md} +12 -9
  186. package/docs/{dispatch-typed-intent.md → architecture/dispatch-typed-intent.md} +68 -46
  187. package/docs/{evidence-and-memory.md → architecture/evidence-and-memory.md} +23 -16
  188. package/docs/{middleware-and-components.md → architecture/middleware-and-components.md} +11 -5
  189. package/docs/{model-catalog.md → architecture/model-catalog.md} +61 -27
  190. package/docs/{observability.md → architecture/observability.md} +38 -14
  191. package/docs/{pi-boundary.md → architecture/pi-boundary.md} +24 -11
  192. package/docs/{prompt-envelope-and-tools.md → architecture/prompt-envelope-and-tools.md} +57 -20
  193. package/docs/{provider-adapter-cookbook.md → architecture/provider-adapter-cookbook.md} +99 -25
  194. package/docs/{safety-model.md → architecture/safety-model.md} +35 -20
  195. package/docs/{session-lifecycle.md → architecture/session-lifecycle.md} +8 -5
  196. package/docs/architecture/time-conventions.md +125 -0
  197. package/docs/{trace-store.md → architecture/trace-store.md} +13 -5
  198. package/docs/{tui-design.md → architecture/tui-design.md} +13 -13
  199. package/docs/{worker-dispatch-mechanics.md → architecture/worker-dispatch-mechanics.md} +27 -30
  200. package/docs/{built-in-agents.md → guide/built-in-agents.md} +65 -35
  201. package/docs/{commands-and-modes.md → guide/commands-and-modes.md} +66 -61
  202. package/docs/{configuration-and-targets.md → guide/configuration-and-targets.md} +323 -297
  203. package/docs/guide/configuration-reference.md +1163 -0
  204. package/docs/{environment-variables.md → guide/environment-variables.md} +33 -28
  205. package/docs/{exit-codes-and-output.md → guide/exit-codes-and-output.md} +6 -3
  206. package/docs/{extensions-and-sharing.md → guide/extensions-and-sharing.md} +41 -14
  207. package/docs/{fleet-dispatch.md → guide/fleet-dispatch.md} +39 -43
  208. package/docs/{glossary.md → guide/glossary.md} +14 -11
  209. package/docs/{installation-and-lifecycle.md → guide/installation-and-lifecycle.md} +81 -17
  210. package/docs/guide/panes-and-files.md +290 -0
  211. package/docs/{proactive-memory.md → guide/proactive-memory.md} +131 -107
  212. package/docs/{resource-library.md → guide/resource-library.md} +13 -4
  213. package/docs/{skills-marketplace.md → guide/skills-marketplace.md} +25 -3
  214. package/docs/{tool-usage.md → guide/tool-usage.md} +87 -23
  215. package/docs/{troubleshooting.md → guide/troubleshooting.md} +9 -4
  216. package/docs/{config-knobs-audit.md → history/config-knobs-audit.md} +11 -11
  217. package/docs/{release-cut-checklist.md → history/release-cut-checklist.md} +29 -2
  218. package/docs/process/development-pipeline.md +152 -0
  219. package/docs/process/documentation-coverage.md +100 -0
  220. package/docs/process/documentation-guide.md +187 -0
  221. package/docs/{eval-runner.md → process/eval-runner.md} +108 -53
  222. package/docs/{evals-internal.md → process/evals-internal.md} +10 -10
  223. package/docs/{evolution.md → process/evolution.md} +2 -2
  224. package/docs/{fleet-demo-runbook.md → process/fleet-demo-runbook.md} +11 -7
  225. package/docs/{git-commit-provenance.md → process/git-commit-provenance.md} +11 -4
  226. package/docs/{performance-methodology.md → process/performance-methodology.md} +87 -69
  227. package/docs/{scientific-validation.md → process/scientific-validation.md} +4 -4
  228. package/evals/README.md +2 -2
  229. package/evals/behavioral-model.yaml +3 -2
  230. package/package.json +10 -8
  231. package/skills/README.md +52 -41
  232. package/skills/coding/ast-grep/SKILL.md +102 -31
  233. package/skills/coding/ast-grep/evals.md +26 -0
  234. package/skills/coding/coding-standards/SKILL.md +41 -6
  235. package/skills/coding/coding-standards/evals.md +23 -0
  236. package/skills/coding/prototype/SKILL.md +88 -29
  237. package/skills/coding/prototype/evals.md +19 -0
  238. package/skills/coding/tdd/SKILL.md +81 -54
  239. package/skills/coding/tdd/evals.md +20 -0
  240. package/skills/context/context-handoff/SKILL.md +44 -3
  241. package/skills/context/context-handoff/evals.md +44 -0
  242. package/skills/context/context-prime/SKILL.md +46 -16
  243. package/skills/context/context-prime/evals.md +45 -0
  244. package/skills/git/branch-closeout/SKILL.md +132 -0
  245. package/skills/git/branch-closeout/evals.md +133 -0
  246. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  247. package/skills/git/file-ticket/SKILL.md +78 -64
  248. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  249. package/skills/git/file-ticket/evals.md +31 -26
  250. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  251. package/skills/git/fix-issue/SKILL.md +88 -65
  252. package/skills/git/fix-issue/evals.md +35 -31
  253. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  254. package/skills/git/resolve-merge-conflicts/SKILL.md +101 -52
  255. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  256. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  257. package/skills/git/ship/SKILL.md +103 -67
  258. package/skills/git/ship/assets/pr-template.md +21 -0
  259. package/skills/git/ship/evals.md +44 -28
  260. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  261. package/skills/git/worktree-create/SKILL.md +80 -50
  262. package/skills/git/worktree-create/evals.md +40 -33
  263. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  264. package/skills/git/worktree-merge/SKILL.md +112 -65
  265. package/skills/git/worktree-merge/evals.md +42 -34
  266. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  267. package/skills/meta/clio-coder-dev/SKILL.md +9 -5
  268. package/skills/meta/clio-coder-dev/evals.md +3 -2
  269. package/skills/meta/clio-coder-test/SKILL.md +102 -95
  270. package/skills/meta/clio-coder-test/evals.md +9 -4
  271. package/skills/meta/clio-coder-test/references/harness.md +100 -124
  272. package/skills/meta/clio-coder-test/references/test-map.md +77 -50
  273. package/skills/meta/credentials/SKILL.md +2 -2
  274. package/skills/meta/find-skills/SKILL.md +2 -2
  275. package/skills/meta/herdr/SKILL.md +2 -2
  276. package/skills/meta/skill-craft/SKILL.md +22 -16
  277. package/skills/planning/archify/SKILL.md +196 -0
  278. package/skills/planning/archify/evals.md +65 -0
  279. package/skills/planning/architecture/SKILL.md +62 -13
  280. package/skills/planning/architecture/evals.md +65 -0
  281. package/skills/planning/backlog/SKILL.md +131 -15
  282. package/skills/planning/backlog/evals.md +142 -0
  283. package/skills/planning/prd/SKILL.md +47 -7
  284. package/skills/planning/prd/evals.md +54 -0
  285. package/skills/planning/product-intent/SKILL.md +58 -3
  286. package/skills/planning/product-intent/evals.md +70 -0
  287. package/skills/planning/tech-spec/SKILL.md +54 -3
  288. package/skills/planning/tech-spec/evals.md +73 -0
  289. package/skills/registry.yaml +70 -62
  290. package/skills/remote.yaml +13 -0
  291. package/skills/research/arxiv-literature/SKILL.md +77 -19
  292. package/skills/research/arxiv-literature/evals.md +50 -0
  293. package/skills/research/experiment-protocol/SKILL.md +21 -2
  294. package/skills/research/experiment-protocol/evals.md +23 -0
  295. package/skills/research/scientific-debugging/SKILL.md +24 -2
  296. package/skills/research/scientific-debugging/evals.md +18 -0
  297. package/skills/research/scientific-modernization/SKILL.md +27 -2
  298. package/skills/research/scientific-modernization/evals.md +27 -0
  299. package/skills/skill-marketplace.json +97 -62
  300. package/skills/workflow/cut-it/SKILL.md +66 -6
  301. package/skills/workflow/cut-it/evals.md +101 -0
  302. package/skills/workflow/design-council/SKILL.md +118 -28
  303. package/skills/workflow/design-council/evals.md +161 -0
  304. package/skills/workflow/grill-me/SKILL.md +87 -11
  305. package/skills/workflow/grill-me/evals.md +153 -0
  306. package/skills/workflow/workflow-distiller/SKILL.md +77 -18
  307. package/skills/workflow/workflow-distiller/evals.md +118 -0
  308. package/src/cli/args.ts +2 -2
  309. package/src/cli/bootstrap-generate.ts +1 -1
  310. package/src/cli/config-inspect.ts +65 -12
  311. package/src/cli/configure-interop.ts +105 -13
  312. package/src/cli/configure-oauth.ts +57 -0
  313. package/src/cli/configure-onboarding.ts +980 -0
  314. package/src/cli/configure-target.ts +594 -0
  315. package/src/cli/configure.ts +1082 -532
  316. package/src/cli/context-map.ts +114 -0
  317. package/src/cli/context.ts +4 -0
  318. package/src/cli/docs.ts +22 -14
  319. package/src/cli/doctor-naming.ts +5 -5
  320. package/src/cli/doctor-toolchain.ts +3 -3
  321. package/src/cli/eval.ts +1 -2
  322. package/src/cli/extensions.ts +2 -1
  323. package/src/cli/fleet.ts +1 -1
  324. package/src/cli/index.ts +3 -1
  325. package/src/cli/internal-dispatch.ts +3 -4
  326. package/src/cli/lifecycle-presenter.ts +436 -0
  327. package/src/cli/models.ts +10 -2
  328. package/src/cli/modes/print.ts +5 -1
  329. package/src/cli/panes.ts +19 -5
  330. package/src/cli/reset.ts +228 -106
  331. package/src/cli/run.ts +9 -4
  332. package/src/cli/select.ts +664 -0
  333. package/src/cli/share.ts +5 -1
  334. package/src/cli/skills-eval.ts +3 -3
  335. package/src/cli/skills.ts +9 -2
  336. package/src/cli/targets.ts +5 -6
  337. package/src/cli/trace.ts +55 -4
  338. package/src/cli/uninstall.ts +233 -165
  339. package/src/cli/upgrade.ts +204 -149
  340. package/src/cli/usage.ts +86 -27
  341. package/src/cli/validate-model.ts +3 -3
  342. package/src/cli/wiki-generate.ts +1 -1
  343. package/src/core/artifact-paths.ts +1 -1
  344. package/src/core/bash-exec.ts +131 -86
  345. package/src/core/bus-events.ts +51 -6
  346. package/src/core/config.ts +61 -1
  347. package/src/core/defaults.ts +7 -4
  348. package/src/core/dispatch-outcome.ts +16 -0
  349. package/src/core/external-diagnostic.ts +44 -0
  350. package/src/core/gateway-routing.ts +157 -0
  351. package/src/core/guardrails.ts +10 -49
  352. package/src/core/prompt-hint.ts +9 -0
  353. package/src/core/safe-exec.ts +17 -2
  354. package/src/core/skill-activation.ts +89 -2
  355. package/src/domains/agents/builtins/architect.md +2 -3
  356. package/src/domains/agents/builtins/coder.md +3 -2
  357. package/src/domains/agents/builtins/debugger.md +2 -2
  358. package/src/domains/agents/builtins/documenter.md +2 -2
  359. package/src/domains/agents/builtins/git-master.md +1 -1
  360. package/src/domains/agents/builtins/oracle.md +1 -1
  361. package/src/domains/agents/builtins/provenance.md +1 -1
  362. package/src/domains/agents/builtins/researcher.md +1 -1
  363. package/src/domains/agents/builtins/scout.md +1 -1
  364. package/src/domains/agents/builtins/tester.md +2 -2
  365. package/src/domains/agents/builtins/verifier.md +2 -2
  366. package/src/domains/agents/builtins/wiki-writer.md +1 -1
  367. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  368. package/src/domains/agents/catalog.ts +13 -15
  369. package/src/domains/agents/contract.ts +2 -0
  370. package/src/domains/agents/extension.ts +23 -1
  371. package/src/domains/agents/result-contract.ts +70 -0
  372. package/src/domains/config/keybindings.ts +8 -0
  373. package/src/domains/context/extension.ts +0 -3
  374. package/src/domains/context/wiki/map-seed.ts +589 -0
  375. package/src/domains/context/wiki/plan.ts +2 -2
  376. package/src/domains/context/working-set/path-index.ts +1 -0
  377. package/src/domains/dispatch/admission.ts +29 -0
  378. package/src/domains/dispatch/agent-candidates.ts +10 -0
  379. package/src/domains/dispatch/budget-envelope.ts +86 -1
  380. package/src/domains/dispatch/capability-match.ts +11 -0
  381. package/src/domains/dispatch/capacity-lease.ts +17 -0
  382. package/src/domains/dispatch/contract.ts +11 -1
  383. package/src/domains/dispatch/extension.ts +237 -49
  384. package/src/domains/dispatch/host-verification.ts +435 -39
  385. package/src/domains/dispatch/intent-requirements.ts +10 -0
  386. package/src/domains/dispatch/intent.ts +18 -1
  387. package/src/domains/dispatch/path-scope.ts +235 -24
  388. package/src/domains/dispatch/run-event-journal.ts +4 -15
  389. package/src/domains/dispatch/state.ts +2 -3
  390. package/src/domains/dispatch/transport.ts +45 -21
  391. package/src/domains/dispatch/types.ts +58 -3
  392. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  393. package/src/domains/eval/artifacts/store.ts +5 -0
  394. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  395. package/src/domains/eval/metrics/token-stream.ts +201 -31
  396. package/src/domains/eval/metrics/tracked.ts +40 -4
  397. package/src/domains/eval/runners/clio-run.ts +5 -2
  398. package/src/domains/eval/schema/suite.ts +28 -0
  399. package/src/domains/eval/schema/verdict.ts +2 -2
  400. package/src/domains/eval/store.ts +8 -1
  401. package/src/domains/eval/suites/resolve.ts +13 -1
  402. package/src/domains/eval/suites/run.ts +24 -3
  403. package/src/domains/evidence/trust-status.ts +10 -1
  404. package/src/domains/extensions/contract.ts +15 -1
  405. package/src/domains/extensions/discovery.ts +238 -41
  406. package/src/domains/extensions/extension.ts +105 -6
  407. package/src/domains/extensions/index.ts +24 -0
  408. package/src/domains/extensions/integrity.ts +189 -0
  409. package/src/domains/extensions/manager.ts +17 -1
  410. package/src/domains/extensions/resource-path.ts +27 -0
  411. package/src/domains/extensions/resources.ts +18 -38
  412. package/src/domains/extensions/snapshot-store.ts +39 -0
  413. package/src/domains/extensions/snapshot.ts +180 -0
  414. package/src/domains/extensions/state.ts +385 -57
  415. package/src/domains/extensions/types.ts +118 -1
  416. package/src/domains/interop/registry.ts +6 -2
  417. package/src/domains/interop/types.ts +4 -0
  418. package/src/domains/lifecycle/migrations/2026-09-01-extension-install-digests.ts +27 -0
  419. package/src/domains/lifecycle/migrations/index.ts +6 -0
  420. package/src/domains/lifecycle/naming-resources.ts +19 -4
  421. package/src/domains/lifecycle/naming-yazi.ts +10 -5
  422. package/src/domains/memory/task-memory-policy.ts +70 -26
  423. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  424. package/src/domains/middleware/contract.ts +26 -0
  425. package/src/domains/middleware/extension.ts +24 -24
  426. package/src/domains/middleware/hook-receipts.ts +27 -4
  427. package/src/domains/middleware/hooks-io.ts +65 -32
  428. package/src/domains/middleware/hooks.ts +64 -0
  429. package/src/domains/middleware/index.ts +28 -5
  430. package/src/domains/middleware/marketplace-offer.ts +3 -35
  431. package/src/domains/middleware/memory-intervention.ts +127 -32
  432. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  433. package/src/domains/middleware/registrations.ts +326 -0
  434. package/src/domains/middleware/runtime.ts +28 -0
  435. package/src/domains/middleware/skills-reminder.ts +31 -2
  436. package/src/domains/middleware/snapshot.ts +20 -7
  437. package/src/domains/mux/contract.ts +38 -0
  438. package/src/domains/mux/detect.ts +6 -13
  439. package/src/domains/mux/index.ts +1 -1
  440. package/src/domains/mux/operations.ts +44 -5
  441. package/src/domains/mux/yazi/assets/yazi.toml +2 -2
  442. package/src/domains/mux/yazi/session.ts +53 -4
  443. package/src/domains/mux/yazi/theme.ts +117 -17
  444. package/src/domains/observability/compaction-usage.ts +118 -0
  445. package/src/domains/observability/contract.ts +10 -11
  446. package/src/domains/observability/cost.ts +1 -1
  447. package/src/domains/observability/extension.ts +17 -4
  448. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  449. package/src/domains/observability/projection.ts +14 -90
  450. package/src/domains/observability/trace-store.ts +43 -7
  451. package/src/domains/prompts/compiler.ts +73 -53
  452. package/src/domains/prompts/contract.ts +15 -3
  453. package/src/domains/prompts/extension.ts +97 -9
  454. package/src/domains/prompts/fragments/identity/clio-worker.md +1 -3
  455. package/src/domains/prompts/fragments/identity/clio.md +6 -12
  456. package/src/domains/prompts/fragments/identity/docs-routing.md +1 -2
  457. package/src/domains/prompts/fragments/identity/self-awareness.md +3 -11
  458. package/src/domains/prompts/fragments/operating/contract.md +7 -15
  459. package/src/domains/prompts/fragments/operating/delegation.md +32 -34
  460. package/src/domains/prompts/fragments/operating/skills.md +10 -24
  461. package/src/domains/prompts/fragments/operating/worker.md +1 -8
  462. package/src/domains/providers/contract.ts +4 -1
  463. package/src/domains/providers/extension.ts +40 -9
  464. package/src/domains/providers/index.ts +1 -1
  465. package/src/domains/providers/model-capabilities.ts +9 -0
  466. package/src/domains/providers/model-discovery.ts +2 -0
  467. package/src/domains/providers/model-runtime-capabilities.ts +99 -25
  468. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +699 -114
  469. package/src/domains/providers/runtime-resolution.ts +31 -0
  470. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  471. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  472. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  473. package/src/domains/providers/runtimes/common/probe-helpers.ts +7 -2
  474. package/src/domains/providers/runtimes/local-native/llamacpp.ts +9 -1
  475. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  476. package/src/domains/providers/support.ts +11 -5
  477. package/src/domains/providers/target-model-cache.ts +25 -2
  478. package/src/domains/providers/types/capability-flags.ts +2 -0
  479. package/src/domains/providers/types/cost-provenance.ts +19 -0
  480. package/src/domains/providers/types/local-model-quirks.ts +85 -37
  481. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  482. package/src/domains/providers/types/target-descriptor.ts +19 -0
  483. package/src/domains/resources/index.ts +3 -0
  484. package/src/domains/resources/skills/install.ts +72 -7
  485. package/src/domains/resources/skills/loader.ts +23 -19
  486. package/src/domains/resources/skills/marketplace.ts +63 -11
  487. package/src/domains/safety/autonomy.ts +15 -0
  488. package/src/domains/safety/call-target.ts +1 -1
  489. package/src/domains/safety/index.ts +1 -0
  490. package/src/domains/safety/loop-detector.ts +7 -4
  491. package/src/domains/safety/path-policy.ts +1 -1
  492. package/src/domains/safety/policy-engine.ts +34 -11
  493. package/src/domains/safety/protected-artifacts.ts +191 -88
  494. package/src/domains/safety/run-effects.ts +2 -22
  495. package/src/domains/safety/skill-authority.ts +55 -0
  496. package/src/domains/session/compaction/compact.ts +72 -22
  497. package/src/domains/session/entries.ts +6 -0
  498. package/src/domains/session/task-board.ts +10 -9
  499. package/src/domains/session/usage.ts +3 -3
  500. package/src/domains/share/archive.ts +164 -7
  501. package/src/engine/acp/server.ts +62 -9
  502. package/src/engine/agent.ts +13 -3
  503. package/src/engine/ai.ts +26 -8
  504. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  505. package/src/engine/api-registry.ts +3 -0
  506. package/src/engine/apis/llamacpp-residency.ts +3 -4
  507. package/src/engine/apis/lmstudio.ts +3 -3
  508. package/src/engine/apis/ollama-native.ts +6 -6
  509. package/src/engine/apis/openai-completions.ts +145 -39
  510. package/src/engine/apis/output-budget.ts +8 -18
  511. package/src/engine/apis/residency.ts +8 -27
  512. package/src/engine/external-subprocess.ts +114 -6
  513. package/src/engine/gemma-channel-filter.ts +19 -0
  514. package/src/engine/loop-guard.ts +92 -12
  515. package/src/engine/worker-runtime.ts +40 -11
  516. package/src/engine/worker-tools.ts +3 -1
  517. package/src/entry/background-model-metadata.ts +18 -0
  518. package/src/entry/compaction-prompt.ts +57 -0
  519. package/src/entry/extension-hook-sources.ts +28 -0
  520. package/src/entry/extension-reload.ts +309 -0
  521. package/src/entry/orchestrator.ts +464 -251
  522. package/src/entry/task-memory-lifecycle.ts +35 -0
  523. package/src/interactive/application-controller.ts +2 -1
  524. package/src/interactive/bus-notices.ts +8 -1
  525. package/src/interactive/chat-loop-messages.ts +16 -17
  526. package/src/interactive/chat-loop.ts +75 -3
  527. package/src/interactive/chat-panel.ts +36 -13
  528. package/src/interactive/chat-renderer.ts +72 -7
  529. package/src/interactive/cost-overlay.ts +26 -2
  530. package/src/interactive/dispatch-board.ts +6 -11
  531. package/src/interactive/footer/widgets.ts +13 -0
  532. package/src/interactive/interactive-application.ts +39 -4
  533. package/src/interactive/interactive-input-runtime.ts +4 -0
  534. package/src/interactive/interactive-presentation.ts +2 -2
  535. package/src/interactive/interactive-slash-runtime.ts +4 -1
  536. package/src/interactive/overlays/extensions.ts +9 -1
  537. package/src/interactive/overlays/help-reference.ts +13 -0
  538. package/src/interactive/overlays/settings.ts +27 -16
  539. package/src/interactive/panes-runtime.ts +111 -35
  540. package/src/interactive/prompt-cache-identity.ts +88 -0
  541. package/src/interactive/renderers/worker-entry.ts +32 -0
  542. package/src/interactive/slash-commands.ts +153 -20
  543. package/src/interactive/stream-pacing-policy.ts +0 -23
  544. package/src/interactive/theme/labels.ts +19 -13
  545. package/src/interactive/turn-context.ts +39 -20
  546. package/src/interactive/turn-recovery.ts +8 -0
  547. package/src/interactive/turn-runtime.ts +27 -11
  548. package/src/interactive/turn-state.ts +7 -0
  549. package/src/interactive/worker-receipts.ts +1 -0
  550. package/src/interactive/worker-stream.ts +6 -1
  551. package/src/interactive/yazi-bridge.ts +60 -6
  552. package/src/tools/agent-tools.ts +30 -1
  553. package/src/tools/artifact.ts +2 -2
  554. package/src/tools/ask-user.ts +3 -3
  555. package/src/tools/bash.ts +1 -1
  556. package/src/tools/bootstrap.ts +4 -0
  557. package/src/tools/builtin-tool-catalog.ts +52 -22
  558. package/src/tools/codewiki/code-nav-surface.ts +6 -0
  559. package/src/tools/codewiki/code-nav.ts +99 -13
  560. package/src/tools/context/docs-engine.ts +20 -7
  561. package/src/tools/context/index.ts +59 -21
  562. package/src/tools/core-bootstrap.ts +28 -6
  563. package/src/tools/credential-present.ts +1 -2
  564. package/src/tools/dispatch-arguments.ts +6 -1
  565. package/src/tools/dispatch-event-text.ts +10 -0
  566. package/src/tools/dispatch-plan.ts +49 -4
  567. package/src/tools/dispatch-run-events.ts +1 -1
  568. package/src/tools/dispatch-runner.ts +12 -0
  569. package/src/tools/dispatch-schema.ts +338 -0
  570. package/src/tools/dispatch-types.ts +3 -0
  571. package/src/tools/dispatch.ts +9 -254
  572. package/src/tools/ledger.ts +3 -5
  573. package/src/tools/monitor-surface.ts +5 -13
  574. package/src/tools/observation.ts +4 -5
  575. package/src/tools/panes-surface.ts +4 -11
  576. package/src/tools/panes.ts +4 -2
  577. package/src/tools/policy.ts +15 -2
  578. package/src/tools/read.ts +5 -6
  579. package/src/tools/registry.ts +41 -12
  580. package/src/tools/result-shaping.ts +18 -14
  581. package/src/tools/steer-surface.ts +1 -1
  582. package/src/tools/tasks.ts +1 -1
  583. package/src/tools/truncate.ts +6 -5
  584. package/src/tools/verify/surface.ts +6 -12
  585. package/src/tools/web-fetch-surface.ts +1 -3
  586. package/src/tools/worker-evidence.ts +3 -1
  587. package/src/worker/spec-contract.ts +4 -0
  588. package/dist/builtins-UJLMOVOV.js +0 -17
  589. package/dist/chunk-5QIAJV2D.js +0 -48
  590. package/dist/chunk-JZWT5J3Y.js +0 -814
  591. package/dist/chunk-K7VKOLQQ.js +0 -15
  592. package/dist/chunk-PMZCIOCJ.js +0 -25
  593. package/dist/chunk-SUW5DORT.js +0 -819
  594. package/dist/chunk-UOV2BYIW.js +0 -107
  595. package/dist/chunk-WR6U3OVP.js +0 -45
  596. package/dist/chunk-Y45G3AXC.js +0 -1558
  597. package/dist/reset-EOLM7GVE.js +0 -230
  598. package/dist/uninstall-N34PCTGJ.js +0 -331
  599. package/dist/upgrade-H7TOM7YL.js +0 -323
  600. package/docs/artifact-versions.md +0 -67
  601. package/docs/development-pipeline.md +0 -121
  602. package/docs/documentation-coverage.md +0 -46
  603. package/docs/documentation-guide.md +0 -167
  604. package/docs/time-conventions.md +0 -101
@@ -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
  }
@@ -6,6 +6,25 @@ import type {
6
6
  ThinkingContent,
7
7
  } from "@earendil-works/pi-ai";
8
8
  import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
9
+ import { inferLocalModelFamily } from "../domains/providers/model-family.js";
10
+
11
+ /**
12
+ * Whether a model id names a Gemma 4 checkpoint, whose chat template writes its
13
+ * private channel with the markers below when llama-server leaves the thought
14
+ * inline in `content` instead of extracting it to `reasoning_content`.
15
+ *
16
+ * Keyed on the wire model id rather than on the resolved family, because
17
+ * `capabilityFamily` returns the knowledge-base family whenever the catalog
18
+ * matches an id and every Gemma entry there names a build (`gemma4-26b-a4b`,
19
+ * `gemma-4-31b-it-qat-mtp`, `gemopus-4-31b-it`), never the literal `gemma-4`
20
+ * that `inferLocalModelFamily` produces for an id the catalog does not match.
21
+ * Gating on the family therefore ran the filter only for Gemma ids the catalog
22
+ * did not know, so naming `gemma4-26b-moe` and `gemma4-31b-dense` in it (issue
23
+ * #263) turned the filter off for the two ids the router actually serves.
24
+ */
25
+ export function usesGemmaChannelMarkers(modelId: string): boolean {
26
+ return inferLocalModelFamily(modelId) === "gemma-4";
27
+ }
9
28
 
10
29
  const THOUGHT_START = /<\|channel>[^\n]*\n/;
11
30
  const BARE_THOUGHT_START = /^\s*(?:thought|own[- ]?(?:thought|think))\s*\n/i;
@@ -87,9 +87,9 @@ export const LOOP_SYNTHESIS_BACKSTOP_DENIALS = 2;
87
87
  export const WIDE_BATCH_DENIAL_FLOOR = 32;
88
88
 
89
89
  /**
90
- * Default lifetime tool-call cap for a worker run. Value and env override live
91
- * in core/guardrails.ts (`CLIO_CODER_WORKER_TOOL_CALL_CAP`); re-exported here for
92
- * tests and callers that reason about the guard's tuning in one place.
90
+ * Default lifetime tool-call cap for a worker run. The configurable value lives
91
+ * at `fleet.limits.toolCallsPerRun`; this default is re-exported for tests and
92
+ * callers that reason about the guard's tuning in one place.
93
93
  */
94
94
  export const DEFAULT_WORKER_TOOL_CALL_CAP = GUARDRAIL_DEFAULTS.workerToolCallCap;
95
95
 
@@ -105,7 +105,7 @@ export const DEFAULT_WORKER_TOOL_CALL_CAP = GUARDRAIL_DEFAULTS.workerToolCallCap
105
105
  * DISTINCT unproductive calls. Legitimate deep work (a repo-wide audit runs
106
106
  * 25+ productive calls in one turn) must not be decapitated by it; mainstream
107
107
  * harnesses run 100+ calls per turn with no ceiling at all. Value and env
108
- * override (`CLIO_CODER_TURN_TOOL_CALL_BUDGET`) live in core/guardrails.ts.
108
+ * live at `safety.limits.chatToolCallsPerTurn`.
109
109
  */
110
110
  export const DEFAULT_ORCH_TURN_TOOL_CALL_BUDGET = GUARDRAIL_DEFAULTS.turnToolCallBudget;
111
111
  /**
@@ -185,6 +185,70 @@ function loopBlockBaseReason(tool: string, repeatCount: number, priorSuccesses:
185
185
  );
186
186
  }
187
187
 
188
+ /** True when a sanitized locked reply holds nothing but the fallback notice. */
189
+ export function isLockedSynthesisFallbackOnly(message: AgentMessage | undefined): boolean {
190
+ if (!message || typeof message !== "object" || (message as { role?: unknown }).role !== "assistant") return false;
191
+ const content = (message as { content?: unknown }).content;
192
+ if (!Array.isArray(content)) return false;
193
+ const text = content
194
+ .filter((block): block is { type: "text"; text: string } => block?.type === "text" && typeof block.text === "string")
195
+ .map((block) => block.text)
196
+ .join("")
197
+ .trim();
198
+ return text === lockedSynthesisFallbackText();
199
+ }
200
+
201
+ export const LOCKED_SYNTHESIS_REPROMPT_TOOL = "clio_synthesis_reprompt";
202
+
203
+ /**
204
+ * One bounded re-prompt after a locked round came back as tool-call markup
205
+ * only. Delivered as a paired synthetic tool exchange, never a user turn, for
206
+ * the same reason the result-contract repair is (#55: a late user message
207
+ * re-renders every earlier assistant turn on templates keyed off the last
208
+ * user message and invalidates the whole prompt cache).
209
+ */
210
+ export function lockedSynthesisRepromptMessages(
211
+ attempt: number,
212
+ origin: { provider: string; api: string; model: string },
213
+ ): ReadonlyArray<AgentMessage> {
214
+ const id = `clio-synthesis-reprompt-${attempt}`;
215
+ const timestamp = Date.now();
216
+ return [
217
+ {
218
+ role: "assistant",
219
+ content: [{ type: "toolCall", id, name: LOCKED_SYNTHESIS_REPROMPT_TOOL, arguments: {} }],
220
+ stopReason: "toolUse",
221
+ usage: {
222
+ input: 0,
223
+ output: 0,
224
+ cacheRead: 0,
225
+ cacheWrite: 0,
226
+ totalTokens: 0,
227
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
228
+ },
229
+ ...origin,
230
+ timestamp,
231
+ },
232
+ {
233
+ role: "toolResult",
234
+ toolCallId: id,
235
+ toolName: LOCKED_SYNTHESIS_REPROMPT_TOOL,
236
+ content: [
237
+ {
238
+ type: "text",
239
+ text:
240
+ "Your previous reply contained only tool-call markup, which cannot run: tool calls are disabled for " +
241
+ "the rest of this run. Everything you gathered is already in the conversation above. Write the " +
242
+ "final answer now as plain prose (or the exact result format the task asked for), with no tool-call " +
243
+ "markup of any kind.",
244
+ },
245
+ ],
246
+ isError: true,
247
+ timestamp,
248
+ },
249
+ ] as unknown as ReadonlyArray<AgentMessage>;
250
+ }
251
+
188
252
  /**
189
253
  * Directive returned when a turn is locked to synthesis: tool use is over, so
190
254
  * the model must answer from what it already gathered. Fed back to the model as
@@ -360,8 +424,8 @@ export interface OrchTurnToolCallBudget {
360
424
  hard: number;
361
425
  }
362
426
 
363
- export function readOrchTurnToolCallBudget(env: NodeJS.ProcessEnv = process.env): OrchTurnToolCallBudget {
364
- const soft = resolveGuardrail("turnToolCallBudget", env);
427
+ export function readOrchTurnToolCallBudget(): OrchTurnToolCallBudget {
428
+ const soft = resolveGuardrail("turnToolCallBudget");
365
429
  return { soft, hard: soft + ORCH_TURN_TOOL_CALL_HARD_MARGIN };
366
430
  }
367
431
 
@@ -416,7 +480,7 @@ export interface CreateLoopGuardRegistrationOptions {
416
480
  * the turn over the bus. Absent for workers, which rely on the lifetime
417
481
  * {@link toolCallCap} instead.
418
482
  */
419
- turnToolCallBudget?: OrchTurnToolCallBudget;
483
+ turnToolCallBudget?: OrchTurnToolCallBudget | (() => OrchTurnToolCallBudget);
420
484
  /**
421
485
  * When the per-turn loop-block budget is exhausted, lock tool use for the
422
486
  * rest of the turn instead of cancelling it outright. Every further call is
@@ -467,7 +531,6 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
467
531
  (cap === undefined || options.toolCallSoftLimit <= cap)
468
532
  ? options.toolCallSoftLimit
469
533
  : undefined;
470
- const turnBudget = options.turnToolCallBudget;
471
534
  const synthesisLockout = options.turnSynthesisLockout === true;
472
535
  let softReadReserve = options.toolCallSoftReadReserve ?? 0;
473
536
  let softReadReserveThreshold =
@@ -516,6 +579,12 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
516
579
  let reserveDirectiveEmitted = false;
517
580
  const blocksByTurn = new Map<string, number>();
518
581
  const callsByTurn = new Map<string, number>();
582
+ /**
583
+ * Per-turn count of successful write or edit calls. The identical-call key
584
+ * carries it, so a check rerun after a change (fix-verify) is a fresh call
585
+ * while a verbatim repeat with nothing changed in between still counts.
586
+ */
587
+ const mutationEpochByTurn = new Map<string, number>();
519
588
  /**
520
589
  * Turns whose tool use is locked to synthesis after reaching the block
521
590
  * budget. Key present means locked; the value carries the block that tripped
@@ -594,6 +663,9 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
594
663
  // (admission returns before execution), so only real results anchor.
595
664
  const recordSuccessfulResult = (input: MiddlewareHookInput): void => {
596
665
  if (input.metadata?.resultKind !== "ok") return;
666
+ if (input.toolName === ToolNames.Write || input.toolName === ToolNames.Edit) {
667
+ bumpBoundedCounter(mutationEpochByTurn, input.turnId ?? NO_TURN_BUCKET);
668
+ }
597
669
  if (!resultCarriesEvidence(input.toolResultDetails)) return;
598
670
  const tool = input.toolName;
599
671
  if (typeof tool !== "string" || tool.length === 0) return;
@@ -748,11 +820,11 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
748
820
 
749
821
  const emitBudgetEvent = (
750
822
  input: MiddlewareHookInput,
823
+ turnBudget: OrchTurnToolCallBudget,
751
824
  callsThisTurn: number,
752
825
  interrupted: boolean,
753
826
  at: number,
754
827
  ): void => {
755
- if (turnBudget === undefined) return;
756
828
  const payload: ToolBudgetExceededPayload = {
757
829
  tool: input.toolName ?? "unknown",
758
830
  callsThisTurn,
@@ -766,6 +838,8 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
766
838
  };
767
839
 
768
840
  const evaluateTurnBudget = (input: MiddlewareHookInput, now: number): ReadonlyArray<MiddlewareEffect> | null => {
841
+ const budgetSource = options.turnToolCallBudget;
842
+ const turnBudget = typeof budgetSource === "function" ? budgetSource() : budgetSource;
769
843
  if (turnBudget === undefined) return null;
770
844
  const turnKey = input.turnId ?? NO_TURN_BUCKET;
771
845
  const callsThisTurn = bumpBoundedCounter(callsByTurn, turnKey);
@@ -773,7 +847,7 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
773
847
  // Hard ceiling: interrupt the turn the same way the block budget does.
774
848
  // The bus event drives chat.cancel() in the interactive layer; the
775
849
  // block effect makes the in-flight attempt fail with the same reason.
776
- if (callsThisTurn === turnBudget.hard) emitBudgetEvent(input, callsThisTurn, true, now);
850
+ if (callsThisTurn === turnBudget.hard) emitBudgetEvent(input, turnBudget, callsThisTurn, true, now);
777
851
  return [
778
852
  {
779
853
  kind: "block_tool",
@@ -792,7 +866,7 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
792
866
  // ever run. The operator sees one warn notice per turn (the first
793
867
  // crossing); the model keeps getting the directive on every further
794
868
  // over-budget attempt so it cannot quietly resume spraying calls.
795
- if (callsThisTurn === turnBudget.soft) emitBudgetEvent(input, callsThisTurn, false, now);
869
+ if (callsThisTurn === turnBudget.soft) emitBudgetEvent(input, turnBudget, callsThisTurn, false, now);
796
870
  return [
797
871
  {
798
872
  kind: "block_tool",
@@ -1084,7 +1158,13 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
1084
1158
  // regardless of age, so an unscoped key would let one identical call
1085
1159
  // per user turn (rerunning a build across turns) accumulate into a
1086
1160
  // false loop.
1087
- const verdict = options.safety.observeLoop(`${turnKey}|${fingerprint}`, now);
1161
+ // The epoch scopes repeats to "since the last successful write or
1162
+ // edit": a live session repeated a batch of eleven read-only bash
1163
+ // calls five times, every one admitted, because the detector kept
1164
+ // only a 30 s window or the last four attempts and no batch ever
1165
+ // showed a third repeat inside either.
1166
+ const epoch = mutationEpochByTurn.get(turnKey) ?? 0;
1167
+ const verdict = options.safety.observeLoop(`${turnKey}|${epoch}|${fingerprint}`, now);
1088
1168
  if (verdict.looping) {
1089
1169
  const tool = input.toolName ?? "unknown";
1090
1170
  const priorSuccesses = succeededFingerprints.get(fingerprint) ?? 0;
@@ -73,8 +73,10 @@ import { startClaudeSdkWorkerRun } from "./claude/sdk-runtime.js";
73
73
  import { startClaudeCodeWorkerRun } from "./claude/subprocess-runtime.js";
74
74
  import {
75
75
  createLoopGuardRegistration,
76
+ isLockedSynthesisFallbackOnly,
76
77
  isLoopGuardSynthesisBackstopReason,
77
78
  lockedSynthesisFallbackText,
79
+ lockedSynthesisRepromptMessages,
78
80
  resolveDeliveryTools,
79
81
  sanitizeLockedSynthesisMessage,
80
82
  workerLoopBlockBudget,
@@ -378,9 +380,8 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
378
380
  // already surfaced.
379
381
  const workerSettings = validateSettingsFile().settings;
380
382
  setGlobalDefaultMaxOutputTokens(workerSettings.chat.maxOutputTokens);
381
- // Same mirroring for guardrail policy: the worker's loop-guard cap and tool
382
- // byte caps resolve settings-first, so the fresh process needs the section
383
- // installed before any registry or tool construction reads it.
383
+ // Same mirroring for guardrail policy: the fresh process needs its settings
384
+ // projection installed before any registry or tool construction reads it.
384
385
  configureGuardrails(guardrailValuesFromSettings(workerSettings));
385
386
  const fauxModel = registerFauxFromEnv();
386
387
  // Workers are bounded runs against an admission-verified recipe surface.
@@ -412,8 +413,10 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
412
413
  : {}),
413
414
  });
414
415
  // Flipped by the loop guard's lockout callback; read by onPayload below to
415
- // force the remaining model rounds text-only via tool_choice none.
416
+ // force the remaining model rounds text-only by removing the tool surface
417
+ // (tool_choice none on Anthropic; see patchToolSurfaceLockedPayload).
416
418
  let synthesisToolLock = false;
419
+ let lockedSynthesisReprompts = 0;
417
420
  let workerBoundFailure: string | null = null;
418
421
  let workerBoundAborted = false;
419
422
  let abortWorkerForBound: (() => void) | null = null;
@@ -596,6 +599,7 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
596
599
  includeInteractiveTools: false,
597
600
  invokeOptions: () => ({
598
601
  correlationId: `worker-model-round-${workerModelRound}`,
602
+ toolResultMaxBytes: workerSettings.context.toolResultMaxBytes,
599
603
  ...(agentSkillPolicy ? { pendingSkillPolicy: agentSkillPolicy } : {}),
600
604
  }),
601
605
  });
@@ -661,13 +665,38 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
661
665
  pendingReadCitations.delete(event.toolCallId);
662
666
  if (request !== undefined && event.isError !== true) recordObservedRead(request, event.result);
663
667
  }
664
- // Synthesis-locked run: a model that ignores tool_choice none emits its
665
- // chat template's tool-call syntax as plain text. Sanitize the finished
666
- // message in place before it hits stdout; pi stores this same object in
667
- // agent state, so the NDJSON event, the dispatch consumer's answer
668
- // reconstruction, and any later provider round all see the same text.
668
+ // Synthesis-locked run: the round ships no tool surface, so a model that
669
+ // calls a tool anyway lands its chat template's tool-call syntax in the
670
+ // reply as plain text. Sanitize the finished message in place before it
671
+ // hits stdout; pi stores this same object in agent state, so the NDJSON
672
+ // event, the dispatch consumer's answer reconstruction, and any later
673
+ // provider round all see the same text.
674
+ // A markup-only locked reply re-prompted this round; the result-contract
675
+ // check below stands aside so the round costs the one re-prompt, not one
676
+ // of the contract's bounded repair slots (on ornith both were queued for
677
+ // the same message and the run then ran out of repairs on a later
678
+ // grounding violation).
679
+ let repromptedThisMessage = false;
669
680
  if (synthesisToolLock && event.type === "message_end") {
670
- sanitizeLockedSynthesisMessage(event.message);
681
+ const stripped = sanitizeLockedSynthesisMessage(event.message);
682
+ // A model that calls a tool anyway hands its markup back as text and
683
+ // the sanitizer leaves only the fallback notice: the training habit
684
+ // survives the strip (Qwen3.8 did it in 1 of 5 stripped runs). One
685
+ // re-prompt, delivered as a tool exchange like the result-contract
686
+ // repair, asks for the answer in prose; a second markup-only reply
687
+ // keeps the notice.
688
+ if (stripped && lockedSynthesisReprompts < 1 && isLockedSynthesisFallbackOnly(event.message)) {
689
+ lockedSynthesisReprompts += 1;
690
+ repromptedThisMessage = true;
691
+ process.stderr.write("[worker] synthesis lock: reply was tool-call markup only; re-prompting once for prose\n");
692
+ for (const message of lockedSynthesisRepromptMessages(lockedSynthesisReprompts, {
693
+ provider: model.provider,
694
+ api: model.api,
695
+ model: model.id,
696
+ })) {
697
+ agent.followUp(message as unknown as AgentMessage);
698
+ }
699
+ }
671
700
  }
672
701
  // Bounded terminal-contract repair, on whichever message ends the run.
673
702
  // A worker that finishes inside its budget never trips the synthesis
@@ -676,7 +705,7 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
676
705
  // same contract against the sealed receipt; this is the only point at
677
706
  // which the model can still act on the validator's reason.
678
707
  const contract = input.resultContract;
679
- if (contract && event.type === "message_end" && isTerminalAssistantMessage(event.message)) {
708
+ if (contract && !repromptedThisMessage && event.type === "message_end" && isTerminalAssistantMessage(event.message)) {
680
709
  const violation = terminalContractViolation(
681
710
  contract,
682
711
  event.message,
@@ -155,8 +155,10 @@ export function createWorkerToolRegistry(
155
155
  // The ledger tool registers unconditionally. attestedToolSignature signs the
156
156
  // names a bare registry produces, so a conditional registration would drift
157
157
  // the signature and fail admission; only the injected port varies, and a run
158
- // without one answers that it has no coordination ledger.
158
+ // without one answers that it has no coordination ledger. The explicit flag
159
+ // is what keeps that true now that the session registry omits the tool.
159
160
  const registration = registerCoreTools(registry, {
161
+ includeLedgerTools: true,
160
162
  ...(agentLedger ? { agentLedger } : {}),
161
163
  // A worker cannot install a skill or reach the operator who could, so
162
164
  // its skill listing carries installed (or bound) skills only; the
@@ -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
+ }
@@ -0,0 +1,28 @@
1
+ import type { ExtensionSnapshot } from "../domains/extensions/index.js";
2
+ import type { CapturedHookSourceSet } from "../domains/middleware/index.js";
3
+
4
+ /**
5
+ * Adapt an extension snapshot's captured hook declarations to the
6
+ * middleware-native captured source set. This is the only place the two
7
+ * domains' shapes meet; middleware never imports extension types.
8
+ */
9
+ export function capturedHookSourcesFor(
10
+ snapshot: Pick<ExtensionSnapshot, "generation" | "hookSources">,
11
+ ): CapturedHookSourceSet {
12
+ return {
13
+ generation: snapshot.generation,
14
+ sources: snapshot.hookSources.map((source) => ({
15
+ provenance: {
16
+ id: source.provenance.id,
17
+ scope: source.provenance.scope,
18
+ ...(source.provenance.sourcePath !== undefined ? { sourcePath: source.provenance.sourcePath } : {}),
19
+ canonicalRoot: source.provenance.canonicalRoot,
20
+ manifestDigest: source.provenance.manifestDigest,
21
+ contentDigest: source.provenance.contentDigest,
22
+ },
23
+ declarationsDigest: source.declarationsDigest,
24
+ declarations: source.declarations,
25
+ ...(source.parseError !== undefined ? { parseError: source.parseError } : {}),
26
+ })),
27
+ };
28
+ }