micro-models-agent 2.10.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (359) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/assets/app.js +22 -4
  3. package/dist/assets/index.html +29 -0
  4. package/dist/assets/js/dom.js +2 -1
  5. package/dist/assets/js/feed.js +38 -26
  6. package/dist/assets/js/format.js +1 -1
  7. package/dist/assets/js/rail.js +114 -0
  8. package/dist/assets/js/settings-controls.js +2 -2
  9. package/dist/assets/js/settings.js +246 -51
  10. package/dist/assets/js/store.js +3 -0
  11. package/dist/assets/js/tabs.js +104 -2
  12. package/dist/assets/styles.css +215 -66
  13. package/dist/cli/cache-line.js +1 -0
  14. package/dist/cli/command-suggest.js +5 -4
  15. package/dist/cli/commands.js +231 -122
  16. package/dist/cli/completer.js +132 -31
  17. package/dist/cli/index.js +1 -0
  18. package/dist/cli/json-payload.js +4 -4
  19. package/dist/cli/main.js +166 -61
  20. package/dist/cli/onboarding-commands.js +126 -0
  21. package/dist/cli/plugin-commands.js +2 -1
  22. package/dist/cli/relaunch.js +16 -13
  23. package/dist/cli/repl-commands-lsp.js +75 -0
  24. package/dist/cli/repl-commands-skills.js +105 -0
  25. package/dist/cli/repl-commands.js +589 -266
  26. package/dist/cli/repl.js +436 -177
  27. package/dist/cli/run-result.js +5 -6
  28. package/dist/cli/security-commands.js +37 -28
  29. package/dist/cli/setup-order.js +4 -55
  30. package/dist/cli/setup-prompt.js +3 -2
  31. package/dist/cli/setup.js +27 -107
  32. package/dist/cli/web-command.js +98 -0
  33. package/dist/config/budget.js +5 -5
  34. package/dist/config/config.js +103 -96
  35. package/dist/config/defaults.js +38 -8
  36. package/dist/config/domains.js +24 -27
  37. package/dist/config/env.js +46 -0
  38. package/dist/config/experts.js +2 -0
  39. package/dist/config/index.js +1 -0
  40. package/dist/config/paths.js +56 -0
  41. package/dist/config/redact.js +44 -0
  42. package/dist/config/registry-extra.js +334 -0
  43. package/dist/config/registry.js +584 -0
  44. package/dist/config/security.js +35 -36
  45. package/dist/config/timeouts.js +74 -0
  46. package/dist/config/utils.js +16 -15
  47. package/dist/core/agent/artifact-progress.js +59 -0
  48. package/dist/core/agent/artifact-resolve.js +79 -0
  49. package/dist/core/agent/audit-gate.js +7 -3
  50. package/dist/core/agent/compaction.js +2 -2
  51. package/dist/core/agent/constants.js +33 -4
  52. package/dist/core/agent/context-log.js +454 -0
  53. package/dist/core/agent/context-renderer.js +5 -0
  54. package/dist/core/agent/critic-gate.js +132 -0
  55. package/dist/core/agent/hallucination-gate.js +148 -38
  56. package/dist/core/agent/loop-state.js +15 -0
  57. package/dist/core/agent/prefix-monitor.js +1 -1
  58. package/dist/core/agent/reasoning-resolver.js +10 -1
  59. package/dist/core/agent/token-tracker.js +7 -4
  60. package/dist/core/agent/tool-batch.js +93 -18
  61. package/dist/core/agent/tool-images.js +18 -0
  62. package/dist/core/agent/tool-output.js +65 -11
  63. package/dist/core/agent/tool-pairing.js +60 -0
  64. package/dist/core/agent-moe.js +25 -24
  65. package/dist/core/agent.js +429 -108
  66. package/dist/core/bootstrap/agent-phase.js +66 -0
  67. package/dist/core/bootstrap/config-phase.js +86 -0
  68. package/dist/core/bootstrap/modules-phase.js +269 -0
  69. package/dist/core/bootstrap/provider-phase.js +183 -0
  70. package/dist/core/bootstrap/session-phase.js +204 -0
  71. package/dist/core/bootstrap/state.js +94 -0
  72. package/dist/core/bootstrap/workspace-phase.js +115 -0
  73. package/dist/core/bootstrap.js +92 -660
  74. package/dist/core/crash-handler.js +17 -7
  75. package/dist/core/environment.js +37 -12
  76. package/dist/core/host-plan.js +43 -0
  77. package/dist/core/host.js +23 -0
  78. package/dist/core/index.js +1 -0
  79. package/dist/core/prompt-builder.js +17 -1
  80. package/dist/core/prompt-overflow.js +42 -43
  81. package/dist/core/session-logger.js +122 -30
  82. package/dist/core/system-prompt.js +117 -0
  83. package/dist/core/version.js +6 -4
  84. package/dist/core/web-host.js +34 -0
  85. package/dist/core/workspace.js +19 -15
  86. package/dist/i18n/en.json +106 -21
  87. package/dist/i18n/index.js +37 -3
  88. package/dist/i18n/ru.json +109 -21
  89. package/dist/index.js +7 -3
  90. package/dist/llm/image-utils.js +55 -64
  91. package/dist/llm/llm-errors.js +9 -0
  92. package/dist/llm/model-loader.js +10 -32
  93. package/dist/llm/models-dev.js +121 -0
  94. package/dist/llm/openai-compat.js +146 -73
  95. package/dist/llm/orchestrator.js +33 -11
  96. package/dist/llm/provider-budget.js +2 -2
  97. package/dist/llm/provider-context.js +233 -0
  98. package/dist/llm/provider.js +1 -1
  99. package/dist/llm/response.js +5 -0
  100. package/dist/llm/token-counter.js +27 -11
  101. package/dist/logger/app-logger.js +48 -22
  102. package/dist/logger/file-log.js +28 -19
  103. package/dist/logger/index.js +1 -0
  104. package/dist/main.js +24285 -22447
  105. package/dist/migration/backup.js +14 -3
  106. package/dist/migration/detect.js +12 -3
  107. package/dist/migration/index.js +1 -0
  108. package/dist/modules/artifacts/store.js +9 -8
  109. package/dist/modules/browser/actions.js +6 -0
  110. package/dist/modules/browser/bridge-client.js +14 -12
  111. package/dist/modules/browser/bridge-path.js +4 -4
  112. package/dist/modules/browser/console-buffer.js +37 -0
  113. package/dist/modules/browser/cookie-store.js +5 -0
  114. package/dist/modules/browser/driver-types.js +1 -0
  115. package/dist/modules/browser/driver.js +19 -11
  116. package/dist/modules/browser/index.js +3 -1
  117. package/dist/modules/browser/module.js +2 -1
  118. package/dist/modules/browser/session.js +10 -30
  119. package/dist/modules/browser/snapshot.js +19 -13
  120. package/dist/modules/browser/types.js +1 -0
  121. package/dist/modules/certification/cli.js +28 -20
  122. package/dist/modules/certification/fact-checker.js +9 -1
  123. package/dist/modules/certification/loader.js +9 -2
  124. package/dist/modules/certification/manifest.js +83 -21
  125. package/dist/modules/certification/runner.js +23 -13
  126. package/dist/modules/certification/scenarios.js +20 -20
  127. package/dist/modules/certification/syntax-scenarios.js +1 -2
  128. package/dist/modules/context/chunk-query.js +11 -9
  129. package/dist/modules/context/fact-extractor.js +37 -39
  130. package/dist/modules/context/index.js +1 -0
  131. package/dist/modules/context/manager.js +106 -80
  132. package/dist/modules/critic/client.js +176 -0
  133. package/dist/modules/critic/evidence.js +81 -0
  134. package/dist/modules/critic/provider.js +45 -0
  135. package/dist/modules/critic/tools.js +78 -0
  136. package/dist/modules/critic/types.js +1 -0
  137. package/dist/modules/execution/audit-runners.js +28 -34
  138. package/dist/modules/execution/auditor.js +53 -49
  139. package/dist/modules/execution/execution-plugin.js +142 -102
  140. package/dist/modules/execution/index.js +1 -1
  141. package/dist/modules/execution/input-from.js +37 -13
  142. package/dist/modules/execution/module.js +368 -191
  143. package/dist/modules/execution/moe-executor.js +53 -26
  144. package/dist/modules/execution/plan-actions.js +320 -202
  145. package/dist/modules/execution/plan-checks.js +35 -0
  146. package/dist/modules/execution/plan-coverage.js +36 -33
  147. package/dist/modules/execution/plan-steps.js +11 -3
  148. package/dist/modules/execution/plan-store.js +83 -17
  149. package/dist/modules/execution/plan-tool.js +89 -35
  150. package/dist/modules/execution/plan-validator.js +30 -10
  151. package/dist/modules/execution/planner.js +22 -13
  152. package/dist/modules/execution/stuck-detector.js +196 -93
  153. package/dist/modules/execution/tracker.js +22 -8
  154. package/dist/modules/execution/verifier.js +69 -48
  155. package/dist/modules/execution/windows-commands.js +12 -12
  156. package/dist/modules/hallucination/artifact-claim.js +93 -0
  157. package/dist/modules/hallucination/confidence.js +13 -11
  158. package/dist/modules/hallucination/consistency.js +10 -2
  159. package/dist/modules/hallucination/detector.js +10 -5
  160. package/dist/modules/hallucination/factual.js +43 -26
  161. package/dist/modules/hallucination/index.js +1 -0
  162. package/dist/modules/hallucination/js-identifiers.js +44 -45
  163. package/dist/modules/hallucination/llm-judge.js +17 -12
  164. package/dist/modules/hallucination/success-claim.js +59 -0
  165. package/dist/modules/indexer/cache.js +52 -4
  166. package/dist/modules/indexer/index.js +1 -0
  167. package/dist/modules/indexer/map-command.js +2 -4
  168. package/dist/modules/indexer/map-select.js +16 -16
  169. package/dist/modules/indexer/module.js +47 -12
  170. package/dist/modules/indexer/project-profile.js +8 -7
  171. package/dist/modules/indexer/symbols.js +15 -15
  172. package/dist/modules/indexer/walker.js +159 -29
  173. package/dist/modules/lsp/check-tool.js +7 -5
  174. package/dist/modules/lsp/client.js +39 -39
  175. package/dist/modules/lsp/command.js +17 -24
  176. package/dist/modules/lsp/config.js +12 -11
  177. package/dist/modules/lsp/index.js +1 -0
  178. package/dist/modules/lsp/module.js +48 -46
  179. package/dist/modules/lsp/probe.js +13 -14
  180. package/dist/modules/lsp/project-root.js +14 -17
  181. package/dist/modules/lsp/startup-check.js +18 -16
  182. package/dist/modules/mcp/client.js +59 -62
  183. package/dist/modules/mcp/index.js +1 -0
  184. package/dist/modules/mcp/module.js +66 -37
  185. package/dist/modules/mcp/registry.js +5 -0
  186. package/dist/modules/memory/index.js +1 -0
  187. package/dist/modules/memory/module.js +12 -8
  188. package/dist/modules/memory/search.js +4 -1
  189. package/dist/modules/memory/store.js +40 -2
  190. package/dist/modules/onboarding/init.js +47 -0
  191. package/dist/modules/onboarding/meet.js +30 -0
  192. package/dist/modules/onboarding/mode.js +30 -0
  193. package/dist/modules/onboarding/prompts.js +43 -0
  194. package/dist/modules/pipelines/engine.js +10 -0
  195. package/dist/modules/pipelines/index.js +1 -0
  196. package/dist/modules/pipelines/parser.js +4 -1
  197. package/dist/modules/pipelines/template.js +4 -0
  198. package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
  199. package/dist/modules/plugins/builtin/notify.js +2 -0
  200. package/dist/modules/plugins/index.js +1 -0
  201. package/dist/modules/plugins/loader.js +18 -3
  202. package/dist/modules/plugins/manager.js +37 -17
  203. package/dist/modules/pricing/index.js +10 -11
  204. package/dist/modules/pricing/prices.js +12 -12
  205. package/dist/modules/processes/index.js +1 -0
  206. package/dist/modules/processes/kill-tree.js +27 -31
  207. package/dist/modules/processes/registry.js +44 -25
  208. package/dist/modules/processes/runner.js +4 -4
  209. package/dist/modules/providers/capabilities.js +28 -0
  210. package/dist/modules/providers/create.js +3 -4
  211. package/dist/modules/providers/factory.js +7 -7
  212. package/dist/modules/providers/fallback.js +12 -12
  213. package/dist/modules/providers/health.js +4 -4
  214. package/dist/modules/providers/manager.js +50 -19
  215. package/dist/modules/providers/naming.js +15 -0
  216. package/dist/modules/providers/preflight.js +71 -0
  217. package/dist/modules/providers/presets.js +39 -8
  218. package/dist/modules/providers/registry.js +7 -2
  219. package/dist/modules/reasoning/policy.js +8 -6
  220. package/dist/modules/reasoning/probe.js +30 -25
  221. package/dist/modules/security/audit-log.js +19 -36
  222. package/dist/modules/security/audit-notifier.js +35 -75
  223. package/dist/modules/security/command-validator.js +99 -51
  224. package/dist/modules/security/content-scanner.js +8 -8
  225. package/dist/modules/security/data-sanitizer.js +9 -16
  226. package/dist/modules/security/encryption.js +32 -52
  227. package/dist/modules/security/index.js +2 -1
  228. package/dist/modules/security/network-validator.js +120 -43
  229. package/dist/modules/security/path-validator.js +61 -46
  230. package/dist/modules/security/protected-paths.js +73 -0
  231. package/dist/modules/security/rate-limiter.js +17 -49
  232. package/dist/modules/security/security-policies.js +25 -57
  233. package/dist/modules/security/session-encryption.js +32 -71
  234. package/dist/modules/security/session-isolation.js +15 -28
  235. package/dist/modules/session/auto-name.js +63 -0
  236. package/dist/modules/session/export.js +98 -0
  237. package/dist/modules/session/index.js +1 -0
  238. package/dist/modules/session/manager.js +79 -28
  239. package/dist/modules/session/module.js +4 -3
  240. package/dist/modules/session/store.js +57 -20
  241. package/dist/modules/setup/answers.js +35 -0
  242. package/dist/modules/setup/apply.js +79 -0
  243. package/dist/modules/setup/discovery.js +72 -0
  244. package/dist/modules/skills/index.js +1 -0
  245. package/dist/modules/skills/loader.js +9 -1
  246. package/dist/modules/skills/module.js +11 -0
  247. package/dist/modules/updater/changelog-reader.js +15 -16
  248. package/dist/modules/updater/checker.js +9 -4
  249. package/dist/modules/updater/dev-detect.js +7 -6
  250. package/dist/modules/updater/index.js +1 -0
  251. package/dist/modules/updater/module.js +17 -15
  252. package/dist/modules/user-profile/compressor.js +2 -0
  253. package/dist/modules/user-profile/index.js +1 -0
  254. package/dist/modules/user-profile/profile.js +10 -1
  255. package/dist/modules/webui/answer-api.js +29 -0
  256. package/dist/modules/webui/assets.js +50 -0
  257. package/dist/modules/webui/body.js +21 -0
  258. package/dist/modules/webui/commands-settings.js +209 -0
  259. package/dist/modules/webui/commands-shared.js +60 -0
  260. package/dist/modules/webui/commands.js +174 -0
  261. package/dist/modules/webui/config-api.js +134 -0
  262. package/dist/modules/webui/file-api.js +202 -0
  263. package/dist/modules/webui/forward.js +159 -0
  264. package/dist/modules/webui/history-api.js +112 -0
  265. package/dist/modules/webui/host-state.js +60 -0
  266. package/dist/modules/webui/i18n-api.js +9 -0
  267. package/dist/modules/webui/limits.js +6 -0
  268. package/dist/modules/webui/log-forward.js +20 -0
  269. package/dist/modules/webui/memory-commands.js +64 -0
  270. package/dist/modules/webui/model-cache.js +18 -0
  271. package/dist/modules/webui/module.js +148 -0
  272. package/dist/modules/webui/persist.js +24 -0
  273. package/dist/modules/webui/prompt-io.js +97 -0
  274. package/dist/modules/webui/providers-api.js +337 -0
  275. package/dist/modules/webui/providers-view.js +56 -0
  276. package/dist/modules/webui/reasoning-view.js +15 -0
  277. package/dist/modules/webui/run-agent.js +81 -0
  278. package/dist/modules/webui/server.js +284 -0
  279. package/dist/modules/webui/sessions-api.js +178 -0
  280. package/dist/modules/webui/settings-api.js +561 -0
  281. package/dist/modules/webui/setup-api.js +201 -0
  282. package/dist/modules/webui/state.js +74 -0
  283. package/dist/modules/webui/util.js +261 -0
  284. package/dist/output/bus.js +7 -2
  285. package/dist/output/channel.js +80 -28
  286. package/dist/output/format.js +2 -1
  287. package/dist/output/index.js +1 -0
  288. package/dist/output/json-sink.js +5 -3
  289. package/dist/output/machine.js +4 -3
  290. package/dist/output/session-sink.js +4 -4
  291. package/dist/tools/approve.js +4 -0
  292. package/dist/tools/attach-image.js +5 -4
  293. package/dist/tools/auto-fixer.js +26 -44
  294. package/dist/tools/auto-tags.js +83 -0
  295. package/dist/tools/bash.js +193 -116
  296. package/dist/tools/browser.js +76 -31
  297. package/dist/tools/chunk-query.js +14 -6
  298. package/dist/tools/create-dir.js +9 -5
  299. package/dist/tools/delete-file.js +9 -5
  300. package/dist/tools/download-file.js +32 -7
  301. package/dist/tools/edit-file.js +52 -14
  302. package/dist/tools/enable-tools.js +6 -6
  303. package/dist/tools/executor.js +132 -27
  304. package/dist/tools/file-info.js +3 -1
  305. package/dist/tools/filter-tools.js +3 -3
  306. package/dist/tools/glob-tool.js +4 -1
  307. package/dist/tools/grep-tool.js +13 -10
  308. package/dist/tools/hidden-tools-block.js +6 -6
  309. package/dist/tools/index.js +16 -8
  310. package/dist/tools/list-dir.js +4 -2
  311. package/dist/tools/load-skill.js +7 -0
  312. package/dist/tools/mcp-call.js +15 -4
  313. package/dist/tools/meet-done.js +14 -0
  314. package/dist/tools/move-file.js +14 -7
  315. package/dist/tools/path-utils.js +29 -29
  316. package/dist/tools/pipeline-run.js +29 -7
  317. package/dist/tools/preview.js +1 -1
  318. package/dist/tools/process-ids.js +31 -0
  319. package/dist/tools/process-kill.js +9 -16
  320. package/dist/tools/process-list.js +1 -0
  321. package/dist/tools/process-log.js +11 -16
  322. package/dist/tools/process-wait.js +63 -0
  323. package/dist/tools/prompt-io.js +4 -3
  324. package/dist/tools/question.js +172 -25
  325. package/dist/tools/read-file.js +55 -34
  326. package/dist/tools/recall.js +14 -8
  327. package/dist/tools/registry.js +6 -5
  328. package/dist/tools/remember.js +8 -4
  329. package/dist/tools/scope-request.js +11 -6
  330. package/dist/tools/search-history.js +5 -2
  331. package/dist/tools/session-info.js +2 -0
  332. package/dist/tools/set-thinking.js +8 -4
  333. package/dist/tools/subagent.js +59 -39
  334. package/dist/tools/syntax-validator.js +292 -90
  335. package/dist/tools/user-input.js +22 -18
  336. package/dist/tools/web-browse.js +9 -3
  337. package/dist/tools/web-fetch.js +12 -23
  338. package/dist/tools/web-search.js +7 -5
  339. package/dist/tools/write-file.js +45 -15
  340. package/dist/ui/action-log.js +13 -0
  341. package/dist/ui/box.js +4 -3
  342. package/dist/ui/colors.js +2 -0
  343. package/dist/ui/diff.js +6 -5
  344. package/dist/ui/index.js +1 -0
  345. package/dist/ui/line-editor.js +230 -122
  346. package/dist/ui/line-math.js +10 -2
  347. package/dist/ui/md-formatter.js +57 -37
  348. package/dist/ui/output.js +8 -3
  349. package/dist/ui/plan-view.js +21 -10
  350. package/dist/ui/renderer.js +393 -67
  351. package/dist/ui/spinner.js +16 -5
  352. package/dist/ui/table.js +15 -17
  353. package/dist/ui/theme.js +143 -0
  354. package/dist/utils/child-run.js +20 -0
  355. package/dist/utils/error.js +1 -1
  356. package/dist/utils/index.js +2 -0
  357. package/dist/utils/retry.js +2 -2
  358. package/dist/utils/truncate.js +1 -1
  359. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ // Загрузка/сохранение конфига: DEFAULTS → legacy → домены → .mmrc → env → decrypt.
1
2
  import { existsSync, readFileSync, unlinkSync, writeFileSync, mkdirSync } from "fs";
2
3
  import { join, dirname } from "path";
3
4
  import { homedir } from "os";
@@ -13,15 +14,20 @@ import { validateExpertConfig } from "./experts";
13
14
  import { ConfigEncryptor } from "../modules/security/encryption";
14
15
  import { loadDomainFiles, saveDomainFiles } from "./domains";
15
16
  import { defaultOutputBus } from "../output";
16
- // ── Centralized path constants ──────────────────────────────────────────
17
- export const CONFIG_DIR = join(homedir(), ".mma");
17
+ import { MMA_DIR_NAME } from "./paths";
18
+ import { MMA_ENV } from "./env";
19
+ // ── Централизованные константы путей ──────────────────────────────────────
20
+ /** Глобальный каталог конфига. */
21
+ export const CONFIG_DIR = join(homedir(), MMA_DIR_NAME);
22
+ /** Legacy-файл конфига. */
18
23
  export const CONFIG_FILE = join(CONFIG_DIR, "config.json");
24
+ /** Каталог файлов доменов. */
19
25
  export const CONFIG_DOMAIN_DIR = join(CONFIG_DIR, "config");
20
- // ── Internal helpers ────────────────────────────────────────────────────
26
+ // ── Внутренние хелперы ────────────────────────────────────────────────────
21
27
  /**
22
- * Restore RegExp instances in dangerousPatterns that were serialized as {}
23
- * (pre-0.8.0 configs) or as {__regex, source, flags} (new format).
24
- * Falls back to default patterns for any entry that is not a real RegExp.
28
+ * Восстанавливает RegExp в dangerousPatterns, сериализованных как {}
29
+ * (конфиги до 0.8.0) или как {__regex, source, flags} (новый формат).
30
+ * Для записей без настоящего RegExp — фолбэк на дефолтные паттерны.
25
31
  */
26
32
  function restoreDangerousPatterns(patterns, defaults) {
27
33
  const fallback = (Array.isArray(defaults) ? defaults : []);
@@ -45,9 +51,7 @@ function restoreDangerousPatterns(patterns, defaults) {
45
51
  return fallback[i] || fallback[0] || p;
46
52
  });
47
53
  }
48
- /**
49
- * Legacy npx `args` for LSP servers that predate the fixed invocation.
50
- */
54
+ /** Legacy npx-`args` LSP-серверов, предшествующие фикс. вызову. */
51
55
  const LEGACY_LSP_SERVER_ARGS = {
52
56
  typescript: { args: ["typescript-language-server", "--stdio"], timeout: 30000 },
53
57
  javascript: { args: ["typescript-language-server", "--stdio"], timeout: 30000 },
@@ -55,9 +59,7 @@ const LEGACY_LSP_SERVER_ARGS = {
55
59
  html: { args: ["vscode-html-languageserver", "--stdio"], timeout: 60000 },
56
60
  css: { args: ["vscode-css-languageserver", "--stdio"], timeout: 60000 },
57
61
  };
58
- /**
59
- * Replace stale pre-0.41.1 LSP server invocations with current defaults.
60
- */
62
+ /** Заменяет протухшие pre-0.41.1 вызовы LSP-серверов текущими дефолтами. */
61
63
  function normalizeLspServerArgs(config) {
62
64
  const servers = config.lsp?.servers;
63
65
  if (!servers)
@@ -65,7 +67,7 @@ function normalizeLspServerArgs(config) {
65
67
  for (const [lang, legacy] of Object.entries(LEGACY_LSP_SERVER_ARGS)) {
66
68
  const server = servers[lang];
67
69
  const def = DEFAULT_LSP_CONFIG.servers[lang];
68
- if (!server || server.command !== "npx" || !def)
70
+ if (server?.command !== "npx" || !def)
69
71
  continue;
70
72
  const storedArgs = Array.isArray(server.args) ? server.args : null;
71
73
  const isLegacy = storedArgs === null || JSON.stringify(storedArgs) === JSON.stringify(legacy.args);
@@ -75,6 +77,7 @@ function normalizeLspServerArgs(config) {
75
77
  }
76
78
  }
77
79
  }
80
+ /** Чтение JSON с восстановлением RegExp. Битый файл — warn с путём, не тишина. */
78
81
  function loadJSON(path) {
79
82
  if (!existsSync(path))
80
83
  return null;
@@ -82,14 +85,14 @@ function loadJSON(path) {
82
85
  return JSON.parse(readFileSync(path, "utf-8"), regexReviver);
83
86
  }
84
87
  catch {
85
- // Never silent: a typo'd config must not quietly wipe the user's settings.
88
+ // Не молчим: конфиг с опечаткой не должен тихо затирать настройки юзера.
86
89
  defaultOutputBus.log("warn", "config", t("config.parse_failed", { path }));
87
90
  }
88
91
  return null;
89
92
  }
90
93
  /**
91
- * Apply MMA_* env overrides. Returns the overridden keys with their PRE-ENV
92
- * values so `saveConfig` can avoid persisting session-scoped env values (C4).
94
+ * Применяет MMA_*-оверрайды окружения. Возвращает тронутые ключи с ДО-env
95
+ * значениями, чтобы `saveConfig` не персистил сессионные env-значения (C4).
93
96
  */
94
97
  function applyEnvVars(config) {
95
98
  const env = process.env;
@@ -100,15 +103,14 @@ function applyEnvVars(config) {
100
103
  if (original !== envValue)
101
104
  overrides.push({ key, envValue, originalValue: original });
102
105
  };
103
- if (env.MMA_MODEL) {
104
- record("model", env.MMA_MODEL);
105
- result.model = env.MMA_MODEL;
106
+ if (env[MMA_ENV.MODEL]) {
107
+ record("model", env[MMA_ENV.MODEL]);
108
+ result.model = env[MMA_ENV.MODEL];
106
109
  }
107
110
  const originalProvider = result.provider;
108
- // normalizeEntries prefers entries[] and ignores the flat provider fields, so
109
- // a flat-only env override is a silent no-op on multi-provider configs (the
110
- // certification harness depends on these vars working). Resolve the active
111
- // entry and patch it too.
111
+ // normalizeEntries предпочитает entries[] и игнорит плоские поля провайдера, так
112
+ // что плоский env-оверрайд на мультиконфиге — тихий no-op (а cert-харнесс зависит
113
+ // от работы этих vars). Резолвим активную запись и патчим её тоже.
112
114
  const activeEntryIndex = (() => {
113
115
  const entries = result.provider.entries;
114
116
  if (!entries || entries.length === 0)
@@ -127,71 +129,76 @@ function applyEnvVars(config) {
127
129
  next[activeEntryIndex] = { ...next[activeEntryIndex], ...patch };
128
130
  result.provider = { ...result.provider, entries: next };
129
131
  };
130
- if (env.MMA_PROVIDER_TYPE) {
131
- result.provider = { ...result.provider, type: env.MMA_PROVIDER_TYPE };
132
- patchActiveEntry({ type: env.MMA_PROVIDER_TYPE });
132
+ if (env[MMA_ENV.PROVIDER_TYPE]) {
133
+ result.provider = { ...result.provider, type: env[MMA_ENV.PROVIDER_TYPE] };
134
+ patchActiveEntry({ type: env[MMA_ENV.PROVIDER_TYPE] });
133
135
  }
134
- if (env.MMA_PROVIDER_BASEURL) {
135
- result.provider = { ...result.provider, baseUrl: env.MMA_PROVIDER_BASEURL };
136
- patchActiveEntry({ baseUrl: env.MMA_PROVIDER_BASEURL });
136
+ if (env[MMA_ENV.PROVIDER_BASEURL]) {
137
+ result.provider = { ...result.provider, baseUrl: env[MMA_ENV.PROVIDER_BASEURL] };
138
+ patchActiveEntry({ baseUrl: env[MMA_ENV.PROVIDER_BASEURL] });
137
139
  }
138
- if (env.MMA_PROVIDER_APIKEY) {
139
- result.provider = { ...result.provider, apiKey: env.MMA_PROVIDER_APIKEY };
140
- patchActiveEntry({ apiKey: env.MMA_PROVIDER_APIKEY });
140
+ if (env[MMA_ENV.PROVIDER_APIKEY]) {
141
+ result.provider = { ...result.provider, apiKey: env[MMA_ENV.PROVIDER_APIKEY] };
142
+ patchActiveEntry({ apiKey: env[MMA_ENV.PROVIDER_APIKEY] });
141
143
  }
142
- if (env.MMA_PROVIDER_TYPE || env.MMA_PROVIDER_BASEURL || env.MMA_PROVIDER_APIKEY) {
143
- // Record once with the FINAL env-mutated provider object so the identity
144
- // check in stripEnvOverrides matches what the loaded config holds.
144
+ if (env[MMA_ENV.PROVIDER_TYPE] || env[MMA_ENV.PROVIDER_BASEURL] || env[MMA_ENV.PROVIDER_APIKEY]) {
145
+ // Пишем один раз с ФИНАЛЬНЫМ env-мутированным объектом провайдера, чтобы
146
+ // identity-проверка в stripEnvOverrides сошлась с загруженным конфигом.
145
147
  overrides.push({ key: "provider", envValue: result.provider, originalValue: originalProvider });
146
148
  }
147
- if (env.MMA_LOG_LEVEL) {
148
- record("logLevel", env.MMA_LOG_LEVEL);
149
- result.logLevel = env.MMA_LOG_LEVEL;
149
+ if (env[MMA_ENV.LOG_LEVEL]) {
150
+ record("logLevel", env[MMA_ENV.LOG_LEVEL]);
151
+ result.logLevel = env[MMA_ENV.LOG_LEVEL];
150
152
  }
151
- if (env.MMA_LOCALE) {
152
- record("locale", env.MMA_LOCALE);
153
- result.locale = env.MMA_LOCALE;
153
+ if (env[MMA_ENV.LOCALE]) {
154
+ record("locale", env[MMA_ENV.LOCALE]);
155
+ result.locale = env[MMA_ENV.LOCALE];
154
156
  }
155
- if (env.MMA_CONTEXT_WINDOW) {
156
- const v = parseInt(env.MMA_CONTEXT_WINDOW, 10);
157
- if (!isNaN(v)) {
157
+ if (env[MMA_ENV.CONTEXT_WINDOW]) {
158
+ const v = parseInt(env[MMA_ENV.CONTEXT_WINDOW], 10);
159
+ if (!Number.isNaN(v)) {
158
160
  record("contextWindow", v);
159
161
  result.contextWindow = v;
160
162
  }
161
163
  }
162
- if (env.MMA_MAX_TOOL_ITERATIONS) {
163
- const v = parseInt(env.MMA_MAX_TOOL_ITERATIONS, 10);
164
- if (!isNaN(v)) {
164
+ if (env[MMA_ENV.MAX_TOOL_ITERATIONS]) {
165
+ const v = parseInt(env[MMA_ENV.MAX_TOOL_ITERATIONS], 10);
166
+ if (!Number.isNaN(v)) {
165
167
  record("maxToolIterations", v);
166
168
  result.maxToolIterations = v;
167
169
  }
168
170
  }
169
- if (env.MMA_STUCK_THRESHOLD) {
170
- const v = parseInt(env.MMA_STUCK_THRESHOLD, 10);
171
- if (!isNaN(v)) {
171
+ if (env[MMA_ENV.STUCK_THRESHOLD]) {
172
+ const v = parseInt(env[MMA_ENV.STUCK_THRESHOLD], 10);
173
+ if (!Number.isNaN(v)) {
172
174
  record("stuckThreshold", v);
173
175
  result.stuckThreshold = v;
174
176
  }
175
177
  }
176
- if (env.MMA_AUTO_PLAN) {
177
- const v = env.MMA_AUTO_PLAN === "true";
178
+ if (env[MMA_ENV.AUTO_PLAN]) {
179
+ const v = env[MMA_ENV.AUTO_PLAN] === "true";
178
180
  record("autoPlan", v);
179
181
  result.autoPlan = v;
180
182
  }
181
- if (env.MMA_MOE_ENABLED) {
182
- const next = { ...result.moe, enabled: env.MMA_MOE_ENABLED === "true" };
183
+ if (env[MMA_ENV.MOE_ENABLED]) {
184
+ const next = { ...result.moe, enabled: env[MMA_ENV.MOE_ENABLED] === "true" };
183
185
  record("moe", next);
184
186
  result.moe = next;
185
187
  }
188
+ if (env[MMA_ENV.CONTEXT_LOG]) {
189
+ const next = { ...result.contextLog, enabled: env[MMA_ENV.CONTEXT_LOG] !== "false" };
190
+ record("contextLog", next);
191
+ result.contextLog = next;
192
+ }
186
193
  return { config: result, overrides };
187
194
  }
188
- /** Pre-env values for env-touched keys, keyed by the loaded config object. */
195
+ /** До-env значения env-тронутых ключей, по объекту загруженного конфига. */
189
196
  const envOverrideMap = new WeakMap();
190
197
  /**
191
- * Restore pre-env values before persisting — session-scoped env overrides
192
- * (e.g. the cert harness baseUrl) must NOT leak into domain files. An explicit
193
- * later change (setProvider/reconfigure replacing provider, /model use) shows
194
- * up as a different object/value and is kept as-is.
198
+ * Возвращает до-env значения перед сохранением: сессионные env-оверрайды
199
+ * (например, baseUrl cert-харнесса) НЕ должны протекать в файлы доменов. Явное
200
+ * позднее изменение (setProvider/reconfigure заменил provider, /model use)
201
+ * видно как другой объект/значение и сохраняется как есть.
195
202
  */
196
203
  function stripEnvOverrides(config) {
197
204
  const overrides = envOverrideMap.get(config);
@@ -199,35 +206,37 @@ function stripEnvOverrides(config) {
199
206
  return config;
200
207
  const result = { ...config };
201
208
  for (const o of overrides) {
202
- if (result[o.key] === o.envValue) {
203
- result[o.key] = o.originalValue;
209
+ const rec = result;
210
+ if (rec[o.key] === o.envValue) {
211
+ rec[o.key] = o.originalValue;
204
212
  }
205
213
  }
206
214
  return result;
207
215
  }
208
216
  /**
209
- * Load config from domain files (preferred) or legacy config.json (fallback).
217
+ * Грузит конфиг из файлов доменов (предпочтительно) или legacy config.json.
210
218
  *
211
- * Pipeline:
219
+ * Конвейер:
212
220
  * 1. DEFAULTS
213
- * 2. deepMerge(legacy config.json) — if exists
214
- * 3. deepMerge(domain files) — if exist, override legacy
215
- * 4. deepMerge(project .mmrc) — overrides everything
216
- * 5. Post-load fixups (RegExp, LSP args)
217
- * 6. Env vars
221
+ * 2. deepMerge(legacy config.json) — если есть
222
+ * 3. deepMerge(файлы доменов) — если есть, поверх legacy
223
+ * 4. deepMerge(проектный .mmrc) — поверх всего
224
+ * 5. Пост-загрузочные фиксы (RegExp, LSP-args)
225
+ * 6. Env-переменные
218
226
  * 7. Decrypt
219
227
  *
220
- * On first run (no config.json, no domain files), auto-creates domain files.
228
+ * На первом запуске (нет ни config.json, ни доменов) результат — DEFAULTS;
229
+ * ничего не пишется: персист только через `saveConfig`.
221
230
  */
222
231
  export function loadConfig(options) {
223
232
  const globalPath = join(options.configDir, "config.json");
224
233
  mkdirSync(options.configDir, { recursive: true });
225
- // v1 → v2 migration check (reads config.json directly, before domain logic)
234
+ // Проверка v1 → v2 (читает config.json напрямую, до логики доменов).
226
235
  const detector = new MigrationDetector(options.configDir);
227
236
  if (detector.needsMigration()) {
228
237
  const backup = new BackupManager(options.configDir);
229
- // Full-directory backup (documented behavior) + config.json snapshot
230
- // before we touch anything.
238
+ // Полный бэкап каталога (документированное поведение) + снапшот config.json
239
+ // до любых касаний.
231
240
  backup.backupAll();
232
241
  backup.backupConfig();
233
242
  const summary = backup.getBackupSummary();
@@ -236,54 +245,51 @@ export function loadConfig(options) {
236
245
  unlinkSync(globalPath);
237
246
  }
238
247
  catch (e) {
239
- // Best-effort removal: loadConfig falls back to defaults either way,
240
- // but a leftover v1 file would re-trigger migration on every startup.
248
+ // Best-effort удаление: loadConfig и так откатится на дефолты, но
249
+ // leftover v1-файл ретриггерил бы миграцию на каждом старте.
241
250
  defaultOutputBus.log("error", "config", `[config] failed to remove legacy config.json: ${e instanceof Error ? e.message : String(e)}`);
242
251
  }
243
252
  }
244
- // Deep clone: DEFAULTS holds singleton objects (security, lsp, tools…)
245
- // that must never be mutated by runtime config changes.
253
+ // Глубокий клон: DEFAULTS держит синглтон-объекты (security, lsp, tools…),
254
+ // которые рантайм-изменения конфига мутировать не должны.
246
255
  let config = structuredClone(DEFAULTS);
247
256
  let legacyDetected = false;
248
- // Step 1: legacy config.json (fallback)
257
+ // Шаг 2: legacy config.json (фолбэк).
249
258
  const globalData = loadJSON(globalPath);
250
259
  if (globalData) {
251
260
  config = deepMerge(config, globalData);
252
261
  legacyDetected = true;
253
262
  }
254
- // Step 2: domain files (override legacy)
263
+ // Шаг 3: файлы доменов (поверх legacy).
255
264
  const domainConfig = loadDomainFiles(options.configDir);
256
265
  if (domainConfig) {
257
266
  config = deepMerge(config, domainConfig);
258
267
  legacyDetected = false;
259
268
  }
260
- // Step 3: first run — auto-create domain files instead of config.json
261
- if (!globalData && !domainConfig) {
262
- saveDomainFiles(config, options.configDir);
263
- }
264
- // Step 4: project .mmrc (overrides everything)
269
+ // Шаг 4: проектный .mmrc (поверх всего).
265
270
  const projectData = loadJSON(options.projectConfigPath);
266
271
  if (projectData) {
267
272
  config = deepMerge(config, projectData);
268
273
  }
269
- // Step 5: post-load fixups — AFTER all merges
274
+ // Шаг 5: пост-загрузочные фиксы — ПОСЛЕ всех мержей.
270
275
  if (config.security?.contentScan?.dangerousPatterns) {
271
276
  config.security.contentScan.dangerousPatterns = restoreDangerousPatterns(config.security.contentScan.dangerousPatterns, DEFAULT_SECURITY_CONFIG.contentScan.dangerousPatterns);
272
277
  }
273
278
  normalizeLspServerArgs(config);
274
- // Step 6: env vars
279
+ // Шаг 6: env-переменные.
275
280
  const { config: envConfig, overrides } = applyEnvVars(config);
276
281
  config = envConfig;
277
- // Step 7: decrypt
282
+ // Шаг 7: decrypt.
278
283
  try {
279
284
  const encryptor = new ConfigEncryptor();
280
285
  const decrypted = encryptor.decrypt(config);
286
+ const configRec = config;
281
287
  for (const key of Object.keys(decrypted)) {
282
- config[key] = decrypted[key];
288
+ configRec[key] = decrypted[key];
283
289
  }
284
- // Decrypt rebuilds the object tree (new identities). Re-point the provider
285
- // override at the post-decrypt provider so the save-time identity check in
286
- // stripEnvOverrides still matches the loaded config.
290
+ // Decrypt пересобирает дерево объектов (новые identity). Переприцеливаем
291
+ // provider-оверрайд на пост-decrypt провайдер, чтобы save-time identity-чек
292
+ // в stripEnvOverrides по-прежнему сходился с загруженным конфигом.
287
293
  const providerOverride = overrides.find((o) => o.key === "provider");
288
294
  if (providerOverride)
289
295
  providerOverride.envValue = config.provider;
@@ -295,6 +301,7 @@ export function loadConfig(options) {
295
301
  envOverrideMap.set(config, overrides);
296
302
  return { config, legacyDetected };
297
303
  }
304
+ /** Валидирует конфиг против известных тегов тулов. Ошибки — throw. */
298
305
  export function validateConfig(config, allToolTags) {
299
306
  const errors = validateExpertConfig(config, allToolTags);
300
307
  const knownTags = new Set(allToolTags);
@@ -308,20 +315,20 @@ export function validateConfig(config, allToolTags) {
308
315
  }
309
316
  }
310
317
  /**
311
- * Save config to domain files (preferred) or legacy single file (fallback).
318
+ * Пишет конфиг в файлы доменов (предпочтительно) или legacy-файл (фолбэк).
312
319
  *
313
- * @param config Full AppConfig to save
314
- * @param configPath Legacy path (e.g. ~/.mma/config.json) — used when configDir is absent
315
- * @param configDir Optional config directory (e.g. ~/.mma). When provided, writes domain files.
320
+ * @param config полный AppConfig для сохранения;
321
+ * @param configPath legacy-путь (напр. ~/.mma/config.json) — когда configDir нет;
322
+ * @param configDir каталог конфига (напр. ~/.mma). Если задан — пишет файлы доменов.
316
323
  */
317
324
  export function saveConfig(config, configPath, configDir) {
318
- // Session-scoped MMA_* env overrides must not be persisted (C4).
325
+ // Сессионные MMA_*-оверрайды персистить нельзя (C4).
319
326
  const toWrite = stripEnvOverrides(config);
320
327
  if (configDir) {
321
328
  saveDomainFiles(toWrite, configDir);
322
329
  return;
323
330
  }
324
- // Legacy single-file write (backward compat for tests / edge cases)
331
+ // Legacy-запись одним файлом (обратная совместимость для тестов/крайних случаев).
325
332
  const dir = dirname(configPath);
326
333
  mkdirSync(dir, { recursive: true });
327
334
  try {
@@ -1,7 +1,8 @@
1
1
  import { DEFAULT_SECURITY_CONFIG } from "./security";
2
2
  import { DEFAULT_LSP_CONFIG } from "../modules/lsp/config";
3
- /** Single source of truth for the default model (experts inherit it, C8). */
3
+ /** Дефолтная модель (эксперты наследуют её). */
4
4
  const DEFAULT_MODEL = "qwen/qwen3.5-9b";
5
+ /** Дефолтный конфиг. Глубоко мержится с пользовательским при загрузке. */
5
6
  export const DEFAULTS = {
6
7
  version: "2.0.0",
7
8
  model: DEFAULT_MODEL,
@@ -18,15 +19,28 @@ export const DEFAULTS = {
18
19
  enabled: false,
19
20
  maxReplanCycles: 3,
20
21
  },
22
+ critic: {
23
+ enabled: true,
24
+ // Phase 2: выделенные provider/model — opt-in; пока не заданы, делит активный
25
+ // провайдер (поведение v1). Read-only verify-цикл включён: запускается только
26
+ // после вердикта concern/critical, ограничен и fail-open.
27
+ provider: undefined,
28
+ model: undefined,
29
+ tools: true,
30
+ maxCalls: 4,
31
+ maxRepairCycles: 1,
32
+ timeoutMs: 15000,
33
+ },
21
34
  tools: {
22
- // TODO(2026-08-29): revisit tool-set visibility. enableOnDemand:false
23
- // restores the full ~28-tool set (legacy behavior) — safe for small
24
- // models but costs schema tokens per call. The on-demand narrowing
25
- // (defaultTags + enable_tools) kept the footprint small but made the 9B
26
- // model call hidden tools as shell commands / drift to MCP. Decide the
27
- // right trade-off for small models and pick one mode.
35
+ // Автосужение P5 (2026-09-28): enableOnDemand:true показывает только
36
+ // defaultTags + alwaysOn структурные/интерактивные тулы; теги под тип задачи
37
+ // детерминированно досеиваются до первого вызова LLM (src/tools/auto-tags.ts),
38
+ // а модель расширяет набор через enable_tools. Дрейф 9B, вызвавший откат
39
+ // (скрытые тулы звались как shell-команды), гасится блоком промпта про скрытые
40
+ // тулы, хинтом bash про скрытые тулы + редиректом tool-call и alwaysOn
41
+ // question/approve.
28
42
  defaultTags: ["file", "code"],
29
- enableOnDemand: false,
43
+ enableOnDemand: true,
30
44
  },
31
45
  experts: {
32
46
  code: {
@@ -54,11 +68,16 @@ export const DEFAULTS = {
54
68
  description: "browser + file reading for visual tasks",
55
69
  },
56
70
  },
71
+ // Переопределения возможностей по моделям (ключ — id модели). Пусто по
72
+ // умолчанию: первичны живой native-probe (LM Studio) и пресет провайдера,
73
+ // эта карта — ручной escape hatch, когда оба ошиблись.
74
+ modelCapabilities: {},
57
75
  contextWindow: 32768,
58
76
  contextBudget: {
59
77
  systemPrompt: 0.1,
60
78
  responseReserve: 0.15,
61
79
  compactionThreshold: 0.75,
80
+ compactionMinLoad: 0.5,
62
81
  },
63
82
  instructions: {
64
83
  summarize: true,
@@ -85,6 +104,11 @@ export const DEFAULTS = {
85
104
  autoSave: true,
86
105
  maxSessions: 50,
87
106
  isolateMemory: false,
107
+ autoName: true,
108
+ },
109
+ contextLog: {
110
+ enabled: false,
111
+ redact: true,
88
112
  },
89
113
  browser: {
90
114
  enabled: true,
@@ -114,9 +138,12 @@ export const DEFAULTS = {
114
138
  },
115
139
  ui: {
116
140
  spinner: true,
141
+ activityRail: "line",
117
142
  showContextStats: false,
118
143
  showCompaction: true,
119
144
  verbose: false,
145
+ verbosity: "quiet",
146
+ fontSize: "comfortable",
120
147
  },
121
148
  mcpServers: {
122
149
  context7: {
@@ -163,5 +190,8 @@ export const DEFAULTS = {
163
190
  min: "low",
164
191
  max: "high",
165
192
  overrideCooldown: 5,
193
+ // Тривиальные Q&A-ходы не должны платить за medium reasoning; политика всё
194
+ // равно поднимает до high на галлюцинациях/отклонениях аудита/создании плана.
195
+ baseline: "low",
166
196
  },
167
197
  };
@@ -1,11 +1,12 @@
1
+ // Домены конфига: раскладка AppConfig по файлам ~/.mma/config/<домен>.json.
1
2
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "fs";
2
3
  import { join } from "path";
3
4
  import { t } from "../i18n/index";
4
5
  import { deepMerge, regexReplacer, regexReviver } from "./utils";
5
6
  import { defaultOutputBus } from "../output";
6
7
  /**
7
- * Mapping: domain name → AppConfig keys that belong to it.
8
- * Each domain becomes one JSON file in ~/.mma/config/<domain>.json.
8
+ * Маппинг: имя домена → ключи AppConfig. Каждый домен — один JSON-файл
9
+ * в ~/.mma/config/<domain>.json.
9
10
  */
10
11
  export const CONFIG_DOMAINS = {
11
12
  core: [
@@ -24,43 +25,39 @@ export const CONFIG_DOMAINS = {
24
25
  "reasoning",
25
26
  "hallucination",
26
27
  ],
27
- provider: ["provider", "orchestrator", "retry", "pricing"],
28
+ provider: ["provider", "orchestrator", "retry", "pricing", "modelCapabilities"],
28
29
  moe: ["moe", "experts"],
30
+ critic: ["critic"],
29
31
  security: ["security"],
30
32
  browser: ["browser"],
31
33
  search: ["webSearch", "errorWebSearch"],
32
34
  tools: ["tools", "subagent"],
33
- session: ["session", "sessionIsolation"],
35
+ session: ["session", "sessionIsolation", "contextLog"],
34
36
  ui: ["ui"],
35
37
  mcp: ["mcpServers"],
36
38
  lsp: ["lsp"],
37
39
  skills: ["skills"],
38
40
  updater: ["updater"],
39
41
  };
40
- /**
41
- * Extract a domain subset from a full AppConfig.
42
- */
42
+ /** Вырезает подмножество ключей домена из полного AppConfig. */
43
43
  export function extractDomain(config, domain) {
44
44
  const keys = CONFIG_DOMAINS[domain];
45
45
  if (!keys)
46
46
  return {};
47
47
  const subset = {};
48
+ const source = config;
48
49
  for (const key of keys) {
49
- if (config[key] !== undefined) {
50
- subset[key] = config[key];
50
+ if (source[key] !== undefined) {
51
+ subset[key] = source[key];
51
52
  }
52
53
  }
53
54
  return subset;
54
55
  }
55
- /**
56
- * Get the file path for a domain config file.
57
- */
56
+ /** Путь к файлу домена. */
58
57
  export function getDomainFilePath(configDir, domain) {
59
58
  return join(configDir, "config", `${domain}.json`);
60
59
  }
61
- /**
62
- * Check if domain files exist in config/ directory.
63
- */
60
+ /** Проверяет, есть ли файлы доменов в каталоге config/. */
64
61
  export function hasDomainFiles(configDir) {
65
62
  const configDirPath = join(configDir, "config");
66
63
  if (!existsSync(configDirPath))
@@ -74,9 +71,9 @@ export function hasDomainFiles(configDir) {
74
71
  }
75
72
  }
76
73
  /**
77
- * Load all domain files from ~/.mma/config/ and merge into a partial AppConfig.
78
- * Each file is parsed with regexReviver to restore RegExp objects.
79
- * Returns null if no domain files exist.
74
+ * Грузит все файлы доменов из ~/.mma/config/ и мержит в частичный AppConfig.
75
+ * Каждый файл парсится с regexReviver (восстановление RegExp).
76
+ * Возвращает null, если файлов доменов нет.
80
77
  */
81
78
  export function loadDomainFiles(configDir) {
82
79
  const configDirPath = join(configDir, "config");
@@ -95,17 +92,17 @@ export function loadDomainFiles(configDir) {
95
92
  anyLoaded = true;
96
93
  }
97
94
  catch {
98
- // Never silent: surface the offending path instead of silently ignoring
99
- // a domain the user believes is active.
95
+ // Не молчим: показываем битый путь, а не тихо игнорим домен,
96
+ // который пользователь считает активным.
100
97
  defaultOutputBus.log("warn", "config", t("config.parse_failed", { path: filePath }));
101
98
  }
102
99
  }
103
100
  return anyLoaded ? result : null;
104
101
  }
105
102
  /**
106
- * Save a full AppConfig to domain files in ~/.mma/config/.
107
- * Each domain's keys are extracted and written to config/<domain>.json.
108
- * RegExp objects are serialized via regexReplacer.
103
+ * Пишет полный AppConfig по файлам доменов в ~/.mma/config/.
104
+ * Ключи каждого домена вырезаются и пишутся в config/<domain>.json.
105
+ * RegExp сериализуются через regexReplacer.
109
106
  */
110
107
  export function saveDomainFiles(config, configDir) {
111
108
  const configDirPath = join(configDir, "config");
@@ -113,9 +110,10 @@ export function saveDomainFiles(config, configDir) {
113
110
  for (const [domain, keys] of Object.entries(CONFIG_DOMAINS)) {
114
111
  const subset = {};
115
112
  let hasContent = false;
113
+ const source = config;
116
114
  for (const key of keys) {
117
- if (config[key] !== undefined) {
118
- subset[key] = config[key];
115
+ if (source[key] !== undefined) {
116
+ subset[key] = source[key];
119
117
  hasContent = true;
120
118
  }
121
119
  }
@@ -126,8 +124,7 @@ export function saveDomainFiles(config, configDir) {
126
124
  }
127
125
  }
128
126
  /**
129
- * Determine which domain a config key belongs to.
130
- * Returns the domain name or null if not found.
127
+ * Определяет домен ключа конфига. Возвращает имя домена или null.
131
128
  */
132
129
  export function domainForKey(key) {
133
130
  for (const [domain, keys] of Object.entries(CONFIG_DOMAINS)) {