micro-models-agent 0.61.1 → 0.62.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.
package/dist/main.js CHANGED
@@ -3188,6 +3188,25 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
3188
3188
  "repl.cost": "Total cost: {cost}",
3189
3189
  "repl.cost_breakdown": "By provider: {breakdown}",
3190
3190
  "repl.tokens": "Tokens used: {tokens}",
3191
+ "repl.cache": "Cache: {hit}% hit · saved {saved}",
3192
+ "repl.cache_nosave": "Cache: {hit}% hit",
3193
+ "repl.prefix": "Prefix stable: {stable}% · broke: {cause}",
3194
+ "cache.cause.system": "system prompt",
3195
+ "cache.cause.tools": "tool set",
3196
+ "cache.cause.history": "history",
3197
+ "cache.cause.volatile": "volatile content",
3198
+ "cache.cause.unknown": "unknown",
3199
+ "cli.session_usage": "API usage: {prompt} prompt + {completion} completion = {total} tokens",
3200
+ "cli.session_cache": "Cache: {hit}% hit ({cached} cached / {uncached} uncached)",
3201
+ "cli.usage": "Show provider balance/usage (OpenRouter)",
3202
+ "cli.usage_unsupported": 'Provider "{provider}" does not expose an API balance. Only OpenRouter does; OpenCode Zen/Go show it in the web dashboard.',
3203
+ "cli.usage_no_key": "No API key configured for the active provider.",
3204
+ "cli.usage_error": "Failed to fetch balance: {error}",
3205
+ "cli.usage_key_usage": "Key usage: {usage}",
3206
+ "cli.usage_key_limit": "Key limit: {limit} · remaining {remaining}",
3207
+ "cli.usage_balance": "Balance: {balance}",
3208
+ "cli.usage_account": "Account: {credits} purchased · {used} used",
3209
+ "cli.usage_empty": "No balance information returned.",
3191
3210
  "repl.ctrl_c_interrupt": `
3192
3211
  [Ctrl+C] Stopping agent... (press again to force)`,
3193
3212
  "exec.stop_directive": 'STOP. Step {stepId} ("{description}") took {iterations} iterations with no progress. DO NOT continue this step. Immediately call: plan update step={stepId} status=done (if code works despite warnings) OR plan update step={stepId} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.',
@@ -4005,6 +4024,25 @@ var init_ru = __esm(() => {
4005
4024
  "repl.cost": "Итого потрачено: {cost}",
4006
4025
  "repl.cost_breakdown": "По провайдерам: {breakdown}",
4007
4026
  "repl.tokens": "Потрачено токенов: {tokens}",
4027
+ "repl.cache": "Кеш: {hit}% попаданий · сэкономлено {saved}",
4028
+ "repl.cache_nosave": "Кеш: {hit}% попаданий",
4029
+ "repl.prefix": "Префикс стабилен: {stable}% · сломалось: {cause}",
4030
+ "cache.cause.system": "системный промпт",
4031
+ "cache.cause.tools": "набор инструментов",
4032
+ "cache.cause.history": "история",
4033
+ "cache.cause.volatile": "волатильное содержимое",
4034
+ "cache.cause.unknown": "неизвестно",
4035
+ "cli.session_usage": "API-использование: {prompt} prompt + {completion} completion = {total} токенов",
4036
+ "cli.session_cache": "Кеш: {hit}% попаданий ({cached} из кеша / {uncached} новых)",
4037
+ "cli.usage": "Показать баланс/расход провайдера (OpenRouter)",
4038
+ "cli.usage_unsupported": 'Провайдер "{provider}" не отдаёт баланс по API. Это умеет только OpenRouter; OpenCode Zen/Go показывают его в веб-дашборде.',
4039
+ "cli.usage_no_key": "Для активного провайдера не настроен API-ключ.",
4040
+ "cli.usage_error": "Не удалось получить баланс: {error}",
4041
+ "cli.usage_key_usage": "Расход по ключу: {usage}",
4042
+ "cli.usage_key_limit": "Лимит ключа: {limit} · осталось {remaining}",
4043
+ "cli.usage_balance": "Баланс: {balance}",
4044
+ "cli.usage_account": "Аккаунт: куплено {credits} · израсходовано {used}",
4045
+ "cli.usage_empty": "Провайдер не вернул данных о балансе.",
4008
4046
  "repl.ctrl_c_interrupt": `
4009
4047
  [Ctrl+C] Остановка агента... (ещё раз — принудительно)`,
4010
4048
  "exec.stop_directive": 'STOP. Step {stepId} ("{description}") took {iterations} iterations with no progress. DO NOT continue this step. Immediately call: plan update step={stepId} status=done (if code works despite warnings) OR plan update step={stepId} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.',
@@ -5893,6 +5931,73 @@ class StreamState {
5893
5931
  }
5894
5932
  }
5895
5933
 
5934
+ // src/llm/cache-usage.ts
5935
+ function num(value) {
5936
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
5937
+ }
5938
+ function isRecord(value) {
5939
+ return typeof value === "object" && value !== null;
5940
+ }
5941
+ function parseCacheUsage(usage, format, estimatedPromptTokens) {
5942
+ if (!isRecord(usage))
5943
+ return;
5944
+ switch (format) {
5945
+ case "openai": {
5946
+ const details = usage.prompt_tokens_details;
5947
+ if (!isRecord(details))
5948
+ return;
5949
+ const cached = num(details.cached_tokens);
5950
+ if (cached === undefined)
5951
+ return;
5952
+ const prompt = num(usage.prompt_tokens) ?? cached;
5953
+ const write = num(details.cache_write_tokens) ?? 0;
5954
+ return {
5955
+ cachedTokens: cached,
5956
+ cacheWriteTokens: write,
5957
+ uncachedTokens: Math.max(0, prompt - cached),
5958
+ source: "api"
5959
+ };
5960
+ }
5961
+ case "deepseek": {
5962
+ const hit = num(usage.prompt_cache_hit_tokens);
5963
+ const miss = num(usage.prompt_cache_miss_tokens);
5964
+ if (hit === undefined && miss === undefined)
5965
+ return;
5966
+ return {
5967
+ cachedTokens: hit ?? 0,
5968
+ cacheWriteTokens: 0,
5969
+ uncachedTokens: miss ?? 0,
5970
+ source: "api"
5971
+ };
5972
+ }
5973
+ case "anthropic": {
5974
+ const read = num(usage.cache_read_input_tokens);
5975
+ const creation = num(usage.cache_creation_input_tokens);
5976
+ if (read === undefined && creation === undefined)
5977
+ return;
5978
+ return {
5979
+ cachedTokens: read ?? 0,
5980
+ cacheWriteTokens: creation ?? 0,
5981
+ uncachedTokens: num(usage.input_tokens) ?? 0,
5982
+ source: "api"
5983
+ };
5984
+ }
5985
+ case "ollama": {
5986
+ const evaluated = num(usage.prompt_eval_count);
5987
+ if (evaluated === undefined || estimatedPromptTokens === undefined)
5988
+ return;
5989
+ return {
5990
+ cachedTokens: Math.max(0, estimatedPromptTokens - evaluated),
5991
+ cacheWriteTokens: 0,
5992
+ uncachedTokens: evaluated,
5993
+ source: "derived"
5994
+ };
5995
+ }
5996
+ default:
5997
+ return;
5998
+ }
5999
+ }
6000
+
5896
6001
  // src/llm/openai-compat.ts
5897
6002
  var exports_openai_compat = {};
5898
6003
  __export(exports_openai_compat, {
@@ -5911,6 +6016,14 @@ function buildRequestBody(opts) {
5911
6016
  };
5912
6017
  if (opts.maxTokens !== undefined)
5913
6018
  body.max_tokens = opts.maxTokens;
6019
+ if (opts.cachePrompt)
6020
+ body.cache_prompt = true;
6021
+ if (opts.promptCacheKey)
6022
+ body.prompt_cache_key = opts.promptCacheKey;
6023
+ if (opts.sessionId)
6024
+ body.session_id = opts.sessionId;
6025
+ if (opts.stream && opts.streamUsage)
6026
+ body.stream_options = { include_usage: true };
5914
6027
  const strategy = opts.reasoningStrategy ?? "openai-effort";
5915
6028
  const level = opts.reasoningEffort;
5916
6029
  if (strategy === "openai-effort" && level && level !== "default") {
@@ -5944,10 +6057,14 @@ class OpenAICompatProvider {
5944
6057
  debug;
5945
6058
  getSessionId;
5946
6059
  userAgent;
6060
+ cache;
6061
+ cacheReport;
5947
6062
  constructor(config) {
5948
6063
  this.config = config;
5949
6064
  this.model = config.model;
5950
6065
  this.contextWindow = config.contextWindow ?? 32768;
6066
+ this.cache = config.cache;
6067
+ this.cacheReport = config.cache?.report ?? "openai";
5951
6068
  this.tokenCounter = new TokenCounter;
5952
6069
  this.retryConfig = config.retry ?? {
5953
6070
  maxRetries: 3,
@@ -6050,7 +6167,8 @@ class OpenAICompatProvider {
6050
6167
  stream: true,
6051
6168
  maxTokens,
6052
6169
  reasoningEffort: options?.reasoningEffort,
6053
- reasoningStrategy: options?.reasoningStrategy
6170
+ reasoningStrategy: options?.reasoningStrategy,
6171
+ ...this.cacheHints()
6054
6172
  });
6055
6173
  this.debug?.("LLM stream request", {
6056
6174
  baseUrl: this.config.baseUrl,
@@ -6158,7 +6276,8 @@ class OpenAICompatProvider {
6158
6276
  st.usage = {
6159
6277
  promptTokens: parsed.usage.prompt_tokens ?? 0,
6160
6278
  completionTokens: parsed.usage.completion_tokens ?? 0,
6161
- totalTokens: parsed.usage.total_tokens ?? 0
6279
+ totalTokens: parsed.usage.total_tokens ?? 0,
6280
+ cache: parseCacheUsage(parsed.usage, this.cacheReport)
6162
6281
  };
6163
6282
  this.debug?.("LLM stream usage", { ...st.usage });
6164
6283
  } else if (parsed.error) {
@@ -6246,6 +6365,15 @@ class OpenAICompatProvider {
6246
6365
  }
6247
6366
  return st.sawDone;
6248
6367
  }
6368
+ cacheHints() {
6369
+ const sessionId = this.getSessionId?.();
6370
+ return {
6371
+ cachePrompt: this.cache?.requestCachePrompt === true,
6372
+ promptCacheKey: this.cache?.requestPromptCacheKey ? sessionId : undefined,
6373
+ sessionId: this.cache?.requestSessionId ? sessionId : undefined,
6374
+ streamUsage: this.cache?.requestStreamUsage === true
6375
+ };
6376
+ }
6249
6377
  buildRequestSetup(signal) {
6250
6378
  const headers = {
6251
6379
  "Content-Type": "application/json",
@@ -6255,7 +6383,8 @@ class OpenAICompatProvider {
6255
6383
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6256
6384
  }
6257
6385
  const sessionId = this.getSessionId?.();
6258
- if (sessionId)
6386
+ const sendSessionHeader = this.cache ? this.cache.sessionHeader : true;
6387
+ if (sessionId && sendSessionHeader)
6259
6388
  headers["x-opencode-session"] = sessionId;
6260
6389
  const controller = new AbortController;
6261
6390
  let timedOut = false;
@@ -6294,7 +6423,8 @@ class OpenAICompatProvider {
6294
6423
  stream: false,
6295
6424
  maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
6296
6425
  reasoningEffort: options?.reasoningEffort,
6297
- reasoningStrategy: options?.reasoningStrategy
6426
+ reasoningStrategy: options?.reasoningStrategy,
6427
+ ...this.cacheHints()
6298
6428
  });
6299
6429
  const { headers, abortSignal, cleanup, isTimeout } = this.buildRequestSetup(signal);
6300
6430
  try {
@@ -6347,7 +6477,8 @@ class OpenAICompatProvider {
6347
6477
  usage: {
6348
6478
  promptTokens: data.usage.prompt_tokens ?? 0,
6349
6479
  completionTokens: data.usage.completion_tokens ?? 0,
6350
- totalTokens: data.usage.total_tokens ?? 0
6480
+ totalTokens: data.usage.total_tokens ?? 0,
6481
+ cache: parseCacheUsage(data.usage, this.cacheReport)
6351
6482
  }
6352
6483
  });
6353
6484
  }
@@ -6469,6 +6600,7 @@ function openaiCompat(opts) {
6469
6600
  rateLimits,
6470
6601
  maxCompletionTokens: opts.maxCompletionTokens,
6471
6602
  getSessionId: opts.getSessionId,
6603
+ cache: opts.capabilities?.cache,
6472
6604
  logger: opts.logger
6473
6605
  });
6474
6606
  }
@@ -6486,7 +6618,17 @@ var init_presets = __esm(() => {
6486
6618
  reasoningStrategy: "prompt-tag",
6487
6619
  listModels: true,
6488
6620
  requiresKey: false,
6489
- auth: "bearer"
6621
+ auth: "bearer",
6622
+ cache: {
6623
+ mechanism: "local",
6624
+ report: "openai",
6625
+ sessionHeader: false,
6626
+ requestCachePrompt: false,
6627
+ requestPromptCacheKey: false,
6628
+ requestSessionId: false,
6629
+ requestCacheControl: false,
6630
+ requestStreamUsage: true
6631
+ }
6490
6632
  },
6491
6633
  create: openaiCompat
6492
6634
  };
@@ -6501,7 +6643,17 @@ var init_presets = __esm(() => {
6501
6643
  reasoningStrategy: "openai-effort",
6502
6644
  listModels: true,
6503
6645
  requiresKey: true,
6504
- auth: "bearer"
6646
+ auth: "bearer",
6647
+ cache: {
6648
+ mechanism: "auto",
6649
+ report: "openai",
6650
+ sessionHeader: false,
6651
+ requestCachePrompt: false,
6652
+ requestPromptCacheKey: false,
6653
+ requestSessionId: true,
6654
+ requestCacheControl: false,
6655
+ requestStreamUsage: true
6656
+ }
6505
6657
  },
6506
6658
  create: openaiCompat
6507
6659
  };
@@ -6516,7 +6668,17 @@ var init_presets = __esm(() => {
6516
6668
  reasoningStrategy: "openai-effort",
6517
6669
  listModels: true,
6518
6670
  requiresKey: true,
6519
- auth: "bearer"
6671
+ auth: "bearer",
6672
+ cache: {
6673
+ mechanism: "auto",
6674
+ report: "openai",
6675
+ sessionHeader: false,
6676
+ requestCachePrompt: false,
6677
+ requestPromptCacheKey: true,
6678
+ requestSessionId: false,
6679
+ requestCacheControl: false,
6680
+ requestStreamUsage: true
6681
+ }
6520
6682
  },
6521
6683
  create: openaiCompat
6522
6684
  };
@@ -6531,7 +6693,17 @@ var init_presets = __esm(() => {
6531
6693
  reasoningStrategy: "none",
6532
6694
  listModels: false,
6533
6695
  requiresKey: true,
6534
- auth: "header"
6696
+ auth: "header",
6697
+ cache: {
6698
+ mechanism: "explicit",
6699
+ report: "anthropic",
6700
+ sessionHeader: false,
6701
+ requestCachePrompt: false,
6702
+ requestPromptCacheKey: false,
6703
+ requestSessionId: false,
6704
+ requestCacheControl: true,
6705
+ requestStreamUsage: false
6706
+ }
6535
6707
  },
6536
6708
  create: openaiCompat
6537
6709
  };
@@ -6546,7 +6718,17 @@ var init_presets = __esm(() => {
6546
6718
  reasoningStrategy: "openai-effort",
6547
6719
  listModels: true,
6548
6720
  requiresKey: false,
6549
- auth: "bearer"
6721
+ auth: "bearer",
6722
+ cache: {
6723
+ mechanism: "auto",
6724
+ report: "openai",
6725
+ sessionHeader: true,
6726
+ requestCachePrompt: false,
6727
+ requestPromptCacheKey: false,
6728
+ requestSessionId: false,
6729
+ requestCacheControl: false,
6730
+ requestStreamUsage: true
6731
+ }
6550
6732
  },
6551
6733
  create: openaiCompat
6552
6734
  };
@@ -6561,7 +6743,17 @@ var init_presets = __esm(() => {
6561
6743
  reasoningStrategy: "openai-effort",
6562
6744
  listModels: true,
6563
6745
  requiresKey: false,
6564
- auth: "bearer"
6746
+ auth: "bearer",
6747
+ cache: {
6748
+ mechanism: "auto",
6749
+ report: "openai",
6750
+ sessionHeader: true,
6751
+ requestCachePrompt: false,
6752
+ requestPromptCacheKey: false,
6753
+ requestSessionId: false,
6754
+ requestCacheControl: false,
6755
+ requestStreamUsage: true
6756
+ }
6565
6757
  },
6566
6758
  create: openaiCompat
6567
6759
  };
@@ -6624,6 +6816,25 @@ var init_create = __esm(() => {
6624
6816
  init_presets();
6625
6817
  });
6626
6818
 
6819
+ // src/modules/providers/cache.ts
6820
+ function resolveCacheCapability(spec, override) {
6821
+ const base = spec?.capabilities.cache ?? NO_CACHE_CAPABILITY;
6822
+ return { ...base, ...override ?? {} };
6823
+ }
6824
+ var NO_CACHE_CAPABILITY;
6825
+ var init_cache = __esm(() => {
6826
+ NO_CACHE_CAPABILITY = {
6827
+ mechanism: "none",
6828
+ report: "none",
6829
+ sessionHeader: false,
6830
+ requestCachePrompt: false,
6831
+ requestPromptCacheKey: false,
6832
+ requestSessionId: false,
6833
+ requestCacheControl: false,
6834
+ requestStreamUsage: false
6835
+ };
6836
+ });
6837
+
6627
6838
  // src/modules/providers/manager.ts
6628
6839
  class ProviderManager {
6629
6840
  entries;
@@ -6747,17 +6958,25 @@ class ProviderManager {
6747
6958
  rateLimits: entry.rateLimits ?? this.opts.rateLimits,
6748
6959
  maxCompletionTokens: entry.maxCompletionTokens,
6749
6960
  getSessionId: this.opts.getSessionId,
6961
+ capabilities: this.resolveCapabilities(entry),
6750
6962
  logger: this.opts.logger
6751
6963
  }, this.registry);
6752
6964
  this.cache.set(key, provider);
6753
6965
  return provider;
6754
6966
  }
6967
+ resolveCapabilities(entry) {
6968
+ const spec = this.registry.get(entry.type);
6969
+ if (!spec)
6970
+ return { cache: resolveCacheCapability(undefined, entry.cache) };
6971
+ return { ...spec.capabilities, cache: resolveCacheCapability(spec, entry.cache) };
6972
+ }
6755
6973
  resetCache() {
6756
6974
  this.cache.clear();
6757
6975
  }
6758
6976
  }
6759
6977
  var init_manager = __esm(() => {
6760
6978
  init_create();
6979
+ init_cache();
6761
6980
  init_presets();
6762
6981
  });
6763
6982
 
@@ -8160,8 +8379,8 @@ function buildDiff(oldLines, newLines) {
8160
8379
  return result;
8161
8380
  }
8162
8381
  function formatLine(line, maxNumWidth) {
8163
- const num = line.type === "remove" ? line.oldNum : line.newNum;
8164
- const numStr = num !== null ? String(num).padStart(maxNumWidth) : " ".repeat(maxNumWidth);
8382
+ const num2 = line.type === "remove" ? line.oldNum : line.newNum;
8383
+ const numStr = num2 !== null ? String(num2).padStart(maxNumWidth) : " ".repeat(maxNumWidth);
8165
8384
  if (line.type === "remove") {
8166
8385
  return `${numStr} ${pc2.red("-")} ${line.content}`;
8167
8386
  } else if (line.type === "add") {
@@ -10296,7 +10515,12 @@ class SessionLogger {
10296
10515
  completionTokens: usage.completionTokens,
10297
10516
  totalTokens: usage.totalTokens,
10298
10517
  source: usage.source,
10299
- durationMs: usage.durationMs
10518
+ durationMs: usage.durationMs,
10519
+ cacheStable: usage.prefix ? Number(usage.prefix.stableRatio.toFixed(4)) : undefined,
10520
+ cacheCause: usage.prefix?.cause,
10521
+ cachedTokens: usage.cache?.cachedTokens,
10522
+ cacheWriteTokens: usage.cache?.cacheWriteTokens,
10523
+ cacheSource: usage.cache?.source
10300
10524
  });
10301
10525
  }
10302
10526
  logError(message) {
@@ -10946,52 +11170,52 @@ ${output}
10946
11170
  }
10947
11171
  function parseNumber() {
10948
11172
  const start = i;
10949
- let num = "";
11173
+ let num2 = "";
10950
11174
  let invalid = false;
10951
11175
  if (text[i] === "-") {
10952
- num += text[i];
11176
+ num2 += text[i];
10953
11177
  i++;
10954
11178
  if (!isDigit(text[i]) && atEndOfNumber()) {
10955
- num += "0";
11179
+ num2 += "0";
10956
11180
  }
10957
11181
  }
10958
11182
  if (text[i] === "0" && isDigit(text[i + 1])) {
10959
11183
  invalid = true;
10960
11184
  }
10961
11185
  while (isDigit(text[i])) {
10962
- num += text[i];
11186
+ num2 += text[i];
10963
11187
  i++;
10964
11188
  }
10965
11189
  if (text[i] === ".") {
10966
- if (num === "" || num === "-") {
10967
- num += "0";
11190
+ if (num2 === "" || num2 === "-") {
11191
+ num2 += "0";
10968
11192
  }
10969
- num += text[i];
11193
+ num2 += text[i];
10970
11194
  i++;
10971
11195
  if (!isDigit(text[i])) {
10972
- num += "0";
11196
+ num2 += "0";
10973
11197
  }
10974
11198
  while (isDigit(text[i])) {
10975
- num += text[i];
11199
+ num2 += text[i];
10976
11200
  i++;
10977
11201
  }
10978
11202
  }
10979
11203
  if (i > start) {
10980
11204
  if (text[i] === "e" || text[i] === "E") {
10981
- if (num === "-") {
11205
+ if (num2 === "-") {
10982
11206
  invalid = true;
10983
11207
  }
10984
- num += text[i];
11208
+ num2 += text[i];
10985
11209
  i++;
10986
11210
  if (text[i] === "-" || text[i] === "+") {
10987
- num += text[i];
11211
+ num2 += text[i];
10988
11212
  i++;
10989
11213
  }
10990
11214
  if (!isDigit(text[i])) {
10991
- num += "0";
11215
+ num2 += "0";
10992
11216
  }
10993
11217
  while (isDigit(text[i])) {
10994
- num += text[i];
11218
+ num2 += text[i];
10995
11219
  i++;
10996
11220
  }
10997
11221
  }
@@ -10999,7 +11223,7 @@ ${output}
10999
11223
  i = start;
11000
11224
  return false;
11001
11225
  }
11002
- output += invalid ? `"${text.substring(start, i)}"` : num;
11226
+ output += invalid ? `"${text.substring(start, i)}"` : num2;
11003
11227
  return true;
11004
11228
  }
11005
11229
  return false;
@@ -12791,6 +13015,24 @@ var init_moe_executor = __esm(() => {
12791
13015
  // src/modules/lsp/project-root.ts
12792
13016
  import { existsSync as existsSync24 } from "fs";
12793
13017
  import { dirname as dirname9, join as join14, relative as relative3, isAbsolute as isAbsolute3 } from "path";
13018
+ import { platform as platform4 } from "os";
13019
+ function resolveTscCommand(startDir, maxLevels = 8) {
13020
+ const runtime = process.execPath;
13021
+ let dir = startDir;
13022
+ for (let i = 0;i < maxLevels; i++) {
13023
+ const js = join14(dir, "node_modules", "typescript", "bin", "tsc");
13024
+ if (existsSync24(js))
13025
+ return `"${runtime}" "${js}"`;
13026
+ const stub = join14(dir, "node_modules", ".bin", platform4() === "win32" ? "tsc.cmd" : "tsc");
13027
+ if (existsSync24(stub))
13028
+ return `"${stub}"`;
13029
+ const parent = dirname9(dir);
13030
+ if (parent === dir)
13031
+ break;
13032
+ dir = parent;
13033
+ }
13034
+ return null;
13035
+ }
12794
13036
  function findProjectRoot(filePath, baseDir, markers) {
12795
13037
  if (!markers || markers.length === 0)
12796
13038
  return baseDir;
@@ -13163,7 +13405,10 @@ function findTypecheckRoot(baseDir, existingFiles = []) {
13163
13405
  return best?.root ?? null;
13164
13406
  }
13165
13407
  async function runTypecheck(baseDir) {
13166
- const entry = processRegistry.start("npx --no-install tsc --noEmit --skipLibCheck", baseDir);
13408
+ const tsc = resolveTscCommand(baseDir);
13409
+ if (!tsc)
13410
+ return null;
13411
+ const entry = processRegistry.start(`${tsc} --noEmit --skipLibCheck`, baseDir);
13167
13412
  const exited = await processRegistry.waitForExit(entry.id, 90000);
13168
13413
  const output = entry.log.join(`
13169
13414
  `);
@@ -13176,6 +13421,7 @@ var SKIP_DIRS, TEST_EXT_RE, PY_TEST_RE, TEST_STEP_RE;
13176
13421
  var init_audit_runners = __esm(() => {
13177
13422
  init_bash();
13178
13423
  init_processes();
13424
+ init_project_root();
13179
13425
  SKIP_DIRS = new Set([
13180
13426
  "node_modules",
13181
13427
  ".git",
@@ -13354,7 +13600,7 @@ var init_auditor = __esm(() => {
13354
13600
 
13355
13601
  // src/modules/execution/verifier.ts
13356
13602
  import { existsSync as existsSync27, readFileSync as readFileSync14 } from "fs";
13357
- import { resolve as resolve13, extname as extname3, join as join17 } from "path";
13603
+ import { resolve as resolve13, extname as extname3, dirname as dirname11, join as join17 } from "path";
13358
13604
  import { spawn as spawn4 } from "child_process";
13359
13605
 
13360
13606
  class StepVerifier {
@@ -13386,8 +13632,11 @@ class StepVerifier {
13386
13632
  if (!existsSync27(tsconfigPath)) {
13387
13633
  return { passed: true, message: "No tsconfig.json found — skipping type check" };
13388
13634
  }
13635
+ const tsc = resolveTscCommand(this.baseDir);
13636
+ if (!tsc)
13637
+ return { passed: true, message: "tsc not installed — skipping type check" };
13389
13638
  try {
13390
- await this.runAsync("npx tsc --noEmit", this.baseDir, 60000);
13639
+ await this.runAsync(`${tsc} --noEmit`, this.baseDir, 60000);
13391
13640
  return { passed: true, message: "TypeScript type check passed" };
13392
13641
  } catch (e) {
13393
13642
  const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
@@ -13400,8 +13649,11 @@ class StepVerifier {
13400
13649
  if (!existsSync27(tsconfigPath)) {
13401
13650
  return { passed: true, message: "No tsconfig.json found — skipping type check" };
13402
13651
  }
13652
+ const tsc = resolveTscCommand(projectRoot);
13653
+ if (!tsc)
13654
+ return { passed: true, message: "tsc not installed — skipping type check" };
13403
13655
  try {
13404
- await this.runAsync("npx tsc --noEmit --skipLibCheck", projectRoot, 60000);
13656
+ await this.runAsync(`${tsc} --noEmit --skipLibCheck`, projectRoot, 60000);
13405
13657
  return { passed: true, message: "TypeScript type check passed" };
13406
13658
  } catch (e) {
13407
13659
  const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
@@ -13554,8 +13806,11 @@ class StepVerifier {
13554
13806
  async validateSyntax(filePath) {
13555
13807
  const ext = extname3(filePath);
13556
13808
  if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
13809
+ const tsc = resolveTscCommand(dirname11(filePath));
13810
+ if (!tsc)
13811
+ return true;
13557
13812
  try {
13558
- await this.runAsync(`npx tsc --noEmit --skipLibCheck "${filePath}"`, this.baseDir, 1e4);
13813
+ await this.runAsync(`${tsc} --noEmit --skipLibCheck "${filePath}"`, this.baseDir, 20000);
13559
13814
  return true;
13560
13815
  } catch (err) {
13561
13816
  if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
@@ -13885,11 +14140,18 @@ function resolvePrice(model, config) {
13885
14140
  return direct;
13886
14141
  return ZEN_PRICES[bare];
13887
14142
  }
13888
- function calculateCost(model, promptTokens, completionTokens, config) {
14143
+ function calculateCostDetailed(model, promptTokens, completionTokens, config, cache) {
13889
14144
  const price = resolvePrice(model, config);
13890
14145
  if (!price)
13891
14146
  return;
13892
- return promptTokens / 1e6 * price.input + completionTokens / 1e6 * price.output;
14147
+ const cached = Math.max(0, cache?.cachedTokens ?? 0);
14148
+ const write = Math.max(0, cache?.cacheWriteTokens ?? 0);
14149
+ const uncached = Math.max(0, promptTokens - cached);
14150
+ const cachedRate = price.cachedInput ?? price.input;
14151
+ const writeRate = price.cacheWrite ?? 0;
14152
+ const cost = uncached / 1e6 * price.input + cached / 1e6 * cachedRate + write / 1e6 * writeRate + completionTokens / 1e6 * price.output;
14153
+ const saved = cached / 1e6 * (price.input - cachedRate);
14154
+ return { cost, saved };
13893
14155
  }
13894
14156
  function formatCost(cost) {
13895
14157
  if (cost === 0)
@@ -13909,12 +14171,12 @@ var init_prices = __esm(() => {
13909
14171
  "nemotron-3-ultra-free": { input: 0, output: 0 },
13910
14172
  "nemotron-3.5-lightning-free": { input: 0, output: 0 },
13911
14173
  "muse-spark-1.2-contributor-free": { input: 0, output: 0 },
13912
- "minimax-m3": { input: 0.3, output: 1.2 },
13913
- "minimax-m2.7": { input: 0.3, output: 1.2 },
13914
- "minimax-m2.5": { input: 0.3, output: 1.2 },
13915
- "glm-5.2": { input: 1.4, output: 4.4 },
13916
- "glm-5.1": { input: 1.4, output: 4.4 },
13917
- "glm-5": { input: 1, output: 3.2 },
14174
+ "minimax-m3": { input: 0.3, output: 1.2, cachedInput: 0.06 },
14175
+ "minimax-m2.7": { input: 0.3, output: 1.2, cachedInput: 0.06 },
14176
+ "minimax-m2.5": { input: 0.3, output: 1.2, cachedInput: 0.06 },
14177
+ "glm-5.2": { input: 1.4, output: 4.4, cachedInput: 0.26 },
14178
+ "glm-5.1": { input: 1.4, output: 4.4, cachedInput: 0.26 },
14179
+ "glm-5": { input: 1, output: 3.2, cachedInput: 0.2 },
13918
14180
  "glm-5.3": { input: 1.4, output: 4.4 },
13919
14181
  "kimi-k2.7-code": { input: 0.95, output: 4 },
13920
14182
  "kimi-k3": { input: 3, output: 15 },
@@ -13976,6 +14238,7 @@ class CostTracker {
13976
14238
  config;
13977
14239
  model;
13978
14240
  _total = 0;
14241
+ _saved = 0;
13979
14242
  _known = false;
13980
14243
  byProvider = new Map;
13981
14244
  constructor(model, config) {
@@ -13985,24 +14248,28 @@ class CostTracker {
13985
14248
  setModel(model) {
13986
14249
  this.model = model;
13987
14250
  }
13988
- record(promptTokens, completionTokens, provider) {
14251
+ record(promptTokens, completionTokens, provider, cache) {
13989
14252
  const key = provider || "unknown";
13990
- const cost = calculateCost(this.model, promptTokens, completionTokens, this.config);
13991
- if (cost === undefined)
14253
+ const result = calculateCostDetailed(this.model, promptTokens, completionTokens, this.config, cache);
14254
+ if (!result)
13992
14255
  return;
13993
14256
  this._known = true;
13994
- this._total += cost;
14257
+ this._total += result.cost;
14258
+ this._saved += result.saved;
13995
14259
  let entry = this.byProvider.get(key);
13996
14260
  if (!entry) {
13997
14261
  entry = { total: 0, known: false };
13998
14262
  this.byProvider.set(key, entry);
13999
14263
  }
14000
14264
  entry.known = true;
14001
- entry.total += cost;
14265
+ entry.total += result.cost;
14002
14266
  }
14003
14267
  get total() {
14004
14268
  return this._known ? this._total : undefined;
14005
14269
  }
14270
+ get saved() {
14271
+ return this._known ? this._saved : undefined;
14272
+ }
14006
14273
  totalFor(provider) {
14007
14274
  const entry = this.byProvider.get(provider);
14008
14275
  return entry && entry.known ? entry.total : undefined;
@@ -14083,6 +14350,10 @@ function createLoopState() {
14083
14350
  apiCompletionChars: 0,
14084
14351
  estimatedPromptTokensTotal: 0,
14085
14352
  totalLlmDuration: 0,
14353
+ cacheCachedTotal: 0,
14354
+ cacheUncachedTotal: 0,
14355
+ cacheWriteTotal: 0,
14356
+ cacheSource: "none",
14086
14357
  auditRetries: 0,
14087
14358
  lastAuditSummary: "",
14088
14359
  emptyResponseRetries: 0,
@@ -14189,6 +14460,7 @@ class TokenTracker {
14189
14460
  }
14190
14461
  beginIteration(state, estimatedPromptTokens) {
14191
14462
  state.estimatedPromptTokensTotal += estimatedPromptTokens;
14463
+ state.cacheUsage = undefined;
14192
14464
  return {
14193
14465
  promptBefore: state.apiPromptTokens,
14194
14466
  completionBefore: state.apiCompletionTokens
@@ -14197,6 +14469,7 @@ class TokenTracker {
14197
14469
  recordApiUsage(state, baseline, usage) {
14198
14470
  state.apiPromptTokens = baseline.promptBefore + usage.promptTokens;
14199
14471
  state.apiCompletionTokens = baseline.completionBefore + usage.completionTokens;
14472
+ state.cacheUsage = usage.cache;
14200
14473
  }
14201
14474
  addCompletionChars(state, chars) {
14202
14475
  state.apiCompletionChars += chars;
@@ -14207,14 +14480,22 @@ class TokenTracker {
14207
14480
  const source = usagePrompt > 0 || usageCompletion > 0 ? "api" : "estimate";
14208
14481
  const prompt = source === "api" ? usagePrompt : contextTokens;
14209
14482
  const completion = source === "api" ? usageCompletion : estimateTokens(textContent);
14483
+ if (state.cacheUsage) {
14484
+ state.cacheCachedTotal += state.cacheUsage.cachedTokens;
14485
+ state.cacheUncachedTotal += state.cacheUsage.uncachedTokens;
14486
+ state.cacheWriteTotal += state.cacheUsage.cacheWriteTokens;
14487
+ state.cacheSource = state.cacheUsage.source;
14488
+ }
14210
14489
  slog.logLlmUsage(iteration, {
14211
14490
  promptTokens: prompt,
14212
14491
  completionTokens: completion,
14213
14492
  totalTokens: prompt + completion,
14214
14493
  source,
14215
- durationMs
14494
+ durationMs,
14495
+ prefix: state.prefixDelta,
14496
+ cache: state.cacheUsage
14216
14497
  });
14217
- this.costTracker.record(prompt, completion, this.getProviderName());
14498
+ this.costTracker.record(prompt, completion, this.getProviderName(), source === "api" ? state.cacheUsage : undefined);
14218
14499
  }
14219
14500
  resolveFinal(state) {
14220
14501
  if (state.apiPromptTokens > 0 || state.apiCompletionTokens > 0) {
@@ -14233,6 +14514,75 @@ var init_token_tracker = __esm(() => {
14233
14514
  init_token_counter();
14234
14515
  });
14235
14516
 
14517
+ // src/core/agent/prefix-monitor.ts
14518
+ function serializeTools(tools) {
14519
+ return tools.map((t2) => `${t2.name}\x01${t2.description}\x01${JSON.stringify(t2.parameters)}`).join("\x02");
14520
+ }
14521
+ function serializeMessages(messages) {
14522
+ return messages.map((m) => `${m.role}\x01${typeof m.content === "string" ? m.content : JSON.stringify(m.content)}`).join("\x02");
14523
+ }
14524
+ function buildPromptSnapshot(messages, tools) {
14525
+ const system = [];
14526
+ const rest = [];
14527
+ for (const m of messages) {
14528
+ if (m.role === "system")
14529
+ system.push(m);
14530
+ else
14531
+ rest.push(m);
14532
+ }
14533
+ return {
14534
+ system: serializeMessages(system),
14535
+ tools: serializeTools(tools),
14536
+ history: serializeMessages(rest)
14537
+ };
14538
+ }
14539
+ function frame(s) {
14540
+ const full = s.system + SEP + s.tools + SEP + s.history;
14541
+ const toolsStart = s.system.length + SEP.length;
14542
+ const historyStart = toolsStart + s.tools.length + SEP.length;
14543
+ return { full, toolsStart, historyStart };
14544
+ }
14545
+ function diffPrompt(prev, curr, countTokens) {
14546
+ const c = frame(curr);
14547
+ const currTokens = countTokens(c.full);
14548
+ if (!prev) {
14549
+ return {
14550
+ prevTokens: 0,
14551
+ currTokens,
14552
+ commonPrefixTokens: 0,
14553
+ stableRatio: 0,
14554
+ cause: "unknown"
14555
+ };
14556
+ }
14557
+ const p = frame(prev);
14558
+ const prevTokens = countTokens(p.full);
14559
+ const maxCp = Math.min(p.full.length, c.full.length);
14560
+ let cp = 0;
14561
+ while (cp < maxCp && p.full[cp] === c.full[cp])
14562
+ cp++;
14563
+ const commonPrefixTokens = countTokens(c.full.slice(0, cp));
14564
+ const stableRatio = currTokens === 0 ? 0 : Math.min(1, commonPrefixTokens / currTokens);
14565
+ if (cp === maxCp) {
14566
+ return { prevTokens, currTokens, commonPrefixTokens, stableRatio, cause: "none" };
14567
+ }
14568
+ let cause;
14569
+ if (cp >= c.historyStart)
14570
+ cause = "history";
14571
+ else if (cp >= c.toolsStart)
14572
+ cause = "tools";
14573
+ else
14574
+ cause = "system";
14575
+ const window = c.full.slice(Math.max(0, cp - 40), cp + 160) + p.full.slice(Math.max(0, cp - 40), cp + 160);
14576
+ if (VOLATILE_RE.test(window))
14577
+ cause = "volatile";
14578
+ return { prevTokens, currTokens, commonPrefixTokens, stableRatio, cause };
14579
+ }
14580
+ var SEP = `
14581
+ `, VOLATILE_RE;
14582
+ var init_prefix_monitor = __esm(() => {
14583
+ VOLATILE_RE = /(\d{4}-\d{2}-\d{2}|\d{2}:\d{2}:\d{2}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}|[A-Za-z]:\\|\\AppData\\|\\Temp\\|\/tmp\/)/;
14584
+ });
14585
+
14236
14586
  // src/core/agent/reasoning-resolver.ts
14237
14587
  class ReasoningEffortResolver {
14238
14588
  resolve(deps) {
@@ -15261,6 +15611,12 @@ class Agent {
15261
15611
  compactionService.compactIfNeeded(state);
15262
15612
  this.refreshSystemPrompt();
15263
15613
  const history = contextManager.getActiveHistory();
15614
+ const promptSnapshot = buildPromptSnapshot(history, allToolsForBudget);
15615
+ state.prefixDelta = diffPrompt(state.prevPrompt, promptSnapshot, estimateTokens);
15616
+ state.prevPrompt = promptSnapshot;
15617
+ if (state.prefixDelta.cause === "system" || state.prefixDelta.cause === "tools" || state.prefixDelta.cause === "history" || state.prefixDelta.cause === "volatile") {
15618
+ state.lastPrefixBreak = state.prefixDelta;
15619
+ }
15264
15620
  slog.logToolDefs(allToolsForBudget.length, allToolsForBudget.map((t2) => t2.name), state.iteration);
15265
15621
  if (state.iteration === 1) {
15266
15622
  const { blocks } = this.buildSystemPrompt();
@@ -15509,6 +15865,7 @@ class Agent {
15509
15865
  const tokensUsed = contextManager.getEstimatedTokens();
15510
15866
  const budget = contextManager.getBudget();
15511
15867
  const usageTokens = tokenTracker.resolveFinal(state);
15868
+ const cacheStats = this.buildCacheStats(state);
15512
15869
  if (state.iteration >= config.maxToolIterations && !state.finalAnswerAccepted) {
15513
15870
  return {
15514
15871
  success: false,
@@ -15522,6 +15879,7 @@ class Agent {
15522
15879
  totalTokens: usageTokens.total,
15523
15880
  totalCost: this.costTracker.total,
15524
15881
  costBreakdown: this.costTracker.breakdown(),
15882
+ cache: cacheStats,
15525
15883
  compactionCount: contextManager.getCompactionCount(),
15526
15884
  contextQuality: contextManager.getQuality()
15527
15885
  };
@@ -15538,11 +15896,29 @@ class Agent {
15538
15896
  totalTokens: usageTokens.total,
15539
15897
  totalCost: this.costTracker.total,
15540
15898
  costBreakdown: this.costTracker.breakdown(),
15899
+ cache: cacheStats,
15541
15900
  compactionCount: contextManager.getCompactionCount(),
15542
15901
  contextQuality: contextManager.getQuality(),
15543
15902
  llmDurationMs: state.totalLlmDuration
15544
15903
  };
15545
15904
  }
15905
+ buildCacheStats(state) {
15906
+ const hasTokens = state.cacheCachedTotal > 0 || state.cacheUncachedTotal > 0 || state.cacheWriteTotal > 0;
15907
+ const prefix = state.lastPrefixBreak ?? state.prefixDelta;
15908
+ if (!hasTokens && prefix?.stableRatio === undefined)
15909
+ return;
15910
+ const denom = state.cacheCachedTotal + state.cacheUncachedTotal;
15911
+ return {
15912
+ cachedTokens: state.cacheCachedTotal,
15913
+ uncachedTokens: state.cacheUncachedTotal,
15914
+ cacheWriteTokens: state.cacheWriteTotal,
15915
+ hitRate: denom > 0 ? state.cacheCachedTotal / denom : 0,
15916
+ saved: this.costTracker.saved,
15917
+ prefixStable: prefix?.stableRatio,
15918
+ prefixCause: prefix?.cause,
15919
+ source: state.cacheSource
15920
+ };
15921
+ }
15546
15922
  clearContext() {
15547
15923
  this.deps.contextManager.clear();
15548
15924
  this.systemPromptAdded = false;
@@ -15647,6 +16023,7 @@ var init_agent = __esm(() => {
15647
16023
  init_loop_state();
15648
16024
  init_compaction();
15649
16025
  init_token_tracker();
16026
+ init_prefix_monitor();
15650
16027
  init_reasoning_resolver();
15651
16028
  init_context_renderer();
15652
16029
  init_tool_batch();
@@ -16538,8 +16915,8 @@ var init_detector = __esm(() => {
16538
16915
  // src/modules/lsp/command.ts
16539
16916
  import { delimiter, join as join21 } from "path";
16540
16917
  import { existsSync as existsSync30 } from "fs";
16541
- import { platform as platform4 } from "os";
16542
- function resolveSpawnCommand(command, platformName = platform4(), pathEnv = process.env.PATH ?? "") {
16918
+ import { platform as platform5 } from "os";
16919
+ function resolveSpawnCommand(command, platformName = platform5(), pathEnv = process.env.PATH ?? "") {
16543
16920
  if (platformName !== "win32")
16544
16921
  return command;
16545
16922
  if (command.includes("/") || command.includes("\\") || WIN_EXTS.some((ext) => command.toLowerCase().endsWith(ext))) {
@@ -16571,7 +16948,7 @@ var init_command = __esm(() => {
16571
16948
  });
16572
16949
 
16573
16950
  // src/modules/updater/checker.ts
16574
- import { platform as platform5 } from "os";
16951
+ import { platform as platform6 } from "os";
16575
16952
  function semverGt(a, b) {
16576
16953
  const pa = a.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
16577
16954
  const pb = b.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
@@ -16642,7 +17019,7 @@ class Updater {
16642
17019
  buildInstallCommand(latest) {
16643
17020
  const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
16644
17021
  const command = resolveSpawnCommand("npm");
16645
- if (platform5() === "win32" && /\.(cmd|bat)$/i.test(command)) {
17022
+ if (platform6() === "win32" && /\.(cmd|bat)$/i.test(command)) {
16646
17023
  return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
16647
17024
  }
16648
17025
  return { command, args: npmArgs };
@@ -20167,11 +20544,11 @@ async function readClipboardImage() {
20167
20544
  return readClipboardFallback();
20168
20545
  }
20169
20546
  async function readClipboardFallback() {
20170
- const { platform: platform6 } = await import("os");
20547
+ const { platform: platform7 } = await import("os");
20171
20548
  const { execSync } = await import("child_process");
20172
20549
  const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
20173
20550
  const { join: join30 } = await import("path");
20174
- if (platform6() !== "linux")
20551
+ if (platform7() !== "linux")
20175
20552
  return null;
20176
20553
  const tmpPath = join30(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
20177
20554
  const commands = [
@@ -20847,7 +21224,7 @@ var init_auto_fixer = __esm(() => {
20847
21224
  import { spawn as spawn7, execSync } from "child_process";
20848
21225
  import { existsSync as existsSync37, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "fs";
20849
21226
  import { resolve as resolve17, extname as extname6, join as join31 } from "path";
20850
- import { platform as platform6 } from "os";
21227
+ import { platform as platform7 } from "os";
20851
21228
  function lintCacheKey(baseDir, lintScript) {
20852
21229
  return `${baseDir}::${lintScript}`;
20853
21230
  }
@@ -20868,7 +21245,7 @@ function contentHash2(content) {
20868
21245
  function getWinDecoder() {
20869
21246
  if (_winDecoder !== undefined)
20870
21247
  return _winDecoder;
20871
- if (platform6() !== "win32") {
21248
+ if (platform7() !== "win32") {
20872
21249
  _winDecoder = new TextDecoder("utf-8");
20873
21250
  return _winDecoder;
20874
21251
  }
@@ -21029,6 +21406,10 @@ ${stdout}`;
21029
21406
  if (!existsSync37(tsconfigPath)) {
21030
21407
  return;
21031
21408
  }
21409
+ const tsc = resolveTscCommand(projectRoot);
21410
+ if (!tsc) {
21411
+ return;
21412
+ }
21032
21413
  const now = Date.now();
21033
21414
  if (this._checkPromise && now - this._checkTimestamp < TYPE_CHECK_DEBOUNCE_MS) {
21034
21415
  const error2 = await this._checkPromise;
@@ -21040,10 +21421,10 @@ ${stdout}`;
21040
21421
  return;
21041
21422
  }
21042
21423
  this._checkTimestamp = now;
21043
- this._checkPromise = this.runTscCheck(projectRoot, signal);
21424
+ this._checkPromise = this.runTscCheck(tsc, projectRoot, signal);
21044
21425
  const error = await this._checkPromise;
21045
21426
  if (error) {
21046
- const { stdout, stderr } = await runAsync(`npx tsc --noEmit --skipLibCheck 2>&1`, projectRoot, 30000, signal).catch(() => ({ stdout: "", stderr: error }));
21427
+ const { stdout, stderr } = await runAsync(`${tsc} --noEmit --skipLibCheck 2>&1`, projectRoot, 30000, signal).catch(() => ({ stdout: "", stderr: error }));
21047
21428
  const output = stderr || stdout;
21048
21429
  const errors = parseTscOutput(output);
21049
21430
  if (errors.length > 0) {
@@ -21072,9 +21453,9 @@ ${stdout}`;
21072
21453
  }
21073
21454
  }
21074
21455
  }
21075
- async runTscCheck(baseDir, signal) {
21456
+ async runTscCheck(tsc, baseDir, signal) {
21076
21457
  try {
21077
- const { stdout, stderr } = await runAsync(`npx tsc --noEmit --skipLibCheck`, baseDir, 30000, signal);
21458
+ const { stdout, stderr } = await runAsync(`${tsc} --noEmit --skipLibCheck`, baseDir, 30000, signal);
21078
21459
  return null;
21079
21460
  } catch (err) {
21080
21461
  if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
@@ -21560,7 +21941,7 @@ function switchStepToDelete(plan, step, save) {
21560
21941
  }
21561
21942
 
21562
21943
  // src/modules/execution/execution-plugin.ts
21563
- import { platform as platform7 } from "os";
21944
+ import { platform as platform8 } from "os";
21564
21945
  function normalizeBrokenPath(p) {
21565
21946
  return toForwardSlash(p).replace(/^\.\//, "");
21566
21947
  }
@@ -21793,7 +22174,7 @@ Last compile error: ${first[1]}`;
21793
22174
  if (call.name === "bash") {
21794
22175
  deps.stuckDetector.recordBashAttempt(false);
21795
22176
  }
21796
- if (call.name === "bash" && platform7() === "win32") {
22177
+ if (call.name === "bash" && platform8() === "win32") {
21797
22178
  const cmd = String(call.arguments?.command ?? "");
21798
22179
  const forbidden = forbiddenWindowsCommand(cmd);
21799
22180
  if (forbidden) {
@@ -23687,7 +24068,7 @@ class ProfileCompressor {
23687
24068
  // src/modules/user-profile/profile.ts
23688
24069
  import { readFileSync as readFileSync26, writeFileSync as writeFileSync17, existsSync as existsSync42, mkdirSync as mkdirSync18 } from "fs";
23689
24070
  import { join as join35 } from "path";
23690
- import { homedir as homedir10, hostname, platform as platform8, type } from "os";
24071
+ import { homedir as homedir10, hostname, platform as platform9, type } from "os";
23691
24072
  import { env } from "process";
23692
24073
 
23693
24074
  class UserProfile {
@@ -23699,7 +24080,7 @@ class UserProfile {
23699
24080
  }
23700
24081
  collect() {
23701
24082
  this.info = {
23702
- platform: platform8(),
24083
+ platform: platform9(),
23703
24084
  os: `${type()} ${hostname()}`,
23704
24085
  hostname: hostname(),
23705
24086
  shell: env.SHELL || env.ComSpec || "unknown",
@@ -24021,7 +24402,7 @@ var init_browser2 = __esm(() => {
24021
24402
  // src/modules/lsp/client.ts
24022
24403
  import { spawn as spawn8, execSync as execSync2 } from "child_process";
24023
24404
  import { resolve as resolve19 } from "path";
24024
- import { platform as platform9 } from "os";
24405
+ import { platform as platform10 } from "os";
24025
24406
 
24026
24407
  class FileOnlyLogger {
24027
24408
  logger;
@@ -24141,7 +24522,7 @@ class LspClient {
24141
24522
  if (config.command === "npx" && effectiveArgs.length > 0) {
24142
24523
  const binaryName = this.extractBinaryFromNpxArgs(effectiveArgs);
24143
24524
  if (binaryName) {
24144
- const whichCmd = platform9() === "win32" ? `where ${binaryName}` : `which ${binaryName}`;
24525
+ const whichCmd = platform10() === "win32" ? `where ${binaryName}` : `which ${binaryName}`;
24145
24526
  try {
24146
24527
  execSync2(whichCmd, { stdio: "pipe", timeout: 3000 });
24147
24528
  effectiveCommand = binaryName;
@@ -24154,7 +24535,7 @@ class LspClient {
24154
24535
  }
24155
24536
  return new Promise((resolve20, reject) => {
24156
24537
  const args = effectiveArgs;
24157
- const isWin = platform9() === "win32";
24538
+ const isWin = platform10() === "win32";
24158
24539
  let spawnCommand = resolveSpawnCommand(effectiveCommand);
24159
24540
  let spawnArgs = args;
24160
24541
  const spawnOpts = {
@@ -24760,8 +25141,11 @@ async function runCheck(config, baseDir, deps) {
24760
25141
  return { lines: lines.slice(0, STARTUP_CHECK_ERROR_CAP) };
24761
25142
  }
24762
25143
  async function runTscDefault(projectRoot, timeoutMs) {
25144
+ const tsc = resolveTscCommand(projectRoot);
25145
+ if (!tsc)
25146
+ return [];
24763
25147
  return new Promise((resolve22) => {
24764
- const child = spawn9("npx", ["tsc", "--noEmit", "--skipLibCheck"], {
25148
+ const child = spawn9(`${tsc} --noEmit --skipLibCheck`, {
24765
25149
  cwd: projectRoot,
24766
25150
  shell: true,
24767
25151
  windowsHide: true,
@@ -25165,7 +25549,7 @@ class IndexCache {
25165
25549
  }
25166
25550
  }
25167
25551
  }
25168
- var init_cache = () => {};
25552
+ var init_cache2 = () => {};
25169
25553
 
25170
25554
  // src/modules/indexer/map-select.ts
25171
25555
  function isNoiseFile(file) {
@@ -25637,7 +26021,7 @@ ${stackLine}` : summary;
25637
26021
  var MAP_FILE_LIMIT = 80;
25638
26022
  var init_module6 = __esm(() => {
25639
26023
  init_walker();
25640
- init_cache();
26024
+ init_cache2();
25641
26025
  init_map_select();
25642
26026
  init_project_profile();
25643
26027
  init_i18n();
@@ -25648,7 +26032,7 @@ var init_module6 = __esm(() => {
25648
26032
  // src/modules/indexer/index.ts
25649
26033
  var init_indexer = __esm(() => {
25650
26034
  init_walker();
25651
- init_cache();
26035
+ init_cache2();
25652
26036
  init_module6();
25653
26037
  });
25654
26038
 
@@ -25993,7 +26377,7 @@ import { spawnSync as spawnSync3 } from "child_process";
25993
26377
  import { createRequire as createRequire2 } from "module";
25994
26378
  import { join as join43, dirname as dirname16 } from "path";
25995
26379
  import { fileURLToPath as fileURLToPath3 } from "url";
25996
- import { arch, homedir as homedir12, hostname as hostname2, platform as platform10, release } from "os";
26380
+ import { arch, homedir as homedir12, hostname as hostname2, platform as platform11, release } from "os";
25997
26381
  import { env as env2 } from "process";
25998
26382
  function readEngineRequirement() {
25999
26383
  const here = dirname16(fileURLToPath3(import.meta.url));
@@ -26139,7 +26523,7 @@ function collectEnvironment(opts) {
26139
26523
  mmaVersion: readMmaVersion(),
26140
26524
  runtime,
26141
26525
  os: {
26142
- platform: platform10(),
26526
+ platform: platform11(),
26143
26527
  arch: arch(),
26144
26528
  release: release(),
26145
26529
  hostname: hostname2(),
@@ -26496,6 +26880,7 @@ var init_set_thinking = __esm(() => {
26496
26880
  // src/core/bootstrap.ts
26497
26881
  var exports_bootstrap = {};
26498
26882
  __export(exports_bootstrap, {
26883
+ setOneShotMode: () => setOneShotMode,
26499
26884
  buildSystemInfo: () => buildSystemInfo,
26500
26885
  bootstrap: () => bootstrap
26501
26886
  });
@@ -26611,6 +26996,9 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
26611
26996
  return lines.join(`
26612
26997
  `);
26613
26998
  }
26999
+ function setOneShotMode(value) {
27000
+ oneShotMode = value;
27001
+ }
26614
27002
  async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reasoningLevel) {
26615
27003
  const dir = configDir || process.env.MMA_CONFIG_DIR || join46(homedir15(), ".mma");
26616
27004
  const projectConfigPath = projectDir ? join46(projectDir, ".mmrc") : join46(process.cwd(), ".mmrc");
@@ -26846,7 +27234,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
26846
27234
  moduleRegistry.register(lspModule);
26847
27235
  registerBuiltinPlugin(pluginManager, lspModule.getPlugin());
26848
27236
  let startupCheckBlock = null;
26849
- const startupCheckPromise = !exitOnComplete ? runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir, { logger: lspLogger }) : Promise.resolve(null);
27237
+ const startupCheckPromise = !oneShotMode && !exitOnComplete ? runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir, { logger: lspLogger }) : Promise.resolve(null);
26850
27238
  const moduleTools = moduleRegistry.collectToolDefinitions();
26851
27239
  for (const tool of moduleTools) {
26852
27240
  toolRegistry.register(tool);
@@ -27001,6 +27389,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
27001
27389
  contextProbe: contextProbePromise
27002
27390
  };
27003
27391
  }
27392
+ var oneShotMode = false;
27004
27393
  var init_bootstrap = __esm(() => {
27005
27394
  init_config2();
27006
27395
  init_app_logger();
@@ -27476,9 +27865,9 @@ class Spinner {
27476
27865
  tick() {
27477
27866
  if (!this.timer)
27478
27867
  return;
27479
- const frame = FRAMES[this.frame % FRAMES.length];
27868
+ const frame2 = FRAMES[this.frame % FRAMES.length];
27480
27869
  this.frame++;
27481
- this.stream.write("\r" + pc2.cyan(frame) + " " + this.message + "\x1B[K");
27870
+ this.stream.write("\r" + pc2.cyan(frame2) + " " + this.message + "\x1B[K");
27482
27871
  }
27483
27872
  }
27484
27873
  var FRAMES;
@@ -30074,9 +30463,9 @@ var require_stringifyNumber = __commonJS((exports) => {
30074
30463
  function stringifyNumber({ format, minFractionDigits, tag, value }) {
30075
30464
  if (typeof value === "bigint")
30076
30465
  return String(value);
30077
- const num = typeof value === "number" ? value : Number(value);
30078
- if (!isFinite(num))
30079
- return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
30466
+ const num3 = typeof value === "number" ? value : Number(value);
30467
+ if (!isFinite(num3))
30468
+ return isNaN(num3) ? ".nan" : num3 < 0 ? "-.inf" : ".inf";
30080
30469
  let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
30081
30470
  if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
30082
30471
  let i = n.indexOf(".");
@@ -30113,8 +30502,8 @@ var require_float = __commonJS((exports) => {
30113
30502
  test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,
30114
30503
  resolve: (str) => parseFloat(str),
30115
30504
  stringify(node) {
30116
- const num = Number(node.value);
30117
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
30505
+ const num3 = Number(node.value);
30506
+ return isFinite(num3) ? num3.toExponential() : stringifyNumber.stringifyNumber(node);
30118
30507
  }
30119
30508
  };
30120
30509
  var float = {
@@ -30518,8 +30907,8 @@ var require_float2 = __commonJS((exports) => {
30518
30907
  test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,
30519
30908
  resolve: (str) => parseFloat(str.replace(/_/g, "")),
30520
30909
  stringify(node) {
30521
- const num = Number(node.value);
30522
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
30910
+ const num3 = Number(node.value);
30911
+ return isFinite(num3) ? num3.toExponential() : stringifyNumber.stringifyNumber(node);
30523
30912
  }
30524
30913
  };
30525
30914
  var float = {
@@ -30709,23 +31098,23 @@ var require_timestamp = __commonJS((exports) => {
30709
31098
  function parseSexagesimal(str, asBigInt) {
30710
31099
  const sign = str[0];
30711
31100
  const parts = sign === "-" || sign === "+" ? str.substring(1) : str;
30712
- const num = (n) => asBigInt ? BigInt(n) : Number(n);
30713
- const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0));
30714
- return sign === "-" ? num(-1) * res : res;
31101
+ const num3 = (n) => asBigInt ? BigInt(n) : Number(n);
31102
+ const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num3(60) + num3(p), num3(0));
31103
+ return sign === "-" ? num3(-1) * res : res;
30715
31104
  }
30716
31105
  function stringifySexagesimal(node) {
30717
31106
  let { value } = node;
30718
- let num = (n) => n;
31107
+ let num3 = (n) => n;
30719
31108
  if (typeof value === "bigint")
30720
- num = (n) => BigInt(n);
31109
+ num3 = (n) => BigInt(n);
30721
31110
  else if (isNaN(value) || !isFinite(value))
30722
31111
  return stringifyNumber.stringifyNumber(node);
30723
31112
  let sign = "";
30724
31113
  if (value < 0) {
30725
31114
  sign = "-";
30726
- value *= num(-1);
31115
+ value *= num3(-1);
30727
31116
  }
30728
- const _60 = num(60);
31117
+ const _60 = num3(60);
30729
31118
  const parts = [value % _60];
30730
31119
  if (value < 60) {
30731
31120
  parts.unshift(0);
@@ -37566,6 +37955,63 @@ init_version();
37566
37955
  init_map_command();
37567
37956
  init_budget();
37568
37957
 
37958
+ // src/llm/provider-budget.ts
37959
+ var DEFAULT_TIMEOUT_MS = 5000;
37960
+ function num2(value) {
37961
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
37962
+ }
37963
+ async function getJson(url, apiKey, timeoutMs) {
37964
+ const response = await fetch(url, {
37965
+ headers: { Authorization: `Bearer ${apiKey}` },
37966
+ signal: AbortSignal.timeout(timeoutMs)
37967
+ });
37968
+ if (!response.ok) {
37969
+ throw new Error(`HTTP ${response.status}`);
37970
+ }
37971
+ return response.json();
37972
+ }
37973
+ async function fetchProviderBudget(provider, baseUrl, apiKey, options) {
37974
+ if (provider !== "openrouter") {
37975
+ options.log("debug", `budget: provider "${provider}" does not expose an API balance`);
37976
+ return { budget: null, reason: "unsupported" };
37977
+ }
37978
+ if (!apiKey) {
37979
+ options.log("debug", "budget: no api key configured");
37980
+ return { budget: null, reason: "no-key" };
37981
+ }
37982
+ const base = baseUrl.replace(/\/$/, "");
37983
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
37984
+ try {
37985
+ const keyBody = await getJson(`${base}/key`, apiKey, timeoutMs);
37986
+ const data = keyBody?.data ?? {};
37987
+ const budget = { provider, source: "openrouter" };
37988
+ budget.keyLabel = typeof data.label === "string" ? data.label : undefined;
37989
+ budget.keyUsageUsd = num2(data.usage);
37990
+ budget.keyLimitUsd = num2(data.limit);
37991
+ budget.keyRemainingUsd = num2(data.limit_remaining);
37992
+ budget.isFreeTier = typeof data.is_free_tier === "boolean" ? data.is_free_tier : undefined;
37993
+ try {
37994
+ const creditsBody = await getJson(`${base}/credits`, apiKey, timeoutMs);
37995
+ const c = creditsBody?.data ?? {};
37996
+ budget.totalCreditsUsd = num2(c.total_credits);
37997
+ budget.totalUsageUsd = num2(c.total_usage);
37998
+ if (budget.totalCreditsUsd !== undefined && budget.totalUsageUsd !== undefined) {
37999
+ budget.balanceUsd = budget.totalCreditsUsd - budget.totalUsageUsd;
38000
+ }
38001
+ } catch (creditsErr) {
38002
+ options.log("debug", `budget: /credits unavailable (${creditsErr instanceof Error ? creditsErr.message : String(creditsErr)})`);
38003
+ }
38004
+ return { budget, reason: "ok" };
38005
+ } catch (err) {
38006
+ const message = err instanceof Error ? err.message : String(err);
38007
+ options.log("warn", `budget: request failed (${message})`);
38008
+ return { budget: null, reason: "error", error: message };
38009
+ }
38010
+ }
38011
+
38012
+ // src/cli/commands.ts
38013
+ init_prices();
38014
+
37569
38015
  // src/modules/updater/changelog-reader.ts
37570
38016
  import { readFileSync as readFileSync35, existsSync as existsSync53 } from "fs";
37571
38017
  import { dirname as dirname20, join as join48 } from "path";
@@ -37854,6 +38300,53 @@ function buildContextCommand(program2) {
37854
38300
  console.log(t("cli.context_set", { size: contextWindow }));
37855
38301
  });
37856
38302
  }
38303
+ function buildUsageCommand(program2) {
38304
+ program2.command("usage").description(t("cli.usage")).action(async () => {
38305
+ const { config, logger: logger4 } = await bootstrap();
38306
+ const { type: provider, baseUrl, apiKey } = config.provider;
38307
+ const result = await fetchProviderBudget(provider, baseUrl, apiKey, {
38308
+ log: (level, message) => level === "warn" ? logger4.warn(message) : logger4.debug(message)
38309
+ });
38310
+ if (result.reason === "unsupported") {
38311
+ console.log(t("cli.usage_unsupported", { provider }));
38312
+ return;
38313
+ }
38314
+ if (result.reason === "no-key") {
38315
+ console.log(t("cli.usage_no_key"));
38316
+ return;
38317
+ }
38318
+ if (result.reason === "error" || !result.budget) {
38319
+ console.log(t("cli.usage_error", { error: result.error ?? "" }));
38320
+ return;
38321
+ }
38322
+ const b = result.budget;
38323
+ let printed = false;
38324
+ if (b.keyUsageUsd !== undefined) {
38325
+ console.log(t("cli.usage_key_usage", { usage: formatCost(b.keyUsageUsd) }));
38326
+ printed = true;
38327
+ }
38328
+ if (b.keyLimitUsd !== undefined && b.keyRemainingUsd !== undefined) {
38329
+ console.log(t("cli.usage_key_limit", {
38330
+ limit: formatCost(b.keyLimitUsd),
38331
+ remaining: formatCost(b.keyRemainingUsd)
38332
+ }));
38333
+ printed = true;
38334
+ }
38335
+ if (b.balanceUsd !== undefined) {
38336
+ console.log(t("cli.usage_balance", { balance: formatCost(b.balanceUsd) }));
38337
+ printed = true;
38338
+ }
38339
+ if (b.totalCreditsUsd !== undefined && b.totalUsageUsd !== undefined) {
38340
+ console.log(t("cli.usage_account", {
38341
+ credits: formatCost(b.totalCreditsUsd),
38342
+ used: formatCost(b.totalUsageUsd)
38343
+ }));
38344
+ printed = true;
38345
+ }
38346
+ if (!printed)
38347
+ console.log(t("cli.usage_empty"));
38348
+ });
38349
+ }
37857
38350
  function buildMapCommand(program2) {
37858
38351
  program2.command("map").description(t("cli.map.description")).argument("[action]", t("cli.map.action"), "summary").argument("[query]", t("cli.map.query")).action(async (action, query) => {
37859
38352
  const { agent } = await bootstrap();
@@ -38010,6 +38503,31 @@ function buildSessionCommands(program2) {
38010
38503
  }
38011
38504
  }
38012
38505
  }
38506
+ const usageEvents = sessionManager.loadSessionLog(id).filter((e) => e.type === "llm_usage");
38507
+ if (usageEvents.length > 0) {
38508
+ let prompt = 0;
38509
+ let completion = 0;
38510
+ let cached = 0;
38511
+ for (const e of usageEvents) {
38512
+ prompt += e.promptTokens ?? 0;
38513
+ completion += e.completionTokens ?? 0;
38514
+ cached += e.cachedTokens ?? 0;
38515
+ }
38516
+ console.log("");
38517
+ console.log(pc2.cyan(t("cli.session_usage", {
38518
+ prompt,
38519
+ completion,
38520
+ total: prompt + completion
38521
+ })));
38522
+ if (cached > 0 && prompt > 0) {
38523
+ const hit = Math.round(cached / prompt * 100);
38524
+ console.log(pc2.dim(t("cli.session_cache", {
38525
+ hit,
38526
+ cached,
38527
+ uncached: Math.max(0, prompt - cached)
38528
+ })));
38529
+ }
38530
+ }
38013
38531
  });
38014
38532
  session2.command("delete").argument("<id>", "Session id").description(t("cli.delete_session")).action(async (id) => {
38015
38533
  const { sessionManager } = await bootstrap();
@@ -38049,6 +38567,7 @@ function createProgram() {
38049
38567
  buildConfigCommands(program2);
38050
38568
  buildModelCommands(program2);
38051
38569
  buildContextCommand(program2);
38570
+ buildUsageCommand(program2);
38052
38571
  buildMapCommand(program2);
38053
38572
  buildProviderCommands(program2);
38054
38573
  buildSessionCommands(program2);
@@ -38056,11 +38575,15 @@ function createProgram() {
38056
38575
  createPluginCommand(program2);
38057
38576
  buildChangelogCommand(program2);
38058
38577
  program2.argument("[prompt...]", "Prompt to execute").description("Run a single prompt").action((prompt) => {});
38059
- for (const cmd of program2.commands) {
38578
+ const attachOneShotExit = (cmd) => {
38060
38579
  cmd.hook("postAction", () => {
38061
38580
  process.exit(0);
38062
38581
  });
38063
- }
38582
+ for (const sub of cmd.commands)
38583
+ attachOneShotExit(sub);
38584
+ };
38585
+ for (const cmd of program2.commands)
38586
+ attachOneShotExit(cmd);
38064
38587
  return program2;
38065
38588
  }
38066
38589
 
@@ -39693,6 +40216,28 @@ function stepContextForTool(plan, tool, args) {
39693
40216
  // src/cli/repl.ts
39694
40217
  init_prices();
39695
40218
 
40219
+ // src/cli/cache-line.ts
40220
+ init_i18n();
40221
+ init_prices();
40222
+ function formatCacheLine(cache) {
40223
+ if (!cache)
40224
+ return;
40225
+ const total = cache.cachedTokens + cache.uncachedTokens;
40226
+ const hit = Math.round(cache.hitRate * 100);
40227
+ if (total > 0) {
40228
+ if (cache.saved !== undefined && cache.saved > 0) {
40229
+ return t("repl.cache", { hit, saved: formatCost(cache.saved) });
40230
+ }
40231
+ return t("repl.cache_nosave", { hit });
40232
+ }
40233
+ const broken = cache.prefixCause !== undefined && cache.prefixCause !== "none" && cache.prefixCause !== "unknown";
40234
+ if (broken && cache.prefixStable !== undefined && cache.prefixStable < 0.98) {
40235
+ const stable = Math.round(cache.prefixStable * 100);
40236
+ return t("repl.prefix", { stable, cause: t(`cache.cause.${cache.prefixCause}`) });
40237
+ }
40238
+ return;
40239
+ }
40240
+
39696
40241
  // src/ui/output.ts
39697
40242
  function writeWarning(text) {
39698
40243
  process.stdout.write(formatWarning(text) + `
@@ -40199,6 +40744,10 @@ ${t("image.clipboard_empty")}`));
40199
40744
  if (result.totalCost !== undefined && result.totalCost > 0) {
40200
40745
  console.log(pc2.yellow(` ${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
40201
40746
  }
40747
+ const cacheLine = formatCacheLine(result.cache);
40748
+ if (cacheLine) {
40749
+ console.log(pc2.dim(` ${cacheLine}`));
40750
+ }
40202
40751
  } else if (ui?.showCompaction && result.compactionCount !== undefined) {
40203
40752
  if (result.compactionCount > this.lastCompactionShown) {
40204
40753
  this.lastCompactionShown = result.compactionCount;
@@ -40383,6 +40932,10 @@ function printRunResult(result, flush) {
40383
40932
  } else if (result.totalTokens !== undefined && result.totalTokens > 0) {
40384
40933
  console.log(pc2.dim(`${t("repl.tokens", { tokens: result.totalTokens })}`));
40385
40934
  }
40935
+ const cacheLine = formatCacheLine(result.cache);
40936
+ if (cacheLine) {
40937
+ console.log(pc2.dim(` ${cacheLine}`));
40938
+ }
40386
40939
  if (!result.text) {
40387
40940
  console.log(pc2.yellow(t("cli.no_output")));
40388
40941
  }
@@ -40621,6 +41174,22 @@ function startAutoUpdate(config) {
40621
41174
  async function main() {
40622
41175
  installCrashHandlers();
40623
41176
  const program2 = createProgram();
41177
+ const valueOpts = new Set(["-d", "--dir", "--reasoning"]);
41178
+ const argv = process.argv.slice(2);
41179
+ let firstPositional;
41180
+ for (let i = 0;i < argv.length; i++) {
41181
+ const a = argv[i];
41182
+ if (a.startsWith("-")) {
41183
+ if (valueOpts.has(a))
41184
+ i++;
41185
+ continue;
41186
+ }
41187
+ firstPositional = a;
41188
+ break;
41189
+ }
41190
+ if (firstPositional && program2.commands.some((c) => c.name() === firstPositional)) {
41191
+ setOneShotMode(true);
41192
+ }
40624
41193
  program2.parse(process.argv);
40625
41194
  const cmdNames = new Set(program2.commands.map((c) => c.name()));
40626
41195
  const opts = program2.opts();
@@ -40672,7 +41241,8 @@ async function main() {
40672
41241
  completionTokens: result2.completionTokens ?? null,
40673
41242
  totalTokens: result2.totalTokens ?? null,
40674
41243
  totalCost: result2.totalCost ?? null,
40675
- costBreakdown: result2.costBreakdown ?? []
41244
+ costBreakdown: result2.costBreakdown ?? [],
41245
+ cache: result2.cache ?? null
40676
41246
  }, null, 2));
40677
41247
  process.stdout.write(`
40678
41248
  `);