@tea-agent/loop-agent 0.39.0-beta.1 → 0.39.0-beta.10

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 (331) hide show
  1. package/AGENTS.md +4 -1
  2. package/CHANGELOG.md +248 -95
  3. package/README.md +9 -5
  4. package/bin/loop-agent.js +7 -3
  5. package/dist/application/task-lifecycle/advance.js +12 -0
  6. package/dist/application/task-lifecycle/recommendations.js +13 -2
  7. package/dist/build-stamp.json +3 -3
  8. package/dist/cli/command-definitions.js +23 -5
  9. package/dist/cli/program.js +21 -2
  10. package/dist/cli/update/notifier.js +2 -2
  11. package/dist/cli/update/runtime-activity.js +1 -29
  12. package/dist/cli.js +2 -1
  13. package/dist/commands/client-recovery.js +3 -0
  14. package/dist/commands/dag-follow-up.js +138 -0
  15. package/dist/commands/dag-rerun.js +55 -1
  16. package/dist/commands/init-model-catalog.js +464 -0
  17. package/dist/commands/init-upgrade.js +265 -97
  18. package/dist/commands/init.js +455 -28
  19. package/dist/commands/inspect-next.js +8 -0
  20. package/dist/commands/task-advance.js +19 -0
  21. package/dist/executors/dag-pi-executor.js +2306 -61
  22. package/dist/executors/pi-executor.js +7 -0
  23. package/dist/executors/pi-extension-resolver.js +233 -0
  24. package/dist/executors/pi-playwright-cli-tool.js +74 -28
  25. package/dist/executors/pi-read-budget-policy.js +239 -0
  26. package/dist/executors/pi-sdk-executor.js +180 -33
  27. package/dist/executors/shell-executor.js +1249 -224
  28. package/dist/executors/shell-write-guard.js +14 -0
  29. package/dist/executors/workspace-write-snapshot.js +68 -0
  30. package/dist/infrastructure/harness/atomic-write.js +23 -0
  31. package/dist/shared/dag-failure-category.js +150 -0
  32. package/dist/shared/dag-prompt-override.js +27 -0
  33. package/dist/shared/openspec-spec.js +70 -4
  34. package/dist/shared/operator/capabilities.js +39 -0
  35. package/dist/shared/playwright-cli-command-policy.js +15 -0
  36. package/dist/shared/update/console-notifier.js +100 -0
  37. package/dist/{cli → shared}/update/npm-client.js +70 -15
  38. package/dist/{cli → shared}/update/state.js +41 -13
  39. package/dist/task/config-types.js +50 -5
  40. package/dist/task/contract/apply.js +11 -1
  41. package/dist/task/contract/constants.js +2 -0
  42. package/dist/task/contract/hash.js +3 -0
  43. package/dist/task/contract/observe.js +25 -4
  44. package/dist/task/contract/paths.js +2 -1
  45. package/dist/task/contract/project.js +18 -1
  46. package/dist/task/contract/schema.js +4 -1
  47. package/dist/task/contract/transaction.js +12 -0
  48. package/dist/task/frontend-project-capability.js +199 -18
  49. package/dist/task/source-prepare/completeness.js +188 -0
  50. package/dist/task/source-prepare/fragment-inventory.js +477 -0
  51. package/dist/task/source-prepare/index.js +3 -0
  52. package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
  53. package/dist/task/source-prepare/ledger-review.js +214 -0
  54. package/dist/task/source-prepare/ledger.js +545 -0
  55. package/dist/task/source-prepare/parse-intent.js +24 -4
  56. package/dist/task/source-prepare/prepare.js +298 -1
  57. package/dist/task/source-prepare/semantic-intake.js +25 -5
  58. package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
  59. package/dist/task/source-prepare/types.js +22 -0
  60. package/dist/task/source-references.js +22 -1
  61. package/dist/worker/cli.js +18 -2
  62. package/dist/worker/console/chat/chat-event-store.js +62 -4
  63. package/dist/worker/console/chat/context-panel.js +7 -0
  64. package/dist/worker/console/chat/deferred-turn.js +12 -0
  65. package/dist/worker/console/chat/operation-card.js +8 -1
  66. package/dist/worker/console/chat/pi-console-config.js +81 -10
  67. package/dist/worker/console/chat/pi-runtime.js +264 -16
  68. package/dist/worker/console/chat/provider-error.js +98 -0
  69. package/dist/worker/console/chat/routes.js +298 -32
  70. package/dist/worker/console/chat/session-stats.js +179 -0
  71. package/dist/worker/console/chat/session-store.js +93 -63
  72. package/dist/worker/console/chat/todos.js +115 -0
  73. package/dist/worker/console/chat/tool-preview.js +42 -0
  74. package/dist/worker/console/chat/turn-process.js +20 -55
  75. package/dist/worker/console/chat/user-questions.js +266 -0
  76. package/dist/worker/console/chat/workspace-landing.js +14 -0
  77. package/dist/worker/console/console-handoff.js +82 -0
  78. package/dist/worker/console/console-update-and-init.js +115 -0
  79. package/dist/worker/console/console-update-runtime.js +84 -0
  80. package/dist/worker/console/dag-execution-receipt.js +33 -0
  81. package/dist/worker/console/draft-store.js +49 -0
  82. package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
  83. package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
  84. package/dist/worker/console/index.js +3 -0
  85. package/dist/worker/console/inspect-split.js +18 -0
  86. package/dist/worker/console/native-directory-picker.js +13 -0
  87. package/dist/worker/console/operation-run-facts.js +19 -3
  88. package/dist/worker/console/operation-store.js +227 -7
  89. package/dist/worker/console/operator-actions.js +388 -0
  90. package/dist/worker/console/operator-surface-health.js +1 -0
  91. package/dist/worker/console/operator-user-error.js +2 -2
  92. package/dist/worker/console/prd-intake-bridge.js +13 -0
  93. package/dist/worker/console/routes.js +355 -3
  94. package/dist/worker/console/security.js +67 -13
  95. package/dist/worker/console/server.js +609 -164
  96. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-BAsjg7ew.js} +1 -1
  97. package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-CyIlrwZw.js} +1 -1
  98. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-Bo4tobhH.js} +1 -1
  99. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-Dfpow9gu.js} +1 -1
  100. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-DPmzG_B4.js} +1 -1
  101. package/dist/worker/console/static/assets/channel-DSrJAn0-.js +1 -0
  102. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-pKQpB1UW.js} +1 -1
  103. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-5n9YYquZ.js} +1 -1
  104. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-DP3CtA2a.js} +1 -1
  105. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-DYxMJPUy.js} +1 -1
  106. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-CS3D50Io.js} +1 -1
  107. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-gSdDfo5_.js} +1 -1
  108. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-A48q0ZNt.js} +1 -1
  109. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-ow8Mm3DY.js} +1 -1
  110. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-CMKfLWYE.js} +1 -1
  111. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BiTBdQPX.js} +1 -1
  112. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DeMmPl0_.js +1 -0
  113. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DeMmPl0_.js +1 -0
  114. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-Cm7exRz_.js} +1 -1
  115. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-D9O5qGOu.js} +1 -1
  116. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-CkvQnako.js} +1 -1
  117. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-B1rzp7CB.js} +1 -1
  118. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-BP07Ipcc.js} +1 -1
  119. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BlPzweQb.js} +1 -1
  120. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-CxPa7n0S.js} +1 -1
  121. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-CMTZs53A.js} +1 -1
  122. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-CxG_X52r.js} +1 -1
  123. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-D8axtJM0.js} +1 -1
  124. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-DIsGzn7b.js} +1 -1
  125. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CU9zkDG0.js} +1 -1
  126. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-DCjTmz5P.js} +1 -1
  127. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-BksdmFzg.js} +1 -1
  128. package/dist/worker/console/static/assets/index-B-O5ChYq.css +1 -0
  129. package/dist/worker/console/static/assets/index-CfnY8AQx.js +407 -0
  130. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-CGzzzJtL.js} +1 -1
  131. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-BdseB80N.js} +1 -1
  132. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-DM9gsgha.js} +1 -1
  133. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-3q-_9R2b.js} +1 -1
  134. package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-B8N-Mxsd.js} +1 -1
  135. package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-EPIHUUsu.js} +5 -5
  136. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-CxwO12aa.js} +1 -1
  137. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-bPqbVX6Y.js} +1 -1
  138. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-KzqIvzeX.js} +1 -1
  139. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-CyofpWiV.js} +1 -1
  140. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-cTWJvLTP.js} +1 -1
  141. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-BK--q27B.js} +1 -1
  142. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-C0SDOHXK.js} +1 -1
  143. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-Cong6YIo.js} +1 -1
  144. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DUVkgZx8.js} +1 -1
  145. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C7XgxuZg.js} +1 -1
  146. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dzbnd5oX.js +1 -0
  147. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-D9Ayvwcy.js} +2 -2
  148. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BY0hHk6G.js +8 -0
  149. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-Byb000cH.js} +1 -1
  150. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DMLegPt4.js} +1 -1
  151. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-ChMMeOsP.js} +1 -1
  152. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-bEMEPisW.js} +1 -1
  153. package/dist/worker/console/static/index.html +2 -2
  154. package/dist/worker/console/static-src/active-run-badge.js +17 -0
  155. package/dist/worker/console/static-src/app/console-types.js +68 -4
  156. package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
  157. package/dist/worker/console/static-src/app/useOperatorActions.js +41 -6
  158. package/dist/worker/console/static-src/app/useRecoveryConsole.js +45 -10
  159. package/dist/worker/console/static-src/app/useRunProgress.js +47 -0
  160. package/dist/worker/console/static-src/app/useTaskWizard.js +74 -2
  161. package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
  162. package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
  163. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +140 -8
  164. package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
  165. package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
  166. package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
  167. package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
  168. package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
  169. package/dist/worker/console/static-src/operator-chat/turn-submission.js +16 -3
  170. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +168 -72
  171. package/dist/worker/console/static-src/operator-chat/useChatStream.js +102 -21
  172. package/dist/worker/console/static-src/operator-chat/useChatThread.js +184 -4
  173. package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
  174. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
  175. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  176. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
  177. package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
  178. package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
  179. package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
  180. package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
  181. package/dist/worker/console/static-src/shell/workspace-route.js +337 -0
  182. package/dist/worker/console/workspace-context.js +344 -0
  183. package/dist/worker/console/workspace-registry.js +214 -0
  184. package/dist/worker/loop-agent/loop-agent-client.js +7 -3
  185. package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
  186. package/dist/worker/materialize/harness-task-lineage.js +5 -2
  187. package/dist/worker/observability/init-runtime-activity.js +26 -0
  188. package/dist/worker/observability/read-model.js +22 -0
  189. package/dist/worker/observe/node-input.js +160 -3
  190. package/dist/worker/observe/node-process.js +377 -0
  191. package/dist/worker/observe/routes.js +224 -3
  192. package/dist/worker/observe/server.js +4 -0
  193. package/dist/worker/observe/shell-handler-keys.js +34 -0
  194. package/dist/worker/observe/static/api.js +90 -4
  195. package/dist/worker/observe/static/app.js +18 -70
  196. package/dist/worker/observe/static/constants.js +8 -1
  197. package/dist/worker/observe/static/dag-history-labels.js +4 -0
  198. package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
  199. package/dist/worker/observe/static/dag-node-purpose.js +208 -0
  200. package/dist/worker/observe/static/format.js +60 -0
  201. package/dist/worker/observe/static/index.html +6 -1
  202. package/dist/worker/observe/static/inspect-workspace.js +307 -0
  203. package/dist/worker/observe/static/markdown-render.js +20 -1
  204. package/dist/worker/observe/static/operator-chrome.css +145 -20
  205. package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
  206. package/dist/worker/observe/static/operator-chrome.js +330 -96
  207. package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
  208. package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
  209. package/dist/worker/observe/static/router.d.ts +46 -0
  210. package/dist/worker/observe/static/router.js +25 -1
  211. package/dist/worker/observe/static/state.d.ts +50 -0
  212. package/dist/worker/observe/static/state.js +184 -1
  213. package/dist/worker/observe/static/styles.css +395 -0
  214. package/dist/worker/observe/static/views/dag-graph.js +4 -2
  215. package/dist/worker/observe/static/views/dag-inspector.js +1067 -9
  216. package/dist/worker/observe/static/views/dag.d.ts +13 -0
  217. package/dist/worker/observe/static/views/dag.js +14 -5
  218. package/dist/worker/observe/static/views/dashboard.js +6 -4
  219. package/dist/worker/observe/static/views/failures.js +5 -2
  220. package/dist/worker/observe/static/views/night.js +2 -2
  221. package/dist/worker/observe/static/views/pool.js +1 -0
  222. package/dist/worker/observe/static/views/run.js +3 -1
  223. package/dist/worker/observe/static/views/session-timeline.js +78 -9
  224. package/dist/worker/observe/static/views/task.js +56 -1
  225. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +49 -3
  226. package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
  227. package/dist/workflows/dag/backend-test-pytest-collection.js +98 -14
  228. package/dist/workflows/dag/backend-test-writer-completeness.js +10 -5
  229. package/dist/workflows/dag/contract-output-registry.js +15 -0
  230. package/dist/workflows/dag/contract-validator-registrations.js +1 -2
  231. package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
  232. package/dist/workflows/dag/dynamic-runtime/map.js +1 -1
  233. package/dist/workflows/dag/failure-category.js +7 -116
  234. package/dist/workflows/dag/frontend-closeout.js +221 -0
  235. package/dist/workflows/dag/frontend-design-policy.js +400 -0
  236. package/dist/workflows/dag/frontend-human-decision.js +182 -0
  237. package/dist/workflows/dag/frontend-implementation-contract.js +538 -166
  238. package/dist/workflows/dag/frontend-plan-render.js +24 -0
  239. package/dist/workflows/dag/frontend-prewrite-gate.js +388 -392
  240. package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
  241. package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
  242. package/dist/workflows/dag/frontend-recovery-controller.js +202 -0
  243. package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
  244. package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
  245. package/dist/workflows/dag/frontend-recovery-run.js +33 -20
  246. package/dist/workflows/dag/frontend-repair.js +7 -424
  247. package/dist/workflows/dag/frontend-review-context.js +288 -14
  248. package/dist/workflows/dag/frontend-review-findings.js +270 -0
  249. package/dist/workflows/dag/frontend-risk.js +15 -2
  250. package/dist/workflows/dag/frontend-shadow-dual-write.js +814 -0
  251. package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
  252. package/dist/workflows/dag/frontend-shape-facts.js +409 -0
  253. package/dist/workflows/dag/frontend-shape.js +427 -0
  254. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
  255. package/dist/workflows/dag/frontend-split-application-service.js +203 -0
  256. package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
  257. package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
  258. package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
  259. package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
  260. package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
  261. package/dist/workflows/dag/frontend-test-html-report.js +3 -1
  262. package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
  263. package/dist/workflows/dag/frontend-test-layout.js +159 -0
  264. package/dist/workflows/dag/frontend-test-markdown.js +61 -0
  265. package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
  266. package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
  267. package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
  268. package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
  269. package/dist/workflows/dag/frontend-verification-trace.js +52 -17
  270. package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
  271. package/dist/workflows/dag/frontend-writer-admission.js +272 -0
  272. package/dist/workflows/dag/frontend-writer-status.js +244 -0
  273. package/dist/workflows/dag/init-hybrid.js +984 -613
  274. package/dist/workflows/dag/interrupt-request.js +10 -3
  275. package/dist/workflows/dag/lifecycle.js +3 -3
  276. package/dist/workflows/dag/node-execution.js +468 -58
  277. package/dist/workflows/dag/prompt.js +44 -19
  278. package/dist/workflows/dag/recovery-lease.js +80 -0
  279. package/dist/workflows/dag/report.js +37 -1
  280. package/dist/workflows/dag/rerun-feedback.js +124 -1
  281. package/dist/workflows/dag/rerun-plan.js +144 -5
  282. package/dist/workflows/dag/rerun-run.js +81 -6
  283. package/dist/workflows/dag/rerun-task.js +29 -0
  284. package/dist/workflows/dag/retry-policy.js +318 -8
  285. package/dist/workflows/dag/runner.js +429 -122
  286. package/dist/workflows/dag/scheduler.js +114 -16
  287. package/dist/workflows/dag/structured-output-repair.js +712 -0
  288. package/dist/workflows/dag/types.js +321 -31
  289. package/dist/workflows/dag/upstream-artifacts.js +0 -4
  290. package/dist/workflows/dag/validate.js +92 -32
  291. package/docs/README.md +2 -0
  292. package/docs/architecture/evolution.md +45 -0
  293. package/docs/init-surface.manifest.json +9 -0
  294. package/docs/operations/README.md +1 -1
  295. package/docs/operations/local-development-environment.md +21 -0
  296. package/docs/templates/README.md +2 -1
  297. package/docs/templates/agent-dag-report.schema.json +8 -2
  298. package/docs/templates/agent-dag.schema.json +45 -1
  299. package/docs/templates/frontend-implementation-contract.schema.json +684 -24
  300. package/docs/templates/frontend-test-case-checklist.md +2 -2
  301. package/docs/templates/frontend-test-dag.json +8 -10
  302. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
  303. package/docs/templates/init-managed-agents.md +13 -3
  304. package/docs/templates/spec-registry.schema.json +45 -0
  305. package/harness.json +3 -3
  306. package/package.json +7 -1
  307. package/scripts/next-info.mjs +356 -0
  308. package/scripts/next-publish-gate.mjs +238 -0
  309. package/scripts/release-source-binding.mjs +251 -0
  310. package/skills/codebase-scout/SKILL.md +1 -1
  311. package/skills/fe-test-ui-scout/SKILL.md +65 -0
  312. package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
  313. package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
  314. package/skills/frontend-bounded-implement/SKILL.md +8 -14
  315. package/skills/frontend-design-review/SKILL.md +20 -41
  316. package/skills/frontend-implementation/SKILL.md +3 -4
  317. package/skills/frontend-implementation/references/design-spec.md +15 -10
  318. package/skills/frontend-implementation/references/node-contracts.md +23 -13
  319. package/skills/frontend-review/SKILL.md +22 -14
  320. package/skills/frontend-review/references/review-findings.md +6 -7
  321. package/skills/loop-agent/references/command-reference.md +12 -4
  322. package/skills/loop-agent/references/hybrid-dag.md +8 -1
  323. package/skills/playwright-cli/SKILL.md +23 -1
  324. package/skills/playwright-cli-case-generator/SKILL.md +29 -8
  325. package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
  326. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
  327. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
  328. package/dist/worker/console/static/assets/index-DTZOKgAn.js +0 -404
  329. package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
  330. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
  331. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
@@ -98,6 +98,13 @@ export function isEphemeralToolCachePath(filePath) {
98
98
  const normalized = normalizePath(filePath);
99
99
  if (!normalized)
100
100
  return false;
101
+ // CodeGraph extension sync side effect (plan 2026-08-21 D7): a loaded
102
+ // pi-codegraph extension keeps an existing .codegraph/ index fresh at
103
+ // session start; those index files are extension-owned run state, not
104
+ // writer output, so they never count as write-guard violations.
105
+ if (normalized === ".codegraph" || normalized.startsWith(".codegraph/")) {
106
+ return true;
107
+ }
101
108
  if (normalized === ".pytest_cache" ||
102
109
  normalized.startsWith(".pytest_cache/")) {
103
110
  return true;
@@ -117,6 +124,13 @@ export function isEphemeralToolCachePath(filePath) {
117
124
  if (normalized === ".coverage" || normalized.startsWith(".coverage.")) {
118
125
  return true;
119
126
  }
127
+ // vite/vitest runtime caches live under node_modules/.vite (e.g.
128
+ // node_modules/.vite/vitest/results.json). node_modules is the gitignored
129
+ // dependency tree — tool cache writes there are never a writer's product
130
+ // change and must not trip the write guard.
131
+ if (normalized.startsWith("node_modules/.vite/")) {
132
+ return true;
133
+ }
120
134
  if (/(?:^|\/)__pycache__(?:\/|$)/.test(normalized))
121
135
  return true;
122
136
  if (/\.(?:pyc|pyo)$/.test(normalized))
@@ -0,0 +1,68 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstat, readdir, readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { pathMatchesPattern } from "../shared/git-progress.js";
5
+ const normalize = (value) => value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
6
+ function rootBeforeWildcard(pattern) {
7
+ const value = normalize(pattern);
8
+ const wildcard = value.search(/[?*]/);
9
+ if (wildcard < 0)
10
+ return path.posix.dirname(value);
11
+ const prefix = value.slice(0, wildcard);
12
+ return prefix.slice(0, prefix.lastIndexOf("/")) || ".";
13
+ }
14
+ async function walk(rootCwd, relative, files) {
15
+ let entries;
16
+ try {
17
+ entries = await readdir(path.resolve(rootCwd, relative), { withFileTypes: true });
18
+ }
19
+ catch {
20
+ return;
21
+ }
22
+ for (const entry of entries) {
23
+ if ([".git", ".harness", "node_modules"].includes(entry.name))
24
+ continue;
25
+ const child = normalize(path.posix.join(relative, entry.name));
26
+ if (entry.isDirectory())
27
+ await walk(rootCwd, child, files);
28
+ else if (entry.isFile())
29
+ files.add(child);
30
+ }
31
+ }
32
+ export async function captureWorkspaceWriteSnapshot(input) {
33
+ const candidates = new Set();
34
+ for (const pattern of input.paths) {
35
+ await walk(input.rootCwd, rootBeforeWildcard(pattern), candidates);
36
+ if (!/[?*]/.test(pattern)) {
37
+ try {
38
+ if ((await lstat(path.resolve(input.rootCwd, pattern))).isFile())
39
+ candidates.add(normalize(pattern));
40
+ }
41
+ catch { /* absent target */ }
42
+ }
43
+ }
44
+ const files = {};
45
+ let totalBytes = 0;
46
+ for (const relative of [...candidates].filter((file) => input.paths.some((pattern) => pathMatchesPattern(file, pattern))).sort()) {
47
+ if (Object.keys(files).length >= 20_000)
48
+ throw new Error("workspace snapshot file limit exceeded");
49
+ const absolute = path.resolve(input.rootCwd, relative);
50
+ const stat = await lstat(absolute);
51
+ if (!stat.isFile())
52
+ throw new Error(`workspace snapshot encountered non-regular file: ${relative}`);
53
+ totalBytes += stat.size;
54
+ if (totalBytes > 64 * 1024 * 1024)
55
+ throw new Error("workspace snapshot byte limit exceeded");
56
+ files[relative] = { size: stat.size, sha256: createHash("sha256").update(await readFile(absolute)).digest("hex") };
57
+ }
58
+ const body = JSON.stringify(files);
59
+ return { schemaVersion: 1, controlMode: "filesystem-only", files, sha256: createHash("sha256").update(body).digest("hex") };
60
+ }
61
+ export function diffWorkspaceWriteSnapshots(before, after) {
62
+ const paths = new Set([...Object.keys(before.files), ...Object.keys(after.files)]);
63
+ return [...paths].sort().filter((file) => {
64
+ const left = before.files[file];
65
+ const right = after.files[file];
66
+ return !left || !right || left.size !== right.size || left.sha256 !== right.sha256;
67
+ });
68
+ }
@@ -2,6 +2,29 @@ import { copyFile, mkdir, readFile, rename, unlink, writeFile } from "node:fs/pr
2
2
  import path from "node:path";
3
3
  import { assertHarnessWriteAllowed, } from "./completed-facts-guard.js";
4
4
  let atomicWriteSequence = 0;
5
+ function isTransientJsonReadError(error) {
6
+ if (error instanceof SyntaxError)
7
+ return true;
8
+ if (!error || typeof error !== "object" || !("code" in error))
9
+ return false;
10
+ const code = String(error.code ?? "");
11
+ return code === "EBUSY" || code === "EPERM" || code === "EACCES";
12
+ }
13
+ export async function readJsonWithTransientRetry(targetPath, options = {}) {
14
+ const attempts = Math.max(1, Math.floor(options.attempts ?? 6));
15
+ const delayMs = Math.max(0, Math.floor(options.delayMs ?? 20));
16
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
17
+ try {
18
+ return JSON.parse(await readFile(targetPath, "utf-8"));
19
+ }
20
+ catch (error) {
21
+ if (attempt === attempts || !isTransientJsonReadError(error))
22
+ throw error;
23
+ await new Promise((resolve) => setTimeout(resolve, delayMs * attempt));
24
+ }
25
+ }
26
+ throw new Error("unreachable transient JSON read state");
27
+ }
5
28
  async function writeAtomic(targetPath, content, options) {
6
29
  assertHarnessWriteAllowed(targetPath, options);
7
30
  const resolved = options?.repoRoot && !path.isAbsolute(targetPath)
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Pure DAG failure-category classification shared by the workflows kernel and
3
+ * the Operator Console projection (console sources may not import
4
+ * src/workflows — security-and-tools boundary test).
5
+ *
6
+ * No runtime imports: this module is a leaf safe for both the CLI kernel and
7
+ * the browser bundle.
8
+ */
9
+ export const DAG_NORMALIZED_FAILURE_CATEGORY_VALUES = [
10
+ "success",
11
+ "validation",
12
+ "executor",
13
+ "write-guard",
14
+ "timeout",
15
+ "auth",
16
+ "human-required",
17
+ "human-rejected",
18
+ "shell-command",
19
+ "static-error",
20
+ "decision-envelope",
21
+ "skipped",
22
+ "unknown",
23
+ ];
24
+ const RAW_TO_NORMALIZED = {
25
+ success: "success",
26
+ "write-guard": "write-guard",
27
+ timeout: "timeout",
28
+ // Clean implementer stall with zero writes — still a timeout class for report,
29
+ // but retryable under WRITER_TRANSPORT_RETRY_POLICY.
30
+ "writer-clean-timeout": "timeout",
31
+ "missing-api-key": "auth",
32
+ auth: "auth",
33
+ "human-rejected": "human-rejected",
34
+ "human-required": "human-required",
35
+ "decision-envelope-invalid": "decision-envelope",
36
+ "nonzero-exit": "shell-command",
37
+ "spawn-error": "shell-command",
38
+ "test-timeout": "timeout",
39
+ "static-error": "static-error",
40
+ "invalid-output": "validation",
41
+ "structured-output-truncated": "validation",
42
+ "output-too-large": "validation",
43
+ "structured-repair-exhausted": "validation",
44
+ "governance-blocked": "validation",
45
+ "path-violation": "validation",
46
+ "empty-output": "validation",
47
+ "verify-failure": "validation",
48
+ "test-failure": "validation",
49
+ "verification-plan-stale": "validation",
50
+ "read-burst": "validation",
51
+ // Writer persisted real file changes but the final provider call hit a
52
+ // context overflow; verification never ran. Distinct from empty-output so
53
+ // the report shows partial success instead of a spec issue.
54
+ "partial-success-with-context-overflow": "validation",
55
+ // Writer burned an excessive token budget (unconverged read-edit-test loop)
56
+ // and still failed; distinct from empty-output for accurate reporting.
57
+ "writer-budget-exhausted": "validation",
58
+ network: "executor",
59
+ quota: "executor",
60
+ "rate-limit": "executor",
61
+ unavailable: "executor",
62
+ // Context window overflow (400 request-too-large): the session needs
63
+ // compaction / fresh context before retrying — an executor-side
64
+ // environment condition, not a plan-quality defect.
65
+ "context-overflow": "executor",
66
+ // Deterministic capability/policy refusal: missing verified launcher JS
67
+ // entry, capability allowlist rejection, or missing bounded write tool
68
+ // profile. These are environment/config defects the operator repairs
69
+ // before rerunning; classify as executor so recovery recommends
70
+ // rerun-after-fix WITHOUT forcing a human-decision gate on
71
+ // standaloneTaskRerun (run-failed same-task rerun).
72
+ "tool-policy": "executor",
73
+ unknown: "unknown",
74
+ };
75
+ /** Status inputs are deliberately `string`: projection layers pass bounded
76
+ * text from CLI JSON; unmatched values simply fall through to unknown. */
77
+ function isFinishedStatus(status) {
78
+ return status === "FINISHED" || status === "finished";
79
+ }
80
+ function isInProgressStatus(status) {
81
+ return (status === "PENDING" ||
82
+ status === "RUNNING" ||
83
+ status === "pending" ||
84
+ status === "running");
85
+ }
86
+ function isFailureStatus(status) {
87
+ return (status === "ERROR" ||
88
+ status === "failed" ||
89
+ status === "partial_failed");
90
+ }
91
+ function mapRawCategory(raw) {
92
+ const direct = RAW_TO_NORMALIZED[raw];
93
+ if (direct)
94
+ return direct;
95
+ const lower = raw.toLowerCase();
96
+ if (lower.includes("timeout"))
97
+ return "timeout";
98
+ if (lower.includes("validation") ||
99
+ lower.includes("invalid") ||
100
+ lower.includes("verification-plan-stale") ||
101
+ lower.includes("plan-stale"))
102
+ return "validation";
103
+ if (lower.includes("auth") || lower.includes("api-key"))
104
+ return "auth";
105
+ if (lower.includes("write-guard") || lower.includes("write_guard"))
106
+ return "write-guard";
107
+ if (lower.includes("decision-envelope") || lower.includes("decision_envelope"))
108
+ return "decision-envelope";
109
+ if (lower.includes("human-rejected"))
110
+ return "human-rejected";
111
+ if (lower.includes("human-required"))
112
+ return "human-required";
113
+ if (lower.includes("static-error"))
114
+ return "static-error";
115
+ if (lower.includes("nonzero-exit") ||
116
+ lower.includes("spawn-error") ||
117
+ lower.includes("shell"))
118
+ return "shell-command";
119
+ return undefined;
120
+ }
121
+ export function isDagFailureCategory(value) {
122
+ return DAG_NORMALIZED_FAILURE_CATEGORY_VALUES.includes(value);
123
+ }
124
+ export function normalizeDagFailureCategory(raw, status) {
125
+ if (status === "SKIPPED") {
126
+ return "skipped";
127
+ }
128
+ if (raw !== undefined) {
129
+ const mapped = mapRawCategory(raw);
130
+ if (mapped) {
131
+ if (mapped === "success") {
132
+ return isFinishedStatus(status) ? "success" : "unknown";
133
+ }
134
+ return mapped;
135
+ }
136
+ }
137
+ if (raw === undefined && isFinishedStatus(status)) {
138
+ return "success";
139
+ }
140
+ if (isInProgressStatus(status)) {
141
+ return "unknown";
142
+ }
143
+ if (isFailureStatus(status)) {
144
+ return "unknown";
145
+ }
146
+ if (raw === "success" && isFinishedStatus(status)) {
147
+ return "success";
148
+ }
149
+ return "unknown";
150
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Internal switch for the experimental "rerun failed node with an edited
3
+ * prompt" capability. The feature is enabled by default; the environment
4
+ * variable remains as an emergency kill switch while the feature is internal.
5
+ */
6
+ export const DAG_RERUN_PROMPT_OVERRIDE_ENV = "HARNESS_DAG_RERUN_PROMPT_OVERRIDE";
7
+ /** Upper bound for an override input body (characters). */
8
+ export const MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS = 65_536;
9
+ export function isDagRerunPromptOverrideEnabled(env = process.env) {
10
+ const value = env[DAG_RERUN_PROMPT_OVERRIDE_ENV]?.trim().toLowerCase();
11
+ return value !== "0" && value !== "false" && value !== "off";
12
+ }
13
+ export const DAG_RERUN_PROMPT_ADDENDUM_TAG = "operator_addendum";
14
+ /**
15
+ * Compose the final subtask_prompt for a rerun override.
16
+ * - "replace": the operator text becomes the whole prompt (advanced mode).
17
+ * - "append" (recommended): the frozen base prompt is kept verbatim and the
18
+ * operator addendum is appended in a clearly marked block, mirroring the
19
+ * rerunFeedback / retry_instruction augmentation semantics — small diffs,
20
+ * no risk of dropping template scaffolding.
21
+ */
22
+ export function composeDagPromptOverridePrompt(input) {
23
+ if (input.mode === "replace") {
24
+ return input.text;
25
+ }
26
+ return `${input.basePrompt}\n\n<${DAG_RERUN_PROMPT_ADDENDUM_TAG}>\n${input.text.trim()}\n</${DAG_RERUN_PROMPT_ADDENDUM_TAG}>\n`;
27
+ }
@@ -12,6 +12,19 @@ export const OPENSPEC_SPEC_DIRS = [
12
12
  "openspec/project-specs",
13
13
  "ai_workspace",
14
14
  ];
15
+ /** Default roots used when a frontend task does not declare specRoots. */
16
+ export const DEFAULT_FRONTEND_SPEC_ROOTS = OPENSPEC_SPEC_DIRS;
17
+ /** A task-owned spec root must be a normalized, repo-relative POSIX directory. */
18
+ export function isValidFrontendSpecRoot(value) {
19
+ return (Boolean(value) &&
20
+ !value.includes("\\") &&
21
+ !value.startsWith("/") &&
22
+ !value.includes("//") &&
23
+ !value.split("/").some((segment) => !segment ||
24
+ segment === "." ||
25
+ segment === ".." ||
26
+ /\s/.test(segment)));
27
+ }
15
28
  /** Allowed spec file extensions (case-insensitive). */
16
29
  export const OPENSPEC_SPEC_EXT_RE = /\.(md|mdx|json|yaml|yml)$/i;
17
30
  /**
@@ -43,14 +56,17 @@ export function isGovernanceSubtreePath(filePath, governanceRoot) {
43
56
  * directories. `filePath` must be a repo-relative POSIX path (backslashes are
44
57
  * normalised internally). Directory names are case-sensitive.
45
58
  */
46
- export function isOpenspecSpecPath(filePath) {
59
+ export function isFrontendSpecPath(filePath, specRoots = DEFAULT_FRONTEND_SPEC_ROOTS) {
47
60
  const normalized = filePath.replaceAll("\\", "/");
48
- for (const dir of OPENSPEC_SPEC_DIRS) {
61
+ for (const dir of specRoots) {
49
62
  if (normalized === dir || normalized.startsWith(dir + "/"))
50
63
  return true;
51
64
  }
52
65
  return false;
53
66
  }
67
+ export function isOpenspecSpecPath(filePath) {
68
+ return isFrontendSpecPath(filePath);
69
+ }
54
70
  /**
55
71
  * Returns `true` when `filename` (the basename only, not a full path) has an
56
72
  * allowed frontend specification extension.
@@ -59,10 +75,13 @@ export function isValidOpenspecExtension(filename) {
59
75
  return OPENSPEC_SPEC_EXT_RE.test(filename);
60
76
  }
61
77
  /** Returns `true` for a supported file inside a canonical openspec spec dir. */
62
- export function isOpenspecSpecFilePath(filePath) {
63
- return (isOpenspecSpecPath(filePath) &&
78
+ export function isFrontendSpecFilePath(filePath, specRoots = DEFAULT_FRONTEND_SPEC_ROOTS) {
79
+ return (isFrontendSpecPath(filePath, specRoots) &&
64
80
  isValidOpenspecExtension(filePath.replaceAll("\\", "/")));
65
81
  }
82
+ export function isOpenspecSpecFilePath(filePath) {
83
+ return isFrontendSpecFilePath(filePath);
84
+ }
66
85
  /**
67
86
  * Returns `true` when free-form search input references a canonical frontend
68
87
  * specification directory with exact lowercase directory names.
@@ -123,3 +142,50 @@ export function extractTaskSourceOpenspecPaths(markdown, governanceRoot = DEFAUL
123
142
  !isGovernanceSubtreePath(token, governanceRoot))
124
143
  .sort();
125
144
  }
145
+ /**
146
+ * Extract explicit specification-file citations for a task's frozen spec roots.
147
+ * This is intentionally path-only: source preparation decides which roots are
148
+ * normative, while DAG execution only consumes that frozen configuration.
149
+ */
150
+ export function extractTaskSourceFrontendSpecPaths(markdown, specRoots, governanceRoot = DEFAULT_OPENSPEC_GOVERNANCE_ROOT) {
151
+ if (!markdown)
152
+ return [];
153
+ const raw = new Set();
154
+ const collect = (token) => {
155
+ const normalized = normalizeCitationToken(token);
156
+ if (normalized)
157
+ raw.add(normalized);
158
+ };
159
+ for (const match of markdown.matchAll(/`([^`\n]+)`/g))
160
+ collect(match[1] ?? "");
161
+ for (const match of markdown.matchAll(/\[[^\]]*\]\(([^)\s]+)\)/g))
162
+ collect(match[1] ?? "");
163
+ for (const root of specRoots) {
164
+ const escaped = root.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
165
+ for (const match of markdown.matchAll(new RegExp(`(?:^|[\\s\"'([>])(${escaped}/[A-Za-z0-9._/-]+)`, "g"))) {
166
+ collect(match[1] ?? "");
167
+ }
168
+ }
169
+ return [...raw]
170
+ .filter((token) => isFrontendSpecFilePath(token, specRoots) && !isGovernanceSubtreePath(token, governanceRoot))
171
+ .sort();
172
+ }
173
+ /**
174
+ * Source-preparation helper for explicitly named normative directories. A root
175
+ * is inferred only on a line that declares a spec/规范/design-system intent;
176
+ * arbitrary Markdown links never silently broaden task configuration.
177
+ */
178
+ export function extractDeclaredFrontendSpecRoots(markdown) {
179
+ const roots = new Set();
180
+ for (const line of markdown.split(/\r?\n/)) {
181
+ if (!/(?:规范|规约|标准|设计系统|\bspec(?:ification)?\b|\bdesign[ -]?system\b|\bguidelines?\b|\brules?\b)/i.test(line))
182
+ continue;
183
+ for (const match of line.matchAll(/`([^`]+)`|\[[^\]]+\]\(([^)\s]+)\)/g)) {
184
+ const raw = (match[1] ?? match[2] ?? "").replaceAll("\\", "/").replace(/\/+$/, "");
185
+ if (!raw || !isValidFrontendSpecRoot(raw))
186
+ continue;
187
+ roots.add(isValidOpenspecExtension(raw) ? raw.split("/").slice(0, -1).join("/") : raw);
188
+ }
189
+ }
190
+ return [...roots].filter(isValidFrontendSpecRoot).sort();
191
+ }
@@ -1155,6 +1155,19 @@ export function buildOperatorCapabilitiesDocument() {
1155
1155
  modelCallable: "always",
1156
1156
  humanConfirmation: "none",
1157
1157
  },
1158
+ {
1159
+ action: "dagPromptOverrideConfig",
1160
+ cli: "console dag-prompt-override-config",
1161
+ kind: "read",
1162
+ inputSchemaVersion: 1,
1163
+ resultSchemaVersion: 1,
1164
+ envelopeSchemaVersion: 1,
1165
+ requiredErrorCodes: [],
1166
+ description: "Read-only probe of the experimental prompt-override rerun switch (env-gated, default off). The browser Recovery UI hides the failed-node prompt editor unless this returns enabled=true. Never mutates.",
1167
+ inputParams: [],
1168
+ modelCallable: "always",
1169
+ humanConfirmation: "none",
1170
+ },
1158
1171
  {
1159
1172
  action: "dagRerunPlan",
1160
1173
  cli: "loop-agent dag rerun --run-id <id> --from-node <node> --plan --json",
@@ -1177,6 +1190,12 @@ export function buildOperatorCapabilitiesDocument() {
1177
1190
  required: true,
1178
1191
  description: "node id to rerun from (prefer failed/attention node from dagReport primaryFailure.nodeId)",
1179
1192
  },
1193
+ {
1194
+ name: "promptOverrides",
1195
+ type: "array",
1196
+ required: false,
1197
+ description: "experimental (switch off by default): [{nodeId, prompt, mode?}] — mode append (recommended) keeps the frozen base prompt and appends the text as a marked addendum block, replace swaps the whole subtask_prompt; only FAILED nodes inside the reset closure are admitted and the planHash binds the composed change",
1198
+ },
1180
1199
  ],
1181
1200
  modelCallable: "always",
1182
1201
  humanConfirmation: "none",
@@ -1219,6 +1238,12 @@ export function buildOperatorCapabilitiesDocument() {
1219
1238
  required: true,
1220
1239
  description: "why continue from this node (e.g. provider flake; resume from review-pi)",
1221
1240
  },
1241
+ {
1242
+ name: "promptOverrides",
1243
+ type: "array",
1244
+ required: false,
1245
+ description: "experimental (switch off by default): must match the dagRerunPlan promptOverrides exactly (same mode/text); the planHash already binds them, any drift is rejected",
1246
+ },
1222
1247
  ],
1223
1248
  // always: fresh eligible dagRerun plans are model-callable (2026-08-11).
1224
1249
  // The CLI keeps enforcing parent lifecycle, fromNode, planHash,
@@ -1822,6 +1847,13 @@ export const OPERATOR_COMMAND_COVERAGE = Object.freeze([
1822
1847
  source: "loop-agent",
1823
1848
  exclusionReason: "Not exposed to Operator Chat in M5.",
1824
1849
  },
1850
+ {
1851
+ command: "loop-agent init model-catalog",
1852
+ coverage: "excluded",
1853
+ action: null,
1854
+ source: "loop-agent",
1855
+ exclusionReason: "Not exposed to Operator Chat in M5.",
1856
+ },
1825
1857
  {
1826
1858
  command: "loop-agent init doctor",
1827
1859
  coverage: "excluded",
@@ -2200,6 +2232,13 @@ export const OPERATOR_COMMAND_COVERAGE = Object.freeze([
2200
2232
  source: "loop-agent",
2201
2233
  note: "server-side read-only run-facts check (ineligible plan / explicit rerun-task recommendation) replaces the browser Human Gate (2026-08-11).",
2202
2234
  },
2235
+ {
2236
+ command: "loop-agent dag follow-up",
2237
+ coverage: "excluded",
2238
+ action: null,
2239
+ source: "loop-agent",
2240
+ exclusionReason: "Frontend review recovery helper is a direct advanced CLI composition (create/import task and optional advance), not an Operator action endpoint.",
2241
+ },
2203
2242
  {
2204
2243
  command: "loop-agent dag reconcile-tasks",
2205
2244
  coverage: "human-gated-required",
@@ -9,6 +9,7 @@ export const PLAYWRIGHT_CLI_ALLOWED_COMMANDS = [
9
9
  "goto",
10
10
  "snapshot",
11
11
  "find",
12
+ "find-unique",
12
13
  "click",
13
14
  "dblclick",
14
15
  "fill",
@@ -35,6 +36,20 @@ export const PLAYWRIGHT_CLI_ALLOWED_COMMANDS = [
35
36
  "requests",
36
37
  "request",
37
38
  ];
39
+ /**
40
+ * Parse the match count from a playwright-cli find output. The CLI prints
41
+ * `Found N match(es) for "...":` on success and `No matches found for "...".`
42
+ * on zero — both with exit code 0, so the count is the only assertion signal.
43
+ * Returns undefined when the output shape is unrecognized (e.g. error pages).
44
+ */
45
+ export function parsePlaywrightCliFindMatchCount(output) {
46
+ const found = /Found\s+(\d+)\s+match(?:es)?\s+for\b/i.exec(output);
47
+ if (found)
48
+ return Number(found[1]);
49
+ if (/No matches found for\b/i.test(output))
50
+ return 0;
51
+ return undefined;
52
+ }
38
53
  const allowedCommandSet = new Set(PLAYWRIGHT_CLI_ALLOWED_COMMANDS);
39
54
  export function isPlaywrightCliCommand(value) {
40
55
  return allowedCommandSet.has(value);
@@ -0,0 +1,100 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import semver from "semver";
3
+ import { acquireUpdateLock, readUpdateState, updateLockPath, updateStatePath, writeUpdateState, } from "./state.js";
4
+ export async function runConsoleDailyUpdateCheck(options) {
5
+ void options.writeStdout;
6
+ void options.packageRoot;
7
+ void options.cwd;
8
+ const writeStderr = options.writeStderr ?? ((chunk) => process.stderr.write(chunk));
9
+ const now = options.now ?? (() => new Date());
10
+ try {
11
+ if (!canPromptInteractively(options))
12
+ return;
13
+ const statePath = updateStatePath(options.homeDir);
14
+ const today = formatLocalDate(now());
15
+ const existing = await readUpdateState(statePath);
16
+ if (existing.lastPromptedLocalDate === today)
17
+ return;
18
+ const release = await acquireUpdateLock(updateLockPath(options.homeDir), {
19
+ now,
20
+ });
21
+ if (!release)
22
+ return;
23
+ try {
24
+ const state = await readUpdateState(statePath);
25
+ if (state.lastPromptedLocalDate === today)
26
+ return;
27
+ const check = await options.client.latestVersionCheck();
28
+ if (!check.ok)
29
+ return;
30
+ if (!semver.gt(check.version, options.currentVersion))
31
+ return;
32
+ const parsedLatest = semver.parse(check.version);
33
+ if (!parsedLatest || parsedLatest.prerelease.length > 0)
34
+ return;
35
+ await markPromptedToday(statePath, state, today);
36
+ const answer = await ask(options, `loop-agent 有新版本:${options.currentVersion} → ${check.version}\n现在更新到 ${check.version}?[y/N] `);
37
+ if (!isYes(answer))
38
+ return;
39
+ const proof = await options.client.canAutoInstall();
40
+ if (!proof.ok) {
41
+ writeStderr(`当前 loop-agent 不是由此 npm 的正式全局安装管理,无法自动升级。\n请运行:npm install -g @tea-agent/loop-agent@${check.version}\n`);
42
+ return;
43
+ }
44
+ const install = await options.client.installExact(check.version);
45
+ if (!install.ok) {
46
+ writeStderr(`loop-agent 更新失败:${install.reason}\n`);
47
+ return;
48
+ }
49
+ const verified = await options.client.verifyInstalled(check.version);
50
+ if (!verified.ok) {
51
+ writeStderr(`loop-agent 已尝试安装,但验证失败:${verified.reason}\n`);
52
+ return;
53
+ }
54
+ writeStderr(`loop-agent 已更新到 ${check.version};新版本将在下一次命令调用时完全生效。\n`);
55
+ }
56
+ finally {
57
+ await release().catch(() => undefined);
58
+ }
59
+ }
60
+ catch (error) {
61
+ writeStderr(`loop-agent 更新检查已跳过:${error instanceof Error ? error.message : String(error)}\n`);
62
+ }
63
+ }
64
+ function canPromptInteractively(options) {
65
+ if (!options.stdinIsTTY || !options.stderrIsTTY)
66
+ return false;
67
+ if (options.env.CI)
68
+ return false;
69
+ if (options.env.LOOP_AGENT_DISABLE_UPDATE_CHECK === "1")
70
+ return false;
71
+ return true;
72
+ }
73
+ function formatLocalDate(date) {
74
+ const year = date.getFullYear();
75
+ const month = String(date.getMonth() + 1).padStart(2, "0");
76
+ const day = String(date.getDate()).padStart(2, "0");
77
+ return `${year}-${month}-${day}`;
78
+ }
79
+ async function markPromptedToday(statePath, state, today) {
80
+ await writeUpdateState(statePath, {
81
+ ...state,
82
+ lastPromptedLocalDate: today,
83
+ });
84
+ }
85
+ async function ask(options, prompt) {
86
+ if (options.readLine) {
87
+ options.writeStderr?.(prompt);
88
+ return options.readLine(prompt);
89
+ }
90
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
91
+ try {
92
+ return await rl.question(prompt);
93
+ }
94
+ finally {
95
+ rl.close();
96
+ }
97
+ }
98
+ function isYes(answer) {
99
+ return /^(y|yes)$/i.test(answer.trim());
100
+ }