micro-models-agent 0.60.0 → 0.61.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/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ All notable changes to Micro Models Agent (MMA) will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.61.0] - 2026-09-15
8
+
9
+ ### Added
10
+ - **Budget share controls**: `mma context --system <f>` / `--reserve <f>` and REPL `/context system <f>` / `reserve <f>` set `contextBudget.systemPrompt` / `responseReserve` (validated 0.05–0.9, system + reserve < 0.95). `mma context` and `/context` with no argument now print the budget breakdown (system / reserve / history in tokens and %). `mma config set contextBudget.systemPrompt <f>` keeps working.
11
+
12
+ ### Changed
13
+ - **Overflow fix advice**: the "system prompt exceeds budget" warning no longer only snaps to a coarse standard context size — a 32K project was jumping straight to `131072`, which looked arbitrary and was often unusable on a local model. The hint (`prompt.overflow.hint_*`, en+ru) now states the raw requirement (`needs ~N system tokens; current budget: W × f = B`) and both levers: raise `contextWindow` to the exact minimum (with the standard size) **or** keep the window and raise `contextBudget.systemPrompt` to the required share (`mma context --system <f>`).
14
+
15
+ ### Fixed
16
+ - **`mma context` ignored `MMA_CONFIG_DIR`**: it saved to a hardcoded `~/.mma/config.json`; it now writes to the config dir resolved by bootstrap.
17
+
18
+ ## [0.60.1] - 2026-09-15
19
+
20
+ ### Fixed
21
+ - **Silent prompt-overflow summarization**: the startup dry-run warns that AGENTS.md / the project map "will be summarized before the first run", but the actual summarization ran with no notice — the first prompt appeared to hang while a local model rewrote a large AGENTS.md. `resolvePromptOverflow` now emits (en+ru, `prompt.overflow.*`): a "compressing N block(s)…" line before it starts, a per-block "summarizing \"label\" (N → M tok)…" line before the model call, a "truncating …" line on the fallback, and a failure line before falling back to truncation.
22
+ - **Hardcoded English startup logs**: model auto-load success/failure, reasoning probe result, project indexing start/finish/failure, unreadable index entries and background-processes-killed-on-shutdown were untranslated; they now use `env.*` keys (en+ru). Project indexing now announces start and completion (`Indexed N file(s)`), so a slow first walk no longer pauses startup silently.
23
+
7
24
  ## [0.60.0] - 2026-09-15
8
25
 
9
26
  ### Added
package/dist/i18n/en.json CHANGED
@@ -52,6 +52,16 @@
52
52
  "env.playwright_missing": "Playwright package is not installed — the browser tool will fail. Install with: bun add playwright",
53
53
  "env.playwright_browsers_missing": "Playwright browsers not downloaded ({dir}). The browser tool will fail. Install with: bunx playwright install chromium",
54
54
  "env.crash_stderr": "MMA crashed ({type}): {message} — crash report written to ~/.mma/logs/crash.jsonl",
55
+ "env.model_autoload_failed": "Model auto-load failed: {error}. Continuing with manual load.",
56
+ "env.model_loaded": "Model {model} loaded in {seconds}s",
57
+ "env.reasoning_probe": "Reasoning probe: strategy={strategy} → {result}",
58
+ "env.reasoning_respected": "mechanism respected",
59
+ "env.reasoning_ignored": "mechanism IGNORED",
60
+ "env.indexing": "Indexing project files…",
61
+ "env.indexed": "Indexed {files} file(s) for the project map",
62
+ "env.indexing_failed": "Project indexing failed: {error}",
63
+ "env.index_skip_unreadable": "indexer: skipping unreadable entry \"{path}\"",
64
+ "env.killed_processes": "Killed {count} background process(es) on shutdown",
55
65
  "session.started": "Session started: {id}",
56
66
  "session.ended": "Session ended: {id}",
57
67
  "session.not_found": "Session not found: {id}",
@@ -269,6 +279,14 @@
269
279
  "cli.manage_context": "Manage context window",
270
280
  "cli.invalid_context_size": "Invalid context size. Must be a number >= 1024",
271
281
  "cli.context_set": "Context window set to: {size} tokens",
282
+ "cli.context_budget_header": "Context budget (window {window} tokens):",
283
+ "cli.context_system_line": " system prompt: {tokens} tokens ({percent}%)",
284
+ "cli.context_reserve_line": " response reserve: {tokens} tokens ({percent}%)",
285
+ "cli.context_history_line": " history: {tokens} tokens ({percent}%)",
286
+ "cli.context_system_fraction": "System-prompt share of the context window (0.05–0.9)",
287
+ "cli.context_reserve_fraction": "Response-reserve share of the context window (0.05–0.9)",
288
+ "cli.context_fraction_set": "{key} share set to {value}",
289
+ "cli.context_invalid_fraction": "Invalid share. Use a number between 0.05 and 0.9 (system + reserve must stay below 0.95).",
272
290
  "repl.reload": "Reload agent with current config",
273
291
  "repl.reload_usage": "Usage: /reload",
274
292
  "repl.reloading": "Reloading agent...",
@@ -432,7 +450,7 @@
432
450
  "repl.sysprompt_desc": "Show system prompt",
433
451
  "repl.sysprompt_tokens": "System prompt ({count} tokens):",
434
452
  "repl.excluded_blocks": "\nExcluded blocks: {count}",
435
- "repl.context_usage": "Usage: /context <size> (min 1024)",
453
+ "repl.context_usage": "Usage: /context [<size> | system <fraction> | reserve <fraction>] (min 1024; fractions 0.05–0.9)",
436
454
  "repl.msgs": "msgs",
437
455
  "repl.max_iters": "Max iters:",
438
456
  "repl.stuck_thresh": "Stuck thresh:",
@@ -715,6 +733,13 @@
715
733
  "moe.partial_result": "Partial result",
716
734
  "moe.scope_approved": "🔓 Scope expanded for \"{subtask}\": {files}",
717
735
  "moe.scope_rejected": "⛔ Scope request rejected for \"{subtask}\"",
736
+ "prompt.overflow.compressing": "System prompt overflow: compressing {count} block(s) before the first run — this may take a while on a local model.",
737
+ "prompt.overflow.summarizing": "System prompt overflow: summarizing \"{label}\" ({original} tok → {budget} tok budget) with the model…",
738
+ "prompt.overflow.truncating": "System prompt overflow: truncating \"{label}\" ({original} tok) — summarization unavailable.",
739
+ "prompt.overflow.summarize_failed": "System prompt overflow: summarization failed for \"{label}\": {error} — falling back to truncation.",
740
+ "prompt.overflow.hint_needed": "the prompt needs ~{needed} system tokens (current budget: {window} × {fraction} = {budget})",
741
+ "prompt.overflow.hint_window": "raise contextWindow to at least {required} (standard size {recommended}) — {how}",
742
+ "prompt.overflow.hint_fraction": "or keep the window and raise contextBudget.systemPrompt to ~{fraction} (run: mma context --system {fraction})",
718
743
  "prompt.overflow.exceeded": "Prompt overflow: \"{label}\" ({original} tok) exceeded the system-prompt budget ({budget} tok) — {mode} to {resolved} tok. To fit fully, {hint}.",
719
744
  "prompt.overflow.failed": "Prompt overflow resolution failed: {error} — oversized blocks will be dropped.",
720
745
  "prompt.overflow.startup": "Startup check: {block} ({original} tok) exceeds the system-prompt budget ({budget} tok) — it will be summarized/truncated before the first run. To include it fully, {hint}.",
package/dist/i18n/ru.json CHANGED
@@ -51,6 +51,16 @@
51
51
  "env.playwright_missing": "Пакет Playwright не установлен — браузерный инструмент не будет работать. Установите: bun add playwright",
52
52
  "env.playwright_browsers_missing": "Браузеры Playwright не скачаны ({dir}). Браузерный инструмент не будет работать. Установите: bunx playwright install chromium",
53
53
  "env.crash_stderr": "MMA упал ({type}): {message} — отчёт о падении записан в ~/.mma/logs/crash.jsonl",
54
+ "env.model_autoload_failed": "Не удалось автоматически загрузить модель: {error}. Продолжаю с ручной загрузкой.",
55
+ "env.model_loaded": "Модель {model} загружена за {seconds}с",
56
+ "env.reasoning_probe": "Проверка reasoning: стратегия={strategy} → {result}",
57
+ "env.reasoning_respected": "механизм соблюдается",
58
+ "env.reasoning_ignored": "механизм ИГНОРИРУЕТСЯ",
59
+ "env.indexing": "Индексация файлов проекта…",
60
+ "env.indexed": "Проиндексировано файлов: {files} (карта проекта)",
61
+ "env.indexing_failed": "Ошибка индексации проекта: {error}",
62
+ "env.index_skip_unreadable": "индексатор: пропускаю недоступный файл \"{path}\"",
63
+ "env.killed_processes": "Остановлено фоновых процессов при завершении: {count}",
54
64
  "session.started": "Сессия начата: {id}",
55
65
  "session.ended": "Сессия завершена: {id}",
56
66
  "session.not_found": "Сессия не найдена: {id}",
@@ -267,6 +277,14 @@
267
277
  "cli.manage_context": "Управление контекстным окном",
268
278
  "cli.invalid_context_size": "Некорректный размер контекста. Должно быть число >= 1024",
269
279
  "cli.context_set": "Контекстное окно установлено: {size} токенов",
280
+ "cli.context_budget_header": "Бюджет контекста (окно {window} токенов):",
281
+ "cli.context_system_line": " системный промпт: {tokens} токенов ({percent}%)",
282
+ "cli.context_reserve_line": " резерв ответа: {tokens} токенов ({percent}%)",
283
+ "cli.context_history_line": " история: {tokens} токенов ({percent}%)",
284
+ "cli.context_system_fraction": "Доля системного промпта в контекстном окне (0.05–0.9)",
285
+ "cli.context_reserve_fraction": "Доля резерва ответа в контекстном окне (0.05–0.9)",
286
+ "cli.context_fraction_set": "Доля {key} установлена: {value}",
287
+ "cli.context_invalid_fraction": "Некорректная доля. Нужно число от 0.05 до 0.9 (системный + резерв должны оставаться ниже 0.95).",
270
288
  "cli.set_model": "Установить модель по умолчанию",
271
289
  "cli.model_set": "Модель установлена: {name}",
272
290
  "cli.certify": "Запустить сертификационный набор для модели",
@@ -426,7 +444,7 @@
426
444
  "repl.sysprompt_desc": "Показать системный промпт",
427
445
  "repl.sysprompt_tokens": "Системный промпт ({count} токенов):",
428
446
  "repl.excluded_blocks": "\nИсключённые блоки: {count}",
429
- "repl.context_usage": "Использование: /context <размер> (мин 1024)",
447
+ "repl.context_usage": "Использование: /context [<размер> | system <доля> | reserve <доля>] (мин 1024; доли 0.05–0.9)",
430
448
  "repl.msgs": "сообщ.",
431
449
  "repl.max_iters": "Макс итераций:",
432
450
  "repl.stuck_thresh": "Порог зависания:",
@@ -715,6 +733,13 @@
715
733
  "moe.partial_result": "Частичный результат",
716
734
  "moe.scope_approved": "🔓 Scope расширен для \"{subtask}\": {files}",
717
735
  "moe.scope_rejected": "⛔ Запрос scope отклонён для \"{subtask}\"",
736
+ "prompt.overflow.compressing": "Переполнение системного промпта: сжимаю {count} блок(ов) перед первым запуском — на локальной модели это может занять время.",
737
+ "prompt.overflow.summarizing": "Переполнение системного промпта: суммаризирую \"{label}\" ({original} токенов → бюджет {budget} токенов) моделью…",
738
+ "prompt.overflow.truncating": "Переполнение системного промпта: обрезаю \"{label}\" ({original} токенов) — суммаризация недоступна.",
739
+ "prompt.overflow.summarize_failed": "Переполнение системного промпта: не удалось суммаризировать \"{label}\": {error} — перехожу к обрезке.",
740
+ "prompt.overflow.hint_needed": "промпту нужно ~{needed} токенов системного бюджета (сейчас: {window} × {fraction} = {budget})",
741
+ "prompt.overflow.hint_window": "подними contextWindow минимум до {required} (стандартный размер {recommended}) — {how}",
742
+ "prompt.overflow.hint_fraction": "или оставь окно и подними contextBudget.systemPrompt до ~{fraction} (выполни: mma context --system {fraction})",
718
743
  "prompt.overflow.exceeded": "Промпт переполнен: \"{label}\" ({original} токенов) превысил бюджет системного промпта ({budget} токенов) — {mode} до {resolved} токенов. Чтобы включить полностью, {hint}.",
719
744
  "prompt.overflow.failed": "Не удалось разрешить переполнение промпта: {error} — слишком большие блоки будут отброшены.",
720
745
  "prompt.overflow.startup": "Проверка при старте: {block} ({original} токенов) превышает бюджет системного промпта ({budget} токенов) — будет суммаризован/обрезан перед первым запуском. Чтобы включить полностью, {hint}.",
package/dist/main.js CHANGED
@@ -2492,6 +2492,16 @@ The path was joined onto the working directory because it does not exist as give
2492
2492
  "env.playwright_missing": "Playwright package is not installed — the browser tool will fail. Install with: bun add playwright",
2493
2493
  "env.playwright_browsers_missing": "Playwright browsers not downloaded ({dir}). The browser tool will fail. Install with: bunx playwright install chromium",
2494
2494
  "env.crash_stderr": "MMA crashed ({type}): {message} — crash report written to ~/.mma/logs/crash.jsonl",
2495
+ "env.model_autoload_failed": "Model auto-load failed: {error}. Continuing with manual load.",
2496
+ "env.model_loaded": "Model {model} loaded in {seconds}s",
2497
+ "env.reasoning_probe": "Reasoning probe: strategy={strategy} → {result}",
2498
+ "env.reasoning_respected": "mechanism respected",
2499
+ "env.reasoning_ignored": "mechanism IGNORED",
2500
+ "env.indexing": "Indexing project files…",
2501
+ "env.indexed": "Indexed {files} file(s) for the project map",
2502
+ "env.indexing_failed": "Project indexing failed: {error}",
2503
+ "env.index_skip_unreadable": 'indexer: skipping unreadable entry "{path}"',
2504
+ "env.killed_processes": "Killed {count} background process(es) on shutdown",
2495
2505
  "session.started": "Session started: {id}",
2496
2506
  "session.ended": "Session ended: {id}",
2497
2507
  "session.not_found": "Session not found: {id}",
@@ -2718,6 +2728,14 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
2718
2728
  "cli.manage_context": "Manage context window",
2719
2729
  "cli.invalid_context_size": "Invalid context size. Must be a number >= 1024",
2720
2730
  "cli.context_set": "Context window set to: {size} tokens",
2731
+ "cli.context_budget_header": "Context budget (window {window} tokens):",
2732
+ "cli.context_system_line": " system prompt: {tokens} tokens ({percent}%)",
2733
+ "cli.context_reserve_line": " response reserve: {tokens} tokens ({percent}%)",
2734
+ "cli.context_history_line": " history: {tokens} tokens ({percent}%)",
2735
+ "cli.context_system_fraction": "System-prompt share of the context window (0.05–0.9)",
2736
+ "cli.context_reserve_fraction": "Response-reserve share of the context window (0.05–0.9)",
2737
+ "cli.context_fraction_set": "{key} share set to {value}",
2738
+ "cli.context_invalid_fraction": "Invalid share. Use a number between 0.05 and 0.9 (system + reserve must stay below 0.95).",
2721
2739
  "repl.reload": "Reload agent with current config",
2722
2740
  "repl.reload_usage": "Usage: /reload",
2723
2741
  "repl.reloading": "Reloading agent...",
@@ -2883,7 +2901,7 @@ Available commands:`,
2883
2901
  "repl.sysprompt_tokens": "System prompt ({count} tokens):",
2884
2902
  "repl.excluded_blocks": `
2885
2903
  Excluded blocks: {count}`,
2886
- "repl.context_usage": "Usage: /context <size> (min 1024)",
2904
+ "repl.context_usage": "Usage: /context [<size> | system <fraction> | reserve <fraction>] (min 1024; fractions 0.05–0.9)",
2887
2905
  "repl.msgs": "msgs",
2888
2906
  "repl.max_iters": "Max iters:",
2889
2907
  "repl.stuck_thresh": "Stuck thresh:",
@@ -3192,6 +3210,13 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
3192
3210
  "moe.partial_result": "Partial result",
3193
3211
  "moe.scope_approved": '🔓 Scope expanded for "{subtask}": {files}',
3194
3212
  "moe.scope_rejected": '⛔ Scope request rejected for "{subtask}"',
3213
+ "prompt.overflow.compressing": "System prompt overflow: compressing {count} block(s) before the first run — this may take a while on a local model.",
3214
+ "prompt.overflow.summarizing": 'System prompt overflow: summarizing "{label}" ({original} tok → {budget} tok budget) with the model…',
3215
+ "prompt.overflow.truncating": 'System prompt overflow: truncating "{label}" ({original} tok) — summarization unavailable.',
3216
+ "prompt.overflow.summarize_failed": 'System prompt overflow: summarization failed for "{label}": {error} — falling back to truncation.',
3217
+ "prompt.overflow.hint_needed": "the prompt needs ~{needed} system tokens (current budget: {window} × {fraction} = {budget})",
3218
+ "prompt.overflow.hint_window": "raise contextWindow to at least {required} (standard size {recommended}) — {how}",
3219
+ "prompt.overflow.hint_fraction": "or keep the window and raise contextBudget.systemPrompt to ~{fraction} (run: mma context --system {fraction})",
3195
3220
  "prompt.overflow.exceeded": 'Prompt overflow: "{label}" ({original} tok) exceeded the system-prompt budget ({budget} tok) — {mode} to {resolved} tok. To fit fully, {hint}.',
3196
3221
  "prompt.overflow.failed": "Prompt overflow resolution failed: {error} — oversized blocks will be dropped.",
3197
3222
  "prompt.overflow.startup": "Startup check: {block} ({original} tok) exceeds the system-prompt budget ({budget} tok) — it will be summarized/truncated before the first run. To include it fully, {hint}.",
@@ -3282,6 +3307,16 @@ var init_ru = __esm(() => {
3282
3307
  "env.playwright_missing": "Пакет Playwright не установлен — браузерный инструмент не будет работать. Установите: bun add playwright",
3283
3308
  "env.playwright_browsers_missing": "Браузеры Playwright не скачаны ({dir}). Браузерный инструмент не будет работать. Установите: bunx playwright install chromium",
3284
3309
  "env.crash_stderr": "MMA упал ({type}): {message} — отчёт о падении записан в ~/.mma/logs/crash.jsonl",
3310
+ "env.model_autoload_failed": "Не удалось автоматически загрузить модель: {error}. Продолжаю с ручной загрузкой.",
3311
+ "env.model_loaded": "Модель {model} загружена за {seconds}с",
3312
+ "env.reasoning_probe": "Проверка reasoning: стратегия={strategy} → {result}",
3313
+ "env.reasoning_respected": "механизм соблюдается",
3314
+ "env.reasoning_ignored": "механизм ИГНОРИРУЕТСЯ",
3315
+ "env.indexing": "Индексация файлов проекта…",
3316
+ "env.indexed": "Проиндексировано файлов: {files} (карта проекта)",
3317
+ "env.indexing_failed": "Ошибка индексации проекта: {error}",
3318
+ "env.index_skip_unreadable": 'индексатор: пропускаю недоступный файл "{path}"',
3319
+ "env.killed_processes": "Остановлено фоновых процессов при завершении: {count}",
3285
3320
  "session.started": "Сессия начата: {id}",
3286
3321
  "session.ended": "Сессия завершена: {id}",
3287
3322
  "session.not_found": "Сессия не найдена: {id}",
@@ -3507,6 +3542,14 @@ var init_ru = __esm(() => {
3507
3542
  "cli.manage_context": "Управление контекстным окном",
3508
3543
  "cli.invalid_context_size": "Некорректный размер контекста. Должно быть число >= 1024",
3509
3544
  "cli.context_set": "Контекстное окно установлено: {size} токенов",
3545
+ "cli.context_budget_header": "Бюджет контекста (окно {window} токенов):",
3546
+ "cli.context_system_line": " системный промпт: {tokens} токенов ({percent}%)",
3547
+ "cli.context_reserve_line": " резерв ответа: {tokens} токенов ({percent}%)",
3548
+ "cli.context_history_line": " история: {tokens} токенов ({percent}%)",
3549
+ "cli.context_system_fraction": "Доля системного промпта в контекстном окне (0.05–0.9)",
3550
+ "cli.context_reserve_fraction": "Доля резерва ответа в контекстном окне (0.05–0.9)",
3551
+ "cli.context_fraction_set": "Доля {key} установлена: {value}",
3552
+ "cli.context_invalid_fraction": "Некорректная доля. Нужно число от 0.05 до 0.9 (системный + резерв должны оставаться ниже 0.95).",
3510
3553
  "cli.set_model": "Установить модель по умолчанию",
3511
3554
  "cli.model_set": "Модель установлена: {name}",
3512
3555
  "cli.certify": "Запустить сертификационный набор для модели",
@@ -3668,7 +3711,7 @@ var init_ru = __esm(() => {
3668
3711
  "repl.sysprompt_tokens": "Системный промпт ({count} токенов):",
3669
3712
  "repl.excluded_blocks": `
3670
3713
  Исключённые блоки: {count}`,
3671
- "repl.context_usage": "Использование: /context <размер> (мин 1024)",
3714
+ "repl.context_usage": "Использование: /context [<размер> | system <доля> | reserve <доля>] (мин 1024; доли 0.05–0.9)",
3672
3715
  "repl.msgs": "сообщ.",
3673
3716
  "repl.max_iters": "Макс итераций:",
3674
3717
  "repl.stuck_thresh": "Порог зависания:",
@@ -3984,6 +4027,13 @@ var init_ru = __esm(() => {
3984
4027
  "moe.partial_result": "Частичный результат",
3985
4028
  "moe.scope_approved": '🔓 Scope расширен для "{subtask}": {files}',
3986
4029
  "moe.scope_rejected": '⛔ Запрос scope отклонён для "{subtask}"',
4030
+ "prompt.overflow.compressing": "Переполнение системного промпта: сжимаю {count} блок(ов) перед первым запуском — на локальной модели это может занять время.",
4031
+ "prompt.overflow.summarizing": 'Переполнение системного промпта: суммаризирую "{label}" ({original} токенов → бюджет {budget} токенов) моделью…',
4032
+ "prompt.overflow.truncating": 'Переполнение системного промпта: обрезаю "{label}" ({original} токенов) — суммаризация недоступна.',
4033
+ "prompt.overflow.summarize_failed": 'Переполнение системного промпта: не удалось суммаризировать "{label}": {error} — перехожу к обрезке.',
4034
+ "prompt.overflow.hint_needed": "промпту нужно ~{needed} токенов системного бюджета (сейчас: {window} × {fraction} = {budget})",
4035
+ "prompt.overflow.hint_window": "подними contextWindow минимум до {required} (стандартный размер {recommended}) — {how}",
4036
+ "prompt.overflow.hint_fraction": "или оставь окно и подними contextBudget.systemPrompt до ~{fraction} (выполни: mma context --system {fraction})",
3987
4037
  "prompt.overflow.exceeded": 'Промпт переполнен: "{label}" ({original} токенов) превысил бюджет системного промпта ({budget} токенов) — {mode} до {resolved} токенов. Чтобы включить полностью, {hint}.',
3988
4038
  "prompt.overflow.failed": "Не удалось разрешить переполнение промпта: {error} — слишком большие блоки будут отброшены.",
3989
4039
  "prompt.overflow.startup": "Проверка при старте: {block} ({original} токенов) превышает бюджет системного промпта ({budget} токенов) — будет суммаризован/обрезан перед первым запуском. Чтобы включить полностью, {hint}.",
@@ -14629,6 +14679,9 @@ async function resolvePromptOverflow(opts) {
14629
14679
  const warnings = [];
14630
14680
  let used = includedTokens;
14631
14681
  let unresolved = [];
14682
+ if (sorted.length > 0) {
14683
+ logger?.warn(t("prompt.overflow.compressing", { count: String(sorted.length) }));
14684
+ }
14632
14685
  for (const block of sorted) {
14633
14686
  const kind = block.kind;
14634
14687
  const maxTokens = systemBudget - used - HINT_BLOCK_TOKENS;
@@ -14646,6 +14699,11 @@ async function resolvePromptOverflow(opts) {
14646
14699
  logger?.debug(`Prompt overflow: cache hit for ${kind} (${key}, ${mode})`);
14647
14700
  } else if (provider) {
14648
14701
  const targetChars = Math.floor(maxTokens * (CHARS_PER_TOKEN - 1));
14702
+ logger?.warn(t("prompt.overflow.summarizing", {
14703
+ label: blockLabel2(block.content),
14704
+ original: String(block.estimatedTokens),
14705
+ budget: String(maxTokens)
14706
+ }));
14649
14707
  try {
14650
14708
  const prompt = [
14651
14709
  `Summarize the following ${KIND_LABEL[kind]} document.`,
@@ -14675,12 +14733,19 @@ async function resolvePromptOverflow(opts) {
14675
14733
  `), maxTokens);
14676
14734
  }
14677
14735
  } catch (err) {
14678
- logger?.warn(`Prompt overflow: summarization failed for ${kind}: ${err?.message ?? err}`);
14736
+ logger?.warn(t("prompt.overflow.summarize_failed", {
14737
+ label: blockLabel2(block.content),
14738
+ error: String(err?.message ?? err)
14739
+ }));
14679
14740
  text = "";
14680
14741
  }
14681
14742
  }
14682
14743
  if (!text) {
14683
14744
  mode = "truncate";
14745
+ logger?.warn(t("prompt.overflow.truncating", {
14746
+ label: blockLabel2(block.content),
14747
+ original: String(block.estimatedTokens)
14748
+ }));
14684
14749
  text = truncateToTokens(block.content, maxTokens);
14685
14750
  }
14686
14751
  if (estimateTokens(text) > maxTokens) {
@@ -14741,10 +14806,38 @@ function recommendContextSize(neededSystemTokens) {
14741
14806
  const sizes = [8192, 16384, 32768, 65536, 131072, 262144];
14742
14807
  return sizes.find((s) => Math.floor(s * 0.1) >= neededSystemTokens) ?? sizes[sizes.length - 1];
14743
14808
  }
14809
+ function requiredContextWindow(neededTokens, systemFraction) {
14810
+ const fraction = systemFraction > 0 ? systemFraction : 0.1;
14811
+ return Math.ceil(neededTokens / fraction);
14812
+ }
14813
+ function requiredSystemFraction(neededTokens, contextWindow) {
14814
+ if (contextWindow <= 0)
14815
+ return 1;
14816
+ return Math.min(1, Math.ceil(neededTokens / contextWindow * 100) / 100);
14817
+ }
14818
+ function overflowHint(config, configDir, neededTokens) {
14819
+ const fraction = config.contextBudget?.systemPrompt ?? 0.1;
14820
+ const budget = Math.floor(config.contextWindow * fraction);
14821
+ const recommended = recommendContextSize(neededTokens);
14822
+ const requiredWindow = requiredContextWindow(neededTokens, fraction);
14823
+ const requiredFraction = requiredSystemFraction(neededTokens, config.contextWindow);
14824
+ const how = configDir ? contextWindowHint(config, configDir, recommended) : `increase contextWindow (e.g. to ${recommended})`;
14825
+ return [
14826
+ t("prompt.overflow.hint_needed", {
14827
+ needed: neededTokens,
14828
+ window: config.contextWindow,
14829
+ fraction,
14830
+ budget
14831
+ }),
14832
+ t("prompt.overflow.hint_window", { required: requiredWindow, recommended, how }),
14833
+ t("prompt.overflow.hint_fraction", { fraction: requiredFraction })
14834
+ ].join(" ");
14835
+ }
14744
14836
  var HINT_BLOCK_TOKENS = 60, CHARS_PER_TOKEN = 4, KIND_ORDER, KIND_LABEL, KIND_SOURCE_FILE;
14745
14837
  var init_prompt_overflow = __esm(() => {
14746
14838
  init_prompt_builder();
14747
14839
  init_token_counter();
14840
+ init_i18n();
14748
14841
  KIND_ORDER = ["instructions", "project-map"];
14749
14842
  KIND_LABEL = {
14750
14843
  instructions: "project instructions (AGENTS.md)",
@@ -14920,8 +15013,8 @@ class Agent {
14920
15013
  this.promptOverrides.set(r.kind, r);
14921
15014
  }
14922
15015
  this.overflowHintBlock = res.hintBlock;
14923
- const recommended = recommendContextSize(dry.blocks.filter((b) => b.included).reduce((s, b) => s + b.tokens, 0) + overflow.reduce((s, b) => s + b.estimatedTokens, 0) + HINT_BLOCK_TOKENS);
14924
- const hint = this.deps.configDir ? contextWindowHint(cfg, this.deps.configDir, recommended) : `increase contextWindow (e.g. to ${recommended})`;
15016
+ const needed = dry.blocks.filter((b) => b.included).reduce((s, b) => s + b.tokens, 0) + overflow.reduce((s, b) => s + b.estimatedTokens, 0) + HINT_BLOCK_TOKENS;
15017
+ const hint = overflowHint(cfg, this.deps.configDir, needed);
14925
15018
  for (const w of res.warnings) {
14926
15019
  this.deps.logger.warn(t("prompt.overflow.exceeded", {
14927
15020
  label: w.label,
@@ -15483,7 +15576,7 @@ class Agent {
15483
15576
  contextManager.onCompact = null;
15484
15577
  const killed = processRegistry.killAll();
15485
15578
  if (killed > 0) {
15486
- logger.info(`Killed ${killed} background process(es) on shutdown`);
15579
+ logger.info(t("env.killed_processes", { count: String(killed) }));
15487
15580
  }
15488
15581
  pluginManager.runOnSessionEnd({
15489
15582
  logger,
@@ -24926,7 +25019,7 @@ class Indexer {
24926
25019
  }
24927
25020
  }
24928
25021
  } catch (e) {
24929
- logger2.warn(`indexer: skipping unreadable entry "${relPath}"`, { error: String(e) });
25022
+ logger2.warn(t("env.index_skip_unreadable", { path: relPath }), { error: String(e) });
24930
25023
  continue;
24931
25024
  }
24932
25025
  }
@@ -24947,6 +25040,7 @@ var logger2, IGNORE_DIRS;
24947
25040
  var init_walker = __esm(() => {
24948
25041
  init_app_logger();
24949
25042
  init_symbols();
25043
+ init_i18n();
24950
25044
  logger2 = new Logger("warn", "indexer");
24951
25045
  IGNORE_DIRS = new Set([
24952
25046
  ".git",
@@ -26524,11 +26618,11 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
26524
26618
  autoLoad: config.modelLoad.autoLoad
26525
26619
  });
26526
26620
  if (!loadResult.success) {
26527
- logger4.warn(`Model auto-load failed: ${loadResult.error}. Continuing with manual load.`);
26621
+ logger4.warn(t("env.model_autoload_failed", { error: String(loadResult.error ?? "") }));
26528
26622
  } else if (loadResult.alreadyLoaded) {
26529
26623
  logger4.debug(`Model ${config.model} already loaded`);
26530
26624
  } else {
26531
- logger4.info(`Model ${config.model} loaded in ${loadResult.loadTime}s`);
26625
+ logger4.info(t("env.model_loaded", { model: config.model, seconds: String(loadResult.loadTime) }));
26532
26626
  }
26533
26627
  }
26534
26628
  const contextProbePromise = (async () => {
@@ -26564,7 +26658,10 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
26564
26658
  const passed = cached !== undefined ? cached : await probeReasoningSupport2(llmProvider, reasoningStrategy);
26565
26659
  if (cached === undefined && passed !== null)
26566
26660
  setCachedProbeResult2(cacheK, passed);
26567
- logger4.info(`Reasoning probe: strategy=${reasoningStrategy} → ${passed === true ? "mechanism respected" : "mechanism IGNORED"}`);
26661
+ logger4.info(t("env.reasoning_probe", {
26662
+ strategy: reasoningStrategy,
26663
+ result: passed === true ? t("env.reasoning_respected") : t("env.reasoning_ignored")
26664
+ }));
26568
26665
  return passed === true;
26569
26666
  } catch {
26570
26667
  return false;
@@ -26588,9 +26685,15 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
26588
26685
  cacheDir: projectMapCacheDir
26589
26686
  });
26590
26687
  try {
26591
- await indexerModule.buildIndex();
26688
+ logger4.info(t("env.indexing"));
26689
+ const index = await indexerModule.buildIndex();
26690
+ if (index) {
26691
+ logger4.info(t("env.indexed", { files: String(index.files.length) }));
26692
+ } else {
26693
+ logger4.warn(t("env.indexing_failed", { error: "no index produced" }));
26694
+ }
26592
26695
  } catch (err) {
26593
- logger4.warn(`Project indexing failed: ${err.message}`);
26696
+ logger4.warn(t("env.indexing_failed", { error: String(err?.message ?? err) }));
26594
26697
  }
26595
26698
  const skillsLoader = new SkillsLoader;
26596
26699
  const builtinDir = join46(import.meta.dirname, "skills", "builtin");
@@ -26782,8 +26885,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
26782
26885
  const dry = dryRunOverflow([...promptBlocks, ...dynamicBlocks], systemBudget);
26783
26886
  if (dry.overflow.length > 0) {
26784
26887
  const overflowTokens = dry.overflow.reduce((s, b) => s + b.estimatedTokens, 0);
26785
- const recommended = recommendContextSize(dry.includedTokens + overflowTokens);
26786
- const hint = contextWindowHint(config, dir, recommended);
26888
+ const hint = overflowHint(config, dir, dry.includedTokens + overflowTokens);
26787
26889
  for (const b of dry.overflow) {
26788
26890
  logger4.warn(t("prompt.overflow.startup", {
26789
26891
  block: b.kind === "instructions" ? "AGENTS.md" : "project map",
@@ -27688,6 +27790,49 @@ var init_map_command = __esm(() => {
27688
27790
  init_i18n();
27689
27791
  });
27690
27792
 
27793
+ // src/config/budget.ts
27794
+ function budgetBreakdown(config) {
27795
+ const window = config.contextWindow;
27796
+ const budget = config.contextBudget;
27797
+ const system = Math.floor(window * budget.systemPrompt);
27798
+ const reserve = Math.floor(window * budget.responseReserve);
27799
+ const history = window - system - reserve;
27800
+ return {
27801
+ window,
27802
+ system,
27803
+ reserve,
27804
+ history,
27805
+ systemFraction: budget.systemPrompt,
27806
+ reserveFraction: budget.responseReserve,
27807
+ historyFraction: window > 0 ? history / window : 0
27808
+ };
27809
+ }
27810
+ function setBudgetShare(config, key, value) {
27811
+ if (!Number.isFinite(value) || value < MIN_SHARE || value > MAX_SHARE)
27812
+ return "range";
27813
+ const budget = config.contextBudget;
27814
+ const nextSystem = key === "system" ? value : budget.systemPrompt;
27815
+ const nextReserve = key === "reserve" ? value : budget.responseReserve;
27816
+ if (nextSystem + nextReserve > MAX_COMBINED)
27817
+ return "combined";
27818
+ budget[key === "system" ? "systemPrompt" : "responseReserve"] = value;
27819
+ return null;
27820
+ }
27821
+ function budgetBreakdownLines(config) {
27822
+ const bd = budgetBreakdown(config);
27823
+ const pct = (f) => Math.round(f * 100);
27824
+ return [
27825
+ t("cli.context_budget_header", { window: bd.window }),
27826
+ t("cli.context_system_line", { tokens: bd.system, percent: pct(bd.systemFraction) }),
27827
+ t("cli.context_reserve_line", { tokens: bd.reserve, percent: pct(bd.reserveFraction) }),
27828
+ t("cli.context_history_line", { tokens: bd.history, percent: pct(bd.historyFraction) })
27829
+ ];
27830
+ }
27831
+ var MIN_SHARE = 0.05, MAX_SHARE = 0.9, MAX_COMBINED = 0.95;
27832
+ var init_budget = __esm(() => {
27833
+ init_i18n();
27834
+ });
27835
+
27691
27836
  // node_modules/yaml/dist/nodes/identity.js
27692
27837
  var require_identity = __commonJS((exports) => {
27693
27838
  var ALIAS = Symbol.for("yaml.alias");
@@ -36239,11 +36384,28 @@ function registerProviderCommands(ctx) {
36239
36384
  ctx.registerCommand({
36240
36385
  name: "context",
36241
36386
  description: t("cli.manage_context"),
36242
- usage: "/context <size>",
36387
+ usage: t("repl.context_usage"),
36243
36388
  action: async (args) => {
36389
+ const save = async () => {
36390
+ const configPath = join54(ctx.configDir, "config.json");
36391
+ saveConfig(ctx.config, configPath, dirname24(configPath));
36392
+ await ctx.agent.reconfigure(ctx.config);
36393
+ };
36244
36394
  if (args.length === 0) {
36245
- console.log(`/context ${t("repl.context")} ${ctx.config.contextWindow}`);
36246
- console.log(t("repl.context_usage"));
36395
+ for (const line of budgetBreakdownLines(ctx.config))
36396
+ console.log(line);
36397
+ console.log(pc2.dim(t("repl.context_usage")));
36398
+ return;
36399
+ }
36400
+ if (args[0] === "system" || args[0] === "reserve") {
36401
+ const key = args[0];
36402
+ const value = Number(args[1]);
36403
+ if (setBudgetShare(ctx.config, key, value) !== null) {
36404
+ console.log(pc2.yellow(t("cli.context_invalid_fraction")));
36405
+ return;
36406
+ }
36407
+ await save();
36408
+ console.log(pc2.green(t("cli.context_fraction_set", { key, value })));
36247
36409
  return;
36248
36410
  }
36249
36411
  const size = parseInt(args[0], 10);
@@ -36252,9 +36414,7 @@ function registerProviderCommands(ctx) {
36252
36414
  return;
36253
36415
  }
36254
36416
  ctx.config.contextWindow = size;
36255
- const configPath = join54(ctx.configDir, "config.json");
36256
- saveConfig(ctx.config, configPath, dirname24(configPath));
36257
- await ctx.agent.reconfigure(ctx.config);
36417
+ await save();
36258
36418
  console.log(pc2.green(t("cli.context_set", { size })));
36259
36419
  }
36260
36420
  });
@@ -36641,6 +36801,7 @@ var init_repl_commands = __esm(() => {
36641
36801
  init_i18n();
36642
36802
  init_setup();
36643
36803
  init_config2();
36804
+ init_budget();
36644
36805
  init_token_counter();
36645
36806
  init_map_command();
36646
36807
  init_utils();
@@ -37372,6 +37533,7 @@ function targetsFromProviders(entries) {
37372
37533
  // src/cli/commands.ts
37373
37534
  init_version();
37374
37535
  init_map_command();
37536
+ init_budget();
37375
37537
 
37376
37538
  // src/modules/updater/changelog-reader.ts
37377
37539
  import { readFileSync as readFileSync35, existsSync as existsSync53 } from "fs";
@@ -37632,9 +37794,25 @@ function buildModelCommands(program2) {
37632
37794
  });
37633
37795
  }
37634
37796
  function buildContextCommand(program2) {
37635
- program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
37636
- const configPath = join53(homedir17(), ".mma", "config.json");
37637
- const { config } = await bootstrap();
37797
+ program2.command("context").description(t("cli.manage_context")).argument("[size]", "Context window size in tokens (omit to show the budget breakdown)").option("--system <fraction>", t("cli.context_system_fraction")).option("--reserve <fraction>", t("cli.context_reserve_fraction")).action(async (size, opts) => {
37798
+ const { config, configDir } = await bootstrap();
37799
+ const configPath = join53(configDir, "config.json");
37800
+ if (opts.system !== undefined || opts.reserve !== undefined) {
37801
+ const key = opts.system !== undefined ? "system" : "reserve";
37802
+ const value = Number(opts.system ?? opts.reserve);
37803
+ if (setBudgetShare(config, key, value) !== null) {
37804
+ console.log(t("cli.context_invalid_fraction"));
37805
+ return;
37806
+ }
37807
+ saveConfig(config, configPath, dirname23(configPath));
37808
+ console.log(t("cli.context_fraction_set", { key, value }));
37809
+ return;
37810
+ }
37811
+ if (size === undefined) {
37812
+ for (const line of budgetBreakdownLines(config))
37813
+ console.log(line);
37814
+ return;
37815
+ }
37638
37816
  const contextWindow = parseInt(size, 10);
37639
37817
  if (isNaN(contextWindow) || contextWindow < 1024) {
37640
37818
  console.log(t("cli.invalid_context_size"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.60.0",
3
+ "version": "0.61.0",
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": {