micro-models-agent 0.50.1 → 0.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +360 -77
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2276,7 +2276,7 @@ var init_defaults = __esm(() => {
2276
2276
  baseDelay: 1000,
2277
2277
  maxDelay: 30000,
2278
2278
  maxStreamRetries: 2,
2279
- noDataTimeoutMs: 60000
2279
+ noDataTimeoutMs: 180000
2280
2280
  },
2281
2281
  maxToolIterations: 1000,
2282
2282
  stuckThreshold: 6,
@@ -2388,6 +2388,7 @@ The path was joined onto the working directory because it does not exist as give
2388
2388
  "file.truncated": `
2389
2389
  ... (truncated)`,
2390
2390
  "file.read_header": "── {path} ({ext}, {total} lines, lines {from}-{to})",
2391
+ "file.read_range": "lines {from}-{to} of {total}",
2391
2392
  "file.read_truncated": `
2392
2393
 
2393
2394
  [Truncated: {remaining} more lines. Use read_file with offset={next} to continue]`,
@@ -2411,7 +2412,10 @@ The path was joined onto the working directory because it does not exist as give
2411
2412
  "error.llm_retries": "LLM request failed after retries",
2412
2413
  "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}",
2413
2414
  "error.no_response_body": "No response body stream",
2414
- "error.llm_stream_idle": "LLM stream stalled — no data for {timeout}ms",
2415
+ "error.llm_stream_idle": 'LLM stream stalled — no data for {timeout}ms. If the provider buffers streaming (e.g. LM Studio) or generates very long tool calls, raise "retry.noDataTimeoutMs" in ~/.mma/config.json (restart required)',
2416
+ "error.llm_stream_idle_toolcall": 'LLM stream stalled after a tool_call started — no data for {timeout}ms. The provider most likely buffers SSE instead of streaming tool-call argument deltas (seen with LM Studio). Raise "retry.noDataTimeoutMs" in ~/.mma/config.json (restart required)',
2417
+ "error.llm_truncated": 'Response hit the completion token limit ({tokens}) and was cut off before any content arrived. Split the task into smaller outputs or raise "maxCompletionTokens" in the config',
2418
+ "error.llm_truncated_toolcall": 'Response hit the completion token limit ({tokens}) in the middle of a tool_call — its arguments were cut off. Write the file in smaller chunks (several write_file/edit_file calls) or raise "maxCompletionTokens" in the config',
2415
2419
  "error.llm_timeout": "LLM request timed out ({timeout}ms)",
2416
2420
  "env.runtime_node": "Running under Node (v{version}) — clipboard image paste, subagent performance and LSP spawn on Windows degrade. Install Bun (https://bun.sh) for full features.",
2417
2421
  "env.runtime_old": "Runtime version {version} is below the required engines {engine}.",
@@ -2910,6 +2914,7 @@ Use this knowledge to answer the user's question.`,
2910
2914
  "exec.tool_errors_recovery": "Tool {tool} has failed {count} times in a row. Try an alternative: create files directly via write_file, use a different command, or if nothing works — skip this step via plan update step=N status=skipped with a note explaining why.",
2911
2915
  "exec.repetitive_tool": "Called {tool} {count} times with identical arguments and result. Try a different approach — create files directly, change arguments, or check process status via process_log.",
2912
2916
  "exec.consecutive_failures_recovery": "{count} consecutive tool failures. Create files directly via write_file instead of terminal commands. Check that dependencies are installed (npm install). Do not run build/tests until all files are created.",
2917
+ "exec.bash_flailing_recovery": "bash failed in {fails} of the last {window} attempts. Stop using shell commands for file operations on this machine — they do not work. Use the dedicated tools instead: read_file (the header shows line counts), write_file, edit_file, file_info (file size), glob/grep for search",
2913
2918
  "exec.read_only_loop": "No write/exec for {count} tool calls — the agent is only reading/exploring.",
2914
2919
  "exec.read_only_loop_recovery": "{count} consecutive read-only tool calls (read_file/glob/grep/browser) with no writes. STOP exploring and make the edit the task requires: read the file, then call write_file or edit_file. If you cannot finish the task, ask the user instead of re-reading the same files.",
2915
2920
  "exec.plan_warning": 'Current plan step {step} is "{description}", but {tool} is being called for files outside this step. Complete the current step first, then call plan update step={step} status=done before moving to the next step.',
@@ -3103,6 +3108,7 @@ var init_ru = __esm(() => {
3103
3108
  "file.truncated": `
3104
3109
  ... (обрезано)`,
3105
3110
  "file.read_header": "── {path} ({ext}, {total} строк, строки {from}-{to})",
3111
+ "file.read_range": "строки {from}-{to} из {total}",
3106
3112
  "file.read_truncated": `
3107
3113
 
3108
3114
  [Обрезано: ещё {remaining} строк. Продолжите чтение через read_file с offset={next}]`,
@@ -3126,7 +3132,10 @@ var init_ru = __esm(() => {
3126
3132
  "error.llm_retries": "Запрос LLM не удался после повторов",
3127
3133
  "error.llm_429": "Превышен лимит запросов (HTTP 429) для {model} на {baseUrl}. Провайдер ограничивает трафик — особенно строгие free-модели. Получите API-ключ или переключитесь на платную/быструю модель: {baseUrl}",
3128
3134
  "error.no_response_body": "Нет потока тела ответа",
3129
- "error.llm_stream_idle": "Поток LLM завис — нет данных {timeout}мс",
3135
+ "error.llm_stream_idle": 'Поток LLM завис — нет данных {timeout}мс. Если провайдер буферизирует стрим (например, LM Studio) или генерирует очень длинные tool calls, увеличьте "retry.noDataTimeoutMs" в ~/.mma/config.json (нужен рестарт)',
3136
+ "error.llm_stream_idle_toolcall": 'Поток LLM завис после начала tool_call — нет данных {timeout}мс. Скорее всего провайдер буферизирует SSE вместо потоковой передачи аргументов tool_call (наблюдается в LM Studio). Увеличьте "retry.noDataTimeoutMs" в ~/.mma/config.json (нужен рестарт)',
3137
+ "error.llm_truncated": 'Ответ упёрся в лимит токенов генерации ({tokens}) и был обрезан до какого-либо содержимого. Разбейте задачу на меньшие порции вывода или увеличьте "maxCompletionTokens" в конфиге',
3138
+ "error.llm_truncated_toolcall": 'Ответ упёрся в лимит токенов генерации ({tokens}) посреди tool_call — его аргументы обрезаны. Пишите файл частями (несколько вызовов write_file/edit_file) или увеличьте "maxCompletionTokens" в конфиге',
3130
3139
  "error.llm_timeout": "Время запроса LLM истекло ({timeout}мс)",
3131
3140
  "env.runtime_node": "Запущено под Node (v{version}) — вставка изображений из буфера и LSP на Windows работают урезанно. Установите Bun (https://bun.sh) для полного функционала.",
3132
3141
  "env.runtime_old": "Версия рантайма {version} ниже требуемой engines {engine}.",
@@ -3624,6 +3633,7 @@ var init_ru = __esm(() => {
3624
3633
  "exec.tool_errors_recovery": "Инструмент {tool} упал {count} раз подряд. Попробуйте альтернативу: создайте файлы напрямую через write_file, используйте другую команду, или пропустите этот шаг через plan update step=N status=skipped с пометкой почему.",
3625
3634
  "exec.repetitive_tool": "Инструмент {tool} вызван {count} раз с одинаковыми аргументами и результатом. Попробуйте другой подход — создайте файлы напрямую, измените аргументы или проверьте статус процесса через process_log.",
3626
3635
  "exec.consecutive_failures_recovery": "{count} инструментов подряд упали. Создавайте файлы напрямую через write_file вместо команд терминала. Проверьте что зависимости установлены (npm install). Не запускайте сборку/тесты пока не созданы все файлы.",
3636
+ "exec.bash_flailing_recovery": "bash упал в {fails} из последних {window} попыток. Хватит использовать шелл-команды для файловых операций на этой машине — они не работают. Используй специализированные инструменты: read_file (в заголовке число строк), write_file, edit_file, file_info (размер файла), glob/grep для поиска",
3627
3637
  "exec.read_only_loop": "Нет записи/выполнения за {count} вызовов — агент только читает/исследует.",
3628
3638
  "exec.read_only_loop_recovery": "{count} вызовов только для чтения подряд (read_file/glob/grep/browser) без записи. Хватит исследовать — внесите правку, которую требует задача: прочитайте файл, затем вызовите write_file или edit_file. Если не можете завершить задачу — спросите пользователя, а не перечитывайте одни и те же файлы.",
3629
3639
  "exec.plan_warning": 'Текущий шаг плана {step} — "{description}", но вызывается {tool} для файлов вне этого шага. Завершите текущий шаг, вызовите plan update step={step} status=done, затем переходите к следующему.',
@@ -5344,7 +5354,7 @@ class OpenAICompatProvider {
5344
5354
  baseDelay: 1000,
5345
5355
  maxDelay: 30000,
5346
5356
  maxStreamRetries: 2,
5347
- noDataTimeoutMs: 60000
5357
+ noDataTimeoutMs: 180000
5348
5358
  };
5349
5359
  this.rateLimiter = createRateLimiter(config.rateLimits);
5350
5360
  }
@@ -5373,7 +5383,7 @@ class OpenAICompatProvider {
5373
5383
  async* doStream(messages, tools, signal, options) {
5374
5384
  const { baseDelay, maxDelay, maxStreamRetries, noDataTimeoutMs } = this.retryConfig;
5375
5385
  const streamRetries = maxStreamRetries ?? 2;
5376
- const idleTimeoutMs = noDataTimeoutMs ?? 60000;
5386
+ const idleTimeoutMs = noDataTimeoutMs ?? 180000;
5377
5387
  for (let attempt = 0;; attempt++) {
5378
5388
  let emitted = false;
5379
5389
  const onEmit = () => {
@@ -5397,12 +5407,13 @@ class OpenAICompatProvider {
5397
5407
  }
5398
5408
  }
5399
5409
  async* streamOnce(messages, tools, signal, options, onEmit, idleTimeoutMs) {
5410
+ const maxTokens = options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096;
5400
5411
  const body = buildRequestBody({
5401
5412
  model: this.model,
5402
5413
  messages,
5403
5414
  tools,
5404
5415
  stream: true,
5405
- maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
5416
+ maxTokens,
5406
5417
  reasoningEffort: options?.reasoningEffort
5407
5418
  });
5408
5419
  const { headers, abortSignal, cleanup, isTimeout, flagTimeout, controller } = this.buildRequestSetup(signal);
@@ -5440,8 +5451,11 @@ class OpenAICompatProvider {
5440
5451
  const decoder = new TextDecoder;
5441
5452
  let buffer = "";
5442
5453
  const toolCallAccs = new Map;
5454
+ let sawToolCallStart = false;
5443
5455
  let usage;
5444
5456
  let sawDone = false;
5457
+ let lastFinishReason;
5458
+ let sawText = false;
5445
5459
  const readIdle = () => new Promise((resolve, reject) => {
5446
5460
  const idleTimer = setTimeout(() => {
5447
5461
  flagTimeout();
@@ -5450,13 +5464,13 @@ class OpenAICompatProvider {
5450
5464
  reader.read().then((result) => {
5451
5465
  clearTimeout(idleTimer);
5452
5466
  if (isTimeout())
5453
- reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
5467
+ reject(new Error(t(sawToolCallStart ? "error.llm_stream_idle_toolcall" : "error.llm_stream_idle", { timeout: idleTimeoutMs })));
5454
5468
  else
5455
5469
  resolve(result);
5456
5470
  }, (err) => {
5457
5471
  clearTimeout(idleTimer);
5458
5472
  if (isTimeout())
5459
- reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
5473
+ reject(new Error(t(sawToolCallStart ? "error.llm_stream_idle_toolcall" : "error.llm_stream_idle", { timeout: idleTimeoutMs })));
5460
5474
  else
5461
5475
  reject(err);
5462
5476
  });
@@ -5494,11 +5508,14 @@ class OpenAICompatProvider {
5494
5508
  }
5495
5509
  const delta = choice.delta || {};
5496
5510
  const finishReason = choice.finish_reason;
5511
+ if (finishReason)
5512
+ lastFinishReason = finishReason;
5497
5513
  if (delta.reasoning_content) {
5498
5514
  onEmit();
5499
5515
  yield { type: "reasoning", content: delta.reasoning_content };
5500
5516
  }
5501
5517
  if (delta.tool_calls) {
5518
+ sawToolCallStart = true;
5502
5519
  for (const tc of delta.tool_calls) {
5503
5520
  const idx = tc.index ?? 0;
5504
5521
  if (!toolCallAccs.has(idx)) {
@@ -5516,6 +5533,7 @@ class OpenAICompatProvider {
5516
5533
  }
5517
5534
  if (delta.content) {
5518
5535
  onEmit();
5536
+ sawText = true;
5519
5537
  yield { type: "text", content: delta.content };
5520
5538
  }
5521
5539
  if (finishReason === "tool_calls" && toolCallAccs.size > 0) {
@@ -5541,6 +5559,17 @@ class OpenAICompatProvider {
5541
5559
  onEmit();
5542
5560
  yield { type: "done", usage };
5543
5561
  }
5562
+ if (lastFinishReason === "length") {
5563
+ const truncatedToolCall = sawToolCallStart && toolCallAccs.size > 0;
5564
+ if (truncatedToolCall || !sawText) {
5565
+ const err = new Error(t(truncatedToolCall ? "error.llm_truncated_toolcall" : "error.llm_truncated", {
5566
+ tokens: maxTokens
5567
+ }));
5568
+ err.llmTerminal = true;
5569
+ err.recoverableLlm = true;
5570
+ throw err;
5571
+ }
5572
+ }
5544
5573
  } finally {
5545
5574
  cleanup();
5546
5575
  reader.releaseLock();
@@ -7117,7 +7146,12 @@ var init_read_file = __esm(() => {
7117
7146
  remaining: String(total - end),
7118
7147
  next: String(end + 1)
7119
7148
  }) : "";
7120
- const display = truncated ? `${header}${truncated}` : header;
7149
+ const fullRead = offset === 1 && end >= total;
7150
+ const display = fullRead ? "" : t("file.read_range", {
7151
+ from: String(offset),
7152
+ to: String(end),
7153
+ total: String(total)
7154
+ });
7121
7155
  return {
7122
7156
  success: true,
7123
7157
  output: `${header}
@@ -10504,6 +10538,7 @@ class StuckDetector {
10504
10538
  lastBashOutput = "";
10505
10539
  emptyBashRunCount = 0;
10506
10540
  readOnlyStreak = 0;
10541
+ bashRecent = [];
10507
10542
  errorSignatureCounts = new Map;
10508
10543
  searchedErrorSignatures = new Set;
10509
10544
  webSearchThreshold;
@@ -10558,6 +10593,20 @@ class StuckDetector {
10558
10593
  this.lastBashCommand = command;
10559
10594
  this.lastBashOutput = output;
10560
10595
  }
10596
+ recordBashAttempt(success) {
10597
+ this.bashRecent.push(success);
10598
+ if (this.bashRecent.length > BASH_FLAIL_WINDOW)
10599
+ this.bashRecent.shift();
10600
+ }
10601
+ hasBashFlailing() {
10602
+ if (this.bashRecent.length < BASH_FLAIL_THRESHOLD)
10603
+ return false;
10604
+ let failures = 0;
10605
+ for (const ok of this.bashRecent)
10606
+ if (!ok)
10607
+ failures++;
10608
+ return failures >= BASH_FLAIL_THRESHOLD;
10609
+ }
10561
10610
  getLastBashCommand() {
10562
10611
  return this.lastBashCommand;
10563
10612
  }
@@ -10796,6 +10845,8 @@ class StuckDetector {
10796
10845
  const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
10797
10846
  if (errorTool)
10798
10847
  return `tool-errors:${errorTool[0]}`;
10848
+ if (this.hasBashFlailing())
10849
+ return "bash-flailing";
10799
10850
  if (this.hasConsecutiveFailures())
10800
10851
  return "consecutive";
10801
10852
  if (this.hasRepetitiveToolCalls())
@@ -10844,6 +10895,13 @@ class StuckDetector {
10844
10895
  count: this.consecutiveFailures
10845
10896
  });
10846
10897
  }
10898
+ if (reason === "bash-flailing") {
10899
+ const fails = this.bashRecent.filter((ok) => !ok).length;
10900
+ return t("exec.bash_flailing_recovery", {
10901
+ fails: String(fails),
10902
+ window: String(this.bashRecent.length)
10903
+ });
10904
+ }
10847
10905
  if (reason === "repetitive") {
10848
10906
  return this.getRepetitiveToolMessage();
10849
10907
  }
@@ -10860,6 +10918,8 @@ class StuckDetector {
10860
10918
  const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
10861
10919
  if (errorTool)
10862
10920
  return "tool-errors";
10921
+ if (this.hasBashFlailing())
10922
+ return "bash-flailing";
10863
10923
  if (this.hasConsecutiveFailures())
10864
10924
  return "consecutive";
10865
10925
  if (this.hasRepetitiveToolCalls())
@@ -10881,6 +10941,7 @@ class StuckDetector {
10881
10941
  this.lastBashCommand = "";
10882
10942
  this.lastBashOutput = "";
10883
10943
  this.emptyBashRunCount = 0;
10944
+ this.bashRecent = [];
10884
10945
  this.readOnlyStreak = 0;
10885
10946
  this.errorSignatureCounts.clear();
10886
10947
  this.searchedErrorSignatures.clear();
@@ -10897,6 +10958,7 @@ class StuckDetector {
10897
10958
  this.lastBashCommand = "";
10898
10959
  this.lastBashOutput = "";
10899
10960
  this.emptyBashRunCount = 0;
10961
+ this.bashRecent = [];
10900
10962
  this.errorSignatureCounts.clear();
10901
10963
  this.searchedErrorSignatures.clear();
10902
10964
  }
@@ -10912,12 +10974,13 @@ class StuckDetector {
10912
10974
  this.lastBashCommand = "";
10913
10975
  this.lastBashOutput = "";
10914
10976
  this.emptyBashRunCount = 0;
10977
+ this.bashRecent = [];
10915
10978
  this.readOnlyStreak = 0;
10916
10979
  this.errorSignatureCounts.clear();
10917
10980
  this.searchedErrorSignatures.clear();
10918
10981
  }
10919
10982
  }
10920
- var READ_ONLY_TOOLS, READ_ONLY_LOOP_THRESHOLD = 10, ANSI_RE, ERROR_LOC_RE, SIGNATURE_MAX_CHARS = 120, SEARCHABLE_PATTERNS, RUNTIME_WORDS, FRAMEWORK_WORDS;
10983
+ var READ_ONLY_TOOLS, READ_ONLY_LOOP_THRESHOLD = 10, BASH_FLAIL_WINDOW = 6, BASH_FLAIL_THRESHOLD = 4, ANSI_RE, ERROR_LOC_RE, SIGNATURE_MAX_CHARS = 120, SEARCHABLE_PATTERNS, RUNTIME_WORDS, FRAMEWORK_WORDS;
10921
10984
  var init_stuck_detector = __esm(() => {
10922
10985
  init_i18n();
10923
10986
  READ_ONLY_TOOLS = new Set([
@@ -12576,16 +12639,41 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
12576
12639
  }
12577
12640
  contextManager.addMessage({ role: "user", content: input });
12578
12641
  slog.logUser(input);
12579
- if (config.moe?.enabled) {
12580
- return runWithMoE({
12642
+ const startedAt = Date.now();
12643
+ let toolCalls = 0;
12644
+ const countTool = (ev) => {
12645
+ if (ev.type === "end")
12646
+ toolCalls++;
12647
+ onTool?.(ev);
12648
+ };
12649
+ const emitTurnEnd = (result, err) => {
12650
+ const interrupted = err instanceof Error && err.name === "AbortError" || this.shutdownRequested;
12651
+ pluginManager.runOnTurnEnd?.({
12652
+ logger,
12653
+ sessionManager: sessionManager?.getActiveMeta(),
12654
+ contextManager
12655
+ }, {
12656
+ success: !!result && result.success !== false && !err,
12657
+ durationMs: Date.now() - startedAt,
12658
+ textLength: (result?.text || "").length,
12659
+ toolCalls,
12660
+ interrupted: interrupted || undefined
12661
+ });
12662
+ };
12663
+ try {
12664
+ const result = config.moe?.enabled === true ? await runWithMoE({
12581
12665
  config,
12582
12666
  llmProvider,
12583
12667
  toolExecutor,
12584
12668
  logger,
12585
12669
  baseDir: this.deps.baseDir
12586
- }, input, () => this.executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase), { onMeta, onTool, onPhase });
12670
+ }, input, () => this.executeSingleAgentLoop(input, onChunk, onMeta, countTool, onPhase), { onMeta, onTool: countTool, onPhase }) : await this.executeSingleAgentLoop(input, onChunk, onMeta, countTool, onPhase);
12671
+ emitTurnEnd(result);
12672
+ return result;
12673
+ } catch (err) {
12674
+ emitTurnEnd(null, err);
12675
+ throw err;
12587
12676
  }
12588
- return this.executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase);
12589
12677
  }
12590
12678
  async executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase) {
12591
12679
  const {
@@ -12626,6 +12714,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
12626
12714
  let suppressRepetitionRetry = false;
12627
12715
  let repeatedToolCount = 0;
12628
12716
  const MAX_REPEATED_TOOL_CALLS = 2;
12717
+ let llmErrorRetries = 0;
12629
12718
  let allToolsForBudget = toolExecutor.getToolDefinitions(this.deps.toolTags);
12630
12719
  let boundedToolNames = new Set(allToolsForBudget.filter((t2) => t2.boundedOutput).map((t2) => t2.name));
12631
12720
  let toolTokenEstimate = allToolsForBudget.reduce((sum, t2) => sum + Math.ceil((t2.description.length + JSON.stringify(t2.parameters).length) / 4), 0);
@@ -12750,6 +12839,17 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
12750
12839
  logger.info("LLM call aborted (interrupt)");
12751
12840
  break;
12752
12841
  }
12842
+ if (err?.recoverableLlm && llmErrorRetries < MAX_LLM_ERROR_RETRIES) {
12843
+ llmErrorRetries++;
12844
+ logger.warn(`Recoverable LLM error, feeding back (${llmErrorRetries}/${MAX_LLM_ERROR_RETRIES}): ${err.message}`);
12845
+ slog.logError(err.message);
12846
+ pluginManager.runOnError({ iteration, logger, contextManager }, err);
12847
+ contextManager.addMessage({
12848
+ role: "user",
12849
+ content: `<system-summary>${err.message}</system-summary>`
12850
+ });
12851
+ continue;
12852
+ }
12753
12853
  logger.logLLMResponse(config.model, textContent.length, Date.now() - llmStart, err.message, "agent");
12754
12854
  logger.error(`LLM call failed: ${err.message}`);
12755
12855
  slog.logError(err.message);
@@ -13245,7 +13345,7 @@ ${warnLine}
13245
13345
  });
13246
13346
  }
13247
13347
  }
13248
- var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, FORCED_COMPACTION_COOLDOWN = 3;
13348
+ var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, FORCED_COMPACTION_COOLDOWN = 3, MAX_LLM_ERROR_RETRIES = 2;
13249
13349
  var init_agent = __esm(() => {
13250
13350
  init_manager();
13251
13351
  init_i18n();
@@ -14228,6 +14328,18 @@ var init_checker = __esm(() => {
14228
14328
  // src/modules/plugins/manager.ts
14229
14329
  class PluginManager {
14230
14330
  plugins = [];
14331
+ hostBridge = null;
14332
+ setHostBridge(bridge) {
14333
+ this.hostBridge = bridge;
14334
+ }
14335
+ withHost(ctx) {
14336
+ if (!ctx || typeof ctx !== "object")
14337
+ return ctx;
14338
+ if (!this.hostBridge || ctx.hostBridge !== undefined) {
14339
+ return ctx;
14340
+ }
14341
+ return { ...ctx, hostBridge: this.hostBridge };
14342
+ }
14231
14343
  register(plugin, source) {
14232
14344
  if (source)
14233
14345
  plugin.source = source;
@@ -14266,7 +14378,8 @@ class PluginManager {
14266
14378
  }
14267
14379
  });
14268
14380
  }
14269
- async runOnBeforeTool(ctx, call) {
14381
+ async runOnBeforeTool(rawCtx, call) {
14382
+ const ctx = this.withHost(rawCtx);
14270
14383
  for (const plugin of this.plugins) {
14271
14384
  if (plugin.onBeforeTool) {
14272
14385
  try {
@@ -14278,7 +14391,8 @@ class PluginManager {
14278
14391
  }
14279
14392
  return true;
14280
14393
  }
14281
- async runOnAfterTool(ctx, call, result) {
14394
+ async runOnAfterTool(rawCtx, call, result) {
14395
+ const ctx = this.withHost(rawCtx);
14282
14396
  for (const plugin of this.plugins) {
14283
14397
  if (plugin.onAfterTool) {
14284
14398
  try {
@@ -14287,7 +14401,8 @@ class PluginManager {
14287
14401
  }
14288
14402
  }
14289
14403
  }
14290
- async runOnError(ctx, error) {
14404
+ async runOnError(rawCtx, error) {
14405
+ const ctx = this.withHost(rawCtx);
14291
14406
  for (const plugin of this.plugins) {
14292
14407
  if (plugin.onError) {
14293
14408
  try {
@@ -14296,7 +14411,8 @@ class PluginManager {
14296
14411
  }
14297
14412
  }
14298
14413
  }
14299
- runOnSessionStart(ctx) {
14414
+ runOnSessionStart(rawCtx) {
14415
+ const ctx = this.withHost(rawCtx);
14300
14416
  for (const plugin of this.plugins) {
14301
14417
  if (plugin.onSessionStart) {
14302
14418
  try {
@@ -14305,7 +14421,8 @@ class PluginManager {
14305
14421
  }
14306
14422
  }
14307
14423
  }
14308
- runOnBeforeThink(ctx) {
14424
+ runOnBeforeThink(rawCtx) {
14425
+ const ctx = this.withHost(rawCtx);
14309
14426
  for (const plugin of this.plugins) {
14310
14427
  if (plugin.onBeforeThink) {
14311
14428
  try {
@@ -14314,7 +14431,8 @@ class PluginManager {
14314
14431
  }
14315
14432
  }
14316
14433
  }
14317
- runOnAfterThink(ctx, response) {
14434
+ runOnAfterThink(rawCtx, response) {
14435
+ const ctx = this.withHost(rawCtx);
14318
14436
  for (const plugin of this.plugins) {
14319
14437
  if (plugin.onAfterThink) {
14320
14438
  try {
@@ -14323,7 +14441,8 @@ class PluginManager {
14323
14441
  }
14324
14442
  }
14325
14443
  }
14326
- runOnSessionEnd(ctx) {
14444
+ runOnSessionEnd(rawCtx) {
14445
+ const ctx = this.withHost(rawCtx);
14327
14446
  for (const plugin of this.plugins) {
14328
14447
  if (plugin.onSessionEnd) {
14329
14448
  try {
@@ -14332,7 +14451,8 @@ class PluginManager {
14332
14451
  }
14333
14452
  }
14334
14453
  }
14335
- runOnToolCall(ctx) {
14454
+ runOnToolCall(rawCtx) {
14455
+ const ctx = this.withHost(rawCtx);
14336
14456
  for (const plugin of this.plugins) {
14337
14457
  if (plugin.onToolCall) {
14338
14458
  try {
@@ -14341,7 +14461,8 @@ class PluginManager {
14341
14461
  }
14342
14462
  }
14343
14463
  }
14344
- runOnPhase(ctx, phase) {
14464
+ runOnPhase(rawCtx, phase) {
14465
+ const ctx = this.withHost(rawCtx);
14345
14466
  for (const plugin of this.plugins) {
14346
14467
  if (plugin.onPhase) {
14347
14468
  try {
@@ -14350,7 +14471,8 @@ class PluginManager {
14350
14471
  }
14351
14472
  }
14352
14473
  }
14353
- runOnToolStart(ctx, call) {
14474
+ runOnToolStart(rawCtx, call) {
14475
+ const ctx = this.withHost(rawCtx);
14354
14476
  for (const plugin of this.plugins) {
14355
14477
  if (plugin.onToolStart) {
14356
14478
  try {
@@ -14359,7 +14481,8 @@ class PluginManager {
14359
14481
  }
14360
14482
  }
14361
14483
  }
14362
- runOnToolEnd(ctx, call, result, durationMs) {
14484
+ runOnToolEnd(rawCtx, call, result, durationMs) {
14485
+ const ctx = this.withHost(rawCtx);
14363
14486
  for (const plugin of this.plugins) {
14364
14487
  if (plugin.onToolEnd) {
14365
14488
  try {
@@ -14368,7 +14491,8 @@ class PluginManager {
14368
14491
  }
14369
14492
  }
14370
14493
  }
14371
- runOnText(ctx, chunk) {
14494
+ runOnText(rawCtx, chunk) {
14495
+ const ctx = this.withHost(rawCtx);
14372
14496
  for (const plugin of this.plugins) {
14373
14497
  if (plugin.onText) {
14374
14498
  try {
@@ -14380,7 +14504,8 @@ class PluginManager {
14380
14504
  }
14381
14505
  return chunk;
14382
14506
  }
14383
- runOnMeta(ctx, chunk) {
14507
+ runOnMeta(rawCtx, chunk) {
14508
+ const ctx = this.withHost(rawCtx);
14384
14509
  for (const plugin of this.plugins) {
14385
14510
  if (plugin.onMeta) {
14386
14511
  try {
@@ -14392,6 +14517,16 @@ class PluginManager {
14392
14517
  }
14393
14518
  return chunk;
14394
14519
  }
14520
+ runOnTurnEnd(rawCtx, summary) {
14521
+ const ctx = this.withHost(rawCtx);
14522
+ for (const plugin of this.plugins) {
14523
+ if (plugin.onTurnEnd) {
14524
+ try {
14525
+ plugin.onTurnEnd(ctx, summary);
14526
+ } catch {}
14527
+ }
14528
+ }
14529
+ }
14395
14530
  }
14396
14531
  var init_manager3 = __esm(() => {
14397
14532
  init_checker();
@@ -18072,7 +18207,7 @@ function lintCacheKey(baseDir, lintScript) {
18072
18207
  return `${baseDir}::${lintScript}`;
18073
18208
  }
18074
18209
  function isMissingBinaryError(err, combinedOutput) {
18075
- if (err.status === 127)
18210
+ if (err.status === 127 || err.status === 9009)
18076
18211
  return true;
18077
18212
  if (err.code === "ENOENT")
18078
18213
  return true;
@@ -18086,24 +18221,43 @@ function contentHash(content) {
18086
18221
  return String(h);
18087
18222
  }
18088
18223
  function getWinDecoder() {
18089
- if (_winDecoder === undefined) {
18090
- if (platform5() !== "win32") {
18091
- _winDecoder = null;
18092
- } else {
18093
- try {
18094
- const cpOut = execSync("chcp.com", {
18095
- encoding: "buffer",
18096
- timeout: 2000,
18097
- windowsHide: true
18098
- }).toString("latin1");
18099
- const m = cpOut.match(/(\d+)/);
18100
- _winDecoder = m ? new TextDecoder("ibm" + m[1]) : null;
18101
- } catch {
18102
- _winDecoder = null;
18103
- }
18224
+ if (_winDecoder !== undefined)
18225
+ return _winDecoder;
18226
+ if (platform5() !== "win32") {
18227
+ _winDecoder = new TextDecoder("utf-8");
18228
+ return _winDecoder;
18229
+ }
18230
+ let decoder = null;
18231
+ try {
18232
+ const cpOut = execSync("chcp.com", {
18233
+ encoding: "buffer",
18234
+ timeout: 2000,
18235
+ windowsHide: true
18236
+ }).toString("latin1");
18237
+ const m = cpOut.match(/(\d+)/);
18238
+ if (m)
18239
+ decoder = new TextDecoder("ibm" + m[1]);
18240
+ } catch {}
18241
+ if (!decoder) {
18242
+ try {
18243
+ decoder = new TextDecoder("ibm866");
18244
+ } catch {
18245
+ return new TextDecoder("utf-8");
18104
18246
  }
18105
18247
  }
18106
- return _winDecoder ?? new TextDecoder("utf-8");
18248
+ _winDecoder = decoder;
18249
+ return _winDecoder;
18250
+ }
18251
+ function decodeOutput(chunks) {
18252
+ const raw = Buffer.concat(chunks);
18253
+ try {
18254
+ return new TextDecoder("utf-8", { fatal: true }).decode(raw);
18255
+ } catch {}
18256
+ try {
18257
+ return getWinDecoder().decode(raw);
18258
+ } catch {
18259
+ return raw.toString("latin1");
18260
+ }
18107
18261
  }
18108
18262
 
18109
18263
  class LintOnWritePlugin {
@@ -18275,14 +18429,13 @@ function runAsync(command, cwd, timeoutMs, signal) {
18275
18429
  windowsHide: true,
18276
18430
  stdio: ["ignore", "pipe", "pipe"]
18277
18431
  });
18278
- let stdout = "";
18279
- let stderr = "";
18280
- const decoder = getWinDecoder();
18432
+ const utf8Chunks = [];
18433
+ const oemChunks = [];
18281
18434
  child.stdout?.on("data", (d) => {
18282
- stdout += decoder.decode(d, { stream: true });
18435
+ utf8Chunks.push(Buffer.from(d));
18283
18436
  });
18284
18437
  child.stderr?.on("data", (d) => {
18285
- stderr += decoder.decode(d, { stream: true });
18438
+ oemChunks.push(Buffer.from(d));
18286
18439
  });
18287
18440
  const timer = setTimeout(() => {
18288
18441
  child.kill();
@@ -18298,7 +18451,7 @@ function runAsync(command, cwd, timeoutMs, signal) {
18298
18451
  clearTimeout(timer);
18299
18452
  signal?.removeEventListener("abort", onAbort);
18300
18453
  if (signal?.aborted) {
18301
- resolve19({ stdout, stderr });
18454
+ resolve19({ stdout: decodeOutput(utf8Chunks), stderr: decodeOutput(oemChunks) });
18302
18455
  } else {
18303
18456
  reject(err);
18304
18457
  }
@@ -18306,8 +18459,8 @@ function runAsync(command, cwd, timeoutMs, signal) {
18306
18459
  child.on("close", (code) => {
18307
18460
  clearTimeout(timer);
18308
18461
  signal?.removeEventListener("abort", onAbort);
18309
- stdout += decoder.decode();
18310
- stderr += decoder.decode();
18462
+ const stdout = decodeOutput(utf8Chunks);
18463
+ const stderr = decodeOutput(oemChunks);
18311
18464
  if (signal?.aborted || code === 0) {
18312
18465
  resolve19({ stdout, stderr });
18313
18466
  } else {
@@ -18991,6 +19144,9 @@ Last compile error: ${first[1]}`;
18991
19144
  }
18992
19145
  }
18993
19146
  if (!result.success) {
19147
+ if (call.name === "bash") {
19148
+ deps.stuckDetector.recordBashAttempt(false);
19149
+ }
18994
19150
  if (call.name === "bash" && platform6() === "win32") {
18995
19151
  const cmd = String(call.arguments?.command ?? "");
18996
19152
  const forbidden = forbiddenWindowsCommand(cmd);
@@ -19010,6 +19166,9 @@ Last compile error: ${first[1]}`;
19010
19166
  }
19011
19167
  } else {
19012
19168
  deps.stuckDetector.recordToolSuccess();
19169
+ if (call.name === "bash") {
19170
+ deps.stuckDetector.recordBashAttempt(true);
19171
+ }
19013
19172
  if (call.name === "bash" && result.success) {
19014
19173
  const cmd = String(call.arguments?.command ?? "");
19015
19174
  const testRun = detectTestResults(String(result.output ?? ""));
@@ -33371,8 +33530,9 @@ function stripAnsi2(s) {
33371
33530
  function charLen(s) {
33372
33531
  return Array.from(s).length;
33373
33532
  }
33374
- function wrapLine(line, width) {
33533
+ function wrapLine(line, width, firstRowWidth) {
33375
33534
  const w = width > 0 ? width : 80;
33535
+ let limit = firstRowWidth !== undefined && firstRowWidth > 0 ? firstRowWidth : w;
33376
33536
  const chars = Array.from(line);
33377
33537
  const rows = [];
33378
33538
  let start = 0;
@@ -33387,10 +33547,11 @@ function wrapLine(line, width) {
33387
33547
  continue;
33388
33548
  }
33389
33549
  const cw = stringWidth(ch);
33390
- if (cells + cw > w && cells > 0) {
33550
+ if (cells + cw > limit && cells > 0) {
33391
33551
  rows.push({ text: chars.slice(start, i).join(""), charStart: start });
33392
33552
  start = i;
33393
33553
  cells = cw;
33554
+ limit = w;
33394
33555
  } else {
33395
33556
  cells += cw;
33396
33557
  }
@@ -33398,10 +33559,10 @@ function wrapLine(line, width) {
33398
33559
  rows.push({ text: chars.slice(start).join(""), charStart: start });
33399
33560
  return rows;
33400
33561
  }
33401
- function buildLayout(lines, width) {
33562
+ function buildLayout(lines, width, firstRowWidth) {
33402
33563
  const rows = [];
33403
33564
  for (let b = 0;b < lines.length; b++) {
33404
- for (const r of wrapLine(lines[b], width)) {
33565
+ for (const r of wrapLine(lines[b], width, b === 0 ? firstRowWidth : undefined)) {
33405
33566
  rows.push({ bufRow: b, charStart: r.charStart, text: r.text });
33406
33567
  }
33407
33568
  }
@@ -33438,6 +33599,7 @@ class LineEditor {
33438
33599
  input;
33439
33600
  output;
33440
33601
  onKeyInput;
33602
+ onInterrupt;
33441
33603
  promptStr;
33442
33604
  completer;
33443
33605
  history;
@@ -33456,11 +33618,15 @@ class LineEditor {
33456
33618
  rawMode = false;
33457
33619
  listeners = {};
33458
33620
  prevCursorRow = 0;
33621
+ frameTopAbs = null;
33622
+ dsrPending = false;
33623
+ dsrTail = "";
33459
33624
  constructor(opts) {
33460
33625
  this.input = opts.input;
33461
33626
  this.output = opts.output;
33462
33627
  this.promptStr = opts.prompt ?? "";
33463
33628
  this.completer = opts.completer;
33629
+ this.onInterrupt = opts.onInterrupt;
33464
33630
  this.history = opts.history ?? [];
33465
33631
  this.historySize = opts.historySize ?? 50;
33466
33632
  if (this.history.length > this.historySize) {
@@ -33471,6 +33637,7 @@ class LineEditor {
33471
33637
  if (this.rawMode)
33472
33638
  this.input.setRawMode(true);
33473
33639
  this.enableTerminalProtocols();
33640
+ this.input.on("data", (buf) => this.consumeDsrReplies(buf.toString("utf-8")));
33474
33641
  readline2.emitKeypressEvents(this.input);
33475
33642
  this.input.on("keypress", (str, key) => this.onKey(str, key));
33476
33643
  } else {
@@ -33513,7 +33680,7 @@ class LineEditor {
33513
33680
  this.col = 0;
33514
33681
  this.historyIndex = -1;
33515
33682
  this.stash = null;
33516
- this.prevCursorRow = 0;
33683
+ this.invalidateAnchor();
33517
33684
  if (this.input.isTTY)
33518
33685
  this.render();
33519
33686
  }
@@ -33523,7 +33690,7 @@ class LineEditor {
33523
33690
  this.lines = [""];
33524
33691
  this.row = 0;
33525
33692
  this.col = 0;
33526
- this.prevCursorRow = 0;
33693
+ this.invalidateAnchor();
33527
33694
  if (this.input.isTTY)
33528
33695
  this.render();
33529
33696
  }
@@ -33743,10 +33910,10 @@ class LineEditor {
33743
33910
  this.commitFrame();
33744
33911
  this.output.write(`\r
33745
33912
  `);
33913
+ this.invalidateAnchor();
33746
33914
  this.lines = [""];
33747
33915
  this.row = 0;
33748
33916
  this.col = 0;
33749
- this.prevCursorRow = 0;
33750
33917
  cb(answer);
33751
33918
  return;
33752
33919
  }
@@ -33763,17 +33930,21 @@ class LineEditor {
33763
33930
  this.commitFrame();
33764
33931
  this.output.write(`\r
33765
33932
  `);
33933
+ this.invalidateAnchor();
33766
33934
  this.lines = [""];
33767
33935
  this.row = 0;
33768
33936
  this.col = 0;
33769
- this.prevCursorRow = 0;
33770
33937
  this.emitLine(text);
33771
33938
  }
33772
33939
  commitFrame() {
33773
- const layout = this.layout();
33940
+ const layout = this.visibleLayout();
33941
+ const lastLen = charLen(stripAnsi2(layout[layout.length - 1].text));
33942
+ if (this.frameTopAbs !== null) {
33943
+ this.output.write(`\x1B[${this.frameTopAbs + layout.length - 1};${lastLen + 1}H`);
33944
+ return;
33945
+ }
33774
33946
  const idx = layoutIndexFor(layout, this.row, this.col);
33775
33947
  const rowsDown = layout.length - 1 - idx;
33776
- const lastLen = charLen(stripAnsi2(layout[layout.length - 1].text));
33777
33948
  const out = [];
33778
33949
  if (rowsDown > 0)
33779
33950
  out.push(`\x1B[${rowsDown}B`);
@@ -33786,9 +33957,15 @@ class LineEditor {
33786
33957
  this.lines = [""];
33787
33958
  this.row = 0;
33788
33959
  this.col = 0;
33789
- this.prevCursorRow = 0;
33960
+ this.invalidateAnchor();
33790
33961
  this.render();
33791
- process.emit("SIGINT");
33962
+ if (this.onInterrupt) {
33963
+ this.onInterrupt();
33964
+ return;
33965
+ }
33966
+ if (process.listenerCount("SIGINT") > 0) {
33967
+ process.emit("SIGINT");
33968
+ }
33792
33969
  }
33793
33970
  ctrlD() {
33794
33971
  if (this.lines.length === 1 && this.lines[0] === "") {
@@ -34055,28 +34232,68 @@ class LineEditor {
34055
34232
  }
34056
34233
  clearScreen() {
34057
34234
  this.output.write("\x1B[2J\x1B[H");
34058
- this.prevCursorRow = 0;
34235
+ this.invalidateAnchor();
34059
34236
  this.render();
34060
34237
  }
34061
34238
  columns() {
34062
34239
  return this.output.columns || 80;
34063
34240
  }
34064
34241
  layout() {
34065
- return buildLayout(this.lines, this.columns());
34242
+ const promptWidth = stringWidth(stripAnsi2(this.promptStr));
34243
+ return buildLayout(this.lines, this.columns(), Math.max(1, this.columns() - promptWidth));
34244
+ }
34245
+ screenRows() {
34246
+ const r = this.output.rows;
34247
+ return typeof r === "number" && r > 3 ? r : null;
34248
+ }
34249
+ visibleLayout() {
34250
+ const layout = this.layout();
34251
+ const H = this.screenRows();
34252
+ const maxRows = H ? H - 2 : 0;
34253
+ if (maxRows > 0 && layout.length > maxRows) {
34254
+ return layout.slice(layout.length - maxRows);
34255
+ }
34256
+ return layout;
34257
+ }
34258
+ invalidateAnchor() {
34259
+ this.frameTopAbs = null;
34260
+ this.dsrPending = false;
34261
+ this.dsrTail = "";
34262
+ this.prevCursorRow = 0;
34263
+ }
34264
+ consumeDsrReplies(chunk) {
34265
+ this.dsrTail = (this.dsrTail + chunk).slice(-80);
34266
+ const m = /\x1b\[(\d+);(\d+)R/.exec(this.dsrTail);
34267
+ if (!m)
34268
+ return;
34269
+ this.dsrTail = "";
34270
+ if (!this.dsrPending)
34271
+ return;
34272
+ this.dsrPending = false;
34273
+ const row = Number(m[1]);
34274
+ const idx = Number(m[2]);
34275
+ const top = row - this.prevCursorRow;
34276
+ if (top >= 1)
34277
+ this.frameTopAbs = top;
34066
34278
  }
34067
34279
  render() {
34068
34280
  if (!this.input.isTTY || this.isDone)
34069
34281
  return;
34070
- const layout = this.layout();
34282
+ const layout = this.visibleLayout();
34071
34283
  const promptWidth = stringWidth(stripAnsi2(this.promptStr));
34072
34284
  const idx = layoutIndexFor(layout, this.row, this.col);
34073
34285
  const vcol = visualColAt(layout, idx, this.col);
34074
34286
  const rowsAfter = layout.length - idx - 1;
34075
34287
  const ccol = (idx === 0 ? promptWidth : 0) + vcol;
34076
34288
  const out = [];
34077
- if (this.prevCursorRow > 0)
34289
+ if (this.frameTopAbs !== null) {
34290
+ out.push(`\x1B[${this.frameTopAbs};1H`);
34291
+ } else if (this.prevCursorRow > 0) {
34078
34292
  out.push(`\x1B[${this.prevCursorRow}A`);
34079
- out.push("\r");
34293
+ out.push("\r");
34294
+ } else {
34295
+ out.push("\r");
34296
+ }
34080
34297
  for (let i = 0;i < layout.length; i++) {
34081
34298
  out.push("\x1B[2K");
34082
34299
  if (i === 0)
@@ -34092,8 +34309,17 @@ class LineEditor {
34092
34309
  out.push("\r");
34093
34310
  if (ccol > 0)
34094
34311
  out.push(`\x1B[${ccol}C`);
34095
- this.prevCursorRow = idx;
34096
34312
  this.output.write(out.join(""));
34313
+ if (this.frameTopAbs !== null) {
34314
+ const H = this.screenRows();
34315
+ if (H) {
34316
+ const T = this.frameTopAbs;
34317
+ const L = layout.length;
34318
+ if (T + L - 1 > H)
34319
+ this.frameTopAbs = H - L + 1;
34320
+ }
34321
+ } else if (!this.dsrPending) {}
34322
+ this.prevCursorRow = idx;
34097
34323
  }
34098
34324
  enableTerminalProtocols() {
34099
34325
  this.output.write("\x1B[?2004h");
@@ -34450,9 +34676,28 @@ init_box();
34450
34676
  init_table();
34451
34677
  init_i18n();
34452
34678
  init_prices();
34679
+ import { isAbsolute as isAbsolute4, relative as relative6, sep as sep2 } from "path";
34453
34680
  function formatUsd(cost) {
34454
34681
  return formatCost(cost);
34455
34682
  }
34683
+ var PATH_TOOLS = new Set([
34684
+ "read_file",
34685
+ "write_file",
34686
+ "edit_file",
34687
+ "delete_file",
34688
+ "create_dir",
34689
+ "move_file",
34690
+ "list_dir",
34691
+ "file_info"
34692
+ ]);
34693
+ function toDisplayPath(baseDir, p) {
34694
+ if (!baseDir || !isAbsolute4(p))
34695
+ return p;
34696
+ const rel = relative6(baseDir, p);
34697
+ if (!rel || rel.startsWith("..") || isAbsolute4(rel))
34698
+ return p;
34699
+ return rel.split(sep2).join("/");
34700
+ }
34456
34701
  var GUTTER = " ";
34457
34702
  var BUSY_TOOLS = new Set(["lsp_check"]);
34458
34703
  function toolMarker(tool) {
@@ -34506,6 +34751,7 @@ class Renderer {
34506
34751
  err;
34507
34752
  width;
34508
34753
  toolStyle;
34754
+ baseDir;
34509
34755
  card = null;
34510
34756
  constructor(opts = {}) {
34511
34757
  this.rich = opts.rich ?? isRichTerminal();
@@ -34513,6 +34759,7 @@ class Renderer {
34513
34759
  this.err = opts.err ?? process.stderr;
34514
34760
  this.width = opts.width ?? getTerminalWidth();
34515
34761
  this.toolStyle = opts.toolStyle ?? "inline";
34762
+ this.baseDir = opts.baseDir;
34516
34763
  this.spinner = new Spinner({
34517
34764
  enabled: this.rich && (opts.spinner ?? true),
34518
34765
  stream: this.err,
@@ -34560,7 +34807,8 @@ class Renderer {
34560
34807
  toolStart(tool, args, stepContext, icon) {
34561
34808
  this.endCard();
34562
34809
  this.spinner.stop();
34563
- const summary = summarizeArgs2(args);
34810
+ const displayArgs = PATH_TOOLS.has(tool) && typeof args.path === "string" ? { ...args, path: toDisplayPath(this.baseDir, args.path) } : args;
34811
+ const summary = summarizeArgs2(displayArgs);
34564
34812
  const step = stepContext ? ` ${pc2.cyan(`← ${stepContext}`)}` : "";
34565
34813
  const marker = icon || toolMarker(tool);
34566
34814
  if (!this.rich) {
@@ -34862,6 +35110,22 @@ function formatContextBar(used, limit, compactions, quality) {
34862
35110
  }
34863
35111
  return line;
34864
35112
  }
35113
+ function createHostBridge(opts) {
35114
+ return {
35115
+ isBusy: opts.isBusy,
35116
+ async submit(text, o) {
35117
+ if (opts.isBusy())
35118
+ throw new Error("agent is busy");
35119
+ for (const url of o?.images ?? [])
35120
+ opts.addPendingImage(url);
35121
+ return opts.runAgent(text);
35122
+ },
35123
+ interrupt() {
35124
+ if (opts.isBusy())
35125
+ opts.interruptRun();
35126
+ }
35127
+ };
35128
+ }
34865
35129
 
34866
35130
  class Repl {
34867
35131
  completer = new Completer;
@@ -34934,6 +35198,19 @@ class Repl {
34934
35198
  registerAllCommands(this);
34935
35199
  this.setupCompleter();
34936
35200
  this.setupListeners();
35201
+ if (pluginManager) {
35202
+ pluginManager.setHostBridge(createHostBridge({
35203
+ isBusy: () => this.agentRunning,
35204
+ addPendingImage: (url) => {
35205
+ this.agent.contextManager?.addPendingImage?.({
35206
+ type: "image_url",
35207
+ image_url: { url }
35208
+ });
35209
+ },
35210
+ runAgent: (text) => this.runAgent(text),
35211
+ interruptRun: () => this.agent.shutdown()
35212
+ }));
35213
+ }
34937
35214
  }
34938
35215
  loadHistory() {
34939
35216
  if (existsSync52(this.historyPath)) {
@@ -35071,7 +35348,10 @@ class Repl {
35071
35348
  this.rl.onKeyInput = (str, key) => this.handleSpecialKey(str, key);
35072
35349
  }
35073
35350
  let forceExitTimer = null;
35074
- process.on("SIGINT", () => {
35351
+ if (typeof Repl.sigintHandler === "function") {
35352
+ process.removeListener("SIGINT", Repl.sigintHandler);
35353
+ }
35354
+ Repl.sigintHandler = () => {
35075
35355
  if (this.agentRunning) {
35076
35356
  console.log(pc2.yellow(t("repl.ctrl_c_interrupt")));
35077
35357
  this.agent.shutdown();
@@ -35082,7 +35362,8 @@ class Repl {
35082
35362
  } else {
35083
35363
  process.exit(0);
35084
35364
  }
35085
- });
35365
+ };
35366
+ process.on("SIGINT", Repl.sigintHandler);
35086
35367
  }
35087
35368
  handleSpecialKey(str, key) {
35088
35369
  if (key.name === "escape") {
@@ -35160,7 +35441,8 @@ ${t("image.clipboard_empty")}`));
35160
35441
  ` + pc2.green(t("repl.agent")));
35161
35442
  const renderer = new Renderer({
35162
35443
  spinner: this.config.ui?.spinner ?? true,
35163
- toolStyle: this.config.ui?.toolStyle ?? "inline"
35444
+ toolStyle: this.config.ui?.toolStyle ?? "inline",
35445
+ baseDir: this.baseDir
35164
35446
  });
35165
35447
  const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
35166
35448
  if (ev.type === "start") {
@@ -35642,7 +35924,7 @@ async function main() {
35642
35924
  }
35643
35925
  if (program2.args.length > 0) {
35644
35926
  const prompt = program2.args.join(" ");
35645
- const { agent, config } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
35927
+ const { agent, config, baseDir } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
35646
35928
  const updater = exitOnComplete ? undefined : startAutoUpdate(config);
35647
35929
  if (jsonMode) {
35648
35930
  const result2 = await agent.run(prompt);
@@ -35667,7 +35949,8 @@ async function main() {
35667
35949
  }
35668
35950
  const renderer = new Renderer({
35669
35951
  spinner: config.ui?.spinner ?? true,
35670
- toolStyle: config.ui?.toolStyle ?? "inline"
35952
+ toolStyle: config.ui?.toolStyle ?? "inline",
35953
+ baseDir
35671
35954
  });
35672
35955
  const result = await agent.run(prompt, (chunk) => renderer.text(chunk), (meta) => renderer.meta(meta), (ev) => {
35673
35956
  if (ev.type === "start") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.50.1",
3
+ "version": "0.51.1",
4
4
  "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
5
  "type": "module",
6
6
  "bin": {