micro-models-agent 0.46.2 → 0.46.3

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 (218) hide show
  1. package/README.md +312 -312
  2. package/dist/main.js +711 -784
  3. package/dist/modules/browser/bridge-server.mjs +202 -202
  4. package/dist/skills/builtin/git.md +36 -36
  5. package/dist/skills/builtin/typescript.md +35 -35
  6. package/package.json +48 -48
  7. package/dist/cli/commands.js +0 -323
  8. package/dist/cli/completer.js +0 -167
  9. package/dist/cli/index.js +0 -2
  10. package/dist/cli/main.js +0 -165
  11. package/dist/cli/plugin-commands.js +0 -36
  12. package/dist/cli/repl-commands.js +0 -661
  13. package/dist/cli/repl.js +0 -616
  14. package/dist/cli/run-result.js +0 -22
  15. package/dist/cli/security-commands.js +0 -164
  16. package/dist/cli/setup.js +0 -231
  17. package/dist/config/config.js +0 -249
  18. package/dist/config/defaults.js +0 -124
  19. package/dist/config/experts.js +0 -15
  20. package/dist/config/index.js +0 -3
  21. package/dist/config/security.js +0 -193
  22. package/dist/config/types.js +0 -1
  23. package/dist/core/agent-moe.js +0 -102
  24. package/dist/core/agent.js +0 -886
  25. package/dist/core/bootstrap.js +0 -404
  26. package/dist/core/index.js +0 -2
  27. package/dist/core/prompt-builder.js +0 -76
  28. package/dist/core/session-logger.js +0 -197
  29. package/dist/core/types.js +0 -1
  30. package/dist/core/version.js +0 -24
  31. package/dist/core/workspace.js +0 -76
  32. package/dist/i18n/en.json +0 -598
  33. package/dist/i18n/index.js +0 -46
  34. package/dist/i18n/ru.json +0 -598
  35. package/dist/index.js +0 -22
  36. package/dist/llm/image-utils.js +0 -143
  37. package/dist/llm/index.js +0 -4
  38. package/dist/llm/model-loader.js +0 -78
  39. package/dist/llm/openai-compat.js +0 -359
  40. package/dist/llm/orchestrator.js +0 -198
  41. package/dist/llm/provider.js +0 -10
  42. package/dist/llm/response.js +0 -39
  43. package/dist/llm/token-counter.js +0 -39
  44. package/dist/llm/types.js +0 -1
  45. package/dist/logger/app-logger.js +0 -143
  46. package/dist/logger/file-log.js +0 -151
  47. package/dist/logger/index.js +0 -1
  48. package/dist/migration/backup.js +0 -45
  49. package/dist/migration/detect.js +0 -50
  50. package/dist/migration/index.js +0 -2
  51. package/dist/modules/artifacts/store.js +0 -61
  52. package/dist/modules/browser/actions.js +0 -76
  53. package/dist/modules/browser/bridge-client.js +0 -199
  54. package/dist/modules/browser/bridge-path.js +0 -10
  55. package/dist/modules/browser/cookie-store.js +0 -24
  56. package/dist/modules/browser/driver.js +0 -136
  57. package/dist/modules/browser/index.js +0 -7
  58. package/dist/modules/browser/module.js +0 -29
  59. package/dist/modules/browser/session.js +0 -338
  60. package/dist/modules/browser/snapshot.js +0 -148
  61. package/dist/modules/browser/types.js +0 -12
  62. package/dist/modules/certification/cli.js +0 -174
  63. package/dist/modules/certification/fact-checker.js +0 -82
  64. package/dist/modules/certification/loader.js +0 -105
  65. package/dist/modules/certification/manifest.js +0 -50
  66. package/dist/modules/certification/runner.js +0 -159
  67. package/dist/modules/certification/scenarios.js +0 -124
  68. package/dist/modules/certification/types.js +0 -1
  69. package/dist/modules/context/chunk-query.js +0 -100
  70. package/dist/modules/context/fact-extractor.js +0 -162
  71. package/dist/modules/context/history.js +0 -15
  72. package/dist/modules/context/index.js +0 -1
  73. package/dist/modules/context/manager.js +0 -423
  74. package/dist/modules/execution/audit-runners.js +0 -152
  75. package/dist/modules/execution/auditor.js +0 -218
  76. package/dist/modules/execution/execution-plugin.js +0 -272
  77. package/dist/modules/execution/index.js +0 -8
  78. package/dist/modules/execution/module.js +0 -436
  79. package/dist/modules/execution/moe-executor.js +0 -291
  80. package/dist/modules/execution/plan-coverage.js +0 -68
  81. package/dist/modules/execution/plan-persister.js +0 -46
  82. package/dist/modules/execution/plan-store.js +0 -157
  83. package/dist/modules/execution/plan-tool.js +0 -508
  84. package/dist/modules/execution/plan-validator.js +0 -153
  85. package/dist/modules/execution/planner.js +0 -90
  86. package/dist/modules/execution/stuck-detector.js +0 -510
  87. package/dist/modules/execution/tracker.js +0 -67
  88. package/dist/modules/execution/types.js +0 -1
  89. package/dist/modules/execution/verifier.js +0 -222
  90. package/dist/modules/execution/windows-commands.js +0 -41
  91. package/dist/modules/hallucination/confidence.js +0 -66
  92. package/dist/modules/hallucination/consistency.js +0 -26
  93. package/dist/modules/hallucination/detector.js +0 -43
  94. package/dist/modules/hallucination/factual.js +0 -129
  95. package/dist/modules/hallucination/index.js +0 -5
  96. package/dist/modules/hallucination/js-identifiers.js +0 -262
  97. package/dist/modules/hallucination/llm-judge.js +0 -101
  98. package/dist/modules/index.js +0 -5
  99. package/dist/modules/indexer/cache.js +0 -40
  100. package/dist/modules/indexer/index.js +0 -3
  101. package/dist/modules/indexer/module.js +0 -245
  102. package/dist/modules/indexer/project-profile.js +0 -183
  103. package/dist/modules/indexer/walker.js +0 -101
  104. package/dist/modules/lsp/check-tool.js +0 -58
  105. package/dist/modules/lsp/client.js +0 -278
  106. package/dist/modules/lsp/command.js +0 -60
  107. package/dist/modules/lsp/config.js +0 -135
  108. package/dist/modules/lsp/index.js +0 -3
  109. package/dist/modules/lsp/module.js +0 -232
  110. package/dist/modules/lsp/probe.js +0 -76
  111. package/dist/modules/lsp/project-root.js +0 -32
  112. package/dist/modules/lsp/startup-check.js +0 -141
  113. package/dist/modules/lsp/types.js +0 -1
  114. package/dist/modules/mcp/client.js +0 -399
  115. package/dist/modules/mcp/index.js +0 -3
  116. package/dist/modules/mcp/module.js +0 -142
  117. package/dist/modules/mcp/registry.js +0 -15
  118. package/dist/modules/memory/index.js +0 -1
  119. package/dist/modules/memory/module.js +0 -96
  120. package/dist/modules/memory/search.js +0 -42
  121. package/dist/modules/memory/store.js +0 -69
  122. package/dist/modules/pipelines/engine.js +0 -60
  123. package/dist/modules/pipelines/index.js +0 -3
  124. package/dist/modules/pipelines/parser.js +0 -56
  125. package/dist/modules/pipelines/template.js +0 -14
  126. package/dist/modules/plugins/builtin/lint-on-write.js +0 -231
  127. package/dist/modules/plugins/builtin/notify.js +0 -9
  128. package/dist/modules/plugins/index.js +0 -1
  129. package/dist/modules/plugins/loader.js +0 -70
  130. package/dist/modules/plugins/manager.js +0 -217
  131. package/dist/modules/plugins/types.js +0 -1
  132. package/dist/modules/processes/detect.js +0 -34
  133. package/dist/modules/processes/index.js +0 -2
  134. package/dist/modules/processes/registry.js +0 -327
  135. package/dist/modules/processes/runner.js +0 -23
  136. package/dist/modules/registry.js +0 -47
  137. package/dist/modules/security/audit-log.js +0 -136
  138. package/dist/modules/security/audit-notifier.js +0 -292
  139. package/dist/modules/security/command-validator.js +0 -205
  140. package/dist/modules/security/content-scanner.js +0 -53
  141. package/dist/modules/security/data-sanitizer.js +0 -89
  142. package/dist/modules/security/encryption.js +0 -242
  143. package/dist/modules/security/index.js +0 -14
  144. package/dist/modules/security/network-validator.js +0 -71
  145. package/dist/modules/security/path-validator.js +0 -207
  146. package/dist/modules/security/rate-limiter.js +0 -119
  147. package/dist/modules/security/security-policies.js +0 -531
  148. package/dist/modules/security/session-encryption.js +0 -210
  149. package/dist/modules/security/session-isolation.js +0 -95
  150. package/dist/modules/session/index.js +0 -3
  151. package/dist/modules/session/manager.js +0 -172
  152. package/dist/modules/session/module.js +0 -24
  153. package/dist/modules/session/store.js +0 -222
  154. package/dist/modules/session/types.js +0 -1
  155. package/dist/modules/skills/index.js +0 -2
  156. package/dist/modules/skills/loader.js +0 -72
  157. package/dist/modules/skills/matcher.js +0 -27
  158. package/dist/modules/skills/module.js +0 -129
  159. package/dist/modules/types.js +0 -1
  160. package/dist/modules/updater/checker.js +0 -96
  161. package/dist/modules/updater/index.js +0 -2
  162. package/dist/modules/updater/module.js +0 -116
  163. package/dist/modules/user-profile/compressor.js +0 -16
  164. package/dist/modules/user-profile/index.js +0 -1
  165. package/dist/modules/user-profile/profile.js +0 -68
  166. package/dist/tools/approve.js +0 -32
  167. package/dist/tools/attach-image.js +0 -89
  168. package/dist/tools/bash.js +0 -496
  169. package/dist/tools/browser.js +0 -114
  170. package/dist/tools/chunk-query.js +0 -99
  171. package/dist/tools/create-dir.js +0 -55
  172. package/dist/tools/delete-file.js +0 -62
  173. package/dist/tools/download-file.js +0 -116
  174. package/dist/tools/edit-file.js +0 -79
  175. package/dist/tools/enable-tools.js +0 -58
  176. package/dist/tools/executor.js +0 -144
  177. package/dist/tools/file-info.js +0 -46
  178. package/dist/tools/filter-tools.js +0 -17
  179. package/dist/tools/glob-tool.js +0 -26
  180. package/dist/tools/grep-tool.js +0 -84
  181. package/dist/tools/hidden-tools-block.js +0 -37
  182. package/dist/tools/index.js +0 -78
  183. package/dist/tools/list-dir.js +0 -48
  184. package/dist/tools/load-skill.js +0 -42
  185. package/dist/tools/mcp-call.js +0 -68
  186. package/dist/tools/move-file.js +0 -85
  187. package/dist/tools/path-utils.js +0 -51
  188. package/dist/tools/pipeline-run.js +0 -144
  189. package/dist/tools/preview.js +0 -2
  190. package/dist/tools/process-kill.js +0 -29
  191. package/dist/tools/process-list.js +0 -36
  192. package/dist/tools/process-log.js +0 -45
  193. package/dist/tools/question.js +0 -140
  194. package/dist/tools/read-file.js +0 -91
  195. package/dist/tools/recall.js +0 -117
  196. package/dist/tools/registry.js +0 -47
  197. package/dist/tools/remember.js +0 -67
  198. package/dist/tools/scope-check.js +0 -30
  199. package/dist/tools/search-history.js +0 -84
  200. package/dist/tools/subagent.js +0 -196
  201. package/dist/tools/types.js +0 -1
  202. package/dist/tools/user-input.js +0 -123
  203. package/dist/tools/web-browse.js +0 -86
  204. package/dist/tools/web-fetch.js +0 -98
  205. package/dist/tools/web-search.js +0 -78
  206. package/dist/tools/write-file.js +0 -81
  207. package/dist/ui/box.js +0 -77
  208. package/dist/ui/colors.js +0 -4
  209. package/dist/ui/diff.js +0 -178
  210. package/dist/ui/index.js +0 -6
  211. package/dist/ui/line-editor.js +0 -703
  212. package/dist/ui/line-math.js +0 -69
  213. package/dist/ui/md-formatter.js +0 -212
  214. package/dist/ui/output.js +0 -13
  215. package/dist/ui/plan-view.js +0 -103
  216. package/dist/ui/renderer.js +0 -209
  217. package/dist/ui/spinner.js +0 -70
  218. package/dist/ui/table.js +0 -144
package/dist/main.js CHANGED
@@ -2263,9 +2263,7 @@ var init_defaults = __esm(() => {
2263
2263
  retry: {
2264
2264
  maxRetries: 3,
2265
2265
  baseDelay: 1000,
2266
- maxDelay: 30000,
2267
- maxStreamRetries: 2,
2268
- noDataTimeoutMs: 60000
2266
+ maxDelay: 30000
2269
2267
  },
2270
2268
  maxToolIterations: 1000,
2271
2269
  stuckThreshold: 6,
@@ -2398,14 +2396,6 @@ Use read_file on {path} to see the current content before editing — the target
2398
2396
  "error.llm_retries": "LLM request failed after retries",
2399
2397
  "error.llm_429": "Rate limit exceeded (HTTP 429) for {model} on {baseUrl}. The provider is throttling requests — free models are especially strict. Get an API key or switch to a paid/faster model: {baseUrl}",
2400
2398
  "error.no_response_body": "No response body stream",
2401
- "error.llm_stream_idle": "LLM stream stalled — no data for {timeout}ms",
2402
- "error.llm_timeout": "LLM request timed out ({timeout}ms)",
2403
- "env.runtime_node": "Running under Node (v{version}) — clipboard image paste, subagent performance and LSP spawn on Windows degrade. Install Bun (https://bun.sh) for full features.",
2404
- "env.runtime_old": "Runtime version {version} is below the required engines {engine}.",
2405
- "env.tool_missing": "Tool not found on PATH: {tool}",
2406
- "env.playwright_missing": "Playwright package is not installed — the browser tool will fail. Install with: bun add playwright",
2407
- "env.playwright_browsers_missing": "Playwright browsers not downloaded ({dir}). The browser tool will fail. Install with: bunx playwright install chromium",
2408
- "env.crash_stderr": "MMA crashed ({type}): {message} — crash report written to ~/.mma/logs/crash.jsonl",
2409
2399
  "session.started": "Session started: {id}",
2410
2400
  "session.ended": "Session ended: {id}",
2411
2401
  "session.not_found": "Session not found: {id}",
@@ -2789,19 +2779,6 @@ Available commands:`,
2789
2779
  "repl.lsp_timeout": "timeout",
2790
2780
  "repl.lsp_failed": "did not start",
2791
2781
  "repl.lsp_unknown": "unknown",
2792
- "repl.lsp": "LSP server management (status, restart, check)",
2793
- "repl.lsp_usage": "Usage: /lsp [status|restart|check <path>]",
2794
- "repl.lsp_not_available": "LSP module not available",
2795
- "repl.lsp_status_header": "LSP Status:",
2796
- "repl.lsp_enabled": "Enabled:",
2797
- "repl.lsp_disabled_servers": "Disabled servers:",
2798
- "repl.lsp_failure_counts": "Failure counts:",
2799
- "repl.lsp_all_ok": "All servers operational",
2800
- "repl.lsp_restarting": "Resetting LSP server state...",
2801
- "repl.lsp_restarted": "LSP servers reset. Failed servers will be retried on next use.",
2802
- "repl.lsp_check_usage": "Usage: /lsp check <file-or-directory>",
2803
- "repl.lsp_checking": "Running LSP check on {path}...",
2804
- "repl.lsp_check_error": "LSP check failed: {error}",
2805
2782
  "repl.work_dir": "Dir:",
2806
2783
  "repl.agents_label": "Instructions:",
2807
2784
  "repl.not_found": "not found",
@@ -3029,6 +3006,10 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
3029
3006
  "tools.hidden_header": "Additional tools (enable on demand via enable_tools or route to subagent tool_tags):",
3030
3007
  "tool.friendly.enable_tools": "Enable tools",
3031
3008
  "cli.provider_base_hint": "Base URL set to: {baseUrl}",
3009
+ "cli.add_provider": "Add a provider to the config",
3010
+ "cli.provider_no_url": 'No base URL for provider "{name}". Pass --url.',
3011
+ "cli.provider_added": "Provider added: {name}",
3012
+ "cli.provider_switch_hint": "Switch to it with: mma provider use <name>",
3032
3013
  "repl.cost": "Total cost: {cost}",
3033
3014
  "repl.tokens": "Tokens used: {tokens}"
3034
3015
  };
@@ -3082,14 +3063,6 @@ var init_ru = __esm(() => {
3082
3063
  "error.llm_retries": "Запрос LLM не удался после повторов",
3083
3064
  "error.llm_429": "Превышен лимит запросов (HTTP 429) для {model} на {baseUrl}. Провайдер ограничивает трафик — особенно строгие free-модели. Получите API-ключ или переключитесь на платную/быструю модель: {baseUrl}",
3084
3065
  "error.no_response_body": "Нет потока тела ответа",
3085
- "error.llm_stream_idle": "Поток LLM завис — нет данных {timeout}мс",
3086
- "error.llm_timeout": "Время запроса LLM истекло ({timeout}мс)",
3087
- "env.runtime_node": "Запущено под Node (v{version}) — вставка изображений из буфера и LSP на Windows работают урезанно. Установите Bun (https://bun.sh) для полного функционала.",
3088
- "env.runtime_old": "Версия рантайма {version} ниже требуемой engines {engine}.",
3089
- "env.tool_missing": "Инструмент не найден в PATH: {tool}",
3090
- "env.playwright_missing": "Пакет Playwright не установлен — браузерный инструмент не будет работать. Установите: bun add playwright",
3091
- "env.playwright_browsers_missing": "Браузеры Playwright не скачаны ({dir}). Браузерный инструмент не будет работать. Установите: bunx playwright install chromium",
3092
- "env.crash_stderr": "MMA упал ({type}): {message} — отчёт о падении записан в ~/.mma/logs/crash.jsonl",
3093
3066
  "session.started": "Сессия начата: {id}",
3094
3067
  "session.ended": "Сессия завершена: {id}",
3095
3068
  "session.not_found": "Сессия не найдена: {id}",
@@ -3468,19 +3441,6 @@ var init_ru = __esm(() => {
3468
3441
  "repl.lsp_timeout": "таймаут",
3469
3442
  "repl.lsp_failed": "не стартовал",
3470
3443
  "repl.lsp_unknown": "неизвестно",
3471
- "repl.lsp": "Управление LSP-серверами (status, restart, check)",
3472
- "repl.lsp_usage": "Использование: /lsp [status|restart|check <путь>]",
3473
- "repl.lsp_not_available": "Модуль LSP недоступен",
3474
- "repl.lsp_status_header": "Статус LSP:",
3475
- "repl.lsp_enabled": "Включён:",
3476
- "repl.lsp_disabled_servers": "Отключённые серверы:",
3477
- "repl.lsp_failure_counts": "Количество ошибок:",
3478
- "repl.lsp_all_ok": "Все серверы работают",
3479
- "repl.lsp_restarting": "Сброс состояния LSP-серверов...",
3480
- "repl.lsp_restarted": "LSP-серверы сброшены. Упавшие серверы будут повторно запущены при следующем использовании.",
3481
- "repl.lsp_check_usage": "Использование: /lsp check <файл-или-каталог>",
3482
- "repl.lsp_checking": "Запуск LSP-проверки {path}...",
3483
- "repl.lsp_check_error": "LSP-проверка не удалась: {error}",
3484
3444
  "repl.work_dir": "Директория:",
3485
3445
  "repl.agents_label": "Инструкции:",
3486
3446
  "repl.not_found": "не найден",
@@ -3714,6 +3674,10 @@ var init_ru = __esm(() => {
3714
3674
  "tools.hidden_header": "Дополнительные тулы (включите по требованию через enable_tools или маршрутизируйте через subagent tool_tags):",
3715
3675
  "tool.friendly.enable_tools": "Включить тулы",
3716
3676
  "cli.provider_base_hint": "Базовый URL установлен: {baseUrl}",
3677
+ "cli.add_provider": "Добавить провайдера в конфиг",
3678
+ "cli.provider_no_url": 'Нет базового URL для провайдера "{name}". Укажите --url.',
3679
+ "cli.provider_added": "Провайдер добавлен: {name}",
3680
+ "cli.provider_switch_hint": "Переключитесь на него: mma provider use <имя>",
3717
3681
  "repl.cost": "Итого потрачено: {cost}",
3718
3682
  "repl.tokens": "Потрачено токенов: {tokens}"
3719
3683
  };
@@ -4640,20 +4604,6 @@ class Logger {
4640
4604
  }
4641
4605
  return sanitized;
4642
4606
  }
4643
- logStructured(type, data) {
4644
- const logTarget = this.sessionDir ?? this.logDir;
4645
- if (!logTarget)
4646
- return;
4647
- try {
4648
- appendFileSync2(join6(logTarget, "app.jsonl"), JSON.stringify({
4649
- level: "info",
4650
- ts: new Date().toISOString(),
4651
- type,
4652
- meta: this.sanitizeMeta(data)
4653
- }) + `
4654
- `, "utf-8");
4655
- } catch {}
4656
- }
4657
4607
  }
4658
4608
  var import_picocolors, LEVELS, LEVEL_COLORS;
4659
4609
  var init_app_logger = __esm(() => {
@@ -4669,6 +4619,26 @@ var init_app_logger = __esm(() => {
4669
4619
  };
4670
4620
  });
4671
4621
 
4622
+ // src/modules/providers/registry.ts
4623
+ class ProviderRegistry {
4624
+ specs = new Map;
4625
+ register(spec) {
4626
+ this.specs.set(spec.type, spec);
4627
+ }
4628
+ get(type) {
4629
+ return this.specs.get(type);
4630
+ }
4631
+ has(type) {
4632
+ return this.specs.has(type);
4633
+ }
4634
+ list() {
4635
+ return [...this.specs.values()];
4636
+ }
4637
+ types() {
4638
+ return [...this.specs.keys()];
4639
+ }
4640
+ }
4641
+
4672
4642
  // node_modules/base64-js/index.js
4673
4643
  var require_base64_js = __commonJS((exports) => {
4674
4644
  exports.byteLength = byteLength;
@@ -5230,9 +5200,7 @@ class OpenAICompatProvider {
5230
5200
  this.retryConfig = config.retry ?? {
5231
5201
  maxRetries: 3,
5232
5202
  baseDelay: 1000,
5233
- maxDelay: 30000,
5234
- maxStreamRetries: 2,
5235
- noDataTimeoutMs: 60000
5203
+ maxDelay: 30000
5236
5204
  };
5237
5205
  this.rateLimiter = createRateLimiter(config.rateLimits);
5238
5206
  }
@@ -5263,32 +5231,6 @@ class OpenAICompatProvider {
5263
5231
  }
5264
5232
  }
5265
5233
  async* doStream(messages, tools, signal, options) {
5266
- const { baseDelay, maxDelay, maxStreamRetries, noDataTimeoutMs } = this.retryConfig;
5267
- const streamRetries = maxStreamRetries ?? 2;
5268
- const idleTimeoutMs = noDataTimeoutMs ?? 60000;
5269
- for (let attempt = 0;; attempt++) {
5270
- let emitted = false;
5271
- const onEmit = () => {
5272
- emitted = true;
5273
- };
5274
- try {
5275
- const sawDone = yield* this.streamOnce(messages, tools, signal, options, onEmit, idleTimeoutMs);
5276
- if (sawDone || emitted)
5277
- return;
5278
- if (attempt >= streamRetries)
5279
- return;
5280
- } catch (err) {
5281
- if (err?.name === "AbortError" || err?.llmTerminal || signal?.aborted)
5282
- throw err;
5283
- if (emitted || attempt >= streamRetries)
5284
- throw err;
5285
- }
5286
- const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
5287
- const jitter = Math.random() * baseDelay * 0.1;
5288
- await this.sleep(delay + jitter, signal);
5289
- }
5290
- }
5291
- async* streamOnce(messages, tools, signal, options, onEmit, idleTimeoutMs) {
5292
5234
  const body = buildRequestBody({
5293
5235
  model: this.model,
5294
5236
  messages,
@@ -5304,11 +5246,8 @@ class OpenAICompatProvider {
5304
5246
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
5305
5247
  }
5306
5248
  const controller = new AbortController;
5307
- let timedOut = false;
5308
- const timeoutId = setTimeout(() => {
5309
- timedOut = true;
5310
- controller.abort();
5311
- }, REQUEST_TIMEOUT_MS);
5249
+ const totalTimeoutMs = 120000;
5250
+ const timeoutId = setTimeout(() => controller.abort(), totalTimeoutMs);
5312
5251
  const abortSignal = (() => {
5313
5252
  if (!signal)
5314
5253
  return controller.signal;
@@ -5321,31 +5260,20 @@ class OpenAICompatProvider {
5321
5260
  return controller.signal;
5322
5261
  }
5323
5262
  })();
5324
- let response;
5325
- try {
5326
- response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
5327
- method: "POST",
5328
- headers,
5329
- body: JSON.stringify(body),
5330
- signal: abortSignal
5331
- });
5332
- } catch (err) {
5333
- if (err?.name === "AbortError")
5334
- throw err;
5335
- const wrapped = err instanceof Error ? err : new Error(String(err));
5336
- wrapped.llmTerminal = true;
5337
- throw wrapped;
5338
- }
5263
+ const response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
5264
+ method: "POST",
5265
+ headers,
5266
+ body: JSON.stringify(body),
5267
+ signal: abortSignal
5268
+ });
5339
5269
  if (!response.ok) {
5340
5270
  clearTimeout(timeoutId);
5341
5271
  const errorText = await response.text();
5342
- const err = new Error(t("error.llm_api", {
5272
+ throw new Error(t("error.llm_api", {
5343
5273
  status: response.status,
5344
5274
  statusText: response.statusText,
5345
5275
  errorText
5346
5276
  }));
5347
- err.llmTerminal = true;
5348
- throw err;
5349
5277
  }
5350
5278
  const reader = response.body?.getReader();
5351
5279
  if (!reader) {
@@ -5356,29 +5284,9 @@ class OpenAICompatProvider {
5356
5284
  let buffer = "";
5357
5285
  const toolCallAccs = new Map;
5358
5286
  let usage;
5359
- let sawDone = false;
5360
- const readIdle = () => new Promise((resolve, reject) => {
5361
- const idleTimer = setTimeout(() => {
5362
- timedOut = true;
5363
- controller.abort();
5364
- }, idleTimeoutMs);
5365
- reader.read().then((result) => {
5366
- clearTimeout(idleTimer);
5367
- if (timedOut)
5368
- reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
5369
- else
5370
- resolve(result);
5371
- }, (err) => {
5372
- clearTimeout(idleTimer);
5373
- if (timedOut)
5374
- reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
5375
- else
5376
- reject(err);
5377
- });
5378
- });
5379
5287
  try {
5380
5288
  while (true) {
5381
- const { done, value } = await readIdle();
5289
+ const { done, value } = await reader.read();
5382
5290
  if (done)
5383
5291
  break;
5384
5292
  buffer += decoder.decode(value, { stream: true });
@@ -5390,10 +5298,8 @@ class OpenAICompatProvider {
5390
5298
  if (!trimmed || !trimmed.startsWith("data: "))
5391
5299
  continue;
5392
5300
  const data = trimmed.slice(6);
5393
- if (data === "[DONE]") {
5394
- sawDone = true;
5301
+ if (data === "[DONE]")
5395
5302
  continue;
5396
- }
5397
5303
  try {
5398
5304
  const parsed = JSON.parse(data);
5399
5305
  const choice = parsed.choices?.[0];
@@ -5410,7 +5316,6 @@ class OpenAICompatProvider {
5410
5316
  const delta = choice.delta || {};
5411
5317
  const finishReason = choice.finish_reason;
5412
5318
  if (delta.reasoning_content) {
5413
- onEmit();
5414
5319
  yield { type: "reasoning", content: delta.reasoning_content };
5415
5320
  }
5416
5321
  if (delta.tool_calls) {
@@ -5430,13 +5335,11 @@ class OpenAICompatProvider {
5430
5335
  }
5431
5336
  }
5432
5337
  if (delta.content) {
5433
- onEmit();
5434
5338
  yield { type: "text", content: delta.content };
5435
5339
  }
5436
5340
  if (finishReason === "tool_calls" && toolCallAccs.size > 0) {
5437
5341
  for (const [, acc] of toolCallAccs) {
5438
5342
  if (acc.name) {
5439
- onEmit();
5440
5343
  yield {
5441
5344
  type: "tool_call",
5442
5345
  toolCall: {
@@ -5453,14 +5356,12 @@ class OpenAICompatProvider {
5453
5356
  }
5454
5357
  }
5455
5358
  if (usage) {
5456
- onEmit();
5457
5359
  yield { type: "done", usage };
5458
5360
  }
5459
5361
  } finally {
5460
5362
  clearTimeout(timeoutId);
5461
5363
  reader.releaseLock();
5462
5364
  }
5463
- return sawDone;
5464
5365
  }
5465
5366
  async doNonStreaming(messages, tools, signal, options) {
5466
5367
  const body = buildRequestBody({
@@ -5477,30 +5378,12 @@ class OpenAICompatProvider {
5477
5378
  if (this.config.apiKey && this.config.apiKey !== "not-needed") {
5478
5379
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
5479
5380
  }
5480
- const controller = new AbortController;
5481
- let timedOut = false;
5482
- const timeoutId = setTimeout(() => {
5483
- timedOut = true;
5484
- controller.abort();
5485
- }, REQUEST_TIMEOUT_MS);
5486
- const abortSignal = (() => {
5487
- if (!signal)
5488
- return controller.signal;
5489
- try {
5490
- return AbortSignal.any([controller.signal, signal]);
5491
- } catch {
5492
- signal.addEventListener("abort", () => controller.abort(), {
5493
- once: true
5494
- });
5495
- return controller.signal;
5496
- }
5497
- })();
5498
5381
  try {
5499
5382
  const response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
5500
5383
  method: "POST",
5501
5384
  headers,
5502
5385
  body: JSON.stringify(body),
5503
- signal: abortSignal
5386
+ signal
5504
5387
  });
5505
5388
  if (!response.ok) {
5506
5389
  const errorText = await response.text();
@@ -5547,12 +5430,7 @@ class OpenAICompatProvider {
5547
5430
  }
5548
5431
  return chunks;
5549
5432
  } catch (err) {
5550
- if (timedOut && err?.name === "AbortError") {
5551
- throw new Error(t("error.llm_timeout", { timeout: REQUEST_TIMEOUT_MS }));
5552
- }
5553
5433
  throw err instanceof Error ? err : new Error(String(err));
5554
- } finally {
5555
- clearTimeout(timeoutId);
5556
5434
  }
5557
5435
  }
5558
5436
  countTokens(text) {
@@ -5636,13 +5514,281 @@ class OpenAICompatProvider {
5636
5514
  });
5637
5515
  }
5638
5516
  }
5639
- var REQUEST_TIMEOUT_MS = 120000;
5640
5517
  var init_openai_compat = __esm(() => {
5641
5518
  init_token_counter();
5642
5519
  init_i18n();
5643
5520
  init_rate_limiter();
5644
5521
  });
5645
5522
 
5523
+ // src/modules/providers/presets.ts
5524
+ function openaiCompat(opts) {
5525
+ const retry = opts.retry ?? {
5526
+ maxRetries: 3,
5527
+ baseDelay: 1000,
5528
+ maxDelay: 30000
5529
+ };
5530
+ const rateLimits = opts.rateLimits;
5531
+ return new OpenAICompatProvider({
5532
+ model: opts.model,
5533
+ baseUrl: opts.baseUrl,
5534
+ apiKey: opts.apiKey,
5535
+ contextWindow: opts.contextWindow,
5536
+ retry,
5537
+ rateLimits
5538
+ });
5539
+ }
5540
+ var OPENAI_COMPAT, OPENROUTER, OPENAI, ANTHROPIC, OPENCODE_ZEN, OPENCODE_GO, BUILTIN_PROVIDERS, HOSTED_BASE_URLS;
5541
+ var init_presets = __esm(() => {
5542
+ init_openai_compat();
5543
+ OPENAI_COMPAT = {
5544
+ type: "openai-compat",
5545
+ label: "OpenAI-compatible (LM Studio, Ollama, vLLM, etc.)",
5546
+ baseUrl: "",
5547
+ capabilities: {
5548
+ streaming: true,
5549
+ tools: true,
5550
+ vision: false,
5551
+ reasoningEffort: false,
5552
+ listModels: true,
5553
+ requiresKey: false,
5554
+ auth: "bearer"
5555
+ },
5556
+ create: openaiCompat
5557
+ };
5558
+ OPENROUTER = {
5559
+ type: "openrouter",
5560
+ label: "OpenRouter (openrouter.ai)",
5561
+ baseUrl: "https://openrouter.ai/api/v1",
5562
+ capabilities: {
5563
+ streaming: true,
5564
+ tools: true,
5565
+ vision: true,
5566
+ reasoningEffort: false,
5567
+ listModels: true,
5568
+ requiresKey: true,
5569
+ auth: "bearer"
5570
+ },
5571
+ create: openaiCompat
5572
+ };
5573
+ OPENAI = {
5574
+ type: "openai",
5575
+ label: "OpenAI API",
5576
+ baseUrl: "https://api.openai.com/v1",
5577
+ capabilities: {
5578
+ streaming: true,
5579
+ tools: true,
5580
+ vision: true,
5581
+ reasoningEffort: true,
5582
+ listModels: true,
5583
+ requiresKey: true,
5584
+ auth: "bearer"
5585
+ },
5586
+ create: openaiCompat
5587
+ };
5588
+ ANTHROPIC = {
5589
+ type: "anthropic",
5590
+ label: "Anthropic Claude",
5591
+ baseUrl: "https://api.anthropic.com",
5592
+ capabilities: {
5593
+ streaming: false,
5594
+ tools: false,
5595
+ vision: true,
5596
+ reasoningEffort: false,
5597
+ listModels: false,
5598
+ requiresKey: true,
5599
+ auth: "header"
5600
+ },
5601
+ create: openaiCompat
5602
+ };
5603
+ OPENCODE_ZEN = {
5604
+ type: "opencode-zen",
5605
+ label: "OpenCode Zen (opencode.ai/zen)",
5606
+ baseUrl: "https://opencode.ai/zen/v1",
5607
+ capabilities: {
5608
+ streaming: true,
5609
+ tools: true,
5610
+ vision: false,
5611
+ reasoningEffort: false,
5612
+ listModels: true,
5613
+ requiresKey: false,
5614
+ auth: "bearer"
5615
+ },
5616
+ create: openaiCompat
5617
+ };
5618
+ OPENCODE_GO = {
5619
+ type: "opencode-go",
5620
+ label: "OpenCode Go (opencode.ai/go)",
5621
+ baseUrl: "https://opencode.ai/zen/go/v1",
5622
+ capabilities: {
5623
+ streaming: true,
5624
+ tools: true,
5625
+ vision: false,
5626
+ reasoningEffort: false,
5627
+ listModels: true,
5628
+ requiresKey: false,
5629
+ auth: "bearer"
5630
+ },
5631
+ create: openaiCompat
5632
+ };
5633
+ BUILTIN_PROVIDERS = [
5634
+ OPENAI_COMPAT,
5635
+ OPENROUTER,
5636
+ OPENAI,
5637
+ ANTHROPIC,
5638
+ OPENCODE_ZEN,
5639
+ OPENCODE_GO
5640
+ ];
5641
+ HOSTED_BASE_URLS = {};
5642
+ for (const spec of BUILTIN_PROVIDERS) {
5643
+ if (spec.baseUrl)
5644
+ HOSTED_BASE_URLS[spec.type] = spec.baseUrl;
5645
+ }
5646
+ });
5647
+
5648
+ // src/modules/providers/create.ts
5649
+ var exports_create = {};
5650
+ __export(exports_create, {
5651
+ defaultRegistry: () => defaultRegistry,
5652
+ createProvider: () => createProvider
5653
+ });
5654
+ function createProvider(type, opts, registry = defaultRegistry()) {
5655
+ const spec = registry.get(type);
5656
+ if (!spec) {
5657
+ return BUILTIN_PROVIDERS[0].create(opts);
5658
+ }
5659
+ return spec.create(opts);
5660
+ }
5661
+ function defaultRegistry() {
5662
+ const registry = new ProviderRegistry;
5663
+ for (const spec of BUILTIN_PROVIDERS) {
5664
+ registry.register(spec);
5665
+ }
5666
+ return registry;
5667
+ }
5668
+ var init_create = __esm(() => {
5669
+ init_presets();
5670
+ });
5671
+
5672
+ // src/modules/providers/manager.ts
5673
+ class ProviderManager {
5674
+ entries;
5675
+ activeProviderName;
5676
+ model;
5677
+ opts;
5678
+ registry;
5679
+ cache = new Map;
5680
+ constructor(config, opts) {
5681
+ this.opts = opts;
5682
+ this.registry = opts.registry ?? defaultRegistry();
5683
+ this.entries = this.normalizeEntries(config);
5684
+ this.activeProviderName = this.resolveActive(config);
5685
+ this.model = "";
5686
+ }
5687
+ get activeModel() {
5688
+ return this.model;
5689
+ }
5690
+ get activeName() {
5691
+ return this.activeProviderName;
5692
+ }
5693
+ get active() {
5694
+ return this.build(this.activeProviderName);
5695
+ }
5696
+ get activeEntry() {
5697
+ const entry = this.entryByName(this.activeProviderName);
5698
+ if (!entry)
5699
+ throw new Error(`Unknown active provider "${this.activeProviderName}"`);
5700
+ return entry;
5701
+ }
5702
+ list() {
5703
+ return this.entries.map((e) => {
5704
+ const preset = this.registry.get(e.type);
5705
+ return { ...e, label: e.label || preset?.label || e.type };
5706
+ });
5707
+ }
5708
+ listNames() {
5709
+ return this.list().map((e) => e.label);
5710
+ }
5711
+ switch(name, model) {
5712
+ if (!this.entryByName(name)) {
5713
+ throw new Error(`Unknown provider "${name}"`);
5714
+ }
5715
+ this.activeProviderName = name;
5716
+ if (model !== undefined)
5717
+ this.model = model;
5718
+ return this.active;
5719
+ }
5720
+ setModel(model) {
5721
+ this.model = model;
5722
+ return this.active;
5723
+ }
5724
+ toConfig() {
5725
+ const active = this.activeEntry;
5726
+ return {
5727
+ active: this.activeProviderName,
5728
+ entries: this.entries,
5729
+ fallback: false,
5730
+ type: active.type,
5731
+ baseUrl: active.baseUrl,
5732
+ apiKey: active.apiKey
5733
+ };
5734
+ }
5735
+ normalizeEntries(config) {
5736
+ if (Array.isArray(config.entries) && config.entries.length > 0) {
5737
+ return config.entries.map((e) => ({ ...e, label: e.label || e.type }));
5738
+ }
5739
+ return [
5740
+ {
5741
+ type: config.type ?? "openai-compat",
5742
+ label: config.type ?? "openai-compat",
5743
+ baseUrl: config.baseUrl ?? "",
5744
+ apiKey: config.apiKey
5745
+ }
5746
+ ];
5747
+ }
5748
+ resolveActive(config) {
5749
+ if (config.active && this.entryByName(config.active))
5750
+ return config.active;
5751
+ const first = this.entries[0];
5752
+ return first ? first.label ?? "openai-compat" : "openai-compat";
5753
+ }
5754
+ entryByName(name) {
5755
+ return this.entries.find((e) => e.label === name || e.type === name);
5756
+ }
5757
+ build(name) {
5758
+ const entry = this.entryByName(name);
5759
+ if (!entry)
5760
+ throw new Error(`Unknown provider "${name}"`);
5761
+ const key = `${name}|${this.model}`;
5762
+ const cached = this.cache.get(key);
5763
+ if (cached)
5764
+ return cached;
5765
+ const provider = createProvider(entry.type, {
5766
+ model: this.model,
5767
+ baseUrl: entry.baseUrl,
5768
+ apiKey: entry.apiKey,
5769
+ contextWindow: entry.contextWindow ?? this.opts.contextWindow,
5770
+ retry: entry.retry ?? this.opts.retry,
5771
+ rateLimits: entry.rateLimits ?? this.opts.rateLimits
5772
+ }, this.registry);
5773
+ this.cache.set(key, provider);
5774
+ return provider;
5775
+ }
5776
+ resetCache() {
5777
+ this.cache.clear();
5778
+ }
5779
+ }
5780
+ var init_manager = __esm(() => {
5781
+ init_create();
5782
+ init_presets();
5783
+ });
5784
+
5785
+ // src/modules/providers/index.ts
5786
+ var init_providers = __esm(() => {
5787
+ init_create();
5788
+ init_manager();
5789
+ init_presets();
5790
+ });
5791
+
5646
5792
  // src/llm/model-loader.ts
5647
5793
  class ModelLoader {
5648
5794
  baseUrl;
@@ -6414,10 +6560,20 @@ var init_audit_log = __esm(() => {
6414
6560
  });
6415
6561
 
6416
6562
  // src/tools/path-utils.ts
6417
- import { resolve as resolve3, normalize as normalize2, dirname as dirname3, basename, sep } from "path";
6563
+ import { resolve as resolve3, normalize as normalize2, dirname as dirname3, basename, sep, isAbsolute } from "path";
6418
6564
  import { existsSync as existsSync9 } from "fs";
6419
6565
  function safeResolvePath(baseDir, userPath) {
6420
6566
  const norm = normalize2(userPath);
6567
+ if (isAbsolute(norm)) {
6568
+ if (existsSync9(norm) || existsSync9(dirname3(norm)))
6569
+ return norm;
6570
+ const stripped2 = norm.replace(/^[/\\]/, "");
6571
+ const relativeCandidate = resolve3(baseDir, stripped2);
6572
+ if (existsSync9(relativeCandidate) || existsSync9(dirname3(relativeCandidate))) {
6573
+ return relativeCandidate;
6574
+ }
6575
+ return norm;
6576
+ }
6421
6577
  const stripped = norm.replace(/^[/\\]/, "");
6422
6578
  const resolved = resolve3(baseDir, stripped);
6423
6579
  if (existsSync9(resolved) || existsSync9(dirname3(resolved)))
@@ -8401,9 +8557,11 @@ var init_prompt_builder = __esm(() => {
8401
8557
  // src/core/session-logger.ts
8402
8558
  class SessionLogger {
8403
8559
  session;
8560
+ getCaller;
8404
8561
  logger;
8405
- constructor(session, logger) {
8562
+ constructor(session, logger, getCaller) {
8406
8563
  this.session = session;
8564
+ this.getCaller = getCaller;
8407
8565
  this.logger = logger;
8408
8566
  this.bindSessionDir();
8409
8567
  }
@@ -8441,12 +8599,15 @@ class SessionLogger {
8441
8599
  });
8442
8600
  }
8443
8601
  logAssistant(content, reasoning, toolCalls, iteration) {
8602
+ const caller = this.getCaller?.();
8444
8603
  if (reasoning) {
8445
8604
  this.session?.appendLog({
8446
8605
  ts: new Date().toISOString(),
8447
8606
  type: "reasoning",
8448
8607
  content: reasoning,
8449
- iteration
8608
+ iteration,
8609
+ provider: caller?.provider,
8610
+ model: caller?.model
8450
8611
  });
8451
8612
  }
8452
8613
  this.session?.appendLog({
@@ -8460,14 +8621,19 @@ class SessionLogger {
8460
8621
  arguments: tc.arguments
8461
8622
  }))
8462
8623
  } : {},
8463
- iteration
8624
+ iteration,
8625
+ provider: caller?.provider,
8626
+ model: caller?.model
8464
8627
  });
8465
8628
  }
8466
8629
  saveAssistantMessage(content) {
8630
+ const caller = this.getCaller?.();
8467
8631
  this.session?.appendMessage({
8468
8632
  role: "assistant",
8469
8633
  content: content.slice(0, 500),
8470
- timestamp: new Date().toISOString()
8634
+ timestamp: new Date().toISOString(),
8635
+ provider: caller?.provider,
8636
+ model: caller?.model
8471
8637
  });
8472
8638
  }
8473
8639
  logToolDefs(toolCount, toolNames, iteration) {
@@ -8490,11 +8656,14 @@ class SessionLogger {
8490
8656
  });
8491
8657
  }
8492
8658
  logToolResult(call, result, duration, iteration) {
8659
+ const caller = this.getCaller?.();
8493
8660
  this.session?.appendMessage({
8494
8661
  role: "tool",
8495
8662
  content: result.output.slice(0, 500),
8496
8663
  name: call.name,
8497
- timestamp: new Date().toISOString()
8664
+ timestamp: new Date().toISOString(),
8665
+ provider: caller?.provider,
8666
+ model: caller?.model
8498
8667
  });
8499
8668
  this.session?.appendLog({
8500
8669
  ts: new Date().toISOString(),
@@ -8505,7 +8674,9 @@ class SessionLogger {
8505
8674
  content: result.output.slice(0, 1000),
8506
8675
  diff: result.diff,
8507
8676
  duration,
8508
- iteration
8677
+ iteration,
8678
+ provider: caller?.provider,
8679
+ model: caller?.model
8509
8680
  });
8510
8681
  }
8511
8682
  logCompaction(info) {
@@ -8532,6 +8703,8 @@ class SessionLogger {
8532
8703
  type: "context",
8533
8704
  content: info.kind === "start" ? "context snapshot at session start" : `context at iteration ${info.iteration}`,
8534
8705
  iteration: info.iteration,
8706
+ provider: info.provider,
8707
+ model: info.model,
8535
8708
  window: info.window,
8536
8709
  systemBudget: info.systemBudget,
8537
8710
  reserveBudget: info.reserveBudget,
@@ -8551,6 +8724,8 @@ class SessionLogger {
8551
8724
  ts: new Date().toISOString(),
8552
8725
  type: "llm_usage",
8553
8726
  iteration,
8727
+ provider: usage.provider,
8728
+ model: usage.model,
8554
8729
  promptTokens: usage.promptTokens,
8555
8730
  completionTokens: usage.completionTokens,
8556
8731
  totalTokens: usage.totalTokens,
@@ -9363,18 +9538,20 @@ class OrchestratorClient {
9363
9538
  this.config = config;
9364
9539
  if (config.model) {
9365
9540
  if (config.provider) {
9366
- this.provider = new OpenAICompatProvider({
9541
+ this.provider = createProvider(config.provider.type ?? "openai-compat", {
9367
9542
  model: config.model,
9368
9543
  baseUrl: config.provider.baseUrl || "http://localhost:1234/v1",
9369
9544
  apiKey: config.provider.apiKey,
9545
+ contextWindow: 32768,
9370
9546
  retry: config.retry
9371
9547
  });
9372
9548
  } else if (defaultProvider) {
9373
9549
  this.provider = defaultProvider;
9374
9550
  } else {
9375
- this.provider = new OpenAICompatProvider({
9551
+ this.provider = createProvider("openai-compat", {
9376
9552
  model: config.model,
9377
9553
  baseUrl: "http://localhost:1234/v1",
9554
+ contextWindow: 32768,
9378
9555
  retry: config.retry
9379
9556
  });
9380
9557
  }
@@ -9541,7 +9718,7 @@ Respond with JSON only:
9541
9718
 
9542
9719
  Max 3 re-plan cycles. After 3 cycles, return partial result.`;
9543
9720
  var init_orchestrator = __esm(() => {
9544
- init_openai_compat();
9721
+ init_create();
9545
9722
  init_esm();
9546
9723
  });
9547
9724
 
@@ -10239,7 +10416,7 @@ var init_stuck_detector = __esm(() => {
10239
10416
 
10240
10417
  // src/modules/artifacts/store.ts
10241
10418
  import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
10242
- import { resolve as resolve10, relative, isAbsolute } from "node:path";
10419
+ import { resolve as resolve10, relative, isAbsolute as isAbsolute2 } from "node:path";
10243
10420
 
10244
10421
  class ArtifactStore {
10245
10422
  root;
@@ -10259,7 +10436,7 @@ class ArtifactStore {
10259
10436
  const rel = relative(root, candidate);
10260
10437
  if (rel === "")
10261
10438
  return true;
10262
- return !rel.startsWith("..") && !isAbsolute(rel);
10439
+ return !rel.startsWith("..") && !isAbsolute2(rel);
10263
10440
  }
10264
10441
  save(name, content, ext = "md") {
10265
10442
  const safe = ArtifactStore.sanitizeName(name);
@@ -11606,6 +11783,8 @@ var init_pricing = __esm(() => {
11606
11783
  });
11607
11784
 
11608
11785
  // src/core/agent.ts
11786
+ import { join as join14 } from "path";
11787
+ import { homedir as homedir5 } from "os";
11609
11788
  function isToolCallJson(text) {
11610
11789
  const trimmed = text.trim();
11611
11790
  if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
@@ -11670,6 +11849,17 @@ class Agent {
11670
11849
  blocks: result.blocks
11671
11850
  };
11672
11851
  }
11852
+ callerProvenance() {
11853
+ const manager = this.deps.providerManager;
11854
+ if (manager) {
11855
+ return {
11856
+ provider: manager.activeName,
11857
+ model: manager.activeModel || this.deps.config.model
11858
+ };
11859
+ }
11860
+ const p = this.deps.config.provider;
11861
+ return { provider: p.type, model: this.deps.config.model };
11862
+ }
11673
11863
  logContextStat(kind, iteration, slog, blocks) {
11674
11864
  const cm = this.deps.contextManager;
11675
11865
  if (typeof cm.getSnapshot !== "function")
@@ -11677,9 +11867,12 @@ class Agent {
11677
11867
  const snap = cm.getSnapshot();
11678
11868
  const history = cm.getActiveHistory();
11679
11869
  const systemMsg = history.find((m) => m.role === "system");
11870
+ const caller = this.callerProvenance();
11680
11871
  slog.logContext({
11681
11872
  kind,
11682
11873
  iteration,
11874
+ provider: caller.provider,
11875
+ model: caller.model,
11683
11876
  window: snap.window,
11684
11877
  systemBudget: snap.budget.systemPrompt,
11685
11878
  reserveBudget: snap.budget.responseReserve,
@@ -11738,7 +11931,6 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11738
11931
  onPhase?.(phase);
11739
11932
  }
11740
11933
  async run(input, onChunk, onMeta, onTool, onPhase) {
11741
- this.shutdownRequested = false;
11742
11934
  this.setScope();
11743
11935
  const {
11744
11936
  config,
@@ -11750,7 +11942,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11750
11942
  sessionManager,
11751
11943
  baseDir
11752
11944
  } = this.deps;
11753
- const slog = new SessionLogger(sessionManager, logger);
11945
+ const slog = new SessionLogger(sessionManager, logger, () => this.callerProvenance());
11754
11946
  if (sessionManager && !sessionManager.getActive()) {
11755
11947
  sessionManager.create();
11756
11948
  logger.debug(`Session started: ${sessionManager.getActive()}`);
@@ -11812,7 +12004,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11812
12004
  sessionManager,
11813
12005
  baseDir
11814
12006
  } = this.deps;
11815
- const slog = new SessionLogger(sessionManager, logger);
12007
+ const slog = new SessionLogger(sessionManager, logger, () => this.callerProvenance());
11816
12008
  this.abortController = new AbortController;
11817
12009
  let iteration = 0;
11818
12010
  let lastText = "";
@@ -11915,7 +12107,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11915
12107
  const llmStart = Date.now();
11916
12108
  const promptBefore = apiPromptTokens;
11917
12109
  const completionBefore = apiCompletionTokens;
11918
- logger.logLLMRequest(config.model, history.length, input, "agent");
12110
+ logger.logLLMRequest(config.model, history.length, input, this.callerProvenance().provider);
11919
12111
  try {
11920
12112
  for await (const chunk of llmProvider.chat(history, allToolsForBudget, this.abortController?.signal)) {
11921
12113
  if (this.shutdownRequested)
@@ -11977,14 +12169,17 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11977
12169
  this.emitPhase(iteration, "done", onPhase);
11978
12170
  }
11979
12171
  apiCompletionChars += (textContent || reasoningContent).length;
11980
- logger.logLLMResponse(config.model, (textContent || reasoningContent).length, Date.now() - llmStart, undefined, "agent");
12172
+ logger.logLLMResponse(config.model, (textContent || reasoningContent).length, Date.now() - llmStart, undefined, this.callerProvenance().provider);
11981
12173
  {
11982
12174
  const usagePrompt = apiPromptTokens - promptBefore;
11983
12175
  const usageCompletion = apiCompletionTokens - completionBefore;
11984
12176
  const source = usagePrompt > 0 || usageCompletion > 0 ? "api" : "estimate";
11985
12177
  const prompt = source === "api" ? usagePrompt : contextManager.getEstimatedTokens();
11986
12178
  const completion = source === "api" ? usageCompletion : Math.ceil((textContent || reasoningContent).length / 4);
12179
+ const caller = this.callerProvenance();
11987
12180
  slog.logLlmUsage(iteration, {
12181
+ provider: caller.provider,
12182
+ model: caller.model,
11988
12183
  promptTokens: prompt,
11989
12184
  completionTokens: completion,
11990
12185
  totalTokens: prompt + completion,
@@ -12101,6 +12296,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
12101
12296
  arguments: call.arguments
12102
12297
  });
12103
12298
  const tokensAfterTool = contextManager.getEstimatedTokens();
12299
+ const caller = this.callerProvenance();
12104
12300
  onTool?.({
12105
12301
  type: "end",
12106
12302
  tool: call.name,
@@ -12108,7 +12304,9 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
12108
12304
  duration,
12109
12305
  error: !result.success,
12110
12306
  ctxDelta: tokensAfterTool - tokensBeforeTool,
12111
- costUsd: this.costTracker.total
12307
+ costUsd: this.costTracker.total,
12308
+ provider: caller.provider,
12309
+ model: caller.model
12112
12310
  });
12113
12311
  summaries.push(`[Tool: ${call.name} (${JSON.stringify(call.arguments)}) → ${truncatedOutput.slice(0, 200)}]`);
12114
12312
  if (config.session.autoSave) {
@@ -12368,23 +12566,57 @@ ${warnLine}
12368
12566
  this.systemPromptAdded = false;
12369
12567
  }
12370
12568
  async reconfigure(config) {
12371
- const { OpenAICompatProvider: OpenAICompatProvider2 } = await Promise.resolve().then(() => (init_openai_compat(), exports_openai_compat));
12372
12569
  const { TokenCounter: TokenCounter2 } = await Promise.resolve().then(() => (init_token_counter(), exports_token_counter));
12373
- const newProvider = new OpenAICompatProvider2({
12374
- model: config.model,
12375
- baseUrl: config.provider.baseUrl,
12570
+ const manager = this.deps.providerManager;
12571
+ const newProvider = manager ? manager.setModel(config.model) : await this.buildLegacyProvider(config, config.model);
12572
+ this.deps.llmProvider = newProvider;
12573
+ this.deps.toolExecutor.updateProvider(newProvider);
12574
+ this.deps.toolExecutor.ctx.llmProvider = newProvider;
12575
+ const newTokenCounter = new TokenCounter2(config.model);
12576
+ this.deps.contextManager.resize(config.contextWindow, config.contextBudget, newTokenCounter);
12577
+ this.deps.config = config;
12578
+ this.costTracker.setModel(config.model);
12579
+ }
12580
+ async buildLegacyProvider(config, model) {
12581
+ const { createProvider: createProvider2 } = await Promise.resolve().then(() => (init_create(), exports_create));
12582
+ return createProvider2(config.provider.type ?? "openai-compat", {
12583
+ model,
12584
+ baseUrl: config.provider.baseUrl ?? "",
12376
12585
  apiKey: config.provider.apiKey,
12377
12586
  contextWindow: config.contextWindow,
12378
12587
  retry: config.retry,
12379
12588
  rateLimits: config.security?.rateLimits
12380
12589
  });
12590
+ }
12591
+ async setProvider(name, model) {
12592
+ const manager = this.deps.providerManager;
12593
+ if (!manager) {
12594
+ throw new Error("ProviderManager is not available (legacy single-provider config)");
12595
+ }
12596
+ const newProvider = manager.switch(name, model ?? this.deps.config.model);
12597
+ const config = this.deps.config;
12598
+ config.provider.active = name;
12599
+ config.provider.type = manager.activeEntry.type;
12600
+ config.provider.baseUrl = manager.activeEntry.baseUrl;
12601
+ config.provider.apiKey = manager.activeEntry.apiKey;
12381
12602
  this.deps.llmProvider = newProvider;
12382
12603
  this.deps.toolExecutor.updateProvider(newProvider);
12383
12604
  this.deps.toolExecutor.ctx.llmProvider = newProvider;
12384
- const newTokenCounter = new TokenCounter2(config.model);
12385
- this.deps.contextManager.resize(config.contextWindow, config.contextBudget, newTokenCounter);
12386
- this.deps.config = config;
12387
- this.costTracker.setModel(config.model);
12605
+ this.costTracker.setModel(newProvider.model);
12606
+ const configPath = join14(homedir5(), ".mma", "config.json");
12607
+ const { saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
12608
+ saveConfig2(config, configPath);
12609
+ }
12610
+ listProviders() {
12611
+ const manager = this.deps.providerManager;
12612
+ if (!manager)
12613
+ return [];
12614
+ return manager.list().map((e) => ({
12615
+ type: e.type,
12616
+ label: e.label,
12617
+ baseUrl: e.baseUrl,
12618
+ active: e.label === manager.activeName
12619
+ }));
12388
12620
  }
12389
12621
  setContext(messages) {
12390
12622
  const { contextManager } = this.deps;
@@ -12411,6 +12643,7 @@ ${warnLine}
12411
12643
  if (killed > 0) {
12412
12644
  logger.info(`Killed ${killed} background process(es) on shutdown`);
12413
12645
  }
12646
+ logger.closeSessionLog();
12414
12647
  pluginManager.runOnSessionEnd({
12415
12648
  logger,
12416
12649
  sessionManager: sessionManager?.getActiveMeta(),
@@ -12887,7 +13120,7 @@ ${lines.join(`
12887
13120
  }
12888
13121
  }
12889
13122
  var COMPACTION_INTERVAL = 15, KEEP_LAST_N = 6;
12890
- var init_manager = __esm(() => {
13123
+ var init_manager2 = __esm(() => {
12891
13124
  init_fact_extractor();
12892
13125
  });
12893
13126
 
@@ -12984,7 +13217,7 @@ var init_confidence = __esm(() => {
12984
13217
 
12985
13218
  // src/modules/hallucination/factual.ts
12986
13219
  import { existsSync as existsSync23, readdirSync as readdirSync7 } from "fs";
12987
- import { resolve as resolve14, isAbsolute as isAbsolute2, join as join14 } from "path";
13220
+ import { resolve as resolve14, isAbsolute as isAbsolute3, join as join15 } from "path";
12988
13221
 
12989
13222
  class FactualCheck {
12990
13223
  baseDir;
@@ -13023,7 +13256,7 @@ class FactualCheck {
13023
13256
  return { status: "pass" };
13024
13257
  }
13025
13258
  pathExists(fp) {
13026
- if (isAbsolute2(fp))
13259
+ if (isAbsolute3(fp))
13027
13260
  return existsSync23(fp);
13028
13261
  if (this.knownFiles.has(fp))
13029
13262
  return true;
@@ -13078,7 +13311,7 @@ class FactualCheck {
13078
13311
  for (const entry of entries) {
13079
13312
  if (count >= FactualCheck.MAX_INDEXED_FILES)
13080
13313
  break;
13081
- const full = join14(dir, entry.name);
13314
+ const full = join15(dir, entry.name);
13082
13315
  if (entry.isDirectory()) {
13083
13316
  if (!IGNORED_DIRS.has(entry.name)) {
13084
13317
  count = this.scanDir(full, index, count);
@@ -13252,7 +13485,7 @@ var init_detector = __esm(() => {
13252
13485
  });
13253
13486
 
13254
13487
  // src/modules/lsp/command.ts
13255
- import { delimiter, join as join15 } from "path";
13488
+ import { delimiter, join as join16 } from "path";
13256
13489
  import { existsSync as existsSync24 } from "fs";
13257
13490
  import { platform as platform3 } from "os";
13258
13491
  function resolveSpawnCommand(command, platformName = platform3(), pathEnv = process.env.PATH ?? "") {
@@ -13264,7 +13497,7 @@ function resolveSpawnCommand(command, platformName = platform3(), pathEnv = proc
13264
13497
  const dirs = pathEnv.split(delimiter).filter(Boolean);
13265
13498
  for (const dir of dirs) {
13266
13499
  for (const ext of WIN_EXTS) {
13267
- const candidate = join15(dir, `${command}${ext}`);
13500
+ const candidate = join16(dir, `${command}${ext}`);
13268
13501
  if (existsSync24(candidate))
13269
13502
  return `${command}${ext}`;
13270
13503
  }
@@ -13542,7 +13775,7 @@ class PluginManager {
13542
13775
  return chunk;
13543
13776
  }
13544
13777
  }
13545
- var init_manager2 = __esm(() => {
13778
+ var init_manager3 = __esm(() => {
13546
13779
  init_checker();
13547
13780
  });
13548
13781
 
@@ -13562,9 +13795,9 @@ Workflow rules:
13562
13795
  var init_subagent = __esm(() => {
13563
13796
  init_i18n();
13564
13797
  init_agent();
13565
- init_manager();
13566
- init_detector();
13567
13798
  init_manager2();
13799
+ init_detector();
13800
+ init_manager3();
13568
13801
  init_audit_log();
13569
13802
  init_session_isolation();
13570
13803
  init_store();
@@ -14637,8 +14870,8 @@ var init_pipeline_run = __esm(() => {
14637
14870
  init_engine();
14638
14871
  init_parser();
14639
14872
  init_agent();
14640
- init_manager();
14641
14873
  init_manager2();
14874
+ init_manager3();
14642
14875
  init_detector();
14643
14876
  init_audit_log();
14644
14877
  init_session_isolation();
@@ -15224,8 +15457,8 @@ ${JSON.stringify(result, null, 2)}`
15224
15457
 
15225
15458
  // src/tools/search-history.ts
15226
15459
  import * as fs from "fs";
15227
- import { join as join16 } from "path";
15228
- import { homedir as homedir5 } from "os";
15460
+ import { join as join17 } from "path";
15461
+ import { homedir as homedir6 } from "os";
15229
15462
  function searchFile(filePath, query, maxResults, results) {
15230
15463
  if (!fs.existsSync(filePath))
15231
15464
  return;
@@ -15269,7 +15502,7 @@ var init_search_history = __esm(() => {
15269
15502
  const query = String(args.query || "").toLowerCase();
15270
15503
  const maxResults = Number(args.maxResults) || 5;
15271
15504
  const sessionId = args.sessionId ? String(args.sessionId) : null;
15272
- const sessionDir = join16(homedir5(), ".mma", "sessions");
15505
+ const sessionDir = join17(homedir6(), ".mma", "sessions");
15273
15506
  const results = [];
15274
15507
  try {
15275
15508
  if (!fs.existsSync(sessionDir)) {
@@ -15284,7 +15517,7 @@ var init_search_history = __esm(() => {
15284
15517
  continue;
15285
15518
  if (sessionId && entry.name !== sessionId)
15286
15519
  continue;
15287
- const historyFile = join16(sessionDir, entry.name, "history.jsonl");
15520
+ const historyFile = join17(sessionDir, entry.name, "history.jsonl");
15288
15521
  searchFile(historyFile, query, maxResults, results);
15289
15522
  if (results.length >= maxResults)
15290
15523
  break;
@@ -15312,7 +15545,7 @@ var init_search_history = __esm(() => {
15312
15545
 
15313
15546
  // src/modules/memory/search.ts
15314
15547
  import { readFileSync as readFileSync13, existsSync as existsSync26 } from "fs";
15315
- import { join as join17 } from "path";
15548
+ import { join as join18 } from "path";
15316
15549
 
15317
15550
  class MemorySearch {
15318
15551
  memoryDir;
@@ -15323,7 +15556,7 @@ class MemorySearch {
15323
15556
  const results = [];
15324
15557
  const lowerQuery = query.toLowerCase();
15325
15558
  for (const name of MEMORY_FILES) {
15326
- const path = join17(this.memoryDir, `${name}.md`);
15559
+ const path = join18(this.memoryDir, `${name}.md`);
15327
15560
  if (!existsSync26(path))
15328
15561
  continue;
15329
15562
  const content = readFileSync13(path, "utf-8");
@@ -15335,7 +15568,7 @@ class MemorySearch {
15335
15568
  }
15336
15569
  }
15337
15570
  }
15338
- const prefsPath = join17(this.memoryDir, "preferences.json");
15571
+ const prefsPath = join18(this.memoryDir, "preferences.json");
15339
15572
  if (existsSync26(prefsPath)) {
15340
15573
  try {
15341
15574
  const prefs = JSON.parse(readFileSync13(prefsPath, "utf-8"));
@@ -15357,7 +15590,7 @@ var init_search = __esm(() => {
15357
15590
 
15358
15591
  // src/modules/memory/store.ts
15359
15592
  import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, existsSync as existsSync27, mkdirSync as mkdirSync13 } from "fs";
15360
- import { join as join18 } from "path";
15593
+ import { join as join19 } from "path";
15361
15594
 
15362
15595
  class MemoryStore {
15363
15596
  memoryDir;
@@ -15365,7 +15598,7 @@ class MemoryStore {
15365
15598
  this.memoryDir = memoryDir;
15366
15599
  this.ensureDir();
15367
15600
  for (const name of MEMORY_FILES2) {
15368
- const path = join18(this.memoryDir, `${name}.md`);
15601
+ const path = join19(this.memoryDir, `${name}.md`);
15369
15602
  if (!existsSync27(path)) {
15370
15603
  writeFileSync9(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}
15371
15604
 
@@ -15379,13 +15612,13 @@ class MemoryStore {
15379
15612
  }
15380
15613
  }
15381
15614
  read(name) {
15382
- const path = join18(this.memoryDir, `${name}.md`);
15615
+ const path = join19(this.memoryDir, `${name}.md`);
15383
15616
  if (!existsSync27(path))
15384
15617
  return "";
15385
15618
  return readFileSync14(path, "utf-8");
15386
15619
  }
15387
15620
  append(name, entry) {
15388
- const path = join18(this.memoryDir, `${name}.md`);
15621
+ const path = join19(this.memoryDir, `${name}.md`);
15389
15622
  const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
15390
15623
  const formatted = `- **${timestamp}** — ${entry}
15391
15624
  `;
@@ -15396,7 +15629,7 @@ class MemoryStore {
15396
15629
  return searchModule.query(query);
15397
15630
  }
15398
15631
  prefsPath() {
15399
- return join18(this.memoryDir, "preferences.json");
15632
+ return join19(this.memoryDir, "preferences.json");
15400
15633
  }
15401
15634
  getPreferences() {
15402
15635
  const path = this.prefsPath();
@@ -15435,8 +15668,8 @@ var init_store2 = __esm(() => {
15435
15668
  });
15436
15669
 
15437
15670
  // src/tools/remember.ts
15438
- import { homedir as homedir6 } from "os";
15439
- import { join as join19 } from "path";
15671
+ import { homedir as homedir7 } from "os";
15672
+ import { join as join20 } from "path";
15440
15673
  var CATEGORIES, rememberTool;
15441
15674
  var init_remember = __esm(() => {
15442
15675
  init_i18n();
@@ -15475,7 +15708,7 @@ var init_remember = __esm(() => {
15475
15708
  if (!CATEGORIES.includes(category)) {
15476
15709
  return { success: false, output: t("tool.invalid_params") };
15477
15710
  }
15478
- const memoryDir = join19(homedir6(), ".mma", "memory");
15711
+ const memoryDir = join20(homedir7(), ".mma", "memory");
15479
15712
  const store = new MemoryStore(memoryDir);
15480
15713
  try {
15481
15714
  if (category === "preferences") {
@@ -15507,8 +15740,8 @@ var init_remember = __esm(() => {
15507
15740
  });
15508
15741
 
15509
15742
  // src/tools/recall.ts
15510
- import { homedir as homedir7 } from "os";
15511
- import { join as join20 } from "path";
15743
+ import { homedir as homedir8 } from "os";
15744
+ import { join as join21 } from "path";
15512
15745
  function formatAll(store) {
15513
15746
  const parts = [];
15514
15747
  const prefs = store.getPreferences();
@@ -15592,7 +15825,7 @@ var init_recall = __esm(() => {
15592
15825
  handler: async (_ctx, args) => {
15593
15826
  const query = args.query ? String(args.query) : "";
15594
15827
  const category = args.category ? String(args.category) : "";
15595
- const memoryDir = join20(homedir7(), ".mma", "memory");
15828
+ const memoryDir = join21(homedir8(), ".mma", "memory");
15596
15829
  const store = new MemoryStore(memoryDir);
15597
15830
  try {
15598
15831
  if (!query && !category) {
@@ -15643,13 +15876,13 @@ __export(exports_bridge_client, {
15643
15876
  });
15644
15877
  import { spawn as spawn4 } from "child_process";
15645
15878
  import { createInterface } from "readline";
15646
- import { dirname as dirname10, join as join21 } from "path";
15879
+ import { dirname as dirname10, join as join22 } from "path";
15647
15880
  import { fileURLToPath } from "url";
15648
15881
  function bridgeScriptPath() {
15649
15882
  const dir = dirname10(fileURLToPath(import.meta.url));
15650
15883
  const candidates = [
15651
- join21(dir, "bridge-server.mjs"),
15652
- join21(dir, "modules", "browser", "bridge-server.mjs")
15884
+ join22(dir, "bridge-server.mjs"),
15885
+ join22(dir, "modules", "browser", "bridge-server.mjs")
15653
15886
  ];
15654
15887
  return pickExistingPath(candidates);
15655
15888
  }
@@ -16203,15 +16436,15 @@ function buildTextExtractionScript() {
16203
16436
 
16204
16437
  // src/modules/browser/cookie-store.ts
16205
16438
  import { readFile, writeFile, mkdir } from "fs/promises";
16206
- import { join as join22 } from "path";
16439
+ import { join as join23 } from "path";
16207
16440
 
16208
16441
  class CookieStore {
16209
16442
  filePath;
16210
16443
  constructor(cookieDir) {
16211
- this.filePath = join22(cookieDir, "cookies.json");
16444
+ this.filePath = join23(cookieDir, "cookies.json");
16212
16445
  }
16213
16446
  async save(cookies) {
16214
- await mkdir(join22(this.filePath, ".."), { recursive: true });
16447
+ await mkdir(join23(this.filePath, ".."), { recursive: true });
16215
16448
  await writeFile(this.filePath, JSON.stringify(cookies, null, 2), "utf-8");
16216
16449
  }
16217
16450
  async load() {
@@ -16566,10 +16799,10 @@ var init_session = __esm(() => {
16566
16799
  });
16567
16800
 
16568
16801
  // src/tools/browser.ts
16569
- import { join as join23 } from "path";
16802
+ import { join as join24 } from "path";
16570
16803
  function getSession(ctx) {
16571
16804
  if (!session) {
16572
- const cookieDir = join23(ctx.baseDir, ".mma", "browser");
16805
+ const cookieDir = join24(ctx.baseDir, ".mma", "browser");
16573
16806
  session = new BrowserSession({
16574
16807
  ...DEFAULT_BROWSER_CONFIG,
16575
16808
  headless: ctx.config.browser?.headless ?? true,
@@ -16717,8 +16950,8 @@ async function readClipboardFallback() {
16717
16950
  const { platform: platform5 } = await import("os");
16718
16951
  const { execSync } = await import("child_process");
16719
16952
  const { readFileSync: readFileSync16, unlinkSync: unlinkSync4 } = await import("fs");
16720
- const { join: join24 } = await import("path");
16721
- const tmpPath = join24(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
16953
+ const { join: join25 } = await import("path");
16954
+ const tmpPath = join25(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
16722
16955
  try {
16723
16956
  if (platform5() === "linux") {
16724
16957
  execSync(`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`, {
@@ -17100,7 +17333,7 @@ class ModuleRegistry {
17100
17333
 
17101
17334
  // src/modules/plugins/loader.ts
17102
17335
  import { readdirSync as readdirSync9, existsSync as existsSync30, statSync as statSync5 } from "fs";
17103
- import { join as join24, basename as basename3 } from "path";
17336
+ import { join as join25, basename as basename3 } from "path";
17104
17337
 
17105
17338
  class PluginLoader {
17106
17339
  loadFromDir(dirPath, pluginManager, logger, options) {
@@ -17108,7 +17341,7 @@ class PluginLoader {
17108
17341
  return;
17109
17342
  const entries = readdirSync9(dirPath).sort();
17110
17343
  for (const entry of entries) {
17111
- const fullPath = join24(dirPath, entry);
17344
+ const fullPath = join25(dirPath, entry);
17112
17345
  const stat = statSync5(fullPath);
17113
17346
  if (stat.isFile()) {
17114
17347
  if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
@@ -17124,7 +17357,7 @@ class PluginLoader {
17124
17357
  }
17125
17358
  findEntryFile(dir) {
17126
17359
  for (const name of FOLDER_ENTRY_NAMES) {
17127
- const candidate = join24(dir, name);
17360
+ const candidate = join25(dir, name);
17128
17361
  if (existsSync30(candidate))
17129
17362
  return candidate;
17130
17363
  }
@@ -17168,7 +17401,7 @@ var init_loader = __esm(() => {
17168
17401
  // src/modules/plugins/builtin/lint-on-write.ts
17169
17402
  import { spawn as spawn5, execSync } from "child_process";
17170
17403
  import { existsSync as existsSync31, readFileSync as readFileSync16 } from "fs";
17171
- import { resolve as resolve18, extname as extname4, join as join25 } from "path";
17404
+ import { resolve as resolve18, extname as extname4, join as join26 } from "path";
17172
17405
  import { platform as platform5 } from "os";
17173
17406
  function contentHash(content) {
17174
17407
  let h = 5381;
@@ -17280,7 +17513,7 @@ class LintOnWritePlugin {
17280
17513
  }
17281
17514
  async runProjectLint(ctx, result, signal) {
17282
17515
  try {
17283
- const packageJsonPath = join25(ctx.baseDir, "package.json");
17516
+ const packageJsonPath = join26(ctx.baseDir, "package.json");
17284
17517
  if (!existsSync31(packageJsonPath)) {
17285
17518
  return;
17286
17519
  }
@@ -17306,7 +17539,7 @@ class LintOnWritePlugin {
17306
17539
  }
17307
17540
  async runProjectTypeCheck(filePath, baseDir, result, signal) {
17308
17541
  const projectRoot = findProjectRoot(filePath, baseDir, ["tsconfig.json", "package.json"]);
17309
- const tsconfigPath = join25(projectRoot, "tsconfig.json");
17542
+ const tsconfigPath = join26(projectRoot, "tsconfig.json");
17310
17543
  if (!existsSync31(tsconfigPath)) {
17311
17544
  return;
17312
17545
  }
@@ -17554,7 +17787,7 @@ var init_tracker = () => {};
17554
17787
 
17555
17788
  // src/modules/execution/plan-store.ts
17556
17789
  import { readFileSync as readFileSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync32, readdirSync as readdirSync10, rmSync } from "fs";
17557
- import { join as join26 } from "path";
17790
+ import { join as join27 } from "path";
17558
17791
  function readPlanFile(path, fallbackBaseDir) {
17559
17792
  try {
17560
17793
  const raw = readFileSync17(path, "utf-8");
@@ -17582,7 +17815,7 @@ function listDir(dir, baseDir) {
17582
17815
  if (!existsSync32(dir))
17583
17816
  return [];
17584
17817
  const files = readdirSync10(dir).filter((f) => f.endsWith(".json"));
17585
- return files.map((f) => readPlanFile(join26(dir, f), baseDir)).filter((p) => p !== null);
17818
+ return files.map((f) => readPlanFile(join27(dir, f), baseDir)).filter((p) => p !== null);
17586
17819
  }
17587
17820
  function toMeta(plan, status) {
17588
17821
  return {
@@ -17603,21 +17836,21 @@ class PlanStore {
17603
17836
  archiveDir;
17604
17837
  legacyPath;
17605
17838
  constructor(baseDir) {
17606
- const mmaDir = join26(baseDir, ".mma");
17839
+ const mmaDir = join27(baseDir, ".mma");
17607
17840
  if (!existsSync32(mmaDir))
17608
17841
  mkdirSync14(mmaDir, { recursive: true });
17609
17842
  this.baseDir = baseDir;
17610
- this.plansDir = join26(mmaDir, "plans");
17611
- this.draftsDir = join26(this.plansDir, "drafts");
17612
- this.archiveDir = join26(this.plansDir, "archive");
17613
- this.legacyPath = join26(mmaDir, LEGACY_FILE);
17843
+ this.plansDir = join27(mmaDir, "plans");
17844
+ this.draftsDir = join27(this.plansDir, "drafts");
17845
+ this.archiveDir = join27(this.plansDir, "archive");
17846
+ this.legacyPath = join27(mmaDir, LEGACY_FILE);
17614
17847
  for (const dir of [this.plansDir, this.draftsDir, this.archiveDir]) {
17615
17848
  if (!existsSync32(dir))
17616
17849
  mkdirSync14(dir, { recursive: true });
17617
17850
  }
17618
17851
  }
17619
17852
  activePath() {
17620
- return join26(this.plansDir, "active.json");
17853
+ return join27(this.plansDir, "active.json");
17621
17854
  }
17622
17855
  saveActive(plan) {
17623
17856
  writePlanFile(this.activePath(), plan);
@@ -17647,14 +17880,14 @@ class PlanStore {
17647
17880
  rmSync(p, { force: true });
17648
17881
  }
17649
17882
  saveDraft(plan) {
17650
- writePlanFile(join26(this.draftsDir, `${plan.id}.json`), plan);
17883
+ writePlanFile(join27(this.draftsDir, `${plan.id}.json`), plan);
17651
17884
  }
17652
17885
  loadDraft(id) {
17653
- const p = join26(this.draftsDir, `${id}.json`);
17886
+ const p = join27(this.draftsDir, `${id}.json`);
17654
17887
  return existsSync32(p) ? readPlanFile(p, this.baseDir) : null;
17655
17888
  }
17656
17889
  removeDraft(id) {
17657
- const p = join26(this.draftsDir, `${id}.json`);
17890
+ const p = join27(this.draftsDir, `${id}.json`);
17658
17891
  if (existsSync32(p))
17659
17892
  rmSync(p, { force: true });
17660
17893
  }
@@ -17662,7 +17895,7 @@ class PlanStore {
17662
17895
  return listDir(this.draftsDir, this.baseDir);
17663
17896
  }
17664
17897
  archivePlan(plan) {
17665
- writePlanFile(join26(this.archiveDir, `${plan.id}.json`), plan);
17898
+ writePlanFile(join27(this.archiveDir, `${plan.id}.json`), plan);
17666
17899
  this.removeDraft(plan.id);
17667
17900
  const active = this.loadActive();
17668
17901
  if (active && active.id === plan.id) {
@@ -17673,7 +17906,7 @@ class PlanStore {
17673
17906
  return listDir(this.archiveDir, this.baseDir);
17674
17907
  }
17675
17908
  removeArchived(id) {
17676
- const p = join26(this.archiveDir, `${id}.json`);
17909
+ const p = join27(this.archiveDir, `${id}.json`);
17677
17910
  if (existsSync32(p))
17678
17911
  rmSync(p, { force: true });
17679
17912
  }
@@ -17706,12 +17939,12 @@ class PlanStore {
17706
17939
  this.clearActive();
17707
17940
  return "active";
17708
17941
  }
17709
- const draftPath = join26(this.draftsDir, `${id}.json`);
17942
+ const draftPath = join27(this.draftsDir, `${id}.json`);
17710
17943
  if (existsSync32(draftPath)) {
17711
17944
  rmSync(draftPath, { force: true });
17712
17945
  return "draft";
17713
17946
  }
17714
- const archivedPath = join26(this.archiveDir, `${id}.json`);
17947
+ const archivedPath = join27(this.archiveDir, `${id}.json`);
17715
17948
  if (existsSync32(archivedPath)) {
17716
17949
  rmSync(archivedPath, { force: true });
17717
17950
  return "archived";
@@ -19185,8 +19418,8 @@ var init_module = __esm(() => {
19185
19418
 
19186
19419
  // src/modules/security/session-encryption.ts
19187
19420
  import { readFileSync as readFileSync19, writeFileSync as writeFileSync11, existsSync as existsSync34, readdirSync as readdirSync11, unlinkSync as unlinkSync4 } from "fs";
19188
- import { join as join27 } from "path";
19189
- import { homedir as homedir8 } from "os";
19421
+ import { join as join28 } from "path";
19422
+ import { homedir as homedir9 } from "os";
19190
19423
 
19191
19424
  class SessionFileEncryptor {
19192
19425
  config;
@@ -19194,7 +19427,7 @@ class SessionFileEncryptor {
19194
19427
  constructor(config) {
19195
19428
  this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
19196
19429
  this.encryptor = new ConfigEncryptor({
19197
- keyPath: config?.keyPath || join27(homedir8(), ".mma", ".session-encryption-key")
19430
+ keyPath: config?.keyPath || join28(homedir9(), ".mma", ".session-encryption-key")
19198
19431
  });
19199
19432
  }
19200
19433
  isEnabled() {
@@ -19280,7 +19513,7 @@ class SessionFileEncryptor {
19280
19513
  return;
19281
19514
  const files = readdirSync11(sessionDir);
19282
19515
  for (const file of files) {
19283
- const filePath = join27(sessionDir, file);
19516
+ const filePath = join28(sessionDir, file);
19284
19517
  if (existsSync34(filePath) && !file.endsWith(".enc")) {
19285
19518
  try {
19286
19519
  const content = readFileSync19(filePath, "utf8");
@@ -19297,7 +19530,7 @@ class SessionFileEncryptor {
19297
19530
  const files = readdirSync11(sessionDir);
19298
19531
  for (const file of files) {
19299
19532
  if (file.endsWith(".enc")) {
19300
- const encFilePath = join27(sessionDir, file);
19533
+ const encFilePath = join28(sessionDir, file);
19301
19534
  const decFilePath = encFilePath.slice(0, -4);
19302
19535
  try {
19303
19536
  const content = readFileSync19(encFilePath, "utf8");
@@ -19330,7 +19563,7 @@ import {
19330
19563
  writeFileSync as writeFileSync12,
19331
19564
  appendFileSync as appendFileSync6
19332
19565
  } from "fs";
19333
- import { join as join28 } from "path";
19566
+ import { join as join29 } from "path";
19334
19567
  import { gzipSync } from "zlib";
19335
19568
 
19336
19569
  class SessionStore {
@@ -19344,7 +19577,7 @@ class SessionStore {
19344
19577
  }
19345
19578
  }
19346
19579
  getSessionDir(id) {
19347
- return join28(this.baseDir, id);
19580
+ return join29(this.baseDir, id);
19348
19581
  }
19349
19582
  updateEncryption(config) {
19350
19583
  if (config?.enabled) {
@@ -19360,16 +19593,16 @@ class SessionStore {
19360
19593
  mkdirSync15(this.baseDir, { recursive: true });
19361
19594
  }
19362
19595
  sessionDir(id) {
19363
- return join28(this.baseDir, id);
19596
+ return join29(this.baseDir, id);
19364
19597
  }
19365
19598
  metaPath(id) {
19366
- return join28(this.sessionDir(id), "meta.json");
19599
+ return join29(this.sessionDir(id), "meta.json");
19367
19600
  }
19368
19601
  historyPath(id) {
19369
- return join28(this.sessionDir(id), "history.jsonl");
19602
+ return join29(this.sessionDir(id), "history.jsonl");
19370
19603
  }
19371
19604
  sessionLogPath(id) {
19372
- return join28(this.sessionDir(id), "session.jsonl");
19605
+ return join29(this.sessionDir(id), "session.jsonl");
19373
19606
  }
19374
19607
  sessionExists(id) {
19375
19608
  return existsSync35(this.metaPath(id));
@@ -19523,7 +19756,7 @@ class SessionStore {
19523
19756
  if (existsSync35(historyPath)) {
19524
19757
  const content = readFileSync20(historyPath, "utf-8");
19525
19758
  const compressed = gzipSync(content);
19526
- const gzPath = join28(this.baseDir, `${session2.id}.jsonl.gz`);
19759
+ const gzPath = join29(this.baseDir, `${session2.id}.jsonl.gz`);
19527
19760
  writeFileSync12(gzPath, compressed);
19528
19761
  rmSync2(historyPath);
19529
19762
  }
@@ -19676,7 +19909,7 @@ class SessionManager {
19676
19909
  }
19677
19910
  }
19678
19911
  }
19679
- var init_manager3 = __esm(() => {
19912
+ var init_manager4 = __esm(() => {
19680
19913
  init_i18n();
19681
19914
  init_session_isolation();
19682
19915
  });
@@ -19712,7 +19945,7 @@ var init_module2 = __esm(() => {
19712
19945
  // src/modules/session/index.ts
19713
19946
  var init_session2 = __esm(() => {
19714
19947
  init_store3();
19715
- init_manager3();
19948
+ init_manager4();
19716
19949
  init_module2();
19717
19950
  });
19718
19951
 
@@ -19734,8 +19967,8 @@ class ProfileCompressor {
19734
19967
 
19735
19968
  // src/modules/user-profile/profile.ts
19736
19969
  import { readFileSync as readFileSync21, writeFileSync as writeFileSync13, existsSync as existsSync36, mkdirSync as mkdirSync16 } from "fs";
19737
- import { join as join29 } from "path";
19738
- import { homedir as homedir9, hostname, platform as platform7, type } from "os";
19970
+ import { join as join30 } from "path";
19971
+ import { homedir as homedir10, hostname, platform as platform7, type } from "os";
19739
19972
  import { env } from "process";
19740
19973
 
19741
19974
  class UserProfile {
@@ -19751,7 +19984,7 @@ class UserProfile {
19751
19984
  os: `${type()} ${hostname()}`,
19752
19985
  hostname: hostname(),
19753
19986
  shell: env.SHELL || env.ComSpec || "unknown",
19754
- home: homedir9(),
19987
+ home: homedir10(),
19755
19988
  nodeVersion: process.version,
19756
19989
  preferences: { ...this.preferences }
19757
19990
  };
@@ -19761,10 +19994,10 @@ class UserProfile {
19761
19994
  if (!existsSync36(this.profileDir)) {
19762
19995
  mkdirSync16(this.profileDir, { recursive: true });
19763
19996
  }
19764
- writeFileSync13(join29(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
19997
+ writeFileSync13(join30(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
19765
19998
  }
19766
19999
  load() {
19767
- const path = join29(this.profileDir, "profile.json");
20000
+ const path = join30(this.profileDir, "profile.json");
19768
20001
  if (!existsSync36(path))
19769
20002
  return null;
19770
20003
  try {
@@ -19804,7 +20037,7 @@ var init_profile = () => {};
19804
20037
 
19805
20038
  // src/modules/skills/loader.ts
19806
20039
  import { readdirSync as readdirSync13, readFileSync as readFileSync22, existsSync as existsSync37, statSync as statSync6 } from "fs";
19807
- import { join as join30 } from "path";
20040
+ import { join as join31 } from "path";
19808
20041
 
19809
20042
  class SkillsLoader {
19810
20043
  loadFromDir(dirPath) {
@@ -19817,7 +20050,7 @@ class SkillsLoader {
19817
20050
  scanDir(dirPath, skills) {
19818
20051
  const entries = readdirSync13(dirPath);
19819
20052
  for (const entry of entries) {
19820
- const fullPath = join30(dirPath, entry);
20053
+ const fullPath = join31(dirPath, entry);
19821
20054
  const stat = statSync6(fullPath);
19822
20055
  if (stat.isDirectory()) {
19823
20056
  this.scanDir(fullPath, skills);
@@ -20066,10 +20299,6 @@ import { resolve as resolve20 } from "path";
20066
20299
  import { platform as platform8 } from "os";
20067
20300
 
20068
20301
  class LspClient {
20069
- logger = null;
20070
- setLogger(logger) {
20071
- this.logger = logger;
20072
- }
20073
20302
  process = null;
20074
20303
  requestId = 0;
20075
20304
  pending = new Map;
@@ -20129,25 +20358,11 @@ class LspClient {
20129
20358
  try {
20130
20359
  await this.sendRequest("initialize", initParams, timeout);
20131
20360
  } catch (e) {
20132
- const msg = e instanceof Error ? e.message : String(e);
20133
- this.logger?.warn(`LSP initialize failed (attempt 1): ${msg}`, {
20134
- command: config.command,
20135
- projectRoot
20136
- });
20137
20361
  if (!(e instanceof Error) || !e.message.includes("initialize"))
20138
20362
  throw e;
20139
20363
  await this.shutdown();
20140
20364
  await this.startServer(config, projectRoot);
20141
- try {
20142
- await this.sendRequest("initialize", initParams, timeout);
20143
- } catch (retryErr) {
20144
- const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
20145
- this.logger?.error(`LSP initialize failed (attempt 2, giving up): ${retryMsg}`, {
20146
- command: config.command,
20147
- projectRoot
20148
- });
20149
- throw retryErr;
20150
- }
20365
+ await this.sendRequest("initialize", initParams, timeout);
20151
20366
  }
20152
20367
  this.initialized = true;
20153
20368
  }
@@ -20185,11 +20400,8 @@ class LspClient {
20185
20400
  });
20186
20401
  this.process = proc;
20187
20402
  setTimeout(() => {
20188
- if (!this.initialized && this.process) {
20189
- const msg = "LSP server start timeout";
20190
- this.logger?.error(msg, { command: config.command, projectRoot, timeout: config.timeout ?? 1e4 });
20191
- reject(new Error(msg));
20192
- }
20403
+ if (!this.initialized && this.process)
20404
+ reject(new Error("LSP server start timeout"));
20193
20405
  }, config.timeout ?? 1e4);
20194
20406
  });
20195
20407
  }
@@ -20228,9 +20440,7 @@ class LspClient {
20228
20440
  try {
20229
20441
  const msg = JSON.parse(body);
20230
20442
  this.handleMessage(msg);
20231
- } catch (e) {
20232
- this.logger?.debug(`LSP JSON parse error: ${e instanceof Error ? e.message : String(e)}`);
20233
- }
20443
+ } catch {}
20234
20444
  }
20235
20445
  }
20236
20446
  handleMessage(msg) {
@@ -20268,9 +20478,7 @@ class LspClient {
20268
20478
  setTimeout(() => {
20269
20479
  if (this.pending.has(id)) {
20270
20480
  this.pending.delete(id);
20271
- const msg = `LSP request timeout: ${method}`;
20272
- this.logger?.warn(msg, { method, timeout });
20273
- reject(new Error(msg));
20481
+ reject(new Error(`LSP request timeout: ${method}`));
20274
20482
  }
20275
20483
  }, timeout);
20276
20484
  });
@@ -20280,37 +20488,27 @@ class LspClient {
20280
20488
  this.write(message);
20281
20489
  }
20282
20490
  write(message) {
20283
- if (!this.process?.stdin?.writable) {
20284
- this.logger?.debug("LSP write failed: process stdin not writable");
20491
+ if (!this.process?.stdin?.writable)
20285
20492
  return;
20286
- }
20287
20493
  const header = `Content-Length: ${Buffer.byteLength(message)}\r
20288
20494
  \r
20289
20495
  `;
20290
20496
  try {
20291
20497
  this.process.stdin.write(header + message);
20292
- } catch (e) {
20293
- this.logger?.debug(`LSP write error: ${e instanceof Error ? e.message : String(e)}`);
20294
- }
20498
+ } catch {}
20295
20499
  }
20296
20500
  async shutdown() {
20297
20501
  try {
20298
20502
  if (this.process && this.initialized && this.process.stdin?.writable) {
20299
- this.sendRequest("shutdown", null, 3000).catch((e) => {
20300
- this.logger?.debug(`LSP shutdown request failed: ${e instanceof Error ? e.message : String(e)}`);
20301
- });
20503
+ this.sendRequest("shutdown", null, 3000).catch(() => {});
20302
20504
  this.sendNotification("exit", null);
20303
20505
  await new Promise((r) => setTimeout(r, 200));
20304
20506
  }
20305
- } catch (e) {
20306
- this.logger?.debug(`LSP shutdown error: ${e instanceof Error ? e.message : String(e)}`);
20307
- } finally {
20507
+ } catch {} finally {
20308
20508
  if (this.process) {
20309
20509
  try {
20310
20510
  killTree(this.process);
20311
- } catch (e) {
20312
- this.logger?.debug(`LSP killTree error: ${e instanceof Error ? e.message : String(e)}`);
20313
- }
20511
+ } catch {}
20314
20512
  this.process = null;
20315
20513
  }
20316
20514
  this.initialized = false;
@@ -20418,26 +20616,9 @@ class LspModule {
20418
20616
  client;
20419
20617
  failuresByServer = new Map;
20420
20618
  disabledServers = new Set;
20421
- logger = null;
20422
- constructor(config, client, logger) {
20619
+ constructor(config, client) {
20423
20620
  this.config = { ...DEFAULT_LSP_CONFIG, ...config };
20424
20621
  this.client = client ?? new LspClient;
20425
- this.logger = logger ?? null;
20426
- this.client.setLogger(this.logger);
20427
- }
20428
- setLogger(logger) {
20429
- this.logger = logger;
20430
- this.client.setLogger(logger);
20431
- }
20432
- resetDisabledServers() {
20433
- this.failuresByServer.clear();
20434
- this.disabledServers.clear();
20435
- }
20436
- getDisabledServers() {
20437
- return Array.from(this.disabledServers);
20438
- }
20439
- getFailureCounts() {
20440
- return new Map(this.failuresByServer);
20441
20622
  }
20442
20623
  isLspDisabled() {
20443
20624
  return this.disabledServers.size > 0;
@@ -20657,7 +20838,7 @@ var init_lsp = __esm(() => {
20657
20838
 
20658
20839
  // src/modules/lsp/startup-check.ts
20659
20840
  import { existsSync as existsSync39 } from "fs";
20660
- import { join as join31 } from "path";
20841
+ import { join as join32 } from "path";
20661
20842
  import { spawn as spawn7 } from "child_process";
20662
20843
  async function runStartupHealthCheck(config, baseDir, deps = {}) {
20663
20844
  if (!config.enabled)
@@ -20684,7 +20865,7 @@ ${result.lines.join(`
20684
20865
  }
20685
20866
  async function runCheck(config, baseDir, deps) {
20686
20867
  const projectRoot = findProjectRoot(baseDir, baseDir, ["tsconfig.json", "package.json"]);
20687
- if (existsSync39(join31(projectRoot, "tsconfig.json"))) {
20868
+ if (existsSync39(join32(projectRoot, "tsconfig.json"))) {
20688
20869
  const runTsc = deps.runTsc ?? runTscDefault;
20689
20870
  const errors = await runTsc(projectRoot, STARTUP_CHECK_TIMEOUT_MS);
20690
20871
  if (errors.length === 0)
@@ -20708,9 +20889,7 @@ async function runCheck(config, baseDir, deps) {
20708
20889
  lines.push(formatStartupError(file, baseDir, d));
20709
20890
  }
20710
20891
  }
20711
- } catch (e) {
20712
- deps.logger?.debug(`Startup LSP check failed for ${file}: ${e instanceof Error ? e.message : String(e)}`);
20713
- }
20892
+ } catch {}
20714
20893
  }
20715
20894
  if (checked === 0 || lines.length === 0)
20716
20895
  return null;
@@ -20783,7 +20962,7 @@ var init_startup_check = __esm(() => {
20783
20962
 
20784
20963
  // src/modules/indexer/walker.ts
20785
20964
  import { readdirSync as readdirSync14, readFileSync as readFileSync23, statSync as statSync7, existsSync as existsSync40, watch } from "fs";
20786
- import { join as join32, relative as relative4, extname as extname5 } from "path";
20965
+ import { join as join33, relative as relative4, extname as extname5 } from "path";
20787
20966
 
20788
20967
  class Indexer {
20789
20968
  baseDir;
@@ -20821,7 +21000,7 @@ class Indexer {
20821
21000
  for (const entry of entries) {
20822
21001
  if (count >= this.MAX_FILES)
20823
21002
  return;
20824
- const fullPath = join32(dir, entry);
21003
+ const fullPath = join33(dir, entry);
20825
21004
  const relPath = relative4(this.baseDir, fullPath);
20826
21005
  const stat2 = statSync7(fullPath);
20827
21006
  if (stat2.isDirectory()) {
@@ -20885,13 +21064,13 @@ var init_walker = __esm(() => {
20885
21064
 
20886
21065
  // src/modules/indexer/cache.ts
20887
21066
  import { readFileSync as readFileSync24, writeFileSync as writeFileSync14, existsSync as existsSync41, mkdirSync as mkdirSync17, rmSync as rmSync3 } from "fs";
20888
- import { join as join33 } from "path";
21067
+ import { join as join34 } from "path";
20889
21068
 
20890
21069
  class IndexCache {
20891
21070
  cachePath;
20892
21071
  cache = null;
20893
21072
  constructor(cacheDir) {
20894
- this.cachePath = join33(cacheDir, "index-cache.json");
21073
+ this.cachePath = join34(cacheDir, "index-cache.json");
20895
21074
  }
20896
21075
  load() {
20897
21076
  if (this.cache)
@@ -20907,7 +21086,7 @@ class IndexCache {
20907
21086
  }
20908
21087
  save(result) {
20909
21088
  this.cache = result;
20910
- const dir = join33(this.cachePath, "..");
21089
+ const dir = join34(this.cachePath, "..");
20911
21090
  if (!existsSync41(dir))
20912
21091
  mkdirSync17(dir, { recursive: true });
20913
21092
  writeFileSync14(this.cachePath, JSON.stringify(result), "utf-8");
@@ -20925,10 +21104,10 @@ var init_cache = () => {};
20925
21104
 
20926
21105
  // src/modules/indexer/project-profile.ts
20927
21106
  import { readFileSync as readFileSync25, existsSync as existsSync42 } from "fs";
20928
- import { join as join34 } from "path";
21107
+ import { join as join35 } from "path";
20929
21108
  function detectManifest(baseDir) {
20930
21109
  for (const manifest of MANIFEST_ORDER) {
20931
- if (existsSync42(join34(baseDir, manifest)))
21110
+ if (existsSync42(join35(baseDir, manifest)))
20932
21111
  return manifest;
20933
21112
  }
20934
21113
  return null;
@@ -20945,7 +21124,7 @@ function cleanDependency(entry) {
20945
21124
  }
20946
21125
  function readPackageJson(baseDir) {
20947
21126
  try {
20948
- const raw = JSON.parse(readFileSync25(join34(baseDir, "package.json"), "utf-8"));
21127
+ const raw = JSON.parse(readFileSync25(join35(baseDir, "package.json"), "utf-8"));
20949
21128
  if (!raw || typeof raw !== "object")
20950
21129
  return null;
20951
21130
  const profile = {
@@ -20969,7 +21148,7 @@ function readPackageJson(baseDir) {
20969
21148
  }
20970
21149
  function readPyproject(baseDir) {
20971
21150
  try {
20972
- const content = readFileSync25(join34(baseDir, "pyproject.toml"), "utf-8");
21151
+ const content = readFileSync25(join35(baseDir, "pyproject.toml"), "utf-8");
20973
21152
  const profile = { runtime: "python", deps: [], devDeps: [], scripts: {} };
20974
21153
  const nameMatch = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
20975
21154
  if (nameMatch)
@@ -20985,7 +21164,7 @@ function readPyproject(baseDir) {
20985
21164
  }
20986
21165
  function readCargo(baseDir) {
20987
21166
  try {
20988
- const content = readFileSync25(join34(baseDir, "Cargo.toml"), "utf-8");
21167
+ const content = readFileSync25(join35(baseDir, "Cargo.toml"), "utf-8");
20989
21168
  const profile = { runtime: "rust", deps: [], devDeps: [], scripts: {} };
20990
21169
  const nameMatch = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
20991
21170
  if (nameMatch)
@@ -21009,7 +21188,7 @@ function readCargo(baseDir) {
21009
21188
  }
21010
21189
  function readGoMod(baseDir) {
21011
21190
  try {
21012
- const content = readFileSync25(join34(baseDir, "go.mod"), "utf-8");
21191
+ const content = readFileSync25(join35(baseDir, "go.mod"), "utf-8");
21013
21192
  const profile = { runtime: "go", deps: [], devDeps: [], scripts: {} };
21014
21193
  const moduleMatch = content.match(/^module\s+(\S+)/m);
21015
21194
  if (moduleMatch)
@@ -21027,7 +21206,7 @@ function readGoMod(baseDir) {
21027
21206
  }
21028
21207
  function readRequirements(baseDir) {
21029
21208
  try {
21030
- const content = readFileSync25(join34(baseDir, "requirements.txt"), "utf-8");
21209
+ const content = readFileSync25(join35(baseDir, "requirements.txt"), "utf-8");
21031
21210
  const profile = { runtime: "python", deps: [], devDeps: [], scripts: {} };
21032
21211
  for (const line of content.split(`
21033
21212
  `)) {
@@ -21485,8 +21664,8 @@ var init_mcp = __esm(() => {
21485
21664
  });
21486
21665
 
21487
21666
  // src/modules/memory/module.ts
21488
- import { homedir as homedir10 } from "os";
21489
- import { join as join35 } from "path";
21667
+ import { homedir as homedir11 } from "os";
21668
+ import { join as join36 } from "path";
21490
21669
 
21491
21670
  class MemoryModule {
21492
21671
  name = "memory";
@@ -21495,7 +21674,7 @@ class MemoryModule {
21495
21674
  if (storeOrDir instanceof MemoryStore) {
21496
21675
  this.store = storeOrDir;
21497
21676
  } else {
21498
- const dir = storeOrDir || join35(homedir10(), ".mma", "memory");
21677
+ const dir = storeOrDir || join36(homedir11(), ".mma", "memory");
21499
21678
  this.store = new MemoryStore(dir);
21500
21679
  }
21501
21680
  }
@@ -21582,11 +21761,11 @@ var init_module8 = __esm(() => {
21582
21761
 
21583
21762
  // src/core/version.ts
21584
21763
  import { existsSync as existsSync43, readFileSync as readFileSync26 } from "fs";
21585
- import { join as join36, dirname as dirname13 } from "path";
21764
+ import { join as join37, dirname as dirname13 } from "path";
21586
21765
  import { fileURLToPath as fileURLToPath2 } from "url";
21587
21766
  function readMmaVersion() {
21588
21767
  const here = dirname13(fileURLToPath2(import.meta.url));
21589
- const candidates = [join36(here, "..", "..", "package.json"), join36(here, "..", "package.json")];
21768
+ const candidates = [join37(here, "..", "..", "package.json"), join37(here, "..", "package.json")];
21590
21769
  for (const p of candidates) {
21591
21770
  if (existsSync43(p)) {
21592
21771
  try {
@@ -21600,188 +21779,6 @@ function readMmaVersion() {
21600
21779
  }
21601
21780
  var init_version = () => {};
21602
21781
 
21603
- // src/core/environment.ts
21604
- import { existsSync as existsSync44, readFileSync as readFileSync27, readdirSync as readdirSync15 } from "fs";
21605
- import { spawnSync as spawnSync2 } from "child_process";
21606
- import { createRequire as createRequire2 } from "module";
21607
- import { join as join37, dirname as dirname14 } from "path";
21608
- import { fileURLToPath as fileURLToPath3 } from "url";
21609
- import { arch, homedir as homedir11, hostname as hostname2, platform as platform9, release } from "os";
21610
- import { env as env2 } from "process";
21611
- function readEngineRequirement() {
21612
- const here = dirname14(fileURLToPath3(import.meta.url));
21613
- const candidates = [join37(here, "..", "..", "package.json"), join37(here, "..", "package.json")];
21614
- for (const p of candidates) {
21615
- if (!existsSync44(p))
21616
- continue;
21617
- try {
21618
- const raw = JSON.parse(readFileSync27(p, "utf8"));
21619
- if (raw.engines?.node)
21620
- return String(raw.engines.node);
21621
- } catch {}
21622
- }
21623
- return ">=20";
21624
- }
21625
- function satisfiesMinimum(version, requirement) {
21626
- const minMatch = requirement.match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
21627
- if (!minMatch)
21628
- return true;
21629
- const min = [
21630
- parseInt(minMatch[1], 10),
21631
- minMatch[2] ? parseInt(minMatch[2], 10) : 0,
21632
- minMatch[3] ? parseInt(minMatch[3], 10) : 0
21633
- ];
21634
- const parts = version.replace(/^v/i, "").split(".");
21635
- const got = [
21636
- parts[0] ? parseInt(parts[0], 10) : 0,
21637
- parts[1] ? parseInt(parts[1], 10) : 0,
21638
- parts[2] ? parseInt(parts[2], 10) : 0
21639
- ];
21640
- for (let i = 0;i < 3; i++) {
21641
- if (got[i] > min[i])
21642
- return true;
21643
- if (got[i] < min[i])
21644
- return false;
21645
- }
21646
- return true;
21647
- }
21648
- function detectRuntime() {
21649
- const bun = globalThis.Bun;
21650
- const isBun = typeof bun !== "undefined" && typeof bun?.version !== "undefined";
21651
- const engine2 = readEngineRequirement();
21652
- const runtime = isBun ? "bun" : "node";
21653
- const runtimeVersion = isBun ? String(bun.version) : process.version;
21654
- return {
21655
- runtime,
21656
- runtimeVersion,
21657
- nodeVersion: process.version,
21658
- engine: engine2,
21659
- engineOk: satisfiesMinimum(process.version, engine2)
21660
- };
21661
- }
21662
- function toolVersion(cmd) {
21663
- try {
21664
- const res = spawnSync2(cmd, ["--version"], {
21665
- encoding: "utf8",
21666
- timeout: 3000,
21667
- windowsHide: true,
21668
- stdio: ["ignore", "pipe", "pipe"]
21669
- });
21670
- if (res.error || res.status !== 0)
21671
- return "missing";
21672
- const out = ((res.stdout || "") + (res.stderr || "")).trim();
21673
- return out.split(/\r?\n/)[0].slice(0, 40) || "ok";
21674
- } catch {
21675
- return "missing";
21676
- }
21677
- }
21678
- function playwrightBrowsersDir() {
21679
- if (process.env.PLAYWRIGHT_BROWSERS_PATH)
21680
- return process.env.PLAYWRIGHT_BROWSERS_PATH;
21681
- return process.platform === "win32" ? join37(homedir11(), "AppData", "Local", "ms-playwright") : join37(homedir11(), ".cache", "ms-playwright");
21682
- }
21683
- function playwrightInfo() {
21684
- let installed = false;
21685
- let version = "";
21686
- const browsersDir = playwrightBrowsersDir();
21687
- try {
21688
- const require2 = createRequire2(import.meta.url);
21689
- const pkgPath = require2.resolve("playwright/package.json");
21690
- installed = existsSync44(pkgPath);
21691
- version = JSON.parse(readFileSync27(pkgPath, "utf8")).version || "";
21692
- } catch {
21693
- installed = false;
21694
- }
21695
- let browsersInstalled = false;
21696
- try {
21697
- if (existsSync44(browsersDir)) {
21698
- browsersInstalled = readdirSync15(browsersDir).some((d) => /chrom/i.test(d));
21699
- }
21700
- } catch {
21701
- browsersInstalled = false;
21702
- }
21703
- return { installed, version, browsersDir, browsersInstalled };
21704
- }
21705
- function checkEnvironmentRequirements(report) {
21706
- const warnings = [];
21707
- if (report.runtime.runtime === "node") {
21708
- warnings.push(t("env.runtime_node", { version: report.runtime.nodeVersion }));
21709
- }
21710
- if (!report.runtime.engineOk) {
21711
- warnings.push(t("env.runtime_old", {
21712
- version: report.runtime.nodeVersion,
21713
- engine: report.runtime.engine
21714
- }));
21715
- }
21716
- if (report.tools.bun === "missing") {
21717
- warnings.push(t("env.tool_missing", { tool: "bun" }));
21718
- }
21719
- if (report.tools.git === "missing") {
21720
- warnings.push(t("env.tool_missing", { tool: "git" }));
21721
- }
21722
- if (report.features.browser && report.playwright) {
21723
- if (!report.playwright.installed) {
21724
- warnings.push(t("env.playwright_missing"));
21725
- } else if (!report.playwright.browsersInstalled) {
21726
- warnings.push(t("env.playwright_browsers_missing", { dir: report.playwright.browsersDir }));
21727
- }
21728
- }
21729
- return warnings;
21730
- }
21731
- function collectEnvironment(opts) {
21732
- const runtime = detectRuntime();
21733
- const tools = {};
21734
- if (opts.scanTools) {
21735
- for (const tool of TOOL_CHECKS)
21736
- tools[tool] = toolVersion(tool);
21737
- }
21738
- const provider = opts.config ? {
21739
- model: opts.config.model,
21740
- baseUrl: sanitizeUrl(opts.config.provider?.baseUrl || ""),
21741
- contextWindow: opts.config.contextWindow,
21742
- retry: {
21743
- maxRetries: opts.config.retry?.maxRetries ?? 0,
21744
- baseDelay: opts.config.retry?.baseDelay ?? 0,
21745
- maxDelay: opts.config.retry?.maxDelay ?? 0,
21746
- maxStreamRetries: opts.config.retry?.maxStreamRetries ?? 0,
21747
- noDataTimeoutMs: opts.config.retry?.noDataTimeoutMs ?? 0
21748
- }
21749
- } : null;
21750
- const report = {
21751
- ts: new Date().toISOString(),
21752
- mmaVersion: readMmaVersion(),
21753
- runtime,
21754
- os: {
21755
- platform: platform9(),
21756
- arch: arch(),
21757
- release: release(),
21758
- hostname: hostname2(),
21759
- shell: env2.SHELL || env2.ComSpec || "unknown",
21760
- home: homedir11(),
21761
- cwd: process.cwd()
21762
- },
21763
- paths: { configDir: opts.configDir, baseDir: opts.baseDir || process.cwd() },
21764
- tools,
21765
- provider,
21766
- features: { browser: Boolean(opts.config?.browser?.enabled) },
21767
- playwright: playwrightInfo(),
21768
- warnings: []
21769
- };
21770
- report.warnings = checkEnvironmentRequirements(report);
21771
- return report;
21772
- }
21773
- function logEnvironment(report, logger) {
21774
- logger.info(`Environment: ${report.os.platform} ${report.os.arch} | ${report.runtime.runtime} ${report.runtime.runtimeVersion} (node ${report.runtime.nodeVersion}) | mma ${report.mmaVersion} | ${report.provider?.model ?? "no provider"}`);
21775
- logger.logStructured("environment", report);
21776
- }
21777
- var TOOL_CHECKS;
21778
- var init_environment = __esm(() => {
21779
- init_version();
21780
- init_network_validator();
21781
- init_i18n();
21782
- TOOL_CHECKS = ["bun", "node", "git", "python"];
21783
- });
21784
-
21785
21782
  // src/core/bootstrap.ts
21786
21783
  var exports_bootstrap = {};
21787
21784
  __export(exports_bootstrap, {
@@ -21790,7 +21787,7 @@ __export(exports_bootstrap, {
21790
21787
  });
21791
21788
  import { homedir as homedir12 } from "os";
21792
21789
  import { join as join38, resolve as resolve23 } from "path";
21793
- import { existsSync as existsSync45, readFileSync as readFileSync28, writeFileSync as writeFileSync15 } from "fs";
21790
+ import { existsSync as existsSync44, readFileSync as readFileSync27, writeFileSync as writeFileSync15 } from "fs";
21794
21791
  function buildSystemInfo(config, baseDir, profileCompressed) {
21795
21792
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
21796
21793
  const isWin = profileCompressed.toLowerCase().includes("win32");
@@ -21852,25 +21849,14 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
21852
21849
  const profile = new UserProfile(join38(dir));
21853
21850
  profile.load() || profile.collect();
21854
21851
  profile.save();
21855
- const llmProvider = new OpenAICompatProvider({
21856
- model: config.model,
21857
- baseUrl: config.provider.baseUrl,
21858
- apiKey: config.provider.apiKey,
21852
+ const providerManager = new ProviderManager(config.provider, {
21859
21853
  contextWindow: config.contextWindow,
21860
21854
  retry: config.retry,
21861
21855
  rateLimits: config.security?.rateLimits
21862
21856
  });
21857
+ providerManager.setModel(config.model);
21858
+ const llmProvider = providerManager.active;
21863
21859
  const baseDir = projectDir ? resolve23(projectDir) : process.cwd();
21864
- const envReport = collectEnvironment({
21865
- configDir: dir,
21866
- baseDir,
21867
- config,
21868
- scanTools: true
21869
- });
21870
- logEnvironment(envReport, logger);
21871
- for (const warning of envReport.warnings) {
21872
- logger.warn(warning);
21873
- }
21874
21860
  const projectMapCacheDir = join38(baseDir, ".mma");
21875
21861
  const indexerModule = new IndexerModule({
21876
21862
  baseDir,
@@ -21901,7 +21887,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
21901
21887
  estimatedTokens: Math.ceil(systemInfoContent.length / 4)
21902
21888
  };
21903
21889
  const agentsMdGlobal = join38(dir, "AGENTS.md");
21904
- if (!existsSync45(agentsMdGlobal)) {
21890
+ if (!existsSync44(agentsMdGlobal)) {
21905
21891
  writeFileSync15(agentsMdGlobal, "", "utf-8");
21906
21892
  }
21907
21893
  const sessionDir = join38(dir, "sessions");
@@ -21993,7 +21979,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
21993
21979
  pluginManager.register(browserPlugin);
21994
21980
  }
21995
21981
  }
21996
- const lspModule = new LspModule(config.lsp, undefined, logger);
21982
+ const lspModule = new LspModule(config.lsp);
21997
21983
  moduleRegistry.register(lspModule);
21998
21984
  const lspPlugin = lspModule.getPlugin();
21999
21985
  if (lspPlugin) {
@@ -22066,8 +22052,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
22066
22052
  join38(dir, "AGENTS.md")
22067
22053
  ];
22068
22054
  for (const p of agentsMdCandidates) {
22069
- if (existsSync45(p)) {
22070
- const content = readFileSync28(p, "utf-8").trim();
22055
+ if (existsSync44(p)) {
22056
+ const content = readFileSync27(p, "utf-8").trim();
22071
22057
  if (content) {
22072
22058
  agentsMdBlocks.push({
22073
22059
  content,
@@ -22087,6 +22073,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
22087
22073
  const agentDeps = {
22088
22074
  config,
22089
22075
  llmProvider,
22076
+ providerManager,
22090
22077
  toolExecutor,
22091
22078
  pluginManager,
22092
22079
  contextManager,
@@ -22145,15 +22132,15 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
22145
22132
  var init_bootstrap = __esm(() => {
22146
22133
  init_config2();
22147
22134
  init_app_logger();
22148
- init_openai_compat();
22135
+ init_providers();
22149
22136
  init_tools();
22150
22137
  init_hidden_tools_block();
22151
22138
  init_executor();
22152
- init_manager2();
22139
+ init_manager3();
22153
22140
  init_loader();
22154
22141
  init_lint_on_write();
22155
22142
  init_notify();
22156
- init_manager();
22143
+ init_manager2();
22157
22144
  init_token_counter();
22158
22145
  init_detector();
22159
22146
  init_module();
@@ -22170,7 +22157,6 @@ var init_bootstrap = __esm(() => {
22170
22157
  init_i18n();
22171
22158
  init_agent();
22172
22159
  init_version();
22173
- init_environment();
22174
22160
  });
22175
22161
 
22176
22162
  // node_modules/ansi-regex/index.js
@@ -22897,35 +22883,24 @@ async function runSetup(externalRl) {
22897
22883
  console.log(l);
22898
22884
  return answers;
22899
22885
  }
22900
- var PROVIDER_TYPES, KNOWN_PORTS, HOSTED_BASE_URLS;
22886
+ var PROVIDER_TYPES, KNOWN_PORTS;
22901
22887
  var init_setup = __esm(() => {
22902
22888
  init_colors();
22903
22889
  init_i18n();
22904
22890
  init_spinner();
22891
+ init_presets();
22905
22892
  init_box();
22906
22893
  init_table();
22907
- PROVIDER_TYPES = [
22908
- {
22909
- value: "openai-compat",
22910
- label: "OpenAI-compatible (LM Studio, Ollama, vLLM, etc.)"
22911
- },
22912
- { value: "openai", label: "OpenAI API" },
22913
- { value: "anthropic", label: "Anthropic Claude" },
22914
- { value: "opencode-zen", label: "OpenCode Zen (opencode.ai/zen)" },
22915
- { value: "opencode-go", label: "OpenCode Go (opencode.ai/go)" }
22916
- ];
22894
+ PROVIDER_TYPES = BUILTIN_PROVIDERS.map((spec) => ({
22895
+ value: spec.type,
22896
+ label: spec.label
22897
+ }));
22917
22898
  KNOWN_PORTS = [
22918
22899
  { port: 1234, label: "LM Studio" },
22919
22900
  { port: 11434, label: "Ollama" },
22920
22901
  { port: 8080, label: "vLLM / custom" },
22921
22902
  { port: 4891, label: "GPT4All" }
22922
22903
  ];
22923
- HOSTED_BASE_URLS = {
22924
- openai: "https://api.openai.com/v1",
22925
- anthropic: "https://api.anthropic.com",
22926
- "opencode-zen": "https://opencode.ai/zen/v1",
22927
- "opencode-go": "https://opencode.ai/zen/go/v1"
22928
- };
22929
22904
  });
22930
22905
 
22931
22906
  // src/modules/certification/manifest.ts
@@ -22939,13 +22914,13 @@ __export(exports_manifest, {
22939
22914
  getCertMark: () => getCertMark,
22940
22915
  MANIFEST_PATH: () => MANIFEST_PATH
22941
22916
  });
22942
- import { existsSync as existsSync46, readFileSync as readFileSync29, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
22917
+ import { existsSync as existsSync45, readFileSync as readFileSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
22943
22918
  import { homedir as homedir14 } from "os";
22944
22919
  import { join as join40 } from "path";
22945
22920
  function readManifest(path = MANIFEST_PATH) {
22946
22921
  try {
22947
- if (existsSync46(path)) {
22948
- const raw = JSON.parse(readFileSync29(path, "utf-8"));
22922
+ if (existsSync45(path)) {
22923
+ const raw = JSON.parse(readFileSync28(path, "utf-8"));
22949
22924
  return { version: 1, certifications: raw.certifications ?? [] };
22950
22925
  }
22951
22926
  } catch {}
@@ -30110,7 +30085,7 @@ var init_scenarios = __esm(() => {
30110
30085
  });
30111
30086
 
30112
30087
  // src/modules/certification/loader.ts
30113
- import { existsSync as existsSync47, readdirSync as readdirSync16, readFileSync as readFileSync30 } from "fs";
30088
+ import { existsSync as existsSync46, readdirSync as readdirSync15, readFileSync as readFileSync29 } from "fs";
30114
30089
  import { join as join41 } from "path";
30115
30090
  function validateScenario(s) {
30116
30091
  const errors2 = [];
@@ -30160,12 +30135,12 @@ function loadScenarios(userDir) {
30160
30135
  else
30161
30136
  scenarios.push(s);
30162
30137
  }
30163
- if (userDir && existsSync47(userDir)) {
30164
- for (const file of readdirSync16(userDir)) {
30138
+ if (userDir && existsSync46(userDir)) {
30139
+ for (const file of readdirSync15(userDir)) {
30165
30140
  if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
30166
30141
  continue;
30167
30142
  try {
30168
- const raw = readFileSync30(join41(userDir, file), "utf-8");
30143
+ const raw = readFileSync29(join41(userDir, file), "utf-8");
30169
30144
  const data = $parse(raw);
30170
30145
  const parsed = normalizeScenario(data, file);
30171
30146
  const errs = validateScenario(parsed);
@@ -30218,7 +30193,7 @@ var init_loader3 = __esm(() => {
30218
30193
  });
30219
30194
 
30220
30195
  // src/modules/certification/fact-checker.ts
30221
- import { existsSync as existsSync48, readFileSync as readFileSync31, statSync as statSync8 } from "fs";
30196
+ import { existsSync as existsSync47, readFileSync as readFileSync30, statSync as statSync8 } from "fs";
30222
30197
  import { join as join42 } from "path";
30223
30198
  function checkSandbox(sandboxDir, checks, exitCode, output) {
30224
30199
  const failures = [];
@@ -30238,14 +30213,14 @@ function runCheck2(sandboxDir, check, exitCode, output) {
30238
30213
  case "fileExists":
30239
30214
  return isFile(join42(sandboxDir, check.path));
30240
30215
  case "fileNotExists":
30241
- return !existsSync48(join42(sandboxDir, check.path));
30216
+ return !existsSync47(join42(sandboxDir, check.path));
30242
30217
  case "dirExists":
30243
30218
  return isDir(join42(sandboxDir, check.path));
30244
30219
  case "fileContent": {
30245
30220
  const abs = join42(sandboxDir, check.path);
30246
30221
  if (!isFile(abs))
30247
30222
  return false;
30248
- const content = readFileSync31(abs, "utf-8");
30223
+ const content = readFileSync30(abs, "utf-8");
30249
30224
  if (check.contains !== undefined)
30250
30225
  return content.includes(check.contains);
30251
30226
  if (check.equals !== undefined)
@@ -30256,7 +30231,7 @@ function runCheck2(sandboxDir, check, exitCode, output) {
30256
30231
  const abs = join42(sandboxDir, check.path);
30257
30232
  if (!isFile(abs))
30258
30233
  return false;
30259
- return new RegExp(check.pattern).test(readFileSync31(abs, "utf-8"));
30234
+ return new RegExp(check.pattern).test(readFileSync30(abs, "utf-8"));
30260
30235
  }
30261
30236
  default:
30262
30237
  return false;
@@ -30264,14 +30239,14 @@ function runCheck2(sandboxDir, check, exitCode, output) {
30264
30239
  }
30265
30240
  function isFile(p) {
30266
30241
  try {
30267
- return existsSync48(p) && statSync8(p).isFile();
30242
+ return existsSync47(p) && statSync8(p).isFile();
30268
30243
  } catch {
30269
30244
  return false;
30270
30245
  }
30271
30246
  }
30272
30247
  function isDir(p) {
30273
30248
  try {
30274
- return existsSync48(p) && statSync8(p).isDirectory();
30249
+ return existsSync47(p) && statSync8(p).isDirectory();
30275
30250
  } catch {
30276
30251
  return false;
30277
30252
  }
@@ -30302,9 +30277,9 @@ var init_fact_checker = () => {};
30302
30277
 
30303
30278
  // src/modules/certification/runner.ts
30304
30279
  import { spawn as spawn8 } from "child_process";
30305
- import { existsSync as existsSync49, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
30306
- import { platform as platform10 } from "os";
30307
- import { join as join43, resolve as resolve24, dirname as dirname15 } from "path";
30280
+ import { existsSync as existsSync48, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
30281
+ import { platform as platform9 } from "os";
30282
+ import { join as join43, resolve as resolve24, dirname as dirname14 } from "path";
30308
30283
  async function runScenario(scenario, opts) {
30309
30284
  if (scenario.mode === "skip") {
30310
30285
  return {
@@ -30337,15 +30312,15 @@ async function runScenario(scenario, opts) {
30337
30312
  sandbox,
30338
30313
  scenario.prompt
30339
30314
  ];
30340
- const env3 = {
30315
+ const env2 = {
30341
30316
  ...process.env,
30342
30317
  MMA_MODEL: opts.model,
30343
30318
  MMA_PROVIDER_BASEURL: opts.providerUrl,
30344
30319
  MMA_CONTEXT_WINDOW: String(opts.contextWindow ?? 32000)
30345
30320
  };
30346
30321
  if (opts.providerKey)
30347
- env3.MMA_PROVIDER_APIKEY = opts.providerKey;
30348
- const res = await runner(env3, opts.mmaRoot, args, timeoutMs);
30322
+ env2.MMA_PROVIDER_APIKEY = opts.providerKey;
30323
+ const res = await runner(env2, opts.mmaRoot, args, timeoutMs);
30349
30324
  output = `${res.stdout}
30350
30325
  ${res.stderr}`;
30351
30326
  exitCode = res.code ?? -1;
@@ -30385,24 +30360,24 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
30385
30360
  mkdirSync19(sandbox, { recursive: true });
30386
30361
  for (const f of scenario.fixtures ?? []) {
30387
30362
  const src = join43(mmaRoot, f.source);
30388
- if (!existsSync49(src)) {
30363
+ if (!existsSync48(src)) {
30389
30364
  throw new Error(`fixture missing: ${f.source}`);
30390
30365
  }
30391
30366
  const dest = join43(sandbox, f.dest);
30392
- mkdirSync19(dirname15(dest), { recursive: true });
30367
+ mkdirSync19(dirname14(dest), { recursive: true });
30393
30368
  cpSync2(src, dest);
30394
30369
  }
30395
30370
  }
30396
30371
  function resolveMmaEntry(mmaRoot) {
30397
30372
  const dev = join43(mmaRoot, "src", "cli", "main.ts");
30398
- if (existsSync49(dev))
30373
+ if (existsSync48(dev))
30399
30374
  return dev;
30400
30375
  return join43(mmaRoot, "dist", "main.js");
30401
30376
  }
30402
30377
  function findMmaRoot(fromDir) {
30403
30378
  const candidates = [resolve24(fromDir, "..", "..", ".."), resolve24(fromDir, "..")];
30404
30379
  for (const c of candidates) {
30405
- if (existsSync49(join43(c, "package.json")))
30380
+ if (existsSync48(join43(c, "package.json")))
30406
30381
  return c;
30407
30382
  }
30408
30383
  return process.cwd();
@@ -30411,7 +30386,7 @@ function killTree2(child) {
30411
30386
  const pid = child.pid;
30412
30387
  if (!pid)
30413
30388
  return;
30414
- if (platform10() === "win32") {
30389
+ if (platform9() === "win32") {
30415
30390
  spawn8("taskkill", ["/pid", String(pid), "/T", "/F"], {
30416
30391
  windowsHide: true,
30417
30392
  stdio: "ignore"
@@ -30426,10 +30401,10 @@ function killTree2(child) {
30426
30401
  } catch {}
30427
30402
  }
30428
30403
  }
30429
- var defaultRunner2 = (env3, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
30404
+ var defaultRunner2 = (env2, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
30430
30405
  const child = spawn8(process.execPath, args, {
30431
30406
  cwd,
30432
- env: env3,
30407
+ env: env2,
30433
30408
  windowsHide: true,
30434
30409
  stdio: ["ignore", "pipe", "pipe"]
30435
30410
  });
@@ -30470,15 +30445,15 @@ __export(exports_cli, {
30470
30445
  });
30471
30446
  import { rmSync as rmSync5 } from "fs";
30472
30447
  import { homedir as homedir15 } from "os";
30473
- import { join as join44, dirname as dirname16 } from "path";
30474
- import { fileURLToPath as fileURLToPath4 } from "url";
30475
- import { existsSync as existsSync50, readFileSync as readFileSync32 } from "fs";
30448
+ import { join as join44, dirname as dirname15 } from "path";
30449
+ import { fileURLToPath as fileURLToPath3 } from "url";
30450
+ import { existsSync as existsSync49, readFileSync as readFileSync31 } from "fs";
30476
30451
  function readVersion() {
30477
30452
  const candidates = [join44(MMA_ROOT, "package.json")];
30478
30453
  for (const p of candidates) {
30479
- if (existsSync50(p)) {
30454
+ if (existsSync49(p)) {
30480
30455
  try {
30481
- const raw = JSON.parse(readFileSync32(p, "utf-8"));
30456
+ const raw = JSON.parse(readFileSync31(p, "utf-8"));
30482
30457
  if (raw.version)
30483
30458
  return raw.version;
30484
30459
  } catch {}
@@ -30626,7 +30601,7 @@ var init_cli = __esm(() => {
30626
30601
  init_loader3();
30627
30602
  init_runner2();
30628
30603
  init_manifest();
30629
- HERE = dirname16(fileURLToPath4(import.meta.url));
30604
+ HERE = dirname15(fileURLToPath3(import.meta.url));
30630
30605
  MMA_ROOT = findMmaRoot(HERE);
30631
30606
  USER_SCENARIO_DIR = join44(homedir15(), ".mma", "certification", "scenarios");
30632
30607
  });
@@ -30637,17 +30612,17 @@ __export(exports_repl_commands, {
30637
30612
  registerAllCommands: () => registerAllCommands,
30638
30613
  COMMAND_GROUPS: () => COMMAND_GROUPS
30639
30614
  });
30640
- import { join as join46, dirname as dirname18 } from "path";
30615
+ import { join as join46, dirname as dirname17 } from "path";
30641
30616
  import { homedir as homedir17 } from "os";
30642
- import { existsSync as existsSync52, readFileSync as readFileSync34 } from "fs";
30643
- import { fileURLToPath as fileURLToPath6 } from "url";
30617
+ import { existsSync as existsSync51, readFileSync as readFileSync33 } from "fs";
30618
+ import { fileURLToPath as fileURLToPath5 } from "url";
30644
30619
  function readVersion3() {
30645
- const here = dirname18(fileURLToPath6(import.meta.url));
30620
+ const here = dirname17(fileURLToPath5(import.meta.url));
30646
30621
  const candidates = [join46(here, "..", "..", "package.json"), join46(here, "..", "package.json")];
30647
30622
  for (const p of candidates) {
30648
- if (existsSync52(p)) {
30623
+ if (existsSync51(p)) {
30649
30624
  try {
30650
- const raw = JSON.parse(readFileSync34(p, "utf8"));
30625
+ const raw = JSON.parse(readFileSync33(p, "utf8"));
30651
30626
  if (raw.version)
30652
30627
  return raw.version;
30653
30628
  } catch {}
@@ -30711,7 +30686,7 @@ function registerMmaCommands(ctx) {
30711
30686
  }
30712
30687
  try {
30713
30688
  const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
30714
- const { existsSync: existsSync53 } = await import("fs");
30689
+ const { existsSync: existsSync52 } = await import("fs");
30715
30690
  const { resolve: resolve25 } = await import("path");
30716
30691
  let dataUrl;
30717
30692
  let label;
@@ -30731,7 +30706,7 @@ function registerMmaCommands(ctx) {
30731
30706
  label = source;
30732
30707
  } else {
30733
30708
  const absPath = resolve25(process.cwd(), source);
30734
- if (!existsSync53(absPath)) {
30709
+ if (!existsSync52(absPath)) {
30735
30710
  console.log(pc2.red(t("image.not_found", { path: source })));
30736
30711
  return;
30737
30712
  }
@@ -30765,7 +30740,13 @@ function registerMmaCommands(ctx) {
30765
30740
  const sys = Math.floor(ctxW * cfg.contextBudget.systemPrompt);
30766
30741
  const res = Math.floor(ctxW * cfg.contextBudget.responseReserve);
30767
30742
  console.log(`${t("repl.model")} ${cfg.model}`);
30768
- console.log(`${t("repl.provider")} ${cfg.provider.type} → ${cfg.provider.baseUrl}`);
30743
+ const providers = ctx.agent.listProviders();
30744
+ const active = providers.find((p) => p.active);
30745
+ if (active) {
30746
+ console.log(`${t("repl.provider")} ${active.label} (${pc2.dim(active.type)}) → ${pc2.dim(active.baseUrl)}`);
30747
+ } else {
30748
+ console.log(`${t("repl.provider")} ${cfg.provider.type} → ${cfg.provider.baseUrl}`);
30749
+ }
30769
30750
  console.log(`${t("repl.context")} ${ctxW} (sys:${sys} res:${res} hist:${ctxW - sys - res})`);
30770
30751
  console.log(`${t("repl.max_iters")} ${cfg.maxToolIterations}`);
30771
30752
  console.log(`${t("repl.stuck_thresh")} ${cfg.stuckThreshold}`);
@@ -30793,8 +30774,14 @@ function registerMmaCommands(ctx) {
30793
30774
  description: t("repl.status"),
30794
30775
  usage: t("repl.status_usage"),
30795
30776
  action: () => {
30777
+ const providers = ctx.agent.listProviders();
30778
+ const active = providers.find((p) => p.active);
30796
30779
  console.log(`${t("repl.model")} ${ctx.config.model}`);
30797
- console.log(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
30780
+ if (active) {
30781
+ console.log(`${t("repl.provider")} ${active.label} (${pc2.dim(active.type)}) @ ${pc2.dim(active.baseUrl)}`);
30782
+ } else {
30783
+ console.log(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
30784
+ }
30798
30785
  const meta = ctx.sessionManager?.getActiveMeta();
30799
30786
  if (meta) {
30800
30787
  console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
@@ -30853,8 +30840,17 @@ Excluded blocks: ${info.excluded.length}`));
30853
30840
  action: async (args) => {
30854
30841
  const subcmd = args[0];
30855
30842
  if (!subcmd || subcmd === "list") {
30856
- console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
30857
- console.log(` ${ctx.config.provider.baseUrl}`);
30843
+ const providers = ctx.agent.listProviders();
30844
+ if (providers.length === 0) {
30845
+ console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
30846
+ console.log(` ${ctx.config.provider.baseUrl}`);
30847
+ } else {
30848
+ console.log(t("repl.provider_current"));
30849
+ for (const p of providers) {
30850
+ const marker = p.active ? pc2.green("* ") : " ";
30851
+ console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}`);
30852
+ }
30853
+ }
30858
30854
  return;
30859
30855
  }
30860
30856
  if (subcmd === "use") {
@@ -30863,11 +30859,12 @@ Excluded blocks: ${info.excluded.length}`));
30863
30859
  console.log(t("repl.provider_usage"));
30864
30860
  return;
30865
30861
  }
30866
- ctx.config.provider.type = name;
30867
- const configPath = join46(homedir17(), ".mma", "config.json");
30868
- saveConfig(ctx.config, configPath);
30869
- await ctx.agent.reconfigure(ctx.config);
30870
- console.log(pc2.green(t("repl.provider_set", { name })));
30862
+ try {
30863
+ await ctx.agent.setProvider(name);
30864
+ console.log(pc2.green(t("repl.provider_set", { name })));
30865
+ } catch (e) {
30866
+ console.log(pc2.red(e.message));
30867
+ }
30871
30868
  return;
30872
30869
  }
30873
30870
  console.log(t("repl.provider_usage"));
@@ -31018,70 +31015,6 @@ Excluded blocks: ${info.excluded.length}`));
31018
31015
  }
31019
31016
  }
31020
31017
  });
31021
- ctx.registerCommand({
31022
- name: "lsp",
31023
- description: t("repl.lsp"),
31024
- usage: t("repl.lsp_usage"),
31025
- action: async (args) => {
31026
- const subcommand = args[0] || "status";
31027
- const lspModule = ctx.agent.deps?.moduleRegistry?.getModules?.()?.find((m) => m.name === "lsp");
31028
- if (!lspModule) {
31029
- console.log(pc2.yellow(t("repl.lsp_not_available")));
31030
- return;
31031
- }
31032
- switch (subcommand) {
31033
- case "status": {
31034
- const disabled = lspModule.getDisabledServers();
31035
- const failures = lspModule.getFailureCounts();
31036
- const config = ctx.config.lsp;
31037
- console.log(pc2.bold(t("repl.lsp_status_header")));
31038
- console.log(`${t("repl.lsp_enabled")} ${config?.enabled ? pc2.green("yes") : pc2.red("no")}`);
31039
- if (disabled.length > 0) {
31040
- console.log(pc2.yellow(`${t("repl.lsp_disabled_servers")} ${disabled.join(", ")}`));
31041
- }
31042
- if (failures.size > 0) {
31043
- console.log(pc2.dim(t("repl.lsp_failure_counts")));
31044
- for (const [server, count] of failures) {
31045
- console.log(pc2.dim(` ${server}: ${count}`));
31046
- }
31047
- }
31048
- if (disabled.length === 0 && failures.size === 0) {
31049
- console.log(pc2.green(t("repl.lsp_all_ok")));
31050
- }
31051
- break;
31052
- }
31053
- case "restart": {
31054
- console.log(pc2.yellow(t("repl.lsp_restarting")));
31055
- lspModule.resetDisabledServers();
31056
- console.log(pc2.green(t("repl.lsp_restarted")));
31057
- break;
31058
- }
31059
- case "check": {
31060
- const path = args[1];
31061
- if (!path) {
31062
- console.log(pc2.yellow(t("repl.lsp_check_usage")));
31063
- return;
31064
- }
31065
- console.log(pc2.dim(t("repl.lsp_checking", { path })));
31066
- try {
31067
- const result = await ctx.agent.deps?.toolExecutor?.execute("lsp_check", { path }, ctx.agent.deps?.toolCtx);
31068
- if (result?.output) {
31069
- console.log(result.output);
31070
- }
31071
- } catch (e) {
31072
- console.log(pc2.red(t("repl.lsp_check_error", {
31073
- error: e instanceof Error ? e.message : String(e)
31074
- })));
31075
- }
31076
- break;
31077
- }
31078
- default: {
31079
- console.log(pc2.dim(t("repl.lsp_usage")));
31080
- break;
31081
- }
31082
- }
31083
- }
31084
- });
31085
31018
  }
31086
31019
  function registerSessionCommands(ctx) {
31087
31020
  if (!ctx.sessionManager)
@@ -31336,7 +31269,6 @@ var init_repl_commands = __esm(() => {
31336
31269
  reload: "agent",
31337
31270
  wizard: "agent",
31338
31271
  sysprompt: "agent",
31339
- lsp: "agent",
31340
31272
  sessions: "session",
31341
31273
  new: "session",
31342
31274
  resume: "session",
@@ -31368,9 +31300,10 @@ init_bootstrap();
31368
31300
  init_config2();
31369
31301
  init_setup();
31370
31302
  init_i18n();
31371
- import { join as join45, dirname as dirname17 } from "path";
31303
+ init_colors();
31304
+ import { join as join45, dirname as dirname16 } from "path";
31372
31305
  import { homedir as homedir16 } from "os";
31373
- import { existsSync as existsSync51, readFileSync as readFileSync33 } from "fs";
31306
+ import { existsSync as existsSync50, readFileSync as readFileSync32 } from "fs";
31374
31307
 
31375
31308
  // src/cli/security-commands.ts
31376
31309
  init_bootstrap();
@@ -32005,15 +31938,15 @@ function createPluginCommand(program2) {
32005
31938
  }
32006
31939
 
32007
31940
  // src/cli/commands.ts
32008
- init_setup();
32009
- import { fileURLToPath as fileURLToPath5 } from "url";
31941
+ init_presets();
31942
+ import { fileURLToPath as fileURLToPath4 } from "url";
32010
31943
  function readVersion2() {
32011
- const here = dirname17(fileURLToPath5(import.meta.url));
31944
+ const here = dirname16(fileURLToPath4(import.meta.url));
32012
31945
  const candidates = [join45(here, "..", "..", "package.json"), join45(here, "..", "package.json")];
32013
31946
  for (const p of candidates) {
32014
- if (existsSync51(p)) {
31947
+ if (existsSync50(p)) {
32015
31948
  try {
32016
- const raw = JSON.parse(readFileSync33(p, "utf8"));
31949
+ const raw = JSON.parse(readFileSync32(p, "utf8"));
32017
31950
  if (raw.version)
32018
31951
  return raw.version;
32019
31952
  } catch {}
@@ -32102,8 +32035,8 @@ function createProgram() {
32102
32035
  model.command("list").description(t("cli.list_models")).action(async () => {
32103
32036
  const { config } = await bootstrap();
32104
32037
  console.log(t("cli.current_model"), config.model);
32105
- const { OpenAICompatProvider: OpenAICompatProvider2 } = await Promise.resolve().then(() => (init_openai_compat(), exports_openai_compat));
32106
- const provider2 = new OpenAICompatProvider2({
32038
+ const { createProvider: createProvider2 } = await Promise.resolve().then(() => (init_create(), exports_create));
32039
+ const provider2 = createProvider2(config.provider.type, {
32107
32040
  model: config.model,
32108
32041
  baseUrl: config.provider.baseUrl,
32109
32042
  apiKey: config.provider.apiKey,
@@ -32183,13 +32116,31 @@ function createProgram() {
32183
32116
  });
32184
32117
  const provider = program2.command("provider").description(t("cli.manage_providers"));
32185
32118
  provider.command("list").description(t("cli.list_providers")).action(async () => {
32186
- const { config } = await bootstrap();
32119
+ const { config, agent } = await bootstrap();
32120
+ const providers = agent.listProviders();
32121
+ if (providers.length > 0) {
32122
+ console.log(t("cli.current_provider"));
32123
+ for (const p of providers) {
32124
+ const marker = p.active ? pc2.green("* ") : " ";
32125
+ console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}`);
32126
+ }
32127
+ return;
32128
+ }
32187
32129
  console.log(t("cli.current_provider"), config.provider.type);
32188
32130
  console.log(t("cli.base_url"), config.provider.baseUrl);
32189
32131
  });
32190
32132
  provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
32191
32133
  const configPath = join45(homedir16(), ".mma", "config.json");
32192
- const { config } = await bootstrap();
32134
+ const { config, agent } = await bootstrap();
32135
+ if (config.provider.entries && config.provider.entries.length > 0) {
32136
+ try {
32137
+ await agent.setProvider(name);
32138
+ console.log(t("cli.provider_set", { name }));
32139
+ } catch (e) {
32140
+ console.log(pc2.red(e.message));
32141
+ }
32142
+ return;
32143
+ }
32193
32144
  config.provider.type = name;
32194
32145
  const baseUrl = HOSTED_BASE_URLS[name];
32195
32146
  if (baseUrl) {
@@ -32201,6 +32152,35 @@ function createProgram() {
32201
32152
  console.log(t("cli.provider_base_hint", { baseUrl }));
32202
32153
  }
32203
32154
  });
32155
+ provider.command("add").argument("<name>", "Provider type or label").option("--url <url>", "Base URL").option("--key <key>", "API key").description(t("cli.add_provider")).action(async (name, opts) => {
32156
+ const configPath = join45(homedir16(), ".mma", "config.json");
32157
+ const { config } = await bootstrap();
32158
+ const entries = Array.isArray(config.provider.entries) ? config.provider.entries : [];
32159
+ if (entries.length === 0) {
32160
+ entries.push({
32161
+ type: config.provider.type,
32162
+ label: config.provider.type,
32163
+ baseUrl: config.provider.baseUrl,
32164
+ apiKey: config.provider.apiKey
32165
+ });
32166
+ }
32167
+ const baseUrl = opts.url || HOSTED_BASE_URLS[name] || HOSTED_BASE_URLS[`opencode-${name}`] || "";
32168
+ if (!baseUrl) {
32169
+ console.log(pc2.red(t("cli.provider_no_url", { name })));
32170
+ return;
32171
+ }
32172
+ entries.push({
32173
+ type: name,
32174
+ label: name,
32175
+ baseUrl,
32176
+ apiKey: opts.key
32177
+ });
32178
+ config.provider.entries = entries;
32179
+ config.provider.active = config.provider.active || config.provider.type;
32180
+ saveConfig(config, configPath);
32181
+ console.log(pc2.green(t("cli.provider_added", { name })));
32182
+ console.log(t("cli.provider_switch_hint"));
32183
+ });
32204
32184
  const session2 = program2.command("session").description(t("cli.manage_sessions"));
32205
32185
  session2.command("list").description(t("cli.list_sessions")).action(async () => {
32206
32186
  const { sessionManager } = await bootstrap();
@@ -33003,7 +32983,7 @@ class LineEditor {
33003
32983
  }
33004
32984
 
33005
32985
  // src/cli/repl.ts
33006
- import { existsSync as existsSync54, readFileSync as readFileSync36, writeFileSync as writeFileSync17 } from "fs";
32986
+ import { existsSync as existsSync53, readFileSync as readFileSync35, writeFileSync as writeFileSync17 } from "fs";
33007
32987
  import { join as join48 } from "path";
33008
32988
  import { homedir as homedir18 } from "os";
33009
32989
 
@@ -33449,8 +33429,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
33449
33429
  `);
33450
33430
  }
33451
33431
  }
33452
- toolEnd(_tool, duration, error, ctxDelta, costUsd) {
33432
+ toolEnd(_tool, duration, error, ctxDelta, costUsd, provider, model) {
33453
33433
  this.spinner.stop();
33434
+ const prov = provider && model ? `${pc2.dim(`${provider}·${model}`)}` : undefined;
33454
33435
  if (!this.rich) {
33455
33436
  const parts = [];
33456
33437
  if (ctxDelta !== undefined && ctxDelta !== 0) {
@@ -33460,6 +33441,8 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
33460
33441
  if (costUsd !== undefined && costUsd > 0) {
33461
33442
  parts.push(`${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`);
33462
33443
  }
33444
+ if (prov)
33445
+ parts.push(prov);
33463
33446
  if (parts.length > 0)
33464
33447
  this.out.write(`${parts.join(" ")}
33465
33448
  `);
@@ -33477,6 +33460,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
33477
33460
  if (costUsd !== undefined && costUsd > 0) {
33478
33461
  footer += ` ${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`;
33479
33462
  }
33463
+ if (prov) {
33464
+ footer += ` ${pc2.dim("via")} ${prov}`;
33465
+ }
33480
33466
  if (this.toolStyle === "inline") {
33481
33467
  this.out.write(`${GUTTER}${footer}
33482
33468
  `);
@@ -33576,24 +33562,16 @@ async function probeLspServers(config, baseDir, deps = {}) {
33576
33562
  await probeClient.probe(server, projectRoot, serverTimeout);
33577
33563
  ok.push({ language, ok: true, durationMs: Date.now() - started });
33578
33564
  } catch (e) {
33579
- const errorMsg = e instanceof Error ? e.message : String(e);
33580
- deps.logger?.warn(`LSP probe failed for ${language}: ${errorMsg}`, {
33581
- command: server.command,
33582
- timeout: serverTimeout,
33583
- durationMs: Date.now() - started
33584
- });
33585
33565
  failed.push({
33586
33566
  language,
33587
33567
  ok: false,
33588
- error: errorMsg,
33568
+ error: e instanceof Error ? e.message : String(e),
33589
33569
  durationMs: Date.now() - started
33590
33570
  });
33591
33571
  }
33592
33572
  };
33593
33573
  for (let i = 0;i < servers.length; i += maxParallel) {
33594
33574
  if (Date.now() >= deadline) {
33595
- const remaining = servers.slice(i).map((s) => s.language);
33596
- deps.logger?.warn(`LSP probe deadline reached, skipping: ${remaining.join(", ")}`);
33597
33575
  for (const { language } of servers.slice(i)) {
33598
33576
  failed.push({ language, ok: false, error: "timeout", durationMs: 0 });
33599
33577
  }
@@ -33612,14 +33590,14 @@ init_config();
33612
33590
  init_colors();
33613
33591
  init_js_identifiers();
33614
33592
  init_i18n();
33615
- import { existsSync as existsSync53, readFileSync as readFileSync35 } from "fs";
33593
+ import { existsSync as existsSync52, readFileSync as readFileSync34 } from "fs";
33616
33594
  import { join as join47 } from "path";
33617
33595
  function readActivePlan(baseDir) {
33618
33596
  const p = join47(baseDir, ".mma", "plans", "active.json");
33619
- if (!existsSync53(p))
33597
+ if (!existsSync52(p))
33620
33598
  return null;
33621
33599
  try {
33622
- const raw = readFileSync35(p, "utf-8");
33600
+ const raw = readFileSync34(p, "utf-8");
33623
33601
  if (!raw.trim())
33624
33602
  return null;
33625
33603
  const parsed = JSON.parse(raw);
@@ -33798,9 +33776,9 @@ class Repl {
33798
33776
  this.setupListeners();
33799
33777
  }
33800
33778
  loadHistory() {
33801
- if (existsSync54(this.historyPath)) {
33779
+ if (existsSync53(this.historyPath)) {
33802
33780
  try {
33803
- const raw = readFileSync36(this.historyPath, "utf-8");
33781
+ const raw = readFileSync35(this.historyPath, "utf-8");
33804
33782
  this.history = raw.split(`
33805
33783
  `).filter(Boolean).slice(-this.maxHistory);
33806
33784
  } catch {
@@ -33892,7 +33870,6 @@ class Repl {
33892
33870
  this.running = false;
33893
33871
  this.saveHistory();
33894
33872
  this.agent.shutdown();
33895
- this.logger?.closeSessionLog();
33896
33873
  if (this.exitOnClose)
33897
33874
  process.exit(0);
33898
33875
  });
@@ -33901,7 +33878,6 @@ class Repl {
33901
33878
  this.running = false;
33902
33879
  this.saveHistory();
33903
33880
  this.agent.shutdown();
33904
- this.logger?.closeSessionLog();
33905
33881
  process.exit(0);
33906
33882
  });
33907
33883
  }
@@ -34006,7 +33982,7 @@ ${t("image.clipboard_empty")}`));
34006
33982
  if (ev.type === "start") {
34007
33983
  renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.activePlan, ev.tool, ev.args), ev.icon);
34008
33984
  } else {
34009
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
33985
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd, ev.provider, ev.model);
34010
33986
  if (ev.tool === "plan" || ev.tool === "todo") {
34011
33987
  this.refreshActivePlan(renderer);
34012
33988
  }
@@ -34182,7 +34158,7 @@ ${t("image.clipboard_empty")}`));
34182
34158
  join48(this.baseDir, ".mma", "AGENTS.md"),
34183
34159
  join48(this.configDir, "AGENTS.md")
34184
34160
  ];
34185
- const foundAgents = agentsMdCandidates.filter((p) => existsSync54(p));
34161
+ const foundAgents = agentsMdCandidates.filter((p) => existsSync53(p));
34186
34162
  if (foundAgents.length > 0) {
34187
34163
  for (const p of foundAgents) {
34188
34164
  row(t("repl.agents_label"), pc2.dim(p));
@@ -34248,7 +34224,6 @@ ${t("image.clipboard_empty")}`));
34248
34224
  this.running = false;
34249
34225
  this.saveHistory();
34250
34226
  this.agent.shutdown();
34251
- this.logger?.closeSessionLog();
34252
34227
  this.rl.close();
34253
34228
  }
34254
34229
  }
@@ -34279,10 +34254,10 @@ init_setup();
34279
34254
  init_config2();
34280
34255
  init_i18n();
34281
34256
  init_colors();
34282
- import { existsSync as existsSync55, readFileSync as readFileSync37 } from "fs";
34283
- import { join as join50, dirname as dirname19 } from "path";
34284
- import { homedir as homedir20 } from "os";
34285
- import { fileURLToPath as fileURLToPath7 } from "url";
34257
+ import { existsSync as existsSync54, readFileSync as readFileSync36 } from "fs";
34258
+ import { join as join49, dirname as dirname18 } from "path";
34259
+ import { homedir as homedir19 } from "os";
34260
+ import { fileURLToPath as fileURLToPath6 } from "url";
34286
34261
 
34287
34262
  // src/modules/updater/index.ts
34288
34263
  init_checker();
@@ -34381,61 +34356,14 @@ class UpdaterModule {
34381
34356
  await this.runOnce();
34382
34357
  }
34383
34358
  }
34384
- // src/core/crash-handler.ts
34385
- init_environment();
34386
- init_data_sanitizer();
34387
- init_i18n();
34388
- import { appendFileSync as appendFileSync7, mkdirSync as mkdirSync20 } from "fs";
34389
- import { join as join49 } from "path";
34390
- import { homedir as homedir19 } from "os";
34391
- var CRASH_LOG_DIR = join49(homedir19(), ".mma", "logs");
34392
- var CRASH_LOG_FILE = "crash.jsonl";
34393
- function formatCrashEntry(type2, err) {
34394
- const message = err instanceof Error ? err.message : String(err);
34395
- const stack = err instanceof Error && err.stack ? err.stack : message;
34396
- return {
34397
- ts: new Date().toISOString(),
34398
- type: type2,
34399
- message: sanitizeLogMessage(message),
34400
- stack: sanitizeLogMessage(stack),
34401
- environment: collectEnvironment({ configDir: homedir19(), scanTools: false })
34402
- };
34403
- }
34404
- function writeCrashEntry(dir, entry) {
34405
- try {
34406
- mkdirSync20(dir, { recursive: true });
34407
- appendFileSync7(join49(dir, CRASH_LOG_FILE), JSON.stringify(entry) + `
34408
- `, "utf-8");
34409
- } catch {}
34410
- }
34411
- var installed = false;
34412
- function installCrashHandlers() {
34413
- if (installed)
34414
- return;
34415
- installed = true;
34416
- process.on("uncaughtException", (err) => {
34417
- const entry = formatCrashEntry("uncaughtException", err);
34418
- writeCrashEntry(CRASH_LOG_DIR, entry);
34419
- process.stderr.write(`${t("env.crash_stderr", { type: entry.type, message: entry.message })}
34420
- `);
34421
- process.exit(1);
34422
- });
34423
- process.on("unhandledRejection", (reason) => {
34424
- const entry = formatCrashEntry("unhandledRejection", reason);
34425
- writeCrashEntry(CRASH_LOG_DIR, entry);
34426
- process.stderr.write(`${t("env.crash_stderr", { type: entry.type, message: entry.message })}
34427
- `);
34428
- });
34429
- }
34430
-
34431
34359
  // src/cli/main.ts
34432
34360
  function readVersion4() {
34433
- const here = dirname19(fileURLToPath7(import.meta.url));
34434
- const candidates = [join50(here, "..", "..", "package.json"), join50(here, "..", "package.json")];
34361
+ const here = dirname18(fileURLToPath6(import.meta.url));
34362
+ const candidates = [join49(here, "..", "..", "package.json"), join49(here, "..", "package.json")];
34435
34363
  for (const p of candidates) {
34436
- if (existsSync55(p)) {
34364
+ if (existsSync54(p)) {
34437
34365
  try {
34438
- const raw = JSON.parse(readFileSync37(p, "utf8"));
34366
+ const raw = JSON.parse(readFileSync36(p, "utf8"));
34439
34367
  if (raw.version)
34440
34368
  return raw.version;
34441
34369
  } catch {}
@@ -34460,7 +34388,6 @@ function startAutoUpdate(config) {
34460
34388
  }
34461
34389
  }
34462
34390
  async function main() {
34463
- installCrashHandlers();
34464
34391
  const program2 = createProgram();
34465
34392
  program2.parse(process.argv);
34466
34393
  const cmdNames = new Set(program2.commands.map((c) => c.name()));
@@ -34520,15 +34447,15 @@ async function main() {
34520
34447
  await updater?.waitForIdle();
34521
34448
  process.exit(exitCode);
34522
34449
  } else {
34523
- const configPath = join50(homedir20(), ".mma", "config.json");
34524
- if (!existsSync55(configPath)) {
34450
+ const configPath = join49(homedir19(), ".mma", "config.json");
34451
+ if (!existsSync54(configPath)) {
34525
34452
  console.log(pc2.yellow(`
34526
34453
  ` + t("cli.first_run") + `
34527
34454
  `));
34528
34455
  const answers = await runSetup();
34529
34456
  const config2 = loadConfig({
34530
- configDir: join50(homedir20(), ".mma"),
34531
- projectConfigPath: projectDir ? join50(projectDir, ".mmrc") : join50(process.cwd(), ".mmrc")
34457
+ configDir: join49(homedir19(), ".mma"),
34458
+ projectConfigPath: projectDir ? join49(projectDir, ".mmrc") : join49(process.cwd(), ".mmrc")
34532
34459
  });
34533
34460
  config2.provider.type = answers.provider;
34534
34461
  config2.provider.baseUrl = answers.apiBase;