@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
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Strong evidence only: package.json deps, config files, source imports.
|
|
4
4
|
* Lockfile-only or directory-name-only must NOT activate adapters.
|
|
5
5
|
*/
|
|
6
|
-
import { readFile, readdir, stat } from "node:fs/promises";
|
|
6
|
+
import { lstat, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
import { DEFAULT_OPENSPEC_GOVERNANCE_ROOT, MAX_OPENSPEC_CANDIDATE_PATHS, OPENSPEC_SPEC_DIRS, OPENSPEC_SPEC_EXT_RE, isGovernanceSubtreePath,
|
|
9
|
+
import { DEFAULT_OPENSPEC_GOVERNANCE_ROOT, MAX_OPENSPEC_CANDIDATE_PATHS, OPENSPEC_SPEC_DIRS, OPENSPEC_SPEC_EXT_RE, isGovernanceSubtreePath, } from "../shared/openspec-spec.js";
|
|
10
10
|
const classifiedPathSchema = z
|
|
11
11
|
.string()
|
|
12
|
-
.refine((candidate) =>
|
|
12
|
+
.refine((candidate) => OPENSPEC_SPEC_EXT_RE.test(candidate), "classified path must be a repo-relative supported spec file");
|
|
13
13
|
/**
|
|
14
14
|
* Stable semantic partition of `designEvidence.normativePaths`. Keys are a
|
|
15
15
|
* generic directory convention (not project-specific file names); the same
|
|
@@ -81,6 +81,34 @@ async function exists(filePath) {
|
|
|
81
81
|
return false;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve only unambiguous symlink-root aliases. The physical target is never
|
|
86
|
+
* emitted as a candidate path: it merely lets task-source wording such as
|
|
87
|
+
* `ai_doc/rules.md` resolve to the stable repo-local path `ai_workspace/rules.md`.
|
|
88
|
+
*/
|
|
89
|
+
export async function resolveFrontendSpecRootAliases(repoRoot, specRoots) {
|
|
90
|
+
const candidates = [];
|
|
91
|
+
for (const logicalRoot of specRoots) {
|
|
92
|
+
const full = path.join(repoRoot, logicalRoot);
|
|
93
|
+
try {
|
|
94
|
+
if (!(await lstat(full)).isSymbolicLink())
|
|
95
|
+
continue;
|
|
96
|
+
const alias = path.basename(await realpath(full));
|
|
97
|
+
if (alias && alias !== path.basename(logicalRoot)) {
|
|
98
|
+
candidates.push({ alias, logicalRoot });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Missing/broken links are handled by normal candidate discovery drift.
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const counts = new Map();
|
|
106
|
+
for (const item of candidates)
|
|
107
|
+
counts.set(item.alias, (counts.get(item.alias) ?? 0) + 1);
|
|
108
|
+
return candidates
|
|
109
|
+
.filter((item) => counts.get(item.alias) === 1)
|
|
110
|
+
.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
111
|
+
}
|
|
84
112
|
/**
|
|
85
113
|
* Resolve the governance subtree root from harness.json.governanceRoot.
|
|
86
114
|
* Falls back to DEFAULT_OPENSPEC_GOVERNANCE_ROOT when the manifest is missing,
|
|
@@ -110,22 +138,20 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
110
138
|
return true;
|
|
111
139
|
return extraExcludedPrefixes.some((prefix) => rel === prefix || rel.startsWith(prefix + "/"));
|
|
112
140
|
};
|
|
113
|
-
for (const specDir of OPENSPEC_SPEC_DIRS) {
|
|
141
|
+
for (const specDir of options.specRoots ?? [...OPENSPEC_SPEC_DIRS]) {
|
|
114
142
|
const root = path.join(repoRoot, specDir);
|
|
115
143
|
if (!(await exists(root)))
|
|
116
144
|
continue;
|
|
117
145
|
await walk(root, 0);
|
|
118
146
|
}
|
|
119
|
-
//
|
|
120
|
-
//
|
|
147
|
+
// Keep the established default-root priority for deterministic compatibility;
|
|
148
|
+
// configured extra roots are appended in lexical order.
|
|
121
149
|
const unique = [...new Set(collected)];
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
const aiWorkspacePaths = unique
|
|
126
|
-
.filter((candidate) => candidate.startsWith("ai_workspace/"))
|
|
150
|
+
const defaultPaths = OPENSPEC_SPEC_DIRS.flatMap((root) => unique.filter((candidate) => candidate.startsWith(root + "/")).sort());
|
|
151
|
+
const customPaths = unique
|
|
152
|
+
.filter((candidate) => !defaultPaths.includes(candidate))
|
|
127
153
|
.sort();
|
|
128
|
-
const deduped = [...
|
|
154
|
+
const deduped = [...defaultPaths, ...customPaths];
|
|
129
155
|
const truncated = deduped.length > MAX_OPENSPEC_CANDIDATE_PATHS;
|
|
130
156
|
const paths = truncated
|
|
131
157
|
? deduped.slice(0, MAX_OPENSPEC_CANDIDATE_PATHS)
|
|
@@ -260,7 +286,7 @@ function classifyOpenspecPaths(paths) {
|
|
|
260
286
|
return "other";
|
|
261
287
|
};
|
|
262
288
|
for (const candidate of paths) {
|
|
263
|
-
if (!
|
|
289
|
+
if (!OPENSPEC_SPEC_EXT_RE.test(candidate))
|
|
264
290
|
continue;
|
|
265
291
|
const segments = candidate.split("/");
|
|
266
292
|
const basename = segments[segments.length - 1] ?? "";
|
|
@@ -361,13 +387,13 @@ export function buildAdapterGuidance(capability) {
|
|
|
361
387
|
* Discover frontend project capability from repo root.
|
|
362
388
|
* Strong evidence: package.json direct deps + config/bootstrap files.
|
|
363
389
|
*/
|
|
364
|
-
export async function discoverFrontendProjectCapability(repoRoot) {
|
|
390
|
+
export async function discoverFrontendProjectCapability(repoRoot, options = {}) {
|
|
365
391
|
const evidencePaths = [];
|
|
366
392
|
const reasons = [];
|
|
367
393
|
const pkgPath = path.join(repoRoot, "package.json");
|
|
368
394
|
const pkgRaw = await readJson(pkgPath);
|
|
369
395
|
if (!pkgRaw || typeof pkgRaw !== "object") {
|
|
370
|
-
const discovery = await listOpenspecDiscovery(repoRoot);
|
|
396
|
+
const discovery = await listOpenspecDiscovery(repoRoot, options);
|
|
371
397
|
const designEvidence = buildDesignEvidence(discovery.paths);
|
|
372
398
|
const base = {
|
|
373
399
|
schemaVersion: 1,
|
|
@@ -540,7 +566,7 @@ export async function discoverFrontendProjectCapability(repoRoot) {
|
|
|
540
566
|
router = router ?? "next-router";
|
|
541
567
|
if (hasDep(deps, "vue-router"))
|
|
542
568
|
router = "vue-router";
|
|
543
|
-
const discovery = await listOpenspecDiscovery(repoRoot);
|
|
569
|
+
const discovery = await listOpenspecDiscovery(repoRoot, options);
|
|
544
570
|
const designEvidence = buildDesignEvidence(discovery.paths);
|
|
545
571
|
if (discovery.paths.length)
|
|
546
572
|
evidencePaths.push(...discovery.paths.slice(0, 5));
|
|
@@ -35,8 +35,15 @@ const HEADING_ALIASES = {
|
|
|
35
35
|
assumptions: new Set(["假设", "assumptions"]),
|
|
36
36
|
};
|
|
37
37
|
const OBJECTIVE_MAX_CHARS = 1200;
|
|
38
|
-
/**
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Product Requirement V3/V4 style embedded AC heading, e.g. AC-FE-001 /
|
|
40
|
+
* AC-BE-12 / AC-001 — and catalog-qualified feature segments such as
|
|
41
|
+
* AC-FE-INS-001 / AC-FE-CHAT-005 (uppercase letter/digit segments).
|
|
42
|
+
* Catalog ids embedded in bold (“**AC-FE-INS-001**:…”) must bind as-is;
|
|
43
|
+
* falling through to a minted AC-00N historically produced dual ids and
|
|
44
|
+
* acceptanceCoverage.missing noise (2026-08-23 fe-test loop finding 5).
|
|
45
|
+
*/
|
|
46
|
+
const EMBEDDED_AC_HEADING = /^(AC(?:[-_](?:FE|BE|UI|API))?[-_]?[A-Z0-9]+(?:[-_][A-Z0-9]+)*)\s*[::]?\s*(.*)$/u;
|
|
40
47
|
function stripFencedAndComments(text) {
|
|
41
48
|
// Remove fenced code blocks and HTML comments so headings inside them are ignored.
|
|
42
49
|
return text
|
|
@@ -52,12 +59,25 @@ function normalizeAcceptanceId(raw) {
|
|
|
52
59
|
.replace(/^(AC)(FE|BE|UI|API)(?=-|\d)/u, "$1-$2");
|
|
53
60
|
}
|
|
54
61
|
function parseEmbeddedAcHeading(headingText) {
|
|
55
|
-
const normalized = headingText
|
|
62
|
+
const normalized = headingText
|
|
63
|
+
.trim()
|
|
64
|
+
// Strip leading section numbers ("2.1、" style) and bold/emphasis
|
|
65
|
+
// wrappers ("**AC-FE-INS-001**") so embedded catalog ids bind.
|
|
66
|
+
.replace(/^(?:\d+(?:\.\d+)*[..、]?\s*)+/u, "")
|
|
67
|
+
.replace(/^\*\*|\*\*$/g, "")
|
|
68
|
+
.replace(/^__|__$/g, "")
|
|
69
|
+
.trim();
|
|
56
70
|
const match = EMBEDDED_AC_HEADING.exec(normalized);
|
|
57
71
|
if (!match)
|
|
58
72
|
return null;
|
|
59
73
|
const id = normalizeAcceptanceId(match[1]);
|
|
60
|
-
|
|
74
|
+
// Bold-wrapped ids leave their closing marker inside the captured text
|
|
75
|
+
// ("**AC-FE-INS-001**:xxx" captures "**:xxx"); strip emphasis remnants
|
|
76
|
+
// and leading separators so the bound text is clean prose.
|
|
77
|
+
const text = (match[2] ?? "")
|
|
78
|
+
.replace(/^[*_]+\s*/, "")
|
|
79
|
+
.replace(/^\s*[::]\s*/, "")
|
|
80
|
+
.trim() || id;
|
|
61
81
|
return { id, text };
|
|
62
82
|
}
|
|
63
83
|
function normalizeHeadingText(raw) {
|
|
@@ -5,6 +5,8 @@ import { ContractMutationError, applyTaskContract, logicalRevisionForState, obse
|
|
|
5
5
|
import { TASK_CONTRACT_DRAFT_SCHEMA_VERSION } from "../contract/constants.js";
|
|
6
6
|
import { getTaskContractPaths } from "../contract/paths.js";
|
|
7
7
|
import { loadTaskConfig } from "../runtime.js";
|
|
8
|
+
import { resolveFrontendSpecRootAliases } from "../frontend-project-capability.js";
|
|
9
|
+
import { DEFAULT_FRONTEND_SPEC_ROOTS, extractDeclaredFrontendSpecRoots, } from "../../shared/openspec-spec.js";
|
|
8
10
|
import { buildPrepareDraft } from "./build-draft.js";
|
|
9
11
|
import { hasBlockingGaps, hasBlockingRisks, isFreshLegacyUnversioned, listPrepareGaps, } from "./completeness.js";
|
|
10
12
|
import { detectProductArtifactMeta, isIntakeSoftGapCode, } from "./artifact-meta.js";
|
|
@@ -591,6 +593,40 @@ export async function prepareTaskSource(input) {
|
|
|
591
593
|
flags: input.flags,
|
|
592
594
|
references,
|
|
593
595
|
});
|
|
596
|
+
// A user can explicitly name a non-default normative directory in the task
|
|
597
|
+
// prompt (for example “规范目录 `design-system`”). Freeze that root into
|
|
598
|
+
// the managed task config before the DAG exists; runtime selectors never
|
|
599
|
+
// mutate task.json.
|
|
600
|
+
if (built.draft.taskKind === "frontend-implementation") {
|
|
601
|
+
// An imported PRD is materialized under sourceIntegrity rather than copied
|
|
602
|
+
// into intent.text. Use both inputs so a `--prd` invocation can declare a
|
|
603
|
+
// task-owned spec root just like supplemental free-form text can.
|
|
604
|
+
const sourceText = [
|
|
605
|
+
...(sourceIntegrity?.documents ?? []).flatMap((document) => document.content ? [document.content] : []),
|
|
606
|
+
...(input.intent.kind === "facts" && input.intent.text
|
|
607
|
+
? [input.intent.text]
|
|
608
|
+
: []),
|
|
609
|
+
].join("\n");
|
|
610
|
+
const inferredRoots = extractDeclaredFrontendSpecRoots(sourceText);
|
|
611
|
+
if (inferredRoots.length > 0) {
|
|
612
|
+
const current = existingTaskConfig?.frontendOpenspec;
|
|
613
|
+
const configuredRoots = current?.specRoots ?? [...DEFAULT_FRONTEND_SPEC_ROOTS];
|
|
614
|
+
// Include roots declared by this source before resolving physical-target
|
|
615
|
+
// aliases. A newly declared root may itself be a symlink (for example
|
|
616
|
+
// `project-specs -> .../ai_doc`), so resolving aliases against only the
|
|
617
|
+
// previous config cannot map `ai_doc/rules.md` back to project-specs.
|
|
618
|
+
const provisionalRoots = [
|
|
619
|
+
...new Set([...configuredRoots, ...inferredRoots]),
|
|
620
|
+
].sort();
|
|
621
|
+
const aliases = await resolveFrontendSpecRootAliases(repoRoot, provisionalRoots);
|
|
622
|
+
const aliasMap = new Map(aliases.map(({ alias, logicalRoot }) => [alias, logicalRoot]));
|
|
623
|
+
built.draft.frontendOpenspec = {
|
|
624
|
+
policy: current?.policy ?? "cited",
|
|
625
|
+
specRoots: [...new Set([...configuredRoots, ...inferredRoots.map((root) => aliasMap.get(root) ?? root)])].sort(),
|
|
626
|
+
requiredReadPaths: current?.requiredReadPaths ?? [],
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
}
|
|
594
630
|
const { gaps, risks } = listPrepareGaps({
|
|
595
631
|
draft: built.draft,
|
|
596
632
|
state,
|
package/dist/worker/cli.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { Command } from "commander";
|
|
7
|
+
import { createNpmUpdateClient } from "../shared/update/npm-client.js";
|
|
8
|
+
import { runConsoleDailyUpdateCheck } from "../shared/update/console-notifier.js";
|
|
6
9
|
import { readPackageVersion } from "../shared/package-metadata.js";
|
|
7
10
|
import YAML from "yaml";
|
|
8
11
|
import { LoopAgentClient } from "./loop-agent/loop-agent-client.js";
|
|
@@ -838,10 +841,33 @@ function parseOptionalTimeoutMs(raw) {
|
|
|
838
841
|
return value;
|
|
839
842
|
}
|
|
840
843
|
/** Console HTTP entry for bare `agent-worker console` (listen first, then optional browser open). */
|
|
841
|
-
async function runConsoleServe(options) {
|
|
844
|
+
export async function runConsoleServe(options) {
|
|
842
845
|
const { createConsoleServer } = await import("./console/server.js");
|
|
843
846
|
const { maybeOpenConsoleBrowser } = await import("./console/open-browser.js");
|
|
844
847
|
try {
|
|
848
|
+
try {
|
|
849
|
+
const packageRoot = path.dirname(path.dirname(path.dirname(fileURLToPath(import.meta.url))));
|
|
850
|
+
const currentVersion = readPackageVersion(packageRoot) ?? "0.0.0";
|
|
851
|
+
await runConsoleDailyUpdateCheck({
|
|
852
|
+
currentVersion,
|
|
853
|
+
packageRoot,
|
|
854
|
+
homeDir: os.homedir(),
|
|
855
|
+
cwd: options.repoRoot,
|
|
856
|
+
env: process.env,
|
|
857
|
+
stdinIsTTY: process.stdin.isTTY === true,
|
|
858
|
+
stderrIsTTY: process.stderr.isTTY === true,
|
|
859
|
+
client: createNpmUpdateClient({
|
|
860
|
+
currentVersion,
|
|
861
|
+
packageRoot,
|
|
862
|
+
cwd: options.repoRoot,
|
|
863
|
+
env: process.env,
|
|
864
|
+
}),
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
catch (error) {
|
|
868
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
869
|
+
process.stderr.write(`loop-agent 更新检查已跳过:${detail}\n`);
|
|
870
|
+
}
|
|
845
871
|
const server = await createConsoleServer({
|
|
846
872
|
repoRoot: options.repoRoot,
|
|
847
873
|
host: options.host,
|
|
@@ -857,6 +883,10 @@ async function runConsoleServe(options) {
|
|
|
857
883
|
noOpen: options.noOpen === true,
|
|
858
884
|
serverUrl: server.url,
|
|
859
885
|
});
|
|
886
|
+
if (options.holdUntilSignal === false) {
|
|
887
|
+
await server.close();
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
860
890
|
await new Promise((resolve) => {
|
|
861
891
|
const shutdown = () => resolve();
|
|
862
892
|
process.once("SIGINT", shutdown);
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { open, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
/** 新建模型时,未显式提供的字段使用的默认值(仅含 6 个定义参数)。 */
|
|
5
|
+
const DEFAULT_MODEL_FIELDS = {
|
|
6
|
+
contextWindow: 32000,
|
|
7
|
+
maxTokens: 8192,
|
|
8
|
+
reasoning: false,
|
|
9
|
+
input: ["text"],
|
|
10
|
+
};
|
|
4
11
|
const revision = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
5
12
|
export async function readModelsConfig(agentDir) {
|
|
6
13
|
let raw = "{}";
|
|
@@ -29,6 +36,18 @@ export async function readModelsConfig(agentDir) {
|
|
|
29
36
|
return {
|
|
30
37
|
id: String(item.id ?? ""),
|
|
31
38
|
name: typeof item.name === "string" ? item.name : undefined,
|
|
39
|
+
contextWindow: typeof item.contextWindow === "number"
|
|
40
|
+
? item.contextWindow
|
|
41
|
+
: undefined,
|
|
42
|
+
maxTokens: typeof item.maxTokens === "number"
|
|
43
|
+
? item.maxTokens
|
|
44
|
+
: undefined,
|
|
45
|
+
input: Array.isArray(item.input)
|
|
46
|
+
? item.input.filter((entry) => typeof entry === "string")
|
|
47
|
+
: undefined,
|
|
48
|
+
reasoning: typeof item.reasoning === "boolean"
|
|
49
|
+
? item.reasoning
|
|
50
|
+
: undefined,
|
|
32
51
|
};
|
|
33
52
|
})
|
|
34
53
|
.filter((model) => model.id)
|
|
@@ -81,19 +100,25 @@ export async function patchModelsConfig(agentDir, input) {
|
|
|
81
100
|
code: "INVALID_INPUT",
|
|
82
101
|
status: 400,
|
|
83
102
|
});
|
|
103
|
+
const fields = validateModelFields(input.model);
|
|
104
|
+
const existingIndex = models.findIndex((model) => model.id === input.model.id);
|
|
105
|
+
const merged = {
|
|
106
|
+
// id/name 在前;新建模型用默认字段补齐(保证定义完整),更新保留已有值。
|
|
107
|
+
id: input.model.id,
|
|
108
|
+
...(fields.name !== undefined ? { name: fields.name } : {}),
|
|
109
|
+
...(existingIndex >= 0 ? models[existingIndex] : DEFAULT_MODEL_FIELDS),
|
|
110
|
+
...fields,
|
|
111
|
+
};
|
|
84
112
|
provider.models =
|
|
85
113
|
input.action === "remove-model"
|
|
86
114
|
? models.filter((model) => model.id !== input.model.id)
|
|
87
|
-
:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: {}),
|
|
95
|
-
},
|
|
96
|
-
];
|
|
115
|
+
: existingIndex >= 0
|
|
116
|
+
? [
|
|
117
|
+
...models.slice(0, existingIndex),
|
|
118
|
+
merged,
|
|
119
|
+
...models.slice(existingIndex + 1),
|
|
120
|
+
]
|
|
121
|
+
: [...models, merged];
|
|
97
122
|
}
|
|
98
123
|
const body = `${JSON.stringify(parsed, null, 2)}\n`;
|
|
99
124
|
const tmp = path.join(agentDir, `.models.${process.pid}.${randomBytes(4).toString("hex")}.tmp`);
|
|
@@ -108,6 +133,52 @@ export async function patchModelsConfig(agentDir, input) {
|
|
|
108
133
|
await rename(tmp, file);
|
|
109
134
|
return readModelsConfig(agentDir);
|
|
110
135
|
}
|
|
136
|
+
/** Whitelist + validate model fields carried by a client upsert. Never accept
|
|
137
|
+
* arbitrary keys; only known numeric/boolean/string-array shape fields. */
|
|
138
|
+
function validateModelFields(model) {
|
|
139
|
+
const fields = {};
|
|
140
|
+
if (model.name !== undefined)
|
|
141
|
+
fields.name = String(model.name);
|
|
142
|
+
if (model.contextWindow !== undefined) {
|
|
143
|
+
if (typeof model.contextWindow !== "number" || model.contextWindow < 0)
|
|
144
|
+
throw invalid("contextWindow must be a non-negative number");
|
|
145
|
+
fields.contextWindow = model.contextWindow;
|
|
146
|
+
}
|
|
147
|
+
if (model.maxTokens !== undefined) {
|
|
148
|
+
if (typeof model.maxTokens !== "number" || model.maxTokens < 0)
|
|
149
|
+
throw invalid("maxTokens must be a non-negative number");
|
|
150
|
+
fields.maxTokens = model.maxTokens;
|
|
151
|
+
}
|
|
152
|
+
if (model.reasoning !== undefined) {
|
|
153
|
+
if (typeof model.reasoning !== "boolean")
|
|
154
|
+
throw invalid("reasoning must be a boolean");
|
|
155
|
+
fields.reasoning = model.reasoning;
|
|
156
|
+
}
|
|
157
|
+
if (model.input !== undefined) {
|
|
158
|
+
if (!Array.isArray(model.input) ||
|
|
159
|
+
!model.input.every((item) => typeof item === "string"))
|
|
160
|
+
throw invalid("input must be an array of strings");
|
|
161
|
+
fields.input = model.input.map((item) => String(item));
|
|
162
|
+
}
|
|
163
|
+
if (model.cost !== undefined) {
|
|
164
|
+
if (!model.cost || typeof model.cost !== "object")
|
|
165
|
+
throw invalid("cost must be an object");
|
|
166
|
+
const cost = {};
|
|
167
|
+
for (const key of ["input", "output", "cacheRead", "cacheWrite"]) {
|
|
168
|
+
const value = model.cost[key];
|
|
169
|
+
if (value !== undefined) {
|
|
170
|
+
if (typeof value !== "number" || value < 0)
|
|
171
|
+
throw invalid(`cost.${key} must be a non-negative number`);
|
|
172
|
+
cost[key] = value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
fields.cost = cost;
|
|
176
|
+
}
|
|
177
|
+
return fields;
|
|
178
|
+
}
|
|
179
|
+
function invalid(message) {
|
|
180
|
+
return Object.assign(new Error(message), { code: "INVALID_INPUT", status: 400 });
|
|
181
|
+
}
|
|
111
182
|
/** Max bytes of a package.json we are willing to read (read-only, bounded). */
|
|
112
183
|
export const MAX_PACKAGE_JSON_BYTES = 64 * 1024;
|
|
113
184
|
/** Max directory levels walked upward from an owned resolved resource path. */
|
|
@@ -270,6 +270,7 @@ export function parseChatImages(value) {
|
|
|
270
270
|
}
|
|
271
271
|
function gateMutation(req, deps) {
|
|
272
272
|
const failure = evaluateMutationGate(req, {
|
|
273
|
+
cookieName: deps.bootToken.cookieName,
|
|
273
274
|
bootToken: deps.bootToken.value,
|
|
274
275
|
confirmationToken: deps.bootToken.confirmationToken,
|
|
275
276
|
consoleOrigin: deps.getConsoleOrigin(),
|
|
@@ -35,6 +35,32 @@ import { hashDagBytes, } from "./dag-confirmation.js";
|
|
|
35
35
|
* framework invocations that don't embed the word "verify".
|
|
36
36
|
*/
|
|
37
37
|
export const STRUCTURED_VERIFICATION_COMMAND_RE = /\b(vitest|jest|playwright|cypress|pytest|py-test|go\s+test|cargo\s+test|npm\s+run\s+(lint|typecheck|test|verify)|npm\s+(test|run\s+test)|check-repo\.sh|loop-agent-standard-verify)\b/i;
|
|
38
|
+
/**
|
|
39
|
+
* Deterministic shell preset keys whose executors are themselves structured
|
|
40
|
+
* verification/gate materializers: their shell.commands[] is empty and the
|
|
41
|
+
* preset alone carries the verification authority (hash-bound artifact
|
|
42
|
+
* gates, evidence validators, report finalize). Mirrors the named-shell
|
|
43
|
+
* handler registry in shell-executor.ts so preset-only workflows
|
|
44
|
+
* (frontend-test finalize/reports, backend-test pipeline, verify-evidence
|
|
45
|
+
* gates) pass the G2 autonomous execution gate without depending on node-id
|
|
46
|
+
* naming conventions.
|
|
47
|
+
*/
|
|
48
|
+
export const STRUCTURED_VERIFICATION_SHELL_PRESETS = new Set([
|
|
49
|
+
"frontendTestCaseChecklist",
|
|
50
|
+
"frontendTestCaseManifest",
|
|
51
|
+
"frontendTestEvidenceValidation",
|
|
52
|
+
"frontendTestResultFinalize",
|
|
53
|
+
"frontendTestL5Report",
|
|
54
|
+
"frontendTestHtmlReport",
|
|
55
|
+
"frontendTestReports",
|
|
56
|
+
"backendTestPipeline",
|
|
57
|
+
"verifyEvidence",
|
|
58
|
+
"repairArtifactGate",
|
|
59
|
+
"jacocoCoverage",
|
|
60
|
+
"requirementCoverageGate",
|
|
61
|
+
"jsonArtifactGate",
|
|
62
|
+
"verdictGate",
|
|
63
|
+
]);
|
|
38
64
|
export const DEFAULT_EXECUTION_RECEIPT_TTL_MS = 30 * 60 * 1000;
|
|
39
65
|
function normalizePathEntry(entry) {
|
|
40
66
|
return entry.trim().replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -157,6 +183,13 @@ export function deriveEligibilityFactsFromDagSpec(input) {
|
|
|
157
183
|
return false;
|
|
158
184
|
if (/verify|verification/i.test(task.id ?? ""))
|
|
159
185
|
return true;
|
|
186
|
+
// Preset-only verification shells (empty commands[]): the preset key is
|
|
187
|
+
// the verification authority (frontend-test finalize, backend-test
|
|
188
|
+
// pipeline, evidence/artifact gates).
|
|
189
|
+
const shellRecord = task.shell;
|
|
190
|
+
const hasVerificationPreset = Object.keys(shellRecord).some((key) => STRUCTURED_VERIFICATION_SHELL_PRESETS.has(key));
|
|
191
|
+
if (hasVerificationPreset)
|
|
192
|
+
return true;
|
|
160
193
|
const commands = stringList(task.shell.commands);
|
|
161
194
|
return commands.some((command) => STRUCTURED_VERIFICATION_COMMAND_RE.test(command));
|
|
162
195
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { parseFrontendHumanDecisionProjection } from "../../workflows/dag/frontend-human-decision.js";
|
|
2
|
+
/** Worker/Console persistence seam. Canonical validation and freshness remain in the workflow service. */
|
|
3
|
+
export function createFrontendHumanDecisionAdapter(store) {
|
|
4
|
+
return {
|
|
5
|
+
async load(operationId) {
|
|
6
|
+
const record = await store.get(operationId);
|
|
7
|
+
if (!record || record.revision === undefined || !record.frontendHumanDecision)
|
|
8
|
+
return undefined;
|
|
9
|
+
return { revision: record.revision, projection: parseFrontendHumanDecisionProjection(record.frontendHumanDecision) };
|
|
10
|
+
},
|
|
11
|
+
materialize(input) {
|
|
12
|
+
return store.materializeFrontendHumanDecision(input);
|
|
13
|
+
},
|
|
14
|
+
consume(input) {
|
|
15
|
+
return store.consumeFrontendHumanDecision(input);
|
|
16
|
+
},
|
|
17
|
+
initializeSplit: store.initializeFrontendHumanApprovedSplit.bind(store),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FrontendSplitOrchestrator, frontendSplitProjectionSchema, } from "../../workflows/dag/frontend-split-application-service.js";
|
|
2
|
+
/** Worker/Console boundary adapter. All orchestration policy remains canonical. */
|
|
3
|
+
export function createFrontendSplitOperationAdapter(dependencies) {
|
|
4
|
+
const ports = {
|
|
5
|
+
load: async (operationId) => {
|
|
6
|
+
const record = await dependencies.store.get(operationId);
|
|
7
|
+
return record?.frontendSplit === undefined ? undefined : frontendSplitProjectionSchema.parse(record.frontendSplit);
|
|
8
|
+
},
|
|
9
|
+
compareAndSwap: (input) => dependencies.store.compareAndSwapFrontendSplit(input),
|
|
10
|
+
materialize: async (request) => {
|
|
11
|
+
const result = await dependencies.materializer.materialize(request);
|
|
12
|
+
return { taskId: result.taskId, operationId: result.operationId };
|
|
13
|
+
},
|
|
14
|
+
schedule: dependencies.schedule,
|
|
15
|
+
auditStale: dependencies.auditStale,
|
|
16
|
+
loadChildCloseoutFacts: (input) => dependencies.store.loadChildCloseoutFacts(input),
|
|
17
|
+
settleAggregate: (input) => dependencies.store.settleFrontendSplitAggregate({ ...input, prepareAggregate: dependencies.prepareAggregate }),
|
|
18
|
+
};
|
|
19
|
+
return new FrontendSplitOrchestrator(ports);
|
|
20
|
+
}
|
|
@@ -11,6 +11,9 @@ export { createClosedResourceLoader, createInterviewResourceLoader, tryActivateT
|
|
|
11
11
|
export { resolveSiblingLoopAgentBin } from "./sibling-controller.js";
|
|
12
12
|
export { openConsoleAppData, defaultConsoleAppDataRoot } from "./app-data.js";
|
|
13
13
|
export { OperationStore } from "./operation-store.js";
|
|
14
|
+
export { createFrontendSplitOperationAdapter } from "./frontend-split-operation-adapter.js";
|
|
15
|
+
export { createFrontendHumanDecisionAdapter } from "./frontend-human-decision-adapter.js";
|
|
16
|
+
export { FrontendSplitTaskMaterializer } from "../materialize/frontend-split-task-materializer.js";
|
|
14
17
|
export { createOperationEventStore } from "./operation-sse.js";
|
|
15
18
|
export { DagConfirmationStore } from "./dag-confirmation.js";
|
|
16
19
|
export { DraftStore } from "./draft-store.js";
|