micro-models-agent 2.11.0 → 2.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/assets/app.js +22 -2
  3. package/dist/assets/index.html +39 -9
  4. package/dist/assets/js/dom.js +15 -0
  5. package/dist/assets/js/feed.js +129 -10
  6. package/dist/assets/js/sidebar.js +512 -0
  7. package/dist/assets/js/tabs.js +30 -5
  8. package/dist/assets/setup.js +267 -8
  9. package/dist/assets/styles.css +168 -3
  10. package/dist/cli/cache-line.js +1 -0
  11. package/dist/cli/command-suggest.js +5 -4
  12. package/dist/cli/commands.js +231 -122
  13. package/dist/cli/completer.js +132 -31
  14. package/dist/cli/index.js +1 -0
  15. package/dist/cli/json-payload.js +4 -4
  16. package/dist/cli/main.js +166 -61
  17. package/dist/cli/onboarding-commands.js +126 -0
  18. package/dist/cli/plugin-commands.js +2 -1
  19. package/dist/cli/relaunch.js +16 -13
  20. package/dist/cli/repl-commands-lsp.js +75 -0
  21. package/dist/cli/repl-commands-skills.js +105 -0
  22. package/dist/cli/repl-commands.js +589 -266
  23. package/dist/cli/repl.js +436 -177
  24. package/dist/cli/run-result.js +5 -6
  25. package/dist/cli/security-commands.js +37 -28
  26. package/dist/cli/setup-order.js +4 -55
  27. package/dist/cli/setup-prompt.js +3 -2
  28. package/dist/cli/setup.js +27 -107
  29. package/dist/cli/web-command.js +98 -0
  30. package/dist/config/budget.js +5 -5
  31. package/dist/config/config.js +103 -96
  32. package/dist/config/defaults.js +38 -8
  33. package/dist/config/domains.js +24 -27
  34. package/dist/config/env.js +46 -0
  35. package/dist/config/experts.js +2 -0
  36. package/dist/config/index.js +1 -0
  37. package/dist/config/paths.js +56 -0
  38. package/dist/config/redact.js +44 -0
  39. package/dist/config/registry-extra.js +334 -0
  40. package/dist/config/registry.js +584 -0
  41. package/dist/config/security.js +35 -36
  42. package/dist/config/timeouts.js +74 -0
  43. package/dist/config/utils.js +16 -15
  44. package/dist/core/agent/artifact-progress.js +59 -0
  45. package/dist/core/agent/artifact-resolve.js +79 -0
  46. package/dist/core/agent/audit-gate.js +7 -3
  47. package/dist/core/agent/compaction.js +2 -2
  48. package/dist/core/agent/constants.js +33 -4
  49. package/dist/core/agent/context-log.js +454 -0
  50. package/dist/core/agent/context-renderer.js +5 -0
  51. package/dist/core/agent/critic-gate.js +132 -0
  52. package/dist/core/agent/hallucination-gate.js +148 -38
  53. package/dist/core/agent/loop-state.js +15 -0
  54. package/dist/core/agent/prefix-monitor.js +1 -1
  55. package/dist/core/agent/reasoning-resolver.js +10 -1
  56. package/dist/core/agent/token-tracker.js +7 -4
  57. package/dist/core/agent/tool-batch.js +93 -18
  58. package/dist/core/agent/tool-images.js +18 -0
  59. package/dist/core/agent/tool-output.js +65 -11
  60. package/dist/core/agent/tool-pairing.js +60 -0
  61. package/dist/core/agent-moe.js +25 -24
  62. package/dist/core/agent.js +429 -108
  63. package/dist/core/bootstrap/agent-phase.js +66 -0
  64. package/dist/core/bootstrap/config-phase.js +86 -0
  65. package/dist/core/bootstrap/modules-phase.js +269 -0
  66. package/dist/core/bootstrap/provider-phase.js +183 -0
  67. package/dist/core/bootstrap/session-phase.js +204 -0
  68. package/dist/core/bootstrap/state.js +94 -0
  69. package/dist/core/bootstrap/workspace-phase.js +115 -0
  70. package/dist/core/bootstrap.js +92 -660
  71. package/dist/core/crash-handler.js +17 -7
  72. package/dist/core/environment.js +37 -12
  73. package/dist/core/host-plan.js +43 -0
  74. package/dist/core/host.js +23 -0
  75. package/dist/core/index.js +1 -0
  76. package/dist/core/prompt-builder.js +17 -1
  77. package/dist/core/prompt-overflow.js +42 -43
  78. package/dist/core/session-logger.js +122 -30
  79. package/dist/core/system-prompt.js +117 -0
  80. package/dist/core/version.js +6 -4
  81. package/dist/core/web-host.js +34 -0
  82. package/dist/core/workspace.js +19 -15
  83. package/dist/i18n/en.json +89 -3
  84. package/dist/i18n/index.js +37 -3
  85. package/dist/i18n/ru.json +89 -3
  86. package/dist/index.js +7 -3
  87. package/dist/llm/image-utils.js +55 -64
  88. package/dist/llm/llm-errors.js +9 -0
  89. package/dist/llm/model-loader.js +10 -32
  90. package/dist/llm/models-dev.js +121 -0
  91. package/dist/llm/openai-compat.js +146 -73
  92. package/dist/llm/orchestrator.js +33 -11
  93. package/dist/llm/provider-budget.js +2 -2
  94. package/dist/llm/provider-context.js +233 -0
  95. package/dist/llm/provider.js +1 -1
  96. package/dist/llm/response.js +5 -0
  97. package/dist/llm/token-counter.js +27 -11
  98. package/dist/logger/app-logger.js +48 -22
  99. package/dist/logger/file-log.js +28 -19
  100. package/dist/logger/index.js +1 -0
  101. package/dist/main.js +888 -139
  102. package/dist/migration/backup.js +14 -3
  103. package/dist/migration/detect.js +12 -3
  104. package/dist/migration/index.js +1 -0
  105. package/dist/modules/artifacts/store.js +9 -8
  106. package/dist/modules/browser/actions.js +6 -0
  107. package/dist/modules/browser/bridge-client.js +14 -12
  108. package/dist/modules/browser/bridge-path.js +4 -4
  109. package/dist/modules/browser/console-buffer.js +37 -0
  110. package/dist/modules/browser/cookie-store.js +5 -0
  111. package/dist/modules/browser/driver-types.js +1 -0
  112. package/dist/modules/browser/driver.js +19 -11
  113. package/dist/modules/browser/index.js +3 -1
  114. package/dist/modules/browser/module.js +2 -1
  115. package/dist/modules/browser/session.js +10 -30
  116. package/dist/modules/browser/snapshot.js +19 -13
  117. package/dist/modules/browser/types.js +1 -0
  118. package/dist/modules/certification/cli.js +28 -20
  119. package/dist/modules/certification/fact-checker.js +9 -1
  120. package/dist/modules/certification/loader.js +9 -2
  121. package/dist/modules/certification/manifest.js +83 -21
  122. package/dist/modules/certification/runner.js +23 -13
  123. package/dist/modules/certification/scenarios.js +20 -20
  124. package/dist/modules/certification/syntax-scenarios.js +1 -2
  125. package/dist/modules/context/chunk-query.js +11 -9
  126. package/dist/modules/context/fact-extractor.js +37 -39
  127. package/dist/modules/context/index.js +1 -0
  128. package/dist/modules/context/manager.js +106 -80
  129. package/dist/modules/critic/client.js +176 -0
  130. package/dist/modules/critic/evidence.js +81 -0
  131. package/dist/modules/critic/provider.js +45 -0
  132. package/dist/modules/critic/tools.js +78 -0
  133. package/dist/modules/critic/types.js +1 -0
  134. package/dist/modules/execution/audit-runners.js +28 -34
  135. package/dist/modules/execution/auditor.js +53 -49
  136. package/dist/modules/execution/execution-plugin.js +142 -102
  137. package/dist/modules/execution/index.js +1 -1
  138. package/dist/modules/execution/input-from.js +37 -13
  139. package/dist/modules/execution/module.js +368 -191
  140. package/dist/modules/execution/moe-executor.js +53 -26
  141. package/dist/modules/execution/plan-actions.js +320 -202
  142. package/dist/modules/execution/plan-checks.js +35 -0
  143. package/dist/modules/execution/plan-coverage.js +36 -33
  144. package/dist/modules/execution/plan-steps.js +11 -3
  145. package/dist/modules/execution/plan-store.js +83 -17
  146. package/dist/modules/execution/plan-tool.js +89 -35
  147. package/dist/modules/execution/plan-validator.js +30 -10
  148. package/dist/modules/execution/planner.js +22 -13
  149. package/dist/modules/execution/stuck-detector.js +196 -93
  150. package/dist/modules/execution/tracker.js +22 -8
  151. package/dist/modules/execution/verifier.js +69 -48
  152. package/dist/modules/execution/windows-commands.js +12 -12
  153. package/dist/modules/hallucination/artifact-claim.js +93 -0
  154. package/dist/modules/hallucination/confidence.js +13 -11
  155. package/dist/modules/hallucination/consistency.js +10 -2
  156. package/dist/modules/hallucination/detector.js +10 -5
  157. package/dist/modules/hallucination/factual.js +43 -26
  158. package/dist/modules/hallucination/index.js +1 -0
  159. package/dist/modules/hallucination/js-identifiers.js +44 -45
  160. package/dist/modules/hallucination/llm-judge.js +17 -12
  161. package/dist/modules/hallucination/success-claim.js +59 -0
  162. package/dist/modules/indexer/cache.js +52 -4
  163. package/dist/modules/indexer/index.js +1 -0
  164. package/dist/modules/indexer/map-command.js +2 -4
  165. package/dist/modules/indexer/map-select.js +16 -16
  166. package/dist/modules/indexer/module.js +47 -12
  167. package/dist/modules/indexer/project-profile.js +8 -7
  168. package/dist/modules/indexer/symbols.js +15 -15
  169. package/dist/modules/indexer/walker.js +159 -29
  170. package/dist/modules/lsp/check-tool.js +7 -5
  171. package/dist/modules/lsp/client.js +39 -39
  172. package/dist/modules/lsp/command.js +17 -24
  173. package/dist/modules/lsp/config.js +12 -11
  174. package/dist/modules/lsp/index.js +1 -0
  175. package/dist/modules/lsp/module.js +48 -46
  176. package/dist/modules/lsp/probe.js +13 -14
  177. package/dist/modules/lsp/project-root.js +14 -17
  178. package/dist/modules/lsp/startup-check.js +18 -16
  179. package/dist/modules/mcp/client.js +59 -62
  180. package/dist/modules/mcp/index.js +1 -0
  181. package/dist/modules/mcp/module.js +66 -37
  182. package/dist/modules/mcp/registry.js +5 -0
  183. package/dist/modules/memory/index.js +1 -0
  184. package/dist/modules/memory/module.js +12 -8
  185. package/dist/modules/memory/search.js +4 -1
  186. package/dist/modules/memory/store.js +40 -2
  187. package/dist/modules/onboarding/init.js +47 -0
  188. package/dist/modules/onboarding/meet.js +30 -0
  189. package/dist/modules/onboarding/mode.js +30 -0
  190. package/dist/modules/onboarding/prompts.js +43 -0
  191. package/dist/modules/pipelines/engine.js +10 -0
  192. package/dist/modules/pipelines/index.js +1 -0
  193. package/dist/modules/pipelines/parser.js +4 -1
  194. package/dist/modules/pipelines/template.js +4 -0
  195. package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
  196. package/dist/modules/plugins/builtin/notify.js +2 -0
  197. package/dist/modules/plugins/index.js +1 -0
  198. package/dist/modules/plugins/loader.js +18 -3
  199. package/dist/modules/plugins/manager.js +37 -17
  200. package/dist/modules/pricing/index.js +10 -11
  201. package/dist/modules/pricing/prices.js +12 -12
  202. package/dist/modules/processes/index.js +1 -0
  203. package/dist/modules/processes/kill-tree.js +27 -31
  204. package/dist/modules/processes/registry.js +44 -25
  205. package/dist/modules/processes/runner.js +4 -4
  206. package/dist/modules/providers/capabilities.js +28 -0
  207. package/dist/modules/providers/create.js +3 -4
  208. package/dist/modules/providers/factory.js +7 -7
  209. package/dist/modules/providers/fallback.js +12 -12
  210. package/dist/modules/providers/health.js +4 -4
  211. package/dist/modules/providers/manager.js +50 -19
  212. package/dist/modules/providers/naming.js +15 -0
  213. package/dist/modules/providers/preflight.js +71 -0
  214. package/dist/modules/providers/presets.js +39 -8
  215. package/dist/modules/providers/registry.js +7 -2
  216. package/dist/modules/reasoning/policy.js +8 -6
  217. package/dist/modules/reasoning/probe.js +30 -25
  218. package/dist/modules/security/audit-log.js +19 -36
  219. package/dist/modules/security/audit-notifier.js +35 -75
  220. package/dist/modules/security/command-validator.js +99 -51
  221. package/dist/modules/security/content-scanner.js +8 -8
  222. package/dist/modules/security/data-sanitizer.js +9 -16
  223. package/dist/modules/security/encryption.js +32 -52
  224. package/dist/modules/security/index.js +2 -1
  225. package/dist/modules/security/network-validator.js +120 -43
  226. package/dist/modules/security/path-validator.js +61 -46
  227. package/dist/modules/security/protected-paths.js +73 -0
  228. package/dist/modules/security/rate-limiter.js +17 -49
  229. package/dist/modules/security/security-policies.js +25 -57
  230. package/dist/modules/security/session-encryption.js +32 -71
  231. package/dist/modules/security/session-isolation.js +15 -28
  232. package/dist/modules/session/auto-name.js +63 -0
  233. package/dist/modules/session/export.js +98 -0
  234. package/dist/modules/session/index.js +1 -0
  235. package/dist/modules/session/manager.js +79 -28
  236. package/dist/modules/session/module.js +4 -3
  237. package/dist/modules/session/store.js +57 -20
  238. package/dist/modules/setup/answers.js +35 -0
  239. package/dist/modules/setup/apply.js +79 -0
  240. package/dist/modules/setup/discovery.js +72 -0
  241. package/dist/modules/skills/index.js +1 -0
  242. package/dist/modules/skills/loader.js +9 -1
  243. package/dist/modules/skills/module.js +11 -0
  244. package/dist/modules/updater/changelog-reader.js +15 -16
  245. package/dist/modules/updater/checker.js +9 -4
  246. package/dist/modules/updater/dev-detect.js +7 -6
  247. package/dist/modules/updater/index.js +1 -0
  248. package/dist/modules/updater/module.js +17 -15
  249. package/dist/modules/user-profile/compressor.js +2 -0
  250. package/dist/modules/user-profile/index.js +1 -0
  251. package/dist/modules/user-profile/profile.js +10 -1
  252. package/dist/modules/webui/answer-api.js +29 -0
  253. package/dist/modules/webui/assets.js +50 -0
  254. package/dist/modules/webui/body.js +21 -0
  255. package/dist/modules/webui/commands-settings.js +209 -0
  256. package/dist/modules/webui/commands-shared.js +60 -0
  257. package/dist/modules/webui/commands.js +174 -0
  258. package/dist/modules/webui/config-api.js +134 -0
  259. package/dist/modules/webui/file-api.js +202 -0
  260. package/dist/modules/webui/forward.js +159 -0
  261. package/dist/modules/webui/history-api.js +112 -0
  262. package/dist/modules/webui/host-state.js +60 -0
  263. package/dist/modules/webui/i18n-api.js +9 -0
  264. package/dist/modules/webui/limits.js +6 -0
  265. package/dist/modules/webui/log-forward.js +20 -0
  266. package/dist/modules/webui/memory-commands.js +64 -0
  267. package/dist/modules/webui/model-cache.js +18 -0
  268. package/dist/modules/webui/module.js +148 -0
  269. package/dist/modules/webui/persist.js +24 -0
  270. package/dist/modules/webui/prompt-io.js +97 -0
  271. package/dist/modules/webui/providers-api.js +337 -0
  272. package/dist/modules/webui/providers-view.js +56 -0
  273. package/dist/modules/webui/reasoning-view.js +15 -0
  274. package/dist/modules/webui/run-agent.js +81 -0
  275. package/dist/modules/webui/server.js +284 -0
  276. package/dist/modules/webui/sessions-api.js +178 -0
  277. package/dist/modules/webui/settings-api.js +561 -0
  278. package/dist/modules/webui/setup-api.js +201 -0
  279. package/dist/modules/webui/state.js +74 -0
  280. package/dist/modules/webui/util.js +261 -0
  281. package/dist/output/bus.js +7 -2
  282. package/dist/output/channel.js +80 -28
  283. package/dist/output/format.js +2 -1
  284. package/dist/output/index.js +1 -0
  285. package/dist/output/json-sink.js +5 -3
  286. package/dist/output/machine.js +4 -3
  287. package/dist/output/session-sink.js +4 -4
  288. package/dist/tools/approve.js +4 -0
  289. package/dist/tools/attach-image.js +5 -4
  290. package/dist/tools/auto-fixer.js +26 -44
  291. package/dist/tools/auto-tags.js +83 -0
  292. package/dist/tools/bash.js +193 -116
  293. package/dist/tools/browser.js +76 -31
  294. package/dist/tools/chunk-query.js +14 -6
  295. package/dist/tools/create-dir.js +9 -5
  296. package/dist/tools/delete-file.js +9 -5
  297. package/dist/tools/download-file.js +32 -7
  298. package/dist/tools/edit-file.js +52 -14
  299. package/dist/tools/enable-tools.js +6 -6
  300. package/dist/tools/executor.js +132 -27
  301. package/dist/tools/file-info.js +3 -1
  302. package/dist/tools/filter-tools.js +3 -3
  303. package/dist/tools/glob-tool.js +4 -1
  304. package/dist/tools/grep-tool.js +13 -10
  305. package/dist/tools/hidden-tools-block.js +6 -6
  306. package/dist/tools/index.js +16 -8
  307. package/dist/tools/list-dir.js +4 -2
  308. package/dist/tools/load-skill.js +7 -0
  309. package/dist/tools/mcp-call.js +15 -4
  310. package/dist/tools/meet-done.js +14 -0
  311. package/dist/tools/move-file.js +14 -7
  312. package/dist/tools/path-utils.js +29 -29
  313. package/dist/tools/pipeline-run.js +29 -7
  314. package/dist/tools/preview.js +1 -1
  315. package/dist/tools/process-ids.js +31 -0
  316. package/dist/tools/process-kill.js +9 -16
  317. package/dist/tools/process-list.js +1 -0
  318. package/dist/tools/process-log.js +11 -16
  319. package/dist/tools/process-wait.js +63 -0
  320. package/dist/tools/prompt-io.js +4 -3
  321. package/dist/tools/question.js +172 -25
  322. package/dist/tools/read-file.js +55 -34
  323. package/dist/tools/recall.js +14 -8
  324. package/dist/tools/registry.js +6 -5
  325. package/dist/tools/remember.js +8 -4
  326. package/dist/tools/scope-request.js +11 -6
  327. package/dist/tools/search-history.js +5 -2
  328. package/dist/tools/session-info.js +2 -0
  329. package/dist/tools/set-thinking.js +8 -4
  330. package/dist/tools/subagent.js +59 -39
  331. package/dist/tools/syntax-validator.js +292 -90
  332. package/dist/tools/user-input.js +22 -18
  333. package/dist/tools/web-browse.js +9 -3
  334. package/dist/tools/web-fetch.js +12 -23
  335. package/dist/tools/web-search.js +7 -5
  336. package/dist/tools/write-file.js +45 -15
  337. package/dist/ui/action-log.js +13 -0
  338. package/dist/ui/box.js +4 -3
  339. package/dist/ui/colors.js +2 -0
  340. package/dist/ui/diff.js +6 -5
  341. package/dist/ui/index.js +1 -0
  342. package/dist/ui/line-editor.js +230 -122
  343. package/dist/ui/line-math.js +10 -2
  344. package/dist/ui/md-formatter.js +57 -37
  345. package/dist/ui/output.js +8 -3
  346. package/dist/ui/plan-view.js +21 -10
  347. package/dist/ui/renderer.js +393 -67
  348. package/dist/ui/spinner.js +16 -5
  349. package/dist/ui/table.js +15 -17
  350. package/dist/ui/theme.js +143 -0
  351. package/dist/utils/child-run.js +20 -0
  352. package/dist/utils/error.js +1 -1
  353. package/dist/utils/index.js +2 -0
  354. package/dist/utils/retry.js +2 -2
  355. package/dist/utils/truncate.js +1 -1
  356. package/package.json +1 -1
@@ -2,10 +2,10 @@ import { createProvider, defaultRegistry } from "./create";
2
2
  import { resolveCacheCapability } from "./cache";
3
3
  import { BUILTIN_PROVIDERS } from "./presets";
4
4
  /**
5
- * Manages the active provider + model at runtime. Supports several configured
6
- * providers (`ProviderConfig.entries`) with hot-swap (`switch`, `setModel`).
7
- * Legacy single-provider configs (`{type, baseUrl, apiKey}`) are normalized
8
- * into a single-entry list, so existing configs keep working.
5
+ * Рулит активным провайдером + моделью в рантайме. Несколько настроенных
6
+ * провайдеров (`ProviderConfig.entries`) с hot-swap (`switch`, `setModel`).
7
+ * Легаси-конфиги (`{type, baseUrl, apiKey}`) нормализуются в список из одной
8
+ * записи — старые конфиги продолжают работать.
9
9
  */
10
10
  export class ProviderManager {
11
11
  entries;
@@ -23,24 +23,26 @@ export class ProviderManager {
23
23
  this.lastKnownFallback = config.fallback;
24
24
  this.model = "";
25
25
  }
26
- /** The model the active provider is built with. */
26
+ /** Модель, с которой собран активный провайдер. */
27
27
  get activeModel() {
28
28
  return this.model;
29
29
  }
30
- /** Name/label of the active provider. */
30
+ /** Имя/label активного провайдера. */
31
31
  get activeName() {
32
32
  return this.activeProviderName;
33
33
  }
34
+ /** Активный провайдер (строится и кешируется). */
34
35
  get active() {
35
36
  return this.build(this.activeProviderName);
36
37
  }
38
+ /** Активная запись (неизвестная — ошибка). */
37
39
  get activeEntry() {
38
40
  const entry = this.entryByName(this.activeProviderName);
39
41
  if (!entry)
40
42
  throw new Error(`Unknown active provider "${this.activeProviderName}"`);
41
43
  return entry;
42
44
  }
43
- /** All configured entries (resolved with preset metadata), priority-ordered. */
45
+ /** Все записи (с метаданными пресетов), по приоритету. */
44
46
  list() {
45
47
  return this.entries
46
48
  .map((e) => {
@@ -51,17 +53,18 @@ export class ProviderManager {
51
53
  .sort((a, b) => a.p - b.p || a.i - b.i)
52
54
  .map(({ e }) => e);
53
55
  }
56
+ /** Имена всех записей по приоритету. */
54
57
  listNames() {
55
58
  return this.list().map((e) => e.label);
56
59
  }
57
60
  /**
58
- * CODE-ONLY capability routing (Phase 4.4): pick the first entry (priority
59
- * order) whose preset capabilities and context window satisfy `needs`.
60
- * Never exposed to the LLM — the model must not choose providers.
61
+ * CODE-ONLY роутинг capabilities: первая запись (по приоритету), чей пресет
62
+ * и окно контекста закрывают `needs`. Модели не exposed — модель провайдеров
63
+ * не выбирает.
61
64
  */
62
65
  pickFor(needs) {
63
66
  const globalWindow = this.opts.contextWindow;
64
- // No constraints → keep the current active provider.
67
+ // Без ограничений — оставить текущий активный провайдер.
65
68
  if (!needs.vision && !needs.tools && needs.minContext === undefined) {
66
69
  return this.activeProviderName;
67
70
  }
@@ -79,7 +82,7 @@ export class ProviderManager {
79
82
  }
80
83
  return undefined;
81
84
  }
82
- /** Hot-swap to a named provider; rebuild the active provider. */
85
+ /** Hot-swap на именованный провайдер; пересобрать активный. */
83
86
  switch(name, model) {
84
87
  if (!this.entryByName(name)) {
85
88
  throw new Error(`Unknown provider "${name}"`);
@@ -89,19 +92,23 @@ export class ProviderManager {
89
92
  this.model = model;
90
93
  return this.active;
91
94
  }
92
- /** Rebuild the active provider with a new model. */
95
+ /** Пересобрать активный провайдер с новой моделью. */
93
96
  setModel(model) {
94
97
  this.model = model;
98
+ // Кэш ключуется `${name}|${model}`; старые записи под прежнюю модель
99
+ // бесполезны и копятся. Сбрасываем — провайдер пересоберётся под новый
100
+ // model при следующем обращении к `active`.
101
+ this.resetCache();
95
102
  return this.active;
96
103
  }
97
- /** Serialize back to ProviderConfig (keeps active/entries in sync). */
104
+ /** Сериализовать обратно в ProviderConfig (active/entries в синхроне). */
98
105
  toConfig(fallback) {
99
106
  const active = this.activeEntry;
100
107
  return {
101
108
  active: this.activeProviderName,
102
109
  entries: this.entries,
103
- // Preserve the configured value — hardcoding false silently dropped
104
- // the user's provider.fallback on every hot-swap.
110
+ // Сохранить настроенное значение — хардкод false молча ронял
111
+ // пользовательский provider.fallback при каждом hot-swap.
105
112
  fallback: fallback ?? this.lastKnownFallback ?? false,
106
113
  type: active.type,
107
114
  baseUrl: active.baseUrl,
@@ -112,7 +119,7 @@ export class ProviderManager {
112
119
  if (Array.isArray(config.entries) && config.entries.length > 0) {
113
120
  return config.entries.map((e) => ({ ...e, label: e.label || e.type }));
114
121
  }
115
- // Legacy single-provider form (or empty config) → one default entry.
122
+ // Легаси-форма одного провайдера (или пустой конфиг) → одна дефолтная запись.
116
123
  return [
117
124
  {
118
125
  type: config.type ?? "openai-compat",
@@ -162,15 +169,39 @@ export class ProviderManager {
162
169
  return { cache: resolveCacheCapability(undefined, entry.cache) };
163
170
  return { ...spec.capabilities, cache: resolveCacheCapability(spec, entry.cache) };
164
171
  }
165
- /** Clear the build cache (on config change). */
172
+ /** Очистить кеш сборки (при смене конфига). */
166
173
  resetCache() {
167
174
  this.cache.clear();
168
175
  }
169
176
  }
170
- /** Export preset metadata for the CLI (label/capabilities/baseUrl). */
177
+ /** Метаданные пресетов для CLI (label/capabilities/baseUrl). */
171
178
  export function presetOf(type) {
172
179
  const spec = BUILTIN_PROVIDERS.find((p) => p.type === type);
173
180
  if (!spec)
174
181
  return undefined;
175
182
  return { label: spec.label, baseUrl: spec.baseUrl };
176
183
  }
184
+ /**
185
+ * Эндпоинт АКТИВНОГО провайдера для внеполосных вызовов (context probe,
186
+ * автозагрузка моделей, ключ кеша reasoning-пробы). Легаси-плоские поля
187
+ * (`type`/`baseUrl`/`apiKey`) отражают активное на момент последней записи и
188
+ * протухают после hot-swap `/provider use` — истина в entries. Фолбэк на
189
+ * плоские поля для легаси-конфигов.
190
+ */
191
+ export function activeProviderEndpoint(config) {
192
+ const entries = Array.isArray(config?.entries) ? config.entries : [];
193
+ const active = entries.find((e) => e.label === config?.active || e.type === config?.active) ?? entries[0];
194
+ if (active) {
195
+ return { baseUrl: active.baseUrl, apiKey: active.apiKey, type: active.type };
196
+ }
197
+ return { baseUrl: config?.baseUrl ?? "", apiKey: config?.apiKey, type: config?.type ?? "openai-compat" };
198
+ }
199
+ /**
200
+ * Стратегия reasoning АКТИВНОЙ записи провайдера (из её пресета). Легаси-плоское
201
+ * `type` протухает после hot-swap `/provider use` и давало чужую стратегию
202
+ * (напр. `prompt-tag` при активном `opencode-go`); неизвестный тип → "none".
203
+ */
204
+ export function reasoningStrategyForProvider(config) {
205
+ const type = activeProviderEndpoint(config).type;
206
+ return BUILTIN_PROVIDERS.find((p) => p.type === type)?.capabilities.reasoningStrategy ?? "none";
207
+ }
@@ -0,0 +1,15 @@
1
+ /** Display/identity-имя записи: label, иначе type. */
2
+ export function providerEntryName(entry) {
3
+ return entry.label || entry.type;
4
+ }
5
+ /**
6
+ * Занято ли `name` другой записью (кроме `exceptName`).
7
+ * Записи адресуются по `label || type`, легаси-поиск матчит и голый `type`,
8
+ * так что новый/переименованный провайдер не должен коллизить ни с тем, ни с
9
+ * другим — иначе `provider.active` и edit/delete молча бьют не в ту запись.
10
+ * `exceptName` позволяет правке сохранить собственное текущее имя.
11
+ */
12
+ export function providerNameTaken(entries, name, exceptName) {
13
+ return entries.some((e) => providerEntryName(e) !== exceptName &&
14
+ (providerEntryName(e) === name || e.type === name));
15
+ }
@@ -0,0 +1,71 @@
1
+ import { errMsg } from "../../utils";
2
+ /** Base URL + ключ активного провайдера (сначала entries, потом legacy). */
3
+ export function resolveProviderTarget(provider) {
4
+ const entries = provider.entries ?? [];
5
+ if (entries.length > 0) {
6
+ const active = provider.active;
7
+ const entry = entries.find((e) => (e.label ?? e.type) === active) ??
8
+ entries.find((e) => e.type === active) ??
9
+ entries[0];
10
+ return { baseUrl: entry.baseUrl, apiKey: entry.apiKey ?? "" };
11
+ }
12
+ return { baseUrl: provider.baseUrl, apiKey: provider.apiKey ?? "" };
13
+ }
14
+ /** Резолв активного провайдера из конфига, затем его preflight. */
15
+ export function preflightFromConfig(ctx) {
16
+ const target = resolveProviderTarget(ctx.config.provider);
17
+ return preflightProvider({
18
+ baseUrl: target.baseUrl,
19
+ apiKey: target.apiKey,
20
+ model: ctx.config.model,
21
+ });
22
+ }
23
+ /** Минимальная дешёвая chat-проба: подтверждает, что модель загружена. */
24
+ export async function probeChat(cfg, timeoutMs) {
25
+ const url = `${cfg.baseUrl.replace(/\/+$/, "")}/chat/completions`;
26
+ const controller = new AbortController();
27
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
28
+ try {
29
+ const resp = await fetch(url, {
30
+ method: "POST",
31
+ headers: {
32
+ Authorization: `Bearer ${cfg.apiKey}`,
33
+ "Content-Type": "application/json",
34
+ },
35
+ body: JSON.stringify({
36
+ model: cfg.model,
37
+ messages: [{ role: "user", content: "ok" }],
38
+ max_tokens: 1,
39
+ }),
40
+ signal: controller.signal,
41
+ });
42
+ if (!resp.ok)
43
+ throw new Error(`HTTP ${resp.status}`);
44
+ const data = (await resp.json());
45
+ const msg = data.choices?.[0]?.message;
46
+ return msg?.content || msg?.reasoning_content || "";
47
+ }
48
+ finally {
49
+ clearTimeout(timer);
50
+ }
51
+ }
52
+ /**
53
+ * Проверить, что провайдер отвечает и модель загружена. Один chat-вызов с
54
+ * max_tokens=1 (стоимость пренебрежима). Не висит: ограничен timeoutMs.
55
+ */
56
+ export async function preflightProvider(cfg, opts = {}) {
57
+ const timeoutMs = opts.timeoutMs ?? 3000;
58
+ const probe = opts.probe ?? probeChat;
59
+ const start = Date.now();
60
+ try {
61
+ const text = await probe(cfg, timeoutMs);
62
+ const ms = Date.now() - start;
63
+ return text ? { ok: true, reason: "ok", ms } : { ok: false, reason: "no-model", ms };
64
+ }
65
+ catch (err) {
66
+ const ms = Date.now() - start;
67
+ const message = errMsg(err);
68
+ const reason = /abort|timeout/i.test(message) ? "timeout" : "unreachable";
69
+ return { ok: false, reason, ms };
70
+ }
71
+ }
@@ -1,6 +1,7 @@
1
+ // Builtin-пресеты провайдеров: capabilities, кеш-поведение, фабрики, base URL.
1
2
  // src/modules/providers/presets.ts
2
3
  import { OpenAICompatProvider } from "../../llm/openai-compat";
3
- /** Build an OpenAI-compatible provider (default for most backends). */
4
+ /** Сборка OpenAI-совместимого провайдера (дефолт для большинства бэкендов). */
4
5
  function openaiCompat(opts) {
5
6
  const retry = (opts.retry ?? {
6
7
  maxRetries: 3,
@@ -48,8 +49,37 @@ const OPENAI_COMPAT = {
48
49
  },
49
50
  create: openaiCompat,
50
51
  };
51
- const OPENROUTER = {
52
- type: "openrouter",
52
+ /** LM Studio как first-class провайдер: тот же OpenAI-транспорт,
53
+ * но свой type — native discovery (`/api/v1/models`: загруженный контекст,
54
+ * capabilities) и фиксированный дефолтный URL едут с ним, а не прячутся
55
+ * внутри generic-записи `openai-compat`. */
56
+ const LMSTUDIO = {
57
+ type: "lmstudio",
58
+ label: "LM Studio (localhost:1234)",
59
+ baseUrl: "http://localhost:1234/v1",
60
+ capabilities: {
61
+ streaming: true,
62
+ tools: true,
63
+ vision: false,
64
+ reasoningStrategy: "prompt-tag",
65
+ listModels: true,
66
+ requiresKey: false,
67
+ auth: "bearer",
68
+ // Тот же локальный KV-кеш, что и у generic openai-compat.
69
+ cache: {
70
+ mechanism: "local",
71
+ report: "openai",
72
+ sessionHeader: false,
73
+ requestCachePrompt: false,
74
+ requestPromptCacheKey: false,
75
+ requestSessionId: false,
76
+ requestCacheControl: false,
77
+ requestStreamUsage: true,
78
+ },
79
+ },
80
+ create: openaiCompat,
81
+ };
82
+ const OPENROUTER = { type: "openrouter",
53
83
  label: "OpenRouter (openrouter.ai)",
54
84
  baseUrl: "https://openrouter.ai/api/v1",
55
85
  capabilities: {
@@ -127,9 +157,9 @@ const ANTHROPIC = {
127
157
  requestStreamUsage: false,
128
158
  },
129
159
  },
130
- // The Anthropic messages API is NOT OpenAI-compatible. This preset is a
131
- // placeholder until a real AnthropicProvider lands; the factory still uses
132
- // the OpenAI-compatible code path today (broken), flagged via capabilities.
160
+ // Messages API Anthropic НЕ OpenAI-совместим. Пресет — заглушка до настоящего
161
+ // AnthropicProvider; фабрика пока идёт по OpenAI-кодпасу (сломано), что видно
162
+ // через capabilities.
133
163
  create: openaiCompat,
134
164
  };
135
165
  const OPENCODE_ZEN = {
@@ -184,16 +214,17 @@ const OPENCODE_GO = {
184
214
  },
185
215
  create: openaiCompat,
186
216
  };
187
- /** All builtin provider presets. */
217
+ /** Все builtin-пресеты провайдеров. */
188
218
  export const BUILTIN_PROVIDERS = [
189
219
  OPENAI_COMPAT,
220
+ LMSTUDIO,
190
221
  OPENROUTER,
191
222
  OPENAI,
192
223
  ANTHROPIC,
193
224
  OPENCODE_ZEN,
194
225
  OPENCODE_GO,
195
226
  ];
196
- /** Fixed base URLs for hosted providers (no port scan / model autodetect). */
227
+ /** Фиксированные base URL хостед-провайдеров (без скан-портов / автодетекта моделей). */
197
228
  export const HOSTED_BASE_URLS = {};
198
229
  for (const spec of BUILTIN_PROVIDERS) {
199
230
  if (spec.baseUrl)
@@ -1,21 +1,26 @@
1
1
  /**
2
- * Single source of truth mapping provider `type` → factory + capabilities.
3
- * Adding a provider = one `register()` call; no changes to the agent loop.
2
+ * Единый источник правды type → фабрика + capabilities.
3
+ * Добавление провайдера — один вызов `register()`; петля агента не меняется.
4
4
  */
5
5
  export class ProviderRegistry {
6
6
  specs = new Map();
7
+ /** Зарегистрировать спек провайдера. */
7
8
  register(spec) {
8
9
  this.specs.set(spec.type, spec);
9
10
  }
11
+ /** Спек по типу, иначе undefined. */
10
12
  get(type) {
11
13
  return this.specs.get(type);
12
14
  }
15
+ /** Зарегистрирован ли тип. */
13
16
  has(type) {
14
17
  return this.specs.has(type);
15
18
  }
19
+ /** Все спеки. */
16
20
  list() {
17
21
  return [...this.specs.values()];
18
22
  }
23
+ /** Все зарегистрированные типы. */
19
24
  types() {
20
25
  return [...this.specs.keys()];
21
26
  }
@@ -1,26 +1,28 @@
1
+ /** Начальное состояние политики. */
1
2
  export function createPolicyState() {
2
3
  return { iterationsSinceRaise: 0, boostRemaining: 0 };
3
4
  }
4
5
  const DECAY_THRESHOLD = 5;
6
+ /** Вычисляет reasoning-уровень: подъём на сбоях, буст после плана, спад на рутине. */
5
7
  export function evaluateReasoningPolicy(input, state) {
6
- // Raise signals (highest priority)
8
+ // Сигналы подъёма (высший приоритет)
7
9
  if (input.hallucinationRetry || input.auditRejection || input.recoverableLlmError) {
8
10
  state.iterationsSinceRaise = 0;
9
11
  return { level: "high", reason: input.hallucinationRetry ? "policy:hallucination-retry" : input.auditRejection ? "policy:audit-rejection" : "policy:recoverable-llm-error" };
10
12
  }
11
- // Plan created → boost for N iterations
13
+ // Создан план → буст на N итераций
12
14
  if (input.planCreated) {
13
15
  state.boostRemaining = 3;
14
16
  state.iterationsSinceRaise = 0;
15
17
  return { level: "high", reason: "policy:plan-created" };
16
18
  }
17
- // Active boost from plan creation
19
+ // Активный буст от создания плана
18
20
  if (state.boostRemaining > 0) {
19
21
  state.boostRemaining--;
20
22
  state.iterationsSinceRaise = 0;
21
23
  return { level: "high", reason: "policy:plan-boost" };
22
24
  }
23
- // Lower signals
25
+ // Сигналы понижения
24
26
  if (input.consecutiveSuccesses >= 2) {
25
27
  state.iterationsSinceRaise++;
26
28
  return { level: "low", reason: "policy:routine-write" };
@@ -29,12 +31,12 @@ export function evaluateReasoningPolicy(input, state) {
29
31
  state.iterationsSinceRaise++;
30
32
  return { level: "low", reason: "policy:repetitive-tools" };
31
33
  }
32
- // Decay: if raised for too long, return to baseline
34
+ // Затухание: подъём держался слишком долго — возврат к базовому
33
35
  if (state.iterationsSinceRaise >= DECAY_THRESHOLD) {
34
36
  state.iterationsSinceRaise = 0;
35
37
  return { level: input.baseline, reason: "policy:decay" };
36
38
  }
37
- // Default: baseline
39
+ // По умолчанию: базовый уровень
38
40
  state.iterationsSinceRaise++;
39
41
  return { level: input.baseline, reason: "policy:default" };
40
42
  }
@@ -1,30 +1,33 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
2
2
  import { join, dirname } from "path";
3
- import { homedir } from "os";
3
+ import { globalMmaDir } from "../../config/paths";
4
+ import { readEnv } from "../../config/env";
5
+ import { REASONING_CACHE_TTL_MS as CACHE_TTL_MS } from "../../config/timeouts";
4
6
  const PROBE_MESSAGES = [
5
7
  { role: "user", content: "Reply with exactly: ok" },
6
8
  ];
7
- /** TTL: re-probe after 24 hours. */
8
- const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
9
- /** Disk cache path: ~/.mma/reasoning-cache.json (override with MMA_REASONING_CACHE). */
9
+ /** Путь дискового кэша: ~/.mma/reasoning-cache.json (переопределяется MMA_REASONING_CACHE). */
10
10
  function cachePath() {
11
- return process.env.MMA_REASONING_CACHE ?? join(homedir(), ".mma", "reasoning-cache.json");
11
+ return readEnv("REASONING_CACHE") ?? join(globalMmaDir(), "reasoning-cache.json");
12
12
  }
13
- /** In-memory L1 cache (same-process fast path). */
13
+ /** In-memory L1-кэш (быстрый путь в пределах процесса). */
14
14
  const memCache = new Map();
15
15
  /**
16
- * Probe whether the backend actually respects the reasoning effort mechanism.
17
- * Sends a trivial non-streaming request with level forced to "none" via the
18
- * mapped strategy. If reasoning_content still appears, the mechanism is ignored.
19
- */
20
- /**
21
- * Probe whether the backend actually respects the reasoning effort mechanism.
22
- * @returns `true` = mechanism respected, `false` = mechanism ignored,
23
- * `null` = transient error (caller must NOT cache this).
16
+ * Проверяет, уважает ли бэкенд механизм reasoning effort.
17
+ * Шлёт тривиальный запрос с уровнем "none": если reasoning_content всё равно
18
+ * появился — механизм игнорируется.
19
+ * @returns `true` — механизм уважается, `false` — игнорируется,
20
+ * `null` — транзиентная ошибка (вызывающий НЕ должен кэшировать).
24
21
  */
25
22
  export async function probeReasoningSupport(provider, strategy, signal) {
26
23
  if (strategy === "none")
27
24
  return false;
25
+ // "openai-effort" — стандартное поле OpenAI. Sentinel "none", которым проба
26
+ // измеряет подавление, транспорт вырезает (strict OpenAI на "none" отвечает
27
+ // 400), поэтому измерить нечем — считаем механизм поддерживаемым. Если
28
+ // бэкенд поле игнорирует, это безопасно (effort просто не влияет).
29
+ if (strategy === "openai-effort")
30
+ return true;
28
31
  try {
29
32
  let hasReasoning = false;
30
33
  for await (const chunk of provider.chat(PROBE_MESSAGES, undefined, signal, {
@@ -35,30 +38,31 @@ export async function probeReasoningSupport(provider, strategy, signal) {
35
38
  if (chunk.type === "reasoning")
36
39
  hasReasoning = true;
37
40
  }
38
- // If reasoning content appeared despite "none", the mechanism is ignored
41
+ // reasoning_content появился вопреки "none" — механизм игнорируется
39
42
  return !hasReasoning;
40
43
  }
41
44
  catch {
42
- // Transient error (network/auth/stream) — do NOT report "not supported".
43
- // Returning null signals the caller to skip caching, so a one-off failure
44
- // does not disable the reasoning mechanism for the 24h TTL.
45
+ // Транзиентная ошибка (сеть/авторизация/стрим) — НЕ сообщаем "не поддерживается".
46
+ // Возврат null запрещает вызывающему кэшировать, чтобы разовый сбой
47
+ // не отключал reasoning-механизм на весь 24ч TTL.
45
48
  return null;
46
49
  }
47
50
  }
51
+ /** Ключ кэша пробы: baseUrl + модель. */
48
52
  export function cacheKey(baseUrl, model) {
49
53
  return `${baseUrl}|${model}`;
50
54
  }
51
55
  /**
52
- * Read cached probe result. Checks in-memory first, then disk.
53
- * Returns `undefined` if cache miss or stale (>24h).
56
+ * Читает кэшированный результат пробы. Сначала память, потом диск.
57
+ * Возвращает `undefined` при промахе или протухании (>24ч).
54
58
  */
55
59
  export function getCachedProbeResult(key) {
56
- // L1: in-memory
60
+ // L1: память
57
61
  const mem = memCache.get(key);
58
62
  if (mem && Date.now() - mem.ts < CACHE_TTL_MS) {
59
63
  return mem.result;
60
64
  }
61
- // L2: disk
65
+ // L2: диск
62
66
  const disk = readDiskCache();
63
67
  const entry = disk[key];
64
68
  if (entry) {
@@ -71,7 +75,7 @@ export function getCachedProbeResult(key) {
71
75
  return undefined;
72
76
  }
73
77
  /**
74
- * Store probe result in both in-memory and disk cache.
78
+ * Кладет результат пробы в память и на диск.
75
79
  */
76
80
  export function setCachedProbeResult(key, result) {
77
81
  const now = new Date().toISOString();
@@ -80,6 +84,7 @@ export function setCachedProbeResult(key, result) {
80
84
  disk[key] = { result, ts: now };
81
85
  writeDiskCache(disk);
82
86
  }
87
+ /** Сбрасывает кэш пробы (память + дисковый файл). */
83
88
  export function resetProbeCache() {
84
89
  memCache.clear();
85
90
  const path = cachePath();
@@ -95,7 +100,7 @@ function readDiskCache() {
95
100
  }
96
101
  }
97
102
  catch {
98
- // Corrupted cache — start fresh
103
+ // Битый кэш — начинаем с чистого
99
104
  }
100
105
  return {};
101
106
  }
@@ -106,6 +111,6 @@ function writeDiskCache(data) {
106
111
  writeFileSync(path, JSON.stringify(data, null, 2), "utf-8");
107
112
  }
108
113
  catch {
109
- // Best-effort — disk write failure must not crash the agent
114
+ // Best-effort: сбой записи на диск не должен ронять агента
110
115
  }
111
116
  }