homegraph 0.9.10 → 1.1.1

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 (436) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +243 -193
  3. package/dist/bin/fatal-handler.d.ts +20 -0
  4. package/dist/bin/fatal-handler.d.ts.map +1 -0
  5. package/dist/bin/fatal-handler.js +118 -0
  6. package/dist/bin/fatal-handler.js.map +1 -0
  7. package/dist/bin/homegraph.d.ts +6 -6
  8. package/dist/bin/homegraph.js +532 -109
  9. package/dist/bin/homegraph.js.map +1 -1
  10. package/dist/bin/node-version-check.d.ts +4 -4
  11. package/dist/bin/node-version-check.js +12 -12
  12. package/dist/bin/uninstall.d.ts +1 -1
  13. package/dist/bin/uninstall.js +1 -1
  14. package/dist/context/index.d.ts.map +1 -1
  15. package/dist/context/index.js +8 -1
  16. package/dist/context/index.js.map +1 -1
  17. package/dist/db/index.d.ts +22 -1
  18. package/dist/db/index.d.ts.map +1 -1
  19. package/dist/db/index.js +48 -2
  20. package/dist/db/index.js.map +1 -1
  21. package/dist/db/migrations.d.ts +1 -1
  22. package/dist/db/migrations.d.ts.map +1 -1
  23. package/dist/db/migrations.js +26 -17
  24. package/dist/db/migrations.js.map +1 -1
  25. package/dist/db/queries.d.ts +21 -1
  26. package/dist/db/queries.d.ts.map +1 -1
  27. package/dist/db/queries.js +180 -137
  28. package/dist/db/queries.js.map +1 -1
  29. package/dist/db/schema.sql +152 -151
  30. package/dist/db/sqlite-adapter.d.ts +1 -1
  31. package/dist/db/sqlite-adapter.js +3 -3
  32. package/dist/directory.d.ts +87 -6
  33. package/dist/directory.d.ts.map +1 -1
  34. package/dist/directory.js +319 -57
  35. package/dist/directory.js.map +1 -1
  36. package/dist/errors.d.ts +15 -15
  37. package/dist/errors.js +20 -20
  38. package/dist/extraction/astro-extractor.d.ts +79 -0
  39. package/dist/extraction/astro-extractor.d.ts.map +1 -0
  40. package/dist/extraction/astro-extractor.js +320 -0
  41. package/dist/extraction/astro-extractor.js.map +1 -0
  42. package/dist/extraction/context.d.ts +1 -1
  43. package/dist/extraction/context.js +1 -1
  44. package/dist/extraction/extraction-version.d.ts +2 -2
  45. package/dist/extraction/extraction-version.d.ts.map +1 -1
  46. package/dist/extraction/extraction-version.js +2 -2
  47. package/dist/extraction/extraction-version.js.map +1 -1
  48. package/dist/extraction/function-ref.d.ts +118 -0
  49. package/dist/extraction/function-ref.d.ts.map +1 -0
  50. package/dist/extraction/function-ref.js +727 -0
  51. package/dist/extraction/function-ref.js.map +1 -0
  52. package/dist/extraction/generated-detection.d.ts.map +1 -1
  53. package/dist/extraction/generated-detection.js +3 -0
  54. package/dist/extraction/generated-detection.js.map +1 -1
  55. package/dist/extraction/grammars.d.ts +15 -5
  56. package/dist/extraction/grammars.d.ts.map +1 -1
  57. package/dist/extraction/grammars.js +44 -10
  58. package/dist/extraction/grammars.js.map +1 -1
  59. package/dist/extraction/index.d.ts +37 -0
  60. package/dist/extraction/index.d.ts.map +1 -1
  61. package/dist/extraction/index.js +562 -70
  62. package/dist/extraction/index.js.map +1 -1
  63. package/dist/extraction/languages/arkts-viewtree.d.ts +22 -0
  64. package/dist/extraction/languages/arkts-viewtree.d.ts.map +1 -0
  65. package/dist/extraction/languages/arkts-viewtree.js +133 -0
  66. package/dist/extraction/languages/arkts-viewtree.js.map +1 -0
  67. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  68. package/dist/extraction/languages/arkts.js +211 -20
  69. package/dist/extraction/languages/arkts.js.map +1 -1
  70. package/dist/extraction/languages/c-cpp.d.ts +8 -0
  71. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  72. package/dist/extraction/languages/c-cpp.js +104 -2
  73. package/dist/extraction/languages/c-cpp.js.map +1 -1
  74. package/dist/extraction/languages/csharp.d.ts.map +1 -1
  75. package/dist/extraction/languages/csharp.js +58 -3
  76. package/dist/extraction/languages/csharp.js.map +1 -1
  77. package/dist/extraction/languages/dart.d.ts.map +1 -1
  78. package/dist/extraction/languages/dart.js +183 -1
  79. package/dist/extraction/languages/dart.js.map +1 -1
  80. package/dist/extraction/languages/go.d.ts.map +1 -1
  81. package/dist/extraction/languages/go.js +37 -0
  82. package/dist/extraction/languages/go.js.map +1 -1
  83. package/dist/extraction/languages/index.d.ts.map +1 -1
  84. package/dist/extraction/languages/index.js +2 -0
  85. package/dist/extraction/languages/index.js.map +1 -1
  86. package/dist/extraction/languages/java.d.ts.map +1 -1
  87. package/dist/extraction/languages/java.js +241 -0
  88. package/dist/extraction/languages/java.js.map +1 -1
  89. package/dist/extraction/languages/javascript.d.ts.map +1 -1
  90. package/dist/extraction/languages/javascript.js +16 -0
  91. package/dist/extraction/languages/javascript.js.map +1 -1
  92. package/dist/extraction/languages/kotlin.d.ts.map +1 -1
  93. package/dist/extraction/languages/kotlin.js +94 -0
  94. package/dist/extraction/languages/kotlin.js.map +1 -1
  95. package/dist/extraction/languages/objc.d.ts.map +1 -1
  96. package/dist/extraction/languages/objc.js +42 -0
  97. package/dist/extraction/languages/objc.js.map +1 -1
  98. package/dist/extraction/languages/pascal.d.ts.map +1 -1
  99. package/dist/extraction/languages/pascal.js +11 -0
  100. package/dist/extraction/languages/pascal.js.map +1 -1
  101. package/dist/extraction/languages/php.d.ts.map +1 -1
  102. package/dist/extraction/languages/php.js +77 -1
  103. package/dist/extraction/languages/php.js.map +1 -1
  104. package/dist/extraction/languages/r.d.ts +3 -0
  105. package/dist/extraction/languages/r.d.ts.map +1 -0
  106. package/dist/extraction/languages/r.js +314 -0
  107. package/dist/extraction/languages/r.js.map +1 -0
  108. package/dist/extraction/languages/rust.d.ts.map +1 -1
  109. package/dist/extraction/languages/rust.js +29 -0
  110. package/dist/extraction/languages/rust.js.map +1 -1
  111. package/dist/extraction/languages/scala.d.ts.map +1 -1
  112. package/dist/extraction/languages/scala.js +44 -9
  113. package/dist/extraction/languages/scala.js.map +1 -1
  114. package/dist/extraction/languages/swift.d.ts.map +1 -1
  115. package/dist/extraction/languages/swift.js +61 -0
  116. package/dist/extraction/languages/swift.js.map +1 -1
  117. package/dist/extraction/languages/typescript.d.ts +13 -0
  118. package/dist/extraction/languages/typescript.d.ts.map +1 -1
  119. package/dist/extraction/languages/typescript.js +38 -0
  120. package/dist/extraction/languages/typescript.js.map +1 -1
  121. package/dist/extraction/liquid-extractor.d.ts.map +1 -1
  122. package/dist/extraction/liquid-extractor.js +4 -1
  123. package/dist/extraction/liquid-extractor.js.map +1 -1
  124. package/dist/extraction/parse-worker.js +4 -1
  125. package/dist/extraction/parse-worker.js.map +1 -1
  126. package/dist/extraction/svelte-extractor.d.ts.map +1 -1
  127. package/dist/extraction/svelte-extractor.js +6 -3
  128. package/dist/extraction/svelte-extractor.js.map +1 -1
  129. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  130. package/dist/extraction/tree-sitter-helpers.js +59 -10
  131. package/dist/extraction/tree-sitter-helpers.js.map +1 -1
  132. package/dist/extraction/tree-sitter-types.d.ts +29 -0
  133. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  134. package/dist/extraction/tree-sitter.d.ts +192 -0
  135. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  136. package/dist/extraction/tree-sitter.js +1553 -45
  137. package/dist/extraction/tree-sitter.js.map +1 -1
  138. package/dist/extraction/vue-extractor.d.ts.map +1 -1
  139. package/dist/extraction/vue-extractor.js +6 -3
  140. package/dist/extraction/vue-extractor.js.map +1 -1
  141. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  142. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  143. package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
  144. package/dist/graph/traversal.d.ts.map +1 -1
  145. package/dist/graph/traversal.js +11 -2
  146. package/dist/graph/traversal.js.map +1 -1
  147. package/dist/index.d.ts +64 -23
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +137 -41
  150. package/dist/index.js.map +1 -1
  151. package/dist/installer/config-writer.d.ts +1 -1
  152. package/dist/installer/config-writer.js +1 -1
  153. package/dist/installer/index.d.ts +4 -4
  154. package/dist/installer/index.d.ts.map +1 -1
  155. package/dist/installer/index.js +100 -73
  156. package/dist/installer/index.js.map +1 -1
  157. package/dist/installer/instructions-template.d.ts +34 -11
  158. package/dist/installer/instructions-template.d.ts.map +1 -1
  159. package/dist/installer/instructions-template.js +44 -12
  160. package/dist/installer/instructions-template.js.map +1 -1
  161. package/dist/installer/targets/antigravity.d.ts +6 -6
  162. package/dist/installer/targets/antigravity.js +14 -14
  163. package/dist/installer/targets/claude.d.ts +19 -13
  164. package/dist/installer/targets/claude.d.ts.map +1 -1
  165. package/dist/installer/targets/claude.js +97 -29
  166. package/dist/installer/targets/claude.js.map +1 -1
  167. package/dist/installer/targets/codebuddy.d.ts +17 -0
  168. package/dist/installer/targets/codebuddy.d.ts.map +1 -0
  169. package/dist/installer/targets/codebuddy.js +150 -0
  170. package/dist/installer/targets/codebuddy.js.map +1 -0
  171. package/dist/installer/targets/codex.js +5 -7
  172. package/dist/installer/targets/codex.js.map +1 -1
  173. package/dist/installer/targets/cursor.d.ts +1 -1
  174. package/dist/installer/targets/cursor.js +7 -7
  175. package/dist/installer/targets/deveco.d.ts +23 -0
  176. package/dist/installer/targets/deveco.d.ts.map +1 -0
  177. package/dist/installer/targets/deveco.js +217 -0
  178. package/dist/installer/targets/deveco.js.map +1 -0
  179. package/dist/installer/targets/gemini.js +5 -7
  180. package/dist/installer/targets/gemini.js.map +1 -1
  181. package/dist/installer/targets/hermes.js +15 -15
  182. package/dist/installer/targets/kiro.d.ts +1 -1
  183. package/dist/installer/targets/kiro.js +4 -4
  184. package/dist/installer/targets/opencode.d.ts +9 -1
  185. package/dist/installer/targets/opencode.d.ts.map +1 -1
  186. package/dist/installer/targets/opencode.js +92 -41
  187. package/dist/installer/targets/opencode.js.map +1 -1
  188. package/dist/installer/targets/registry.d.ts.map +1 -1
  189. package/dist/installer/targets/registry.js +4 -0
  190. package/dist/installer/targets/registry.js.map +1 -1
  191. package/dist/installer/targets/shared.d.ts +28 -4
  192. package/dist/installer/targets/shared.d.ts.map +1 -1
  193. package/dist/installer/targets/shared.js +32 -15
  194. package/dist/installer/targets/shared.js.map +1 -1
  195. package/dist/installer/targets/types.d.ts +9 -2
  196. package/dist/installer/targets/types.d.ts.map +1 -1
  197. package/dist/mcp/daemon-manager.d.ts +42 -0
  198. package/dist/mcp/daemon-manager.d.ts.map +1 -0
  199. package/dist/mcp/daemon-manager.js +129 -0
  200. package/dist/mcp/daemon-manager.js.map +1 -0
  201. package/dist/mcp/daemon-paths.d.ts +1 -1
  202. package/dist/mcp/daemon-paths.js +3 -3
  203. package/dist/mcp/daemon-registry.d.ts +47 -0
  204. package/dist/mcp/daemon-registry.d.ts.map +1 -0
  205. package/dist/mcp/daemon-registry.js +229 -0
  206. package/dist/mcp/daemon-registry.js.map +1 -0
  207. package/dist/mcp/daemon.d.ts.map +1 -1
  208. package/dist/mcp/daemon.js +27 -6
  209. package/dist/mcp/daemon.js.map +1 -1
  210. package/dist/mcp/dynamic-boundaries.d.ts +41 -0
  211. package/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
  212. package/dist/mcp/dynamic-boundaries.js +359 -0
  213. package/dist/mcp/dynamic-boundaries.js.map +1 -0
  214. package/dist/mcp/engine.d.ts +4 -4
  215. package/dist/mcp/engine.d.ts.map +1 -1
  216. package/dist/mcp/engine.js +32 -24
  217. package/dist/mcp/engine.js.map +1 -1
  218. package/dist/mcp/index.d.ts +8 -7
  219. package/dist/mcp/index.d.ts.map +1 -1
  220. package/dist/mcp/index.js +39 -18
  221. package/dist/mcp/index.js.map +1 -1
  222. package/dist/mcp/liveness-watchdog.d.ts +18 -0
  223. package/dist/mcp/liveness-watchdog.d.ts.map +1 -0
  224. package/dist/mcp/liveness-watchdog.js +207 -0
  225. package/dist/mcp/liveness-watchdog.js.map +1 -0
  226. package/dist/mcp/proxy.d.ts +6 -0
  227. package/dist/mcp/proxy.d.ts.map +1 -1
  228. package/dist/mcp/proxy.js +75 -18
  229. package/dist/mcp/proxy.js.map +1 -1
  230. package/dist/mcp/server-instructions.d.ts +22 -7
  231. package/dist/mcp/server-instructions.d.ts.map +1 -1
  232. package/dist/mcp/server-instructions.js +91 -59
  233. package/dist/mcp/server-instructions.js.map +1 -1
  234. package/dist/mcp/session.d.ts +3 -1
  235. package/dist/mcp/session.d.ts.map +1 -1
  236. package/dist/mcp/session.js +43 -7
  237. package/dist/mcp/session.js.map +1 -1
  238. package/dist/mcp/stdin-teardown.d.ts +27 -0
  239. package/dist/mcp/stdin-teardown.d.ts.map +1 -0
  240. package/dist/mcp/stdin-teardown.js +49 -0
  241. package/dist/mcp/stdin-teardown.js.map +1 -0
  242. package/dist/mcp/tools.d.ts +137 -16
  243. package/dist/mcp/tools.d.ts.map +1 -1
  244. package/dist/mcp/tools.js +1271 -219
  245. package/dist/mcp/tools.js.map +1 -1
  246. package/dist/mcp/transport.d.ts +1 -1
  247. package/dist/mcp/transport.d.ts.map +1 -1
  248. package/dist/mcp/transport.js +20 -4
  249. package/dist/mcp/transport.js.map +1 -1
  250. package/dist/mcp/version.d.ts +1 -1
  251. package/dist/mcp/version.js +2 -2
  252. package/dist/project-config.d.ts +36 -0
  253. package/dist/project-config.d.ts.map +1 -0
  254. package/dist/project-config.js +235 -0
  255. package/dist/project-config.js.map +1 -0
  256. package/dist/reasoning/config.d.ts +45 -0
  257. package/dist/reasoning/config.d.ts.map +1 -0
  258. package/dist/reasoning/config.js +171 -0
  259. package/dist/reasoning/config.js.map +1 -0
  260. package/dist/reasoning/credentials.d.ts +5 -0
  261. package/dist/reasoning/credentials.d.ts.map +1 -0
  262. package/dist/reasoning/credentials.js +83 -0
  263. package/dist/reasoning/credentials.js.map +1 -0
  264. package/dist/reasoning/login.d.ts +21 -0
  265. package/dist/reasoning/login.d.ts.map +1 -0
  266. package/dist/reasoning/login.js +85 -0
  267. package/dist/reasoning/login.js.map +1 -0
  268. package/dist/reasoning/reasoner.d.ts +43 -0
  269. package/dist/reasoning/reasoner.d.ts.map +1 -0
  270. package/dist/reasoning/reasoner.js +308 -0
  271. package/dist/reasoning/reasoner.js.map +1 -0
  272. package/dist/resolution/c-fnptr-synthesizer.d.ts +33 -0
  273. package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
  274. package/dist/resolution/c-fnptr-synthesizer.js +352 -0
  275. package/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
  276. package/dist/resolution/callback-synthesizer.d.ts +6 -1
  277. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  278. package/dist/resolution/callback-synthesizer.js +1197 -1
  279. package/dist/resolution/callback-synthesizer.js.map +1 -1
  280. package/dist/resolution/frameworks/arkts-entry.d.ts +12 -0
  281. package/dist/resolution/frameworks/arkts-entry.d.ts.map +1 -0
  282. package/dist/resolution/frameworks/arkts-entry.js +214 -0
  283. package/dist/resolution/frameworks/arkts-entry.js.map +1 -0
  284. package/dist/resolution/frameworks/arkts-napi.d.ts +3 -0
  285. package/dist/resolution/frameworks/arkts-napi.d.ts.map +1 -0
  286. package/dist/resolution/frameworks/arkts-napi.js +166 -0
  287. package/dist/resolution/frameworks/arkts-napi.js.map +1 -0
  288. package/dist/resolution/frameworks/astro.d.ts +9 -0
  289. package/dist/resolution/frameworks/astro.d.ts.map +1 -0
  290. package/dist/resolution/frameworks/astro.js +169 -0
  291. package/dist/resolution/frameworks/astro.js.map +1 -0
  292. package/dist/resolution/frameworks/goframe.d.ts +41 -0
  293. package/dist/resolution/frameworks/goframe.d.ts.map +1 -0
  294. package/dist/resolution/frameworks/goframe.js +112 -0
  295. package/dist/resolution/frameworks/goframe.js.map +1 -0
  296. package/dist/resolution/frameworks/index.d.ts +3 -0
  297. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  298. package/dist/resolution/frameworks/index.js +17 -1
  299. package/dist/resolution/frameworks/index.js.map +1 -1
  300. package/dist/resolution/frameworks/java.js +6 -1
  301. package/dist/resolution/frameworks/java.js.map +1 -1
  302. package/dist/resolution/frameworks/react.d.ts.map +1 -1
  303. package/dist/resolution/frameworks/react.js +32 -63
  304. package/dist/resolution/frameworks/react.js.map +1 -1
  305. package/dist/resolution/frameworks/svelte.js +6 -2
  306. package/dist/resolution/frameworks/svelte.js.map +1 -1
  307. package/dist/resolution/frameworks/vue.js +24 -27
  308. package/dist/resolution/frameworks/vue.js.map +1 -1
  309. package/dist/resolution/goframe-synthesizer.d.ts +28 -0
  310. package/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
  311. package/dist/resolution/goframe-synthesizer.js +158 -0
  312. package/dist/resolution/goframe-synthesizer.js.map +1 -0
  313. package/dist/resolution/import-resolver.d.ts +10 -0
  314. package/dist/resolution/import-resolver.d.ts.map +1 -1
  315. package/dist/resolution/import-resolver.js +125 -2
  316. package/dist/resolution/import-resolver.js.map +1 -1
  317. package/dist/resolution/index.d.ts +37 -0
  318. package/dist/resolution/index.d.ts.map +1 -1
  319. package/dist/resolution/index.js +306 -3
  320. package/dist/resolution/index.js.map +1 -1
  321. package/dist/resolution/name-matcher.d.ts +41 -0
  322. package/dist/resolution/name-matcher.d.ts.map +1 -1
  323. package/dist/resolution/name-matcher.js +519 -14
  324. package/dist/resolution/name-matcher.js.map +1 -1
  325. package/dist/resolution/strip-comments.d.ts +1 -1
  326. package/dist/resolution/strip-comments.d.ts.map +1 -1
  327. package/dist/resolution/strip-comments.js +2 -0
  328. package/dist/resolution/strip-comments.js.map +1 -1
  329. package/dist/resolution/types.d.ts +20 -3
  330. package/dist/resolution/types.d.ts.map +1 -1
  331. package/dist/search/query-utils.d.ts +17 -1
  332. package/dist/search/query-utils.d.ts.map +1 -1
  333. package/dist/search/query-utils.js +79 -10
  334. package/dist/search/query-utils.js.map +1 -1
  335. package/dist/sync/git-hooks.d.ts +4 -4
  336. package/dist/sync/git-hooks.js +5 -5
  337. package/dist/sync/watcher.d.ts +69 -2
  338. package/dist/sync/watcher.d.ts.map +1 -1
  339. package/dist/sync/watcher.js +226 -21
  340. package/dist/sync/watcher.js.map +1 -1
  341. package/dist/sync/worktree.d.ts +3 -3
  342. package/dist/sync/worktree.js +5 -5
  343. package/dist/telemetry/index.d.ts +143 -0
  344. package/dist/telemetry/index.d.ts.map +1 -0
  345. package/dist/telemetry/index.js +541 -0
  346. package/dist/telemetry/index.js.map +1 -0
  347. package/dist/types.d.ts +18 -3
  348. package/dist/types.d.ts.map +1 -1
  349. package/dist/types.js +3 -1
  350. package/dist/types.js.map +1 -1
  351. package/dist/upgrade/index.d.ts.map +1 -1
  352. package/dist/upgrade/index.js +46 -10
  353. package/dist/upgrade/index.js.map +1 -1
  354. package/dist/utils.d.ts +46 -27
  355. package/dist/utils.d.ts.map +1 -1
  356. package/dist/utils.js +87 -53
  357. package/dist/utils.js.map +1 -1
  358. package/package.json +57 -57
  359. package/scripts/add-lang/bench.sh +60 -60
  360. package/scripts/add-lang/check-grammar.mjs +75 -75
  361. package/scripts/add-lang/dump-ast.mjs +103 -103
  362. package/scripts/add-lang/verify-extraction.mjs +70 -70
  363. package/scripts/agent-eval/ab-adoption.sh +91 -0
  364. package/scripts/agent-eval/ab-hook.sh +86 -0
  365. package/scripts/agent-eval/ab-impl.sh +78 -0
  366. package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -0
  367. package/scripts/agent-eval/ab-sufficiency.sh +78 -0
  368. package/scripts/agent-eval/arms-F.sh +21 -21
  369. package/scripts/agent-eval/arms-matrix.sh +37 -37
  370. package/scripts/agent-eval/audit.sh +68 -68
  371. package/scripts/agent-eval/bench-readme.sh +28 -28
  372. package/scripts/agent-eval/bench-why-repo.sh +22 -22
  373. package/scripts/agent-eval/block-read-hook.sh +19 -19
  374. package/scripts/agent-eval/hook-settings.json +15 -15
  375. package/scripts/agent-eval/itrun.sh +120 -120
  376. package/scripts/agent-eval/offload-eval-3arm.sh +72 -0
  377. package/scripts/agent-eval/offload-eval-cost.mjs +133 -0
  378. package/scripts/agent-eval/offload-eval-effort.mjs +108 -0
  379. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -0
  380. package/scripts/agent-eval/offload-eval-frontload.sh +47 -0
  381. package/scripts/agent-eval/offload-eval-ground-truth.json +18 -0
  382. package/scripts/agent-eval/offload-eval-hook.mjs +84 -0
  383. package/scripts/agent-eval/offload-eval-judge.mjs +103 -0
  384. package/scripts/agent-eval/offload-eval-matrix.sh +20 -0
  385. package/scripts/agent-eval/offload-eval-metrics.mjs +94 -0
  386. package/scripts/agent-eval/offload-eval-refs1.sh +50 -0
  387. package/scripts/agent-eval/offload-eval-setup.sh +24 -0
  388. package/scripts/agent-eval/offload-eval-styles.sh +72 -0
  389. package/scripts/agent-eval/offload-eval-summarize.mjs +68 -0
  390. package/scripts/agent-eval/offload-eval.md +76 -0
  391. package/scripts/agent-eval/parse-arms.mjs +116 -116
  392. package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
  393. package/scripts/agent-eval/parse-run.mjs +45 -45
  394. package/scripts/agent-eval/parse-session.mjs +93 -93
  395. package/scripts/agent-eval/probe-context.mjs +21 -21
  396. package/scripts/agent-eval/probe-explore.mjs +40 -40
  397. package/scripts/agent-eval/probe-node.mjs +20 -20
  398. package/scripts/agent-eval/probe-sweep.mjs +119 -119
  399. package/scripts/agent-eval/probe-trace.mjs +20 -20
  400. package/scripts/agent-eval/redirect-read-hook.sh +38 -0
  401. package/scripts/agent-eval/run-agent.sh +34 -34
  402. package/scripts/agent-eval/run-all.sh +69 -67
  403. package/scripts/agent-eval/run-arms.sh +56 -56
  404. package/scripts/agent-eval/seq-matrix.mjs +137 -137
  405. package/scripts/build-bundle.sh +118 -118
  406. package/scripts/extract-release-notes.mjs +130 -130
  407. package/scripts/local-install.sh +41 -41
  408. package/scripts/npm-sdk.js +75 -75
  409. package/scripts/npm-shim.js +246 -246
  410. package/scripts/pack-npm.sh +119 -119
  411. package/scripts/prepare-release.mjs +270 -270
  412. package/scripts/qa_eval/README.md +400 -0
  413. package/scripts/qa_eval/agent_runner.py +526 -0
  414. package/scripts/qa_eval/data/.gitignore +4 -0
  415. package/scripts/qa_eval/data/test-set.jsonl +22 -0
  416. package/scripts/qa_eval/eval_metrics.py +233 -0
  417. package/scripts/qa_eval/external_agent.py +577 -0
  418. package/scripts/qa_eval/llm_config.py +92 -0
  419. package/scripts/qa_eval/memory_monitor.py +132 -0
  420. package/scripts/qa_eval/my_answer_accuracy.py +187 -0
  421. package/scripts/qa_eval/requirements.txt +2 -0
  422. package/scripts/qa_eval/run_pipeline.py +702 -0
  423. package/scripts/qa_eval/stats_efficiency.py +279 -0
  424. package/scripts/qa_eval/stats_scores.py +207 -0
  425. package/dist/bin/codegraph.d.ts +0 -26
  426. package/dist/bin/codegraph.d.ts.map +0 -1
  427. package/dist/bin/codegraph.js +0 -1572
  428. package/dist/bin/codegraph.js.map +0 -1
  429. package/dist/extraction/arkanalyzer-extractor.d.ts +0 -6
  430. package/dist/extraction/arkanalyzer-extractor.d.ts.map +0 -1
  431. package/dist/extraction/arkanalyzer-extractor.js +0 -11
  432. package/dist/extraction/arkanalyzer-extractor.js.map +0 -1
  433. package/dist/extraction/arkanalyzer-project.d.ts +0 -20
  434. package/dist/extraction/arkanalyzer-project.d.ts.map +0 -1
  435. package/dist/extraction/arkanalyzer-project.js +0 -596
  436. package/dist/extraction/arkanalyzer-project.js.map +0 -1
@@ -0,0 +1,577 @@
1
+ """Run qa_eval dataset through real Agent hosts: Claude Code CLI, DevEco Code (opencode CLI)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ import os
8
+ import re
9
+ import shutil
10
+ import subprocess
11
+ import tempfile
12
+ import time
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from memory_monitor import sample_memory
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ TIME_FMT = "%Y-%m-%d %H:%M:%S.%f"
22
+
23
+ HOST_CLAUDE = "claude-code"
24
+ HOST_DEVECO = "deveco-code"
25
+ SUPPORTED_HOSTS = (HOST_CLAUDE, HOST_DEVECO)
26
+
27
+
28
+ def _strip_ansi(text: str) -> str:
29
+ return re.sub(r"\x1b\[[0-9;]*m", "", text or "")
30
+
31
+
32
+ def _cli_error_summary(stderr: str, stdout: str, *, max_len: int = 400) -> str:
33
+ """Pick a short user-facing error from CLI stderr/stdout."""
34
+ for raw in (stderr, stdout):
35
+ clean = _strip_ansi(raw).strip()
36
+ if not clean:
37
+ continue
38
+ for line in clean.splitlines():
39
+ line = line.strip()
40
+ if not line or line.lower().startswith("error:"):
41
+ continue
42
+ if len(line) > 20:
43
+ return line[:max_len]
44
+ if clean:
45
+ return clean[:max_len]
46
+ return ""
47
+
48
+
49
+ def auth_failure_reason(text: str) -> str | None:
50
+ """Return a user-facing reason if output looks like an auth/login failure."""
51
+ t = _strip_ansi(text or "").lower()
52
+ if any(h in t for h in ("not logged in", "please run /login")) or (
53
+ "/login" in t and "run" in t
54
+ ):
55
+ return "Claude Code 未登录,请先运行: claude login"
56
+ if "credentials cannot be decrypted" in t or "saved provider credentials are unavailable" in t:
57
+ return (
58
+ "DevEco Code 本地凭证无法解密。请运行: deveco providers reset,"
59
+ "然后在 TUI 中重新配置模型,或执行 deveco providers login"
60
+ )
61
+ if "deveco providers reset" in t or "deveco auth reset" in t:
62
+ return (
63
+ "DevEco Code 未配置或凭证无效。请运行: deveco providers reset,"
64
+ "然后重新登录/配置 provider"
65
+ )
66
+ if "invalid api key" in t or "incorrect api key" in t:
67
+ return "API Key 无效"
68
+ if "authentication required" in t or "unauthorized" in t:
69
+ return "未授权,请检查登录或 API Key"
70
+ return None
71
+
72
+
73
+ def _log_line(log_file: Path | None, msg: str) -> None:
74
+ if log_file is None:
75
+ return
76
+ log_file.parent.mkdir(parents=True, exist_ok=True)
77
+ with log_file.open("a", encoding="utf-8") as f:
78
+ f.write(f"{datetime.now().strftime(TIME_FMT)} {msg}\n")
79
+
80
+
81
+ def _log_memory(log_file: Path | None, mem: dict[str, float | None]) -> None:
82
+ if mem.get("peak_rss_mb") is not None:
83
+ _log_line(log_file, f"peakRssMb = {mem['peak_rss_mb']}")
84
+ if mem.get("avg_rss_mb") is not None:
85
+ _log_line(log_file, f"avgRssMb = {mem['avg_rss_mb']}")
86
+
87
+
88
+ def find_claude_cli() -> str:
89
+ found = shutil.which("claude")
90
+ if not found:
91
+ raise FileNotFoundError(
92
+ "未找到 claude CLI。请安装 Claude Code 并确保 `claude` 在 PATH 中。"
93
+ )
94
+ return found
95
+
96
+
97
+ def find_deveco_cli() -> str:
98
+ for name in ("deveco", "opencode"):
99
+ found = shutil.which(name)
100
+ if found:
101
+ return found
102
+ raise FileNotFoundError(
103
+ "未找到 DevEco Code / opencode CLI。请安装 DevEco Code 或 opencode 并加入 PATH。"
104
+ )
105
+
106
+
107
+ def write_mcp_config(path: Path, *, hg_command: str, hg_args: list[str]) -> None:
108
+ path.write_text(
109
+ json.dumps({"mcpServers": {"homegraph": {"command": hg_command, "args": hg_args}}}, indent=2)
110
+ + "\n",
111
+ encoding="utf-8",
112
+ )
113
+
114
+
115
+ def _split_hg_bin(hg_bin: str) -> tuple[str, list[str]]:
116
+ if hg_bin.startswith("node "):
117
+ parts = hg_bin.split(" ", 1)
118
+ return parts[0], parts[1].split() + ["serve", "--mcp"]
119
+ return hg_bin, ["serve", "--mcp"]
120
+
121
+
122
+ def parse_claude_stream_json(raw: str) -> dict[str, Any]:
123
+ tool_trace: list[str] = []
124
+ answer_parts: list[str] = []
125
+ total_tokens = 0
126
+ max_turn = 0
127
+ duration_ms = 0
128
+
129
+ for line in raw.splitlines():
130
+ line = line.strip()
131
+ if not line:
132
+ continue
133
+ try:
134
+ ev = json.loads(line)
135
+ except json.JSONDecodeError:
136
+ continue
137
+
138
+ if ev.get("type") == "assistant" and ev.get("message", {}).get("content"):
139
+ max_turn += 1
140
+ for block in ev["message"]["content"]:
141
+ if block.get("type") == "text" and block.get("text"):
142
+ answer_parts.append(str(block["text"]))
143
+ if block.get("type") == "tool_use":
144
+ name = block.get("name", "?")
145
+ inp = block.get("input") or {}
146
+ tool_trace.append(
147
+ f"---\n{name}\nargs: {json.dumps(inp, ensure_ascii=False)[:500]}\n---"
148
+ )
149
+
150
+ if ev.get("type") == "result":
151
+ usage = ev.get("usage") or {}
152
+ total_tokens = int(
153
+ (usage.get("input_tokens") or 0)
154
+ + (usage.get("cache_read_input_tokens") or 0)
155
+ + (usage.get("cache_creation_input_tokens") or 0)
156
+ + (usage.get("output_tokens") or 0)
157
+ )
158
+ duration_ms = int(ev.get("duration_ms") or 0)
159
+ if ev.get("num_turns"):
160
+ max_turn = max(max_turn, int(ev["num_turns"]))
161
+ if ev.get("result") and not answer_parts:
162
+ answer_parts.append(str(ev["result"]))
163
+
164
+ answer = "\n".join(answer_parts).strip()
165
+ output = "\n\n".join(tool_trace + ([answer] if answer else []))
166
+ auth_err = auth_failure_reason(answer) or auth_failure_reason(raw)
167
+ if auth_err:
168
+ return {
169
+ "output_answer": output,
170
+ "agent_status": "error",
171
+ "agent_error": auth_err,
172
+ "agent_turns": max_turn,
173
+ "agent_duration_ms": duration_ms,
174
+ "agent_usage": {"total_tokens": total_tokens} if total_tokens else {},
175
+ }
176
+ return {
177
+ "output_answer": output,
178
+ "agent_status": "success" if answer else "error",
179
+ "agent_turns": max_turn,
180
+ "agent_duration_ms": duration_ms,
181
+ "agent_usage": {"total_tokens": total_tokens} if total_tokens else {},
182
+ }
183
+
184
+
185
+ def parse_opencode_json_events(raw: str) -> dict[str, Any]:
186
+ tool_trace: list[str] = []
187
+ answer_parts: list[str] = []
188
+ total_tokens = 0
189
+ max_turn = 0
190
+ duration_ms = 0
191
+
192
+ for line in raw.splitlines():
193
+ line = line.strip()
194
+ if not line:
195
+ continue
196
+ try:
197
+ ev = json.loads(line)
198
+ except json.JSONDecodeError:
199
+ continue
200
+
201
+ ev_type = ev.get("type") or ev.get("event")
202
+ if ev_type in ("message", "assistant", "text"):
203
+ content = ev.get("content") or ev.get("text") or ev.get("message")
204
+ if isinstance(content, str) and content.strip():
205
+ answer_parts.append(content.strip())
206
+ max_turn += 1
207
+ elif isinstance(content, list):
208
+ max_turn += 1
209
+ for block in content:
210
+ if isinstance(block, dict):
211
+ if block.get("type") == "text" and block.get("text"):
212
+ answer_parts.append(str(block["text"]))
213
+ if block.get("type") == "tool_use":
214
+ tool_trace.append(
215
+ f"---\n{block.get('name', '?')}\n"
216
+ f"args: {json.dumps(block.get('input') or {}, ensure_ascii=False)[:500]}\n---"
217
+ )
218
+
219
+ usage = ev.get("usage") or ev.get("tokens")
220
+ if isinstance(usage, dict):
221
+ total = usage.get("total") or usage.get("total_tokens")
222
+ if total:
223
+ total_tokens = max(total_tokens, int(total))
224
+
225
+ if ev.get("duration_ms"):
226
+ duration_ms = max(duration_ms, int(ev["duration_ms"]))
227
+
228
+ answer = "\n".join(answer_parts).strip()
229
+ output = "\n\n".join(tool_trace + ([answer] if answer else []))
230
+ auth_err = auth_failure_reason(answer) or auth_failure_reason(raw)
231
+ if auth_err:
232
+ return {
233
+ "output_answer": output,
234
+ "agent_status": "error",
235
+ "agent_error": auth_err,
236
+ "agent_turns": max_turn,
237
+ "agent_duration_ms": duration_ms,
238
+ "agent_usage": {"total_tokens": total_tokens} if total_tokens else {},
239
+ }
240
+ return {
241
+ "output_answer": output,
242
+ "agent_status": "success" if answer else "error",
243
+ "agent_turns": max_turn,
244
+ "agent_duration_ms": duration_ms,
245
+ "agent_usage": {"total_tokens": total_tokens} if total_tokens else {},
246
+ }
247
+
248
+
249
+ def verify_claude_login() -> None:
250
+ """Fail fast before burning the whole dataset on 'Not logged in'."""
251
+ claude = find_claude_cli()
252
+ proc = subprocess.run(
253
+ [claude, "-p", "ping", "--output-format", "text", "--max-turns", "1"],
254
+ capture_output=True,
255
+ text=True,
256
+ timeout=45,
257
+ cwd=os.getcwd(),
258
+ )
259
+ combined = f"{proc.stdout}\n{proc.stderr}"
260
+ reason = auth_failure_reason(combined)
261
+ if reason:
262
+ raise RuntimeError(f"{reason}\n(探测输出: {combined.strip()[:200]})")
263
+
264
+
265
+ def run_claude_query(
266
+ repo: Path,
267
+ query: str,
268
+ *,
269
+ arm: str,
270
+ hg_bin: str,
271
+ log_file: Path | None,
272
+ task_id: int,
273
+ timeout_sec: int = 600,
274
+ ) -> dict[str, Any]:
275
+ claude = find_claude_cli()
276
+ backend = f"claude-code-{'with' if arm == 'with' else 'without'}-homegraph"
277
+
278
+ with tempfile.TemporaryDirectory(prefix="qa-eval-mcp-") as tmp:
279
+ mcp_path = Path(tmp) / "mcp.json"
280
+ if arm == "with":
281
+ cmd, base_args = _split_hg_bin(hg_bin)
282
+ write_mcp_config(
283
+ mcp_path,
284
+ hg_command=cmd,
285
+ hg_args=[*base_args, "--path", str(repo.resolve())],
286
+ )
287
+ mcp_args = ["--strict-mcp-config", "--mcp-config", str(mcp_path)]
288
+ else:
289
+ mcp_path.write_text('{"mcpServers":{}}\n', encoding="utf-8")
290
+ mcp_args = ["--strict-mcp-config", "--mcp-config", str(mcp_path)]
291
+
292
+ cmd = [
293
+ claude,
294
+ "-p",
295
+ query,
296
+ "--output-format",
297
+ "stream-json",
298
+ "--verbose",
299
+ "--permission-mode",
300
+ "bypassPermissions",
301
+ *mcp_args,
302
+ ]
303
+
304
+ _log_line(log_file, f"Evaluate {task_id}:")
305
+ _log_line(log_file, "the 1 turn")
306
+ t0 = time.time()
307
+
308
+ proc = subprocess.Popen(
309
+ cmd,
310
+ cwd=str(repo),
311
+ stdout=subprocess.PIPE,
312
+ stderr=subprocess.PIPE,
313
+ text=True,
314
+ )
315
+ with sample_memory(proc.pid) as sampler:
316
+ try:
317
+ stdout, stderr = proc.communicate(timeout=timeout_sec)
318
+ except subprocess.TimeoutExpired:
319
+ proc.kill()
320
+ stdout, stderr = proc.communicate()
321
+ mem = sampler.last_stats
322
+ _log_memory(log_file, mem)
323
+ return {
324
+ "output_answer": "",
325
+ "agent_status": "error",
326
+ "agent_error": f"timeout after {timeout_sec}s",
327
+ "agent_backend": backend,
328
+ "agent_memory_mb": mem,
329
+ }
330
+ mem = sampler.last_stats
331
+
332
+ if proc.returncode != 0 and not stdout.strip():
333
+ _log_memory(log_file, mem)
334
+ return {
335
+ "output_answer": (stderr or "")[:2000],
336
+ "agent_status": "error",
337
+ "agent_error": f"claude exit {proc.returncode}",
338
+ "agent_backend": backend,
339
+ "agent_memory_mb": mem,
340
+ }
341
+
342
+ _log_line(log_file, "first token")
343
+ combined = f"{stdout}\n{stderr}"
344
+ auth_err = auth_failure_reason(combined)
345
+ if auth_err:
346
+ _log_memory(log_file, mem)
347
+ return {
348
+ "output_answer": (stdout or stderr or "")[:2000],
349
+ "agent_status": "error",
350
+ "agent_error": auth_err,
351
+ "agent_backend": backend,
352
+ "agent_host": HOST_CLAUDE,
353
+ "agent_memory_mb": mem,
354
+ }
355
+ parsed = parse_claude_stream_json(stdout)
356
+ tokens = (parsed.get("agent_usage") or {}).get("total_tokens") or 0
357
+ if tokens:
358
+ _log_line(log_file, f"totalTokenCount = {tokens}")
359
+ _log_memory(log_file, mem)
360
+
361
+ duration_ms = parsed.get("agent_duration_ms") or int((time.time() - t0) * 1000)
362
+ return {
363
+ **parsed,
364
+ "agent_duration_ms": duration_ms,
365
+ "agent_backend": backend,
366
+ "agent_host": HOST_CLAUDE,
367
+ "ab_arm": "with-homegraph" if arm == "with" else "without-homegraph",
368
+ "agent_memory_mb": mem,
369
+ }
370
+
371
+
372
+ def run_deveco_query(
373
+ repo: Path,
374
+ query: str,
375
+ *,
376
+ arm: str,
377
+ hg_bin: str,
378
+ log_file: Path | None,
379
+ task_id: int,
380
+ model: str | None = None,
381
+ timeout_sec: int = 600,
382
+ ) -> dict[str, Any]:
383
+ cli = find_deveco_cli()
384
+ backend = f"deveco-code-{'with' if arm == 'with' else 'without'}-homegraph"
385
+
386
+ config_dir = repo / ".qa_eval_deveco"
387
+ config_dir.mkdir(exist_ok=True)
388
+ config_path = config_dir / "opencode.jsonc"
389
+ if arm == "with":
390
+ cmd, base_args = _split_hg_bin(hg_bin)
391
+ config_path.write_text(
392
+ json.dumps(
393
+ {
394
+ "$schema": "https://opencode.ai/config.json",
395
+ "mcp": {
396
+ "homegraph": {
397
+ "type": "local",
398
+ "command": [cmd, *base_args, "--path", str(repo.resolve())],
399
+ "enabled": True,
400
+ }
401
+ },
402
+ },
403
+ indent=2,
404
+ )
405
+ + "\n",
406
+ encoding="utf-8",
407
+ )
408
+ else:
409
+ config_path.write_text(
410
+ '{"$schema":"https://opencode.ai/config.json","mcp":{}}\n',
411
+ encoding="utf-8",
412
+ )
413
+
414
+ run_cmd = [
415
+ cli,
416
+ "run",
417
+ query,
418
+ "--format",
419
+ "json",
420
+ "--dir",
421
+ str(repo),
422
+ "--dangerously-skip-permissions",
423
+ ]
424
+ if model:
425
+ run_cmd.extend(["--model", model])
426
+
427
+ _log_line(log_file, f"Evaluate {task_id}:")
428
+ _log_line(log_file, "the 1 turn")
429
+ t0 = time.time()
430
+
431
+ proc = subprocess.Popen(
432
+ run_cmd,
433
+ cwd=str(repo),
434
+ stdout=subprocess.PIPE,
435
+ stderr=subprocess.PIPE,
436
+ text=True,
437
+ env={**os.environ, "XDG_CONFIG_HOME": str(config_dir)},
438
+ )
439
+ with sample_memory(proc.pid) as sampler:
440
+ try:
441
+ stdout, stderr = proc.communicate(timeout=timeout_sec)
442
+ except subprocess.TimeoutExpired:
443
+ proc.kill()
444
+ stdout, stderr = proc.communicate()
445
+ mem = sampler.last_stats
446
+ _log_memory(log_file, mem)
447
+ return {
448
+ "output_answer": "",
449
+ "agent_status": "error",
450
+ "agent_error": f"timeout after {timeout_sec}s",
451
+ "agent_backend": backend,
452
+ "agent_memory_mb": mem,
453
+ }
454
+ mem = sampler.last_stats
455
+ combined = f"{stdout or ''}\n{stderr or ''}"
456
+
457
+ if proc.returncode != 0:
458
+ auth_err = auth_failure_reason(combined)
459
+ detail = auth_err or _cli_error_summary(stderr, stdout)
460
+ err_msg = detail or f"{cli} exit {proc.returncode}"
461
+ _log_memory(log_file, mem)
462
+ return {
463
+ "output_answer": _strip_ansi(stderr or stdout or "")[:2000],
464
+ "agent_status": "error",
465
+ "agent_error": err_msg,
466
+ "agent_backend": backend,
467
+ "agent_host": HOST_DEVECO,
468
+ "agent_memory_mb": mem,
469
+ }
470
+
471
+ _log_line(log_file, "first token")
472
+ parsed = parse_opencode_json_events(stdout if stdout.strip() else stderr)
473
+ tokens = (parsed.get("agent_usage") or {}).get("total_tokens") or 0
474
+ if tokens:
475
+ _log_line(log_file, f"totalTokenCount = {tokens}")
476
+ _log_memory(log_file, mem)
477
+
478
+ duration_ms = parsed.get("agent_duration_ms") or int((time.time() - t0) * 1000)
479
+ return {
480
+ **parsed,
481
+ "agent_duration_ms": duration_ms,
482
+ "agent_backend": backend,
483
+ "agent_host": HOST_DEVECO,
484
+ "ab_arm": "with-homegraph" if arm == "with" else "without-homegraph",
485
+ "agent_memory_mb": mem,
486
+ }
487
+
488
+
489
+ def run_external_dataset(
490
+ host: str,
491
+ repo: Path,
492
+ dataset: list[dict[str, Any]],
493
+ *,
494
+ arm: str,
495
+ output: Path,
496
+ log_file: Path | None,
497
+ hg_bin: str,
498
+ model: str | None = None,
499
+ ) -> list[dict[str, Any]]:
500
+ if host not in SUPPORTED_HOSTS:
501
+ raise ValueError(f"unknown agent host: {host}")
502
+
503
+ from agent_runner import find_homegraph_bin, print_agent_progress, require_index, _arm_short
504
+
505
+ if arm == "with":
506
+ require_index(repo)
507
+ hg = find_homegraph_bin(hg_bin) if arm == "with" else ""
508
+
509
+ output.parent.mkdir(parents=True, exist_ok=True)
510
+ if log_file:
511
+ log_file.write_text("", encoding="utf-8")
512
+
513
+ results: list[dict[str, Any]] = []
514
+ total = len(dataset)
515
+ print(f" → [{host}] {_arm_short(arm)} 臂:共 {total} 题", flush=True)
516
+
517
+ auth_abort: str | None = None
518
+ with output.open("w", encoding="utf-8") as f:
519
+ for i, item in enumerate(dataset, 1):
520
+ if auth_abort:
521
+ meta = {
522
+ "output_answer": "",
523
+ "agent_status": "error",
524
+ "agent_error": auth_abort,
525
+ "agent_backend": f"{host}-{arm}",
526
+ "agent_host": host,
527
+ }
528
+ print_agent_progress(arm, i, total, str(item.get("id") or i), f"跳过: {auth_abort[:80]}")
529
+ row = {**item, **meta}
530
+ results.append(row)
531
+ f.write(json.dumps(row, ensure_ascii=False) + "\n")
532
+ f.flush()
533
+ continue
534
+ q = str(item["query"])
535
+ item_id = str(item.get("id") or i)
536
+ print_agent_progress(arm, i, total, item_id, f"[{host}] 开始…")
537
+ logger.info("[%s/%s] %s/%s %s", host, arm, i, total, item_id)
538
+ try:
539
+ common = dict(
540
+ arm=arm,
541
+ hg_bin=hg,
542
+ log_file=log_file,
543
+ task_id=i,
544
+ )
545
+ if host == HOST_CLAUDE:
546
+ meta = run_claude_query(repo, q, **common)
547
+ else:
548
+ meta = run_deveco_query(repo, q, model=model, **common)
549
+ except Exception as e:
550
+ logger.error("External agent failed %s: %s", item.get("id"), e)
551
+ meta = {
552
+ "output_answer": "",
553
+ "agent_status": "error",
554
+ "agent_error": str(e),
555
+ "agent_backend": f"{host}-{arm}",
556
+ "agent_host": host,
557
+ }
558
+ row = {**item, **meta}
559
+ results.append(row)
560
+ f.write(json.dumps(row, ensure_ascii=False) + "\n")
561
+ f.flush()
562
+ if meta.get("agent_status") == "success":
563
+ dur_ms = meta.get("agent_duration_ms")
564
+ dur_s = f"{dur_ms / 1000:.1f}s" if isinstance(dur_ms, (int, float)) else "?"
565
+ print_agent_progress(arm, i, total, item_id, f"[{host}] 完成 ({dur_s})")
566
+ else:
567
+ err = str(meta.get("agent_error") or meta.get("agent_status") or "error")
568
+ print_agent_progress(arm, i, total, item_id, f"[{host}] 失败: {err[:100]}")
569
+ if i == 1 and meta.get("agent_error") and (
570
+ "未登录" in str(meta["agent_error"])
571
+ or "DevEco Code" in str(meta["agent_error"])
572
+ ):
573
+ auth_abort = str(meta["agent_error"])
574
+ print(f"\n ✗ {auth_abort} — 后续题目跳过", flush=True)
575
+ ok = sum(1 for r in results if r.get("agent_status") == "success")
576
+ print(f" → [{host}] {_arm_short(arm)} 臂结束:{ok}/{total} 成功", flush=True)
577
+ return results
@@ -0,0 +1,92 @@
1
+ """LLM provider presets for qa_eval (DashScope Qwen / 智谱 Zhipu)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class LlmConfig:
11
+ provider: str
12
+ api_key: str
13
+ base_url: str
14
+ model: str
15
+ extra_body: dict | None
16
+
17
+
18
+ PROVIDER_DASHSCOPE = "dashscope"
19
+ PROVIDER_ZHIPU = "zhipu"
20
+
21
+ _PRESETS: dict[str, dict] = {
22
+ PROVIDER_DASHSCOPE: {
23
+ "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
24
+ "model": "qwen3-235b-a22b-instruct-2507",
25
+ "api_key_envs": ("DASHSCOPE_API_KEY", "OPENAI_API_KEY"),
26
+ "extra_body": {"enable_thinking": False},
27
+ },
28
+ PROVIDER_ZHIPU: {
29
+ "base_url": "https://open.bigmodel.cn/api/paas/v4/",
30
+ "model": "glm-4-flash",
31
+ "api_key_envs": ("ZHIPU_API_KEY", "ZAI_API_KEY", "OPENAI_API_KEY"),
32
+ "extra_body": None,
33
+ },
34
+ }
35
+
36
+
37
+ def _read_key(env_names: tuple[str, ...]) -> str | None:
38
+ for name in env_names:
39
+ val = os.environ.get(name, "").strip()
40
+ if val:
41
+ return val
42
+ return None
43
+
44
+
45
+ def detect_provider() -> str:
46
+ """Prefer explicit ZHIPU key over DashScope when both are set."""
47
+ if _read_key(_PRESETS[PROVIDER_ZHIPU]["api_key_envs"]):
48
+ return PROVIDER_ZHIPU
49
+ if _read_key(_PRESETS[PROVIDER_DASHSCOPE]["api_key_envs"]):
50
+ return PROVIDER_DASHSCOPE
51
+ return PROVIDER_DASHSCOPE
52
+
53
+
54
+ def resolve_llm_config(
55
+ *,
56
+ provider: str | None = None,
57
+ model: str | None = None,
58
+ base_url: str | None = None,
59
+ ) -> LlmConfig:
60
+ name = (provider or detect_provider()).strip().lower()
61
+ if name not in _PRESETS:
62
+ known = ", ".join(_PRESETS)
63
+ raise ValueError(f"未知 LLM provider: {provider!r},可选: {known}")
64
+
65
+ preset = _PRESETS[name]
66
+ api_key = _read_key(preset["api_key_envs"])
67
+ if not api_key:
68
+ env_hint = " / ".join(preset["api_key_envs"])
69
+ raise RuntimeError(
70
+ f"未找到 {name} 的 API Key。请设置环境变量: {env_hint}\n"
71
+ f" 智谱: export ZHIPU_API_KEY='your-id.your-secret'\n"
72
+ f" DashScope: export DASHSCOPE_API_KEY='sk-...'"
73
+ )
74
+
75
+ # Strip mistaken sk- prefix on Zhipu keys (id.secret format).
76
+ if name == PROVIDER_ZHIPU and api_key.startswith("sk-") and "." in api_key[3:]:
77
+ api_key = api_key[3:]
78
+
79
+ return LlmConfig(
80
+ provider=name,
81
+ api_key=api_key,
82
+ base_url=(base_url or preset["base_url"]).rstrip("/") + "/",
83
+ model=model or preset["model"],
84
+ extra_body=preset["extra_body"],
85
+ )
86
+
87
+
88
+ def provider_help() -> str:
89
+ return (
90
+ f"dashscope (默认 Qwen, { _PRESETS[PROVIDER_DASHSCOPE]['model']}) | "
91
+ f"zhipu (智谱, 默认 { _PRESETS[PROVIDER_ZHIPU]['model']})"
92
+ )