@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
@@ -0,0 +1,1799 @@
1
+ import {
2
+ cutView,
3
+ type DataRefusal,
4
+ DataRefusalError,
5
+ type DataViewFlags,
6
+ exactView,
7
+ MAX_NUMBER_LITERAL_CHARS,
8
+ numberLiteralPrecision,
9
+ type PrecisionIssue,
10
+ refusal,
11
+ refusalFromError,
12
+ resolveMaxRows,
13
+ sampledNotice,
14
+ sampledView,
15
+ statDataFile,
16
+ streamTextChunks,
17
+ type TextStreamStats,
18
+ throwIfAborted,
19
+ } from "./shared.js";
20
+
21
+ /**
22
+ * Streaming JSON reader. A hand-written push parser walks a document as
23
+ * events, so a multi-gigabyte array can be inspected or selected from without
24
+ * being loaded, and numbers keep their source text so precision loss is
25
+ * reported instead of silently rounded. `JSON.parse` never touches a whole
26
+ * data file here.
27
+ */
28
+
29
+ export type JsonValueType = "object" | "array" | "string" | "number" | "boolean" | "null";
30
+
31
+ export interface JsonScalarEvent {
32
+ type: "string" | "number" | "boolean" | "null";
33
+ /** Parsed value; a string that was not captured is "" with `captured: false`. */
34
+ value: string | number | boolean | null;
35
+ /** Source text for numbers; null otherwise. */
36
+ literal: string | null;
37
+ /** False when the string content was skipped because no consumer needed it. */
38
+ captured: boolean;
39
+ /** Decoded length for strings (even when not captured), 0 otherwise. */
40
+ length: number;
41
+ /** True when a captured string or literal was cut at its capture cap. */
42
+ truncated: boolean;
43
+ }
44
+
45
+ /**
46
+ * The event consumer. `depth` is the nesting level of the value the event
47
+ * belongs to: the root value is depth 0, its members and elements depth 1.
48
+ * Each callback may return false to stop the parse after that event; the
49
+ * parser then reports `stopped: true` and reads nothing further.
50
+ */
51
+ export interface JsonEventSink {
52
+ onStartObject(depth: number): boolean | undefined;
53
+ onKey(key: string, depth: number, truncated: boolean): boolean | undefined;
54
+ onEndObject(depth: number, memberCount: number): boolean | undefined;
55
+ onStartArray(depth: number): boolean | undefined;
56
+ onEndArray(depth: number, length: number): boolean | undefined;
57
+ onScalar(scalar: JsonScalarEvent, depth: number): boolean | undefined;
58
+ /** Consulted at the start of each string value: capture its content or only measure it. */
59
+ captureStrings(depth: number): boolean;
60
+ }
61
+
62
+ export const JSON_MAX_DEPTH = 1024;
63
+ const KEY_CAPTURE_CAP = 1024;
64
+ const STRING_CAPTURE_CAP = 65_536;
65
+
66
+ enum TokenState {
67
+ None = 0,
68
+ String = 1,
69
+ Number = 2,
70
+ Literal = 3,
71
+ }
72
+
73
+ enum Expect {
74
+ Value = 0,
75
+ KeyOrEnd = 1,
76
+ Key = 2,
77
+ Colon = 3,
78
+ CommaOrEnd = 4,
79
+ ValueOrEnd = 5,
80
+ End = 6,
81
+ }
82
+
83
+ interface Container {
84
+ kind: "object" | "array";
85
+ count: number;
86
+ expect: Expect;
87
+ }
88
+
89
+ const CHAR_QUOTE = 0x22;
90
+ const CHAR_BACKSLASH = 0x5c;
91
+ const CHAR_LF = 0x0a;
92
+
93
+ function escapeChar(code: number): string | null {
94
+ switch (code) {
95
+ case CHAR_QUOTE:
96
+ return '"';
97
+ case CHAR_BACKSLASH:
98
+ return "\\";
99
+ case 0x2f:
100
+ return "/";
101
+ case 0x62:
102
+ return "\b";
103
+ case 0x66:
104
+ return "\f";
105
+ case 0x6e:
106
+ return "\n";
107
+ case 0x72:
108
+ return "\r";
109
+ case 0x74:
110
+ return "\t";
111
+ default:
112
+ return null;
113
+ }
114
+ }
115
+
116
+ function isWhitespace(code: number): boolean {
117
+ return code === 0x20 || code === 0x09 || code === CHAR_LF || code === 0x0d;
118
+ }
119
+
120
+ function isNumberChar(code: number): boolean {
121
+ return (
122
+ (code >= 0x30 && code <= 0x39) || code === 0x2d || code === 0x2b || code === 0x2e || code === 0x45 || code === 0x65
123
+ );
124
+ }
125
+
126
+ const NUMBER_GRAMMAR = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/u;
127
+
128
+ /**
129
+ * Incremental JSON parser. Feed decoded text with `push`, finish with `end`.
130
+ * Syntax errors throw an `invalid-json` refusal carrying line, column, and
131
+ * byte offset; nesting past JSON_MAX_DEPTH throws `nesting-too-deep`.
132
+ */
133
+ export class JsonStreamParser {
134
+ private readonly stack: Container[] = [];
135
+ private expect = Expect.Value;
136
+ private tokenState = TokenState.None;
137
+ private token = "";
138
+ private tokenTruncated = false;
139
+ private stringCapture = false;
140
+ private stringLength = 0;
141
+ private stringIsKey = false;
142
+ private escape = false;
143
+ private unicodeHex: string | null = null;
144
+ private line = 1;
145
+ private column = 0;
146
+ private chunkText = "";
147
+ private chunkByteOffset = 0;
148
+ private chunkIndex = 0;
149
+ private stopped = false;
150
+ private rootComplete = false;
151
+ /** Depth of the deepest container opened so far (root object or array is 0). */
152
+ maxDepth = 0;
153
+
154
+ constructor(private readonly sink: JsonEventSink) {}
155
+
156
+ get isStopped(): boolean {
157
+ return this.stopped;
158
+ }
159
+
160
+ get depth(): number {
161
+ return this.stack.length;
162
+ }
163
+
164
+ /** Feed one chunk. Returns false once a sink asked to stop; further pushes are ignored. */
165
+ push(text: string, byteOffset = 0): boolean {
166
+ if (this.stopped) return false;
167
+ this.chunkText = text;
168
+ this.chunkByteOffset = byteOffset;
169
+ const length = text.length;
170
+ let index = 0;
171
+ while (index < length) {
172
+ this.chunkIndex = index;
173
+ const code = text.charCodeAt(index);
174
+ if (code === CHAR_LF) {
175
+ this.line += 1;
176
+ this.column = 0;
177
+ } else {
178
+ this.column += 1;
179
+ }
180
+ switch (this.tokenState) {
181
+ case TokenState.String:
182
+ index = this.scanString(text, index, length);
183
+ break;
184
+ case TokenState.Number:
185
+ if (isNumberChar(code)) {
186
+ this.appendNumberChar(text[index] as string);
187
+ index += 1;
188
+ } else {
189
+ // The terminator belongs to the next token; undo its position bookkeeping.
190
+ this.column -= 1;
191
+ if (code === CHAR_LF) {
192
+ this.line -= 1;
193
+ this.column = 0;
194
+ }
195
+ this.completeNumber();
196
+ this.tokenState = TokenState.None;
197
+ }
198
+ break;
199
+ case TokenState.Literal:
200
+ if (code >= 0x61 && code <= 0x7a) {
201
+ this.token += text[index] as string;
202
+ index += 1;
203
+ if (this.token.length >= 5) this.completeLiteral();
204
+ } else {
205
+ this.column -= 1;
206
+ if (code === CHAR_LF) {
207
+ this.line -= 1;
208
+ this.column = 0;
209
+ }
210
+ this.completeLiteral();
211
+ }
212
+ break;
213
+ default:
214
+ index = this.scanStructural(text, index, code);
215
+ break;
216
+ }
217
+ if (this.stopped) return false;
218
+ }
219
+ return true;
220
+ }
221
+
222
+ end(): void {
223
+ if (this.stopped) return;
224
+ this.chunkIndex = this.chunkText.length;
225
+ if (this.tokenState === TokenState.Number) {
226
+ this.completeNumber();
227
+ this.tokenState = TokenState.None;
228
+ } else if (this.tokenState === TokenState.Literal) {
229
+ this.completeLiteral();
230
+ } else if (this.tokenState === TokenState.String) {
231
+ throw this.error("unterminated string at end of input");
232
+ }
233
+ if (this.stopped) return;
234
+ if (!this.rootComplete) {
235
+ throw this.error(this.stack.length === 0 ? "unexpected end of input: no JSON value" : "unexpected end of input");
236
+ }
237
+ }
238
+
239
+ private scanStructural(text: string, index: number, code: number): number {
240
+ if (isWhitespace(code)) return index + 1;
241
+ if (this.expect === Expect.End) throw this.error("unexpected content after the JSON value");
242
+ switch (code) {
243
+ case CHAR_QUOTE: {
244
+ const isKey = this.expect === Expect.Key || this.expect === Expect.KeyOrEnd;
245
+ if (!isKey && this.expect !== Expect.Value && this.expect !== Expect.ValueOrEnd) {
246
+ throw this.error(this.describeExpectation());
247
+ }
248
+ this.tokenState = TokenState.String;
249
+ this.stringIsKey = isKey;
250
+ this.stringCapture = isKey || this.sink.captureStrings(this.stack.length);
251
+ this.token = "";
252
+ this.tokenTruncated = false;
253
+ this.stringLength = 0;
254
+ this.escape = false;
255
+ this.unicodeHex = null;
256
+ return index + 1;
257
+ }
258
+ case 0x7b: // {
259
+ this.requireValueExpectation();
260
+ this.openContainer("object");
261
+ return index + 1;
262
+ case 0x5b: // [
263
+ this.requireValueExpectation();
264
+ this.openContainer("array");
265
+ return index + 1;
266
+ case 0x7d: // }
267
+ this.closeContainer("object");
268
+ return index + 1;
269
+ case 0x5d: // ]
270
+ this.closeContainer("array");
271
+ return index + 1;
272
+ case 0x3a: // :
273
+ if (this.expect !== Expect.Colon) throw this.error(this.describeExpectation());
274
+ this.expect = Expect.Value;
275
+ return index + 1;
276
+ case 0x2c: {
277
+ // ,
278
+ if (this.expect !== Expect.CommaOrEnd) throw this.error(this.describeExpectation());
279
+ const top = this.stack[this.stack.length - 1] as Container;
280
+ top.expect = top.kind === "object" ? Expect.Key : Expect.Value;
281
+ this.expect = top.expect;
282
+ return index + 1;
283
+ }
284
+ default:
285
+ this.requireValueExpectation();
286
+ if (isNumberChar(code) && code !== 0x2b && code !== 0x2e && code !== 0x45 && code !== 0x65) {
287
+ this.tokenState = TokenState.Number;
288
+ this.token = text[index] as string;
289
+ this.tokenTruncated = false;
290
+ return index + 1;
291
+ }
292
+ if (code >= 0x61 && code <= 0x7a) {
293
+ this.tokenState = TokenState.Literal;
294
+ this.token = text[index] as string;
295
+ return index + 1;
296
+ }
297
+ throw this.error(`unexpected character ${JSON.stringify(text[index])}`);
298
+ }
299
+ }
300
+
301
+ private scanString(text: string, index: number, length: number): number {
302
+ // Fast path: consume a run of ordinary characters in one slice.
303
+ if (!this.escape && this.unicodeHex === null) {
304
+ let end = index;
305
+ while (end < length) {
306
+ const code = text.charCodeAt(end);
307
+ if (code === CHAR_QUOTE || code === CHAR_BACKSLASH || code < 0x20) break;
308
+ end += 1;
309
+ }
310
+ if (end > index) {
311
+ const run = end - index;
312
+ this.stringLength += run;
313
+ if (this.stringCapture) this.captureString(text.slice(index, end));
314
+ // Position bookkeeping for the run beyond its first character, which push counted.
315
+ this.column += run - 1;
316
+ if (end >= length) return end;
317
+ // The terminator is a quote, a backslash, or a control character; a
318
+ // fault on it must point at it, not at the run's first character.
319
+ index = end;
320
+ this.chunkIndex = index;
321
+ this.column += 1;
322
+ }
323
+ }
324
+ const code = text.charCodeAt(index);
325
+ if (this.unicodeHex !== null) {
326
+ if (!((code >= 0x30 && code <= 0x39) || (code >= 0x41 && code <= 0x46) || (code >= 0x61 && code <= 0x66))) {
327
+ throw this.error("invalid \\u escape in string");
328
+ }
329
+ this.unicodeHex += text[index] as string;
330
+ if (this.unicodeHex.length === 4) {
331
+ const decoded = String.fromCharCode(Number.parseInt(this.unicodeHex, 16));
332
+ this.unicodeHex = null;
333
+ this.stringLength += 1;
334
+ if (this.stringCapture) this.captureString(decoded);
335
+ }
336
+ return index + 1;
337
+ }
338
+ if (this.escape) {
339
+ this.escape = false;
340
+ if (code === 0x75) {
341
+ this.unicodeHex = "";
342
+ return index + 1;
343
+ }
344
+ const decoded = escapeChar(code);
345
+ if (decoded === null) throw this.error(`invalid escape \\${text[index]} in string`);
346
+ this.stringLength += 1;
347
+ if (this.stringCapture) this.captureString(decoded);
348
+ return index + 1;
349
+ }
350
+ if (code === CHAR_QUOTE) {
351
+ this.completeString();
352
+ return index + 1;
353
+ }
354
+ if (code === CHAR_BACKSLASH) {
355
+ this.escape = true;
356
+ return index + 1;
357
+ }
358
+ if (code < 0x20) throw this.error("unescaped control character in string");
359
+ this.stringLength += 1;
360
+ if (this.stringCapture) this.captureString(text[index] as string);
361
+ return index + 1;
362
+ }
363
+
364
+ private captureString(piece: string): void {
365
+ const cap = this.stringIsKey ? KEY_CAPTURE_CAP : STRING_CAPTURE_CAP;
366
+ if (this.token.length >= cap) {
367
+ this.tokenTruncated = true;
368
+ return;
369
+ }
370
+ if (this.token.length + piece.length <= cap) {
371
+ this.token += piece;
372
+ return;
373
+ }
374
+ this.token += piece.slice(0, cap - this.token.length);
375
+ this.tokenTruncated = true;
376
+ }
377
+
378
+ private completeString(): void {
379
+ this.tokenState = TokenState.None;
380
+ if (this.stringIsKey) {
381
+ const top = this.stack[this.stack.length - 1] as Container;
382
+ top.expect = Expect.Colon;
383
+ this.expect = Expect.Colon;
384
+ this.dispatch(this.sink.onKey(this.token, this.stack.length, this.tokenTruncated));
385
+ return;
386
+ }
387
+ this.completeValue({
388
+ type: "string",
389
+ value: this.token,
390
+ literal: null,
391
+ captured: this.stringCapture,
392
+ length: this.stringLength,
393
+ truncated: this.tokenTruncated,
394
+ });
395
+ }
396
+
397
+ private appendNumberChar(char: string): void {
398
+ if (this.token.length >= MAX_NUMBER_LITERAL_CHARS) {
399
+ this.tokenTruncated = true;
400
+ return;
401
+ }
402
+ this.token += char;
403
+ }
404
+
405
+ private completeNumber(): void {
406
+ if (!this.tokenTruncated && !NUMBER_GRAMMAR.test(this.token)) {
407
+ throw this.error(`invalid number literal ${JSON.stringify(this.token)}`);
408
+ }
409
+ const literal = this.tokenTruncated ? `${this.token}…` : this.token;
410
+ this.completeValue({
411
+ type: "number",
412
+ value: this.tokenTruncated ? Number.NaN : Number(this.token),
413
+ literal,
414
+ captured: true,
415
+ length: 0,
416
+ truncated: this.tokenTruncated,
417
+ });
418
+ }
419
+
420
+ private completeLiteral(): void {
421
+ this.tokenState = TokenState.None;
422
+ const scalar: JsonScalarEvent = {
423
+ type: "null",
424
+ value: null,
425
+ literal: null,
426
+ captured: true,
427
+ length: 0,
428
+ truncated: false,
429
+ };
430
+ if (this.token === "true") {
431
+ scalar.type = "boolean";
432
+ scalar.value = true;
433
+ } else if (this.token === "false") {
434
+ scalar.type = "boolean";
435
+ scalar.value = false;
436
+ } else if (this.token !== "null") {
437
+ throw this.error(`unexpected literal ${JSON.stringify(this.token)}`);
438
+ }
439
+ this.completeValue(scalar);
440
+ }
441
+
442
+ private requireValueExpectation(): void {
443
+ if (this.expect !== Expect.Value && this.expect !== Expect.ValueOrEnd) throw this.error(this.describeExpectation());
444
+ }
445
+
446
+ private openContainer(kind: "object" | "array"): void {
447
+ if (this.stack.length >= JSON_MAX_DEPTH) {
448
+ throw new DataRefusalError(
449
+ refusal("nesting-too-deep", `JSON nesting exceeds ${JSON_MAX_DEPTH} levels at line ${this.line}`, {
450
+ line: this.line,
451
+ column: this.column,
452
+ byteOffset: this.byteOffset(),
453
+ }),
454
+ );
455
+ }
456
+ const depth = this.stack.length;
457
+ if (depth > this.maxDepth) this.maxDepth = depth;
458
+ const container: Container = { kind, count: 0, expect: kind === "object" ? Expect.KeyOrEnd : Expect.ValueOrEnd };
459
+ this.stack.push(container);
460
+ this.expect = container.expect;
461
+ this.dispatch(kind === "object" ? this.sink.onStartObject(depth) : this.sink.onStartArray(depth));
462
+ }
463
+
464
+ private closeContainer(kind: "object" | "array"): void {
465
+ const top = this.stack[this.stack.length - 1];
466
+ if (top === undefined || top.kind !== kind) throw this.error(`unexpected ${kind === "object" ? "}" : "]"}`);
467
+ const canClose =
468
+ this.expect === Expect.CommaOrEnd ||
469
+ (kind === "object" && this.expect === Expect.KeyOrEnd) ||
470
+ (kind === "array" && this.expect === Expect.ValueOrEnd);
471
+ if (!canClose) throw this.error(this.describeExpectation());
472
+ this.stack.pop();
473
+ const depth = this.stack.length;
474
+ this.dispatch(kind === "object" ? this.sink.onEndObject(depth, top.count) : this.sink.onEndArray(depth, top.count));
475
+ if (this.stopped) return;
476
+ this.afterValue();
477
+ }
478
+
479
+ private completeValue(scalar: JsonScalarEvent): void {
480
+ this.dispatch(this.sink.onScalar(scalar, this.stack.length));
481
+ if (this.stopped) return;
482
+ this.afterValue();
483
+ }
484
+
485
+ private afterValue(): void {
486
+ const top = this.stack[this.stack.length - 1];
487
+ if (top === undefined) {
488
+ this.rootComplete = true;
489
+ this.expect = Expect.End;
490
+ return;
491
+ }
492
+ top.count += 1;
493
+ top.expect = Expect.CommaOrEnd;
494
+ this.expect = Expect.CommaOrEnd;
495
+ }
496
+
497
+ private dispatch(verdict: boolean | undefined): void {
498
+ if (verdict === false) this.stopped = true;
499
+ }
500
+
501
+ private describeExpectation(): string {
502
+ switch (this.expect) {
503
+ case Expect.Value:
504
+ return "expected a JSON value";
505
+ case Expect.ValueOrEnd:
506
+ return "expected a JSON value or ']'";
507
+ case Expect.KeyOrEnd:
508
+ return "expected a string key or '}'";
509
+ case Expect.Key:
510
+ return "expected a string key";
511
+ case Expect.Colon:
512
+ return "expected ':' after object key";
513
+ case Expect.CommaOrEnd:
514
+ return `expected ',' or '${(this.stack[this.stack.length - 1] as Container).kind === "object" ? "}" : "]"}'`;
515
+ default:
516
+ return "unexpected content after the JSON value";
517
+ }
518
+ }
519
+
520
+ private byteOffset(): number {
521
+ return this.chunkByteOffset + Buffer.byteLength(this.chunkText.slice(0, this.chunkIndex), "utf8");
522
+ }
523
+
524
+ private error(message: string): DataRefusalError {
525
+ const byteOffset = this.byteOffset();
526
+ return new DataRefusalError(
527
+ refusal("invalid-json", `${message} at line ${this.line} column ${this.column} (byte offset ${byteOffset})`, {
528
+ line: this.line,
529
+ column: this.column,
530
+ byteOffset,
531
+ }),
532
+ );
533
+ }
534
+ }
535
+
536
+ /** A number whose double representation would misstate its literal keeps the literal in place. */
537
+ export interface JsonLiteralPlaceholder {
538
+ $literal: string;
539
+ precision: string;
540
+ }
541
+
542
+ /** Stands in for a subtree a builder could not carry within its budget. */
543
+ export interface JsonSummaryPlaceholder {
544
+ $summary: { type: JsonValueType; members?: number; length?: number; reason: "budget" };
545
+ }
546
+
547
+ /** A string cut at the capture cap: its true length and the prefix that was kept. */
548
+ export interface JsonTruncatedString {
549
+ $truncated: "string";
550
+ length: number;
551
+ text: string;
552
+ }
553
+
554
+ export type JsonValue =
555
+ | null
556
+ | boolean
557
+ | number
558
+ | string
559
+ | JsonValue[]
560
+ | { [key: string]: JsonValue }
561
+ | JsonLiteralPlaceholder
562
+ | JsonSummaryPlaceholder
563
+ | JsonTruncatedString;
564
+
565
+ interface BuildFrame {
566
+ kind: "object" | "array";
567
+ value: Record<string, JsonValue> | JsonValue[];
568
+ key: string | null;
569
+ /** Members or elements seen, including those dropped after the budget ran out. */
570
+ count: number;
571
+ }
572
+
573
+ /**
574
+ * Materialize one subtree from events under a character budget. Once the
575
+ * budget is spent, the subtree's root becomes a `$summary` placeholder and the
576
+ * builder keeps counting so the summary reports the true member count.
577
+ * Numbers with a precision issue become `$literal` placeholders; `issues`
578
+ * collects them with their pointer relative to the subtree root.
579
+ */
580
+ export class JsonValueBuilder {
581
+ private readonly frames: BuildFrame[] = [];
582
+ private spent = 0;
583
+ private exceeded = false;
584
+ private summaryType: "object" | "array" = "object";
585
+ private result: JsonValue | undefined;
586
+ private done = false;
587
+ private stringsCut = 0;
588
+ readonly issues: PrecisionIssue[] = [];
589
+
590
+ constructor(
591
+ private readonly budgetChars: number,
592
+ private readonly rootPointer: string,
593
+ ) {}
594
+
595
+ get complete(): boolean {
596
+ return this.done;
597
+ }
598
+
599
+ /** The budget ran out; the value is (or contains) a $summary placeholder. */
600
+ get overBudget(): boolean {
601
+ return this.exceeded;
602
+ }
603
+
604
+ /** Strings cut at the capture cap, each left as a $truncated placeholder. */
605
+ get cutStrings(): number {
606
+ return this.stringsCut;
607
+ }
608
+
609
+ /** Anything in the value stands in for content that was not carried. */
610
+ get truncated(): boolean {
611
+ return this.exceeded || this.stringsCut > 0;
612
+ }
613
+
614
+ get value(): JsonValue | undefined {
615
+ return this.result;
616
+ }
617
+
618
+ private pointer(): string {
619
+ let pointer = this.rootPointer;
620
+ for (const frame of this.frames) {
621
+ pointer +=
622
+ frame.kind === "object"
623
+ ? `/${(frame.key ?? "").replace(/~/gu, "~0").replace(/\//gu, "~1")}`
624
+ : `/${frame.value.length}`;
625
+ }
626
+ return pointer;
627
+ }
628
+
629
+ private charge(chars: number): boolean {
630
+ if (this.exceeded) return false;
631
+ this.spent += chars;
632
+ if (this.spent > this.budgetChars) {
633
+ this.exceeded = true;
634
+ return false;
635
+ }
636
+ return true;
637
+ }
638
+
639
+ private place(value: JsonValue): void {
640
+ const frame = this.frames[this.frames.length - 1];
641
+ if (frame === undefined) {
642
+ this.result = value;
643
+ this.done = true;
644
+ return;
645
+ }
646
+ if (frame.kind === "array") (frame.value as JsonValue[]).push(value);
647
+ else (frame.value as Record<string, JsonValue>)[frame.key ?? ""] = value;
648
+ frame.count += 1;
649
+ }
650
+
651
+ /**
652
+ * The budget ran out somewhere inside the subtree. Everything built so far
653
+ * is discarded and the whole subtree becomes one `$summary` placed when its
654
+ * root closes, carrying the root's true member count from the parser.
655
+ */
656
+ private exhaust(rootKind: "object" | "array"): void {
657
+ this.exceeded = true;
658
+ this.summaryType = this.frames[0]?.kind ?? rootKind;
659
+ this.frames.length = 0;
660
+ }
661
+
662
+ startContainer(kind: "object" | "array", _relativeDepth: number): void {
663
+ if (this.exceeded) return;
664
+ if (!this.charge(2)) {
665
+ this.exhaust(kind);
666
+ return;
667
+ }
668
+ this.frames.push({ kind, value: kind === "object" ? {} : [], key: null, count: 0 });
669
+ }
670
+
671
+ key(name: string, _relativeDepth: number): void {
672
+ if (this.exceeded) return;
673
+ const frame = this.frames[this.frames.length - 1];
674
+ if (frame === undefined) return;
675
+ if (!this.charge(name.length + 4)) {
676
+ this.exhaust(frame.kind);
677
+ return;
678
+ }
679
+ frame.key = name;
680
+ }
681
+
682
+ endContainer(kind: "object" | "array", relativeDepth: number, count: number): void {
683
+ if (this.exceeded) {
684
+ if (relativeDepth === 0) {
685
+ this.result = {
686
+ $summary:
687
+ this.summaryType === "object"
688
+ ? { type: "object", members: count, reason: "budget" }
689
+ : { type: "array", length: count, reason: "budget" },
690
+ };
691
+ this.done = true;
692
+ }
693
+ return;
694
+ }
695
+ const frame = this.frames.pop();
696
+ if (frame === undefined || frame.kind !== kind) return;
697
+ this.place(frame.value);
698
+ }
699
+
700
+ scalar(scalar: JsonScalarEvent, _relativeDepth: number): void {
701
+ if (this.exceeded) return;
702
+ const cost = scalar.type === "string" ? scalar.length + 2 : (scalar.literal?.length ?? 4);
703
+ if (!this.charge(cost)) {
704
+ const frame = this.frames[this.frames.length - 1];
705
+ if (frame === undefined) {
706
+ // A scalar root over budget: keep it, the budget exists for containers.
707
+ this.exceeded = false;
708
+ this.spent = 0;
709
+ } else {
710
+ this.exhaust(frame.kind);
711
+ return;
712
+ }
713
+ }
714
+ if (scalar.type === "number") {
715
+ const literal = scalar.literal ?? "";
716
+ const precision = scalar.truncated ? "oversized-literal" : numberLiteralPrecision(literal);
717
+ if (precision !== null) {
718
+ this.issues.push({ kind: precision, literal, path: this.pointer() });
719
+ this.place({ $literal: literal, precision });
720
+ return;
721
+ }
722
+ this.place(scalar.value as number);
723
+ return;
724
+ }
725
+ if (scalar.type === "string") {
726
+ if (scalar.truncated) {
727
+ this.stringsCut += 1;
728
+ this.place({ $truncated: "string", length: scalar.length, text: scalar.value as string });
729
+ } else this.place(scalar.value as string);
730
+ return;
731
+ }
732
+ this.place(scalar.value as boolean | null);
733
+ }
734
+ }
735
+
736
+ function escapePointerSegment(segment: string): string {
737
+ return segment.replace(/~/gu, "~0").replace(/\//gu, "~1");
738
+ }
739
+
740
+ /** Parse an RFC 6901 pointer into segments; "" is the whole document. */
741
+ export function parseJsonPointer(pointer: string): string[] | Error {
742
+ if (pointer === "") return [];
743
+ if (!pointer.startsWith("/")) return new Error("JSON pointer must start with '/' or be empty for the whole document");
744
+ return pointer
745
+ .slice(1)
746
+ .split("/")
747
+ .map((segment) => segment.replace(/~1/gu, "/").replace(/~0/gu, "~"));
748
+ }
749
+
750
+ /** Tracks the pointer of the value about to start, from the events already seen. */
751
+ class PathTracker {
752
+ readonly segments: string[] = [];
753
+ private readonly kinds: Array<"object" | "array"> = [];
754
+ private readonly indexes: number[] = [];
755
+
756
+ startContainer(kind: "object" | "array"): void {
757
+ this.kinds.push(kind);
758
+ this.indexes.push(0);
759
+ this.segments.push(kind === "array" ? "0" : "");
760
+ }
761
+
762
+ key(name: string): void {
763
+ this.segments[this.segments.length - 1] = name;
764
+ }
765
+
766
+ endContainer(): void {
767
+ this.kinds.pop();
768
+ this.indexes.pop();
769
+ this.segments.pop();
770
+ }
771
+
772
+ /** Advance after a value completes inside the current container. */
773
+ advance(): void {
774
+ const top = this.kinds.length - 1;
775
+ if (top < 0) return;
776
+ if (this.kinds[top] === "array") {
777
+ const next = (this.indexes[top] as number) + 1;
778
+ this.indexes[top] = next;
779
+ this.segments[top] = String(next);
780
+ }
781
+ }
782
+
783
+ pointer(): string {
784
+ return this.segments.map((segment) => `/${escapePointerSegment(segment)}`).join("");
785
+ }
786
+
787
+ /** Pointer of the innermost open container. */
788
+ containerPointer(): string {
789
+ return this.segments
790
+ .slice(0, -1)
791
+ .map((segment) => `/${escapePointerSegment(segment)}`)
792
+ .join("");
793
+ }
794
+ }
795
+
796
+ export interface JsonDuplicateKeyReport {
797
+ count: number;
798
+ first: { path: string; key: string } | null;
799
+ /** False when an object exceeded the per-object tracking cap, so later duplicates there went unchecked. */
800
+ checkedFully: boolean;
801
+ }
802
+
803
+ export interface JsonPrecisionReport {
804
+ count: number;
805
+ first: PrecisionIssue[];
806
+ }
807
+
808
+ const DUPLICATE_KEY_TRACK_CAP = 10_000;
809
+ const KEY_LIST_CAP = 200;
810
+ const PRECISION_FIRST_CAP = 20;
811
+ const SAMPLE_BUDGET_CHARS = 8192;
812
+ const SELECT_BUDGET_CHARS = 1_048_576;
813
+
814
+ interface ScanFrame {
815
+ seen: Set<string> | null;
816
+ overCap: boolean;
817
+ }
818
+
819
+ /**
820
+ * Structural scan state shared by inspect, validate, and the JSONL line
821
+ * reader: duplicate keys at any depth, precision issues with their pointer,
822
+ * and the pointer tracker every consumer needs.
823
+ */
824
+ class StructureScanner {
825
+ readonly path = new PathTracker();
826
+ private readonly frames: ScanFrame[] = [];
827
+ duplicateCount = 0;
828
+ duplicateFirst: { path: string; key: string } | null = null;
829
+ duplicateCheckedFully = true;
830
+ precisionCount = 0;
831
+ readonly precisionFirst: PrecisionIssue[] = [];
832
+
833
+ constructor(private readonly pointerPrefix: string = "") {}
834
+
835
+ startContainer(kind: "object" | "array"): void {
836
+ this.path.startContainer(kind);
837
+ this.frames.push({ seen: kind === "object" ? new Set() : null, overCap: false });
838
+ }
839
+
840
+ key(name: string): void {
841
+ this.path.key(name);
842
+ const frame = this.frames[this.frames.length - 1];
843
+ if (frame?.seen === null || frame === undefined) return;
844
+ if (frame.overCap) return;
845
+ if (frame.seen.has(name)) {
846
+ this.duplicateCount += 1;
847
+ if (this.duplicateFirst === null) {
848
+ this.duplicateFirst = { path: this.pointerPrefix + this.path.containerPointer(), key: name };
849
+ }
850
+ return;
851
+ }
852
+ if (frame.seen.size >= DUPLICATE_KEY_TRACK_CAP) {
853
+ frame.overCap = true;
854
+ frame.seen = null;
855
+ this.duplicateCheckedFully = false;
856
+ return;
857
+ }
858
+ frame.seen.add(name);
859
+ }
860
+
861
+ endContainer(): void {
862
+ this.frames.pop();
863
+ this.path.endContainer();
864
+ this.path.advance();
865
+ }
866
+
867
+ scalar(scalar: JsonScalarEvent): void {
868
+ if (scalar.type === "number") {
869
+ const literal = scalar.literal ?? "";
870
+ const precision = scalar.truncated ? "oversized-literal" : numberLiteralPrecision(literal);
871
+ if (precision !== null) {
872
+ this.precisionCount += 1;
873
+ if (this.precisionFirst.length < PRECISION_FIRST_CAP) {
874
+ this.precisionFirst.push({ kind: precision, literal, path: this.pointerPrefix + this.path.pointer() });
875
+ }
876
+ }
877
+ }
878
+ this.path.advance();
879
+ }
880
+
881
+ duplicates(): JsonDuplicateKeyReport {
882
+ return { count: this.duplicateCount, first: this.duplicateFirst, checkedFully: this.duplicateCheckedFully };
883
+ }
884
+
885
+ precision(): JsonPrecisionReport {
886
+ return { count: this.precisionCount, first: [...this.precisionFirst] };
887
+ }
888
+ }
889
+
890
+ export interface JsonSampleMember {
891
+ key: string;
892
+ value: JsonValue;
893
+ }
894
+
895
+ export interface JsonInspectResult {
896
+ ok: true;
897
+ format: "json";
898
+ path: string;
899
+ bytes: number;
900
+ bytesScanned: number;
901
+ view: DataViewFlags;
902
+ /** Top-level elements (array) or members (object) scanned; 1 for a scalar root. */
903
+ rowsScanned: number;
904
+ /** Array length or member count; null when the scan stopped at maxRows. */
905
+ rowCount: number | null;
906
+ root: JsonValueType;
907
+ /** Root object member names, in document order, capped. */
908
+ keys?: { names: string[]; truncated: boolean };
909
+ /** Root array element type histogram over the scanned elements. */
910
+ elementTypes?: Record<string, number>;
911
+ /** Root object member value type histogram over the scanned members. */
912
+ memberTypes?: Record<string, number>;
913
+ /** Root scalar value; a number with a precision issue keeps its literal. */
914
+ value?: JsonValue;
915
+ maxDepth: number;
916
+ duplicateKeys: JsonDuplicateKeyReport;
917
+ precision: JsonPrecisionReport;
918
+ /** First elements (array) or members (object), each within an 8 KiB budget. */
919
+ sample: JsonValue[] | JsonSampleMember[];
920
+ notes: string[];
921
+ }
922
+
923
+ export interface JsonInspectOptions {
924
+ sampleRows?: number;
925
+ maxRows?: number | null;
926
+ signal?: AbortSignal | undefined;
927
+ }
928
+
929
+ export const JSON_DEFAULT_SAMPLE_ROWS = 10;
930
+ export const JSON_DEFAULT_MAX_ROWS = 100_000;
931
+
932
+ class InspectSink implements JsonEventSink {
933
+ readonly scanner = new StructureScanner();
934
+ root: JsonValueType | null = null;
935
+ rows = 0;
936
+ rowCount: number | null = null;
937
+ stoppedAtRows = false;
938
+ readonly keys: string[] = [];
939
+ keysTruncated = false;
940
+ readonly types = new Map<string, number>();
941
+ readonly sample: JsonValue[] | JsonSampleMember[] = [];
942
+ /** Sample values that carry a $summary or $truncated placeholder; the counts stay exact. */
943
+ sampleCuts = 0;
944
+ rootValue: JsonValue | undefined;
945
+ private builder: JsonValueBuilder | null = null;
946
+ private builderDepth = 0;
947
+ private currentKey = "";
948
+ private readonly sampleIssues: PrecisionIssue[] = [];
949
+
950
+ constructor(
951
+ private readonly sampleRows: number,
952
+ private readonly maxRows: number,
953
+ ) {}
954
+
955
+ private countType(type: JsonValueType): void {
956
+ this.types.set(type, (this.types.get(type) ?? 0) + 1);
957
+ }
958
+
959
+ /** A depth-1 value begins: count it, decide whether it is sampled, or stop at the row bound. */
960
+ private beginRow(type: JsonValueType): boolean {
961
+ if (this.rows >= this.maxRows) {
962
+ this.stoppedAtRows = true;
963
+ return false;
964
+ }
965
+ this.rows += 1;
966
+ this.countType(type);
967
+ if (this.sample.length < this.sampleRows) {
968
+ const pointer = this.root === "array" ? `/${this.rows - 1}` : `/${escapePointerSegment(this.currentKey)}`;
969
+ this.builder = new JsonValueBuilder(SAMPLE_BUDGET_CHARS, pointer);
970
+ this.builderDepth = 1;
971
+ }
972
+ return true;
973
+ }
974
+
975
+ private finishSampled(): void {
976
+ const builder = this.builder;
977
+ if (builder === null || !builder.complete) return;
978
+ this.builder = null;
979
+ if (builder.truncated) this.sampleCuts += 1;
980
+ const value = builder.value as JsonValue;
981
+ if (this.root === "array") (this.sample as JsonValue[]).push(value);
982
+ else (this.sample as JsonSampleMember[]).push({ key: this.currentKey, value });
983
+ this.sampleIssues.push(...builder.issues);
984
+ }
985
+
986
+ onStartObject(depth: number): boolean | undefined {
987
+ if (depth === 0) {
988
+ this.root = "object";
989
+ this.scanner.startContainer("object");
990
+ return undefined;
991
+ }
992
+ if (depth === 1 && !this.beginRow("object")) return false;
993
+ this.scanner.startContainer("object");
994
+ this.builder?.startContainer("object", depth - this.builderDepth);
995
+ return undefined;
996
+ }
997
+
998
+ onStartArray(depth: number): boolean | undefined {
999
+ if (depth === 0) {
1000
+ this.root = "array";
1001
+ this.scanner.startContainer("array");
1002
+ return undefined;
1003
+ }
1004
+ if (depth === 1 && !this.beginRow("array")) return false;
1005
+ this.scanner.startContainer("array");
1006
+ this.builder?.startContainer("array", depth - this.builderDepth);
1007
+ return undefined;
1008
+ }
1009
+
1010
+ onKey(key: string, depth: number, truncated: boolean): boolean | undefined {
1011
+ this.scanner.key(key);
1012
+ if (depth === 1) {
1013
+ // The row this key introduces is counted when its value starts; the key
1014
+ // list and the stop decision belong here so a stop never records a key
1015
+ // whose value was never scanned.
1016
+ if (this.rows >= this.maxRows) {
1017
+ this.stoppedAtRows = true;
1018
+ return false;
1019
+ }
1020
+ this.currentKey = truncated ? `${key}…` : key;
1021
+ if (this.keys.length < KEY_LIST_CAP) this.keys.push(this.currentKey);
1022
+ else this.keysTruncated = true;
1023
+ return undefined;
1024
+ }
1025
+ this.builder?.key(key, depth - this.builderDepth);
1026
+ return undefined;
1027
+ }
1028
+
1029
+ onEndObject(depth: number, memberCount: number): boolean | undefined {
1030
+ if (depth === 0) {
1031
+ this.rowCount = memberCount;
1032
+ return undefined;
1033
+ }
1034
+ this.scanner.endContainer();
1035
+ this.builder?.endContainer("object", depth - this.builderDepth, memberCount);
1036
+ if (depth === 1) this.finishSampled();
1037
+ return undefined;
1038
+ }
1039
+
1040
+ onEndArray(depth: number, length: number): boolean | undefined {
1041
+ if (depth === 0) {
1042
+ this.rowCount = length;
1043
+ return undefined;
1044
+ }
1045
+ this.scanner.endContainer();
1046
+ this.builder?.endContainer("array", depth - this.builderDepth, length);
1047
+ if (depth === 1) this.finishSampled();
1048
+ return undefined;
1049
+ }
1050
+
1051
+ onScalar(scalar: JsonScalarEvent, depth: number): boolean | undefined {
1052
+ if (depth === 0) {
1053
+ this.root = scalar.type;
1054
+ this.rows = 1;
1055
+ this.rowCount = 1;
1056
+ const builder = new JsonValueBuilder(SAMPLE_BUDGET_CHARS, "");
1057
+ builder.scalar(scalar, 0);
1058
+ this.rootValue = builder.value;
1059
+ this.sampleIssues.push(...builder.issues);
1060
+ this.scanner.scalar(scalar);
1061
+ return undefined;
1062
+ }
1063
+ if (depth === 1 && !this.beginRow(scalar.type)) return false;
1064
+ this.scanner.scalar(scalar);
1065
+ this.builder?.scalar(scalar, depth - this.builderDepth);
1066
+ if (depth === 1) this.finishSampled();
1067
+ return undefined;
1068
+ }
1069
+
1070
+ captureStrings(depth: number): boolean {
1071
+ if (depth === 0) return true;
1072
+ if (depth === 1) return this.rows < this.maxRows && this.sample.length < this.sampleRows;
1073
+ return this.builder !== null && !this.builder.overBudget;
1074
+ }
1075
+
1076
+ /** Precision issues seen inside sampled values that the scanner already counted; used only for cross-checks. */
1077
+ get sampledIssues(): ReadonlyArray<PrecisionIssue> {
1078
+ return this.sampleIssues;
1079
+ }
1080
+ }
1081
+
1082
+ async function runParser(
1083
+ path: string,
1084
+ sink: JsonEventSink,
1085
+ signal: AbortSignal | undefined,
1086
+ stats: TextStreamStats,
1087
+ ): Promise<{ parser: JsonStreamParser; stopped: boolean }> {
1088
+ const parser = new JsonStreamParser(sink);
1089
+ for await (const chunk of streamTextChunks(path, { signal, stats })) {
1090
+ if (!parser.push(chunk.text, chunk.byteOffset)) return { parser, stopped: true };
1091
+ }
1092
+ parser.end();
1093
+ throwIfAborted(signal, path);
1094
+ return { parser, stopped: parser.isStopped };
1095
+ }
1096
+
1097
+ export async function inspectJson(
1098
+ path: string,
1099
+ options: JsonInspectOptions = {},
1100
+ ): Promise<JsonInspectResult | DataRefusal> {
1101
+ const stat = statDataFile(path);
1102
+ if ("ok" in stat) return stat;
1103
+ const sampleRows = Math.max(0, Math.min(1000, Math.floor(options.sampleRows ?? JSON_DEFAULT_SAMPLE_ROWS)));
1104
+ const maxRows = resolveMaxRows(options.maxRows, JSON_DEFAULT_MAX_ROWS);
1105
+ const stats: TextStreamStats = { bytesRead: 0, bom: false };
1106
+ const sink = new InspectSink(sampleRows, maxRows);
1107
+ let parser: JsonStreamParser;
1108
+ try {
1109
+ parser = (await runParser(path, sink, options.signal, stats)).parser;
1110
+ } catch (error) {
1111
+ return refusalFromError(error, path, options.signal);
1112
+ }
1113
+ const sampled = sink.stoppedAtRows;
1114
+ const notes: string[] = [];
1115
+ if (stats.bom) notes.push("a UTF-8 byte-order mark was stripped");
1116
+ if (sampled) notes.push(sampledNotice(sink.rows, sink.root === "object" ? "members" : "elements"));
1117
+ if (sink.sampleCuts > 0) {
1118
+ notes.push(
1119
+ `${sink.sampleCuts} sample value(s) were cut to the preview budget and carry $summary or $truncated placeholders; the counts are exact`,
1120
+ );
1121
+ }
1122
+ if (!sink.scanner.duplicateCheckedFully) {
1123
+ notes.push(
1124
+ `duplicate-key tracking stopped at ${DUPLICATE_KEY_TRACK_CAP} keys in one object; later duplicates there are unreported`,
1125
+ );
1126
+ }
1127
+ if (sink.keysTruncated) notes.push(`key list capped at ${KEY_LIST_CAP} names`);
1128
+ const root = sink.root ?? "null";
1129
+ const result: JsonInspectResult = {
1130
+ ok: true,
1131
+ format: "json",
1132
+ path,
1133
+ bytes: stat.size,
1134
+ bytesScanned: stats.bytesRead,
1135
+ view: sampled ? sampledView() : exactView(),
1136
+ rowsScanned: sink.rows,
1137
+ rowCount: sampled ? null : sink.rowCount,
1138
+ root,
1139
+ maxDepth: parser.maxDepth,
1140
+ duplicateKeys: sink.scanner.duplicates(),
1141
+ precision: sink.scanner.precision(),
1142
+ sample: sink.sample,
1143
+ notes,
1144
+ };
1145
+ const types: Record<string, number> = {};
1146
+ for (const [type, count] of sink.types) types[type] = count;
1147
+ if (root === "array") result.elementTypes = types;
1148
+ else if (root === "object") {
1149
+ result.keys = { names: sink.keys, truncated: sink.keysTruncated };
1150
+ result.memberTypes = types;
1151
+ } else if (sink.rootValue !== undefined) result.value = sink.rootValue;
1152
+ return result;
1153
+ }
1154
+
1155
+ export interface JsonSelectOptions {
1156
+ /** RFC 6901 pointer; "" selects the whole document. */
1157
+ pointer?: string;
1158
+ /** Array targets only: first element index to return. */
1159
+ offset?: number;
1160
+ /** Array targets only: maximum elements to return. */
1161
+ limit?: number;
1162
+ signal?: AbortSignal | undefined;
1163
+ }
1164
+
1165
+ export const JSON_SELECT_DEFAULT_LIMIT = 50;
1166
+ export const JSON_SELECT_MAX_LIMIT = 1000;
1167
+
1168
+ export interface JsonSelectResult {
1169
+ ok: true;
1170
+ format: "json";
1171
+ path: string;
1172
+ bytes: number;
1173
+ bytesScanned: number;
1174
+ view: DataViewFlags;
1175
+ pointer: string;
1176
+ /** The selected value, unless the target is an array read with offset/limit. */
1177
+ value?: JsonValue;
1178
+ /** Elements [offset, offset + returned) of the target array. */
1179
+ elements?: JsonValue[];
1180
+ offset?: number;
1181
+ limit?: number;
1182
+ returned?: number;
1183
+ /** Whether the target array holds elements past the returned window. */
1184
+ hasMore?: boolean;
1185
+ precision: JsonPrecisionReport;
1186
+ /** Duplicate object keys inside the selected value; materialization kept the last value. */
1187
+ duplicateKeys: JsonDuplicateKeyReport;
1188
+ notes: string[];
1189
+ }
1190
+
1191
+ type SelectPhase = "seeking" | "capturing" | "done";
1192
+
1193
+ class SelectSink implements JsonEventSink {
1194
+ readonly path = new PathTracker();
1195
+ phase: SelectPhase = "seeking";
1196
+ targetDepth = -1;
1197
+ found = false;
1198
+ targetType: JsonValueType | null = null;
1199
+ builder: JsonValueBuilder | null = null;
1200
+ /** Array-window mode state. */
1201
+ windowed = false;
1202
+ elementIndex = 0;
1203
+ readonly elements: JsonValue[] = [];
1204
+ hasMore = false;
1205
+ readonly issues: PrecisionIssue[] = [];
1206
+ tooLarge = false;
1207
+ cutStrings = 0;
1208
+ /** Deepest matched prefix length, for the not-found message. */
1209
+ deepestMatch = 0;
1210
+ /** Per-object key sets inside the captured value; null past the tracking cap. */
1211
+ private readonly keySets: Array<Set<string> | null> = [];
1212
+ duplicateCount = 0;
1213
+ duplicateFirst: { path: string; key: string } | null = null;
1214
+ duplicatesCheckedFully = true;
1215
+
1216
+ constructor(
1217
+ private readonly segments: string[],
1218
+ private readonly windowOffset: number | null,
1219
+ private readonly windowLimit: number,
1220
+ ) {}
1221
+
1222
+ private trackObjectStart(): void {
1223
+ if (this.builder !== null) this.keySets.push(new Set());
1224
+ }
1225
+
1226
+ private trackKey(key: string): void {
1227
+ if (this.builder === null) return;
1228
+ const keys = this.keySets[this.keySets.length - 1];
1229
+ if (keys === null || keys === undefined) return;
1230
+ if (keys.has(key)) {
1231
+ this.duplicateCount += 1;
1232
+ if (this.duplicateFirst === null) this.duplicateFirst = { path: this.path.containerPointer(), key };
1233
+ return;
1234
+ }
1235
+ if (keys.size >= DUPLICATE_KEY_TRACK_CAP) {
1236
+ this.duplicatesCheckedFully = false;
1237
+ this.keySets[this.keySets.length - 1] = null;
1238
+ return;
1239
+ }
1240
+ keys.add(key);
1241
+ }
1242
+
1243
+ private trackObjectEnd(): void {
1244
+ if (this.builder !== null) this.keySets.pop();
1245
+ }
1246
+
1247
+ private pointerMatches(): boolean {
1248
+ const current = this.path.segments;
1249
+ if (current.length !== this.segments.length) return false;
1250
+ for (let index = 0; index < current.length; index += 1) {
1251
+ if (current[index] !== this.segments[index]) return false;
1252
+ }
1253
+ return true;
1254
+ }
1255
+
1256
+ private prefixMatches(): boolean {
1257
+ const current = this.path.segments;
1258
+ if (current.length > this.segments.length) return false;
1259
+ for (let index = 0; index < current.length; index += 1) {
1260
+ if (current[index] !== this.segments[index]) return false;
1261
+ }
1262
+ if (current.length > this.deepestMatch) this.deepestMatch = current.length;
1263
+ return true;
1264
+ }
1265
+
1266
+ private beginTarget(type: JsonValueType, depth: number): void {
1267
+ this.found = true;
1268
+ this.targetType = type;
1269
+ this.targetDepth = depth;
1270
+ this.phase = "capturing";
1271
+ if (type === "array" && this.windowOffset !== null) {
1272
+ this.windowed = true;
1273
+ return;
1274
+ }
1275
+ this.builder = new JsonValueBuilder(SELECT_BUDGET_CHARS, this.path.pointer());
1276
+ }
1277
+
1278
+ private finishBuilder(): boolean | undefined {
1279
+ const builder = this.builder;
1280
+ if (builder === null || !builder.complete) return undefined;
1281
+ this.builder = null;
1282
+ if (builder.overBudget) {
1283
+ this.tooLarge = true;
1284
+ return false;
1285
+ }
1286
+ this.cutStrings += builder.cutStrings;
1287
+ this.issues.push(...builder.issues);
1288
+ if (this.windowed) {
1289
+ this.elements.push(builder.value as JsonValue);
1290
+ if (this.elements.length >= this.windowLimit) {
1291
+ // Keep parsing only far enough to learn whether another element follows.
1292
+ return undefined;
1293
+ }
1294
+ return undefined;
1295
+ }
1296
+ this.value = builder.value;
1297
+ this.phase = "done";
1298
+ return false;
1299
+ }
1300
+
1301
+ value: JsonValue | undefined;
1302
+
1303
+ private beginElement(type: JsonValueType, depth: number): boolean | undefined {
1304
+ if (!this.windowed || depth !== this.targetDepth + 1) return undefined;
1305
+ const index = this.elementIndex;
1306
+ this.elementIndex += 1;
1307
+ const offset = this.windowOffset as number;
1308
+ if (index < offset) return undefined;
1309
+ if (this.elements.length >= this.windowLimit) {
1310
+ this.hasMore = true;
1311
+ this.phase = "done";
1312
+ return false;
1313
+ }
1314
+ this.builder = new JsonValueBuilder(SELECT_BUDGET_CHARS, `${this.path.pointer()}`);
1315
+ void type;
1316
+ return undefined;
1317
+ }
1318
+
1319
+ onStartObject(depth: number): boolean | undefined {
1320
+ if (this.phase === "seeking") {
1321
+ if (this.pointerMatches()) {
1322
+ this.beginTarget("object", depth);
1323
+ this.builder?.startContainer("object", 0);
1324
+ } else this.prefixMatches();
1325
+ } else if (this.phase === "capturing") {
1326
+ const stop = this.beginElement("object", depth);
1327
+ if (stop === false) return false;
1328
+ this.builder?.startContainer("object", depth - (this.windowed ? this.targetDepth + 1 : this.targetDepth));
1329
+ }
1330
+ this.trackObjectStart();
1331
+ this.path.startContainer("object");
1332
+ return undefined;
1333
+ }
1334
+
1335
+ onStartArray(depth: number): boolean | undefined {
1336
+ if (this.phase === "seeking") {
1337
+ if (this.pointerMatches()) {
1338
+ this.beginTarget("array", depth);
1339
+ this.builder?.startContainer("array", 0);
1340
+ } else this.prefixMatches();
1341
+ } else if (this.phase === "capturing") {
1342
+ const stop = this.beginElement("array", depth);
1343
+ if (stop === false) return false;
1344
+ this.builder?.startContainer("array", depth - (this.windowed ? this.targetDepth + 1 : this.targetDepth));
1345
+ }
1346
+ this.path.startContainer("array");
1347
+ return undefined;
1348
+ }
1349
+
1350
+ onKey(key: string, depth: number): boolean | undefined {
1351
+ this.path.key(key);
1352
+ if (this.phase === "seeking") this.prefixMatches();
1353
+ else if (this.phase === "capturing") {
1354
+ this.trackKey(key);
1355
+ this.builder?.key(key, depth - (this.windowed ? this.targetDepth + 1 : this.targetDepth));
1356
+ }
1357
+ return undefined;
1358
+ }
1359
+
1360
+ onEndObject(depth: number, memberCount: number): boolean | undefined {
1361
+ this.trackObjectEnd();
1362
+ this.path.endContainer();
1363
+ this.path.advance();
1364
+ if (this.phase !== "capturing") return undefined;
1365
+ if (this.windowed) {
1366
+ if (depth === this.targetDepth) {
1367
+ this.phase = "done";
1368
+ return false;
1369
+ }
1370
+ this.builder?.endContainer("object", depth - (this.targetDepth + 1), memberCount);
1371
+ if (depth === this.targetDepth + 1) return this.finishBuilder();
1372
+ return undefined;
1373
+ }
1374
+ this.builder?.endContainer("object", depth - this.targetDepth, memberCount);
1375
+ if (depth === this.targetDepth) return this.finishBuilder();
1376
+ return undefined;
1377
+ }
1378
+
1379
+ onEndArray(depth: number, length: number): boolean | undefined {
1380
+ this.path.endContainer();
1381
+ this.path.advance();
1382
+ if (this.phase !== "capturing") return undefined;
1383
+ if (this.windowed) {
1384
+ if (depth === this.targetDepth) {
1385
+ this.phase = "done";
1386
+ return false;
1387
+ }
1388
+ this.builder?.endContainer("array", depth - (this.targetDepth + 1), length);
1389
+ if (depth === this.targetDepth + 1) return this.finishBuilder();
1390
+ return undefined;
1391
+ }
1392
+ this.builder?.endContainer("array", depth - this.targetDepth, length);
1393
+ if (depth === this.targetDepth) return this.finishBuilder();
1394
+ return undefined;
1395
+ }
1396
+
1397
+ onScalar(scalar: JsonScalarEvent, depth: number): boolean | undefined {
1398
+ let verdict: boolean | undefined;
1399
+ if (this.phase === "seeking") {
1400
+ if (this.pointerMatches()) {
1401
+ this.beginTarget(scalar.type, depth);
1402
+ this.builder?.scalar(scalar, 0);
1403
+ verdict = this.finishBuilder();
1404
+ } else this.prefixMatches();
1405
+ } else if (this.phase === "capturing") {
1406
+ const stop = this.beginElement(scalar.type, depth);
1407
+ if (stop === false) return false;
1408
+ if (this.windowed) {
1409
+ this.builder?.scalar(scalar, depth - (this.targetDepth + 1));
1410
+ if (depth === this.targetDepth + 1) verdict = this.finishBuilder();
1411
+ } else {
1412
+ this.builder?.scalar(scalar, depth - this.targetDepth);
1413
+ }
1414
+ }
1415
+ this.path.advance();
1416
+ return verdict;
1417
+ }
1418
+
1419
+ captureStrings(depth: number): boolean {
1420
+ if (this.phase === "seeking") return this.pointerMatches();
1421
+ if (this.phase !== "capturing") return false;
1422
+ if (this.windowed && depth === this.targetDepth + 1) {
1423
+ return this.elementIndex >= (this.windowOffset as number) && this.elements.length < this.windowLimit;
1424
+ }
1425
+ return this.builder !== null && !this.builder.overBudget;
1426
+ }
1427
+ }
1428
+
1429
+ export async function selectJson(
1430
+ path: string,
1431
+ options: JsonSelectOptions = {},
1432
+ ): Promise<JsonSelectResult | DataRefusal> {
1433
+ const stat = statDataFile(path);
1434
+ if ("ok" in stat) return stat;
1435
+ const pointer = options.pointer ?? "";
1436
+ const segments = parseJsonPointer(pointer);
1437
+ if (segments instanceof Error) return refusal("invalid-argument", segments.message, { path });
1438
+ const windowed = options.offset !== undefined || options.limit !== undefined;
1439
+ const offset = windowed ? Math.max(0, Math.floor(options.offset ?? 0)) : null;
1440
+ const limit = Math.max(1, Math.min(JSON_SELECT_MAX_LIMIT, Math.floor(options.limit ?? JSON_SELECT_DEFAULT_LIMIT)));
1441
+ const stats: TextStreamStats = { bytesRead: 0, bom: false };
1442
+ const sink = new SelectSink(segments, offset, limit);
1443
+ let stopped = false;
1444
+ try {
1445
+ stopped = (await runParser(path, sink, options.signal, stats)).stopped;
1446
+ } catch (error) {
1447
+ return refusalFromError(error, path, options.signal);
1448
+ }
1449
+ if (sink.tooLarge) {
1450
+ const where = pointer || "the document root";
1451
+ return refusal(
1452
+ "selection-too-large",
1453
+ windowed
1454
+ ? `an element of the array at ${where} exceeds the ${SELECT_BUDGET_CHARS}-character selection budget; select a deeper pointer into that element, or a window of 1 to isolate it`
1455
+ : `the value at ${where} exceeds the ${SELECT_BUDGET_CHARS}-character selection budget; select a deeper pointer or an array window with offset and limit`,
1456
+ { path },
1457
+ );
1458
+ }
1459
+ if (!sink.found) {
1460
+ const matched = segments.slice(0, sink.deepestMatch);
1461
+ const matchedPointer = matched.map((segment) => `/${escapePointerSegment(segment)}`).join("");
1462
+ return refusal(
1463
+ "pointer-not-found",
1464
+ `no value at ${pointer}; the deepest existing prefix is ${matchedPointer === "" ? "the document root" : matchedPointer}`,
1465
+ { path },
1466
+ );
1467
+ }
1468
+ const notes: string[] = [];
1469
+ if (stats.bom) notes.push("a UTF-8 byte-order mark was stripped");
1470
+ if (stopped) {
1471
+ notes.push("reading stopped once the selection was complete; the rest of the document was not checked for syntax");
1472
+ }
1473
+ if (sink.cutStrings > 0) {
1474
+ notes.push(
1475
+ `${sink.cutStrings} string(s) longer than ${STRING_CAPTURE_CAP} characters were cut; each is a $truncated placeholder with its full length`,
1476
+ );
1477
+ }
1478
+ if (sink.duplicateCount > 0) {
1479
+ notes.push(`${sink.duplicateCount} duplicate object key(s) inside the selection; the last value was kept`);
1480
+ }
1481
+ const precision: JsonPrecisionReport = {
1482
+ count: sink.issues.length,
1483
+ first: sink.issues.slice(0, PRECISION_FIRST_CAP),
1484
+ };
1485
+ const base = {
1486
+ ok: true as const,
1487
+ format: "json" as const,
1488
+ path,
1489
+ bytes: stat.size,
1490
+ bytesScanned: stats.bytesRead,
1491
+ view: sink.cutStrings > 0 ? cutView() : exactView(),
1492
+ pointer,
1493
+ precision,
1494
+ duplicateKeys: {
1495
+ count: sink.duplicateCount,
1496
+ first: sink.duplicateFirst,
1497
+ checkedFully: sink.duplicatesCheckedFully,
1498
+ } satisfies JsonDuplicateKeyReport,
1499
+ notes,
1500
+ };
1501
+ if (windowed) {
1502
+ if (sink.targetType !== "array") {
1503
+ return refusal(
1504
+ "invalid-argument",
1505
+ `offset and limit apply to an array target; the value at ${pointer || "the document root"} is ${sink.targetType ?? "unknown"}`,
1506
+ { path },
1507
+ );
1508
+ }
1509
+ return {
1510
+ ...base,
1511
+ elements: sink.elements,
1512
+ offset: offset as number,
1513
+ limit,
1514
+ returned: sink.elements.length,
1515
+ hasMore: sink.hasMore,
1516
+ };
1517
+ }
1518
+ return { ...base, value: sink.value ?? null };
1519
+ }
1520
+
1521
+ export interface JsonValidateResult {
1522
+ ok: true;
1523
+ format: "json";
1524
+ path: string;
1525
+ bytes: number;
1526
+ bytesScanned: number;
1527
+ view: DataViewFlags;
1528
+ /** True when the whole document parsed; null when the scan stopped at maxRows before the end. */
1529
+ valid: boolean | null;
1530
+ complete: boolean;
1531
+ rowsScanned: number;
1532
+ rowCount: number | null;
1533
+ root: JsonValueType | null;
1534
+ maxDepth: number;
1535
+ duplicateKeys: JsonDuplicateKeyReport;
1536
+ precision: JsonPrecisionReport;
1537
+ /** The syntax fault, when the document is invalid. */
1538
+ syntaxError?: { message: string; line?: number; column?: number; byteOffset?: number };
1539
+ notes: string[];
1540
+ }
1541
+
1542
+ export interface JsonValidateOptions {
1543
+ maxRows?: number | null;
1544
+ signal?: AbortSignal | undefined;
1545
+ }
1546
+
1547
+ class ValidateSink implements JsonEventSink {
1548
+ readonly scanner = new StructureScanner();
1549
+ root: JsonValueType | null = null;
1550
+ rows = 0;
1551
+ rowCount: number | null = null;
1552
+ stoppedAtRows = false;
1553
+
1554
+ constructor(private readonly maxRows: number) {}
1555
+
1556
+ private beginRow(): boolean {
1557
+ if (this.rows >= this.maxRows) {
1558
+ this.stoppedAtRows = true;
1559
+ return false;
1560
+ }
1561
+ this.rows += 1;
1562
+ return true;
1563
+ }
1564
+
1565
+ onStartObject(depth: number): boolean | undefined {
1566
+ if (depth === 0) this.root = "object";
1567
+ else if (depth === 1 && !this.beginRow()) return false;
1568
+ this.scanner.startContainer("object");
1569
+ return undefined;
1570
+ }
1571
+
1572
+ onStartArray(depth: number): boolean | undefined {
1573
+ if (depth === 0) this.root = "array";
1574
+ else if (depth === 1 && !this.beginRow()) return false;
1575
+ this.scanner.startContainer("array");
1576
+ return undefined;
1577
+ }
1578
+
1579
+ onKey(key: string, depth: number): boolean | undefined {
1580
+ if (depth === 1 && this.rows >= this.maxRows) {
1581
+ this.stoppedAtRows = true;
1582
+ return false;
1583
+ }
1584
+ this.scanner.key(key);
1585
+ return undefined;
1586
+ }
1587
+
1588
+ onEndObject(depth: number, memberCount: number): boolean | undefined {
1589
+ if (depth === 0) this.rowCount = memberCount;
1590
+ this.scanner.endContainer();
1591
+ return undefined;
1592
+ }
1593
+
1594
+ onEndArray(depth: number, length: number): boolean | undefined {
1595
+ if (depth === 0) this.rowCount = length;
1596
+ this.scanner.endContainer();
1597
+ return undefined;
1598
+ }
1599
+
1600
+ onScalar(scalar: JsonScalarEvent, depth: number): boolean | undefined {
1601
+ if (depth === 0) {
1602
+ this.root = scalar.type;
1603
+ this.rows = 1;
1604
+ this.rowCount = 1;
1605
+ } else if (depth === 1 && !this.beginRow()) return false;
1606
+ this.scanner.scalar(scalar);
1607
+ return undefined;
1608
+ }
1609
+
1610
+ captureStrings(): boolean {
1611
+ return false;
1612
+ }
1613
+ }
1614
+
1615
+ export async function validateJson(
1616
+ path: string,
1617
+ options: JsonValidateOptions = {},
1618
+ ): Promise<JsonValidateResult | DataRefusal> {
1619
+ const stat = statDataFile(path);
1620
+ if ("ok" in stat) return stat;
1621
+ const maxRows = resolveMaxRows(options.maxRows, JSON_DEFAULT_MAX_ROWS);
1622
+ const stats: TextStreamStats = { bytesRead: 0, bom: false };
1623
+ const sink = new ValidateSink(maxRows);
1624
+ const parser = new JsonStreamParser(sink);
1625
+ let syntaxError: JsonValidateResult["syntaxError"];
1626
+ try {
1627
+ let stopped = false;
1628
+ for await (const chunk of streamTextChunks(path, { signal: options.signal, stats })) {
1629
+ if (!parser.push(chunk.text, chunk.byteOffset)) {
1630
+ stopped = true;
1631
+ break;
1632
+ }
1633
+ }
1634
+ if (!stopped) parser.end();
1635
+ throwIfAborted(options.signal, path);
1636
+ } catch (error) {
1637
+ if (
1638
+ error instanceof DataRefusalError &&
1639
+ (error.refusal.reason === "invalid-json" || error.refusal.reason === "nesting-too-deep")
1640
+ ) {
1641
+ syntaxError = {
1642
+ message: error.refusal.message,
1643
+ ...(error.refusal.line !== undefined ? { line: error.refusal.line } : {}),
1644
+ ...(error.refusal.column !== undefined ? { column: error.refusal.column } : {}),
1645
+ ...(error.refusal.byteOffset !== undefined ? { byteOffset: error.refusal.byteOffset } : {}),
1646
+ };
1647
+ } else {
1648
+ return refusalFromError(error, path, options.signal);
1649
+ }
1650
+ }
1651
+ const complete = !sink.stoppedAtRows;
1652
+ const notes: string[] = [];
1653
+ if (stats.bom) notes.push("a UTF-8 byte-order mark was stripped");
1654
+ if (!complete) {
1655
+ notes.push(
1656
+ `validation incomplete: stopped after ${sink.rows} top-level rows; pass maxRows null to validate the whole document`,
1657
+ );
1658
+ }
1659
+ if (sink.scanner.duplicateCount > 0)
1660
+ notes.push(`${sink.scanner.duplicateCount} duplicate object key(s); JSON.parse would keep the last value`);
1661
+ return {
1662
+ ok: true,
1663
+ format: "json",
1664
+ path,
1665
+ bytes: stat.size,
1666
+ bytesScanned: stats.bytesRead,
1667
+ view: complete ? exactView() : sampledView(),
1668
+ valid: syntaxError !== undefined ? false : complete ? true : null,
1669
+ complete,
1670
+ rowsScanned: sink.rows,
1671
+ rowCount: complete && syntaxError === undefined ? sink.rowCount : null,
1672
+ root: sink.root,
1673
+ maxDepth: parser.maxDepth,
1674
+ duplicateKeys: sink.scanner.duplicates(),
1675
+ precision: sink.scanner.precision(),
1676
+ ...(syntaxError !== undefined ? { syntaxError } : {}),
1677
+ notes,
1678
+ };
1679
+ }
1680
+
1681
+ /**
1682
+ * Parse one complete JSON text (a JSONL line) with a structural scan and an
1683
+ * optional materialization. Returns the parsed value under the builder budget,
1684
+ * the top-level shape, and the precision and duplicate facts the scanner saw.
1685
+ */
1686
+ export interface JsonTextScan {
1687
+ root: JsonValueType;
1688
+ /** Top-level member names for an object root, in order. */
1689
+ keys: string[];
1690
+ /** Top-level member value types by key (object root) or element types (array root). */
1691
+ memberTypes: Map<string, JsonValueType>;
1692
+ maxDepth: number;
1693
+ duplicateKeys: number;
1694
+ precision: PrecisionIssue[];
1695
+ value: JsonValue | undefined;
1696
+ valueTruncated: boolean;
1697
+ }
1698
+
1699
+ class TextScanSink implements JsonEventSink {
1700
+ readonly scanner: StructureScanner;
1701
+ root: JsonValueType | null = null;
1702
+ readonly keys: string[] = [];
1703
+ readonly memberTypes = new Map<string, JsonValueType>();
1704
+ private currentKey = "";
1705
+ private readonly builder: JsonValueBuilder | null;
1706
+
1707
+ constructor(pointerPrefix: string, budgetChars: number | null) {
1708
+ this.scanner = new StructureScanner(pointerPrefix);
1709
+ this.builder = budgetChars === null ? null : new JsonValueBuilder(budgetChars, "");
1710
+ }
1711
+
1712
+ get value(): JsonValue | undefined {
1713
+ return this.builder?.value;
1714
+ }
1715
+
1716
+ get valueTruncated(): boolean {
1717
+ return this.builder?.truncated ?? false;
1718
+ }
1719
+
1720
+ private noteMember(type: JsonValueType, depth: number): void {
1721
+ if (depth !== 1) return;
1722
+ if (this.root === "object") {
1723
+ if (!this.memberTypes.has(this.currentKey)) this.memberTypes.set(this.currentKey, type);
1724
+ }
1725
+ }
1726
+
1727
+ onStartObject(depth: number): boolean | undefined {
1728
+ if (depth === 0) this.root = "object";
1729
+ this.noteMember("object", depth);
1730
+ this.scanner.startContainer("object");
1731
+ this.builder?.startContainer("object", depth);
1732
+ return undefined;
1733
+ }
1734
+
1735
+ onStartArray(depth: number): boolean | undefined {
1736
+ if (depth === 0) this.root = "array";
1737
+ this.noteMember("array", depth);
1738
+ this.scanner.startContainer("array");
1739
+ this.builder?.startContainer("array", depth);
1740
+ return undefined;
1741
+ }
1742
+
1743
+ onKey(key: string, depth: number): boolean | undefined {
1744
+ this.scanner.key(key);
1745
+ if (depth === 1) {
1746
+ this.currentKey = key;
1747
+ this.keys.push(key);
1748
+ }
1749
+ this.builder?.key(key, depth);
1750
+ return undefined;
1751
+ }
1752
+
1753
+ onEndObject(depth: number, memberCount: number): boolean | undefined {
1754
+ this.scanner.endContainer();
1755
+ this.builder?.endContainer("object", depth, memberCount);
1756
+ return undefined;
1757
+ }
1758
+
1759
+ onEndArray(depth: number, length: number): boolean | undefined {
1760
+ this.scanner.endContainer();
1761
+ this.builder?.endContainer("array", depth, length);
1762
+ return undefined;
1763
+ }
1764
+
1765
+ onScalar(scalar: JsonScalarEvent, depth: number): boolean | undefined {
1766
+ if (depth === 0) this.root = scalar.type;
1767
+ this.noteMember(scalar.type, depth);
1768
+ this.scanner.scalar(scalar);
1769
+ this.builder?.scalar(scalar, depth);
1770
+ return undefined;
1771
+ }
1772
+
1773
+ captureStrings(): boolean {
1774
+ return this.builder !== null && !this.builder.overBudget;
1775
+ }
1776
+ }
1777
+
1778
+ /** Scan one JSON text. Throws a DataRefusalError for a syntax fault (offsets relative to the text). */
1779
+ export function scanJsonText(
1780
+ text: string,
1781
+ options: { pointerPrefix?: string; materializeBudgetChars?: number | null } = {},
1782
+ ): JsonTextScan {
1783
+ const sink = new TextScanSink(options.pointerPrefix ?? "", options.materializeBudgetChars ?? null);
1784
+ const parser = new JsonStreamParser(sink);
1785
+ parser.push(text, 0);
1786
+ parser.end();
1787
+ return {
1788
+ root: sink.root ?? "null",
1789
+ keys: sink.keys,
1790
+ memberTypes: sink.memberTypes,
1791
+ maxDepth: parser.maxDepth,
1792
+ duplicateKeys: sink.scanner.duplicateCount,
1793
+ precision: [...sink.scanner.precisionFirst],
1794
+ value: sink.value,
1795
+ valueTruncated: sink.valueTruncated,
1796
+ };
1797
+ }
1798
+
1799
+ export { PRECISION_FIRST_CAP as JSON_PRECISION_FIRST_CAP, SAMPLE_BUDGET_CHARS as JSON_SAMPLE_BUDGET_CHARS };