@xcompiler/cli 0.2.3 → 0.2.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/README.CN.md +137 -202
- package/README.md +138 -205
- package/config.example.yaml +64 -32
- package/debug-wiki/README.md +12 -0
- package/debug-wiki/wiki/agent/calibration-fixtures.md +33 -0
- package/debug-wiki/wiki/agent/calibration-network-api.md +36 -0
- package/debug-wiki/wiki/agent/calibration-python-imports.md +33 -0
- package/debug-wiki/wiki/system/debug-issue-flow.md +29 -0
- package/debug-wiki/wiki/system/no-poisoning.md +29 -0
- package/dist/acp/index.d.ts +17 -2
- package/dist/acp/index.js +2403 -432
- package/dist/acp/index.js.map +1 -1
- package/dist/cli/xcompiler.js +2507 -523
- package/dist/cli/xcompiler.js.map +1 -1
- package/dist/cli/xcompiler_build.js +730 -248
- package/dist/cli/xcompiler_build.js.map +1 -1
- package/dist/cli/xcompiler_run.js +2185 -359
- package/dist/cli/xcompiler_run.js.map +1 -1
- package/dist/plugins/index.d.ts +16 -3
- package/dist/plugins/index.js +27 -17
- package/dist/plugins/index.js.map +1 -1
- package/dist/runtime/runtime.d.ts +38 -13
- package/dist/runtime/runtime.js +2495 -515
- package/dist/runtime/runtime.js.map +1 -1
- package/docs/XCompiler_design.md +542 -0
- package/docs/acp.md +4 -4
- package/docs/assets/iterative-v-model-pipeline.svg +169 -0
- package/docs/assets/system-architecture.svg +134 -0
- package/docs/assets/xcompiler-icon.png +0 -0
- package/docs/deploy.md +381 -0
- package/docs/openrouter.md +10 -1
- package/package.json +5 -1
|
@@ -21,7 +21,7 @@ import { Command } from "commander";
|
|
|
21
21
|
|
|
22
22
|
// src/runtime/build.ts
|
|
23
23
|
import path13 from "path";
|
|
24
|
-
import { promises as
|
|
24
|
+
import { promises as fs12 } from "fs";
|
|
25
25
|
|
|
26
26
|
// src/config/config.ts
|
|
27
27
|
import { promises as fs } from "fs";
|
|
@@ -128,6 +128,7 @@ var PYTHON_EXECUTOR_SYSTEM = `You are XCompiler's Step Executor. You may only in
|
|
|
128
128
|
Every round you must return strict JSON:
|
|
129
129
|
{
|
|
130
130
|
"thoughts": "<one sentence describing this round's intent>",
|
|
131
|
+
"issueResolutionPlan": "<required only in DEBUG issue mode: concise root cause, repair target, and validation plan>",
|
|
131
132
|
"actions": [ { "tool": "<tool name>", "args": { ... } }, ... ],
|
|
132
133
|
"done": true | false
|
|
133
134
|
}
|
|
@@ -212,6 +213,7 @@ var TYPESCRIPT_EXECUTOR_SYSTEM = `You are XCompiler's Step Executor. You may onl
|
|
|
212
213
|
Every round you must return strict JSON:
|
|
213
214
|
{
|
|
214
215
|
"thoughts": "<one sentence describing this round's intent>",
|
|
216
|
+
"issueResolutionPlan": "<required only in DEBUG issue mode: concise root cause, repair target, and validation plan>",
|
|
215
217
|
"actions": [ { "tool": "<tool name>", "args": { ... } }, ... ],
|
|
216
218
|
"done": true | false
|
|
217
219
|
}
|
|
@@ -235,7 +237,7 @@ Rules:
|
|
|
235
237
|
7. package.json is the dependency manifest. Use add_dependency for npm packages; never write requirements.txt.
|
|
236
238
|
8. run_program runs the project entry with \`npx tsx\`, run_tests runs Vitest via \`npm test\`, and the final delivery gate also verifies the direct Node entry command.`;
|
|
237
239
|
var PLANNER_CLARIFY_SYSTEM = `You are the requirements analyst for XCompiler's V-model. Do not restate the topic; expose unresolved decisions that would change functional design, acceptance, or architecture boundaries.
|
|
238
|
-
Return strict JSON only. Each question must be directly answerable by a product owner
|
|
240
|
+
Return strict JSON only. Each question must be directly answerable by a product owner and cover one decision. Avoid vague catch-all or implementation-stack questions, except for the single development-language question when the user prompt explicitly requires it.`;
|
|
239
241
|
function buildPlannerSystem(profile) {
|
|
240
242
|
return (profile.id === "typescript" ? TYPESCRIPT_PLANNER_SYSTEM : PYTHON_PLANNER_SYSTEM) + profile.plannerPromptOverride;
|
|
241
243
|
}
|
|
@@ -327,13 +329,13 @@ var messages = {
|
|
|
327
329
|
preflightOllamaUnreachable: (baseUrl, message) => `preflight: ollama ${baseUrl} unreachable: ${message}`,
|
|
328
330
|
preflightAutoAdded: (providers, roles) => `preflight: auto-added ${providers} provider(s) for roles [${roles}]`,
|
|
329
331
|
scoreFileHeader: "# XCompiler LLM provider score snapshot (maintained automatically by ScoreStore; do not edit)",
|
|
330
|
-
scoreFileSemantics: "# Scores: default 1.0; automatic range 0.1-1.0; providers tagged cluster default to 0.2-0.5 unless llm.cluster_score_min/max widens it; failure -0.5; success +0.1
|
|
332
|
+
scoreFileSemantics: "# Scores: dynamic snapshot; default 1.0; automatic range 0.1-1.0; providers tagged cluster default to 0.2-0.5 unless llm.cluster_score_min/max widens it; failure -0.5; success +0.1. Put user overrides in llm_scores_user.yaml; 0 disables a provider."
|
|
331
333
|
},
|
|
332
334
|
system: {
|
|
333
335
|
configEnvMissing: (names) => `[xcompiler] unset config environment variables were replaced with empty strings: ${names}`,
|
|
334
336
|
unhandledError: (message) => `Unhandled error: ${message}`,
|
|
335
337
|
unsupportedPypiOnlyNetwork: "network=pypi-only is rejected because Docker cannot enforce a PyPI-only allowlist by itself. Use network=off for isolation or network=download-only for explicitly unrestricted outbound downloads.",
|
|
336
|
-
dockerInsideContainerUnsupported: "XCompiler is running inside a container, so sandbox
|
|
338
|
+
dockerInsideContainerUnsupported: "XCompiler is running inside a container, so sandbox mode docker is unsupported because Docker-outside-of-Docker can mis-map bind mounts and docker.sock permissions. Use agent.sandboxes.<language>.mode=subprocess, run XCompiler on the host, or set XC_IN_CONTAINER=0 only in a controlled environment.",
|
|
337
339
|
firejailUnsupported: "sandbox=firejail is not implemented; use subprocess or docker.",
|
|
338
340
|
smokeHeader: (baseUrl) => `Smoke test against ${baseUrl} (streaming)`,
|
|
339
341
|
smokeOk: (model, totalMs, firstTokenMs, chunks, preview) => `[OK total=${totalMs}ms first-token=${firstTokenMs}ms chunks=${chunks}] ${model} -> ${preview}`,
|
|
@@ -350,8 +352,8 @@ var messages = {
|
|
|
350
352
|
loaded: (plugin, version) => `Plugin ${plugin}@${version} loaded.`,
|
|
351
353
|
extensionConflict: (plugin, kind, name) => `Plugin ${plugin} cannot replace existing ${kind} "${name}".`,
|
|
352
354
|
hookFailed: (plugin, stage, message) => `Plugin ${plugin} failed during ${stage}: ${message}`,
|
|
353
|
-
manifestReadFailed: (
|
|
354
|
-
moduleLoadFailed: (plugin,
|
|
355
|
+
manifestReadFailed: (path31, message) => `Cannot read plugin manifest ${path31}: ${message}`,
|
|
356
|
+
moduleLoadFailed: (plugin, path31, message) => `Cannot load plugin ${plugin} from ${path31}: ${message}`,
|
|
355
357
|
exportInvalid: (plugin, exportName) => `Plugin ${plugin} export ${exportName} is not a valid XCompiler plugin`,
|
|
356
358
|
manifestMismatch: (plugin) => `Plugin ${plugin} runtime manifest does not match its preflight manifest`
|
|
357
359
|
},
|
|
@@ -439,6 +441,7 @@ var messages = {
|
|
|
439
441
|
optIntent: "plan intent: greenfield | feature | refactor | self",
|
|
440
442
|
optBaselinePlan: "existing baseline phasePlan.json / plan.json path (default <workspace>/phasePlan.json)",
|
|
441
443
|
optProjectFile: "XXX.xc project file path (default <workspace>/<name>.xc)",
|
|
444
|
+
optDebugWikiPath: "debug wiki root directory path (default <XCompiler path>/.xcompiler/debug-wiki)",
|
|
442
445
|
argPlan: "phasePlan.json or legacy plan.json path (default = <workspace>/phasePlan.json)",
|
|
443
446
|
argProjectFile: "XXX.xc project file",
|
|
444
447
|
argStepId: "Step ID, e.g. S001",
|
|
@@ -653,7 +656,7 @@ var messages = {
|
|
|
653
656
|
deliveryGateReason: (command, exitCode, timedOut) => `FUNCTIONAL_TEST gate: \`${command}\` exit=${exitCode}${timedOut ? " (timeout)" : ""}`,
|
|
654
657
|
missingPythonEntrypoint: "missing Python entrypoint: expected src/main.py, src/<package>/__main__.py, or an explicit CLI file such as src/cli.py",
|
|
655
658
|
missingTypeScriptEntrypoint: "missing TypeScript entrypoint: expected package.json start/bin or one of src/main.ts, src/index.ts, src/main.tsx",
|
|
656
|
-
invalidPythonEntrypointSource: (
|
|
659
|
+
invalidPythonEntrypointSource: (path31) => `invalid Python entrypoint source in ${path31}: expected a real CLI entry structure such as def main(...), argparse.ArgumentParser, or if __name__ == "__main__"; placeholder/import-only files are not runnable applications.`,
|
|
657
660
|
entrypointHelpOutputMissing: (command) => `entrypoint probe \`${command}\` exited 0 but produced no meaningful help/usage text; implement --help instead of relying on an empty script exit.`,
|
|
658
661
|
reasonLine: (reason) => `reason: ${reason}`,
|
|
659
662
|
roundsLine: (rounds) => `rounds: ${rounds}`,
|
|
@@ -722,12 +725,13 @@ Question mix (functionality first):
|
|
|
722
725
|
- Label options sequentially from A through the last generated option, for example A-B, A-C, A-D, or A-E. A should be the recommended/default setting when one is apparent. Options must be concrete business/product settings, not vague placeholders.
|
|
723
726
|
- Do not include \u201COther\u201D, \u201CCustom\u201D, or \u201CLet the user decide\u201D as an option. The CLI already allows the user to reply with one of the shown labels or enter a custom free-form answer.
|
|
724
727
|
${opts.projectShapeAmbiguous ? "- Required for this topic: ask the API library vs runnable application vs mixed-deliverable boundary explicitly.\n" : ""}
|
|
728
|
+
${opts.languageAmbiguous ? "- Required for this topic: include exactly one boundary question confirming the target development language. Options must be A. Python (default/recommended) and B. TypeScript / Node.js. The user may still answer with custom free-form text.\n" : ""}
|
|
725
729
|
|
|
726
|
-
[Hard constraint] The implementation stack is already fixed by
|
|
730
|
+
${opts.languageAmbiguous ? `[Stack decision] XCompiler could not infer the target language from the topic or baseline. Ask only the development-language question described above; do not ask package manager, test framework, or OS questions. If the user does not choose, Python is the default.` : `[Hard constraint] The implementation stack is already fixed by the user's topic or existing project baseline. Do not reopen language/runtime/package-manager decisions.
|
|
727
731
|
**Do NOT** ask questions of these forms:
|
|
728
732
|
- "Which programming language / framework / runtime should this use?"
|
|
729
733
|
- "Which test framework / build tool / package manager?"
|
|
730
|
-
- "Which OS is the target platform?"
|
|
734
|
+
- "Which OS is the target platform?"`}
|
|
731
735
|
${opts.intent && opts.intent !== "greenfield" ? `This is an incremental ${opts.intent} request against an existing project${opts.hasBaseline ? " with a separate baseline summary that will be provided during decomposition" : ""}. Ask ONLY delta questions; do not ask to rebuild the project from scratch.` : ""}The majority of questions must concern functional behaviour; performance, boundaries, and extensibility should eliminate ambiguities that affect this delivery.`,
|
|
732
736
|
plannerDecompose: (raw, qa, addenda, opts = {}) => `Original requirement:
|
|
733
737
|
"""
|
|
@@ -829,7 +833,7 @@ Return strict JSON StepPlan for the current phase only.`,
|
|
|
829
833
|
executorDebugBlock: (reason, suggestions) => `
|
|
830
834
|
|
|
831
835
|
You are now in DEBUG retry mode. Previous failure reason: ${reason}
|
|
832
|
-
DEBUG may edit upstream source files and tests within the current allowedWrites. If the failure reveals a real implementation, contract, or downstream integration mismatch, fix that real defect; do not pass by weakening assertions, skipping tests, deleting failing cases, or merely accommodating an incorrect test. If this rollback is in a design/requirements step and the concrete code change belongs to a later V-model step outside the current allowedWrites, update the current contract, test plan, or diagnostic artifact and finish this step so the later CODE step can implement it; do not attempt denied writes. If the failure is a missing third-party dependency or wrong library choice, use add_dependency with the real package name or change the source back to the real library selected by HIGH_LEVEL_DESIGN; never add try/except ImportError fake modules, fake classes/functions, empty implementations, or fallback mocks in production src/ code to bypass the error. Begin with read_file / code_search to localise the issue, then make the smallest possible fix via apply_patch / replace_in_file / add_dependency, and finally run_tests to verify. A DEBUG retry cannot be marked complete from read-only inspection alone: it must produce a successful repair action or a successful verification command in this retry. If the previous failure reason mentions repeated read-only/probe actions, use the existing failure log as sufficient context and make the next action a patch/write/dependency change or a verification command. When a test executes and fails an assertion about returned behaviour, do not repeatedly rewrite fixtures or samples. Only edit fixtures when the evidence is missing-file, malformed-fixture, or parse-error in the fixture itself; otherwise patch the implementation, interface contract, dependency choice, or test expectation that is actually wrong. If the failure log shows a network/API failure, do not stop at probing endpoints: use at most two consecutive http_fetch probes, reject 2xx responses with empty or unusable bodies, then patch the real integration and verify with run_program plus run_tests. Do not set done=true while the entrypoint still reports a network/API failure.` + (suggestions ? `
|
|
836
|
+
When this retry is handling an issue, every JSON response must include issueResolutionPlan before or while fixing it. The plan must be concise and actionable: root cause hypothesis, files/contracts to change, validation command or gate, and what would disprove the plan. DEBUG may edit upstream source files and tests within the current allowedWrites. If the failure reveals a real implementation, contract, or downstream integration mismatch, fix that real defect; do not pass by weakening assertions, skipping tests, deleting failing cases, or merely accommodating an incorrect test. If this rollback is in a design/requirements step and the concrete code change belongs to a later V-model step outside the current allowedWrites, update the current contract, test plan, or diagnostic artifact and finish this step so the later CODE step can implement it; do not attempt denied writes. If the failure is a missing third-party dependency or wrong library choice, use add_dependency with the real package name or change the source back to the real library selected by HIGH_LEVEL_DESIGN; never add try/except ImportError fake modules, fake classes/functions, empty implementations, or fallback mocks in production src/ code to bypass the error. Begin with read_file / code_search to localise the issue, then make the smallest possible fix via apply_patch / replace_in_file / add_dependency, and finally run_tests to verify. A DEBUG retry cannot be marked complete from read-only inspection alone: it must produce a successful repair action or a successful verification command in this retry. If the previous failure reason mentions repeated read-only/probe actions, use the existing failure log as sufficient context and make the next action a patch/write/dependency change or a verification command. When a test executes and fails an assertion about returned behaviour, do not repeatedly rewrite fixtures or samples. Only edit fixtures when the evidence is missing-file, malformed-fixture, or parse-error in the fixture itself; otherwise patch the implementation, interface contract, dependency choice, or test expectation that is actually wrong. If the failure log shows a network/API failure, do not stop at probing endpoints: use at most two consecutive http_fetch probes, reject 2xx responses with empty or unusable bodies, then patch the real integration and verify with run_program plus run_tests. Do not set done=true while the entrypoint still reports a network/API failure.` + (suggestions ? `
|
|
833
837
|
|
|
834
838
|
${suggestions}` : ""),
|
|
835
839
|
executorGlobalBlock: (globalPrompt) => `
|
|
@@ -846,7 +850,8 @@ ${sp}`,
|
|
|
846
850
|
executorFeedbackVerifyMissing: (paths) => `outputs still missing: ${paths}. Please continue.`,
|
|
847
851
|
executorFeedbackReadOnlyLoopWarning: (rounds, targets) => `Loop guard warning: the last ${rounds} round(s) used only read/probe tools` + (targets ? ` (${targets})` : "") + ". Next response must include a successful repair action (apply_patch / replace_in_file / write_file / add_dependency) or a concrete verification action (run_tests / run_program). Do not continue with only read_file, list_dir, code_search, or http_fetch.",
|
|
848
852
|
executorFeedbackReadOnlyRecoveryRequired: "Read-only recovery mode is active because the previous attempt already failed from probing. The next response must use existing failure evidence to patch/write/change dependency or run verification; one more read-only-only response will fail this retry.",
|
|
849
|
-
executorFeedbackRepairEvidenceMissing: "Invalid DEBUG completion: this retry has not produced repair evidence yet. Before done=true, perform at least one successful repair action or successful verification run; otherwise stop only with a concrete blocker in thoughts."
|
|
853
|
+
executorFeedbackRepairEvidenceMissing: "Invalid DEBUG completion: this retry has not produced repair evidence yet. Before done=true, perform at least one successful repair action or successful verification run; otherwise stop only with a concrete blocker in thoughts.",
|
|
854
|
+
executorFeedbackIssueResolutionPlanMissing: "Invalid DEBUG issue completion: issueResolutionPlan is required before the issue can be resolved. Return JSON with a concise handling plan plus the needed repair or verification actions."
|
|
850
855
|
},
|
|
851
856
|
skills: {
|
|
852
857
|
patcher: "Use apply_patch / replace_in_file for small in-place edits to existing files; never overwrite a whole file.",
|
|
@@ -867,7 +872,7 @@ ${sp}`,
|
|
|
867
872
|
summaryOk: "all checks passed.",
|
|
868
873
|
summaryWarn: (n) => `passed with ${n} warning(s).`,
|
|
869
874
|
summaryFail: (n) => `${n} failure(s) detected.`,
|
|
870
|
-
configLoadOk: (
|
|
875
|
+
configLoadOk: (path31) => `config loaded: ${path31}`,
|
|
871
876
|
configLoadFail: (msg) => `failed to load config: ${msg}`,
|
|
872
877
|
configLocale: (locale) => `locale=${locale}`,
|
|
873
878
|
llmNoProviders: "no LLM providers defined in config.llm.providers",
|
|
@@ -885,7 +890,7 @@ ${sp}`,
|
|
|
885
890
|
roleOk: (role, provider) => `role "${role}" \u2192 ${provider}`,
|
|
886
891
|
sandboxKind: (kind) => `sandbox=${kind}`,
|
|
887
892
|
sandboxNetworkPolicy: (policy, ports) => `network=${policy}` + (ports.length ? ` (expose_ports=[${ports.join(", ")}])` : ""),
|
|
888
|
-
sandboxFullNoPorts: "network=full but no expose_ports configured \u2014 host-side cannot reach container services. Add `agent.
|
|
893
|
+
sandboxFullNoPorts: "network=full but no expose_ports configured \u2014 host-side cannot reach container services. Add `agent.sandboxes.<language>.<local|docker>.limits.expose_ports: [<port>]` in config.yaml.",
|
|
889
894
|
sandboxNodeMissing: "node not found on PATH (required by TypeScript subprocess sandbox)",
|
|
890
895
|
sandboxNodeOk: (version) => `node OK (${version})`,
|
|
891
896
|
sandboxNpmMissing: "npm not found on PATH (required by TypeScript subprocess sandbox)",
|
|
@@ -1004,6 +1009,7 @@ var PYTHON_EXECUTOR_SYSTEM2 = `\u4F60\u662F XCompiler \u7684 Step Executor\u3002
|
|
|
1004
1009
|
\u6BCF\u4E00\u8F6E\u4F60\u5FC5\u987B\u8FD4\u56DE\u4E25\u683C JSON\uFF1A
|
|
1005
1010
|
{
|
|
1006
1011
|
"thoughts": "<\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u672C\u8F6E\u610F\u56FE>",
|
|
1012
|
+
"issueResolutionPlan": "<\u4EC5 DEBUG issue \u6A21\u5F0F\u5FC5\u586B\uFF1A\u7B80\u660E\u8BF4\u660E\u6839\u56E0\u5047\u8BBE\u3001\u4FEE\u590D\u76EE\u6807\u548C\u9A8C\u8BC1\u65B9\u6848>",
|
|
1007
1013
|
"actions": [ { "tool": "<\u5DE5\u5177\u540D>", "args": { ... } }, ... ],
|
|
1008
1014
|
"done": true | false
|
|
1009
1015
|
}
|
|
@@ -1087,6 +1093,7 @@ var TYPESCRIPT_EXECUTOR_SYSTEM2 = `\u4F60\u662F XCompiler \u7684 Step Executor\u
|
|
|
1087
1093
|
\u6BCF\u4E00\u8F6E\u4F60\u5FC5\u987B\u8FD4\u56DE\u4E25\u683C JSON\uFF1A
|
|
1088
1094
|
{
|
|
1089
1095
|
"thoughts": "<\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u672C\u8F6E\u610F\u56FE>",
|
|
1096
|
+
"issueResolutionPlan": "<\u4EC5 DEBUG issue \u6A21\u5F0F\u5FC5\u586B\uFF1A\u7B80\u660E\u8BF4\u660E\u6839\u56E0\u5047\u8BBE\u3001\u4FEE\u590D\u76EE\u6807\u548C\u9A8C\u8BC1\u65B9\u6848>",
|
|
1090
1097
|
"actions": [ { "tool": "<\u5DE5\u5177\u540D>", "args": { ... } }, ... ],
|
|
1091
1098
|
"done": true | false
|
|
1092
1099
|
}
|
|
@@ -1200,13 +1207,13 @@ var messages2 = {
|
|
|
1200
1207
|
preflightOllamaUnreachable: (baseUrl, message) => `\u9884\u68C0\uFF1AOllama ${baseUrl} \u4E0D\u53EF\u8FBE\uFF1A${message}`,
|
|
1201
1208
|
preflightAutoAdded: (providers, roles) => `\u9884\u68C0\uFF1A\u81EA\u52A8\u589E\u52A0 ${providers} \u4E2A provider\uFF0C\u8986\u76D6\u89D2\u8272 [${roles}]`,
|
|
1202
1209
|
scoreFileHeader: "# XCompiler LLM provider \u8BC4\u5206\u5FEB\u7167\uFF08\u7531 ScoreStore \u81EA\u52A8\u7EF4\u62A4\uFF0C\u8BF7\u52FF\u624B\u5DE5\u7F16\u8F91\uFF09",
|
|
1203
|
-
scoreFileSemantics: "# \u8BC4\u5206\u8BED\u4E49\uFF1A\u9ED8\u8BA4 1.0\uFF1B\u81EA\u52A8\u8BC4\u5206\u8303\u56F4 0.1\uFF5E1.0\uFF1Btags: [cluster] \u7684 provider \u9ED8\u8BA4 0.2\uFF5E0.5\uFF0C\u9664\u975E llm.cluster_score_min/max \u6269\u5BBD\uFF1B\u5931\u8D25 -0.5\uFF1B\u6210\u529F +0.1\
|
|
1210
|
+
scoreFileSemantics: "# \u8BC4\u5206\u8BED\u4E49\uFF1A\u8FD9\u662F\u52A8\u6001\u8BC4\u5206\u5FEB\u7167\uFF1B\u9ED8\u8BA4 1.0\uFF1B\u81EA\u52A8\u8BC4\u5206\u8303\u56F4 0.1\uFF5E1.0\uFF1Btags: [cluster] \u7684 provider \u9ED8\u8BA4 0.2\uFF5E0.5\uFF0C\u9664\u975E llm.cluster_score_min/max \u6269\u5BBD\uFF1B\u5931\u8D25 -0.5\uFF1B\u6210\u529F +0.1\u3002\u7528\u6237\u8986\u76D6\u8BF7\u5199 llm_scores_user.yaml\uFF0C0 \u8868\u793A\u7981\u7528\u3002"
|
|
1204
1211
|
},
|
|
1205
1212
|
system: {
|
|
1206
1213
|
configEnvMissing: (names) => `[xcompiler] \u914D\u7F6E\u4E2D\u7684\u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E\uFF0C\u5DF2\u66FF\u6362\u4E3A\u7A7A\u5B57\u7B26\u4E32\uFF1A${names}`,
|
|
1207
1214
|
unhandledError: (message) => `\u672A\u5904\u7406\u9519\u8BEF\uFF1A${message}`,
|
|
1208
1215
|
unsupportedPypiOnlyNetwork: "\u62D2\u7EDD network=pypi-only\uFF1ADocker \u672C\u8EAB\u65E0\u6CD5\u53EF\u9760\u6267\u884C\u201C\u4EC5 PyPI\u201D\u57DF\u540D\u767D\u540D\u5355\u3002\u9700\u8981\u9694\u79BB\u8BF7\u4F7F\u7528 network=off\uFF1B\u660E\u786E\u5141\u8BB8\u4EFB\u610F\u51FA\u7AD9\u4E0B\u8F7D\u65F6\u4F7F\u7528 network=download-only\u3002",
|
|
1209
|
-
dockerInsideContainerUnsupported: "\u68C0\u6D4B\u5230 XCompiler \u8FD0\u884C\u5728\u5BB9\u5668\u5185\uFF0Csandbox=docker \u53EF\u80FD\u5BFC\u81F4 bind-mount \u8DEF\u5F84\u53CA docker.sock \u6743\u9650\u9519\u4F4D\uFF0C\u56E0\u6B64\u4E0D\u53D7\u652F\u6301\u3002\u8BF7\u4F7F\u7528 agent.
|
|
1216
|
+
dockerInsideContainerUnsupported: "\u68C0\u6D4B\u5230 XCompiler \u8FD0\u884C\u5728\u5BB9\u5668\u5185\uFF0Csandbox mode=docker \u53EF\u80FD\u5BFC\u81F4 bind-mount \u8DEF\u5F84\u53CA docker.sock \u6743\u9650\u9519\u4F4D\uFF0C\u56E0\u6B64\u4E0D\u53D7\u652F\u6301\u3002\u8BF7\u4F7F\u7528 agent.sandboxes.<language>.mode=subprocess\u3001\u6539\u5728\u5BBF\u4E3B\u673A\u8FD0\u884C\uFF0C\u6216\u4EC5\u5728\u53D7\u63A7\u73AF\u5883\u8BBE\u7F6E XC_IN_CONTAINER=0\u3002",
|
|
1210
1217
|
firejailUnsupported: "\u5C1A\u672A\u5B9E\u73B0 sandbox=firejail\uFF0C\u8BF7\u4F7F\u7528 subprocess \u6216 docker\u3002",
|
|
1211
1218
|
smokeHeader: (baseUrl) => `\u6B63\u5728\u5BF9 ${baseUrl} \u6267\u884C\u6D41\u5F0F\u5192\u70DF\u6D4B\u8BD5`,
|
|
1212
1219
|
smokeOk: (model, totalMs, firstTokenMs, chunks, preview) => `[\u6210\u529F \u603B\u8017\u65F6=${totalMs}ms \u9996Token=${firstTokenMs}ms \u5206\u5757=${chunks}] ${model} -> ${preview}`,
|
|
@@ -1223,8 +1230,8 @@ var messages2 = {
|
|
|
1223
1230
|
loaded: (plugin, version) => `\u63D2\u4EF6 ${plugin}@${version} \u5DF2\u52A0\u8F7D\u3002`,
|
|
1224
1231
|
extensionConflict: (plugin, kind, name) => `\u63D2\u4EF6 ${plugin} \u4E0D\u80FD\u8986\u76D6\u5DF2\u6709 ${kind} \u201C${name}\u201D\u3002`,
|
|
1225
1232
|
hookFailed: (plugin, stage, message) => `\u63D2\u4EF6 ${plugin} \u5728 ${stage} \u9636\u6BB5\u6267\u884C\u5931\u8D25\uFF1A${message}`,
|
|
1226
|
-
manifestReadFailed: (
|
|
1227
|
-
moduleLoadFailed: (plugin,
|
|
1233
|
+
manifestReadFailed: (path31, message) => `\u65E0\u6CD5\u8BFB\u53D6\u63D2\u4EF6\u6E05\u5355 ${path31}\uFF1A${message}`,
|
|
1234
|
+
moduleLoadFailed: (plugin, path31, message) => `\u65E0\u6CD5\u4ECE ${path31} \u52A0\u8F7D\u63D2\u4EF6 ${plugin}\uFF1A${message}`,
|
|
1228
1235
|
exportInvalid: (plugin, exportName) => `\u63D2\u4EF6 ${plugin} \u7684\u5BFC\u51FA ${exportName} \u4E0D\u662F\u6709\u6548 XCompiler \u63D2\u4EF6`,
|
|
1229
1236
|
manifestMismatch: (plugin) => `\u63D2\u4EF6 ${plugin} \u7684\u8FD0\u884C\u65F6\u6E05\u5355\u4E0E\u9884\u68C0\u6E05\u5355\u4E0D\u4E00\u81F4`
|
|
1230
1237
|
},
|
|
@@ -1312,6 +1319,7 @@ var messages2 = {
|
|
|
1312
1319
|
optIntent: "\u8BA1\u5212\u610F\u56FE\uFF1Agreenfield | feature | refactor | self",
|
|
1313
1320
|
optBaselinePlan: "\u5DF2\u6709\u57FA\u7EBF phasePlan.json / plan.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 <workspace>/phasePlan.json\uFF09",
|
|
1314
1321
|
optProjectFile: "XXX.xc \u5DE5\u7A0B\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 <workspace>/<name>.xc\uFF09",
|
|
1322
|
+
optDebugWikiPath: "debug wiki \u6839\u76EE\u5F55\u8DEF\u5F84\uFF08\u9ED8\u8BA4 <XCompiler path>/.xcompiler/debug-wiki\uFF09",
|
|
1315
1323
|
argPlan: "phasePlan.json \u6216\u5386\u53F2 plan.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 = <workspace>/phasePlan.json\uFF09",
|
|
1316
1324
|
argProjectFile: "XXX.xc \u5DE5\u7A0B\u6587\u4EF6",
|
|
1317
1325
|
argStepId: "Step ID\uFF0C\u5982 S001",
|
|
@@ -1526,7 +1534,7 @@ var messages2 = {
|
|
|
1526
1534
|
deliveryGateReason: (command, exitCode, timedOut) => `FUNCTIONAL_TEST \u95E8\u7981\uFF1A\`${command}\` \u9000\u51FA\u7801=${exitCode}${timedOut ? "\uFF08\u8D85\u65F6\uFF09" : ""}`,
|
|
1527
1535
|
missingPythonEntrypoint: "\u7F3A\u5C11 Python \u5165\u53E3\uFF1A\u9700\u8981 src/main.py\u3001src/<package>/__main__.py\uFF0C\u6216 src/cli.py \u7B49\u663E\u5F0F CLI \u6587\u4EF6",
|
|
1528
1536
|
missingTypeScriptEntrypoint: "\u7F3A\u5C11 TypeScript \u5165\u53E3\uFF1A\u9700\u8981 package.json start/bin \u6216 src/main.ts\u3001src/index.ts\u3001src/main.tsx \u4E4B\u4E00",
|
|
1529
|
-
invalidPythonEntrypointSource: (
|
|
1537
|
+
invalidPythonEntrypointSource: (path31) => `Python \u5165\u53E3\u6E90\u7801\u65E0\u6548\uFF1A${path31} \u5FC5\u987B\u662F\u771F\u5B9E CLI \u5165\u53E3\uFF0C\u81F3\u5C11\u5305\u542B def main(...)\u3001argparse.ArgumentParser \u6216 if __name__ == "__main__" \u8FD9\u7C7B\u5165\u53E3\u7ED3\u6784\uFF1B\u4EC5 import/comment \u7684\u5360\u4F4D\u6587\u4EF6\u4E0D\u80FD\u7B97\u53EF\u8FD0\u884C\u5E94\u7528\u3002`,
|
|
1530
1538
|
entrypointHelpOutputMissing: (command) => `\u5165\u53E3\u63A2\u6D4B \`${command}\` \u867D\u7136\u9000\u51FA\u7801\u4E3A 0\uFF0C\u4F46\u6CA1\u6709\u8F93\u51FA\u6709\u610F\u4E49\u7684 help/usage \u6587\u672C\uFF1B\u5FC5\u987B\u5B9E\u73B0 --help\uFF0C\u4E0D\u80FD\u9760\u7A7A\u811A\u672C\u81EA\u7136\u9000\u51FA\u8FC7\u5173\u3002`,
|
|
1531
1539
|
reasonLine: (reason) => `\u539F\u56E0\uFF1A${reason}`,
|
|
1532
1540
|
roundsLine: (rounds) => `\u8F6E\u6B21\uFF1A${rounds}`,
|
|
@@ -1571,7 +1579,7 @@ var messages2 = {
|
|
|
1571
1579
|
- \u6BCF\u4E2A CODE/\u6D4B\u8BD5\u4EA7\u7269\u5FC5\u987B\u4E25\u683C\u9650\u5B9A\u5728\u672C\u6B21\u589E\u91CF\u8303\u56F4\uFF0C\u7981\u6B62\u6574\u4F53\u91CD\u5EFA\u6216\u66FF\u6362\u4ED3\u5E93\u3002
|
|
1572
1580
|
- \u5C06\u7A33\u5B9A\u5BBF\u4E3B\u89C6\u4E3A N \u4EE3\u3001\u9694\u79BB worktree \u4E2D\u7684\u5019\u9009\u7248\u672C\u89C6\u4E3A N+1 \u4EE3\uFF1B\u7981\u6B62\u8BBE\u8BA1\u8FDB\u7A0B\u5185\u70ED\u66FF\u6362\u3002`,
|
|
1573
1581
|
plannerClarifySystem: `\u4F60\u662F XCompiler V \u6A21\u578B\u7684\u9700\u6C42\u5206\u6790\u5E08\u3002\u4F60\u7684\u804C\u8D23\u4E0D\u662F\u590D\u8FF0 topic\uFF0C\u800C\u662F\u53D1\u73B0\u4F1A\u6539\u53D8\u529F\u80FD\u8BBE\u8BA1\u3001\u9A8C\u6536\u7ED3\u679C\u6216\u67B6\u6784\u8FB9\u754C\u7684\u672A\u51B3\u4E8B\u9879\u3002
|
|
1574
|
-
\u53EA\u8FD4\u56DE\u4E25\u683C JSON\u3002\u95EE\u9898\u5FC5\u987B\u53EF\u7531\u4E1A\u52A1\u65B9\u76F4\u63A5\u4F5C\u7B54\u3001\u4E00\u6B21\u53EA\u786E\u8BA4\u4E00\u4E2A\u51B3\u7B56\uFF0C\u907F\u514D\u7A7A\u6CDB\u7684\u201C\u8FD8\u6709\u4EC0\u4E48\u8981\u6C42\u201D\u6216\u6280\u672F\u6808\u9009\u578B\u95EE\u9898\u3002`,
|
|
1582
|
+
\u53EA\u8FD4\u56DE\u4E25\u683C JSON\u3002\u95EE\u9898\u5FC5\u987B\u53EF\u7531\u4E1A\u52A1\u65B9\u76F4\u63A5\u4F5C\u7B54\u3001\u4E00\u6B21\u53EA\u786E\u8BA4\u4E00\u4E2A\u51B3\u7B56\uFF0C\u907F\u514D\u7A7A\u6CDB\u7684\u201C\u8FD8\u6709\u4EC0\u4E48\u8981\u6C42\u201D\u6216\u6280\u672F\u6808\u9009\u578B\u95EE\u9898\uFF1B\u53EA\u6709\u7528\u6237\u63D0\u793A\u660E\u786E\u8981\u6C42\u65F6\uFF0C\u624D\u5141\u8BB8\u63D0\u51FA\u552F\u4E00\u4E00\u6761\u5F00\u53D1\u8BED\u8A00\u786E\u8BA4\u95EE\u9898\u3002`,
|
|
1575
1583
|
plannerClarify: (raw, opts = {}) => `\u7528\u6237\u7684\u539F\u59CB\u9700\u6C42\u5982\u4E0B\uFF1A
|
|
1576
1584
|
|
|
1577
1585
|
"""
|
|
@@ -1596,12 +1604,13 @@ ${raw}
|
|
|
1596
1604
|
- \u9009\u9879\u4ECE A \u5F00\u59CB\u8FDE\u7EED\u6807\u53F7\uFF0C\u5230\u5B9E\u9645\u6700\u540E\u4E00\u4E2A\u9009\u9879\u7ED3\u675F\uFF0C\u4F8B\u5982 A-B\u3001A-C\u3001A-D \u6216 A-E\uFF1B\u5982\u679C\u80FD\u5224\u65AD\u63A8\u8350/\u9ED8\u8BA4\u65B9\u6848\uFF0CA \u5E94\u662F\u6700\u9AD8\u4F18\u5148\u7EA7\u65B9\u6848\u3002\u9009\u9879\u5FC5\u987B\u662F\u5177\u4F53\u4E1A\u52A1/\u4EA7\u54C1\u8BBE\u5B9A\uFF0C\u4E0D\u8981\u5199\u6210\u7A7A\u6CDB\u5360\u4F4D\u3002
|
|
1597
1605
|
- \u4E0D\u8981\u628A\u201C\u5176\u4ED6 / \u81EA\u5B9A\u4E49 / \u7528\u6237\u51B3\u5B9A\u201D\u4F5C\u4E3A\u9009\u9879\uFF1BCLI \u5DF2\u652F\u6301\u7528\u6237\u8F93\u5165\u5DF2\u5C55\u793A\u7684\u9009\u9879\u5B57\u6BCD\u6216\u76F4\u63A5\u8F93\u5165\u81EA\u5B9A\u4E49\u56DE\u7B54\u5185\u5BB9\u3002
|
|
1598
1606
|
${opts.projectShapeAmbiguous ? "- \u672C topic \u5FC5\u95EE\uFF1A\u660E\u786E\u786E\u8BA4 API library / \u53EF\u8FD0\u884C\u5E94\u7528 / mixed \u4EA4\u4ED8\u8FB9\u754C\u3002\n" : ""}
|
|
1607
|
+
${opts.languageAmbiguous ? "- \u672C topic \u5FC5\u95EE\uFF1A\u5FC5\u987B\u5305\u542B\u4E14\u53EA\u5305\u542B 1 \u4E2A boundary \u95EE\u9898\u786E\u8BA4\u76EE\u6807\u5F00\u53D1\u8BED\u8A00\u3002\u9009\u9879\u5FC5\u987B\u662F A. Python\uFF08\u9ED8\u8BA4/\u63A8\u8350\uFF09 \u548C B. TypeScript / Node.js\uFF1B\u7528\u6237\u4ECD\u53EF\u8F93\u5165\u81EA\u5B9A\u4E49\u56DE\u7B54\u3002\n" : ""}
|
|
1599
1608
|
|
|
1600
|
-
\u3010\u786C\u7EA6\u675F\u3011\u5B9E\u73B0\u6280\u672F\u6808\u5DF2\u7ECF\u7531
|
|
1609
|
+
${opts.languageAmbiguous ? `\u3010\u6280\u672F\u6808\u786E\u8BA4\u3011XCompiler \u65E0\u6CD5\u4ECE topic \u6216\u57FA\u7EBF\u4E2D\u5224\u65AD\u76EE\u6807\u5F00\u53D1\u8BED\u8A00\u3002\u53EA\u8BE2\u95EE\u4E0A\u9762\u8981\u6C42\u7684\u5F00\u53D1\u8BED\u8A00\u95EE\u9898\uFF1B\u4E0D\u8981\u8BE2\u95EE\u5305\u7BA1\u7406\u5668\u3001\u6D4B\u8BD5\u6846\u67B6\u6216\u64CD\u4F5C\u7CFB\u7EDF\u3002\u82E5\u7528\u6237\u4E0D\u9009\u62E9\uFF0C\u9ED8\u8BA4 Python\u3002` : `\u3010\u786C\u7EA6\u675F\u3011\u5B9E\u73B0\u6280\u672F\u6808\u5DF2\u7ECF\u7531\u7528\u6237 topic / \u73B0\u6709\u5DE5\u7A0B\u57FA\u7EBF\u56FA\u5B9A\uFF0C\u4E0D\u8981\u91CD\u65B0\u8BE2\u95EE\u8BED\u8A00\u3001\u8FD0\u884C\u65F6\u3001\u5305\u7BA1\u7406\u5668\u8FD9\u7C7B\u95EE\u9898\u3002
|
|
1601
1610
|
**\u4E25\u7981**\u63D0\u51FA\u4EE5\u4E0B\u7C7B\u578B\u7684\u95EE\u9898\uFF1A
|
|
1602
1611
|
- "\u5E0C\u671B\u7528\u4EC0\u4E48\u7F16\u7A0B\u8BED\u8A00 / \u6846\u67B6 / \u8FD0\u884C\u65F6\u5B9E\u73B0\uFF1F"
|
|
1603
1612
|
- "\u9700\u8981\u54EA\u79CD\u6D4B\u8BD5\u6846\u67B6 / \u6784\u5EFA\u5DE5\u5177 / \u5305\u7BA1\u7406\u5668\uFF1F"
|
|
1604
|
-
- "\u76EE\u6807\u5E73\u53F0\u662F\u54EA\u79CD\u64CD\u4F5C\u7CFB\u7EDF\uFF1F"
|
|
1613
|
+
- "\u76EE\u6807\u5E73\u53F0\u662F\u54EA\u79CD\u64CD\u4F5C\u7CFB\u7EDF\uFF1F"`}
|
|
1605
1614
|
${opts.intent && opts.intent !== "greenfield" ? `\u8FD9\u662F\u4E00\u6761\u9488\u5BF9\u73B0\u6709\u5DE5\u7A0B\u7684\u589E\u91CF ${opts.intent} \u8BF7\u6C42${opts.hasBaseline ? "\uFF1B\u5206\u89E3\u9636\u6BB5\u8FD8\u4F1A\u63D0\u4F9B\u4E00\u4EFD\u57FA\u7EBF\u6458\u8981" : ""}\u3002\u8BF7\u53EA\u95EE\u201C\u53D8\u66F4\u589E\u91CF\u201D\u76F8\u5173\u95EE\u9898\uFF0C\u4E0D\u8981\u628A\u9879\u76EE\u5F53\u6210\u4ECE\u96F6\u5F00\u59CB\u91CD\u505A\u3002` : ""}\u95EE\u9898\u4E3B\u4F53\u5FC5\u987B\u805A\u7126\u529F\u80FD\u884C\u4E3A\uFF1B\u6027\u80FD\u3001\u8FB9\u754C\u548C\u6269\u5C55\u6027\u7528\u4E8E\u6D88\u9664\u4F1A\u5F71\u54CD\u672C\u671F\u8BBE\u8BA1\u7684\u5173\u952E\u6B67\u4E49\u3002`,
|
|
1606
1615
|
plannerDecompose: (raw, qa, addenda, opts = {}) => `\u539F\u59CB\u9700\u6C42\uFF1A
|
|
1607
1616
|
"""
|
|
@@ -1703,7 +1712,7 @@ ${opts.phasePlan}
|
|
|
1703
1712
|
executorDebugBlock: (reason, suggestions) => `
|
|
1704
1713
|
|
|
1705
1714
|
\u6B63\u5904\u4E8E DEBUG \u91CD\u8BD5\u6A21\u5F0F\u3002\u4E0A\u4E00\u8F6E\u5931\u8D25\u539F\u56E0: ${reason}
|
|
1706
|
-
DEBUG \u53EF\u4EE5\u4FEE\u6539\u5F53\u524D allowedWrites \u5185\u7684\u4E0A\u6E38\u6E90\u7801\u4E0E\u6D4B\u8BD5\u6587\u4EF6\uFF1B\u82E5\u5931\u8D25\u66B4\u9732\u7684\u662F\u5B9E\u73B0\u3001\u5951\u7EA6\u6216\u4E0B\u6E38\u8C03\u7528\u4E0D\u4E00\u81F4\uFF0C\u5FC5\u987B\u4FEE\u771F\u5B9E\u7F3A\u9677\uFF0C\u7981\u6B62\u901A\u8FC7\u524A\u5F31\u65AD\u8A00\u3001\u8DF3\u8FC7\u6D4B\u8BD5\u3001\u5220\u9664\u5931\u8D25\u7528\u4F8B\u6216\u53EA\u8FCE\u5408\u9519\u8BEF\u6D4B\u8BD5\u6765\u8FC7\u5173\u3002\u5982\u679C\u672C\u6B21\u56DE\u9000\u5904\u4E8E\u9700\u6C42/\u8BBE\u8BA1\u9636\u6BB5\uFF0C\u800C\u5177\u4F53\u6E90\u7801\u4FEE\u6539\u5C5E\u4E8E\u540E\u7EED V \u6A21\u578B Step \u4E14\u76EE\u6807\u6587\u4EF6\u4E0D\u5728\u5F53\u524D allowedWrites \u5185\uFF0C\u5E94\u66F4\u65B0\u5F53\u524D\u5951\u7EA6\u3001\u6D4B\u8BD5\u8BA1\u5212\u6216\u8BCA\u65AD\u4EA7\u7269\u5E76\u5B8C\u6210\u5F53\u524D Step\uFF0C\u8BA9\u540E\u7EED CODE Step \u5B9E\u73B0\uFF1B\u4E0D\u8981\u5C1D\u8BD5\u88AB\u62D2\u7EDD\u7684\u8D8A\u6743\u5199\u5165\u3002\u82E5\u5931\u8D25\u662F\u7B2C\u4E09\u65B9\u4F9D\u8D56\u7F3A\u5931\u6216\u5E93\u9009\u578B\u9519\u8BEF\uFF0C\u5FC5\u987B\u7528 add_dependency \u5199\u5165\u771F\u5B9E\u5305\u540D\uFF0C\u6216\u628A\u6E90\u7801\u6539\u56DE HIGH_LEVEL_DESIGN \u9009\u5B9A\u7684\u771F\u5B9E\u5E93\uFF1B\u4E25\u7981\u5728 src/ \u751F\u4EA7\u4EE3\u7801\u91CC try/except ImportError \u540E\u4F2A\u9020 module\u3001fake class/function\u3001\u7A7A\u5B9E\u73B0\u6216 fallback mock \u6765\u7ED5\u8FC7\u9519\u8BEF\u3002\u8BF7\u5305\u542B read_file/code_search \u5148\u5B9A\u4F4D\u95EE\u9898\uFF0C\u518D\u4EE5 apply_patch / replace_in_file / add_dependency \u4F5C\u6700\u5C0F\u4FEE\u6539\uFF0C\u6700\u540E run_tests \u9A8C\u8BC1\u3002DEBUG \u91CD\u8BD5\u4E0D\u80FD\u53EA\u9760\u53EA\u8BFB\u68C0\u67E5\u5C31\u6807\u8BB0\u5B8C\u6210\uFF1A\u672C\u6B21\u91CD\u8BD5\u5FC5\u987B\u4EA7\u751F\u4E00\u6B21\u6210\u529F\u7684\u4FEE\u590D\u52A8\u4F5C\uFF0C\u6216\u4E00\u6B21\u6210\u529F\u7684\u9A8C\u8BC1\u547D\u4EE4\u3002\u5982\u679C\u4E0A\u4E00\u8F6E\u5931\u8D25\u539F\u56E0\u5305\u542B repeated read-only/probe actions\uFF0C\u8BF7\u628A\u5DF2\u6709 failure log \u89C6\u4E3A\u8DB3\u591F\u4E0A\u4E0B\u6587\uFF0C\u4E0B\u4E00\u6B65\u76F4\u63A5 patch/write/\u6539\u4F9D\u8D56\u6216\u6267\u884C\u9A8C\u8BC1\u547D\u4EE4\u3002\u5F53\u6D4B\u8BD5\u5DF2\u6B63\u5E38\u6267\u884C\u4F46\u5931\u8D25\u70B9\u662F\u8FD4\u56DE\u884C\u4E3A\u65AD\u8A00\u65F6\uFF0C\u7981\u6B62\u53CD\u590D\u91CD\u5199 fixture \u6216\u6837\u4F8B\u3002\u53EA\u6709\u8BC1\u636E\u660E\u786E\u662F\u6587\u4EF6\u7F3A\u5931\u3001fixture \u683C\u5F0F\u9519\u8BEF\u6216 fixture \u672C\u8EAB\u89E3\u6790\u5931\u8D25\u65F6\u624D\u4FEE\u6539 fixture\uFF1B\u5426\u5219\u5E94\u4FEE\u5B9E\u73B0\u3001\u63A5\u53E3\u5951\u7EA6\u3001\u4F9D\u8D56\u9009\u578B\u6216\u786E\u5B9E\u9519\u8BEF\u7684\u65AD\u8A00\u3002\u5982\u679C\u5931\u8D25\u65E5\u5FD7\u663E\u793A\u7F51\u7EDC/API \u8C03\u7528\u5931\u8D25\uFF0C\u4E0D\u5141\u8BB8\u53EA\u505C\u7559\u5728\u63A2\u6D4B\u63A5\u53E3\uFF1A\u6700\u591A\u8FDE\u7EED\u6267\u884C 2 \u6B21 http_fetch \u63A2\u6D4B\uFF1BHTTP 2xx \u4F46 body \u4E3A\u7A7A\u6216\u683C\u5F0F\u4E0D\u53EF\u7528\u4E0D\u7B97\u53EF\u7528\u63A5\u53E3\uFF1B\u968F\u540E\u5FC5\u987B patch \u771F\u5B9E\u96C6\u6210\u4EE3\u7801\uFF0C\u5E76\u7528 run_program \u548C run_tests \u9A8C\u8BC1\u3002\u5165\u53E3\u4ECD\u8F93\u51FA\u7F51\u7EDC/API \u5931\u8D25\u65F6\u4E0D\u5F97 done=true\u3002` + (suggestions ? `
|
|
1715
|
+
\u5F53\u672C\u6B21 DEBUG \u6B63\u5728\u5904\u7406 issue \u65F6\uFF0C\u6BCF\u4E00\u8F6E JSON \u90FD\u5FC5\u987B\u5728\u4FEE\u590D\u524D\u6216\u4FEE\u590D\u8FC7\u7A0B\u4E2D\u989D\u5916\u5305\u542B issueResolutionPlan\u3002\u8BE5\u65B9\u6848\u5FC5\u987B\u7B80\u6D01\u4E14\u53EF\u6267\u884C\uFF1A\u6839\u56E0\u5047\u8BBE\u3001\u8981\u4FEE\u6539\u7684\u6587\u4EF6/\u5951\u7EA6\u3001\u9A8C\u8BC1\u547D\u4EE4\u6216\u95E8\u7981\uFF0C\u4EE5\u53CA\u4EC0\u4E48\u8BC1\u636E\u4F1A\u63A8\u7FFB\u8BE5\u65B9\u6848\u3002DEBUG \u53EF\u4EE5\u4FEE\u6539\u5F53\u524D allowedWrites \u5185\u7684\u4E0A\u6E38\u6E90\u7801\u4E0E\u6D4B\u8BD5\u6587\u4EF6\uFF1B\u82E5\u5931\u8D25\u66B4\u9732\u7684\u662F\u5B9E\u73B0\u3001\u5951\u7EA6\u6216\u4E0B\u6E38\u8C03\u7528\u4E0D\u4E00\u81F4\uFF0C\u5FC5\u987B\u4FEE\u771F\u5B9E\u7F3A\u9677\uFF0C\u7981\u6B62\u901A\u8FC7\u524A\u5F31\u65AD\u8A00\u3001\u8DF3\u8FC7\u6D4B\u8BD5\u3001\u5220\u9664\u5931\u8D25\u7528\u4F8B\u6216\u53EA\u8FCE\u5408\u9519\u8BEF\u6D4B\u8BD5\u6765\u8FC7\u5173\u3002\u5982\u679C\u672C\u6B21\u56DE\u9000\u5904\u4E8E\u9700\u6C42/\u8BBE\u8BA1\u9636\u6BB5\uFF0C\u800C\u5177\u4F53\u6E90\u7801\u4FEE\u6539\u5C5E\u4E8E\u540E\u7EED V \u6A21\u578B Step \u4E14\u76EE\u6807\u6587\u4EF6\u4E0D\u5728\u5F53\u524D allowedWrites \u5185\uFF0C\u5E94\u66F4\u65B0\u5F53\u524D\u5951\u7EA6\u3001\u6D4B\u8BD5\u8BA1\u5212\u6216\u8BCA\u65AD\u4EA7\u7269\u5E76\u5B8C\u6210\u5F53\u524D Step\uFF0C\u8BA9\u540E\u7EED CODE Step \u5B9E\u73B0\uFF1B\u4E0D\u8981\u5C1D\u8BD5\u88AB\u62D2\u7EDD\u7684\u8D8A\u6743\u5199\u5165\u3002\u82E5\u5931\u8D25\u662F\u7B2C\u4E09\u65B9\u4F9D\u8D56\u7F3A\u5931\u6216\u5E93\u9009\u578B\u9519\u8BEF\uFF0C\u5FC5\u987B\u7528 add_dependency \u5199\u5165\u771F\u5B9E\u5305\u540D\uFF0C\u6216\u628A\u6E90\u7801\u6539\u56DE HIGH_LEVEL_DESIGN \u9009\u5B9A\u7684\u771F\u5B9E\u5E93\uFF1B\u4E25\u7981\u5728 src/ \u751F\u4EA7\u4EE3\u7801\u91CC try/except ImportError \u540E\u4F2A\u9020 module\u3001fake class/function\u3001\u7A7A\u5B9E\u73B0\u6216 fallback mock \u6765\u7ED5\u8FC7\u9519\u8BEF\u3002\u8BF7\u5305\u542B read_file/code_search \u5148\u5B9A\u4F4D\u95EE\u9898\uFF0C\u518D\u4EE5 apply_patch / replace_in_file / add_dependency \u4F5C\u6700\u5C0F\u4FEE\u6539\uFF0C\u6700\u540E run_tests \u9A8C\u8BC1\u3002DEBUG \u91CD\u8BD5\u4E0D\u80FD\u53EA\u9760\u53EA\u8BFB\u68C0\u67E5\u5C31\u6807\u8BB0\u5B8C\u6210\uFF1A\u672C\u6B21\u91CD\u8BD5\u5FC5\u987B\u4EA7\u751F\u4E00\u6B21\u6210\u529F\u7684\u4FEE\u590D\u52A8\u4F5C\uFF0C\u6216\u4E00\u6B21\u6210\u529F\u7684\u9A8C\u8BC1\u547D\u4EE4\u3002\u5982\u679C\u4E0A\u4E00\u8F6E\u5931\u8D25\u539F\u56E0\u5305\u542B repeated read-only/probe actions\uFF0C\u8BF7\u628A\u5DF2\u6709 failure log \u89C6\u4E3A\u8DB3\u591F\u4E0A\u4E0B\u6587\uFF0C\u4E0B\u4E00\u6B65\u76F4\u63A5 patch/write/\u6539\u4F9D\u8D56\u6216\u6267\u884C\u9A8C\u8BC1\u547D\u4EE4\u3002\u5F53\u6D4B\u8BD5\u5DF2\u6B63\u5E38\u6267\u884C\u4F46\u5931\u8D25\u70B9\u662F\u8FD4\u56DE\u884C\u4E3A\u65AD\u8A00\u65F6\uFF0C\u7981\u6B62\u53CD\u590D\u91CD\u5199 fixture \u6216\u6837\u4F8B\u3002\u53EA\u6709\u8BC1\u636E\u660E\u786E\u662F\u6587\u4EF6\u7F3A\u5931\u3001fixture \u683C\u5F0F\u9519\u8BEF\u6216 fixture \u672C\u8EAB\u89E3\u6790\u5931\u8D25\u65F6\u624D\u4FEE\u6539 fixture\uFF1B\u5426\u5219\u5E94\u4FEE\u5B9E\u73B0\u3001\u63A5\u53E3\u5951\u7EA6\u3001\u4F9D\u8D56\u9009\u578B\u6216\u786E\u5B9E\u9519\u8BEF\u7684\u65AD\u8A00\u3002\u5982\u679C\u5931\u8D25\u65E5\u5FD7\u663E\u793A\u7F51\u7EDC/API \u8C03\u7528\u5931\u8D25\uFF0C\u4E0D\u5141\u8BB8\u53EA\u505C\u7559\u5728\u63A2\u6D4B\u63A5\u53E3\uFF1A\u6700\u591A\u8FDE\u7EED\u6267\u884C 2 \u6B21 http_fetch \u63A2\u6D4B\uFF1BHTTP 2xx \u4F46 body \u4E3A\u7A7A\u6216\u683C\u5F0F\u4E0D\u53EF\u7528\u4E0D\u7B97\u53EF\u7528\u63A5\u53E3\uFF1B\u968F\u540E\u5FC5\u987B patch \u771F\u5B9E\u96C6\u6210\u4EE3\u7801\uFF0C\u5E76\u7528 run_program \u548C run_tests \u9A8C\u8BC1\u3002\u5165\u53E3\u4ECD\u8F93\u51FA\u7F51\u7EDC/API \u5931\u8D25\u65F6\u4E0D\u5F97 done=true\u3002` + (suggestions ? `
|
|
1707
1716
|
|
|
1708
1717
|
${suggestions}` : ""),
|
|
1709
1718
|
executorGlobalBlock: (globalPrompt) => `
|
|
@@ -1720,7 +1729,8 @@ ${sp}`,
|
|
|
1720
1729
|
executorFeedbackVerifyMissing: (paths) => `outputs \u4ECD\u7F3A\u5931\uFF1A${paths}\u3002\u8BF7\u7EE7\u7EED\u3002`,
|
|
1721
1730
|
executorFeedbackReadOnlyLoopWarning: (rounds, targets) => `\u5FAA\u73AF\u95E8\u7981\u8B66\u544A\uFF1A\u6700\u8FD1 ${rounds} \u8F6E\u53EA\u8C03\u7528\u4E86\u8BFB\u53D6/\u63A2\u6D4B\u5DE5\u5177` + (targets ? `\uFF08${targets}\uFF09` : "") + "\u3002\u4E0B\u4E00\u8F6E\u5FC5\u987B\u5305\u542B\u4E00\u6B21\u6210\u529F\u7684\u4FEE\u590D\u52A8\u4F5C\uFF08apply_patch / replace_in_file / write_file / add_dependency\uFF09\u6216\u660E\u786E\u7684\u9A8C\u8BC1\u52A8\u4F5C\uFF08run_tests / run_program\uFF09\u3002\u4E0D\u8981\u7EE7\u7EED\u53EA\u8C03\u7528 read_file\u3001list_dir\u3001code_search \u6216 http_fetch\u3002",
|
|
1722
1731
|
executorFeedbackReadOnlyRecoveryRequired: "\u53EA\u8BFB\u6062\u590D\u6A21\u5F0F\u5DF2\u542F\u7528\uFF1A\u4E0A\u4E00\u8F6E\u5DF2\u7ECF\u56E0\u4E3A\u6301\u7EED\u63A2\u6D4B\u5931\u8D25\u3002\u672C\u8F6E\u5FC5\u987B\u57FA\u4E8E\u5DF2\u6709 failure log \u76F4\u63A5 patch/write/\u6539\u4F9D\u8D56\u6216\u6267\u884C\u9A8C\u8BC1\uFF1B\u5982\u679C\u4E0B\u4E00\u8F6E\u4ECD\u7136\u53EA\u6709\u53EA\u8BFB/\u63A2\u6D4B\u52A8\u4F5C\uFF0C\u672C\u6B21\u91CD\u8BD5\u4F1A\u5931\u8D25\u5E76\u56DE\u9000\u3002",
|
|
1723
|
-
executorFeedbackRepairEvidenceMissing: "DEBUG \u5B8C\u6210\u65E0\u6548\uFF1A\u672C\u6B21\u91CD\u8BD5\u8FD8\u6CA1\u6709\u4EA7\u751F\u4FEE\u590D\u8BC1\u636E\u3002\u8BBE\u7F6E done=true \u524D\uFF0C\u5FC5\u987B\u81F3\u5C11\u5B8C\u6210\u4E00\u6B21\u6210\u529F\u7684\u4FEE\u590D\u52A8\u4F5C\u6216\u6210\u529F\u7684\u9A8C\u8BC1\u8FD0\u884C\uFF1B\u5426\u5219\u53EA\u80FD\u5728 thoughts \u4E2D\u7ED9\u51FA\u5177\u4F53 blocker \u540E\u505C\u6B62\u3002"
|
|
1732
|
+
executorFeedbackRepairEvidenceMissing: "DEBUG \u5B8C\u6210\u65E0\u6548\uFF1A\u672C\u6B21\u91CD\u8BD5\u8FD8\u6CA1\u6709\u4EA7\u751F\u4FEE\u590D\u8BC1\u636E\u3002\u8BBE\u7F6E done=true \u524D\uFF0C\u5FC5\u987B\u81F3\u5C11\u5B8C\u6210\u4E00\u6B21\u6210\u529F\u7684\u4FEE\u590D\u52A8\u4F5C\u6216\u6210\u529F\u7684\u9A8C\u8BC1\u8FD0\u884C\uFF1B\u5426\u5219\u53EA\u80FD\u5728 thoughts \u4E2D\u7ED9\u51FA\u5177\u4F53 blocker \u540E\u505C\u6B62\u3002",
|
|
1733
|
+
executorFeedbackIssueResolutionPlanMissing: "DEBUG issue \u5B8C\u6210\u65E0\u6548\uFF1AissueResolutionPlan \u662F issue resolved \u7684\u5FC5\u8981\u5B57\u6BB5\u3002\u8BF7\u8FD4\u56DE\u5305\u542B\u7B80\u660E\u5904\u7406\u65B9\u6848\u7684 JSON\uFF0C\u5E76\u540C\u65F6\u7ED9\u51FA\u5FC5\u8981\u4FEE\u590D\u6216\u9A8C\u8BC1\u52A8\u4F5C\u3002"
|
|
1724
1734
|
},
|
|
1725
1735
|
skills: {
|
|
1726
1736
|
patcher: "\u901A\u8FC7 apply_patch / replace_in_file \u5BF9\u5DF2\u6709\u6587\u4EF6\u505A\u5C0F\u6539\u52A8\uFF0C\u7981\u6B62\u6574\u6587\u4EF6\u8986\u76D6\u3002",
|
|
@@ -1741,7 +1751,7 @@ ${sp}`,
|
|
|
1741
1751
|
summaryOk: "\u5168\u90E8\u68C0\u67E5\u901A\u8FC7\u3002",
|
|
1742
1752
|
summaryWarn: (n) => `\u901A\u8FC7\uFF0C\u4F46\u6709 ${n} \u6761 warning\u3002`,
|
|
1743
1753
|
summaryFail: (n) => `\u68C0\u6D4B\u5230 ${n} \u9879\u5931\u8D25\u3002`,
|
|
1744
|
-
configLoadOk: (
|
|
1754
|
+
configLoadOk: (path31) => `\u914D\u7F6E\u5DF2\u52A0\u8F7D\uFF1A${path31}`,
|
|
1745
1755
|
configLoadFail: (msg) => `\u914D\u7F6E\u52A0\u8F7D\u5931\u8D25\uFF1A${msg}`,
|
|
1746
1756
|
configLocale: (locale) => `locale=${locale}`,
|
|
1747
1757
|
llmNoProviders: "config.llm.providers \u4E3A\u7A7A\uFF0C\u672A\u58F0\u660E\u4EFB\u4F55 provider",
|
|
@@ -1759,7 +1769,7 @@ ${sp}`,
|
|
|
1759
1769
|
roleOk: (role, provider) => `\u89D2\u8272 "${role}" \u2192 ${provider}`,
|
|
1760
1770
|
sandboxKind: (kind) => `sandbox=${kind}`,
|
|
1761
1771
|
sandboxNetworkPolicy: (policy, ports) => `network=${policy}` + (ports.length ? `\uFF08expose_ports=[${ports.join(", ")}]\uFF09` : ""),
|
|
1762
|
-
sandboxFullNoPorts: "network=full \u4F46\u672A\u914D\u7F6E expose_ports\u2014\u5BBF\u4E3B\u4FA7\u65E0\u6CD5\u8BBF\u95EE\u5BB9\u5668\u5185\u670D\u52A1\u3002\u8BF7\u5728 config.yaml \u4E2D\u8BBE\u7F6E `agent.
|
|
1772
|
+
sandboxFullNoPorts: "network=full \u4F46\u672A\u914D\u7F6E expose_ports\u2014\u5BBF\u4E3B\u4FA7\u65E0\u6CD5\u8BBF\u95EE\u5BB9\u5668\u5185\u670D\u52A1\u3002\u8BF7\u5728 config.yaml \u4E2D\u8BBE\u7F6E `agent.sandboxes.<language>.<local|docker>.limits.expose_ports: [<port>]`\u3002",
|
|
1763
1773
|
sandboxNodeMissing: "PATH \u4E0A\u627E\u4E0D\u5230 node\uFF08TypeScript subprocess \u6C99\u76D2\u5FC5\u9700\uFF09",
|
|
1764
1774
|
sandboxNodeOk: (version) => `node OK\uFF08${version}\uFF09`,
|
|
1765
1775
|
sandboxNpmMissing: "PATH \u4E0A\u627E\u4E0D\u5230 npm\uFF08TypeScript subprocess \u6C99\u76D2\u5FC5\u9700\uFF09",
|
|
@@ -1858,6 +1868,73 @@ var ProviderSchema = z.object({
|
|
|
1858
1868
|
think: z.boolean().optional()
|
|
1859
1869
|
});
|
|
1860
1870
|
var LocaleSchema = z.enum(["en", "zh"]);
|
|
1871
|
+
var TargetLanguageSchema = z.enum(["python", "typescript"]);
|
|
1872
|
+
var SandboxModeSchema = z.enum(["subprocess", "docker", "firejail"]);
|
|
1873
|
+
var SandboxLimitsSchema = z.object({
|
|
1874
|
+
cpu: z.number().positive().default(1),
|
|
1875
|
+
memory_mb: z.number().int().positive().default(1024),
|
|
1876
|
+
wall_seconds: z.number().int().positive().default(60),
|
|
1877
|
+
/**
|
|
1878
|
+
* Sandbox network policy.
|
|
1879
|
+
* - `off` — no network at all (`docker --network none`).
|
|
1880
|
+
* - `download-only` — outbound traffic allowed, no inbound port publishing.
|
|
1881
|
+
* - `pypi-only` — legacy value; rejected at sandbox creation rather than silently
|
|
1882
|
+
* allowing unrestricted outbound traffic.
|
|
1883
|
+
* - `full` — outbound + every port in `expose_ports` is published
|
|
1884
|
+
* to `127.0.0.1` so host-side tests can reach the app.
|
|
1885
|
+
*/
|
|
1886
|
+
network: z.enum(["off", "pypi-only", "download-only", "full"]).default("download-only"),
|
|
1887
|
+
/** Container ports to publish to 127.0.0.1 when `network=full`. */
|
|
1888
|
+
expose_ports: z.array(z.number().int().min(1).max(65535)).default([])
|
|
1889
|
+
}).default({
|
|
1890
|
+
cpu: 1,
|
|
1891
|
+
memory_mb: 1024,
|
|
1892
|
+
wall_seconds: 60,
|
|
1893
|
+
network: "download-only",
|
|
1894
|
+
expose_ports: []
|
|
1895
|
+
});
|
|
1896
|
+
var LocalSandboxSchema = z.object({
|
|
1897
|
+
sandbox_dir: z.string().min(1).optional(),
|
|
1898
|
+
python_bin: z.string().min(1).optional(),
|
|
1899
|
+
inherit_env: z.boolean().optional(),
|
|
1900
|
+
limits: SandboxLimitsSchema
|
|
1901
|
+
}).default(() => ({ limits: defaultSandboxLimits() }));
|
|
1902
|
+
var DockerSandboxSchema = z.object({
|
|
1903
|
+
image: z.string().default("python:3.11-slim"),
|
|
1904
|
+
workdir: z.string().default("/workspace"),
|
|
1905
|
+
pull: z.boolean().default(false),
|
|
1906
|
+
docker_bin: z.string().default("docker"),
|
|
1907
|
+
extra_run_args: z.array(z.string()).default([]),
|
|
1908
|
+
sandbox_dir: z.string().min(1).optional(),
|
|
1909
|
+
limits: SandboxLimitsSchema
|
|
1910
|
+
}).default({
|
|
1911
|
+
image: "python:3.11-slim",
|
|
1912
|
+
workdir: "/workspace",
|
|
1913
|
+
pull: false,
|
|
1914
|
+
docker_bin: "docker",
|
|
1915
|
+
extra_run_args: [],
|
|
1916
|
+
limits: defaultSandboxLimits()
|
|
1917
|
+
});
|
|
1918
|
+
var LanguageSandboxSchema = z.object({
|
|
1919
|
+
mode: SandboxModeSchema.default("subprocess"),
|
|
1920
|
+
local: LocalSandboxSchema,
|
|
1921
|
+
docker: DockerSandboxSchema
|
|
1922
|
+
}).default(() => ({
|
|
1923
|
+
mode: "subprocess",
|
|
1924
|
+
local: { limits: defaultSandboxLimits() },
|
|
1925
|
+
docker: {
|
|
1926
|
+
image: "python:3.11-slim",
|
|
1927
|
+
workdir: "/workspace",
|
|
1928
|
+
pull: false,
|
|
1929
|
+
docker_bin: "docker",
|
|
1930
|
+
extra_run_args: [],
|
|
1931
|
+
limits: defaultSandboxLimits()
|
|
1932
|
+
}
|
|
1933
|
+
}));
|
|
1934
|
+
var SandboxesSchema = z.object({
|
|
1935
|
+
python: LanguageSandboxSchema.optional(),
|
|
1936
|
+
typescript: LanguageSandboxSchema.optional()
|
|
1937
|
+
}).default({});
|
|
1861
1938
|
var LlmSchema = z.object({
|
|
1862
1939
|
default: z.string(),
|
|
1863
1940
|
providers: z.record(z.string(), ProviderSchema),
|
|
@@ -1865,7 +1942,7 @@ var LlmSchema = z.object({
|
|
|
1865
1942
|
* 角色 → provider 数组的映射。
|
|
1866
1943
|
* 兼容旧格式:单字符串 `Coder: ollama_code` 自动归一化为 `[ollama_code]`。
|
|
1867
1944
|
* 数组形式 `Coder: [ollama_code, openai]` 表示该角色的候选 LLM 池;
|
|
1868
|
-
* 实际选择顺序由
|
|
1945
|
+
* 实际选择顺序由 ScoreStore 有效评分降序决定;有效评分为用户覆盖优先,否则使用动态评分。
|
|
1869
1946
|
*/
|
|
1870
1947
|
roles: z.record(z.string(), z.union([z.string(), z.array(z.string())])).default({}).transform((obj) => {
|
|
1871
1948
|
const out = {};
|
|
@@ -1879,9 +1956,8 @@ var LlmSchema = z.object({
|
|
|
1879
1956
|
/** 可选:按角色指定 fallback 链(覆盖全局) */
|
|
1880
1957
|
role_fallbacks: z.record(z.string(), z.array(z.string())).default({}),
|
|
1881
1958
|
/**
|
|
1882
|
-
* Provider
|
|
1883
|
-
*
|
|
1884
|
-
* 用户手工设置评分 = 0 表示禁用;运行时自动评分只在 0.1~1.0 范围内调整。
|
|
1959
|
+
* Provider 兼容初值。运行时动态评分由 ScoreStore 写入 llm_scores.yaml;
|
|
1960
|
+
* 用户手动覆盖应写入 llm_scores_user.yaml。旧配置里显式 0 仍表示用户禁用。
|
|
1885
1961
|
*/
|
|
1886
1962
|
scores: z.record(z.string(), z.number().min(0)).default({}),
|
|
1887
1963
|
/**
|
|
@@ -1901,65 +1977,107 @@ var LlmSchema = z.object({
|
|
|
1901
1977
|
});
|
|
1902
1978
|
}
|
|
1903
1979
|
});
|
|
1980
|
+
var AgentSchema = z.object({
|
|
1981
|
+
/** @deprecated Target project language is inferred from topic/baseline. Kept for legacy configs only. */
|
|
1982
|
+
language: TargetLanguageSchema.optional(),
|
|
1983
|
+
max_steps: z.number().int().positive().default(50),
|
|
1984
|
+
max_debug_retries: z.number().int().positive().default(3),
|
|
1985
|
+
/** Debugger 滑动窗口的硬上限(默认 = max(max_debug_retries*4, 10))。 */
|
|
1986
|
+
max_debug_retries_cap: z.number().int().positive().optional(),
|
|
1987
|
+
max_rounds_per_step: z.number().int().positive().default(6),
|
|
1988
|
+
max_debug_rounds_per_step: z.number().int().positive().optional(),
|
|
1989
|
+
max_edit_lines_per_step: z.union([z.literal("auto"), z.number().int().positive()]).default("auto"),
|
|
1990
|
+
max_write_chunk_bytes: z.union([z.literal("auto"), z.number().int().positive()]).default("auto"),
|
|
1991
|
+
/** @deprecated Use agent.sandboxes.<language>.mode. */
|
|
1992
|
+
sandbox: SandboxModeSchema.optional(),
|
|
1993
|
+
/** @deprecated Use agent.sandboxes.<language>.<local|docker>.limits. */
|
|
1994
|
+
sandbox_limits: SandboxLimitsSchema.optional(),
|
|
1995
|
+
/** @deprecated Use agent.sandboxes.<language>.docker. */
|
|
1996
|
+
sandbox_docker: DockerSandboxSchema.optional(),
|
|
1997
|
+
sandboxes: SandboxesSchema
|
|
1998
|
+
}).transform((agent) => {
|
|
1999
|
+
const legacyLanguage = agent.language ?? "python";
|
|
2000
|
+
const legacyMode = agent.sandbox ?? "subprocess";
|
|
2001
|
+
const legacyLimits = agent.sandbox_limits ?? defaultSandboxLimits();
|
|
2002
|
+
const defaults = {
|
|
2003
|
+
python: defaultLanguageSandbox("python", legacyMode, legacyLimits),
|
|
2004
|
+
typescript: defaultLanguageSandbox("typescript", legacyMode, legacyLimits)
|
|
2005
|
+
};
|
|
2006
|
+
const sandboxes = {
|
|
2007
|
+
python: mergeLanguageSandbox(
|
|
2008
|
+
defaults.python,
|
|
2009
|
+
agent.sandboxes.python,
|
|
2010
|
+
legacyLanguage === "python" ? agent.sandbox_docker : void 0
|
|
2011
|
+
),
|
|
2012
|
+
typescript: mergeLanguageSandbox(
|
|
2013
|
+
defaults.typescript,
|
|
2014
|
+
agent.sandboxes.typescript,
|
|
2015
|
+
legacyLanguage === "typescript" ? agent.sandbox_docker : void 0
|
|
2016
|
+
)
|
|
2017
|
+
};
|
|
2018
|
+
return {
|
|
2019
|
+
...agent,
|
|
2020
|
+
language: legacyLanguage,
|
|
2021
|
+
sandbox: sandboxes.python.mode,
|
|
2022
|
+
sandbox_limits: sandboxes.python.local.limits,
|
|
2023
|
+
sandbox_docker: sandboxes.python.docker,
|
|
2024
|
+
sandboxes
|
|
2025
|
+
};
|
|
2026
|
+
});
|
|
1904
2027
|
var ConfigSchema = z.object({
|
|
1905
2028
|
/** CLI / prompt locale. Accepts 'en' (default) or 'zh'. */
|
|
1906
2029
|
locale: LocaleSchema.optional(),
|
|
1907
2030
|
/** @deprecated use `locale` instead. Kept as a backwards-compatible alias. */
|
|
1908
2031
|
ui_language: LocaleSchema.optional(),
|
|
1909
2032
|
llm: LlmSchema,
|
|
1910
|
-
agent:
|
|
1911
|
-
language: z.enum(["python", "typescript"]).default("python"),
|
|
1912
|
-
max_steps: z.number().int().positive().default(50),
|
|
1913
|
-
max_debug_retries: z.number().int().positive().default(3),
|
|
1914
|
-
/** Debugger 滑动窗口的硬上限(默认 = max(max_debug_retries*4, 10))。 */
|
|
1915
|
-
max_debug_retries_cap: z.number().int().positive().optional(),
|
|
1916
|
-
max_rounds_per_step: z.number().int().positive().default(6),
|
|
1917
|
-
max_debug_rounds_per_step: z.number().int().positive().optional(),
|
|
1918
|
-
max_edit_lines_per_step: z.union([z.literal("auto"), z.number().int().positive()]).default("auto"),
|
|
1919
|
-
max_write_chunk_bytes: z.union([z.literal("auto"), z.number().int().positive()]).default("auto"),
|
|
1920
|
-
sandbox: z.enum(["subprocess", "docker", "firejail"]).default("subprocess"),
|
|
1921
|
-
sandbox_limits: z.object({
|
|
1922
|
-
cpu: z.number().positive().default(1),
|
|
1923
|
-
memory_mb: z.number().int().positive().default(1024),
|
|
1924
|
-
wall_seconds: z.number().int().positive().default(60),
|
|
1925
|
-
/**
|
|
1926
|
-
* Sandbox network policy.
|
|
1927
|
-
* - `off` — no network at all (`docker --network none`).
|
|
1928
|
-
* - `download-only` — outbound traffic allowed, no inbound port publishing
|
|
1929
|
-
* (default; lets python pip-install / fetch web pages).
|
|
1930
|
-
* - `pypi-only` — legacy value; rejected at sandbox creation rather than silently
|
|
1931
|
-
* allowing unrestricted outbound traffic.
|
|
1932
|
-
* - `full` — outbound + every port in `expose_ports` is published
|
|
1933
|
-
* to `127.0.0.1` so host-side tests can reach the app.
|
|
1934
|
-
*/
|
|
1935
|
-
network: z.enum(["off", "pypi-only", "download-only", "full"]).default("download-only"),
|
|
1936
|
-
/** Container ports to publish to 127.0.0.1 when `network=full`. */
|
|
1937
|
-
expose_ports: z.array(z.number().int().min(1).max(65535)).default([])
|
|
1938
|
-
}).default({
|
|
1939
|
-
cpu: 1,
|
|
1940
|
-
memory_mb: 1024,
|
|
1941
|
-
wall_seconds: 60,
|
|
1942
|
-
network: "download-only",
|
|
1943
|
-
expose_ports: []
|
|
1944
|
-
}),
|
|
1945
|
-
sandbox_docker: z.object({
|
|
1946
|
-
image: z.string().default("python:3.11-slim"),
|
|
1947
|
-
workdir: z.string().default("/workspace"),
|
|
1948
|
-
pull: z.boolean().default(false),
|
|
1949
|
-
docker_bin: z.string().default("docker"),
|
|
1950
|
-
extra_run_args: z.array(z.string()).default([])
|
|
1951
|
-
}).default({
|
|
1952
|
-
image: "python:3.11-slim",
|
|
1953
|
-
workdir: "/workspace",
|
|
1954
|
-
pull: false,
|
|
1955
|
-
docker_bin: "docker",
|
|
1956
|
-
extra_run_args: []
|
|
1957
|
-
})
|
|
1958
|
-
})
|
|
2033
|
+
agent: AgentSchema
|
|
1959
2034
|
}).transform(({ locale, ui_language, ...rest }) => ({
|
|
1960
2035
|
locale: locale ?? ui_language ?? "en",
|
|
1961
2036
|
...rest
|
|
1962
2037
|
}));
|
|
2038
|
+
function defaultSandboxLimits() {
|
|
2039
|
+
return {
|
|
2040
|
+
cpu: 1,
|
|
2041
|
+
memory_mb: 1024,
|
|
2042
|
+
wall_seconds: 60,
|
|
2043
|
+
network: "download-only",
|
|
2044
|
+
expose_ports: []
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
function defaultLanguageSandbox(language, mode, limits) {
|
|
2048
|
+
return {
|
|
2049
|
+
mode,
|
|
2050
|
+
local: {
|
|
2051
|
+
sandbox_dir: `.sandbox/${language}`,
|
|
2052
|
+
limits: { ...limits, expose_ports: [...limits.expose_ports ?? []] }
|
|
2053
|
+
},
|
|
2054
|
+
docker: {
|
|
2055
|
+
image: language === "typescript" ? "node:24-slim" : "python:3.11-slim",
|
|
2056
|
+
workdir: "/workspace",
|
|
2057
|
+
pull: false,
|
|
2058
|
+
docker_bin: "docker",
|
|
2059
|
+
extra_run_args: [],
|
|
2060
|
+
sandbox_dir: `.sandbox/${language}`,
|
|
2061
|
+
limits: { ...limits, expose_ports: [...limits.expose_ports ?? []] }
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
function mergeLanguageSandbox(defaults, override, legacyDocker) {
|
|
2066
|
+
const dockerOverride = legacyDocker ?? override?.docker;
|
|
2067
|
+
return {
|
|
2068
|
+
mode: override?.mode ?? defaults.mode,
|
|
2069
|
+
local: {
|
|
2070
|
+
...defaults.local,
|
|
2071
|
+
...override?.local ?? {},
|
|
2072
|
+
limits: override?.local?.limits ?? defaults.local.limits
|
|
2073
|
+
},
|
|
2074
|
+
docker: {
|
|
2075
|
+
...defaults.docker,
|
|
2076
|
+
...dockerOverride ?? {},
|
|
2077
|
+
limits: dockerOverride?.limits ?? defaults.docker.limits
|
|
2078
|
+
}
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
1963
2081
|
function getXCompilerPath() {
|
|
1964
2082
|
const env = xcEnv("PATH");
|
|
1965
2083
|
if (env && env.trim()) return path.resolve(env.trim());
|
|
@@ -2023,6 +2141,11 @@ var LOOP_REPEATS = 12;
|
|
|
2023
2141
|
var LOOP_MIN_LEN = 1500;
|
|
2024
2142
|
var LOOP_MIN_WINDOW = 96;
|
|
2025
2143
|
var LOOP_MAX_PERIOD = 256;
|
|
2144
|
+
var TEXT_LOOP_MIN_LEN = 6e3;
|
|
2145
|
+
var TEXT_LOOP_TAIL = 14e3;
|
|
2146
|
+
var TEXT_LOOP_SAMPLE_LEN = 48;
|
|
2147
|
+
var TEXT_LOOP_STRIDE = 8;
|
|
2148
|
+
var TEXT_LOOP_REPEATS = 5;
|
|
2026
2149
|
function detectCyclicTokenLoop(aggregate) {
|
|
2027
2150
|
if (aggregate.length < LOOP_MIN_LEN) return false;
|
|
2028
2151
|
const maxPeriod = Math.min(LOOP_MAX_PERIOD, Math.floor(aggregate.length / LOOP_REPEATS));
|
|
@@ -2044,6 +2167,35 @@ function detectCyclicTokenLoop(aggregate) {
|
|
|
2044
2167
|
}
|
|
2045
2168
|
return false;
|
|
2046
2169
|
}
|
|
2170
|
+
function detectRepeatedTextLoop(aggregate) {
|
|
2171
|
+
if (aggregate.length < TEXT_LOOP_MIN_LEN) return false;
|
|
2172
|
+
const tail = aggregate.slice(-TEXT_LOOP_TAIL).replace(/\s+/gu, " ").trim();
|
|
2173
|
+
if (tail.length < TEXT_LOOP_MIN_LEN) return false;
|
|
2174
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2175
|
+
for (let i = 0; i + TEXT_LOOP_SAMPLE_LEN <= tail.length; i += TEXT_LOOP_STRIDE) {
|
|
2176
|
+
const sample = tail.slice(i, i + TEXT_LOOP_SAMPLE_LEN);
|
|
2177
|
+
if (!isHighSignalLoopSample(sample)) continue;
|
|
2178
|
+
const prior = seen.get(sample);
|
|
2179
|
+
if (!prior) {
|
|
2180
|
+
seen.set(sample, { count: 1, first: i, last: i });
|
|
2181
|
+
continue;
|
|
2182
|
+
}
|
|
2183
|
+
const next = { count: prior.count + 1, first: prior.first, last: i };
|
|
2184
|
+
seen.set(sample, next);
|
|
2185
|
+
if (next.count >= TEXT_LOOP_REPEATS && next.last - next.first >= TEXT_LOOP_SAMPLE_LEN * 3) {
|
|
2186
|
+
return true;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
return false;
|
|
2190
|
+
}
|
|
2191
|
+
function isHighSignalLoopSample(sample) {
|
|
2192
|
+
const chars = [...sample];
|
|
2193
|
+
const signalChars = chars.filter((ch) => /[\p{L}\p{N}]/u.test(ch));
|
|
2194
|
+
if (signalChars.length < 20) return false;
|
|
2195
|
+
if (signalChars.length / Math.max(1, chars.length) < 0.4) return false;
|
|
2196
|
+
const unique = new Set(signalChars.map((ch) => ch.toLowerCase())).size;
|
|
2197
|
+
return unique >= 10;
|
|
2198
|
+
}
|
|
2047
2199
|
var RepeatTokenDetector = class {
|
|
2048
2200
|
constructor(threshold = 40) {
|
|
2049
2201
|
this.threshold = threshold;
|
|
@@ -2222,6 +2374,12 @@ function streamPostNdjson(url, body, watchdog, onLine, shouldStopWhen) {
|
|
|
2222
2374
|
);
|
|
2223
2375
|
return;
|
|
2224
2376
|
}
|
|
2377
|
+
if (detectRepeatedTextLoop(aggregate)) {
|
|
2378
|
+
fail(
|
|
2379
|
+
new Error("detected repeated text loop in stream (semantic tail repetition); aborting")
|
|
2380
|
+
);
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2225
2383
|
}
|
|
2226
2384
|
if (obj.error) {
|
|
2227
2385
|
fail(new Error(`Ollama error: ${obj.error}`));
|
|
@@ -2250,6 +2408,10 @@ function streamPostNdjson(url, body, watchdog, onLine, shouldStopWhen) {
|
|
|
2250
2408
|
fail(new Error("detected token loop in stream; aborting"));
|
|
2251
2409
|
return;
|
|
2252
2410
|
}
|
|
2411
|
+
if (detectRepeatedTextLoop(aggregate)) {
|
|
2412
|
+
fail(new Error("detected repeated text loop in stream; aborting"));
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2253
2415
|
}
|
|
2254
2416
|
});
|
|
2255
2417
|
res.on("end", () => {
|
|
@@ -2520,6 +2682,9 @@ var OpenAIClient = class {
|
|
|
2520
2682
|
if (detectCyclicTokenLoop(aggregate)) {
|
|
2521
2683
|
throw new Error("detected cyclic token loop in OpenAI stream (periodic tail); aborting");
|
|
2522
2684
|
}
|
|
2685
|
+
if (detectRepeatedTextLoop(aggregate)) {
|
|
2686
|
+
throw new Error("detected repeated text loop in OpenAI stream (semantic tail repetition); aborting");
|
|
2687
|
+
}
|
|
2523
2688
|
if (maxOutputChars > 0 && expectsJsonObject && aggregate.length > maxOutputChars && hasInvalidJsonPrefix(aggregate)) {
|
|
2524
2689
|
throw new Error(`OpenAI stream exceeded ${maxOutputChars} chars without a valid JSON prefix; aborting`);
|
|
2525
2690
|
}
|
|
@@ -3060,18 +3225,24 @@ import { promises as fs2 } from "fs";
|
|
|
3060
3225
|
import path2 from "path";
|
|
3061
3226
|
import YAML2 from "yaml";
|
|
3062
3227
|
var CLUSTER_PROVIDER_TAG = "cluster";
|
|
3228
|
+
var LLM_DYNAMIC_SCORES_FILE = "llm_scores.yaml";
|
|
3229
|
+
var LLM_USER_SCORES_FILE = "llm_scores_user.yaml";
|
|
3063
3230
|
var ScoreStore = class _ScoreStore {
|
|
3064
3231
|
constructor(configPath, initial = {}, audit, opts = {}) {
|
|
3065
3232
|
this.audit = audit;
|
|
3066
|
-
|
|
3233
|
+
const configDir = path2.dirname(configPath);
|
|
3234
|
+
this.sidecarPath = path2.join(configDir, LLM_DYNAMIC_SCORES_FILE);
|
|
3235
|
+
this.userScoresPath = path2.join(configDir, LLM_USER_SCORES_FILE);
|
|
3067
3236
|
this.clusterProviders = new Set(opts.clusterProviderNames ?? []);
|
|
3068
3237
|
const bounds = normalizeClusterBounds(opts.clusterScoreMin, opts.clusterScoreMax);
|
|
3069
3238
|
this.clusterMin = bounds.min;
|
|
3070
3239
|
this.clusterMax = bounds.max;
|
|
3071
3240
|
for (const [k, v] of Object.entries(initial)) {
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3241
|
+
if (v === _ScoreStore.DISABLED) {
|
|
3242
|
+
this.userScores.set(k, _ScoreStore.DISABLED);
|
|
3243
|
+
} else {
|
|
3244
|
+
this.dynamicScores.set(k, this.clampDynamic(k, v));
|
|
3245
|
+
}
|
|
3075
3246
|
}
|
|
3076
3247
|
}
|
|
3077
3248
|
audit;
|
|
@@ -3083,24 +3254,28 @@ var ScoreStore = class _ScoreStore {
|
|
|
3083
3254
|
static CLUSTER_MAX = 0.5;
|
|
3084
3255
|
static DECAY = 0.5;
|
|
3085
3256
|
static BOOST = 0.1;
|
|
3086
|
-
|
|
3087
|
-
|
|
3257
|
+
dynamicScores = /* @__PURE__ */ new Map();
|
|
3258
|
+
userScores = /* @__PURE__ */ new Map();
|
|
3088
3259
|
clusterProviders;
|
|
3089
3260
|
clusterMin;
|
|
3090
3261
|
clusterMax;
|
|
3091
3262
|
dirty = false;
|
|
3092
3263
|
writeQueue = Promise.resolve();
|
|
3093
3264
|
sidecarPath;
|
|
3094
|
-
|
|
3265
|
+
userScoresPath;
|
|
3266
|
+
/** 异步加载动态 sidecar 与用户覆盖文件;失败/不存在不抛错,使用 ctor 提供的初值。 */
|
|
3095
3267
|
async load() {
|
|
3268
|
+
await this.loadDynamicScores();
|
|
3269
|
+
await this.loadUserScores();
|
|
3270
|
+
}
|
|
3271
|
+
async loadDynamicScores() {
|
|
3096
3272
|
try {
|
|
3097
3273
|
const raw = await fs2.readFile(this.sidecarPath, "utf8");
|
|
3098
3274
|
const parsed = YAML2.parse(raw);
|
|
3099
3275
|
if (parsed && typeof parsed === "object") {
|
|
3100
3276
|
for (const [k, v] of Object.entries(parsed)) {
|
|
3101
3277
|
if (typeof v === "number" && Number.isFinite(v)) {
|
|
3102
|
-
|
|
3103
|
-
this.scores.set(k, this.clampDynamic(k, v));
|
|
3278
|
+
this.dynamicScores.set(k, this.clampPersistedDynamic(k, v));
|
|
3104
3279
|
}
|
|
3105
3280
|
}
|
|
3106
3281
|
}
|
|
@@ -3113,44 +3288,73 @@ var ScoreStore = class _ScoreStore {
|
|
|
3113
3288
|
}
|
|
3114
3289
|
}
|
|
3115
3290
|
}
|
|
3291
|
+
async loadUserScores() {
|
|
3292
|
+
try {
|
|
3293
|
+
const raw = await fs2.readFile(this.userScoresPath, "utf8");
|
|
3294
|
+
const parsed = YAML2.parse(raw);
|
|
3295
|
+
if (parsed && typeof parsed === "object") {
|
|
3296
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
3297
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
3298
|
+
this.userScores.set(k, this.clampUserScore(v));
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
} catch (err) {
|
|
3303
|
+
if (err.code !== "ENOENT") {
|
|
3304
|
+
await this.audit?.event("llm.error", t().llm.scoreReadFailed(this.userScoresPath, err.message), {
|
|
3305
|
+
messageId: "llm.user_score_read_failed",
|
|
3306
|
+
sidecarPath: this.userScoresPath
|
|
3307
|
+
});
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3116
3311
|
get(name) {
|
|
3117
|
-
|
|
3312
|
+
if (this.userScores.has(name)) return this.userScores.get(name);
|
|
3313
|
+
return this.dynamicScores.has(name) ? this.dynamicScores.get(name) : this.defaultFor(name);
|
|
3118
3314
|
}
|
|
3119
|
-
/**
|
|
3315
|
+
/** 主动设置动态评分;用户覆盖只从配置或 llm_scores_user.yaml 读取。 */
|
|
3120
3316
|
set(name, value, reason) {
|
|
3121
|
-
if (this.userDisabled.has(name) && value !== _ScoreStore.DISABLED) return;
|
|
3122
3317
|
const v = value === _ScoreStore.DISABLED ? _ScoreStore.DISABLED : this.clampDynamic(name, value);
|
|
3123
|
-
const prev = this.
|
|
3318
|
+
const prev = this.dynamicScores.get(name);
|
|
3124
3319
|
if (prev === v) return;
|
|
3125
|
-
this.
|
|
3320
|
+
this.dynamicScores.set(name, v);
|
|
3126
3321
|
this.dirty = true;
|
|
3127
|
-
void this.audit?.event("llm.score", t().llm.scoreChanged(name, v.toFixed(2), (prev ??
|
|
3322
|
+
void this.audit?.event("llm.score", t().llm.scoreChanged(name, v.toFixed(2), (prev ?? this.defaultFor(name)).toFixed(2)), {
|
|
3128
3323
|
messageId: "llm.score_changed",
|
|
3129
3324
|
provider: name,
|
|
3130
3325
|
score: v,
|
|
3131
|
-
previous: prev ??
|
|
3326
|
+
previous: prev ?? this.defaultFor(name),
|
|
3132
3327
|
reason: reason ?? "set"
|
|
3133
3328
|
});
|
|
3134
3329
|
this.scheduleSave();
|
|
3135
3330
|
}
|
|
3136
3331
|
decay(name, reason) {
|
|
3137
|
-
if (this.
|
|
3138
|
-
|
|
3332
|
+
if (this.isUserDisabled(name)) return;
|
|
3333
|
+
const cur = this.dynamicScore(name);
|
|
3334
|
+
if (cur === _ScoreStore.DISABLED) return;
|
|
3335
|
+
this.set(name, Math.max(this.minFor(name), cur - _ScoreStore.DECAY), reason);
|
|
3139
3336
|
}
|
|
3140
3337
|
boost(name, reason) {
|
|
3141
|
-
|
|
3338
|
+
if (this.isUserDisabled(name)) return;
|
|
3339
|
+
const cur = this.dynamicScore(name);
|
|
3142
3340
|
if (cur === _ScoreStore.DISABLED) return;
|
|
3143
3341
|
if (cur >= this.maxFor(name)) return;
|
|
3144
3342
|
this.set(name, Math.min(this.maxFor(name), cur + _ScoreStore.BOOST), reason);
|
|
3145
3343
|
}
|
|
3146
|
-
/** True only for providers explicitly disabled by user config
|
|
3344
|
+
/** True only for providers explicitly disabled by user config or llm_scores_user.yaml. */
|
|
3147
3345
|
isUserDisabled(name) {
|
|
3148
|
-
return this.
|
|
3346
|
+
return this.userScores.get(name) === _ScoreStore.DISABLED;
|
|
3149
3347
|
}
|
|
3150
|
-
/**
|
|
3348
|
+
/** 动态评分快照(用于持久化、测试与 audit 输出;不包含用户覆盖)。 */
|
|
3151
3349
|
snapshot() {
|
|
3152
3350
|
const out = {};
|
|
3153
|
-
for (const [k, v] of this.
|
|
3351
|
+
for (const [k, v] of this.dynamicScores) out[k] = v;
|
|
3352
|
+
return out;
|
|
3353
|
+
}
|
|
3354
|
+
/** 用户覆盖快照(用于测试/诊断;运行时不会改写用户文件)。 */
|
|
3355
|
+
userSnapshot() {
|
|
3356
|
+
const out = {};
|
|
3357
|
+
for (const [k, v] of this.userScores) out[k] = v;
|
|
3154
3358
|
return out;
|
|
3155
3359
|
}
|
|
3156
3360
|
/** 等待待写入完成(CLI 退出前调用,确保评分已落盘)。 */
|
|
@@ -3188,10 +3392,8 @@ ${t().llm.scoreFileSemantics}
|
|
|
3188
3392
|
isClusterProvider(name) {
|
|
3189
3393
|
return this.clusterProviders.has(name);
|
|
3190
3394
|
}
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
if (v === _ScoreStore.DISABLED) return _ScoreStore.DISABLED;
|
|
3194
|
-
return this.clampDynamic(name, v);
|
|
3395
|
+
dynamicScore(name) {
|
|
3396
|
+
return this.dynamicScores.has(name) ? this.dynamicScores.get(name) : this.defaultFor(name);
|
|
3195
3397
|
}
|
|
3196
3398
|
clampDynamic(name, v) {
|
|
3197
3399
|
if (!Number.isFinite(v)) return this.defaultFor(name);
|
|
@@ -3200,6 +3402,18 @@ ${t().llm.scoreFileSemantics}
|
|
|
3200
3402
|
if (v > this.maxFor(name)) return this.maxFor(name);
|
|
3201
3403
|
return v;
|
|
3202
3404
|
}
|
|
3405
|
+
clampPersistedDynamic(name, v) {
|
|
3406
|
+
if (!Number.isFinite(v)) return this.defaultFor(name);
|
|
3407
|
+
if (v === _ScoreStore.DISABLED) return _ScoreStore.DISABLED;
|
|
3408
|
+
return this.clampDynamic(name, v);
|
|
3409
|
+
}
|
|
3410
|
+
clampUserScore(v) {
|
|
3411
|
+
if (!Number.isFinite(v)) return _ScoreStore.DEFAULT;
|
|
3412
|
+
if (v <= _ScoreStore.DISABLED) return _ScoreStore.DISABLED;
|
|
3413
|
+
if (v < _ScoreStore.MIN) return _ScoreStore.MIN;
|
|
3414
|
+
if (v > _ScoreStore.MAX) return _ScoreStore.MAX;
|
|
3415
|
+
return v;
|
|
3416
|
+
}
|
|
3203
3417
|
};
|
|
3204
3418
|
function normalizeClusterBounds(rawMin, rawMax) {
|
|
3205
3419
|
const min = clampBound(rawMin ?? ScoreStore.CLUSTER_MIN);
|
|
@@ -3618,6 +3832,9 @@ var PlanSchema = z2.object({
|
|
|
3618
3832
|
dependencies: dependencies ?? pythonRequirements ?? []
|
|
3619
3833
|
}));
|
|
3620
3834
|
|
|
3835
|
+
// src/core/language.ts
|
|
3836
|
+
import { promises as fs5 } from "fs";
|
|
3837
|
+
|
|
3621
3838
|
// src/core/entry_gate.ts
|
|
3622
3839
|
import path5 from "path";
|
|
3623
3840
|
|
|
@@ -3644,8 +3861,10 @@ function detectNetworkApiFailure(text) {
|
|
|
3644
3861
|
function isTestAssertionDiagnosticLine(line) {
|
|
3645
3862
|
const text = line.trim();
|
|
3646
3863
|
if (!text) return false;
|
|
3864
|
+
if (/^\d+\|\s/u.test(text)) return true;
|
|
3647
3865
|
if (/^(?:[→>-]\s*)?expected\b/iu.test(text)) return true;
|
|
3648
3866
|
if (/\bAssertionError\b/iu.test(text) && /\bexpected\b/iu.test(text)) return true;
|
|
3867
|
+
if (/\bexpect\s*\(/u.test(text) && /\.(?:to|not)\w*\s*\(/u.test(text)) return true;
|
|
3649
3868
|
return /\bexpected\b[\s\S]{0,240}\b(?:got|received|to\s+(?:be|equal|throw|contain|have|match))\b/iu.test(text);
|
|
3650
3869
|
}
|
|
3651
3870
|
function isTestRunnerStatusLine(line) {
|
|
@@ -3692,8 +3911,8 @@ async function autoFixSrcImports(ws, audit) {
|
|
|
3692
3911
|
const candidates = [];
|
|
3693
3912
|
if (await ws.exists("src/main.py")) candidates.push("src/main.py");
|
|
3694
3913
|
try {
|
|
3695
|
-
const
|
|
3696
|
-
const entries = await
|
|
3914
|
+
const fs29 = await import("fs/promises");
|
|
3915
|
+
const entries = await fs29.readdir(ws.abs("src"), { withFileTypes: true });
|
|
3697
3916
|
for (const e of entries) {
|
|
3698
3917
|
if (!e.isDirectory()) continue;
|
|
3699
3918
|
const rel = `src/${e.name}/__main__.py`;
|
|
@@ -3844,8 +4063,8 @@ async function detectPythonEntrypoint(ws) {
|
|
|
3844
4063
|
if (await ws.exists("src/main.py")) return fileCandidate("src/main.py");
|
|
3845
4064
|
if (await ws.exists("src/__main__.py")) return fileCandidate("src/__main__.py");
|
|
3846
4065
|
try {
|
|
3847
|
-
const
|
|
3848
|
-
const entries = await
|
|
4066
|
+
const fs29 = await import("fs/promises");
|
|
4067
|
+
const entries = await fs29.readdir(ws.abs("src"), { withFileTypes: true });
|
|
3849
4068
|
for (const e of entries) {
|
|
3850
4069
|
if (!e.isDirectory()) continue;
|
|
3851
4070
|
const rel = `src/${e.name}/__main__.py`;
|
|
@@ -3904,7 +4123,7 @@ var typescriptProfile = {
|
|
|
3904
4123
|
manifestFile: "package.json",
|
|
3905
4124
|
codeExtensions: [".ts", ".tsx"],
|
|
3906
4125
|
seedManifestFromDeps: false,
|
|
3907
|
-
defaultDockerImage: "node:
|
|
4126
|
+
defaultDockerImage: "node:24-slim",
|
|
3908
4127
|
renderManifest(deps) {
|
|
3909
4128
|
const pkg = {
|
|
3910
4129
|
name: "app",
|
|
@@ -3933,6 +4152,9 @@ var typescriptProfile = {
|
|
|
3933
4152
|
},
|
|
3934
4153
|
plannerPromptOverride: "",
|
|
3935
4154
|
executorPromptOverride: "",
|
|
4155
|
+
async autoFixImports(ws, audit) {
|
|
4156
|
+
return autoFixTypeScriptTypeOnlyImports(ws, audit);
|
|
4157
|
+
},
|
|
3936
4158
|
async probeEntry(ws, sandbox) {
|
|
3937
4159
|
return probeTsEntrypoint(ws, sandbox);
|
|
3938
4160
|
}
|
|
@@ -4082,6 +4304,99 @@ async function readJsonFile(ws, rel) {
|
|
|
4082
4304
|
return null;
|
|
4083
4305
|
}
|
|
4084
4306
|
}
|
|
4307
|
+
var TYPE_ONLY_IMPORTS_BY_PACKAGE = {
|
|
4308
|
+
axios: /* @__PURE__ */ new Set([
|
|
4309
|
+
"AxiosAdapter",
|
|
4310
|
+
"AxiosBasicCredentials",
|
|
4311
|
+
"AxiosHeaderValue",
|
|
4312
|
+
"AxiosInstance",
|
|
4313
|
+
"AxiosInterceptorManager",
|
|
4314
|
+
"AxiosPromise",
|
|
4315
|
+
"AxiosProxyConfig",
|
|
4316
|
+
"AxiosRequestConfig",
|
|
4317
|
+
"AxiosRequestHeaders",
|
|
4318
|
+
"AxiosResponse",
|
|
4319
|
+
"AxiosResponseHeaders",
|
|
4320
|
+
"CreateAxiosDefaults",
|
|
4321
|
+
"InternalAxiosRequestConfig",
|
|
4322
|
+
"RawAxiosRequestHeaders"
|
|
4323
|
+
])
|
|
4324
|
+
};
|
|
4325
|
+
async function autoFixTypeScriptTypeOnlyImports(ws, audit) {
|
|
4326
|
+
const files = await listTypeScriptSourceFiles(ws, "src");
|
|
4327
|
+
const fixed = [];
|
|
4328
|
+
for (const rel of files) {
|
|
4329
|
+
const original = await ws.readFile(rel);
|
|
4330
|
+
const next = rewriteKnownTypeOnlyImports(original);
|
|
4331
|
+
if (next === original) continue;
|
|
4332
|
+
await ws.writeFile(rel, next);
|
|
4333
|
+
fixed.push(rel);
|
|
4334
|
+
await audit.event("note", `fixed type-only imports in ${rel}`, {
|
|
4335
|
+
messageId: "audit.typescript_imports_autofix",
|
|
4336
|
+
path: rel
|
|
4337
|
+
});
|
|
4338
|
+
}
|
|
4339
|
+
return fixed;
|
|
4340
|
+
}
|
|
4341
|
+
async function listTypeScriptSourceFiles(ws, dir) {
|
|
4342
|
+
const abs = ws.abs(dir);
|
|
4343
|
+
let entries;
|
|
4344
|
+
try {
|
|
4345
|
+
entries = await fs5.readdir(abs, { withFileTypes: true });
|
|
4346
|
+
} catch {
|
|
4347
|
+
return [];
|
|
4348
|
+
}
|
|
4349
|
+
const files = [];
|
|
4350
|
+
for (const entry of entries) {
|
|
4351
|
+
const rel = `${dir}/${entry.name}`.replace(/\\/g, "/");
|
|
4352
|
+
if (entry.isDirectory()) {
|
|
4353
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
4354
|
+
files.push(...await listTypeScriptSourceFiles(ws, rel));
|
|
4355
|
+
} else if (entry.isFile() && /\.tsx?$/.test(entry.name)) {
|
|
4356
|
+
files.push(rel);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
return files.sort();
|
|
4360
|
+
}
|
|
4361
|
+
function rewriteKnownTypeOnlyImports(source) {
|
|
4362
|
+
const importRe = /^import\s+([^'"\n]+?)\s+from\s+(['"])([^'"]+)\2\s*;?$/gm;
|
|
4363
|
+
return source.replace(importRe, (full, clauseRaw, quote, specifier) => {
|
|
4364
|
+
const knownTypes = TYPE_ONLY_IMPORTS_BY_PACKAGE[specifier];
|
|
4365
|
+
if (!knownTypes) return full;
|
|
4366
|
+
if (clauseRaw.trim().startsWith("type ")) return full;
|
|
4367
|
+
const parsed = splitImportClause(clauseRaw);
|
|
4368
|
+
if (!parsed?.named.length) return full;
|
|
4369
|
+
const valueNamed = [];
|
|
4370
|
+
const typeNamed = [];
|
|
4371
|
+
for (const item of parsed.named) {
|
|
4372
|
+
const importedName = item.replace(/^type\s+/u, "").split(/\s+as\s+/iu)[0]?.trim() ?? "";
|
|
4373
|
+
if (knownTypes.has(importedName)) typeNamed.push(item.replace(/^type\s+/u, "").trim());
|
|
4374
|
+
else valueNamed.push(item);
|
|
4375
|
+
}
|
|
4376
|
+
if (typeNamed.length === 0) return full;
|
|
4377
|
+
const lines = [];
|
|
4378
|
+
if (parsed.defaultImport && valueNamed.length > 0) {
|
|
4379
|
+
lines.push(`import ${parsed.defaultImport}, { ${valueNamed.join(", ")} } from ${quote}${specifier}${quote};`);
|
|
4380
|
+
} else if (parsed.defaultImport) {
|
|
4381
|
+
lines.push(`import ${parsed.defaultImport} from ${quote}${specifier}${quote};`);
|
|
4382
|
+
} else if (valueNamed.length > 0) {
|
|
4383
|
+
lines.push(`import { ${valueNamed.join(", ")} } from ${quote}${specifier}${quote};`);
|
|
4384
|
+
}
|
|
4385
|
+
lines.push(`import type { ${typeNamed.join(", ")} } from ${quote}${specifier}${quote};`);
|
|
4386
|
+
return lines.join("\n");
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
function splitImportClause(clauseRaw) {
|
|
4390
|
+
const clause = clauseRaw.trim();
|
|
4391
|
+
const namedMatch = clause.match(/\{([\s\S]*)\}$/u);
|
|
4392
|
+
if (!namedMatch) return void 0;
|
|
4393
|
+
const beforeNamed = clause.slice(0, namedMatch.index).replace(/,\s*$/u, "").trim();
|
|
4394
|
+
const named = (namedMatch[1] ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
4395
|
+
return {
|
|
4396
|
+
defaultImport: beforeNamed || void 0,
|
|
4397
|
+
named
|
|
4398
|
+
};
|
|
4399
|
+
}
|
|
4085
4400
|
|
|
4086
4401
|
// src/core/architecture.ts
|
|
4087
4402
|
var COMPLEXITY_SURFACES = [
|
|
@@ -4273,7 +4588,7 @@ function validateArchitectureContract(modules, steps, language, demand) {
|
|
|
4273
4588
|
}
|
|
4274
4589
|
allTestPaths.add(testPath);
|
|
4275
4590
|
}
|
|
4276
|
-
const owners = codeSteps.filter((step) => module.sourcePaths.every((
|
|
4591
|
+
const owners = codeSteps.filter((step) => module.sourcePaths.every((path31) => pathCoveredByOutputs(path31, step.outputs))).sort((a, b) => (stepIndex.get(a.id) ?? 0) - (stepIndex.get(b.id) ?? 0));
|
|
4277
4592
|
if (owners.length === 0) {
|
|
4278
4593
|
issues.push({
|
|
4279
4594
|
message: `${module.id} must map all sourcePaths to a CODE macro step; found 0.`
|
|
@@ -4303,7 +4618,7 @@ function validateArchitectureContract(modules, steps, language, demand) {
|
|
|
4303
4618
|
ownedModules.push(module);
|
|
4304
4619
|
modulesByCodeOwner.set(codeOwner.id, ownedModules);
|
|
4305
4620
|
const matchingTests = testSteps.filter(
|
|
4306
|
-
(step) => module.testPaths.some((
|
|
4621
|
+
(step) => module.testPaths.some((path31) => pathCoveredByOutputs(path31, step.outputs))
|
|
4307
4622
|
);
|
|
4308
4623
|
if (matchingTests.length === 0) {
|
|
4309
4624
|
issues.push({ message: `${module.id} testPaths are not produced by any MODULE_TEST step.` });
|
|
@@ -4360,21 +4675,21 @@ function flattenSubTasks(step) {
|
|
|
4360
4675
|
}
|
|
4361
4676
|
return out;
|
|
4362
4677
|
}
|
|
4363
|
-
function pathCoveredByOutputs(
|
|
4364
|
-
const normalizedPath = normalizePath(
|
|
4678
|
+
function pathCoveredByOutputs(path31, outputs) {
|
|
4679
|
+
const normalizedPath = normalizePath(path31);
|
|
4365
4680
|
return outputs.some((output) => {
|
|
4366
4681
|
const normalizedOutput = normalizePath(output);
|
|
4367
4682
|
return normalizedPath === normalizedOutput || normalizedPath.startsWith(`${normalizedOutput}/`);
|
|
4368
4683
|
});
|
|
4369
4684
|
}
|
|
4370
|
-
function isSourcePath(
|
|
4371
|
-
return
|
|
4685
|
+
function isSourcePath(path31, extensions) {
|
|
4686
|
+
return path31.startsWith("src/") && extensions.some((extension) => path31.endsWith(extension));
|
|
4372
4687
|
}
|
|
4373
|
-
function isTestPath(
|
|
4374
|
-
return
|
|
4688
|
+
function isTestPath(path31, extensions) {
|
|
4689
|
+
return path31.startsWith("tests/") && extensions.some((extension) => path31.endsWith(extension));
|
|
4375
4690
|
}
|
|
4376
|
-
function normalizePath(
|
|
4377
|
-
return
|
|
4691
|
+
function normalizePath(path31) {
|
|
4692
|
+
return path31.replace(/\\/g, "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
|
|
4378
4693
|
}
|
|
4379
4694
|
function transitivelyDependsOn(step, targetId, byId) {
|
|
4380
4695
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4666,6 +4981,18 @@ var DOC_PATH_ALIASES = {
|
|
|
4666
4981
|
"docs/integration-test.md": DOC_NAMES.integrationTest,
|
|
4667
4982
|
"docs/module-test.md": DOC_NAMES.moduleTest,
|
|
4668
4983
|
"docs/functional-test.md": DOC_NAMES.functionalTest,
|
|
4984
|
+
"docs/unit-test-plan.md": DOC_NAMES.unitTestPlan,
|
|
4985
|
+
"docs/unit_test_plan.md": DOC_NAMES.unitTestPlan,
|
|
4986
|
+
"docs/tests/unit_test_plan.md": DOC_NAMES.unitTestPlan,
|
|
4987
|
+
"docs/integration-test-plan.md": DOC_NAMES.integrationTestPlan,
|
|
4988
|
+
"docs/integration_test_plan.md": DOC_NAMES.integrationTestPlan,
|
|
4989
|
+
"docs/tests/integration_test_plan.md": DOC_NAMES.integrationTestPlan,
|
|
4990
|
+
"docs/module-test-plan.md": DOC_NAMES.moduleTestPlan,
|
|
4991
|
+
"docs/module_test_plan.md": DOC_NAMES.moduleTestPlan,
|
|
4992
|
+
"docs/tests/module_test_plan.md": DOC_NAMES.moduleTestPlan,
|
|
4993
|
+
"docs/functional-test-plan.md": DOC_NAMES.functionalTestPlan,
|
|
4994
|
+
"docs/functional_test_plan.md": DOC_NAMES.functionalTestPlan,
|
|
4995
|
+
"docs/tests/functional_test_plan.md": DOC_NAMES.functionalTestPlan,
|
|
4669
4996
|
"docs/quick-start.md": DOC_NAMES.quickstart,
|
|
4670
4997
|
"docs/quick_start.md": DOC_NAMES.quickstart,
|
|
4671
4998
|
"docs/quickstart.md": DOC_NAMES.quickstart,
|
|
@@ -4673,13 +5000,25 @@ var DOC_PATH_ALIASES = {
|
|
|
4673
5000
|
"docs/api_guide.md": DOC_NAMES.apiGuide,
|
|
4674
5001
|
"docs/api-guide.md": DOC_NAMES.apiGuide
|
|
4675
5002
|
};
|
|
5003
|
+
function canonicalTestPlanAlias(path31) {
|
|
5004
|
+
const normalized = path31.replace(/\\/g, "/");
|
|
5005
|
+
const match = normalized.match(
|
|
5006
|
+
/^docs\/(?:tests\/)?(?:\d{1,2}[-_])?(functional|integration|module|unit)[-_]?test[-_]?plan\.md$/iu
|
|
5007
|
+
);
|
|
5008
|
+
const kind = match?.[1]?.toLowerCase();
|
|
5009
|
+
if (kind === "functional") return DOC_NAMES.functionalTestPlan;
|
|
5010
|
+
if (kind === "integration") return DOC_NAMES.integrationTestPlan;
|
|
5011
|
+
if (kind === "module") return DOC_NAMES.moduleTestPlan;
|
|
5012
|
+
if (kind === "unit") return DOC_NAMES.unitTestPlan;
|
|
5013
|
+
return void 0;
|
|
5014
|
+
}
|
|
4676
5015
|
function calibrateDocPaths(steps, projectType = "application") {
|
|
4677
|
-
const remap = (p) => DOC_PATH_ALIASES[p] ?? p;
|
|
5016
|
+
const remap = (p) => DOC_PATH_ALIASES[p] ?? canonicalTestPlanAlias(p) ?? p;
|
|
4678
5017
|
const dropTopic = (p) => p !== DOC_NAMES.topic;
|
|
4679
5018
|
return steps.map((s) => {
|
|
4680
5019
|
const iterationId = s.iterationId ?? "P1";
|
|
4681
|
-
const inputs = (s.inputs ?? []).map((p) => iterationScopedInput(remap(p), s.phase, iterationId));
|
|
4682
|
-
let outputs = (s.outputs ?? []).map((p) => iterationScopedDoc(remap(p), s.phase, iterationId)).filter(dropTopic);
|
|
5020
|
+
const inputs = dedup((s.inputs ?? []).map((p) => iterationScopedInput(remap(p), s.phase, iterationId)));
|
|
5021
|
+
let outputs = dedup((s.outputs ?? []).map((p) => iterationScopedDoc(remap(p), s.phase, iterationId)).filter(dropTopic));
|
|
4683
5022
|
outputs = outputs.filter((out) => {
|
|
4684
5023
|
const ownerPhase = testPlanOwnerPhase(out, iterationId);
|
|
4685
5024
|
return !ownerPhase || ownerPhase === s.phase;
|
|
@@ -4723,6 +5062,31 @@ function calibrateVModelDependencies(steps) {
|
|
|
4723
5062
|
}
|
|
4724
5063
|
return out;
|
|
4725
5064
|
}
|
|
5065
|
+
function calibrateArchitectureModuleDependencies(modules, dependencies) {
|
|
5066
|
+
const architectureModules = (modules ?? []).map((module) => ({
|
|
5067
|
+
...module,
|
|
5068
|
+
dependencies: [...module.dependencies ?? []]
|
|
5069
|
+
}));
|
|
5070
|
+
const moduleIds = new Set(architectureModules.map((module) => module.id));
|
|
5071
|
+
const projectDependencies = [...dependencies ?? []];
|
|
5072
|
+
for (const module of architectureModules) {
|
|
5073
|
+
const internalDependencies = [];
|
|
5074
|
+
for (const rawDependency of module.dependencies) {
|
|
5075
|
+
const dependency = String(rawDependency ?? "").trim();
|
|
5076
|
+
if (!dependency) continue;
|
|
5077
|
+
if (moduleIds.has(dependency) || /^M\d{3,}$/u.test(dependency)) {
|
|
5078
|
+
internalDependencies.push(dependency);
|
|
5079
|
+
} else {
|
|
5080
|
+
projectDependencies.push(dependency);
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
module.dependencies = dedup(internalDependencies);
|
|
5084
|
+
}
|
|
5085
|
+
return {
|
|
5086
|
+
architectureModules,
|
|
5087
|
+
dependencies: dedup(projectDependencies.map((dependency) => dependency.trim()).filter(Boolean))
|
|
5088
|
+
};
|
|
5089
|
+
}
|
|
4726
5090
|
function calibrateLanguageStepOwnership(steps, args) {
|
|
4727
5091
|
const profile = getLanguageProfile(args.language);
|
|
4728
5092
|
const out = steps.map((step) => ({
|
|
@@ -4784,35 +5148,35 @@ function preferredTestOwnerPhase(output, modules) {
|
|
|
4784
5148
|
function findIterationStep(steps, iterationId, phase) {
|
|
4785
5149
|
return steps.find((step) => (step.iterationId ?? "P1") === iterationId && step.phase === phase);
|
|
4786
5150
|
}
|
|
4787
|
-
function testPlanOwnerPhase(
|
|
5151
|
+
function testPlanOwnerPhase(path31, iterationId) {
|
|
4788
5152
|
for (const [sourcePhase, testPhase] of Object.entries(V_MODEL_SOURCE_TO_TEST_PHASE)) {
|
|
4789
|
-
if (
|
|
5153
|
+
if (path31 === testPlanDocForIteration(testPhase, iterationId)) {
|
|
4790
5154
|
return sourcePhase;
|
|
4791
5155
|
}
|
|
4792
5156
|
}
|
|
4793
5157
|
return void 0;
|
|
4794
5158
|
}
|
|
4795
|
-
function iterationScopedDoc(
|
|
4796
|
-
if (iterationId === "P1") return
|
|
4797
|
-
if (
|
|
5159
|
+
function iterationScopedDoc(path31, phase, iterationId) {
|
|
5160
|
+
if (iterationId === "P1") return path31;
|
|
5161
|
+
if (path31 === DOC_NAMES.readme && phase === "FUNCTIONAL_TEST") {
|
|
4798
5162
|
return `docs/iterations/${iterationId}/README.md`;
|
|
4799
5163
|
}
|
|
4800
|
-
if (
|
|
5164
|
+
if (path31 === DOC_NAMES.quickstart && phase === "FUNCTIONAL_TEST") {
|
|
4801
5165
|
return `docs/iterations/${iterationId}/quickstart.md`;
|
|
4802
5166
|
}
|
|
4803
|
-
if (
|
|
5167
|
+
if (path31 === DOC_NAMES.apiGuide && phase === "FUNCTIONAL_TEST") {
|
|
4804
5168
|
return `docs/iterations/${iterationId}/api-guide.md`;
|
|
4805
5169
|
}
|
|
4806
5170
|
for (const [docPhase, canonical] of Object.entries(PHASE_DOC)) {
|
|
4807
|
-
if (
|
|
4808
|
-
return phaseDocForIteration(docPhase, iterationId) ??
|
|
5171
|
+
if (path31 === canonical) {
|
|
5172
|
+
return phaseDocForIteration(docPhase, iterationId) ?? path31;
|
|
4809
5173
|
}
|
|
4810
5174
|
}
|
|
4811
|
-
return
|
|
5175
|
+
return path31;
|
|
4812
5176
|
}
|
|
4813
|
-
function iterationScopedInput(
|
|
4814
|
-
if (iterationId === "P1" || phase === "REQUIREMENT_ANALYSIS") return
|
|
4815
|
-
return iterationScopedDoc(
|
|
5177
|
+
function iterationScopedInput(path31, phase, iterationId) {
|
|
5178
|
+
if (iterationId === "P1" || phase === "REQUIREMENT_ANALYSIS") return path31;
|
|
5179
|
+
return iterationScopedDoc(path31, phase, iterationId);
|
|
4816
5180
|
}
|
|
4817
5181
|
function calibrateStepIds(steps) {
|
|
4818
5182
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -5143,8 +5507,8 @@ function dedupModules(modules) {
|
|
|
5143
5507
|
}
|
|
5144
5508
|
return out;
|
|
5145
5509
|
}
|
|
5146
|
-
function isTestImplementationPath(
|
|
5147
|
-
return /^tests\/.+\.(?:py|ts|tsx)$/i.test(
|
|
5510
|
+
function isTestImplementationPath(path31) {
|
|
5511
|
+
return /^tests\/.+\.(?:py|ts|tsx)$/i.test(path31);
|
|
5148
5512
|
}
|
|
5149
5513
|
function isNonModuleTestPhase(phase) {
|
|
5150
5514
|
return phase === "UNIT_TEST" || phase === "INTEGRATION_TEST" || phase === "FUNCTIONAL_TEST";
|
|
@@ -5167,7 +5531,7 @@ function testPathPrefixForPhase(phase) {
|
|
|
5167
5531
|
return "unit";
|
|
5168
5532
|
}
|
|
5169
5533
|
function inferUnitTestExtension(modules) {
|
|
5170
|
-
return modules.some((module) => module.testPaths.some((
|
|
5534
|
+
return modules.some((module) => module.testPaths.some((path31) => /\.tsx?$/i.test(path31))) ? ".test.ts" : ".py";
|
|
5171
5535
|
}
|
|
5172
5536
|
function collectTransitiveDependencyIds(step, stepById) {
|
|
5173
5537
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5358,7 +5722,8 @@ var Planner = class {
|
|
|
5358
5722
|
intent: opts.intent ?? "greenfield",
|
|
5359
5723
|
hasBaseline: !!opts.hasBaseline,
|
|
5360
5724
|
complex: demand.nonTrivial,
|
|
5361
|
-
projectShapeAmbiguous
|
|
5725
|
+
projectShapeAmbiguous,
|
|
5726
|
+
languageAmbiguous: !!opts.languageAmbiguous
|
|
5362
5727
|
});
|
|
5363
5728
|
const rep = makeStreamReporter("Planner.clarify", this.llm.name);
|
|
5364
5729
|
let provider;
|
|
@@ -5382,7 +5747,8 @@ var Planner = class {
|
|
|
5382
5747
|
// 在 provider fallback 层校验问题集质量,避免“只有两三个泛泛问题”直接进入 Gate 1。
|
|
5383
5748
|
validate: (t2) => validateClarifyJson(t2, demand.nonTrivial, {
|
|
5384
5749
|
projectShapeAmbiguous,
|
|
5385
|
-
externalApiRequired
|
|
5750
|
+
externalApiRequired,
|
|
5751
|
+
languageAmbiguous: !!opts.languageAmbiguous
|
|
5386
5752
|
})
|
|
5387
5753
|
}
|
|
5388
5754
|
);
|
|
@@ -5453,14 +5819,14 @@ ${t().prompts.plannerSelfMode}` : "")
|
|
|
5453
5819
|
},
|
|
5454
5820
|
{ role: "user", content: prompt + feedback }
|
|
5455
5821
|
],
|
|
5456
|
-
validate: (t2) => parsePhaseStepPlanJson(t2, parseContext, phasePlan, currentPhase
|
|
5822
|
+
validate: (t2) => parsePhaseStepPlanJson(t2, parseContext, phasePlan, currentPhase)
|
|
5457
5823
|
});
|
|
5458
5824
|
await this.audit?.plannerThought("decompose", text, {
|
|
5459
5825
|
qaCount: input2.clarifications.length,
|
|
5460
5826
|
provider,
|
|
5461
5827
|
phaseId: currentPhase.id
|
|
5462
5828
|
});
|
|
5463
|
-
return parsePhaseStepPlanJson(text, parseContext, phasePlan, currentPhase
|
|
5829
|
+
return parsePhaseStepPlanJson(text, parseContext, phasePlan, currentPhase);
|
|
5464
5830
|
}
|
|
5465
5831
|
async chatWithStructuredValidationRetry(input2) {
|
|
5466
5832
|
const maxAttempts = plannerStructuredRepairAttemptLimit(input2.context);
|
|
@@ -5539,7 +5905,7 @@ function formatPlannerValidationFeedback(err) {
|
|
|
5539
5905
|
function isPlannerStructuredValidationError(err) {
|
|
5540
5906
|
const message = errorMessage2(err);
|
|
5541
5907
|
if (isPlannerTransportFailure(message)) return false;
|
|
5542
|
-
return /^Planner (?:architecture|phase|JSON|draft|complexityAssessment|implementationPhases|iteration)/u.test(message);
|
|
5908
|
+
return /^Planner (?:architecture|phase|PhasePlan|JSON|draft|complexityAssessment|implementationPhases|iteration)/u.test(message);
|
|
5543
5909
|
}
|
|
5544
5910
|
function isPlannerTransportFailure(message) {
|
|
5545
5911
|
const text = message.toLowerCase();
|
|
@@ -5580,6 +5946,12 @@ function buildPlan(draft, opts = {}) {
|
|
|
5580
5946
|
draft.requirementDigest
|
|
5581
5947
|
);
|
|
5582
5948
|
const phaseId = implementationPhases.find((phase) => phase.status === "current")?.id ?? draft.steps.find((step) => step.iterationId)?.iterationId ?? "P1";
|
|
5949
|
+
const architectureDependencyCalibration = calibrateArchitectureModuleDependencies(
|
|
5950
|
+
draft.architectureModules ?? [],
|
|
5951
|
+
draft.dependencies
|
|
5952
|
+
);
|
|
5953
|
+
const architectureModules = architectureDependencyCalibration.architectureModules;
|
|
5954
|
+
const draftDependencies = architectureDependencyCalibration.dependencies;
|
|
5583
5955
|
const iterated = normalizeStepIterations(draft.steps, implementationPhases);
|
|
5584
5956
|
const shaped = calibrateLanguageStepOwnership(
|
|
5585
5957
|
calibrateVModelDependencies(
|
|
@@ -5588,14 +5960,14 @@ function buildPlan(draft, opts = {}) {
|
|
|
5588
5960
|
{
|
|
5589
5961
|
language,
|
|
5590
5962
|
intent: opts.intent ?? "greenfield",
|
|
5591
|
-
architectureModules
|
|
5963
|
+
architectureModules
|
|
5592
5964
|
}
|
|
5593
5965
|
);
|
|
5594
|
-
const mapped = calibrateArchitectureStepMappings(shaped,
|
|
5595
|
-
const contracted = injectArchitectureContractPrompts(mapped,
|
|
5966
|
+
const mapped = calibrateArchitectureStepMappings(shaped, architectureModules);
|
|
5967
|
+
const contracted = injectArchitectureContractPrompts(mapped, architectureModules);
|
|
5596
5968
|
const languageContracted = injectLanguageContractPrompts(contracted, language);
|
|
5597
5969
|
const steps = calibratePlanCoverage(languageContracted, language);
|
|
5598
|
-
const dependencies = language === "python" ? calibratePythonRequirements(
|
|
5970
|
+
const dependencies = language === "python" ? calibratePythonRequirements(draftDependencies) : [...new Set((draftDependencies ?? []).map((d) => d.trim()).filter(Boolean))];
|
|
5599
5971
|
return {
|
|
5600
5972
|
version: "1",
|
|
5601
5973
|
language,
|
|
@@ -5605,7 +5977,7 @@ function buildPlan(draft, opts = {}) {
|
|
|
5605
5977
|
requirementDigest: draft.requirementDigest,
|
|
5606
5978
|
complexityAssessment,
|
|
5607
5979
|
implementationPhases,
|
|
5608
|
-
architectureModules
|
|
5980
|
+
architectureModules,
|
|
5609
5981
|
globalPrompt: draft.globalPrompt,
|
|
5610
5982
|
baselineSummary: opts.baselineSummary ?? "",
|
|
5611
5983
|
dependencies,
|
|
@@ -5815,6 +6187,11 @@ function validateClarifyJson(text, complex, opts = {}) {
|
|
|
5815
6187
|
"clarify missing required external API credential question: ask whether the user has an API/key/token; if not, default to open no-key APIs"
|
|
5816
6188
|
);
|
|
5817
6189
|
}
|
|
6190
|
+
if (opts.languageAmbiguous && !parsed.some(isDevelopmentLanguageClarification)) {
|
|
6191
|
+
throw new Error(
|
|
6192
|
+
"clarify missing required development language question: ask whether the project should use Python or TypeScript/Node.js, with Python as the default option"
|
|
6193
|
+
);
|
|
6194
|
+
}
|
|
5818
6195
|
return parsed;
|
|
5819
6196
|
}
|
|
5820
6197
|
function parsePhasePlanJson(text, context) {
|
|
@@ -5882,7 +6259,7 @@ function parsePhasePlanJson(text, context) {
|
|
|
5882
6259
|
implementationPhases
|
|
5883
6260
|
};
|
|
5884
6261
|
}
|
|
5885
|
-
function parsePhaseStepPlanJson(text, context, phasePlan,
|
|
6262
|
+
function parsePhaseStepPlanJson(text, context, phasePlan, currentPhase) {
|
|
5886
6263
|
const data = safeJson(text);
|
|
5887
6264
|
if (!data || typeof data !== "object") {
|
|
5888
6265
|
throw new Error("Planner did not return a JSON object for phase Step planning.");
|
|
@@ -5890,7 +6267,7 @@ function parsePhaseStepPlanJson(text, context, phasePlan, currentIterationId) {
|
|
|
5890
6267
|
const obj = data;
|
|
5891
6268
|
const rawDeps = Array.isArray(obj.dependencies) ? obj.dependencies : Array.isArray(obj.pythonRequirements) ? obj.pythonRequirements : [];
|
|
5892
6269
|
const draft = {
|
|
5893
|
-
requirementDigest: typeof obj.requirementDigest === "string" && obj.requirementDigest.trim() ? obj.requirementDigest : phasePlan.requirementDigest,
|
|
6270
|
+
requirementDigest: typeof obj.requirementDigest === "string" && obj.requirementDigest.trim() ? obj.requirementDigest : currentPhase.objective || phasePlan.requirementDigest,
|
|
5894
6271
|
globalPrompt: typeof obj.globalPrompt === "string" && obj.globalPrompt.trim() ? obj.globalPrompt : phasePlan.globalPrompt,
|
|
5895
6272
|
projectType: phasePlan.projectType,
|
|
5896
6273
|
complexityAssessment: phasePlan.complexityAssessment,
|
|
@@ -5899,7 +6276,11 @@ function parsePhaseStepPlanJson(text, context, phasePlan, currentIterationId) {
|
|
|
5899
6276
|
architectureModules: obj.architectureModules,
|
|
5900
6277
|
steps: obj.steps
|
|
5901
6278
|
};
|
|
5902
|
-
const parsed = parseDraftPlanJson(JSON.stringify(draft),
|
|
6279
|
+
const parsed = parseDraftPlanJson(JSON.stringify(draft), {
|
|
6280
|
+
...context,
|
|
6281
|
+
phaseDemandText: phaseDemandText(currentPhase)
|
|
6282
|
+
});
|
|
6283
|
+
const currentIterationId = currentPhase.id;
|
|
5903
6284
|
const wrongIteration = parsed.steps.find((step) => (step.iterationId ?? "P1") !== currentIterationId);
|
|
5904
6285
|
if (wrongIteration) {
|
|
5905
6286
|
throw new Error(
|
|
@@ -5934,7 +6315,7 @@ function parseDraftPlanJson(text, context) {
|
|
|
5934
6315
|
context?.baselineSummary ?? ""
|
|
5935
6316
|
].join("\n"));
|
|
5936
6317
|
const rawDeps = Array.isArray(obj.dependencies) ? obj.dependencies : Array.isArray(obj.pythonRequirements) ? obj.pythonRequirements : [];
|
|
5937
|
-
|
|
6318
|
+
let dependencies = rawDeps.filter((s) => typeof s === "string");
|
|
5938
6319
|
if (context) {
|
|
5939
6320
|
const validPhaseNames = new Set(PHASES);
|
|
5940
6321
|
const nonCanonical = steps.map((rawStep, index) => {
|
|
@@ -5967,7 +6348,12 @@ function parseDraftPlanJson(text, context) {
|
|
|
5967
6348
|
if (!architectureResult.success) {
|
|
5968
6349
|
throw new Error(`Planner architectureModules invalid: ${architectureResult.error.issues.map((i) => i.message).join("; ")}`);
|
|
5969
6350
|
}
|
|
5970
|
-
const
|
|
6351
|
+
const architectureDependencyCalibration = calibrateArchitectureModuleDependencies(
|
|
6352
|
+
architectureResult.data,
|
|
6353
|
+
dependencies
|
|
6354
|
+
);
|
|
6355
|
+
const architectureModules = architectureDependencyCalibration.architectureModules;
|
|
6356
|
+
dependencies = architectureDependencyCalibration.dependencies;
|
|
5971
6357
|
const parsedComplexityAssessment = parseComplexityAssessment(obj.complexityAssessment);
|
|
5972
6358
|
if (context && !parsedComplexityAssessment) {
|
|
5973
6359
|
throw new Error("Planner JSON missing valid complexityAssessment; complexity must be assessed during plan decomposition.");
|
|
@@ -6001,14 +6387,7 @@ function parseDraftPlanJson(text, context) {
|
|
|
6001
6387
|
}
|
|
6002
6388
|
if (context) {
|
|
6003
6389
|
const demand = analyzeArchitectureDemand(
|
|
6004
|
-
|
|
6005
|
-
requirementDigest: digest,
|
|
6006
|
-
rawRequirement: context.rawRequirement,
|
|
6007
|
-
userAddenda: context.userAddenda,
|
|
6008
|
-
globalPrompt,
|
|
6009
|
-
baselineSummary: context.baselineSummary,
|
|
6010
|
-
intent: context.intent
|
|
6011
|
-
},
|
|
6390
|
+
architectureDemandInputForDraft(context, digest, globalPrompt),
|
|
6012
6391
|
context.language
|
|
6013
6392
|
);
|
|
6014
6393
|
const forcedPhaseSplit = hasForcedPhaseSplit([
|
|
@@ -6058,6 +6437,34 @@ function parseDraftPlanJson(text, context) {
|
|
|
6058
6437
|
steps: stepsWithIterations
|
|
6059
6438
|
};
|
|
6060
6439
|
}
|
|
6440
|
+
function architectureDemandInputForDraft(context, digest, globalPrompt) {
|
|
6441
|
+
if (!context.phaseDemandText) {
|
|
6442
|
+
return {
|
|
6443
|
+
requirementDigest: digest,
|
|
6444
|
+
rawRequirement: context.rawRequirement,
|
|
6445
|
+
userAddenda: context.userAddenda,
|
|
6446
|
+
globalPrompt,
|
|
6447
|
+
baselineSummary: context.baselineSummary,
|
|
6448
|
+
intent: context.intent
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6451
|
+
return {
|
|
6452
|
+
requirementDigest: context.phaseDemandText,
|
|
6453
|
+
baselineSummary: context.baselineSummary,
|
|
6454
|
+
intent: context.intent
|
|
6455
|
+
};
|
|
6456
|
+
}
|
|
6457
|
+
function phaseDemandText(currentPhase) {
|
|
6458
|
+
const gate = currentPhase.verificationGate;
|
|
6459
|
+
return [
|
|
6460
|
+
currentPhase.title,
|
|
6461
|
+
currentPhase.objective,
|
|
6462
|
+
currentPhase.scope.join("\n"),
|
|
6463
|
+
currentPhase.deliverables.join("\n"),
|
|
6464
|
+
gate?.summary ?? "",
|
|
6465
|
+
gate?.checks.join("\n") ?? ""
|
|
6466
|
+
].map((part) => part.trim()).filter(Boolean).join("\n");
|
|
6467
|
+
}
|
|
6061
6468
|
function parseComplexityAssessment(value) {
|
|
6062
6469
|
const result = ComplexityAssessmentSchema.safeParse(value);
|
|
6063
6470
|
return result.success ? result.data : void 0;
|
|
@@ -6144,6 +6551,15 @@ ${question.options.map((option) => option.answer).join("\n")}`.toLowerCase();
|
|
|
6144
6551
|
const externalApiContext = /\b(api|url|endpoint|provider|external|third[- ]party|fetch|request)\b/u.test(text) || /外部|第三方|接口|数据源|天气|节假日|联网/u.test(text);
|
|
6145
6552
|
return externalApiContext && asksCredential && mentionsNoKeyFallback;
|
|
6146
6553
|
}
|
|
6554
|
+
function isDevelopmentLanguageClarification(question) {
|
|
6555
|
+
const text = `${question.question}
|
|
6556
|
+
${question.why}
|
|
6557
|
+
${question.options.map((option) => option.answer).join("\n")}`.toLowerCase();
|
|
6558
|
+
const mentionsPython = /\bpython\b/u.test(text) || /python\s*脚本|python\s*项目/u.test(text);
|
|
6559
|
+
const mentionsTypeScript = /\btypescript\b|\btype\s*script\b|\bnode(?:\.js)?\b/u.test(text) || /type\s*script|ts\s*(程序|工程|项目|脚本|语言|实现)|typescript\s*项目/u.test(text);
|
|
6560
|
+
const asksLanguage = /\b(language|runtime|implementation stack|programming language)\b/u.test(text) || /开发语言|编程语言|实现语言|运行时|技术栈/u.test(text);
|
|
6561
|
+
return asksLanguage && mentionsPython && mentionsTypeScript;
|
|
6562
|
+
}
|
|
6147
6563
|
function projectShapeSignals(text) {
|
|
6148
6564
|
const lower = text.toLowerCase();
|
|
6149
6565
|
const libraryLike = /\b(api[- ]?library|library|sdk|package|npm package|pypi package|client library|api client|reusable module|public api)\b/u.test(lower) || /api\s*(库|客户端)|公共\s*api|可复用接口|公共库|库项目|软件包|客户端库|开发包/u.test(text);
|
|
@@ -6271,7 +6687,7 @@ ${text.slice(0, 500)}`);
|
|
|
6271
6687
|
}
|
|
6272
6688
|
|
|
6273
6689
|
// src/core/incremental.ts
|
|
6274
|
-
import { promises as
|
|
6690
|
+
import { promises as fs8 } from "fs";
|
|
6275
6691
|
import path9 from "path";
|
|
6276
6692
|
|
|
6277
6693
|
// src/core/phase_plan.ts
|
|
@@ -6351,11 +6767,11 @@ function relativeFrom(base, target) {
|
|
|
6351
6767
|
}
|
|
6352
6768
|
|
|
6353
6769
|
// src/core/project_memory.ts
|
|
6354
|
-
import { promises as
|
|
6770
|
+
import { promises as fs7 } from "fs";
|
|
6355
6771
|
import path8 from "path";
|
|
6356
6772
|
|
|
6357
6773
|
// src/core/storage.ts
|
|
6358
|
-
import { promises as
|
|
6774
|
+
import { promises as fs6 } from "fs";
|
|
6359
6775
|
import path7 from "path";
|
|
6360
6776
|
|
|
6361
6777
|
// src/core/lint.ts
|
|
@@ -6791,21 +7207,21 @@ function parseLoadedPlan(json) {
|
|
|
6791
7207
|
}
|
|
6792
7208
|
async function savePlan(planPath, plan) {
|
|
6793
7209
|
PlanSchema.parse(plan);
|
|
6794
|
-
await
|
|
6795
|
-
await
|
|
7210
|
+
await fs6.mkdir(path7.dirname(planPath), { recursive: true });
|
|
7211
|
+
await fs6.writeFile(planPath, JSON.stringify(plan, null, 2) + "\n", "utf8");
|
|
6796
7212
|
}
|
|
6797
7213
|
async function loadPhasePlan(phasePlanPath) {
|
|
6798
|
-
const raw = await
|
|
7214
|
+
const raw = await fs6.readFile(phasePlanPath, "utf8");
|
|
6799
7215
|
return PhasePlanSchema.parse(JSON.parse(raw));
|
|
6800
7216
|
}
|
|
6801
7217
|
async function savePhasePlan(phasePlanPath, phasePlan) {
|
|
6802
7218
|
PhasePlanSchema.parse(phasePlan);
|
|
6803
|
-
await
|
|
6804
|
-
await
|
|
7219
|
+
await fs6.mkdir(path7.dirname(phasePlanPath), { recursive: true });
|
|
7220
|
+
await fs6.writeFile(phasePlanPath, JSON.stringify(phasePlan, null, 2) + "\n", "utf8");
|
|
6805
7221
|
}
|
|
6806
7222
|
async function loadPlanTarget(inputPath) {
|
|
6807
7223
|
const requestedPath = path7.resolve(inputPath);
|
|
6808
|
-
const raw = await
|
|
7224
|
+
const raw = await fs6.readFile(requestedPath, "utf8");
|
|
6809
7225
|
const json = JSON.parse(raw);
|
|
6810
7226
|
const phasePlanResult = PhasePlanSchema.safeParse(json);
|
|
6811
7227
|
if (phasePlanResult.success) {
|
|
@@ -6815,7 +7231,7 @@ async function loadPlanTarget(inputPath) {
|
|
|
6815
7231
|
throw new Error(`phasePlan ${requestedPath} has no planPath for current phase ${phasePlan.currentPhaseId}`);
|
|
6816
7232
|
}
|
|
6817
7233
|
const planPath = path7.resolve(path7.dirname(requestedPath), phase.planPath);
|
|
6818
|
-
const loaded2 = parseLoadedPlan(JSON.parse(await
|
|
7234
|
+
const loaded2 = parseLoadedPlan(JSON.parse(await fs6.readFile(planPath, "utf8")));
|
|
6819
7235
|
return {
|
|
6820
7236
|
plan: loaded2.plan,
|
|
6821
7237
|
planPath,
|
|
@@ -6982,7 +7398,7 @@ async function defaultPlanMetadataPath(root) {
|
|
|
6982
7398
|
}
|
|
6983
7399
|
async function fileExists(filePath) {
|
|
6984
7400
|
try {
|
|
6985
|
-
await
|
|
7401
|
+
await fs7.stat(filePath);
|
|
6986
7402
|
return true;
|
|
6987
7403
|
} catch {
|
|
6988
7404
|
return false;
|
|
@@ -7010,7 +7426,7 @@ async function readWorkspaceText(ws, rel, maxChars) {
|
|
|
7010
7426
|
}
|
|
7011
7427
|
async function readAbsoluteText(fullPath, maxChars) {
|
|
7012
7428
|
try {
|
|
7013
|
-
const raw = await
|
|
7429
|
+
const raw = await fs7.readFile(fullPath, "utf8");
|
|
7014
7430
|
return truncate(raw, maxChars);
|
|
7015
7431
|
} catch {
|
|
7016
7432
|
return "";
|
|
@@ -7032,7 +7448,7 @@ async function walk(abs, rel, out, depth) {
|
|
|
7032
7448
|
if (depth > 6 || out.length >= 160) return;
|
|
7033
7449
|
let entries;
|
|
7034
7450
|
try {
|
|
7035
|
-
entries = await
|
|
7451
|
+
entries = await fs7.readdir(abs, { withFileTypes: true });
|
|
7036
7452
|
} catch {
|
|
7037
7453
|
return;
|
|
7038
7454
|
}
|
|
@@ -7214,20 +7630,20 @@ function extractImportedStems(text) {
|
|
|
7214
7630
|
function normalizeImportStem(value) {
|
|
7215
7631
|
return value.replace(/^\.\//u, "").replace(/^\.\.\//u, "").replace(/\.(js|ts|tsx|py)$/u, "").replace(/\/index$/u, "").trim();
|
|
7216
7632
|
}
|
|
7217
|
-
function extractContractsFromDoc(
|
|
7633
|
+
function extractContractsFromDoc(path31, text) {
|
|
7218
7634
|
const contracts = [];
|
|
7219
7635
|
for (const rawLine of text.split("\n")) {
|
|
7220
7636
|
const line = rawLine.replace(/^[-*#\d.\s]+/u, "").trim();
|
|
7221
7637
|
if (line.length < 18) continue;
|
|
7222
7638
|
const lower = line.toLowerCase();
|
|
7223
7639
|
if (INVARIANT_RE.test(lower)) {
|
|
7224
|
-
contracts.push({ kind: "invariant", subject:
|
|
7640
|
+
contracts.push({ kind: "invariant", subject: path31, path: path31, detail: trimContractLine(line) });
|
|
7225
7641
|
} else if (LIMITATION_RE.test(lower)) {
|
|
7226
|
-
contracts.push({ kind: "limitation", subject:
|
|
7642
|
+
contracts.push({ kind: "limitation", subject: path31, path: path31, detail: trimContractLine(line) });
|
|
7227
7643
|
} else if (EXTENSION_RE.test(lower)) {
|
|
7228
|
-
contracts.push({ kind: "extension-point", subject:
|
|
7644
|
+
contracts.push({ kind: "extension-point", subject: path31, path: path31, detail: trimContractLine(line) });
|
|
7229
7645
|
} else if (INTEGRATION_RE.test(lower)) {
|
|
7230
|
-
contracts.push({ kind: "integration", subject:
|
|
7646
|
+
contracts.push({ kind: "integration", subject: path31, path: path31, detail: trimContractLine(line) });
|
|
7231
7647
|
}
|
|
7232
7648
|
}
|
|
7233
7649
|
return contracts;
|
|
@@ -7326,7 +7742,7 @@ ${sec.slice(0, remain)}
|
|
|
7326
7742
|
}
|
|
7327
7743
|
async function loadPlanSummary(_ws, planPath, label) {
|
|
7328
7744
|
try {
|
|
7329
|
-
const raw = await
|
|
7745
|
+
const raw = await fs8.readFile(planPath, "utf8");
|
|
7330
7746
|
const json = JSON.parse(raw);
|
|
7331
7747
|
const phasePlanParsed = PhasePlanSchema.safeParse(json);
|
|
7332
7748
|
if (phasePlanParsed.success) {
|
|
@@ -7394,7 +7810,7 @@ async function defaultBaselinePlanPath(root) {
|
|
|
7394
7810
|
}
|
|
7395
7811
|
async function fileExists2(filePath) {
|
|
7396
7812
|
try {
|
|
7397
|
-
await
|
|
7813
|
+
await fs8.stat(filePath);
|
|
7398
7814
|
return true;
|
|
7399
7815
|
} catch {
|
|
7400
7816
|
return false;
|
|
@@ -7458,7 +7874,7 @@ async function walk2(abs, rel, out, depth) {
|
|
|
7458
7874
|
if (depth > 6 || out.length >= 120) return;
|
|
7459
7875
|
let entries;
|
|
7460
7876
|
try {
|
|
7461
|
-
entries = await
|
|
7877
|
+
entries = await fs8.readdir(abs, { withFileTypes: true });
|
|
7462
7878
|
} catch {
|
|
7463
7879
|
return;
|
|
7464
7880
|
}
|
|
@@ -7636,7 +8052,7 @@ function renderSubTasks(tasks, depth) {
|
|
|
7636
8052
|
}
|
|
7637
8053
|
|
|
7638
8054
|
// src/core/project_file.ts
|
|
7639
|
-
import { promises as
|
|
8055
|
+
import { promises as fs9 } from "fs";
|
|
7640
8056
|
import path10 from "path";
|
|
7641
8057
|
import { z as z4 } from "zod";
|
|
7642
8058
|
var XCOMPILER_PROJECT_KIND = "xcompiler.project";
|
|
@@ -7697,7 +8113,7 @@ async function findProjectFile(workspace) {
|
|
|
7697
8113
|
const ws = path10.resolve(workspace);
|
|
7698
8114
|
let entries;
|
|
7699
8115
|
try {
|
|
7700
|
-
entries = await
|
|
8116
|
+
entries = await fs9.readdir(ws, { withFileTypes: true });
|
|
7701
8117
|
} catch {
|
|
7702
8118
|
return void 0;
|
|
7703
8119
|
}
|
|
@@ -7747,8 +8163,8 @@ async function updateProjectFile(opts) {
|
|
|
7747
8163
|
history: nextHistory
|
|
7748
8164
|
};
|
|
7749
8165
|
XCompilerProjectFileSchema.parse(data);
|
|
7750
|
-
await
|
|
7751
|
-
await
|
|
8166
|
+
await fs9.mkdir(path10.dirname(filePath), { recursive: true });
|
|
8167
|
+
await fs9.writeFile(filePath, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
7752
8168
|
return filePath;
|
|
7753
8169
|
}
|
|
7754
8170
|
function buildProjectProgress(plan) {
|
|
@@ -7791,7 +8207,7 @@ function buildProjectProgress(plan) {
|
|
|
7791
8207
|
}
|
|
7792
8208
|
async function readExistingProjectFile(filePath) {
|
|
7793
8209
|
try {
|
|
7794
|
-
const raw = await
|
|
8210
|
+
const raw = await fs9.readFile(filePath, "utf8");
|
|
7795
8211
|
return XCompilerProjectFileSchema.parse(JSON.parse(raw));
|
|
7796
8212
|
} catch {
|
|
7797
8213
|
return void 0;
|
|
@@ -7820,7 +8236,7 @@ function assertProjectFileExtension(filePath) {
|
|
|
7820
8236
|
}
|
|
7821
8237
|
|
|
7822
8238
|
// src/audit/audit.ts
|
|
7823
|
-
import { promises as
|
|
8239
|
+
import { promises as fs10, appendFileSync, mkdirSync, existsSync, writeFileSync } from "fs";
|
|
7824
8240
|
import { createHash } from "crypto";
|
|
7825
8241
|
import path11 from "path";
|
|
7826
8242
|
var AuditLogger = class {
|
|
@@ -7977,8 +8393,8 @@ var AuditLogger = class {
|
|
|
7977
8393
|
});
|
|
7978
8394
|
}
|
|
7979
8395
|
/**
|
|
7980
|
-
* 记录一轮 Executor
|
|
7981
|
-
* 写入 jsonl + markdown
|
|
8396
|
+
* 记录一轮 Executor 执行摘要:简短 intent、issue 处理方案、计划调用的 actions、是否完成。
|
|
8397
|
+
* 写入 jsonl + markdown 折叠块,交付时可追溯每轮决策和动作。
|
|
7982
8398
|
*/
|
|
7983
8399
|
async executorTurn(stepId, role, round, payload) {
|
|
7984
8400
|
const storedPayload = protectAuditContent(payload, this.contentMode);
|
|
@@ -8050,8 +8466,8 @@ ${t().audit.processLogPreamble}
|
|
|
8050
8466
|
}
|
|
8051
8467
|
async appendMd(text) {
|
|
8052
8468
|
this.mdQueue = this.mdQueue.then(
|
|
8053
|
-
() =>
|
|
8054
|
-
() =>
|
|
8469
|
+
() => fs10.appendFile(this.mdAbs, text, "utf8"),
|
|
8470
|
+
() => fs10.appendFile(this.mdAbs, text, "utf8")
|
|
8055
8471
|
).catch((err) => {
|
|
8056
8472
|
console.warn(t().audit.markdownAppendFailed(err.message));
|
|
8057
8473
|
});
|
|
@@ -8133,7 +8549,7 @@ function redactText(value) {
|
|
|
8133
8549
|
}
|
|
8134
8550
|
|
|
8135
8551
|
// src/core/lock.ts
|
|
8136
|
-
import { promises as
|
|
8552
|
+
import { promises as fs11, unlinkSync } from "fs";
|
|
8137
8553
|
import path12 from "path";
|
|
8138
8554
|
import os from "os";
|
|
8139
8555
|
var LockError = class extends Error {
|
|
@@ -8160,7 +8576,7 @@ function isAlive(pid) {
|
|
|
8160
8576
|
}
|
|
8161
8577
|
async function readLock(file) {
|
|
8162
8578
|
try {
|
|
8163
|
-
const raw = await
|
|
8579
|
+
const raw = await fs11.readFile(file, "utf8");
|
|
8164
8580
|
return JSON.parse(raw);
|
|
8165
8581
|
} catch {
|
|
8166
8582
|
return null;
|
|
@@ -8168,7 +8584,7 @@ async function readLock(file) {
|
|
|
8168
8584
|
}
|
|
8169
8585
|
async function acquireLock(workspace, command, options = {}) {
|
|
8170
8586
|
const file = lockPath(workspace);
|
|
8171
|
-
await
|
|
8587
|
+
await fs11.mkdir(path12.dirname(file), { recursive: true });
|
|
8172
8588
|
const info = {
|
|
8173
8589
|
pid: process.pid,
|
|
8174
8590
|
host: os.hostname(),
|
|
@@ -8178,7 +8594,7 @@ async function acquireLock(workspace, command, options = {}) {
|
|
|
8178
8594
|
const payload = JSON.stringify(info, null, 2);
|
|
8179
8595
|
const tryCreate = async () => {
|
|
8180
8596
|
try {
|
|
8181
|
-
const fh = await
|
|
8597
|
+
const fh = await fs11.open(file, "wx");
|
|
8182
8598
|
await fh.writeFile(payload, "utf8");
|
|
8183
8599
|
await fh.close();
|
|
8184
8600
|
return true;
|
|
@@ -8191,14 +8607,14 @@ async function acquireLock(workspace, command, options = {}) {
|
|
|
8191
8607
|
return makeReleaser(file);
|
|
8192
8608
|
}
|
|
8193
8609
|
if (options.force) {
|
|
8194
|
-
await
|
|
8610
|
+
await fs11.writeFile(file, payload, "utf8");
|
|
8195
8611
|
return makeReleaser(file);
|
|
8196
8612
|
}
|
|
8197
8613
|
const existing = await readLock(file);
|
|
8198
8614
|
if (existing) {
|
|
8199
8615
|
const sameHost = existing.host === info.host;
|
|
8200
8616
|
if (sameHost && !isAlive(existing.pid)) {
|
|
8201
|
-
await
|
|
8617
|
+
await fs11.writeFile(file, payload, "utf8");
|
|
8202
8618
|
return makeReleaser(file);
|
|
8203
8619
|
}
|
|
8204
8620
|
throw new LockError(
|
|
@@ -8207,7 +8623,7 @@ async function acquireLock(workspace, command, options = {}) {
|
|
|
8207
8623
|
existing
|
|
8208
8624
|
);
|
|
8209
8625
|
}
|
|
8210
|
-
await
|
|
8626
|
+
await fs11.writeFile(file, payload, "utf8");
|
|
8211
8627
|
return makeReleaser(file);
|
|
8212
8628
|
}
|
|
8213
8629
|
function makeReleaser(file) {
|
|
@@ -8216,7 +8632,7 @@ function makeReleaser(file) {
|
|
|
8216
8632
|
if (released) return;
|
|
8217
8633
|
released = true;
|
|
8218
8634
|
try {
|
|
8219
|
-
await
|
|
8635
|
+
await fs11.unlink(file);
|
|
8220
8636
|
} catch {
|
|
8221
8637
|
}
|
|
8222
8638
|
};
|
|
@@ -8241,7 +8657,7 @@ function makeReleaser(file) {
|
|
|
8241
8657
|
}
|
|
8242
8658
|
|
|
8243
8659
|
// src/version.ts
|
|
8244
|
-
var XCOMPILER_VERSION = "0.2.
|
|
8660
|
+
var XCOMPILER_VERSION = "0.2.4";
|
|
8245
8661
|
var XCOMPILER_PLUGIN_API_VERSION = 1;
|
|
8246
8662
|
|
|
8247
8663
|
// src/plugins/compatibility.ts
|
|
@@ -8584,8 +9000,51 @@ function resolveClarificationAnswer(q, rawAnswer) {
|
|
|
8584
9000
|
}
|
|
8585
9001
|
return answer;
|
|
8586
9002
|
}
|
|
8587
|
-
function resolveCompileLanguage(
|
|
8588
|
-
|
|
9003
|
+
function resolveCompileLanguage(inputOrConfigured, intent, baseline) {
|
|
9004
|
+
if (typeof inputOrConfigured === "string") {
|
|
9005
|
+
return isIncrementalIntent(intent ?? "greenfield") ? baseline?.language ?? inputOrConfigured : inputOrConfigured;
|
|
9006
|
+
}
|
|
9007
|
+
const input2 = inputOrConfigured;
|
|
9008
|
+
if (isIncrementalIntent(input2.intent ?? "greenfield") && input2.baseline?.language) {
|
|
9009
|
+
return { language: input2.baseline.language, source: "baseline", ambiguous: false };
|
|
9010
|
+
}
|
|
9011
|
+
const topicInferred = inferCompileLanguageFromText(input2.rawRequirement);
|
|
9012
|
+
if (topicInferred) {
|
|
9013
|
+
return {
|
|
9014
|
+
language: topicInferred,
|
|
9015
|
+
source: "topic",
|
|
9016
|
+
ambiguous: false
|
|
9017
|
+
};
|
|
9018
|
+
}
|
|
9019
|
+
const clarificationText = formatLanguageClarificationText(input2.clarifications ?? []);
|
|
9020
|
+
const clarifiedInferred = inferCompileLanguageFromText([
|
|
9021
|
+
clarificationText,
|
|
9022
|
+
input2.userAddenda ?? ""
|
|
9023
|
+
].join("\n"));
|
|
9024
|
+
if (clarifiedInferred) {
|
|
9025
|
+
return { language: clarifiedInferred, source: "clarification", ambiguous: false };
|
|
9026
|
+
}
|
|
9027
|
+
return { language: "python", source: "default", ambiguous: true };
|
|
9028
|
+
}
|
|
9029
|
+
function inferCompileLanguageFromText(text) {
|
|
9030
|
+
const normalized = text.toLowerCase().replace(/[,。;:、()【】]/gu, " ").replace(/\s+/gu, " ");
|
|
9031
|
+
const pythonStrong = /\bpython\b/u.test(normalized) || /python\s*脚本/u.test(normalized) || /\.py\b/u.test(normalized) || /\bpytest\b/u.test(normalized) || /\bpip\b/u.test(normalized);
|
|
9032
|
+
const typescriptStrong = /\btypescript\b/u.test(normalized) || /\btype\s*script\b/u.test(normalized) || /(^|[^a-z0-9])ts\s*(程序|工程|项目|脚本|语言|实现)/u.test(normalized) || /\.tsx?\b/u.test(normalized) || /\btsx\b/u.test(normalized);
|
|
9033
|
+
if (pythonStrong && !typescriptStrong) return "python";
|
|
9034
|
+
if (typescriptStrong && !pythonStrong) return "typescript";
|
|
9035
|
+
const pythonWeak = /\bopenpyxl\b/u.test(normalized) || /\bpandas\b/u.test(normalized) || /\bfastapi\b/u.test(normalized) || /\bflask\b/u.test(normalized);
|
|
9036
|
+
const typescriptWeak = /\bnode(?:\.js)?\b/u.test(normalized) || /\bnpm\b/u.test(normalized) || /\bvitest\b/u.test(normalized) || /\bpackage\.json\b/u.test(normalized) || /\bjavascript\b/u.test(normalized) || /\bjs\s*(程序|工程|项目|脚本|语言|实现)\b/u.test(normalized);
|
|
9037
|
+
if ((pythonStrong || pythonWeak) && !(typescriptStrong || typescriptWeak)) return "python";
|
|
9038
|
+
if ((typescriptStrong || typescriptWeak) && !(pythonStrong || pythonWeak)) return "typescript";
|
|
9039
|
+
return void 0;
|
|
9040
|
+
}
|
|
9041
|
+
function formatLanguageClarificationText(input2) {
|
|
9042
|
+
return input2.map((item) => [
|
|
9043
|
+
item.question,
|
|
9044
|
+
item.answer,
|
|
9045
|
+
item.why ?? "",
|
|
9046
|
+
...(item.options ?? []).map((option) => option.answer)
|
|
9047
|
+
].join("\n")).join("\n\n");
|
|
8589
9048
|
}
|
|
8590
9049
|
async function runCompile(opts) {
|
|
8591
9050
|
const io = opts.io ?? silentRuntimeIO;
|
|
@@ -8662,15 +9121,7 @@ async function runCompile(opts) {
|
|
|
8662
9121
|
if (baseline.summary) {
|
|
8663
9122
|
await runtimeLog(io, "success", M.compile.baselineLoaded(intent, baseline.sources.join(", ")));
|
|
8664
9123
|
}
|
|
8665
|
-
const
|
|
8666
|
-
if (isIncrementalIntent(intent) && baseline.language && baseline.language !== cfg.agent.language) {
|
|
8667
|
-
await runtimeLog(
|
|
8668
|
-
io,
|
|
8669
|
-
"warning",
|
|
8670
|
-
M.compile.baselineLanguageOverride(baseline.language, baseline.languageSource ?? "baseline", cfg.agent.language)
|
|
8671
|
-
);
|
|
8672
|
-
}
|
|
8673
|
-
const planner = new Planner(router.for("Planner"), audit, language);
|
|
9124
|
+
const plannerClient = router.for("Planner");
|
|
8674
9125
|
const trace = (msg) => {
|
|
8675
9126
|
if (xcEnv("TRACE") === "1") {
|
|
8676
9127
|
void runtimeLog(io, "dim", t().audit.traceLine("xcompiler-trace", msg));
|
|
@@ -8679,7 +9130,7 @@ async function runCompile(opts) {
|
|
|
8679
9130
|
let rawRequirement;
|
|
8680
9131
|
if (topicMode) {
|
|
8681
9132
|
trace("topic.read");
|
|
8682
|
-
rawRequirement = await
|
|
9133
|
+
rawRequirement = await fs12.readFile(path13.resolve(opts.topicFile), "utf8");
|
|
8683
9134
|
if (!rawRequirement.trim()) {
|
|
8684
9135
|
await runtimeLog(io, "error", M.compile.topicEmptyExit);
|
|
8685
9136
|
await audit.end({ status: "aborted", reason: "empty topic file" });
|
|
@@ -8700,19 +9151,26 @@ async function runCompile(opts) {
|
|
|
8700
9151
|
await audit.userInput(M.compile.auditOriginalRequirement, rawRequirement);
|
|
8701
9152
|
trace("audit.userInput.intake.done");
|
|
8702
9153
|
}
|
|
9154
|
+
const initialLanguage = resolveCompileLanguage({
|
|
9155
|
+
rawRequirement,
|
|
9156
|
+
intent,
|
|
9157
|
+
baseline
|
|
9158
|
+
});
|
|
8703
9159
|
trace("clarify.section.enter");
|
|
8704
9160
|
const clarifications = [];
|
|
8705
9161
|
let clarificationQuestions = [];
|
|
8706
9162
|
trace(`clarify.section.flag yes=${opts.yes} topicMode=${topicMode}`);
|
|
8707
9163
|
if (!opts.yes && !topicMode) {
|
|
9164
|
+
const clarifyPlanner = new Planner(plannerClient, audit, initialLanguage.language);
|
|
8708
9165
|
trace("ora.clarify.start");
|
|
8709
9166
|
const spin = io.progress(M.compile.spinClarify, { animate: false });
|
|
8710
9167
|
trace("ora.clarify.started");
|
|
8711
9168
|
try {
|
|
8712
9169
|
trace("planner.clarify.call");
|
|
8713
|
-
clarificationQuestions = await
|
|
9170
|
+
clarificationQuestions = await clarifyPlanner.clarify(rawRequirement, {
|
|
8714
9171
|
intent,
|
|
8715
|
-
hasBaseline: !!baseline.summary
|
|
9172
|
+
hasBaseline: !!baseline.summary,
|
|
9173
|
+
languageAmbiguous: initialLanguage.ambiguous
|
|
8716
9174
|
});
|
|
8717
9175
|
trace(`planner.clarify.return n=${clarificationQuestions.length}`);
|
|
8718
9176
|
spin.succeed(M.compile.clarifySucceed(clarificationQuestions.length));
|
|
@@ -8801,6 +9259,20 @@ ${M.compile.topicPreviewHeader}`);
|
|
|
8801
9259
|
}
|
|
8802
9260
|
trace("ws.readFile.finalTopic");
|
|
8803
9261
|
const finalTopicMd = await ws.readFile(draftTopic);
|
|
9262
|
+
const languageResolution = resolveCompileLanguage({
|
|
9263
|
+
rawRequirement: finalTopicMd,
|
|
9264
|
+
clarifications,
|
|
9265
|
+
userAddenda,
|
|
9266
|
+
intent,
|
|
9267
|
+
baseline
|
|
9268
|
+
});
|
|
9269
|
+
const language = languageResolution.language;
|
|
9270
|
+
await audit.event("note", `target language resolved: ${language}`, {
|
|
9271
|
+
messageId: "compile.language_resolved",
|
|
9272
|
+
language,
|
|
9273
|
+
source: languageResolution.source,
|
|
9274
|
+
ambiguous: languageResolution.ambiguous
|
|
9275
|
+
});
|
|
8804
9276
|
await archiveIfExists(ws, DOC_NAMES.topic, audit);
|
|
8805
9277
|
await ws.writeFile(DOC_NAMES.topic, finalTopicMd);
|
|
8806
9278
|
await audit.event("topic.persist", M.compile.auditTopicPersisted(ws.abs(DOC_NAMES.topic)), {
|
|
@@ -8814,6 +9286,7 @@ ${M.compile.topicPreviewHeader}`);
|
|
|
8814
9286
|
trace("ora.spin2.started");
|
|
8815
9287
|
let draft;
|
|
8816
9288
|
try {
|
|
9289
|
+
const planner = new Planner(plannerClient, audit, language);
|
|
8817
9290
|
const plannerInput = {
|
|
8818
9291
|
rawRequirement: finalTopicMd,
|
|
8819
9292
|
clarifications,
|
|
@@ -8951,7 +9424,7 @@ async function tryLoadPhasePlan(phasePlanPath) {
|
|
|
8951
9424
|
}
|
|
8952
9425
|
async function intake(inputFile, io) {
|
|
8953
9426
|
if (inputFile) {
|
|
8954
|
-
return
|
|
9427
|
+
return fs12.readFile(path13.resolve(inputFile), "utf8");
|
|
8955
9428
|
}
|
|
8956
9429
|
return requireRuntimeInteraction(io, "requirement intake").readMultiline({
|
|
8957
9430
|
message: t().compile.requirementInputHint
|
|
@@ -8995,14 +9468,14 @@ function renderTopicDraft(raw, qa, addenda = "") {
|
|
|
8995
9468
|
}
|
|
8996
9469
|
|
|
8997
9470
|
// src/runtime/commands.ts
|
|
8998
|
-
import
|
|
8999
|
-
import { promises as
|
|
9471
|
+
import path30 from "path";
|
|
9472
|
+
import { promises as fs28 } from "fs";
|
|
9000
9473
|
|
|
9001
9474
|
// src/runtime/run.ts
|
|
9002
|
-
import
|
|
9475
|
+
import path28 from "path";
|
|
9003
9476
|
|
|
9004
9477
|
// src/workspace/git.ts
|
|
9005
|
-
import { promises as
|
|
9478
|
+
import { promises as fs13 } from "fs";
|
|
9006
9479
|
import path14 from "path";
|
|
9007
9480
|
import { simpleGit } from "simple-git";
|
|
9008
9481
|
|
|
@@ -9011,7 +9484,7 @@ import { existsSync as existsSync2, readFileSync } from "fs";
|
|
|
9011
9484
|
|
|
9012
9485
|
// src/sandbox/subprocess.ts
|
|
9013
9486
|
import { spawn } from "child_process";
|
|
9014
|
-
import { promises as
|
|
9487
|
+
import { promises as fs14 } from "fs";
|
|
9015
9488
|
import path15 from "path";
|
|
9016
9489
|
import crypto from "crypto";
|
|
9017
9490
|
|
|
@@ -9042,13 +9515,16 @@ function formatCommand(cmd, argv) {
|
|
|
9042
9515
|
return [cmd, ...argv].filter(Boolean).join(" ");
|
|
9043
9516
|
}
|
|
9044
9517
|
|
|
9518
|
+
// src/sandbox/subprocess.ts
|
|
9519
|
+
var INSTALL_IDLE_TIMEOUT_FLOOR_MS = 15 * 6e4;
|
|
9520
|
+
|
|
9045
9521
|
// src/sandbox/docker.ts
|
|
9046
|
-
import { promises as
|
|
9522
|
+
import { promises as fs15 } from "fs";
|
|
9047
9523
|
import path16 from "path";
|
|
9048
9524
|
import crypto2 from "crypto";
|
|
9049
9525
|
|
|
9050
9526
|
// src/core/engine.ts
|
|
9051
|
-
import
|
|
9527
|
+
import path27 from "path";
|
|
9052
9528
|
import chalk2 from "chalk";
|
|
9053
9529
|
|
|
9054
9530
|
// src/util/spinner.ts
|
|
@@ -9134,10 +9610,10 @@ init_types();
|
|
|
9134
9610
|
// src/tools/fs.ts
|
|
9135
9611
|
init_types();
|
|
9136
9612
|
import path18 from "path";
|
|
9137
|
-
import { promises as
|
|
9613
|
+
import { promises as fs17 } from "fs";
|
|
9138
9614
|
|
|
9139
9615
|
// src/tools/path_guard.ts
|
|
9140
|
-
import { promises as
|
|
9616
|
+
import { promises as fs16 } from "fs";
|
|
9141
9617
|
import path17 from "path";
|
|
9142
9618
|
async function resolveWorkspacePath(ws, rawPath, operation, opts = {}) {
|
|
9143
9619
|
const raw = rawPath && rawPath.trim() ? rawPath : ".";
|
|
@@ -9146,19 +9622,19 @@ async function resolveWorkspacePath(ws, rawPath, operation, opts = {}) {
|
|
|
9146
9622
|
if (!isInside(root, abs)) return deny(operation, raw);
|
|
9147
9623
|
if (opts.mustExist) {
|
|
9148
9624
|
try {
|
|
9149
|
-
const real = await
|
|
9625
|
+
const real = await fs16.realpath(abs);
|
|
9150
9626
|
if (!isInside(root, real)) return deny(operation, raw);
|
|
9151
9627
|
} catch (err) {
|
|
9152
9628
|
return { ok: false, error: `${operation} failed: ${err.message}` };
|
|
9153
9629
|
}
|
|
9154
9630
|
} else if (opts.forWrite) {
|
|
9155
|
-
const existingTarget = await
|
|
9631
|
+
const existingTarget = await fs16.realpath(abs).catch(() => void 0);
|
|
9156
9632
|
if (existingTarget && !isInside(root, existingTarget)) return deny(operation, raw);
|
|
9157
9633
|
const parent = await nearestExistingParent(path17.dirname(abs));
|
|
9158
|
-
const realParent = await
|
|
9634
|
+
const realParent = await fs16.realpath(parent).catch(() => parent);
|
|
9159
9635
|
if (!isInside(root, realParent)) return deny(operation, raw);
|
|
9160
9636
|
} else {
|
|
9161
|
-
const existing = await
|
|
9637
|
+
const existing = await fs16.realpath(abs).catch(() => void 0);
|
|
9162
9638
|
if (existing && !isInside(root, existing)) return deny(operation, raw);
|
|
9163
9639
|
}
|
|
9164
9640
|
return { ok: true, abs };
|
|
@@ -9168,13 +9644,13 @@ function isInside(root, candidate) {
|
|
|
9168
9644
|
return rel === "" || !!rel && !rel.startsWith("..") && !path17.isAbsolute(rel);
|
|
9169
9645
|
}
|
|
9170
9646
|
async function realpathOrResolve(p) {
|
|
9171
|
-
return
|
|
9647
|
+
return fs16.realpath(p).catch(() => path17.resolve(p));
|
|
9172
9648
|
}
|
|
9173
9649
|
async function nearestExistingParent(start) {
|
|
9174
9650
|
let current2 = path17.resolve(start);
|
|
9175
9651
|
while (true) {
|
|
9176
9652
|
try {
|
|
9177
|
-
const stat = await
|
|
9653
|
+
const stat = await fs16.stat(current2);
|
|
9178
9654
|
if (stat.isDirectory()) return current2;
|
|
9179
9655
|
} catch {
|
|
9180
9656
|
}
|
|
@@ -9193,7 +9669,7 @@ function deny(operation, rawPath) {
|
|
|
9193
9669
|
// src/tools/patch.ts
|
|
9194
9670
|
init_types();
|
|
9195
9671
|
import path19 from "path";
|
|
9196
|
-
import { promises as
|
|
9672
|
+
import { promises as fs18 } from "fs";
|
|
9197
9673
|
|
|
9198
9674
|
// src/tools/sandbox.ts
|
|
9199
9675
|
function tailLines(text, n) {
|
|
@@ -9268,13 +9744,13 @@ var pipInstallTool = {
|
|
|
9268
9744
|
// src/tools/edit.ts
|
|
9269
9745
|
init_types();
|
|
9270
9746
|
import path20 from "path";
|
|
9271
|
-
import { promises as
|
|
9747
|
+
import { promises as fs19 } from "fs";
|
|
9272
9748
|
|
|
9273
9749
|
// src/tools/deps.ts
|
|
9274
|
-
import { promises as
|
|
9750
|
+
import { promises as fs20 } from "fs";
|
|
9275
9751
|
|
|
9276
9752
|
// src/tools/net.ts
|
|
9277
|
-
import { promises as
|
|
9753
|
+
import { promises as fs21 } from "fs";
|
|
9278
9754
|
import path21 from "path";
|
|
9279
9755
|
var DEFAULT_MAX_BYTES = 256 * 1024;
|
|
9280
9756
|
|
|
@@ -9282,25 +9758,31 @@ var DEFAULT_MAX_BYTES = 256 * 1024;
|
|
|
9282
9758
|
init_types();
|
|
9283
9759
|
|
|
9284
9760
|
// src/tools/guard.ts
|
|
9285
|
-
import { promises as
|
|
9761
|
+
import { promises as fs22 } from "fs";
|
|
9286
9762
|
import path22 from "path";
|
|
9287
9763
|
|
|
9288
9764
|
// src/agents/executor.ts
|
|
9289
9765
|
import path23 from "path";
|
|
9290
|
-
import { promises as
|
|
9766
|
+
import { promises as fs23 } from "fs";
|
|
9291
9767
|
import { jsonrepair } from "jsonrepair";
|
|
9292
9768
|
|
|
9293
9769
|
// src/core/debug_cache.ts
|
|
9294
|
-
import { promises as
|
|
9770
|
+
import { promises as fs24 } from "fs";
|
|
9295
9771
|
import path24 from "path";
|
|
9296
9772
|
|
|
9297
|
-
// src/core/
|
|
9298
|
-
import { promises as
|
|
9773
|
+
// src/core/debug_wiki.ts
|
|
9774
|
+
import { promises as fs25 } from "fs";
|
|
9299
9775
|
import path25 from "path";
|
|
9776
|
+
import { fileURLToPath } from "url";
|
|
9777
|
+
import YAML3 from "yaml";
|
|
9778
|
+
|
|
9779
|
+
// src/core/project_audit.ts
|
|
9780
|
+
import { promises as fs26 } from "fs";
|
|
9781
|
+
import path26 from "path";
|
|
9300
9782
|
|
|
9301
9783
|
// src/runtime/workspace.ts
|
|
9302
|
-
import
|
|
9303
|
-
import { promises as
|
|
9784
|
+
import path29 from "path";
|
|
9785
|
+
import { promises as fs27 } from "fs";
|
|
9304
9786
|
function defaultProjectName(now = /* @__PURE__ */ new Date()) {
|
|
9305
9787
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
9306
9788
|
return `xcompiler-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
@@ -9308,14 +9790,14 @@ function defaultProjectName(now = /* @__PURE__ */ new Date()) {
|
|
|
9308
9790
|
async function resolveCompileWorkspace(opts) {
|
|
9309
9791
|
const explicit = opts.output ?? opts.workspace;
|
|
9310
9792
|
if (explicit) {
|
|
9311
|
-
const ws2 =
|
|
9312
|
-
await
|
|
9793
|
+
const ws2 = path29.resolve(explicit);
|
|
9794
|
+
await fs27.mkdir(ws2, { recursive: true });
|
|
9313
9795
|
return ws2;
|
|
9314
9796
|
}
|
|
9315
|
-
const base = opts.baseDir ?
|
|
9797
|
+
const base = opts.baseDir ? path29.resolve(opts.baseDir) : "/tmp";
|
|
9316
9798
|
const name = opts.name ?? defaultProjectName();
|
|
9317
|
-
const ws =
|
|
9318
|
-
await
|
|
9799
|
+
const ws = path29.join(base, name);
|
|
9800
|
+
await fs27.mkdir(ws, { recursive: true });
|
|
9319
9801
|
return ws;
|
|
9320
9802
|
}
|
|
9321
9803
|
|