@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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure decision core for the TasksPage run-id resolver (2026-08-23).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the React effect so the full lifecycle — wizard first run,
|
|
5
|
+
* rerun dispatch, task switch, foreign shared selection, baseline-gated
|
|
6
|
+
* lookup — is unit-testable as state transitions without mounting React.
|
|
7
|
+
*/
|
|
8
|
+
export function initialRunResolutionState() {
|
|
9
|
+
return { baseline: null, ownedRunId: "", pendingSelection: "" };
|
|
10
|
+
}
|
|
11
|
+
export function observeRunResolution(state, input) {
|
|
12
|
+
const firstObservation = state.task === undefined;
|
|
13
|
+
const taskChanged = !firstObservation && state.task !== input.task;
|
|
14
|
+
const dispatchChanged = !firstObservation && state.dispatch !== input.dispatchId;
|
|
15
|
+
const next = {
|
|
16
|
+
...state,
|
|
17
|
+
task: input.task,
|
|
18
|
+
dispatch: input.dispatchId,
|
|
19
|
+
pendingSelection: "",
|
|
20
|
+
};
|
|
21
|
+
if (taskChanged) {
|
|
22
|
+
next.baseline = null;
|
|
23
|
+
next.ownedRunId = "";
|
|
24
|
+
return { state: next, observation: { action: "reset-selection" } };
|
|
25
|
+
}
|
|
26
|
+
if (dispatchChanged) {
|
|
27
|
+
next.ownedRunId = "";
|
|
28
|
+
if (input.selectedRunId) {
|
|
29
|
+
next.baseline = input.selectedRunId;
|
|
30
|
+
return { state: next, observation: { action: "invalidate-selection" } };
|
|
31
|
+
}
|
|
32
|
+
// Nothing shown yet: keep the existing baseline and keep probing.
|
|
33
|
+
}
|
|
34
|
+
const selected = input.selectedRunId;
|
|
35
|
+
if (selected && selected !== next.ownedRunId) {
|
|
36
|
+
next.pendingSelection = selected;
|
|
37
|
+
return { state: next, observation: { action: "verify-ownership" } };
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
state: next,
|
|
41
|
+
observation: { action: selected ? "idle" : "probe" },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function markBaselineUnknown(state) {
|
|
45
|
+
return { ...state, baseline: null };
|
|
46
|
+
}
|
|
47
|
+
export function applyBaselineSnapshot(state, dagRunId) {
|
|
48
|
+
return { ...state, baseline: dagRunId ?? "" };
|
|
49
|
+
}
|
|
50
|
+
/** Accept an observeLink lookup: only a run of this task that differs from
|
|
51
|
+
* the pre-dispatch baseline may be adopted — never the previous run, and
|
|
52
|
+
* never anything while the baseline is still unknown. */
|
|
53
|
+
export function applyRunLookup(state, outcome) {
|
|
54
|
+
if (!outcome.ok || !outcome.dagRunId) {
|
|
55
|
+
return { state, acceptedRunId: null };
|
|
56
|
+
}
|
|
57
|
+
if (state.baseline === null || outcome.dagRunId === state.baseline) {
|
|
58
|
+
return { state, acceptedRunId: null };
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
state: { ...state, ownedRunId: outcome.dagRunId },
|
|
62
|
+
acceptedRunId: outcome.dagRunId,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Apply an authoritative ownership answer for the pending selection.
|
|
66
|
+
* `clearSelection` tells the adapter to drop the shared selection and
|
|
67
|
+
* snapshot a fresh baseline; owning marks it as resolver-owned. */
|
|
68
|
+
export function applyOwnership(state, owns) {
|
|
69
|
+
if (owns) {
|
|
70
|
+
return {
|
|
71
|
+
state: { ...state, ownedRunId: state.pendingSelection, pendingSelection: "" },
|
|
72
|
+
clearSelection: false,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
state: { ...state, ownedRunId: "", pendingSelection: "", baseline: null },
|
|
77
|
+
clearSelection: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ownership re-check backoff for an unverified shared DAG-run selection
|
|
3
|
+
* (2026-08-23): a transient candidates-lookup failure must neither leave
|
|
4
|
+
* another task's run displayed indefinitely (waiting for a state change
|
|
5
|
+
* that may never come) nor hammer the API.
|
|
6
|
+
*/
|
|
7
|
+
const OWNERSHIP_RETRY_DELAYS_MS = [
|
|
8
|
+
2_000, 2_000, 4_000, 4_000, 8_000, 8_000, 15_000, 15_000, 30_000, 30_000,
|
|
9
|
+
];
|
|
10
|
+
/** `owns`: true = same task, false = foreign task, null = check unavailable. */
|
|
11
|
+
export function planOwnershipVerification(owns, attempt) {
|
|
12
|
+
if (owns === true)
|
|
13
|
+
return { kind: "own" };
|
|
14
|
+
if (owns === false)
|
|
15
|
+
return { kind: "clear" };
|
|
16
|
+
const delayMs = OWNERSHIP_RETRY_DELAYS_MS[attempt];
|
|
17
|
+
return delayMs === undefined
|
|
18
|
+
? { kind: "give-up" }
|
|
19
|
+
: { kind: "retry", delayMs };
|
|
20
|
+
}
|
|
21
|
+
/** Total bounded re-verification window before giving up (keeps the last
|
|
22
|
+
* selection; the next state change restarts verification). */
|
|
23
|
+
export const OWNERSHIP_RETRY_WINDOW_MS = OWNERSHIP_RETRY_DELAYS_MS.reduce((total, delay) => total + delay, 0);
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure projection from a DAG run detail payload (GET /api/dag-runs/:id) into
|
|
3
|
+
* the structured run progress shown on the wizard RunPanel: node counts,
|
|
4
|
+
* current node + elapsed time, failure pointer, and stall signal. The panel
|
|
5
|
+
* used to show only a raw SSE log tail — node/phase information drowned in
|
|
6
|
+
* log noise during 20-40 minute runs.
|
|
7
|
+
*/
|
|
8
|
+
export function classifyRunProgressNodeStatus(raw) {
|
|
9
|
+
const status = (raw ?? "").trim().toLowerCase();
|
|
10
|
+
if (!status)
|
|
11
|
+
return "pending";
|
|
12
|
+
if (status.includes("running") || status === "in_progress" || status === "active") {
|
|
13
|
+
return "running";
|
|
14
|
+
}
|
|
15
|
+
if (status.includes("fail") || status.includes("error") || status === "blocked") {
|
|
16
|
+
return "failed";
|
|
17
|
+
}
|
|
18
|
+
if (status.includes("finish") ||
|
|
19
|
+
status.includes("success") ||
|
|
20
|
+
status === "done" ||
|
|
21
|
+
status === "ok") {
|
|
22
|
+
return "finished";
|
|
23
|
+
}
|
|
24
|
+
if (status.includes("skip"))
|
|
25
|
+
return "skipped";
|
|
26
|
+
return "pending";
|
|
27
|
+
}
|
|
28
|
+
function toProgressNode(node) {
|
|
29
|
+
const nodeId = String(node.nodeId ?? node.id ?? "").trim();
|
|
30
|
+
return {
|
|
31
|
+
nodeId,
|
|
32
|
+
label: String((node.label ?? node.name ?? nodeId) || "未命名节点"),
|
|
33
|
+
status: String(node.status ?? ""),
|
|
34
|
+
rank: typeof node.rank === "string" ? node.rank : undefined,
|
|
35
|
+
startedAt: typeof node.startedAt === "string" ? node.startedAt : undefined,
|
|
36
|
+
durationMs: typeof node.durationMs === "number" ? node.durationMs : undefined,
|
|
37
|
+
errorPreview: typeof node.errorPreview === "string" ? node.errorPreview : undefined,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function elapsedSince(iso, now) {
|
|
41
|
+
if (!iso)
|
|
42
|
+
return null;
|
|
43
|
+
const started = Date.parse(iso);
|
|
44
|
+
if (Number.isNaN(started))
|
|
45
|
+
return null;
|
|
46
|
+
const elapsed = now - started;
|
|
47
|
+
return elapsed >= 0 ? elapsed : null;
|
|
48
|
+
}
|
|
49
|
+
export function summarizeRunProgress(dag, now = Date.now()) {
|
|
50
|
+
if (!dag) {
|
|
51
|
+
return {
|
|
52
|
+
total: 0,
|
|
53
|
+
finished: 0,
|
|
54
|
+
running: null,
|
|
55
|
+
failed: null,
|
|
56
|
+
terminal: false,
|
|
57
|
+
effectiveStatus: null,
|
|
58
|
+
stalled: false,
|
|
59
|
+
currentNodeElapsedMs: null,
|
|
60
|
+
runElapsedMs: null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const rawNodes = Array.isArray(dag.nodes) ? dag.nodes : [];
|
|
64
|
+
const nodes = rawNodes.map(toProgressNode);
|
|
65
|
+
const classes = nodes.map((node) => classifyRunProgressNodeStatus(node.status));
|
|
66
|
+
const finished = classes.filter((cls) => cls === "finished" || cls === "skipped").length;
|
|
67
|
+
const runningIndex = classes.indexOf("running");
|
|
68
|
+
const running = runningIndex >= 0 ? nodes[runningIndex] : null;
|
|
69
|
+
const failedIndex = classes.indexOf("failed");
|
|
70
|
+
const failed = failedIndex >= 0 ? nodes[failedIndex] : null;
|
|
71
|
+
const effectiveStatus = String(dag.effectiveStatus ??
|
|
72
|
+
dag.status ??
|
|
73
|
+
"").toLowerCase();
|
|
74
|
+
const terminal = TERMINAL_RUN_PROGRESS_STATUSES.has(effectiveStatus);
|
|
75
|
+
const runStartedAt = typeof dag.startedAt === "string" ? dag.startedAt : undefined;
|
|
76
|
+
const runDurationMs = typeof dag.durationMs === "number" ? dag.durationMs : undefined;
|
|
77
|
+
return {
|
|
78
|
+
total: nodes.length,
|
|
79
|
+
finished,
|
|
80
|
+
running,
|
|
81
|
+
failed,
|
|
82
|
+
terminal,
|
|
83
|
+
effectiveStatus: effectiveStatus || null,
|
|
84
|
+
stalled: effectiveStatus === "running-suspected-stall",
|
|
85
|
+
currentNodeElapsedMs: running?.durationMs ?? elapsedSince(running?.startedAt, now),
|
|
86
|
+
runElapsedMs: terminal
|
|
87
|
+
? (runDurationMs ?? elapsedSince(runStartedAt, now))
|
|
88
|
+
: elapsedSince(runStartedAt, now),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const TERMINAL_RUN_PROGRESS_STATUSES = new Set([
|
|
92
|
+
"finished",
|
|
93
|
+
"failed",
|
|
94
|
+
"partial_failed",
|
|
95
|
+
"superseded",
|
|
96
|
+
"abandoned",
|
|
97
|
+
]);
|
|
98
|
+
export function formatDurationMs(ms) {
|
|
99
|
+
if (ms == null || !Number.isFinite(ms) || ms < 0)
|
|
100
|
+
return "—";
|
|
101
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
102
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
103
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
104
|
+
const seconds = totalSeconds % 60;
|
|
105
|
+
if (hours > 0)
|
|
106
|
+
return `${hours} 时 ${minutes} 分`;
|
|
107
|
+
if (minutes > 0)
|
|
108
|
+
return `${minutes} 分 ${seconds} 秒`;
|
|
109
|
+
return `${seconds} 秒`;
|
|
110
|
+
}
|
|
@@ -292,9 +292,13 @@ function assertControllerIdentityUnchangedBeforeSpawn(identity) {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
function controllerIdentityDriftError(message, cause) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
295
|
+
// Preserve the raw drift detail verbatim; the actionable suffix serves
|
|
296
|
+
// every consumer surface — operation errorMessage, SSE events, and agent/
|
|
297
|
+
// API callers that never reach the UI translation layer — with the exact
|
|
298
|
+
// recovery step: the running Console holds a stale controller fingerprint
|
|
299
|
+
// after rebuilds or commits touching fingerprint-scoped paths
|
|
300
|
+
// (package.json, bin/**, dist/**, skills/**).
|
|
301
|
+
return new Error(`controller identity changed before spawn: ${message}. Fix: restart the Console process so it re-resolves the current build, then retry the operation; avoid rebuilding or committing fingerprint-scoped files (package.json, bin/**, dist/**, skills/**) mid-session.`, { cause });
|
|
298
302
|
}
|
|
299
303
|
/**
|
|
300
304
|
* Detect whether loopAgentBin is a Node-like executable and baseArgs
|
|
@@ -129,11 +129,14 @@ export async function resolveRevisionDigestsForTaskSpec(input) {
|
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
131
|
/** Union TaskSpec allowed_paths with workflow-owned write roots. */
|
|
132
|
-
export function expandAllowedPathsForWorkflow(workflow, allowedPaths) {
|
|
132
|
+
export function expandAllowedPathsForWorkflow(workflow, allowedPaths, options) {
|
|
133
133
|
const extras = workflow === "backend-test"
|
|
134
134
|
? ["testcase/**", "docs/test-reports/**"]
|
|
135
135
|
: workflow === "frontend-test"
|
|
136
|
-
? [
|
|
136
|
+
? [
|
|
137
|
+
`${options?.frontendTestRoot ?? "testcase/frontend"}/**`,
|
|
138
|
+
"docs/test-reports/**",
|
|
139
|
+
]
|
|
137
140
|
: [];
|
|
138
141
|
return Array.from(new Set([...allowedPaths, ...extras]));
|
|
139
142
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { buildGlobalSnapshot } from "./read-model.js";
|
|
4
|
+
/**
|
|
5
|
+
* Read-only runtime guard shared by Console maintenance and init-surface
|
|
6
|
+
* writes. Worker activity remains owned by the Observe projection so both
|
|
7
|
+
* callers use one canonical liveness interpretation.
|
|
8
|
+
*/
|
|
9
|
+
export async function readInitRuntimeActivity(repoRoot) {
|
|
10
|
+
const resolvedRoot = path.resolve(repoRoot);
|
|
11
|
+
const activeDagRoot = path.join(resolvedRoot, ".harness", "dag-runs", "active");
|
|
12
|
+
const entries = await readdir(activeDagRoot, { withFileTypes: true }).catch(() => []);
|
|
13
|
+
const activeDagRunIds = entries
|
|
14
|
+
.filter((entry) => entry.isDirectory() && entry.name !== ".gitkeep")
|
|
15
|
+
.map((entry) => entry.name)
|
|
16
|
+
.sort();
|
|
17
|
+
const snapshot = await buildGlobalSnapshot({ repoRoot: resolvedRoot });
|
|
18
|
+
return {
|
|
19
|
+
activeDagRunIds,
|
|
20
|
+
activeWorkerBatches: snapshot.health.worker.activeBatches,
|
|
21
|
+
activeWorkerTasks: snapshot.health.worker.activeTasks,
|
|
22
|
+
...(snapshot.projectionError
|
|
23
|
+
? { workerProjectionError: snapshot.projectionError.message }
|
|
24
|
+
: {}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -1483,6 +1483,9 @@ function mergeDagRun(existing, incoming) {
|
|
|
1483
1483
|
computeDurationMs(startedAt, finishedAt);
|
|
1484
1484
|
return {
|
|
1485
1485
|
dagRunId: incoming.dagRunId,
|
|
1486
|
+
...((incoming.taskId ?? existing.taskId)
|
|
1487
|
+
? { taskId: incoming.taskId ?? existing.taskId }
|
|
1488
|
+
: {}),
|
|
1486
1489
|
status: incoming.status ?? existing.status,
|
|
1487
1490
|
lifecycle: incoming.lifecycle ?? existing.lifecycle,
|
|
1488
1491
|
executionMode: incoming.executionMode ?? existing.executionMode,
|
|
@@ -1914,8 +1917,10 @@ async function parseDagStateFile(statePath, now) {
|
|
|
1914
1917
|
}
|
|
1915
1918
|
: undefined;
|
|
1916
1919
|
const failureCategory = readString(parsed, "failureCategory");
|
|
1920
|
+
const boundTaskId = await readRunSpecTaskBinding(runSpecPath);
|
|
1917
1921
|
return {
|
|
1918
1922
|
dagRunId,
|
|
1923
|
+
...(boundTaskId ? { taskId: boundTaskId } : {}),
|
|
1919
1924
|
status,
|
|
1920
1925
|
...(lifecycle ? { lifecycle } : {}),
|
|
1921
1926
|
...(executionMode ? { executionMode } : {}),
|
|
@@ -2435,6 +2440,23 @@ async function safeReadJson(filePath) {
|
|
|
2435
2440
|
return undefined;
|
|
2436
2441
|
return parsed;
|
|
2437
2442
|
}
|
|
2443
|
+
/** Task binding for a DAG run from run.json — same precedence as
|
|
2444
|
+
* console/resolve-dag-run-for-task (taskContractBinding → sourceBinding →
|
|
2445
|
+
* Hybrid DAG title). */
|
|
2446
|
+
async function readRunSpecTaskBinding(runSpecPath) {
|
|
2447
|
+
const parsed = await safeReadJson(runSpecPath);
|
|
2448
|
+
if (!parsed)
|
|
2449
|
+
return undefined;
|
|
2450
|
+
const binding = parsed.taskContractBinding;
|
|
2451
|
+
const source = parsed.sourceBinding;
|
|
2452
|
+
const fromBinding = (typeof binding?.taskId === "string" && binding.taskId.trim()) ||
|
|
2453
|
+
(typeof source?.taskId === "string" && source.taskId.trim());
|
|
2454
|
+
if (fromBinding)
|
|
2455
|
+
return fromBinding;
|
|
2456
|
+
const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
|
|
2457
|
+
const hybrid = /^Hybrid DAG:\s*(.+)$/i.exec(title);
|
|
2458
|
+
return hybrid?.[1]?.trim() || undefined;
|
|
2459
|
+
}
|
|
2438
2460
|
function safeParseJson(raw) {
|
|
2439
2461
|
try {
|
|
2440
2462
|
const parsed = JSON.parse(raw);
|
|
@@ -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 = [
|
|
@@ -30,9 +31,11 @@ function unavailableBody(dagRunId, nodeId, reason, warnings = []) {
|
|
|
30
31
|
summary: null,
|
|
31
32
|
dependency: null,
|
|
32
33
|
prompt: null,
|
|
34
|
+
promptCandidates: [],
|
|
33
35
|
executorInput: null,
|
|
34
36
|
boundaries: null,
|
|
35
37
|
promptFingerprint: null,
|
|
38
|
+
piExtensionRunFacts: null,
|
|
36
39
|
warnings,
|
|
37
40
|
};
|
|
38
41
|
}
|
|
@@ -137,7 +140,7 @@ function resolveModelHint(spec, task) {
|
|
|
137
140
|
}
|
|
138
141
|
return null;
|
|
139
142
|
}
|
|
140
|
-
function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warnings) {
|
|
143
|
+
function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, piExtensionRunFacts, promptCandidates, warnings) {
|
|
141
144
|
const defaults = spec.defaults && typeof spec.defaults === "object"
|
|
142
145
|
? spec.defaults
|
|
143
146
|
: undefined;
|
|
@@ -216,6 +219,7 @@ function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warni
|
|
|
216
219
|
: null,
|
|
217
220
|
failFast: typeof shellRaw.failFast === "boolean" ? shellRaw.failFast : null,
|
|
218
221
|
gates: shellGateNames(shellRaw),
|
|
222
|
+
handlers: namedShellHandlers(shellRaw),
|
|
219
223
|
}
|
|
220
224
|
: null;
|
|
221
225
|
const staticPreview = staticRaw && typeof staticRaw.resultMarkdown === "string"
|
|
@@ -264,10 +268,12 @@ function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warni
|
|
|
264
268
|
maxBytes: promptBody.maxBytes,
|
|
265
269
|
source: promptSource,
|
|
266
270
|
},
|
|
271
|
+
promptCandidates,
|
|
267
272
|
executorInput: {
|
|
268
273
|
kind,
|
|
269
274
|
skills,
|
|
270
275
|
modelHint: resolveModelHint(spec, task),
|
|
276
|
+
piExtensions: redactStringList(task.piExtensions),
|
|
271
277
|
shell,
|
|
272
278
|
static: staticBlock,
|
|
273
279
|
},
|
|
@@ -284,9 +290,155 @@ function projectTask(dagRunId, nodeId, lifecycle, spec, task, fingerprint, warni
|
|
|
284
290
|
: null,
|
|
285
291
|
},
|
|
286
292
|
promptFingerprint: fingerprint,
|
|
293
|
+
piExtensionRunFacts,
|
|
287
294
|
warnings,
|
|
288
295
|
};
|
|
289
296
|
}
|
|
297
|
+
async function loadFailedPromptCandidates(repoRoot, dagRunId, nodeId, warnings) {
|
|
298
|
+
const stateResolved = resolveDagRunArtifact(repoRoot, dagRunId, ["state.json"]);
|
|
299
|
+
if (!stateResolved.ok)
|
|
300
|
+
return [];
|
|
301
|
+
let state;
|
|
302
|
+
try {
|
|
303
|
+
state = JSON.parse(await readFile(stateResolved.result.absolutePath, "utf-8"));
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
warnings.push("state.json could not be read for failed prompt candidates.");
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
const nodes = state.nodes && typeof state.nodes === "object" && !Array.isArray(state.nodes)
|
|
310
|
+
? state.nodes
|
|
311
|
+
: null;
|
|
312
|
+
const node = nodes?.[nodeId] &&
|
|
313
|
+
typeof nodes[nodeId] === "object" &&
|
|
314
|
+
!Array.isArray(nodes[nodeId])
|
|
315
|
+
? nodes[nodeId]
|
|
316
|
+
: null;
|
|
317
|
+
if (!node)
|
|
318
|
+
return [];
|
|
319
|
+
const failedAttempts = new Set();
|
|
320
|
+
if (Array.isArray(node.attempts)) {
|
|
321
|
+
for (const entry of node.attempts) {
|
|
322
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
323
|
+
continue;
|
|
324
|
+
const record = entry;
|
|
325
|
+
if (record.ok === false &&
|
|
326
|
+
typeof record.attempt === "number" &&
|
|
327
|
+
Number.isSafeInteger(record.attempt) &&
|
|
328
|
+
record.attempt > 0) {
|
|
329
|
+
failedAttempts.add(record.attempt);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (node.status === "ERROR" &&
|
|
334
|
+
typeof node.currentAttempt === "number" &&
|
|
335
|
+
Number.isSafeInteger(node.currentAttempt) &&
|
|
336
|
+
node.currentAttempt > 0) {
|
|
337
|
+
failedAttempts.add(node.currentAttempt);
|
|
338
|
+
}
|
|
339
|
+
const candidates = [];
|
|
340
|
+
for (const attempt of [...failedAttempts].sort((a, b) => a - b)) {
|
|
341
|
+
const fileName = `prompt-candidate-attempt-${attempt}.json`;
|
|
342
|
+
const artifact = path.posix.join(nodeId, fileName);
|
|
343
|
+
const resolved = resolveDagRunArtifact(repoRoot, dagRunId, [nodeId, fileName]);
|
|
344
|
+
if (!resolved.ok) {
|
|
345
|
+
warnings.push(`Failed prompt candidate missing for attempt ${attempt}.`);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const raw = JSON.parse(await readFile(resolved.result.absolutePath, "utf-8"));
|
|
350
|
+
if (raw.schemaVersion !== 1 ||
|
|
351
|
+
raw.attempt !== attempt ||
|
|
352
|
+
typeof raw.failureCategory !== "string" ||
|
|
353
|
+
typeof raw.prompt !== "string" ||
|
|
354
|
+
typeof raw.truncated !== "boolean" ||
|
|
355
|
+
typeof raw.maxBytes !== "number" ||
|
|
356
|
+
!Number.isSafeInteger(raw.maxBytes) ||
|
|
357
|
+
raw.maxBytes < 1 ||
|
|
358
|
+
raw.maxBytes > NODE_INPUT_PREVIEW_MAX_BYTES ||
|
|
359
|
+
typeof raw.sha256 !== "string" ||
|
|
360
|
+
!/^([a-f0-9]{64})$/.test(raw.sha256)) {
|
|
361
|
+
throw new Error("invalid candidate schema");
|
|
362
|
+
}
|
|
363
|
+
const prompt = truncatedField(raw.prompt, raw.maxBytes);
|
|
364
|
+
candidates.push({
|
|
365
|
+
attempt,
|
|
366
|
+
failureCategory: redactSecrets(raw.failureCategory),
|
|
367
|
+
prompt: prompt.text,
|
|
368
|
+
truncated: raw.truncated || prompt.truncated,
|
|
369
|
+
maxBytes: raw.maxBytes,
|
|
370
|
+
sha256Prefix: raw.sha256.slice(0, 12),
|
|
371
|
+
artifact,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
warnings.push(`Failed prompt candidate is invalid for attempt ${attempt}.`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return candidates;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Load run-time Pi extension facts from <node>/pi-extensions.json (plan
|
|
382
|
+
* 2026-08-22 R2). Returns null when the node declares no piExtensions
|
|
383
|
+
* (no expected artifact). Never throws: unreadable/corrupt artifacts degrade
|
|
384
|
+
* to parseError stubs so the node-input endpoint stays 200.
|
|
385
|
+
*/
|
|
386
|
+
async function loadPiExtensionRunFacts(repoRoot, dagRunId, nodeId, declared, warnings) {
|
|
387
|
+
if (declared.length === 0)
|
|
388
|
+
return null;
|
|
389
|
+
const artifact = path.posix.join(nodeId, "pi-extensions.json");
|
|
390
|
+
const resolved = resolveDagRunArtifact(repoRoot, dagRunId, [
|
|
391
|
+
nodeId,
|
|
392
|
+
"pi-extensions.json",
|
|
393
|
+
]);
|
|
394
|
+
if (!resolved.ok) {
|
|
395
|
+
if (resolved.reason === "ambiguous") {
|
|
396
|
+
warnings.push("Ambiguous pi-extensions.json lifecycle copies.");
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
artifact,
|
|
400
|
+
exists: false,
|
|
401
|
+
requested: [...declared],
|
|
402
|
+
resolved: [],
|
|
403
|
+
missing: [],
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const empty = {
|
|
407
|
+
artifact,
|
|
408
|
+
exists: false,
|
|
409
|
+
requested: [...declared],
|
|
410
|
+
resolved: [],
|
|
411
|
+
missing: [],
|
|
412
|
+
};
|
|
413
|
+
try {
|
|
414
|
+
const raw = JSON.parse(await readFile(resolved.result.absolutePath, "utf-8"));
|
|
415
|
+
const resolvedList = Array.isArray(raw.resolved) ? raw.resolved : [];
|
|
416
|
+
const missingList = Array.isArray(raw.missing) ? raw.missing : [];
|
|
417
|
+
const projected = resolvedList
|
|
418
|
+
.filter((entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry))
|
|
419
|
+
.map((entry) => ({
|
|
420
|
+
id: redactSecrets(String(entry.id ?? "")),
|
|
421
|
+
packageName: redactSecrets(String(entry.packageName ?? "")),
|
|
422
|
+
version: redactSecrets(String(entry.version ?? "")),
|
|
423
|
+
}));
|
|
424
|
+
const missingProjected = missingList
|
|
425
|
+
.filter((entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry))
|
|
426
|
+
.map((entry) => ({
|
|
427
|
+
id: redactSecrets(String(entry.id ?? "")),
|
|
428
|
+
reason: redactSecrets(String(entry.reason ?? "")),
|
|
429
|
+
}));
|
|
430
|
+
return {
|
|
431
|
+
artifact,
|
|
432
|
+
exists: true,
|
|
433
|
+
requested: redactStringList(raw.requested ?? declared),
|
|
434
|
+
resolved: projected,
|
|
435
|
+
missing: missingProjected,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
return { ...empty, parseError: "read-failed" };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
290
442
|
async function loadPromptFingerprint(repoRoot, dagRunId, nodeId, warnings) {
|
|
291
443
|
const resolved = resolveDagNodePromptRedacted(repoRoot, dagRunId, nodeId);
|
|
292
444
|
if (!resolved.ok) {
|
|
@@ -415,8 +567,13 @@ export async function buildDagNodeInput(repoRoot, dagRunId, nodeId) {
|
|
|
415
567
|
}
|
|
416
568
|
const warnings = [];
|
|
417
569
|
const fingerprint = await loadPromptFingerprint(repoRoot, dagRunId, nodeId, warnings);
|
|
570
|
+
const declaredPiExtensions = Array.isArray(task.piExtensions)
|
|
571
|
+
? task.piExtensions.filter((item) => typeof item === "string")
|
|
572
|
+
: [];
|
|
573
|
+
const piExtensionRunFacts = await loadPiExtensionRunFacts(repoRoot, dagRunId, nodeId, declaredPiExtensions, warnings);
|
|
574
|
+
const promptCandidates = await loadFailedPromptCandidates(repoRoot, dagRunId, nodeId, warnings);
|
|
418
575
|
// Reject unknown nested leakage by projecting only through allowlist helpers.
|
|
419
|
-
const body = projectTask(dagRunId, nodeId, runResolved.result.lifecycle, spec, task, fingerprint, warnings);
|
|
576
|
+
const body = projectTask(dagRunId, nodeId, runResolved.result.lifecycle, spec, task, fingerprint, piExtensionRunFacts, promptCandidates, warnings);
|
|
420
577
|
// Defense-in-depth: ensure raw task never sneaks in.
|
|
421
578
|
if ("task" in body) {
|
|
422
579
|
return {
|