@iowarp/clio-coder 0.4.4 → 0.4.5

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 (555) hide show
  1. package/CHANGELOG.md +92 -2
  2. package/CONTRIBUTING.md +25 -15
  3. package/README.md +340 -27
  4. package/dist/{acp-WNAYYF4F.js → acp-H3CU2HBQ.js} +25 -24
  5. package/dist/{agents-3OKXHLOI.js → agents-IXQQZ7BD.js} +77 -77
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-VKNNMGPU.js → auth-R7KYF7N3.js} +38 -38
  8. package/dist/builtins-CRFDQUVP.js +22 -0
  9. package/dist/{chunk-TQAHXW6Y.js → chunk-22PQ4TX4.js} +4 -4
  10. package/dist/{chunk-NEDJ26B5.js → chunk-2GPGAE5W.js} +6 -51
  11. package/dist/{chunk-IKCO5N3L.js → chunk-2QPI3TR3.js} +3 -3
  12. package/dist/{chunk-HRQMFLJP.js → chunk-32KJPXAM.js} +6 -6
  13. package/dist/{chunk-65DEGPJ6.js → chunk-3A3CUMKA.js} +6 -4
  14. package/dist/{chunk-JCI2ROMZ.js → chunk-3T4DXWP6.js} +7 -7
  15. package/dist/{chunk-DR52UMZW.js → chunk-3UW3KU4A.js} +2 -2
  16. package/dist/{chunk-6Q3CYFD3.js → chunk-4SVIVOMC.js} +184 -33
  17. package/dist/{chunk-W54I7H25.js → chunk-5A6XR3RJ.js} +44 -19
  18. package/dist/{chunk-YPC6ZR5L.js → chunk-5CMARGPF.js} +119 -5
  19. package/dist/{chunk-KXDSS5WJ.js → chunk-5CY4Q6QI.js} +4 -4
  20. package/dist/{chunk-I2DWJ4GM.js → chunk-5DDOTTG4.js} +54 -42
  21. package/dist/{chunk-D4CJV46V.js → chunk-5GZICIYO.js} +3 -3
  22. package/dist/{chunk-4IUZQIJ3.js → chunk-5PHVPU4G.js} +2 -2
  23. package/dist/{chunk-3ODX73FK.js → chunk-5VTJP36Q.js} +4 -4
  24. package/dist/{chunk-54X7T7DK.js → chunk-5ZMXGW7P.js} +79 -7
  25. package/dist/chunk-5ZSZEYJK.js +119 -0
  26. package/dist/{chunk-IXIY2H4R.js → chunk-633EXGZA.js} +5 -5
  27. package/dist/{chunk-CUSRQKPU.js → chunk-6CDNRHWH.js} +299 -89
  28. package/dist/{chunk-EBY63B7G.js → chunk-6EXUUOSN.js} +2 -2
  29. package/dist/{chunk-SVNXH4L2.js → chunk-6ICZKDUC.js} +2 -2
  30. package/dist/{chunk-JEIYHLOR.js → chunk-6SYEGZI3.js} +12 -12
  31. package/dist/{chunk-DQOVN6KV.js → chunk-6YA46L5A.js} +4 -4
  32. package/dist/{chunk-NZMNUPZZ.js → chunk-77CZRFTE.js} +3 -3
  33. package/dist/{chunk-6PAZTBPA.js → chunk-7HBHHVCK.js} +15 -6
  34. package/dist/{chunk-25QBEXRS.js → chunk-7TKWWPD7.js} +5 -4
  35. package/dist/{chunk-2VTFPG5O.js → chunk-7WLRF2U5.js} +2 -2
  36. package/dist/chunk-7WUZEB2M.js +165 -0
  37. package/dist/{chunk-3UUXNFEX.js → chunk-7YJWEF2L.js} +61 -34
  38. package/dist/{chunk-3EBYEESD.js → chunk-A46ILRS2.js} +5 -5
  39. package/dist/{chunk-UOIZ7DA4.js → chunk-AP7HDJOP.js} +2 -2
  40. package/dist/{chunk-APJ265NV.js → chunk-B5GGDIHT.js} +146 -146
  41. package/dist/{chunk-PMZVFWSR.js → chunk-B5ZTR6KZ.js} +2 -2
  42. package/dist/{chunk-I5FWO7L5.js → chunk-B6UM3OZC.js} +2 -2
  43. package/dist/{chunk-2CU2H6KE.js → chunk-BBV5TKE6.js} +5 -5
  44. package/dist/{chunk-4O6MANBS.js → chunk-BS4GAMUB.js} +14 -10
  45. package/dist/{chunk-S66XZJOF.js → chunk-C27U2TYG.js} +2 -2
  46. package/dist/{chunk-QZWQA4DE.js → chunk-CBSKDFWL.js} +3 -3
  47. package/dist/{chunk-2LEAKD4C.js → chunk-CKK25WN3.js} +3 -3
  48. package/dist/{chunk-BYP5D4HI.js → chunk-CTTI7G3T.js} +2 -2
  49. package/dist/{chunk-XAKHZX5N.js → chunk-CVDAFGPW.js} +4 -4
  50. package/dist/{chunk-RKKLTLYB.js → chunk-CXQ37WJH.js} +3 -3
  51. package/dist/{chunk-GTUD2WMY.js → chunk-DBREN25X.js} +44 -9
  52. package/dist/{chunk-YNFKXPEC.js → chunk-DIMRLNIC.js} +35 -66
  53. package/dist/{chunk-NDDGJUXD.js → chunk-DNCE23JJ.js} +2 -2
  54. package/dist/{chunk-HVDIIIQW.js → chunk-DNHVRBH4.js} +3 -3
  55. package/dist/{chunk-XULDXHTN.js → chunk-DRD2A54W.js} +102 -50
  56. package/dist/{chunk-D7E7WCDF.js → chunk-DVNAPPXU.js} +2 -2
  57. package/dist/{chunk-YXLYO42X.js → chunk-DXVMLM3Z.js} +3 -2
  58. package/dist/{chunk-5MEZN6CB.js → chunk-DZK63TZJ.js} +6 -6
  59. package/dist/{chunk-IGLP3ODT.js → chunk-E3BQTQUP.js} +15 -15
  60. package/dist/{chunk-IWHMRKLL.js → chunk-E3LEPLQ2.js} +2 -2
  61. package/dist/{chunk-3R73A4XB.js → chunk-ECBHH7LU.js} +3 -3
  62. package/dist/chunk-ECOMVOHP.js +139 -0
  63. package/dist/chunk-EFSEIZE6.js +56 -0
  64. package/dist/{chunk-FEAXX7B6.js → chunk-EHCPSZJF.js} +4 -4
  65. package/dist/{chunk-4NSRCOYP.js → chunk-EHOSNVVM.js} +3 -2
  66. package/dist/{chunk-7UGL4MB5.js → chunk-EQEJJ53P.js} +10 -10
  67. package/dist/{chunk-GWZNEVM2.js → chunk-EWD5P3GK.js} +9 -9
  68. package/dist/{chunk-6UINWWS6.js → chunk-F34AR6O3.js} +14 -14
  69. package/dist/{chunk-GHS5EBTQ.js → chunk-FKIOX6DS.js} +29 -20
  70. package/dist/{chunk-HAXOFFRH.js → chunk-FQOOGXBH.js} +7 -7
  71. package/dist/{chunk-Y55JBDO5.js → chunk-FSTIBAGM.js} +38 -23
  72. package/dist/{chunk-ZTJUVLTU.js → chunk-FXFVDPWY.js} +2 -2
  73. package/dist/{chunk-XPLRXC72.js → chunk-GD55RTIW.js} +3 -3
  74. package/dist/{chunk-5B2AEOW5.js → chunk-GH5ZR6JP.js} +472 -472
  75. package/dist/{chunk-TOIVGRUX.js → chunk-GJ24ODAX.js} +5 -5
  76. package/dist/{chunk-QUFRYSWI.js → chunk-GNQRWTMY.js} +129 -48
  77. package/dist/{chunk-5ICU3EUH.js → chunk-GVYWZNRK.js} +5 -5
  78. package/dist/{chunk-GX5WYQO4.js → chunk-GXCCHKN6.js} +6 -6
  79. package/dist/{chunk-K6T2ZAMZ.js → chunk-HGSLRG33.js} +6 -6
  80. package/dist/{chunk-26QSH3EJ.js → chunk-HHVJNVUJ.js} +5 -5
  81. package/dist/{chunk-F4EKGO4N.js → chunk-HL26ADLH.js} +4 -2
  82. package/dist/{chunk-JT2RFCC5.js → chunk-HR3HPQ76.js} +5 -5
  83. package/dist/{chunk-ID64D7PE.js → chunk-HXHFDHB6.js} +24 -24
  84. package/dist/{chunk-ELWDPP3Y.js → chunk-I7VLWRK2.js} +8 -8
  85. package/dist/{chunk-6OSVSQL5.js → chunk-ICO4TQ2F.js} +7 -5
  86. package/dist/{chunk-SZAA6XDG.js → chunk-IFDRAD6F.js} +2 -2
  87. package/dist/{chunk-72YIHOZQ.js → chunk-ILJ7DWGE.js} +9 -9
  88. package/dist/{chunk-2JH2WHGE.js → chunk-J2FYEQ5L.js} +4 -4
  89. package/dist/{chunk-BMS5RKQY.js → chunk-JABWPKFY.js} +3 -3
  90. package/dist/{chunk-QDC3K2U3.js → chunk-K2BLQC5E.js} +3 -3
  91. package/dist/{chunk-AUPNRN7C.js → chunk-K2HL3GBL.js} +4 -4
  92. package/dist/{chunk-2ASED4PZ.js → chunk-K6RDB7IA.js} +26 -26
  93. package/dist/{chunk-FFUPXJC4.js → chunk-KDWLXVMN.js} +23 -24
  94. package/dist/{chunk-OZNBF4L3.js → chunk-KECNXMDE.js} +2 -2
  95. package/dist/{chunk-6QOTUPRG.js → chunk-KF6GFW6O.js} +66 -18
  96. package/dist/{chunk-BTK3SSSK.js → chunk-KMF54VH3.js} +2 -2
  97. package/dist/chunk-KPRLAMP4.js +32 -0
  98. package/dist/{chunk-ECH6PKUQ.js → chunk-KQ6IDQIB.js} +2 -2
  99. package/dist/{chunk-3UNOLWNZ.js → chunk-KTFSFCVN.js} +20 -9
  100. package/dist/{chunk-57XXR6DR.js → chunk-KYKNBD4Y.js} +24 -23
  101. package/dist/{chunk-IRXAATOX.js → chunk-L4N3DWNW.js} +3 -3
  102. package/dist/{chunk-LCGCVYZ4.js → chunk-LQTKDAUO.js} +2 -2
  103. package/dist/{chunk-U6TMQNSI.js → chunk-LTR5QCLS.js} +4 -4
  104. package/dist/chunk-LUL5Z66S.js +221 -0
  105. package/dist/{chunk-ELZVTCGV.js → chunk-LVFANMOH.js} +151 -327
  106. package/dist/{chunk-H7IXIC72.js → chunk-LY4N4TTL.js} +17 -3
  107. package/dist/{chunk-VKPAQYEB.js → chunk-LZBMXNLP.js} +5 -5
  108. package/dist/{chunk-UL3WSD3F.js → chunk-M4KFUXS7.js} +2 -2
  109. package/dist/{chunk-NXYCB2VD.js → chunk-M6TAYK66.js} +6 -6
  110. package/dist/{chunk-NEKRRTYW.js → chunk-MC3JSVR6.js} +2 -2
  111. package/dist/{chunk-CFGTUFWB.js → chunk-MCJKEGHO.js} +2 -2
  112. package/dist/{chunk-DXKJURES.js → chunk-MDU3C27C.js} +10 -7
  113. package/dist/{chunk-6XLNIQDB.js → chunk-MYI32N4R.js} +2 -2
  114. package/dist/{chunk-3GY4F45V.js → chunk-NC4KV7EL.js} +6 -6
  115. package/dist/{chunk-LLXSDWXS.js → chunk-NF3BK5WR.js} +5 -5
  116. package/dist/chunk-NTK3WTHM.js +141 -0
  117. package/dist/{chunk-GYV6VZOC.js → chunk-NTTS3O53.js} +2 -2
  118. package/dist/{chunk-RZDWV63N.js → chunk-NY6IWDVU.js} +6 -6
  119. package/dist/{chunk-XXYSBZIQ.js → chunk-O2RQ7LCK.js} +4 -4
  120. package/dist/{chunk-4BJ5BYCE.js → chunk-O3VV6IVK.js} +2 -2
  121. package/dist/{chunk-W6GROXXM.js → chunk-O5F25NIE.js} +2 -2
  122. package/dist/{chunk-NXIMQY5W.js → chunk-ODUTYZIU.js} +5 -5
  123. package/dist/{chunk-LTIKRKFL.js → chunk-ONRTXOZY.js} +4 -4
  124. package/dist/{chunk-KFV5L5SK.js → chunk-OQM5IVJY.js} +118 -232
  125. package/dist/{chunk-XRZT5WY5.js → chunk-P5SC4DLJ.js} +4 -4
  126. package/dist/{chunk-R6Q67RJH.js → chunk-PECBKCDK.js} +34 -6
  127. package/dist/{chunk-33YXPOE3.js → chunk-PPEZJWUG.js} +2 -2
  128. package/dist/{chunk-RXTN6AKH.js → chunk-PWWSIB2A.js} +5 -5
  129. package/dist/{chunk-2STGEDFV.js → chunk-QEQISF75.js} +2 -2
  130. package/dist/{chunk-3DPEIQKN.js → chunk-QH3ZTUOA.js} +2 -2
  131. package/dist/{chunk-YAKGTXEV.js → chunk-QRFXEO6Y.js} +2 -2
  132. package/dist/{chunk-35MSIRKH.js → chunk-QUIJZSDW.js} +2 -2
  133. package/dist/{chunk-HHV2GANA.js → chunk-R2P2U3ZS.js} +2 -2
  134. package/dist/{chunk-2DSOYNFC.js → chunk-R6MAYTMS.js} +7 -7
  135. package/dist/{chunk-JQLNNIKT.js → chunk-R6MICQYU.js} +6 -6
  136. package/dist/{chunk-NAI6ZFCY.js → chunk-RLTT5GVY.js} +122 -11
  137. package/dist/{chunk-DT3LWJOB.js → chunk-RXIOCVBZ.js} +24 -6
  138. package/dist/{chunk-N56KALIC.js → chunk-S32UQ5GL.js} +61 -34
  139. package/dist/{chunk-UEPWCCTY.js → chunk-SBZX6ARC.js} +12 -12
  140. package/dist/chunk-SD7SHNDW.js +15 -0
  141. package/dist/{chunk-NRO2BJRH.js → chunk-SDWNDICN.js} +10235 -4015
  142. package/dist/{chunk-UXCU4E3T.js → chunk-SE4ZKILO.js} +6 -6
  143. package/dist/chunk-SL2RDHKO.js +19 -0
  144. package/dist/{chunk-NUGM5KR6.js → chunk-SRCEQKQS.js} +2 -2
  145. package/dist/{chunk-OMQNJVKW.js → chunk-SRZOW5L2.js} +43 -13
  146. package/dist/{chunk-3M6DQK6S.js → chunk-T334FW2H.js} +7 -7
  147. package/dist/{chunk-V76WTFTW.js → chunk-T52TPHYC.js} +6 -6
  148. package/dist/{chunk-I7XBWTYH.js → chunk-T53XI6I7.js} +5 -5
  149. package/dist/{chunk-2ZSONWVL.js → chunk-T7FDXNRU.js} +13 -13
  150. package/dist/{chunk-JSD46VO2.js → chunk-TEG3RYVZ.js} +43 -36
  151. package/dist/{chunk-3I7MS7N2.js → chunk-TK2BMCBZ.js} +2 -2
  152. package/dist/{chunk-5OIVVPHF.js → chunk-TN5FW4XY.js} +13 -6
  153. package/dist/{chunk-75W7L2E2.js → chunk-TZ7MFRG4.js} +1041 -162
  154. package/dist/{chunk-36CT5VVL.js → chunk-U2JOLB7O.js} +126 -77
  155. package/dist/{chunk-LL4KHSZI.js → chunk-U2MSJ57E.js} +2 -2
  156. package/dist/{chunk-FTMGRKEF.js → chunk-U44YYLVV.js} +4 -4
  157. package/dist/{chunk-YECAMM3D.js → chunk-U7H5WRG5.js} +5 -5
  158. package/dist/{chunk-RQCKCSRL.js → chunk-U7VQ4NI4.js} +22 -22
  159. package/dist/{chunk-5TUB6SLS.js → chunk-UFYNOQK4.js} +7 -7
  160. package/dist/{chunk-R346GLFC.js → chunk-UHW5V7HC.js} +2 -2
  161. package/dist/{chunk-USR47QNF.js → chunk-UL36QJX6.js} +14 -14
  162. package/dist/{chunk-VIA6RFQZ.js → chunk-UQIVHSE4.js} +4 -4
  163. package/dist/{chunk-RAY4OVGZ.js → chunk-URBGEOH3.js} +5 -5
  164. package/dist/chunk-USZA7GWL.js +341 -0
  165. package/dist/{chunk-YD5GIKET.js → chunk-UTUNT5QL.js} +48 -579
  166. package/dist/{chunk-ZA4VCIGV.js → chunk-UUQ5G6EZ.js} +60 -41
  167. package/dist/{chunk-23L32XTI.js → chunk-UXAC2FX4.js} +68 -39
  168. package/dist/{chunk-VG7TBQIY.js → chunk-V552WL53.js} +68 -4
  169. package/dist/{chunk-BWH6N36D.js → chunk-VHKTOLEW.js} +272 -42
  170. package/dist/{chunk-EXLD33WO.js → chunk-VLPTKCKT.js} +24 -9
  171. package/dist/{chunk-336NSSKI.js → chunk-VNBICSS2.js} +2 -2
  172. package/dist/{chunk-VAVR4IJY.js → chunk-VQD7NH7Z.js} +3 -3
  173. package/dist/{chunk-LBMZMYH2.js → chunk-W2RJEH6Q.js} +3 -3
  174. package/dist/{chunk-7CR24IG7.js → chunk-WDV62RKY.js} +2 -2
  175. package/dist/{chunk-S6PYF2XF.js → chunk-WFKDPU7U.js} +4 -4
  176. package/dist/{chunk-BJVFZO5U.js → chunk-WJIMKGD6.js} +7 -7
  177. package/dist/{chunk-5636DCO5.js → chunk-WLMSOT73.js} +59 -12
  178. package/dist/{chunk-MXHC5QYU.js → chunk-WTQGRBHV.js} +5 -144
  179. package/dist/chunk-WXA2CFJO.js +23 -0
  180. package/dist/{chunk-QSNYB6ZV.js → chunk-XCUDZQLX.js} +3 -3
  181. package/dist/{chunk-4M6Z5QVF.js → chunk-XXBQKW32.js} +11 -11
  182. package/dist/{chunk-OEJSLEPW.js → chunk-YEJIG7LN.js} +332 -332
  183. package/dist/chunk-YGDMBK7O.js +567 -0
  184. package/dist/{chunk-4WR7VSYB.js → chunk-YYB3MC65.js} +23 -9
  185. package/dist/{chunk-2IR2NMPA.js → chunk-ZIDFQJRB.js} +2 -2
  186. package/dist/{chunk-Q4WO54TA.js → chunk-ZMZQEBKI.js} +14 -14
  187. package/dist/{chunk-6EJMN2Y3.js → chunk-ZTNFYMIO.js} +2 -2
  188. package/dist/{chunk-WEPFGWHJ.js → chunk-ZZFWWYLX.js} +2 -2
  189. package/dist/cli/index.js +44 -44
  190. package/dist/clio-5I2IBFYL.js +26 -0
  191. package/dist/{code-nav-IJR2DBPR.js → code-nav-YXIAEZVS.js} +48 -32
  192. package/dist/codewiki/build-worker.js +76 -29
  193. package/dist/{compile-cache-OQAVB7Y5.js → compile-cache-WJEPUZEJ.js} +4 -4
  194. package/dist/{components-2TGAI2RC.js → components-TNIKXCBH.js} +13 -13
  195. package/dist/{config-IUA6OYNS.js → config-Y2JDJ3JJ.js} +210 -124
  196. package/dist/configure-SML6UBZ5.js +55 -0
  197. package/dist/{context-4MPR7WKB.js → context-P5YSEO55.js} +125 -121
  198. package/dist/{context-2DKHWH2T.js → context-QQ53HSGJ.js} +91 -62
  199. package/dist/{context-BOYF5EJM.js → context-S2EEC3EJ.js} +62 -31
  200. package/dist/context-clear-TMEQCOTH.js +183 -0
  201. package/dist/{context-index-HNG3MOME.js → context-index-HP2JFUWA.js} +13 -11
  202. package/dist/{context-map-COB37XXN.js → context-map-INTXD54D.js} +71 -21
  203. package/dist/{context-working-set-3I3FYX6Y.js → context-working-set-CNLSQN5K.js} +31 -31
  204. package/dist/detail-5572VLBO.js +114 -0
  205. package/dist/{dispatch-runner-RJ5I2F2O.js → dispatch-runner-FRHD6P2O.js} +191 -116
  206. package/dist/{docs-SPOV3BAN.js → docs-BL7ROUFN.js} +4 -4
  207. package/dist/{doctor-DKICC2SN.js → doctor-N3LCM2LG.js} +58 -58
  208. package/dist/{eval-OQOQUDHK.js → eval-BUU3ZOUZ.js} +473 -168
  209. package/dist/{eval-inventory-Y6QRFOH5.js → eval-inventory-QOYZERXJ.js} +7 -7
  210. package/dist/evidence-DTGEN456.js +224 -0
  211. package/dist/{evidence-4DQ25GUQ.js → evidence-SQ5DNFEN.js} +110 -94
  212. package/dist/{evolve-GSS52E5J.js → evolve-IJNUFZDS.js} +107 -91
  213. package/dist/{extensions-G7MFLYHT.js → extensions-YVRHX6ZI.js} +16 -16
  214. package/dist/{fleet-Q37YHHAQ.js → fleet-ZNNVSYFO.js} +147 -147
  215. package/dist/{fleet-commands-G7E4N7SM.js → fleet-commands-5YE7NPJL.js} +27 -26
  216. package/dist/{fleet-decisions-O7M6QBA2.js → fleet-decisions-3SBSCTCW.js} +16 -15
  217. package/dist/{fleet-graph-TOUBW6OW.js → fleet-graph-6HHPWF5T.js} +39 -38
  218. package/dist/{fleet-inspect-SW33JJNI.js → fleet-inspect-JEYMGCGC.js} +107 -91
  219. package/dist/{fleet-new-RDVJLHHH.js → fleet-new-RCKGSRDV.js} +3 -3
  220. package/dist/{fleet-preflight-CV2655TW.js → fleet-preflight-QRSHIIDE.js} +10 -9
  221. package/dist/{fleet-validate-KESZX2YH.js → fleet-validate-4PM4GDDL.js} +46 -42
  222. package/dist/{fleet-verify-UQPMTVE3.js → fleet-verify-NJDTHTO3.js} +105 -89
  223. package/dist/fleet-view-UP2BWQQY.js +117 -0
  224. package/dist/{init-PXEXQSBF.js → init-S3GZQ5ZF.js} +123 -123
  225. package/dist/{install-NKNG7KD4.js → install-3RZDDP23.js} +5 -5
  226. package/dist/{interop-ZG5T62U3.js → interop-YOTGXFJM.js} +25 -25
  227. package/dist/inventory-K2OZ36YJ.js +117 -0
  228. package/dist/{library-B2W4N74O.js → library-GJI6W6OK.js} +45 -44
  229. package/dist/{memory-YCANYS5A.js → memory-OQOXQGYD.js} +107 -93
  230. package/dist/{models-GERTU3YI.js → models-3IK2Y5WE.js} +63 -62
  231. package/dist/{monitor-CPNIUULB.js → monitor-FTJLTKCQ.js} +136 -107
  232. package/dist/{orchestrator-J4BSH4WQ.js → orchestrator-N47O7SFD.js} +1159 -697
  233. package/dist/{panes-BOHAEGYC.js → panes-7BTYH3XQ.js} +8 -8
  234. package/dist/{panes-NXSLDQZ2.js → panes-WPOMEMRH.js} +22 -22
  235. package/dist/{paths-VSUWNC22.js → paths-J3KN7UAO.js} +12 -12
  236. package/dist/preload-65U34NG6.js +115 -0
  237. package/dist/{registry-LG64LTF4.js → registry-DESGAXWK.js} +3 -3
  238. package/dist/{reset-TNWTB5LU.js → reset-MUVQDLNL.js} +20 -20
  239. package/dist/{resources-4PXNMD5G.js → resources-LM76COAO.js} +41 -40
  240. package/dist/{run-D6XJ34CN.js → run-3VFZQ33N.js} +162 -156
  241. package/dist/{share-2NWMJJEE.js → share-4JUB4BL7.js} +43 -42
  242. package/dist/{skills-KR7WON5G.js → skills-S36AAJHU.js} +47 -46
  243. package/dist/{skills-eval-O2ZNOLDS.js → skills-eval-G6T57L7M.js} +127 -107
  244. package/dist/{skills-inventory-ZZOUBK7O.js → skills-inventory-WLM46PJH.js} +41 -40
  245. package/dist/slash-commands-GBARG3Y7.js +91 -0
  246. package/dist/{steer-XA25PSCS.js → steer-S7D7IKPU.js} +7 -7
  247. package/dist/{support-7EMVWYG2.js → support-5VAEC32W.js} +14 -14
  248. package/dist/{targets-OMH2XCSN.js → targets-N5REUETU.js} +145 -94
  249. package/dist/tasks-6BVY5YZF.js +36 -0
  250. package/dist/{terminal-lease-C2J3JYRE.js → terminal-lease-57UEOJKS.js} +12 -11
  251. package/dist/tools-6OCQ6SVB.js +28 -0
  252. package/dist/{trace-FXMXUZUF.js → trace-JHLXWUMN.js} +18 -17
  253. package/dist/{tree-sitter-HGKH6LG4.js → tree-sitter-AQUUOSYM.js} +6 -6
  254. package/dist/{tui-primitives-K5PASC3O.js → tui-primitives-RCY2HN7G.js} +3 -3
  255. package/dist/{uninstall-HALS6BLF.js → uninstall-FXYMFFRT.js} +17 -17
  256. package/dist/{upgrade-MS72RJEP.js → upgrade-4HD5HAIV.js} +40 -39
  257. package/dist/{usage-NHG6MCJM.js → usage-4GWM2WWP.js} +136 -105
  258. package/dist/{verifiers-7AUNVXDY.js → verifiers-GSKWKWVG.js} +28 -27
  259. package/dist/verify-3AS3ULCY.js +30 -0
  260. package/dist/{version-KGEG7EPO.js → version-RFPKO6T7.js} +4 -4
  261. package/dist/{web-fetch-V4FKSDAV.js → web-fetch-MQN4FH3X.js} +169 -18
  262. package/dist/wiki-generate-7BV7XWIY.js +614 -0
  263. package/dist/{with-panes-BDQEWBRT.js → with-panes-OFTNAR4Y.js} +34 -20
  264. package/dist/worker/entry.js +99 -88
  265. package/dist/{workspace-ZJ6BFM3Q.js → workspace-DZ7JADBG.js} +6 -6
  266. package/docs/README.md +7 -6
  267. package/docs/architecture/acp.md +10 -0
  268. package/docs/architecture/architecture.md +5 -5
  269. package/docs/architecture/context-engine.md +44 -59
  270. package/docs/architecture/context-working-set.md +7 -9
  271. package/docs/architecture/dispatch-architecture-rationale.md +1 -1
  272. package/docs/architecture/evidence-and-memory.md +10 -0
  273. package/docs/architecture/pi-boundary.md +37 -7
  274. package/docs/architecture/prompt-envelope-and-tools.md +7 -7
  275. package/docs/architecture/session-lifecycle.md +4 -4
  276. package/docs/architecture/worker-dispatch-mechanics.md +4 -0
  277. package/docs/guide/commands-and-modes.md +49 -36
  278. package/docs/guide/configuration-and-targets.md +7 -2
  279. package/docs/guide/configuration-reference.md +13 -3
  280. package/docs/guide/environment-variables.md +6 -2
  281. package/docs/guide/fleet-dispatch.md +12 -0
  282. package/docs/guide/installation-and-lifecycle.md +35 -9
  283. package/docs/guide/proactive-memory.md +1 -1
  284. package/docs/guide/skills-marketplace.md +11 -3
  285. package/docs/guide/tool-usage.md +9 -2
  286. package/docs/process/development-pipeline.md +10 -10
  287. package/docs/process/documentation-guide.md +2 -2
  288. package/docs/process/eval-runner.md +15 -0
  289. package/docs/process/evals-internal.md +1 -1
  290. package/docs/process/release-cut-checklist.md +6 -6
  291. package/evals/README.md +4 -0
  292. package/evals/behavioral-corpus-grader.mjs +147 -14
  293. package/evals/behavioral-model-negative-control.yaml +1 -0
  294. package/evals/behavioral-model.yaml +266 -1
  295. package/evals/fixtures/docs-retrieval.json +102 -0
  296. package/evals/fixtures/scalar-decision.json +17 -0
  297. package/evals/fixtures/scout-citation-pipeline.json +55 -0
  298. package/evals/fixtures/source-explanation.json +74 -0
  299. package/evals/fixtures/tool-arguments.json +18 -0
  300. package/evals/fixtures/tool-surface/read.txt +4 -0
  301. package/evals/fixtures/tool-surface/subdir/keep.txt +1 -0
  302. package/evals/no-edit-memory-grader.mjs +149 -0
  303. package/evals/scalar-decision-grader.py +41 -0
  304. package/evals/scalar-decision-machinery.ts +145 -0
  305. package/evals/source-explanation-grader.mjs +118 -0
  306. package/evals/tool-surface-grader.mjs +53 -0
  307. package/evals/tool-surface-model.yaml +274 -0
  308. package/package.json +14 -14
  309. package/skills/README.md +6 -6
  310. package/skills/registry.yaml +1 -1
  311. package/skills/skill-marketplace.json +1 -1
  312. package/src/cli/bootstrap-generate.ts +6 -0
  313. package/src/cli/config-inspect.ts +24 -7
  314. package/src/cli/config-trust.ts +62 -0
  315. package/src/cli/config.ts +8 -0
  316. package/src/cli/context-map.ts +75 -14
  317. package/src/cli/context.ts +46 -17
  318. package/src/cli/eval.ts +17 -3
  319. package/src/cli/modes/print.ts +32 -4
  320. package/src/cli/run.ts +16 -14
  321. package/src/cli/skills-eval.ts +10 -8
  322. package/src/cli/steer-channel.ts +31 -18
  323. package/src/cli/targets.ts +83 -31
  324. package/src/cli/tasks.ts +10 -1
  325. package/src/cli/upgrade.ts +9 -6
  326. package/src/cli/usage.ts +24 -0
  327. package/src/cli/wiki-generate.ts +239 -96
  328. package/src/core/bash-exec.ts +43 -15
  329. package/src/core/bus-events.ts +15 -0
  330. package/src/core/config.ts +33 -14
  331. package/src/core/defaults.ts +5 -4
  332. package/src/core/file-references.ts +11 -1
  333. package/src/core/incomplete-installation.ts +3 -1
  334. package/src/core/settings-layers.ts +21 -5
  335. package/src/core/termination.ts +26 -9
  336. package/src/core/untrusted-content.ts +16 -0
  337. package/src/core/workspace-trust.ts +179 -0
  338. package/src/domains/agents/builtins/coder.md +4 -2
  339. package/src/domains/agents/builtins/context-bootstrap.md +3 -1
  340. package/src/domains/agents/builtins/documenter.md +7 -4
  341. package/src/domains/agents/builtins/researcher.md +1 -0
  342. package/src/domains/agents/builtins/scout.md +7 -1
  343. package/src/domains/agents/result-contract.ts +199 -26
  344. package/src/domains/context/bootstrap-prompt.ts +11 -9
  345. package/src/domains/context/bootstrap.ts +74 -39
  346. package/src/domains/context/clio-md.ts +88 -26
  347. package/src/domains/context/codewiki/build-operation.ts +121 -0
  348. package/src/domains/context/codewiki/build-worker-protocol.ts +37 -3
  349. package/src/domains/context/codewiki/build-worker.ts +3 -35
  350. package/src/domains/context/codewiki/coordinator.ts +36 -6
  351. package/src/domains/context/codewiki/indexer.ts +59 -19
  352. package/src/domains/context/contract.ts +6 -2
  353. package/src/domains/context/extension.ts +72 -10
  354. package/src/domains/context/fingerprint.ts +39 -34
  355. package/src/domains/context/index.ts +7 -1
  356. package/src/domains/context/prompt-context.ts +55 -14
  357. package/src/domains/context/refresh.ts +9 -57
  358. package/src/domains/context/wiki/assemble.ts +14 -10
  359. package/src/domains/context/wiki/evidence.ts +209 -0
  360. package/src/domains/context/wiki/generate.ts +201 -67
  361. package/src/domains/context/wiki/meta.ts +10 -2
  362. package/src/domains/context/wiki/plan-store.ts +70 -14
  363. package/src/domains/context/wiki/plan.ts +71 -14
  364. package/src/domains/context/wiki/prompts.ts +42 -1
  365. package/src/domains/context/wiki/source-content.ts +106 -0
  366. package/src/domains/context/wiki/staleness.ts +33 -22
  367. package/src/domains/context/working-set/contract.ts +8 -2
  368. package/src/domains/context/working-set/engine.ts +17 -4
  369. package/src/domains/context/working-set/fold.ts +15 -2
  370. package/src/domains/context/working-set/marker.ts +2 -1
  371. package/src/domains/context/working-set/project.ts +18 -7
  372. package/src/domains/context/working-set/recall.ts +82 -56
  373. package/src/domains/dispatch/active-route-planner.ts +6 -3
  374. package/src/domains/dispatch/admission-queue.ts +19 -7
  375. package/src/domains/dispatch/admission.ts +3 -3
  376. package/src/domains/dispatch/budget-envelope.ts +47 -82
  377. package/src/domains/dispatch/capacity-lease.ts +57 -19
  378. package/src/domains/dispatch/contract.ts +10 -2
  379. package/src/domains/dispatch/event-pump.ts +28 -7
  380. package/src/domains/dispatch/execution-role.ts +49 -2
  381. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  382. package/src/domains/dispatch/extension.ts +190 -105
  383. package/src/domains/dispatch/fleet-run.ts +20 -0
  384. package/src/domains/dispatch/gate-role-prompts.ts +4 -2
  385. package/src/domains/dispatch/parallel-writers.ts +34 -0
  386. package/src/domains/dispatch/reservation-store.ts +8 -34
  387. package/src/domains/dispatch/route-approval.ts +3 -3
  388. package/src/domains/dispatch/route-observer.ts +4 -2
  389. package/src/domains/dispatch/route-quality.ts +6 -1
  390. package/src/domains/dispatch/types.ts +4 -3
  391. package/src/domains/dispatch/worker-spawn.ts +58 -21
  392. package/src/domains/dispatch/workspace-checkpoint.ts +57 -0
  393. package/src/domains/eval/compare/behavioral.ts +81 -14
  394. package/src/domains/eval/compare/compare.ts +15 -5
  395. package/src/domains/eval/compare/envelope.ts +12 -5
  396. package/src/domains/eval/metrics/context.ts +24 -1
  397. package/src/domains/eval/metrics/tracked.ts +4 -28
  398. package/src/domains/eval/reports/comparison.ts +9 -9
  399. package/src/domains/eval/runners/clio-run.ts +5 -1
  400. package/src/domains/eval/schema/suite.ts +2 -0
  401. package/src/domains/eval/schema/validate.ts +1 -0
  402. package/src/domains/eval/suites/run.ts +189 -13
  403. package/src/domains/eval/verifiers/integrity.ts +39 -0
  404. package/src/domains/eval/verifiers/patch.ts +58 -2
  405. package/src/domains/eval/workspaces/temp-copy.ts +3 -0
  406. package/src/domains/evidence/build.ts +4 -2
  407. package/src/domains/interop/registry.ts +2 -2
  408. package/src/domains/middleware/hooks-io.ts +45 -26
  409. package/src/domains/middleware/hooks.ts +6 -0
  410. package/src/domains/middleware/injection-screen.ts +23 -0
  411. package/src/domains/middleware/rules.ts +5 -1
  412. package/src/domains/middleware/stalled-turn.ts +18 -3
  413. package/src/domains/middleware/task-board-reminder.ts +4 -2
  414. package/src/domains/middleware/task-nudge.ts +4 -1
  415. package/src/domains/mux/contract.ts +34 -1
  416. package/src/domains/mux/dock-controller.ts +8 -1
  417. package/src/domains/mux/index.ts +1 -0
  418. package/src/domains/mux/socket-client.ts +13 -3
  419. package/src/domains/observability/contract.ts +2 -2
  420. package/src/domains/observability/projection.ts +30 -9
  421. package/src/domains/prompts/compiler.ts +10 -2
  422. package/src/domains/prompts/contract.ts +7 -4
  423. package/src/domains/prompts/extension.ts +28 -64
  424. package/src/domains/prompts/fragments/identity/clio.md +23 -0
  425. package/src/domains/prompts/fragments/operating/contract.md +10 -0
  426. package/src/domains/prompts/fragments/operating/delegation.md +5 -0
  427. package/src/domains/prompts/fragments/wiki/page.md +28 -15
  428. package/src/domains/prompts/fragments/wiki/plan.md +4 -4
  429. package/src/domains/prompts/preload-prefix.ts +31 -0
  430. package/src/domains/prompts/preload.ts +135 -61
  431. package/src/domains/providers/auth/backend-file.ts +15 -11
  432. package/src/domains/providers/auth/storage.ts +3 -5
  433. package/src/domains/providers/catalog.ts +18 -3
  434. package/src/domains/providers/runtime-resolution.ts +11 -9
  435. package/src/domains/providers/runtimes/cloud/openrouter.ts +2 -0
  436. package/src/domains/resources/skills/promotion.ts +8 -1
  437. package/src/domains/safety/action-classifier.ts +9 -0
  438. package/src/domains/safety/autonomy.ts +10 -1
  439. package/src/domains/safety/contract.ts +5 -1
  440. package/src/domains/safety/default-path-policy.ts +11 -0
  441. package/src/domains/safety/extension.ts +11 -2
  442. package/src/domains/safety/policy-engine.ts +189 -32
  443. package/src/domains/safety/project-policy.ts +7 -2
  444. package/src/domains/safety/workspace-trust.ts +43 -0
  445. package/src/domains/session/compaction/compact.ts +384 -21
  446. package/src/domains/session/compaction/cut-point.ts +42 -5
  447. package/src/domains/session/compaction/tokens.ts +9 -1
  448. package/src/domains/session/decision-board.ts +43 -33
  449. package/src/domains/session/entries.ts +169 -4
  450. package/src/domains/session/prompt-manifest.ts +47 -8
  451. package/src/domains/session/task-board.ts +21 -3
  452. package/src/domains/user-tasks/handoff.ts +16 -0
  453. package/src/engine/acp/adapter.ts +44 -12
  454. package/src/engine/acp/server.ts +1 -0
  455. package/src/engine/acp/transport.ts +2 -1
  456. package/src/engine/acp/types.ts +6 -1
  457. package/src/engine/antigravity/subprocess-runtime.ts +20 -4
  458. package/src/engine/api-registry.ts +19 -2
  459. package/src/engine/apis/openai-completions.ts +13 -6
  460. package/src/engine/apis/output-budget.ts +26 -10
  461. package/src/engine/claude/sdk-runtime.ts +4 -3
  462. package/src/engine/env-api-keys.ts +2 -1
  463. package/src/engine/external-subprocess.ts +14 -3
  464. package/src/engine/loop-guard.ts +19 -2
  465. package/src/engine/provider-diagnostics.ts +129 -0
  466. package/src/engine/provider-payload.ts +3 -1
  467. package/src/engine/worker-runtime.ts +13 -2
  468. package/src/engine/worker-tools.ts +5 -1
  469. package/src/entry/extension-reload.ts +1 -0
  470. package/src/entry/orchestrator.ts +53 -15
  471. package/src/interactive/chat-loop-messages.ts +12 -4
  472. package/src/interactive/chat-loop.ts +115 -45
  473. package/src/interactive/chat-renderer.ts +60 -0
  474. package/src/interactive/context-recall-command.ts +2 -1
  475. package/src/interactive/dispatch-board.ts +17 -2
  476. package/src/interactive/editor-submit.ts +13 -0
  477. package/src/interactive/interactive-application.ts +8 -2
  478. package/src/interactive/interactive-event-projection.ts +7 -1
  479. package/src/interactive/interactive-presentation.ts +15 -7
  480. package/src/interactive/interactive-slash-runtime.ts +2 -0
  481. package/src/interactive/layout.ts +2 -1
  482. package/src/interactive/model-session-replay.ts +2 -2
  483. package/src/interactive/overlay-frame.ts +1 -0
  484. package/src/interactive/overlay-general-openers.ts +3 -1
  485. package/src/interactive/overlay-session-lifecycle.ts +6 -0
  486. package/src/interactive/overlays/ask-user.ts +1 -0
  487. package/src/interactive/overlays/message-picker.ts +1 -1
  488. package/src/interactive/renderers/tool-execution.ts +17 -1
  489. package/src/interactive/renderers/worker-entry.ts +25 -10
  490. package/src/interactive/slash-commands.ts +9 -7
  491. package/src/interactive/turn-context.ts +221 -53
  492. package/src/interactive/turn-middleware.ts +3 -1
  493. package/src/interactive/turn-persistence.ts +3 -0
  494. package/src/interactive/turn-runtime.ts +11 -2
  495. package/src/interactive/turn-state.ts +10 -9
  496. package/src/interactive/watch-pane.ts +18 -4
  497. package/src/interactive/worker-stream.ts +7 -7
  498. package/src/tools/agent-tools.ts +6 -3
  499. package/src/tools/bash.ts +1 -1
  500. package/src/tools/builtin-tool-catalog.ts +9 -2
  501. package/src/tools/codewiki/code-nav.ts +4 -10
  502. package/src/tools/compete-judge-task.ts +71 -0
  503. package/src/tools/compete-worktrees.ts +34 -0
  504. package/src/tools/context/index.ts +32 -7
  505. package/src/tools/context/surface.ts +15 -4
  506. package/src/tools/decide.ts +1 -1
  507. package/src/tools/dispatch-admission.ts +12 -1
  508. package/src/tools/dispatch-arguments.ts +38 -1
  509. package/src/tools/dispatch-plan.ts +7 -7
  510. package/src/tools/dispatch-runner.ts +48 -32
  511. package/src/tools/dispatch-schema.ts +30 -23
  512. package/src/tools/dispatch-scout-admission.ts +10 -44
  513. package/src/tools/dispatch-scout.ts +2 -3
  514. package/src/tools/find.ts +13 -4
  515. package/src/tools/grep.ts +13 -1
  516. package/src/tools/ls.ts +7 -1
  517. package/src/tools/monitor.ts +26 -11
  518. package/src/tools/network-policy.ts +11 -24
  519. package/src/tools/observation.ts +39 -2
  520. package/src/tools/read.ts +112 -15
  521. package/src/tools/registry.ts +54 -17
  522. package/src/tools/result-shaping.ts +110 -2
  523. package/src/tools/tasks.ts +33 -7
  524. package/src/tools/verify/frontend.ts +67 -30
  525. package/src/tools/verify/numeric.ts +6 -1
  526. package/src/tools/web-fetch-network.ts +179 -0
  527. package/src/tools/web-fetch-surface.ts +2 -1
  528. package/src/tools/web-fetch.ts +15 -13
  529. package/src/tools/worker-evidence.ts +27 -0
  530. package/src/worker/spec-contract.ts +12 -8
  531. package/dist/builtins-WGALA46I.js +0 -22
  532. package/dist/chunk-2WZ546HR.js +0 -792
  533. package/dist/chunk-5PSMVOLM.js +0 -56
  534. package/dist/chunk-EL24TAU4.js +0 -1560
  535. package/dist/chunk-IZXGRF7P.js +0 -4048
  536. package/dist/chunk-JVCV3ICN.js +0 -47
  537. package/dist/chunk-MNJGS2IN.js +0 -33
  538. package/dist/chunk-O5CVSAG5.js +0 -122
  539. package/dist/chunk-ODGTEFFI.js +0 -50
  540. package/dist/chunk-OQE5J4C6.js +0 -73
  541. package/dist/chunk-UUVG37B4.js +0 -244
  542. package/dist/chunk-V6HJFQZE.js +0 -228
  543. package/dist/clio-QLICPCF5.js +0 -25
  544. package/dist/configure-VEPX4NMX.js +0 -55
  545. package/dist/context-clear-S4ZJCQUX.js +0 -182
  546. package/dist/detail-A7JAVSIG.js +0 -98
  547. package/dist/evidence-4F5USFKH.js +0 -208
  548. package/dist/fleet-view-MN2VG4MR.js +0 -101
  549. package/dist/inventory-C26CFDRR.js +0 -101
  550. package/dist/preload-P6DGH2PZ.js +0 -13
  551. package/dist/slash-commands-ZXPJD64J.js +0 -92
  552. package/dist/tasks-IPAGMEIX.js +0 -36
  553. package/dist/tools-EFFEAIDP.js +0 -28
  554. package/dist/verify-FWYGPKMR.js +0 -29
  555. package/dist/wiki-generate-743CIGJW.js +0 -476
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { mkdirSync, readdirSync, rmdirSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { isAbsolute, join, relative, resolve } from "node:path";
4
+ import { INSTRUCTION_SHAPED_WARNING } from "../core/untrusted-content.js";
4
5
  import { clioStateDir } from "../core/xdg.js";
5
6
  import type { ToolInvokeOptions, ToolResult, ToolResultDetails, ToolSpec } from "./registry.js";
6
7
  import {
@@ -24,7 +25,10 @@ const RESULT_OFFLOAD_MAX_BYTES = 10 * 1024 * 1024;
24
25
  const TAIL_NOTICE_RESERVE_BYTES = 512;
25
26
  export const TOOL_OFFLOAD_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1000;
26
27
 
27
- type ToolResultShapeContext = Pick<ToolInvokeOptions, "sessionId" | "toolCallId" | "toolResultMaxBytes">;
28
+ type ToolResultShapeContext = Pick<
29
+ ToolInvokeOptions,
30
+ "sessionId" | "toolCallId" | "toolResultMaxBytes" | "supportsImages"
31
+ >;
28
32
 
29
33
  function mergeDetails(details: ToolResultDetails | undefined, resultSize: Record<string, unknown>): ToolResultDetails {
30
34
  return { ...(details ?? {}), resultSize };
@@ -419,7 +423,7 @@ function withOffloadMetadata(
419
423
  * Apply legacy shaping or one declared canonical disposition. Registry calls
420
424
  * this once, after middleware annotations have produced the terminal result.
421
425
  */
422
- export function shapeToolResult(
426
+ function shapeTextToolResultBody(
423
427
  spec: ToolSpec,
424
428
  result: ToolResult,
425
429
  context?: ToolResultShapeContext,
@@ -519,3 +523,107 @@ export function shapeToolResult(
519
523
  modelContext: projection.text,
520
524
  };
521
525
  }
526
+
527
+ /** Keep the harness warning visible even when the chosen projection omits the source body. */
528
+ function shapeTextToolResult(
529
+ spec: ToolSpec,
530
+ result: ToolResult,
531
+ context?: ToolResultShapeContext,
532
+ requestedDisposition?: ToolResultDisposition,
533
+ ): ToolResult {
534
+ const shaped = shapeTextToolResultBody(spec, result, context, requestedDisposition);
535
+ const warning = `[middleware:warn] ${INSTRUCTION_SHAPED_WARNING}`;
536
+ if (!resultText(result).includes(warning)) return shaped;
537
+ const hardMaxBytes = maxBytesFor(spec, context);
538
+ const disposition = normalizeToolResultDisposition(spec, hardMaxBytes, requestedDisposition);
539
+ const preserve = (text: string, cap: number, tail: boolean): string => {
540
+ if (text.startsWith(warning) && byteLength(text) <= cap) return text;
541
+ const prefix = `${warning}\n\n`;
542
+ const remainder = text.split(warning).join("").trimStart();
543
+ const allowance = Math.max(0, cap - byteLength(prefix));
544
+ if (allowance === 0) return truncateUtf8(warning, cap, "");
545
+ const body = tail
546
+ ? truncateTail(remainder, { maxBytes: allowance, maxLines: Number.MAX_SAFE_INTEGER }).content
547
+ : truncateUtf8(remainder, allowance, "");
548
+ return prefix + body;
549
+ };
550
+ const output = preserve(
551
+ resultText(shaped),
552
+ Math.min(hardMaxBytes, disposition?.presentation.maxBytes ?? hardMaxBytes),
553
+ disposition?.presentation.overflow === "tail",
554
+ );
555
+ const modelContext =
556
+ shaped.modelContext === undefined
557
+ ? undefined
558
+ : preserve(
559
+ shaped.modelContext,
560
+ Math.min(hardMaxBytes, disposition?.context.maxBytes ?? hardMaxBytes),
561
+ disposition?.context.mode === "bounded" && disposition.context.excerpt === "tail",
562
+ );
563
+ const previous = existingDisposition(shaped.details);
564
+ const details = { ...shaped.details };
565
+ if (previous !== null)
566
+ details.resultDisposition = {
567
+ ...previous,
568
+ presentation: { ...previous.presentation, content: output },
569
+ displayedBytes: byteLength(output),
570
+ contextBytes: byteLength(modelContext ?? output),
571
+ presentationTruncated: previous.presentationTruncated || byteLength(resultText(shaped)) > byteLength(output),
572
+ contextTruncated:
573
+ previous.contextTruncated || byteLength(shaped.modelContext ?? "") > byteLength(modelContext ?? ""),
574
+ };
575
+ const size = detailsRecord(shaped.details, "resultSize");
576
+ if (size !== null) details.resultSize = { ...size, shownBytes: byteLength(output) };
577
+ return {
578
+ ...shaped,
579
+ ...(shaped.kind === "ok" ? { output } : { message: output }),
580
+ ...(modelContext === undefined ? {} : { modelContext }),
581
+ details,
582
+ };
583
+ }
584
+
585
+ /** Image bytes share the same payload cap as text, including after annotation. */
586
+ export function shapeToolResult(
587
+ spec: ToolSpec,
588
+ result: ToolResult,
589
+ context?: ToolResultShapeContext,
590
+ requestedDisposition?: ToolResultDisposition,
591
+ ): ToolResult {
592
+ if (result.kind !== "ok" || !result.images?.length)
593
+ return shapeTextToolResult(spec, result, context, requestedDisposition);
594
+ const hardMaxBytes = maxBytesFor(spec, context);
595
+ const disposition = normalizeToolResultDisposition(spec, hardMaxBytes, requestedDisposition);
596
+ const maxBytes = Math.min(hardMaxBytes, disposition?.context.maxBytes ?? hardMaxBytes);
597
+ const images: NonNullable<typeof result.images> = [];
598
+ let imageBytes = 0;
599
+ for (const image of result.images) {
600
+ const bytes = byteLength(image.data);
601
+ if (
602
+ context?.supportsImages === true &&
603
+ disposition?.context.mode !== "metadata-only" &&
604
+ bytes + imageBytes < maxBytes - 512
605
+ ) {
606
+ images.push(image);
607
+ imageBytes += bytes;
608
+ }
609
+ }
610
+ const omitted = result.images.length - images.length;
611
+ const notice =
612
+ omitted > 0 ? `[${omitted} image(s) omitted: model capability, context policy, or tool result byte cap.]\n` : "";
613
+ const remaining = Math.max(1, maxBytes - imageBytes);
614
+ const shaped = shapeTextToolResult(
615
+ spec,
616
+ { ...result, output: notice + result.output, images },
617
+ { ...context, toolResultMaxBytes: remaining },
618
+ requestedDisposition,
619
+ );
620
+ if (shaped.kind !== "ok") return shaped;
621
+ // Legacy truncation notices may extend beyond their text allowance; the image
622
+ // envelope has a strict combined cap, so bound the final projections too.
623
+ return {
624
+ ...shaped,
625
+ images,
626
+ output: truncateUtf8(shaped.output, remaining, ""),
627
+ ...(shaped.modelContext === undefined ? {} : { modelContext: truncateUtf8(shaped.modelContext, remaining, "") }),
628
+ };
629
+ }
@@ -47,7 +47,8 @@ function waitingTrailer(tasks: ReadonlyArray<UserTask>): string[] {
47
47
  .filter((task) => task.status === "open" || task.status === "handed")
48
48
  .map(
49
49
  (task) =>
50
- `operator tasks waiting: ${task.id} ${JSON.stringify(task.title)} — pick up with action="pick" id="${task.id}"`,
50
+ `operator tasks waiting: ${task.id} ${JSON.stringify(task.title)} — durable status=${task.status}; ` +
51
+ `not completed; pick only if this is the intended operator task, with action="pick" id="${task.id}" before work`,
51
52
  );
52
53
  }
53
54
 
@@ -60,13 +61,30 @@ function renderTaskBoardText(board: TaskBoardSnapshot, userTasks: ReadonlyArray<
60
61
  if (task.status === "blocked" && task.reason) line += ` — blocked: ${task.reason}`;
61
62
  if (task.status === "cancelled" && task.reason) line += ` — dropped: ${task.reason}`;
62
63
  lines.push(line);
63
- const outputs = userTasks.find((item) => item.id === task.userTaskId)?.acceptance?.expectedOutputs;
64
+ const operatorTask = userTasks.find((item) => item.id === task.userTaskId);
65
+ if (task.userTaskId) {
66
+ lines.push(
67
+ ` operator task ${task.userTaskId}: durable status=${operatorTask?.status ?? "unavailable"}; ` +
68
+ `session=${operatorTask?.handedSessionId ?? "unlinked"}; board task=${operatorTask?.boardTaskId ?? "unlinked"}`,
69
+ );
70
+ }
71
+ const outputs = operatorTask?.acceptance?.expectedOutputs;
64
72
  if (outputs?.length) lines.push(` expected outputs: ${outputs.join(", ")}`);
65
- for (const item of task.requiredValidationEvidence ?? [])
66
- lines.push(` acceptance: ${item.command} (${item.status}; ${item.notes})`);
73
+ for (const item of task.requiredValidationEvidence ?? []) {
74
+ if (item.status === "required") {
75
+ lines.push(
76
+ ` acceptance requirement: ${item.command} (${item.notes}; declaration only; execution status is in verification receipts)`,
77
+ );
78
+ } else {
79
+ lines.push(` acceptance: ${item.command} (${item.status}; ${item.notes})`);
80
+ }
81
+ }
67
82
  }
68
83
  if (counts.open > 0 && counts.active === 0) {
69
- lines.push(`next: start a task with action="start" before working it`);
84
+ lines.push(
85
+ `next: start only an authorized task with action="start" before working it; ` +
86
+ `block proposed implementation with a note naming the pending operator decision, or drop it, and wait`,
87
+ );
70
88
  }
71
89
  lines.push(...waitingTrailer(userTasks));
72
90
  return lines.join("\n");
@@ -145,7 +163,12 @@ export function createTasksTool(deps: TasksToolDeps): ToolSpec {
145
163
  "pick moves one operator task uN onto the board; start marks one task active (the current focus); " +
146
164
  "done completes a task (started or still pending) and requires note as the " +
147
165
  "evidence the work actually finished; block parks it with a required reason; drop cancels it; list shows the board. " +
148
- "Work that did not happen is blocked or dropped, never done.",
166
+ "For an operator handoff, pick the intended uN before work and use its returned linked tN; CLI hand alone does not pick it. " +
167
+ "Never pick unrelated inbox tasks. Before claiming operator completion, list and confirm the linked board row is completed " +
168
+ "and the durable operator status is done with the same session/board link; report those IDs and actual states. " +
169
+ "Work that did not happen is blocked or dropped, never done. A self-created plan is not operator authorization. " +
170
+ "For proposal-only work, block deferred implementation with note naming the pending operator decision, or drop it; " +
171
+ "wait for an explicit operator go-ahead before start or implementation. A skill-install choice does not grant that go-ahead.",
149
172
  parameters: Type.Object({
150
173
  action: StringEnum(TASKS_ACTIONS, { description: "Board action." }),
151
174
  title: Type.Optional(Type.String({ description: "Board title (plan)." })),
@@ -192,7 +215,10 @@ export function createTasksTool(deps: TasksToolDeps): ToolSpec {
192
215
  if (typedAction === "list") {
193
216
  const board = deps.board.snapshot();
194
217
  if (!board) {
195
- const output = ['no task board yet; declare one with action="plan"', ...waitingTrailer(userTasks)].join("\n");
218
+ const output = [
219
+ 'no task board yet; for an operator handoff, pick the intended uN before work; otherwise declare one with action="plan"',
220
+ ...waitingTrailer(userTasks),
221
+ ].join("\n");
196
222
  return {
197
223
  kind: "ok",
198
224
  output,
@@ -3,10 +3,12 @@ import { tmpdir } from "node:os";
3
3
  import path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { Script } from "node:vm";
6
+ import { prepareBoundedImage } from "../../core/file-references.js";
6
7
  import { combineSafeOutput, runCommandVector } from "../../core/safe-exec.js";
8
+ import type { ImageContent } from "../../engine/types.js";
7
9
  import { escapeRegExp } from "../ignore-policy.js";
8
10
  import { resolveReadPath } from "../path-utils.js";
9
- import type { ToolResult, ToolResultDetails } from "../registry.js";
11
+ import type { ToolInvokeOptions, ToolResult, ToolResultDetails } from "../registry.js";
10
12
  import { maxOutputArg, timeoutArg } from "../safe-exec.js";
11
13
  import { truncateUtf8 } from "../truncate-utf8.js";
12
14
  import { BROWSER_MODES } from "./surface.js";
@@ -25,6 +27,9 @@ interface FrontendCheck {
25
27
  }
26
28
 
27
29
  interface ValidationOptions {
30
+ supportsImages?: boolean;
31
+ imageMaxBytes?: number;
32
+ images?: ImageContent[];
28
33
  timeoutMs: number;
29
34
  maxOutputBytes: number;
30
35
  signal?: AbortSignal;
@@ -84,7 +89,7 @@ const JAVASCRIPT_TYPES = new Set([
84
89
  */
85
90
  export async function runFrontendCheck(
86
91
  args: Record<string, unknown>,
87
- options?: { signal?: AbortSignal },
92
+ options?: ToolInvokeOptions,
88
93
  ): Promise<ToolResult> {
89
94
  const pathArg = typeof args.path === "string" ? args.path.trim() : "";
90
95
  if (pathArg.length === 0) return { kind: "error", message: "verify: check=frontend requires a path argument" };
@@ -106,6 +111,9 @@ export async function runFrontendCheck(
106
111
  const validateOptions: ValidationOptions = {
107
112
  timeoutMs: timeoutArg(args),
108
113
  maxOutputBytes: maxOutputArg(args),
114
+ supportsImages: options?.supportsImages === true,
115
+ imageMaxBytes: Math.min(maxOutputArg(args), options?.toolResultMaxBytes ?? 50_000) - 4096,
116
+ images: [],
109
117
  };
110
118
  if (options?.signal !== undefined) validateOptions.signal = options.signal;
111
119
 
@@ -131,7 +139,7 @@ export async function runFrontendCheck(
131
139
  details,
132
140
  };
133
141
  }
134
- return { kind: "ok", output, details };
142
+ return { kind: "ok", output, details, ...(validateOptions.images?.length ? { images: validateOptions.images } : {}) };
135
143
  }
136
144
 
137
145
  async function validateArtifact(
@@ -550,38 +558,67 @@ async function validateBrowserLoad(
550
558
  });
551
559
  return;
552
560
  }
553
- const result = await runCommandVector(
554
- browser,
555
- [
556
- "--headless=new",
557
- "--disable-gpu",
558
- "--no-sandbox",
559
- "--disable-dev-shm-usage",
560
- "--dump-dom",
561
- pathToFileURL(artifactPath).href,
562
- ],
563
- {
564
- timeoutMs: options.timeoutMs,
565
- maxOutputBytes: options.maxOutputBytes,
566
- ...(options.signal !== undefined ? { signal: options.signal } : {}),
567
- },
568
- );
569
- if (result.exitCode === 0 && !result.timedOut && !result.aborted && !result.outputCapped) {
561
+ // A DOM dump duplicates the complete artifact on stdout. Large, otherwise
562
+ // valid pages then trip the diagnostic output cap. A private screenshot
563
+ // proves that the browser rendered without charging page bytes to that cap.
564
+ // Vision-capable routes retain bounded pixels before the scratch cleanup.
565
+ const dir = mkdtempSync(path.join(tmpdir(), "clio-coder-browser-check-"));
566
+ const screenshot = path.join(dir, "loaded.png");
567
+ try {
568
+ const result = await runCommandVector(
569
+ browser,
570
+ [
571
+ "--headless=new",
572
+ "--disable-gpu",
573
+ "--no-sandbox",
574
+ "--disable-dev-shm-usage",
575
+ `--user-data-dir=${path.join(dir, "profile")}`,
576
+ `--screenshot=${screenshot}`,
577
+ pathToFileURL(artifactPath).href,
578
+ ],
579
+ {
580
+ timeoutMs: options.timeoutMs,
581
+ maxOutputBytes: options.maxOutputBytes,
582
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
583
+ },
584
+ );
585
+ let failure: string;
586
+ if (result.aborted) failure = "headless browser aborted";
587
+ else if (result.timedOut) failure = `headless browser timed out after ${options.timeoutMs}ms`;
588
+ else if (result.outputCapped) failure = `headless browser output exceeded ${options.maxOutputBytes} bytes`;
589
+ else if (result.exitCode !== 0) {
590
+ failure = `headless browser exited with ${result.exitCode ?? result.signal ?? "unknown"}`;
591
+ } else if (!existsSync(screenshot) || !statSync(screenshot).isFile() || statSync(screenshot).size === 0) {
592
+ failure = "headless browser exited without a rendered screenshot";
593
+ } else {
594
+ if (options.supportsImages) {
595
+ const image = await prepareBoundedImage(readFileSync(screenshot), options.imageMaxBytes ?? 0);
596
+ if (image) options.images?.push(image);
597
+ else
598
+ checks.push({
599
+ name: "browser image",
600
+ status: "warn",
601
+ message: "Screenshot omitted: could not encode within the tool result byte cap.",
602
+ });
603
+ }
604
+ checks.push({
605
+ name: "browser load",
606
+ status: "pass",
607
+ message: `loaded and rendered with ${path.basename(browser)}`,
608
+ path: artifactPath,
609
+ });
610
+ return;
611
+ }
612
+ const output = truncateUtf8(combineSafeOutput(result).trim(), 800, " [truncated]");
570
613
  checks.push({
571
614
  name: "browser load",
572
- status: "pass",
573
- message: `loaded with ${path.basename(browser)}`,
615
+ status: "fail",
616
+ message: `${failure}${output ? `: ${output}` : ""}`,
574
617
  path: artifactPath,
575
618
  });
576
- return;
619
+ } finally {
620
+ rmSync(dir, { recursive: true, force: true });
577
621
  }
578
- const output = truncateUtf8(combineSafeOutput(result).trim(), 800, " [truncated]");
579
- checks.push({
580
- name: "browser load",
581
- status: "fail",
582
- message: `headless browser exited with ${result.exitCode ?? result.signal ?? "unknown"}${output ? `: ${output}` : ""}`,
583
- path: artifactPath,
584
- });
585
622
  }
586
623
 
587
624
  function parseAttributes(source: string): Record<string, string> {
@@ -156,7 +156,12 @@ export function parseNumericPayload(text: string, label: string): NumericPayload
156
156
  function deviation(actual: number, expected: number, index?: number): NumericDeviation {
157
157
  const finite = Number.isFinite(actual) && Number.isFinite(expected);
158
158
  const absolute = finite ? Math.abs(actual - expected) : Number.POSITIVE_INFINITY;
159
- const relative = !finite ? null : expected === 0 ? (absolute === 0 ? 0 : null) : absolute / Math.abs(expected);
159
+ let relative = !finite ? null : expected === 0 ? (absolute === 0 ? 0 : null) : absolute / Math.abs(expected);
160
+ if (finite && !Number.isFinite(absolute)) {
161
+ // Finite subtraction overflows only across signs, where division first
162
+ // is safe. Keep subtraction first for nearby values to preserve precision.
163
+ relative = Math.abs(actual / expected - 1);
164
+ }
160
165
  return {
161
166
  ...(index !== undefined ? { index } : {}),
162
167
  actual,
@@ -0,0 +1,179 @@
1
+ import { lookup } from "node:dns/promises";
2
+ import { request as httpRequest } from "node:http";
3
+ import { request as httpsRequest } from "node:https";
4
+ import { BlockList, isIP } from "node:net";
5
+ import { Readable } from "node:stream";
6
+ import { createBrotliDecompress, createGunzip, createInflate } from "node:zlib";
7
+ import { WEB_FETCH_ALLOW_PRIVATE_NETWORK_ENV } from "./network-policy.js";
8
+
9
+ const blocked = new BlockList();
10
+ for (const [address, prefix] of [
11
+ ["0.0.0.0", 8],
12
+ ["10.0.0.0", 8],
13
+ ["100.64.0.0", 10],
14
+ ["127.0.0.0", 8],
15
+ ["169.254.0.0", 16],
16
+ ["172.16.0.0", 12],
17
+ ["192.168.0.0", 16],
18
+ ["192.0.0.0", 24],
19
+ ["192.0.2.0", 24],
20
+ ["198.18.0.0", 15],
21
+ ["198.51.100.0", 24],
22
+ ["203.0.113.0", 24],
23
+ ["224.0.0.0", 4],
24
+ ["240.0.0.0", 4],
25
+ ] as const)
26
+ blocked.addSubnet(address, prefix, "ipv4");
27
+ // Permit only global unicast IPv6. Block transition mechanisms that can embed
28
+ // an IPv4 destination and local/documentation allocations within that range.
29
+ for (const [address, prefix] of [
30
+ ["2001::", 32],
31
+ ["2001:db8::", 32],
32
+ ["2002::", 16],
33
+ ] as const)
34
+ blocked.addSubnet(address, prefix, "ipv6");
35
+ const globalV6 = new BlockList();
36
+ globalV6.addSubnet("2000::", 3, "ipv6");
37
+ export function isPublicWebAddress(address: string): boolean {
38
+ const family = isIP(address);
39
+ if (family === 4) return !blocked.check(address, "ipv4");
40
+ return family === 6 && globalV6.check(address, "ipv6") && !blocked.check(address, "ipv6");
41
+ }
42
+
43
+ export class WebFetchNetworkError extends Error {
44
+ constructor(message: string) {
45
+ super(`WEB_FETCH_PRIVATE_NETWORK: ${message}`);
46
+ this.name = "WebFetchNetworkError";
47
+ }
48
+ }
49
+
50
+ interface Address {
51
+ address: string;
52
+ family: number;
53
+ }
54
+ export interface WebFetchNetworkDependencies {
55
+ resolve?: (hostname: string) => Promise<Address[]>;
56
+ request?: (url: URL, init: RequestInit, address: Address) => Promise<Response>;
57
+ allowPrivateNetwork?: boolean;
58
+ }
59
+
60
+ /** Pin the checked address in the socket lookup while retaining the URL host
61
+ * for Host, TLS SNI and certificate verification. No second DNS resolution,
62
+ * proxy, pooled socket, or automatic redirect can bypass admission. */
63
+ async function requestPinnedWebUrl(url: URL, init: RequestInit, address: Address): Promise<Response> {
64
+ return new Promise((resolve, reject) => {
65
+ const headers = new Headers(init.headers);
66
+ headers.delete("host");
67
+ const request = (url.protocol === "https:" ? httpsRequest : httpRequest)(
68
+ url,
69
+ {
70
+ method: init.method ?? "GET",
71
+ headers: Object.fromEntries(headers),
72
+ agent: false,
73
+ ...(init.signal ? { signal: init.signal } : {}),
74
+ lookup: (_hostname, options, callback) => {
75
+ if (options.all) callback(null, [address]);
76
+ else callback(null, address.address, address.family);
77
+ },
78
+ },
79
+ (response) => {
80
+ const responseHeaders = new Headers();
81
+ for (const [key, value] of Object.entries(response.headers)) {
82
+ if (value !== undefined) responseHeaders.set(key, Array.isArray(value) ? value.join(", ") : value);
83
+ }
84
+ const status = response.statusCode ?? 500;
85
+ let stream: Readable = response;
86
+ const encoding = responseHeaders.get("content-encoding");
87
+ const decoder =
88
+ encoding === "gzip"
89
+ ? createGunzip()
90
+ : encoding === "br"
91
+ ? createBrotliDecompress()
92
+ : encoding === "deflate"
93
+ ? createInflate()
94
+ : null;
95
+ if (decoder) {
96
+ response.on("error", (error) => decoder.destroy(error));
97
+ decoder.on("close", () => response.destroy());
98
+ stream = response.pipe(decoder);
99
+ responseHeaders.delete("content-encoding");
100
+ responseHeaders.delete("content-length");
101
+ }
102
+ const noBody = init.method === "HEAD" || [204, 205, 304].includes(status);
103
+ if (noBody) response.resume();
104
+ const result = new Response(noBody ? null : (Readable.toWeb(stream) as ReadableStream<Uint8Array>), {
105
+ status,
106
+ statusText: response.statusMessage ?? "",
107
+ headers: responseHeaders,
108
+ });
109
+ Object.defineProperty(result, "url", { value: url.toString() });
110
+ resolve(result);
111
+ },
112
+ );
113
+ request.on("error", reject);
114
+ request.end(typeof init.body === "string" ? init.body : undefined);
115
+ });
116
+ }
117
+
118
+ /** DNS lookup itself is not cancellable; stop awaiting it on turn cancellation. */
119
+ async function abortableLookup(
120
+ pending: Promise<Address[]>,
121
+ signal: AbortSignal | null | undefined,
122
+ ): Promise<Address[]> {
123
+ if (!signal) return pending;
124
+ signal.throwIfAborted();
125
+ return new Promise((resolve, reject) => {
126
+ const onAbort = (): void => reject(signal.reason);
127
+ signal.addEventListener("abort", onAbort, { once: true });
128
+ pending.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
129
+ });
130
+ }
131
+
132
+ /** Every hop, including helper URLs, resolves and validates before connecting. */
133
+ export async function fetchWebUrl(
134
+ input: string | URL,
135
+ initial: RequestInit = {},
136
+ dependencies: WebFetchNetworkDependencies = {},
137
+ ): Promise<Response> {
138
+ let url = new URL(input);
139
+ const init = { ...initial, headers: new Headers(initial.headers) };
140
+ const allowPrivate = dependencies.allowPrivateNetwork ?? process.env[WEB_FETCH_ALLOW_PRIVATE_NETWORK_ENV] === "1";
141
+ for (let hop = 0; hop <= 10; hop++) {
142
+ init.signal?.throwIfAborted();
143
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password)
144
+ throw new Error("web_fetch: unsupported URL scheme or embedded credentials");
145
+ const hostname = url.hostname.replace(/^\[|\]$/g, "");
146
+ const addresses = isIP(hostname)
147
+ ? [{ address: hostname, family: isIP(hostname) }]
148
+ : await abortableLookup((dependencies.resolve ?? ((host) => lookup(host, { all: true })))(hostname), init.signal);
149
+ init.signal?.throwIfAborted();
150
+ const address = addresses[0];
151
+ if (address === undefined) throw new Error("web_fetch: DNS returned no addresses");
152
+ if (!allowPrivate && addresses.some((address) => !isPublicWebAddress(address.address)))
153
+ throw new WebFetchNetworkError(
154
+ `destination ${url.hostname} is not public; operator opt-in ${WEB_FETCH_ALLOW_PRIVATE_NETWORK_ENV}=1 is required`,
155
+ );
156
+ const response = await (dependencies.request ?? requestPinnedWebUrl)(url, init, address);
157
+ const location = response.headers.get("location");
158
+ if (![301, 302, 303, 307, 308].includes(response.status) || location === null) return response;
159
+ await response.body?.cancel();
160
+ if (hop === 10) throw new Error("web_fetch: too many redirects");
161
+ const next = new URL(location, url);
162
+ if (next.origin !== url.origin) {
163
+ // Custom headers can carry credentials too. A cross-origin redirect gets
164
+ // no caller headers; the target still receives the ordinary Host header.
165
+ init.headers = new Headers();
166
+ }
167
+ if (
168
+ (response.status === 303 && init.method !== "HEAD") ||
169
+ ([301, 302].includes(response.status) && init.method === "POST")
170
+ ) {
171
+ init.method = "GET";
172
+ delete init.body;
173
+ init.headers.delete("content-type");
174
+ init.headers.delete("content-length");
175
+ }
176
+ url = next;
177
+ }
178
+ throw new Error("web_fetch: too many redirects");
179
+ }
@@ -4,7 +4,8 @@ import type { ToolSurface } from "./lazy-tool.js";
4
4
 
5
5
  export const webFetchToolSurface = {
6
6
  name: ToolNames.WebFetch,
7
- description: "Fetch an http(s) URL; HTML is cleaned and converted to Markdown. Non-2xx responses are errors.",
7
+ description:
8
+ "Fetch an http(s) URL; HTML is cleaned and converted to Markdown. Non-2xx responses are errors. Non-GET/HEAD methods or any body require outward-action approval; private networks require operator opt-in.",
8
9
  parameters: Type.Object({
9
10
  url: Type.String({ description: "Fully-qualified http(s) URL." }),
10
11
  method: Type.Optional(Type.String({ description: "HTTP method (default GET)." })),
@@ -1,5 +1,7 @@
1
+ import { UNTRUSTED_CONTENT_BANNER } from "../core/untrusted-content.js";
1
2
  import type { ToolResult, ToolSpec } from "./registry.js";
2
3
  import { truncateUtf8 } from "./truncate-utf8.js";
4
+ import { fetchWebUrl } from "./web-fetch-network.js";
3
5
  import { webFetchToolSurface } from "./web-fetch-surface.js";
4
6
 
5
7
  const DEFAULT_TIMEOUT_MS = 30_000;
@@ -374,7 +376,7 @@ async function fetchArxivPaperSummary(
374
376
  maxBytes: number,
375
377
  ): Promise<ToolResult | null> {
376
378
  const metadataUrl = `https://export.arxiv.org/api/query?id_list=${encodeURIComponent(paperId)}`;
377
- const response = await fetch(metadataUrl, init);
379
+ const response = await fetchWebUrl(metadataUrl, init);
378
380
  if (response.status < 200 || response.status >= 300) return null;
379
381
  const read = await readResponseText(response, Math.min(120_000, maxBytes));
380
382
  const entry = /<entry\b[^>]*>([\s\S]*?)<\/entry>/i.exec(read.text)?.[1];
@@ -384,7 +386,7 @@ async function fetchArxivPaperSummary(
384
386
  let alphaxivRead: ReadResult | null = null;
385
387
  const alphaUrl = `https://alphaxiv.org/overview/${encodeURIComponent(paperId)}.md`;
386
388
  try {
387
- const alpha = await fetch(alphaUrl, init);
389
+ const alpha = await fetchWebUrl(alphaUrl, init);
388
390
  if (alpha.status >= 200 && alpha.status < 300) {
389
391
  alphaxivRead = await readResponseText(alpha, Math.min(80_000, Math.max(0, maxBytes - read.bytesRead)));
390
392
  if (alphaxivRead.text.trim().length > 0) paper.alphaxivOverview = alphaxivRead.text.trim();
@@ -416,7 +418,7 @@ async function fetchArxivPaperSummary(
416
418
  const bytesRead = read.bytesRead + (alphaxivRead?.bytesRead ?? 0);
417
419
  return {
418
420
  kind: "ok",
419
- output: truncate(lines.join("\n"), maxBytes),
421
+ output: truncate(`${UNTRUSTED_CONTENT_BANNER}\n${lines.join("\n")}`, maxBytes),
420
422
  details: {
421
423
  url: `https://arxiv.org/abs/${paper.id}`,
422
424
  status: 200,
@@ -437,7 +439,7 @@ async function fetchArxivApiSummary(
437
439
  init: Parameters<typeof fetch>[1],
438
440
  maxBytes: number,
439
441
  ): Promise<ToolResult | null> {
440
- const response = await fetch(url, init);
442
+ const response = await fetchWebUrl(url, init);
441
443
  if (response.status < 200 || response.status >= 300) return null;
442
444
  const read = await readResponseText(response, Math.min(240_000, maxBytes));
443
445
  const entries = Array.from(read.text.matchAll(/<entry\b[^>]*>([\s\S]*?)<\/entry>/gi))
@@ -469,7 +471,7 @@ async function fetchArxivApiSummary(
469
471
  }
470
472
  return {
471
473
  kind: "ok",
472
- output: truncate(lines.join("\n"), maxBytes),
474
+ output: truncate(`${UNTRUSTED_CONTENT_BANNER}\n${lines.join("\n")}`, maxBytes),
473
475
  details: {
474
476
  url: url.toString(),
475
477
  status: 200,
@@ -553,7 +555,7 @@ async function fetchRepoTreeSummary(
553
555
  init: Parameters<typeof fetch>[1],
554
556
  maxBytes: number,
555
557
  ): Promise<ToolResult | null> {
556
- const apiResponse = await fetch(target.apiUrl, init);
558
+ const apiResponse = await fetchWebUrl(target.apiUrl, init);
557
559
  if (apiResponse.status < 200 || apiResponse.status >= 300) return null;
558
560
  const apiRead = await readResponseText(apiResponse, Math.min(200_000, maxBytes));
559
561
  let entries: unknown[];
@@ -581,7 +583,7 @@ async function fetchRepoTreeSummary(
581
583
  for (const file of files) {
582
584
  if (remaining <= 2000) break;
583
585
  const rawUrl = `${target.rawBaseUrl}/${file}`;
584
- const rawResponse = await fetch(rawUrl, init);
586
+ const rawResponse = await fetchWebUrl(rawUrl, init);
585
587
  if (rawResponse.status < 200 || rawResponse.status >= 300) continue;
586
588
  const rawRead = await readResponseText(rawResponse, Math.min(80_000, remaining));
587
589
  sections.push("", `--- ${file} ---`, rawRead.text.trim());
@@ -589,7 +591,7 @@ async function fetchRepoTreeSummary(
589
591
  }
590
592
  return {
591
593
  kind: "ok",
592
- output: truncate(sections.join("\n"), maxBytes),
594
+ output: truncate(`${UNTRUSTED_CONTENT_BANNER}\n${sections.join("\n")}`, maxBytes),
593
595
  details: {
594
596
  url: target.apiUrl,
595
597
  status: 200,
@@ -621,7 +623,7 @@ function formatOutput(args: {
621
623
  if (args.extracted.description) lines.push(`Description: ${args.extracted.description}`);
622
624
  if (args.extracted.canonical) lines.push(`Canonical: ${args.extracted.canonical}`);
623
625
  lines.push("", "Content:", args.extracted.content || "[empty]");
624
- return truncate(lines.join("\n"), args.maxBytes);
626
+ return truncate(`${UNTRUSTED_CONTENT_BANNER}\n${lines.join("\n")}`, args.maxBytes);
625
627
  }
626
628
 
627
629
  export const webFetchTool: ToolSpec = {
@@ -683,10 +685,10 @@ export const webFetchTool: ToolSpec = {
683
685
  method,
684
686
  headers,
685
687
  signal: controller.signal,
686
- redirect: "follow",
688
+ redirect: "manual",
687
689
  };
688
690
  if (body !== undefined) init.body = body;
689
- if (method === "GET" && body === undefined && format !== "raw") {
691
+ if (method === "GET" && body === undefined && format !== "raw" && Object.keys(userHeaders).length === 0) {
690
692
  if (arxivApiUrl(parsed)) {
691
693
  const arxivApiSummary = await fetchArxivApiSummary(parsed, init, maxBytes);
692
694
  if (arxivApiSummary) return arxivApiSummary;
@@ -702,7 +704,7 @@ export const webFetchTool: ToolSpec = {
702
704
  if (repoSummary) return repoSummary;
703
705
  }
704
706
  }
705
- const response = await fetch(parsed, init);
707
+ const response = await fetchWebUrl(parsed, init);
706
708
  const contentType = headerValue(response, "content-type");
707
709
  const readLimit =
708
710
  response.status >= 200 && response.status < 300 ? maxBytes : Math.min(ERROR_PREVIEW_BYTES, maxBytes);
@@ -711,7 +713,7 @@ export const webFetchTool: ToolSpec = {
711
713
  const preview = extractWebFetchContent(read.text, contentType, response.url || parsed.toString(), "auto").content;
712
714
  return {
713
715
  kind: "error",
714
- message: `web_fetch: HTTP ${response.status}: ${response.statusText}${preview ? `\nPreview:\n${truncate(preview, ERROR_PREVIEW_BYTES)}` : ""}`,
716
+ message: `web_fetch: HTTP ${response.status}: ${response.statusText}${preview ? `\n${UNTRUSTED_CONTENT_BANNER}\nPreview:\n${truncate(preview, ERROR_PREVIEW_BYTES)}` : ""}`,
715
717
  };
716
718
  }
717
719
  if (isProbablyBinary(contentType, read.text)) {