@ggui-ai/ui-gen 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/dist/{adapter-h4_V69LI.d.ts → adapter-DLQ-5COm.d.ts} +1 -1
  2. package/dist/adapters/base.js +2 -27
  3. package/dist/adapters/base.js.map +1 -1
  4. package/dist/adapters/generation-dispatch.d.ts +14 -6
  5. package/dist/adapters/generation-dispatch.js +40 -19260
  6. package/dist/adapters/generation-dispatch.js.map +1 -1
  7. package/dist/adapters/index.d.ts +1 -1
  8. package/dist/adapters/index.js +25 -3561
  9. package/dist/adapters/index.js.map +1 -1
  10. package/dist/adapters/types.js +2 -9
  11. package/dist/adapters/types.js.map +1 -1
  12. package/dist/advanced/index.d.ts +13 -12
  13. package/dist/advanced/index.js +63 -21954
  14. package/dist/advanced/index.js.map +1 -1
  15. package/dist/{bank-Ctdp3FUA.d.ts → bank-LWaiAtaA.d.ts} +1 -1
  16. package/dist/blueprint-validator.js +6 -2301
  17. package/dist/blueprint-validator.js.map +1 -1
  18. package/dist/boilerplate.d.ts +3 -2
  19. package/dist/boilerplate.js +6 -1867
  20. package/dist/boilerplate.js.map +1 -1
  21. package/dist/check/index.d.ts +3 -2
  22. package/dist/check/index.js +6 -1923
  23. package/dist/check/index.js.map +1 -1
  24. package/dist/chunk-33B3BAWS.js +24 -0
  25. package/dist/chunk-33B3BAWS.js.map +1 -0
  26. package/dist/chunk-3HSGA6NI.js +591 -0
  27. package/dist/chunk-3HSGA6NI.js.map +1 -0
  28. package/dist/chunk-3NNNGNTO.js +1707 -0
  29. package/dist/chunk-3NNNGNTO.js.map +1 -0
  30. package/dist/chunk-3T2HMTON.js +79 -0
  31. package/dist/chunk-3T2HMTON.js.map +1 -0
  32. package/dist/chunk-3ZSSDV3L.js +123 -0
  33. package/dist/chunk-3ZSSDV3L.js.map +1 -0
  34. package/dist/chunk-4MBR5GA3.js +99 -0
  35. package/dist/chunk-4MBR5GA3.js.map +1 -0
  36. package/dist/chunk-56NJFD55.js +337 -0
  37. package/dist/chunk-56NJFD55.js.map +1 -0
  38. package/dist/chunk-5AGTY555.js +26 -0
  39. package/dist/chunk-5AGTY555.js.map +1 -0
  40. package/dist/chunk-5EJM7EGY.js +115 -0
  41. package/dist/chunk-5EJM7EGY.js.map +1 -0
  42. package/dist/chunk-5KXCQLLR.js +529 -0
  43. package/dist/chunk-5KXCQLLR.js.map +1 -0
  44. package/dist/chunk-5VBXHVEH.js +171 -0
  45. package/dist/chunk-5VBXHVEH.js.map +1 -0
  46. package/dist/chunk-6KDDEING.js +515 -0
  47. package/dist/chunk-6KDDEING.js.map +1 -0
  48. package/dist/chunk-6VDRWAP6.js +123 -0
  49. package/dist/chunk-6VDRWAP6.js.map +1 -0
  50. package/dist/chunk-732IBFEJ.js +2400 -0
  51. package/dist/chunk-732IBFEJ.js.map +1 -0
  52. package/dist/chunk-73AURS3I.js +46 -0
  53. package/dist/chunk-73AURS3I.js.map +1 -0
  54. package/dist/chunk-77H5SKLX.js +8 -0
  55. package/dist/chunk-77H5SKLX.js.map +1 -0
  56. package/dist/chunk-7QE3AHLE.js +35 -0
  57. package/dist/chunk-7QE3AHLE.js.map +1 -0
  58. package/dist/chunk-A375NNWN.js +11 -0
  59. package/dist/chunk-A375NNWN.js.map +1 -0
  60. package/dist/chunk-AOU33EFS.js +140 -0
  61. package/dist/chunk-AOU33EFS.js.map +1 -0
  62. package/dist/chunk-BA4QYA6E.js +461 -0
  63. package/dist/chunk-BA4QYA6E.js.map +1 -0
  64. package/dist/chunk-DBP764GL.js +3 -0
  65. package/dist/chunk-DBP764GL.js.map +1 -0
  66. package/dist/chunk-DUU3ZJDQ.js +41 -0
  67. package/dist/chunk-DUU3ZJDQ.js.map +1 -0
  68. package/dist/chunk-DXKRBXNU.js +30 -0
  69. package/dist/chunk-DXKRBXNU.js.map +1 -0
  70. package/dist/chunk-FB7OEEAH.js +1467 -0
  71. package/dist/chunk-FB7OEEAH.js.map +1 -0
  72. package/dist/chunk-FLRNMZV2.js +18 -0
  73. package/dist/chunk-FLRNMZV2.js.map +1 -0
  74. package/dist/chunk-FWJIMKFZ.js +2032 -0
  75. package/dist/chunk-FWJIMKFZ.js.map +1 -0
  76. package/dist/chunk-FXRHWYIQ.js +163 -0
  77. package/dist/chunk-FXRHWYIQ.js.map +1 -0
  78. package/dist/chunk-G3PMV62Z.js +33 -0
  79. package/dist/chunk-G3PMV62Z.js.map +1 -0
  80. package/dist/chunk-GAJI7Y3P.js +13 -0
  81. package/dist/chunk-GAJI7Y3P.js.map +1 -0
  82. package/dist/chunk-GYINWMPR.js +2052 -0
  83. package/dist/chunk-GYINWMPR.js.map +1 -0
  84. package/dist/chunk-HD6ABIK7.js +484 -0
  85. package/dist/chunk-HD6ABIK7.js.map +1 -0
  86. package/dist/chunk-HDQVHYO6.js +100 -0
  87. package/dist/chunk-HDQVHYO6.js.map +1 -0
  88. package/dist/chunk-IHUT3SIW.js +7 -0
  89. package/dist/chunk-IHUT3SIW.js.map +1 -0
  90. package/dist/chunk-JDH5EGNZ.js +126 -0
  91. package/dist/chunk-JDH5EGNZ.js.map +1 -0
  92. package/dist/chunk-JMD4RGSV.js +37 -0
  93. package/dist/chunk-JMD4RGSV.js.map +1 -0
  94. package/dist/chunk-KJWGZL5B.js +33 -0
  95. package/dist/chunk-KJWGZL5B.js.map +1 -0
  96. package/dist/chunk-MKMDQFZ7.js +86 -0
  97. package/dist/chunk-MKMDQFZ7.js.map +1 -0
  98. package/dist/chunk-NF6JUZWF.js +85 -0
  99. package/dist/chunk-NF6JUZWF.js.map +1 -0
  100. package/dist/chunk-O2YC6XDC.js +1933 -0
  101. package/dist/chunk-O2YC6XDC.js.map +1 -0
  102. package/dist/chunk-OVLH5ICV.js +29 -0
  103. package/dist/chunk-OVLH5ICV.js.map +1 -0
  104. package/dist/chunk-PBOYHGSD.js +106 -0
  105. package/dist/chunk-PBOYHGSD.js.map +1 -0
  106. package/dist/chunk-PJFH7QWA.js +81 -0
  107. package/dist/chunk-PJFH7QWA.js.map +1 -0
  108. package/dist/chunk-PUH4CXGZ.js +284 -0
  109. package/dist/chunk-PUH4CXGZ.js.map +1 -0
  110. package/dist/chunk-QLXXPGTV.js +18 -0
  111. package/dist/chunk-QLXXPGTV.js.map +1 -0
  112. package/dist/chunk-R5GIGPW2.js +2845 -0
  113. package/dist/chunk-R5GIGPW2.js.map +1 -0
  114. package/dist/chunk-RH4IUSTO.js +83 -0
  115. package/dist/chunk-RH4IUSTO.js.map +1 -0
  116. package/dist/chunk-S2GVXKKY.js +153 -0
  117. package/dist/chunk-S2GVXKKY.js.map +1 -0
  118. package/dist/chunk-TVHGKFJS.js +58 -0
  119. package/dist/chunk-TVHGKFJS.js.map +1 -0
  120. package/dist/chunk-U7GALWLG.js +66 -0
  121. package/dist/chunk-U7GALWLG.js.map +1 -0
  122. package/dist/chunk-VTC263LT.js +256 -0
  123. package/dist/chunk-VTC263LT.js.map +1 -0
  124. package/dist/chunk-WEOXFEVE.js +739 -0
  125. package/dist/chunk-WEOXFEVE.js.map +1 -0
  126. package/dist/chunk-WYHP46LT.js +28 -0
  127. package/dist/chunk-WYHP46LT.js.map +1 -0
  128. package/dist/chunk-XGLO5LGG.js +274 -0
  129. package/dist/chunk-XGLO5LGG.js.map +1 -0
  130. package/dist/chunk-XRBCRKA2.js +29 -0
  131. package/dist/chunk-XRBCRKA2.js.map +1 -0
  132. package/dist/chunk-XW7ADFFV.js +340 -0
  133. package/dist/chunk-XW7ADFFV.js.map +1 -0
  134. package/dist/chunk-YIQ42IUX.js +34 -0
  135. package/dist/chunk-YIQ42IUX.js.map +1 -0
  136. package/dist/chunk-YJZTUY7B.js +101 -0
  137. package/dist/chunk-YJZTUY7B.js.map +1 -0
  138. package/dist/chunk-Z3CXME3K.js +107 -0
  139. package/dist/chunk-Z3CXME3K.js.map +1 -0
  140. package/dist/classifier/index.js +2 -527
  141. package/dist/classifier/index.js.map +1 -1
  142. package/dist/client-JBPCXU7W.js +4 -0
  143. package/dist/client-JBPCXU7W.js.map +1 -0
  144. package/dist/compose.js +3 -367
  145. package/dist/compose.js.map +1 -1
  146. package/dist/cost-tracker-HHGQ4FTW.js +67 -0
  147. package/dist/cost-tracker-HHGQ4FTW.js.map +1 -0
  148. package/dist/{create-ui-generator-BrZHF0cf.d.ts → create-ui-generator-BY-WeaPi.d.ts} +3 -3
  149. package/dist/{design-mode-DGWN_Gkm.d.ts → design-mode-XyOQmzE0.d.ts} +4 -3
  150. package/dist/{dispatch-B07TI30d.d.ts → dispatch-8LzTddKw.d.ts} +3 -2
  151. package/dist/dist-UALN6ZVB.js +543 -0
  152. package/dist/dist-UALN6ZVB.js.map +1 -0
  153. package/dist/dupe-break-X6K7QFIS.js +4 -0
  154. package/dist/dupe-break-X6K7QFIS.js.map +1 -0
  155. package/dist/evaluation/axis-checks/index.d.ts +5 -4
  156. package/dist/evaluation/axis-checks/index.js +6 -1914
  157. package/dist/evaluation/axis-checks/index.js.map +1 -1
  158. package/dist/evaluation/axis-checks/registry.d.ts +3 -2
  159. package/dist/evaluation/axis-checks/registry.js +3 -1844
  160. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  161. package/dist/evaluation/index.d.ts +49 -132
  162. package/dist/evaluation/index.js +302 -6466
  163. package/dist/evaluation/index.js.map +1 -1
  164. package/dist/evaluation/types.d.ts +3 -2
  165. package/dist/evaluation/types.js +2 -5
  166. package/dist/evaluation/types.js.map +1 -1
  167. package/dist/fragments/index.js +2 -338
  168. package/dist/fragments/index.js.map +1 -1
  169. package/dist/{generate-tSO_shQ0.d.ts → generate-DQ4ci90s.d.ts} +1 -1
  170. package/dist/harness/check/runtime-render/index.d.ts +35 -15
  171. package/dist/harness/check/runtime-render/index.js +6 -2390
  172. package/dist/harness/check/runtime-render/index.js.map +1 -1
  173. package/dist/harness/check/runtime-render/render-check-worker.js +4 -1739
  174. package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
  175. package/dist/harness/index.d.ts +3 -2
  176. package/dist/harness/index.js +23 -5126
  177. package/dist/harness/index.js.map +1 -1
  178. package/dist/harness/llm-trace-sink.js +2 -24
  179. package/dist/harness/llm-trace-sink.js.map +1 -1
  180. package/dist/harness/prompts.d.ts +2 -2
  181. package/dist/harness/prompts.js +8 -324
  182. package/dist/harness/prompts.js.map +1 -1
  183. package/dist/harness/result-types.d.ts +6 -5
  184. package/dist/harness/result-types.js +2 -0
  185. package/dist/harness/result-types.js.map +1 -1
  186. package/dist/harness/runtime.d.ts +22 -5
  187. package/dist/harness/runtime.js +10 -2313
  188. package/dist/harness/runtime.js.map +1 -1
  189. package/dist/harness/types-public.d.ts +3 -2
  190. package/dist/harness/types-public.js +16 -2954
  191. package/dist/harness/types-public.js.map +1 -1
  192. package/dist/harness/validator-trace-sink.d.ts +3 -2
  193. package/dist/harness/validator-trace-sink.js +2 -26
  194. package/dist/harness/validator-trace-sink.js.map +1 -1
  195. package/dist/hash.js +2 -39
  196. package/dist/hash.js.map +1 -1
  197. package/dist/hashline-5OKREIRM.js +4 -0
  198. package/dist/hashline-5OKREIRM.js.map +1 -0
  199. package/dist/index.d.ts +50 -11
  200. package/dist/index.js +57 -22015
  201. package/dist/index.js.map +1 -1
  202. package/dist/llm-evaluator-PN3WFCS4.js +653 -0
  203. package/dist/llm-evaluator-PN3WFCS4.js.map +1 -0
  204. package/dist/magic-string.es-2NZTC4GZ.js +1307 -0
  205. package/dist/magic-string.es-2NZTC4GZ.js.map +1 -0
  206. package/dist/patch.d.ts +3 -2
  207. package/dist/patch.js +2 -56
  208. package/dist/patch.js.map +1 -1
  209. package/dist/policy-VCYKGSAC.js +5 -0
  210. package/dist/policy-VCYKGSAC.js.map +1 -0
  211. package/dist/policy.js +2 -35
  212. package/dist/policy.js.map +1 -1
  213. package/dist/primitive-index-2OF6F4TZ.js +131 -0
  214. package/dist/primitive-index-2OF6F4TZ.js.map +1 -0
  215. package/dist/primitive-slice-WTMBLYFJ.js +235 -0
  216. package/dist/primitive-slice-WTMBLYFJ.js.map +1 -0
  217. package/dist/{prompts-BhNUuRoZ.d.ts → prompts-CLZGywMP.d.ts} +1 -1
  218. package/dist/provider-adapter-contract.js +10407 -14204
  219. package/dist/provider-adapter-contract.js.map +1 -1
  220. package/dist/provider-adapter.js +2 -44
  221. package/dist/provider-adapter.js.map +1 -1
  222. package/dist/providers/index.d.ts +89 -1
  223. package/dist/providers/index.js +115 -44
  224. package/dist/providers/index.js.map +1 -1
  225. package/dist/raw-C6KBH5I3.js +141 -0
  226. package/dist/raw-C6KBH5I3.js.map +1 -0
  227. package/dist/raw-FXSBXQTZ.js +7 -0
  228. package/dist/raw-FXSBXQTZ.js.map +1 -0
  229. package/dist/raw-MURDUPGK.js +8 -0
  230. package/dist/raw-MURDUPGK.js.map +1 -0
  231. package/dist/raw-T3GW3UVX.js +8 -0
  232. package/dist/raw-T3GW3UVX.js.map +1 -0
  233. package/dist/render-check-host-ZNIS6BJ5.js +5 -0
  234. package/dist/render-check-host-ZNIS6BJ5.js.map +1 -0
  235. package/dist/sdk-4JNSDNSR.js +6 -0
  236. package/dist/sdk-4JNSDNSR.js.map +1 -0
  237. package/dist/sdk-DIVJMHCX.js +7 -0
  238. package/dist/sdk-DIVJMHCX.js.map +1 -0
  239. package/dist/sdk-QRJOCRTS.js +7 -0
  240. package/dist/sdk-QRJOCRTS.js.map +1 -0
  241. package/dist/tag-balance-CTHCTRJ7.js +123 -0
  242. package/dist/tag-balance-CTHCTRJ7.js.map +1 -0
  243. package/dist/tools/get-primitives-ts.d.ts +1 -1
  244. package/dist/tools/get-primitives-ts.js +3 -1
  245. package/dist/tools/get-primitives-ts.js.map +1 -1
  246. package/dist/tools/render-check-worker.js +3 -40
  247. package/dist/tools/render-check-worker.js.map +1 -1
  248. package/dist/{types-public-h8IMnYuh.d.ts → types-public-hYlrerZh.d.ts} +35 -2
  249. package/dist/validation/index.d.ts +2 -2
  250. package/dist/validation/index.js +3 -498
  251. package/dist/validation/index.js.map +1 -1
  252. package/dist/{visual-evaluator-C2strxqD.d.ts → visual-evaluator-KskajxD5.d.ts} +14 -4
  253. package/dist/visual-evaluator-TGEXYQTP.js +12 -0
  254. package/dist/visual-evaluator-TGEXYQTP.js.map +1 -0
  255. package/dist/workflows.d.ts +3 -2
  256. package/dist/workflows.js +2 -151
  257. package/dist/workflows.js.map +1 -1
  258. package/package.json +20 -16
  259. package/src/adapters/claude/sdk.ts +4 -2
  260. package/src/adapters/claude/tool-bridge.ts +2 -1
  261. package/src/adapters/generation-dispatch.ts +9 -0
  262. package/src/adapters/openai/raw.ts +4 -0
  263. package/src/adapters/openai/sdk.ts +6 -1
  264. package/src/adapters/openai/tokens.ts +21 -7
  265. package/src/adapters/tool-bridge.ts +1 -1
  266. package/src/adapters/tools.ts +1 -1
  267. package/src/advanced/feedback.ts +1 -2
  268. package/src/advanced/generator.ts +4 -3
  269. package/src/boilerplate/free-design-prompt.ts +7 -0
  270. package/src/boilerplate/generate.ts +18 -5
  271. package/src/boilerplate/hard-sections.ts +8 -3
  272. package/src/boilerplate/react-hooks.ts +56 -0
  273. package/src/boilerplate/system-prompt.ts +12 -3
  274. package/src/check/run-tier0.ts +233 -8
  275. package/src/check/type-checker.ts +128 -16
  276. package/src/coding-agent/tools.ts +26 -3
  277. package/src/design-mode.ts +4 -9
  278. package/src/evaluation/axis-checks/checks/state-merge.ts +2 -4
  279. package/src/evaluation/axis-checks/checks/state-payload.ts +1 -28
  280. package/src/evaluation/axis-checks/checks/tint.ts +197 -0
  281. package/src/evaluation/axis-checks/helpers.ts +121 -0
  282. package/src/evaluation/axis-checks/registry.ts +2 -0
  283. package/src/evaluation/criteria/__fixtures__/edge-v2-draw2.json +4629 -0
  284. package/src/evaluation/criteria/bank.ts +1 -1
  285. package/src/evaluation/criteria/edge-instruments.ts +14 -0
  286. package/src/evaluation/criteria/edge-probe-v2.ts +352 -0
  287. package/src/evaluation/criteria/edge-v2.ts +242 -0
  288. package/src/evaluation/criteria/resolve.ts +24 -5
  289. package/src/evaluation/index.ts +3 -5
  290. package/src/evaluation/mcp-server.ts +0 -50
  291. package/src/evaluation/types-public.ts +6 -1
  292. package/src/evaluation/visual-evaluator.ts +161 -30
  293. package/src/harness/check/runtime-render/index.ts +28 -4
  294. package/src/harness/check/runtime-render/render-check.ts +20 -21
  295. package/src/harness/claude-agent-sdk-loader.ts +93 -0
  296. package/src/harness/claude-code-login-query.ts +1 -1
  297. package/src/harness/coding/generate-task-runner.ts +1 -0
  298. package/src/harness/coding/run-coding-turn.ts +3 -0
  299. package/src/harness/coding/run-eval-round.ts +9 -0
  300. package/src/harness/llm-router.ts +50 -2
  301. package/src/harness/runtime.ts +17 -0
  302. package/src/index.ts +9 -0
  303. package/src/providers/anthropic-tool-call.ts +263 -0
  304. package/src/providers/index.ts +13 -0
  305. package/src/tools/get-primitives-ts.ts +1 -1
  306. package/src/tools/render-check.ts +4 -4
  307. package/src/validation/primitives.ts +2 -1
  308. package/src/evaluation/loop.ts +0 -218
@@ -0,0 +1,2845 @@
1
+ import { createDupeBreakState, recordToolExchange, SAME_EXCHANGE_NUDGE, SAME_EXCHANGE_BREAK_AT, clearWriteFailures, recordWriteFailure, SAME_FAILURE_NUDGE_AT, SAME_FAILURE_BREAK_AT, SAME_FAILURE_NUDGE } from './chunk-RH4IUSTO.js';
2
+ import { resolveHarnessPolicy } from './chunk-5VBXHVEH.js';
3
+ import { formatWithHashlines } from './chunk-4MBR5GA3.js';
4
+ import { buildSystemPrompt } from './chunk-5EJM7EGY.js';
5
+ import { PRIMITIVES_DOCUMENTATION } from './chunk-HD6ABIK7.js';
6
+ import { createHarness, runHarness, APPLY_CHANGES_TOOL, GET_ICONS_TOOL, APPLY_CHANGES_TOOL_SCOPED, ICON_LIST_NO_MATCH_GUIDANCE, runCheck, REWRITE_TOOL, WRITE_PLAN_TOOL, GET_COMPONENTS_INFO_TOOL, APPLY_CHANGES_HASHLINE_TOOL_FLAT, APPLY_CHANGES_HASHLINE_TOOL, APPLY_CHANGES_TOOL_FLAT } from './chunk-WEOXFEVE.js';
7
+ import { runTier0Checks } from './chunk-FWJIMKFZ.js';
8
+ import { classifyAxes } from './chunk-5KXCQLLR.js';
9
+ import { criteriaContextFor } from './chunk-DXKRBXNU.js';
10
+ import { createAgent } from './chunk-O2YC6XDC.js';
11
+ import { traceAxisChecksSkipped, runGatedAxisChecks } from './chunk-U7GALWLG.js';
12
+ import { REGISTRY } from './chunk-GYINWMPR.js';
13
+ import { warmupRuntimeRenderProbe, DEFAULT_RUNTIME_RENDER_CHECK, isRecoverableRenderCrash, parseRuntimeSubcategory } from './chunk-BA4QYA6E.js';
14
+ import { RENDER_CHECK_KINDS } from './chunk-3NNNGNTO.js';
15
+ import { restoreReactImport } from './chunk-Z3CXME3K.js';
16
+ import { matches, getActionableIssues, notApplicableCoverage } from './chunk-PUH4CXGZ.js';
17
+ import { canvasForRendering, DEFAULT_DESIGN_MODE } from './chunk-3T2HMTON.js';
18
+ import { DEFAULT_HARNESS_POLICY } from './chunk-JMD4RGSV.js';
19
+ import * as esbuild from 'esbuild';
20
+ import { LUCIDE_ICON_NAMES } from '@ggui-ai/design';
21
+ import { listContractGadgets } from '@ggui-ai/protocol';
22
+ import git from 'isomorphic-git';
23
+ import { Volume, createFsFromVolume } from 'memfs';
24
+ import { createPatch, applyPatch } from 'diff';
25
+
26
+ // src/harness/coding/assemble-result.ts
27
+ async function assembleGenerationResult(input) {
28
+ const { session, telemetry, source } = input;
29
+ const a1Mode = session.harness.process.mode === "staged";
30
+ let needsBackgroundImprovement = false;
31
+ if (session.qualityMode === "auto-improve" && telemetry.evalResult && session.tiersMod) {
32
+ const remaining = getActionableIssues(
33
+ telemetry.evalResult,
34
+ "high-quality"
35
+ );
36
+ if (remaining.length > 0) {
37
+ needsBackgroundImprovement = true;
38
+ console.log(
39
+ `[simple] auto-improve: ${remaining.length} remaining issues \u2192 background improvement needed`
40
+ );
41
+ }
42
+ }
43
+ const totalMs = Date.now() - session.startedAtMs;
44
+ const evalWallMs = telemetry.cumulativeEvalWallMs;
45
+ const codingOnlyMs = Math.max(0, telemetry.codingMs - evalWallMs);
46
+ const setupMs = Math.max(0, totalMs - telemetry.codingMs);
47
+ const timing = {
48
+ codingMs: codingOnlyMs,
49
+ evalMs: evalWallMs,
50
+ setupMs,
51
+ totalMs
52
+ };
53
+ await session.codingAgent.cleanup();
54
+ console.log(
55
+ `[simple] timing: coding=${codingOnlyMs}ms eval=${evalWallMs}ms setup=${setupMs}ms total=${totalMs}ms`
56
+ );
57
+ console.log(
58
+ `[simple] DONE | ${totalMs}ms | turns=${telemetry.turnsUsed} | evalRounds=${telemetry.evalRoundsUsed} | tokens=${telemetry.totalIn + telemetry.totalOut} | compiled=${telemetry.compiledCode.length}B`
59
+ );
60
+ const { phases, outcomes } = telemetry.counters;
61
+ console.log(
62
+ `[simple] SUMMARY | turns=${telemetry.turnsUsed} impl=${phases.impl} patch=${phases.patch} evalFix=${phases.evalFix}` + (a1Mode ? ` scaffold=${phases.scaffold} fill=${phases.fill}` : "") + ` | pass=${outcomes.pass} patchInvalid=${outcomes.patchInvalid} selfCheckFail=${outcomes.selfCheckFail} diffFail=${outcomes.diffFail} | evalRounds=${telemetry.evalRoundsUsed} | codingLlmMs=${telemetry.cumulativeLlmMs} evalLlmMs=${telemetry.cumulativeEvalLlmMs} toolMs=${telemetry.cumulativeToolMs} evalWallMs=${evalWallMs}`
63
+ );
64
+ return {
65
+ compiledCode: telemetry.compiledCode,
66
+ sourceCode: source,
67
+ tokens: {
68
+ input: telemetry.totalIn,
69
+ output: telemetry.totalOut,
70
+ // ggui#1186: the FULL processed footprint — the adapters' convention
71
+ // (non-cached input + cache creation + cache reads + output) — so a
72
+ // row's cached count is `total − input − output` even where the cache
73
+ // fields are not published. `input + output` alone priced the cached
74
+ // prefix and hid it (a 36k-token luna turn published as 1.6k).
75
+ total: telemetry.totalIn + (telemetry.cacheCreationTokens ?? 0) + (telemetry.cacheReadTokens ?? 0) + telemetry.totalOut
76
+ },
77
+ // Prompt-cache counters are provider-specific — pass through truthfully.
78
+ // Absent on the telemetry (provider didn't report) stays absent here,
79
+ // never defaulted to 0, so the downstream observability ratio reflects
80
+ // real cache activity instead of a structural zero.
81
+ ...telemetry.cacheReadTokens !== void 0 ? { cacheReadTokens: telemetry.cacheReadTokens } : {},
82
+ ...telemetry.cacheCreationTokens !== void 0 ? { cacheCreationTokens: telemetry.cacheCreationTokens } : {},
83
+ // ggui#1522 — the in-loop visual judge's spend, beside `tokens` and never inside it.
84
+ ...telemetry.inLoopVisualTokens !== void 0 ? { inLoopVisualTokens: telemetry.inLoopVisualTokens } : {},
85
+ generationTimeMs: totalMs,
86
+ turnsUsed: telemetry.turnsUsed,
87
+ passesUsed: 1,
88
+ // ggui#1261 — the contract-feedback round's record rides the result the
89
+ // generation ends with (whose probe stamp is the post-round verdict).
90
+ evalResult: telemetry.evalResult !== void 0 && telemetry.contractFeedback !== void 0 ? { ...telemetry.evalResult, contractFeedback: telemetry.contractFeedback } : telemetry.evalResult,
91
+ // ggui#404 — present only when the same-exchange guard ended the run.
92
+ ...telemetry.sameExchangeBreak !== void 0 ? { sameExchangeBreak: telemetry.sameExchangeBreak } : {},
93
+ ...telemetry.sameFailureBreak !== void 0 ? { sameFailureBreak: telemetry.sameFailureBreak } : {},
94
+ needsBackgroundImprovement,
95
+ selfCheckPassed: telemetry.selfCheckPassed,
96
+ timing,
97
+ breakdown: {
98
+ phases: { ...phases },
99
+ outcomes: { ...outcomes },
100
+ evalRounds: telemetry.evalRoundsUsed,
101
+ // `llmMs` retained as coding-turn LLM (stable semantics for existing
102
+ // consumers in benchmarks/multi-sdk/runner.ts:312); `evalLlmMs` is
103
+ // new, covers eval-round parallel LLM+visual wall-time.
104
+ llmMs: telemetry.cumulativeLlmMs,
105
+ evalLlmMs: telemetry.cumulativeEvalLlmMs,
106
+ toolMs: telemetry.cumulativeToolMs,
107
+ evalMs: evalWallMs,
108
+ codingMs: codingOnlyMs,
109
+ setupMs
110
+ }
111
+ };
112
+ }
113
+
114
+ // src/coding-agent/self-check.ts
115
+ function getSoftWarnings(code) {
116
+ const warnings = [];
117
+ if (!/var\(--ggui-/.test(code)) {
118
+ warnings.push(
119
+ "uses_design_tokens: No CSS variables (var(--ggui-*)) found \u2014 consider using design system tokens"
120
+ );
121
+ }
122
+ if (!/aria-label/.test(code)) {
123
+ warnings.push(
124
+ "has_aria_labels: No aria-label attributes found \u2014 consider adding for accessibility"
125
+ );
126
+ }
127
+ return warnings;
128
+ }
129
+
130
+ // src/coding-agent/tools.ts
131
+ function axisCountsWithIds(issues) {
132
+ const idOf = (i) => i.subcategory ?? i.category;
133
+ const warns = issues.filter((i) => i.result === "warn").map(idOf);
134
+ const fails = issues.filter((i) => i.result !== "warn").map(idOf);
135
+ const groups = [...fails.length > 0 ? [`F:${fails.join(",")}`] : [], ...warns.length > 0 ? [`W:${warns.join(",")}`] : []];
136
+ return `${fails.length}F/${warns.length}W${groups.length > 0 ? `[${groups.join(" ")}]` : ""}`;
137
+ }
138
+ function getComponentDocumentation(name) {
139
+ const marker = `### ${name}`;
140
+ const startIdx = PRIMITIVES_DOCUMENTATION.indexOf(marker);
141
+ if (startIdx === -1) {
142
+ return `Component "${name}" not found. Available primitives: Container, Card, Stack, Row, Box, Text, Heading, Button, Input, Select, Checkbox, Toggle, Badge, Alert, Progress, Image, Icon, Divider, Tabs, Accordion, Table, Tooltip, Spinner, Avatar, Link`;
143
+ }
144
+ const nextMarker = PRIMITIVES_DOCUMENTATION.indexOf("### ", startIdx + marker.length);
145
+ const section = nextMarker === -1 ? PRIMITIVES_DOCUMENTATION.slice(startIdx) : PRIMITIVES_DOCUMENTATION.slice(startIdx, nextMarker);
146
+ return section.replace(/\\n/g, "\n").replace(/\\"/g, '"').slice(0, 3e3);
147
+ }
148
+ async function autoCommit(workspace, commitMeta, message, contract, contextPolicy, gadgetTypes, designMode = DEFAULT_DESIGN_MODE, axis) {
149
+ const commitStart = Date.now();
150
+ const raw = workspace.read();
151
+ if (!raw && raw !== "") {
152
+ return { result: "FAILED: no file to compile", error: true };
153
+ }
154
+ const restore = restoreReactImport(raw);
155
+ if (restore.added.length > 0) {
156
+ workspace.write(restore.source);
157
+ console.log(`[coding-agent] react-import restored: ${restore.added.map((h) => `+${h}`).join(",")}`);
158
+ }
159
+ const formatted = restore.source;
160
+ const buildStart = Date.now();
161
+ let buildSuccess = false;
162
+ let compiledCode = "";
163
+ const buildErrors = [];
164
+ try {
165
+ const result = await esbuild.transform(formatted, {
166
+ loader: "tsx",
167
+ target: "es2020",
168
+ format: "esm",
169
+ jsx: "automatic",
170
+ jsxImportSource: "react",
171
+ minify: true,
172
+ keepNames: true
173
+ });
174
+ compiledCode = result.code;
175
+ buildSuccess = true;
176
+ } catch (e) {
177
+ buildErrors.push(e instanceof Error ? e.message : String(e));
178
+ }
179
+ const buildMs = Date.now() - buildStart;
180
+ const buildResult = {
181
+ success: buildSuccess,
182
+ compiledCode: buildSuccess ? compiledCode : void 0,
183
+ errors: buildErrors.length > 0 ? buildErrors : void 0
184
+ };
185
+ const selfCheckStart = Date.now();
186
+ const tier0Issues = await runTier0Checks(
187
+ formatted,
188
+ contract,
189
+ buildResult.errors,
190
+ gadgetTypes,
191
+ designMode
192
+ );
193
+ const axisFacts = axis !== void 0 ? {
194
+ sourceCode: formatted,
195
+ ...contract !== void 0 ? { contract } : {},
196
+ originalPrompt: axis.originalPrompt,
197
+ classification: axis.classification,
198
+ designMode,
199
+ ...axis.canvas !== void 0 ? { canvas: axis.canvas } : {},
200
+ ...axis.fixtureProps !== void 0 ? { fixtureProps: axis.fixtureProps } : {}
201
+ } : void 0;
202
+ if (axisFacts !== void 0 && !buildSuccess) traceAxisChecksSkipped(axisFacts, "build failed \u2014 no check ran");
203
+ const axisRun = axis !== void 0 && axisFacts !== void 0 && buildSuccess ? runGatedAxisChecks(axis.checks, { ...axisFacts, compiledCode }) : void 0;
204
+ const axisIssues = axisRun?.issues ?? [];
205
+ const checkIssues = [...tier0Issues, ...axisIssues];
206
+ const tier0Fails = checkIssues.filter((i) => i.result === "fail");
207
+ const selfCheckPassed = tier0Fails.length === 0;
208
+ const violations = checkIssues.filter((i) => i.result === "fail").map((i) => contextPolicy?.labeledTier0 ? `[${i.priority ?? "P0"}-${i.category}] ${i.description}
209
+ Fix: ${i.fix}` : `[${i.category}] ${i.description}
210
+ Fix: ${i.fix}`);
211
+ const axisWarnings = axisIssues.filter((i) => i.result === "warn").map((i) => `[${i.subcategory}] ${i.description} Fix: ${i.fix}`);
212
+ const softWarnings = [...getSoftWarnings(raw), ...axisWarnings];
213
+ const selfCheckMs = Date.now() - selfCheckStart;
214
+ const gitStart = Date.now();
215
+ const oid = await workspace.commit(message);
216
+ commitMeta.set(oid, { build: buildResult, selfCheck: { passed: selfCheckPassed, violations } });
217
+ const gitMs = Date.now() - gitStart;
218
+ const status = buildSuccess && selfCheckPassed ? "PASS" : "FAIL";
219
+ console.log(
220
+ `[coding-agent] auto-commit: ${status} | build=${buildMs}ms self-check=${selfCheckMs}ms git=${gitMs}ms total=${Date.now() - commitStart}ms | violations=${violations.length} | axis=${axisRun === void 0 ? "off" : axisCountsWithIds(axisIssues)}`
221
+ );
222
+ if (violations.length > 0) {
223
+ for (const v of violations) {
224
+ console.log(`[coding-agent] \u2717 ${v}`);
225
+ }
226
+ }
227
+ if (buildSuccess && selfCheckPassed) {
228
+ const warnStr = softWarnings.length > 0 ? `
229
+ Warnings (non-blocking): ${softWarnings.join("; ")}` : "";
230
+ return {
231
+ result: `Committed ${oid.slice(0, 7)}: "${message}"
232
+ Build: OK
233
+ Self-check: PASS${warnStr}`,
234
+ done: true,
235
+ compiledCode
236
+ };
237
+ }
238
+ const errors = [
239
+ ...buildErrors.length > 0 ? [`Build errors:
240
+ ${buildErrors.join("\n")}`] : [],
241
+ ...!selfCheckPassed ? [`Self-check violations:
242
+ ${violations.join("\n")}`] : []
243
+ ];
244
+ return {
245
+ result: `Committed ${oid.slice(0, 7)}: "${message}"
246
+ ${errors.join("\n")}
247
+ Fix the issues.`
248
+ };
249
+ }
250
+ async function executeTool(workspace, tool, input, commitMeta, contract, applyPatch2, contextPolicy, gadgetTypes, designMode = DEFAULT_DESIGN_MODE, axis) {
251
+ switch (tool) {
252
+ case "write":
253
+ case "rewrite": {
254
+ const code = input.code;
255
+ if (!code && code !== "") {
256
+ console.warn(
257
+ `[coding-agent] write: no "code" field. Keys: [${Object.keys(input).join(", ")}]`
258
+ );
259
+ return {
260
+ result: `FAILED: write requires a "code" field. Received: [${Object.keys(input).join(", ")}]`,
261
+ error: true
262
+ };
263
+ }
264
+ workspace.write(code);
265
+ await workspace.stage();
266
+ const lineCount = code.split("\n").length;
267
+ const message = input.commit_message || `write ${lineCount} lines`;
268
+ console.log(`[coding-agent] write: ${lineCount} lines \u2192 auto-commit`);
269
+ return autoCommit(workspace, commitMeta, message, contract, contextPolicy, gadgetTypes, designMode, axis);
270
+ }
271
+ case "apply_changes": {
272
+ const currentFile = workspace.read();
273
+ if (!currentFile && currentFile !== "") {
274
+ return { result: "FAILED: No file exists.", error: true };
275
+ }
276
+ const inputChanges = input.changes;
277
+ const rawChanges = inputChanges ? Array.isArray(inputChanges) ? inputChanges : [inputChanges] : [];
278
+ const allowBroken = input.allowBroken === true;
279
+ const { parseHashlineRef, validateHashlineRefs, formatHashlineStaleMessage } = await import('./hashline-5OKREIRM.js');
280
+ const normalizedChanges = [];
281
+ for (let i = 0; i < (rawChanges ?? []).length; i++) {
282
+ const c = rawChanges[i];
283
+ let startLine;
284
+ let expectedStartHash;
285
+ if (typeof c.startLine === "string") {
286
+ const parsed = parseHashlineRef(c.startLine);
287
+ if (!parsed) {
288
+ return {
289
+ result: `FAILED: change[${i}].startLine = ${JSON.stringify(c.startLine)} is not a valid hashline ref (expected "N:hh" format).`,
290
+ error: true
291
+ };
292
+ }
293
+ startLine = parsed.line;
294
+ expectedStartHash = parsed.expectedHash;
295
+ } else {
296
+ startLine = c.startLine;
297
+ }
298
+ let endLine;
299
+ let expectedEndHash;
300
+ if (typeof c.endLine === "string") {
301
+ const parsed = parseHashlineRef(c.endLine);
302
+ if (!parsed) {
303
+ return {
304
+ result: `FAILED: change[${i}].endLine = ${JSON.stringify(c.endLine)} is not a valid hashline ref (expected "N:hh" format).`,
305
+ error: true
306
+ };
307
+ }
308
+ endLine = parsed.line;
309
+ expectedEndHash = parsed.expectedHash;
310
+ } else {
311
+ endLine = c.endLine;
312
+ }
313
+ normalizedChanges.push({
314
+ startLine,
315
+ endLine,
316
+ code: Array.isArray(c.code) ? c.code : typeof c.code === "string" ? c.code.split("\n") : [],
317
+ description: c.description ?? `change ${i + 1}`,
318
+ expectedStartHash,
319
+ expectedEndHash
320
+ });
321
+ }
322
+ const hashlineIssues = validateHashlineRefs(currentFile, normalizedChanges);
323
+ if (hashlineIssues.length > 0) {
324
+ const ranges = normalizedChanges.map((c) => `${c.startLine}-${c.endLine}`).join(", ");
325
+ console.log(
326
+ `[coding-agent] apply_changes: HASHLINE_STALE | ranges=${ranges} | ${hashlineIssues.length} mismatch(es)`
327
+ );
328
+ return {
329
+ result: formatHashlineStaleMessage(hashlineIssues),
330
+ error: true
331
+ };
332
+ }
333
+ const gutterLine = normalizedChanges.flatMap((c) => c.code).find((l) => /^\s*\d+(?::[0-9a-f]{2})?│/.test(l));
334
+ if (gutterLine !== void 0) {
335
+ console.log(
336
+ `[coding-agent] apply_changes: GUTTER_TRANSCRIBED | ${JSON.stringify(gutterLine.slice(0, 60))}`
337
+ );
338
+ return {
339
+ result: `PATCH_INVALID: your \`code\` contains the line-number gutter from the Current File view (e.g. ${JSON.stringify(gutterLine.trim().slice(0, 40))}). The \`N\u2502\` prefix is display decoration \u2014 \`code\` must be raw file content only. Re-emit the patch without the gutter.`,
340
+ error: true
341
+ };
342
+ }
343
+ const { defaultApplyPatch } = await import('./patch.js');
344
+ const patcher = applyPatch2 ?? defaultApplyPatch;
345
+ const patchResult = await patcher({
346
+ sourceBefore: currentFile,
347
+ changes: normalizedChanges
348
+ });
349
+ if (!patchResult.ok) {
350
+ console.log(`[coding-agent] apply_changes: REJECTED | ${patchResult.error}`);
351
+ return { result: `PATCH_INVALID: ${patchResult.error}`, error: true };
352
+ }
353
+ const candidate = patchResult.sourceAfter ?? currentFile;
354
+ const changes = [...normalizedChanges].sort((a, b) => a.startLine - b.startLine);
355
+ for (const c of changes) {
356
+ console.log(
357
+ `[coding-agent] change: lines ${c.startLine}-${c.endLine} \u2192 ${c.code.length} lines | ${c.description}`
358
+ );
359
+ }
360
+ const resultLines = candidate.split("\n");
361
+ let tagImbalanceSummary = null;
362
+ {
363
+ const { checkPatchTagBalance } = await import('./tag-balance-CTHCTRJ7.js');
364
+ const imbalance = checkPatchTagBalance(currentFile, changes);
365
+ if (imbalance.imbalanced) {
366
+ const ranges = changes.map((c) => `${c.startLine}-${c.endLine}`).join(", ");
367
+ const totalsSummary = imbalance.totals.map((d) => `${d.tag}${d.netDelta >= 0 ? "+" : ""}${d.netDelta}`).join(" ");
368
+ console.log(
369
+ `[coding-agent] apply_changes: tag-balance-diag | ranges=${ranges} | ${totalsSummary}`
370
+ );
371
+ tagImbalanceSummary = totalsSummary;
372
+ }
373
+ }
374
+ try {
375
+ await esbuild.transform(candidate, {
376
+ loader: "tsx",
377
+ target: "es2020",
378
+ format: "esm",
379
+ jsx: "automatic",
380
+ jsxImportSource: "react",
381
+ minify: true,
382
+ keepNames: true
383
+ });
384
+ } catch (e) {
385
+ const errMsg = e instanceof Error ? e.message : String(e);
386
+ const esErrs = e.errors ?? [];
387
+ const firstErr = esErrs[0];
388
+ const errLine = firstErr?.location?.line;
389
+ const errCol = firstErr?.location?.column;
390
+ const errText = firstErr?.text ?? errMsg.split("\n")[0];
391
+ const ranges = changes.map((c) => `${c.startLine}-${c.endLine}`).join(", ");
392
+ let sliceStart;
393
+ let sliceEnd;
394
+ let sliceLabel;
395
+ if (typeof errLine === "number" && errLine > 0) {
396
+ sliceStart = Math.max(0, errLine - 6);
397
+ sliceEnd = Math.min(resultLines.length, errLine + 5);
398
+ sliceLabel = `Candidate slice (\xB15 lines around esbuild error at line ${errLine}${typeof errCol === "number" ? `:${errCol}` : ""})`;
399
+ } else {
400
+ const first = changes[0];
401
+ sliceStart = Math.max(0, first.startLine - 6);
402
+ sliceEnd = Math.min(resultLines.length, first.endLine + 5);
403
+ sliceLabel = `Candidate slice (around first changed range)`;
404
+ }
405
+ const slice = resultLines.slice(sliceStart, sliceEnd).map((l, i) => {
406
+ const lineNum = sliceStart + i + 1;
407
+ const marker = lineNum === errLine ? " \u25C4\u2500\u2500 esbuild error here" : "";
408
+ return `${lineNum}\u2502${l}${marker}`;
409
+ }).join("\n");
410
+ workspace.write(candidate);
411
+ console.log(
412
+ `[coding-agent] apply_changes: APPLIED-BROKEN (preflight failed)${allowBroken ? " [allowBroken]" : ""} | ranges=${ranges} | line=${errLine ?? "?"} | ${errText}`
413
+ );
414
+ const preflightPrefix = contextPolicy?.labeledPreflight ? `[P0-compile] ` : ``;
415
+ const imbalanceHint = tagImbalanceSummary ? `
416
+
417
+ Patch tag-balance imbalance: ${tagImbalanceSummary}
418
+ (Net opens vs closes inside your edit ranges. If a tag has +N, you opened N more than you closed within the patch \u2014 verify each <Tag> has a matching </Tag> in your changes, or that the surrounding scaffold provides the closer.)` : "";
419
+ const contextAddendum = buildRangeContextAddendum(
420
+ errLine,
421
+ changes,
422
+ currentFile.split("\n")
423
+ );
424
+ if (contextAddendum.length > 0) {
425
+ console.log(
426
+ `[coding-agent] apply_changes: RANGE_CONTEXT_DIAG | line=${errLine} | ${JSON.stringify(contextAddendum.slice(0, 80))}`
427
+ );
428
+ }
429
+ return {
430
+ result: `${preflightPrefix}PATCH_APPLIED_BROKEN: patch applied but file has a syntax error. Workspace updated; no git commit yet.
431
+ Changed ranges: ${ranges}
432
+ esbuild error: ${errText}${typeof errLine === "number" ? ` (line ${errLine}${typeof errCol === "number" ? `, col ${errCol}` : ""})` : ""}
433
+
434
+ ${sliceLabel}:
435
+ ${slice}
436
+
437
+ The file now reflects your latest patch. Submit a follow-up apply_changes targeting the error location above to converge toward a compilable file.${contextAddendum}${imbalanceHint}`,
438
+ error: false
439
+ };
440
+ }
441
+ workspace.write(candidate);
442
+ await workspace.stage();
443
+ const message = input.commit_message || "apply changes";
444
+ console.log(`[coding-agent] apply_changes: ${changes.length} changes applied \u2192 auto-commit`);
445
+ return autoCommit(workspace, commitMeta, message, contract, contextPolicy, gadgetTypes, designMode, axis);
446
+ }
447
+ case "get_components_info": {
448
+ const names = input.names ?? [];
449
+ const docs = names.map((name) => getComponentDocumentation(name));
450
+ return { result: docs.join("\n\n---\n\n") };
451
+ }
452
+ case "write_plan": {
453
+ const components = input.components ?? [];
454
+ const structure = input.structure ?? "";
455
+ const wiring = input.wiring ?? "";
456
+ const summary = `PLAN_COMMITTED
457
+
458
+ Components: ${components.join(", ")}
459
+
460
+ Structure: ${structure}
461
+
462
+ Wiring: ${wiring}
463
+
464
+ On the next turn, use apply_changes to write the code. Fetch more component docs only if needed.`;
465
+ return { result: summary };
466
+ }
467
+ // `cat` is NOT advertised on the bench's coding-turn tool list —
468
+ // `run-coding-turn.ts::selectTurnTools` omits it because every turn's
469
+ // prompt already injects the current file as a `## Current File`
470
+ // block, so no read-tool is needed there. The case stays for
471
+ // dev-agent workflows that don't auto-inject file content.
472
+ case "cat":
473
+ return {
474
+ result: workspace.cat(
475
+ input.start_line,
476
+ input.end_line
477
+ )
478
+ };
479
+ case "grep":
480
+ return {
481
+ result: workspace.grep(
482
+ input.pattern,
483
+ input.context
484
+ )
485
+ };
486
+ case "diff":
487
+ return { result: await workspace.diffWorking() };
488
+ case "log": {
489
+ const commits = await workspace.log(input.depth);
490
+ if (commits.length === 0) return { result: "(no commits)" };
491
+ const lines = commits.map((c) => {
492
+ const meta = commitMeta.get(c.oid);
493
+ const status = meta?.selfCheck.passed ? "PASS" : meta?.selfCheck ? "FAIL" : "\u2014";
494
+ return `${c.oid.slice(0, 7)} [${status}] ${c.commit.message.trim()}`;
495
+ });
496
+ return { result: lines.join("\n") };
497
+ }
498
+ case "show": {
499
+ const oidPrefix = input.oid;
500
+ const commits = await workspace.log();
501
+ const idx = commits.findIndex((c) => c.oid.startsWith(oidPrefix));
502
+ if (idx === -1) {
503
+ return { result: `Commit not found: ${oidPrefix}`, error: true };
504
+ }
505
+ const thisOid = commits[idx].oid;
506
+ const parentOid = idx + 1 < commits.length ? commits[idx + 1].oid : null;
507
+ if (!parentOid) {
508
+ const content = await workspace.readFileAtCommit(thisOid);
509
+ return { result: `(initial commit)
510
+ ${content}` };
511
+ }
512
+ return { result: await workspace.diffBetween(parentOid, thisOid) };
513
+ }
514
+ case "revert": {
515
+ const oidPrefix = input.oid;
516
+ const commits = await workspace.log();
517
+ const match = commits.find((c) => c.oid.startsWith(oidPrefix));
518
+ if (!match) {
519
+ const available = commits.map((c) => c.oid.slice(0, 7)).join(", ");
520
+ return {
521
+ result: `OID not found: ${oidPrefix}. Available: ${available || "(none)"}`,
522
+ error: true
523
+ };
524
+ }
525
+ await workspace.checkout(match.oid);
526
+ return { result: `Reverted working copy to ${match.oid.slice(0, 7)}` };
527
+ }
528
+ default:
529
+ return { result: `Unknown tool: ${tool}`, error: true };
530
+ }
531
+ }
532
+ function buildRangeContextAddendum(errLine, changes, originalLines) {
533
+ if (typeof errLine !== "number") return "";
534
+ const hit = changes.find((c) => c.startLine === errLine);
535
+ if (!hit) return "";
536
+ const firstPayloadLine = hit.code.find((l) => l.trim().length > 0) ?? "";
537
+ const declMatch = /^\s*(const|let|var|function|type|interface)\b/.exec(
538
+ firstPayloadLine
539
+ );
540
+ if (declMatch) {
541
+ for (let i = hit.startLine - 2; i >= 0; i--) {
542
+ const line = originalLines[i];
543
+ if (line === void 0 || line.trim().length === 0) continue;
544
+ if (/\breturn\s*\($/.test(line.trim())) {
545
+ return `
546
+
547
+ Range/context mismatch: line ${i + 1} is \`return (\` \u2014 your range starts at ${hit.startLine}, INSIDE the returned JSX expression, but your payload begins with a declaration (\`${declMatch[1]}\`). Declarations cannot live inside JSX: start the range at or before line ${i + 1} and re-emit \`return (\` after them.`;
548
+ }
549
+ break;
550
+ }
551
+ }
552
+ if (hit.code.some((l) => l.includes("export default function"))) {
553
+ const existing = originalLines.findIndex(
554
+ (l) => l.includes("export default function")
555
+ );
556
+ if (existing >= 0 && existing + 1 < hit.startLine) {
557
+ return `
558
+
559
+ Range/context mismatch: your payload emits a second \`export default function\` but the range starts at line ${hit.startLine}, INSIDE the existing component that opens at line ${existing + 1}. Patch within the existing function body \u2014 do not re-emit the component definition.`;
560
+ }
561
+ }
562
+ return "";
563
+ }
564
+ function extractFailSignature(resultText) {
565
+ const esbuildMatch = resultText.match(/esbuild error: ([^\n]+)/);
566
+ if (esbuildMatch) return esbuildMatch[1];
567
+ const violationMatch = resultText.match(/^\[[A-Z][^\]]*\] [^\n]+/m);
568
+ if (violationMatch) return violationMatch[0];
569
+ const buildErrMatch = resultText.match(/Build errors:\s*\n([^\n]+)/);
570
+ if (buildErrMatch) return buildErrMatch[1];
571
+ return "";
572
+ }
573
+ function selectTurnTools(turnsUsed, forceEscape = false, consecutiveBrokenApplies = 0, hashlineMode = "off", primitiveIndexMode = "off", primitiveIndexForceFetch = false, primitiveIndexPlanTurn = false, planFirstTurn = false, codeFormat = "array") {
574
+ if (forceEscape) return [APPLY_CHANGES_TOOL_SCOPED];
575
+ if (consecutiveBrokenApplies >= 3) return [REWRITE_TOOL];
576
+ if (planFirstTurn && turnsUsed === 1) {
577
+ return [WRITE_PLAN_TOOL];
578
+ }
579
+ if (primitiveIndexForceFetch && primitiveIndexMode !== "off" && turnsUsed === 1) {
580
+ return [GET_COMPONENTS_INFO_TOOL];
581
+ }
582
+ if (primitiveIndexPlanTurn && primitiveIndexMode !== "off" && turnsUsed === 2) {
583
+ return [WRITE_PLAN_TOOL];
584
+ }
585
+ const applyTool = hashlineMode === "v2" ? codeFormat === "flat" ? APPLY_CHANGES_HASHLINE_TOOL_FLAT : APPLY_CHANGES_HASHLINE_TOOL : codeFormat === "flat" ? APPLY_CHANGES_TOOL_FLAT : APPLY_CHANGES_TOOL;
586
+ const tools = [applyTool, GET_ICONS_TOOL];
587
+ if (primitiveIndexMode !== "off") tools.push(GET_COMPONENTS_INFO_TOOL);
588
+ return tools;
589
+ }
590
+ async function runCodingTurn(ctx, input) {
591
+ const {
592
+ workspace,
593
+ codingAgent,
594
+ codingModel,
595
+ systemPrompt,
596
+ harness,
597
+ contract,
598
+ commitMeta,
599
+ originalProps,
600
+ costTracker
601
+ } = ctx;
602
+ const {
603
+ turnsUsed,
604
+ a1Phase,
605
+ lastResultText,
606
+ lastDiffFailed,
607
+ isEvalFeedback
608
+ } = input;
609
+ let { iconNamesCache, preWarmedContext } = input;
610
+ if (originalProps) {
611
+ const current = workspace.read() ?? "";
612
+ const currentProps = current.match(/interface Props \{[\s\S]*?\n\}/)?.[0];
613
+ if (currentProps && currentProps !== originalProps) {
614
+ workspace.write(current.replace(currentProps, originalProps));
615
+ await workspace.stage();
616
+ }
617
+ }
618
+ const hashlineMode = ctx.contextPolicy?.hashline ?? "off";
619
+ const currentFile = hashlineMode === "v2" ? formatWithHashlines(workspace.read() ?? "") : workspace.cat();
620
+ let userPrompt;
621
+ if (a1Phase === "scaffold") {
622
+ userPrompt = `Produce a COMPILEABLE structural first pass. This is turn 1 of 2 \u2014 you are NOT implementing the feature yet.
623
+
624
+ ## Required this turn (non-negotiable)
625
+
626
+ 1. Declare all hooks from the boilerplate: \`useState\` for local state you'll need, plus every \`useAction\` / \`useStream\` / \`useGguiContext\` from the contract, plus any capability hooks declared in \`clientCapabilities\` (from \`@ggui-ai/gadgets\`)
627
+ 2. Build a minimal valid JSX skeleton \u2014 one top-level \`<Container>\` or \`<Card>\` with a \`<Stack>\` of empty-but-valid region containers (one per logical section of the UI)
628
+ 3. Keep the \`export default function\` signature + \`Props\` interface exactly as in the boilerplate
629
+
630
+ ## NOT required yet (next turn will fill these in)
631
+
632
+ - Rendering prop values or real content
633
+ - Wiring hook callbacks to UI elements (empty \`onClick={() => {}}\` is fine)
634
+ - Styling polish, icons, badges, variants
635
+ - Interactive flows and derived state
636
+
637
+ **Goal: compile + pass syntactic checks. Broken JSX wastes the whole round-trip \u2014 keep it minimal and valid.**
638
+
639
+ ## Current File
640
+ \`\`\`tsx
641
+ ${currentFile}
642
+ \`\`\``;
643
+ } else if (a1Phase === "fill") {
644
+ userPrompt = `The scaffold compiled. Now FILL in the details against the compiled skeleton. Make minimal, targeted patches \u2014 don't rewrite the whole file.
645
+
646
+ ## Required this turn (non-negotiable)
647
+
648
+ 1. Render every required prop from the Props interface in the appropriate region
649
+ 2. Wire every hook to a real interactive element with the correct payload shape
650
+ 3. Add real content \u2014 text, labels, icons, variants \u2014 so the UI matches the user's request
651
+
652
+ ## Allowed to simplify (ONLY flourishes/polish)
653
+
654
+ - Elaborate animations \u2192 plain CSS transitions or static styles
655
+ - Rare empty states \u2192 skip the branch
656
+ - Fancy layout refinements \u2192 simpler grid/stack
657
+
658
+ **Preserve the scaffolded region containers wherever possible.** Patch surgically inside them instead of rewriting the tree. Broken JSX or type errors waste a whole round-trip.
659
+
660
+ ## Current File (scaffolded, compiles)
661
+ \`\`\`tsx
662
+ ${currentFile}
663
+ \`\`\``;
664
+ } else if (turnsUsed === 1) {
665
+ userPrompt = `Deliver a compileable, end-to-end first pass in one turn.
666
+
667
+ ## Required this turn (non-negotiable)
668
+
669
+ 1. JSX structure that parses \u2014 balanced tags, closed fragments, no stray braces
670
+ 2. Render every required prop from the Props interface
671
+ 3. Wire every \`useAction\` / \`useStream\` / \`useGguiContext\` hook from the boilerplate to a visible element. If the contract declared \`clientCapabilities\`, the boilerplate emits a capability-hook call for each (e.g., \`const loc = useGeolocation();\`); call \`.start()\` from a UI control and render the result.
672
+
673
+ ## Allowed to simplify (ONLY flourishes/polish \u2014 NOT core work above)
674
+
675
+ - Elaborate animations \u2192 plain CSS transitions or static styles
676
+ - Rare empty states \u2192 skip the branch entirely
677
+ - Fancy layout refinements \u2192 simpler grid/stack
678
+
679
+ **If a flourish risks broken JSX or type errors, OMIT it rather than leaving a placeholder or partial structure.** No TODOs, no half-finished helpers, no stub components. Broken syntax or missing core work wastes a whole round-trip.
680
+
681
+ Read the boilerplate comments \u2014 each hook has its type and call signature inline. Follow P0 \u2192 P1 \u2192 P2 priority from the system prompt.
682
+
683
+ ## Current File
684
+ \`\`\`tsx
685
+ ${currentFile}
686
+ \`\`\``;
687
+ } else {
688
+ const issues = lastResultText;
689
+ const diffHint = lastDiffFailed ? `
690
+ **Your last diff did not apply.** Make sure context lines match the current file exactly. Use 3 lines of context around each change.
691
+ ` : "";
692
+ if (!preWarmedContext && input.preWarmPromise) {
693
+ preWarmedContext = await Promise.race([
694
+ input.preWarmPromise,
695
+ new Promise((r) => setTimeout(r, 0, null))
696
+ ]);
697
+ }
698
+ const issueHeader = isEvalFeedback ? "## Evaluation Issues (fix all)" : "## Issues (fix all)";
699
+ if (ctx.dupeBreak?.pendingDiagnosticTurn) {
700
+ ctx.dupeBreak.pendingDiagnosticTurn = false;
701
+ ctx.dupeBreak.awaitingDiagnosticOutcome = true;
702
+ const recents = ctx.dupeBreak.recentFailedPatches;
703
+ const head = recents[0];
704
+ const lines = currentFile.split("\n");
705
+ const firstRange = head?.rangesShort.split(",")[0]?.trim() ?? "";
706
+ const m = /^(\d+)-(\d+)$/.exec(firstRange);
707
+ const focusStart = m ? Math.max(1, parseInt(m[1], 10) - 15) : 1;
708
+ const focusEnd = m ? Math.min(lines.length, parseInt(m[2], 10) + 15) : lines.length;
709
+ const numbered = lines.slice(focusStart - 1, focusEnd).map((line, i) => `${String(focusStart + i).padStart(4, "0")}: ${line}`).join("\n");
710
+ const priorBlock = recents.map(
711
+ (p, i) => ` Attempt ${recents.length - i}: ranges=[${p.rangesShort}] error="${p.errClass}" fp=${p.fingerprint.slice(0, 12)}`
712
+ ).join("\n");
713
+ const promptPolicy = ctx.contextPolicy ?? harness.policy.context;
714
+ const noForce = promptPolicy.dupeBreakAction === "diagnostic-noforce";
715
+ const step2 = noForce ? `2. Then call \`apply_changes\`. The information above (line numbers + prior fingerprints) should let you see exactly what's wrong. If your patch turns out to be the same one you tried before, that's fine \u2014 but make sure you've verified line-by-line against the focused excerpt that the patch is actually correct.` : `2. Then call \`apply_changes\` with a patch that **differs materially** from your prior failed attempt \u2014 different ranges OR meaningfully different code at the same range. Re-emitting the same patch is not allowed.`;
716
+ userPrompt = `[LOOP DETECTED] Your last patch was byte-identical to a prior failed patch and produced the same preflight error. You are stuck in a fixed point \u2014 apply_changes alone won't escape this loop unless you re-examine the file.
717
+
718
+ ## Prior failed attempts (most recent first)
719
+ ${priorBlock}
720
+
721
+ ## Failing region \u2014 focused excerpt (lines ${focusStart}\u2013${focusEnd}, line-numbered)
722
+ \`\`\`tsx
723
+ ${numbered}
724
+ \`\`\`
725
+
726
+ ## What to emit on this turn
727
+
728
+ 1. Set \`commit_message\` to a one-line structured diagnosis in this exact format:
729
+ \`DIAG: why=<why the prior patch failed, \u226480 chars> | next=<what you will change instead, \u226480 chars> | lines=<startLine-endLine you will target>\`
730
+ ${step2}
731
+
732
+ ## Issues from prior turn (full text \u2014 for context)
733
+ ${issues}
734
+ `;
735
+ } else {
736
+ const batchFixForced = typeof process !== "undefined" && process.env?.GGUI_BATCH_FIX === "on";
737
+ const closingInstruction = batchFixForced ? `**Fix EVERY error listed above in THIS single \`apply_changes\` call.** Emit multiple \`changes[]\` entries \u2014 one per range.` : `Produce ONE \`apply_changes\` call targeting only the lines that need fixing.`;
738
+ userPrompt = `Fix the issues below. Make **minimal, targeted changes** \u2014 do NOT rewrite the whole file.${diffHint}
739
+
740
+ ${issueHeader}
741
+ ${issues}
742
+
743
+ ## Current File
744
+ \`\`\`tsx
745
+ ${currentFile}
746
+ \`\`\`
747
+
748
+ ${closingInstruction}`;
749
+ }
750
+ }
751
+ const forceEscape = ctx.dupeBreak?.forceEscapeNextTurn === true;
752
+ if (ctx.dupeBreak && forceEscape) ctx.dupeBreak.forceEscapeNextTurn = false;
753
+ if (ctx.dupeBreak && ctx.dupeBreak.cooldown > 0) ctx.dupeBreak.cooldown--;
754
+ const consecutiveBrokenApplies = ctx.dupeBreak?.consecutiveBrokenApplies ?? 0;
755
+ const primitiveIndexMode = ctx.contextPolicy?.primitiveIndex ?? "off";
756
+ const primitiveIndexForceFetch = ctx.contextPolicy?.primitiveIndexForceFetch ?? false;
757
+ const primitiveIndexPlanTurn = ctx.contextPolicy?.primitiveIndexPlanTurn ?? false;
758
+ const planFirstTurn = ctx.contextPolicy?.planFirstTurn ?? false;
759
+ const codeFormat = ctx.contextPolicy?.codeFormat ?? "array";
760
+ const tools = selectTurnTools(
761
+ turnsUsed,
762
+ forceEscape,
763
+ consecutiveBrokenApplies,
764
+ hashlineMode,
765
+ primitiveIndexMode,
766
+ primitiveIndexForceFetch,
767
+ primitiveIndexPlanTurn,
768
+ planFirstTurn,
769
+ codeFormat
770
+ );
771
+ ctx.onPrompt?.({ turn: turnsUsed, systemPrompt, userPrompt });
772
+ const llmStart = Date.now();
773
+ const response = await codingAgent.callTools(
774
+ codingModel,
775
+ systemPrompt,
776
+ userPrompt,
777
+ tools,
778
+ "required",
779
+ [APPLY_CHANGES_TOOL_SCOPED]
780
+ );
781
+ const llmMs = Date.now() - llmStart;
782
+ if (costTracker) {
783
+ costTracker.record(codingModel, response.inputTokens, response.outputTokens, {
784
+ ...response.cacheReadTokens !== void 0 ? { read: response.cacheReadTokens } : {},
785
+ ...response.cacheCreationTokens !== void 0 ? { write: response.cacheCreationTokens } : {}
786
+ });
787
+ }
788
+ const tokens = { input: response.inputTokens, output: response.outputTokens };
789
+ const cacheTokens = {
790
+ ...response.cacheReadTokens !== void 0 ? { cacheReadTokens: response.cacheReadTokens } : {},
791
+ ...response.cacheCreationTokens !== void 0 ? { cacheCreationTokens: response.cacheCreationTokens } : {}
792
+ };
793
+ const call = response.toolCalls[0];
794
+ if (!call) {
795
+ if (ctx.dupeBreak) {
796
+ ctx.dupeBreak.consecutiveNoToolCalls += 1;
797
+ }
798
+ const noToolStreak = ctx.dupeBreak?.consecutiveNoToolCalls ?? 1;
799
+ const shouldBreak = noToolStreak >= 2;
800
+ console.warn(
801
+ `[simple] turn ${turnsUsed}: no tool call` + (shouldBreak ? ` \u2014 breaking (streak=${noToolStreak})` : ` \u2014 retrying (streak=${noToolStreak})`)
802
+ );
803
+ const retryDirective = shouldBreak ? lastResultText : `${lastResultText ?? ""}
804
+
805
+ [harness] Your previous turn returned no tool call. You MUST issue an apply_changes (or scoped) tool call. Re-read the current file state and emit a corrected patch.`.trim();
806
+ return {
807
+ control: shouldBreak ? "break" : "continue",
808
+ compiledCode: "",
809
+ selfCheckPassed: false,
810
+ outcome: void 0,
811
+ phase: void 0,
812
+ tokens,
813
+ ...cacheTokens,
814
+ llmMs,
815
+ toolMs: 0,
816
+ lastResultText: retryDirective,
817
+ lastDiffFailed,
818
+ isEvalFeedback,
819
+ iconNamesCache,
820
+ preWarmedContext
821
+ };
822
+ }
823
+ if (ctx.dupeBreak) {
824
+ ctx.dupeBreak.consecutiveNoToolCalls = 0;
825
+ }
826
+ if (call.name === "get_available_icons") {
827
+ if (!iconNamesCache) {
828
+ iconNamesCache = LUCIDE_ICON_NAMES.join(", ");
829
+ }
830
+ console.log(`[simple] turn ${turnsUsed}: get_available_icons`);
831
+ let iconResult = `Available Lucide icon names (use with <Icon name="...">):
832
+ ${iconNamesCache}
833
+
834
+ ${ICON_LIST_NO_MATCH_GUIDANCE}`;
835
+ let iconRepeats = 0;
836
+ if (ctx.dupeBreak) {
837
+ iconRepeats = recordToolExchange(ctx.dupeBreak, call.name, call.input, iconResult);
838
+ if (iconRepeats === 2) iconResult += SAME_EXCHANGE_NUDGE;
839
+ }
840
+ await codingAgent.sendToolResult([{ callId: call.id, name: call.name, result: iconResult }]);
841
+ if (iconRepeats >= SAME_EXCHANGE_BREAK_AT) {
842
+ console.log(
843
+ `[coding-agent] SAME_EXCHANGE_BREAK | tool=${call.name} | repeats=${iconRepeats} \u2014 terminating run (ggui#404)`
844
+ );
845
+ return {
846
+ control: "break",
847
+ compiledCode: "",
848
+ selfCheckPassed: false,
849
+ outcome: void 0,
850
+ phase: void 0,
851
+ tokens,
852
+ ...cacheTokens,
853
+ llmMs,
854
+ toolMs: 0,
855
+ lastResultText: iconResult,
856
+ lastDiffFailed: false,
857
+ isEvalFeedback: false,
858
+ iconNamesCache,
859
+ preWarmedContext,
860
+ sameExchangeBreak: { tool: call.name, repeats: iconRepeats }
861
+ };
862
+ }
863
+ return {
864
+ control: "continue",
865
+ compiledCode: "",
866
+ selfCheckPassed: false,
867
+ outcome: void 0,
868
+ phase: void 0,
869
+ tokens,
870
+ ...cacheTokens,
871
+ llmMs,
872
+ toolMs: 0,
873
+ lastResultText: iconResult,
874
+ lastDiffFailed: false,
875
+ isEvalFeedback: false,
876
+ iconNamesCache,
877
+ preWarmedContext
878
+ };
879
+ }
880
+ const toolStart = Date.now();
881
+ const result = await executeTool(
882
+ workspace,
883
+ call.name,
884
+ call.input,
885
+ commitMeta,
886
+ contract,
887
+ harness.what.applyPatch,
888
+ // Prefer the caller-threaded runtime-resolved policy
889
+ // (session.resolvedPolicy.context) so experimental profiles take
890
+ // effect. Falls back to the harness's static policy.
891
+ ctx.contextPolicy ?? harness.policy.context,
892
+ // Forward the third-party wrapper `.d.ts` map so autoCommit's
893
+ // typecheck overlays the real wrapper declarations and a generated
894
+ // direct gadget import gets strict option/return narrowing.
895
+ harness.what.gadgetTypes,
896
+ // Which tier-0 legs fire — the design-vocabulary legs stand down in
897
+ // `free` mode; every contract leg runs in both.
898
+ harness.designMode,
899
+ // ggui#1122: the harness's pre-filtered axis checks run at auto-commit — the serve path.
900
+ {
901
+ checks: harness.check.axisChecks,
902
+ classification: harness.classification,
903
+ originalPrompt: ctx.originalPrompt,
904
+ ...harness.canvas !== void 0 ? { canvas: harness.canvas } : {},
905
+ ...ctx.fixtureProps !== void 0 ? { fixtureProps: ctx.fixtureProps } : {}
906
+ }
907
+ );
908
+ const toolMs = Date.now() - toolStart;
909
+ if (forceEscape && ctx.dupeBreak && call.name === "apply_changes") {
910
+ const changes = call.input?.changes ?? [];
911
+ const scopedShape = changes.length === 1 && changes[0] !== void 0 && changes[0].endLine - changes[0].startLine <= 20;
912
+ if (scopedShape) {
913
+ ctx.dupeBreak.scopedEscapeUsedCount++;
914
+ console.log(
915
+ `[coding-agent] SCOPED_ESCAPE_USED | range=${changes[0]?.startLine}-${changes[0]?.endLine} | count=${ctx.dupeBreak.scopedEscapeUsedCount}`
916
+ );
917
+ }
918
+ }
919
+ const effectivePolicy = ctx.contextPolicy ?? harness.policy.context;
920
+ const isPatchInvalid = !!result.error && result.result.startsWith("PATCH_INVALID");
921
+ const isApplyChanges = call.name === "apply_changes";
922
+ const isFailedPatch = !result.done && isApplyChanges;
923
+ if (ctx.dupeBreak?.awaitingDiagnosticOutcome) {
924
+ ctx.dupeBreak.awaitingDiagnosticOutcome = false;
925
+ const commitMsg = typeof call.input?.commit_message === "string" ? call.input.commit_message : "";
926
+ const returnedDiag = /^\s*DIAG\s*:/i.test(commitMsg);
927
+ if (returnedDiag) {
928
+ ctx.dupeBreak.diagnosticReturnedCount++;
929
+ console.log(
930
+ `[coding-agent] DUPE_DIAGNOSTIC_RETURNED | msg=${JSON.stringify(commitMsg.slice(0, 120))}`
931
+ );
932
+ } else {
933
+ console.log(
934
+ `[coding-agent] DUPE_DIAGNOSTIC_RETURNED=false | msg=${JSON.stringify(commitMsg.slice(0, 120))}`
935
+ );
936
+ }
937
+ const dupeFp = ctx.dupeBreak.lastFailedPatchFingerprint;
938
+ let brokeLoop = false;
939
+ if (result.done) {
940
+ brokeLoop = true;
941
+ } else if (isApplyChanges && dupeFp) {
942
+ const { computePatchFingerprint } = await import('./dupe-break-X6K7QFIS.js');
943
+ const changes2 = call.input?.changes ?? [];
944
+ const errText2 = extractFailSignature(result.result);
945
+ const currentFp = computePatchFingerprint(changes2, errText2);
946
+ brokeLoop = currentFp !== dupeFp;
947
+ } else {
948
+ brokeLoop = true;
949
+ }
950
+ if (brokeLoop) {
951
+ ctx.dupeBreak.diagnosticBrokeLoopCount++;
952
+ console.log(`[coding-agent] DUPE_DIAGNOSTIC_BROKE_LOOP=true`);
953
+ } else {
954
+ console.log(`[coding-agent] DUPE_DIAGNOSTIC_BROKE_LOOP=false`);
955
+ }
956
+ }
957
+ let augmentedResultText = result.result;
958
+ if (isFailedPatch && effectivePolicy?.breakDuplicatePatch && ctx.dupeBreak) {
959
+ const { computePatchFingerprint } = await import('./dupe-break-X6K7QFIS.js');
960
+ const changes = call.input?.changes ?? [];
961
+ const errText = extractFailSignature(result.result);
962
+ const fingerprint = computePatchFingerprint(changes, errText);
963
+ const priorPrint = ctx.dupeBreak.lastFailedPatchFingerprint;
964
+ const inCooldown = ctx.dupeBreak.cooldown > 0;
965
+ if (priorPrint && priorPrint === fingerprint && !inCooldown) {
966
+ ctx.dupeBreak.cooldown = 3;
967
+ ctx.dupeBreak.firedCount++;
968
+ const action = effectivePolicy.dupeBreakAction ?? "escape";
969
+ console.log(
970
+ `[coding-agent] DUPLICATE_PATCH_BREAK fired | action=${action} | fingerprint=${fingerprint.slice(0, 12)} error=${JSON.stringify(errText.slice(0, 48))}`
971
+ );
972
+ const codeHead = (() => {
973
+ const sorted = [...changes].sort(
974
+ (a, b) => a.startLine - b.startLine || a.endLine - b.endLine
975
+ );
976
+ const flat = sorted.map((c) => Array.isArray(c.code) ? c.code.join("\n") : String(c.code ?? "")).join("\n");
977
+ return flat.slice(0, 200);
978
+ })();
979
+ const rangesShort = [...changes].sort((a, b) => a.startLine - b.startLine || a.endLine - b.endLine).map((c) => `${c.startLine}-${c.endLine}`).join(", ");
980
+ const errClass = (errText || "unknown").toLowerCase().slice(0, 60);
981
+ const summary = { rangesShort, codeHead, errClass, fingerprint };
982
+ ctx.dupeBreak.recentFailedPatches = [
983
+ summary,
984
+ ...ctx.dupeBreak.recentFailedPatches
985
+ ].slice(0, 2);
986
+ if (action === "diagnostic" || action === "diagnostic-noforce") {
987
+ ctx.dupeBreak.pendingDiagnosticTurn = true;
988
+ ctx.dupeBreak.diagnosticFiredCount++;
989
+ augmentedResultText = `[DUPLICATE_PATCH] Identical failed patch detected.
990
+ Your previous patch was byte-identical to one you tried before and failed for the same reason. The next prompt will give you a focused excerpt of the failing region with line numbers and require a one-line structured diagnosis (in commit_message) before your next patch. Your next apply_changes MUST differ materially from the prior failed patch \u2014 different ranges OR meaningfully different code.
991
+
992
+ Repeated error: ${errText}
993
+
994
+ ---
995
+
996
+ ` + result.result;
997
+ } else {
998
+ ctx.dupeBreak.forceEscapeNextTurn = true;
999
+ augmentedResultText = `[DUPLICATE_PATCH] Identical failed patch detected.
1000
+ Your previous patch failed with the same error, and this retry is byte-identical.
1001
+ The next turn is FORCED to a scoped single-change apply_changes (one change, \u226420 lines). Target the minimal failing region \u2014 do not retry the full patch. The rest of the file is correct.
1002
+
1003
+ Repeated error: ${errText}
1004
+
1005
+ ---
1006
+
1007
+ ` + result.result;
1008
+ }
1009
+ }
1010
+ ctx.dupeBreak.lastFailedPatchFingerprint = fingerprint;
1011
+ }
1012
+ if (result.done && ctx.dupeBreak?.lastFailedPatchFingerprint) {
1013
+ ctx.dupeBreak.lastFailedPatchFingerprint = null;
1014
+ }
1015
+ if (ctx.dupeBreak && call.name === "apply_changes") {
1016
+ const wasBrokenApply = result.result.startsWith("PATCH_APPLIED_BROKEN");
1017
+ if (wasBrokenApply) {
1018
+ ctx.dupeBreak.consecutiveBrokenApplies++;
1019
+ } else if (!result.error) {
1020
+ ctx.dupeBreak.consecutiveBrokenApplies = 0;
1021
+ }
1022
+ } else if (ctx.dupeBreak && call.name === "rewrite") {
1023
+ if (!result.error) ctx.dupeBreak.consecutiveBrokenApplies = 0;
1024
+ }
1025
+ let sameExchangeRepeats = 0;
1026
+ if (ctx.dupeBreak) {
1027
+ sameExchangeRepeats = recordToolExchange(
1028
+ ctx.dupeBreak,
1029
+ call.name,
1030
+ call.input,
1031
+ result.result
1032
+ );
1033
+ if (sameExchangeRepeats === 2) {
1034
+ augmentedResultText += SAME_EXCHANGE_NUDGE;
1035
+ }
1036
+ }
1037
+ const isWrite = call.name === "apply_changes" || call.name === "rewrite";
1038
+ let repeatedFailure;
1039
+ if (ctx.dupeBreak && isWrite) {
1040
+ if (result.done) {
1041
+ clearWriteFailures(ctx.dupeBreak);
1042
+ } else {
1043
+ repeatedFailure = recordWriteFailure(ctx.dupeBreak, result.result);
1044
+ if (repeatedFailure?.repeats === SAME_FAILURE_NUDGE_AT) {
1045
+ augmentedResultText += SAME_FAILURE_NUDGE;
1046
+ }
1047
+ }
1048
+ }
1049
+ await codingAgent.sendToolResult([{
1050
+ callId: call.id,
1051
+ name: call.name,
1052
+ result: augmentedResultText,
1053
+ isError: !!result.error
1054
+ }]);
1055
+ if (sameExchangeRepeats >= SAME_EXCHANGE_BREAK_AT) {
1056
+ console.log(
1057
+ `[coding-agent] SAME_EXCHANGE_BREAK | tool=${call.name} | repeats=${sameExchangeRepeats} \u2014 terminating run (ggui#404)`
1058
+ );
1059
+ return {
1060
+ control: "break",
1061
+ compiledCode: "",
1062
+ selfCheckPassed: false,
1063
+ outcome: void 0,
1064
+ phase: void 0,
1065
+ tokens,
1066
+ ...cacheTokens,
1067
+ llmMs,
1068
+ toolMs,
1069
+ lastResultText: augmentedResultText,
1070
+ lastDiffFailed: false,
1071
+ isEvalFeedback: false,
1072
+ iconNamesCache,
1073
+ preWarmedContext,
1074
+ sameExchangeBreak: { tool: call.name, repeats: sameExchangeRepeats }
1075
+ };
1076
+ }
1077
+ if (repeatedFailure !== void 0 && repeatedFailure.repeats >= SAME_FAILURE_BREAK_AT) {
1078
+ const signature = repeatedFailure.signature.slice(0, 160);
1079
+ console.log(
1080
+ `[coding-agent] SAME_FAILURE_BREAK | tool=${call.name} | repeats=${repeatedFailure.repeats} | failure=${JSON.stringify(signature.slice(0, 80))} \u2014 terminating run (ggui#1791)`
1081
+ );
1082
+ return {
1083
+ control: "break",
1084
+ compiledCode: "",
1085
+ selfCheckPassed: false,
1086
+ outcome: void 0,
1087
+ phase: void 0,
1088
+ tokens,
1089
+ ...cacheTokens,
1090
+ llmMs,
1091
+ toolMs,
1092
+ lastResultText: augmentedResultText,
1093
+ lastDiffFailed: false,
1094
+ isEvalFeedback: false,
1095
+ iconNamesCache,
1096
+ preWarmedContext,
1097
+ sameFailureBreak: { signature, repeats: repeatedFailure.repeats }
1098
+ };
1099
+ }
1100
+ const turnDiffFailed = !!result.error && result.result.includes("DIFF");
1101
+ const isDiffFail = turnDiffFailed && !isPatchInvalid;
1102
+ const outcome = result.done ? "PASS" : isPatchInvalid ? "PATCH_INVALID" : isDiffFail ? "DIFF_FAIL" : "SELF_CHECK_FAIL";
1103
+ const phase = a1Phase === "scaffold" ? "scaffold" : a1Phase === "fill" ? "fill" : turnsUsed === 1 ? "impl" : isEvalFeedback ? "eval-fix" : "patch";
1104
+ console.log(
1105
+ `[simple] turn ${turnsUsed} (${phase}): ${outcome} | llm=${llmMs}ms tool=${toolMs}ms | in=${response.inputTokens} out=${response.outputTokens}`
1106
+ );
1107
+ if (!result.done) {
1108
+ const preview = JSON.stringify(call.input).slice(0, 150);
1109
+ console.log(`[simple] turn ${turnsUsed} preview: ${preview}...`);
1110
+ }
1111
+ if (!result.done) {
1112
+ return {
1113
+ control: "continue",
1114
+ compiledCode: "",
1115
+ selfCheckPassed: false,
1116
+ outcome,
1117
+ phase,
1118
+ tokens,
1119
+ ...cacheTokens,
1120
+ llmMs,
1121
+ toolMs,
1122
+ // Use the augmented text so the next turn's prompt gets the
1123
+ // [DUPLICATE_PATCH] preface when dupe-break fired (noop otherwise).
1124
+ lastResultText: augmentedResultText,
1125
+ lastDiffFailed: turnDiffFailed,
1126
+ isEvalFeedback: false,
1127
+ iconNamesCache,
1128
+ preWarmedContext
1129
+ };
1130
+ }
1131
+ let compiledCode = "";
1132
+ let selfCheckPassed = false;
1133
+ for (const [, meta] of commitMeta) {
1134
+ if (meta.build.success && meta.selfCheck.passed && meta.build.compiledCode) {
1135
+ compiledCode = meta.build.compiledCode;
1136
+ selfCheckPassed = true;
1137
+ }
1138
+ }
1139
+ if (!compiledCode) {
1140
+ for (const [, meta] of commitMeta) {
1141
+ if (meta.build.success && meta.build.compiledCode) {
1142
+ compiledCode = meta.build.compiledCode;
1143
+ }
1144
+ }
1145
+ }
1146
+ return {
1147
+ control: "proceed",
1148
+ compiledCode,
1149
+ selfCheckPassed,
1150
+ outcome,
1151
+ phase,
1152
+ tokens,
1153
+ ...cacheTokens,
1154
+ llmMs,
1155
+ toolMs,
1156
+ lastResultText,
1157
+ lastDiffFailed: turnDiffFailed,
1158
+ isEvalFeedback,
1159
+ iconNamesCache,
1160
+ preWarmedContext
1161
+ };
1162
+ }
1163
+
1164
+ // src/harness/enforced-coding.ts
1165
+ function mapProviderForEvaluator(provider) {
1166
+ return provider === "anthropic" ? "claude" : provider;
1167
+ }
1168
+ function visionJudgeProvider(provider) {
1169
+ switch (provider) {
1170
+ case "anthropic":
1171
+ return "claude";
1172
+ case "google":
1173
+ return "google";
1174
+ case "openai":
1175
+ case "openrouter":
1176
+ return void 0;
1177
+ }
1178
+ }
1179
+
1180
+ // src/harness/coding/run-eval-round.ts
1181
+ var MAX_FEEDBACK_ISSUES = 3;
1182
+ function isFitFail(issue) {
1183
+ return issue.result === "fail" && issue.category === "visual" && issue.subcategory === "canvas-overflow";
1184
+ }
1185
+ var FIT_FINGERPRINT_PREFIX = "visual|canvas-overflow|";
1186
+ function formatFitFeedback(issue) {
1187
+ return `[fit] ${issue.category}/${issue.subcategory ?? ""}: ${issue.description}
1188
+ Fix: ${issue.fix ?? ""}`;
1189
+ }
1190
+ var EVAL_ROUND_THREW_REASON = "eval round threw";
1191
+ var PROBE_ONLY_CRITERIA_REASON = "probe-only round: no evaluator configured";
1192
+ var PROBE_ONLY_VISUAL_REASON = "probe-only round: no visual evaluator configured";
1193
+ var PROBE_ONLY_PASS = "probe-only";
1194
+ var COST_TRACKER_ABSENT_REASON = "cost tracker absent on an evaluation round";
1195
+ function requireCostTracker(costTracker) {
1196
+ if (costTracker === null) throw new Error(COST_TRACKER_ABSENT_REASON);
1197
+ return costTracker;
1198
+ }
1199
+ function runVisualLeg(visualEvalAgent, run, measureFit) {
1200
+ const provider = visionJudgeProvider(visualEvalAgent.provider);
1201
+ if (provider === void 0) {
1202
+ const noJudge = `no vision judge for provider '${visualEvalAgent.provider}' (model ${visualEvalAgent.model}) \u2014 the in-loop visual judge runs on a vision provider (anthropic, google)`;
1203
+ if (measureFit === void 0) {
1204
+ return Promise.resolve({ issues: [], coverage: { status: "skipped", reason: noJudge } });
1205
+ }
1206
+ const notMeasured = (why) => ({
1207
+ issues: [],
1208
+ coverage: { status: "skipped", reason: `${noJudge}; fit not measured: ${why}` }
1209
+ });
1210
+ return measureFit().then(
1211
+ (fit) => fit.status === "measured" ? {
1212
+ issues: fit.issues,
1213
+ coverage: { status: "skipped", reason: `${noJudge}; fit measured without the judge: ${describeFitReadings(fit.readings)}` }
1214
+ } : notMeasured(fit.reason),
1215
+ (e) => notMeasured(e instanceof Error ? e.message : String(e))
1216
+ );
1217
+ }
1218
+ return run(provider).catch((e) => ({
1219
+ issues: [],
1220
+ coverage: { status: "skipped", reason: `visual leg failed: ${e instanceof Error ? e.message : String(e)}` }
1221
+ }));
1222
+ }
1223
+ function describeFitReadings(readings) {
1224
+ return readings.map((r) => `${r.canvas} ${r.viewport.width}\xD7${r.viewport.height} content ${r.contentHeight ?? "?"}px${r.overflow ? " (overflow)" : ""}`).join(", ");
1225
+ }
1226
+ var LOW_RISK_BYPASS_REASON = "same-image low-risk bypass: axis checks clean, tier-1/2 evaluation not invoked";
1227
+ function selectActionableFeedback(issues) {
1228
+ const pool = issues.filter((i) => i.result === "fail" || i.result === "warn");
1229
+ const reserved = pool.find((i) => i.tier === 0 && i.result === "fail");
1230
+ return [
1231
+ ...reserved ? [reserved] : [],
1232
+ ...pool.filter((i) => i !== reserved).sort((a, b) => (a.result === "fail" ? 0 : 1) - (b.result === "fail" ? 0 : 1))
1233
+ ].slice(0, MAX_FEEDBACK_ISSUES);
1234
+ }
1235
+ function fingerprintFail(issue) {
1236
+ const cat = issue.category ?? "";
1237
+ const sub = issue.subcategory ?? "";
1238
+ const desc = (issue.description ?? "").toLowerCase().replace(/line\s+\d+/g, "line N").replace(/\s+/g, " ").trim().slice(0, 40);
1239
+ return `${cat}|${sub}|${desc}`;
1240
+ }
1241
+ function failedCheckKinds(probeIssues) {
1242
+ const failed = /* @__PURE__ */ new Set();
1243
+ for (const issue of probeIssues) {
1244
+ if (issue.result !== "fail" || issue.subcategory === void 0) continue;
1245
+ const kind = parseRuntimeSubcategory(issue.subcategory);
1246
+ if (kind !== void 0) failed.add(kind);
1247
+ }
1248
+ return RENDER_CHECK_KINDS.filter((kind) => failed.has(kind));
1249
+ }
1250
+ function probeMetaOf(outcome, probeIssues) {
1251
+ const detail = {
1252
+ ...outcome.reason !== void 0 ? { reason: outcome.reason } : {},
1253
+ ...outcome.renderMs !== void 0 ? { renderMs: outcome.renderMs } : {},
1254
+ ...outcome.hostLoad !== void 0 ? { hostLoad: outcome.hostLoad } : {},
1255
+ ...outcome.queuedMs !== void 0 ? { queuedMs: outcome.queuedMs } : {},
1256
+ ...outcome.pendingAffordance !== void 0 ? { pendingAffordance: outcome.pendingAffordance } : {}
1257
+ };
1258
+ const elapsed = outcome.elapsedMs !== void 0 ? { elapsedMs: outcome.elapsedMs } : {};
1259
+ if (outcome.status !== "ran") {
1260
+ return { status: outcome.status, ...elapsed, ...detail };
1261
+ }
1262
+ const [first, ...rest] = failedCheckKinds(probeIssues);
1263
+ if (first === void 0) {
1264
+ return { status: "ran", verdict: "pass", ...elapsed, ...detail };
1265
+ }
1266
+ return { status: "ran", verdict: "fail", failChecks: [first, ...rest], ...elapsed, ...detail };
1267
+ }
1268
+ function describeProbeMeta(meta) {
1269
+ if (meta.verdict === void 0) return meta.status;
1270
+ return meta.verdict === "pass" ? "ran, verdict pass" : `ran, verdict fail (${meta.failChecks.join(", ")})`;
1271
+ }
1272
+ async function runProbeAtExit(input) {
1273
+ const { harness, sourceCode, compiledCode, contract, fixtureProps } = input;
1274
+ const probe = harness.check.runtimeRender;
1275
+ if (!probe || !compiledCode) {
1276
+ return {
1277
+ fired: false,
1278
+ recoverableFail: false,
1279
+ probeIssues: [],
1280
+ meta: {
1281
+ status: "not-applicable",
1282
+ reason: probe ? "no compiled code" : "no runtimeRender check configured"
1283
+ }
1284
+ };
1285
+ }
1286
+ let outcome;
1287
+ try {
1288
+ outcome = await probe.run({
1289
+ sourceCode,
1290
+ compiledCode,
1291
+ contract,
1292
+ fixtureProps
1293
+ });
1294
+ } catch (e) {
1295
+ const message = e instanceof Error ? e.message : String(e);
1296
+ console.warn(`[simple] in-loop probe at exit: infra failure \u2014 ${message}`);
1297
+ return {
1298
+ fired: false,
1299
+ recoverableFail: false,
1300
+ probeIssues: [],
1301
+ meta: { status: "infra-skipped", reason: message }
1302
+ };
1303
+ }
1304
+ const meta = probeMetaOf(outcome, outcome.issues);
1305
+ if (outcome.status !== "ran") {
1306
+ return { fired: false, recoverableFail: false, probeIssues: [], meta };
1307
+ }
1308
+ const probeIssues = outcome.issues;
1309
+ const recoverableFail = probeIssues.some(
1310
+ (i) => i.result === "fail" && typeof i.subcategory === "string" && i.subcategory.startsWith("runtime:render-no-throw") && typeof i.description === "string" && isRecoverableRenderCrash(i.description)
1311
+ );
1312
+ return { fired: true, recoverableFail, probeIssues, meta };
1313
+ }
1314
+ function formatRuntimeProbeFeedback(issue) {
1315
+ const subcat = issue.subcategory ? `/${issue.subcategory}` : "";
1316
+ return `[runtime] ${issue.category}${subcat}: ${issue.description}
1317
+ Fix: ${issue.fix ?? ""}`;
1318
+ }
1319
+ var FEEDBACK_PROBE_CHECKS = ["prop-sensitivity"];
1320
+ var CONTRACT_FEEDBACK_BONUS = 1;
1321
+ function isFeedbackProbeFail(issue) {
1322
+ const sub = issue.subcategory;
1323
+ if (issue.result !== "fail" || typeof sub !== "string") return false;
1324
+ return FEEDBACK_PROBE_CHECKS.some((check) => sub === `runtime:${check}` || sub.startsWith(`runtime:${check}:`));
1325
+ }
1326
+ function contractFeedbackRecord(fails, sourceBefore) {
1327
+ return { firedOn: fails.flatMap((i) => i.subcategory !== void 0 ? [i.subcategory] : []), sourceBefore };
1328
+ }
1329
+ function contractFeedbackFails(exitProbe, alreadyFedBack) {
1330
+ if (!exitProbe.fired) return [];
1331
+ return exitProbe.probeIssues.filter((i) => isFeedbackProbeFail(i) && !alreadyFedBack.has(fingerprintFail(i)));
1332
+ }
1333
+ async function runEvalRound(ctx, input) {
1334
+ const {
1335
+ workspace,
1336
+ harness,
1337
+ contract,
1338
+ userPrompt,
1339
+ fixtureProps,
1340
+ classification,
1341
+ evaluationAgent,
1342
+ visualEvalAgent,
1343
+ visualEvaluation,
1344
+ visualThreshold,
1345
+ qualityMode,
1346
+ maxEvalRounds,
1347
+ llmEvalMod,
1348
+ visualMod,
1349
+ preWarmPromise,
1350
+ onProgress,
1351
+ probeOnly,
1352
+ probeRepairUsed,
1353
+ probeRepairTrigger
1354
+ } = ctx;
1355
+ const { compiledCode, prevModeSubcats, prevFailFingerprints } = input;
1356
+ let { evalRoundsUsed, preWarmedContext } = input;
1357
+ evalRoundsUsed++;
1358
+ onProgress?.({ type: "generating", phase: "evaluating" });
1359
+ const currentSource = workspace.read() ?? "";
1360
+ let evalResult;
1361
+ let evalTokens = { input: 0, output: 0 };
1362
+ let inLoopVisualTokens = void 0;
1363
+ let evalLlmMs = 0;
1364
+ try {
1365
+ if (probeOnly) {
1366
+ const exitProbe = await runProbeAtExit({
1367
+ harness,
1368
+ sourceCode: currentSource,
1369
+ compiledCode,
1370
+ contract,
1371
+ fixtureProps
1372
+ });
1373
+ const probeOnlyResult = {
1374
+ issues: [...exitProbe.probeIssues],
1375
+ pass: [PROBE_ONLY_PASS],
1376
+ runtimeProbe: exitProbe.meta,
1377
+ criteriaCoverage: notApplicableCoverage(PROBE_ONLY_CRITERIA_REASON),
1378
+ visualCoverage: { status: "not-applicable", reason: PROBE_ONLY_VISUAL_REASON }
1379
+ };
1380
+ const recoverableFail = exitProbe.fired && exitProbe.recoverableFail;
1381
+ if (probeRepairUsed) {
1382
+ if (probeRepairTrigger === void 0) {
1383
+ throw new Error("harness defect: a probe-only re-probe round without the probe that bought the repair turn");
1384
+ }
1385
+ const repair = { attempted: true, compiled: true, trigger: probeRepairTrigger };
1386
+ evalResult = { ...probeOnlyResult, runtimeProbeRepair: repair };
1387
+ console.log(
1388
+ `[simple] eval round ${evalRoundsUsed}: probe-only re-probe after the repair turn \u2014 ${describeProbeMeta(exitProbe.meta)}${recoverableFail ? ", recoverable crash still present" : ""} \u2014 serving`
1389
+ );
1390
+ return {
1391
+ control: "break",
1392
+ evalDone: true,
1393
+ evalResult,
1394
+ evalRoundsUsed,
1395
+ prevModeSubcats,
1396
+ prevFailFingerprints,
1397
+ preWarmedContext,
1398
+ evalTokens,
1399
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1400
+ evalLlmMs,
1401
+ lastResultText: "",
1402
+ isEvalFeedback: false,
1403
+ lastDiffFailed: false
1404
+ };
1405
+ }
1406
+ evalResult = probeOnlyResult;
1407
+ if (recoverableFail) {
1408
+ const runtimeFails = exitProbe.probeIssues.filter(
1409
+ (i) => i.result === "fail" && i.subcategory?.startsWith("runtime:")
1410
+ );
1411
+ const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
1412
+ console.log(
1413
+ `[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 runtime probe fail (recoverable) \u2014 granting the one repair turn`
1414
+ );
1415
+ return {
1416
+ control: "feedback",
1417
+ evalDone: false,
1418
+ evalResult,
1419
+ evalRoundsUsed,
1420
+ prevModeSubcats,
1421
+ prevFailFingerprints,
1422
+ preWarmedContext,
1423
+ evalTokens,
1424
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1425
+ evalLlmMs,
1426
+ lastResultText: lines.join("\n\n"),
1427
+ isEvalFeedback: true,
1428
+ lastDiffFailed: false
1429
+ };
1430
+ }
1431
+ console.log(
1432
+ `[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 probe ${describeProbeMeta(exitProbe.meta)} \u2014 serving`
1433
+ );
1434
+ return {
1435
+ control: "break",
1436
+ evalDone: true,
1437
+ evalResult,
1438
+ evalRoundsUsed,
1439
+ prevModeSubcats,
1440
+ prevFailFingerprints,
1441
+ preWarmedContext,
1442
+ evalTokens,
1443
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1444
+ evalLlmMs,
1445
+ lastResultText: "",
1446
+ isEvalFeedback: false,
1447
+ lastDiffFailed: false
1448
+ };
1449
+ }
1450
+ const checkResult = await runCheck({
1451
+ harness,
1452
+ sourceCode: currentSource,
1453
+ compiledCode,
1454
+ contract,
1455
+ prompt: userPrompt,
1456
+ fixtureProps,
1457
+ skipRuntimeRender: true
1458
+ });
1459
+ const modeIssues = [...checkResult.issues];
1460
+ const modeHasBlocking = modeIssues.some((i) => i.result === "fail");
1461
+ const contractDeclaresGadgets = contract !== void 0 && listContractGadgets(contract).length > 0;
1462
+ if (classification.riskTier === "low" && !modeHasBlocking && !contractDeclaresGadgets) {
1463
+ const exitProbe = await runProbeAtExit({
1464
+ harness,
1465
+ sourceCode: currentSource,
1466
+ compiledCode,
1467
+ contract,
1468
+ fixtureProps
1469
+ });
1470
+ if (exitProbe.fired && exitProbe.recoverableFail) {
1471
+ const runtimeFails = exitProbe.probeIssues.filter(
1472
+ (i) => i.result === "fail" && i.subcategory?.startsWith("runtime:")
1473
+ );
1474
+ const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
1475
+ const allIssues2 = [...modeIssues, ...exitProbe.probeIssues];
1476
+ evalResult = {
1477
+ issues: allIssues2,
1478
+ pass: ["axis.low-risk"],
1479
+ criteriaCoverage: notApplicableCoverage(LOW_RISK_BYPASS_REASON),
1480
+ runtimeProbe: exitProbe.meta
1481
+ };
1482
+ console.log(
1483
+ `[simple] eval round ${evalRoundsUsed}: low-risk bypass clean BUT runtime probe fail (recoverable) \u2014 granting +1 turn`
1484
+ );
1485
+ return {
1486
+ control: "feedback",
1487
+ evalDone: false,
1488
+ evalResult,
1489
+ evalRoundsUsed,
1490
+ prevModeSubcats,
1491
+ prevFailFingerprints,
1492
+ preWarmedContext,
1493
+ evalTokens,
1494
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1495
+ evalLlmMs,
1496
+ lastResultText: lines.join("\n\n"),
1497
+ isEvalFeedback: true,
1498
+ lastDiffFailed: false
1499
+ };
1500
+ }
1501
+ evalResult = {
1502
+ issues: [...modeIssues, ...exitProbe.probeIssues],
1503
+ pass: ["axis.low-risk"],
1504
+ criteriaCoverage: notApplicableCoverage(LOW_RISK_BYPASS_REASON),
1505
+ runtimeProbe: exitProbe.meta
1506
+ };
1507
+ console.log(
1508
+ `[simple] eval round ${evalRoundsUsed}: low-risk bypass (axis-checks clean, tier-1/2 skipped${exitProbe.fired ? ", probe ran" : `, probe ${exitProbe.meta.status}`}) \u2014 PASS`
1509
+ );
1510
+ return {
1511
+ control: "break",
1512
+ evalDone: true,
1513
+ evalResult,
1514
+ evalRoundsUsed,
1515
+ prevModeSubcats,
1516
+ prevFailFingerprints,
1517
+ preWarmedContext,
1518
+ evalTokens,
1519
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1520
+ evalLlmMs,
1521
+ lastResultText: "",
1522
+ isEvalFeedback: false,
1523
+ lastDiffFailed: false
1524
+ };
1525
+ }
1526
+ const costTracker = requireCostTracker(ctx.costTracker);
1527
+ if (evalRoundsUsed === 1 && !preWarmedContext && preWarmPromise) {
1528
+ preWarmedContext = await preWarmPromise;
1529
+ }
1530
+ let llmResult = null;
1531
+ let visualIssues = null;
1532
+ const visualContext = {
1533
+ compiledCode,
1534
+ originalPrompt: userPrompt,
1535
+ profile: harness.profile,
1536
+ // The caller's theme, when named — the round paints what the app paints.
1537
+ ...visualEvaluation?.cssTokens !== void 0 ? { cssTokens: visualEvaluation.cssTokens } : {},
1538
+ // ggui#1436 — the criteria bank and the card's context; the judge selects per canvas, report-only.
1539
+ ...visualEvaluation?.criteriaBank !== void 0 ? {
1540
+ criteria: {
1541
+ bank: visualEvaluation.criteriaBank,
1542
+ context: criteriaContextFor({
1543
+ classification,
1544
+ contract,
1545
+ cssTokens: visualEvaluation.cssTokens,
1546
+ profile: harness.profile,
1547
+ shell: ctx.shellType ?? "unknown",
1548
+ ...visualEvaluation.criteriaKind !== void 0 ? { kind: visualEvaluation.criteriaKind } : {}
1549
+ })
1550
+ }
1551
+ } : {}
1552
+ };
1553
+ const visualFrame = {
1554
+ // The in-loop visual round renders the same sample the runtime probe
1555
+ // does (the caller's fixture) unless the config names its own.
1556
+ sampleProps: visualEvaluation?.sampleProps ?? fixtureProps,
1557
+ // Per-canvas judging (arm-neutral) — absent = single shot.
1558
+ ...visualEvaluation?.canvases !== void 0 ? { canvases: visualEvaluation.canvases } : {},
1559
+ // ggui#1195 — the declared box per canvas (same-target with the composer).
1560
+ ...visualEvaluation?.canvasViewports !== void 0 ? { canvasViewports: visualEvaluation.canvasViewports } : {}
1561
+ };
1562
+ const measureFit = visualMod && visualFrame.canvases !== void 0 && visualFrame.canvases.length > 0 ? () => visualMod.runVisualFit(visualContext, visualFrame) : void 0;
1563
+ const evalLlmStart = Date.now();
1564
+ const [llm, visual] = await Promise.all([
1565
+ llmEvalMod ? llmEvalMod.runLLMEvaluation(
1566
+ {
1567
+ sourceCode: currentSource,
1568
+ originalPrompt: userPrompt,
1569
+ contract,
1570
+ // Criteria + mother prompt follow the triad that produced
1571
+ // the source (free mode: color-only tokens, arm-neutral
1572
+ // visual judging).
1573
+ designMode: harness.designMode,
1574
+ profile: harness.profile
1575
+ },
1576
+ {
1577
+ provider: mapProviderForEvaluator(evaluationAgent.provider),
1578
+ model: evaluationAgent.model,
1579
+ // #484 — thread the coding-agent's routing override
1580
+ // through to the evaluator so its LLM call never falls
1581
+ // back to `process.env` either (the eval calls run
1582
+ // inside the same concurrent generation, so the race is
1583
+ // real here too, not just on the coding turn).
1584
+ routeOverride: evaluationAgent.routeOverride,
1585
+ // #489 — thread the same provider-429-retry observer.
1586
+ // Without this, a rate-limited retry inside the
1587
+ // evaluation agent's apiCall() happens (the base-class
1588
+ // retry loop is unconditional) but never reaches the
1589
+ // caller, silently missing the host application's
1590
+ // structured log.
1591
+ onRetry: evaluationAgent.onRetry
1592
+ },
1593
+ preWarmedContext
1594
+ ) : null,
1595
+ visualMod ? runVisualLeg(
1596
+ visualEvalAgent,
1597
+ (provider) => visualMod.runVisualEval(visualContext, {
1598
+ provider,
1599
+ model: visualEvalAgent.model,
1600
+ passThreshold: visualThreshold,
1601
+ viewport: visualEvaluation?.viewport,
1602
+ ...visualFrame,
1603
+ // #484/#489 — same threading as the LLM-eval leg above:
1604
+ // the visual eval's multimodal call runs inside the same
1605
+ // concurrent generation, so it needs the same routing
1606
+ // override (never fall back to `process.env`) and the
1607
+ // same provider-429-retry observer (retries reach the
1608
+ // host application's structured log).
1609
+ routeOverride: visualEvalAgent.routeOverride,
1610
+ onRetry: visualEvalAgent.onRetry
1611
+ }),
1612
+ measureFit
1613
+ ) : null
1614
+ ]);
1615
+ evalLlmMs = Date.now() - evalLlmStart;
1616
+ llmResult = llm;
1617
+ visualIssues = visual?.issues ?? null;
1618
+ inLoopVisualTokens = visual?.tokens;
1619
+ if (inLoopVisualTokens !== void 0) {
1620
+ costTracker.record(visualEvalAgent.model, inLoopVisualTokens.inputTokens, inLoopVisualTokens.outputTokens);
1621
+ const criteriaSpend = inLoopVisualTokens.criteria;
1622
+ if (criteriaSpend !== void 0) costTracker.record(visualEvalAgent.model, criteriaSpend.inputTokens, criteriaSpend.outputTokens);
1623
+ }
1624
+ const visualSummary = visual?.summary;
1625
+ const visualCoverage = visual?.coverage ?? { status: "not-applicable", reason: "visual leg not configured" };
1626
+ if (visualCoverage.status === "skipped") {
1627
+ console.log(`[eval] visual-judge: SKIPPED (${visualCoverage.reason ?? "no reason recorded"})`);
1628
+ }
1629
+ if (llmResult) {
1630
+ costTracker.record(evaluationAgent.model, llmResult.inputTokens, llmResult.outputTokens, {
1631
+ ...llmResult.cacheReadTokens !== void 0 ? { read: llmResult.cacheReadTokens } : {},
1632
+ ...llmResult.cacheCreationTokens !== void 0 ? { write: llmResult.cacheCreationTokens } : {}
1633
+ });
1634
+ evalTokens = {
1635
+ input: llmResult.inputTokens,
1636
+ output: llmResult.outputTokens,
1637
+ ...llmResult.cacheReadTokens !== void 0 ? { cacheRead: llmResult.cacheReadTokens } : {},
1638
+ ...llmResult.cacheCreationTokens !== void 0 ? { cacheCreation: llmResult.cacheCreationTokens } : {}
1639
+ };
1640
+ }
1641
+ const allIssues = [
1642
+ ...llmResult?.issues ?? [],
1643
+ ...visualIssues ?? [],
1644
+ ...modeIssues
1645
+ ];
1646
+ const allPass = [...llmResult?.pass ?? []];
1647
+ evalResult = {
1648
+ issues: allIssues,
1649
+ pass: allPass,
1650
+ ...llmResult?.criteriaCoverage !== void 0 ? { criteriaCoverage: llmResult.criteriaCoverage } : {},
1651
+ // Per-canvas visual summary — present only when canvases were
1652
+ // requested and the leg ran; every later re-stamp spreads this
1653
+ // base so it survives to the assembled result.
1654
+ ...visualSummary !== void 0 ? { visual: visualSummary } : {},
1655
+ visualCoverage
1656
+ };
1657
+ const fails = allIssues.filter((i) => i.result === "fail");
1658
+ const warns = allIssues.filter((i) => i.result === "warn");
1659
+ const modeFails = modeIssues.filter((i) => i.result === "fail").length;
1660
+ const modeWarns = modeIssues.filter((i) => i.result === "warn").length;
1661
+ const modeSubcatCounts = modeIssues.reduce((acc, i) => {
1662
+ const key = i.subcategory ?? "mode";
1663
+ acc[key] = (acc[key] ?? 0) + 1;
1664
+ return acc;
1665
+ }, {});
1666
+ const modeBreakdown = Object.entries(modeSubcatCounts).map(([k, v]) => `${k}\xD7${v}`).join(", ");
1667
+ const currSubcats = new Set(Object.keys(modeSubcatCounts));
1668
+ let crossRound = "";
1669
+ if (evalRoundsUsed > 1 && (prevModeSubcats.size > 0 || currSubcats.size > 0)) {
1670
+ const unresolved = [...currSubcats].filter((s) => prevModeSubcats.has(s));
1671
+ const fixed = [...prevModeSubcats].filter((s) => !currSubcats.has(s));
1672
+ const fresh = [...currSubcats].filter((s) => !prevModeSubcats.has(s));
1673
+ const parts = [];
1674
+ if (unresolved.length) parts.push(`unresolved=[${unresolved.join(",")}]`);
1675
+ if (fixed.length) parts.push(`fixed=[${fixed.join(",")}]`);
1676
+ if (fresh.length) parts.push(`new=[${fresh.join(",")}]`);
1677
+ if (parts.length) crossRound = ` | ${parts.join(" ")}`;
1678
+ }
1679
+ const updatedPrevModeSubcats = currSubcats;
1680
+ console.log(
1681
+ `[simple] eval round ${evalRoundsUsed}: ${fails.length} fails, ${warns.length} warns | cost=$${costTracker.getTotal().toFixed(4)} | tiers: t1+2=${llmResult?.issues.length ?? 0} visual=${visualIssues?.length ?? 0} axis[${classification.riskTier}]=${modeFails}F/${modeWarns}W${modeBreakdown ? ` (${modeBreakdown})` : ""}${crossRound}`
1682
+ );
1683
+ const blocking = evalResult.issues.filter((i) => i.result === "fail");
1684
+ const visualMajorsActed = visualEvaluation?.actOn === "major" ? (visualIssues ?? []).filter((i) => i.origin === "judge" && i.result === "warn" && i.severity === "major") : [];
1685
+ const shouldContinue = qualityMode === "fast" ? blocking.length > 0 || visualMajorsActed.length > 0 : blocking.length > 0 || warns.length > 0;
1686
+ const currFailFingerprints = new Set(blocking.map(fingerprintFail));
1687
+ if (!shouldContinue) {
1688
+ const exitProbe = await runProbeAtExit({
1689
+ harness,
1690
+ sourceCode: currentSource,
1691
+ compiledCode,
1692
+ contract,
1693
+ fixtureProps
1694
+ });
1695
+ if (exitProbe.fired && exitProbe.recoverableFail) {
1696
+ const runtimeFails = exitProbe.probeIssues.filter(
1697
+ (i) => i.result === "fail" && i.subcategory?.startsWith("runtime:")
1698
+ );
1699
+ const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
1700
+ const enrichedIssues = [...allIssues, ...exitProbe.probeIssues];
1701
+ evalResult = { ...evalResult, issues: enrichedIssues, pass: allPass, runtimeProbe: exitProbe.meta };
1702
+ const enrichedFingerprints = /* @__PURE__ */ new Set([
1703
+ ...currFailFingerprints,
1704
+ ...runtimeFails.map(fingerprintFail)
1705
+ ]);
1706
+ console.log(
1707
+ `[simple] eval round ${evalRoundsUsed}: tier-1/2 clean BUT runtime probe fail (recoverable) \u2014 granting +1 turn`
1708
+ );
1709
+ return {
1710
+ control: "feedback",
1711
+ evalDone: false,
1712
+ evalResult,
1713
+ evalRoundsUsed,
1714
+ prevModeSubcats: updatedPrevModeSubcats,
1715
+ prevFailFingerprints: enrichedFingerprints,
1716
+ preWarmedContext,
1717
+ evalTokens,
1718
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1719
+ evalLlmMs,
1720
+ lastResultText: lines.join("\n\n"),
1721
+ isEvalFeedback: true,
1722
+ lastDiffFailed: false
1723
+ };
1724
+ }
1725
+ const contractFails = contractFeedbackFails(exitProbe, prevFailFingerprints);
1726
+ if (contractFails.length > 0 && evalRoundsUsed < maxEvalRounds + CONTRACT_FEEDBACK_BONUS) {
1727
+ const lines = contractFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
1728
+ evalResult = {
1729
+ ...evalResult,
1730
+ issues: [...allIssues, ...exitProbe.probeIssues],
1731
+ pass: allPass,
1732
+ runtimeProbe: exitProbe.meta
1733
+ };
1734
+ console.log(
1735
+ `[simple] eval round ${evalRoundsUsed}: tier-1/2 clean BUT contract probe fail \u2014 granting +1 turn (ggui#1261; ${contractFails.map((i) => i.subcategory).join(", ")})`
1736
+ );
1737
+ return {
1738
+ control: "feedback",
1739
+ evalDone: false,
1740
+ evalResult,
1741
+ evalRoundsUsed,
1742
+ prevModeSubcats: updatedPrevModeSubcats,
1743
+ prevFailFingerprints: /* @__PURE__ */ new Set([...currFailFingerprints, ...contractFails.map(fingerprintFail)]),
1744
+ preWarmedContext,
1745
+ evalTokens,
1746
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1747
+ evalLlmMs,
1748
+ lastResultText: lines.join("\n\n"),
1749
+ isEvalFeedback: true,
1750
+ lastDiffFailed: false,
1751
+ contractFeedback: contractFeedbackRecord(contractFails, currentSource)
1752
+ };
1753
+ }
1754
+ evalResult = {
1755
+ ...evalResult,
1756
+ issues: [...allIssues, ...exitProbe.probeIssues],
1757
+ pass: allPass,
1758
+ runtimeProbe: exitProbe.meta
1759
+ };
1760
+ console.log(`[simple] eval round ${evalRoundsUsed}: no blocking issues \u2014 PASS`);
1761
+ return {
1762
+ control: "break",
1763
+ evalDone: true,
1764
+ evalResult,
1765
+ evalRoundsUsed,
1766
+ prevModeSubcats: updatedPrevModeSubcats,
1767
+ prevFailFingerprints: currFailFingerprints,
1768
+ preWarmedContext,
1769
+ evalTokens,
1770
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1771
+ evalLlmMs,
1772
+ lastResultText: "",
1773
+ isEvalFeedback: false,
1774
+ lastDiffFailed: false
1775
+ };
1776
+ }
1777
+ const onlyExtensionExemptFails = currFailFingerprints.size > 0 && [...currFailFingerprints].every((fp) => fp.includes("|runtime:") || fp.startsWith(FIT_FINGERPRINT_PREFIX));
1778
+ if (evalRoundsUsed > 1 && currFailFingerprints.size > 0 && !onlyExtensionExemptFails && [...currFailFingerprints].every((fp) => prevFailFingerprints.has(fp))) {
1779
+ console.log(
1780
+ `[simple] eval round ${evalRoundsUsed}: stuck \u2014 all ${currFailFingerprints.size} fails recur from prior round, exiting eval-fix loop`
1781
+ );
1782
+ const stuckExitProbe = await runProbeAtExit({
1783
+ harness,
1784
+ sourceCode: currentSource,
1785
+ compiledCode,
1786
+ contract,
1787
+ fixtureProps
1788
+ });
1789
+ evalResult = {
1790
+ ...evalResult,
1791
+ issues: [...evalResult.issues, ...stuckExitProbe.probeIssues],
1792
+ runtimeProbe: stuckExitProbe.meta
1793
+ };
1794
+ const stuckContractFails = contractFeedbackFails(stuckExitProbe, prevFailFingerprints);
1795
+ if (stuckContractFails.length > 0 && evalRoundsUsed < maxEvalRounds + CONTRACT_FEEDBACK_BONUS) {
1796
+ const lines = stuckContractFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
1797
+ console.log(
1798
+ `[simple] eval round ${evalRoundsUsed}: stuck BUT contract probe fail never delivered \u2014 granting +1 turn (ggui#1261; ${stuckContractFails.map((i) => i.subcategory).join(", ")})`
1799
+ );
1800
+ return {
1801
+ control: "feedback",
1802
+ evalDone: false,
1803
+ evalResult,
1804
+ evalRoundsUsed,
1805
+ prevModeSubcats: updatedPrevModeSubcats,
1806
+ prevFailFingerprints: /* @__PURE__ */ new Set([...currFailFingerprints, ...stuckContractFails.map(fingerprintFail)]),
1807
+ preWarmedContext,
1808
+ evalTokens,
1809
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1810
+ evalLlmMs,
1811
+ lastResultText: lines.join("\n\n"),
1812
+ isEvalFeedback: true,
1813
+ lastDiffFailed: false,
1814
+ contractFeedback: contractFeedbackRecord(stuckContractFails, currentSource)
1815
+ };
1816
+ }
1817
+ return {
1818
+ control: "break",
1819
+ evalDone: false,
1820
+ evalResult,
1821
+ evalRoundsUsed,
1822
+ prevModeSubcats: updatedPrevModeSubcats,
1823
+ prevFailFingerprints: currFailFingerprints,
1824
+ preWarmedContext,
1825
+ evalTokens,
1826
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1827
+ evalLlmMs,
1828
+ lastResultText: "",
1829
+ isEvalFeedback: false,
1830
+ lastDiffFailed: false
1831
+ };
1832
+ }
1833
+ const RUNTIME_EXTENSION_BONUS = 2;
1834
+ const FIT_EXTENSION_BONUS = 1;
1835
+ if (evalRoundsUsed >= maxEvalRounds) {
1836
+ const exitProbe = await runProbeAtExit({
1837
+ harness,
1838
+ sourceCode: currentSource,
1839
+ compiledCode,
1840
+ contract,
1841
+ fixtureProps
1842
+ });
1843
+ const hasRuntimeFail = exitProbe.fired && exitProbe.recoverableFail;
1844
+ const allowRuntimeExtension = hasRuntimeFail && evalRoundsUsed < maxEvalRounds + RUNTIME_EXTENSION_BONUS;
1845
+ const fitFails = evalResult.issues.filter(isFitFail);
1846
+ const allowFitExtension = !allowRuntimeExtension && fitFails.length > 0 && evalRoundsUsed < maxEvalRounds + FIT_EXTENSION_BONUS;
1847
+ if (allowFitExtension) {
1848
+ const fitLines = fitFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatFitFeedback);
1849
+ evalResult = {
1850
+ ...evalResult,
1851
+ issues: [...evalResult.issues, ...exitProbe.probeIssues],
1852
+ runtimeProbe: exitProbe.meta
1853
+ };
1854
+ console.log(
1855
+ `[simple] eval round ${evalRoundsUsed}: fit fail active at cap \u2014 granting +1 retry (ggui#1195; ${fitFails.length} canvas-overflow)`
1856
+ );
1857
+ return {
1858
+ control: "feedback",
1859
+ evalDone: false,
1860
+ evalResult,
1861
+ evalRoundsUsed,
1862
+ prevModeSubcats: updatedPrevModeSubcats,
1863
+ prevFailFingerprints: /* @__PURE__ */ new Set([...currFailFingerprints, ...fitFails.map(fingerprintFail)]),
1864
+ preWarmedContext,
1865
+ evalTokens,
1866
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1867
+ evalLlmMs,
1868
+ lastResultText: fitLines.join("\n\n"),
1869
+ isEvalFeedback: true,
1870
+ lastDiffFailed: false
1871
+ };
1872
+ }
1873
+ const capContractFails = allowRuntimeExtension ? [] : contractFeedbackFails(exitProbe, prevFailFingerprints);
1874
+ if (capContractFails.length > 0 && evalRoundsUsed < maxEvalRounds + CONTRACT_FEEDBACK_BONUS) {
1875
+ const contractLines = capContractFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
1876
+ evalResult = {
1877
+ ...evalResult,
1878
+ issues: [...evalResult.issues, ...exitProbe.probeIssues],
1879
+ runtimeProbe: exitProbe.meta
1880
+ };
1881
+ console.log(
1882
+ `[simple] eval round ${evalRoundsUsed}: contract probe fail active at cap \u2014 granting +1 retry (ggui#1261; ${capContractFails.map((i) => i.subcategory).join(", ")})`
1883
+ );
1884
+ return {
1885
+ control: "feedback",
1886
+ evalDone: false,
1887
+ evalResult,
1888
+ evalRoundsUsed,
1889
+ prevModeSubcats: updatedPrevModeSubcats,
1890
+ prevFailFingerprints: /* @__PURE__ */ new Set([...currFailFingerprints, ...capContractFails.map(fingerprintFail)]),
1891
+ preWarmedContext,
1892
+ evalTokens,
1893
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1894
+ evalLlmMs,
1895
+ lastResultText: contractLines.join("\n\n"),
1896
+ isEvalFeedback: true,
1897
+ lastDiffFailed: false,
1898
+ contractFeedback: contractFeedbackRecord(capContractFails, currentSource)
1899
+ };
1900
+ }
1901
+ if (!allowRuntimeExtension) {
1902
+ evalResult = {
1903
+ ...evalResult,
1904
+ issues: [...evalResult.issues, ...exitProbe.probeIssues],
1905
+ runtimeProbe: exitProbe.meta
1906
+ };
1907
+ console.log(`[simple] eval round ${evalRoundsUsed}: max eval rounds reached \u2014 stopping`);
1908
+ return {
1909
+ control: "break",
1910
+ evalDone: false,
1911
+ evalResult,
1912
+ evalRoundsUsed,
1913
+ prevModeSubcats: updatedPrevModeSubcats,
1914
+ prevFailFingerprints: currFailFingerprints,
1915
+ preWarmedContext,
1916
+ evalTokens,
1917
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1918
+ evalLlmMs,
1919
+ lastResultText: "",
1920
+ isEvalFeedback: false,
1921
+ lastDiffFailed: false
1922
+ };
1923
+ }
1924
+ const runtimeFails = exitProbe.probeIssues.filter(
1925
+ (i) => i.result === "fail" && i.subcategory?.startsWith("runtime:")
1926
+ );
1927
+ const probeLines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
1928
+ const enrichedIssues = [...evalResult.issues, ...exitProbe.probeIssues];
1929
+ evalResult = { ...evalResult, issues: enrichedIssues, runtimeProbe: exitProbe.meta };
1930
+ console.log(
1931
+ `[simple] eval round ${evalRoundsUsed}: runtime-probe fail active at cap \u2014 granting +1 retry (${maxEvalRounds + RUNTIME_EXTENSION_BONUS - evalRoundsUsed} of ${RUNTIME_EXTENSION_BONUS} bonus rounds remain)`
1932
+ );
1933
+ return {
1934
+ control: "feedback",
1935
+ evalDone: false,
1936
+ evalResult,
1937
+ evalRoundsUsed,
1938
+ prevModeSubcats: updatedPrevModeSubcats,
1939
+ prevFailFingerprints: /* @__PURE__ */ new Set([
1940
+ ...currFailFingerprints,
1941
+ ...runtimeFails.map(fingerprintFail)
1942
+ ]),
1943
+ preWarmedContext,
1944
+ evalTokens,
1945
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1946
+ evalLlmMs,
1947
+ lastResultText: probeLines.join("\n\n"),
1948
+ isEvalFeedback: true,
1949
+ lastDiffFailed: false
1950
+ };
1951
+ }
1952
+ if (!costTracker.canContinue()) {
1953
+ console.log(
1954
+ `[simple] eval: budget exhausted (spent=$${costTracker.getTotal().toFixed(4)}) \u2014 stopping`
1955
+ );
1956
+ return {
1957
+ control: "break",
1958
+ evalDone: false,
1959
+ evalResult,
1960
+ evalRoundsUsed,
1961
+ prevModeSubcats: updatedPrevModeSubcats,
1962
+ prevFailFingerprints: currFailFingerprints,
1963
+ preWarmedContext,
1964
+ evalTokens,
1965
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
1966
+ evalLlmMs,
1967
+ lastResultText: "",
1968
+ isEvalFeedback: false,
1969
+ lastDiffFailed: false
1970
+ };
1971
+ }
1972
+ const allActionable = selectActionableFeedback(evalResult.issues);
1973
+ const issueLines = allActionable.map((issue) => {
1974
+ const tag = issue.result === "fail" ? "FAIL" : "WARN";
1975
+ return `[${tag}] ${issue.category}${issue.subcategory ? "/" + issue.subcategory : ""}: ${issue.description}
1976
+ Fix: ${issue.fix}`;
1977
+ });
1978
+ console.log(
1979
+ `[simple] eval round ${evalRoundsUsed}: ${allActionable.length} issues (${blocking.length} fail, ${warns.length} warn) \u2192 feeding back to coding loop`
1980
+ );
1981
+ for (const issue of evalResult.issues) {
1982
+ if (issue.tier !== 0 || issue.result !== "fail" && issue.result !== "warn") continue;
1983
+ console.log(
1984
+ `[simple] eval round ${evalRoundsUsed}: tier0 ${issue.result} ${issue.subcategory ?? issue.category} delivered=${allActionable.includes(issue)}`
1985
+ );
1986
+ }
1987
+ if (visualEvaluation?.actOn !== void 0) {
1988
+ console.log(
1989
+ `[simple] eval round ${evalRoundsUsed}: continuing: fails=${blocking.length}, visual majors acted=${visualMajorsActed.length} (actOn=${visualEvaluation.actOn})`
1990
+ );
1991
+ }
1992
+ return {
1993
+ control: "feedback",
1994
+ evalDone: false,
1995
+ evalResult,
1996
+ evalRoundsUsed,
1997
+ prevModeSubcats: updatedPrevModeSubcats,
1998
+ // DELIVERED fails only (Exp 54 P1 addendum, proposal 2): the
1999
+ // stuck detector's premise is "the LLM saw these and did not
2000
+ // resolve them" — recording fails that the MAX_FEEDBACK_ISSUES
2001
+ // cap cut from the feedback turns truncation into a premature
2002
+ // stuck-exit (the model never saw the issue it is blamed for
2003
+ // not fixing). An undelivered fail recurring next round now
2004
+ // reads as NEW, gets delivered, and earns its fix round.
2005
+ prevFailFingerprints: new Set(
2006
+ allActionable.filter((i) => i.result === "fail").map(fingerprintFail)
2007
+ ),
2008
+ preWarmedContext,
2009
+ evalTokens,
2010
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
2011
+ evalLlmMs,
2012
+ lastResultText: issueLines.join("\n\n"),
2013
+ isEvalFeedback: true,
2014
+ lastDiffFailed: false
2015
+ };
2016
+ } catch (e) {
2017
+ const message = e instanceof Error ? e.message : String(e);
2018
+ console.error("[simple] eval failed:", message);
2019
+ evalResult = evalResult ?? {
2020
+ issues: [],
2021
+ pass: [],
2022
+ criteriaCoverage: notApplicableCoverage(`${EVAL_ROUND_THREW_REASON}: ${message}`),
2023
+ runtimeProbe: { status: "infra-skipped", reason: `${EVAL_ROUND_THREW_REASON}: ${message}` }
2024
+ };
2025
+ return {
2026
+ control: "break",
2027
+ evalDone: false,
2028
+ evalResult,
2029
+ evalRoundsUsed,
2030
+ prevModeSubcats,
2031
+ prevFailFingerprints,
2032
+ preWarmedContext,
2033
+ evalTokens,
2034
+ ...inLoopVisualTokens !== void 0 ? { inLoopVisualTokens } : {},
2035
+ evalLlmMs,
2036
+ lastResultText: "",
2037
+ isEvalFeedback: false,
2038
+ lastDiffFailed: false
2039
+ };
2040
+ }
2041
+ }
2042
+
2043
+ // src/harness/coding/generate-task-runner.ts
2044
+ function createTelemetry() {
2045
+ return {
2046
+ counters: {
2047
+ phases: { impl: 0, patch: 0, evalFix: 0, scaffold: 0, fill: 0 },
2048
+ outcomes: { pass: 0, patchInvalid: 0, selfCheckFail: 0, diffFail: 0 }
2049
+ },
2050
+ turnsUsed: 0,
2051
+ evalRoundsUsed: 0,
2052
+ cumulativeLlmMs: 0,
2053
+ cumulativeToolMs: 0,
2054
+ cumulativeEvalWallMs: 0,
2055
+ cumulativeEvalLlmMs: 0,
2056
+ totalIn: 0,
2057
+ totalOut: 0,
2058
+ evalResult: void 0,
2059
+ probeRepairUsed: false,
2060
+ probeRepairTrigger: void 0,
2061
+ contractFeedback: void 0,
2062
+ sameExchangeBreak: void 0,
2063
+ sameFailureBreak: void 0,
2064
+ compiledCode: "",
2065
+ pairedSource: "",
2066
+ selfCheckPassed: false,
2067
+ initialResultDelivered: false,
2068
+ codingStartedAtMs: 0,
2069
+ codingMs: 0
2070
+ };
2071
+ }
2072
+ function absorbTokens(telemetry, usage) {
2073
+ telemetry.totalIn += usage.input;
2074
+ telemetry.totalOut += usage.output;
2075
+ if (usage.cacheRead !== void 0) {
2076
+ telemetry.cacheReadTokens = (telemetry.cacheReadTokens ?? 0) + usage.cacheRead;
2077
+ }
2078
+ if (usage.cacheCreation !== void 0) {
2079
+ telemetry.cacheCreationTokens = (telemetry.cacheCreationTokens ?? 0) + usage.cacheCreation;
2080
+ }
2081
+ }
2082
+ function absorbInLoopVisualTokens(telemetry, part) {
2083
+ if (part === void 0) return;
2084
+ const prev = telemetry.inLoopVisualTokens;
2085
+ const prevCriteria = prev?.criteria;
2086
+ const criteria = part.criteria === void 0 ? prevCriteria : {
2087
+ inputTokens: (prevCriteria?.inputTokens ?? 0) + part.criteria.inputTokens,
2088
+ outputTokens: (prevCriteria?.outputTokens ?? 0) + part.criteria.outputTokens
2089
+ };
2090
+ telemetry.inLoopVisualTokens = {
2091
+ inputTokens: (prev?.inputTokens ?? 0) + part.inputTokens,
2092
+ outputTokens: (prev?.outputTokens ?? 0) + part.outputTokens,
2093
+ ...criteria !== void 0 ? { criteria } : {}
2094
+ };
2095
+ }
2096
+ function absorbTurn(telemetry, turn) {
2097
+ absorbTokens(telemetry, {
2098
+ input: turn.tokens.input,
2099
+ output: turn.tokens.output,
2100
+ ...turn.cacheReadTokens !== void 0 ? { cacheRead: turn.cacheReadTokens } : {},
2101
+ ...turn.cacheCreationTokens !== void 0 ? { cacheCreation: turn.cacheCreationTokens } : {}
2102
+ });
2103
+ telemetry.cumulativeLlmMs += turn.llmMs;
2104
+ telemetry.cumulativeToolMs += turn.toolMs;
2105
+ if (turn.phase) {
2106
+ if (turn.phase === "scaffold") telemetry.counters.phases.scaffold++;
2107
+ else if (turn.phase === "fill") telemetry.counters.phases.fill++;
2108
+ else if (turn.phase === "impl") telemetry.counters.phases.impl++;
2109
+ else if (turn.phase === "eval-fix") telemetry.counters.phases.evalFix++;
2110
+ else telemetry.counters.phases.patch++;
2111
+ }
2112
+ if (turn.outcome) {
2113
+ if (turn.outcome === "PASS") telemetry.counters.outcomes.pass++;
2114
+ else if (turn.outcome === "PATCH_INVALID") telemetry.counters.outcomes.patchInvalid++;
2115
+ else if (turn.outcome === "DIFF_FAIL") telemetry.counters.outcomes.diffFail++;
2116
+ else telemetry.counters.outcomes.selfCheckFail++;
2117
+ }
2118
+ if (turn.sameExchangeBreak !== void 0) telemetry.sameExchangeBreak = turn.sameExchangeBreak;
2119
+ if (turn.sameFailureBreak !== void 0) telemetry.sameFailureBreak = turn.sameFailureBreak;
2120
+ }
2121
+ function createGenerateTaskRunner(input) {
2122
+ const { session, params, classification, telemetry } = input;
2123
+ const maxTurns = input.maxTurns ?? 30;
2124
+ const a1Mode = session.harness.process.mode === "staged";
2125
+ let a1Phase = a1Mode ? "scaffold" : "post";
2126
+ let lastResultText = "";
2127
+ let lastDiffFailed = false;
2128
+ let isEvalFeedback = false;
2129
+ let iconNamesCache = null;
2130
+ let preWarmedContext;
2131
+ let prevModeSubcats = /* @__PURE__ */ new Set();
2132
+ let prevFailFingerprints = /* @__PURE__ */ new Set();
2133
+ let evalDone = false;
2134
+ return async function generate(_task, _ctx) {
2135
+ if (telemetry.codingStartedAtMs === 0) {
2136
+ telemetry.codingStartedAtMs = Date.now();
2137
+ params.onProgress?.({ type: "generating", phase: "coding" });
2138
+ }
2139
+ do {
2140
+ telemetry.turnsUsed++;
2141
+ const turn = await runCodingTurn(
2142
+ {
2143
+ workspace: session.workspace,
2144
+ codingAgent: session.codingAgent,
2145
+ codingModel: session.agents.coding.model,
2146
+ systemPrompt: session.systemPrompt,
2147
+ harness: session.harness,
2148
+ contract: params.contract,
2149
+ originalPrompt: params.userPrompt,
2150
+ commitMeta: session.commitMeta,
2151
+ originalProps: session.originalProps,
2152
+ // ggui#1285: the fixture the probe and judge render with, for the echo check at auto-commit.
2153
+ ...params.fixtureProps !== void 0 ? { fixtureProps: params.fixtureProps } : {},
2154
+ costTracker: session.costTracker,
2155
+ // Thread the dispatch-resolved policy so experimental
2156
+ // profiles take effect on both preflight and tier-0 retry
2157
+ // feedback.
2158
+ contextPolicy: session.resolvedPolicy.context,
2159
+ // Dupe-break state is mutable; runCodingTurn updates it in
2160
+ // place (fingerprint + cooldown + forceEscape +
2161
+ // scopedEscapeUsedCount).
2162
+ dupeBreak: session.dupeBreak,
2163
+ ...params.onCodingTurnPrompt !== void 0 ? { onPrompt: params.onCodingTurnPrompt } : {}
2164
+ },
2165
+ {
2166
+ turnsUsed: telemetry.turnsUsed,
2167
+ a1Phase,
2168
+ lastResultText,
2169
+ lastDiffFailed,
2170
+ isEvalFeedback,
2171
+ iconNamesCache,
2172
+ preWarmedContext,
2173
+ preWarmPromise: session.preWarmPromise
2174
+ }
2175
+ );
2176
+ absorbTurn(telemetry, turn);
2177
+ iconNamesCache = turn.iconNamesCache;
2178
+ preWarmedContext = turn.preWarmedContext;
2179
+ if (turn.control !== "proceed" && session.probeOnlyEnabled && telemetry.probeRepairUsed) {
2180
+ if (telemetry.evalResult !== void 0) {
2181
+ telemetry.evalResult = { ...telemetry.evalResult, runtimeProbeRepair: { attempted: true, compiled: false } };
2182
+ }
2183
+ console.log(
2184
+ `[simple] probe-only repair turn did not compile (${turn.outcome ?? "no tool call"}) \u2014 serving the pre-repair card`
2185
+ );
2186
+ break;
2187
+ }
2188
+ if (turn.control === "break") break;
2189
+ if (turn.control === "continue") {
2190
+ lastResultText = turn.lastResultText;
2191
+ lastDiffFailed = turn.lastDiffFailed;
2192
+ isEvalFeedback = turn.isEvalFeedback;
2193
+ continue;
2194
+ }
2195
+ telemetry.compiledCode = turn.compiledCode;
2196
+ telemetry.pairedSource = session.workspace.read() ?? "";
2197
+ telemetry.selfCheckPassed = turn.selfCheckPassed;
2198
+ if (a1Mode && a1Phase === "scaffold" && telemetry.selfCheckPassed && telemetry.compiledCode) {
2199
+ console.log(
2200
+ `[simple] A1: scaffold compiled (${telemetry.compiledCode.length}B, turns=${telemetry.turnsUsed}) \u2192 entering fill phase`
2201
+ );
2202
+ a1Phase = "fill";
2203
+ telemetry.selfCheckPassed = false;
2204
+ telemetry.compiledCode = "";
2205
+ telemetry.pairedSource = "";
2206
+ lastResultText = "";
2207
+ isEvalFeedback = false;
2208
+ continue;
2209
+ }
2210
+ if (a1Mode && a1Phase === "fill" && telemetry.selfCheckPassed) {
2211
+ console.log(`[simple] A1: fill completed (turns=${telemetry.turnsUsed}) \u2192 post phase`);
2212
+ a1Phase = "post";
2213
+ }
2214
+ if (!telemetry.initialResultDelivered && telemetry.compiledCode) {
2215
+ const sourceCode = session.workspace.read() ?? "";
2216
+ console.log(`[simple] compiled: ${telemetry.compiledCode.length}B`);
2217
+ if (params.onInitialResult) {
2218
+ await params.onInitialResult({ componentCode: telemetry.compiledCode, sourceCode });
2219
+ }
2220
+ telemetry.initialResultDelivered = true;
2221
+ const codingDoneMs = Date.now() - telemetry.codingStartedAtMs;
2222
+ console.log(
2223
+ `[simple] initial result delivered | ${codingDoneMs}ms | turns=${telemetry.turnsUsed} | tokens=${telemetry.totalIn + telemetry.totalOut} | compiled=${telemetry.compiledCode.length}B`
2224
+ );
2225
+ }
2226
+ if (!evalDone && telemetry.compiledCode && (session.tiersMod && (session.codeEvalEnabled || session.visualEvalEnabled) || session.probeOnlyEnabled)) {
2227
+ const evalRoundStart = Date.now();
2228
+ const round = await runEvalRound(
2229
+ {
2230
+ workspace: session.workspace,
2231
+ harness: session.harness,
2232
+ contract: params.contract,
2233
+ userPrompt: params.userPrompt,
2234
+ fixtureProps: params.fixtureProps,
2235
+ classification,
2236
+ evaluationAgent: session.agents.evaluation,
2237
+ visualEvalAgent: session.agents.visualEval,
2238
+ visualEvaluation: params.visualEvaluation,
2239
+ shellType: params.shellType,
2240
+ visualThreshold: session.visualThreshold,
2241
+ qualityMode: session.qualityMode,
2242
+ maxEvalRounds: session.maxEvalRounds,
2243
+ costTracker: session.costTracker,
2244
+ llmEvalMod: session.llmEvalMod,
2245
+ visualMod: session.visualMod,
2246
+ preWarmPromise: session.preWarmPromise,
2247
+ onProgress: params.onProgress,
2248
+ probeOnly: session.probeOnlyEnabled,
2249
+ probeRepairUsed: telemetry.probeRepairUsed,
2250
+ ...telemetry.probeRepairTrigger !== void 0 ? { probeRepairTrigger: telemetry.probeRepairTrigger } : {}
2251
+ },
2252
+ {
2253
+ compiledCode: telemetry.compiledCode,
2254
+ evalRoundsUsed: telemetry.evalRoundsUsed,
2255
+ preWarmedContext,
2256
+ prevModeSubcats,
2257
+ prevFailFingerprints
2258
+ }
2259
+ );
2260
+ telemetry.cumulativeEvalWallMs += Date.now() - evalRoundStart;
2261
+ telemetry.cumulativeEvalLlmMs += round.evalLlmMs;
2262
+ telemetry.evalRoundsUsed = round.evalRoundsUsed;
2263
+ prevModeSubcats = round.prevModeSubcats;
2264
+ prevFailFingerprints = round.prevFailFingerprints;
2265
+ preWarmedContext = round.preWarmedContext;
2266
+ if (round.evalResult) telemetry.evalResult = round.evalResult;
2267
+ if (round.contractFeedback) telemetry.contractFeedback = round.contractFeedback;
2268
+ evalDone = round.evalDone;
2269
+ absorbTokens(telemetry, round.evalTokens);
2270
+ absorbInLoopVisualTokens(telemetry, round.inLoopVisualTokens);
2271
+ if (round.control === "break") break;
2272
+ if (session.probeOnlyEnabled) {
2273
+ telemetry.probeRepairUsed = true;
2274
+ telemetry.probeRepairTrigger = round.evalResult?.runtimeProbe;
2275
+ }
2276
+ lastResultText = round.lastResultText;
2277
+ isEvalFeedback = round.isEvalFeedback;
2278
+ lastDiffFailed = round.lastDiffFailed;
2279
+ } else {
2280
+ break;
2281
+ }
2282
+ } while (telemetry.turnsUsed < maxTurns && !evalDone);
2283
+ if (session.probeOnlyEnabled && telemetry.probeRepairUsed && telemetry.evalResult !== void 0 && telemetry.evalResult.runtimeProbeRepair === void 0) {
2284
+ console.log(`[simple] probe-only repair turn not taken \u2014 turn cap (${maxTurns}) reached before it`);
2285
+ }
2286
+ telemetry.codingMs = Date.now() - telemetry.codingStartedAtMs;
2287
+ if (!telemetry.compiledCode) {
2288
+ for (const [, meta] of session.commitMeta) {
2289
+ if (meta.build.success && meta.selfCheck.passed && meta.build.compiledCode) {
2290
+ telemetry.compiledCode = meta.build.compiledCode;
2291
+ telemetry.selfCheckPassed = true;
2292
+ }
2293
+ }
2294
+ if (!telemetry.compiledCode) {
2295
+ for (const [, meta] of session.commitMeta) {
2296
+ if (meta.build.success && meta.build.compiledCode) {
2297
+ telemetry.compiledCode = meta.build.compiledCode;
2298
+ }
2299
+ }
2300
+ }
2301
+ if (telemetry.compiledCode && !telemetry.pairedSource) {
2302
+ telemetry.pairedSource = session.workspace.read() ?? "";
2303
+ }
2304
+ console.log(`[simple] compiled (fallback): ${telemetry.compiledCode.length}B`);
2305
+ }
2306
+ if (!telemetry.initialResultDelivered && params.onInitialResult && telemetry.compiledCode) {
2307
+ await params.onInitialResult({
2308
+ componentCode: telemetry.compiledCode,
2309
+ sourceCode: telemetry.pairedSource
2310
+ });
2311
+ }
2312
+ return telemetry.pairedSource || (session.workspace.read() ?? "");
2313
+ };
2314
+ }
2315
+ var FILE = "ui.tsx";
2316
+ var DIR = "/workspace";
2317
+ var FILEPATH = `${DIR}/${FILE}`;
2318
+ var AUTHOR = { name: "ggui-agent", email: "agent@ggui.ai" };
2319
+ var AgentWorkspace = class {
2320
+ vol;
2321
+ fs;
2322
+ constructor() {
2323
+ this.vol = new Volume();
2324
+ this.fs = createFsFromVolume(this.vol);
2325
+ }
2326
+ async init() {
2327
+ this.fs.mkdirSync(DIR, { recursive: true });
2328
+ await git.init({ fs: this.fs, dir: DIR });
2329
+ }
2330
+ // ── File Operations ────────────────────────────────
2331
+ read() {
2332
+ try {
2333
+ return this.fs.readFileSync(FILEPATH, "utf-8");
2334
+ } catch {
2335
+ return null;
2336
+ }
2337
+ }
2338
+ write(code) {
2339
+ this.fs.writeFileSync(FILEPATH, code);
2340
+ }
2341
+ cat(startLine, endLine) {
2342
+ const content = this.read();
2343
+ if (!content) return "(no file yet \u2014 use `write` to create it)";
2344
+ const lines = content.split("\n");
2345
+ const start = (startLine ?? 1) - 1;
2346
+ const end = endLine ?? lines.length;
2347
+ const padWidth = String(end).length;
2348
+ return lines.slice(start, end).map((line, i) => `${String(start + i + 1).padStart(padWidth)}\u2502 ${line}`).join("\n");
2349
+ }
2350
+ grep(pattern, contextLines = 0) {
2351
+ const content = this.read();
2352
+ if (!content) return "(no file)";
2353
+ const lines = content.split("\n");
2354
+ const matchedIndices = /* @__PURE__ */ new Set();
2355
+ const matchLines = /* @__PURE__ */ new Set();
2356
+ for (let i = 0; i < lines.length; i++) {
2357
+ if (new RegExp(pattern, "gi").test(lines[i])) {
2358
+ matchLines.add(i);
2359
+ for (let j = Math.max(0, i - contextLines); j <= Math.min(lines.length - 1, i + contextLines); j++) {
2360
+ matchedIndices.add(j);
2361
+ }
2362
+ }
2363
+ }
2364
+ if (matchedIndices.size === 0) return "(no matches)";
2365
+ const padWidth = String(lines.length).length;
2366
+ return [...matchedIndices].sort((a, b) => a - b).map((i) => {
2367
+ const prefix = matchLines.has(i) ? ">" : " ";
2368
+ return `${prefix} ${String(i + 1).padStart(padWidth)}\u2502 ${lines[i]}`;
2369
+ }).join("\n");
2370
+ }
2371
+ // ── Git Operations ─────────────────────────────────
2372
+ async stage() {
2373
+ await git.add({ fs: this.fs, dir: DIR, filepath: FILE });
2374
+ }
2375
+ async commit(message) {
2376
+ await this.stage();
2377
+ return git.commit({
2378
+ fs: this.fs,
2379
+ dir: DIR,
2380
+ message,
2381
+ author: AUTHOR
2382
+ });
2383
+ }
2384
+ async log(depth) {
2385
+ try {
2386
+ return await git.log({ fs: this.fs, dir: DIR, depth: depth ?? 20 });
2387
+ } catch {
2388
+ return [];
2389
+ }
2390
+ }
2391
+ async readFileAtCommit(oid) {
2392
+ const { blob } = await git.readBlob({
2393
+ fs: this.fs,
2394
+ dir: DIR,
2395
+ oid,
2396
+ filepath: FILE
2397
+ });
2398
+ return new TextDecoder().decode(blob);
2399
+ }
2400
+ async checkout(oid) {
2401
+ const content = await this.readFileAtCommit(oid);
2402
+ this.write(content);
2403
+ }
2404
+ // ── Diff Operations ────────────────────────────────
2405
+ async diffWorking() {
2406
+ const commits = await this.log(1);
2407
+ if (commits.length === 0) return "(no commits yet \u2014 new file)";
2408
+ const committed = await this.readFileAtCommit(commits[0].oid);
2409
+ const working = this.read() ?? "";
2410
+ return createPatch(FILE, committed, working, "committed", "working", {
2411
+ context: 3
2412
+ });
2413
+ }
2414
+ async diffBetween(oldOid, newOid) {
2415
+ const oldContent = await this.readFileAtCommit(oldOid);
2416
+ const newContent = await this.readFileAtCommit(newOid);
2417
+ return createPatch(
2418
+ FILE,
2419
+ oldContent,
2420
+ newContent,
2421
+ oldOid.slice(0, 7),
2422
+ newOid.slice(0, 7),
2423
+ { context: 3 }
2424
+ );
2425
+ }
2426
+ applyDiff(patch) {
2427
+ const current = this.read() ?? "";
2428
+ if (!patch.includes("@@")) {
2429
+ return {
2430
+ success: false,
2431
+ error: "Invalid diff format \u2014 missing @@ hunk headers. Use standard unified diff format."
2432
+ };
2433
+ }
2434
+ const result = applyPatch(current, patch, {
2435
+ fuzzFactor: 2,
2436
+ compareLine: (_lineNum, line, _op, patchContent) => {
2437
+ return line.trimEnd() === patchContent.trimEnd();
2438
+ }
2439
+ });
2440
+ if (result === false) {
2441
+ return {
2442
+ success: false,
2443
+ error: "Patch failed to apply \u2014 context lines in your diff do not match the current file. Re-read the current file provided in the prompt and produce a corrected diff."
2444
+ };
2445
+ }
2446
+ this.write(result);
2447
+ return { success: true };
2448
+ }
2449
+ };
2450
+
2451
+ // src/harness/in-loop-evaluation.ts
2452
+ function inLoopEvaluation(options) {
2453
+ return {
2454
+ text: options.evaluation?.enabled === true,
2455
+ visual: options.visualEvaluation?.enabled === true || options.qualityConfig?.visualEval === true
2456
+ };
2457
+ }
2458
+ function runsInLoopEvaluation(options) {
2459
+ const legs = inLoopEvaluation(options);
2460
+ return legs.text || legs.visual;
2461
+ }
2462
+
2463
+ // src/harness/coding/init-session.ts
2464
+ function resolveSessionAgents(config) {
2465
+ const coding = config.codingAgent;
2466
+ const evaluation = config.evaluationAgent ?? coding;
2467
+ const visualEval = config.visualEvalAgent ?? evaluation;
2468
+ return { coding, evaluation, visualEval };
2469
+ }
2470
+ async function initSession(input) {
2471
+ const { harness, params, agents } = input;
2472
+ const resolvedPolicy = input.resolvedPolicy ?? harness.policy ?? DEFAULT_HARNESS_POLICY;
2473
+ const startedAtMs = Date.now();
2474
+ const boilerplate = harness.what.boilerplate;
2475
+ console.log(
2476
+ `[simple] boilerplate: ${boilerplate.split("\n").length} lines, shell=${params.shellType ?? "fullscreen"}, screen=${params.screen ?? "universal"}`
2477
+ );
2478
+ const workspace = new AgentWorkspace();
2479
+ await workspace.init();
2480
+ const commitMeta = /* @__PURE__ */ new Map();
2481
+ workspace.write(boilerplate);
2482
+ await workspace.stage();
2483
+ await workspace.commit("scaffold: boilerplate");
2484
+ const systemPrompt = input.systemPromptOverride ?? harness.how.systemPrompt;
2485
+ const codingAgent = createAgent(agents.coding);
2486
+ await codingAgent.warmCache(
2487
+ agents.coding.model,
2488
+ systemPrompt,
2489
+ [APPLY_CHANGES_TOOL, GET_ICONS_TOOL],
2490
+ "required"
2491
+ );
2492
+ const { text: codeEvalEnabled, visual: visualEvalEnabled } = inLoopEvaluation(params);
2493
+ let preWarmPromise;
2494
+ if (codeEvalEnabled) {
2495
+ const evalSpec = agents.evaluation;
2496
+ preWarmPromise = import('./llm-evaluator-PN3WFCS4.js').then(
2497
+ (mod) => mod.preWarmEval(
2498
+ {
2499
+ sourceCode: "",
2500
+ originalPrompt: params.userPrompt,
2501
+ contract: params.contract,
2502
+ designContext: void 0,
2503
+ designMode: harness.designMode,
2504
+ profile: harness.profile
2505
+ },
2506
+ {
2507
+ provider: mapProviderForEvaluator(evalSpec.provider),
2508
+ model: evalSpec.model
2509
+ }
2510
+ )
2511
+ ).catch((e) => {
2512
+ console.warn(`[simple] eval pre-warm failed: ${e instanceof Error ? e.message : e}`);
2513
+ return null;
2514
+ });
2515
+ }
2516
+ const propsMatch = boilerplate.match(/interface Props \{[\s\S]*?\n\}/);
2517
+ const originalProps = propsMatch?.[0];
2518
+ const qualityConfig = params.qualityConfig;
2519
+ const qualityMode = qualityConfig?.quality ?? "fast";
2520
+ const probeOnlyEnabled = harness.check.runtimeRender !== void 0 && !codeEvalEnabled && !visualEvalEnabled;
2521
+ const maxEvalRounds = qualityMode === "high-quality" ? params.evaluation?.maxRounds ?? 3 : params.evaluation?.maxRounds ?? 2;
2522
+ const visualThreshold = params.visualEvaluation?.passThreshold ?? 70;
2523
+ let tiersMod = null;
2524
+ let llmEvalMod = null;
2525
+ let visualMod = null;
2526
+ let costTracker = null;
2527
+ if (codeEvalEnabled || visualEvalEnabled) {
2528
+ tiersMod = await import('./check/index.js');
2529
+ const costTrackerMod = await import('./cost-tracker-HHGQ4FTW.js');
2530
+ costTracker = new costTrackerMod.CostTracker(
2531
+ qualityConfig?.maxCostPerGeneration ?? null
2532
+ );
2533
+ if (codeEvalEnabled) {
2534
+ llmEvalMod = await import('./llm-evaluator-PN3WFCS4.js');
2535
+ }
2536
+ if (visualEvalEnabled) {
2537
+ visualMod = await import('./visual-evaluator-TGEXYQTP.js').catch(() => null);
2538
+ }
2539
+ }
2540
+ return {
2541
+ harness,
2542
+ workspace,
2543
+ codingAgent,
2544
+ agents,
2545
+ commitMeta,
2546
+ boilerplate,
2547
+ originalProps,
2548
+ systemPrompt,
2549
+ codeEvalEnabled,
2550
+ visualEvalEnabled,
2551
+ probeOnlyEnabled,
2552
+ maxEvalRounds,
2553
+ visualThreshold,
2554
+ qualityMode,
2555
+ tiersMod,
2556
+ llmEvalMod,
2557
+ visualMod,
2558
+ costTracker,
2559
+ preWarmPromise,
2560
+ resolvedPolicy,
2561
+ dupeBreak: createDupeBreakState(),
2562
+ startedAtMs
2563
+ };
2564
+ }
2565
+
2566
+ // src/adapters/generation-dispatch.ts
2567
+ function mapProviderForLLMRouter(provider) {
2568
+ return provider === "claude" ? "anthropic" : provider;
2569
+ }
2570
+ async function dispatchGeneration(params) {
2571
+ if ((params.enableRuntimeRender ?? true) === true) {
2572
+ void warmupRuntimeRenderProbe().catch(() => {
2573
+ });
2574
+ }
2575
+ const routerProvider = mapProviderForLLMRouter(params.provider);
2576
+ const resolvedModel = params.models?.coding ?? params.models?.default ?? params.model;
2577
+ const codingAgent = {
2578
+ provider: routerProvider,
2579
+ model: resolvedModel,
2580
+ routeOverride: params.routeOverride,
2581
+ onRetry: params.onRetry
2582
+ };
2583
+ const evaluationAgent = params.models?.evaluation ? {
2584
+ provider: routerProvider,
2585
+ model: params.models.evaluation,
2586
+ routeOverride: params.routeOverride,
2587
+ onRetry: params.onRetry
2588
+ } : void 0;
2589
+ const visualEvalAgent = params.visualEvalAgent ? {
2590
+ provider: params.visualEvalAgent.provider,
2591
+ model: params.visualEvalAgent.model,
2592
+ ...params.visualEvalAgent.apiKey !== void 0 ? { routeOverride: { apiKey: params.visualEvalAgent.apiKey } } : {},
2593
+ onRetry: params.onRetry
2594
+ } : void 0;
2595
+ const effectiveContract = params.contract;
2596
+ const sessionParams = {
2597
+ userPrompt: params.originalPrompt ?? params.userPrompt,
2598
+ contract: effectiveContract,
2599
+ shellType: params.shellType,
2600
+ screen: params.screen,
2601
+ evaluation: params.evaluation ? {
2602
+ enabled: params.evaluation.enabled,
2603
+ passThreshold: params.evaluation.passThreshold,
2604
+ maxRounds: params.evaluation.maxRounds ?? params.maxEvalRounds
2605
+ } : void 0,
2606
+ visualEvaluation: params.visualEvaluation,
2607
+ qualityConfig: params.qualityConfig,
2608
+ fixtureProps: params.fixtureProps,
2609
+ ...params.onCodingTurnPrompt !== void 0 ? { onCodingTurnPrompt: params.onCodingTurnPrompt } : {}
2610
+ };
2611
+ const classification = classifyAxes({
2612
+ contract: effectiveContract ?? {},
2613
+ prompt: sessionParams.userPrompt
2614
+ });
2615
+ const harness = createHarness({
2616
+ classification,
2617
+ contract: effectiveContract ?? {},
2618
+ prompt: sessionParams.userPrompt,
2619
+ shellType: sessionParams.shellType,
2620
+ screen: sessionParams.screen,
2621
+ // Forward the registered catalog into createHarness so
2622
+ // `generateBoilerplate` emits correct `import { useLeafletMap }
2623
+ // from '<package>'` lines for thin contract refs. The closure
2624
+ // capture below (systemPromptBuilder) wires the SAME catalog
2625
+ // into the HOW leg.
2626
+ ...params.appGadgets !== void 0 ? { appGadgets: params.appGadgets } : {},
2627
+ // Forward the third-party wrapper `.d.ts` map so
2628
+ // `WhatLeg.gadgetTypes` reaches the coding-agent's typecheck
2629
+ // overlay (autoCommit → runTier0Checks → typecheck). The
2630
+ // systemPromptBuilder closure below also captures `gadgetTypes`
2631
+ // from `params` directly for the prompt `Type:` lines.
2632
+ ...params.gadgetTypes !== void 0 ? { gadgetTypes: params.gadgetTypes } : {},
2633
+ // Design mode + canvas — the harness carries them to every leg.
2634
+ ...params.designMode !== void 0 ? { designMode: params.designMode } : {},
2635
+ // ggui#1117 — the canvas is ALWAYS carried: a stated one, else the class
2636
+ // shell × screen renders on. The axis checks judge for it
2637
+ // (`universal.root_width_cap` stands down without one — which is what every
2638
+ // served generation got, since the serving runtime states no canvas); the
2639
+ // constrained prompt ignores it (INVARIANT 1, `design-mode.pin.test.ts`),
2640
+ // so the prompt digests do not move; the free prompt renders it as before.
2641
+ canvas: params.canvas ?? canvasForRendering(params.shellType, params.screen),
2642
+ ...params.profile !== void 0 ? { profile: params.profile } : {},
2643
+ // Policy is pre-resolved at the dispatch boundary so `createHarness`
2644
+ // stays free of experiment plumbing. `resolveHarnessPolicy` applies
2645
+ // the `GGUI_POLICY_PROFILE` experimental-profile layer and hands the
2646
+ // result to the constructor.
2647
+ policy: resolveHarnessPolicy(classification),
2648
+ // Fully-populated system-prompt builder. This `buildSystemPrompt`
2649
+ // wrapper pre-fills pitfalls + design-system docs + primitives +
2650
+ // wire content.
2651
+ //
2652
+ // `appGadgets` is captured from `params` at dispatch time and
2653
+ // threaded into the wrapper so the code-gen prompt's
2654
+ // `clientCapabilities — registered catalog` section renders the
2655
+ // operator-registered gadget set (Leaflet, Mapbox, …) instead of
2656
+ // falling back to STDLIB. When omitted, the wrapper defaults to
2657
+ // STDLIB-only.
2658
+ // `params.gadgetTypes` is captured into the closure so the code-gen
2659
+ // prompt's gadget catalog renders a `Type:` line per third-party
2660
+ // gadget (the LLM sees a wrapper's real call shape it otherwise
2661
+ // can't know).
2662
+ systemPromptBuilder: ({
2663
+ userRequest,
2664
+ shellType,
2665
+ screen,
2666
+ axisDelta,
2667
+ designMode,
2668
+ canvas,
2669
+ profile
2670
+ }) => buildSystemPrompt(
2671
+ userRequest,
2672
+ shellType,
2673
+ screen,
2674
+ axisDelta,
2675
+ params.appGadgets,
2676
+ params.gadgetTypes,
2677
+ designMode,
2678
+ canvas,
2679
+ profile
2680
+ ),
2681
+ // Pre-filtered axis-check registry. The `matches()` predicate
2682
+ // selects the checks relevant to this generation's axis vector.
2683
+ axisChecks: REGISTRY.filter(
2684
+ (check) => matches(classification.vector, check)
2685
+ ),
2686
+ // The runtime-render check, built under the caller's probe config.
2687
+ // Heavy deps (happy-dom + wire + design) stay on the dispatch side of
2688
+ // the boundary; the skeleton createHarness defaults this to undefined.
2689
+ //
2690
+ // Gated behind `enableRuntimeRender` (default true) so
2691
+ // benchmark/dev callers can opt out of in-loop probe execution and
2692
+ // run the probe externally as a final post-generation check.
2693
+ runtimeRender: params.enableRuntimeRender ?? true ? params.runtimeRender ?? DEFAULT_RUNTIME_RENDER_CHECK : void 0
2694
+ });
2695
+ console.log(
2696
+ `[harness] id=${harness.id} name=${harness.name} workflow=${harness.process.workflow.name} overrides=${harness.meta.overrides.join(",") || "none"}`
2697
+ );
2698
+ console.log(
2699
+ `[simple] axis-vector: ${JSON.stringify(classification.vector)} risk=${classification.riskTier}`
2700
+ );
2701
+ if (harness.process.workflow.name === "staged") {
2702
+ throw new Error(
2703
+ `[dispatch] STAGED workflow routed but architect/coder TaskRunners are not wired on the dispatch path. This is a misconfiguration (probably pickProcessMode returning WORKFLOWS.staged). For staged-like behaviour, set process.mode="staged" + workflow.name="single_pass" instead \u2014 the A1 scaffold\u2192fill phase machine lives inside the generate runner and fires without the workflow topology. See ralph #3 report (2026-04-14) for the full rationale.`
2704
+ );
2705
+ }
2706
+ const agents = resolveSessionAgents({
2707
+ codingAgent,
2708
+ evaluationAgent,
2709
+ ...visualEvalAgent !== void 0 ? { visualEvalAgent } : {}
2710
+ });
2711
+ const { resolveRunPolicyForProfile } = await import('./policy-VCYKGSAC.js');
2712
+ const resolvedPolicy = resolveRunPolicyForProfile(process.env.GGUI_POLICY_PROFILE, harness, {
2713
+ provider: routerProvider,
2714
+ modelId: resolvedModel
2715
+ });
2716
+ let systemPromptOverride;
2717
+ {
2718
+ const { PRIMITIVES_DOCUMENTATION: PRIMITIVES_DOCUMENTATION2 } = await import('./validation/index.js');
2719
+ const fullBytes = Buffer.byteLength(PRIMITIVES_DOCUMENTATION2, "utf8");
2720
+ const docFormat = resolvedPolicy.context.primitiveDocFormat ?? "markdown";
2721
+ const indexMode = resolvedPolicy.context.primitiveIndex ?? "off";
2722
+ const mode = resolvedPolicy.context.primitiveDocSlice ?? "full";
2723
+ let doc = PRIMITIVES_DOCUMENTATION2;
2724
+ if (docFormat === "ts" && indexMode === "off") {
2725
+ const { PRIMITIVES_DOCUMENTATION_TS } = await import('./tools/get-primitives-ts.js');
2726
+ doc = PRIMITIVES_DOCUMENTATION_TS;
2727
+ const tsBytes = Buffer.byteLength(doc, "utf8");
2728
+ console.log(
2729
+ `[simple] primitive-doc-ts-bytes=${tsBytes} baseline=${fullBytes} drop=${((1 - tsBytes / fullBytes) * 100).toFixed(1)}%`
2730
+ );
2731
+ }
2732
+ const formatBytes = Buffer.byteLength(doc, "utf8");
2733
+ if (mode === "axis-keyed" || mode === "axis-keyed+contract") {
2734
+ const {
2735
+ computePrimitiveAllowlist,
2736
+ computeContractPrimitives,
2737
+ slicePrimitiveDocumentation,
2738
+ slicePrimitiveDocumentationTs
2739
+ } = await import('./primitive-slice-WTMBLYFJ.js');
2740
+ let allowlist = computePrimitiveAllowlist(classification);
2741
+ if (mode === "axis-keyed+contract") {
2742
+ const fromContract = computeContractPrimitives(effectiveContract);
2743
+ allowlist = [.../* @__PURE__ */ new Set([...allowlist, ...fromContract])].sort();
2744
+ }
2745
+ const excludes = resolvedPolicy.context.primitiveDocExcludes ?? [];
2746
+ if (excludes.length > 0) {
2747
+ const excludeSet = new Set(excludes);
2748
+ allowlist = allowlist.filter((p) => !excludeSet.has(p));
2749
+ }
2750
+ doc = docFormat === "ts" && indexMode === "off" ? slicePrimitiveDocumentationTs(doc, allowlist) : slicePrimitiveDocumentation(doc, allowlist);
2751
+ const slicedBytes = Buffer.byteLength(doc, "utf8");
2752
+ const excludeTag = excludes.length ? ` excludes=${excludes.join(",")}` : "";
2753
+ console.log(
2754
+ `[simple] primitive-doc-bytes=${slicedBytes} mode=${mode} baseline=${formatBytes} drop=${((1 - slicedBytes / formatBytes) * 100).toFixed(1)}% allowlist=${allowlist.length}/${allowlist.join(",")}${excludeTag}`
2755
+ );
2756
+ } else {
2757
+ console.log(
2758
+ `[simple] primitive-doc-bytes=${formatBytes} mode=full baseline=${formatBytes} drop=0.0%`
2759
+ );
2760
+ }
2761
+ if (indexMode !== "off") {
2762
+ const { buildPrimitiveIndex } = await import('./primitive-index-2OF6F4TZ.js');
2763
+ doc = buildPrimitiveIndex(PRIMITIVES_DOCUMENTATION2, indexMode);
2764
+ const indexBytes = Buffer.byteLength(doc, "utf8");
2765
+ console.log(
2766
+ `[simple] primitive-index-bytes=${indexBytes} mode=${indexMode} baseline=${fullBytes} drop=${((1 - indexBytes / fullBytes) * 100).toFixed(1)}%`
2767
+ );
2768
+ }
2769
+ if (doc !== PRIMITIVES_DOCUMENTATION2) {
2770
+ systemPromptOverride = harness.how.systemPrompt.replace(PRIMITIVES_DOCUMENTATION2, doc);
2771
+ }
2772
+ const sentPrompt = systemPromptOverride ?? harness.how.systemPrompt;
2773
+ console.log(
2774
+ `[simple] system-prompt-bytes=${Buffer.byteLength(sentPrompt, "utf8")} doc-format=${docFormat === "ts" && indexMode === "off" ? "ts" : docFormat} doc-bytes=${Buffer.byteLength(doc, "utf8")} mode=${indexMode !== "off" ? `index:${indexMode}` : mode}`
2775
+ );
2776
+ if (resolvedPolicy.context.planFirstTurn) {
2777
+ const phaseGuidance = `
2778
+
2779
+ ## Generation Phases
2780
+
2781
+ This generation runs in TWO phases:
2782
+
2783
+ **Turn 1 \u2014 PLAN.** The harness only advertises the \`write_plan\` tool. Commit to:
2784
+ \u2022 \`components\`: which primitives/components you will use (from the reference above).
2785
+ \u2022 \`structure\`: a brief JSX outline showing nesting.
2786
+ \u2022 \`wiring\`: state/actions/streams you will use.
2787
+ Do NOT attempt to write code \u2014 \`apply_changes\` is not available on turn 1.
2788
+
2789
+ **Turn 2+ \u2014 EXECUTE.** Now write code via \`apply_changes\`. If your patches accumulate into a tangled broken state, \`rewrite\` is available as an escape. There is no fetch tool \u2014 the component reference above is complete.
2790
+
2791
+ Your plan is a commitment, not a design doc. Keep it tight. Do not over-plan; just enough to pick components and sketch structure before writing.`;
2792
+ systemPromptOverride = (systemPromptOverride ?? harness.how.systemPrompt) + phaseGuidance;
2793
+ console.log(`[simple] plan-first-turn=on`);
2794
+ }
2795
+ }
2796
+ const session = await initSession({
2797
+ harness,
2798
+ params: sessionParams,
2799
+ agents,
2800
+ resolvedPolicy,
2801
+ systemPromptOverride
2802
+ });
2803
+ const telemetry = createTelemetry();
2804
+ const generate = createGenerateTaskRunner({
2805
+ session,
2806
+ params: sessionParams,
2807
+ classification,
2808
+ telemetry,
2809
+ maxTurns: params.maxAttempts ?? 8
2810
+ });
2811
+ const runResult = await runHarness({
2812
+ harness,
2813
+ prompt: sessionParams.userPrompt,
2814
+ contract: params.contract ?? { intent: "" },
2815
+ taskRunners: { generate },
2816
+ compile: async () => telemetry.compiledCode || null,
2817
+ passes: () => true,
2818
+ skipCheck: true,
2819
+ maxIterations: 1
2820
+ });
2821
+ if (!runResult.ok) {
2822
+ console.error(
2823
+ `[dispatch] runHarness terminated: ${runResult.reason} (source=${runResult.finalSource ? runResult.finalSource.length + "B" : "null"}, compiled=${runResult.finalCompiled ? runResult.finalCompiled.length + "B" : "null"})`
2824
+ );
2825
+ }
2826
+ const finalSource = runResult.finalSource ?? "";
2827
+ const result = await assembleGenerationResult({
2828
+ session,
2829
+ telemetry,
2830
+ source: finalSource
2831
+ });
2832
+ return withArmRecord(result, params);
2833
+ }
2834
+ function withArmRecord(result, params) {
2835
+ const recordedCanvas = params.canvas ?? (params.designMode === "free" ? canvasForRendering(params.shellType, params.screen) : void 0);
2836
+ return {
2837
+ ...result,
2838
+ ...params.designMode !== void 0 ? { designMode: params.designMode } : {},
2839
+ ...recordedCanvas !== void 0 ? { canvas: recordedCanvas } : {}
2840
+ };
2841
+ }
2842
+
2843
+ export { dispatchGeneration, inLoopEvaluation, runsInLoopEvaluation };
2844
+ //# sourceMappingURL=chunk-R5GIGPW2.js.map
2845
+ //# sourceMappingURL=chunk-R5GIGPW2.js.map