micro-models-agent 0.43.3 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +312 -312
  2. package/bin/mma.mjs +41 -41
  3. package/dist/main.js +333 -66
  4. package/dist/skills/builtin/git.md +36 -36
  5. package/dist/skills/builtin/typescript.md +35 -35
  6. package/package.json +1 -1
  7. package/dist/cli/commands.js +0 -333
  8. package/dist/cli/completer.js +0 -168
  9. package/dist/cli/index.js +0 -2
  10. package/dist/cli/main.js +0 -140
  11. package/dist/cli/repl-commands.js +0 -633
  12. package/dist/cli/repl.js +0 -486
  13. package/dist/cli/security-commands.js +0 -166
  14. package/dist/cli/setup.js +0 -249
  15. package/dist/config/config.js +0 -202
  16. package/dist/config/defaults.js +0 -100
  17. package/dist/config/experts.js +0 -15
  18. package/dist/config/index.js +0 -3
  19. package/dist/config/security.js +0 -200
  20. package/dist/config/types.js +0 -1
  21. package/dist/core/agent-moe.js +0 -110
  22. package/dist/core/agent.js +0 -695
  23. package/dist/core/bootstrap.js +0 -337
  24. package/dist/core/index.js +0 -2
  25. package/dist/core/prompt-builder.js +0 -55
  26. package/dist/core/session-logger.js +0 -155
  27. package/dist/core/types.js +0 -1
  28. package/dist/core/workspace.js +0 -76
  29. package/dist/i18n/en.json +0 -525
  30. package/dist/i18n/index.js +0 -46
  31. package/dist/i18n/ru.json +0 -525
  32. package/dist/index.js +0 -22
  33. package/dist/llm/image-utils.js +0 -144
  34. package/dist/llm/index.js +0 -4
  35. package/dist/llm/model-loader.js +0 -78
  36. package/dist/llm/openai-compat.js +0 -353
  37. package/dist/llm/orchestrator.js +0 -194
  38. package/dist/llm/provider.js +0 -10
  39. package/dist/llm/response.js +0 -39
  40. package/dist/llm/token-counter.js +0 -39
  41. package/dist/llm/types.js +0 -1
  42. package/dist/logger/app-logger.js +0 -143
  43. package/dist/logger/file-log.js +0 -151
  44. package/dist/logger/index.js +0 -1
  45. package/dist/migration/backup.js +0 -45
  46. package/dist/migration/detect.js +0 -50
  47. package/dist/migration/index.js +0 -2
  48. package/dist/modules/browser/actions.js +0 -46
  49. package/dist/modules/browser/cookie-store.js +0 -24
  50. package/dist/modules/browser/index.js +0 -5
  51. package/dist/modules/browser/module.js +0 -28
  52. package/dist/modules/browser/session.js +0 -335
  53. package/dist/modules/browser/snapshot.js +0 -114
  54. package/dist/modules/browser/types.js +0 -9
  55. package/dist/modules/certification/cli.js +0 -176
  56. package/dist/modules/certification/fact-checker.js +0 -84
  57. package/dist/modules/certification/loader.js +0 -111
  58. package/dist/modules/certification/manifest.js +0 -50
  59. package/dist/modules/certification/runner.js +0 -162
  60. package/dist/modules/certification/scenarios.js +0 -124
  61. package/dist/modules/certification/types.js +0 -1
  62. package/dist/modules/context/index.js +0 -1
  63. package/dist/modules/context/manager.js +0 -349
  64. package/dist/modules/execution/auditor.js +0 -66
  65. package/dist/modules/execution/index.js +0 -8
  66. package/dist/modules/execution/module.js +0 -779
  67. package/dist/modules/execution/moe-executor.js +0 -266
  68. package/dist/modules/execution/plan-coverage.js +0 -68
  69. package/dist/modules/execution/plan-persister.js +0 -46
  70. package/dist/modules/execution/plan-store.js +0 -159
  71. package/dist/modules/execution/plan-validator.js +0 -153
  72. package/dist/modules/execution/planner.js +0 -85
  73. package/dist/modules/execution/stuck-detector.js +0 -347
  74. package/dist/modules/execution/tracker.js +0 -67
  75. package/dist/modules/execution/types.js +0 -1
  76. package/dist/modules/execution/verifier.js +0 -178
  77. package/dist/modules/hallucination/confidence.js +0 -59
  78. package/dist/modules/hallucination/consistency.js +0 -26
  79. package/dist/modules/hallucination/detector.js +0 -46
  80. package/dist/modules/hallucination/factual.js +0 -190
  81. package/dist/modules/hallucination/index.js +0 -5
  82. package/dist/modules/hallucination/js-identifiers.js +0 -72
  83. package/dist/modules/hallucination/llm-judge.js +0 -103
  84. package/dist/modules/index.js +0 -5
  85. package/dist/modules/indexer/cache.js +0 -38
  86. package/dist/modules/indexer/index.js +0 -3
  87. package/dist/modules/indexer/module.js +0 -192
  88. package/dist/modules/indexer/walker.js +0 -101
  89. package/dist/modules/lsp/client.js +0 -235
  90. package/dist/modules/lsp/config.js +0 -81
  91. package/dist/modules/lsp/index.js +0 -3
  92. package/dist/modules/lsp/module.js +0 -68
  93. package/dist/modules/lsp/types.js +0 -1
  94. package/dist/modules/mcp/client.js +0 -399
  95. package/dist/modules/mcp/index.js +0 -3
  96. package/dist/modules/mcp/module.js +0 -146
  97. package/dist/modules/mcp/registry.js +0 -15
  98. package/dist/modules/memory/index.js +0 -1
  99. package/dist/modules/memory/module.js +0 -48
  100. package/dist/modules/memory/search.js +0 -40
  101. package/dist/modules/memory/store.js +0 -69
  102. package/dist/modules/pipelines/engine.js +0 -60
  103. package/dist/modules/pipelines/index.js +0 -3
  104. package/dist/modules/pipelines/parser.js +0 -53
  105. package/dist/modules/pipelines/template.js +0 -14
  106. package/dist/modules/plugins/builtin/lint-on-write.js +0 -226
  107. package/dist/modules/plugins/builtin/notify.js +0 -8
  108. package/dist/modules/plugins/index.js +0 -1
  109. package/dist/modules/plugins/loader.js +0 -28
  110. package/dist/modules/plugins/manager.js +0 -161
  111. package/dist/modules/plugins/types.js +0 -1
  112. package/dist/modules/processes/index.js +0 -2
  113. package/dist/modules/processes/registry.js +0 -238
  114. package/dist/modules/processes/runner.js +0 -23
  115. package/dist/modules/registry.js +0 -45
  116. package/dist/modules/security/audit-log.js +0 -136
  117. package/dist/modules/security/audit-notifier.js +0 -292
  118. package/dist/modules/security/command-validator.js +0 -211
  119. package/dist/modules/security/content-scanner.js +0 -53
  120. package/dist/modules/security/data-sanitizer.js +0 -97
  121. package/dist/modules/security/encryption.js +0 -240
  122. package/dist/modules/security/index.js +0 -14
  123. package/dist/modules/security/network-validator.js +0 -79
  124. package/dist/modules/security/path-validator.js +0 -209
  125. package/dist/modules/security/rate-limiter.js +0 -119
  126. package/dist/modules/security/security-policies.js +0 -547
  127. package/dist/modules/security/session-encryption.js +0 -210
  128. package/dist/modules/security/session-isolation.js +0 -95
  129. package/dist/modules/session/index.js +0 -3
  130. package/dist/modules/session/manager.js +0 -172
  131. package/dist/modules/session/module.js +0 -24
  132. package/dist/modules/session/store.js +0 -228
  133. package/dist/modules/session/types.js +0 -1
  134. package/dist/modules/skills/index.js +0 -2
  135. package/dist/modules/skills/loader.js +0 -72
  136. package/dist/modules/skills/module.js +0 -130
  137. package/dist/modules/types.js +0 -1
  138. package/dist/modules/updater/checker.js +0 -32
  139. package/dist/modules/updater/index.js +0 -1
  140. package/dist/modules/user-profile/compressor.js +0 -16
  141. package/dist/modules/user-profile/index.js +0 -1
  142. package/dist/modules/user-profile/profile.js +0 -68
  143. package/dist/tools/approve.js +0 -32
  144. package/dist/tools/attach-image.js +0 -89
  145. package/dist/tools/bash.js +0 -337
  146. package/dist/tools/browser.js +0 -97
  147. package/dist/tools/create-dir.js +0 -55
  148. package/dist/tools/delete-file.js +0 -62
  149. package/dist/tools/edit-file.js +0 -79
  150. package/dist/tools/executor.js +0 -145
  151. package/dist/tools/file-info.js +0 -45
  152. package/dist/tools/filter-tools.js +0 -10
  153. package/dist/tools/glob-tool.js +0 -26
  154. package/dist/tools/grep-tool.js +0 -86
  155. package/dist/tools/index.js +0 -67
  156. package/dist/tools/list-dir.js +0 -47
  157. package/dist/tools/load-skill.js +0 -44
  158. package/dist/tools/mcp-call.js +0 -68
  159. package/dist/tools/move-file.js +0 -85
  160. package/dist/tools/path-utils.js +0 -51
  161. package/dist/tools/pipeline-run.js +0 -144
  162. package/dist/tools/preview.js +0 -2
  163. package/dist/tools/process-kill.js +0 -29
  164. package/dist/tools/process-list.js +0 -38
  165. package/dist/tools/process-log.js +0 -41
  166. package/dist/tools/question.js +0 -142
  167. package/dist/tools/read-file.js +0 -83
  168. package/dist/tools/recall.js +0 -110
  169. package/dist/tools/registry.js +0 -36
  170. package/dist/tools/remember.js +0 -67
  171. package/dist/tools/scope-check.js +0 -30
  172. package/dist/tools/search-history.js +0 -84
  173. package/dist/tools/subagent.js +0 -151
  174. package/dist/tools/types.js +0 -1
  175. package/dist/tools/user-input.js +0 -123
  176. package/dist/tools/web-browse.js +0 -86
  177. package/dist/tools/web-fetch.js +0 -98
  178. package/dist/tools/web-search.js +0 -78
  179. package/dist/tools/write-file.js +0 -83
  180. package/dist/ui/box.js +0 -81
  181. package/dist/ui/colors.js +0 -4
  182. package/dist/ui/diff.js +0 -178
  183. package/dist/ui/index.js +0 -6
  184. package/dist/ui/md-formatter.js +0 -212
  185. package/dist/ui/output.js +0 -13
  186. package/dist/ui/renderer.js +0 -204
  187. package/dist/ui/spinner.js +0 -70
  188. package/dist/ui/table.js +0 -144
package/dist/main.js CHANGED
@@ -2298,6 +2298,10 @@ var init_defaults = __esm(() => {
2298
2298
  maxSearchesPerSession: 3,
2299
2299
  requestTimeoutMs: 1e4
2300
2300
  },
2301
+ pricing: {
2302
+ enabled: true,
2303
+ overrides: {}
2304
+ },
2301
2305
  ui: {
2302
2306
  spinner: true,
2303
2307
  toolStyle: "inline",
@@ -2390,6 +2394,7 @@ Use read_file on {path} to see the current content before editing — the target
2390
2394
  "error.http": "HTTP {status}: {statusText}",
2391
2395
  "error.llm_api": "LLM API error {status} ({statusText}): {errorText}",
2392
2396
  "error.llm_retries": "LLM request failed after retries",
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}",
2393
2398
  "error.no_response_body": "No response body stream",
2394
2399
  "session.started": "Session started: {id}",
2395
2400
  "session.ended": "Session ended: {id}",
@@ -2999,7 +3004,10 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
2999
3004
  "tools.enable_already_active": "Tool tags already active: {tags}.",
3000
3005
  "tools.enable_added": "Enabled tool tags: {tags}. Available tools now: {tools}",
3001
3006
  "tools.hidden_header": "Additional tools (enable on demand via enable_tools or route to subagent tool_tags):",
3002
- "tool.friendly.enable_tools": "Enable tools"
3007
+ "tool.friendly.enable_tools": "Enable tools",
3008
+ "cli.provider_base_hint": "Base URL set to: {baseUrl}",
3009
+ "repl.cost": "Total cost: {cost}",
3010
+ "repl.tokens": "Tokens used: {tokens}"
3003
3011
  };
3004
3012
  });
3005
3013
 
@@ -3049,6 +3057,7 @@ var init_ru = __esm(() => {
3049
3057
  "error.http": "HTTP {status}: {statusText}",
3050
3058
  "error.llm_api": "Ошибка LLM API {status} ({statusText}): {errorText}",
3051
3059
  "error.llm_retries": "Запрос LLM не удался после повторов",
3060
+ "error.llm_429": "Превышен лимит запросов (HTTP 429) для {model} на {baseUrl}. Провайдер ограничивает трафик — особенно строгие free-модели. Получите API-ключ или переключитесь на платную/быструю модель: {baseUrl}",
3052
3061
  "error.no_response_body": "Нет потока тела ответа",
3053
3062
  "session.started": "Сессия начата: {id}",
3054
3063
  "session.ended": "Сессия завершена: {id}",
@@ -3659,7 +3668,10 @@ var init_ru = __esm(() => {
3659
3668
  "tools.enable_already_active": "Теги тулов уже активны: {tags}.",
3660
3669
  "tools.enable_added": "Включены теги тулов: {tags}. Теперь доступны тулы: {tools}",
3661
3670
  "tools.hidden_header": "Дополнительные тулы (включите по требованию через enable_tools или маршрутизируйте через subagent tool_tags):",
3662
- "tool.friendly.enable_tools": "Включить тулы"
3671
+ "tool.friendly.enable_tools": "Включить тулы",
3672
+ "cli.provider_base_hint": "Базовый URL установлен: {baseUrl}",
3673
+ "repl.cost": "Итого потрачено: {cost}",
3674
+ "repl.tokens": "Потрачено токенов: {tokens}"
3663
3675
  };
3664
3676
  });
3665
3677
 
@@ -5421,26 +5433,36 @@ class OpenAICompatProvider {
5421
5433
  }
5422
5434
  async fetchWithRetry(url, init) {
5423
5435
  const { maxRetries, baseDelay, maxDelay } = this.retryConfig;
5424
- let lastError = null;
5436
+ let lastStatus = 0;
5437
+ let lastRetryAfter = 0;
5425
5438
  for (let attempt = 0;attempt <= maxRetries; attempt++) {
5426
5439
  try {
5427
5440
  const response = await fetch(url, init);
5428
5441
  if (!this.isRetryable(response.status))
5429
5442
  return response;
5430
- lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);
5443
+ lastStatus = response.status;
5444
+ const retryAfter = Number(response.headers.get("retry-after") ?? 0);
5445
+ lastRetryAfter = retryAfter > 0 ? retryAfter * 1000 : 0;
5431
5446
  } catch (err) {
5432
5447
  if (err.name === "AbortError") {
5433
5448
  throw err;
5434
5449
  }
5435
- lastError = err;
5450
+ if (attempt === maxRetries)
5451
+ throw err;
5436
5452
  }
5437
5453
  if (attempt < maxRetries) {
5438
- const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
5454
+ const delay = lastRetryAfter > 0 ? Math.min(lastRetryAfter, maxDelay) : Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
5439
5455
  const jitter = Math.random() * baseDelay * 0.1;
5440
5456
  await this.sleep(delay + jitter, init.signal ?? undefined);
5441
5457
  }
5442
5458
  }
5443
- throw lastError ?? new Error(t("error.llm_retries"));
5459
+ if (lastStatus === 429) {
5460
+ throw new Error(t("error.llm_429", {
5461
+ model: this.model,
5462
+ baseUrl: this.config.baseUrl
5463
+ }));
5464
+ }
5465
+ throw new Error(t("error.llm_retries"));
5444
5466
  }
5445
5467
  isRetryable(status) {
5446
5468
  return status === 429 || status >= 500;
@@ -6292,6 +6314,7 @@ var init_read_file = __esm(() => {
6292
6314
  init_path_utils();
6293
6315
  readFileTool = {
6294
6316
  name: "read_file",
6317
+ icon: "\uD83D\uDC40",
6295
6318
  description: `Read a file from the filesystem. Reads up to ${DEFAULT_LIMIT} lines at a time by default; use offset to page through large files.`,
6296
6319
  tags: ["file", "code"],
6297
6320
  boundedOutput: true,
@@ -6661,6 +6684,7 @@ var init_write_file = __esm(() => {
6661
6684
  init_path_utils();
6662
6685
  writeFileTool = {
6663
6686
  name: "write_file",
6687
+ icon: "\uD83D\uDCDD",
6664
6688
  description: "Create or overwrite a file with given content. Creates intermediate directories if needed.",
6665
6689
  tags: ["file", "code"],
6666
6690
  parameters: {
@@ -6736,6 +6760,7 @@ var init_edit_file = __esm(() => {
6736
6760
  init_path_utils();
6737
6761
  editFileTool = {
6738
6762
  name: "edit_file",
6763
+ icon: "✏️",
6739
6764
  description: "Find and replace text in an existing file. Uses exact string match (not regex).",
6740
6765
  tags: ["file", "code"],
6741
6766
  parameters: {
@@ -6809,6 +6834,7 @@ var init_glob_tool = __esm(() => {
6809
6834
  init_i18n();
6810
6835
  globTool = {
6811
6836
  name: "glob",
6837
+ icon: "\uD83D\uDD0E",
6812
6838
  description: `Search for files matching a glob pattern. Shows up to ${MAX_PREVIEW_LINES} results by default. Uses standard glob syntax (e.g., **/*.ts, src/**/*.test.ts).`,
6813
6839
  tags: ["file", "code", "research"],
6814
6840
  parameters: {
@@ -6850,6 +6876,7 @@ var init_grep_tool = __esm(() => {
6850
6876
  init_audit_log();
6851
6877
  grepTool = {
6852
6878
  name: "grep",
6879
+ icon: "\uD83D\uDD0D",
6853
6880
  description: `Search file contents using a regular expression. Shows up to ${MAX_PREVIEW_LINES} matching lines by default. Uses ripgrep (rg) if available, otherwise falls back to grep -r.`,
6854
6881
  tags: ["file", "code", "research"],
6855
6882
  parameters: {
@@ -6925,6 +6952,7 @@ var init_list_dir = __esm(() => {
6925
6952
  init_path_utils();
6926
6953
  listDirTool = {
6927
6954
  name: "list_dir",
6955
+ icon: "\uD83D\uDCC2",
6928
6956
  description: `List files and directories in a given path. Shows up to ${MAX_PREVIEW_LINES} entries by default.`,
6929
6957
  tags: ["file"],
6930
6958
  boundedOutput: true,
@@ -6977,6 +7005,7 @@ var init_create_dir = __esm(() => {
6977
7005
  init_path_utils();
6978
7006
  createDirTool = {
6979
7007
  name: "create_dir",
7008
+ icon: "\uD83D\uDCC1",
6980
7009
  description: "Create a directory (and any intermediate directories).",
6981
7010
  tags: ["file"],
6982
7011
  parameters: {
@@ -7031,6 +7060,7 @@ var init_delete_file = __esm(() => {
7031
7060
  init_path_utils();
7032
7061
  deleteFileTool = {
7033
7062
  name: "delete_file",
7063
+ icon: "\uD83D\uDDD1️",
7034
7064
  description: "Delete a file from the filesystem.",
7035
7065
  tags: ["file"],
7036
7066
  parameters: {
@@ -7092,6 +7122,7 @@ var init_move_file = __esm(() => {
7092
7122
  init_path_utils();
7093
7123
  moveFileTool = {
7094
7124
  name: "move_file",
7125
+ icon: "\uD83D\uDCE6",
7095
7126
  description: "Move or rename a file or directory.",
7096
7127
  tags: ["file"],
7097
7128
  parameters: {
@@ -7170,6 +7201,7 @@ var init_file_info = __esm(() => {
7170
7201
  init_path_utils();
7171
7202
  fileInfoTool = {
7172
7203
  name: "file_info",
7204
+ icon: "ℹ️",
7173
7205
  description: "Get metadata about a file or directory (size, creation date, modification date).",
7174
7206
  tags: ["file"],
7175
7207
  boundedOutput: true,
@@ -7844,6 +7876,7 @@ var init_bash = __esm(() => {
7844
7876
  NPM_EXEC_RE = /could not determine executable to run/i;
7845
7877
  bashTool = {
7846
7878
  name: "bash",
7879
+ icon: "\uD83D\uDCBB",
7847
7880
  description: `Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_kill. Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers).
7848
7881
 
7849
7882
  Windows notes: the shell is cmd.exe — PowerShell cmdlets (Write-Host, Get-Content, Select-String, Out-File) and bash heredocs (cat << EOF) do NOT work there. Use the read_file/write_file tools instead of cat/echo redirection. Sequential commands: use && (a leading ; is auto-converted to &).`,
@@ -8012,6 +8045,7 @@ var init_process_list = __esm(() => {
8012
8045
  init_i18n();
8013
8046
  processListTool = {
8014
8047
  name: "process_list",
8048
+ icon: "\uD83D\uDCCA",
8015
8049
  description: "List background processes started via the bash tool (dev servers, watchers, long-running commands). Shows id, pid, command, status, and recent output. Use with process_log and process_kill to inspect or stop them.",
8016
8050
  tags: ["shell"],
8017
8051
  parameters: {
@@ -8056,6 +8090,7 @@ var init_process_log = __esm(() => {
8056
8090
  DEFAULT_TAIL = MAX_PREVIEW_LINES;
8057
8091
  processLogTool = {
8058
8092
  name: "process_log",
8093
+ icon: "\uD83D\uDCDC",
8059
8094
  description: `Show the buffered output of a background process started via bash. Shows the last ${DEFAULT_TAIL} lines by default. Use after starting a dev server to verify it came up without errors, and while it runs to check its state.`,
8060
8095
  tags: ["shell"],
8061
8096
  boundedOutput: true,
@@ -8101,6 +8136,7 @@ var init_process_kill = __esm(() => {
8101
8136
  init_i18n();
8102
8137
  processKillTool = {
8103
8138
  name: "process_kill",
8139
+ icon: "⛔",
8104
8140
  description: "Stop a background process started via bash (dev server, watcher). Kills the whole process tree (children included). Use the id returned by bash or process_list.",
8105
8141
  tags: ["shell"],
8106
8142
  parameters: {
@@ -11279,6 +11315,145 @@ var init_agent_moe = __esm(() => {
11279
11315
  init_verifier();
11280
11316
  });
11281
11317
 
11318
+ // src/modules/pricing/prices.ts
11319
+ function normalizeModelId(model) {
11320
+ let id = model.trim();
11321
+ for (const prefix of PROVIDER_PREFIXES) {
11322
+ if (id.startsWith(prefix)) {
11323
+ id = id.slice(prefix.length);
11324
+ break;
11325
+ }
11326
+ }
11327
+ const parts = id.split("/");
11328
+ return parts[parts.length - 1];
11329
+ }
11330
+ function resolvePrice(model, config) {
11331
+ if (!config?.enabled)
11332
+ return;
11333
+ const bare = normalizeModelId(model);
11334
+ const overrides = config.overrides ?? {};
11335
+ const direct = overrides[model] ?? overrides[bare];
11336
+ if (direct)
11337
+ return direct;
11338
+ return ZEN_PRICES[bare];
11339
+ }
11340
+ function calculateCost(model, promptTokens, completionTokens, config) {
11341
+ const price = resolvePrice(model, config);
11342
+ if (!price)
11343
+ return;
11344
+ return promptTokens / 1e6 * price.input + completionTokens / 1e6 * price.output;
11345
+ }
11346
+ function formatCost(cost) {
11347
+ if (cost === 0)
11348
+ return "$0.00";
11349
+ if (cost >= 0.01)
11350
+ return `$${cost.toFixed(2)}`;
11351
+ if (cost >= 0.0001)
11352
+ return `$${cost.toFixed(4)}`;
11353
+ return `$${cost.toFixed(6)}`;
11354
+ }
11355
+ var ZEN_PRICES, PROVIDER_PREFIXES;
11356
+ var init_prices = __esm(() => {
11357
+ ZEN_PRICES = {
11358
+ "big-pickle": { input: 0, output: 0 },
11359
+ "mimo-v2.5-free": { input: 0, output: 0 },
11360
+ "hy3-free": { input: 0, output: 0 },
11361
+ "nemotron-3-ultra-free": { input: 0, output: 0 },
11362
+ "nemotron-3.5-lightning-free": { input: 0, output: 0 },
11363
+ "muse-spark-1.2-contributor-free": { input: 0, output: 0 },
11364
+ "minimax-m3": { input: 0.3, output: 1.2 },
11365
+ "minimax-m2.7": { input: 0.3, output: 1.2 },
11366
+ "minimax-m2.5": { input: 0.3, output: 1.2 },
11367
+ "glm-5.2": { input: 1.4, output: 4.4 },
11368
+ "glm-5.1": { input: 1.4, output: 4.4 },
11369
+ "glm-5": { input: 1, output: 3.2 },
11370
+ "glm-5.3": { input: 1.4, output: 4.4 },
11371
+ "kimi-k2.7-code": { input: 0.95, output: 4 },
11372
+ "kimi-k3": { input: 3, output: 15 },
11373
+ "kimi-k2.6": { input: 0.95, output: 4 },
11374
+ "kimi-k2.5": { input: 0.6, output: 3 },
11375
+ "qwen3.7-max": { input: 2.5, output: 7.5 },
11376
+ "qwen3.7-plus": { input: 0.4, output: 1.6 },
11377
+ "qwen3.6-plus": { input: 0.5, output: 3 },
11378
+ "qwen3.5-plus": { input: 0.2, output: 1.2 },
11379
+ "qwen3.8-max": { input: 2.5, output: 7.5 },
11380
+ "deepseek-v4-pro": { input: 0.66, output: 1.98 },
11381
+ "deepseek-v4-flash": { input: 0.22, output: 0.66 },
11382
+ "claude-fable-5": { input: 10, output: 50 },
11383
+ "claude-opus-5": { input: 5, output: 25 },
11384
+ "claude-opus-4-8": { input: 5, output: 25 },
11385
+ "claude-opus-4-7": { input: 5, output: 25 },
11386
+ "claude-opus-4-6": { input: 5, output: 25 },
11387
+ "claude-opus-4-5": { input: 5, output: 25 },
11388
+ "claude-sonnet-5": { input: 2, output: 10 },
11389
+ "claude-sonnet-4-6": { input: 3, output: 15 },
11390
+ "claude-sonnet-4-5": { input: 3, output: 15 },
11391
+ "claude-haiku-4-5": { input: 1, output: 5 },
11392
+ "gemini-3.7-flash": { input: 1.5, output: 7.5 },
11393
+ "gemini-3.6-flash": { input: 1.5, output: 7.5 },
11394
+ "gemini-3.5-flash": { input: 1.5, output: 9 },
11395
+ "gemini-3.5-flash-lite": { input: 0.3, output: 2.5 },
11396
+ "gemini-3.1-pro": { input: 2, output: 12 },
11397
+ "gemini-3-flash": { input: 0.5, output: 3 },
11398
+ "grok-4.6": { input: 2, output: 6 },
11399
+ "grok-4.5": { input: 2, output: 6 },
11400
+ "grok-build-0.1": { input: 1, output: 2 },
11401
+ "muse-spark-1.2": { input: 1.25, output: 4.25 },
11402
+ "gpt-5.6-sol": { input: 5, output: 30 },
11403
+ "gpt-5.6-terra": { input: 2, output: 12 },
11404
+ "gpt-5.6-luna": { input: 0.2, output: 1.2 },
11405
+ "gpt-5.5": { input: 5, output: 30 },
11406
+ "gpt-5.5-pro": { input: 30, output: 180 },
11407
+ "gpt-5.4": { input: 2.5, output: 15 },
11408
+ "gpt-5.4-pro": { input: 30, output: 180 },
11409
+ "gpt-5.4-mini": { input: 0.75, output: 4.5 },
11410
+ "gpt-5.4-nano": { input: 0.2, output: 1.25 },
11411
+ "gpt-5.3-codex": { input: 1.75, output: 14 },
11412
+ "gpt-5.3-codex-spark": { input: 1.75, output: 14 },
11413
+ "gpt-5.2": { input: 1.75, output: 14 },
11414
+ "gpt-5.2-codex": { input: 1.75, output: 14 },
11415
+ "gpt-5.1": { input: 1.07, output: 8.5 },
11416
+ "gpt-5.1-codex": { input: 1.07, output: 8.5 },
11417
+ "gpt-5.1-codex-max": { input: 1.25, output: 10 },
11418
+ "gpt-5.1-codex-mini": { input: 0.25, output: 2 },
11419
+ "gpt-5": { input: 1.07, output: 8.5 },
11420
+ "gpt-5-codex": { input: 1.07, output: 8.5 },
11421
+ "gpt-5-nano": { input: 0.05, output: 0.4 }
11422
+ };
11423
+ PROVIDER_PREFIXES = ["opencode-go/", "opencode/", "openai/", "anthropic/"];
11424
+ });
11425
+
11426
+ // src/modules/pricing/index.ts
11427
+ class CostTracker {
11428
+ config;
11429
+ model;
11430
+ _total = 0;
11431
+ _known = false;
11432
+ constructor(model, config) {
11433
+ this.model = model;
11434
+ this.config = config;
11435
+ }
11436
+ setModel(model) {
11437
+ this.model = model;
11438
+ }
11439
+ record(promptTokens, completionTokens) {
11440
+ const cost = calculateCost(this.model, promptTokens, completionTokens, this.config);
11441
+ if (cost === undefined)
11442
+ return;
11443
+ this._known = true;
11444
+ this._total += cost;
11445
+ }
11446
+ get total() {
11447
+ return this._known ? this._total : undefined;
11448
+ }
11449
+ get formatted() {
11450
+ return this._known ? formatCost(this._total) : undefined;
11451
+ }
11452
+ }
11453
+ var init_pricing = __esm(() => {
11454
+ init_prices();
11455
+ });
11456
+
11282
11457
  // src/core/agent.ts
11283
11458
  function isToolCallJson(text) {
11284
11459
  const trimmed = text.trim();
@@ -11305,8 +11480,10 @@ class Agent {
11305
11480
  shutdownRequested = false;
11306
11481
  abortController = null;
11307
11482
  lastCompactionShown = 0;
11483
+ costTracker;
11308
11484
  constructor(deps) {
11309
11485
  this.deps = deps;
11486
+ this.costTracker = new CostTracker(deps.config.model, deps.config.pricing);
11310
11487
  }
11311
11488
  get contextManager() {
11312
11489
  return this.deps.contextManager;
@@ -11536,6 +11713,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11536
11713
  if (result) {
11537
11714
  logger.debug("Context compacted");
11538
11715
  slog.logCompaction({ reason: "interval", iteration, ...result });
11716
+ hallucinationDetector.addKnownFiles(contextManager.getKnownFiles());
11539
11717
  }
11540
11718
  }
11541
11719
  const currentTokens = contextManager.getEstimatedTokens();
@@ -11551,6 +11729,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11551
11729
  iteration,
11552
11730
  ...result
11553
11731
  });
11732
+ hallucinationDetector.addKnownFiles(contextManager.getKnownFiles());
11554
11733
  }
11555
11734
  }
11556
11735
  if (currentTokens > budget2.history) {
@@ -11562,6 +11741,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11562
11741
  iteration,
11563
11742
  ...result
11564
11743
  });
11744
+ hallucinationDetector.addKnownFiles(contextManager.getKnownFiles());
11565
11745
  }
11566
11746
  }
11567
11747
  this.refreshSystemPrompt();
@@ -11659,6 +11839,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11659
11839
  source,
11660
11840
  durationMs: Date.now() - llmStart
11661
11841
  });
11842
+ this.costTracker.record(prompt, completion);
11662
11843
  }
11663
11844
  if (this.shutdownRequested) {
11664
11845
  break;
@@ -11713,6 +11894,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11713
11894
  });
11714
11895
  const summaries = [];
11715
11896
  let anyToolFailed = false;
11897
+ const infoTools = new Set(["file_info"]);
11716
11898
  for (const call of toolCalls) {
11717
11899
  this.setScope();
11718
11900
  const startTime = Date.now();
@@ -11721,14 +11903,15 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11721
11903
  args: call.arguments
11722
11904
  });
11723
11905
  pluginManager.runOnToolStart({ iteration, logger, contextManager }, { id: call.id, name: call.name, arguments: call.arguments });
11724
- onTool?.({ type: "start", tool: call.name, args: call.arguments });
11906
+ const toolIcon = this.deps.toolExecutor.getRegistry().get(call.name)?.icon;
11907
+ onTool?.({ type: "start", tool: call.name, args: call.arguments, icon: toolIcon });
11725
11908
  slog.logToolCall(call, iteration);
11726
11909
  const tokensBeforeTool = contextManager.getEstimatedTokens();
11727
11910
  const result = await toolExecutor.execute(call, this.abortController?.signal);
11728
11911
  if (this.shutdownRequested)
11729
11912
  break;
11730
11913
  const duration = Date.now() - startTime;
11731
- if (!result.success)
11914
+ if (!result.success && !infoTools.has(call.name))
11732
11915
  anyToolFailed = true;
11733
11916
  if (result.success && call.arguments.path) {
11734
11917
  const filePath = String(call.arguments.path);
@@ -11772,7 +11955,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11772
11955
  args: call.arguments,
11773
11956
  duration,
11774
11957
  error: !result.success,
11775
- ctxDelta: tokensAfterTool - tokensBeforeTool
11958
+ ctxDelta: tokensAfterTool - tokensBeforeTool,
11959
+ costUsd: this.costTracker.total
11776
11960
  });
11777
11961
  summaries.push(`[Tool: ${call.name} (${JSON.stringify(call.arguments)}) → ${truncatedOutput.slice(0, 200)}]`);
11778
11962
  if (config.session.autoSave) {
@@ -11787,9 +11971,10 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
11787
11971
  iteration,
11788
11972
  ...result2
11789
11973
  });
11974
+ hallucinationDetector.addKnownFiles(contextManager.getKnownFiles());
11790
11975
  }
11791
11976
  }
11792
- if (!result.success) {
11977
+ if (!result.success && !infoTools.has(call.name)) {
11793
11978
  const key = call.name;
11794
11979
  const prev = toolFailureCounts.get(key) ?? { count: 0, error: "" };
11795
11980
  prev.count++;
@@ -12006,6 +12191,7 @@ ${warnLine}
12006
12191
  promptTokens: usageTokens.prompt,
12007
12192
  completionTokens: usageTokens.completion,
12008
12193
  totalTokens: usageTokens.total,
12194
+ totalCost: this.costTracker.total,
12009
12195
  compactionCount: contextManager.getCompactionCount(),
12010
12196
  contextQuality: contextManager.getQuality()
12011
12197
  };
@@ -12020,6 +12206,7 @@ ${warnLine}
12020
12206
  promptTokens: usageTokens.prompt,
12021
12207
  completionTokens: usageTokens.completion,
12022
12208
  totalTokens: usageTokens.total,
12209
+ totalCost: this.costTracker.total,
12023
12210
  compactionCount: contextManager.getCompactionCount(),
12024
12211
  contextQuality: contextManager.getQuality()
12025
12212
  };
@@ -12045,6 +12232,7 @@ ${warnLine}
12045
12232
  const newTokenCounter = new TokenCounter2(config.model);
12046
12233
  this.deps.contextManager.resize(config.contextWindow, config.contextBudget, newTokenCounter);
12047
12234
  this.deps.config = config;
12235
+ this.costTracker.setModel(config.model);
12048
12236
  }
12049
12237
  setContext(messages) {
12050
12238
  const { contextManager } = this.deps;
@@ -12088,6 +12276,7 @@ var init_agent = __esm(() => {
12088
12276
  init_session_logger();
12089
12277
  init_agent_moe();
12090
12278
  init_verifier();
12279
+ init_pricing();
12091
12280
  });
12092
12281
 
12093
12282
  // src/modules/context/fact-extractor.ts
@@ -12184,6 +12373,10 @@ class FactExtractor {
12184
12373
  return null;
12185
12374
  return `[Files: ${liveFiles.slice(-15).join("; ")}]`;
12186
12375
  }
12376
+ getKnownFiles() {
12377
+ const deletedSet = new Set(this.deletedFacts);
12378
+ return this.fileFacts.filter((p) => !deletedSet.has(p));
12379
+ }
12187
12380
  deletedLine() {
12188
12381
  if (this.deletedFacts.length === 0)
12189
12382
  return null;
@@ -12308,6 +12501,9 @@ class ContextManager {
12308
12501
  getIterationsSinceCompaction() {
12309
12502
  return this.iterationsSinceCompaction;
12310
12503
  }
12504
+ getKnownFiles() {
12505
+ return this.facts.getKnownFiles();
12506
+ }
12311
12507
  noteIteration() {
12312
12508
  this.iterationsSinceCompaction++;
12313
12509
  }
@@ -12647,9 +12843,14 @@ class FactualCheck {
12647
12843
  static NAME_CACHE_TTL_MS = 5000;
12648
12844
  static RESCAN_COOLDOWN_MS = 2000;
12649
12845
  static MAX_INDEXED_FILES = 20000;
12846
+ knownFiles = new Set;
12650
12847
  constructor(baseDir) {
12651
12848
  this.baseDir = baseDir;
12652
12849
  }
12850
+ addKnownFiles(files) {
12851
+ for (const f of files)
12852
+ this.knownFiles.add(f);
12853
+ }
12653
12854
  validate(response) {
12654
12855
  const filePaths = this.extractFilePaths(response);
12655
12856
  if (filePaths.length === 0)
@@ -12673,6 +12874,8 @@ class FactualCheck {
12673
12874
  pathExists(fp) {
12674
12875
  if (isAbsolute2(fp))
12675
12876
  return existsSync23(fp);
12877
+ if (this.knownFiles.has(fp))
12878
+ return true;
12676
12879
  if (!fp.includes("/") && !fp.includes("\\")) {
12677
12880
  return this.bareNameExists(fp);
12678
12881
  }
@@ -12683,6 +12886,11 @@ class FactualCheck {
12683
12886
  return true;
12684
12887
  if (this.indexHas(name))
12685
12888
  return true;
12889
+ for (const known of this.knownFiles) {
12890
+ if (known.endsWith(`/${name}`) || known.endsWith(`\\${name}`) || known === name) {
12891
+ return true;
12892
+ }
12893
+ }
12686
12894
  const now = Date.now();
12687
12895
  if (now - this.lastRescan < FactualCheck.RESCAN_COOLDOWN_MS)
12688
12896
  return false;
@@ -12862,6 +13070,9 @@ class HallucinationDetector {
12862
13070
  getConfidenceCheck() {
12863
13071
  return this.confidence;
12864
13072
  }
13073
+ addKnownFiles(files) {
13074
+ this.factual?.addKnownFiles(files);
13075
+ }
12865
13076
  async validate(response) {
12866
13077
  const confidenceResult = this.confidence.validate(response);
12867
13078
  if (confidenceResult.status === "retry" || confidenceResult.status === "block") {
@@ -13208,6 +13419,7 @@ var init_subagent = __esm(() => {
13208
13419
  init_store();
13209
13420
  subagentTool = {
13210
13421
  name: "subagent",
13422
+ icon: "\uD83E\uDD16",
13211
13423
  description: "Spawn an isolated sub-agent to work on a task independently. The sub-agent has its own context and executes autonomously. Use for parallel work or complex sub-tasks.",
13212
13424
  tags: ["code"],
13213
13425
  parameters: {
@@ -13506,6 +13718,7 @@ var init_chunk_query = __esm(() => {
13506
13718
  init_scope_check();
13507
13719
  chunkQueryTool = {
13508
13720
  name: "chunk_query",
13721
+ icon: "\uD83E\uDDE9",
13509
13722
  description: "Answer a query over a large text (file or inline) by splitting it into chunks and querying the model per chunk in parallel, then optionally synthesizing a final answer. Use when the input is too large for the context window.",
13510
13723
  tags: ["research", "code"],
13511
13724
  timeoutMs: 300000,
@@ -13705,6 +13918,7 @@ var init_web_search = __esm(() => {
13705
13918
  init_session_isolation();
13706
13919
  webSearchTool = {
13707
13920
  name: "web_search",
13921
+ icon: "\uD83C\uDF0D",
13708
13922
  description: "Search the web for information. Returns search results with titles and snippets. Use this for search queries instead of opening search engines (Google/Yandex/Bing) in the browser — they block headless browsers.",
13709
13923
  tags: ["research"],
13710
13924
  parameters: {
@@ -13772,6 +13986,7 @@ var init_web_fetch = __esm(() => {
13772
13986
  init_security();
13773
13987
  webFetchTool = {
13774
13988
  name: "web_fetch",
13989
+ icon: "⬇️",
13775
13990
  description: 'Fetch a URL and convert its content to markdown. Use for reading documentation, APIs, web pages. This is a plain HTTP request without a browser engine — it does NOT run JavaScript, so JS-rendered single-page apps (weather sites, dashboards, modern SPAs) return empty or near-empty content; use the "browser" tool for those.',
13776
13991
  tags: ["research"],
13777
13992
  parameters: {
@@ -13858,6 +14073,7 @@ var init_web_browse = __esm(() => {
13858
14073
  init_session_isolation();
13859
14074
  webBrowseTool = {
13860
14075
  name: "web_browse",
14076
+ icon: "\uD83C\uDF10",
13861
14077
  description: `Fetch and read a web page. Returns the page content as plain text (max ${MAX_PREVIEW_LINES} lines / ${MAX_CHARS2} chars). This is a plain HTTP request — it does NOT execute JavaScript, so JS-rendered single-page apps (weather sites, dashboards, modern SPAs) return empty or near-empty content. For such pages use the "browser" tool instead.`,
13862
14078
  tags: ["research"],
13863
14079
  parameters: {
@@ -13942,6 +14158,7 @@ var init_download_file = __esm(() => {
13942
14158
  MAX_DOWNLOAD_BYTES = 100 * 1024 * 1024;
13943
14159
  downloadFileTool = {
13944
14160
  name: "download_file",
14161
+ icon: "⬇️",
13945
14162
  description: "Download a file (image, archive, binary, font, etc.) from a URL and save it to disk. Returns only the path, size and content type — read the saved file with read_file/attach_image afterwards. Use this for binary files; web_fetch/web_browse return text only and cannot save bytes.",
13946
14163
  tags: ["file", "research"],
13947
14164
  parameters: {
@@ -14045,6 +14262,7 @@ var init_download_file = __esm(() => {
14045
14262
  function createLoadSkillTool(skillsModule) {
14046
14263
  return {
14047
14264
  name: "load_skill",
14265
+ icon: "\uD83C\uDFAF",
14048
14266
  tags: ["core"],
14049
14267
  description: "Load a skill by name. Skills provide specialized instructions and workflows. When loaded, the skill content is injected into the system prompt (not conversation history) so it persists through context compaction. Only use when the task clearly requires domain-specific knowledge.",
14050
14268
  parameters: {
@@ -14276,6 +14494,7 @@ var init_pipeline_run = __esm(() => {
14276
14494
  engine = new PipelineEngine;
14277
14495
  pipelineRunTool = {
14278
14496
  name: "pipeline_run",
14497
+ icon: "▶️",
14279
14498
  description: "Run a named pipeline with YAML definition. Creates a DAG of sub-agents that execute in dependency order.",
14280
14499
  tags: ["shell", "code"],
14281
14500
  parameters: {
@@ -14807,6 +15026,7 @@ var init_mcp_call = __esm(() => {
14807
15026
  registry = new MCPRegistry;
14808
15027
  mcpCallTool = {
14809
15028
  name: "mcp_call",
15029
+ icon: "\uD83D\uDD17",
14810
15030
  description: "Call a tool on an MCP (Model Context Protocol) server. MCP servers provide external capabilities like databases, APIs, or specialized tools.",
14811
15031
  tags: ["code", "research"],
14812
15032
  parameters: {
@@ -14876,6 +15096,7 @@ var init_search_history = __esm(() => {
14876
15096
  init_i18n();
14877
15097
  searchHistoryTool = {
14878
15098
  name: "search_history",
15099
+ icon: "\uD83D\uDD75️",
14879
15100
  description: "Search through session history for past interactions across all sessions.",
14880
15101
  tags: ["research"],
14881
15102
  parameters: {
@@ -15072,6 +15293,7 @@ var init_remember = __esm(() => {
15072
15293
  CATEGORIES = ["preferences", "conventions", "decisions", "errors", "facts"];
15073
15294
  rememberTool = {
15074
15295
  name: "remember",
15296
+ icon: "\uD83E\uDDE0",
15075
15297
  description: "Remember information across sessions. Use for user preferences, facts, conventions, decisions, or errors.",
15076
15298
  tags: ["memory"],
15077
15299
  parameters: {
@@ -15199,6 +15421,7 @@ var init_recall = __esm(() => {
15199
15421
  CATEGORIES2 = ["preferences", "conventions", "decisions", "errors", "facts"];
15200
15422
  recallTool = {
15201
15423
  name: "recall",
15424
+ icon: "\uD83D\uDCBE",
15202
15425
  description: "Recall stored information from memory. Search across preferences, facts, conventions, decisions, errors.",
15203
15426
  tags: ["memory"],
15204
15427
  parameters: {
@@ -16228,6 +16451,7 @@ function formatScreenshotResult(snapshot, screenshot, supportsVision) {
16228
16451
  function createBrowserTool() {
16229
16452
  return {
16230
16453
  name: "browser",
16454
+ icon: "\uD83C\uDF10",
16231
16455
  tags: ["browser", "vision"],
16232
16456
  description: [
16233
16457
  "Control a headless browser. Navigate pages, click elements, type text, scroll, take screenshots.",
@@ -16432,6 +16656,7 @@ var init_attach_image = __esm(() => {
16432
16656
  init_audit_log();
16433
16657
  attachImageTool = {
16434
16658
  name: "attach_image",
16659
+ icon: "\uD83D\uDDBC️",
16435
16660
  description: "Attach an image to the conversation from a file path, URL, or clipboard. The image will be included in the next message sent to the model. Supports PNG, JPEG, GIF, WebP.",
16436
16661
  tags: ["vision", "image"],
16437
16662
  parameters: {
@@ -16517,6 +16742,7 @@ var init_enable_tools = __esm(() => {
16517
16742
  init_i18n();
16518
16743
  enableToolsTool = {
16519
16744
  name: "enable_tools",
16745
+ icon: "\uD83D\uDD0C",
16520
16746
  description: "Enable hidden tool categories for the current session. Only the essential tools (file/code/shell) are active by default. Call this with the tags you need and they become available from the next turn. Available tags: file, code, shell, research, browser, vision, image, memory, core. Returns the newly available tools. To run a task in a separate context instead, use subagent with tool_tags.",
16521
16747
  alwaysOn: true,
16522
16748
  parameters: {
@@ -17741,6 +17967,7 @@ function createPlanToolDefinitions(deps) {
17741
17967
  return [
17742
17968
  {
17743
17969
  name: "plan",
17970
+ icon: "\uD83D\uDCCB",
17744
17971
  alwaysOn: true,
17745
17972
  description: `Create, update, show, abort, list, switch, delete, purge, or re-plan multi-step plans.
17746
17973
 
@@ -18017,6 +18244,20 @@ ${progress2}`,
18017
18244
  };
18018
18245
  }
18019
18246
  }
18247
+ if (status === "done" && target.kind !== "delete") {
18248
+ const missing = deps.missingStepDeliverables(target);
18249
+ if (missing.length > 0) {
18250
+ const existing = deps.stillExistingDeliverables(target);
18251
+ const allGone = deps.hasStepDeliverables(target) && existing.length === 0;
18252
+ if (allGone) {
18253
+ const wasDeleted = deps.wasFileDeleted ? missing.every((f) => deps.wasFileDeleted(f)) : false;
18254
+ if (wasDeleted) {
18255
+ target.kind = "delete";
18256
+ deps.store.saveActive(tracker.getPlan());
18257
+ }
18258
+ }
18259
+ }
18260
+ }
18020
18261
  if (status === "done") {
18021
18262
  if (target.kind === "delete") {
18022
18263
  const leftovers = deps.stillExistingDeliverables(target);
@@ -18186,6 +18427,7 @@ ${progress}${vacuousNote}`,
18186
18427
  },
18187
18428
  {
18188
18429
  name: "todo",
18430
+ icon: "☑️",
18189
18431
  alwaysOn: true,
18190
18432
  description: "Manage sub-tasks within current plan step. Use to break down complex steps into smaller tasks.",
18191
18433
  parameters: {
@@ -18282,6 +18524,7 @@ ${lines.join(`
18282
18524
  },
18283
18525
  {
18284
18526
  name: "verify",
18527
+ icon: "✅",
18285
18528
  alwaysOn: true,
18286
18529
  description: "Run verification for the current step. Checks files mentioned in the step description.",
18287
18530
  parameters: {
@@ -18342,6 +18585,7 @@ class ExecutionModule {
18342
18585
  retryWebSearchSignatures = new Set;
18343
18586
  lastErrorSearchAt = 0;
18344
18587
  errorSearchCooldownMs;
18588
+ hallucinationDetector = null;
18345
18589
  state = {
18346
18590
  lastRecoveryIteration: -STUCK_RECOVERY_COOLDOWN,
18347
18591
  consecutivePlanWarnings: 0,
@@ -18380,6 +18624,9 @@ class ExecutionModule {
18380
18624
  this.completedPlan = null;
18381
18625
  this._auditSkipsRemaining = 0;
18382
18626
  }
18627
+ setHallucinationDetector(detector) {
18628
+ this.hallucinationDetector = detector;
18629
+ }
18383
18630
  recordCompleted(plan) {
18384
18631
  this.completedPlan = plan;
18385
18632
  }
@@ -18534,7 +18781,8 @@ class ExecutionModule {
18534
18781
  parseKinds: (a, st) => this.parseKinds(a, st),
18535
18782
  getTaskText: (c) => this.getTaskText(c),
18536
18783
  typecheckFailures: this.state.typecheckFailures,
18537
- baseDir: this.baseDir
18784
+ baseDir: this.baseDir,
18785
+ wasFileDeleted: this.hallucinationDetector ? (path) => this.hallucinationDetector.getConsistencyCheck().getDeletedFiles().includes(path) : undefined
18538
18786
  });
18539
18787
  }
18540
18788
  getPlugin() {
@@ -20067,6 +20315,7 @@ ${items}`;
20067
20315
  return [
20068
20316
  {
20069
20317
  name: "lsp_check",
20318
+ icon: "\uD83E\uDE7A",
20070
20319
  description: 'Run static checks (LSP diagnostics) on a file or directory. For "check for errors" tasks: pass a file to check it, or a directory to check its source files (capped at 15). Returns [LSP errors] / [LSP warnings] for files with a configured LSP server.',
20071
20320
  tags: ["code", "check"],
20072
20321
  alwaysOn: true,
@@ -20801,6 +21050,7 @@ ${t("indexer.and_more", { count: result.files.length - 100 })}` : "";
20801
21050
  createProjectMapTool() {
20802
21051
  return {
20803
21052
  name: "project_map",
21053
+ icon: "\uD83D\uDDFA️",
20804
21054
  alwaysOn: true,
20805
21055
  description: t("indexer.project_map_desc"),
20806
21056
  parameters: {
@@ -21327,6 +21577,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
21327
21577
  execModule.restorePlan();
21328
21578
  }
21329
21579
  moduleRegistry.register(execModule);
21580
+ execModule.setHallucinationDetector(hallucinationDetector);
21330
21581
  const sessionModule = new SessionModule(sessionManager);
21331
21582
  moduleRegistry.register(sessionModule);
21332
21583
  moduleRegistry.register(indexerModule);
@@ -22182,10 +22433,8 @@ async function runSetup(externalRl) {
22182
22433
  console.log(t("setup.no_servers"));
22183
22434
  apiBase = await ask(rl, t("setup.api_base"), "http://localhost:1234/v1");
22184
22435
  }
22185
- } else if (provider === "openai") {
22186
- apiBase = "https://api.openai.com/v1";
22187
- } else if (provider === "anthropic") {
22188
- apiBase = "https://api.anthropic.com";
22436
+ } else {
22437
+ apiBase = HOSTED_BASE_URLS[provider] ?? "";
22189
22438
  }
22190
22439
  const defaultKey = provider === "openai-compat" ? "not-needed" : "";
22191
22440
  const apiKey = await ask(rl, t("setup.api_key"), defaultKey);
@@ -22250,7 +22499,7 @@ async function runSetup(externalRl) {
22250
22499
  console.log(l);
22251
22500
  return answers;
22252
22501
  }
22253
- var PROVIDER_TYPES, KNOWN_PORTS;
22502
+ var PROVIDER_TYPES, KNOWN_PORTS, HOSTED_BASE_URLS;
22254
22503
  var init_setup = __esm(() => {
22255
22504
  init_colors();
22256
22505
  init_i18n();
@@ -22263,7 +22512,9 @@ var init_setup = __esm(() => {
22263
22512
  label: "OpenAI-compatible (LM Studio, Ollama, vLLM, etc.)"
22264
22513
  },
22265
22514
  { value: "openai", label: "OpenAI API" },
22266
- { value: "anthropic", label: "Anthropic Claude" }
22515
+ { value: "anthropic", label: "Anthropic Claude" },
22516
+ { value: "opencode-zen", label: "OpenCode Zen (opencode.ai/zen)" },
22517
+ { value: "opencode-go", label: "OpenCode Go (opencode.ai/go)" }
22267
22518
  ];
22268
22519
  KNOWN_PORTS = [
22269
22520
  { port: 1234, label: "LM Studio" },
@@ -22271,6 +22522,12 @@ var init_setup = __esm(() => {
22271
22522
  { port: 8080, label: "vLLM / custom" },
22272
22523
  { port: 4891, label: "GPT4All" }
22273
22524
  ];
22525
+ HOSTED_BASE_URLS = {
22526
+ openai: "https://api.openai.com/v1",
22527
+ anthropic: "https://api.anthropic.com",
22528
+ "opencode-zen": "https://opencode.ai/zen/v1",
22529
+ "opencode-go": "https://opencode.ai/zen/go/v1"
22530
+ };
22274
22531
  });
22275
22532
 
22276
22533
  // src/modules/certification/manifest.ts
@@ -31285,6 +31542,7 @@ function createPluginCommand(program2) {
31285
31542
  }
31286
31543
 
31287
31544
  // src/cli/commands.ts
31545
+ init_setup();
31288
31546
  import { fileURLToPath as fileURLToPath4 } from "url";
31289
31547
  function readVersion2() {
31290
31548
  const here = dirname16(fileURLToPath4(import.meta.url));
@@ -31470,8 +31728,15 @@ function createProgram() {
31470
31728
  const configPath = join44(homedir15(), ".mma", "config.json");
31471
31729
  const { config } = await bootstrap();
31472
31730
  config.provider.type = name;
31731
+ const baseUrl = HOSTED_BASE_URLS[name];
31732
+ if (baseUrl) {
31733
+ config.provider.baseUrl = baseUrl;
31734
+ }
31473
31735
  saveConfig(config, configPath);
31474
31736
  console.log(t("cli.provider_set", { name }));
31737
+ if (baseUrl) {
31738
+ console.log(t("cli.provider_base_hint", { baseUrl }));
31739
+ }
31475
31740
  });
31476
31741
  const session2 = program2.command("session").description(t("cli.manage_sessions"));
31477
31742
  session2.command("list").description(t("cli.list_sessions")).action(async () => {
@@ -32276,9 +32541,8 @@ class LineEditor {
32276
32541
 
32277
32542
  // src/cli/repl.ts
32278
32543
  import { existsSync as existsSync53, readFileSync as readFileSync35, writeFileSync as writeFileSync17 } from "fs";
32279
- import { join as join47, dirname as dirname18 } from "path";
32544
+ import { join as join47 } from "path";
32280
32545
  import { homedir as homedir17 } from "os";
32281
- import { fileURLToPath as fileURLToPath6 } from "url";
32282
32546
 
32283
32547
  // src/cli/completer.ts
32284
32548
  class SlashCommandProvider {
@@ -32582,6 +32846,10 @@ init_spinner();
32582
32846
  init_box();
32583
32847
  init_table();
32584
32848
  init_i18n();
32849
+ init_prices();
32850
+ function formatUsd(cost) {
32851
+ return formatCost(cost);
32852
+ }
32585
32853
  var GUTTER = " ";
32586
32854
  var BUSY_TOOLS = new Set(["lsp_check"]);
32587
32855
  function toolMarker(tool) {
@@ -32686,20 +32954,20 @@ class Renderer {
32686
32954
  thinkingEnd() {
32687
32955
  this.spinner.stop();
32688
32956
  }
32689
- toolStart(tool, args, stepContext) {
32957
+ toolStart(tool, args, stepContext, icon) {
32690
32958
  this.endCard();
32691
32959
  this.spinner.stop();
32692
32960
  const summary = summarizeArgs2(args);
32693
32961
  const step = stepContext ? ` ${pc2.cyan(`← ${stepContext}`)}` : "";
32962
+ const marker = icon || toolMarker(tool);
32694
32963
  if (!this.rich) {
32695
32964
  this.out.write(`
32696
- ${pc2.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}
32965
+ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}
32697
32966
  `);
32698
32967
  return;
32699
32968
  }
32700
32969
  this.card = { tool, args, body: [], start: Date.now() };
32701
32970
  if (this.toolStyle === "inline") {
32702
- const marker = toolMarker(tool);
32703
32971
  this.out.write(`
32704
32972
  ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}
32705
32973
  `);
@@ -32708,7 +32976,7 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
32708
32976
  }
32709
32977
  return;
32710
32978
  }
32711
- this.spinner.start(`${pc2.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}`);
32979
+ this.spinner.start(`${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}`);
32712
32980
  }
32713
32981
  planBlock(lines) {
32714
32982
  this.endCard();
@@ -32718,14 +32986,20 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
32718
32986
  `);
32719
32987
  }
32720
32988
  }
32721
- toolEnd(_tool, duration, error, ctxDelta) {
32989
+ toolEnd(_tool, duration, error, ctxDelta, costUsd) {
32722
32990
  this.spinner.stop();
32723
32991
  if (!this.rich) {
32992
+ const parts = [];
32724
32993
  if (ctxDelta !== undefined && ctxDelta !== 0) {
32725
32994
  const deltaStr = ctxDelta > 0 ? pc2.green(`+${ctxDelta}`) : pc2.yellow(`${ctxDelta} ↓`);
32726
- this.out.write(`${pc2.dim("ctx")} ${deltaStr}
32727
- `);
32995
+ parts.push(`${pc2.dim("ctx")} ${deltaStr}`);
32728
32996
  }
32997
+ if (costUsd !== undefined && costUsd > 0) {
32998
+ parts.push(`${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`);
32999
+ }
33000
+ if (parts.length > 0)
33001
+ this.out.write(`${parts.join(" ")}
33002
+ `);
32729
33003
  return;
32730
33004
  }
32731
33005
  if (!this.card)
@@ -32737,6 +33011,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
32737
33011
  const deltaStr = ctxDelta > 0 ? pc2.green(`+${ctxDelta}`) : pc2.yellow(`${ctxDelta} ↓`);
32738
33012
  footer += ` ${pc2.dim("ctx")} ${deltaStr}`;
32739
33013
  }
33014
+ if (costUsd !== undefined && costUsd > 0) {
33015
+ footer += ` ${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`;
33016
+ }
32740
33017
  if (this.toolStyle === "inline") {
32741
33018
  this.out.write(`${GUTTER}${footer}
32742
33019
  `);
@@ -32964,21 +33241,7 @@ function stepContextForTool(plan, tool, args) {
32964
33241
  }
32965
33242
 
32966
33243
  // src/cli/repl.ts
32967
- function readVersion4() {
32968
- const here = dirname18(fileURLToPath6(import.meta.url));
32969
- const candidates = [join47(here, "..", "..", "package.json"), join47(here, "..", "package.json")];
32970
- for (const p of candidates) {
32971
- if (existsSync53(p)) {
32972
- try {
32973
- const raw = JSON.parse(readFileSync35(p, "utf8"));
32974
- if (raw.version)
32975
- return raw.version;
32976
- } catch {}
32977
- }
32978
- }
32979
- return "0.0.0";
32980
- }
32981
- var version3 = readVersion4();
33244
+ init_prices();
32982
33245
  function formatContextBar(used, limit, compactions, quality) {
32983
33246
  const pct = Math.min(100, Math.round(used / limit * 100));
32984
33247
  const barLen = 20;
@@ -33268,9 +33531,9 @@ ${t("image.clipboard_empty")}`));
33268
33531
  this.refreshActivePlan(renderer);
33269
33532
  const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
33270
33533
  if (ev.type === "start") {
33271
- renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.activePlan, ev.tool, ev.args));
33534
+ renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.activePlan, ev.tool, ev.args), ev.icon);
33272
33535
  } else {
33273
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
33536
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
33274
33537
  if (ev.tool === "plan" || ev.tool === "todo") {
33275
33538
  this.refreshActivePlan(renderer);
33276
33539
  }
@@ -33389,6 +33652,9 @@ ${t("image.clipboard_empty")}`));
33389
33652
  const apiLine = pc2.dim(` API: ${result.promptTokens} prompt + ${result.completionTokens} completion = ${result.totalTokens} total`);
33390
33653
  console.log(apiLine);
33391
33654
  }
33655
+ if (result.totalCost !== undefined && result.totalCost > 0) {
33656
+ console.log(pc2.yellow(` ${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
33657
+ }
33392
33658
  } else if (ui?.showCompaction && result.compactionCount !== undefined) {
33393
33659
  if (result.compactionCount > this.lastCompactionShown) {
33394
33660
  this.lastCompactionShown = result.compactionCount;
@@ -33420,9 +33686,9 @@ ${t("image.clipboard_empty")}`));
33420
33686
  const infos = this.pluginManager.getPluginInfos().filter(({ plugin: plugin3 }) => !plugin3.isBuiltin);
33421
33687
  if (infos.length > 0) {
33422
33688
  const pluginNames = infos.map(({ plugin: plugin3, source }) => {
33423
- const version4 = plugin3.version ? `${pc2.dim(plugin3.version)}` : "";
33689
+ const version3 = plugin3.version ? `${pc2.dim(plugin3.version)}` : "";
33424
33690
  const origin = source ? pc2.dim(` [${source}]`) : "";
33425
- return `${plugin3.name}${version4}${origin}`;
33691
+ return `${plugin3.name}${version3}${origin}`;
33426
33692
  }).join(", ");
33427
33693
  row(t("repl.plugins_label"), `${pc2.white(String(infos.length))} active ${pc2.dim(`(${pluginNames})`)}`);
33428
33694
  }
@@ -33463,33 +33729,27 @@ ${t("image.clipboard_empty")}`));
33463
33729
  if (lspEnabled) {
33464
33730
  row(t("repl.lsp_label"), pc2.dim("…"));
33465
33731
  }
33466
- const boxLines = box(info, {
33467
- title: t("repl.title", { version: version3 }),
33468
- width: headerWidth
33469
- });
33470
- for (const line of boxLines) {
33471
- console.log(line);
33732
+ for (const line of info) {
33733
+ console.log(pc2.dim(line).trimEnd());
33472
33734
  }
33473
33735
  console.log();
33474
33736
  this.rl.prompt();
33475
33737
  if (lspEnabled) {
33476
33738
  this.probeLspBanner().then((lspSummary) => {
33477
33739
  if (lspSummary)
33478
- this.updateLspRow(boxLines.length, headerWidth, lspSummary);
33740
+ this.updateLspRow(headerWidth, lspSummary);
33479
33741
  }).catch(() => {});
33480
33742
  }
33481
33743
  }
33482
- updateLspRow(boxLineCount, headerWidth, summary) {
33744
+ updateLspRow(headerWidth, summary) {
33483
33745
  if (this.agentRunning || !process.stdout.isTTY)
33484
33746
  return;
33485
33747
  if ((process.stdout.columns ?? 96) < headerWidth)
33486
33748
  return;
33487
- const inner = headerWidth - 4;
33488
33749
  const rowText = `${pc2.yellow(t("repl.lsp_label"))} ${summary.replace(/\s+/g, " ").trim()}`;
33489
- if (stringWidth(rowText) > inner)
33750
+ if (stringWidth(rowText) > headerWidth)
33490
33751
  return;
33491
- const padded = rowText + " ".repeat(Math.max(0, inner - stringWidth(rowText)));
33492
- const line = pc2.dim("│") + " " + padded + " " + pc2.dim("│");
33752
+ const line = pc2.dim(rowText);
33493
33753
  process.stdout.write(`\x1B[2A\r\x1B[2K${line}\x1B[2B\r`);
33494
33754
  }
33495
33755
  async probeLspBanner() {
@@ -33522,9 +33782,15 @@ ${t("image.clipboard_empty")}`));
33522
33782
  // src/cli/run-result.ts
33523
33783
  init_i18n();
33524
33784
  init_colors();
33785
+ init_prices();
33525
33786
  function printRunResult(result, flush) {
33526
33787
  flush();
33527
33788
  if (result.success) {
33789
+ if (result.totalCost !== undefined && result.totalCost > 0) {
33790
+ console.log(pc2.dim(`${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
33791
+ } else if (result.totalTokens !== undefined && result.totalTokens > 0) {
33792
+ console.log(pc2.dim(`${t("repl.tokens", { tokens: result.totalTokens })}`));
33793
+ }
33528
33794
  if (!result.text) {
33529
33795
  console.log(pc2.yellow(t("cli.no_output")));
33530
33796
  }
@@ -33540,9 +33806,9 @@ init_config2();
33540
33806
  init_i18n();
33541
33807
  init_colors();
33542
33808
  import { existsSync as existsSync54, readFileSync as readFileSync36 } from "fs";
33543
- import { join as join48, dirname as dirname19 } from "path";
33809
+ import { join as join48, dirname as dirname18 } from "path";
33544
33810
  import { homedir as homedir18 } from "os";
33545
- import { fileURLToPath as fileURLToPath7 } from "url";
33811
+ import { fileURLToPath as fileURLToPath6 } from "url";
33546
33812
 
33547
33813
  // src/modules/updater/index.ts
33548
33814
  init_checker();
@@ -33642,8 +33908,8 @@ class UpdaterModule {
33642
33908
  }
33643
33909
  }
33644
33910
  // src/cli/main.ts
33645
- function readVersion5() {
33646
- const here = dirname19(fileURLToPath7(import.meta.url));
33911
+ function readVersion4() {
33912
+ const here = dirname18(fileURLToPath6(import.meta.url));
33647
33913
  const candidates = [join48(here, "..", "..", "package.json"), join48(here, "..", "package.json")];
33648
33914
  for (const p of candidates) {
33649
33915
  if (existsSync54(p)) {
@@ -33658,7 +33924,7 @@ function readVersion5() {
33658
33924
  }
33659
33925
  function startAutoUpdate(config) {
33660
33926
  try {
33661
- const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent", {
33927
+ const module = new UpdaterModule(config.updater, readVersion4(), "micro-models-agent", {
33662
33928
  info: (m) => process.stderr.write(pc2.dim(m) + `
33663
33929
  `),
33664
33930
  warn: (m) => process.stderr.write(pc2.yellow(m) + `
@@ -33701,7 +33967,8 @@ async function main() {
33701
33967
  contextLimit: result2.contextLimit ?? null,
33702
33968
  promptTokens: result2.promptTokens ?? null,
33703
33969
  completionTokens: result2.completionTokens ?? null,
33704
- totalTokens: result2.totalTokens ?? null
33970
+ totalTokens: result2.totalTokens ?? null,
33971
+ totalCost: result2.totalCost ?? null
33705
33972
  }, null, 2));
33706
33973
  process.stdout.write(`
33707
33974
  `);
@@ -33714,9 +33981,9 @@ async function main() {
33714
33981
  });
33715
33982
  const result = await agent.run(prompt, (chunk) => renderer.text(chunk), (meta) => renderer.meta(meta), (ev) => {
33716
33983
  if (ev.type === "start") {
33717
- renderer.toolStart(ev.tool, ev.args);
33984
+ renderer.toolStart(ev.tool, ev.args, undefined, ev.icon);
33718
33985
  } else {
33719
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
33986
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
33720
33987
  }
33721
33988
  }, (phase) => {
33722
33989
  if (phase === "thinking") {