micro-models-agent 0.44.0 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/bin/mma.mjs +41 -41
  2. package/dist/cli/commands.js +9 -19
  3. package/dist/cli/completer.js +36 -37
  4. package/dist/cli/index.js +2 -2
  5. package/dist/cli/main.js +48 -23
  6. package/dist/cli/plugin-commands.js +36 -0
  7. package/dist/cli/repl-commands.js +40 -12
  8. package/dist/cli/repl.js +217 -87
  9. package/dist/cli/run-result.js +22 -0
  10. package/dist/cli/security-commands.js +5 -7
  11. package/dist/cli/setup.js +8 -26
  12. package/dist/config/config.js +52 -5
  13. package/dist/config/defaults.js +29 -5
  14. package/dist/config/experts.js +1 -1
  15. package/dist/config/index.js +3 -3
  16. package/dist/config/security.js +3 -10
  17. package/dist/core/agent-moe.js +2 -10
  18. package/dist/core/agent.js +273 -82
  19. package/dist/core/bootstrap.js +80 -13
  20. package/dist/core/index.js +2 -2
  21. package/dist/core/prompt-builder.js +23 -2
  22. package/dist/core/session-logger.js +46 -4
  23. package/dist/core/version.js +24 -0
  24. package/dist/i18n/en.json +75 -2
  25. package/dist/i18n/ru.json +74 -1
  26. package/dist/index.js +1 -1
  27. package/dist/llm/image-utils.js +4 -5
  28. package/dist/llm/index.js +4 -4
  29. package/dist/llm/model-loader.js +6 -6
  30. package/dist/llm/openai-compat.js +40 -34
  31. package/dist/llm/orchestrator.js +33 -29
  32. package/dist/llm/response.js +9 -9
  33. package/dist/logger/app-logger.js +1 -1
  34. package/dist/logger/index.js +1 -1
  35. package/dist/main.js +525 -85
  36. package/dist/migration/backup.js +13 -13
  37. package/dist/migration/detect.js +11 -11
  38. package/dist/migration/index.js +2 -2
  39. package/dist/modules/artifacts/store.js +61 -0
  40. package/dist/modules/browser/actions.js +34 -4
  41. package/dist/modules/browser/bridge-client.js +199 -0
  42. package/dist/modules/browser/bridge-path.js +10 -0
  43. package/dist/modules/browser/bridge-server.mjs +202 -202
  44. package/dist/modules/browser/cookie-store.js +6 -6
  45. package/dist/modules/browser/driver.js +136 -0
  46. package/dist/modules/browser/index.js +7 -5
  47. package/dist/modules/browser/module.js +8 -7
  48. package/dist/modules/browser/session.js +87 -84
  49. package/dist/modules/browser/snapshot.js +92 -58
  50. package/dist/modules/browser/types.js +4 -1
  51. package/dist/modules/certification/cli.js +2 -4
  52. package/dist/modules/certification/fact-checker.js +1 -3
  53. package/dist/modules/certification/loader.js +3 -9
  54. package/dist/modules/certification/runner.js +1 -4
  55. package/dist/modules/context/chunk-query.js +100 -0
  56. package/dist/modules/context/fact-extractor.js +162 -0
  57. package/dist/modules/context/history.js +15 -0
  58. package/dist/modules/context/index.js +1 -1
  59. package/dist/modules/context/manager.js +160 -86
  60. package/dist/modules/execution/audit-runners.js +152 -0
  61. package/dist/modules/execution/auditor.js +177 -25
  62. package/dist/modules/execution/execution-plugin.js +272 -0
  63. package/dist/modules/execution/module.js +201 -544
  64. package/dist/modules/execution/moe-executor.js +25 -0
  65. package/dist/modules/execution/plan-store.js +1 -3
  66. package/dist/modules/execution/plan-tool.js +508 -0
  67. package/dist/modules/execution/plan-validator.js +10 -10
  68. package/dist/modules/execution/planner.js +6 -1
  69. package/dist/modules/execution/stuck-detector.js +173 -10
  70. package/dist/modules/execution/verifier.js +86 -42
  71. package/dist/modules/execution/windows-commands.js +41 -0
  72. package/dist/modules/hallucination/confidence.js +8 -1
  73. package/dist/modules/hallucination/detector.js +2 -5
  74. package/dist/modules/hallucination/factual.js +3 -64
  75. package/dist/modules/hallucination/index.js +1 -1
  76. package/dist/modules/hallucination/js-identifiers.js +190 -0
  77. package/dist/modules/hallucination/llm-judge.js +1 -3
  78. package/dist/modules/indexer/cache.js +9 -7
  79. package/dist/modules/indexer/index.js +3 -3
  80. package/dist/modules/indexer/module.js +95 -42
  81. package/dist/modules/indexer/project-profile.js +183 -0
  82. package/dist/modules/indexer/walker.js +17 -17
  83. package/dist/modules/lsp/check-tool.js +58 -0
  84. package/dist/modules/lsp/client.js +74 -31
  85. package/dist/modules/lsp/command.js +60 -0
  86. package/dist/modules/lsp/config.js +87 -33
  87. package/dist/modules/lsp/index.js +3 -3
  88. package/dist/modules/lsp/module.js +185 -21
  89. package/dist/modules/lsp/probe.js +76 -0
  90. package/dist/modules/lsp/project-root.js +32 -0
  91. package/dist/modules/lsp/startup-check.js +141 -0
  92. package/dist/modules/mcp/module.js +2 -6
  93. package/dist/modules/memory/index.js +1 -1
  94. package/dist/modules/memory/module.js +71 -23
  95. package/dist/modules/memory/search.js +11 -9
  96. package/dist/modules/memory/store.js +13 -13
  97. package/dist/modules/pipelines/engine.js +10 -10
  98. package/dist/modules/pipelines/index.js +3 -3
  99. package/dist/modules/pipelines/parser.js +17 -14
  100. package/dist/modules/pipelines/template.js +1 -1
  101. package/dist/modules/plugins/builtin/lint-on-write.js +21 -16
  102. package/dist/modules/plugins/builtin/notify.js +3 -2
  103. package/dist/modules/plugins/index.js +1 -1
  104. package/dist/modules/plugins/loader.js +59 -17
  105. package/dist/modules/plugins/manager.js +73 -17
  106. package/dist/modules/processes/detect.js +34 -0
  107. package/dist/modules/processes/index.js +1 -1
  108. package/dist/modules/processes/registry.js +135 -46
  109. package/dist/modules/registry.js +4 -2
  110. package/dist/modules/security/audit-notifier.js +39 -39
  111. package/dist/modules/security/command-validator.js +2 -8
  112. package/dist/modules/security/data-sanitizer.js +1 -9
  113. package/dist/modules/security/encryption.js +58 -56
  114. package/dist/modules/security/network-validator.js +1 -9
  115. package/dist/modules/security/path-validator.js +1 -3
  116. package/dist/modules/security/security-policies.js +3 -19
  117. package/dist/modules/security/session-encryption.js +1 -1
  118. package/dist/modules/security/session-isolation.js +8 -8
  119. package/dist/modules/session/index.js +3 -3
  120. package/dist/modules/session/module.js +5 -5
  121. package/dist/modules/session/store.js +3 -9
  122. package/dist/modules/skills/matcher.js +27 -0
  123. package/dist/modules/skills/module.js +1 -2
  124. package/dist/modules/updater/checker.js +70 -6
  125. package/dist/modules/updater/index.js +2 -1
  126. package/dist/modules/updater/module.js +116 -0
  127. package/dist/modules/user-profile/compressor.js +2 -2
  128. package/dist/modules/user-profile/index.js +1 -1
  129. package/dist/modules/user-profile/profile.js +9 -9
  130. package/dist/tools/attach-image.js +1 -1
  131. package/dist/tools/bash.js +178 -19
  132. package/dist/tools/browser.js +46 -29
  133. package/dist/tools/chunk-query.js +99 -0
  134. package/dist/tools/download-file.js +116 -0
  135. package/dist/tools/enable-tools.js +58 -0
  136. package/dist/tools/executor.js +4 -5
  137. package/dist/tools/file-info.js +13 -12
  138. package/dist/tools/filter-tools.js +9 -2
  139. package/dist/tools/glob-tool.js +11 -11
  140. package/dist/tools/grep-tool.js +1 -3
  141. package/dist/tools/hidden-tools-block.js +37 -0
  142. package/dist/tools/index.js +13 -2
  143. package/dist/tools/list-dir.js +18 -17
  144. package/dist/tools/load-skill.js +1 -3
  145. package/dist/tools/path-utils.js +4 -4
  146. package/dist/tools/pipeline-run.js +25 -25
  147. package/dist/tools/process-kill.js +11 -11
  148. package/dist/tools/process-list.js +20 -22
  149. package/dist/tools/process-log.js +22 -18
  150. package/dist/tools/question.js +1 -3
  151. package/dist/tools/read-file.js +10 -2
  152. package/dist/tools/recall.js +44 -37
  153. package/dist/tools/registry.js +15 -4
  154. package/dist/tools/remember.js +29 -29
  155. package/dist/tools/scope-check.js +9 -9
  156. package/dist/tools/subagent.js +54 -9
  157. package/dist/tools/user-input.js +1 -1
  158. package/dist/tools/web-browse.js +3 -3
  159. package/dist/tools/web-fetch.js +3 -3
  160. package/dist/tools/web-search.js +3 -3
  161. package/dist/tools/write-file.js +1 -3
  162. package/dist/ui/box.js +1 -5
  163. package/dist/ui/index.js +6 -6
  164. package/dist/ui/line-editor.js +703 -0
  165. package/dist/ui/line-math.js +69 -0
  166. package/dist/ui/md-formatter.js +33 -33
  167. package/dist/ui/output.js +5 -5
  168. package/dist/ui/plan-view.js +103 -0
  169. package/dist/ui/renderer.js +15 -10
  170. package/dist/ui/table.js +1 -1
  171. package/package.json +48 -48
package/dist/main.js CHANGED
@@ -2263,7 +2263,9 @@ var init_defaults = __esm(() => {
2263
2263
  retry: {
2264
2264
  maxRetries: 3,
2265
2265
  baseDelay: 1000,
2266
- maxDelay: 30000
2266
+ maxDelay: 30000,
2267
+ maxStreamRetries: 2,
2268
+ noDataTimeoutMs: 60000
2267
2269
  },
2268
2270
  maxToolIterations: 1000,
2269
2271
  stuckThreshold: 6,
@@ -2298,6 +2300,10 @@ var init_defaults = __esm(() => {
2298
2300
  maxSearchesPerSession: 3,
2299
2301
  requestTimeoutMs: 1e4
2300
2302
  },
2303
+ pricing: {
2304
+ enabled: true,
2305
+ overrides: {}
2306
+ },
2301
2307
  ui: {
2302
2308
  spinner: true,
2303
2309
  toolStyle: "inline",
@@ -2390,7 +2396,10 @@ Use read_file on {path} to see the current content before editing — the target
2390
2396
  "error.http": "HTTP {status}: {statusText}",
2391
2397
  "error.llm_api": "LLM API error {status} ({statusText}): {errorText}",
2392
2398
  "error.llm_retries": "LLM request failed after retries",
2399
+ "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}",
2393
2400
  "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)",
2394
2403
  "session.started": "Session started: {id}",
2395
2404
  "session.ended": "Session ended: {id}",
2396
2405
  "session.not_found": "Session not found: {id}",
@@ -2774,6 +2783,19 @@ Available commands:`,
2774
2783
  "repl.lsp_timeout": "timeout",
2775
2784
  "repl.lsp_failed": "did not start",
2776
2785
  "repl.lsp_unknown": "unknown",
2786
+ "repl.lsp": "LSP server management (status, restart, check)",
2787
+ "repl.lsp_usage": "Usage: /lsp [status|restart|check <path>]",
2788
+ "repl.lsp_not_available": "LSP module not available",
2789
+ "repl.lsp_status_header": "LSP Status:",
2790
+ "repl.lsp_enabled": "Enabled:",
2791
+ "repl.lsp_disabled_servers": "Disabled servers:",
2792
+ "repl.lsp_failure_counts": "Failure counts:",
2793
+ "repl.lsp_all_ok": "All servers operational",
2794
+ "repl.lsp_restarting": "Resetting LSP server state...",
2795
+ "repl.lsp_restarted": "LSP servers reset. Failed servers will be retried on next use.",
2796
+ "repl.lsp_check_usage": "Usage: /lsp check <file-or-directory>",
2797
+ "repl.lsp_checking": "Running LSP check on {path}...",
2798
+ "repl.lsp_check_error": "LSP check failed: {error}",
2777
2799
  "repl.work_dir": "Dir:",
2778
2800
  "repl.agents_label": "Instructions:",
2779
2801
  "repl.not_found": "not found",
@@ -2999,7 +3021,10 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
2999
3021
  "tools.enable_already_active": "Tool tags already active: {tags}.",
3000
3022
  "tools.enable_added": "Enabled tool tags: {tags}. Available tools now: {tools}",
3001
3023
  "tools.hidden_header": "Additional tools (enable on demand via enable_tools or route to subagent tool_tags):",
3002
- "tool.friendly.enable_tools": "Enable tools"
3024
+ "tool.friendly.enable_tools": "Enable tools",
3025
+ "cli.provider_base_hint": "Base URL set to: {baseUrl}",
3026
+ "repl.cost": "Total cost: {cost}",
3027
+ "repl.tokens": "Tokens used: {tokens}"
3003
3028
  };
3004
3029
  });
3005
3030
 
@@ -3049,7 +3074,10 @@ var init_ru = __esm(() => {
3049
3074
  "error.http": "HTTP {status}: {statusText}",
3050
3075
  "error.llm_api": "Ошибка LLM API {status} ({statusText}): {errorText}",
3051
3076
  "error.llm_retries": "Запрос LLM не удался после повторов",
3077
+ "error.llm_429": "Превышен лимит запросов (HTTP 429) для {model} на {baseUrl}. Провайдер ограничивает трафик — особенно строгие free-модели. Получите API-ключ или переключитесь на платную/быструю модель: {baseUrl}",
3052
3078
  "error.no_response_body": "Нет потока тела ответа",
3079
+ "error.llm_stream_idle": "Поток LLM завис — нет данных {timeout}мс",
3080
+ "error.llm_timeout": "Время запроса LLM истекло ({timeout}мс)",
3053
3081
  "session.started": "Сессия начата: {id}",
3054
3082
  "session.ended": "Сессия завершена: {id}",
3055
3083
  "session.not_found": "Сессия не найдена: {id}",
@@ -3428,6 +3456,19 @@ var init_ru = __esm(() => {
3428
3456
  "repl.lsp_timeout": "таймаут",
3429
3457
  "repl.lsp_failed": "не стартовал",
3430
3458
  "repl.lsp_unknown": "неизвестно",
3459
+ "repl.lsp": "Управление LSP-серверами (status, restart, check)",
3460
+ "repl.lsp_usage": "Использование: /lsp [status|restart|check <путь>]",
3461
+ "repl.lsp_not_available": "Модуль LSP недоступен",
3462
+ "repl.lsp_status_header": "Статус LSP:",
3463
+ "repl.lsp_enabled": "Включён:",
3464
+ "repl.lsp_disabled_servers": "Отключённые серверы:",
3465
+ "repl.lsp_failure_counts": "Количество ошибок:",
3466
+ "repl.lsp_all_ok": "Все серверы работают",
3467
+ "repl.lsp_restarting": "Сброс состояния LSP-серверов...",
3468
+ "repl.lsp_restarted": "LSP-серверы сброшены. Упавшие серверы будут повторно запущены при следующем использовании.",
3469
+ "repl.lsp_check_usage": "Использование: /lsp check <файл-или-каталог>",
3470
+ "repl.lsp_checking": "Запуск LSP-проверки {path}...",
3471
+ "repl.lsp_check_error": "LSP-проверка не удалась: {error}",
3431
3472
  "repl.work_dir": "Директория:",
3432
3473
  "repl.agents_label": "Инструкции:",
3433
3474
  "repl.not_found": "не найден",
@@ -3659,7 +3700,10 @@ var init_ru = __esm(() => {
3659
3700
  "tools.enable_already_active": "Теги тулов уже активны: {tags}.",
3660
3701
  "tools.enable_added": "Включены теги тулов: {tags}. Теперь доступны тулы: {tools}",
3661
3702
  "tools.hidden_header": "Дополнительные тулы (включите по требованию через enable_tools или маршрутизируйте через subagent tool_tags):",
3662
- "tool.friendly.enable_tools": "Включить тулы"
3703
+ "tool.friendly.enable_tools": "Включить тулы",
3704
+ "cli.provider_base_hint": "Базовый URL установлен: {baseUrl}",
3705
+ "repl.cost": "Итого потрачено: {cost}",
3706
+ "repl.tokens": "Потрачено токенов: {tokens}"
3663
3707
  };
3664
3708
  });
3665
3709
 
@@ -5160,7 +5204,9 @@ class OpenAICompatProvider {
5160
5204
  this.retryConfig = config.retry ?? {
5161
5205
  maxRetries: 3,
5162
5206
  baseDelay: 1000,
5163
- maxDelay: 30000
5207
+ maxDelay: 30000,
5208
+ maxStreamRetries: 2,
5209
+ noDataTimeoutMs: 60000
5164
5210
  };
5165
5211
  this.rateLimiter = createRateLimiter(config.rateLimits);
5166
5212
  }
@@ -5191,6 +5237,32 @@ class OpenAICompatProvider {
5191
5237
  }
5192
5238
  }
5193
5239
  async* doStream(messages, tools, signal, options) {
5240
+ const { baseDelay, maxDelay, maxStreamRetries, noDataTimeoutMs } = this.retryConfig;
5241
+ const streamRetries = maxStreamRetries ?? 2;
5242
+ const idleTimeoutMs = noDataTimeoutMs ?? 60000;
5243
+ for (let attempt = 0;; attempt++) {
5244
+ let emitted = false;
5245
+ const onEmit = () => {
5246
+ emitted = true;
5247
+ };
5248
+ try {
5249
+ const sawDone = yield* this.streamOnce(messages, tools, signal, options, onEmit, idleTimeoutMs);
5250
+ if (sawDone || emitted)
5251
+ return;
5252
+ if (attempt >= streamRetries)
5253
+ return;
5254
+ } catch (err) {
5255
+ if (err?.name === "AbortError" || err?.llmTerminal || signal?.aborted)
5256
+ throw err;
5257
+ if (emitted || attempt >= streamRetries)
5258
+ throw err;
5259
+ }
5260
+ const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
5261
+ const jitter = Math.random() * baseDelay * 0.1;
5262
+ await this.sleep(delay + jitter, signal);
5263
+ }
5264
+ }
5265
+ async* streamOnce(messages, tools, signal, options, onEmit, idleTimeoutMs) {
5194
5266
  const body = buildRequestBody({
5195
5267
  model: this.model,
5196
5268
  messages,
@@ -5206,8 +5278,11 @@ class OpenAICompatProvider {
5206
5278
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
5207
5279
  }
5208
5280
  const controller = new AbortController;
5209
- const totalTimeoutMs = 120000;
5210
- const timeoutId = setTimeout(() => controller.abort(), totalTimeoutMs);
5281
+ let timedOut = false;
5282
+ const timeoutId = setTimeout(() => {
5283
+ timedOut = true;
5284
+ controller.abort();
5285
+ }, REQUEST_TIMEOUT_MS);
5211
5286
  const abortSignal = (() => {
5212
5287
  if (!signal)
5213
5288
  return controller.signal;
@@ -5220,20 +5295,31 @@ class OpenAICompatProvider {
5220
5295
  return controller.signal;
5221
5296
  }
5222
5297
  })();
5223
- const response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
5224
- method: "POST",
5225
- headers,
5226
- body: JSON.stringify(body),
5227
- signal: abortSignal
5228
- });
5298
+ let response;
5299
+ try {
5300
+ response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
5301
+ method: "POST",
5302
+ headers,
5303
+ body: JSON.stringify(body),
5304
+ signal: abortSignal
5305
+ });
5306
+ } catch (err) {
5307
+ if (err?.name === "AbortError")
5308
+ throw err;
5309
+ const wrapped = err instanceof Error ? err : new Error(String(err));
5310
+ wrapped.llmTerminal = true;
5311
+ throw wrapped;
5312
+ }
5229
5313
  if (!response.ok) {
5230
5314
  clearTimeout(timeoutId);
5231
5315
  const errorText = await response.text();
5232
- throw new Error(t("error.llm_api", {
5316
+ const err = new Error(t("error.llm_api", {
5233
5317
  status: response.status,
5234
5318
  statusText: response.statusText,
5235
5319
  errorText
5236
5320
  }));
5321
+ err.llmTerminal = true;
5322
+ throw err;
5237
5323
  }
5238
5324
  const reader = response.body?.getReader();
5239
5325
  if (!reader) {
@@ -5244,9 +5330,29 @@ class OpenAICompatProvider {
5244
5330
  let buffer = "";
5245
5331
  const toolCallAccs = new Map;
5246
5332
  let usage;
5333
+ let sawDone = false;
5334
+ const readIdle = () => new Promise((resolve, reject) => {
5335
+ const idleTimer = setTimeout(() => {
5336
+ timedOut = true;
5337
+ controller.abort();
5338
+ }, idleTimeoutMs);
5339
+ reader.read().then((result) => {
5340
+ clearTimeout(idleTimer);
5341
+ if (timedOut)
5342
+ reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
5343
+ else
5344
+ resolve(result);
5345
+ }, (err) => {
5346
+ clearTimeout(idleTimer);
5347
+ if (timedOut)
5348
+ reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
5349
+ else
5350
+ reject(err);
5351
+ });
5352
+ });
5247
5353
  try {
5248
5354
  while (true) {
5249
- const { done, value } = await reader.read();
5355
+ const { done, value } = await readIdle();
5250
5356
  if (done)
5251
5357
  break;
5252
5358
  buffer += decoder.decode(value, { stream: true });
@@ -5258,8 +5364,10 @@ class OpenAICompatProvider {
5258
5364
  if (!trimmed || !trimmed.startsWith("data: "))
5259
5365
  continue;
5260
5366
  const data = trimmed.slice(6);
5261
- if (data === "[DONE]")
5367
+ if (data === "[DONE]") {
5368
+ sawDone = true;
5262
5369
  continue;
5370
+ }
5263
5371
  try {
5264
5372
  const parsed = JSON.parse(data);
5265
5373
  const choice = parsed.choices?.[0];
@@ -5276,6 +5384,7 @@ class OpenAICompatProvider {
5276
5384
  const delta = choice.delta || {};
5277
5385
  const finishReason = choice.finish_reason;
5278
5386
  if (delta.reasoning_content) {
5387
+ onEmit();
5279
5388
  yield { type: "reasoning", content: delta.reasoning_content };
5280
5389
  }
5281
5390
  if (delta.tool_calls) {
@@ -5295,11 +5404,13 @@ class OpenAICompatProvider {
5295
5404
  }
5296
5405
  }
5297
5406
  if (delta.content) {
5407
+ onEmit();
5298
5408
  yield { type: "text", content: delta.content };
5299
5409
  }
5300
5410
  if (finishReason === "tool_calls" && toolCallAccs.size > 0) {
5301
5411
  for (const [, acc] of toolCallAccs) {
5302
5412
  if (acc.name) {
5413
+ onEmit();
5303
5414
  yield {
5304
5415
  type: "tool_call",
5305
5416
  toolCall: {
@@ -5316,12 +5427,14 @@ class OpenAICompatProvider {
5316
5427
  }
5317
5428
  }
5318
5429
  if (usage) {
5430
+ onEmit();
5319
5431
  yield { type: "done", usage };
5320
5432
  }
5321
5433
  } finally {
5322
5434
  clearTimeout(timeoutId);
5323
5435
  reader.releaseLock();
5324
5436
  }
5437
+ return sawDone;
5325
5438
  }
5326
5439
  async doNonStreaming(messages, tools, signal, options) {
5327
5440
  const body = buildRequestBody({
@@ -5338,12 +5451,30 @@ class OpenAICompatProvider {
5338
5451
  if (this.config.apiKey && this.config.apiKey !== "not-needed") {
5339
5452
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
5340
5453
  }
5454
+ const controller = new AbortController;
5455
+ let timedOut = false;
5456
+ const timeoutId = setTimeout(() => {
5457
+ timedOut = true;
5458
+ controller.abort();
5459
+ }, REQUEST_TIMEOUT_MS);
5460
+ const abortSignal = (() => {
5461
+ if (!signal)
5462
+ return controller.signal;
5463
+ try {
5464
+ return AbortSignal.any([controller.signal, signal]);
5465
+ } catch {
5466
+ signal.addEventListener("abort", () => controller.abort(), {
5467
+ once: true
5468
+ });
5469
+ return controller.signal;
5470
+ }
5471
+ })();
5341
5472
  try {
5342
5473
  const response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
5343
5474
  method: "POST",
5344
5475
  headers,
5345
5476
  body: JSON.stringify(body),
5346
- signal
5477
+ signal: abortSignal
5347
5478
  });
5348
5479
  if (!response.ok) {
5349
5480
  const errorText = await response.text();
@@ -5390,7 +5521,12 @@ class OpenAICompatProvider {
5390
5521
  }
5391
5522
  return chunks;
5392
5523
  } catch (err) {
5524
+ if (timedOut && err?.name === "AbortError") {
5525
+ throw new Error(t("error.llm_timeout", { timeout: REQUEST_TIMEOUT_MS }));
5526
+ }
5393
5527
  throw err instanceof Error ? err : new Error(String(err));
5528
+ } finally {
5529
+ clearTimeout(timeoutId);
5394
5530
  }
5395
5531
  }
5396
5532
  countTokens(text) {
@@ -5421,26 +5557,36 @@ class OpenAICompatProvider {
5421
5557
  }
5422
5558
  async fetchWithRetry(url, init) {
5423
5559
  const { maxRetries, baseDelay, maxDelay } = this.retryConfig;
5424
- let lastError = null;
5560
+ let lastStatus = 0;
5561
+ let lastRetryAfter = 0;
5425
5562
  for (let attempt = 0;attempt <= maxRetries; attempt++) {
5426
5563
  try {
5427
5564
  const response = await fetch(url, init);
5428
5565
  if (!this.isRetryable(response.status))
5429
5566
  return response;
5430
- lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);
5567
+ lastStatus = response.status;
5568
+ const retryAfter = Number(response.headers.get("retry-after") ?? 0);
5569
+ lastRetryAfter = retryAfter > 0 ? retryAfter * 1000 : 0;
5431
5570
  } catch (err) {
5432
5571
  if (err.name === "AbortError") {
5433
5572
  throw err;
5434
5573
  }
5435
- lastError = err;
5574
+ if (attempt === maxRetries)
5575
+ throw err;
5436
5576
  }
5437
5577
  if (attempt < maxRetries) {
5438
- const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
5578
+ const delay = lastRetryAfter > 0 ? Math.min(lastRetryAfter, maxDelay) : Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
5439
5579
  const jitter = Math.random() * baseDelay * 0.1;
5440
5580
  await this.sleep(delay + jitter, init.signal ?? undefined);
5441
5581
  }
5442
5582
  }
5443
- throw lastError ?? new Error(t("error.llm_retries"));
5583
+ if (lastStatus === 429) {
5584
+ throw new Error(t("error.llm_429", {
5585
+ model: this.model,
5586
+ baseUrl: this.config.baseUrl
5587
+ }));
5588
+ }
5589
+ throw new Error(t("error.llm_retries"));
5444
5590
  }
5445
5591
  isRetryable(status) {
5446
5592
  return status === 429 || status >= 500;
@@ -5464,6 +5610,7 @@ class OpenAICompatProvider {
5464
5610
  });
5465
5611
  }
5466
5612
  }
5613
+ var REQUEST_TIMEOUT_MS = 120000;
5467
5614
  var init_openai_compat = __esm(() => {
5468
5615
  init_token_counter();
5469
5616
  init_i18n();
@@ -11293,6 +11440,145 @@ var init_agent_moe = __esm(() => {
11293
11440
  init_verifier();
11294
11441
  });
11295
11442
 
11443
+ // src/modules/pricing/prices.ts
11444
+ function normalizeModelId(model) {
11445
+ let id = model.trim();
11446
+ for (const prefix of PROVIDER_PREFIXES) {
11447
+ if (id.startsWith(prefix)) {
11448
+ id = id.slice(prefix.length);
11449
+ break;
11450
+ }
11451
+ }
11452
+ const parts = id.split("/");
11453
+ return parts[parts.length - 1];
11454
+ }
11455
+ function resolvePrice(model, config) {
11456
+ if (!config?.enabled)
11457
+ return;
11458
+ const bare = normalizeModelId(model);
11459
+ const overrides = config.overrides ?? {};
11460
+ const direct = overrides[model] ?? overrides[bare];
11461
+ if (direct)
11462
+ return direct;
11463
+ return ZEN_PRICES[bare];
11464
+ }
11465
+ function calculateCost(model, promptTokens, completionTokens, config) {
11466
+ const price = resolvePrice(model, config);
11467
+ if (!price)
11468
+ return;
11469
+ return promptTokens / 1e6 * price.input + completionTokens / 1e6 * price.output;
11470
+ }
11471
+ function formatCost(cost) {
11472
+ if (cost === 0)
11473
+ return "$0.00";
11474
+ if (cost >= 0.01)
11475
+ return `$${cost.toFixed(2)}`;
11476
+ if (cost >= 0.0001)
11477
+ return `$${cost.toFixed(4)}`;
11478
+ return `$${cost.toFixed(6)}`;
11479
+ }
11480
+ var ZEN_PRICES, PROVIDER_PREFIXES;
11481
+ var init_prices = __esm(() => {
11482
+ ZEN_PRICES = {
11483
+ "big-pickle": { input: 0, output: 0 },
11484
+ "mimo-v2.5-free": { input: 0, output: 0 },
11485
+ "hy3-free": { input: 0, output: 0 },
11486
+ "nemotron-3-ultra-free": { input: 0, output: 0 },
11487
+ "nemotron-3.5-lightning-free": { input: 0, output: 0 },
11488
+ "muse-spark-1.2-contributor-free": { input: 0, output: 0 },
11489
+ "minimax-m3": { input: 0.3, output: 1.2 },
11490
+ "minimax-m2.7": { input: 0.3, output: 1.2 },
11491
+ "minimax-m2.5": { input: 0.3, output: 1.2 },
11492
+ "glm-5.2": { input: 1.4, output: 4.4 },
11493
+ "glm-5.1": { input: 1.4, output: 4.4 },
11494
+ "glm-5": { input: 1, output: 3.2 },
11495
+ "glm-5.3": { input: 1.4, output: 4.4 },
11496
+ "kimi-k2.7-code": { input: 0.95, output: 4 },
11497
+ "kimi-k3": { input: 3, output: 15 },
11498
+ "kimi-k2.6": { input: 0.95, output: 4 },
11499
+ "kimi-k2.5": { input: 0.6, output: 3 },
11500
+ "qwen3.7-max": { input: 2.5, output: 7.5 },
11501
+ "qwen3.7-plus": { input: 0.4, output: 1.6 },
11502
+ "qwen3.6-plus": { input: 0.5, output: 3 },
11503
+ "qwen3.5-plus": { input: 0.2, output: 1.2 },
11504
+ "qwen3.8-max": { input: 2.5, output: 7.5 },
11505
+ "deepseek-v4-pro": { input: 0.66, output: 1.98 },
11506
+ "deepseek-v4-flash": { input: 0.22, output: 0.66 },
11507
+ "claude-fable-5": { input: 10, output: 50 },
11508
+ "claude-opus-5": { input: 5, output: 25 },
11509
+ "claude-opus-4-8": { input: 5, output: 25 },
11510
+ "claude-opus-4-7": { input: 5, output: 25 },
11511
+ "claude-opus-4-6": { input: 5, output: 25 },
11512
+ "claude-opus-4-5": { input: 5, output: 25 },
11513
+ "claude-sonnet-5": { input: 2, output: 10 },
11514
+ "claude-sonnet-4-6": { input: 3, output: 15 },
11515
+ "claude-sonnet-4-5": { input: 3, output: 15 },
11516
+ "claude-haiku-4-5": { input: 1, output: 5 },
11517
+ "gemini-3.7-flash": { input: 1.5, output: 7.5 },
11518
+ "gemini-3.6-flash": { input: 1.5, output: 7.5 },
11519
+ "gemini-3.5-flash": { input: 1.5, output: 9 },
11520
+ "gemini-3.5-flash-lite": { input: 0.3, output: 2.5 },
11521
+ "gemini-3.1-pro": { input: 2, output: 12 },
11522
+ "gemini-3-flash": { input: 0.5, output: 3 },
11523
+ "grok-4.6": { input: 2, output: 6 },
11524
+ "grok-4.5": { input: 2, output: 6 },
11525
+ "grok-build-0.1": { input: 1, output: 2 },
11526
+ "muse-spark-1.2": { input: 1.25, output: 4.25 },
11527
+ "gpt-5.6-sol": { input: 5, output: 30 },
11528
+ "gpt-5.6-terra": { input: 2, output: 12 },
11529
+ "gpt-5.6-luna": { input: 0.2, output: 1.2 },
11530
+ "gpt-5.5": { input: 5, output: 30 },
11531
+ "gpt-5.5-pro": { input: 30, output: 180 },
11532
+ "gpt-5.4": { input: 2.5, output: 15 },
11533
+ "gpt-5.4-pro": { input: 30, output: 180 },
11534
+ "gpt-5.4-mini": { input: 0.75, output: 4.5 },
11535
+ "gpt-5.4-nano": { input: 0.2, output: 1.25 },
11536
+ "gpt-5.3-codex": { input: 1.75, output: 14 },
11537
+ "gpt-5.3-codex-spark": { input: 1.75, output: 14 },
11538
+ "gpt-5.2": { input: 1.75, output: 14 },
11539
+ "gpt-5.2-codex": { input: 1.75, output: 14 },
11540
+ "gpt-5.1": { input: 1.07, output: 8.5 },
11541
+ "gpt-5.1-codex": { input: 1.07, output: 8.5 },
11542
+ "gpt-5.1-codex-max": { input: 1.25, output: 10 },
11543
+ "gpt-5.1-codex-mini": { input: 0.25, output: 2 },
11544
+ "gpt-5": { input: 1.07, output: 8.5 },
11545
+ "gpt-5-codex": { input: 1.07, output: 8.5 },
11546
+ "gpt-5-nano": { input: 0.05, output: 0.4 }
11547
+ };
11548
+ PROVIDER_PREFIXES = ["opencode-go/", "opencode/", "openai/", "anthropic/"];
11549
+ });
11550
+
11551
+ // src/modules/pricing/index.ts
11552
+ class CostTracker {
11553
+ config;
11554
+ model;
11555
+ _total = 0;
11556
+ _known = false;
11557
+ constructor(model, config) {
11558
+ this.model = model;
11559
+ this.config = config;
11560
+ }
11561
+ setModel(model) {
11562
+ this.model = model;
11563
+ }
11564
+ record(promptTokens, completionTokens) {
11565
+ const cost = calculateCost(this.model, promptTokens, completionTokens, this.config);
11566
+ if (cost === undefined)
11567
+ return;
11568
+ this._known = true;
11569
+ this._total += cost;
11570
+ }
11571
+ get total() {
11572
+ return this._known ? this._total : undefined;
11573
+ }
11574
+ get formatted() {
11575
+ return this._known ? formatCost(this._total) : undefined;
11576
+ }
11577
+ }
11578
+ var init_pricing = __esm(() => {
11579
+ init_prices();
11580
+ });
11581
+
11296
11582
  // src/core/agent.ts
11297
11583
  function isToolCallJson(text) {
11298
11584
  const trimmed = text.trim();
@@ -11319,8 +11605,10 @@ class Agent {
11319
11605
  shutdownRequested = false;
11320
11606
  abortController = null;
11321
11607
  lastCompactionShown = 0;
11608
+ costTracker;
11322
11609
  constructor(deps) {
11323
11610
  this.deps = deps;
11611
+ this.costTracker = new CostTracker(deps.config.model, deps.config.pricing);
11324
11612
  }
11325
11613
  get contextManager() {
11326
11614
  return this.deps.contextManager;
@@ -11424,6 +11712,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11424
11712
  onPhase?.(phase);
11425
11713
  }
11426
11714
  async run(input, onChunk, onMeta, onTool, onPhase) {
11715
+ this.shutdownRequested = false;
11427
11716
  this.setScope();
11428
11717
  const {
11429
11718
  config,
@@ -11676,6 +11965,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11676
11965
  source,
11677
11966
  durationMs: Date.now() - llmStart
11678
11967
  });
11968
+ this.costTracker.record(prompt, completion);
11679
11969
  }
11680
11970
  if (this.shutdownRequested) {
11681
11971
  break;
@@ -11791,7 +12081,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11791
12081
  args: call.arguments,
11792
12082
  duration,
11793
12083
  error: !result.success,
11794
- ctxDelta: tokensAfterTool - tokensBeforeTool
12084
+ ctxDelta: tokensAfterTool - tokensBeforeTool,
12085
+ costUsd: this.costTracker.total
11795
12086
  });
11796
12087
  summaries.push(`[Tool: ${call.name} (${JSON.stringify(call.arguments)}) → ${truncatedOutput.slice(0, 200)}]`);
11797
12088
  if (config.session.autoSave) {
@@ -12026,6 +12317,7 @@ ${warnLine}
12026
12317
  promptTokens: usageTokens.prompt,
12027
12318
  completionTokens: usageTokens.completion,
12028
12319
  totalTokens: usageTokens.total,
12320
+ totalCost: this.costTracker.total,
12029
12321
  compactionCount: contextManager.getCompactionCount(),
12030
12322
  contextQuality: contextManager.getQuality()
12031
12323
  };
@@ -12040,6 +12332,7 @@ ${warnLine}
12040
12332
  promptTokens: usageTokens.prompt,
12041
12333
  completionTokens: usageTokens.completion,
12042
12334
  totalTokens: usageTokens.total,
12335
+ totalCost: this.costTracker.total,
12043
12336
  compactionCount: contextManager.getCompactionCount(),
12044
12337
  contextQuality: contextManager.getQuality()
12045
12338
  };
@@ -12065,6 +12358,7 @@ ${warnLine}
12065
12358
  const newTokenCounter = new TokenCounter2(config.model);
12066
12359
  this.deps.contextManager.resize(config.contextWindow, config.contextBudget, newTokenCounter);
12067
12360
  this.deps.config = config;
12361
+ this.costTracker.setModel(config.model);
12068
12362
  }
12069
12363
  setContext(messages) {
12070
12364
  const { contextManager } = this.deps;
@@ -12091,7 +12385,6 @@ ${warnLine}
12091
12385
  if (killed > 0) {
12092
12386
  logger.info(`Killed ${killed} background process(es) on shutdown`);
12093
12387
  }
12094
- logger.closeSessionLog();
12095
12388
  pluginManager.runOnSessionEnd({
12096
12389
  logger,
12097
12390
  sessionManager: sessionManager?.getActiveMeta(),
@@ -12108,6 +12401,7 @@ var init_agent = __esm(() => {
12108
12401
  init_session_logger();
12109
12402
  init_agent_moe();
12110
12403
  init_verifier();
12404
+ init_pricing();
12111
12405
  });
12112
12406
 
12113
12407
  // src/modules/context/fact-extractor.ts
@@ -19746,6 +20040,10 @@ import { resolve as resolve20 } from "path";
19746
20040
  import { platform as platform8 } from "os";
19747
20041
 
19748
20042
  class LspClient {
20043
+ logger = null;
20044
+ setLogger(logger) {
20045
+ this.logger = logger;
20046
+ }
19749
20047
  process = null;
19750
20048
  requestId = 0;
19751
20049
  pending = new Map;
@@ -19805,11 +20103,25 @@ class LspClient {
19805
20103
  try {
19806
20104
  await this.sendRequest("initialize", initParams, timeout);
19807
20105
  } catch (e) {
20106
+ const msg = e instanceof Error ? e.message : String(e);
20107
+ this.logger?.warn(`LSP initialize failed (attempt 1): ${msg}`, {
20108
+ command: config.command,
20109
+ projectRoot
20110
+ });
19808
20111
  if (!(e instanceof Error) || !e.message.includes("initialize"))
19809
20112
  throw e;
19810
20113
  await this.shutdown();
19811
20114
  await this.startServer(config, projectRoot);
19812
- await this.sendRequest("initialize", initParams, timeout);
20115
+ try {
20116
+ await this.sendRequest("initialize", initParams, timeout);
20117
+ } catch (retryErr) {
20118
+ const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
20119
+ this.logger?.error(`LSP initialize failed (attempt 2, giving up): ${retryMsg}`, {
20120
+ command: config.command,
20121
+ projectRoot
20122
+ });
20123
+ throw retryErr;
20124
+ }
19813
20125
  }
19814
20126
  this.initialized = true;
19815
20127
  }
@@ -19847,8 +20159,11 @@ class LspClient {
19847
20159
  });
19848
20160
  this.process = proc;
19849
20161
  setTimeout(() => {
19850
- if (!this.initialized && this.process)
19851
- reject(new Error("LSP server start timeout"));
20162
+ if (!this.initialized && this.process) {
20163
+ const msg = "LSP server start timeout";
20164
+ this.logger?.error(msg, { command: config.command, projectRoot, timeout: config.timeout ?? 1e4 });
20165
+ reject(new Error(msg));
20166
+ }
19852
20167
  }, config.timeout ?? 1e4);
19853
20168
  });
19854
20169
  }
@@ -19887,7 +20202,9 @@ class LspClient {
19887
20202
  try {
19888
20203
  const msg = JSON.parse(body);
19889
20204
  this.handleMessage(msg);
19890
- } catch {}
20205
+ } catch (e) {
20206
+ this.logger?.debug(`LSP JSON parse error: ${e instanceof Error ? e.message : String(e)}`);
20207
+ }
19891
20208
  }
19892
20209
  }
19893
20210
  handleMessage(msg) {
@@ -19925,7 +20242,9 @@ class LspClient {
19925
20242
  setTimeout(() => {
19926
20243
  if (this.pending.has(id)) {
19927
20244
  this.pending.delete(id);
19928
- reject(new Error(`LSP request timeout: ${method}`));
20245
+ const msg = `LSP request timeout: ${method}`;
20246
+ this.logger?.warn(msg, { method, timeout });
20247
+ reject(new Error(msg));
19929
20248
  }
19930
20249
  }, timeout);
19931
20250
  });
@@ -19935,27 +20254,37 @@ class LspClient {
19935
20254
  this.write(message);
19936
20255
  }
19937
20256
  write(message) {
19938
- if (!this.process?.stdin?.writable)
20257
+ if (!this.process?.stdin?.writable) {
20258
+ this.logger?.debug("LSP write failed: process stdin not writable");
19939
20259
  return;
20260
+ }
19940
20261
  const header = `Content-Length: ${Buffer.byteLength(message)}\r
19941
20262
  \r
19942
20263
  `;
19943
20264
  try {
19944
20265
  this.process.stdin.write(header + message);
19945
- } catch {}
20266
+ } catch (e) {
20267
+ this.logger?.debug(`LSP write error: ${e instanceof Error ? e.message : String(e)}`);
20268
+ }
19946
20269
  }
19947
20270
  async shutdown() {
19948
20271
  try {
19949
20272
  if (this.process && this.initialized && this.process.stdin?.writable) {
19950
- this.sendRequest("shutdown", null, 3000).catch(() => {});
20273
+ this.sendRequest("shutdown", null, 3000).catch((e) => {
20274
+ this.logger?.debug(`LSP shutdown request failed: ${e instanceof Error ? e.message : String(e)}`);
20275
+ });
19951
20276
  this.sendNotification("exit", null);
19952
20277
  await new Promise((r) => setTimeout(r, 200));
19953
20278
  }
19954
- } catch {} finally {
20279
+ } catch (e) {
20280
+ this.logger?.debug(`LSP shutdown error: ${e instanceof Error ? e.message : String(e)}`);
20281
+ } finally {
19955
20282
  if (this.process) {
19956
20283
  try {
19957
20284
  killTree(this.process);
19958
- } catch {}
20285
+ } catch (e) {
20286
+ this.logger?.debug(`LSP killTree error: ${e instanceof Error ? e.message : String(e)}`);
20287
+ }
19959
20288
  this.process = null;
19960
20289
  }
19961
20290
  this.initialized = false;
@@ -20063,9 +20392,26 @@ class LspModule {
20063
20392
  client;
20064
20393
  failuresByServer = new Map;
20065
20394
  disabledServers = new Set;
20066
- constructor(config, client) {
20395
+ logger = null;
20396
+ constructor(config, client, logger) {
20067
20397
  this.config = { ...DEFAULT_LSP_CONFIG, ...config };
20068
20398
  this.client = client ?? new LspClient;
20399
+ this.logger = logger ?? null;
20400
+ this.client.setLogger(this.logger);
20401
+ }
20402
+ setLogger(logger) {
20403
+ this.logger = logger;
20404
+ this.client.setLogger(logger);
20405
+ }
20406
+ resetDisabledServers() {
20407
+ this.failuresByServer.clear();
20408
+ this.disabledServers.clear();
20409
+ }
20410
+ getDisabledServers() {
20411
+ return Array.from(this.disabledServers);
20412
+ }
20413
+ getFailureCounts() {
20414
+ return new Map(this.failuresByServer);
20069
20415
  }
20070
20416
  isLspDisabled() {
20071
20417
  return this.disabledServers.size > 0;
@@ -20336,7 +20682,9 @@ async function runCheck(config, baseDir, deps) {
20336
20682
  lines.push(formatStartupError(file, baseDir, d));
20337
20683
  }
20338
20684
  }
20339
- } catch {}
20685
+ } catch (e) {
20686
+ deps.logger?.debug(`Startup LSP check failed for ${file}: ${e instanceof Error ? e.message : String(e)}`);
20687
+ }
20340
20688
  }
20341
20689
  if (checked === 0 || lines.length === 0)
20342
20690
  return null;
@@ -21427,7 +21775,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
21427
21775
  pluginManager.register(browserPlugin);
21428
21776
  }
21429
21777
  }
21430
- const lspModule = new LspModule(config.lsp);
21778
+ const lspModule = new LspModule(config.lsp, undefined, logger);
21431
21779
  moduleRegistry.register(lspModule);
21432
21780
  const lspPlugin = lspModule.getPlugin();
21433
21781
  if (lspPlugin) {
@@ -22264,10 +22612,8 @@ async function runSetup(externalRl) {
22264
22612
  console.log(t("setup.no_servers"));
22265
22613
  apiBase = await ask(rl, t("setup.api_base"), "http://localhost:1234/v1");
22266
22614
  }
22267
- } else if (provider === "openai") {
22268
- apiBase = "https://api.openai.com/v1";
22269
- } else if (provider === "anthropic") {
22270
- apiBase = "https://api.anthropic.com";
22615
+ } else {
22616
+ apiBase = HOSTED_BASE_URLS[provider] ?? "";
22271
22617
  }
22272
22618
  const defaultKey = provider === "openai-compat" ? "not-needed" : "";
22273
22619
  const apiKey = await ask(rl, t("setup.api_key"), defaultKey);
@@ -22332,7 +22678,7 @@ async function runSetup(externalRl) {
22332
22678
  console.log(l);
22333
22679
  return answers;
22334
22680
  }
22335
- var PROVIDER_TYPES, KNOWN_PORTS;
22681
+ var PROVIDER_TYPES, KNOWN_PORTS, HOSTED_BASE_URLS;
22336
22682
  var init_setup = __esm(() => {
22337
22683
  init_colors();
22338
22684
  init_i18n();
@@ -22345,7 +22691,9 @@ var init_setup = __esm(() => {
22345
22691
  label: "OpenAI-compatible (LM Studio, Ollama, vLLM, etc.)"
22346
22692
  },
22347
22693
  { value: "openai", label: "OpenAI API" },
22348
- { value: "anthropic", label: "Anthropic Claude" }
22694
+ { value: "anthropic", label: "Anthropic Claude" },
22695
+ { value: "opencode-zen", label: "OpenCode Zen (opencode.ai/zen)" },
22696
+ { value: "opencode-go", label: "OpenCode Go (opencode.ai/go)" }
22349
22697
  ];
22350
22698
  KNOWN_PORTS = [
22351
22699
  { port: 1234, label: "LM Studio" },
@@ -22353,6 +22701,12 @@ var init_setup = __esm(() => {
22353
22701
  { port: 8080, label: "vLLM / custom" },
22354
22702
  { port: 4891, label: "GPT4All" }
22355
22703
  ];
22704
+ HOSTED_BASE_URLS = {
22705
+ openai: "https://api.openai.com/v1",
22706
+ anthropic: "https://api.anthropic.com",
22707
+ "opencode-zen": "https://opencode.ai/zen/v1",
22708
+ "opencode-go": "https://opencode.ai/zen/go/v1"
22709
+ };
22356
22710
  });
22357
22711
 
22358
22712
  // src/modules/certification/manifest.ts
@@ -30445,6 +30799,70 @@ Excluded blocks: ${info.excluded.length}`));
30445
30799
  }
30446
30800
  }
30447
30801
  });
30802
+ ctx.registerCommand({
30803
+ name: "lsp",
30804
+ description: t("repl.lsp"),
30805
+ usage: t("repl.lsp_usage"),
30806
+ action: async (args) => {
30807
+ const subcommand = args[0] || "status";
30808
+ const lspModule = ctx.agent.deps?.moduleRegistry?.getModules?.()?.find((m) => m.name === "lsp");
30809
+ if (!lspModule) {
30810
+ console.log(pc2.yellow(t("repl.lsp_not_available")));
30811
+ return;
30812
+ }
30813
+ switch (subcommand) {
30814
+ case "status": {
30815
+ const disabled = lspModule.getDisabledServers();
30816
+ const failures = lspModule.getFailureCounts();
30817
+ const config = ctx.config.lsp;
30818
+ console.log(pc2.bold(t("repl.lsp_status_header")));
30819
+ console.log(`${t("repl.lsp_enabled")} ${config?.enabled ? pc2.green("yes") : pc2.red("no")}`);
30820
+ if (disabled.length > 0) {
30821
+ console.log(pc2.yellow(`${t("repl.lsp_disabled_servers")} ${disabled.join(", ")}`));
30822
+ }
30823
+ if (failures.size > 0) {
30824
+ console.log(pc2.dim(t("repl.lsp_failure_counts")));
30825
+ for (const [server, count] of failures) {
30826
+ console.log(pc2.dim(` ${server}: ${count}`));
30827
+ }
30828
+ }
30829
+ if (disabled.length === 0 && failures.size === 0) {
30830
+ console.log(pc2.green(t("repl.lsp_all_ok")));
30831
+ }
30832
+ break;
30833
+ }
30834
+ case "restart": {
30835
+ console.log(pc2.yellow(t("repl.lsp_restarting")));
30836
+ lspModule.resetDisabledServers();
30837
+ console.log(pc2.green(t("repl.lsp_restarted")));
30838
+ break;
30839
+ }
30840
+ case "check": {
30841
+ const path = args[1];
30842
+ if (!path) {
30843
+ console.log(pc2.yellow(t("repl.lsp_check_usage")));
30844
+ return;
30845
+ }
30846
+ console.log(pc2.dim(t("repl.lsp_checking", { path })));
30847
+ try {
30848
+ const result = await ctx.agent.deps?.toolExecutor?.execute("lsp_check", { path }, ctx.agent.deps?.toolCtx);
30849
+ if (result?.output) {
30850
+ console.log(result.output);
30851
+ }
30852
+ } catch (e) {
30853
+ console.log(pc2.red(t("repl.lsp_check_error", {
30854
+ error: e instanceof Error ? e.message : String(e)
30855
+ })));
30856
+ }
30857
+ break;
30858
+ }
30859
+ default: {
30860
+ console.log(pc2.dim(t("repl.lsp_usage")));
30861
+ break;
30862
+ }
30863
+ }
30864
+ }
30865
+ });
30448
30866
  }
30449
30867
  function registerSessionCommands(ctx) {
30450
30868
  if (!ctx.sessionManager)
@@ -30699,6 +31117,7 @@ var init_repl_commands = __esm(() => {
30699
31117
  reload: "agent",
30700
31118
  wizard: "agent",
30701
31119
  sysprompt: "agent",
31120
+ lsp: "agent",
30702
31121
  sessions: "session",
30703
31122
  new: "session",
30704
31123
  resume: "session",
@@ -31367,6 +31786,7 @@ function createPluginCommand(program2) {
31367
31786
  }
31368
31787
 
31369
31788
  // src/cli/commands.ts
31789
+ init_setup();
31370
31790
  import { fileURLToPath as fileURLToPath4 } from "url";
31371
31791
  function readVersion2() {
31372
31792
  const here = dirname16(fileURLToPath4(import.meta.url));
@@ -31552,8 +31972,15 @@ function createProgram() {
31552
31972
  const configPath = join44(homedir15(), ".mma", "config.json");
31553
31973
  const { config } = await bootstrap();
31554
31974
  config.provider.type = name;
31975
+ const baseUrl = HOSTED_BASE_URLS[name];
31976
+ if (baseUrl) {
31977
+ config.provider.baseUrl = baseUrl;
31978
+ }
31555
31979
  saveConfig(config, configPath);
31556
31980
  console.log(t("cli.provider_set", { name }));
31981
+ if (baseUrl) {
31982
+ console.log(t("cli.provider_base_hint", { baseUrl }));
31983
+ }
31557
31984
  });
31558
31985
  const session2 = program2.command("session").description(t("cli.manage_sessions"));
31559
31986
  session2.command("list").description(t("cli.list_sessions")).action(async () => {
@@ -32358,9 +32785,8 @@ class LineEditor {
32358
32785
 
32359
32786
  // src/cli/repl.ts
32360
32787
  import { existsSync as existsSync53, readFileSync as readFileSync35, writeFileSync as writeFileSync17 } from "fs";
32361
- import { join as join47, dirname as dirname18 } from "path";
32788
+ import { join as join47 } from "path";
32362
32789
  import { homedir as homedir17 } from "os";
32363
- import { fileURLToPath as fileURLToPath6 } from "url";
32364
32790
 
32365
32791
  // src/cli/completer.ts
32366
32792
  class SlashCommandProvider {
@@ -32664,6 +33090,10 @@ init_spinner();
32664
33090
  init_box();
32665
33091
  init_table();
32666
33092
  init_i18n();
33093
+ init_prices();
33094
+ function formatUsd(cost) {
33095
+ return formatCost(cost);
33096
+ }
32667
33097
  var GUTTER = " ";
32668
33098
  var BUSY_TOOLS = new Set(["lsp_check"]);
32669
33099
  function toolMarker(tool) {
@@ -32800,14 +33230,20 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
32800
33230
  `);
32801
33231
  }
32802
33232
  }
32803
- toolEnd(_tool, duration, error, ctxDelta) {
33233
+ toolEnd(_tool, duration, error, ctxDelta, costUsd) {
32804
33234
  this.spinner.stop();
32805
33235
  if (!this.rich) {
33236
+ const parts = [];
32806
33237
  if (ctxDelta !== undefined && ctxDelta !== 0) {
32807
33238
  const deltaStr = ctxDelta > 0 ? pc2.green(`+${ctxDelta}`) : pc2.yellow(`${ctxDelta} ↓`);
32808
- this.out.write(`${pc2.dim("ctx")} ${deltaStr}
32809
- `);
33239
+ parts.push(`${pc2.dim("ctx")} ${deltaStr}`);
33240
+ }
33241
+ if (costUsd !== undefined && costUsd > 0) {
33242
+ parts.push(`${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`);
32810
33243
  }
33244
+ if (parts.length > 0)
33245
+ this.out.write(`${parts.join(" ")}
33246
+ `);
32811
33247
  return;
32812
33248
  }
32813
33249
  if (!this.card)
@@ -32819,6 +33255,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
32819
33255
  const deltaStr = ctxDelta > 0 ? pc2.green(`+${ctxDelta}`) : pc2.yellow(`${ctxDelta} ↓`);
32820
33256
  footer += ` ${pc2.dim("ctx")} ${deltaStr}`;
32821
33257
  }
33258
+ if (costUsd !== undefined && costUsd > 0) {
33259
+ footer += ` ${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`;
33260
+ }
32822
33261
  if (this.toolStyle === "inline") {
32823
33262
  this.out.write(`${GUTTER}${footer}
32824
33263
  `);
@@ -32918,16 +33357,24 @@ async function probeLspServers(config, baseDir, deps = {}) {
32918
33357
  await probeClient.probe(server, projectRoot, serverTimeout);
32919
33358
  ok.push({ language, ok: true, durationMs: Date.now() - started });
32920
33359
  } catch (e) {
33360
+ const errorMsg = e instanceof Error ? e.message : String(e);
33361
+ deps.logger?.warn(`LSP probe failed for ${language}: ${errorMsg}`, {
33362
+ command: server.command,
33363
+ timeout: serverTimeout,
33364
+ durationMs: Date.now() - started
33365
+ });
32921
33366
  failed.push({
32922
33367
  language,
32923
33368
  ok: false,
32924
- error: e instanceof Error ? e.message : String(e),
33369
+ error: errorMsg,
32925
33370
  durationMs: Date.now() - started
32926
33371
  });
32927
33372
  }
32928
33373
  };
32929
33374
  for (let i = 0;i < servers.length; i += maxParallel) {
32930
33375
  if (Date.now() >= deadline) {
33376
+ const remaining = servers.slice(i).map((s) => s.language);
33377
+ deps.logger?.warn(`LSP probe deadline reached, skipping: ${remaining.join(", ")}`);
32931
33378
  for (const { language } of servers.slice(i)) {
32932
33379
  failed.push({ language, ok: false, error: "timeout", durationMs: 0 });
32933
33380
  }
@@ -33046,21 +33493,7 @@ function stepContextForTool(plan, tool, args) {
33046
33493
  }
33047
33494
 
33048
33495
  // src/cli/repl.ts
33049
- function readVersion4() {
33050
- const here = dirname18(fileURLToPath6(import.meta.url));
33051
- const candidates = [join47(here, "..", "..", "package.json"), join47(here, "..", "package.json")];
33052
- for (const p of candidates) {
33053
- if (existsSync53(p)) {
33054
- try {
33055
- const raw = JSON.parse(readFileSync35(p, "utf8"));
33056
- if (raw.version)
33057
- return raw.version;
33058
- } catch {}
33059
- }
33060
- }
33061
- return "0.0.0";
33062
- }
33063
- var version3 = readVersion4();
33496
+ init_prices();
33064
33497
  function formatContextBar(used, limit, compactions, quality) {
33065
33498
  const pct = Math.min(100, Math.round(used / limit * 100));
33066
33499
  const barLen = 20;
@@ -33240,6 +33673,7 @@ class Repl {
33240
33673
  this.running = false;
33241
33674
  this.saveHistory();
33242
33675
  this.agent.shutdown();
33676
+ this.logger?.closeSessionLog();
33243
33677
  if (this.exitOnClose)
33244
33678
  process.exit(0);
33245
33679
  });
@@ -33248,6 +33682,7 @@ class Repl {
33248
33682
  this.running = false;
33249
33683
  this.saveHistory();
33250
33684
  this.agent.shutdown();
33685
+ this.logger?.closeSessionLog();
33251
33686
  process.exit(0);
33252
33687
  });
33253
33688
  }
@@ -33352,7 +33787,7 @@ ${t("image.clipboard_empty")}`));
33352
33787
  if (ev.type === "start") {
33353
33788
  renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.activePlan, ev.tool, ev.args), ev.icon);
33354
33789
  } else {
33355
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
33790
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
33356
33791
  if (ev.tool === "plan" || ev.tool === "todo") {
33357
33792
  this.refreshActivePlan(renderer);
33358
33793
  }
@@ -33471,6 +33906,9 @@ ${t("image.clipboard_empty")}`));
33471
33906
  const apiLine = pc2.dim(` API: ${result.promptTokens} prompt + ${result.completionTokens} completion = ${result.totalTokens} total`);
33472
33907
  console.log(apiLine);
33473
33908
  }
33909
+ if (result.totalCost !== undefined && result.totalCost > 0) {
33910
+ console.log(pc2.yellow(` ${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
33911
+ }
33474
33912
  } else if (ui?.showCompaction && result.compactionCount !== undefined) {
33475
33913
  if (result.compactionCount > this.lastCompactionShown) {
33476
33914
  this.lastCompactionShown = result.compactionCount;
@@ -33502,9 +33940,9 @@ ${t("image.clipboard_empty")}`));
33502
33940
  const infos = this.pluginManager.getPluginInfos().filter(({ plugin: plugin3 }) => !plugin3.isBuiltin);
33503
33941
  if (infos.length > 0) {
33504
33942
  const pluginNames = infos.map(({ plugin: plugin3, source }) => {
33505
- const version4 = plugin3.version ? `${pc2.dim(plugin3.version)}` : "";
33943
+ const version3 = plugin3.version ? `${pc2.dim(plugin3.version)}` : "";
33506
33944
  const origin = source ? pc2.dim(` [${source}]`) : "";
33507
- return `${plugin3.name}${version4}${origin}`;
33945
+ return `${plugin3.name}${version3}${origin}`;
33508
33946
  }).join(", ");
33509
33947
  row(t("repl.plugins_label"), `${pc2.white(String(infos.length))} active ${pc2.dim(`(${pluginNames})`)}`);
33510
33948
  }
@@ -33545,33 +33983,27 @@ ${t("image.clipboard_empty")}`));
33545
33983
  if (lspEnabled) {
33546
33984
  row(t("repl.lsp_label"), pc2.dim("…"));
33547
33985
  }
33548
- const boxLines = box(info, {
33549
- title: t("repl.title", { version: version3 }),
33550
- width: headerWidth
33551
- });
33552
- for (const line of boxLines) {
33553
- console.log(line);
33986
+ for (const line of info) {
33987
+ console.log(pc2.dim(line).trimEnd());
33554
33988
  }
33555
33989
  console.log();
33556
33990
  this.rl.prompt();
33557
33991
  if (lspEnabled) {
33558
33992
  this.probeLspBanner().then((lspSummary) => {
33559
33993
  if (lspSummary)
33560
- this.updateLspRow(boxLines.length, headerWidth, lspSummary);
33994
+ this.updateLspRow(headerWidth, lspSummary);
33561
33995
  }).catch(() => {});
33562
33996
  }
33563
33997
  }
33564
- updateLspRow(boxLineCount, headerWidth, summary) {
33998
+ updateLspRow(headerWidth, summary) {
33565
33999
  if (this.agentRunning || !process.stdout.isTTY)
33566
34000
  return;
33567
34001
  if ((process.stdout.columns ?? 96) < headerWidth)
33568
34002
  return;
33569
- const inner = headerWidth - 4;
33570
34003
  const rowText = `${pc2.yellow(t("repl.lsp_label"))} ${summary.replace(/\s+/g, " ").trim()}`;
33571
- if (stringWidth(rowText) > inner)
34004
+ if (stringWidth(rowText) > headerWidth)
33572
34005
  return;
33573
- const padded = rowText + " ".repeat(Math.max(0, inner - stringWidth(rowText)));
33574
- const line = pc2.dim("│") + " " + padded + " " + pc2.dim("│");
34006
+ const line = pc2.dim(rowText);
33575
34007
  process.stdout.write(`\x1B[2A\r\x1B[2K${line}\x1B[2B\r`);
33576
34008
  }
33577
34009
  async probeLspBanner() {
@@ -33597,6 +34029,7 @@ ${t("image.clipboard_empty")}`));
33597
34029
  this.running = false;
33598
34030
  this.saveHistory();
33599
34031
  this.agent.shutdown();
34032
+ this.logger?.closeSessionLog();
33600
34033
  this.rl.close();
33601
34034
  }
33602
34035
  }
@@ -33604,9 +34037,15 @@ ${t("image.clipboard_empty")}`));
33604
34037
  // src/cli/run-result.ts
33605
34038
  init_i18n();
33606
34039
  init_colors();
34040
+ init_prices();
33607
34041
  function printRunResult(result, flush) {
33608
34042
  flush();
33609
34043
  if (result.success) {
34044
+ if (result.totalCost !== undefined && result.totalCost > 0) {
34045
+ console.log(pc2.dim(`${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
34046
+ } else if (result.totalTokens !== undefined && result.totalTokens > 0) {
34047
+ console.log(pc2.dim(`${t("repl.tokens", { tokens: result.totalTokens })}`));
34048
+ }
33610
34049
  if (!result.text) {
33611
34050
  console.log(pc2.yellow(t("cli.no_output")));
33612
34051
  }
@@ -33622,9 +34061,9 @@ init_config2();
33622
34061
  init_i18n();
33623
34062
  init_colors();
33624
34063
  import { existsSync as existsSync54, readFileSync as readFileSync36 } from "fs";
33625
- import { join as join48, dirname as dirname19 } from "path";
34064
+ import { join as join48, dirname as dirname18 } from "path";
33626
34065
  import { homedir as homedir18 } from "os";
33627
- import { fileURLToPath as fileURLToPath7 } from "url";
34066
+ import { fileURLToPath as fileURLToPath6 } from "url";
33628
34067
 
33629
34068
  // src/modules/updater/index.ts
33630
34069
  init_checker();
@@ -33724,8 +34163,8 @@ class UpdaterModule {
33724
34163
  }
33725
34164
  }
33726
34165
  // src/cli/main.ts
33727
- function readVersion5() {
33728
- const here = dirname19(fileURLToPath7(import.meta.url));
34166
+ function readVersion4() {
34167
+ const here = dirname18(fileURLToPath6(import.meta.url));
33729
34168
  const candidates = [join48(here, "..", "..", "package.json"), join48(here, "..", "package.json")];
33730
34169
  for (const p of candidates) {
33731
34170
  if (existsSync54(p)) {
@@ -33740,7 +34179,7 @@ function readVersion5() {
33740
34179
  }
33741
34180
  function startAutoUpdate(config) {
33742
34181
  try {
33743
- const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent", {
34182
+ const module = new UpdaterModule(config.updater, readVersion4(), "micro-models-agent", {
33744
34183
  info: (m) => process.stderr.write(pc2.dim(m) + `
33745
34184
  `),
33746
34185
  warn: (m) => process.stderr.write(pc2.yellow(m) + `
@@ -33783,7 +34222,8 @@ async function main() {
33783
34222
  contextLimit: result2.contextLimit ?? null,
33784
34223
  promptTokens: result2.promptTokens ?? null,
33785
34224
  completionTokens: result2.completionTokens ?? null,
33786
- totalTokens: result2.totalTokens ?? null
34225
+ totalTokens: result2.totalTokens ?? null,
34226
+ totalCost: result2.totalCost ?? null
33787
34227
  }, null, 2));
33788
34228
  process.stdout.write(`
33789
34229
  `);
@@ -33798,7 +34238,7 @@ async function main() {
33798
34238
  if (ev.type === "start") {
33799
34239
  renderer.toolStart(ev.tool, ev.args, undefined, ev.icon);
33800
34240
  } else {
33801
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
34241
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
33802
34242
  }
33803
34243
  }, (phase) => {
33804
34244
  if (phase === "thinking") {