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.
Files changed (359) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/assets/app.js +22 -4
  3. package/dist/assets/index.html +29 -0
  4. package/dist/assets/js/dom.js +2 -1
  5. package/dist/assets/js/feed.js +38 -26
  6. package/dist/assets/js/format.js +1 -1
  7. package/dist/assets/js/rail.js +114 -0
  8. package/dist/assets/js/settings-controls.js +2 -2
  9. package/dist/assets/js/settings.js +246 -51
  10. package/dist/assets/js/store.js +3 -0
  11. package/dist/assets/js/tabs.js +104 -2
  12. package/dist/assets/styles.css +215 -66
  13. package/dist/cli/cache-line.js +1 -0
  14. package/dist/cli/command-suggest.js +5 -4
  15. package/dist/cli/commands.js +231 -122
  16. package/dist/cli/completer.js +132 -31
  17. package/dist/cli/index.js +1 -0
  18. package/dist/cli/json-payload.js +4 -4
  19. package/dist/cli/main.js +166 -61
  20. package/dist/cli/onboarding-commands.js +126 -0
  21. package/dist/cli/plugin-commands.js +2 -1
  22. package/dist/cli/relaunch.js +16 -13
  23. package/dist/cli/repl-commands-lsp.js +75 -0
  24. package/dist/cli/repl-commands-skills.js +105 -0
  25. package/dist/cli/repl-commands.js +589 -266
  26. package/dist/cli/repl.js +436 -177
  27. package/dist/cli/run-result.js +5 -6
  28. package/dist/cli/security-commands.js +37 -28
  29. package/dist/cli/setup-order.js +4 -55
  30. package/dist/cli/setup-prompt.js +3 -2
  31. package/dist/cli/setup.js +27 -107
  32. package/dist/cli/web-command.js +98 -0
  33. package/dist/config/budget.js +5 -5
  34. package/dist/config/config.js +103 -96
  35. package/dist/config/defaults.js +38 -8
  36. package/dist/config/domains.js +24 -27
  37. package/dist/config/env.js +46 -0
  38. package/dist/config/experts.js +2 -0
  39. package/dist/config/index.js +1 -0
  40. package/dist/config/paths.js +56 -0
  41. package/dist/config/redact.js +44 -0
  42. package/dist/config/registry-extra.js +334 -0
  43. package/dist/config/registry.js +584 -0
  44. package/dist/config/security.js +35 -36
  45. package/dist/config/timeouts.js +74 -0
  46. package/dist/config/utils.js +16 -15
  47. package/dist/core/agent/artifact-progress.js +59 -0
  48. package/dist/core/agent/artifact-resolve.js +79 -0
  49. package/dist/core/agent/audit-gate.js +7 -3
  50. package/dist/core/agent/compaction.js +2 -2
  51. package/dist/core/agent/constants.js +33 -4
  52. package/dist/core/agent/context-log.js +454 -0
  53. package/dist/core/agent/context-renderer.js +5 -0
  54. package/dist/core/agent/critic-gate.js +132 -0
  55. package/dist/core/agent/hallucination-gate.js +148 -38
  56. package/dist/core/agent/loop-state.js +15 -0
  57. package/dist/core/agent/prefix-monitor.js +1 -1
  58. package/dist/core/agent/reasoning-resolver.js +10 -1
  59. package/dist/core/agent/token-tracker.js +7 -4
  60. package/dist/core/agent/tool-batch.js +93 -18
  61. package/dist/core/agent/tool-images.js +18 -0
  62. package/dist/core/agent/tool-output.js +65 -11
  63. package/dist/core/agent/tool-pairing.js +60 -0
  64. package/dist/core/agent-moe.js +25 -24
  65. package/dist/core/agent.js +429 -108
  66. package/dist/core/bootstrap/agent-phase.js +66 -0
  67. package/dist/core/bootstrap/config-phase.js +86 -0
  68. package/dist/core/bootstrap/modules-phase.js +269 -0
  69. package/dist/core/bootstrap/provider-phase.js +183 -0
  70. package/dist/core/bootstrap/session-phase.js +204 -0
  71. package/dist/core/bootstrap/state.js +94 -0
  72. package/dist/core/bootstrap/workspace-phase.js +115 -0
  73. package/dist/core/bootstrap.js +92 -660
  74. package/dist/core/crash-handler.js +17 -7
  75. package/dist/core/environment.js +37 -12
  76. package/dist/core/host-plan.js +43 -0
  77. package/dist/core/host.js +23 -0
  78. package/dist/core/index.js +1 -0
  79. package/dist/core/prompt-builder.js +17 -1
  80. package/dist/core/prompt-overflow.js +42 -43
  81. package/dist/core/session-logger.js +122 -30
  82. package/dist/core/system-prompt.js +117 -0
  83. package/dist/core/version.js +6 -4
  84. package/dist/core/web-host.js +34 -0
  85. package/dist/core/workspace.js +19 -15
  86. package/dist/i18n/en.json +106 -21
  87. package/dist/i18n/index.js +37 -3
  88. package/dist/i18n/ru.json +109 -21
  89. package/dist/index.js +7 -3
  90. package/dist/llm/image-utils.js +55 -64
  91. package/dist/llm/llm-errors.js +9 -0
  92. package/dist/llm/model-loader.js +10 -32
  93. package/dist/llm/models-dev.js +121 -0
  94. package/dist/llm/openai-compat.js +146 -73
  95. package/dist/llm/orchestrator.js +33 -11
  96. package/dist/llm/provider-budget.js +2 -2
  97. package/dist/llm/provider-context.js +233 -0
  98. package/dist/llm/provider.js +1 -1
  99. package/dist/llm/response.js +5 -0
  100. package/dist/llm/token-counter.js +27 -11
  101. package/dist/logger/app-logger.js +48 -22
  102. package/dist/logger/file-log.js +28 -19
  103. package/dist/logger/index.js +1 -0
  104. package/dist/main.js +24285 -22447
  105. package/dist/migration/backup.js +14 -3
  106. package/dist/migration/detect.js +12 -3
  107. package/dist/migration/index.js +1 -0
  108. package/dist/modules/artifacts/store.js +9 -8
  109. package/dist/modules/browser/actions.js +6 -0
  110. package/dist/modules/browser/bridge-client.js +14 -12
  111. package/dist/modules/browser/bridge-path.js +4 -4
  112. package/dist/modules/browser/console-buffer.js +37 -0
  113. package/dist/modules/browser/cookie-store.js +5 -0
  114. package/dist/modules/browser/driver-types.js +1 -0
  115. package/dist/modules/browser/driver.js +19 -11
  116. package/dist/modules/browser/index.js +3 -1
  117. package/dist/modules/browser/module.js +2 -1
  118. package/dist/modules/browser/session.js +10 -30
  119. package/dist/modules/browser/snapshot.js +19 -13
  120. package/dist/modules/browser/types.js +1 -0
  121. package/dist/modules/certification/cli.js +28 -20
  122. package/dist/modules/certification/fact-checker.js +9 -1
  123. package/dist/modules/certification/loader.js +9 -2
  124. package/dist/modules/certification/manifest.js +83 -21
  125. package/dist/modules/certification/runner.js +23 -13
  126. package/dist/modules/certification/scenarios.js +20 -20
  127. package/dist/modules/certification/syntax-scenarios.js +1 -2
  128. package/dist/modules/context/chunk-query.js +11 -9
  129. package/dist/modules/context/fact-extractor.js +37 -39
  130. package/dist/modules/context/index.js +1 -0
  131. package/dist/modules/context/manager.js +106 -80
  132. package/dist/modules/critic/client.js +176 -0
  133. package/dist/modules/critic/evidence.js +81 -0
  134. package/dist/modules/critic/provider.js +45 -0
  135. package/dist/modules/critic/tools.js +78 -0
  136. package/dist/modules/critic/types.js +1 -0
  137. package/dist/modules/execution/audit-runners.js +28 -34
  138. package/dist/modules/execution/auditor.js +53 -49
  139. package/dist/modules/execution/execution-plugin.js +142 -102
  140. package/dist/modules/execution/index.js +1 -1
  141. package/dist/modules/execution/input-from.js +37 -13
  142. package/dist/modules/execution/module.js +368 -191
  143. package/dist/modules/execution/moe-executor.js +53 -26
  144. package/dist/modules/execution/plan-actions.js +320 -202
  145. package/dist/modules/execution/plan-checks.js +35 -0
  146. package/dist/modules/execution/plan-coverage.js +36 -33
  147. package/dist/modules/execution/plan-steps.js +11 -3
  148. package/dist/modules/execution/plan-store.js +83 -17
  149. package/dist/modules/execution/plan-tool.js +89 -35
  150. package/dist/modules/execution/plan-validator.js +30 -10
  151. package/dist/modules/execution/planner.js +22 -13
  152. package/dist/modules/execution/stuck-detector.js +196 -93
  153. package/dist/modules/execution/tracker.js +22 -8
  154. package/dist/modules/execution/verifier.js +69 -48
  155. package/dist/modules/execution/windows-commands.js +12 -12
  156. package/dist/modules/hallucination/artifact-claim.js +93 -0
  157. package/dist/modules/hallucination/confidence.js +13 -11
  158. package/dist/modules/hallucination/consistency.js +10 -2
  159. package/dist/modules/hallucination/detector.js +10 -5
  160. package/dist/modules/hallucination/factual.js +43 -26
  161. package/dist/modules/hallucination/index.js +1 -0
  162. package/dist/modules/hallucination/js-identifiers.js +44 -45
  163. package/dist/modules/hallucination/llm-judge.js +17 -12
  164. package/dist/modules/hallucination/success-claim.js +59 -0
  165. package/dist/modules/indexer/cache.js +52 -4
  166. package/dist/modules/indexer/index.js +1 -0
  167. package/dist/modules/indexer/map-command.js +2 -4
  168. package/dist/modules/indexer/map-select.js +16 -16
  169. package/dist/modules/indexer/module.js +47 -12
  170. package/dist/modules/indexer/project-profile.js +8 -7
  171. package/dist/modules/indexer/symbols.js +15 -15
  172. package/dist/modules/indexer/walker.js +159 -29
  173. package/dist/modules/lsp/check-tool.js +7 -5
  174. package/dist/modules/lsp/client.js +39 -39
  175. package/dist/modules/lsp/command.js +17 -24
  176. package/dist/modules/lsp/config.js +12 -11
  177. package/dist/modules/lsp/index.js +1 -0
  178. package/dist/modules/lsp/module.js +48 -46
  179. package/dist/modules/lsp/probe.js +13 -14
  180. package/dist/modules/lsp/project-root.js +14 -17
  181. package/dist/modules/lsp/startup-check.js +18 -16
  182. package/dist/modules/mcp/client.js +59 -62
  183. package/dist/modules/mcp/index.js +1 -0
  184. package/dist/modules/mcp/module.js +66 -37
  185. package/dist/modules/mcp/registry.js +5 -0
  186. package/dist/modules/memory/index.js +1 -0
  187. package/dist/modules/memory/module.js +12 -8
  188. package/dist/modules/memory/search.js +4 -1
  189. package/dist/modules/memory/store.js +40 -2
  190. package/dist/modules/onboarding/init.js +47 -0
  191. package/dist/modules/onboarding/meet.js +30 -0
  192. package/dist/modules/onboarding/mode.js +30 -0
  193. package/dist/modules/onboarding/prompts.js +43 -0
  194. package/dist/modules/pipelines/engine.js +10 -0
  195. package/dist/modules/pipelines/index.js +1 -0
  196. package/dist/modules/pipelines/parser.js +4 -1
  197. package/dist/modules/pipelines/template.js +4 -0
  198. package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
  199. package/dist/modules/plugins/builtin/notify.js +2 -0
  200. package/dist/modules/plugins/index.js +1 -0
  201. package/dist/modules/plugins/loader.js +18 -3
  202. package/dist/modules/plugins/manager.js +37 -17
  203. package/dist/modules/pricing/index.js +10 -11
  204. package/dist/modules/pricing/prices.js +12 -12
  205. package/dist/modules/processes/index.js +1 -0
  206. package/dist/modules/processes/kill-tree.js +27 -31
  207. package/dist/modules/processes/registry.js +44 -25
  208. package/dist/modules/processes/runner.js +4 -4
  209. package/dist/modules/providers/capabilities.js +28 -0
  210. package/dist/modules/providers/create.js +3 -4
  211. package/dist/modules/providers/factory.js +7 -7
  212. package/dist/modules/providers/fallback.js +12 -12
  213. package/dist/modules/providers/health.js +4 -4
  214. package/dist/modules/providers/manager.js +50 -19
  215. package/dist/modules/providers/naming.js +15 -0
  216. package/dist/modules/providers/preflight.js +71 -0
  217. package/dist/modules/providers/presets.js +39 -8
  218. package/dist/modules/providers/registry.js +7 -2
  219. package/dist/modules/reasoning/policy.js +8 -6
  220. package/dist/modules/reasoning/probe.js +30 -25
  221. package/dist/modules/security/audit-log.js +19 -36
  222. package/dist/modules/security/audit-notifier.js +35 -75
  223. package/dist/modules/security/command-validator.js +99 -51
  224. package/dist/modules/security/content-scanner.js +8 -8
  225. package/dist/modules/security/data-sanitizer.js +9 -16
  226. package/dist/modules/security/encryption.js +32 -52
  227. package/dist/modules/security/index.js +2 -1
  228. package/dist/modules/security/network-validator.js +120 -43
  229. package/dist/modules/security/path-validator.js +61 -46
  230. package/dist/modules/security/protected-paths.js +73 -0
  231. package/dist/modules/security/rate-limiter.js +17 -49
  232. package/dist/modules/security/security-policies.js +25 -57
  233. package/dist/modules/security/session-encryption.js +32 -71
  234. package/dist/modules/security/session-isolation.js +15 -28
  235. package/dist/modules/session/auto-name.js +63 -0
  236. package/dist/modules/session/export.js +98 -0
  237. package/dist/modules/session/index.js +1 -0
  238. package/dist/modules/session/manager.js +79 -28
  239. package/dist/modules/session/module.js +4 -3
  240. package/dist/modules/session/store.js +57 -20
  241. package/dist/modules/setup/answers.js +35 -0
  242. package/dist/modules/setup/apply.js +79 -0
  243. package/dist/modules/setup/discovery.js +72 -0
  244. package/dist/modules/skills/index.js +1 -0
  245. package/dist/modules/skills/loader.js +9 -1
  246. package/dist/modules/skills/module.js +11 -0
  247. package/dist/modules/updater/changelog-reader.js +15 -16
  248. package/dist/modules/updater/checker.js +9 -4
  249. package/dist/modules/updater/dev-detect.js +7 -6
  250. package/dist/modules/updater/index.js +1 -0
  251. package/dist/modules/updater/module.js +17 -15
  252. package/dist/modules/user-profile/compressor.js +2 -0
  253. package/dist/modules/user-profile/index.js +1 -0
  254. package/dist/modules/user-profile/profile.js +10 -1
  255. package/dist/modules/webui/answer-api.js +29 -0
  256. package/dist/modules/webui/assets.js +50 -0
  257. package/dist/modules/webui/body.js +21 -0
  258. package/dist/modules/webui/commands-settings.js +209 -0
  259. package/dist/modules/webui/commands-shared.js +60 -0
  260. package/dist/modules/webui/commands.js +174 -0
  261. package/dist/modules/webui/config-api.js +134 -0
  262. package/dist/modules/webui/file-api.js +202 -0
  263. package/dist/modules/webui/forward.js +159 -0
  264. package/dist/modules/webui/history-api.js +112 -0
  265. package/dist/modules/webui/host-state.js +60 -0
  266. package/dist/modules/webui/i18n-api.js +9 -0
  267. package/dist/modules/webui/limits.js +6 -0
  268. package/dist/modules/webui/log-forward.js +20 -0
  269. package/dist/modules/webui/memory-commands.js +64 -0
  270. package/dist/modules/webui/model-cache.js +18 -0
  271. package/dist/modules/webui/module.js +148 -0
  272. package/dist/modules/webui/persist.js +24 -0
  273. package/dist/modules/webui/prompt-io.js +97 -0
  274. package/dist/modules/webui/providers-api.js +337 -0
  275. package/dist/modules/webui/providers-view.js +56 -0
  276. package/dist/modules/webui/reasoning-view.js +15 -0
  277. package/dist/modules/webui/run-agent.js +81 -0
  278. package/dist/modules/webui/server.js +284 -0
  279. package/dist/modules/webui/sessions-api.js +178 -0
  280. package/dist/modules/webui/settings-api.js +561 -0
  281. package/dist/modules/webui/setup-api.js +201 -0
  282. package/dist/modules/webui/state.js +74 -0
  283. package/dist/modules/webui/util.js +261 -0
  284. package/dist/output/bus.js +7 -2
  285. package/dist/output/channel.js +80 -28
  286. package/dist/output/format.js +2 -1
  287. package/dist/output/index.js +1 -0
  288. package/dist/output/json-sink.js +5 -3
  289. package/dist/output/machine.js +4 -3
  290. package/dist/output/session-sink.js +4 -4
  291. package/dist/tools/approve.js +4 -0
  292. package/dist/tools/attach-image.js +5 -4
  293. package/dist/tools/auto-fixer.js +26 -44
  294. package/dist/tools/auto-tags.js +83 -0
  295. package/dist/tools/bash.js +193 -116
  296. package/dist/tools/browser.js +76 -31
  297. package/dist/tools/chunk-query.js +14 -6
  298. package/dist/tools/create-dir.js +9 -5
  299. package/dist/tools/delete-file.js +9 -5
  300. package/dist/tools/download-file.js +32 -7
  301. package/dist/tools/edit-file.js +52 -14
  302. package/dist/tools/enable-tools.js +6 -6
  303. package/dist/tools/executor.js +132 -27
  304. package/dist/tools/file-info.js +3 -1
  305. package/dist/tools/filter-tools.js +3 -3
  306. package/dist/tools/glob-tool.js +4 -1
  307. package/dist/tools/grep-tool.js +13 -10
  308. package/dist/tools/hidden-tools-block.js +6 -6
  309. package/dist/tools/index.js +16 -8
  310. package/dist/tools/list-dir.js +4 -2
  311. package/dist/tools/load-skill.js +7 -0
  312. package/dist/tools/mcp-call.js +15 -4
  313. package/dist/tools/meet-done.js +14 -0
  314. package/dist/tools/move-file.js +14 -7
  315. package/dist/tools/path-utils.js +29 -29
  316. package/dist/tools/pipeline-run.js +29 -7
  317. package/dist/tools/preview.js +1 -1
  318. package/dist/tools/process-ids.js +31 -0
  319. package/dist/tools/process-kill.js +9 -16
  320. package/dist/tools/process-list.js +1 -0
  321. package/dist/tools/process-log.js +11 -16
  322. package/dist/tools/process-wait.js +63 -0
  323. package/dist/tools/prompt-io.js +4 -3
  324. package/dist/tools/question.js +172 -25
  325. package/dist/tools/read-file.js +55 -34
  326. package/dist/tools/recall.js +14 -8
  327. package/dist/tools/registry.js +6 -5
  328. package/dist/tools/remember.js +8 -4
  329. package/dist/tools/scope-request.js +11 -6
  330. package/dist/tools/search-history.js +5 -2
  331. package/dist/tools/session-info.js +2 -0
  332. package/dist/tools/set-thinking.js +8 -4
  333. package/dist/tools/subagent.js +59 -39
  334. package/dist/tools/syntax-validator.js +292 -90
  335. package/dist/tools/user-input.js +22 -18
  336. package/dist/tools/web-browse.js +9 -3
  337. package/dist/tools/web-fetch.js +12 -23
  338. package/dist/tools/web-search.js +7 -5
  339. package/dist/tools/write-file.js +45 -15
  340. package/dist/ui/action-log.js +13 -0
  341. package/dist/ui/box.js +4 -3
  342. package/dist/ui/colors.js +2 -0
  343. package/dist/ui/diff.js +6 -5
  344. package/dist/ui/index.js +1 -0
  345. package/dist/ui/line-editor.js +230 -122
  346. package/dist/ui/line-math.js +10 -2
  347. package/dist/ui/md-formatter.js +57 -37
  348. package/dist/ui/output.js +8 -3
  349. package/dist/ui/plan-view.js +21 -10
  350. package/dist/ui/renderer.js +393 -67
  351. package/dist/ui/spinner.js +16 -5
  352. package/dist/ui/table.js +15 -17
  353. package/dist/ui/theme.js +143 -0
  354. package/dist/utils/child-run.js +20 -0
  355. package/dist/utils/error.js +1 -1
  356. package/dist/utils/index.js +2 -0
  357. package/dist/utils/retry.js +2 -2
  358. package/dist/utils/truncate.js +1 -1
  359. 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
- /** Directories never searched for test files. */
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
- * Resolve the project's test command from its manifest, so the final audit
23
- * actually runs the runner the project uses instead of always assuming
24
- * `bun test` (a Python or Go project, or a vitest/jest setup, would fail or
25
- * silently produce no summary under bun test).
22
+ * Резолвит тестовую команду проекта из манифеста: финальный аудит реально
23
+ * гоняет раннер проекта, а не всегда `bun test` (Python/Go-проект или
24
+ * vitest/jest-сетап под bun test упал бы или молча не дал сводки).
26
25
  *
27
- * Priority: package.json scripts.test → test-framework config files → Python
28
- * markers (pyproject/pytest.ini/conftest.py) → go.mod → Cargo.toml → bun.
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
- /* invalid package.json — fall through */
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
- /** Recursive, depth-limited walk that stops early on the first test file. */
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
- /** The plan's step descriptions mention running/checking tests. */
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
- // Timed out — we cannot claim success or failure from an unfinished run.
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
- // No recognizable runner summary. A non-zero exit means the test run
135
- // (or its runner) failed — the audit must not pass on it; report a
136
- // single "at least one failed" since we cannot count them. Exit 0
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
- /** First `error TS…` line in a tsc output, or null when none/inconclusive. */
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
- * Locate the nearest project root that owns a tsconfig.json.
160
+ * Находит ближайший корень проекта с tsconfig.json.
166
161
  *
167
- * The agent frequently creates the project in a nested subfolder of its
168
- * baseDir (e.g. `bicycle-shop/`), so the tsconfig lives at
169
- * `baseDir/bicycle-shop/` and the old `existsSync(baseDir/tsconfig.json)`
170
- * gate silently skipped the final typecheck — the audit passed even with
171
- * dozens of tsc errors (observed in both analyzed sessions). This walks each
172
- * candidate (baseDir plus the resolved step files) upward to the nearest
173
- * tsconfig.json, preferring the shallowest hit so the baseDir project wins
174
- * over a stray tsconfig further up. Returns null when no tsconfig exists in
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
- // A timeout, or output with no `error TS` lines (missing typescript / wrong
208
- // working dir) is inconclusive — never block completion on those.
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, hasTestStep, runTests, runTypecheck, } from "./audit-runners";
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
- /** Subtree walk depth cap for file resolution. */
10
+ /** Кап глубины обхода поддерева для резолва файлов. */
10
11
  const RESOLVE_MAX_DEPTH = 8;
11
12
  /**
12
- * Resolve a plan-step file token to an existing file.
13
+ * Резолвит файловый токен шага плана в существующий файл.
13
14
  *
14
- * A step description frequently names a file by its bare basename
15
- * ("App.tsx") or a partially-prefixed path ("src/App.tsx") while the real
16
- * file lives in a nested project directory relative to the agent baseDir
17
- * (e.g. `bicycle-shop/src/App.tsx`). Naively resolving against baseDir
18
- * reports the file as missing — observed in session ses_mssl5izw where the
19
- * final audit gate rejected a fully-completed task with "1 files missing"
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
- * Strategy: exact resolve first, then a bounded subtree walk matching either
23
- * the exact basename or a path suffix (so both "App.tsx" and
24
- * "src/App.tsx" find "bicycle-shop/src/App.tsx").
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("/" + suffix)) {
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
- // Only steps the model claims completed may require their named files to
71
- // exist. Skipped/pending steps must never contribute "missing files"
72
- // (observed: a skipped step's prose mention of "type.ts" kept the audit
73
- // gate rejecting a finished task with "1 files missing" forever).
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
- // File tokens are extracted with the SAME shared filter as the
79
- // hallucination factual check (js-identifiers.ts). URLs are stripped
80
- // first so a hostname / https://host/repo.git in a step description
81
- // can never be reported as a missing file (observed: a git-remote
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
- // Keep the resolved absolute path: findTypecheckRoot walks up
92
- // from it to the nearest tsconfig, and a relative token would
93
- // resolve against the process cwd instead of baseDir.
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
- // A done delete-kind step requires its named files to be GONE.
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
- // Real test run: when the plan includes a verification step, do not
107
- // trust the model's word (or exit codes) — actually execute the tests
108
- // and check the runner summary. This catches the "all 7 passing ✅"
109
- // final answers that hide 3 failing tests.
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 (hasTestStep(plan) && findTestFile(this.baseDir)) {
116
+ if (findTestFile(this.baseDir)) {
112
117
  try {
113
118
  testRun = await runTests(this.baseDir);
114
119
  }
115
120
  catch {
116
- testRun = null; // infra failure — don't block on it
121
+ testRun = null; // сбой инфры — на нём не блокируемся
117
122
  }
118
123
  }
119
- // Real typecheck: the audit must catch "the build is broken" even when
120
- // every file exists (observed: deleting .js files left the .tsx sources
121
- // with 11 tsc errors and the agent still declared success). Runs only
122
- // when a tsconfig.json is reachable from baseDir or the resolved step
123
- // files AND the file checks already passed — an audit that is doomed by
124
- // missing/leftover files must not pay a 90s tsc spawn. Inconclusive
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 are both deliberate terminal states: `done` claims the
140
- // work, `skipped` documents a decision that the step is not needed (the
141
- // deliverable gate's documented escape hatch). Neither is a lie the
142
- // audit must catch — a plan with a legitimately skipped step must be
143
- // able to pass (observed: skipped step 1 kept the task rejected forever).
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
- // A "Task complete ✓" summary for an unfinished plan is a lie the model
157
- // reads as a green light (observed: audit gate injected "[✓] Task
158
- // complete: 1/3 steps done" while the plan had 2 pending steps). The
159
- // summary must only claim completion when every step is actually done.
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) {