micro-models-agent 2.10.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/dist/assets/app.js +22 -4
- package/dist/assets/index.html +29 -0
- package/dist/assets/js/dom.js +2 -1
- package/dist/assets/js/feed.js +38 -26
- package/dist/assets/js/format.js +1 -1
- package/dist/assets/js/rail.js +114 -0
- package/dist/assets/js/settings-controls.js +2 -2
- package/dist/assets/js/settings.js +246 -51
- package/dist/assets/js/store.js +3 -0
- package/dist/assets/js/tabs.js +104 -2
- package/dist/assets/styles.css +215 -66
- package/dist/cli/cache-line.js +1 -0
- package/dist/cli/command-suggest.js +5 -4
- package/dist/cli/commands.js +231 -122
- package/dist/cli/completer.js +132 -31
- package/dist/cli/index.js +1 -0
- package/dist/cli/json-payload.js +4 -4
- package/dist/cli/main.js +166 -61
- package/dist/cli/onboarding-commands.js +126 -0
- package/dist/cli/plugin-commands.js +2 -1
- package/dist/cli/relaunch.js +16 -13
- package/dist/cli/repl-commands-lsp.js +75 -0
- package/dist/cli/repl-commands-skills.js +105 -0
- package/dist/cli/repl-commands.js +589 -266
- package/dist/cli/repl.js +436 -177
- package/dist/cli/run-result.js +5 -6
- package/dist/cli/security-commands.js +37 -28
- package/dist/cli/setup-order.js +4 -55
- package/dist/cli/setup-prompt.js +3 -2
- package/dist/cli/setup.js +27 -107
- package/dist/cli/web-command.js +98 -0
- package/dist/config/budget.js +5 -5
- package/dist/config/config.js +103 -96
- package/dist/config/defaults.js +38 -8
- package/dist/config/domains.js +24 -27
- package/dist/config/env.js +46 -0
- package/dist/config/experts.js +2 -0
- package/dist/config/index.js +1 -0
- package/dist/config/paths.js +56 -0
- package/dist/config/redact.js +44 -0
- package/dist/config/registry-extra.js +334 -0
- package/dist/config/registry.js +584 -0
- package/dist/config/security.js +35 -36
- package/dist/config/timeouts.js +74 -0
- package/dist/config/utils.js +16 -15
- package/dist/core/agent/artifact-progress.js +59 -0
- package/dist/core/agent/artifact-resolve.js +79 -0
- package/dist/core/agent/audit-gate.js +7 -3
- package/dist/core/agent/compaction.js +2 -2
- package/dist/core/agent/constants.js +33 -4
- package/dist/core/agent/context-log.js +454 -0
- package/dist/core/agent/context-renderer.js +5 -0
- package/dist/core/agent/critic-gate.js +132 -0
- package/dist/core/agent/hallucination-gate.js +148 -38
- package/dist/core/agent/loop-state.js +15 -0
- package/dist/core/agent/prefix-monitor.js +1 -1
- package/dist/core/agent/reasoning-resolver.js +10 -1
- package/dist/core/agent/token-tracker.js +7 -4
- package/dist/core/agent/tool-batch.js +93 -18
- package/dist/core/agent/tool-images.js +18 -0
- package/dist/core/agent/tool-output.js +65 -11
- package/dist/core/agent/tool-pairing.js +60 -0
- package/dist/core/agent-moe.js +25 -24
- package/dist/core/agent.js +429 -108
- package/dist/core/bootstrap/agent-phase.js +66 -0
- package/dist/core/bootstrap/config-phase.js +86 -0
- package/dist/core/bootstrap/modules-phase.js +269 -0
- package/dist/core/bootstrap/provider-phase.js +183 -0
- package/dist/core/bootstrap/session-phase.js +204 -0
- package/dist/core/bootstrap/state.js +94 -0
- package/dist/core/bootstrap/workspace-phase.js +115 -0
- package/dist/core/bootstrap.js +92 -660
- package/dist/core/crash-handler.js +17 -7
- package/dist/core/environment.js +37 -12
- package/dist/core/host-plan.js +43 -0
- package/dist/core/host.js +23 -0
- package/dist/core/index.js +1 -0
- package/dist/core/prompt-builder.js +17 -1
- package/dist/core/prompt-overflow.js +42 -43
- package/dist/core/session-logger.js +122 -30
- package/dist/core/system-prompt.js +117 -0
- package/dist/core/version.js +6 -4
- package/dist/core/web-host.js +34 -0
- package/dist/core/workspace.js +19 -15
- package/dist/i18n/en.json +106 -21
- package/dist/i18n/index.js +37 -3
- package/dist/i18n/ru.json +109 -21
- package/dist/index.js +7 -3
- package/dist/llm/image-utils.js +55 -64
- package/dist/llm/llm-errors.js +9 -0
- package/dist/llm/model-loader.js +10 -32
- package/dist/llm/models-dev.js +121 -0
- package/dist/llm/openai-compat.js +146 -73
- package/dist/llm/orchestrator.js +33 -11
- package/dist/llm/provider-budget.js +2 -2
- package/dist/llm/provider-context.js +233 -0
- package/dist/llm/provider.js +1 -1
- package/dist/llm/response.js +5 -0
- package/dist/llm/token-counter.js +27 -11
- package/dist/logger/app-logger.js +48 -22
- package/dist/logger/file-log.js +28 -19
- package/dist/logger/index.js +1 -0
- package/dist/main.js +24285 -22447
- package/dist/migration/backup.js +14 -3
- package/dist/migration/detect.js +12 -3
- package/dist/migration/index.js +1 -0
- package/dist/modules/artifacts/store.js +9 -8
- package/dist/modules/browser/actions.js +6 -0
- package/dist/modules/browser/bridge-client.js +14 -12
- package/dist/modules/browser/bridge-path.js +4 -4
- package/dist/modules/browser/console-buffer.js +37 -0
- package/dist/modules/browser/cookie-store.js +5 -0
- package/dist/modules/browser/driver-types.js +1 -0
- package/dist/modules/browser/driver.js +19 -11
- package/dist/modules/browser/index.js +3 -1
- package/dist/modules/browser/module.js +2 -1
- package/dist/modules/browser/session.js +10 -30
- package/dist/modules/browser/snapshot.js +19 -13
- package/dist/modules/browser/types.js +1 -0
- package/dist/modules/certification/cli.js +28 -20
- package/dist/modules/certification/fact-checker.js +9 -1
- package/dist/modules/certification/loader.js +9 -2
- package/dist/modules/certification/manifest.js +83 -21
- package/dist/modules/certification/runner.js +23 -13
- package/dist/modules/certification/scenarios.js +20 -20
- package/dist/modules/certification/syntax-scenarios.js +1 -2
- package/dist/modules/context/chunk-query.js +11 -9
- package/dist/modules/context/fact-extractor.js +37 -39
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +106 -80
- package/dist/modules/critic/client.js +176 -0
- package/dist/modules/critic/evidence.js +81 -0
- package/dist/modules/critic/provider.js +45 -0
- package/dist/modules/critic/tools.js +78 -0
- package/dist/modules/critic/types.js +1 -0
- package/dist/modules/execution/audit-runners.js +28 -34
- package/dist/modules/execution/auditor.js +53 -49
- package/dist/modules/execution/execution-plugin.js +142 -102
- package/dist/modules/execution/index.js +1 -1
- package/dist/modules/execution/input-from.js +37 -13
- package/dist/modules/execution/module.js +368 -191
- package/dist/modules/execution/moe-executor.js +53 -26
- package/dist/modules/execution/plan-actions.js +320 -202
- package/dist/modules/execution/plan-checks.js +35 -0
- package/dist/modules/execution/plan-coverage.js +36 -33
- package/dist/modules/execution/plan-steps.js +11 -3
- package/dist/modules/execution/plan-store.js +83 -17
- package/dist/modules/execution/plan-tool.js +89 -35
- package/dist/modules/execution/plan-validator.js +30 -10
- package/dist/modules/execution/planner.js +22 -13
- package/dist/modules/execution/stuck-detector.js +196 -93
- package/dist/modules/execution/tracker.js +22 -8
- package/dist/modules/execution/verifier.js +69 -48
- package/dist/modules/execution/windows-commands.js +12 -12
- package/dist/modules/hallucination/artifact-claim.js +93 -0
- package/dist/modules/hallucination/confidence.js +13 -11
- package/dist/modules/hallucination/consistency.js +10 -2
- package/dist/modules/hallucination/detector.js +10 -5
- package/dist/modules/hallucination/factual.js +43 -26
- package/dist/modules/hallucination/index.js +1 -0
- package/dist/modules/hallucination/js-identifiers.js +44 -45
- package/dist/modules/hallucination/llm-judge.js +17 -12
- package/dist/modules/hallucination/success-claim.js +59 -0
- package/dist/modules/indexer/cache.js +52 -4
- package/dist/modules/indexer/index.js +1 -0
- package/dist/modules/indexer/map-command.js +2 -4
- package/dist/modules/indexer/map-select.js +16 -16
- package/dist/modules/indexer/module.js +47 -12
- package/dist/modules/indexer/project-profile.js +8 -7
- package/dist/modules/indexer/symbols.js +15 -15
- package/dist/modules/indexer/walker.js +159 -29
- package/dist/modules/lsp/check-tool.js +7 -5
- package/dist/modules/lsp/client.js +39 -39
- package/dist/modules/lsp/command.js +17 -24
- package/dist/modules/lsp/config.js +12 -11
- package/dist/modules/lsp/index.js +1 -0
- package/dist/modules/lsp/module.js +48 -46
- package/dist/modules/lsp/probe.js +13 -14
- package/dist/modules/lsp/project-root.js +14 -17
- package/dist/modules/lsp/startup-check.js +18 -16
- package/dist/modules/mcp/client.js +59 -62
- package/dist/modules/mcp/index.js +1 -0
- package/dist/modules/mcp/module.js +66 -37
- package/dist/modules/mcp/registry.js +5 -0
- package/dist/modules/memory/index.js +1 -0
- package/dist/modules/memory/module.js +12 -8
- package/dist/modules/memory/search.js +4 -1
- package/dist/modules/memory/store.js +40 -2
- package/dist/modules/onboarding/init.js +47 -0
- package/dist/modules/onboarding/meet.js +30 -0
- package/dist/modules/onboarding/mode.js +30 -0
- package/dist/modules/onboarding/prompts.js +43 -0
- package/dist/modules/pipelines/engine.js +10 -0
- package/dist/modules/pipelines/index.js +1 -0
- package/dist/modules/pipelines/parser.js +4 -1
- package/dist/modules/pipelines/template.js +4 -0
- package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
- package/dist/modules/plugins/builtin/notify.js +2 -0
- package/dist/modules/plugins/index.js +1 -0
- package/dist/modules/plugins/loader.js +18 -3
- package/dist/modules/plugins/manager.js +37 -17
- package/dist/modules/pricing/index.js +10 -11
- package/dist/modules/pricing/prices.js +12 -12
- package/dist/modules/processes/index.js +1 -0
- package/dist/modules/processes/kill-tree.js +27 -31
- package/dist/modules/processes/registry.js +44 -25
- package/dist/modules/processes/runner.js +4 -4
- package/dist/modules/providers/capabilities.js +28 -0
- package/dist/modules/providers/create.js +3 -4
- package/dist/modules/providers/factory.js +7 -7
- package/dist/modules/providers/fallback.js +12 -12
- package/dist/modules/providers/health.js +4 -4
- package/dist/modules/providers/manager.js +50 -19
- package/dist/modules/providers/naming.js +15 -0
- package/dist/modules/providers/preflight.js +71 -0
- package/dist/modules/providers/presets.js +39 -8
- package/dist/modules/providers/registry.js +7 -2
- package/dist/modules/reasoning/policy.js +8 -6
- package/dist/modules/reasoning/probe.js +30 -25
- package/dist/modules/security/audit-log.js +19 -36
- package/dist/modules/security/audit-notifier.js +35 -75
- package/dist/modules/security/command-validator.js +99 -51
- package/dist/modules/security/content-scanner.js +8 -8
- package/dist/modules/security/data-sanitizer.js +9 -16
- package/dist/modules/security/encryption.js +32 -52
- package/dist/modules/security/index.js +2 -1
- package/dist/modules/security/network-validator.js +120 -43
- package/dist/modules/security/path-validator.js +61 -46
- package/dist/modules/security/protected-paths.js +73 -0
- package/dist/modules/security/rate-limiter.js +17 -49
- package/dist/modules/security/security-policies.js +25 -57
- package/dist/modules/security/session-encryption.js +32 -71
- package/dist/modules/security/session-isolation.js +15 -28
- package/dist/modules/session/auto-name.js +63 -0
- package/dist/modules/session/export.js +98 -0
- package/dist/modules/session/index.js +1 -0
- package/dist/modules/session/manager.js +79 -28
- package/dist/modules/session/module.js +4 -3
- package/dist/modules/session/store.js +57 -20
- package/dist/modules/setup/answers.js +35 -0
- package/dist/modules/setup/apply.js +79 -0
- package/dist/modules/setup/discovery.js +72 -0
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/loader.js +9 -1
- package/dist/modules/skills/module.js +11 -0
- package/dist/modules/updater/changelog-reader.js +15 -16
- package/dist/modules/updater/checker.js +9 -4
- package/dist/modules/updater/dev-detect.js +7 -6
- package/dist/modules/updater/index.js +1 -0
- package/dist/modules/updater/module.js +17 -15
- package/dist/modules/user-profile/compressor.js +2 -0
- package/dist/modules/user-profile/index.js +1 -0
- package/dist/modules/user-profile/profile.js +10 -1
- package/dist/modules/webui/answer-api.js +29 -0
- package/dist/modules/webui/assets.js +50 -0
- package/dist/modules/webui/body.js +21 -0
- package/dist/modules/webui/commands-settings.js +209 -0
- package/dist/modules/webui/commands-shared.js +60 -0
- package/dist/modules/webui/commands.js +174 -0
- package/dist/modules/webui/config-api.js +134 -0
- package/dist/modules/webui/file-api.js +202 -0
- package/dist/modules/webui/forward.js +159 -0
- package/dist/modules/webui/history-api.js +112 -0
- package/dist/modules/webui/host-state.js +60 -0
- package/dist/modules/webui/i18n-api.js +9 -0
- package/dist/modules/webui/limits.js +6 -0
- package/dist/modules/webui/log-forward.js +20 -0
- package/dist/modules/webui/memory-commands.js +64 -0
- package/dist/modules/webui/model-cache.js +18 -0
- package/dist/modules/webui/module.js +148 -0
- package/dist/modules/webui/persist.js +24 -0
- package/dist/modules/webui/prompt-io.js +97 -0
- package/dist/modules/webui/providers-api.js +337 -0
- package/dist/modules/webui/providers-view.js +56 -0
- package/dist/modules/webui/reasoning-view.js +15 -0
- package/dist/modules/webui/run-agent.js +81 -0
- package/dist/modules/webui/server.js +284 -0
- package/dist/modules/webui/sessions-api.js +178 -0
- package/dist/modules/webui/settings-api.js +561 -0
- package/dist/modules/webui/setup-api.js +201 -0
- package/dist/modules/webui/state.js +74 -0
- package/dist/modules/webui/util.js +261 -0
- package/dist/output/bus.js +7 -2
- package/dist/output/channel.js +80 -28
- package/dist/output/format.js +2 -1
- package/dist/output/index.js +1 -0
- package/dist/output/json-sink.js +5 -3
- package/dist/output/machine.js +4 -3
- package/dist/output/session-sink.js +4 -4
- package/dist/tools/approve.js +4 -0
- package/dist/tools/attach-image.js +5 -4
- package/dist/tools/auto-fixer.js +26 -44
- package/dist/tools/auto-tags.js +83 -0
- package/dist/tools/bash.js +193 -116
- package/dist/tools/browser.js +76 -31
- package/dist/tools/chunk-query.js +14 -6
- package/dist/tools/create-dir.js +9 -5
- package/dist/tools/delete-file.js +9 -5
- package/dist/tools/download-file.js +32 -7
- package/dist/tools/edit-file.js +52 -14
- package/dist/tools/enable-tools.js +6 -6
- package/dist/tools/executor.js +132 -27
- package/dist/tools/file-info.js +3 -1
- package/dist/tools/filter-tools.js +3 -3
- package/dist/tools/glob-tool.js +4 -1
- package/dist/tools/grep-tool.js +13 -10
- package/dist/tools/hidden-tools-block.js +6 -6
- package/dist/tools/index.js +16 -8
- package/dist/tools/list-dir.js +4 -2
- package/dist/tools/load-skill.js +7 -0
- package/dist/tools/mcp-call.js +15 -4
- package/dist/tools/meet-done.js +14 -0
- package/dist/tools/move-file.js +14 -7
- package/dist/tools/path-utils.js +29 -29
- package/dist/tools/pipeline-run.js +29 -7
- package/dist/tools/preview.js +1 -1
- package/dist/tools/process-ids.js +31 -0
- package/dist/tools/process-kill.js +9 -16
- package/dist/tools/process-list.js +1 -0
- package/dist/tools/process-log.js +11 -16
- package/dist/tools/process-wait.js +63 -0
- package/dist/tools/prompt-io.js +4 -3
- package/dist/tools/question.js +172 -25
- package/dist/tools/read-file.js +55 -34
- package/dist/tools/recall.js +14 -8
- package/dist/tools/registry.js +6 -5
- package/dist/tools/remember.js +8 -4
- package/dist/tools/scope-request.js +11 -6
- package/dist/tools/search-history.js +5 -2
- package/dist/tools/session-info.js +2 -0
- package/dist/tools/set-thinking.js +8 -4
- package/dist/tools/subagent.js +59 -39
- package/dist/tools/syntax-validator.js +292 -90
- package/dist/tools/user-input.js +22 -18
- package/dist/tools/web-browse.js +9 -3
- package/dist/tools/web-fetch.js +12 -23
- package/dist/tools/web-search.js +7 -5
- package/dist/tools/write-file.js +45 -15
- package/dist/ui/action-log.js +13 -0
- package/dist/ui/box.js +4 -3
- package/dist/ui/colors.js +2 -0
- package/dist/ui/diff.js +6 -5
- package/dist/ui/index.js +1 -0
- package/dist/ui/line-editor.js +230 -122
- package/dist/ui/line-math.js +10 -2
- package/dist/ui/md-formatter.js +57 -37
- package/dist/ui/output.js +8 -3
- package/dist/ui/plan-view.js +21 -10
- package/dist/ui/renderer.js +393 -67
- package/dist/ui/spinner.js +16 -5
- package/dist/ui/table.js +15 -17
- package/dist/ui/theme.js +143 -0
- package/dist/utils/child-run.js +20 -0
- package/dist/utils/error.js +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/retry.js +2 -2
- package/dist/utils/truncate.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Сборка пакета доказательств для критика: дайджест тулов, файлы, команды.
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
import { summarizeToolArgs } from "../../core/agent/tool-output";
|
|
5
|
+
const MUTATING_TOOLS = new Set(["write_file", "edit_file", "delete_file", "move_file", "create_dir"]);
|
|
6
|
+
/** Мутация работы = тул, меняющий ФС (bash входит; plan/todo-бухгалтерия — нет). */
|
|
7
|
+
export function isWorkMutation(name) {
|
|
8
|
+
return MUTATING_TOOLS.has(name) || name === "bash";
|
|
9
|
+
}
|
|
10
|
+
const MAX_DIGEST_LINES = 40;
|
|
11
|
+
/** Хвост вывода, прикладываемый к bash/фейловым строкам дайджеста (результаты тестов/сборки — сигнал верификации). */
|
|
12
|
+
const MAX_DIGEST_OUTPUT_CHARS = 300;
|
|
13
|
+
const MAX_EXCERPT_FILES = 5;
|
|
14
|
+
const MAX_EXCERPT_CHARS = 1200;
|
|
15
|
+
/** Собирает EvidencePack из событий тулов: дайджест, файлы, команды, выдержки. */
|
|
16
|
+
export function buildEvidencePack(input) {
|
|
17
|
+
const changedFiles = new Set();
|
|
18
|
+
const commands = [];
|
|
19
|
+
const digest = [];
|
|
20
|
+
for (const ev of input.toolEvents) {
|
|
21
|
+
digest.push(digestLine(ev));
|
|
22
|
+
const file = changedFileOf(ev);
|
|
23
|
+
if (file)
|
|
24
|
+
changedFiles.add(file);
|
|
25
|
+
const cmd = commandOf(ev);
|
|
26
|
+
if (cmd)
|
|
27
|
+
commands.push(cmd);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
task: input.task,
|
|
31
|
+
planSummary: input.planSummary,
|
|
32
|
+
changedFiles: [...changedFiles],
|
|
33
|
+
commands,
|
|
34
|
+
toolDigest: digest.slice(-MAX_DIGEST_LINES).join("\n"),
|
|
35
|
+
fileExcerpts: input.baseDir ? readExcerpts(input.baseDir, [...changedFiles]) : undefined,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Одна строка дайджеста на событие. Вывод bash (результаты тестов/сборки) и вывод
|
|
39
|
+
* ошибок идут вместе со строкой — иначе критик судит об успехе вслепую. */
|
|
40
|
+
function digestLine(ev) {
|
|
41
|
+
const args = ev.args ? summarizeToolArgs(ev.args) : "";
|
|
42
|
+
const line = `${ev.ok === false ? "✗" : "✓"} ${ev.tool} ${args}`.trim();
|
|
43
|
+
if (ev.output && (ev.tool === "bash" || ev.ok === false)) {
|
|
44
|
+
return `${line} » ${ev.output.slice(-MAX_DIGEST_OUTPUT_CHARS)}`;
|
|
45
|
+
}
|
|
46
|
+
return line;
|
|
47
|
+
}
|
|
48
|
+
/** Относительный путь файла, затронутого событием write/edit. */
|
|
49
|
+
function changedFileOf(ev) {
|
|
50
|
+
if (ev.tool !== "write_file" && ev.tool !== "edit_file")
|
|
51
|
+
return undefined;
|
|
52
|
+
return typeof ev.args?.path === "string" ? ev.args.path : undefined;
|
|
53
|
+
}
|
|
54
|
+
/** Событие bash как запись команды, видимая критику (с ограниченным хвостом вывода). */
|
|
55
|
+
function commandOf(ev) {
|
|
56
|
+
if (ev.tool !== "bash" || typeof ev.args?.command !== "string")
|
|
57
|
+
return undefined;
|
|
58
|
+
return {
|
|
59
|
+
command: ev.args.command,
|
|
60
|
+
exitCode: ev.ok === false ? 1 : 0,
|
|
61
|
+
output: ev.output?.slice(-MAX_DIGEST_OUTPUT_CHARS),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Best-effort ограниченные head-выдержки изменённых файлов. Нечитаемые (удалены, бинарные, вне root) — пропуск, никогда не бросает. */
|
|
65
|
+
function readExcerpts(baseDir, paths) {
|
|
66
|
+
const out = [];
|
|
67
|
+
const root = resolve(baseDir);
|
|
68
|
+
for (const p of paths.slice(0, MAX_EXCERPT_FILES)) {
|
|
69
|
+
try {
|
|
70
|
+
const abs = resolve(root, p);
|
|
71
|
+
if (abs !== root && !abs.startsWith(`${root}/`) && !abs.startsWith(`${root}\\`))
|
|
72
|
+
continue;
|
|
73
|
+
const text = readFileSync(abs, "utf-8");
|
|
74
|
+
out.push({ path: p, excerpt: text.slice(0, MAX_EXCERPT_CHARS) });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Нечитаемый (удалён, бинарный, вне root) — молча пропускаем.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ProviderManager } from "../providers/manager";
|
|
2
|
+
import { FallbackProvider } from "../providers/fallback";
|
|
3
|
+
const DEFAULT_CRITIC_BASE_URL = "http://localhost:1234/v1";
|
|
4
|
+
/** Окно контекста, если ни запись провайдера, ни конфиг его не задали. */
|
|
5
|
+
const DEFAULT_CRITIC_CONTEXT_WINDOW = 32768;
|
|
6
|
+
/**
|
|
7
|
+
* Резолвит LLM для критика (Phase 2). Зеркалит OrchestratorClient:
|
|
8
|
+
* управляемый провайдер строится через ProviderManager (per-entry
|
|
9
|
+
* contextWindow/retry/rateLimits, failover) ТОЛЬКО если задан `critic.model`;
|
|
10
|
+
* иначе критик делит активный провайдер (поведение v1). `critic.provider`
|
|
11
|
+
* без модели сохраняет поведение v1.
|
|
12
|
+
*
|
|
13
|
+
* Fail-open: битый оверрайд предупреждает и откатывается к активному
|
|
14
|
+
* провайдеру — неверно настроенный критик не должен молча отключать проверку.
|
|
15
|
+
*/
|
|
16
|
+
export function buildCriticProvider(critic, config, defaultProvider, deps) {
|
|
17
|
+
const model = critic?.model?.trim();
|
|
18
|
+
if (!model)
|
|
19
|
+
return defaultProvider;
|
|
20
|
+
try {
|
|
21
|
+
const providerCfg = critic?.provider ?? {
|
|
22
|
+
type: "openai-compat",
|
|
23
|
+
baseUrl: DEFAULT_CRITIC_BASE_URL,
|
|
24
|
+
};
|
|
25
|
+
const manager = new ProviderManager({ ...providerCfg, baseUrl: providerCfg.baseUrl || DEFAULT_CRITIC_BASE_URL }, {
|
|
26
|
+
contextWindow: config.contextWindow ?? DEFAULT_CRITIC_CONTEXT_WINDOW,
|
|
27
|
+
retry: config.retry,
|
|
28
|
+
rateLimits: config.security?.rateLimits,
|
|
29
|
+
getSessionId: deps?.getSessionId,
|
|
30
|
+
logger: deps?.logger,
|
|
31
|
+
});
|
|
32
|
+
manager.setModel(model);
|
|
33
|
+
let provider = manager.active;
|
|
34
|
+
if (providerCfg.fallback && manager.listNames().length > 1) {
|
|
35
|
+
provider = new FallbackProvider(manager, (from, to, error) => {
|
|
36
|
+
deps?.logger?.warn(`critic provider failover: ${from} -> ${to} (${error.message.slice(0, 120)})`);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return provider;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
deps?.logger?.warn(`critic provider override failed, using active provider: ${String(err?.message ?? err).slice(0, 160)}`);
|
|
43
|
+
return defaultProvider;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ограниченный read-only контур верификации (Phase 2, Agent-as-a-Judge).
|
|
3
|
+
*
|
|
4
|
+
* После вердикта concern/critical критик может запросить конкретные файловые
|
|
5
|
+
* проверки, чтобы подтвердить/опровергнуть проблему, а не судить вслепую.
|
|
6
|
+
* Доступны только read-only тулы — записи, shell, web и память исключены,
|
|
7
|
+
* так что скомпрометированный/запутавшийся критик ничего не мутирует. Цикл
|
|
8
|
+
* ограничен (доп. LLM-вызовы + проверки делят verify-таймаут), при сбое —
|
|
9
|
+
* откат к вердикту первого прохода.
|
|
10
|
+
*/
|
|
11
|
+
/** Read-only тулы, доступные критику. Записи/shell/web/память исключены by design. */
|
|
12
|
+
export const CRITIC_READONLY_TOOLS = ["read_file", "grep", "glob", "file_info", "list_dir"];
|
|
13
|
+
/** Жёсткий кап проверок на вердикт (внутренний дефолт, не конфиг — спек §4.3). */
|
|
14
|
+
export const MAX_CRITIC_CHECKS = 3;
|
|
15
|
+
/** Символов вывода на проверку, возвращаемых критику (ограничено — у verify-вызова свой бюджет). */
|
|
16
|
+
export const MAX_CHECK_OUTPUT_CHARS = 1500;
|
|
17
|
+
/** True, если тул разрешён критику (входит в read-only allowlist). */
|
|
18
|
+
export function isAllowedCriticTool(tool) {
|
|
19
|
+
return CRITIC_READONLY_TOOLS.includes(tool);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Проверки из вердикта критика (сырой массив `checks`, allowlist + кап).
|
|
23
|
+
* Чужое — неизвестные тулы, не-объектные args — молча дропается:
|
|
24
|
+
* критик теряет только глубину верификации, не корректность.
|
|
25
|
+
*/
|
|
26
|
+
export function parseChecks(verdict) {
|
|
27
|
+
if (!verdict?.checks || !Array.isArray(verdict.checks))
|
|
28
|
+
return [];
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const c of verdict.checks) {
|
|
31
|
+
if (out.length >= MAX_CRITIC_CHECKS)
|
|
32
|
+
break;
|
|
33
|
+
const check = asCriticCheck(c);
|
|
34
|
+
if (check)
|
|
35
|
+
out.push(check);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
/** Одна сырая запись → валидированная проверка, или null, если непригодна/запрещена. */
|
|
40
|
+
function asCriticCheck(c) {
|
|
41
|
+
if (!c || typeof c !== "object")
|
|
42
|
+
return null;
|
|
43
|
+
const rec = c;
|
|
44
|
+
if (typeof rec.tool !== "string" || !isAllowedCriticTool(rec.tool))
|
|
45
|
+
return null;
|
|
46
|
+
if (!rec.args || typeof rec.args !== "object" || Array.isArray(rec.args))
|
|
47
|
+
return null;
|
|
48
|
+
return { tool: rec.tool, args: rec.args };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Выполняет валидированные проверки через инжектированный раннер (в Agent —
|
|
52
|
+
* ToolExecutor с read-only allowlist). Последовательно, никогда параллельно
|
|
53
|
+
* с вызовами тулов главного цикла. Сбой одной проверки даёт failure-результат,
|
|
54
|
+
* остальные проверки не прерываются.
|
|
55
|
+
*/
|
|
56
|
+
export async function runCriticChecks(checks, runCheck) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const c of checks.slice(0, MAX_CRITIC_CHECKS)) {
|
|
59
|
+
try {
|
|
60
|
+
const r = await runCheck(c.tool, c.args);
|
|
61
|
+
out.push({ tool: c.tool, ok: r.ok, output: (r.output ?? "").slice(0, MAX_CHECK_OUTPUT_CHARS) });
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
out.push({
|
|
65
|
+
tool: c.tool,
|
|
66
|
+
ok: false,
|
|
67
|
+
output: `check failed: ${String(err?.message ?? err).slice(0, 200)}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** Рендерит результаты проверок строками доказательств, видимых критику. */
|
|
74
|
+
export function renderCheckResults(results) {
|
|
75
|
+
return results
|
|
76
|
+
.map((r) => `${r.ok ? "✓" : "✗"} ${r.tool} » ${r.output}`)
|
|
77
|
+
.join("\n");
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
// Раннеры финального аудита: поиск тестов, прогон тестов, поиск корня typecheck.
|
|
1
2
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
2
3
|
import { dirname, join, resolve } from "path";
|
|
3
4
|
import { detectTestResults } from "../../tools/bash";
|
|
4
5
|
import { processRegistry } from "../processes";
|
|
5
6
|
import { resolveTscCommand } from "../lsp/project-root";
|
|
6
|
-
/**
|
|
7
|
+
/** Каталоги, где тестовые файлы не ищем. */
|
|
7
8
|
export const SKIP_DIRS = new Set([
|
|
8
9
|
"node_modules",
|
|
9
10
|
".git",
|
|
@@ -17,15 +18,13 @@ export const SKIP_DIRS = new Set([
|
|
|
17
18
|
]);
|
|
18
19
|
const TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
|
|
19
20
|
const PY_TEST_RE = /^test_.*\.py$|^.*_test\.py$/i;
|
|
20
|
-
const TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* silently produce no summary under bun test).
|
|
22
|
+
* Резолвит тестовую команду проекта из манифеста: финальный аудит реально
|
|
23
|
+
* гоняет раннер проекта, а не всегда `bun test` (Python/Go-проект или
|
|
24
|
+
* vitest/jest-сетап под bun test упал бы или молча не дал сводки).
|
|
26
25
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* Приоритет: package.json scripts.test → конфиги тест-фреймворков → Python-
|
|
27
|
+
* маркеры (pyproject/pytest.ini/conftest.py) → go.mod → Cargo.toml → bun.
|
|
29
28
|
*/
|
|
30
29
|
export function resolveTestCommand(dir) {
|
|
31
30
|
const pkgPath = join(dir, "package.json");
|
|
@@ -37,7 +36,7 @@ export function resolveTestCommand(dir) {
|
|
|
37
36
|
return script.trim();
|
|
38
37
|
}
|
|
39
38
|
catch {
|
|
40
|
-
/*
|
|
39
|
+
/* битый package.json — идём дальше */
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
42
|
for (const f of [
|
|
@@ -70,7 +69,7 @@ export function resolveTestCommand(dir) {
|
|
|
70
69
|
return "cargo test";
|
|
71
70
|
return "bun test";
|
|
72
71
|
}
|
|
73
|
-
/**
|
|
72
|
+
/** Рекурсивный поиск тестового файла с капом глубины и ранним выходом. */
|
|
74
73
|
export function findTestFile(dir, depth = 0) {
|
|
75
74
|
if (depth > 5)
|
|
76
75
|
return null;
|
|
@@ -96,11 +95,7 @@ export function findTestFile(dir, depth = 0) {
|
|
|
96
95
|
}
|
|
97
96
|
return null;
|
|
98
97
|
}
|
|
99
|
-
/**
|
|
100
|
-
export function hasTestStep(plan) {
|
|
101
|
-
return plan.steps.some((s) => TEST_STEP_RE.test(s.description));
|
|
102
|
-
}
|
|
103
|
-
/** Extract failing test names from a runner's output (bun/vitest style). */
|
|
98
|
+
/** Вытаскивает имена упавших тестов из вывода раннера (стиль bun/vitest). */
|
|
104
99
|
function extractFailingNames(output, limit = 5) {
|
|
105
100
|
const names = [];
|
|
106
101
|
for (const m of output.matchAll(/\(fail\)\s*([^\n]+)/g)) {
|
|
@@ -112,6 +107,7 @@ function extractFailingNames(output, limit = 5) {
|
|
|
112
107
|
}
|
|
113
108
|
return names;
|
|
114
109
|
}
|
|
110
|
+
/** Прогоняет тесты проекта (90s) и разбирает сводку раннера. Таймаут — inconclusive. */
|
|
115
111
|
export async function runTests(baseDir) {
|
|
116
112
|
const command = resolveTestCommand(baseDir);
|
|
117
113
|
const entry = processRegistry.start(command, baseDir);
|
|
@@ -119,7 +115,7 @@ export async function runTests(baseDir) {
|
|
|
119
115
|
const output = entry.log.join("\n");
|
|
120
116
|
processRegistry.remove(entry.id);
|
|
121
117
|
if (!exited) {
|
|
122
|
-
//
|
|
118
|
+
// Таймаут — успех/фейл незавершённого прогона утверждать не можем.
|
|
123
119
|
return {
|
|
124
120
|
checked: true,
|
|
125
121
|
passed: true, // don't block completion on an inconclusive run
|
|
@@ -131,10 +127,9 @@ export async function runTests(baseDir) {
|
|
|
131
127
|
}
|
|
132
128
|
const run = detectTestResults(output);
|
|
133
129
|
if (!run) {
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
// with unrecognized output stays checked-but-inconclusive.
|
|
130
|
+
// Нет узнаваемой сводки раннера. Ненулевой exit — прогон (или раннер) упал:
|
|
131
|
+
// аудит его пропускать не должен; репортим "как минимум один фейл", т.к.
|
|
132
|
+
// посчитать не можем. Exit 0 с неузнанным выводом — checked, но inconclusive.
|
|
138
133
|
return {
|
|
139
134
|
checked: true,
|
|
140
135
|
passed: entry.exitCode === 0,
|
|
@@ -156,24 +151,22 @@ export async function runTests(baseDir) {
|
|
|
156
151
|
command,
|
|
157
152
|
};
|
|
158
153
|
}
|
|
159
|
-
/**
|
|
154
|
+
/** Первая строка `error TS…` из вывода tsc, или null если нет/inconclusive. */
|
|
160
155
|
export function parseTypecheckErrors(output) {
|
|
161
156
|
const line = output.split("\n").find((l) => /error TS\d+/.test(l));
|
|
162
157
|
return line ? line.trim().slice(0, 300) : null;
|
|
163
158
|
}
|
|
164
159
|
/**
|
|
165
|
-
*
|
|
160
|
+
* Находит ближайший корень проекта с tsconfig.json.
|
|
166
161
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* tsconfig
|
|
174
|
-
*
|
|
175
|
-
* any reachable subtree. `existingFiles` must contain resolved absolute paths
|
|
176
|
-
* (relative tokens would resolve against the process cwd, not baseDir).
|
|
162
|
+
* Агент часто создаёт проект во вложенной папке baseDir (напр. `bicycle-shop/`),
|
|
163
|
+
* и tsconfig лежит в `baseDir/bicycle-shop/`; старый гейт `existsSync(baseDir/tsconfig.json)`
|
|
164
|
+
* молча скипал финальный typecheck — аудит проходил при десятках tsc-ошибок
|
|
165
|
+
* (было в обеих разобранных сессиях). Обходит каждого кандидата (baseDir плюс
|
|
166
|
+
* резолвленные файлы шагов) вверх до ближайшего tsconfig.json, предпочитая
|
|
167
|
+
* мельчайшее попадание (проект baseDir бьёт шальной tsconfig выше). Null, если
|
|
168
|
+
* tsconfig недостижим. `existingFiles` — резолвленные абсолютные пути
|
|
169
|
+
* (относительные токены резолвились бы от cwd процесса, не от baseDir).
|
|
177
170
|
*/
|
|
178
171
|
export function findTypecheckRoot(baseDir, existingFiles = []) {
|
|
179
172
|
const candidates = [baseDir, ...existingFiles];
|
|
@@ -194,6 +187,7 @@ export function findTypecheckRoot(baseDir, existingFiles = []) {
|
|
|
194
187
|
}
|
|
195
188
|
return best?.root ?? null;
|
|
196
189
|
}
|
|
190
|
+
/** Прогоняет `tsc --noEmit --skipLibCheck` (90s). Null, если проверить нельзя/таймаут. */
|
|
197
191
|
export async function runTypecheck(baseDir) {
|
|
198
192
|
// Локальный tsc напрямую: npx-обёртка добавляет секунды и без локального
|
|
199
193
|
// пакета уходит в сеть. Нет tsc — проверка невозможна, не блокируем.
|
|
@@ -204,8 +198,8 @@ export async function runTypecheck(baseDir) {
|
|
|
204
198
|
const exited = await processRegistry.waitForExit(entry.id, 90_000);
|
|
205
199
|
const output = entry.log.join("\n");
|
|
206
200
|
processRegistry.remove(entry.id);
|
|
207
|
-
//
|
|
208
|
-
//
|
|
201
|
+
// Таймаут или вывод без строк `error TS` (нет typescript / не та рабочая
|
|
202
|
+
// папка) — inconclusive, завершение на этом не блокируем.
|
|
209
203
|
if (!exited)
|
|
210
204
|
return null;
|
|
211
205
|
return parseTypecheckErrors(output);
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
+
// Финальный аудитор: файлы done-шагов, тесты, typecheck, сводка честности.
|
|
1
2
|
import { existsSync, readdirSync } from "fs";
|
|
2
3
|
import { resolve, join, basename } from "path";
|
|
3
4
|
import { t } from "../../i18n/index";
|
|
4
5
|
import { extractFileLikeTokens, stripUrls } from "../hallucination/js-identifiers";
|
|
5
6
|
import { toForwardSlash } from "../../tools/path-utils";
|
|
6
|
-
import { SKIP_DIRS, findTestFile, findTypecheckRoot,
|
|
7
|
+
import { SKIP_DIRS, findTestFile, findTypecheckRoot, runTests, runTypecheck, } from "./audit-runners";
|
|
7
8
|
export { parseTypecheckErrors } from "./audit-runners";
|
|
8
9
|
const MASS_EDIT_THRESHOLD = 10;
|
|
9
|
-
/**
|
|
10
|
+
/** Кап глубины обхода поддерева для резолва файлов. */
|
|
10
11
|
const RESOLVE_MAX_DEPTH = 8;
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
+
* Резолвит файловый токен шага плана в существующий файл.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
* ("App.tsx")
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* forever, sending the model into a read/retry loop.
|
|
15
|
+
* Описание шага часто называет файл голым basename ("App.tsx") или частично
|
|
16
|
+
* префиксным путём ("src/App.tsx"), а реальный файл лежит во вложенной папке
|
|
17
|
+
* проекта относительно baseDir агента (напр. `bicycle-shop/src/App.tsx`).
|
|
18
|
+
* Наивный резолв от baseDir репортит файл пропавшим — было в ses_mssl5izw:
|
|
19
|
+
* финальный audit-gate вечно реджектил полностью готовую задачу с "1 files
|
|
20
|
+
* missing", загоняя модель в read/retry-петлю.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* Стратегия: сначала точный резолв, затем ограниченный обход поддерева с
|
|
23
|
+
* матчем точного basename или суффикса пути ("App.tsx" и "src/App.tsx"
|
|
24
|
+
* находят "bicycle-shop/src/App.tsx").
|
|
25
25
|
*/
|
|
26
26
|
export function findExistingFile(baseDir, filePath) {
|
|
27
27
|
const direct = resolve(baseDir, filePath);
|
|
@@ -52,7 +52,7 @@ export function findExistingFile(baseDir, filePath) {
|
|
|
52
52
|
else if (e.name.toLowerCase() === name ||
|
|
53
53
|
toForwardSlash(full)
|
|
54
54
|
.toLowerCase()
|
|
55
|
-
.endsWith(
|
|
55
|
+
.endsWith(`/${suffix}`)) {
|
|
56
56
|
found = full;
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
@@ -61,25 +61,26 @@ export function findExistingFile(baseDir, filePath) {
|
|
|
61
61
|
walk(baseDir, 0);
|
|
62
62
|
return found;
|
|
63
63
|
}
|
|
64
|
+
/** Аудитор: проверяет done-шаги плана — файлы, тесты, typecheck. */
|
|
64
65
|
export class Auditor {
|
|
65
66
|
baseDir;
|
|
66
67
|
constructor(baseDir) {
|
|
67
68
|
this.baseDir = baseDir;
|
|
68
69
|
}
|
|
70
|
+
/** Аудитит план: файлы done-шагов, реальный прогон тестов, typecheck, сводка. */
|
|
69
71
|
async audit(plan) {
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
// (
|
|
73
|
-
//
|
|
72
|
+
// Существования требуем только с файлов шагов, которые модель заявила
|
|
73
|
+
// готовыми. Скипнутые/пендинг шаги в "missing files" вклада не дают
|
|
74
|
+
// (было: проза скипнутого шага с "type.ts" вечно реджектила готовую
|
|
75
|
+
// задачу гейтом с "1 files missing").
|
|
74
76
|
const doneCreate = plan.steps.filter((s) => s.status === "done" && s.kind !== "delete");
|
|
75
77
|
const doneDelete = plan.steps.filter((s) => s.status === "done" && s.kind === "delete");
|
|
76
78
|
const createText = doneCreate.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
77
79
|
const deleteText = doneDelete.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
// plan flagged `whom-pointing-jewel-maine.trycloudflare.com`).
|
|
80
|
+
// Токены файлов извлекаем ТЕМ ЖЕ общим фильтром, что фактическая проверка
|
|
81
|
+
// галлюцинаций (js-identifiers.ts). URL вырезаем заранее, чтобы hostname /
|
|
82
|
+
// https://host/repo.git в описании шага не репортился пропавшим файлом
|
|
83
|
+
// (было: git-remote план флаговал `whom-pointing-jewel-maine.trycloudflare.com`).
|
|
83
84
|
const createFiles = extractFileLikeTokens(stripUrls(createText));
|
|
84
85
|
const deleteFiles = extractFileLikeTokens(stripUrls(deleteText));
|
|
85
86
|
const missingFiles = [];
|
|
@@ -88,41 +89,44 @@ export class Auditor {
|
|
|
88
89
|
for (const filePath of createFiles) {
|
|
89
90
|
const resolved = findExistingFile(this.baseDir, filePath);
|
|
90
91
|
if (resolved) {
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
92
|
+
// Храним резолвленный абсолютный путь: findTypecheckRoot идёт от него
|
|
93
|
+
// вверх к ближайшему tsconfig, а относительный токен резолвился бы
|
|
94
|
+
// от cwd процесса вместо baseDir.
|
|
94
95
|
existingFiles.push(resolved);
|
|
95
96
|
}
|
|
96
97
|
else {
|
|
97
98
|
missingFiles.push(filePath);
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
|
-
//
|
|
101
|
+
// Done-шаг kind=delete требует ОТСУТСТВИЯ именованных файлов.
|
|
101
102
|
for (const filePath of deleteFiles) {
|
|
102
103
|
if (findExistingFile(this.baseDir, filePath)) {
|
|
103
104
|
leftoverFiles.push(filePath);
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
107
|
+
// Реальный прогон тестов: слову модели (и exit-кодам) не верим — реально
|
|
108
|
+
// исполняем тесты и смотрим сводку раннера. Ловит финальные "all 7
|
|
109
|
+
// passing ✅", прячущие 3 упавших.
|
|
110
|
+
//
|
|
111
|
+
// Решение EVIDENCE-based (тестовый файл есть на диске), не классификация
|
|
112
|
+
// прозы шагов по ключевым словам: правило #10 запрещает выводить требования
|
|
113
|
+
// из слов "test"/"тест" (план с тестами, но без такого слова, скипал аудит;
|
|
114
|
+
// постороннее "testing" форсило 90s-прогон).
|
|
110
115
|
let testRun = null;
|
|
111
|
-
if (
|
|
116
|
+
if (findTestFile(this.baseDir)) {
|
|
112
117
|
try {
|
|
113
118
|
testRun = await runTests(this.baseDir);
|
|
114
119
|
}
|
|
115
120
|
catch {
|
|
116
|
-
testRun = null; //
|
|
121
|
+
testRun = null; // сбой инфры — на нём не блокируемся
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
// results never block.
|
|
124
|
+
// Реальный typecheck: аудит ловит "сборка сломана", даже если все файлы на
|
|
125
|
+
// месте (было: удаление .js оставило .tsx-исходники с 11 tsc-ошибками, а
|
|
126
|
+
// агент заявил успех). Гоняется, только если tsconfig.json достижим от
|
|
127
|
+
// baseDir или резолвленных файлов шагов И файловые проверки уже прошли —
|
|
128
|
+
// обречённому на missing/leftover аудиту 90s tsc-спавн не платим.
|
|
129
|
+
// Inconclusive-результаты не блокируют.
|
|
126
130
|
let typecheckError = null;
|
|
127
131
|
if (missingFiles.length === 0 && leftoverFiles.length === 0) {
|
|
128
132
|
const root = findTypecheckRoot(this.baseDir, existingFiles);
|
|
@@ -136,11 +140,11 @@ export class Auditor {
|
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
const doneSteps = plan.steps.filter((s) => s.status === "done").length;
|
|
139
|
-
// done + skipped
|
|
140
|
-
//
|
|
141
|
-
// deliverable
|
|
142
|
-
//
|
|
143
|
-
//
|
|
143
|
+
// done + skipped — оба осознанные терминальные состояния: `done` заявляет
|
|
144
|
+
// работу, `skipped` документирует решение, что шаг не нужен (задокументированная
|
|
145
|
+
// лазейка deliverable-gate). Ни то, ни другое — не ложь для аудита: план с
|
|
146
|
+
// легитимно скипнутым шагом обязан проходить (было: скипнутый шаг 1 вечно
|
|
147
|
+
// держал задачу реджектнутой).
|
|
144
148
|
const terminalSteps = plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
|
|
145
149
|
const totalSteps = plan.steps.length;
|
|
146
150
|
const auditedFiles = [...new Set([...createFiles, ...deleteFiles])];
|
|
@@ -153,10 +157,10 @@ export class Auditor {
|
|
|
153
157
|
const testsFailing = testRun !== null && !testRun.passed;
|
|
154
158
|
const typecheckFailing = typecheckError !== null;
|
|
155
159
|
const passed = missingFiles.length === 0 && leftoverFiles.length === 0 && !testsFailing && !typecheckFailing;
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
+
// Сводка "Task complete ✓" для незаконченного плана — ложь, которую модель
|
|
161
|
+
// читает как зелёный свет (было: audit-gate инжектил "[✓] Task complete:
|
|
162
|
+
// 1/3 steps done" при 2 пендинг шагах). Сводка заявляет завершение, только
|
|
163
|
+
// если реально done каждый шаг.
|
|
160
164
|
const stepsPending = terminalSteps < totalSteps;
|
|
161
165
|
let summary;
|
|
162
166
|
if (missingFiles.length > 0) {
|