micro-models-agent 2.11.0 → 2.13.1

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 (356) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/assets/app.js +22 -2
  3. package/dist/assets/index.html +39 -9
  4. package/dist/assets/js/dom.js +15 -0
  5. package/dist/assets/js/feed.js +129 -10
  6. package/dist/assets/js/sidebar.js +512 -0
  7. package/dist/assets/js/tabs.js +30 -5
  8. package/dist/assets/setup.js +267 -8
  9. package/dist/assets/styles.css +168 -3
  10. package/dist/cli/cache-line.js +1 -0
  11. package/dist/cli/command-suggest.js +5 -4
  12. package/dist/cli/commands.js +231 -122
  13. package/dist/cli/completer.js +132 -31
  14. package/dist/cli/index.js +1 -0
  15. package/dist/cli/json-payload.js +4 -4
  16. package/dist/cli/main.js +166 -61
  17. package/dist/cli/onboarding-commands.js +126 -0
  18. package/dist/cli/plugin-commands.js +2 -1
  19. package/dist/cli/relaunch.js +16 -13
  20. package/dist/cli/repl-commands-lsp.js +75 -0
  21. package/dist/cli/repl-commands-skills.js +105 -0
  22. package/dist/cli/repl-commands.js +589 -266
  23. package/dist/cli/repl.js +436 -177
  24. package/dist/cli/run-result.js +5 -6
  25. package/dist/cli/security-commands.js +37 -28
  26. package/dist/cli/setup-order.js +4 -55
  27. package/dist/cli/setup-prompt.js +3 -2
  28. package/dist/cli/setup.js +27 -107
  29. package/dist/cli/web-command.js +98 -0
  30. package/dist/config/budget.js +5 -5
  31. package/dist/config/config.js +103 -96
  32. package/dist/config/defaults.js +38 -8
  33. package/dist/config/domains.js +24 -27
  34. package/dist/config/env.js +46 -0
  35. package/dist/config/experts.js +2 -0
  36. package/dist/config/index.js +1 -0
  37. package/dist/config/paths.js +56 -0
  38. package/dist/config/redact.js +44 -0
  39. package/dist/config/registry-extra.js +334 -0
  40. package/dist/config/registry.js +584 -0
  41. package/dist/config/security.js +35 -36
  42. package/dist/config/timeouts.js +74 -0
  43. package/dist/config/utils.js +16 -15
  44. package/dist/core/agent/artifact-progress.js +59 -0
  45. package/dist/core/agent/artifact-resolve.js +79 -0
  46. package/dist/core/agent/audit-gate.js +7 -3
  47. package/dist/core/agent/compaction.js +2 -2
  48. package/dist/core/agent/constants.js +33 -4
  49. package/dist/core/agent/context-log.js +454 -0
  50. package/dist/core/agent/context-renderer.js +5 -0
  51. package/dist/core/agent/critic-gate.js +132 -0
  52. package/dist/core/agent/hallucination-gate.js +148 -38
  53. package/dist/core/agent/loop-state.js +15 -0
  54. package/dist/core/agent/prefix-monitor.js +1 -1
  55. package/dist/core/agent/reasoning-resolver.js +10 -1
  56. package/dist/core/agent/token-tracker.js +7 -4
  57. package/dist/core/agent/tool-batch.js +93 -18
  58. package/dist/core/agent/tool-images.js +18 -0
  59. package/dist/core/agent/tool-output.js +65 -11
  60. package/dist/core/agent/tool-pairing.js +60 -0
  61. package/dist/core/agent-moe.js +25 -24
  62. package/dist/core/agent.js +429 -108
  63. package/dist/core/bootstrap/agent-phase.js +66 -0
  64. package/dist/core/bootstrap/config-phase.js +86 -0
  65. package/dist/core/bootstrap/modules-phase.js +269 -0
  66. package/dist/core/bootstrap/provider-phase.js +183 -0
  67. package/dist/core/bootstrap/session-phase.js +204 -0
  68. package/dist/core/bootstrap/state.js +94 -0
  69. package/dist/core/bootstrap/workspace-phase.js +115 -0
  70. package/dist/core/bootstrap.js +92 -660
  71. package/dist/core/crash-handler.js +17 -7
  72. package/dist/core/environment.js +37 -12
  73. package/dist/core/host-plan.js +43 -0
  74. package/dist/core/host.js +23 -0
  75. package/dist/core/index.js +1 -0
  76. package/dist/core/prompt-builder.js +17 -1
  77. package/dist/core/prompt-overflow.js +42 -43
  78. package/dist/core/session-logger.js +122 -30
  79. package/dist/core/system-prompt.js +117 -0
  80. package/dist/core/version.js +6 -4
  81. package/dist/core/web-host.js +34 -0
  82. package/dist/core/workspace.js +19 -15
  83. package/dist/i18n/en.json +89 -3
  84. package/dist/i18n/index.js +37 -3
  85. package/dist/i18n/ru.json +89 -3
  86. package/dist/index.js +7 -3
  87. package/dist/llm/image-utils.js +55 -64
  88. package/dist/llm/llm-errors.js +9 -0
  89. package/dist/llm/model-loader.js +10 -32
  90. package/dist/llm/models-dev.js +121 -0
  91. package/dist/llm/openai-compat.js +146 -73
  92. package/dist/llm/orchestrator.js +33 -11
  93. package/dist/llm/provider-budget.js +2 -2
  94. package/dist/llm/provider-context.js +233 -0
  95. package/dist/llm/provider.js +1 -1
  96. package/dist/llm/response.js +5 -0
  97. package/dist/llm/token-counter.js +27 -11
  98. package/dist/logger/app-logger.js +48 -22
  99. package/dist/logger/file-log.js +28 -19
  100. package/dist/logger/index.js +1 -0
  101. package/dist/main.js +888 -139
  102. package/dist/migration/backup.js +14 -3
  103. package/dist/migration/detect.js +12 -3
  104. package/dist/migration/index.js +1 -0
  105. package/dist/modules/artifacts/store.js +9 -8
  106. package/dist/modules/browser/actions.js +6 -0
  107. package/dist/modules/browser/bridge-client.js +14 -12
  108. package/dist/modules/browser/bridge-path.js +4 -4
  109. package/dist/modules/browser/console-buffer.js +37 -0
  110. package/dist/modules/browser/cookie-store.js +5 -0
  111. package/dist/modules/browser/driver-types.js +1 -0
  112. package/dist/modules/browser/driver.js +19 -11
  113. package/dist/modules/browser/index.js +3 -1
  114. package/dist/modules/browser/module.js +2 -1
  115. package/dist/modules/browser/session.js +10 -30
  116. package/dist/modules/browser/snapshot.js +19 -13
  117. package/dist/modules/browser/types.js +1 -0
  118. package/dist/modules/certification/cli.js +28 -20
  119. package/dist/modules/certification/fact-checker.js +9 -1
  120. package/dist/modules/certification/loader.js +9 -2
  121. package/dist/modules/certification/manifest.js +83 -21
  122. package/dist/modules/certification/runner.js +23 -13
  123. package/dist/modules/certification/scenarios.js +20 -20
  124. package/dist/modules/certification/syntax-scenarios.js +1 -2
  125. package/dist/modules/context/chunk-query.js +11 -9
  126. package/dist/modules/context/fact-extractor.js +37 -39
  127. package/dist/modules/context/index.js +1 -0
  128. package/dist/modules/context/manager.js +106 -80
  129. package/dist/modules/critic/client.js +176 -0
  130. package/dist/modules/critic/evidence.js +81 -0
  131. package/dist/modules/critic/provider.js +45 -0
  132. package/dist/modules/critic/tools.js +78 -0
  133. package/dist/modules/critic/types.js +1 -0
  134. package/dist/modules/execution/audit-runners.js +28 -34
  135. package/dist/modules/execution/auditor.js +53 -49
  136. package/dist/modules/execution/execution-plugin.js +142 -102
  137. package/dist/modules/execution/index.js +1 -1
  138. package/dist/modules/execution/input-from.js +37 -13
  139. package/dist/modules/execution/module.js +368 -191
  140. package/dist/modules/execution/moe-executor.js +53 -26
  141. package/dist/modules/execution/plan-actions.js +320 -202
  142. package/dist/modules/execution/plan-checks.js +35 -0
  143. package/dist/modules/execution/plan-coverage.js +36 -33
  144. package/dist/modules/execution/plan-steps.js +11 -3
  145. package/dist/modules/execution/plan-store.js +83 -17
  146. package/dist/modules/execution/plan-tool.js +89 -35
  147. package/dist/modules/execution/plan-validator.js +30 -10
  148. package/dist/modules/execution/planner.js +22 -13
  149. package/dist/modules/execution/stuck-detector.js +196 -93
  150. package/dist/modules/execution/tracker.js +22 -8
  151. package/dist/modules/execution/verifier.js +69 -48
  152. package/dist/modules/execution/windows-commands.js +12 -12
  153. package/dist/modules/hallucination/artifact-claim.js +93 -0
  154. package/dist/modules/hallucination/confidence.js +13 -11
  155. package/dist/modules/hallucination/consistency.js +10 -2
  156. package/dist/modules/hallucination/detector.js +10 -5
  157. package/dist/modules/hallucination/factual.js +43 -26
  158. package/dist/modules/hallucination/index.js +1 -0
  159. package/dist/modules/hallucination/js-identifiers.js +44 -45
  160. package/dist/modules/hallucination/llm-judge.js +17 -12
  161. package/dist/modules/hallucination/success-claim.js +59 -0
  162. package/dist/modules/indexer/cache.js +52 -4
  163. package/dist/modules/indexer/index.js +1 -0
  164. package/dist/modules/indexer/map-command.js +2 -4
  165. package/dist/modules/indexer/map-select.js +16 -16
  166. package/dist/modules/indexer/module.js +47 -12
  167. package/dist/modules/indexer/project-profile.js +8 -7
  168. package/dist/modules/indexer/symbols.js +15 -15
  169. package/dist/modules/indexer/walker.js +159 -29
  170. package/dist/modules/lsp/check-tool.js +7 -5
  171. package/dist/modules/lsp/client.js +39 -39
  172. package/dist/modules/lsp/command.js +17 -24
  173. package/dist/modules/lsp/config.js +12 -11
  174. package/dist/modules/lsp/index.js +1 -0
  175. package/dist/modules/lsp/module.js +48 -46
  176. package/dist/modules/lsp/probe.js +13 -14
  177. package/dist/modules/lsp/project-root.js +14 -17
  178. package/dist/modules/lsp/startup-check.js +18 -16
  179. package/dist/modules/mcp/client.js +59 -62
  180. package/dist/modules/mcp/index.js +1 -0
  181. package/dist/modules/mcp/module.js +66 -37
  182. package/dist/modules/mcp/registry.js +5 -0
  183. package/dist/modules/memory/index.js +1 -0
  184. package/dist/modules/memory/module.js +12 -8
  185. package/dist/modules/memory/search.js +4 -1
  186. package/dist/modules/memory/store.js +40 -2
  187. package/dist/modules/onboarding/init.js +47 -0
  188. package/dist/modules/onboarding/meet.js +30 -0
  189. package/dist/modules/onboarding/mode.js +30 -0
  190. package/dist/modules/onboarding/prompts.js +43 -0
  191. package/dist/modules/pipelines/engine.js +10 -0
  192. package/dist/modules/pipelines/index.js +1 -0
  193. package/dist/modules/pipelines/parser.js +4 -1
  194. package/dist/modules/pipelines/template.js +4 -0
  195. package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
  196. package/dist/modules/plugins/builtin/notify.js +2 -0
  197. package/dist/modules/plugins/index.js +1 -0
  198. package/dist/modules/plugins/loader.js +18 -3
  199. package/dist/modules/plugins/manager.js +37 -17
  200. package/dist/modules/pricing/index.js +10 -11
  201. package/dist/modules/pricing/prices.js +12 -12
  202. package/dist/modules/processes/index.js +1 -0
  203. package/dist/modules/processes/kill-tree.js +27 -31
  204. package/dist/modules/processes/registry.js +44 -25
  205. package/dist/modules/processes/runner.js +4 -4
  206. package/dist/modules/providers/capabilities.js +28 -0
  207. package/dist/modules/providers/create.js +3 -4
  208. package/dist/modules/providers/factory.js +7 -7
  209. package/dist/modules/providers/fallback.js +12 -12
  210. package/dist/modules/providers/health.js +4 -4
  211. package/dist/modules/providers/manager.js +50 -19
  212. package/dist/modules/providers/naming.js +15 -0
  213. package/dist/modules/providers/preflight.js +71 -0
  214. package/dist/modules/providers/presets.js +39 -8
  215. package/dist/modules/providers/registry.js +7 -2
  216. package/dist/modules/reasoning/policy.js +8 -6
  217. package/dist/modules/reasoning/probe.js +30 -25
  218. package/dist/modules/security/audit-log.js +19 -36
  219. package/dist/modules/security/audit-notifier.js +35 -75
  220. package/dist/modules/security/command-validator.js +99 -51
  221. package/dist/modules/security/content-scanner.js +8 -8
  222. package/dist/modules/security/data-sanitizer.js +9 -16
  223. package/dist/modules/security/encryption.js +32 -52
  224. package/dist/modules/security/index.js +2 -1
  225. package/dist/modules/security/network-validator.js +120 -43
  226. package/dist/modules/security/path-validator.js +61 -46
  227. package/dist/modules/security/protected-paths.js +73 -0
  228. package/dist/modules/security/rate-limiter.js +17 -49
  229. package/dist/modules/security/security-policies.js +25 -57
  230. package/dist/modules/security/session-encryption.js +32 -71
  231. package/dist/modules/security/session-isolation.js +15 -28
  232. package/dist/modules/session/auto-name.js +63 -0
  233. package/dist/modules/session/export.js +98 -0
  234. package/dist/modules/session/index.js +1 -0
  235. package/dist/modules/session/manager.js +79 -28
  236. package/dist/modules/session/module.js +4 -3
  237. package/dist/modules/session/store.js +57 -20
  238. package/dist/modules/setup/answers.js +35 -0
  239. package/dist/modules/setup/apply.js +79 -0
  240. package/dist/modules/setup/discovery.js +72 -0
  241. package/dist/modules/skills/index.js +1 -0
  242. package/dist/modules/skills/loader.js +9 -1
  243. package/dist/modules/skills/module.js +11 -0
  244. package/dist/modules/updater/changelog-reader.js +15 -16
  245. package/dist/modules/updater/checker.js +9 -4
  246. package/dist/modules/updater/dev-detect.js +7 -6
  247. package/dist/modules/updater/index.js +1 -0
  248. package/dist/modules/updater/module.js +17 -15
  249. package/dist/modules/user-profile/compressor.js +2 -0
  250. package/dist/modules/user-profile/index.js +1 -0
  251. package/dist/modules/user-profile/profile.js +10 -1
  252. package/dist/modules/webui/answer-api.js +29 -0
  253. package/dist/modules/webui/assets.js +50 -0
  254. package/dist/modules/webui/body.js +21 -0
  255. package/dist/modules/webui/commands-settings.js +209 -0
  256. package/dist/modules/webui/commands-shared.js +60 -0
  257. package/dist/modules/webui/commands.js +174 -0
  258. package/dist/modules/webui/config-api.js +134 -0
  259. package/dist/modules/webui/file-api.js +202 -0
  260. package/dist/modules/webui/forward.js +159 -0
  261. package/dist/modules/webui/history-api.js +112 -0
  262. package/dist/modules/webui/host-state.js +60 -0
  263. package/dist/modules/webui/i18n-api.js +9 -0
  264. package/dist/modules/webui/limits.js +6 -0
  265. package/dist/modules/webui/log-forward.js +20 -0
  266. package/dist/modules/webui/memory-commands.js +64 -0
  267. package/dist/modules/webui/model-cache.js +18 -0
  268. package/dist/modules/webui/module.js +148 -0
  269. package/dist/modules/webui/persist.js +24 -0
  270. package/dist/modules/webui/prompt-io.js +97 -0
  271. package/dist/modules/webui/providers-api.js +337 -0
  272. package/dist/modules/webui/providers-view.js +56 -0
  273. package/dist/modules/webui/reasoning-view.js +15 -0
  274. package/dist/modules/webui/run-agent.js +81 -0
  275. package/dist/modules/webui/server.js +284 -0
  276. package/dist/modules/webui/sessions-api.js +178 -0
  277. package/dist/modules/webui/settings-api.js +561 -0
  278. package/dist/modules/webui/setup-api.js +201 -0
  279. package/dist/modules/webui/state.js +74 -0
  280. package/dist/modules/webui/util.js +261 -0
  281. package/dist/output/bus.js +7 -2
  282. package/dist/output/channel.js +80 -28
  283. package/dist/output/format.js +2 -1
  284. package/dist/output/index.js +1 -0
  285. package/dist/output/json-sink.js +5 -3
  286. package/dist/output/machine.js +4 -3
  287. package/dist/output/session-sink.js +4 -4
  288. package/dist/tools/approve.js +4 -0
  289. package/dist/tools/attach-image.js +5 -4
  290. package/dist/tools/auto-fixer.js +26 -44
  291. package/dist/tools/auto-tags.js +83 -0
  292. package/dist/tools/bash.js +193 -116
  293. package/dist/tools/browser.js +76 -31
  294. package/dist/tools/chunk-query.js +14 -6
  295. package/dist/tools/create-dir.js +9 -5
  296. package/dist/tools/delete-file.js +9 -5
  297. package/dist/tools/download-file.js +32 -7
  298. package/dist/tools/edit-file.js +52 -14
  299. package/dist/tools/enable-tools.js +6 -6
  300. package/dist/tools/executor.js +132 -27
  301. package/dist/tools/file-info.js +3 -1
  302. package/dist/tools/filter-tools.js +3 -3
  303. package/dist/tools/glob-tool.js +4 -1
  304. package/dist/tools/grep-tool.js +13 -10
  305. package/dist/tools/hidden-tools-block.js +6 -6
  306. package/dist/tools/index.js +16 -8
  307. package/dist/tools/list-dir.js +4 -2
  308. package/dist/tools/load-skill.js +7 -0
  309. package/dist/tools/mcp-call.js +15 -4
  310. package/dist/tools/meet-done.js +14 -0
  311. package/dist/tools/move-file.js +14 -7
  312. package/dist/tools/path-utils.js +29 -29
  313. package/dist/tools/pipeline-run.js +29 -7
  314. package/dist/tools/preview.js +1 -1
  315. package/dist/tools/process-ids.js +31 -0
  316. package/dist/tools/process-kill.js +9 -16
  317. package/dist/tools/process-list.js +1 -0
  318. package/dist/tools/process-log.js +11 -16
  319. package/dist/tools/process-wait.js +63 -0
  320. package/dist/tools/prompt-io.js +4 -3
  321. package/dist/tools/question.js +172 -25
  322. package/dist/tools/read-file.js +55 -34
  323. package/dist/tools/recall.js +14 -8
  324. package/dist/tools/registry.js +6 -5
  325. package/dist/tools/remember.js +8 -4
  326. package/dist/tools/scope-request.js +11 -6
  327. package/dist/tools/search-history.js +5 -2
  328. package/dist/tools/session-info.js +2 -0
  329. package/dist/tools/set-thinking.js +8 -4
  330. package/dist/tools/subagent.js +59 -39
  331. package/dist/tools/syntax-validator.js +292 -90
  332. package/dist/tools/user-input.js +22 -18
  333. package/dist/tools/web-browse.js +9 -3
  334. package/dist/tools/web-fetch.js +12 -23
  335. package/dist/tools/web-search.js +7 -5
  336. package/dist/tools/write-file.js +45 -15
  337. package/dist/ui/action-log.js +13 -0
  338. package/dist/ui/box.js +4 -3
  339. package/dist/ui/colors.js +2 -0
  340. package/dist/ui/diff.js +6 -5
  341. package/dist/ui/index.js +1 -0
  342. package/dist/ui/line-editor.js +230 -122
  343. package/dist/ui/line-math.js +10 -2
  344. package/dist/ui/md-formatter.js +57 -37
  345. package/dist/ui/output.js +8 -3
  346. package/dist/ui/plan-view.js +21 -10
  347. package/dist/ui/renderer.js +393 -67
  348. package/dist/ui/spinner.js +16 -5
  349. package/dist/ui/table.js +15 -17
  350. package/dist/ui/theme.js +143 -0
  351. package/dist/utils/child-run.js +20 -0
  352. package/dist/utils/error.js +1 -1
  353. package/dist/utils/index.js +2 -0
  354. package/dist/utils/retry.js +2 -2
  355. package/dist/utils/truncate.js +1 -1
  356. package/package.json +1 -1
@@ -1,11 +1,13 @@
1
+ // lsp_check: сбор файлов для проверки и форматирование диагностик.
1
2
  import { readdir, stat } from "fs/promises";
2
3
  import { resolve, relative } from "path";
3
4
  import { getServerForFile } from "./config";
4
5
  import { toForwardSlash } from "../../tools/path-utils";
5
- /** lsp_check directory mode: walk depth and per-call file cap. */
6
+ /** Режим каталога lsp_check: глубина обхода и кап файлов на вызов. */
6
7
  export const MAX_LSP_CHECK_FILES = 15;
7
8
  const MAX_LSP_WALK_DEPTH = 4;
8
9
  const SKIP_DIRS = new Set(["node_modules", ".git", ".mma", "dist"]);
10
+ /** Подписи severity диагностик. */
9
11
  export const severityLabels = {
10
12
  1: "error",
11
13
  2: "warning",
@@ -13,10 +15,9 @@ export const severityLabels = {
13
15
  4: "hint",
14
16
  };
15
17
  /**
16
- * Resolve the target to a file (single check) or a bounded list of source
17
- * files (directory check, capped at MAX_LSP_CHECK_FILES). Only files with a
18
- * configured LSP server are collected, so unsupported extensions are simply
19
- * not checked rather than producing noise.
18
+ * Резолв цели: файл (точечная проверка) или ограниченный список исходников
19
+ * (проверка каталога, кап MAX_LSP_CHECK_FILES). Собираются только файлы с
20
+ * настроенным LSP-сервером — неподдерживаемые расширения молча не проверяются.
20
21
  */
21
22
  export async function collectCheckFiles(resolved, config) {
22
23
  const info = await stat(resolved).catch(() => null);
@@ -45,6 +46,7 @@ async function walkDir(dir, depth, out, config) {
45
46
  }
46
47
  }
47
48
  }
49
+ /** Форматировать диагностики списком `файл(стр,кол): severity: текст` (первые 40). */
48
50
  export function formatCheckDiagnostics(items, baseDir) {
49
51
  return items
50
52
  .slice(0, 40)
@@ -1,3 +1,4 @@
1
+ // LSP-клиент: spawn сервера, JSON-RPC поверх stdio, диагностики, tree-kill при shutdown.
1
2
  import { spawn, execSync } from "child_process";
2
3
  import { resolve } from "path";
3
4
  import { platform } from "os";
@@ -5,11 +6,11 @@ import { resolveSpawnCommand, buildWinCommandLine } from "./command";
5
6
  import { killTree } from "../processes/registry";
6
7
  import { toForwardSlash } from "../../tools/path-utils";
7
8
  import { errMsg } from "../../utils";
9
+ import { LSP_DEFAULT_TIMEOUT_MS as DEFAULT_TIMEOUT } from "../../config/timeouts";
8
10
  /**
9
- * Logger adapter that writes LSP diagnostics to app.jsonl only — never to
10
- * console/terminal. LSP warnings are noisy (timeout on first run, npx
11
- * install) and should be diagnosable from session files without polluting
12
- * the user's chat.
11
+ * Логгер-адаптер: LSP-диагностику только в app.jsonl, никогда в консоль/терминал.
12
+ * LSP-предупреждения шумные (таймаут на первом запуске, установка npx) и должны
13
+ * диагностироваться из файлов сессии, не мусор в чате пользователя.
13
14
  */
14
15
  export class FileOnlyLogger {
15
16
  logger;
@@ -29,9 +30,10 @@ export class FileOnlyLogger {
29
30
  this.logger.logSilent("error", msg, meta);
30
31
  }
31
32
  }
32
- const DEFAULT_TIMEOUT = 15000;
33
+ /** LSP-клиент: checkFile/probe поверх spawn + initialize + shutdown. */
33
34
  export class LspClient {
34
35
  logger = null;
36
+ /** Назначить логгер (null — молчать). */
35
37
  setLogger(logger) {
36
38
  this.logger = logger;
37
39
  }
@@ -44,6 +46,7 @@ export class LspClient {
44
46
  diagnosticsResolve = null;
45
47
  diagnosticsTimer = null;
46
48
  initialized = false;
49
+ /** Проверить файл: открыть в сервере, дождаться publishDiagnostics, вернуть их. */
47
50
  async checkFile(filePath, baseDir, config, projectRoot = baseDir) {
48
51
  const timeout = config.timeout ?? DEFAULT_TIMEOUT;
49
52
  try {
@@ -71,7 +74,7 @@ export class LspClient {
71
74
  return await diagPromise;
72
75
  }
73
76
  catch (e) {
74
- // Re-throw so the LspModule can surface failures instead of hiding them.
77
+ // Пробросить наружу, чтобы LspModule показал падение, а не спрятал.
75
78
  throw e instanceof Error ? e : new Error(String(e));
76
79
  }
77
80
  finally {
@@ -79,10 +82,9 @@ export class LspClient {
79
82
  }
80
83
  }
81
84
  /**
82
- * Lightweight "does the server actually start" probe: spawn + initialize +
83
- * shutdown. Used by the REPL banner to surface a dead server (npx
84
- * auto-install failure, wrong package name, missing binary) up front instead
85
- * of the first write_file timing out.
85
+ * Лёгкая проба «сервер реально стартует»: spawn + initialize + shutdown.
86
+ * Для баннера REPL — мёртвый сервер (упавшая npx-автоустановка, неверное имя
87
+ * пакета, нет бинарника) виден заранее, а не таймаутом первого write_file.
86
88
  */
87
89
  async probe(config, projectRoot, timeout) {
88
90
  try {
@@ -110,7 +112,7 @@ export class LspClient {
110
112
  command: config.command,
111
113
  projectRoot,
112
114
  });
113
- // npx auto-install can blow past the initialize timeout on first run — retry once.
115
+ // npx-автоустановка может вылететь за таймаут initialize на первом запуске — один ретрай.
114
116
  if (!(e instanceof Error) || !e.message.includes("initialize"))
115
117
  throw e;
116
118
  await this.shutdown();
@@ -138,9 +140,9 @@ export class LspClient {
138
140
  throw new Error(`${config.command} not found in PATH`);
139
141
  }
140
142
  }
141
- // When the command is npx, try to find the actual binary globally first.
142
- // npx downloads packages on every invocation (slow on Windows, ~8-15s),
143
- // even when the binary is already installed globally.
143
+ // Команда npx — сначала ищем реальный бинарник глобально.
144
+ // npx качает пакеты на каждый вызов (на Windows медленно, ~8-15с),
145
+ // даже если бинарник уже стоит глобально.
144
146
  let effectiveCommand = config.command;
145
147
  let effectiveArgs = config.args ?? [];
146
148
  if (config.command === "npx" && effectiveArgs.length > 0) {
@@ -149,13 +151,13 @@ export class LspClient {
149
151
  const whichCmd = platform() === "win32" ? `where ${binaryName}` : `which ${binaryName}`;
150
152
  try {
151
153
  execSync(whichCmd, { stdio: "pipe", timeout: 3000 });
152
- // Binary found globally — use it directly, skip npx.
154
+ // Бинарник есть глобально — напрямую, без npx.
153
155
  effectiveCommand = binaryName;
154
156
  effectiveArgs = effectiveArgs.filter((a) => a !== "--yes" && a !== "--package" && a !== binaryName && !this.isPackageVersion(a));
155
157
  this.logger?.debug(`LSP: using globally installed ${binaryName} (skipped npx)`);
156
158
  }
157
159
  catch {
158
- // Not found globally — fall back to npx (will auto-install).
160
+ // Глобально нет — фолбэк на npx (самоустановится).
159
161
  this.logger?.debug(`LSP: ${binaryName} not found globally, falling back to npx`);
160
162
  }
161
163
  }
@@ -169,14 +171,13 @@ export class LspClient {
169
171
  stdio: ["pipe", "pipe", "pipe"],
170
172
  env: { ...process.env, ...config.env },
171
173
  cwd: projectRoot,
172
- // POSIX: make the child a process-group leader so killTree()'s
173
- // `kill(-pid)` reaches the whole chain (npx → node → tsserver).
174
- // Without it the group kill fails with ESRCH and grandchildren
175
- // survive as orphans. Windows relies on taskkill /T instead.
174
+ // POSIX: потомок — лидер группы, чтобы `kill(-pid)` killTree() доставал
175
+ // всю цепочку (npx → node → tsserver). Без этого групповой kill падает
176
+ // с ESRCH, а внуки выживают сиротами. Windows — taskkill /T.
176
177
  detached: !isWin,
177
178
  };
178
179
  if (isWin) {
179
- // A `.cmd` shim can't be spawned directly under Node (`spawn npx EINVAL`) — run via cmd.exe.
180
+ // `.cmd`-шим напрямую под Node не спавнится (`spawn npx EINVAL`) — через cmd.exe.
180
181
  spawnCommand = buildWinCommandLine(spawnCommand, args);
181
182
  spawnArgs = [];
182
183
  spawnOpts.shell = true;
@@ -210,7 +211,7 @@ export class LspClient {
210
211
  const headerEnd = this.buffer.indexOf("\r\n\r\n");
211
212
  if (headerEnd === -1)
212
213
  return;
213
- // Headers are ASCII — decode just the header slice.
214
+ // Заголовки — ASCII: декодим только срез заголовка.
214
215
  const header = this.buffer.subarray(0, headerEnd).toString("utf8");
215
216
  const match = header.match(/Content-Length: (\d+)/i);
216
217
  if (!match) {
@@ -218,7 +219,7 @@ export class LspClient {
218
219
  continue;
219
220
  }
220
221
  this.contentLength = parseInt(match[1], 10);
221
- // Guard against malicious/buggy servers sending absurd Content-Length
222
+ // Защита от злонамеренных/битых серверов с абсурдным Content-Length
222
223
  if (this.contentLength > 10 * 1024 * 1024) {
223
224
  this.buffer = this.buffer.subarray(headerEnd + 4);
224
225
  this.contentLength = -1;
@@ -226,9 +227,9 @@ export class LspClient {
226
227
  }
227
228
  this.buffer = this.buffer.subarray(headerEnd + 4);
228
229
  }
229
- // Content-Length counts BYTES, not JS characters. Slicing a decoded
230
- // string by character count corrupts frames containing multi-byte
231
- // UTF-8 (e.g. Cyrillic diagnostics) — keep the remainder as raw bytes.
230
+ // Content-Length считает БАЙТЫ, не JS-символы. Нарезка декодированной
231
+ // строки по числу символов портит фреймы с многобайтным UTF-8
232
+ // (напр. кириллические диагностики) — остаток держим сырыми байтами.
232
233
  if (this.buffer.length < this.contentLength)
233
234
  return;
234
235
  const body = this.buffer.subarray(0, this.contentLength).toString("utf8");
@@ -306,10 +307,9 @@ export class LspClient {
306
307
  async shutdown() {
307
308
  try {
308
309
  if (this.process && this.initialized && this.process.stdin?.writable) {
309
- // Await the shutdown response (with timeout) so the server has time to
310
- // acknowledge before we send exit and kill the process. The old code
311
- // fired shutdown as fire-and-forget, then killed immediately — the
312
- // server never had a chance to reply, producing spurious timeout warnings.
310
+ // Ответ shutdown ждём (с таймаутом), чтобы сервер успел подтвердить до
311
+ // exit и kill. Старый код слал shutdown fire-and-forget и тут же убивал —
312
+ // сервер ответить не успевал, сыпались ложные timeout-предупреждения.
313
313
  try {
314
314
  await this.sendRequest("shutdown", null, 3000);
315
315
  }
@@ -325,9 +325,9 @@ export class LspClient {
325
325
  }
326
326
  finally {
327
327
  if (this.process) {
328
- // The direct child on win32 is the cmd.exe wrapper; killing it alone
329
- // orphans the npx → node → tsserver tree (observed as leaked node
330
- // processes after every check). Tree-kill so the whole chain dies.
328
+ // Прямой потомок на win32 — cmd.exe-обёртка; её убийство в одиночку
329
+ // сиротит дерево npx → node → tsserver (наблюдались текущие node после
330
+ // каждого чека). Tree-kill — умирает вся цепочка.
331
331
  try {
332
332
  killTree(this.process);
333
333
  }
@@ -374,11 +374,11 @@ export class LspClient {
374
374
  return map[ext ?? ""] ?? "plaintext";
375
375
  }
376
376
  /**
377
- * Extract the actual binary name from npx args — only for the strict layout
378
- * `[--yes] [--package X]... <binary> --stdio`. Servers with trailing server
379
- * arguments (e.g. angular's `ngserver --stdio --tsProbeLocations …`) return
380
- * null and always run through npx, so their flags are never mistaken for the
381
- * binary or stripped.
377
+ * Имя реального бинарника из npx-args — только строгий layout
378
+ * `[--yes] [--package X]... <binary> --stdio`. Серверы с хвостовыми
379
+ * серверными аргументами (напр. angular `ngserver --stdio --tsProbeLocations …`)
380
+ * дают null и всегда идут через npx — их флаги не принять за бинарник
381
+ * и не вырезать.
382
382
  */
383
383
  extractBinaryFromNpxArgs(args) {
384
384
  const stdioIdx = args.lastIndexOf("--stdio");
@@ -399,7 +399,7 @@ export class LspClient {
399
399
  }
400
400
  return binary;
401
401
  }
402
- /** Check if a string looks like a package version specifier (e.g. "typescript@5"). */
402
+ /** Похоже ли на спецификатор версии пакета (напр. "typescript@5"). */
403
403
  isPackageVersion(s) {
404
404
  return /^[\w-]+@[\d.+*-]/.test(s);
405
405
  }
@@ -1,23 +1,20 @@
1
+ // Запуск LSP-серверов на Windows: резолв shim (.cmd/.bat) и строка для cmd.exe.
1
2
  import { delimiter, join } from "path";
2
3
  import { existsSync } from "fs";
3
4
  import { platform } from "os";
4
5
  const WIN_EXTS = [".cmd", ".bat", ".exe", ".com"];
5
6
  /**
6
- * Resolve the command string passed to `child_process.spawn` on Windows.
7
- *
8
- * Node's `spawn` cannot execute `.cmd`/`.bat` shims directly without a shell,
9
- * and a bare `npx` fails with `spawn npx ENOENT` because on Windows the
10
- * real executable is `npx.cmd`. This returns the first existing shim
11
- * (`npx.cmd`, `npx.bat`, `npx.exe`, ...) found on PATH, or the bare command
12
- * when nothing exists (the spawn will then fail with a meaningful ENOENT).
13
- *
14
- * Non-Windows platforms and commands that already carry an extension or a
15
- * path separator are returned unchanged.
7
+ * Резолв команды для `child_process.spawn` на Windows.
8
+ * Node-`spawn` не исполняет `.cmd`/`.bat`-шимы напрямую без shell, а голый
9
+ * `npx` падает с `spawn npx ENOENT` — на Windows реальный бинарник это
10
+ * `npx.cmd`. Возвращает первый найденный на PATH шим (`npx.cmd`, `npx.bat`,
11
+ * `npx.exe`, …), а если ничего нет — голую команду (spawn упадёт с понятным
12
+ * ENOENT). Не-Windows и команды с расширением/сепаратором — как есть.
16
13
  */
17
14
  export function resolveSpawnCommand(command, platformName = platform(), pathEnv = process.env.PATH ?? "") {
18
15
  if (platformName !== "win32")
19
16
  return command;
20
- // Already a path or has an extension — pass through.
17
+ // Уже путь или с расширением — пропустить как есть.
21
18
  if (command.includes("/") ||
22
19
  command.includes("\\") ||
23
20
  WIN_EXTS.some((ext) => command.toLowerCase().endsWith(ext))) {
@@ -34,19 +31,15 @@ export function resolveSpawnCommand(command, platformName = platform(), pathEnv
34
31
  return command;
35
32
  }
36
33
  /**
37
- * Join a resolved command and its args into a single cmd.exe command line.
38
- *
39
- * Under Node on Windows, `spawn` of a `.cmd`/`.bat` shim WITHOUT a shell throws
40
- * `spawn EINVAL` (CreateProcess cannot run batch files directly). Resolving
41
- * `npx` → `npx.cmd` fixes the old `ENOENT` but not the `EINVAL` — the spawn
42
- * still has no shell. The reliable fix is to run the whole command line through
43
- * `cmd.exe /d /s /c` (i.e. `spawn(line, { shell: true })`). This helper builds
44
- * that line, quoting any argument that contains cmd.exe metacharacters so the
45
- * package names and flags in LSP configs (`typescript-language-server --stdio`,
46
- * `pyright --stdio`) survive the round-trip.
47
- *
48
- * Non-Windows platforms never call this (they spawn the resolved command +
49
- * args directly, no shell).
34
+ * Склейка резолвленной команды и args в одну строку для cmd.exe.
35
+ * Под Node на Windows `spawn` `.cmd`/`.bat`-шима БЕЗ shell бросает
36
+ * `spawn EINVAL` (CreateProcess не запускает батники напрямую). Резолв
37
+ * `npx` → `npx.cmd` чинит старый `ENOENT`, но не `EINVAL` — shell'а всё равно
38
+ * нет. Надёжный фикс — гнать всю строку через `cmd.exe /d /s /c`
39
+ * (т.е. `spawn(line, { shell: true })`). Хелпер строит строку, квотируя
40
+ * аргументы с cmd-метасимволами, чтобы имена пакетов и флаги LSP-конфигов
41
+ * (`typescript-language-server --stdio`, `pyright --stdio`) пережили round-trip.
42
+ * Не-Windows сюда не ходят (спавнят команду + args напрямую, без shell).
50
43
  */
51
44
  export function buildWinCommandLine(command, args) {
52
45
  const quote = (part) => {
@@ -1,11 +1,11 @@
1
+ // Дефолтные LSP-серверы и выбор сервера по файлу (язык, Angular-исключение).
1
2
  import { existsSync } from "fs";
2
3
  import { dirname, join } from "path";
3
4
  /**
4
- * LSP server defaults.
5
- *
6
- * Strategy: prefer `npx` (auto-downloads) over globally installed binaries.
7
- * Servers using system tools (rust-analyzer, gopls) have `autoInstall: false`
8
- * and are skipped silently when not found.
5
+ * Дефолты LSP-серверов.
6
+ * Стратегия: предпочитать `npx` (самодокачка) глобальным бинарникам.
7
+ * Серверы на системных тулах (rust-analyzer, gopls) — `autoInstall: false`,
8
+ * при отсутствии молча скипаются.
9
9
  */
10
10
  export const DEFAULT_LSP_CONFIG = {
11
11
  enabled: true,
@@ -141,7 +141,7 @@ const LANGUAGE_MAP = {
141
141
  css: "css",
142
142
  scss: "css",
143
143
  };
144
- /** Language id for a file extension (via LANGUAGE_MAP), or null when unsupported. */
144
+ /** Id языка по расширению файла (через LANGUAGE_MAP), неподдерживаемое — null. */
145
145
  export function getLanguageForFile(filePath) {
146
146
  const ext = filePath.split(".").pop()?.toLowerCase();
147
147
  if (!ext)
@@ -151,8 +151,8 @@ export function getLanguageForFile(filePath) {
151
151
  const ANGULAR_MARKER = "angular.json";
152
152
  const ANGULAR_LOOKUP_DEPTH = 10;
153
153
  /**
154
- * True when the file lives inside an Angular workspace (an `angular.json`
155
- * marker exists in one of its ancestor directories, bounded lookup).
154
+ * Лежит ли файл внутри Angular-workspace (маркер `angular.json` в одном из
155
+ * предков, поиск ограничен глубиной).
156
156
  */
157
157
  function isAngularProject(filePath) {
158
158
  let dir = dirname(filePath);
@@ -167,9 +167,9 @@ function isAngularProject(filePath) {
167
167
  return false;
168
168
  }
169
169
  /**
170
- * Config key of the LSP server for a file. `.html` routes to the `angular`
171
- * server inside Angular workspaces (templates with bindings need the Angular
172
- * language service) and to the generic `html` server everywhere else.
170
+ * Ключ конфига LSP-сервера для файла. `.html` идёт на сервер `angular` внутри
171
+ * Angular-workspace (шаблонам с биндингами нужен Angular language service),
172
+ * везде иначе — на generic `html`.
173
173
  */
174
174
  export function getServerKeyForFile(filePath, config) {
175
175
  const lang = getLanguageForFile(filePath);
@@ -180,6 +180,7 @@ export function getServerKeyForFile(filePath, config) {
180
180
  }
181
181
  return lang;
182
182
  }
183
+ /** Конфиг LSP-сервера для файла (null — не поддерживается). */
183
184
  export function getServerForFile(filePath, config) {
184
185
  const key = getServerKeyForFile(filePath, config);
185
186
  if (!key)
@@ -1,3 +1,4 @@
1
+ // Публичный фасад модуля LSP: модуль, клиент, конфиг, типы.
1
2
  export { LspModule } from "./module";
2
3
  export { LspClient, FileOnlyLogger } from "./client";
3
4
  export { getServerForFile, DEFAULT_LSP_CONFIG } from "./config";
@@ -12,23 +12,22 @@ import { severityLabels, collectCheckFiles, formatCheckDiagnostics } from "./che
12
12
  import { errMsg } from "../../utils";
13
13
  import { join } from "path";
14
14
  /**
15
- * Consecutive LSP failures before a server is disabled for the session. A dead
16
- * server (not installed, npx auto-install failed, path missing) otherwise
17
- * spams `[LSP check failed]` on EVERY write, teaching the model to ignore the
18
- * signal entirely (observed: ses_mst2r0r5 had LSP dead all session and the
19
- * model never reacted). After the threshold we say so once and stop the noise
20
- * for that server — a CSS timeout must not take TypeScript checks down too.
15
+ * Подряд идущих падений LSP до отключения сервера на сессию. Мёртвый сервер
16
+ * (не стоит, упала npx-автоустановка, нет пути) иначе спамит `[LSP check failed]`
17
+ * на КАЖДУЮ запись, приучая модель игнорировать сигнал целиком (наблюдалось:
18
+ * ses_mst2r0r5 — LSP мёртв всю сессию, модель не реагировала). После порога —
19
+ * одно громкое сообщение и тишина по этому серверу: таймаут CSS не должен
20
+ * ронять проверки TypeScript.
21
21
  */
22
22
  const MAX_CONSECUTIVE_LSP_FAILURES = 3;
23
23
  /**
24
- * Module-resolution diagnostics that are meaningless without a type
25
- * environment. In a bare directory (no tsconfig.json / node_modules) the
26
- * TypeScript server reports `Cannot find module 'fs'` and
27
- * `Cannot find name '__dirname'` for perfectly valid Node code — the model
28
- * then abandons a working approach because of phantom errors (observed
29
- * ses_mtho2oab: LSP "errors" pushed the model off a planned .ts file to .js
30
- * and into a plan-drift spiral). Suppressed with an explicit inconclusive
31
- * note when no type env exists near the project root.
24
+ * Диагностики резолва модулей, бессмысленные без type-окружения. В голом
25
+ * каталоге (без tsconfig.json / node_modules) TS-сервер рапортует
26
+ * `Cannot find module 'fs'` и `Cannot find name '__dirname'` на валидный
27
+ * Node-код — модель бросает рабочий подход из-за фантомных ошибок (наблюдалось
28
+ * ses_mtho2oab: LSP-"ошибки" столкнули модель с планового .ts на .js и в
29
+ * спираль plan-drift). Душатся с явной пометкой inconclusive, если рядом с
30
+ * корнем проекта нет type-окружения.
32
31
  */
33
32
  const TYPE_ENV_DIAG_PATTERN = /Cannot find module ['"][^'"]+['"] or its corresponding type declarations|Cannot find name '(__dirname|__filename|require|process|Buffer|console)'|Do you need to install type definitions/;
34
33
  function hasTypeEnvironment(projectRoot) {
@@ -36,6 +35,7 @@ function hasTypeEnvironment(projectRoot) {
36
35
  existsSync(join(projectRoot, "jsconfig.json")) ||
37
36
  existsSync(join(projectRoot, "node_modules")));
38
37
  }
38
+ /** Модуль LSP: пост-проверки записи, тул lsp_check, учёт мёртвых серверов. */
39
39
  export class LspModule {
40
40
  name = "lsp";
41
41
  config;
@@ -49,43 +49,42 @@ export class LspModule {
49
49
  this.logger = logger ?? null;
50
50
  this.client.setLogger(this.logger);
51
51
  }
52
- /** Update the logger (e.g., after session switch). */
52
+ /** Обновить логгер (напр. после смены сессии). */
53
53
  setLogger(logger) {
54
54
  this.logger = logger;
55
55
  this.client.setLogger(logger);
56
56
  }
57
- /** Reset disabled servers (e.g., after /lsp restart). */
57
+ /** Сбросить отключённые серверы (напр. после /lsp restart). */
58
58
  resetDisabledServers() {
59
59
  this.failuresByServer.clear();
60
60
  this.disabledServers.clear();
61
61
  }
62
- /** Get list of disabled server keys. */
62
+ /** Список отключённых ключей серверов. */
63
63
  getDisabledServers() {
64
64
  return Array.from(this.disabledServers);
65
65
  }
66
- /** Get failure counts per server. */
66
+ /** Счётчики падений по серверам. */
67
67
  getFailureCounts() {
68
68
  return new Map(this.failuresByServer);
69
69
  }
70
- /** Test hook: has the module disabled any LSP server for the session? */
70
+ /** Тест-хук: отключал ли модуль какой-то LSP-сервер на сессии. */
71
71
  isLspDisabled() {
72
72
  return this.disabledServers.size > 0;
73
73
  }
74
- /** Test hook: is the given server (config-derived key) disabled? */
74
+ /** Тест-хук: отключён ли сервер (ключ из конфига). */
75
75
  isServerDisabled(key) {
76
76
  return this.disabledServers.has(key);
77
77
  }
78
- /** Stable server identity derived from the config. */
78
+ /** Стабильная идентичность сервера из конфига. */
79
79
  serverKey(server) {
80
80
  return `${server.command}:${(server.args ?? []).join(" ")}`;
81
81
  }
82
82
  getPlugin() {
83
- const self = this;
84
83
  return {
85
84
  name: "lsp-check",
86
85
  priority: 15,
87
86
  onAfterTool: async (_ctx, call, result) => {
88
- if (!self.config.enabled)
87
+ if (!this.config.enabled)
89
88
  return;
90
89
  if (call.name !== "write_file" && call.name !== "edit_file")
91
90
  return;
@@ -97,16 +96,16 @@ export class LspModule {
97
96
  const fullPath = resolve(_ctx.baseDir, filePath);
98
97
  if (!existsSync(fullPath))
99
98
  return;
100
- const serverConfig = getServerForFile(fullPath, self.config);
99
+ const serverConfig = getServerForFile(fullPath, this.config);
101
100
  if (!serverConfig)
102
101
  return;
103
- const key = self.serverKey(serverConfig);
104
- if (self.disabledServers.has(key))
102
+ const key = this.serverKey(serverConfig);
103
+ if (this.disabledServers.has(key))
105
104
  return;
106
105
  const projectRoot = findProjectRoot(fullPath, _ctx.baseDir, serverConfig.workspaceMarkers ?? []);
107
106
  try {
108
- const diagnostics = await self.client.checkFile(fullPath, _ctx.baseDir, serverConfig, projectRoot);
109
- self.failuresByServer.set(key, 0);
107
+ const diagnostics = await this.client.checkFile(fullPath, _ctx.baseDir, serverConfig, projectRoot);
108
+ this.failuresByServer.set(key, 0);
110
109
  const errors = diagnostics.filter((d) => d.severity === 1);
111
110
  const warnings = diagnostics.filter((d) => d.severity === 2);
112
111
  if (errors.length > 0) {
@@ -119,20 +118,22 @@ export class LspModule {
119
118
  }
120
119
  }
121
120
  catch (e) {
122
- // Surface failures instead of silently returning [] — a dead LSP
123
- // previously produced ZERO diagnostics across whole sessions and the
124
- // model only found type errors reactively via `bun run build`.
121
+ // Падения — наружу, а не молчаливый []: мёртвый LSP раньше давал
122
+ // НОЛЬ диагностик целыми сессиями, и модель находила ошибки типов
123
+ // лишь реактивно через `bun run build`.
125
124
  const msg = errMsg(e);
126
- const failures = (self.failuresByServer.get(key) ?? 0) + 1;
127
- self.failuresByServer.set(key, failures);
125
+ const failures = (this.failuresByServer.get(key) ?? 0) + 1;
126
+ this.failuresByServer.set(key, failures);
128
127
  try {
129
128
  _ctx.logger?.warn(`LSP check failed for ${filePath}: ${msg}`);
130
129
  }
131
- catch { }
130
+ catch {
131
+ // Падающий логгер не должен ломать путь проверки.
132
+ }
132
133
  if (failures >= MAX_CONSECUTIVE_LSP_FAILURES) {
133
- // One loud, actionable message, then stop the per-write noise for
134
- // the rest of the session. The model must not learn to ignore LSP.
135
- self.disabledServers.add(key);
134
+ // Одно громкое actionable-сообщение, дальше — без шума на каждую
135
+ // запись до конца сессии. Модель не должна учиться игнорить LSP.
136
+ this.disabledServers.add(key);
136
137
  result.output += `\n\n[LSP disabled: ${t("lsp.unavailable")}]`;
137
138
  }
138
139
  else {
@@ -143,15 +144,14 @@ export class LspModule {
143
144
  };
144
145
  }
145
146
  /**
146
- * On-demand diagnostics tool. LSP checks only ran after write/edit (the
147
- * plugin above), so a read-only "check this project for errors" task got
148
- * ZERO compiler feedback — the model scanned files with read_file and never
149
- * learned whether the code compiled (observed: ses_msvuao0h — a "проверь на
150
- * ошибки" task ran no build, no lsp, and declared success). `lsp_check`
151
- * gives the model a way to ask for static diagnostics on demand.
147
+ * Тул диагностики по требованию. Проверки LSP шли только после write/edit
148
+ * (плагин выше), и read-only задача «проверь проект на ошибки» получала
149
+ * НОЛЬ фидбэка от компилятора — модель сканировала файлы через read_file и
150
+ * не узнавала, компилируется ли код (наблюдалось: ses_msvuao0h — задача
151
+ * "проверь на ошибки" без build, без lsp, с декларацией успеха). `lsp_check`
152
+ * даёт модели способ спросить статические диагностики по требованию.
152
153
  */
153
154
  getToolDefinitions() {
154
- const self = this;
155
155
  return [
156
156
  {
157
157
  name: "lsp_check",
@@ -171,7 +171,7 @@ export class LspModule {
171
171
  },
172
172
  required: ["path"],
173
173
  },
174
- handler: async (ctx, args) => self.runCheck(ctx, args),
174
+ handler: async (ctx, args) => this.runCheck(ctx, args),
175
175
  },
176
176
  ];
177
177
  }
@@ -240,7 +240,9 @@ export class LspModule {
240
240
  try {
241
241
  ctx.logger?.warn(`LSP check failed for ${file}: ${msg}`);
242
242
  }
243
- catch { }
243
+ catch {
244
+ // Падающий логгер не должен ломать путь проверки.
245
+ }
244
246
  if (failures >= MAX_CONSECUTIVE_LSP_FAILURES) {
245
247
  this.disabledServers.add(key);
246
248
  notes.push(`[LSP disabled: ${t("lsp.unavailable")}]`);
@@ -3,13 +3,12 @@ import { getServerKeyForFile } from "./config";
3
3
  import { collectCheckFiles } from "./check-tool";
4
4
  import { findProjectRoot } from "./project-root";
5
5
  import { errMsg } from "../../utils";
6
- const DEFAULT_PROBE_TIMEOUT_MS = 30_000;
7
- const DEFAULT_PROBE_DEADLINE_MS = 60_000;
6
+ import { LSP_PROBE_TIMEOUT_MS as DEFAULT_PROBE_TIMEOUT_MS, LSP_PROBE_DEADLINE_MS as DEFAULT_PROBE_DEADLINE_MS, } from "../../config/timeouts";
8
7
  const DEFAULT_MAX_PARALLEL = 3;
9
8
  /**
10
- * Check which LSP servers apply to the project (via the same bounded file
11
- * walk as lsp_check) and probe each one: spawn + initialize + shutdown.
12
- * Used by the REPL banner so a dead server is visible before the first write.
9
+ * Выяснить, какие LSP-серверы применимы к проекту (тот же ограниченный обход,
10
+ * что в lsp_check), и пробить каждый: spawn + initialize + shutdown.
11
+ * Для баннера REPL — мёртвый сервер виден до первой записи.
13
12
  */
14
13
  export async function probeLspServers(config, baseDir, deps = {}) {
15
14
  if (!config.enabled)
@@ -33,15 +32,15 @@ export async function probeLspServers(config, baseDir, deps = {}) {
33
32
  }
34
33
  if (servers.length === 0)
35
34
  return { ok: [], failed: [], noneApplicable: true };
36
- // Probe in bounded waves. Probing everything in parallel on a cold npx cache
37
- // makes the servers fight over the shared cache lock — observed as `LSP
38
- // request timeout: initialize` for half the servers. Fully sequential probing
39
- // avoids that but doubles the REPL banner wait (each server ~2s). A wave of
40
- // `maxParallel` keeps the banner fast on a warm cache while capping cold-cache
41
- // contention; each server in a wave gets its own LspClient (the client is
42
- // stateful — process, request ids, response buffer — so concurrent probes on
43
- // a shared instance would cross-talk). When a client is injected (tests), it
44
- // is reused for every server.
35
+ // Проба ограниченными волнами. Параллельная проба всего на холодном
36
+ // npx-кеше заставляет серверы драться за общий лок кеша — наблюдалось как
37
+ // `LSP request timeout: initialize` у половины серверов. Чисто
38
+ // последовательная проба убирает драку, но удваивает ожидание баннера REPL
39
+ // (сервер ~2с). Волна `maxParallel` держит баннер быстрым на тёплом кеше и
40
+ // режет конкуренцию на холодном; у каждого сервера в волне свой LspClient
41
+ // (клиент stateful — процесс, id запросов, буфер ответов, — конкурентные
42
+ // пробы на общем инстансе кросс-токали бы). Инжектированный клиент (тесты)
43
+ // переиспользуется на все серверы.
45
44
  const ok = [];
46
45
  const failed = [];
47
46
  const deadline = Date.now() + deadlineMs;