@tea-agent/loop-agent 0.39.0-next.9 → 0.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -1
- package/CHANGELOG.md +269 -69
- package/README.md +11 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/dag/generate-task-dag.js +9 -0
- package/dist/application/task-lifecycle/advance.js +16 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli/update/runtime-activity.js +1 -29
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +124 -18
- package/dist/executors/pi-event-serializer.js +42 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +555 -34
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-failure-category.js +138 -0
- package/dist/shared/dag-prompt-override.js +27 -0
- package/dist/shared/operator/capabilities.js +181 -27
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +70 -15
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +28 -0
- package/dist/task/contract/apply.js +11 -1
- package/dist/task/contract/constants.js +3 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/hash.js +3 -0
- package/dist/task/contract/observe.js +25 -4
- package/dist/task/contract/paths.js +2 -1
- package/dist/task/contract/project.js +20 -1
- package/dist/task/contract/schema.js +8 -1
- package/dist/task/contract/transaction.js +12 -0
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/completeness.js +188 -0
- package/dist/task/source-prepare/fragment-inventory.js +474 -0
- package/dist/task/source-prepare/index.js +3 -0
- package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
- package/dist/task/source-prepare/ledger-review.js +214 -0
- package/dist/task/source-prepare/ledger.js +545 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/task/source-prepare/prepare.js +262 -1
- package/dist/task/source-prepare/semantic-intake.js +19 -3
- package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
- package/dist/task/source-prepare/types.js +22 -0
- package/dist/worker/cli.js +18 -2
- package/dist/worker/console/chat/deferred-turn.js +91 -0
- package/dist/worker/console/chat/mcp-inventory.js +221 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +165 -83
- package/dist/worker/console/chat/repo-walk.js +10 -1
- package/dist/worker/console/chat/routes.js +480 -55
- package/dist/worker/console/chat/semantic-activity.js +20 -8
- package/dist/worker/console/chat/session-store.js +146 -2
- package/dist/worker/console/chat/turn-process.js +10 -10
- package/dist/worker/console/console-handoff.js +82 -0
- package/dist/worker/console/console-update-and-init.js +115 -0
- package/dist/worker/console/console-update-runtime.js +84 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/draft-store.js +49 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operation-store.js +1 -1
- package/dist/worker/console/operator-actions.js +320 -4
- package/dist/worker/console/operator-surface-health.js +1 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +5 -4
- package/dist/worker/console/prd-intake-bridge.js +13 -0
- package/dist/worker/console/recovery-cta.js +4 -4
- package/dist/worker/console/recovery-error-copy.js +4 -4
- package/dist/worker/console/routes.js +46 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +111 -7
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BLXFe4qI.js → abnfDiagram-N423BO3Z-CTx1TrNa.js} +1 -1
- package/dist/worker/console/static/assets/{arc-7iOJeqho.js → arc-B4i9cVxX.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CYoyj5K7.js → architectureDiagram-T3A2C74G-BAXE8_Rl.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-fK01JNlR.js → blockDiagram-VBNYF7ZC-C5Xh59at.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-bzbw5D_Z.js → c4Diagram-5PPSVZJV-B_asOzAu.js} +1 -1
- package/dist/worker/console/static/assets/channel-Dj_4Moei.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Cf6hJFFG.js → chunk-2GRJ4B5K-wRO_iRNc.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DSFXR-et.js → chunk-2Q5K7J3B-ByOaSh_G.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BagJechl.js → chunk-5RXB4S5H-DjLzudan.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-E7QNMFaW.js → chunk-5VM5RSS4-JCNGkXYT.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BG_eOBrC.js → chunk-6Q2QTUOP-BhrPXXMZ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bo42dqol.js → chunk-GF5L2VYU-TJ5JuEBT.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-dibM-87v.js → chunk-JWPE2WC7-VVthn_mD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bytrp3Oe.js → chunk-KBJHAD2P-D60-Mny4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DBABILrr.js → chunk-RYQCIY6F-zXv7aVye.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DCUk5Pf9.js → chunk-XXDRQBXY-By9VQoi1.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C0YYAyCa.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C0YYAyCa.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BlGE9v6E.js → cose-bilkent-JH36ORCC-C4zQLe-M.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DGl5ewph.js → cynefin-VYW2F7L2-wh5N2ReN.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DsJ_SVH_.js → cynefinDiagram-MW4NZA55-Dlu5wIqs.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BIhjFdzv.js → dagre-VZM6K2ZE-CrDgthc6.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dmy1ctTb.js → diagram-7IWD3JNH-zN1clg27.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYCtWUF3.js → diagram-B4RE2ZJO-BN-o7J7-.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BMZFHVd2.js → diagram-LBJQPF4R-C8SuPt0d.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-b_U8gfaO.js → diagram-Q27KOJAE-BoizuHbZ.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dzy_bn-I.js → diagram-UB23O5K3-BzUzXixG.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DJCiMNEI.js → ebnfDiagram-BXEA7PRR-Ev3lA4Cf.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-28lD016B.js → erDiagram-JOGREHBK-GALUzRbs.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DGyHJmIw.js → flowDiagram-UKHOOZJN-CZJN65Sb.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CzdwUjrO.js → ganttDiagram-PKOTCBZU-JLB9OESl.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D9bGwuo8.js → gitGraphDiagram-DS77QQ5N-CcvCr4GD.js} +1 -1
- package/dist/worker/console/static/assets/index-CW88Fjbw.js +406 -0
- package/dist/worker/console/static/assets/index-DP0ZJl39.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-LJgJUQFC.js → infoDiagram-6WML65LV-HG5z7aVg.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-98LaB8zb.js → ishikawaDiagram-WSZJBQD7-BHEu9chR.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-D-ISzwlQ.js → journeyDiagram-NVQOT4AX-Cp_Up8-a.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-gfYOI-EL.js → kanban-definition-27J2QSJJ-DvVeY-em.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CyntZgKV.js → linear-aGmcjgTn.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BBOR7dg0.js → mermaid.core-Biyo-tqA.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CQAgwPMk.js → mindmap-definition-FAOFIHXS-DyxY2pIb.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DkMiZFuV.js → pegDiagram-VL7TDLO6-CkdGuc50.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DLNXa_0J.js → pieDiagram-7S7Q4E2Y-NNQI9xVR.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js → quadrantDiagram-CIZ2JOQS-PAJkuz_G.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BxMB0QqW.js → railroadDiagram-AXF67PYL-ZwrQod0O.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-FjumoKn4.js → requirementDiagram-LRYGKXZP-D3ZfrzM2.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CkhKc_E3.js → sankeyDiagram-W5VNT64P-DDGFmWk5.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DoBWqTTH.js → sequenceDiagram-SI44F4Z6-DO6mmXTA.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CQeh_YeE.js → sizeCapture-X5ZJPWSS-DMbCQiJt.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-LuHmFW-I.js → stateDiagram-OKZ733FA-Di_YUEfC.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrdDvela.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CHwbDctY.js → swimlanes-SLNWSIFB-Bn2PphNJ.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DPkEAULl.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CoMngk0X.js → timeline-definition-Z64GVDOM-DPh8pRsp.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CURYbtbO.js → vennDiagram-T6HMQDX7-GMfl26pW.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BdqSECjU.js → wardleyDiagram-T6FBY63Y-DTlmVrmF.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DbMUYuAg.js → xychartDiagram-ELKLHX3M-YGBjKAS5.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +40 -1
- package/dist/worker/console/static-src/app/useOperatorActions.js +6 -1
- package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +38 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +46 -0
- package/dist/worker/console/static-src/app/useTaskWizard.js +73 -1
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +74 -26
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
- package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +54 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +237 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +50 -22
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +26 -1
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/init-runtime-activity.js +26 -0
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +160 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +224 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +67 -1
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +60 -0
- package/dist/worker/observe/static/index.html +5 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +85 -0
- package/dist/worker/observe/static/views/dag-inspector.js +912 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/worker/observe/static/views/task.js +55 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +856 -54
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
- package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
- package/dist/workflows/dag/backend-test-intake-context.js +32 -0
- package/dist/workflows/dag/backend-test-layout.js +133 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +343 -33
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +454 -36
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
- package/dist/workflows/dag/backend-test-scenario-partitions.js +321 -0
- package/dist/workflows/dag/backend-test-writer-completeness.js +110 -61
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +3 -1
- package/dist/workflows/dag/convergence/controller.js +141 -0
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +25 -9
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-implementation-contract.js +465 -6
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +293 -84
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-review-context.js +92 -10
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +1019 -209
- package/dist/workflows/dag/node-execution.js +228 -15
- package/dist/workflows/dag/prompt.js +60 -1
- package/dist/workflows/dag/rerun-feedback.js +212 -0
- package/dist/workflows/dag/rerun-plan.js +224 -27
- package/dist/workflows/dag/rerun-run.js +61 -6
- package/dist/workflows/dag/rerun-task.js +61 -2
- package/dist/workflows/dag/retry-policy.js +109 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +185 -12
- package/dist/workflows/dag/validate.js +64 -20
- package/docs/README.md +1 -0
- package/docs/architecture/evolution.md +45 -0
- package/docs/init-surface.manifest.json +6 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +1 -5
- package/docs/skills/vetted-skill-registry.md +14 -0
- package/docs/templates/README.md +2 -2
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/backend-test-dag.json +60 -35
- package/docs/templates/frontend-implementation-contract.schema.json +681 -24
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +6 -4
- package/package.json +12 -4
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/analyze-product-requirements/SKILL.md +8 -5
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
- package/skills/analyze-product-requirements/references/example.md +50 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
- package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/references/node-contracts.md +2 -2
- package/skills/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/improve-codebase-architecture/deepening.md +37 -0
- package/skills/improve-codebase-architecture/html-report.md +123 -0
- package/skills/improve-codebase-architecture/interface-design.md +44 -0
- package/skills/improve-codebase-architecture/language.md +53 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/loop-agent/references/hybrid-dag.md +7 -0
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-BBOTJHvD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/index-B-qWGThd.js +0 -325
- package/dist/worker/console/static/assets/index-C-xRgY_0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DZihfdKc.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CKnXCjHX.js +0 -8
|
@@ -11,14 +11,24 @@ import {
|
|
|
11
11
|
} from "../constants.js";
|
|
12
12
|
import { clearNode, el } from "../dom.js";
|
|
13
13
|
import { attemptFailureLabel } from "../dag-history-labels.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import {
|
|
15
|
+
fetchJson,
|
|
16
|
+
fetchSpecEvidenceFile,
|
|
17
|
+
fetchDagNodeInput,
|
|
18
|
+
fetchDagNodeProcess,
|
|
19
|
+
postOperatorAction,
|
|
20
|
+
fetchOperatorOperation,
|
|
21
|
+
} from "../api.js";
|
|
22
|
+
import { badge, formatLedgerSummary, formatMs, prettyJsonBlock } from "../format.js";
|
|
23
|
+
import { renderMarkdown, renderInspectorDocument } from "../markdown-render.js";
|
|
24
|
+
import { buildPromptRestartCandidateOptions } from "../prompt-restart-candidates.js";
|
|
25
|
+
import { copyText } from "../copy.js";
|
|
17
26
|
import {
|
|
18
27
|
uiState,
|
|
19
28
|
makeSessionEventIdentity,
|
|
20
29
|
makeDagNodeInputIdentity,
|
|
21
30
|
makeDagNodeExecutionOutputIdentity,
|
|
31
|
+
makeDagNodeProcessIdentity,
|
|
22
32
|
getDagNodeExecutionOutputCache,
|
|
23
33
|
setDagNodeExecutionOutputCache,
|
|
24
34
|
clearDagNodeExecutionOutputCache,
|
|
@@ -44,6 +54,85 @@ import { createCustomSelect } from "../custom-select.js";
|
|
|
44
54
|
// reference, always resolving the currently selected node identity.
|
|
45
55
|
const inspectorSelection = { dagRunId: null, node: null };
|
|
46
56
|
|
|
57
|
+
function createPromptRestartCopyButton(readCandidate) {
|
|
58
|
+
const buildIcon = (kind) => {
|
|
59
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
60
|
+
svg.setAttribute(
|
|
61
|
+
"class",
|
|
62
|
+
`dags-copy-icon dags-copy-icon-${kind === "check" ? "check" : "rects"}`,
|
|
63
|
+
);
|
|
64
|
+
svg.setAttribute("viewBox", "0 0 16 16");
|
|
65
|
+
svg.setAttribute("width", "16");
|
|
66
|
+
svg.setAttribute("height", "16");
|
|
67
|
+
svg.setAttribute("aria-hidden", "true");
|
|
68
|
+
svg.setAttribute("focusable", "false");
|
|
69
|
+
if (kind === "check") {
|
|
70
|
+
const check = document.createElementNS(
|
|
71
|
+
"http://www.w3.org/2000/svg",
|
|
72
|
+
"path",
|
|
73
|
+
);
|
|
74
|
+
check.setAttribute("d", "M3.5 8.5 6.5 11.5 12.5 4.5");
|
|
75
|
+
check.setAttribute("fill", "none");
|
|
76
|
+
check.setAttribute("stroke", "currentColor");
|
|
77
|
+
check.setAttribute("stroke-width", "1.8");
|
|
78
|
+
check.setAttribute("stroke-linecap", "round");
|
|
79
|
+
check.setAttribute("stroke-linejoin", "round");
|
|
80
|
+
svg.appendChild(check);
|
|
81
|
+
return svg;
|
|
82
|
+
}
|
|
83
|
+
for (const [x, y] of [
|
|
84
|
+
[2, 2],
|
|
85
|
+
[5, 5],
|
|
86
|
+
]) {
|
|
87
|
+
const rect = document.createElementNS(
|
|
88
|
+
"http://www.w3.org/2000/svg",
|
|
89
|
+
"rect",
|
|
90
|
+
);
|
|
91
|
+
rect.setAttribute("x", String(x));
|
|
92
|
+
rect.setAttribute("y", String(y));
|
|
93
|
+
rect.setAttribute("width", "9");
|
|
94
|
+
rect.setAttribute("height", "9");
|
|
95
|
+
rect.setAttribute("rx", "1.5");
|
|
96
|
+
rect.setAttribute("fill", "none");
|
|
97
|
+
rect.setAttribute("stroke", "currentColor");
|
|
98
|
+
rect.setAttribute("stroke-width", "1.5");
|
|
99
|
+
svg.appendChild(rect);
|
|
100
|
+
}
|
|
101
|
+
return svg;
|
|
102
|
+
};
|
|
103
|
+
const button = el(
|
|
104
|
+
"button",
|
|
105
|
+
"dags-copy-btn dags-copy-btn-icon prompt-restart-copy-icon",
|
|
106
|
+
);
|
|
107
|
+
button.type = "button";
|
|
108
|
+
button.setAttribute("data-testid", "prompt-restart-copy");
|
|
109
|
+
button.setAttribute("aria-label", "复制当前提示词候选");
|
|
110
|
+
button.setAttribute("title", "复制当前提示词候选");
|
|
111
|
+
button.appendChild(buildIcon("copy"));
|
|
112
|
+
button.appendChild(buildIcon("check"));
|
|
113
|
+
button.addEventListener("click", (event) => {
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
event.stopPropagation();
|
|
116
|
+
const candidate = readCandidate();
|
|
117
|
+
void copyText(candidate?.prompt ?? "").then((ok) => {
|
|
118
|
+
const feedback = ok
|
|
119
|
+
? candidate?.truncated
|
|
120
|
+
? "已复制截断预览"
|
|
121
|
+
: "已复制"
|
|
122
|
+
: "复制失败";
|
|
123
|
+
button.classList.toggle("is-copied", ok);
|
|
124
|
+
button.setAttribute("aria-label", feedback);
|
|
125
|
+
button.setAttribute("title", feedback);
|
|
126
|
+
setTimeout(() => {
|
|
127
|
+
button.classList.remove("is-copied");
|
|
128
|
+
button.setAttribute("aria-label", "复制当前提示词候选");
|
|
129
|
+
button.setAttribute("title", "复制当前提示词候选");
|
|
130
|
+
}, 1500);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
return button;
|
|
134
|
+
}
|
|
135
|
+
|
|
47
136
|
function renderDagInspectorForCurrent() {
|
|
48
137
|
if (!inspectorSelection.dagRunId || !inspectorSelection.node) return;
|
|
49
138
|
renderDagInspector(
|
|
@@ -68,6 +157,10 @@ function resetInspectorNodeSelection(dagRunId, nodeId) {
|
|
|
68
157
|
uiState.dagNodeInputData = null;
|
|
69
158
|
uiState.dagNodeInputError = null;
|
|
70
159
|
uiState.dagNodeInputLoading = false;
|
|
160
|
+
uiState.dagNodeProcessData = null;
|
|
161
|
+
uiState.dagNodeProcessError = null;
|
|
162
|
+
uiState.dagNodeProcessLoading = false;
|
|
163
|
+
uiState.dagNodeProcessIdentity = null;
|
|
71
164
|
// Drop execution-output cache/identity so node A's bodies never leak into B.
|
|
72
165
|
clearDagNodeExecutionOutputCache();
|
|
73
166
|
// Bump the session-event identity token so any in-flight response for the
|
|
@@ -725,6 +818,11 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
725
818
|
}
|
|
726
819
|
section.appendChild(dl);
|
|
727
820
|
appendCodeList(section, "skills", data.executorInput.skills);
|
|
821
|
+
appendCodeList(
|
|
822
|
+
section,
|
|
823
|
+
"piExtensions(声明)",
|
|
824
|
+
data.executorInput.piExtensions,
|
|
825
|
+
);
|
|
728
826
|
if (data.executorInput.shell) {
|
|
729
827
|
const shell = data.executorInput.shell;
|
|
730
828
|
const shellDl = el("dl", "node-input-summary");
|
|
@@ -743,6 +841,12 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
743
841
|
(shell.gates ?? []).join(", ") || "(none)",
|
|
744
842
|
null,
|
|
745
843
|
);
|
|
844
|
+
appendMetaRow(
|
|
845
|
+
shellDl,
|
|
846
|
+
"handlers",
|
|
847
|
+
(shell.handlers ?? []).join(", ") || "(none)",
|
|
848
|
+
null,
|
|
849
|
+
);
|
|
746
850
|
section.appendChild(shellDl);
|
|
747
851
|
if ((shell.commands ?? []).length > 0) {
|
|
748
852
|
const list = el("ol", "node-input-command-list");
|
|
@@ -799,6 +903,52 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
799
903
|
wrap.appendChild(section);
|
|
800
904
|
}
|
|
801
905
|
|
|
906
|
+
if (data.piExtensionRunFacts) {
|
|
907
|
+
const facts = data.piExtensionRunFacts;
|
|
908
|
+
const section = el("section", "node-input-section");
|
|
909
|
+
section.appendChild(el("h4", null, "Pi 扩展运行事实"));
|
|
910
|
+
if (!facts.exists) {
|
|
911
|
+
section.appendChild(
|
|
912
|
+
el(
|
|
913
|
+
"p",
|
|
914
|
+
"muted",
|
|
915
|
+
facts.parseError
|
|
916
|
+
? `产物 ${facts.artifact} 读取失败(${facts.parseError})。`
|
|
917
|
+
: `产物 ${facts.artifact} 尚未生成(节点未执行或缺失)。`,
|
|
918
|
+
),
|
|
919
|
+
);
|
|
920
|
+
} else {
|
|
921
|
+
const dl = el("dl", "node-input-summary");
|
|
922
|
+
appendMetaRow(
|
|
923
|
+
dl,
|
|
924
|
+
"resolved",
|
|
925
|
+
facts.resolved
|
|
926
|
+
.map((entry) => `${entry.id} → ${entry.packageName}@${entry.version}`)
|
|
927
|
+
.join(", ") || "(none)",
|
|
928
|
+
null,
|
|
929
|
+
);
|
|
930
|
+
section.appendChild(dl);
|
|
931
|
+
appendCodeList(
|
|
932
|
+
section,
|
|
933
|
+
"requested",
|
|
934
|
+
facts.requested,
|
|
935
|
+
);
|
|
936
|
+
for (const missingEntry of facts.missing ?? []) {
|
|
937
|
+
section.appendChild(
|
|
938
|
+
el(
|
|
939
|
+
"p",
|
|
940
|
+
"node-input-warning",
|
|
941
|
+
`降级:${missingEntry.id} 未加载(${missingEntry.reason || "unknown"})`,
|
|
942
|
+
),
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
if ((facts.missing ?? []).length === 0) {
|
|
946
|
+
section.appendChild(el("p", "muted", "全部声明扩展均已解析加载。"));
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
wrap.appendChild(section);
|
|
950
|
+
}
|
|
951
|
+
|
|
802
952
|
if (data.promptFingerprint?.exists) {
|
|
803
953
|
const section = el("section", "node-input-section");
|
|
804
954
|
section.appendChild(el("h4", null, "Assembled prompt 指纹"));
|
|
@@ -914,10 +1064,526 @@ async function renderDagNodeInput(content, dagRunId, node) {
|
|
|
914
1064
|
|
|
915
1065
|
renderDagNodeInputDetail(content, data);
|
|
916
1066
|
finishInputRender();
|
|
1067
|
+
// Frontend prewrite 门禁 / source fidelity ledger 展示(AC-009):只读安全摘要,
|
|
1068
|
+
// 从 run 产物(frontend-prewrite-result.json)与 /api/tasks/:id/ledger 拉取;
|
|
1069
|
+
// 接口失败进入安全降级,不泄漏敏感内容。
|
|
1070
|
+
if (/prewrite/.test(nodeId)) {
|
|
1071
|
+
void renderFrontendGateLedger(content, dagRunId);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Frontend prewrite 门禁 + ledger 摘要行(AC-BLOCK-005):纯函数,测试直接断言。
|
|
1077
|
+
* prewriteResult = {ok:true, body}|{ok:false, status}|null(网络失败);ledgerBody 为
|
|
1078
|
+
* /api/tasks/:id/ledger 响应或 null。绝不把 envelope/预览原文塞入 <pre>(删除
|
|
1079
|
+
* 旧 lines.push(previewText) 路径),缺失/拒绝显示明确 fail-closed 文案。
|
|
1080
|
+
*/
|
|
1081
|
+
export function frontendGateLedgerLines(input) {
|
|
1082
|
+
const { prewriteResult, ledgerBody, taskId } = input;
|
|
1083
|
+
const lines = [];
|
|
1084
|
+
if (prewriteResult?.ok && prewriteResult.body) {
|
|
1085
|
+
const parsed = prewriteResult.body;
|
|
1086
|
+
if (parsed?.classification) {
|
|
1087
|
+
lines.push(`classification: ${String(parsed.classification)}`);
|
|
1088
|
+
if (parsed.failureCode) {
|
|
1089
|
+
lines.push(`failureCode: ${String(parsed.failureCode)}`);
|
|
1090
|
+
}
|
|
1091
|
+
if (parsed.failureReason) {
|
|
1092
|
+
lines.push(`failureReason: ${String(parsed.failureReason)}`);
|
|
1093
|
+
}
|
|
1094
|
+
} else {
|
|
1095
|
+
lines.push("frontend-prewrite-result.json 内容无法解析为结构化结果。");
|
|
1096
|
+
}
|
|
1097
|
+
} else if (prewriteResult && prewriteResult.status === 404) {
|
|
1098
|
+
lines.push("frontend-prewrite-result.json 尚未生成(门禁未执行或缺失)。");
|
|
1099
|
+
} else if (prewriteResult) {
|
|
1100
|
+
lines.push(
|
|
1101
|
+
"frontend-prewrite-result.json 读取失败(只读接口拒绝/服务异常,安全降级)。",
|
|
1102
|
+
);
|
|
1103
|
+
} else {
|
|
1104
|
+
lines.push("frontend-prewrite-result.json 尚未生成(门禁未执行或缺失)。");
|
|
1105
|
+
}
|
|
1106
|
+
if (ledgerBody && ledgerBody.present === true) {
|
|
1107
|
+
lines.push(formatLedgerSummary(ledgerBody));
|
|
1108
|
+
} else if (ledgerBody) {
|
|
1109
|
+
lines.push("ledger: 无(任务无引用或 ledger 缺失,不伪造计数)");
|
|
1110
|
+
} else if (taskId) {
|
|
1111
|
+
lines.push("ledger: 暂不可查(只读接口失败,安全降级)");
|
|
1112
|
+
}
|
|
1113
|
+
return lines;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/** DAG run 专属 prewrite-result 只读拉取(内联 fetch,不新增 api.js 依赖)。 */
|
|
1117
|
+
async function fetchPrewriteResultJson(dagRunId) {
|
|
1118
|
+
try {
|
|
1119
|
+
const res = await fetch(
|
|
1120
|
+
`/api/dag-runs/${encodeURIComponent(dagRunId)}/prewrite-result`,
|
|
1121
|
+
{ credentials: "same-origin" },
|
|
1122
|
+
);
|
|
1123
|
+
if (!res.ok) return { ok: false, status: res.status };
|
|
1124
|
+
return { ok: true, body: await res.json() };
|
|
1125
|
+
} catch {
|
|
1126
|
+
return null; // 网络失败
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* Resolve the DAG-owned task identity, prewrite result, and safe ledger summary.
|
|
1132
|
+
* Exported so the real browser request sequence can be regression-tested without
|
|
1133
|
+
* relying on source-string assertions.
|
|
1134
|
+
*/
|
|
1135
|
+
export async function fetchFrontendGateLedgerData(dagRunId) {
|
|
1136
|
+
const run = await fetchJson(
|
|
1137
|
+
`/api/dag-runs/${encodeURIComponent(dagRunId)}`,
|
|
1138
|
+
);
|
|
1139
|
+
const taskId = typeof run?.taskId === "string" ? run.taskId : "";
|
|
1140
|
+
const prewriteResult = await fetchPrewriteResultJson(dagRunId);
|
|
1141
|
+
let ledgerBody = null;
|
|
1142
|
+
if (taskId) {
|
|
1143
|
+
ledgerBody = await fetchJson(
|
|
1144
|
+
`/api/tasks/${encodeURIComponent(taskId)}/ledger`,
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
return { taskId, prewriteResult, ledgerBody };
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Inspect 节点面板:frontend prewrite 门禁分类/稳定 failure code + ledger 安全摘要。
|
|
1152
|
+
* 数据全部来自只读接口与确定性产物(AC-BLOCK-005):task identity 从 DAG run
|
|
1153
|
+
* GET /api/dag-runs/:dagRunId 读取,prewrite 门禁调 DAG run 专属
|
|
1154
|
+
* GET /api/dag-runs/:dagRunId/prewrite-result;任何失败仅显示安全降级文案,
|
|
1155
|
+
* 绝不 push envelope。
|
|
1156
|
+
*/
|
|
1157
|
+
async function renderFrontendGateLedger(content, dagRunId) {
|
|
1158
|
+
const section = el("section", "node-input-section");
|
|
1159
|
+
section.appendChild(
|
|
1160
|
+
el("h4", null, "Frontend prewrite 门禁 / source fidelity ledger"),
|
|
1161
|
+
);
|
|
1162
|
+
const status = el("p", "muted", "加载中…");
|
|
1163
|
+
status.setAttribute("role", "status");
|
|
1164
|
+
section.appendChild(status);
|
|
1165
|
+
content.appendChild(section);
|
|
1166
|
+
try {
|
|
1167
|
+
const { taskId, prewriteResult, ledgerBody } =
|
|
1168
|
+
await fetchFrontendGateLedgerData(dagRunId);
|
|
1169
|
+
const lines = frontendGateLedgerLines({
|
|
1170
|
+
prewriteResult,
|
|
1171
|
+
ledgerBody,
|
|
1172
|
+
taskId,
|
|
1173
|
+
});
|
|
1174
|
+
const pre = el("pre", "cmd", lines.join("\n"));
|
|
1175
|
+
pre.dataset.testid = "frontend-prewrite-ledger-summary";
|
|
1176
|
+
status.replaceWith(pre);
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
status.textContent = `加载失败:${
|
|
1179
|
+
error instanceof Error ? error.message : String(error)
|
|
1180
|
+
}(安全降级,不展示敏感内容)`;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* Experimental prompt-override rerun tab. Only reachable when the server
|
|
1186
|
+
* injected the mutation token (switch on). Every node shows its frozen
|
|
1187
|
+
* subtask_prompt here; only FAILED (ERROR) nodes additionally get the editor +
|
|
1188
|
+
* restart flow: preflight (dagRerunPlan) binds the edit into planHash, dagRerun
|
|
1189
|
+
* executes it as a Console operation, and on success we jump to the new run.
|
|
1190
|
+
*/
|
|
1191
|
+
async function renderPromptRestart(content, dagRunId, node) {
|
|
1192
|
+
const nodeId = node.nodeId;
|
|
1193
|
+
clearNode(content);
|
|
1194
|
+
|
|
1195
|
+
const loading = el("p", "muted", "正在加载冻结提示词…");
|
|
1196
|
+
loading.setAttribute("role", "status");
|
|
1197
|
+
loading.setAttribute("aria-live", "polite");
|
|
1198
|
+
content.appendChild(loading);
|
|
1199
|
+
const input = await fetchDagNodeInput(dagRunId, nodeId);
|
|
1200
|
+
if (
|
|
1201
|
+
!content.isConnected ||
|
|
1202
|
+
content.dataset.dagNodeId !== String(nodeId) ||
|
|
1203
|
+
content.dataset.dagRunId !== String(dagRunId) ||
|
|
1204
|
+
activeInspectorTab() !== "prompt-restart"
|
|
1205
|
+
) {
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
clearNode(content);
|
|
1209
|
+
|
|
1210
|
+
if (!input.ok || !input.body?.available) {
|
|
1211
|
+
const error = el("p", "node-input-error", "无法读取该节点的冻结提示词。");
|
|
1212
|
+
error.setAttribute("role", "alert");
|
|
1213
|
+
content.appendChild(error);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
const frozenBasePrompt =
|
|
1217
|
+
typeof input.body.prompt?.text === "string" ? input.body.prompt.text : "";
|
|
1218
|
+
if (!frozenBasePrompt) {
|
|
1219
|
+
const error = el("p", "node-input-error", "该节点没有冻结提示词。");
|
|
1220
|
+
error.setAttribute("role", "alert");
|
|
1221
|
+
content.appendChild(error);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
const promptCandidateOptions = buildPromptRestartCandidateOptions(
|
|
1225
|
+
frozenBasePrompt,
|
|
1226
|
+
input.body.promptCandidates,
|
|
1227
|
+
);
|
|
1228
|
+
let selectedCandidate = promptCandidateOptions[0];
|
|
1229
|
+
const historicalCandidatesMissing = Array.isArray(input.body.warnings)
|
|
1230
|
+
? input.body.warnings.some((warning) =>
|
|
1231
|
+
String(warning).startsWith("Failed prompt candidate missing for attempt "),
|
|
1232
|
+
)
|
|
1233
|
+
: false;
|
|
1234
|
+
|
|
1235
|
+
if (node.status !== "ERROR") {
|
|
1236
|
+
// Non-failed nodes still show their frozen prompt — read-only.
|
|
1237
|
+
const hint = el(
|
|
1238
|
+
"p",
|
|
1239
|
+
"muted",
|
|
1240
|
+
`当前节点状态为 ${node.status ?? "unknown"};仅失败(ERROR)节点支持编辑提示词并重启。以下可只读查看冻结提示词与历史失败候选。`,
|
|
1241
|
+
);
|
|
1242
|
+
hint.setAttribute("role", "status");
|
|
1243
|
+
content.appendChild(hint);
|
|
1244
|
+
const picker = el("label", "prompt-restart-field");
|
|
1245
|
+
picker.appendChild(el("span", "muted", "提示词版本(只读)"));
|
|
1246
|
+
const select = document.createElement("select");
|
|
1247
|
+
select.className = "prompt-restart-mode-select";
|
|
1248
|
+
select.setAttribute("data-testid", "prompt-restart-candidate");
|
|
1249
|
+
for (const option of promptCandidateOptions) {
|
|
1250
|
+
const element = document.createElement("option");
|
|
1251
|
+
element.value = option.key;
|
|
1252
|
+
element.textContent = option.label;
|
|
1253
|
+
select.appendChild(element);
|
|
1254
|
+
}
|
|
1255
|
+
picker.appendChild(select);
|
|
1256
|
+
if (promptCandidateOptions.length === 1) {
|
|
1257
|
+
picker.appendChild(
|
|
1258
|
+
el(
|
|
1259
|
+
"span",
|
|
1260
|
+
"muted",
|
|
1261
|
+
historicalCandidatesMissing
|
|
1262
|
+
? "该历史运行未保存失败 attempt 的实际提示词;新运行会在此列出。"
|
|
1263
|
+
: "该节点没有已保存的失败 attempt。",
|
|
1264
|
+
),
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
content.appendChild(picker);
|
|
1268
|
+
|
|
1269
|
+
const promptCard = el("div", "prompt-restart-prompt-card");
|
|
1270
|
+
const promptHead = el("div", "prompt-restart-prompt-head");
|
|
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;
|
|
1287
|
+
});
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
if (input.body.prompt?.truncated === true) {
|
|
1292
|
+
const error = el(
|
|
1293
|
+
"p",
|
|
1294
|
+
"node-input-error",
|
|
1295
|
+
"冻结提示词被截断,为避免误替换请改用 CLI(dag rerun --prompt-override)。",
|
|
1296
|
+
);
|
|
1297
|
+
error.setAttribute("role", "alert");
|
|
1298
|
+
content.appendChild(error);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
const box = el("div", "prompt-restart-box");
|
|
1303
|
+
box.appendChild(
|
|
1304
|
+
el(
|
|
1305
|
+
"p",
|
|
1306
|
+
"muted",
|
|
1307
|
+
"编辑该失败节点的提示词并从它重启:父 run spec 保持冻结,将创建带修改提示词的续跑 run(实验能力,仅 ERROR 节点)。",
|
|
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})`),
|
|
1359
|
+
);
|
|
1360
|
+
baseHead.appendChild(createPromptRestartCopyButton(() => selectedCandidate));
|
|
1361
|
+
baseLabel.appendChild(baseHead);
|
|
1362
|
+
const baseBlock = document.createElement("pre");
|
|
1363
|
+
baseBlock.className = "prompt-restart-readonly";
|
|
1364
|
+
baseBlock.setAttribute("data-testid", "prompt-restart-base");
|
|
1365
|
+
baseBlock.textContent = selectedCandidate.prompt;
|
|
1366
|
+
baseLabel.appendChild(baseBlock);
|
|
1367
|
+
box.appendChild(baseLabel);
|
|
1368
|
+
|
|
1369
|
+
const addendumLabel = el("label", "prompt-restart-field");
|
|
1370
|
+
addendumLabel.appendChild(
|
|
1371
|
+
el(
|
|
1372
|
+
"span",
|
|
1373
|
+
"muted",
|
|
1374
|
+
"追加指令(将包裹为 <operator_addendum> 块附加到原提示词末尾)",
|
|
1375
|
+
),
|
|
1376
|
+
);
|
|
1377
|
+
const addendumArea = document.createElement("textarea");
|
|
1378
|
+
addendumArea.className = "prompt-restart-textarea";
|
|
1379
|
+
addendumArea.rows = 6;
|
|
1380
|
+
addendumArea.placeholder = "例如:重点检查 X 边界;输出前先自检 Y 约束…";
|
|
1381
|
+
addendumArea.setAttribute("data-testid", "prompt-restart-addendum");
|
|
1382
|
+
addendumLabel.appendChild(addendumArea);
|
|
1383
|
+
box.appendChild(addendumLabel);
|
|
1384
|
+
|
|
1385
|
+
const replaceLabel = el("label", "prompt-restart-field");
|
|
1386
|
+
replaceLabel.appendChild(el("span", "muted", `完整提示词(${nodeId})`));
|
|
1387
|
+
const replaceArea = document.createElement("textarea");
|
|
1388
|
+
replaceArea.className = "prompt-restart-textarea";
|
|
1389
|
+
replaceArea.rows = 16;
|
|
1390
|
+
replaceArea.value = selectedCandidate.prompt;
|
|
1391
|
+
replaceArea.setAttribute("data-testid", "prompt-restart-textarea");
|
|
1392
|
+
replaceLabel.appendChild(replaceArea);
|
|
1393
|
+
box.appendChild(replaceLabel);
|
|
1394
|
+
|
|
1395
|
+
const applyModeVisibility = () => {
|
|
1396
|
+
const append = modeSelect.value === "append";
|
|
1397
|
+
baseLabel.style.display = append ? "" : "none";
|
|
1398
|
+
addendumLabel.style.display = append ? "" : "none";
|
|
1399
|
+
replaceLabel.style.display = append ? "none" : "";
|
|
1400
|
+
};
|
|
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
|
+
applyModeVisibility();
|
|
1422
|
+
|
|
1423
|
+
const reasonLabel = el("label", "prompt-restart-field");
|
|
1424
|
+
reasonLabel.appendChild(el("span", "muted", "重启原因(结构化说明)"));
|
|
1425
|
+
const reasonArea = document.createElement("textarea");
|
|
1426
|
+
reasonArea.className = "prompt-restart-textarea";
|
|
1427
|
+
reasonArea.rows = 3;
|
|
1428
|
+
reasonArea.value = `operator-prompt-override / run=${dagRunId} / node=${nodeId} / 手工编辑失败节点提示词后重启`;
|
|
1429
|
+
reasonArea.setAttribute("data-testid", "prompt-restart-reason");
|
|
1430
|
+
reasonLabel.appendChild(reasonArea);
|
|
1431
|
+
box.appendChild(reasonLabel);
|
|
1432
|
+
|
|
1433
|
+
const row = el("div", "prompt-restart-actions");
|
|
1434
|
+
const submit = document.createElement("button");
|
|
1435
|
+
submit.type = "button";
|
|
1436
|
+
submit.className = "prompt-restart-submit";
|
|
1437
|
+
submit.textContent = "资格预检并重启";
|
|
1438
|
+
submit.setAttribute("data-testid", "prompt-restart-submit");
|
|
1439
|
+
row.appendChild(submit);
|
|
1440
|
+
box.appendChild(row);
|
|
1441
|
+
|
|
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
|
+
content.appendChild(box);
|
|
1448
|
+
|
|
1449
|
+
const setStatus = (text, isError = false) => {
|
|
1450
|
+
status.textContent = text ?? "";
|
|
1451
|
+
status.classList.toggle("prompt-restart-error", isError);
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
submit.addEventListener("click", () => {
|
|
1455
|
+
void (async () => {
|
|
1456
|
+
if (selectedCandidate.truncated) {
|
|
1457
|
+
setStatus("所选失败提示词已截断,不能作为完整提示词重启。", true);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
const mode = modeSelect.value === "replace" ? "replace" : "append";
|
|
1461
|
+
const prompt =
|
|
1462
|
+
mode === "append" ? addendumArea.value : replaceArea.value;
|
|
1463
|
+
const reason = reasonArea.value.trim();
|
|
1464
|
+
if (!prompt.trim()) {
|
|
1465
|
+
setStatus(
|
|
1466
|
+
mode === "append" ? "追加指令不能为空。" : "提示词不能为空。",
|
|
1467
|
+
true,
|
|
1468
|
+
);
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (
|
|
1472
|
+
mode === "replace" &&
|
|
1473
|
+
selectedCandidate.key === "frozen" &&
|
|
1474
|
+
prompt === frozenBasePrompt
|
|
1475
|
+
) {
|
|
1476
|
+
setStatus("提示词未修改;如需按原提示词重跑请使用恢复工作台。", true);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
if (!reason) {
|
|
1480
|
+
setStatus("需要填写重启原因。", true);
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
submit.disabled = true;
|
|
1484
|
+
try {
|
|
1485
|
+
setStatus("正在做重跑资格预检(dagRerunPlan)…");
|
|
1486
|
+
const overrides = [{ nodeId, prompt, mode }];
|
|
1487
|
+
const planRes = await postOperatorAction("dagRerunPlan", {
|
|
1488
|
+
runId: dagRunId,
|
|
1489
|
+
fromNode: nodeId,
|
|
1490
|
+
promptOverrides: overrides,
|
|
1491
|
+
});
|
|
1492
|
+
const plan = planRes.body?.result ?? null;
|
|
1493
|
+
if (!planRes.ok || plan?.eligible !== true || !plan?.planHash) {
|
|
1494
|
+
setStatus(
|
|
1495
|
+
planRes.body?.error?.message ??
|
|
1496
|
+
plan?.blockedReasons?.join(", ") ??
|
|
1497
|
+
"资格预检未通过。",
|
|
1498
|
+
true,
|
|
1499
|
+
);
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
const est = plan.estimatedExecution;
|
|
1504
|
+
const estText = est
|
|
1505
|
+
? `(将执行 ${est.nodeCount ?? "?"} 个节点 / ${est.piCallCount ?? "?"} 次 Pi 调用)`
|
|
1506
|
+
: "";
|
|
1507
|
+
setStatus(`预检通过${estText},正在提交续跑(dagRerun)…`);
|
|
1508
|
+
const clientRequestId = `inspect-prompt-restart-${Date.now()}-${Math.random()
|
|
1509
|
+
.toString(36)
|
|
1510
|
+
.slice(2, 10)}`;
|
|
1511
|
+
const runRes = await postOperatorAction(
|
|
1512
|
+
"dagRerun",
|
|
1513
|
+
{
|
|
1514
|
+
runId: dagRunId,
|
|
1515
|
+
fromNode: nodeId,
|
|
1516
|
+
planHash: plan.planHash,
|
|
1517
|
+
reason,
|
|
1518
|
+
promptOverrides: overrides,
|
|
1519
|
+
},
|
|
1520
|
+
clientRequestId,
|
|
1521
|
+
);
|
|
1522
|
+
const operationId = runRes.body?.operationId;
|
|
1523
|
+
if (!runRes.ok || !operationId) {
|
|
1524
|
+
setStatus(runRes.body?.error?.message ?? "提交续跑失败。", true);
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
const op = await pollOperationToTerminal(operationId, (text) =>
|
|
1529
|
+
setStatus(text),
|
|
1530
|
+
);
|
|
1531
|
+
if (op?.state === "succeeded") {
|
|
1532
|
+
const newRunId =
|
|
1533
|
+
op.dagRunId ?? op.result?.newRunId ?? op.runSummary?.runId ?? null;
|
|
1534
|
+
if (newRunId) {
|
|
1535
|
+
setStatus(`重启完成,新 run:${newRunId}`);
|
|
1536
|
+
const open = document.createElement("button");
|
|
1537
|
+
open.type = "button";
|
|
1538
|
+
open.className = "prompt-restart-submit";
|
|
1539
|
+
open.textContent = "打开新 run";
|
|
1540
|
+
open.addEventListener("click", () => {
|
|
1541
|
+
window.location.hash = `#/dag/${encodeURIComponent(String(newRunId))}`;
|
|
1542
|
+
});
|
|
1543
|
+
row.appendChild(open);
|
|
1544
|
+
} else {
|
|
1545
|
+
setStatus("重启完成(未能解析新 run id,请在 DAG 历史中查看)。");
|
|
1546
|
+
}
|
|
1547
|
+
} else if (op) {
|
|
1548
|
+
setStatus(
|
|
1549
|
+
`续跑操作终态:${op.state}${op.errorMessage ? ` — ${op.errorMessage}` : ""}`,
|
|
1550
|
+
true,
|
|
1551
|
+
);
|
|
1552
|
+
} else {
|
|
1553
|
+
setStatus("等待超时:操作仍在运行,可在操作台查看进度。", true);
|
|
1554
|
+
}
|
|
1555
|
+
} catch (err) {
|
|
1556
|
+
setStatus(err instanceof Error ? err.message : String(err), true);
|
|
1557
|
+
} finally {
|
|
1558
|
+
submit.disabled = false;
|
|
1559
|
+
}
|
|
1560
|
+
})();
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
/** Poll a Console operation until a terminal state; null on poll budget end. */
|
|
1565
|
+
async function pollOperationToTerminal(operationId, onProgress) {
|
|
1566
|
+
const terminal = new Set([
|
|
1567
|
+
"succeeded",
|
|
1568
|
+
"failed",
|
|
1569
|
+
"timed-out",
|
|
1570
|
+
"needs-reconcile",
|
|
1571
|
+
]);
|
|
1572
|
+
for (let attempt = 0; attempt < 1200; attempt += 1) {
|
|
1573
|
+
const res = await fetchOperatorOperation(operationId);
|
|
1574
|
+
const op = res.body?.operation ?? res.body;
|
|
1575
|
+
const state = op?.state ?? "";
|
|
1576
|
+
if (res.ok && terminal.has(state)) {
|
|
1577
|
+
return op;
|
|
1578
|
+
}
|
|
1579
|
+
onProgress(`续跑执行中…(${state || "queued"})`);
|
|
1580
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
1581
|
+
}
|
|
1582
|
+
return null;
|
|
917
1583
|
}
|
|
918
1584
|
|
|
919
1585
|
function activeInspectorTab() {
|
|
920
|
-
return ["input", "timeline", "spec-evidence"].includes(
|
|
1586
|
+
return ["input", "timeline", "spec-evidence", "prompt-restart"].includes(
|
|
921
1587
|
uiState.dagInspectorTab,
|
|
922
1588
|
)
|
|
923
1589
|
? uiState.dagInspectorTab
|
|
@@ -1254,7 +1920,7 @@ function appendOutputBody(content, outputText, errorText, options = null) {
|
|
|
1254
1920
|
content.appendChild(el("p", "empty", UI_TEXT.noOutput));
|
|
1255
1921
|
return;
|
|
1256
1922
|
}
|
|
1257
|
-
if (outputText) content.appendChild(
|
|
1923
|
+
if (outputText) content.appendChild(renderInspectorDocument(outputText));
|
|
1258
1924
|
// Truncation note only for full/cached execution-output bodies (AC-003);
|
|
1259
1925
|
// preview path never invents truncated/maxBytes.
|
|
1260
1926
|
if (options?.truncated === true) {
|
|
@@ -1270,7 +1936,7 @@ function appendOutputBody(content, outputText, errorText, options = null) {
|
|
|
1270
1936
|
if (errorText) {
|
|
1271
1937
|
const error = el("section", "markdown-error");
|
|
1272
1938
|
error.appendChild(el("p", "markdown-error-title", "错误输出"));
|
|
1273
|
-
error.appendChild(
|
|
1939
|
+
error.appendChild(renderInspectorDocument(errorText));
|
|
1274
1940
|
content.appendChild(error);
|
|
1275
1941
|
}
|
|
1276
1942
|
}
|
|
@@ -1404,6 +2070,228 @@ function repaintOutputTabBody(content, dagRunId, node) {
|
|
|
1404
2070
|
restoreInspectorContentScroll(content, dagRunId, node.nodeId, "output");
|
|
1405
2071
|
}
|
|
1406
2072
|
|
|
2073
|
+
function processEmptyMessage(body) {
|
|
2074
|
+
const reason = body?.emptyReason;
|
|
2075
|
+
if (reason === "static-no-process") return UI_TEXT.staticNoProcess;
|
|
2076
|
+
if (reason === "shell-receipt-missing") return UI_TEXT.noShellProcess;
|
|
2077
|
+
if (reason === "shell-preset-pending") return UI_TEXT.noPresetProcess;
|
|
2078
|
+
if (reason === "pi-session") return UI_TEXT.noSessionEvents;
|
|
2079
|
+
return UI_TEXT.noSessionEvents;
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
function formatProcessBlock(text) {
|
|
2083
|
+
if (!text) return "";
|
|
2084
|
+
return prettyJsonBlock(text) ?? text;
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
function appendProcessPre(parent, text) {
|
|
2088
|
+
if (!text) return;
|
|
2089
|
+
const pre = el("pre", "process-timeline-code");
|
|
2090
|
+
pre.textContent = formatProcessBlock(text);
|
|
2091
|
+
parent.appendChild(pre);
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
function renderProcessCommandCard(command) {
|
|
2095
|
+
const failed = command.ok === false;
|
|
2096
|
+
const card = el(
|
|
2097
|
+
"div",
|
|
2098
|
+
`process-timeline-card process-timeline-tool-card${failed ? " is-error" : ""}`,
|
|
2099
|
+
);
|
|
2100
|
+
const details = document.createElement("details");
|
|
2101
|
+
details.className = "process-timeline-details";
|
|
2102
|
+
details.open = failed;
|
|
2103
|
+
const summary = el("summary", "process-timeline-summary");
|
|
2104
|
+
summary.appendChild(
|
|
2105
|
+
el("span", "process-timeline-summary-title", command.command || "command"),
|
|
2106
|
+
);
|
|
2107
|
+
summary.appendChild(
|
|
2108
|
+
el(
|
|
2109
|
+
"span",
|
|
2110
|
+
`process-timeline-summary-status status-${failed ? "failed" : "succeeded"}`,
|
|
2111
|
+
failed ? "失败" : command.reused ? "复用" : "成功",
|
|
2112
|
+
),
|
|
2113
|
+
);
|
|
2114
|
+
details.appendChild(summary);
|
|
2115
|
+
const body = el("div", "process-timeline-card-body");
|
|
2116
|
+
const meta = [
|
|
2117
|
+
command.exitCode != null ? `exit ${command.exitCode}` : null,
|
|
2118
|
+
command.durationMs != null ? `耗时 ${formatMs(command.durationMs)}` : null,
|
|
2119
|
+
command.failureCategory ? command.failureCategory : null,
|
|
2120
|
+
].filter(Boolean);
|
|
2121
|
+
if (meta.length > 0) {
|
|
2122
|
+
body.appendChild(el("div", "process-timeline-section-label", meta.join(" · ")));
|
|
2123
|
+
}
|
|
2124
|
+
if (command.stdout) {
|
|
2125
|
+
body.appendChild(el("div", "process-timeline-section-label", "stdout"));
|
|
2126
|
+
appendProcessPre(body, command.stdout);
|
|
2127
|
+
}
|
|
2128
|
+
if (command.stderr) {
|
|
2129
|
+
body.appendChild(el("div", "process-timeline-section-label", "stderr"));
|
|
2130
|
+
appendProcessPre(body, command.stderr);
|
|
2131
|
+
}
|
|
2132
|
+
if (command.truncated) {
|
|
2133
|
+
body.appendChild(el("p", "muted", "内容过长,已截断。"));
|
|
2134
|
+
}
|
|
2135
|
+
details.appendChild(body);
|
|
2136
|
+
card.appendChild(details);
|
|
2137
|
+
return card;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
function renderProcessStepCard(step) {
|
|
2141
|
+
const failed = step.ok === false;
|
|
2142
|
+
const card = el(
|
|
2143
|
+
"div",
|
|
2144
|
+
`process-timeline-card process-timeline-tool-card${failed ? " is-error" : ""}`,
|
|
2145
|
+
);
|
|
2146
|
+
const details = document.createElement("details");
|
|
2147
|
+
details.className = "process-timeline-details";
|
|
2148
|
+
details.open = true;
|
|
2149
|
+
const summary = el("summary", "process-timeline-summary");
|
|
2150
|
+
summary.appendChild(
|
|
2151
|
+
el("span", "process-timeline-summary-title", step.label || step.id || "step"),
|
|
2152
|
+
);
|
|
2153
|
+
summary.appendChild(
|
|
2154
|
+
el(
|
|
2155
|
+
"span",
|
|
2156
|
+
`process-timeline-summary-status status-${failed ? "failed" : "succeeded"}`,
|
|
2157
|
+
failed ? "失败" : "成功",
|
|
2158
|
+
),
|
|
2159
|
+
);
|
|
2160
|
+
details.appendChild(summary);
|
|
2161
|
+
const body = el("div", "process-timeline-card-body");
|
|
2162
|
+
if (step.durationMs != null) {
|
|
2163
|
+
body.appendChild(
|
|
2164
|
+
el("div", "process-timeline-section-label", `耗时 ${formatMs(step.durationMs)}`),
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
if (step.detail) appendProcessPre(body, step.detail);
|
|
2168
|
+
if (step.truncated) {
|
|
2169
|
+
body.appendChild(el("p", "muted", "内容过长,已截断。"));
|
|
2170
|
+
}
|
|
2171
|
+
details.appendChild(body);
|
|
2172
|
+
card.appendChild(details);
|
|
2173
|
+
return card;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
function renderNonPiProcess(content, body) {
|
|
2177
|
+
content.classList.add("process-timeline-scroll");
|
|
2178
|
+
if (!body || body.emptyReason || ((body.commands ?? []).length === 0 && (body.steps ?? []).length === 0)) {
|
|
2179
|
+
content.appendChild(el("p", "empty", processEmptyMessage(body)));
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
const heading =
|
|
2183
|
+
body.kind === "shell-preset"
|
|
2184
|
+
? UI_TEXT.shellPresetHeading
|
|
2185
|
+
: UI_TEXT.shellCommandsHeading;
|
|
2186
|
+
if (body.handler) {
|
|
2187
|
+
content.appendChild(
|
|
2188
|
+
el("p", "process-timeline-note", `${heading} · ${body.handler}`),
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
const ul = el("ul", "process-timeline process-timeline-rich");
|
|
2192
|
+
const items = (body.commands ?? []).length > 0 ? body.commands : body.steps ?? [];
|
|
2193
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
2194
|
+
const item = items[index];
|
|
2195
|
+
const failed = item.ok === false;
|
|
2196
|
+
const li = el(
|
|
2197
|
+
"li",
|
|
2198
|
+
`process-timeline-item process-timeline-tool-end${failed ? " is-error" : ""}${index === 0 ? " is-first" : ""}${index === items.length - 1 ? " is-last" : ""}`,
|
|
2199
|
+
);
|
|
2200
|
+
const rail = el("span", "process-timeline-rail");
|
|
2201
|
+
rail.appendChild(el("span", "process-timeline-dot"));
|
|
2202
|
+
li.appendChild(rail);
|
|
2203
|
+
const cardBody = el("div", "process-timeline-body");
|
|
2204
|
+
cardBody.appendChild(
|
|
2205
|
+
body.kind === "shell-preset"
|
|
2206
|
+
? renderProcessStepCard(item)
|
|
2207
|
+
: renderProcessCommandCard(item),
|
|
2208
|
+
);
|
|
2209
|
+
li.appendChild(cardBody);
|
|
2210
|
+
ul.appendChild(li);
|
|
2211
|
+
}
|
|
2212
|
+
content.appendChild(ul);
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
export async function mergeNodeProcess(dagRunId, node) {
|
|
2216
|
+
if (!dagRunId || !node?.nodeId) return;
|
|
2217
|
+
const nodeId = node.nodeId;
|
|
2218
|
+
const key = effectiveExecutionKey(dagRunId, node);
|
|
2219
|
+
const identity = makeDagNodeProcessIdentity(dagRunId, nodeId, key);
|
|
2220
|
+
uiState.dagNodeProcessIdentity = identity;
|
|
2221
|
+
uiState.dagNodeProcessLoading = true;
|
|
2222
|
+
const result = await fetchDagNodeProcess(dagRunId, nodeId, key);
|
|
2223
|
+
if (uiState.dagNodeProcessIdentity !== identity) return;
|
|
2224
|
+
if (!result.ok || !result.body) {
|
|
2225
|
+
uiState.dagNodeProcessError =
|
|
2226
|
+
result.body?.error || UI_TEXT.processError;
|
|
2227
|
+
uiState.dagNodeProcessData = {
|
|
2228
|
+
schemaVersion: 1,
|
|
2229
|
+
dagRunId,
|
|
2230
|
+
nodeId,
|
|
2231
|
+
kind: "none",
|
|
2232
|
+
executor: node.executor ?? null,
|
|
2233
|
+
handler: null,
|
|
2234
|
+
emptyReason: "not-executed",
|
|
2235
|
+
key: key ?? null,
|
|
2236
|
+
commands: [],
|
|
2237
|
+
steps: [],
|
|
2238
|
+
};
|
|
2239
|
+
uiState.dagNodeProcessLoading = false;
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
uiState.dagNodeProcessError = null;
|
|
2243
|
+
uiState.dagNodeProcessData = result.body;
|
|
2244
|
+
uiState.dagNodeProcessLoading = false;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
function renderExecutorAwareTimeline(content, dagRunId, node) {
|
|
2248
|
+
content.dataset.dagRunId = String(dagRunId ?? "");
|
|
2249
|
+
content.dataset.nodeId = String(node.nodeId ?? "");
|
|
2250
|
+
const key = effectiveExecutionKey(dagRunId, node);
|
|
2251
|
+
const cached = uiState.dagNodeProcessData;
|
|
2252
|
+
const sameNode =
|
|
2253
|
+
cached &&
|
|
2254
|
+
cached.dagRunId === dagRunId &&
|
|
2255
|
+
cached.nodeId === node.nodeId &&
|
|
2256
|
+
(cached.key ?? null) === (key ?? null);
|
|
2257
|
+
if (sameNode && uiState.dagNodeProcessError) {
|
|
2258
|
+
content.appendChild(el("p", "empty", uiState.dagNodeProcessError));
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
if (sameNode && cached.kind === "pi-session") {
|
|
2262
|
+
renderSessionTimeline(content, node.nodeId, {
|
|
2263
|
+
executionKey: effectiveExecutionKey(dagRunId, node),
|
|
2264
|
+
});
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
if (sameNode && cached.kind !== "pi-session") {
|
|
2268
|
+
renderNonPiProcess(content, cached);
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
if ((node.executor ?? "pi") === "pi") {
|
|
2272
|
+
renderSessionTimeline(content, node.nodeId, {
|
|
2273
|
+
executionKey: effectiveExecutionKey(dagRunId, node),
|
|
2274
|
+
});
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
if (uiState.dagNodeProcessError && sameNode) {
|
|
2278
|
+
content.appendChild(el("p", "empty", uiState.dagNodeProcessError));
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
const loading = el("p", "muted", UI_TEXT.processLoading);
|
|
2282
|
+
loading.setAttribute("role", "status");
|
|
2283
|
+
content.appendChild(loading);
|
|
2284
|
+
void mergeNodeProcess(dagRunId, node).then(() => {
|
|
2285
|
+
if (
|
|
2286
|
+
uiState.dagInspectorTab === "timeline" &&
|
|
2287
|
+
uiState.selectedDagNodeId === node.nodeId &&
|
|
2288
|
+
uiState.currentDagRunId === dagRunId
|
|
2289
|
+
) {
|
|
2290
|
+
renderDagInspectorForCurrent();
|
|
2291
|
+
}
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
|
|
1407
2295
|
function fillDagInspectorContent(content, dagRunId, node) {
|
|
1408
2296
|
const previousRenderedNode = content.dataset.dagNodeId ?? "";
|
|
1409
2297
|
const previousRenderedRun = content.dataset.dagRunId ?? "";
|
|
@@ -1450,10 +2338,12 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
1450
2338
|
);
|
|
1451
2339
|
if (activeTab === "spec-evidence") {
|
|
1452
2340
|
void renderSpecEvidence(content, dagRunId, node.nodeId);
|
|
2341
|
+
} else if (activeTab === "prompt-restart") {
|
|
2342
|
+
void renderPromptRestart(content, dagRunId, node);
|
|
1453
2343
|
} else if (activeTab === "timeline") {
|
|
1454
2344
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
1455
2345
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
1456
|
-
|
|
2346
|
+
renderExecutorAwareTimeline(content, dagRunId, node);
|
|
1457
2347
|
} else if (activeTab === "input") {
|
|
1458
2348
|
void renderDagNodeInput(content, dagRunId, node);
|
|
1459
2349
|
} else {
|
|
@@ -1517,6 +2407,13 @@ function buildDagInspectorHeader(dagRunId, node) {
|
|
|
1517
2407
|
["timeline", "执行过程", "ri-route-line"],
|
|
1518
2408
|
["spec-evidence", "规范证据", "ri-book-open-line"],
|
|
1519
2409
|
];
|
|
2410
|
+
// Prompt-override rerun: the server injects the SCOPED inspect mutation
|
|
2411
|
+
// token into /inspect HTML by default (it only authorizes
|
|
2412
|
+
// dagRerunPlan/dagRerun). The emergency kill switch suppresses the token;
|
|
2413
|
+
// no token → no tab → the inspector stays strictly read-only.
|
|
2414
|
+
if (globalThis.__INSPECT_MUTATION_TOKEN__) {
|
|
2415
|
+
tabDefs.push(["prompt-restart", "提示词重启", "ri-restart-line"]);
|
|
2416
|
+
}
|
|
1520
2417
|
const tabs = el("div", "dag-inspector-tabs");
|
|
1521
2418
|
tabs.setAttribute("role", "tablist");
|
|
1522
2419
|
tabs.setAttribute("aria-label", "节点检查器面板");
|
|
@@ -1606,7 +2503,14 @@ function dagInspectorSignature(dagRunId, node) {
|
|
|
1606
2503
|
uiState.dagNodeExecutionOutputLoading ? "loading" : "idle",
|
|
1607
2504
|
]
|
|
1608
2505
|
: tab === "timeline"
|
|
1609
|
-
? [
|
|
2506
|
+
? [
|
|
2507
|
+
uiState.sessionEventOffset,
|
|
2508
|
+
uiState.dagNodeProcessData?.kind ?? "",
|
|
2509
|
+
uiState.dagNodeProcessData?.emptyReason ?? "",
|
|
2510
|
+
(uiState.dagNodeProcessData?.commands ?? []).length,
|
|
2511
|
+
(uiState.dagNodeProcessData?.steps ?? []).length,
|
|
2512
|
+
uiState.dagNodeProcessLoading ? "loading" : "idle",
|
|
2513
|
+
]
|
|
1610
2514
|
: [node.status ?? ""];
|
|
1611
2515
|
// Attempt dimension: selection identity + history shape. Poll refreshes keep
|
|
1612
2516
|
// the DOM while selection/history are unchanged; switching attempt or a
|