@tea-agent/loop-agent 0.44.0-next.1 → 0.44.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +379 -416
- package/dist/application/evaluation/budget.js +21 -0
- package/dist/application/evaluation/corpus-hash.js +10 -15
- package/dist/application/evaluation/corpus.js +2 -1
- package/dist/application/evaluation/frontend-browser-acceptance.js +77 -0
- package/dist/application/evaluation/frontend-corpus-browser-acceptance.js +175 -0
- package/dist/application/evaluation/frontend-gateway-evidence.js +226 -0
- package/dist/application/evaluation/frontend-pair-registration.js +143 -0
- package/dist/application/evaluation/frontend-paired-summary.js +150 -0
- package/dist/application/evaluation/frontend-run-observation.js +144 -0
- package/dist/application/evaluation/frontend-shared-evidence.js +105 -0
- package/dist/application/evaluation/types.js +45 -12
- package/dist/application/task-lifecycle/observe.js +26 -1
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +11 -0
- package/dist/cli/program.js +4 -0
- package/dist/commands/task-advance.js +3 -5
- package/dist/commands/task-source-prepare.js +13 -1
- package/dist/executors/dag-pi/guards.js +4 -0
- package/dist/executors/dag-pi/model-config.js +30 -0
- package/dist/executors/dag-pi/plan/fact-adoption.js +55 -0
- package/dist/{workflows/dag/frontend-plan-completeness.js → executors/dag-pi/plan/facts.js} +22 -28
- package/dist/executors/dag-pi/plan/finalize-tools.js +259 -0
- package/dist/executors/dag-pi/plan/ledger-contract.js +55 -0
- package/dist/executors/dag-pi/plan/record-tools.js +861 -0
- package/dist/executors/dag-pi/plan/schema.js +129 -0
- package/dist/executors/dag-pi/plan/session.js +1 -0
- package/dist/executors/dag-pi/plan/tool-helpers.js +16 -0
- package/dist/executors/dag-pi/sessions/index.js +6 -0
- package/dist/executors/dag-pi/sessions/plan-batches.js +280 -0
- package/dist/executors/dag-pi/sessions/plan-prompts.js +367 -0
- package/dist/executors/dag-pi/sessions/scout-parallel.js +197 -0
- package/dist/executors/dag-pi/sessions/segmented-plan.js +1184 -0
- package/dist/executors/dag-pi/sessions/writer-evidence.js +60 -0
- package/dist/executors/dag-pi/tools/contract-tools.js +515 -0
- package/dist/executors/dag-pi/tools/design-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/plan-decision-tools.js +529 -0
- package/dist/executors/dag-pi/tools/review-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/scout-evidence-tools.js +270 -0
- package/dist/executors/dag-pi-executor.js +733 -5782
- package/dist/executors/pi-executor.js +6 -1
- package/dist/executors/pi-sdk-executor.js +112 -19
- package/dist/executors/shell-executor.js +88 -12
- package/dist/executors/shell-presets.js +1 -1
- package/dist/executors/shell-write-guard.js +26 -3
- package/dist/infrastructure/console/operation-store.js +48 -14
- package/dist/infrastructure/evaluation/corpus-store.js +37 -12
- package/dist/shared/file-identity.js +23 -0
- package/dist/shared/git-workspace-status.js +482 -0
- package/dist/shared/operator/capabilities.js +10 -5
- package/dist/shared/path-safety.js +2 -2
- package/dist/shared/wizard-local-telemetry.js +51 -0
- package/dist/task/config-types.js +22 -8
- package/dist/task/contract/project.js +13 -0
- package/dist/task/contract/schema.js +2 -7
- package/dist/task/frontend-preflight.js +1 -1
- package/dist/task/source-prepare/parse-intent.js +124 -5
- package/dist/task/source-prepare/prepare.js +6 -1
- package/dist/task/source-prepare/semantic-intake.js +18 -3
- package/dist/task/source-prepare/source-execution.js +65 -0
- package/dist/task/source-prepare/source-fidelity-pi.js +22 -9
- package/dist/task/source-prepare/source-provider-budget.js +290 -0
- package/dist/worker/console/chat/assistant-content.js +5 -34
- package/dist/worker/console/chat/chat-event-store.js +62 -10
- package/dist/worker/console/chat/context-insights.js +723 -0
- package/dist/worker/console/chat/conversation-nodes.js +383 -0
- package/dist/worker/console/chat/db-connections.js +854 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +1 -1
- package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +655 -0
- package/dist/worker/console/chat/pi-runtime.js +445 -36
- package/dist/worker/console/chat/provider-error.js +0 -12
- package/dist/worker/console/chat/repo-browser.js +52 -4
- package/dist/worker/console/chat/routes.js +577 -72
- package/dist/worker/console/chat/sdd-data-alignment.js +8 -31
- package/dist/worker/console/chat/session-store.js +147 -19
- package/dist/worker/console/chat/shortcuts.js +7 -0
- package/dist/worker/console/chat/timeline-message-order.js +22 -0
- package/dist/worker/console/chat/tools.js +34 -0
- package/dist/worker/console/chat/turn-process.js +237 -66
- package/dist/worker/console/chat/user-turn-outline.js +19 -0
- package/dist/worker/console/console-update-runtime.js +2 -2
- package/dist/worker/console/dag-confirmation.js +3 -0
- package/dist/worker/console/dag-execution-receipt.js +3 -0
- package/dist/worker/console/operator-actions.js +32 -3
- package/dist/worker/console/operator-user-error.js +28 -1
- package/dist/worker/console/pi-readiness.js +4 -4
- package/dist/worker/console/prd-intake-bridge.js +5 -12
- package/dist/worker/console/recovery-cta.js +14 -14
- package/dist/worker/console/server.js +5 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-8-j6y-sd.js → abnfDiagram-N423BO3Z-r1NAPU_5.js} +1 -1
- package/dist/worker/console/static/assets/{arc-eoQiMvuk.js → arc-dT5T4sVL.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4A3uMcI.js → architectureDiagram-T3A2C74G-CP_cOq46.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D4zD2F-Q.js → blockDiagram-VBNYF7ZC-sToEZxEf.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-j1RkJziL.js → c4Diagram-5PPSVZJV-DSxwwi2m.js} +1 -1
- package/dist/worker/console/static/assets/channel-CyBY_yGk.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-YBHmhik1.js → chunk-2GRJ4B5K-B9fh0IkF.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-COfWyo9P.js → chunk-2Q5K7J3B-CAUNl-A6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-I99OUkHY.js → chunk-5RXB4S5H-BomeqbNX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XKxoJNJ4.js → chunk-5VM5RSS4-BNAuTOLE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DLT_cYx1.js → chunk-6Q2QTUOP-DfzjRD5y.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CxcKZ9mV.js → chunk-GF5L2VYU-C3uE-yc2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-V1EqXdjY.js → chunk-JWPE2WC7-C3sQMMYr.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DebTtdFp.js → chunk-KBJHAD2P-D_AtEYQL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-B-ivNRDf.js → chunk-RYQCIY6F-DW4TwhKv.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DC_Ds11b.js → chunk-XXDRQBXY-CTQDmJcp.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-sWEqKwIb.js → cose-bilkent-JH36ORCC-iMqPuBsL.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXa_dcu4.js → cynefin-VYW2F7L2-DktIuDWs.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-C-Mle84F.js → cynefinDiagram-MW4NZA55-BgVnjyEs.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CXPDBITe.js → dagre-VZM6K2ZE-CR25E4bu.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CC-WJQfa.js → diagram-7IWD3JNH-CPM2h5HY.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CDAV5Vs4.js → diagram-B4RE2ZJO-DqF0x2g1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CmFiAcNz.js → diagram-LBJQPF4R-G8BYp2eb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DPBZHuyn.js → diagram-Q27KOJAE-BbC4kT_D.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-jUlm_Ds3.js → diagram-UB23O5K3-CKZwZ-G5.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DWhQ3mfY.js → ebnfDiagram-BXEA7PRR-kJDmheia.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Tr2gMqet.js → erDiagram-JOGREHBK-DFTVHyxB.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DJjVQHPA.js → flowDiagram-UKHOOZJN-BqdN5_os.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-D74dQ4u0.js → ganttDiagram-PKOTCBZU-SYDjVakh.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js → gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js} +1 -1
- package/dist/worker/console/static/assets/index-B28Onmfy.css +1 -0
- package/dist/worker/console/static/assets/index-CTMZsDmh.js +486 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-ILCbxJyb.js → infoDiagram-6WML65LV-DIeAmJ45.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DeuWBQ89.js → ishikawaDiagram-WSZJBQD7-oikqUAlo.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CF5ih8Fk.js → journeyDiagram-NVQOT4AX-mKEF_xYT.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C7yOSuRO.js → kanban-definition-27J2QSJJ-BdWJHPnN.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BDZ9riWi.js → linear-DGRlieMH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-7pKqYtpZ.js → mermaid.core-4P9yPC9c.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-LeJDybSU.js → mindmap-definition-FAOFIHXS-5PTSkos6.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BJvT3pMD.js → pegDiagram-VL7TDLO6-DONIFpG5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-_rGqpMan.js → pieDiagram-7S7Q4E2Y-D_Z4S70M.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DPcSv5aA.js → quadrantDiagram-CIZ2JOQS-CAsjh8iY.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Drxx4hkJ.js → railroadDiagram-AXF67PYL-Cx4Dd6ac.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BCTUdU4z.js → requirementDiagram-LRYGKXZP-cQb7mwyH.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6wzbZmB.js → sankeyDiagram-W5VNT64P-Cd-DhUdf.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BGt8d3QQ.js → sequenceDiagram-SI44F4Z6-AlKYRgIk.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-7nlhJKo7.js → sizeCapture-X5ZJPWSS-CUy_ET4e.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Cv2stqMA.js → stateDiagram-OKZ733FA-CTuVe6Sa.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-qDAo4Yc1.js → swimlanes-SLNWSIFB-ChFLJb-v.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKbDNKTr.js → timeline-definition-Z64GVDOM-DZZeiAPr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DI-9EHic.js → vennDiagram-T6HMQDX7-t4O91cZU.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BrzzRDpX.js → wardleyDiagram-T6FBY63Y-CV5qGHZu.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BclH5hGh.js → xychartDiagram-ELKLHX3M-DyXbytUI.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -1
- package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
- package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +186 -5
- package/dist/worker/console/static-src/operator-chat/chat-stream-continuity.js +101 -0
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +18 -3
- package/dist/worker/console/static-src/operator-chat/context-insights-store.js +64 -0
- package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
- package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +17 -3
- package/dist/worker/console/static-src/operator-chat/process-label.js +27 -5
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +16 -7
- package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +4 -1
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
- package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +211 -40
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +124 -27
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +88 -20
- package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
- package/dist/worker/console/workspace-context.js +2 -0
- package/dist/worker/console/workspace-initialization.js +3 -3
- package/dist/worker/observability/read-model.js +23 -2
- package/dist/worker/observe/node-context-insights.js +479 -0
- package/dist/worker/observe/static/console-theme.css +174 -0
- package/dist/worker/observe/static/console-theme.js +36 -0
- package/dist/worker/observe/static/operator-chrome.css +85 -0
- package/dist/worker/observe/static/operator-chrome.js +51 -0
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
- package/dist/worker/scheduler/scheduled-goal-evidence.js +66 -3
- package/dist/worker/scheduler/scheduled-goal-store.js +10 -0
- package/dist/workflows/dag/budget-enforcement.js +31 -2
- package/dist/workflows/dag/checkpoint.js +686 -0
- package/dist/workflows/dag/frontend-closeout.js +3 -1
- package/dist/workflows/dag/frontend-contract-facts.js +11 -8
- package/dist/workflows/dag/frontend-design-policy.js +6 -6
- package/dist/workflows/dag/frontend-durable-tools.js +90 -70
- package/dist/workflows/dag/frontend-implementation-contract.js +146 -57
- package/dist/workflows/dag/frontend-plan-canary.js +66 -16
- package/dist/workflows/dag/frontend-plan-decision-contract.js +13 -21
- package/dist/workflows/dag/frontend-plan-progress.js +92 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -5
- package/dist/workflows/dag/frontend-recovery-run.js +51 -46
- package/dist/workflows/dag/frontend-repair-assertions.js +36 -0
- package/dist/workflows/dag/frontend-review-context.js +29 -1
- package/dist/workflows/dag/frontend-review-scopes.js +92 -2
- package/dist/workflows/dag/frontend-risk.js +8 -3
- package/dist/workflows/dag/frontend-root-observation.js +261 -0
- package/dist/workflows/dag/frontend-session-budget.js +52 -39
- package/dist/workflows/dag/frontend-session-context.js +10 -0
- package/dist/workflows/dag/frontend-test-execution-evidence.js +206 -48
- package/dist/workflows/dag/frontend-typed-event-store.js +11 -1
- package/dist/workflows/dag/frontend-verification-trace.js +6 -0
- package/dist/workflows/dag/frontend-writer-admission.js +3 -1
- package/dist/workflows/dag/hybrid/sources.js +1812 -0
- package/dist/workflows/dag/hybrid/templates/backend-test.js +1807 -0
- package/dist/workflows/dag/hybrid/templates/frontend-test.js +702 -0
- package/dist/workflows/dag/hybrid/templates/frontend.js +1529 -0
- package/dist/workflows/dag/hybrid/templates/index.js +8 -0
- package/dist/workflows/dag/hybrid/templates/kg-bootstrap.js +449 -0
- package/dist/workflows/dag/hybrid/templates/knowledge-sync.js +495 -0
- package/dist/workflows/dag/hybrid/templates/shared.js +101 -0
- package/dist/workflows/dag/hybrid/templates/standard.js +265 -0
- package/dist/workflows/dag/hybrid/types.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +57 -7106
- package/dist/workflows/dag/node-execution.js +11 -11
- package/dist/workflows/dag/output-protocol.js +1 -0
- package/dist/workflows/dag/rerun-feedback.js +35 -4
- package/dist/workflows/dag/rerun-task.js +62 -66
- package/dist/workflows/dag/runner.js +38 -1159
- package/dist/workflows/dag/terminal-status.js +502 -0
- package/dist/workflows/dag/types.js +15 -8
- package/dist/workflows/loop/benchmark.js +11 -8
- package/docs/README.md +1 -1
- package/docs/architecture/dag-execution.md +1 -1
- package/docs/architecture/runtime-boundaries.md +1 -1
- package/docs/governance/harness-methodology-verification.md +11 -0
- package/docs/templates/backend-test-dag.json +5 -4
- package/docs/templates/frontend-implementation-contract.schema.json +31 -11
- package/package.json +6 -4
- package/skills/frontend-design-review/SKILL.md +8 -11
- package/skills/frontend-design-review/references/review-checklist.md +3 -4
- package/skills/frontend-review/SKILL.md +5 -1
- package/skills/loop-agent/references/command-reference.md +8 -0
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +0 -1
- package/dist/worker/console/static/assets/index-Cdkvw_H6.js +0 -469
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +0 -8
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/** DAG Pi 分段会话的提示词投影:覆盖/UX/全局阶段 prompt 与账本上下文压缩,禁止反向依赖执行器入口。 */
|
|
2
|
+
import { projectFrontendInputScope } from "../../../workflows/dag/frontend-input-projection.js";
|
|
3
|
+
import { isRecordObject } from "../guards.js";
|
|
4
|
+
import { committedFactFromPlanRecord, planFactScopeIntersects, planFactStringList } from "../plan/facts.js";
|
|
5
|
+
export function compactPromptString(value, _maxChars) {
|
|
6
|
+
return typeof value === "string" && value.trim().length ? value.trim() : undefined;
|
|
7
|
+
}
|
|
8
|
+
export function compactPromptStringArray(value, _maxEntries = 12, maxChars = 180) {
|
|
9
|
+
if (!Array.isArray(value))
|
|
10
|
+
return [];
|
|
11
|
+
return value
|
|
12
|
+
.map((item) => compactPromptString(item, maxChars))
|
|
13
|
+
.filter((item) => item !== undefined);
|
|
14
|
+
}
|
|
15
|
+
export function compactFrontendPlanPromptForRequirementSlice(basePrompt, requirementIds, options = {}) {
|
|
16
|
+
const slice = [...new Set(requirementIds.filter((id) => id.trim().length > 0))];
|
|
17
|
+
if (slice.length === 0)
|
|
18
|
+
return basePrompt;
|
|
19
|
+
const planInputPattern = /<frontend_plan_input>[\s\S]*?<\/frontend_plan_input>/;
|
|
20
|
+
const planInputMatch = planInputPattern.exec(basePrompt);
|
|
21
|
+
if (!planInputMatch)
|
|
22
|
+
return basePrompt;
|
|
23
|
+
const blockLines = planInputMatch[0].split(/\r?\n/);
|
|
24
|
+
let payload;
|
|
25
|
+
for (const line of blockLines) {
|
|
26
|
+
try {
|
|
27
|
+
const parsed = JSON.parse(line);
|
|
28
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
29
|
+
payload = parsed;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// The surrounding block contains prose; only its JSON line is data.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (!payload || !Array.isArray(payload.requirements))
|
|
38
|
+
throw Error("FRONTEND_INPUT_INVALID: plan inventory is not parseable");
|
|
39
|
+
const requirementsById = new Map();
|
|
40
|
+
for (const value of payload.requirements) {
|
|
41
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
42
|
+
continue;
|
|
43
|
+
const requirement = value;
|
|
44
|
+
if (typeof requirement.id === "string") {
|
|
45
|
+
requirementsById.set(requirement.id, requirement);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const requirements = slice.map((id) => requirementsById.get(id));
|
|
49
|
+
if (requirements.some((requirement) => requirement === undefined)) {
|
|
50
|
+
throw Error(`FRONTEND_INPUT_SCOPE_MISSING: ${slice.filter(id => !requirementsById.has(id)).join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
const compactRequirements = requirements.map((requirement) => ({
|
|
53
|
+
id: compactPromptString(requirement.id, 80),
|
|
54
|
+
...(options.includeRequirementText !== false && compactPromptString(requirement.text, 240)
|
|
55
|
+
? { text: compactPromptString(requirement.text, 240) }
|
|
56
|
+
: {}),
|
|
57
|
+
sourceFragmentIds: compactPromptStringArray(requirement.sourceFragmentIds, 20, 80),
|
|
58
|
+
}));
|
|
59
|
+
const sliceSet = new Set(slice);
|
|
60
|
+
const compactVerificationTargets = options.includeVerificationTargets === false
|
|
61
|
+
? []
|
|
62
|
+
: Array.isArray(payload.verificationTargets)
|
|
63
|
+
? payload.verificationTargets.flatMap((value) => {
|
|
64
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
65
|
+
return [];
|
|
66
|
+
const target = value;
|
|
67
|
+
if (options.verificationTargetIds && !options.verificationTargetIds.includes(String(target.id)))
|
|
68
|
+
return [];
|
|
69
|
+
const targetRequirementIds = planFactStringList(target.requirementIds);
|
|
70
|
+
const relatedRequirementIds = targetRequirementIds.filter((id) => sliceSet.has(id));
|
|
71
|
+
if (relatedRequirementIds.length === 0)
|
|
72
|
+
return [];
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
...(typeof target.id === "string"
|
|
76
|
+
? { id: target.id }
|
|
77
|
+
: {}),
|
|
78
|
+
...(typeof target.commandId === "string"
|
|
79
|
+
? { commandId: target.commandId }
|
|
80
|
+
: {}),
|
|
81
|
+
...(compactPromptString(target.commandLabel, 180)
|
|
82
|
+
? { commandLabel: compactPromptString(target.commandLabel, 180) }
|
|
83
|
+
: {}),
|
|
84
|
+
...(typeof target.file === "string"
|
|
85
|
+
? { file: target.file }
|
|
86
|
+
: {}),
|
|
87
|
+
requirementIds: relatedRequirementIds,
|
|
88
|
+
uiStates: planFactStringList(target.uiStates),
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
})
|
|
92
|
+
: [];
|
|
93
|
+
const compactTargetSurface = Array.isArray(payload.targetSurface)
|
|
94
|
+
? payload.targetSurface.flatMap((value) => {
|
|
95
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
96
|
+
return [];
|
|
97
|
+
const surface = value;
|
|
98
|
+
return [
|
|
99
|
+
{
|
|
100
|
+
...(compactPromptString(surface.completeness, 32)
|
|
101
|
+
? { completeness: compactPromptString(surface.completeness, 32) }
|
|
102
|
+
: {}),
|
|
103
|
+
...(compactPromptString(surface.entrypoint, 180)
|
|
104
|
+
? { entrypoint: compactPromptString(surface.entrypoint, 180) }
|
|
105
|
+
: {}),
|
|
106
|
+
...(compactPromptString(surface.routeOrMount, 180)
|
|
107
|
+
? { routeOrMount: compactPromptString(surface.routeOrMount, 180) }
|
|
108
|
+
: {}),
|
|
109
|
+
implementationPaths: compactPromptStringArray(surface.implementationPaths),
|
|
110
|
+
proposedPaths: compactPromptStringArray(surface.proposedPaths),
|
|
111
|
+
testPaths: compactPromptStringArray(surface.testPaths),
|
|
112
|
+
...(compactPromptString(surface.dataSource, 180)
|
|
113
|
+
? { dataSource: compactPromptString(surface.dataSource, 180) }
|
|
114
|
+
: {}),
|
|
115
|
+
allowedPathConflicts: compactPromptStringArray(surface.allowedPathConflicts),
|
|
116
|
+
unresolvedPaths: compactPromptStringArray(surface.unresolvedPaths),
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
})
|
|
120
|
+
: [];
|
|
121
|
+
const compactDesignEvidence = options.includeDesignEvidence === true &&
|
|
122
|
+
Array.isArray(payload.designEvidence)
|
|
123
|
+
? payload.designEvidence.flatMap((value) => {
|
|
124
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
125
|
+
return [];
|
|
126
|
+
const evidence = value;
|
|
127
|
+
return [{
|
|
128
|
+
...(compactPromptString(evidence.source, 180)
|
|
129
|
+
? { source: compactPromptString(evidence.source, 180) }
|
|
130
|
+
: {}),
|
|
131
|
+
paths: compactPromptStringArray(evidence.paths, 12, 180),
|
|
132
|
+
conflicts: compactPromptStringArray(evidence.conflicts, 24, 180),
|
|
133
|
+
}];
|
|
134
|
+
})
|
|
135
|
+
: [];
|
|
136
|
+
const compactDeclaredUiStates = Array.isArray(payload.declaredUiStates)
|
|
137
|
+
? payload.declaredUiStates.flatMap((value) => {
|
|
138
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
139
|
+
return [];
|
|
140
|
+
const state = value;
|
|
141
|
+
const id = typeof state.id === "string" ? state.id : undefined;
|
|
142
|
+
if (!id)
|
|
143
|
+
return [];
|
|
144
|
+
return [{
|
|
145
|
+
id,
|
|
146
|
+
...(compactPromptString(state.trigger, 180)
|
|
147
|
+
? { trigger: compactPromptString(state.trigger, 180) }
|
|
148
|
+
: {}),
|
|
149
|
+
...(compactPromptString(state.observableOutcome, 240)
|
|
150
|
+
? { observableOutcome: compactPromptString(state.observableOutcome, 240) }
|
|
151
|
+
: {}),
|
|
152
|
+
}];
|
|
153
|
+
})
|
|
154
|
+
: [];
|
|
155
|
+
const committedUx = payload.committedUx &&
|
|
156
|
+
typeof payload.committedUx === "object" &&
|
|
157
|
+
!Array.isArray(payload.committedUx)
|
|
158
|
+
? payload.committedUx
|
|
159
|
+
: undefined;
|
|
160
|
+
const compactPayload = {
|
|
161
|
+
inputManifest: { ...projectFrontendInputScope({ ...payload, requirements: [...requirementsById.values()] }, slice).inputManifest, semantics: options.includeRequirementText === false ? "navigation-only" : "full" },
|
|
162
|
+
constraints: payload.constraints,
|
|
163
|
+
executionGroups: Array.isArray(payload.executionGroups) ? payload.executionGroups.filter(g => isRecordObject(g) && Array.isArray(g.requirementIds) && g.requirementIds.some(id => slice.includes(String(id)))) : [],
|
|
164
|
+
requirements: compactRequirements,
|
|
165
|
+
requiredDeliverables: Array.isArray(payload.requiredDeliverables) ? payload.requiredDeliverables : [],
|
|
166
|
+
...(compactTargetSurface.length > 0
|
|
167
|
+
? { targetSurface: compactTargetSurface }
|
|
168
|
+
: {}),
|
|
169
|
+
...(compactVerificationTargets.length > 0
|
|
170
|
+
? { verificationTargets: compactVerificationTargets }
|
|
171
|
+
: {}),
|
|
172
|
+
...(compactDesignEvidence.length > 0
|
|
173
|
+
? { designEvidence: compactDesignEvidence }
|
|
174
|
+
: {}),
|
|
175
|
+
...(compactDeclaredUiStates.length > 0
|
|
176
|
+
? { declaredUiStates: compactDeclaredUiStates }
|
|
177
|
+
: {}),
|
|
178
|
+
...(committedUx
|
|
179
|
+
? {
|
|
180
|
+
committedUx: {
|
|
181
|
+
uiStateNames: compactPromptStringArray(committedUx.uiStateNames, 40, 100),
|
|
182
|
+
interactionNames: compactPromptStringArray(committedUx.interactionNames, 60, 120),
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
: {}),
|
|
186
|
+
};
|
|
187
|
+
const compactBlock = [
|
|
188
|
+
"<frontend_plan_input>",
|
|
189
|
+
`Committed Contract/Scout facts for ONLY the current requirement slice (${slice.join(", ")}); do not infer or record requirements outside this slice.`,
|
|
190
|
+
JSON.stringify(compactPayload),
|
|
191
|
+
"Do not read upstream artifacts, task sources, or repository files. If this slice cannot support a decision, record a genuine evidence gap.",
|
|
192
|
+
"</frontend_plan_input>",
|
|
193
|
+
].join("\n");
|
|
194
|
+
let compactPrompt = basePrompt.replace(planInputPattern, compactBlock);
|
|
195
|
+
compactPrompt = compactPrompt.replace(/<upstream_context>[\s\S]*?<\/upstream_context>/, "<upstream_context>\n(full upstream prose omitted; use only the slice-specific typed facts above)\n</upstream_context>");
|
|
196
|
+
compactPrompt = compactPrompt.replace(/Cover each frozen requirement ID exactly once:[^\n]*/, `Cover ONLY the current requirement slice: ${slice.join(", ")}. Record each slice requirement exactly once.`);
|
|
197
|
+
const checklistPattern = /<plan_review_checklist>([\s\S]*?)<\/plan_review_checklist>/;
|
|
198
|
+
const checklistMatch = checklistPattern.exec(compactPrompt);
|
|
199
|
+
if (checklistMatch) {
|
|
200
|
+
if (options.includeChecklist === false) {
|
|
201
|
+
compactPrompt = compactPrompt.replace(checklistPattern, "");
|
|
202
|
+
return compactPrompt;
|
|
203
|
+
}
|
|
204
|
+
const checklist = checklistMatch[1]
|
|
205
|
+
.split(/\r?\n/)
|
|
206
|
+
.filter((line) => {
|
|
207
|
+
const requirementLine = /^Requirements requiring behavioural coverage:\s*(.*)$/.exec(line.trim());
|
|
208
|
+
if (requirementLine) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const citationId = /^([^\s]+)\s+\+/.exec(line.trim())?.[1];
|
|
212
|
+
return citationId === undefined || sliceSet.has(citationId);
|
|
213
|
+
})
|
|
214
|
+
.concat([`Requirements requiring behavioural coverage: ${slice.join(", ") || "(none)"}`])
|
|
215
|
+
.join("\n");
|
|
216
|
+
compactPrompt = compactPrompt.replace(checklistPattern, `<plan_review_checklist>${checklist}</plan_review_checklist>`);
|
|
217
|
+
}
|
|
218
|
+
return compactPrompt;
|
|
219
|
+
}
|
|
220
|
+
export function compactFrontendPlanLedgerContext(input) {
|
|
221
|
+
const slice = new Set(input.requirementIds);
|
|
222
|
+
const allowed = new Set(input.kinds);
|
|
223
|
+
const scopedKinds = new Set(input.scopedKinds ?? []);
|
|
224
|
+
const compactFacts = [];
|
|
225
|
+
for (const value of input.committedFacts) {
|
|
226
|
+
const fact = committedFactFromPlanRecord(value);
|
|
227
|
+
if (!fact || fact.origin !== "plan" || typeof fact.kind !== "string" || !allowed.has(fact.kind))
|
|
228
|
+
continue;
|
|
229
|
+
if (scopedKinds.has(fact.kind) && !planFactScopeIntersects(fact, slice)) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const entry = fact.entry && typeof fact.entry === "object" && !Array.isArray(fact.entry)
|
|
233
|
+
? fact.entry
|
|
234
|
+
: undefined;
|
|
235
|
+
if (fact.kind === "plan-requirement") {
|
|
236
|
+
if (!entry || typeof entry.id !== "string" || !slice.has(entry.id))
|
|
237
|
+
continue;
|
|
238
|
+
compactFacts.push({
|
|
239
|
+
kind: fact.kind,
|
|
240
|
+
entry: {
|
|
241
|
+
id: entry.id,
|
|
242
|
+
implementationTargets: planFactStringList(entry.implementationTargets).slice(0, 12),
|
|
243
|
+
verificationTargetIds: planFactStringList(entry.verificationTargetIds).slice(0, 12),
|
|
244
|
+
...(compactPromptString(entry.expectedOutcome, 240)
|
|
245
|
+
? { expectedOutcome: compactPromptString(entry.expectedOutcome, 240) }
|
|
246
|
+
: {}),
|
|
247
|
+
...(entry.evidenceGap && typeof entry.evidenceGap === "object"
|
|
248
|
+
? { evidenceGap: entry.evidenceGap }
|
|
249
|
+
: {}),
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (fact.kind === "plan-verification-target") {
|
|
255
|
+
if (!entry)
|
|
256
|
+
continue;
|
|
257
|
+
// Scope against the complete canonical binding before compacting.
|
|
258
|
+
// A shared target may bind a late requirement beyond the preview cap.
|
|
259
|
+
const requirementIds = planFactStringList(entry.requirementIds).filter((id) => slice.has(id));
|
|
260
|
+
if (requirementIds.length === 0)
|
|
261
|
+
continue;
|
|
262
|
+
compactFacts.push({
|
|
263
|
+
kind: fact.kind,
|
|
264
|
+
entry: {
|
|
265
|
+
id: entry.id,
|
|
266
|
+
commandId: entry.commandId,
|
|
267
|
+
file: entry.file,
|
|
268
|
+
requirementIds,
|
|
269
|
+
uiStates: planFactStringList(entry.uiStates).slice(0, 12),
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (fact.kind === "component-choice") {
|
|
275
|
+
const choices = Array.isArray(fact.uiComponentChoices)
|
|
276
|
+
? fact.uiComponentChoices.flatMap((choice) => {
|
|
277
|
+
if (!choice || typeof choice !== "object" || Array.isArray(choice))
|
|
278
|
+
return [];
|
|
279
|
+
const item = choice;
|
|
280
|
+
return [{
|
|
281
|
+
purpose: compactPromptString(item.purpose, 120),
|
|
282
|
+
component: compactPromptString(item.component, 120),
|
|
283
|
+
decision: compactPromptString(item.decision, 40),
|
|
284
|
+
covers: compactPromptStringArray(item.covers, 40, 120),
|
|
285
|
+
evidencePath: compactPromptString(item.evidencePath, 180),
|
|
286
|
+
}];
|
|
287
|
+
})
|
|
288
|
+
: [];
|
|
289
|
+
if (choices.length > 0)
|
|
290
|
+
compactFacts.push({ kind: fact.kind, uiComponentChoices: choices });
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (fact.kind === "state-registry") {
|
|
294
|
+
compactFacts.push({
|
|
295
|
+
kind: fact.kind,
|
|
296
|
+
uiStateNames: planFactStringList(fact.uiStateNames).slice(0, 40),
|
|
297
|
+
interactionNames: planFactStringList(fact.interactionNames).slice(0, 60),
|
|
298
|
+
});
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (fact.kind === "state-flow") {
|
|
302
|
+
compactFacts.push({
|
|
303
|
+
kind: fact.kind,
|
|
304
|
+
uiStates: Array.isArray(fact.uiStates) ? fact.uiStates : [],
|
|
305
|
+
interactions: Array.isArray(fact.interactions) ? fact.interactions : [],
|
|
306
|
+
removeUiStateNames: compactPromptStringArray(fact.removeUiStateNames, 24, 100),
|
|
307
|
+
removeInteractionNames: compactPromptStringArray(fact.removeInteractionNames, 24, 100),
|
|
308
|
+
});
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (fact.kind === "data-flow") {
|
|
312
|
+
compactFacts.push({
|
|
313
|
+
kind: fact.kind,
|
|
314
|
+
interactions: compactPromptStringArray(fact.interactions, 32, 120),
|
|
315
|
+
endpoints: compactPromptStringArray(fact.endpoints, 32, 180),
|
|
316
|
+
});
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (fact.kind === "mock-api") {
|
|
320
|
+
const mockApi = fact.mockApi && typeof fact.mockApi === "object" && !Array.isArray(fact.mockApi)
|
|
321
|
+
? fact.mockApi
|
|
322
|
+
: {};
|
|
323
|
+
compactFacts.push({
|
|
324
|
+
kind: fact.kind,
|
|
325
|
+
mockApi: {
|
|
326
|
+
strategy: compactPromptString(mockApi.strategy, 40),
|
|
327
|
+
activation: compactPromptString(mockApi.activation, 180),
|
|
328
|
+
endpoints: Array.isArray(mockApi.endpoints) ? mockApi.endpoints : [],
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (fact.kind === "design-deviation") {
|
|
334
|
+
compactFacts.push({ kind: fact.kind, conflicts: compactPromptStringArray(fact.conflicts, 32, 180) });
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (fact.kind === "dependency") {
|
|
338
|
+
compactFacts.push({ kind: fact.kind, policy: compactPromptString(fact.policy, 300) });
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (fact.kind === "target-surface") {
|
|
342
|
+
compactFacts.push({ kind: fact.kind, routes: compactPromptStringArray(fact.routes, 24, 180), implementationPaths: compactPromptStringArray(fact.implementationPaths, 24, 180), proposedPaths: compactPromptStringArray(fact.proposedPaths, 24, 180), testPaths: compactPromptStringArray(fact.testPaths, 24, 180) });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (compactFacts.length === 0)
|
|
346
|
+
return "";
|
|
347
|
+
const priorityFacts = compactFacts.filter((fact) => fact.kind === "plan-requirement" || fact.kind === "plan-verification-target");
|
|
348
|
+
const otherFacts = compactFacts.filter((fact) => fact.kind !== "plan-requirement" && fact.kind !== "plan-verification-target");
|
|
349
|
+
let previewBytes = 0;
|
|
350
|
+
const boundedFacts = [
|
|
351
|
+
...priorityFacts.slice(0, 64),
|
|
352
|
+
...otherFacts.slice(-32),
|
|
353
|
+
].slice(0, 96).filter(fact => {
|
|
354
|
+
const bytes = Buffer.byteLength(JSON.stringify(fact));
|
|
355
|
+
if (previewBytes + bytes > 24_000)
|
|
356
|
+
return false;
|
|
357
|
+
previewBytes += bytes;
|
|
358
|
+
return true;
|
|
359
|
+
});
|
|
360
|
+
return [
|
|
361
|
+
"<frontend_plan_ledger>",
|
|
362
|
+
"Preview of committed plan facts; not a replacement payload. Fields and lists may be omitted or shortened. Use read_plan_facts with kind, entryId/eventId and optional field to retrieve complete values in bounded pages before corrections; preserve all other bindings. Absence from this preview is not absence from the ledger.",
|
|
363
|
+
JSON.stringify({ complete: false, matchingFacts: compactFacts.length, omittedFacts: compactFacts.length - boundedFacts.length }),
|
|
364
|
+
JSON.stringify(boundedFacts),
|
|
365
|
+
"</frontend_plan_ledger>",
|
|
366
|
+
].join("\n");
|
|
367
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/** DAG Pi scout 并行分片会话:结果聚合、串行结果合并与 completedShards 缓存。 */
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { observeFrontendSession } from "../../../workflows/dag/frontend-session-budget.js";
|
|
4
|
+
import { createFrontendScoutEvidenceTools } from "../tools/scout-evidence-tools.js";
|
|
5
|
+
export function aggregateParallelPiResults(results) {
|
|
6
|
+
const first = results[0];
|
|
7
|
+
const failed = results.find((result) => !result.ok);
|
|
8
|
+
const representative = failed ?? first;
|
|
9
|
+
return {
|
|
10
|
+
...representative,
|
|
11
|
+
ok: failed === undefined,
|
|
12
|
+
failureCategory: failed?.failureCategory ?? "success",
|
|
13
|
+
durationMs: Math.max(...results.map((result) => result.durationMs), 0),
|
|
14
|
+
exitCode: failed ? failed.exitCode : 0,
|
|
15
|
+
stderr: results.map((result) => result.stderr).filter(Boolean).join("\n"),
|
|
16
|
+
tokensUsed: results.reduce((total, result) => total + result.tokensUsed, 0),
|
|
17
|
+
parsedEvents: results.reduce((total, result) => total + result.parsedEvents, 0),
|
|
18
|
+
attemptedModels: [
|
|
19
|
+
...new Set(results.flatMap((result) => result.attemptedModels)),
|
|
20
|
+
],
|
|
21
|
+
fallbackUsed: results.some((result) => result.fallbackUsed),
|
|
22
|
+
timedOut: results.some((result) => result.timedOut),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function combineSequentialPiResults(first, second) {
|
|
26
|
+
return {
|
|
27
|
+
...second,
|
|
28
|
+
durationMs: first.durationMs + second.durationMs,
|
|
29
|
+
stderr: [first.stderr, second.stderr].filter(Boolean).join("\n"),
|
|
30
|
+
tokensUsed: first.tokensUsed + second.tokensUsed,
|
|
31
|
+
parsedEvents: first.parsedEvents + second.parsedEvents,
|
|
32
|
+
attemptedModels: [
|
|
33
|
+
...new Set([...first.attemptedModels, ...second.attemptedModels]),
|
|
34
|
+
],
|
|
35
|
+
fallbackUsed: first.fallbackUsed || second.fallbackUsed,
|
|
36
|
+
timedOut: first.timedOut || second.timedOut,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export async function runFrontendScoutParallelSessions(input) {
|
|
40
|
+
const [{ createTypedEventStore }] = await Promise.all([
|
|
41
|
+
import("../../../workflows/dag/frontend-typed-event-store.js"),
|
|
42
|
+
]);
|
|
43
|
+
const shards = [
|
|
44
|
+
{
|
|
45
|
+
id: "surface",
|
|
46
|
+
toolName: "record_target_surface",
|
|
47
|
+
instruction: [
|
|
48
|
+
"PARALLEL SCOUT SHARD — target surface only.",
|
|
49
|
+
"Inspect routes, entrypoints, implementation ownership, data source, and existing test entrypoints. Inspect project source/config first; do not recursively explore dependency internals merely to reconfirm standard test-runner behavior. If a concrete required capability cannot be established from project evidence, report the precise gap.",
|
|
50
|
+
"Call record_target_surface exactly once with the complete runtime-evidenced surface. Do not call record_design_evidence.",
|
|
51
|
+
].join(" "),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "design",
|
|
55
|
+
toolName: "record_design_evidence",
|
|
56
|
+
instruction: [
|
|
57
|
+
"PARALLEL SCOUT SHARD — design evidence only.",
|
|
58
|
+
"Inspect project-owned frontend framework, styling/theme conventions, reusable components, and relevant design/spec files. The surface shard owns test-runner/environment discovery; do not duplicate its Vitest/happy-dom investigation. Stop discovery once the design evidence is sufficient and commit it.",
|
|
59
|
+
"Call record_design_evidence for the evidence you actually read. Do not call record_target_surface.",
|
|
60
|
+
].join(" "),
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
const outcomes = await Promise.all(shards.map(async (shard) => {
|
|
64
|
+
const cacheKey = JSON.stringify([input.runDir, input.nodeId, shard.id, input.sessionOptions.modelConfig, input.observation?.model, input.observation?.sourceDigest, input.sourceDeclaredPaths]);
|
|
65
|
+
const cached = input.completedShards?.get(cacheKey);
|
|
66
|
+
if (cached)
|
|
67
|
+
return { shard, facts: cached.facts, result: { ...cached.result, durationMs: 0, tokensUsed: 0, parsedEvents: 0, attemptedModels: [], fallbackUsed: false } };
|
|
68
|
+
let shardTools;
|
|
69
|
+
let shardResult;
|
|
70
|
+
try {
|
|
71
|
+
const store = createTypedEventStore();
|
|
72
|
+
const shardNodeId = `${input.nodeId}/parallel/${shard.id}`;
|
|
73
|
+
shardTools = await createFrontendScoutEvidenceTools({
|
|
74
|
+
attemptId: `${input.attemptId}:parallel:${shard.id}`,
|
|
75
|
+
store,
|
|
76
|
+
runDir: input.runDir,
|
|
77
|
+
nodeId: shardNodeId,
|
|
78
|
+
workspaceRoot: input.workspaceRoot,
|
|
79
|
+
sourceDeclaredPaths: input.sourceDeclaredPaths,
|
|
80
|
+
});
|
|
81
|
+
const customTools = shardTools.customTools.filter((tool) => typeof tool === "object" &&
|
|
82
|
+
tool !== null &&
|
|
83
|
+
tool.name === shard.toolName);
|
|
84
|
+
const sessionOptions = {
|
|
85
|
+
...input.sessionOptions,
|
|
86
|
+
sessionEventsPath: path.join(input.runDir, shardNodeId, "session-events.jsonl"),
|
|
87
|
+
};
|
|
88
|
+
const prompt = `${input.basePrompt}\n\n${shard.instruction}`;
|
|
89
|
+
const tools = [...customTools, ...(input.readBudgetTools ?? [])];
|
|
90
|
+
shardResult = await observeFrontendSession({
|
|
91
|
+
...input.observation,
|
|
92
|
+
phase: `scout/parallel/${shard.id}`,
|
|
93
|
+
scopeIds: [shard.id],
|
|
94
|
+
prompt,
|
|
95
|
+
userMessage: sessionOptions.userMessage,
|
|
96
|
+
customTools: tools,
|
|
97
|
+
artifactPath: input.observation?.artifactPath ? `${input.observation.artifactPath}-${shard.id}.json` : undefined,
|
|
98
|
+
committedCount: () => shardTools?.committedFacts().length ?? 0,
|
|
99
|
+
durableCommittedCount: () => shardTools?.committedFacts().length ?? 0,
|
|
100
|
+
}, observer => input.piStepFn({
|
|
101
|
+
...sessionOptions,
|
|
102
|
+
onAttemptObservation: observer,
|
|
103
|
+
prompt,
|
|
104
|
+
writerToolPolicy: {
|
|
105
|
+
requireSdk: true,
|
|
106
|
+
customTools: tools,
|
|
107
|
+
},
|
|
108
|
+
}));
|
|
109
|
+
await shardTools.flush();
|
|
110
|
+
const facts = shardTools.committedFacts();
|
|
111
|
+
const expectedKind = shard.id === "design" ? "design-evidence" : "target-surface";
|
|
112
|
+
if (shardResult.ok && facts.some(record => { const fact = record.fact; return fact.kind === expectedKind && (shard.id !== "surface" || fact.completeness === "complete"); })) {
|
|
113
|
+
input.completedShards?.set(cacheKey, { result: structuredClone(shardResult), facts: structuredClone(facts) });
|
|
114
|
+
}
|
|
115
|
+
return { shard, result: shardResult, tools: shardTools, facts };
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const crashMessage = `frontend scout parallel shard ${shard.id} crashed: ${error instanceof Error ? error.message : String(error)}`;
|
|
119
|
+
return {
|
|
120
|
+
shard,
|
|
121
|
+
tools: shardTools,
|
|
122
|
+
result: shardResult
|
|
123
|
+
? {
|
|
124
|
+
...shardResult,
|
|
125
|
+
ok: false,
|
|
126
|
+
failureCategory: shardResult.ok
|
|
127
|
+
? "invalid-output"
|
|
128
|
+
: shardResult.failureCategory,
|
|
129
|
+
stderr: [shardResult.stderr, crashMessage]
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.join("\n"),
|
|
132
|
+
}
|
|
133
|
+
: {
|
|
134
|
+
ok: false,
|
|
135
|
+
assistantText: "",
|
|
136
|
+
command: [],
|
|
137
|
+
durationMs: 0,
|
|
138
|
+
exitCode: null,
|
|
139
|
+
failureCategory: "tool-policy",
|
|
140
|
+
modelDisplay: "unknown",
|
|
141
|
+
parsedEvents: 0,
|
|
142
|
+
stderr: crashMessage,
|
|
143
|
+
stdout: "",
|
|
144
|
+
timedOut: false,
|
|
145
|
+
attemptedModels: [],
|
|
146
|
+
fallbackUsed: false,
|
|
147
|
+
tokensUsed: 0,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
const ordered = [...outcomes].sort((left, right) => left.shard.id.localeCompare(right.shard.id));
|
|
153
|
+
try {
|
|
154
|
+
for (const outcome of ordered) {
|
|
155
|
+
if (outcome.result.ok) {
|
|
156
|
+
await input.mainTools.adoptCommittedFacts(outcome.facts ?? outcome.tools?.committedFacts() ?? []);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const aggregate = aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
162
|
+
return {
|
|
163
|
+
...aggregate,
|
|
164
|
+
ok: false,
|
|
165
|
+
failureCategory: "invalid-output",
|
|
166
|
+
stderr: [
|
|
167
|
+
aggregate.stderr,
|
|
168
|
+
`frontend scout parallel fact merge failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
169
|
+
]
|
|
170
|
+
.filter(Boolean)
|
|
171
|
+
.join("\n"),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const failed = ordered.find((outcome) => !outcome.result.ok);
|
|
175
|
+
if (failed) {
|
|
176
|
+
const aggregate = aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
177
|
+
return {
|
|
178
|
+
...aggregate,
|
|
179
|
+
ok: false,
|
|
180
|
+
stderr: `${aggregate.stderr}\nfrontend scout parallel shard failed: ${failed.shard.id}`.trim(),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const committedKinds = new Set(ordered.flatMap((outcome) => (outcome.facts ?? outcome.tools?.committedFacts() ?? [])
|
|
184
|
+
.map((record) => record.fact?.kind)
|
|
185
|
+
.filter((kind) => typeof kind === "string")));
|
|
186
|
+
const missing = ["target-surface", "design-evidence"].filter((kind) => !committedKinds.has(kind));
|
|
187
|
+
if (missing.length > 0) {
|
|
188
|
+
const fallback = aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
189
|
+
return {
|
|
190
|
+
...fallback,
|
|
191
|
+
ok: false,
|
|
192
|
+
failureCategory: "invalid-output",
|
|
193
|
+
stderr: `${fallback.stderr}\nfrontend scout parallel shards committed no ${missing.join(" or ")} fact`.trim(),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
197
|
+
}
|