carthagent 0.0.0-stage → 1.0.7

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 (379) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +219 -2
  3. package/bin/ctg.mjs +736 -0
  4. package/docs/budgets.md +72 -0
  5. package/docs/compaction.md +34 -0
  6. package/docs/context-efficiency-plan.md +24 -0
  7. package/docs/context-efficiency-review.md +27 -0
  8. package/docs/context-efficiency.md +49 -0
  9. package/docs/deepseek-jev-benchmark-plan.md +70 -0
  10. package/docs/deepseek-jev-benchmark-repair-plan.md +20 -0
  11. package/docs/deepseek-jev-benchmark-restart.md +9 -0
  12. package/docs/deepseek-jev-benchmark-results.md +77 -0
  13. package/docs/deepseek-jev-benchmark-running.md +60 -0
  14. package/docs/deepseek-jev-benchmark-v3-results.md +41 -0
  15. package/docs/deepseek-jev-benchmark-v4-protocol.md +15 -0
  16. package/docs/deepseek-jev-benchmark-v4-results.md +119 -0
  17. package/docs/deepseek-jev-http400-diagnosis.md +45 -0
  18. package/docs/delivery-review-evidence.md +53 -0
  19. package/docs/delivery-timeout-repair-plan.md +51 -0
  20. package/docs/evaluation-readiness.md +31 -0
  21. package/docs/evaluation.md +70 -0
  22. package/docs/extending.md +354 -0
  23. package/docs/glm-pilot-execution.md +9 -0
  24. package/docs/glm-pilot-plan.md +15 -0
  25. package/docs/glm-pilot-results.md +71 -0
  26. package/docs/graph-context-optimization.md +15 -0
  27. package/docs/jev-repository-pilot-plan.md +25 -0
  28. package/docs/jev-repository-pilot-protocol.md +39 -0
  29. package/docs/jev-repository-pilot-restart.md +7 -0
  30. package/docs/jev-repository-pilot-results.md +79 -0
  31. package/docs/jev.md +32 -0
  32. package/docs/model-registry.md +25 -0
  33. package/docs/native-compatibility.md +117 -0
  34. package/docs/opencode-archive.md +9 -0
  35. package/docs/opencode-migration.md +115 -0
  36. package/docs/opencode-runtime.md +94 -0
  37. package/docs/opencode-workspace.md +31 -0
  38. package/docs/owned-runtime-workflow-registration.md +17 -0
  39. package/docs/plan-advisor-evaluation.md +38 -0
  40. package/docs/planning-startup-fix-plan.md +23 -0
  41. package/docs/planning-startup-fix-review.md +24 -0
  42. package/docs/planning.md +118 -0
  43. package/docs/production-merge-review.md +23 -0
  44. package/docs/quality.md +35 -0
  45. package/docs/repository-benchmark-design.md +111 -0
  46. package/docs/review-assurance.md +9 -0
  47. package/docs/review-experiment-results.md +59 -0
  48. package/docs/tested-planning-roadmap.md +58 -0
  49. package/docs/tested-planning.md +58 -0
  50. package/docs/tui-pickers.md +9 -0
  51. package/docs/webdriver.md +83 -0
  52. package/docs/workflow-modes.md +26 -0
  53. package/docs/workflow.d2 +17 -0
  54. package/docs/workflow.svg +149 -0
  55. package/docs/workspace-coordination.md +9 -0
  56. package/evaluate.mjs +249 -0
  57. package/extensions/codegraph.ts +36 -0
  58. package/extensions/delivery.ts +819 -0
  59. package/extensions/experiential.ts +21 -0
  60. package/extensions/subscriptions.ts +21 -0
  61. package/guidance/profiles/authenticated-web.json +24 -0
  62. package/guidance/profiles/data-visualization.json +25 -0
  63. package/guidance/profiles/database-change.json +24 -0
  64. package/guidance/profiles/external-api.json +24 -0
  65. package/guidance/profiles/transactional-data.json +25 -0
  66. package/guidance/profiles/web-application.json +26 -0
  67. package/lib/acceptance-review.mjs +4 -0
  68. package/lib/browser-bin.mjs +160 -0
  69. package/lib/browser-errors.mjs +54 -0
  70. package/lib/browser.mjs +219 -0
  71. package/lib/budget.mjs +37 -0
  72. package/lib/cdp.mjs +221 -0
  73. package/lib/cloud/client.mjs +275 -0
  74. package/lib/cloud/control-plane.mjs +350 -0
  75. package/lib/cloud/gateway.mjs +380 -0
  76. package/lib/cloud/http.mjs +135 -0
  77. package/lib/cloud/ledger.mjs +215 -0
  78. package/lib/codegraph-query.mjs +60 -0
  79. package/lib/codegraph.mjs +378 -0
  80. package/lib/context-archive.mjs +18 -0
  81. package/lib/delivery-context.mjs +132 -0
  82. package/lib/delivery-guidance.mjs +9 -0
  83. package/lib/delivery-review.mjs +94 -0
  84. package/lib/delivery.mjs +610 -0
  85. package/lib/engine.mjs +137 -0
  86. package/lib/guidance.mjs +71 -0
  87. package/lib/jev-advisor.mjs +47 -0
  88. package/lib/opencode/archive.mjs +122 -0
  89. package/lib/opencode/compatibility.mjs +19 -0
  90. package/lib/opencode/credentials.mjs +205 -0
  91. package/lib/opencode/evaluation-budget-plugin/index.ts +147 -0
  92. package/lib/opencode/evaluation.mjs +242 -0
  93. package/lib/opencode/file-lock.mjs +86 -0
  94. package/lib/opencode/headless.mjs +200 -0
  95. package/lib/opencode/review.mjs +213 -0
  96. package/lib/opencode/runtime.mjs +246 -0
  97. package/lib/opencode/service.mjs +249 -0
  98. package/lib/opencode/session-options.mjs +60 -0
  99. package/lib/opencode/workspace.mjs +133 -0
  100. package/lib/openrouter-jev.mjs +37 -0
  101. package/lib/outcome-contract.mjs +21 -0
  102. package/lib/plan-advisor.mjs +38 -0
  103. package/lib/planning-access.mjs +7 -0
  104. package/lib/planning-advice.mjs +28 -0
  105. package/lib/planning-discovery.mjs +14 -0
  106. package/lib/planning-guide.mjs +34 -0
  107. package/lib/planning-view.mjs +57 -0
  108. package/lib/planning.mjs +155 -0
  109. package/lib/probe-diagnostics.mjs +13 -0
  110. package/lib/providers/experiential.mjs +292 -0
  111. package/lib/providers/model-catalog.json +700 -0
  112. package/lib/providers/model-catalog.mjs +7 -0
  113. package/lib/providers/names.mjs +13 -0
  114. package/lib/providers/subscription-models.mjs +483 -0
  115. package/lib/reports.mjs +80 -0
  116. package/lib/review-state.mjs +49 -0
  117. package/lib/review.mjs +114 -0
  118. package/lib/scenarios.mjs +99 -0
  119. package/lib/ship-auth.mjs +44 -0
  120. package/lib/ship-jev.mjs +59 -0
  121. package/lib/ship-telemetry.mjs +183 -0
  122. package/lib/tui/ansi.mjs +255 -0
  123. package/lib/tui/app.mjs +2445 -0
  124. package/lib/tui/auth.mjs +249 -0
  125. package/lib/tui/carthage.mjs +109 -0
  126. package/lib/tui/clipboard.mjs +122 -0
  127. package/lib/tui/d2.mjs +431 -0
  128. package/lib/tui/feed.mjs +467 -0
  129. package/lib/tui/framing.mjs +80 -0
  130. package/lib/tui/glyphs.mjs +172 -0
  131. package/lib/tui/hash-worker.mjs +11 -0
  132. package/lib/tui/mock-engine.mjs +222 -0
  133. package/lib/tui/models.mjs +73 -0
  134. package/lib/tui/picker.mjs +18 -0
  135. package/lib/tui/theme.mjs +663 -0
  136. package/lib/tui/title.mjs +7 -0
  137. package/lib/update-channel.mjs +62 -0
  138. package/lib/update.mjs +230 -0
  139. package/lib/webdriver.mjs +82 -0
  140. package/lib/workflow.mjs +88 -0
  141. package/lib/workspace.mjs +39 -0
  142. package/metadata.json +8 -0
  143. package/package.json +94 -4
  144. package/plugins/carthagent/index.ts +3 -0
  145. package/plugins/carthagent/package.json +28 -0
  146. package/plugins/carthagent/src/archive-rpc.ts +46 -0
  147. package/plugins/carthagent/src/bootstrap.ts +38 -0
  148. package/plugins/carthagent/src/index.ts +62 -0
  149. package/plugins/carthagent/src/pull-request-rpc.ts +54 -0
  150. package/plugins/carthagent/src/rpc.ts +76 -0
  151. package/plugins/carthagent/src/server/archive-rpc.ts +327 -0
  152. package/plugins/carthagent/src/server/budgets.ts +21 -0
  153. package/plugins/carthagent/src/server/codegraph.ts +46 -0
  154. package/plugins/carthagent/src/server/delivery.ts +488 -0
  155. package/plugins/carthagent/src/server/device-auth.ts +91 -0
  156. package/plugins/carthagent/src/server/native-delivery.ts +26 -0
  157. package/plugins/carthagent/src/server/orchestration.ts +56 -0
  158. package/plugins/carthagent/src/server/policy.ts +24 -0
  159. package/plugins/carthagent/src/server/providers.ts +424 -0
  160. package/plugins/carthagent/src/server/pull-request-rpc.ts +387 -0
  161. package/plugins/carthagent/src/server/session-controls.ts +82 -0
  162. package/plugins/carthagent/src/server/telemetry.ts +92 -0
  163. package/plugins/carthagent/src/server/workspace-rpc.ts +349 -0
  164. package/plugins/carthagent/src/session-rpc.ts +31 -0
  165. package/plugins/carthagent/src/testing.ts +85 -0
  166. package/plugins/carthagent/src/workspace-rpc.ts +133 -0
  167. package/plugins/carthagent/test-plugin/index.ts +4 -0
  168. package/plugins/carthagent/test-plugin/package.json +1 -0
  169. package/plugins/carthagent/tsconfig.json +19 -0
  170. package/plugins/carthagent/tui.ts +1 -0
  171. package/prompts/delivery.md +7 -0
  172. package/public/index.html +983 -0
  173. package/scenarios/cli/grade_cli.py +69 -0
  174. package/scenarios/cli/scenario.json +21 -0
  175. package/scenarios/game/baseline/minecraft.py +193 -0
  176. package/scenarios/game/context.md +1 -0
  177. package/scenarios/game/grade_minecraft_clone.py +603 -0
  178. package/scenarios/game/requirements.txt +2 -0
  179. package/scenarios/game/scenario.json +33 -0
  180. package/server.mjs +341 -0
  181. package/skills/game-development/SKILL.md +36 -0
  182. package/skills/game-development/assets/ursina_starter.py +98 -0
  183. package/skills/game-development/scripts/verify_ursina.py +80 -0
  184. package/skills/software-delivery/SKILL.md +50 -0
  185. package/skills/typescript-delivery/SKILL.md +69 -0
  186. package/tests/acceptance-review.test.mjs +13 -0
  187. package/tests/auth.test.mjs +234 -0
  188. package/tests/bounded-summary.test.mjs +68 -0
  189. package/tests/browser-bin.test.mjs +91 -0
  190. package/tests/browser.test.mjs +96 -0
  191. package/tests/budget-engine.test.mjs +110 -0
  192. package/tests/budget.test.mjs +23 -0
  193. package/tests/cli.test.mjs +245 -0
  194. package/tests/clipboard.test.mjs +79 -0
  195. package/tests/cloud-client.test.mjs +129 -0
  196. package/tests/cloud-control-plane.test.mjs +144 -0
  197. package/tests/cloud-gateway.test.mjs +147 -0
  198. package/tests/cloud-http.test.mjs +102 -0
  199. package/tests/cloud-ledger.test.mjs +75 -0
  200. package/tests/codegraph-query.test.mjs +66 -0
  201. package/tests/codegraph.test.mjs +142 -0
  202. package/tests/delivery-context.test.mjs +66 -0
  203. package/tests/delivery-review.test.mjs +139 -0
  204. package/tests/delivery.test.mjs +343 -0
  205. package/tests/engine-api.mjs +24 -0
  206. package/tests/experiential-extension.test.mjs +37 -0
  207. package/tests/experiential.test.mjs +300 -0
  208. package/tests/extension.test.mjs +921 -0
  209. package/tests/fixtures/archive-plugin/index.ts +10 -0
  210. package/tests/guidance.test.mjs +78 -0
  211. package/tests/model-registry.test.mjs +68 -0
  212. package/tests/models.test.mjs +70 -0
  213. package/tests/openai-idempotency.test.mjs +99 -0
  214. package/tests/opencode-archive-rpc.test.mjs +82 -0
  215. package/tests/opencode-delivery.test.mjs +231 -0
  216. package/tests/opencode-providers.test.mjs +414 -0
  217. package/tests/opencode-runtime.test.mjs +455 -0
  218. package/tests/opencode-session-controls.test.mjs +115 -0
  219. package/tests/opencode-session-options.test.mjs +26 -0
  220. package/tests/opencode-workspace.test.mjs +339 -0
  221. package/tests/outcome-contract.test.mjs +17 -0
  222. package/tests/picker.test.mjs +34 -0
  223. package/tests/plan-advisor.test.mjs +88 -0
  224. package/tests/plan-review-loop.test.mjs +53 -0
  225. package/tests/planning-advice.test.mjs +27 -0
  226. package/tests/planning-contract.test.mjs +65 -0
  227. package/tests/planning-startup.test.mjs +38 -0
  228. package/tests/planning-view.test.mjs +33 -0
  229. package/tests/planning.test.mjs +223 -0
  230. package/tests/probe-diagnostics.test.mjs +21 -0
  231. package/tests/pull-request-rpc.test.mjs +366 -0
  232. package/tests/review-state.test.mjs +32 -0
  233. package/tests/review.test.mjs +127 -0
  234. package/tests/server.test.mjs +252 -0
  235. package/tests/ship-jev.test.mjs +77 -0
  236. package/tests/ship-telemetry.test.mjs +333 -0
  237. package/tests/standard-workflow.test.mjs +28 -0
  238. package/tests/subscription-models.test.mjs +416 -0
  239. package/tests/subscriptions-extension.test.mjs +27 -0
  240. package/tests/terminal-title.test.mjs +13 -0
  241. package/tests/test_grade_minecraft_clone.py +199 -0
  242. package/tests/test_starter.py +78 -0
  243. package/tests/theme.test.mjs +175 -0
  244. package/tests/tui-copy-paste.test.mjs +54 -0
  245. package/tests/tui.test.mjs +808 -0
  246. package/tests/update-channels.test.mjs +129 -0
  247. package/tests/update.test.mjs +175 -0
  248. package/tests/webdriver.test.mjs +31 -0
  249. package/tests/workflow.test.mjs +47 -0
  250. package/tests/workspace-rpc.test.mjs +236 -0
  251. package/tests/workspace.test.mjs +24 -0
  252. package/tools/benchmark/advisor.test.mjs +7 -0
  253. package/tools/benchmark/advisor.ts +27 -0
  254. package/tools/benchmark/campaign.mjs +45 -0
  255. package/tools/benchmark/diagnose-request.mjs +40 -0
  256. package/tools/benchmark/eval-common.mjs +32 -0
  257. package/tools/benchmark/eval-ledger.mjs +22 -0
  258. package/tools/benchmark/eval-shop.mjs +26 -0
  259. package/tools/benchmark/eval-voxel.mjs +20 -0
  260. package/tools/benchmark/evaluate.mjs +24 -0
  261. package/tools/benchmark/evaluators.test.mjs +13 -0
  262. package/tools/benchmark/fixtures/shop/server.mjs +18 -0
  263. package/tools/benchmark/fixtures/shop/ui.html +8 -0
  264. package/tools/benchmark/freeze.mjs +30 -0
  265. package/tools/benchmark/gateway.mjs +91 -0
  266. package/tools/benchmark/gateway.test.mjs +38 -0
  267. package/tools/benchmark/generator.mjs +11 -0
  268. package/tools/benchmark/generator.test.mjs +26 -0
  269. package/tools/benchmark/guard.test.mjs +133 -0
  270. package/tools/benchmark/guard.ts +136 -0
  271. package/tools/benchmark/native-plugin/index.ts +24 -0
  272. package/tools/benchmark/planning-startup.test.mjs +47 -0
  273. package/tools/benchmark/preflight-live.mjs +31 -0
  274. package/tools/benchmark/preflight.mjs +22 -0
  275. package/tools/benchmark/replay-diagnostic.mjs +23 -0
  276. package/tools/benchmark/runner.mjs +141 -0
  277. package/tools/benchmark/runner.test.mjs +93 -0
  278. package/tools/benchmark/runtime.mjs +23 -0
  279. package/tools/benchmark/selftest.mjs +47 -0
  280. package/tools/benchmark/smoke-jev.mjs +5 -0
  281. package/tools/benchmark/tasks.mjs +20 -0
  282. package/tools/benchmark/transport.mjs +65 -0
  283. package/tools/benchmark/transport.test.mjs +44 -0
  284. package/tools/benchmark/upstream-error.mjs +18 -0
  285. package/tools/benchmark/upstream-error.test.mjs +10 -0
  286. package/tools/browser-check.mjs +109 -0
  287. package/tools/check-syntax.mjs +27 -0
  288. package/tools/evaluate-plan-advisor.mjs +20 -0
  289. package/tools/native-fixture.mjs +194 -0
  290. package/tools/native-pty-smoke.py +108 -0
  291. package/tools/native-report-plugin/index.ts +50 -0
  292. package/tools/native-report-plugin/package.json +7 -0
  293. package/tools/patch-compaction.mjs +24 -0
  294. package/tools/patch-model-catalog.mjs +12 -0
  295. package/tools/patch-print-shutdown.mjs +39 -0
  296. package/tools/repo-pilot/evaluator.cjs +32 -0
  297. package/tools/repo-pilot/reference.mjs +43 -0
  298. package/tools/repo-pilot/runner.mjs +85 -0
  299. package/tools/repo-pilot/sandbox-entry.mjs +12 -0
  300. package/tools/repo-pilot/tasks.mjs +20 -0
  301. package/tools/repo-pilot/type-evaluator.cjs +6 -0
  302. package/tools/test-native-controls.mjs +84 -0
  303. package/tools/test-native-delivery-host.mjs +307 -0
  304. package/tools/test-native-evaluation.mjs +46 -0
  305. package/tools/test-native-host.mjs +385 -0
  306. package/tools/test-native-outcomes.mjs +73 -0
  307. package/tools/test-native-package.mjs +70 -0
  308. package/tools/test-native-pty.mjs +20 -0
  309. package/tools/test-native-report.mjs +88 -0
  310. package/tools/test-native-swe-host.mjs +132 -0
  311. package/tools/test-native-swe.mjs +122 -0
  312. package/tools/test-picker-pty.py +45 -0
  313. package/tools/test-python.mjs +29 -0
  314. package/tools/test-webdriver.mjs +54 -0
  315. package/tools/verify-release-version.mjs +37 -0
  316. package/tools/webdriver-check.mjs +52 -0
  317. package/vendor/agent/bounded-summary.mjs +88 -0
  318. package/vendor/agent/chunks/anthropic-messages-VWZZOSJQ.js +6 -0
  319. package/vendor/agent/chunks/anthropic.js +75 -0
  320. package/vendor/agent/chunks/azure-openai-responses-BJLAM6CD.js +2 -0
  321. package/vendor/agent/chunks/bedrock-converse-stream.js +74 -0
  322. package/vendor/agent/chunks/chord-context.js +96 -0
  323. package/vendor/agent/chunks/chunk-4L3WN2XY.js +2 -0
  324. package/vendor/agent/chunks/chunk-6CYCI3EJ.js +12 -0
  325. package/vendor/agent/chunks/chunk-6R7UI4IY.js +2 -0
  326. package/vendor/agent/chunks/chunk-7ZC4UXSL.js +2 -0
  327. package/vendor/agent/chunks/chunk-AXIIZGTV.js +2 -0
  328. package/vendor/agent/chunks/chunk-CLNPYIDP.js +42 -0
  329. package/vendor/agent/chunks/chunk-DRQDKNXA.js +2 -0
  330. package/vendor/agent/chunks/chunk-GMWCTUPB.js +2 -0
  331. package/vendor/agent/chunks/chunk-IDDQWTHI.js +2 -0
  332. package/vendor/agent/chunks/chunk-JVUZSMYM.js +1548 -0
  333. package/vendor/agent/chunks/chunk-L2ZPNLYD.js +2 -0
  334. package/vendor/agent/chunks/chunk-NUHFSC37.js +25 -0
  335. package/vendor/agent/chunks/chunk-OUXLLA64.js +11 -0
  336. package/vendor/agent/chunks/chunk-PGXDEOVV.js +2 -0
  337. package/vendor/agent/chunks/chunk-PJACZCIR.js +2 -0
  338. package/vendor/agent/chunks/chunk-PRRNXB7S.js +2 -0
  339. package/vendor/agent/chunks/chunk-TEPMHNKQ.js +2 -0
  340. package/vendor/agent/chunks/chunk-TRDNDS6A.js +4 -0
  341. package/vendor/agent/chunks/chunk-U6ZMQKGD.js +50 -0
  342. package/vendor/agent/chunks/chunk-UAQELI3K.js +2 -0
  343. package/vendor/agent/chunks/chunk-USJZMAIR.js +2 -0
  344. package/vendor/agent/chunks/chunk-XDUWOHPL.js +6 -0
  345. package/vendor/agent/chunks/chunk-XNGRGP62.js +2 -0
  346. package/vendor/agent/chunks/github-copilot.js +2 -0
  347. package/vendor/agent/chunks/google-generative-ai-Q2K7FBDI.js +2 -0
  348. package/vendor/agent/chunks/google-vertex-7UJBF4S7.js +2 -0
  349. package/vendor/agent/chunks/https-proxy-agent-2VXB7436.js +13 -0
  350. package/vendor/agent/chunks/image-resize-worker.js +2 -0
  351. package/vendor/agent/chunks/kimi-coding.js +2 -0
  352. package/vendor/agent/chunks/lib-HGQMNYVT.js +9 -0
  353. package/vendor/agent/chunks/mistral-conversations-4M6BZBX6.js +5 -0
  354. package/vendor/agent/chunks/multipart-parser-SQ6GDJL7.js +3 -0
  355. package/vendor/agent/chunks/node-KG362ECQ.js +16 -0
  356. package/vendor/agent/chunks/node-TWVVAGPO.js +2 -0
  357. package/vendor/agent/chunks/openai-codex-responses-R6VYZTVY.js +10 -0
  358. package/vendor/agent/chunks/openai-codex.js +75 -0
  359. package/vendor/agent/chunks/openai-completions-EKZT2IH2.js +7 -0
  360. package/vendor/agent/chunks/openai-responses-TFDINO6W.js +2 -0
  361. package/vendor/agent/chunks/openrouter-images-N4XVVFSD.js +2 -0
  362. package/vendor/agent/chunks/openrouter.js +75 -0
  363. package/vendor/agent/chunks/pi-messages-PML4SPVJ.js +8 -0
  364. package/vendor/agent/chunks/radius.js +75 -0
  365. package/vendor/agent/chunks/src-UK77SYZE.js +4 -0
  366. package/vendor/agent/chunks/xai.js +2 -0
  367. package/vendor/agent/cli.js +3 -0
  368. package/vendor/agent/dist/modes/interactive/theme/dark.json +91 -0
  369. package/vendor/agent/dist/modes/interactive/theme/light.json +90 -0
  370. package/vendor/agent/dist/modes/interactive/theme/theme-schema.json +356 -0
  371. package/vendor/agent/index.js +2 -0
  372. package/vendor/agent/modes/interactive/theme/dark.json +91 -0
  373. package/vendor/agent/modes/interactive/theme/light.json +90 -0
  374. package/vendor/agent/modes/interactive/theme/theme-schema.json +356 -0
  375. package/vendor/agent/package.json +14 -0
  376. package/vendor/agent/rpc-entry.js +3 -0
  377. package/vendor/agent/theme/dark.json +91 -0
  378. package/vendor/agent/theme/light.json +90 -0
  379. package/vendor/agent/theme/theme-schema.json +356 -0
@@ -0,0 +1,83 @@
1
+ # Real-browser checks
2
+
3
+ `tools/webdriver-check.mjs` runs checks in a real browser. By default it uses
4
+ the bundled `chrome-headless-shell` build — a lightweight headless-only
5
+ browser downloaded on first use into `~/.carthagent/browser/` (pinned version,
6
+ `CARTHAGENT_BROWSER_VERSION` to override, `CARTHAGENT_BROWSER` to supply your
7
+ own binary; system Chrome/Chromium is used when present). The bundled browser
8
+ is driven directly over the DevTools protocol, so no driver binary or npm
9
+ dependency is required.
10
+
11
+ ```sh
12
+ node tools/webdriver-check.mjs \
13
+ --root ./my-app --page index.html \
14
+ --assert-text '#status:ready' --click '#submit' \
15
+ --assert-text '#status:saved' --screenshot artifacts/browser.png
16
+ ```
17
+
18
+ To drive an external W3C WebDriver service instead (e.g. geckodriver or
19
+ chromedriver you manage), pass `--endpoint`:
20
+
21
+ ```sh
22
+ geckodriver --host 127.0.0.1 --port 4444 &
23
+ node tools/webdriver-check.mjs --endpoint http://127.0.0.1:4444 \
24
+ --root ./my-app --page index.html --assert-text '#status:ready'
25
+ ```
26
+
27
+ Options are processed in order. `--assert` checks element existence;
28
+ `--assert-text` checks visible text contains the supplied string; `--click`
29
+ dispatches a click on the element after it exists. Screenshots use the same
30
+ session after all steps. Exit 1 means failure, including unavailable browser
31
+ or unsupported options. Requests and navigation have deadlines. The spawned
32
+ browser is terminated on success and failure.
33
+
34
+ For an already running Vite/Next.js or other application server, use
35
+ `--url http://127.0.0.1:5173` instead of `--root`/`--page`. Start and stop that
36
+ server separately. Only navigate to applications you trust: browser checks
37
+ execute their scripts and clicks can change application data.
38
+
39
+ Element existence and text assertions poll for up to `--wait-ms 5000` per step
40
+ (default 5000; integer range 1–60000). Invalid selectors and driver errors fail
41
+ immediately. Clicks wait for element existence but are executed exactly once;
42
+ interaction errors are not retried. Page navigation has a separate 15-second
43
+ timeout. This is a per-step bound, not a whole-run budget.
44
+
45
+ Failures are JSON with `category` (configuration, launch, driver, capture,
46
+ navigation, assertion, interaction, browser-errors, screenshot, or cleanup),
47
+ `error`, and where applicable zero-based `stepIndex`, `selector`, and
48
+ `expected`. Use this evidence to fix the relevant
49
+ step rather than blindly repeating a mutating interaction.
50
+ Use meaningful application-state assertions: element existence alone does not
51
+ prove that a module ran. Console-error capture is automatic and mandatory —
52
+ there is no `--console-clean` flag; it is rejected rather than silently
53
+ ignored. Screenshot success is not visual
54
+ review. The existing jsdom tool remains useful for classic-script DOM checks,
55
+ but rejects unsupported module execution.
56
+
57
+ Run the optional actual-browser regression from the repository root:
58
+
59
+ ```sh
60
+ node tools/test-webdriver.mjs
61
+ ```
62
+
63
+ It provisions the bundled browser (first run downloads ~100MB into
64
+ `~/.carthagent/browser/`), exercises a module application and broken-module
65
+ failure, and writes `artifacts/webdriver.png`.
66
+ Default `npm test` tests CLI failures without requiring a browser.
67
+
68
+ ### Mandatory browser-error evidence
69
+
70
+ Browser error capture subscribes before navigation (DevTools `Runtime` and
71
+ `Log` domains; WebDriver endpoints use BiDi `log.entryAdded`), capturing
72
+ startup console errors, uncaught exceptions and unhandled promise rejections.
73
+ Unexpected error-level entries fail the check even when DOM assertions pass.
74
+ Capture-channel failure fails closed; there is no silent DOM-only fallback. Diagnostics accompany assertion failures too and retain at most 50
75
+ entries, 2,000 text characters and eight bounded stack frames per entry; dropped
76
+ entries are counted. No broad ignore option is provided. Expected-error tests
77
+ must currently use a separate explicit validator rather than suppressing logs.
78
+
79
+ Coverage ends after the declared interactions/assertions and a BiDi round trip;
80
+ this is not a promise to detect errors occurring arbitrarily later. Declare a
81
+ readiness assertion for asynchronous work. Network failures are not classified
82
+ as application errors unless the browser emits an error-level log. Warnings,
83
+ visual correctness and all network traffic are not covered by this signal.
@@ -0,0 +1,26 @@
1
+ # Workflow modes and evidence
2
+
3
+ The ctg CLI defaults to `--workflow standard`. A valid required acceptance contract enables implementation without mandatory component/port/scenario review. Final verification remains strict: fresh check identities, new requirement executions, artifact/output existence, structured step completion and a snapshot-bound final review are required. Step `done` means implementation complete, not passing integrated checks.
4
+
5
+ `--workflow strict` keeps tested-design approval before implementation. Direct extension hosts default to strict; restored runs retain their recorded workflowMode. Neither mode is an OS sandbox. Informational contracts cannot authorize implementation.
6
+
7
+ `delivery_probe` exposes only Node/npm version argv with a ten-second bound; it is discovery, not check evidence. Unknown custom tools remain fail-closed.
8
+
9
+ Partial revisions merge omitted design/workflow object fields; supplied arrays replace their respective arrays. Strict revisions still relock approval. Standard revisions continue implementation but invalidate affected evidence and final review. Validation observes the current plan and records findings without clearing approval; receipt identity still detects changed plans/source.
10
+
11
+ Graph navigation is permitted during discovery, returns pagination/coverage and supports file/container filtering. Absolute workspace file paths normalize; directory filters return actionable errors. Name-based call matches remain candidates rather than runtime resolution.
12
+
13
+ ## Four-arm local pilot
14
+
15
+ One natural-user trade-validation task, identical bundled engine/model/dependencies, external 900-second/120-tool ceilings, 47 frozen checks:
16
+
17
+ | Arm | Elapsed | Recorded USD | External checks | Terminal outcome |
18
+ |---|---:|---:|---:|---|
19
+ | Plain Pi | 247.4 s | 0.033085 | 47/47 | Normal final answer |
20
+ | Pi + graph | 535.7 s | 0.066948 | 47/47 | Provider failure; no final answer |
21
+ | ctg standard | 509.5 s | 0.097460 | 47/47 | Verified handoff |
22
+ | ctg strict | 900.3 s | 0.108461 | 12/47 | Timeout before product implementation |
23
+
24
+ This is a single-task pilot, not a statistically significant cost/completion claim. Providers varied between requests; interrupted requests have unreconciled usage, so affected totals are lower bounds. Standard completed where strict did not, but plain Pi was faster and cheaper than standard. Graph benefit was not established. The evaluator's 12 strict passes are preserved pre-existing behavior/scope, not delivery completion. Candidate documented test commands are audited separately from external behavior.
25
+
26
+ Local task, source freezes, events, usage receipts and reports reside in `/home/baissi/ctg_test/`; these paths describe this development machine, not distributed benchmark artifacts. Future evaluation should randomize repeated tasks, retain plain Pi, classify provider errors separately and report user task completion separately from internal handoff completion.
@@ -0,0 +1,17 @@
1
+ direction: right
2
+ inspect: "Inspect repository + installed APIs\nIdentify relevant knowledge bases/skills"
3
+ plan: "Acceptance contract + plan\nScope, risks, concrete checks"
4
+ pressure: "Pressure-test plan\nVerify checks detect failure"
5
+ slice: "Runnable vertical slice"
6
+ build: "Implement features + regression tests\nMark progress per step"
7
+ verify: "Run checks against source fingerprint\nRecord exit status + output tail"
8
+ review: "Adversarial review\nInteraction + visual + failure paths"
9
+ repair: "Repair failures (bounded)\nFailing check + quoted output tail"
10
+ adjust: "Adjust plan on wrong assumptions\nUpdate steps/checks to match reality"
11
+ deliver: "Evidence + launch instructions\nExplicit limitations"
12
+ inspect -> plan -> pressure -> slice -> build -> verify -> review -> deliver
13
+ verify -> repair: failed / stale
14
+ repair -> verify
15
+ build -> adjust: wrong assumption
16
+ adjust -> plan: revise contract
17
+ review -> repair: defect
@@ -0,0 +1,149 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3660 460" width="3660" height="460" preserveAspectRatio="xMinYMin meet">
3
+ <defs>
4
+ <style type="text/css"><![CDATA[
5
+ .node-text-bold { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-weight: 700; font-size: 15px; fill: #0A0F25; }
6
+ .node-text-sub { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-weight: 500; font-size: 13.5px; fill: #4B5563; }
7
+ .edge-label { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-style: italic; font-size: 14px; fill: #475569; }
8
+ .edge-label-success { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-weight: 600; font-size: 13.5px; fill: #047857; }
9
+ .edge-label-critique { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-weight: 600; font-size: 13.5px; fill: #6366F1; }
10
+ .arrow { stroke: #0D32B2; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
11
+ .arrow-critique { stroke: #4F46E5; stroke-width: 2.2; fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 4,3; }
12
+ .arrow-success { stroke: #059669; stroke-width: 2.2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
13
+ .arrow-repair { stroke: #D97706; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
14
+ ]]></style>
15
+
16
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="8" refY="5" viewBox="0 0 10 10" orient="auto" markerUnits="userSpaceOnUse">
17
+ <polygon points="0,1.5 8,5 0,8.5" fill="#0D32B2" />
18
+ </marker>
19
+ <marker id="arrowhead-critique" markerWidth="10" markerHeight="10" refX="8" refY="5" viewBox="0 0 10 10" orient="auto" markerUnits="userSpaceOnUse">
20
+ <polygon points="0,1.5 8,5 0,8.5" fill="#4F46E5" />
21
+ </marker>
22
+ <marker id="arrowhead-success" markerWidth="10" markerHeight="10" refX="8" refY="5" viewBox="0 0 10 10" orient="auto" markerUnits="userSpaceOnUse">
23
+ <polygon points="0,1.5 8,5 0,8.5" fill="#059669" />
24
+ </marker>
25
+ <marker id="arrowhead-repair" markerWidth="10" markerHeight="10" refX="8" refY="5" viewBox="0 0 10 10" orient="auto" markerUnits="userSpaceOnUse">
26
+ <polygon points="0,1.5 8,5 0,8.5" fill="#D97706" />
27
+ </marker>
28
+ </defs>
29
+
30
+ <rect x="0" y="0" width="3660" height="460" fill="#FFFFFF" rx="8" />
31
+
32
+ <!-- 1. INSPECT NODE -->
33
+ <g id="node-inspect">
34
+ <rect x="20" y="175" width="270" height="70" rx="8" stroke="#0D32B2" stroke-width="2" fill="#F7F8FE" />
35
+ <text x="155" y="216" text-anchor="middle" class="node-text-bold">Inspect repository + installed APIs</text>
36
+ </g>
37
+
38
+ <!-- inspect -> contract -->
39
+ <path d="M 290 210 L 350 210" class="arrow" marker-end="url(#arrowhead)" />
40
+
41
+ <!-- 2. CONTRACT DRAFT NODE -->
42
+ <g id="node-contract">
43
+ <rect x="350" y="167" width="270" height="86" rx="8" stroke="#0D32B2" stroke-width="2" fill="#F7F8FE" />
44
+ <text x="485" y="205" text-anchor="middle" class="node-text-bold">Acceptance contract draft</text>
45
+ <text x="485" y="228" text-anchor="middle" class="node-text-sub">Scope, risks, concrete checks</text>
46
+ </g>
47
+
48
+ <!-- contract -> review_plan -->
49
+ <path d="M 620 210 L 685 210" class="arrow" marker-end="url(#arrowhead)" />
50
+
51
+ <!-- 3. REVIEW_PLAN (CRITIQUE LOOP) NODE -->
52
+ <g id="node-review_plan">
53
+ <rect x="690" y="156" width="340" height="106" rx="10" stroke="#4F46E5" stroke-width="2.5" fill="#EEF2FF" />
54
+ <rect x="702" y="166" width="94" height="20" rx="10" fill="#E0E7FF" />
55
+ <text x="749" y="180" text-anchor="middle" font-size="11" font-weight="700" fill="#4338CA">CRITIQUE LOOP</text>
56
+ <text x="860" y="205" text-anchor="middle" class="node-text-bold" fill="#1E1B4B">Plan &amp; architecture critique loop</text>
57
+ <text x="860" y="227" text-anchor="middle" class="node-text-sub" fill="#3730A3">Confidence &#x2265; 8/10 &amp; failure modes</text>
58
+ <text x="860" y="247" text-anchor="middle" class="node-text-sub" fill="#4338CA">Bounded iterations (&#x2265; 2 rounds)</text>
59
+ </g>
60
+
61
+ <!-- review_plan -> review_plan SELF LOOP (Low confidence / unhandled risks) -->
62
+ <path d="M 770 156 C 770 95, 950 95, 950 152" class="arrow-critique" marker-end="url(#arrowhead-critique)" />
63
+ <rect x="745" y="70" width="230" height="22" rx="4" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="1" />
64
+ <text x="860" y="85" text-anchor="middle" class="edge-label-critique">Low confidence / unhandled risks</text>
65
+
66
+ <!-- review_plan -> plan (High confidence & approved) -->
67
+ <path d="M 1030 210 L 1315 210" class="arrow-success" marker-end="url(#arrowhead-success)" />
68
+ <rect x="1055" y="196" width="235" height="24" rx="4" fill="#ECFDF5" stroke="#A7F3D0" stroke-width="1" />
69
+ <text x="1172" y="213" text-anchor="middle" class="edge-label-success">High confidence (&#x2265; 8/10) &amp; approved</text>
70
+
71
+ <!-- 4. PLAN (LOCKED) NODE -->
72
+ <g id="node-plan">
73
+ <rect x="1320" y="167" width="250" height="86" rx="8" stroke="#0D32B2" stroke-width="2" fill="#F7F8FE" />
74
+ <text x="1445" y="205" text-anchor="middle" class="node-text-bold">D2 implementation plan (locked)</text>
75
+ <text x="1445" y="228" text-anchor="middle" class="node-text-sub">Frozen scope, bound checks</text>
76
+ </g>
77
+
78
+ <!-- plan -> slice -->
79
+ <path d="M 1570 210 L 1635 210" class="arrow" marker-end="url(#arrowhead)" />
80
+
81
+ <!-- 5. RUNNABLE VERTICAL SLICE NODE -->
82
+ <g id="node-slice">
83
+ <rect x="1640" y="167" width="280" height="86" rx="8" stroke="#0D32B2" stroke-width="2" fill="#F7F8FE" />
84
+ <text x="1780" y="205" text-anchor="middle" class="node-text-bold">Runnable vertical slice</text>
85
+ <text x="1780" y="228" text-anchor="middle" class="node-text-sub">Rendering verified first for games</text>
86
+ </g>
87
+
88
+ <!-- slice -> build -->
89
+ <path d="M 1920 210 L 1985 210" class="arrow" marker-end="url(#arrowhead)" />
90
+
91
+ <!-- 6. IMPLEMENT FEATURES NODE -->
92
+ <g id="node-build">
93
+ <rect x="1990" y="175" width="290" height="70" rx="8" stroke="#0D32B2" stroke-width="2" fill="#F7F8FE" />
94
+ <text x="2135" y="216" text-anchor="middle" class="node-text-bold">Implement features + regression tests</text>
95
+ </g>
96
+
97
+ <!-- build -> verify -->
98
+ <path d="M 2280 210 L 2345 210" class="arrow" marker-end="url(#arrowhead)" />
99
+
100
+ <!-- 7. VERIFY CHECKS NODE -->
101
+ <g id="node-verify">
102
+ <rect x="2350" y="167" width="310" height="86" rx="8" stroke="#0D32B2" stroke-width="2" fill="#F7F8FE" />
103
+ <text x="2505" y="205" text-anchor="middle" class="node-text-bold">Run checks against fingerprint</text>
104
+ <text x="2505" y="228" text-anchor="middle" class="node-text-sub">Record exit status + output tail</text>
105
+ </g>
106
+
107
+ <!-- 8. ADVERSARIAL REVIEW NODE -->
108
+ <g id="node-review">
109
+ <rect x="2770" y="30" width="290" height="84" rx="8" stroke="#0D32B2" stroke-width="2" fill="#F7F8FE" />
110
+ <text x="2915" y="68" text-anchor="middle" class="node-text-bold">Adversarial review</text>
111
+ <text x="2915" y="91" text-anchor="middle" class="node-text-sub">UX, edge cases, limitations</text>
112
+ </g>
113
+
114
+ <!-- verify -> review -->
115
+ <path d="M 2550 167 C 2630 100, 2700 72, 2765 72" class="arrow" marker-end="url(#arrowhead)" />
116
+
117
+ <!-- 9. REPAIR FAILURES NODE -->
118
+ <g id="node-repair">
119
+ <rect x="3230" y="167" width="290" height="86" rx="8" stroke="#D97706" stroke-width="2" fill="#FFFBEB" />
120
+ <text x="3375" y="205" text-anchor="middle" class="node-text-bold" fill="#92400E">Repair failures (bounded)</text>
121
+ <text x="3375" y="228" text-anchor="middle" class="node-text-sub" fill="#B45309">Failing check + quoted output tail</text>
122
+ </g>
123
+
124
+ <!-- 10. DELIVER NODE -->
125
+ <g id="node-deliver">
126
+ <rect x="3230" y="30" width="280" height="84" rx="8" stroke="#059669" stroke-width="2" fill="#ECFDF5" />
127
+ <text x="3370" y="68" text-anchor="middle" class="node-text-bold" fill="#065F46">Evidence + launch instructions</text>
128
+ <text x="3370" y="91" text-anchor="middle" class="node-text-sub" fill="#047857">Explicit limitations</text>
129
+ </g>
130
+
131
+ <!-- review -> deliver -->
132
+ <path d="M 3060 72 L 3225 72" class="arrow-success" marker-end="url(#arrowhead-success)" />
133
+
134
+ <!-- verify -> repair (failed / stale) -->
135
+ <path d="M 2660 210 L 3225 210" class="arrow-repair" marker-end="url(#arrowhead-repair)" />
136
+ <rect x="2860" y="198" width="110" height="22" rx="4" fill="#FEF3C7" stroke="#FDE68A" stroke-width="1" />
137
+ <text x="2915" y="214" text-anchor="middle" class="edge-label" fill="#B45309">failed / stale</text>
138
+
139
+ <!-- review -> repair (defect) -->
140
+ <path d="M 3060 92 C 3140 120, 3180 150, 3225 180" class="arrow-repair" marker-end="url(#arrowhead-repair)" />
141
+ <rect x="3130" y="118" width="60" height="20" rx="4" fill="#FEF3C7" stroke="#FDE68A" stroke-width="1" />
142
+ <text x="3160" y="132" text-anchor="middle" class="edge-label" fill="#B45309">defect</text>
143
+
144
+ <!-- repair -> verify (feedback loop) -->
145
+ <path d="M 3375 253 C 3375 350, 2505 350, 2505 258" class="arrow" marker-end="url(#arrowhead)" />
146
+ <rect x="2880" y="338" width="120" height="22" rx="4" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1" />
147
+ <text x="2940" y="353" text-anchor="middle" class="edge-label">repair &#x2192; verify</text>
148
+
149
+ </svg>
@@ -0,0 +1,9 @@
1
+ # Workspace coordination
2
+
3
+ CLI checks, extension delivery tools (TUI/headless), and dashboard mutations share an exclusive `.harness/workspace.lock` for the whole operation, including subprocess checks. Contention fails explicitly; operations are not silently replayed. Same-process extension calls retain FIFO ordering. Dashboard contention returns HTTP 409.
4
+
5
+ An explicit new plan selects `.harness/active.json`. Status/dashboard/CLI selection uses this pointer rather than report modification time. Legacy workspaces without a pointer retain newest-report discovery until a coordinated operation selects a run. A missing or invalid selected report fails closed.
6
+
7
+ Before each extension mutation, the latest same-run persisted state is reconciled. Superseded runs cannot write or trigger automatic repairs; explicitly creating a new plan can select fresh work once the workspace is idle. Dashboard mutations refresh state under the lock. Existing report version checks still reject non-cooperating stale writers. Failed operations are not automatically replayed, and new plans intentionally supersede idle runs rather than merging unrelated contracts.
8
+
9
+ Locks are cooperative, local filesystem controls, not security isolation or a distributed lease. They do not serialize arbitrary shell edits, reviewers, or external tools. A crashed process can leave a lock: inspect its PID and the report, confirm no writer/check child is alive, then remove the lock manually. Never steal locks based solely on age. Manual state-file edits can bypass these controls. Separate worktrees have separate coordination state.
package/evaluate.mjs ADDED
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ /** Opt-in live A/B runs. Separate directories are NOT security sandboxes.
3
+ * Task domains are pluggable scenario manifests under scenarios/<name>/ —
4
+ * a game, a CLI task, or any future profile is one directory of config,
5
+ * graders and seeds, not a special case in this file.
6
+ */
7
+ import { parseArgs } from 'node:util';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { basename, delimiter, dirname, join } from 'node:path';
10
+ import { existsSync, mkdirSync, copyFileSync, cpSync, realpathSync, readdirSync, statSync, writeFileSync, readFileSync, appendFileSync } from 'node:fs';
11
+ import { randomUUID } from 'node:crypto';
12
+ import { atomicJson, runCommand } from './lib/delivery.mjs';
13
+ import { loadScenario, expandPlaceholders } from './lib/scenarios.mjs';
14
+ import { runNativeEvaluation, NATIVE_EVALUATION_RUNTIME } from './lib/opencode/evaluation.mjs';
15
+ import { ensureService, createSession, stopService } from './lib/opencode/service.mjs';
16
+ import { provisionRuntime } from './lib/opencode/runtime.mjs';
17
+ import { OpenCode } from '@opencode/client';
18
+
19
+ const { values: args } = parseArgs({ options: {
20
+ mode: { type: 'string', default: 'both' }, task: { type: 'string', default: 'cli' }, scenario: { type: 'string' },
21
+ model: { type: 'string', default: 'inception/mercury-2.5-preview' }, provider: { type: 'string', default: 'openrouter' },
22
+ thinking: { type: 'string', default: 'medium' },
23
+ timeout: { type: 'string', default: '600' }, 'max-turns': { type: 'string', default: '100' }, 'max-cost': { type: 'string', default: '3' },
24
+ python: { type: 'string' }, 'seed-from': { type: 'string' }, 'allow-live': { type: 'boolean', default: false },
25
+ 'dry-run': { type: 'boolean', default: false },
26
+ } });
27
+ if (!args['allow-live'] && !args['dry-run']) throw Error('Live calls spend API credit and generated code runs with your permissions. Use --allow-live explicitly (or --dry-run to preview the assembled run without calling a provider).');
28
+ if (!['baseline', 'custom', 'both'].includes(args.mode)) throw Error('Use --mode baseline|custom|both');
29
+ for (const key of ['timeout', 'max-turns', 'max-cost']) if (!(Number(args[key]) > 0 && Number.isFinite(Number(args[key])))) throw Error(`Invalid ${key}`);
30
+ if (Number(args['max-turns']) > 1000 || Number(args.timeout) > 3600) throw Error('Evaluation bounds may not exceed 1000 turns or 3600 seconds.');
31
+
32
+ const root = dirname(fileURLToPath(import.meta.url)), project = dirname(root);
33
+ // Layout: standalone checkouts keep evaluation fixtures beside this script;
34
+ // when this package is nested inside a workspace, the venv and evidence
35
+ // directories live in the parent project instead.
36
+ const home = existsSync(join(root, 'scenarios')) ? root : project;
37
+ const providerKey = process.env[`${args.provider.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_API_KEY`];
38
+ if (!args['dry-run'] && !providerKey) throw Error(`Set ${args.provider.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_API_KEY explicitly; evaluation never discovers credentials from a home-directory agent config.`);
39
+ const scenarioName = args.scenario || args.task;
40
+ const scenario = loadScenario(root, scenarioName);
41
+ const findExecutable = name => {
42
+ const names = process.platform === 'win32' ? [`${name}.exe`, name] : [name];
43
+ for (const dir of (process.env.PATH || '').split(delimiter)) {
44
+ for (const candidate of names.map(file => join(dir, file))) if (existsSync(candidate)) return candidate;
45
+ }
46
+ return null;
47
+ };
48
+ const venvPython = join(home, '.venv', process.platform === 'win32' ? 'Scripts/python.exe' : 'bin/python');
49
+ const python = args.python || (existsSync(venvPython) ? venvPython : findExecutable('python3') || findExecutable('python') || venvPython);
50
+ if (scenario.needsPython && !existsSync(python)) {
51
+ const msg = `Scenario ${scenarioName} needs Python — pass --python /absolute/path/to/python${existsSync(join(scenario.dir, 'requirements.txt')) ? ` after installing scenarios/${scenarioName}/requirements.txt` : ''}`;
52
+ if (args['dry-run']) console.error(`warning: ${msg}`); else throw Error(msg);
53
+ }
54
+ const seedFrom = args['seed-from'] ? realpathSync(args['seed-from']) : null;
55
+ if (seedFrom && !statSync(seedFrom).isDirectory()) throw Error('--seed-from must name a candidate directory');
56
+ const continuationEvidence = (() => {
57
+ if (!seedFrom) return '';
58
+ const summaryPath = join(dirname(seedFrom), 'summary.json');
59
+ if (!existsSync(summaryPath)) return '';
60
+ try {
61
+ const parsed = JSON.parse(readFileSync(summaryPath, 'utf8'));
62
+ const summary = Array.isArray(parsed) ? parsed.at(-1) : parsed;
63
+ const failed = (summary.finalChecks || [])
64
+ .filter(check => check.code !== 0)
65
+ .map(check => `${check.id}:\n${check.output}`)
66
+ .join('\n\n');
67
+ return failed.slice(-6000);
68
+ } catch {
69
+ return '';
70
+ }
71
+ })();
72
+
73
+ const vars = ({ cwd }) => ({ python, node: process.execPath, root, home, cwd, scenario: scenario.dir, ...(scenario.runtimes || {}) });
74
+ const scenarioChecks = (list, cwd) => list.map(c => ({ ...c, argv: expandPlaceholders(c.argv, vars({ cwd })) }));
75
+
76
+ const base = join(home, '.harness', 'evaluations', `${Date.now()}-${scenarioName}-${randomUUID().slice(0, 6)}`);
77
+ if (!args['dry-run']) mkdirSync(base, { recursive: true });
78
+ const runtimeInfo = [
79
+ scenario.needsPython ? `Python executable: ${JSON.stringify(python)}.` : null,
80
+ `Node: ${JSON.stringify(process.execPath)}.`,
81
+ ].filter(Boolean).join(' ');
82
+ const environment = `Evaluation execution context:
83
+ The current working directory is the isolated product workspace. Write product files with workspace-relative paths such as README.md or src/index.ts; do not prefix them with the current directory or recreate an absolute path as nested folders. ${runtimeInfo} Invoke these exact executable paths and quote them in shell commands. Absolute paths in this context identify external tools and are valid as-is; do not hunt for them.
84
+ Set a bounded timeout on every shell command and never run filesystem-wide searches (find /, ls -R from the root, find of whole drives): one hung command can consume the entire time budget. Do not touch files outside this work directory or run nested engine agents. No network installs or deployment.
85
+ You have at most ${args['max-turns']} productive model turns (recovered provider connection errors do not consume that budget) and ${args.timeout} seconds; reserve time for verification. If declared checks pass with budget unused, re-check every user requirement and finish missing behavior rather than stopping at the first passing slice. Generated logs/screenshots go in artifacts/.
86
+ ${expandPlaceholders(scenario.setup, vars({ cwd: '<the isolated product workspace>' }))}`;
87
+ const environmentPath = join(base, 'execution-context.md');
88
+ if (!args['dry-run']) writeFileSync(environmentPath, environment);
89
+ const summaries = [];
90
+
91
+ function copySeeds(cwd) {
92
+ if (seedFrom) {
93
+ const excluded = new Set(['.harness', 'artifacts', '__pycache__', '.pytest_cache', '.venv', 'node_modules', 'dist', 'build', 'coverage', '.turbo']);
94
+ for (const entry of readdirSync(seedFrom, { withFileTypes: true })) {
95
+ if (!excluded.has(entry.name)) {
96
+ cpSync(join(seedFrom, entry.name), join(cwd, entry.name), { recursive: true });
97
+ }
98
+ }
99
+ return;
100
+ }
101
+ for (const seed of scenario.seeds) {
102
+ const target = join(cwd, seed.to);
103
+ mkdirSync(dirname(target), { recursive: true });
104
+ copyFileSync(seed.from, target);
105
+ }
106
+ }
107
+
108
+ async function runScenarioChecks(mode, cwd, tier, checks) {
109
+ const results = [];
110
+ for (const check of scenarioChecks(checks, cwd)) {
111
+ const result = await runCommand(check.argv, {
112
+ cwd,
113
+ timeoutSeconds: check.timeoutSeconds,
114
+ logPath: join(base, `${mode}-${tier}-${check.id}.log`),
115
+ });
116
+ results.push({
117
+ id: check.id,
118
+ tier,
119
+ code: result.code,
120
+ timedOut: result.timedOut,
121
+ output: result.output,
122
+ });
123
+ }
124
+ return results;
125
+ }
126
+
127
+ for (const mode of args.mode === 'both' ? ['baseline', 'custom'] : [args.mode]) {
128
+ const cwd = join(base, mode);
129
+ if (!args['dry-run']) {
130
+ mkdirSync(cwd, { recursive: true });
131
+ copySeeds(cwd);
132
+ }
133
+ const systemPrompt = `${environment}\n\n${mode === 'custom' ? scenario.context : ''}`;
134
+ if (mode === 'custom') {
135
+ const manifest = join(base, 'required-validators.json');
136
+ if (!args['dry-run']) atomicJson(manifest, { version: 1, checks: scenarioChecks(scenario.developmentChecks, cwd) });
137
+ // The native plugin owns delivery configuration; the manifest remains an
138
+ // explicit piece of immutable evidence for that treatment.
139
+ // Domain context from the manifest, plus generic continuation guidance.
140
+ const context = [
141
+ scenario.context,
142
+ seedFrom && 'This workspace contains an earlier generated candidate. Every behavior not listed in the independent failures already passes. Preserve those behaviors, avoid subsystem rewrites, and make the smallest focused correction that addresses only the listed failures.',
143
+ continuationEvidence && `Independent failures from the candidate being continued:\n${continuationEvidence}`,
144
+ ].filter(Boolean).join('\n\n');
145
+ if (context) {
146
+ const contextPath = join(base, `${basename(scenarioName)}-context.md`);
147
+ if (!args['dry-run']) writeFileSync(contextPath, context);
148
+ }
149
+ }
150
+ const controller = new AbortController();
151
+ console.log(`Starting ${mode} ${scenarioName} with ${args.provider}/${args.model}; ${cwd}`);
152
+ if (args['dry-run']) {
153
+ console.log(JSON.stringify({
154
+ mode, cwd, runtime: NATIVE_EVALUATION_RUNTIME, nativeRequest: { directory: cwd, provider: args.provider, model: args.model, prompt: scenario.prompt, systemPrompt, timeoutSeconds: Number(args.timeout), maxTurns: Number(args['max-turns']), maxCost: Number(args['max-cost']) }, seeds: scenario.seeds.map(s => s.to),
155
+ ...(mode === 'custom' ? { requiredValidators: scenarioChecks(scenario.developmentChecks, cwd) } : {}),
156
+ }, null, 2));
157
+ continue;
158
+ }
159
+ const isolation = join(base, 'native-service');
160
+ let service;
161
+ try {
162
+ const providerID = args.provider === 'openrouter' ? 'openrouter' : args.provider;
163
+ const modelID = args.model;
164
+ await provisionRuntime({ root:isolation, profile:{
165
+ model:`${providerID}/${modelID}`,
166
+ default_agent:'build',
167
+ plugins:[join(root,'lib/opencode/evaluation-budget-plugin'), ...(mode === 'custom' ? [`${root}/plugins/carthagent`] : [])],
168
+ skills:mode === 'custom' ? [`${root}/skills`] : [],
169
+ instructions:[environmentPath],
170
+ } });
171
+ service = await ensureService({ root:isolation, env:{
172
+ OPENROUTER_API_KEY:providerKey,
173
+ [`${args.provider.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_API_KEY`]:providerKey,
174
+ CTG_EVALUATION_MAX_TURNS:String(args['max-turns']),
175
+ CTG_EVALUATION_MAX_COST:String(args['max-cost']),
176
+ } });
177
+ process.env.CTG_OPENCODE_URL = service.url;
178
+ process.env.CTG_OPENCODE_AUTHORIZATION = service.headers.authorization;
179
+ const serviceClient = OpenCode.make({ baseUrl:service.url, headers:service.headers });
180
+ const probe = await createSession(service, cwd, { signal:AbortSignal.timeout(15000), body:{ model:{providerID,id:modelID}, title:'CTG evaluation plugin activation probe' } });
181
+ const pluginDeadline = Date.now() + 15000;
182
+ while (Date.now() < pluginDeadline) {
183
+ const inventory = await serviceClient.plugin.list({ location:{ directory:cwd } }, { signal:AbortSignal.timeout(5000) });
184
+ const guard = inventory.data.find(item => item.id === 'ctg.evaluation-budget');
185
+ if (guard?.state.status === 'active') break;
186
+ if (guard?.state.status === 'failed') throw Error(`Evaluation admission guard failed to activate: ${guard.state.error}`);
187
+ if (Date.now() + 100 >= pluginDeadline) throw Error(`Evaluation admission guard did not activate for probe session ${probe.id}`);
188
+ await new Promise(resolve => setTimeout(resolve, 100));
189
+ }
190
+ const result = await runNativeEvaluation({ directory: cwd, systemPrompt, prompt: scenario.prompt, model: args.model, provider: args.provider, timeoutSeconds: Number(args.timeout), maxTurns: Number(args['max-turns']), maxCost: Number(args['max-cost']), signal: controller.signal, onEvent: event => appendFileSync(join(base, `${mode}-events.jsonl`), `${JSON.stringify(event)}\n`) });
191
+ const summary = {
192
+ mode,
193
+ task: scenarioName,
194
+ oracleGuided: mode === 'custom',
195
+ provider: args.provider,
196
+ model: args.model,
197
+ cwd,
198
+ evidenceSchema: 2,
199
+ runtime: NATIVE_EVALUATION_RUNTIME,
200
+ runtimeVersion: result.opencodeVersion,
201
+ exitCode: result.exitCode,
202
+ timedOut: result.timedOut,
203
+ budgetReason: result.budgetReason,
204
+ outcome: result.outcome,
205
+ interruptionStatus: result.interruptionStatus,
206
+ sessionInfo: { costScope:'session', usageSource:'session.get', terminalAuthority:'session.outcome', outcome:result.outcome },
207
+ turns: result.turns,
208
+ productiveTurns: result.productiveTurns,
209
+ providerErrors: result.providerErrors,
210
+ endedOnProviderError: result.lastStop === 'error',
211
+ reportedCost: result.cost,
212
+ costEvidence: 'native-session-usage; fail-closed when absent',
213
+ reportedTokens: result.tokens,
214
+ lastStop: result.lastStop,
215
+ deliveryStatus: result.deliveryStatus,
216
+ errors: result.errors,
217
+ tools: result.tools,
218
+ sessionID: result.sessionID,
219
+ campaignComparability: 'not-comparable-to-pi-runtime',
220
+ durationMs: result.durationMs,
221
+ };
222
+ summary.developmentChecks = await runScenarioChecks(mode, cwd, 'development', scenario.developmentChecks);
223
+ summary.holdoutChecks = await runScenarioChecks(mode, cwd, 'holdout', scenario.holdoutChecks);
224
+ summary.finalChecks = [...summary.developmentChecks, ...summary.holdoutChecks];
225
+ summary.externalGrade = {
226
+ code: summary.holdoutChecks.some(check => check.code !== 0 || check.timedOut) ? 1 : 0,
227
+ output: summary.holdoutChecks.map(check => `# ${check.id}\n${check.output}`).join('\n'),
228
+ };
229
+ summaries.push(summary);
230
+ atomicJson(join(base, 'summary.json'), summaries);
231
+ console.log(JSON.stringify(summary, null, 2));
232
+ } finally {
233
+ try {
234
+ if (service) await stopService({ root:isolation });
235
+ } finally {
236
+ delete process.env.CTG_OPENCODE_URL;
237
+ delete process.env.CTG_OPENCODE_AUTHORIZATION;
238
+ }
239
+ }
240
+ }
241
+ console.log(`Evidence: ${join(base, 'summary.json')}`);
242
+ if (summaries.some(s =>
243
+ s.exitCode !== 0 ||
244
+ (s.endedOnProviderError && s.deliveryStatus !== 'verified') ||
245
+ (s.mode === 'custom' && s.deliveryStatus !== 'verified') ||
246
+ s.timedOut ||
247
+ s.budgetReason ||
248
+ s.finalChecks?.some(check => check.code !== 0 || check.timedOut)
249
+ )) process.exitCode = 1;
@@ -0,0 +1,36 @@
1
+ import { truncateTail, type ExtensionAPI, type ExtensionContext } from '@earendil-works/pi-coding-agent';
2
+ import { createCodeIndex } from '../lib/codegraph.mjs';
3
+ import { boundedCodeNavResult } from '../lib/codegraph-query.mjs';
4
+
5
+ type CodeIndex = Awaited<ReturnType<typeof createCodeIndex>>;
6
+ export default function codegraph(pi: ExtensionAPI) {
7
+ pi.registerFlag('codegraph-max-files', { description: 'Positive maximum source files indexed by code_nav (default 8000)', type: 'string', default: '8000' });
8
+ const indexes = new Map<string, Promise<CodeIndex>>();
9
+ const indexFor = (ctx: ExtensionContext) => {
10
+ let pending = indexes.get(ctx.cwd);
11
+ if (!pending) {
12
+ const raw = pi.getFlag('codegraph-max-files') ?? '8000';
13
+ if (!/^\d+$/.test(String(raw)) || !Number.isSafeInteger(Number(raw)) || Number(raw) < 1) throw Error('codegraph-max-files must be a positive safe integer');
14
+ pending = createCodeIndex(ctx.cwd, { maxFiles: Number(raw) });
15
+ pending.catch(() => indexes.delete(ctx.cwd));
16
+ indexes.set(ctx.cwd, pending);
17
+ }
18
+ return pending;
19
+ };
20
+ pi.registerTool({
21
+ name: 'code_nav', label: 'Code navigation',
22
+ description: 'Read-only structural graph for JS/TS/Python/Rust. Use definition/symbols/outline to locate code, callers/importers before changing APIs, then targeted source reads. Calls are name-based candidates, not resolved bindings. Results include coverage, total and nextOffset; page or filter incomplete answers. path filters definitions/symbols or caller call-site files; container filters enclosing classes.',
23
+ parameters: { type: 'object', required: ['op'], additionalProperties: false, properties: {
24
+ op: { type: 'string', enum: ['definition', 'symbols', 'callers', 'callees', 'imports', 'importers', 'outline', 'status'] },
25
+ name: { type: 'string' }, path: { type: 'string' }, container: { type: 'string' },
26
+ limit: { type: 'integer', minimum: 1, maximum: 200, default: 50 }, offset: { type: 'integer', minimum: 0, default: 0 },
27
+ } },
28
+ async execute(_id, params: { op: string; name?: string; path?: string; container?: string; limit?: number; offset?: number }, _signal: unknown, _update: unknown, ctx: ExtensionContext) {
29
+ const index = await indexFor(ctx);
30
+ await index.refresh();
31
+ const serialized = boundedCodeNavResult(index, params);
32
+ const output = truncateTail(serialized, { maxBytes: 24000, maxLines: 400 });
33
+ return { content: [{ type: 'text' as const, text: output.content + (output.truncated ? '\n[Oversized record truncated; narrow query.]' : '') }], details: {} };
34
+ },
35
+ });
36
+ }