@tea-agent/loop-agent 0.43.0-next.1 → 0.43.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.
- package/CHANGELOG.md +144 -9
- package/dist/application/dag/run-dag.js +5 -0
- package/dist/application/evaluation/budget.js +19 -1
- package/dist/application/task-lifecycle/advance.js +17 -4
- 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/executors/dag-pi-executor.js +1237 -586
- package/dist/executors/pi-executor.js +22 -1
- package/dist/executors/pi-sdk-executor.js +140 -39
- package/dist/executors/shell-executor.js +8 -4
- package/dist/shared/frontend-execution-policy.js +23 -0
- package/dist/task/config-types.js +4 -0
- 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/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 +113 -0
- package/dist/worker/console/chat/browser-routes.js +140 -0
- package/dist/worker/console/chat/explore-tools.js +13 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +86 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
- package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
- package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
- package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
- package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
- package/dist/worker/console/chat/pi-runtime.js +207 -1178
- package/dist/worker/console/chat/routes.js +208 -71
- 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 +65 -9
- package/dist/worker/console/chat/session-catalog.js +32 -0
- package/dist/worker/console/chat/session-mode-view.js +49 -0
- package/dist/worker/console/chat/session-mode.js +219 -0
- package/dist/worker/console/chat/session-store.js +22 -6
- package/dist/worker/console/chat/shortcuts.js +6 -0
- package/dist/worker/console/chat/terminal-routes.js +216 -0
- package/dist/worker/console/chat/terminal-sessions.js +348 -0
- package/dist/worker/console/chat/terminal-tools.js +211 -0
- package/dist/worker/console/chat/tool-preview.js +11 -0
- package/dist/worker/console/chat/tools.js +2 -0
- package/dist/worker/console/server.js +2 -28
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-B0Q0nClf.js → abnfDiagram-N423BO3Z-CTSK0xbU.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DCPjC19G.js → arc-_2MfctFP.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CP5n9jmG.js → architectureDiagram-T3A2C74G-DsDe2wTg.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CmaWi_Wg.js → blockDiagram-VBNYF7ZC-Dtz_779U.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Ct5tcMfZ.js → c4Diagram-5PPSVZJV-LaULi-gw.js} +1 -1
- package/dist/worker/console/static/assets/channel-CqS4yBWT.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-C0osm1Zf.js → chunk-2GRJ4B5K-DuHBqqX8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CDWviED7.js → chunk-2Q5K7J3B-BZgZs1dn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-mtSk1-j7.js → chunk-5RXB4S5H-tzwmlIO_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D5J9PHC7.js → chunk-5VM5RSS4-CeYrLkSn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Begg4WAa.js → chunk-6Q2QTUOP-C92cgilm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-sF1AEy7T.js → chunk-GF5L2VYU-XOXpGC0F.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BqbsWXZb.js → chunk-JWPE2WC7-CGHbJyCH.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BKJrPcJ6.js → chunk-KBJHAD2P-Bs2-LTzf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CcwNMRho.js → chunk-RYQCIY6F-rN7up7E4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BEJi3iIs.js → chunk-XXDRQBXY-B_fWjKpR.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DVOxNOq0.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DVOxNOq0.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-DRLgTvVu.js → cose-bilkent-JH36ORCC-DE3SdJvB.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BoAVG3cs.js → cynefin-VYW2F7L2-B-4yuRAX.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bzol06hR.js → cynefinDiagram-MW4NZA55-TiCPFuO8.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-UmliJM77.js → dagre-VZM6K2ZE-7crzLMzA.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BWLcJkFo.js → diagram-7IWD3JNH-Cvr-x6Fd.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CgApOm-O.js → diagram-B4RE2ZJO-BzcRHUjH.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Bh3RgTLs.js → diagram-LBJQPF4R-BjkAXilh.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Dsh-D5nE.js → diagram-Q27KOJAE-BkzyATK7.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dkbbpcpb.js → diagram-UB23O5K3-rZuTarYP.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-3EBaA3t3.js → ebnfDiagram-BXEA7PRR-CAYENaN1.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bq2Dc5ok.js → erDiagram-JOGREHBK-DCEam0Uq.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-De7y55ha.js → flowDiagram-UKHOOZJN-e_dH4wVO.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-fLiDbQRh.js → ganttDiagram-PKOTCBZU-CvZa2zOK.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Du617mp1.js → gitGraphDiagram-DS77QQ5N-CbT5TuKp.js} +1 -1
- package/dist/worker/console/static/assets/index-CUPeoE8W.js +468 -0
- package/dist/worker/console/static/assets/index-SAANIK8g.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BwmlF-XP.js → infoDiagram-6WML65LV-BWzHQa_Z.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-zsGtS59u.js → ishikawaDiagram-WSZJBQD7-BTjlovtU.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DTTPTe3f.js → journeyDiagram-NVQOT4AX-DkvQoCc1.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BkXg9aP-.js → kanban-definition-27J2QSJJ-CHSzGi3W.js} +1 -1
- package/dist/worker/console/static/assets/{linear-7U2ue5IE.js → linear-DZMkTCWH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BUuGHmWO.js → mermaid.core-BIMhty7A.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-7q6fX0Sv.js → mindmap-definition-FAOFIHXS-Dx-tyMl3.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CkQPJN55.js → pegDiagram-VL7TDLO6-C6G34rbY.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CCal_tfX.js → pieDiagram-7S7Q4E2Y-CHK9XOfm.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js → quadrantDiagram-CIZ2JOQS-CDARkI0w.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CJmww7D-.js → railroadDiagram-AXF67PYL-DiuUe0ZQ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D6ldWJEv.js → requirementDiagram-LRYGKXZP-RE61UR56.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-VGh33I9e.js → sankeyDiagram-W5VNT64P-KDNLHMEG.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CCkDrIjU.js → sequenceDiagram-SI44F4Z6-CeVgXnD9.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-NC8f5Otb.js → sizeCapture-X5ZJPWSS-CXwGyjjG.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-_92ezZdF.js → stateDiagram-OKZ733FA-BjTfozab.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BXoYsyr2.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BdUCGtbP.js → swimlanes-SLNWSIFB-BaXOB8GT.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DfKliS4e.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CdD1H8ia.js → timeline-definition-Z64GVDOM-C3r2hqUU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-Dkdk3oFo.js → vennDiagram-T6HMQDX7-MWKgl1S9.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CKu2uPPY.js → wardleyDiagram-T6FBY63Y-C9iFcZPk.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-ClqAnuG8.js → xychartDiagram-ELKLHX3M-DqnM24Jp.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +10 -0
- 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 +122 -10
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +7 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +27 -15
- package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +10 -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 +44 -24
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +18 -2
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +41 -8
- package/dist/worker/console/static-src/operator-chat/useComposer.js +26 -11
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -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/dag-inspector-humanize.js +3 -0
- 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/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/relations.js +2 -0
- 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 +263 -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 +50 -3
- package/dist/worker/observe/static/views/dag-inspector.js +742 -268
- 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 +49 -11
- 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 +189 -240
- package/dist/worker/observe/static/views/task.js +81 -62
- package/dist/workflows/dag/budget-enforcement.js +53 -3
- package/dist/workflows/dag/frontend-capacity.js +9 -0
- package/dist/workflows/dag/frontend-closeout.js +1 -1
- package/dist/workflows/dag/frontend-design-policy.js +2 -1
- 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 +13 -0
- 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 +10 -3
- package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
- package/dist/workflows/dag/frontend-recovery-controller.js +7 -7
- package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
- package/dist/workflows/dag/frontend-recovery-run.js +4 -0
- package/dist/workflows/dag/frontend-review-context.js +2 -1
- package/dist/workflows/dag/frontend-review-scopes.js +139 -0
- package/dist/workflows/dag/frontend-session-budget.js +249 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +20 -2
- package/dist/workflows/dag/frontend-shape-facts.js +12 -2
- package/dist/workflows/dag/frontend-shape.js +30 -1
- package/dist/workflows/dag/frontend-test-execution-evidence.js +3 -2
- package/dist/workflows/dag/frontend-typed-event-store.js +11 -0
- package/dist/workflows/dag/frontend-verification-trace.js +1 -1
- package/dist/workflows/dag/init-hybrid.js +65 -167
- package/dist/workflows/dag/node-execution.js +38 -155
- package/dist/workflows/dag/prompt.js +4 -0
- package/dist/workflows/dag/rerun-plan.js +7 -1
- package/dist/workflows/dag/runner.js +26 -1
- package/dist/workflows/dag/types.js +6 -0
- package/docs/operations/README.md +1 -0
- package/docs/templates/frontend-design-contract.md +4 -4
- package/docs/templates/frontend-implementation-contract.schema.json +34 -2
- package/docs/templates/frontend-implementation-dag.json +14 -13
- package/package.json +6 -3
- package/skills/frontend-bounded-implement/SKILL.md +3 -3
- package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
- package/skills/frontend-contract/SKILL.md +2 -1
- package/skills/frontend-contract/references/contract-protocol.md +22 -4
- package/skills/frontend-design-review/SKILL.md +22 -16
- package/skills/frontend-design-review/references/review-checklist.md +7 -7
- package/skills/frontend-plan/SKILL.md +21 -2
- package/skills/frontend-plan/references/decision-contract.md +42 -23
- package/skills/frontend-review/SKILL.md +22 -29
- package/skills/frontend-review/references/review-findings.md +24 -43
- package/skills/frontend-scout/references/scout-evidence.md +4 -0
- package/dist/worker/console/static/assets/channel-DAS07MdS.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +0 -1
- package/dist/worker/console/static/assets/index-C0O48S_P.js +0 -449
- package/dist/worker/console/static/assets/index-CzKf4U8P.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.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,
|
|
@@ -82,6 +84,7 @@ import {
|
|
|
82
84
|
showView,
|
|
83
85
|
setBreadcrumb,
|
|
84
86
|
updateHeaderRefresh,
|
|
87
|
+
bindManualViewRefresh,
|
|
85
88
|
mountRelationBar,
|
|
86
89
|
} from "../shell-chrome.js";
|
|
87
90
|
import {
|
|
@@ -238,12 +241,20 @@ export function buildDagNodeTable(dagRunId, nodes) {
|
|
|
238
241
|
return nodeTable;
|
|
239
242
|
}
|
|
240
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
|
+
|
|
241
250
|
export async function renderDagDetail(dagRunId, initial = true) {
|
|
242
251
|
showView("dag");
|
|
243
|
-
|
|
252
|
+
const breadcrumbParts = (label) => [
|
|
244
253
|
{ label: UI_TEXT.dashboard, href: "#/" },
|
|
245
|
-
{ label:
|
|
246
|
-
|
|
254
|
+
...(inspectRouteStates.get("return-list") ? [{ label: "返回结果列表", href: inspectRouteStates.get("return-list") }] : []),
|
|
255
|
+
{ label },
|
|
256
|
+
];
|
|
257
|
+
setBreadcrumb(breadcrumbParts(dagRunId));
|
|
247
258
|
|
|
248
259
|
if (initial || uiState.currentDagRunId !== dagRunId) {
|
|
249
260
|
uiState.currentDagRunId = dagRunId;
|
|
@@ -252,7 +263,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
252
263
|
uiState.sessionEvents = [];
|
|
253
264
|
uiState.sessionEventIdentity = null;
|
|
254
265
|
uiState.dagInspectorOpen = false;
|
|
255
|
-
uiState.dagInspectorTab =
|
|
266
|
+
uiState.dagInspectorTab = null;
|
|
256
267
|
uiState.dagInspectorAttempt = null;
|
|
257
268
|
uiState.dagGraphViewportState = null;
|
|
258
269
|
uiState.dagTimelineViewportState = null;
|
|
@@ -274,13 +285,30 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
274
285
|
dagGraphViewportStates.set(dagRunId, uiState.dagGraphViewportState);
|
|
275
286
|
}
|
|
276
287
|
|
|
277
|
-
const
|
|
278
|
-
|
|
288
|
+
const requestGeneration = uiState.pollingGeneration;
|
|
289
|
+
const [dagResult, snapshot] = await Promise.all([
|
|
290
|
+
fetchJsonResult(`/api/dag-runs/${encodeURIComponent(dagRunId)}`),
|
|
279
291
|
fetchJson("/api/snapshot"),
|
|
280
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)));
|
|
281
308
|
if (snapshot) {
|
|
282
309
|
uiState.lastSnapshot = snapshot;
|
|
283
|
-
updateHeaderRefresh(snapshot.generatedAt);
|
|
310
|
+
updateHeaderRefresh(snapshot.generatedAt, { mode: dag && isDagRunActive(dag) ? "auto" : "ended" });
|
|
311
|
+
bindManualViewRefresh("dag", () => renderDagDetail(dagRunId, false));
|
|
284
312
|
}
|
|
285
313
|
if (!dag) {
|
|
286
314
|
clearNode(metaEl);
|
|
@@ -310,6 +338,16 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
310
338
|
);
|
|
311
339
|
|
|
312
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
|
+
}
|
|
313
351
|
if (
|
|
314
352
|
uiState.selectedDagNodeId &&
|
|
315
353
|
!nodes.some((n) => n.nodeId === uiState.selectedDagNodeId)
|
|
@@ -330,7 +368,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
330
368
|
(n) => (n.status ?? "").toLowerCase() === "skipped",
|
|
331
369
|
).length;
|
|
332
370
|
const succeededCount = nodes.filter(
|
|
333
|
-
(n) => isNodeFinished(n.status) && !isNodeFailed(n.status),
|
|
371
|
+
(n) => isNodeFinished(n.status) && !isNodeFailed(n.status) && String(n.status ?? "").toLowerCase() !== "skipped",
|
|
334
372
|
).length;
|
|
335
373
|
|
|
336
374
|
// reuse-dag-detail-shell: on a poll refresh (non-initial) we keep the
|
|
@@ -384,9 +422,8 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
384
422
|
["开始时间", formatTs(dag.startedAt)],
|
|
385
423
|
["结束时间", formatTs(dag.finishedAt)],
|
|
386
424
|
["总耗时", formatDuration(dag.durationMs)],
|
|
387
|
-
["
|
|
425
|
+
["已结束", `${progress.finished}/${progress.total}`],
|
|
388
426
|
["成功/失败/跳过", `${succeededCount}/${failedCount}/${skippedCount}`],
|
|
389
|
-
["记录保存位置", dag.dagPath ?? "—"],
|
|
390
427
|
);
|
|
391
428
|
const lintSummary = frontendLintSummary(dag.frontendLint);
|
|
392
429
|
if (lintSummary) {
|
|
@@ -404,6 +441,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
404
441
|
],
|
|
405
442
|
["允许恢复", visibility.showResume ? "是" : "否"],
|
|
406
443
|
["允许收口", visibility.showReconcile ? "是" : "否"],
|
|
444
|
+
["记录保存位置", dag.dagPath ?? "—"],
|
|
407
445
|
]);
|
|
408
446
|
if (!initial && existingDetails?.parentNode === metaEl) {
|
|
409
447
|
const existingPrimary = metaEl.querySelector(":scope > .meta-grid");
|
|
@@ -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
|
}
|
|
@@ -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";
|
|
@@ -63,6 +65,7 @@ import {
|
|
|
63
65
|
updateDagGraphViewportState,
|
|
64
66
|
updateDagTimelineViewportState,
|
|
65
67
|
computeFollowLatest,
|
|
68
|
+
isScrollRegionAboveBottom,
|
|
66
69
|
updateDagOutputViewportState,
|
|
67
70
|
renderViewState,
|
|
68
71
|
mountViewState,
|
|
@@ -195,6 +198,32 @@ export function renderRunDagProgress(task, snapshot) {
|
|
|
195
198
|
);
|
|
196
199
|
}
|
|
197
200
|
|
|
201
|
+
/** 运行页「最新输出」的对话式「回到底部」按钮:可滚动且离开底部即出现,点击回到底部。 */
|
|
202
|
+
function buildRunOutputFollowDock(pre) {
|
|
203
|
+
const dock = el("div", "run-output-follow-dock");
|
|
204
|
+
const button = document.createElement("button");
|
|
205
|
+
button.type = "button";
|
|
206
|
+
button.className = "process-timeline-autoscroll";
|
|
207
|
+
button.setAttribute("aria-label", "回到底部");
|
|
208
|
+
button.setAttribute("data-tooltip", "回到底部");
|
|
209
|
+
const icon = el("i", "ri-arrow-down-line");
|
|
210
|
+
icon.setAttribute("aria-hidden", "true");
|
|
211
|
+
button.appendChild(icon);
|
|
212
|
+
const sync = () => {
|
|
213
|
+
dock.hidden = !isScrollRegionAboveBottom(pre);
|
|
214
|
+
};
|
|
215
|
+
pre._syncJumpToBottom = sync;
|
|
216
|
+
button.addEventListener("click", () => {
|
|
217
|
+
pre.scrollLeft = 0;
|
|
218
|
+
pre.scrollTop = pre.scrollHeight;
|
|
219
|
+
sync();
|
|
220
|
+
});
|
|
221
|
+
pre.addEventListener("scroll", sync, { passive: true });
|
|
222
|
+
sync();
|
|
223
|
+
dock.appendChild(button);
|
|
224
|
+
return dock;
|
|
225
|
+
}
|
|
226
|
+
|
|
198
227
|
export function renderRunPanels(task, events, snapshot) {
|
|
199
228
|
const metaEl = document.getElementById("run-meta");
|
|
200
229
|
const stepsEl = document.getElementById("run-steps");
|
|
@@ -366,7 +395,10 @@ export function renderRunPanels(task, events, snapshot) {
|
|
|
366
395
|
clearNode(outputEl);
|
|
367
396
|
outputEl.appendChild(el("h3", null, "最新输出 (tail)"));
|
|
368
397
|
pre = el("pre", "output-block", nextOutput);
|
|
369
|
-
|
|
398
|
+
const shell = el("div", "run-output-shell");
|
|
399
|
+
shell.appendChild(pre);
|
|
400
|
+
shell.appendChild(buildRunOutputFollowDock(pre));
|
|
401
|
+
outputEl.appendChild(shell);
|
|
370
402
|
} else if (pre.textContent !== nextOutput) {
|
|
371
403
|
pre.textContent = nextOutput;
|
|
372
404
|
}
|
|
@@ -376,6 +408,7 @@ export function renderRunPanels(task, events, snapshot) {
|
|
|
376
408
|
pre.scrollTop = runOutputScrollTop;
|
|
377
409
|
}
|
|
378
410
|
pre.scrollLeft = runOutputScrollLeft;
|
|
411
|
+
pre._syncJumpToBottom?.();
|
|
379
412
|
const runRegionKey = `run:${task.workerRunId ?? task.taskId}:output`;
|
|
380
413
|
uiState.runOutputViewportState = updateDagOutputViewportState(
|
|
381
414
|
uiState.runOutputViewportState,
|
|
@@ -424,12 +457,22 @@ export async function renderRun(workerRunId, initial = true) {
|
|
|
424
457
|
return false;
|
|
425
458
|
}
|
|
426
459
|
|
|
427
|
-
const
|
|
428
|
-
|
|
460
|
+
const generation = uiState.pollingGeneration;
|
|
461
|
+
const [taskResult, snapshot] = await Promise.all([
|
|
462
|
+
fetchJsonResult(`/api/runs/${encodeURIComponent(workerRunId)}`),
|
|
429
463
|
fetchJson("/api/snapshot"),
|
|
430
464
|
]);
|
|
465
|
+
if (generation !== uiState.pollingGeneration) return false;
|
|
466
|
+
const task = taskResult.ok ? taskResult.body : null;
|
|
467
|
+
if (!task && taskResult.status !== 404) {
|
|
468
|
+
const meta = document.getElementById("run-meta");
|
|
469
|
+
let error = meta?.querySelector(".run-refresh-error");
|
|
470
|
+
if (!error && meta) { error = el("p", "run-refresh-error node-input-error"); meta.appendChild(error); }
|
|
471
|
+
if (error) error.textContent = `更新失败,保留上次内容。${summarizeFetchError(taskResult)}`;
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
431
474
|
if (!task) {
|
|
432
|
-
|
|
475
|
+
{
|
|
433
476
|
const runMeta = document.getElementById("run-meta");
|
|
434
477
|
if (runMeta) mountViewState(runMeta, "error", "找不到该运行。");
|
|
435
478
|
}
|
|
@@ -438,7 +481,7 @@ export async function renderRun(workerRunId, initial = true) {
|
|
|
438
481
|
|
|
439
482
|
if (snapshot) {
|
|
440
483
|
uiState.lastSnapshot = snapshot;
|
|
441
|
-
updateHeaderRefresh(snapshot.generatedAt);
|
|
484
|
+
updateHeaderRefresh(snapshot.generatedAt, { mode: TERMINAL_RUN_STATUSES.has(String(task.status ?? "").toLowerCase()) ? "ended" : "auto" });
|
|
442
485
|
}
|
|
443
486
|
|
|
444
487
|
uiState.currentBatchRunId = task.batchRunId ?? uiState.currentBatchRunId;
|