micro-models-agent 2.10.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (359) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/assets/app.js +22 -4
  3. package/dist/assets/index.html +29 -0
  4. package/dist/assets/js/dom.js +2 -1
  5. package/dist/assets/js/feed.js +38 -26
  6. package/dist/assets/js/format.js +1 -1
  7. package/dist/assets/js/rail.js +114 -0
  8. package/dist/assets/js/settings-controls.js +2 -2
  9. package/dist/assets/js/settings.js +246 -51
  10. package/dist/assets/js/store.js +3 -0
  11. package/dist/assets/js/tabs.js +104 -2
  12. package/dist/assets/styles.css +215 -66
  13. package/dist/cli/cache-line.js +1 -0
  14. package/dist/cli/command-suggest.js +5 -4
  15. package/dist/cli/commands.js +231 -122
  16. package/dist/cli/completer.js +132 -31
  17. package/dist/cli/index.js +1 -0
  18. package/dist/cli/json-payload.js +4 -4
  19. package/dist/cli/main.js +166 -61
  20. package/dist/cli/onboarding-commands.js +126 -0
  21. package/dist/cli/plugin-commands.js +2 -1
  22. package/dist/cli/relaunch.js +16 -13
  23. package/dist/cli/repl-commands-lsp.js +75 -0
  24. package/dist/cli/repl-commands-skills.js +105 -0
  25. package/dist/cli/repl-commands.js +589 -266
  26. package/dist/cli/repl.js +436 -177
  27. package/dist/cli/run-result.js +5 -6
  28. package/dist/cli/security-commands.js +37 -28
  29. package/dist/cli/setup-order.js +4 -55
  30. package/dist/cli/setup-prompt.js +3 -2
  31. package/dist/cli/setup.js +27 -107
  32. package/dist/cli/web-command.js +98 -0
  33. package/dist/config/budget.js +5 -5
  34. package/dist/config/config.js +103 -96
  35. package/dist/config/defaults.js +38 -8
  36. package/dist/config/domains.js +24 -27
  37. package/dist/config/env.js +46 -0
  38. package/dist/config/experts.js +2 -0
  39. package/dist/config/index.js +1 -0
  40. package/dist/config/paths.js +56 -0
  41. package/dist/config/redact.js +44 -0
  42. package/dist/config/registry-extra.js +334 -0
  43. package/dist/config/registry.js +584 -0
  44. package/dist/config/security.js +35 -36
  45. package/dist/config/timeouts.js +74 -0
  46. package/dist/config/utils.js +16 -15
  47. package/dist/core/agent/artifact-progress.js +59 -0
  48. package/dist/core/agent/artifact-resolve.js +79 -0
  49. package/dist/core/agent/audit-gate.js +7 -3
  50. package/dist/core/agent/compaction.js +2 -2
  51. package/dist/core/agent/constants.js +33 -4
  52. package/dist/core/agent/context-log.js +454 -0
  53. package/dist/core/agent/context-renderer.js +5 -0
  54. package/dist/core/agent/critic-gate.js +132 -0
  55. package/dist/core/agent/hallucination-gate.js +148 -38
  56. package/dist/core/agent/loop-state.js +15 -0
  57. package/dist/core/agent/prefix-monitor.js +1 -1
  58. package/dist/core/agent/reasoning-resolver.js +10 -1
  59. package/dist/core/agent/token-tracker.js +7 -4
  60. package/dist/core/agent/tool-batch.js +93 -18
  61. package/dist/core/agent/tool-images.js +18 -0
  62. package/dist/core/agent/tool-output.js +65 -11
  63. package/dist/core/agent/tool-pairing.js +60 -0
  64. package/dist/core/agent-moe.js +25 -24
  65. package/dist/core/agent.js +429 -108
  66. package/dist/core/bootstrap/agent-phase.js +66 -0
  67. package/dist/core/bootstrap/config-phase.js +86 -0
  68. package/dist/core/bootstrap/modules-phase.js +269 -0
  69. package/dist/core/bootstrap/provider-phase.js +183 -0
  70. package/dist/core/bootstrap/session-phase.js +204 -0
  71. package/dist/core/bootstrap/state.js +94 -0
  72. package/dist/core/bootstrap/workspace-phase.js +115 -0
  73. package/dist/core/bootstrap.js +92 -660
  74. package/dist/core/crash-handler.js +17 -7
  75. package/dist/core/environment.js +37 -12
  76. package/dist/core/host-plan.js +43 -0
  77. package/dist/core/host.js +23 -0
  78. package/dist/core/index.js +1 -0
  79. package/dist/core/prompt-builder.js +17 -1
  80. package/dist/core/prompt-overflow.js +42 -43
  81. package/dist/core/session-logger.js +122 -30
  82. package/dist/core/system-prompt.js +117 -0
  83. package/dist/core/version.js +6 -4
  84. package/dist/core/web-host.js +34 -0
  85. package/dist/core/workspace.js +19 -15
  86. package/dist/i18n/en.json +106 -21
  87. package/dist/i18n/index.js +37 -3
  88. package/dist/i18n/ru.json +109 -21
  89. package/dist/index.js +7 -3
  90. package/dist/llm/image-utils.js +55 -64
  91. package/dist/llm/llm-errors.js +9 -0
  92. package/dist/llm/model-loader.js +10 -32
  93. package/dist/llm/models-dev.js +121 -0
  94. package/dist/llm/openai-compat.js +146 -73
  95. package/dist/llm/orchestrator.js +33 -11
  96. package/dist/llm/provider-budget.js +2 -2
  97. package/dist/llm/provider-context.js +233 -0
  98. package/dist/llm/provider.js +1 -1
  99. package/dist/llm/response.js +5 -0
  100. package/dist/llm/token-counter.js +27 -11
  101. package/dist/logger/app-logger.js +48 -22
  102. package/dist/logger/file-log.js +28 -19
  103. package/dist/logger/index.js +1 -0
  104. package/dist/main.js +24285 -22447
  105. package/dist/migration/backup.js +14 -3
  106. package/dist/migration/detect.js +12 -3
  107. package/dist/migration/index.js +1 -0
  108. package/dist/modules/artifacts/store.js +9 -8
  109. package/dist/modules/browser/actions.js +6 -0
  110. package/dist/modules/browser/bridge-client.js +14 -12
  111. package/dist/modules/browser/bridge-path.js +4 -4
  112. package/dist/modules/browser/console-buffer.js +37 -0
  113. package/dist/modules/browser/cookie-store.js +5 -0
  114. package/dist/modules/browser/driver-types.js +1 -0
  115. package/dist/modules/browser/driver.js +19 -11
  116. package/dist/modules/browser/index.js +3 -1
  117. package/dist/modules/browser/module.js +2 -1
  118. package/dist/modules/browser/session.js +10 -30
  119. package/dist/modules/browser/snapshot.js +19 -13
  120. package/dist/modules/browser/types.js +1 -0
  121. package/dist/modules/certification/cli.js +28 -20
  122. package/dist/modules/certification/fact-checker.js +9 -1
  123. package/dist/modules/certification/loader.js +9 -2
  124. package/dist/modules/certification/manifest.js +83 -21
  125. package/dist/modules/certification/runner.js +23 -13
  126. package/dist/modules/certification/scenarios.js +20 -20
  127. package/dist/modules/certification/syntax-scenarios.js +1 -2
  128. package/dist/modules/context/chunk-query.js +11 -9
  129. package/dist/modules/context/fact-extractor.js +37 -39
  130. package/dist/modules/context/index.js +1 -0
  131. package/dist/modules/context/manager.js +106 -80
  132. package/dist/modules/critic/client.js +176 -0
  133. package/dist/modules/critic/evidence.js +81 -0
  134. package/dist/modules/critic/provider.js +45 -0
  135. package/dist/modules/critic/tools.js +78 -0
  136. package/dist/modules/critic/types.js +1 -0
  137. package/dist/modules/execution/audit-runners.js +28 -34
  138. package/dist/modules/execution/auditor.js +53 -49
  139. package/dist/modules/execution/execution-plugin.js +142 -102
  140. package/dist/modules/execution/index.js +1 -1
  141. package/dist/modules/execution/input-from.js +37 -13
  142. package/dist/modules/execution/module.js +368 -191
  143. package/dist/modules/execution/moe-executor.js +53 -26
  144. package/dist/modules/execution/plan-actions.js +320 -202
  145. package/dist/modules/execution/plan-checks.js +35 -0
  146. package/dist/modules/execution/plan-coverage.js +36 -33
  147. package/dist/modules/execution/plan-steps.js +11 -3
  148. package/dist/modules/execution/plan-store.js +83 -17
  149. package/dist/modules/execution/plan-tool.js +89 -35
  150. package/dist/modules/execution/plan-validator.js +30 -10
  151. package/dist/modules/execution/planner.js +22 -13
  152. package/dist/modules/execution/stuck-detector.js +196 -93
  153. package/dist/modules/execution/tracker.js +22 -8
  154. package/dist/modules/execution/verifier.js +69 -48
  155. package/dist/modules/execution/windows-commands.js +12 -12
  156. package/dist/modules/hallucination/artifact-claim.js +93 -0
  157. package/dist/modules/hallucination/confidence.js +13 -11
  158. package/dist/modules/hallucination/consistency.js +10 -2
  159. package/dist/modules/hallucination/detector.js +10 -5
  160. package/dist/modules/hallucination/factual.js +43 -26
  161. package/dist/modules/hallucination/index.js +1 -0
  162. package/dist/modules/hallucination/js-identifiers.js +44 -45
  163. package/dist/modules/hallucination/llm-judge.js +17 -12
  164. package/dist/modules/hallucination/success-claim.js +59 -0
  165. package/dist/modules/indexer/cache.js +52 -4
  166. package/dist/modules/indexer/index.js +1 -0
  167. package/dist/modules/indexer/map-command.js +2 -4
  168. package/dist/modules/indexer/map-select.js +16 -16
  169. package/dist/modules/indexer/module.js +47 -12
  170. package/dist/modules/indexer/project-profile.js +8 -7
  171. package/dist/modules/indexer/symbols.js +15 -15
  172. package/dist/modules/indexer/walker.js +159 -29
  173. package/dist/modules/lsp/check-tool.js +7 -5
  174. package/dist/modules/lsp/client.js +39 -39
  175. package/dist/modules/lsp/command.js +17 -24
  176. package/dist/modules/lsp/config.js +12 -11
  177. package/dist/modules/lsp/index.js +1 -0
  178. package/dist/modules/lsp/module.js +48 -46
  179. package/dist/modules/lsp/probe.js +13 -14
  180. package/dist/modules/lsp/project-root.js +14 -17
  181. package/dist/modules/lsp/startup-check.js +18 -16
  182. package/dist/modules/mcp/client.js +59 -62
  183. package/dist/modules/mcp/index.js +1 -0
  184. package/dist/modules/mcp/module.js +66 -37
  185. package/dist/modules/mcp/registry.js +5 -0
  186. package/dist/modules/memory/index.js +1 -0
  187. package/dist/modules/memory/module.js +12 -8
  188. package/dist/modules/memory/search.js +4 -1
  189. package/dist/modules/memory/store.js +40 -2
  190. package/dist/modules/onboarding/init.js +47 -0
  191. package/dist/modules/onboarding/meet.js +30 -0
  192. package/dist/modules/onboarding/mode.js +30 -0
  193. package/dist/modules/onboarding/prompts.js +43 -0
  194. package/dist/modules/pipelines/engine.js +10 -0
  195. package/dist/modules/pipelines/index.js +1 -0
  196. package/dist/modules/pipelines/parser.js +4 -1
  197. package/dist/modules/pipelines/template.js +4 -0
  198. package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
  199. package/dist/modules/plugins/builtin/notify.js +2 -0
  200. package/dist/modules/plugins/index.js +1 -0
  201. package/dist/modules/plugins/loader.js +18 -3
  202. package/dist/modules/plugins/manager.js +37 -17
  203. package/dist/modules/pricing/index.js +10 -11
  204. package/dist/modules/pricing/prices.js +12 -12
  205. package/dist/modules/processes/index.js +1 -0
  206. package/dist/modules/processes/kill-tree.js +27 -31
  207. package/dist/modules/processes/registry.js +44 -25
  208. package/dist/modules/processes/runner.js +4 -4
  209. package/dist/modules/providers/capabilities.js +28 -0
  210. package/dist/modules/providers/create.js +3 -4
  211. package/dist/modules/providers/factory.js +7 -7
  212. package/dist/modules/providers/fallback.js +12 -12
  213. package/dist/modules/providers/health.js +4 -4
  214. package/dist/modules/providers/manager.js +50 -19
  215. package/dist/modules/providers/naming.js +15 -0
  216. package/dist/modules/providers/preflight.js +71 -0
  217. package/dist/modules/providers/presets.js +39 -8
  218. package/dist/modules/providers/registry.js +7 -2
  219. package/dist/modules/reasoning/policy.js +8 -6
  220. package/dist/modules/reasoning/probe.js +30 -25
  221. package/dist/modules/security/audit-log.js +19 -36
  222. package/dist/modules/security/audit-notifier.js +35 -75
  223. package/dist/modules/security/command-validator.js +99 -51
  224. package/dist/modules/security/content-scanner.js +8 -8
  225. package/dist/modules/security/data-sanitizer.js +9 -16
  226. package/dist/modules/security/encryption.js +32 -52
  227. package/dist/modules/security/index.js +2 -1
  228. package/dist/modules/security/network-validator.js +120 -43
  229. package/dist/modules/security/path-validator.js +61 -46
  230. package/dist/modules/security/protected-paths.js +73 -0
  231. package/dist/modules/security/rate-limiter.js +17 -49
  232. package/dist/modules/security/security-policies.js +25 -57
  233. package/dist/modules/security/session-encryption.js +32 -71
  234. package/dist/modules/security/session-isolation.js +15 -28
  235. package/dist/modules/session/auto-name.js +63 -0
  236. package/dist/modules/session/export.js +98 -0
  237. package/dist/modules/session/index.js +1 -0
  238. package/dist/modules/session/manager.js +79 -28
  239. package/dist/modules/session/module.js +4 -3
  240. package/dist/modules/session/store.js +57 -20
  241. package/dist/modules/setup/answers.js +35 -0
  242. package/dist/modules/setup/apply.js +79 -0
  243. package/dist/modules/setup/discovery.js +72 -0
  244. package/dist/modules/skills/index.js +1 -0
  245. package/dist/modules/skills/loader.js +9 -1
  246. package/dist/modules/skills/module.js +11 -0
  247. package/dist/modules/updater/changelog-reader.js +15 -16
  248. package/dist/modules/updater/checker.js +9 -4
  249. package/dist/modules/updater/dev-detect.js +7 -6
  250. package/dist/modules/updater/index.js +1 -0
  251. package/dist/modules/updater/module.js +17 -15
  252. package/dist/modules/user-profile/compressor.js +2 -0
  253. package/dist/modules/user-profile/index.js +1 -0
  254. package/dist/modules/user-profile/profile.js +10 -1
  255. package/dist/modules/webui/answer-api.js +29 -0
  256. package/dist/modules/webui/assets.js +50 -0
  257. package/dist/modules/webui/body.js +21 -0
  258. package/dist/modules/webui/commands-settings.js +209 -0
  259. package/dist/modules/webui/commands-shared.js +60 -0
  260. package/dist/modules/webui/commands.js +174 -0
  261. package/dist/modules/webui/config-api.js +134 -0
  262. package/dist/modules/webui/file-api.js +202 -0
  263. package/dist/modules/webui/forward.js +159 -0
  264. package/dist/modules/webui/history-api.js +112 -0
  265. package/dist/modules/webui/host-state.js +60 -0
  266. package/dist/modules/webui/i18n-api.js +9 -0
  267. package/dist/modules/webui/limits.js +6 -0
  268. package/dist/modules/webui/log-forward.js +20 -0
  269. package/dist/modules/webui/memory-commands.js +64 -0
  270. package/dist/modules/webui/model-cache.js +18 -0
  271. package/dist/modules/webui/module.js +148 -0
  272. package/dist/modules/webui/persist.js +24 -0
  273. package/dist/modules/webui/prompt-io.js +97 -0
  274. package/dist/modules/webui/providers-api.js +337 -0
  275. package/dist/modules/webui/providers-view.js +56 -0
  276. package/dist/modules/webui/reasoning-view.js +15 -0
  277. package/dist/modules/webui/run-agent.js +81 -0
  278. package/dist/modules/webui/server.js +284 -0
  279. package/dist/modules/webui/sessions-api.js +178 -0
  280. package/dist/modules/webui/settings-api.js +561 -0
  281. package/dist/modules/webui/setup-api.js +201 -0
  282. package/dist/modules/webui/state.js +74 -0
  283. package/dist/modules/webui/util.js +261 -0
  284. package/dist/output/bus.js +7 -2
  285. package/dist/output/channel.js +80 -28
  286. package/dist/output/format.js +2 -1
  287. package/dist/output/index.js +1 -0
  288. package/dist/output/json-sink.js +5 -3
  289. package/dist/output/machine.js +4 -3
  290. package/dist/output/session-sink.js +4 -4
  291. package/dist/tools/approve.js +4 -0
  292. package/dist/tools/attach-image.js +5 -4
  293. package/dist/tools/auto-fixer.js +26 -44
  294. package/dist/tools/auto-tags.js +83 -0
  295. package/dist/tools/bash.js +193 -116
  296. package/dist/tools/browser.js +76 -31
  297. package/dist/tools/chunk-query.js +14 -6
  298. package/dist/tools/create-dir.js +9 -5
  299. package/dist/tools/delete-file.js +9 -5
  300. package/dist/tools/download-file.js +32 -7
  301. package/dist/tools/edit-file.js +52 -14
  302. package/dist/tools/enable-tools.js +6 -6
  303. package/dist/tools/executor.js +132 -27
  304. package/dist/tools/file-info.js +3 -1
  305. package/dist/tools/filter-tools.js +3 -3
  306. package/dist/tools/glob-tool.js +4 -1
  307. package/dist/tools/grep-tool.js +13 -10
  308. package/dist/tools/hidden-tools-block.js +6 -6
  309. package/dist/tools/index.js +16 -8
  310. package/dist/tools/list-dir.js +4 -2
  311. package/dist/tools/load-skill.js +7 -0
  312. package/dist/tools/mcp-call.js +15 -4
  313. package/dist/tools/meet-done.js +14 -0
  314. package/dist/tools/move-file.js +14 -7
  315. package/dist/tools/path-utils.js +29 -29
  316. package/dist/tools/pipeline-run.js +29 -7
  317. package/dist/tools/preview.js +1 -1
  318. package/dist/tools/process-ids.js +31 -0
  319. package/dist/tools/process-kill.js +9 -16
  320. package/dist/tools/process-list.js +1 -0
  321. package/dist/tools/process-log.js +11 -16
  322. package/dist/tools/process-wait.js +63 -0
  323. package/dist/tools/prompt-io.js +4 -3
  324. package/dist/tools/question.js +172 -25
  325. package/dist/tools/read-file.js +55 -34
  326. package/dist/tools/recall.js +14 -8
  327. package/dist/tools/registry.js +6 -5
  328. package/dist/tools/remember.js +8 -4
  329. package/dist/tools/scope-request.js +11 -6
  330. package/dist/tools/search-history.js +5 -2
  331. package/dist/tools/session-info.js +2 -0
  332. package/dist/tools/set-thinking.js +8 -4
  333. package/dist/tools/subagent.js +59 -39
  334. package/dist/tools/syntax-validator.js +292 -90
  335. package/dist/tools/user-input.js +22 -18
  336. package/dist/tools/web-browse.js +9 -3
  337. package/dist/tools/web-fetch.js +12 -23
  338. package/dist/tools/web-search.js +7 -5
  339. package/dist/tools/write-file.js +45 -15
  340. package/dist/ui/action-log.js +13 -0
  341. package/dist/ui/box.js +4 -3
  342. package/dist/ui/colors.js +2 -0
  343. package/dist/ui/diff.js +6 -5
  344. package/dist/ui/index.js +1 -0
  345. package/dist/ui/line-editor.js +230 -122
  346. package/dist/ui/line-math.js +10 -2
  347. package/dist/ui/md-formatter.js +57 -37
  348. package/dist/ui/output.js +8 -3
  349. package/dist/ui/plan-view.js +21 -10
  350. package/dist/ui/renderer.js +393 -67
  351. package/dist/ui/spinner.js +16 -5
  352. package/dist/ui/table.js +15 -17
  353. package/dist/ui/theme.js +143 -0
  354. package/dist/utils/child-run.js +20 -0
  355. package/dist/utils/error.js +1 -1
  356. package/dist/utils/index.js +2 -0
  357. package/dist/utils/retry.js +2 -2
  358. package/dist/utils/truncate.js +1 -1
  359. package/package.json +1 -1
@@ -0,0 +1,63 @@
1
+ // Автоимена сессий: детерминированное из первого сообщения + LLM-уточнение (fail-open).
2
+ import { t } from "../../i18n/index";
3
+ const MAX_NAME_LEN = 60;
4
+ const MIN_CUT = 20;
5
+ /** Детерминированное мгновенное имя сессии из первого сообщения пользователя. */
6
+ export function deriveSessionName(input, date) {
7
+ const firstLine = input.split(/\r?\n/).find((l) => l.trim().length > 0) ?? "";
8
+ let s = firstLine
9
+ .replace(/```[\s\S]*?```/g, " ")
10
+ .replace(/`[^`]*`/g, " ")
11
+ .replace(/https?:\/\/\S+/gi, " ")
12
+ .replace(/^[#>\-*+\s]+/, "")
13
+ .replace(/\s+/g, " ")
14
+ .trim()
15
+ .replace(/[.,;:!?…]+$/u, "")
16
+ .trim();
17
+ if (!s) {
18
+ return t("session.default_name", { date: date ?? new Date().toISOString().slice(0, 10) });
19
+ }
20
+ if (s.length > MAX_NAME_LEN) {
21
+ const cut = s.slice(0, MAX_NAME_LEN);
22
+ const lastSpace = cut.lastIndexOf(" ");
23
+ s = `${(lastSpace > MIN_CUT ? cut.slice(0, lastSpace) : cut).trim()}…`;
24
+ }
25
+ return s;
26
+ }
27
+ /** Нормализовать заголовок от модели; мусор — null. */
28
+ export function sanitizeTitle(raw) {
29
+ const first = raw
30
+ .split(/\r?\n/)
31
+ .map((l) => l.trim())
32
+ .find((l) => l.length > 0) ?? "";
33
+ const cleaned = first.replace(/^["'«»`]+|["'«»`.!,;:]+$/gu, "").trim();
34
+ if (!cleaned)
35
+ return null;
36
+ return cleaned.length > MAX_NAME_LEN ? `${cleaned.slice(0, MAX_NAME_LEN).trim()}…` : cleaned;
37
+ }
38
+ /** Собрать промпт заголовка из отрывка сообщения (первые 1000 символов). */
39
+ export function buildTitlePrompt(input) {
40
+ const excerpt = input.slice(0, 1000);
41
+ return [
42
+ "Give a short session title for the message below.",
43
+ "Rules: 3-6 words, same language as the message, no quotes, no emoji, no trailing punctuation.",
44
+ "Reply with the title only.",
45
+ "",
46
+ excerpt,
47
+ ].join("\n");
48
+ }
49
+ /** Разовый LLM-заголовок с таймаутом. Fail-open — любая ошибка даёт null. */
50
+ export async function generateSessionTitle(input, chat, timeoutMs = 8000) {
51
+ const controller = new AbortController();
52
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
53
+ try {
54
+ const raw = await chat(buildTitlePrompt(input), controller.signal);
55
+ return sanitizeTitle(raw);
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ finally {
61
+ clearTimeout(timer);
62
+ }
63
+ }
@@ -0,0 +1,98 @@
1
+ import { getMessageText } from "../../llm/provider";
2
+ import { redactContextSecrets } from "../../core/agent/context-log";
3
+ function roleHeading(role) {
4
+ switch (role) {
5
+ case "user":
6
+ return "User";
7
+ case "assistant":
8
+ return "Assistant";
9
+ case "tool":
10
+ return "Tool";
11
+ case "system":
12
+ return "System";
13
+ default:
14
+ return role;
15
+ }
16
+ }
17
+ /** Отрендерить сессию в Markdown. */
18
+ export function renderSessionMarkdown(meta, messages, opts) {
19
+ const lines = [];
20
+ const name = meta?.name ?? "session";
21
+ const id = meta?.id ?? "unknown";
22
+ lines.push(`# ${name} (${id})`);
23
+ lines.push("");
24
+ if (meta) {
25
+ lines.push(`- Model: ${meta.model}`);
26
+ lines.push(`- Created: ${meta.createdAt}`);
27
+ lines.push(`- Updated: ${meta.updatedAt}`);
28
+ lines.push(`- Messages: ${meta.messageCount}`);
29
+ if (meta.projectDir)
30
+ lines.push(`- Project: ${meta.projectDir}`);
31
+ }
32
+ lines.push("");
33
+ lines.push("---");
34
+ lines.push("");
35
+ for (const msg of messages) {
36
+ appendMessageLines(lines, msg, opts);
37
+ }
38
+ return lines.join("\n");
39
+ }
40
+ function appendMessageLines(lines, msg, opts) {
41
+ // Compact режет тела tool-результатов (основной объём длинных сессий);
42
+ // однострочники `- tool_call:` на ходах ассистента показывают, что запускалось.
43
+ if (opts?.compact && msg.role === "tool")
44
+ return;
45
+ const heading = roleHeading(msg.role);
46
+ const nameSuffix = msg.name ? ` (${msg.name})` : "";
47
+ lines.push(`## ${heading}${nameSuffix}`);
48
+ lines.push("");
49
+ const text = getMessageText(msg.content);
50
+ lines.push(text.length > 0 ? text : "_(empty)_");
51
+ lines.push("");
52
+ if (Array.isArray(msg.tool_calls)) {
53
+ for (const tc of msg.tool_calls) {
54
+ lines.push(`- tool_call: \`${tc.function.name}\` ${tc.function.arguments}`);
55
+ }
56
+ lines.push("");
57
+ }
58
+ }
59
+ /** Собрать JSON-экспорт сессии. */
60
+ export function buildSessionJson(meta, messages, opts) {
61
+ const list = opts?.compact ? messages.filter((m) => m.role !== "tool") : messages;
62
+ return JSON.stringify({ meta, messages: list }, null, 2);
63
+ }
64
+ /** Объёмные снапшот-записи, выкидываемые --compact (для чтения человеком). */
65
+ const COMPACT_JSONL_DROP = new Set(["context", "tool_defs", "tool_result", "llm_usage"]);
66
+ /** Собрать JSONL-экспорт из журнала. */
67
+ export function buildSessionJsonl(log, opts) {
68
+ const list = opts?.compact ? log.filter((e) => !COMPACT_JSONL_DROP.has(e.type)) : log;
69
+ return list.map((e) => JSON.stringify(e)).join("\n");
70
+ }
71
+ /** Отформатировать пакет в заданном формате. */
72
+ export function formatExport(bundle, format, opts) {
73
+ switch (format) {
74
+ case "md":
75
+ return renderSessionMarkdown(bundle.meta, bundle.messages, opts);
76
+ case "json":
77
+ return buildSessionJson(bundle.meta, bundle.messages, opts);
78
+ case "jsonl":
79
+ return buildSessionJsonl(bundle.log, opts);
80
+ }
81
+ }
82
+ /** Вычистить секреты из экспорта, если не отключено. */
83
+ export function redactExport(text, redact) {
84
+ return redact ? redactContextSecrets(text) : text;
85
+ }
86
+ /** Санитизировать имя сессии под имя файла. */
87
+ export function sanitizeFileName(name) {
88
+ const cleaned = name
89
+ .replace(/[<>:"/\\|?*\u0000-\u001f]/g, "-")
90
+ .replace(/\s+/g, "-")
91
+ .replace(/-+/g, "-")
92
+ .replace(/^[-.]+|[-.]+$/g, "");
93
+ return cleaned.length > 0 ? cleaned.slice(0, 60) : "session";
94
+ }
95
+ /** Расширение файла под формат. */
96
+ export function extensionFor(format) {
97
+ return format === "jsonl" ? "jsonl" : format;
98
+ }
@@ -1,3 +1,4 @@
1
+ // Публичный фасад модуля сессий: стор, менеджер, модуль, типы.
1
2
  export { SessionStore } from "./store";
2
3
  export { SessionManager } from "./manager";
3
4
  export { SessionModule } from "./module";
@@ -1,34 +1,31 @@
1
1
  import { t } from "../../i18n/index";
2
+ import { getMessageText } from "../../llm/provider";
2
3
  import { createSessionContext, DEFAULT_SESSION_ISOLATION, } from "../../modules/security/session-isolation";
4
+ /** Менеджер: активная сессия, контексты изоляции, maxSessions, prune пустых. */
3
5
  export class SessionManager {
4
6
  store;
5
7
  activeId = null;
6
8
  options;
7
- /**
8
- * Map of session ID to session context (for isolation)
9
- */
9
+ /** Контексты сессий (для изоляции) по id */
10
10
  sessionContexts = new Map();
11
11
  constructor(store, options) {
12
12
  this.store = store;
13
13
  this.options = options;
14
- // Initialize encryption if configured
14
+ // Шифрование — если настроено
15
15
  if (options.encryption) {
16
16
  this.store.updateEncryption(options.encryption);
17
17
  }
18
18
  }
19
- /**
20
- * Check if session file encryption is enabled
21
- */
19
+ /** Включено ли шифрование файлов сессий. */
22
20
  isEncryptionEnabled() {
23
21
  return this.store.isEncryptionEnabled();
24
22
  }
25
- /**
26
- * Update encryption configuration
27
- */
23
+ /** Обновить конфиг шифрования. */
28
24
  updateEncryption(config) {
29
25
  this.store.updateEncryption(config);
30
26
  this.options.encryption = config;
31
27
  }
28
+ /** Создать сессию (имя/оверрайды опциональны), сделать активной. */
32
29
  create(name, securityOverrides) {
33
30
  const now = new Date().toISOString();
34
31
  const id = `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
@@ -36,6 +33,7 @@ export class SessionManager {
36
33
  const meta = {
37
34
  id,
38
35
  name: sessionName,
36
+ nameSource: name ? "manual" : "default",
39
37
  createdAt: now,
40
38
  updatedAt: now,
41
39
  messageCount: 0,
@@ -47,22 +45,31 @@ export class SessionManager {
47
45
  this.store.saveMeta(id, meta);
48
46
  this.activeId = id;
49
47
  this.enforceMaxSessions();
50
- // Create session context
48
+ try {
49
+ this.pruneEmpty(id);
50
+ }
51
+ catch {
52
+ // fail-open: автопрунинг не должен ломать создание сессии
53
+ }
54
+ // Контекст сессии
51
55
  const context = createSessionContext(id, this.options.projectDir, this.options.isolation, this.normalizeSecurityOverrides(securityOverrides));
52
56
  this.sessionContexts.set(id, context);
53
57
  return meta;
54
58
  }
59
+ /** Список сессий. */
55
60
  list() {
56
61
  return this.store.listSessions();
57
62
  }
63
+ /** Мета по id (null — нет). */
58
64
  get(id) {
59
65
  return this.store.loadMeta(id);
60
66
  }
67
+ /** Удалить сессию (+ контекст, сброс активной). */
61
68
  delete(id) {
62
- // Clean up session context
69
+ // Чистим контекст сессии
63
70
  const context = this.sessionContexts.get(id);
64
71
  if (context) {
65
- // In a real implementation, we would clean up temp files here
72
+ // Реально temp-файлы тут не чистятся (нет реализации)
66
73
  this.sessionContexts.delete(id);
67
74
  }
68
75
  this.store.deleteSession(id);
@@ -70,38 +77,79 @@ export class SessionManager {
70
77
  this.activeId = null;
71
78
  }
72
79
  }
73
- rename(id, name) {
80
+ /** Переименовать сессию (источник имени: auto/manual). */
81
+ rename(id, name, source = "manual") {
74
82
  const meta = this.store.loadMeta(id);
75
83
  if (!meta)
76
84
  throw new Error(t("session.not_found", { id }));
77
85
  meta.name = name;
86
+ meta.nameSource = source;
78
87
  this.store.saveMeta(id, meta);
79
88
  }
89
+ /** Пустая ли сессия (нет user-сообщения с текстом). Fail-open: ошибки чтения — непустая. */
90
+ isEmptySession(id) {
91
+ const meta = this.store.loadMeta(id);
92
+ if (!meta)
93
+ return false;
94
+ if ((meta.messageCount ?? 0) === 0)
95
+ return true;
96
+ let history;
97
+ try {
98
+ history = this.getHistory(id);
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ return !history.some((m) => m.role === "user" && getMessageText(m.content).trim().length > 0);
104
+ }
105
+ /** Удалить все пустые сессии, кроме `excludeId`. Вернуть удалённые id. Не бросает. */
106
+ pruneEmpty(excludeId) {
107
+ const deleted = [];
108
+ for (const s of this.store.listSessions()) {
109
+ if (s.id === excludeId)
110
+ continue;
111
+ try {
112
+ if (this.isEmptySession(s.id)) {
113
+ this.delete(s.id);
114
+ deleted.push(s.id);
115
+ }
116
+ }
117
+ catch {
118
+ // fail-open: нечитаемые пропускаем
119
+ }
120
+ }
121
+ return deleted;
122
+ }
123
+ /** Id активной сессии (null — нет). */
80
124
  getActive() {
81
125
  return this.activeId;
82
126
  }
127
+ /** Мета активной сессии (null — нет). */
83
128
  getActiveMeta() {
84
129
  if (!this.activeId)
85
130
  return null;
86
131
  return this.store.loadMeta(this.activeId);
87
132
  }
133
+ /** Сделать сессию активной (нет такой — ошибка). */
88
134
  setActive(id) {
89
135
  if (!this.store.sessionExists(id)) {
90
136
  throw new Error(t("session.not_found", { id }));
91
137
  }
92
138
  this.activeId = id;
93
139
  }
140
+ /** Добавить сообщение в активную сессию (нет активной — ошибка). */
94
141
  appendMessage(msg) {
95
142
  if (!this.activeId)
96
143
  throw new Error(t("session.no_active"));
97
144
  this.store.appendMessage(this.activeId, msg);
98
145
  }
146
+ /** Добавить запись в лог активной сессии (без активной — молча пропуск). */
99
147
  appendLog(entry) {
100
148
  if (!this.activeId)
101
149
  return;
102
150
  this.store.appendSessionLog(this.activeId, entry);
103
151
  }
104
- /** Read the full session.jsonl of any session (used by `session show`). */
152
+ /** Прочитать полный session.jsonl любой сессии (для `session show`). */
105
153
  loadSessionLog(id) {
106
154
  try {
107
155
  return this.store.loadSessionLog(id);
@@ -110,25 +158,34 @@ export class SessionManager {
110
158
  return [];
111
159
  }
112
160
  }
161
+ /** История активной сессии ([] — нет активной). */
113
162
  loadHistory() {
114
163
  if (!this.activeId)
115
164
  return [];
116
165
  return this.store.loadHistory(this.activeId);
117
166
  }
118
167
  /**
119
- * Get the session context for the active session
168
+ * История любой сессии, включая ротированные (фолбэк на архив
169
+ * `<id>.jsonl.gz`, если живого history-файла нет). Для
170
+ * `session export` / `/export`.
120
171
  */
172
+ getHistory(id) {
173
+ const live = this.store.loadHistory(id);
174
+ if (live.length > 0)
175
+ return live;
176
+ return this.store.loadRotatedHistory(id);
177
+ }
178
+ /** Контекст активной сессии (для изоляции). */
121
179
  getSessionContext() {
122
180
  if (!this.activeId)
123
181
  return undefined;
124
182
  return this.getSessionContextById(this.activeId);
125
183
  }
126
- /**
127
- * Get the session context for a specific session
128
- */
184
+ /** Каталог указанной сессии. */
129
185
  getSessionDirectory(id) {
130
186
  return this.store.getSessionDir(id);
131
187
  }
188
+ /** Контекст сессии по id (лениво создаётся из меты). */
132
189
  getSessionContextById(sessionId) {
133
190
  if (!this.sessionContexts.has(sessionId)) {
134
191
  const meta = this.store.loadMeta(sessionId);
@@ -139,21 +196,15 @@ export class SessionManager {
139
196
  }
140
197
  return this.sessionContexts.get(sessionId);
141
198
  }
142
- /**
143
- * Get session isolation configuration
144
- */
199
+ /** Конфиг изоляции сессий. */
145
200
  getIsolationConfig() {
146
201
  return this.options.isolation ?? DEFAULT_SESSION_ISOLATION;
147
202
  }
148
- /**
149
- * Check if session isolation is enabled
150
- */
203
+ /** Включена ли изоляция сессий. */
151
204
  isIsolationEnabled() {
152
205
  return this.getIsolationConfig().enabled;
153
206
  }
154
- /**
155
- * Normalize security overrides to match SecurityConfig structure
156
- */
207
+ /** Нормализовать оверрайды под структуру SecurityConfig. */
157
208
  normalizeSecurityOverrides(overrides) {
158
209
  if (!overrides)
159
210
  return undefined;
@@ -1,4 +1,5 @@
1
1
  import { t } from "../../i18n/index";
2
+ /** Модуль сессии: системный блок со стабильными полями + пустой плагин. */
2
3
  export class SessionModule {
3
4
  name = "session";
4
5
  manager;
@@ -9,9 +10,9 @@ export class SessionModule {
9
10
  const meta = this.manager.getActiveMeta();
10
11
  if (!meta)
11
12
  return null;
12
- // Stable per session: id/name/model/contextWindow do not change between
13
- // turns, so the system prompt is not mutated (KV-cache, rule #10). Live
14
- // values (message count) are exposed through the session_info tool.
13
+ // Стабильно на сессию: id/имя/модель/окно между ходами не меняются,
14
+ // так что системный промпт не мутирует (KV-кеш). Живое (счётчик сообщений)
15
+ // отдаётся через тул session_info.
15
16
  return {
16
17
  content: t("session.info_full", {
17
18
  name: meta.name,
@@ -1,11 +1,13 @@
1
+ // Хранилище сессий: meta/history/session-лог на диске, шифрование, ротация в .gz.
1
2
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync, appendFileSync, } from "fs";
2
3
  import { join } from "path";
3
- import { gzipSync } from "zlib";
4
+ import { gzipSync, gunzipSync } from "zlib";
4
5
  import { SessionFileEncryptor } from "../security/session-encryption";
6
+ /** Файловое хранилище сессий: meta.json, history.jsonl, session.jsonl. */
5
7
  export class SessionStore {
6
8
  baseDir;
7
9
  encryptor = null;
8
- // In-memory meta cache to avoid O(N²) disk reads on appendMessage.
10
+ // In-memory кеш меты — иначе O(N²) чтений диска на appendMessage.
9
11
  _metaCache = new Map();
10
12
  constructor(baseDir, encryptionConfig) {
11
13
  this.baseDir = baseDir;
@@ -13,12 +15,11 @@ export class SessionStore {
13
15
  this.encryptor = new SessionFileEncryptor(encryptionConfig);
14
16
  }
15
17
  }
18
+ /** Каталог сессии по id. */
16
19
  getSessionDir(id) {
17
20
  return join(this.baseDir, id);
18
21
  }
19
- /**
20
- * Update encryption configuration
21
- */
22
+ /** Обновить конфиг шифрования. */
22
23
  updateEncryption(config) {
23
24
  if (config?.enabled) {
24
25
  this.encryptor = new SessionFileEncryptor(config);
@@ -27,12 +28,11 @@ export class SessionStore {
27
28
  this.encryptor = null;
28
29
  }
29
30
  }
30
- /**
31
- * Check if encryption is enabled
32
- */
31
+ /** Включено ли шифрование. */
33
32
  isEncryptionEnabled() {
34
33
  return this.encryptor?.isEnabled() ?? false;
35
34
  }
35
+ /** Создать базовый каталог (0700). */
36
36
  init() {
37
37
  mkdirSync(this.baseDir, { recursive: true, mode: 0o700 });
38
38
  }
@@ -48,9 +48,11 @@ export class SessionStore {
48
48
  sessionLogPath(id) {
49
49
  return join(this.sessionDir(id), "session.jsonl");
50
50
  }
51
+ /** Есть ли сессия (по meta.json). */
51
52
  sessionExists(id) {
52
53
  return existsSync(this.metaPath(id));
53
54
  }
55
+ /** Сохранить мету (кеш + диск 0600, опционально шифр). */
54
56
  saveMeta(id, meta) {
55
57
  this._metaCache.set(id, meta);
56
58
  const dir = this.sessionDir(id);
@@ -66,6 +68,7 @@ export class SessionStore {
66
68
  writeFileSync(this.metaPath(id), content, { encoding: "utf-8", mode: 0o600 });
67
69
  }
68
70
  }
71
+ /** Загрузить мету (кеш, иначе диск); нет/битая — null. */
69
72
  loadMeta(id) {
70
73
  const cached = this._metaCache.get(id);
71
74
  if (cached)
@@ -84,8 +87,8 @@ export class SessionStore {
84
87
  return null;
85
88
  }
86
89
  }
87
- /** Read meta straight from disk, bypassing _metaCache — used where counter
88
- * accuracy matters more than read speed (see appendMessage). */
90
+ /** Мета строго с диска, мимо _metaCache — там, где точность счётчика
91
+ * важнее скорости чтения (см. appendMessage). */
89
92
  readMetaFromDisk(id) {
90
93
  const path = this.metaPath(id);
91
94
  if (!existsSync(path))
@@ -99,23 +102,24 @@ export class SessionStore {
99
102
  return null;
100
103
  }
101
104
  }
105
+ /** Добавить сообщение в историю; мету инкрементить с диска (защита от гонки процессов). */
102
106
  appendMessage(id, msg) {
103
107
  const dir = this.sessionDir(id);
104
108
  mkdirSync(dir, { recursive: true, mode: 0o700 });
105
109
  const line = JSON.stringify(msg);
106
110
  if (this.encryptor?.isEnabled()) {
107
- appendFileSync(this.historyPath(id), this.encryptor.encryptFileContent(line) + "\n", {
111
+ appendFileSync(this.historyPath(id), `${this.encryptor.encryptFileContent(line)}\n`, {
108
112
  encoding: "utf-8",
109
113
  mode: 0o600,
110
114
  });
111
115
  }
112
116
  else {
113
- appendFileSync(this.historyPath(id), line + "\n", { encoding: "utf-8", mode: 0o600 });
117
+ appendFileSync(this.historyPath(id), `${line}\n`, { encoding: "utf-8", mode: 0o600 });
114
118
  }
115
- // Re-read meta from disk (not the cache) right before the increment so
116
- // counters appended by ANOTHER process are not clobbered by a stale
117
- // in-memory snapshot (last-write-wins). Full cross-process locking is out
118
- // of scope; this narrows the race window.
119
+ // Мету перечитываем с диска прямо перед инкрементом, чтобы счётчики
120
+ // ДРУГОГО процесса не затирались протухшим in-memory снапшотом
121
+ // (last-write-wins). Полноценные кросс-процессные локи вне скоупа;
122
+ // это сужает окно гонки.
119
123
  const meta = this.readMetaFromDisk(id) ?? this._metaCache.get(id) ?? null;
120
124
  if (meta) {
121
125
  meta.messageCount = (meta.messageCount || 0) + 1;
@@ -123,6 +127,7 @@ export class SessionStore {
123
127
  this.saveMeta(id, meta);
124
128
  }
125
129
  }
130
+ /** Загрузить историю (битые строки пропускаются); нет файла — []. */
126
131
  loadHistory(id) {
127
132
  const path = this.historyPath(id);
128
133
  if (!existsSync(path))
@@ -158,20 +163,49 @@ export class SessionStore {
158
163
  return [];
159
164
  }
160
165
  }
166
+ /**
167
+ * История ротированной сессии из `<baseDir>/<id>.jsonl.gz`.
168
+ * Нет gz или не читается — [].
169
+ */
170
+ loadRotatedHistory(id) {
171
+ const gzPath = join(this.baseDir, `${id}.jsonl.gz`);
172
+ if (!existsSync(gzPath))
173
+ return [];
174
+ try {
175
+ const content = gunzipSync(readFileSync(gzPath)).toString("utf-8");
176
+ return content
177
+ .split("\n")
178
+ .filter(Boolean)
179
+ .map((line) => {
180
+ try {
181
+ return JSON.parse(line);
182
+ }
183
+ catch {
184
+ return null;
185
+ }
186
+ })
187
+ .filter((m) => m !== null);
188
+ }
189
+ catch {
190
+ return [];
191
+ }
192
+ }
193
+ /** Добавить запись в session-лог. */
161
194
  appendSessionLog(id, entry) {
162
195
  const dir = this.sessionDir(id);
163
196
  mkdirSync(dir, { recursive: true, mode: 0o700 });
164
197
  const line = JSON.stringify(entry);
165
198
  if (this.encryptor?.isEnabled()) {
166
- appendFileSync(this.sessionLogPath(id), this.encryptor.encryptFileContent(line) + "\n", {
199
+ appendFileSync(this.sessionLogPath(id), `${this.encryptor.encryptFileContent(line)}\n`, {
167
200
  encoding: "utf-8",
168
201
  mode: 0o600,
169
202
  });
170
203
  }
171
204
  else {
172
- appendFileSync(this.sessionLogPath(id), line + "\n", { encoding: "utf-8", mode: 0o600 });
205
+ appendFileSync(this.sessionLogPath(id), `${line}\n`, { encoding: "utf-8", mode: 0o600 });
173
206
  }
174
207
  }
208
+ /** Загрузить session-лог (битые строки пропускаются); нет файла — []. */
175
209
  loadSessionLog(id) {
176
210
  const path = this.sessionLogPath(id);
177
211
  if (!existsSync(path))
@@ -207,6 +241,7 @@ export class SessionStore {
207
241
  return [];
208
242
  }
209
243
  }
244
+ /** Список сессий по убыванию updatedAt. */
210
245
  listSessions() {
211
246
  if (!existsSync(this.baseDir))
212
247
  return [];
@@ -222,6 +257,7 @@ export class SessionStore {
222
257
  sessions.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
223
258
  return sessions;
224
259
  }
260
+ /** Удалить сессию (кеш + каталог). */
225
261
  deleteSession(id) {
226
262
  this._metaCache.delete(id);
227
263
  const dir = this.sessionDir(id);
@@ -229,6 +265,7 @@ export class SessionStore {
229
265
  rmSync(dir, { recursive: true, force: true });
230
266
  }
231
267
  }
268
+ /** Ротация: истории старше 30 дней — в .jsonl.gz, мета помечается rotated. */
232
269
  rotateOldSessions() {
233
270
  const thirtyDaysAgo = new Date();
234
271
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
@@ -243,8 +280,8 @@ export class SessionStore {
243
280
  const gzPath = join(this.baseDir, `${session.id}.jsonl.gz`);
244
281
  writeFileSync(gzPath, compressed);
245
282
  rmSync(historyPath);
246
- // Mark the meta so the session stops looking "live" in lists —
247
- // otherwise a gzipped session still advertises messageCount > 0.
283
+ // Пометить мету, чтобы сессия не выглядела «живой» в списках —
284
+ // иначе gz-сессия рекламирует messageCount > 0.
248
285
  const meta = this.loadMeta(session.id);
249
286
  if (meta) {
250
287
  meta.messageCount = 0;
@@ -0,0 +1,35 @@
1
+ // Модель данных first-run визарда: ответы, языки, дефолты, провайдеры для UI.
2
+ /**
3
+ * Общая модель данных first-run визарда.
4
+ * Едят оба фронтенда (терминальный `src/cli/setup.ts` и браузерный визард
5
+ * `mma web`), чтобы форма и дефолты не расходились. Без терминальных/UI
6
+ * зависимостей — безопасно импортить с веб-сервера.
7
+ */
8
+ import { BUILTIN_PROVIDERS } from "../providers/presets";
9
+ /** Языки UI визарда (подписи — на своём языке). */
10
+ export const SETUP_LANGUAGES = [
11
+ { value: "en", label: "English" },
12
+ { value: "ru", label: "Русский" },
13
+ ];
14
+ /** Числовые дефолты шага настроек агента. */
15
+ export const SETUP_DEFAULTS = {
16
+ contextWindow: 32768,
17
+ maxToolIterations: 1000,
18
+ };
19
+ /** Статические display-данные, общие для веб-визарда и его тестов. */
20
+ export function buildSetupDisplay() {
21
+ const providers = BUILTIN_PROVIDERS.map((spec) => {
22
+ const option = { type: spec.type, label: spec.label };
23
+ if (spec.baseUrl)
24
+ option.baseUrl = spec.baseUrl;
25
+ return option;
26
+ });
27
+ return {
28
+ languages: SETUP_LANGUAGES.map((l) => ({ value: l.value, label: l.label })),
29
+ providers,
30
+ defaults: {
31
+ contextWindow: SETUP_DEFAULTS.contextWindow,
32
+ maxToolIterations: SETUP_DEFAULTS.maxToolIterations,
33
+ },
34
+ };
35
+ }