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
@@ -4,30 +4,31 @@ import { resolve, extname, join } from "path";
4
4
  import { platform } from "os";
5
5
  import { findProjectRoot, resolveTscCommand } from "../../lsp/project-root";
6
6
  import { autoFixErrors, parseTscOutput } from "../../../tools/auto-fixer";
7
- const TYPE_CHECK_DEBOUNCE_MS = 2000;
8
- // Projects whose lint script points at a binary that is not installed
9
- // (keyed by baseDir::script). The run itself is expensive and the failure
10
- // marker is pure noise — skip for the rest of the process.
7
+ import { LINT_TYPE_CHECK_DEBOUNCE_MS as TYPE_CHECK_DEBOUNCE_MS } from "../../../config/timeouts";
8
+ import { settleChildClose } from "../../../utils/child-run";
9
+ // Проекты, чей lint-скрипт ссылается на неустановленный бинарник
10
+ // (ключ baseDir::script). Сам прогон дорог, а маркер провала — чистый шум,
11
+ // поэтому пропускаем до конца процесса.
11
12
  const missingLintBinaries = new Set();
12
13
  function lintCacheKey(baseDir, lintScript) {
13
14
  return `${baseDir}::${lintScript}`;
14
15
  }
15
- // Shell-level "command not found" across locales/runtimes: POSIX `sh` prints
16
- // "command not found" (exit 127); Windows cmd.exe prints "is not recognized"
17
- // (en) / "не является внутренней или внешней командой" (ru) with exit 1/9009.
16
+ // Шелл-уровень "command not found" по локалям/рантаймам: POSIX `sh` пишет
17
+ // "command not found" (exit 127); Windows cmd.exe — "is not recognized"
18
+ // (en) / "не является внутренней или внешней командой" (ru), exit 1/9009.
18
19
  const MISSING_BINARY_RE = /command not found|not recognized|не является внутренней|не распознан|не є внутрішньою|".+?" не является/i;
19
20
  function isMissingBinaryError(err, combinedOutput) {
20
21
  // 127 = POSIX sh "command not found"; 9009 = Windows cmd.exe "is not
21
- // recognized" — both are authoritative regardless of output encoding.
22
+ // recognized" — оба авторитетны независимо от кодировки вывода.
22
23
  if (err.status === 127 || err.status === 9009)
23
24
  return true;
24
25
  if (err.code === "ENOENT")
25
26
  return true;
26
27
  return MISSING_BINARY_RE.test(combinedOutput);
27
28
  }
28
- // Per-file syntax checks are cached by (path, content hash): re-writing a file
29
- // with identical content skips the (expensive) check. Content changes → cache
30
- // miss → fresh check.
29
+ // Пофайловые синтаксис-проверки кешируются по (путь, хеш содержимого): перезапись
30
+ // файла с тем же содержимым скипает (дорогую) проверку. Смена контента →
31
+ // промах кеша → свежая проверка.
31
32
  const syntaxCache = new Map();
32
33
  function contentHash(content) {
33
34
  let h = 5381;
@@ -38,10 +39,10 @@ function contentHash(content) {
38
39
  }
39
40
  let _winDecoder;
40
41
  /**
41
- * OEM decoder for the active console code page (CP866 on Russian Windows).
42
- * Mirrors `getOemDecoder()` in modules/processes/registry.ts: a transient
43
- * chcp.com failure under load must NOT be cached — only a successful probe
44
- * (or the hard ibm866 fallback) is memoized.
42
+ * OEM-декодер активной консольной кодовой страницы (CP866 на русской Windows).
43
+ * Зеркалит `getOemDecoder()` из modules/processes/registry.ts: транзиентный
44
+ * провал chcp.com под нагрузкой НЕ кешируется — мемоизируется только успешный
45
+ * проб (или жёсткий фолбэк ibm866).
45
46
  */
46
47
  function getWinDecoder() {
47
48
  if (_winDecoder !== undefined)
@@ -59,26 +60,26 @@ function getWinDecoder() {
59
60
  }).toString("latin1");
60
61
  const m = cpOut.match(/(\d+)/);
61
62
  if (m)
62
- decoder = new TextDecoder("ibm" + m[1]);
63
+ decoder = new TextDecoder(`ibm${m[1]}`);
63
64
  }
64
65
  catch {
65
- /* chcp failed (load/ENOENT) — retry on the next call */
66
+ /* chcp упал (нагрузка/ENOENT) — повторим при следующем вызове */
66
67
  }
67
68
  if (!decoder) {
68
69
  try {
69
70
  decoder = new TextDecoder("ibm866");
70
71
  }
71
72
  catch {
72
- return new TextDecoder("utf-8"); // unknown encoding — do not cache
73
+ return new TextDecoder("utf-8"); // неизвестная кодировка — не кешируем
73
74
  }
74
75
  }
75
76
  _winDecoder = decoder;
76
77
  return _winDecoder;
77
78
  }
78
79
  /**
79
- * Decode child-process output to UTF-8. Modern tools (bun, node, npm, tsc)
80
- * write UTF-8 into the pipe; cmd.exe builtins/errors use the OEM code page.
81
- * Strict UTF-8 first; on invalid byte sequences fall back to OEM.
80
+ * Декодирует вывод дочернего процесса в UTF-8. Современные тулы (bun, node,
81
+ * npm, tsc) пишут UTF-8 в пайп; встроенные команды/ошибки cmd.exe — OEM-страницу.
82
+ * Сначала строгий UTF-8; на невалидных байтах — откат на OEM.
82
83
  */
83
84
  function decodeOutput(chunks) {
84
85
  const raw = Buffer.concat(chunks);
@@ -86,7 +87,7 @@ function decodeOutput(chunks) {
86
87
  return new TextDecoder("utf-8", { fatal: true }).decode(raw);
87
88
  }
88
89
  catch {
89
- /* not valid UTF-8 → OEM code page output */
90
+ /* невалидный UTF-8 → вывод в OEM-кодировке */
90
91
  }
91
92
  try {
92
93
  return getWinDecoder().decode(raw);
@@ -95,12 +96,15 @@ function decodeOutput(chunks) {
95
96
  return raw.toString("latin1");
96
97
  }
97
98
  }
99
+ /** Встроенный плагин: проверяет каждую запись/правку файла (синтаксис, lint, типы). */
98
100
  export class LintOnWritePlugin {
99
101
  name = "lint-on-write";
100
102
  priority = 10;
101
103
  isBuiltin = true;
102
- _checkPromise = null;
103
- _checkTimestamp = 0;
104
+ // Дебаунснутый проектный typecheck по корню проекта. Один общий слот
105
+ // протекал tsc-ошибками одного проекта в записи другого (монорепо).
106
+ _checks = new Map();
107
+ /** После записи/правки: синтаксис файла, затем проектные lint и typecheck. */
104
108
  async onAfterTool(ctx, call, result) {
105
109
  if (call.name !== "write_file" && call.name !== "edit_file") {
106
110
  return;
@@ -132,9 +136,9 @@ export class LintOnWritePlugin {
132
136
  }
133
137
  async checkSyntax(filePath, ext, baseDir, signal) {
134
138
  if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
135
- // Syntax-only check via bun's parser (~200ms) instead of `npx tsc`
136
- // (~3-5s per write). Full type errors are still surfaced by the
137
- // debounced project typecheck below when a tsconfig exists.
139
+ // Проверка только синтаксиса парсером bun (~200мс) вместо `npx tsc`
140
+ // (~3-5с на запись). Полные ошибки типов всё равно отдаёт дебаунснутый
141
+ // проектный typecheck ниже, когда есть tsconfig.
138
142
  let content = "";
139
143
  try {
140
144
  content = readFileSync(filePath, "utf-8");
@@ -148,8 +152,8 @@ export class LintOnWritePlugin {
148
152
  return cached.error;
149
153
  }
150
154
  try {
151
- await runAsync(`bun build --no-bundle --target=bun "${filePath}"`, baseDir, 10000, signal);
152
- // Aborted check (interrupt) never validated the file — don't cache it.
155
+ await runAsync({ file: "bun", args: ["build", "--no-bundle", "--target=bun", filePath] }, baseDir, 10000, signal);
156
+ // Прерванная проверка (interrupt) файл не валидировала — не кешируем.
153
157
  if (signal?.aborted)
154
158
  return null;
155
159
  syntaxCache.set(filePath, { hash, error: null });
@@ -169,7 +173,7 @@ export class LintOnWritePlugin {
169
173
  }
170
174
  if (ext === ".js" || ext === ".jsx" || ext === ".cjs" || ext === ".mjs") {
171
175
  try {
172
- await runAsync(`node --check "${filePath}"`, baseDir, 5000, signal);
176
+ await runAsync({ file: "node", args: ["--check", filePath] }, baseDir, 5000, signal);
173
177
  return null;
174
178
  }
175
179
  catch (err) {
@@ -192,10 +196,10 @@ export class LintOnWritePlugin {
192
196
  if (!lintScript) {
193
197
  return;
194
198
  }
195
- // A declared-but-missing linter binary ("eslint" not installed) is a
196
- // project-setup gap, not a code problem — appending it to every write
197
- // result polluted the context ~25×/session (ses_mt4fn58c). Detect it
198
- // once and skip silently for the rest of the process.
199
+ // Объявленный, но неустановленный бинарник линтера ("eslint" не стоит) —
200
+ // пробел проектного сетапа, а не проблема кода: дописывание его к каждой
201
+ // записи засоряло контекст ~25×/сессию (ses_mt4fn58c). Детектим раз
202
+ // и дальше скипаем молча до конца процесса.
199
203
  if (missingLintBinaries.has(lintCacheKey(ctx.baseDir, lintScript))) {
200
204
  ctx.logger.debug(`Lint skipped (linter for "${lintScript}" not installed)`);
201
205
  return;
@@ -224,10 +228,10 @@ export class LintOnWritePlugin {
224
228
  }
225
229
  }
226
230
  async runProjectTypeCheck(filePath, baseDir, result, signal) {
227
- // Resolve the project root from the EDITED FILE, not the agent baseDir.
228
- // In a nested layout (`baseDir/proj/tsconfig.json`) the old baseDir-based
229
- // lookup silently skipped the typecheck on every write — the whole
230
- // session then ran with zero type feedback (observed in ses_mst2r0r5).
231
+ // Корень проекта резолвим от РЕДАКТИРОВАННОГО ФАЙЛА, а не от baseDir агента.
232
+ // Во вложенной раскладке (`baseDir/proj/tsconfig.json`) старый поиск от
233
+ // baseDir молча скипал typecheck на каждой записи — вся сессия шла
234
+ // вообще без типового фидбэка (наблюдено в ses_mst2r0r5).
231
235
  const projectRoot = findProjectRoot(filePath, baseDir, ["tsconfig.json", "package.json"]);
232
236
  const tsconfigPath = join(projectRoot, "tsconfig.json");
233
237
  if (!existsSync(tsconfigPath)) {
@@ -239,28 +243,29 @@ export class LintOnWritePlugin {
239
243
  return;
240
244
  }
241
245
  const now = Date.now();
242
- if (this._checkPromise && now - this._checkTimestamp < TYPE_CHECK_DEBOUNCE_MS) {
243
- const error = await this._checkPromise;
246
+ const prev = this._checks.get(projectRoot);
247
+ if (prev && now - prev.at < TYPE_CHECK_DEBOUNCE_MS) {
248
+ const error = await prev.promise;
244
249
  if (error) {
245
250
  result.output += `\n\n[Project typecheck failed]: ${error}`;
246
251
  }
247
252
  return;
248
253
  }
249
- this._checkTimestamp = now;
250
- this._checkPromise = this.runTscCheck(tsc, projectRoot, signal);
251
- const error = await this._checkPromise;
254
+ const promise = this.runTscCheck(tsc, projectRoot, signal);
255
+ this._checks.set(projectRoot, { at: now, promise });
256
+ const error = await promise;
252
257
  if (error) {
253
- // Try to auto-fix fixable errors
258
+ // Пробуем автофиксить чинимые ошибки
254
259
  const { stdout, stderr } = await runAsync(`${tsc} --noEmit --skipLibCheck 2>&1`, projectRoot, 30000, signal).catch(() => ({ stdout: "", stderr: error }));
255
260
  const output = stderr || stdout;
256
261
  const errors = parseTscOutput(output);
257
262
  if (errors.length > 0) {
258
263
  const fixResult = autoFixErrors(filePath, readFileSync(filePath, "utf-8"), errors);
259
264
  if (fixResult.fixed) {
260
- // Write the fixed content
265
+ // Пишем починенное содержимое
261
266
  writeFileSync(filePath, fixResult.newContent, "utf-8");
262
267
  result.output += `\n\n[Auto-fixed]: ${fixResult.applied.join("; ")}`;
263
- // If there are remaining errors, show them
268
+ // Если остались ошибки — показываем их
264
269
  if (fixResult.remaining.length > 0) {
265
270
  const remainingMsg = fixResult.remaining
266
271
  .map((e) => `TS${e.code}: ${e.message}`)
@@ -279,7 +284,7 @@ export class LintOnWritePlugin {
279
284
  }
280
285
  async runTscCheck(tsc, baseDir, signal) {
281
286
  try {
282
- const { stdout, stderr } = await runAsync(`${tsc} --noEmit --skipLibCheck`, baseDir, 30000, signal);
287
+ await runAsync(`${tsc} --noEmit --skipLibCheck`, baseDir, 30000, signal);
283
288
  return null;
284
289
  }
285
290
  catch (err) {
@@ -299,17 +304,29 @@ export class LintOnWritePlugin {
299
304
  }
300
305
  }
301
306
  /**
302
- * Run a command asynchronously (non-blocking event loop) and resolve when it
303
- * exits. Rejects with captured stderr/stdout on non-zero exit or timeout.
307
+ * Гоняет команду асинхронно (не стопая event loop) и резолвится на выходе.
308
+ * Реджектит с захваченными stderr/stdout при ненулевом коде или таймауте.
309
+ *
310
+ * Простая строка идёт через шелл (проектные скрипты). Спецификация
311
+ * `{file,args}` спавнится БЕЗ шелла — обязательно для всего, собранного из
312
+ * модельного пути (`bun build … <file>`, `node --check <file>`): в имени могут
313
+ * быть `$()`, бектики или `%VAR%`, а shell:true их исполнил бы (инъекция).
304
314
  */
305
315
  function runAsync(command, cwd, timeoutMs, signal) {
306
316
  return new Promise((resolve, reject) => {
307
- const child = spawn(command, {
308
- cwd,
309
- shell: true,
310
- windowsHide: true,
311
- stdio: ["ignore", "pipe", "pipe"],
312
- });
317
+ const child = typeof command === "string"
318
+ ? spawn(command, {
319
+ cwd,
320
+ shell: true,
321
+ windowsHide: true,
322
+ stdio: ["ignore", "pipe", "pipe"],
323
+ })
324
+ : spawn(command.file, command.args, {
325
+ cwd,
326
+ shell: false,
327
+ windowsHide: true,
328
+ stdio: ["ignore", "pipe", "pipe"],
329
+ });
313
330
  const utf8Chunks = [];
314
331
  const oemChunks = [];
315
332
  child.stdout?.on("data", (d) => {
@@ -322,10 +339,10 @@ function runAsync(command, cwd, timeoutMs, signal) {
322
339
  child.kill();
323
340
  reject(new Error(`Command timed out after ${timeoutMs}ms`));
324
341
  }, timeoutMs);
325
- // Interrupt (Esc/Ctrl+C): kill the child so the agent loop can return to
326
- // the prompt immediately instead of waiting out a long tsc/lint run. The
327
- // close handler resolves empty when aborted — the caller's `signal.aborted`
328
- // guard discards the result anyway.
342
+ // Прерывание (Esc/Ctrl+C): убиваем ребёнка, чтобы петля агента сразу
343
+ // вернулась в промпт, а не высиживала длинный tsc/lint. Хендлер close
344
+ // при аборте резолвит пусто — гард `signal.aborted` вызывающего результат
345
+ // всё равно отбрасывает.
329
346
  const onAbort = () => child.kill();
330
347
  if (signal?.aborted) {
331
348
  child.kill();
@@ -348,17 +365,13 @@ function runAsync(command, cwd, timeoutMs, signal) {
348
365
  signal?.removeEventListener("abort", onAbort);
349
366
  const stdout = decodeOutput(utf8Chunks);
350
367
  const stderr = decodeOutput(oemChunks);
351
- if (signal?.aborted || code === 0) {
368
+ if (signal?.aborted) {
352
369
  resolve({ stdout, stderr });
370
+ return;
353
371
  }
354
- else {
355
- const err = new Error(`Command failed with exit code ${code}`);
356
- err.stdout = stdout;
357
- err.stderr = stderr;
358
- err.status = code;
359
- reject(err);
360
- }
372
+ settleChildClose(code, stdout, stderr, `Command failed with exit code ${code}`, resolve, reject);
361
373
  });
362
374
  });
363
375
  }
376
+ /** Готовый инстанс для регистрации. */
364
377
  export const plugin = new LintOnWritePlugin();
@@ -1,3 +1,4 @@
1
+ /** Встроенный плагин: пишет в лог завершение сессии. */
1
2
  export class NotifyPlugin {
2
3
  name = "notify";
3
4
  priority = 5;
@@ -6,4 +7,5 @@ export class NotifyPlugin {
6
7
  ctx.logger.info("Session ended");
7
8
  }
8
9
  }
10
+ /** Готовый инстанс для регистрации. */
9
11
  export const plugin = new NotifyPlugin();
@@ -1 +1,2 @@
1
+ // Публичная поверхность плагинов: менеджер и тип плагина.
1
2
  export { PluginManager } from "./manager";
@@ -1,20 +1,35 @@
1
+ // Загрузчик плагинов из каталогов: файлы и папки, гейт по minMmaVersion.
1
2
  import { readdirSync, existsSync, statSync } from "fs";
2
3
  import { join, basename } from "path";
3
4
  import { t } from "../../i18n/index";
4
5
  import { semverGt } from "../updater/checker";
5
6
  /**
6
- * Entry file candidates for folder plugins. Looked up in order:
7
+ * Кандидаты точки входа для плагинов-папок. Порядок:
7
8
  * `plugin.ts` > `plugin.js` > `index.ts` > `index.js`.
8
9
  */
9
10
  const FOLDER_ENTRY_NAMES = ["plugin.ts", "plugin.js", "index.ts", "index.js"];
11
+ /** Грузит плагины из каталога и регистрирует в менеджере (битые — скип с варном). */
10
12
  export class PluginLoader {
13
+ /**
14
+ * Сканирует каталог (файлы .ts/.js и папки с точкой входа).
15
+ * Нет каталога — no-op.
16
+ */
11
17
  loadFromDir(dirPath, pluginManager, logger, options) {
12
18
  if (!existsSync(dirPath))
13
19
  return;
14
20
  const entries = readdirSync(dirPath).sort();
15
21
  for (const entry of entries) {
16
22
  const fullPath = join(dirPath, entry);
17
- const stat = statSync(fullPath);
23
+ // Битый симлинк / отказ в доступе не должен ронять весь скан плагинов
24
+ // (а с ним и bootstrap) — пропускаем с предупреждением.
25
+ let stat;
26
+ try {
27
+ stat = statSync(fullPath);
28
+ }
29
+ catch (e) {
30
+ logger?.warn(`Skipping unreadable plugin entry "${fullPath}": ${String(e)}`);
31
+ continue;
32
+ }
18
33
  if (stat.isFile()) {
19
34
  if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
20
35
  continue;
@@ -40,7 +55,7 @@ export class PluginLoader {
40
55
  try {
41
56
  const mod = require(fullPath);
42
57
  const plugin = mod.default || mod.plugin;
43
- if (plugin && plugin.name) {
58
+ if (plugin?.name) {
44
59
  if (plugin.minMmaVersion &&
45
60
  options?.mmaVersion &&
46
61
  semverGt(plugin.minMmaVersion, options.mmaVersion)) {
@@ -2,16 +2,16 @@ import { semverGt } from "../updater/checker";
2
2
  import { Logger } from "../../logger/app-logger";
3
3
  const logger = new Logger("warn", "plugins");
4
4
  /**
5
- * Run a (possibly async) plugin hook with error isolation. Sync throws and
6
- * async rejections are both logged and swallowed — one broken plugin must
7
- * never crash the agent or other plugins.
5
+ * Гоняет (возможно асинхронный) хук плагина с изоляцией ошибок. Синхронные
6
+ * броски и асинхронные реджекты логгируются и глотаются — один битый плагин
7
+ * не должен ронять агента и остальные плагины.
8
8
  */
9
9
  function safeInvoke(plugin, hookName, fn) {
10
10
  try {
11
11
  const out = fn();
12
12
  if (out instanceof Promise) {
13
- // The promise rejects AFTER this try/catch has exited — attach a catch
14
- // now, otherwise isolation is broken by an unhandledRejection.
13
+ // Промис реджектится УЖЕ ПОСЛЕ выхода из этого try/catch — вешаем catch
14
+ // сразу, иначе изоляцию ломает необработанный unhandledRejection.
15
15
  out.catch((e) => {
16
16
  logger.warn(`plugin "${plugin.name}" ${hookName} rejected`, { error: String(e) });
17
17
  });
@@ -24,17 +24,18 @@ function safeInvoke(plugin, hookName, fn) {
24
24
  return undefined;
25
25
  }
26
26
  }
27
+ /** Менеджер плагинов: реестр, сортировка по приоритету, диспатч хуков. */
27
28
  export class PluginManager {
28
29
  plugins = [];
29
30
  hostBridge = null;
30
31
  /**
31
- * Register the host bridge (set once by the REPL). It is merged into every
32
- * hook context as `ctx.hostBridge` unless the caller already provided one.
32
+ * Регистрирует host-мост (ставит REPL один раз). Подмешивается в каждый
33
+ * хук-контекст как `ctx.hostBridge`, если вызывающий не дал свой.
33
34
  */
34
35
  setHostBridge(bridge) {
35
36
  this.hostBridge = bridge;
36
37
  }
37
- /** Merge the host bridge into a hook context (caller-provided value wins). */
38
+ /** Подмешивает host-мост в хук-контекст (значение вызывающего побеждает). */
38
39
  withHost(ctx) {
39
40
  if (!ctx || typeof ctx !== "object")
40
41
  return ctx;
@@ -44,9 +45,9 @@ export class PluginManager {
44
45
  return { ...ctx, hostBridge: this.hostBridge };
45
46
  }
46
47
  /**
47
- * Register a plugin. Plugins with the same `name` are deduplicated — the
48
- * higher `version` wins (unversioned plugins count as "0.0.0"). The loser is
49
- * discarded; `skipped_older` lets the caller warn about the dropped copy.
48
+ * Регистрирует плагин. Одноимённые дедупятся — побеждает старшая `version`
49
+ * (безверсейные считаются "0.0.0"). Проигравший отбрасывается; `skipped_older`
50
+ * даёт вызывающему предупредить о выкинутой копии.
50
51
  */
51
52
  register(plugin, source) {
52
53
  if (source)
@@ -68,27 +69,36 @@ export class PluginManager {
68
69
  sort() {
69
70
  this.plugins.sort((a, b) => (b.priority || 0) - (a.priority || 0));
70
71
  }
72
+ /** Все зарегистрированные плагины (копия, по приоритету). */
71
73
  getAllPlugins() {
72
74
  return [...this.plugins];
73
75
  }
74
- /** Detailed plugin list (name, version, source) for REPL / CLI display. */
76
+ /** Детальный список (имя, версия, источник) для показа в REPL/CLI. */
75
77
  getPluginInfos() {
76
78
  return [...this.plugins].map((plugin) => ({ plugin, source: plugin.source }));
77
79
  }
78
- /** Find a registered plugin by name, or null. */
80
+ /** Ищет зарегистрированный плагин по имени; null, если нет. */
79
81
  getPlugin(name) {
80
82
  return this.plugins.find((p) => p.name === name) ?? null;
81
83
  }
84
+ /** Гоняет хук сборки промпта по всем плагинам; собирает вклады. */
82
85
  runOnBuildPrompt() {
83
86
  return this.plugins.map((p) => safeInvoke(p, "onBuildPrompt", () => p.onBuildPrompt?.() ?? null) ?? null);
84
87
  }
88
+ /**
89
+ * Хук перед тулом. False (любой плагин против) — не выполнять.
90
+ * Возвращает false при блокировке, true при разрешении.
91
+ */
85
92
  async runOnBeforeTool(rawCtx, call) {
86
93
  const ctx = this.withHost(rawCtx);
87
94
  for (const plugin of this.plugins) {
88
95
  if (plugin.onBeforeTool) {
89
96
  try {
90
97
  const result = await plugin.onBeforeTool(ctx, call);
91
- if (result === false)
98
+ // Контракт плагина разрешает блокировать `false` ИЛИ строкой-причиной
99
+ // (экзекутор тулов понимает оба). Проигнорировать строку здесь —
100
+ // молча не заблокировать, если путь когда-нибудь задействуют.
101
+ if (result === false || typeof result === "string")
92
102
  return false;
93
103
  }
94
104
  catch (e) {
@@ -98,6 +108,7 @@ export class PluginManager {
98
108
  }
99
109
  return true;
100
110
  }
111
+ /** Хук после тула: отдаёт вызов и результат всем плагинам. */
101
112
  async runOnAfterTool(rawCtx, call, result) {
102
113
  const ctx = this.withHost(rawCtx);
103
114
  for (const plugin of this.plugins) {
@@ -111,6 +122,7 @@ export class PluginManager {
111
122
  }
112
123
  }
113
124
  }
125
+ /** Хук ошибки агента: рассылает ошибку всем плагинам. */
114
126
  async runOnError(rawCtx, error) {
115
127
  const ctx = this.withHost(rawCtx);
116
128
  for (const plugin of this.plugins) {
@@ -124,6 +136,7 @@ export class PluginManager {
124
136
  }
125
137
  }
126
138
  }
139
+ /** Хук старта сессии. */
127
140
  runOnSessionStart(rawCtx) {
128
141
  const ctx = this.withHost(rawCtx);
129
142
  for (const plugin of this.plugins) {
@@ -132,6 +145,7 @@ export class PluginManager {
132
145
  }
133
146
  }
134
147
  }
148
+ /** Хук перед обдумыванием (THINK). */
135
149
  runOnBeforeThink(rawCtx) {
136
150
  const ctx = this.withHost(rawCtx);
137
151
  for (const plugin of this.plugins) {
@@ -140,6 +154,7 @@ export class PluginManager {
140
154
  }
141
155
  }
142
156
  }
157
+ /** Хук после ответа LLM. */
143
158
  runOnAfterThink(rawCtx, response) {
144
159
  const ctx = this.withHost(rawCtx);
145
160
  for (const plugin of this.plugins) {
@@ -148,6 +163,7 @@ export class PluginManager {
148
163
  }
149
164
  }
150
165
  }
166
+ /** Хук конца сессии. */
151
167
  runOnSessionEnd(rawCtx) {
152
168
  const ctx = this.withHost(rawCtx);
153
169
  for (const plugin of this.plugins) {
@@ -156,6 +172,7 @@ export class PluginManager {
156
172
  }
157
173
  }
158
174
  }
175
+ /** Хук вызова тула (факт вызова, без результата). */
159
176
  runOnToolCall(rawCtx) {
160
177
  const ctx = this.withHost(rawCtx);
161
178
  for (const plugin of this.plugins) {
@@ -164,6 +181,7 @@ export class PluginManager {
164
181
  }
165
182
  }
166
183
  }
184
+ /** Хук смены фазы агента. */
167
185
  runOnPhase(rawCtx, phase) {
168
186
  const ctx = this.withHost(rawCtx);
169
187
  for (const plugin of this.plugins) {
@@ -172,6 +190,7 @@ export class PluginManager {
172
190
  }
173
191
  }
174
192
  }
193
+ /** Хук старта тул-вызова. */
175
194
  runOnToolStart(rawCtx, call) {
176
195
  const ctx = this.withHost(rawCtx);
177
196
  for (const plugin of this.plugins) {
@@ -180,6 +199,7 @@ export class PluginManager {
180
199
  }
181
200
  }
182
201
  }
202
+ /** Хук конца тул-вызова (вызов, результат, длительность). */
183
203
  runOnToolEnd(rawCtx, call, result, durationMs) {
184
204
  const ctx = this.withHost(rawCtx);
185
205
  for (const plugin of this.plugins) {
@@ -188,7 +208,7 @@ export class PluginManager {
188
208
  }
189
209
  }
190
210
  }
191
- /** Pipe a text chunk through plugin transforms; returns the (possibly replaced) chunk. */
211
+ /** Прогоняет текстовый чанк через трансформы плагинов; возвращает (возможно заменённый) чанк. */
192
212
  runOnText(rawCtx, chunk) {
193
213
  const ctx = this.withHost(rawCtx);
194
214
  for (const plugin of this.plugins) {
@@ -200,7 +220,7 @@ export class PluginManager {
200
220
  }
201
221
  return chunk;
202
222
  }
203
- /** Pipe a meta chunk through plugin transforms; returns the (possibly replaced) chunk. */
223
+ /** Прогоняет мета-чанк через трансформы плагинов; возвращает (возможно заменённый) чанк. */
204
224
  runOnMeta(rawCtx, chunk) {
205
225
  const ctx = this.withHost(rawCtx);
206
226
  for (const plugin of this.plugins) {
@@ -212,7 +232,7 @@ export class PluginManager {
212
232
  }
213
233
  return chunk;
214
234
  }
215
- /** Dispatch a completed-run summary to all plugins (once per Agent.run()). */
235
+ /** Рассылает сводку завершённого прогона всем плагинам (раз за Agent.run()). */
216
236
  runOnTurnEnd(rawCtx, summary) {
217
237
  const ctx = this.withHost(rawCtx);
218
238
  for (const plugin of this.plugins) {
@@ -1,10 +1,9 @@
1
1
  import { calculateCostDetailed, formatCost } from "./prices";
2
2
  /**
3
- * Accumulates per-call LLM cost for a session, attributed to the provider
4
- * that served the call. Call `record()` after each LLM call with the
5
- * (estimated or API) token usage and the provider name; read `total` for the
6
- * running total, `totalFor(provider)` / `breakdown()` for per-provider
7
- * attribution. When the model has no known price, `record` is a no-op.
3
+ * Копит стоимость LLM-вызовов сессии с атрибуцией обслужившему провайдеру.
4
+ * После каждого вызова — `record()` с usage (оценочным или API) и именем
5
+ * провайдера; `total` даёт running-итог, `totalFor(provider)` / `breakdown()` —
6
+ * разбивку. Без известной цены модели `record` — no-op.
8
7
  */
9
8
  export class CostTracker {
10
9
  config;
@@ -17,7 +16,7 @@ export class CostTracker {
17
16
  this.model = model;
18
17
  this.config = config;
19
18
  }
20
- /** Update the model (on /model switch). Later records use the new price. */
19
+ /** Сменяет модель (при /model switch). Дальнейшие записи — по новой цене. */
21
20
  setModel(model) {
22
21
  this.model = model;
23
22
  }
@@ -42,7 +41,7 @@ export class CostTracker {
42
41
  entry.known = true;
43
42
  entry.total += result.cost;
44
43
  }
45
- /** Running total cost in USD, or undefined if no model price is known. */
44
+ /** Итоговая стоимость в USD; undefined без известной цены модели. */
46
45
  get total() {
47
46
  return this._known ? this._total : undefined;
48
47
  }
@@ -50,12 +49,12 @@ export class CostTracker {
50
49
  get saved() {
51
50
  return this._known ? this._saved : undefined;
52
51
  }
53
- /** Total for one provider, or undefined when nothing was priced for it. */
52
+ /** Итог по одному провайдеру; undefined, если по нему ничего не оценено. */
54
53
  totalFor(provider) {
55
54
  const entry = this.byProvider.get(provider);
56
- return entry && entry.known ? entry.total : undefined;
55
+ return entry?.known ? entry.total : undefined;
57
56
  }
58
- /** Per-provider totals sorted by cost descending (priced providers only). */
57
+ /** Итоги по провайдерам по убыванию стоимости (только оценённые). */
59
58
  breakdown() {
60
59
  const entries = [];
61
60
  for (const [provider, entry] of this.byProvider) {
@@ -64,7 +63,7 @@ export class CostTracker {
64
63
  }
65
64
  return entries.sort((a, b) => b.cost - a.cost);
66
65
  }
67
- /** Human-readable total, or undefined when pricing is unavailable. */
66
+ /** Читаемый итог; undefined, когда цен нет. */
68
67
  get formatted() {
69
68
  return this._known ? formatCost(this._total) : undefined;
70
69
  }