micro-models-agent 2.8.0 → 2.9.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 +14 -0
- package/dist/assets/styles.css +2 -0
- package/dist/i18n/en.json +11 -4
- package/dist/i18n/ru.json +11 -4
- package/dist/main.js +447 -117
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ 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
|
+
## [2.9.0] - 2026-09-26
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`process_wait` tool** (`src/tools/bash.ts`, wired into `manage_hint`, `bash`/`process_log` descriptions and the hidden-tool redirect list): a single blocking wait on a background process (`id`/`timeoutMs`/`tail`) that returns the buffered tail when it exits — replaces `process_log` polling loops and `ping`/`sleep` delays.
|
|
12
|
+
- **Result memoization** (`src/tools/executor.ts`, `src/tools/bash.ts`): read-only tools (`read_file`/`list_dir`/`glob`/`grep`/`file_info`) plus single, read-only bash commands are cached per session (120s TTL), wiped by any mutation. Repeats of the same read no longer re-execute.
|
|
13
|
+
- **Success-claim check** (`src/modules/hallucination/success-claim.ts`, wired into `src/core/agent/hallucination-gate.ts`): a strong completion claim ("done"/"published") together with error signals in the last 10 tool outputs and no hedging triggers one bounded re-check (`MAX_CLAIM_CHECK_RETRIES = 1`, `hall.claim_contradiction`), then fails open — never a block. Deterministic (regex + tool success flags, no LLM), Cyrillic-aware.
|
|
14
|
+
- **`session export --compact`** (`src/cli/commands.ts`, `src/modules/session/export.ts`): human-readable export without tool-result dumps.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **repeats made visible**: `getStuckReason` now surfaces repetitive calls (previously a warning key with no logged reason); `process_log` poll loops point at `process_wait`; the quiet renderer folds consecutive identical action lines into a single `xN` line.
|
|
19
|
+
- **web think-toggle alignment** (`src/modules/webui/assets/styles.css`, web `action.hide/show` i18n): dropped the `▾`/`▸` glyphs from the toggle labels in favor of an inline-flex SVG slot, fixing the alignment.
|
|
20
|
+
|
|
7
21
|
## [2.8.0] - 2026-09-26
|
|
8
22
|
|
|
9
23
|
### Added
|
package/dist/assets/styles.css
CHANGED
|
@@ -239,6 +239,8 @@ body {
|
|
|
239
239
|
.ans { color: var(--ans); white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; }
|
|
240
240
|
.think-h { color: var(--think); cursor: pointer; user-select: none; display: flex; gap: 8px; align-items: baseline; font-size: 12.5px; }
|
|
241
241
|
.think-h .dur, .think-h .tog { color: var(--dim-b); }
|
|
242
|
+
.think-h .tog { display: inline-flex; align-items: center; gap: 4px; }
|
|
243
|
+
.think-h .tog .svg-icon { width: 12px; height: 12px; }
|
|
242
244
|
.think-h .tog:hover { color: var(--dim-a); }
|
|
243
245
|
.think-body { color: var(--dim-c); padding-left: 18px; white-space: pre-wrap; font-size: 12.5px; overflow-wrap: anywhere; }
|
|
244
246
|
.think-body.hidden { display: none; }
|
package/dist/i18n/en.json
CHANGED
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
"tool.interactive_disabled": "Interactive tool is disabled in exit-on-complete mode. Proceed without asking the user.",
|
|
201
201
|
"proc.started": "Started background process {id} (PID {pid}).\nCommand: {command}",
|
|
202
202
|
"proc.promoted_hint": "Command still running after {ms} ms — moved to the background",
|
|
203
|
-
"proc.manage_hint": "Check output: process_log id={id}. Stop it: process_kill id={id}. List all: process_list.",
|
|
203
|
+
"proc.manage_hint": "Check output: process_log id={id}. Wait for completion: process_wait id={id}. Stop it: process_kill id={id}. List all: process_list.",
|
|
204
204
|
"proc.output_preview": "First output:\n{lines}",
|
|
205
205
|
"proc.none": "No background processes running.",
|
|
206
206
|
"proc.not_found": "Process not found: {id}",
|
|
@@ -215,9 +215,13 @@
|
|
|
215
215
|
"proc.status_running": "running",
|
|
216
216
|
"proc.status_exited": "exited",
|
|
217
217
|
"proc.status_killed": "killed",
|
|
218
|
+
"proc.wait_exited": "Process {id} {status} after {ms} ms with exit code {code}.",
|
|
219
|
+
"proc.wait_running": "Process {id} still running after {ms} ms.",
|
|
220
|
+
"proc.wait_hint": "Wait again: process_wait id={id}. Check output: process_log id={id}. Stop it: process_kill id={id}.",
|
|
218
221
|
"tool.friendly.process_list": "Listing background processes",
|
|
219
222
|
"tool.friendly.process_log": "Process output",
|
|
220
223
|
"tool.friendly.process_kill": "Stopping process",
|
|
224
|
+
"tool.friendly.process_wait": "Waiting for process",
|
|
221
225
|
"plan.no_steps": "No plan steps specified. Provide concrete steps with files and commands.",
|
|
222
226
|
"plan.created": "Plan created: {title} ({steps} steps)",
|
|
223
227
|
"plan.coverage_warning": "Plan may be missing required files from the task: {missing}. Add steps covering them.",
|
|
@@ -419,6 +423,7 @@
|
|
|
419
423
|
"cli.export_format": "Output format: md|json|jsonl (default md)",
|
|
420
424
|
"cli.export_out": "Output path ('-' for stdout)",
|
|
421
425
|
"cli.export_no_redact": "Do not redact secrets",
|
|
426
|
+
"cli.export_compact": "Omit tool result bodies (compact readable export)",
|
|
422
427
|
"cli.first_run": "First run detected. Running setup wizard...",
|
|
423
428
|
"cli.first_run_oneshot": "No configuration found — running with defaults. Run `mma` (no arguments) once to set up the wizard.",
|
|
424
429
|
"cli.web_setup_required": "No configuration found — opening the browser setup wizard.",
|
|
@@ -623,7 +628,7 @@
|
|
|
623
628
|
"repl.help_aliases": "Aliases: {names}",
|
|
624
629
|
"repl.help_examples": "Examples:",
|
|
625
630
|
"repl.export": "Export a session to a file",
|
|
626
|
-
"repl.export_usage": "Usage: /export [<id|name>] [--format md|json|jsonl] [--out <path>] [--no-redact]",
|
|
631
|
+
"repl.export_usage": "Usage: /export [<id|name>] [--format md|json|jsonl] [--out <path>] [--no-redact] [--compact]",
|
|
627
632
|
"config.header": "Managed settings",
|
|
628
633
|
"config.col_setting": "Setting",
|
|
629
634
|
"config.col_value": "Value",
|
|
@@ -751,6 +756,7 @@
|
|
|
751
756
|
"exec.tool_errors": "Tool {tool} failed {count} times. Consider using a different tool.",
|
|
752
757
|
"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.",
|
|
753
758
|
"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.",
|
|
759
|
+
"exec.repetitive_process_log_hint": "You are polling a background process — call process_wait once instead of polling process_log.",
|
|
754
760
|
"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.",
|
|
755
761
|
"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",
|
|
756
762
|
"exec.bash_flailing": "bash failed in {fails} of the last {window} attempts.",
|
|
@@ -793,6 +799,7 @@
|
|
|
793
799
|
"hall.uncertainty": "Uncertainty markers: {markers}",
|
|
794
800
|
"hall.unknown_paths": "Mentioned file paths not found in known files: {paths}",
|
|
795
801
|
"hall.contradiction_llm": "Contradicts an earlier decision{reason}",
|
|
802
|
+
"hall.claim_contradiction": "The answer claims success, but a recent tool result indicates a problem ({tool}: {snippet}). Either verify and fix, then re-run the check — or soften the claim to match the evidence (e.g. staged, will appear shortly).",
|
|
796
803
|
"hall.uncertainty_prefix": "\n\n[⚠️ Uncertainty] ",
|
|
797
804
|
"browser.repeated_action": "You have repeated the same action ({action}) {threshold} times. Try a different approach: use \"snapshot\" to re-read the page, try different element numbers, or navigate to a different URL.",
|
|
798
805
|
"browser.unknown_action": "Unknown action: {action}",
|
|
@@ -1218,8 +1225,8 @@
|
|
|
1218
1225
|
"web.selectors.current": "current",
|
|
1219
1226
|
"web.selectors.context_prompt": "context window (tokens)",
|
|
1220
1227
|
"web.action.thinking": "thinking",
|
|
1221
|
-
"web.action.hide": "
|
|
1222
|
-
"web.action.show": "
|
|
1228
|
+
"web.action.hide": "hide",
|
|
1229
|
+
"web.action.show": "show",
|
|
1223
1230
|
"web.action.actions_one": "{count} action",
|
|
1224
1231
|
"web.action.actions_few": "{count} actions",
|
|
1225
1232
|
"web.action.actions_many": "{count} actions",
|
package/dist/i18n/ru.json
CHANGED
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
"tool.interactive_disabled": "Интерактивный инструмент отключён в режиме exit-on-complete. Продолжай без вопроса пользователю.",
|
|
199
199
|
"proc.started": "Фоновый процесс запущен: {id} (PID {pid}).\nКоманда: {command}",
|
|
200
200
|
"proc.promoted_hint": "Команда всё ещё выполняется через {ms} мс — переведена в фоновый режим",
|
|
201
|
-
"proc.manage_hint": "Проверить вывод: process_log id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
|
|
201
|
+
"proc.manage_hint": "Проверить вывод: process_log id={id}. Дождаться завершения: process_wait id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
|
|
202
202
|
"proc.output_preview": "Первые строки вывода:\n{lines}",
|
|
203
203
|
"proc.none": "Фоновых процессов нет.",
|
|
204
204
|
"proc.not_found": "Процесс не найден: {id}",
|
|
@@ -213,9 +213,13 @@
|
|
|
213
213
|
"proc.status_running": "работает",
|
|
214
214
|
"proc.status_exited": "завершён",
|
|
215
215
|
"proc.status_killed": "остановлен",
|
|
216
|
+
"proc.wait_exited": "Процесс {id} {status} за {ms} мс с кодом {code}.",
|
|
217
|
+
"proc.wait_running": "Процесс {id} всё ещё работает (ждали {ms} мс).",
|
|
218
|
+
"proc.wait_hint": "Подождать ещё: process_wait id={id}. Проверить вывод: process_log id={id}. Остановить: process_kill id={id}.",
|
|
216
219
|
"tool.friendly.process_list": "Список фоновых процессов",
|
|
217
220
|
"tool.friendly.process_log": "Вывод процесса",
|
|
218
221
|
"tool.friendly.process_kill": "Остановка процесса",
|
|
222
|
+
"tool.friendly.process_wait": "Ожидание процесса",
|
|
219
223
|
"plan.no_steps": "Не указаны шаги плана. Укажите конкретные шаги с файлами и командами.",
|
|
220
224
|
"plan.created": "План создан: {title} ({steps} шагов)",
|
|
221
225
|
"plan.coverage_warning": "План может не покрывать требуемые файлы из постановки: {missing}. Добавьте шаги, покрывающие их.",
|
|
@@ -413,6 +417,7 @@
|
|
|
413
417
|
"cli.export_format": "Формат вывода: md|json|jsonl (по умолчанию md)",
|
|
414
418
|
"cli.export_out": "Путь вывода ('-' для stdout)",
|
|
415
419
|
"cli.export_no_redact": "Не скрывать секреты",
|
|
420
|
+
"cli.export_compact": "Пропустить тела результатов тулов (компактный читаемый экспорт)",
|
|
416
421
|
"cli.first_run": "Первый запуск. Запускаем мастер настройки...",
|
|
417
422
|
"cli.first_run_oneshot": "Конфигурация не найдена — запуск с настройками по умолчанию. Запустите `mma` без аргументов один раз для настройки.",
|
|
418
423
|
"cli.web_setup_required": "Конфигурация не найдена — открывается мастер настройки в браузере.",
|
|
@@ -617,7 +622,7 @@
|
|
|
617
622
|
"repl.help_aliases": "Алиасы: {names}",
|
|
618
623
|
"repl.help_examples": "Примеры:",
|
|
619
624
|
"repl.export": "Экспортировать сессию в файл",
|
|
620
|
-
"repl.export_usage": "Использование: /export [<id|имя>] [--format md|json|jsonl] [--out <путь>] [--no-redact]",
|
|
625
|
+
"repl.export_usage": "Использование: /export [<id|имя>] [--format md|json|jsonl] [--out <путь>] [--no-redact] [--compact]",
|
|
621
626
|
"config.header": "Управляемые настройки",
|
|
622
627
|
"config.col_setting": "Настройка",
|
|
623
628
|
"config.col_value": "Значение",
|
|
@@ -749,6 +754,7 @@
|
|
|
749
754
|
"exec.tool_errors": "Инструмент {tool} упал {count} раз. Попробуйте другой инструмент.",
|
|
750
755
|
"exec.tool_errors_recovery": "Инструмент {tool} упал {count} раз подряд. Попробуйте альтернативу: создайте файлы напрямую через write_file, используйте другую команду, или пропустите этот шаг через plan update step=N status=skipped с пометкой почему.",
|
|
751
756
|
"exec.repetitive_tool": "Инструмент {tool} вызван {count} раз с одинаковыми аргументами и результатом. Попробуйте другой подход — создайте файлы напрямую, измените аргументы или проверьте статус процесса через process_log.",
|
|
757
|
+
"exec.repetitive_process_log_hint": "Вы опрашиваете фоновый процесс в цикле — вызовите process_wait один раз вместо опроса через process_log.",
|
|
752
758
|
"exec.consecutive_failures_recovery": "{count} инструментов подряд упали. Создавайте файлы напрямую через write_file вместо команд терминала. Проверьте что зависимости установлены (npm install). Не запускайте сборку/тесты пока не созданы все файлы.",
|
|
753
759
|
"exec.bash_flailing_recovery": "bash упал в {fails} из последних {window} попыток. Хватит использовать шелл-команды для файловых операций на этой машине — они не работают. Используй специализированные инструменты: read_file (в заголовке число строк), write_file, edit_file, file_info (размер файла), glob/grep для поиска",
|
|
754
760
|
"exec.bash_flailing": "bash упал в {fails} из последних {window} попыток.",
|
|
@@ -791,6 +797,7 @@
|
|
|
791
797
|
"hall.uncertainty": "Маркеры неопределённости: {markers}",
|
|
792
798
|
"hall.unknown_paths": "Упомянутые файлы не найдены: {paths}",
|
|
793
799
|
"hall.contradiction_llm": "Противоречит ранее принятому решению{reason}",
|
|
800
|
+
"hall.claim_contradiction": "Ответ заявляет об успехе, но свежий результат тула указывает на проблему ({tool}: {snippet}). Либо проверь и исправь, затем перезапусти проверку — либо смягчи формулировку под факты (например, застейджено, появится в ближайшее время).",
|
|
794
801
|
"hall.uncertainty_prefix": "\n\n[⚠️ Неопределённость] ",
|
|
795
802
|
"browser.repeated_action": "Вы повторили то же действие ({action}) {threshold} раз. Попробуйте другой подход: используйте \"snapshot\" для перечитки страницы, другие номера элементов или другой URL.",
|
|
796
803
|
"browser.unknown_action": "Неизвестное действие: {action}",
|
|
@@ -1194,8 +1201,8 @@
|
|
|
1194
1201
|
"web.selectors.current": "текущее",
|
|
1195
1202
|
"web.selectors.context_prompt": "контекстное окно (токены)",
|
|
1196
1203
|
"web.action.thinking": "думаю",
|
|
1197
|
-
"web.action.hide": "
|
|
1198
|
-
"web.action.show": "
|
|
1204
|
+
"web.action.hide": "скрыть",
|
|
1205
|
+
"web.action.show": "показать",
|
|
1199
1206
|
"web.action.actions_one": "{count} действие",
|
|
1200
1207
|
"web.action.actions_few": "{count} действия",
|
|
1201
1208
|
"web.action.actions_many": "{count} действий",
|
package/dist/main.js
CHANGED
|
@@ -2671,7 +2671,7 @@ Iterations: {iterations}`,
|
|
|
2671
2671
|
"proc.started": `Started background process {id} (PID {pid}).
|
|
2672
2672
|
Command: {command}`,
|
|
2673
2673
|
"proc.promoted_hint": "Command still running after {ms} ms — moved to the background",
|
|
2674
|
-
"proc.manage_hint": "Check output: process_log id={id}. Stop it: process_kill id={id}. List all: process_list.",
|
|
2674
|
+
"proc.manage_hint": "Check output: process_log id={id}. Wait for completion: process_wait id={id}. Stop it: process_kill id={id}. List all: process_list.",
|
|
2675
2675
|
"proc.output_preview": `First output:
|
|
2676
2676
|
{lines}`,
|
|
2677
2677
|
"proc.none": "No background processes running.",
|
|
@@ -2687,9 +2687,13 @@ Command: {command}`,
|
|
|
2687
2687
|
"proc.status_running": "running",
|
|
2688
2688
|
"proc.status_exited": "exited",
|
|
2689
2689
|
"proc.status_killed": "killed",
|
|
2690
|
+
"proc.wait_exited": "Process {id} {status} after {ms} ms with exit code {code}.",
|
|
2691
|
+
"proc.wait_running": "Process {id} still running after {ms} ms.",
|
|
2692
|
+
"proc.wait_hint": "Wait again: process_wait id={id}. Check output: process_log id={id}. Stop it: process_kill id={id}.",
|
|
2690
2693
|
"tool.friendly.process_list": "Listing background processes",
|
|
2691
2694
|
"tool.friendly.process_log": "Process output",
|
|
2692
2695
|
"tool.friendly.process_kill": "Stopping process",
|
|
2696
|
+
"tool.friendly.process_wait": "Waiting for process",
|
|
2693
2697
|
"plan.no_steps": "No plan steps specified. Provide concrete steps with files and commands.",
|
|
2694
2698
|
"plan.created": "Plan created: {title} ({steps} steps)",
|
|
2695
2699
|
"plan.coverage_warning": "Plan may be missing required files from the task: {missing}. Add steps covering them.",
|
|
@@ -2893,6 +2897,7 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
|
|
|
2893
2897
|
"cli.export_format": "Output format: md|json|jsonl (default md)",
|
|
2894
2898
|
"cli.export_out": "Output path ('-' for stdout)",
|
|
2895
2899
|
"cli.export_no_redact": "Do not redact secrets",
|
|
2900
|
+
"cli.export_compact": "Omit tool result bodies (compact readable export)",
|
|
2896
2901
|
"cli.first_run": "First run detected. Running setup wizard...",
|
|
2897
2902
|
"cli.first_run_oneshot": "No configuration found — running with defaults. Run `mma` (no arguments) once to set up the wizard.",
|
|
2898
2903
|
"cli.web_setup_required": "No configuration found — opening the browser setup wizard.",
|
|
@@ -3105,7 +3110,7 @@ Excluded blocks: {count}`,
|
|
|
3105
3110
|
"repl.help_aliases": "Aliases: {names}",
|
|
3106
3111
|
"repl.help_examples": "Examples:",
|
|
3107
3112
|
"repl.export": "Export a session to a file",
|
|
3108
|
-
"repl.export_usage": "Usage: /export [<id|name>] [--format md|json|jsonl] [--out <path>] [--no-redact]",
|
|
3113
|
+
"repl.export_usage": "Usage: /export [<id|name>] [--format md|json|jsonl] [--out <path>] [--no-redact] [--compact]",
|
|
3109
3114
|
"config.header": "Managed settings",
|
|
3110
3115
|
"config.col_setting": "Setting",
|
|
3111
3116
|
"config.col_value": "Value",
|
|
@@ -3248,6 +3253,7 @@ Use this knowledge to answer the user's question.`,
|
|
|
3248
3253
|
"exec.tool_errors": "Tool {tool} failed {count} times. Consider using a different tool.",
|
|
3249
3254
|
"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.",
|
|
3250
3255
|
"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.",
|
|
3256
|
+
"exec.repetitive_process_log_hint": "You are polling a background process — call process_wait once instead of polling process_log.",
|
|
3251
3257
|
"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.",
|
|
3252
3258
|
"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",
|
|
3253
3259
|
"exec.bash_flailing": "bash failed in {fails} of the last {window} attempts.",
|
|
@@ -3296,6 +3302,7 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
3296
3302
|
"hall.uncertainty": "Uncertainty markers: {markers}",
|
|
3297
3303
|
"hall.unknown_paths": "Mentioned file paths not found in known files: {paths}",
|
|
3298
3304
|
"hall.contradiction_llm": "Contradicts an earlier decision{reason}",
|
|
3305
|
+
"hall.claim_contradiction": "The answer claims success, but a recent tool result indicates a problem ({tool}: {snippet}). Either verify and fix, then re-run the check — or soften the claim to match the evidence (e.g. staged, will appear shortly).",
|
|
3299
3306
|
"hall.uncertainty_prefix": `
|
|
3300
3307
|
|
|
3301
3308
|
[⚠️ Uncertainty] `,
|
|
@@ -3737,8 +3744,8 @@ Updated: {updatedAt}`,
|
|
|
3737
3744
|
"web.selectors.current": "current",
|
|
3738
3745
|
"web.selectors.context_prompt": "context window (tokens)",
|
|
3739
3746
|
"web.action.thinking": "thinking",
|
|
3740
|
-
"web.action.hide": "
|
|
3741
|
-
"web.action.show": "
|
|
3747
|
+
"web.action.hide": "hide",
|
|
3748
|
+
"web.action.show": "show",
|
|
3742
3749
|
"web.action.actions_one": "{count} action",
|
|
3743
3750
|
"web.action.actions_few": "{count} actions",
|
|
3744
3751
|
"web.action.actions_many": "{count} actions",
|
|
@@ -4094,7 +4101,7 @@ var init_ru = __esm(() => {
|
|
|
4094
4101
|
"proc.started": `Фоновый процесс запущен: {id} (PID {pid}).
|
|
4095
4102
|
Команда: {command}`,
|
|
4096
4103
|
"proc.promoted_hint": "Команда всё ещё выполняется через {ms} мс — переведена в фоновый режим",
|
|
4097
|
-
"proc.manage_hint": "Проверить вывод: process_log id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
|
|
4104
|
+
"proc.manage_hint": "Проверить вывод: process_log id={id}. Дождаться завершения: process_wait id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
|
|
4098
4105
|
"proc.output_preview": `Первые строки вывода:
|
|
4099
4106
|
{lines}`,
|
|
4100
4107
|
"proc.none": "Фоновых процессов нет.",
|
|
@@ -4110,9 +4117,13 @@ var init_ru = __esm(() => {
|
|
|
4110
4117
|
"proc.status_running": "работает",
|
|
4111
4118
|
"proc.status_exited": "завершён",
|
|
4112
4119
|
"proc.status_killed": "остановлен",
|
|
4120
|
+
"proc.wait_exited": "Процесс {id} {status} за {ms} мс с кодом {code}.",
|
|
4121
|
+
"proc.wait_running": "Процесс {id} всё ещё работает (ждали {ms} мс).",
|
|
4122
|
+
"proc.wait_hint": "Подождать ещё: process_wait id={id}. Проверить вывод: process_log id={id}. Остановить: process_kill id={id}.",
|
|
4113
4123
|
"tool.friendly.process_list": "Список фоновых процессов",
|
|
4114
4124
|
"tool.friendly.process_log": "Вывод процесса",
|
|
4115
4125
|
"tool.friendly.process_kill": "Остановка процесса",
|
|
4126
|
+
"tool.friendly.process_wait": "Ожидание процесса",
|
|
4116
4127
|
"plan.no_steps": "Не указаны шаги плана. Укажите конкретные шаги с файлами и командами.",
|
|
4117
4128
|
"plan.created": "План создан: {title} ({steps} шагов)",
|
|
4118
4129
|
"plan.coverage_warning": "План может не покрывать требуемые файлы из постановки: {missing}. Добавьте шаги, покрывающие их.",
|
|
@@ -4312,6 +4323,7 @@ var init_ru = __esm(() => {
|
|
|
4312
4323
|
"cli.export_format": "Формат вывода: md|json|jsonl (по умолчанию md)",
|
|
4313
4324
|
"cli.export_out": "Путь вывода ('-' для stdout)",
|
|
4314
4325
|
"cli.export_no_redact": "Не скрывать секреты",
|
|
4326
|
+
"cli.export_compact": "Пропустить тела результатов тулов (компактный читаемый экспорт)",
|
|
4315
4327
|
"cli.first_run": "Первый запуск. Запускаем мастер настройки...",
|
|
4316
4328
|
"cli.first_run_oneshot": "Конфигурация не найдена — запуск с настройками по умолчанию. Запустите `mma` без аргументов один раз для настройки.",
|
|
4317
4329
|
"cli.web_setup_required": "Конфигурация не найдена — открывается мастер настройки в браузере.",
|
|
@@ -4524,7 +4536,7 @@ var init_ru = __esm(() => {
|
|
|
4524
4536
|
"repl.help_aliases": "Алиасы: {names}",
|
|
4525
4537
|
"repl.help_examples": "Примеры:",
|
|
4526
4538
|
"repl.export": "Экспортировать сессию в файл",
|
|
4527
|
-
"repl.export_usage": "Использование: /export [<id|имя>] [--format md|json|jsonl] [--out <путь>] [--no-redact]",
|
|
4539
|
+
"repl.export_usage": "Использование: /export [<id|имя>] [--format md|json|jsonl] [--out <путь>] [--no-redact] [--compact]",
|
|
4528
4540
|
"config.header": "Управляемые настройки",
|
|
4529
4541
|
"config.col_setting": "Настройка",
|
|
4530
4542
|
"config.col_value": "Значение",
|
|
@@ -4671,6 +4683,7 @@ var init_ru = __esm(() => {
|
|
|
4671
4683
|
"exec.tool_errors": "Инструмент {tool} упал {count} раз. Попробуйте другой инструмент.",
|
|
4672
4684
|
"exec.tool_errors_recovery": "Инструмент {tool} упал {count} раз подряд. Попробуйте альтернативу: создайте файлы напрямую через write_file, используйте другую команду, или пропустите этот шаг через plan update step=N status=skipped с пометкой почему.",
|
|
4673
4685
|
"exec.repetitive_tool": "Инструмент {tool} вызван {count} раз с одинаковыми аргументами и результатом. Попробуйте другой подход — создайте файлы напрямую, измените аргументы или проверьте статус процесса через process_log.",
|
|
4686
|
+
"exec.repetitive_process_log_hint": "Вы опрашиваете фоновый процесс в цикле — вызовите process_wait один раз вместо опроса через process_log.",
|
|
4674
4687
|
"exec.consecutive_failures_recovery": "{count} инструментов подряд упали. Создавайте файлы напрямую через write_file вместо команд терминала. Проверьте что зависимости установлены (npm install). Не запускайте сборку/тесты пока не созданы все файлы.",
|
|
4675
4688
|
"exec.bash_flailing_recovery": "bash упал в {fails} из последних {window} попыток. Хватит использовать шелл-команды для файловых операций на этой машине — они не работают. Используй специализированные инструменты: read_file (в заголовке число строк), write_file, edit_file, file_info (размер файла), glob/grep для поиска",
|
|
4676
4689
|
"exec.bash_flailing": "bash упал в {fails} из последних {window} попыток.",
|
|
@@ -4719,6 +4732,7 @@ var init_ru = __esm(() => {
|
|
|
4719
4732
|
"hall.uncertainty": "Маркеры неопределённости: {markers}",
|
|
4720
4733
|
"hall.unknown_paths": "Упомянутые файлы не найдены: {paths}",
|
|
4721
4734
|
"hall.contradiction_llm": "Противоречит ранее принятому решению{reason}",
|
|
4735
|
+
"hall.claim_contradiction": "Ответ заявляет об успехе, но свежий результат тула указывает на проблему ({tool}: {snippet}). Либо проверь и исправь, затем перезапусти проверку — либо смягчи формулировку под факты (например, застейджено, появится в ближайшее время).",
|
|
4722
4736
|
"hall.uncertainty_prefix": `
|
|
4723
4737
|
|
|
4724
4738
|
[⚠️ Неопределённость] `,
|
|
@@ -5139,8 +5153,8 @@ var init_ru = __esm(() => {
|
|
|
5139
5153
|
"web.selectors.current": "текущее",
|
|
5140
5154
|
"web.selectors.context_prompt": "контекстное окно (токены)",
|
|
5141
5155
|
"web.action.thinking": "думаю",
|
|
5142
|
-
"web.action.hide": "
|
|
5143
|
-
"web.action.show": "
|
|
5156
|
+
"web.action.hide": "скрыть",
|
|
5157
|
+
"web.action.show": "показать",
|
|
5144
5158
|
"web.action.actions_one": "{count} действие",
|
|
5145
5159
|
"web.action.actions_few": "{count} действия",
|
|
5146
5160
|
"web.action.actions_many": "{count} действий",
|
|
@@ -9154,10 +9168,21 @@ var init_runner = __esm(() => {
|
|
|
9154
9168
|
});
|
|
9155
9169
|
|
|
9156
9170
|
// src/tools/executor.ts
|
|
9171
|
+
function stableArgsKey(value) {
|
|
9172
|
+
if (Array.isArray(value))
|
|
9173
|
+
return value.map(stableArgsKey);
|
|
9174
|
+
if (value && typeof value === "object") {
|
|
9175
|
+
const entries = Object.keys(value).sort().map((k) => [k, stableArgsKey(value[k])]);
|
|
9176
|
+
return Object.fromEntries(entries);
|
|
9177
|
+
}
|
|
9178
|
+
return value;
|
|
9179
|
+
}
|
|
9180
|
+
|
|
9157
9181
|
class ToolExecutor {
|
|
9158
9182
|
registry;
|
|
9159
9183
|
ctx;
|
|
9160
9184
|
pluginManager;
|
|
9185
|
+
memo = new Map;
|
|
9161
9186
|
constructor(registry, ctx, pluginManager) {
|
|
9162
9187
|
this.registry = registry;
|
|
9163
9188
|
this.ctx = ctx;
|
|
@@ -9180,6 +9205,31 @@ class ToolExecutor {
|
|
|
9180
9205
|
hasTool(name) {
|
|
9181
9206
|
return this.registry.has(name);
|
|
9182
9207
|
}
|
|
9208
|
+
memoKey(name, args) {
|
|
9209
|
+
return `${this.ctx.sessionId ?? ""}::${name}::${JSON.stringify(stableArgsKey(args))}`;
|
|
9210
|
+
}
|
|
9211
|
+
memoGet(name, args) {
|
|
9212
|
+
const hit = this.memo.get(this.memoKey(name, args));
|
|
9213
|
+
if (!hit)
|
|
9214
|
+
return;
|
|
9215
|
+
if (Date.now() - hit.at > MEMO_TTL_MS) {
|
|
9216
|
+
this.memo.delete(this.memoKey(name, args));
|
|
9217
|
+
return;
|
|
9218
|
+
}
|
|
9219
|
+
return { ...hit.result };
|
|
9220
|
+
}
|
|
9221
|
+
memoSet(name, args, result) {
|
|
9222
|
+
const key = this.memoKey(name, args);
|
|
9223
|
+
if (!this.memo.has(key) && this.memo.size >= MEMO_MAX_ENTRIES) {
|
|
9224
|
+
const oldest = this.memo.keys().next();
|
|
9225
|
+
if (!oldest.done)
|
|
9226
|
+
this.memo.delete(oldest.value);
|
|
9227
|
+
}
|
|
9228
|
+
this.memo.set(key, { result: { ...result }, at: Date.now() });
|
|
9229
|
+
}
|
|
9230
|
+
memoInvalidate() {
|
|
9231
|
+
this.memo.clear();
|
|
9232
|
+
}
|
|
9183
9233
|
allowedNames() {
|
|
9184
9234
|
const allow = this.ctx.toolAllowlist;
|
|
9185
9235
|
return allow ? new Set(allow) : null;
|
|
@@ -9221,10 +9271,11 @@ class ToolExecutor {
|
|
|
9221
9271
|
}
|
|
9222
9272
|
}
|
|
9223
9273
|
let result;
|
|
9274
|
+
const memoHit = tool.cacheable ? this.memoGet(call.name, call.arguments) : undefined;
|
|
9224
9275
|
try {
|
|
9225
9276
|
this.ctx.activeCallId = call.id;
|
|
9226
9277
|
if (tool.interactive) {
|
|
9227
|
-
result = await tool.handler(this.ctx, call.arguments);
|
|
9278
|
+
result = memoHit ?? await tool.handler(this.ctx, call.arguments);
|
|
9228
9279
|
} else {
|
|
9229
9280
|
const timeoutMs = tool.timeoutMs ?? TOOL_EXECUTION_TIMEOUT_MS;
|
|
9230
9281
|
const failFast = (message) => {
|
|
@@ -9248,7 +9299,7 @@ class ToolExecutor {
|
|
|
9248
9299
|
}, timeoutMs);
|
|
9249
9300
|
});
|
|
9250
9301
|
const racePromises = [
|
|
9251
|
-
tool.handler(this.ctx, call.arguments),
|
|
9302
|
+
memoHit ? Promise.resolve(memoHit) : tool.handler(this.ctx, call.arguments),
|
|
9252
9303
|
timeoutPromise
|
|
9253
9304
|
];
|
|
9254
9305
|
if (signal) {
|
|
@@ -9284,6 +9335,13 @@ class ToolExecutor {
|
|
|
9284
9335
|
toolCallId: call.id
|
|
9285
9336
|
};
|
|
9286
9337
|
}
|
|
9338
|
+
if (!memoHit) {
|
|
9339
|
+
if (tool.cacheable && result.success) {
|
|
9340
|
+
this.memoSet(call.name, call.arguments, result);
|
|
9341
|
+
} else if (!tool.cacheable && !tool.manualMemo) {
|
|
9342
|
+
this.memoInvalidate();
|
|
9343
|
+
}
|
|
9344
|
+
}
|
|
9287
9345
|
if (signal?.aborted) {
|
|
9288
9346
|
this.ctx.logger.debug(`Tool ${call.name}: abort, skipping onAfterTool plugins`);
|
|
9289
9347
|
} else {
|
|
@@ -9337,7 +9395,7 @@ class ToolExecutor {
|
|
|
9337
9395
|
this.ctx.llmProvider = provider;
|
|
9338
9396
|
}
|
|
9339
9397
|
}
|
|
9340
|
-
var TOOL_EXECUTION_TIMEOUT_MS = 60000;
|
|
9398
|
+
var TOOL_EXECUTION_TIMEOUT_MS = 60000, MEMO_TTL_MS = 120000, MEMO_MAX_ENTRIES = 100;
|
|
9341
9399
|
var init_executor = __esm(() => {
|
|
9342
9400
|
init_runner();
|
|
9343
9401
|
init_i18n();
|
|
@@ -10030,6 +10088,7 @@ var init_read_file = __esm(() => {
|
|
|
10030
10088
|
description: `Read a file from the filesystem. Reads up to ${DEFAULT_LIMIT} lines at a time by default; use offset to page through large files.`,
|
|
10031
10089
|
tags: ["file", "code"],
|
|
10032
10090
|
boundedOutput: true,
|
|
10091
|
+
cacheable: true,
|
|
10033
10092
|
parameters: {
|
|
10034
10093
|
type: "object",
|
|
10035
10094
|
properties: {
|
|
@@ -10957,6 +11016,7 @@ var init_glob_tool = __esm(() => {
|
|
|
10957
11016
|
icon: "\uD83D\uDD0E",
|
|
10958
11017
|
description: `Search for files matching a glob pattern. Shows up to ${MAX_PREVIEW_LINES} results by default. Uses standard glob syntax (e.g., **/*.ts, src/**/*.test.ts).`,
|
|
10959
11018
|
tags: ["file", "code", "research"],
|
|
11019
|
+
cacheable: true,
|
|
10960
11020
|
parameters: {
|
|
10961
11021
|
type: "object",
|
|
10962
11022
|
properties: {
|
|
@@ -11041,6 +11101,7 @@ var init_grep_tool = __esm(() => {
|
|
|
11041
11101
|
icon: "\uD83D\uDD0D",
|
|
11042
11102
|
description: `Search file contents using a regular expression. Shows up to ${MAX_PREVIEW_LINES} matching lines by default. Uses ripgrep (rg) if available, otherwise falls back to grep -r.`,
|
|
11043
11103
|
tags: ["file", "code", "research"],
|
|
11104
|
+
cacheable: true,
|
|
11044
11105
|
parameters: {
|
|
11045
11106
|
type: "object",
|
|
11046
11107
|
properties: {
|
|
@@ -11124,6 +11185,7 @@ var init_list_dir = __esm(() => {
|
|
|
11124
11185
|
description: `List files and directories in a given path. Shows up to ${MAX_PREVIEW_LINES} entries by default.`,
|
|
11125
11186
|
tags: ["file"],
|
|
11126
11187
|
boundedOutput: true,
|
|
11188
|
+
cacheable: true,
|
|
11127
11189
|
parameters: {
|
|
11128
11190
|
type: "object",
|
|
11129
11191
|
properties: {
|
|
@@ -11388,6 +11450,7 @@ var init_file_info = __esm(() => {
|
|
|
11388
11450
|
description: "Get metadata about a file or directory (size, creation date, modification date).",
|
|
11389
11451
|
tags: ["file"],
|
|
11390
11452
|
boundedOutput: true,
|
|
11453
|
+
cacheable: true,
|
|
11391
11454
|
parameters: {
|
|
11392
11455
|
type: "object",
|
|
11393
11456
|
properties: {
|
|
@@ -12048,6 +12111,26 @@ function emptyCliRunHint(command, output, code) {
|
|
|
12048
12111
|
return null;
|
|
12049
12112
|
return "the command exited 0 but printed NOTHING to stdout. If this should run a CLI program, the file probably has no entry point: read it with read_file and check the code actually calls its main function with command-line arguments (e.g. main(process.argv[2])) and prints results with console.log.";
|
|
12050
12113
|
}
|
|
12114
|
+
function isMemoizableCommand(command) {
|
|
12115
|
+
const cmd = command.trim().toLowerCase();
|
|
12116
|
+
if (!cmd || /[|<>;&]/.test(cmd))
|
|
12117
|
+
return false;
|
|
12118
|
+
return READONLY_COMMAND_PATTERNS.some((re) => re.test(cmd));
|
|
12119
|
+
}
|
|
12120
|
+
function memoRead(ctx, args) {
|
|
12121
|
+
const get = ctx.toolExecutor?.memoGet;
|
|
12122
|
+
return typeof get === "function" ? get.call(ctx.toolExecutor, "bash", args) : undefined;
|
|
12123
|
+
}
|
|
12124
|
+
function memoWrite(ctx, args, result) {
|
|
12125
|
+
const set = ctx.toolExecutor?.memoSet;
|
|
12126
|
+
if (typeof set === "function")
|
|
12127
|
+
set.call(ctx.toolExecutor, "bash", args, result);
|
|
12128
|
+
}
|
|
12129
|
+
function memoClear(ctx) {
|
|
12130
|
+
const clear = ctx.toolExecutor?.memoInvalidate;
|
|
12131
|
+
if (typeof clear === "function")
|
|
12132
|
+
clear.call(ctx.toolExecutor);
|
|
12133
|
+
}
|
|
12051
12134
|
function resolveBashSecurityConfig(ctx) {
|
|
12052
12135
|
const appConfig = ctx.config || {};
|
|
12053
12136
|
const fullSecurityConfig = ctx.sessionContext ? getSessionSecurityConfig(appConfig, ctx.sessionContext) : appConfig.security || DEFAULT_SECURITY_CONFIG;
|
|
@@ -12137,7 +12220,7 @@ Hint: ${t("exec.interactive_hint")}`;
|
|
|
12137
12220
|
}
|
|
12138
12221
|
return output;
|
|
12139
12222
|
}
|
|
12140
|
-
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, BG_OUTPUT_PREVIEW_LINES = 15, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, lastFailureSessionId, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, NPM_EXEC_RE, INTERACTIVE_PROMPT_RE, HIDDEN_PROCESS_TOOLS, bashTool;
|
|
12223
|
+
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, BG_OUTPUT_PREVIEW_LINES = 15, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, lastFailureSessionId, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, NPM_EXEC_RE, INTERACTIVE_PROMPT_RE, HIDDEN_PROCESS_TOOLS, READONLY_COMMAND_PREFIXES, READONLY_COMMAND_PATTERNS, bashTool;
|
|
12141
12224
|
var init_bash = __esm(() => {
|
|
12142
12225
|
init_command_validator();
|
|
12143
12226
|
init_audit_log();
|
|
@@ -12216,11 +12299,41 @@ var init_bash = __esm(() => {
|
|
|
12216
12299
|
CLI_FILE_RUN_RE = /\b(bun|node|deno|python|python3|tsx|ts-node|php|ruby|go\s+run)\S*\s+(run\s+)?["']?[\w./\\-]+\.(ts|js|tsx|jsx|mjs|cjs|py)\b/;
|
|
12217
12300
|
NPM_EXEC_RE = /could not determine executable to run/i;
|
|
12218
12301
|
INTERACTIVE_PROMPT_RE = /operation (?:cancelled|canceled|aborted)|stdin is not a tty|not a tty|cannot prompt without a tty/i;
|
|
12219
|
-
HIDDEN_PROCESS_TOOLS = ["process_list", "process_log", "process_kill"];
|
|
12302
|
+
HIDDEN_PROCESS_TOOLS = ["process_list", "process_log", "process_kill", "process_wait"];
|
|
12303
|
+
READONLY_COMMAND_PREFIXES = [
|
|
12304
|
+
"git status",
|
|
12305
|
+
"git log",
|
|
12306
|
+
"git diff",
|
|
12307
|
+
"git branch",
|
|
12308
|
+
"git tag",
|
|
12309
|
+
"git rev-parse",
|
|
12310
|
+
"git ls-remote",
|
|
12311
|
+
"git remote",
|
|
12312
|
+
"git show",
|
|
12313
|
+
"npm view",
|
|
12314
|
+
"npm list",
|
|
12315
|
+
"npm config get",
|
|
12316
|
+
"npm whoami",
|
|
12317
|
+
"npm ping",
|
|
12318
|
+
"npm --version",
|
|
12319
|
+
"node --version",
|
|
12320
|
+
"python --version",
|
|
12321
|
+
"python3 --version",
|
|
12322
|
+
"bun --version",
|
|
12323
|
+
"git --version",
|
|
12324
|
+
"dir",
|
|
12325
|
+
"ls",
|
|
12326
|
+
"echo",
|
|
12327
|
+
"type",
|
|
12328
|
+
"where",
|
|
12329
|
+
"which",
|
|
12330
|
+
"cat"
|
|
12331
|
+
];
|
|
12332
|
+
READONLY_COMMAND_PATTERNS = READONLY_COMMAND_PREFIXES.map((p) => new RegExp(`^${p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(\\s|$)`));
|
|
12220
12333
|
bashTool = {
|
|
12221
12334
|
name: "bash",
|
|
12222
12335
|
icon: "\uD83D\uDCBB",
|
|
12223
|
-
description: `Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_kill. Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers).
|
|
12336
|
+
description: `Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_wait, process_kill. For a finite background task prefer a single process_wait over polling process_log or sleeping (never use ping/timeout/sleep as a delay). Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers).
|
|
12224
12337
|
|
|
12225
12338
|
Windows notes: the shell is cmd.exe — PowerShell cmdlets (Write-Host, Get-Content, Select-String, Out-File) and bash heredocs (cat << EOF) do NOT work there. Use the read_file/write_file tools instead of cat/echo redirection. Sequential commands: use && (a leading ; is auto-converted to &).`,
|
|
12226
12339
|
tags: ["shell", "code"],
|
|
@@ -12239,6 +12352,7 @@ Windows notes: the shell is cmd.exe — PowerShell cmdlets (Write-Host, Get-Cont
|
|
|
12239
12352
|
},
|
|
12240
12353
|
required: ["command"]
|
|
12241
12354
|
},
|
|
12355
|
+
manualMemo: true,
|
|
12242
12356
|
handler: async (ctx, args) => {
|
|
12243
12357
|
const originalCommand = String(args.command);
|
|
12244
12358
|
const toolCall = detectToolCallInBash(originalCommand);
|
|
@@ -12281,6 +12395,15 @@ Hint: run one plain command per tool call. For output redirection write the file
|
|
|
12281
12395
|
if (securityConfig?.logCommands) {
|
|
12282
12396
|
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), false, `Working directory: ${workdir}`);
|
|
12283
12397
|
}
|
|
12398
|
+
const memoKey = { command, workdir };
|
|
12399
|
+
const memoizable = args.background !== true && isMemoizableCommand(command);
|
|
12400
|
+
if (memoizable) {
|
|
12401
|
+
const hit = memoRead(ctx, memoKey);
|
|
12402
|
+
if (hit)
|
|
12403
|
+
return { ...hit };
|
|
12404
|
+
} else {
|
|
12405
|
+
memoClear(ctx);
|
|
12406
|
+
}
|
|
12284
12407
|
const entry = processRegistry.start(command, workdir, ctx.sessionId);
|
|
12285
12408
|
if (ctx.activeCallId) {
|
|
12286
12409
|
registerKillable(ctx.activeCallId, () => processRegistry.kill(entry.id));
|
|
@@ -12311,6 +12434,9 @@ Hint: check the "workdir" path exists and the command is valid for this OS.`
|
|
|
12311
12434
|
workdir,
|
|
12312
12435
|
securityConfig
|
|
12313
12436
|
});
|
|
12437
|
+
if (memoizable && code === 0) {
|
|
12438
|
+
memoWrite(ctx, memoKey, { success: true, output: output2 });
|
|
12439
|
+
}
|
|
12314
12440
|
return { success: code === 0, output: output2 };
|
|
12315
12441
|
}
|
|
12316
12442
|
const explicit = args.background === true;
|
|
@@ -12386,16 +12512,41 @@ ${t("proc.hint", {
|
|
|
12386
12512
|
};
|
|
12387
12513
|
});
|
|
12388
12514
|
|
|
12515
|
+
// src/tools/process-ids.ts
|
|
12516
|
+
function missingProcessIdHint() {
|
|
12517
|
+
const ids = processRegistry.list().map((p) => p.id);
|
|
12518
|
+
const listPart = ids.length ? `
|
|
12519
|
+
${t("proc.list_header")}: ${ids.join(", ")}` : `
|
|
12520
|
+
${t("proc.missing_id_list")}`;
|
|
12521
|
+
return `${t("proc.missing_id")}${listPart}`;
|
|
12522
|
+
}
|
|
12523
|
+
function lookupProcess(args) {
|
|
12524
|
+
if (args.id === undefined || String(args.id).trim() === "") {
|
|
12525
|
+
return { ok: false, output: missingProcessIdHint() };
|
|
12526
|
+
}
|
|
12527
|
+
const id = String(args.id);
|
|
12528
|
+
const entry = processRegistry.get(id);
|
|
12529
|
+
if (!entry) {
|
|
12530
|
+
return { ok: false, output: t("proc.not_found", { id }) };
|
|
12531
|
+
}
|
|
12532
|
+
return { ok: true, id, entry };
|
|
12533
|
+
}
|
|
12534
|
+
var init_process_ids = __esm(() => {
|
|
12535
|
+
init_processes();
|
|
12536
|
+
init_i18n();
|
|
12537
|
+
});
|
|
12538
|
+
|
|
12389
12539
|
// src/tools/process-log.ts
|
|
12390
12540
|
var DEFAULT_TAIL, MAX_TAIL = 300, processLogTool;
|
|
12391
12541
|
var init_process_log = __esm(() => {
|
|
12392
12542
|
init_processes();
|
|
12393
12543
|
init_i18n();
|
|
12544
|
+
init_process_ids();
|
|
12394
12545
|
DEFAULT_TAIL = MAX_PREVIEW_LINES;
|
|
12395
12546
|
processLogTool = {
|
|
12396
12547
|
name: "process_log",
|
|
12397
12548
|
icon: "\uD83D\uDCDC",
|
|
12398
|
-
description: `Show the buffered output of a background process started via bash. Shows the last ${DEFAULT_TAIL} lines by default. Use after starting a dev server to verify it came up without errors, and while it runs to check its state.`,
|
|
12549
|
+
description: `Show the buffered output of a background process started via bash. Shows the last ${DEFAULT_TAIL} lines by default. Use after starting a dev server to verify it came up without errors, and while it runs to check its state. For a finite task that should complete, prefer process_wait over polling this tool.`,
|
|
12399
12550
|
tags: ["shell"],
|
|
12400
12551
|
boundedOutput: true,
|
|
12401
12552
|
parameters: {
|
|
@@ -12410,19 +12561,10 @@ var init_process_log = __esm(() => {
|
|
|
12410
12561
|
required: ["id"]
|
|
12411
12562
|
},
|
|
12412
12563
|
handler: async (_ctx, args) => {
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
${t("proc.list_header")}: ${ids.join(", ")}` : `
|
|
12418
|
-
${t("proc.missing_id_list")}`;
|
|
12419
|
-
return { success: false, output: `${t("proc.missing_id")}${listPart}` };
|
|
12420
|
-
}
|
|
12421
|
-
const id = String(args.id);
|
|
12422
|
-
const entry = processRegistry.get(id);
|
|
12423
|
-
if (!entry) {
|
|
12424
|
-
return { success: false, output: t("proc.not_found", { id }) };
|
|
12425
|
-
}
|
|
12564
|
+
const found = lookupProcess(args);
|
|
12565
|
+
if (!found.ok)
|
|
12566
|
+
return { success: false, output: found.output };
|
|
12567
|
+
const { id, entry } = found;
|
|
12426
12568
|
const status = entry.status === "running" ? t("proc.status_running") : entry.status === "exited" ? t("proc.status_exited") : t("proc.status_killed");
|
|
12427
12569
|
const tail = Math.min(MAX_TAIL, Math.max(1, typeof args.tail === "number" ? Math.floor(args.tail) : DEFAULT_TAIL));
|
|
12428
12570
|
const log = processRegistry.getLog(id, tail);
|
|
@@ -12446,6 +12588,7 @@ var processKillTool;
|
|
|
12446
12588
|
var init_process_kill = __esm(() => {
|
|
12447
12589
|
init_processes();
|
|
12448
12590
|
init_i18n();
|
|
12591
|
+
init_process_ids();
|
|
12449
12592
|
processKillTool = {
|
|
12450
12593
|
name: "process_kill",
|
|
12451
12594
|
icon: "⛔",
|
|
@@ -12459,26 +12602,79 @@ var init_process_kill = __esm(() => {
|
|
|
12459
12602
|
required: ["id"]
|
|
12460
12603
|
},
|
|
12461
12604
|
handler: async (_ctx, args) => {
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
return { success: false, output:
|
|
12605
|
+
const found = lookupProcess(args);
|
|
12606
|
+
if (!found.ok)
|
|
12607
|
+
return { success: false, output: found.output };
|
|
12608
|
+
const { id, entry } = found;
|
|
12609
|
+
const killed = processRegistry.kill(id);
|
|
12610
|
+
if (!killed) {
|
|
12611
|
+
return { success: false, output: t("proc.kill_failed", { id }) };
|
|
12469
12612
|
}
|
|
12470
|
-
|
|
12613
|
+
return {
|
|
12614
|
+
success: true,
|
|
12615
|
+
output: t("proc.killed", { id, pid: entry.pid })
|
|
12616
|
+
};
|
|
12617
|
+
}
|
|
12618
|
+
};
|
|
12619
|
+
});
|
|
12620
|
+
|
|
12621
|
+
// src/tools/process-wait.ts
|
|
12622
|
+
var DEFAULT_WAIT_MS = 30000, MAX_WAIT_MS = 110000, DEFAULT_TAIL2 = 20, MAX_TAIL2 = 100, processWaitTool;
|
|
12623
|
+
var init_process_wait = __esm(() => {
|
|
12624
|
+
init_processes();
|
|
12625
|
+
init_i18n();
|
|
12626
|
+
init_process_ids();
|
|
12627
|
+
processWaitTool = {
|
|
12628
|
+
name: "process_wait",
|
|
12629
|
+
icon: "⏳",
|
|
12630
|
+
description: "Wait for a background process started via bash to finish, instead of polling process_log or sleeping (ping/timeout/sleep commands). Blocks up to timeoutMs, then reports whether the process exited (with its exit code and recent output) or is still running. For dev servers/watchers that never exit, use process_log to inspect and process_kill to stop them.",
|
|
12631
|
+
tags: ["shell"],
|
|
12632
|
+
boundedOutput: true,
|
|
12633
|
+
timeoutMs: 120000,
|
|
12634
|
+
parameters: {
|
|
12635
|
+
type: "object",
|
|
12636
|
+
properties: {
|
|
12637
|
+
id: { type: "string", description: "Process id from bash output or process_list" },
|
|
12638
|
+
timeoutMs: {
|
|
12639
|
+
type: "number",
|
|
12640
|
+
description: `How long to wait in milliseconds (default: ${DEFAULT_WAIT_MS}, max ${MAX_WAIT_MS})`
|
|
12641
|
+
},
|
|
12642
|
+
tail: {
|
|
12643
|
+
type: "number",
|
|
12644
|
+
description: `Number of trailing output lines to include (default: ${DEFAULT_TAIL2}, max ${MAX_TAIL2})`
|
|
12645
|
+
}
|
|
12646
|
+
},
|
|
12647
|
+
required: ["id"]
|
|
12648
|
+
},
|
|
12649
|
+
handler: async (_ctx, args) => {
|
|
12650
|
+
const found = lookupProcess(args);
|
|
12651
|
+
if (!found.ok)
|
|
12652
|
+
return { success: false, output: found.output };
|
|
12653
|
+
const { id } = found;
|
|
12654
|
+
const waitMs = Math.min(MAX_WAIT_MS, Math.max(1000, typeof args.timeoutMs === "number" ? Math.floor(args.timeoutMs) : DEFAULT_WAIT_MS));
|
|
12655
|
+
const started = Date.now();
|
|
12656
|
+
await processRegistry.waitForExit(id, waitMs);
|
|
12657
|
+
const elapsed = Date.now() - started;
|
|
12471
12658
|
const entry = processRegistry.get(id);
|
|
12472
12659
|
if (!entry) {
|
|
12473
12660
|
return { success: false, output: t("proc.not_found", { id }) };
|
|
12474
12661
|
}
|
|
12475
|
-
const
|
|
12476
|
-
|
|
12477
|
-
|
|
12662
|
+
const tail = Math.min(MAX_TAIL2, Math.max(1, typeof args.tail === "number" ? Math.floor(args.tail) : DEFAULT_TAIL2));
|
|
12663
|
+
const log = processRegistry.getLog(id, tail);
|
|
12664
|
+
const logPart = log ? `
|
|
12665
|
+
${log}` : `
|
|
12666
|
+
${t("proc.log_empty")}`;
|
|
12667
|
+
if (entry.status !== "running") {
|
|
12668
|
+
const status = entry.status === "exited" ? t("proc.status_exited") : t("proc.status_killed");
|
|
12669
|
+
return {
|
|
12670
|
+
success: true,
|
|
12671
|
+
output: `${t("proc.wait_exited", { id, ms: elapsed, status, code: entry.exitCode ?? "?" })}${logPart}`
|
|
12672
|
+
};
|
|
12478
12673
|
}
|
|
12479
12674
|
return {
|
|
12480
12675
|
success: true,
|
|
12481
|
-
output: t("proc.
|
|
12676
|
+
output: `${t("proc.wait_running", { id, ms: elapsed })}
|
|
12677
|
+
${t("proc.wait_hint", { id })}${logPart}`
|
|
12482
12678
|
};
|
|
12483
12679
|
}
|
|
12484
12680
|
};
|
|
@@ -14943,10 +15139,14 @@ class StuckDetector {
|
|
|
14943
15139
|
if (!this.hasRepetitiveToolCalls())
|
|
14944
15140
|
return "";
|
|
14945
15141
|
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
14946
|
-
|
|
15142
|
+
const base = t("exec.repetitive_tool", {
|
|
14947
15143
|
tool: last.name,
|
|
14948
15144
|
count: this.repetitionThreshold
|
|
14949
15145
|
});
|
|
15146
|
+
if (last.name === "process_log") {
|
|
15147
|
+
return `${base} ${t("exec.repetitive_process_log_hint")}`;
|
|
15148
|
+
}
|
|
15149
|
+
return base;
|
|
14950
15150
|
}
|
|
14951
15151
|
getWarningKey() {
|
|
14952
15152
|
if (this.isStuck())
|
|
@@ -14986,6 +15186,9 @@ class StuckDetector {
|
|
|
14986
15186
|
if (this.hasConsecutiveFailures()) {
|
|
14987
15187
|
return t("exec.consecutive_failures", { count: String(this.consecutiveFailures) });
|
|
14988
15188
|
}
|
|
15189
|
+
if (this.hasRepetitiveToolCalls()) {
|
|
15190
|
+
return this.getRepetitiveToolMessage();
|
|
15191
|
+
}
|
|
14989
15192
|
if (this.hasReadOnlyLoop()) {
|
|
14990
15193
|
return t("exec.read_only_loop", {
|
|
14991
15194
|
count: String(this.readOnlyStreak)
|
|
@@ -17084,7 +17287,7 @@ function evaluateReasoningPolicy(input, state) {
|
|
|
17084
17287
|
var DECAY_THRESHOLD = 5;
|
|
17085
17288
|
|
|
17086
17289
|
// src/core/agent/constants.ts
|
|
17087
|
-
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, TOOL_ARGS_SUMMARY_MAX_CHARS = 240, TOOL_ARG_VALUE_MAX_CHARS = 48, FORCED_COMPACTION_COOLDOWN = 3, MAX_LLM_ERROR_RETRIES = 2, MAX_HALLUCINATION_RETRIES = 3, MAX_CONSECUTIVE_TOOL_FAILURES = 5, MIN_REPEATED_TOOL_FAILURES = 3, MAX_AUDIT_RETRIES = 3, MAX_EMPTY_RESPONSE_RETRIES = 2, MAX_REPEATED_TOOL_CALLS = 2, MAX_REPEATED_TOOL_CALLS_INTERACTIVE = 5, MUTATION_CYCLE_NUDGE_THRESHOLD = 5;
|
|
17290
|
+
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, TOOL_ARGS_SUMMARY_MAX_CHARS = 240, TOOL_ARG_VALUE_MAX_CHARS = 48, FORCED_COMPACTION_COOLDOWN = 3, MAX_LLM_ERROR_RETRIES = 2, MAX_HALLUCINATION_RETRIES = 3, MAX_CLAIM_CHECK_RETRIES = 1, MAX_CONSECUTIVE_TOOL_FAILURES = 5, MIN_REPEATED_TOOL_FAILURES = 3, MAX_AUDIT_RETRIES = 3, MAX_EMPTY_RESPONSE_RETRIES = 2, MAX_REPEATED_TOOL_CALLS = 2, MAX_REPEATED_TOOL_CALLS_INTERACTIVE = 5, MUTATION_CYCLE_NUDGE_THRESHOLD = 5;
|
|
17088
17291
|
|
|
17089
17292
|
// src/core/agent/artifact-progress.ts
|
|
17090
17293
|
function isTestOrSpecPath(filePath) {
|
|
@@ -17135,6 +17338,7 @@ function createLoopState() {
|
|
|
17135
17338
|
lastModelText: "",
|
|
17136
17339
|
lastForcedCompactionIteration: -FORCED_COMPACTION_COOLDOWN,
|
|
17137
17340
|
hallucinationRetries: 0,
|
|
17341
|
+
claimCheckRetries: 0,
|
|
17138
17342
|
lastToolSignature: "",
|
|
17139
17343
|
consecutiveToolFailures: 0,
|
|
17140
17344
|
consecutiveToolSuccesses: 0,
|
|
@@ -17772,7 +17976,51 @@ var init_tool_batch = __esm(() => {
|
|
|
17772
17976
|
init_utils();
|
|
17773
17977
|
});
|
|
17774
17978
|
|
|
17979
|
+
// src/modules/hallucination/success-claim.ts
|
|
17980
|
+
function checkSuccessClaim(response, tools) {
|
|
17981
|
+
if (!response || !SUCCESS_CLAIM_RE.test(response))
|
|
17982
|
+
return null;
|
|
17983
|
+
if (HEDGE_RE.test(response))
|
|
17984
|
+
return null;
|
|
17985
|
+
const recent = tools.filter((t2) => t2.text?.trim()).slice(-EVIDENCE_WINDOW);
|
|
17986
|
+
for (let i = recent.length - 1;i >= 0; i--) {
|
|
17987
|
+
const entry = recent[i];
|
|
17988
|
+
const text = entry.text.slice(0, EVIDENCE_TEXT_CAP);
|
|
17989
|
+
if (entry.ok === false || FAILURE_SIGNAL_RE.test(text)) {
|
|
17990
|
+
const firstLine = text.split(`
|
|
17991
|
+
`).map((l) => l.trim()).find((l) => l.length > 0);
|
|
17992
|
+
if (!firstLine)
|
|
17993
|
+
continue;
|
|
17994
|
+
return {
|
|
17995
|
+
tool: entry.name || "tool",
|
|
17996
|
+
snippet: firstLine.length > SNIPPET_MAX_CHARS ? `${firstLine.slice(0, SNIPPET_MAX_CHARS - 1)}…` : firstLine
|
|
17997
|
+
};
|
|
17998
|
+
}
|
|
17999
|
+
}
|
|
18000
|
+
return null;
|
|
18001
|
+
}
|
|
18002
|
+
var WORD = "A-Za-zА-Яа-яЁё0-9_", wb = (body) => new RegExp(`(?<![${WORD}])(?:${body})(?![${WORD}])`, "i"), SUCCESS_CLAIM_RE, HEDGE_RE, FAILURE_SIGNAL_RE, EVIDENCE_WINDOW = 10, EVIDENCE_TEXT_CAP = 400, SNIPPET_MAX_CHARS = 160;
|
|
18003
|
+
var init_success_claim = __esm(() => {
|
|
18004
|
+
SUCCESS_CLAIM_RE = wb("published|опубликован[а-яё]*|задеплоен[а-яё]*|deployed|shipped|готово|выполнено|завершен[а-яё]*|успешно|success(?:ful(?:ly)?)?|done|completed?|finished|все сделано|всё сделано");
|
|
18005
|
+
HEDGE_RE = wb("may take|being processed|processing|staged|propagat[a-z]*|in progress|shortly|soon|will appear|несколько минут|обрабатывается|застейджен[а-яё]*|подожд[а-яё]*|проверь[а-яё]* позже|появится|пропагир[а-яё]*|кэш[а-яё]*|cach[a-z]*|обычно|typically");
|
|
18006
|
+
FAILURE_SIGNAL_RE = new RegExp(`${wb("404|409|40[03]|50\\d|failed|failure|not found|ERR_[A-Z_]+|E(?:404|409|ACCES|PERM|CONNREFUSED|TIMED?OUT)|conflict|denied|ошибка|ошибк[а-яё]*|не найден[а-яё]*|не удалось|отклонено").source}|✗|⛔`, "i");
|
|
18007
|
+
});
|
|
18008
|
+
|
|
17775
18009
|
// src/core/agent/hallucination-gate.ts
|
|
18010
|
+
function lastToolEvidence(deps) {
|
|
18011
|
+
let history;
|
|
18012
|
+
try {
|
|
18013
|
+
history = deps.contextManager.getActiveHistory() ?? [];
|
|
18014
|
+
} catch {
|
|
18015
|
+
return [];
|
|
18016
|
+
}
|
|
18017
|
+
return history.filter((m) => m.role === "tool").slice(-10).map((m) => ({
|
|
18018
|
+
name: m.name,
|
|
18019
|
+
ok: m.success,
|
|
18020
|
+
text: typeof m.content === "string" ? m.content : JSON.stringify(m.content) ?? ""
|
|
18021
|
+
})).filter((m) => m.text.trim().length > 0);
|
|
18022
|
+
}
|
|
18023
|
+
|
|
17776
18024
|
class HallucinationGate {
|
|
17777
18025
|
detector;
|
|
17778
18026
|
constructor(detector) {
|
|
@@ -17794,36 +18042,66 @@ class HallucinationGate {
|
|
|
17794
18042
|
return { action: "warn" };
|
|
17795
18043
|
}
|
|
17796
18044
|
if (result.status === "retry") {
|
|
17797
|
-
|
|
17798
|
-
if (deps.exitOnComplete && textContent?.trim() && !deps.hasFinalAudit) {
|
|
17799
|
-
logger2.debug("Exit-on-complete: stopping on first response");
|
|
17800
|
-
state.lastText = textContent;
|
|
17801
|
-
state.finalAnswerAccepted = true;
|
|
17802
|
-
return { action: "exit" };
|
|
17803
|
-
}
|
|
17804
|
-
if (state.suppressRepetitionRetry && result.kind === "repetition") {
|
|
17805
|
-
state.suppressRepetitionRetry = false;
|
|
17806
|
-
logger2.warn(`Audit-triggered re-answer repetition — not counted as hallucination retry`);
|
|
17807
|
-
this.displayWarnLine(result.reason, deps);
|
|
17808
|
-
return { action: "accept" };
|
|
17809
|
-
}
|
|
17810
|
-
if (state.hallucinationRetries >= MAX_HALLUCINATION_RETRIES) {
|
|
17811
|
-
logger2.warn(`Hallucination retries exhausted (${MAX_HALLUCINATION_RETRIES}), returning error`);
|
|
17812
|
-
return { action: "block", reason: t("hall.max_retries_exhausted") };
|
|
17813
|
-
}
|
|
17814
|
-
state.hallucinationRetries++;
|
|
17815
|
-
logger2.warn(`Hallucination retry (${state.hallucinationRetries}/${MAX_HALLUCINATION_RETRIES}): ${result.reason}`);
|
|
17816
|
-
if (textContent) {
|
|
17817
|
-
deps.contextManager.addMessage({ role: "assistant", content: textContent });
|
|
17818
|
-
}
|
|
17819
|
-
deps.contextManager.addMessage({
|
|
17820
|
-
role: "user",
|
|
17821
|
-
content: `<system-summary>[Retry context: ${result.reason}. Original task: "${deps.input}". You must either call a needed tool or provide a substantive response. Empty replies are not allowed.]</system-summary>`
|
|
17822
|
-
});
|
|
17823
|
-
return { action: "retry" };
|
|
18045
|
+
return this.applyRetry(deps, textContent, result);
|
|
17824
18046
|
}
|
|
18047
|
+
const claimRetry = this.checkSuccessClaim(deps, textContent);
|
|
18048
|
+
if (claimRetry)
|
|
18049
|
+
return claimRetry;
|
|
17825
18050
|
return { action: "accept" };
|
|
17826
18051
|
}
|
|
18052
|
+
applyRetry(deps, textContent, result) {
|
|
18053
|
+
const { state, logger: logger2 } = deps;
|
|
18054
|
+
state.iterHallucinationRetry = true;
|
|
18055
|
+
if (deps.exitOnComplete && textContent?.trim() && !deps.hasFinalAudit) {
|
|
18056
|
+
logger2.debug("Exit-on-complete: stopping on first response");
|
|
18057
|
+
state.lastText = textContent;
|
|
18058
|
+
state.finalAnswerAccepted = true;
|
|
18059
|
+
return { action: "exit" };
|
|
18060
|
+
}
|
|
18061
|
+
if (state.suppressRepetitionRetry && result.kind === "repetition") {
|
|
18062
|
+
state.suppressRepetitionRetry = false;
|
|
18063
|
+
logger2.warn(`Audit-triggered re-answer repetition — not counted as hallucination retry`);
|
|
18064
|
+
this.displayWarnLine(result.reason, deps);
|
|
18065
|
+
return { action: "accept" };
|
|
18066
|
+
}
|
|
18067
|
+
if (state.hallucinationRetries >= MAX_HALLUCINATION_RETRIES) {
|
|
18068
|
+
logger2.warn(`Hallucination retries exhausted (${MAX_HALLUCINATION_RETRIES}), returning error`);
|
|
18069
|
+
return { action: "block", reason: t("hall.max_retries_exhausted") };
|
|
18070
|
+
}
|
|
18071
|
+
state.hallucinationRetries++;
|
|
18072
|
+
logger2.warn(`Hallucination retry (${state.hallucinationRetries}/${MAX_HALLUCINATION_RETRIES}): ${result.reason}`);
|
|
18073
|
+
if (textContent) {
|
|
18074
|
+
deps.contextManager.addMessage({ role: "assistant", content: textContent });
|
|
18075
|
+
}
|
|
18076
|
+
deps.contextManager.addMessage({
|
|
18077
|
+
role: "user",
|
|
18078
|
+
content: `<system-summary>[Retry context: ${result.reason}. Original task: "${deps.input}". You must either call a needed tool or provide a substantive response. Empty replies are not allowed.]</system-summary>`
|
|
18079
|
+
});
|
|
18080
|
+
return { action: "retry" };
|
|
18081
|
+
}
|
|
18082
|
+
checkSuccessClaim(deps, textContent) {
|
|
18083
|
+
const { state, logger: logger2 } = deps;
|
|
18084
|
+
const contradiction = checkSuccessClaim(textContent ?? "", lastToolEvidence(deps));
|
|
18085
|
+
if (!contradiction || state.claimCheckRetries >= MAX_CLAIM_CHECK_RETRIES) {
|
|
18086
|
+
return null;
|
|
18087
|
+
}
|
|
18088
|
+
state.claimCheckRetries++;
|
|
18089
|
+
state.iterHallucinationRetry = true;
|
|
18090
|
+
state.suppressRepetitionRetry = true;
|
|
18091
|
+
const reason = t("hall.claim_contradiction", {
|
|
18092
|
+
tool: contradiction.tool,
|
|
18093
|
+
snippet: contradiction.snippet
|
|
18094
|
+
});
|
|
18095
|
+
logger2.warn(`Success-claim contradiction: ${contradiction.tool}: ${contradiction.snippet}`);
|
|
18096
|
+
if (textContent) {
|
|
18097
|
+
deps.contextManager.addMessage({ role: "assistant", content: textContent });
|
|
18098
|
+
}
|
|
18099
|
+
deps.contextManager.addMessage({
|
|
18100
|
+
role: "user",
|
|
18101
|
+
content: `<system-summary>${reason} Original task: "${deps.input}".</system-summary>`
|
|
18102
|
+
});
|
|
18103
|
+
return { action: "retry" };
|
|
18104
|
+
}
|
|
17827
18105
|
displayWarnLine(reason, deps) {
|
|
17828
18106
|
const warnLine = `${t("hall.uncertainty_prefix").trim()} ${reason ?? ""}`;
|
|
17829
18107
|
if (deps.onMeta) {
|
|
@@ -17840,6 +18118,7 @@ ${warnLine}
|
|
|
17840
18118
|
var init_hallucination_gate = __esm(() => {
|
|
17841
18119
|
init_colors();
|
|
17842
18120
|
init_i18n();
|
|
18121
|
+
init_success_claim();
|
|
17843
18122
|
});
|
|
17844
18123
|
|
|
17845
18124
|
// src/core/agent/audit-gate.ts
|
|
@@ -24709,6 +24988,7 @@ function registerAllTools(registry2, skillsModule, opts) {
|
|
|
24709
24988
|
processListTool,
|
|
24710
24989
|
processLogTool,
|
|
24711
24990
|
processKillTool,
|
|
24991
|
+
processWaitTool,
|
|
24712
24992
|
webSearchTool,
|
|
24713
24993
|
webFetchTool,
|
|
24714
24994
|
webBrowseTool,
|
|
@@ -24746,6 +25026,7 @@ var init_tools = __esm(() => {
|
|
|
24746
25026
|
init_process_list();
|
|
24747
25027
|
init_process_log();
|
|
24748
25028
|
init_process_kill();
|
|
25029
|
+
init_process_wait();
|
|
24749
25030
|
init_subagent();
|
|
24750
25031
|
init_chunk_query2();
|
|
24751
25032
|
init_web_search();
|
|
@@ -32730,7 +33011,7 @@ function roleHeading(role) {
|
|
|
32730
33011
|
return role;
|
|
32731
33012
|
}
|
|
32732
33013
|
}
|
|
32733
|
-
function renderSessionMarkdown(meta, messages) {
|
|
33014
|
+
function renderSessionMarkdown(meta, messages, opts) {
|
|
32734
33015
|
const lines = [];
|
|
32735
33016
|
const name = meta?.name ?? "session";
|
|
32736
33017
|
const id = meta?.id ?? "unknown";
|
|
@@ -32748,38 +33029,45 @@ function renderSessionMarkdown(meta, messages) {
|
|
|
32748
33029
|
lines.push("---");
|
|
32749
33030
|
lines.push("");
|
|
32750
33031
|
for (const msg of messages) {
|
|
32751
|
-
|
|
32752
|
-
const nameSuffix = msg.name ? ` (${msg.name})` : "";
|
|
32753
|
-
lines.push(`## ${heading}${nameSuffix}`);
|
|
32754
|
-
lines.push("");
|
|
32755
|
-
const text = getMessageText(msg.content);
|
|
32756
|
-
lines.push(text.length > 0 ? text : "_(empty)_");
|
|
32757
|
-
lines.push("");
|
|
32758
|
-
if (Array.isArray(msg.tool_calls)) {
|
|
32759
|
-
for (const tc of msg.tool_calls) {
|
|
32760
|
-
lines.push(`- tool_call: \`${tc.function.name}\` ${tc.function.arguments}`);
|
|
32761
|
-
}
|
|
32762
|
-
lines.push("");
|
|
32763
|
-
}
|
|
33032
|
+
appendMessageLines(lines, msg, opts);
|
|
32764
33033
|
}
|
|
32765
33034
|
return lines.join(`
|
|
32766
33035
|
`);
|
|
32767
33036
|
}
|
|
32768
|
-
function
|
|
32769
|
-
|
|
33037
|
+
function appendMessageLines(lines, msg, opts) {
|
|
33038
|
+
if (opts?.compact && msg.role === "tool")
|
|
33039
|
+
return;
|
|
33040
|
+
const heading = roleHeading(msg.role);
|
|
33041
|
+
const nameSuffix = msg.name ? ` (${msg.name})` : "";
|
|
33042
|
+
lines.push(`## ${heading}${nameSuffix}`);
|
|
33043
|
+
lines.push("");
|
|
33044
|
+
const text = getMessageText(msg.content);
|
|
33045
|
+
lines.push(text.length > 0 ? text : "_(empty)_");
|
|
33046
|
+
lines.push("");
|
|
33047
|
+
if (Array.isArray(msg.tool_calls)) {
|
|
33048
|
+
for (const tc of msg.tool_calls) {
|
|
33049
|
+
lines.push(`- tool_call: \`${tc.function.name}\` ${tc.function.arguments}`);
|
|
33050
|
+
}
|
|
33051
|
+
lines.push("");
|
|
33052
|
+
}
|
|
33053
|
+
}
|
|
33054
|
+
function buildSessionJson(meta, messages, opts) {
|
|
33055
|
+
const list = opts?.compact ? messages.filter((m) => m.role !== "tool") : messages;
|
|
33056
|
+
return JSON.stringify({ meta, messages: list }, null, 2);
|
|
32770
33057
|
}
|
|
32771
|
-
function buildSessionJsonl(log2) {
|
|
32772
|
-
|
|
33058
|
+
function buildSessionJsonl(log2, opts) {
|
|
33059
|
+
const list = opts?.compact ? log2.filter((e) => !COMPACT_JSONL_DROP.has(e.type)) : log2;
|
|
33060
|
+
return list.map((e) => JSON.stringify(e)).join(`
|
|
32773
33061
|
`);
|
|
32774
33062
|
}
|
|
32775
|
-
function formatExport(bundle, format) {
|
|
33063
|
+
function formatExport(bundle, format, opts) {
|
|
32776
33064
|
switch (format) {
|
|
32777
33065
|
case "md":
|
|
32778
|
-
return renderSessionMarkdown(bundle.meta, bundle.messages);
|
|
33066
|
+
return renderSessionMarkdown(bundle.meta, bundle.messages, opts);
|
|
32779
33067
|
case "json":
|
|
32780
|
-
return buildSessionJson(bundle.meta, bundle.messages);
|
|
33068
|
+
return buildSessionJson(bundle.meta, bundle.messages, opts);
|
|
32781
33069
|
case "jsonl":
|
|
32782
|
-
return buildSessionJsonl(bundle.log);
|
|
33070
|
+
return buildSessionJsonl(bundle.log, opts);
|
|
32783
33071
|
}
|
|
32784
33072
|
}
|
|
32785
33073
|
function redactExport(text, redact) {
|
|
@@ -32792,8 +33080,10 @@ function sanitizeFileName(name) {
|
|
|
32792
33080
|
function extensionFor(format) {
|
|
32793
33081
|
return format === "jsonl" ? "jsonl" : format;
|
|
32794
33082
|
}
|
|
33083
|
+
var COMPACT_JSONL_DROP;
|
|
32795
33084
|
var init_export = __esm(() => {
|
|
32796
33085
|
init_context_log();
|
|
33086
|
+
COMPACT_JSONL_DROP = new Set(["context", "tool_defs", "tool_result", "llm_usage"]);
|
|
32797
33087
|
});
|
|
32798
33088
|
|
|
32799
33089
|
// src/modules/providers/naming.ts
|
|
@@ -41593,7 +41883,8 @@ var init_theme = __esm(() => {
|
|
|
41593
41883
|
"recall",
|
|
41594
41884
|
"session_info",
|
|
41595
41885
|
"process_list",
|
|
41596
|
-
"process_log"
|
|
41886
|
+
"process_log",
|
|
41887
|
+
"process_wait"
|
|
41597
41888
|
]),
|
|
41598
41889
|
mutate: new Set([
|
|
41599
41890
|
"write_file",
|
|
@@ -43041,6 +43332,7 @@ function registerSessionCommands(ctx) {
|
|
|
43041
43332
|
let format = "md";
|
|
43042
43333
|
let out;
|
|
43043
43334
|
let redact = true;
|
|
43335
|
+
let compact = false;
|
|
43044
43336
|
const positional = [];
|
|
43045
43337
|
for (let i = 0;i < args.length; i++) {
|
|
43046
43338
|
const a = args[i];
|
|
@@ -43060,6 +43352,8 @@ function registerSessionCommands(ctx) {
|
|
|
43060
43352
|
out = v;
|
|
43061
43353
|
} else if (a === "--no-redact") {
|
|
43062
43354
|
redact = false;
|
|
43355
|
+
} else if (a === "--compact") {
|
|
43356
|
+
compact = true;
|
|
43063
43357
|
} else {
|
|
43064
43358
|
positional.push(a);
|
|
43065
43359
|
}
|
|
@@ -43081,7 +43375,7 @@ function registerSessionCommands(ctx) {
|
|
|
43081
43375
|
ctx.output.writeLine(pc.yellow(t("export.empty")));
|
|
43082
43376
|
return;
|
|
43083
43377
|
}
|
|
43084
|
-
const text = redactExport(formatExport({ meta: match, messages, log: log2 }, format), redact);
|
|
43378
|
+
const text = redactExport(formatExport({ meta: match, messages, log: log2 }, format, { compact }), redact);
|
|
43085
43379
|
if (out === "-") {
|
|
43086
43380
|
ctx.output.writeLine(text);
|
|
43087
43381
|
return;
|
|
@@ -44507,7 +44801,7 @@ function buildSessionCommands(program2) {
|
|
|
44507
44801
|
}
|
|
44508
44802
|
}
|
|
44509
44803
|
});
|
|
44510
|
-
session2.command("export").argument("<id>", "Session id (or name prefix)").description(t("cli.export_session")).option("--format <format>", t("cli.export_format"), "md").option("--out <path>", t("cli.export_out")).option("--no-redact", t("cli.export_no_redact")).action(async (id, opts) => {
|
|
44804
|
+
session2.command("export").argument("<id>", "Session id (or name prefix)").description(t("cli.export_session")).option("--format <format>", t("cli.export_format"), "md").option("--out <path>", t("cli.export_out")).option("--no-redact", t("cli.export_no_redact")).option("--compact", t("cli.export_compact")).action(async (id, opts) => {
|
|
44511
44805
|
const { sessionManager } = await bootstrap();
|
|
44512
44806
|
const meta = sessionManager.get(id) ?? sessionManager.list().find((s) => s.id.startsWith(id)) ?? sessionManager.list().find((s) => s.name.toLowerCase().includes(id.toLowerCase()));
|
|
44513
44807
|
if (!meta) {
|
|
@@ -44525,7 +44819,7 @@ function buildSessionCommands(program2) {
|
|
|
44525
44819
|
getDefaultChannel().writeLine(t("export.empty"));
|
|
44526
44820
|
return;
|
|
44527
44821
|
}
|
|
44528
|
-
const text = redactExport(formatExport({ meta, messages, log: log2 }, format), opts.redact);
|
|
44822
|
+
const text = redactExport(formatExport({ meta, messages, log: log2 }, format, { compact: opts.compact }), opts.redact);
|
|
44529
44823
|
if (opts.out === "-") {
|
|
44530
44824
|
getDefaultChannel().writeLine(text);
|
|
44531
44825
|
return;
|
|
@@ -46400,6 +46694,9 @@ class Renderer {
|
|
|
46400
46694
|
thinkingLabel = "";
|
|
46401
46695
|
textStarted = false;
|
|
46402
46696
|
reconBuffer = [];
|
|
46697
|
+
pendingFooter = null;
|
|
46698
|
+
repeatCount = 0;
|
|
46699
|
+
repeatMs = 0;
|
|
46403
46700
|
outputLineCount = 0;
|
|
46404
46701
|
outputSuppressed = false;
|
|
46405
46702
|
constructor(opts = {}) {
|
|
@@ -46548,6 +46845,7 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
46548
46845
|
this.verbosity = verbosity;
|
|
46549
46846
|
}
|
|
46550
46847
|
flushRecon() {
|
|
46848
|
+
this.flushPendingFooter();
|
|
46551
46849
|
if (this.reconBuffer.length === 0)
|
|
46552
46850
|
return;
|
|
46553
46851
|
const items = this.reconBuffer;
|
|
@@ -46563,6 +46861,20 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
46563
46861
|
const total = items.reduce((a, b) => a + b.duration, 0);
|
|
46564
46862
|
const label = t("ui.actions_summary", { count: String(items.length) });
|
|
46565
46863
|
this.channel.writeRaw(`${pc.dim(glyph.group)} ${label} ${successMark()} ${pc.dim(`· ${formatDuration(total)}`)} ${pc.dim("· /verbose")}
|
|
46864
|
+
`);
|
|
46865
|
+
}
|
|
46866
|
+
flushPendingFooter() {
|
|
46867
|
+
const p = this.pendingFooter;
|
|
46868
|
+
if (!p)
|
|
46869
|
+
return;
|
|
46870
|
+
this.pendingFooter = null;
|
|
46871
|
+
const times = this.repeatCount + 1;
|
|
46872
|
+
const total = p.duration + this.repeatMs;
|
|
46873
|
+
this.repeatCount = 0;
|
|
46874
|
+
this.repeatMs = 0;
|
|
46875
|
+
const head = `${p.leadMark} ${toolAccent(p.tool)(p.label)}${p.summary ? ` ${pc.dim("·")} ${pc.dim(p.summary)}` : ""}`;
|
|
46876
|
+
const timesPart = times > 1 ? ` ${pc.dim(`×${times}`)}` : "";
|
|
46877
|
+
this.channel.writeRaw(`${head} ${successMark()} ${pc.dim(formatDuration(total))}${timesPart}
|
|
46566
46878
|
`);
|
|
46567
46879
|
}
|
|
46568
46880
|
planBlock(lines) {
|
|
@@ -46612,28 +46924,7 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
46612
46924
|
tail += ` ${pc.dim("cost")} ${pc.yellow(formatUsd(costUsd))}`;
|
|
46613
46925
|
}
|
|
46614
46926
|
if (deferred) {
|
|
46615
|
-
|
|
46616
|
-
this.reconBuffer.push({
|
|
46617
|
-
tool: this.card.tool,
|
|
46618
|
-
mark: leadMark,
|
|
46619
|
-
label,
|
|
46620
|
-
summary,
|
|
46621
|
-
duration
|
|
46622
|
-
});
|
|
46623
|
-
this.recordLast(label, summary, error, duration);
|
|
46624
|
-
this.card = null;
|
|
46625
|
-
return;
|
|
46626
|
-
}
|
|
46627
|
-
this.flushRecon();
|
|
46628
|
-
const head = `${leadMark} ${toolAccent(this.card.tool)(label)}${summary ? ` ${pc.dim("·")} ${pc.dim(summary)}` : ""}`;
|
|
46629
|
-
this.channel.writeRaw(`${head} ${tail}
|
|
46630
|
-
`);
|
|
46631
|
-
if (error && firstBody) {
|
|
46632
|
-
const soft = firstBody.startsWith("⚠");
|
|
46633
|
-
const color = soft ? pc.yellow : pc.red;
|
|
46634
|
-
this.channel.writeRaw(`${GUTTER}${pc.dim(glyph.detail)} ${color(firstBody)}
|
|
46635
|
-
`);
|
|
46636
|
-
}
|
|
46927
|
+
this.toolEndDeferred(tail, duration, error);
|
|
46637
46928
|
} else {
|
|
46638
46929
|
const footer = `${GUTTER}${tail}`;
|
|
46639
46930
|
this.channel.writeRaw(`${footer}
|
|
@@ -46646,6 +46937,45 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
46646
46937
|
this.recordLast(label, summary, error, duration);
|
|
46647
46938
|
this.card = null;
|
|
46648
46939
|
}
|
|
46940
|
+
toolEndDeferred(tail, duration, error) {
|
|
46941
|
+
const card = this.card;
|
|
46942
|
+
if (!card)
|
|
46943
|
+
return;
|
|
46944
|
+
const { label, summary, mark: leadMark, firstBody } = card;
|
|
46945
|
+
if (!error && RECON_TOOLS.has(card.tool)) {
|
|
46946
|
+
this.flushPendingFooter();
|
|
46947
|
+
this.reconBuffer.push({ tool: card.tool, mark: leadMark, label, summary, duration });
|
|
46948
|
+
return;
|
|
46949
|
+
}
|
|
46950
|
+
const repeatKey = `${card.tool}::${label}::${summary}`;
|
|
46951
|
+
if (!error && this.pendingFooter?.key === repeatKey) {
|
|
46952
|
+
this.repeatCount += 1;
|
|
46953
|
+
this.repeatMs += duration;
|
|
46954
|
+
return;
|
|
46955
|
+
}
|
|
46956
|
+
this.flushPendingFooter();
|
|
46957
|
+
this.flushRecon();
|
|
46958
|
+
if (!error) {
|
|
46959
|
+
this.pendingFooter = {
|
|
46960
|
+
key: repeatKey,
|
|
46961
|
+
leadMark,
|
|
46962
|
+
tool: card.tool,
|
|
46963
|
+
label,
|
|
46964
|
+
summary,
|
|
46965
|
+
duration
|
|
46966
|
+
};
|
|
46967
|
+
return;
|
|
46968
|
+
}
|
|
46969
|
+
const head = `${leadMark} ${toolAccent(card.tool)(label)}${summary ? ` ${pc.dim("·")} ${pc.dim(summary)}` : ""}`;
|
|
46970
|
+
this.channel.writeRaw(`${head} ${tail}
|
|
46971
|
+
`);
|
|
46972
|
+
if (error && firstBody) {
|
|
46973
|
+
const soft = firstBody.startsWith("⚠");
|
|
46974
|
+
const color = soft ? pc.yellow : pc.red;
|
|
46975
|
+
this.channel.writeRaw(`${GUTTER}${pc.dim(glyph.detail)} ${color(firstBody)}
|
|
46976
|
+
`);
|
|
46977
|
+
}
|
|
46978
|
+
}
|
|
46649
46979
|
recordLast(label, summary, error, duration) {
|
|
46650
46980
|
const firstBody = this.card?.firstBody ?? "";
|
|
46651
46981
|
const status = !error ? "ok" : firstBody.startsWith("⚠") ? "warn" : "error";
|