@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.
- package/CHANGELOG.md +111 -45
- package/dist/application/dag/run-dag.js +8 -2
- package/dist/application/evaluation/budget.js +19 -1
- package/dist/application/task-lifecycle/advance.js +26 -8
- package/dist/application/task-lifecycle/observe.js +43 -29
- package/dist/application/task-lifecycle/plan-transitions.js +5 -4
- package/dist/build-stamp.json +3 -3
- package/dist/cli/program.js +1 -1
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/task-source-prepare.js +3 -1
- package/dist/executors/dag-pi-executor.js +2584 -600
- package/dist/executors/pi-executor.js +22 -1
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/pi-sdk-executor.js +140 -39
- package/dist/executors/shell-executor.js +135 -55
- package/dist/shared/dag-failure-category.js +6 -0
- package/dist/shared/frontend-execution-policy.js +23 -0
- package/dist/task/config-types.js +4 -0
- package/dist/task/contract/apply.js +36 -2
- package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
- package/dist/task/source-prepare/ledger-review.js +6 -9
- package/dist/task/source-prepare/parse-intent.js +7 -0
- package/dist/task/source-prepare/semantic-intake.js +16 -26
- package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
- package/dist/task/source-references.js +48 -23
- package/dist/worker/console/chat/assistant-content.js +23 -2
- package/dist/worker/console/chat/browser-policy.js +143 -0
- package/dist/worker/console/chat/browser-routes.js +148 -0
- package/dist/worker/console/chat/chat-event-store.js +4 -2
- package/dist/worker/console/chat/explore-tools.js +13 -0
- package/dist/worker/console/chat/pi-runtime.js +173 -94
- package/dist/worker/console/chat/resource-loader.js +4 -1
- package/dist/worker/console/chat/routes.js +208 -66
- package/dist/worker/console/chat/scm-routes.js +217 -0
- package/dist/worker/console/chat/scm-service.js +283 -0
- package/dist/worker/console/chat/scm-tools.js +111 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +78 -11
- package/dist/worker/console/chat/session-catalog.js +32 -0
- package/dist/worker/console/chat/session-mode-view.js +48 -0
- package/dist/worker/console/chat/session-mode.js +218 -0
- package/dist/worker/console/chat/session-store.js +27 -7
- package/dist/worker/console/chat/shortcuts.js +6 -0
- package/dist/worker/console/chat/subagents/agent-tool.js +62 -0
- package/dist/worker/console/chat/subagents/explore-agent.js +169 -0
- package/dist/worker/console/chat/subagents/index.js +5 -0
- package/dist/worker/console/chat/subagents/orchestrator.js +273 -0
- package/dist/worker/console/chat/subagents/tool-policy.js +53 -0
- package/dist/worker/console/chat/subagents/types.js +13 -0
- package/dist/worker/console/chat/terminal-routes.js +216 -0
- package/dist/worker/console/chat/terminal-sessions.js +284 -0
- package/dist/worker/console/chat/terminal-tools.js +199 -0
- package/dist/worker/console/chat/tool-preview.js +21 -0
- package/dist/worker/console/chat/tools.js +13 -1
- package/dist/worker/console/chat/turn-process.js +1 -0
- package/dist/worker/console/interview/tools.js +1 -0
- package/dist/worker/console/server.js +2 -28
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-C9eI7nEo.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-VJWsxWhB.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-BGcyiMSo.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-BScnFwyi.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Bl9_BsLi.js} +1 -1
- package/dist/worker/console/static/assets/channel-B6sQYuOE.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-F1uKxiUt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-rSRMynVu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-DAsJm1WD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-DQIdVI0a.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-4A_8rJ-Q.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-BHnT-vjh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-Rq7QKBkn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BXh-AI2u.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-BblcLy8m.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-zHZmVnB6.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Z6s5QoeI.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Z6s5QoeI.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-Cmwz0Wi4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-Dq76MHQU.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-zPahV1fM.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-DQJ3xPc_.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BwX7u6fv.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CodnDCco.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-gAjtWkxG.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-CGCqz0cS.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-RHwYEywd.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-B6seb_p_.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-CmwCKYe8.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-BfNsaZ0V.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-zmYgi_Z3.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-BDEzzSKF.js} +1 -1
- package/dist/worker/console/static/assets/index-DgenUAfc.js +468 -0
- package/dist/worker/console/static/assets/index-aF4u-Y__.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-DeSz8l42.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-B1LV29aH.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-CCw4W2oU.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BozaMoRJ.js} +1 -1
- package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-G1JR40SX.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-CCgPF0oA.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-CEqnCHnD.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-DpmQPKuI.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-BP501AxR.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-D1dBhl_A.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-DgZDS5pY.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-BzB3pyIq.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-B9ZprFFP.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CC2-_XrA.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-UVtAUqgB.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-CWcS5JYG.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BooX8u1Q.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-C4p-dn06.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D64_eqqE.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-Dnz52OHX.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-CzTE0Nfu.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-NAR9cjQQ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-DVMk8413.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/chat-view-types.js +1 -1
- package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +142 -7
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +44 -0
- package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
- package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +33 -18
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +48 -16
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +170 -7
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -7
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
- package/dist/worker/console/workspace-context.js +15 -1
- package/dist/worker/observe/node-transparency.js +81 -72
- package/dist/worker/observe/routes.js +20 -1
- package/dist/worker/observe/static/constants.js +22 -22
- package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
- package/dist/worker/observe/static/dag-history-labels.js +1 -0
- package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
- package/dist/worker/observe/static/dag-inspector-humanize.js +342 -0
- package/dist/worker/observe/static/dag-node-purpose.js +10 -10
- package/dist/worker/observe/static/dom.js +20 -1
- package/dist/worker/observe/static/format-pool.d.ts +2 -0
- package/dist/worker/observe/static/format-pool.js +6 -0
- package/dist/worker/observe/static/format.js +7 -0
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/inspect-workspace.js +34 -7
- package/dist/worker/observe/static/inspector-submission.js +32 -0
- package/dist/worker/observe/static/kpi.js +1 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +7 -5
- package/dist/worker/observe/static/router.js +13 -0
- package/dist/worker/observe/static/run-processing.js +2 -0
- package/dist/worker/observe/static/shell-chrome.js +36 -3
- package/dist/worker/observe/static/state.js +35 -2
- package/dist/worker/observe/static/styles.css +431 -39
- package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
- package/dist/worker/observe/static/task-failure-labels.js +67 -0
- package/dist/worker/observe/static/task-history.js +12 -0
- package/dist/worker/observe/static/views/batch.js +6 -13
- package/dist/worker/observe/static/views/dag-graph.js +51 -4
- package/dist/worker/observe/static/views/dag-inspector.js +1069 -426
- package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
- package/dist/worker/observe/static/views/dag.d.ts +6 -0
- package/dist/worker/observe/static/views/dag.js +64 -22
- package/dist/worker/observe/static/views/dags.js +2 -0
- package/dist/worker/observe/static/views/dashboard.js +21 -12
- package/dist/worker/observe/static/views/failures.js +21 -11
- package/dist/worker/observe/static/views/feature.js +11 -29
- package/dist/worker/observe/static/views/pool.js +37 -28
- package/dist/worker/observe/static/views/run.js +48 -5
- package/dist/worker/observe/static/views/session-timeline.js +194 -243
- package/dist/worker/observe/static/views/task.js +81 -62
- package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
- package/dist/workflows/dag/budget-enforcement.js +53 -3
- package/dist/workflows/dag/dag-retry-schema.js +3 -0
- package/dist/workflows/dag/frontend-capacity.js +9 -0
- package/dist/workflows/dag/frontend-contract-facts.js +130 -0
- package/dist/workflows/dag/frontend-design-policy.js +103 -17
- package/dist/workflows/dag/frontend-durable-tools.js +193 -0
- package/dist/workflows/dag/frontend-execution-groups.js +24 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +423 -41
- package/dist/workflows/dag/frontend-input-projection.js +76 -0
- package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
- package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
- package/dist/workflows/dag/frontend-plan-render.js +21 -3
- package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
- package/dist/workflows/dag/frontend-recovery-controller.js +32 -8
- package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +4 -1
- package/dist/workflows/dag/frontend-recovery-run.js +146 -16
- package/dist/workflows/dag/frontend-review-scopes.js +139 -0
- package/dist/workflows/dag/frontend-risk.js +2 -0
- package/dist/workflows/dag/frontend-session-budget.js +249 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +37 -3
- package/dist/workflows/dag/frontend-shape-facts.js +12 -2
- package/dist/workflows/dag/frontend-shape.js +46 -7
- package/dist/workflows/dag/frontend-test-execution-evidence.js +49 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +14 -0
- package/dist/workflows/dag/frontend-verification-trace.js +38 -4
- package/dist/workflows/dag/frontend-writer-admission.js +2 -2
- package/dist/workflows/dag/init-hybrid.js +70 -35
- package/dist/workflows/dag/node-execution.js +225 -226
- package/dist/workflows/dag/prompt.js +4 -0
- package/dist/workflows/dag/recovery-lease.js +106 -16
- package/dist/workflows/dag/rerun-feedback.js +1 -0
- package/dist/workflows/dag/rerun-plan.js +96 -4
- package/dist/workflows/dag/rerun-run.js +28 -6
- package/dist/workflows/dag/rerun-task.js +221 -18
- package/dist/workflows/dag/retry-policy.js +27 -10
- package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
- package/dist/workflows/dag/runner.js +246 -19
- package/dist/workflows/dag/structured-output-repair.js +4 -1
- package/dist/workflows/dag/types.js +10 -3
- package/dist/workflows/dag/validate.js +10 -8
- package/dist/workflows/dag/workspace-checkpoint.js +66 -0
- package/docs/operations/README.md +2 -0
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +6 -4
- package/docs/templates/frontend-design-contract.md +4 -4
- package/docs/templates/frontend-implementation-contract.schema.json +68 -4
- package/docs/templates/frontend-implementation-dag.json +5 -5
- package/harness.json +1 -1
- package/package.json +8 -6
- package/skills/frontend-contract/SKILL.md +2 -1
- package/skills/frontend-contract/references/contract-protocol.md +19 -3
- package/skills/frontend-design-review/SKILL.md +12 -11
- package/skills/frontend-plan/SKILL.md +22 -2
- package/skills/frontend-plan/references/decision-contract.md +114 -5
- package/skills/frontend-plan/references/design-decisions.md +32 -0
- package/skills/frontend-review/SKILL.md +10 -11
- package/skills/frontend-scout/references/scout-evidence.md +4 -0
- package/dist/worker/console/static/assets/channel-DsZxrgqe.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/index-D9Sc0f0y.js +0 -449
- package/dist/worker/console/static/assets/index-DDQc5a50.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +0 -8
|
@@ -100,6 +100,9 @@ function buildOccurrenceCard(dagRunId, occurrence) {
|
|
|
100
100
|
false;
|
|
101
101
|
const card = document.createElement("button");
|
|
102
102
|
card.type = "button";
|
|
103
|
+
card.setAttribute("data-node-id", occurrence.nodeId);
|
|
104
|
+
card.setAttribute("data-execution-id", occurrence.executionId);
|
|
105
|
+
card.setAttribute("data-execution-key", occurrence.pass ? `pass-${occurrence.pass}` : "");
|
|
103
106
|
const extraClasses = [
|
|
104
107
|
recovered ? "is-recovered" : "",
|
|
105
108
|
occurrence.availability === "partial" ? "is-partial" : "",
|
|
@@ -11,3 +11,9 @@ export function resolveNodeRemark(node: {
|
|
|
11
11
|
nodeId: string;
|
|
12
12
|
rank?: string | number | null;
|
|
13
13
|
} | null | undefined): DagNodeRemark;
|
|
14
|
+
|
|
15
|
+
/** 面包屑次级:可读标题优先(截断),缺失/失败回退 run ID。 */
|
|
16
|
+
export function dagBreadcrumbLabel(
|
|
17
|
+
dag: { title?: string | null } | null | undefined,
|
|
18
|
+
dagRunId: string,
|
|
19
|
+
): string;
|
|
@@ -43,6 +43,8 @@ import {
|
|
|
43
43
|
} from "../router.js";
|
|
44
44
|
import {
|
|
45
45
|
fetchJson,
|
|
46
|
+
fetchJsonResult,
|
|
47
|
+
summarizeFetchError,
|
|
46
48
|
artifactUrl,
|
|
47
49
|
parseArtifactPreviewResponse,
|
|
48
50
|
} from "../api.js";
|
|
@@ -67,7 +69,7 @@ import {
|
|
|
67
69
|
mountViewState,
|
|
68
70
|
makeSessionEventIdentity,
|
|
69
71
|
} from "../state.js";
|
|
70
|
-
import { uiState, dagGraphViewportStates, dagGraphViewModes } from "../state.js";
|
|
72
|
+
import { uiState, dagGraphViewportStates, dagGraphViewModes, inspectRouteStates } from "../state.js";
|
|
71
73
|
import { sortPoolTasks } from "../format-pool.js";
|
|
72
74
|
import {
|
|
73
75
|
dagMetaVisibility,
|
|
@@ -76,11 +78,13 @@ import {
|
|
|
76
78
|
findDagForRun,
|
|
77
79
|
} from "../dag-model.js";
|
|
78
80
|
import { describeDagNodePurpose } from "../dag-node-purpose.js";
|
|
81
|
+
import { inspectorValueLabel } from "../dag-inspector-humanize.js";
|
|
79
82
|
import { layoutDag } from "../dag-layout.js";
|
|
80
83
|
import {
|
|
81
84
|
showView,
|
|
82
85
|
setBreadcrumb,
|
|
83
86
|
updateHeaderRefresh,
|
|
87
|
+
bindManualViewRefresh,
|
|
84
88
|
mountRelationBar,
|
|
85
89
|
} from "../shell-chrome.js";
|
|
86
90
|
import {
|
|
@@ -197,7 +201,9 @@ export function buildDagNodeTable(dagRunId, nodes) {
|
|
|
197
201
|
index + 1,
|
|
198
202
|
node.nodeId,
|
|
199
203
|
node.rank ?? "—",
|
|
200
|
-
node.executor
|
|
204
|
+
node.executor
|
|
205
|
+
? inspectorValueLabel("executor", node.executor)
|
|
206
|
+
: "—",
|
|
201
207
|
node.model ?? "—",
|
|
202
208
|
node.status ? badge(node.status) : "—",
|
|
203
209
|
formatMs(node.durationMs),
|
|
@@ -211,10 +217,10 @@ export function buildDagNodeTable(dagRunId, nodes) {
|
|
|
211
217
|
const nodeTable = buildTable(
|
|
212
218
|
[
|
|
213
219
|
"序号",
|
|
214
|
-
"
|
|
215
|
-
"
|
|
220
|
+
"步骤编号",
|
|
221
|
+
"执行层级",
|
|
216
222
|
"执行方式",
|
|
217
|
-
"
|
|
223
|
+
"实际模型",
|
|
218
224
|
"状态",
|
|
219
225
|
"耗时",
|
|
220
226
|
"执行历史",
|
|
@@ -235,12 +241,20 @@ export function buildDagNodeTable(dagRunId, nodes) {
|
|
|
235
241
|
return nodeTable;
|
|
236
242
|
}
|
|
237
243
|
|
|
244
|
+
/** 面包屑次级:可读标题优先(截断),缺失/失败回退 run ID。 */
|
|
245
|
+
export function dagBreadcrumbLabel(dag, dagRunId) {
|
|
246
|
+
const title = typeof dag?.title === "string" ? dag.title.trim() : "";
|
|
247
|
+
return title ? truncateText(title, 48) : dagRunId;
|
|
248
|
+
}
|
|
249
|
+
|
|
238
250
|
export async function renderDagDetail(dagRunId, initial = true) {
|
|
239
251
|
showView("dag");
|
|
240
|
-
|
|
252
|
+
const breadcrumbParts = (label) => [
|
|
241
253
|
{ label: UI_TEXT.dashboard, href: "#/" },
|
|
242
|
-
{ label:
|
|
243
|
-
|
|
254
|
+
...(inspectRouteStates.get("return-list") ? [{ label: "返回结果列表", href: inspectRouteStates.get("return-list") }] : []),
|
|
255
|
+
{ label },
|
|
256
|
+
];
|
|
257
|
+
setBreadcrumb(breadcrumbParts(dagRunId));
|
|
244
258
|
|
|
245
259
|
if (initial || uiState.currentDagRunId !== dagRunId) {
|
|
246
260
|
uiState.currentDagRunId = dagRunId;
|
|
@@ -249,7 +263,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
249
263
|
uiState.sessionEvents = [];
|
|
250
264
|
uiState.sessionEventIdentity = null;
|
|
251
265
|
uiState.dagInspectorOpen = false;
|
|
252
|
-
uiState.dagInspectorTab =
|
|
266
|
+
uiState.dagInspectorTab = null;
|
|
253
267
|
uiState.dagInspectorAttempt = null;
|
|
254
268
|
uiState.dagGraphViewportState = null;
|
|
255
269
|
uiState.dagTimelineViewportState = null;
|
|
@@ -261,7 +275,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
261
275
|
const metaEl = document.getElementById("dag-meta");
|
|
262
276
|
const ranksEl = document.getElementById("dag-ranks");
|
|
263
277
|
const nodesEl = document.getElementById("dag-nodes");
|
|
264
|
-
if (initial && metaEl) mountViewState(metaEl, "loading", "
|
|
278
|
+
if (initial && metaEl) mountViewState(metaEl, "loading", "正在加载运行流程…");
|
|
265
279
|
uiState.dagGraphViewportState = captureDagGraphViewportState(
|
|
266
280
|
uiState.dagGraphViewportState,
|
|
267
281
|
dagRunId,
|
|
@@ -271,17 +285,34 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
271
285
|
dagGraphViewportStates.set(dagRunId, uiState.dagGraphViewportState);
|
|
272
286
|
}
|
|
273
287
|
|
|
274
|
-
const
|
|
275
|
-
|
|
288
|
+
const requestGeneration = uiState.pollingGeneration;
|
|
289
|
+
const [dagResult, snapshot] = await Promise.all([
|
|
290
|
+
fetchJsonResult(`/api/dag-runs/${encodeURIComponent(dagRunId)}`),
|
|
276
291
|
fetchJson("/api/snapshot"),
|
|
277
292
|
]);
|
|
293
|
+
if (requestGeneration !== uiState.pollingGeneration || uiState.currentDagRunId !== dagRunId) return false;
|
|
294
|
+
if (!dagResult.ok && dagResult.status !== 404) {
|
|
295
|
+
let error = metaEl.querySelector(".dag-refresh-error");
|
|
296
|
+
if (!error) { error = el("div", "dag-refresh-error"); metaEl.appendChild(error); }
|
|
297
|
+
clearNode(error);
|
|
298
|
+
error.appendChild(el("span", "node-input-error", `更新失败,保留上次内容。${summarizeFetchError(dagResult)}`));
|
|
299
|
+
const retry = el("button", "btn btn-secondary", "重试");
|
|
300
|
+
retry.type = "button";
|
|
301
|
+
retry.addEventListener("click", () => { void renderDagDetail(dagRunId, false); });
|
|
302
|
+
error.appendChild(retry);
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
metaEl.querySelector(".dag-refresh-error")?.remove();
|
|
306
|
+
const dag = dagResult.ok ? dagResult.body : null;
|
|
307
|
+
if (dag) setBreadcrumb(breadcrumbParts(dagBreadcrumbLabel(dag, dagRunId)));
|
|
278
308
|
if (snapshot) {
|
|
279
309
|
uiState.lastSnapshot = snapshot;
|
|
280
|
-
updateHeaderRefresh(snapshot.generatedAt);
|
|
310
|
+
updateHeaderRefresh(snapshot.generatedAt, { mode: dag && isDagRunActive(dag) ? "auto" : "ended" });
|
|
311
|
+
bindManualViewRefresh("dag", () => renderDagDetail(dagRunId, false));
|
|
281
312
|
}
|
|
282
313
|
if (!dag) {
|
|
283
314
|
clearNode(metaEl);
|
|
284
|
-
mountViewState(metaEl, "error", "
|
|
315
|
+
mountViewState(metaEl, "error", "找不到这次运行。运行记录可能已被清理。");
|
|
285
316
|
mountRelationBar("dag-relations", {}, snapshot);
|
|
286
317
|
closeDagInspector();
|
|
287
318
|
return false;
|
|
@@ -307,6 +338,16 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
307
338
|
);
|
|
308
339
|
|
|
309
340
|
const nodes = dag.nodes ?? [];
|
|
341
|
+
if (initial) {
|
|
342
|
+
const routeState = parseRoute().inspector ?? inspectRouteStates.get(`dag:${dagRunId}`);
|
|
343
|
+
if (routeState?.nodeId && routeState.open !== false && nodes.some((node) => node.nodeId === routeState.nodeId)) {
|
|
344
|
+
uiState.selectedDagNodeId = routeState.nodeId;
|
|
345
|
+
uiState.dagInspectorOpen = true;
|
|
346
|
+
uiState.dagInspectorTab = ["overview", "input", "context", "timeline", "compare", "output", "lineage", "spec-evidence", "prompt-restart"].includes(routeState.tab) ? routeState.tab : null;
|
|
347
|
+
uiState.dagInspectorAttempt = { dagRunId, nodeId: routeState.nodeId, key: routeState.executionKey };
|
|
348
|
+
uiState.sessionEventIdentity = makeSessionEventIdentity(dagRunId, routeState.nodeId);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
310
351
|
if (
|
|
311
352
|
uiState.selectedDagNodeId &&
|
|
312
353
|
!nodes.some((n) => n.nodeId === uiState.selectedDagNodeId)
|
|
@@ -327,7 +368,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
327
368
|
(n) => (n.status ?? "").toLowerCase() === "skipped",
|
|
328
369
|
).length;
|
|
329
370
|
const succeededCount = nodes.filter(
|
|
330
|
-
(n) => isNodeFinished(n.status) && !isNodeFailed(n.status),
|
|
371
|
+
(n) => isNodeFinished(n.status) && !isNodeFailed(n.status) && String(n.status ?? "").toLowerCase() !== "skipped",
|
|
331
372
|
).length;
|
|
332
373
|
|
|
333
374
|
// reuse-dag-detail-shell: on a poll refresh (non-initial) we keep the
|
|
@@ -354,7 +395,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
354
395
|
LIVENESS_LABELS[dag.liveness] ?? dag.liveness ?? LIVENESS_LABELS.unknown;
|
|
355
396
|
const primaryEntries = [
|
|
356
397
|
["标题", dag.title || dag.dagRunId],
|
|
357
|
-
["
|
|
398
|
+
["运行编号", dag.dagRunId],
|
|
358
399
|
["状态", dagStatusBadge(dag.effectiveStatus ?? dag.status)],
|
|
359
400
|
];
|
|
360
401
|
if (visibility.showExecutorStatus)
|
|
@@ -381,9 +422,8 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
381
422
|
["开始时间", formatTs(dag.startedAt)],
|
|
382
423
|
["结束时间", formatTs(dag.finishedAt)],
|
|
383
424
|
["总耗时", formatDuration(dag.durationMs)],
|
|
384
|
-
["
|
|
425
|
+
["已结束", `${progress.finished}/${progress.total}`],
|
|
385
426
|
["成功/失败/跳过", `${succeededCount}/${failedCount}/${skippedCount}`],
|
|
386
|
-
["DAG 路径", dag.dagPath ?? "—"],
|
|
387
427
|
);
|
|
388
428
|
const lintSummary = frontendLintSummary(dag.frontendLint);
|
|
389
429
|
if (lintSummary) {
|
|
@@ -401,6 +441,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
401
441
|
],
|
|
402
442
|
["允许恢复", visibility.showResume ? "是" : "否"],
|
|
403
443
|
["允许收口", visibility.showReconcile ? "是" : "否"],
|
|
444
|
+
["记录保存位置", dag.dagPath ?? "—"],
|
|
404
445
|
]);
|
|
405
446
|
if (!initial && existingDetails?.parentNode === metaEl) {
|
|
406
447
|
const existingPrimary = metaEl.querySelector(":scope > .meta-grid");
|
|
@@ -429,7 +470,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
429
470
|
);
|
|
430
471
|
if (nodes.length === 0) {
|
|
431
472
|
clearNode(ranksEl);
|
|
432
|
-
ranksEl.appendChild(el("h3", null, "
|
|
473
|
+
ranksEl.appendChild(el("h3", null, "执行步骤关系"));
|
|
433
474
|
ranksEl.appendChild(el("p", "empty", UI_TEXT.noNodes));
|
|
434
475
|
} else if (reuseShell && existingGraphWrap?.parentNode === ranksEl) {
|
|
435
476
|
// reuse-dag-graph-wrap + viewport identity: keep wrap/viewport alive.
|
|
@@ -440,16 +481,16 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
440
481
|
}
|
|
441
482
|
} else {
|
|
442
483
|
clearNode(ranksEl);
|
|
443
|
-
ranksEl.appendChild(el("h3", null, "
|
|
484
|
+
ranksEl.appendChild(el("h3", null, "执行步骤关系"));
|
|
444
485
|
ranksEl.appendChild(renderDagGraph(dag, dagRunId, null));
|
|
445
486
|
}
|
|
446
487
|
|
|
447
488
|
const nodesScrollLeft = nodesEl.scrollLeft;
|
|
448
489
|
clearNode(nodesEl);
|
|
449
490
|
const nodeHeading = el("div", "dag-section-heading");
|
|
450
|
-
nodeHeading.appendChild(el("h3", null, "
|
|
491
|
+
nodeHeading.appendChild(el("h3", null, "执行步骤"));
|
|
451
492
|
nodeHeading.appendChild(
|
|
452
|
-
el("span", "dag-section-meta", `共 ${nodes.length}
|
|
493
|
+
el("span", "dag-section-meta", `共 ${nodes.length} 个步骤`),
|
|
453
494
|
);
|
|
454
495
|
nodesEl.appendChild(nodeHeading);
|
|
455
496
|
|
|
@@ -479,6 +520,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
479
520
|
dagRunId,
|
|
480
521
|
nodes.find((node) => node.nodeId === uiState.selectedDagNodeId),
|
|
481
522
|
initial,
|
|
523
|
+
dag,
|
|
482
524
|
);
|
|
483
525
|
return isDagRunActive(dag);
|
|
484
526
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isPlainNavigationClick, isRowNavigationClick } from "../router.js";
|
|
1
2
|
/** Observe UI — formal DAG run history page (independent #/dags). */
|
|
2
3
|
import { UI_TEXT } from "../constants.js";
|
|
3
4
|
import { el, clearNode } from "../dom.js";
|
|
@@ -331,6 +332,7 @@ function buildRow(run) {
|
|
|
331
332
|
`/dag/${encodeURIComponent(run.dagRunId)}`,
|
|
332
333
|
);
|
|
333
334
|
nameLink.addEventListener("click", (event) => {
|
|
335
|
+
if (!isPlainNavigationClick(event)) return;
|
|
334
336
|
event.preventDefault();
|
|
335
337
|
event.stopPropagation();
|
|
336
338
|
navigate(`/dag/${encodeURIComponent(run.dagRunId)}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRowNavigationClick } from "../router.js";
|
|
1
2
|
/** Observe UI view module (R5). */
|
|
2
3
|
import {
|
|
3
4
|
UI_TEXT,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
import { clearNode, el, reconcileKeyed, syncCompatibleNode } from "../dom.js";
|
|
20
21
|
import {
|
|
21
22
|
badge,
|
|
23
|
+
batchStatusBadge,
|
|
22
24
|
badgeClass,
|
|
23
25
|
statusLabel,
|
|
24
26
|
formatTs,
|
|
@@ -287,16 +289,12 @@ export async function renderDashboard(scrollTo) {
|
|
|
287
289
|
const dagStaleRuns = dagHealth?.staleRuns ?? 0;
|
|
288
290
|
const dagInterruptedRuns = dagHealth?.interruptedRuns ?? 0;
|
|
289
291
|
const dagInconsistentRuns = dagHealth?.inconsistentRuns ?? 0;
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
["stale", "orphaned"].includes((dag.liveness ?? "").toLowerCase()) ||
|
|
297
|
-
["interrupted", "remote-unknown"].includes(dag.effectiveStatus) ||
|
|
298
|
-
dag.stateConsistent === false),
|
|
299
|
-
).length;
|
|
292
|
+
const attentionDags = dagRuns.filter((dag) =>
|
|
293
|
+
["paused", "failed", "error", "interrupted"].includes(String(dag.lifecycle ?? "").toLowerCase()) ||
|
|
294
|
+
["failed", "error", "interrupted", "remote-unknown"].includes(String(dag.effectiveStatus ?? dag.status ?? "").toLowerCase()) ||
|
|
295
|
+
["stale", "orphaned"].includes(String(dag.liveness ?? "").toLowerCase()) || dag.stateConsistent === false,
|
|
296
|
+
);
|
|
297
|
+
const dagAttentionRuns = attentionDags.length;
|
|
300
298
|
|
|
301
299
|
const quietCount = snapshot.health?.quietCount ?? 0;
|
|
302
300
|
const staleCount = snapshot.health?.staleCount ?? 0;
|
|
@@ -504,6 +502,7 @@ export async function renderDashboard(scrollTo) {
|
|
|
504
502
|
dagBlock.appendChild(el("div", "risk-block-label", "需处理 DAG"));
|
|
505
503
|
dagBlock.appendChild(el("div", "risk-total", String(block.total)));
|
|
506
504
|
dagBlock.appendChild(el("p", "risk-summary", block.summary));
|
|
505
|
+
const details = el("details", "risk-dag-list"); details.appendChild(el("summary", "risk-link", "查看需处理运行")); details.appendChild(el("div", "risk-dag-items")); dagBlock.appendChild(details);
|
|
507
506
|
return dagBlock;
|
|
508
507
|
},
|
|
509
508
|
update: (dagBlock, block) => {
|
|
@@ -511,6 +510,15 @@ export async function renderDashboard(scrollTo) {
|
|
|
511
510
|
if (total) total.textContent = String(block.total);
|
|
512
511
|
const summary = dagBlock.querySelector(".risk-summary");
|
|
513
512
|
if (summary) summary.textContent = block.summary;
|
|
513
|
+
const list = dagBlock.querySelector(".risk-dag-items");
|
|
514
|
+
if (list) {
|
|
515
|
+
clearNode(list); for (const dag of attentionDags) {
|
|
516
|
+
const link = el("a", "object-link", dag.title || dag.dagRunId);
|
|
517
|
+
link.href = inspectPathToCanonicalHref(`/dag/${encodeURIComponent(dag.dagRunId)}`);
|
|
518
|
+
list.appendChild(link);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
514
522
|
},
|
|
515
523
|
},
|
|
516
524
|
);
|
|
@@ -634,7 +642,8 @@ export async function renderDashboard(scrollTo) {
|
|
|
634
642
|
create: (batch) => {
|
|
635
643
|
const tr = document.createElement("tr");
|
|
636
644
|
tr.classList.add("clickable");
|
|
637
|
-
tr.addEventListener("click", () =>
|
|
645
|
+
tr.addEventListener("click", (event) =>
|
|
646
|
+
isRowNavigationClick(event) &&
|
|
638
647
|
navigate(`/batch/${encodeURIComponent(batch.batchRunId)}`),
|
|
639
648
|
);
|
|
640
649
|
return tr;
|
|
@@ -643,7 +652,7 @@ export async function renderDashboard(scrollTo) {
|
|
|
643
652
|
const cells = [
|
|
644
653
|
batch.batchRunId,
|
|
645
654
|
batch.featureId ?? "—",
|
|
646
|
-
|
|
655
|
+
batchStatusBadge(batch),
|
|
647
656
|
formatTs(batch.startedAt),
|
|
648
657
|
summaryText(batch.summary),
|
|
649
658
|
];
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { bindManualViewRefresh, mountTaskRelationPicker } from "../shell-chrome.js";
|
|
2
|
+
import { sortAttentionTasks } from "../format-pool.js";
|
|
1
3
|
/** Observe UI view module (R5). */
|
|
2
4
|
import {
|
|
3
5
|
UI_TEXT,
|
|
@@ -66,9 +68,23 @@ import {
|
|
|
66
68
|
import { layoutDag } from "../dag-layout.js";
|
|
67
69
|
import { showView, setBreadcrumb, updateHeaderRefresh } from "../shell-chrome.js";
|
|
68
70
|
import { artifactLink } from "../kpi.js";
|
|
71
|
+
import { taskFailureCategoryLabel, followUpLabel } from "../task-failure-labels.js";
|
|
72
|
+
|
|
73
|
+
/** 异常页枚举单元格:中文主文案,raw token 保留在 title 供排障(未知原文透传)。 */
|
|
74
|
+
function attentionTokenCell(raw, kind) {
|
|
75
|
+
const value = String(raw ?? "").trim();
|
|
76
|
+
if (!value) return "—";
|
|
77
|
+
const kindName = kind === "category" ? "分类" : "跟进";
|
|
78
|
+
const mapped = kind === "category" ? taskFailureCategoryLabel(value) : followUpLabel(value);
|
|
79
|
+
const cell = el("span", "attention-token");
|
|
80
|
+
cell.textContent = mapped;
|
|
81
|
+
cell.setAttribute("title", mapped === value ? `未收录的${kindName} token:${value}` : `${kindName} token:${value}`);
|
|
82
|
+
return cell;
|
|
83
|
+
}
|
|
69
84
|
|
|
70
85
|
export async function renderFailures() {
|
|
71
86
|
showView("failures");
|
|
87
|
+
bindManualViewRefresh("failures", () => renderFailures());
|
|
72
88
|
setBreadcrumb([
|
|
73
89
|
{ label: UI_TEXT.dashboard, href: "#/" },
|
|
74
90
|
{ label: UI_TEXT.failures },
|
|
@@ -89,15 +105,9 @@ export async function renderFailures() {
|
|
|
89
105
|
return;
|
|
90
106
|
}
|
|
91
107
|
uiState.lastSnapshot = snapshot;
|
|
92
|
-
updateHeaderRefresh(snapshot.generatedAt);
|
|
108
|
+
updateHeaderRefresh(snapshot.generatedAt, { mode: "manual" });
|
|
93
109
|
|
|
94
|
-
const tasks = (snapshot.tasks ?? [])
|
|
95
|
-
(task) =>
|
|
96
|
-
task.status === "failed" ||
|
|
97
|
-
task.status === "blocked" ||
|
|
98
|
-
task.status === "stale" ||
|
|
99
|
-
task.liveness === "stale",
|
|
100
|
-
);
|
|
110
|
+
const tasks = sortAttentionTasks(snapshot.tasks ?? []);
|
|
101
111
|
|
|
102
112
|
if (tasks.length === 0) {
|
|
103
113
|
clearNode(listEl);
|
|
@@ -122,11 +132,11 @@ export async function renderFailures() {
|
|
|
122
132
|
? () => navigate(`/run/${encodeURIComponent(task.workerRunId)}`)
|
|
123
133
|
: undefined,
|
|
124
134
|
cells: [
|
|
125
|
-
task.taskId
|
|
135
|
+
objectRouteLink("task", task.taskId, snapshot, task.featureId),
|
|
126
136
|
task.featureId ?? "—",
|
|
127
137
|
badge(task.status),
|
|
128
|
-
task.failureCategory
|
|
129
|
-
task.recommendedFollowUp
|
|
138
|
+
attentionTokenCell(task.failureCategory, "category"),
|
|
139
|
+
attentionTokenCell(task.recommendedFollowUp, "followUp"),
|
|
130
140
|
(() => {
|
|
131
141
|
const links = el("div", "artifact-links");
|
|
132
142
|
const r = refs(task);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { renderInlineArtifactList } from "./run.js";
|
|
2
|
+
import { bindManualViewRefresh, mountTaskRelationPicker } from "../shell-chrome.js";
|
|
1
3
|
/** Observe UI view module (R5). */
|
|
2
4
|
import {
|
|
3
5
|
UI_TEXT,
|
|
@@ -79,6 +81,7 @@ import { artifactLink } from "../kpi.js";
|
|
|
79
81
|
|
|
80
82
|
export async function renderFeatureDetail(featureId) {
|
|
81
83
|
showView("feature");
|
|
84
|
+
bindManualViewRefresh("feature", () => renderFeatureDetail(featureId));
|
|
82
85
|
setBreadcrumb([
|
|
83
86
|
{ label: UI_TEXT.dashboard, href: "#/" },
|
|
84
87
|
{ label: featureId || "Feature" },
|
|
@@ -124,7 +127,7 @@ export async function renderFeatureDetail(featureId) {
|
|
|
124
127
|
return false;
|
|
125
128
|
}
|
|
126
129
|
uiState.lastSnapshot = snapshot;
|
|
127
|
-
updateHeaderRefresh(snapshot.generatedAt);
|
|
130
|
+
updateHeaderRefresh(snapshot.generatedAt, { mode: "manual" });
|
|
128
131
|
|
|
129
132
|
const feature = lookup.kind === "found" ? lookup.feature : undefined;
|
|
130
133
|
if (!feature) {
|
|
@@ -143,31 +146,8 @@ export async function renderFeatureDetail(featureId) {
|
|
|
143
146
|
return false;
|
|
144
147
|
}
|
|
145
148
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
.map((t) => t.taskId)
|
|
149
|
-
.find((id) => objectExistsInSnapshot("task", id, snapshot));
|
|
150
|
-
const poolTask = linkedTask
|
|
151
|
-
? (snapshot?.tasks ?? []).find((t) => t.taskId === linkedTask)
|
|
152
|
-
: undefined;
|
|
153
|
-
const batchRunId =
|
|
154
|
-
poolTask?.batchRunId ??
|
|
155
|
-
(poolTask ? findBatchRunIdForTask(snapshot, poolTask) : null);
|
|
156
|
-
const dag =
|
|
157
|
-
poolTask != null
|
|
158
|
-
? findDagForRun(snapshot, poolTask.workerRunId, poolTask)
|
|
159
|
-
: undefined;
|
|
160
|
-
mountRelationBar(
|
|
161
|
-
"feature-relations",
|
|
162
|
-
{
|
|
163
|
-
featureId: feature.featureId,
|
|
164
|
-
taskId: linkedTask,
|
|
165
|
-
batchRunId: batchRunId ?? undefined,
|
|
166
|
-
workerRunId: poolTask?.workerRunId,
|
|
167
|
-
dagRunId: dag?.dagRunId ?? poolTask?.dagRunId,
|
|
168
|
-
},
|
|
169
|
-
snapshot,
|
|
170
|
-
);
|
|
149
|
+
const linkedTasks = (feature.tasks ?? []).map((task) => ({ ...task, ...(snapshot.tasks ?? []).find((entry) => entry.taskId === task.taskId && entry.featureId === feature.featureId), featureId: feature.featureId }));
|
|
150
|
+
mountTaskRelationPicker("feature-relations", { featureId: feature.featureId }, linkedTasks, snapshot);
|
|
171
151
|
|
|
172
152
|
try {
|
|
173
153
|
metaEl.appendChild(el("h3", "section-heading", "状态与摘要"));
|
|
@@ -205,9 +185,11 @@ export async function renderFeatureDetail(featureId) {
|
|
|
205
185
|
ac.required ? "是" : "否",
|
|
206
186
|
ac.status ?? "—",
|
|
207
187
|
(ac.blockedBy ?? []).join(", ") || "—",
|
|
208
|
-
(
|
|
209
|
-
|
|
210
|
-
|
|
188
|
+
(() => {
|
|
189
|
+
if (!(ac.evidence ?? []).length) return "缺失";
|
|
190
|
+
const details = el("details", "ac-evidence"); details.appendChild(el("summary", null, `${ac.evidence.length} 份证据`));
|
|
191
|
+
let loaded = false; details.addEventListener("toggle", () => { if (details.open && !loaded) { loaded = true; const body = el("div"); details.appendChild(body); renderInlineArtifactList(body, ac.evidence.map((path, index) => [`证据 ${index + 1}`, path])); } }); return details;
|
|
192
|
+
})(),
|
|
211
193
|
],
|
|
212
194
|
})),
|
|
213
195
|
),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isPlainNavigationClick, isRowNavigationClick } from "../router.js";
|
|
1
2
|
/** Observe UI view module — Task Pool ops surface (R5 + ops refresh). */
|
|
2
3
|
import { UI_TEXT, POOL_POLL_MS } from "../constants.js";
|
|
3
4
|
import { clearNode, el, reconcileKeyed } from "../dom.js";
|
|
@@ -26,6 +27,7 @@ import { uiState } from "../state.js";
|
|
|
26
27
|
import {
|
|
27
28
|
sortPoolTasks,
|
|
28
29
|
matchesPoolStatusFilter,
|
|
30
|
+
matchesPoolTaskFilter,
|
|
29
31
|
poolStatusCounts,
|
|
30
32
|
sortAttentionTasks,
|
|
31
33
|
collectPlannerReadyTasks,
|
|
@@ -125,6 +127,7 @@ function linkTo(path, label, className) {
|
|
|
125
127
|
const a = el("a", className || "object-link", label);
|
|
126
128
|
a.href = inspectPathToCanonicalHref(path);
|
|
127
129
|
a.addEventListener("click", (e) => {
|
|
130
|
+
if (!isPlainNavigationClick(e)) return;
|
|
128
131
|
e.preventDefault();
|
|
129
132
|
e.stopPropagation();
|
|
130
133
|
navigate(path);
|
|
@@ -264,7 +267,7 @@ export async function renderPool(options = {}) {
|
|
|
264
267
|
|
|
265
268
|
if (updatedAtEl) {
|
|
266
269
|
updatedAtEl.textContent = snapshot.generatedAt
|
|
267
|
-
?
|
|
270
|
+
? `数据更新于 ${formatTs(snapshot.generatedAt)} · 仅提示`
|
|
268
271
|
: "";
|
|
269
272
|
}
|
|
270
273
|
|
|
@@ -302,21 +305,12 @@ export async function renderPool(options = {}) {
|
|
|
302
305
|
const routeFilters = parseRoute().filters ?? {};
|
|
303
306
|
const statusFilter = (routeFilters.status ?? "").toLowerCase();
|
|
304
307
|
const qFilter = (routeFilters.q ?? "").trim().toLowerCase();
|
|
305
|
-
const filteredTasks = allTasks.filter((task) =>
|
|
306
|
-
if (statusFilter && !matchesPoolStatusFilter(task.status, statusFilter)) {
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
if (qFilter) {
|
|
310
|
-
const hay =
|
|
311
|
-
`${task.taskId ?? ""} ${task.featureId ?? ""} ${task.workerRunId ?? ""} ${task.failureCategory ?? ""} ${task.recommendedFollowUp ?? ""}`.toLowerCase();
|
|
312
|
-
if (!hay.includes(qFilter)) return false;
|
|
313
|
-
}
|
|
314
|
-
return true;
|
|
315
|
-
});
|
|
308
|
+
const filteredTasks = allTasks.filter((task) => matchesPoolTaskFilter(task, routeFilters));
|
|
316
309
|
const poolPresent = snapshot.taskPool?.present === true;
|
|
317
310
|
const projectionFailed = Boolean(snapshot.projectionError);
|
|
318
|
-
const
|
|
319
|
-
const
|
|
311
|
+
const taskById = new Map(allTasks.map((task) => [taskIdentityKey(task.featureId, task.taskId), task]));
|
|
312
|
+
const readyItems = collectPlannerReadyTasks(snapshot.features).filter((item) => matchesPoolTaskFilter({ ...item, status: "pending", ...taskById.get(taskIdentityKey(item.featureId, item.taskId)) }, routeFilters));
|
|
313
|
+
const attentionTasks = sortAttentionTasks(filteredTasks);
|
|
320
314
|
const counts = poolStatusCounts(allTasks);
|
|
321
315
|
const health = snapshot.health ?? {};
|
|
322
316
|
|
|
@@ -352,7 +346,7 @@ export async function renderPool(options = {}) {
|
|
|
352
346
|
qLabel.appendChild(document.createTextNode("搜索"));
|
|
353
347
|
const qInput = document.createElement("input");
|
|
354
348
|
qInput.type = "search";
|
|
355
|
-
qInput.placeholder = "Task / Feature / run…";
|
|
349
|
+
qInput.placeholder = "名称 / Task / Feature / run…";
|
|
356
350
|
qInput.setAttribute("aria-label", "搜索 Task");
|
|
357
351
|
qInput.dataset.role = "q";
|
|
358
352
|
qLabel.appendChild(qInput);
|
|
@@ -366,14 +360,20 @@ export async function renderPool(options = {}) {
|
|
|
366
360
|
}),
|
|
367
361
|
);
|
|
368
362
|
});
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
})
|
|
375
|
-
);
|
|
376
|
-
}
|
|
363
|
+
let searchTimer;
|
|
364
|
+
const applySearch = () => {
|
|
365
|
+
clearTimeout(searchTimer);
|
|
366
|
+
searchTimer = setTimeout(() => {
|
|
367
|
+
if (parseRoute().view !== "pool" || !qInput.isConnected) return;
|
|
368
|
+
navigate(buildHashPath("/pool", { status: statusSelect.value, q: qInput.value.trim() }));
|
|
369
|
+
}, 220);
|
|
370
|
+
};
|
|
371
|
+
qInput.addEventListener("input", (event) => { if (!event.isComposing) applySearch(); });
|
|
372
|
+
qInput.addEventListener("compositionend", applySearch);
|
|
373
|
+
const clear = el("button", "btn btn-secondary", "清空筛选");
|
|
374
|
+
clear.type = "button";
|
|
375
|
+
clear.addEventListener("click", () => { clearTimeout(searchTimer); qInput.value = ""; statusSelect.value = ""; navigate("/pool"); });
|
|
376
|
+
wrap.appendChild(clear);
|
|
377
377
|
wrap.appendChild(statusLabelEl);
|
|
378
378
|
wrap.appendChild(qLabel);
|
|
379
379
|
wrap.appendChild(count);
|
|
@@ -469,22 +469,31 @@ export async function renderPool(options = {}) {
|
|
|
469
469
|
health.timeoutRiskCount ?? 0,
|
|
470
470
|
(health.timeoutRiskCount ?? 0) > 0 ? "kpi-risk" : "",
|
|
471
471
|
],
|
|
472
|
-
|
|
472
|
+
// 口径与 Ready 不同:Ready 来自 Feature 规划项,此处只数已建 Task。
|
|
473
|
+
[
|
|
474
|
+
"已建 Task",
|
|
475
|
+
allTasks.length,
|
|
476
|
+
"",
|
|
477
|
+
"当前已创建的 Task 数;上方 Ready 是 Feature 决策产生的规划项,尚未建 Task,不计入本数。",
|
|
478
|
+
],
|
|
473
479
|
];
|
|
474
480
|
reconcileKeyed(cards, entries, {
|
|
475
481
|
getKey: ([label]) => label,
|
|
476
|
-
create: ([label, value, extra]) => {
|
|
482
|
+
create: ([label, value, extra, title]) => {
|
|
477
483
|
const card = el("div", `kpi-card${extra ? ` ${extra}` : ""}`);
|
|
478
484
|
card.appendChild(el("div", "kpi-value", String(value)));
|
|
479
485
|
card.appendChild(el("div", "kpi-label", label));
|
|
486
|
+
if (title) card.setAttribute("title", title);
|
|
480
487
|
return card;
|
|
481
488
|
},
|
|
482
|
-
update: (card, [label, value, extra]) => {
|
|
489
|
+
update: (card, [label, value, extra, title]) => {
|
|
483
490
|
card.className = `kpi-card${extra ? ` ${extra}` : ""}`;
|
|
484
491
|
const valueEl = card.querySelector(".kpi-value");
|
|
485
492
|
const labelEl = card.querySelector(".kpi-label");
|
|
486
493
|
if (valueEl) valueEl.textContent = String(value);
|
|
487
494
|
if (labelEl) labelEl.textContent = label;
|
|
495
|
+
if (title) card.setAttribute("title", title);
|
|
496
|
+
else card.removeAttribute("title");
|
|
488
497
|
},
|
|
489
498
|
});
|
|
490
499
|
},
|
|
@@ -621,7 +630,7 @@ export async function renderPool(options = {}) {
|
|
|
621
630
|
create: (task) => {
|
|
622
631
|
const tr = document.createElement("tr");
|
|
623
632
|
tr.classList.add("clickable");
|
|
624
|
-
tr.addEventListener("click", () => navigate(taskPath(task)));
|
|
633
|
+
tr.addEventListener("click", (event) => { if (isRowNavigationClick(event)) navigate(taskPath(task)); });
|
|
625
634
|
for (let i = 0; i < 8; i++) tr.appendChild(document.createElement("td"));
|
|
626
635
|
return tr;
|
|
627
636
|
},
|
|
@@ -703,7 +712,7 @@ export async function renderPool(options = {}) {
|
|
|
703
712
|
export function startPoolView() {
|
|
704
713
|
stopPoolAutoPolling();
|
|
705
714
|
syncPoolToolbar();
|
|
706
|
-
|
|
715
|
+
return renderPool({ reason: "route" }).then(() => {
|
|
707
716
|
if (poolAutoRefreshEnabled()) startPoolAutoPolling();
|
|
708
717
|
});
|
|
709
718
|
}
|