@iowarp/clio-coder 0.4.8 → 0.4.9

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 (513) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +7 -6
  3. package/dist/{acp-JWPQZM2D.js → acp-ZWZCPS2A.js} +13 -13
  4. package/dist/{agents-4GCDIRVX.js → agents-LYSOWZUE.js} +53 -52
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-SYAKTDQE.js → auth-WBQGKWQ5.js} +27 -27
  7. package/dist/{background-OW67WMYR.js → background-BUMOBAQN.js} +4 -4
  8. package/dist/{builtins-TKS63VNV.js → builtins-ODRJOEDI.js} +7 -7
  9. package/dist/{chunk-JLBVCROW.js → chunk-2DRRZ57Q.js} +33 -24
  10. package/dist/chunk-2JBZYUPY.js +105 -0
  11. package/dist/chunk-2KUBKL63.js +336 -0
  12. package/dist/{chunk-K45LHSCI.js → chunk-2VBWFXYE.js} +7 -3
  13. package/dist/{chunk-XTLJDC2X.js → chunk-2X2R3UTH.js} +23 -18
  14. package/dist/{chunk-5HULHKT2.js → chunk-37LW32LZ.js} +5 -5
  15. package/dist/{chunk-W7KNI3F5.js → chunk-3AND2EFU.js} +5 -5
  16. package/dist/{chunk-36XSBPXL.js → chunk-3RDHGNU7.js} +95 -27
  17. package/dist/{chunk-BIY22UUK.js → chunk-3SUDBGEI.js} +11 -10
  18. package/dist/{chunk-3IQOZRQ6.js → chunk-3TQN67I2.js} +7 -7
  19. package/dist/{chunk-KWJXCL2X.js → chunk-3U4BL2EV.js} +3 -3
  20. package/dist/{chunk-RBES4TCZ.js → chunk-3UQADNG2.js} +3 -3
  21. package/dist/{chunk-V4ZKOKRB.js → chunk-434LRQ6Z.js} +61 -53
  22. package/dist/{chunk-MWMW53WW.js → chunk-4ATBOFRP.js} +649 -193
  23. package/dist/{chunk-QGIC5SX5.js → chunk-4DNL43EZ.js} +3 -3
  24. package/dist/{chunk-GVU4CXPJ.js → chunk-4HFJKRGZ.js} +113 -54
  25. package/dist/{chunk-5A6XR3RJ.js → chunk-555E2WVV.js} +16 -2
  26. package/dist/{chunk-LZDLYSUN.js → chunk-5RTEFNBP.js} +3 -3
  27. package/dist/{chunk-HMGAKB7E.js → chunk-64XJW7NT.js} +15 -15
  28. package/dist/{chunk-EXKUF426.js → chunk-7FBCRWVT.js} +280 -280
  29. package/dist/{chunk-K3W6BF5C.js → chunk-7JSOSVLP.js} +2 -2
  30. package/dist/{chunk-WFTSK7N3.js → chunk-7N3OLLSL.js} +4 -4
  31. package/dist/{chunk-JRA6OBHY.js → chunk-A6DULIMH.js} +17 -5
  32. package/dist/{chunk-R5NRX5N4.js → chunk-AB3QXXBC.js} +2 -2
  33. package/dist/chunk-ACCECAH4.js +49 -0
  34. package/dist/{chunk-GJBJHJNY.js → chunk-AM6CEP2K.js} +5 -5
  35. package/dist/{chunk-MUXNDNMZ.js → chunk-AY4XE6GN.js} +19 -13
  36. package/dist/{chunk-MATCRH3M.js → chunk-BBA2TAWV.js} +9 -9
  37. package/dist/{chunk-PMUUAABD.js → chunk-BMNVVKEW.js} +9 -9
  38. package/dist/{chunk-TOPICHGR.js → chunk-CTGZCEI6.js} +2 -2
  39. package/dist/{chunk-L75H5B3B.js → chunk-DIAJYJYG.js} +2 -2
  40. package/dist/{chunk-JIMWEYWO.js → chunk-EHZRAB4U.js} +2 -2
  41. package/dist/{chunk-YXANQGCX.js → chunk-EM2DIQAJ.js} +6 -6
  42. package/dist/{chunk-JCVIL6TY.js → chunk-ETF4ROKH.js} +4 -4
  43. package/dist/{chunk-5W4FRA5Q.js → chunk-F2YM3JY4.js} +483 -253
  44. package/dist/{chunk-YGIV24MF.js → chunk-FEBQWHJT.js} +21 -16
  45. package/dist/{chunk-PGEFOTJB.js → chunk-FPS4R2RN.js} +5 -4
  46. package/dist/{chunk-XC5POIL4.js → chunk-G5R53COU.js} +1 -1
  47. package/dist/chunk-GHXGMFQ5.js +40 -0
  48. package/dist/{chunk-QLKCCC2J.js → chunk-GJDRMBDI.js} +14 -14
  49. package/dist/{chunk-2ZWLMA3T.js → chunk-GREYLTZ5.js} +7 -7
  50. package/dist/{chunk-YXFKLMX2.js → chunk-H2WSCZZT.js} +22 -6
  51. package/dist/{chunk-FNVPQQYT.js → chunk-H6IUCY6Q.js} +2 -2
  52. package/dist/{chunk-5C2OQHLR.js → chunk-H72DCH3E.js} +92 -203
  53. package/dist/{chunk-JYKUMHCQ.js → chunk-H7TXJR23.js} +21 -21
  54. package/dist/chunk-HBJQQTKM.js +40 -0
  55. package/dist/chunk-HCVKSGNZ.js +990 -0
  56. package/dist/{chunk-ARRUMZ7X.js → chunk-I47EJPYL.js} +3 -3
  57. package/dist/{chunk-YKIIWURD.js → chunk-IALGWTLO.js} +3 -3
  58. package/dist/{chunk-FB3KTE2J.js → chunk-IALZJE37.js} +4 -4
  59. package/dist/{chunk-RAUOKUZV.js → chunk-IC7JXLOM.js} +15 -10
  60. package/dist/{chunk-LLCGGVSW.js → chunk-IOSVTTCD.js} +7 -7
  61. package/dist/{chunk-DBREN25X.js → chunk-IPTPMLA6.js} +8 -3
  62. package/dist/{chunk-V552WL53.js → chunk-IZZI3SKQ.js} +3 -67
  63. package/dist/{chunk-UDAK6GR2.js → chunk-JIYWMQZ5.js} +2 -2
  64. package/dist/{chunk-O3HSB2YL.js → chunk-JKU34ACI.js} +2 -2
  65. package/dist/{chunk-6X7VOCDY.js → chunk-JTR3VSV3.js} +3 -3
  66. package/dist/{chunk-O2QJDRWP.js → chunk-JU6MAMRJ.js} +4 -4
  67. package/dist/chunk-JURMQWVJ.js +15 -0
  68. package/dist/{chunk-STC6CWUZ.js → chunk-K3XN4LZ4.js} +5 -5
  69. package/dist/{chunk-ATB3Q7YO.js → chunk-KIVFXGBM.js} +27 -2
  70. package/dist/{chunk-WJWNZXRM.js → chunk-KOAHT3N2.js} +2 -2
  71. package/dist/chunk-KP5BPXR3.js +73 -0
  72. package/dist/{chunk-K53E2PTW.js → chunk-KRJQG47M.js} +3 -3
  73. package/dist/{chunk-IILXVWJY.js → chunk-KW6OJ7IY.js} +19 -21
  74. package/dist/{chunk-A46ILRS2.js → chunk-L3NBWLSN.js} +4 -4
  75. package/dist/{chunk-2U2U5POA.js → chunk-L4RUTLRL.js} +5 -5
  76. package/dist/{chunk-QYSAZMSH.js → chunk-LGTVWPRN.js} +2 -2
  77. package/dist/{chunk-KGP3UQGJ.js → chunk-LSQW6KZO.js} +5 -5
  78. package/dist/{chunk-P4JS3MH5.js → chunk-LWJYOHT3.js} +8 -8
  79. package/dist/{chunk-U3TPUXOQ.js → chunk-M6YLF66Z.js} +74 -40
  80. package/dist/{chunk-5D2FNBMG.js → chunk-MJ4QEJVT.js} +2 -2
  81. package/dist/{chunk-RM3XJX7P.js → chunk-MO65K5CH.js} +5 -5
  82. package/dist/{chunk-H5DPSUBB.js → chunk-MTOZEQHL.js} +3 -3
  83. package/dist/{chunk-6R5MBXBJ.js → chunk-NBPA5QM6.js} +2 -2
  84. package/dist/{chunk-H77XBUBM.js → chunk-NMBEW4PP.js} +3 -3
  85. package/dist/{chunk-QWUSS2QP.js → chunk-NPLLK3HO.js} +67 -87
  86. package/dist/{chunk-K5UJD6MM.js → chunk-NSYGPIVW.js} +5 -5
  87. package/dist/{context-EZZPT2ZV.js → chunk-NXTV5OI5.js} +30 -84
  88. package/dist/{chunk-F7Y6QTPL.js → chunk-NYDG4QP4.js} +4 -4
  89. package/dist/{chunk-PFZJMXIY.js → chunk-OA2GDACN.js} +2 -2
  90. package/dist/{chunk-Q32C6AWI.js → chunk-PJI57EC6.js} +4 -4
  91. package/dist/{chunk-EFXMQYI6.js → chunk-PJJBUGMR.js} +52 -52
  92. package/dist/{chunk-ICYIUNO4.js → chunk-PNJE7JPS.js} +18 -3
  93. package/dist/chunk-PP23PZEV.js +1347 -0
  94. package/dist/chunk-PPIFDSBK.js +409 -0
  95. package/dist/{chunk-RFULVPBX.js → chunk-PTFISXQO.js} +6 -5
  96. package/dist/{chunk-JV25I7ID.js → chunk-QB33Z5S7.js} +5 -5
  97. package/dist/{chunk-D55WO5J7.js → chunk-QGNII5VC.js} +8 -8
  98. package/dist/{chunk-RKFNU5UA.js → chunk-QI72X67S.js} +104 -27
  99. package/dist/chunk-QKHZZFKH.js +98 -0
  100. package/dist/{chunk-5UG6EZI3.js → chunk-RDKX2PME.js} +4 -4
  101. package/dist/{chunk-B5ZTR6KZ.js → chunk-RL7LJYN4.js} +1 -1
  102. package/dist/{chunk-BE4I3UJ2.js → chunk-RQROVOV3.js} +15 -13
  103. package/dist/{chunk-TEHVMHOK.js → chunk-S34A7BSP.js} +3 -3
  104. package/dist/{chunk-4P6M2TTE.js → chunk-SJETVJHX.js} +2 -2
  105. package/dist/{chunk-OPC4DQD6.js → chunk-SJMNU4ZS.js} +2 -2
  106. package/dist/{chunk-2JSCCEKO.js → chunk-SLLG2G44.js} +2 -2
  107. package/dist/{chunk-EPYATQMG.js → chunk-SQAZN2V3.js} +42 -16
  108. package/dist/{chunk-E2NSPYLC.js → chunk-SQBT46OF.js} +5 -5
  109. package/dist/{chunk-WTZVSMQZ.js → chunk-TCUOGIL2.js} +5 -5
  110. package/dist/{chunk-RRZFRIJP.js → chunk-TIWPKCVO.js} +5 -5
  111. package/dist/{chunk-QLAGXTTV.js → chunk-UIU7MGZ3.js} +5 -1
  112. package/dist/{chunk-WLHX3MM5.js → chunk-V4XJWDHR.js} +21 -11
  113. package/dist/{chunk-PJ5H5B7A.js → chunk-VPWGADBG.js} +18 -18
  114. package/dist/{chunk-GEYX2NZ5.js → chunk-VWFCAK34.js} +6 -6
  115. package/dist/{chunk-44U2OUSM.js → chunk-W72LLZAV.js} +2 -2
  116. package/dist/{chunk-ORRNGQDV.js → chunk-WGRDABW7.js} +4 -4
  117. package/dist/{chunk-RNLAPBJL.js → chunk-XFLSL7BT.js} +2 -2
  118. package/dist/{chunk-2SXGG2IV.js → chunk-XJZEDCNT.js} +6 -6
  119. package/dist/{chunk-UB33F3YW.js → chunk-XLLEL3K7.js} +5 -5
  120. package/dist/{chunk-2GPGAE5W.js → chunk-XNIZBOFV.js} +15 -1
  121. package/dist/{chunk-WS4FOGLY.js → chunk-XNZWWDC3.js} +2 -2
  122. package/dist/{chunk-P7GTE4FX.js → chunk-XRECG65F.js} +2 -2
  123. package/dist/chunk-XTQ7MEJG.js +457 -0
  124. package/dist/{chunk-GEVQNST4.js → chunk-XZWSP67B.js} +2 -2
  125. package/dist/{chunk-OEMFEL26.js → chunk-Y3M2T5RO.js} +3 -3
  126. package/dist/{chunk-HP7KJ5O2.js → chunk-YPQOPFP7.js} +6 -6
  127. package/dist/{chunk-MEHBW2SP.js → chunk-YQYINEJI.js} +6 -6
  128. package/dist/{chunk-RXI7K4J5.js → chunk-ZF6CV4WZ.js} +1338 -217
  129. package/dist/{chunk-3XE4NRLF.js → chunk-ZKJU55MX.js} +4 -2
  130. package/dist/{chunk-526ABLHD.js → chunk-ZKQ63XZ5.js} +12 -35
  131. package/dist/{chunk-PAQVETLA.js → chunk-ZKVMDNAN.js} +5 -5
  132. package/dist/cli/index.js +47 -37
  133. package/dist/{clio-BPBAPZGA.js → clio-JUDCLNHC.js} +7 -7
  134. package/dist/clio-context-tools-FT672MNZ.js +136 -0
  135. package/dist/{code-nav-KBQ3RIFK.js → code-nav-7OVCTP2Z.js} +28 -26
  136. package/dist/{components-6DKAXWFN.js → components-OZXZMDWU.js} +5 -5
  137. package/dist/{config-FWL5JK2H.js → config-P3ZQVF63.js} +80 -75
  138. package/dist/{config-graph-ACDB26L2.js → config-graph-GKAOBAS5.js} +74 -70
  139. package/dist/{configure-ZIZ4YV62.js → configure-25LRYZJG.js} +45 -44
  140. package/dist/context-72VQV7JX.js +73 -0
  141. package/dist/{context-VIL5MFRH.js → context-COWRWXP3.js} +74 -70
  142. package/dist/{context-Z3GOBHDK.js → context-WB3D474G.js} +12 -12
  143. package/dist/{context-clear-ZPB3L7MN.js → context-clear-Y4LYMWC6.js} +70 -66
  144. package/dist/{context-working-set-2GLBVF7L.js → context-working-set-TMZYVC7A.js} +15 -14
  145. package/dist/data-tool-KMBWKKWR.js +2987 -0
  146. package/dist/{detail-VSBWYSGZ.js → detail-4GLROGY5.js} +71 -67
  147. package/dist/{dispatch-runner-EEEHGKX4.js → dispatch-runner-TR3LBP5P.js} +92 -88
  148. package/dist/{docs-VMMWFVHL.js → docs-ZK777W2G.js} +3 -3
  149. package/dist/{doctor-V5L4IQGP.js → doctor-WC4OV2OB.js} +61 -59
  150. package/dist/{eval-VJYJ2BV3.js → eval-FUNWJNUJ.js} +45 -45
  151. package/dist/{evals-J2H5V6DD.js → evals-L73EBHSU.js} +4 -4
  152. package/dist/{evidence-Y35YSXH6.js → evidence-AVJ2RTHX.js} +80 -76
  153. package/dist/{evidence-POWAEBEW.js → evidence-P4NZOAK7.js} +70 -66
  154. package/dist/{evidence-XCCCLS36.js → evidence-Y55WKYHH.js} +79 -75
  155. package/dist/{evolve-GPG72VB6.js → evolve-MAWKRYWF.js} +75 -71
  156. package/dist/{extensions-YIZ7W3DA.js → extensions-PYQH3H47.js} +8 -8
  157. package/dist/{fleet-2MDCQ2VK.js → fleet-M4H2QBWT.js} +84 -80
  158. package/dist/{fleet-W2AF3TOJ.js → fleet-ULBXXRDR.js} +123 -119
  159. package/dist/{fleet-commands-WS56G26H.js → fleet-commands-7NNC4JZU.js} +17 -16
  160. package/dist/{fleet-decisions-MYGZ3JW7.js → fleet-decisions-EHEY7KPU.js} +9 -9
  161. package/dist/{fleet-graph-ELTBXBAV.js → fleet-graph-LQ2AHZT3.js} +15 -15
  162. package/dist/{fleet-inspect-45IK73L4.js → fleet-inspect-5IGBIIYZ.js} +76 -72
  163. package/dist/{fleet-preflight-T5NCZKHE.js → fleet-preflight-UTTNOKMG.js} +7 -2
  164. package/dist/{fleet-validate-VG4JBLA4.js → fleet-validate-YGILJKA6.js} +20 -20
  165. package/dist/{fleet-verify-VTSABAYO.js → fleet-verify-AUWTCDKR.js} +70 -66
  166. package/dist/{fleet-view-XC7WMKSS.js → fleet-view-TGUFCY5A.js} +73 -69
  167. package/dist/{init-VTJNNTIP.js → init-GFN2Z7NF.js} +94 -90
  168. package/dist/{interop-IU6BNA7C.js → interop-EF5JUWWK.js} +21 -21
  169. package/dist/{inventory-QPQUTUUZ.js → inventory-I6VJDDYF.js} +73 -69
  170. package/dist/{library-Q3NWL5CM.js → library-3J5IO2OJ.js} +13 -13
  171. package/dist/{library-LF7DLU6S.js → library-IJTOFTDS.js} +16 -15
  172. package/dist/{library-HUTAS57T.js → library-Z3KZSHPE.js} +27 -27
  173. package/dist/{library-import-PPW4YUC5.js → library-import-IMKFLDHX.js} +21 -21
  174. package/dist/{library-inventory-DIRVFRZU.js → library-inventory-CQP5D6GU.js} +13 -13
  175. package/dist/{library-validation-XHUZ3INE.js → library-validation-33UTX7AI.js} +11 -11
  176. package/dist/mcp-5FWZKUS6.js +26 -0
  177. package/dist/{memory-Z2LSKNV6.js → memory-36CWN67Y.js} +75 -71
  178. package/dist/{models-3FTREYPY.js → models-KQ6PQJJQ.js} +49 -49
  179. package/dist/{monitor-HF2MQRB3.js → monitor-XX6ZD6KC.js} +83 -79
  180. package/dist/{orchestrator-UYPA6W4S.js → orchestrator-SK6HDAOP.js} +1337 -772
  181. package/dist/{panes-736XB73K.js → panes-L27QRXUH.js} +12 -12
  182. package/dist/{panes-C4P3PIWT.js → panes-LXZIWNOZ.js} +4 -4
  183. package/dist/{paths-JQ4QYIYB.js → paths-7DJDU4P4.js} +5 -5
  184. package/dist/{preload-XSLRYH3Y.js → preload-K2TDYI4Y.js} +70 -66
  185. package/dist/{reset-V7QSPZYE.js → reset-73VU3XNT.js} +10 -10
  186. package/dist/{resources-OJGDFHYT.js → resources-3BYJYVYL.js} +16 -16
  187. package/dist/{run-T354SMDW.js → run-AR7PY6GW.js} +132 -128
  188. package/dist/run-script-H2KUDIII.js +41 -0
  189. package/dist/{settings-MWZXO6RV.js → settings-3VMBMWID.js} +4 -4
  190. package/dist/{share-Y5ZKJPND.js → share-HVWH4FPF.js} +21 -21
  191. package/dist/{skills-4GLZC4LN.js → skills-5TYEY75Q.js} +22 -22
  192. package/dist/{skills-eval-L5T7GODM.js → skills-eval-CK3MD63Z.js} +96 -87
  193. package/dist/{skills-inventory-LASKXEZI.js → skills-inventory-KSFYG3I6.js} +16 -16
  194. package/dist/{slash-commands-OIMRU36D.js → slash-commands-DMVHML7P.js} +47 -44
  195. package/dist/{steer-KMAM2Y2C.js → steer-XCD6V3EW.js} +4 -4
  196. package/dist/{support-AWC3XKPX.js → support-JAZL6VBR.js} +7 -7
  197. package/dist/{system-CD75NYBU.js → system-3TDQRMZZ.js} +26 -26
  198. package/dist/{targets-VSZX2J2S.js → targets-EZKHAYRN.js} +66 -65
  199. package/dist/{tasks-AN567LYH.js → tasks-SNEFHWYW.js} +12 -12
  200. package/dist/{terminal-lease-6KTWNHVY.js → terminal-lease-BNLFA4OF.js} +8 -7
  201. package/dist/{tools-AGRK3V5J.js → tools-7IM6BDCE.js} +12 -12
  202. package/dist/{trace-P4MQZD33.js → trace-C3MBGD4S.js} +8 -8
  203. package/dist/{tui-primitives-A6MUW32V.js → tui-primitives-36BY2774.js} +2 -2
  204. package/dist/{uninstall-BRUTEPIC.js → uninstall-WWLOSZ42.js} +10 -10
  205. package/dist/{upgrade-SBCGHQGJ.js → upgrade-ETK2JNSJ.js} +37 -47
  206. package/dist/{usage-BIDVTO6V.js → usage-S2HOB7BZ.js} +93 -89
  207. package/dist/{verifiers-LTGCAMXS.js → verifiers-QDTEVH7R.js} +24 -22
  208. package/dist/{verify-OZU4LLNP.js → verify-SUKUK6XM.js} +15 -14
  209. package/dist/web/client/assets/{abnfDiagram-VCTEODGH-C5wfoHm3.js → abnfDiagram-VCTEODGH-BznNOe_I.js} +1 -1
  210. package/dist/web/client/assets/{arc-oH91sfLl.js → arc-Pry-7G1_.js} +1 -1
  211. package/dist/web/client/assets/architecture-7GRP2DOG-BQO9DL_y.js +1 -0
  212. package/dist/web/client/assets/architectureDiagram-5GKGNRK7-CKPbdvyx.js +36 -0
  213. package/dist/web/client/assets/{blockDiagram-I7D4REHJ-wi0_CYHL.js → blockDiagram-I7D4REHJ-prINbpbM.js} +1 -1
  214. package/dist/web/client/assets/{c4Diagram-7LVT6UL2-DV7NYc2W.js → c4Diagram-7LVT6UL2-DkVRKtyU.js} +1 -1
  215. package/dist/web/client/assets/channel-c4EeUUqA.js +1 -0
  216. package/dist/web/client/assets/{chunk-4HAMMTFA-Bhsegh3F.js → chunk-4HAMMTFA-CV0Z1ApC.js} +1 -1
  217. package/dist/web/client/assets/{chunk-75Z2AOVW-RQCckcQG.js → chunk-75Z2AOVW-D34Nohxq.js} +1 -1
  218. package/dist/web/client/assets/chunk-DU6HZSFF-Das_cS-O.js +125 -0
  219. package/dist/web/client/assets/{chunk-F27PBJKO-BcNqv2x1.js → chunk-F27PBJKO-CmRojxkG.js} +1 -1
  220. package/dist/web/client/assets/{chunk-GMAD6QVW-Bq8JWiZT.js → chunk-GMAD6QVW-ClYZmc73.js} +1 -1
  221. package/dist/web/client/assets/{chunk-GVQU2GXP-DbchrAeJ.js → chunk-GVQU2GXP-B2BiHFt-.js} +1 -1
  222. package/dist/web/client/assets/{chunk-IMKFNOWR-DrpvjocK.js → chunk-IMKFNOWR-BGApZ8nP.js} +1 -1
  223. package/dist/web/client/assets/{chunk-L3NEJ4N5-DHLmTnCe.js → chunk-L3NEJ4N5-DIzCgpkY.js} +1 -1
  224. package/dist/web/client/assets/{chunk-OSK3NFVY-CsRbtN9n.js → chunk-OSK3NFVY-BPT-yePv.js} +1 -1
  225. package/dist/web/client/assets/{chunk-P2QGCYS3-aqPbxoMh.js → chunk-P2QGCYS3-BC1WTc2t.js} +1 -1
  226. package/dist/web/client/assets/{chunk-POPQ4Y6H-Dl5vw0h0.js → chunk-POPQ4Y6H-BSjzBmZU.js} +1 -1
  227. package/dist/web/client/assets/{chunk-PWAF6VOD-D7OBRSdO.js → chunk-PWAF6VOD-BIjLs86Z.js} +1 -1
  228. package/dist/web/client/assets/{chunk-SHT3W25Y-CdKCIwuB.js → chunk-SHT3W25Y-CduDz2cm.js} +1 -1
  229. package/dist/web/client/assets/{chunk-SVP7TREG-B94Nu_1-.js → chunk-SVP7TREG-XUn4pPAP.js} +1 -1
  230. package/dist/web/client/assets/{chunk-TICWLB2K-jjRQI_hk.js → chunk-TICWLB2K-DcBy2DKy.js} +1 -1
  231. package/dist/web/client/assets/{chunk-XXDRQBXY-azkbKE37.js → chunk-XXDRQBXY-DqAYeljN.js} +1 -1
  232. package/dist/web/client/assets/classDiagram-ZZMXUADV-C0HR7YKN.js +1 -0
  233. package/dist/web/client/assets/classDiagram-v2-VYDZK3BY-C0HR7YKN.js +1 -0
  234. package/dist/web/client/assets/cose-bilkent-JH36ORCC-CVh1icwb.js +1 -0
  235. package/dist/web/client/assets/{cynefin-OW5HDTMX--i9RDU5f.js → cynefin-OW5HDTMX-BecPbyM6.js} +1 -1
  236. package/dist/web/client/assets/{cynefinDiagram-5FMLGOSQ-cRcvyBdp.js → cynefinDiagram-5FMLGOSQ-BzMzFwyT.js} +1 -1
  237. package/dist/web/client/assets/{dagre-GXQ25YYZ-C9mZmAPe.js → dagre-GXQ25YYZ-CAWJmRMG.js} +1 -1
  238. package/dist/web/client/assets/{diagram-S7CK7UJ4-Oj_zlLDY.js → diagram-S7CK7UJ4--lp0ALmn.js} +1 -1
  239. package/dist/web/client/assets/{diagram-UQ7AKVKN-BWKbbfcR.js → diagram-UQ7AKVKN-gKUnsjcj.js} +1 -1
  240. package/dist/web/client/assets/{diagram-VSXAHHWV-C3N3f7DO.js → diagram-VSXAHHWV-BoN5D1Gh.js} +1 -1
  241. package/dist/web/client/assets/{diagram-VX7I27RA-CtDKDAWe.js → diagram-VX7I27RA-BMtuswQ6.js} +1 -1
  242. package/dist/web/client/assets/{diagram-Z3DM3KII-B8bkBRCz.js → diagram-Z3DM3KII-DtU8LXrA.js} +1 -1
  243. package/dist/web/client/assets/dist-v5Q1xZ2K.js +1 -0
  244. package/dist/web/client/assets/{ebnfDiagram-PWID7BFC-CskW-Whx.js → ebnfDiagram-PWID7BFC-DDcnv9BH.js} +1 -1
  245. package/dist/web/client/assets/{erDiagram-RLTQ6QDP-C-83cgCW.js → erDiagram-RLTQ6QDP-DvRb4iDL.js} +1 -1
  246. package/dist/web/client/assets/eventmodeling-NTZA5JFV-01h4SbTc.js +1 -0
  247. package/dist/web/client/assets/flowDiagram-HODETNUW-Bll_YZ6T.js +1 -0
  248. package/dist/web/client/assets/framework-YGoR34M4.js +9 -0
  249. package/dist/web/client/assets/ganttDiagram-EL5Y4UJY-DdFZ9hvH.js +292 -0
  250. package/dist/web/client/assets/{gitGraph-4MIJSDKK-B40cQtBE.js → gitGraph-4MIJSDKK-DjD_AAMj.js} +1 -1
  251. package/dist/web/client/assets/{gitGraphDiagram-WWUBYQGX-CH-7P7Di.js → gitGraphDiagram-WWUBYQGX-CDyN9n2u.js} +1 -1
  252. package/dist/web/client/assets/index-CVcy0FAn.js +75 -0
  253. package/dist/web/client/assets/{info-A6RAGUB7-eCCE9D_Q.js → info-A6RAGUB7-C_XK8Hq0.js} +1 -1
  254. package/dist/web/client/assets/{infoDiagram-27XIBGKW-DRnvqMjM.js → infoDiagram-27XIBGKW-DIe4tJ1i.js} +1 -1
  255. package/dist/web/client/assets/{ishikawaDiagram-5VMMS53U-DBA_55wp.js → ishikawaDiagram-5VMMS53U-CVl5unKI.js} +1 -1
  256. package/dist/web/client/assets/{journeyDiagram-3NMN7TZE-C-ewpNl7.js → journeyDiagram-3NMN7TZE-CPEB1Vt8.js} +1 -1
  257. package/dist/web/client/assets/{kanban-definition-UXKFOSKX-B1taGsu0.js → kanban-definition-UXKFOSKX-j-9WXjYQ.js} +1 -1
  258. package/dist/web/client/assets/{line-lZ0kyxDI.js → line-BIDAcEtX.js} +1 -1
  259. package/dist/web/client/assets/{linear-DIc8FgZj.js → linear-SzVmGSw4.js} +1 -1
  260. package/dist/web/client/assets/{mermaid-parser.core-SXolxpXw.js → mermaid-parser.core-Ux4vssh0.js} +3 -3
  261. package/dist/web/client/assets/{mermaid.core-DXgQtkNg.js → mermaid.core-CDuwVKeh.js} +4 -4
  262. package/dist/web/client/assets/{mindmap-definition-YA3MSWOX-DXJas9K7.js → mindmap-definition-YA3MSWOX-5MlQEeQ6.js} +1 -1
  263. package/dist/web/client/assets/{packet-AYTQ26CC-OqVl_07o.js → packet-AYTQ26CC-DRO66c9Q.js} +1 -1
  264. package/dist/web/client/assets/{pegDiagram-XKGWAZYB-CPE_zdfN.js → pegDiagram-XKGWAZYB-BakZnSjM.js} +1 -1
  265. package/dist/web/client/assets/{pie-WAS4IAKB-DAxnMBLe.js → pie-WAS4IAKB-BEzcmhz8.js} +1 -1
  266. package/dist/web/client/assets/{pieDiagram-E7YTZNPT-Uwjpufxv.js → pieDiagram-E7YTZNPT-DpmqgoPt.js} +1 -1
  267. package/dist/web/client/assets/prism-core-BtsZdCS6.js +1 -0
  268. package/dist/web/client/assets/{quadrantDiagram-AXDQQJYC-Bjs4yjgs.js → quadrantDiagram-AXDQQJYC-DuMPwwV2.js} +1 -1
  269. package/dist/web/client/assets/{radar-RG4KPBEZ-CuCKjHM3.js → radar-RG4KPBEZ-Djm3oXl5.js} +1 -1
  270. package/dist/web/client/assets/{railroad-74A4TZTK-CnQK-Cc1.js → railroad-74A4TZTK-Cpvkwsao.js} +1 -1
  271. package/dist/web/client/assets/railroad-abnf-HS5TGJTU-CpRQ3ruG.js +1 -0
  272. package/dist/web/client/assets/railroad-ebnf-LZEXJU2U-1Rm5a5eJ.js +1 -0
  273. package/dist/web/client/assets/railroad-peg-WCYAUIDC-C8344cTh.js +1 -0
  274. package/dist/web/client/assets/{railroadDiagram-O6MQD6OU-BDDIPNLL.js → railroadDiagram-O6MQD6OU-CgMS4B6R.js} +1 -1
  275. package/dist/web/client/assets/{requirementDiagram-BXWQKSXE-B5Ydyxc1.js → requirementDiagram-BXWQKSXE-CX5eoNdN.js} +1 -1
  276. package/dist/web/client/assets/rolldown-runtime-hePW80VL.js +1 -0
  277. package/dist/web/client/assets/{sankeyDiagram-P5KCCOFB-Bw2Qym75.js → sankeyDiagram-P5KCCOFB-CODBX2N3.js} +1 -1
  278. package/dist/web/client/assets/{sequenceDiagram-WJ2MYXX4-BZf36nO0.js → sequenceDiagram-WJ2MYXX4-eUhhF8OR.js} +1 -1
  279. package/dist/web/client/assets/src-B6xuSHsQ.js +1 -0
  280. package/dist/web/client/assets/{stateDiagram-D77RDMKH-DnH6JRkp.js → stateDiagram-D77RDMKH-vR6l0NC2.js} +1 -1
  281. package/dist/web/client/assets/stateDiagram-v2-MP3YSRHH-BYUm8jLY.js +1 -0
  282. package/dist/web/client/assets/{swimlanes-42K2YHIH-D9PZmtZg.js → swimlanes-42K2YHIH-v9Tw_zX7.js} +1 -1
  283. package/dist/web/client/assets/swimlanesDiagram-VR7AAH4N-B2jdCvhD.js +8 -0
  284. package/dist/web/client/assets/{timeline-definition-24CTP7MA-CrkZZBo7.js → timeline-definition-24CTP7MA-DxianbhU.js} +1 -1
  285. package/dist/web/client/assets/{treeView-Q6P3EWNA-C9zDShoC.js → treeView-Q6P3EWNA-DLxnZb9R.js} +1 -1
  286. package/dist/web/client/assets/{treemap-WGGIJYW6-B-plj9yK.js → treemap-WGGIJYW6-I7UnZ6xJ.js} +1 -1
  287. package/dist/web/client/assets/{vennDiagram-4TSXK5OY-VeOdaphN.js → vennDiagram-4TSXK5OY-Ar7M8Mrj.js} +1 -1
  288. package/dist/web/client/assets/{wardley-WFR3VGLG-Wl1P_m6L.js → wardley-WFR3VGLG-B8ZSHeDu.js} +1 -1
  289. package/dist/web/client/assets/{wardleyDiagram-VM6X3IG4-B85-R9t2.js → wardleyDiagram-VM6X3IG4-Qks2FmIn.js} +1 -1
  290. package/dist/web/client/assets/{xychartDiagram-S5SC5T6Z-BrD8OW8m.js → xychartDiagram-S5SC5T6Z-BBBjRtiE.js} +1 -1
  291. package/dist/web/client/index.html +3 -1
  292. package/dist/web/reads-worker.js +17 -17
  293. package/dist/web/server.js +25 -25
  294. package/dist/{web-EU6ATJWA.js → web-EMGUGGEI.js} +3 -3
  295. package/dist/{web-fetch-U22Q7HV4.js → web-fetch-6BRRVQON.js} +22 -5
  296. package/dist/{wiki-generate-LZRLFG6I.js → wiki-generate-XHSJTDOP.js} +104 -100
  297. package/dist/{with-panes-X5CZOGML.js → with-panes-Z6BTEHMZ.js} +11 -11
  298. package/dist/worker/entry.js +101 -87
  299. package/docs/README.md +1 -1
  300. package/docs/architecture/artifact-placement.md +12 -1
  301. package/docs/architecture/library.md +1 -1
  302. package/docs/architecture/prompt-envelope-and-tools.md +37 -27
  303. package/docs/architecture/safety-model.md +19 -6
  304. package/docs/architecture/session-lifecycle.md +10 -4
  305. package/docs/architecture/tui-design.md +37 -14
  306. package/docs/guide/commands-and-modes.md +39 -19
  307. package/docs/guide/configuration-and-targets.md +7 -5
  308. package/docs/guide/configuration-reference.md +116 -13
  309. package/docs/guide/environment-variables.md +2 -5
  310. package/docs/guide/fleet-dispatch.md +3 -2
  311. package/docs/guide/glossary.md +1 -1
  312. package/docs/guide/installation-and-lifecycle.md +2 -2
  313. package/docs/guide/resource-library.md +1 -1
  314. package/docs/guide/tool-usage.md +166 -64
  315. package/docs/guide/troubleshooting.md +1 -1
  316. package/docs/process/documentation-guide.md +1 -0
  317. package/docs/process/fleet-demo-runbook.md +6 -6
  318. package/docs/process/scientific-validation.md +22 -2
  319. package/docs/process/tool-audit-v0.4.9.md +357 -0
  320. package/package.json +1 -1
  321. package/src/cli/argv.ts +31 -0
  322. package/src/cli/clio.ts +17 -13
  323. package/src/cli/config.ts +4 -0
  324. package/src/cli/doctor-panes.ts +1 -1
  325. package/src/cli/doctor.ts +3 -1
  326. package/src/cli/index.ts +14 -1
  327. package/src/cli/mcp.ts +106 -0
  328. package/src/cli/skills-eval.ts +8 -2
  329. package/src/cli/upgrade.ts +34 -38
  330. package/src/cli/verifiers.ts +1 -1
  331. package/src/core/bash-exec.ts +27 -4
  332. package/src/core/config.ts +2 -1
  333. package/src/core/defaults.ts +10 -1
  334. package/src/core/event-bus.ts +1 -1
  335. package/src/core/run-records.ts +438 -0
  336. package/src/core/safe-exec.ts +506 -56
  337. package/src/core/skill-activation.ts +5 -3
  338. package/src/core/termination.ts +3 -3
  339. package/src/core/tool-names.ts +33 -12
  340. package/src/domains/context/clio-md.ts +1 -1
  341. package/src/domains/context/worker/pressure.ts +6 -2
  342. package/src/domains/context/working-set/path-index.ts +7 -2
  343. package/src/domains/dispatch/code-step.ts +7 -2
  344. package/src/domains/dispatch/contract.ts +7 -0
  345. package/src/domains/dispatch/execution-role.ts +3 -3
  346. package/src/domains/dispatch/execution-scheduler.ts +204 -79
  347. package/src/domains/dispatch/extension.ts +131 -14
  348. package/src/domains/dispatch/fleet-preflight.ts +18 -3
  349. package/src/domains/dispatch/fleet-run.ts +1 -0
  350. package/src/domains/dispatch/gate-decisions.ts +3 -3
  351. package/src/domains/dispatch/heartbeat.ts +1 -1
  352. package/src/domains/dispatch/host-verification.ts +16 -14
  353. package/src/domains/dispatch/receipt-findings.ts +3 -4
  354. package/src/domains/dispatch/reservation-store.ts +7 -9
  355. package/src/domains/dispatch/route-facts.ts +1 -1
  356. package/src/domains/dispatch/state.ts +3 -3
  357. package/src/domains/dispatch/types.ts +1 -1
  358. package/src/domains/evidence/build.ts +35 -7
  359. package/src/domains/gateway/mcp/client.ts +923 -0
  360. package/src/domains/gateway/mcp/config.ts +456 -0
  361. package/src/domains/gateway/mcp/index.ts +88 -0
  362. package/src/domains/gateway/mcp/protocol.ts +273 -0
  363. package/src/domains/gateway/mcp/trust.ts +263 -0
  364. package/src/domains/lifecycle/doctor.ts +17 -3
  365. package/src/domains/mux/contract.ts +5 -9
  366. package/src/domains/mux/detect.ts +1 -1
  367. package/src/domains/mux/extension.ts +1 -1
  368. package/src/domains/mux/protocol.ts +5 -7
  369. package/src/domains/mux/socket-client.ts +3 -3
  370. package/src/domains/observability/accountability.ts +2 -2
  371. package/src/domains/observability/contract.ts +7 -3
  372. package/src/domains/observability/evidence-index.ts +2 -2
  373. package/src/domains/observability/projection.ts +10 -0
  374. package/src/domains/prompts/compiler.ts +41 -23
  375. package/src/domains/prompts/fragments/identity/docs-routing.md +2 -2
  376. package/src/domains/prompts/fragments/operating/skills.md +12 -10
  377. package/src/domains/providers/index.ts +5 -1
  378. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +42 -10
  379. package/src/domains/providers/probe/http.ts +44 -59
  380. package/src/domains/providers/probe/reasoning.ts +2 -0
  381. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +20 -31
  382. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +9 -4
  383. package/src/domains/safety/action-classifier.ts +17 -1
  384. package/src/domains/safety/call-target.ts +14 -0
  385. package/src/domains/safety/contract.ts +1 -1
  386. package/src/domains/safety/finish-contract.ts +9 -3
  387. package/src/domains/safety/loop-detector.ts +1 -1
  388. package/src/domains/safety/policy-engine.ts +7 -0
  389. package/src/domains/safety/protected-artifacts.ts +3 -4
  390. package/src/domains/safety/rejection-feedback.ts +1 -1
  391. package/src/domains/safety/scope.ts +2 -3
  392. package/src/domains/safety/validation-contract.ts +3 -2
  393. package/src/domains/scheduling/budget.ts +5 -3
  394. package/src/domains/scheduling/contract.ts +2 -1
  395. package/src/domains/scheduling/extension.ts +3 -3
  396. package/src/domains/session/compaction/branch-summary.ts +1 -1
  397. package/src/domains/session/compaction/compact.ts +25 -2
  398. package/src/domains/session/compaction/cut-point.ts +1 -1
  399. package/src/domains/session/compaction/tokens.ts +5 -10
  400. package/src/domains/session/contract.ts +1 -1
  401. package/src/domains/session/cwd-fallback.ts +2 -2
  402. package/src/domains/session/handoff.ts +5 -1
  403. package/src/domains/session/history.ts +6 -0
  404. package/src/domains/session/manager.ts +14 -7
  405. package/src/domains/session/retry.ts +2 -1
  406. package/src/domains/session/session-artifacts.ts +12 -7
  407. package/src/domains/session/task-board.ts +2 -3
  408. package/src/engine/apis/openai-completions.ts +8 -0
  409. package/src/engine/loop-guard.ts +7 -0
  410. package/src/engine/provider-error-body.ts +65 -0
  411. package/src/engine/session.ts +118 -29
  412. package/src/engine/worker-runtime.ts +3 -0
  413. package/src/entry/boot-options.ts +2 -0
  414. package/src/entry/orchestrator.ts +18 -2
  415. package/src/interactive/chat-loop-policy.ts +1 -0
  416. package/src/interactive/chat-loop.ts +8 -4
  417. package/src/interactive/chat-panel.ts +23 -9
  418. package/src/interactive/chat-renderer.ts +33 -11
  419. package/src/interactive/clio-editor.ts +13 -5
  420. package/src/interactive/dispatch-board.ts +70 -30
  421. package/src/interactive/editor-submit.ts +30 -25
  422. package/src/interactive/footer/widgets.ts +12 -9
  423. package/src/interactive/interactive-presentation.ts +2 -3
  424. package/src/interactive/mux-bridge.ts +3 -7
  425. package/src/interactive/overlay-general-openers.ts +14 -7
  426. package/src/interactive/overlays/cwd-fallback.ts +1 -1
  427. package/src/interactive/overlays/library-model.ts +114 -35
  428. package/src/interactive/overlays/library.ts +52 -5
  429. package/src/interactive/overlays/list-overlay.ts +4 -1
  430. package/src/interactive/overlays/session-selector.ts +28 -23
  431. package/src/interactive/overlays/settings.ts +20 -1
  432. package/src/interactive/overlays/tree-selector.ts +47 -10
  433. package/src/interactive/panes-runtime.ts +1 -1
  434. package/src/interactive/renderers/compaction-summary.ts +1 -1
  435. package/src/interactive/renderers/provider-error.ts +118 -0
  436. package/src/interactive/renderers/retry-status.ts +59 -23
  437. package/src/interactive/renderers/tool-execution.ts +67 -7
  438. package/src/interactive/renderers/worker-answer.ts +198 -0
  439. package/src/interactive/renderers/worker-entry.ts +57 -163
  440. package/src/interactive/slash-commands.ts +26 -1
  441. package/src/interactive/slash-spec.ts +2 -2
  442. package/src/interactive/theme/labels.ts +66 -30
  443. package/src/interactive/turn-persistence.ts +6 -1
  444. package/src/interactive/turn-recovery.ts +2 -1
  445. package/src/interactive/turn-runtime.ts +22 -5
  446. package/src/interactive/view/artifacts.ts +3 -2
  447. package/src/interactive/view/view-overlay.ts +96 -20
  448. package/src/tools/agent-tools.ts +21 -7
  449. package/src/tools/artifact.ts +16 -8
  450. package/src/tools/bash.ts +90 -11
  451. package/src/tools/bootstrap.ts +42 -4
  452. package/src/tools/builtin-tool-catalog.ts +72 -7
  453. package/src/tools/context/index.ts +27 -31
  454. package/src/tools/context/library.ts +3 -1
  455. package/src/tools/context/surface.ts +12 -26
  456. package/src/tools/core-bootstrap.ts +72 -1
  457. package/src/tools/data/csv.ts +1189 -0
  458. package/src/tools/data/index.ts +308 -0
  459. package/src/tools/data/json.ts +1799 -0
  460. package/src/tools/data/jsonl.ts +561 -0
  461. package/src/tools/data/shared.ts +452 -0
  462. package/src/tools/edit-diff.ts +0 -7
  463. package/src/tools/edit.ts +115 -18
  464. package/src/tools/file-mutation-queue.ts +114 -4
  465. package/src/tools/find.ts +108 -29
  466. package/src/tools/gateway/caps.ts +11 -0
  467. package/src/tools/gateway/clio-context-surface.ts +41 -0
  468. package/src/tools/gateway/clio-context-tools.ts +77 -0
  469. package/src/tools/gateway/data-surface.ts +138 -0
  470. package/src/tools/gateway/data-tool.ts +201 -0
  471. package/src/tools/gateway/index.ts +342 -0
  472. package/src/tools/gateway/mcp-capabilities.ts +469 -0
  473. package/src/tools/grep.ts +88 -33
  474. package/src/tools/harness-extensions.ts +3 -0
  475. package/src/tools/ignore-policy.ts +7 -5
  476. package/src/tools/ls.ts +111 -20
  477. package/src/tools/policy.ts +35 -13
  478. package/src/tools/presentation.ts +6 -0
  479. package/src/tools/read.ts +634 -156
  480. package/src/tools/registry.ts +94 -6
  481. package/src/tools/run-script.ts +997 -0
  482. package/src/tools/spawn-hygiene.ts +157 -5
  483. package/src/tools/surface.ts +163 -0
  484. package/src/tools/verify/authoring.ts +8 -3
  485. package/src/tools/verify/catalog.ts +14 -1
  486. package/src/tools/verify/numeric.ts +367 -59
  487. package/src/tools/verify/perf.ts +171 -9
  488. package/src/tools/verify/scripts.ts +178 -26
  489. package/src/tools/web-fetch-surface.ts +22 -0
  490. package/src/tools/web-fetch.ts +26 -1
  491. package/src/tools/write.ts +45 -19
  492. package/dist/chunk-B6UM3OZC.js +0 -15
  493. package/dist/chunk-GJ24ODAX.js +0 -196
  494. package/dist/chunk-LZCCBLRC.js +0 -57
  495. package/dist/web/client/assets/architecture-7GRP2DOG-EGozsYEO.js +0 -1
  496. package/dist/web/client/assets/architectureDiagram-5GKGNRK7-z9FokbJl.js +0 -36
  497. package/dist/web/client/assets/channel-CiUWbHN9.js +0 -1
  498. package/dist/web/client/assets/chunk-DU6HZSFF-C5USiOAy.js +0 -125
  499. package/dist/web/client/assets/classDiagram-ZZMXUADV-CMODalmo.js +0 -1
  500. package/dist/web/client/assets/classDiagram-v2-VYDZK3BY-CMODalmo.js +0 -1
  501. package/dist/web/client/assets/cose-bilkent-JH36ORCC-RbQL23qS.js +0 -1
  502. package/dist/web/client/assets/dist-D0_q-A2b.js +0 -1
  503. package/dist/web/client/assets/eventmodeling-NTZA5JFV-DjVq8wKU.js +0 -1
  504. package/dist/web/client/assets/flowDiagram-HODETNUW-BUiCBGXt.js +0 -1
  505. package/dist/web/client/assets/ganttDiagram-EL5Y4UJY-B2lb8lvF.js +0 -292
  506. package/dist/web/client/assets/index-FSIC94pi.js +0 -83
  507. package/dist/web/client/assets/prism-core-BngLtbUd.js +0 -1
  508. package/dist/web/client/assets/railroad-abnf-HS5TGJTU-BcW01NZG.js +0 -1
  509. package/dist/web/client/assets/railroad-ebnf-LZEXJU2U-B7YCEDDd.js +0 -1
  510. package/dist/web/client/assets/railroad-peg-WCYAUIDC-DjNZbSTZ.js +0 -1
  511. package/dist/web/client/assets/src-yeptRXpt.js +0 -1
  512. package/dist/web/client/assets/stateDiagram-v2-MP3YSRHH-C7QtHeK4.js +0 -1
  513. package/dist/web/client/assets/swimlanesDiagram-VR7AAH4N-CBZlPFky.js +0 -8
@@ -1,33 +1,58 @@
1
1
  import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
+ import {
3
+ clioDocsToolSurface,
4
+ clioLibraryToolSurface
5
+ } from "./chunk-ACCECAH4.js";
2
6
  import {
3
7
  contextToolSurface
4
- } from "./chunk-LZCCBLRC.js";
8
+ } from "./chunk-HBJQQTKM.js";
5
9
  import {
6
10
  resolveFdBinary,
7
11
  resolveRgBinary
8
12
  } from "./chunk-JABWPKFY.js";
9
13
  import {
10
- webFetchToolSurface
11
- } from "./chunk-ICYIUNO4.js";
14
+ runScriptToolSurface
15
+ } from "./chunk-HCVKSGNZ.js";
16
+ import {
17
+ webFetchToolSurface,
18
+ webReadToolSurface
19
+ } from "./chunk-PNJE7JPS.js";
12
20
  import {
13
21
  verifyToolSurface
14
- } from "./chunk-2U2U5POA.js";
22
+ } from "./chunk-L4RUTLRL.js";
23
+ import {
24
+ dataToolSurface,
25
+ prepareDataAdmissionArguments
26
+ } from "./chunk-PPIFDSBK.js";
15
27
  import {
16
28
  codeNavToolSurface
17
- } from "./chunk-WTZVSMQZ.js";
29
+ } from "./chunk-TCUOGIL2.js";
30
+ import {
31
+ McpError,
32
+ createMcpStdioClient,
33
+ resolveMcpServers
34
+ } from "./chunk-PP23PZEV.js";
35
+ import {
36
+ incompleteInstallationAdvice
37
+ } from "./chunk-3A3CUMKA.js";
18
38
  import {
19
39
  RESPONSE_SCHEMA_RUNTIME_ID,
20
40
  bashTool,
21
41
  builtin,
22
42
  createAskUserTool,
43
+ nestedBlockedResult,
44
+ nestedExecutedResult,
23
45
  readMaxBytes,
24
46
  readTool,
25
47
  renderAgentLedger
26
- } from "./chunk-MWMW53WW.js";
48
+ } from "./chunk-4ATBOFRP.js";
27
49
  import {
28
50
  createTaskBoardStore,
29
51
  taskBoardCounts
30
- } from "./chunk-WJWNZXRM.js";
52
+ } from "./chunk-KOAHT3N2.js";
53
+ import {
54
+ hashToolCall
55
+ } from "./chunk-AY4XE6GN.js";
31
56
  import {
32
57
  isLoadableExtension
33
58
  } from "./chunk-OH3YYA3B.js";
@@ -37,18 +62,34 @@ import {
37
62
  parseExtensionManifest,
38
63
  resolveExtensionEntrypoint
39
64
  } from "./chunk-6TKOFFGE.js";
65
+ import {
66
+ gatewayCapabilityKind,
67
+ toolSpecPlacement,
68
+ withGatewayForCapabilities
69
+ } from "./chunk-2JBZYUPY.js";
40
70
  import {
41
71
  describeCallAction
42
- } from "./chunk-2GPGAE5W.js";
72
+ } from "./chunk-XNIZBOFV.js";
43
73
  import {
44
74
  applyToolProfile
45
- } from "./chunk-GEVQNST4.js";
75
+ } from "./chunk-XZWSP67B.js";
76
+ import {
77
+ BusChannels
78
+ } from "./chunk-W5RM7YAH.js";
46
79
  import {
47
80
  networkToolsDisabled
48
81
  } from "./chunk-SL2RDHKO.js";
49
82
  import {
50
- gitTool
51
- } from "./chunk-MEHBW2SP.js";
83
+ DATA_OBSERVATION_SELF_CAP_BYTES,
84
+ GATEWAY_FIND_SELF_CAP_BYTES
85
+ } from "./chunk-JURMQWVJ.js";
86
+ import {
87
+ compileGlobRegex,
88
+ fallbackIgnoredDirs,
89
+ fdIgnoreArgs,
90
+ normalizeGlobInput,
91
+ rgIgnoreArgs
92
+ } from "./chunk-IZZI3SKQ.js";
52
93
  import {
53
94
  OBSERVATION_POLICY_SLACK_BYTES,
54
95
  OBSERVE_SELF_CAPS,
@@ -59,61 +100,51 @@ import {
59
100
  observationBudgetExhausted,
60
101
  releaseObservation,
61
102
  reserveObservation,
62
- toolResultContextText,
103
+ toolResultContextText
104
+ } from "./chunk-ZKQ63XZ5.js";
105
+ import {
106
+ createSafetyPolicyEngine,
107
+ formatModelRejection
108
+ } from "./chunk-IC7JXLOM.js";
109
+ import {
63
110
  truncateHead
64
- } from "./chunk-526ABLHD.js";
111
+ } from "./chunk-GHXGMFQ5.js";
65
112
  import {
66
113
  GREP_MAX_LINE_LENGTH,
67
114
  truncateLine
68
115
  } from "./chunk-KECNXMDE.js";
69
116
  import {
70
- compileGlobRegex,
71
- fallbackIgnoredDirs,
72
- fdIgnoreArgs,
73
- normalizeGlobInput,
74
117
  resolveReadPath,
75
118
  resolveToCwd,
76
- rgIgnoreArgs,
77
119
  toPosixPath
78
- } from "./chunk-V552WL53.js";
79
- import {
80
- parseAgentLedgerBody
81
- } from "./chunk-VQD7NH7Z.js";
82
- import {
83
- hashToolCall
84
- } from "./chunk-MUXNDNMZ.js";
85
- import {
86
- formatModelRejection
87
- } from "./chunk-RAUOKUZV.js";
88
- import {
89
- StringEnum,
90
- engineStreamSimple
91
- } from "./chunk-QLAGXTTV.js";
92
- import {
93
- byteLength,
94
- truncateUtf8
95
- } from "./chunk-MYI32N4R.js";
96
- import {
97
- BusChannels
98
- } from "./chunk-W5RM7YAH.js";
120
+ } from "./chunk-KP5BPXR3.js";
99
121
  import {
100
122
  extensionContentDigestWithCapture
101
123
  } from "./chunk-XW2UZICE.js";
102
124
  import {
103
125
  classify
104
- } from "./chunk-YXFKLMX2.js";
126
+ } from "./chunk-H2WSCZZT.js";
105
127
  import {
106
128
  protectArtifact,
107
129
  protectedArtifactMutationBlockReason
108
- } from "./chunk-E2NSPYLC.js";
130
+ } from "./chunk-SQBT46OF.js";
131
+ import {
132
+ gitTool
133
+ } from "./chunk-YQYINEJI.js";
134
+ import {
135
+ byteLength,
136
+ truncateUtf8
137
+ } from "./chunk-MYI32N4R.js";
109
138
  import {
110
139
  CLIO_ARTIFACT_DIR,
111
140
  artifactDefaultPath
112
141
  } from "./chunk-NTTS3O53.js";
113
142
  import {
114
143
  ToolNames,
115
- isBuiltinToolName
116
- } from "./chunk-WLHX3MM5.js";
144
+ isBuiltinToolName,
145
+ isMcpToolName,
146
+ mcpToolName
147
+ } from "./chunk-V4XJWDHR.js";
117
148
  import {
118
149
  GUARDRAIL_DEFAULTS,
119
150
  resolveGuardrail,
@@ -126,16 +157,21 @@ import {
126
157
  require_dist
127
158
  } from "./chunk-B5GGDIHT.js";
128
159
  import {
129
- incompleteInstallationAdvice
130
- } from "./chunk-3A3CUMKA.js";
160
+ StringEnum,
161
+ engineStreamSimple,
162
+ validateEngineToolArguments
163
+ } from "./chunk-UIU7MGZ3.js";
164
+ import {
165
+ parseAgentLedgerBody
166
+ } from "./chunk-VQD7NH7Z.js";
131
167
  import {
132
168
  buildSafeToolEnv,
133
169
  runCommandVector
134
- } from "./chunk-GJ24ODAX.js";
170
+ } from "./chunk-XTQ7MEJG.js";
135
171
  import {
136
172
  decisionRef,
137
173
  skillActivationFromToolDetails
138
- } from "./chunk-6R5MBXBJ.js";
174
+ } from "./chunk-NBPA5QM6.js";
139
175
  import {
140
176
  assertSafeId
141
177
  } from "./chunk-UHW5V7HC.js";
@@ -652,6 +688,7 @@ function createLoopGuardRegistration(options) {
652
688
  return true;
653
689
  },
654
690
  evaluate(input) {
691
+ if (input.metadata?.nested === true) return [];
655
692
  if (input.hook === "after_tool") {
656
693
  recordSuccessfulResult(input);
657
694
  recordResultForStagnation(input);
@@ -1040,7 +1077,7 @@ function resolveAgentTools(input) {
1040
1077
  const specs = [];
1041
1078
  for (const name of effectiveToolNames(input)) {
1042
1079
  const spec = input.registry.get(name);
1043
- if (spec) specs.push(spec);
1080
+ if (spec && toolSpecPlacement(spec) === "direct") specs.push(spec);
1044
1081
  }
1045
1082
  specs.sort((a, b) => a.name.localeCompare(b.name));
1046
1083
  return specs.map((spec) => toAgentTool(spec, input.registry, input.telemetry, input.invokeOptions));
@@ -1050,7 +1087,9 @@ function effectiveToolNames(input) {
1050
1087
  ...input.agentId !== void 0 ? { agentId: input.agentId } : {},
1051
1088
  ...input.task !== void 0 ? { task: input.task } : {}
1052
1089
  };
1053
- const toolIds = applyToolProfile(input.registry.listRegistered(), input.toolProfile, profileContext);
1090
+ const gatewayNames = input.registry.listGateway().map((spec) => spec.name);
1091
+ const registered = [...input.registry.listRegistered(), ...gatewayNames];
1092
+ const toolIds = applyToolProfile(registered, input.toolProfile, profileContext);
1054
1093
  const allowed = input.allowedTools ? new Set(input.allowedTools) : null;
1055
1094
  const includeInteractiveTools = input.includeInteractiveTools !== false;
1056
1095
  const names = [];
@@ -1059,7 +1098,8 @@ function effectiveToolNames(input) {
1059
1098
  if (allowed && !allowed.has(name)) continue;
1060
1099
  names.push(name);
1061
1100
  }
1062
- return names;
1101
+ if (input.registry.get(ToolNames.Gateway) === void 0) return names;
1102
+ return withGatewayForCapabilities(names);
1063
1103
  }
1064
1104
  function resolveSessionTools(runtime, toolRegistry, invokeOptions, telemetry) {
1065
1105
  if (!toolRegistry || runtime.runtimeResolution.capabilityDecisions.tools !== true) return [];
@@ -1258,6 +1298,558 @@ function patchWorkerRequestPayload(payload, model, options) {
1258
1298
  return changed ? patched : void 0;
1259
1299
  }
1260
1300
 
1301
+ // src/tools/gateway/mcp-capabilities.ts
1302
+ init_esm_shims();
1303
+ var MCP_RESULT_CONTEXT_BYTES = 16 * 1024;
1304
+ function mcpTrustRemedy(id) {
1305
+ return { remedy: `clio-coder mcp trust ${id}`, interactiveRemedy: `/mcp trust ${id}` };
1306
+ }
1307
+ function quoted(value) {
1308
+ return `'${value.replaceAll("'", "'\\''")}'`;
1309
+ }
1310
+ function errorMessage2(error) {
1311
+ if (error instanceof McpError) return `${error.message} (${error.code})`;
1312
+ return error instanceof Error ? error.message : String(error);
1313
+ }
1314
+ function imageBlocks(result) {
1315
+ const images = [];
1316
+ for (const block of result.content) {
1317
+ if (block.type !== "image") continue;
1318
+ const { data, mimeType } = block;
1319
+ if (typeof data === "string" && typeof mimeType === "string" && mimeType.startsWith("image/")) {
1320
+ images.push({ type: "image", data, mimeType });
1321
+ }
1322
+ }
1323
+ return images;
1324
+ }
1325
+ function createMcpCapabilitySource(options) {
1326
+ const registry = options.registry;
1327
+ const clientFactory = options.clientFactory ?? createMcpStdioClient;
1328
+ let states = null;
1329
+ let diagnostics = [];
1330
+ let exitHookInstalled = false;
1331
+ let closed = false;
1332
+ let closePromise = null;
1333
+ const teardowns = [];
1334
+ const killLaunchedOnExit = () => {
1335
+ for (const state of states?.values() ?? []) state.client?.killOwnedOnExit();
1336
+ };
1337
+ const installExitHook = () => {
1338
+ if (exitHookInstalled) return;
1339
+ exitHookInstalled = true;
1340
+ process.once("exit", killLaunchedOnExit);
1341
+ };
1342
+ const closeClient = (state) => {
1343
+ if (state.closing !== null) return state.closing;
1344
+ const client = state.client;
1345
+ if (client === null) return void 0;
1346
+ state.closing = client.close().then((outcome) => {
1347
+ teardowns.push({ id: state.declaration.id, outcome });
1348
+ state.client = null;
1349
+ if (!outcome.complete) {
1350
+ process.stderr.write(
1351
+ `[gateway] mcp server ${state.declaration.id}: process group ${outcome.pgid} survived SIGKILL for ${outcome.boundMs}ms; recorded, not signalled again
1352
+ `
1353
+ );
1354
+ }
1355
+ return outcome;
1356
+ });
1357
+ return state.closing;
1358
+ };
1359
+ const resolveStates = () => {
1360
+ if (states !== null) return states;
1361
+ const resolved = resolveMcpServers({
1362
+ cwd: options.cwd,
1363
+ ...options.configDir ? { configDir: options.configDir } : {}
1364
+ });
1365
+ diagnostics = [
1366
+ ...resolved.diagnostics.map((entry) => `${entry.scope} ${entry.path}: ${entry.message}`),
1367
+ ...resolved.trustDiagnostics
1368
+ ];
1369
+ states = new Map(
1370
+ resolved.servers.map((declaration) => [
1371
+ declaration.id,
1372
+ {
1373
+ declaration,
1374
+ client: null,
1375
+ connecting: null,
1376
+ closing: null,
1377
+ tools: [],
1378
+ truncated: false,
1379
+ registered: [],
1380
+ unregistrable: [],
1381
+ failure: null
1382
+ }
1383
+ ])
1384
+ );
1385
+ return states;
1386
+ };
1387
+ const makeSpec = (state, tool, name) => {
1388
+ const { declaration } = state;
1389
+ const trustClass = declaration.trust.actionClass;
1390
+ const timeoutMs = declaration.timeoutMs ?? options.requestTimeoutMs;
1391
+ const spec = {
1392
+ name,
1393
+ description: `${tool.description.trim().length > 0 ? tool.description.trim() : `MCP tool ${tool.name}`}
1394
+ Local MCP server ${declaration.id} (${declaration.scope} scope), trusted with action class ${trustClass}; launching it does not sandbox it.`,
1395
+ parameters: tool.inputSchema,
1396
+ baseActionClass: trustClass,
1397
+ executionMode: "sequential",
1398
+ placement: "gateway",
1399
+ sourceInfo: { path: declaration.path, scope: "domain" },
1400
+ metadata: {
1401
+ objective: tool.description.trim().length > 0 ? tool.description.trim() : `MCP tool ${tool.name}`,
1402
+ uiLabel: `${declaration.id}/${tool.name}`,
1403
+ retrySafety: "unknown",
1404
+ // The session cap alone (64 KiB) let one search result spend a sixth
1405
+ // of a 131k window. Bound MCP output like the built-in observation
1406
+ // tools; the full text is offloaded and stays readable by path.
1407
+ resultSizePolicy: {
1408
+ kind: "bounded",
1409
+ maxBytes: MCP_RESULT_CONTEXT_BYTES,
1410
+ followUpHint: "Call again with a narrower query or the server's own limit/page arguments, or read the offloaded file in windows."
1411
+ },
1412
+ costLatency: "local_slow"
1413
+ },
1414
+ async run(args, invokeOptions) {
1415
+ const client = state.client;
1416
+ if (client === null || client.state().status !== "ready") {
1417
+ return {
1418
+ kind: "error",
1419
+ message: `${name}: mcp server ${declaration.id} is not connected${state.failure ? ` (${state.failure})` : ""}; a server that exited stays closed for this session`
1420
+ };
1421
+ }
1422
+ try {
1423
+ const result = await client.callTool(tool.name, args, {
1424
+ ...invokeOptions?.signal ? { signal: invokeOptions.signal } : {},
1425
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
1426
+ });
1427
+ const details = {
1428
+ server: declaration.id,
1429
+ tool: tool.name,
1430
+ contentTypes: result.content.map((block) => block.type),
1431
+ textTruncated: result.textTruncated,
1432
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {},
1433
+ ...result.structuredContentJson !== void 0 ? { structuredContentJson: result.structuredContentJson } : {}
1434
+ };
1435
+ if (result.isError) return { kind: "error", message: `${name}: ${result.text || "tool error"}`, details };
1436
+ const images = imageBlocks(result);
1437
+ return { kind: "ok", output: result.text, details, ...images.length > 0 ? { images } : {} };
1438
+ } catch (error) {
1439
+ if (error instanceof McpError && (error.code === "closed" || error.code === "spawn")) {
1440
+ state.failure = errorMessage2(error);
1441
+ }
1442
+ return { kind: "error", message: `${name}: ${errorMessage2(error)}` };
1443
+ }
1444
+ }
1445
+ };
1446
+ if (trustClass === "execute") {
1447
+ spec.safetyCall = () => ({
1448
+ tool: "bash",
1449
+ args: { command: [declaration.command, ...declaration.args].map(quoted).join(" ") }
1450
+ });
1451
+ }
1452
+ return spec;
1453
+ };
1454
+ const connect = async (state) => {
1455
+ if (state.connecting !== null) return state.connecting;
1456
+ if (closed || state.client !== null || state.failure !== null) return;
1457
+ if (state.declaration.trust.status !== "trusted") return;
1458
+ state.connecting = (async () => {
1459
+ const { declaration } = state;
1460
+ let client = null;
1461
+ try {
1462
+ client = clientFactory(
1463
+ {
1464
+ id: declaration.id,
1465
+ command: declaration.command,
1466
+ args: declaration.args,
1467
+ cwd: declaration.cwd,
1468
+ env: declaration.env
1469
+ },
1470
+ {
1471
+ workspaceRoot: declaration.cwdRoot,
1472
+ ...declaration.timeoutMs !== null ? { requestTimeoutMs: declaration.timeoutMs } : options.requestTimeoutMs !== void 0 ? { requestTimeoutMs: options.requestTimeoutMs } : {}
1473
+ }
1474
+ );
1475
+ state.client = client;
1476
+ installExitHook();
1477
+ await client.initialize();
1478
+ const listing = await client.listTools();
1479
+ state.tools = listing.tools;
1480
+ state.truncated = listing.truncated;
1481
+ for (const tool of listing.tools) {
1482
+ const name = mcpToolName(declaration.id, tool.name);
1483
+ if (name === null || ownerOf(name) !== state || registry.get(name) !== void 0) {
1484
+ state.unregistrable.push(tool.name);
1485
+ continue;
1486
+ }
1487
+ registry.register(makeSpec(state, tool, name));
1488
+ state.registered.push(name);
1489
+ }
1490
+ } catch (error) {
1491
+ state.failure = errorMessage2(error);
1492
+ await closeClient(state);
1493
+ } finally {
1494
+ state.connecting = null;
1495
+ }
1496
+ })();
1497
+ return state.connecting;
1498
+ };
1499
+ const discover = async (state, signal) => {
1500
+ const aborted = () => new McpError("aborted", "MCP discovery aborted");
1501
+ if (signal?.aborted) throw aborted();
1502
+ let cleanup;
1503
+ const onAbort = () => {
1504
+ state.failure = errorMessage2(aborted());
1505
+ cleanup = closeClient(state);
1506
+ };
1507
+ signal?.addEventListener("abort", onAbort, { once: true });
1508
+ try {
1509
+ await connect(state);
1510
+ await cleanup;
1511
+ if (signal?.aborted) throw aborted();
1512
+ } finally {
1513
+ signal?.removeEventListener("abort", onAbort);
1514
+ }
1515
+ };
1516
+ const listingFor = (state) => {
1517
+ const { declaration } = state;
1518
+ const base = { id: declaration.id, scope: declaration.scope, actionClass: declaration.trust.actionClass };
1519
+ if (declaration.trust.status !== "trusted") {
1520
+ return {
1521
+ ...base,
1522
+ status: declaration.trust.status,
1523
+ reason: declaration.trust.reason,
1524
+ ...mcpTrustRemedy(declaration.id)
1525
+ };
1526
+ }
1527
+ if (state.failure !== null) {
1528
+ return { ...base, status: "failed", reason: state.failure };
1529
+ }
1530
+ if (state.client !== null) {
1531
+ return {
1532
+ ...base,
1533
+ status: "connected",
1534
+ tools: [...state.registered],
1535
+ ...state.truncated ? { truncated: true } : {},
1536
+ ...state.unregistrable.length > 0 ? { unregistrable: [...state.unregistrable] } : {}
1537
+ };
1538
+ }
1539
+ return { ...base, status: "trusted" };
1540
+ };
1541
+ const ownerOf = (name) => {
1542
+ let owner = null;
1543
+ for (const state of resolveStates().values()) {
1544
+ if (name.startsWith(`mcp_${state.declaration.id}__`) && (owner === null || state.declaration.id.length > owner.declaration.id.length)) {
1545
+ owner = state;
1546
+ }
1547
+ }
1548
+ return owner;
1549
+ };
1550
+ return {
1551
+ async list(invokeOptions) {
1552
+ if (invokeOptions?.signal?.aborted) throw new McpError("aborted", "MCP discovery aborted");
1553
+ const all = [...resolveStates().values()];
1554
+ const results = await Promise.allSettled(all.map((state) => discover(state, invokeOptions?.signal)));
1555
+ for (const result of results) {
1556
+ if (result.status === "rejected") throw result.reason;
1557
+ }
1558
+ return { servers: all.map(listingFor), diagnostics: [...diagnostics] };
1559
+ },
1560
+ async ensure(name, invokeOptions) {
1561
+ if (invokeOptions?.signal?.aborted) throw new McpError("aborted", "MCP discovery aborted");
1562
+ const owner = ownerOf(name);
1563
+ if (owner === null) return { spec: null, reason: `no declared MCP server owns ${name}` };
1564
+ const trust = owner.declaration.trust;
1565
+ if (trust.status !== "trusted") {
1566
+ const remedy = mcpTrustRemedy(owner.declaration.id);
1567
+ return {
1568
+ spec: null,
1569
+ reason: `mcp server ${owner.declaration.id} is ${trust.status} (${trust.reason}); it is never launched until trusted: run ${remedy.remedy} (${remedy.interactiveRemedy} in an interactive session)`
1570
+ };
1571
+ }
1572
+ await discover(owner, invokeOptions?.signal);
1573
+ const spec = registry.get(name);
1574
+ if (spec !== void 0 && owner.registered.includes(spec.name)) return { spec };
1575
+ return {
1576
+ spec: null,
1577
+ reason: owner.failure !== null ? `mcp server ${owner.declaration.id} failed: ${owner.failure}` : `mcp server ${owner.declaration.id} offers no tool named ${name.slice(`mcp_${owner.declaration.id}__`.length)}`
1578
+ };
1579
+ },
1580
+ authorityNote(name) {
1581
+ const owner = ownerOf(name);
1582
+ if (owner === null) return null;
1583
+ const { declaration } = owner;
1584
+ return `Local stdio MCP server ${declaration.id} (${declaration.scope} scope, declared in ${declaration.path}), trusted with action class ${declaration.trust.actionClass}: ${declaration.trust.actionClass === "unknown" ? "every call asks for approval and read-only denies it" : declaration.trust.actionClass === "execute" ? "calls are admitted like an unrecognized shell command" : "calls run as reads at every autonomy level"}. Launching the server does not sandbox it.`;
1585
+ },
1586
+ connectedIds() {
1587
+ return [...states?.values() ?? []].filter((state) => state.client !== null).map((state) => state.declaration.id);
1588
+ },
1589
+ teardownReports() {
1590
+ return [...teardowns];
1591
+ },
1592
+ close() {
1593
+ if (closePromise !== null) return closePromise;
1594
+ closed = true;
1595
+ closePromise = (async () => {
1596
+ const all = [...states?.values() ?? []];
1597
+ await Promise.all(all.map(closeClient));
1598
+ await Promise.all(all.map((state) => state.connecting));
1599
+ const report = { incomplete: [] };
1600
+ for (const { id, outcome } of teardowns) {
1601
+ if (!outcome.complete) report.incomplete.push({ id, pgid: outcome.pgid, boundMs: outcome.boundMs });
1602
+ }
1603
+ if (exitHookInstalled) {
1604
+ process.removeListener("exit", killLaunchedOnExit);
1605
+ exitHookInstalled = false;
1606
+ }
1607
+ return report;
1608
+ })();
1609
+ return closePromise;
1610
+ }
1611
+ };
1612
+ }
1613
+
1614
+ // src/tools/gateway/index.ts
1615
+ init_esm_shims();
1616
+ var GATEWAY_OPS = ["find", "describe", "call"];
1617
+ var GATEWAY_FIND_MAX_ENTRIES = 300;
1618
+ var DESCRIPTION = `Reach secondary capabilities on demand. op="find" lists them as JSON {name, kind: builtin|extension|mcp, description, actionClass} (query filters by name or description); op="describe" returns one capability's full description, JSON parameter schema, and authority notes; op="call" runs one with args under the same admission as a direct tool: its own action class, approval, and evidence apply and the result is the capability's result. Capabilities: artifact (terminal plan/review/report documents), web_read (GET-only web reading), web_fetch (full HTTP requests), git (read-only status/diff/log), evidence, credential_present, clio_docs, clio_library, data (CSV/TSV, JSON, JSONL inspection), installed extension commands, and trusted local MCP servers.`;
1619
+ var gatewayToolSurface = {
1620
+ name: ToolNames.Gateway,
1621
+ description: DESCRIPTION,
1622
+ parameters: typebox_exports.Object({
1623
+ op: StringEnum(GATEWAY_OPS, { description: "find, describe, or call." }),
1624
+ capability: typebox_exports.Optional(typebox_exports.String({ description: "describe and call: the capability name from find." })),
1625
+ query: typebox_exports.Optional(typebox_exports.String({ description: "find: case-insensitive filter over names and descriptions." })),
1626
+ args: typebox_exports.Optional(
1627
+ typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
1628
+ description: "call: the capability's arguments, matching its describe schema."
1629
+ })
1630
+ )
1631
+ }),
1632
+ baseActionClass: "read",
1633
+ executionMode: "sequential"
1634
+ };
1635
+ function isRecord3(value) {
1636
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1637
+ }
1638
+ function prepareGatewayArguments(args) {
1639
+ const out = { ...args };
1640
+ if (typeof out.capability !== "string") {
1641
+ const alias = out.name ?? out.tool;
1642
+ if (typeof alias === "string") out.capability = alias;
1643
+ }
1644
+ if (typeof out.args === "string") {
1645
+ try {
1646
+ const parsed = JSON.parse(out.args);
1647
+ if (isRecord3(parsed)) out.args = parsed;
1648
+ } catch {
1649
+ }
1650
+ }
1651
+ if (typeof out.op !== "string") {
1652
+ if (typeof out.capability === "string" && out.args !== void 0) out.op = "call";
1653
+ else if (typeof out.capability === "string") out.op = "describe";
1654
+ else out.op = "find";
1655
+ }
1656
+ return out;
1657
+ }
1658
+ function firstSentence(text) {
1659
+ const line = text.split("\n")[0]?.trim() ?? "";
1660
+ const match = /^(.*?[.!?])(?:\s|$)/u.exec(line);
1661
+ return (match?.[1] ?? line).slice(0, 240);
1662
+ }
1663
+ function autonomyNote(actionClass) {
1664
+ switch (actionClass) {
1665
+ case "read":
1666
+ return "Read class: runs at every autonomy level.";
1667
+ case "write":
1668
+ return "Write class: parks for operator approval at suggest, runs at auto-edit and full-auto, and is denied at read-only.";
1669
+ case "execute":
1670
+ return "Execute class: an unrecognized command asks at auto-edit, runs at full-auto, and is denied at read-only.";
1671
+ case "dispatch":
1672
+ return "Dispatch class: plan-scale calls ask below full-auto; denied at read-only.";
1673
+ case "unknown":
1674
+ return "No action class was declared: every call asks for approval and read-only denies it.";
1675
+ default:
1676
+ return `Action class ${actionClass}.`;
1677
+ }
1678
+ }
1679
+ function createGatewayTool(deps) {
1680
+ const { registry } = deps;
1681
+ const allowedSet = (options) => options?.allowedTools ? new Set(options.allowedTools) : null;
1682
+ const resolveCapability = async (name, options) => {
1683
+ let spec = registry.get(name);
1684
+ if (spec === void 0 && isMcpToolName(name) && deps.mcp) {
1685
+ const ensured = await deps.mcp.ensure(name, options?.signal ? { signal: options.signal } : {});
1686
+ if (ensured.spec === null)
1687
+ return { spec: null, message: `gateway: ${ensured.reason ?? `unknown capability ${name}`}` };
1688
+ spec = ensured.spec;
1689
+ }
1690
+ if (spec === void 0) {
1691
+ return {
1692
+ spec: null,
1693
+ message: `gateway: unknown capability "${name}"; run gateway(op="find") to list the capabilities this session offers`
1694
+ };
1695
+ }
1696
+ if (toolSpecPlacement(spec) === "direct") {
1697
+ return {
1698
+ spec: null,
1699
+ message: `gateway: "${name}" is a direct tool with an attached schema; call it directly, not through the gateway`
1700
+ };
1701
+ }
1702
+ return { spec };
1703
+ };
1704
+ const outsideSurface = (name, allowed) => ({
1705
+ kind: "error",
1706
+ message: `gateway: capability "${name}" is not on this run's admitted tool surface (${[...allowed].sort().join(", ")}); the recipe did not declare it, so the gateway refuses to call it`
1707
+ });
1708
+ const runFind = async (args, options) => {
1709
+ const query = typeof args.query === "string" ? args.query.trim().toLowerCase() : "";
1710
+ const allowed = allowedSet(options);
1711
+ const reservation = reserveObservation(GATEWAY_FIND_SELF_CAP_BYTES, options);
1712
+ if (reservation.exhausted) {
1713
+ return observationBudgetExhausted({
1714
+ tool: ToolNames.Gateway,
1715
+ unit: "entries",
1716
+ reservation,
1717
+ subject: query.length > 0 ? `find query=${query}` : "find",
1718
+ hint: "Continue in a follow-up turn."
1719
+ });
1720
+ }
1721
+ commitObservationReservation(reservation);
1722
+ try {
1723
+ let servers = [];
1724
+ let diagnostics = [];
1725
+ if (deps.mcp && allowed === null) {
1726
+ const listing = await deps.mcp.list(options?.signal ? { signal: options.signal } : {});
1727
+ servers = listing.servers;
1728
+ diagnostics = listing.diagnostics;
1729
+ }
1730
+ const entries = registry.listGateway().filter((spec) => allowed === null || allowed.has(spec.name)).map((spec) => ({
1731
+ name: spec.name,
1732
+ kind: gatewayCapabilityKind(spec.name),
1733
+ description: firstSentence(spec.description),
1734
+ actionClass: spec.baseActionClass
1735
+ })).filter(
1736
+ (entry) => query.length === 0 || entry.name.toLowerCase().includes(query) || entry.description.toLowerCase().includes(query)
1737
+ ).sort((left, right) => left.name.localeCompare(right.name));
1738
+ const total = entries.length;
1739
+ const shown = entries.slice(0, GATEWAY_FIND_MAX_ENTRIES);
1740
+ const truncated = shown.length < total;
1741
+ const payload = {
1742
+ capabilities: shown,
1743
+ count: shown.length,
1744
+ total,
1745
+ ...servers.length > 0 ? { servers } : {},
1746
+ ...diagnostics.length > 0 ? { diagnostics } : {},
1747
+ ...truncated ? { note: "listing truncated; narrow it with query" } : {}
1748
+ };
1749
+ return finalizeObservation({
1750
+ tool: ToolNames.Gateway,
1751
+ unit: "entries",
1752
+ format: "json",
1753
+ output: JSON.stringify(payload),
1754
+ shownCount: shown.length,
1755
+ totalCount: total,
1756
+ truncated,
1757
+ ...truncated ? { next: "query=<narrower terms>" } : {},
1758
+ details: { op: "find", count: shown.length, total, servers: servers.length },
1759
+ reservation,
1760
+ ...options ? { options } : {}
1761
+ });
1762
+ } catch (error) {
1763
+ releaseObservation(reservation);
1764
+ return { kind: "error", message: `gateway: find failed: ${error instanceof Error ? error.message : String(error)}` };
1765
+ }
1766
+ };
1767
+ const runDescribe = async (args, options) => {
1768
+ const name = typeof args.capability === "string" ? args.capability.trim() : "";
1769
+ if (name.length === 0) return { kind: "error", message: 'gateway: op="describe" requires capability' };
1770
+ const allowed = allowedSet(options);
1771
+ if (allowed !== null && !allowed.has(name)) return outsideSurface(name, allowed);
1772
+ const resolved = await resolveCapability(name, options);
1773
+ if (resolved.spec === null) return { kind: "error", message: resolved.message };
1774
+ const spec = resolved.spec;
1775
+ const kind = gatewayCapabilityKind(spec.name);
1776
+ const authority = [
1777
+ `Runs through the same admission as a direct call, under its own name and action class (${spec.baseActionClass}); the approval overlay, audit row, and ledger record name ${spec.name}, not the gateway.`,
1778
+ autonomyNote(spec.baseActionClass)
1779
+ ];
1780
+ if (spec.name === ToolNames.WebFetch) {
1781
+ authority.push(
1782
+ "A non-GET method or a body is an outward action and asks at suggest and auto-edit; use web_read when a plain GET is enough."
1783
+ );
1784
+ }
1785
+ if (spec.name === ToolNames.Artifact) {
1786
+ authority.push("Terminal: writing the artifact completes the turn, so close the task board before calling it.");
1787
+ }
1788
+ if (kind === "extension") {
1789
+ authority.push(
1790
+ `Harness extension${spec.sourceInfo?.extension ? ` ${spec.sourceInfo.extension.contentDigest.slice(0, 12)}` : ""}: an installed command run with JSON input on the workspace; its argv is classified like a bash command, and running it does not sandbox it.`
1791
+ );
1792
+ }
1793
+ const mcpNote = kind === "mcp" ? deps.mcp?.authorityNote(spec.name) : null;
1794
+ if (mcpNote) authority.push(mcpNote);
1795
+ const payload = {
1796
+ name: spec.name,
1797
+ kind,
1798
+ description: spec.description,
1799
+ parameters: wireParameterSchema(spec.parameters),
1800
+ actionClass: spec.baseActionClass,
1801
+ executionMode: spec.executionMode ?? "sequential",
1802
+ authority
1803
+ };
1804
+ return { kind: "ok", output: JSON.stringify(payload), details: { op: "describe", described: spec.name } };
1805
+ };
1806
+ const runCall = async (args, options) => {
1807
+ const name = typeof args.capability === "string" ? args.capability.trim() : "";
1808
+ if (name.length === 0) return { kind: "error", message: 'gateway: op="call" requires capability' };
1809
+ const allowed = allowedSet(options);
1810
+ if (allowed !== null && !allowed.has(name)) return outsideSurface(name, allowed);
1811
+ const resolved = await resolveCapability(name, options);
1812
+ if (resolved.spec === null) return { kind: "error", message: resolved.message };
1813
+ const spec = resolved.spec;
1814
+ if (args.args !== void 0 && !isRecord3(args.args)) {
1815
+ return { kind: "error", message: `gateway: args for ${spec.name} must be a JSON object` };
1816
+ }
1817
+ const rawArgs = isRecord3(args.args) ? args.args : {};
1818
+ let validated;
1819
+ try {
1820
+ validated = validateEngineToolArguments(
1821
+ { name: spec.name, description: spec.description, parameters: spec.parameters },
1822
+ { type: "toolCall", id: options?.toolCallId ?? "", name: spec.name, arguments: rawArgs }
1823
+ );
1824
+ } catch (error) {
1825
+ return {
1826
+ kind: "error",
1827
+ message: `gateway: ${spec.name} arguments rejected: ${error instanceof Error ? error.message : String(error)}. Use gateway(op="describe", capability="${spec.name}") for the schema.`
1828
+ };
1829
+ }
1830
+ const { approval: _approval, ...outer } = options ?? {};
1831
+ const verdict = await registry.invoke({ tool: spec.name, args: validated }, { ...outer, nested: true });
1832
+ if (verdict.kind === "not_visible") return { kind: "error", message: `gateway: ${verdict.reason}` };
1833
+ if (verdict.kind === "blocked") return nestedBlockedResult(verdict);
1834
+ return nestedExecutedResult(
1835
+ { ...verdict.result, details: { ...verdict.result.details, capability: spec.name } },
1836
+ verdict.decision
1837
+ );
1838
+ };
1839
+ return {
1840
+ ...gatewayToolSurface,
1841
+ prepareArguments: prepareGatewayArguments,
1842
+ async run(rawArgs, options) {
1843
+ const args = prepareGatewayArguments(rawArgs);
1844
+ const op = typeof args.op === "string" ? args.op : "";
1845
+ if (op === "find") return runFind(args, options);
1846
+ if (op === "describe") return runDescribe(args, options);
1847
+ if (op === "call") return runCall(args, options);
1848
+ return { kind: "error", message: `gateway: op must be find, describe, or call; got '${op}'` };
1849
+ }
1850
+ };
1851
+ }
1852
+
1261
1853
  // src/tools/lazy-tool.ts
1262
1854
  init_esm_shims();
1263
1855
  function surfaceSnapshot(spec) {
@@ -1311,20 +1903,29 @@ init_esm_shims();
1311
1903
 
1312
1904
  // src/tools/artifact.ts
1313
1905
  init_esm_shims();
1314
- import { mkdirSync, writeFileSync } from "node:fs";
1315
1906
  import path from "node:path";
1316
1907
 
1317
1908
  // src/tools/file-mutation-queue.ts
1318
1909
  init_esm_shims();
1319
- import { realpathSync } from "node:fs";
1320
- import { resolve } from "node:path";
1910
+ import { randomUUID } from "node:crypto";
1911
+ import { lstatSync, readlinkSync, realpathSync } from "node:fs";
1912
+ import { lstat, mkdir, open, readlink, realpath, rename, stat, unlink } from "node:fs/promises";
1913
+ import { basename, dirname, join, resolve } from "node:path";
1321
1914
  var fileMutationQueues = /* @__PURE__ */ new Map();
1322
- function mutationQueueKey(filePath) {
1915
+ function mutationQueueKey(filePath, depth = 0) {
1323
1916
  const resolved = resolve(filePath);
1917
+ if (depth > 40) throw new Error("Too many symbolic links");
1324
1918
  try {
1325
1919
  return realpathSync.native(resolved);
1326
1920
  } catch {
1327
- return resolved;
1921
+ try {
1922
+ if (lstatSync(resolved).isSymbolicLink()) {
1923
+ return mutationQueueKey(resolve(dirname(resolved), readlinkSync(resolved)), depth + 1);
1924
+ }
1925
+ } catch {
1926
+ }
1927
+ const parent = dirname(resolved);
1928
+ return parent === resolved ? resolved : join(mutationQueueKey(parent, depth), basename(resolved));
1328
1929
  }
1329
1930
  }
1330
1931
  async function withFileMutationQueue(filePath, fn) {
@@ -1346,6 +1947,73 @@ async function withFileMutationQueue(filePath, fn) {
1346
1947
  }
1347
1948
  }
1348
1949
  }
1950
+ async function resolvePublishTarget(filePath, depth = 0) {
1951
+ if (depth > 40) throw new Error("Too many symbolic links");
1952
+ const absolute = resolve(filePath);
1953
+ try {
1954
+ const info = await lstat(absolute);
1955
+ if (info.isSymbolicLink()) {
1956
+ return resolvePublishTarget(resolve(dirname(absolute), await readlink(absolute)), depth + 1);
1957
+ }
1958
+ if (!info.isFile()) throw new Error(`Refusing non-file target (including directories): ${filePath}`);
1959
+ } catch (error) {
1960
+ if (error.code !== "ENOENT") throw error;
1961
+ }
1962
+ await mkdir(dirname(absolute), { recursive: true });
1963
+ return join(await realpath(dirname(absolute)), basename(absolute));
1964
+ }
1965
+ async function publishFileAtomically(filePath, content, options = {}) {
1966
+ let tempPath;
1967
+ try {
1968
+ const target = await resolvePublishTarget(filePath);
1969
+ const previous = await stat(target).catch((error) => {
1970
+ if (error.code === "ENOENT") return null;
1971
+ throw error;
1972
+ });
1973
+ if (previous && !previous.isFile()) throw new Error(`Refusing directory or non-file target: ${filePath}`);
1974
+ tempPath = join(dirname(target), `.clio-coder-publish-${randomUUID()}.tmp`);
1975
+ const handle = await open(tempPath, "wx", previous ? previous.mode & 4095 : 438);
1976
+ let after;
1977
+ try {
1978
+ await handle.writeFile(content);
1979
+ if (previous) await handle.chmod(previous.mode & 4095);
1980
+ await handle.sync();
1981
+ const info = await handle.stat();
1982
+ after = { bytes: info.size, mtimeMs: info.mtimeMs };
1983
+ } finally {
1984
+ await handle.close();
1985
+ }
1986
+ await (options.rename ?? rename)(tempPath, target);
1987
+ tempPath = void 0;
1988
+ const result = {
1989
+ before: previous ? { bytes: previous.size, mtimeMs: previous.mtimeMs } : null,
1990
+ after
1991
+ };
1992
+ try {
1993
+ const directory = await open(dirname(target), "r");
1994
+ try {
1995
+ await directory.sync();
1996
+ } finally {
1997
+ await directory.close();
1998
+ }
1999
+ } catch (error) {
2000
+ result.durabilityWarning = `Published, but directory fsync was unavailable or failed: ${String(error)}`;
2001
+ }
2002
+ return result;
2003
+ } catch (error) {
2004
+ let cleanup = "";
2005
+ if (tempPath) {
2006
+ try {
2007
+ await unlink(tempPath);
2008
+ } catch (cleanupError) {
2009
+ if (cleanupError.code !== "ENOENT") {
2010
+ cleanup = ` Temporary file cleanup failed at ${tempPath}: ${String(cleanupError)}`;
2011
+ }
2012
+ }
2013
+ }
2014
+ throw new Error(`Nothing was published: ${String(error)}.${cleanup}`, { cause: error });
2015
+ }
2016
+ }
1349
2017
 
1350
2018
  // src/tools/artifact.ts
1351
2019
  var ARTIFACT_KINDS = ["plan", "review", "report"];
@@ -1368,11 +2036,9 @@ async function writeTerminalArtifact(kind, args, cwd) {
1368
2036
  const body = title.length > 0 && !content.trimStart().startsWith("#") ? `# ${title}
1369
2037
 
1370
2038
  ${content}` : content;
2039
+ let published;
1371
2040
  try {
1372
- await withFileMutationQueue(target, async () => {
1373
- mkdirSync(path.dirname(target), { recursive: true });
1374
- writeFileSync(target, body, "utf8");
1375
- });
2041
+ published = await withFileMutationQueue(target, () => publishFileAtomically(target, body));
1376
2042
  } catch (err) {
1377
2043
  return { kind: "error", message: `artifact: ${err instanceof Error ? err.message : String(err)}` };
1378
2044
  }
@@ -1380,10 +2046,16 @@ ${content}` : content;
1380
2046
  const bytes = Buffer.byteLength(body, "utf8");
1381
2047
  return {
1382
2048
  kind: "ok",
1383
- output: `wrote ${kind} artifact (${bytes}B) to ${rel}`,
2049
+ output: `wrote ${kind} artifact (${bytes}B) to ${rel}${published.durabilityWarning ? `
2050
+ ${published.durabilityWarning}` : ""}`,
1384
2051
  // shownBytes is the artifact's real size; the ledger otherwise measures
1385
2052
  // this confirmation sentence (a 4753B plan rendered as "60B", #76).
1386
- details: { kind, paths: [target], observation: { shownBytes: bytes } },
2053
+ details: {
2054
+ kind,
2055
+ paths: [target],
2056
+ observation: { shownBytes: bytes },
2057
+ file: { before: published.before, after: published.after }
2058
+ },
1387
2059
  // Writing the artifact is the whole turn; terminate skips the follow-up
1388
2060
  // LLM call that would only restate what was just written.
1389
2061
  terminate: true
@@ -1641,7 +2313,8 @@ function createDecideTool(deps = {}) {
1641
2313
 
1642
2314
  // src/tools/edit.ts
1643
2315
  init_esm_shims();
1644
- import { readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
2316
+ import { readFile, stat as stat2 } from "node:fs/promises";
2317
+ import { setImmediate as yieldImmediate } from "node:timers/promises";
1645
2318
 
1646
2319
  // src/tools/edit-diff.ts
1647
2320
  init_esm_shims();
@@ -2183,12 +2856,6 @@ function capDiffLine(text) {
2183
2856
  if (text.length <= MAX_DIFF_LINE_CHARS) return text;
2184
2857
  return `${text.slice(0, MAX_DIFF_LINE_CHARS)}\u2026 (+${text.length - MAX_DIFF_LINE_CHARS} chars)`;
2185
2858
  }
2186
- function detectLineEnding(content) {
2187
- const crlfIdx = content.indexOf("\r\n");
2188
- const lfIdx = content.indexOf("\n");
2189
- if (lfIdx === -1 || crlfIdx === -1) return "\n";
2190
- return crlfIdx < lfIdx ? "\r\n" : "\n";
2191
- }
2192
2859
  function normalizeToLF(text) {
2193
2860
  return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2194
2861
  }
@@ -2580,6 +3247,78 @@ function generateDiffString(oldContent, newContent, contextLines = 4) {
2580
3247
  }
2581
3248
 
2582
3249
  // src/tools/edit.ts
3250
+ var DIFF_LIMIT = 1024 * 1024;
3251
+ var VALIDATION_CHUNK = 64 * 1024;
3252
+ async function invalidUtf8Offset(bytes) {
3253
+ const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
3254
+ let failingStart = 0;
3255
+ for (let start2 = 0; start2 < bytes.length; start2 += VALIDATION_CHUNK) {
3256
+ try {
3257
+ decoder.decode(bytes.subarray(start2, start2 + VALIDATION_CHUNK), { stream: true });
3258
+ } catch {
3259
+ failingStart = start2;
3260
+ break;
3261
+ }
3262
+ failingStart = Math.min(start2 + VALIDATION_CHUNK, bytes.length);
3263
+ await yieldImmediate();
3264
+ }
3265
+ let start = Math.max(0, failingStart - 4);
3266
+ while (start > 0 && ((bytes[start] ?? 0) & 192) === 128) start -= 1;
3267
+ const narrow = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
3268
+ let sequenceStart = start;
3269
+ const end = Math.min(bytes.length, failingStart + VALIDATION_CHUNK);
3270
+ for (let i = start; i < end; i += 1) {
3271
+ try {
3272
+ if (narrow.decode(bytes.subarray(i, i + 1), { stream: true }).length > 0) sequenceStart = i + 1;
3273
+ } catch {
3274
+ return sequenceStart;
3275
+ }
3276
+ if ((i - start + 1) % 1024 === 0) await yieldImmediate();
3277
+ }
3278
+ return sequenceStart;
3279
+ }
3280
+ async function classifyEndings(bytes) {
3281
+ let crlf = false;
3282
+ let lf = false;
3283
+ let nextYield = VALIDATION_CHUNK;
3284
+ for (let i = 0; i < bytes.length; i += 1) {
3285
+ if (bytes[i] === 0) throw new Error(`NUL byte at byte offset ${i}; refusing binary input`);
3286
+ if (bytes[i] === 13) {
3287
+ if (bytes[i + 1] !== 10) throw new Error("Mixed or bare-CR line endings are unsupported; nothing was normalized");
3288
+ crlf = true;
3289
+ i += 1;
3290
+ } else if (bytes[i] === 10) lf = true;
3291
+ if (crlf && lf) throw new Error("Mixed or bare-CR line endings are unsupported; nothing was normalized");
3292
+ if (i >= nextYield) {
3293
+ nextYield = i + VALIDATION_CHUNK;
3294
+ await yieldImmediate();
3295
+ }
3296
+ }
3297
+ return crlf ? "\r\n" : "\n";
3298
+ }
3299
+ function applyLargeExactEdits(content, edits, ending) {
3300
+ const matches = edits.map((edit, editIndex) => {
3301
+ const oldText = restoreLineEndings(normalizeToLF(edit.oldText), ending);
3302
+ if (!oldText) throw new Error(`edits[${editIndex}].oldText must not be empty`);
3303
+ const index = content.indexOf(oldText);
3304
+ if (index < 0)
3305
+ throw new Error("Files above 1 MiB require exact matches; fuzzy matching is skipped to bound allocation");
3306
+ if (content.indexOf(oldText, index + oldText.length) >= 0)
3307
+ throw new Error("The text must be unique; provide more context");
3308
+ return { index, length: oldText.length, replacement: restoreLineEndings(normalizeToLF(edit.newText), ending) };
3309
+ }).sort((a, b) => a.index - b.index);
3310
+ const pieces = [];
3311
+ let cursor = 0;
3312
+ for (const entry of matches) {
3313
+ if (entry.index < cursor) throw new Error("Edits overlap; target disjoint regions");
3314
+ pieces.push(content.slice(cursor, entry.index), entry.replacement);
3315
+ cursor = entry.index + entry.length;
3316
+ }
3317
+ pieces.push(content.slice(cursor));
3318
+ const result = pieces.join("");
3319
+ if (result === content) throw new Error("No changes made; replacements produced identical content");
3320
+ return result;
3321
+ }
2583
3322
  var editEntrySchema = typebox_exports.Object({
2584
3323
  oldText: typebox_exports.String({ description: "Exact unique text to replace; must not overlap other edits." }),
2585
3324
  newText: typebox_exports.String({ description: "Replacement text." })
@@ -2618,7 +3357,7 @@ function prepareEditArguments(args) {
2618
3357
  }
2619
3358
  var editTool = {
2620
3359
  name: ToolNames.Edit,
2621
- description: "Edit one file with exact text replacements. Each oldText must match a unique region of the original file.",
3360
+ description: "Edit one file with exact text replacements. Each oldText must match a unique region of the original file. Matching tries exact text first, then quote/dash/NFKC and trailing-space normalization, then indentation relaxation. Files above 1 MiB require exact matches and skip diffs to bound allocation. Refuses NUL bytes, invalid UTF-8, and mixed or bare-CR line endings. LF replacement text adopts the file\u2019s LF or CRLF endings; the UTF-8 BOM is preserved. Publishes atomically through symlinks with mode bits preserved. External writers are not locked; the last rename wins. Returned file identities describe publication, not a precondition on an earlier model read.",
2622
3361
  parameters: typebox_exports.Object({
2623
3362
  path: typebox_exports.String({ description: "File path (relative or absolute)." }),
2624
3363
  edits: typebox_exports.Array(editEntrySchema, { description: "One or more targeted replacements." })
@@ -2637,21 +3376,39 @@ var editTool = {
2637
3376
  const filePath = resolveToCwd(pathArg);
2638
3377
  try {
2639
3378
  return await withFileMutationQueue(filePath, async () => {
2640
- const rawContent = readFileSync(filePath, "utf8");
3379
+ const info = await stat2(filePath);
3380
+ if (!info.isFile()) throw new Error("Refusing directory or non-file target");
3381
+ const bytes = await readFile(filePath);
3382
+ const originalEnding = await classifyEndings(bytes);
3383
+ let rawContent;
3384
+ try {
3385
+ rawContent = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
3386
+ } catch {
3387
+ throw new Error(`Invalid UTF-8 at byte offset ${await invalidUtf8Offset(bytes)}`);
3388
+ }
2641
3389
  const { bom, text: content } = stripBom(rawContent);
2642
- const originalEnding = detectLineEnding(content);
2643
- const normalizedContent = normalizeToLF(content);
2644
- const applied = {
2645
- ...applyEditsToNormalizedContent(normalizedContent, edits, pathArg),
2646
- replacements: edits.length
2647
- };
2648
- const finalContent = bom + restoreLineEndings(applied.newContent, originalEnding);
2649
- writeFileSync2(filePath, finalContent, "utf8");
2650
- const diff = generateDiffString(applied.baseContent, applied.newContent);
3390
+ let finalContent;
3391
+ let diff;
3392
+ if (bytes.length > DIFF_LIMIT) {
3393
+ finalContent = bom + applyLargeExactEdits(content, edits, originalEnding);
3394
+ } else {
3395
+ const applied = applyEditsToNormalizedContent(normalizeToLF(content), edits, pathArg);
3396
+ finalContent = bom + restoreLineEndings(applied.newContent, originalEnding);
3397
+ if (Buffer.byteLength(finalContent, "utf8") <= DIFF_LIMIT) {
3398
+ diff = generateDiffString(applied.baseContent, applied.newContent);
3399
+ }
3400
+ }
3401
+ const file = await publishFileAtomically(filePath, finalContent);
2651
3402
  return {
2652
3403
  kind: "ok",
2653
- output: `edited ${pathArg}: ${applied.replacements} replacement(s). Validate now: rerun the failing test or verify; navigation tools do not validate edits.`,
2654
- details: { diff: diff.diff, firstChangedLine: diff.firstChangedLine, paths: [filePath] }
3404
+ output: `edited ${pathArg}: ${edits.length} replacement(s). Validate now: rerun the failing test or verify; navigation tools do not validate edits.${!diff ? "\nnote: diff skipped because the previous or new file exceeds 1 MiB" : ""}${file.durabilityWarning ? `
3405
+ ${file.durabilityWarning}` : ""}`,
3406
+ details: {
3407
+ file: { before: file.before, after: file.after },
3408
+ diff: diff?.diff,
3409
+ firstChangedLine: diff?.firstChangedLine,
3410
+ paths: [filePath]
3411
+ }
2655
3412
  };
2656
3413
  });
2657
3414
  } catch (err) {
@@ -2660,10 +3417,10 @@ var editTool = {
2660
3417
  if (code === "ENOENT") {
2661
3418
  return {
2662
3419
  kind: "error",
2663
- message: `edit: ${msg}. File not found at ${pathArg}. The path may be wrong. Try: code_nav, find, glob, or ls to locate it.`
3420
+ message: `edit: Nothing was published. ${msg}. File not found at ${pathArg}. The path may be wrong. Try: code_nav, find, glob, or ls to locate it.`
2664
3421
  };
2665
3422
  }
2666
- return { kind: "error", message: `edit: ${msg}` };
3423
+ return { kind: "error", message: `edit: Nothing was published. ${msg}` };
2667
3424
  }
2668
3425
  }
2669
3426
  };
@@ -2705,7 +3462,7 @@ var evidenceTool = {
2705
3462
  try {
2706
3463
  const dataDir = clioDataDir();
2707
3464
  if (mode === "list") {
2708
- const { evidenceInventorySnapshot } = await import("./inventory-QPQUTUUZ.js");
3465
+ const { evidenceInventorySnapshot } = await import("./inventory-I6VJDDYF.js");
2709
3466
  return boundedResult(await evidenceInventorySnapshot(Date.now, dataDir));
2710
3467
  }
2711
3468
  const id = mode === "inspect" ? args.id : args.runId;
@@ -2713,8 +3470,8 @@ var evidenceTool = {
2713
3470
  return { kind: "error", message: `evidence: ${mode} requires ${mode === "inspect" ? "id" : "runId"}` };
2714
3471
  }
2715
3472
  assertSafeId(id, mode === "inspect" ? "evidence" : "run");
2716
- const { buildEvidence, EvidenceNotFoundError, inspectEvidence, loadEvidenceGateDecisions } = await import("./evidence-POWAEBEW.js");
2717
- const { evidenceDetailSnapshot } = await import("./detail-VSBWYSGZ.js");
3473
+ const { buildEvidence, EvidenceNotFoundError, inspectEvidence, loadEvidenceGateDecisions } = await import("./evidence-P4NZOAK7.js");
3474
+ const { evidenceDetailSnapshot } = await import("./detail-4GLROGY5.js");
2718
3475
  let evidenceId = mode === "run" ? `run-${id}` : id;
2719
3476
  let inspected;
2720
3477
  try {
@@ -2754,15 +3511,58 @@ var evidenceTool = {
2754
3511
  init_esm_shims();
2755
3512
  import { existsSync, statSync } from "node:fs";
2756
3513
  import { readdir } from "node:fs/promises";
2757
- import { join, relative } from "node:path";
3514
+ import { join as join2, relative, resolve as resolve2 } from "node:path";
2758
3515
 
2759
3516
  // src/tools/spawn-hygiene.ts
2760
3517
  init_esm_shims();
3518
+ import { AsyncLocalStorage } from "node:async_hooks";
2761
3519
  import { spawn } from "node:child_process";
2762
3520
  import { createInterface as createInterface2 } from "node:readline";
3521
+ import { StringDecoder } from "node:string_decoder";
2763
3522
  var SEARCH_SPAWN_TIMEOUT_MS = 3e4;
2764
3523
  var KILL_GRACE_MS = 3e3;
2765
- var STDERR_CAP_BYTES = 16 * 1024;
3524
+ var SEARCH_STDERR_CAP_BYTES = 16 * 1024;
3525
+ var timeoutContext = new AsyncLocalStorage();
3526
+ function decodeBoundedUtf8(buffer) {
3527
+ const decoded = new StringDecoder("utf8").write(buffer);
3528
+ return truncateUtf8(decoded, SEARCH_STDERR_CAP_BYTES, "");
3529
+ }
3530
+ var BoundedDiagnosticDecoder = class {
3531
+ constructor(onLine) {
3532
+ this.onLine = onLine;
3533
+ }
3534
+ onLine;
3535
+ buffer = Buffer.alloc(SEARCH_STDERR_CAP_BYTES);
3536
+ used = 0;
3537
+ truncated = false;
3538
+ oversizedLines = 0;
3539
+ get bufferedBytes() {
3540
+ return this.used;
3541
+ }
3542
+ write(chunk) {
3543
+ let start = 0;
3544
+ while (start < chunk.length) {
3545
+ const newline = chunk.indexOf(10, start);
3546
+ const end = newline < 0 ? chunk.length : newline;
3547
+ const take = Math.min(end - start, this.buffer.length - this.used);
3548
+ chunk.copy(this.buffer, this.used, start, start + take);
3549
+ this.used += take;
3550
+ if (take < end - start) this.truncated = true;
3551
+ if (newline < 0) break;
3552
+ this.flush();
3553
+ start = newline + 1;
3554
+ }
3555
+ }
3556
+ end() {
3557
+ if (this.used || this.truncated) this.flush();
3558
+ }
3559
+ flush() {
3560
+ if (this.truncated) this.oversizedLines++;
3561
+ this.onLine(decodeBoundedUtf8(this.buffer.subarray(0, this.used)).replace(/\r$/, ""), this.truncated);
3562
+ this.used = 0;
3563
+ this.truncated = false;
3564
+ }
3565
+ };
2766
3566
  var MAX_SEARCH_PATTERN_BYTES = 64 * 1024;
2767
3567
  function validateSearchPatternSize(pattern) {
2768
3568
  const bytes = Buffer.byteLength(pattern, "utf8");
@@ -2772,15 +3572,76 @@ function validateSearchPatternSize(pattern) {
2772
3572
  message: `pattern too large (${bytes} bytes; max ${MAX_SEARCH_PATTERN_BYTES}). Narrow the pattern before searching.`
2773
3573
  };
2774
3574
  }
3575
+ function newSearchCompleteness() {
3576
+ return { complete: true, skipped: { count: 0, samples: [] } };
3577
+ }
3578
+ function skipSearchPath(search, path5, allows = () => true) {
3579
+ search.complete = false;
3580
+ search.reason ??= "errors";
3581
+ search.skipped.count += 1;
3582
+ if (allows(path5) && search.skipped.samples.length < 5) search.skipped.samples.push(path5);
3583
+ }
3584
+ function searchDiagnosticPath(line) {
3585
+ const cleaned = line.replace(/^(?:rg: |\[fd error\]:? )/, "").replace(/^IO error for operation on /, "");
3586
+ if (/^(?:regex parse error|error parsing (?:glob|regex)|invalid (?:glob|regex)|error:|Usage:)/i.test(cleaned))
3587
+ return null;
3588
+ const match = /^((?:[A-Za-z]:[\\/])?[^\r\n]+?):(?:\s|$)/.exec(cleaned);
3589
+ return match?.[1] ?? null;
3590
+ }
3591
+ function createSearchDiagnostics(search, allows) {
3592
+ let continuation = false;
3593
+ let realError = false;
3594
+ return {
3595
+ get realError() {
3596
+ return realError;
3597
+ },
3598
+ onLine(line, truncated = false) {
3599
+ if (truncated) {
3600
+ search.skipped.unknown = true;
3601
+ search.complete = false;
3602
+ search.reason ??= "errors";
3603
+ }
3604
+ if (!line.trim()) return;
3605
+ if (continuation && /^\s/.test(line)) return;
3606
+ const path5 = searchDiagnosticPath(line);
3607
+ if (path5) {
3608
+ skipSearchPath(search, path5, allows);
3609
+ continuation = true;
3610
+ return;
3611
+ }
3612
+ if (continuation && /\(os error \d+\)/.test(line)) return;
3613
+ continuation = false;
3614
+ if (/^(?:rg: |\[fd error\]:? )?(?:regex parse error|error parsing (?:glob|regex)|invalid (?:glob|regex)|error:|Usage:)/i.test(
3615
+ line
3616
+ ))
3617
+ realError = true;
3618
+ else search.skipped.unknown = true;
3619
+ search.complete = false;
3620
+ search.reason ??= "errors";
3621
+ }
3622
+ };
3623
+ }
3624
+ function finishSearch(search, result) {
3625
+ const reason = result.aborted ? "cancelled" : result.timedOut ? "timeout" : result.stoppedEarly ? "limit" : result.exitCode !== 0 && result.exitCode !== 1 ? "errors" : search.reason;
3626
+ if (reason) {
3627
+ search.complete = false;
3628
+ search.reason = reason;
3629
+ }
3630
+ }
3631
+ function searchNotice(search, shown, unit) {
3632
+ return search.complete ? "" : `
3633
+ [Search incomplete: ${search.reason}; ${shown} ${unit} shown; ${search.skipped.count}${search.skipped.unknown ? "+ (diagnostic coverage unknown)" : ""} paths skipped.]`;
3634
+ }
2775
3635
  function spawnLineStream(file, args, options) {
2776
- return new Promise((resolve2) => {
2777
- const timeoutMs = options.timeoutMs ?? SEARCH_SPAWN_TIMEOUT_MS;
3636
+ return new Promise((resolve3) => {
3637
+ const timeoutMs = options.timeoutMs ?? timeoutContext.getStore() ?? SEARCH_SPAWN_TIMEOUT_MS;
2778
3638
  let timedOut = false;
2779
3639
  let aborted = false;
2780
3640
  let stoppedEarly = false;
2781
3641
  let killSent = false;
2782
3642
  let settled = false;
2783
- let stderr = "";
3643
+ const retainedStderr = Buffer.alloc(SEARCH_STDERR_CAP_BYTES);
3644
+ let stderrBytes = 0;
2784
3645
  let killGraceTimer = null;
2785
3646
  const spawned = (() => {
2786
3647
  try {
@@ -2798,9 +3659,10 @@ function spawnLineStream(file, args, options) {
2798
3659
  }
2799
3660
  })();
2800
3661
  if (!spawned.ok) {
2801
- resolve2({
3662
+ resolve3({
2802
3663
  exitCode: null,
2803
3664
  stderr: "",
3665
+ oversizedDiagnostics: 0,
2804
3666
  timedOut: false,
2805
3667
  aborted: false,
2806
3668
  stoppedEarly: false,
@@ -2810,6 +3672,7 @@ function spawnLineStream(file, args, options) {
2810
3672
  }
2811
3673
  const child = spawned.child;
2812
3674
  const rl = createInterface2({ input: child.stdout });
3675
+ const stderrLines = new BoundedDiagnosticDecoder((line, truncated) => options.onStderrLine?.(line, truncated));
2813
3676
  const sendSignal = (signalName) => {
2814
3677
  const pid = child.pid;
2815
3678
  if (pid && process.platform !== "win32") {
@@ -2851,9 +3714,11 @@ function spawnLineStream(file, args, options) {
2851
3714
  if (killGraceTimer) clearTimeout(killGraceTimer);
2852
3715
  options.signal?.removeEventListener("abort", onAbort);
2853
3716
  rl.close();
2854
- resolve2({
3717
+ stderrLines.end();
3718
+ resolve3({
2855
3719
  exitCode: child.exitCode,
2856
- stderr,
3720
+ stderr: decodeBoundedUtf8(retainedStderr.subarray(0, stderrBytes)),
3721
+ oversizedDiagnostics: stderrLines.oversizedLines,
2857
3722
  timedOut,
2858
3723
  aborted,
2859
3724
  stoppedEarly,
@@ -2865,7 +3730,10 @@ function spawnLineStream(file, args, options) {
2865
3730
  options.onLine(line, stop);
2866
3731
  });
2867
3732
  child.stderr?.on("data", (chunk) => {
2868
- if (stderr.length < STDERR_CAP_BYTES) stderr += chunk.toString("utf8");
3733
+ const take = Math.min(chunk.length, retainedStderr.length - stderrBytes);
3734
+ chunk.copy(retainedStderr, stderrBytes, 0, take);
3735
+ stderrBytes += take;
3736
+ stderrLines.write(chunk);
2869
3737
  });
2870
3738
  child.on("error", (error) => finish(error.message));
2871
3739
  child.on("close", () => finish(null));
@@ -2875,9 +3743,9 @@ function spawnLineStream(file, args, options) {
2875
3743
  // src/tools/find.ts
2876
3744
  var DEFAULT_LIMIT = 500;
2877
3745
  var MTIME_CANDIDATE_FLOOR = 2e3;
2878
- function buildFdArgs(pattern, searchPath, maxResults, includeIgnored) {
3746
+ function buildFdArgs(pattern, searchPath, collectLimit, includeIgnored) {
2879
3747
  const args = ["--glob", "--color=never", ...fdIgnoreArgs(searchPath, includeIgnored)];
2880
- args.push("--max-results", String(maxResults));
3748
+ args.push("--show-errors", "--max-results", String(collectLimit));
2881
3749
  let effectivePattern = pattern;
2882
3750
  if (pattern.includes("/")) {
2883
3751
  args.push("--full-path");
@@ -2888,35 +3756,38 @@ function buildFdArgs(pattern, searchPath, maxResults, includeIgnored) {
2888
3756
  args.push("--", effectivePattern, searchPath);
2889
3757
  return args;
2890
3758
  }
2891
- async function fdFind(fdPath, pattern, searchPath, collectLimit, includeIgnored, signal) {
3759
+ async function fdFind(fdPath, pattern, searchPath, collectLimit, includeIgnored, search, allows, signal) {
2892
3760
  const lines = [];
3761
+ const diagnostics = createSearchDiagnostics(search, (path5) => allows(resolve2(process.cwd(), path5)));
2893
3762
  const result = await spawnLineStream(fdPath, buildFdArgs(pattern, searchPath, collectLimit, includeIgnored), {
2894
3763
  ...signal ? { signal } : {},
3764
+ onStderrLine: diagnostics.onLine,
2895
3765
  onLine(line, stop) {
2896
3766
  lines.push(line);
2897
3767
  if (lines.length >= collectLimit) stop();
2898
3768
  }
2899
3769
  });
2900
- if (result.aborted) return { ok: false, message: "find: operation aborted" };
2901
- if (result.timedOut) {
3770
+ finishSearch(search, result);
3771
+ if (!result.aborted && !result.timedOut && !result.stoppedEarly && result.exitCode !== 0) {
3772
+ search.complete = false;
3773
+ search.reason = "errors";
3774
+ }
3775
+ if (result.spawnError !== null) return { ok: false, message: `find: failed to run fd: ${result.spawnError}` };
3776
+ if (!result.aborted && !result.timedOut && !result.stoppedEarly && result.exitCode !== 0 && lines.length === 0 && (diagnostics.realError || search.skipped.count === 0 && !search.skipped.unknown)) {
2902
3777
  return {
2903
3778
  ok: false,
2904
- message: `find: fd timed out after ${SEARCH_SPAWN_TIMEOUT_MS / 1e3}s. Narrow the pattern or path, or lower limit.`
3779
+ message: `find: fd rejected the search or failed (exit code ${result.exitCode}). Check the glob and search path.`
2905
3780
  };
2906
3781
  }
2907
- if (result.spawnError !== null) return { ok: false, message: `find: failed to run fd: ${result.spawnError}` };
2908
- if (!result.stoppedEarly && result.exitCode !== 0 && lines.length === 0) {
2909
- return { ok: false, message: `find: ${result.stderr.trim() || `fd exited with code ${result.exitCode}`}` };
2910
- }
2911
- const paths = lines.map((rawLine) => rawLine.replace(/\r$/, "").trim()).filter((line) => line.length > 0).map((line) => {
3782
+ const paths = lines.map((rawLine) => rawLine.replace(/\r$/, "")).filter((line) => line.length > 0).map((line) => {
2912
3783
  const hadTrailingSlash = line.endsWith("/") || line.endsWith("\\");
2913
- let relPath = line.startsWith(searchPath) ? line.slice(searchPath.length + 1) : relative(searchPath, line);
3784
+ let relPath = relative(searchPath, line);
2914
3785
  if (hadTrailingSlash && !relPath.endsWith("/")) relPath += "/";
2915
3786
  return toPosixPath(relPath);
2916
3787
  });
2917
3788
  return { ok: true, paths };
2918
3789
  }
2919
- async function fallbackFind(pattern, searchPath, collectLimit, includeIgnored, signal) {
3790
+ async function fallbackFind(pattern, searchPath, collectLimit, includeIgnored, search, allows, signal) {
2920
3791
  const matcher = compileGlobRegex(pattern.includes("/") ? pattern : `**/${pattern}`);
2921
3792
  const ignored = fallbackIgnoredDirs(includeIgnored);
2922
3793
  const out = [];
@@ -2926,11 +3797,13 @@ async function fallbackFind(pattern, searchPath, collectLimit, includeIgnored, s
2926
3797
  try {
2927
3798
  entries = (await readdir(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
2928
3799
  } catch {
3800
+ skipSearchPath(search, dir, allows);
2929
3801
  return;
2930
3802
  }
2931
3803
  for (const entry of entries) {
2932
3804
  if (out.length >= collectLimit || signal?.aborted) return;
2933
- const absPath = join(dir, entry.name);
3805
+ const absPath = join2(dir, entry.name);
3806
+ if (!allows(absPath)) continue;
2934
3807
  if (entry.isDirectory() && !entry.isSymbolicLink()) {
2935
3808
  if (ignored.has(entry.name)) continue;
2936
3809
  const relDir = `${toPosixPath(relative(searchPath, absPath))}/`;
@@ -2938,7 +3811,13 @@ async function fallbackFind(pattern, searchPath, collectLimit, includeIgnored, s
2938
3811
  await walk(absPath);
2939
3812
  continue;
2940
3813
  }
2941
- if (!entry.isFile()) continue;
3814
+ if (entry.isSymbolicLink()) {
3815
+ try {
3816
+ if (statSync(absPath).isDirectory()) skipSearchPath(search, absPath, allows);
3817
+ } catch {
3818
+ skipSearchPath(search, absPath, allows);
3819
+ }
3820
+ } else if (!entry.isFile()) continue;
2942
3821
  const relPath = toPosixPath(relative(searchPath, absPath));
2943
3822
  if (matcher.test(normalizeGlobInput(relPath))) out.push(relPath);
2944
3823
  }
@@ -2946,13 +3825,14 @@ async function fallbackFind(pattern, searchPath, collectLimit, includeIgnored, s
2946
3825
  await walk(searchPath);
2947
3826
  return out;
2948
3827
  }
2949
- function orderByMtime(paths, searchPath, limit, candidateCap) {
3828
+ function orderByMtime(paths, searchPath, limit, candidateCap, search) {
2950
3829
  const candidateCapHit = paths.length >= candidateCap;
2951
3830
  const stamped = paths.map((relPath) => {
2952
3831
  let mtimeMs = 0;
2953
3832
  try {
2954
- mtimeMs = statSync(join(searchPath, relPath)).mtimeMs;
3833
+ mtimeMs = statSync(join2(searchPath, relPath)).mtimeMs;
2955
3834
  } catch {
3835
+ skipSearchPath(search, join2(searchPath, relPath));
2956
3836
  }
2957
3837
  return { relPath, mtimeMs };
2958
3838
  });
@@ -2960,16 +3840,22 @@ function orderByMtime(paths, searchPath, limit, candidateCap) {
2960
3840
  return { paths: stamped.slice(0, limit).map((entry) => entry.relPath), candidateCapHit, candidateCap };
2961
3841
  }
2962
3842
  function renderFindResult(input) {
2963
- const { ordered, collected, collectLimit, limit, order, reservation, options } = input;
3843
+ const { ordered, collected, collectLimit, limit, order, reservation, options, search } = input;
3844
+ if ((collected > limit || collected >= collectLimit || ordered.candidateCapHit) && search.complete) {
3845
+ search.complete = false;
3846
+ search.reason = "limit";
3847
+ }
3848
+ const coverageNotice = input.fallback ? "\n[Fallback search uses GENERATED_DIRS only; .gitignore is not applied.]" : "\n[Native fd search: skipped symlinked directories are not counted.]";
2964
3849
  if (ordered.paths.length === 0) {
2965
3850
  return finalizeObservation({
2966
3851
  tool: ToolNames.Find,
2967
3852
  withheldPaths: input.withheldPaths,
2968
3853
  unit: "paths",
2969
- output: "No visible files found matching pattern",
3854
+ output: (search.complete ? "No visible files found matching pattern" : "Search incomplete") + searchNotice(search, 0, "paths") + coverageNotice,
3855
+ details: { search, symlinkDirectories: { counted: input.fallback } },
2970
3856
  shownCount: 0,
2971
- totalCount: collected >= collectLimit ? null : 0,
2972
- truncated: collected >= collectLimit,
3857
+ totalCount: search.complete ? 0 : null,
3858
+ truncated: !search.complete,
2973
3859
  ...collected >= collectLimit ? { next: `limit=${limit * 2}` } : {},
2974
3860
  reservation,
2975
3861
  ...options ? { options } : {}
@@ -2981,10 +3867,14 @@ function renderFindResult(input) {
2981
3867
  maxLines: Number.MAX_SAFE_INTEGER
2982
3868
  });
2983
3869
  const limitHit = collected > limit || collected >= collectLimit;
2984
- const totalKnown = !limitHit && !ordered.candidateCapHit;
2985
- const truncated = limitHit || ordered.candidateCapHit || truncation.truncated;
3870
+ if (truncation.truncated && search.complete) {
3871
+ search.complete = false;
3872
+ search.reason = "limit";
3873
+ }
3874
+ const totalKnown = search.complete;
3875
+ const truncated = !search.complete || limitHit || ordered.candidateCapHit || truncation.truncated;
2986
3876
  const next = limitHit ? `limit=${limit * 2}` : ordered.candidateCapHit ? "order=path" : void 0;
2987
- const details = {};
3877
+ const details = { search, symlinkDirectories: { counted: input.fallback } };
2988
3878
  if (order === "mtime" && ordered.candidateCap !== null) {
2989
3879
  details.candidates = {
2990
3880
  cap: ordered.candidateCap,
@@ -2997,7 +3887,7 @@ function renderFindResult(input) {
2997
3887
  tool: ToolNames.Find,
2998
3888
  withheldPaths: input.withheldPaths,
2999
3889
  unit: "paths",
3000
- output: truncation.content,
3890
+ output: truncation.content + searchNotice(search, truncation.outputLines, "paths") + coverageNotice,
3001
3891
  // Offload only when the byte cap cut collected paths; a bare result
3002
3892
  // limit continues via `next`, and the offload would duplicate the body.
3003
3893
  ...truncation.truncated ? { fullOutput } : {},
@@ -3012,7 +3902,7 @@ function renderFindResult(input) {
3012
3902
  }
3013
3903
  var findTool = {
3014
3904
  name: ToolNames.Find,
3015
- description: "Find files and directories by glob pattern (supports *, **, ?, [abc]); returns paths relative to the search directory. Respects .gitignore and skips generated dirs (node_modules, dist, build, ...) unless include_ignored=true. order=path (default) returns fd's native order; order=mtime returns newest first from a bounded candidate set. Truncated results say how to continue and where the full list was saved.",
3905
+ description: "Find files and directories by glob pattern (supports *, **, ?, [abc]); returns paths relative to the search directory. Respects .gitignore and skips generated dirs (node_modules, dist, build, ...) unless include_ignored=true. Symlinked directories are never followed. Only the fallback counts skipped symlinked directories; native fd leaves that count unmeasured, as indicated by details.symlinkDirectories.counted=false. Without fd the fallback uses GENERATED_DIRS only, with no .gitignore support. order=path (default) returns fd's native order; order=mtime returns newest first from a bounded candidate set. Truncated results say how to continue and where the full list was saved.",
3016
3906
  parameters: typebox_exports.Object({
3017
3907
  pattern: typebox_exports.String({ description: "Glob pattern, e.g. 'src/**/*.ts'." }),
3018
3908
  path: typebox_exports.Optional(typebox_exports.String({ description: "Directory to search in." })),
@@ -3052,24 +3942,48 @@ var findTool = {
3052
3942
  commitObservationReservation(reservation);
3053
3943
  try {
3054
3944
  let collectedPaths;
3945
+ const search = newSearchCompleteness();
3946
+ const pathFilter = createObservationPathFilter(process.cwd(), options?.allowsObservationPath);
3055
3947
  const fdPath = resolveFdBinary();
3056
3948
  if (fdPath) {
3057
- const result = await fdFind(fdPath, pattern, searchPath, collectLimit, includeIgnored, options?.signal);
3949
+ const result = await fdFind(
3950
+ fdPath,
3951
+ pattern,
3952
+ searchPath,
3953
+ collectLimit,
3954
+ includeIgnored,
3955
+ search,
3956
+ pathFilter.allows,
3957
+ options?.signal
3958
+ );
3058
3959
  if (!result.ok) return { kind: "error", message: result.message };
3059
3960
  collectedPaths = result.paths;
3060
3961
  } else {
3061
3962
  try {
3062
- collectedPaths = await fallbackFind(pattern, searchPath, collectLimit, includeIgnored, options?.signal);
3963
+ collectedPaths = await fallbackFind(
3964
+ pattern,
3965
+ searchPath,
3966
+ collectLimit,
3967
+ includeIgnored,
3968
+ search,
3969
+ pathFilter.allows,
3970
+ options?.signal
3971
+ );
3063
3972
  } catch (err) {
3064
3973
  const message = err instanceof Error ? err.message : String(err);
3065
3974
  return { kind: "error", message: `find: ${message}` };
3066
3975
  }
3067
3976
  }
3977
+ if (options?.signal?.aborted) {
3978
+ search.complete = false;
3979
+ search.reason = "cancelled";
3980
+ }
3068
3981
  const collectedCount = collectedPaths.length;
3069
- const pathFilter = createObservationPathFilter(process.cwd(), options?.allowsObservationPath);
3070
- collectedPaths = collectedPaths.filter((entry) => pathFilter.allows(join(searchPath, entry)));
3071
- const ordered = order === "mtime" ? orderByMtime(collectedPaths, searchPath, limit, collectLimit) : { paths: collectedPaths.slice(0, limit), candidateCapHit: false, candidateCap: null };
3982
+ collectedPaths = collectedPaths.filter((entry) => pathFilter.allows(join2(searchPath, entry)));
3983
+ const ordered = order === "mtime" ? orderByMtime(collectedPaths, searchPath, limit, collectLimit, search) : { paths: collectedPaths.slice(0, limit), candidateCapHit: false, candidateCap: null };
3072
3984
  return renderFindResult({
3985
+ search,
3986
+ fallback: !fdPath,
3073
3987
  withheldPaths: pathFilter.withheldPaths,
3074
3988
  ordered,
3075
3989
  collected: collectedCount,
@@ -3087,8 +4001,10 @@ var findTool = {
3087
4001
 
3088
4002
  // src/tools/grep.ts
3089
4003
  init_esm_shims();
3090
- import { readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
3091
- import path2, { join as join2, relative as relative2 } from "node:path";
4004
+ import { statSync as statSync2 } from "node:fs";
4005
+ import { readdir as readdir2, readFile as readFile2, stat as stat3 } from "node:fs/promises";
4006
+ import path2, { join as join3, relative as relative2 } from "node:path";
4007
+ import { setImmediate as yieldToLoop } from "node:timers/promises";
3092
4008
  var DEFAULT_LIMIT2 = 100;
3093
4009
  var FALLBACK_MAX_FILE_BYTES = 2e7;
3094
4010
  var MODE_UNITS = {
@@ -3118,16 +4034,18 @@ function statIsDirectory(searchPath) {
3118
4034
  }
3119
4035
  var NO_MATCH_OUTPUT = "No matches found";
3120
4036
  function renderGrepResult(input) {
3121
- const { mode, lines, matchCount, limitHit, limit, linesTruncated, reservation, options } = input;
4037
+ const { mode, lines, matchCount, limitHit, limit, linesTruncated, reservation, options, search } = input;
4038
+ const fallbackNotice = input.fallback ? "\n[Fallback search uses GENERATED_DIRS only; .gitignore is not applied.]" : "";
3122
4039
  if (lines.length === 0) {
3123
4040
  return finalizeObservation({
3124
4041
  tool: ToolNames.Grep,
3125
4042
  withheldPaths: input.withheldPaths,
3126
4043
  unit: MODE_UNITS[mode],
3127
- output: NO_MATCH_OUTPUT,
4044
+ output: (search.complete ? NO_MATCH_OUTPUT : "Search incomplete") + searchNotice(search, 0, "matches") + fallbackNotice,
4045
+ details: { search },
3128
4046
  shownCount: 0,
3129
- totalCount: 0,
3130
- truncated: false,
4047
+ totalCount: search.complete ? 0 : null,
4048
+ truncated: !search.complete,
3131
4049
  reservation,
3132
4050
  ...options ? { options } : {}
3133
4051
  });
@@ -3143,7 +4061,11 @@ function renderGrepResult(input) {
3143
4061
  } else {
3144
4062
  shownCount = truncation.outputLines;
3145
4063
  }
3146
- const truncated = limitHit || truncation.truncated;
4064
+ if (truncation.truncated && search.complete) {
4065
+ search.complete = false;
4066
+ search.reason = "limit";
4067
+ }
4068
+ const truncated = !search.complete || limitHit || truncation.truncated;
3147
4069
  const next = limitHit ? `limit=${limit * 2}` : truncation.truncated && mode === "content" ? "mode=files" : void 0;
3148
4070
  let output = truncation.content;
3149
4071
  if (linesTruncated) {
@@ -3155,12 +4077,13 @@ function renderGrepResult(input) {
3155
4077
  tool: ToolNames.Grep,
3156
4078
  withheldPaths: input.withheldPaths,
3157
4079
  unit: MODE_UNITS[mode],
3158
- output,
4080
+ output: output + searchNotice(search, shownCount, "matches") + fallbackNotice,
4081
+ details: { search },
3159
4082
  // Offload only when the byte cap cut collected content; a bare match
3160
4083
  // limit continues via `next`, and the offload would duplicate the body.
3161
4084
  ...truncation.truncated ? { fullOutput } : {},
3162
4085
  shownCount,
3163
- totalCount: limitHit ? null : matchCount,
4086
+ totalCount: search.complete ? matchCount : null,
3164
4087
  truncated,
3165
4088
  ...next !== void 0 ? { next } : {},
3166
4089
  reservation,
@@ -3244,22 +4167,23 @@ async function runRipgrep(input) {
3244
4167
  stop();
3245
4168
  }
3246
4169
  };
4170
+ const search = newSearchCompleteness();
4171
+ const diagnostics = createSearchDiagnostics(search, (filePath) => input.pathFilter.allows(path2.resolve(filePath)));
3247
4172
  const result = await spawnLineStream(input.rgPath, buildRgArgs(input), {
3248
4173
  ...input.options?.signal ? { signal: input.options.signal } : {},
3249
- onLine: input.mode === "content" ? onContentLine : onListLine
4174
+ onLine: input.mode === "content" ? onContentLine : onListLine,
4175
+ onStderrLine: diagnostics.onLine
3250
4176
  });
3251
- if (result.aborted) return { kind: "error", message: "grep: operation aborted" };
3252
- if (result.timedOut) {
4177
+ finishSearch(search, result);
4178
+ if (result.spawnError !== null) return { kind: "error", message: `grep: failed to run rg: ${result.spawnError}` };
4179
+ if (!result.aborted && !result.timedOut && !result.stoppedEarly && result.exitCode !== 0 && result.exitCode !== 1 && matchCount === 0 && (diagnostics.realError || search.skipped.count === 0 && !search.skipped.unknown)) {
3253
4180
  return {
3254
4181
  kind: "error",
3255
- message: `grep: rg timed out after ${SEARCH_SPAWN_TIMEOUT_MS / 1e3}s. Narrow the pattern, path, or glob, lower context, or use mode=files.`
4182
+ message: `grep: rg rejected the search or failed (exit code ${result.exitCode}). Check the pattern and search path.`
3256
4183
  };
3257
4184
  }
3258
- if (result.spawnError !== null) return { kind: "error", message: `grep: failed to run rg: ${result.spawnError}` };
3259
- if (!result.stoppedEarly && result.exitCode !== 0 && result.exitCode !== 1) {
3260
- return { kind: "error", message: `grep: ${result.stderr.trim() || `ripgrep exited with code ${result.exitCode}`}` };
3261
- }
3262
4185
  return renderGrepResult({
4186
+ search,
3263
4187
  withheldPaths: input.pathFilter.withheldPaths,
3264
4188
  mode: input.mode,
3265
4189
  lines: rendered,
@@ -3287,7 +4211,8 @@ function looksBinary(buffer) {
3287
4211
  }
3288
4212
  return false;
3289
4213
  }
3290
- function fallbackGrep(input) {
4214
+ async function fallbackGrep(input) {
4215
+ const search = newSearchCompleteness();
3291
4216
  const built = buildMatchRegex(input.pattern, input.literal, input.ignoreCase);
3292
4217
  if (!built.ok) return { kind: "error", message: built.message };
3293
4218
  const regex = built.regex;
@@ -3312,21 +4237,32 @@ function fallbackGrep(input) {
3312
4237
  if (truncatedLine.wasTruncated) linesTruncated = true;
3313
4238
  rendered.push({ text: truncatedLine.text, isMatch });
3314
4239
  };
3315
- const searchFile = (filePath) => {
3316
- if (limitHit || !input.pathFilter.allows(filePath)) return;
4240
+ const searchFile = async (filePath) => {
4241
+ if (limitHit || signal?.aborted || !input.pathFilter.allows(filePath)) return;
3317
4242
  let buffer;
3318
4243
  try {
3319
- const stat = statSync2(filePath);
3320
- if (!stat.isFile() || stat.size > FALLBACK_MAX_FILE_BYTES) return;
3321
- buffer = readFileSync2(filePath);
4244
+ const fileStat = await stat3(filePath);
4245
+ if (!fileStat.isFile() || fileStat.size > FALLBACK_MAX_FILE_BYTES) {
4246
+ skipSearchPath(search, filePath, input.pathFilter.allows);
4247
+ return;
4248
+ }
4249
+ buffer = await readFile2(filePath);
3322
4250
  } catch {
4251
+ skipSearchPath(search, filePath, input.pathFilter.allows);
4252
+ return;
4253
+ }
4254
+ if (looksBinary(buffer)) {
4255
+ skipSearchPath(search, filePath, input.pathFilter.allows);
3323
4256
  return;
3324
4257
  }
3325
- if (looksBinary(buffer)) return;
3326
4258
  const lines = buffer.toString("utf8").replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
3327
4259
  const rel = formatPath(filePath, input.searchPath, input.isDirectory);
3328
4260
  let fileMatches = 0;
3329
4261
  for (let i = 0; i < lines.length; i += 1) {
4262
+ if (i % 1024 === 0) {
4263
+ await yieldToLoop();
4264
+ if (signal?.aborted) return;
4265
+ }
3330
4266
  const lineText = lines[i] ?? "";
3331
4267
  regex.lastIndex = 0;
3332
4268
  if (!regex.test(lineText)) continue;
@@ -3358,34 +4294,45 @@ function fallbackGrep(input) {
3358
4294
  if (matchCount >= input.limit) limitHit = true;
3359
4295
  }
3360
4296
  };
3361
- const walk = (dir) => {
4297
+ const walk = async (dir) => {
4298
+ await yieldToLoop();
3362
4299
  if (limitHit || signal?.aborted) return;
3363
4300
  let entries;
3364
4301
  try {
3365
- entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
4302
+ entries = (await readdir2(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
3366
4303
  } catch {
4304
+ skipSearchPath(search, dir, input.pathFilter.allows);
3367
4305
  return;
3368
4306
  }
3369
4307
  for (const entry of entries) {
3370
4308
  if (limitHit || signal?.aborted) return;
3371
- const absPath = join2(dir, entry.name);
4309
+ const absPath = join3(dir, entry.name);
4310
+ if (!input.pathFilter.allows(absPath)) continue;
3372
4311
  if (entry.isDirectory()) {
3373
4312
  if (ignored.has(entry.name)) continue;
3374
- walk(absPath);
4313
+ await walk(absPath);
4314
+ continue;
4315
+ }
4316
+ if (!entry.isFile()) {
4317
+ skipSearchPath(search, absPath, input.pathFilter.allows);
3375
4318
  continue;
3376
4319
  }
3377
- if (!entry.isFile()) continue;
3378
4320
  if (globMatcher) {
3379
4321
  const relPath = normalizeGlobInput(relative2(input.searchPath, absPath));
3380
4322
  if (!globMatcher.test(relPath)) continue;
3381
4323
  }
3382
- searchFile(absPath);
4324
+ await searchFile(absPath);
3383
4325
  }
3384
4326
  };
3385
- if (input.isDirectory) walk(input.searchPath);
3386
- else searchFile(input.searchPath);
3387
- if (signal?.aborted) return { kind: "error", message: "grep: operation aborted" };
4327
+ if (input.isDirectory) await walk(input.searchPath);
4328
+ else await searchFile(input.searchPath);
4329
+ if (signal?.aborted || limitHit) {
4330
+ search.complete = false;
4331
+ search.reason = signal?.aborted ? "cancelled" : "limit";
4332
+ }
3388
4333
  return renderGrepResult({
4334
+ fallback: true,
4335
+ search,
3389
4336
  withheldPaths: input.pathFilter.withheldPaths,
3390
4337
  mode: input.mode,
3391
4338
  lines: rendered,
@@ -3399,7 +4346,7 @@ function fallbackGrep(input) {
3399
4346
  }
3400
4347
  var grepTool = {
3401
4348
  name: ToolNames.Grep,
3402
- description: `Search file contents with ripgrep. mode=content (default) returns matching lines with paths and line numbers, mode=files returns only matching file paths, mode=count returns per-file match counts. Respects .gitignore and skips generated dirs unless include_ignored=true. Capped at ${DEFAULT_LIMIT2} matches by default; truncated results say how to continue and where the full output was saved.`,
4349
+ description: `Search file contents with ripgrep. mode=content (default) returns matching lines with paths and line numbers, mode=files returns only matching file paths, mode=count returns per-file match counts. Respects .gitignore and skips generated dirs unless include_ignored=true. Without rg, the async fallback uses GENERATED_DIRS only and does not apply .gitignore; unreadable, binary, oversized files and symlinks are reported as skipped. Capped at ${DEFAULT_LIMIT2} matches by default; truncated results say how to continue and where the full output was saved.`,
3403
4350
  parameters: typebox_exports.Object({
3404
4351
  pattern: typebox_exports.String({ description: "Search pattern (regex by default)." }),
3405
4352
  path: typebox_exports.Optional(typebox_exports.String({ description: "Directory or file to search." })),
@@ -3425,8 +4372,8 @@ var grepTool = {
3425
4372
  const context = parseContext(args.context);
3426
4373
  if (context === null) return { kind: "error", message: "grep: context must be a non-negative number" };
3427
4374
  const searchPath = resolveReadPath(typeof args.path === "string" && args.path.length > 0 ? args.path : ".");
3428
- const stat = statIsDirectory(searchPath);
3429
- if (!stat.ok) return { kind: "error", message: `grep: ${stat.message}` };
4375
+ const stat6 = statIsDirectory(searchPath);
4376
+ if (!stat6.ok) return { kind: "error", message: `grep: ${stat6.message}` };
3430
4377
  const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : DEFAULT_LIMIT2;
3431
4378
  const glob = typeof args.glob === "string" && args.glob.length > 0 ? args.glob : void 0;
3432
4379
  const ignoreCase = args.ignore_case === true;
@@ -3448,7 +4395,7 @@ var grepTool = {
3448
4395
  mode,
3449
4396
  pattern,
3450
4397
  searchPath,
3451
- isDirectory: stat.isDirectory,
4398
+ isDirectory: stat6.isDirectory,
3452
4399
  ...glob !== void 0 ? { glob } : {},
3453
4400
  ignoreCase,
3454
4401
  literal,
@@ -3462,7 +4409,7 @@ var grepTool = {
3462
4409
  try {
3463
4410
  const rgPath = resolveRgBinary();
3464
4411
  if (rgPath) return await runRipgrep({ rgPath, ...shared });
3465
- return fallbackGrep(shared);
4412
+ return await fallbackGrep(shared);
3466
4413
  } finally {
3467
4414
  releaseObservation(reservation);
3468
4415
  }
@@ -3675,15 +4622,62 @@ var limitationTool = {
3675
4622
 
3676
4623
  // src/tools/ls.ts
3677
4624
  init_esm_shims();
3678
- import { readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
4625
+ import { lstat as lstat2, opendir, readlink as readlink2, stat as stat4 } from "node:fs/promises";
3679
4626
  import path3 from "node:path";
4627
+ import { setImmediate as yieldToLoop2 } from "node:timers/promises";
3680
4628
  var DEFAULT_LIMIT3 = 500;
3681
4629
  function parseLimit(value) {
3682
- return typeof value === "number" && value > 0 ? Math.floor(value) : DEFAULT_LIMIT3;
3683
- }
4630
+ return typeof value === "number" && Number.isFinite(value) && value >= 1 ? Math.floor(value) : DEFAULT_LIMIT3;
4631
+ }
4632
+ var compareEntries = (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()) || a.order - b.order;
4633
+ var BoundedListingSelection = class {
4634
+ constructor(limit) {
4635
+ this.limit = limit;
4636
+ }
4637
+ limit;
4638
+ entries = [];
4639
+ nextOrder = 0;
4640
+ get retained() {
4641
+ return this.entries.length;
4642
+ }
4643
+ entryAt(index) {
4644
+ const entry = this.entries[index];
4645
+ if (!entry) throw new Error("Invalid listing heap index");
4646
+ return entry;
4647
+ }
4648
+ add(name) {
4649
+ const entry = { name, order: this.nextOrder++ };
4650
+ if (this.entries.length < this.limit) {
4651
+ this.entries.push(entry);
4652
+ let i2 = this.entries.length - 1;
4653
+ while (i2 > 0) {
4654
+ const parent = Math.floor((i2 - 1) / 2);
4655
+ if (compareEntries(this.entryAt(parent), entry) >= 0) break;
4656
+ this.entries[i2] = this.entryAt(parent);
4657
+ i2 = parent;
4658
+ }
4659
+ this.entries[i2] = entry;
4660
+ return;
4661
+ }
4662
+ if (compareEntries(entry, this.entryAt(0)) >= 0) return;
4663
+ this.entries[0] = entry;
4664
+ let i = 0;
4665
+ while (2 * i + 1 < this.entries.length) {
4666
+ let child = 2 * i + 1;
4667
+ if (child + 1 < this.entries.length && compareEntries(this.entryAt(child + 1), this.entryAt(child)) > 0) child++;
4668
+ if (compareEntries(entry, this.entryAt(child)) >= 0) break;
4669
+ this.entries[i] = this.entryAt(child);
4670
+ i = child;
4671
+ }
4672
+ this.entries[i] = entry;
4673
+ }
4674
+ sorted() {
4675
+ return [...this.entries].sort(compareEntries).map((entry) => entry.name);
4676
+ }
4677
+ };
3684
4678
  var lsTool = {
3685
4679
  name: ToolNames.Ls,
3686
- description: 'List directory entries sorted alphabetically, "/" suffix for directories, dotfiles included.',
4680
+ description: 'List directory entries sorted alphabetically, "/" suffix for directories, dotfiles included. Symlinks render as name@ -> target, broken links as name@ (broken), and inaccessible entries are marked and counted.',
3687
4681
  parameters: typebox_exports.Object({
3688
4682
  path: typebox_exports.Optional(typebox_exports.String({ description: "Directory to list." })),
3689
4683
  limit: typebox_exports.Optional(typebox_exports.Number({ description: `Max entries (default ${DEFAULT_LIMIT3}).` }))
@@ -3695,7 +4689,7 @@ var lsTool = {
3695
4689
  const root = resolveReadPath(rootArg);
3696
4690
  const limit = parseLimit(args.limit);
3697
4691
  try {
3698
- const rootStat = statSync3(root);
4692
+ const rootStat = await stat4(root);
3699
4693
  if (!rootStat.isDirectory()) {
3700
4694
  return { kind: "error", message: `ls: not a directory: ${root}` };
3701
4695
  }
@@ -3713,28 +4707,57 @@ var lsTool = {
3713
4707
  hint: "Use find with a narrower pattern or continue in a follow-up turn."
3714
4708
  });
3715
4709
  }
4710
+ commitObservationReservation(reservation);
3716
4711
  try {
3717
- const pathFilter = createObservationPathFilter(process.cwd(), options?.allowsObservationPath);
3718
- const entries = readdirSync2(root).filter((entry) => pathFilter.allows(path3.join(root, entry))).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
4712
+ const policy = options?.allowsObservationPath ? void 0 : createSafetyPolicyEngine({ cwd: process.cwd() });
4713
+ const allows = options?.allowsObservationPath ?? ((entryPath) => policy?.evaluate({ tool: "read", args: { path: entryPath } }).reasonCode !== "path-policy:zeroAccessPaths");
4714
+ let withheldPaths = 0;
4715
+ let visibleCount = 0;
4716
+ let scanned = 0;
4717
+ const selection = new BoundedListingSelection(limit);
4718
+ for await (const entry of await opendir(root)) {
4719
+ if (++scanned % 128 === 0) await yieldToLoop2();
4720
+ if (options?.signal?.aborted) return { kind: "error", message: "ls: operation aborted during enumeration" };
4721
+ if (!allows(path3.join(root, entry.name))) {
4722
+ withheldPaths++;
4723
+ continue;
4724
+ }
4725
+ visibleCount++;
4726
+ selection.add(entry.name);
4727
+ }
4728
+ const entries = selection.sorted();
3719
4729
  const outputEntries = [];
3720
- let entryLimitReached = false;
4730
+ const entryLimitReached = visibleCount > limit;
4731
+ const search = newSearchCompleteness();
3721
4732
  for (const entry of entries) {
3722
- if (outputEntries.length >= limit) {
3723
- entryLimitReached = true;
3724
- break;
3725
- }
4733
+ const entryPath = path3.join(root, entry);
3726
4734
  try {
3727
- const entryStat = statSync3(path3.join(root, entry));
3728
- outputEntries.push(entryStat.isDirectory() ? `${entry}/` : entry);
4735
+ const entryStat = await lstat2(entryPath);
4736
+ if (entryStat.isSymbolicLink()) {
4737
+ const target = await readlink2(entryPath);
4738
+ try {
4739
+ await stat4(entryPath);
4740
+ outputEntries.push(`${entry}@ -> ${target}`);
4741
+ } catch (error) {
4742
+ const code = error.code;
4743
+ outputEntries.push(`${entry}@ (${code === "ENOENT" || code === "ENOTDIR" ? "broken" : "unreadable"})`);
4744
+ skipSearchPath(search, entryPath);
4745
+ }
4746
+ } else {
4747
+ outputEntries.push(entryStat.isDirectory() ? `${entry}/` : entry);
4748
+ }
3729
4749
  } catch {
4750
+ outputEntries.push(`${entry} (unreadable)`);
4751
+ skipSearchPath(search, entryPath);
3730
4752
  }
3731
4753
  }
3732
4754
  if (outputEntries.length === 0) {
3733
4755
  return finalizeObservation({
3734
4756
  tool: ToolNames.Ls,
3735
- withheldPaths: pathFilter.withheldPaths,
4757
+ withheldPaths,
3736
4758
  unit: "entries",
3737
4759
  output: "(empty directory)",
4760
+ details: { skipped: search.skipped, selection: { scanned, retained: selection.retained } },
3738
4761
  shownCount: 0,
3739
4762
  totalCount: 0,
3740
4763
  truncated: false,
@@ -3750,14 +4773,15 @@ var lsTool = {
3750
4773
  const truncated = entryLimitReached || truncation.truncated;
3751
4774
  return finalizeObservation({
3752
4775
  tool: ToolNames.Ls,
3753
- withheldPaths: pathFilter.withheldPaths,
4776
+ withheldPaths,
3754
4777
  unit: "entries",
3755
4778
  output: truncation.content,
4779
+ details: { skipped: search.skipped, selection: { scanned, retained: selection.retained } },
3756
4780
  // Offload only when the byte cap cut collected entries; a bare
3757
4781
  // entry limit continues via `next`.
3758
4782
  ...truncation.truncated ? { fullOutput } : {},
3759
4783
  shownCount: truncation.outputLines,
3760
- totalCount: entries.length,
4784
+ totalCount: visibleCount,
3761
4785
  truncated,
3762
4786
  ...entryLimitReached ? { next: `limit=${limit * 2}` } : {},
3763
4787
  reservation,
@@ -3766,6 +4790,8 @@ var lsTool = {
3766
4790
  } catch (err) {
3767
4791
  const msg = err instanceof Error ? err.message : String(err);
3768
4792
  return { kind: "error", message: `ls: ${msg}` };
4793
+ } finally {
4794
+ releaseObservation(reservation);
3769
4795
  }
3770
4796
  }
3771
4797
  };
@@ -3781,6 +4807,12 @@ var TOOL_PLANES = {
3781
4807
  [ToolNames.CodeNav]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
3782
4808
  [ToolNames.Context]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
3783
4809
  [ToolNames.CredentialPresent]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
4810
+ // clio_docs and clio_library are the bundled-documentation and recipe-catalog
4811
+ // reads that used to be context scopes; same envelope, same read class.
4812
+ [ToolNames.ClioDocs]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
4813
+ [ToolNames.ClioLibrary]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
4814
+ // data streams structured files and never writes; parallel like read.
4815
+ [ToolNames.Data]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
3784
4816
  [ToolNames.Write]: { plane: "mutate", actionClass: "write", executionMode: "sequential" },
3785
4817
  [ToolNames.Edit]: { plane: "mutate", actionClass: "write", executionMode: "sequential" },
3786
4818
  [ToolNames.Bash]: { plane: "execute", actionClass: "execute", executionMode: "sequential" },
@@ -3788,6 +4820,9 @@ var TOOL_PLANES = {
3788
4820
  // its containment posture, read class for its safety disposition.
3789
4821
  [ToolNames.Git]: { plane: "execute", actionClass: "read", executionMode: "parallel" },
3790
4822
  [ToolNames.Verify]: { plane: "execute", actionClass: "execute", executionMode: "sequential" },
4823
+ // run_script runs one interpreter over one workspace script and projects
4824
+ // itself to a bash command for the policy engine; execute class, sequential.
4825
+ [ToolNames.RunScript]: { plane: "execute", actionClass: "execute", executionMode: "sequential" },
3791
4826
  [ToolNames.Dispatch]: { plane: "orchestrate", actionClass: "dispatch", executionMode: "sequential" },
3792
4827
  // monitor never mutates a run, so it stays read class and parallel.
3793
4828
  [ToolNames.Monitor]: { plane: "orchestrate", actionClass: "read", executionMode: "parallel" },
@@ -3814,9 +4849,17 @@ var TOOL_PLANES = {
3814
4849
  // nothing else. Read class so it never trips a safety gate; sequential so
3815
4850
  // two decisions in one batch never race the supersede lookup.
3816
4851
  [ToolNames.Decide]: { plane: "orchestrate", actionClass: "read", executionMode: "sequential" },
4852
+ // web_read is the GET-only half of the split: no method, headers, or body,
4853
+ // so it is never an outward action.
4854
+ [ToolNames.WebRead]: { plane: "retrieve", actionClass: "read", executionMode: "parallel" },
3817
4855
  [ToolNames.WebFetch]: { plane: "retrieve", actionClass: "read", executionMode: "parallel" },
3818
4856
  [ToolNames.AskUser]: { plane: "interact", actionClass: "read", executionMode: "sequential" },
3819
- [ToolNames.Artifact]: { plane: "artifact", actionClass: "write", executionMode: "sequential" }
4857
+ [ToolNames.Artifact]: { plane: "artifact", actionClass: "write", executionMode: "sequential" },
4858
+ // gateway lists, describes, and calls secondary capabilities. Its own class
4859
+ // is read; a call carries the capability's class through the same admission.
4860
+ // Sequential because a call may park for approval or run a sequential
4861
+ // capability, and two such calls in one batch must not interleave.
4862
+ [ToolNames.Gateway]: { plane: "gateway", actionClass: "read", executionMode: "sequential" }
3820
4863
  };
3821
4864
  var OBSERVE_ENVELOPE_SELF_CAPS = [
3822
4865
  [ToolNames.Read, () => readMaxBytes()],
@@ -3824,22 +4867,18 @@ var OBSERVE_ENVELOPE_SELF_CAPS = [
3824
4867
  [ToolNames.Find, () => OBSERVE_SELF_CAPS.find],
3825
4868
  [ToolNames.Ls, () => OBSERVE_SELF_CAPS.ls],
3826
4869
  [ToolNames.CodeNav, () => OBSERVE_SELF_CAPS.codeNav],
3827
- [
3828
- ToolNames.Context,
3829
- () => Math.max(
3830
- OBSERVE_SELF_CAPS.contextDocs,
3831
- OBSERVE_SELF_CAPS.contextSkills,
3832
- OBSERVE_SELF_CAPS.contextWorkspace,
3833
- OBSERVE_SELF_CAPS.contextLibrary
3834
- )
3835
- ]
4870
+ [ToolNames.Context, () => Math.max(OBSERVE_SELF_CAPS.contextSkills, OBSERVE_SELF_CAPS.contextWorkspace)],
4871
+ [ToolNames.ClioDocs, () => OBSERVE_SELF_CAPS.contextDocs],
4872
+ [ToolNames.ClioLibrary, () => OBSERVE_SELF_CAPS.contextLibrary],
4873
+ [ToolNames.Data, () => DATA_OBSERVATION_SELF_CAP_BYTES],
4874
+ [ToolNames.Gateway, () => GATEWAY_FIND_SELF_CAP_BYTES]
3836
4875
  ];
3837
4876
  var SESSION_BOUND_TOOLS = /* @__PURE__ */ new Set([]);
3838
4877
  var DISPATCH_BOUND_TOOLS = /* @__PURE__ */ new Set([ToolNames.Dispatch, ToolNames.Monitor, ToolNames.Steer]);
3839
4878
  var INTERACTIVE_BOUND_TOOLS = /* @__PURE__ */ new Set([ToolNames.AskUser]);
3840
4879
  var PANES_BOUND_TOOLS = /* @__PURE__ */ new Set([ToolNames.Panes]);
3841
4880
  var LEDGER_BOUND_TOOLS = /* @__PURE__ */ new Set([ToolNames.Ledger]);
3842
- var NETWORK_BOUND_TOOLS = /* @__PURE__ */ new Set([ToolNames.WebFetch]);
4881
+ var NETWORK_BOUND_TOOLS = /* @__PURE__ */ new Set([ToolNames.WebRead, ToolNames.WebFetch]);
3843
4882
  function validateBuiltinToolPolicy(specs, options = {}) {
3844
4883
  const errors = [];
3845
4884
  const registered = /* @__PURE__ */ new Map();
@@ -4138,11 +5177,10 @@ function createTasksTool(deps) {
4138
5177
 
4139
5178
  // src/tools/write.ts
4140
5179
  init_esm_shims();
4141
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
4142
- import { dirname } from "node:path";
5180
+ import { open as open2, stat as stat5 } from "node:fs/promises";
4143
5181
  var writeTool = {
4144
5182
  name: ToolNames.Write,
4145
- description: "Write a UTF-8 text file, creating parent directories and overwriting existing files. Use edit for surgical changes.",
5183
+ description: "Write a UTF-8 text file, creating parent directories and overwriting existing files. Publishes atomically through symlinks, preserving mode bits. In-process writes are serialized; external writers are not locked and the last rename wins. Use edit for surgical changes.",
4146
5184
  parameters: typebox_exports.Object({
4147
5185
  path: typebox_exports.String({ description: "File path (relative or absolute)." }),
4148
5186
  content: typebox_exports.String({ description: "Full UTF-8 file contents." })
@@ -4156,30 +5194,56 @@ var writeTool = {
4156
5194
  if (content === null) return { kind: "error", message: "write: missing content argument" };
4157
5195
  const filePath = resolveToCwd(pathArg);
4158
5196
  try {
4159
- let previousEndedWithNewline = false;
4160
- let previousContent = "";
4161
- await withFileMutationQueue(filePath, async () => {
4162
- try {
4163
- previousContent = readFileSync3(filePath, "utf8");
4164
- previousEndedWithNewline = previousContent.endsWith("\n");
4165
- } catch {
4166
- previousContent = "";
4167
- previousEndedWithNewline = false;
5197
+ const bytes = Buffer.byteLength(content, "utf8");
5198
+ const { file, diff, previousEndedWithNewline, skipDiff } = await withFileMutationQueue(filePath, async () => {
5199
+ const previous = await stat5(filePath).catch((error) => {
5200
+ if (error.code === "ENOENT") return null;
5201
+ throw error;
5202
+ });
5203
+ if (previous && !previous.isFile()) throw new Error("Refusing directory or non-file target");
5204
+ let skipDiff2 = Math.max(previous?.size ?? 0, bytes) > 1024 * 1024;
5205
+ let previousContent = "";
5206
+ if (previous && !skipDiff2) {
5207
+ const handle = await open2(filePath, "r");
5208
+ try {
5209
+ const buffer = Buffer.allocUnsafe(1024 * 1024 + 1);
5210
+ let total = 0;
5211
+ while (total < buffer.length) {
5212
+ const { bytesRead } = await handle.read(buffer, total, buffer.length - total, total);
5213
+ if (bytesRead === 0) break;
5214
+ total += bytesRead;
5215
+ }
5216
+ skipDiff2 = total > 1024 * 1024;
5217
+ if (!skipDiff2) previousContent = buffer.toString("utf8", 0, total);
5218
+ } finally {
5219
+ await handle.close();
5220
+ }
4168
5221
  }
4169
- mkdirSync2(dirname(filePath), { recursive: true });
4170
- writeFileSync3(filePath, content, "utf8");
5222
+ const diff2 = skipDiff2 ? void 0 : generateDiffString(previousContent, content).diff;
5223
+ const file2 = await publishFileAtomically(filePath, content);
5224
+ return { file: file2, diff: diff2, previousEndedWithNewline: previousContent.endsWith("\n"), skipDiff: skipDiff2 };
4171
5225
  });
4172
- const bytes = Buffer.byteLength(content, "utf8");
4173
- const diff = generateDiffString(previousContent, content).diff;
4174
5226
  let output = `wrote ${bytes}B to ${pathArg}`;
5227
+ if (skipDiff) output += "\nnote: diff skipped because the previous or new file exceeds 1 MiB";
5228
+ if (file.durabilityWarning) output += `
5229
+ ${file.durabilityWarning}`;
4175
5230
  if (previousEndedWithNewline && !content.endsWith("\n")) {
4176
5231
  output += `
4177
5232
  note: ${pathArg} no longer ends with a newline; the previous content did`;
4178
5233
  }
4179
- return { kind: "ok", output, details: { diff, paths: [filePath], observation: { shownBytes: bytes } } };
5234
+ return {
5235
+ kind: "ok",
5236
+ output,
5237
+ details: {
5238
+ file: { before: file.before, after: file.after },
5239
+ diff,
5240
+ paths: [filePath],
5241
+ observation: { shownBytes: bytes }
5242
+ }
5243
+ };
4180
5244
  } catch (err) {
4181
5245
  const msg = err instanceof Error ? err.message : String(err);
4182
- return { kind: "error", message: `write: ${msg}` };
5246
+ return { kind: "error", message: `write: Nothing was published. ${msg}` };
4183
5247
  }
4184
5248
  }
4185
5249
  };
@@ -4212,7 +5276,13 @@ function registerCoreTools(registry, deps = {}) {
4212
5276
  if (includeNetworkTools) {
4213
5277
  registry.register({
4214
5278
  ...builtin(
4215
- lazyTool(webFetchToolSurface, async () => (await import("./web-fetch-U22Q7HV4.js")).webFetchTool),
5279
+ lazyTool(webReadToolSurface, async () => (await import("./web-fetch-6BRRVQON.js")).webReadTool),
5280
+ { path: "src/tools/web-fetch.ts", scope: "core" }
5281
+ )
5282
+ });
5283
+ registry.register({
5284
+ ...builtin(
5285
+ lazyTool(webFetchToolSurface, async () => (await import("./web-fetch-6BRRVQON.js")).webFetchTool),
4216
5286
  { path: "src/tools/web-fetch.ts", scope: "core" }
4217
5287
  )
4218
5288
  });
@@ -4222,13 +5292,35 @@ function registerCoreTools(registry, deps = {}) {
4222
5292
  });
4223
5293
  registry.register({
4224
5294
  ...builtin(
4225
- lazyTool(verifyToolSurface, async () => (await import("./verify-OZU4LLNP.js")).verifyTool),
5295
+ lazyTool(verifyToolSurface, async () => (await import("./verify-SUKUK6XM.js")).verifyTool),
4226
5296
  { path: "src/tools/verify/index.ts", scope: "core" }
4227
5297
  )
4228
5298
  });
5299
+ const getWorkspaceRoot = () => deps.session?.current()?.cwd ?? process.cwd();
5300
+ registry.register({
5301
+ ...builtin(
5302
+ lazyTool({ ...runScriptToolSurface, name: ToolNames.RunScript }, async () => ({
5303
+ ...(await import("./run-script-H2KUDIII.js")).createRunScriptTool({ getWorkspaceRoot }),
5304
+ name: ToolNames.RunScript
5305
+ })),
5306
+ { path: "src/tools/run-script.ts", scope: "core" }
5307
+ )
5308
+ });
5309
+ registry.register({
5310
+ ...builtin(
5311
+ lazyTool(
5312
+ {
5313
+ ...dataToolSurface,
5314
+ prepareAdmissionArguments: (args) => prepareDataAdmissionArguments(args, getWorkspaceRoot())
5315
+ },
5316
+ async () => (await import("./data-tool-KMBWKKWR.js")).createDataTool({ getCwd: getWorkspaceRoot })
5317
+ ),
5318
+ { path: "src/tools/gateway/data-tool.ts", scope: "core" }
5319
+ )
5320
+ });
4229
5321
  registry.register({
4230
5322
  ...builtin(
4231
- lazyTool(codeNavToolSurface, async () => (await import("./code-nav-KBQ3RIFK.js")).codeNavTool),
5323
+ lazyTool(codeNavToolSurface, async () => (await import("./code-nav-7OVCTP2Z.js")).codeNavTool),
4232
5324
  { path: "src/tools/codewiki/code-nav.ts", scope: "core" }
4233
5325
  )
4234
5326
  });
@@ -4263,7 +5355,7 @@ function registerCoreTools(registry, deps = {}) {
4263
5355
  registry.register({
4264
5356
  ...builtin(
4265
5357
  lazyTool(contextToolSurface, async () => {
4266
- const { createContextTool } = await import("./context-EZZPT2ZV.js");
5358
+ const { createContextTool } = await import("./context-72VQV7JX.js");
4267
5359
  if (!session) return createContextTool(skillToolDeps);
4268
5360
  const { probeWorkspace } = await import("./workspace-DZ7JADBG.js");
4269
5361
  return createContextTool({
@@ -4297,6 +5389,31 @@ function registerCoreTools(registry, deps = {}) {
4297
5389
  registry.register({
4298
5390
  ...builtin(createArtifactTool({ getCwd: skillToolDeps.getCwd }), { path: "src/tools/artifact.ts", scope: "core" })
4299
5391
  });
5392
+ registry.register({
5393
+ ...builtin(
5394
+ lazyTool(clioDocsToolSurface, async () => (await import("./clio-context-tools-FT672MNZ.js")).createClioDocsTool()),
5395
+ { path: "src/tools/gateway/clio-context-tools.ts", scope: "core" }
5396
+ )
5397
+ });
5398
+ registry.register({
5399
+ ...builtin(
5400
+ lazyTool(
5401
+ clioLibraryToolSurface,
5402
+ async () => (await import("./clio-context-tools-FT672MNZ.js")).createClioLibraryTool({
5403
+ getCwd: skillToolDeps.getCwd,
5404
+ ...deps.skillMarketplace !== void 0 ? { skillMarketplace: deps.skillMarketplace } : {},
5405
+ ...deps.getSkillLoaderOptions ? { getSkillLoaderOptions: deps.getSkillLoaderOptions } : {}
5406
+ })
5407
+ ),
5408
+ { path: "src/tools/gateway/clio-context-tools.ts", scope: "core" }
5409
+ )
5410
+ });
5411
+ registry.register({
5412
+ ...builtin(createGatewayTool({ registry, ...deps.mcpCapabilities ? { mcp: deps.mcpCapabilities } : {} }), {
5413
+ path: "src/tools/gateway/index.ts",
5414
+ scope: "core"
5415
+ })
5416
+ });
4300
5417
  const includeLedgerTools = deps.includeLedgerTools === true || deps.agentLedger !== void 0;
4301
5418
  if (includeLedgerTools) {
4302
5419
  registry.register({
@@ -4335,7 +5452,7 @@ init_esm_shims();
4335
5452
  import path4 from "node:path";
4336
5453
  var import_yaml = __toESM(require_dist(), 1);
4337
5454
  var MAX_INPUT_BYTES = 64 * 1024;
4338
- function quoted(value) {
5455
+ function quoted2(value) {
4339
5456
  return `'${value.replaceAll("'", "'\\''")}'`;
4340
5457
  }
4341
5458
  function jsonInput(args) {
@@ -4374,6 +5491,9 @@ Harness extension ${extension.id}@${extension.version}; runs an installed comman
4374
5491
  parameters: tool.inputSchema,
4375
5492
  baseActionClass: "execute",
4376
5493
  executionMode: "sequential",
5494
+ // Reached through gateway find/describe/call; the spec, its execute
5495
+ // class, and its bash projection are unchanged by the placement.
5496
+ placement: "gateway",
4377
5497
  sourceInfo: { path: extension.manifestPath, scope: "domain", extension: extension.provenance },
4378
5498
  metadata: {
4379
5499
  objective: tool.description,
@@ -4388,7 +5508,7 @@ Harness extension ${extension.id}@${extension.version}; runs an installed comman
4388
5508
  input = jsonInput(args);
4389
5509
  } catch {
4390
5510
  }
4391
- return { tool: "bash", args: { command: [vector.file, ...vector.args, input].map(quoted).join(" "), cwd } };
5511
+ return { tool: "bash", args: { command: [vector.file, ...vector.args, input].map(quoted2).join(" "), cwd } };
4392
5512
  },
4393
5513
  async run(args, options) {
4394
5514
  try {
@@ -4485,9 +5605,10 @@ export {
4485
5605
  stripBom,
4486
5606
  applyEditsToNormalizedContent,
4487
5607
  generateDiffString,
5608
+ createMcpCapabilitySource,
4488
5609
  lazyTool,
4489
5610
  registerCoreTools,
4490
5611
  assertRegisteredBuiltinTools,
4491
5612
  registerHarnessExtensionTools
4492
5613
  };
4493
- //# sourceMappingURL=chunk-RXI7K4J5.js.map
5614
+ //# sourceMappingURL=chunk-ZF6CV4WZ.js.map