@tea-agent/loop-agent 0.39.0-next.8 → 0.39.0
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 +264 -67
- 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 +4 -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.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 +554 -33
- 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 +28 -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 +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +28 -0
- package/dist/task/contract/constants.js +1 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/project.js +5 -0
- package/dist/task/contract/schema.js +6 -1
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- 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/dag-execution-receipt.js +33 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operator-actions.js +195 -4
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +5 -4
- 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 +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +42 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BLXFe4qI.js → abnfDiagram-N423BO3Z-Br92EGb7.js} +1 -1
- package/dist/worker/console/static/assets/{arc-7iOJeqho.js → arc-oulImtQq.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CYoyj5K7.js → architectureDiagram-T3A2C74G-tyHc6aqO.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-fK01JNlR.js → blockDiagram-VBNYF7ZC-CY86BSpT.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-bzbw5D_Z.js → c4Diagram-5PPSVZJV-BKKsDmNs.js} +1 -1
- package/dist/worker/console/static/assets/channel-TY36zt-i.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Cf6hJFFG.js → chunk-2GRJ4B5K-CXZdjl3B.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DSFXR-et.js → chunk-2Q5K7J3B-B-aIFDQ5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BagJechl.js → chunk-5RXB4S5H-BDgAW_B2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-E7QNMFaW.js → chunk-5VM5RSS4-DABa4UPR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BG_eOBrC.js → chunk-6Q2QTUOP-aGbysVOx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bo42dqol.js → chunk-GF5L2VYU-B8MSZDVs.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-dibM-87v.js → chunk-JWPE2WC7-CpelV73g.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bytrp3Oe.js → chunk-KBJHAD2P-BE04ty3z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DBABILrr.js → chunk-RYQCIY6F-BUxTkaaR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DCUk5Pf9.js → chunk-XXDRQBXY-CYYBVKfX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BlGE9v6E.js → cose-bilkent-JH36ORCC-CEQF_xmQ.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DGl5ewph.js → cynefin-VYW2F7L2-CqpxN5IT.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DsJ_SVH_.js → cynefinDiagram-MW4NZA55-B3PzanVi.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BIhjFdzv.js → dagre-VZM6K2ZE-BSUGCNx1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dmy1ctTb.js → diagram-7IWD3JNH-CdLevU5T.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYCtWUF3.js → diagram-B4RE2ZJO-BjpF_Kok.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BMZFHVd2.js → diagram-LBJQPF4R-C4I6NJny.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-b_U8gfaO.js → diagram-Q27KOJAE-C8hecCr5.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dzy_bn-I.js → diagram-UB23O5K3-Daod6tOS.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DJCiMNEI.js → ebnfDiagram-BXEA7PRR-BvvmazGv.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-28lD016B.js → erDiagram-JOGREHBK-DeN_vfV4.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DGyHJmIw.js → flowDiagram-UKHOOZJN-D6dq1t9z.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CzdwUjrO.js → ganttDiagram-PKOTCBZU-DziIdNY4.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D9bGwuo8.js → gitGraphDiagram-DS77QQ5N-Dy4s5CXr.js} +1 -1
- package/dist/worker/console/static/assets/index-C3GgHg-g.css +1 -0
- package/dist/worker/console/static/assets/index-Ci8ksvBm.js +406 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-LJgJUQFC.js → infoDiagram-6WML65LV-Czv5eYx2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-98LaB8zb.js → ishikawaDiagram-WSZJBQD7-Bsk3nFRT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-D-ISzwlQ.js → journeyDiagram-NVQOT4AX-DxGmNj5K.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-gfYOI-EL.js → kanban-definition-27J2QSJJ-CYCFqkO_.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CyntZgKV.js → linear-B0YzRwrd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BBOR7dg0.js → mermaid.core-t3EZtIGh.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CQAgwPMk.js → mindmap-definition-FAOFIHXS-BTXpTv0T.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DkMiZFuV.js → pegDiagram-VL7TDLO6-DU8UkKqA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DLNXa_0J.js → pieDiagram-7S7Q4E2Y-DP4y8ohp.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js → quadrantDiagram-CIZ2JOQS-B07vNaYR.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BxMB0QqW.js → railroadDiagram-AXF67PYL-xDWktb2i.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-FjumoKn4.js → requirementDiagram-LRYGKXZP-BLNMKqeS.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CkhKc_E3.js → sankeyDiagram-W5VNT64P-n0adalxH.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DoBWqTTH.js → sequenceDiagram-SI44F4Z6-BbnS-2DL.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CQeh_YeE.js → sizeCapture-X5ZJPWSS-B4uPpCLu.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-LuHmFW-I.js → stateDiagram-OKZ733FA--MMbB0fX.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqOigMQ4.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CHwbDctY.js → swimlanes-SLNWSIFB-BXUgp2QY.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Caxz9UA2.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CoMngk0X.js → timeline-definition-Z64GVDOM-CsaMxki7.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CURYbtbO.js → vennDiagram-T6HMQDX7-C2IuG9iH.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BdqSECjU.js → wardleyDiagram-T6FBY63Y-CmEWOXCW.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DbMUYuAg.js → xychartDiagram-ELKLHX3M-D_9LE0MG.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +1 -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/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/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -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/workflows/dag/backend-test-case-coverage-analysis.js +827 -53
- 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 +167 -32
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +368 -33
- 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 +100 -56
- 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 +377 -4
- package/dist/workflows/dag/frontend-plan-render.js +1 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +96 -84
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +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 +894 -205
- package/dist/workflows/dag/node-execution.js +183 -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 +143 -1
- package/dist/workflows/dag/validate.js +64 -20
- package/docs/init-surface.manifest.json +5 -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 +1 -1
- 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 +1 -1
- 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/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
|
@@ -2,7 +2,8 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { redactSecrets, truncateUtf8Preview } from "../../shared/preview.js";
|
|
4
4
|
import { resolveDagNodeSkills } from "../../workflows/dag/skills.js";
|
|
5
|
-
import { resolveDagNodePromptRedacted, resolveDagRunJson, } from "./dag-run-artifacts.js";
|
|
5
|
+
import { resolveDagNodePromptRedacted, resolveDagRunArtifact, resolveDagRunJson, } from "./dag-run-artifacts.js";
|
|
6
|
+
import { namedShellHandlers } from "./shell-handler-keys.js";
|
|
6
7
|
export const NODE_INPUT_SCHEMA_VERSION = 1;
|
|
7
8
|
export const NODE_INPUT_PREVIEW_MAX_BYTES = 64 * 1024;
|
|
8
9
|
const SHELL_GATE_KEYS = [
|
|
@@ -33,6 +34,7 @@ function unavailableBody(dagRunId, nodeId, reason, warnings = []) {
|
|
|
33
34
|
executorInput: null,
|
|
34
35
|
boundaries: null,
|
|
35
36
|
promptFingerprint: null,
|
|
37
|
+
piExtensionRunFacts: null,
|
|
36
38
|
warnings,
|
|
37
39
|
};
|
|
38
40
|
}
|
|
@@ -137,7 +139,7 @@ function resolveModelHint(spec, task) {
|
|
|
137
139
|
}
|
|
138
140
|
return null;
|
|
139
141
|
}
|
|
140
|
-
function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warnings) {
|
|
142
|
+
function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, piExtensionRunFacts, warnings) {
|
|
141
143
|
const defaults = spec.defaults && typeof spec.defaults === "object"
|
|
142
144
|
? spec.defaults
|
|
143
145
|
: undefined;
|
|
@@ -216,6 +218,7 @@ function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warni
|
|
|
216
218
|
: null,
|
|
217
219
|
failFast: typeof shellRaw.failFast === "boolean" ? shellRaw.failFast : null,
|
|
218
220
|
gates: shellGateNames(shellRaw),
|
|
221
|
+
handlers: namedShellHandlers(shellRaw),
|
|
219
222
|
}
|
|
220
223
|
: null;
|
|
221
224
|
const staticPreview = staticRaw && typeof staticRaw.resultMarkdown === "string"
|
|
@@ -268,6 +271,7 @@ function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warni
|
|
|
268
271
|
kind,
|
|
269
272
|
skills,
|
|
270
273
|
modelHint: resolveModelHint(spec, task),
|
|
274
|
+
piExtensions: redactStringList(task.piExtensions),
|
|
271
275
|
shell,
|
|
272
276
|
static: staticBlock,
|
|
273
277
|
},
|
|
@@ -284,9 +288,72 @@ function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warni
|
|
|
284
288
|
: null,
|
|
285
289
|
},
|
|
286
290
|
promptFingerprint: fingerprint,
|
|
291
|
+
piExtensionRunFacts,
|
|
287
292
|
warnings,
|
|
288
293
|
};
|
|
289
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Load run-time Pi extension facts from <node>/pi-extensions.json (plan
|
|
297
|
+
* 2026-08-22 R2). Returns null when the node declares no piExtensions
|
|
298
|
+
* (no expected artifact). Never throws: unreadable/corrupt artifacts degrade
|
|
299
|
+
* to parseError stubs so the node-input endpoint stays 200.
|
|
300
|
+
*/
|
|
301
|
+
async function loadPiExtensionRunFacts(repoRoot, dagRunId, nodeId, declared, warnings) {
|
|
302
|
+
if (declared.length === 0)
|
|
303
|
+
return null;
|
|
304
|
+
const artifact = path.posix.join(nodeId, "pi-extensions.json");
|
|
305
|
+
const resolved = resolveDagRunArtifact(repoRoot, dagRunId, [
|
|
306
|
+
nodeId,
|
|
307
|
+
"pi-extensions.json",
|
|
308
|
+
]);
|
|
309
|
+
if (!resolved.ok) {
|
|
310
|
+
if (resolved.reason === "ambiguous") {
|
|
311
|
+
warnings.push("Ambiguous pi-extensions.json lifecycle copies.");
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
artifact,
|
|
315
|
+
exists: false,
|
|
316
|
+
requested: [...declared],
|
|
317
|
+
resolved: [],
|
|
318
|
+
missing: [],
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const empty = {
|
|
322
|
+
artifact,
|
|
323
|
+
exists: false,
|
|
324
|
+
requested: [...declared],
|
|
325
|
+
resolved: [],
|
|
326
|
+
missing: [],
|
|
327
|
+
};
|
|
328
|
+
try {
|
|
329
|
+
const raw = JSON.parse(await readFile(resolved.result.absolutePath, "utf-8"));
|
|
330
|
+
const resolvedList = Array.isArray(raw.resolved) ? raw.resolved : [];
|
|
331
|
+
const missingList = Array.isArray(raw.missing) ? raw.missing : [];
|
|
332
|
+
const projected = resolvedList
|
|
333
|
+
.filter((entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry))
|
|
334
|
+
.map((entry) => ({
|
|
335
|
+
id: redactSecrets(String(entry.id ?? "")),
|
|
336
|
+
packageName: redactSecrets(String(entry.packageName ?? "")),
|
|
337
|
+
version: redactSecrets(String(entry.version ?? "")),
|
|
338
|
+
}));
|
|
339
|
+
const missingProjected = missingList
|
|
340
|
+
.filter((entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry))
|
|
341
|
+
.map((entry) => ({
|
|
342
|
+
id: redactSecrets(String(entry.id ?? "")),
|
|
343
|
+
reason: redactSecrets(String(entry.reason ?? "")),
|
|
344
|
+
}));
|
|
345
|
+
return {
|
|
346
|
+
artifact,
|
|
347
|
+
exists: true,
|
|
348
|
+
requested: redactStringList(raw.requested ?? declared),
|
|
349
|
+
resolved: projected,
|
|
350
|
+
missing: missingProjected,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return { ...empty, parseError: "read-failed" };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
290
357
|
async function loadPromptFingerprint(repoRoot, dagRunId, nodeId, warnings) {
|
|
291
358
|
const resolved = resolveDagNodePromptRedacted(repoRoot, dagRunId, nodeId);
|
|
292
359
|
if (!resolved.ok) {
|
|
@@ -415,8 +482,12 @@ export async function buildDagNodeInput(repoRoot, dagRunId, nodeId) {
|
|
|
415
482
|
}
|
|
416
483
|
const warnings = [];
|
|
417
484
|
const fingerprint = await loadPromptFingerprint(repoRoot, dagRunId, nodeId, warnings);
|
|
485
|
+
const declaredPiExtensions = Array.isArray(task.piExtensions)
|
|
486
|
+
? task.piExtensions.filter((item) => typeof item === "string")
|
|
487
|
+
: [];
|
|
488
|
+
const piExtensionRunFacts = await loadPiExtensionRunFacts(repoRoot, dagRunId, nodeId, declaredPiExtensions, warnings);
|
|
418
489
|
// Reject unknown nested leakage by projecting only through allowlist helpers.
|
|
419
|
-
const body = projectTask(dagRunId, nodeId, runResolved.result.lifecycle, spec, task, fingerprint, warnings);
|
|
490
|
+
const body = projectTask(dagRunId, nodeId, runResolved.result.lifecycle, spec, task, fingerprint, piExtensionRunFacts, warnings);
|
|
420
491
|
// Defense-in-depth: ensure raw task never sneaks in.
|
|
421
492
|
if ("task" in body) {
|
|
422
493
|
return {
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { redactSecrets, truncateUtf8Preview } from "../../shared/preview.js";
|
|
3
|
+
import { isSafeObservabilityIdentifier } from "../observability/event-store.js";
|
|
4
|
+
import { parseExecutionOutputKey, } from "./dag-node-execution-output.js";
|
|
5
|
+
import { resolveDagRunArtifact, resolveDagRunJson, } from "./dag-run-artifacts.js";
|
|
6
|
+
import { namedShellHandlers } from "./shell-handler-keys.js";
|
|
7
|
+
export const NODE_PROCESS_SCHEMA_VERSION = 1;
|
|
8
|
+
export const NODE_PROCESS_FIELD_MAX_BYTES = 16 * 1024;
|
|
9
|
+
function boundField(value) {
|
|
10
|
+
if (value == null || value === "")
|
|
11
|
+
return { text: null, truncated: false };
|
|
12
|
+
const redacted = redactSecrets(value);
|
|
13
|
+
const truncated = truncateUtf8Preview(redacted, NODE_PROCESS_FIELD_MAX_BYTES);
|
|
14
|
+
return {
|
|
15
|
+
text: truncated,
|
|
16
|
+
truncated: truncated !== redacted,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function asRecord(value) {
|
|
20
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
21
|
+
return null;
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function asString(value) {
|
|
25
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
26
|
+
}
|
|
27
|
+
function asNumber(value) {
|
|
28
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
29
|
+
}
|
|
30
|
+
function asBoolean(value) {
|
|
31
|
+
return value === true;
|
|
32
|
+
}
|
|
33
|
+
async function readJsonArtifact(repoRoot, dagRunId, segments) {
|
|
34
|
+
const resolved = resolveDagRunArtifact(repoRoot, dagRunId, segments);
|
|
35
|
+
if (!resolved.ok)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
const raw = await readFile(resolved.result.absolutePath, "utf8");
|
|
39
|
+
const parsed = JSON.parse(raw);
|
|
40
|
+
return asRecord(parsed);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function readTextArtifact(repoRoot, dagRunId, segments) {
|
|
47
|
+
const resolved = resolveDagRunArtifact(repoRoot, dagRunId, segments);
|
|
48
|
+
if (!resolved.ok)
|
|
49
|
+
return { text: null, truncated: false };
|
|
50
|
+
try {
|
|
51
|
+
const raw = await readFile(resolved.result.absolutePath, "utf8");
|
|
52
|
+
return boundField(raw);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { text: null, truncated: false };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function emptyBody(dagRunId, nodeId, kind, emptyReason, extras = {}) {
|
|
59
|
+
return {
|
|
60
|
+
schemaVersion: NODE_PROCESS_SCHEMA_VERSION,
|
|
61
|
+
dagRunId,
|
|
62
|
+
nodeId,
|
|
63
|
+
kind,
|
|
64
|
+
executor: extras.executor ?? null,
|
|
65
|
+
handler: extras.handler ?? null,
|
|
66
|
+
emptyReason,
|
|
67
|
+
key: extras.key ?? null,
|
|
68
|
+
commands: extras.commands ?? [],
|
|
69
|
+
steps: extras.steps ?? [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function classifyKind(task) {
|
|
73
|
+
const executor = asString(task.executor);
|
|
74
|
+
const shell = asRecord(task.shell);
|
|
75
|
+
const handlers = namedShellHandlers(shell);
|
|
76
|
+
const handler = handlers[0] ?? null;
|
|
77
|
+
const commands = shell?.commands;
|
|
78
|
+
const commandsDeclared = Array.isArray(commands) &&
|
|
79
|
+
commands.some((item) => typeof item === "string" && item.trim());
|
|
80
|
+
if (executor === "static" || task.static) {
|
|
81
|
+
return { kind: "static", executor: executor ?? "static", handler, commandsDeclared };
|
|
82
|
+
}
|
|
83
|
+
if (executor === "shell" || shell) {
|
|
84
|
+
if (commandsDeclared) {
|
|
85
|
+
return { kind: "shell-commands", executor: executor ?? "shell", handler, commandsDeclared };
|
|
86
|
+
}
|
|
87
|
+
if (handler) {
|
|
88
|
+
return { kind: "shell-preset", executor: executor ?? "shell", handler, commandsDeclared };
|
|
89
|
+
}
|
|
90
|
+
return { kind: "shell-commands", executor: executor ?? "shell", handler, commandsDeclared };
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
kind: "pi-session",
|
|
94
|
+
executor: executor ?? "pi",
|
|
95
|
+
handler,
|
|
96
|
+
commandsDeclared,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function receiptAttemptSegments(nodeId, parsed) {
|
|
100
|
+
if (parsed?.kind === "attempt") {
|
|
101
|
+
return [nodeId, `attempt-${parsed.index}`, "shell-command-receipt.json"];
|
|
102
|
+
}
|
|
103
|
+
return [nodeId, "shell-command-receipt.json"];
|
|
104
|
+
}
|
|
105
|
+
async function loadShellCommands(repoRoot, dagRunId, nodeId, parsed) {
|
|
106
|
+
const preferred = await readJsonArtifact(repoRoot, dagRunId, receiptAttemptSegments(nodeId, parsed));
|
|
107
|
+
const receipt = preferred ??
|
|
108
|
+
(parsed
|
|
109
|
+
? await readJsonArtifact(repoRoot, dagRunId, [
|
|
110
|
+
nodeId,
|
|
111
|
+
"shell-command-receipt.json",
|
|
112
|
+
])
|
|
113
|
+
: null);
|
|
114
|
+
if (!receipt)
|
|
115
|
+
return null;
|
|
116
|
+
const rawCommands = Array.isArray(receipt.commands) ? receipt.commands : [];
|
|
117
|
+
const commands = [];
|
|
118
|
+
for (let i = 0; i < rawCommands.length; i += 1) {
|
|
119
|
+
const entry = asRecord(rawCommands[i]) ?? {};
|
|
120
|
+
const stdout = await readTextArtifact(repoRoot, dagRunId, [
|
|
121
|
+
nodeId,
|
|
122
|
+
"commands",
|
|
123
|
+
`${i + 1}.stdout.txt`,
|
|
124
|
+
]);
|
|
125
|
+
const stderr = await readTextArtifact(repoRoot, dagRunId, [
|
|
126
|
+
nodeId,
|
|
127
|
+
"commands",
|
|
128
|
+
`${i + 1}.stderr.txt`,
|
|
129
|
+
]);
|
|
130
|
+
const commandBound = boundField(asString(entry.command) ?? "");
|
|
131
|
+
commands.push({
|
|
132
|
+
index: i + 1,
|
|
133
|
+
command: commandBound.text ?? "(command)",
|
|
134
|
+
ok: asBoolean(entry.ok),
|
|
135
|
+
exitCode: asNumber(entry.exitCode),
|
|
136
|
+
failureCategory: asString(entry.failureCategory),
|
|
137
|
+
startedAt: asString(entry.startedAt),
|
|
138
|
+
finishedAt: asString(entry.finishedAt),
|
|
139
|
+
durationMs: asNumber(entry.wallDurationMs) ??
|
|
140
|
+
asNumber(entry.executionDurationMs) ??
|
|
141
|
+
null,
|
|
142
|
+
reused: asBoolean(entry.reused),
|
|
143
|
+
stdout: stdout.text,
|
|
144
|
+
stderr: stderr.text,
|
|
145
|
+
truncated: commandBound.truncated || stdout.truncated || stderr.truncated,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return commands;
|
|
149
|
+
}
|
|
150
|
+
function handlerLabel(handler) {
|
|
151
|
+
if (handler === "frontendBrowserToolPreflight")
|
|
152
|
+
return "浏览器工具能力探测";
|
|
153
|
+
if (handler === "frontendTestStandardScenarios")
|
|
154
|
+
return "标准场景物化";
|
|
155
|
+
if (handler === "frontendTestEnvironmentProbe")
|
|
156
|
+
return "前端环境探测";
|
|
157
|
+
if (handler === "frontendTestCaseChecklist")
|
|
158
|
+
return "用例清单校验";
|
|
159
|
+
if (handler === "frontendTestCaseManifest")
|
|
160
|
+
return "用例清单物化";
|
|
161
|
+
if (handler === "frontendTestEvidenceValidation")
|
|
162
|
+
return "证据校验";
|
|
163
|
+
if (handler === "frontendTestResultFinalize")
|
|
164
|
+
return "结果定稿";
|
|
165
|
+
if (handler === "frontendTestReports" || handler === "frontendTestL5Report" || handler === "frontendTestHtmlReport") {
|
|
166
|
+
return "测试报告";
|
|
167
|
+
}
|
|
168
|
+
if (handler === "frontendPrewriteGate")
|
|
169
|
+
return "前端预写门禁";
|
|
170
|
+
if (handler === "finalWriteSetApprovalGate")
|
|
171
|
+
return "writeSet 终态批准";
|
|
172
|
+
return handler;
|
|
173
|
+
}
|
|
174
|
+
async function loadPresetSteps(repoRoot, dagRunId, nodeId, handler, parsed) {
|
|
175
|
+
const written = await readJsonArtifact(repoRoot, dagRunId, [
|
|
176
|
+
nodeId,
|
|
177
|
+
"process-receipt.json",
|
|
178
|
+
]);
|
|
179
|
+
if (written && Array.isArray(written.steps)) {
|
|
180
|
+
return written.steps.flatMap((item, index) => {
|
|
181
|
+
const step = asRecord(item);
|
|
182
|
+
if (!step)
|
|
183
|
+
return [];
|
|
184
|
+
const detail = boundField(asString(step.detail));
|
|
185
|
+
return [
|
|
186
|
+
{
|
|
187
|
+
id: asString(step.id) ?? `step-${index + 1}`,
|
|
188
|
+
label: asString(step.label) ?? handlerLabel(handler ?? "preset"),
|
|
189
|
+
ok: asBoolean(step.ok),
|
|
190
|
+
detail: detail.text,
|
|
191
|
+
durationMs: asNumber(step.durationMs),
|
|
192
|
+
truncated: detail.truncated,
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const capability = await readJsonArtifact(repoRoot, dagRunId, [
|
|
198
|
+
nodeId,
|
|
199
|
+
"frontend-browser-capability.json",
|
|
200
|
+
]);
|
|
201
|
+
if (capability && handler === "frontendBrowserToolPreflight") {
|
|
202
|
+
const detail = boundField(JSON.stringify(capability, null, 2));
|
|
203
|
+
return [
|
|
204
|
+
{
|
|
205
|
+
id: "capability",
|
|
206
|
+
label: handlerLabel(handler),
|
|
207
|
+
ok: asString(capability.status) !== "failed",
|
|
208
|
+
detail: detail.text,
|
|
209
|
+
durationMs: null,
|
|
210
|
+
truncated: detail.truncated,
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
const attemptIndex = parsed?.kind === "attempt" ? parsed.index : 1;
|
|
215
|
+
const attempt = await readJsonArtifact(repoRoot, dagRunId, [
|
|
216
|
+
nodeId,
|
|
217
|
+
`attempt-${attemptIndex}.json`,
|
|
218
|
+
]);
|
|
219
|
+
if (attempt) {
|
|
220
|
+
const ok = asBoolean(attempt.ok);
|
|
221
|
+
const detail = boundField(ok
|
|
222
|
+
? (asString(attempt.stdout) ?? asString(attempt.assistantText))
|
|
223
|
+
: (asString(attempt.stderr) ?? asString(attempt.stdout)));
|
|
224
|
+
return [
|
|
225
|
+
{
|
|
226
|
+
id: "run",
|
|
227
|
+
label: handlerLabel(handler ?? "preset"),
|
|
228
|
+
ok,
|
|
229
|
+
detail: detail.text,
|
|
230
|
+
durationMs: asNumber(attempt.durationMs),
|
|
231
|
+
truncated: detail.truncated,
|
|
232
|
+
},
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
export async function loadDagNodeProcess(repoRoot, dagRunId, nodeId, rawKey) {
|
|
238
|
+
if (!isSafeObservabilityIdentifier(dagRunId) ||
|
|
239
|
+
!isSafeObservabilityIdentifier(nodeId)) {
|
|
240
|
+
return {
|
|
241
|
+
status: 400,
|
|
242
|
+
body: { error: "Invalid dag run or node identifier", reason: "invalid-id" },
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
let parsedKey = null;
|
|
246
|
+
if (rawKey != null && String(rawKey).trim() !== "") {
|
|
247
|
+
const parsed = parseExecutionOutputKey(rawKey);
|
|
248
|
+
if (!parsed.ok) {
|
|
249
|
+
return {
|
|
250
|
+
status: 400,
|
|
251
|
+
body: {
|
|
252
|
+
error: "Invalid execution key (expected attempt-N or pass-N)",
|
|
253
|
+
reason: "invalid-key",
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
parsedKey = parsed;
|
|
258
|
+
}
|
|
259
|
+
const runResolved = resolveDagRunJson(repoRoot, dagRunId);
|
|
260
|
+
if (!runResolved.ok) {
|
|
261
|
+
if (runResolved.reason === "invalid-id") {
|
|
262
|
+
return {
|
|
263
|
+
status: 400,
|
|
264
|
+
body: { error: "Invalid dag run or node identifier", reason: "invalid-id" },
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
if (runResolved.reason === "ambiguous") {
|
|
268
|
+
return {
|
|
269
|
+
status: 409,
|
|
270
|
+
body: { error: "Ambiguous dag run lifecycle", reason: "ambiguous" },
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
status: 404,
|
|
275
|
+
body: { error: "DAG run not found", reason: "not-found" },
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
let spec = {};
|
|
279
|
+
try {
|
|
280
|
+
const raw = JSON.parse(await readFile(runResolved.result.absolutePath, "utf8"));
|
|
281
|
+
spec = asRecord(raw) ?? {};
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return {
|
|
285
|
+
status: 404,
|
|
286
|
+
body: { error: "run.json is unreadable", reason: "not-found" },
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const tasks = Array.isArray(spec.tasks) ? spec.tasks : [];
|
|
290
|
+
const task = tasks
|
|
291
|
+
.map((item) => asRecord(item))
|
|
292
|
+
.find((item) => item && asString(item.id) === nodeId);
|
|
293
|
+
if (!task) {
|
|
294
|
+
return {
|
|
295
|
+
status: 200,
|
|
296
|
+
body: emptyBody(dagRunId, nodeId, "none", "not-executed"),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const classified = classifyKind(task);
|
|
300
|
+
const key = parsedKey?.key ?? null;
|
|
301
|
+
if (classified.kind === "pi-session") {
|
|
302
|
+
return {
|
|
303
|
+
status: 200,
|
|
304
|
+
body: emptyBody(dagRunId, nodeId, "pi-session", "pi-session", {
|
|
305
|
+
executor: classified.executor,
|
|
306
|
+
key,
|
|
307
|
+
}),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
if (classified.kind === "static") {
|
|
311
|
+
return {
|
|
312
|
+
status: 200,
|
|
313
|
+
body: emptyBody(dagRunId, nodeId, "static", "static-no-process", {
|
|
314
|
+
executor: classified.executor,
|
|
315
|
+
key,
|
|
316
|
+
}),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (classified.kind === "shell-commands" || classified.kind === "shell-preset") {
|
|
320
|
+
const commands = await loadShellCommands(repoRoot, dagRunId, nodeId, parsedKey);
|
|
321
|
+
if (commands && commands.length > 0) {
|
|
322
|
+
return {
|
|
323
|
+
status: 200,
|
|
324
|
+
body: {
|
|
325
|
+
schemaVersion: NODE_PROCESS_SCHEMA_VERSION,
|
|
326
|
+
dagRunId,
|
|
327
|
+
nodeId,
|
|
328
|
+
kind: "shell-commands",
|
|
329
|
+
executor: classified.executor,
|
|
330
|
+
handler: classified.handler,
|
|
331
|
+
emptyReason: null,
|
|
332
|
+
key,
|
|
333
|
+
commands,
|
|
334
|
+
steps: [],
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
if (classified.handler || classified.kind === "shell-preset") {
|
|
339
|
+
const steps = await loadPresetSteps(repoRoot, dagRunId, nodeId, classified.handler, parsedKey);
|
|
340
|
+
if (steps.length > 0) {
|
|
341
|
+
return {
|
|
342
|
+
status: 200,
|
|
343
|
+
body: {
|
|
344
|
+
schemaVersion: NODE_PROCESS_SCHEMA_VERSION,
|
|
345
|
+
dagRunId,
|
|
346
|
+
nodeId,
|
|
347
|
+
kind: "shell-preset",
|
|
348
|
+
executor: classified.executor,
|
|
349
|
+
handler: classified.handler,
|
|
350
|
+
emptyReason: null,
|
|
351
|
+
key,
|
|
352
|
+
commands: [],
|
|
353
|
+
steps,
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
status: 200,
|
|
360
|
+
body: emptyBody(dagRunId, nodeId, classified.kind, classified.kind === "shell-preset"
|
|
361
|
+
? "shell-preset-pending"
|
|
362
|
+
: "shell-receipt-missing", {
|
|
363
|
+
executor: classified.executor,
|
|
364
|
+
handler: classified.handler,
|
|
365
|
+
key,
|
|
366
|
+
}),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
status: 200,
|
|
371
|
+
body: emptyBody(dagRunId, nodeId, "none", "not-executed", {
|
|
372
|
+
executor: classified.executor,
|
|
373
|
+
handler: classified.handler,
|
|
374
|
+
key,
|
|
375
|
+
}),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
@@ -16,6 +16,7 @@ import { isAllowedArtifactTextPath, resolveArtifactPath, resolveRepoFilePreview,
|
|
|
16
16
|
import { extractSpecEvidence, extractSpecReadContent, } from "./spec-evidence.js";
|
|
17
17
|
import { buildDagNodeInput } from "./node-input.js";
|
|
18
18
|
import { loadDagNodeExecutionOutput } from "./dag-node-execution-output.js";
|
|
19
|
+
import { loadDagNodeProcess } from "./node-process.js";
|
|
19
20
|
import { buildObserveHealthV1 } from "./health.js";
|
|
20
21
|
import { buildNightJobDetail, buildNightJobsDoctor, buildNightJobsList, buildNightJobsMorning, } from "./night-jobs.js";
|
|
21
22
|
const ARTIFACT_PREVIEW_MAX_BYTES = 64 * 1024;
|
|
@@ -100,6 +101,11 @@ export const ROUTES = [
|
|
|
100
101
|
pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/execution-output$/,
|
|
101
102
|
handler: handleDagNodeExecutionOutput,
|
|
102
103
|
},
|
|
104
|
+
{
|
|
105
|
+
method: "GET",
|
|
106
|
+
pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/process$/,
|
|
107
|
+
handler: handleDagNodeProcess,
|
|
108
|
+
},
|
|
103
109
|
{
|
|
104
110
|
method: "GET",
|
|
105
111
|
pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/spec-evidence$/,
|
|
@@ -221,14 +227,23 @@ export async function serveObserveStatic(req, res, staticDir, options = {}) {
|
|
|
221
227
|
sendJson(res, 404, { error: "Not found" });
|
|
222
228
|
return;
|
|
223
229
|
}
|
|
224
|
-
|
|
230
|
+
let content = await readFile(resolvedFile);
|
|
225
231
|
const contentType = contentTypeFor(pathname);
|
|
226
|
-
|
|
232
|
+
const headers = {
|
|
227
233
|
"Content-Type": contentType,
|
|
228
234
|
"Content-Length": content.byteLength,
|
|
229
235
|
"X-Content-Type-Options": "nosniff",
|
|
230
236
|
"X-Frame-Options": "SAMEORIGIN",
|
|
231
|
-
}
|
|
237
|
+
};
|
|
238
|
+
if (contentType.startsWith("text/html") && options.htmlEnhancer) {
|
|
239
|
+
const enhanced = options.htmlEnhancer(Buffer.from(content));
|
|
240
|
+
content = enhanced.content;
|
|
241
|
+
headers["Content-Length"] = content.byteLength;
|
|
242
|
+
if (enhanced.setCookie) {
|
|
243
|
+
headers["Set-Cookie"] = enhanced.setCookie;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
res.writeHead(200, headers);
|
|
232
247
|
res.end(content);
|
|
233
248
|
}
|
|
234
249
|
async function handleHealth(_req, res, _match, ctx) {
|
|
@@ -570,6 +585,24 @@ async function handleDagNodeExecutionOutput(_req, res, match, ctx) {
|
|
|
570
585
|
const result = await loadDagNodeExecutionOutput(ctx.repoRoot, dagRunId, nodeId, key);
|
|
571
586
|
sendJson(res, result.status, result.body);
|
|
572
587
|
}
|
|
588
|
+
async function handleDagNodeProcess(_req, res, match, ctx) {
|
|
589
|
+
const dagRunId = match.params.id;
|
|
590
|
+
const nodeId = match.params.sub;
|
|
591
|
+
if (!isSafeObservabilityIdentifier(dagRunId) ||
|
|
592
|
+
!isSafeObservabilityIdentifier(nodeId)) {
|
|
593
|
+
sendJson(res, 400, { error: "Invalid dag run or node identifier" });
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if (match.query.has("path") || match.query.has("artifactPath")) {
|
|
597
|
+
sendJson(res, 400, {
|
|
598
|
+
error: "Artifact path is not accepted; use optional key=attempt-N|pass-N",
|
|
599
|
+
reason: "path-not-allowed",
|
|
600
|
+
});
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const result = await loadDagNodeProcess(ctx.repoRoot, dagRunId, nodeId, match.query.get("key"));
|
|
604
|
+
sendJson(res, result.status, result.body);
|
|
605
|
+
}
|
|
573
606
|
async function handleDagNodeSessionEvents(_req, res, match, ctx) {
|
|
574
607
|
const dagRunId = match.params.id;
|
|
575
608
|
const nodeId = match.params.sub;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allowlist of in-process shell handlers (preset/gate flags on task.shell).
|
|
3
|
+
* Observe projects these names; the executor writes process receipts for them.
|
|
4
|
+
*/
|
|
5
|
+
export const SHELL_NAMED_HANDLER_KEYS = [
|
|
6
|
+
"verdictGate",
|
|
7
|
+
"projectGovernanceGate",
|
|
8
|
+
"requirementCoverageGate",
|
|
9
|
+
"jsonArtifactGate",
|
|
10
|
+
"frontendPrewriteGate",
|
|
11
|
+
"frontendLintBaseline",
|
|
12
|
+
"frontendVerificationBundle",
|
|
13
|
+
"frontendReviewContext",
|
|
14
|
+
"frontendTestCaseChecklist",
|
|
15
|
+
"frontendBrowserToolPreflight",
|
|
16
|
+
"frontendTestStandardScenarios",
|
|
17
|
+
"frontendTestEnvironmentProbe",
|
|
18
|
+
"frontendTestEvidenceValidation",
|
|
19
|
+
"finalWriteSetApprovalGate",
|
|
20
|
+
"frontendTestL5Report",
|
|
21
|
+
"frontendTestHtmlReport",
|
|
22
|
+
"frontendTestCaseManifest",
|
|
23
|
+
"frontendTestResultFinalize",
|
|
24
|
+
"frontendTestReports",
|
|
25
|
+
"backendTestPipeline",
|
|
26
|
+
"jacocoCoverage",
|
|
27
|
+
"verifyEvidence",
|
|
28
|
+
"repairArtifactGate",
|
|
29
|
+
];
|
|
30
|
+
export function namedShellHandlers(shell) {
|
|
31
|
+
if (!shell)
|
|
32
|
+
return [];
|
|
33
|
+
return SHELL_NAMED_HANDLER_KEYS.filter((key) => shell[key] != null);
|
|
34
|
+
}
|
|
@@ -77,6 +77,14 @@ export async function fetchDagNodeInput(runId, nodeId) {
|
|
|
77
77
|
return fetchJsonResult(url);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
export async function fetchDagNodeProcess(runId, nodeId, key) {
|
|
81
|
+
const params = new URLSearchParams();
|
|
82
|
+
if (key) params.set("key", String(key));
|
|
83
|
+
const qs = params.toString();
|
|
84
|
+
const url = `/api/dag-runs/${encodeURIComponent(runId)}/nodes/${encodeURIComponent(nodeId)}/process${qs ? `?${qs}` : ""}`;
|
|
85
|
+
return fetchJsonResult(url);
|
|
86
|
+
}
|
|
87
|
+
|
|
80
88
|
/**
|
|
81
89
|
* Fetch a spec-evidence file preview (binding source or successful read).
|
|
82
90
|
* Resolves to the parsed JSON body, or throws an Error carrying the HTTP
|
|
@@ -104,6 +112,64 @@ export function artifactUrl(artifactPath) {
|
|
|
104
112
|
return `/api/artifacts?path=${encodeURIComponent(artifactPath)}&tail=200`;
|
|
105
113
|
}
|
|
106
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Experimental (prompt-override rerun only): POST one allowlisted operator
|
|
117
|
+
* action through the Console mutation gate using the SCOPED inspect token
|
|
118
|
+
* (window.__INSPECT_MUTATION_TOKEN__). The server injects it into /inspect
|
|
119
|
+
* HTML only when HARNESS_DAG_RERUN_PROMPT_OVERRIDE is enabled, and it only
|
|
120
|
+
* authorizes dagRerunPlan / dagRerun — never general Console mutations.
|
|
121
|
+
* Absent token → the call is not attempted.
|
|
122
|
+
*/
|
|
123
|
+
export async function postOperatorAction(action, actionParams = {}) {
|
|
124
|
+
const token = globalThis.__INSPECT_MUTATION_TOKEN__;
|
|
125
|
+
if (!token) {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
status: 0,
|
|
129
|
+
body: { error: { code: "PROMPT_OVERRIDE_DISABLED", message: "提示词重启未启用(服务端未注入确认 token)" } },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const res = await fetch("/api/operator/v1/operations", {
|
|
134
|
+
method: "POST",
|
|
135
|
+
credentials: "same-origin",
|
|
136
|
+
headers: {
|
|
137
|
+
"Content-Type": "application/json",
|
|
138
|
+
"x-loop-console-confirmation": token,
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({ action, actionParams }),
|
|
141
|
+
});
|
|
142
|
+
let body = null;
|
|
143
|
+
try {
|
|
144
|
+
body = await res.json();
|
|
145
|
+
} catch {
|
|
146
|
+
body = null;
|
|
147
|
+
}
|
|
148
|
+
return { ok: res.ok, status: res.status, body };
|
|
149
|
+
} catch {
|
|
150
|
+
return { ok: false, status: 0, body: null };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Poll a Console operation snapshot (read-only GET). */
|
|
155
|
+
export async function fetchOperatorOperation(operationId) {
|
|
156
|
+
try {
|
|
157
|
+
const res = await fetch(
|
|
158
|
+
`/api/operator/v1/operations/${encodeURIComponent(operationId)}`,
|
|
159
|
+
{ credentials: "same-origin" },
|
|
160
|
+
);
|
|
161
|
+
let body = null;
|
|
162
|
+
try {
|
|
163
|
+
body = await res.json();
|
|
164
|
+
} catch {
|
|
165
|
+
body = null;
|
|
166
|
+
}
|
|
167
|
+
return { ok: res.ok, status: res.status, body };
|
|
168
|
+
} catch {
|
|
169
|
+
return { ok: false, status: 0, body: null };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
107
173
|
export function parseArtifactPreviewResponse(text) {
|
|
108
174
|
const parsed = JSON.parse(text);
|
|
109
175
|
if (!parsed || typeof parsed.content !== "string") {
|