@tea-agent/loop-agent 0.38.2 → 0.38.4
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 +171 -96
- package/README.md +11 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/advance.js +4 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +76 -10
- package/dist/executors/pi-event-serializer.js +42 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +51 -12
- package/dist/executors/pi-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +265 -15
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- 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/contract/constants.js +1 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/project.js +5 -0
- package/dist/task/contract/schema.js +6 -1
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/operator-actions.js +116 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +4 -4
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +36 -5
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4bmo3iY.js → abnfDiagram-N423BO3Z-gauhD9kD.js} +1 -1
- package/dist/worker/console/static/assets/{arc-SAD7McOS.js → arc-pm3c2Rsl.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DAVLUdK1.js → architectureDiagram-T3A2C74G-D_JAg3wH.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CPPdv4vj.js → blockDiagram-VBNYF7ZC-Dk7-ex52.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-D1GRGZax.js → c4Diagram-5PPSVZJV-Uval9oRI.js} +1 -1
- package/dist/worker/console/static/assets/channel-CVrnUdZl.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D_Bq2ZqK.js → chunk-2GRJ4B5K-BSYjHs-v.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CPLVtamK.js → chunk-2Q5K7J3B-KGUuBXI8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DnaZWcL3.js → chunk-5RXB4S5H-JqAtxD9N.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BRTmFgnq.js → chunk-5VM5RSS4-D6EJOVQK.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BRlfYZoH.js → chunk-6Q2QTUOP-B1JriSjm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjNybD4u.js → chunk-GF5L2VYU-D62b96Jd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-D3e-74fG.js → chunk-JWPE2WC7-BsNKEC_r.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Br3ir6f2.js → chunk-KBJHAD2P-B56v6-ZP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-1NgVuRbh.js → chunk-RYQCIY6F-D2KPnd8K.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BTYuvuWN.js → chunk-XXDRQBXY-kzKSCaaL.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BC6Bgosf.js → cose-bilkent-JH36ORCC-C6N6KNJd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DV-HypfL.js → cynefin-VYW2F7L2-DiyymT2D.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DhTaddO-.js → cynefinDiagram-MW4NZA55-BeKAXvf3.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BnEcOmiI.js → dagre-VZM6K2ZE-TkIdXfPF.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-xphGRVKp.js → diagram-7IWD3JNH-BFmSG3ks.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CrdD1mRG.js → diagram-B4RE2ZJO-DqpJT2Fs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-D4YdLsaF.js → diagram-LBJQPF4R-r5ATN43t.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-LCKyGZ5U.js → diagram-Q27KOJAE-DEcSCMuR.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BeiTe7t0.js → diagram-UB23O5K3-VOvYRLVc.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BO2cq8GS.js → ebnfDiagram-BXEA7PRR-D7f4Cb1u.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CmWww3Nt.js → erDiagram-JOGREHBK-ziUf3U0G.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-C2kkUYg5.js → flowDiagram-UKHOOZJN-BoRdQ-1y.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DY_JZ2iQ.js → ganttDiagram-PKOTCBZU-B1-GncNm.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BizhCSw9.js → gitGraphDiagram-DS77QQ5N-CTW-ph8W.js} +1 -1
- package/dist/worker/console/static/assets/index-C8rOIAJr.css +1 -0
- package/dist/worker/console/static/assets/{index-BXN89VPX.js → index-DEbyrEfW.js} +147 -68
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Dlo2eK_d.js → infoDiagram-6WML65LV-BFA337tZ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-3EIlIIuZ.js → ishikawaDiagram-WSZJBQD7-BtEujgRR.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DiMDe5Yh.js → journeyDiagram-NVQOT4AX-C8ZwD2Jp.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-sN553oIN.js → kanban-definition-27J2QSJJ-Ck-z8B11.js} +1 -1
- package/dist/worker/console/static/assets/{linear-D9Py31Ld.js → linear-CAyXjQjN.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-D7Occzk_.js → mermaid.core-CcWKn6tS.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DjDRboiR.js → mindmap-definition-FAOFIHXS-C2NuDO7E.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Cgc4qlzp.js → pegDiagram-VL7TDLO6-BFub3PDz.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DxZj8pKd.js → pieDiagram-7S7Q4E2Y-BJx5THZ5.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CJ50MngR.js → quadrantDiagram-CIZ2JOQS-DhalkDWV.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BY-OD8Tr.js → railroadDiagram-AXF67PYL-CVcBM36o.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CQFButfK.js → requirementDiagram-LRYGKXZP-D_vy4n0w.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BB_QUK_b.js → sankeyDiagram-W5VNT64P-DGYdJL1D.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CqhXZjyx.js → sequenceDiagram-SI44F4Z6-C1xJFg3k.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-2l1U4rgJ.js → sizeCapture-X5ZJPWSS-D_Fc43df.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BX1FbrhJ.js → stateDiagram-OKZ733FA-BhGbIjbu.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DUt1lyFL.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DOSnT7hb.js → swimlanes-SLNWSIFB-DVkNcO-V.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BM6SGr7j.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DVJQA7AN.js → timeline-definition-Z64GVDOM-BQU9ufV0.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-74uxl9gx.js → vennDiagram-T6HMQDX7-BCQhRmYB.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DsBxlcg_.js → wardleyDiagram-T6FBY63Y-DWerOyHO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-snuW9Mjw.js → xychartDiagram-ELKLHX3M-DxLeebQU.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- 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 +76 -6
- package/dist/workflows/dag/backend-test-scenario-partitions.js +73 -1
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/failure-category.js +12 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +145 -40
- package/dist/workflows/dag/frontend-prewrite-gate.js +87 -135
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- 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-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +196 -117
- package/dist/workflows/dag/node-execution.js +139 -10
- package/dist/workflows/dag/prompt.js +15 -1
- package/dist/workflows/dag/rerun-plan.js +134 -3
- package/dist/workflows/dag/rerun-run.js +58 -6
- package/dist/workflows/dag/retry-policy.js +91 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +32 -1
- package/dist/workflows/dag/validate.js +41 -1
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +1 -5
- package/docs/skills/vetted-skill-registry.md +14 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/backend-test-dag.json +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +2 -2
- package/package.json +12 -4
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/analyze-product-requirements/SKILL.md +8 -5
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
- package/skills/analyze-product-requirements/references/example.md +50 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
- package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/improve-codebase-architecture/deepening.md +37 -0
- package/skills/improve-codebase-architecture/html-report.md +123 -0
- package/skills/improve-codebase-architecture/interface-design.md +44 -0
- package/skills/improve-codebase-architecture/language.md +53 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +22 -0
- package/skills/playwright-cli-case-generator/SKILL.md +20 -1
- package/dist/worker/console/static/assets/channel-R6oQIDkw.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BPXlmPWI.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BPXlmPWI.js +0 -1
- package/dist/worker/console/static/assets/index-CuH5CNFu.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DJKi0Qel.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-YYe7EEm5.js +0 -8
|
@@ -15,6 +15,8 @@ import { materializeBackendTestAnalysisContract } from "../workflows/dag/backend
|
|
|
15
15
|
import { extractBackendTestContractEnvelope } from "../workflows/dag/backend-test-contract-envelope.js";
|
|
16
16
|
import { materializeFrontendImplementationContract } from "../workflows/dag/frontend-implementation-contract.js";
|
|
17
17
|
import { materializeFrontendTestResult, validateFrontendCaseEvidence, } from "../workflows/dag/frontend-test-result-contract.js";
|
|
18
|
+
import { copyFrontendTestStandardScenarios } from "../workflows/dag/frontend-test-standard-scenarios.js";
|
|
19
|
+
import { probeFrontendTestEnvironment } from "../workflows/dag/frontend-test-environment-probe.js";
|
|
18
20
|
import { renderFrontendTestL5Report } from "../workflows/dag/frontend-test-l5-report.js";
|
|
19
21
|
import { validateFrontendCaseChecklist } from "../workflows/dag/frontend-test-case-checklist.js";
|
|
20
22
|
import { materializeFrontendTestCaseManifest } from "../workflows/dag/frontend-test-case-manifest.js";
|
|
@@ -121,6 +123,39 @@ function shouldReuseSuccessfulShellCommands(shell) {
|
|
|
121
123
|
const DEFAULT_SHELL_TIMEOUT_MS = 300_000;
|
|
122
124
|
const SUMMARY_STDOUT_MAX = 4_000;
|
|
123
125
|
const SUMMARY_STDERR_MAX = 2_000;
|
|
126
|
+
const TEST_TIMEOUT_CLASSIFY_MAX_CHARS = 8_000;
|
|
127
|
+
const TEST_TIMEOUT_MARKERS = [
|
|
128
|
+
/Test timed out in \d+ms/i,
|
|
129
|
+
/Timeout of \d+ms exceeded/i,
|
|
130
|
+
];
|
|
131
|
+
const DETERMINISTIC_NON_TIMEOUT_FAILURE_MARKERS = [
|
|
132
|
+
/AssertionError/i,
|
|
133
|
+
/expected:?\s/i,
|
|
134
|
+
/received:?\s/i,
|
|
135
|
+
/Snapshot /i,
|
|
136
|
+
/toMatchSnapshot/i,
|
|
137
|
+
/SyntaxError/i,
|
|
138
|
+
/TS\d{4}/,
|
|
139
|
+
/error TS/i,
|
|
140
|
+
/eslint/i,
|
|
141
|
+
/compile error/i,
|
|
142
|
+
/Failed to compile/i,
|
|
143
|
+
];
|
|
144
|
+
function isIdentifiedTestCommand(command) {
|
|
145
|
+
return /(?:^|[\s"'`])(?:npx\s+)?vitest(?:\s|$)|(?:^|[\s"'`])npm(?:\.cmd)?\s+test(?:\s|$)|(?:^|[\s"'`])pnpm(?:\.cmd)?\s+test(?:\s|$)|(?:^|[\s"'`])yarn(?:\.cmd)?\s+test(?:\s|$)/i.test(command);
|
|
146
|
+
}
|
|
147
|
+
function classifyShellCommandFailure(input) {
|
|
148
|
+
if (input.processCategory !== "nonzero-exit")
|
|
149
|
+
return input.processCategory;
|
|
150
|
+
if (!isIdentifiedTestCommand(input.command))
|
|
151
|
+
return "nonzero-exit";
|
|
152
|
+
const sample = `${input.stdout}\n${input.stderr}`.slice(0, TEST_TIMEOUT_CLASSIFY_MAX_CHARS);
|
|
153
|
+
const hasTimeoutMarker = TEST_TIMEOUT_MARKERS.some((marker) => marker.test(sample));
|
|
154
|
+
if (!hasTimeoutMarker)
|
|
155
|
+
return "nonzero-exit";
|
|
156
|
+
const mixedDeterministicFailure = DETERMINISTIC_NON_TIMEOUT_FAILURE_MARKERS.some((marker) => marker.test(sample));
|
|
157
|
+
return mixedDeterministicFailure ? "nonzero-exit" : "test-timeout";
|
|
158
|
+
}
|
|
124
159
|
const NODE_STDOUT_MAX = 12_000;
|
|
125
160
|
const NODE_STDERR_MAX = 8_000;
|
|
126
161
|
const MAX_BUFFERED_OUTPUT_BYTES = 64 * 1024;
|
|
@@ -327,16 +362,21 @@ export async function executeShellCommand(input) {
|
|
|
327
362
|
});
|
|
328
363
|
});
|
|
329
364
|
child.on("close", (code) => {
|
|
330
|
-
let
|
|
365
|
+
let processCategory = "nonzero-exit";
|
|
331
366
|
if (timedOut) {
|
|
332
|
-
|
|
367
|
+
processCategory = "timeout";
|
|
333
368
|
}
|
|
334
369
|
else if (code === 0) {
|
|
335
|
-
|
|
370
|
+
processCategory = "success";
|
|
336
371
|
}
|
|
337
372
|
finish({
|
|
338
373
|
exitCode: code,
|
|
339
|
-
failureCategory
|
|
374
|
+
failureCategory: classifyShellCommandFailure({
|
|
375
|
+
command: input.command,
|
|
376
|
+
processCategory,
|
|
377
|
+
stdout: formatBoundedOutput(stdout),
|
|
378
|
+
stderr: formatBoundedOutput(stderr),
|
|
379
|
+
}),
|
|
340
380
|
ok: code === 0 && !timedOut,
|
|
341
381
|
});
|
|
342
382
|
});
|
|
@@ -704,7 +744,7 @@ async function executeBackendTestPipeline(input, meta) {
|
|
|
704
744
|
// coverage findings stay advisory, but a declared filter axis missing
|
|
705
745
|
// an each-value/omitted slot or its complement slot must block pytest
|
|
706
746
|
// generation until the Markdown declares the slot.
|
|
707
|
-
const partitionBlocking = coverage.facts.findings.filter((finding) => /^(?:MISSING_PARTITION|MISSING_PARTITION_SLOT|MISSING_COMPLEMENT_SLOT|MISSING_PARTITION_TABLE)/.test(finding));
|
|
747
|
+
const partitionBlocking = coverage.facts.findings.filter((finding) => /^(?:MISSING_PARTITION|MISSING_PARTITION_SLOT|MISSING_COMPLEMENT_SLOT|MISSING_PARTITION_TABLE|INVALID_PARTITION_DOMAIN|NO_FINITE_DOMAIN|PARTITION_SOURCE_MISMATCH)/.test(finding));
|
|
708
748
|
if (partitionBlocking.length > 0) {
|
|
709
749
|
return {
|
|
710
750
|
ok: false,
|
|
@@ -877,7 +917,7 @@ async function executeBackendTestPipeline(input, meta) {
|
|
|
877
917
|
});
|
|
878
918
|
}
|
|
879
919
|
else if (initial.status === "REPAIRABLE") {
|
|
880
|
-
const mappedScripts = await collectBackendTestMappedPytestScripts(input.cwd);
|
|
920
|
+
const mappedScripts = await collectBackendTestMappedPytestScripts(input.cwd, backendLayout);
|
|
881
921
|
const scenario = await runBackendScenarioParamPreflight({
|
|
882
922
|
workspaceRoot: input.cwd,
|
|
883
923
|
runDir: meta.runDir,
|
|
@@ -2061,6 +2101,52 @@ async function executeFrontendTestHtmlReport(input, meta) {
|
|
|
2061
2101
|
};
|
|
2062
2102
|
}
|
|
2063
2103
|
}
|
|
2104
|
+
async function executeFrontendTestStandardScenarios(input) {
|
|
2105
|
+
const started = Date.now();
|
|
2106
|
+
try {
|
|
2107
|
+
const result = await copyFrontendTestStandardScenarios({
|
|
2108
|
+
workspaceRoot: input.cwd,
|
|
2109
|
+
});
|
|
2110
|
+
return {
|
|
2111
|
+
ok: true,
|
|
2112
|
+
stdout: JSON.stringify(result),
|
|
2113
|
+
stderr: "",
|
|
2114
|
+
failureCategory: "success",
|
|
2115
|
+
durationMs: Date.now() - started,
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
catch (error) {
|
|
2119
|
+
return {
|
|
2120
|
+
ok: false,
|
|
2121
|
+
stdout: "",
|
|
2122
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
2123
|
+
failureCategory: "invalid-output",
|
|
2124
|
+
durationMs: Date.now() - started,
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
async function executeFrontendTestEnvironmentProbe(input) {
|
|
2129
|
+
const started = Date.now();
|
|
2130
|
+
const result = await probeFrontendTestEnvironment({
|
|
2131
|
+
workspaceRoot: input.cwd,
|
|
2132
|
+
});
|
|
2133
|
+
if (result.ok) {
|
|
2134
|
+
return {
|
|
2135
|
+
ok: true,
|
|
2136
|
+
stdout: result.stdout,
|
|
2137
|
+
stderr: "",
|
|
2138
|
+
failureCategory: "success",
|
|
2139
|
+
durationMs: Date.now() - started,
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
return {
|
|
2143
|
+
ok: false,
|
|
2144
|
+
stdout: "",
|
|
2145
|
+
stderr: result.stderr,
|
|
2146
|
+
failureCategory: "nonzero-exit",
|
|
2147
|
+
durationMs: Date.now() - started,
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2064
2150
|
async function executeFrontendTestEvidenceValidation(input) {
|
|
2065
2151
|
const started = Date.now();
|
|
2066
2152
|
try {
|
|
@@ -2300,7 +2386,76 @@ async function executeFrontendLintBaseline(input, meta) {
|
|
|
2300
2386
|
};
|
|
2301
2387
|
}
|
|
2302
2388
|
}
|
|
2303
|
-
|
|
2389
|
+
async function writeShellPresetProcessReceipt(input) {
|
|
2390
|
+
await writeDagNodeJsonArtifact(input.runDir, input.nodeId, "process-receipt.json", {
|
|
2391
|
+
schemaVersion: 1,
|
|
2392
|
+
kind: "shell-preset",
|
|
2393
|
+
preset: input.preset,
|
|
2394
|
+
ok: input.ok,
|
|
2395
|
+
durationMs: input.durationMs,
|
|
2396
|
+
steps: input.steps,
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
async function ensureShellPresetProcessReceipt(input) {
|
|
2400
|
+
const receiptPath = path.join(input.runDir, input.nodeId, "process-receipt.json");
|
|
2401
|
+
if (existsSync(receiptPath))
|
|
2402
|
+
return;
|
|
2403
|
+
if (existsSync(path.join(input.runDir, input.nodeId, "shell-command-receipt.json"))) {
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
const detail = input.result.ok ? input.result.stdout : input.result.stderr;
|
|
2407
|
+
await writeShellPresetProcessReceipt({
|
|
2408
|
+
runDir: input.runDir,
|
|
2409
|
+
nodeId: input.nodeId,
|
|
2410
|
+
preset: input.preset,
|
|
2411
|
+
ok: input.result.ok,
|
|
2412
|
+
durationMs: input.result.durationMs,
|
|
2413
|
+
steps: [
|
|
2414
|
+
{
|
|
2415
|
+
id: "run",
|
|
2416
|
+
label: input.preset,
|
|
2417
|
+
ok: input.result.ok,
|
|
2418
|
+
detail: detail?.slice(0, 8 * 1024),
|
|
2419
|
+
durationMs: input.result.durationMs,
|
|
2420
|
+
},
|
|
2421
|
+
],
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
function namedShellHandler(shell) {
|
|
2425
|
+
if (!shell)
|
|
2426
|
+
return null;
|
|
2427
|
+
const record = shell;
|
|
2428
|
+
for (const key of [
|
|
2429
|
+
"finalWriteSetApprovalGate",
|
|
2430
|
+
"frontendTestStandardScenarios",
|
|
2431
|
+
"frontendTestEnvironmentProbe",
|
|
2432
|
+
"frontendBrowserToolPreflight",
|
|
2433
|
+
"frontendPrewriteGate",
|
|
2434
|
+
"frontendLintBaseline",
|
|
2435
|
+
"frontendVerificationBundle",
|
|
2436
|
+
"frontendReviewContext",
|
|
2437
|
+
"frontendTestCaseChecklist",
|
|
2438
|
+
"frontendTestCaseManifest",
|
|
2439
|
+
"frontendTestEvidenceValidation",
|
|
2440
|
+
"frontendTestResultFinalize",
|
|
2441
|
+
"frontendTestL5Report",
|
|
2442
|
+
"frontendTestHtmlReport",
|
|
2443
|
+
"frontendTestReports",
|
|
2444
|
+
"backendTestPipeline",
|
|
2445
|
+
"jsonArtifactGate",
|
|
2446
|
+
"projectGovernanceGate",
|
|
2447
|
+
"requirementCoverageGate",
|
|
2448
|
+
"verdictGate",
|
|
2449
|
+
"verifyEvidence",
|
|
2450
|
+
"repairArtifactGate",
|
|
2451
|
+
"jacocoCoverage",
|
|
2452
|
+
]) {
|
|
2453
|
+
if (record[key] != null)
|
|
2454
|
+
return key;
|
|
2455
|
+
}
|
|
2456
|
+
return null;
|
|
2457
|
+
}
|
|
2458
|
+
async function executeDagShellNodeInner(input, meta) {
|
|
2304
2459
|
const shell = input.task.shell;
|
|
2305
2460
|
if (shell?.finalWriteSetApprovalGate) {
|
|
2306
2461
|
const started = Date.now();
|
|
@@ -2339,30 +2494,89 @@ export async function executeDagShellNode(input, meta) {
|
|
|
2339
2494
|
};
|
|
2340
2495
|
}
|
|
2341
2496
|
}
|
|
2497
|
+
if (shell?.frontendTestStandardScenarios) {
|
|
2498
|
+
return executeFrontendTestStandardScenarios(input);
|
|
2499
|
+
}
|
|
2500
|
+
if (shell?.frontendTestEnvironmentProbe) {
|
|
2501
|
+
return executeFrontendTestEnvironmentProbe(input);
|
|
2502
|
+
}
|
|
2342
2503
|
if (shell?.frontendBrowserToolPreflight) {
|
|
2343
2504
|
const started = Date.now();
|
|
2505
|
+
const steps = [];
|
|
2506
|
+
const finish = async (result) => {
|
|
2507
|
+
await writeShellPresetProcessReceipt({
|
|
2508
|
+
runDir: meta.runDir,
|
|
2509
|
+
nodeId: input.task.id,
|
|
2510
|
+
preset: "frontendBrowserToolPreflight",
|
|
2511
|
+
ok: result.ok,
|
|
2512
|
+
durationMs: result.durationMs,
|
|
2513
|
+
steps: steps.length > 0
|
|
2514
|
+
? steps
|
|
2515
|
+
: [
|
|
2516
|
+
{
|
|
2517
|
+
id: "run",
|
|
2518
|
+
label: "浏览器工具能力探测",
|
|
2519
|
+
ok: result.ok,
|
|
2520
|
+
detail: result.ok ? result.stdout : result.stderr,
|
|
2521
|
+
durationMs: result.durationMs,
|
|
2522
|
+
},
|
|
2523
|
+
],
|
|
2524
|
+
});
|
|
2525
|
+
return result;
|
|
2526
|
+
};
|
|
2344
2527
|
try {
|
|
2345
2528
|
if (resolvePiBackend() === "cli-only") {
|
|
2529
|
+
steps.push({
|
|
2530
|
+
id: "pi-backend",
|
|
2531
|
+
label: "Pi SDK 后端可用",
|
|
2532
|
+
ok: false,
|
|
2533
|
+
detail: "CODE_AGENT_PI_BACKEND=cli-only",
|
|
2534
|
+
});
|
|
2346
2535
|
throw new Error("browser-command-capability-unavailable: CODE_AGENT_PI_BACKEND=cli-only rejects SDK-only playwright_cli");
|
|
2347
2536
|
}
|
|
2537
|
+
steps.push({
|
|
2538
|
+
id: "pi-backend",
|
|
2539
|
+
label: "Pi SDK 后端可用",
|
|
2540
|
+
ok: true,
|
|
2541
|
+
});
|
|
2348
2542
|
const sdkCapability = await checkPiSdkCustomToolCapability(input.cwd);
|
|
2349
2543
|
if (!sdkCapability.ok) {
|
|
2544
|
+
steps.push({
|
|
2545
|
+
id: "sdk-custom-tool",
|
|
2546
|
+
label: "Pi SDK custom-tool 能力",
|
|
2547
|
+
ok: false,
|
|
2548
|
+
detail: sdkCapability.detail,
|
|
2549
|
+
});
|
|
2350
2550
|
throw new Error(`browser-command-capability-unavailable: ${sdkCapability.detail}`);
|
|
2351
2551
|
}
|
|
2552
|
+
steps.push({
|
|
2553
|
+
id: "sdk-custom-tool",
|
|
2554
|
+
label: "Pi SDK custom-tool 能力",
|
|
2555
|
+
ok: true,
|
|
2556
|
+
});
|
|
2352
2557
|
const toolProbe = await createPlaywrightCliTool({
|
|
2353
2558
|
repoRoot: input.cwd,
|
|
2354
2559
|
runDir: meta.runDir,
|
|
2355
2560
|
nodeId: input.task.id,
|
|
2356
2561
|
caseId: "frontend-browser-preflight",
|
|
2357
2562
|
evidenceDir: "testcase/frontend/evidence/frontend-browser-preflight",
|
|
2358
|
-
// Capability-only registration probe. The tested URL is selected later by
|
|
2359
|
-
// retrieve-frontend-test-context-pi and validated by the environment shell.
|
|
2360
2563
|
baseUrl: "http://localhost:5173/",
|
|
2361
2564
|
inputRoot: "testcase/frontend/fixtures",
|
|
2362
2565
|
});
|
|
2363
2566
|
if (toolProbe.name !== "playwright_cli") {
|
|
2567
|
+
steps.push({
|
|
2568
|
+
id: "structured-tool",
|
|
2569
|
+
label: "注册 playwright_cli",
|
|
2570
|
+
ok: false,
|
|
2571
|
+
detail: toolProbe.name,
|
|
2572
|
+
});
|
|
2364
2573
|
throw new Error("browser-command-capability-unavailable: structured playwright_cli tool registration failed");
|
|
2365
2574
|
}
|
|
2575
|
+
steps.push({
|
|
2576
|
+
id: "structured-tool",
|
|
2577
|
+
label: "注册 playwright_cli",
|
|
2578
|
+
ok: true,
|
|
2579
|
+
});
|
|
2366
2580
|
const launcher = resolvePlaywrightCliLauncher(input.cwd);
|
|
2367
2581
|
const result = await defaultPlaywrightCliCommandRunner({
|
|
2368
2582
|
executable: launcher.executable,
|
|
@@ -2375,8 +2589,19 @@ export async function executeDagShellNode(input, meta) {
|
|
|
2375
2589
|
if (result.exitCode !== 0 ||
|
|
2376
2590
|
result.timedOut ||
|
|
2377
2591
|
!["open", "close", "find", "snapshot", "click"].every((command) => new RegExp(`\\b${command}\\b`, "i").test(help))) {
|
|
2592
|
+
steps.push({
|
|
2593
|
+
id: "playwright-cli-help",
|
|
2594
|
+
label: "playwright-cli --help 合同",
|
|
2595
|
+
ok: false,
|
|
2596
|
+
detail: help.slice(0, 8 * 1024),
|
|
2597
|
+
});
|
|
2378
2598
|
throw new Error("playwright-cli-contract-incompatible");
|
|
2379
2599
|
}
|
|
2600
|
+
steps.push({
|
|
2601
|
+
id: "playwright-cli-help",
|
|
2602
|
+
label: "playwright-cli --help 合同",
|
|
2603
|
+
ok: true,
|
|
2604
|
+
});
|
|
2380
2605
|
const capability = {
|
|
2381
2606
|
schemaVersion: 1,
|
|
2382
2607
|
status: "ready",
|
|
@@ -2387,22 +2612,28 @@ export async function executeDagShellNode(input, meta) {
|
|
|
2387
2612
|
targetUrlAuthority: "retrieve-context-and-environment-probe",
|
|
2388
2613
|
};
|
|
2389
2614
|
await writeDagNodeJsonArtifact(meta.runDir, input.task.id, "frontend-browser-capability.json", capability);
|
|
2390
|
-
|
|
2615
|
+
steps.push({
|
|
2616
|
+
id: "capability-artifact",
|
|
2617
|
+
label: "写出 frontend-browser-capability.json",
|
|
2618
|
+
ok: true,
|
|
2619
|
+
detail: JSON.stringify(capability, null, 2),
|
|
2620
|
+
});
|
|
2621
|
+
return await finish({
|
|
2391
2622
|
ok: true,
|
|
2392
2623
|
stdout: JSON.stringify(capability),
|
|
2393
2624
|
stderr: "",
|
|
2394
2625
|
failureCategory: "success",
|
|
2395
2626
|
durationMs: Date.now() - started,
|
|
2396
|
-
};
|
|
2627
|
+
});
|
|
2397
2628
|
}
|
|
2398
2629
|
catch (error) {
|
|
2399
|
-
return {
|
|
2630
|
+
return await finish({
|
|
2400
2631
|
ok: false,
|
|
2401
2632
|
stdout: "",
|
|
2402
2633
|
stderr: `playwright-cli-unavailable: ${error instanceof Error ? error.message : String(error)}`,
|
|
2403
2634
|
failureCategory: "tool-policy",
|
|
2404
2635
|
durationMs: Date.now() - started,
|
|
2405
|
-
};
|
|
2636
|
+
});
|
|
2406
2637
|
}
|
|
2407
2638
|
}
|
|
2408
2639
|
if (shell?.frontendPrewriteGate) {
|
|
@@ -2824,6 +3055,7 @@ export async function executeDagShellNode(input, meta) {
|
|
|
2824
3055
|
const cwd = resolveShellCwd(input.cwd, shell.cwd);
|
|
2825
3056
|
const timeoutMs = shell.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
|
|
2826
3057
|
const failFast = shell.failFast !== false;
|
|
3058
|
+
const attemptNumber = input.attempt ?? 1;
|
|
2827
3059
|
const results = [];
|
|
2828
3060
|
const executedCommandKeys = new Set();
|
|
2829
3061
|
let beforeStatus;
|
|
@@ -2919,8 +3151,8 @@ export async function executeDagShellNode(input, meta) {
|
|
|
2919
3151
|
dagRunMeta: { runDir: meta.runDir, runId: meta.runId },
|
|
2920
3152
|
reportActivity: input.reportActivity,
|
|
2921
3153
|
outputArtifacts: {
|
|
2922
|
-
stdoutPath: path.join(meta.runDir, input.task.id, "commands", `${commandNumber}.stdout.txt`),
|
|
2923
|
-
stderrPath: path.join(meta.runDir, input.task.id, "commands", `${commandNumber}.stderr.txt`),
|
|
3154
|
+
stdoutPath: path.join(meta.runDir, input.task.id, `attempt-${attemptNumber}`, "commands", `${commandNumber}.stdout.txt`),
|
|
3155
|
+
stderrPath: path.join(meta.runDir, input.task.id, `attempt-${attemptNumber}`, "commands", `${commandNumber}.stderr.txt`),
|
|
2924
3156
|
},
|
|
2925
3157
|
});
|
|
2926
3158
|
results.push(result);
|
|
@@ -2979,6 +3211,7 @@ export async function executeDagShellNode(input, meta) {
|
|
|
2979
3211
|
schemaVersion: 1,
|
|
2980
3212
|
runId: meta.runId,
|
|
2981
3213
|
nodeId: input.task.id,
|
|
3214
|
+
attempt: attemptNumber,
|
|
2982
3215
|
workspaceFingerprint: workspaceFingerprint ?? null,
|
|
2983
3216
|
failFast,
|
|
2984
3217
|
commands: results.map((result) => ({
|
|
@@ -2997,6 +3230,7 @@ export async function executeDagShellNode(input, meta) {
|
|
|
2997
3230
|
subchecks: parseCheckRepoSubcheckDurations(result),
|
|
2998
3231
|
})),
|
|
2999
3232
|
};
|
|
3233
|
+
await writeDagNodeJsonArtifact(meta.runDir, input.task.id, path.posix.join(`attempt-${attemptNumber}`, "shell-command-receipt.json"), receipt);
|
|
3000
3234
|
await writeDagNodeJsonArtifact(meta.runDir, input.task.id, "shell-command-receipt.json", receipt);
|
|
3001
3235
|
const summaryMarkdown = buildShellResultSummaryMarkdown({
|
|
3002
3236
|
nodeId: input.task.id,
|
|
@@ -3048,8 +3282,24 @@ export async function executeDagShellNode(input, meta) {
|
|
|
3048
3282
|
stdout: `${recordPrefix}${reuseNote}${aggregate.stdout}`,
|
|
3049
3283
|
stderr: stderrParts.filter(Boolean).join("\n\n"),
|
|
3050
3284
|
failureCategory,
|
|
3285
|
+
...(firstFailure?.failureCategory
|
|
3286
|
+
? { rawFailureCategory: firstFailure.failureCategory }
|
|
3287
|
+
: {}),
|
|
3051
3288
|
durationMs: Date.now() - started,
|
|
3052
3289
|
// extra field consumed by node-execution if supported; also embedded in stdout marker
|
|
3053
3290
|
...{ commandResults },
|
|
3054
3291
|
};
|
|
3055
3292
|
}
|
|
3293
|
+
export async function executeDagShellNode(input, meta) {
|
|
3294
|
+
const handler = namedShellHandler(input.task.shell);
|
|
3295
|
+
const result = await executeDagShellNodeInner(input, meta);
|
|
3296
|
+
if (handler) {
|
|
3297
|
+
await ensureShellPresetProcessReceipt({
|
|
3298
|
+
runDir: meta.runDir,
|
|
3299
|
+
nodeId: input.task.id,
|
|
3300
|
+
preset: handler,
|
|
3301
|
+
result,
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3304
|
+
return result;
|
|
3305
|
+
}
|
|
@@ -98,6 +98,13 @@ export function isEphemeralToolCachePath(filePath) {
|
|
|
98
98
|
const normalized = normalizePath(filePath);
|
|
99
99
|
if (!normalized)
|
|
100
100
|
return false;
|
|
101
|
+
// CodeGraph extension sync side effect (plan 2026-08-21 D7): a loaded
|
|
102
|
+
// pi-codegraph extension keeps an existing .codegraph/ index fresh at
|
|
103
|
+
// session start; those index files are extension-owned run state, not
|
|
104
|
+
// writer output, so they never count as write-guard violations.
|
|
105
|
+
if (normalized === ".codegraph" || normalized.startsWith(".codegraph/")) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
101
108
|
if (normalized === ".pytest_cache" ||
|
|
102
109
|
normalized.startsWith(".pytest_cache/")) {
|
|
103
110
|
return true;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal switch for the experimental "rerun failed node with an edited
|
|
3
|
+
* prompt" capability. Default is OFF; nothing may depend on it being enabled.
|
|
4
|
+
* The switch is deliberately env-based (no harness.json surface) while the
|
|
5
|
+
* feature is internal.
|
|
6
|
+
*/
|
|
7
|
+
export const DAG_RERUN_PROMPT_OVERRIDE_ENV = "HARNESS_DAG_RERUN_PROMPT_OVERRIDE";
|
|
8
|
+
/** Upper bound for an override input body (characters). */
|
|
9
|
+
export const MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS = 65_536;
|
|
10
|
+
export function isDagRerunPromptOverrideEnabled(env = process.env) {
|
|
11
|
+
const value = env[DAG_RERUN_PROMPT_OVERRIDE_ENV];
|
|
12
|
+
return value === "1" || value === "true";
|
|
13
|
+
}
|
|
14
|
+
export const DAG_RERUN_PROMPT_ADDENDUM_TAG = "operator_addendum";
|
|
15
|
+
/**
|
|
16
|
+
* Compose the final subtask_prompt for a rerun override.
|
|
17
|
+
* - "replace": the operator text becomes the whole prompt (advanced mode).
|
|
18
|
+
* - "append" (recommended): the frozen base prompt is kept verbatim and the
|
|
19
|
+
* operator addendum is appended in a clearly marked block, mirroring the
|
|
20
|
+
* rerunFeedback / retry_instruction augmentation semantics — small diffs,
|
|
21
|
+
* no risk of dropping template scaffolding.
|
|
22
|
+
*/
|
|
23
|
+
export function composeDagPromptOverridePrompt(input) {
|
|
24
|
+
if (input.mode === "replace") {
|
|
25
|
+
return input.text;
|
|
26
|
+
}
|
|
27
|
+
return `${input.basePrompt}\n\n<${DAG_RERUN_PROMPT_ADDENDUM_TAG}>\n${input.text.trim()}\n</${DAG_RERUN_PROMPT_ADDENDUM_TAG}>\n`;
|
|
28
|
+
}
|
|
@@ -1155,6 +1155,19 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
1155
1155
|
modelCallable: "always",
|
|
1156
1156
|
humanConfirmation: "none",
|
|
1157
1157
|
},
|
|
1158
|
+
{
|
|
1159
|
+
action: "dagPromptOverrideConfig",
|
|
1160
|
+
cli: "console dag-prompt-override-config",
|
|
1161
|
+
kind: "read",
|
|
1162
|
+
inputSchemaVersion: 1,
|
|
1163
|
+
resultSchemaVersion: 1,
|
|
1164
|
+
envelopeSchemaVersion: 1,
|
|
1165
|
+
requiredErrorCodes: [],
|
|
1166
|
+
description: "Read-only probe of the experimental prompt-override rerun switch (env-gated, default off). The browser Recovery UI hides the failed-node prompt editor unless this returns enabled=true. Never mutates.",
|
|
1167
|
+
inputParams: [],
|
|
1168
|
+
modelCallable: "always",
|
|
1169
|
+
humanConfirmation: "none",
|
|
1170
|
+
},
|
|
1158
1171
|
{
|
|
1159
1172
|
action: "dagRerunPlan",
|
|
1160
1173
|
cli: "loop-agent dag rerun --run-id <id> --from-node <node> --plan --json",
|
|
@@ -1177,6 +1190,12 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
1177
1190
|
required: true,
|
|
1178
1191
|
description: "node id to rerun from (prefer failed/attention node from dagReport primaryFailure.nodeId)",
|
|
1179
1192
|
},
|
|
1193
|
+
{
|
|
1194
|
+
name: "promptOverrides",
|
|
1195
|
+
type: "array",
|
|
1196
|
+
required: false,
|
|
1197
|
+
description: "experimental (switch off by default): [{nodeId, prompt, mode?}] — mode append (recommended) keeps the frozen base prompt and appends the text as a marked addendum block, replace swaps the whole subtask_prompt; only FAILED nodes inside the reset closure are admitted and the planHash binds the composed change",
|
|
1198
|
+
},
|
|
1180
1199
|
],
|
|
1181
1200
|
modelCallable: "always",
|
|
1182
1201
|
humanConfirmation: "none",
|
|
@@ -1219,6 +1238,12 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
1219
1238
|
required: true,
|
|
1220
1239
|
description: "why continue from this node (e.g. provider flake; resume from review-pi)",
|
|
1221
1240
|
},
|
|
1241
|
+
{
|
|
1242
|
+
name: "promptOverrides",
|
|
1243
|
+
type: "array",
|
|
1244
|
+
required: false,
|
|
1245
|
+
description: "experimental (switch off by default): must match the dagRerunPlan promptOverrides exactly (same mode/text); the planHash already binds them, any drift is rejected",
|
|
1246
|
+
},
|
|
1222
1247
|
],
|
|
1223
1248
|
// always: fresh eligible dagRerun plans are model-callable (2026-08-11).
|
|
1224
1249
|
// The CLI keeps enforcing parent lifecycle, fromNode, planHash,
|
|
@@ -1822,6 +1847,13 @@ export const OPERATOR_COMMAND_COVERAGE = Object.freeze([
|
|
|
1822
1847
|
source: "loop-agent",
|
|
1823
1848
|
exclusionReason: "Not exposed to Operator Chat in M5.",
|
|
1824
1849
|
},
|
|
1850
|
+
{
|
|
1851
|
+
command: "loop-agent init model-catalog",
|
|
1852
|
+
coverage: "excluded",
|
|
1853
|
+
action: null,
|
|
1854
|
+
source: "loop-agent",
|
|
1855
|
+
exclusionReason: "Not exposed to Operator Chat in M5.",
|
|
1856
|
+
},
|
|
1825
1857
|
{
|
|
1826
1858
|
command: "loop-agent init doctor",
|
|
1827
1859
|
coverage: "excluded",
|
|
@@ -9,6 +9,7 @@ export const PLAYWRIGHT_CLI_ALLOWED_COMMANDS = [
|
|
|
9
9
|
"goto",
|
|
10
10
|
"snapshot",
|
|
11
11
|
"find",
|
|
12
|
+
"find-unique",
|
|
12
13
|
"click",
|
|
13
14
|
"dblclick",
|
|
14
15
|
"fill",
|
|
@@ -35,6 +36,20 @@ export const PLAYWRIGHT_CLI_ALLOWED_COMMANDS = [
|
|
|
35
36
|
"requests",
|
|
36
37
|
"request",
|
|
37
38
|
];
|
|
39
|
+
/**
|
|
40
|
+
* Parse the match count from a playwright-cli find output. The CLI prints
|
|
41
|
+
* `Found N match(es) for "...":` on success and `No matches found for "...".`
|
|
42
|
+
* on zero — both with exit code 0, so the count is the only assertion signal.
|
|
43
|
+
* Returns undefined when the output shape is unrecognized (e.g. error pages).
|
|
44
|
+
*/
|
|
45
|
+
export function parsePlaywrightCliFindMatchCount(output) {
|
|
46
|
+
const found = /Found\s+(\d+)\s+match(?:es)?\s+for\b/i.exec(output);
|
|
47
|
+
if (found)
|
|
48
|
+
return Number(found[1]);
|
|
49
|
+
if (/No matches found for\b/i.test(output))
|
|
50
|
+
return 0;
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
38
53
|
const allowedCommandSet = new Set(PLAYWRIGHT_CLI_ALLOWED_COMMANDS);
|
|
39
54
|
export function isPlaywrightCliCommand(value) {
|
|
40
55
|
return allowedCommandSet.has(value);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import semver from "semver";
|
|
3
|
+
import { acquireUpdateLock, readUpdateState, updateLockPath, updateStatePath, writeUpdateState, } from "./state.js";
|
|
4
|
+
export async function runConsoleDailyUpdateCheck(options) {
|
|
5
|
+
void options.writeStdout;
|
|
6
|
+
void options.packageRoot;
|
|
7
|
+
void options.cwd;
|
|
8
|
+
const writeStderr = options.writeStderr ?? ((chunk) => process.stderr.write(chunk));
|
|
9
|
+
const now = options.now ?? (() => new Date());
|
|
10
|
+
try {
|
|
11
|
+
if (!canPromptInteractively(options))
|
|
12
|
+
return;
|
|
13
|
+
const statePath = updateStatePath(options.homeDir);
|
|
14
|
+
const today = formatLocalDate(now());
|
|
15
|
+
const existing = await readUpdateState(statePath);
|
|
16
|
+
if (existing.lastPromptedLocalDate === today)
|
|
17
|
+
return;
|
|
18
|
+
const release = await acquireUpdateLock(updateLockPath(options.homeDir), {
|
|
19
|
+
now,
|
|
20
|
+
});
|
|
21
|
+
if (!release)
|
|
22
|
+
return;
|
|
23
|
+
try {
|
|
24
|
+
const state = await readUpdateState(statePath);
|
|
25
|
+
if (state.lastPromptedLocalDate === today)
|
|
26
|
+
return;
|
|
27
|
+
const check = await options.client.latestVersionCheck();
|
|
28
|
+
if (!check.ok)
|
|
29
|
+
return;
|
|
30
|
+
if (!semver.gt(check.version, options.currentVersion))
|
|
31
|
+
return;
|
|
32
|
+
const parsedLatest = semver.parse(check.version);
|
|
33
|
+
if (!parsedLatest || parsedLatest.prerelease.length > 0)
|
|
34
|
+
return;
|
|
35
|
+
await markPromptedToday(statePath, state, today);
|
|
36
|
+
const answer = await ask(options, `loop-agent 有新版本:${options.currentVersion} → ${check.version}\n现在更新到 ${check.version}?[y/N] `);
|
|
37
|
+
if (!isYes(answer))
|
|
38
|
+
return;
|
|
39
|
+
const proof = await options.client.canAutoInstall();
|
|
40
|
+
if (!proof.ok) {
|
|
41
|
+
writeStderr(`当前 loop-agent 不是由此 npm 的正式全局安装管理,无法自动升级。\n请运行:npm install -g @tea-agent/loop-agent@${check.version}\n`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const install = await options.client.installExact(check.version);
|
|
45
|
+
if (!install.ok) {
|
|
46
|
+
writeStderr(`loop-agent 更新失败:${install.reason}\n`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const verified = await options.client.verifyInstalled(check.version);
|
|
50
|
+
if (!verified.ok) {
|
|
51
|
+
writeStderr(`loop-agent 已尝试安装,但验证失败:${verified.reason}\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
writeStderr(`loop-agent 已更新到 ${check.version};新版本将在下一次命令调用时完全生效。\n`);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
await release().catch(() => undefined);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
writeStderr(`loop-agent 更新检查已跳过:${error instanceof Error ? error.message : String(error)}\n`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function canPromptInteractively(options) {
|
|
65
|
+
if (!options.stdinIsTTY || !options.stderrIsTTY)
|
|
66
|
+
return false;
|
|
67
|
+
if (options.env.CI)
|
|
68
|
+
return false;
|
|
69
|
+
if (options.env.LOOP_AGENT_DISABLE_UPDATE_CHECK === "1")
|
|
70
|
+
return false;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
function formatLocalDate(date) {
|
|
74
|
+
const year = date.getFullYear();
|
|
75
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
76
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
77
|
+
return `${year}-${month}-${day}`;
|
|
78
|
+
}
|
|
79
|
+
async function markPromptedToday(statePath, state, today) {
|
|
80
|
+
await writeUpdateState(statePath, {
|
|
81
|
+
...state,
|
|
82
|
+
lastPromptedLocalDate: today,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async function ask(options, prompt) {
|
|
86
|
+
if (options.readLine) {
|
|
87
|
+
options.writeStderr?.(prompt);
|
|
88
|
+
return options.readLine(prompt);
|
|
89
|
+
}
|
|
90
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
91
|
+
try {
|
|
92
|
+
return await rl.question(prompt);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
rl.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function isYes(answer) {
|
|
99
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
100
|
+
}
|