@tea-agent/loop-agent 0.40.0-next.1 → 0.40.0-next.11

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 (213) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +114 -109
  3. package/README.md +2 -2
  4. package/dist/application/dag/generate-task-dag.js +6 -2
  5. package/dist/application/task-lifecycle/advance.js +12 -0
  6. package/dist/build-stamp.json +3 -3
  7. package/dist/cli/update/runtime-activity.js +1 -29
  8. package/dist/commands/dag-rerun.js +1 -1
  9. package/dist/executors/dag-pi-executor.js +69 -7
  10. package/dist/executors/pi-executor.js +4 -2
  11. package/dist/executors/pi-sdk-executor.js +46 -1
  12. package/dist/executors/pi-writer-tool-policy.js +57 -1
  13. package/dist/executors/shell-executor.js +107 -20
  14. package/dist/executors/workspace-write-snapshot.js +68 -0
  15. package/dist/governance/checks.js +1 -0
  16. package/dist/infrastructure/harness/atomic-write.js +23 -0
  17. package/dist/shared/dag-prompt-override.js +4 -5
  18. package/dist/shared/update/npm-client.js +33 -11
  19. package/dist/task/config-types.js +10 -0
  20. package/dist/task/contract/apply.js +11 -1
  21. package/dist/task/contract/constants.js +2 -0
  22. package/dist/task/contract/hash.js +3 -0
  23. package/dist/task/contract/observe.js +25 -4
  24. package/dist/task/contract/paths.js +2 -1
  25. package/dist/task/contract/project.js +15 -1
  26. package/dist/task/contract/schema.js +2 -0
  27. package/dist/task/contract/transaction.js +12 -0
  28. package/dist/task/source-prepare/completeness.js +188 -0
  29. package/dist/task/source-prepare/fragment-inventory.js +474 -0
  30. package/dist/task/source-prepare/index.js +3 -0
  31. package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
  32. package/dist/task/source-prepare/ledger-review.js +214 -0
  33. package/dist/task/source-prepare/ledger.js +545 -0
  34. package/dist/task/source-prepare/prepare.js +262 -1
  35. package/dist/task/source-prepare/semantic-intake.js +19 -3
  36. package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
  37. package/dist/task/source-prepare/types.js +22 -0
  38. package/dist/worker/cli.js +12 -26
  39. package/dist/worker/console/chat/chat-event-store.js +75 -5
  40. package/dist/worker/console/chat/context-panel.js +7 -0
  41. package/dist/worker/console/chat/deferred-turn.js +12 -0
  42. package/dist/worker/console/chat/pi-runtime.js +331 -18
  43. package/dist/worker/console/chat/provider-error.js +98 -0
  44. package/dist/worker/console/chat/routes.js +389 -56
  45. package/dist/worker/console/chat/session-stats.js +179 -0
  46. package/dist/worker/console/chat/session-store.js +93 -63
  47. package/dist/worker/console/chat/todos.js +115 -0
  48. package/dist/worker/console/chat/tool-preview.js +42 -0
  49. package/dist/worker/console/chat/turn-process.js +20 -55
  50. package/dist/worker/console/chat/user-questions.js +266 -0
  51. package/dist/worker/console/chat/workspace-landing.js +79 -0
  52. package/dist/worker/console/console-handoff.js +82 -0
  53. package/dist/worker/console/console-update-and-init.js +115 -0
  54. package/dist/worker/console/console-update-runtime.js +84 -0
  55. package/dist/worker/console/dag-execution-receipt.js +20 -2
  56. package/dist/worker/console/draft-store.js +49 -0
  57. package/dist/worker/console/native-directory-picker.js +13 -0
  58. package/dist/worker/console/operation-store.js +1 -1
  59. package/dist/worker/console/operator-actions.js +133 -7
  60. package/dist/worker/console/operator-surface-health.js +1 -0
  61. package/dist/worker/console/prd-intake-bridge.js +13 -0
  62. package/dist/worker/console/routes.js +312 -1
  63. package/dist/worker/console/security.js +4 -4
  64. package/dist/worker/console/server.js +583 -172
  65. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Br92EGb7.js → abnfDiagram-N423BO3Z-Bs-CDAXM.js} +1 -1
  66. package/dist/worker/console/static/assets/{arc-oulImtQq.js → arc-CAkA3We3.js} +1 -1
  67. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-tyHc6aqO.js → architectureDiagram-T3A2C74G-DEmI_zqr.js} +1 -1
  68. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CY86BSpT.js → blockDiagram-VBNYF7ZC-uDHANebE.js} +1 -1
  69. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BKKsDmNs.js → c4Diagram-5PPSVZJV-Bv97b4_B.js} +1 -1
  70. package/dist/worker/console/static/assets/channel-amxUpk7o.js +1 -0
  71. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CXZdjl3B.js → chunk-2GRJ4B5K-shmQKsFz.js} +1 -1
  72. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-B-aIFDQ5.js → chunk-2Q5K7J3B-BAraNvcN.js} +1 -1
  73. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BDgAW_B2.js → chunk-5RXB4S5H-BirhxUop.js} +1 -1
  74. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DABa4UPR.js → chunk-5VM5RSS4-C9LgUJpR.js} +1 -1
  75. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-aGbysVOx.js → chunk-6Q2QTUOP-DrU-s-t7.js} +1 -1
  76. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-B8MSZDVs.js → chunk-GF5L2VYU-Tx-H_FA2.js} +1 -1
  77. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CpelV73g.js → chunk-JWPE2WC7-BjLtNFrR.js} +1 -1
  78. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BE04ty3z.js → chunk-KBJHAD2P-UNLVwSsy.js} +1 -1
  79. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-BUxTkaaR.js → chunk-RYQCIY6F-CBEUX8XD.js} +1 -1
  80. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CYYBVKfX.js → chunk-XXDRQBXY-WTL7IZOq.js} +1 -1
  81. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +1 -0
  82. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +1 -0
  83. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CEQF_xmQ.js → cose-bilkent-JH36ORCC-CkHG6W3s.js} +1 -1
  84. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CqpxN5IT.js → cynefin-VYW2F7L2-BRzFqTaL.js} +1 -1
  85. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-B3PzanVi.js → cynefinDiagram-MW4NZA55-2_CFp_i7.js} +1 -1
  86. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BSUGCNx1.js → dagre-VZM6K2ZE-BJtsmdkE.js} +1 -1
  87. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CdLevU5T.js → diagram-7IWD3JNH-DNEFFo6w.js} +1 -1
  88. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BjpF_Kok.js → diagram-B4RE2ZJO-B_IWV7eo.js} +1 -1
  89. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-C4I6NJny.js → diagram-LBJQPF4R-DvvRx2dz.js} +1 -1
  90. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C8hecCr5.js → diagram-Q27KOJAE-CsibsWLg.js} +1 -1
  91. package/dist/worker/console/static/assets/{diagram-UB23O5K3-Daod6tOS.js → diagram-UB23O5K3-les_djBQ.js} +1 -1
  92. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BvvmazGv.js → ebnfDiagram-BXEA7PRR-BEm2n7qJ.js} +1 -1
  93. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DeN_vfV4.js → erDiagram-JOGREHBK-BI7vHHVm.js} +1 -1
  94. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-D6dq1t9z.js → flowDiagram-UKHOOZJN-CUFCERrB.js} +1 -1
  95. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DziIdNY4.js → ganttDiagram-PKOTCBZU-wxyLiGWt.js} +1 -1
  96. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dy4s5CXr.js → gitGraphDiagram-DS77QQ5N-Bt897K8Z.js} +1 -1
  97. package/dist/worker/console/static/assets/index-BHhUOTri.css +1 -0
  98. package/dist/worker/console/static/assets/index-DqJbO3-p.js +407 -0
  99. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czv5eYx2.js → infoDiagram-6WML65LV-DYNx_IPv.js} +1 -1
  100. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-Bsk3nFRT.js → ishikawaDiagram-WSZJBQD7-CzpLNEu6.js} +1 -1
  101. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DxGmNj5K.js → journeyDiagram-NVQOT4AX-97f9owNU.js} +1 -1
  102. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CYCFqkO_.js → kanban-definition-27J2QSJJ-BxM9hyBn.js} +1 -1
  103. package/dist/worker/console/static/assets/{linear-B0YzRwrd.js → linear-ByuxcHvp.js} +1 -1
  104. package/dist/worker/console/static/assets/{mermaid.core-t3EZtIGh.js → mermaid.core-DwPGvpCv.js} +5 -5
  105. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BTXpTv0T.js → mindmap-definition-FAOFIHXS-DCoqG9RT.js} +1 -1
  106. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DU8UkKqA.js → pegDiagram-VL7TDLO6-BLSBmBMx.js} +1 -1
  107. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DP4y8ohp.js → pieDiagram-7S7Q4E2Y-B0t_0oJW.js} +1 -1
  108. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-B07vNaYR.js → quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js} +1 -1
  109. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-xDWktb2i.js → railroadDiagram-AXF67PYL-B3jRdATC.js} +1 -1
  110. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BLNMKqeS.js → requirementDiagram-LRYGKXZP-D-cTtXEN.js} +1 -1
  111. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-n0adalxH.js → sankeyDiagram-W5VNT64P-B25igJRE.js} +1 -1
  112. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BbnS-2DL.js → sequenceDiagram-SI44F4Z6-jW_lM1_D.js} +1 -1
  113. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4uPpCLu.js → sizeCapture-X5ZJPWSS-CrzTgsHE.js} +1 -1
  114. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA--MMbB0fX.js → stateDiagram-OKZ733FA-B01MvlqE.js} +1 -1
  115. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +1 -0
  116. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BXUgp2QY.js → swimlanes-SLNWSIFB-YcF12FiU.js} +2 -2
  117. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +8 -0
  118. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CsaMxki7.js → timeline-definition-Z64GVDOM-ClnRcg2s.js} +1 -1
  119. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-C2IuG9iH.js → vennDiagram-T6HMQDX7-B6gr_z-q.js} +1 -1
  120. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CmEWOXCW.js → wardleyDiagram-T6FBY63Y-CvSgguGH.js} +1 -1
  121. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-D_9LE0MG.js → xychartDiagram-ELKLHX3M-23OtSZCb.js} +1 -1
  122. package/dist/worker/console/static/index.html +2 -2
  123. package/dist/worker/console/static-src/app/console-types.js +68 -4
  124. package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
  125. package/dist/worker/console/static-src/app/useOperatorActions.js +41 -6
  126. package/dist/worker/console/static-src/app/useRecoveryConsole.js +10 -11
  127. package/dist/worker/console/static-src/app/useRunProgress.js +2 -1
  128. package/dist/worker/console/static-src/app/useTaskWizard.js +74 -2
  129. package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
  130. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +141 -8
  131. package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
  132. package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
  133. package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
  134. package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
  135. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  136. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
  137. package/dist/worker/console/static-src/operator-chat/turn-submission.js +3 -2
  138. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
  139. package/dist/worker/console/static-src/operator-chat/useChatStream.js +159 -44
  140. package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
  141. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
  142. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  143. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
  144. package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
  145. package/dist/worker/console/static-src/shell/workspace-route.js +339 -0
  146. package/dist/worker/console/workspace-context.js +234 -0
  147. package/dist/worker/console/workspace-registry.js +214 -0
  148. package/dist/worker/observability/init-runtime-activity.js +26 -0
  149. package/dist/worker/observe/node-input.js +88 -2
  150. package/dist/worker/observe/routes.js +188 -0
  151. package/dist/worker/observe/server.js +4 -0
  152. package/dist/worker/observe/static/api.js +29 -9
  153. package/dist/worker/observe/static/app.js +18 -70
  154. package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
  155. package/dist/worker/observe/static/dag-node-purpose.js +203 -0
  156. package/dist/worker/observe/static/format.js +37 -0
  157. package/dist/worker/observe/static/index.html +6 -1
  158. package/dist/worker/observe/static/inspect-workspace.js +307 -0
  159. package/dist/worker/observe/static/operator-chrome.css +145 -20
  160. package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
  161. package/dist/worker/observe/static/operator-chrome.js +330 -100
  162. package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
  163. package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
  164. package/dist/worker/observe/static/router.d.ts +46 -0
  165. package/dist/worker/observe/static/router.js +25 -1
  166. package/dist/worker/observe/static/state.d.ts +50 -0
  167. package/dist/worker/observe/static/state.js +174 -2
  168. package/dist/worker/observe/static/styles.css +357 -35
  169. package/dist/worker/observe/static/views/dag-graph.js +4 -2
  170. package/dist/worker/observe/static/views/dag-inspector.js +533 -79
  171. package/dist/worker/observe/static/views/dag.d.ts +13 -0
  172. package/dist/worker/observe/static/views/dag.js +6 -4
  173. package/dist/worker/observe/static/views/dashboard.js +6 -4
  174. package/dist/worker/observe/static/views/night.js +2 -2
  175. package/dist/worker/observe/static/views/pool.js +1 -0
  176. package/dist/worker/observe/static/views/run.js +3 -1
  177. package/dist/worker/observe/static/views/task.js +56 -1
  178. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +186 -8
  179. package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
  180. package/dist/workflows/dag/backend-test-pytest-collection.js +168 -16
  181. package/dist/workflows/dag/backend-test-result-contract.js +4 -0
  182. package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
  183. package/dist/workflows/dag/backend-test-writer-completeness.js +28 -17
  184. package/dist/workflows/dag/dynamic-runtime/map.js +1 -0
  185. package/dist/workflows/dag/frontend-implementation-contract.js +88 -2
  186. package/dist/workflows/dag/frontend-plan-render.js +24 -0
  187. package/dist/workflows/dag/frontend-prewrite-gate.js +197 -0
  188. package/dist/workflows/dag/frontend-review-context.js +92 -10
  189. package/dist/workflows/dag/init-hybrid.js +239 -52
  190. package/dist/workflows/dag/interrupt-request.js +3 -3
  191. package/dist/workflows/dag/lifecycle.js +3 -3
  192. package/dist/workflows/dag/node-execution.js +45 -0
  193. package/dist/workflows/dag/types.js +62 -12
  194. package/docs/README.md +2 -0
  195. package/docs/architecture/evolution.md +45 -0
  196. package/docs/init-surface.manifest.json +1 -0
  197. package/docs/operations/local-development-environment.md +21 -0
  198. package/docs/templates/README.md +1 -1
  199. package/docs/templates/agent-dag.schema.json +18 -0
  200. package/docs/templates/backend-test-dag.json +267 -238
  201. package/docs/templates/frontend-implementation-contract.schema.json +681 -24
  202. package/harness.json +3 -3
  203. package/package.json +3 -1
  204. package/skills/loop-agent/references/hybrid-dag.md +7 -0
  205. package/dist/worker/console/static/assets/channel-TY36zt-i.js +0 -1
  206. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Dk7QCChM.js +0 -1
  207. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Dk7QCChM.js +0 -1
  208. package/dist/worker/console/static/assets/index-C3GgHg-g.css +0 -1
  209. package/dist/worker/console/static/assets/index-Ci8ksvBm.js +0 -406
  210. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqOigMQ4.js +0 -1
  211. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Caxz9UA2.js +0 -8
  212. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  213. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
@@ -11,7 +11,11 @@ import { buildGlobalSnapshot, clampDagRunHistoryPageSize, clampTaskRunHistoryLim
11
11
  import { loadDagRunExecutionTrajectory } from "../observability/dag-execution-trajectory.js";
12
12
  import { resolveDagTaskSourcePath } from "../../task/dag-source-paths.js";
13
13
  import { dagSourceBindingSchema } from "../../workflows/dag/types.js";
14
+ import { getTaskPaths } from "../../task/runtime.js";
15
+ import { classifyLedgerGate, computeLedgerInputDigest, LedgerParseError, parseLedgerJson, recoverLedgerInputContract, summarizeLedger, } from "../../task/source-prepare/ledger.js";
14
16
  import { getTaskPoolRoot } from "../pool/run-store.js";
17
+ import { resolveDagRunArtifact, } from "./dag-run-artifacts.js";
18
+ import { frontendPrewriteResultV1Schema } from "../../workflows/dag/frontend-prewrite-gate.js";
15
19
  import { isAllowedArtifactTextPath, resolveArtifactPath, resolveRepoFilePreview, toRepoRelativeArtifactPath, } from "./paths.js";
16
20
  import { extractSpecEvidence, extractSpecReadContent, } from "./spec-evidence.js";
17
21
  import { buildDagNodeInput } from "./node-input.js";
@@ -70,6 +74,15 @@ export const ROUTES = [
70
74
  pattern: /^\/api\/tasks\/([^/]+)\/runs$/,
71
75
  handler: handleTaskRuns,
72
76
  },
77
+ /**
78
+ * Read-only source fidelity ledger safe summary (AC-009 / AC-FIDELITY-009):
79
+ * 只返回计数/状态/failure code,不泄漏敏感原文。
80
+ */
81
+ {
82
+ method: "GET",
83
+ pattern: /^\/api\/tasks\/([^/]+)\/ledger$/,
84
+ handler: handleTaskLedger,
85
+ },
73
86
  {
74
87
  method: "GET",
75
88
  pattern: /^\/api\/tasks\/([^/]+)$/,
@@ -116,6 +129,16 @@ export const ROUTES = [
116
129
  pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/spec-evidence\/file$/,
117
130
  handler: handleDagNodeSpecEvidenceFile,
118
131
  },
132
+ // DAG run 专属只读 prewrite 结果(AC-006 / AC-BLOCK-006):唯一读路径为
133
+ // run-owned contracts/frontend-prewrite-result.json(resolveDagRunArtifact 四类
134
+ // fail-closed),不扩大 isAllowedArtifactTextPath 通用 allowlist;响应以
135
+ // frontendPrewriteResultV1Schema(.strict())解析,Inspector 不再做
136
+ // /api/artifacts 预览 → JSON.stringify → JSON.parse 双编码。
137
+ {
138
+ method: "GET",
139
+ pattern: /^\/api\/dag-runs\/([^/]+)\/prewrite-result$/,
140
+ handler: handleDagRunPrewriteResult,
141
+ },
119
142
  {
120
143
  method: "GET",
121
144
  pattern: /^\/api\/dag-runs\/([^/]+)$/,
@@ -448,6 +471,117 @@ async function handleTaskRuns(_req, res, match, ctx) {
448
471
  });
449
472
  sendJson(res, 200, page);
450
473
  }
474
+ /**
475
+ * GET /api/tasks/:id/ledger — 只读 ledger 安全摘要。任务存在但无 ledger 时返回
476
+ * `{ present: false }`(空态,不伪造计数);任务不存在返回 404;ledger 解析失败
477
+ * 返回带稳定 SOURCE_FIDELITY_* 错误码的 `{ present: false }`(AC-HARD-003:与
478
+ * parseLedgerJson 同一稳定码,不泄漏原文)。binding v2 标记 + ledgerSha256 +
479
+ * inputDigest 仅在真实 v2 绑定(非空 canonical requirement 集)时展示(AC-HARD-004:
480
+ * v1+optional 冒充 v2 不标记);inputDigest 与当前权威源输入不一致时置 stale。
481
+ */
482
+ async function handleTaskLedger(_req, res, match, ctx) {
483
+ const taskId = match.params.id;
484
+ if (!isSafeObservabilityIdentifier(taskId)) {
485
+ sendJson(res, 400, { error: "Invalid task identifier" });
486
+ return;
487
+ }
488
+ const taskPaths = getTaskPaths(ctx.repoRoot, taskId);
489
+ try {
490
+ await stat(taskPaths.taskDir);
491
+ }
492
+ catch {
493
+ sendJson(res, 404, { error: "Task not found" });
494
+ return;
495
+ }
496
+ const ledgerPath = path.join(taskPaths.sourceDir, "requirement-ledger.json");
497
+ let raw;
498
+ try {
499
+ raw = await readFile(ledgerPath, "utf8");
500
+ }
501
+ catch (error) {
502
+ if (error.code === "ENOENT") {
503
+ sendJson(res, 200, { present: false, taskId });
504
+ return;
505
+ }
506
+ sendJson(res, 200, {
507
+ present: false,
508
+ taskId,
509
+ error: "ledger-unreadable",
510
+ });
511
+ return;
512
+ }
513
+ let ledger;
514
+ try {
515
+ ledger = parseLedgerJson(raw);
516
+ }
517
+ catch (error) {
518
+ // AC-HARD-003:解析失败分支使用与 parseLedgerJson 相同的稳定 SOURCE_FIDELITY_*
519
+ // 错误码(结构化可消费),而非裸字符串/裸 Error。
520
+ const code = error instanceof LedgerParseError
521
+ ? error.code
522
+ : "SOURCE_FIDELITY_LEDGER_STALE";
523
+ sendJson(res, 200, {
524
+ present: false,
525
+ taskId,
526
+ error: code,
527
+ });
528
+ return;
529
+ }
530
+ const classification = classifyLedgerGate(ledger);
531
+ const summary = summarizeLedger(ledger, {
532
+ gateStatus: classification.ok ? "pass" : "fail",
533
+ failureCode: classification.ok ? null : classification.code,
534
+ });
535
+ // v2 binding 语义(AC-HARD-004):仅在真实 v2 绑定(canonical requirement 非空,
536
+ // 具备 requirement→fragment 映射证据)时标记 bindingSchemaVersion=2 并透传
537
+ // ledgerSha256/inputDigest;空 canonical 的 ledger 是退化/伪造 v2,不标记。
538
+ const contract = recoverLedgerInputContract(ledger);
539
+ const realV2Binding = contract.canonicalRequirements.length > 0;
540
+ let stale = false;
541
+ if (realV2Binding) {
542
+ // 当前权威源输入:ledger.sourcePaths(repo-root relative POSIX)→ 当前字节;
543
+ // 缺失/清空的权威源视为 stale(fail-closed,与 init-hybrid 语义一致)。
544
+ const currentByPath = new Map();
545
+ for (const sourcePath of contract.sourcePaths) {
546
+ try {
547
+ currentByPath.set(sourcePath, await readFile(path.resolve(ctx.repoRoot, sourcePath), "utf8"));
548
+ }
549
+ catch {
550
+ // 权威源缺失:保持缺失,expectedInputDigest 不一致 → stale。
551
+ }
552
+ }
553
+ const expectedDigest = computeLedgerInputDigest({
554
+ sourceDocuments: contract.sourcePaths.map((sourcePath) => ({
555
+ path: sourcePath,
556
+ content: currentByPath.get(sourcePath) ?? "",
557
+ })),
558
+ canonicalRequirements: contract.canonicalRequirements,
559
+ });
560
+ stale = ledger.inputDigest !== expectedDigest;
561
+ }
562
+ const ledgerSha256 = createHash("sha256").update(raw).digest("hex");
563
+ sendJson(res, 200, {
564
+ present: true,
565
+ taskId,
566
+ ...(realV2Binding
567
+ ? {
568
+ bindingSchemaVersion: 2,
569
+ ledgerSha256,
570
+ inputDigest: ledger.inputDigest,
571
+ stale,
572
+ }
573
+ : {}),
574
+ fragmentCount: summary.fragmentCount,
575
+ canonicalRequirementCount: summary.canonicalRequirementCount,
576
+ contextOnlyCount: summary.contextOnlyCount,
577
+ duplicateCount: summary.duplicateCount,
578
+ unresolvedCount: summary.unresolvedCount,
579
+ conflictCount: summary.conflictCount,
580
+ gateStatus: summary.gateStatus,
581
+ failureCode: summary.failureCode,
582
+ exclusions: summary.exclusions,
583
+ });
584
+ }
451
585
  function ambiguousTaskBody(taskId, candidates) {
452
586
  return {
453
587
  error: "Task identity ambiguous",
@@ -535,6 +669,60 @@ async function handleDagRuns(_req, res, match, ctx) {
535
669
  const body = listDagRunHistoryFromSnapshot(snapshot, { page, pageSize });
536
670
  sendJson(res, 200, body);
537
671
  }
672
+ /**
673
+ * GET /api/dag-runs/:dagRunId/prewrite-result(AC-006 / AC-BLOCK-006)
674
+ * 只读 run-owned contracts/frontend-prewrite-result.json:resolveDagRunArtifact 是
675
+ * 唯一读路径(active/paused/completed 生命周期;invalid-id / unsafe-path /
676
+ * not-found / ambiguous 四类 fail-closed)。响应以 frontendPrewriteResultV1Schema
677
+ * (.strict())解析,拒绝 envelope 之外的字段;不扩大通用 artifact allowlist。
678
+ */
679
+ async function handleDagRunPrewriteResult(_req, res, match, ctx) {
680
+ const dagRunId = match.params.id;
681
+ const resolved = resolveDagRunArtifact(ctx.repoRoot, dagRunId, [
682
+ "contracts",
683
+ "frontend-prewrite-result.json",
684
+ ]);
685
+ if (!resolved.ok) {
686
+ if (resolved.reason === "invalid-id") {
687
+ sendJson(res, 400, { error: "Invalid dag run identifier" });
688
+ return;
689
+ }
690
+ if (resolved.reason === "ambiguous") {
691
+ sendJson(res, 409, { error: "DAG run identity ambiguous" });
692
+ return;
693
+ }
694
+ sendJson(res, 404, { error: "Prewrite result not found" });
695
+ return;
696
+ }
697
+ let raw;
698
+ try {
699
+ raw = await readFile(resolved.result.absolutePath, "utf8");
700
+ }
701
+ catch {
702
+ sendJson(res, 404, { error: "Prewrite result not found" });
703
+ return;
704
+ }
705
+ let parsed;
706
+ try {
707
+ parsed = JSON.parse(raw);
708
+ }
709
+ catch {
710
+ sendJson(res, 500, {
711
+ error: "Prewrite result is not valid JSON",
712
+ reason: "prewrite-result-unparseable",
713
+ });
714
+ return;
715
+ }
716
+ const envelope = frontendPrewriteResultV1Schema.safeParse(parsed);
717
+ if (!envelope.success) {
718
+ sendJson(res, 500, {
719
+ error: "Prewrite result is not a valid frontend-prewrite-result-v1 envelope",
720
+ reason: "prewrite-result-schema-invalid",
721
+ });
722
+ return;
723
+ }
724
+ sendJson(res, 200, envelope.data);
725
+ }
538
726
  export async function handleDagRunById(_req, res, match, ctx) {
539
727
  const snapshot = await getSnapshot(ctx);
540
728
  const dagRun = snapshot.dagRuns.find((d) => d.dagRunId === match.params.id);
@@ -83,6 +83,10 @@ export function createObserveServer(options) {
83
83
  url: `http://${host}:${addr.port}`,
84
84
  close: () => new Promise((closeResolve, closeReject) => {
85
85
  server.close((err) => (err ? closeReject(err) : closeResolve()));
86
+ // Explicit shutdown must not wait for the 65s keep-alive window.
87
+ // This also closes active SSE clients so process/test teardown is
88
+ // deterministic instead of timing out behind pooled connections.
89
+ server.closeAllConnections();
86
90
  }),
87
91
  });
88
92
  });
@@ -1,8 +1,12 @@
1
1
  /** Fetch and artifact helpers (Observe UI R5). */
2
+ import {
3
+ fetchWithWorkspaceScope,
4
+ workspaceScope,
5
+ } from "./state.js";
2
6
 
3
7
  export async function fetchJson(url) {
4
8
  try {
5
- const res = await fetch(url);
9
+ const res = await fetchWithWorkspaceScope(url);
6
10
  if (!res.ok) return null;
7
11
  return await res.json();
8
12
  } catch {
@@ -18,7 +22,10 @@ export async function fetchJson(url) {
18
22
  export async function fetchJsonResult(url, options = {}) {
19
23
  const signal = options?.signal;
20
24
  try {
21
- const res = await fetch(url, signal ? { signal } : undefined);
25
+ const res = await fetchWithWorkspaceScope(
26
+ url,
27
+ signal ? { signal } : undefined,
28
+ );
22
29
  let body = null;
23
30
  try {
24
31
  body = await res.json();
@@ -92,7 +99,7 @@ export async function fetchDagNodeProcess(runId, nodeId, key) {
92
99
  */
93
100
  export async function fetchSpecEvidenceFile(runId, nodeId, source, relPath) {
94
101
  const url = `/api/dag-runs/${encodeURIComponent(runId)}/nodes/${encodeURIComponent(nodeId)}/spec-evidence/file?source=${encodeURIComponent(source)}&path=${encodeURIComponent(relPath)}`;
95
- const res = await fetch(url);
102
+ const res = await fetchWithWorkspaceScope(url);
96
103
  let body = null;
97
104
  try {
98
105
  body = await res.json();
@@ -116,8 +123,9 @@ export function artifactUrl(artifactPath) {
116
123
  * Experimental (prompt-override rerun only): POST one allowlisted operator
117
124
  * action through the Console mutation gate using the SCOPED inspect token
118
125
  * (window.__INSPECT_MUTATION_TOKEN__). The server injects it into /inspect
119
- * HTML only when HARNESS_DAG_RERUN_PROMPT_OVERRIDE is enabled, and it only
120
- * authorizes dagRerunPlan / dagRerun — never general Console mutations.
126
+ * HTML by default; HARNESS_DAG_RERUN_PROMPT_OVERRIDE=0|false|off disables
127
+ * injection. The token only authorizes dagRerunPlan / dagRerun — never general
128
+ * Console mutations.
121
129
  * Absent token → the call is not attempted.
122
130
  */
123
131
  export async function postOperatorAction(action, actionParams = {}) {
@@ -129,15 +137,28 @@ export async function postOperatorAction(action, actionParams = {}) {
129
137
  body: { error: { code: "PROMPT_OVERRIDE_DISABLED", message: "提示词重启未启用(服务端未注入确认 token)" } },
130
138
  };
131
139
  }
140
+ const workspaceId = workspaceScope();
141
+ if (!workspaceId) {
142
+ return {
143
+ ok: false,
144
+ status: 400,
145
+ body: {
146
+ error: {
147
+ code: "WORKSPACE_REQUIRED",
148
+ message: "workspaceId is required",
149
+ },
150
+ },
151
+ };
152
+ }
132
153
  try {
133
- const res = await fetch("/api/operator/v1/operations", {
154
+ const res = await fetchWithWorkspaceScope("/api/operator/v1/operations", {
134
155
  method: "POST",
135
156
  credentials: "same-origin",
136
157
  headers: {
137
158
  "Content-Type": "application/json",
138
159
  "x-loop-console-confirmation": token,
139
160
  },
140
- body: JSON.stringify({ action, actionParams }),
161
+ body: JSON.stringify({ workspaceId, action, actionParams }),
141
162
  });
142
163
  let body = null;
143
164
  try {
@@ -154,7 +175,7 @@ export async function postOperatorAction(action, actionParams = {}) {
154
175
  /** Poll a Console operation snapshot (read-only GET). */
155
176
  export async function fetchOperatorOperation(operationId) {
156
177
  try {
157
- const res = await fetch(
178
+ const res = await fetchWithWorkspaceScope(
158
179
  `/api/operator/v1/operations/${encodeURIComponent(operationId)}`,
159
180
  { credentials: "same-origin" },
160
181
  );
@@ -180,4 +201,3 @@ export function parseArtifactPreviewResponse(text) {
180
201
  truncated: parsed.truncated === true,
181
202
  };
182
203
  }
183
-
@@ -1,9 +1,8 @@
1
- /** Observe UI bootstrap: route wiring, listeners, compatibility re-exports. */
1
+ /** Observe UI bootstrap: standalone compatibility entry + re-exports. */
2
2
  import {
3
3
  parseHashRoute,
4
4
  parseHashRouteWithFilters,
5
5
  buildHashPath,
6
- parseRoute,
7
6
  } from "./router.js";
8
7
  import { parseArtifactPreviewResponse } from "./api.js";
9
8
  import {
@@ -15,7 +14,6 @@ import {
15
14
  } from "./relations.js";
16
15
  import { copyText, renderRecommendedCommand } from "./copy.js";
17
16
  import {
18
- isPageVisible,
19
17
  detailPollDelay,
20
18
  updateDagGraphViewportState,
21
19
  captureDagGraphViewportState,
@@ -25,11 +23,9 @@ import {
25
23
  shouldReuseDagDetailShell,
26
24
  shouldPreserveDiagnosticsOpen,
27
25
  isInteractiveGestureActive,
28
- stopTimers,
29
26
  renderViewState,
30
27
  mountViewState,
31
28
  viewStateDescriptor,
32
- uiState,
33
29
  } from "./state.js";
34
30
  import { sortPoolTasks, poolStatusCounts } from "./format-pool.js";
35
31
  import {
@@ -45,34 +41,22 @@ import {
45
41
  dashboardVisibleFeatures,
46
42
  findDagForRun,
47
43
  } from "./dag-model.js";
48
-
49
- import { renderFailures } from "./views/failures.js";
50
- import { startPoolView } from "./views/pool.js";
51
- import { startDashboardPolling } from "./views/dashboard.js";
52
- import { startDagsView } from "./views/dags.js";
53
- import { renderFeatureDetail } from "./views/feature.js";
54
- import { renderBatch } from "./views/batch.js";
55
- import { startTaskPolling } from "./views/task.js";
56
- import { startRunPolling } from "./views/run.js";
57
- import { startDagPolling } from "./views/dag.js";
58
44
  import { renderDagGraph } from "./views/dag-graph.js";
59
- import { renderNightJobs } from "./views/night.js";
60
45
  import {
61
46
  selectDagNode,
62
47
  closeDagInspector,
63
- closeDagInspectorOnOutsideClick,
64
48
  renderDagInspector,
65
49
  } from "./views/dag-inspector.js";
66
50
  import {
67
51
  mergeSessionEvents,
68
52
  renderSessionTimeline,
69
53
  } from "./views/session-timeline.js";
70
- import { mountOperatorChrome } from "./operator-chrome.js";
54
+ import {
55
+ route,
56
+ startStandaloneInspect,
57
+ tryCanonicalizeStandaloneInspect,
58
+ } from "./inspect-workspace.js";
71
59
 
72
- // Compatibility re-exports for poll-stable UI helpers (owned by state.js / views):
73
- // reuse-dag-detail-shell, reuse-dag-graph-wrap, existingGraphWrap?.parentNode === ranksEl,
74
- // existingDetails?.parentNode === metaEl, let pre = existingPre, computeFollowLatest,
75
- // updateDagOutputViewportState, isInteractiveGestureActive.
76
60
  export {
77
61
  detailPollDelay,
78
62
  parseMarkdownBlocks,
@@ -114,56 +98,20 @@ export {
114
98
  renderDagInspector,
115
99
  renderSessionTimeline,
116
100
  mergeSessionEvents,
101
+ route,
102
+ tryCanonicalizeStandaloneInspect,
117
103
  };
118
104
 
119
- function route() {
120
- stopTimers();
121
- const r = parseRoute();
122
- if (r.view === "dashboard") {
123
- startDashboardPolling(r.scrollTo);
124
- } else if (r.view === "dags") {
125
- void startDagsView();
126
- } else if (r.view === "batch") {
127
- void renderBatch(r.batchRunId);
128
- } else if (r.view === "run") {
129
- startRunPolling(r.workerRunId);
130
- } else if (r.view === "dag") {
131
- startDagPolling(r.dagRunId);
132
- } else if (r.view === "failures") {
133
- void renderFailures();
134
- } else if (r.view === "pool") {
135
- startPoolView();
136
- } else if (r.view === "night") {
137
- void renderNightJobs(r.scheduleId);
138
- } else if (r.view === "task") {
139
- startTaskPolling(r.featureId, r.taskId);
140
- } else if (r.view === "feature") {
141
- void renderFeatureDetail(r.featureId);
142
- }
143
- }
144
-
145
105
  if (typeof window !== "undefined") {
146
- // 先同步挂载共享壳,再启动路由;首个 snapshot 的新鲜度不会因异步挂载丢失。
147
- const chromeHost = document.getElementById("operator-chrome");
148
- if (chromeHost) {
149
- mountOperatorChrome(chromeHost, {
150
- mode: "inspect",
151
- liveLocation: true,
152
- healthUrl: "/api/health",
153
- readinessTitle: "Pi 状态(只读展示)。模型与认证管理在操作侧,点击进入。",
154
- });
106
+ // Compatibility listeners remain owned by inspect-workspace.js:
107
+ // window.addEventListener("hashchange", route)
108
+ // window.addEventListener("load", route)
109
+ // document.addEventListener("pointerdown", closeDagInspectorOnOutsideClick)
110
+ // document.addEventListener("visibilitychange", ...)
111
+ // reuse-dag-detail-shell, reuse-dag-graph-wrap, existingGraphWrap?.parentNode === ranksEl,
112
+ // existingDetails?.parentNode === metaEl, let pre = existingPre, computeFollowLatest,
113
+ // updateDagOutputViewportState, isInteractiveGestureActive.
114
+ if (!tryCanonicalizeStandaloneInspect(window.location)) {
115
+ startStandaloneInspect();
155
116
  }
156
- window.addEventListener("hashchange", route);
157
- window.addEventListener("load", route);
158
- document.addEventListener("pointerdown", closeDagInspectorOnOutsideClick);
159
- document.addEventListener("visibilitychange", () => {
160
- if (!isPageVisible()) return;
161
- const r = parseRoute();
162
- if (r.view === "dashboard") startDashboardPolling();
163
- // History page is manual-refresh only (no 5s dashboard polling).
164
- if (r.view === "run") startRunPolling(r.workerRunId, false);
165
- if (r.view === "dag") startDagPolling(r.dagRunId, false);
166
- if (r.view === "pool" && uiState.poolAutoRefresh) startPoolView();
167
- if (r.view === "task") startTaskPolling(r.featureId, r.taskId);
168
- });
169
117
  }
@@ -0,0 +1,6 @@
1
+ export const NODE_PURPOSE_BY_ID: Record<string, string>;
2
+
3
+ export function describeDagNodePurpose(
4
+ nodeId: string | null | undefined,
5
+ node?: { executor?: string | null; label?: string | null },
6
+ ): string;