Headless-режим¶
v0.1.6
Headless-режим Koda CLI выполняет запрос без интерактивного интерфейса и возвращает результат в stdout. Он предназначен для скриптов, пайплайнов CI/CD и программных клиентов, которым нужен машинночитаемый ответ.
В этом режиме TUI не рендерится: CLI принимает запрос, работает с моделью и инструментами, а затем завершает процесс с кодом, по которому можно судить об исходе.
Все флаги запуска перечислены в разделе неинтерактивного режима справочника аргументов.
Когда включается headless-режим¶
Headless-режим активируется автоматически, если соблюдено одно из условий:
- передан флаг
-p(или--prompt) с текстом запроса; - ввод подаётся через stdin, когда процесс запущен не в TTY-окружении (например, через конвейер);
- задан флаг
--input-format stream-json, даже если stdin подключён к терминалу.
В следующем примере команда передаётся через echo, а результат выводится в терминал:
Тот же результат достигается флагом --prompt:
Совместимость способов запуска
Содержимое stdin добавляется к тексту --prompt с разделителем в виде пустой строки.
Так можно комбинировать директиву из скрипта с динамическим вводом из конвейера.
Ввод¶
Источники ввода для headless-режима:
- флаг
--prompt; - stdin, если процесс запущен не в TTY и ввод не пуст;
- комбинация: stdin добавляется к запросу из
--prompt.
Лимит объёма stdin — 8 МБ. При превышении ввод усекается до лимита, а в stderr выводится предупреждение.
Проброс ввода через конвейер
Флаг --prompt-interactive (-i) нельзя использовать вместе с вводом из stdin.
Если -i передан, а процесс не запущен в TTY, CLI завершается с ошибкой и кодом 1.
Флаг --input-format stream-json заменяет одиночный ввод потоком сообщений: stdin читается построчно, а --prompt и --prompt-interactive с ним несовместимы.
Для работы без интерактивного интерфейса настройте способ аутентификации заранее через переменные окружения:
KODA_AUTH_ACCESS_TOKEN, KODA_AUTH_REFRESH_TOKEN или KODA_API_KEY.
Подробнее — в разделе аутентификации.
Вывод¶
В headless-режиме stdout и stderr разделены:
| Поток | Что попадает |
|---|---|
stdout |
ответ модели или машиночитаемый вывод в выбранном формате |
stderr |
reasoning модели, служебные сообщения и ошибки |
Формат задаёт флаг --output-format:
| Значение | Что печатает |
|---|---|
text (умолчание) |
только ответ модели в stdout |
json |
один JSON-объект с результатом и метриками в конце прогона |
stream-json |
поток событий NDJSON: одно событие — одна строка stdout |
Reasoning выводится при флаге --show-reasoning.
В режимах text и json он идёт в stderr, а в stream-json — отдельными событиями thought.
Последовательные переводы строк в ответе схлопываются до двух подряд, чтобы вывод оставался компактным.
Правило одинаково действует и в текстовом ответе, и в поле result структурированного вывода.
Если вывод направлен в команду, которая завершилась раньше, обрыв канала считается штатным завершением:
процесс выходит с кодом 0, не порождая ошибку EPIPE.
Формат json¶
v1.3.0
{
"type": "result",
"stop_reason": "final",
"is_error": false,
"result": "Финальный ответ модели",
"session_id": "0f1c…",
"prompt_id": "a7b3…",
"num_turns": 3,
"duration_ms": 8421,
"usage": {
"tokens": {
"prompt": 10240,
"candidates": 512,
"cached": 8192,
"thoughts": 96,
"tool": 48,
"total": 10752
},
"models": {
"koda-1": {
"requests": 3,
"errors": 0,
"latency_ms": 7310,
"tokens": {
"prompt": 10240,
"candidates": 512,
"cached": 8192,
"thoughts": 96,
"tool": 48,
"total": 10752
}
}
},
"tools": {
"calls": 2,
"success": 2,
"fail": 0,
"duration_ms": 640
}
},
"error": null
}
Объект печатается с отступами, поэтому читайте stdout целиком: построчный разбор рассчитан на режим stream-json.
Поля:
stop_reason— причина завершения:final(модель ответила),limit(упор в лимит витков),loop(обнаружен цикл),cancel(прогон отменён),error;is_error—trueтолько приstop_reason: "error";result— финальный текст ответа, тот же, что печатает текстовый режим;session_idиprompt_id— идентификаторы сессии и запроса;num_turns— сколько витков агента отработало;duration_ms— длительность прогона;usage— токены по моделям и суммарно, число запросов и вызовов инструментов;error— текст ошибки или сообщения о лимите, иначеnull.
Денежная стоимость не считается: у CLI нет таблицы цен.
Формат stream-json¶
v1.3.0
Каждая строка — отдельный JSON-объект.
Порядок событий: init, затем события прогона, последним — result с той же структурой, что и в режиме json.
type |
Поля | Когда |
|---|---|---|
init |
session_id, prompt_id, model |
в начале прогона |
thought |
text |
«мысль» модели (только с --show-reasoning) |
content |
text |
фрагмент ответа модели |
tool_request |
call_id, name, args |
модель запросила инструмент |
tool_result |
call_id, name, error, display |
инструмент отработал |
notice |
text |
служебное сообщение: ошибка инструмента, лимит |
result |
как в режиме json |
в конце прогона |
Коды завершения¶
v1.3.0
Код выхода различает успех и причину остановки:
| Код | stop_reason |
Значение |
|---|---|---|
0 |
final |
модель дала финальный ответ |
1 |
error |
ошибка API или прогона, а также некорректная строка ввода stream-json |
2 |
limit |
исчерпан лимит витков |
3 |
loop |
обнаружен цикл, прогон остановлен досрочно |
4 |
cancel |
прогон отменён |
Упор в лимит и обнаруженный цикл раньше завершались с кодом 0, поэтому программно их нельзя было отличить от успеха.
Теперь вызывающий скрипт видит причину по коду выхода.
Ошибки разбора аргументов и несовместимые комбинации флагов завершают процесс до старта прогона с кодом 1.
Если не передан --prompt и stdin пуст, процесс также завершается с кодом 1.
Ограничение числа шагов
Максимальное число ходов модели регулируется настройкой maxSessionTurns или флагом --max-turns.
Остальные настройки — на странице настройки.
Отчёт об использовании¶
v1.3.0
В режимах json и stream-json отчёт всегда входит в поле usage.
В текстовом режиме его выводит флаг --show-usage: отчёт печатается в stderr, чтобы не засорять stdout.
Отчёт содержит число витков, длительность, токены и количество вызовов инструментов.
Лимит витков¶
v1.3.0
Флаг перекрывает настройку maxSessionTurns и принимает целое число не меньше 1.
Упор в лимит даёт код выхода 2 и stop_reason: "limit".
Контроль инструментов¶
v1.3.0
koda -p "Почини auth.ts" --approval-mode yolo \
--allowed-tools read_file,write_file,search_file_content
koda -p "Разберись в коде" --disallowed-tools run_shell_command
--allowed-toolsоставляет модели только перечисленные встроенные инструменты — как настройкаcoreTools, но на один запуск;--disallowed-toolsисключает перечисленные инструменты и применяется поверх--allowed-tools;- оба флага принимают список через запятую или повторяются несколько раз.
Флаги не заменяют режим подтверждений: тот решает, спрашивать ли согласие, а эти — какие инструменты модель вообще видит.
В неинтерактивном режиме с --approval-mode default инструменты, требующие подтверждения, исключаются автоматически.
Добавка к системному промпту¶
v1.3.0
gh pr diff 123 | koda -p \
--append-system-prompt "Ты инженер по безопасности. Ищи уязвимости." \
--output-format json
Текст дописывается в конец системного промпта после памяти проекта из KODA.md.
Файлы проекта при этом не меняются.
Слеш-команды в -p¶
Если запрос начинается с /, он исполняется как слеш-команда:
- команда, выводящая текст, печатает его и завершает прогон без обращения к модели;
- команда, разворачивающаяся в промпт, отправляет этот промпт модели;
- команда, запрашивающая инструмент, исполняет его напрямую;
- команды, которым нужен интерактивный диалог или подтверждение, отклоняются с понятным сообщением.
В режимах json и stream-json служебные сообщения команд уходят в stderr, чтобы stdout оставался машиночитаемым.
Только для одиночного запроса
В режиме --input-format stream-json слеш-команды не исполняются: строка со / уходит модели как обычное сообщение.
Многовитковый диалог¶
v1.3.0
Флаг --input-format stream-json превращает stdin в поток сообщений: каждое сообщение обрабатывается в той же сессии, и на каждое печатается свой result.
Так программа ведёт диалог с одним процессом Koda.
printf '%s\n' \
'{"type":"user","text":"перечисли файлы в src"}' \
'{"type":"user","text":"а теперь опиши самый большой"}' \
'{"type":"control","action":"end"}' |
koda --input-format stream-json --output-format stream-json
Поддерживаемые строки:
| Строка | Значение |
|---|---|
{"type":"user","text":"…"} |
сообщение пользователя, короткая форма |
{"type":"user","message":{"content":"…"}} |
то же, совместимо с другими агентными CLI |
{"type":"user","message":{"content":[{"type":"text","text":"…"}]}} |
текст по частям |
{"type":"control","action":"end"} |
завершить диалог, как конец потока |
Пустые строки игнорируются.
Строка, которая не разбирается, завершает прогон с кодом 1 и stop_reason: "error".
Прогон также обрывается на первом сообщении с ненулевым кодом: лимит витков, цикл или ошибка.
Пустой поток завершается с кодом 0.
Событие init печатается один раз в начале диалога, а каждый result содержит свой prompt_id.
Идентификатор сессии session_id общий для всего диалога.
Несовместимость с одиночным вводом
--input-format stream-json нельзя использовать вместе с --prompt и --prompt-interactive: сообщения приходят только из stdin.
Подтверждение действий¶
По умолчанию в неинтерактивном режиме инструменты, требующие подтверждения, отключаются, чтобы агент не выполнял действия без вашего согласия.
Выбор режима подтверждения задаётся флагом --approval-mode:
| Режим | Что происходит |
|---|---|
default |
инструменты с подтверждением отключены |
auto_edit |
автоматически подтверждаются инструменты редактирования файлов |
yolo |
автоматически подтверждаются все инструменты |
Допустимо использовать -y (--yolo) вместо --approval-mode yolo, но не одновременно.
Управление компьютером
Инструмент computer_use в неинтерактивном режиме доступен только при --yolo: этот запуск не умеет спрашивать согласие на ввод.
В остальных режимах инструмент не предлагается модели.
Примеры автоматизации¶
Простой запрос с флагом --prompt:
Отправка вывода конвейером, reasoning не показывается:
Запрос с заданной моделью и подтверждением правок:
Использование в CI: выполнить запрос и считать результат из stdout:
Машиночитаемый результат для скрипта:
koda -p "Найди неиспользуемые функции" --output-format json |
jq -r 'select(.stop_reason == "final") | .result'
Проверка причины остановки по коду выхода:
koda -p "Прогони тесты" --max-turns 10
case $? in
0) echo "готово" ;;
2) echo "упёрлись в лимит витков" ;;
3) echo "обнаружен цикл" ;;
*) echo "ошибка" ;;
esac
Ограничения¶
- Интерактивные возможности TUI в headless-режиме недоступны.
- Подтверждение инструментов по умолчанию выключено, что защищает от нежелательных действий.
- Слеш-команды исполняются только в режиме одиночного запроса.
- Продолжения прошлой сессии (
--continueи--resume) и вывода по JSON-схеме нет. - Стоимость запроса в деньгах не рассчитывается: у CLI нет таблицы цен.