micro-models-agent 0.28.17 → 0.29.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 (94) hide show
  1. package/dist/cli/commands.js +3 -116
  2. package/dist/cli/main.js +8 -35
  3. package/dist/cli/repl.js +611 -110
  4. package/dist/cli/setup.js +12 -32
  5. package/dist/config/config.js +30 -46
  6. package/dist/config/defaults.js +1 -10
  7. package/dist/config/security.js +8 -15
  8. package/dist/core/agent-moe.js +12 -24
  9. package/dist/core/agent.js +47 -281
  10. package/dist/core/bootstrap.js +36 -52
  11. package/dist/core/session-logger.js +2 -35
  12. package/dist/i18n/en.json +15 -79
  13. package/dist/i18n/index.js +9 -12
  14. package/dist/i18n/ru.json +15 -79
  15. package/dist/index.js +13 -13
  16. package/dist/llm/openai-compat.js +10 -39
  17. package/dist/logger/app-logger.js +16 -83
  18. package/dist/main.js +625 -243
  19. package/dist/modules/browser/session.js +60 -108
  20. package/dist/modules/context/history.js +15 -0
  21. package/dist/modules/context/manager.js +10 -119
  22. package/dist/modules/execution/auditor.js +39 -33
  23. package/dist/modules/execution/index.js +6 -8
  24. package/dist/modules/execution/module.js +32 -474
  25. package/dist/modules/execution/moe-executor.js +40 -97
  26. package/dist/modules/execution/planner.js +13 -63
  27. package/dist/modules/execution/stuck-detector.js +39 -252
  28. package/dist/modules/execution/tracker.js +7 -21
  29. package/dist/modules/execution/verifier.js +17 -46
  30. package/dist/modules/hallucination/confidence.js +2 -7
  31. package/dist/modules/hallucination/consistency.js +42 -8
  32. package/dist/modules/hallucination/detector.js +21 -26
  33. package/dist/modules/hallucination/factual.js +150 -170
  34. package/dist/modules/hallucination/index.js +4 -5
  35. package/dist/modules/index.js +5 -5
  36. package/dist/modules/mcp/client.js +2 -8
  37. package/dist/modules/memory/store.js +0 -4
  38. package/dist/modules/plugins/builtin/lint-on-write.js +38 -143
  39. package/dist/modules/processes/detect.js +34 -0
  40. package/dist/modules/processes/index.js +2 -1
  41. package/dist/modules/processes/registry.js +35 -125
  42. package/dist/modules/processes/runner.js +110 -9
  43. package/dist/modules/security/audit-log.js +10 -30
  44. package/dist/modules/security/command-validator.js +16 -42
  45. package/dist/modules/security/content-scanner.js +8 -9
  46. package/dist/modules/security/network-validator.js +2 -2
  47. package/dist/modules/security/path-validator.js +10 -64
  48. package/dist/modules/security/security-policies.js +67 -221
  49. package/dist/modules/security/session-encryption.js +25 -42
  50. package/dist/modules/session/manager.js +10 -15
  51. package/dist/modules/session/store.js +8 -62
  52. package/dist/modules/skills/index.js +3 -2
  53. package/dist/modules/skills/matcher.js +27 -0
  54. package/dist/modules/skills/module.js +23 -10
  55. package/dist/tools/bash.js +90 -287
  56. package/dist/tools/create-dir.js +1 -0
  57. package/dist/tools/delete-file.js +1 -0
  58. package/dist/tools/edit-file.js +8 -10
  59. package/dist/tools/executor.js +7 -57
  60. package/dist/tools/grep-tool.js +29 -51
  61. package/dist/tools/index.js +40 -55
  62. package/dist/tools/load-skill.js +18 -14
  63. package/dist/tools/move-file.js +2 -3
  64. package/dist/tools/pipeline-run.js +1 -1
  65. package/dist/tools/read-file.js +5 -15
  66. package/dist/tools/search-history.js +22 -42
  67. package/dist/tools/subagent.js +12 -21
  68. package/dist/tools/web-browse.js +25 -54
  69. package/dist/tools/web-fetch.js +34 -60
  70. package/dist/tools/web-search.js +20 -39
  71. package/dist/tools/write-file.js +10 -13
  72. package/dist/ui/diff.js +16 -9
  73. package/dist/ui/renderer.js +6 -69
  74. package/package.json +1 -1
  75. package/dist/cli/repl-commands.js +0 -633
  76. package/dist/core/workspace.js +0 -76
  77. package/dist/logger/file-log.js +0 -151
  78. package/dist/modules/certification/cli.js +0 -176
  79. package/dist/modules/certification/fact-checker.js +0 -84
  80. package/dist/modules/certification/loader.js +0 -111
  81. package/dist/modules/certification/manifest.js +0 -50
  82. package/dist/modules/certification/runner.js +0 -162
  83. package/dist/modules/certification/scenarios.js +0 -124
  84. package/dist/modules/certification/types.js +0 -1
  85. package/dist/modules/execution/plan-coverage.js +0 -68
  86. package/dist/modules/execution/plan-persister.js +0 -46
  87. package/dist/modules/execution/plan-store.js +0 -159
  88. package/dist/modules/hallucination/js-identifiers.js +0 -72
  89. package/dist/modules/hallucination/llm-judge.js +0 -103
  90. package/dist/modules/lsp/client.js +0 -235
  91. package/dist/modules/lsp/config.js +0 -81
  92. package/dist/modules/lsp/index.js +0 -3
  93. package/dist/modules/lsp/module.js +0 -68
  94. package/dist/modules/lsp/types.js +0 -1
package/dist/i18n/ru.json CHANGED
@@ -26,7 +26,6 @@
26
26
  "error.llm": "Ошибка LLM: {message}",
27
27
  "error.response_blocked": "Ответ заблокирован: {reason}",
28
28
  "error.max_iters": "Достигнут максимум итераций ({max})",
29
- "error.empty_response": "Модель вернула пустой ответ после повторных попыток",
30
29
  "error.grep_failed": "Ошибка grep: {message}",
31
30
  "error.search_failed": "Ошибка поиска: {message}",
32
31
  "error.fetch_failed": "Ошибка загрузки: {message}",
@@ -64,7 +63,6 @@
64
63
  "tool.failed": "Инструмент {name} упал: {error}",
65
64
  "tool.unknown": "Неизвестный инструмент: {name}",
66
65
  "tool.blocked": "Заблокировано плагином: {plugin}",
67
- "tool.blocked_reason": "Заблокировано плагином: {plugin}. Причина: {reason}",
68
66
  "tool.using": "[{label}]",
69
67
  "tool.friendly.write_file": "Запись файла",
70
68
  "tool.friendly.read_file": "Чтение файла",
@@ -84,9 +82,6 @@
84
82
  "tool.friendly.web_search": "Поиск в интернете",
85
83
  "tool.friendly.web_fetch": "Загрузка страницы",
86
84
  "tool.friendly.web_browse": "Просмотр страницы",
87
- "tool.web_fetch_result": "Загружена страница: {url} — {chars} симв., {lines} строк{truncated}",
88
- "tool.web_browse_result": "Просмотрена страница: {url} — {chars} симв., {lines} строк{truncated}",
89
- "tool.web_search_result": "Результаты поиска \"{query}\" — {count} результатов",
90
85
  "tool.friendly.browser": "Браузер",
91
86
  "tool.friendly.subagent": "Задача подагенту",
92
87
  "tool.friendly.question": "Вопрос пользователю",
@@ -117,7 +112,7 @@
117
112
  "tool.question.answered": "Пользователь ответил на ваши вопросы: {formatted}. Теперь продолжайте с учётом ответов.",
118
113
  "tool.name_or_task": "Укажите параметр \"name\" или \"task\"",
119
114
  "tool.invalid_params": "Неверные параметры",
120
- "tool.skill_budget": "Скилл \"{name}\" загружен ({tokens} токенов, осталось {remaining} в бюджете скиллов). Контент скилла теперь в системном промпте — перезагрузка после компрессии не нужна.",
115
+ "tool.skill_budget": "[Скилл загружен. Осталось {remaining} токенов в бюджете скиллов]",
121
116
  "tool.skill_available_hint": "Доступные скиллы",
122
117
  "tool.no_results": "Нет результатов по \"{query}\"",
123
118
  "tool.search_results": "Результаты поиска \"{query}\":\n{results}",
@@ -128,10 +123,9 @@
128
123
  "tool.memory_error": "Ошибка памяти: {error}",
129
124
  "tool.screenshot_unavailable": "[Скриншот сделан — изображение недоступно для текстовой модели]",
130
125
  "tool.timeout": "Инструмент {name} превысил таймаут ({seconds} сек)",
131
- "tool.aborted": "Инструмент {name} прерван пользователем",
132
126
  "tool.interactive_disabled": "Интерактивный инструмент отключён в режиме exit-on-complete. Продолжай без вопроса пользователю.",
133
127
  "proc.started": "Фоновый процесс запущен: {id} (PID {pid}).\nКоманда: {command}",
134
- "proc.promoted_hint": "Команда всё ещё выполняется через {ms} мс переведена в фоновый режим",
128
+ "proc.detected_hint": "[Обнаружена длительная командазапущена в фоне]",
135
129
  "proc.manage_hint": "Проверить вывод: process_log id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
136
130
  "proc.none": "Фоновых процессов нет.",
137
131
  "proc.not_found": "Процесс не найден: {id}",
@@ -140,6 +134,7 @@
140
134
  "proc.list_header": "Фоновые процессы",
141
135
  "proc.log_header": "Вывод процесса {id} ({status}):",
142
136
  "proc.log_empty": "(вывода пока нет)",
137
+ "proc.timed_out": "Команда превысила таймаут {ms} мс и была остановлена.",
143
138
  "proc.hint": "Управление: {list}, {log}, {kill}.",
144
139
  "proc.status_running": "работает",
145
140
  "proc.status_exited": "завершён",
@@ -147,29 +142,19 @@
147
142
  "tool.friendly.process_list": "Список фоновых процессов",
148
143
  "tool.friendly.process_log": "Вывод процесса",
149
144
  "tool.friendly.process_kill": "Остановка процесса",
150
- "plan.no_steps": "Не указаны шаги плана. Укажите конкретные шаги с файлами и командами.",
151
145
  "plan.created": "План создан: {title} ({steps} шагов)",
152
- "plan.coverage_warning": "План может не покрывать требуемые файлы из постановки: {missing}. Добавьте шаги, покрывающие их.",
153
146
  "plan.step_done": "Шаг {n}/{total}: {description} ✓",
154
147
  "plan.complete": "Задача выполнена: {summary}",
155
148
  "plan.title_steps": "План \"{title}\" создан с {count} шагами",
156
149
  "plan.step_marked": "Шаг {step} отмечен как {status}",
157
150
  "plan.aborted": "План отменён",
158
151
  "plan.unknown_action": "Неизвестное действие плана: {action}",
159
- "plan.updated": "План обновлён: {title} ({steps} шагов)",
160
152
  "plan.acknowledged": "План {action}: принято",
161
153
  "plan.step_status": "Шаг {step}: {status}",
162
154
  "plan.no_active": "Нет активного плана",
163
155
  "plan.step_not_found": "Шаг не найден",
164
156
  "plan.show_header": "Статус плана:",
165
157
  "plan.show_empty": "(в плане нет шагов)",
166
- "plan.list_header": "Планы:",
167
- "plan.list_empty": "Нет планов",
168
- "plan.switch_no_id": "Укажите id плана для переключения",
169
- "plan.not_found": "План не найден: {id}",
170
- "plan.switched": "Переключено на план {id}: {title}",
171
- "plan.replanned": "План перепланирован: {kept} выполненных шагов сохранено, {steps} новых шагов добавлено",
172
- "plan.replan_no_steps": "Укажите новые шаги для перепланирования",
173
158
  "todo.added": "Добавлено {count} задач: {items}",
174
159
  "todo.marked_done": "Отмечено выполненными: {count}",
175
160
  "todo.no_active": "Нет активных задач",
@@ -203,35 +188,6 @@
203
188
  "cli.context_set": "Контекстное окно установлено: {size} токенов",
204
189
  "cli.set_model": "Установить модель по умолчанию",
205
190
  "cli.model_set": "Модель установлена: {name}",
206
- "cli.certify": "Запустить сертификационный набор для модели",
207
- "cli.cert_provider_url": "Базовый URL провайдера для сертификации (по умолчанию — текущий конфиг)",
208
- "cli.cert_provider_key": "API-ключ провайдера (необязательно)",
209
- "cli.cert_context_window": "Размер контекстного окна в токенах",
210
- "cli.cert_tags": "Теги сценариев через запятую (core, security, image, network, browser)",
211
- "cli.cert_reps": "Повторов по умолчанию на сценарий (переопределяется сценарием)",
212
- "cli.cert_force": "Перепрогнать существующую сертификацию",
213
- "cli.cert_clean": "Удалить песочницы после успешного прогона",
214
- "cli.cert_security_required": "Безопасность отключена. Включите её (например `mma security set-policy balanced`), чтобы запустить security-сьют.",
215
- "cli.cert_no_scenarios": "Нет сценариев под теги: {tags}",
216
- "cli.cert_exists": "Сертификация {model} на этом провайдере уже существует.",
217
- "cli.cert_exists_hint": "Используйте --force для перепрогона.",
218
- "cli.cert_started": "Сертификация {model} на {provider}...",
219
- "cli.cert_done": "Сертификация завершена: {passed} pass, {failed} fail, {skipped} skipped ({total} всего)",
220
- "cli.cert_rep_pass": "pass",
221
- "cli.cert_rep_fail": "fail",
222
- "cli.cert_status": "Показать статус сертификации модели",
223
- "cli.cert_list": "Список всех сертификаций",
224
- "cli.cert_uncertify": "Удалить сертификацию",
225
- "cli.cert_not_found": "Сертификация {model} не найдена",
226
- "cli.cert_uncertified": "Сертификация {model} удалена",
227
- "cli.cert_empty": "Сертификаций пока нет. Запустите `mma model certify <модель>`.",
228
- "cli.cert_provider_col": "провайдер",
229
- "cli.cert_suite_col": "сьют",
230
- "cli.cert_date_col": "дата",
231
- "cli.cert_version_col": "версия MMA",
232
- "cli.cert_stale_hint": "Запустите `mma model certify --force` для обновления.",
233
- "cli.cert_fixture_missing": "Фикстура отсутствует для {id}: {path}",
234
- "cli.cert_marks_hint": "Маркеры: ✔ сертифицировано на этом провайдере, ○ сертифицировано на старой версии MMA, · не сертифицировано",
235
191
  "cli.manage_providers": "Управление провайдерами",
236
192
  "cli.list_providers": "Список провайдеров",
237
193
  "cli.current_provider": "Текущий провайдер:",
@@ -330,14 +286,10 @@
330
286
  "repl.skill_unknown_sub": "Неизвестная подкоманда скилла: {subcmd}",
331
287
  "repl.skill_usage": "Использование: /skill [list|loaded|load|unload|search]",
332
288
  "repl.agent": "Агент: ",
333
- "repl.interrupt": "Прервано (Esc)",
334
- "repl.title": "MMA REPL v{version}",
289
+ "repl.title": "MMA REPL v2",
335
290
  "repl.model": "Модель:",
336
291
  "repl.provider": "Провайдер:",
337
292
  "repl.context": "Контекст:",
338
- "repl.sysprompt_label": "Системный промпт:",
339
- "repl.sysprompt_size": "{used} / {budget} токенов",
340
- "repl.sysprompt_desc": "Показать системный промпт",
341
293
  "repl.max_iters": "Макс итераций:",
342
294
  "repl.stuck_thresh": "Порог зависания:",
343
295
  "repl.reasoning_label": "Рассуждения:",
@@ -419,34 +371,25 @@
419
371
  "skill.prompt_hint": "НЕ загружайте скиллы автоматически. Загружайте скилл ТОЛЬКО когда пользователь явно об этом просит (например 'используй скилл X' или 'загрузи скилл Y'). Скиллы ниже перечислены только для справки — не угадывайте какой скилл подходит по описанию задачи.",
420
372
  "skill.prompt_fallback": "Если load_skill не удался из-за большого размера скилла, продолжайте задачу без него — не останавливайтесь.",
421
373
  "skill.loaded_content": "[Скилл загружен: {name}]\n{content}\n\nИспользуйте эти знания для ответа на вопрос пользователя.",
422
- "exec.stuck": "Нет прогресса на шаге {stepId} ({description}) {iterations} итераций.",
423
- "exec.stuck_recovery": "Шаг {stepId} — \"{description}\" без прогресса уже {iterations} итераций. Попробуйте другой подход: проверьте что нужно для этого шага, установлены ли зависимости, или создайте файлы напрямую через write_file вместо команд терминала. После завершения шага вызовите plan update step={stepId} status=done.",
424
- "exec.tool_errors": "Инструмент {tool} упал {count} раз. Попробуйте другой инструмент.",
425
- "exec.tool_errors_recovery": "Инструмент {tool} упал {count} раз подряд. Попробуйте альтернативу: создайте файлы напрямую через write_file, используйте другую команду, или пропустите этот шаг через plan update step=N status=skipped с пометкой почему.",
426
- "exec.repetitive_tool": "Инструмент {tool} вызван {count} раз с одинаковыми аргументами и результатом. Попробуйте другой подход создайте файлы напрямую, измените аргументы или проверьте статус процесса через process_log.",
427
- "exec.consecutive_failures_recovery": "{count} инструментов подряд упали. Создавайте файлы напрямую через write_file вместо команд терминала. Проверьте что зависимости установлены (npm install). Не запускайте сборку/тесты пока не созданы все файлы.",
428
- "exec.plan_warning": "Текущий шаг плана {step} \"{description}\", но вызывается {tool} для файлов вне этого шага. Завершите текущий шаг, вызовите plan update step={step} status=done, затем переходите к следующему.",
429
- "exec.plan_blocked": "{max} вызовов подряд вне текущего шага. Завершите шаг {step} — остальные шаги ждут пока этот не будет выполнен.",
430
- "exec.off_track": "Шаг {stepId} — \"{description}\", но используется {tool} для другого пути. Вернитесь к текущему шагу.",
431
- "exec.step_gate_deps": "[⚠ Шаг {step} \"{description}\": зависимости не установлены. Сначала выполните команду установки (npm install, pip install и т.д.). Проверьте что lock-файл или директория зависимостей существует. Если этот шаг на самом деле не нужен (внешних зависимостей нет) — пропусти его: plan update step={step} status=skipped note=\"зависимости не нужны\".]",
432
- "exec.step_gate_deps_force": "[⚠ Шаг {step}: зависимости всё ещё не установлены (нет lock-файла) — второе предупреждение. Если этот шаг НЕ нужен — НЕМЕДЛЕННО вызови: plan update step={step} status=skipped note=\"зависимости не нужны\". Если нужен — выполни установку прямо сейчас. Не делай других вызовов инструментов до обновления плана.]",
433
- "exec.step_gate_empty": "[⚠ Шаг {step}: файлы существуют, но выглядят пустыми: {files}. Добавьте реальный код в эти файлы перед тем как переходить к следующему шагу.]",
434
- "exec.step_gate_ok": "[✓] Шаг {step} завершён и проверен. ПЕРЕХОДИМ к шагу {nextStep}: \"{nextDesc}\". Работайте ТОЛЬКО над этим шагом.",
435
- "exec.step_gate_last": "[✓] Шаг {step} завершён — это был последний шаг. Проверьте всё вместе и предоставьте финальный ответ.]",
374
+ "exec.stuck": "Застряли на шаге {stepId}. Текущий: {description}. Нет прогресса {iterations} итераций.",
375
+ "exec.stuck_recovery": "[Обнаружено застревание: {iterations} итераций без прогресса. Прекратите повторять тот же подход. Попробуйте принципиально другую стратегию: используйте другой инструмент (например, 'browser' для сайтов с JavaScript вместо 'web_browse'), разбейте задачу на подзадачи или объясните пользователю что пошло не так и спросите совета.]",
376
+ "exec.tool_errors": "Инструмент {tool} сбоил {count} раз. Предложите другой подход.",
377
+ "exec.tool_errors_recovery": "[Инструмент {tool} сбоил {count} раз. Прекратите использовать этот инструмент и попробуйте другой подход или объясните проблему пользователю.]",
378
+ "exec.repetitive_tool": "[Вы вызвали {tool} {count} раз подряд с одинаковыми аргументами и результатом. Прекратите повторять — попробуйте другой подход: создайте файлы, измените аргументы или объясните пользователю проблему.]",
379
+ "exec.consecutive_failures_recovery": "[{count} последовательных вызовов тулов завершились ошибкой. Вы ходите по кругу прекратите пытаться выполнить команды, которые постоянно падают. Объясните проблему пользователю или попробуйте совершенно другой подход: напишите файлы напрямую, используйте другой инструмент или разбейте задачу на части.]",
380
+ "exec.task_reminder": "[Напоминание о задаче: {task}. Не переключайтесь на посторонние задачи. Если не можете выполнить задачу объясните почему и спросите совета.]",
381
+ "exec.plan_warning": "[⚠ Внимание: вы работаете над шагом {step} — \"{description}\". Ваш вызов {tool} касается файлов, не упомянутых в текущем шаге. Сосредоточьтесь на текущем шаге или обновите план через plan update.]",
382
+ "exec.off_track": "[⚠ Вне плана: шаг {stepId} — \"{description}\", но вы используете {tool} для другого пути. Объясните или вернитесь.]",
436
383
  "exec.audit_pass": "[✓] Задача выполнена: {done}/{total} шагов, {files} файлов проверено",
437
384
  "exec.audit_fail": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов отсутствует",
438
385
  "exec.audit_fail_typecheck": "[✗] Задача не выполнена: {done}/{total} шагов, {missing} файлов отсутствует, ошибка typecheck: {typeError}",
439
386
  "exec.audit_incomplete": "[⚠ Финальная проверка не пройдена: {summary}. Задача НЕ завершена — продолжайте работу. Оставшиеся шаги: {steps}]",
440
- "exec.mass_edit_warning": "⚠️ План затрагивает {count} файлов — проверьте полный список перед продолжением.",
441
- "exec.escalation": "\n\n⚠️ Агент застрял на шаге {stepId} ({description}). Эскалация к пользователю — пожалуйста, подскажите как действовать.",
442
- "exec.hints": "\n[Подсказки]\n{hints}",
443
- "exec.file_rewrite_warning": "⚠️ Файл {file} был перезаписан {count} раз. Попробуйте другой подход — текущая стратегия исправлений не работает.",
444
387
  "hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
445
388
  "hall.short_response": "Слишком короткий или пустой ответ",
446
389
  "hall.repetitive": "Слишком повторяющийся ответ ({pct}% совпадение)",
447
390
  "hall.uncertainty": "Маркеры неопределённости: {markers}",
448
391
  "hall.unknown_paths": "Упомянутые файлы не найдены: {paths}",
449
- "hall.contradiction_llm": "Противоречит ранее принятому решению{reason}",
392
+ "hall.contradiction": "Противоречит предыдущему решению: \"{decision}\" в {location}",
450
393
  "hall.uncertainty_prefix": "\n\n[⚠️ Неопределённость] ",
451
394
  "browser.repeated_action": "Вы повторили то же действие ({action}) {threshold} раз. Попробуйте другой подход: используйте \"snapshot\" для перечитки страницы, другие номера элементов или другой URL.",
452
395
  "browser.unknown_action": "Неизвестное действие: {action}",
@@ -514,12 +457,5 @@
514
457
  "tool.remember.entry_required": "Требуется текст записи",
515
458
  "tool.recall.empty": "Ничего не найдено по \"{query}\"",
516
459
  "tool.recall.no_memory": "Память пуста",
517
- "tool.recall.search_results": "Результаты {category}:\n{results}",
518
- "ctx.compactions": "сжатий: {count}",
519
- "ctx.quality": "качество: {percent}%",
520
- "ctx.delta_pos": "контекст +{tokens}",
521
- "ctx.delta_neg": "контекст -{tokens} ↓",
522
- "ctx.delta_zero": "контекст ±0",
523
- "file.notfound_resolved": "Файл не найден: {path} (резолвится в {resolved})",
524
- "bash.echo_write_blocked": "Запись файлов через echo/printf ненадёжна в Windows cmd.exe (кавычки и многострочность ломаются). Используй инструмент write_file вместо этого (цель: {path})."
460
+ "tool.recall.search_results": "Результаты {category}:\n{results}"
525
461
  }
package/dist/index.js CHANGED
@@ -1,22 +1,22 @@
1
- export { DEFAULTS, loadConfig } from "./config/index";
2
- export { Logger } from "./logger/index";
3
- export { PromptBuilder } from "./core/index";
4
- export { OpenAICompatProvider, TokenCounter, parseChunks, OrchestratorClient, } from "./llm/index";
5
- export { ToolRegistry, ToolExecutor, readFileTool, writeFileTool, editFileTool, globTool, grepTool, listDirTool, createDirTool, deleteFileTool, moveFileTool, fileInfoTool, bashTool, subagentTool, webSearchTool, webFetchTool, webBrowseTool, createLoadSkillTool, pipelineRunTool, mcpCallTool, searchHistoryTool, registerAllTools, } from "./tools/index";
6
- export { MigrationDetector, BackupManager } from "./migration/index";
7
- import { MigrationDetector } from "./migration/detect";
8
- import { BackupManager } from "./migration/backup";
9
- import { t } from "./i18n/index";
10
- import { homedir } from "os";
11
- import { join } from "path";
1
+ export { DEFAULTS, loadConfig } from './config/index';
2
+ export { Logger } from './logger/index';
3
+ export { PromptBuilder } from './core/index';
4
+ export { OpenAICompatProvider, TokenCounter, parseChunks, OrchestratorClient } from './llm/index';
5
+ export { ToolRegistry, ToolExecutor, readFileTool, writeFileTool, editFileTool, globTool, grepTool, listDirTool, createDirTool, deleteFileTool, moveFileTool, fileInfoTool, bashTool, subagentTool, webSearchTool, webFetchTool, webBrowseTool, questionTool, approveTool, createLoadSkillTool, pipelineRunTool, mcpCallTool, searchHistoryTool, registerAllTools, } from './tools/index';
6
+ export { MigrationDetector, BackupManager } from './migration/index';
7
+ import { MigrationDetector } from './migration/detect';
8
+ import { BackupManager } from './migration/backup';
9
+ import { t } from './i18n/index';
10
+ import { homedir } from 'os';
11
+ import { join } from 'path';
12
12
  export function checkMigration(configDir) {
13
- const dir = configDir || join(homedir(), ".mma");
13
+ const dir = configDir || join(homedir(), '.mma');
14
14
  const detector = new MigrationDetector(dir);
15
15
  if (detector.needsMigration()) {
16
16
  const backup = new BackupManager(dir);
17
17
  backup.backupConfig();
18
18
  backup.backupAll();
19
19
  const summary = backup.getBackupSummary();
20
- console.log(t("migration.summary", { summary }));
20
+ console.log(t('migration.summary', { summary }));
21
21
  }
22
22
  }
@@ -1,6 +1,6 @@
1
1
  import { TokenCounter } from "./token-counter";
2
2
  import { t } from "../i18n/index";
3
- import { createRateLimiter, } from "../modules/security/rate-limiter";
3
+ import { createRateLimiter } from "../modules/security/rate-limiter";
4
4
  export class OpenAICompatProvider {
5
5
  model;
6
6
  contextWindow;
@@ -20,14 +20,14 @@ export class OpenAICompatProvider {
20
20
  };
21
21
  this.rateLimiter = createRateLimiter(config.rateLimits);
22
22
  }
23
- async *chat(messages, tools, signal) {
23
+ async *chat(messages, tools) {
24
24
  // Check rate limit before making request
25
25
  if (!this.rateLimiter.canMakeRequest()) {
26
26
  throw new Error(`Rate limit exceeded: ${this.rateLimiter.getConfig().maxRequestsPerMinute} requests per minute`);
27
27
  }
28
28
  // Record this request
29
29
  this.rateLimiter.recordRequest();
30
- const streamResult = this.doStream(messages, tools, signal);
30
+ const streamResult = this.doStream(messages, tools);
31
31
  let hasToolCall = false;
32
32
  let hasText = false;
33
33
  let reasoningAcc = "";
@@ -42,13 +42,13 @@ export class OpenAICompatProvider {
42
42
  yield chunk;
43
43
  }
44
44
  if (!hasToolCall && !hasText) {
45
- const fallback = await this.doNonStreaming(messages, tools, signal);
45
+ const fallback = await this.doNonStreaming(messages, tools);
46
46
  for (const chunk of fallback) {
47
47
  yield chunk;
48
48
  }
49
49
  }
50
50
  }
51
- async *doStream(messages, tools, signal) {
51
+ async *doStream(messages, tools) {
52
52
  const body = {
53
53
  model: this.model,
54
54
  messages,
@@ -75,24 +75,11 @@ export class OpenAICompatProvider {
75
75
  const controller = new AbortController();
76
76
  const totalTimeoutMs = 120000;
77
77
  const timeoutId = setTimeout(() => controller.abort(), totalTimeoutMs);
78
- const abortSignal = (() => {
79
- if (!signal)
80
- return controller.signal;
81
- try {
82
- return AbortSignal.any([controller.signal, signal]);
83
- }
84
- catch {
85
- signal.addEventListener("abort", () => controller.abort(), {
86
- once: true,
87
- });
88
- return controller.signal;
89
- }
90
- })();
91
78
  const response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
92
79
  method: "POST",
93
80
  headers,
94
81
  body: JSON.stringify(body),
95
- signal: abortSignal,
82
+ signal: controller.signal,
96
83
  });
97
84
  if (!response.ok) {
98
85
  clearTimeout(timeoutId);
@@ -195,7 +182,7 @@ export class OpenAICompatProvider {
195
182
  reader.releaseLock();
196
183
  }
197
184
  }
198
- async doNonStreaming(messages, tools, signal) {
185
+ async doNonStreaming(messages, tools) {
199
186
  const body = {
200
187
  model: this.model,
201
188
  messages,
@@ -224,7 +211,6 @@ export class OpenAICompatProvider {
224
211
  method: "POST",
225
212
  headers,
226
213
  body: JSON.stringify(body),
227
- signal,
228
214
  });
229
215
  if (!response.ok) {
230
216
  const errorText = await response.text();
@@ -324,7 +310,7 @@ export class OpenAICompatProvider {
324
310
  if (attempt < maxRetries) {
325
311
  const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
326
312
  const jitter = Math.random() * baseDelay * 0.1;
327
- await this.sleep(delay + jitter, init.signal ?? undefined);
313
+ await this.sleep(delay + jitter);
328
314
  }
329
315
  }
330
316
  throw lastError ?? new Error(t("error.llm_retries"));
@@ -332,22 +318,7 @@ export class OpenAICompatProvider {
332
318
  isRetryable(status) {
333
319
  return status === 429 || status >= 500;
334
320
  }
335
- sleep(ms, signal) {
336
- return new Promise((resolve, reject) => {
337
- if (signal?.aborted) {
338
- reject(new DOMException("Aborted", "AbortError"));
339
- return;
340
- }
341
- let timer;
342
- const onAbort = () => {
343
- clearTimeout(timer);
344
- reject(new DOMException("Aborted", "AbortError"));
345
- };
346
- timer = setTimeout(() => {
347
- signal?.removeEventListener("abort", onAbort);
348
- resolve();
349
- }, ms);
350
- signal?.addEventListener("abort", onAbort, { once: true });
351
- });
321
+ sleep(ms) {
322
+ return new Promise((resolve) => setTimeout(resolve, ms));
352
323
  }
353
324
  }
@@ -1,28 +1,14 @@
1
- import { appendFileSync, mkdirSync, existsSync } from "fs";
2
- import { join } from "path";
3
- import { sanitizeLogMessage } from "../modules/security/data-sanitizer";
4
- import { FileLogWriter } from "./file-log";
5
- import pc from "picocolors";
1
+ import { appendFileSync, mkdirSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { sanitizeLogMessage } from '../modules/security/data-sanitizer';
6
4
  const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
7
- const LEVEL_COLORS = {
8
- debug: (s) => pc.dim(s),
9
- info: (s) => s,
10
- warn: (s) => pc.yellow(s),
11
- error: (s) => pc.red(s),
12
- };
13
- function isColorEnabled() {
14
- return (!process.env.NO_COLOR && !process.env.CI && process.stdout.isTTY === true);
15
- }
16
5
  export class Logger {
17
6
  level;
18
7
  prefix;
19
8
  logDir = null;
20
- sessionDir = null;
21
- fileLog;
22
- constructor(level = "info", prefix = "") {
9
+ constructor(level = 'info', prefix = '') {
23
10
  this.level = level;
24
11
  this.prefix = prefix;
25
- this.fileLog = new FileLogWriter();
26
12
  }
27
13
  setLevel(level) {
28
14
  this.level = level;
@@ -32,66 +18,24 @@ export class Logger {
32
18
  if (!existsSync(dir)) {
33
19
  mkdirSync(dir, { recursive: true });
34
20
  }
35
- this.fileLog.setLogDir(dir);
36
- }
37
- setSessionDir(dir) {
38
- this.sessionDir = dir;
39
- if (!existsSync(dir)) {
40
- mkdirSync(dir, { recursive: true });
41
- }
42
- }
43
- clearSessionDir() {
44
- this.sessionDir = null;
45
- }
46
- /** Open a per-session `.log` file (legacy v1 behavior). */
47
- initSessionLog(sessionId) {
48
- this.fileLog.initSessionLog(sessionId);
49
- }
50
- /** Fall back to daily log files when no session is active. */
51
- closeSessionLog() {
52
- this.fileLog.closeSessionLog();
53
- }
54
- getLogPath() {
55
- return this.fileLog.getLogPath();
56
- }
57
- cleanupOldLogs(maxDays, maxFiles) {
58
- this.fileLog.cleanupOldLogs(maxDays, maxFiles);
59
- }
60
- // --- Tagged helpers (legacy v1 API) ---
61
- logLLMRequest(model, messagesCount, promptPreview, caller) {
62
- this.fileLog.logLLMRequest(model, messagesCount, promptPreview, caller);
63
- }
64
- logLLMResponse(model, responseLength, genTimeMs, error, caller) {
65
- this.fileLog.logLLMResponse(model, responseLength, genTimeMs, error, caller);
66
- }
67
- logToolCall(tool, preview, result) {
68
- this.fileLog.logToolCall(tool, preview, result);
69
- }
70
- logToolOutput(tool, output, exitCode) {
71
- this.fileLog.logToolOutput(tool, output, exitCode);
72
- }
73
- logREPL(tag, content) {
74
- this.fileLog.logREPL(tag, content);
75
21
  }
76
22
  child(prefix) {
77
23
  const childLogger = new Logger(this.level, this.prefix ? `${this.prefix}:${prefix}` : prefix);
78
24
  if (this.logDir)
79
25
  childLogger.setLogDir(this.logDir);
80
- if (this.sessionDir)
81
- childLogger.setSessionDir(this.sessionDir);
82
26
  return childLogger;
83
27
  }
84
28
  debug(msg, meta) {
85
- this.log("debug", msg, meta);
29
+ this.log('debug', msg, meta);
86
30
  }
87
31
  info(msg, meta) {
88
- this.log("info", msg, meta);
32
+ this.log('info', msg, meta);
89
33
  }
90
34
  warn(msg, meta) {
91
- this.log("warn", msg, meta);
35
+ this.log('warn', msg, meta);
92
36
  }
93
37
  error(msg, meta) {
94
- this.log("error", msg, meta);
38
+ this.log('error', msg, meta);
95
39
  }
96
40
  log(level, msg, meta) {
97
41
  if (LEVELS[level] < LEVELS[this.level])
@@ -100,26 +44,15 @@ export class Logger {
100
44
  const sanitizedMsg = sanitizeLogMessage(msg);
101
45
  const sanitizedMeta = meta ? this.sanitizeMeta(meta) : undefined;
102
46
  const ts = new Date().toISOString();
103
- const prefix = this.prefix ? ` [${this.prefix}]` : "";
104
- const metaStr = sanitizedMeta ? ` ${JSON.stringify(sanitizedMeta)}` : "";
47
+ const prefix = this.prefix ? ` [${this.prefix}]` : '';
48
+ const metaStr = sanitizedMeta ? ` ${JSON.stringify(sanitizedMeta)}` : '';
105
49
  const line = `[${level.toUpperCase()}]${prefix} ${ts} — ${sanitizedMsg}${metaStr}`;
106
- console.log(isColorEnabled() ? LEVEL_COLORS[level](line) : line);
107
- // Legacy v1 behavior: also append to the tagged `.log` file (session or daily).
108
- this.fileLog.log(level.toUpperCase(), this.prefix || "MMA", `${ts} — ${sanitizedMsg}${metaStr}`);
109
- const logTarget = this.sessionDir ?? this.logDir;
110
- if (logTarget) {
50
+ console.log(line);
51
+ if (this.logDir) {
111
52
  try {
112
- appendFileSync(join(logTarget, "app.jsonl"), JSON.stringify({
113
- level,
114
- ts,
115
- prefix: this.prefix,
116
- msg: sanitizedMsg,
117
- meta: sanitizedMeta ?? null,
118
- }) + "\n", "utf-8");
119
- }
120
- catch {
121
- /* file logging is best-effort */
53
+ appendFileSync(join(this.logDir, 'app.jsonl'), JSON.stringify({ level, ts, prefix: this.prefix, msg: sanitizedMsg, meta: sanitizedMeta ?? null }) + '\n', 'utf-8');
122
54
  }
55
+ catch { /* file logging is best-effort */ }
123
56
  }
124
57
  }
125
58
  /**
@@ -128,10 +61,10 @@ export class Logger {
128
61
  sanitizeMeta(meta) {
129
62
  const sanitized = {};
130
63
  for (const [key, value] of Object.entries(meta)) {
131
- if (typeof value === "string") {
64
+ if (typeof value === 'string') {
132
65
  sanitized[key] = sanitizeLogMessage(value);
133
66
  }
134
- else if (typeof value === "object" && value !== null) {
67
+ else if (typeof value === 'object' && value !== null) {
135
68
  sanitized[key] = this.sanitizeMeta(value);
136
69
  }
137
70
  else {