@tea-agent/loop-agent 0.43.0-next.9 → 0.43.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 (287) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +31 -2309
  3. package/README.md +7 -0
  4. package/dist/application/dag/args.js +8 -0
  5. package/dist/application/task-lifecycle/advance.js +38 -7
  6. package/dist/build-stamp.json +3 -3
  7. package/dist/cli/command-definitions.js +2 -0
  8. package/dist/cli/program.js +3 -1
  9. package/dist/cli/update/init-surface-notifier.js +2 -0
  10. package/dist/cli/update/policy.js +2 -0
  11. package/dist/commands/client-recovery.js +20 -55
  12. package/dist/commands/cursor-prompt.js +2 -0
  13. package/dist/commands/dag-approve.js +2 -0
  14. package/dist/commands/dag-final-verification.js +2 -0
  15. package/dist/commands/dag-init-hybrid.js +2 -0
  16. package/dist/commands/dag-reconcile-tasks.js +2 -0
  17. package/dist/commands/dag-reject.js +2 -0
  18. package/dist/commands/dag-report.js +2 -0
  19. package/dist/commands/dag-rerun.js +2 -0
  20. package/dist/commands/dag-resume.js +2 -0
  21. package/dist/commands/dag-workflow-compile.js +2 -0
  22. package/dist/commands/dag-workflow-plan.js +2 -0
  23. package/dist/commands/dag-workflow-validate.js +2 -0
  24. package/dist/commands/delegate.js +2 -0
  25. package/dist/commands/examples.js +2 -0
  26. package/dist/commands/import-prd.js +2 -0
  27. package/dist/commands/init-model-catalog.js +2 -0
  28. package/dist/commands/init.js +19 -13
  29. package/dist/commands/instructions.js +2 -0
  30. package/dist/commands/knowledge.js +2 -0
  31. package/dist/commands/loop-benchmark.js +2 -0
  32. package/dist/commands/pi-prompt.js +2 -0
  33. package/dist/commands/pi-reuse-benchmark.js +2 -0
  34. package/dist/commands/promote-run.js +2 -0
  35. package/dist/commands/stats.js +1 -1
  36. package/dist/commands/study-init.js +2 -0
  37. package/dist/commands/task-advance.js +34 -0
  38. package/dist/commands/task-source-prepare.js +2 -0
  39. package/dist/commands/worktree-create.js +2 -0
  40. package/dist/commands/worktree-remove.js +2 -0
  41. package/dist/executors/dag-pi-executor.js +1650 -198
  42. package/dist/executors/pi-executor.js +85 -3
  43. package/dist/executors/pi-sdk-executor.js +18 -0
  44. package/dist/executors/shell-executor.js +93 -7
  45. package/dist/executors/shell-verification.js +3 -0
  46. package/dist/executors/shell-write-guard.js +27 -32
  47. package/dist/governance/exec-plans.js +6 -3
  48. package/dist/infrastructure/console/app-data.js +6 -0
  49. package/dist/shared/artifacts-core.js +2 -0
  50. package/dist/shared/backend-dogfood-preflight.js +47 -0
  51. package/dist/shared/dag-failure-category.js +3 -0
  52. package/dist/shared/git-progress.js +2 -0
  53. package/dist/shared/one-shot-prompt-args.js +2 -0
  54. package/dist/shared/operator/capabilities.js +180 -0
  55. package/dist/shared/package-metadata.js +6 -4
  56. package/dist/shared/pi-context-pressure/extension.js +26 -16
  57. package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
  58. package/dist/shared/pi-provider-recovery.js +182 -0
  59. package/dist/shared/pi-retry-settings.js +40 -0
  60. package/dist/shared/reference-context.js +2 -0
  61. package/dist/task/config-types.js +29 -0
  62. package/dist/task/contract/project.js +9 -0
  63. package/dist/task/contract/recover.js +6 -0
  64. package/dist/task/contract/schema.js +17 -0
  65. package/dist/task/source-prepare/artifact-meta.js +9 -2
  66. package/dist/task/source-prepare/build-draft.js +60 -0
  67. package/dist/task/source-prepare/fragment-inventory.js +4 -1
  68. package/dist/task/source-prepare/parse-intent.js +36 -11
  69. package/dist/task/task-demand-routing.js +5 -2
  70. package/dist/worker/console/chat/browser-automation.js +523 -0
  71. package/dist/worker/console/chat/browser-policy.js +6 -36
  72. package/dist/worker/console/chat/browser-routes.js +188 -56
  73. package/dist/worker/console/chat/explore-tools.js +5 -2
  74. package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
  75. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
  76. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +184 -0
  77. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
  78. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
  79. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +94 -0
  80. package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
  81. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
  82. package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
  83. package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
  84. package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
  85. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
  86. package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
  87. package/dist/worker/console/chat/pi-runtime.js +208 -1138
  88. package/dist/worker/console/chat/routes.js +27 -7
  89. package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
  90. package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
  91. package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
  92. package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
  93. package/dist/worker/console/chat/session-mode-view.js +5 -4
  94. package/dist/worker/console/chat/session-mode.js +18 -12
  95. package/dist/worker/console/chat/session-store.js +42 -9
  96. package/dist/worker/console/chat/tool-preview.js +115 -0
  97. package/dist/worker/console/chat/tools.js +10 -0
  98. package/dist/worker/console/chat/turn-order.js +13 -0
  99. package/dist/worker/console/chat/turn-process.js +32 -30
  100. package/dist/worker/console/console-update-and-init.js +1 -19
  101. package/dist/worker/console/console-update-runtime.js +5 -20
  102. package/dist/worker/console/operator-actions.js +63 -1
  103. package/dist/worker/console/prd-intake-bridge.js +54 -1
  104. package/dist/worker/console/routes.js +8 -0
  105. package/dist/worker/console/scheduled-goal-host.js +98 -0
  106. package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
  107. package/dist/worker/console/server.js +24 -1
  108. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D_fNbWc_.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
  109. package/dist/worker/console/static/assets/{arc-_HuqLW6m.js → arc-eoQiMvuk.js} +1 -1
  110. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-Djy2qY1m.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
  111. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-bB-1TSyY.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
  112. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-C5Hmghrz.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
  113. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
  114. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Duyl39oy.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
  115. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-x6mdnSyf.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
  116. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-WlBuFj7W.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
  117. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CTF429HX.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
  118. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Dwd6r-SF.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
  119. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C-Br82D1.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
  120. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CuGp0DeE.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
  121. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CJhFX6Lh.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
  122. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DpGOa_jT.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
  123. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DXJaTynd.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
  124. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
  125. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
  126. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CRBZWjE4.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
  127. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BdXcxtlb.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
  128. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-07UKsOts.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
  129. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-D38p9__S.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
  130. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-nSiBNNjH.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
  131. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BFOTot5D.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
  132. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DypKaZ6k.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
  133. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-B6yoOQeW.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
  134. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BHYS6viX.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
  135. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-D2VD5Snj.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
  136. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-E98mWxN1.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
  137. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CT1zxhqP.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
  138. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-6GcngNcy.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
  139. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DCL6NvKe.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
  140. package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
  141. package/dist/worker/console/static/assets/index-Cdkvw_H6.js +469 -0
  142. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C0627e-9.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
  143. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C_u3P6Ik.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
  144. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DPDPv9HL.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
  145. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-D3OHdWiO.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
  146. package/dist/worker/console/static/assets/{linear-DI88zD7n.js → linear-BDZ9riWi.js} +1 -1
  147. package/dist/worker/console/static/assets/{mermaid.core-p2l8IrWY.js → mermaid.core-7pKqYtpZ.js} +5 -5
  148. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-B1bAX-oS.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
  149. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DwOnWxdD.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
  150. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-xRkTXnJC.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
  151. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-_9UC5Zio.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
  152. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CgEUirWJ.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
  153. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-Czbpwiyz.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
  154. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-_j7_KiqY.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
  155. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BNJa6Hyi.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
  156. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DedpKtOc.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
  157. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BBvthr7c.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
  158. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
  159. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Ds7umgEM.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
  160. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
  161. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKa-p1nF.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
  162. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-LoUNR56o.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
  163. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bqbl6ym7.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
  164. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-B8dUg4lf.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
  165. package/dist/worker/console/static/index.html +2 -2
  166. package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +24 -0
  167. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
  168. package/dist/worker/console/static-src/operator-chat/compaction-message.js +3 -17
  169. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +192 -0
  170. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +9 -1
  171. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
  172. package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
  173. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +92 -38
  174. package/dist/worker/console/static-src/operator-chat/useChatStream.js +145 -70
  175. package/dist/worker/console/static-src/operator-chat/useChatThread.js +38 -55
  176. package/dist/worker/console/static-src/operator-chat/useComposer.js +182 -69
  177. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
  178. package/dist/worker/console/static-src/prd-file-import.js +3 -1
  179. package/dist/worker/console/workspace-context.js +22 -0
  180. package/dist/worker/console/workspace-initialization.js +209 -0
  181. package/dist/worker/delivery/git-transaction.js +2 -0
  182. package/dist/worker/feature/fullstack-validate.js +2 -0
  183. package/dist/worker/loop-agent/loop-agent-client.js +8 -2
  184. package/dist/worker/materialize/harness-task-materializer.js +4 -1
  185. package/dist/worker/metrics/projector.js +1 -1
  186. package/dist/worker/observability/event-store.js +4 -1
  187. package/dist/worker/observability/read-model.js +4 -1
  188. package/dist/worker/observe/node-transparency.js +23 -1
  189. package/dist/worker/observe/routes.js +4 -0
  190. package/dist/worker/observe/static/operator-chrome.js +3 -1
  191. package/dist/worker/observe/static/styles.css +7 -4
  192. package/dist/worker/observe/static/views/dag-inspector.js +5 -19
  193. package/dist/worker/outcomes/declared-artifacts.js +2 -0
  194. package/dist/worker/preflight.js +3 -0
  195. package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
  196. package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
  197. package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
  198. package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
  199. package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
  200. package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
  201. package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
  202. package/dist/worker/task-spec/validate.js +4 -1
  203. package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
  204. package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
  205. package/dist/workflows/dag/dag-retry-schema.js +11 -0
  206. package/dist/workflows/dag/decision-envelope.js +2 -0
  207. package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
  208. package/dist/workflows/dag/frontend-closeout.js +3 -1
  209. package/dist/workflows/dag/frontend-committed-facts.js +461 -0
  210. package/dist/workflows/dag/frontend-durable-tools.js +15 -3
  211. package/dist/workflows/dag/frontend-implementation-contract.js +369 -12
  212. package/dist/workflows/dag/frontend-plan-canary.js +53 -0
  213. package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
  214. package/dist/workflows/dag/frontend-plan-render.js +0 -2
  215. package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
  216. package/dist/workflows/dag/frontend-recovery-run.js +57 -0
  217. package/dist/workflows/dag/frontend-repair.js +55 -76
  218. package/dist/workflows/dag/frontend-review-context.js +42 -68
  219. package/dist/workflows/dag/frontend-review-scopes.js +2 -2
  220. package/dist/workflows/dag/frontend-risk.js +92 -10
  221. package/dist/workflows/dag/frontend-session-budget.js +117 -3
  222. package/dist/workflows/dag/frontend-shape.js +11 -55
  223. package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
  224. package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
  225. package/dist/workflows/dag/frontend-verification-trace.js +33 -54
  226. package/dist/workflows/dag/frontend-writer-admission.js +3 -47
  227. package/dist/workflows/dag/frontend-writer-status.js +0 -23
  228. package/dist/workflows/dag/init-hybrid.js +47 -19
  229. package/dist/workflows/dag/interrupt-request.js +2 -0
  230. package/dist/workflows/dag/lifecycle.js +11 -2
  231. package/dist/workflows/dag/node-execution.js +50 -13
  232. package/dist/workflows/dag/reconcile-run.js +2 -0
  233. package/dist/workflows/dag/repair-artifact.js +3 -1
  234. package/dist/workflows/dag/report.js +2 -0
  235. package/dist/workflows/dag/rerun-plan.js +10 -0
  236. package/dist/workflows/dag/rerun-task.js +79 -1
  237. package/dist/workflows/dag/retry-policy.js +18 -0
  238. package/dist/workflows/dag/scheduler.js +2 -4
  239. package/dist/workflows/dag/types.js +44 -6
  240. package/dist/workflows/dag/validate.js +4 -0
  241. package/docs/README.md +1 -0
  242. package/docs/architecture/runtime-boundaries.md +3 -3
  243. package/docs/governance/README.md +1 -0
  244. package/docs/init-surface.manifest.json +1 -0
  245. package/docs/operations/README.md +2 -0
  246. package/docs/templates/README.md +1 -1
  247. package/docs/templates/agent-dag.schema.json +2 -2
  248. package/docs/templates/backend-test-dag.json +14 -10
  249. package/docs/templates/frontend-implementation-contract.schema.json +0 -7
  250. package/package.json +9 -3
  251. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  252. package/skills/frontend-contract/references/contract-protocol.md +3 -1
  253. package/skills/frontend-plan/SKILL.md +8 -6
  254. package/skills/frontend-plan/references/decision-contract.md +3 -3
  255. package/skills/frontend-review/SKILL.md +18 -28
  256. package/skills/frontend-review/references/review-findings.md +8 -3
  257. package/dist/commands/new-task.js +0 -5
  258. package/dist/commands/status.js +0 -57
  259. package/dist/commands/task-contract.js +0 -269
  260. package/dist/shared/prompts.js +0 -26
  261. package/dist/task/config.js +0 -2
  262. package/dist/task/contract/validate-draft.js +0 -106
  263. package/dist/task/goal.js +0 -3
  264. package/dist/task/index.js +0 -12
  265. package/dist/task/lifecycle.js +0 -1
  266. package/dist/task/operator/capabilities.js +0 -6
  267. package/dist/task/operator/envelope.js +0 -2
  268. package/dist/task/operator/index.js +0 -5
  269. package/dist/task/operator/registry.js +0 -2
  270. package/dist/task/operator/types.js +0 -1
  271. package/dist/task/paths.js +0 -1
  272. package/dist/task/source-state.js +0 -1
  273. package/dist/task/subagent-guidance.js +0 -1
  274. package/dist/worker/console/chat/sift-bridge.js +0 -1
  275. package/dist/worker/console/index.js +0 -20
  276. package/dist/worker/console/static/assets/channel-C0A6UEIa.js +0 -1
  277. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DzguNppy.js +0 -1
  278. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DzguNppy.js +0 -1
  279. package/dist/worker/console/static/assets/index-24jp3aBh.js +0 -468
  280. package/dist/worker/console/static/assets/index-CY6y6I8M.css +0 -1
  281. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BABVQnfM.js +0 -1
  282. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D0qsLaxR.js +0 -8
  283. package/dist/worker/scheduler/index.js +0 -25
  284. package/dist/workflows/dag/facts.js +0 -4
  285. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
  286. package/dist/workflows/dag/index.js +0 -6
  287. package/dist/workflows/dynamic/index.js +0 -7
@@ -1,3 +1,6 @@
1
+ import { deliverScheduledGoalResult } from "./chat/scheduled-goal-delivery.js";
2
+ import { ScheduledGoalHost } from "./scheduled-goal-host.js";
3
+ import { ScheduledGoalStore } from "../scheduler/scheduled-goal-store.js";
1
4
  /**
2
5
  * Repo-scoped Workspace Context (2026-08-22 multi-workspace foundation, W1).
3
6
  *
@@ -39,6 +42,7 @@ import { GoalRegistry } from "./chat/goals.js";
39
42
  import { GoalRoundDriver } from "./chat/goal-round-driver.js";
40
43
  import { TerminalSessionRegistry } from "./chat/terminal-sessions.js";
41
44
  import { ScmService } from "./chat/scm-service.js";
45
+ import { BrowserAutomationService } from "./chat/browser-automation.js";
42
46
  import { driveGoalRound, recoverDeferredTurns } from "./chat/routes.js";
43
47
  import { createOperationEventStore, } from "./operation-sse.js";
44
48
  import { OperationStore } from "../../infrastructure/console/operation-store.js";
@@ -52,6 +56,7 @@ import { probePiReadiness, } from "./pi-readiness.js";
52
56
  import { resolveSiblingLoopAgentBin } from "./sibling-controller.js";
53
57
  import { NightAuxTicker } from "./night-aux-ticker.js";
54
58
  import { createObserveRouteContext, } from "../observe/routes.js";
59
+ import { WorkspaceInitializationService } from "./workspace-initialization.js";
55
60
  import { WorkspaceRegistry } from "./workspace-registry.js";
56
61
  import { resolveConsoleDefaultTimeoutMs } from "./server.js";
57
62
  /**
@@ -242,6 +247,7 @@ export async function createWorkspaceContext(record, deps) {
242
247
  readiness: readinessCache,
243
248
  getReadiness,
244
249
  refreshReadiness,
250
+ workspaceInitialization: new WorkspaceInitializationService({ workspaceId: record.workspaceId, name: record.name, repoRoot, client, operations, events, activeChatTurnCount: () => chatTurnExecutions.size() }),
245
251
  observeBaseUrl: deps.observeBaseUrl,
246
252
  fetchImpl: deps.fetchImpl,
247
253
  getConsoleOrigin: deps.getConsoleOrigin,
@@ -295,6 +301,11 @@ export async function createWorkspaceContext(record, deps) {
295
301
  // Workspace-scoped git source control shared by the model tool (scm) and
296
302
  // the Console UI SCM panel (HTTP API).
297
303
  const scmService = new ScmService(repoRoot);
304
+ const browserAutomation = new BrowserAutomationService({
305
+ profileDir: appData.browserProfile,
306
+ downloadsDir: appData.browserDownloads,
307
+ screenshotsDir: appData.browserScreenshots,
308
+ });
298
309
  const chatRuntime = new ConsolePiRuntime({
299
310
  cwd: repoRoot,
300
311
  sessionDir: appData.chatSessions,
@@ -306,6 +317,7 @@ export async function createWorkspaceContext(record, deps) {
306
317
  sddDataAlignment,
307
318
  terminalRegistry,
308
319
  scmService,
320
+ browserAutomation,
309
321
  });
310
322
  let chatRouteDeps;
311
323
  const goalRoundDriver = new GoalRoundDriver((sessionId) => driveGoalRound(chatRouteDeps, sessionId));
@@ -322,6 +334,7 @@ export async function createWorkspaceContext(record, deps) {
322
334
  operationLinker: chatOperationLinker,
323
335
  terminals: terminalRegistry,
324
336
  scm: scmService,
337
+ browserAutomation,
325
338
  appData,
326
339
  bootToken: deps.bootToken,
327
340
  getConsoleOrigin: deps.getConsoleOrigin,
@@ -330,6 +343,12 @@ export async function createWorkspaceContext(record, deps) {
330
343
  detachSession: (sessionId) => registry.detachSession(record.workspaceId, sessionId),
331
344
  };
332
345
  const nightAuxTicker = new NightAuxTicker({ repoRoot, client });
346
+ const scheduledGoals = new ScheduledGoalStore(repoRoot);
347
+ const scheduledGoalHost = new ScheduledGoalHost({
348
+ goals: scheduledGoals, client, operations, events,
349
+ deliver: (goal) => deliverScheduledGoalResult(goal, scheduledGoals, chatStore),
350
+ onError: (goalId, error) => process.stderr.write(`[scheduled-goal] ${goalId ?? "wake"}: ${error instanceof Error ? error.message : String(error)}\n`),
351
+ });
333
352
  const observeRouteContext = createObserveRouteContext(repoRoot, {
334
353
  staticDir: deps.inspectStaticDir,
335
354
  });
@@ -364,6 +383,7 @@ export async function createWorkspaceContext(record, deps) {
364
383
  if (disposed)
365
384
  return;
366
385
  disposed = true;
386
+ scheduledGoalHost.stop();
367
387
  try {
368
388
  goalRoundDriver.dispose();
369
389
  }
@@ -382,6 +402,7 @@ export async function createWorkspaceContext(record, deps) {
382
402
  catch {
383
403
  // best-effort
384
404
  }
405
+ void browserAutomation.dispose();
385
406
  try {
386
407
  nightAuxTicker.dispose();
387
408
  }
@@ -390,6 +411,7 @@ export async function createWorkspaceContext(record, deps) {
390
411
  }
391
412
  },
392
413
  };
414
+ scheduledGoalHost.start();
393
415
  return context;
394
416
  }
395
417
  /** Inspect-facing readiness/capability summary for a registry record. */
@@ -0,0 +1,209 @@
1
+ import { lstat, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { acquireUpdateLock, updateLockPath } from "../../shared/update/state.js";
4
+ import os from "node:os";
5
+ import { TERMINAL_OPERATION_STATES } from "../../infrastructure/console/operation-store.js";
6
+ import { readInitRuntimeActivity } from "../observability/init-runtime-activity.js";
7
+ const record = (value) => {
8
+ if (!value || typeof value !== "object" || Array.isArray(value))
9
+ throw new Error("初始化检查返回了无效数据");
10
+ return value;
11
+ };
12
+ const entries = (value) => {
13
+ if (!Array.isArray(value))
14
+ throw new Error("初始化检查缺少状态清单");
15
+ return value.map(record);
16
+ };
17
+ /** A single registered workspace owns checks, CLI cwd and durable operations. */
18
+ export class WorkspaceInitializationService {
19
+ options;
20
+ cached;
21
+ pending;
22
+ activeOperationId;
23
+ generation = 0;
24
+ constructor(options) {
25
+ this.options = options;
26
+ }
27
+ snapshot(state, message, pendingPaths = []) {
28
+ const { workspaceId, name, repoRoot } = this.options;
29
+ return { workspaceId, name, displayPath: repoRoot, state, message, pendingPaths, canUpdate: false, pendingItems: [] };
30
+ }
31
+ async jsonFile(relative) {
32
+ const file = path.join(this.options.repoRoot, relative);
33
+ try {
34
+ const stat = await lstat(file);
35
+ if (!stat.isFile() || stat.isSymbolicLink())
36
+ throw new Error(`${relative} 不是普通配置文件`);
37
+ return record(JSON.parse(await readFile(file, "utf8")));
38
+ }
39
+ catch (error) {
40
+ if (error.code === "ENOENT")
41
+ return undefined;
42
+ throw new Error(`${relative} 无法读取,请检查文件内容与权限`);
43
+ }
44
+ }
45
+ async command(args, name) {
46
+ const result = await this.options.client.run(args, {
47
+ cwd: this.options.repoRoot, artifactName: `workspace-init-${name}`,
48
+ expectJson: true, timeoutMs: 120_000,
49
+ });
50
+ if (!result.ok || result.exitCode !== 0 || result.timedOut || result.parseFailure) {
51
+ throw new Error(`工作区配置${name === "check" ? "检查" : "操作"}失败,请查看操作记录或检查模型与初始化配置`);
52
+ }
53
+ return record(result.json);
54
+ }
55
+ async catalog() {
56
+ const catalog = await this.command(["init", "model-catalog", "--json"], "models");
57
+ return entries(catalog.models).filter(item => item.eligibleForHarness === true && typeof item.reference === "string")
58
+ .map(item => ({ reference: item.reference, name: typeof item.name === "string" ? item.name : item.reference }));
59
+ }
60
+ async inspect() {
61
+ try {
62
+ try {
63
+ const harnessDir = await lstat(path.join(this.options.repoRoot, ".harness"));
64
+ if (!harnessDir.isDirectory() || harnessDir.isSymbolicLink())
65
+ throw new Error(".harness 必须是仓库内的普通目录");
66
+ }
67
+ catch (error) {
68
+ if (error.code !== "ENOENT")
69
+ throw error;
70
+ }
71
+ const surface = await this.jsonFile(".harness/init-surface.json");
72
+ const harness = await this.jsonFile("harness.json");
73
+ if (surface && (surface.schemaVersion !== 1 || !surface.files || typeof surface.files !== "object" || Array.isArray(surface.files))) {
74
+ throw new Error("初始化记录不完整,请检查 .harness/init-surface.json");
75
+ }
76
+ if (!surface && !harness) {
77
+ const snapshot = this.snapshot("uninitialized", "此仓库尚未初始化。初始化后可使用项目任务与工作流配置。");
78
+ try {
79
+ snapshot.models = await this.catalog();
80
+ }
81
+ catch {
82
+ snapshot.message = "此仓库尚未初始化;可用模型读取失败,请检查模型设置后重新检查。";
83
+ }
84
+ return snapshot;
85
+ }
86
+ const report = await this.command(["init", "check-update", "--json"], "check");
87
+ if (!["recorded", "inferred-baseline", "missing"].includes(String(report.surfaceState)) || typeof report.ok !== "boolean") {
88
+ throw new Error("初始化检查返回了未知状态");
89
+ }
90
+ const actions = entries(report.deterministicActions);
91
+ const merges = entries(report.modelMergeTasks);
92
+ const decisions = entries(report.humanDecisions);
93
+ const pendingItems = [];
94
+ for (const [kind, list] of [["safe-update", actions], ["merge", merges], ["confirmation", decisions]]) {
95
+ for (const item of list) {
96
+ if (typeof item.path !== "string")
97
+ continue;
98
+ const detail = typeof item.reason === "string" ? item.reason : undefined;
99
+ const baseline = item.type === "bootstrap-surface";
100
+ const itemKind = baseline ? "confirmation" : kind;
101
+ const reason = baseline ? "缺少可靠的初始化记录,不能直接判断哪些内容可覆盖。"
102
+ : kind === "safe-update" ? "此项可由程序安全更新,保留项目自定义内容。"
103
+ : kind === "merge" ? "现有内容与新版初始化模板不同,需要保留项目配置后合并。"
104
+ : /not a directory/.test(detail ?? "") ? "这里需要一个目录,但当前路径被文件占用。"
105
+ : /removed.*local changes/.test(detail ?? "") ? "新版不再使用此文件,但它包含本地修改,需要决定保留还是删除。"
106
+ : /target path.*already exists/.test(detail ?? "") ? "迁移目标已有不同内容,需要确认两份文件如何保留。"
107
+ : "此项无法安全自动处理,请先核对检查详情,再确定处理方式。";
108
+ pendingItems.push({ path: item.path, kind: itemKind, reason,
109
+ nextStep: itemKind === "safe-update" ? "点击更新按钮即可处理。"
110
+ : itemKind === "merge" ? "可将下方处理说明交给智能体合并;只有需求冲突时才需你确认。"
111
+ : "将处理说明交给智能体检查;需要取舍时确认方案,处理后重新检查。",
112
+ ...(detail ? { detail } : {}) });
113
+ }
114
+ }
115
+ const modelsReady = record(report.modelReadiness).status === "ready";
116
+ if (!modelsReady)
117
+ pendingItems.push({ path: "模型配置", kind: "model",
118
+ reason: "当前模型配置或认证尚未就绪,更新文件不能解决此问题。",
119
+ nextStep: "检查模型定义及提供商认证,保留仍可用的模型配置,完成后重新检查。" });
120
+ const canUpdate = pendingItems.some(item => item.kind === "safe-update");
121
+ const needsAttention = pendingItems.some(item => item.kind !== "safe-update") || !report.ok || report.surfaceState !== "recorded" && !canUpdate;
122
+ if (needsAttention || canUpdate) {
123
+ const safeCount = pendingItems.filter(item => item.kind === "safe-update").length;
124
+ const message = canUpdate
125
+ ? `有 ${safeCount} 项可在此更新。${needsAttention ? "其余待处理项的原因和下一步见下方。" : "更新会保留项目自定义内容。"}`
126
+ : "暂无可直接更新的内容。请按下方原因处理,完成后重新检查。";
127
+ if (!pendingItems.length)
128
+ pendingItems.push({ path: "初始化配置", kind: "confirmation",
129
+ reason: "检查尚未通过,但没有返回可自动更新的项目。",
130
+ nextStep: "复制处理说明交给智能体检查初始化记录,完成后重新检查。" });
131
+ return { ...this.snapshot(needsAttention ? "needs-attention" : "update-available", message, [...new Set(pendingItems.map(item => item.path))]), canUpdate, pendingItems };
132
+ }
133
+ return this.snapshot("ready", "工作区配置已就绪");
134
+ }
135
+ catch (error) {
136
+ return this.snapshot("error", error instanceof Error ? error.message : "初始化状态检查失败");
137
+ }
138
+ }
139
+ async check(refresh = false) {
140
+ const latest = (await this.options.operations.list()).filter(op => op.action === "workspaceInitialize").at(-1);
141
+ const running = latest && !TERMINAL_OPERATION_STATES.has(latest.state) && latest.state !== "needs-reconcile";
142
+ if (!running && (refresh || !this.cached || Date.now() - this.cached.at > 30_000)) {
143
+ const generation = this.generation;
144
+ this.pending ??= this.inspect().then(value => { if (generation === this.generation)
145
+ this.cached = { value, at: Date.now() }; return value; }).finally(() => { this.pending = undefined; });
146
+ await this.pending;
147
+ }
148
+ const value = this.cached?.value ?? this.snapshot("uninitialized", "正在初始化工作区…");
149
+ return { ...value, ...(latest ? { operation: { operationId: latest.operationId, state: latest.state, ...(latest.errorMessage ? { message: latest.errorMessage } : {}) } } : {}) };
150
+ }
151
+ async start(clientRequestId, model) {
152
+ const created = await this.options.operations.create({ clientRequestId, action: "workspaceInitialize", actionParams: { ...(model ? { model } : {}) } });
153
+ if (created.kind !== "created")
154
+ return created;
155
+ const operationId = created.operation.operationId;
156
+ if (this.activeOperationId) {
157
+ await this.options.operations.update(operationId, { state: "failed", finishedAt: new Date().toISOString(), errorCode: "WORKSPACE_INIT_BUSY", errorMessage: "此工作区已有初始化操作正在运行" });
158
+ return created;
159
+ }
160
+ this.activeOperationId = operationId;
161
+ this.generation += 1;
162
+ queueMicrotask(() => { void this.execute(operationId, model); });
163
+ return created;
164
+ }
165
+ async execute(operationId, model) {
166
+ let release;
167
+ try {
168
+ await this.options.operations.update(operationId, { state: "running", startedAt: new Date().toISOString() });
169
+ this.options.events.append(operationId, { at: new Date().toISOString(), kind: "state", state: "running", message: `正在检查并配置 ${this.options.name}` });
170
+ release = await (this.options.acquireLock ?? (() => acquireUpdateLock(updateLockPath(os.homedir()))))();
171
+ if (!release)
172
+ throw new Error("程序更新或其他初始化操作正在进行,请稍后重试");
173
+ const activity = await (this.options.readActivity ?? readInitRuntimeActivity)(this.options.repoRoot);
174
+ if (activity.workerProjectionError || activity.activeDagRunIds.length || activity.activeWorkerTasks || activity.activeWorkerBatches || this.options.activeChatTurnCount())
175
+ throw new Error("当前工作区有任务或对话正在运行,请结束后重试");
176
+ const operations = await this.options.operations.list();
177
+ if (operations.some(op => op.operationId !== operationId && !TERMINAL_OPERATION_STATES.has(op.state) && op.state !== "needs-reconcile"))
178
+ throw new Error("当前工作区有其他操作正在运行,请稍后重试");
179
+ const before = await this.inspect();
180
+ if (before.state === "error")
181
+ throw new Error(before.message);
182
+ if (before.state === "uninitialized") {
183
+ if (!model || !before.models?.some(item => item.reference === model))
184
+ throw new Error("请选择一个可用的初始模型;可在模型设置中完成认证后重新检查");
185
+ await this.command(["init", "--profile", "full", "--merge", "--client-recovery", "project", "--model", model, "--json"], "apply");
186
+ }
187
+ else if (before.canUpdate) {
188
+ await this.command(["init", "update", "--apply-safe", "--client-recovery", "project", "--json"], "apply");
189
+ }
190
+ else if (before.state !== "ready")
191
+ throw new Error("没有可安全更新的内容;请查看待处理项的原因和下一步。");
192
+ const after = await this.inspect();
193
+ this.cached = { value: after, at: Date.now() };
194
+ const state = after.state === "ready" ? "succeeded" : "needs-reconcile";
195
+ await this.options.operations.update(operationId, { state, finishedAt: new Date().toISOString(), result: { initialization: after }, ...(state !== "succeeded" ? { errorCode: "INIT_NEEDS_RECONCILE", errorMessage: after.message } : {}) });
196
+ this.options.events.append(operationId, { at: new Date().toISOString(), kind: "state", state, message: after.message });
197
+ }
198
+ catch (error) {
199
+ this.cached = undefined;
200
+ const message = error instanceof Error ? error.message : "工作区初始化失败";
201
+ await this.options.operations.update(operationId, { state: "failed", finishedAt: new Date().toISOString(), errorCode: "WORKSPACE_INIT_FAILED", errorMessage: message });
202
+ this.options.events.append(operationId, { at: new Date().toISOString(), kind: "error", state: "failed", message });
203
+ }
204
+ finally {
205
+ this.activeOperationId = undefined;
206
+ await release?.().catch(() => undefined);
207
+ }
208
+ }
209
+ }
@@ -360,6 +360,8 @@ function matchesGlob(filePath, glob) {
360
360
  let pattern = "^";
361
361
  for (let index = 0; index < normalizedGlob.length; index += 1) {
362
362
  const char = normalizedGlob[index];
363
+ if (char === undefined)
364
+ continue;
363
365
  if (char === "*" && normalizedGlob[index + 1] === "*") {
364
366
  pattern += ".*";
365
367
  index += 1;
@@ -204,6 +204,8 @@ export function validateFullstackStructure(input) {
204
204
  for (let j = i + 1; j < writerNodes.length; j++) {
205
205
  const a = writerNodes[i];
206
206
  const b = writerNodes[j];
207
+ if (!a || !b)
208
+ continue;
207
209
  const aPaths = collectWritePaths(taskSpecs.get(a.id));
208
210
  const bPaths = collectWritePaths(taskSpecs.get(b.id));
209
211
  if (!writeSetsOverlap(aPaths, bPaths))
@@ -310,7 +310,10 @@ function findSourceScript(baseArgs, cwd) {
310
310
  // Look for the last .ts/.js/.mjs/.cjs file in baseArgs
311
311
  let scriptIdx = -1;
312
312
  for (let i = baseArgs.length - 1; i >= 0; i--) {
313
- const ext = path.extname(baseArgs[i]).toLowerCase();
313
+ const candidate = baseArgs[i];
314
+ if (candidate === undefined)
315
+ continue;
316
+ const ext = path.extname(candidate).toLowerCase();
314
317
  if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
315
318
  scriptIdx = i;
316
319
  break;
@@ -318,7 +321,10 @@ function findSourceScript(baseArgs, cwd) {
318
321
  }
319
322
  if (scriptIdx < 0)
320
323
  return undefined;
321
- const scriptPath = path.resolve(cwd, baseArgs[scriptIdx]);
324
+ const scriptArg = baseArgs[scriptIdx];
325
+ if (scriptArg === undefined)
326
+ return undefined;
327
+ const scriptPath = path.resolve(cwd, scriptArg);
322
328
  try {
323
329
  const s = statSync(scriptPath);
324
330
  if (s.isFile())
@@ -551,7 +551,10 @@ export function indexAcceptanceItems(raw) {
551
551
  if (!match)
552
552
  continue;
553
553
  const id = match[1];
554
- const summary = compactSummary(match[2]);
554
+ const body = match[2];
555
+ if (id === undefined || body === undefined)
556
+ continue;
557
+ const summary = compactSummary(body);
555
558
  if (!map.has(id))
556
559
  map.set(id, summary);
557
560
  }
@@ -110,7 +110,7 @@ export async function projectMonthlyMetrics(input) {
110
110
  }
111
111
  function ratio(numerator, denominator, missingData) { return { numerator, denominator, value: denominator ? numerator / denominator : null, sampleSize: denominator, missingData }; }
112
112
  function median(values) { if (!values.length)
113
- return null; const middle = Math.floor(values.length / 2); return values.length % 2 ? values[middle] : (values[middle - 1] + values[middle]) / 2; }
113
+ return null; const middle = Math.floor(values.length / 2); return values.length % 2 ? (values[middle] ?? 0) : ((values[middle - 1] ?? 0) + (values[middle] ?? 0)) / 2; }
114
114
  function inWindow(value, start, end) { const time = new Date(value).getTime(); return time >= start.getTime() && time < end.getTime(); }
115
115
  function dedupeRuns(runs) { return [...new Map(runs.map((run) => [run.workerRunId, run])).values()]; }
116
116
  async function countBoundaryInterceptions(root, start, end) { let interceptions = 0; let audited = 0; try {
@@ -11,7 +11,10 @@ function serializeEvent(event) {
11
11
  }
12
12
  /** Identifiers used as event-store path segments must not alter the route. */
13
13
  export function isSafeObservabilityIdentifier(value) {
14
- return value !== "." && value !== ".." && !/[\\/\0]/.test(value);
14
+ return (typeof value === "string" &&
15
+ value !== "." &&
16
+ value !== ".." &&
17
+ !/[\\/\0]/.test(value));
15
18
  }
16
19
  export function createWorkerEventStore(jsonlPath) {
17
20
  return {
@@ -1444,8 +1444,11 @@ async function loadFailureHandoffs(repoRoot) {
1444
1444
  const match = /^(.+)-failure-closeout-draft\.md$/.exec(entry.name);
1445
1445
  if (!match)
1446
1446
  continue;
1447
+ const workerRunId = match[1];
1448
+ if (workerRunId === undefined)
1449
+ continue;
1447
1450
  handoffs.push({
1448
- workerRunId: match[1],
1451
+ workerRunId,
1449
1452
  closeoutDraftPath: path.join(handoffDir, entry.name),
1450
1453
  });
1451
1454
  }
@@ -106,6 +106,12 @@ export async function loadDagNodeContextSummary(repoRoot, dagRunId, nodeId, atte
106
106
  body: { error: "Invalid node identifier", reason: "invalid-id" },
107
107
  };
108
108
  }
109
+ if (!isSafeObservabilityIdentifier(dagRunId)) {
110
+ return {
111
+ status: 400,
112
+ body: { error: "Invalid DAG run identifier", reason: "invalid-id" },
113
+ };
114
+ }
109
115
  const loaded = await loadRunFacts(repoRoot, dagRunId);
110
116
  if (!loaded.ok)
111
117
  return loaded.failure;
@@ -242,6 +248,12 @@ export async function loadDagNodeArtifactInventory(repoRoot, dagRunId, nodeId) {
242
248
  body: { error: "Invalid node identifier", reason: "invalid-id" },
243
249
  };
244
250
  }
251
+ if (!isSafeObservabilityIdentifier(dagRunId)) {
252
+ return {
253
+ status: 400,
254
+ body: { error: "Invalid DAG run identifier", reason: "invalid-id" },
255
+ };
256
+ }
245
257
  const loaded = await loadRunFacts(repoRoot, dagRunId);
246
258
  if (!loaded.ok)
247
259
  return loaded.failure;
@@ -436,6 +448,8 @@ async function loadVerifiedArtifactBytes(repoRoot, dagRunId, nodeId, artifactId)
436
448
  }
437
449
  }
438
450
  export async function loadDagNodeArtifactPreview(repoRoot, dagRunId, nodeId, artifactId) {
451
+ if (!isSafeObservabilityIdentifier(dagRunId) || !isSafeObservabilityIdentifier(nodeId))
452
+ return { status: 400, body: { error: "Invalid DAG run or node identifier", reason: "invalid-id" } };
439
453
  const verified = await loadVerifiedArtifactBytes(repoRoot, dagRunId, nodeId, artifactId);
440
454
  if (verified.status !== 200)
441
455
  return verified;
@@ -448,6 +462,8 @@ export async function loadDagNodeArtifactPreview(repoRoot, dagRunId, nodeId, art
448
462
  }
449
463
  /** Full text uses the same binding and safety checks as the bounded preview. */
450
464
  export async function loadDagNodeArtifactDownload(repoRoot, dagRunId, nodeId, artifactId) {
465
+ if (!isSafeObservabilityIdentifier(dagRunId) || !isSafeObservabilityIdentifier(nodeId))
466
+ return { status: 400, body: { error: "Invalid DAG run or node identifier", reason: "invalid-id" } };
451
467
  const verified = await loadVerifiedArtifactBytes(repoRoot, dagRunId, nodeId, artifactId);
452
468
  if (verified.status !== 200)
453
469
  return verified;
@@ -469,7 +485,7 @@ async function readValidatedContextReceipt(boundaryRoot, targetPath) {
469
485
  return effectiveNodeContextV1Schema.safeParse(null);
470
486
  }
471
487
  export async function loadDagNodeContextComponent(repoRoot, dagRunId, nodeId, attempt, componentIndex) {
472
- if (!isSafeObservabilityIdentifier(nodeId) || !Number.isSafeInteger(attempt) || attempt < 1 || !Number.isSafeInteger(componentIndex) || componentIndex < 0)
488
+ if (!isSafeObservabilityIdentifier(dagRunId) || !isSafeObservabilityIdentifier(nodeId) || !Number.isSafeInteger(attempt) || attempt < 1 || !Number.isSafeInteger(componentIndex) || componentIndex < 0)
473
489
  return { status: 400, body: { error: "Invalid context selection", reason: "invalid-id" } };
474
490
  const loaded = await loadRunFacts(repoRoot, dagRunId);
475
491
  if (!loaded.ok)
@@ -502,6 +518,12 @@ function stringField(value, key) {
502
518
  return typeof value?.[key] === "string" ? value[key] : undefined;
503
519
  }
504
520
  export async function loadDagRunLineage(repoRoot, dagRunId) {
521
+ if (!isSafeObservabilityIdentifier(dagRunId)) {
522
+ return {
523
+ status: 400,
524
+ body: { error: "Invalid DAG run identifier", reason: "invalid-id" },
525
+ };
526
+ }
505
527
  const loaded = await loadRunFacts(repoRoot, dagRunId);
506
528
  if (!loaded.ok)
507
529
  return loaded.failure;
@@ -835,6 +835,10 @@ async function handleDagNodeExecutionOutput(_req, res, match, ctx) {
835
835
  const dagRunId = match.params.id;
836
836
  const nodeId = match.params.sub;
837
837
  const key = match.query.get("key");
838
+ if (dagRunId === undefined || nodeId === undefined) {
839
+ sendJson(res, 400, { error: "Invalid DAG node execution output selection", reason: "invalid-id" });
840
+ return;
841
+ }
838
842
  // Reject path-shaped inputs even if smuggled via key/query aliases.
839
843
  if (match.query.has("path") || match.query.has("artifactPath")) {
840
844
  sendJson(res, 400, {
@@ -26,6 +26,7 @@ export const INSPECT_TABS = Object.freeze([
26
26
  { id: "dashboard", label: "总览", href: "/#/inspect" },
27
27
  { id: "pool", label: "资源池", href: "/#/inspect/pool" },
28
28
  { id: "failures", label: "异常 Task", href: "/#/inspect/failures" },
29
+ { id: "scheduled-goals", label: "定时任务", href: "/#/inspect/scheduled-goals" },
29
30
  ]);
30
31
 
31
32
  export const READINESS_LABELS = Object.freeze({
@@ -92,7 +93,7 @@ export function parseInspectObjectView(hashValue) {
92
93
 
93
94
  /**
94
95
  * Inspect 当前 hash 应高亮的工作区 tab href。
95
- * 三个一级集合视图映射到各自 tab;下钻/对象页(dag/dags/run/night 等)
96
+ * 一级集合视图映射到各自 tab;下钻/对象页(dag/dags/run/night 等)
96
97
  * 不高亮任何 tab——位置由面包屑与上下文 chip 表达(`#/dags` 自 0.33.0
97
98
  * 起是独立历史分页页,不再是总览滚动别名)。
98
99
  */
@@ -106,6 +107,7 @@ export function activeInspectTabHref(hashValue) {
106
107
  return "/#/inspect/pool";
107
108
  }
108
109
  if (internal === "/failures") return "/#/inspect/failures";
110
+ if (internal === "/scheduled-goals") return "/#/inspect/scheduled-goals";
109
111
  if (internal.startsWith("/feature/")) return "/#/inspect";
110
112
  return null;
111
113
  }
@@ -1281,6 +1281,9 @@ body.is-resizing-dag-graph {
1281
1281
  user-select: none;
1282
1282
  }
1283
1283
  .dag-inspector-header {
1284
+ /* Keep the attempt menu above the content's sticky timeline toolbar. */
1285
+ position: relative;
1286
+ z-index: 6;
1284
1287
  display: grid;
1285
1288
  grid-template-columns: minmax(0, 1fr) auto;
1286
1289
  gap: var(--space-3);
@@ -5401,10 +5404,10 @@ button.copy-command:focus-visible {
5401
5404
  .risk-dag-items { display: grid; gap: 6px; padding-top: 8px; }
5402
5405
  /* The hosted Console also has .panel styles; keep the reader shell flush. */
5403
5406
  #dag-inspector { padding: 0; gap: 0; }
5404
- .dag-inspector-header .dag-inspector-attempt-bar { flex-wrap: nowrap; background: transparent; }
5405
- .dag-inspector-header .dag-inspector-attempt-select { min-width: 0; flex: 0 1 126px; }
5406
- .dag-inspector-header .dag-inspector-attempt-preview { flex: 1 1 auto; flex-wrap: nowrap; padding: 0; }
5407
- .dag-inspector-header .dag-inspector-attempt-category,
5407
+ .dag-inspector-header .dag-inspector-attempt-bar { flex-wrap: wrap; gap: 8px; background: transparent; }
5408
+ .dag-inspector-header .dag-inspector-attempt-select { min-width: 112px; flex: 0 0 126px; }
5409
+ .dag-inspector-header .dag-inspector-attempt-preview { flex: 1 1 100px; flex-wrap: wrap; padding: 0; }
5410
+ .dag-inspector-header .dag-inspector-attempt-category { display: inline; white-space: normal; overflow-wrap: anywhere; }
5408
5411
  .dag-inspector-header .dag-inspector-attempt-duration { display: none; }
5409
5412
  @container (max-width: 560px) {
5410
5413
  .process-timeline-toolbar { display: grid; grid-template-columns: minmax(80px, 1fr) 32px 32px 32px; gap: 4px; }
@@ -3590,21 +3590,13 @@ function buildAttemptBar(dagRunId, node) {
3590
3590
 
3591
3591
  const bar = el("div", "dag-inspector-attempt-bar");
3592
3592
  bar.appendChild(el("label", "dag-inspector-attempt-label", "执行分次"));
3593
- const choose = (nextKey, source = "select") => {
3593
+ const choose = (nextKey) => {
3594
3594
  uiState.dagInspectorAttempt = { dagRunId, nodeId: node.nodeId, key: String(nextKey) };
3595
3595
  inspectorNodeSelections.set(`${dagRunId}:${node.nodeId}`, uiState.dagInspectorAttempt);
3596
3596
  renderDagInspectorForCurrent();
3597
3597
  const nextBar = document.querySelector(".dag-inspector-header .dag-inspector-attempt-bar");
3598
- const control = nextBar?.querySelector(`[data-attempt-action="${source}"]`);
3599
- (control && !control.disabled ? control : nextBar?.querySelector(".custom-select-trigger"))?.focus({ preventScroll: true });
3598
+ nextBar?.querySelector(".custom-select-trigger")?.focus({ preventScroll: true });
3600
3599
  };
3601
- const selectedIndex = options.findIndex((option) => option.key === selectedKey);
3602
- const previous = iconButton("上一次尝试", "ri-arrow-left-s-line");
3603
- previous.type = "button";
3604
- previous.dataset.attemptAction = "previous";
3605
- previous.disabled = selectedIndex <= 0;
3606
- previous.addEventListener("click", () => choose(options[selectedIndex - 1].key, "previous"));
3607
- bar.appendChild(previous);
3608
3600
  const select = createCustomSelect({
3609
3601
  ariaLabel: "选择执行分次",
3610
3602
  className: "dag-inspector-attempt-select",
@@ -3619,12 +3611,6 @@ function buildAttemptBar(dagRunId, node) {
3619
3611
  // `.value` after assign + change (StubNode handler contract).
3620
3612
  select.value = selectedKey;
3621
3613
  bar.appendChild(select);
3622
- const next = iconButton("下一次尝试", "ri-arrow-right-s-line");
3623
- next.type = "button";
3624
- next.dataset.attemptAction = "next";
3625
- next.disabled = selectedIndex >= options.length - 1;
3626
- next.addEventListener("click", () => choose(options[selectedIndex + 1].key, "next"));
3627
- bar.appendChild(next);
3628
3614
 
3629
3615
  const selected =
3630
3616
  options.find((option) => option.key === selectedKey) ??
@@ -3636,16 +3622,16 @@ function buildAttemptBar(dagRunId, node) {
3636
3622
  if (status) {
3637
3623
  preview.appendChild(el("span", `badge badge-${status.cls}`, status.label));
3638
3624
  }
3639
- if (status?.cls === "failed" && data.failureCategory) {
3625
+ if (status?.cls === "failed") {
3640
3626
  // 仅失败尝试显示原因;部分 runtime 会在成功尝试保留
3641
3627
  // failureCategory="success",不能将其误映射为「其他失败原因」。
3642
3628
  // 未知失败分类仍 fail-closed,raw token 仅保留在 title 诊断中。
3643
3629
  const reason = el(
3644
3630
  "span",
3645
3631
  "dag-inspector-attempt-category",
3646
- `原因 ${attemptFailureLabel(data.failureCategory)}`,
3632
+ data.failureCategory ? `原因 ${attemptFailureLabel(data.failureCategory)}` : "未记录失败原因",
3647
3633
  );
3648
- reason.setAttribute("title", `failureCategory ${data.failureCategory}`);
3634
+ if (data.failureCategory) reason.setAttribute("title", `failureCategory ${data.failureCategory}`);
3649
3635
  preview.appendChild(reason);
3650
3636
  }
3651
3637
  if (data.durationMs !== undefined) {
@@ -12,6 +12,8 @@ export function matchesPathGlob(filePath, glob) {
12
12
  let pattern = "^";
13
13
  for (let index = 0; index < normalizedGlob.length; index += 1) {
14
14
  const char = normalizedGlob[index];
15
+ if (char === undefined)
16
+ continue;
15
17
  if (char === "*" && normalizedGlob[index + 1] === "*") {
16
18
  pattern += ".*";
17
19
  index += 1;
@@ -120,6 +120,9 @@ export async function preflightTargetRepo(input) {
120
120
  }
121
121
  if (input.runCheckRepo) {
122
122
  const [command, ...args] = input.checkRepoCommand ?? ["bash", "scripts/check-repo.sh"];
123
+ if (command === undefined) {
124
+ throw new Error("check-repo command requires a binary");
125
+ }
123
126
  const checkRepo = await input.client.runExternal(command, args, {
124
127
  cwd: input.repoRoot,
125
128
  artifactName: "check-repo",