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
@@ -103,6 +103,7 @@
103
103
  securityBashBlock: false,
104
104
  securityFlagsBlock: false,
105
105
  securityPathsRestrict: true,
106
+ securityDetail: { bash: {}, paths: {}, flags: {}, network: {}, advanced: {} },
106
107
  },
107
108
  };
108
109
 
@@ -118,6 +119,12 @@
118
119
  state.answers.contextWindow = r.data.defaults.contextWindow || state.answers.contextWindow;
119
120
  state.answers.maxToolIterations =
120
121
  r.data.defaults.maxToolIterations || state.answers.maxToolIterations;
122
+ if (r.data.defaults.security) {
123
+ state.answers.securityDetail = Object.assign(
124
+ state.answers.securityDetail,
125
+ r.data.defaults.security,
126
+ );
127
+ }
121
128
  }
122
129
  const first = (r.data.providers || [])[0];
123
130
  if (first) {
@@ -137,6 +144,7 @@
137
144
  // --- rendering -------------------------------------------------------------
138
145
 
139
146
  function render() {
147
+ closePopup();
140
148
  const wrap = h("div", "setup-wrap");
141
149
  const title = h("h1", "setup-h");
142
150
  title.appendChild(h("span", "glyph", "◇"));
@@ -258,6 +266,27 @@
258
266
  });
259
267
  box.appendChild(field(tr("web.setup.provider.base_url"), input));
260
268
 
269
+ const actions = h("div", "setup-row-inline");
270
+ const test = h("button", "setup-btn", tr("web.setup.provider.test"));
271
+ test.type = "button";
272
+ test.addEventListener("click", async () => {
273
+ if (!state.answers.apiBase) {
274
+ setMsg(box, "err", tr("web.setup.provider.required"));
275
+ return;
276
+ }
277
+ test.disabled = true;
278
+ setMsg(box, "", tr("web.setup.provider.testing"));
279
+ const r = await api("/api/setup/models", {
280
+ method: "POST",
281
+ body: { baseUrl: state.answers.apiBase, apiKey: state.answers.apiKey },
282
+ });
283
+ test.disabled = false;
284
+ const models = (r.ok && r.data && r.data.models) || [];
285
+ if (r.ok && models.length) setMsg(box, "ok", tr("web.setup.provider.test_ok"));
286
+ else setMsg(box, "warn", tr("web.setup.provider.test_fail"));
287
+ });
288
+ actions.appendChild(test);
289
+
261
290
  if (state.answers.provider === "openai-compat" || state.answers.provider === "lmstudio") {
262
291
  const scan = h("button", "setup-btn", tr("web.setup.provider.scan"));
263
292
  scan.type = "button";
@@ -291,8 +320,11 @@
291
320
  results.appendChild(b);
292
321
  });
293
322
  });
294
- box.appendChild(scan);
323
+ actions.appendChild(scan);
324
+ box.appendChild(actions);
295
325
  box.appendChild(results);
326
+ } else {
327
+ box.appendChild(actions);
296
328
  }
297
329
  box.appendChild(messageEl());
298
330
  return box;
@@ -393,6 +425,38 @@
393
425
  });
394
426
  box.appendChild(field(tr("web.setup.context.contextWindow"), cw));
395
427
 
428
+ const detect = h("button", "setup-btn", tr("web.setup.context.detect"));
429
+ detect.type = "button";
430
+ detect.addEventListener("click", async () => {
431
+ if (!state.answers.apiBase || !state.answers.model) {
432
+ setMsg(box, "err", tr("web.setup.context.detect_fail"));
433
+ return;
434
+ }
435
+ detect.disabled = true;
436
+ setMsg(box, "", tr("web.setup.context.detecting"));
437
+ const r = await api("/api/setup/context", {
438
+ method: "POST",
439
+ body: {
440
+ baseUrl: state.answers.apiBase,
441
+ apiKey: state.answers.apiKey,
442
+ model: state.answers.model,
443
+ providerType: state.answers.provider,
444
+ },
445
+ });
446
+ detect.disabled = false;
447
+ const n = r.ok && r.data && r.data.contextWindow;
448
+ if (r.ok && r.data && r.data.ok && typeof n === "number" && n > 0) {
449
+ state.answers.contextWindow = n;
450
+ cw.value = String(n);
451
+ setMsg(box, "ok", tr("web.setup.context.detect_ok", { count: n }));
452
+ } else {
453
+ setMsg(box, "warn", tr("web.setup.context.detect_fail"));
454
+ }
455
+ });
456
+ const row = h("div", "setup-row-inline");
457
+ row.appendChild(detect);
458
+ box.appendChild(row);
459
+
396
460
  const it = h("input", "setup-input");
397
461
  it.type = "number";
398
462
  it.min = "1";
@@ -408,25 +472,219 @@
408
472
  function renderSecurity() {
409
473
  const box = stepBody("security");
410
474
  const toggles = [
411
- ["securityPathsRestrict", "web.setup.security.paths"],
412
- ["securityBashBlock", "web.setup.security.bash"],
413
- ["securityFlagsBlock", "web.setup.security.flags"],
475
+ ["securityPathsRestrict", "web.setup.security.paths", "web.setup.security.paths_desc", "paths"],
476
+ ["securityBashBlock", "web.setup.security.bash", "web.setup.security.bash_desc", "bash"],
477
+ ["securityFlagsBlock", "web.setup.security.flags", "web.setup.security.flags_desc", "flags"],
414
478
  ];
415
- toggles.forEach(([key, labelKey]) => {
416
- const row = h("label", "setup-toggle");
479
+ toggles.forEach(([key, labelKey, descKey, group]) => {
480
+ const row = h("div", "setup-sec-row");
481
+ const label = h("label", "setup-toggle");
417
482
  const cb = h("input");
418
483
  cb.type = "checkbox";
419
484
  cb.checked = Boolean(state.answers[key]);
420
485
  cb.addEventListener("change", () => {
421
486
  state.answers[key] = cb.checked;
422
487
  });
423
- row.appendChild(cb);
424
- row.appendChild(h("span", null, tr(labelKey)));
488
+ label.appendChild(cb);
489
+ const texts = h("span", "setup-toggle-texts");
490
+ texts.appendChild(h("span", "t", tr(labelKey)));
491
+ const desc = tr(descKey);
492
+ if (desc !== descKey) texts.appendChild(h("span", "d", desc));
493
+ label.appendChild(texts);
494
+ row.appendChild(label);
495
+ row.appendChild(detailButton(group, tr(labelKey)));
425
496
  box.appendChild(row);
426
497
  });
498
+
499
+ const advRow = h("div", "setup-sec-row");
500
+ const advTexts = h("span", "setup-toggle setup-adv-label");
501
+ advTexts.appendChild(h("span", "t", tr("web.setup.security.advanced")));
502
+ advRow.appendChild(advTexts);
503
+ advRow.appendChild(detailButton("advanced", tr("web.setup.security.advanced")));
504
+ box.appendChild(advRow);
427
505
  return box;
428
506
  }
429
507
 
508
+ // Описание поп-апов тонкой настройки: путь в securityDetail + тип поля.
509
+ const SECURITY_POPUPS = {
510
+ paths: [
511
+ ["paths.denied", "list", "web.setup.security.denied"],
512
+ ["paths.allowed", "list", "web.setup.security.allowed"],
513
+ ],
514
+ bash: [
515
+ ["bash.blacklist", "list", "web.setup.security.blacklist"],
516
+ ["bash.whitelist", "list", "web.setup.security.whitelist"],
517
+ ["bash.logCommands", "bool", "web.setup.security.log_commands"],
518
+ ],
519
+ flags: [
520
+ ["flags.dangerousFlags", "list", "web.setup.security.dangerous_flags"],
521
+ ["flags.dangerousOperators", "list", "web.setup.security.dangerous_operators"],
522
+ ],
523
+ advanced: [
524
+ ["network.enabled", "bool", "web.setup.security.network_enabled"],
525
+ ["network.deniedDomains", "list", "web.setup.security.denied_domains"],
526
+ ["network.allowedDomains", "list", "web.setup.security.allowed_domains"],
527
+ ["network.requestTimeout", "int", "web.setup.security.request_timeout"],
528
+ ["network.maxResponseSize", "int", "web.setup.security.max_response_size"],
529
+ ["advanced.maxRecursionDepth", "int", "web.setup.security.max_recursion"],
530
+ ["advanced.maxFileOperations", "int", "web.setup.security.max_file_ops"],
531
+ ["advanced.maxRequestsPerMinute", "int", "web.setup.security.rate_rpm"],
532
+ ["advanced.maxParallelTasks", "int", "web.setup.security.rate_parallel"],
533
+ ["advanced.contentScanEnabled", "bool", "web.setup.security.content_scan"],
534
+ ["advanced.sessionEncryptionEnabled", "bool", "web.setup.security.session_encryption"],
535
+ ["advanced.auditNotifierEnabled", "bool", "web.setup.security.audit_enabled"],
536
+ ["advanced.auditNotifierMinSeverity", "enum", "web.setup.security.audit_severity"],
537
+ ["advanced.auditNotifierEventTypes", "list", "web.setup.security.audit_events"],
538
+ ["advanced.auditNotifierMaxRetries", "int", "web.setup.security.audit_retries"],
539
+ ["advanced.auditNotifierWebhookTimeout", "int", "web.setup.security.audit_timeout"],
540
+ ],
541
+ };
542
+ const SEVERITIES = ["low", "medium", "high", "critical"];
543
+ const GROUP_KEYS = {
544
+ paths: ["paths"],
545
+ bash: ["bash"],
546
+ flags: ["flags"],
547
+ advanced: ["network", "advanced"],
548
+ };
549
+
550
+ function getPath(obj, path) {
551
+ return path.split(".").reduce((o, k) => (o == null ? undefined : o[k]), obj);
552
+ }
553
+ function setPath(obj, path, value) {
554
+ const keys = path.split(".");
555
+ let cur = obj;
556
+ for (let i = 0; i < keys.length - 1; i++) {
557
+ if (typeof cur[keys[i]] !== "object" || cur[keys[i]] === null) cur[keys[i]] = {};
558
+ cur = cur[keys[i]];
559
+ }
560
+ cur[keys[keys.length - 1]] = value;
561
+ }
562
+
563
+ function detailButton(group, title) {
564
+ const btn = h("button", "setup-detail-btn");
565
+ btn.type = "button";
566
+ btn.appendChild(h("span", "g", "⚙"));
567
+ btn.appendChild(h("span", "t", tr("web.setup.security.configure")));
568
+ btn.title = title;
569
+ btn.addEventListener("click", (e) => {
570
+ e.preventDefault();
571
+ openSecurityPopup(group, title);
572
+ });
573
+ return btn;
574
+ }
575
+
576
+ let popup = null;
577
+
578
+ function openSecurityPopup(group, title) {
579
+ const overlay = h("div", "setup-overlay");
580
+ const modal = h("div", "setup-modal");
581
+ modal.setAttribute("role", "dialog");
582
+ modal.setAttribute("aria-modal", "true");
583
+
584
+ const head = h("div", "setup-modal-head");
585
+ const hIcon = h("span", "glyph", "⚙");
586
+ head.appendChild(hIcon);
587
+ head.appendChild(h("span", "setup-modal-title", title));
588
+ const close = h("button", "setup-modal-x", "✕");
589
+ close.type = "button";
590
+ close.addEventListener("click", closePopup);
591
+ head.appendChild(close);
592
+ modal.appendChild(head);
593
+
594
+ const body = h("div", "setup-modal-body");
595
+ body.appendChild(h("p", "setup-hint", tr("web.setup.security.popup_hint")));
596
+ SECURITY_POPUPS[group].forEach(([path, type, labelKey]) => {
597
+ body.appendChild(popupField(path, type, tr(labelKey)));
598
+ });
599
+ modal.appendChild(body);
600
+
601
+ const foot = h("div", "setup-modal-foot");
602
+ const reset = h("button", "setup-btn link", tr("web.setup.security.reset"));
603
+ reset.type = "button";
604
+ reset.addEventListener("click", () => {
605
+ const defaults = (state.display && state.display.defaults && state.display.defaults.security) || {};
606
+ (GROUP_KEYS[group] || [group]).forEach((src) => {
607
+ const fresh = defaults[src] || {};
608
+ Object.keys(fresh).forEach((k) => setPath(state.answers.securityDetail, src + "." + k, fresh[k]));
609
+ });
610
+ closePopup();
611
+ openSecurityPopup(group, title);
612
+ });
613
+ foot.appendChild(reset);
614
+ foot.appendChild(h("span", "spacer"));
615
+ const done = h("button", "setup-btn primary", tr("web.setup.security.popup_done"));
616
+ done.type = "button";
617
+ done.addEventListener("click", closePopup);
618
+ foot.appendChild(done);
619
+ modal.appendChild(foot);
620
+
621
+ overlay.appendChild(modal);
622
+ overlay.addEventListener("click", (e) => {
623
+ if (e.target === overlay) closePopup();
624
+ });
625
+ popup = overlay;
626
+ gate.appendChild(overlay);
627
+ }
628
+
629
+ function popupField(path, type, label) {
630
+ const wrap = h("div", "setup-field");
631
+ if (type === "list") {
632
+ wrap.appendChild(h("label", "setup-label", label));
633
+ const ta = h("textarea", "setup-input setup-textarea");
634
+ const cur = getPath(state.answers.securityDetail, path);
635
+ ta.value = Array.isArray(cur) ? cur.join("\n") : "";
636
+ ta.rows = 5;
637
+ ta.spellcheck = false;
638
+ ta.addEventListener("input", () => {
639
+ const items = ta.value
640
+ .split("\n")
641
+ .map((s) => s.trim())
642
+ .filter(Boolean);
643
+ setPath(state.answers.securityDetail, path, items);
644
+ });
645
+ wrap.appendChild(ta);
646
+ } else if (type === "bool") {
647
+ const row = h("label", "setup-toggle");
648
+ const cb = h("input");
649
+ cb.type = "checkbox";
650
+ cb.checked = Boolean(getPath(state.answers.securityDetail, path));
651
+ cb.addEventListener("change", () => setPath(state.answers.securityDetail, path, cb.checked));
652
+ row.appendChild(cb);
653
+ row.appendChild(h("span", "t", label));
654
+ wrap.appendChild(row);
655
+ } else if (type === "int") {
656
+ wrap.appendChild(h("label", "setup-label", label));
657
+ const inp = h("input", "setup-input");
658
+ inp.type = "number";
659
+ inp.min = "1";
660
+ const cur = getPath(state.answers.securityDetail, path);
661
+ inp.value = typeof cur === "number" ? String(cur) : "";
662
+ inp.addEventListener("input", () => {
663
+ const n = parseInt(inp.value, 10);
664
+ setPath(state.answers.securityDetail, path, Number.isFinite(n) && n > 0 ? n : undefined);
665
+ });
666
+ wrap.appendChild(inp);
667
+ } else if (type === "enum") {
668
+ wrap.appendChild(h("label", "setup-label", label));
669
+ const sel = h("select", "setup-input");
670
+ const cur = getPath(state.answers.securityDetail, path);
671
+ SEVERITIES.forEach((sev) => {
672
+ const opt = h("option", null, tr("web.setup.security.severity." + sev));
673
+ opt.value = sev;
674
+ if (cur === sev) opt.selected = true;
675
+ sel.appendChild(opt);
676
+ });
677
+ sel.addEventListener("change", () => setPath(state.answers.securityDetail, path, sel.value));
678
+ wrap.appendChild(sel);
679
+ }
680
+ return wrap;
681
+ }
682
+
683
+ function closePopup() {
684
+ if (popup && popup.parentNode) popup.parentNode.removeChild(popup);
685
+ popup = null;
686
+ }
687
+
430
688
  function securitySummary() {
431
689
  const on = [];
432
690
  if (state.answers.securityPathsRestrict) on.push(tr("web.setup.security.paths"));
@@ -534,6 +792,7 @@
534
792
  securityBashBlock: state.answers.securityBashBlock,
535
793
  securityFlagsBlock: state.answers.securityFlagsBlock,
536
794
  securityPathsRestrict: state.answers.securityPathsRestrict,
795
+ securityDetail: state.answers.securityDetail,
537
796
  },
538
797
  });
539
798
  if (!r.ok) {
@@ -229,7 +229,7 @@ body[data-font="large"] { font-size: 17px; }
229
229
  .layout { flex: 1; display: flex; min-height: 0; }
230
230
 
231
231
  /* chat */
232
- .chat { flex: 1; display: flex; flex-direction: column; min-width: 0; }
232
+ .chat { flex: 1; display: flex; flex-direction: column; min-width: 0; position: relative; }
233
233
  .msgs { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 18px 22px; display: flex; flex-direction: column; gap: 11px; }
234
234
  .msgs > * { flex-shrink: 0; min-height: fit-content; }
235
235
  /* messages live inside a per-tab `.feed` wrapper; it must mirror the `.msgs`
@@ -452,6 +452,18 @@ body.light .imgcard .ph { background: var(--ph-light); }
452
452
  }
453
453
  .empty .g { color: var(--green); }
454
454
 
455
+ /* jump-to-latest pill: only visible when the reader scrolled up */
456
+ .to-latest {
457
+ position: absolute; left: 50%; bottom: 148px; transform: translateX(-50%);
458
+ z-index: 20; font: inherit; font-size: 12.5px; cursor: pointer;
459
+ color: var(--accent); background: var(--panel);
460
+ border: 1px solid var(--border-2); border-radius: 999px;
461
+ padding: 5px 14px; box-shadow: var(--shadow-menu);
462
+ white-space: nowrap;
463
+ }
464
+ .to-latest:hover { border-color: var(--accent); }
465
+ .to-latest[hidden] { display: none; }
466
+
455
467
  /* ---- composer: selectors, attachments, input, autocomplete ---- */
456
468
  .input-area { margin: 14px 22px 16px; position: relative; }
457
469
  .selectors { display: flex; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; min-width: 0; }
@@ -590,6 +602,61 @@ body.light .sel > button { border: 1px solid var(--border); }
590
602
  .input-area.dragover .composer { border-color: var(--accent); box-shadow: 0 0 0 2px var(--input-shadow); }
591
603
  @keyframes blink { 50% { opacity: 0; } }
592
604
 
605
+ /* ---- chat motion ("magic"): entrances, caret, pulses, pops -----------------
606
+ Rules: only opacity/transform animations (never height/margin), 100–200ms,
607
+ calm single pulses. Killed globally by the `prefers-reduced-motion` block
608
+ below. Glyphs stay textual per STYLE.md — motion, not decoration. */
609
+ @keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
610
+ @keyframes pop { from { opacity: 0; transform: scale(.98) translateY(2px); } to { opacity: 1; transform: none; } }
611
+ @keyframes softPulse { 50% { opacity: .45; } }
612
+ @keyframes fadeIn { from { opacity: 0; } }
613
+
614
+ /* new rows rise in softly (a bulk history render animates once as a whole —
615
+ one shared fade, still cheap: opacity/transform only) */
616
+ .feed > :is(.u, .md, .act, .think, .q, .filecard, .planblock, .imgcard, .linkline,
617
+ .toolout, .metric, .detail, .metaln, .warnline, .errline, .cmdout, .sysline,
618
+ .startbanner, .grouped, .msg-actions, .busy) {
619
+ animation: rise .18s ease-out;
620
+ }
621
+ /* quiet-mode run folding fades instead of snapping away */
622
+ .fold-out { opacity: 0; transition: opacity .15s ease-out; }
623
+
624
+ /* streaming caret at the tail of live markdown */
625
+ .md.streaming::after {
626
+ content: "▌"; color: var(--accent); margin-left: 2px;
627
+ animation: blink 1s steps(2, start) infinite;
628
+ }
629
+ /* alive labels: in-feed spinner, the *live* think block, rail phase,
630
+ a running tool. Finished blocks stay static — a pulsing "думаю" after
631
+ the turn reads as hung. */
632
+ .busy .lab, .think.live .think-h .lab, .rail-l1 .lab, .act.running .lab {
633
+ animation: softPulse 1.6s ease-in-out infinite;
634
+ }
635
+
636
+ /* menus, autocomplete and dialogs pop instead of snapping */
637
+ .dd.open .dd-menu, .sel.open .menu, .cmdk:not([hidden]) { animation: pop .12s ease-out; }
638
+ .to-latest:not([hidden]) { animation: pop .15s ease-out; }
639
+ .overlay.open { animation: fadeIn .15s ease-out; }
640
+ .overlay.open .modal { animation: rise .18s ease-out; }
641
+
642
+ /* hover/active blends */
643
+ .sel > button, .menu .mi, .stab, .q .qopt, .q .qsend, .filecard, .icon-btn,
644
+ .btn, .linkbtn, .chip, .cb-copy, .act-btn, .copy-btn, .composer .attach-btn,
645
+ .composer .send-btn, .dd-menu .si, .dd-menu .act, .cmdk-item, .toggle {
646
+ transition: background-color .12s, color .12s, border-color .12s;
647
+ }
648
+ .composer { transition: border-color .15s, box-shadow .15s; }
649
+
650
+ /* scroll edge fades on the transcript (classes from the feed scroll hook) */
651
+ .msgs::before, .msgs::after {
652
+ content: ""; position: sticky; display: block; height: 22px; flex: 0 0 auto;
653
+ pointer-events: none; z-index: 5; opacity: 0; transition: opacity .25s;
654
+ }
655
+ .msgs::before { top: 0; margin-bottom: -22px; background: linear-gradient(var(--bg), transparent); }
656
+ .msgs::after { bottom: 0; margin-top: -22px; background: linear-gradient(transparent, var(--bg)); }
657
+ .msgs.scrolled::before { opacity: 1; }
658
+ .msgs.unfollowed::after { opacity: 1; }
659
+
593
660
  /* ---- activity rail — indicator above the composer while a turn runs ----
594
661
  Reproduces docs/design/ui/demo-activity-rail-chat.html (variant №12).
595
662
  `l-line` (default) = compact one-liner, `l-meta` = inside the hint row,
@@ -686,6 +753,66 @@ body.light .sel > button { border: 1px solid var(--border); }
686
753
  .sec.plan-sec { flex: 1 1 auto; }
687
754
  .sec.usage-sec { flex: 0 1 auto; max-height: 45%; }
688
755
 
756
+ /* ---- sidebar tabs ---- */
757
+ .sb-tabs { display: flex; gap: 6px; padding: 10px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
758
+ .sb-tab {
759
+ position: relative; width: 34px; height: 34px; flex: 0 0 auto;
760
+ display: inline-flex; align-items: center; justify-content: center; padding: 0;
761
+ border: 1px solid var(--border-2); border-radius: 6px;
762
+ background: transparent; color: var(--stab-fg); cursor: pointer; font: inherit;
763
+ }
764
+ .sb-tab:hover { color: var(--fg); background: var(--hover-bg); }
765
+ .sb-tab.on { color: var(--accent); background: var(--accent-bg); border-color: transparent; }
766
+ .sb-tab .sb-ico svg { width: 17px; height: 17px; }
767
+ .sb-tab .sb-dot {
768
+ position: absolute; top: 4px; right: 4px; width: 6px; height: 6px; border-radius: 50%;
769
+ background: var(--accent); opacity: 0; transition: opacity .2s;
770
+ }
771
+ .sb-tab[data-indicator="live"] .sb-dot { opacity: 1; animation: sbPulse 1s ease-in-out infinite; }
772
+ .sb-tab[data-indicator="busy"] .sb-dot { opacity: 1; background: var(--accent); }
773
+ .sb-tab[data-indicator="stale"] .sb-dot { opacity: 1; background: var(--yellow); }
774
+ @keyframes sbPulse { 0%,100% { opacity: 1 } 50% { opacity: .25 } }
775
+
776
+ .sb-name {
777
+ display: flex; align-items: center; gap: 8px; flex-shrink: 0;
778
+ padding: 10px 16px; color: var(--fg); font-size: 12.5px; font-weight: 600;
779
+ letter-spacing: .03em; border-bottom: 1px solid var(--border);
780
+ }
781
+ .sb-name .sb-ico svg { width: 14px; height: 14px; color: var(--accent); }
782
+
783
+ .sb-pane { display: flex; flex-direction: column; flex: 1; min-height: 0; }
784
+ .sb-pane[hidden] { display: none; }
785
+
786
+ /* master-detail expansion */
787
+ .sb { transition: width .22s ease, min-width .22s ease; }
788
+ .sb.sb-expanded { width: 680px; min-width: 680px; }
789
+ #pane-trace { flex-direction: row; }
790
+ .sb-expanded #pane-trace { position: relative; }
791
+ .trace-list { flex: 1; min-width: 0; overflow-y: auto; padding: 6px 10px 12px; display: flex; flex-direction: column; gap: 1px; }
792
+ .trace-turn { color: var(--dim-c); font-size: 11px; margin: 8px 6px 2px; }
793
+ .trc { display: flex; gap: 8px; align-items: baseline; padding: 4px 6px; font-size: 12px; color: var(--muted); border-radius: 4px; cursor: pointer; }
794
+ .trc:hover { background: var(--hover-bg); }
795
+ .trc.sel { background: var(--accent-bg); }
796
+ .trc .g { flex: 0 0 auto; }
797
+ .trc.recon .g { color: var(--cyan); } .trc.mutate .g { color: var(--purple); }
798
+ .trc.execute .g { color: var(--accent); } .trc.meta .g { color: var(--meta); } .trc.phase .g { color: var(--think); }
799
+ .trc .lab { color: var(--fg); white-space: nowrap; }
800
+ .trc .args { color: var(--args); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1; }
801
+ .trc .t { color: var(--dim-b); margin-left: auto; flex: 0 0 auto; }
802
+ .trc.warn .g, .trc.warn .lab { color: var(--yellow); }
803
+ .trc.phase .lab { color: var(--think); }
804
+ .trc-detail { display: flex; flex-direction: column; flex: 0 0 340px; min-width: 0; border-left: 1px solid var(--border); }
805
+ .trc-detail[hidden] { display: none; }
806
+ .detail-h { display: flex; gap: 8px; align-items: center; padding: 10px 14px; border-bottom: 1px solid var(--border); color: var(--fg); font-size: 12.5px; flex-shrink: 0; }
807
+ .detail-h .g { color: var(--accent); }
808
+ .detail-h .x { margin-left: auto; color: var(--dim-b); cursor: pointer; }
809
+ .detail-b { padding: 10px 14px; display: flex; flex-direction: column; gap: 12px; overflow-y: auto; }
810
+ .detail-b h5 { margin: 0 0 4px; font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--sec-h); font-weight: 600; }
811
+ .map-sec { flex: 1 1 auto; }
812
+ .map-reindex { margin-top: 10px; font: inherit; font-size: 12px; color: var(--accent); background: transparent; border: 1px solid var(--border-2); border-radius: 5px; padding: 4px 10px; cursor: pointer; width: fit-content; }
813
+ .map-reindex:disabled { color: var(--dim-b); cursor: default; }
814
+ .kv.wn b { color: var(--yellow); }
815
+
689
816
  ::-webkit-scrollbar { width: 8px; }
690
817
  ::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: 4px; }
691
818
 
@@ -1212,6 +1339,7 @@ button.toggle:disabled { opacity: .5; cursor: default; }
1212
1339
  .chat { min-height: 0; }
1213
1340
  /* Phones: sidebar hidden, opens as an overlay drawer via body.sb-open. */
1214
1341
  .sb { display: none; }
1342
+ .sb-tab[data-tab="trace"], #pane-trace { display: none; }
1215
1343
  body.sb-open .sb {
1216
1344
  display: flex; position: fixed; top: 41px; right: 0; bottom: 0; z-index: 45;
1217
1345
  width: min(360px, 85vw); min-width: 0; max-height: none;
@@ -1349,8 +1477,45 @@ body.setup-active .layout { display: none; }
1349
1477
  .setup-summary .k { color: var(--dim-a); width: 150px; flex: 0 0 auto; }
1350
1478
  .setup-summary .v { color: var(--fg); word-break: break-all; }
1351
1479
 
1352
- .setup-toggle { display: flex; align-items: center; gap: 9px; padding: 7px 0; font-size: 13px; cursor: pointer; }
1353
- .setup-toggle input { accent-color: var(--green-fill); width: 15px; height: 15px; }
1480
+ .setup-toggle { display: flex; align-items: flex-start; gap: 9px; padding: 7px 0; font-size: 13px; cursor: pointer; }
1481
+ .setup-toggle input { accent-color: var(--green-fill); width: 15px; height: 15px; margin-top: 2px; flex: 0 0 auto; }
1482
+ .setup-toggle-texts { display: flex; flex-direction: column; gap: 2px; }
1483
+ .setup-toggle-texts .t { font-weight: 500; }
1484
+ .setup-toggle-texts .d { color: var(--dim-a); font-size: 12px; line-height: 1.5; }
1485
+
1486
+ .setup-sec-row { display: flex; align-items: center; gap: 8px; }
1487
+ .setup-sec-row .setup-toggle { flex: 1; }
1488
+ .setup-adv-label { padding: 7px 0; }
1489
+ .setup-detail-btn {
1490
+ display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto;
1491
+ background: transparent; color: var(--dim-a);
1492
+ border: 1px solid var(--border); border-radius: 7px;
1493
+ padding: 5px 9px; font-size: 12px; font-family: inherit; cursor: pointer;
1494
+ }
1495
+ .setup-detail-btn:hover { background: var(--hover-bg); color: var(--fg); }
1496
+ .setup-detail-btn .g { color: var(--accent); }
1497
+
1498
+ .setup-textarea { resize: vertical; min-height: 84px; font-family: var(--mono, ui-monospace, monospace); font-size: 12.5px; line-height: 1.5; }
1499
+
1500
+ .setup-overlay {
1501
+ position: fixed; inset: 0; z-index: 60;
1502
+ background: rgba(0, 0, 0, .55);
1503
+ display: flex; align-items: center; justify-content: center; padding: 20px;
1504
+ }
1505
+ .setup-modal {
1506
+ width: 100%; max-width: 560px; max-height: 86vh; display: flex; flex-direction: column;
1507
+ background: var(--surface); border: 1px solid var(--border-2); border-radius: 10px;
1508
+ box-shadow: 0 18px 50px rgba(0, 0, 0, .45);
1509
+ }
1510
+ .setup-modal-head { display: flex; align-items: center; gap: 8px; padding: 14px 16px; border-bottom: 1px solid var(--border-3); }
1511
+ .setup-modal-head .glyph { color: var(--accent); }
1512
+ .setup-modal-title { font-weight: 600; font-size: 14px; flex: 1; }
1513
+ .setup-modal-x { background: none; border: none; color: var(--dim-a); font-size: 15px; cursor: pointer; padding: 2px 6px; }
1514
+ .setup-modal-x:hover { color: var(--fg); }
1515
+ .setup-modal-body { padding: 14px 16px; overflow-y: auto; }
1516
+ .setup-modal-body .setup-field:last-child { margin-bottom: 0; }
1517
+ .setup-modal-foot { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-top: 1px solid var(--border-3); }
1518
+ .setup-modal-foot .spacer { flex: 1; }
1354
1519
 
1355
1520
  @media (max-width: 640px) {
1356
1521
  .setup-gate { padding: 24px 14px; }
@@ -1,3 +1,4 @@
1
+ // Строка кеша в футере: хитрейт, экономия, стабильность префикса.
1
2
  import { t } from "../i18n/index";
2
3
  import { formatCost } from "../modules/pricing/prices";
3
4
  /**
@@ -1,4 +1,5 @@
1
- /** Levenshtein edit distance between two strings (case-insensitive). */
1
+ // Подсказки при опечатках в командах: расстояние Левенштейна.
2
+ /** Редакционное расстояние Левенштейна между строками (без учёта регистра). */
2
3
  export function levenshtein(a, b) {
3
4
  const s = a.toLowerCase();
4
5
  const t = b.toLowerCase();
@@ -20,9 +21,9 @@ export function levenshtein(a, b) {
20
21
  return prev[t.length];
21
22
  }
22
23
  /**
23
- * The known command whose name is within `maxDistance` of `input`, or
24
- * `undefined` when nothing is close enough. Used to catch subcommand typos
25
- * (`mma sesion list`) that would otherwise be silently sent to the LLM.
24
+ * Известная команда, чьё имя в пределах `maxDistance` от `input`, или
25
+ * `undefined`, если близких нет. Ловит опечатки в субкомандах
26
+ * (`mma sesion list`), которые иначе молча ушли бы в LLM.
26
27
  */
27
28
  export function closestCommand(input, known, maxDistance = 2) {
28
29
  let best;