@tea-agent/loop-agent 0.39.1 → 0.40.0-next.10
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 +106 -116
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +69 -7
- package/dist/executors/pi-executor.js +4 -2
- package/dist/executors/pi-sdk-executor.js +46 -1
- package/dist/executors/pi-writer-tool-policy.js +57 -1
- package/dist/executors/shell-executor.js +42 -2
- package/dist/executors/workspace-write-snapshot.js +68 -0
- package/dist/infrastructure/harness/atomic-write.js +23 -0
- package/dist/task/config-types.js +10 -0
- package/dist/worker/console/chat/chat-event-store.js +75 -5
- package/dist/worker/console/chat/context-panel.js +7 -0
- package/dist/worker/console/chat/deferred-turn.js +12 -0
- package/dist/worker/console/chat/pi-runtime.js +331 -18
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +389 -56
- package/dist/worker/console/chat/session-stats.js +179 -0
- package/dist/worker/console/chat/session-store.js +93 -63
- package/dist/worker/console/chat/todos.js +115 -0
- package/dist/worker/console/chat/tool-preview.js +42 -0
- package/dist/worker/console/chat/turn-process.js +20 -55
- package/dist/worker/console/chat/user-questions.js +266 -0
- package/dist/worker/console/chat/workspace-landing.js +79 -0
- package/dist/worker/console/native-directory-picker.js +13 -0
- package/dist/worker/console/routes.js +309 -1
- package/dist/worker/console/security.js +4 -4
- package/dist/worker/console/server.js +529 -186
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CTx1TrNa.js → abnfDiagram-N423BO3Z-Bs-CDAXM.js} +1 -1
- package/dist/worker/console/static/assets/{arc-B4i9cVxX.js → arc-CAkA3We3.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BAXE8_Rl.js → architectureDiagram-T3A2C74G-DEmI_zqr.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-C5Xh59at.js → blockDiagram-VBNYF7ZC-uDHANebE.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B_asOzAu.js → c4Diagram-5PPSVZJV-Bv97b4_B.js} +1 -1
- package/dist/worker/console/static/assets/channel-amxUpk7o.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-wRO_iRNc.js → chunk-2GRJ4B5K-shmQKsFz.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-ByOaSh_G.js → chunk-2Q5K7J3B-BAraNvcN.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLzudan.js → chunk-5RXB4S5H-BirhxUop.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-JCNGkXYT.js → chunk-5VM5RSS4-C9LgUJpR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BhrPXXMZ.js → chunk-6Q2QTUOP-DrU-s-t7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-TJ5JuEBT.js → chunk-GF5L2VYU-Tx-H_FA2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-VVthn_mD.js → chunk-JWPE2WC7-BjLtNFrR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-D60-Mny4.js → chunk-KBJHAD2P-UNLVwSsy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-zXv7aVye.js → chunk-RYQCIY6F-CBEUX8XD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-By9VQoi1.js → chunk-XXDRQBXY-WTL7IZOq.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-C4zQLe-M.js → cose-bilkent-JH36ORCC-CkHG6W3s.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-wh5N2ReN.js → cynefin-VYW2F7L2-BRzFqTaL.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Dlu5wIqs.js → cynefinDiagram-MW4NZA55-2_CFp_i7.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CrDgthc6.js → dagre-VZM6K2ZE-BJtsmdkE.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-zN1clg27.js → diagram-7IWD3JNH-DNEFFo6w.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BN-o7J7-.js → diagram-B4RE2ZJO-B_IWV7eo.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-C8SuPt0d.js → diagram-LBJQPF4R-DvvRx2dz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BoizuHbZ.js → diagram-Q27KOJAE-CsibsWLg.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BzUzXixG.js → diagram-UB23O5K3-les_djBQ.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Ev3lA4Cf.js → ebnfDiagram-BXEA7PRR-BEm2n7qJ.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-GALUzRbs.js → erDiagram-JOGREHBK-BI7vHHVm.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CZJN65Sb.js → flowDiagram-UKHOOZJN-CUFCERrB.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-JLB9OESl.js → ganttDiagram-PKOTCBZU-wxyLiGWt.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CcvCr4GD.js → gitGraphDiagram-DS77QQ5N-Bt897K8Z.js} +1 -1
- package/dist/worker/console/static/assets/index-BHhUOTri.css +1 -0
- package/dist/worker/console/static/assets/index-DqJbO3-p.js +407 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-HG5z7aVg.js → infoDiagram-6WML65LV-DYNx_IPv.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BHEu9chR.js → ishikawaDiagram-WSZJBQD7-CzpLNEu6.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Cp_Up8-a.js → journeyDiagram-NVQOT4AX-97f9owNU.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-DvVeY-em.js → kanban-definition-27J2QSJJ-BxM9hyBn.js} +1 -1
- package/dist/worker/console/static/assets/{linear-aGmcjgTn.js → linear-ByuxcHvp.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Biyo-tqA.js → mermaid.core-DwPGvpCv.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DyxY2pIb.js → mindmap-definition-FAOFIHXS-DCoqG9RT.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CkdGuc50.js → pegDiagram-VL7TDLO6-BLSBmBMx.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-NNQI9xVR.js → pieDiagram-7S7Q4E2Y-B0t_0oJW.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-PAJkuz_G.js → quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-ZwrQod0O.js → railroadDiagram-AXF67PYL-B3jRdATC.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D3ZfrzM2.js → requirementDiagram-LRYGKXZP-D-cTtXEN.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DDGFmWk5.js → sankeyDiagram-W5VNT64P-B25igJRE.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DO6mmXTA.js → sequenceDiagram-SI44F4Z6-jW_lM1_D.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DMbCQiJt.js → sizeCapture-X5ZJPWSS-CrzTgsHE.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Di_YUEfC.js → stateDiagram-OKZ733FA-B01MvlqE.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Bn2PphNJ.js → swimlanes-SLNWSIFB-YcF12FiU.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DPh8pRsp.js → timeline-definition-Z64GVDOM-ClnRcg2s.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-GMfl26pW.js → vennDiagram-T6HMQDX7-B6gr_z-q.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DTlmVrmF.js → wardleyDiagram-T6FBY63Y-CvSgguGH.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-YGBjKAS5.js → xychartDiagram-ELKLHX3M-23OtSZCb.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +29 -4
- package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
- package/dist/worker/console/static-src/app/useOperatorActions.js +36 -6
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +10 -11
- package/dist/worker/console/static-src/app/useRunProgress.js +2 -1
- package/dist/worker/console/static-src/app/useTaskWizard.js +2 -2
- package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +141 -8
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
- package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
- package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
- package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +3 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +159 -44
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
- package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +339 -0
- package/dist/worker/console/workspace-context.js +234 -0
- package/dist/worker/console/workspace-registry.js +214 -0
- package/dist/worker/observe/server.js +4 -0
- package/dist/worker/observe/static/api.js +26 -6
- package/dist/worker/observe/static/app.js +18 -70
- package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
- package/dist/worker/observe/static/dag-node-purpose.js +203 -0
- package/dist/worker/observe/static/index.html +1 -1
- package/dist/worker/observe/static/inspect-workspace.js +307 -0
- package/dist/worker/observe/static/operator-chrome.css +145 -20
- package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
- package/dist/worker/observe/static/operator-chrome.js +330 -100
- package/dist/worker/observe/static/router.d.ts +46 -0
- package/dist/worker/observe/static/router.js +25 -1
- package/dist/worker/observe/static/state.d.ts +50 -0
- package/dist/worker/observe/static/state.js +174 -2
- package/dist/worker/observe/static/styles.css +348 -38
- package/dist/worker/observe/static/views/dag-graph.js +4 -2
- package/dist/worker/observe/static/views/dag-inspector.js +310 -156
- package/dist/worker/observe/static/views/dag.d.ts +13 -0
- package/dist/worker/observe/static/views/dag.js +6 -4
- package/dist/worker/observe/static/views/dashboard.js +6 -4
- package/dist/worker/observe/static/views/night.js +2 -2
- package/dist/worker/observe/static/views/pool.js +1 -0
- package/dist/worker/observe/static/views/run.js +3 -1
- package/dist/worker/observe/static/views/task.js +1 -1
- package/dist/workflows/dag/backend-test-pytest-collection.js +11 -10
- package/dist/workflows/dag/backend-test-writer-completeness.js +7 -12
- package/dist/workflows/dag/dynamic-runtime/map.js +1 -0
- package/dist/workflows/dag/init-hybrid.js +113 -47
- package/dist/workflows/dag/interrupt-request.js +3 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/types.js +20 -1
- package/docs/README.md +1 -0
- package/docs/operations/local-development-environment.md +21 -0
- package/docs/templates/agent-dag.schema.json +18 -0
- package/docs/templates/backend-test-dag.json +265 -237
- package/harness.json +3 -3
- package/package.json +3 -1
- package/dist/worker/console/static/assets/channel-Dj_4Moei.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C0YYAyCa.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C0YYAyCa.js +0 -1
- package/dist/worker/console/static/assets/index-CW88Fjbw.js +0 -406
- package/dist/worker/console/static/assets/index-DP0ZJl39.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrdDvela.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DPkEAULl.js +0 -8
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
setDagGraphViewMode,
|
|
23
23
|
currentDagGraphViewMode,
|
|
24
24
|
} from "../state.js";
|
|
25
|
+
import { describeDagNodePurpose } from "../dag-node-purpose.js";
|
|
25
26
|
import { selectDagNode } from "./dag-inspector.js";
|
|
26
27
|
import { renderTrajectoryStoryboard } from "./dag-trajectory.js";
|
|
27
28
|
|
|
@@ -335,12 +336,13 @@ export function renderDagGraph(dag, dagRunId, existingWrap = null) {
|
|
|
335
336
|
const statusLine = `${statusSymbol(status)} ${statusLabel(node.status)} · ${formatMs(node.durationMs)}`;
|
|
336
337
|
// 节点宽 216、左右 padding≈14:按显示列宽截断(CJK 双宽)。
|
|
337
338
|
const maxDisplayWidth = Math.max(10, Math.floor((position.width - 28) / 7));
|
|
339
|
+
const purpose = describeDagNodePurpose(node.nodeId, node);
|
|
338
340
|
const group = svgEl("g", {
|
|
339
341
|
transform: `translate(${position.x} ${position.y})`,
|
|
340
342
|
class: `dag-graph-node status-${status}${node.nodeId === uiState.selectedDagNodeId ? " selected" : ""}${uiState.selectedDagNodeId && !related.has(node.nodeId) ? " dag-dimmed" : ""}`,
|
|
341
343
|
tabindex: 0,
|
|
342
344
|
role: "button",
|
|
343
|
-
"aria-label": `${node.nodeId}
|
|
345
|
+
"aria-label": `${node.nodeId},${purpose ? `${purpose},` : ""}状态 ${status}${historyLabel ? `,执行历史 ${historyLabel}` : ""}`,
|
|
344
346
|
});
|
|
345
347
|
group.appendChild(
|
|
346
348
|
svgEl("rect", { width: position.width, height: position.height, rx: 10 }),
|
|
@@ -352,7 +354,7 @@ export function renderDagGraph(dag, dagRunId, existingWrap = null) {
|
|
|
352
354
|
: "";
|
|
353
355
|
const attemptHint =
|
|
354
356
|
node.attemptCount > 0 ? `\nattempts: ${node.attemptCount}` : "";
|
|
355
|
-
title.textContent = `${node.nodeId}\n${node.executor ?? "未知执行器"}\n${status}${historyLabel ? `\n执行历史:${historyLabel}` : ""}${passHint}${attemptHint}`;
|
|
357
|
+
title.textContent = `${node.nodeId}${purpose ? `\n${purpose}` : ""}\n${node.executor ?? "未知执行器"}\n${status}${historyLabel ? `\n执行历史:${historyLabel}` : ""}${passHint}${attemptHint}`;
|
|
356
358
|
group.appendChild(title);
|
|
357
359
|
const label = svgEl("text", {
|
|
358
360
|
x: 14,
|
|
@@ -21,9 +21,11 @@ import {
|
|
|
21
21
|
} from "../api.js";
|
|
22
22
|
import { badge, formatLedgerSummary, formatMs, prettyJsonBlock } from "../format.js";
|
|
23
23
|
import { renderMarkdown, renderInspectorDocument } from "../markdown-render.js";
|
|
24
|
+
import { describeDagNodePurpose } from "../dag-node-purpose.js";
|
|
24
25
|
import { buildPromptRestartCandidateOptions } from "../prompt-restart-candidates.js";
|
|
25
26
|
import { copyText } from "../copy.js";
|
|
26
27
|
import {
|
|
28
|
+
fetchWithWorkspaceScope,
|
|
27
29
|
uiState,
|
|
28
30
|
makeSessionEventIdentity,
|
|
29
31
|
makeDagNodeInputIdentity,
|
|
@@ -133,6 +135,175 @@ function createPromptRestartCopyButton(readCandidate) {
|
|
|
133
135
|
return button;
|
|
134
136
|
}
|
|
135
137
|
|
|
138
|
+
function formatPromptRestartMeta(text) {
|
|
139
|
+
const value = String(text ?? "");
|
|
140
|
+
const chars = [...value].length;
|
|
141
|
+
const lines = value.length === 0 ? 0 : value.split("\n").length;
|
|
142
|
+
return `${lines} 行 · ${chars.toLocaleString("zh-CN")} 字`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function createPromptRestartBanner({ tone, kicker, title, body }) {
|
|
146
|
+
const banner = el("div", `prompt-restart-banner is-${tone}`);
|
|
147
|
+
banner.setAttribute("role", "status");
|
|
148
|
+
banner.appendChild(el("span", "prompt-restart-banner-kicker", kicker));
|
|
149
|
+
banner.appendChild(el("p", "prompt-restart-banner-title", title));
|
|
150
|
+
banner.appendChild(el("p", "prompt-restart-banner-body", body));
|
|
151
|
+
return banner;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function createPromptRestartCandidatePicker({
|
|
155
|
+
options,
|
|
156
|
+
value,
|
|
157
|
+
onChange,
|
|
158
|
+
label,
|
|
159
|
+
hint,
|
|
160
|
+
}) {
|
|
161
|
+
const field = el("div", "prompt-restart-field");
|
|
162
|
+
field.appendChild(el("span", "prompt-restart-label", label));
|
|
163
|
+
const select = createCustomSelect({
|
|
164
|
+
ariaLabel: label,
|
|
165
|
+
className: "prompt-restart-candidate-select",
|
|
166
|
+
value,
|
|
167
|
+
disabled: options.length < 2,
|
|
168
|
+
options: options.map((option) => ({
|
|
169
|
+
value: option.key,
|
|
170
|
+
label: option.label,
|
|
171
|
+
})),
|
|
172
|
+
onChange: (nextKey) => {
|
|
173
|
+
onChange(String(nextKey));
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
select.setAttribute("data-testid", "prompt-restart-candidate");
|
|
177
|
+
field.appendChild(select);
|
|
178
|
+
const hintEl = el("p", "prompt-restart-hint", hint ?? "");
|
|
179
|
+
if (!hint) hintEl.hidden = true;
|
|
180
|
+
field.appendChild(hintEl);
|
|
181
|
+
return { field, select, hintEl };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function createPromptRestartModeSwitch({ value = "append", onChange }) {
|
|
185
|
+
const field = el("div", "prompt-restart-field prompt-restart-mode-field");
|
|
186
|
+
field.appendChild(el("span", "prompt-restart-label", "编辑模式"));
|
|
187
|
+
const group = el("div", "prompt-restart-mode-switch");
|
|
188
|
+
group.setAttribute("role", "radiogroup");
|
|
189
|
+
group.setAttribute("aria-label", "编辑模式");
|
|
190
|
+
group.setAttribute("data-testid", "prompt-restart-mode");
|
|
191
|
+
let current = value === "replace" ? "replace" : "append";
|
|
192
|
+
const buttons = [];
|
|
193
|
+
const sync = () => {
|
|
194
|
+
for (const btn of buttons) {
|
|
195
|
+
const selected = btn.dataset.mode === current;
|
|
196
|
+
btn.setAttribute("aria-checked", selected ? "true" : "false");
|
|
197
|
+
btn.classList.toggle("is-selected", selected);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
for (const mode of [
|
|
201
|
+
{ value: "append", label: "追加指令" },
|
|
202
|
+
{ value: "replace", label: "全文替换" },
|
|
203
|
+
]) {
|
|
204
|
+
const btn = document.createElement("button");
|
|
205
|
+
btn.type = "button";
|
|
206
|
+
btn.className = "prompt-restart-mode-option";
|
|
207
|
+
btn.dataset.mode = mode.value;
|
|
208
|
+
btn.setAttribute("role", "radio");
|
|
209
|
+
btn.setAttribute("aria-label", mode.label);
|
|
210
|
+
btn.textContent = mode.label;
|
|
211
|
+
btn.addEventListener("click", () => {
|
|
212
|
+
if (current === mode.value) return;
|
|
213
|
+
current = mode.value;
|
|
214
|
+
sync();
|
|
215
|
+
onChange?.(current);
|
|
216
|
+
});
|
|
217
|
+
buttons.push(btn);
|
|
218
|
+
group.appendChild(btn);
|
|
219
|
+
}
|
|
220
|
+
Object.defineProperty(group, "value", {
|
|
221
|
+
configurable: true,
|
|
222
|
+
enumerable: true,
|
|
223
|
+
get() {
|
|
224
|
+
return current;
|
|
225
|
+
},
|
|
226
|
+
set(next) {
|
|
227
|
+
current = next === "replace" ? "replace" : "append";
|
|
228
|
+
sync();
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
sync();
|
|
232
|
+
field.appendChild(group);
|
|
233
|
+
const hint = el(
|
|
234
|
+
"p",
|
|
235
|
+
"prompt-restart-hint",
|
|
236
|
+
"追加会保留原提示词;全文替换会改写整份底稿。",
|
|
237
|
+
);
|
|
238
|
+
field.appendChild(hint);
|
|
239
|
+
return { field, group, hint };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function createPromptRestartPromptCard({
|
|
243
|
+
title,
|
|
244
|
+
badge,
|
|
245
|
+
readCandidate,
|
|
246
|
+
preTestId,
|
|
247
|
+
collapsible = false,
|
|
248
|
+
startCollapsed = false,
|
|
249
|
+
}) {
|
|
250
|
+
const card = el("div", "prompt-restart-prompt-card");
|
|
251
|
+
const head = el("div", "prompt-restart-prompt-head");
|
|
252
|
+
const titles = el("div", "prompt-restart-prompt-titles");
|
|
253
|
+
const titleEl = el("span", "prompt-restart-prompt-title", title);
|
|
254
|
+
titles.appendChild(titleEl);
|
|
255
|
+
if (badge) {
|
|
256
|
+
titles.appendChild(el("span", "prompt-restart-prompt-badge", badge));
|
|
257
|
+
}
|
|
258
|
+
const metaEl = el("span", "prompt-restart-prompt-meta", "");
|
|
259
|
+
titles.appendChild(metaEl);
|
|
260
|
+
head.appendChild(titles);
|
|
261
|
+
const headActions = el("div", "prompt-restart-prompt-actions");
|
|
262
|
+
let fold = null;
|
|
263
|
+
let collapsed = Boolean(collapsible && startCollapsed);
|
|
264
|
+
const syncFold = () => {
|
|
265
|
+
if (!fold) return;
|
|
266
|
+
card.classList.toggle("is-collapsed", collapsed);
|
|
267
|
+
fold.setAttribute("aria-expanded", collapsed ? "false" : "true");
|
|
268
|
+
fold.textContent = collapsed ? "展开" : "收起";
|
|
269
|
+
};
|
|
270
|
+
if (collapsible) {
|
|
271
|
+
card.classList.add("is-collapsible");
|
|
272
|
+
fold = document.createElement("button");
|
|
273
|
+
fold.type = "button";
|
|
274
|
+
fold.className = "prompt-restart-prompt-fold";
|
|
275
|
+
fold.setAttribute("aria-controls", preTestId);
|
|
276
|
+
fold.addEventListener("click", (event) => {
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
event.stopPropagation();
|
|
279
|
+
collapsed = !collapsed;
|
|
280
|
+
syncFold();
|
|
281
|
+
});
|
|
282
|
+
headActions.appendChild(fold);
|
|
283
|
+
}
|
|
284
|
+
headActions.appendChild(createPromptRestartCopyButton(readCandidate));
|
|
285
|
+
head.appendChild(headActions);
|
|
286
|
+
card.appendChild(head);
|
|
287
|
+
const pre = document.createElement("pre");
|
|
288
|
+
pre.className = "prompt-restart-readonly";
|
|
289
|
+
pre.setAttribute("data-testid", preTestId);
|
|
290
|
+
if (preTestId) pre.id = preTestId;
|
|
291
|
+
card.appendChild(pre);
|
|
292
|
+
const sync = (candidate) => {
|
|
293
|
+
const next = candidate ?? readCandidate?.();
|
|
294
|
+
titleEl.textContent =
|
|
295
|
+
next?.key === "frozen"
|
|
296
|
+
? "冻结原始提示词"
|
|
297
|
+
: (next?.label ?? title);
|
|
298
|
+
pre.textContent = next?.prompt ?? "";
|
|
299
|
+
metaEl.textContent = formatPromptRestartMeta(next?.prompt ?? "");
|
|
300
|
+
if (next?.truncated) metaEl.textContent += " · 已截断";
|
|
301
|
+
};
|
|
302
|
+
sync(readCandidate?.());
|
|
303
|
+
syncFold();
|
|
304
|
+
return { card, pre, titleEl, metaEl, sync };
|
|
305
|
+
}
|
|
306
|
+
|
|
136
307
|
function renderDagInspectorForCurrent() {
|
|
137
308
|
if (!inspectorSelection.dagRunId || !inspectorSelection.node) return;
|
|
138
309
|
renderDagInspector(
|
|
@@ -1116,7 +1287,7 @@ export function frontendGateLedgerLines(input) {
|
|
|
1116
1287
|
/** DAG run 专属 prewrite-result 只读拉取(内联 fetch,不新增 api.js 依赖)。 */
|
|
1117
1288
|
async function fetchPrewriteResultJson(dagRunId) {
|
|
1118
1289
|
try {
|
|
1119
|
-
const res = await
|
|
1290
|
+
const res = await fetchWithWorkspaceScope(
|
|
1120
1291
|
`/api/dag-runs/${encodeURIComponent(dagRunId)}/prewrite-result`,
|
|
1121
1292
|
{ credentials: "same-origin" },
|
|
1122
1293
|
);
|
|
@@ -1234,57 +1405,41 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
1234
1405
|
|
|
1235
1406
|
if (node.status !== "ERROR") {
|
|
1236
1407
|
// Non-failed nodes still show their frozen prompt — read-only.
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1408
|
+
const panel = el("div", "prompt-restart-panel is-readonly");
|
|
1409
|
+
panel.appendChild(
|
|
1410
|
+
createPromptRestartBanner({
|
|
1411
|
+
tone: "readonly",
|
|
1412
|
+
kicker: "只读",
|
|
1413
|
+
title: `当前节点 ${node.status ?? "unknown"}`,
|
|
1414
|
+
body: "仅失败(ERROR)节点支持编辑提示词并重启。以下可只读查看冻结提示词与历史失败候选。",
|
|
1415
|
+
}),
|
|
1241
1416
|
);
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
picker.appendChild(select);
|
|
1256
|
-
if (promptCandidateOptions.length === 1) {
|
|
1257
|
-
picker.appendChild(
|
|
1258
|
-
el(
|
|
1259
|
-
"span",
|
|
1260
|
-
"muted",
|
|
1261
|
-
historicalCandidatesMissing
|
|
1417
|
+
const promptCard = createPromptRestartPromptCard({
|
|
1418
|
+
title: "冻结原始提示词",
|
|
1419
|
+
badge: "只读",
|
|
1420
|
+
readCandidate: () => selectedCandidate,
|
|
1421
|
+
preTestId: "prompt-restart-readonly",
|
|
1422
|
+
});
|
|
1423
|
+
const picker = createPromptRestartCandidatePicker({
|
|
1424
|
+
options: promptCandidateOptions,
|
|
1425
|
+
value: selectedCandidate.key,
|
|
1426
|
+
label: "提示词版本(只读)",
|
|
1427
|
+
hint:
|
|
1428
|
+
promptCandidateOptions.length === 1
|
|
1429
|
+
? historicalCandidatesMissing
|
|
1262
1430
|
? "该历史运行未保存失败 attempt 的实际提示词;新运行会在此列出。"
|
|
1263
|
-
: "该节点没有已保存的失败 attempt。"
|
|
1264
|
-
|
|
1265
|
-
)
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
promptHead.appendChild(el("span", "muted", "当前提示词"));
|
|
1272
|
-
promptHead.appendChild(
|
|
1273
|
-
createPromptRestartCopyButton(() => selectedCandidate),
|
|
1274
|
-
);
|
|
1275
|
-
promptCard.appendChild(promptHead);
|
|
1276
|
-
const pre = document.createElement("pre");
|
|
1277
|
-
pre.className = "prompt-restart-readonly";
|
|
1278
|
-
pre.setAttribute("data-testid", "prompt-restart-readonly");
|
|
1279
|
-
pre.textContent = selectedCandidate.prompt;
|
|
1280
|
-
promptCard.appendChild(pre);
|
|
1281
|
-
content.appendChild(promptCard);
|
|
1282
|
-
select.addEventListener("change", () => {
|
|
1283
|
-
selectedCandidate =
|
|
1284
|
-
promptCandidateOptions.find((option) => option.key === select.value) ??
|
|
1285
|
-
promptCandidateOptions[0];
|
|
1286
|
-
pre.textContent = selectedCandidate.prompt;
|
|
1431
|
+
: "该节点没有已保存的失败 attempt。"
|
|
1432
|
+
: "可切换查看冻结底稿与历史失败候选。",
|
|
1433
|
+
onChange: (nextKey) => {
|
|
1434
|
+
selectedCandidate =
|
|
1435
|
+
promptCandidateOptions.find((option) => option.key === nextKey) ??
|
|
1436
|
+
promptCandidateOptions[0];
|
|
1437
|
+
promptCard.sync(selectedCandidate);
|
|
1438
|
+
},
|
|
1287
1439
|
});
|
|
1440
|
+
panel.appendChild(picker.field);
|
|
1441
|
+
panel.appendChild(promptCard.card);
|
|
1442
|
+
content.appendChild(panel);
|
|
1288
1443
|
return;
|
|
1289
1444
|
}
|
|
1290
1445
|
|
|
@@ -1299,151 +1454,129 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
1299
1454
|
return;
|
|
1300
1455
|
}
|
|
1301
1456
|
|
|
1302
|
-
const box = el("div", "prompt-restart-box");
|
|
1457
|
+
const box = el("div", "prompt-restart-box prompt-restart-panel is-edit");
|
|
1303
1458
|
box.appendChild(
|
|
1304
|
-
|
|
1305
|
-
"
|
|
1306
|
-
"
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
const candidateRow = el("label", "prompt-restart-field");
|
|
1311
|
-
candidateRow.appendChild(el("span", "muted", "提示词版本"));
|
|
1312
|
-
const candidateSelect = document.createElement("select");
|
|
1313
|
-
candidateSelect.className = "prompt-restart-mode-select";
|
|
1314
|
-
candidateSelect.setAttribute("data-testid", "prompt-restart-candidate");
|
|
1315
|
-
for (const option of promptCandidateOptions) {
|
|
1316
|
-
const element = document.createElement("option");
|
|
1317
|
-
element.value = option.key;
|
|
1318
|
-
element.textContent = option.label;
|
|
1319
|
-
candidateSelect.appendChild(element);
|
|
1320
|
-
}
|
|
1321
|
-
candidateRow.appendChild(candidateSelect);
|
|
1322
|
-
const candidateHint = el(
|
|
1323
|
-
"span",
|
|
1324
|
-
"muted",
|
|
1325
|
-
promptCandidateOptions.length > 1
|
|
1326
|
-
? "选择失败候选会载入全文替换;冻结原始提示词仍可使用追加模式。"
|
|
1327
|
-
: historicalCandidatesMissing
|
|
1328
|
-
? "该历史运行未保存失败 attempt 的实际提示词;新运行会在此列出。"
|
|
1329
|
-
: "该节点没有已保存的失败 attempt;当前仅有冻结原始提示词。",
|
|
1330
|
-
);
|
|
1331
|
-
candidateRow.appendChild(candidateHint);
|
|
1332
|
-
box.appendChild(candidateRow);
|
|
1333
|
-
|
|
1334
|
-
const modeRow = el("div", "prompt-restart-mode");
|
|
1335
|
-
modeRow.appendChild(el("span", "muted", "编辑模式"));
|
|
1336
|
-
const modeSelect = document.createElement("select");
|
|
1337
|
-
modeSelect.className = "prompt-restart-mode-select";
|
|
1338
|
-
modeSelect.setAttribute("data-testid", "prompt-restart-mode");
|
|
1339
|
-
const appendOption = document.createElement("option");
|
|
1340
|
-
appendOption.value = "append";
|
|
1341
|
-
appendOption.textContent =
|
|
1342
|
-
"追加指令(推荐):保留原提示词,在末尾追加 operator_addendum 块";
|
|
1343
|
-
const replaceOption = document.createElement("option");
|
|
1344
|
-
replaceOption.value = "replace";
|
|
1345
|
-
replaceOption.textContent = "全文替换(高级):替换整个提示词";
|
|
1346
|
-
modeSelect.appendChild(appendOption);
|
|
1347
|
-
modeSelect.appendChild(replaceOption);
|
|
1348
|
-
modeSelect.value = "append";
|
|
1349
|
-
modeRow.appendChild(modeSelect);
|
|
1350
|
-
box.appendChild(modeRow);
|
|
1351
|
-
|
|
1352
|
-
const baseLabel = el(
|
|
1353
|
-
"div",
|
|
1354
|
-
"prompt-restart-field prompt-restart-prompt-card",
|
|
1355
|
-
);
|
|
1356
|
-
const baseHead = el("div", "prompt-restart-prompt-head");
|
|
1357
|
-
baseHead.appendChild(
|
|
1358
|
-
el("span", "muted", `冻结提示词(只读底稿 · ${nodeId})`),
|
|
1459
|
+
createPromptRestartBanner({
|
|
1460
|
+
tone: "edit",
|
|
1461
|
+
kicker: "可编辑",
|
|
1462
|
+
title: "从失败节点编辑提示词并重启",
|
|
1463
|
+
body: "父 run spec 保持冻结,将创建带修改提示词的续跑 run。",
|
|
1464
|
+
}),
|
|
1359
1465
|
);
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1466
|
+
const promptCard = createPromptRestartPromptCard({
|
|
1467
|
+
title: `冻结提示词(只读底稿 · ${nodeId})`,
|
|
1468
|
+
badge: "底稿",
|
|
1469
|
+
readCandidate: () => selectedCandidate,
|
|
1470
|
+
preTestId: "prompt-restart-base",
|
|
1471
|
+
collapsible: true,
|
|
1472
|
+
startCollapsed: true,
|
|
1473
|
+
});
|
|
1474
|
+
const picker = createPromptRestartCandidatePicker({
|
|
1475
|
+
options: promptCandidateOptions,
|
|
1476
|
+
value: selectedCandidate.key,
|
|
1477
|
+
label: "提示词版本",
|
|
1478
|
+
hint:
|
|
1479
|
+
promptCandidateOptions.length > 1
|
|
1480
|
+
? "选择失败候选会载入全文替换;冻结原始提示词仍可使用追加模式。"
|
|
1481
|
+
: historicalCandidatesMissing
|
|
1482
|
+
? "该历史运行未保存失败 attempt 的实际提示词;新运行会在此列出。"
|
|
1483
|
+
: "该节点没有已保存的失败 attempt;当前仅有冻结原始提示词。",
|
|
1484
|
+
onChange: (nextKey) => {
|
|
1485
|
+
selectedCandidate =
|
|
1486
|
+
promptCandidateOptions.find((option) => option.key === nextKey) ??
|
|
1487
|
+
promptCandidateOptions[0];
|
|
1488
|
+
promptCard.sync(selectedCandidate);
|
|
1489
|
+
replaceArea.value = selectedCandidate.prompt;
|
|
1490
|
+
if (selectedCandidate.key !== "frozen") {
|
|
1491
|
+
modeSelect.value = "replace";
|
|
1492
|
+
}
|
|
1493
|
+
picker.hintEl.hidden = false;
|
|
1494
|
+
picker.hintEl.textContent = selectedCandidate.truncated
|
|
1495
|
+
? "该候选已截断,仅供查看,不能直接提交重启。"
|
|
1496
|
+
: selectedCandidate.key === "frozen"
|
|
1497
|
+
? "冻结原始提示词可使用追加或全文替换。"
|
|
1498
|
+
: "已载入该失败候选;可在全文替换编辑器中修订后重启。";
|
|
1499
|
+
applyModeVisibility();
|
|
1500
|
+
},
|
|
1501
|
+
});
|
|
1502
|
+
const modeSwitch = createPromptRestartModeSwitch({
|
|
1503
|
+
value: "append",
|
|
1504
|
+
onChange: () => applyModeVisibility(),
|
|
1505
|
+
});
|
|
1506
|
+
const modeSelect = modeSwitch.group;
|
|
1507
|
+
const toolbar = el("div", "prompt-restart-toolbar");
|
|
1508
|
+
toolbar.appendChild(picker.field);
|
|
1509
|
+
toolbar.appendChild(modeSwitch.field);
|
|
1510
|
+
box.appendChild(toolbar);
|
|
1368
1511
|
|
|
1369
|
-
const addendumLabel = el("label", "prompt-restart-field");
|
|
1512
|
+
const addendumLabel = el("label", "prompt-restart-field prompt-restart-work");
|
|
1513
|
+
addendumLabel.appendChild(
|
|
1514
|
+
el("span", "prompt-restart-label", "追加指令"),
|
|
1515
|
+
);
|
|
1370
1516
|
addendumLabel.appendChild(
|
|
1371
1517
|
el(
|
|
1372
|
-
"
|
|
1373
|
-
"
|
|
1374
|
-
"
|
|
1518
|
+
"p",
|
|
1519
|
+
"prompt-restart-hint",
|
|
1520
|
+
"将包裹为 <operator_addendum> 块附加到原提示词末尾",
|
|
1375
1521
|
),
|
|
1376
1522
|
);
|
|
1377
1523
|
const addendumArea = document.createElement("textarea");
|
|
1378
|
-
addendumArea.className = "prompt-restart-textarea";
|
|
1379
|
-
addendumArea.rows =
|
|
1524
|
+
addendumArea.className = "prompt-restart-textarea prompt-restart-textarea-work";
|
|
1525
|
+
addendumArea.rows = 8;
|
|
1380
1526
|
addendumArea.placeholder = "例如:重点检查 X 边界;输出前先自检 Y 约束…";
|
|
1381
1527
|
addendumArea.setAttribute("data-testid", "prompt-restart-addendum");
|
|
1382
1528
|
addendumLabel.appendChild(addendumArea);
|
|
1383
1529
|
box.appendChild(addendumLabel);
|
|
1384
1530
|
|
|
1385
|
-
const replaceLabel = el("label", "prompt-restart-field");
|
|
1386
|
-
replaceLabel.appendChild(
|
|
1531
|
+
const replaceLabel = el("label", "prompt-restart-field prompt-restart-work");
|
|
1532
|
+
replaceLabel.appendChild(
|
|
1533
|
+
el("span", "prompt-restart-label", `完整提示词(${nodeId})`),
|
|
1534
|
+
);
|
|
1387
1535
|
const replaceArea = document.createElement("textarea");
|
|
1388
|
-
replaceArea.className = "prompt-restart-textarea";
|
|
1389
|
-
replaceArea.rows =
|
|
1536
|
+
replaceArea.className = "prompt-restart-textarea prompt-restart-textarea-work";
|
|
1537
|
+
replaceArea.rows = 12;
|
|
1390
1538
|
replaceArea.value = selectedCandidate.prompt;
|
|
1391
1539
|
replaceArea.setAttribute("data-testid", "prompt-restart-textarea");
|
|
1392
1540
|
replaceLabel.appendChild(replaceArea);
|
|
1393
1541
|
box.appendChild(replaceLabel);
|
|
1394
1542
|
|
|
1543
|
+
const baseLabel = promptCard.card;
|
|
1544
|
+
box.appendChild(baseLabel);
|
|
1545
|
+
|
|
1395
1546
|
const applyModeVisibility = () => {
|
|
1396
1547
|
const append = modeSelect.value === "append";
|
|
1397
|
-
baseLabel.
|
|
1398
|
-
addendumLabel.
|
|
1399
|
-
replaceLabel.
|
|
1548
|
+
baseLabel.classList.toggle("is-hidden", !append);
|
|
1549
|
+
addendumLabel.classList.toggle("is-hidden", !append);
|
|
1550
|
+
replaceLabel.classList.toggle("is-hidden", append);
|
|
1400
1551
|
};
|
|
1401
|
-
modeSelect.addEventListener("change", applyModeVisibility);
|
|
1402
|
-
candidateSelect.addEventListener("change", () => {
|
|
1403
|
-
selectedCandidate =
|
|
1404
|
-
promptCandidateOptions.find(
|
|
1405
|
-
(option) => option.key === candidateSelect.value,
|
|
1406
|
-
) ?? promptCandidateOptions[0];
|
|
1407
|
-
baseBlock.textContent = selectedCandidate.prompt;
|
|
1408
|
-
replaceArea.value = selectedCandidate.prompt;
|
|
1409
|
-
if (selectedCandidate.key !== "frozen") {
|
|
1410
|
-
modeSelect.value = "replace";
|
|
1411
|
-
}
|
|
1412
|
-
if (candidateHint) {
|
|
1413
|
-
candidateHint.textContent = selectedCandidate.truncated
|
|
1414
|
-
? "该候选已截断,仅供查看,不能直接提交重启。"
|
|
1415
|
-
: selectedCandidate.key === "frozen"
|
|
1416
|
-
? "冻结原始提示词可使用追加或全文替换。"
|
|
1417
|
-
: "已载入该失败候选;可在全文替换编辑器中修订后重启。";
|
|
1418
|
-
}
|
|
1419
|
-
applyModeVisibility();
|
|
1420
|
-
});
|
|
1421
1552
|
applyModeVisibility();
|
|
1422
1553
|
|
|
1423
1554
|
const reasonLabel = el("label", "prompt-restart-field");
|
|
1424
|
-
reasonLabel.appendChild(el("span", "
|
|
1555
|
+
reasonLabel.appendChild(el("span", "prompt-restart-label", "重启原因"));
|
|
1556
|
+
reasonLabel.appendChild(
|
|
1557
|
+
el("p", "prompt-restart-hint", "结构化说明,会写入续跑 provenance"),
|
|
1558
|
+
);
|
|
1425
1559
|
const reasonArea = document.createElement("textarea");
|
|
1426
|
-
reasonArea.className = "prompt-restart-textarea";
|
|
1560
|
+
reasonArea.className = "prompt-restart-textarea prompt-restart-textarea-compact";
|
|
1427
1561
|
reasonArea.rows = 3;
|
|
1428
1562
|
reasonArea.value = `operator-prompt-override / run=${dagRunId} / node=${nodeId} / 手工编辑失败节点提示词后重启`;
|
|
1429
1563
|
reasonArea.setAttribute("data-testid", "prompt-restart-reason");
|
|
1430
1564
|
reasonLabel.appendChild(reasonArea);
|
|
1431
1565
|
box.appendChild(reasonLabel);
|
|
1432
1566
|
|
|
1433
|
-
const row = el("div", "prompt-restart-actions");
|
|
1567
|
+
const row = el("div", "prompt-restart-actions prompt-restart-footer");
|
|
1568
|
+
const status = el("p", "prompt-restart-status", "");
|
|
1569
|
+
status.setAttribute("role", "status");
|
|
1570
|
+
status.setAttribute("aria-live", "polite");
|
|
1434
1571
|
const submit = document.createElement("button");
|
|
1435
1572
|
submit.type = "button";
|
|
1436
1573
|
submit.className = "prompt-restart-submit";
|
|
1437
1574
|
submit.textContent = "资格预检并重启";
|
|
1438
1575
|
submit.setAttribute("data-testid", "prompt-restart-submit");
|
|
1576
|
+
row.appendChild(status);
|
|
1439
1577
|
row.appendChild(submit);
|
|
1440
1578
|
box.appendChild(row);
|
|
1441
1579
|
|
|
1442
|
-
const status = el("p", "prompt-restart-status", "");
|
|
1443
|
-
status.setAttribute("role", "status");
|
|
1444
|
-
status.setAttribute("aria-live", "polite");
|
|
1445
|
-
box.appendChild(status);
|
|
1446
|
-
|
|
1447
1580
|
content.appendChild(box);
|
|
1448
1581
|
|
|
1449
1582
|
const setStatus = (text, isError = false) => {
|
|
@@ -1535,7 +1668,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
1535
1668
|
setStatus(`重启完成,新 run:${newRunId}`);
|
|
1536
1669
|
const open = document.createElement("button");
|
|
1537
1670
|
open.type = "button";
|
|
1538
|
-
open.className = "prompt-restart-submit";
|
|
1671
|
+
open.className = "prompt-restart-submit is-secondary";
|
|
1539
1672
|
open.textContent = "打开新 run";
|
|
1540
1673
|
open.addEventListener("click", () => {
|
|
1541
1674
|
window.location.hash = `#/dag/${encodeURIComponent(String(newRunId))}`;
|
|
@@ -1880,7 +2013,7 @@ function effectiveExecutionKey(dagRunId, node) {
|
|
|
1880
2013
|
async function fetchExecutionOutput(dagRunId, nodeId, key) {
|
|
1881
2014
|
const url = `/api/dag-runs/${encodeURIComponent(dagRunId)}/nodes/${encodeURIComponent(nodeId)}/execution-output?key=${encodeURIComponent(key)}`;
|
|
1882
2015
|
try {
|
|
1883
|
-
const res = await
|
|
2016
|
+
const res = await fetchWithWorkspaceScope(url);
|
|
1884
2017
|
let body = null;
|
|
1885
2018
|
try {
|
|
1886
2019
|
body = await res.json();
|
|
@@ -2378,6 +2511,16 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
2378
2511
|
}
|
|
2379
2512
|
}
|
|
2380
2513
|
|
|
2514
|
+
function inspectorNodePurpose(node) {
|
|
2515
|
+
return describeDagNodePurpose(node?.nodeId, node);
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
function appendInspectorPurpose(title, node) {
|
|
2519
|
+
const purpose = inspectorNodePurpose(node);
|
|
2520
|
+
if (!purpose) return;
|
|
2521
|
+
title.appendChild(el("p", "dag-inspector-purpose", purpose));
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2381
2524
|
function buildDagInspectorHeader(dagRunId, node) {
|
|
2382
2525
|
const header = el("div", "dag-inspector-header");
|
|
2383
2526
|
header.dataset.dagRunId = String(dagRunId ?? "");
|
|
@@ -2385,6 +2528,7 @@ function buildDagInspectorHeader(dagRunId, node) {
|
|
|
2385
2528
|
const title = el("div", "dag-inspector-title");
|
|
2386
2529
|
title.appendChild(el("h3", null, "节点检查器"));
|
|
2387
2530
|
title.appendChild(el("div", "dag-inspector-node", node.nodeId));
|
|
2531
|
+
appendInspectorPurpose(title, node);
|
|
2388
2532
|
header.appendChild(title);
|
|
2389
2533
|
const controls = el("div", "dag-inspector-controls");
|
|
2390
2534
|
const status = badge(node.status ?? "unknown");
|
|
@@ -2464,6 +2608,16 @@ function syncDagInspectorHeader(panel, dagRunId, node) {
|
|
|
2464
2608
|
header.dataset.dagNodeId = String(node.nodeId ?? "");
|
|
2465
2609
|
const nodeLabel = header.querySelector(".dag-inspector-node");
|
|
2466
2610
|
if (nodeLabel) nodeLabel.textContent = node.nodeId;
|
|
2611
|
+
const title = header.querySelector(".dag-inspector-title");
|
|
2612
|
+
const purposeLine = header.querySelector(".dag-inspector-purpose");
|
|
2613
|
+
const purpose = inspectorNodePurpose(node);
|
|
2614
|
+
if (purposeLine && purpose) {
|
|
2615
|
+
purposeLine.textContent = purpose;
|
|
2616
|
+
} else if (purposeLine && !purpose) {
|
|
2617
|
+
purposeLine.remove();
|
|
2618
|
+
} else if (!purposeLine && purpose && title) {
|
|
2619
|
+
title.appendChild(el("p", "dag-inspector-purpose", purpose));
|
|
2620
|
+
}
|
|
2467
2621
|
const status = header.querySelector(".dag-inspector-status");
|
|
2468
2622
|
if (status) {
|
|
2469
2623
|
const next = badge(node.status ?? "unknown");
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DagNodeRemark {
|
|
2
|
+
failed: boolean;
|
|
3
|
+
text: string;
|
|
4
|
+
isErrorSummary: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function resolveNodeRemark(node: {
|
|
8
|
+
status?: string | null;
|
|
9
|
+
errorPreview?: string | null;
|
|
10
|
+
label?: string | null;
|
|
11
|
+
nodeId: string;
|
|
12
|
+
rank?: string | number | null;
|
|
13
|
+
} | null | undefined): DagNodeRemark;
|
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
dashboardVisibleFeatures,
|
|
71
71
|
findDagForRun,
|
|
72
72
|
} from "../dag-model.js";
|
|
73
|
+
import { describeDagNodePurpose } from "../dag-node-purpose.js";
|
|
73
74
|
import { layoutDag } from "../dag-layout.js";
|
|
74
75
|
import {
|
|
75
76
|
showView,
|
|
@@ -136,7 +137,7 @@ export function extractFirstErrorSummary(errorPreview) {
|
|
|
136
137
|
return effective ?? "";
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
/** Resolve 备注 cell content for a DAG node (failed → summary; else
|
|
140
|
+
/** Resolve 备注 cell content for a DAG node (failed → summary; else purpose). */
|
|
140
141
|
export function resolveNodeRemark(node) {
|
|
141
142
|
const failed = isNodeFailed(node?.status);
|
|
142
143
|
if (failed) {
|
|
@@ -147,10 +148,10 @@ export function resolveNodeRemark(node) {
|
|
|
147
148
|
isErrorSummary: Boolean(summary),
|
|
148
149
|
};
|
|
149
150
|
}
|
|
150
|
-
const
|
|
151
|
+
const purpose = describeDagNodePurpose(node?.nodeId, node);
|
|
151
152
|
return {
|
|
152
153
|
failed: false,
|
|
153
|
-
text:
|
|
154
|
+
text: purpose || "—",
|
|
154
155
|
isErrorSummary: false,
|
|
155
156
|
};
|
|
156
157
|
}
|
|
@@ -363,7 +364,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
363
364
|
"page-action-btn",
|
|
364
365
|
"在操作台中止此运行 →",
|
|
365
366
|
);
|
|
366
|
-
interruptLink.href = `/#/recovery?dagRunId=${encodeURIComponent(dag.dagRunId)}&intent=interrupt`;
|
|
367
|
+
interruptLink.href = `/#/operate/recovery?dagRunId=${encodeURIComponent(dag.dagRunId)}&intent=interrupt`;
|
|
367
368
|
interruptLink.setAttribute("data-testid", "inspect-dag-interrupt-link");
|
|
368
369
|
interruptLink.title = "只跳转到操作台 Recovery,不会在观测页执行中止";
|
|
369
370
|
primaryEntries.push(["协作中止", interruptLink]);
|
|
@@ -481,6 +482,7 @@ export function startDagPolling(dagRunId, initial = true) {
|
|
|
481
482
|
const active = await renderDagDetail(dagRunId, isInitial);
|
|
482
483
|
if (generation !== uiState.pollingGeneration || !active) return;
|
|
483
484
|
const delay = detailPollDelay("running", isPageVisible());
|
|
485
|
+
if (delay == null) return;
|
|
484
486
|
uiState.dagPollTimer = setTimeout(() => {
|
|
485
487
|
uiState.dagPollTimer = null;
|
|
486
488
|
void poll(false);
|