@tea-agent/loop-agent 0.42.0-next.1 → 0.42.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +321 -1
- package/dist/application/dag/generate-task-dag.js +75 -19
- package/dist/application/dag/run-dag.js +41 -0
- package/dist/application/task-lifecycle/advance.js +24 -5
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +7 -0
- package/dist/cli/program.js +6 -1
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/init.js +27 -1
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +4010 -78
- package/dist/executors/pi-executor.js +15 -4
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/shell-executor.js +978 -188
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/infrastructure/console/operation-store.js +226 -6
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +7 -0
- package/dist/task/config-types.js +105 -5
- package/dist/task/contract/adopt.js +4 -0
- package/dist/task/contract/import-revision.js +4 -0
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +203 -20
- package/dist/task/runtime.js +5 -2
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +64 -25
- package/dist/task/source-prepare/prepare.js +126 -21
- package/dist/task/source-prepare/semantic-intake.js +6 -2
- package/dist/task/source-references.js +22 -1
- package/dist/worker/console/chat/chat-ui-policy.js +4 -2
- package/dist/worker/console/chat/model-resolver.js +7 -4
- package/dist/worker/console/chat/pi-runtime.js +258 -11
- package/dist/worker/console/chat/repo-browser.js +10 -3
- package/dist/worker/console/chat/routes.js +165 -71
- package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
- package/dist/worker/console/chat/session-catalog.js +2 -0
- package/dist/worker/console/chat/session-store.js +159 -70
- package/dist/worker/console/chat/shortcuts.js +19 -3
- package/dist/worker/console/chat/turn-process.js +111 -56
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/operator-actions.js +169 -7
- package/dist/worker/console/pi-readiness.js +3 -2
- package/dist/worker/console/server.js +23 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DZZ8m3PO.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
- package/dist/worker/console/static/assets/{arc-D6PvaVd-.js → arc-COptKq2S.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-B_OTOiI8.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Bv6rqHBg.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B8eHr0oz.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
- package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DYwR0im2.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-D2WPGqXt.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CQISJ_I7.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C0o2Du1e.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-4f8kr-U7.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D_OWvXzX.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CasdPz5X.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Cj8lRrla.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CUvD1FSd.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CqLw_eqB.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-ku-WqJPx.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-Bq_PYDhl.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BiGZV641.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C4JRlglF.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B0dNeWYG.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Cj35YvYM.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-uzQoJ2-8.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D1a-Buoz.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-XjRrLRSs.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Da_O24cW.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BLJ8jrYU.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-B9GrLjM0.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CeJ0TqiK.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
- package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
- package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DB26i3d8.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BOGRyeZT.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CZRoBO_6.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-qlWhJyoB.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CfUiDDB3.js → linear-D59YJ9kB.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BQe6fpqj.js → mermaid.core-Bl13LpNa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-zFzWHw64.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DFGUqjZO.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BB5i0l8Z.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-obbVZ_X5.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BwdaNzc1.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CkTJaugh.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BwJ-hIgq.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BzSgmm7w.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Bn3oj5Q1.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-dxhGtL8K.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BLpEInya.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ChJGYcXy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DK-qjRer.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-3qeaWAg-.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CootlyP9.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
- package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
- package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
- package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
- package/dist/worker/console/workspace-context.js +115 -1
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/routes.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-helpers.js +4 -2
- 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 +13 -8
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/inspect-workspace.js +23 -0
- package/dist/worker/observe/static/kpi.js +2 -2
- package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
- package/dist/worker/observe/static/operator-chrome.js +37 -23
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +7 -7
- package/dist/worker/observe/static/router.d.ts +12 -1
- package/dist/worker/observe/static/router.js +48 -6
- package/dist/worker/observe/static/run-processing.js +4 -2
- package/dist/worker/observe/static/shell-chrome.js +2 -2
- package/dist/worker/observe/static/state.js +5 -0
- package/dist/worker/observe/static/styles.css +231 -0
- package/dist/worker/observe/static/views/dag-graph.js +1 -1
- package/dist/worker/observe/static/views/dag-inspector.js +336 -181
- package/dist/worker/observe/static/views/dag.js +26 -14
- package/dist/worker/observe/static/views/dags.js +3 -1
- package/dist/worker/observe/static/views/dashboard.js +14 -6
- package/dist/worker/observe/static/views/pool.js +7 -2
- package/dist/worker/observe/static/views/run.js +12 -3
- package/dist/worker/observe/static/views/session-timeline.js +74 -13
- package/dist/worker/observe/static/views/task.js +7 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-plan-protocol.js +210 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
- package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +1237 -192
- package/dist/workflows/dag/frontend-plan-render.js +2 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +226 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
- package/dist/workflows/dag/frontend-recovery-run.js +166 -34
- package/dist/workflows/dag/frontend-repair.js +1 -432
- package/dist/workflows/dag/frontend-review-context.js +261 -15
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +419 -0
- package/dist/workflows/dag/frontend-shape.js +435 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +252 -25
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +319 -0
- package/dist/workflows/dag/frontend-writer-status.js +256 -0
- package/dist/workflows/dag/init-hybrid.js +1031 -564
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +854 -4
- package/dist/workflows/dag/prompt.js +70 -3
- package/dist/workflows/dag/recovery-lease.js +170 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +315 -1
- package/dist/workflows/dag/rerun-plan.js +17 -6
- package/dist/workflows/dag/rerun-run.js +44 -6
- package/dist/workflows/dag/rerun-task.js +251 -13
- package/dist/workflows/dag/retry-policy.js +230 -104
- package/dist/workflows/dag/runner.js +580 -124
- package/dist/workflows/dag/scheduler.js +133 -20
- package/dist/workflows/dag/types.js +244 -16
- package/dist/workflows/dag/validate.js +28 -12
- package/docs/examples/README.md +5 -0
- package/docs/init-surface.manifest.json +30 -12
- package/docs/skills/vetted-skill-registry.md +4 -2
- package/docs/templates/README.md +2 -0
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/backend-test-dag.json +25 -20
- package/docs/templates/frontend-implementation-contract.schema.json +4 -1
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +4 -3
- package/skills/frontend-bounded-implement/SKILL.md +15 -14
- package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
- package/skills/frontend-contract/SKILL.md +23 -0
- package/skills/frontend-contract/references/contract-protocol.md +34 -0
- package/skills/frontend-design-review/SKILL.md +22 -41
- package/skills/frontend-plan/SKILL.md +26 -0
- package/skills/frontend-plan/references/decision-contract.md +37 -0
- package/skills/frontend-plan/references/design-decisions.md +17 -0
- package/skills/frontend-review/SKILL.md +20 -15
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/frontend-scout/SKILL.md +25 -0
- package/skills/frontend-scout/references/design-evidence.md +16 -0
- package/skills/frontend-scout/references/scout-evidence.md +23 -0
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/worker/console/static/assets/channel-BU5gOilw.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/index-H9rFJiGL.css +0 -1
- package/dist/worker/console/static/assets/index-xwu9GxEc.js +0 -451
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +0 -8
- package/skills/frontend-implementation/SKILL.md +0 -52
- package/skills/frontend-implementation/references/code-standards.md +0 -33
- package/skills/frontend-implementation/references/design-spec.md +0 -56
- package/skills/frontend-implementation/references/node-contracts.md +0 -31
|
@@ -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,12 +481,17 @@ function renderDagInspectorForCurrent() {
|
|
|
415
481
|
inspectorSelection.dagRunId,
|
|
416
482
|
inspectorSelection.node,
|
|
417
483
|
false,
|
|
484
|
+
inspectorSelection.run,
|
|
418
485
|
);
|
|
419
486
|
}
|
|
420
487
|
|
|
421
488
|
function resetInspectorNodeSelection(dagRunId, nodeId) {
|
|
422
489
|
uiState.selectedDagNodeId = nodeId;
|
|
423
490
|
uiState.dagInspectorOpen = true;
|
|
491
|
+
// Each node opens in live-follow mode by default. The explicit floating
|
|
492
|
+
// control can pause it without losing the reader's current position.
|
|
493
|
+
uiState.dagTimelineAutoScrollEnabled = true;
|
|
494
|
+
uiState.dagTimelineViewportState = null;
|
|
424
495
|
// Reset the per-attempt selection so an old node's attempt cannot leak
|
|
425
496
|
// into the newly selected node (always defaults back to the latest).
|
|
426
497
|
uiState.dagInspectorAttempt = null;
|
|
@@ -594,18 +665,13 @@ export function createDagInspectorResizeHandle(panel) {
|
|
|
594
665
|
export function captureDagTimelineViewportState(panel) {
|
|
595
666
|
const scrollEl = panel.querySelector(".process-timeline-scroll");
|
|
596
667
|
if (scrollEl?.dataset.dagRunId && scrollEl.dataset.nodeId) {
|
|
597
|
-
const followLatest = computeFollowLatest(
|
|
598
|
-
scrollEl.scrollTop,
|
|
599
|
-
scrollEl.scrollHeight,
|
|
600
|
-
scrollEl.clientHeight,
|
|
601
|
-
);
|
|
602
668
|
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
603
669
|
uiState.dagTimelineViewportState,
|
|
604
670
|
scrollEl.dataset.dagRunId,
|
|
605
671
|
scrollEl.dataset.nodeId,
|
|
606
672
|
scrollEl.scrollLeft,
|
|
607
673
|
scrollEl.scrollTop,
|
|
608
|
-
|
|
674
|
+
uiState.dagTimelineAutoScrollEnabled !== false,
|
|
609
675
|
);
|
|
610
676
|
}
|
|
611
677
|
}
|
|
@@ -629,6 +695,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
629
695
|
restoreInspectorContentScroll(content, dagRunId, nodeId, "spec-evidence");
|
|
630
696
|
return;
|
|
631
697
|
}
|
|
698
|
+
ensureInspectorPageLead(content, "spec-evidence");
|
|
632
699
|
if (!evidence) {
|
|
633
700
|
content.appendChild(el("p", "empty", "无法加载规范证据。"));
|
|
634
701
|
restoreInspectorContentScroll(content, dagRunId, nodeId, "spec-evidence");
|
|
@@ -639,8 +706,8 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
639
706
|
const statusLabels = {
|
|
640
707
|
"spec-read": "已读取规范",
|
|
641
708
|
"source-bound": "已绑定任务源",
|
|
642
|
-
"spec-injected": "
|
|
643
|
-
"search-only": "
|
|
709
|
+
"spec-injected": "已提供阅读能力,但没有读取记录",
|
|
710
|
+
"search-only": "只搜索过,没有读取内容",
|
|
644
711
|
"kb-queried": "已观察知识库查询",
|
|
645
712
|
"no-evidence": "未观察到读取",
|
|
646
713
|
};
|
|
@@ -681,7 +748,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
681
748
|
};
|
|
682
749
|
|
|
683
750
|
appendListSection(
|
|
684
|
-
"
|
|
751
|
+
"提供给节点的技能",
|
|
685
752
|
evidence.skillInjection?.skills,
|
|
686
753
|
(skill) => {
|
|
687
754
|
const item = el("li", null);
|
|
@@ -706,9 +773,9 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
706
773
|
icon.setAttribute("aria-hidden", "true");
|
|
707
774
|
button.append(
|
|
708
775
|
icon,
|
|
709
|
-
document.createTextNode(` ${source.kind}: `),
|
|
776
|
+
document.createTextNode(` ${inspectorListValueLabel(source.kind)}: `),
|
|
710
777
|
el("code", null, source.path),
|
|
711
|
-
document.createTextNode(` ·
|
|
778
|
+
document.createTextNode(` · 内容校验码 ${source.sha256.slice(0, 12)}…`),
|
|
712
779
|
);
|
|
713
780
|
button.addEventListener("click", () =>
|
|
714
781
|
onSpecEvidenceFileClick(
|
|
@@ -768,7 +835,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
768
835
|
item.appendChild(button);
|
|
769
836
|
return item;
|
|
770
837
|
},
|
|
771
|
-
"
|
|
838
|
+
"没有记录到节点读取规范文件。",
|
|
772
839
|
);
|
|
773
840
|
appendListSection(
|
|
774
841
|
`规范检索(${evidence.specSearches?.length ?? 0})`,
|
|
@@ -783,7 +850,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
783
850
|
);
|
|
784
851
|
return item;
|
|
785
852
|
},
|
|
786
|
-
"
|
|
853
|
+
"没有记录到节点检索规范。",
|
|
787
854
|
);
|
|
788
855
|
appendListSection(
|
|
789
856
|
`知识库查询(${evidence.knowledgeBaseQueries?.length ?? 0})`,
|
|
@@ -795,7 +862,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
795
862
|
item.append(icon, document.createTextNode(` ${query.connector}`));
|
|
796
863
|
return item;
|
|
797
864
|
},
|
|
798
|
-
"
|
|
865
|
+
"没有记录到知识库查询;可能未配置知识库连接,或节点没有使用它。",
|
|
799
866
|
);
|
|
800
867
|
|
|
801
868
|
if (
|
|
@@ -810,10 +877,10 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
|
|
|
810
877
|
icon,
|
|
811
878
|
document.createTextNode(
|
|
812
879
|
evidence.status === "source-bound"
|
|
813
|
-
? "
|
|
880
|
+
? "系统已确认任务来源和文件版本,但没有记录到节点成功读取完整文件。仅绑定文件不能证明节点真的读过内容。"
|
|
814
881
|
: evidence.status === "no-evidence"
|
|
815
|
-
? "
|
|
816
|
-
: "
|
|
882
|
+
? "没有记录到节点读取规范。模型总结里提到文件名,不等于节点真正读取了文件。"
|
|
883
|
+
: "已把规范阅读能力提供给节点,但没有记录到它读取规范或查询知识库;只搜索到文件名不等于读过文件内容。",
|
|
817
884
|
),
|
|
818
885
|
);
|
|
819
886
|
content.appendChild(warning);
|
|
@@ -924,7 +991,7 @@ function renderSpecEvidenceDetail(content, dagRunId, nodeId) {
|
|
|
924
991
|
el(
|
|
925
992
|
"span",
|
|
926
993
|
"spec-evidence-detail-hash",
|
|
927
|
-
|
|
994
|
+
`登记的内容校验码 ${String(detail.data.sha256 ?? "").slice(0, 12)}… · 当前文件${detail.data.hashMatch ? "一致" : "不一致"}`,
|
|
928
995
|
),
|
|
929
996
|
);
|
|
930
997
|
} else if (detail.source === "read" && detail.data?.readAt) {
|
|
@@ -970,15 +1037,22 @@ function renderSpecEvidenceDetail(content, dagRunId, nodeId) {
|
|
|
970
1037
|
|
|
971
1038
|
function sourceLabel(source) {
|
|
972
1039
|
if (source === "defaults") return "继承默认值";
|
|
973
|
-
if (source === "schema-default") return "
|
|
974
|
-
if (source === "merged") return "
|
|
1040
|
+
if (source === "schema-default") return "系统默认值";
|
|
1041
|
+
if (source === "merged") return "合并后的值";
|
|
975
1042
|
return null;
|
|
976
1043
|
}
|
|
977
1044
|
|
|
978
1045
|
function appendMetaRow(dl, label, value, source) {
|
|
979
1046
|
if (value == null || value === "") return;
|
|
980
|
-
const
|
|
981
|
-
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) dt.setAttribute("title", labelHelp.join("\n"));
|
|
1055
|
+
if (copy.rawValue) dd.setAttribute("title", `原始值:${copy.rawValue}`);
|
|
982
1056
|
const inherited = sourceLabel(source);
|
|
983
1057
|
if (inherited) {
|
|
984
1058
|
dd.appendChild(document.createTextNode(" "));
|
|
@@ -991,12 +1065,20 @@ function appendMetaRow(dl, label, value, source) {
|
|
|
991
1065
|
function appendCodeList(parent, title, values) {
|
|
992
1066
|
if (!values || values.length === 0) return;
|
|
993
1067
|
const section = el("section", "node-input-section");
|
|
994
|
-
|
|
1068
|
+
const heading = el("h4", null, inspectorFieldLabel(title));
|
|
1069
|
+
if (inspectorFieldLabel(title) !== title) {
|
|
1070
|
+
heading.setAttribute("title", `技术字段:${title}`);
|
|
1071
|
+
}
|
|
1072
|
+
section.appendChild(heading);
|
|
995
1073
|
const list = el("ul", "node-input-code-list");
|
|
996
1074
|
for (const value of values) {
|
|
997
1075
|
const item = document.createElement("li");
|
|
998
1076
|
const code = document.createElement("code");
|
|
999
|
-
|
|
1077
|
+
const rawValue = String(value);
|
|
1078
|
+
code.textContent = inspectorListValueLabel(rawValue);
|
|
1079
|
+
if (code.textContent !== rawValue) {
|
|
1080
|
+
code.setAttribute("title", `原始值:${rawValue}`);
|
|
1081
|
+
}
|
|
1000
1082
|
item.appendChild(code);
|
|
1001
1083
|
list.appendChild(item);
|
|
1002
1084
|
}
|
|
@@ -1064,9 +1146,9 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1064
1146
|
section.appendChild(el("h4", null, "任务正文"));
|
|
1065
1147
|
if (data.prompt.source?.path) {
|
|
1066
1148
|
const meta = el("p", "muted");
|
|
1067
|
-
meta.textContent =
|
|
1149
|
+
meta.textContent = `任务说明来源:${data.prompt.source.path}${
|
|
1068
1150
|
data.prompt.source.sha256Prefix
|
|
1069
|
-
?
|
|
1151
|
+
? `(内容校验码 ${data.prompt.source.sha256Prefix}…)`
|
|
1070
1152
|
: ""
|
|
1071
1153
|
}`;
|
|
1072
1154
|
section.appendChild(meta);
|
|
@@ -1086,7 +1168,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1086
1168
|
|
|
1087
1169
|
if (data.executorInput) {
|
|
1088
1170
|
const section = el("section", "node-input-section");
|
|
1089
|
-
section.appendChild(el("h4", null, "
|
|
1171
|
+
section.appendChild(el("h4", null, "将如何执行"));
|
|
1090
1172
|
const dl = el("dl", "node-input-summary");
|
|
1091
1173
|
appendMetaRow(dl, "kind", data.executorInput.kind, null);
|
|
1092
1174
|
if (data.executorInput.modelHint) {
|
|
@@ -1159,7 +1241,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1159
1241
|
|
|
1160
1242
|
if (data.boundaries) {
|
|
1161
1243
|
const section = el("section", "node-input-section");
|
|
1162
|
-
section.appendChild(el("h4", null, "
|
|
1244
|
+
section.appendChild(el("h4", null, "读写范围与输出要求"));
|
|
1163
1245
|
const dl = el("dl", "node-input-summary");
|
|
1164
1246
|
appendMetaRow(dl, "outputMode", data.boundaries.outputMode, null);
|
|
1165
1247
|
appendMetaRow(
|
|
@@ -1170,7 +1252,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1170
1252
|
);
|
|
1171
1253
|
section.appendChild(dl);
|
|
1172
1254
|
if (data.boundaries.outputContract) {
|
|
1173
|
-
section.appendChild(el("h5", null, "
|
|
1255
|
+
section.appendChild(el("h5", null, "输出内容要求"));
|
|
1174
1256
|
section.appendChild(renderMarkdown(data.boundaries.outputContract));
|
|
1175
1257
|
}
|
|
1176
1258
|
appendCodeList(section, "allowedPaths", data.boundaries.allowedPaths);
|
|
@@ -1182,7 +1264,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1182
1264
|
if (data.piExtensionRunFacts) {
|
|
1183
1265
|
const facts = data.piExtensionRunFacts;
|
|
1184
1266
|
const section = el("section", "node-input-section");
|
|
1185
|
-
section.appendChild(el("h4", null, "
|
|
1267
|
+
section.appendChild(el("h4", null, "AI 扩展加载结果"));
|
|
1186
1268
|
if (!facts.exists) {
|
|
1187
1269
|
section.appendChild(
|
|
1188
1270
|
el(
|
|
@@ -1223,7 +1305,7 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
1223
1305
|
|
|
1224
1306
|
if (data.promptFingerprint?.exists) {
|
|
1225
1307
|
const section = el("section", "node-input-section");
|
|
1226
|
-
section.appendChild(el("h4", null, "
|
|
1308
|
+
section.appendChild(el("h4", null, "最终提示内容校验信息"));
|
|
1227
1309
|
const dl = el("dl", "node-input-summary");
|
|
1228
1310
|
appendMetaRow(
|
|
1229
1311
|
dl,
|
|
@@ -1275,6 +1357,7 @@ async function renderDagNodeInput(content, dagRunId, node) {
|
|
|
1275
1357
|
preserveIntent: true,
|
|
1276
1358
|
});
|
|
1277
1359
|
clearNode(content);
|
|
1360
|
+
ensureInspectorPageLead(content, "input");
|
|
1278
1361
|
const loading = el("p", "muted", UI_TEXT.inputLoading);
|
|
1279
1362
|
loading.setAttribute("role", "status");
|
|
1280
1363
|
loading.setAttribute("aria-live", "polite");
|
|
@@ -1294,6 +1377,7 @@ async function renderDagNodeInput(content, dagRunId, node) {
|
|
|
1294
1377
|
}
|
|
1295
1378
|
|
|
1296
1379
|
clearNode(content);
|
|
1380
|
+
ensureInspectorPageLead(content, "input");
|
|
1297
1381
|
const finishInputRender = () => {
|
|
1298
1382
|
restoreInspectorContentScroll(content, dagRunId, nodeId, "input");
|
|
1299
1383
|
uiState.dagNodeInputLoading = false;
|
|
@@ -1356,31 +1440,45 @@ export function frontendGateLedgerLines(input) {
|
|
|
1356
1440
|
if (prewriteResult?.ok && prewriteResult.body) {
|
|
1357
1441
|
const parsed = prewriteResult.body;
|
|
1358
1442
|
if (parsed?.classification) {
|
|
1359
|
-
|
|
1443
|
+
const classifications = {
|
|
1444
|
+
accepted: "通过,可以进入写入阶段",
|
|
1445
|
+
blocked: "未通过,已阻止写入",
|
|
1446
|
+
warning: "有提醒,需要留意",
|
|
1447
|
+
};
|
|
1448
|
+
lines.push(
|
|
1449
|
+
`写入前检查:${classifications[parsed.classification] ?? String(parsed.classification)}`,
|
|
1450
|
+
);
|
|
1360
1451
|
if (parsed.failureCode) {
|
|
1361
|
-
lines.push(
|
|
1452
|
+
lines.push(`问题编号:${String(parsed.failureCode)}`);
|
|
1362
1453
|
}
|
|
1363
1454
|
if (parsed.failureReason) {
|
|
1364
|
-
lines.push(
|
|
1455
|
+
lines.push(`原因:${String(parsed.failureReason)}`);
|
|
1365
1456
|
}
|
|
1366
1457
|
} else {
|
|
1367
|
-
lines.push("
|
|
1458
|
+
lines.push("写入前检查结果无法解析,请查看运行记录。");
|
|
1368
1459
|
}
|
|
1369
1460
|
} else if (prewriteResult && prewriteResult.status === 404) {
|
|
1370
|
-
lines.push("
|
|
1461
|
+
lines.push("写入前检查尚未执行,或没有留下检查结果。");
|
|
1371
1462
|
} else if (prewriteResult) {
|
|
1372
1463
|
lines.push(
|
|
1373
|
-
"
|
|
1464
|
+
"暂时无法读取写入前检查结果;为安全起见,不把它当成已通过。",
|
|
1374
1465
|
);
|
|
1375
1466
|
} else {
|
|
1376
|
-
lines.push("
|
|
1467
|
+
lines.push("写入前检查尚未执行,或没有留下检查结果。");
|
|
1377
1468
|
}
|
|
1378
1469
|
if (ledgerBody && ledgerBody.present === true) {
|
|
1379
|
-
|
|
1470
|
+
const value = (candidate) =>
|
|
1471
|
+
typeof candidate === "number" ? candidate : "未知";
|
|
1472
|
+
lines.push(
|
|
1473
|
+
`需求覆盖记录:原始片段 ${value(ledgerBody.fragmentCount)} 个;整理后的需求 ${value(ledgerBody.canonicalRequirementCount)} 条;待处理 ${value(ledgerBody.unresolvedCount)} 条;冲突 ${value(ledgerBody.conflictCount)} 条。`,
|
|
1474
|
+
);
|
|
1475
|
+
if (ledgerBody.stale === true) {
|
|
1476
|
+
lines.push("提醒:任务来源后来发生过变化,这份需求覆盖记录可能已经过时。");
|
|
1477
|
+
}
|
|
1380
1478
|
} else if (ledgerBody) {
|
|
1381
|
-
lines.push("
|
|
1479
|
+
lines.push("没有需求覆盖记录;可能是任务没有引用资料,或记录尚未生成。不会猜测数量。");
|
|
1382
1480
|
} else if (taskId) {
|
|
1383
|
-
lines.push("
|
|
1481
|
+
lines.push("需求覆盖记录暂时无法查看;不会把读取失败当成没有问题。");
|
|
1384
1482
|
}
|
|
1385
1483
|
return lines;
|
|
1386
1484
|
}
|
|
@@ -1427,7 +1525,7 @@ export async function fetchFrontendGateLedgerData(dagRunId) {
|
|
|
1427
1525
|
async function renderFrontendGateLedger(content, dagRunId) {
|
|
1428
1526
|
const section = el("section", "node-input-section");
|
|
1429
1527
|
section.appendChild(
|
|
1430
|
-
el("h4", null, "
|
|
1528
|
+
el("h4", null, "前端写入前检查与需求覆盖记录"),
|
|
1431
1529
|
);
|
|
1432
1530
|
const status = el("p", "muted", "加载中…");
|
|
1433
1531
|
status.setAttribute("role", "status");
|
|
@@ -1473,11 +1571,11 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1473
1571
|
const heading = el("div", "context-export-heading");
|
|
1474
1572
|
const headingCopy = el("div", null);
|
|
1475
1573
|
headingCopy.appendChild(el("span", "context-export-eyebrow", "上下文交接包"));
|
|
1476
|
-
const title = el("h4", null, "
|
|
1574
|
+
const title = el("h4", null, "把这个节点的资料交给另一个 AI");
|
|
1477
1575
|
title.id = "context-export-title";
|
|
1478
1576
|
headingCopy.appendChild(title);
|
|
1479
1577
|
headingCopy.appendChild(
|
|
1480
|
-
el("p", null, "
|
|
1578
|
+
el("p", null, "选择接收方式和是否脱敏,系统会生成一个带来源校验信息的 ZIP 交接包。"),
|
|
1481
1579
|
);
|
|
1482
1580
|
const invariant = el("div", "context-export-invariant");
|
|
1483
1581
|
const lockIcon = el("i", "ri-lock-2-line");
|
|
@@ -1561,10 +1659,10 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1561
1659
|
ticket.setAttribute("aria-live", "polite");
|
|
1562
1660
|
const ticketTop = el("div", "context-export-ticket-top");
|
|
1563
1661
|
ticketTop.append(
|
|
1564
|
-
el("span", "context-export-ticket-type", "
|
|
1662
|
+
el("span", "context-export-ticket-type", "交接资料 / ZIP"),
|
|
1565
1663
|
el("span", "context-export-ticket-safety", "已脱敏"),
|
|
1566
1664
|
);
|
|
1567
|
-
const ticketTitle = el("strong", "context-export-ticket-title", "通用
|
|
1665
|
+
const ticketTitle = el("strong", "context-export-ticket-title", "通用 AI 交接包");
|
|
1568
1666
|
const ticketRows = el("dl", "context-export-ticket-rows");
|
|
1569
1667
|
const appendTicketRow = (label, value, className) => {
|
|
1570
1668
|
const term = el("dt", null, label);
|
|
@@ -1572,12 +1670,12 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1572
1670
|
ticketRows.append(term, detail);
|
|
1573
1671
|
return detail;
|
|
1574
1672
|
};
|
|
1575
|
-
const recipientValue = appendTicketRow("用途", "适合交给其他
|
|
1673
|
+
const recipientValue = appendTicketRow("用途", "适合交给其他 AI 工具继续处理");
|
|
1576
1674
|
const contentsValue = appendTicketRow("包含", CONTEXT_EXPORT_TARGETS[0].contents, "is-mono");
|
|
1577
1675
|
const protectionValue = appendTicketRow("保护", "自动隐藏匹配到的疑似凭据");
|
|
1578
1676
|
const attemptValue = appendTicketRow(
|
|
1579
1677
|
"来源",
|
|
1580
|
-
`${node.nodeId} ·
|
|
1678
|
+
`${node.nodeId} · 第 ${selectedAttemptNumber(dagRunId, node) ?? 1} 次执行`,
|
|
1581
1679
|
"is-mono",
|
|
1582
1680
|
);
|
|
1583
1681
|
ticket.append(
|
|
@@ -1585,7 +1683,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1585
1683
|
el("span", "context-export-ticket-label", "即将生成"),
|
|
1586
1684
|
ticketTitle,
|
|
1587
1685
|
ticketRows,
|
|
1588
|
-
el("p", "context-export-ticket-foot", "
|
|
1686
|
+
el("p", "context-export-ticket-foot", "附带文件清单和内容校验码,可以核对资料来源与完整性"),
|
|
1589
1687
|
);
|
|
1590
1688
|
workspace.appendChild(ticket);
|
|
1591
1689
|
section.appendChild(workspace);
|
|
@@ -1605,7 +1703,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1605
1703
|
recipientValue.textContent = target.description;
|
|
1606
1704
|
contentsValue.textContent = target.contents;
|
|
1607
1705
|
protectionValue.textContent = profile.description;
|
|
1608
|
-
attemptValue.textContent = `${node.nodeId} ·
|
|
1706
|
+
attemptValue.textContent = `${node.nodeId} · 第 ${selectedAttemptNumber(dagRunId, node) ?? 1} 次执行`;
|
|
1609
1707
|
const safety = ticket.querySelector(".context-export-ticket-safety");
|
|
1610
1708
|
if (safety) {
|
|
1611
1709
|
safety.textContent = profileValue === "raw" ? "原始内容" : "已脱敏";
|
|
@@ -1648,7 +1746,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1648
1746
|
}
|
|
1649
1747
|
submit.disabled = true;
|
|
1650
1748
|
try {
|
|
1651
|
-
setStatus("
|
|
1749
|
+
setStatus("正在生成带来源校验信息的交接包…");
|
|
1652
1750
|
const clientRequestId = `inspect-context-export-${Date.now()}-${Math.random()
|
|
1653
1751
|
.toString(36)
|
|
1654
1752
|
.slice(2, 10)}`;
|
|
@@ -1674,7 +1772,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1674
1772
|
if (operation?.state !== "succeeded" || !operation.result?.downloadUrl) {
|
|
1675
1773
|
throw new Error(operation?.errorMessage ?? "导出未成功完成");
|
|
1676
1774
|
}
|
|
1677
|
-
setStatus("
|
|
1775
|
+
setStatus("交接包已生成,正在安全下载…");
|
|
1678
1776
|
const downloaded = await downloadContextExport(operation.result.downloadUrl);
|
|
1679
1777
|
const objectUrl = URL.createObjectURL(downloaded.blob);
|
|
1680
1778
|
const link = document.createElement("a");
|
|
@@ -1685,7 +1783,7 @@ function buildContextExportSection(dagRunId, node) {
|
|
|
1685
1783
|
link.remove();
|
|
1686
1784
|
URL.revokeObjectURL(objectUrl);
|
|
1687
1785
|
setStatus(
|
|
1688
|
-
`下载完成 · ${operation.result.bundleId} · ${new Intl.NumberFormat("zh-CN").format(operation.result.bytes)}
|
|
1786
|
+
`下载完成 · ${operation.result.bundleId} · ${new Intl.NumberFormat("zh-CN").format(operation.result.bytes)} 字节\n内容校验码 ${operation.result.bundleSha256}`,
|
|
1689
1787
|
);
|
|
1690
1788
|
} catch (error) {
|
|
1691
1789
|
setStatus(transferErrorStatusText(error, "导出未成功完成"), true);
|
|
@@ -1817,14 +1915,14 @@ function createArtifactContentPreview(
|
|
|
1817
1915
|
el(
|
|
1818
1916
|
"span",
|
|
1819
1917
|
null,
|
|
1820
|
-
`${data.mediaType ?? "
|
|
1918
|
+
`${data.mediaType ?? "未知类型"} · ${new Intl.NumberFormat("zh-CN").format(data.sourceBytes ?? 0)} 字节`,
|
|
1821
1919
|
),
|
|
1822
1920
|
];
|
|
1823
1921
|
if (typeof data.sha256 === "string" && data.sha256.length >= 12) {
|
|
1824
1922
|
const sha = el(
|
|
1825
1923
|
"span",
|
|
1826
1924
|
null,
|
|
1827
|
-
` ·
|
|
1925
|
+
` · 内容校验码 ${data.sha256.slice(0, 12)}…`,
|
|
1828
1926
|
);
|
|
1829
1927
|
sha.title = data.sha256;
|
|
1830
1928
|
metaChildren.push(sha);
|
|
@@ -1836,7 +1934,7 @@ function createArtifactContentPreview(
|
|
|
1836
1934
|
el(
|
|
1837
1935
|
"p",
|
|
1838
1936
|
"artifact-content-preview-unavailable",
|
|
1839
|
-
"
|
|
1937
|
+
"该文件类型不适合直接在页面中显示;系统已经核对文件位置、大小和内容校验码。",
|
|
1840
1938
|
),
|
|
1841
1939
|
);
|
|
1842
1940
|
return;
|
|
@@ -1857,7 +1955,7 @@ function createArtifactContentPreview(
|
|
|
1857
1955
|
if (data.redacted) notes.push("疑似凭据已脱敏");
|
|
1858
1956
|
if (data.truncated) {
|
|
1859
1957
|
notes.push(
|
|
1860
|
-
`仅显示前 ${new Intl.NumberFormat("zh-CN").format(data.maxBytes ?? 0)}
|
|
1958
|
+
`仅显示前 ${new Intl.NumberFormat("zh-CN").format(data.maxBytes ?? 0)} 字节`,
|
|
1861
1959
|
);
|
|
1862
1960
|
}
|
|
1863
1961
|
if (notes.length > 0) {
|
|
@@ -1873,19 +1971,57 @@ function createArtifactContentPreview(
|
|
|
1873
1971
|
return { element: details, reset };
|
|
1874
1972
|
}
|
|
1875
1973
|
|
|
1876
|
-
function
|
|
1974
|
+
export function artifactRevisionRunEligibility(run) {
|
|
1975
|
+
// File-level eligibility only says that a file has a known replacement
|
|
1976
|
+
// contract. Creating a derived run has stricter, run-level safeguards.
|
|
1977
|
+
// Keep this check here so the UI does not invite an action the server will
|
|
1978
|
+
// necessarily reject later.
|
|
1979
|
+
if (!run) return { eligible: true };
|
|
1980
|
+
const lifecycle = String(run.lifecycle ?? "").toLowerCase();
|
|
1981
|
+
const status = String(run.status ?? "").toLowerCase();
|
|
1982
|
+
if (lifecycle !== "completed") {
|
|
1983
|
+
return {
|
|
1984
|
+
eligible: false,
|
|
1985
|
+
title: "这次运行还没有完整结束",
|
|
1986
|
+
description:
|
|
1987
|
+
"请等待运行收尾为“已完成”。进行中、暂停中,或尚未收尾的报错,都不能从替换产出继续。",
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
if (!new Set(["failed", "partial_failed"]).has(status)) {
|
|
1991
|
+
return {
|
|
1992
|
+
eligible: false,
|
|
1993
|
+
title: "这次运行不支持替换产出后继续",
|
|
1994
|
+
description:
|
|
1995
|
+
"只有已完整结束且结果为“失败”或“部分失败”的运行,才能换文件后继续。当前运行已经成功完成;文件本身可替换,不代表可以创建后续运行。",
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
return { eligible: true };
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
function buildArtifactRevisionSection(dagRunId, node, run) {
|
|
1877
2002
|
const { section, body } = createDagActionCard({
|
|
1878
|
-
eyebrow: "
|
|
1879
|
-
title: "
|
|
1880
|
-
description: "
|
|
2003
|
+
eyebrow: "替换已有文件",
|
|
2004
|
+
title: "换掉这个节点的产出后继续",
|
|
2005
|
+
description: "上传新文件后,系统会先检查格式并告诉你哪些后续节点需要重做;确认前不会改动原运行。",
|
|
1881
2006
|
icon: "ri-file-transfer-line",
|
|
1882
2007
|
className: "artifact-revision-box",
|
|
1883
2008
|
});
|
|
1884
2009
|
section.dataset.actionSection = "artifact-revision";
|
|
2010
|
+
const runEligibility = artifactRevisionRunEligibility(run);
|
|
2011
|
+
if (!runEligibility.eligible) {
|
|
2012
|
+
body.appendChild(
|
|
2013
|
+
buildDagActionAvailability({
|
|
2014
|
+
tone: "blocked",
|
|
2015
|
+
title: runEligibility.title,
|
|
2016
|
+
description: runEligibility.description,
|
|
2017
|
+
}),
|
|
2018
|
+
);
|
|
2019
|
+
return section;
|
|
2020
|
+
}
|
|
1885
2021
|
body.appendChild(
|
|
1886
2022
|
buildDagActionAvailability({
|
|
1887
|
-
title: "
|
|
1888
|
-
description: "
|
|
2023
|
+
title: "正在检查这个文件能不能安全替换",
|
|
2024
|
+
description: "系统正在读取运行开始前保存的文件关系和替换规则。",
|
|
1889
2025
|
}),
|
|
1890
2026
|
);
|
|
1891
2027
|
void (async () => {
|
|
@@ -1895,7 +2031,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1895
2031
|
body.appendChild(
|
|
1896
2032
|
buildDagActionAvailability({
|
|
1897
2033
|
tone: "blocked",
|
|
1898
|
-
title: "
|
|
2034
|
+
title: "这个运行暂时不能安全替换产出",
|
|
1899
2035
|
description:
|
|
1900
2036
|
"缺少结构化的产出关系,系统无法可靠判断哪些后续节点会受影响,也不会根据文件名或提示词猜测。",
|
|
1901
2037
|
reasonCode:
|
|
@@ -1922,8 +2058,8 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1922
2058
|
body.appendChild(
|
|
1923
2059
|
buildDagActionAvailability({
|
|
1924
2060
|
tone: "ready",
|
|
1925
|
-
title:
|
|
1926
|
-
description: "
|
|
2061
|
+
title: `可以替换 ${eligible.length} 个已生成文件`,
|
|
2062
|
+
description: "原运行不会被修改;确认后系统会另外创建一次新运行。",
|
|
1927
2063
|
}),
|
|
1928
2064
|
);
|
|
1929
2065
|
const form = el("div", "dag-action-form artifact-revision-form");
|
|
@@ -1952,7 +2088,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1952
2088
|
form.appendChild(currentArtifactPreview.element);
|
|
1953
2089
|
artifactSelect.addEventListener("change", currentArtifactPreview.reset);
|
|
1954
2090
|
const fileLabel = el("label", "prompt-restart-field");
|
|
1955
|
-
fileLabel.appendChild(el("span", "dag-action-field-label", "选择新文件(最多 10
|
|
2091
|
+
fileLabel.appendChild(el("span", "dag-action-field-label", "选择新文件(最多 10 MB)"));
|
|
1956
2092
|
const file = document.createElement("input");
|
|
1957
2093
|
file.type = "file";
|
|
1958
2094
|
file.className = "dag-action-file-input";
|
|
@@ -1964,7 +2100,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1964
2100
|
const reason = document.createElement("textarea");
|
|
1965
2101
|
reason.className = "prompt-restart-textarea";
|
|
1966
2102
|
reason.rows = 3;
|
|
1967
|
-
reason.placeholder = "
|
|
2103
|
+
reason.placeholder = "例如:计划遗漏了一个验收条件,需要更正后继续";
|
|
1968
2104
|
reason.dataset.testid = "artifact-revision-reason";
|
|
1969
2105
|
reasonLabel.appendChild(reason);
|
|
1970
2106
|
form.appendChild(reasonLabel);
|
|
@@ -1998,7 +2134,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
1998
2134
|
return;
|
|
1999
2135
|
}
|
|
2000
2136
|
if (selectedFile.size > 10 * 1024 * 1024) {
|
|
2001
|
-
setStatus("文件超过 10
|
|
2137
|
+
setStatus("文件超过 10 MB 限制。", true);
|
|
2002
2138
|
return;
|
|
2003
2139
|
}
|
|
2004
2140
|
if (!reason.value.trim()) {
|
|
@@ -2015,7 +2151,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2015
2151
|
reader.readAsDataURL(selectedFile);
|
|
2016
2152
|
});
|
|
2017
2153
|
const candidateBase64 = String(dataUrl).split(",", 2)[1] ?? "";
|
|
2018
|
-
setStatus("
|
|
2154
|
+
setStatus("正在检查新文件格式并对比差异…");
|
|
2019
2155
|
const stageId = `artifact-stage-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2020
2156
|
const staged = await postOperatorAction(
|
|
2021
2157
|
"artifactRevisionStage",
|
|
@@ -2036,7 +2172,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2036
2172
|
if (stageOp?.state !== "succeeded" || !revision?.revisionId) {
|
|
2037
2173
|
throw new Error(stageOp?.errorMessage ?? "修订校验失败");
|
|
2038
2174
|
}
|
|
2039
|
-
setStatus("
|
|
2175
|
+
setStatus("文件检查通过,正在计算哪些后续节点需要重新执行…");
|
|
2040
2176
|
const planId = `artifact-plan-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2041
2177
|
const planned = await postOperatorAction(
|
|
2042
2178
|
"artifactRevisionPlan",
|
|
@@ -2049,17 +2185,23 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2049
2185
|
);
|
|
2050
2186
|
const plan = planOp?.result?.plan;
|
|
2051
2187
|
if (!plan) throw new Error(planOp?.errorMessage ?? "影响预览失败");
|
|
2052
|
-
planHost.appendChild(el("h5", null, "
|
|
2188
|
+
planHost.appendChild(el("h5", null, "替换结果与影响范围"));
|
|
2053
2189
|
planHost.appendChild(
|
|
2054
2190
|
transparencyMetaList([
|
|
2055
|
-
["
|
|
2056
|
-
["
|
|
2057
|
-
[
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
[
|
|
2062
|
-
|
|
2191
|
+
["本次替换编号", revision.revisionId],
|
|
2192
|
+
["新文件校验码", revision.candidate?.sha256],
|
|
2193
|
+
[
|
|
2194
|
+
"文件内容类型",
|
|
2195
|
+
inspectorValueLabel("差异类型", revision.diff?.kind),
|
|
2196
|
+
],
|
|
2197
|
+
[
|
|
2198
|
+
"预计风险",
|
|
2199
|
+
inspectorValueLabel("风险级别", plan.risk),
|
|
2200
|
+
],
|
|
2201
|
+
["最先受影响的节点", plan.impact?.directConsumerNodeIds?.join(", ") || "无"],
|
|
2202
|
+
["会重新执行的节点", plan.impact?.affectedNodeIds?.join(", ") || "无"],
|
|
2203
|
+
["会沿用结果的节点", plan.impact?.importedNodeIds?.join(", ") || "无"],
|
|
2204
|
+
["本次计划校验码", plan.planHash],
|
|
2063
2205
|
]),
|
|
2064
2206
|
);
|
|
2065
2207
|
if (revision.diff?.summary) {
|
|
@@ -2070,7 +2212,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2070
2212
|
}
|
|
2071
2213
|
if (plan.eligible !== true) {
|
|
2072
2214
|
setStatus(
|
|
2073
|
-
|
|
2215
|
+
`暂时不能创建新运行:${formatContextTransferReasonList(plan.blockedReasons) ?? "当前不满足继续运行的条件"}`,
|
|
2074
2216
|
true,
|
|
2075
2217
|
);
|
|
2076
2218
|
return;
|
|
@@ -2085,7 +2227,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2085
2227
|
void (async () => {
|
|
2086
2228
|
execute.disabled = true;
|
|
2087
2229
|
try {
|
|
2088
|
-
setStatus("
|
|
2230
|
+
setStatus("正在创建新运行并重新执行受影响节点;原运行保持不变…");
|
|
2089
2231
|
const executeId = `artifact-execute-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2090
2232
|
const accepted = await postOperatorAction(
|
|
2091
2233
|
"artifactRevisionExecute",
|
|
@@ -2098,7 +2240,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2098
2240
|
},
|
|
2099
2241
|
executeId,
|
|
2100
2242
|
);
|
|
2101
|
-
if (!accepted.ok || !accepted.body?.operationId) throw new Error(accepted.body?.error?.message ?? "
|
|
2243
|
+
if (!accepted.ok || !accepted.body?.operationId) throw new Error(accepted.body?.error?.message ?? "新运行未被接受");
|
|
2102
2244
|
const operation = await pollOperationToTerminal(accepted.body.operationId, setStatus);
|
|
2103
2245
|
const childRunId = operation?.result?.newRunId;
|
|
2104
2246
|
if (childRunId) {
|
|
@@ -2111,22 +2253,22 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2111
2253
|
? "打开新运行"
|
|
2112
2254
|
: "打开失败的新运行";
|
|
2113
2255
|
open.addEventListener("click", () => {
|
|
2114
|
-
|
|
2256
|
+
navigate(`/dag/${encodeURIComponent(childRunId)}`);
|
|
2115
2257
|
});
|
|
2116
2258
|
planHost.appendChild(open);
|
|
2117
2259
|
}
|
|
2118
2260
|
if (operation?.state !== "succeeded" || !operation.result?.newRunId) {
|
|
2119
|
-
throw new Error(operation?.errorMessage ?? operation?.result?.message ?? "
|
|
2261
|
+
throw new Error(operation?.errorMessage ?? operation?.result?.message ?? "新运行失败");
|
|
2120
2262
|
}
|
|
2121
|
-
setStatus(
|
|
2263
|
+
setStatus(`新运行完成:${operation.result.newRunId}`);
|
|
2122
2264
|
} catch (error) {
|
|
2123
|
-
setStatus(transferErrorStatusText(error, "
|
|
2265
|
+
setStatus(transferErrorStatusText(error, "新运行失败"), true);
|
|
2124
2266
|
} finally {
|
|
2125
2267
|
execute.disabled = false;
|
|
2126
2268
|
}
|
|
2127
2269
|
})();
|
|
2128
2270
|
});
|
|
2129
|
-
setStatus("
|
|
2271
|
+
setStatus("影响范围已经锁定;确认时系统会再次检查文件、运行和计划是否有变化。" );
|
|
2130
2272
|
} catch (error) {
|
|
2131
2273
|
setStatus(transferErrorStatusText(error, "影响预览失败"), true);
|
|
2132
2274
|
} finally {
|
|
@@ -2150,20 +2292,26 @@ function buildDagActionIntro(node) {
|
|
|
2150
2292
|
"重跑会创建新的运行;当前运行及其已经保存的事实始终保持不变。",
|
|
2151
2293
|
),
|
|
2152
2294
|
);
|
|
2153
|
-
const
|
|
2295
|
+
const rawState = node.status ?? "unknown";
|
|
2296
|
+
const state = el(
|
|
2297
|
+
"span",
|
|
2298
|
+
"dag-action-node-state",
|
|
2299
|
+
inspectorValueLabel("status", rawState),
|
|
2300
|
+
);
|
|
2301
|
+
if (state.textContent !== rawState) state.setAttribute("title", `原始值:${rawState}`);
|
|
2154
2302
|
intro.append(copy, state);
|
|
2155
2303
|
return intro;
|
|
2156
2304
|
}
|
|
2157
2305
|
|
|
2158
|
-
async function renderPromptRestart(content, dagRunId, node) {
|
|
2306
|
+
async function renderPromptRestart(content, dagRunId, node, run) {
|
|
2159
2307
|
const nodeId = node.nodeId;
|
|
2160
2308
|
clearNode(content);
|
|
2161
2309
|
const intro = buildDagActionIntro(node);
|
|
2162
|
-
const revisionSection = buildArtifactRevisionSection(dagRunId, node);
|
|
2310
|
+
const revisionSection = buildArtifactRevisionSection(dagRunId, node, run);
|
|
2163
2311
|
const { section: promptSection, body: promptBody } = createDagActionCard({
|
|
2164
2312
|
eyebrow: "提示词重跑",
|
|
2165
2313
|
title: "修改失败提示后重新执行",
|
|
2166
|
-
description: "
|
|
2314
|
+
description: "只有执行失败的节点可以修改提示词并从这里继续;其他节点只能查看运行开始前保存的提示词。",
|
|
2167
2315
|
icon: "ri-restart-line",
|
|
2168
2316
|
className: "prompt-rerun-action-box",
|
|
2169
2317
|
});
|
|
@@ -2171,7 +2319,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2171
2319
|
promptBody.appendChild(
|
|
2172
2320
|
buildDagActionAvailability({
|
|
2173
2321
|
title: "正在读取节点提示",
|
|
2174
|
-
description: "
|
|
2322
|
+
description: "检查运行开始前保存的版本,以及每次失败时实际使用的版本。",
|
|
2175
2323
|
}),
|
|
2176
2324
|
);
|
|
2177
2325
|
const exportSection = buildContextExportSection(dagRunId, node);
|
|
@@ -2200,7 +2348,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2200
2348
|
promptBody.appendChild(
|
|
2201
2349
|
buildDagActionAvailability({
|
|
2202
2350
|
tone: "blocked",
|
|
2203
|
-
title: "
|
|
2351
|
+
title: "无法读取这个节点原先保存的提示词",
|
|
2204
2352
|
description: "当前不能安全预览或修改提示词。可以继续使用最下方的上下文导出。",
|
|
2205
2353
|
}),
|
|
2206
2354
|
);
|
|
@@ -2211,7 +2359,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2211
2359
|
if (!frozenBasePrompt) {
|
|
2212
2360
|
promptBody.appendChild(
|
|
2213
2361
|
buildDagActionAvailability({
|
|
2214
|
-
title: "
|
|
2362
|
+
title: "这个节点没有保存原始提示词",
|
|
2215
2363
|
description: "因此不能从这里修改提示词重跑。",
|
|
2216
2364
|
}),
|
|
2217
2365
|
);
|
|
@@ -2237,12 +2385,12 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2237
2385
|
createPromptRestartBanner({
|
|
2238
2386
|
tone: "readonly",
|
|
2239
2387
|
kicker: "只读",
|
|
2240
|
-
title:
|
|
2241
|
-
body: "
|
|
2388
|
+
title: `当前节点:${inspectorValueLabel("status", node.status ?? "unknown")}`,
|
|
2389
|
+
body: "只有执行失败的节点支持编辑提示词并重启。下面可以查看原始提示词,以及历次失败时实际使用的提示词。",
|
|
2242
2390
|
}),
|
|
2243
2391
|
);
|
|
2244
2392
|
const promptCard = createPromptRestartPromptCard({
|
|
2245
|
-
title: "
|
|
2393
|
+
title: "运行开始前保存的提示词",
|
|
2246
2394
|
badge: "只读",
|
|
2247
2395
|
readCandidate: () => selectedCandidate,
|
|
2248
2396
|
preTestId: "prompt-restart-readonly",
|
|
@@ -2254,9 +2402,9 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2254
2402
|
hint:
|
|
2255
2403
|
promptCandidateOptions.length === 1
|
|
2256
2404
|
? historicalCandidatesMissing
|
|
2257
|
-
? "
|
|
2258
|
-
: "
|
|
2259
|
-
: "
|
|
2405
|
+
? "这个较早的运行没有保存失败时实际使用的提示词;新运行会在这里列出。"
|
|
2406
|
+
: "该节点没有保存过失败时使用的提示词。"
|
|
2407
|
+
: "可以切换查看原始版本和历次失败时使用的版本。",
|
|
2260
2408
|
onChange: (nextKey) => {
|
|
2261
2409
|
selectedCandidate =
|
|
2262
2410
|
promptCandidateOptions.find((option) => option.key === nextKey) ??
|
|
@@ -2274,7 +2422,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2274
2422
|
promptBody.appendChild(
|
|
2275
2423
|
buildDagActionAvailability({
|
|
2276
2424
|
tone: "blocked",
|
|
2277
|
-
title: "
|
|
2425
|
+
title: "保存的提示词不完整,无法安全编辑",
|
|
2278
2426
|
description:
|
|
2279
2427
|
"为避免用截断文本覆盖完整提示,请改用 CLI:dag rerun --prompt-override。",
|
|
2280
2428
|
}),
|
|
@@ -2288,11 +2436,11 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2288
2436
|
tone: "edit",
|
|
2289
2437
|
kicker: "可编辑",
|
|
2290
2438
|
title: "从失败节点编辑提示词并重启",
|
|
2291
|
-
body: "
|
|
2439
|
+
body: "原运行的设置和记录保持不变;系统会另建一次使用新提示词的运行。",
|
|
2292
2440
|
}),
|
|
2293
2441
|
);
|
|
2294
2442
|
const promptCard = createPromptRestartPromptCard({
|
|
2295
|
-
title:
|
|
2443
|
+
title: `原始提示词(只读 · ${nodeId})`,
|
|
2296
2444
|
badge: "底稿",
|
|
2297
2445
|
readCandidate: () => selectedCandidate,
|
|
2298
2446
|
preTestId: "prompt-restart-base",
|
|
@@ -2305,10 +2453,10 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2305
2453
|
label: "提示词版本",
|
|
2306
2454
|
hint:
|
|
2307
2455
|
promptCandidateOptions.length > 1
|
|
2308
|
-
? "
|
|
2456
|
+
? "选择某次失败使用的版本后,会载入完整文本供你修改;原始提示词仍可选择只追加补充说明。"
|
|
2309
2457
|
: historicalCandidatesMissing
|
|
2310
|
-
? "
|
|
2311
|
-
: "
|
|
2458
|
+
? "这个较早的运行没有保存失败时实际使用的提示词;新运行会在这里列出。"
|
|
2459
|
+
: "该节点没有保存过失败时使用的提示词;当前只有原始提示词。",
|
|
2312
2460
|
onChange: (nextKey) => {
|
|
2313
2461
|
selectedCandidate =
|
|
2314
2462
|
promptCandidateOptions.find((option) => option.key === nextKey) ??
|
|
@@ -2322,8 +2470,8 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2322
2470
|
picker.hintEl.textContent = selectedCandidate.truncated
|
|
2323
2471
|
? "该候选已截断,仅供查看,不能直接提交重启。"
|
|
2324
2472
|
: selectedCandidate.key === "frozen"
|
|
2325
|
-
? "
|
|
2326
|
-
: "
|
|
2473
|
+
? "原始提示词可以选择追加说明,也可以全文替换。"
|
|
2474
|
+
: "已载入这次失败时使用的提示词;修改完整文本后即可重启。";
|
|
2327
2475
|
applyModeVisibility();
|
|
2328
2476
|
},
|
|
2329
2477
|
});
|
|
@@ -2345,7 +2493,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2345
2493
|
el(
|
|
2346
2494
|
"p",
|
|
2347
2495
|
"prompt-restart-hint",
|
|
2348
|
-
"
|
|
2496
|
+
"这段补充会加在原提示词末尾,不会改写原文(内部记录为 operator_addendum)。",
|
|
2349
2497
|
),
|
|
2350
2498
|
);
|
|
2351
2499
|
const addendumArea = document.createElement("textarea");
|
|
@@ -2380,14 +2528,14 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2380
2528
|
applyModeVisibility();
|
|
2381
2529
|
|
|
2382
2530
|
const reasonLabel = el("label", "prompt-restart-field");
|
|
2383
|
-
reasonLabel.appendChild(el("span", "prompt-restart-label", "
|
|
2531
|
+
reasonLabel.appendChild(el("span", "prompt-restart-label", "为什么要重启?"));
|
|
2384
2532
|
reasonLabel.appendChild(
|
|
2385
|
-
el("p", "prompt-restart-hint", "
|
|
2533
|
+
el("p", "prompt-restart-hint", "这段说明会随新运行一起保存,方便以后追溯。"),
|
|
2386
2534
|
);
|
|
2387
2535
|
const reasonArea = document.createElement("textarea");
|
|
2388
2536
|
reasonArea.className = "prompt-restart-textarea prompt-restart-textarea-compact";
|
|
2389
2537
|
reasonArea.rows = 3;
|
|
2390
|
-
reasonArea.value =
|
|
2538
|
+
reasonArea.value = `修改失败节点 ${nodeId} 的提示词后重新运行(来源运行:${dagRunId})`;
|
|
2391
2539
|
reasonArea.setAttribute("data-testid", "prompt-restart-reason");
|
|
2392
2540
|
reasonLabel.appendChild(reasonArea);
|
|
2393
2541
|
box.appendChild(reasonLabel);
|
|
@@ -2399,7 +2547,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2399
2547
|
const submit = document.createElement("button");
|
|
2400
2548
|
submit.type = "button";
|
|
2401
2549
|
submit.className = "prompt-restart-submit";
|
|
2402
|
-
submit.textContent = "
|
|
2550
|
+
submit.textContent = "检查是否可重跑并开始";
|
|
2403
2551
|
submit.setAttribute("data-testid", "prompt-restart-submit");
|
|
2404
2552
|
row.appendChild(status);
|
|
2405
2553
|
row.appendChild(submit);
|
|
@@ -2442,7 +2590,7 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2442
2590
|
}
|
|
2443
2591
|
submit.disabled = true;
|
|
2444
2592
|
try {
|
|
2445
|
-
setStatus("
|
|
2593
|
+
setStatus("正在检查这次重跑是否安全可行…");
|
|
2446
2594
|
const overrides = [{ nodeId, prompt, mode }];
|
|
2447
2595
|
const planRes = await postOperatorAction("dagRerunPlan", {
|
|
2448
2596
|
runId: dagRunId,
|
|
@@ -2462,9 +2610,9 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2462
2610
|
|
|
2463
2611
|
const est = plan.estimatedExecution;
|
|
2464
2612
|
const estText = est
|
|
2465
|
-
? `(将执行 ${est.nodeCount ?? "?"}
|
|
2613
|
+
? `(将执行 ${est.nodeCount ?? "?"} 个节点,其中 ${est.piCallCount ?? "?"} 个需要 AI 处理)`
|
|
2466
2614
|
: "";
|
|
2467
|
-
setStatus(
|
|
2615
|
+
setStatus(`检查通过${estText},正在创建新运行…`);
|
|
2468
2616
|
const clientRequestId = `inspect-prompt-restart-${Date.now()}-${Math.random()
|
|
2469
2617
|
.toString(36)
|
|
2470
2618
|
.slice(2, 10)}`;
|
|
@@ -2492,21 +2640,21 @@ async function renderPromptRestart(content, dagRunId, node) {
|
|
|
2492
2640
|
const newRunId =
|
|
2493
2641
|
op.dagRunId ?? op.result?.newRunId ?? op.runSummary?.runId ?? null;
|
|
2494
2642
|
if (newRunId) {
|
|
2495
|
-
setStatus(
|
|
2643
|
+
setStatus(`重启完成,新运行:${newRunId}`);
|
|
2496
2644
|
const open = document.createElement("button");
|
|
2497
2645
|
open.type = "button";
|
|
2498
2646
|
open.className = "prompt-restart-submit is-secondary";
|
|
2499
|
-
open.textContent = "
|
|
2647
|
+
open.textContent = "打开新运行";
|
|
2500
2648
|
open.addEventListener("click", () => {
|
|
2501
2649
|
navigate(`/dag/${encodeURIComponent(String(newRunId))}`);
|
|
2502
2650
|
});
|
|
2503
2651
|
row.appendChild(open);
|
|
2504
2652
|
} else {
|
|
2505
|
-
setStatus("
|
|
2653
|
+
setStatus("重启完成(暂时无法取得新运行编号,请在 DAG 历史中查看)。");
|
|
2506
2654
|
}
|
|
2507
2655
|
} else if (op) {
|
|
2508
2656
|
setStatus(
|
|
2509
|
-
|
|
2657
|
+
`新运行最终状态:${inspectorValueLabel("status", op.state)}${op.errorMessage ? ` — ${op.errorMessage}` : ""}`,
|
|
2510
2658
|
true,
|
|
2511
2659
|
);
|
|
2512
2660
|
} else {
|
|
@@ -2553,8 +2701,9 @@ function transparencyMetaList(rows) {
|
|
|
2553
2701
|
|
|
2554
2702
|
function renderNodeOverview(content, dagRunId, node) {
|
|
2555
2703
|
const root = el("div", "node-transparency-root");
|
|
2704
|
+
root.appendChild(buildInspectorPageLead("overview"));
|
|
2556
2705
|
root.appendChild(
|
|
2557
|
-
el("p", "node-input-semantics", "
|
|
2706
|
+
el("p", "node-input-semantics", "这里展示系统为当前运行保存的真实记录;字段已转换为易懂名称,悬停可查看原始技术字段。"),
|
|
2558
2707
|
);
|
|
2559
2708
|
const summary = el("section", "node-input-section");
|
|
2560
2709
|
summary.appendChild(el("h4", null, "节点事实"));
|
|
@@ -2572,12 +2721,12 @@ function renderNodeOverview(content, dagRunId, node) {
|
|
|
2572
2721
|
);
|
|
2573
2722
|
root.appendChild(summary);
|
|
2574
2723
|
const semantics = el("section", "node-input-section");
|
|
2575
|
-
semantics.appendChild(el("h4", null, "
|
|
2724
|
+
semantics.appendChild(el("h4", null, "这些信息分别在什么时候产生"));
|
|
2576
2725
|
semantics.appendChild(
|
|
2577
2726
|
el(
|
|
2578
2727
|
"p",
|
|
2579
2728
|
"muted",
|
|
2580
|
-
"
|
|
2729
|
+
"“执行设置”是运行开始前保存的配置;“实际上下文”是下发任务时真正交给节点的内容;“产出”是执行结束后保存的文件。缺少哪个阶段会明确显示,预览内容不会被当成已经发生的事实。",
|
|
2581
2730
|
),
|
|
2582
2731
|
);
|
|
2583
2732
|
root.appendChild(semantics);
|
|
@@ -2592,6 +2741,7 @@ function selectedAttemptNumber(dagRunId, node) {
|
|
|
2592
2741
|
}
|
|
2593
2742
|
|
|
2594
2743
|
async function renderEffectiveContext(content, dagRunId, node) {
|
|
2744
|
+
ensureInspectorPageLead(content, "context");
|
|
2595
2745
|
content.appendChild(el("p", "muted", "正在加载有效上下文事实…"));
|
|
2596
2746
|
const result = await fetchDagNodeContext(
|
|
2597
2747
|
dagRunId,
|
|
@@ -2606,6 +2756,7 @@ async function renderEffectiveContext(content, dagRunId, node) {
|
|
|
2606
2756
|
)
|
|
2607
2757
|
return;
|
|
2608
2758
|
clearNode(content);
|
|
2759
|
+
ensureInspectorPageLead(content, "context");
|
|
2609
2760
|
if (!result.ok || !result.body) {
|
|
2610
2761
|
const error = el(
|
|
2611
2762
|
"p",
|
|
@@ -2629,33 +2780,29 @@ async function renderEffectiveContext(content, dagRunId, node) {
|
|
|
2629
2780
|
);
|
|
2630
2781
|
if (receipt?.status !== "captured") {
|
|
2631
2782
|
const missing = el("div", "node-transparency-omission");
|
|
2783
|
+
const reasonCode = receipt?.reasonCode ?? "CONTEXT_RECEIPT_MISSING";
|
|
2784
|
+
const missingLabel = contextTransferReasonLabel(reasonCode);
|
|
2632
2785
|
missing.appendChild(
|
|
2633
2786
|
el(
|
|
2634
2787
|
"h4",
|
|
2635
2788
|
null,
|
|
2636
|
-
receipt?.status === "invalid" ? "
|
|
2789
|
+
receipt?.status === "invalid" ? "执行时记录不可用" : "没有执行时记录",
|
|
2637
2790
|
),
|
|
2638
2791
|
);
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
);
|
|
2642
|
-
const missingLabel = contextTransferReasonLabel(
|
|
2643
|
-
receipt?.reasonCode ?? "CONTEXT_RECEIPT_MISSING",
|
|
2644
|
-
);
|
|
2645
|
-
if (missingLabel) {
|
|
2646
|
-
missing.appendChild(el("p", "muted", missingLabel));
|
|
2647
|
-
}
|
|
2792
|
+
const reason = el("p", "muted", missingLabel ?? reasonCode);
|
|
2793
|
+
if (missingLabel) reason.setAttribute("title", `技术代码:${reasonCode}`);
|
|
2794
|
+
missing.appendChild(reason);
|
|
2648
2795
|
missing.appendChild(
|
|
2649
2796
|
el(
|
|
2650
2797
|
"p",
|
|
2651
2798
|
"muted",
|
|
2652
|
-
"
|
|
2799
|
+
"当前只有运行前设置和提示内容校验码,无法证明执行时真正收到了哪些内容。较早的运行不会事后补写这份记录。",
|
|
2653
2800
|
),
|
|
2654
2801
|
);
|
|
2655
2802
|
root.appendChild(missing);
|
|
2656
2803
|
} else {
|
|
2657
2804
|
const captured = el("section", "node-input-section");
|
|
2658
|
-
captured.appendChild(el("h4", null, "
|
|
2805
|
+
captured.appendChild(el("h4", null, "这次执行实际收到的内容"));
|
|
2659
2806
|
captured.appendChild(
|
|
2660
2807
|
transparencyMetaList([
|
|
2661
2808
|
["context digest", receipt.contextDigest],
|
|
@@ -2691,21 +2838,20 @@ async function renderEffectiveContext(content, dagRunId, node) {
|
|
|
2691
2838
|
|
|
2692
2839
|
function artifactInventorySection(data, dagRunId, nodeId) {
|
|
2693
2840
|
const root = el("section", "node-input-section node-artifact-inventory");
|
|
2694
|
-
root.appendChild(el("h4", null, "
|
|
2841
|
+
root.appendChild(el("h4", null, "本节点生成和使用的文件"));
|
|
2695
2842
|
if (!data?.binding?.available) {
|
|
2696
2843
|
const warning = el("div", "node-transparency-omission");
|
|
2697
2844
|
const bindingCode =
|
|
2698
2845
|
data?.binding?.reasonCode ?? "ARTIFACT_BINDING_UNAVAILABLE";
|
|
2699
|
-
warning.appendChild(el("code", null, bindingCode));
|
|
2700
2846
|
const bindingLabel = contextTransferReasonLabel(bindingCode);
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2847
|
+
const reason = el("p", "muted", bindingLabel ?? bindingCode);
|
|
2848
|
+
if (bindingLabel) reason.setAttribute("title", `技术代码:${bindingCode}`);
|
|
2849
|
+
warning.appendChild(reason);
|
|
2704
2850
|
warning.appendChild(
|
|
2705
2851
|
el(
|
|
2706
2852
|
"p",
|
|
2707
2853
|
"muted",
|
|
2708
|
-
"
|
|
2854
|
+
"这个较早的运行没有保存明确的文件使用关系;系统不会根据提示词、输出要求或文件名猜测谁会使用它。",
|
|
2709
2855
|
),
|
|
2710
2856
|
);
|
|
2711
2857
|
root.appendChild(warning);
|
|
@@ -2743,7 +2889,7 @@ function artifactInventorySection(data, dagRunId, nodeId) {
|
|
|
2743
2889
|
);
|
|
2744
2890
|
appendCodeList(
|
|
2745
2891
|
card,
|
|
2746
|
-
"
|
|
2892
|
+
"会直接使用此文件的节点",
|
|
2747
2893
|
artifact.directConsumerNodeIds ?? [],
|
|
2748
2894
|
);
|
|
2749
2895
|
if (artifact.materialization?.status === "materialized") {
|
|
@@ -2757,7 +2903,7 @@ function artifactInventorySection(data, dagRunId, nodeId) {
|
|
|
2757
2903
|
}
|
|
2758
2904
|
for (const artifact of data.consumes ?? []) {
|
|
2759
2905
|
const card = el("div", "node-artifact-card is-consumed");
|
|
2760
|
-
card.appendChild(el("h5", null,
|
|
2906
|
+
card.appendChild(el("h5", null, `使用 ${artifact.artifactId}`));
|
|
2761
2907
|
card.appendChild(
|
|
2762
2908
|
transparencyMetaList([
|
|
2763
2909
|
["producer", artifact.producerNodeId],
|
|
@@ -2776,7 +2922,7 @@ function artifactInventorySection(data, dagRunId, nodeId) {
|
|
|
2776
2922
|
(data.produces ?? []).length === 0 &&
|
|
2777
2923
|
(data.consumes ?? []).length === 0
|
|
2778
2924
|
) {
|
|
2779
|
-
root.appendChild(el("p", "empty", "
|
|
2925
|
+
root.appendChild(el("p", "empty", "该节点没有登记会生成或使用的文件。"));
|
|
2780
2926
|
}
|
|
2781
2927
|
return root;
|
|
2782
2928
|
}
|
|
@@ -2798,10 +2944,12 @@ async function prependArtifactInventory(content, dagRunId, node) {
|
|
|
2798
2944
|
"node-input-error node-artifact-inventory",
|
|
2799
2945
|
result.body?.error || "无法加载产物绑定事实。",
|
|
2800
2946
|
);
|
|
2801
|
-
content.
|
|
2947
|
+
const lead = content.querySelector(".inspector-page-lead");
|
|
2948
|
+
content.insertBefore(section, lead?.nextSibling ?? content.firstChild);
|
|
2802
2949
|
}
|
|
2803
2950
|
|
|
2804
2951
|
async function renderRunLineage(content, dagRunId, node) {
|
|
2952
|
+
ensureInspectorPageLead(content, "lineage");
|
|
2805
2953
|
content.appendChild(el("p", "muted", "正在加载来源与影响关系…"));
|
|
2806
2954
|
const result = await fetchDagRunLineage(dagRunId);
|
|
2807
2955
|
if (
|
|
@@ -2812,6 +2960,7 @@ async function renderRunLineage(content, dagRunId, node) {
|
|
|
2812
2960
|
)
|
|
2813
2961
|
return;
|
|
2814
2962
|
clearNode(content);
|
|
2963
|
+
ensureInspectorPageLead(content, "lineage");
|
|
2815
2964
|
if (!result.ok || !result.body) {
|
|
2816
2965
|
content.appendChild(
|
|
2817
2966
|
el("p", "node-input-error", result.body?.error || "无法加载来源与影响关系。"),
|
|
@@ -2820,21 +2969,11 @@ async function renderRunLineage(content, dagRunId, node) {
|
|
|
2820
2969
|
}
|
|
2821
2970
|
const data = result.body;
|
|
2822
2971
|
const root = el("div", "node-transparency-root");
|
|
2823
|
-
const intro = el("section", "node-lineage-intro");
|
|
2824
|
-
intro.append(
|
|
2825
|
-
el("h4", null, "这个节点从哪里来,会影响哪里"),
|
|
2826
|
-
el(
|
|
2827
|
-
"p",
|
|
2828
|
-
"muted",
|
|
2829
|
-
"这里展示派生运行继承、替换或重新执行了哪些节点。产出文件的直接使用者请到“产出”中查看。",
|
|
2830
|
-
),
|
|
2831
|
-
);
|
|
2832
|
-
root.appendChild(intro);
|
|
2833
2972
|
root.appendChild(
|
|
2834
2973
|
transparencyMetaList([
|
|
2835
2974
|
["运行类型", data.kind === "root" ? "首次运行" : "派生运行"],
|
|
2836
2975
|
["来源运行", data.parentRunId ?? null],
|
|
2837
|
-
["发起方式", data.trigger
|
|
2976
|
+
["发起方式", inspectorValueLabel("发起方式", data.trigger)],
|
|
2838
2977
|
["人工选择节点", data.operatorSelectedNodeId ?? null],
|
|
2839
2978
|
["从此节点重新执行", data.effectiveFromNodeId ?? null],
|
|
2840
2979
|
["重跑计划哈希", data.planHash ?? null],
|
|
@@ -2919,6 +3058,11 @@ function restoreInspectorContentScroll(content, dagRunId, nodeId, tab) {
|
|
|
2919
3058
|
) {
|
|
2920
3059
|
return;
|
|
2921
3060
|
}
|
|
3061
|
+
if (tab === "timeline" && uiState.dagTimelineAutoScrollEnabled !== false) {
|
|
3062
|
+
content.scrollLeft = uiState.dagTimelineViewportState?.scrollLeft ?? 0;
|
|
3063
|
+
content.scrollTop = content.scrollHeight;
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
2922
3066
|
if (tab === "output") {
|
|
2923
3067
|
const saved = uiState.dagNodeOutputViewportState;
|
|
2924
3068
|
if (saved?.regionKey === regionKey && saved.followLatest) {
|
|
@@ -3242,7 +3386,7 @@ function renderCachedOrPreviewBody(content, dagRunId, node, key, isLatest) {
|
|
|
3242
3386
|
}
|
|
3243
3387
|
if (cached && cached.ok === false) {
|
|
3244
3388
|
const message =
|
|
3245
|
-
cached.error || "
|
|
3389
|
+
cached.error || "该次执行输出暂不可用(记录文件缺失或无法读取)。";
|
|
3246
3390
|
const error = el("p", "empty", message);
|
|
3247
3391
|
error.setAttribute("role", "status");
|
|
3248
3392
|
content.appendChild(error);
|
|
@@ -3286,7 +3430,7 @@ async function loadAndRenderExecutionOutput(
|
|
|
3286
3430
|
(result.status === 0
|
|
3287
3431
|
? "加载执行输出失败(网络错误)。"
|
|
3288
3432
|
: result.status === 404
|
|
3289
|
-
? "
|
|
3433
|
+
? "该次执行输出暂不可用(记录文件缺失或无法读取)。"
|
|
3290
3434
|
: `加载执行输出失败(HTTP ${result.status})。`);
|
|
3291
3435
|
// Cache the miss for this key only — never wipe other valid bodies.
|
|
3292
3436
|
setDagNodeExecutionOutputCache(dagRunId, nodeId, key, {
|
|
@@ -3324,7 +3468,8 @@ function repaintOutputTabBody(content, dagRunId, node) {
|
|
|
3324
3468
|
// Remove everything after the attempt bar (or all if no bar).
|
|
3325
3469
|
const inventory = content.querySelector(".node-artifact-inventory");
|
|
3326
3470
|
const bar = content.querySelector(".dag-inspector-attempt-bar");
|
|
3327
|
-
const
|
|
3471
|
+
const lead = content.querySelector(".inspector-page-lead");
|
|
3472
|
+
const keep = [lead, inventory, bar].filter(Boolean);
|
|
3328
3473
|
while (content.firstChild) content.removeChild(content.firstChild);
|
|
3329
3474
|
for (const nodeEl of keep) content.appendChild(nodeEl);
|
|
3330
3475
|
|
|
@@ -3390,7 +3535,7 @@ function renderProcessCommandCard(command) {
|
|
|
3390
3535
|
details.appendChild(summary);
|
|
3391
3536
|
const body = el("div", "process-timeline-card-body");
|
|
3392
3537
|
const meta = [
|
|
3393
|
-
command.exitCode != null ?
|
|
3538
|
+
command.exitCode != null ? `退出码 ${command.exitCode}` : null,
|
|
3394
3539
|
command.durationMs != null ? `耗时 ${formatMs(command.durationMs)}` : null,
|
|
3395
3540
|
command.failureCategory ? command.failureCategory : null,
|
|
3396
3541
|
].filter(Boolean);
|
|
@@ -3400,11 +3545,11 @@ function renderProcessCommandCard(command) {
|
|
|
3400
3545
|
);
|
|
3401
3546
|
}
|
|
3402
3547
|
if (command.stdout) {
|
|
3403
|
-
body.appendChild(el("div", "process-timeline-section-label", "
|
|
3548
|
+
body.appendChild(el("div", "process-timeline-section-label", "正常输出"));
|
|
3404
3549
|
appendProcessPre(body, command.stdout);
|
|
3405
3550
|
}
|
|
3406
3551
|
if (command.stderr) {
|
|
3407
|
-
body.appendChild(el("div", "process-timeline-section-label", "
|
|
3552
|
+
body.appendChild(el("div", "process-timeline-section-label", "错误输出"));
|
|
3408
3553
|
appendProcessPre(body, command.stderr);
|
|
3409
3554
|
}
|
|
3410
3555
|
if (command.truncated) {
|
|
@@ -3615,7 +3760,7 @@ function renderExecutorAwareTimeline(content, dagRunId, node) {
|
|
|
3615
3760
|
});
|
|
3616
3761
|
}
|
|
3617
3762
|
|
|
3618
|
-
function fillDagInspectorContent(content, dagRunId, node) {
|
|
3763
|
+
function fillDagInspectorContent(content, dagRunId, node, run) {
|
|
3619
3764
|
const previousRenderedNode = content.dataset.dagNodeId ?? "";
|
|
3620
3765
|
const previousRenderedRun = content.dataset.dagRunId ?? "";
|
|
3621
3766
|
const previousTab = content.dataset.inspectorTab ?? "";
|
|
@@ -3653,6 +3798,9 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
3653
3798
|
content.dataset.inspectorTab = activeTab;
|
|
3654
3799
|
// Drop timeline-only classes when switching tabs so output layout stays clean.
|
|
3655
3800
|
content.className = "dag-inspector-content";
|
|
3801
|
+
if (activeTab !== "prompt-restart") {
|
|
3802
|
+
content.classList.add("is-readable-page");
|
|
3803
|
+
}
|
|
3656
3804
|
ensureInspectorContentScrollCapture(
|
|
3657
3805
|
content,
|
|
3658
3806
|
dagRunId,
|
|
@@ -3666,17 +3814,23 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
3666
3814
|
} else if (activeTab === "lineage") {
|
|
3667
3815
|
void renderRunLineage(content, dagRunId, node);
|
|
3668
3816
|
} else if (activeTab === "spec-evidence") {
|
|
3817
|
+
ensureInspectorPageLead(content, "spec-evidence");
|
|
3669
3818
|
void renderSpecEvidence(content, dagRunId, node.nodeId);
|
|
3670
3819
|
} else if (activeTab === "prompt-restart") {
|
|
3671
|
-
void renderPromptRestart(content, dagRunId, node);
|
|
3820
|
+
void renderPromptRestart(content, dagRunId, node, run);
|
|
3672
3821
|
} else if (activeTab === "timeline") {
|
|
3822
|
+
ensureInspectorPageLead(content, "timeline");
|
|
3673
3823
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
3674
3824
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
3675
3825
|
renderExecutorAwareTimeline(content, dagRunId, node);
|
|
3826
|
+
// Initial content is rendered before it is attached. Restore again on the
|
|
3827
|
+
// connected animation frame so live-follow sees the final scroll height.
|
|
3828
|
+
restoreInspectorContentScroll(content, dagRunId, node.nodeId, "timeline");
|
|
3676
3829
|
} else if (activeTab === "input") {
|
|
3677
3830
|
void renderDagNodeInput(content, dagRunId, node);
|
|
3678
3831
|
} else {
|
|
3679
3832
|
// output tab: attempt bar + latest preview immediate / historical async body.
|
|
3833
|
+
ensureInspectorPageLead(content, "output");
|
|
3680
3834
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
3681
3835
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
3682
3836
|
const key = effectiveExecutionKey(dagRunId, node);
|
|
@@ -3877,12 +4031,13 @@ function dagInspectorSignature(dagRunId, node) {
|
|
|
3877
4031
|
]);
|
|
3878
4032
|
}
|
|
3879
4033
|
|
|
3880
|
-
export function renderDagInspector(dagRunId, node, initial = true) {
|
|
4034
|
+
export function renderDagInspector(dagRunId, node, initial = true, run = null) {
|
|
3881
4035
|
// Record the current selection before any early-return so tab handlers
|
|
3882
4036
|
// (which reference the mutable inspectorSelection slot) always re-render
|
|
3883
4037
|
// the currently selected node, never a stale build-time closure.
|
|
3884
4038
|
inspectorSelection.dagRunId = dagRunId;
|
|
3885
4039
|
inspectorSelection.node = node;
|
|
4040
|
+
inspectorSelection.run = run;
|
|
3886
4041
|
const panel = document.getElementById("dag-inspector");
|
|
3887
4042
|
if (!panel) return;
|
|
3888
4043
|
captureDagTimelineViewportState(panel);
|
|
@@ -3932,7 +4087,7 @@ export function renderDagInspector(dagRunId, node, initial = true) {
|
|
|
3932
4087
|
scrollTop: pre.scrollTop,
|
|
3933
4088
|
}));
|
|
3934
4089
|
clearNode(existingContent);
|
|
3935
|
-
fillDagInspectorContent(existingContent, dagRunId, node);
|
|
4090
|
+
fillDagInspectorContent(existingContent, dagRunId, node, run);
|
|
3936
4091
|
// Async tabs restore themselves after fetch; sync tabs restore here.
|
|
3937
4092
|
if (activeTab === "output" || activeTab === "timeline") {
|
|
3938
4093
|
restoreInspectorContentScroll(
|
|
@@ -3963,7 +4118,7 @@ export function renderDagInspector(dagRunId, node, initial = true) {
|
|
|
3963
4118
|
panel.appendChild(buildDagInspectorHeader(dagRunId, node));
|
|
3964
4119
|
|
|
3965
4120
|
const content = el("div", "dag-inspector-content");
|
|
3966
|
-
fillDagInspectorContent(content, dagRunId, node);
|
|
4121
|
+
fillDagInspectorContent(content, dagRunId, node, run);
|
|
3967
4122
|
panel.appendChild(content);
|
|
3968
4123
|
panel.dataset.contentSignature = nextContentSignature;
|
|
3969
4124
|
}
|