@tea-agent/loop-agent 0.42.0-next.9 → 0.42.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/CHANGELOG.md +111 -45
- package/dist/application/dag/run-dag.js +8 -2
- package/dist/application/evaluation/budget.js +19 -1
- package/dist/application/task-lifecycle/advance.js +26 -8
- package/dist/application/task-lifecycle/observe.js +43 -29
- package/dist/application/task-lifecycle/plan-transitions.js +5 -4
- package/dist/build-stamp.json +3 -3
- package/dist/cli/program.js +1 -1
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/task-source-prepare.js +3 -1
- package/dist/executors/dag-pi-executor.js +2584 -600
- package/dist/executors/pi-executor.js +22 -1
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/pi-sdk-executor.js +140 -39
- package/dist/executors/shell-executor.js +135 -55
- package/dist/shared/dag-failure-category.js +6 -0
- package/dist/shared/frontend-execution-policy.js +23 -0
- package/dist/task/config-types.js +4 -0
- package/dist/task/contract/apply.js +36 -2
- package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
- package/dist/task/source-prepare/ledger-review.js +6 -9
- package/dist/task/source-prepare/parse-intent.js +7 -0
- package/dist/task/source-prepare/semantic-intake.js +16 -26
- package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
- package/dist/task/source-references.js +48 -23
- package/dist/worker/console/chat/assistant-content.js +23 -2
- package/dist/worker/console/chat/browser-policy.js +143 -0
- package/dist/worker/console/chat/browser-routes.js +148 -0
- package/dist/worker/console/chat/chat-event-store.js +4 -2
- package/dist/worker/console/chat/explore-tools.js +13 -0
- package/dist/worker/console/chat/pi-runtime.js +173 -94
- package/dist/worker/console/chat/resource-loader.js +4 -1
- package/dist/worker/console/chat/routes.js +208 -66
- package/dist/worker/console/chat/scm-routes.js +217 -0
- package/dist/worker/console/chat/scm-service.js +283 -0
- package/dist/worker/console/chat/scm-tools.js +111 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +78 -11
- package/dist/worker/console/chat/session-catalog.js +32 -0
- package/dist/worker/console/chat/session-mode-view.js +48 -0
- package/dist/worker/console/chat/session-mode.js +218 -0
- package/dist/worker/console/chat/session-store.js +27 -7
- package/dist/worker/console/chat/shortcuts.js +6 -0
- package/dist/worker/console/chat/subagents/agent-tool.js +62 -0
- package/dist/worker/console/chat/subagents/explore-agent.js +169 -0
- package/dist/worker/console/chat/subagents/index.js +5 -0
- package/dist/worker/console/chat/subagents/orchestrator.js +273 -0
- package/dist/worker/console/chat/subagents/tool-policy.js +53 -0
- package/dist/worker/console/chat/subagents/types.js +13 -0
- package/dist/worker/console/chat/terminal-routes.js +216 -0
- package/dist/worker/console/chat/terminal-sessions.js +284 -0
- package/dist/worker/console/chat/terminal-tools.js +199 -0
- package/dist/worker/console/chat/tool-preview.js +21 -0
- package/dist/worker/console/chat/tools.js +13 -1
- package/dist/worker/console/chat/turn-process.js +1 -0
- package/dist/worker/console/interview/tools.js +1 -0
- package/dist/worker/console/server.js +2 -28
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-C9eI7nEo.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-VJWsxWhB.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-BGcyiMSo.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-BScnFwyi.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Bl9_BsLi.js} +1 -1
- package/dist/worker/console/static/assets/channel-B6sQYuOE.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-F1uKxiUt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-rSRMynVu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-DAsJm1WD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-DQIdVI0a.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-4A_8rJ-Q.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-BHnT-vjh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-Rq7QKBkn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BXh-AI2u.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-BblcLy8m.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-zHZmVnB6.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Z6s5QoeI.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Z6s5QoeI.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-Cmwz0Wi4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-Dq76MHQU.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-zPahV1fM.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-DQJ3xPc_.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BwX7u6fv.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CodnDCco.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-gAjtWkxG.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-CGCqz0cS.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-RHwYEywd.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-B6seb_p_.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-CmwCKYe8.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-BfNsaZ0V.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-zmYgi_Z3.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-BDEzzSKF.js} +1 -1
- package/dist/worker/console/static/assets/index-DgenUAfc.js +468 -0
- package/dist/worker/console/static/assets/index-aF4u-Y__.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-DeSz8l42.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-B1LV29aH.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-CCw4W2oU.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BozaMoRJ.js} +1 -1
- package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-G1JR40SX.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-CCgPF0oA.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-CEqnCHnD.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-DpmQPKuI.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-BP501AxR.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-D1dBhl_A.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-DgZDS5pY.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-BzB3pyIq.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-B9ZprFFP.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CC2-_XrA.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-UVtAUqgB.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-CWcS5JYG.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BooX8u1Q.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-C4p-dn06.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D64_eqqE.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-Dnz52OHX.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-CzTE0Nfu.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-NAR9cjQQ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-DVMk8413.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/chat-view-types.js +1 -1
- package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +142 -7
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +44 -0
- package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
- package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +33 -18
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +48 -16
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +170 -7
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -7
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
- package/dist/worker/console/workspace-context.js +15 -1
- package/dist/worker/observe/node-transparency.js +81 -72
- package/dist/worker/observe/routes.js +20 -1
- package/dist/worker/observe/static/constants.js +22 -22
- package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
- package/dist/worker/observe/static/dag-history-labels.js +1 -0
- package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
- package/dist/worker/observe/static/dag-inspector-humanize.js +342 -0
- package/dist/worker/observe/static/dag-node-purpose.js +10 -10
- package/dist/worker/observe/static/dom.js +20 -1
- package/dist/worker/observe/static/format-pool.d.ts +2 -0
- package/dist/worker/observe/static/format-pool.js +6 -0
- package/dist/worker/observe/static/format.js +7 -0
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/inspect-workspace.js +34 -7
- package/dist/worker/observe/static/inspector-submission.js +32 -0
- package/dist/worker/observe/static/kpi.js +1 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +7 -5
- package/dist/worker/observe/static/router.js +13 -0
- package/dist/worker/observe/static/run-processing.js +2 -0
- package/dist/worker/observe/static/shell-chrome.js +36 -3
- package/dist/worker/observe/static/state.js +35 -2
- package/dist/worker/observe/static/styles.css +431 -39
- package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
- package/dist/worker/observe/static/task-failure-labels.js +67 -0
- package/dist/worker/observe/static/task-history.js +12 -0
- package/dist/worker/observe/static/views/batch.js +6 -13
- package/dist/worker/observe/static/views/dag-graph.js +51 -4
- package/dist/worker/observe/static/views/dag-inspector.js +1069 -426
- package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
- package/dist/worker/observe/static/views/dag.d.ts +6 -0
- package/dist/worker/observe/static/views/dag.js +64 -22
- package/dist/worker/observe/static/views/dags.js +2 -0
- package/dist/worker/observe/static/views/dashboard.js +21 -12
- package/dist/worker/observe/static/views/failures.js +21 -11
- package/dist/worker/observe/static/views/feature.js +11 -29
- package/dist/worker/observe/static/views/pool.js +37 -28
- package/dist/worker/observe/static/views/run.js +48 -5
- package/dist/worker/observe/static/views/session-timeline.js +194 -243
- package/dist/worker/observe/static/views/task.js +81 -62
- package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
- package/dist/workflows/dag/budget-enforcement.js +53 -3
- package/dist/workflows/dag/dag-retry-schema.js +3 -0
- package/dist/workflows/dag/frontend-capacity.js +9 -0
- package/dist/workflows/dag/frontend-contract-facts.js +130 -0
- package/dist/workflows/dag/frontend-design-policy.js +103 -17
- package/dist/workflows/dag/frontend-durable-tools.js +193 -0
- package/dist/workflows/dag/frontend-execution-groups.js +24 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +423 -41
- package/dist/workflows/dag/frontend-input-projection.js +76 -0
- package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
- package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
- package/dist/workflows/dag/frontend-plan-render.js +21 -3
- package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
- package/dist/workflows/dag/frontend-recovery-controller.js +32 -8
- package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +4 -1
- package/dist/workflows/dag/frontend-recovery-run.js +146 -16
- package/dist/workflows/dag/frontend-review-scopes.js +139 -0
- package/dist/workflows/dag/frontend-risk.js +2 -0
- package/dist/workflows/dag/frontend-session-budget.js +249 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +37 -3
- package/dist/workflows/dag/frontend-shape-facts.js +12 -2
- package/dist/workflows/dag/frontend-shape.js +46 -7
- package/dist/workflows/dag/frontend-test-execution-evidence.js +49 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +14 -0
- package/dist/workflows/dag/frontend-verification-trace.js +38 -4
- package/dist/workflows/dag/frontend-writer-admission.js +2 -2
- package/dist/workflows/dag/init-hybrid.js +70 -35
- package/dist/workflows/dag/node-execution.js +225 -226
- package/dist/workflows/dag/prompt.js +4 -0
- package/dist/workflows/dag/recovery-lease.js +106 -16
- package/dist/workflows/dag/rerun-feedback.js +1 -0
- package/dist/workflows/dag/rerun-plan.js +96 -4
- package/dist/workflows/dag/rerun-run.js +28 -6
- package/dist/workflows/dag/rerun-task.js +221 -18
- package/dist/workflows/dag/retry-policy.js +27 -10
- package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
- package/dist/workflows/dag/runner.js +246 -19
- package/dist/workflows/dag/structured-output-repair.js +4 -1
- package/dist/workflows/dag/types.js +10 -3
- package/dist/workflows/dag/validate.js +10 -8
- package/dist/workflows/dag/workspace-checkpoint.js +66 -0
- package/docs/operations/README.md +2 -0
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +6 -4
- package/docs/templates/frontend-design-contract.md +4 -4
- package/docs/templates/frontend-implementation-contract.schema.json +68 -4
- package/docs/templates/frontend-implementation-dag.json +5 -5
- package/harness.json +1 -1
- package/package.json +8 -6
- package/skills/frontend-contract/SKILL.md +2 -1
- package/skills/frontend-contract/references/contract-protocol.md +19 -3
- package/skills/frontend-design-review/SKILL.md +12 -11
- package/skills/frontend-plan/SKILL.md +22 -2
- package/skills/frontend-plan/references/decision-contract.md +114 -5
- package/skills/frontend-plan/references/design-decisions.md +32 -0
- package/skills/frontend-review/SKILL.md +10 -11
- package/skills/frontend-scout/references/scout-evidence.md +4 -0
- package/dist/worker/console/static/assets/channel-DsZxrgqe.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/index-D9Sc0f0y.js +0 -449
- package/dist/worker/console/static/assets/index-DDQc5a50.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +0 -8
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { inspectRouteStates } from "../state.js";
|
|
2
|
+
import { mergeTaskHistoryPage } from "../task-history.js";
|
|
1
3
|
/** Observe UI view module (R5). */
|
|
2
4
|
import {
|
|
3
5
|
UI_TEXT,
|
|
@@ -16,7 +18,7 @@ import {
|
|
|
16
18
|
DAG_EFFECTIVE_STATUS_LABELS,
|
|
17
19
|
STATUS_LABELS,
|
|
18
20
|
} from "../constants.js";
|
|
19
|
-
import { clearNode, el, metaGrid, buildTable } from "../dom.js";
|
|
21
|
+
import { clearNode, el, metaGrid, buildTable, reconcileKeyed } from "../dom.js";
|
|
20
22
|
import {
|
|
21
23
|
badge,
|
|
22
24
|
badgeClass,
|
|
@@ -45,6 +47,7 @@ import {
|
|
|
45
47
|
import {
|
|
46
48
|
fetchJson,
|
|
47
49
|
fetchJsonResult,
|
|
50
|
+
summarizeFetchError,
|
|
48
51
|
artifactUrl,
|
|
49
52
|
parseArtifactPreviewResponse,
|
|
50
53
|
} from "../api.js";
|
|
@@ -66,7 +69,7 @@ import {
|
|
|
66
69
|
renderViewState,
|
|
67
70
|
mountViewState,
|
|
68
71
|
} from "../state.js";
|
|
69
|
-
import { uiState } from "../state.js";
|
|
72
|
+
import { uiState, taskHistoryStates, workspaceScope } from "../state.js";
|
|
70
73
|
import { sortPoolTasks } from "../format-pool.js";
|
|
71
74
|
import {
|
|
72
75
|
dagMetaVisibility,
|
|
@@ -75,7 +78,7 @@ import {
|
|
|
75
78
|
findDagForRun,
|
|
76
79
|
} from "../dag-model.js";
|
|
77
80
|
import { layoutDag } from "../dag-layout.js";
|
|
78
|
-
import { showView, setBreadcrumb, updateHeaderRefresh, mountRelationBar } from "../shell-chrome.js";
|
|
81
|
+
import { showView, setBreadcrumb, updateHeaderRefresh, mountRelationBar, bindManualViewRefresh } from "../shell-chrome.js";
|
|
79
82
|
import { artifactLink } from "../kpi.js";
|
|
80
83
|
import { findBatchRunIdForTask, taskRunHistoryRow } from "../run-processing.js";
|
|
81
84
|
|
|
@@ -91,7 +94,7 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
91
94
|
showView("task");
|
|
92
95
|
setBreadcrumb([
|
|
93
96
|
{ label: UI_TEXT.dashboard, href: "#/" },
|
|
94
|
-
{ label: "资源池", href: "/#/inspect/pool" },
|
|
97
|
+
{ label: "资源池", href: inspectRouteStates.get("return-list") ?? "/#/inspect/pool" },
|
|
95
98
|
{ label: displayLabel },
|
|
96
99
|
]);
|
|
97
100
|
|
|
@@ -103,13 +106,29 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
103
106
|
const ledgerEl = document.getElementById("task-ledger");
|
|
104
107
|
if (!metaEl || !linksEl || !evidenceEl || !runsEl) return false;
|
|
105
108
|
if (titleEl) titleEl.textContent = displayLabel;
|
|
106
|
-
|
|
109
|
+
const identity = `${workspaceScope() ?? ""}:${featureId ?? ""}:${taskId}`;
|
|
110
|
+
const requestGeneration = uiState.pollingGeneration;
|
|
111
|
+
if (metaEl.dataset.taskIdentity !== identity) {
|
|
112
|
+
metaEl.dataset.taskIdentity = identity;
|
|
113
|
+
mountViewState(metaEl, "loading", "加载 Task…");
|
|
114
|
+
clearNode(runsEl);
|
|
115
|
+
for (const element of [linksEl, evidenceEl, ledgerEl]) if (element) clearNode(element);
|
|
116
|
+
}
|
|
117
|
+
if (!taskHistoryStates.has(identity)) taskHistoryStates.set(identity, { runs: [], nextBefore: null, loadedOlder: false, loadingOlder: false });
|
|
118
|
+
const historyState = taskHistoryStates.get(identity);
|
|
107
119
|
|
|
108
120
|
const [taskResult, historyResult, snapshot] = await Promise.all([
|
|
109
121
|
fetchJsonResult(taskApi),
|
|
110
122
|
fetchJsonResult(`${taskApi}/runs?limit=20`),
|
|
111
123
|
fetchJson("/api/snapshot"),
|
|
112
124
|
]);
|
|
125
|
+
if (requestGeneration !== uiState.pollingGeneration || metaEl.dataset.taskIdentity !== identity) return false;
|
|
126
|
+
if (!taskResult.ok && ![404, 409].includes(taskResult.status)) {
|
|
127
|
+
let error = metaEl.querySelector(".task-refresh-error");
|
|
128
|
+
if (!error) { error = el("p", "task-refresh-error node-input-error"); metaEl.appendChild(error); }
|
|
129
|
+
error.textContent = `更新失败,保留上次内容。${summarizeFetchError(taskResult)}`;
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
113
132
|
const task = taskResult.ok ? taskResult.body : null;
|
|
114
133
|
const history = historyResult.ok ? historyResult.body : null;
|
|
115
134
|
if (snapshot) {
|
|
@@ -120,7 +139,6 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
120
139
|
clearNode(metaEl);
|
|
121
140
|
clearNode(linksEl);
|
|
122
141
|
clearNode(evidenceEl);
|
|
123
|
-
clearNode(runsEl);
|
|
124
142
|
|
|
125
143
|
if (!task) {
|
|
126
144
|
if (taskResult.status === 409) {
|
|
@@ -234,27 +252,9 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
234
252
|
evidenceEl.appendChild(evidenceLinks);
|
|
235
253
|
}
|
|
236
254
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
mountViewState(runsEl, "empty", "暂无 ledger 运行记录。");
|
|
241
|
-
} else {
|
|
242
|
-
const rows = runs.map(taskRunHistoryRow);
|
|
243
|
-
runsEl.appendChild(
|
|
244
|
-
buildTable(
|
|
245
|
-
["worker run", "状态", "时间", "batch", "retryOf", "失败分类"],
|
|
246
|
-
rows,
|
|
247
|
-
),
|
|
248
|
-
);
|
|
249
|
-
if (history?.nextBefore) {
|
|
250
|
-
const more = el("button", "link-button", "加载更早的运行");
|
|
251
|
-
more.type = "button";
|
|
252
|
-
more.addEventListener("click", () => {
|
|
253
|
-
void loadOlderTaskRuns(featureId, taskId, history.nextBefore, runsEl);
|
|
254
|
-
});
|
|
255
|
-
runsEl.appendChild(more);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
255
|
+
if (history) mergeTaskHistoryPage(historyState, history);
|
|
256
|
+
else historyState.error = summarizeFetchError(historyResult);
|
|
257
|
+
renderTaskHistory(runsEl, historyState, featureId, taskId, identity);
|
|
258
258
|
|
|
259
259
|
const active = ["running", "pending", "blocked", "stale"].includes(
|
|
260
260
|
(task.status ?? "").toLowerCase(),
|
|
@@ -262,6 +262,8 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
262
262
|
if (ledgerEl) {
|
|
263
263
|
void renderTaskLedger(ledgerEl, taskId);
|
|
264
264
|
}
|
|
265
|
+
updateHeaderRefresh(snapshot?.generatedAt ?? uiState.lastSnapshot?.generatedAt, { mode: active ? "auto" : "ended" });
|
|
266
|
+
bindManualViewRefresh("task", () => renderTaskDetail(featureId, taskId));
|
|
265
267
|
return active;
|
|
266
268
|
}
|
|
267
269
|
|
|
@@ -315,42 +317,59 @@ async function renderTaskLedger(ledgerEl, taskId) {
|
|
|
315
317
|
ledgerEl.appendChild(pre);
|
|
316
318
|
}
|
|
317
319
|
|
|
318
|
-
|
|
319
|
-
if (runsEl
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
function renderTaskHistory(runsEl, state, featureId, taskId, identity) {
|
|
321
|
+
if (runsEl.dataset.historyIdentity !== identity) { clearNode(runsEl); runsEl.dataset.historyIdentity = identity; }
|
|
322
|
+
let table = runsEl.querySelector("table");
|
|
323
|
+
if (!table) {
|
|
324
|
+
runsEl.appendChild(el("h3", "section-heading", "运行历史"));
|
|
325
|
+
table = buildTable(["worker run", "状态", "时间", "batch", "retryOf", "失败分类"], []);
|
|
326
|
+
runsEl.appendChild(table);
|
|
327
|
+
const status = el("p", "muted task-history-status");
|
|
328
|
+
status.setAttribute("role", "status");
|
|
329
|
+
runsEl.appendChild(status);
|
|
330
|
+
const more = el("button", "link-button", "加载更早的运行");
|
|
331
|
+
more.type = "button";
|
|
332
|
+
more.addEventListener("click", () => { void loadOlderTaskRuns(featureId, taskId, state.nextBefore, runsEl); });
|
|
333
|
+
runsEl.appendChild(more);
|
|
324
334
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
335
|
+
reconcileKeyed(table.querySelector("tbody"), state.runs, {
|
|
336
|
+
getKey: (run) => run.workerRunId,
|
|
337
|
+
create: () => el("tr"),
|
|
338
|
+
update: (row, run) => {
|
|
339
|
+
const signature = JSON.stringify(run);
|
|
340
|
+
if (row.dataset.signature === signature) return;
|
|
341
|
+
clearNode(row);
|
|
342
|
+
for (const cell of taskRunHistoryRow(run).cells) {
|
|
343
|
+
const td = el("td");
|
|
344
|
+
if (cell instanceof Node) td.appendChild(cell); else td.textContent = cell ?? "—";
|
|
345
|
+
row.appendChild(td);
|
|
346
|
+
}
|
|
347
|
+
row.dataset.signature = signature;
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
const status = runsEl.querySelector(".task-history-status");
|
|
351
|
+
status.textContent = state.error ? `历史加载失败:${state.error}` : state.runs.length ? `已加载 ${state.runs.length} 次运行` : "暂无 ledger 运行记录。";
|
|
352
|
+
const more = runsEl.querySelector("button.link-button");
|
|
353
|
+
more.hidden = !state.nextBefore && !state.error;
|
|
354
|
+
more.disabled = state.loadingOlder;
|
|
355
|
+
more.textContent = state.loadingOlder ? "正在加载…" : state.error ? "重试加载" : "加载更早的运行";
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export async function loadOlderTaskRuns(featureId, taskId, before, runsEl) {
|
|
359
|
+
const identity = `${workspaceScope() ?? ""}:${featureId ?? ""}:${taskId}`;
|
|
360
|
+
const state = taskHistoryStates.get(identity);
|
|
361
|
+
if (!state || state.loadingOlder) return;
|
|
362
|
+
state.loadingOlder = true;
|
|
363
|
+
renderTaskHistory(runsEl, state, featureId, taskId, identity);
|
|
364
|
+
const taskApi = featureId ? `/api/features/${encodeURIComponent(featureId)}/tasks/${encodeURIComponent(taskId)}` : `/api/tasks/${encodeURIComponent(taskId)}`;
|
|
365
|
+
try {
|
|
366
|
+
const result = await fetchJsonResult(`${taskApi}/runs?limit=20${state.nextBefore ? `&before=${encodeURIComponent(state.nextBefore)}` : ""}`);
|
|
367
|
+
if (taskHistoryStates.get(identity) !== state) return;
|
|
368
|
+
if (result.ok && result.body) mergeTaskHistoryPage(state, result.body, Boolean(state.nextBefore));
|
|
369
|
+
else state.error = summarizeFetchError(result);
|
|
370
|
+
} finally {
|
|
371
|
+
state.loadingOlder = false;
|
|
372
|
+
if (runsEl.dataset.historyIdentity === identity && taskHistoryStates.get(identity) === state) renderTaskHistory(runsEl, state, featureId, taskId, identity);
|
|
354
373
|
}
|
|
355
374
|
}
|
|
356
375
|
|
|
@@ -101,6 +101,110 @@ export function lookupRequiredVariantSlotsForMarkdownPath(inventory, markdownPat
|
|
|
101
101
|
.replace(/\.md$/i, "");
|
|
102
102
|
return inventory.modules.find((item) => item.stem === stem)?.requiredVariantSlots ?? [];
|
|
103
103
|
}
|
|
104
|
+
const MODULE_INDEX_HEADER = [
|
|
105
|
+
"Module Stem",
|
|
106
|
+
"Business Resource",
|
|
107
|
+
"Owned Operations",
|
|
108
|
+
"Owned Rule Keys",
|
|
109
|
+
"Case IDs",
|
|
110
|
+
"Split Reason",
|
|
111
|
+
"Markdown Path",
|
|
112
|
+
"Pytest Path",
|
|
113
|
+
];
|
|
114
|
+
function markdownTableCells(line) {
|
|
115
|
+
const trimmed = line.trim();
|
|
116
|
+
if (!trimmed.startsWith("|") || !trimmed.endsWith("|"))
|
|
117
|
+
return undefined;
|
|
118
|
+
return trimmed
|
|
119
|
+
.slice(1, -1)
|
|
120
|
+
.split("|")
|
|
121
|
+
.map((cell) => cell.trim().replace(/^`|`$/g, ""));
|
|
122
|
+
}
|
|
123
|
+
function isMarkdownSeparatorRow(cells) {
|
|
124
|
+
return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
|
125
|
+
}
|
|
126
|
+
/** Validate the minimum complete run-owned Markdown protocol before N2 succeeds. */
|
|
127
|
+
export function assessBackendTestPlanProtocol(markdown) {
|
|
128
|
+
const canonical = canonicalizeBackendTestPlanProtocolHeadings(markdown).markdown;
|
|
129
|
+
const lines = canonical.split("\n");
|
|
130
|
+
const issues = [];
|
|
131
|
+
if (lines.some((line) => /^(?:```|~~~)/.test(line.trim()))) {
|
|
132
|
+
issues.push({
|
|
133
|
+
code: "fenced-plan-output",
|
|
134
|
+
detail: "return the final Markdown plan directly, without a code fence",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const headingIndexes = new Map();
|
|
138
|
+
for (const heading of [
|
|
139
|
+
"Coverage Scope",
|
|
140
|
+
"Coverage Matrix",
|
|
141
|
+
"Scenario Partitions",
|
|
142
|
+
"Module Index",
|
|
143
|
+
]) {
|
|
144
|
+
headingIndexes.set(heading, lines.flatMap((line, index) => line.trim() === `## ${heading}` ? [index] : []));
|
|
145
|
+
}
|
|
146
|
+
const requireUnique = (heading, missingCode, duplicateCode) => {
|
|
147
|
+
const count = headingIndexes.get(heading)?.length ?? 0;
|
|
148
|
+
if (count === 0) {
|
|
149
|
+
issues.push({
|
|
150
|
+
code: missingCode,
|
|
151
|
+
detail: `require exactly one exact ## ${heading} section`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
else if (count > 1) {
|
|
155
|
+
issues.push({
|
|
156
|
+
code: duplicateCode,
|
|
157
|
+
detail: `found ${count}; require exactly one exact ## ${heading} section`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
// Module Index is first so retry diagnostics lead with the shard authority.
|
|
162
|
+
requireUnique("Module Index", "missing-module-index", "duplicate-module-index");
|
|
163
|
+
requireUnique("Coverage Scope", "missing-coverage-scope", "duplicate-coverage-scope");
|
|
164
|
+
requireUnique("Coverage Matrix", "missing-coverage-matrix", "duplicate-coverage-matrix");
|
|
165
|
+
const scenarioCount = headingIndexes.get("Scenario Partitions")?.length ?? 0;
|
|
166
|
+
if (scenarioCount > 1) {
|
|
167
|
+
issues.push({
|
|
168
|
+
code: "duplicate-scenario-partitions",
|
|
169
|
+
detail: `found ${scenarioCount}; require at most one exact ## Scenario Partitions section`,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const moduleIndexes = headingIndexes.get("Module Index") ?? [];
|
|
173
|
+
if (moduleIndexes.length === 1) {
|
|
174
|
+
const start = moduleIndexes[0] + 1;
|
|
175
|
+
const nextHeading = lines
|
|
176
|
+
.slice(start)
|
|
177
|
+
.findIndex((line) => /^##\s+\S/.test(line.trim()));
|
|
178
|
+
const end = nextHeading < 0 ? lines.length : start + nextHeading;
|
|
179
|
+
const rows = lines
|
|
180
|
+
.slice(start, end)
|
|
181
|
+
.map(markdownTableCells)
|
|
182
|
+
.filter((cells) => cells !== undefined);
|
|
183
|
+
const headerIndex = rows.findIndex((cells) => cells.length === MODULE_INDEX_HEADER.length &&
|
|
184
|
+
MODULE_INDEX_HEADER.every((name, index) => cells[index] === name));
|
|
185
|
+
if (headerIndex < 0) {
|
|
186
|
+
issues.push({
|
|
187
|
+
code: "invalid-module-index-header",
|
|
188
|
+
detail: `require exact Module Index header: ${MODULE_INDEX_HEADER.join(" | ")}`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const dataRows = rows
|
|
193
|
+
.slice(headerIndex + 1)
|
|
194
|
+
.filter((cells) => !isMarkdownSeparatorRow(cells));
|
|
195
|
+
if (!dataRows.some((cells) => cells.length === MODULE_INDEX_HEADER.length &&
|
|
196
|
+
Boolean(cells[0]?.trim()) &&
|
|
197
|
+
Boolean(cells[6]?.trim()) &&
|
|
198
|
+
Boolean(cells[7]?.trim()))) {
|
|
199
|
+
issues.push({
|
|
200
|
+
code: "empty-module-index",
|
|
201
|
+
detail: "Module Index must contain at least one complete 8-cell module row",
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return { ok: issues.length === 0, issues };
|
|
207
|
+
}
|
|
104
208
|
export function isCanonicalPlanHeadingLine(line) {
|
|
105
209
|
return CANONICAL_HEADING_LINE.test(line.trim());
|
|
106
210
|
}
|
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { checkBudgetPreNode, createBudgetLedger, formatBudgetReportMarkdown, isHardBudgetBreached, recordNodeBudgetSample, skipPendingNodesForBudgetBreach, } from "../../application/evaluation/budget.js";
|
|
3
|
+
import { checkBudgetPreNode, createBudgetLedger, formatBudgetReportMarkdown, isHardBudgetBreached, recordNodeBudgetSample, reserveProviderBudget, skipPendingNodesForBudgetBreach, } from "../../application/evaluation/budget.js";
|
|
4
|
+
import { sha256OfCanonicalJson } from "../../task/contract/hash.js";
|
|
5
|
+
/** A continuation already contains its ancestors' consumption. Never sum both. */
|
|
6
|
+
export function validateCumulativeBudgetSuccessor(parent, child) {
|
|
7
|
+
const lineage = child.continuation?.budgetLineage ?? child.frontendRecoveryState?.budgetLineage;
|
|
8
|
+
const previous = parent.budgetLedger;
|
|
9
|
+
const next = child.budgetLedger;
|
|
10
|
+
if (!previous || !next || !lineage || (child.continuation?.parentRunId ?? child.frontendRecoveryState?.parentRunId) !== parent.runId || lineage.parentLedgerSha256 !== sha256OfCanonicalJson(previous) || sha256OfCanonicalJson(lineage.inheritedConsumed) !== sha256OfCanonicalJson(previous.consumed) || sha256OfCanonicalJson(parent.budget ?? null) !== sha256OfCanonicalJson(child.budget ?? null))
|
|
11
|
+
throw Error("FRONTEND_PROVIDER_BUDGET_LINEAGE_INVALID: unbound cumulative continuation");
|
|
12
|
+
for (const key of ["executorCalls", "providerRequests", "wallTimeMs", "repairPasses", "peakContextChars"])
|
|
13
|
+
if ((next.consumed[key] ?? 0) < (previous.consumed[key] ?? 0))
|
|
14
|
+
throw Error("FRONTEND_PROVIDER_BUDGET_LINEAGE_INVALID: consumption decreased");
|
|
15
|
+
return next;
|
|
16
|
+
}
|
|
17
|
+
const requestQueues = new WeakMap();
|
|
18
|
+
/** One shared, attempt-fenced, durable admission queue for every request in a run. */
|
|
19
|
+
export async function reserveDagProviderRequest(input) {
|
|
20
|
+
const ledger = input.state.budgetLedger;
|
|
21
|
+
if (!ledger || ledger.limits.maxProviderRequests === undefined)
|
|
22
|
+
throw Error("FRONTEND_PROVIDER_BUDGET_INVALID: frozen request budget unavailable");
|
|
23
|
+
const previous = requestQueues.get(ledger) ?? Promise.resolve();
|
|
24
|
+
const current = previous.catch(() => { }).then(async () => {
|
|
25
|
+
const active = () => input.state.status === "running" && input.state.nodes[input.nodeId]?.status === "RUNNING" && input.state.nodes[input.nodeId]?.currentAttempt === input.attempt;
|
|
26
|
+
if (!active())
|
|
27
|
+
throw Error("FRONTEND_PROVIDER_BUDGET_STALE: request belongs to an inactive attempt");
|
|
28
|
+
if (isHardBudgetBreached(ledger))
|
|
29
|
+
throw Error("FRONTEND_PROVIDER_BUDGET_EXHAUSTED: run budget already breached");
|
|
30
|
+
const breach = reserveProviderBudget(ledger, input.nodeId);
|
|
31
|
+
if (breach)
|
|
32
|
+
applyHardBudgetBreach(input.state, breach);
|
|
33
|
+
try {
|
|
34
|
+
await input.persist();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
ledger.status = "breached";
|
|
38
|
+
throw Error(`FRONTEND_PROVIDER_BUDGET_PERSIST_FAILED: ${error instanceof Error ? error.message : String(error)}`);
|
|
39
|
+
}
|
|
40
|
+
if (breach)
|
|
41
|
+
throw Error("FRONTEND_PROVIDER_BUDGET_EXHAUSTED: request quota reached");
|
|
42
|
+
if (!active())
|
|
43
|
+
throw Error("FRONTEND_PROVIDER_BUDGET_STALE: attempt stopped while reserving request");
|
|
44
|
+
});
|
|
45
|
+
requestQueues.set(ledger, current);
|
|
46
|
+
try {
|
|
47
|
+
await current;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (requestQueues.get(ledger) === current)
|
|
51
|
+
requestQueues.delete(ledger);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
4
54
|
export function initRunBudgetLedger(state, budget) {
|
|
5
55
|
if (!budget)
|
|
6
56
|
return;
|
|
@@ -8,7 +58,7 @@ export function initRunBudgetLedger(state, budget) {
|
|
|
8
58
|
state.budgetLedger = createBudgetLedger(budget);
|
|
9
59
|
}
|
|
10
60
|
function wallTimeMs(state, now = Date.now()) {
|
|
11
|
-
const lineage = state.continuation?.budgetLineage;
|
|
61
|
+
const lineage = state.continuation?.budgetLineage ?? state.frontendRecoveryState?.budgetLineage;
|
|
12
62
|
if (lineage) {
|
|
13
63
|
return (lineage.inheritedConsumed.wallTimeMs +
|
|
14
64
|
Math.max(0, now - new Date(lineage.inheritedAt).getTime()));
|
|
@@ -16,7 +66,7 @@ function wallTimeMs(state, now = Date.now()) {
|
|
|
16
66
|
return Math.max(0, now - new Date(state.startedAt).getTime());
|
|
17
67
|
}
|
|
18
68
|
function repairPasses(state) {
|
|
19
|
-
const lineage = state.continuation?.budgetLineage;
|
|
69
|
+
const lineage = state.continuation?.budgetLineage ?? state.frontendRecoveryState?.budgetLineage;
|
|
20
70
|
if (lineage) {
|
|
21
71
|
return (lineage.inheritedConsumed.repairPasses +
|
|
22
72
|
Math.max(0, (state.convergence?.currentPass ?? 1) - 1));
|
|
@@ -32,7 +32,10 @@ export const DEFAULT_DAG_RETRY_CATEGORIES = [
|
|
|
32
32
|
"rate-limit",
|
|
33
33
|
"unavailable",
|
|
34
34
|
"empty-output",
|
|
35
|
+
"output-limit",
|
|
35
36
|
];
|
|
37
|
+
/** Provider stopReason=length: output/context capacity ended the turn early. */
|
|
38
|
+
export const OUTPUT_LIMIT_RETRY_CATEGORY = "output-limit";
|
|
36
39
|
export const STRUCTURED_OUTPUT_RETRY_CATEGORY = "output-too-large";
|
|
37
40
|
export const PROTOCOL_INVALID_RETRY_CATEGORY = "protocol-invalid";
|
|
38
41
|
/** Recoverable model artifact/schema formatting failure on read-only structured nodes. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Shared Contract/Plan bound. Permissions and directory globs are not files. */
|
|
2
|
+
export const FRONTEND_MAX_IMPLEMENTATION_FILES = 15;
|
|
3
|
+
export function frontendConcreteDeliveryFiles(files) {
|
|
4
|
+
if (!Array.isArray(files))
|
|
5
|
+
return [];
|
|
6
|
+
return [...new Set(files.filter((file) => typeof file === "string")
|
|
7
|
+
.map(file => file.trim().replace(/\\/g, "/").replace(/^(\.\/)+/, ""))
|
|
8
|
+
.filter(file => file.length > 0 && !/[*?{}]/.test(file) && !file.endsWith("/")))];
|
|
9
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Shared projections of committed frontend facts. Callers filter commit phase. */
|
|
3
|
+
function object(value) {
|
|
4
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
5
|
+
? value
|
|
6
|
+
: undefined;
|
|
7
|
+
}
|
|
8
|
+
/** Join requirement-owned identity with separately committed evidence by id.
|
|
9
|
+
* Later evidence declarations replace only the lanes they explicitly name. */
|
|
10
|
+
export function resolveFrontendContractRequirements(facts) {
|
|
11
|
+
const contractFacts = facts.map(object).filter((fact) => fact?.origin === "contract");
|
|
12
|
+
const evidenceById = new Map();
|
|
13
|
+
for (const fact of contractFacts) {
|
|
14
|
+
if (fact.kind !== "evidence-expectation" ||
|
|
15
|
+
typeof fact.requirementId !== "string")
|
|
16
|
+
continue;
|
|
17
|
+
evidenceById.set(fact.requirementId, {
|
|
18
|
+
...evidenceById.get(fact.requirementId),
|
|
19
|
+
...object(fact.evidence),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return contractFacts.flatMap((fact) => fact.kind === "requirement" && typeof fact.id === "string"
|
|
23
|
+
? [{
|
|
24
|
+
...fact,
|
|
25
|
+
id: fact.id,
|
|
26
|
+
evidence: { ...object(fact.evidence), ...evidenceById.get(fact.id) },
|
|
27
|
+
}]
|
|
28
|
+
: []);
|
|
29
|
+
}
|
|
30
|
+
function names(value) {
|
|
31
|
+
return Array.isArray(value)
|
|
32
|
+
? value.filter((item) => typeof item === "string" && item.trim().length > 0)
|
|
33
|
+
: [];
|
|
34
|
+
}
|
|
35
|
+
const evidenceStatus = z.enum(["required", "optional", "not-applicable"]);
|
|
36
|
+
export const frontendEvidenceExpectationSchema = z.object({
|
|
37
|
+
requirementId: z.string().min(1),
|
|
38
|
+
evidence: z.object({
|
|
39
|
+
static: evidenceStatus.optional(),
|
|
40
|
+
behavior: evidenceStatus.optional(),
|
|
41
|
+
mock: evidenceStatus.optional(),
|
|
42
|
+
"real-integration": evidenceStatus.optional(),
|
|
43
|
+
}).strict().refine((evidence) => Object.keys(evidence).length > 0, "declare at least one evidence lane"),
|
|
44
|
+
}).strict();
|
|
45
|
+
const concretePath = z.string().min(1).refine((value) => value === value.trim() &&
|
|
46
|
+
!/^[A-Za-z]:/.test(value) &&
|
|
47
|
+
!/[\\*?\[\]{}\0]/.test(value) &&
|
|
48
|
+
value.split("/").every((part) => part !== "" && part !== "." && part !== ".."), "must be a concrete relative POSIX file path without traversal");
|
|
49
|
+
export const frontendRequiredDeliverablesSchema = z.object({
|
|
50
|
+
items: z.array(z.object({
|
|
51
|
+
path: concretePath,
|
|
52
|
+
requirementId: z.string().min(1),
|
|
53
|
+
sourceFragmentId: z.string().min(1),
|
|
54
|
+
}).strict()),
|
|
55
|
+
}).strict();
|
|
56
|
+
/** Validate structure and provenance; the Contract model owns the distinction
|
|
57
|
+
* between delivery obligations, permissions, prohibitions and references. */
|
|
58
|
+
export function validateFrontendRequiredDeliverables(value, requirements) {
|
|
59
|
+
const declaration = frontendRequiredDeliverablesSchema.parse(value);
|
|
60
|
+
for (const item of declaration.items) {
|
|
61
|
+
const requirement = requirements.get(item.requirementId);
|
|
62
|
+
if (!requirement?.sourceFragmentIds.includes(item.sourceFragmentId)) {
|
|
63
|
+
throw new Error(`required deliverable ${item.path} has an unknown requirement/fragment binding`);
|
|
64
|
+
}
|
|
65
|
+
const text = requirement.sourceFragments?.get(item.sourceFragmentId);
|
|
66
|
+
const escaped = item.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
67
|
+
// A sentence-ending period is punctuation; a dot followed by filename
|
|
68
|
+
// characters is still part of the token (app.ts must not match app.ts.map).
|
|
69
|
+
const exactPath = new RegExp(`(^|[^\\p{L}\\p{N}_./-])${escaped}(?=$|[^\\p{L}\\p{N}_./-]|\\.(?=$|[^\\p{L}\\p{N}_./-]))`, "u");
|
|
70
|
+
if (!text || !exactPath.test(text)) {
|
|
71
|
+
throw new Error(`required deliverable ${item.path} is not an exact path in frozen source fragment ${item.sourceFragmentId}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return declaration;
|
|
75
|
+
}
|
|
76
|
+
/** Read the latest explicit inventory. Never infer obligations from prose. */
|
|
77
|
+
export function extractRequiredDeliverablePaths(facts) {
|
|
78
|
+
const declaration = facts.map(object).filter((fact) => fact?.origin === "contract" && fact.kind === "required-deliverables").at(-1);
|
|
79
|
+
if (!declaration)
|
|
80
|
+
return [];
|
|
81
|
+
const { items } = frontendRequiredDeliverablesSchema.parse({ items: declaration.items });
|
|
82
|
+
return [...new Set(items.map((item) => item.path))].sort();
|
|
83
|
+
}
|
|
84
|
+
/** Apply state-flow replacement/removal semantics exactly as the plan compiler does. */
|
|
85
|
+
export function collectCanonicalStateFlowNames(committedFacts, includeRegistry = false) {
|
|
86
|
+
const uiStateNames = new Set();
|
|
87
|
+
const interactionNames = new Set();
|
|
88
|
+
for (const value of committedFacts) {
|
|
89
|
+
const record = object(value);
|
|
90
|
+
if (!record || (record.phase !== undefined && record.phase !== "committed"))
|
|
91
|
+
continue;
|
|
92
|
+
const fact = object(record.fact) ?? record;
|
|
93
|
+
if (fact.origin !== "plan")
|
|
94
|
+
continue;
|
|
95
|
+
if (includeRegistry && fact.kind === "state-registry") {
|
|
96
|
+
uiStateNames.clear();
|
|
97
|
+
interactionNames.clear();
|
|
98
|
+
for (const name of names(fact.uiStateNames))
|
|
99
|
+
uiStateNames.add(name);
|
|
100
|
+
for (const name of names(fact.interactionNames))
|
|
101
|
+
interactionNames.add(name);
|
|
102
|
+
}
|
|
103
|
+
if (fact.kind !== "state-flow")
|
|
104
|
+
continue;
|
|
105
|
+
for (const name of names(fact.removeUiStateNames)) {
|
|
106
|
+
uiStateNames.delete(name);
|
|
107
|
+
}
|
|
108
|
+
for (const name of names(fact.removeInteractionNames)) {
|
|
109
|
+
interactionNames.delete(name);
|
|
110
|
+
}
|
|
111
|
+
for (const state of Array.isArray(fact.uiStates) ? fact.uiStates : []) {
|
|
112
|
+
if (!state || typeof state !== "object" || Array.isArray(state))
|
|
113
|
+
continue;
|
|
114
|
+
const name = state.name;
|
|
115
|
+
if (typeof name === "string" && name.trim())
|
|
116
|
+
uiStateNames.add(name);
|
|
117
|
+
}
|
|
118
|
+
for (const interaction of Array.isArray(fact.interactions)
|
|
119
|
+
? fact.interactions
|
|
120
|
+
: []) {
|
|
121
|
+
if (!interaction || typeof interaction !== "object" || Array.isArray(interaction)) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const name = interaction.name;
|
|
125
|
+
if (typeof name === "string" && name.trim())
|
|
126
|
+
interactionNames.add(name);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { uiStateNames, interactionNames };
|
|
130
|
+
}
|