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
@@ -1,36 +1,48 @@
1
+ // Трекер плана: указатель текущего шага, статусы, прогресс-строка.
2
+ // Двигает указатель мимо уже settled (skipped/done) шагов, не мутируя их —
3
+ // перезапись "skipped" в "in_progress" дедлочит план (модель не возьмётся за
4
+ // шаг, который осознанно пропустила).
1
5
  import { PlanCreator, getPlanProgress } from "./planner";
6
+ /** Трекер плана: указатель, статусы шагов, прогресс. */
2
7
  export class PlanTracker {
3
8
  plan;
4
9
  currentStepIndex = 0;
5
10
  constructor(plan) {
6
11
  this.plan = plan;
7
12
  }
13
+ /** План целиком. */
8
14
  getPlan() {
9
15
  return this.plan;
10
16
  }
17
+ /** Индекс текущего шага. */
11
18
  getCurrentStepIndex() {
12
19
  return this.currentStepIndex;
13
20
  }
21
+ /** Текущий шаг (по указателю). */
14
22
  getCurrentStep() {
15
23
  return this.plan.steps[this.currentStepIndex];
16
24
  }
25
+ /** Шаг по id. */
17
26
  getStep(id) {
18
27
  return this.plan.steps.find((s) => s.id === id);
19
28
  }
29
+ /** Ставит статус шагу. */
20
30
  updateStepStatus(id, status) {
21
31
  const step = this.getStep(id);
22
32
  if (step)
23
33
  step.status = status;
24
34
  }
35
+ /** Добавляет заметку к шагу. */
25
36
  addNote(id, note) {
26
37
  const step = this.getStep(id);
27
38
  if (step)
28
39
  step.note = note;
29
40
  }
41
+ /**
42
+ * Двигает указатель к следующему незакрытому шагу (мимо done/skipped без
43
+ * мутаций). False, если шагов не осталось.
44
+ */
30
45
  advance() {
31
- // Move past any already-settled (skipped/done) steps without mutating
32
- // them — overwriting a "skipped" step with "in_progress" deadlocks the
33
- // plan (the model will never work on a step it deliberately skipped).
34
46
  let idx = this.currentStepIndex + 1;
35
47
  while (idx < this.plan.steps.length &&
36
48
  (this.plan.steps[idx].status === "skipped" || this.plan.steps[idx].status === "done")) {
@@ -45,11 +57,10 @@ export class PlanTracker {
45
57
  return true;
46
58
  }
47
59
  /**
48
- * Advance the pointer past any done/skipped steps without mutating their
49
- * status. Keeps currentStepIndex in sync when the model marks steps done via
50
- * the `plan update` tool (which never calls advance()). Without this,
51
- * getCurrentStep() stays on the first step forever and the stuck detector
52
- * accumulates false-positive "no progress" warnings.
60
+ * Подтягивает указатель мимо done/skipped шагов без смены статусов.
61
+ * Синхронизирует currentStepIndex, когда модель маркит шаги через тул
62
+ * `plan update` (он advance() не зовёт). Без этого getCurrentStep() вечно
63
+ * стоит на первом шаге и stuck-детектор копит ложные "no progress".
53
64
  */
54
65
  syncCurrentStep() {
55
66
  while (this.currentStepIndex < this.plan.steps.length - 1 &&
@@ -58,9 +69,11 @@ export class PlanTracker {
58
69
  this.currentStepIndex++;
59
70
  }
60
71
  }
72
+ /** True, если все шаги done/skipped. */
61
73
  isComplete() {
62
74
  return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
63
75
  }
76
+ /** Строка прогресса: `[id] title settled/total (skipped) bar pct`. */
64
77
  getProgressString() {
65
78
  const p = getPlanProgress(this.plan);
66
79
  const total = p.total;
@@ -72,6 +85,7 @@ export class PlanTracker {
72
85
  const skippedPart = p.skipped > 0 ? ` (${p.skipped} skipped)` : "";
73
86
  return `[${this.plan.id}] ${this.plan.title} ${settled}/${total}${skippedPart} ${bar} ${pct}%`;
74
87
  }
88
+ /** Промпт-блок плана с текущей позиции указателя. */
75
89
  toPromptBlock() {
76
90
  return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
77
91
  }
@@ -1,3 +1,4 @@
1
+ // Верификатор шагов: файлы, синтаксис, скрипты, typecheck, success_criteria.
1
2
  import { existsSync, readFileSync } from "fs";
2
3
  import { resolve, extname, dirname, join } from "path";
3
4
  import { spawn } from "child_process";
@@ -6,19 +7,27 @@ import { validateExpertConfig } from "../../config/experts";
6
7
  import { findProjectRoot, resolveTscCommand } from "../lsp/project-root";
7
8
  import { extractFileLikeTokens, stripUrls } from "../hallucination/js-identifiers";
8
9
  import { findExistingFile } from "./auditor";
10
+ import { findTypecheckRoot } from "./audit-runners";
11
+ import { killTree } from "../processes/kill-tree";
12
+ import { settleChildClose } from "../../utils/child-run";
13
+ /** Верификатор шагов и MoE-планов: файлы, синтаксис, скрипты, tsc, критерии. */
9
14
  export class StepVerifier {
10
15
  baseDir;
11
16
  constructor(baseDir) {
12
17
  this.baseDir = baseDir;
13
18
  }
19
+ /** Проверяет существование файла (с nested-резолвером findExistingFile). */
14
20
  async checkFileExists(path) {
15
- const resolved = resolve(this.baseDir, path);
16
- const exists = existsSync(resolved);
21
+ // Nested-aware (тот же резолвер, что в verifyStep): проект в
22
+ // `baseDir/proj/` нельзя репортить пропавшим, если файл ниже.
23
+ const resolved = findExistingFile(this.baseDir, path);
24
+ const exists = !!resolved;
17
25
  return {
18
26
  passed: exists,
19
27
  message: exists ? t("verify.file_exists", { path }) : t("verify.file_not_found", { path }),
20
28
  };
21
29
  }
30
+ /** Запускает `bun run <script>` (60s), возвращает пункт проверки. */
22
31
  async runScript(scriptName) {
23
32
  try {
24
33
  await this.runAsync(`bun run ${scriptName}`, this.baseDir, 60_000);
@@ -31,75 +40,88 @@ export class StepVerifier {
31
40
  };
32
41
  }
33
42
  }
34
- async runTypeCheck() {
35
- const tsconfigPath = resolve(this.baseDir, "tsconfig.json");
43
+ /** Проектный typecheck (tsc --noEmit); без tsconfig/tsc — пропуск, не фейл. */
44
+ async runTypeCheck(existingFiles = []) {
45
+ // Корень проекта резолвим и по артефактам: проект во вложенном
46
+ // `baseDir/proj/` надо типчекать, а не скипать ("No tsconfig.json found") —
47
+ // старый поиск только от baseDir его не видел.
48
+ const root = findTypecheckRoot(this.baseDir, existingFiles) ?? this.baseDir;
49
+ const tsconfigPath = join(root, "tsconfig.json");
36
50
  if (!existsSync(tsconfigPath)) {
37
- return { passed: true, message: "No tsconfig.json found — skipping type check" };
51
+ return { passed: true, message: t("verify.typecheck_skipped_no_tsconfig") };
38
52
  }
39
- const tsc = resolveTscCommand(this.baseDir);
53
+ const tsc = resolveTscCommand(root);
40
54
  if (!tsc)
41
- return { passed: true, message: "tsc not installed — skipping type check" };
55
+ return { passed: true, message: t("verify.typecheck_skipped_no_tsc") };
42
56
  try {
43
- await this.runAsync(`${tsc} --noEmit`, this.baseDir, 60_000);
44
- return { passed: true, message: "TypeScript type check passed" };
57
+ await this.runAsync(`${tsc} --noEmit`, root, 60_000);
58
+ return { passed: true, message: t("verify.typecheck_passed") };
45
59
  }
46
60
  catch (e) {
47
61
  const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
48
- return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
62
+ return {
63
+ passed: false,
64
+ message: t("verify.typecheck_failed", { error: stderr.slice(0, 500) }),
65
+ };
49
66
  }
50
67
  }
51
68
  /**
52
- * Type-check a specific file, resolving the project root from the FILE's
53
- * location (not the agent baseDir). For nested layouts (`baseDir/proj/` with
54
- * its own tsconfig.json) the old baseDir-based check silently skipped every
55
- * type error (observed in ses_mst2r0r5: zero type feedback for the whole
56
- * session). Markers mirror the TypeScript LSP server config.
69
+ * Типчекает конкретный файл, резолвя корень проекта от РАСПОЛОЖЕНИЯ ФАЙЛА
70
+ * (не от baseDir агента). Для вложенных раскладок (`baseDir/proj/` со своим
71
+ * tsconfig.json) старая проверка от baseDir молча скипала каждую ошибку типов
72
+ * (было в ses_mst2r0r5: ноль type feedback за всю сессию). Маркеры зеркалят
73
+ * конфиг TypeScript LSP-сервера.
57
74
  */
58
75
  async runTypeCheckForFile(filePath) {
59
76
  const projectRoot = findProjectRoot(filePath, this.baseDir, ["tsconfig.json", "package.json"]);
60
77
  const tsconfigPath = join(projectRoot, "tsconfig.json");
61
78
  if (!existsSync(tsconfigPath)) {
62
- return { passed: true, message: "No tsconfig.json found — skipping type check" };
79
+ return { passed: true, message: t("verify.typecheck_skipped_no_tsconfig") };
63
80
  }
64
81
  const tsc = resolveTscCommand(projectRoot);
65
82
  if (!tsc)
66
- return { passed: true, message: "tsc not installed — skipping type check" };
83
+ return { passed: true, message: t("verify.typecheck_skipped_no_tsc") };
67
84
  try {
68
85
  await this.runAsync(`${tsc} --noEmit --skipLibCheck`, projectRoot, 60_000);
69
- return { passed: true, message: "TypeScript type check passed" };
86
+ return { passed: true, message: t("verify.typecheck_passed") };
70
87
  }
71
88
  catch (e) {
72
89
  const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
73
- return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
90
+ return {
91
+ passed: false,
92
+ message: t("verify.typecheck_failed", { error: stderr.slice(0, 500) }),
93
+ };
74
94
  }
75
95
  }
96
+ /** Запускает `test`-скрипт проекта; без package.json/скрипта — пропуск. */
76
97
  async runTests() {
77
98
  const pkgPath = resolve(this.baseDir, "package.json");
78
99
  if (!existsSync(pkgPath)) {
79
- return { passed: true, message: "No package.json found — skipping tests" };
100
+ return { passed: true, message: t("verify.tests_skipped_no_package") };
80
101
  }
81
102
  try {
82
103
  const pkg = JSON.parse(require("fs").readFileSync(pkgPath, "utf-8"));
83
104
  if (!pkg.scripts?.test) {
84
- return { passed: true, message: "No test script defined — skipping tests" };
105
+ return { passed: true, message: t("verify.tests_skipped_no_script") };
85
106
  }
86
107
  }
87
108
  catch {
88
- /* fall through — attempt to run */
109
+ /* fall through — пробуем запустить */
89
110
  }
90
111
  return this.runScript("test");
91
112
  }
113
+ /** Проверяет существование списка файлов-артефактов (параллельно). */
92
114
  async verifyArtifactFiles(files) {
93
115
  return Promise.all(files.map((f) => this.checkFileExists(f)));
94
116
  }
95
117
  /**
96
- * Check a subtask's machine-readable success_criteria BEFORE the merge
97
- * decision (Plan 5.1). Supported forms:
118
+ * Проверяет machine-readable success_criteria подзадачи ДО решения о merge
119
+ * (Plan 5.1). Поддержаны формы:
98
120
  * - `file-exists:<path>`
99
121
  * - `substring-in-file:<path>:<text>`
100
122
  * - `command-exit-0:<command>`
101
- * Anything else is treated as an LLM-only criterion and reported as a
102
- * warning (never an error) so the Router still sees it in context.
123
+ * Остальное — LLM-only критерий: варнинг (не ошибка), чтобы Router всё равно
124
+ * видел его в контексте.
103
125
  */
104
126
  async verifySubtaskCriteria(subtask) {
105
127
  const errors = [];
@@ -161,6 +183,7 @@ export class StepVerifier {
161
183
  }
162
184
  return { errors, warnings, details };
163
185
  }
186
+ /** Верифицирует MoE-манифест: эксперты, typecheck, файлы и критерии подзадач. */
164
187
  async verifyMoEManifest(plan, config, allToolTags) {
165
188
  const errors = [];
166
189
  const warnings = [];
@@ -170,7 +193,11 @@ export class StepVerifier {
170
193
  errors.push(err);
171
194
  details.push({ passed: false, message: err });
172
195
  }
173
- const tscResult = await this.runTypeCheck();
196
+ const existingArtifacts = plan.subtasks
197
+ .flatMap((s) => s.allowed_files || [])
198
+ .map((f) => findExistingFile(this.baseDir, f))
199
+ .filter((f) => !!f);
200
+ const tscResult = await this.runTypeCheck(existingArtifacts);
174
201
  details.push(tscResult);
175
202
  if (!tscResult.passed) {
176
203
  errors.push(tscResult.message);
@@ -183,7 +210,7 @@ export class StepVerifier {
183
210
  errors.push(check.message);
184
211
  }
185
212
  }
186
- // Machine-readable success_criteria (Plan 5.1) — checked before merge.
213
+ // Machine-readable success_criteria (Plan 5.1) — проверяются до merge.
187
214
  const criteria = await this.verifySubtaskCriteria(sub);
188
215
  errors.push(...criteria.errors);
189
216
  warnings.push(...criteria.warnings);
@@ -196,11 +223,12 @@ export class StepVerifier {
196
223
  details,
197
224
  };
198
225
  }
226
+ /** Верифицирует шаг: файлы из описания существуют (+синтаксис; delete — наоборот). */
199
227
  async verifyStep(stepDescription, kind) {
200
228
  const fileMatches = extractFileLikeTokens(stripUrls(stepDescription));
201
- // No named files — this step cannot be verified by file existence. Do NOT
202
- // trivially pass (empty every() = true); signal noFiles so the tool guides
203
- // the model to run a real check instead.
229
+ // Нет именованных файлов — шаг непроверяем по существованию. НЕ пропускаем
230
+ // тривиально (пустой every() = true); сигналим noFiles, чтобы тул вёл
231
+ // модель к настоящей проверке.
204
232
  if (fileMatches.length === 0) {
205
233
  return { passed: false, syntaxValid: true, failed: [], noFiles: true };
206
234
  }
@@ -210,17 +238,17 @@ export class StepVerifier {
210
238
  const resolved = findExistingFile(this.baseDir, filePath);
211
239
  const exists = !!resolved;
212
240
  if (kind === "delete") {
213
- // For delete steps: file NOT existing is a pass
241
+ // Для delete-шагов: отсутствие файла — pass
214
242
  results.push({
215
243
  passed: !exists,
216
244
  message: exists
217
245
  ? t("verify.file_still_exists", { path: filePath })
218
246
  : t("verify.file_not_found", { path: filePath }),
219
247
  });
220
- // Don't syntax-check a deleted file
248
+ // Удалённый файл синтаксически не проверяем
221
249
  continue;
222
250
  }
223
- // Create steps: file must exist + valid syntax
251
+ // Create-шаги: файл обязан существовать + валидный синтаксис
224
252
  if (!exists) {
225
253
  results.push({ passed: false, message: t("verify.file_not_found", { path: filePath }) });
226
254
  continue;
@@ -248,7 +276,7 @@ export class StepVerifier {
248
276
  if (!tsc)
249
277
  return true;
250
278
  try {
251
- // Quote the path — commands run through a shell and break on spaces.
279
+ // Путь в кавычках — команды идут через shell и ломаются на пробелах.
252
280
  await this.runAsync(`${tsc} --noEmit --skipLibCheck "${filePath}"`, this.baseDir, 20000);
253
281
  return true;
254
282
  }
@@ -277,8 +305,8 @@ export class StepVerifier {
277
305
  return true;
278
306
  }
279
307
  /**
280
- * Run a command asynchronously (non-blocking event loop) and resolve when it
281
- * exits. Rejects with the captured stderr/stdout on non-zero exit or timeout.
308
+ * Запускает команду асинхронно (не блокируя event loop) и резолвится на
309
+ * выходе. На ненулевой exit или таймаут — reject с захваченными stderr/stdout.
282
310
  */
283
311
  runAsync(command, cwd, timeoutMs) {
284
312
  return new Promise((resolve, reject) => {
@@ -297,7 +325,9 @@ export class StepVerifier {
297
325
  stderr += d.toString();
298
326
  });
299
327
  const timer = setTimeout(() => {
300
- child.kill();
328
+ // Убиваем всё дерево: при shell:true child.kill() останавливает только
329
+ // shell, а реальный внук tsc/node продолжает висеть.
330
+ killTree(child);
301
331
  reject(new Error(`Command timed out after ${timeoutMs}ms`));
302
332
  }, timeoutMs);
303
333
  child.on("error", (err) => {
@@ -306,16 +336,7 @@ export class StepVerifier {
306
336
  });
307
337
  child.on("close", (code) => {
308
338
  clearTimeout(timer);
309
- if (code === 0) {
310
- resolve({ stdout, stderr });
311
- }
312
- else {
313
- const err = new Error(`Command failed with exit code ${code}`);
314
- err.stdout = stdout;
315
- err.stderr = stderr;
316
- err.status = code;
317
- reject(err);
318
- }
339
+ settleChildClose(code, stdout, stderr, `Command failed with exit code ${code}`, resolve, reject);
319
340
  });
320
341
  });
321
342
  }
@@ -1,17 +1,17 @@
1
1
  /**
2
- * Unix commands that do not exist (or have incompatible semantics) in Windows
3
- * cmd.exe. The 9B model keeps reaching for grep/sed/ls/find via the bash tool;
4
- * each failure returns a hint, but the model retries.
2
+ * Unix-команды, которых нет в Windows cmd.exe (или семантика несовместима).
3
+ * 9B-модель тянется к grep/sed/ls/find через тул bash; каждый фейл возвращает
4
+ * хинт, но модель ретраит.
5
5
  *
6
- * This is a session-level, module-side companion to the bash tool's own
7
- * per-command hard-stop (`UNIX_TO_WIN_HINTS` / `FAILING_FIRST_WORDS` /
8
- * `HARD_BLOCK_THRESHOLD` in src/tools/bash.ts). That mechanism injects
9
- * "STOP using X" INTO the tool output, which a 9B model tends to ignore; this
10
- * one, gated to win32 like bash.ts, queues a <system-summary> that lands
11
- * before the next model call. Keep the two command lists consistent.
6
+ * Сессионный модульный компаньон пер-командного hard-stop самого тула bash
7
+ * (`UNIX_TO_WIN_HINTS` / `FAILING_FIRST_WORDS` / `HARD_BLOCK_THRESHOLD` в
8
+ * src/tools/bash.ts). Тот механизм инжектит "STOP using X" В вывод тула,
9
+ * который 9B-модель склонна игнорировать; этот, гейченный на win32 как bash.ts,
10
+ * кладёт <system-summary> перед следующим вызовом модели. Списки команд держим
11
+ * синхронно.
12
12
  *
13
- * This is a command allow/deny list (like the security module's command
14
- * blacklist), NOT task classification (rule #10).
13
+ * Это allow/deny-список команд (как чёрный список команд security-модуля),
14
+ * НЕ классификация задач (правило #10).
15
15
  */
16
16
  const FORBIDDEN_COMMANDS = new Set([
17
17
  "grep",
@@ -40,7 +40,7 @@ function firstWord(command) {
40
40
  const segs = command.trim().split(/[\s;|&]+/);
41
41
  return (segs[0] || "").replace(/[^\w-]/g, "").toLowerCase();
42
42
  }
43
- /** The forbidden command name if `command` starts with one (handles `cd x && …`). */
43
+ /** Имя запрещённой команды, если `command` с неё начинается (понимает `cd x && …`). */
44
44
  export function forbiddenWindowsCommand(command) {
45
45
  const m = command.match(/^\s*cd\s+\S+\s*&&\s*(\S+)/);
46
46
  if (m)
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Проверки заявлений об артефактах (ses_mul5pepu_9mdq90): qwen3.5-9b записала fizzbuzz.py,
3
+ * не смогла запустить (python в чёрном списке), затем вставила "ожидаемый" вывод
4
+ * программы с неверными значениями как якобы реальный; также записала schema.drawio
5
+ * с битым `<diagram/>` + висячим `</diagram>` и заявила "валидный XML".
6
+ * Проверка success-claim срабатывает только на сигналы ошибок в выводах тулов,
7
+ * поэтому оба случая прошли молча.
8
+ *
9
+ * Две детерминированные проверки (regex + состояние диска от вызывающего, без LLM):
10
+ * - `checkArtifactClaims`: ответ подаёт файл как созданный/валидный, а на диске
11
+ * его нет или он не парсится.
12
+ * - `checkUnverifiedOutput`: ответ подаёт кодовый блок как реальный вывод
13
+ * программы, но ни один тул в окне доказательств ничего успешно не исполнял.
14
+ *
15
+ * Чистые функции — состояние диска (`ArtifactState[]`) резолвит вызывающий
16
+ * (цикл агента, владелец baseDir), модуль остаётся тестируемым.
17
+ * Точность смещена в сторону recall, как в success-claim.ts: ложное срабатывание
18
+ * стоит одну ограниченную re-check итерацию, пропуск — статус-кво.
19
+ */
20
+ const WORD = "A-Za-zА-Яа-яЁё0-9_";
21
+ const wb = (body) => new RegExp(`(?<![${WORD}])(?:${body})(?![${WORD}])`, "i");
22
+ /** Язык успеха про файл (любая локаль, на которой говорит агент). */
23
+ const ARTIFACT_SUCCESS_RE = wb("создан[а-яё]*|записан[а-яё]*|сохран[её]н[а-яё]*|готов[а-яё]*|валид[а-яё]*|проверен[а-яё]*|существует|valid(?:ated)?|verified|checked|written|saved|created|exists?|ready");
24
+ /**
25
+ * Каким словом успеха (если есть) ответ описывает упоминание файла.
26
+ * Возвращает найденное слово для точного retry-сообщения.
27
+ */
28
+ function claimedAs(response, path) {
29
+ const idx = response.toLowerCase().indexOf(path.toLowerCase());
30
+ if (idx === -1)
31
+ return null;
32
+ // Смотрим окно вокруг упоминания — заявление обычно в том же
33
+ // предложении ("Файл schema.drawio создан и валиден").
34
+ const window = response.slice(Math.max(0, idx - 160), idx + path.length + 160);
35
+ const m = window.match(ARTIFACT_SUCCESS_RE);
36
+ return m ? m[0] : null;
37
+ }
38
+ /**
39
+ * Возвращает противоречие, если ответ подаёт резолвленный артефакт как
40
+ * созданный/валидный, а на диске его нет или он не парсится.
41
+ */
42
+ export function checkArtifactClaims(response, artifacts) {
43
+ if (!response)
44
+ return null;
45
+ for (const a of artifacts) {
46
+ const claim = a.claim ?? claimedAs(response, a.path);
47
+ if (!claim)
48
+ continue;
49
+ if (!a.exists) {
50
+ return {
51
+ tool: "disk",
52
+ path: a.path,
53
+ snippet: `${a.path}: file not found on disk, claimed "${claim}"`,
54
+ };
55
+ }
56
+ if (a.parseError) {
57
+ return {
58
+ tool: "disk",
59
+ path: a.path,
60
+ snippet: `${a.path}: ${a.parseError}, claimed "${claim}"`,
61
+ };
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ /** Фразы, подающие кодовый блок как РЕАЛЬНЫЙ вывод исполнения (не пример). */
67
+ const OUTPUT_AS_RESULT_RE = wb("вывод программы|вывод команды|результат запуска|результат выполнения|program output|command output|execution result|запустил[а-яё]*.*вывод|покажу вывод|actual output");
68
+ /** Хеджирование, понижающее блок до ожидания/примера — честно, пропускаем. */
69
+ const OUTPUT_HEDGE_RE = wb("ожидаем[а-яё]*|expected|пример[а-яё]*|example|для справки|sample|например|предполагаем[а-яё]*|должен быть|should be");
70
+ const FENCE_RE = /```/;
71
+ /** Тулы, чей успех означает "что-то реально исполнилось". */
72
+ const EXEC_TOOLS = new Set(["bash", "process_wait", "pipeline_run"]);
73
+ /**
74
+ * Возвращает противоречие, если ответ показывает вывод как результат запуска,
75
+ * но ни один exec-тул в окне доказательств успешно не отработал.
76
+ */
77
+ export function checkUnverifiedOutput(response, tools) {
78
+ if (!response || !OUTPUT_AS_RESULT_RE.test(response))
79
+ return null;
80
+ if (OUTPUT_HEDGE_RE.test(response))
81
+ return null;
82
+ if (!FENCE_RE.test(response))
83
+ return null;
84
+ const recent = tools.filter((t) => t.text?.trim()).slice(-10);
85
+ const executed = recent.some((t) => t.ok === true && t.name !== undefined && EXEC_TOOLS.has(t.name));
86
+ if (executed)
87
+ return null;
88
+ const m = response.match(OUTPUT_AS_RESULT_RE);
89
+ return {
90
+ tool: "no-execution",
91
+ snippet: `claimed "${m?.[0] ?? "output"}" with no successful execution tool in evidence`,
92
+ };
93
+ }
@@ -1,17 +1,19 @@
1
1
  import { t } from "../../i18n/index";
2
2
  const MIN_CHARS = 1;
3
- /** Both responses must be this long before repetition applies — a short
4
- * honest answer ("Done." / "The build passes.") repeated twice is not
5
- * degeneration and must not burn a hallucination retry. */
3
+ /** Оба ответа должны быть не короче — короткий честный ответ ("Готово.") дважды
4
+ * подряд не деградация и не должен жечь hallucination-retry. */
6
5
  const MIN_REPEAT_CHARS = 120;
7
6
  const MIN_REPEAT_WORDS = 20;
8
- /** Word-overlap above this threshold counts as repetition (was 0.5). */
7
+ /** Пересечение слов выше порога считается повтором (было 0.5). */
9
8
  const REPEAT_OVERLAP_THRESHOLD = 0.8;
9
+ /** Проверка уверенности: пустой ответ, дословный повтор, низкая diversity. */
10
10
  export class ConfidenceCheck {
11
11
  previousResponse = "";
12
+ /** Запоминает предыдущий ответ для детекции повторов. */
12
13
  setPreviousResponse(response) {
13
14
  this.previousResponse = response;
14
15
  }
16
+ /** Валидирует ответ: retry на пустоту/повтор, warn на низкую diversity, иначе pass. */
15
17
  validate(response) {
16
18
  if (!response || response.length < MIN_CHARS) {
17
19
  return {
@@ -20,13 +22,13 @@ export class ConfidenceCheck {
20
22
  reason: t("hall.short_response"),
21
23
  };
22
24
  }
23
- // Short replies are not flagged: a greeting, a terse acknowledgement or a
24
- // one-line confirmation is a valid answer. Real truncation is caught by the
25
- // provider's finish_reason ("length"), and emptiness by the retry above.
25
+ // Короткие реплики не флагуются: приветствие, скупое подтверждение или
26
+ // однострочник — валидный ответ. Реальное усечение ловит finish_reason
27
+ // провайдера ("length"), пустоту — retry выше.
26
28
  const wordCount = response.split(/\s+/).filter(Boolean).length;
27
- // Language-agnostic: repetition detection via word overlap. Only flag when
28
- // BOTH the current and the previous response are substantive — a short
29
- // answer after a long one (or two short answers) is not a loop.
29
+ // Языконезависимо: детекция повторов по пересечению слов. Флагуем только
30
+ // если ОБА ответа содержательные — короткий ответ после длинного (или два
31
+ // коротких) не цикл.
30
32
  if (this.previousResponse) {
31
33
  const prevWords = this.previousResponse.split(/\s+/).filter(Boolean).length;
32
34
  const bothSubstantive = response.length >= MIN_REPEAT_CHARS &&
@@ -44,7 +46,7 @@ export class ConfidenceCheck {
44
46
  }
45
47
  }
46
48
  }
47
- // Language-agnostic: very low word diversity (same words repeated)
49
+ // Языконезависимо: очень низкая diversity слов (одни и те же слова по кругу)
48
50
  const words = response
49
51
  .toLowerCase()
50
52
  .split(/\s+/)
@@ -1,28 +1,36 @@
1
+ /** Проверка консистентности: решения, созданные/удалённые файлы сессии. */
1
2
  export class ConsistencyCheck {
2
3
  decisions = [];
3
4
  createdFiles = new Set();
4
5
  deletedFiles = new Set();
6
+ /** Запоминает решение агента. */
5
7
  trackDecision(decision, location) {
6
8
  this.decisions.push({ decision, location });
7
9
  }
10
+ /** Отмечает созданный файл (снимает с него флаг удалённого). */
8
11
  trackCreatedFile(path) {
9
12
  this.createdFiles.add(path);
10
- // A delete followed by a re-create un-deletes the file — otherwise
11
- // wasFileDeleted() keeps reporting it as gone after delete+create.
13
+ // Удаление с последующим пересозданием снимает флаг — иначе wasFileDeleted()
14
+ // считает файл пропавшим и после delete+create.
12
15
  this.deletedFiles.delete(path);
13
16
  }
17
+ /** Отмечает удалённый файл. */
14
18
  trackDeletedFile(path) {
15
19
  this.deletedFiles.add(path);
16
20
  }
21
+ /** Все созданные файлы. */
17
22
  getCreatedFiles() {
18
23
  return Array.from(this.createdFiles);
19
24
  }
25
+ /** Все удалённые файлы. */
20
26
  getDeletedFiles() {
21
27
  return Array.from(this.deletedFiles);
22
28
  }
29
+ /** Все отслеживаемые решения. */
23
30
  getDecisions() {
24
31
  return this.decisions;
25
32
  }
33
+ /** True, если есть хоть одно решение. */
26
34
  hasDecisions() {
27
35
  return this.decisions.length > 0;
28
36
  }
@@ -1,7 +1,9 @@
1
+ // Оркестратор проверок галлюцинаций: confidence → factual → LLM-судья.
1
2
  import { ConsistencyCheck } from "./consistency";
2
3
  import { ConfidenceCheck } from "./confidence";
3
4
  import { FactualCheck } from "./factual";
4
5
  import { LLMJudge } from "./llm-judge";
6
+ /** Детектор галлюцинаций: объединяет проверки уверенности, фактов и LLM-судью. */
5
7
  export class HallucinationDetector {
6
8
  consistency;
7
9
  confidence;
@@ -11,22 +13,25 @@ export class HallucinationDetector {
11
13
  this.consistency = new ConsistencyCheck();
12
14
  this.confidence = new ConfidenceCheck();
13
15
  this.factual = baseDir ? new FactualCheck(baseDir) : null;
14
- // The judge is opt-out: it only runs when a provider is supplied AND the
15
- // caller did not explicitly disable it (the main agent disables it by
16
- // default via config.hallucination.judge.enabled).
16
+ // Судья opt-out: работает только если передан провайдер И вызывающий явно
17
+ // его не отключил (главный агент отключает по умолчанию через
18
+ // config.hallucination.judge.enabled).
17
19
  const judgeEnabled = options?.judgeEnabled ?? true;
18
20
  this.judge = llmProvider && judgeEnabled ? new LLMJudge(llmProvider) : null;
19
21
  }
22
+ /** Доступ к проверке консистентности (решения, созданные/удалённые файлы). */
20
23
  getConsistencyCheck() {
21
24
  return this.consistency;
22
25
  }
26
+ /** Доступ к проверке уверенности (повторы, diversity). */
23
27
  getConfidenceCheck() {
24
28
  return this.confidence;
25
29
  }
26
- /** Add files known from compaction summaries to the factual checker. */
30
+ /** Добавляет файлы, известные из сводок компакции, в фактическую проверку. */
27
31
  addKnownFiles(files) {
28
32
  this.factual?.addKnownFiles(files);
29
33
  }
34
+ /** Валидирует ответ: retry/block уверенности → факты → LLM-судья + warn-агрегация. */
30
35
  async validate(response) {
31
36
  const confidenceResult = this.confidence.validate(response);
32
37
  if (confidenceResult.status === "retry" || confidenceResult.status === "block") {
@@ -37,7 +42,7 @@ export class HallucinationDetector {
37
42
  if (factualResult && factualResult.status !== "pass") {
38
43
  return factualResult;
39
44
  }
40
- // LLM-as-judge consistency: fast path (no decisions) skips any LLM call.
45
+ // Судья по консистентности (LLM): быстрый путь (нет решений) — без LLM-вызова.
41
46
  const judgeResult = this.judge ? await this.judge.validate(response, this.consistency) : null;
42
47
  const warnings = [];
43
48
  if (judgeResult && judgeResult.status === "warn")