@vizalice/codegraph 1.5.0

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 (821) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +872 -0
  3. package/dist/bin/codegraph.d.ts +26 -0
  4. package/dist/bin/codegraph.d.ts.map +1 -0
  5. package/dist/bin/codegraph.js +2324 -0
  6. package/dist/bin/codegraph.js.map +1 -0
  7. package/dist/bin/command-supervision.d.ts +49 -0
  8. package/dist/bin/command-supervision.d.ts.map +1 -0
  9. package/dist/bin/command-supervision.js +95 -0
  10. package/dist/bin/command-supervision.js.map +1 -0
  11. package/dist/bin/fatal-handler.d.ts +20 -0
  12. package/dist/bin/fatal-handler.d.ts.map +1 -0
  13. package/dist/bin/fatal-handler.js +118 -0
  14. package/dist/bin/fatal-handler.js.map +1 -0
  15. package/dist/bin/node-version-check.d.ts +37 -0
  16. package/dist/bin/node-version-check.d.ts.map +1 -0
  17. package/dist/bin/node-version-check.js +79 -0
  18. package/dist/bin/node-version-check.js.map +1 -0
  19. package/dist/bin/uninstall.d.ts +14 -0
  20. package/dist/bin/uninstall.d.ts.map +1 -0
  21. package/dist/bin/uninstall.js +36 -0
  22. package/dist/bin/uninstall.js.map +1 -0
  23. package/dist/context/formatter.d.ts +36 -0
  24. package/dist/context/formatter.d.ts.map +1 -0
  25. package/dist/context/formatter.js +269 -0
  26. package/dist/context/formatter.js.map +1 -0
  27. package/dist/context/index.d.ts +119 -0
  28. package/dist/context/index.d.ts.map +1 -0
  29. package/dist/context/index.js +1333 -0
  30. package/dist/context/index.js.map +1 -0
  31. package/dist/context/markers.d.ts +19 -0
  32. package/dist/context/markers.d.ts.map +1 -0
  33. package/dist/context/markers.js +22 -0
  34. package/dist/context/markers.js.map +1 -0
  35. package/dist/db/index.d.ts +378 -0
  36. package/dist/db/index.d.ts.map +1 -0
  37. package/dist/db/index.js +831 -0
  38. package/dist/db/index.js.map +1 -0
  39. package/dist/db/migrations.d.ts +44 -0
  40. package/dist/db/migrations.d.ts.map +1 -0
  41. package/dist/db/migrations.js +236 -0
  42. package/dist/db/migrations.js.map +1 -0
  43. package/dist/db/queries.d.ts +705 -0
  44. package/dist/db/queries.d.ts.map +1 -0
  45. package/dist/db/queries.js +2435 -0
  46. package/dist/db/queries.js.map +1 -0
  47. package/dist/db/schema.sql +207 -0
  48. package/dist/db/sqlite-adapter.d.ts +55 -0
  49. package/dist/db/sqlite-adapter.d.ts.map +1 -0
  50. package/dist/db/sqlite-adapter.js +135 -0
  51. package/dist/db/sqlite-adapter.js.map +1 -0
  52. package/dist/db/wal-valve.d.ts +119 -0
  53. package/dist/db/wal-valve.d.ts.map +1 -0
  54. package/dist/db/wal-valve.js +290 -0
  55. package/dist/db/wal-valve.js.map +1 -0
  56. package/dist/directory.d.ts +183 -0
  57. package/dist/directory.d.ts.map +1 -0
  58. package/dist/directory.js +797 -0
  59. package/dist/directory.js.map +1 -0
  60. package/dist/errors.d.ts +136 -0
  61. package/dist/errors.d.ts.map +1 -0
  62. package/dist/errors.js +219 -0
  63. package/dist/errors.js.map +1 -0
  64. package/dist/extraction/astro-extractor.d.ts +79 -0
  65. package/dist/extraction/astro-extractor.d.ts.map +1 -0
  66. package/dist/extraction/astro-extractor.js +320 -0
  67. package/dist/extraction/astro-extractor.js.map +1 -0
  68. package/dist/extraction/cfml-extractor.d.ts +107 -0
  69. package/dist/extraction/cfml-extractor.d.ts.map +1 -0
  70. package/dist/extraction/cfml-extractor.js +494 -0
  71. package/dist/extraction/cfml-extractor.js.map +1 -0
  72. package/dist/extraction/dfm-extractor.d.ts +31 -0
  73. package/dist/extraction/dfm-extractor.d.ts.map +1 -0
  74. package/dist/extraction/dfm-extractor.js +151 -0
  75. package/dist/extraction/dfm-extractor.js.map +1 -0
  76. package/dist/extraction/extraction-version.d.ts +25 -0
  77. package/dist/extraction/extraction-version.d.ts.map +1 -0
  78. package/dist/extraction/extraction-version.js +28 -0
  79. package/dist/extraction/extraction-version.js.map +1 -0
  80. package/dist/extraction/function-ref.d.ts +118 -0
  81. package/dist/extraction/function-ref.d.ts.map +1 -0
  82. package/dist/extraction/function-ref.js +733 -0
  83. package/dist/extraction/function-ref.js.map +1 -0
  84. package/dist/extraction/generated-detection.d.ts +66 -0
  85. package/dist/extraction/generated-detection.d.ts.map +1 -0
  86. package/dist/extraction/generated-detection.js +248 -0
  87. package/dist/extraction/generated-detection.js.map +1 -0
  88. package/dist/extraction/grammars.d.ts +138 -0
  89. package/dist/extraction/grammars.d.ts.map +1 -0
  90. package/dist/extraction/grammars.js +694 -0
  91. package/dist/extraction/grammars.js.map +1 -0
  92. package/dist/extraction/index.d.ts +308 -0
  93. package/dist/extraction/index.d.ts.map +1 -0
  94. package/dist/extraction/index.js +2711 -0
  95. package/dist/extraction/index.js.map +1 -0
  96. package/dist/extraction/kernel/decode.d.ts +9 -0
  97. package/dist/extraction/kernel/decode.d.ts.map +1 -0
  98. package/dist/extraction/kernel/decode.js +162 -0
  99. package/dist/extraction/kernel/decode.js.map +1 -0
  100. package/dist/extraction/kernel/index.d.ts +64 -0
  101. package/dist/extraction/kernel/index.d.ts.map +1 -0
  102. package/dist/extraction/kernel/index.js +279 -0
  103. package/dist/extraction/kernel/index.js.map +1 -0
  104. package/dist/extraction/kernel/layout.d.ts +101 -0
  105. package/dist/extraction/kernel/layout.d.ts.map +1 -0
  106. package/dist/extraction/kernel/layout.js +104 -0
  107. package/dist/extraction/kernel/layout.js.map +1 -0
  108. package/dist/extraction/kernel/loader.d.ts +103 -0
  109. package/dist/extraction/kernel/loader.d.ts.map +1 -0
  110. package/dist/extraction/kernel/loader.js +146 -0
  111. package/dist/extraction/kernel/loader.js.map +1 -0
  112. package/dist/extraction/languages/arkts.d.ts +3 -0
  113. package/dist/extraction/languages/arkts.d.ts.map +1 -0
  114. package/dist/extraction/languages/arkts.js +127 -0
  115. package/dist/extraction/languages/arkts.js.map +1 -0
  116. package/dist/extraction/languages/c-cpp.d.ts +124 -0
  117. package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
  118. package/dist/extraction/languages/c-cpp.js +1689 -0
  119. package/dist/extraction/languages/c-cpp.js.map +1 -0
  120. package/dist/extraction/languages/cfquery.d.ts +12 -0
  121. package/dist/extraction/languages/cfquery.d.ts.map +1 -0
  122. package/dist/extraction/languages/cfquery.js +28 -0
  123. package/dist/extraction/languages/cfquery.js.map +1 -0
  124. package/dist/extraction/languages/cfscript.d.ts +3 -0
  125. package/dist/extraction/languages/cfscript.d.ts.map +1 -0
  126. package/dist/extraction/languages/cfscript.js +73 -0
  127. package/dist/extraction/languages/cfscript.js.map +1 -0
  128. package/dist/extraction/languages/cobol.d.ts +33 -0
  129. package/dist/extraction/languages/cobol.d.ts.map +1 -0
  130. package/dist/extraction/languages/cobol.js +499 -0
  131. package/dist/extraction/languages/cobol.js.map +1 -0
  132. package/dist/extraction/languages/csharp.d.ts +25 -0
  133. package/dist/extraction/languages/csharp.d.ts.map +1 -0
  134. package/dist/extraction/languages/csharp.js +175 -0
  135. package/dist/extraction/languages/csharp.js.map +1 -0
  136. package/dist/extraction/languages/dart.d.ts +3 -0
  137. package/dist/extraction/languages/dart.d.ts.map +1 -0
  138. package/dist/extraction/languages/dart.js +374 -0
  139. package/dist/extraction/languages/dart.js.map +1 -0
  140. package/dist/extraction/languages/erlang.d.ts +3 -0
  141. package/dist/extraction/languages/erlang.d.ts.map +1 -0
  142. package/dist/extraction/languages/erlang.js +350 -0
  143. package/dist/extraction/languages/erlang.js.map +1 -0
  144. package/dist/extraction/languages/go.d.ts +3 -0
  145. package/dist/extraction/languages/go.d.ts.map +1 -0
  146. package/dist/extraction/languages/go.js +111 -0
  147. package/dist/extraction/languages/go.js.map +1 -0
  148. package/dist/extraction/languages/index.d.ts +10 -0
  149. package/dist/extraction/languages/index.d.ts.map +1 -0
  150. package/dist/extraction/languages/index.js +71 -0
  151. package/dist/extraction/languages/index.js.map +1 -0
  152. package/dist/extraction/languages/java.d.ts +3 -0
  153. package/dist/extraction/languages/java.d.ts.map +1 -0
  154. package/dist/extraction/languages/java.js +315 -0
  155. package/dist/extraction/languages/java.js.map +1 -0
  156. package/dist/extraction/languages/javascript.d.ts +3 -0
  157. package/dist/extraction/languages/javascript.d.ts.map +1 -0
  158. package/dist/extraction/languages/javascript.js +106 -0
  159. package/dist/extraction/languages/javascript.js.map +1 -0
  160. package/dist/extraction/languages/kotlin.d.ts +3 -0
  161. package/dist/extraction/languages/kotlin.d.ts.map +1 -0
  162. package/dist/extraction/languages/kotlin.js +379 -0
  163. package/dist/extraction/languages/kotlin.js.map +1 -0
  164. package/dist/extraction/languages/lua.d.ts +3 -0
  165. package/dist/extraction/languages/lua.d.ts.map +1 -0
  166. package/dist/extraction/languages/lua.js +150 -0
  167. package/dist/extraction/languages/lua.js.map +1 -0
  168. package/dist/extraction/languages/luau.d.ts +3 -0
  169. package/dist/extraction/languages/luau.d.ts.map +1 -0
  170. package/dist/extraction/languages/luau.js +37 -0
  171. package/dist/extraction/languages/luau.js.map +1 -0
  172. package/dist/extraction/languages/nix.d.ts +3 -0
  173. package/dist/extraction/languages/nix.d.ts.map +1 -0
  174. package/dist/extraction/languages/nix.js +294 -0
  175. package/dist/extraction/languages/nix.js.map +1 -0
  176. package/dist/extraction/languages/objc.d.ts +3 -0
  177. package/dist/extraction/languages/objc.d.ts.map +1 -0
  178. package/dist/extraction/languages/objc.js +179 -0
  179. package/dist/extraction/languages/objc.js.map +1 -0
  180. package/dist/extraction/languages/pascal.d.ts +3 -0
  181. package/dist/extraction/languages/pascal.d.ts.map +1 -0
  182. package/dist/extraction/languages/pascal.js +77 -0
  183. package/dist/extraction/languages/pascal.js.map +1 -0
  184. package/dist/extraction/languages/php.d.ts +3 -0
  185. package/dist/extraction/languages/php.d.ts.map +1 -0
  186. package/dist/extraction/languages/php.js +196 -0
  187. package/dist/extraction/languages/php.js.map +1 -0
  188. package/dist/extraction/languages/python.d.ts +3 -0
  189. package/dist/extraction/languages/python.d.ts.map +1 -0
  190. package/dist/extraction/languages/python.js +56 -0
  191. package/dist/extraction/languages/python.js.map +1 -0
  192. package/dist/extraction/languages/r.d.ts +3 -0
  193. package/dist/extraction/languages/r.d.ts.map +1 -0
  194. package/dist/extraction/languages/r.js +314 -0
  195. package/dist/extraction/languages/r.js.map +1 -0
  196. package/dist/extraction/languages/ruby.d.ts +3 -0
  197. package/dist/extraction/languages/ruby.d.ts.map +1 -0
  198. package/dist/extraction/languages/ruby.js +149 -0
  199. package/dist/extraction/languages/ruby.js.map +1 -0
  200. package/dist/extraction/languages/rust.d.ts +3 -0
  201. package/dist/extraction/languages/rust.d.ts.map +1 -0
  202. package/dist/extraction/languages/rust.js +145 -0
  203. package/dist/extraction/languages/rust.js.map +1 -0
  204. package/dist/extraction/languages/scala.d.ts +3 -0
  205. package/dist/extraction/languages/scala.d.ts.map +1 -0
  206. package/dist/extraction/languages/scala.js +209 -0
  207. package/dist/extraction/languages/scala.js.map +1 -0
  208. package/dist/extraction/languages/solidity.d.ts +3 -0
  209. package/dist/extraction/languages/solidity.d.ts.map +1 -0
  210. package/dist/extraction/languages/solidity.js +293 -0
  211. package/dist/extraction/languages/solidity.js.map +1 -0
  212. package/dist/extraction/languages/swift.d.ts +3 -0
  213. package/dist/extraction/languages/swift.d.ts.map +1 -0
  214. package/dist/extraction/languages/swift.js +152 -0
  215. package/dist/extraction/languages/swift.js.map +1 -0
  216. package/dist/extraction/languages/terraform.d.ts +3 -0
  217. package/dist/extraction/languages/terraform.d.ts.map +1 -0
  218. package/dist/extraction/languages/terraform.js +641 -0
  219. package/dist/extraction/languages/terraform.js.map +1 -0
  220. package/dist/extraction/languages/typescript.d.ts +16 -0
  221. package/dist/extraction/languages/typescript.d.ts.map +1 -0
  222. package/dist/extraction/languages/typescript.js +167 -0
  223. package/dist/extraction/languages/typescript.js.map +1 -0
  224. package/dist/extraction/languages/vbnet.d.ts +11 -0
  225. package/dist/extraction/languages/vbnet.d.ts.map +1 -0
  226. package/dist/extraction/languages/vbnet.js +141 -0
  227. package/dist/extraction/languages/vbnet.js.map +1 -0
  228. package/dist/extraction/liquid-extractor.d.ts +59 -0
  229. package/dist/extraction/liquid-extractor.d.ts.map +1 -0
  230. package/dist/extraction/liquid-extractor.js +357 -0
  231. package/dist/extraction/liquid-extractor.js.map +1 -0
  232. package/dist/extraction/mybatis-extractor.d.ts +68 -0
  233. package/dist/extraction/mybatis-extractor.d.ts.map +1 -0
  234. package/dist/extraction/mybatis-extractor.js +300 -0
  235. package/dist/extraction/mybatis-extractor.js.map +1 -0
  236. package/dist/extraction/parse-pool.d.ts +160 -0
  237. package/dist/extraction/parse-pool.d.ts.map +1 -0
  238. package/dist/extraction/parse-pool.js +402 -0
  239. package/dist/extraction/parse-pool.js.map +1 -0
  240. package/dist/extraction/parse-worker.d.ts +8 -0
  241. package/dist/extraction/parse-worker.d.ts.map +1 -0
  242. package/dist/extraction/parse-worker.js +138 -0
  243. package/dist/extraction/parse-worker.js.map +1 -0
  244. package/dist/extraction/razor-extractor.d.ts +42 -0
  245. package/dist/extraction/razor-extractor.d.ts.map +1 -0
  246. package/dist/extraction/razor-extractor.js +285 -0
  247. package/dist/extraction/razor-extractor.js.map +1 -0
  248. package/dist/extraction/store-worker.d.ts +24 -0
  249. package/dist/extraction/store-worker.d.ts.map +1 -0
  250. package/dist/extraction/store-worker.js +139 -0
  251. package/dist/extraction/store-worker.js.map +1 -0
  252. package/dist/extraction/store-writer.d.ts +63 -0
  253. package/dist/extraction/store-writer.d.ts.map +1 -0
  254. package/dist/extraction/store-writer.js +174 -0
  255. package/dist/extraction/store-writer.js.map +1 -0
  256. package/dist/extraction/svelte-extractor.d.ts +56 -0
  257. package/dist/extraction/svelte-extractor.d.ts.map +1 -0
  258. package/dist/extraction/svelte-extractor.js +275 -0
  259. package/dist/extraction/svelte-extractor.js.map +1 -0
  260. package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
  261. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
  262. package/dist/extraction/tree-sitter-helpers.js +153 -0
  263. package/dist/extraction/tree-sitter-helpers.js.map +1 -0
  264. package/dist/extraction/tree-sitter-types.d.ts +260 -0
  265. package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
  266. package/dist/extraction/tree-sitter-types.js +10 -0
  267. package/dist/extraction/tree-sitter-types.js.map +1 -0
  268. package/dist/extraction/tree-sitter.d.ts +728 -0
  269. package/dist/extraction/tree-sitter.d.ts.map +1 -0
  270. package/dist/extraction/tree-sitter.js +6676 -0
  271. package/dist/extraction/tree-sitter.js.map +1 -0
  272. package/dist/extraction/vue-extractor.d.ts +51 -0
  273. package/dist/extraction/vue-extractor.d.ts.map +1 -0
  274. package/dist/extraction/vue-extractor.js +254 -0
  275. package/dist/extraction/vue-extractor.js.map +1 -0
  276. package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
  277. package/dist/extraction/wasm/tree-sitter-c.wasm +0 -0
  278. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  279. package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  280. package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  281. package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  282. package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  283. package/dist/extraction/wasm/tree-sitter-cpp.wasm +0 -0
  284. package/dist/extraction/wasm/tree-sitter-dart.wasm +0 -0
  285. package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  286. package/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  287. package/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  288. package/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  289. package/dist/extraction/wasm/tree-sitter-kotlin.wasm +0 -0
  290. package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
  291. package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
  292. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  293. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  294. package/dist/extraction/wasm/tree-sitter-php.wasm +0 -0
  295. package/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  296. package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
  297. package/dist/extraction/wasm/tree-sitter-ruby.wasm +0 -0
  298. package/dist/extraction/wasm/tree-sitter-rust.wasm +0 -0
  299. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  300. package/dist/extraction/wasm/tree-sitter-swift.wasm +0 -0
  301. package/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
  302. package/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  303. package/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  304. package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  305. package/dist/extraction/wasm-runtime-flags.d.ts +53 -0
  306. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
  307. package/dist/extraction/wasm-runtime-flags.js +130 -0
  308. package/dist/extraction/wasm-runtime-flags.js.map +1 -0
  309. package/dist/graph/index.d.ts +8 -0
  310. package/dist/graph/index.d.ts.map +1 -0
  311. package/dist/graph/index.js +13 -0
  312. package/dist/graph/index.js.map +1 -0
  313. package/dist/graph/queries.d.ts +106 -0
  314. package/dist/graph/queries.d.ts.map +1 -0
  315. package/dist/graph/queries.js +341 -0
  316. package/dist/graph/queries.js.map +1 -0
  317. package/dist/graph/traversal.d.ts +127 -0
  318. package/dist/graph/traversal.d.ts.map +1 -0
  319. package/dist/graph/traversal.js +599 -0
  320. package/dist/graph/traversal.js.map +1 -0
  321. package/dist/index.d.ts +677 -0
  322. package/dist/index.d.ts.map +1 -0
  323. package/dist/index.js +1718 -0
  324. package/dist/index.js.map +1 -0
  325. package/dist/installer/beta-signup.d.ts +54 -0
  326. package/dist/installer/beta-signup.d.ts.map +1 -0
  327. package/dist/installer/beta-signup.js +178 -0
  328. package/dist/installer/beta-signup.js.map +1 -0
  329. package/dist/installer/config-writer.d.ts +28 -0
  330. package/dist/installer/config-writer.d.ts.map +1 -0
  331. package/dist/installer/config-writer.js +91 -0
  332. package/dist/installer/config-writer.js.map +1 -0
  333. package/dist/installer/index.d.ts +144 -0
  334. package/dist/installer/index.d.ts.map +1 -0
  335. package/dist/installer/index.js +634 -0
  336. package/dist/installer/index.js.map +1 -0
  337. package/dist/installer/instructions-template.d.ts +41 -0
  338. package/dist/installer/instructions-template.d.ts.map +1 -0
  339. package/dist/installer/instructions-template.js +53 -0
  340. package/dist/installer/instructions-template.js.map +1 -0
  341. package/dist/installer/targets/antigravity.d.ts +57 -0
  342. package/dist/installer/targets/antigravity.d.ts.map +1 -0
  343. package/dist/installer/targets/antigravity.js +308 -0
  344. package/dist/installer/targets/antigravity.js.map +1 -0
  345. package/dist/installer/targets/claude.d.ts +62 -0
  346. package/dist/installer/targets/claude.d.ts.map +1 -0
  347. package/dist/installer/targets/claude.js +486 -0
  348. package/dist/installer/targets/claude.js.map +1 -0
  349. package/dist/installer/targets/codex.d.ts +18 -0
  350. package/dist/installer/targets/codex.d.ts.map +1 -0
  351. package/dist/installer/targets/codex.js +185 -0
  352. package/dist/installer/targets/codex.js.map +1 -0
  353. package/dist/installer/targets/copilot-cli.d.ts +32 -0
  354. package/dist/installer/targets/copilot-cli.d.ts.map +1 -0
  355. package/dist/installer/targets/copilot-cli.js +209 -0
  356. package/dist/installer/targets/copilot-cli.js.map +1 -0
  357. package/dist/installer/targets/copilot-jetbrains.d.ts +42 -0
  358. package/dist/installer/targets/copilot-jetbrains.d.ts.map +1 -0
  359. package/dist/installer/targets/copilot-jetbrains.js +238 -0
  360. package/dist/installer/targets/copilot-jetbrains.js.map +1 -0
  361. package/dist/installer/targets/copilot-vscode.d.ts +47 -0
  362. package/dist/installer/targets/copilot-vscode.d.ts.map +1 -0
  363. package/dist/installer/targets/copilot-vscode.js +219 -0
  364. package/dist/installer/targets/copilot-vscode.js.map +1 -0
  365. package/dist/installer/targets/cursor.d.ts +35 -0
  366. package/dist/installer/targets/cursor.d.ts.map +1 -0
  367. package/dist/installer/targets/cursor.js +254 -0
  368. package/dist/installer/targets/cursor.js.map +1 -0
  369. package/dist/installer/targets/dsh.d.ts +97 -0
  370. package/dist/installer/targets/dsh.d.ts.map +1 -0
  371. package/dist/installer/targets/dsh.js +358 -0
  372. package/dist/installer/targets/dsh.js.map +1 -0
  373. package/dist/installer/targets/gemini.d.ts +26 -0
  374. package/dist/installer/targets/gemini.d.ts.map +1 -0
  375. package/dist/installer/targets/gemini.js +165 -0
  376. package/dist/installer/targets/gemini.js.map +1 -0
  377. package/dist/installer/targets/hermes.d.ts +18 -0
  378. package/dist/installer/targets/hermes.d.ts.map +1 -0
  379. package/dist/installer/targets/hermes.js +359 -0
  380. package/dist/installer/targets/hermes.js.map +1 -0
  381. package/dist/installer/targets/kiro.d.ts +27 -0
  382. package/dist/installer/targets/kiro.d.ts.map +1 -0
  383. package/dist/installer/targets/kiro.js +178 -0
  384. package/dist/installer/targets/kiro.js.map +1 -0
  385. package/dist/installer/targets/opencode.d.ts +38 -0
  386. package/dist/installer/targets/opencode.d.ts.map +1 -0
  387. package/dist/installer/targets/opencode.js +288 -0
  388. package/dist/installer/targets/opencode.js.map +1 -0
  389. package/dist/installer/targets/registry.d.ts +35 -0
  390. package/dist/installer/targets/registry.d.ts.map +1 -0
  391. package/dist/installer/targets/registry.js +101 -0
  392. package/dist/installer/targets/registry.js.map +1 -0
  393. package/dist/installer/targets/shared.d.ts +132 -0
  394. package/dist/installer/targets/shared.d.ts.map +1 -0
  395. package/dist/installer/targets/shared.js +314 -0
  396. package/dist/installer/targets/shared.js.map +1 -0
  397. package/dist/installer/targets/toml.d.ts +53 -0
  398. package/dist/installer/targets/toml.d.ts.map +1 -0
  399. package/dist/installer/targets/toml.js +226 -0
  400. package/dist/installer/targets/toml.js.map +1 -0
  401. package/dist/installer/targets/types.d.ts +109 -0
  402. package/dist/installer/targets/types.d.ts.map +1 -0
  403. package/dist/installer/targets/types.js +16 -0
  404. package/dist/installer/targets/types.js.map +1 -0
  405. package/dist/installer/targets/zcode.d.ts +106 -0
  406. package/dist/installer/targets/zcode.d.ts.map +1 -0
  407. package/dist/installer/targets/zcode.js +451 -0
  408. package/dist/installer/targets/zcode.js.map +1 -0
  409. package/dist/mcp/daemon-manager.d.ts +42 -0
  410. package/dist/mcp/daemon-manager.d.ts.map +1 -0
  411. package/dist/mcp/daemon-manager.js +129 -0
  412. package/dist/mcp/daemon-manager.js.map +1 -0
  413. package/dist/mcp/daemon-paths.d.ts +73 -0
  414. package/dist/mcp/daemon-paths.d.ts.map +1 -0
  415. package/dist/mcp/daemon-paths.js +165 -0
  416. package/dist/mcp/daemon-paths.js.map +1 -0
  417. package/dist/mcp/daemon-registry.d.ts +47 -0
  418. package/dist/mcp/daemon-registry.d.ts.map +1 -0
  419. package/dist/mcp/daemon-registry.js +233 -0
  420. package/dist/mcp/daemon-registry.js.map +1 -0
  421. package/dist/mcp/daemon.d.ts +290 -0
  422. package/dist/mcp/daemon.d.ts.map +1 -0
  423. package/dist/mcp/daemon.js +862 -0
  424. package/dist/mcp/daemon.js.map +1 -0
  425. package/dist/mcp/dynamic-boundaries.d.ts +41 -0
  426. package/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
  427. package/dist/mcp/dynamic-boundaries.js +360 -0
  428. package/dist/mcp/dynamic-boundaries.js.map +1 -0
  429. package/dist/mcp/early-ppid.d.ts +26 -0
  430. package/dist/mcp/early-ppid.d.ts.map +1 -0
  431. package/dist/mcp/early-ppid.js +29 -0
  432. package/dist/mcp/early-ppid.js.map +1 -0
  433. package/dist/mcp/engine.d.ts +122 -0
  434. package/dist/mcp/engine.d.ts.map +1 -0
  435. package/dist/mcp/engine.js +350 -0
  436. package/dist/mcp/engine.js.map +1 -0
  437. package/dist/mcp/explore-dedup.d.ts +137 -0
  438. package/dist/mcp/explore-dedup.d.ts.map +1 -0
  439. package/dist/mcp/explore-dedup.js +236 -0
  440. package/dist/mcp/explore-dedup.js.map +1 -0
  441. package/dist/mcp/explore-diagnostics.d.ts +287 -0
  442. package/dist/mcp/explore-diagnostics.d.ts.map +1 -0
  443. package/dist/mcp/explore-diagnostics.js +555 -0
  444. package/dist/mcp/explore-diagnostics.js.map +1 -0
  445. package/dist/mcp/explore-session-state.d.ts +217 -0
  446. package/dist/mcp/explore-session-state.d.ts.map +1 -0
  447. package/dist/mcp/explore-session-state.js +322 -0
  448. package/dist/mcp/explore-session-state.js.map +1 -0
  449. package/dist/mcp/index.d.ts +137 -0
  450. package/dist/mcp/index.d.ts.map +1 -0
  451. package/dist/mcp/index.js +554 -0
  452. package/dist/mcp/index.js.map +1 -0
  453. package/dist/mcp/liveness-watchdog.d.ts +35 -0
  454. package/dist/mcp/liveness-watchdog.d.ts.map +1 -0
  455. package/dist/mcp/liveness-watchdog.js +269 -0
  456. package/dist/mcp/liveness-watchdog.js.map +1 -0
  457. package/dist/mcp/ppid-watchdog.d.ts +62 -0
  458. package/dist/mcp/ppid-watchdog.d.ts.map +1 -0
  459. package/dist/mcp/ppid-watchdog.js +64 -0
  460. package/dist/mcp/ppid-watchdog.js.map +1 -0
  461. package/dist/mcp/proxy.d.ts +87 -0
  462. package/dist/mcp/proxy.d.ts.map +1 -0
  463. package/dist/mcp/proxy.js +672 -0
  464. package/dist/mcp/proxy.js.map +1 -0
  465. package/dist/mcp/query-pool.d.ts +108 -0
  466. package/dist/mcp/query-pool.d.ts.map +1 -0
  467. package/dist/mcp/query-pool.js +315 -0
  468. package/dist/mcp/query-pool.js.map +1 -0
  469. package/dist/mcp/query-worker.d.ts +24 -0
  470. package/dist/mcp/query-worker.d.ts.map +1 -0
  471. package/dist/mcp/query-worker.js +87 -0
  472. package/dist/mcp/query-worker.js.map +1 -0
  473. package/dist/mcp/server-instructions.d.ts +34 -0
  474. package/dist/mcp/server-instructions.d.ts.map +1 -0
  475. package/dist/mcp/server-instructions.js +111 -0
  476. package/dist/mcp/server-instructions.js.map +1 -0
  477. package/dist/mcp/session.d.ts +109 -0
  478. package/dist/mcp/session.d.ts.map +1 -0
  479. package/dist/mcp/session.js +375 -0
  480. package/dist/mcp/session.js.map +1 -0
  481. package/dist/mcp/startup-handshake.d.ts +44 -0
  482. package/dist/mcp/startup-handshake.d.ts.map +1 -0
  483. package/dist/mcp/startup-handshake.js +73 -0
  484. package/dist/mcp/startup-handshake.js.map +1 -0
  485. package/dist/mcp/stdin-teardown.d.ts +27 -0
  486. package/dist/mcp/stdin-teardown.d.ts.map +1 -0
  487. package/dist/mcp/stdin-teardown.js +49 -0
  488. package/dist/mcp/stdin-teardown.js.map +1 -0
  489. package/dist/mcp/tools.d.ts +862 -0
  490. package/dist/mcp/tools.d.ts.map +1 -0
  491. package/dist/mcp/tools.js +6526 -0
  492. package/dist/mcp/tools.js.map +1 -0
  493. package/dist/mcp/transport.d.ts +188 -0
  494. package/dist/mcp/transport.d.ts.map +1 -0
  495. package/dist/mcp/transport.js +377 -0
  496. package/dist/mcp/transport.js.map +1 -0
  497. package/dist/mcp/version.d.ts +19 -0
  498. package/dist/mcp/version.d.ts.map +1 -0
  499. package/dist/mcp/version.js +71 -0
  500. package/dist/mcp/version.js.map +1 -0
  501. package/dist/project-config.d.ts +94 -0
  502. package/dist/project-config.d.ts.map +1 -0
  503. package/dist/project-config.js +374 -0
  504. package/dist/project-config.js.map +1 -0
  505. package/dist/prompt-hook-output.d.ts +23 -0
  506. package/dist/prompt-hook-output.d.ts.map +1 -0
  507. package/dist/prompt-hook-output.js +35 -0
  508. package/dist/prompt-hook-output.js.map +1 -0
  509. package/dist/resolution/c-fnptr-synthesizer.d.ts +6 -0
  510. package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
  511. package/dist/resolution/c-fnptr-synthesizer.js +1506 -0
  512. package/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
  513. package/dist/resolution/callback-synthesizer.d.ts +59 -0
  514. package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
  515. package/dist/resolution/callback-synthesizer.js +4030 -0
  516. package/dist/resolution/callback-synthesizer.js.map +1 -0
  517. package/dist/resolution/cooperative-yield.d.ts +40 -0
  518. package/dist/resolution/cooperative-yield.d.ts.map +1 -0
  519. package/dist/resolution/cooperative-yield.js +44 -0
  520. package/dist/resolution/cooperative-yield.js.map +1 -0
  521. package/dist/resolution/frameworks/astro.d.ts +9 -0
  522. package/dist/resolution/frameworks/astro.d.ts.map +1 -0
  523. package/dist/resolution/frameworks/astro.js +169 -0
  524. package/dist/resolution/frameworks/astro.js.map +1 -0
  525. package/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
  526. package/dist/resolution/frameworks/cargo-workspace.d.ts.map +1 -0
  527. package/dist/resolution/frameworks/cargo-workspace.js +225 -0
  528. package/dist/resolution/frameworks/cargo-workspace.js.map +1 -0
  529. package/dist/resolution/frameworks/cics.d.ts +20 -0
  530. package/dist/resolution/frameworks/cics.d.ts.map +1 -0
  531. package/dist/resolution/frameworks/cics.js +90 -0
  532. package/dist/resolution/frameworks/cics.js.map +1 -0
  533. package/dist/resolution/frameworks/csharp.d.ts +8 -0
  534. package/dist/resolution/frameworks/csharp.d.ts.map +1 -0
  535. package/dist/resolution/frameworks/csharp.js +241 -0
  536. package/dist/resolution/frameworks/csharp.js.map +1 -0
  537. package/dist/resolution/frameworks/drupal.d.ts +51 -0
  538. package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
  539. package/dist/resolution/frameworks/drupal.js +367 -0
  540. package/dist/resolution/frameworks/drupal.js.map +1 -0
  541. package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
  542. package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
  543. package/dist/resolution/frameworks/expo-modules.js +148 -0
  544. package/dist/resolution/frameworks/expo-modules.js.map +1 -0
  545. package/dist/resolution/frameworks/express.d.ts +8 -0
  546. package/dist/resolution/frameworks/express.d.ts.map +1 -0
  547. package/dist/resolution/frameworks/express.js +308 -0
  548. package/dist/resolution/frameworks/express.js.map +1 -0
  549. package/dist/resolution/frameworks/fabric.d.ts +3 -0
  550. package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
  551. package/dist/resolution/frameworks/fabric.js +354 -0
  552. package/dist/resolution/frameworks/fabric.js.map +1 -0
  553. package/dist/resolution/frameworks/go.d.ts +8 -0
  554. package/dist/resolution/frameworks/go.d.ts.map +1 -0
  555. package/dist/resolution/frameworks/go.js +184 -0
  556. package/dist/resolution/frameworks/go.js.map +1 -0
  557. package/dist/resolution/frameworks/goframe.d.ts +41 -0
  558. package/dist/resolution/frameworks/goframe.d.ts.map +1 -0
  559. package/dist/resolution/frameworks/goframe.js +112 -0
  560. package/dist/resolution/frameworks/goframe.js.map +1 -0
  561. package/dist/resolution/frameworks/index.d.ts +50 -0
  562. package/dist/resolution/frameworks/index.d.ts.map +1 -0
  563. package/dist/resolution/frameworks/index.js +175 -0
  564. package/dist/resolution/frameworks/index.js.map +1 -0
  565. package/dist/resolution/frameworks/java.d.ts +8 -0
  566. package/dist/resolution/frameworks/java.d.ts.map +1 -0
  567. package/dist/resolution/frameworks/java.js +517 -0
  568. package/dist/resolution/frameworks/java.js.map +1 -0
  569. package/dist/resolution/frameworks/laravel.d.ts +13 -0
  570. package/dist/resolution/frameworks/laravel.d.ts.map +1 -0
  571. package/dist/resolution/frameworks/laravel.js +257 -0
  572. package/dist/resolution/frameworks/laravel.js.map +1 -0
  573. package/dist/resolution/frameworks/nestjs.d.ts +26 -0
  574. package/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
  575. package/dist/resolution/frameworks/nestjs.js +698 -0
  576. package/dist/resolution/frameworks/nestjs.js.map +1 -0
  577. package/dist/resolution/frameworks/play.d.ts +19 -0
  578. package/dist/resolution/frameworks/play.d.ts.map +1 -0
  579. package/dist/resolution/frameworks/play.js +111 -0
  580. package/dist/resolution/frameworks/play.js.map +1 -0
  581. package/dist/resolution/frameworks/python.d.ts +10 -0
  582. package/dist/resolution/frameworks/python.d.ts.map +1 -0
  583. package/dist/resolution/frameworks/python.js +400 -0
  584. package/dist/resolution/frameworks/python.js.map +1 -0
  585. package/dist/resolution/frameworks/react-native.d.ts +3 -0
  586. package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
  587. package/dist/resolution/frameworks/react-native.js +410 -0
  588. package/dist/resolution/frameworks/react-native.js.map +1 -0
  589. package/dist/resolution/frameworks/react.d.ts +8 -0
  590. package/dist/resolution/frameworks/react.d.ts.map +1 -0
  591. package/dist/resolution/frameworks/react.js +334 -0
  592. package/dist/resolution/frameworks/react.js.map +1 -0
  593. package/dist/resolution/frameworks/ruby.d.ts +8 -0
  594. package/dist/resolution/frameworks/ruby.d.ts.map +1 -0
  595. package/dist/resolution/frameworks/ruby.js +302 -0
  596. package/dist/resolution/frameworks/ruby.js.map +1 -0
  597. package/dist/resolution/frameworks/rust.d.ts +8 -0
  598. package/dist/resolution/frameworks/rust.d.ts.map +1 -0
  599. package/dist/resolution/frameworks/rust.js +304 -0
  600. package/dist/resolution/frameworks/rust.js.map +1 -0
  601. package/dist/resolution/frameworks/svelte.d.ts +9 -0
  602. package/dist/resolution/frameworks/svelte.d.ts.map +1 -0
  603. package/dist/resolution/frameworks/svelte.js +253 -0
  604. package/dist/resolution/frameworks/svelte.js.map +1 -0
  605. package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
  606. package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
  607. package/dist/resolution/frameworks/swift-objc.js +252 -0
  608. package/dist/resolution/frameworks/swift-objc.js.map +1 -0
  609. package/dist/resolution/frameworks/swift.d.ts +10 -0
  610. package/dist/resolution/frameworks/swift.d.ts.map +1 -0
  611. package/dist/resolution/frameworks/swift.js +400 -0
  612. package/dist/resolution/frameworks/swift.js.map +1 -0
  613. package/dist/resolution/frameworks/terraform.d.ts +38 -0
  614. package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
  615. package/dist/resolution/frameworks/terraform.js +277 -0
  616. package/dist/resolution/frameworks/terraform.js.map +1 -0
  617. package/dist/resolution/frameworks/vue.d.ts +9 -0
  618. package/dist/resolution/frameworks/vue.d.ts.map +1 -0
  619. package/dist/resolution/frameworks/vue.js +303 -0
  620. package/dist/resolution/frameworks/vue.js.map +1 -0
  621. package/dist/resolution/go-module.d.ts +26 -0
  622. package/dist/resolution/go-module.d.ts.map +1 -0
  623. package/dist/resolution/go-module.js +78 -0
  624. package/dist/resolution/go-module.js.map +1 -0
  625. package/dist/resolution/goframe-synthesizer.d.ts +29 -0
  626. package/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
  627. package/dist/resolution/goframe-synthesizer.js +163 -0
  628. package/dist/resolution/goframe-synthesizer.js.map +1 -0
  629. package/dist/resolution/import-resolver.d.ts +84 -0
  630. package/dist/resolution/import-resolver.d.ts.map +1 -0
  631. package/dist/resolution/import-resolver.js +2134 -0
  632. package/dist/resolution/import-resolver.js.map +1 -0
  633. package/dist/resolution/index.d.ts +343 -0
  634. package/dist/resolution/index.d.ts.map +1 -0
  635. package/dist/resolution/index.js +2339 -0
  636. package/dist/resolution/index.js.map +1 -0
  637. package/dist/resolution/lru-cache.d.ts +24 -0
  638. package/dist/resolution/lru-cache.d.ts.map +1 -0
  639. package/dist/resolution/lru-cache.js +62 -0
  640. package/dist/resolution/lru-cache.js.map +1 -0
  641. package/dist/resolution/memory-budget.d.ts +48 -0
  642. package/dist/resolution/memory-budget.d.ts.map +1 -0
  643. package/dist/resolution/memory-budget.js +162 -0
  644. package/dist/resolution/memory-budget.js.map +1 -0
  645. package/dist/resolution/name-matcher.d.ts +123 -0
  646. package/dist/resolution/name-matcher.d.ts.map +1 -0
  647. package/dist/resolution/name-matcher.js +2134 -0
  648. package/dist/resolution/name-matcher.js.map +1 -0
  649. package/dist/resolution/path-aliases.d.ts +68 -0
  650. package/dist/resolution/path-aliases.d.ts.map +1 -0
  651. package/dist/resolution/path-aliases.js +238 -0
  652. package/dist/resolution/path-aliases.js.map +1 -0
  653. package/dist/resolution/resolver-pool.d.ts +106 -0
  654. package/dist/resolution/resolver-pool.d.ts.map +1 -0
  655. package/dist/resolution/resolver-pool.js +344 -0
  656. package/dist/resolution/resolver-pool.js.map +1 -0
  657. package/dist/resolution/resolver-worker.d.ts +17 -0
  658. package/dist/resolution/resolver-worker.d.ts.map +1 -0
  659. package/dist/resolution/resolver-worker.js +141 -0
  660. package/dist/resolution/resolver-worker.js.map +1 -0
  661. package/dist/resolution/strip-comments.d.ts +27 -0
  662. package/dist/resolution/strip-comments.d.ts.map +1 -0
  663. package/dist/resolution/strip-comments.js +492 -0
  664. package/dist/resolution/strip-comments.js.map +1 -0
  665. package/dist/resolution/swift-objc-bridge.d.ts +134 -0
  666. package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
  667. package/dist/resolution/swift-objc-bridge.js +256 -0
  668. package/dist/resolution/swift-objc-bridge.js.map +1 -0
  669. package/dist/resolution/types.d.ts +265 -0
  670. package/dist/resolution/types.d.ts.map +1 -0
  671. package/dist/resolution/types.js +8 -0
  672. package/dist/resolution/types.js.map +1 -0
  673. package/dist/resolution/workspace-packages.d.ts +58 -0
  674. package/dist/resolution/workspace-packages.d.ts.map +1 -0
  675. package/dist/resolution/workspace-packages.js +346 -0
  676. package/dist/resolution/workspace-packages.js.map +1 -0
  677. package/dist/search/identifier-segments.d.ts +60 -0
  678. package/dist/search/identifier-segments.d.ts.map +1 -0
  679. package/dist/search/identifier-segments.js +176 -0
  680. package/dist/search/identifier-segments.js.map +1 -0
  681. package/dist/search/query-parser.d.ts +57 -0
  682. package/dist/search/query-parser.d.ts.map +1 -0
  683. package/dist/search/query-parser.js +177 -0
  684. package/dist/search/query-parser.js.map +1 -0
  685. package/dist/search/query-utils.d.ts +87 -0
  686. package/dist/search/query-utils.d.ts.map +1 -0
  687. package/dist/search/query-utils.js +450 -0
  688. package/dist/search/query-utils.js.map +1 -0
  689. package/dist/sync/git-hooks.d.ts +45 -0
  690. package/dist/sync/git-hooks.d.ts.map +1 -0
  691. package/dist/sync/git-hooks.js +227 -0
  692. package/dist/sync/git-hooks.js.map +1 -0
  693. package/dist/sync/index.d.ts +19 -0
  694. package/dist/sync/index.d.ts.map +1 -0
  695. package/dist/sync/index.js +35 -0
  696. package/dist/sync/index.js.map +1 -0
  697. package/dist/sync/watch-policy.d.ts +48 -0
  698. package/dist/sync/watch-policy.d.ts.map +1 -0
  699. package/dist/sync/watch-policy.js +124 -0
  700. package/dist/sync/watch-policy.js.map +1 -0
  701. package/dist/sync/watcher.d.ts +379 -0
  702. package/dist/sync/watcher.d.ts.map +1 -0
  703. package/dist/sync/watcher.js +921 -0
  704. package/dist/sync/watcher.js.map +1 -0
  705. package/dist/sync/worktree.d.ts +63 -0
  706. package/dist/sync/worktree.d.ts.map +1 -0
  707. package/dist/sync/worktree.js +182 -0
  708. package/dist/sync/worktree.js.map +1 -0
  709. package/dist/telemetry/index.d.ts +143 -0
  710. package/dist/telemetry/index.d.ts.map +1 -0
  711. package/dist/telemetry/index.js +541 -0
  712. package/dist/telemetry/index.js.map +1 -0
  713. package/dist/types.d.ts +465 -0
  714. package/dist/types.d.ts.map +1 -0
  715. package/dist/types.js +117 -0
  716. package/dist/types.js.map +1 -0
  717. package/dist/ui/color.d.ts +21 -0
  718. package/dist/ui/color.d.ts.map +1 -0
  719. package/dist/ui/color.js +42 -0
  720. package/dist/ui/color.js.map +1 -0
  721. package/dist/ui/glyphs.d.ts +67 -0
  722. package/dist/ui/glyphs.d.ts.map +1 -0
  723. package/dist/ui/glyphs.js +125 -0
  724. package/dist/ui/glyphs.js.map +1 -0
  725. package/dist/ui/shimmer-progress.d.ts +11 -0
  726. package/dist/ui/shimmer-progress.d.ts.map +1 -0
  727. package/dist/ui/shimmer-progress.js +164 -0
  728. package/dist/ui/shimmer-progress.js.map +1 -0
  729. package/dist/ui/shimmer-worker.d.ts +2 -0
  730. package/dist/ui/shimmer-worker.d.ts.map +1 -0
  731. package/dist/ui/shimmer-worker.js +126 -0
  732. package/dist/ui/shimmer-worker.js.map +1 -0
  733. package/dist/ui/types.d.ts +15 -0
  734. package/dist/ui/types.d.ts.map +1 -0
  735. package/dist/ui/types.js +3 -0
  736. package/dist/ui/types.js.map +1 -0
  737. package/dist/upgrade/index.d.ts +171 -0
  738. package/dist/upgrade/index.d.ts.map +1 -0
  739. package/dist/upgrade/index.js +659 -0
  740. package/dist/upgrade/index.js.map +1 -0
  741. package/dist/upgrade/remove-binary.d.ts +87 -0
  742. package/dist/upgrade/remove-binary.d.ts.map +1 -0
  743. package/dist/upgrade/remove-binary.js +289 -0
  744. package/dist/upgrade/remove-binary.js.map +1 -0
  745. package/dist/upgrade/update-check.d.ts +92 -0
  746. package/dist/upgrade/update-check.d.ts.map +1 -0
  747. package/dist/upgrade/update-check.js +258 -0
  748. package/dist/upgrade/update-check.js.map +1 -0
  749. package/dist/utils.d.ts +224 -0
  750. package/dist/utils.d.ts.map +1 -0
  751. package/dist/utils.js +583 -0
  752. package/dist/utils.js.map +1 -0
  753. package/package.json +61 -0
  754. package/scripts/add-lang/bench.sh +60 -0
  755. package/scripts/add-lang/check-grammar.mjs +75 -0
  756. package/scripts/add-lang/dump-ast.mjs +103 -0
  757. package/scripts/add-lang/verify-extraction.mjs +70 -0
  758. package/scripts/agent-eval/ab-adoption.sh +91 -0
  759. package/scripts/agent-eval/ab-hook.sh +86 -0
  760. package/scripts/agent-eval/ab-impl.sh +78 -0
  761. package/scripts/agent-eval/ab-new-vs-baseline.sh +161 -0
  762. package/scripts/agent-eval/ab-sufficiency.sh +78 -0
  763. package/scripts/agent-eval/allocation-fixtures.json +344 -0
  764. package/scripts/agent-eval/arms-F.sh +21 -0
  765. package/scripts/agent-eval/arms-matrix.sh +37 -0
  766. package/scripts/agent-eval/audit.sh +68 -0
  767. package/scripts/agent-eval/bench-readme.sh +48 -0
  768. package/scripts/agent-eval/bench-why-repo.sh +22 -0
  769. package/scripts/agent-eval/block-read-hook.sh +19 -0
  770. package/scripts/agent-eval/compare-arms.mjs +209 -0
  771. package/scripts/agent-eval/diff-index-drift.mjs +102 -0
  772. package/scripts/agent-eval/hook-settings.json +15 -0
  773. package/scripts/agent-eval/itrun.sh +120 -0
  774. package/scripts/agent-eval/no-cli-shim.sh +96 -0
  775. package/scripts/agent-eval/offload-eval-3arm.sh +72 -0
  776. package/scripts/agent-eval/offload-eval-cost.mjs +133 -0
  777. package/scripts/agent-eval/offload-eval-effort.mjs +108 -0
  778. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -0
  779. package/scripts/agent-eval/offload-eval-frontload.sh +47 -0
  780. package/scripts/agent-eval/offload-eval-ground-truth.json +18 -0
  781. package/scripts/agent-eval/offload-eval-hook.mjs +84 -0
  782. package/scripts/agent-eval/offload-eval-judge.mjs +103 -0
  783. package/scripts/agent-eval/offload-eval-matrix.sh +20 -0
  784. package/scripts/agent-eval/offload-eval-metrics.mjs +94 -0
  785. package/scripts/agent-eval/offload-eval-refs1.sh +50 -0
  786. package/scripts/agent-eval/offload-eval-setup.sh +24 -0
  787. package/scripts/agent-eval/offload-eval-styles.sh +72 -0
  788. package/scripts/agent-eval/offload-eval-summarize.mjs +68 -0
  789. package/scripts/agent-eval/offload-eval.md +76 -0
  790. package/scripts/agent-eval/parse-arms.mjs +116 -0
  791. package/scripts/agent-eval/parse-bench-readme.mjs +238 -0
  792. package/scripts/agent-eval/parse-run.mjs +1401 -0
  793. package/scripts/agent-eval/parse-session.mjs +130 -0
  794. package/scripts/agent-eval/probe-allocation.mjs +335 -0
  795. package/scripts/agent-eval/probe-context.mjs +21 -0
  796. package/scripts/agent-eval/probe-decl-only.mjs +198 -0
  797. package/scripts/agent-eval/probe-explore.mjs +40 -0
  798. package/scripts/agent-eval/probe-factory-closure.mjs +147 -0
  799. package/scripts/agent-eval/probe-file-spend.mjs +195 -0
  800. package/scripts/agent-eval/probe-named-symbol.mjs +163 -0
  801. package/scripts/agent-eval/probe-node.mjs +20 -0
  802. package/scripts/agent-eval/probe-suite-envelope.mjs +124 -0
  803. package/scripts/agent-eval/probe-sweep.mjs +119 -0
  804. package/scripts/agent-eval/probe-trace.mjs +20 -0
  805. package/scripts/agent-eval/redirect-read-hook.sh +38 -0
  806. package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -0
  807. package/scripts/agent-eval/repro-daemon-clients.mjs +125 -0
  808. package/scripts/agent-eval/run-agent.sh +34 -0
  809. package/scripts/agent-eval/run-all.sh +149 -0
  810. package/scripts/agent-eval/run-arms.sh +56 -0
  811. package/scripts/agent-eval/seq-matrix.mjs +137 -0
  812. package/scripts/build-bundle.sh +145 -0
  813. package/scripts/build-kernel.sh +87 -0
  814. package/scripts/dump-graph.mjs +57 -0
  815. package/scripts/extract-release-notes.mjs +130 -0
  816. package/scripts/kernel-parity.mjs +283 -0
  817. package/scripts/local-install.sh +41 -0
  818. package/scripts/npm-sdk.js +75 -0
  819. package/scripts/npm-shim.js +279 -0
  820. package/scripts/pack-npm.sh +123 -0
  821. package/scripts/prepare-release.mjs +270 -0
@@ -0,0 +1,1401 @@
1
+ #!/usr/bin/env node
2
+ // Parse Claude Code stream-json run log(s): tool-call sequence, token usage, and
3
+ // RESIDUAL CONTEXT OCCUPANCY — how many tokens of the context window each tool
4
+ // family's responses still occupy when the run ends.
5
+ //
6
+ // Usage: parse-run.mjs <run.jsonl> [run.t2.jsonl ...] [--brief] [--envelope] [--answer <glob>]...
7
+ // Multiple files = one multi-turn session's segments, IN ORDER (run-all.sh
8
+ // writes run-<label>.jsonl, run-<label>.t2.jsonl, … for a `Q1||Q2||Q3` set).
9
+ // `--resume` does not replay prior messages, so the segments concatenate
10
+ // cleanly and token accounting carries across the boundary.
11
+ //
12
+ // `--brief` drops the numbered tool-call transcript and keeps everything else,
13
+ // for harnesses that print one of these blocks per run (ab-new-vs-baseline.sh
14
+ // at RUNS>=2 is otherwise mostly call listings).
15
+ //
16
+ // Every run also reports EXPLORE SUFFICIENCY — each codegraph_explore call
17
+ // bucketed by what the agent did next (see classifySufficiency) — and EXPLORE
18
+ // ALLOCATION EFFICIENCY, the share of the bytes explore returned that belonged
19
+ // to files the agent's final answer actually cited (see computeAllocation).
20
+ //
21
+ // `--envelope` additionally reports how the codegraph_explore responses were
22
+ // DIVIDED across files — the per-file share of the source envelope (#1500).
23
+ // `--answer <glob>` (repeatable, implies --envelope) marks the files that
24
+ // actually answer the question and reports their combined share: bar 2 of the
25
+ // CG-1/CG-22 allocation gate. See formatEnvelope for why it parses the
26
+ // rendered markdown rather than the CG-4 diagnostic sidecar.
27
+ //
28
+ // ---------------------------------------------------------------------------
29
+ // Why occupancy, and how it's measured
30
+ // ---------------------------------------------------------------------------
31
+ // A single-question A/B reports cost/tokens/time/tool-calls for ONE answer. It
32
+ // cannot see what issue #1500 measured: a tool response stays in the window for
33
+ // everything that follows, so it is charged against every later turn's headroom.
34
+ // That is a per-session cost our single-question runs structurally miss.
35
+ //
36
+ // Tokens are MEASURED, not estimated at bytes/4. For assistant request k,
37
+ // ctx_k = usage.input_tokens + cache_read_input_tokens + cache_creation_input_tokens
38
+ // is the exact token count of that request's whole prompt. So
39
+ // gap_k = ctx_k - ctx_{k-1}
40
+ // is exactly the tokens appended since the previous request: the previous
41
+ // assistant output (thinking + text + tool_use JSON) plus the tool_results and
42
+ // user text that followed it. We split gap_k across those blocks in proportion
43
+ // to their characters, which attributes each tool_result its measured share.
44
+ // (Measured on real runs, explore output lands near 2.3 chars/token — bytes/4
45
+ // under-counts it by ~40%, which is why the estimate isn't good enough.)
46
+ //
47
+ // Two traps this file works around, both verified against real logs:
48
+ // * Claude Code emits ONE assistant event PER CONTENT BLOCK, all carrying the
49
+ // same message.id and the same `usage`. Summing usage per event double-counts
50
+ // every turn that emits both thinking and a tool_use — dedupe by message.id.
51
+ // * The streamed `output_tokens` is a partial snapshot (observed `out=2` on a
52
+ // turn that really generated ~1100). Never trust it; the char-proportional
53
+ // split doesn't need it.
54
+ //
55
+ // Residual ≠ contributed. Content leaves the window two ways, and both are
56
+ // tracked: a `compact_boundary` system event (everything prior is replaced by a
57
+ // summary) and micro-compaction (ctx drops mid-run — oldest tool results are
58
+ // dropped first, so eviction is applied FIFO).
59
+ import { readFileSync } from 'fs';
60
+ import { pathToFileURL } from 'url';
61
+
62
+ // Nominal window for the share-of-window column. Override for a [1m] context.
63
+ const WINDOW_TOKENS = Number(process.env.CG_WINDOW_TOKENS || 200_000);
64
+ const CHARS_PER_TOKEN_FALLBACK = 3.0;
65
+
66
+ /** Which tool family a tool_use belongs to. */
67
+ function familyOf(name) {
68
+ if (/codegraph/.test(name)) return 'codegraph';
69
+ if (name === 'Read' || name === 'NotebookRead') return 'read';
70
+ if (name === 'Grep' || name === 'Glob') return 'search';
71
+ if (name === 'Bash' || name === 'BashOutput') return 'bash';
72
+ return 'other';
73
+ }
74
+ const FAMILIES = ['codegraph', 'read', 'search', 'bash', 'other'];
75
+ // The without-arm's way of getting the same bytes: reading and searching files.
76
+ const FILE_ACCESS = ['read', 'search', 'bash'];
77
+
78
+ // A Bash command that INVOKES the codegraph CLI, in any command position and by
79
+ // any path. Mentions are not invocations: `grep codegraph src/`, `ls .codegraph`
80
+ // and `which codegraph` all pass. Kept in step with run-all.sh's blocking hook.
81
+ const CG_CLI_RE = /(^|[;&|(]|&&|\|\||\$\(|`)\s*(?:[A-Za-z_]\w*=\S*\s+)*[\w./~-]*codegraph(\s|$)/;
82
+
83
+ const textOf = (content) =>
84
+ Array.isArray(content) ? content.map((c) => c.text ?? (typeof c === 'string' ? c : JSON.stringify(c))).join('')
85
+ : typeof content === 'string' ? content
86
+ : content == null ? '' : JSON.stringify(content);
87
+
88
+ /** Characters an assistant content block occupies once it is back in the prompt. */
89
+ function assistantBlockChars(b) {
90
+ if (b.type === 'text') return (b.text || '').length;
91
+ if (b.type === 'thinking') return (b.thinking || '').length;
92
+ if (b.type === 'tool_use') return JSON.stringify(b.input ?? {}).length + (b.name || '').length;
93
+ return JSON.stringify(b).length;
94
+ }
95
+
96
+ /**
97
+ * Parse one session (its segment files, in order) into tool + occupancy stats.
98
+ * Exported so parse-bench-readme.mjs can aggregate without duplicating any of
99
+ * this — deliberately NOT a separate module file: a new scripts/agent-eval/*.mjs
100
+ * scores into the self-query eval fixture's own corpus and moves its numbers.
101
+ */
102
+ export function parseSession(files) {
103
+ const events = [];
104
+ for (const f of files) {
105
+ for (const line of readFileSync(f, 'utf8').split('\n')) {
106
+ if (!line) continue;
107
+ try { events.push(JSON.parse(line)); } catch { /* partial line */ }
108
+ }
109
+ }
110
+
111
+ const toolCalls = []; // display sequence
112
+ const nameById = new Map(); // tool_use_id -> tool name
113
+ const cliById = new Set(); // tool_use_ids that tried to run the codegraph CLI
114
+ const counts = {}; // tool name -> calls
115
+ // Attempts vs successes: run-all.sh's hook DENIES CLI invocations, and a
116
+ // denied attempt puts no codegraph output in the window. Only a call that
117
+ // actually returned content contaminates the arm.
118
+ let initTools = null, result = null, raced = false, cliCalls = 0, cliContaminated = 0;
119
+ const results = []; // one `result` event per session segment (multi-turn)
120
+ let compactions = 0;
121
+ // Raw codegraph_explore response text, in call order. Feeds the envelope view
122
+ // (see formatEnvelope) — kept here rather than re-parsed from the log later so
123
+ // a multi-segment session's responses stay in one ordered list.
124
+ const exploreTexts = [];
125
+
126
+ // A timeline of everything appended to the context, in order. `req` entries
127
+ // are assistant requests (carrying that request's ctx); `add` entries are
128
+ // characters appended (assistant output blocks, tool results, user text).
129
+ const timeline = [];
130
+ const seenMsgIds = new Set();
131
+
132
+ for (const ev of events) {
133
+ if (ev.type === 'system' && ev.subtype === 'init') {
134
+ initTools = (ev.tools || []).filter((t) => /codegraph/.test(t));
135
+ }
136
+ if (ev.type === 'system' && (ev.subtype === 'compact_boundary' || ev.subtype === 'compaction')) {
137
+ compactions++;
138
+ timeline.push({ kind: 'compact' });
139
+ }
140
+ if (ev.type === 'assistant' && ev.message) {
141
+ const id = ev.message.id;
142
+ // One event per content block, same id + same usage: count usage once,
143
+ // but take the content blocks from every event that carries the id.
144
+ if (id && !seenMsgIds.has(id)) {
145
+ seenMsgIds.add(id);
146
+ const u = ev.message.usage || {};
147
+ const ctx = (u.input_tokens || 0) + (u.cache_read_input_tokens || 0) + (u.cache_creation_input_tokens || 0);
148
+ timeline.push({ kind: 'req', ctx, out: u.output_tokens || 0 });
149
+ }
150
+ for (const b of ev.message.content || []) {
151
+ timeline.push({ kind: 'add', family: null, chars: assistantBlockChars(b) });
152
+ if (b.type === 'tool_use') {
153
+ nameById.set(b.id, b.name);
154
+ counts[b.name] = (counts[b.name] || 0) + 1;
155
+ let detail = '';
156
+ if (b.name === 'Task') detail = ` [subagent_type=${b.input?.subagent_type ?? '?'}] ${(b.input?.description ?? '').slice(0, 40)}`;
157
+ else if (/codegraph/.test(b.name)) detail = ` ${JSON.stringify(b.input?.query ?? b.input?.task ?? b.input?.symbol ?? '').slice(0, 60)}`;
158
+ else if (b.name === 'Bash') {
159
+ detail = ` ${(b.input?.command ?? '').slice(0, 50)}`;
160
+ // An arm with no codegraph MCP can still shell out to the CLI — the
161
+ // target repo carries the .codegraph/ index and the binary is on
162
+ // PATH. That silently turns a "without" arm into codegraph-over-CLI.
163
+ if (CG_CLI_RE.test(b.input?.command ?? '')) { cliCalls++; cliById.add(b.id); }
164
+ }
165
+ else if (b.name === 'Read') detail = ` ${(b.input?.file_path ?? '').split('/').slice(-1)[0]}`;
166
+ toolCalls.push(`${b.name}${detail}`);
167
+ }
168
+ }
169
+ }
170
+ if (ev.type === 'user' && ev.message) {
171
+ const content = ev.message.content;
172
+ if (Array.isArray(content)) {
173
+ for (const b of content) {
174
+ if (b.type === 'tool_result') {
175
+ const t = textOf(b.content);
176
+ // MCP cold-start race: the agent fired before `serve --mcp` had
177
+ // registered its tools, so it floundered into grep/Read. That
178
+ // measures startup latency, not steady-state value — flag it.
179
+ if (/No such tool available/.test(t)) raced = true;
180
+ // A CLI attempt that came back an error was blocked (by the hook, or
181
+ // by the binary being genuinely absent) and put nothing in context.
182
+ if (cliById.has(b.tool_use_id) && !b.is_error) cliContaminated++;
183
+ const name = nameById.get(b.tool_use_id) || '';
184
+ if (/codegraph_explore/.test(name) && !b.is_error) exploreTexts.push(t);
185
+ timeline.push({ kind: 'add', family: familyOf(name), chars: t.length, tool: name });
186
+ } else {
187
+ timeline.push({ kind: 'add', family: null, chars: textOf([b]).length });
188
+ }
189
+ }
190
+ } else if (typeof content === 'string') {
191
+ timeline.push({ kind: 'add', family: null, chars: content.length });
192
+ }
193
+ }
194
+ if (ev.type === 'result') { result = ev; results.push(ev); }
195
+ }
196
+
197
+ // ---- Pass 1: chars/token, calibrated on tool-result-dominated gaps. ------
198
+ // Splitting a gap in proportion to characters over-attributes to tool results
199
+ // whenever the assistant's own output is under-represented in the transcript
200
+ // (redacted/empty thinking blocks are the common case — a gap whose only
201
+ // visible chars were a 73-char tool_result charged it the whole 830-token
202
+ // delta, 5.5 tok/char). So calibrate the ratio on gaps that are ≥80% tool
203
+ // result by characters, then price every result at that ratio.
204
+ const reqIdx = timeline.map((t, i) => (t.kind === 'req' ? i : -1)).filter((i) => i >= 0);
205
+ const gaps = [];
206
+ for (let k = 1; k < reqIdx.length; k++) {
207
+ const prev = timeline[reqIdx[k - 1]], cur = timeline[reqIdx[k]];
208
+ let chars = 0, toolChars = 0, compacted = false;
209
+ const byFamily = {};
210
+ for (let i = reqIdx[k - 1] + 1; i < reqIdx[k]; i++) {
211
+ const t = timeline[i];
212
+ if (t.kind === 'compact') { compacted = true; continue; }
213
+ if (t.kind !== 'add') continue;
214
+ chars += t.chars;
215
+ if (t.family) { toolChars += t.chars; byFamily[t.family] = (byFamily[t.family] || 0) + t.chars; }
216
+ }
217
+ gaps.push({ delta: cur.ctx - prev.ctx, chars, toolChars, byFamily, compacted });
218
+ }
219
+ const clean = gaps.filter((g) => !g.compacted && g.delta > 0 && g.chars > 500 && g.toolChars / g.chars >= 0.8);
220
+ // A gap where the window also SHED content has a delta far below what was
221
+ // added, which reads as absurdly dense text and would drag the whole run's
222
+ // ratio with it. Shedding can only push a gap's chars/token UP, so take the
223
+ // lower median as the honest centre and drop anything well above it, then
224
+ // pool the survivors. (On runs that never shed, every ratio is within a few
225
+ // percent of the others and this changes nothing.)
226
+ const ratios = clean.map((g) => g.toolChars / g.delta).sort((a, b) => a - b);
227
+ const lowerMedian = ratios.length ? ratios[Math.floor((ratios.length - 1) / 2)] : 0;
228
+ let sumD = 0, sumC = 0;
229
+ for (const g of clean) {
230
+ if (lowerMedian > 0 && g.toolChars / g.delta > lowerMedian * 1.5) continue; // shed
231
+ sumD += g.delta; sumC += g.toolChars;
232
+ }
233
+ if (sumD === 0) { // no clean gap — fall back to every growing gap, all chars
234
+ for (const g of gaps) if (!g.compacted && g.delta > 0 && g.chars > 0) { sumD += g.delta; sumC += g.chars; }
235
+ }
236
+ const charsPerToken = sumD > 0 ? sumC / sumD : CHARS_PER_TOKEN_FALLBACK;
237
+ const calibrated = sumD > 0;
238
+
239
+ // How far a single result's token density strays from the run-level ratio.
240
+ // On a gap that is almost entirely one tool result, `delta` IS that result's
241
+ // token count, so |chars/ratio - delta| / delta is the attribution error for
242
+ // that result. The median over such gaps is the metric's real error bar.
243
+ const errs = [];
244
+ for (const g of gaps) {
245
+ if (g.compacted || g.delta <= 0 || g.chars <= 500) continue;
246
+ if (g.toolChars / g.chars < 0.95) continue;
247
+ errs.push(Math.abs(g.toolChars / charsPerToken - g.delta) / g.delta);
248
+ }
249
+ errs.sort((a, b) => a - b);
250
+ const dispersion = errs.length ? errs[(errs.length - 1) >> 1] : null;
251
+
252
+ // ---- Pass 2: attribute gap tokens, then apply evictions FIFO. ------------
253
+ const contributed = Object.fromEntries(FAMILIES.map((f) => [f, 0]));
254
+ const resultChars = Object.fromEntries(FAMILIES.map((f) => [f, 0]));
255
+ const resultCount = Object.fromEntries(FAMILIES.map((f) => [f, 0]));
256
+ for (const t of timeline) if (t.kind === 'add' && t.family) { resultChars[t.family] += t.chars; resultCount[t.family]++; }
257
+
258
+ let queue = []; // resident contributions, oldest first
259
+ let evicted = 0;
260
+ const evict = (tokens) => {
261
+ let left = tokens;
262
+ while (left > 0 && queue.length) {
263
+ const head = queue[0];
264
+ if (head.tokens <= left) { left -= head.tokens; evicted += head.tokens; queue.shift(); }
265
+ else { head.tokens -= left; evicted += left; left = 0; }
266
+ }
267
+ };
268
+
269
+ for (const g of gaps) {
270
+ if (g.compacted) {
271
+ // Everything before the boundary is gone; the summary replaces it.
272
+ evicted += queue.reduce((s, q) => s + q.tokens, 0);
273
+ queue = [];
274
+ }
275
+ let toolTokens = 0;
276
+ for (const [fam, ch] of Object.entries(g.byFamily)) {
277
+ const tok = ch / charsPerToken;
278
+ toolTokens += tok;
279
+ contributed[fam] += tok;
280
+ queue.push({ family: fam, tokens: tok });
281
+ }
282
+ // The gap grew by `delta`; the tool results account for `toolTokens` of it.
283
+ // A shortfall means the window also shed content — micro-compaction drops
284
+ // the OLDEST tool results first, so evict FIFO. The tolerance keeps
285
+ // attribution noise (a run-level ratio priced against one gap's delta,
286
+ // typically ±2%) from reading as an eviction; real shedding is thousands.
287
+ const shortfall = toolTokens - g.delta;
288
+ if (!g.compacted && shortfall > Math.max(200, toolTokens * 0.05)) evict(shortfall);
289
+ }
290
+
291
+ const residual = Object.fromEntries(FAMILIES.map((f) => [f, 0]));
292
+ for (const q of queue) residual[q.family] += q.tokens;
293
+
294
+ const ctxFinal = reqIdx.length ? timeline[reqIdx[reqIdx.length - 1]].ctx : 0;
295
+ // The FIRST request's prompt is system + tool schemas + the question, before
296
+ // any tool has answered. Differencing the arms' ctxBase prices codegraph's
297
+ // FIXED occupancy — its tool schema and MCP `initialize` instructions — which
298
+ // it pays whether or not the agent ever calls it.
299
+ const ctxBase = reqIdx.length ? timeline[reqIdx[0]].ctx : 0;
300
+ // "Tokens processed" = the SUM of every assistant request's whole prompt. Every
301
+ // request re-counts the prefix, which is what makes this a throughput figure and
302
+ // why it cannot answer the occupancy question (that is `residual`, above).
303
+ //
304
+ // It must be summed PER TURN, not read off `result.usage`. The comment that used
305
+ // to sit here asserted result.usage was "cumulative WITHIN a segment"; that was
306
+ // true when the README figures were measured and is NOT true in current Claude
307
+ // Code, where it reports the LAST turn only. Nothing in this repo changed — the
308
+ // host did, silently, and the harness kept reporting the smaller number.
309
+ //
310
+ // The damage is one-sided and therefore worse than noise: it under-counts
311
+ // whichever arm takes more turns, which is always the WITHOUT arm. Measured on
312
+ // the 2026-08-05 campaign it turned a real 62% token saving into 19%, and
313
+ // manufactured a token REGRESSION on tokio (-41%) and alamofire (-25%) that does
314
+ // not exist. CLAUDE.md already warned about this field; the code did not follow.
315
+ //
316
+ // Dedupe by message.id: Claude Code emits one assistant event per content block,
317
+ // each carrying the same `usage`, so summing per EVENT double-counts (~1.7x on a
318
+ // real run). Same rule the occupancy timeline above uses.
319
+ const seenUsageIds = new Set();
320
+ let processed = 0;
321
+ for (const ev of events) {
322
+ if (ev.type !== 'assistant' || !ev.message?.id) continue;
323
+ if (seenUsageIds.has(ev.message.id)) continue;
324
+ seenUsageIds.add(ev.message.id);
325
+ const u = ev.message.usage || {};
326
+ processed += (u.input_tokens || 0) + (u.cache_read_input_tokens || 0)
327
+ + (u.cache_creation_input_tokens || 0) + (u.output_tokens || 0);
328
+ }
329
+
330
+ return {
331
+ files, toolCalls, counts, initTools, result, results, raced, cliCalls, cliContaminated,
332
+ exploreTexts,
333
+ // What the agent did after each explore — the free sufficiency signal (CG-8).
334
+ sufficiency: classifySufficiency(events),
335
+ // How much of what explore returned the answer actually drew on (CG-9).
336
+ allocation: computeAllocation(exploreTexts, finalAnswerText(events)),
337
+ ok: results.length > 0 && results.every((r) => r.subtype === 'success'),
338
+ turns: reqIdx.length,
339
+ tools: toolCalls.filter((t) => !t.startsWith('ToolSearch')).length,
340
+ reads: counts.Read || 0,
341
+ grep: (counts.Grep || 0) + (counts.Glob || 0),
342
+ cg: Object.entries(counts).filter(([n]) => /codegraph/.test(n)).reduce((s, [, v]) => s + v, 0),
343
+ dur: results.reduce((s, r) => s + (r.duration_ms || 0), 0) / 1000,
344
+ cost: results.reduce((s, r) => s + (r.total_cost_usd || 0), 0),
345
+ processed,
346
+ occupancy: {
347
+ ctxFinal, ctxBase, windowTokens: WINDOW_TOKENS,
348
+ charsPerToken, calibrated, compactions, dispersion, evicted: Math.round(evicted),
349
+ residual: Object.fromEntries(FAMILIES.map((f) => [f, Math.round(residual[f])])),
350
+ contributed: Object.fromEntries(FAMILIES.map((f) => [f, Math.round(contributed[f])])),
351
+ chars: resultChars, results: resultCount,
352
+ residualFileAccess: Math.round(FILE_ACCESS.reduce((s, f) => s + residual[f], 0)),
353
+ contributedFileAccess: Math.round(FILE_ACCESS.reduce((s, f) => s + contributed[f], 0)),
354
+ charsFileAccess: FILE_ACCESS.reduce((s, f) => s + resultChars[f], 0),
355
+ },
356
+ };
357
+ }
358
+
359
+ /** The occupancy block, as printed under a run and reused by the aggregator. */
360
+ export function formatOccupancy(s, indent = ' ') {
361
+ const o = s.occupancy;
362
+ const n = (x) => x.toLocaleString('en-US');
363
+ const pctCtx = (t) => (o.ctxFinal > 0 ? ((t / o.ctxFinal) * 100).toFixed(1) : '0.0');
364
+ const pctWin = (t) => ((t / o.windowTokens) * 100).toFixed(1);
365
+ const rows = [];
366
+ const row = (label, tok, chars, results) => rows.push(
367
+ `${indent} ${label.padEnd(18)}${(n(tok) + ' tok').padStart(12)} ${(pctCtx(tok) + '%').padStart(6)} of ctx ` +
368
+ `${(pctWin(tok) + '%').padStart(6)} of ${Math.round(o.windowTokens / 1000)}k win` +
369
+ (chars !== undefined ? ` (${n(chars)} chars, ${results} result${results === 1 ? '' : 's'})` : '')
370
+ );
371
+ const out = [`${indent}Residual context occupancy at end of run:`];
372
+ out.push(`${indent} ${'final context'.padEnd(18)}${(n(o.ctxFinal) + ' tok').padStart(12)} ${(pctWin(o.ctxFinal) + '%').padStart(6)} of ${Math.round(o.windowTokens / 1000)}k window`);
373
+ row('codegraph', o.residual.codegraph, o.chars.codegraph, o.results.codegraph);
374
+ row('Read', o.residual.read, o.chars.read, o.results.read);
375
+ row('Grep/Glob', o.residual.search, o.chars.search, o.results.search);
376
+ row('Bash', o.residual.bash, o.chars.bash, o.results.bash);
377
+ row('→ file-access', o.residualFileAccess, o.charsFileAccess,
378
+ o.results.read + o.results.search + o.results.bash);
379
+ row('other tools', o.residual.other, o.chars.other, o.results.other);
380
+ const toolTotal = Object.values(o.residual).reduce((a, b) => a + b, 0);
381
+ row('base (prompt+prose)', Math.max(0, o.ctxFinal - toolTotal));
382
+ out.push(`${indent} ${' of which fixed'.padEnd(18)}${(n(o.ctxBase) + ' tok').padStart(12)} system + tool schemas + question, before any tool answered`);
383
+ out.push(...rows);
384
+ const dropped = o.contributed.codegraph + o.contributedFileAccess + o.contributed.other
385
+ - (o.residual.codegraph + o.residualFileAccess + o.residual.other);
386
+ out.push(
387
+ `${indent} measure: ${o.charsPerToken.toFixed(2)} chars/tok ${o.calibrated ? 'measured' : '(FALLBACK — no clean gap to calibrate on)'}` +
388
+ (o.dispersion !== null ? ` ±${(o.dispersion * 100).toFixed(1)}%` : '') +
389
+ ` · turns ${s.turns} · compactions ${o.compactions}` +
390
+ (o.evicted > 0 || dropped > 1 ? ` · evicted ${n(o.evicted)} tok` : '')
391
+ );
392
+ return out.join('\n');
393
+ }
394
+
395
+ /**
396
+ * One codegraph_explore response, split into the per-file source sections the
397
+ * allocator divided its budget across.
398
+ *
399
+ * Parsed out of the RENDERED MARKDOWN, not the CG-4 diagnostic sidecar: the
400
+ * sidecar only exists on a post-CG-4 build, so it cannot measure a baseline arm.
401
+ * The markdown parse is the only instrument that measures both arms of a
402
+ * new-vs-baseline A/B the same way. Both the envelope view and the allocation
403
+ * metric key off this one parse.
404
+ *
405
+ * Returns `{ envelope, files: [{ path, chars, symbols }] }`, where `symbols` are
406
+ * the names the section header lists — filtered to the ones the file DEFINES.
407
+ * The header renders `name(kind)` for every node in the shipped clusters, and
408
+ * that includes edge-kind pseudo-entries for call sites (`mutateElement(calls)`
409
+ * on a file that merely calls it). Attributing a file from those would credit
410
+ * every caller of a cited symbol — measured on a real excalidraw run, it marked
411
+ * `dragElements.ts` used because the answer named `mutateElement`.
412
+ */
413
+ /** NodeKind values that mean "this file defines it" (src/types.ts, less file/import/export). */
414
+ const DEFINING_KINDS = new Set([
415
+ 'module', 'class', 'struct', 'interface', 'trait', 'protocol', 'function', 'method',
416
+ 'property', 'field', 'variable', 'constant', 'enum', 'enum_member', 'type_alias',
417
+ 'namespace', 'route', 'component',
418
+ ]);
419
+ export function parseExploreCall(text) {
420
+ const src = String(text ?? '');
421
+ const re = /^\*\*`([^`]+)`\*\*(.*)$/gm;
422
+ const marks = [];
423
+ let m;
424
+ while ((m = re.exec(src)) !== null) marks.push({ path: m[1], header: m[2] || '', at: m.index });
425
+ if (!marks.length) return { envelope: 0, files: [] };
426
+ // Sections run to the next header, or to the trailing guidance quote.
427
+ const tail = src.indexOf('\n> ', marks[marks.length - 1].at);
428
+ const end = tail === -1 ? src.length : tail;
429
+ const files = marks.map((mark, i) => ({
430
+ path: mark.path,
431
+ chars: (i + 1 < marks.length ? marks[i + 1].at : end) - mark.at,
432
+ // `mutateElement(calls), onFinished(variable), +32 more` → the defined ones.
433
+ symbols: [...mark.header.matchAll(/([A-Za-z_$][\w$.]*)\(([a-z_]+)\)/g)]
434
+ .filter((s) => DEFINING_KINDS.has(s[2]))
435
+ .map((s) => ({ name: s[1], kind: s[2] })),
436
+ }));
437
+ return { envelope: files.reduce((s, f) => s + f.chars, 0), files };
438
+ }
439
+
440
+ /**
441
+ * How the codegraph_explore responses the agent received were DIVIDED across
442
+ * files — the per-file share of the source envelope (#1500 / epic CG-1).
443
+ *
444
+ * `answerGlobs` marks the files that actually answer the question; the summary
445
+ * reports their combined share, which is bar 2 of the CG-1/CG-22 gate. That is
446
+ * HAND-SPECIFIED ground truth; the allocation metric below infers the same
447
+ * intersection from the agent's own answer instead.
448
+ */
449
+ export function formatEnvelope(exploreTexts, answerGlobs = [], indent = ' ') {
450
+ // `tools/cache/**` -> /^tools\/cache\/.*$/ . Same semantics as probe-allocation.
451
+ // The `**` sentinel is written as an escape, never a literal NUL byte — a raw
452
+ // one makes git treat this whole script as binary and costs every future diff.
453
+ const glob2re = (glob) => {
454
+ const S = '\\u0000';
455
+ const body = glob.replace(/[.+^${}()|[\]\\]/g, '\\$&')
456
+ .replace(/\*\*/g, S).replace(/\*/g, '[^/]*').replaceAll(S, '.*');
457
+ return new RegExp(`^${body}$`);
458
+ };
459
+ const answerRes = answerGlobs.map(glob2re);
460
+ const isAnswer = (p) => answerRes.some((re) => re.test(p));
461
+
462
+ // Share is over the sum of the per-file sections, i.e. of the source envelope
463
+ // the allocator divides.
464
+ const pooled = new Map();
465
+ let envelope = 0;
466
+ for (const text of exploreTexts) {
467
+ for (const f of parseExploreCall(text).files) {
468
+ pooled.set(f.path, (pooled.get(f.path) ?? 0) + f.chars);
469
+ envelope += f.chars;
470
+ }
471
+ }
472
+ const ranked = [...pooled.entries()]
473
+ .map(([path, chars]) => ({ path, chars, share: envelope ? chars / envelope : 0, answer: isAnswer(path) }))
474
+ .sort((a, b) => b.chars - a.chars);
475
+ const answerChars = ranked.filter((r) => r.answer).reduce((s, r) => s + r.chars, 0);
476
+ const pct = (f) => `${(f * 100).toFixed(1)}%`;
477
+
478
+ const out = [];
479
+ out.push(`${indent}Explore envelope: ${envelope.toLocaleString('en-US')} chars over ${exploreTexts.length} response(s)`);
480
+ if (answerGlobs.length) {
481
+ out.push(`${indent} answer-set share: ${pct(envelope ? answerChars / envelope : 0)} | top file answers: ${ranked[0]?.answer ?? false}`);
482
+ }
483
+ for (const f of ranked.slice(0, 12)) {
484
+ out.push(`${indent} ${f.answer ? '*' : ' '} ${pct(f.share).padStart(6)} ${String(f.chars).padStart(6)} ${f.path}`);
485
+ }
486
+ if (ranked.length > 12) out.push(`${indent} … ${ranked.length - 12} more files`);
487
+ return out.join('\n');
488
+ }
489
+
490
+ // ---------------------------------------------------------------------------
491
+ // Explore allocation efficiency (CG-9)
492
+ // ---------------------------------------------------------------------------
493
+ // The envelope view above needs a human to say which files answer the question
494
+ // (`--answer <glob>`). This one reads that off the AGENT'S OWN FINAL ANSWER and
495
+ // reports a single number every run:
496
+ //
497
+ // allocation efficiency = bytes explore returned for files the answer drew on
498
+ // ─────────────────────────────────────────────────
499
+ // all bytes explore returned
500
+ //
501
+ // That is the #1500 defect as a number. On the CG-1 baseline self-query it sat
502
+ // around 16–30%: one correct file, one marginal, three pure noise.
503
+ //
504
+ // ATTRIBUTION IS IMPERFECT AND THE ERROR IS ONE-SIDED. An agent can use a file's
505
+ // source to rule it out, or to build a mental model, and never cite it — that
506
+ // reads as waste. So this is a RELATIVE metric: valid for comparing two builds
507
+ // on the SAME question, not as an absolute claim about how much of an envelope
508
+ // earned its place. Quoting it as an absolute efficiency figure is a misuse.
509
+ //
510
+ // Two citation channels, deliberately ranked so the weaker one is separable:
511
+ // * PATH — the answer names the file (`lib/response.js:126-220`, or the bare
512
+ // `utils.js:225` agents drop into prose). Strong, and reported on its own as
513
+ // the conservative floor.
514
+ // * SYMBOL — the answer cites a symbol in a code span and only that file's
515
+ // section header lists it. Catches answers written entirely in symbol names.
516
+ // Guarded: a name carried by ≥3 of the returned files is too generic to
517
+ // attribute and is dropped, or `send`/`get` would mark half the envelope
518
+ // used and bias the metric optimistic — the one direction it must not lean.
519
+
520
+ /** Symbol tokens too common in prose/code spans to attribute a file from. */
521
+ const SYMBOL_STOPWORDS = new Set([
522
+ 'function', 'return', 'const', 'this', 'true', 'false', 'null', 'void', 'undefined',
523
+ 'string', 'object', 'number', 'boolean', 'array', 'class', 'import', 'export',
524
+ 'async', 'await', 'default', 'type', 'value', 'name', 'data', 'self', 'super',
525
+ 'else', 'then', 'case', 'from', 'with', 'when', 'where', 'that', 'they', 'this',
526
+ ]);
527
+ /** A name on this many of the returned files stops identifying any one of them. */
528
+ const SYMBOL_AMBIGUITY_LIMIT = 3;
529
+ /** Below this length a token is far more likely prose than a symbol citation. */
530
+ const MIN_SYMBOL_LEN = 4;
531
+
532
+ /**
533
+ * What the agent's final answer CITES: file paths, and the identifiers it puts
534
+ * in code spans. `extensions` are the file extensions actually present in the
535
+ * envelope — the gate that lets `utils.js:225` through as a file citation while
536
+ * rejecting `res.send` and `mime.contentType`, which are the same token shape.
537
+ */
538
+ export function answerCitations(text, extensions = new Set()) {
539
+ const src = String(text ?? '');
540
+ const paths = new Set();
541
+ // Dotted path with at least one directory: `lib/response.js:126-220`.
542
+ for (const m of src.matchAll(/(?:[\w@.+-]+\/)+[\w@.+-]+\.[A-Za-z]\w*/g)) paths.add(m[0]);
543
+ // Bare basename, only when its extension is one the envelope actually shipped.
544
+ for (const m of src.matchAll(/\b[\w@+-]+\.[A-Za-z]\w*/g)) {
545
+ const ext = m[0].slice(m[0].lastIndexOf('.') + 1).toLowerCase();
546
+ if (extensions.has(ext)) paths.add(m[0]);
547
+ }
548
+ // Identifiers inside code spans. Prose mentions are excluded on purpose: a
549
+ // backtick is the agent marking the token as code, which is the whole signal.
550
+ const symbols = new Set();
551
+ for (const span of src.matchAll(/`([^`\n]+)`/g)) {
552
+ for (const t of span[1].matchAll(/[A-Za-z_$][\w$]*/g)) {
553
+ const tok = t[0];
554
+ if (tok.length >= MIN_SYMBOL_LEN && !SYMBOL_STOPWORDS.has(tok.toLowerCase())) symbols.add(tok);
555
+ }
556
+ }
557
+ return { paths: [...paths], symbols };
558
+ }
559
+
560
+ /**
561
+ * Allocation efficiency over one session's explore responses and its final
562
+ * answer(s). Per call AND pooled over the run — a run-level number alone hides
563
+ * the common shape where call 1 is on target and call 3 is pure noise.
564
+ */
565
+ export function computeAllocation(exploreTexts, answerText) {
566
+ const calls = exploreTexts.map(parseExploreCall).filter((c) => c.files.length);
567
+ const extensions = new Set();
568
+ for (const c of calls) {
569
+ for (const f of c.files) {
570
+ const dot = f.path.lastIndexOf('.');
571
+ if (dot > 0) extensions.add(f.path.slice(dot + 1).toLowerCase());
572
+ }
573
+ }
574
+ const cited = answerCitations(answerText, extensions);
575
+
576
+ // symbol -> the returned files whose header lists it as DEFINED. A `variable`
577
+ // is usually an import binding (`var compileETag = require('./utils')…`), so
578
+ // when the same name is also a real definition somewhere in the envelope, the
579
+ // definition wins and the aliasing file is not credited.
580
+ const symbolFiles = new Map();
581
+ for (const c of calls) {
582
+ for (const f of c.files) {
583
+ for (const s of f.symbols) {
584
+ if (!symbolFiles.has(s.name)) symbolFiles.set(s.name, { strong: new Set(), weak: new Set() });
585
+ const owners = symbolFiles.get(s.name);
586
+ (s.kind === 'variable' || s.kind === 'constant' ? owners.weak : owners.strong).add(f.path);
587
+ }
588
+ }
589
+ }
590
+ const ownersOf = (name) => {
591
+ const o = symbolFiles.get(name);
592
+ return o ? (o.strong.size ? o.strong : o.weak) : null;
593
+ };
594
+
595
+ // `lib/response.js` is cited by `lib/response.js`, by `response.js`, and by an
596
+ // absolute path ending in it — samePath already encodes exactly that.
597
+ const viaPath = (path) => cited.paths.some((p) => samePath(path, p));
598
+ const viaSymbol = (path) => {
599
+ for (const s of cited.symbols) {
600
+ const owners = ownersOf(s);
601
+ if (owners && owners.size < SYMBOL_AMBIGUITY_LIMIT && owners.has(path)) return s;
602
+ }
603
+ return null;
604
+ };
605
+
606
+ const verdict = new Map(); // path -> { via, symbol }
607
+ const judge = (path) => {
608
+ if (!verdict.has(path)) {
609
+ if (viaPath(path)) verdict.set(path, { via: 'path' });
610
+ else {
611
+ const s = viaSymbol(path);
612
+ verdict.set(path, s ? { via: 'symbol', symbol: s } : { via: null });
613
+ }
614
+ }
615
+ return verdict.get(path);
616
+ };
617
+
618
+ const perCall = calls.map((c, i) => {
619
+ const files = c.files.map((f) => ({ ...f, ...judge(f.path) }));
620
+ const used = files.filter((f) => f.via).reduce((s, f) => s + f.chars, 0);
621
+ const usedPath = files.filter((f) => f.via === 'path').reduce((s, f) => s + f.chars, 0);
622
+ return {
623
+ call: i + 1, envelope: c.envelope, used, usedPath, files,
624
+ efficiency: c.envelope ? used / c.envelope : 0,
625
+ };
626
+ });
627
+
628
+ // Pooled: a file returned twice is charged twice, because it occupied the
629
+ // window twice. Same accounting as formatEnvelope.
630
+ const pooled = new Map();
631
+ let envelope = 0;
632
+ for (const c of perCall) {
633
+ for (const f of c.files) {
634
+ const prev = pooled.get(f.path) ?? { path: f.path, chars: 0, calls: 0, via: f.via, symbol: f.symbol };
635
+ prev.chars += f.chars; prev.calls++;
636
+ pooled.set(f.path, prev);
637
+ envelope += f.chars;
638
+ }
639
+ }
640
+ const files = [...pooled.values()].sort((a, b) => b.chars - a.chars);
641
+ const used = files.filter((f) => f.via).reduce((s, f) => s + f.chars, 0);
642
+ const usedPath = files.filter((f) => f.via === 'path').reduce((s, f) => s + f.chars, 0);
643
+ return {
644
+ calls: perCall, files, envelope, used, usedPath,
645
+ efficiency: envelope ? used / envelope : 0,
646
+ efficiencyPath: envelope ? usedPath / envelope : 0,
647
+ filesReturned: files.length,
648
+ filesUsed: files.filter((f) => f.via).length,
649
+ hasAnswer: String(answerText ?? '').trim().length > 0,
650
+ };
651
+ }
652
+
653
+ /**
654
+ * Every answered codegraph_explore response in a transcript, in call order.
655
+ * parseSession collects these as it walks the timeline; this is the same list
656
+ * for callers that only have the raw events (parse-session.mjs).
657
+ */
658
+ export function collectExploreTexts(events) {
659
+ const nameById = new Map();
660
+ const texts = [];
661
+ for (const ev of events) {
662
+ const content = ev?.message?.content;
663
+ if (!Array.isArray(content)) continue;
664
+ for (const b of content) {
665
+ if (b.type === 'tool_use') nameById.set(b.id, b.name);
666
+ else if (b.type === 'tool_result' && !b.is_error
667
+ && /codegraph_explore/.test(nameById.get(b.tool_use_id) || '')) texts.push(textOf(b.content));
668
+ }
669
+ }
670
+ return texts;
671
+ }
672
+
673
+ /**
674
+ * The final answer a session produced. Headless stream-json carries it on the
675
+ * `result` event (one per resumed segment, all of which are answers); an
676
+ * interactive transcript has none, so fall back to the LAST assistant text of
677
+ * the main thread — intermediate narration would flood the citation set.
678
+ */
679
+ export function finalAnswerText(events) {
680
+ const answers = events.filter((e) => e.type === 'result' && typeof e.result === 'string').map((e) => e.result);
681
+ if (answers.length) return answers.join('\n\n');
682
+ let last = '';
683
+ for (const ev of events) {
684
+ if (ev.type !== 'assistant' || (ev.parent_tool_use_id ?? null) !== null) continue;
685
+ const text = (ev.message?.content || []).filter((b) => b.type === 'text').map((b) => b.text || '').join('');
686
+ if (text.trim()) last = text;
687
+ }
688
+ return last;
689
+ }
690
+
691
+ /** The allocation block, as printed under a run and reused by aggregators. */
692
+ export function formatAllocation(s, indent = ' ') {
693
+ const a = s.allocation;
694
+ if (!a || !a.calls.length) return `${indent}Explore allocation: no codegraph_explore responses with source sections`;
695
+ const pct = (f) => `${(f * 100).toFixed(1)}%`;
696
+ const n = (x) => x.toLocaleString('en-US');
697
+ const out = [
698
+ `${indent}Explore allocation — share of returned bytes the answer used ` +
699
+ `(${a.calls.length} call${a.calls.length === 1 ? '' : 's'}, ${a.filesReturned} file${a.filesReturned === 1 ? '' : 's'}):`,
700
+ `${indent} efficiency ${pct(a.efficiency).padStart(6)} ${n(a.used)} of ${n(a.envelope)} chars` +
701
+ ` (${a.filesUsed}/${a.filesReturned} files cited; path-cited alone ${pct(a.efficiencyPath)})`,
702
+ ];
703
+ if (!a.hasAnswer) out.push(`${indent} !! no final answer text found — efficiency is not meaningful for this run`);
704
+ for (const c of a.calls) {
705
+ out.push(`${indent} call ${c.call}: ${pct(c.efficiency).padStart(6)} ${n(c.used)}/${n(c.envelope)} chars` +
706
+ ` ${c.files.filter((f) => f.via).length}/${c.files.length} files`);
707
+ }
708
+ for (const f of a.files.slice(0, 12)) {
709
+ const share = a.envelope ? f.chars / a.envelope : 0;
710
+ const why = f.via === 'symbol' ? `symbol \`${f.symbol}\`` : f.via === 'path' ? 'path' : '—';
711
+ out.push(`${indent} ${f.via ? '*' : ' '} ${pct(share).padStart(6)} ${String(f.chars).padStart(6)} ${f.path} ${why}`);
712
+ }
713
+ if (a.files.length > 12) out.push(`${indent} … ${a.files.length - 12} more files`);
714
+ out.push(`${indent} note: relative metric — attribution is by citation, so compare builds on the same question, not absolutes.`);
715
+ return out.join('\n');
716
+ }
717
+
718
+ // ---------------------------------------------------------------------------
719
+ // Explore sufficiency (CG-8)
720
+ // ---------------------------------------------------------------------------
721
+ // The agent's NEXT action after a codegraph_explore is free ground truth about
722
+ // whether that response was enough. The buckets are chosen so each one maps to
723
+ // a distinct fix:
724
+ //
725
+ // another codegraph call insufficient — the response did not answer
726
+ // Read of a file we RETURNED allocation bug — right file, wrong bytes
727
+ // Read of a file we did NOT recall bug — the file never surfaced
728
+ // Grep/Glob recall bug (weaker: the agent is still hunting)
729
+ // anything else / no tool sufficient — the agent moved on
730
+ //
731
+ // Three rules keep this honest:
732
+ // * Only a call issued in a LATER assistant message counts as a reaction. A
733
+ // Read fired in the same message as the explore was issued before its
734
+ // response existed, so it cannot be a verdict on it (those are counted
735
+ // separately as `concurrent`).
736
+ // * ToolSearch/TodoWrite are stepped over: loading a deferred tool schema or
737
+ // ticking a checklist says nothing about the response.
738
+ // * SUBAGENT CALLS ARE A SEPARATE THREAD. Claude Code interleaves a subagent's
739
+ // tool calls into the same stream, tagged `parent_tool_use_id` — verified on
740
+ // a real run where a delegated search's greps landed between the parent's
741
+ // own calls. Reactions are matched within one thread, or the subagent's
742
+ // first grep would be scored as the parent's verdict on an explore it never
743
+ // saw.
744
+ //
745
+ // A delegation (`Agent`/`Task`) is judged by what the SUBAGENT did first, since
746
+ // that thread is right there in the transcript. Scoring the delegation itself as
747
+ // "moved on" would have called this run sufficient while the subagent was off
748
+ // grepping for the file — the one direction of error a tuning metric must not
749
+ // have. A delegation that never runs a tool stays "moved on".
750
+
751
+ /** Tools that carry no signal about whether the previous response was enough. */
752
+ const TRANSPARENT_TOOLS = new Set(['ToolSearch', 'TodoWrite']);
753
+ /** Tools that hand the work to a subagent whose thread we then judge instead. */
754
+ const DELEGATION_TOOLS = new Set(['Agent', 'Task']);
755
+
756
+ /** Buckets, worst → best. Labels double as the summary rows. */
757
+ export const SUFFICIENCY = [
758
+ ['explore_again', 'explore again', 'insufficient: did not answer'],
759
+ ['read_returned', 'Read a file we returned', 'allocation: right file, wrong bytes'],
760
+ ['read_missed', 'Read a file we did not return', 'recall: file never surfaced'],
761
+ ['search', 'Grep/Glob', 'recall (weak): still hunting for the file'],
762
+ ['sufficient', 'moved on / answered', 'sufficient'],
763
+ ];
764
+ const SUFFICIENCY_KEYS = SUFFICIENCY.map(([k]) => k);
765
+
766
+ // Shell equivalents of Read and of Grep. Both arms have Bash, and on small
767
+ // repos an agent reaches for `sed -n 100,200p file` as readily as for Read —
768
+ // counting only the Read tool would score those explores as sufficient.
769
+ const BASH_READ_RE = /(?:^|[;&|]|\$\(|`)\s*(?:sudo\s+)?(?:cat|bat|head|tail|less|more|nl|sed|awk)\s+([^\n|;&]*)/;
770
+ const BASH_SEARCH_RE = /(?:^|[;&|]|\$\(|`)\s*(?:sudo\s+)?(?:grep|egrep|fgrep|rg|ag|ack|find|fd|ls|tree)\b/;
771
+
772
+ /** What a Bash command is really doing, as far as retrieval is concerned. */
773
+ function bashIntent(cmd) {
774
+ const c = String(cmd || '');
775
+ // A heredoc or a redirect is WRITING a file — `cat <<EOF > x` must not read
776
+ // as a Read.
777
+ if (!/<</.test(c) && !/>\s*\S/.test(c)) {
778
+ const m = BASH_READ_RE.exec(c);
779
+ if (m) {
780
+ // Drop flags and numeric arguments (`sed -n '100,200p' lib/x.js`), then
781
+ // take the last path-shaped token.
782
+ const args = m[1].split(/\s+/).filter((a) => a && !a.startsWith('-') && !/^['"]?\d/.test(a));
783
+ const path = args.reverse().find((a) => /[/.]/.test(a));
784
+ if (path) return { kind: 'read', path: path.replace(/^['"]|['"]$/g, '') };
785
+ }
786
+ }
787
+ if (BASH_SEARCH_RE.test(c)) return { kind: 'search' };
788
+ return null;
789
+ }
790
+
791
+ const normPath = (p) => String(p ?? '').replace(/\\/g, '/').replace(/^\.\//, '');
792
+ /** Same file, with either side repo-relative and the other absolute. */
793
+ function samePath(a, b) {
794
+ const x = normPath(a), y = normPath(b);
795
+ if (!x || !y) return false;
796
+ return x === y || x.endsWith('/' + y) || y.endsWith('/' + x);
797
+ }
798
+
799
+ /**
800
+ * The files whose SOURCE an explore response returned — its per-file sections,
801
+ * which start with the unique ``**` `` marker (FILE_SECTION_PREFIX in
802
+ * src/mcp/tools.ts). formatEnvelope keys off the same marker; it needs the byte
803
+ * offsets too, which is why it re-scans rather than calling this.
804
+ */
805
+ export function exploreReturnedFiles(text) {
806
+ return [...String(text ?? '').matchAll(/^\*\*`([^`]+)`\*\*/gm)].map((m) => m[1]);
807
+ }
808
+
809
+ // Every path-shaped token anywhere in a response — flow steps, blast radius,
810
+ // symbol lists. A file in here but NOT in the returned set was POINTED AT and
811
+ // not delivered, which is a different (and more damning) miss than one the
812
+ // response never mentioned at all.
813
+ const PATH_TOKEN_RE = /(?:[\w@.+-]+\/)+[\w@.+-]+\.[A-Za-z]\w*/g;
814
+
815
+ /**
816
+ * The reaction one action represents, given what the explore had returned.
817
+ * `earlier` is what PREVIOUS explores in the same thread returned: a re-read of
818
+ * a file we already shipped is an allocation miss wherever it was shipped, and
819
+ * filing it as recall would point the fix at the wrong end of the pipeline.
820
+ */
821
+ function reactionOf(action, returned, mentioned, earlier = []) {
822
+ const { name, input } = action;
823
+ const readOf = (path, prefix) => {
824
+ const base = normPath(path).split('/').pop() || String(path ?? '');
825
+ if (returned.some((r) => samePath(path, r))) return { bucket: 'read_returned', next: `${prefix}Read ${base}` };
826
+ if (earlier.some((r) => samePath(path, r))) return { bucket: 'read_returned', next: `${prefix}Read ${base} (returned by an earlier explore)` };
827
+ const named = mentioned.some((m) => samePath(path, m));
828
+ return { bucket: 'read_missed', next: `${prefix}Read ${base}${named ? ' (named, not returned)' : ''}`, named };
829
+ };
830
+ if (/codegraph/.test(name)) return { bucket: 'explore_again', next: name.replace(/^mcp__[^_]*__/, '') };
831
+ if (name === 'Read' || name === 'NotebookRead') return readOf(input.file_path ?? input.notebook_path, '');
832
+ if (name === 'Grep' || name === 'Glob') return { bucket: 'search', next: name };
833
+ if (name === 'Bash') {
834
+ const intent = bashIntent(input.command);
835
+ if (intent?.kind === 'read') return readOf(intent.path, 'Bash ');
836
+ if (intent?.kind === 'search') return { bucket: 'search', next: 'Bash search' };
837
+ }
838
+ return { bucket: 'sufficient', next: name };
839
+ }
840
+
841
+ /** Is this action one of the ways an agent gets file bytes into its head? */
842
+ const isFileAccess = (a) =>
843
+ a.name === 'Read' || a.name === 'NotebookRead' || a.name === 'Grep' || a.name === 'Glob'
844
+ || (a.name === 'Bash' && bashIntent(a.input?.command) !== null);
845
+
846
+ /**
847
+ * Bucket every answered codegraph_explore call in a transcript by what the
848
+ * agent did next. Takes the raw JSONL events so it serves both transcript
849
+ * shapes: stream-json runs (parse-run.mjs) and interactive session logs
850
+ * (parse-session.mjs) — both emit one assistant event per content block with
851
+ * `message.id`, and tool results as `tool_result` blocks in user messages.
852
+ */
853
+ export function classifySufficiency(events) {
854
+ // One action list PER THREAD: 'main', plus one per subagent (keyed by the
855
+ // delegating tool_use id, which is what `parent_tool_use_id` carries).
856
+ const threads = new Map();
857
+ const nameById = new Map();
858
+ const textById = new Map(); // explore tool_use_id -> response text
859
+ for (const ev of events) {
860
+ const content = ev?.message?.content;
861
+ if (!Array.isArray(content)) continue;
862
+ const thread = ev.parent_tool_use_id ?? 'main';
863
+ if (ev.type === 'assistant') {
864
+ if (!threads.has(thread)) threads.set(thread, []);
865
+ const list = threads.get(thread);
866
+ for (const b of content) {
867
+ if (b.type !== 'tool_use') continue;
868
+ nameById.set(b.id, b.name);
869
+ // No message.id (never seen on a real log) degrades to "every call is
870
+ // its own message", i.e. same-message calls read as reactions.
871
+ list.push({ msgId: ev.message.id || `#${thread}-${list.length}`, id: b.id, name: b.name, input: b.input || {} });
872
+ }
873
+ } else if (ev.type === 'user') {
874
+ for (const b of content) {
875
+ if (b.type !== 'tool_result') continue;
876
+ const name = nameById.get(b.tool_use_id) || '';
877
+ if (/codegraph_explore/.test(name) && !b.is_error) textById.set(b.tool_use_id, textOf(b.content));
878
+ }
879
+ }
880
+ }
881
+
882
+ const calls = [];
883
+ let errors = 0, concurrent = 0;
884
+ // What a delegation really did: the subagent's first substantive call. A
885
+ // nested delegation is skipped rather than followed, so a subagent that only
886
+ // spawns another subagent leaves the call as "moved on".
887
+ const throughDelegation = (action, returned, mentioned, earlier) => {
888
+ const first = (threads.get(action.id) || []).find((x) => !TRANSPARENT_TOOLS.has(x.name) && !DELEGATION_TOOLS.has(x.name));
889
+ if (!first) return { bucket: 'sufficient', next: action.name };
890
+ const r = reactionOf(first, returned, mentioned, earlier);
891
+ return { ...r, next: `${action.name} → ${r.next}` };
892
+ };
893
+ for (const [thread, actions] of threads) {
894
+ const earlier = []; // files previous explores in THIS thread already shipped
895
+ for (let i = 0; i < actions.length; i++) {
896
+ const a = actions[i];
897
+ if (!/codegraph_explore/.test(a.name)) continue;
898
+ const text = textById.get(a.id);
899
+ // No response text = the call errored, or the run ended before it
900
+ // returned. Nothing to judge the sufficiency of; count it and move on.
901
+ if (text === undefined) { errors++; continue; }
902
+ const returned = exploreReturnedFiles(text);
903
+ const mentioned = text.match(PATH_TOKEN_RE) || [];
904
+ let reaction = { bucket: 'sufficient', next: '(final answer)' };
905
+ for (let j = i + 1; j < actions.length; j++) {
906
+ const b = actions[j];
907
+ if (b.msgId === a.msgId) { if (isFileAccess(b)) concurrent++; continue; }
908
+ if (TRANSPARENT_TOOLS.has(b.name)) continue;
909
+ reaction = DELEGATION_TOOLS.has(b.name)
910
+ ? throughDelegation(b, returned, mentioned, earlier)
911
+ : reactionOf(b, returned, mentioned, earlier);
912
+ break;
913
+ }
914
+ earlier.push(...returned);
915
+ calls.push({ thread, query: String(a.input.query ?? ''), files: returned.length, chars: text.length, ...reaction });
916
+ }
917
+ }
918
+
919
+ const counts = Object.fromEntries(SUFFICIENCY_KEYS.map((k) => [k, 0]));
920
+ for (const c of calls) counts[c.bucket]++;
921
+ return { calls, counts, errors, concurrent, answered: calls.length };
922
+ }
923
+
924
+ /** The sufficiency block, as printed under a run and reused by aggregators. */
925
+ export function formatSufficiency(s, indent = ' ') {
926
+ const f = s.sufficiency;
927
+ if (!f.answered) {
928
+ return `${indent}Explore sufficiency: no answered codegraph_explore calls`
929
+ + (f.errors ? ` (${f.errors} errored or never returned)` : '');
930
+ }
931
+ const pct = (n) => ((n / f.answered) * 100).toFixed(0) + '%';
932
+ const out = [`${indent}Explore sufficiency — what the agent did NEXT (${f.answered} answered call${f.answered === 1 ? '' : 's'}):`];
933
+ for (const [key, label, meaning] of SUFFICIENCY) {
934
+ out.push(`${indent} ${String(f.counts[key]).padStart(3)} ${pct(f.counts[key]).padStart(4)} ${label.padEnd(31)}${meaning}`);
935
+ }
936
+ f.calls.forEach((c, i) => {
937
+ const q = c.query.length > 46 ? c.query.slice(0, 45) + '…' : c.query;
938
+ const where = c.thread && c.thread !== 'main' ? ' [subagent]' : '';
939
+ out.push(`${indent} ${i + 1}.${where} "${q}" [${c.files} file${c.files === 1 ? '' : 's'}] → ${c.next}`);
940
+ });
941
+ const notes = [];
942
+ if (f.errors) notes.push(`${f.errors} errored/unanswered call${f.errors === 1 ? '' : 's'} (not bucketed)`);
943
+ if (f.concurrent) notes.push(`${f.concurrent} file-access call${f.concurrent === 1 ? '' : 's'} in the SAME message as an explore (not a reaction)`);
944
+ if (notes.length) out.push(`${indent} note: ${notes.join(' · ')}`);
945
+ return out.join('\n');
946
+ }
947
+
948
+ // ---------------------------------------------------------------------------
949
+ // `--selftest`: the occupancy math over synthetic transcripts with known
950
+ // answers. It lives here rather than in a test file on purpose — a new
951
+ // scripts/agent-eval/*.mjs scores into the self-query eval fixture's corpus.
952
+ function selftest() {
953
+ const { writeFileSync, mkdtempSync } = require0('fs');
954
+ const { join } = require0('path');
955
+ const { tmpdir } = require0('os');
956
+ const dir = mkdtempSync(join(tmpdir(), 'cg-occ-'));
957
+ let n = 0, failures = 0;
958
+ const check = (name, got, want, tol) => {
959
+ n++;
960
+ const ok = Math.abs(got - want) <= tol;
961
+ if (!ok) failures++;
962
+ console.log(`${ok ? ' ok ' : ' FAIL'} ${name}: got ${Math.round(got)}, want ${want} ±${tol}`);
963
+ };
964
+ // Builders for the event shapes Claude Code actually emits.
965
+ const req = (ctx, id, blocks) => blocks.map((b) => JSON.stringify({
966
+ type: 'assistant',
967
+ message: { id, content: [b], usage: { input_tokens: ctx, cache_read_input_tokens: 0, cache_creation_input_tokens: 0, output_tokens: 2 } },
968
+ }));
969
+ const use = (id, name, input = {}) => ({ type: 'tool_use', id, name, input });
970
+ const res = (id, chars) => JSON.stringify({
971
+ type: 'user', message: { content: [{ type: 'tool_result', tool_use_id: id, content: [{ type: 'text', text: 'x'.repeat(chars) }] }] },
972
+ });
973
+ const done = () => JSON.stringify({ type: 'result', subtype: 'success', duration_ms: 1000, total_cost_usd: 0.1, usage: {} });
974
+ const write = (name, lines) => { const f = join(dir, name); writeFileSync(f, lines.join('\n') + '\n'); return f; };
975
+
976
+ // 1. Attribution: ratio 2.5 chars/tok, two families, no shedding.
977
+ // 10,000 explore chars over a 4,000-tok gap; 5,000 Read chars over 2,000.
978
+ let f = write('basic.jsonl', [
979
+ ...req(10000, 'm1', [use('t1', 'mcp__codegraph__codegraph_explore')]),
980
+ res('t1', 10000),
981
+ ...req(14000, 'm2', [use('t2', 'Read')]),
982
+ res('t2', 5000),
983
+ ...req(16000, 'm3', [{ type: 'text', text: 'done' }]),
984
+ done(),
985
+ ]);
986
+ let o = parseSession([f]).occupancy;
987
+ check('chars/token', o.charsPerToken * 1000, 2500, 30);
988
+ check('codegraph residual', o.residual.codegraph, 4000, 60);
989
+ check('Read residual', o.residual.read, 2000, 40);
990
+ check('file-access residual', o.residualFileAccess, 2000, 40);
991
+ check('final context', o.ctxFinal, 16000, 0);
992
+ check('fixed base', o.ctxBase, 10000, 0);
993
+ check('nothing evicted', o.evicted, 0, 1);
994
+
995
+ // 2. Dedupe: thinking + tool_use are two events sharing one id and one usage.
996
+ // Counting usage per event would report 5 requests instead of 3.
997
+ f = write('dupe.jsonl', [
998
+ ...req(10000, 'm1', [{ type: 'thinking', thinking: '' }, use('t1', 'mcp__codegraph__codegraph_explore')]),
999
+ res('t1', 10000),
1000
+ ...req(14000, 'm2', [{ type: 'thinking', thinking: '' }, use('t2', 'Read')]),
1001
+ res('t2', 5000),
1002
+ ...req(16000, 'm3', [{ type: 'text', text: 'done' }]),
1003
+ done(),
1004
+ ]);
1005
+ let s = parseSession([f]);
1006
+ check('turns deduped by message.id', s.turns, 3, 0);
1007
+ check('codegraph residual (deduped)', s.occupancy.residual.codegraph, 4000, 60);
1008
+
1009
+ // 3. Compaction: the boundary clears everything resident before it.
1010
+ f = write('compact.jsonl', [
1011
+ ...req(10000, 'm1', [use('t1', 'mcp__codegraph__codegraph_explore')]),
1012
+ res('t1', 10000),
1013
+ ...req(14000, 'm2', [use('t2', 'mcp__codegraph__codegraph_explore')]),
1014
+ JSON.stringify({ type: 'system', subtype: 'compact_boundary' }),
1015
+ res('t2', 5000),
1016
+ ...req(8000, 'm3', [{ type: 'text', text: 'done' }]),
1017
+ done(),
1018
+ ]);
1019
+ o = parseSession([f]).occupancy;
1020
+ check('post-compaction residual = last result only', o.residual.codegraph, 2000, 40);
1021
+ check('contributed still counts both', o.contributed.codegraph, 6000, 80);
1022
+
1023
+ // 4. Micro-compaction: context grows less than the results added, so the
1024
+ // oldest result is shed first (FIFO) — here explore, leaving Read.
1025
+ f = write('micro.jsonl', [
1026
+ ...req(10000, 'm1', [use('t1', 'mcp__codegraph__codegraph_explore')]),
1027
+ res('t1', 10000),
1028
+ ...req(14000, 'm2', [use('t2', 'Read')]),
1029
+ res('t2', 10000),
1030
+ ...req(14500, 'm3', [{ type: 'text', text: 'done' }]), // +500 for 4,000 tok of Read
1031
+ done(),
1032
+ ]);
1033
+ o = parseSession([f]).occupancy;
1034
+ check('FIFO evicted the older codegraph result', o.residual.codegraph, 500, 60);
1035
+ check('newer Read result survives', o.residual.read, 4000, 60);
1036
+ check('eviction recorded', o.evicted, 3500, 60);
1037
+
1038
+ // 5. Multi-turn stitching: a resumed segment continues the same context, and
1039
+ // a turn that calls no tool leaves the earlier residual in place.
1040
+ const a = write('seg1.jsonl', [
1041
+ ...req(10000, 'm1', [use('t1', 'mcp__codegraph__codegraph_explore')]),
1042
+ res('t1', 10000),
1043
+ ...req(14000, 'm2', [{ type: 'text', text: 'answer one' }]),
1044
+ done(),
1045
+ ]);
1046
+ const b = write('seg2.jsonl', [
1047
+ ...req(14600, 'm3', [{ type: 'text', text: 'answer two, from what is already here' }]),
1048
+ done(),
1049
+ ]);
1050
+ s = parseSession([a, b]);
1051
+ check('stitched turns', s.turns, 3, 0);
1052
+ check('residual carries into turn 2', s.occupancy.residual.codegraph, 4000, 60);
1053
+ check('stitched final context', s.occupancy.ctxFinal, 14600, 0);
1054
+ check('stitched cost sums segments', s.cost * 100, 20, 0.1);
1055
+
1056
+ // ---- 6. Explore sufficiency: the bucket each explore call earns. --------
1057
+ const checkIs = (name, got, want) => {
1058
+ n++;
1059
+ const ok = got === want;
1060
+ if (!ok) failures++;
1061
+ console.log(`${ok ? ' ok ' : ' FAIL'} ${name}: got ${JSON.stringify(got)}, want ${JSON.stringify(want)}`);
1062
+ };
1063
+ const EXPLORE = 'mcp__codegraph__codegraph_explore';
1064
+ // An explore response's shape that matters here: one `**`path`**` section per
1065
+ // file whose source it returned, plus whatever else it named.
1066
+ const exploreRes = (id, paths, extra = '', isError = false) => JSON.stringify({
1067
+ type: 'user',
1068
+ message: {
1069
+ content: [{
1070
+ type: 'tool_result', tool_use_id: id, ...(isError ? { is_error: true } : {}),
1071
+ content: [{ type: 'text', text: paths.map((p) => `**\`${p}\`** — fn(function)\n\n1\tcode here\n`).join('\n') + extra }],
1072
+ }],
1073
+ },
1074
+ });
1075
+ const suff = (lines) => classifySufficiency(lines.map((l) => JSON.parse(l)));
1076
+
1077
+ // explore → explore is insufficient; the second explore → a Read of a file it
1078
+ // RETURNED is the allocation bucket (this is the CG-22 express baseline).
1079
+ let sf = suff([
1080
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'res.send Content-Type ETag generation' })]),
1081
+ exploreRes('e1', ['lib/response.js', 'lib/utils.js']),
1082
+ ...req(12000, 'm2', [use('e2', EXPLORE, { query: 'response.js res.send function body' })]),
1083
+ exploreRes('e2', ['lib/response.js']),
1084
+ ...req(14000, 'm3', [use('r1', 'Read', { file_path: '/private/tmp/t-base/lib/response.js' })]),
1085
+ res('r1', 3722),
1086
+ ...req(15000, 'm4', [{ type: 'text', text: 'done' }]),
1087
+ done(),
1088
+ ]);
1089
+ check('two answered explore calls', sf.answered, 2, 0);
1090
+ checkIs('explore → explore = insufficient', sf.calls[0].bucket, 'explore_again');
1091
+ checkIs('explore → Read of a returned file (abs path)', sf.calls[1].bucket, 'read_returned');
1092
+
1093
+ // A file the response NAMED but did not return is still a recall miss — and
1094
+ // is flagged as named, since pointing without delivering is its own failure.
1095
+ sf = suff([
1096
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1097
+ exploreRes('e1', ['lib/response.js'], '\n**Flow**\n1. lib/router/index.js:42 handle\n'),
1098
+ ...req(12000, 'm2', [use('r1', 'Read', { file_path: '/t/lib/router/index.js' })]),
1099
+ res('r1', 100),
1100
+ done(),
1101
+ ]);
1102
+ checkIs('explore → Read of a named-but-unreturned file', sf.calls[0].bucket, 'read_missed');
1103
+ checkIs(' …flagged as named', sf.calls[0].named, true);
1104
+
1105
+ sf = suff([
1106
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1107
+ exploreRes('e1', ['lib/response.js']),
1108
+ ...req(12000, 'm2', [use('r1', 'Read', { file_path: '/t/lib/never/mentioned.js' })]),
1109
+ res('r1', 100),
1110
+ done(),
1111
+ ]);
1112
+ checkIs('explore → Read of a file never surfaced', sf.calls[0].bucket, 'read_missed');
1113
+ checkIs(' …not flagged as named', sf.calls[0].named, false);
1114
+
1115
+ // Grep, and the shell equivalents of Read and Grep.
1116
+ const oneShot = (next) => suff([
1117
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1118
+ exploreRes('e1', ['lib/response.js']),
1119
+ ...req(12000, 'm2', [next]),
1120
+ res(next.id, 100),
1121
+ done(),
1122
+ ]).calls[0];
1123
+ checkIs('explore → Grep', oneShot(use('g1', 'Grep', { pattern: 'send' })).bucket, 'search');
1124
+ checkIs('explore → Glob', oneShot(use('g1', 'Glob', { pattern: '**/*.js' })).bucket, 'search');
1125
+ checkIs('explore → Bash sed of a returned file',
1126
+ oneShot(use('b1', 'Bash', { command: "sed -n '100,200p' lib/response.js" })).bucket, 'read_returned');
1127
+ checkIs('explore → Bash grep', oneShot(use('b1', 'Bash', { command: 'grep -rn send lib/' })).bucket, 'search');
1128
+ checkIs('explore → Bash that writes a file is not a read',
1129
+ oneShot(use('b1', 'Bash', { command: "cat > /tmp/note.md <<'EOF'\nx\nEOF" })).bucket, 'sufficient');
1130
+ checkIs('explore → Bash npm test = moved on',
1131
+ oneShot(use('b1', 'Bash', { command: 'npm test' })).bucket, 'sufficient');
1132
+ checkIs('explore → Edit = sufficient',
1133
+ oneShot(use('x1', 'Edit', { file_path: '/t/lib/response.js' })).bucket, 'sufficient');
1134
+
1135
+ // No further tool call at all: the agent answered from the response.
1136
+ sf = suff([
1137
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1138
+ exploreRes('e1', ['lib/response.js']),
1139
+ ...req(12000, 'm2', [{ type: 'text', text: 'here is how it works' }]),
1140
+ done(),
1141
+ ]);
1142
+ checkIs('explore → final answer', sf.calls[0].bucket, 'sufficient');
1143
+ checkIs(' …labelled as the final answer', sf.calls[0].next, '(final answer)');
1144
+
1145
+ // A Read issued in the SAME message as the explore predates its response, so
1146
+ // it is not a verdict on it — step past it and count it separately.
1147
+ sf = suff([
1148
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' }), use('r1', 'Read', { file_path: '/t/lib/response.js' })]),
1149
+ exploreRes('e1', ['lib/response.js']),
1150
+ res('r1', 100),
1151
+ ...req(12000, 'm2', [use('x1', 'Edit', { file_path: '/t/lib/response.js' })]),
1152
+ res('x1', 20),
1153
+ done(),
1154
+ ]);
1155
+ checkIs('same-message Read is not a reaction', sf.calls[0].bucket, 'sufficient');
1156
+ check(' …counted as concurrent instead', sf.concurrent, 1, 0);
1157
+
1158
+ // ToolSearch/TodoWrite carry no signal — the Read behind them is the verdict.
1159
+ sf = suff([
1160
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1161
+ exploreRes('e1', ['lib/response.js']),
1162
+ ...req(12000, 'm2', [use('t1', 'TodoWrite', {})]),
1163
+ res('t1', 20),
1164
+ ...req(13000, 'm3', [use('r1', 'Read', { file_path: '/t/lib/response.js' })]),
1165
+ res('r1', 100),
1166
+ done(),
1167
+ ]);
1168
+ checkIs('bookkeeping tools are stepped over', sf.calls[0].bucket, 'read_returned');
1169
+
1170
+ // A re-read of a file an EARLIER explore shipped is still an allocation miss:
1171
+ // we returned it and clipped it wrong, just not on this call.
1172
+ sf = suff([
1173
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'first' })]),
1174
+ exploreRes('e1', ['lib/response.js', 'lib/utils.js']),
1175
+ ...req(11000, 'm2', [use('e2', EXPLORE, { query: 'second' })]),
1176
+ exploreRes('e2', ['lib/response.js']),
1177
+ ...req(12000, 'm3', [use('r1', 'Read', { file_path: '/t/lib/utils.js' })]),
1178
+ res('r1', 400),
1179
+ done(),
1180
+ ]);
1181
+ checkIs('re-read of an earlier explore’s file is allocation, not recall',
1182
+ sf.calls[1].bucket, 'read_returned');
1183
+ checkIs(' …and says which explore returned it',
1184
+ sf.calls[1].next, 'Read utils.js (returned by an earlier explore)');
1185
+
1186
+ // A subagent's calls are interleaved into the same stream under
1187
+ // `parent_tool_use_id` (verified on a real excalidraw run). They belong to
1188
+ // their own thread: the parent's verdict is the delegation, judged by what
1189
+ // the subagent actually did first — here, grepping for a file we never
1190
+ // returned.
1191
+ const sub = (parent, obj) => JSON.stringify({ ...JSON.parse(obj), parent_tool_use_id: parent });
1192
+ sf = suff([
1193
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1194
+ exploreRes('e1', ['lib/response.js']),
1195
+ ...req(12000, 'm2', [use('a1', 'Agent', { subagent_type: 'Explore' })]),
1196
+ ...req(0, 'sm1', [use('sb1', 'Bash', { command: 'grep -rn nonce lib/' })]).map((l) => sub('a1', l)),
1197
+ sub('a1', res('sb1', 400)),
1198
+ ...req(14000, 'm3', [{ type: 'text', text: 'done' }]),
1199
+ res('a1', 900),
1200
+ done(),
1201
+ ]);
1202
+ checkIs('delegation is judged by what the subagent did', sf.calls[0].bucket, 'search');
1203
+ checkIs(' …and says so', sf.calls[0].next, 'Agent → Bash search');
1204
+
1205
+ // A subagent's Read must NOT be read as the parent's reaction to an explore
1206
+ // the subagent never saw: the parent moved on, the subagent's own explore is
1207
+ // judged inside its own thread.
1208
+ sf = suff([
1209
+ ...req(10000, 'm1', [use('a1', 'Agent', { subagent_type: 'Explore' })]),
1210
+ ...req(0, 'sm1', [use('e1', EXPLORE, { query: 'sub q' })]).map((l) => sub('a1', l)),
1211
+ sub('a1', exploreRes('e1', ['lib/response.js'])),
1212
+ ...req(11000, 'm2', [use('e2', EXPLORE, { query: 'parent q' })]),
1213
+ exploreRes('e2', ['lib/other.js']),
1214
+ ...req(0, 'sm2', [use('sr1', 'Read', { file_path: '/t/lib/response.js' })]).map((l) => sub('a1', l)),
1215
+ sub('a1', res('sr1', 400)),
1216
+ ...req(13000, 'm3', [{ type: 'text', text: 'done' }]),
1217
+ done(),
1218
+ ]);
1219
+ check('both threads bucketed', sf.answered, 2, 0);
1220
+ checkIs('parent explore is not blamed for a subagent Read',
1221
+ sf.calls.find((c) => c.query === 'parent q').bucket, 'sufficient');
1222
+ checkIs('subagent explore is judged in its own thread',
1223
+ sf.calls.find((c) => c.query === 'sub q').bucket, 'read_returned');
1224
+
1225
+ // An errored explore has no response to judge; it is counted, not bucketed.
1226
+ sf = suff([
1227
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1228
+ exploreRes('e1', [], 'not indexed', true),
1229
+ ...req(12000, 'm2', [use('r1', 'Read', { file_path: '/t/lib/response.js' })]),
1230
+ res('r1', 100),
1231
+ done(),
1232
+ ]);
1233
+ check('errored explore is not bucketed', sf.answered, 0, 0);
1234
+ check(' …but is counted', sf.errors, 1, 0);
1235
+
1236
+ // ---- 7. Allocation efficiency: which returned bytes the answer used. ----
1237
+ // The response shape that matters here: a per-file section whose header lists
1238
+ // `name(kind)`, and a body big enough that the shares are readable.
1239
+ const alloc = (files, answer) => computeAllocation(
1240
+ [files.map(([p, syms, size]) =>
1241
+ `**\`${p}\`** — ${syms}\n\n\`\`\`js\n${'x'.repeat(size)}\n\`\`\`\n`).join('\n')
1242
+ + '\n> Treat the code above as already read.\n'],
1243
+ answer,
1244
+ );
1245
+ const round = (f) => Math.round(f * 1000) / 10;
1246
+
1247
+ // The #1500 shape: one file answers, two are noise. The answer names it by
1248
+ // repo-relative path.
1249
+ let al = alloc(
1250
+ [['lib/response.js', 'send(function)', 4000], ['lib/express.js', 'app(variable)', 3000],
1251
+ ['lib/view.js', 'View(class)', 3000]],
1252
+ 'The Content-Type is decided in `lib/response.js:126` by `res.send`.',
1253
+ );
1254
+ check('allocation: one of three files cited', round(al.efficiency), 40.1, 0.6);
1255
+ checkIs(' …cited file is flagged path', al.files.find((f) => /response/.test(f.path)).via, 'path');
1256
+ checkIs(' …noise file is not', al.files.find((f) => /view/.test(f.path)).via, null);
1257
+ check(' …files cited', al.filesUsed, 1, 0);
1258
+
1259
+ // A bare basename is how agents actually cite in prose — but only when the
1260
+ // extension is one the envelope shipped, or `res.send` reads as a file too.
1261
+ al = alloc([['lib/response.js', 'send(function)', 4000], ['lib/view.js', 'View(class)', 4000]],
1262
+ 'It happens in response.js:134, inside `res.send`.');
1263
+ check('bare basename counts as a path citation', round(al.efficiency), 50, 0.6);
1264
+ al = alloc([['lib/response.js', 'send(function)', 4000], ['lib/view.js', 'View(class)', 4000]],
1265
+ 'The `res.send` and `mime.contentType` calls do it.');
1266
+ checkIs('a dotted expression is not a file citation', al.files.every((f) => f.via !== 'path'), true);
1267
+
1268
+ // Symbol citations: a code span naming a symbol the file DEFINES counts; a
1269
+ // file that merely CALLS it does not (the excalidraw dragElements.ts case).
1270
+ al = alloc([['src/mutateElement.ts', 'mutateElement(function)', 4000],
1271
+ ['src/dragElements.ts', 'mutateElement(calls), updateCoords(function)', 4000]],
1272
+ 'Mutation goes through `mutateElement`.');
1273
+ checkIs('symbol citation credits the definer', al.files.find((f) => /mutateElement.ts/.test(f.path)).via, 'symbol');
1274
+ checkIs(' …and not a caller of it', al.files.find((f) => /dragElements/.test(f.path)).via, null);
1275
+
1276
+ // An import binding (`variable`) loses to the real definition of the name.
1277
+ al = alloc([['lib/utils.js', 'compileETag(function)', 4000],
1278
+ ['lib/application.js', 'compileETag(variable), set(calls)', 4000]],
1279
+ 'The generator comes from `compileETag`.');
1280
+ checkIs('a definition beats an import alias of the same name',
1281
+ al.files.find((f) => /utils/.test(f.path)).via, 'symbol');
1282
+ checkIs(' …and the aliasing file is not credited',
1283
+ al.files.find((f) => /application/.test(f.path)).via, null);
1284
+
1285
+ // A name carried by ≥3 returned files identifies none of them: crediting them
1286
+ // all would bias the metric optimistic, the one direction it must not lean.
1287
+ al = alloc([['a/one.js', 'handle(function)', 3000], ['a/two.js', 'handle(function)', 3000],
1288
+ ['a/three.js', 'handle(function)', 3000]],
1289
+ 'It all runs through `handle`.');
1290
+ check('a symbol on 3 files attributes none of them', al.efficiency, 0, 0.001);
1291
+
1292
+ // Prose is not a citation — only a code span is.
1293
+ al = alloc([['lib/response.js', 'sendResponse(function)', 4000], ['lib/view.js', 'View(class)', 4000]],
1294
+ 'The sendResponse path handles it.');
1295
+ check('an unquoted symbol in prose does not count', al.efficiency, 0, 0.001);
1296
+
1297
+ // Per-call: the run number pools, but call 2 being pure noise must still show.
1298
+ const twoCalls = computeAllocation([
1299
+ '**`lib/response.js`** — send(function)\n\n```js\n' + 'x'.repeat(4000) + '\n```\n\n> guidance\n',
1300
+ '**`lib/view.js`** — View(class)\n\n```js\n' + 'x'.repeat(4000) + '\n```\n\n> guidance\n',
1301
+ ], 'See `lib/response.js`.');
1302
+ check('per-call: call 1 fully used', round(twoCalls.calls[0].efficiency), 100, 0.1);
1303
+ check('per-call: call 2 fully wasted', round(twoCalls.calls[1].efficiency), 0, 0.1);
1304
+ check('run pools both calls', round(twoCalls.efficiency), 50, 0.6);
1305
+
1306
+ // A file returned twice is charged twice — it occupied the window twice.
1307
+ const twice = computeAllocation([
1308
+ '**`lib/response.js`** — send(function)\n\n' + 'x'.repeat(4000) + '\n\n> g\n',
1309
+ '**`lib/response.js`** — send(function)\n\n' + 'x'.repeat(4000) + '\n\n> g\n',
1310
+ ], 'See `lib/response.js`.');
1311
+ check('a re-returned file is charged both times', twice.envelope, 8078, 120);
1312
+ check(' …and credited both times', round(twice.efficiency), 100, 0.1);
1313
+
1314
+ // The answer text itself: the `result` event, and the interactive fallback.
1315
+ const ev = (l) => JSON.parse(l);
1316
+ checkIs('final answer comes from the result event',
1317
+ finalAnswerText([ev(done()), { type: 'result', result: 'the answer' }]), 'the answer');
1318
+ checkIs('…else the last main-thread assistant text', finalAnswerText([
1319
+ { type: 'assistant', message: { id: 'm1', content: [{ type: 'text', text: 'let me look' }] } },
1320
+ { type: 'assistant', parent_tool_use_id: 'a1', message: { id: 's1', content: [{ type: 'text', text: 'subagent report' }] } },
1321
+ { type: 'assistant', message: { id: 'm2', content: [{ type: 'text', text: 'the answer' }] } },
1322
+ ]), 'the answer');
1323
+
1324
+ // End to end through parseSession, on the transcript shape a run really has.
1325
+ f = write('alloc.jsonl', [
1326
+ ...req(10000, 'm1', [use('e1', EXPLORE, { query: 'q' })]),
1327
+ JSON.stringify({
1328
+ type: 'user',
1329
+ message: {
1330
+ content: [{
1331
+ type: 'tool_result', tool_use_id: 'e1',
1332
+ content: [{
1333
+ type: 'text',
1334
+ text: '**`lib/response.js`** — send(function)\n\n' + 'x'.repeat(4000)
1335
+ + '\n\n**`lib/view.js`** — View(class)\n\n' + 'x'.repeat(4000) + '\n\n> guidance\n',
1336
+ }],
1337
+ }],
1338
+ },
1339
+ }),
1340
+ ...req(20000, 'm2', [{ type: 'text', text: 'done' }]),
1341
+ JSON.stringify({
1342
+ type: 'result', subtype: 'success', duration_ms: 1000, total_cost_usd: 0.1, usage: {},
1343
+ result: 'It is decided in `lib/response.js` by `res.send`.',
1344
+ }),
1345
+ ]);
1346
+ s = parseSession([f]);
1347
+ check('parseSession reports allocation', round(s.allocation.efficiency), 50, 0.6);
1348
+ checkIs(' …and the block renders', /efficiency\s+50\.\d%/.test(formatAllocation(s)), true);
1349
+ checkIs('a run with no explore says so',
1350
+ formatAllocation({ allocation: computeAllocation([], 'answer') }).includes('no codegraph_explore responses'), true);
1351
+
1352
+ console.log(`\n${n - failures}/${n} checks passed`);
1353
+ return failures;
1354
+ }
1355
+ // `--selftest` needs sync fs helpers the module path doesn't import at top level.
1356
+ function require0(m) { return process.getBuiltinModule(m); }
1357
+
1358
+ const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
1359
+ if (isMain && process.argv.includes('--selftest')) process.exit(selftest() ? 1 : 0);
1360
+ if (isMain) {
1361
+ // `--answer <glob>` is repeatable and implies `--envelope`. Its VALUE is not a
1362
+ // run file, so consume it here rather than letting the positional filter below
1363
+ // mistake a glob for a log path.
1364
+ const argv = process.argv.slice(2);
1365
+ const files = [];
1366
+ const answerGlobs = [];
1367
+ let wantEnvelope = false;
1368
+ let brief = false;
1369
+ for (let i = 0; i < argv.length; i++) {
1370
+ if (argv[i] === '--envelope') wantEnvelope = true;
1371
+ else if (argv[i] === '--brief') brief = true;
1372
+ else if (argv[i] === '--answer') { answerGlobs.push(argv[++i]); wantEnvelope = true; }
1373
+ else if (!argv[i].startsWith('--')) files.push(argv[i]);
1374
+ }
1375
+ if (!files.length) { console.error('usage: parse-run.mjs <run.jsonl> [run.t2.jsonl ...] [--brief] [--envelope] [--answer <glob>]... | --selftest'); process.exit(1); }
1376
+ const s = parseSession(files);
1377
+
1378
+ console.log(`\n=== ${files.map((f) => f.split('/').pop()).join(' + ')} ===`);
1379
+ console.log(`codegraph tools exposed: ${s.initTools ? s.initTools.length : '?'}${s.raced ? ' [MCP COLD-START RACE — tool call hit "No such tool available"]' : ''}`);
1380
+ if (s.cliContaminated) console.log(`!! ${s.cliContaminated} codegraph CLI call${s.cliContaminated === 1 ? '' : 's'} RETURNED OUTPUT via Bash — if this is a without-arm, the run is CONTAMINATED`);
1381
+ else if (s.cliCalls) console.log(` (${s.cliCalls} codegraph CLI attempt${s.cliCalls === 1 ? '' : 's'} blocked — no output entered the window)`);
1382
+ console.log(`\nTool calls (${s.toolCalls.length}):`);
1383
+ console.log(' by type:', JSON.stringify(s.counts));
1384
+ if (!brief) s.toolCalls.forEach((tc, i) => console.log(` ${i + 1}. ${tc}`));
1385
+
1386
+ if (s.result) {
1387
+ const seg = s.results.length > 1 ? ` | ${s.results.length} segments (${s.results.map((r) => r.subtype).join(',')})` : '';
1388
+ console.log(`\nResult: ${s.result.subtype} | duration ${s.dur.toFixed(0)}s | turns ${s.turns}${seg}`);
1389
+ console.log(` tokens processed: ${s.processed.toLocaleString('en-US')} | cost $${s.cost.toFixed(3)}`);
1390
+ }
1391
+ console.log('');
1392
+ console.log(formatOccupancy(s));
1393
+ console.log('');
1394
+ console.log(formatSufficiency(s));
1395
+ console.log('');
1396
+ console.log(formatAllocation(s));
1397
+ if (wantEnvelope) {
1398
+ console.log('');
1399
+ console.log(formatEnvelope(s.exploreTexts, answerGlobs));
1400
+ }
1401
+ }