@tea-agent/loop-agent 0.42.0-next.9 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/CHANGELOG.md +111 -45
  2. package/dist/application/dag/run-dag.js +8 -2
  3. package/dist/application/evaluation/budget.js +19 -1
  4. package/dist/application/task-lifecycle/advance.js +26 -8
  5. package/dist/application/task-lifecycle/observe.js +43 -29
  6. package/dist/application/task-lifecycle/plan-transitions.js +5 -4
  7. package/dist/build-stamp.json +3 -3
  8. package/dist/cli/program.js +1 -1
  9. package/dist/commands/dag-rerun-task.js +2 -0
  10. package/dist/commands/task-source-prepare.js +3 -1
  11. package/dist/executors/dag-pi-executor.js +2584 -600
  12. package/dist/executors/pi-executor.js +22 -1
  13. package/dist/executors/pi-extension-resolver.js +14 -2
  14. package/dist/executors/pi-sdk-executor.js +140 -39
  15. package/dist/executors/shell-executor.js +135 -55
  16. package/dist/shared/dag-failure-category.js +6 -0
  17. package/dist/shared/frontend-execution-policy.js +23 -0
  18. package/dist/task/config-types.js +4 -0
  19. package/dist/task/contract/apply.js +36 -2
  20. package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
  21. package/dist/task/source-prepare/ledger-review.js +6 -9
  22. package/dist/task/source-prepare/parse-intent.js +7 -0
  23. package/dist/task/source-prepare/semantic-intake.js +16 -26
  24. package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
  25. package/dist/task/source-references.js +48 -23
  26. package/dist/worker/console/chat/assistant-content.js +23 -2
  27. package/dist/worker/console/chat/browser-policy.js +143 -0
  28. package/dist/worker/console/chat/browser-routes.js +148 -0
  29. package/dist/worker/console/chat/chat-event-store.js +4 -2
  30. package/dist/worker/console/chat/explore-tools.js +13 -0
  31. package/dist/worker/console/chat/pi-runtime.js +173 -94
  32. package/dist/worker/console/chat/resource-loader.js +4 -1
  33. package/dist/worker/console/chat/routes.js +208 -66
  34. package/dist/worker/console/chat/scm-routes.js +217 -0
  35. package/dist/worker/console/chat/scm-service.js +283 -0
  36. package/dist/worker/console/chat/scm-tools.js +111 -0
  37. package/dist/worker/console/chat/sdd-data-alignment.js +78 -11
  38. package/dist/worker/console/chat/session-catalog.js +32 -0
  39. package/dist/worker/console/chat/session-mode-view.js +48 -0
  40. package/dist/worker/console/chat/session-mode.js +218 -0
  41. package/dist/worker/console/chat/session-store.js +27 -7
  42. package/dist/worker/console/chat/shortcuts.js +6 -0
  43. package/dist/worker/console/chat/subagents/agent-tool.js +62 -0
  44. package/dist/worker/console/chat/subagents/explore-agent.js +169 -0
  45. package/dist/worker/console/chat/subagents/index.js +5 -0
  46. package/dist/worker/console/chat/subagents/orchestrator.js +273 -0
  47. package/dist/worker/console/chat/subagents/tool-policy.js +53 -0
  48. package/dist/worker/console/chat/subagents/types.js +13 -0
  49. package/dist/worker/console/chat/terminal-routes.js +216 -0
  50. package/dist/worker/console/chat/terminal-sessions.js +284 -0
  51. package/dist/worker/console/chat/terminal-tools.js +199 -0
  52. package/dist/worker/console/chat/tool-preview.js +21 -0
  53. package/dist/worker/console/chat/tools.js +13 -1
  54. package/dist/worker/console/chat/turn-process.js +1 -0
  55. package/dist/worker/console/interview/tools.js +1 -0
  56. package/dist/worker/console/server.js +2 -28
  57. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-C9eI7nEo.js} +1 -1
  58. package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-VJWsxWhB.js} +1 -1
  59. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-BGcyiMSo.js} +1 -1
  60. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-BScnFwyi.js} +1 -1
  61. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Bl9_BsLi.js} +1 -1
  62. package/dist/worker/console/static/assets/channel-B6sQYuOE.js +1 -0
  63. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-F1uKxiUt.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-rSRMynVu.js} +1 -1
  65. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-DAsJm1WD.js} +1 -1
  66. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-DQIdVI0a.js} +1 -1
  67. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-4A_8rJ-Q.js} +1 -1
  68. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-BHnT-vjh.js} +1 -1
  69. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-Rq7QKBkn.js} +1 -1
  70. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BXh-AI2u.js} +1 -1
  71. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-BblcLy8m.js} +1 -1
  72. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-zHZmVnB6.js} +1 -1
  73. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Z6s5QoeI.js +1 -0
  74. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Z6s5QoeI.js +1 -0
  75. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-Cmwz0Wi4.js} +1 -1
  76. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-Dq76MHQU.js} +1 -1
  77. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-zPahV1fM.js} +1 -1
  78. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-DQJ3xPc_.js} +1 -1
  79. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BwX7u6fv.js} +1 -1
  80. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CodnDCco.js} +1 -1
  81. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-gAjtWkxG.js} +1 -1
  82. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-CGCqz0cS.js} +1 -1
  83. package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-RHwYEywd.js} +1 -1
  84. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-B6seb_p_.js} +1 -1
  85. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-CmwCKYe8.js} +1 -1
  86. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-BfNsaZ0V.js} +1 -1
  87. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-zmYgi_Z3.js} +1 -1
  88. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-BDEzzSKF.js} +1 -1
  89. package/dist/worker/console/static/assets/index-DgenUAfc.js +468 -0
  90. package/dist/worker/console/static/assets/index-aF4u-Y__.css +1 -0
  91. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-DeSz8l42.js} +1 -1
  92. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-B1LV29aH.js} +1 -1
  93. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-CCw4W2oU.js} +1 -1
  94. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BozaMoRJ.js} +1 -1
  95. package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-G1JR40SX.js} +1 -1
  96. package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-CCgPF0oA.js} +5 -5
  97. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-CEqnCHnD.js} +1 -1
  98. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-DpmQPKuI.js} +1 -1
  99. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-BP501AxR.js} +1 -1
  100. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-D1dBhl_A.js} +1 -1
  101. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-DgZDS5pY.js} +1 -1
  102. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-BzB3pyIq.js} +1 -1
  103. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-B9ZprFFP.js} +1 -1
  104. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CC2-_XrA.js} +1 -1
  105. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-UVtAUqgB.js} +1 -1
  106. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-CWcS5JYG.js} +1 -1
  107. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BooX8u1Q.js +1 -0
  108. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-C4p-dn06.js} +2 -2
  109. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D64_eqqE.js +8 -0
  110. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-Dnz52OHX.js} +1 -1
  111. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-CzTE0Nfu.js} +1 -1
  112. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-NAR9cjQQ.js} +1 -1
  113. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-DVMk8413.js} +1 -1
  114. package/dist/worker/console/static/index.html +2 -2
  115. package/dist/worker/console/static-src/chat-view-types.js +1 -1
  116. package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
  117. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +142 -7
  118. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +44 -0
  119. package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
  120. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
  121. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
  122. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
  123. package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
  124. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +33 -18
  125. package/dist/worker/console/static-src/operator-chat/useChatStream.js +48 -16
  126. package/dist/worker/console/static-src/operator-chat/useChatThread.js +170 -7
  127. package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -7
  128. package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
  129. package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
  130. package/dist/worker/console/workspace-context.js +15 -1
  131. package/dist/worker/observe/node-transparency.js +81 -72
  132. package/dist/worker/observe/routes.js +20 -1
  133. package/dist/worker/observe/static/constants.js +22 -22
  134. package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
  135. package/dist/worker/observe/static/dag-history-labels.js +1 -0
  136. package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
  137. package/dist/worker/observe/static/dag-inspector-humanize.js +342 -0
  138. package/dist/worker/observe/static/dag-node-purpose.js +10 -10
  139. package/dist/worker/observe/static/dom.js +20 -1
  140. package/dist/worker/observe/static/format-pool.d.ts +2 -0
  141. package/dist/worker/observe/static/format-pool.js +6 -0
  142. package/dist/worker/observe/static/format.js +7 -0
  143. package/dist/worker/observe/static/index.html +4 -4
  144. package/dist/worker/observe/static/inspect-workspace.js +34 -7
  145. package/dist/worker/observe/static/inspector-submission.js +32 -0
  146. package/dist/worker/observe/static/kpi.js +1 -0
  147. package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
  148. package/dist/worker/observe/static/relations.js +7 -5
  149. package/dist/worker/observe/static/router.js +13 -0
  150. package/dist/worker/observe/static/run-processing.js +2 -0
  151. package/dist/worker/observe/static/shell-chrome.js +36 -3
  152. package/dist/worker/observe/static/state.js +35 -2
  153. package/dist/worker/observe/static/styles.css +431 -39
  154. package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
  155. package/dist/worker/observe/static/task-failure-labels.js +67 -0
  156. package/dist/worker/observe/static/task-history.js +12 -0
  157. package/dist/worker/observe/static/views/batch.js +6 -13
  158. package/dist/worker/observe/static/views/dag-graph.js +51 -4
  159. package/dist/worker/observe/static/views/dag-inspector.js +1069 -426
  160. package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
  161. package/dist/worker/observe/static/views/dag.d.ts +6 -0
  162. package/dist/worker/observe/static/views/dag.js +64 -22
  163. package/dist/worker/observe/static/views/dags.js +2 -0
  164. package/dist/worker/observe/static/views/dashboard.js +21 -12
  165. package/dist/worker/observe/static/views/failures.js +21 -11
  166. package/dist/worker/observe/static/views/feature.js +11 -29
  167. package/dist/worker/observe/static/views/pool.js +37 -28
  168. package/dist/worker/observe/static/views/run.js +48 -5
  169. package/dist/worker/observe/static/views/session-timeline.js +194 -243
  170. package/dist/worker/observe/static/views/task.js +81 -62
  171. package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
  172. package/dist/workflows/dag/budget-enforcement.js +53 -3
  173. package/dist/workflows/dag/dag-retry-schema.js +3 -0
  174. package/dist/workflows/dag/frontend-capacity.js +9 -0
  175. package/dist/workflows/dag/frontend-contract-facts.js +130 -0
  176. package/dist/workflows/dag/frontend-design-policy.js +103 -17
  177. package/dist/workflows/dag/frontend-durable-tools.js +193 -0
  178. package/dist/workflows/dag/frontend-execution-groups.js +24 -0
  179. package/dist/workflows/dag/frontend-implementation-contract.js +423 -41
  180. package/dist/workflows/dag/frontend-input-projection.js +76 -0
  181. package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
  182. package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
  183. package/dist/workflows/dag/frontend-plan-render.js +21 -3
  184. package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
  185. package/dist/workflows/dag/frontend-recovery-controller.js +32 -8
  186. package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
  187. package/dist/workflows/dag/frontend-recovery-plan.js +4 -1
  188. package/dist/workflows/dag/frontend-recovery-run.js +146 -16
  189. package/dist/workflows/dag/frontend-review-scopes.js +139 -0
  190. package/dist/workflows/dag/frontend-risk.js +2 -0
  191. package/dist/workflows/dag/frontend-session-budget.js +249 -0
  192. package/dist/workflows/dag/frontend-shadow-dual-write.js +37 -3
  193. package/dist/workflows/dag/frontend-shape-facts.js +12 -2
  194. package/dist/workflows/dag/frontend-shape.js +46 -7
  195. package/dist/workflows/dag/frontend-test-execution-evidence.js +49 -0
  196. package/dist/workflows/dag/frontend-typed-event-store.js +14 -0
  197. package/dist/workflows/dag/frontend-verification-trace.js +38 -4
  198. package/dist/workflows/dag/frontend-writer-admission.js +2 -2
  199. package/dist/workflows/dag/init-hybrid.js +70 -35
  200. package/dist/workflows/dag/node-execution.js +225 -226
  201. package/dist/workflows/dag/prompt.js +4 -0
  202. package/dist/workflows/dag/recovery-lease.js +106 -16
  203. package/dist/workflows/dag/rerun-feedback.js +1 -0
  204. package/dist/workflows/dag/rerun-plan.js +96 -4
  205. package/dist/workflows/dag/rerun-run.js +28 -6
  206. package/dist/workflows/dag/rerun-task.js +221 -18
  207. package/dist/workflows/dag/retry-policy.js +27 -10
  208. package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
  209. package/dist/workflows/dag/runner.js +246 -19
  210. package/dist/workflows/dag/structured-output-repair.js +4 -1
  211. package/dist/workflows/dag/types.js +10 -3
  212. package/dist/workflows/dag/validate.js +10 -8
  213. package/dist/workflows/dag/workspace-checkpoint.js +66 -0
  214. package/docs/operations/README.md +2 -0
  215. package/docs/templates/agent-dag.schema.json +2 -2
  216. package/docs/templates/backend-test-dag.json +6 -4
  217. package/docs/templates/frontend-design-contract.md +4 -4
  218. package/docs/templates/frontend-implementation-contract.schema.json +68 -4
  219. package/docs/templates/frontend-implementation-dag.json +5 -5
  220. package/harness.json +1 -1
  221. package/package.json +8 -6
  222. package/skills/frontend-contract/SKILL.md +2 -1
  223. package/skills/frontend-contract/references/contract-protocol.md +19 -3
  224. package/skills/frontend-design-review/SKILL.md +12 -11
  225. package/skills/frontend-plan/SKILL.md +22 -2
  226. package/skills/frontend-plan/references/decision-contract.md +114 -5
  227. package/skills/frontend-plan/references/design-decisions.md +32 -0
  228. package/skills/frontend-review/SKILL.md +10 -11
  229. package/skills/frontend-scout/references/scout-evidence.md +4 -0
  230. package/dist/worker/console/static/assets/channel-DsZxrgqe.js +0 -1
  231. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +0 -1
  232. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +0 -1
  233. package/dist/worker/console/static/assets/index-D9Sc0f0y.js +0 -449
  234. package/dist/worker/console/static/assets/index-DDQc5a50.css +0 -1
  235. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +0 -1
  236. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +0 -8
@@ -1,3 +1,4 @@
1
+ import { inspectRouteStates } from "./state.js";
1
2
  /**
2
3
  * Hosted Inspect workspace adapter.
3
4
  *
@@ -44,6 +45,9 @@ import {
44
45
 
45
46
  let instance = null;
46
47
  let lastInspectHash = "";
48
+ let renderedHash = "";
49
+ let renderedScope = null;
50
+ let routeToken = 0;
47
51
  let listenersBound = false;
48
52
 
49
53
  export function getInspectWorkspaceInstance() {
@@ -129,7 +133,7 @@ function startView(routeValue) {
129
133
  if (routeValue.view === "dashboard") {
130
134
  startDashboardPolling(routeValue.scrollTo);
131
135
  } else if (routeValue.view === "dags") {
132
- void startDagsView();
136
+ return startDagsView();
133
137
  } else if (routeValue.view === "batch") {
134
138
  void renderBatch(routeValue.batchRunId);
135
139
  } else if (routeValue.view === "run") {
@@ -137,9 +141,9 @@ function startView(routeValue) {
137
141
  } else if (routeValue.view === "dag") {
138
142
  startDagPolling(routeValue.dagRunId);
139
143
  } else if (routeValue.view === "failures") {
140
- void renderFailures();
144
+ return renderFailures();
141
145
  } else if (routeValue.view === "pool") {
142
- startPoolView();
146
+ return startPoolView();
143
147
  } else if (routeValue.view === "night") {
144
148
  void renderNightJobs(routeValue.scheduleId);
145
149
  } else if (routeValue.view === "task") {
@@ -150,11 +154,34 @@ function startView(routeValue) {
150
154
  }
151
155
 
152
156
  export function route(hashValue) {
157
+ const scope = workspaceScope();
158
+ const scrollRoot = instance?.root ?? document.scrollingElement;
159
+ const destination = hashValue || (typeof location !== "undefined" ? location.hash : "#/inspect");
160
+ if (renderedHash && renderedScope === scope) {
161
+ const previous = parseHashRouteWithFilters(renderedHash);
162
+ if (["pool", "dags", "failures"].includes(previous.view)) {
163
+ inspectRouteStates.set(`list:${renderedHash}`, { top: scrollRoot?.scrollTop ?? 0, left: scrollRoot?.scrollLeft ?? 0 });
164
+ inspectRouteStates.set("return-list", renderedHash);
165
+ }
166
+ if (previous.view === "dag" && uiState.currentDagRunId) inspectRouteStates.set(`dag:${uiState.currentDagRunId}`, {
167
+ nodeId: uiState.selectedDagNodeId, tab: uiState.dagInspectorTab,
168
+ executionKey: uiState.dagInspectorAttempt?.key ?? null, open: uiState.dagInspectorOpen,
169
+ });
170
+ }
171
+ renderedHash = destination;
172
+ renderedScope = scope;
173
+ const token = ++routeToken;
153
174
  stopTimers();
154
- const parsed = hashValue
155
- ? parseHashRouteWithFilters(hashValue)
156
- : parseRoute();
157
- startView(parsed);
175
+ const parsed = parseHashRouteWithFilters(destination);
176
+ const saved = inspectRouteStates.get(`list:${destination}`);
177
+ Promise.resolve(startView(parsed)).then(() => {
178
+ if (!saved || token !== routeToken || scope !== workspaceScope()) return;
179
+ requestAnimationFrame(() => {
180
+ if (token !== routeToken || scope !== workspaceScope() || !scrollRoot) return;
181
+ scrollRoot.scrollTop = saved.top;
182
+ scrollRoot.scrollLeft = saved.left;
183
+ });
184
+ });
158
185
  }
159
186
 
160
187
  function resumeView(hashValue) {
@@ -0,0 +1,32 @@
1
+ /** A retry always replays the exact reviewed payload and idempotency key. */
2
+ export function createInspectorSubmission(action, payload) {
3
+ return {
4
+ action,
5
+ payload: JSON.parse(JSON.stringify(payload)),
6
+ requestId: `inspect-${action}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
7
+ inFlight: false,
8
+ operationId: null,
9
+ rejected: false,
10
+ };
11
+ }
12
+
13
+ export async function sendInspectorSubmission(submission, send) {
14
+ if (submission.pending) return submission.pending;
15
+ if (submission.operationId) return submission.response;
16
+ submission.inFlight = true;
17
+ submission.pending = (async () => {
18
+ const response = await send(submission.action, submission.payload, submission.requestId);
19
+ submission.response = response;
20
+ submission.operationId = response.ok ? response.body?.operationId ?? null : null;
21
+ // Only a definitive client rejection permits a different payload. Transport
22
+ // failure and server failure can follow acceptance, so they require replay.
23
+ submission.rejected = !response.ok && response.status >= 400 && response.status < 500;
24
+ return response;
25
+ })();
26
+ try { return await submission.pending; }
27
+ finally { submission.inFlight = false; submission.pending = null; }
28
+ }
29
+
30
+ export function hasUnresolvedSubmission(submission) {
31
+ return Boolean(submission?.payload && !submission.operationId && !submission.rejected);
32
+ }
@@ -21,6 +21,7 @@ export function artifactLink(label, refPath) {
21
21
  const a = el("a", "artifact-link", label);
22
22
  a.href = `/api/artifacts?path=${encodeURIComponent(refPath)}&tail=200`;
23
23
  a.target = "_blank";
24
+ a.addEventListener("click", (event) => event.stopPropagation());
24
25
  return a;
25
26
  }
26
27
 
@@ -1,3 +1,5 @@
1
+ import { attemptFailureLabel } from "./dag-history-labels.js";
2
+
1
3
  export function buildPromptRestartCandidateOptions(
2
4
  frozenPrompt,
3
5
  failedCandidates,
@@ -5,7 +7,7 @@ export function buildPromptRestartCandidateOptions(
5
7
  const options = [
6
8
  {
7
9
  key: "frozen",
8
- label: "冻结原始提示词",
10
+ label: "运行开始时的提示词",
9
11
  prompt: String(frozenPrompt ?? ""),
10
12
  truncated: false,
11
13
  },
@@ -27,7 +29,7 @@ export function buildPromptRestartCandidateOptions(
27
29
  : "unknown";
28
30
  options.push({
29
31
  key: `attempt-${attempt}`,
30
- label: `第 ${attempt} 次失败 · ${failureCategory}`,
32
+ label: `第 ${attempt} 次失败 · ${attemptFailureLabel(failureCategory)}`,
31
33
  prompt: candidate.prompt,
32
34
  truncated: candidate.truncated === true,
33
35
  });
@@ -1,3 +1,4 @@
1
+ import { isPlainNavigationClick, isRowNavigationClick } from "./router.js";
1
2
  /** Object relation navigation (Observe UI R5). */
2
3
  import { el } from "./dom.js";
3
4
  import { inspectPathToCanonicalHref, navigate } from "./router.js";
@@ -50,6 +51,7 @@ export function objectRouteLink(kind, id, snapshot, featureId) {
50
51
  const a = el("a", "object-link", id);
51
52
  a.href = inspectPathToCanonicalHref(path);
52
53
  a.addEventListener("click", (e) => {
54
+ if (!isPlainNavigationClick(e)) return;
53
55
  e.preventDefault();
54
56
  e.stopPropagation();
55
57
  navigate(path);
@@ -70,11 +72,11 @@ export function featureLookupState(snapshot, featureId) {
70
72
 
71
73
  export const RELATION_KIND_ORDER = ["feature", "task", "batch", "run", "dag"];
72
74
  export const RELATION_LABELS = {
73
- feature: "Feature",
74
- task: "Task",
75
- batch: "Batch",
76
- run: "Run",
77
- dag: "DAG",
75
+ feature: "需求",
76
+ task: "任务",
77
+ batch: "任务批次",
78
+ run: "执行实例",
79
+ dag: "运行流程",
78
80
  };
79
81
 
80
82
  /**
@@ -201,6 +201,11 @@ export function parseHashRouteWithFilters(hashValue) {
201
201
  const raw = String(hashValue ?? "");
202
202
  const withoutQuery = raw.replace(/^#/, "").split("?")[0];
203
203
  const base = parseHashRoute("#" + (withoutQuery || "/"));
204
+ if (base.view === "dag") {
205
+ const params = new URLSearchParams(raw.includes("?") ? raw.slice(raw.indexOf("?") + 1) : "");
206
+ const nodeId = params.get("node");
207
+ if (nodeId) return { ...base, inspector: { nodeId, tab: params.get("tab") || null, executionKey: /^(attempt|pass)-[1-9]\d*$/.test(params.get("attempt") || "") ? params.get("attempt") : null } };
208
+ }
204
209
  if (base.view === "dags") {
205
210
  const history = parseDagHistoryQuery(raw);
206
211
  return { ...base, page: history.page, pageSize: history.pageSize };
@@ -220,3 +225,11 @@ export function buildHashPath(path, filters = {}) {
220
225
  const qs = params.toString();
221
226
  return qs ? `${base}?${qs}` : base;
222
227
  }
228
+
229
+ /** Leave browser-native modified clicks and text selection intact. */
230
+ export function isPlainNavigationClick(event) {
231
+ return !event.defaultPrevented && (event.button == null || event.button === 0) && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
232
+ }
233
+ export function isRowNavigationClick(event) {
234
+ return isPlainNavigationClick(event) && !event.target?.closest?.("a, button, input, select, textarea, summary, [role='button']") && !String(globalThis.getSelection?.() ?? "").trim();
235
+ }
@@ -1,3 +1,4 @@
1
+ import { isPlainNavigationClick, isRowNavigationClick } from "./router.js";
1
2
  /** Run step/command/artifact processing and task history rows. */
2
3
  import {
3
4
  STEP_LABELS,
@@ -132,6 +133,7 @@ export function taskRunHistoryRow(run) {
132
133
  );
133
134
  a.textContent = run.workerRunId;
134
135
  a.addEventListener("click", (e) => {
136
+ if (!isPlainNavigationClick(e)) return;
135
137
  e.preventDefault();
136
138
  navigate(`#/run/${encodeURIComponent(run.workerRunId)}`);
137
139
  });
@@ -1,6 +1,7 @@
1
+ import { isPlainNavigationClick, isRowNavigationClick } from "./router.js";
1
2
  /** Shell chrome: view switching, breadcrumb, header refresh, relation bar. */
2
3
  import { UI_TEXT } from "./constants.js";
3
- import { clearNode, el } from "./dom.js";
4
+ import { clearNode, el, iconButton } from "./dom.js";
4
5
  import { formatAgo } from "./format.js";
5
6
  import { inspectPathToCanonicalHref, navigate } from "./router.js";
6
7
  import { buildObjectRelations, renderObjectRelationBar } from "./relations.js";
@@ -26,12 +27,15 @@ export function setBreadcrumb(parts) {
26
27
  a.href = inspectPathToCanonicalHref(part.href);
27
28
  a.textContent = part.label;
28
29
  a.addEventListener("click", (e) => {
30
+ if (!isPlainNavigationClick(e)) return;
29
31
  e.preventDefault();
30
32
  navigate(part.href);
31
33
  });
32
34
  nav.appendChild(a);
33
35
  } else {
34
- nav.appendChild(document.createTextNode(part.label));
36
+ // 当前位置包一层 span:窄屏可对末级做 ellipsis 截断。
37
+ const current = el("span", "breadcrumb-current", part.label);
38
+ nav.appendChild(current);
35
39
  }
36
40
  });
37
41
  }
@@ -43,7 +47,7 @@ export function updateHeaderRefresh(generatedAt, options = {}) {
43
47
  elRefresh.textContent = "";
44
48
  return;
45
49
  }
46
- const modeLabel =
50
+ const modeLabel = options.mode === "ended" ? "已结束 · 按需刷新" : options.mode === "paused" ? "刷新已暂停" :
47
51
  options.mode === "manual"
48
52
  ? UI_TEXT.manualRefresh
49
53
  : options.mode === "auto"
@@ -63,3 +67,32 @@ export function mountRelationBar(containerId, ids, snapshot) {
63
67
  elBar.appendChild(el("p", "empty", "关联投影暂时不可用。"));
64
68
  }
65
69
  }
70
+
71
+ export function bindManualViewRefresh(view, refresh) {
72
+ const heading = document.querySelector(`#view-${view} .view-heading`);
73
+ if (!heading) return;
74
+ let button = heading.querySelector(".view-manual-refresh");
75
+ if (!button) { button = iconButton("刷新本页", "ri-refresh-line", "view-manual-refresh"); button.type = "button"; heading.appendChild(button); }
76
+ button.onclick = async () => { button.disabled = true; try { await refresh(); } finally { button.disabled = false; } };
77
+ }
78
+
79
+ export function mountTaskRelationPicker(containerId, ids, tasks, snapshot) {
80
+ const host = document.getElementById(containerId);
81
+ if (!host) return;
82
+ const previous = host.dataset.selectedTask;
83
+ const taskKey = (task) => JSON.stringify([task.featureId ?? ids.featureId ?? "", task.taskId]);
84
+ clearNode(host);
85
+ const select = el("select", "relation-task-picker");
86
+ select.setAttribute("aria-label", `选择关联任务,共 ${tasks.length} 个`);
87
+ for (const task of tasks) { const option = el("option", null, `${task.title || task.taskId} · ${task.status ?? "未记录"}`); option.value = taskKey(task); select.appendChild(option); }
88
+ select.value = tasks.some((task) => taskKey(task) === previous) ? previous : (tasks[0] ? taskKey(tasks[0]) : "");
89
+ const relations = el("div");
90
+ const render = () => {
91
+ host.dataset.selectedTask = select.value;
92
+ const task = tasks.find((task) => taskKey(task) === select.value) ?? tasks[0];
93
+ clearNode(relations);
94
+ relations.appendChild(renderObjectRelationBar(buildObjectRelations({ ...ids, taskId: task?.taskId, featureId: task?.featureId ?? ids.featureId, workerRunId: task?.workerRunId, dagRunId: task?.dagRunId, batchRunId: ids.batchRunId ?? task?.batchRunId }, snapshot), snapshot));
95
+ };
96
+ if (tasks.length > 1) { host.appendChild(el("span", "muted", `${tasks.length} 个关联任务 `)); host.appendChild(select); }
97
+ host.appendChild(relations); select.addEventListener("change", render); render();
98
+ }
@@ -173,8 +173,25 @@ export function updateDagTrajectoryViewportState(
173
173
  export const dagInspectorViewportStates = new Map();
174
174
 
175
175
  /** Build a stable inspector scroll region key (AC-005 context isolation). */
176
- export function inspectorViewportRegionKey(dagRunId, nodeId, tab) {
177
- return `dag:${dagRunId ?? ""}:${nodeId ?? ""}:${tab ?? "output"}`;
176
+ export function inspectorViewportRegionKey(dagRunId, nodeId, tab, executionKey = "") {
177
+ return `dag:${dagRunId ?? ""}:${nodeId ?? ""}:${tab ?? "output"}${executionKey ? `:${executionKey}` : ""}`;
178
+ }
179
+
180
+ /** Session-only reading/draft state. Never persists prompt text to disk. */
181
+ export const inspectorReadingStates = new Map();
182
+ export const inspectorNodeSelections = new Map();
183
+ export const inspectorDrafts = new Map();
184
+ export const inspectorOperations = new Map();
185
+ export const inspectRouteStates = new Map();
186
+ export const taskHistoryStates = new Map();
187
+ export function inspectorReadingState(dagRunId, nodeId, tab, executionKey, followLatest = false) {
188
+ const key = inspectorViewportRegionKey(dagRunId, nodeId, tab, executionKey || "latest");
189
+ if (!inspectorReadingStates.has(key)) inspectorReadingStates.set(key, {
190
+ key, scrollTop: 0, scrollLeft: 0, followLatest,
191
+ filter: "all", query: "", errorsOnly: false, showProtocol: false,
192
+ openDetails: new Set(), seenCount: 0, eventCount: 0,
193
+ });
194
+ return inspectorReadingStates.get(key);
178
195
  }
179
196
 
180
197
  export function updateDagInspectorViewportState(
@@ -403,6 +420,7 @@ export function applyWorkspaceScopeSwitch(
403
420
  trace.push("abort-old");
404
421
  // 2. clear old
405
422
  uiState.lastSnapshot = null;
423
+ for (const cache of [dagInspectorViewportStates, inspectorReadingStates, inspectorNodeSelections, inspectorDrafts, inspectorOperations, inspectRouteStates, taskHistoryStates]) cache.clear();
406
424
  uiState.specEvidenceDetail = null;
407
425
  uiState.specEvidenceListEvidence = null;
408
426
  clearDagNodeExecutionOutputCache();
@@ -531,6 +549,21 @@ export function computeFollowLatest(
531
549
  return height - top - client < Math.max(0, Number(threshold) || 0);
532
550
  }
533
551
 
552
+ // Chat-style jump-to-bottom visibility: the floating control is only useful
553
+ // when the region can scroll and the viewport is away from the bottom. It
554
+ // applies to every execution-record viewport, running or ended alike.
555
+ export function isScrollRegionAboveBottom(content, overflowPx = 4) {
556
+ const scrollHeight = Number(content?.scrollHeight ?? 0);
557
+ const clientHeight = Number(content?.clientHeight ?? 0);
558
+ if (!Number.isFinite(scrollHeight) || !Number.isFinite(clientHeight)) {
559
+ return false;
560
+ }
561
+ if (scrollHeight - clientHeight <= Math.max(0, Number(overflowPx) || 0)) {
562
+ return false;
563
+ }
564
+ return !computeFollowLatest(content?.scrollTop ?? 0, scrollHeight, clientHeight);
565
+ }
566
+
534
567
  // Generalised viewport-state record keyed by `regionKey`, so the same helper
535
568
  // serves the DAG node inspector output ("dag:<runId>:<nodeId>:output") and the
536
569
  // Worker run output ("run:<runId>:output").