@tea-agent/loop-agent 0.38.3 → 0.38.4
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/AGENTS.md +4 -1
- package/CHANGELOG.md +153 -96
- package/README.md +9 -5
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/executors/dag-pi-executor.js +18 -9
- package/dist/executors/pi-playwright-cli-tool.js +51 -12
- package/dist/executors/shell-executor.js +209 -13
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/operator/capabilities.js +32 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/operator-actions.js +116 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +36 -5
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-gauhD9kD.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-pm3c2Rsl.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-D_JAg3wH.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-Dk7-ex52.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-Uval9oRI.js} +1 -1
- package/dist/worker/console/static/assets/channel-CVrnUdZl.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-BSYjHs-v.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-KGUuBXI8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-JqAtxD9N.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-D6EJOVQK.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-B1JriSjm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-D62b96Jd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-BsNKEC_r.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-B56v6-ZP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-D2KPnd8K.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-kzKSCaaL.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-C6N6KNJd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-DiyymT2D.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-BeKAXvf3.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-TkIdXfPF.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-BFmSG3ks.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-DqpJT2Fs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-r5ATN43t.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-DEcSCMuR.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-VOvYRLVc.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-D7f4Cb1u.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-ziUf3U0G.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-BoRdQ-1y.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-B1-GncNm.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-CTW-ph8W.js} +1 -1
- package/dist/worker/console/static/assets/{index-Ya5FE7cD.css → index-C8rOIAJr.css} +1 -1
- package/dist/worker/console/static/assets/{index-DTZOKgAn.js → index-DEbyrEfW.js} +71 -71
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-BFA337tZ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-BtEujgRR.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-C8ZwD2Jp.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-Ck-z8B11.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-CAyXjQjN.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-CcWKn6tS.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-C2NuDO7E.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-BFub3PDz.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-BJx5THZ5.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-DhalkDWV.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-CVcBM36o.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-D_vy4n0w.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-DGYdJL1D.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-C1xJFg3k.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-D_Fc43df.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-BhGbIjbu.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DUt1lyFL.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-DVkNcO-V.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BM6SGr7j.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-BQU9ufV0.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-BCQhRmYB.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-DWerOyHO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-DxLeebQU.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/observe/node-input.js +2 -0
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +11 -0
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +561 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +60 -4
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +20 -2
- package/dist/workflows/dag/failure-category.js +8 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +5 -1
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +26 -2
- package/dist/workflows/dag/frontend-test-case-manifest.js +5 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +151 -11
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +125 -24
- package/dist/workflows/dag/node-execution.js +20 -2
- package/dist/workflows/dag/rerun-plan.js +112 -0
- package/dist/workflows/dag/rerun-run.js +58 -6
- package/dist/workflows/dag/retry-policy.js +91 -0
- package/dist/workflows/dag/types.js +9 -1
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +2 -2
- package/package.json +5 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +22 -0
- package/skills/playwright-cli-case-generator/SKILL.md +20 -1
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
|
@@ -11,14 +11,22 @@ import {
|
|
|
11
11
|
} from "../constants.js";
|
|
12
12
|
import { clearNode, el } from "../dom.js";
|
|
13
13
|
import { attemptFailureLabel } from "../dag-history-labels.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import {
|
|
15
|
+
fetchJson,
|
|
16
|
+
fetchSpecEvidenceFile,
|
|
17
|
+
fetchDagNodeInput,
|
|
18
|
+
fetchDagNodeProcess,
|
|
19
|
+
postOperatorAction,
|
|
20
|
+
fetchOperatorOperation,
|
|
21
|
+
} from "../api.js";
|
|
22
|
+
import { badge, formatMs, prettyJsonBlock } from "../format.js";
|
|
23
|
+
import { renderMarkdown, renderInspectorDocument } from "../markdown-render.js";
|
|
17
24
|
import {
|
|
18
25
|
uiState,
|
|
19
26
|
makeSessionEventIdentity,
|
|
20
27
|
makeDagNodeInputIdentity,
|
|
21
28
|
makeDagNodeExecutionOutputIdentity,
|
|
29
|
+
makeDagNodeProcessIdentity,
|
|
22
30
|
getDagNodeExecutionOutputCache,
|
|
23
31
|
setDagNodeExecutionOutputCache,
|
|
24
32
|
clearDagNodeExecutionOutputCache,
|
|
@@ -68,6 +76,10 @@ function resetInspectorNodeSelection(dagRunId, nodeId) {
|
|
|
68
76
|
uiState.dagNodeInputData = null;
|
|
69
77
|
uiState.dagNodeInputError = null;
|
|
70
78
|
uiState.dagNodeInputLoading = false;
|
|
79
|
+
uiState.dagNodeProcessData = null;
|
|
80
|
+
uiState.dagNodeProcessError = null;
|
|
81
|
+
uiState.dagNodeProcessLoading = false;
|
|
82
|
+
uiState.dagNodeProcessIdentity = null;
|
|
71
83
|
// Drop execution-output cache/identity so node A's bodies never leak into B.
|
|
72
84
|
clearDagNodeExecutionOutputCache();
|
|
73
85
|
// Bump the session-event identity token so any in-flight response for the
|
|
@@ -748,6 +760,12 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
748
760
|
(shell.gates ?? []).join(", ") || "(none)",
|
|
749
761
|
null,
|
|
750
762
|
);
|
|
763
|
+
appendMetaRow(
|
|
764
|
+
shellDl,
|
|
765
|
+
"handlers",
|
|
766
|
+
(shell.handlers ?? []).join(", ") || "(none)",
|
|
767
|
+
null,
|
|
768
|
+
);
|
|
751
769
|
section.appendChild(shellDl);
|
|
752
770
|
if ((shell.commands ?? []).length > 0) {
|
|
753
771
|
const list = el("ol", "node-input-command-list");
|
|
@@ -967,8 +985,304 @@ async function renderDagNodeInput(content, dagRunId, node) {
|
|
|
967
985
|
finishInputRender();
|
|
968
986
|
}
|
|
969
987
|
|
|
988
|
+
/**
|
|
989
|
+
* Experimental prompt-override rerun tab. Only reachable when the server
|
|
990
|
+
* injected the mutation token (switch on). Every node shows its frozen
|
|
991
|
+
* subtask_prompt here; only FAILED (ERROR) nodes additionally get the editor +
|
|
992
|
+
* restart flow: preflight (dagRerunPlan) binds the edit into planHash, dagRerun
|
|
993
|
+
* executes it as a Console operation, and on success we jump to the new run.
|
|
994
|
+
*/
|
|
995
|
+
async function renderPromptRestart(content, dagRunId, node) {
|
|
996
|
+
const nodeId = node.nodeId;
|
|
997
|
+
clearNode(content);
|
|
998
|
+
|
|
999
|
+
const loading = el("p", "muted", "正在加载冻结提示词…");
|
|
1000
|
+
loading.setAttribute("role", "status");
|
|
1001
|
+
loading.setAttribute("aria-live", "polite");
|
|
1002
|
+
content.appendChild(loading);
|
|
1003
|
+
const input = await fetchDagNodeInput(dagRunId, nodeId);
|
|
1004
|
+
if (
|
|
1005
|
+
!content.isConnected ||
|
|
1006
|
+
content.dataset.dagNodeId !== String(nodeId) ||
|
|
1007
|
+
content.dataset.dagRunId !== String(dagRunId) ||
|
|
1008
|
+
activeInspectorTab() !== "prompt-restart"
|
|
1009
|
+
) {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
clearNode(content);
|
|
1013
|
+
|
|
1014
|
+
if (!input.ok || !input.body?.available) {
|
|
1015
|
+
const error = el("p", "node-input-error", "无法读取该节点的冻结提示词。");
|
|
1016
|
+
error.setAttribute("role", "alert");
|
|
1017
|
+
content.appendChild(error);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const basePrompt =
|
|
1021
|
+
typeof input.body.prompt?.text === "string" ? input.body.prompt.text : "";
|
|
1022
|
+
if (!basePrompt) {
|
|
1023
|
+
const error = el("p", "node-input-error", "该节点没有冻结提示词。");
|
|
1024
|
+
error.setAttribute("role", "alert");
|
|
1025
|
+
content.appendChild(error);
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (node.status !== "ERROR") {
|
|
1030
|
+
// Non-failed nodes still show their frozen prompt — read-only.
|
|
1031
|
+
const hint = el(
|
|
1032
|
+
"p",
|
|
1033
|
+
"muted",
|
|
1034
|
+
`当前节点状态为 ${node.status ?? "unknown"};仅失败(ERROR)节点支持编辑提示词并重启,以下为该节点冻结提示词(只读)。`,
|
|
1035
|
+
);
|
|
1036
|
+
hint.setAttribute("role", "status");
|
|
1037
|
+
content.appendChild(hint);
|
|
1038
|
+
const pre = document.createElement("pre");
|
|
1039
|
+
pre.className = "prompt-restart-readonly";
|
|
1040
|
+
pre.setAttribute("data-testid", "prompt-restart-readonly");
|
|
1041
|
+
pre.textContent = basePrompt;
|
|
1042
|
+
content.appendChild(pre);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (input.body.prompt?.truncated === true) {
|
|
1047
|
+
const error = el(
|
|
1048
|
+
"p",
|
|
1049
|
+
"node-input-error",
|
|
1050
|
+
"冻结提示词被截断,为避免误替换请改用 CLI(dag rerun --prompt-override)。",
|
|
1051
|
+
);
|
|
1052
|
+
error.setAttribute("role", "alert");
|
|
1053
|
+
content.appendChild(error);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
const box = el("div", "prompt-restart-box");
|
|
1058
|
+
box.appendChild(
|
|
1059
|
+
el(
|
|
1060
|
+
"p",
|
|
1061
|
+
"muted",
|
|
1062
|
+
"编辑该失败节点的提示词并从它重启:父 run spec 保持冻结,将创建带修改提示词的续跑 run(实验能力,仅 ERROR 节点)。",
|
|
1063
|
+
),
|
|
1064
|
+
);
|
|
1065
|
+
|
|
1066
|
+
const modeRow = el("div", "prompt-restart-mode");
|
|
1067
|
+
modeRow.appendChild(el("span", "muted", "编辑模式"));
|
|
1068
|
+
const modeSelect = document.createElement("select");
|
|
1069
|
+
modeSelect.className = "prompt-restart-mode-select";
|
|
1070
|
+
modeSelect.setAttribute("data-testid", "prompt-restart-mode");
|
|
1071
|
+
const appendOption = document.createElement("option");
|
|
1072
|
+
appendOption.value = "append";
|
|
1073
|
+
appendOption.textContent =
|
|
1074
|
+
"追加指令(推荐):保留原提示词,在末尾追加 operator_addendum 块";
|
|
1075
|
+
const replaceOption = document.createElement("option");
|
|
1076
|
+
replaceOption.value = "replace";
|
|
1077
|
+
replaceOption.textContent = "全文替换(高级):替换整个提示词";
|
|
1078
|
+
modeSelect.appendChild(appendOption);
|
|
1079
|
+
modeSelect.appendChild(replaceOption);
|
|
1080
|
+
modeSelect.value = "append";
|
|
1081
|
+
modeRow.appendChild(modeSelect);
|
|
1082
|
+
box.appendChild(modeRow);
|
|
1083
|
+
|
|
1084
|
+
const baseLabel = el("label", "prompt-restart-field");
|
|
1085
|
+
baseLabel.appendChild(
|
|
1086
|
+
el("span", "muted", `冻结提示词(只读底稿 · ${nodeId})`),
|
|
1087
|
+
);
|
|
1088
|
+
const baseBlock = document.createElement("pre");
|
|
1089
|
+
baseBlock.className = "prompt-restart-readonly";
|
|
1090
|
+
baseBlock.setAttribute("data-testid", "prompt-restart-base");
|
|
1091
|
+
baseBlock.textContent = basePrompt;
|
|
1092
|
+
baseLabel.appendChild(baseBlock);
|
|
1093
|
+
box.appendChild(baseLabel);
|
|
1094
|
+
|
|
1095
|
+
const addendumLabel = el("label", "prompt-restart-field");
|
|
1096
|
+
addendumLabel.appendChild(
|
|
1097
|
+
el(
|
|
1098
|
+
"span",
|
|
1099
|
+
"muted",
|
|
1100
|
+
"追加指令(将包裹为 <operator_addendum> 块附加到原提示词末尾)",
|
|
1101
|
+
),
|
|
1102
|
+
);
|
|
1103
|
+
const addendumArea = document.createElement("textarea");
|
|
1104
|
+
addendumArea.className = "prompt-restart-textarea";
|
|
1105
|
+
addendumArea.rows = 6;
|
|
1106
|
+
addendumArea.placeholder = "例如:重点检查 X 边界;输出前先自检 Y 约束…";
|
|
1107
|
+
addendumArea.setAttribute("data-testid", "prompt-restart-addendum");
|
|
1108
|
+
addendumLabel.appendChild(addendumArea);
|
|
1109
|
+
box.appendChild(addendumLabel);
|
|
1110
|
+
|
|
1111
|
+
const replaceLabel = el("label", "prompt-restart-field");
|
|
1112
|
+
replaceLabel.appendChild(el("span", "muted", `完整提示词(${nodeId})`));
|
|
1113
|
+
const replaceArea = document.createElement("textarea");
|
|
1114
|
+
replaceArea.className = "prompt-restart-textarea";
|
|
1115
|
+
replaceArea.rows = 16;
|
|
1116
|
+
replaceArea.value = basePrompt;
|
|
1117
|
+
replaceArea.setAttribute("data-testid", "prompt-restart-textarea");
|
|
1118
|
+
replaceLabel.appendChild(replaceArea);
|
|
1119
|
+
box.appendChild(replaceLabel);
|
|
1120
|
+
|
|
1121
|
+
const applyModeVisibility = () => {
|
|
1122
|
+
const append = modeSelect.value === "append";
|
|
1123
|
+
baseLabel.style.display = append ? "" : "none";
|
|
1124
|
+
addendumLabel.style.display = append ? "" : "none";
|
|
1125
|
+
replaceLabel.style.display = append ? "none" : "";
|
|
1126
|
+
};
|
|
1127
|
+
modeSelect.addEventListener("change", applyModeVisibility);
|
|
1128
|
+
applyModeVisibility();
|
|
1129
|
+
|
|
1130
|
+
const reasonLabel = el("label", "prompt-restart-field");
|
|
1131
|
+
reasonLabel.appendChild(el("span", "muted", "重启原因(结构化说明)"));
|
|
1132
|
+
const reasonArea = document.createElement("textarea");
|
|
1133
|
+
reasonArea.className = "prompt-restart-textarea";
|
|
1134
|
+
reasonArea.rows = 3;
|
|
1135
|
+
reasonArea.value = `operator-prompt-override / run=${dagRunId} / node=${nodeId} / 手工编辑失败节点提示词后重启`;
|
|
1136
|
+
reasonArea.setAttribute("data-testid", "prompt-restart-reason");
|
|
1137
|
+
reasonLabel.appendChild(reasonArea);
|
|
1138
|
+
box.appendChild(reasonLabel);
|
|
1139
|
+
|
|
1140
|
+
const row = el("div", "prompt-restart-actions");
|
|
1141
|
+
const submit = document.createElement("button");
|
|
1142
|
+
submit.type = "button";
|
|
1143
|
+
submit.className = "prompt-restart-submit";
|
|
1144
|
+
submit.textContent = "资格预检并重启";
|
|
1145
|
+
submit.setAttribute("data-testid", "prompt-restart-submit");
|
|
1146
|
+
row.appendChild(submit);
|
|
1147
|
+
box.appendChild(row);
|
|
1148
|
+
|
|
1149
|
+
const status = el("p", "prompt-restart-status", "");
|
|
1150
|
+
status.setAttribute("role", "status");
|
|
1151
|
+
status.setAttribute("aria-live", "polite");
|
|
1152
|
+
box.appendChild(status);
|
|
1153
|
+
|
|
1154
|
+
content.appendChild(box);
|
|
1155
|
+
|
|
1156
|
+
const setStatus = (text, isError = false) => {
|
|
1157
|
+
status.textContent = text ?? "";
|
|
1158
|
+
status.classList.toggle("prompt-restart-error", isError);
|
|
1159
|
+
};
|
|
1160
|
+
|
|
1161
|
+
submit.addEventListener("click", () => {
|
|
1162
|
+
void (async () => {
|
|
1163
|
+
const mode = modeSelect.value === "replace" ? "replace" : "append";
|
|
1164
|
+
const prompt =
|
|
1165
|
+
mode === "append" ? addendumArea.value : replaceArea.value;
|
|
1166
|
+
const reason = reasonArea.value.trim();
|
|
1167
|
+
if (!prompt.trim()) {
|
|
1168
|
+
setStatus(
|
|
1169
|
+
mode === "append" ? "追加指令不能为空。" : "提示词不能为空。",
|
|
1170
|
+
true,
|
|
1171
|
+
);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
if (mode === "replace" && prompt === basePrompt) {
|
|
1175
|
+
setStatus("提示词未修改;如需按原提示词重跑请使用恢复工作台。", true);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
if (!reason) {
|
|
1179
|
+
setStatus("需要填写重启原因。", true);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
submit.disabled = true;
|
|
1183
|
+
try {
|
|
1184
|
+
setStatus("正在做重跑资格预检(dagRerunPlan)…");
|
|
1185
|
+
const overrides = [{ nodeId, prompt, mode }];
|
|
1186
|
+
const planRes = await postOperatorAction("dagRerunPlan", {
|
|
1187
|
+
runId: dagRunId,
|
|
1188
|
+
fromNode: nodeId,
|
|
1189
|
+
promptOverrides: overrides,
|
|
1190
|
+
});
|
|
1191
|
+
const plan = planRes.body?.result ?? null;
|
|
1192
|
+
if (!planRes.ok || plan?.eligible !== true || !plan?.planHash) {
|
|
1193
|
+
setStatus(
|
|
1194
|
+
planRes.body?.error?.message ??
|
|
1195
|
+
plan?.blockedReasons?.join(", ") ??
|
|
1196
|
+
"资格预检未通过。",
|
|
1197
|
+
true,
|
|
1198
|
+
);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const est = plan.estimatedExecution;
|
|
1203
|
+
const estText = est
|
|
1204
|
+
? `(将执行 ${est.nodeCount ?? "?"} 个节点 / ${est.piCallCount ?? "?"} 次 Pi 调用)`
|
|
1205
|
+
: "";
|
|
1206
|
+
setStatus(`预检通过${estText},正在提交续跑(dagRerun)…`);
|
|
1207
|
+
const clientRequestId = `inspect-prompt-restart-${Date.now()}-${Math.random()
|
|
1208
|
+
.toString(36)
|
|
1209
|
+
.slice(2, 10)}`;
|
|
1210
|
+
const runRes = await postOperatorAction(
|
|
1211
|
+
"dagRerun",
|
|
1212
|
+
{
|
|
1213
|
+
runId: dagRunId,
|
|
1214
|
+
fromNode: nodeId,
|
|
1215
|
+
planHash: plan.planHash,
|
|
1216
|
+
reason,
|
|
1217
|
+
promptOverrides: overrides,
|
|
1218
|
+
},
|
|
1219
|
+
clientRequestId,
|
|
1220
|
+
);
|
|
1221
|
+
const operationId = runRes.body?.operationId;
|
|
1222
|
+
if (!runRes.ok || !operationId) {
|
|
1223
|
+
setStatus(runRes.body?.error?.message ?? "提交续跑失败。", true);
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const op = await pollOperationToTerminal(operationId, (text) =>
|
|
1228
|
+
setStatus(text),
|
|
1229
|
+
);
|
|
1230
|
+
if (op?.state === "succeeded") {
|
|
1231
|
+
const newRunId =
|
|
1232
|
+
op.dagRunId ?? op.result?.newRunId ?? op.runSummary?.runId ?? null;
|
|
1233
|
+
if (newRunId) {
|
|
1234
|
+
setStatus(`重启完成,新 run:${newRunId}`);
|
|
1235
|
+
const open = document.createElement("button");
|
|
1236
|
+
open.type = "button";
|
|
1237
|
+
open.className = "prompt-restart-submit";
|
|
1238
|
+
open.textContent = "打开新 run";
|
|
1239
|
+
open.addEventListener("click", () => {
|
|
1240
|
+
window.location.hash = `#/dag/${encodeURIComponent(String(newRunId))}`;
|
|
1241
|
+
});
|
|
1242
|
+
row.appendChild(open);
|
|
1243
|
+
} else {
|
|
1244
|
+
setStatus("重启完成(未能解析新 run id,请在 DAG 历史中查看)。");
|
|
1245
|
+
}
|
|
1246
|
+
} else if (op) {
|
|
1247
|
+
setStatus(
|
|
1248
|
+
`续跑操作终态:${op.state}${op.errorMessage ? ` — ${op.errorMessage}` : ""}`,
|
|
1249
|
+
true,
|
|
1250
|
+
);
|
|
1251
|
+
} else {
|
|
1252
|
+
setStatus("等待超时:操作仍在运行,可在操作台查看进度。", true);
|
|
1253
|
+
}
|
|
1254
|
+
} catch (err) {
|
|
1255
|
+
setStatus(err instanceof Error ? err.message : String(err), true);
|
|
1256
|
+
} finally {
|
|
1257
|
+
submit.disabled = false;
|
|
1258
|
+
}
|
|
1259
|
+
})();
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/** Poll a Console operation until a terminal state; null on poll budget end. */
|
|
1264
|
+
async function pollOperationToTerminal(operationId, onProgress) {
|
|
1265
|
+
const terminal = new Set([
|
|
1266
|
+
"succeeded",
|
|
1267
|
+
"failed",
|
|
1268
|
+
"timed-out",
|
|
1269
|
+
"needs-reconcile",
|
|
1270
|
+
]);
|
|
1271
|
+
for (let attempt = 0; attempt < 1200; attempt += 1) {
|
|
1272
|
+
const res = await fetchOperatorOperation(operationId);
|
|
1273
|
+
const op = res.body?.operation ?? res.body;
|
|
1274
|
+
const state = op?.state ?? "";
|
|
1275
|
+
if (res.ok && terminal.has(state)) {
|
|
1276
|
+
return op;
|
|
1277
|
+
}
|
|
1278
|
+
onProgress(`续跑执行中…(${state || "queued"})`);
|
|
1279
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
1280
|
+
}
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
970
1284
|
function activeInspectorTab() {
|
|
971
|
-
return ["input", "timeline", "spec-evidence"].includes(
|
|
1285
|
+
return ["input", "timeline", "spec-evidence", "prompt-restart"].includes(
|
|
972
1286
|
uiState.dagInspectorTab,
|
|
973
1287
|
)
|
|
974
1288
|
? uiState.dagInspectorTab
|
|
@@ -1305,7 +1619,7 @@ function appendOutputBody(content, outputText, errorText, options = null) {
|
|
|
1305
1619
|
content.appendChild(el("p", "empty", UI_TEXT.noOutput));
|
|
1306
1620
|
return;
|
|
1307
1621
|
}
|
|
1308
|
-
if (outputText) content.appendChild(
|
|
1622
|
+
if (outputText) content.appendChild(renderInspectorDocument(outputText));
|
|
1309
1623
|
// Truncation note only for full/cached execution-output bodies (AC-003);
|
|
1310
1624
|
// preview path never invents truncated/maxBytes.
|
|
1311
1625
|
if (options?.truncated === true) {
|
|
@@ -1321,7 +1635,7 @@ function appendOutputBody(content, outputText, errorText, options = null) {
|
|
|
1321
1635
|
if (errorText) {
|
|
1322
1636
|
const error = el("section", "markdown-error");
|
|
1323
1637
|
error.appendChild(el("p", "markdown-error-title", "错误输出"));
|
|
1324
|
-
error.appendChild(
|
|
1638
|
+
error.appendChild(renderInspectorDocument(errorText));
|
|
1325
1639
|
content.appendChild(error);
|
|
1326
1640
|
}
|
|
1327
1641
|
}
|
|
@@ -1455,6 +1769,228 @@ function repaintOutputTabBody(content, dagRunId, node) {
|
|
|
1455
1769
|
restoreInspectorContentScroll(content, dagRunId, node.nodeId, "output");
|
|
1456
1770
|
}
|
|
1457
1771
|
|
|
1772
|
+
function processEmptyMessage(body) {
|
|
1773
|
+
const reason = body?.emptyReason;
|
|
1774
|
+
if (reason === "static-no-process") return UI_TEXT.staticNoProcess;
|
|
1775
|
+
if (reason === "shell-receipt-missing") return UI_TEXT.noShellProcess;
|
|
1776
|
+
if (reason === "shell-preset-pending") return UI_TEXT.noPresetProcess;
|
|
1777
|
+
if (reason === "pi-session") return UI_TEXT.noSessionEvents;
|
|
1778
|
+
return UI_TEXT.noSessionEvents;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
function formatProcessBlock(text) {
|
|
1782
|
+
if (!text) return "";
|
|
1783
|
+
return prettyJsonBlock(text) ?? text;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
function appendProcessPre(parent, text) {
|
|
1787
|
+
if (!text) return;
|
|
1788
|
+
const pre = el("pre", "process-timeline-code");
|
|
1789
|
+
pre.textContent = formatProcessBlock(text);
|
|
1790
|
+
parent.appendChild(pre);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
function renderProcessCommandCard(command) {
|
|
1794
|
+
const failed = command.ok === false;
|
|
1795
|
+
const card = el(
|
|
1796
|
+
"div",
|
|
1797
|
+
`process-timeline-card process-timeline-tool-card${failed ? " is-error" : ""}`,
|
|
1798
|
+
);
|
|
1799
|
+
const details = document.createElement("details");
|
|
1800
|
+
details.className = "process-timeline-details";
|
|
1801
|
+
details.open = failed;
|
|
1802
|
+
const summary = el("summary", "process-timeline-summary");
|
|
1803
|
+
summary.appendChild(
|
|
1804
|
+
el("span", "process-timeline-summary-title", command.command || "command"),
|
|
1805
|
+
);
|
|
1806
|
+
summary.appendChild(
|
|
1807
|
+
el(
|
|
1808
|
+
"span",
|
|
1809
|
+
`process-timeline-summary-status status-${failed ? "failed" : "succeeded"}`,
|
|
1810
|
+
failed ? "失败" : command.reused ? "复用" : "成功",
|
|
1811
|
+
),
|
|
1812
|
+
);
|
|
1813
|
+
details.appendChild(summary);
|
|
1814
|
+
const body = el("div", "process-timeline-card-body");
|
|
1815
|
+
const meta = [
|
|
1816
|
+
command.exitCode != null ? `exit ${command.exitCode}` : null,
|
|
1817
|
+
command.durationMs != null ? `耗时 ${formatMs(command.durationMs)}` : null,
|
|
1818
|
+
command.failureCategory ? command.failureCategory : null,
|
|
1819
|
+
].filter(Boolean);
|
|
1820
|
+
if (meta.length > 0) {
|
|
1821
|
+
body.appendChild(el("div", "process-timeline-section-label", meta.join(" · ")));
|
|
1822
|
+
}
|
|
1823
|
+
if (command.stdout) {
|
|
1824
|
+
body.appendChild(el("div", "process-timeline-section-label", "stdout"));
|
|
1825
|
+
appendProcessPre(body, command.stdout);
|
|
1826
|
+
}
|
|
1827
|
+
if (command.stderr) {
|
|
1828
|
+
body.appendChild(el("div", "process-timeline-section-label", "stderr"));
|
|
1829
|
+
appendProcessPre(body, command.stderr);
|
|
1830
|
+
}
|
|
1831
|
+
if (command.truncated) {
|
|
1832
|
+
body.appendChild(el("p", "muted", "内容过长,已截断。"));
|
|
1833
|
+
}
|
|
1834
|
+
details.appendChild(body);
|
|
1835
|
+
card.appendChild(details);
|
|
1836
|
+
return card;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
function renderProcessStepCard(step) {
|
|
1840
|
+
const failed = step.ok === false;
|
|
1841
|
+
const card = el(
|
|
1842
|
+
"div",
|
|
1843
|
+
`process-timeline-card process-timeline-tool-card${failed ? " is-error" : ""}`,
|
|
1844
|
+
);
|
|
1845
|
+
const details = document.createElement("details");
|
|
1846
|
+
details.className = "process-timeline-details";
|
|
1847
|
+
details.open = true;
|
|
1848
|
+
const summary = el("summary", "process-timeline-summary");
|
|
1849
|
+
summary.appendChild(
|
|
1850
|
+
el("span", "process-timeline-summary-title", step.label || step.id || "step"),
|
|
1851
|
+
);
|
|
1852
|
+
summary.appendChild(
|
|
1853
|
+
el(
|
|
1854
|
+
"span",
|
|
1855
|
+
`process-timeline-summary-status status-${failed ? "failed" : "succeeded"}`,
|
|
1856
|
+
failed ? "失败" : "成功",
|
|
1857
|
+
),
|
|
1858
|
+
);
|
|
1859
|
+
details.appendChild(summary);
|
|
1860
|
+
const body = el("div", "process-timeline-card-body");
|
|
1861
|
+
if (step.durationMs != null) {
|
|
1862
|
+
body.appendChild(
|
|
1863
|
+
el("div", "process-timeline-section-label", `耗时 ${formatMs(step.durationMs)}`),
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1866
|
+
if (step.detail) appendProcessPre(body, step.detail);
|
|
1867
|
+
if (step.truncated) {
|
|
1868
|
+
body.appendChild(el("p", "muted", "内容过长,已截断。"));
|
|
1869
|
+
}
|
|
1870
|
+
details.appendChild(body);
|
|
1871
|
+
card.appendChild(details);
|
|
1872
|
+
return card;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
function renderNonPiProcess(content, body) {
|
|
1876
|
+
content.classList.add("process-timeline-scroll");
|
|
1877
|
+
if (!body || body.emptyReason || ((body.commands ?? []).length === 0 && (body.steps ?? []).length === 0)) {
|
|
1878
|
+
content.appendChild(el("p", "empty", processEmptyMessage(body)));
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const heading =
|
|
1882
|
+
body.kind === "shell-preset"
|
|
1883
|
+
? UI_TEXT.shellPresetHeading
|
|
1884
|
+
: UI_TEXT.shellCommandsHeading;
|
|
1885
|
+
if (body.handler) {
|
|
1886
|
+
content.appendChild(
|
|
1887
|
+
el("p", "process-timeline-note", `${heading} · ${body.handler}`),
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
const ul = el("ul", "process-timeline process-timeline-rich");
|
|
1891
|
+
const items = (body.commands ?? []).length > 0 ? body.commands : body.steps ?? [];
|
|
1892
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
1893
|
+
const item = items[index];
|
|
1894
|
+
const failed = item.ok === false;
|
|
1895
|
+
const li = el(
|
|
1896
|
+
"li",
|
|
1897
|
+
`process-timeline-item process-timeline-tool-end${failed ? " is-error" : ""}${index === 0 ? " is-first" : ""}${index === items.length - 1 ? " is-last" : ""}`,
|
|
1898
|
+
);
|
|
1899
|
+
const rail = el("span", "process-timeline-rail");
|
|
1900
|
+
rail.appendChild(el("span", "process-timeline-dot"));
|
|
1901
|
+
li.appendChild(rail);
|
|
1902
|
+
const cardBody = el("div", "process-timeline-body");
|
|
1903
|
+
cardBody.appendChild(
|
|
1904
|
+
body.kind === "shell-preset"
|
|
1905
|
+
? renderProcessStepCard(item)
|
|
1906
|
+
: renderProcessCommandCard(item),
|
|
1907
|
+
);
|
|
1908
|
+
li.appendChild(cardBody);
|
|
1909
|
+
ul.appendChild(li);
|
|
1910
|
+
}
|
|
1911
|
+
content.appendChild(ul);
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
export async function mergeNodeProcess(dagRunId, node) {
|
|
1915
|
+
if (!dagRunId || !node?.nodeId) return;
|
|
1916
|
+
const nodeId = node.nodeId;
|
|
1917
|
+
const key = effectiveExecutionKey(dagRunId, node);
|
|
1918
|
+
const identity = makeDagNodeProcessIdentity(dagRunId, nodeId, key);
|
|
1919
|
+
uiState.dagNodeProcessIdentity = identity;
|
|
1920
|
+
uiState.dagNodeProcessLoading = true;
|
|
1921
|
+
const result = await fetchDagNodeProcess(dagRunId, nodeId, key);
|
|
1922
|
+
if (uiState.dagNodeProcessIdentity !== identity) return;
|
|
1923
|
+
if (!result.ok || !result.body) {
|
|
1924
|
+
uiState.dagNodeProcessError =
|
|
1925
|
+
result.body?.error || UI_TEXT.processError;
|
|
1926
|
+
uiState.dagNodeProcessData = {
|
|
1927
|
+
schemaVersion: 1,
|
|
1928
|
+
dagRunId,
|
|
1929
|
+
nodeId,
|
|
1930
|
+
kind: "none",
|
|
1931
|
+
executor: node.executor ?? null,
|
|
1932
|
+
handler: null,
|
|
1933
|
+
emptyReason: "not-executed",
|
|
1934
|
+
key: key ?? null,
|
|
1935
|
+
commands: [],
|
|
1936
|
+
steps: [],
|
|
1937
|
+
};
|
|
1938
|
+
uiState.dagNodeProcessLoading = false;
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
uiState.dagNodeProcessError = null;
|
|
1942
|
+
uiState.dagNodeProcessData = result.body;
|
|
1943
|
+
uiState.dagNodeProcessLoading = false;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
function renderExecutorAwareTimeline(content, dagRunId, node) {
|
|
1947
|
+
content.dataset.dagRunId = String(dagRunId ?? "");
|
|
1948
|
+
content.dataset.nodeId = String(node.nodeId ?? "");
|
|
1949
|
+
const key = effectiveExecutionKey(dagRunId, node);
|
|
1950
|
+
const cached = uiState.dagNodeProcessData;
|
|
1951
|
+
const sameNode =
|
|
1952
|
+
cached &&
|
|
1953
|
+
cached.dagRunId === dagRunId &&
|
|
1954
|
+
cached.nodeId === node.nodeId &&
|
|
1955
|
+
(cached.key ?? null) === (key ?? null);
|
|
1956
|
+
if (sameNode && uiState.dagNodeProcessError) {
|
|
1957
|
+
content.appendChild(el("p", "empty", uiState.dagNodeProcessError));
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
if (sameNode && cached.kind === "pi-session") {
|
|
1961
|
+
renderSessionTimeline(content, node.nodeId, {
|
|
1962
|
+
executionKey: effectiveExecutionKey(dagRunId, node),
|
|
1963
|
+
});
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
if (sameNode && cached.kind !== "pi-session") {
|
|
1967
|
+
renderNonPiProcess(content, cached);
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
if ((node.executor ?? "pi") === "pi") {
|
|
1971
|
+
renderSessionTimeline(content, node.nodeId, {
|
|
1972
|
+
executionKey: effectiveExecutionKey(dagRunId, node),
|
|
1973
|
+
});
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
if (uiState.dagNodeProcessError && sameNode) {
|
|
1977
|
+
content.appendChild(el("p", "empty", uiState.dagNodeProcessError));
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
const loading = el("p", "muted", UI_TEXT.processLoading);
|
|
1981
|
+
loading.setAttribute("role", "status");
|
|
1982
|
+
content.appendChild(loading);
|
|
1983
|
+
void mergeNodeProcess(dagRunId, node).then(() => {
|
|
1984
|
+
if (
|
|
1985
|
+
uiState.dagInspectorTab === "timeline" &&
|
|
1986
|
+
uiState.selectedDagNodeId === node.nodeId &&
|
|
1987
|
+
uiState.currentDagRunId === dagRunId
|
|
1988
|
+
) {
|
|
1989
|
+
renderDagInspectorForCurrent();
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1458
1994
|
function fillDagInspectorContent(content, dagRunId, node) {
|
|
1459
1995
|
const previousRenderedNode = content.dataset.dagNodeId ?? "";
|
|
1460
1996
|
const previousRenderedRun = content.dataset.dagRunId ?? "";
|
|
@@ -1501,10 +2037,12 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
1501
2037
|
);
|
|
1502
2038
|
if (activeTab === "spec-evidence") {
|
|
1503
2039
|
void renderSpecEvidence(content, dagRunId, node.nodeId);
|
|
2040
|
+
} else if (activeTab === "prompt-restart") {
|
|
2041
|
+
void renderPromptRestart(content, dagRunId, node);
|
|
1504
2042
|
} else if (activeTab === "timeline") {
|
|
1505
2043
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
1506
2044
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
1507
|
-
|
|
2045
|
+
renderExecutorAwareTimeline(content, dagRunId, node);
|
|
1508
2046
|
} else if (activeTab === "input") {
|
|
1509
2047
|
void renderDagNodeInput(content, dagRunId, node);
|
|
1510
2048
|
} else {
|
|
@@ -1568,6 +2106,14 @@ function buildDagInspectorHeader(dagRunId, node) {
|
|
|
1568
2106
|
["timeline", "执行过程", "ri-route-line"],
|
|
1569
2107
|
["spec-evidence", "规范证据", "ri-book-open-line"],
|
|
1570
2108
|
];
|
|
2109
|
+
// Experimental prompt-override rerun: the server injects the SCOPED
|
|
2110
|
+
// inspect mutation token into /inspect HTML only while
|
|
2111
|
+
// HARNESS_DAG_RERUN_PROMPT_OVERRIDE is enabled (and it only authorizes
|
|
2112
|
+
// dagRerunPlan/dagRerun). No token → no tab → the inspector stays
|
|
2113
|
+
// strictly read-only.
|
|
2114
|
+
if (globalThis.__INSPECT_MUTATION_TOKEN__) {
|
|
2115
|
+
tabDefs.push(["prompt-restart", "提示词重启", "ri-restart-line"]);
|
|
2116
|
+
}
|
|
1571
2117
|
const tabs = el("div", "dag-inspector-tabs");
|
|
1572
2118
|
tabs.setAttribute("role", "tablist");
|
|
1573
2119
|
tabs.setAttribute("aria-label", "节点检查器面板");
|
|
@@ -1657,7 +2203,14 @@ function dagInspectorSignature(dagRunId, node) {
|
|
|
1657
2203
|
uiState.dagNodeExecutionOutputLoading ? "loading" : "idle",
|
|
1658
2204
|
]
|
|
1659
2205
|
: tab === "timeline"
|
|
1660
|
-
? [
|
|
2206
|
+
? [
|
|
2207
|
+
uiState.sessionEventOffset,
|
|
2208
|
+
uiState.dagNodeProcessData?.kind ?? "",
|
|
2209
|
+
uiState.dagNodeProcessData?.emptyReason ?? "",
|
|
2210
|
+
(uiState.dagNodeProcessData?.commands ?? []).length,
|
|
2211
|
+
(uiState.dagNodeProcessData?.steps ?? []).length,
|
|
2212
|
+
uiState.dagNodeProcessLoading ? "loading" : "idle",
|
|
2213
|
+
]
|
|
1661
2214
|
: [node.status ?? ""];
|
|
1662
2215
|
// Attempt dimension: selection identity + history shape. Poll refreshes keep
|
|
1663
2216
|
// the DOM while selection/history are unchanged; switching attempt or a
|
|
@@ -90,6 +90,7 @@ import {
|
|
|
90
90
|
closeDagInspector,
|
|
91
91
|
selectDagNode,
|
|
92
92
|
renderDagInspector,
|
|
93
|
+
mergeNodeProcess,
|
|
93
94
|
} from "./dag-inspector.js";
|
|
94
95
|
import { mergeSessionEvents } from "./session-timeline.js";
|
|
95
96
|
|
|
@@ -451,13 +452,19 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
451
452
|
nodesEl.scrollLeft = nodesScrollLeft;
|
|
452
453
|
|
|
453
454
|
if (uiState.dagInspectorOpen && uiState.selectedDagNodeId) {
|
|
455
|
+
const selectedNode = nodes.find(
|
|
456
|
+
(node) => node.nodeId === uiState.selectedDagNodeId,
|
|
457
|
+
);
|
|
454
458
|
if (!uiState.sessionEventIdentity) {
|
|
455
459
|
uiState.sessionEventIdentity = makeSessionEventIdentity(
|
|
456
460
|
dagRunId,
|
|
457
461
|
uiState.selectedDagNodeId,
|
|
458
462
|
);
|
|
459
463
|
}
|
|
460
|
-
await
|
|
464
|
+
await Promise.all([
|
|
465
|
+
mergeSessionEvents(dagRunId, uiState.selectedDagNodeId),
|
|
466
|
+
selectedNode ? mergeNodeProcess(dagRunId, selectedNode) : Promise.resolve(),
|
|
467
|
+
]);
|
|
461
468
|
}
|
|
462
469
|
renderDagInspector(
|
|
463
470
|
dagRunId,
|
|
@@ -101,11 +101,14 @@ export async function renderFailures() {
|
|
|
101
101
|
|
|
102
102
|
if (tasks.length === 0) {
|
|
103
103
|
clearNode(listEl);
|
|
104
|
+
// 2026-08-22 (B-FIND-02): 空态文案与静态 #failures-empty / 页面标题
|
|
105
|
+
// 「异常 Task」保持一致;旧文案「当前没有失败或阻塞的 Task。」与静态
|
|
106
|
+
// HTML 及回归目录 AC-FE-INS-004 漂移,导致 frontend-test 字面 find 0 命中。
|
|
104
107
|
if (emptyEl) {
|
|
105
108
|
emptyEl.hidden = false;
|
|
106
|
-
mountViewState(emptyEl, "empty", "
|
|
109
|
+
mountViewState(emptyEl, "empty", "当前没有异常 Task");
|
|
107
110
|
} else {
|
|
108
|
-
mountViewState(listEl, "empty", "
|
|
111
|
+
mountViewState(listEl, "empty", "当前没有异常 Task");
|
|
109
112
|
}
|
|
110
113
|
return;
|
|
111
114
|
}
|