micro-models-agent 2.11.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 (347) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/assets/js/tabs.js +22 -3
  3. package/dist/cli/cache-line.js +1 -0
  4. package/dist/cli/command-suggest.js +5 -4
  5. package/dist/cli/commands.js +231 -122
  6. package/dist/cli/completer.js +132 -31
  7. package/dist/cli/index.js +1 -0
  8. package/dist/cli/json-payload.js +4 -4
  9. package/dist/cli/main.js +166 -61
  10. package/dist/cli/onboarding-commands.js +126 -0
  11. package/dist/cli/plugin-commands.js +2 -1
  12. package/dist/cli/relaunch.js +16 -13
  13. package/dist/cli/repl-commands-lsp.js +75 -0
  14. package/dist/cli/repl-commands-skills.js +105 -0
  15. package/dist/cli/repl-commands.js +589 -266
  16. package/dist/cli/repl.js +436 -177
  17. package/dist/cli/run-result.js +5 -6
  18. package/dist/cli/security-commands.js +37 -28
  19. package/dist/cli/setup-order.js +4 -55
  20. package/dist/cli/setup-prompt.js +3 -2
  21. package/dist/cli/setup.js +27 -107
  22. package/dist/cli/web-command.js +98 -0
  23. package/dist/config/budget.js +5 -5
  24. package/dist/config/config.js +103 -96
  25. package/dist/config/defaults.js +38 -8
  26. package/dist/config/domains.js +24 -27
  27. package/dist/config/env.js +46 -0
  28. package/dist/config/experts.js +2 -0
  29. package/dist/config/index.js +1 -0
  30. package/dist/config/paths.js +56 -0
  31. package/dist/config/redact.js +44 -0
  32. package/dist/config/registry-extra.js +334 -0
  33. package/dist/config/registry.js +584 -0
  34. package/dist/config/security.js +35 -36
  35. package/dist/config/timeouts.js +74 -0
  36. package/dist/config/utils.js +16 -15
  37. package/dist/core/agent/artifact-progress.js +59 -0
  38. package/dist/core/agent/artifact-resolve.js +79 -0
  39. package/dist/core/agent/audit-gate.js +7 -3
  40. package/dist/core/agent/compaction.js +2 -2
  41. package/dist/core/agent/constants.js +33 -4
  42. package/dist/core/agent/context-log.js +454 -0
  43. package/dist/core/agent/context-renderer.js +5 -0
  44. package/dist/core/agent/critic-gate.js +132 -0
  45. package/dist/core/agent/hallucination-gate.js +148 -38
  46. package/dist/core/agent/loop-state.js +15 -0
  47. package/dist/core/agent/prefix-monitor.js +1 -1
  48. package/dist/core/agent/reasoning-resolver.js +10 -1
  49. package/dist/core/agent/token-tracker.js +7 -4
  50. package/dist/core/agent/tool-batch.js +93 -18
  51. package/dist/core/agent/tool-images.js +18 -0
  52. package/dist/core/agent/tool-output.js +65 -11
  53. package/dist/core/agent/tool-pairing.js +60 -0
  54. package/dist/core/agent-moe.js +25 -24
  55. package/dist/core/agent.js +429 -108
  56. package/dist/core/bootstrap/agent-phase.js +66 -0
  57. package/dist/core/bootstrap/config-phase.js +86 -0
  58. package/dist/core/bootstrap/modules-phase.js +269 -0
  59. package/dist/core/bootstrap/provider-phase.js +183 -0
  60. package/dist/core/bootstrap/session-phase.js +204 -0
  61. package/dist/core/bootstrap/state.js +94 -0
  62. package/dist/core/bootstrap/workspace-phase.js +115 -0
  63. package/dist/core/bootstrap.js +92 -660
  64. package/dist/core/crash-handler.js +17 -7
  65. package/dist/core/environment.js +37 -12
  66. package/dist/core/host-plan.js +43 -0
  67. package/dist/core/host.js +23 -0
  68. package/dist/core/index.js +1 -0
  69. package/dist/core/prompt-builder.js +17 -1
  70. package/dist/core/prompt-overflow.js +42 -43
  71. package/dist/core/session-logger.js +122 -30
  72. package/dist/core/system-prompt.js +117 -0
  73. package/dist/core/version.js +6 -4
  74. package/dist/core/web-host.js +34 -0
  75. package/dist/core/workspace.js +19 -15
  76. package/dist/i18n/index.js +37 -3
  77. package/dist/index.js +7 -3
  78. package/dist/llm/image-utils.js +55 -64
  79. package/dist/llm/llm-errors.js +9 -0
  80. package/dist/llm/model-loader.js +10 -32
  81. package/dist/llm/models-dev.js +121 -0
  82. package/dist/llm/openai-compat.js +146 -73
  83. package/dist/llm/orchestrator.js +33 -11
  84. package/dist/llm/provider-budget.js +2 -2
  85. package/dist/llm/provider-context.js +233 -0
  86. package/dist/llm/provider.js +1 -1
  87. package/dist/llm/response.js +5 -0
  88. package/dist/llm/token-counter.js +27 -11
  89. package/dist/logger/app-logger.js +48 -22
  90. package/dist/logger/file-log.js +28 -19
  91. package/dist/logger/index.js +1 -0
  92. package/dist/main.js +273 -47
  93. package/dist/migration/backup.js +14 -3
  94. package/dist/migration/detect.js +12 -3
  95. package/dist/migration/index.js +1 -0
  96. package/dist/modules/artifacts/store.js +9 -8
  97. package/dist/modules/browser/actions.js +6 -0
  98. package/dist/modules/browser/bridge-client.js +14 -12
  99. package/dist/modules/browser/bridge-path.js +4 -4
  100. package/dist/modules/browser/console-buffer.js +37 -0
  101. package/dist/modules/browser/cookie-store.js +5 -0
  102. package/dist/modules/browser/driver-types.js +1 -0
  103. package/dist/modules/browser/driver.js +19 -11
  104. package/dist/modules/browser/index.js +3 -1
  105. package/dist/modules/browser/module.js +2 -1
  106. package/dist/modules/browser/session.js +10 -30
  107. package/dist/modules/browser/snapshot.js +19 -13
  108. package/dist/modules/browser/types.js +1 -0
  109. package/dist/modules/certification/cli.js +28 -20
  110. package/dist/modules/certification/fact-checker.js +9 -1
  111. package/dist/modules/certification/loader.js +9 -2
  112. package/dist/modules/certification/manifest.js +83 -21
  113. package/dist/modules/certification/runner.js +23 -13
  114. package/dist/modules/certification/scenarios.js +20 -20
  115. package/dist/modules/certification/syntax-scenarios.js +1 -2
  116. package/dist/modules/context/chunk-query.js +11 -9
  117. package/dist/modules/context/fact-extractor.js +37 -39
  118. package/dist/modules/context/index.js +1 -0
  119. package/dist/modules/context/manager.js +106 -80
  120. package/dist/modules/critic/client.js +176 -0
  121. package/dist/modules/critic/evidence.js +81 -0
  122. package/dist/modules/critic/provider.js +45 -0
  123. package/dist/modules/critic/tools.js +78 -0
  124. package/dist/modules/critic/types.js +1 -0
  125. package/dist/modules/execution/audit-runners.js +28 -34
  126. package/dist/modules/execution/auditor.js +53 -49
  127. package/dist/modules/execution/execution-plugin.js +142 -102
  128. package/dist/modules/execution/index.js +1 -1
  129. package/dist/modules/execution/input-from.js +37 -13
  130. package/dist/modules/execution/module.js +368 -191
  131. package/dist/modules/execution/moe-executor.js +53 -26
  132. package/dist/modules/execution/plan-actions.js +320 -202
  133. package/dist/modules/execution/plan-checks.js +35 -0
  134. package/dist/modules/execution/plan-coverage.js +36 -33
  135. package/dist/modules/execution/plan-steps.js +11 -3
  136. package/dist/modules/execution/plan-store.js +83 -17
  137. package/dist/modules/execution/plan-tool.js +89 -35
  138. package/dist/modules/execution/plan-validator.js +30 -10
  139. package/dist/modules/execution/planner.js +22 -13
  140. package/dist/modules/execution/stuck-detector.js +196 -93
  141. package/dist/modules/execution/tracker.js +22 -8
  142. package/dist/modules/execution/verifier.js +69 -48
  143. package/dist/modules/execution/windows-commands.js +12 -12
  144. package/dist/modules/hallucination/artifact-claim.js +93 -0
  145. package/dist/modules/hallucination/confidence.js +13 -11
  146. package/dist/modules/hallucination/consistency.js +10 -2
  147. package/dist/modules/hallucination/detector.js +10 -5
  148. package/dist/modules/hallucination/factual.js +43 -26
  149. package/dist/modules/hallucination/index.js +1 -0
  150. package/dist/modules/hallucination/js-identifiers.js +44 -45
  151. package/dist/modules/hallucination/llm-judge.js +17 -12
  152. package/dist/modules/hallucination/success-claim.js +59 -0
  153. package/dist/modules/indexer/cache.js +52 -4
  154. package/dist/modules/indexer/index.js +1 -0
  155. package/dist/modules/indexer/map-command.js +2 -4
  156. package/dist/modules/indexer/map-select.js +16 -16
  157. package/dist/modules/indexer/module.js +47 -12
  158. package/dist/modules/indexer/project-profile.js +8 -7
  159. package/dist/modules/indexer/symbols.js +15 -15
  160. package/dist/modules/indexer/walker.js +159 -29
  161. package/dist/modules/lsp/check-tool.js +7 -5
  162. package/dist/modules/lsp/client.js +39 -39
  163. package/dist/modules/lsp/command.js +17 -24
  164. package/dist/modules/lsp/config.js +12 -11
  165. package/dist/modules/lsp/index.js +1 -0
  166. package/dist/modules/lsp/module.js +48 -46
  167. package/dist/modules/lsp/probe.js +13 -14
  168. package/dist/modules/lsp/project-root.js +14 -17
  169. package/dist/modules/lsp/startup-check.js +18 -16
  170. package/dist/modules/mcp/client.js +59 -62
  171. package/dist/modules/mcp/index.js +1 -0
  172. package/dist/modules/mcp/module.js +66 -37
  173. package/dist/modules/mcp/registry.js +5 -0
  174. package/dist/modules/memory/index.js +1 -0
  175. package/dist/modules/memory/module.js +12 -8
  176. package/dist/modules/memory/search.js +4 -1
  177. package/dist/modules/memory/store.js +40 -2
  178. package/dist/modules/onboarding/init.js +47 -0
  179. package/dist/modules/onboarding/meet.js +30 -0
  180. package/dist/modules/onboarding/mode.js +30 -0
  181. package/dist/modules/onboarding/prompts.js +43 -0
  182. package/dist/modules/pipelines/engine.js +10 -0
  183. package/dist/modules/pipelines/index.js +1 -0
  184. package/dist/modules/pipelines/parser.js +4 -1
  185. package/dist/modules/pipelines/template.js +4 -0
  186. package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
  187. package/dist/modules/plugins/builtin/notify.js +2 -0
  188. package/dist/modules/plugins/index.js +1 -0
  189. package/dist/modules/plugins/loader.js +18 -3
  190. package/dist/modules/plugins/manager.js +37 -17
  191. package/dist/modules/pricing/index.js +10 -11
  192. package/dist/modules/pricing/prices.js +12 -12
  193. package/dist/modules/processes/index.js +1 -0
  194. package/dist/modules/processes/kill-tree.js +27 -31
  195. package/dist/modules/processes/registry.js +44 -25
  196. package/dist/modules/processes/runner.js +4 -4
  197. package/dist/modules/providers/capabilities.js +28 -0
  198. package/dist/modules/providers/create.js +3 -4
  199. package/dist/modules/providers/factory.js +7 -7
  200. package/dist/modules/providers/fallback.js +12 -12
  201. package/dist/modules/providers/health.js +4 -4
  202. package/dist/modules/providers/manager.js +50 -19
  203. package/dist/modules/providers/naming.js +15 -0
  204. package/dist/modules/providers/preflight.js +71 -0
  205. package/dist/modules/providers/presets.js +39 -8
  206. package/dist/modules/providers/registry.js +7 -2
  207. package/dist/modules/reasoning/policy.js +8 -6
  208. package/dist/modules/reasoning/probe.js +30 -25
  209. package/dist/modules/security/audit-log.js +19 -36
  210. package/dist/modules/security/audit-notifier.js +35 -75
  211. package/dist/modules/security/command-validator.js +99 -51
  212. package/dist/modules/security/content-scanner.js +8 -8
  213. package/dist/modules/security/data-sanitizer.js +9 -16
  214. package/dist/modules/security/encryption.js +32 -52
  215. package/dist/modules/security/index.js +2 -1
  216. package/dist/modules/security/network-validator.js +120 -43
  217. package/dist/modules/security/path-validator.js +61 -46
  218. package/dist/modules/security/protected-paths.js +73 -0
  219. package/dist/modules/security/rate-limiter.js +17 -49
  220. package/dist/modules/security/security-policies.js +25 -57
  221. package/dist/modules/security/session-encryption.js +32 -71
  222. package/dist/modules/security/session-isolation.js +15 -28
  223. package/dist/modules/session/auto-name.js +63 -0
  224. package/dist/modules/session/export.js +98 -0
  225. package/dist/modules/session/index.js +1 -0
  226. package/dist/modules/session/manager.js +79 -28
  227. package/dist/modules/session/module.js +4 -3
  228. package/dist/modules/session/store.js +57 -20
  229. package/dist/modules/setup/answers.js +35 -0
  230. package/dist/modules/setup/apply.js +79 -0
  231. package/dist/modules/setup/discovery.js +72 -0
  232. package/dist/modules/skills/index.js +1 -0
  233. package/dist/modules/skills/loader.js +9 -1
  234. package/dist/modules/skills/module.js +11 -0
  235. package/dist/modules/updater/changelog-reader.js +15 -16
  236. package/dist/modules/updater/checker.js +9 -4
  237. package/dist/modules/updater/dev-detect.js +7 -6
  238. package/dist/modules/updater/index.js +1 -0
  239. package/dist/modules/updater/module.js +17 -15
  240. package/dist/modules/user-profile/compressor.js +2 -0
  241. package/dist/modules/user-profile/index.js +1 -0
  242. package/dist/modules/user-profile/profile.js +10 -1
  243. package/dist/modules/webui/answer-api.js +29 -0
  244. package/dist/modules/webui/assets.js +50 -0
  245. package/dist/modules/webui/body.js +21 -0
  246. package/dist/modules/webui/commands-settings.js +209 -0
  247. package/dist/modules/webui/commands-shared.js +60 -0
  248. package/dist/modules/webui/commands.js +174 -0
  249. package/dist/modules/webui/config-api.js +134 -0
  250. package/dist/modules/webui/file-api.js +202 -0
  251. package/dist/modules/webui/forward.js +159 -0
  252. package/dist/modules/webui/history-api.js +112 -0
  253. package/dist/modules/webui/host-state.js +60 -0
  254. package/dist/modules/webui/i18n-api.js +9 -0
  255. package/dist/modules/webui/limits.js +6 -0
  256. package/dist/modules/webui/log-forward.js +20 -0
  257. package/dist/modules/webui/memory-commands.js +64 -0
  258. package/dist/modules/webui/model-cache.js +18 -0
  259. package/dist/modules/webui/module.js +148 -0
  260. package/dist/modules/webui/persist.js +24 -0
  261. package/dist/modules/webui/prompt-io.js +97 -0
  262. package/dist/modules/webui/providers-api.js +337 -0
  263. package/dist/modules/webui/providers-view.js +56 -0
  264. package/dist/modules/webui/reasoning-view.js +15 -0
  265. package/dist/modules/webui/run-agent.js +81 -0
  266. package/dist/modules/webui/server.js +284 -0
  267. package/dist/modules/webui/sessions-api.js +178 -0
  268. package/dist/modules/webui/settings-api.js +561 -0
  269. package/dist/modules/webui/setup-api.js +201 -0
  270. package/dist/modules/webui/state.js +74 -0
  271. package/dist/modules/webui/util.js +261 -0
  272. package/dist/output/bus.js +7 -2
  273. package/dist/output/channel.js +80 -28
  274. package/dist/output/format.js +2 -1
  275. package/dist/output/index.js +1 -0
  276. package/dist/output/json-sink.js +5 -3
  277. package/dist/output/machine.js +4 -3
  278. package/dist/output/session-sink.js +4 -4
  279. package/dist/tools/approve.js +4 -0
  280. package/dist/tools/attach-image.js +5 -4
  281. package/dist/tools/auto-fixer.js +26 -44
  282. package/dist/tools/auto-tags.js +83 -0
  283. package/dist/tools/bash.js +193 -116
  284. package/dist/tools/browser.js +76 -31
  285. package/dist/tools/chunk-query.js +14 -6
  286. package/dist/tools/create-dir.js +9 -5
  287. package/dist/tools/delete-file.js +9 -5
  288. package/dist/tools/download-file.js +32 -7
  289. package/dist/tools/edit-file.js +52 -14
  290. package/dist/tools/enable-tools.js +6 -6
  291. package/dist/tools/executor.js +132 -27
  292. package/dist/tools/file-info.js +3 -1
  293. package/dist/tools/filter-tools.js +3 -3
  294. package/dist/tools/glob-tool.js +4 -1
  295. package/dist/tools/grep-tool.js +13 -10
  296. package/dist/tools/hidden-tools-block.js +6 -6
  297. package/dist/tools/index.js +16 -8
  298. package/dist/tools/list-dir.js +4 -2
  299. package/dist/tools/load-skill.js +7 -0
  300. package/dist/tools/mcp-call.js +15 -4
  301. package/dist/tools/meet-done.js +14 -0
  302. package/dist/tools/move-file.js +14 -7
  303. package/dist/tools/path-utils.js +29 -29
  304. package/dist/tools/pipeline-run.js +29 -7
  305. package/dist/tools/preview.js +1 -1
  306. package/dist/tools/process-ids.js +31 -0
  307. package/dist/tools/process-kill.js +9 -16
  308. package/dist/tools/process-list.js +1 -0
  309. package/dist/tools/process-log.js +11 -16
  310. package/dist/tools/process-wait.js +63 -0
  311. package/dist/tools/prompt-io.js +4 -3
  312. package/dist/tools/question.js +172 -25
  313. package/dist/tools/read-file.js +55 -34
  314. package/dist/tools/recall.js +14 -8
  315. package/dist/tools/registry.js +6 -5
  316. package/dist/tools/remember.js +8 -4
  317. package/dist/tools/scope-request.js +11 -6
  318. package/dist/tools/search-history.js +5 -2
  319. package/dist/tools/session-info.js +2 -0
  320. package/dist/tools/set-thinking.js +8 -4
  321. package/dist/tools/subagent.js +59 -39
  322. package/dist/tools/syntax-validator.js +292 -90
  323. package/dist/tools/user-input.js +22 -18
  324. package/dist/tools/web-browse.js +9 -3
  325. package/dist/tools/web-fetch.js +12 -23
  326. package/dist/tools/web-search.js +7 -5
  327. package/dist/tools/write-file.js +45 -15
  328. package/dist/ui/action-log.js +13 -0
  329. package/dist/ui/box.js +4 -3
  330. package/dist/ui/colors.js +2 -0
  331. package/dist/ui/diff.js +6 -5
  332. package/dist/ui/index.js +1 -0
  333. package/dist/ui/line-editor.js +230 -122
  334. package/dist/ui/line-math.js +10 -2
  335. package/dist/ui/md-formatter.js +57 -37
  336. package/dist/ui/output.js +8 -3
  337. package/dist/ui/plan-view.js +21 -10
  338. package/dist/ui/renderer.js +393 -67
  339. package/dist/ui/spinner.js +16 -5
  340. package/dist/ui/table.js +15 -17
  341. package/dist/ui/theme.js +143 -0
  342. package/dist/utils/child-run.js +20 -0
  343. package/dist/utils/error.js +1 -1
  344. package/dist/utils/index.js +2 -0
  345. package/dist/utils/retry.js +2 -2
  346. package/dist/utils/truncate.js +1 -1
  347. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ /** Картинки: MIME, буфер обмена, загрузка файла/URL в data URL. */
1
2
  import { readFileSync } from "fs";
2
3
  import { extname } from "path";
3
4
  const MIME_MAP = {
@@ -9,31 +10,54 @@ const MIME_MAP = {
9
10
  ".bmp": "image/bmp",
10
11
  ".svg": "image/svg+xml",
11
12
  };
12
- /** Detect MIME type from file extension. */
13
+ /**
14
+ * Определяет MIME-тип по расширению файла.
15
+ * @param filePath путь к файлу; @returns MIME-тип (дефолт image/png).
16
+ */
13
17
  export function detectMime(filePath) {
14
18
  const ext = extname(filePath).toLowerCase();
15
19
  return MIME_MAP[ext] ?? "image/png";
16
20
  }
21
+ /** Конструктор Bun.Image под Bun, иначе undefined. */
22
+ function getBunImage() {
23
+ return globalThis.Bun?.Image;
24
+ }
25
+ /** Ужимает буфер в JPEG data URL через Bun.Image; null, если недоступно/упало. */
26
+ async function resizeToJpegDataUrl(buf) {
27
+ const Image = getBunImage();
28
+ if (!Image)
29
+ return null;
30
+ try {
31
+ const img = new Image(buf);
32
+ const { width } = await img.metadata();
33
+ const targetWidth = Math.min(800, width || 800);
34
+ return await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
17
40
  /**
18
- * Read image from system clipboard using Bun.Image (macOS/Windows).
19
- * Falls back to platform-specific commands on Linux.
20
- * Returns null if clipboard has no image.
41
+ * Читает картинку из буфера обмена через Bun.Image (macOS/Windows).
42
+ * На Linux — платформенные команды. Null, если картинки нет.
43
+ * @returns буфер PNG/JPEG либо null.
21
44
  */
22
45
  export async function readClipboardImage() {
23
- // Bun.Image.fromClipboard() works on macOS and Windows
24
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
46
+ // Bun.Image.fromClipboard() работает на macOS и Windows
47
+ const Image = getBunImage();
48
+ if (Image) {
25
49
  try {
26
- const img = Bun.Image.fromClipboard();
50
+ const img = Image.fromClipboard();
27
51
  if (img) {
28
52
  const buf = await img.resize(800, 800, { fit: "inside" }).jpeg({ quality: 60 }).buffer();
29
53
  return Buffer.from(buf);
30
54
  }
31
55
  }
32
56
  catch {
33
- // Bun.Image.fromClipboard() failed — fall through to platform fallback
57
+ // Bun.Image.fromClipboard() упал — идём в платформенный фолбэк
34
58
  }
35
59
  }
36
- // Linux fallback: xclip/wl-paste → temp file → read
60
+ // Linux-фолбэк: xclip/wl-paste → временный файл → чтение
37
61
  return readClipboardFallback();
38
62
  }
39
63
  async function readClipboardFallback() {
@@ -44,7 +68,7 @@ async function readClipboardFallback() {
44
68
  if (platform() !== "linux")
45
69
  return null; // macOS/Windows use Bun.Image
46
70
  const tmpPath = join(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
47
- // Try Wayland (wl-paste) first, then X11 (xclip)
71
+ // Сначала Wayland (wl-paste), затем X11 (xclip)
48
72
  const commands = [
49
73
  `wl-paste --type image/png > "${tmpPath}" 2>/dev/null`,
50
74
  `xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`,
@@ -58,45 +82,36 @@ async function readClipboardFallback() {
58
82
  return buf;
59
83
  }
60
84
  catch {
61
- // try next command
85
+ // пробуем следующую команду
62
86
  }
63
87
  }
64
88
  try {
65
89
  unlinkSync(tmpPath);
66
90
  }
67
- catch { }
91
+ catch {
92
+ // Best-effort чистка времянки — зависший файл не должен маскировать null.
93
+ }
68
94
  return null;
69
95
  }
70
96
  /**
71
- * Load an image from a file path, resize via Bun.Image, return as JPEG data URL.
72
- * Target: ~800px wide, JPEG quality 60 — typically 5-15KB (~2-4K tokens).
97
+ * Грузит картинку из файла, ужимает через Bun.Image, отдаёт JPEG data URL.
98
+ * Цель: ~800px по ширине, JPEG 60 — обычно 5–15KB (~2–4K токенов).
73
99
  */
74
100
  export async function loadFileAsDataUrl(filePath) {
75
101
  const buf = readFileSync(filePath);
76
- // Use Bun.Image if available — resize + convert to JPEG
77
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
78
- try {
79
- const img = new Bun.Image(buf);
80
- const { width } = await img.metadata();
81
- const targetWidth = Math.min(800, width || 800);
82
- const dataUrl = await img
83
- .resize(targetWidth, 800, { fit: "inside" })
84
- .jpeg({ quality: 60 })
85
- .dataurl();
86
- return { dataUrl };
87
- }
88
- catch {
89
- // Bun.Image failed — fall through to raw base64
90
- }
91
- }
92
- // Fallback: raw base64 (no resize)
102
+ // Bun.Image, если есть — ресайз + конвертация в JPEG
103
+ const resized = await resizeToJpegDataUrl(buf);
104
+ if (resized)
105
+ return { dataUrl: resized };
106
+ // Фолбэк: сырой base64 (без ресайза)
93
107
  const b64 = buf.toString("base64");
94
108
  const ext = filePath.split(".").pop()?.toLowerCase();
95
109
  const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
96
110
  return { dataUrl: `data:${mime};base64,${b64}` };
97
111
  }
98
112
  /**
99
- * Load an image from a URL, resize, return as JPEG data URL.
113
+ * Грузит картинку по URL, ужимает, отдаёт JPEG data URL.
114
+ * @param url URL картинки; @returns data URL.
100
115
  */
101
116
  export async function loadUrlAsDataUrl(url) {
102
117
  const resp = await fetch(url);
@@ -104,44 +119,20 @@ export async function loadUrlAsDataUrl(url) {
104
119
  throw new Error(`Failed to fetch image: ${resp.status} ${resp.statusText}`);
105
120
  }
106
121
  const buf = Buffer.from(await resp.arrayBuffer());
107
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
108
- try {
109
- const img = new Bun.Image(buf);
110
- const { width } = await img.metadata();
111
- const targetWidth = Math.min(800, width || 800);
112
- const dataUrl = await img
113
- .resize(targetWidth, 800, { fit: "inside" })
114
- .jpeg({ quality: 60 })
115
- .dataurl();
116
- return { dataUrl };
117
- }
118
- catch {
119
- // fall through
120
- }
121
- }
122
+ const resized = await resizeToJpegDataUrl(buf);
123
+ if (resized)
124
+ return { dataUrl: resized };
122
125
  const b64 = buf.toString("base64");
123
126
  return { dataUrl: `data:image/png;base64,${b64}` };
124
127
  }
125
128
  /**
126
- * Process a clipboard buffer (already resized by readClipboardImage)
127
- * into a data URL. Used by Ctrl+V handler.
129
+ * Превращает буфер обмена (уже ужатый readClipboardImage) в data URL.
130
+ * Нужен обработчику Ctrl+V.
128
131
  */
129
132
  export async function bufferToDataUrl(buf) {
130
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
131
- try {
132
- const img = new Bun.Image(buf);
133
- const { width } = await img.metadata();
134
- const targetWidth = Math.min(800, width || 800);
135
- const dataUrl = await img
136
- .resize(targetWidth, 800, { fit: "inside" })
137
- .jpeg({ quality: 60 })
138
- .dataurl();
139
- return { dataUrl };
140
- }
141
- catch {
142
- // fall through
143
- }
144
- }
133
+ const resized = await resizeToJpegDataUrl(buf);
134
+ if (resized)
135
+ return { dataUrl: resized };
145
136
  const b64 = buf.toString("base64");
146
137
  return { dataUrl: `data:image/png;base64,${b64}` };
147
138
  }
@@ -1,3 +1,4 @@
1
+ /** Типизированные ошибки LLM-транспорта (terminal/recoverable/status). */
1
2
  /**
2
3
  * Типизированные ошибки LLM-транспорта.
3
4
  *
@@ -30,10 +31,18 @@ export class LlmError extends Error {
30
31
  this.recoverable = options.recoverable ?? false;
31
32
  this.llmStatus = options.llmStatus;
32
33
  }
34
+ /**
35
+ * Помечает ошибку терминальной (стрим не ретраится).
36
+ * @returns this для чейнинга.
37
+ */
33
38
  markTerminal() {
34
39
  this.terminal = true;
35
40
  return this;
36
41
  }
42
+ /**
43
+ * Помечает ошибку восстановимой (причину вернуть модели через цикл).
44
+ * @returns this для чейнинга.
45
+ */
37
46
  markRecoverable() {
38
47
  this.recoverable = true;
39
48
  return this;
@@ -1,33 +1,7 @@
1
- /**
2
- * Ask LM Studio's native API which context length the model is currently
3
- * loaded with. Prefers the running instance (`loaded_instances[].config.
4
- * context_length`), falls back to the model's `max_context_length`. Returns
5
- * null when the endpoint is unavailable (not LM Studio), the model is not
6
- * found/loaded, or the request fails — the caller must degrade silently.
7
- */
8
- export async function getLoadedContextLength(baseUrl, model, timeoutMs = 3000) {
9
- const base = baseUrl.replace(/\/$/, "").replace(/\/v1\/?$/, "");
10
- try {
11
- const response = await fetch(`${base}/api/v1/models`, {
12
- signal: AbortSignal.timeout(timeoutMs),
13
- });
14
- if (!response.ok)
15
- return null;
16
- const data = (await response.json());
17
- const models = data.models || [];
18
- const found = models.find((m) => m.key === model || m.name === model || model.includes(m.key));
19
- if (!found)
20
- return null;
21
- const loadedInstance = (found.loaded_instances ?? [])[0];
22
- const actual = loadedInstance?.config?.context_length ?? found.max_context_length;
23
- if (typeof actual !== "number" || actual <= 0)
24
- return null;
25
- return { model: found.key ?? found.name ?? model, actual };
26
- }
27
- catch {
28
- return null;
29
- }
30
- }
1
+ // Хелперы живой context-probe — в ./provider-context.ts и ./models-dev.ts;
2
+ // реэкспорт отсюда, чтобы существующие импорты не ломать.
3
+ export { probeModel, getLoadedContextLength, probeProviderContext, } from "./provider-context";
4
+ /** Загрузчик модели LM Studio: ensure + load через native API. */
31
5
  export class ModelLoader {
32
6
  baseUrl;
33
7
  logger;
@@ -36,6 +10,10 @@ export class ModelLoader {
36
10
  this.baseUrl = url.replace(/\/v1\/?$/, "");
37
11
  this.logger = logger;
38
12
  }
13
+ /**
14
+ * Гарантирует загруженную модель с нужным контекстом (иначе грузит).
15
+ * @param config конфиг загрузки; @returns итог (уже была/загружена/ошибка).
16
+ */
39
17
  async ensureModelLoaded(config) {
40
18
  if (config.autoLoad === false) {
41
19
  return { success: true, alreadyLoaded: true };
@@ -61,11 +39,11 @@ export class ModelLoader {
61
39
  if (!response.ok)
62
40
  return false;
63
41
  const data = (await response.json());
64
- const models = data.models || [];
42
+ const models = data.models ?? [];
65
43
  const found = models.find((m) => m.key === model || m.name === model);
66
44
  if (!found)
67
45
  return false;
68
- const loadedInstances = found.loaded_instances || [];
46
+ const loadedInstances = found.loaded_instances ?? [];
69
47
  return loadedInstances.some((inst) => inst.config?.context_length === contextLength);
70
48
  }
71
49
  catch {
@@ -0,0 +1,121 @@
1
+ /**
2
+ * models.dev-фолбэк для хостед-шлюзов (OpenCode Zen/Go), чей собственный
3
+ * `/models` отдаёт голые id без метаданных контекста (проверено вживую:
4
+ * только `{id, object, created, owned_by}`).
5
+ *
6
+ * models.dev — открытая база моделей (MIT, sst/models.dev), её же использует
7
+ * сам opencode. Каталог `api.json`: `Record<providerId, { models:
8
+ * Record<modelId, { limit: { context } }> }>`, провайдер `opencode` держит
9
+ * модели под теми же bare id, что отдаёт Zen (`deepseek-v4.1-flash` →
10
+ * `limit.context`).
11
+ *
12
+ * На диске только slim-проекция (`opencode` → id → контекст):
13
+ * `~/.mma/models-dev-cache.json` (override `MMA_MODELS_DEV_CACHE`), TTL 24h
14
+ * как у reasoning-пробника. Любой сбой — null, вызывающий хранит настроенный
15
+ * `contextWindow` (fail-open).
16
+ */
17
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
18
+ import { join, dirname } from "path";
19
+ import { globalMmaDir } from "../config/paths";
20
+ import { readEnv } from "../config/env";
21
+ import { MODELS_DEV_CATALOG_TTL_MS, MODELS_DEV_FETCH_TIMEOUT_MS, } from "../config/timeouts";
22
+ const MODELS_DEV_URL = "https://models.dev/api.json";
23
+ function cachePath() {
24
+ return readEnv("MODELS_DEV_CACHE") ?? join(globalMmaDir(), "models-dev-cache.json");
25
+ }
26
+ /** In-memory L1: свежая проекция на время жизни процесса. */
27
+ let memCache = null;
28
+ function readDiskCache() {
29
+ try {
30
+ const path = cachePath();
31
+ if (!existsSync(path))
32
+ return null;
33
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
34
+ if (typeof parsed.ts !== "string" || !parsed.models || typeof parsed.models !== "object") {
35
+ return null;
36
+ }
37
+ return { ts: parsed.ts, models: parsed.models };
38
+ }
39
+ catch {
40
+ // Битый кэш — перечитываем ниже.
41
+ return null;
42
+ }
43
+ }
44
+ function writeDiskCache(models) {
45
+ try {
46
+ const path = cachePath();
47
+ mkdirSync(dirname(path), { recursive: true });
48
+ const entry = { ts: new Date().toISOString(), models };
49
+ writeFileSync(path, JSON.stringify(entry), "utf-8");
50
+ }
51
+ catch {
52
+ // Best-effort — падение записи не должно ронять агента.
53
+ }
54
+ }
55
+ /** `limit.context` одной записи модели, если это годное число. */
56
+ function modelContextOf(meta) {
57
+ const context = meta?.limit?.context;
58
+ if (typeof context !== "number" || !Number.isFinite(context) || context <= 0)
59
+ return null;
60
+ return Math.floor(context);
61
+ }
62
+ function extractOpencodeModels(catalog) {
63
+ if (!catalog || typeof catalog !== "object")
64
+ return null;
65
+ const provider = catalog.opencode;
66
+ if (!provider || typeof provider !== "object")
67
+ return null;
68
+ const models = provider.models;
69
+ if (!models || typeof models !== "object")
70
+ return null;
71
+ const out = {};
72
+ for (const [id, meta] of Object.entries(models)) {
73
+ const context = modelContextOf(meta);
74
+ if (context !== null)
75
+ out[id] = context;
76
+ }
77
+ return out;
78
+ }
79
+ async function fetchCatalog(timeoutMs) {
80
+ try {
81
+ const response = await fetch(MODELS_DEV_URL, { signal: AbortSignal.timeout(timeoutMs) });
82
+ if (!response.ok)
83
+ return null;
84
+ return extractOpencodeModels((await response.json()));
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ /**
91
+ * Ищет `model` в секции провайдера `opencode` каталога models.dev.
92
+ * Только точное совпадение id (Zen отдаёт bare id, каталог ключует так же).
93
+ * Возвращает каталожный `limit.context`, на любом промахе — null.
94
+ */
95
+ export async function lookupModelsDevContext(model, opts = {}) {
96
+ const timeoutMs = opts.timeoutMs ?? MODELS_DEV_FETCH_TIMEOUT_MS;
97
+ if (memCache && Date.now() - memCache.ts < MODELS_DEV_CATALOG_TTL_MS) {
98
+ return memCache.models[model] ?? null;
99
+ }
100
+ const disk = readDiskCache();
101
+ if (disk && Date.now() - new Date(disk.ts).getTime() < MODELS_DEV_CATALOG_TTL_MS) {
102
+ memCache = { models: disk.models, ts: Date.now() };
103
+ return disk.models[model] ?? null;
104
+ }
105
+ const fresh = await fetchCatalog(timeoutMs);
106
+ if (!fresh) {
107
+ // Офлайн с протухшим кэшем: протухшее окно лучше никакого.
108
+ if (disk) {
109
+ memCache = { models: disk.models, ts: Date.now() };
110
+ return disk.models[model] ?? null;
111
+ }
112
+ return null;
113
+ }
114
+ memCache = { models: fresh, ts: Date.now() };
115
+ writeDiskCache(fresh);
116
+ return fresh[model] ?? null;
117
+ }
118
+ /** Тестовый шов: сбросить in-memory проекцию (диск изолирован через env). */
119
+ export function resetModelsDevCache() {
120
+ memCache = null;
121
+ }