Перейти к содержимому

Терминал

Агент Kodik выполняет команды через инструмент shell в фоновом процессе, без терминала. Каждый вызов привязан к рабочему каталогу текущей задачи и виден в чате; команда, которая не только читает, требует подтверждения, если её не покрывают правила авто-одобрения. Выполнение команды никогда не открывает и не перехватывает встроенный терминал.

Команды учитывают добавление и удаление переменных окружения в выбранном профиле оболочки. Значение null удаляет переменную из окружения команды, а пустая строка остаётся пустым значением. Эти настройки применяются к команде, не меняя окружение самого Kodik.

Когда агент вызывает shell, команда появляется в чате со статусом pending. Вы можете одобрить или отклонить её. После выполнения агент получает ограниченный результат: исходный вывод команды, а над ним — сведения о выполнении. Для завершившейся команды это только код завершения; идентификатор терминала приходит, только пока команда работает или после её остановки, а размер вывода — только если вывод был сокращён. Текст, похожий на ошибку, не меняет статус успешно завершившейся команды.

Команды выполняются в оболочке вашего терминального профиля по умолчанию (Terminal: Select Default Profile), и агент пишет команды в синтаксисе именно этой оболочки — например Git Bash, Command Prompt, Cygwin или PowerShell на Windows, либо zsh на macOS. Профили, добавленные расширениями, а также fish и nushell вместо этого используют платформенную оболочку, потому что не предоставляют исполняемый путь.

Команды запускаются с закрытым stdin, обычным текстовым выводом и настоящим кодом завершения процесса. Kodik использует выбранный исполняемый профиль оболочки с неинтерактивным login-вызовом, где это поддерживается (-lc для POSIX-оболочек, -Command для PowerShell и /c для Command Prompt). Если профиль входа POSIX-оболочки сбрасывает PATH, как это делают /etc/profile в Debian и большинство образов контейнеров, Kodik возвращает удалённые профилем записи PATH, с которыми он был запущен, перед записями самого профиля. В профилях Bash, zsh, ksh и mksh также включён pipefail, поэтому успешная последняя стадия, например tail, не скрывает ошибку предыдущей стадии. PowerShell применяет собственный статус конвейера и возвращает ошибку инструмента при сбое конвейера. stdout и stderr объединяются в порядке прихода; управляющие ANSI-последовательности удаляются, а переменные pager направляются в cat. TERM и NO_COLOR остаются такими, какие они у самого Kodik, поэтому собственные тесты проекта видят те же настройки терминала и цвета, что и в любой другой неинтерактивной оболочке. Агент получает вывод в том виде, в каком его напечатала команда: отступ первой строки сохраняется, а строка, которую команда перерисовывает через возврат каретки (например, индикатор прогресса), показывается в последнем состоянии. Команду, которой нужны TTY, пароль, SSH/аутентификация, редактор, REPL или другой интерактивный prompt, так выполнить нельзя — агент попросит вас запустить её в своём терминале.

Для команд агента Kodik убирает интерактивные флаги профиля, в том числе объединённые -il и именованные опции zsh. Поэтому настройки prompt и темы из интерактивных startup-файлов не мешают выполнению команды; login-профиль, например .zprofile, и остальные параметры сохраняются. Сам сохранённый профиль терминала не меняется.

Исключение из pipefail — конвейер с кодом 141: если следующий этап, например head, прочитал нужное и закрыл канал, предыдущий этап получает SIGPIPE, и команда всё равно считается успешной, а агент получает пояснение.

В конец PATH команд агента добавляется ripgrep, который поставляется с Kodik, поэтому rg работает даже там, где он не установлен. Установленный вами rg стоит в PATH раньше и используется в первую очередь.

Пока Kodik получает результат команды, работа в других чатах не удаляет её код завершения и вывод. После получения результата данные снова доступны обычной ограниченной очистке.

Каждая команда запускается в новой оболочке в корне рабочей области (или рабочего дерева задачи), поэтому cd и export не переносятся в следующую команду. Чтобы выполнить команду в другой папке, агент передаёт параметр workdir — путь относительно корня рабочей области или абсолютный. Эта папка показывается в карточке команды под самой командой. Команда в защищённой папке, например .git, требует подтверждения, даже если совпадает с шаблоном авто-одобрения. Если папки нет, агент получает сообщение Working directory does not exist.

Kodik понимает и названия параметров, которые используют другие агенты: cwd, working_directory и dir_path означают workdir, а timeout_ms — timeout.

Kodik получает код завершения и вывод команды непосредственно от дочернего процесса. Он ждёт закрытия процесса, поэтому даже очень быстрые команды не отображаются как не выведшие ничего. Если по выводу видно, что команде нужны терминал или ввод, результат подсказывает агенту передать её вам, а не повторять.

Вывод команды остаётся свёрнутым в карточке чата. Большой вывод хранится в нативной сессии и не копируется в каждое обновление чата; при открытии раздела Command Output Kodik загружает и показывает сохранённый вывод, в том числе после перезагрузки окна. Вывод открывается прокрученным к концу, как в терминале; шеврон вверху показывает более ранние строки по частям и загружает их из сессии только по запросу. Если чтение временно завершается ошибкой, карточка показывает её и действие Повторить. Ограниченный результат, передаваемый агенту, при этом не сокращается.

Пока команда выводит данные, чат объединяет промежуточные обновления в окна по 250 мс и пропускает неизменившиеся кадры, уменьшая число записей на диск. Переход в фон, остановка и финальный результат сохраняются без ожидания этого окна. Сбор вывода продолжается между обновлениями, а сохранённый финальный вывод доступен после перезагрузки окна.

Агент читает не больше 16 КБ вывода команды. Если вывод больше, Kodik сокращает его один раз: оставляет начало и последние строки по границам строк, отмечает, сколько строк из середины пропущено, и сохраняет полный вывод в файл, который агент может прочитать. Строка обрывается посередине, только если она одна длиннее лимита, и тогда — на корректной границе Unicode. Усечение не меняет идентификатор, статус, код завершения или отмену команды.

Завершение команды с очень большим журналом не задерживает чат: Kodik сохраняет полный вывод через передачу файла, не обрабатывая весь журнал в окне чата. Большие цветные журналы и перерисовки прогресса сохраняют читаемый вывод без загрузки всего журнала в память. Это действует и для удалённых команд. Собранный вывод остаётся на машине, где выполнялась команда, до успешного сохранения файла восстановления, поэтому временная ошибка копирования не уничтожает его.

Для команды с большим объёмом вывода Kodik также записывает полный вывод в управляемый локальный артефакт. В карточке команды появляется действие Открыть полный вывод с размером файла; открытие в редакторе не добавляет эти данные в контекст модели. Если агент позднее попросит прочитать артефакт, продолжают действовать обычное подтверждение чтения файла и правила защиты чувствительных данных.

Артефакты полного вывода изолированы по командам и сессиям, доступны только владельцу в POSIX-системах, ограничены 512 МиБ на файл и удаляются через семь дней либо при превышении 100 файлов или 2 ГиБ общего кэша. При удалении чата удаляются и его артефакты. Если файл уже очищен, превысил лимит или не смог записаться, карточка показывает Полный вывод недоступен, а не устаревшие или выдуманные данные.

Временные файлы вывода команд и промежуточные файлы нормализации в POSIX-системах также доступны только владельцу.

Если размер вывода превышает доступное контекстное окно, Kodik автоматически запускает сжатие контекста перед возвратом результата.

Если запустившая команда завершилась, а отделённый процесс всё ещё держит её канал вывода открытым, Kodik собирает поздний вывод до одной секунды тишины, затем завершает команду. Отделённый процесс продолжает работать, но его последующий вывод уже не попадает в карточку этой команды. Если сам процесс команды ещё работает, его обычный фоновый статус сохраняется.

Агент ждёт завершения команды 5 секунд, прежде чем продолжить. Параметром timeout он может попросить ждать дольше, до минуты, — для сборки или тестов, которые должны закончиться за это время; более короткий timeout считается 5 секундами, а более длинный — минутой, и вызов не отклоняется. Команда, которая ещё работает, когда ожидание заканчивается, не завершается и не перезапускается: она продолжает работать в фоне под своим terminal_id, а агент получает вывод на этот момент и пометку продолжать работу, которой эта команда не нужна, или завершить ход и ждать: результат команды придёт сам, когда она завершится.

Если агент не задал свой timeout, ожидание заканчивается раньше в двух случаях: вывод команды показывает запущенный сервер или режим наблюдения (например, Local: http://localhost:5173 или Watching for file changes) и две секунды не меняется, или агент вызвал несколько инструментов сразу, а команда ещё работает через секунду после завершения другого инструмента, в том числе вызова, который вы отклонили, или вопроса, на который вы ответили, — тогда готовые результаты доходят до агента, не дожидаясь команды. Сообщение, которое вы отправляете, пока агент ждёт команду, сразу прерывает ожидание, даже если агент сам задал более длинный таймаут, и доходит до агента вместе с terminal_id команды.

Проверять фоновую команду агенту ничто не запрещает, поэтому он может следить и за командой, которая не завершается, например за dev-сервером. Результат приходит так же, как у любой фоновой команды. Когда чат простаивает, первая завершившаяся команда откладывает пробуждение на полсекунды, поэтому команды, завершившиеся за это время, будят чат один раз, и агент читает их результаты в том порядке, в каком команды завершились. Результат, который пришёл, пока агент был занят другим шагом, и не дошёл до него до перезагрузки окна или закрытия Kodik, сохраняется вместе с карточкой команды: когда чат открывается снова, он будит чат, и агент читает его один раз — ровно таким, каким получил бы его раньше. Каждое сообщение о завершении команды (выходе, вашей остановке или завершении вместе с окном) называет вызов агента, который её запустил, — его Call ID — и говорит, что оно пришло от Kodik, а не от вас, поэтому агент соотнесёт его с этим вызовом даже после того, как длинный разговор был сжат. Если у чата ещё работают другие фоновые команды или субагенты, последнее из таких сообщений, которые агент получает за один раз, заканчивается их списком с временем работы каждого, чтобы агент видел, чего он ещё ждёт; если больше ничего не работает, списка нет. В этот список не входят серверы и watch-процессы: если вывод команды две секунды показывает, что она запущена и ждёт, например строкой с её локальным адресом, до перевода в фон или в любой момент после, это сервер, который сам не завершается, поэтому его никто не ждёт. О его завершении агент всё равно узнаёт. Пока чат простаивает и ждёт другую фоновую работу, пульс будит агента, если 5 минут ничего не приходит: он перечисляет, что ещё работает, для каждой команды — сколько она работает и её последнюю строку вывода, и называет, что агент может сделать: проверить команду, остановить её или ждать дальше. Пульс не приходит, пока идёт ход, и после остановки чата, а в запуске с --kodik-unattended тоже приходит. Если разговор сжимается, пока фоновая работа ещё идёт, сводка заканчивается списком всего, что работает на этот момент, включая серверы, и список остаётся в сводке в том виде, в каком был записан. Если агент проверяет такую команду через get_terminal_output, пока она ещё работает, он получает новый вывод и пометку, что результат придёт сам, когда команда завершится, а повторная проверка не ускорит его, поэтому агент занимается другой работой или завершает ход, пока результат его не разбудит. То же сказано и в инструкциях агента, поэтому он не опрашивает команду, которая должна завершиться. Команда, которая только ждёт, например sleep 30 сама по себе или вместе с echo или true, в фон не переходит: она выполняется до минуты или до более короткого timeout агента и заканчивается, как только завершится одна из фоновых команд чата или отчитается субагент, поэтому агент получает этот результат на том же шаге. В её результате сказано, что её завершило или что она дошла до предела. Существующий чат переходит на это поведение со следующего хода, и первый запрос этого хода не берётся из кэша промпта. У headless CLI и агента ACP своё ожидание: по умолчанию две минуты, а с timeout — от одной секунды до пяти минут.

# Запуск dev-сервера
terminal_id = shell("npm run dev") # через 5 секунд он ещё работает, поэтому продолжает работать в фоне
# Проверка прогресса при необходимости
get_terminal_output(terminal_id=terminal_id)
# Остановка по завершении (или для перезапуска)
kill_terminal(terminal_id=terminal_id)

Когда фоновый процесс завершается сам, Kodik автоматически обновляет исходную карточку shell его финальным статусом и выводом и один раз сообщает об этом агенту: код выхода, сколько работала команда, и только тот вывод, которого агент ещё не видел. Если агент уже получил этот результат, перезагрузка окна не доставляет его повторно и не запускает для него ещё один ход. Во время активного хода агент получает сообщение о завершении вместе со следующим результатом инструмента, в том числе результатом правки файла, и оно остаётся в разговоре и дальше; если агент уже ответил, завершение запускает в чате новый ход, чтобы агент сообщил результат, — без добавления сообщения от вашего имени. Если вы остановили чат или его последний ход завершился ошибкой, сообщение о завершении дождётся вашего следующего сообщения; пока выполняется цель (Goal), оно попадает в её следующий ход. Процесс, который вы остановили сами, новый ход не запускает. Фоновый процесс принадлежит своему окну, поэтому перезагрузка или закрытие окна завершает его: карточка фиксируется как Отменено и остаётся такой после перезагрузки, а ваше следующее сообщение сообщит агенту, что команда завершилась вместе с окном.

Команда, которую запустил субагент, сообщает о завершении этому субагенту, а не основному агенту. Субагент, у которого ответ готов, пока его команда ещё работает, дожидается её результата и только потом отчитывается, если это не сервер и не watch-процесс; если 5 минут ничего не приходит, ему напоминают, какие команды ещё выполняются. В запуске с --kodik-unattended ответ чата заканчивает ход, как в любом чате: завершение команды будит чат, запуск не заканчивается, пока команда работает или её результат не дошёл до агента, а если 5 минут ничего не приходит, пульс будит агента списком того, что ещё работает. Сервер или watch-процесс не держит запуск открытым.

Если процесс окна неожиданно завершается, пока агент работает, — например, когда большой прогон тестов исчерпывает память и система его убивает, — ход чата сразу останавливается: команда, которую он ждал, сообщается агенту как прерванная, а не выполняется, пока кто-то не ответит на диалог о сбое. В запуске с --kodik-unattended, где на этот диалог некому ответить, окно перезагружается само (до трёх раз), и чат продолжается с пометкой, которая объясняет агенту, что произошло.

Агент вызывает get_terminal_output только для проверки текущего прогресса или новых логов до завершения; блокирующий параметр wait_ms позволяет обойтись без частого опроса. Так агент может самостоятельно мониторить логи сервера, не требуя от вас вставлять вывод вручную.

Каждый результат get_terminal_output содержит вывод только соответствующей фоновой команды. Возвращённый terminal_id остаётся доступным для проверки, даже если фоновая команда завершилась до следующего запроса статуса. Если процесс завершился без пригодного кода завершения, get_terminal_output сообщает об ошибке, а не о внутреннем значении вроде undefined. Когда Kodik подтверждает, что в сессии нет фоновых терминалов, повторные попытки с другими идентификаторами останавливаются, а агент получает указание изменить подход.

Фоновая команда сохраняет собственный идентификатор процесса, поэтому она никогда не блокирует следующую команду агента — агент может запустить dev-сервер и сразу же выполнять команды против него. Когда фоновый процесс больше не нужен или его требуется перезапустить после изменения, агент останавливает его вызовом kill_terminal(terminal_id); группа процессов завершается, а идентификатор освобождается. Фоновые команды также видны в меню терминалов на вкладке сессии и в её строке на боковой панели, и вы можете остановить любую из них там.

Кнопка Остановить завершает команды, которые агент ещё ждёт, команды его субагентов и команды, которые этот ход сам перевёл в фон: потому что истекло ожидание, потому что его остальные вызовы инструментов уже завершились или потому что вы отправили сообщение, пока он ждал. Продолжают работать серверы и наблюдатели, команды, которые вы сами перевели в фон, и команды, переведённые в фон предыдущими ходами; они остаются в меню терминалов сессии. Ваше следующее сообщение один раз сообщает агенту, какие команды вы остановили, — каждую как событие Terminal event с её Call ID и идентификатором терминала, — а затем перечисляет то, что ещё работает, даже если остановка ничего не завершила. Если продолжившая работать команда завершится после остановки, агент не запустится снова сам: сведения о завершении придут к нему вместе с вашим следующим сообщением. Кнопка Остановить также отменяет команду во время подготовки профиля оболочки. Такая команда не запустится после завершения подготовки.

Если ход агента закончился, пока его фоновые команды или субагенты ещё работают, чат принимает сообщения как обычно, а над полем ввода появляется строка о том, чего он ждёт, например «Ожидание: 2 фоновые команды; агент продолжит, когда всё завершится». Строка видна, только пока чат продолжит работу сам: она исчезает, пока идёт ход, в том числе ход, который начинает завершившаяся команда, и после остановки чата. Пока строка видна, вкладка чата и его строка в списке чатов показывают тот же значок ожидания, что и у чата, который ждёт субагентов, с подсказкой о том, чего он ждёт, например «ждёт: 2 фоновые команды», а уведомление о завершении приходит только после хода, который закончился, когда в фоне уже ничего не работает. Серверы и watch-процессы не считаются: чат, у которого остался только запущенный сервер, например dev-сервер, считается завершённым и получает уведомление, а если команда показывает, что она сервер, уже после конца хода, строка исчезает и уведомление приходит в этот момент. Её кнопка Остановить завершает всё, что она считает, и останавливает чат, так что ничто его не разбудит; серверы и watch-процессы продолжают работать, а ваше следующее сообщение один раз сообщает агенту, какие команды вы остановили.

Когда вы останавливаете команду или агент останавливает её вызовом kill_terminal, строка команды становится янтарной. Она не отображается как ошибка, которая окрашивает строку в красный, а действие остановки исчезает. Команда, которая сама завершилась с кодом 130 или получила сигнал прерывания, считается неудачной с этим кодом; остановленной считается только команда, которую остановили вы или агент.

Если команду остановили вы, следующий результат инструмента или ваше следующее сообщение один раз передаёт агенту событие Terminal event с остановленной командой. Остановка через kill_terminal не помечается как ваша: агент и так знает, что сам её запросил.

Некоторые команды никогда не одобряются автоматически — даже в режиме автопилота. Это защита от случайного удаления данных или деструктивных системных операций. По умолчанию заблокированы:

Удаление файлов:

  • rm *, sudo rm *
  • del *, rd *, rmdir *, Remove-Item *, ri *

Деструктивные команды Git:

  • git clean*

Поиск с удалением:

  • find * -delete*, find * -exec rm*

Работа с дисками:

  • mkfs*, dd if=*, format *

Системные операции:

  • shutdown*, reboot*

Прочие опасные:

  • > /dev/*, :(){:|:&};:, chmod -R 777 /, chown -R *:* /

Вы можете настроить список заблокированных команд в настройках авто-одобрения. Подробнее — в разделе Авто-одобрение.

Команды, которые только читают (cat, ls, rg, git log, sed -n '10,20p' и подобные), выполняются без запроса, как и инструменты чтения агента. Остальные команды агент проверяет по вашим шаблонам (например, npm run * или git *): команда, которая совпадает с шаблоном, не входит в список заблокированных и не пишет в защищённый путь, выполняется без запроса. В цепочке команд разрешён должен быть каждый сегмент: npm run * не одобряет следующий за ним сегмент curl ... | sh. Подробнее — в разделе Авто-одобрение.

В режиме автопилота одобряются все команды, кроме заблокированных. Уведомление отображается, если команда выполняется в фоне более 30 секунд.

Вы можете вручную добавить вывод из встроенного терминала VS Code в контекст чата с помощью команды Add Terminal Output to Chat (kodik.addTerminalOutputToChat). Команда доступна через контекстное меню терминала и через палитру команд. Это удобно, когда нужно показать агенту вывод команды, которую вы запускали самостоятельно.