micro-models-agent 0.50.0 → 0.50.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +385 -99
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1991,7 +1991,6 @@ var init_security = __esm(() => {
|
|
|
1991
1991
|
"python",
|
|
1992
1992
|
"python3",
|
|
1993
1993
|
"node",
|
|
1994
|
-
"bun",
|
|
1995
1994
|
"perl",
|
|
1996
1995
|
"ruby",
|
|
1997
1996
|
"del",
|
|
@@ -2013,8 +2012,6 @@ var init_security = __esm(() => {
|
|
|
2013
2012
|
"--delete",
|
|
2014
2013
|
"--remove",
|
|
2015
2014
|
"--dangerous",
|
|
2016
|
-
"--yes",
|
|
2017
|
-
"-y",
|
|
2018
2015
|
"--no-confirm"
|
|
2019
2016
|
],
|
|
2020
2017
|
dangerousOperators: [">", ">>", "2>", "2>>", ";", "&&", "||", "|", "`", "$("]
|
|
@@ -2279,7 +2276,7 @@ var init_defaults = __esm(() => {
|
|
|
2279
2276
|
baseDelay: 1000,
|
|
2280
2277
|
maxDelay: 30000,
|
|
2281
2278
|
maxStreamRetries: 2,
|
|
2282
|
-
noDataTimeoutMs:
|
|
2279
|
+
noDataTimeoutMs: 180000
|
|
2283
2280
|
},
|
|
2284
2281
|
maxToolIterations: 1000,
|
|
2285
2282
|
stuckThreshold: 6,
|
|
@@ -2391,6 +2388,7 @@ The path was joined onto the working directory because it does not exist as give
|
|
|
2391
2388
|
"file.truncated": `
|
|
2392
2389
|
... (truncated)`,
|
|
2393
2390
|
"file.read_header": "── {path} ({ext}, {total} lines, lines {from}-{to})",
|
|
2391
|
+
"file.read_range": "lines {from}-{to} of {total}",
|
|
2394
2392
|
"file.read_truncated": `
|
|
2395
2393
|
|
|
2396
2394
|
[Truncated: {remaining} more lines. Use read_file with offset={next} to continue]`,
|
|
@@ -2414,7 +2412,10 @@ The path was joined onto the working directory because it does not exist as give
|
|
|
2414
2412
|
"error.llm_retries": "LLM request failed after retries",
|
|
2415
2413
|
"error.llm_429": "Rate limit exceeded (HTTP 429) for {model} on {baseUrl}. The provider is throttling requests — free models are especially strict. Get an API key or switch to a paid/faster model: {baseUrl}",
|
|
2416
2414
|
"error.no_response_body": "No response body stream",
|
|
2417
|
-
"error.llm_stream_idle":
|
|
2415
|
+
"error.llm_stream_idle": 'LLM stream stalled — no data for {timeout}ms. If the provider buffers streaming (e.g. LM Studio) or generates very long tool calls, raise "retry.noDataTimeoutMs" in ~/.mma/config.json (restart required)',
|
|
2416
|
+
"error.llm_stream_idle_toolcall": 'LLM stream stalled after a tool_call started — no data for {timeout}ms. The provider most likely buffers SSE instead of streaming tool-call argument deltas (seen with LM Studio). Raise "retry.noDataTimeoutMs" in ~/.mma/config.json (restart required)',
|
|
2417
|
+
"error.llm_truncated": 'Response hit the completion token limit ({tokens}) and was cut off before any content arrived. Split the task into smaller outputs or raise "maxCompletionTokens" in the config',
|
|
2418
|
+
"error.llm_truncated_toolcall": 'Response hit the completion token limit ({tokens}) in the middle of a tool_call — its arguments were cut off. Write the file in smaller chunks (several write_file/edit_file calls) or raise "maxCompletionTokens" in the config',
|
|
2418
2419
|
"error.llm_timeout": "LLM request timed out ({timeout}ms)",
|
|
2419
2420
|
"env.runtime_node": "Running under Node (v{version}) — clipboard image paste, subagent performance and LSP spawn on Windows degrade. Install Bun (https://bun.sh) for full features.",
|
|
2420
2421
|
"env.runtime_old": "Runtime version {version} is below the required engines {engine}.",
|
|
@@ -2539,6 +2540,8 @@ Command: {command}`,
|
|
|
2539
2540
|
{lines}`,
|
|
2540
2541
|
"proc.none": "No background processes running.",
|
|
2541
2542
|
"proc.not_found": "Process not found: {id}",
|
|
2543
|
+
"proc.missing_id": "Provide the process id: process_kill id=proc_xxx (the id is returned by bash when a command goes to the background, or by process_list).",
|
|
2544
|
+
"proc.missing_id_list": "No background processes are currently running.",
|
|
2542
2545
|
"proc.killed": "Process {id} (PID {pid}) killed.",
|
|
2543
2546
|
"proc.kill_failed": "Failed to kill process {id}",
|
|
2544
2547
|
"proc.list_header": "Background processes",
|
|
@@ -2595,6 +2598,8 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
|
|
|
2595
2598
|
"plan.deleted": "Plan {id} deleted permanently.",
|
|
2596
2599
|
"plan.purged": "Deleted {count} plan(s). Plan storage is empty.",
|
|
2597
2600
|
"plan.no_deliverables": "note: step {step} names no files — its completion cannot be auto-verified; run an explicit check (e.g. build/tests) before your final answer.",
|
|
2601
|
+
"plan.bulk_update_rejected": 'Bulk steps[] rewrite via action=update is not supported: it would discard all step statuses. Use "re-plan" (keeps completed steps, replaces the rest) or mark steps individually: plan update step=N status=done|failed|skipped.',
|
|
2602
|
+
"plan.readonly": 'Plan {id} is {status} (read-only) — it cannot be updated. Use "plan switch" to activate it, or create a new plan for new work.',
|
|
2598
2603
|
"plan.list_legend": "[*] active · [ ] draft · [-] archived",
|
|
2599
2604
|
"exec.plan_nudge": "You made {count} file-changing tool call(s) without a plan. For tasks that create or modify files, or span multiple steps, create a plan first (plan create) with concrete steps (exact filenames, commands, deliverables), then continue.",
|
|
2600
2605
|
"todo.added": "Added {count} todo(s): {items}",
|
|
@@ -2909,6 +2914,7 @@ Use this knowledge to answer the user's question.`,
|
|
|
2909
2914
|
"exec.tool_errors_recovery": "Tool {tool} has failed {count} times in a row. Try an alternative: create files directly via write_file, use a different command, or if nothing works — skip this step via plan update step=N status=skipped with a note explaining why.",
|
|
2910
2915
|
"exec.repetitive_tool": "Called {tool} {count} times with identical arguments and result. Try a different approach — create files directly, change arguments, or check process status via process_log.",
|
|
2911
2916
|
"exec.consecutive_failures_recovery": "{count} consecutive tool failures. Create files directly via write_file instead of terminal commands. Check that dependencies are installed (npm install). Do not run build/tests until all files are created.",
|
|
2917
|
+
"exec.bash_flailing_recovery": "bash failed in {fails} of the last {window} attempts. Stop using shell commands for file operations on this machine — they do not work. Use the dedicated tools instead: read_file (the header shows line counts), write_file, edit_file, file_info (file size), glob/grep for search",
|
|
2912
2918
|
"exec.read_only_loop": "No write/exec for {count} tool calls — the agent is only reading/exploring.",
|
|
2913
2919
|
"exec.read_only_loop_recovery": "{count} consecutive read-only tool calls (read_file/glob/grep/browser) with no writes. STOP exploring and make the edit the task requires: read the file, then call write_file or edit_file. If you cannot finish the task, ask the user instead of re-reading the same files.",
|
|
2914
2920
|
"exec.plan_warning": 'Current plan step {step} is "{description}", but {tool} is being called for files outside this step. Complete the current step first, then call plan update step={step} status=done before moving to the next step.',
|
|
@@ -2941,6 +2947,7 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
2941
2947
|
"exec.error_search_failed": 'Web search returned nothing for "{query}".',
|
|
2942
2948
|
"exec.error_search_no_query": "Error output is not meaningful — skipping the web search.",
|
|
2943
2949
|
"exec.npm_exec_hint": '"could not determine executable to run" — no "bin" for that package/script. Use "npm run <script>" (script must exist in package.json) or "bunx <pkg>" for a package that declares a bin.',
|
|
2950
|
+
"exec.hidden_tool_hint": '"{tool}" is not a shell command — it is an MMA tool that is currently hidden. Call the enable_tools tool with tags ["shell"] to unlock it; it becomes available on the next iteration.',
|
|
2944
2951
|
"exec.task_reminder": "Task: {task}. Continue making progress — do not repeat failed actions.",
|
|
2945
2952
|
"hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
|
|
2946
2953
|
"hall.short_response": "Response too short or empty",
|
|
@@ -3101,6 +3108,7 @@ var init_ru = __esm(() => {
|
|
|
3101
3108
|
"file.truncated": `
|
|
3102
3109
|
... (обрезано)`,
|
|
3103
3110
|
"file.read_header": "── {path} ({ext}, {total} строк, строки {from}-{to})",
|
|
3111
|
+
"file.read_range": "строки {from}-{to} из {total}",
|
|
3104
3112
|
"file.read_truncated": `
|
|
3105
3113
|
|
|
3106
3114
|
[Обрезано: ещё {remaining} строк. Продолжите чтение через read_file с offset={next}]`,
|
|
@@ -3124,7 +3132,10 @@ var init_ru = __esm(() => {
|
|
|
3124
3132
|
"error.llm_retries": "Запрос LLM не удался после повторов",
|
|
3125
3133
|
"error.llm_429": "Превышен лимит запросов (HTTP 429) для {model} на {baseUrl}. Провайдер ограничивает трафик — особенно строгие free-модели. Получите API-ключ или переключитесь на платную/быструю модель: {baseUrl}",
|
|
3126
3134
|
"error.no_response_body": "Нет потока тела ответа",
|
|
3127
|
-
"error.llm_stream_idle":
|
|
3135
|
+
"error.llm_stream_idle": 'Поток LLM завис — нет данных {timeout}мс. Если провайдер буферизирует стрим (например, LM Studio) или генерирует очень длинные tool calls, увеличьте "retry.noDataTimeoutMs" в ~/.mma/config.json (нужен рестарт)',
|
|
3136
|
+
"error.llm_stream_idle_toolcall": 'Поток LLM завис после начала tool_call — нет данных {timeout}мс. Скорее всего провайдер буферизирует SSE вместо потоковой передачи аргументов tool_call (наблюдается в LM Studio). Увеличьте "retry.noDataTimeoutMs" в ~/.mma/config.json (нужен рестарт)',
|
|
3137
|
+
"error.llm_truncated": 'Ответ упёрся в лимит токенов генерации ({tokens}) и был обрезан до какого-либо содержимого. Разбейте задачу на меньшие порции вывода или увеличьте "maxCompletionTokens" в конфиге',
|
|
3138
|
+
"error.llm_truncated_toolcall": 'Ответ упёрся в лимит токенов генерации ({tokens}) посреди tool_call — его аргументы обрезаны. Пишите файл частями (несколько вызовов write_file/edit_file) или увеличьте "maxCompletionTokens" в конфиге',
|
|
3128
3139
|
"error.llm_timeout": "Время запроса LLM истекло ({timeout}мс)",
|
|
3129
3140
|
"env.runtime_node": "Запущено под Node (v{version}) — вставка изображений из буфера и LSP на Windows работают урезанно. Установите Bun (https://bun.sh) для полного функционала.",
|
|
3130
3141
|
"env.runtime_old": "Версия рантайма {version} ниже требуемой engines {engine}.",
|
|
@@ -3248,6 +3259,8 @@ var init_ru = __esm(() => {
|
|
|
3248
3259
|
{lines}`,
|
|
3249
3260
|
"proc.none": "Фоновых процессов нет.",
|
|
3250
3261
|
"proc.not_found": "Процесс не найден: {id}",
|
|
3262
|
+
"proc.missing_id": "Укажите id процесса: process_kill id=proc_xxx (id возвращается bash при уходе команды в фон или выводится через process_list).",
|
|
3263
|
+
"proc.missing_id_list": "Сейчас фоновых процессов нет.",
|
|
3251
3264
|
"proc.killed": "Процесс {id} (PID {pid}) остановлен.",
|
|
3252
3265
|
"proc.kill_failed": "Не удалось остановить процесс {id}",
|
|
3253
3266
|
"proc.list_header": "Фоновые процессы",
|
|
@@ -3304,6 +3317,8 @@ var init_ru = __esm(() => {
|
|
|
3304
3317
|
"plan.deleted": "План {id} удалён окончательно.",
|
|
3305
3318
|
"plan.purged": "Удалено планов: {count}. Хранилище планов пусто.",
|
|
3306
3319
|
"plan.no_deliverables": "примечание: шаг {step} не называет файлов — его выполнение нельзя проверить автоматически; перед финальным ответом явно проверь результат (например, запусти сборку/тесты).",
|
|
3320
|
+
"plan.bulk_update_rejected": 'Массовая перезапись steps[] через action=update не поддерживается: она бы стёрла статусы всех шагов. Используй "re-plan" (сохраняет завершённые шаги, заменяет остальные) или отмечай шаги по одному: plan update step=N status=done|failed|skipped.',
|
|
3321
|
+
"plan.readonly": 'План {id} имеет статус {status} (только чтение) — обновить его нельзя. Используй "plan switch", чтобы активировать его, или создай новый план для новой задачи.',
|
|
3307
3322
|
"plan.list_legend": "[*] активный · [ ] черновик · [-] архив",
|
|
3308
3323
|
"exec.plan_nudge": "Ты сделал(а) {count} вызов(ов), изменяющих файлы, без плана. Для задач, создающих/изменяющих файлы или состоящих из нескольких шагов, сначала создай план (plan create) с конкретными шагами (точные имена файлов, команды, результаты), а потом продолжай.",
|
|
3309
3324
|
"todo.added": "Добавлено {count} задач: {items}",
|
|
@@ -3618,6 +3633,7 @@ var init_ru = __esm(() => {
|
|
|
3618
3633
|
"exec.tool_errors_recovery": "Инструмент {tool} упал {count} раз подряд. Попробуйте альтернативу: создайте файлы напрямую через write_file, используйте другую команду, или пропустите этот шаг через plan update step=N status=skipped с пометкой почему.",
|
|
3619
3634
|
"exec.repetitive_tool": "Инструмент {tool} вызван {count} раз с одинаковыми аргументами и результатом. Попробуйте другой подход — создайте файлы напрямую, измените аргументы или проверьте статус процесса через process_log.",
|
|
3620
3635
|
"exec.consecutive_failures_recovery": "{count} инструментов подряд упали. Создавайте файлы напрямую через write_file вместо команд терминала. Проверьте что зависимости установлены (npm install). Не запускайте сборку/тесты пока не созданы все файлы.",
|
|
3636
|
+
"exec.bash_flailing_recovery": "bash упал в {fails} из последних {window} попыток. Хватит использовать шелл-команды для файловых операций на этой машине — они не работают. Используй специализированные инструменты: read_file (в заголовке число строк), write_file, edit_file, file_info (размер файла), glob/grep для поиска",
|
|
3621
3637
|
"exec.read_only_loop": "Нет записи/выполнения за {count} вызовов — агент только читает/исследует.",
|
|
3622
3638
|
"exec.read_only_loop_recovery": "{count} вызовов только для чтения подряд (read_file/glob/grep/browser) без записи. Хватит исследовать — внесите правку, которую требует задача: прочитайте файл, затем вызовите write_file или edit_file. Если не можете завершить задачу — спросите пользователя, а не перечитывайте одни и те же файлы.",
|
|
3623
3639
|
"exec.plan_warning": 'Текущий шаг плана {step} — "{description}", но вызывается {tool} для файлов вне этого шага. Завершите текущий шаг, вызовите plan update step={step} status=done, затем переходите к следующему.',
|
|
@@ -3650,6 +3666,7 @@ var init_ru = __esm(() => {
|
|
|
3650
3666
|
"exec.error_search_failed": 'Поиск в интернете для "{query}" ничего не дал.',
|
|
3651
3667
|
"exec.error_search_no_query": "Текст ошибки незначимый — поиск в интернете пропущен.",
|
|
3652
3668
|
"exec.npm_exec_hint": '"could not determine executable to run" — у пакета/скрипта нет "bin". Используй "npm run <script>" (скрипт должен быть в package.json) или "bunx <pkg>" для пакета с объявленным bin.',
|
|
3669
|
+
"exec.hidden_tool_hint": '"{tool}" — не команда оболочки, это инструмент MMA, который сейчас скрыт. Вызови тул enable_tools с tags ["shell"], чтобы включить его; он станет доступен на следующей итерации.',
|
|
3653
3670
|
"exec.task_reminder": "Задача: {task}. Продолжай работу — не повторяй неудачные действия.",
|
|
3654
3671
|
"hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
|
|
3655
3672
|
"hall.short_response": "Слишком короткий или пустой ответ",
|
|
@@ -4200,22 +4217,6 @@ function normalizeLspServerArgs(config) {
|
|
|
4200
4217
|
}
|
|
4201
4218
|
}
|
|
4202
4219
|
}
|
|
4203
|
-
function normalizeStaleSecurityConfig(config) {
|
|
4204
|
-
const sec = config.security;
|
|
4205
|
-
if (!sec || sec.enabled !== false)
|
|
4206
|
-
return;
|
|
4207
|
-
const bash = sec.bash;
|
|
4208
|
-
if (!bash || bash.enabled !== false || bash.blockDangerousFlags !== false)
|
|
4209
|
-
return;
|
|
4210
|
-
const LEGACY_OPERATORS = [">", ">>", "2>", "2>>", "`"];
|
|
4211
|
-
if (JSON.stringify(bash.dangerousOperators ?? []) !== JSON.stringify(LEGACY_OPERATORS))
|
|
4212
|
-
return;
|
|
4213
|
-
const defaultCmds = new Set(DEFAULT_SECURITY_CONFIG.bash.blacklist);
|
|
4214
|
-
const blacklist = Array.isArray(bash.blacklist) ? bash.blacklist : [];
|
|
4215
|
-
if (blacklist.some((cmd) => !defaultCmds.has(cmd)))
|
|
4216
|
-
return;
|
|
4217
|
-
config.security = JSON.parse(JSON.stringify(DEFAULT_SECURITY_CONFIG));
|
|
4218
|
-
}
|
|
4219
4220
|
function loadJSON(path) {
|
|
4220
4221
|
try {
|
|
4221
4222
|
if (existsSync4(path)) {
|
|
@@ -4289,7 +4290,6 @@ function loadConfig(options) {
|
|
|
4289
4290
|
config.security.contentScan.dangerousPatterns = restoreDangerousPatterns(config.security.contentScan.dangerousPatterns, DEFAULT_SECURITY_CONFIG.contentScan.dangerousPatterns);
|
|
4290
4291
|
}
|
|
4291
4292
|
normalizeLspServerArgs(config);
|
|
4292
|
-
normalizeStaleSecurityConfig(config);
|
|
4293
4293
|
config = applyEnvVars(config);
|
|
4294
4294
|
try {
|
|
4295
4295
|
const encryptor = new ConfigEncryptor;
|
|
@@ -5354,7 +5354,7 @@ class OpenAICompatProvider {
|
|
|
5354
5354
|
baseDelay: 1000,
|
|
5355
5355
|
maxDelay: 30000,
|
|
5356
5356
|
maxStreamRetries: 2,
|
|
5357
|
-
noDataTimeoutMs:
|
|
5357
|
+
noDataTimeoutMs: 180000
|
|
5358
5358
|
};
|
|
5359
5359
|
this.rateLimiter = createRateLimiter(config.rateLimits);
|
|
5360
5360
|
}
|
|
@@ -5383,7 +5383,7 @@ class OpenAICompatProvider {
|
|
|
5383
5383
|
async* doStream(messages, tools, signal, options) {
|
|
5384
5384
|
const { baseDelay, maxDelay, maxStreamRetries, noDataTimeoutMs } = this.retryConfig;
|
|
5385
5385
|
const streamRetries = maxStreamRetries ?? 2;
|
|
5386
|
-
const idleTimeoutMs = noDataTimeoutMs ??
|
|
5386
|
+
const idleTimeoutMs = noDataTimeoutMs ?? 180000;
|
|
5387
5387
|
for (let attempt = 0;; attempt++) {
|
|
5388
5388
|
let emitted = false;
|
|
5389
5389
|
const onEmit = () => {
|
|
@@ -5407,12 +5407,13 @@ class OpenAICompatProvider {
|
|
|
5407
5407
|
}
|
|
5408
5408
|
}
|
|
5409
5409
|
async* streamOnce(messages, tools, signal, options, onEmit, idleTimeoutMs) {
|
|
5410
|
+
const maxTokens = options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096;
|
|
5410
5411
|
const body = buildRequestBody({
|
|
5411
5412
|
model: this.model,
|
|
5412
5413
|
messages,
|
|
5413
5414
|
tools,
|
|
5414
5415
|
stream: true,
|
|
5415
|
-
maxTokens
|
|
5416
|
+
maxTokens,
|
|
5416
5417
|
reasoningEffort: options?.reasoningEffort
|
|
5417
5418
|
});
|
|
5418
5419
|
const { headers, abortSignal, cleanup, isTimeout, flagTimeout, controller } = this.buildRequestSetup(signal);
|
|
@@ -5450,8 +5451,11 @@ class OpenAICompatProvider {
|
|
|
5450
5451
|
const decoder = new TextDecoder;
|
|
5451
5452
|
let buffer = "";
|
|
5452
5453
|
const toolCallAccs = new Map;
|
|
5454
|
+
let sawToolCallStart = false;
|
|
5453
5455
|
let usage;
|
|
5454
5456
|
let sawDone = false;
|
|
5457
|
+
let lastFinishReason;
|
|
5458
|
+
let sawText = false;
|
|
5455
5459
|
const readIdle = () => new Promise((resolve, reject) => {
|
|
5456
5460
|
const idleTimer = setTimeout(() => {
|
|
5457
5461
|
flagTimeout();
|
|
@@ -5460,13 +5464,13 @@ class OpenAICompatProvider {
|
|
|
5460
5464
|
reader.read().then((result) => {
|
|
5461
5465
|
clearTimeout(idleTimer);
|
|
5462
5466
|
if (isTimeout())
|
|
5463
|
-
reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
|
|
5467
|
+
reject(new Error(t(sawToolCallStart ? "error.llm_stream_idle_toolcall" : "error.llm_stream_idle", { timeout: idleTimeoutMs })));
|
|
5464
5468
|
else
|
|
5465
5469
|
resolve(result);
|
|
5466
5470
|
}, (err) => {
|
|
5467
5471
|
clearTimeout(idleTimer);
|
|
5468
5472
|
if (isTimeout())
|
|
5469
|
-
reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
|
|
5473
|
+
reject(new Error(t(sawToolCallStart ? "error.llm_stream_idle_toolcall" : "error.llm_stream_idle", { timeout: idleTimeoutMs })));
|
|
5470
5474
|
else
|
|
5471
5475
|
reject(err);
|
|
5472
5476
|
});
|
|
@@ -5504,11 +5508,14 @@ class OpenAICompatProvider {
|
|
|
5504
5508
|
}
|
|
5505
5509
|
const delta = choice.delta || {};
|
|
5506
5510
|
const finishReason = choice.finish_reason;
|
|
5511
|
+
if (finishReason)
|
|
5512
|
+
lastFinishReason = finishReason;
|
|
5507
5513
|
if (delta.reasoning_content) {
|
|
5508
5514
|
onEmit();
|
|
5509
5515
|
yield { type: "reasoning", content: delta.reasoning_content };
|
|
5510
5516
|
}
|
|
5511
5517
|
if (delta.tool_calls) {
|
|
5518
|
+
sawToolCallStart = true;
|
|
5512
5519
|
for (const tc of delta.tool_calls) {
|
|
5513
5520
|
const idx = tc.index ?? 0;
|
|
5514
5521
|
if (!toolCallAccs.has(idx)) {
|
|
@@ -5526,6 +5533,7 @@ class OpenAICompatProvider {
|
|
|
5526
5533
|
}
|
|
5527
5534
|
if (delta.content) {
|
|
5528
5535
|
onEmit();
|
|
5536
|
+
sawText = true;
|
|
5529
5537
|
yield { type: "text", content: delta.content };
|
|
5530
5538
|
}
|
|
5531
5539
|
if (finishReason === "tool_calls" && toolCallAccs.size > 0) {
|
|
@@ -5551,6 +5559,17 @@ class OpenAICompatProvider {
|
|
|
5551
5559
|
onEmit();
|
|
5552
5560
|
yield { type: "done", usage };
|
|
5553
5561
|
}
|
|
5562
|
+
if (lastFinishReason === "length") {
|
|
5563
|
+
const truncatedToolCall = sawToolCallStart && toolCallAccs.size > 0;
|
|
5564
|
+
if (truncatedToolCall || !sawText) {
|
|
5565
|
+
const err = new Error(t(truncatedToolCall ? "error.llm_truncated_toolcall" : "error.llm_truncated", {
|
|
5566
|
+
tokens: maxTokens
|
|
5567
|
+
}));
|
|
5568
|
+
err.llmTerminal = true;
|
|
5569
|
+
err.recoverableLlm = true;
|
|
5570
|
+
throw err;
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5554
5573
|
} finally {
|
|
5555
5574
|
cleanup();
|
|
5556
5575
|
reader.releaseLock();
|
|
@@ -7127,7 +7146,12 @@ var init_read_file = __esm(() => {
|
|
|
7127
7146
|
remaining: String(total - end),
|
|
7128
7147
|
next: String(end + 1)
|
|
7129
7148
|
}) : "";
|
|
7130
|
-
const
|
|
7149
|
+
const fullRead = offset === 1 && end >= total;
|
|
7150
|
+
const display = fullRead ? "" : t("file.read_range", {
|
|
7151
|
+
from: String(offset),
|
|
7152
|
+
to: String(end),
|
|
7153
|
+
total: String(total)
|
|
7154
|
+
});
|
|
7131
7155
|
return {
|
|
7132
7156
|
success: true,
|
|
7133
7157
|
output: `${header}
|
|
@@ -8628,7 +8652,7 @@ Hint: ${t("exec.npm_exec_hint")}`;
|
|
|
8628
8652
|
}
|
|
8629
8653
|
return output;
|
|
8630
8654
|
}
|
|
8631
|
-
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, BG_OUTPUT_PREVIEW_LINES = 15, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, NPM_EXEC_RE, bashTool;
|
|
8655
|
+
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, BG_OUTPUT_PREVIEW_LINES = 15, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, NPM_EXEC_RE, HIDDEN_PROCESS_TOOLS, bashTool;
|
|
8632
8656
|
var init_bash = __esm(() => {
|
|
8633
8657
|
init_command_validator();
|
|
8634
8658
|
init_audit_log();
|
|
@@ -8694,6 +8718,7 @@ var init_bash = __esm(() => {
|
|
|
8694
8718
|
]);
|
|
8695
8719
|
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/;
|
|
8696
8720
|
NPM_EXEC_RE = /could not determine executable to run/i;
|
|
8721
|
+
HIDDEN_PROCESS_TOOLS = ["process_list", "process_log", "process_kill"];
|
|
8697
8722
|
bashTool = {
|
|
8698
8723
|
name: "bash",
|
|
8699
8724
|
icon: "\uD83D\uDCBB",
|
|
@@ -8728,6 +8753,13 @@ ${redirected.output}`
|
|
|
8728
8753
|
};
|
|
8729
8754
|
}
|
|
8730
8755
|
const command = adaptCommandForWindows(originalCommand);
|
|
8756
|
+
const baseCmd = originalCommand.trim().split(/\s+/)[0];
|
|
8757
|
+
if (HIDDEN_PROCESS_TOOLS.includes(baseCmd) && ctx.toolExecutor?.hasTool?.(baseCmd)) {
|
|
8758
|
+
return {
|
|
8759
|
+
success: false,
|
|
8760
|
+
output: t("exec.hidden_tool_hint", { tool: baseCmd })
|
|
8761
|
+
};
|
|
8762
|
+
}
|
|
8731
8763
|
if (platform2() === "win32") {
|
|
8732
8764
|
const echoWrite = extractEchoFileWrite(originalCommand);
|
|
8733
8765
|
if (echoWrite) {
|
|
@@ -8930,6 +8962,14 @@ var init_process_log = __esm(() => {
|
|
|
8930
8962
|
required: ["id"]
|
|
8931
8963
|
},
|
|
8932
8964
|
handler: async (ctx, args) => {
|
|
8965
|
+
if (args.id === undefined || String(args.id).trim() === "") {
|
|
8966
|
+
const known = processRegistry.list();
|
|
8967
|
+
const ids = known.map((p) => p.id);
|
|
8968
|
+
const listPart = ids.length ? `
|
|
8969
|
+
${t("proc.list_header")}: ${ids.join(", ")}` : `
|
|
8970
|
+
${t("proc.missing_id_list")}`;
|
|
8971
|
+
return { success: false, output: `${t("proc.missing_id")}${listPart}` };
|
|
8972
|
+
}
|
|
8933
8973
|
const id = String(args.id);
|
|
8934
8974
|
const entry = processRegistry.get(id);
|
|
8935
8975
|
if (!entry) {
|
|
@@ -8971,6 +9011,14 @@ var init_process_kill = __esm(() => {
|
|
|
8971
9011
|
required: ["id"]
|
|
8972
9012
|
},
|
|
8973
9013
|
handler: async (ctx, args) => {
|
|
9014
|
+
if (args.id === undefined || String(args.id).trim() === "") {
|
|
9015
|
+
const known = processRegistry.list();
|
|
9016
|
+
const ids = known.map((p) => p.id);
|
|
9017
|
+
const listPart = ids.length ? `
|
|
9018
|
+
${t("proc.list_header")}: ${ids.join(", ")}` : `
|
|
9019
|
+
${t("proc.missing_id_list")}`;
|
|
9020
|
+
return { success: false, output: `${t("proc.missing_id")}${listPart}` };
|
|
9021
|
+
}
|
|
8974
9022
|
const id = String(args.id);
|
|
8975
9023
|
const entry = processRegistry.get(id);
|
|
8976
9024
|
if (!entry) {
|
|
@@ -10490,6 +10538,7 @@ class StuckDetector {
|
|
|
10490
10538
|
lastBashOutput = "";
|
|
10491
10539
|
emptyBashRunCount = 0;
|
|
10492
10540
|
readOnlyStreak = 0;
|
|
10541
|
+
bashRecent = [];
|
|
10493
10542
|
errorSignatureCounts = new Map;
|
|
10494
10543
|
searchedErrorSignatures = new Set;
|
|
10495
10544
|
webSearchThreshold;
|
|
@@ -10544,6 +10593,20 @@ class StuckDetector {
|
|
|
10544
10593
|
this.lastBashCommand = command;
|
|
10545
10594
|
this.lastBashOutput = output;
|
|
10546
10595
|
}
|
|
10596
|
+
recordBashAttempt(success) {
|
|
10597
|
+
this.bashRecent.push(success);
|
|
10598
|
+
if (this.bashRecent.length > BASH_FLAIL_WINDOW)
|
|
10599
|
+
this.bashRecent.shift();
|
|
10600
|
+
}
|
|
10601
|
+
hasBashFlailing() {
|
|
10602
|
+
if (this.bashRecent.length < BASH_FLAIL_THRESHOLD)
|
|
10603
|
+
return false;
|
|
10604
|
+
let failures = 0;
|
|
10605
|
+
for (const ok of this.bashRecent)
|
|
10606
|
+
if (!ok)
|
|
10607
|
+
failures++;
|
|
10608
|
+
return failures >= BASH_FLAIL_THRESHOLD;
|
|
10609
|
+
}
|
|
10547
10610
|
getLastBashCommand() {
|
|
10548
10611
|
return this.lastBashCommand;
|
|
10549
10612
|
}
|
|
@@ -10580,6 +10643,12 @@ class StuckDetector {
|
|
|
10580
10643
|
this.consecutiveFailures = 0;
|
|
10581
10644
|
this.lastFailedTool = "";
|
|
10582
10645
|
this.escalationCount = 0;
|
|
10646
|
+
for (const [tool, count] of this.toolErrors) {
|
|
10647
|
+
if (count <= 1)
|
|
10648
|
+
this.toolErrors.delete(tool);
|
|
10649
|
+
else
|
|
10650
|
+
this.toolErrors.set(tool, count - 1);
|
|
10651
|
+
}
|
|
10583
10652
|
if (this.iterationsOnCurrentStep > 0) {
|
|
10584
10653
|
this.iterationsOnCurrentStep = Math.max(0, this.iterationsOnCurrentStep - 1);
|
|
10585
10654
|
}
|
|
@@ -10770,6 +10839,22 @@ class StuckDetector {
|
|
|
10770
10839
|
count: this.repetitionThreshold
|
|
10771
10840
|
});
|
|
10772
10841
|
}
|
|
10842
|
+
getWarningKey() {
|
|
10843
|
+
if (this.isStuck())
|
|
10844
|
+
return "stuck";
|
|
10845
|
+
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
10846
|
+
if (errorTool)
|
|
10847
|
+
return `tool-errors:${errorTool[0]}`;
|
|
10848
|
+
if (this.hasBashFlailing())
|
|
10849
|
+
return "bash-flailing";
|
|
10850
|
+
if (this.hasConsecutiveFailures())
|
|
10851
|
+
return "consecutive";
|
|
10852
|
+
if (this.hasRepetitiveToolCalls())
|
|
10853
|
+
return "repetitive";
|
|
10854
|
+
if (this.hasReadOnlyLoop())
|
|
10855
|
+
return "read-only";
|
|
10856
|
+
return "";
|
|
10857
|
+
}
|
|
10773
10858
|
getStuckReason() {
|
|
10774
10859
|
if (this.isStuck()) {
|
|
10775
10860
|
return t("exec.stuck", {
|
|
@@ -10810,6 +10895,13 @@ class StuckDetector {
|
|
|
10810
10895
|
count: this.consecutiveFailures
|
|
10811
10896
|
});
|
|
10812
10897
|
}
|
|
10898
|
+
if (reason === "bash-flailing") {
|
|
10899
|
+
const fails = this.bashRecent.filter((ok) => !ok).length;
|
|
10900
|
+
return t("exec.bash_flailing_recovery", {
|
|
10901
|
+
fails: String(fails),
|
|
10902
|
+
window: String(this.bashRecent.length)
|
|
10903
|
+
});
|
|
10904
|
+
}
|
|
10813
10905
|
if (reason === "repetitive") {
|
|
10814
10906
|
return this.getRepetitiveToolMessage();
|
|
10815
10907
|
}
|
|
@@ -10826,6 +10918,8 @@ class StuckDetector {
|
|
|
10826
10918
|
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
10827
10919
|
if (errorTool)
|
|
10828
10920
|
return "tool-errors";
|
|
10921
|
+
if (this.hasBashFlailing())
|
|
10922
|
+
return "bash-flailing";
|
|
10829
10923
|
if (this.hasConsecutiveFailures())
|
|
10830
10924
|
return "consecutive";
|
|
10831
10925
|
if (this.hasRepetitiveToolCalls())
|
|
@@ -10847,6 +10941,7 @@ class StuckDetector {
|
|
|
10847
10941
|
this.lastBashCommand = "";
|
|
10848
10942
|
this.lastBashOutput = "";
|
|
10849
10943
|
this.emptyBashRunCount = 0;
|
|
10944
|
+
this.bashRecent = [];
|
|
10850
10945
|
this.readOnlyStreak = 0;
|
|
10851
10946
|
this.errorSignatureCounts.clear();
|
|
10852
10947
|
this.searchedErrorSignatures.clear();
|
|
@@ -10863,6 +10958,7 @@ class StuckDetector {
|
|
|
10863
10958
|
this.lastBashCommand = "";
|
|
10864
10959
|
this.lastBashOutput = "";
|
|
10865
10960
|
this.emptyBashRunCount = 0;
|
|
10961
|
+
this.bashRecent = [];
|
|
10866
10962
|
this.errorSignatureCounts.clear();
|
|
10867
10963
|
this.searchedErrorSignatures.clear();
|
|
10868
10964
|
}
|
|
@@ -10878,12 +10974,13 @@ class StuckDetector {
|
|
|
10878
10974
|
this.lastBashCommand = "";
|
|
10879
10975
|
this.lastBashOutput = "";
|
|
10880
10976
|
this.emptyBashRunCount = 0;
|
|
10977
|
+
this.bashRecent = [];
|
|
10881
10978
|
this.readOnlyStreak = 0;
|
|
10882
10979
|
this.errorSignatureCounts.clear();
|
|
10883
10980
|
this.searchedErrorSignatures.clear();
|
|
10884
10981
|
}
|
|
10885
10982
|
}
|
|
10886
|
-
var READ_ONLY_TOOLS, READ_ONLY_LOOP_THRESHOLD = 10, ANSI_RE, ERROR_LOC_RE, SIGNATURE_MAX_CHARS = 120, SEARCHABLE_PATTERNS, RUNTIME_WORDS, FRAMEWORK_WORDS;
|
|
10983
|
+
var READ_ONLY_TOOLS, READ_ONLY_LOOP_THRESHOLD = 10, BASH_FLAIL_WINDOW = 6, BASH_FLAIL_THRESHOLD = 4, ANSI_RE, ERROR_LOC_RE, SIGNATURE_MAX_CHARS = 120, SEARCHABLE_PATTERNS, RUNTIME_WORDS, FRAMEWORK_WORDS;
|
|
10887
10984
|
var init_stuck_detector = __esm(() => {
|
|
10888
10985
|
init_i18n();
|
|
10889
10986
|
READ_ONLY_TOOLS = new Set([
|
|
@@ -12592,6 +12689,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12592
12689
|
let suppressRepetitionRetry = false;
|
|
12593
12690
|
let repeatedToolCount = 0;
|
|
12594
12691
|
const MAX_REPEATED_TOOL_CALLS = 2;
|
|
12692
|
+
let llmErrorRetries = 0;
|
|
12595
12693
|
let allToolsForBudget = toolExecutor.getToolDefinitions(this.deps.toolTags);
|
|
12596
12694
|
let boundedToolNames = new Set(allToolsForBudget.filter((t2) => t2.boundedOutput).map((t2) => t2.name));
|
|
12597
12695
|
let toolTokenEstimate = allToolsForBudget.reduce((sum, t2) => sum + Math.ceil((t2.description.length + JSON.stringify(t2.parameters).length) / 4), 0);
|
|
@@ -12716,6 +12814,17 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12716
12814
|
logger.info("LLM call aborted (interrupt)");
|
|
12717
12815
|
break;
|
|
12718
12816
|
}
|
|
12817
|
+
if (err?.recoverableLlm && llmErrorRetries < MAX_LLM_ERROR_RETRIES) {
|
|
12818
|
+
llmErrorRetries++;
|
|
12819
|
+
logger.warn(`Recoverable LLM error, feeding back (${llmErrorRetries}/${MAX_LLM_ERROR_RETRIES}): ${err.message}`);
|
|
12820
|
+
slog.logError(err.message);
|
|
12821
|
+
pluginManager.runOnError({ iteration, logger, contextManager }, err);
|
|
12822
|
+
contextManager.addMessage({
|
|
12823
|
+
role: "user",
|
|
12824
|
+
content: `<system-summary>${err.message}</system-summary>`
|
|
12825
|
+
});
|
|
12826
|
+
continue;
|
|
12827
|
+
}
|
|
12719
12828
|
logger.logLLMResponse(config.model, textContent.length, Date.now() - llmStart, err.message, "agent");
|
|
12720
12829
|
logger.error(`LLM call failed: ${err.message}`);
|
|
12721
12830
|
slog.logError(err.message);
|
|
@@ -13211,7 +13320,7 @@ ${warnLine}
|
|
|
13211
13320
|
});
|
|
13212
13321
|
}
|
|
13213
13322
|
}
|
|
13214
|
-
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, FORCED_COMPACTION_COOLDOWN = 3;
|
|
13323
|
+
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, FORCED_COMPACTION_COOLDOWN = 3, MAX_LLM_ERROR_RETRIES = 2;
|
|
13215
13324
|
var init_agent = __esm(() => {
|
|
13216
13325
|
init_manager();
|
|
13217
13326
|
init_i18n();
|
|
@@ -18034,6 +18143,16 @@ import { spawn as spawn6, execSync } from "child_process";
|
|
|
18034
18143
|
import { existsSync as existsSync32, readFileSync as readFileSync17 } from "fs";
|
|
18035
18144
|
import { resolve as resolve18, extname as extname4, join as join26 } from "path";
|
|
18036
18145
|
import { platform as platform5 } from "os";
|
|
18146
|
+
function lintCacheKey(baseDir, lintScript) {
|
|
18147
|
+
return `${baseDir}::${lintScript}`;
|
|
18148
|
+
}
|
|
18149
|
+
function isMissingBinaryError(err, combinedOutput) {
|
|
18150
|
+
if (err.status === 127 || err.status === 9009)
|
|
18151
|
+
return true;
|
|
18152
|
+
if (err.code === "ENOENT")
|
|
18153
|
+
return true;
|
|
18154
|
+
return MISSING_BINARY_RE.test(combinedOutput);
|
|
18155
|
+
}
|
|
18037
18156
|
function contentHash(content) {
|
|
18038
18157
|
let h = 5381;
|
|
18039
18158
|
for (let i = 0;i < content.length; i++) {
|
|
@@ -18042,24 +18161,43 @@ function contentHash(content) {
|
|
|
18042
18161
|
return String(h);
|
|
18043
18162
|
}
|
|
18044
18163
|
function getWinDecoder() {
|
|
18045
|
-
if (_winDecoder
|
|
18046
|
-
|
|
18047
|
-
|
|
18048
|
-
|
|
18049
|
-
|
|
18050
|
-
|
|
18051
|
-
|
|
18052
|
-
|
|
18053
|
-
|
|
18054
|
-
|
|
18055
|
-
|
|
18056
|
-
|
|
18057
|
-
|
|
18058
|
-
|
|
18059
|
-
|
|
18164
|
+
if (_winDecoder !== undefined)
|
|
18165
|
+
return _winDecoder;
|
|
18166
|
+
if (platform5() !== "win32") {
|
|
18167
|
+
_winDecoder = new TextDecoder("utf-8");
|
|
18168
|
+
return _winDecoder;
|
|
18169
|
+
}
|
|
18170
|
+
let decoder = null;
|
|
18171
|
+
try {
|
|
18172
|
+
const cpOut = execSync("chcp.com", {
|
|
18173
|
+
encoding: "buffer",
|
|
18174
|
+
timeout: 2000,
|
|
18175
|
+
windowsHide: true
|
|
18176
|
+
}).toString("latin1");
|
|
18177
|
+
const m = cpOut.match(/(\d+)/);
|
|
18178
|
+
if (m)
|
|
18179
|
+
decoder = new TextDecoder("ibm" + m[1]);
|
|
18180
|
+
} catch {}
|
|
18181
|
+
if (!decoder) {
|
|
18182
|
+
try {
|
|
18183
|
+
decoder = new TextDecoder("ibm866");
|
|
18184
|
+
} catch {
|
|
18185
|
+
return new TextDecoder("utf-8");
|
|
18060
18186
|
}
|
|
18061
18187
|
}
|
|
18062
|
-
|
|
18188
|
+
_winDecoder = decoder;
|
|
18189
|
+
return _winDecoder;
|
|
18190
|
+
}
|
|
18191
|
+
function decodeOutput(chunks) {
|
|
18192
|
+
const raw = Buffer.concat(chunks);
|
|
18193
|
+
try {
|
|
18194
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(raw);
|
|
18195
|
+
} catch {}
|
|
18196
|
+
try {
|
|
18197
|
+
return getWinDecoder().decode(raw);
|
|
18198
|
+
} catch {
|
|
18199
|
+
return raw.toString("latin1");
|
|
18200
|
+
}
|
|
18063
18201
|
}
|
|
18064
18202
|
|
|
18065
18203
|
class LintOnWritePlugin {
|
|
@@ -18143,23 +18281,35 @@ class LintOnWritePlugin {
|
|
|
18143
18281
|
return null;
|
|
18144
18282
|
}
|
|
18145
18283
|
async runProjectLint(ctx, result, signal) {
|
|
18284
|
+
let lintScript;
|
|
18146
18285
|
try {
|
|
18147
18286
|
const packageJsonPath = join26(ctx.baseDir, "package.json");
|
|
18148
18287
|
if (!existsSync32(packageJsonPath)) {
|
|
18149
18288
|
return;
|
|
18150
18289
|
}
|
|
18151
18290
|
const packageJson = JSON.parse(readFileSync17(packageJsonPath, "utf-8"));
|
|
18152
|
-
|
|
18291
|
+
lintScript = packageJson.scripts?.lint;
|
|
18153
18292
|
if (!lintScript) {
|
|
18154
18293
|
return;
|
|
18155
18294
|
}
|
|
18295
|
+
if (missingLintBinaries.has(lintCacheKey(ctx.baseDir, lintScript))) {
|
|
18296
|
+
ctx.logger.debug(`Lint skipped (linter for "${lintScript}" not installed)`);
|
|
18297
|
+
return;
|
|
18298
|
+
}
|
|
18156
18299
|
ctx.logger.debug(`Running lint: ${lintScript}`);
|
|
18157
18300
|
await runAsync(lintScript, ctx.baseDir, 30000, signal);
|
|
18158
18301
|
ctx.logger.debug("Lint passed");
|
|
18159
18302
|
} catch (err) {
|
|
18160
18303
|
const stderr = (err.stderr?.toString?.() || "").trim();
|
|
18161
18304
|
const stdout = (err.stdout?.toString?.() || "").trim();
|
|
18162
|
-
const
|
|
18305
|
+
const combined = `${stderr}
|
|
18306
|
+
${stdout}`;
|
|
18307
|
+
if (lintScript && isMissingBinaryError(err, combined)) {
|
|
18308
|
+
missingLintBinaries.add(lintCacheKey(ctx.baseDir, lintScript));
|
|
18309
|
+
ctx.logger.debug(`Lint binary missing — skipping project lint for this session`);
|
|
18310
|
+
return;
|
|
18311
|
+
}
|
|
18312
|
+
const detail = combined.trim().split(`
|
|
18163
18313
|
`).filter(Boolean).slice(-5).join(`
|
|
18164
18314
|
`);
|
|
18165
18315
|
ctx.logger.warn(`Lint failed: ${err.message}`);
|
|
@@ -18219,14 +18369,13 @@ function runAsync(command, cwd, timeoutMs, signal) {
|
|
|
18219
18369
|
windowsHide: true,
|
|
18220
18370
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18221
18371
|
});
|
|
18222
|
-
|
|
18223
|
-
|
|
18224
|
-
const decoder = getWinDecoder();
|
|
18372
|
+
const utf8Chunks = [];
|
|
18373
|
+
const oemChunks = [];
|
|
18225
18374
|
child.stdout?.on("data", (d) => {
|
|
18226
|
-
|
|
18375
|
+
utf8Chunks.push(Buffer.from(d));
|
|
18227
18376
|
});
|
|
18228
18377
|
child.stderr?.on("data", (d) => {
|
|
18229
|
-
|
|
18378
|
+
oemChunks.push(Buffer.from(d));
|
|
18230
18379
|
});
|
|
18231
18380
|
const timer = setTimeout(() => {
|
|
18232
18381
|
child.kill();
|
|
@@ -18242,7 +18391,7 @@ function runAsync(command, cwd, timeoutMs, signal) {
|
|
|
18242
18391
|
clearTimeout(timer);
|
|
18243
18392
|
signal?.removeEventListener("abort", onAbort);
|
|
18244
18393
|
if (signal?.aborted) {
|
|
18245
|
-
resolve19({ stdout, stderr });
|
|
18394
|
+
resolve19({ stdout: decodeOutput(utf8Chunks), stderr: decodeOutput(oemChunks) });
|
|
18246
18395
|
} else {
|
|
18247
18396
|
reject(err);
|
|
18248
18397
|
}
|
|
@@ -18250,8 +18399,8 @@ function runAsync(command, cwd, timeoutMs, signal) {
|
|
|
18250
18399
|
child.on("close", (code) => {
|
|
18251
18400
|
clearTimeout(timer);
|
|
18252
18401
|
signal?.removeEventListener("abort", onAbort);
|
|
18253
|
-
stdout
|
|
18254
|
-
stderr
|
|
18402
|
+
const stdout = decodeOutput(utf8Chunks);
|
|
18403
|
+
const stderr = decodeOutput(oemChunks);
|
|
18255
18404
|
if (signal?.aborted || code === 0) {
|
|
18256
18405
|
resolve19({ stdout, stderr });
|
|
18257
18406
|
} else {
|
|
@@ -18264,9 +18413,11 @@ function runAsync(command, cwd, timeoutMs, signal) {
|
|
|
18264
18413
|
});
|
|
18265
18414
|
});
|
|
18266
18415
|
}
|
|
18267
|
-
var TYPE_CHECK_DEBOUNCE_MS = 2000, syntaxCache, _winDecoder, plugin;
|
|
18416
|
+
var TYPE_CHECK_DEBOUNCE_MS = 2000, missingLintBinaries, MISSING_BINARY_RE, syntaxCache, _winDecoder, plugin;
|
|
18268
18417
|
var init_lint_on_write = __esm(() => {
|
|
18269
18418
|
init_project_root();
|
|
18419
|
+
missingLintBinaries = new Set;
|
|
18420
|
+
MISSING_BINARY_RE = /command not found|not recognized|не является внутренней|не распознан|не є внутрішньою|".+?" не является/i;
|
|
18270
18421
|
syntaxCache = new Map;
|
|
18271
18422
|
plugin = new LintOnWritePlugin;
|
|
18272
18423
|
});
|
|
@@ -18294,6 +18445,13 @@ function generatePlanId() {
|
|
|
18294
18445
|
}
|
|
18295
18446
|
return `plan_${id}`;
|
|
18296
18447
|
}
|
|
18448
|
+
function getPlanProgress(plan) {
|
|
18449
|
+
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
18450
|
+
const skipped = plan.steps.filter((s) => s.status === "skipped").length;
|
|
18451
|
+
const total = plan.steps.length;
|
|
18452
|
+
const settled = done + skipped;
|
|
18453
|
+
return { done, skipped, total, settled, terminal: total > 0 && settled === total };
|
|
18454
|
+
}
|
|
18297
18455
|
|
|
18298
18456
|
class PlanCreator {
|
|
18299
18457
|
static createPlan(title, stepDescriptions, baseDir, kinds) {
|
|
@@ -18334,9 +18492,9 @@ class PlanCreator {
|
|
|
18334
18492
|
}
|
|
18335
18493
|
static toPromptBlock(plan, currentStepIndex) {
|
|
18336
18494
|
const date = plan.createdAt.slice(0, 10);
|
|
18337
|
-
const
|
|
18338
|
-
const
|
|
18339
|
-
const progress = terminal ? `${
|
|
18495
|
+
const p = getPlanProgress(plan);
|
|
18496
|
+
const skippedPart = p.skipped > 0 ? `, ${p.skipped} skipped` : "";
|
|
18497
|
+
const progress = p.terminal ? `${p.settled}/${p.total} done${skippedPart}, complete` : `${p.settled}/${p.total} done${skippedPart}, current: step ${currentStepIndex + 1}`;
|
|
18340
18498
|
const lines = [
|
|
18341
18499
|
`[${plan.id}] ${plan.title}`,
|
|
18342
18500
|
`Dir: ${plan.baseDir}`,
|
|
@@ -18402,13 +18560,15 @@ class PlanTracker {
|
|
|
18402
18560
|
return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
|
|
18403
18561
|
}
|
|
18404
18562
|
getProgressString() {
|
|
18405
|
-
const
|
|
18406
|
-
const total =
|
|
18407
|
-
const
|
|
18563
|
+
const p = getPlanProgress(this.plan);
|
|
18564
|
+
const total = p.total;
|
|
18565
|
+
const settled = p.settled;
|
|
18566
|
+
const pct = total > 0 ? Math.round(settled / total * 100) : 0;
|
|
18408
18567
|
const barWidth = 10;
|
|
18409
|
-
const filled = Math.round(
|
|
18568
|
+
const filled = Math.round(settled / total * barWidth);
|
|
18410
18569
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
18411
|
-
|
|
18570
|
+
const skippedPart = p.skipped > 0 ? ` (${p.skipped} skipped)` : "";
|
|
18571
|
+
return `[${this.plan.id}] ${this.plan.title} ${settled}/${total}${skippedPart} ${bar} ${pct}%`;
|
|
18412
18572
|
}
|
|
18413
18573
|
toPromptBlock() {
|
|
18414
18574
|
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
@@ -18786,13 +18946,20 @@ function createExecutionPlugin(deps) {
|
|
|
18786
18946
|
}
|
|
18787
18947
|
const stuckReason = deps.stuckDetector.getStuckReason();
|
|
18788
18948
|
if (stuckReason) {
|
|
18789
|
-
const
|
|
18949
|
+
const isStuck = deps.stuckDetector.isStuck();
|
|
18950
|
+
const warnKey = deps.stuckDetector.getWarningKey();
|
|
18951
|
+
const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
18952
|
+
const logIt = isStuck ? !deps.state.stuckNotified : warnKey !== deps.state.lastStuckWarnKey || iter - deps.state.lastStuckWarnIter >= STUCK_WARN_REPEAT_EVERY;
|
|
18790
18953
|
if (logIt) {
|
|
18791
18954
|
ctx.logger?.warn(stuckReason);
|
|
18792
18955
|
ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
|
|
18793
|
-
|
|
18956
|
+
deps.state.lastStuckWarnKey = warnKey;
|
|
18957
|
+
deps.state.lastStuckWarnIter = iter;
|
|
18958
|
+
if (isStuck)
|
|
18794
18959
|
deps.state.stuckNotified = true;
|
|
18795
18960
|
}
|
|
18961
|
+
} else {
|
|
18962
|
+
deps.state.lastStuckWarnKey = "";
|
|
18796
18963
|
}
|
|
18797
18964
|
if (deps.stuckDetector.isStuck() || deps.stuckDetector.hasRepetitiveToolCalls() || deps.stuckDetector.hasReadOnlyLoop()) {
|
|
18798
18965
|
const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
@@ -18917,6 +19084,9 @@ Last compile error: ${first[1]}`;
|
|
|
18917
19084
|
}
|
|
18918
19085
|
}
|
|
18919
19086
|
if (!result.success) {
|
|
19087
|
+
if (call.name === "bash") {
|
|
19088
|
+
deps.stuckDetector.recordBashAttempt(false);
|
|
19089
|
+
}
|
|
18920
19090
|
if (call.name === "bash" && platform6() === "win32") {
|
|
18921
19091
|
const cmd = String(call.arguments?.command ?? "");
|
|
18922
19092
|
const forbidden = forbiddenWindowsCommand(cmd);
|
|
@@ -18936,6 +19106,9 @@ Last compile error: ${first[1]}`;
|
|
|
18936
19106
|
}
|
|
18937
19107
|
} else {
|
|
18938
19108
|
deps.stuckDetector.recordToolSuccess();
|
|
19109
|
+
if (call.name === "bash") {
|
|
19110
|
+
deps.stuckDetector.recordBashAttempt(true);
|
|
19111
|
+
}
|
|
18939
19112
|
if (call.name === "bash" && result.success) {
|
|
18940
19113
|
const cmd = String(call.arguments?.command ?? "");
|
|
18941
19114
|
const testRun = detectTestResults(String(result.output ?? ""));
|
|
@@ -18973,7 +19146,7 @@ Last compile error: ${first[1]}`;
|
|
|
18973
19146
|
}
|
|
18974
19147
|
};
|
|
18975
19148
|
}
|
|
18976
|
-
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10, PLAN_NUDGE_THRESHOLD = 2;
|
|
19149
|
+
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10, STUCK_WARN_REPEAT_EVERY = 5, PLAN_NUDGE_THRESHOLD = 2;
|
|
18977
19150
|
var init_execution_plugin = __esm(() => {
|
|
18978
19151
|
init_i18n();
|
|
18979
19152
|
init_bash();
|
|
@@ -19228,12 +19401,12 @@ ${display}`,
|
|
|
19228
19401
|
deps.store.archivePlan(plan);
|
|
19229
19402
|
deps.recordCompleted(plan);
|
|
19230
19403
|
deps.trackerRef.current = null;
|
|
19231
|
-
const
|
|
19404
|
+
const p = getPlanProgress(plan);
|
|
19232
19405
|
return {
|
|
19233
19406
|
success: true,
|
|
19234
19407
|
output: t("plan.completed_archived", {
|
|
19235
19408
|
id: plan.id,
|
|
19236
|
-
done: String(
|
|
19409
|
+
done: String(p.settled),
|
|
19237
19410
|
total: String(plan.steps.length)
|
|
19238
19411
|
})
|
|
19239
19412
|
};
|
|
@@ -19327,13 +19500,13 @@ ${t("plan.no_deliverables", { step: String(stepId) })}` : "";
|
|
|
19327
19500
|
deps.store.archivePlan(plan);
|
|
19328
19501
|
deps.recordCompleted(plan);
|
|
19329
19502
|
deps.trackerRef.current = null;
|
|
19330
|
-
const
|
|
19503
|
+
const p = getPlanProgress(plan);
|
|
19331
19504
|
return {
|
|
19332
19505
|
success: true,
|
|
19333
19506
|
output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
|
|
19334
19507
|
${t("plan.completed_archived", {
|
|
19335
19508
|
id: plan.id,
|
|
19336
|
-
done: String(
|
|
19509
|
+
done: String(p.settled),
|
|
19337
19510
|
total: String(plan.steps.length)
|
|
19338
19511
|
})}${vacuousNote}`
|
|
19339
19512
|
};
|
|
@@ -19348,8 +19521,15 @@ ${progress}${vacuousNote}`,
|
|
|
19348
19521
|
display
|
|
19349
19522
|
};
|
|
19350
19523
|
}
|
|
19351
|
-
if (action === "update" && Array.isArray(args.steps) && args.steps.length > 0 &&
|
|
19524
|
+
if (action === "update" && Array.isArray(args.steps) && args.steps.length > 0 && !args.step) {
|
|
19352
19525
|
const tracker = deps.trackerRef.current;
|
|
19526
|
+
if (!tracker) {
|
|
19527
|
+
return { success: false, output: t("plan.no_active") };
|
|
19528
|
+
}
|
|
19529
|
+
const progressed = tracker.getPlan().steps.some((s) => s.status !== "pending");
|
|
19530
|
+
if (progressed) {
|
|
19531
|
+
return { success: false, output: t("plan.bulk_update_rejected") };
|
|
19532
|
+
}
|
|
19353
19533
|
const title = String(args.title || tracker.getPlan().title);
|
|
19354
19534
|
const steps = args.steps.map(String);
|
|
19355
19535
|
const parsed = deps.parseKinds(args, steps);
|
|
@@ -19436,6 +19616,15 @@ ${progress}${vacuousNote}`,
|
|
|
19436
19616
|
};
|
|
19437
19617
|
}
|
|
19438
19618
|
if (!deps.trackerRef.current) {
|
|
19619
|
+
if (args.id) {
|
|
19620
|
+
const found = deps.store.find(String(args.id));
|
|
19621
|
+
if (found && found.status !== "active") {
|
|
19622
|
+
return {
|
|
19623
|
+
success: false,
|
|
19624
|
+
output: t("plan.readonly", { id: found.plan.id, status: found.status })
|
|
19625
|
+
};
|
|
19626
|
+
}
|
|
19627
|
+
}
|
|
19439
19628
|
return { success: false, output: t("plan.no_active") };
|
|
19440
19629
|
}
|
|
19441
19630
|
return {
|
|
@@ -19610,6 +19799,8 @@ class ExecutionModule {
|
|
|
19610
19799
|
consecutivePlanWarnings: 0,
|
|
19611
19800
|
lastStepId: -1,
|
|
19612
19801
|
stuckNotified: false,
|
|
19802
|
+
lastStuckWarnKey: "",
|
|
19803
|
+
lastStuckWarnIter: -STUCK_WARN_REPEAT_EVERY,
|
|
19613
19804
|
depsGateHints: new Map,
|
|
19614
19805
|
mutationsWithoutPlan: 0,
|
|
19615
19806
|
planNudgeSent: false,
|
|
@@ -33279,8 +33470,9 @@ function stripAnsi2(s) {
|
|
|
33279
33470
|
function charLen(s) {
|
|
33280
33471
|
return Array.from(s).length;
|
|
33281
33472
|
}
|
|
33282
|
-
function wrapLine(line, width) {
|
|
33473
|
+
function wrapLine(line, width, firstRowWidth) {
|
|
33283
33474
|
const w = width > 0 ? width : 80;
|
|
33475
|
+
let limit = firstRowWidth !== undefined && firstRowWidth > 0 ? firstRowWidth : w;
|
|
33284
33476
|
const chars = Array.from(line);
|
|
33285
33477
|
const rows = [];
|
|
33286
33478
|
let start = 0;
|
|
@@ -33295,10 +33487,11 @@ function wrapLine(line, width) {
|
|
|
33295
33487
|
continue;
|
|
33296
33488
|
}
|
|
33297
33489
|
const cw = stringWidth(ch);
|
|
33298
|
-
if (cells + cw >
|
|
33490
|
+
if (cells + cw > limit && cells > 0) {
|
|
33299
33491
|
rows.push({ text: chars.slice(start, i).join(""), charStart: start });
|
|
33300
33492
|
start = i;
|
|
33301
33493
|
cells = cw;
|
|
33494
|
+
limit = w;
|
|
33302
33495
|
} else {
|
|
33303
33496
|
cells += cw;
|
|
33304
33497
|
}
|
|
@@ -33306,10 +33499,10 @@ function wrapLine(line, width) {
|
|
|
33306
33499
|
rows.push({ text: chars.slice(start).join(""), charStart: start });
|
|
33307
33500
|
return rows;
|
|
33308
33501
|
}
|
|
33309
|
-
function buildLayout(lines, width) {
|
|
33502
|
+
function buildLayout(lines, width, firstRowWidth) {
|
|
33310
33503
|
const rows = [];
|
|
33311
33504
|
for (let b = 0;b < lines.length; b++) {
|
|
33312
|
-
for (const r of wrapLine(lines[b], width)) {
|
|
33505
|
+
for (const r of wrapLine(lines[b], width, b === 0 ? firstRowWidth : undefined)) {
|
|
33313
33506
|
rows.push({ bufRow: b, charStart: r.charStart, text: r.text });
|
|
33314
33507
|
}
|
|
33315
33508
|
}
|
|
@@ -33346,6 +33539,7 @@ class LineEditor {
|
|
|
33346
33539
|
input;
|
|
33347
33540
|
output;
|
|
33348
33541
|
onKeyInput;
|
|
33542
|
+
onInterrupt;
|
|
33349
33543
|
promptStr;
|
|
33350
33544
|
completer;
|
|
33351
33545
|
history;
|
|
@@ -33364,11 +33558,15 @@ class LineEditor {
|
|
|
33364
33558
|
rawMode = false;
|
|
33365
33559
|
listeners = {};
|
|
33366
33560
|
prevCursorRow = 0;
|
|
33561
|
+
frameTopAbs = null;
|
|
33562
|
+
dsrPending = false;
|
|
33563
|
+
dsrTail = "";
|
|
33367
33564
|
constructor(opts) {
|
|
33368
33565
|
this.input = opts.input;
|
|
33369
33566
|
this.output = opts.output;
|
|
33370
33567
|
this.promptStr = opts.prompt ?? "";
|
|
33371
33568
|
this.completer = opts.completer;
|
|
33569
|
+
this.onInterrupt = opts.onInterrupt;
|
|
33372
33570
|
this.history = opts.history ?? [];
|
|
33373
33571
|
this.historySize = opts.historySize ?? 50;
|
|
33374
33572
|
if (this.history.length > this.historySize) {
|
|
@@ -33379,6 +33577,7 @@ class LineEditor {
|
|
|
33379
33577
|
if (this.rawMode)
|
|
33380
33578
|
this.input.setRawMode(true);
|
|
33381
33579
|
this.enableTerminalProtocols();
|
|
33580
|
+
this.input.on("data", (buf) => this.consumeDsrReplies(buf.toString("utf-8")));
|
|
33382
33581
|
readline2.emitKeypressEvents(this.input);
|
|
33383
33582
|
this.input.on("keypress", (str, key) => this.onKey(str, key));
|
|
33384
33583
|
} else {
|
|
@@ -33421,7 +33620,7 @@ class LineEditor {
|
|
|
33421
33620
|
this.col = 0;
|
|
33422
33621
|
this.historyIndex = -1;
|
|
33423
33622
|
this.stash = null;
|
|
33424
|
-
this.
|
|
33623
|
+
this.invalidateAnchor();
|
|
33425
33624
|
if (this.input.isTTY)
|
|
33426
33625
|
this.render();
|
|
33427
33626
|
}
|
|
@@ -33431,7 +33630,7 @@ class LineEditor {
|
|
|
33431
33630
|
this.lines = [""];
|
|
33432
33631
|
this.row = 0;
|
|
33433
33632
|
this.col = 0;
|
|
33434
|
-
this.
|
|
33633
|
+
this.invalidateAnchor();
|
|
33435
33634
|
if (this.input.isTTY)
|
|
33436
33635
|
this.render();
|
|
33437
33636
|
}
|
|
@@ -33651,10 +33850,10 @@ class LineEditor {
|
|
|
33651
33850
|
this.commitFrame();
|
|
33652
33851
|
this.output.write(`\r
|
|
33653
33852
|
`);
|
|
33853
|
+
this.invalidateAnchor();
|
|
33654
33854
|
this.lines = [""];
|
|
33655
33855
|
this.row = 0;
|
|
33656
33856
|
this.col = 0;
|
|
33657
|
-
this.prevCursorRow = 0;
|
|
33658
33857
|
cb(answer);
|
|
33659
33858
|
return;
|
|
33660
33859
|
}
|
|
@@ -33671,17 +33870,21 @@ class LineEditor {
|
|
|
33671
33870
|
this.commitFrame();
|
|
33672
33871
|
this.output.write(`\r
|
|
33673
33872
|
`);
|
|
33873
|
+
this.invalidateAnchor();
|
|
33674
33874
|
this.lines = [""];
|
|
33675
33875
|
this.row = 0;
|
|
33676
33876
|
this.col = 0;
|
|
33677
|
-
this.prevCursorRow = 0;
|
|
33678
33877
|
this.emitLine(text);
|
|
33679
33878
|
}
|
|
33680
33879
|
commitFrame() {
|
|
33681
|
-
const layout = this.
|
|
33880
|
+
const layout = this.visibleLayout();
|
|
33881
|
+
const lastLen = charLen(stripAnsi2(layout[layout.length - 1].text));
|
|
33882
|
+
if (this.frameTopAbs !== null) {
|
|
33883
|
+
this.output.write(`\x1B[${this.frameTopAbs + layout.length - 1};${lastLen + 1}H`);
|
|
33884
|
+
return;
|
|
33885
|
+
}
|
|
33682
33886
|
const idx = layoutIndexFor(layout, this.row, this.col);
|
|
33683
33887
|
const rowsDown = layout.length - 1 - idx;
|
|
33684
|
-
const lastLen = charLen(stripAnsi2(layout[layout.length - 1].text));
|
|
33685
33888
|
const out = [];
|
|
33686
33889
|
if (rowsDown > 0)
|
|
33687
33890
|
out.push(`\x1B[${rowsDown}B`);
|
|
@@ -33694,9 +33897,15 @@ class LineEditor {
|
|
|
33694
33897
|
this.lines = [""];
|
|
33695
33898
|
this.row = 0;
|
|
33696
33899
|
this.col = 0;
|
|
33697
|
-
this.
|
|
33900
|
+
this.invalidateAnchor();
|
|
33698
33901
|
this.render();
|
|
33699
|
-
|
|
33902
|
+
if (this.onInterrupt) {
|
|
33903
|
+
this.onInterrupt();
|
|
33904
|
+
return;
|
|
33905
|
+
}
|
|
33906
|
+
if (process.listenerCount("SIGINT") > 0) {
|
|
33907
|
+
process.emit("SIGINT");
|
|
33908
|
+
}
|
|
33700
33909
|
}
|
|
33701
33910
|
ctrlD() {
|
|
33702
33911
|
if (this.lines.length === 1 && this.lines[0] === "") {
|
|
@@ -33963,28 +34172,68 @@ class LineEditor {
|
|
|
33963
34172
|
}
|
|
33964
34173
|
clearScreen() {
|
|
33965
34174
|
this.output.write("\x1B[2J\x1B[H");
|
|
33966
|
-
this.
|
|
34175
|
+
this.invalidateAnchor();
|
|
33967
34176
|
this.render();
|
|
33968
34177
|
}
|
|
33969
34178
|
columns() {
|
|
33970
34179
|
return this.output.columns || 80;
|
|
33971
34180
|
}
|
|
33972
34181
|
layout() {
|
|
33973
|
-
|
|
34182
|
+
const promptWidth = stringWidth(stripAnsi2(this.promptStr));
|
|
34183
|
+
return buildLayout(this.lines, this.columns(), Math.max(1, this.columns() - promptWidth));
|
|
34184
|
+
}
|
|
34185
|
+
screenRows() {
|
|
34186
|
+
const r = this.output.rows;
|
|
34187
|
+
return typeof r === "number" && r > 3 ? r : null;
|
|
34188
|
+
}
|
|
34189
|
+
visibleLayout() {
|
|
34190
|
+
const layout = this.layout();
|
|
34191
|
+
const H = this.screenRows();
|
|
34192
|
+
const maxRows = H ? H - 2 : 0;
|
|
34193
|
+
if (maxRows > 0 && layout.length > maxRows) {
|
|
34194
|
+
return layout.slice(layout.length - maxRows);
|
|
34195
|
+
}
|
|
34196
|
+
return layout;
|
|
34197
|
+
}
|
|
34198
|
+
invalidateAnchor() {
|
|
34199
|
+
this.frameTopAbs = null;
|
|
34200
|
+
this.dsrPending = false;
|
|
34201
|
+
this.dsrTail = "";
|
|
34202
|
+
this.prevCursorRow = 0;
|
|
34203
|
+
}
|
|
34204
|
+
consumeDsrReplies(chunk) {
|
|
34205
|
+
this.dsrTail = (this.dsrTail + chunk).slice(-80);
|
|
34206
|
+
const m = /\x1b\[(\d+);(\d+)R/.exec(this.dsrTail);
|
|
34207
|
+
if (!m)
|
|
34208
|
+
return;
|
|
34209
|
+
this.dsrTail = "";
|
|
34210
|
+
if (!this.dsrPending)
|
|
34211
|
+
return;
|
|
34212
|
+
this.dsrPending = false;
|
|
34213
|
+
const row = Number(m[1]);
|
|
34214
|
+
const idx = Number(m[2]);
|
|
34215
|
+
const top = row - this.prevCursorRow;
|
|
34216
|
+
if (top >= 1)
|
|
34217
|
+
this.frameTopAbs = top;
|
|
33974
34218
|
}
|
|
33975
34219
|
render() {
|
|
33976
34220
|
if (!this.input.isTTY || this.isDone)
|
|
33977
34221
|
return;
|
|
33978
|
-
const layout = this.
|
|
34222
|
+
const layout = this.visibleLayout();
|
|
33979
34223
|
const promptWidth = stringWidth(stripAnsi2(this.promptStr));
|
|
33980
34224
|
const idx = layoutIndexFor(layout, this.row, this.col);
|
|
33981
34225
|
const vcol = visualColAt(layout, idx, this.col);
|
|
33982
34226
|
const rowsAfter = layout.length - idx - 1;
|
|
33983
34227
|
const ccol = (idx === 0 ? promptWidth : 0) + vcol;
|
|
33984
34228
|
const out = [];
|
|
33985
|
-
if (this.
|
|
34229
|
+
if (this.frameTopAbs !== null) {
|
|
34230
|
+
out.push(`\x1B[${this.frameTopAbs};1H`);
|
|
34231
|
+
} else if (this.prevCursorRow > 0) {
|
|
33986
34232
|
out.push(`\x1B[${this.prevCursorRow}A`);
|
|
33987
|
-
|
|
34233
|
+
out.push("\r");
|
|
34234
|
+
} else {
|
|
34235
|
+
out.push("\r");
|
|
34236
|
+
}
|
|
33988
34237
|
for (let i = 0;i < layout.length; i++) {
|
|
33989
34238
|
out.push("\x1B[2K");
|
|
33990
34239
|
if (i === 0)
|
|
@@ -34000,8 +34249,17 @@ class LineEditor {
|
|
|
34000
34249
|
out.push("\r");
|
|
34001
34250
|
if (ccol > 0)
|
|
34002
34251
|
out.push(`\x1B[${ccol}C`);
|
|
34003
|
-
this.prevCursorRow = idx;
|
|
34004
34252
|
this.output.write(out.join(""));
|
|
34253
|
+
if (this.frameTopAbs !== null) {
|
|
34254
|
+
const H = this.screenRows();
|
|
34255
|
+
if (H) {
|
|
34256
|
+
const T = this.frameTopAbs;
|
|
34257
|
+
const L = layout.length;
|
|
34258
|
+
if (T + L - 1 > H)
|
|
34259
|
+
this.frameTopAbs = H - L + 1;
|
|
34260
|
+
}
|
|
34261
|
+
} else if (!this.dsrPending) {}
|
|
34262
|
+
this.prevCursorRow = idx;
|
|
34005
34263
|
}
|
|
34006
34264
|
enableTerminalProtocols() {
|
|
34007
34265
|
this.output.write("\x1B[?2004h");
|
|
@@ -34358,9 +34616,28 @@ init_box();
|
|
|
34358
34616
|
init_table();
|
|
34359
34617
|
init_i18n();
|
|
34360
34618
|
init_prices();
|
|
34619
|
+
import { isAbsolute as isAbsolute4, relative as relative6, sep as sep2 } from "path";
|
|
34361
34620
|
function formatUsd(cost) {
|
|
34362
34621
|
return formatCost(cost);
|
|
34363
34622
|
}
|
|
34623
|
+
var PATH_TOOLS = new Set([
|
|
34624
|
+
"read_file",
|
|
34625
|
+
"write_file",
|
|
34626
|
+
"edit_file",
|
|
34627
|
+
"delete_file",
|
|
34628
|
+
"create_dir",
|
|
34629
|
+
"move_file",
|
|
34630
|
+
"list_dir",
|
|
34631
|
+
"file_info"
|
|
34632
|
+
]);
|
|
34633
|
+
function toDisplayPath(baseDir, p) {
|
|
34634
|
+
if (!baseDir || !isAbsolute4(p))
|
|
34635
|
+
return p;
|
|
34636
|
+
const rel = relative6(baseDir, p);
|
|
34637
|
+
if (!rel || rel.startsWith("..") || isAbsolute4(rel))
|
|
34638
|
+
return p;
|
|
34639
|
+
return rel.split(sep2).join("/");
|
|
34640
|
+
}
|
|
34364
34641
|
var GUTTER = " ";
|
|
34365
34642
|
var BUSY_TOOLS = new Set(["lsp_check"]);
|
|
34366
34643
|
function toolMarker(tool) {
|
|
@@ -34414,6 +34691,7 @@ class Renderer {
|
|
|
34414
34691
|
err;
|
|
34415
34692
|
width;
|
|
34416
34693
|
toolStyle;
|
|
34694
|
+
baseDir;
|
|
34417
34695
|
card = null;
|
|
34418
34696
|
constructor(opts = {}) {
|
|
34419
34697
|
this.rich = opts.rich ?? isRichTerminal();
|
|
@@ -34421,6 +34699,7 @@ class Renderer {
|
|
|
34421
34699
|
this.err = opts.err ?? process.stderr;
|
|
34422
34700
|
this.width = opts.width ?? getTerminalWidth();
|
|
34423
34701
|
this.toolStyle = opts.toolStyle ?? "inline";
|
|
34702
|
+
this.baseDir = opts.baseDir;
|
|
34424
34703
|
this.spinner = new Spinner({
|
|
34425
34704
|
enabled: this.rich && (opts.spinner ?? true),
|
|
34426
34705
|
stream: this.err,
|
|
@@ -34468,7 +34747,8 @@ class Renderer {
|
|
|
34468
34747
|
toolStart(tool, args, stepContext, icon) {
|
|
34469
34748
|
this.endCard();
|
|
34470
34749
|
this.spinner.stop();
|
|
34471
|
-
const
|
|
34750
|
+
const displayArgs = PATH_TOOLS.has(tool) && typeof args.path === "string" ? { ...args, path: toDisplayPath(this.baseDir, args.path) } : args;
|
|
34751
|
+
const summary = summarizeArgs2(displayArgs);
|
|
34472
34752
|
const step = stepContext ? ` ${pc2.cyan(`← ${stepContext}`)}` : "";
|
|
34473
34753
|
const marker = icon || toolMarker(tool);
|
|
34474
34754
|
if (!this.rich) {
|
|
@@ -34979,7 +35259,10 @@ class Repl {
|
|
|
34979
35259
|
this.rl.onKeyInput = (str, key) => this.handleSpecialKey(str, key);
|
|
34980
35260
|
}
|
|
34981
35261
|
let forceExitTimer = null;
|
|
34982
|
-
|
|
35262
|
+
if (typeof Repl.sigintHandler === "function") {
|
|
35263
|
+
process.removeListener("SIGINT", Repl.sigintHandler);
|
|
35264
|
+
}
|
|
35265
|
+
Repl.sigintHandler = () => {
|
|
34983
35266
|
if (this.agentRunning) {
|
|
34984
35267
|
console.log(pc2.yellow(t("repl.ctrl_c_interrupt")));
|
|
34985
35268
|
this.agent.shutdown();
|
|
@@ -34990,7 +35273,8 @@ class Repl {
|
|
|
34990
35273
|
} else {
|
|
34991
35274
|
process.exit(0);
|
|
34992
35275
|
}
|
|
34993
|
-
}
|
|
35276
|
+
};
|
|
35277
|
+
process.on("SIGINT", Repl.sigintHandler);
|
|
34994
35278
|
}
|
|
34995
35279
|
handleSpecialKey(str, key) {
|
|
34996
35280
|
if (key.name === "escape") {
|
|
@@ -35068,7 +35352,8 @@ ${t("image.clipboard_empty")}`));
|
|
|
35068
35352
|
` + pc2.green(t("repl.agent")));
|
|
35069
35353
|
const renderer = new Renderer({
|
|
35070
35354
|
spinner: this.config.ui?.spinner ?? true,
|
|
35071
|
-
toolStyle: this.config.ui?.toolStyle ?? "inline"
|
|
35355
|
+
toolStyle: this.config.ui?.toolStyle ?? "inline",
|
|
35356
|
+
baseDir: this.baseDir
|
|
35072
35357
|
});
|
|
35073
35358
|
const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
|
|
35074
35359
|
if (ev.type === "start") {
|
|
@@ -35550,7 +35835,7 @@ async function main() {
|
|
|
35550
35835
|
}
|
|
35551
35836
|
if (program2.args.length > 0) {
|
|
35552
35837
|
const prompt = program2.args.join(" ");
|
|
35553
|
-
const { agent, config } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
35838
|
+
const { agent, config, baseDir } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
35554
35839
|
const updater = exitOnComplete ? undefined : startAutoUpdate(config);
|
|
35555
35840
|
if (jsonMode) {
|
|
35556
35841
|
const result2 = await agent.run(prompt);
|
|
@@ -35575,7 +35860,8 @@ async function main() {
|
|
|
35575
35860
|
}
|
|
35576
35861
|
const renderer = new Renderer({
|
|
35577
35862
|
spinner: config.ui?.spinner ?? true,
|
|
35578
|
-
toolStyle: config.ui?.toolStyle ?? "inline"
|
|
35863
|
+
toolStyle: config.ui?.toolStyle ?? "inline",
|
|
35864
|
+
baseDir
|
|
35579
35865
|
});
|
|
35580
35866
|
const result = await agent.run(prompt, (chunk) => renderer.text(chunk), (meta) => renderer.meta(meta), (ev) => {
|
|
35581
35867
|
if (ev.type === "start") {
|