@tea-agent/loop-agent 0.39.0-beta.2 → 0.39.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -1
- package/CHANGELOG.md +187 -92
- package/README.md +9 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +429 -28
- package/dist/commands/inspect-next.js +8 -0
- package/dist/executors/dag-pi-executor.js +1645 -29
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +51 -12
- package/dist/executors/pi-sdk-executor.js +180 -33
- package/dist/executors/shell-executor.js +1150 -171
- package/dist/executors/shell-write-guard.js +14 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +32 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +19 -5
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +42 -16
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/task/source-prepare/prepare.js +36 -0
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/operation-store.js +226 -6
- package/dist/worker/console/operator-actions.js +263 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +151 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-zsNyvGaH.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-BDjZ5kE1.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-BkgaujnN.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-CqxOJi0j.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-C2JHLdRi.js} +1 -1
- package/dist/worker/console/static/assets/channel-BuqaGKAT.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-DF_YSvOS.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-D3Ort5Vo.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-XfAfZeUC.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-SLBibhf5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-Dz29Ye_f.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-DB2G4H8V.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-CIoGkxdu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-CG78FGkn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-ZDHAvXPd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BKkkC4VA.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DNdSHjH9.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DNdSHjH9.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-SposUZsO.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-BSCtQpD7.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-DgFHrw9l.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-BVBasYbj.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-6r_4rN_c.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-JmuaB41P.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-xXvAY3sk.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-D2B3lY2o.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-tK54tMEX.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-CNtELzV-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-C1xKMKcW.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CZ7dlPMz.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-BqGb42Xe.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-D_BlXEtt.js} +1 -1
- package/dist/worker/console/static/assets/{index-DTZOKgAn.js → index-D5zGX6fG.js} +68 -68
- package/dist/worker/console/static/assets/index-DBbhESQ_.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-Vce2vmIe.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-DDwVBAvI.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-y_Ew-wv4.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-CjXJFp1K.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-1itMst1Q.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-C-D3ZRUa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-BiYywmZc.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-l2fydNWA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-Dz0hNbYW.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-BujI_mLZ.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-pWe84beb.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-ByYu2frg.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-CoPnilFP.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-1Md2LnS0.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DqqqAoSH.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C4sGkf--.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8rmop_9.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-ByxJrUzW.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B0ky-kBM.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-D8MLgM0O.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DnBis-wZ.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-uuyYT7UO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-BjgabhwK.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +20 -2
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/failure-category.js +12 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +347 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +378 -160
- package/dist/workflows/dag/frontend-prewrite-gate.js +258 -393
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +201 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +7 -424
- package/dist/workflows/dag/frontend-review-context.js +195 -8
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-shadow-dual-write.js +665 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +409 -0
- package/dist/workflows/dag/frontend-shape.js +425 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-test-case-checklist.js +26 -2
- package/dist/workflows/dag/frontend-test-case-manifest.js +5 -0
- package/dist/workflows/dag/frontend-test-case-quality.js +5 -13
- package/dist/workflows/dag/frontend-test-environment-probe.js +227 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +160 -28
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +68 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +438 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +52 -17
- package/dist/workflows/dag/frontend-worktree-diff.js +97 -18
- package/dist/workflows/dag/frontend-writer-admission.js +260 -0
- package/dist/workflows/dag/frontend-writer-status.js +244 -0
- package/dist/workflows/dag/init-hybrid.js +572 -500
- package/dist/workflows/dag/node-execution.js +307 -70
- package/dist/workflows/dag/prompt.js +10 -18
- package/dist/workflows/dag/rerun-plan.js +144 -5
- package/dist/workflows/dag/rerun-run.js +58 -6
- package/dist/workflows/dag/retry-policy.js +228 -4
- package/dist/workflows/dag/runner.js +314 -119
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +235 -23
- package/dist/workflows/dag/upstream-artifacts.js +0 -4
- package/dist/workflows/dag/validate.js +60 -10
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +3 -3
- package/package.json +5 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/SKILL.md +4 -4
- package/skills/frontend-implementation/references/design-spec.md +11 -7
- package/skills/frontend-implementation/references/node-contracts.md +20 -11
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/skills/playwright-cli/SKILL.md +22 -0
- package/skills/playwright-cli-case-generator/SKILL.md +20 -1
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
/**
|
|
4
|
+
* M4: fact-derived frontend writer status + bounded in-node micro-loop.
|
|
5
|
+
*
|
|
6
|
+
* `frontend-implement-pi` derives its implementation status from mechanical
|
|
7
|
+
* facts (write-tool events, run delta, cumulative diff, write guard, requirement
|
|
8
|
+
* coverage, focused-check failures) instead of parsing the
|
|
9
|
+
* `IMPLEMENTATION_OUTCOME:` first line. The legacy `implementation-outcome-v1`
|
|
10
|
+
* protocol (backend/README/module writers + `frontend-repair-pi`) is untouched.
|
|
11
|
+
*
|
|
12
|
+
* Everything here is either a pure function (deterministically testable) or an
|
|
13
|
+
* IO collector that is only reached through a dynamic import from the Pi
|
|
14
|
+
* executor, avoiding an executor↔workflows static cycle.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Write/edit-style tool names emitted by DAG writer customTools. Kept in sync
|
|
18
|
+
* with the `SDK_WRITE_TOOL_NAMES` set in pi-sdk-executor.ts so the persisted
|
|
19
|
+
* session-event count and the in-memory writeToolCallCount agree.
|
|
20
|
+
*/
|
|
21
|
+
export const FRONTEND_WRITE_TOOL_NAMES = new Set([
|
|
22
|
+
"edit",
|
|
23
|
+
"write",
|
|
24
|
+
"str_replace_editor",
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* AC-001/AC-003: derive the writer status from mechanical facts only. The
|
|
28
|
+
* first-line text is never consulted. Ordering:
|
|
29
|
+
* 1. write-guard violation → blocked
|
|
30
|
+
* 2. non-empty diff → changed (diff is the mechanical fact source)
|
|
31
|
+
* 3. empty diff + runtime already-satisfied evidence → already-satisfied
|
|
32
|
+
* 4. empty diff, zero writes, zero diff → blocked (no progress)
|
|
33
|
+
* 5. otherwise → empty-diff (fail-closed)
|
|
34
|
+
*/
|
|
35
|
+
export function deriveFrontendWriterStatus(facts) {
|
|
36
|
+
if (!facts.writeGuardOk) {
|
|
37
|
+
return {
|
|
38
|
+
status: "blocked",
|
|
39
|
+
reason: `write guard violations: ${facts.writeGuardViolations.join(", ") || "unknown"}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (facts.changedFiles.length > 0) {
|
|
43
|
+
return {
|
|
44
|
+
status: "changed",
|
|
45
|
+
reason: `non-empty run-attributed diff (${facts.changedFiles.length} file${facts.changedFiles.length === 1 ? "" : "s"})`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (facts.alreadySatisfiedEvidence) {
|
|
49
|
+
return {
|
|
50
|
+
status: "already-satisfied",
|
|
51
|
+
reason: `empty diff with runtime already-satisfied evidence (${facts.alreadySatisfiedEvidence.requirementTargets.length} target${facts.alreadySatisfiedEvidence.requirementTargets.length === 1 ? "" : "s"})`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (facts.writeToolEvents === 0 && facts.changedFiles.length === 0) {
|
|
55
|
+
return {
|
|
56
|
+
status: "blocked",
|
|
57
|
+
reason: "zero write-tool events and zero diff: no implementation progress",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
status: "empty-diff",
|
|
62
|
+
reason: "empty diff without runtime already-satisfied evidence (fail-closed)",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Deterministic failure fingerprint over the normalized failure signature:
|
|
67
|
+
* category + focused-check failures (target + detail) + guard status. Used for
|
|
68
|
+
* repeated-failure deduplication inside the bounded micro-loop.
|
|
69
|
+
*/
|
|
70
|
+
export function computeFailureFingerprint(facts) {
|
|
71
|
+
const signature = normalizeFailureSignature(facts);
|
|
72
|
+
return createHash("sha256").update(signature).digest("hex");
|
|
73
|
+
}
|
|
74
|
+
function normalizeFailureSignature(facts) {
|
|
75
|
+
const parts = [];
|
|
76
|
+
if (!facts.writeGuardOk) {
|
|
77
|
+
parts.push("write-guard");
|
|
78
|
+
}
|
|
79
|
+
for (const failure of facts.focusedCheckFailures) {
|
|
80
|
+
parts.push(`${failure.category}:${failure.targetId ?? ""}:${failure.detail}`);
|
|
81
|
+
}
|
|
82
|
+
if (facts.changedFiles.length === 0 && facts.writeToolEvents === 0) {
|
|
83
|
+
parts.push("no-progress");
|
|
84
|
+
}
|
|
85
|
+
return parts.sort().join("\n");
|
|
86
|
+
}
|
|
87
|
+
export const DEFAULT_FRONTEND_MICRO_LOOP_BUDGET = {
|
|
88
|
+
maxFocusedCheckRounds: 3,
|
|
89
|
+
maxWriteAttempts: 5,
|
|
90
|
+
maxWallTimeMs: 15 * 60 * 1000,
|
|
91
|
+
maxTokens: 200_000,
|
|
92
|
+
maxRepeatedFingerprints: 2,
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Pure micro-loop continuation gate. Deterministic: same budget + history +
|
|
96
|
+
* timestamps always yield the same decision. The caller (executor) drives the
|
|
97
|
+
* loop by re-checking this function after each focused-check round.
|
|
98
|
+
*/
|
|
99
|
+
export function evaluateMicroLoopContinuation(budget, history, startWallTimeMs, nowWallTimeMs) {
|
|
100
|
+
const rounds = history.length;
|
|
101
|
+
const last = history[rounds - 1];
|
|
102
|
+
if (last) {
|
|
103
|
+
const resolved = (last.status === "changed" &&
|
|
104
|
+
last.writeGuardOk &&
|
|
105
|
+
last.focusedCheckFailures.length === 0) ||
|
|
106
|
+
last.status === "already-satisfied" ||
|
|
107
|
+
last.status === "blocked";
|
|
108
|
+
if (resolved) {
|
|
109
|
+
return { continue: false, reason: "status-resolved" };
|
|
110
|
+
}
|
|
111
|
+
if (last.writeAttempts >= budget.maxWriteAttempts) {
|
|
112
|
+
return { continue: false, reason: "budget-write-attempts" };
|
|
113
|
+
}
|
|
114
|
+
if (last.tokensUsed >= budget.maxTokens) {
|
|
115
|
+
return { continue: false, reason: "budget-tokens" };
|
|
116
|
+
}
|
|
117
|
+
if (nowWallTimeMs - startWallTimeMs >= budget.maxWallTimeMs) {
|
|
118
|
+
return { continue: false, reason: "budget-wall-time" };
|
|
119
|
+
}
|
|
120
|
+
const repeatCount = history.filter((round) => round.fingerprint === last.fingerprint).length;
|
|
121
|
+
if (repeatCount >= budget.maxRepeatedFingerprints) {
|
|
122
|
+
return { continue: false, reason: "budget-repeated-fingerprint" };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (rounds >= budget.maxFocusedCheckRounds) {
|
|
126
|
+
return { continue: false, reason: "budget-focused-check-rounds" };
|
|
127
|
+
}
|
|
128
|
+
return { continue: true };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build a deterministic per-round checkpoint payload. Persistence stays with the
|
|
132
|
+
* caller (executor writes `runDir/<nodeId>/` only, never root `artifacts/**`).
|
|
133
|
+
*/
|
|
134
|
+
export function buildFrontendMicroLoopCheckpoint(input) {
|
|
135
|
+
return {
|
|
136
|
+
schemaVersion: 1,
|
|
137
|
+
nodeId: input.nodeId,
|
|
138
|
+
round: input.round,
|
|
139
|
+
status: deriveFrontendWriterStatus(input.facts).status,
|
|
140
|
+
fingerprint: computeFailureFingerprint(input.facts),
|
|
141
|
+
changedFiles: [...input.facts.changedFiles].sort(),
|
|
142
|
+
focusedCheckFailures: input.facts.focusedCheckFailures.map((failure) => ({
|
|
143
|
+
...failure,
|
|
144
|
+
})),
|
|
145
|
+
writeGuardOk: input.facts.writeGuardOk,
|
|
146
|
+
recordedAt: input.recordedAt,
|
|
147
|
+
wallTimeMs: input.facts.wallTimeMs,
|
|
148
|
+
tokensUsed: input.facts.tokensUsed,
|
|
149
|
+
writeAttempts: input.facts.writeAttempts,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Shadow comparison between the fact-derived status and the legacy
|
|
154
|
+
* `IMPLEMENTATION_OUTCOME:` first-line parse. Never used to decide ok/fail —
|
|
155
|
+
* only persisted for the M4 shadow evaluation.
|
|
156
|
+
*/
|
|
157
|
+
export function compareFactStatusToLegacyOutcome(factStatus, legacyOutcome) {
|
|
158
|
+
const factVerdict = factStatus === "changed" || factStatus === "already-satisfied"
|
|
159
|
+
? "ok"
|
|
160
|
+
: "fail";
|
|
161
|
+
const legacyVerdict = legacyOutcome === "changed" || legacyOutcome === "already-satisfied"
|
|
162
|
+
? "ok"
|
|
163
|
+
: "fail";
|
|
164
|
+
return {
|
|
165
|
+
factStatus,
|
|
166
|
+
legacyOutcome,
|
|
167
|
+
factVerdict,
|
|
168
|
+
legacyVerdict,
|
|
169
|
+
match: factVerdict === legacyVerdict,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Pure session-event scanner: count write-tool events from the persisted
|
|
174
|
+
* session-events.jsonl content. Fail-closed: an unparseable line never counts
|
|
175
|
+
* as a write, so a corrupt/empty log cannot inflate progress.
|
|
176
|
+
*/
|
|
177
|
+
export function countWriteToolEventsFromSessionEvents(content) {
|
|
178
|
+
let count = 0;
|
|
179
|
+
for (const line of content.split("\n")) {
|
|
180
|
+
const trimmed = line.trim();
|
|
181
|
+
if (!trimmed)
|
|
182
|
+
continue;
|
|
183
|
+
let event;
|
|
184
|
+
try {
|
|
185
|
+
event = JSON.parse(trimmed);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (event !== null &&
|
|
191
|
+
typeof event === "object" &&
|
|
192
|
+
event.type === "tool_execution_start" &&
|
|
193
|
+
typeof event.toolName === "string" &&
|
|
194
|
+
FRONTEND_WRITE_TOOL_NAMES.has(event.toolName)) {
|
|
195
|
+
count += 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return count;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* IO collector for frontend facts. Reads only the persisted session-events
|
|
202
|
+
* file; every other fact is passed in by the executor so this stays narrow and
|
|
203
|
+
* deterministic under test (tests inject session content by writing to a temp
|
|
204
|
+
* path or call the pure scanner directly).
|
|
205
|
+
*/
|
|
206
|
+
export async function collectFrontendWriterFacts(input) {
|
|
207
|
+
let writeToolEvents = 0;
|
|
208
|
+
try {
|
|
209
|
+
const content = await readFile(input.sessionEventsPath, "utf8");
|
|
210
|
+
writeToolEvents = countWriteToolEventsFromSessionEvents(content);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Missing/unreadable session log → fail-closed at zero, never fail-open.
|
|
214
|
+
writeToolEvents = 0;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
changedFiles: [...input.changedFiles].sort(),
|
|
218
|
+
writeToolEvents,
|
|
219
|
+
writeGuardOk: input.writeGuard.ok,
|
|
220
|
+
writeGuardViolations: [...input.writeGuard.violations],
|
|
221
|
+
requirementTargets: [...new Set(input.requirementTargets)].sort(),
|
|
222
|
+
coveredRequirementIds: [...new Set(input.coveredRequirementIds)].sort(),
|
|
223
|
+
focusedCheckFailures: input.focusedCheckFailures.map((failure) => ({
|
|
224
|
+
...failure,
|
|
225
|
+
})),
|
|
226
|
+
...(input.alreadySatisfiedEvidence
|
|
227
|
+
? {
|
|
228
|
+
alreadySatisfiedEvidence: {
|
|
229
|
+
requirementTargets: [
|
|
230
|
+
...input.alreadySatisfiedEvidence.requirementTargets,
|
|
231
|
+
].sort(),
|
|
232
|
+
proofSha256: input.alreadySatisfiedEvidence.proofSha256,
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
: {}),
|
|
236
|
+
tokensUsed: input.tokensUsed ?? 0,
|
|
237
|
+
wallTimeMs: input.wallTimeMs ?? 0,
|
|
238
|
+
rounds: input.rounds ?? 1,
|
|
239
|
+
writeAttempts: input.writeAttempts ?? 1,
|
|
240
|
+
...(input.firstLineText !== undefined
|
|
241
|
+
? { firstLineText: input.firstLineText }
|
|
242
|
+
: {}),
|
|
243
|
+
};
|
|
244
|
+
}
|