@tea-agent/loop-agent 0.42.0-next.9 → 0.43.0-next.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 +65 -59
- package/dist/application/dag/run-dag.js +3 -2
- package/dist/application/task-lifecycle/advance.js +9 -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 +1514 -175
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/shell-executor.js +131 -54
- package/dist/shared/dag-failure-category.js +6 -0
- package/dist/task/contract/apply.js +36 -2
- package/dist/task/source-prepare/parse-intent.js +7 -0
- package/dist/worker/console/chat/chat-event-store.js +4 -2
- package/dist/worker/console/chat/pi-runtime.js +45 -2
- package/dist/worker/console/chat/resource-loader.js +4 -1
- package/dist/worker/console/chat/routes.js +28 -12
- package/dist/worker/console/chat/sdd-data-alignment.js +13 -2
- package/dist/worker/console/chat/session-store.js +5 -1
- 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/tool-preview.js +10 -0
- package/dist/worker/console/chat/tools.js +11 -1
- package/dist/worker/console/chat/turn-process.js +1 -0
- package/dist/worker/console/interview/tools.js +1 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-B0Q0nClf.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-DCPjC19G.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-CP5n9jmG.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-CmaWi_Wg.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Ct5tcMfZ.js} +1 -1
- package/dist/worker/console/static/assets/channel-DAS07MdS.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-C0osm1Zf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-CDWviED7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-mtSk1-j7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-D5J9PHC7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-Begg4WAa.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-sF1AEy7T.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-BqbsWXZb.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BKJrPcJ6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-CcwNMRho.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-BEJi3iIs.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-DRLgTvVu.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-BoAVG3cs.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-Bzol06hR.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-UmliJM77.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BWLcJkFo.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CgApOm-O.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-Bh3RgTLs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-Dsh-D5nE.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-Dkbbpcpb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-3EBaA3t3.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-Bq2Dc5ok.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-De7y55ha.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-fLiDbQRh.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-Du617mp1.js} +1 -1
- package/dist/worker/console/static/assets/index-C0O48S_P.js +449 -0
- package/dist/worker/console/static/assets/index-CzKf4U8P.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-BwmlF-XP.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-zsGtS59u.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-DTTPTe3f.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BkXg9aP-.js} +1 -1
- package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-7U2ue5IE.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-BUuGHmWO.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-7q6fX0Sv.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-CkQPJN55.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-CCal_tfX.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-CJmww7D-.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-D6ldWJEv.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-VGh33I9e.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CCkDrIjU.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-NC8f5Otb.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-_92ezZdF.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-BdUCGtbP.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-CdD1H8ia.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-Dkdk3oFo.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-CKu2uPPY.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-ClqAnuG8.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-sse-events.js +23 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +32 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +30 -14
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +133 -5
- package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
- 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 +339 -0
- package/dist/worker/observe/static/dag-node-purpose.js +10 -10
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +5 -5
- package/dist/worker/observe/static/styles.css +171 -0
- package/dist/worker/observe/static/views/dag-graph.js +1 -1
- package/dist/worker/observe/static/views/dag-inspector.js +329 -174
- package/dist/worker/observe/static/views/dag.js +16 -12
- package/dist/worker/observe/static/views/session-timeline.js +5 -3
- package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
- package/dist/workflows/dag/dag-retry-schema.js +3 -0
- package/dist/workflows/dag/frontend-contract-facts.js +130 -0
- package/dist/workflows/dag/frontend-design-policy.js +101 -16
- package/dist/workflows/dag/frontend-implementation-contract.js +410 -41
- package/dist/workflows/dag/frontend-plan-render.js +13 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
- package/dist/workflows/dag/frontend-recovery-controller.js +25 -1
- package/dist/workflows/dag/frontend-recovery-plan.js +4 -1
- package/dist/workflows/dag/frontend-recovery-run.js +142 -16
- package/dist/workflows/dag/frontend-risk.js +2 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +17 -1
- package/dist/workflows/dag/frontend-shape.js +16 -6
- package/dist/workflows/dag/frontend-test-execution-evidence.js +48 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +3 -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 +57 -28
- package/dist/workflows/dag/node-execution.js +201 -85
- 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 +90 -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 +220 -18
- package/dist/workflows/dag/structured-output-repair.js +4 -1
- package/dist/workflows/dag/types.js +4 -3
- package/dist/workflows/dag/validate.js +10 -8
- package/dist/workflows/dag/workspace-checkpoint.js +66 -0
- package/docs/operations/README.md +1 -0
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +6 -4
- package/docs/templates/frontend-implementation-contract.schema.json +34 -2
- package/harness.json +1 -1
- package/package.json +4 -5
- package/skills/frontend-plan/SKILL.md +14 -1
- package/skills/frontend-plan/references/decision-contract.md +101 -5
- package/skills/frontend-plan/references/design-decisions.md +32 -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
|
@@ -16,6 +16,12 @@ import {
|
|
|
16
16
|
describeTransferError,
|
|
17
17
|
formatContextTransferReasonList,
|
|
18
18
|
} from "../dag-context-reason-labels.js";
|
|
19
|
+
import {
|
|
20
|
+
describeInspectorMeta,
|
|
21
|
+
inspectorFieldLabel,
|
|
22
|
+
inspectorListValueLabel,
|
|
23
|
+
inspectorValueLabel,
|
|
24
|
+
} from "../dag-inspector-humanize.js";
|
|
19
25
|
import {
|
|
20
26
|
fetchJson,
|
|
21
27
|
fetchSpecEvidenceFile,
|
|
@@ -31,7 +37,6 @@ import {
|
|
|
31
37
|
} from "../api.js";
|
|
32
38
|
import {
|
|
33
39
|
badge,
|
|
34
|
-
formatLedgerSummary,
|
|
35
40
|
formatMs,
|
|
36
41
|
prettyJsonBlock,
|
|
37
42
|
} from "../format.js";
|
|
@@ -72,7 +77,7 @@ import { createCustomSelect } from "../custom-select.js";
|
|
|
72
77
|
// reused inspector shell never keeps stale node closures: every render writes
|
|
73
78
|
// the latest {dagRunId, node} here and tab clicks re-render from the same
|
|
74
79
|
// reference, always resolving the currently selected node identity.
|
|
75
|
-
const inspectorSelection = { dagRunId: null, node: null };
|
|
80
|
+
const inspectorSelection = { dagRunId: null, node: null, run: null };
|
|
76
81
|
|
|
77
82
|
/**
|
|
78
83
|
* 失败状态行统一入口:错误信息中嵌入的已知 reason code 先给中文语义
|
|
@@ -111,34 +116,95 @@ export const ARTIFACT_FILE_PREVIEW_SURFACES = Object.freeze({
|
|
|
111
116
|
revisionSelection: "artifact-revision-current-preview",
|
|
112
117
|
});
|
|
113
118
|
|
|
119
|
+
const INSPECTOR_PAGE_GUIDANCE = Object.freeze({
|
|
120
|
+
overview: {
|
|
121
|
+
eyebrow: "先看结论",
|
|
122
|
+
title: "这个节点做了什么",
|
|
123
|
+
body: "先看当前状态、执行方式和结果来源;需要追细节时,再进入后面的页面。",
|
|
124
|
+
},
|
|
125
|
+
input: {
|
|
126
|
+
eyebrow: "运行前",
|
|
127
|
+
title: "系统原本准备怎么做",
|
|
128
|
+
body: "这里是开始执行前保存的任务说明、依赖关系和读写范围,不代表这些内容后来一定被实际使用。",
|
|
129
|
+
},
|
|
130
|
+
context: {
|
|
131
|
+
eyebrow: "执行时",
|
|
132
|
+
title: "AI 实际拿到了哪些资料",
|
|
133
|
+
body: "这一页只展示系统在下发任务时真正记录到的内容;没有记录时会明确说明,不会用运行前设置代替。",
|
|
134
|
+
},
|
|
135
|
+
timeline: {
|
|
136
|
+
eyebrow: "执行过程",
|
|
137
|
+
title: "这一步实际发生了什么",
|
|
138
|
+
body: "按顺序查看 AI 的回复、工具调用或命令结果;失败时优先看标红步骤和错误输出。",
|
|
139
|
+
},
|
|
140
|
+
"spec-evidence": {
|
|
141
|
+
eyebrow: "需求依据",
|
|
142
|
+
title: "它有没有真的读过需求",
|
|
143
|
+
body: "只有成功读取文件或查询知识库的记录才算证据;模型自己提到文件名不算。",
|
|
144
|
+
},
|
|
145
|
+
output: {
|
|
146
|
+
eyebrow: "执行结果",
|
|
147
|
+
title: "它产出了什么,谁会用",
|
|
148
|
+
body: "先确认文件是否真实生成、能否替换以及后续谁会使用,再查看具体输出内容。",
|
|
149
|
+
},
|
|
150
|
+
lineage: {
|
|
151
|
+
eyebrow: "来源与影响",
|
|
152
|
+
title: "这一步从哪里来,会影响哪里",
|
|
153
|
+
body: "用于判断结果是本次生成还是沿用旧运行,以及替换或重跑会从哪个位置继续。",
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
function buildInspectorPageLead(tab) {
|
|
158
|
+
const copy = INSPECTOR_PAGE_GUIDANCE[tab];
|
|
159
|
+
if (!copy) return null;
|
|
160
|
+
const lead = el("section", "inspector-page-lead");
|
|
161
|
+
lead.dataset.page = tab;
|
|
162
|
+
lead.append(
|
|
163
|
+
el("span", "inspector-page-eyebrow", copy.eyebrow),
|
|
164
|
+
el("h4", null, copy.title),
|
|
165
|
+
el("p", null, copy.body),
|
|
166
|
+
);
|
|
167
|
+
return lead;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function ensureInspectorPageLead(content, tab) {
|
|
171
|
+
const existing = content.querySelector(
|
|
172
|
+
`.inspector-page-lead[data-page="${tab}"]`,
|
|
173
|
+
);
|
|
174
|
+
if (existing) return existing;
|
|
175
|
+
const lead = buildInspectorPageLead(tab);
|
|
176
|
+
if (lead) content.insertBefore(lead, content.firstChild);
|
|
177
|
+
return lead;
|
|
178
|
+
}
|
|
179
|
+
|
|
114
180
|
export const CONTEXT_EXPORT_TARGETS = Object.freeze([
|
|
115
181
|
{
|
|
116
182
|
value: "generic",
|
|
117
|
-
label: "通用
|
|
118
|
-
description: "适合交给其他
|
|
183
|
+
label: "通用 AI 交接包",
|
|
184
|
+
description: "适合交给其他 AI 工具继续处理",
|
|
119
185
|
icon: "ri-robot-2-line",
|
|
120
|
-
contents: "
|
|
186
|
+
contents: "执行时记录 · 文件清单 · 交接说明",
|
|
121
187
|
},
|
|
122
188
|
{
|
|
123
189
|
value: "pi-session",
|
|
124
|
-
label: "Pi
|
|
125
|
-
description: "
|
|
190
|
+
label: "可在 Pi 中恢复的会话",
|
|
191
|
+
description: "如果记录兼容,会附带可直接恢复的会话文件",
|
|
126
192
|
icon: "ri-terminal-box-line",
|
|
127
|
-
contents: "
|
|
193
|
+
contents: "执行时记录 · 文件清单 · 可恢复的 Pi 会话(兼容时)",
|
|
128
194
|
},
|
|
129
195
|
{
|
|
130
196
|
value: "readable",
|
|
131
197
|
label: "可读执行记录",
|
|
132
198
|
description: "便于人工审阅,不承诺完整重现执行",
|
|
133
199
|
icon: "ri-file-list-3-line",
|
|
134
|
-
contents: "
|
|
200
|
+
contents: "执行时记录 · 文件清单 · 可读对话记录",
|
|
135
201
|
},
|
|
136
202
|
{
|
|
137
203
|
value: "pi-handoff",
|
|
138
|
-
label: "Pi
|
|
139
|
-
description: "
|
|
204
|
+
label: "Pi 专用交接包",
|
|
205
|
+
description: "用于把资料交给 Pi 的继续处理流程",
|
|
140
206
|
icon: "ri-share-forward-box-line",
|
|
141
|
-
contents: "
|
|
207
|
+
contents: "执行时记录 · 文件清单 · Pi 交接说明",
|
|
142
208
|
},
|
|
143
209
|
]);
|
|
144
210
|
|
|
@@ -398,7 +464,7 @@ function createPromptRestartPromptCard({
|
|
|
398
464
|
const next = candidate ?? readCandidate?.();
|
|
399
465
|
titleEl.textContent =
|
|
400
466
|
next?.key === "frozen"
|
|
401
|
-
? "
|
|
467
|
+
? "运行开始时的提示词"
|
|
402
468
|
: (next?.label ?? title);
|
|
403
469
|
pre.textContent = next?.prompt ?? "";
|
|
404
470
|
metaEl.textContent = formatPromptRestartMeta(next?.prompt ?? "");
|
|
@@ -415,6 +481,7 @@ function renderDagInspectorForCurrent() {
|
|
|
415
481
|
inspectorSelection.dagRunId,
|
|
416
482
|
inspectorSelection.node,
|
|
417
483
|
false,
|
|
484
|
+
inspectorSelection.run,
|
|
418
485
|
);
|
|
419
486
|
}
|
|
420
487
|
|
|
@@ -628,6 +695,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
628
695
|
restoreInspectorContentScroll(content, dagRunId, nodeId, "spec-evidence");
|
|
629
696
|
return;
|
|
630
697
|
}
|
|
698
|
+
ensureInspectorPageLead(content, "spec-evidence");
|
|
631
699
|
if (!evidence) {
|
|
632
700
|
content.appendChild(el("p", "empty", "无法加载规范证据。"));
|
|
633
701
|
restoreInspectorContentScroll(content, dagRunId, nodeId, "spec-evidence");
|
|
@@ -638,8 +706,8 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
638
706
|
const statusLabels = {
|
|
639
707
|
"spec-read": "已读取规范",
|
|
640
708
|
"source-bound": "已绑定任务源",
|
|
641
|
-
"spec-injected": "
|
|
642
|
-
"search-only": "
|
|
709
|
+
"spec-injected": "已提供阅读能力,但没有读取记录",
|
|
710
|
+
"search-only": "只搜索过,没有读取内容",
|
|
643
711
|
"kb-queried": "已观察知识库查询",
|
|
644
712
|
"no-evidence": "未观察到读取",
|
|
645
713
|
};
|
|
@@ -680,7 +748,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
680
748
|
};
|
|
681
749
|
|
|
682
750
|
appendListSection(
|
|
683
|
-
"
|
|
751
|
+
"提供给节点的技能",
|
|
684
752
|
evidence.skillInjection?.skills,
|
|
685
753
|
(skill) => {
|
|
686
754
|
const item = el("li", null);
|
|
@@ -705,9 +773,9 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
705
773
|
icon.setAttribute("aria-hidden", "true");
|
|
706
774
|
button.append(
|
|
707
775
|
icon,
|
|
708
|
-
document.createTextNode(` ${source.kind}: `),
|
|
776
|
+
document.createTextNode(` ${inspectorListValueLabel(source.kind)}: `),
|
|
709
777
|
el("code", null, source.path),
|
|
710
|
-
document.createTextNode(` ·
|
|
778
|
+
document.createTextNode(` · 内容校验码 ${source.sha256.slice(0, 12)}…`),
|
|
711
779
|
);
|
|
712
780
|
button.addEventListener("click", () =>
|
|
713
781
|
onSpecEvidenceFileClick(
|
|
@@ -767,7 +835,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
767
835
|
item.appendChild(button);
|
|
768
836
|
return item;
|
|
769
837
|
},
|
|
770
|
-
"
|
|
838
|
+
"没有记录到节点读取规范文件。",
|
|
771
839
|
);
|
|
772
840
|
appendListSection(
|
|
773
841
|
`规范检索(${evidence.specSearches?.length ?? 0})`,
|
|
@@ -782,7 +850,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
782
850
|
);
|
|
783
851
|
return item;
|
|
784
852
|
},
|
|
785
|
-
"
|
|
853
|
+
"没有记录到节点检索规范。",
|
|
786
854
|
);
|
|
787
855
|
appendListSection(
|
|
788
856
|
`知识库查询(${evidence.knowledgeBaseQueries?.length ?? 0})`,
|
|
@@ -794,7 +862,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
794
862
|
item.append(icon, document.createTextNode(` ${query.connector}`));
|
|
795
863
|
return item;
|
|
796
864
|
},
|
|
797
|
-
"
|
|
865
|
+
"没有记录到知识库查询;可能未配置知识库连接,或节点没有使用它。",
|
|
798
866
|
);
|
|
799
867
|
|
|
800
868
|
if (
|
|
@@ -809,10 +877,10 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
809
877
|
icon,
|
|
810
878
|
document.createTextNode(
|
|
811
879
|
evidence.status === "source-bound"
|
|
812
|
-
? "
|
|
880
|
+
? "系统已确认任务来源和文件版本,但没有记录到节点成功读取完整文件。仅绑定文件不能证明节点真的读过内容。"
|
|
813
881
|
: evidence.status === "no-evidence"
|
|
814
|
-
? "
|
|
815
|
-
: "
|
|
882
|
+
? "没有记录到节点读取规范。模型总结里提到文件名,不等于节点真正读取了文件。"
|
|
883
|
+
: "已把规范阅读能力提供给节点,但没有记录到它读取规范或查询知识库;只搜索到文件名不等于读过文件内容。",
|
|
816
884
|
),
|
|
817
885
|
);
|
|
818
886
|
content.appendChild(warning);
|
|
@@ -923,7 +991,7 @@ function renderSpecEvidenceDetail(content, dagRunId, nodeId) {
|
|
|
923
991
|
el(
|
|
924
992
|
"span",
|
|
925
993
|
"spec-evidence-detail-hash",
|
|
926
|
-
|
|
994
|
+
`登记的内容校验码 ${String(detail.data.sha256 ?? "").slice(0, 12)}… · 当前文件${detail.data.hashMatch ? "一致" : "不一致"}`,
|
|
927
995
|
),
|
|
928
996
|
);
|
|
929
997
|
} else if (detail.source === "read" && detail.data?.readAt) {
|
|
@@ -969,15 +1037,29 @@ function renderSpecEvidenceDetail(content, dagRunId, nodeId) {
|
|
|
969
1037
|
|
|
970
1038
|
function sourceLabel(source) {
|
|
971
1039
|
if (source === "defaults") return "继承默认值";
|
|
972
|
-
if (source === "schema-default") return "
|
|
973
|
-
if (source === "merged") return "
|
|
1040
|
+
if (source === "schema-default") return "系统默认值";
|
|
1041
|
+
if (source === "merged") return "合并后的值";
|
|
974
1042
|
return null;
|
|
975
1043
|
}
|
|
976
1044
|
|
|
977
1045
|
function appendMetaRow(dl, label, value, source) {
|
|
978
1046
|
if (value == null || value === "") return;
|
|
979
|
-
const
|
|
980
|
-
const
|
|
1047
|
+
const copy = describeInspectorMeta(label, value);
|
|
1048
|
+
const dt = el("dt", null, copy.label);
|
|
1049
|
+
const dd = el("dd", null, copy.value);
|
|
1050
|
+
const labelHelp = [
|
|
1051
|
+
copy.help ? `含义:${copy.help}` : null,
|
|
1052
|
+
copy.rawLabel ? `技术字段:${copy.rawLabel}` : null,
|
|
1053
|
+
].filter(Boolean);
|
|
1054
|
+
if (labelHelp.length > 0) {
|
|
1055
|
+
const tooltipText = labelHelp.join("\n");
|
|
1056
|
+
const help = el("span", "node-input-summary-help", "?");
|
|
1057
|
+
help.tabIndex = 0;
|
|
1058
|
+
help.setAttribute("data-tooltip", tooltipText);
|
|
1059
|
+
help.setAttribute("aria-label", tooltipText);
|
|
1060
|
+
dt.appendChild(help);
|
|
1061
|
+
}
|
|
1062
|
+
if (copy.rawValue) dd.setAttribute("title", `原始值:${copy.rawValue}`);
|
|
981
1063
|
const inherited = sourceLabel(source);
|
|
982
1064
|
if (inherited) {
|
|
983
1065
|
dd.appendChild(document.createTextNode(" "));
|
|
@@ -990,12 +1072,20 @@ function appendMetaRow(dl, label, value, source) {
|
|
|
990
1072
|
function appendCodeList(parent, title, values) {
|
|
991
1073
|
if (!values || values.length === 0) return;
|
|
992
1074
|
const section = el("section", "node-input-section");
|
|
993
|
-
|
|
1075
|
+
const heading = el("h4", null, inspectorFieldLabel(title));
|
|
1076
|
+
if (inspectorFieldLabel(title) !== title) {
|
|
1077
|
+
heading.setAttribute("title", `技术字段:${title}`);
|
|
1078
|
+
}
|
|
1079
|
+
section.appendChild(heading);
|
|
994
1080
|
const list = el("ul", "node-input-code-list");
|
|
995
1081
|
for (const value of values) {
|
|
996
1082
|
const item = document.createElement("li");
|
|
997
1083
|
const code = document.createElement("code");
|
|
998
|
-
|
|
1084
|
+
const rawValue = String(value);
|
|
1085
|
+
code.textContent = inspectorListValueLabel(rawValue);
|
|
1086
|
+
if (code.textContent !== rawValue) {
|
|
1087
|
+
code.setAttribute("title", `原始值:${rawValue}`);
|
|
1088
|
+
}
|
|
999
1089
|
item.appendChild(code);
|
|
1000
1090
|
list.appendChild(item);
|
|
1001
1091
|
}
|
|
@@ -1063,9 +1153,9 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1063
1153
|
section.appendChild(el("h4", null, "任务正文"));
|
|
1064
1154
|
if (data.prompt.source?.path) {
|
|
1065
1155
|
const meta = el("p", "muted");
|
|
1066
|
-
meta.textContent =
|
|
1156
|
+
meta.textContent = `任务说明来源:${data.prompt.source.path}${
|
|
1067
1157
|
data.prompt.source.sha256Prefix
|
|
1068
|
-
?
|
|
1158
|
+
? `(内容校验码 ${data.prompt.source.sha256Prefix}…)`
|
|
1069
1159
|
: ""
|
|
1070
1160
|
}`;
|
|
1071
1161
|
section.appendChild(meta);
|
|
@@ -1085,7 +1175,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1085
1175
|
|
|
1086
1176
|
if (data.executorInput) {
|
|
1087
1177
|
const section = el("section", "node-input-section");
|
|
1088
|
-
section.appendChild(el("h4", null, "
|
|
1178
|
+
section.appendChild(el("h4", null, "将如何执行"));
|
|
1089
1179
|
const dl = el("dl", "node-input-summary");
|
|
1090
1180
|
appendMetaRow(dl, "kind", data.executorInput.kind, null);
|
|
1091
1181
|
if (data.executorInput.modelHint) {
|
|
@@ -1158,7 +1248,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1158
1248
|
|
|
1159
1249
|
if (data.boundaries) {
|
|
1160
1250
|
const section = el("section", "node-input-section");
|
|
1161
|
-
section.appendChild(el("h4", null, "
|
|
1251
|
+
section.appendChild(el("h4", null, "读写范围与输出要求"));
|
|
1162
1252
|
const dl = el("dl", "node-input-summary");
|
|
1163
1253
|
appendMetaRow(dl, "outputMode", data.boundaries.outputMode, null);
|
|
1164
1254
|
appendMetaRow(
|
|
@@ -1169,7 +1259,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1169
1259
|
);
|
|
1170
1260
|
section.appendChild(dl);
|
|
1171
1261
|
if (data.boundaries.outputContract) {
|
|
1172
|
-
section.appendChild(el("h5", null, "
|
|
1262
|
+
section.appendChild(el("h5", null, "输出内容要求"));
|
|
1173
1263
|
section.appendChild(renderMarkdown(data.boundaries.outputContract));
|
|
1174
1264
|
}
|
|
1175
1265
|
appendCodeList(section, "allowedPaths", data.boundaries.allowedPaths);
|
|
@@ -1181,7 +1271,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1181
1271
|
if (data.piExtensionRunFacts) {
|
|
1182
1272
|
const facts = data.piExtensionRunFacts;
|
|
1183
1273
|
const section = el("section", "node-input-section");
|
|
1184
|
-
section.appendChild(el("h4", null, "
|
|
1274
|
+
section.appendChild(el("h4", null, "AI 扩展加载结果"));
|
|
1185
1275
|
if (!facts.exists) {
|
|
1186
1276
|
section.appendChild(
|
|
1187
1277
|
el(
|
|
@@ -1222,7 +1312,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1222
1312
|
|
|
1223
1313
|
if (data.promptFingerprint?.exists) {
|
|
1224
1314
|
const section = el("section", "node-input-section");
|
|
1225
|
-
section.appendChild(el("h4", null, "
|
|
1315
|
+
section.appendChild(el("h4", null, "最终提示内容校验信息"));
|
|
1226
1316
|
const dl = el("dl", "node-input-summary");
|
|
1227
1317
|
appendMetaRow(
|
|
1228
1318
|
dl,
|
|
@@ -1274,6 +1364,7 @@ async function renderDagNodeInput(content, dagRunId, node) {
|
|
|
1274
1364
|
preserveIntent: true,
|
|
1275
1365
|
});
|
|
1276
1366
|
clearNode(content);
|
|
1367
|
+
ensureInspectorPageLead(content, "input");
|
|
1277
1368
|
const loading = el("p", "muted", UI_TEXT.inputLoading);
|
|
1278
1369
|
loading.setAttribute("role", "status");
|
|
1279
1370
|
loading.setAttribute("aria-live", "polite");
|
|
@@ -1293,6 +1384,7 @@ async function renderDagNodeInput(content, dagRunId, node) {
|
|
|
1293
1384
|
}
|
|
1294
1385
|
|
|
1295
1386
|
clearNode(content);
|
|
1387
|
+
ensureInspectorPageLead(content, "input");
|
|
1296
1388
|
const finishInputRender = () => {
|
|
1297
1389
|
restoreInspectorContentScroll(content, dagRunId, nodeId, "input");
|
|
1298
1390
|
uiState.dagNodeInputLoading = false;
|
|
@@ -1355,31 +1447,45 @@ export function frontendGateLedgerLines(input) {
|
|
|
1355
1447
|
if (prewriteResult?.ok && prewriteResult.body) {
|
|
1356
1448
|
const parsed = prewriteResult.body;
|
|
1357
1449
|
if (parsed?.classification) {
|
|
1358
|
-
|
|
1450
|
+
const classifications = {
|
|
1451
|
+
accepted: "通过,可以进入写入阶段",
|
|
1452
|
+
blocked: "未通过,已阻止写入",
|
|
1453
|
+
warning: "有提醒,需要留意",
|
|
1454
|
+
};
|
|
1455
|
+
lines.push(
|
|
1456
|
+
`写入前检查:${classifications[parsed.classification] ?? String(parsed.classification)}`,
|
|
1457
|
+
);
|
|
1359
1458
|
if (parsed.failureCode) {
|
|
1360
|
-
lines.push(
|
|
1459
|
+
lines.push(`问题编号:${String(parsed.failureCode)}`);
|
|
1361
1460
|
}
|
|
1362
1461
|
if (parsed.failureReason) {
|
|
1363
|
-
lines.push(
|
|
1462
|
+
lines.push(`原因:${String(parsed.failureReason)}`);
|
|
1364
1463
|
}
|
|
1365
1464
|
} else {
|
|
1366
|
-
lines.push("
|
|
1465
|
+
lines.push("写入前检查结果无法解析,请查看运行记录。");
|
|
1367
1466
|
}
|
|
1368
1467
|
} else if (prewriteResult && prewriteResult.status === 404) {
|
|
1369
|
-
lines.push("
|
|
1468
|
+
lines.push("写入前检查尚未执行,或没有留下检查结果。");
|
|
1370
1469
|
} else if (prewriteResult) {
|
|
1371
1470
|
lines.push(
|
|
1372
|
-
"
|
|
1471
|
+
"暂时无法读取写入前检查结果;为安全起见,不把它当成已通过。",
|
|
1373
1472
|
);
|
|
1374
1473
|
} else {
|
|
1375
|
-
lines.push("
|
|
1474
|
+
lines.push("写入前检查尚未执行,或没有留下检查结果。");
|
|
1376
1475
|
}
|
|
1377
1476
|
if (ledgerBody && ledgerBody.present === true) {
|
|
1378
|
-
|
|
1477
|
+
const value = (candidate) =>
|
|
1478
|
+
typeof candidate === "number" ? candidate : "未知";
|
|
1479
|
+
lines.push(
|
|
1480
|
+
`需求覆盖记录:原始片段 ${value(ledgerBody.fragmentCount)} 个;整理后的需求 ${value(ledgerBody.canonicalRequirementCount)} 条;待处理 ${value(ledgerBody.unresolvedCount)} 条;冲突 ${value(ledgerBody.conflictCount)} 条。`,
|
|
1481
|
+
);
|
|
1482
|
+
if (ledgerBody.stale === true) {
|
|
1483
|
+
lines.push("提醒:任务来源后来发生过变化,这份需求覆盖记录可能已经过时。");
|
|
1484
|
+
}
|
|
1379
1485
|
} else if (ledgerBody) {
|
|
1380
|
-
lines.push("
|
|
1486
|
+
lines.push("没有需求覆盖记录;可能是任务没有引用资料,或记录尚未生成。不会猜测数量。");
|
|
1381
1487
|
} else if (taskId) {
|
|
1382
|
-
lines.push("
|
|
1488
|
+
lines.push("需求覆盖记录暂时无法查看;不会把读取失败当成没有问题。");
|
|
1383
1489
|
}
|
|
1384
1490
|
return lines;
|
|
1385
1491
|
}
|
|
@@ -1426,7 +1532,7 @@ export async function fetchFrontendGateLedgerData(dagRunId) {
|
|
|
1426
1532
|
async function renderFrontendGateLedger(content, dagRunId) {
|
|
1427
1533
|
const section = el("section", "node-input-section");
|
|
1428
1534
|
section.appendChild(
|
|
1429
|
-
el("h4", null, "
|
|
1535
|
+
el("h4", null, "前端写入前检查与需求覆盖记录"),
|
|
1430
1536
|
);
|
|
1431
1537
|
const status = el("p", "muted", "加载中…");
|
|
1432
1538
|
status.setAttribute("role", "status");
|
|
@@ -1472,11 +1578,11 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1472
1578
|
const heading = el("div", "context-export-heading");
|
|
1473
1579
|
const headingCopy = el("div", null);
|
|
1474
1580
|
headingCopy.appendChild(el("span", "context-export-eyebrow", "上下文交接包"));
|
|
1475
|
-
const title = el("h4", null, "
|
|
1581
|
+
const title = el("h4", null, "把这个节点的资料交给另一个 AI");
|
|
1476
1582
|
title.id = "context-export-title";
|
|
1477
1583
|
headingCopy.appendChild(title);
|
|
1478
1584
|
headingCopy.appendChild(
|
|
1479
|
-
el("p", null, "
|
|
1585
|
+
el("p", null, "选择接收方式和是否脱敏,系统会生成一个带来源校验信息的 ZIP 交接包。"),
|
|
1480
1586
|
);
|
|
1481
1587
|
const invariant = el("div", "context-export-invariant");
|
|
1482
1588
|
const lockIcon = el("i", "ri-lock-2-line");
|
|
@@ -1560,10 +1666,10 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1560
1666
|
ticket.setAttribute("aria-live", "polite");
|
|
1561
1667
|
const ticketTop = el("div", "context-export-ticket-top");
|
|
1562
1668
|
ticketTop.append(
|
|
1563
|
-
el("span", "context-export-ticket-type", "
|
|
1669
|
+
el("span", "context-export-ticket-type", "交接资料 / ZIP"),
|
|
1564
1670
|
el("span", "context-export-ticket-safety", "已脱敏"),
|
|
1565
1671
|
);
|
|
1566
|
-
const ticketTitle = el("strong", "context-export-ticket-title", "通用
|
|
1672
|
+
const ticketTitle = el("strong", "context-export-ticket-title", "通用 AI 交接包");
|
|
1567
1673
|
const ticketRows = el("dl", "context-export-ticket-rows");
|
|
1568
1674
|
const appendTicketRow = (label, value, className) => {
|
|
1569
1675
|
const term = el("dt", null, label);
|
|
@@ -1571,12 +1677,12 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1571
1677
|
ticketRows.append(term, detail);
|
|
1572
1678
|
return detail;
|
|
1573
1679
|
};
|
|
1574
|
-
const recipientValue = appendTicketRow("用途", "适合交给其他
|
|
1680
|
+
const recipientValue = appendTicketRow("用途", "适合交给其他 AI 工具继续处理");
|
|
1575
1681
|
const contentsValue = appendTicketRow("包含", CONTEXT_EXPORT_TARGETS[0].contents, "is-mono");
|
|
1576
1682
|
const protectionValue = appendTicketRow("保护", "自动隐藏匹配到的疑似凭据");
|
|
1577
1683
|
const attemptValue = appendTicketRow(
|
|
1578
1684
|
"来源",
|
|
1579
|
-
`${node.nodeId} ·
|
|
1685
|
+
`${node.nodeId} · 第 ${selectedAttemptNumber(dagRunId, node) ?? 1} 次执行`,
|
|
1580
1686
|
"is-mono",
|
|
1581
1687
|
);
|
|
1582
1688
|
ticket.append(
|
|
@@ -1584,7 +1690,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1584
1690
|
el("span", "context-export-ticket-label", "即将生成"),
|
|
1585
1691
|
ticketTitle,
|
|
1586
1692
|
ticketRows,
|
|
1587
|
-
el("p", "context-export-ticket-foot", "
|
|
1693
|
+
el("p", "context-export-ticket-foot", "附带文件清单和内容校验码,可以核对资料来源与完整性"),
|
|
1588
1694
|
);
|
|
1589
1695
|
workspace.appendChild(ticket);
|
|
1590
1696
|
section.appendChild(workspace);
|
|
@@ -1604,7 +1710,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1604
1710
|
recipientValue.textContent = target.description;
|
|
1605
1711
|
contentsValue.textContent = target.contents;
|
|
1606
1712
|
protectionValue.textContent = profile.description;
|
|
1607
|
-
attemptValue.textContent = `${node.nodeId} ·
|
|
1713
|
+
attemptValue.textContent = `${node.nodeId} · 第 ${selectedAttemptNumber(dagRunId, node) ?? 1} 次执行`;
|
|
1608
1714
|
const safety = ticket.querySelector(".context-export-ticket-safety");
|
|
1609
1715
|
if (safety) {
|
|
1610
1716
|
safety.textContent = profileValue === "raw" ? "原始内容" : "已脱敏";
|
|
@@ -1647,7 +1753,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1647
1753
|
}
|
|
1648
1754
|
submit.disabled = true;
|
|
1649
1755
|
try {
|
|
1650
|
-
setStatus("
|
|
1756
|
+
setStatus("正在生成带来源校验信息的交接包…");
|
|
1651
1757
|
const clientRequestId = `inspect-context-export-${Date.now()}-${Math.random()
|
|
1652
1758
|
.toString(36)
|
|
1653
1759
|
.slice(2, 10)}`;
|
|
@@ -1673,7 +1779,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1673
1779
|
if (operation?.state !== "succeeded" || !operation.result?.downloadUrl) {
|
|
1674
1780
|
throw new Error(operation?.errorMessage ?? "导出未成功完成");
|
|
1675
1781
|
}
|
|
1676
|
-
setStatus("
|
|
1782
|
+
setStatus("交接包已生成,正在安全下载…");
|
|
1677
1783
|
const downloaded = await downloadContextExport(operation.result.downloadUrl);
|
|
1678
1784
|
const objectUrl = URL.createObjectURL(downloaded.blob);
|
|
1679
1785
|
const link = document.createElement("a");
|
|
@@ -1684,7 +1790,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1684
1790
|
link.remove();
|
|
1685
1791
|
URL.revokeObjectURL(objectUrl);
|
|
1686
1792
|
setStatus(
|
|
1687
|
-
`下载完成 · ${operation.result.bundleId} · ${new Intl.NumberFormat("zh-CN").format(operation.result.bytes)}
|
|
1793
|
+
`下载完成 · ${operation.result.bundleId} · ${new Intl.NumberFormat("zh-CN").format(operation.result.bytes)} 字节\n内容校验码 ${operation.result.bundleSha256}`,
|
|
1688
1794
|
);
|
|
1689
1795
|
} catch (error) {
|
|
1690
1796
|
setStatus(transferErrorStatusText(error, "导出未成功完成"), true);
|
|
@@ -1816,14 +1922,14 @@ function createArtifactContentPreview(
|
|
|
1816
1922
|
el(
|
|
1817
1923
|
"span",
|
|
1818
1924
|
null,
|
|
1819
|
-
`${data.mediaType ?? "
|
|
1925
|
+
`${data.mediaType ?? "未知类型"} · ${new Intl.NumberFormat("zh-CN").format(data.sourceBytes ?? 0)} 字节`,
|
|
1820
1926
|
),
|
|
1821
1927
|
];
|
|
1822
1928
|
if (typeof data.sha256 === "string" && data.sha256.length >= 12) {
|
|
1823
1929
|
const sha = el(
|
|
1824
1930
|
"span",
|
|
1825
1931
|
null,
|
|
1826
|
-
` ·
|
|
1932
|
+
` · 内容校验码 ${data.sha256.slice(0, 12)}…`,
|
|
1827
1933
|
);
|
|
1828
1934
|
sha.title = data.sha256;
|
|
1829
1935
|
metaChildren.push(sha);
|
|
@@ -1835,7 +1941,7 @@ function createArtifactContentPreview(
|
|
|
1835
1941
|
el(
|
|
1836
1942
|
"p",
|
|
1837
1943
|
"artifact-content-preview-unavailable",
|
|
1838
|
-
"
|
|
1944
|
+
"该文件类型不适合直接在页面中显示;系统已经核对文件位置、大小和内容校验码。",
|
|
1839
1945
|
),
|
|
1840
1946
|
);
|
|
1841
1947
|
return;
|
|
@@ -1856,7 +1962,7 @@ function createArtifactContentPreview(
|
|
|
1856
1962
|
if (data.redacted) notes.push("疑似凭据已脱敏");
|
|
1857
1963
|
if (data.truncated) {
|
|
1858
1964
|
notes.push(
|
|
1859
|
-
`仅显示前 ${new Intl.NumberFormat("zh-CN").format(data.maxBytes ?? 0)}
|
|
1965
|
+
`仅显示前 ${new Intl.NumberFormat("zh-CN").format(data.maxBytes ?? 0)} 字节`,
|
|
1860
1966
|
);
|
|
1861
1967
|
}
|
|
1862
1968
|
if (notes.length > 0) {
|
|
@@ -1872,19 +1978,57 @@ function createArtifactContentPreview(
|
|
|
1872
1978
|
return { element: details, reset };
|
|
1873
1979
|
}
|
|
1874
1980
|
|
|
1875
|
-
function
|
|
1981
|
+
export function artifactRevisionRunEligibility(run) {
|
|
1982
|
+
// File-level eligibility only says that a file has a known replacement
|
|
1983
|
+
// contract. Creating a derived run has stricter, run-level safeguards.
|
|
1984
|
+
// Keep this check here so the UI does not invite an action the server will
|
|
1985
|
+
// necessarily reject later.
|
|
1986
|
+
if (!run) return { eligible: true };
|
|
1987
|
+
const lifecycle = String(run.lifecycle ?? "").toLowerCase();
|
|
1988
|
+
const status = String(run.status ?? "").toLowerCase();
|
|
1989
|
+
if (lifecycle !== "completed") {
|
|
1990
|
+
return {
|
|
1991
|
+
eligible: false,
|
|
1992
|
+
title: "这次运行还没有完整结束",
|
|
1993
|
+
description:
|
|
1994
|
+
"请等待运行收尾为“已完成”。进行中、暂停中,或尚未收尾的报错,都不能从替换产出继续。",
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
if (!new Set(["failed", "partial_failed"]).has(status)) {
|
|
1998
|
+
return {
|
|
1999
|
+
eligible: false,
|
|
2000
|
+
title: "这次运行不支持替换产出后继续",
|
|
2001
|
+
description:
|
|
2002
|
+
"只有已完整结束且结果为“失败”或“部分失败”的运行,才能换文件后继续。当前运行已经成功完成;文件本身可替换,不代表可以创建后续运行。",
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
return { eligible: true };
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
function buildArtifactRevisionSection(dagRunId, node, run) {
|
|
1876
2009
|
const { section, body } = createDagActionCard({
|
|
1877
|
-
eyebrow: "
|
|
1878
|
-
title: "
|
|
1879
|
-
description: "
|
|
2010
|
+
eyebrow: "替换已有文件",
|
|
2011
|
+
title: "换掉这个节点的产出后继续",
|
|
2012
|
+
description: "上传新文件后,系统会先检查格式并告诉你哪些后续节点需要重做;确认前不会改动原运行。",
|
|
1880
2013
|
icon: "ri-file-transfer-line",
|
|
1881
2014
|
className: "artifact-revision-box",
|
|
1882
2015
|
});
|
|
1883
2016
|
section.dataset.actionSection = "artifact-revision";
|
|
2017
|
+
const runEligibility = artifactRevisionRunEligibility(run);
|
|
2018
|
+
if (!runEligibility.eligible) {
|
|
2019
|
+
body.appendChild(
|
|
2020
|
+
buildDagActionAvailability({
|
|
2021
|
+
tone: "blocked",
|
|
2022
|
+
title: runEligibility.title,
|
|
2023
|
+
description: runEligibility.description,
|
|
2024
|
+
}),
|
|
2025
|
+
);
|
|
2026
|
+
return section;
|
|
2027
|
+
}
|
|
1884
2028
|
body.appendChild(
|
|
1885
2029
|
buildDagActionAvailability({
|
|
1886
|
-
title: "
|
|
1887
|
-
description: "
|
|
2030
|
+
title: "正在检查这个文件能不能安全替换",
|
|
2031
|
+
description: "系统正在读取运行开始前保存的文件关系和替换规则。",
|
|
1888
2032
|
}),
|
|
1889
2033
|
);
|
|
1890
2034
|
void (async () => {
|
|
@@ -1894,7 +2038,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1894
2038
|
body.appendChild(
|
|
1895
2039
|
buildDagActionAvailability({
|
|
1896
2040
|
tone: "blocked",
|
|
1897
|
-
title: "
|
|
2041
|
+
title: "这个运行暂时不能安全替换产出",
|
|
1898
2042
|
description:
|
|
1899
2043
|
"缺少结构化的产出关系,系统无法可靠判断哪些后续节点会受影响,也不会根据文件名或提示词猜测。",
|
|
1900
2044
|
reasonCode:
|
|
@@ -1921,8 +2065,8 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1921
2065
|
body.appendChild(
|
|
1922
2066
|
buildDagActionAvailability({
|
|
1923
2067
|
tone: "ready",
|
|
1924
|
-
title:
|
|
1925
|
-
description: "
|
|
2068
|
+
title: `可以替换 ${eligible.length} 个已生成文件`,
|
|
2069
|
+
description: "原运行不会被修改;确认后系统会另外创建一次新运行。",
|
|
1926
2070
|
}),
|
|
1927
2071
|
);
|
|
1928
2072
|
const form = el("div", "dag-action-form artifact-revision-form");
|
|
@@ -1951,7 +2095,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1951
2095
|
form.appendChild(currentArtifactPreview.element);
|
|
1952
2096
|
artifactSelect.addEventListener("change", currentArtifactPreview.reset);
|
|
1953
2097
|
const fileLabel = el("label", "prompt-restart-field");
|
|
1954
|
-
fileLabel.appendChild(el("span", "dag-action-field-label", "选择新文件(最多 10
|
|
2098
|
+
fileLabel.appendChild(el("span", "dag-action-field-label", "选择新文件(最多 10 MB)"));
|
|
1955
2099
|
const file = document.createElement("input");
|
|
1956
2100
|
file.type = "file";
|
|
1957
2101
|
file.className = "dag-action-file-input";
|
|
@@ -1963,7 +2107,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1963
2107
|
const reason = document.createElement("textarea");
|
|
1964
2108
|
reason.className = "prompt-restart-textarea";
|
|
1965
2109
|
reason.rows = 3;
|
|
1966
|
-
reason.placeholder = "
|
|
2110
|
+
reason.placeholder = "例如:计划遗漏了一个验收条件,需要更正后继续";
|
|
1967
2111
|
reason.dataset.testid = "artifact-revision-reason";
|
|
1968
2112
|
reasonLabel.appendChild(reason);
|
|
1969
2113
|
form.appendChild(reasonLabel);
|
|
@@ -1997,7 +2141,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1997
2141
|
return;
|
|
1998
2142
|
}
|
|
1999
2143
|
if (selectedFile.size > 10 * 1024 * 1024) {
|
|
2000
|
-
setStatus("文件超过 10
|
|
2144
|
+
setStatus("文件超过 10 MB 限制。", true);
|
|
2001
2145
|
return;
|
|
2002
2146
|
}
|
|
2003
2147
|
if (!reason.value.trim()) {
|
|
@@ -2014,7 +2158,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2014
2158
|
reader.readAsDataURL(selectedFile);
|
|
2015
2159
|
});
|
|
2016
2160
|
const candidateBase64 = String(dataUrl).split(",", 2)[1] ?? "";
|
|
2017
|
-
setStatus("
|
|
2161
|
+
setStatus("正在检查新文件格式并对比差异…");
|
|
2018
2162
|
const stageId = `artifact-stage-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2019
2163
|
const staged = await postOperatorAction(
|
|
2020
2164
|
"artifactRevisionStage",
|
|
@@ -2035,7 +2179,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2035
2179
|
if (stageOp?.state !== "succeeded" || !revision?.revisionId) {
|
|
2036
2180
|
throw new Error(stageOp?.errorMessage ?? "修订校验失败");
|
|
2037
2181
|
}
|
|
2038
|
-
setStatus("
|
|
2182
|
+
setStatus("文件检查通过,正在计算哪些后续节点需要重新执行…");
|
|
2039
2183
|
const planId = `artifact-plan-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2040
2184
|
const planned = await postOperatorAction(
|
|
2041
2185
|
"artifactRevisionPlan",
|
|
@@ -2048,17 +2192,23 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2048
2192
|
);
|
|
2049
2193
|
const plan = planOp?.result?.plan;
|
|
2050
2194
|
if (!plan) throw new Error(planOp?.errorMessage ?? "影响预览失败");
|
|
2051
|
-
planHost.appendChild(el("h5", null, "
|
|
2195
|
+
planHost.appendChild(el("h5", null, "替换结果与影响范围"));
|
|
2052
2196
|
planHost.appendChild(
|
|
2053
2197
|
transparencyMetaList([
|
|
2054
|
-
["
|
|
2055
|
-
["
|
|
2056
|
-
[
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
[
|
|
2061
|
-
|
|
2198
|
+
["本次替换编号", revision.revisionId],
|
|
2199
|
+
["新文件校验码", revision.candidate?.sha256],
|
|
2200
|
+
[
|
|
2201
|
+
"文件内容类型",
|
|
2202
|
+
inspectorValueLabel("差异类型", revision.diff?.kind),
|
|
2203
|
+
],
|
|
2204
|
+
[
|
|
2205
|
+
"预计风险",
|
|
2206
|
+
inspectorValueLabel("风险级别", plan.risk),
|
|
2207
|
+
],
|
|
2208
|
+
["最先受影响的节点", plan.impact?.directConsumerNodeIds?.join(", ") || "无"],
|
|
2209
|
+
["会重新执行的节点", plan.impact?.affectedNodeIds?.join(", ") || "无"],
|
|
2210
|
+
["会沿用结果的节点", plan.impact?.importedNodeIds?.join(", ") || "无"],
|
|
2211
|
+
["本次计划校验码", plan.planHash],
|
|
2062
2212
|
]),
|
|
2063
2213
|
);
|
|
2064
2214
|
if (revision.diff?.summary) {
|
|
@@ -2069,7 +2219,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2069
2219
|
}
|
|
2070
2220
|
if (plan.eligible !== true) {
|
|
2071
2221
|
setStatus(
|
|
2072
|
-
|
|
2222
|
+
`暂时不能创建新运行:${formatContextTransferReasonList(plan.blockedReasons) ?? "当前不满足继续运行的条件"}`,
|
|
2073
2223
|
true,
|
|
2074
2224
|
);
|
|
2075
2225
|
return;
|
|
@@ -2084,7 +2234,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2084
2234
|
void (async () => {
|
|
2085
2235
|
execute.disabled = true;
|
|
2086
2236
|
try {
|
|
2087
|
-
setStatus("
|
|
2237
|
+
setStatus("正在创建新运行并重新执行受影响节点;原运行保持不变…");
|
|
2088
2238
|
const executeId = `artifact-execute-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2089
2239
|
const accepted = await postOperatorAction(
|
|
2090
2240
|
"artifactRevisionExecute",
|
|
@@ -2097,7 +2247,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2097
2247
|
},
|
|
2098
2248
|
executeId,
|
|
2099
2249
|
);
|
|
2100
|
-
if (!accepted.ok || !accepted.body?.operationId) throw new Error(accepted.body?.error?.message ?? "
|
|
2250
|
+
if (!accepted.ok || !accepted.body?.operationId) throw new Error(accepted.body?.error?.message ?? "新运行未被接受");
|
|
2101
2251
|
const operation = await pollOperationToTerminal(accepted.body.operationId, setStatus);
|
|
2102
2252
|
const childRunId = operation?.result?.newRunId;
|
|
2103
2253
|
if (childRunId) {
|
|
@@ -2115,17 +2265,17 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2115
2265
|
planHost.appendChild(open);
|
|
2116
2266
|
}
|
|
2117
2267
|
if (operation?.state !== "succeeded" || !operation.result?.newRunId) {
|
|
2118
|
-
throw new Error(operation?.errorMessage ?? operation?.result?.message ?? "
|
|
2268
|
+
throw new Error(operation?.errorMessage ?? operation?.result?.message ?? "新运行失败");
|
|
2119
2269
|
}
|
|
2120
|
-
setStatus(
|
|
2270
|
+
setStatus(`新运行完成:${operation.result.newRunId}`);
|
|
2121
2271
|
} catch (error) {
|
|
2122
|
-
setStatus(transferErrorStatusText(error, "
|
|
2272
|
+
setStatus(transferErrorStatusText(error, "新运行失败"), true);
|
|
2123
2273
|
} finally {
|
|
2124
2274
|
execute.disabled = false;
|
|
2125
2275
|
}
|
|
2126
2276
|
})();
|
|
2127
2277
|
});
|
|
2128
|
-
setStatus("
|
|
2278
|
+
setStatus("影响范围已经锁定;确认时系统会再次检查文件、运行和计划是否有变化。" );
|
|
2129
2279
|
} catch (error) {
|
|
2130
2280
|
setStatus(transferErrorStatusText(error, "影响预览失败"), true);
|
|
2131
2281
|
} finally {
|
|
@@ -2149,20 +2299,26 @@ function buildDagActionIntro(node) {
|
|
|
2149
2299
|
"重跑会创建新的运行;当前运行及其已经保存的事实始终保持不变。",
|
|
2150
2300
|
),
|
|
2151
2301
|
);
|
|
2152
|
-
const
|
|
2302
|
+
const rawState = node.status ?? "unknown";
|
|
2303
|
+
const state = el(
|
|
2304
|
+
"span",
|
|
2305
|
+
"dag-action-node-state",
|
|
2306
|
+
inspectorValueLabel("status", rawState),
|
|
2307
|
+
);
|
|
2308
|
+
if (state.textContent !== rawState) state.setAttribute("title", `原始值:${rawState}`);
|
|
2153
2309
|
intro.append(copy, state);
|
|
2154
2310
|
return intro;
|
|
2155
2311
|
}
|
|
2156
2312
|
|
|
2157
|
-
async function renderPromptRestart(content, dagRunId, node) {
|
|
2313
|
+
async function renderPromptRestart(content, dagRunId, node, run) {
|
|
2158
2314
|
const nodeId = node.nodeId;
|
|
2159
2315
|
clearNode(content);
|
|
2160
2316
|
const intro = buildDagActionIntro(node);
|
|
2161
|
-
const revisionSection = buildArtifactRevisionSection(dagRunId, node);
|
|
2317
|
+
const revisionSection = buildArtifactRevisionSection(dagRunId, node, run);
|
|
2162
2318
|
const { section: promptSection, body: promptBody } = createDagActionCard({
|
|
2163
2319
|
eyebrow: "提示词重跑",
|
|
2164
2320
|
title: "修改失败提示后重新执行",
|
|
2165
|
-
description: "
|
|
2321
|
+
description: "只有执行失败的节点可以修改提示词并从这里继续;其他节点只能查看运行开始前保存的提示词。",
|
|
2166
2322
|
icon: "ri-restart-line",
|
|
2167
2323
|
className: "prompt-rerun-action-box",
|
|
2168
2324
|
});
|
|
@@ -2170,7 +2326,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2170
2326
|
promptBody.appendChild(
|
|
2171
2327
|
buildDagActionAvailability({
|
|
2172
2328
|
title: "正在读取节点提示",
|
|
2173
|
-
description: "
|
|
2329
|
+
description: "检查运行开始前保存的版本,以及每次失败时实际使用的版本。",
|
|
2174
2330
|
}),
|
|
2175
2331
|
);
|
|
2176
2332
|
const exportSection = buildContextExportSection(dagRunId, node);
|
|
@@ -2199,7 +2355,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2199
2355
|
promptBody.appendChild(
|
|
2200
2356
|
buildDagActionAvailability({
|
|
2201
2357
|
tone: "blocked",
|
|
2202
|
-
title: "
|
|
2358
|
+
title: "无法读取这个节点原先保存的提示词",
|
|
2203
2359
|
description: "当前不能安全预览或修改提示词。可以继续使用最下方的上下文导出。",
|
|
2204
2360
|
}),
|
|
2205
2361
|
);
|
|
@@ -2210,7 +2366,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2210
2366
|
if (!frozenBasePrompt) {
|
|
2211
2367
|
promptBody.appendChild(
|
|
2212
2368
|
buildDagActionAvailability({
|
|
2213
|
-
title: "
|
|
2369
|
+
title: "这个节点没有保存原始提示词",
|
|
2214
2370
|
description: "因此不能从这里修改提示词重跑。",
|
|
2215
2371
|
}),
|
|
2216
2372
|
);
|
|
@@ -2236,12 +2392,12 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2236
2392
|
createPromptRestartBanner({
|
|
2237
2393
|
tone: "readonly",
|
|
2238
2394
|
kicker: "只读",
|
|
2239
|
-
title:
|
|
2240
|
-
body: "
|
|
2395
|
+
title: `当前节点:${inspectorValueLabel("status", node.status ?? "unknown")}`,
|
|
2396
|
+
body: "只有执行失败的节点支持编辑提示词并重启。下面可以查看原始提示词,以及历次失败时实际使用的提示词。",
|
|
2241
2397
|
}),
|
|
2242
2398
|
);
|
|
2243
2399
|
const promptCard = createPromptRestartPromptCard({
|
|
2244
|
-
title: "
|
|
2400
|
+
title: "运行开始前保存的提示词",
|
|
2245
2401
|
badge: "只读",
|
|
2246
2402
|
readCandidate: () => selectedCandidate,
|
|
2247
2403
|
preTestId: "prompt-restart-readonly",
|
|
@@ -2253,9 +2409,9 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2253
2409
|
hint:
|
|
2254
2410
|
promptCandidateOptions.length === 1
|
|
2255
2411
|
? historicalCandidatesMissing
|
|
2256
|
-
? "
|
|
2257
|
-
: "
|
|
2258
|
-
: "
|
|
2412
|
+
? "这个较早的运行没有保存失败时实际使用的提示词;新运行会在这里列出。"
|
|
2413
|
+
: "该节点没有保存过失败时使用的提示词。"
|
|
2414
|
+
: "可以切换查看原始版本和历次失败时使用的版本。",
|
|
2259
2415
|
onChange: (nextKey) => {
|
|
2260
2416
|
selectedCandidate =
|
|
2261
2417
|
promptCandidateOptions.find((option) => option.key === nextKey) ??
|
|
@@ -2273,7 +2429,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2273
2429
|
promptBody.appendChild(
|
|
2274
2430
|
buildDagActionAvailability({
|
|
2275
2431
|
tone: "blocked",
|
|
2276
|
-
title: "
|
|
2432
|
+
title: "保存的提示词不完整,无法安全编辑",
|
|
2277
2433
|
description:
|
|
2278
2434
|
"为避免用截断文本覆盖完整提示,请改用 CLI:dag rerun --prompt-override。",
|
|
2279
2435
|
}),
|
|
@@ -2287,11 +2443,11 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2287
2443
|
tone: "edit",
|
|
2288
2444
|
kicker: "可编辑",
|
|
2289
2445
|
title: "从失败节点编辑提示词并重启",
|
|
2290
|
-
body: "
|
|
2446
|
+
body: "原运行的设置和记录保持不变;系统会另建一次使用新提示词的运行。",
|
|
2291
2447
|
}),
|
|
2292
2448
|
);
|
|
2293
2449
|
const promptCard = createPromptRestartPromptCard({
|
|
2294
|
-
title:
|
|
2450
|
+
title: `原始提示词(只读 · ${nodeId})`,
|
|
2295
2451
|
badge: "底稿",
|
|
2296
2452
|
readCandidate: () => selectedCandidate,
|
|
2297
2453
|
preTestId: "prompt-restart-base",
|
|
@@ -2304,10 +2460,10 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2304
2460
|
label: "提示词版本",
|
|
2305
2461
|
hint:
|
|
2306
2462
|
promptCandidateOptions.length > 1
|
|
2307
|
-
? "
|
|
2463
|
+
? "选择某次失败使用的版本后,会载入完整文本供你修改;原始提示词仍可选择只追加补充说明。"
|
|
2308
2464
|
: historicalCandidatesMissing
|
|
2309
|
-
? "
|
|
2310
|
-
: "
|
|
2465
|
+
? "这个较早的运行没有保存失败时实际使用的提示词;新运行会在这里列出。"
|
|
2466
|
+
: "该节点没有保存过失败时使用的提示词;当前只有原始提示词。",
|
|
2311
2467
|
onChange: (nextKey) => {
|
|
2312
2468
|
selectedCandidate =
|
|
2313
2469
|
promptCandidateOptions.find((option) => option.key === nextKey) ??
|
|
@@ -2321,8 +2477,8 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2321
2477
|
picker.hintEl.textContent = selectedCandidate.truncated
|
|
2322
2478
|
? "该候选已截断,仅供查看,不能直接提交重启。"
|
|
2323
2479
|
: selectedCandidate.key === "frozen"
|
|
2324
|
-
? "
|
|
2325
|
-
: "
|
|
2480
|
+
? "原始提示词可以选择追加说明,也可以全文替换。"
|
|
2481
|
+
: "已载入这次失败时使用的提示词;修改完整文本后即可重启。";
|
|
2326
2482
|
applyModeVisibility();
|
|
2327
2483
|
},
|
|
2328
2484
|
});
|
|
@@ -2344,7 +2500,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2344
2500
|
el(
|
|
2345
2501
|
"p",
|
|
2346
2502
|
"prompt-restart-hint",
|
|
2347
|
-
"
|
|
2503
|
+
"这段补充会加在原提示词末尾,不会改写原文(内部记录为 operator_addendum)。",
|
|
2348
2504
|
),
|
|
2349
2505
|
);
|
|
2350
2506
|
const addendumArea = document.createElement("textarea");
|
|
@@ -2379,14 +2535,14 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2379
2535
|
applyModeVisibility();
|
|
2380
2536
|
|
|
2381
2537
|
const reasonLabel = el("label", "prompt-restart-field");
|
|
2382
|
-
reasonLabel.appendChild(el("span", "prompt-restart-label", "
|
|
2538
|
+
reasonLabel.appendChild(el("span", "prompt-restart-label", "为什么要重启?"));
|
|
2383
2539
|
reasonLabel.appendChild(
|
|
2384
|
-
el("p", "prompt-restart-hint", "
|
|
2540
|
+
el("p", "prompt-restart-hint", "这段说明会随新运行一起保存,方便以后追溯。"),
|
|
2385
2541
|
);
|
|
2386
2542
|
const reasonArea = document.createElement("textarea");
|
|
2387
2543
|
reasonArea.className = "prompt-restart-textarea prompt-restart-textarea-compact";
|
|
2388
2544
|
reasonArea.rows = 3;
|
|
2389
|
-
reasonArea.value =
|
|
2545
|
+
reasonArea.value = `修改失败节点 ${nodeId} 的提示词后重新运行(来源运行:${dagRunId})`;
|
|
2390
2546
|
reasonArea.setAttribute("data-testid", "prompt-restart-reason");
|
|
2391
2547
|
reasonLabel.appendChild(reasonArea);
|
|
2392
2548
|
box.appendChild(reasonLabel);
|
|
@@ -2398,7 +2554,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2398
2554
|
const submit = document.createElement("button");
|
|
2399
2555
|
submit.type = "button";
|
|
2400
2556
|
submit.className = "prompt-restart-submit";
|
|
2401
|
-
submit.textContent = "
|
|
2557
|
+
submit.textContent = "检查是否可重跑并开始";
|
|
2402
2558
|
submit.setAttribute("data-testid", "prompt-restart-submit");
|
|
2403
2559
|
row.appendChild(status);
|
|
2404
2560
|
row.appendChild(submit);
|
|
@@ -2441,7 +2597,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2441
2597
|
}
|
|
2442
2598
|
submit.disabled = true;
|
|
2443
2599
|
try {
|
|
2444
|
-
setStatus("
|
|
2600
|
+
setStatus("正在检查这次重跑是否安全可行…");
|
|
2445
2601
|
const overrides = [{ nodeId, prompt, mode }];
|
|
2446
2602
|
const planRes = await postOperatorAction("dagRerunPlan", {
|
|
2447
2603
|
runId: dagRunId,
|
|
@@ -2461,9 +2617,9 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2461
2617
|
|
|
2462
2618
|
const est = plan.estimatedExecution;
|
|
2463
2619
|
const estText = est
|
|
2464
|
-
? `(将执行 ${est.nodeCount ?? "?"}
|
|
2620
|
+
? `(将执行 ${est.nodeCount ?? "?"} 个节点,其中 ${est.piCallCount ?? "?"} 个需要 AI 处理)`
|
|
2465
2621
|
: "";
|
|
2466
|
-
setStatus(
|
|
2622
|
+
setStatus(`检查通过${estText},正在创建新运行…`);
|
|
2467
2623
|
const clientRequestId = `inspect-prompt-restart-${Date.now()}-${Math.random()
|
|
2468
2624
|
.toString(36)
|
|
2469
2625
|
.slice(2, 10)}`;
|
|
@@ -2491,21 +2647,21 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2491
2647
|
const newRunId =
|
|
2492
2648
|
op.dagRunId ?? op.result?.newRunId ?? op.runSummary?.runId ?? null;
|
|
2493
2649
|
if (newRunId) {
|
|
2494
|
-
setStatus(
|
|
2650
|
+
setStatus(`重启完成,新运行:${newRunId}`);
|
|
2495
2651
|
const open = document.createElement("button");
|
|
2496
2652
|
open.type = "button";
|
|
2497
2653
|
open.className = "prompt-restart-submit is-secondary";
|
|
2498
|
-
open.textContent = "
|
|
2654
|
+
open.textContent = "打开新运行";
|
|
2499
2655
|
open.addEventListener("click", () => {
|
|
2500
2656
|
navigate(`/dag/${encodeURIComponent(String(newRunId))}`);
|
|
2501
2657
|
});
|
|
2502
2658
|
row.appendChild(open);
|
|
2503
2659
|
} else {
|
|
2504
|
-
setStatus("
|
|
2660
|
+
setStatus("重启完成(暂时无法取得新运行编号,请在 DAG 历史中查看)。");
|
|
2505
2661
|
}
|
|
2506
2662
|
} else if (op) {
|
|
2507
2663
|
setStatus(
|
|
2508
|
-
|
|
2664
|
+
`新运行最终状态:${inspectorValueLabel("status", op.state)}${op.errorMessage ? ` — ${op.errorMessage}` : ""}`,
|
|
2509
2665
|
true,
|
|
2510
2666
|
);
|
|
2511
2667
|
} else {
|
|
@@ -2552,8 +2708,9 @@ function transparencyMetaList(rows) {
|
|
|
2552
2708
|
|
|
2553
2709
|
function renderNodeOverview(content, dagRunId, node) {
|
|
2554
2710
|
const root = el("div", "node-transparency-root");
|
|
2711
|
+
root.appendChild(buildInspectorPageLead("overview"));
|
|
2555
2712
|
root.appendChild(
|
|
2556
|
-
el("p", "node-input-semantics", "
|
|
2713
|
+
el("p", "node-input-semantics", "这里展示系统为当前运行保存的真实记录;字段已转换为易懂名称,悬停可查看原始技术字段。"),
|
|
2557
2714
|
);
|
|
2558
2715
|
const summary = el("section", "node-input-section");
|
|
2559
2716
|
summary.appendChild(el("h4", null, "节点事实"));
|
|
@@ -2571,12 +2728,12 @@ function renderNodeOverview(content, dagRunId, node) {
|
|
|
2571
2728
|
);
|
|
2572
2729
|
root.appendChild(summary);
|
|
2573
2730
|
const semantics = el("section", "node-input-section");
|
|
2574
|
-
semantics.appendChild(el("h4", null, "
|
|
2731
|
+
semantics.appendChild(el("h4", null, "这些信息分别在什么时候产生"));
|
|
2575
2732
|
semantics.appendChild(
|
|
2576
2733
|
el(
|
|
2577
2734
|
"p",
|
|
2578
2735
|
"muted",
|
|
2579
|
-
"
|
|
2736
|
+
"“执行设置”是运行开始前保存的配置;“实际上下文”是下发任务时真正交给节点的内容;“产出”是执行结束后保存的文件。缺少哪个阶段会明确显示,预览内容不会被当成已经发生的事实。",
|
|
2580
2737
|
),
|
|
2581
2738
|
);
|
|
2582
2739
|
root.appendChild(semantics);
|
|
@@ -2591,6 +2748,7 @@ function selectedAttemptNumber(dagRunId, node) {
|
|
|
2591
2748
|
}
|
|
2592
2749
|
|
|
2593
2750
|
async function renderEffectiveContext(content, dagRunId, node) {
|
|
2751
|
+
ensureInspectorPageLead(content, "context");
|
|
2594
2752
|
content.appendChild(el("p", "muted", "正在加载有效上下文事实…"));
|
|
2595
2753
|
const result = await fetchDagNodeContext(
|
|
2596
2754
|
dagRunId,
|
|
@@ -2605,6 +2763,7 @@ async function renderEffectiveContext(content, dagRunId, node) {
|
|
|
2605
2763
|
)
|
|
2606
2764
|
return;
|
|
2607
2765
|
clearNode(content);
|
|
2766
|
+
ensureInspectorPageLead(content, "context");
|
|
2608
2767
|
if (!result.ok || !result.body) {
|
|
2609
2768
|
const error = el(
|
|
2610
2769
|
"p",
|
|
@@ -2628,33 +2787,29 @@ async function renderEffectiveContext(content, dagRunId, node) {
|
|
|
2628
2787
|
);
|
|
2629
2788
|
if (receipt?.status !== "captured") {
|
|
2630
2789
|
const missing = el("div", "node-transparency-omission");
|
|
2790
|
+
const reasonCode = receipt?.reasonCode ?? "CONTEXT_RECEIPT_MISSING";
|
|
2791
|
+
const missingLabel = contextTransferReasonLabel(reasonCode);
|
|
2631
2792
|
missing.appendChild(
|
|
2632
2793
|
el(
|
|
2633
2794
|
"h4",
|
|
2634
2795
|
null,
|
|
2635
|
-
receipt?.status === "invalid" ? "
|
|
2796
|
+
receipt?.status === "invalid" ? "执行时记录不可用" : "没有执行时记录",
|
|
2636
2797
|
),
|
|
2637
2798
|
);
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
);
|
|
2641
|
-
const missingLabel = contextTransferReasonLabel(
|
|
2642
|
-
receipt?.reasonCode ?? "CONTEXT_RECEIPT_MISSING",
|
|
2643
|
-
);
|
|
2644
|
-
if (missingLabel) {
|
|
2645
|
-
missing.appendChild(el("p", "muted", missingLabel));
|
|
2646
|
-
}
|
|
2799
|
+
const reason = el("p", "muted", missingLabel ?? reasonCode);
|
|
2800
|
+
if (missingLabel) reason.setAttribute("title", `技术代码:${reasonCode}`);
|
|
2801
|
+
missing.appendChild(reason);
|
|
2647
2802
|
missing.appendChild(
|
|
2648
2803
|
el(
|
|
2649
2804
|
"p",
|
|
2650
2805
|
"muted",
|
|
2651
|
-
"
|
|
2806
|
+
"当前只有运行前设置和提示内容校验码,无法证明执行时真正收到了哪些内容。较早的运行不会事后补写这份记录。",
|
|
2652
2807
|
),
|
|
2653
2808
|
);
|
|
2654
2809
|
root.appendChild(missing);
|
|
2655
2810
|
} else {
|
|
2656
2811
|
const captured = el("section", "node-input-section");
|
|
2657
|
-
captured.appendChild(el("h4", null, "
|
|
2812
|
+
captured.appendChild(el("h4", null, "这次执行实际收到的内容"));
|
|
2658
2813
|
captured.appendChild(
|
|
2659
2814
|
transparencyMetaList([
|
|
2660
2815
|
["context digest", receipt.contextDigest],
|
|
@@ -2690,21 +2845,20 @@ async function renderEffectiveContext(content, dagRunId, node) {
|
|
|
2690
2845
|
|
|
2691
2846
|
function artifactInventorySection(data, dagRunId, nodeId) {
|
|
2692
2847
|
const root = el("section", "node-input-section node-artifact-inventory");
|
|
2693
|
-
root.appendChild(el("h4", null, "
|
|
2848
|
+
root.appendChild(el("h4", null, "本节点生成和使用的文件"));
|
|
2694
2849
|
if (!data?.binding?.available) {
|
|
2695
2850
|
const warning = el("div", "node-transparency-omission");
|
|
2696
2851
|
const bindingCode =
|
|
2697
2852
|
data?.binding?.reasonCode ?? "ARTIFACT_BINDING_UNAVAILABLE";
|
|
2698
|
-
warning.appendChild(el("code", null, bindingCode));
|
|
2699
2853
|
const bindingLabel = contextTransferReasonLabel(bindingCode);
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2854
|
+
const reason = el("p", "muted", bindingLabel ?? bindingCode);
|
|
2855
|
+
if (bindingLabel) reason.setAttribute("title", `技术代码:${bindingCode}`);
|
|
2856
|
+
warning.appendChild(reason);
|
|
2703
2857
|
warning.appendChild(
|
|
2704
2858
|
el(
|
|
2705
2859
|
"p",
|
|
2706
2860
|
"muted",
|
|
2707
|
-
"
|
|
2861
|
+
"这个较早的运行没有保存明确的文件使用关系;系统不会根据提示词、输出要求或文件名猜测谁会使用它。",
|
|
2708
2862
|
),
|
|
2709
2863
|
);
|
|
2710
2864
|
root.appendChild(warning);
|
|
@@ -2742,7 +2896,7 @@ function artifactInventorySection(data, dagRunId, nodeId) {
|
|
|
2742
2896
|
);
|
|
2743
2897
|
appendCodeList(
|
|
2744
2898
|
card,
|
|
2745
|
-
"
|
|
2899
|
+
"会直接使用此文件的节点",
|
|
2746
2900
|
artifact.directConsumerNodeIds ?? [],
|
|
2747
2901
|
);
|
|
2748
2902
|
if (artifact.materialization?.status === "materialized") {
|
|
@@ -2756,7 +2910,7 @@ function artifactInventorySection(data, dagRunId, nodeId) {
|
|
|
2756
2910
|
}
|
|
2757
2911
|
for (const artifact of data.consumes ?? []) {
|
|
2758
2912
|
const card = el("div", "node-artifact-card is-consumed");
|
|
2759
|
-
card.appendChild(el("h5", null,
|
|
2913
|
+
card.appendChild(el("h5", null, `使用 ${artifact.artifactId}`));
|
|
2760
2914
|
card.appendChild(
|
|
2761
2915
|
transparencyMetaList([
|
|
2762
2916
|
["producer", artifact.producerNodeId],
|
|
@@ -2775,7 +2929,7 @@ function artifactInventorySection(data, dagRunId, nodeId) {
|
|
|
2775
2929
|
(data.produces ?? []).length === 0 &&
|
|
2776
2930
|
(data.consumes ?? []).length === 0
|
|
2777
2931
|
) {
|
|
2778
|
-
root.appendChild(el("p", "empty", "
|
|
2932
|
+
root.appendChild(el("p", "empty", "该节点没有登记会生成或使用的文件。"));
|
|
2779
2933
|
}
|
|
2780
2934
|
return root;
|
|
2781
2935
|
}
|
|
@@ -2797,10 +2951,12 @@ async function prependArtifactInventory(content, dagRunId, node) {
|
|
|
2797
2951
|
"node-input-error node-artifact-inventory",
|
|
2798
2952
|
result.body?.error || "无法加载产物绑定事实。",
|
|
2799
2953
|
);
|
|
2800
|
-
content.
|
|
2954
|
+
const lead = content.querySelector(".inspector-page-lead");
|
|
2955
|
+
content.insertBefore(section, lead?.nextSibling ?? content.firstChild);
|
|
2801
2956
|
}
|
|
2802
2957
|
|
|
2803
2958
|
async function renderRunLineage(content, dagRunId, node) {
|
|
2959
|
+
ensureInspectorPageLead(content, "lineage");
|
|
2804
2960
|
content.appendChild(el("p", "muted", "正在加载来源与影响关系…"));
|
|
2805
2961
|
const result = await fetchDagRunLineage(dagRunId);
|
|
2806
2962
|
if (
|
|
@@ -2811,6 +2967,7 @@ async function renderRunLineage(content, dagRunId, node) {
|
|
|
2811
2967
|
)
|
|
2812
2968
|
return;
|
|
2813
2969
|
clearNode(content);
|
|
2970
|
+
ensureInspectorPageLead(content, "lineage");
|
|
2814
2971
|
if (!result.ok || !result.body) {
|
|
2815
2972
|
content.appendChild(
|
|
2816
2973
|
el("p", "node-input-error", result.body?.error || "无法加载来源与影响关系。"),
|
|
@@ -2819,21 +2976,11 @@ async function renderRunLineage(content, dagRunId, node) {
|
|
|
2819
2976
|
}
|
|
2820
2977
|
const data = result.body;
|
|
2821
2978
|
const root = el("div", "node-transparency-root");
|
|
2822
|
-
const intro = el("section", "node-lineage-intro");
|
|
2823
|
-
intro.append(
|
|
2824
|
-
el("h4", null, "这个节点从哪里来,会影响哪里"),
|
|
2825
|
-
el(
|
|
2826
|
-
"p",
|
|
2827
|
-
"muted",
|
|
2828
|
-
"这里展示派生运行继承、替换或重新执行了哪些节点。产出文件的直接使用者请到“产出”中查看。",
|
|
2829
|
-
),
|
|
2830
|
-
);
|
|
2831
|
-
root.appendChild(intro);
|
|
2832
2979
|
root.appendChild(
|
|
2833
2980
|
transparencyMetaList([
|
|
2834
2981
|
["运行类型", data.kind === "root" ? "首次运行" : "派生运行"],
|
|
2835
2982
|
["来源运行", data.parentRunId ?? null],
|
|
2836
|
-
["发起方式", data.trigger
|
|
2983
|
+
["发起方式", inspectorValueLabel("发起方式", data.trigger)],
|
|
2837
2984
|
["人工选择节点", data.operatorSelectedNodeId ?? null],
|
|
2838
2985
|
["从此节点重新执行", data.effectiveFromNodeId ?? null],
|
|
2839
2986
|
["重跑计划哈希", data.planHash ?? null],
|
|
@@ -3246,7 +3393,7 @@ function renderCachedOrPreviewBody(content, dagRunId, node, key, isLatest) {
|
|
|
3246
3393
|
}
|
|
3247
3394
|
if (cached && cached.ok === false) {
|
|
3248
3395
|
const message =
|
|
3249
|
-
cached.error || "
|
|
3396
|
+
cached.error || "该次执行输出暂不可用(记录文件缺失或无法读取)。";
|
|
3250
3397
|
const error = el("p", "empty", message);
|
|
3251
3398
|
error.setAttribute("role", "status");
|
|
3252
3399
|
content.appendChild(error);
|
|
@@ -3290,7 +3437,7 @@ async function loadAndRenderExecutionOutput(
|
|
|
3290
3437
|
(result.status === 0
|
|
3291
3438
|
? "加载执行输出失败(网络错误)。"
|
|
3292
3439
|
: result.status === 404
|
|
3293
|
-
? "
|
|
3440
|
+
? "该次执行输出暂不可用(记录文件缺失或无法读取)。"
|
|
3294
3441
|
: `加载执行输出失败(HTTP ${result.status})。`);
|
|
3295
3442
|
// Cache the miss for this key only — never wipe other valid bodies.
|
|
3296
3443
|
setDagNodeExecutionOutputCache(dagRunId, nodeId, key, {
|
|
@@ -3328,7 +3475,8 @@ function repaintOutputTabBody(content, dagRunId, node) {
|
|
|
3328
3475
|
// Remove everything after the attempt bar (or all if no bar).
|
|
3329
3476
|
const inventory = content.querySelector(".node-artifact-inventory");
|
|
3330
3477
|
const bar = content.querySelector(".dag-inspector-attempt-bar");
|
|
3331
|
-
const
|
|
3478
|
+
const lead = content.querySelector(".inspector-page-lead");
|
|
3479
|
+
const keep = [lead, inventory, bar].filter(Boolean);
|
|
3332
3480
|
while (content.firstChild) content.removeChild(content.firstChild);
|
|
3333
3481
|
for (const nodeEl of keep) content.appendChild(nodeEl);
|
|
3334
3482
|
|
|
@@ -3394,7 +3542,7 @@ function renderProcessCommandCard(command) {
|
|
|
3394
3542
|
details.appendChild(summary);
|
|
3395
3543
|
const body = el("div", "process-timeline-card-body");
|
|
3396
3544
|
const meta = [
|
|
3397
|
-
command.exitCode != null ?
|
|
3545
|
+
command.exitCode != null ? `退出码 ${command.exitCode}` : null,
|
|
3398
3546
|
command.durationMs != null ? `耗时 ${formatMs(command.durationMs)}` : null,
|
|
3399
3547
|
command.failureCategory ? command.failureCategory : null,
|
|
3400
3548
|
].filter(Boolean);
|
|
@@ -3404,11 +3552,11 @@ function renderProcessCommandCard(command) {
|
|
|
3404
3552
|
);
|
|
3405
3553
|
}
|
|
3406
3554
|
if (command.stdout) {
|
|
3407
|
-
body.appendChild(el("div", "process-timeline-section-label", "
|
|
3555
|
+
body.appendChild(el("div", "process-timeline-section-label", "正常输出"));
|
|
3408
3556
|
appendProcessPre(body, command.stdout);
|
|
3409
3557
|
}
|
|
3410
3558
|
if (command.stderr) {
|
|
3411
|
-
body.appendChild(el("div", "process-timeline-section-label", "
|
|
3559
|
+
body.appendChild(el("div", "process-timeline-section-label", "错误输出"));
|
|
3412
3560
|
appendProcessPre(body, command.stderr);
|
|
3413
3561
|
}
|
|
3414
3562
|
if (command.truncated) {
|
|
@@ -3619,7 +3767,7 @@ function renderExecutorAwareTimeline(content, dagRunId, node) {
|
|
|
3619
3767
|
});
|
|
3620
3768
|
}
|
|
3621
3769
|
|
|
3622
|
-
function fillDagInspectorContent(content, dagRunId, node) {
|
|
3770
|
+
function fillDagInspectorContent(content, dagRunId, node, run) {
|
|
3623
3771
|
const previousRenderedNode = content.dataset.dagNodeId ?? "";
|
|
3624
3772
|
const previousRenderedRun = content.dataset.dagRunId ?? "";
|
|
3625
3773
|
const previousTab = content.dataset.inspectorTab ?? "";
|
|
@@ -3657,6 +3805,9 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
3657
3805
|
content.dataset.inspectorTab = activeTab;
|
|
3658
3806
|
// Drop timeline-only classes when switching tabs so output layout stays clean.
|
|
3659
3807
|
content.className = "dag-inspector-content";
|
|
3808
|
+
if (activeTab !== "prompt-restart") {
|
|
3809
|
+
content.classList.add("is-readable-page");
|
|
3810
|
+
}
|
|
3660
3811
|
ensureInspectorContentScrollCapture(
|
|
3661
3812
|
content,
|
|
3662
3813
|
dagRunId,
|
|
@@ -3670,10 +3821,12 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
3670
3821
|
} else if (activeTab === "lineage") {
|
|
3671
3822
|
void renderRunLineage(content, dagRunId, node);
|
|
3672
3823
|
} else if (activeTab === "spec-evidence") {
|
|
3824
|
+
ensureInspectorPageLead(content, "spec-evidence");
|
|
3673
3825
|
void renderSpecEvidence(content, dagRunId, node.nodeId);
|
|
3674
3826
|
} else if (activeTab === "prompt-restart") {
|
|
3675
|
-
void renderPromptRestart(content, dagRunId, node);
|
|
3827
|
+
void renderPromptRestart(content, dagRunId, node, run);
|
|
3676
3828
|
} else if (activeTab === "timeline") {
|
|
3829
|
+
ensureInspectorPageLead(content, "timeline");
|
|
3677
3830
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
3678
3831
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
3679
3832
|
renderExecutorAwareTimeline(content, dagRunId, node);
|
|
@@ -3684,6 +3837,7 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
3684
3837
|
void renderDagNodeInput(content, dagRunId, node);
|
|
3685
3838
|
} else {
|
|
3686
3839
|
// output tab: attempt bar + latest preview immediate / historical async body.
|
|
3840
|
+
ensureInspectorPageLead(content, "output");
|
|
3687
3841
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
3688
3842
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
3689
3843
|
const key = effectiveExecutionKey(dagRunId, node);
|
|
@@ -3884,12 +4038,13 @@ function dagInspectorSignature(dagRunId, node) {
|
|
|
3884
4038
|
]);
|
|
3885
4039
|
}
|
|
3886
4040
|
|
|
3887
|
-
export function renderDagInspector(dagRunId, node, initial = true) {
|
|
4041
|
+
export function renderDagInspector(dagRunId, node, initial = true, run = null) {
|
|
3888
4042
|
// Record the current selection before any early-return so tab handlers
|
|
3889
4043
|
// (which reference the mutable inspectorSelection slot) always re-render
|
|
3890
4044
|
// the currently selected node, never a stale build-time closure.
|
|
3891
4045
|
inspectorSelection.dagRunId = dagRunId;
|
|
3892
4046
|
inspectorSelection.node = node;
|
|
4047
|
+
inspectorSelection.run = run;
|
|
3893
4048
|
const panel = document.getElementById("dag-inspector");
|
|
3894
4049
|
if (!panel) return;
|
|
3895
4050
|
captureDagTimelineViewportState(panel);
|
|
@@ -3939,7 +4094,7 @@ export function renderDagInspector(dagRunId, node, initial = true) {
|
|
|
3939
4094
|
scrollTop: pre.scrollTop,
|
|
3940
4095
|
}));
|
|
3941
4096
|
clearNode(existingContent);
|
|
3942
|
-
fillDagInspectorContent(existingContent, dagRunId, node);
|
|
4097
|
+
fillDagInspectorContent(existingContent, dagRunId, node, run);
|
|
3943
4098
|
// Async tabs restore themselves after fetch; sync tabs restore here.
|
|
3944
4099
|
if (activeTab === "output" || activeTab === "timeline") {
|
|
3945
4100
|
restoreInspectorContentScroll(
|
|
@@ -3970,7 +4125,7 @@ export function renderDagInspector(dagRunId, node, initial = true) {
|
|
|
3970
4125
|
panel.appendChild(buildDagInspectorHeader(dagRunId, node));
|
|
3971
4126
|
|
|
3972
4127
|
const content = el("div", "dag-inspector-content");
|
|
3973
|
-
fillDagInspectorContent(content, dagRunId, node);
|
|
4128
|
+
fillDagInspectorContent(content, dagRunId, node, run);
|
|
3974
4129
|
panel.appendChild(content);
|
|
3975
4130
|
panel.dataset.contentSignature = nextContentSignature;
|
|
3976
4131
|
}
|