Самостоятельно размещаемые окружения находятся в публичной бета-версии на планах Team и Enterprise; Owner включает их, активируя Allow self-hosted environments на странице администратора Cloud environments. На этой странице предполагается наличие работающего средства выполнения; см. быстрый старт для настройки и Deploy to production для рецептов флота.
pool, такие как CLAUDE_RUNNER_POOL_ID; флаги CLI и имена переменных окружения используют environment, такие как --environment-secret-file.
Скрипты-обёртки
Используйте скрипт-обёртку, когда каждой сессии требуется настройка, которую средство выполнения не может выполнить самостоятельно: подготовка краткосрочных учётных данных, ограниченных создателем сессии, экспорт секретов, специфичных для окружения, подготовка цепочек инструментов языка или применение ограничений ресурсов вокруг дочернего процесса. Средство выполнения запускает вашу обёртку вместо двоичного файла Claude Code один раз за сессию. Завершите обёртку, выполнивexec в $CLAUDE_RUNNER_CLAUDE_BIN, собственный двоичный файл средства выполнения, чтобы сигналы и коды выхода распространялись правильно.
Укажите в --exec-path или SELF_HOSTED_RUNNER_EXEC_PATH путь к обёртке при запуске средства выполнения:
Обёртка также наследует остальную часть управляемого окружения дочернего процесса, включая любые переменные окружения, предоставленные сервером.
exec передаёт всё это автоматически; если ваша обёртка порождает дочерний процесс другим способом, пересылайте окружение полностью.
Переменные CLAUDE_CODE_REMOTE_SLACK_* можно читать в вашей обёртке или хуке command, а также в том, что запускает сессия, например в shell-командах, хуках git и хуках Claude Code. Хуки checkout, post-session и spawn-runner их не получают.
Задавайте значение по умолчанию для переменных, которые могут быть не установлены
Если ваш скрипт используетset -u, раскрывайте CCR_SESSION_ACCOUNT_EMAIL, CLAUDE_RUNNER_CLIENT_PLATFORM, CLAUDE_CODE_REMOTE_SLACK_THREAD_URL, CLAUDE_CODE_REMOTE_SLACK_THREAD_TS и CLAUDE_CODE_REMOTE_SLACK_CHANNEL_ID со значением по умолчанию, например "${CCR_SESSION_ACCOUNT_EMAIL:-}". Каждая из них может быть не установлена, а при set -u Bash останавливается с ошибкой unbound variable, когда раскрывает неустановленную переменную.
Заключайте в кавычки аргументы и переменные, которые раскрывает ваш скрипт
Заключайте в кавычки каждый аргумент и каждую переменную, которые средство выполнения передаёт вашей обёртке или хукуcommand. Некоторые значения содержат символы, на которые реагирует оболочка, поскольку в них находится JSON, целые предложения или правило разрешений, например Bash(npm run test *).
- Аргументы: передавайте их дальше как
"$@" - Переменные: заключайте каждую в кавычки, как в
"${CLAUDE_CODE_REMOTE_SLACK_THREAD_URL:-}". Ссылка на ветку Slack, например, может содержать?и&. - Строки
evalиsh -c: не подставляйте значение в строку, которую выполняетevalилиsh -c, даже внутри кавычек. Вместо этого пусть такая строка ссылается на саму переменную.
Сохраняйте stdin и дескриптор файла 3 подключёнными
stdin дочернего процесса — это канал управления средства выполнения. Ротации токенов и сигналы завершения сессии поступают через него. Средство выполнения также открывает канал на дескрипторе файла 3 и читает из него сигналы активности дочернего процесса для управления таймаутами простоя и запуска. Простойexec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" сохраняет оба автоматически.
Если ваша обёртка переводит дочерний процесс в фоновый режим с помощью простого &, она разрывает stdin дочернего процесса. Сессия выглядит работоспособной до истечения примерно 30-минутного времени жизни начального токена OAuth, после чего каждый вызов API, использующий этот токен, завершается ошибкой 401 authentication_error. Если ваша обёртка должна переводить дочерний процесс в фоновый режим, например чтобы сохранить ловушку очистки, сохраните stdin на дескрипторе файла 9 и явно подключите его заново:
- Дескриптор файла 3: передаёт сигналы активности дочернего процесса средству выполнения. Не закрывайте и не переиспользуйте его в обёртке.
- Дескрипторы файлов 4 и 5: средства выполнения на Claude Code v2.1.309 или новее резервируют их для дочернего процесса. Не закрывайте и не переиспользуйте их в обёртке. Если ваша обёртка использует какой-либо из них, например для сохранения stdin, перенесите это использование на дескриптор с 6 по 9 — более ранние версии также оставляют их свободными.
- stderr: когда обёртка или дочерний процесс завершается с ненулевым кодом выхода, средство выполнения публикует последние строки stderr в сессию и выводит их в свой собственный лог. Пользователь сессии видит эти строки, поэтому не выводите секреты в stderr и удалите
set -xперед развёртыванием обёртки. Если вы перенаправите stderr, сессии по-прежнему будут работать, но средство выполнения будет сообщать о сбое только с кодом выхода.
Передавайте флаги системного промпта дальше
Системный промпт и дополнительный системный промпт, которые плоскость управления Anthropic отправляет для сессии, поступают в вашу обёртку в виде путей к файлам, а не встроенного текста. Средство выполнения записывает каждый промпт в файл в каталоге конфигурации сессииCLAUDE_CONFIG_DIR и передаёт его путь в аргументах, которые получает ваша обёртка, как --system-prompt-file <path> или --append-system-prompt-file <path>.
Средства выполнения на Claude Code v2.1.281 или новее передают промпты в виде файлов. До v2.1.281 средство выполнения передавало их как --system-prompt <text> и --append-system-prompt <text>.
В вашем скрипте-обёртке или хуке command обрабатывайте эти флаги следующим образом:
- Передавайте их дальше: завершайте обёртку командой
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@", которая передаёт файловые флаги вместе со всеми остальными аргументами. Не удаляйте и не переписывайте их. Если сессия теряет файловый флаг промпта, она работает без инструкций, которые для неё отправила плоскость управления. - На средстве выполнения версии v2.1.281 или новее добавленный вами файловый флаг заменяет серверный, а не дополняет его: каждый файловый флаг промпта принимает одно значение, и Claude Code использует последнее вхождение, поэтому если вы добавите
--append-system-prompt-file <path>после"$@", содержимое вашего файла заменит дополнительные инструкции сервера. Чтобы добавить инструкции поверх серверных, поместите их вCLAUDE.mdобраза средства выполнения, который средство выполнения добавляет в пользовательскую конфигурацию каждой сессии.
Исправление Argument list too long при запуске сессии
В Linux, если вы объединяете промпт плоскости управления и собственный текст в один аргумент --append-system-prompt, средство выполнения записывает в лог строку такого вида, как только аргумент достигает 128 КБ:
getconf ARG_MAX, часто равное 2 МБ, — это отдельное ограничение на все аргументы и окружение вместе.
На Claude Code v2.1.283 или новее оставьте файловые флаги в "$@" и передавайте как --append-system-prompt только собственный текст. Claude Code использует оба: сначала промпт плоскости управления, затем ваш собственный текст:
CLAUDE.md образа средства выполнения, поскольку это по-прежнему один аргумент.
На v2.1.281 или v2.1.282 эта команда завершается с ошибкой Cannot use both --append-system-prompt and --append-system-prompt-file. На более ранних версиях ваш текст без какой-либо ошибки заменяет промпт плоскости управления. В любом из этих случаев сначала обновитесь или перенесите собственный текст в CLAUDE.md.
Подготовка учётных данных, ограниченных создателем сессии
Используйте подкомандуdecode-token для чтения утверждений из JWT сессии. Она читает токен из аргумента, из CLAUDE_CODE_SESSION_ACCESS_TOKEN или из stdin, в этом порядке; о том, что она проверяет, см. Проверка токена внутри сессии. Пример ниже декодирует идентичность создателя, обменивает её на краткосрочные учётные данные AWS и выполняет exec в Claude Code:
jq -re вместо jq -r, когда извлечённое утверждение определяет решение об авторизации, чтобы при отсутствии утверждения команда завершалась с ненулевым кодом выхода, а не передавала дальше буквальную строку null. Сессии, созданные служебной учётной записью организации, например сессии ботов и агентов, содержат субъект agent: вместо user:, поэтому этот пример отклоняет их; если ваше окружение обслуживает такие сессии, явно решите, должна ли обёртка для них переходить к учётным данным по умолчанию вместо завершения. Если вашему обмену учётными данными вместо этого нужна электронная почта, прочитайте .act.email и обработайте её отсутствие: токен содержит её только тогда, когда интерфейс, создавший сессию, её записал, а у сессии, запущенной через CLI, её может не быть. Полный справочник по утверждениям и описание проверки из служб вне средства выполнения см. в разделе Проверка идентичности сессии.
Lifecycle hooks
Lifecycle hooks заменяют этапы конвейера runner’а для каждой сессии вашими собственными скриптами. Укажите runner’у на директорию с hooks с помощью--hooks-dir <path> или SELF_HOSTED_RUNNER_HOOKS_DIR. Runner ищет исполняемые файлы с известными именами; любой hook, который отсутствует, переходит к встроенному поведению, поэтому вам нужно написать только те, которые вам нужны. Hooks запускаются с собственными привилегиями runner’а, и дочерние процессы сессии используют тот же UID, поэтому монтируйте директорию hooks в режиме только для чтения или встраивайте её в образ, чтобы код сессии не мог её изменять; см. раздел об усилении безопасности.
Эти hooks отличаются от Claude Code hooks, которые запускаются внутри сессии; lifecycle hooks запускаются на runner’е, вокруг сессии.
checkout
Запускается один раз на репозиторий вместо встроенного клонирования и получения данных runner’а. Используйте хук для клонирования из зеркала сквозного доступа, к которому вы обращаетесь по HTTPS или SSH, инициализации рабочего дерева из архива или применения аутентификации git для каждой сессии. Runner устанавливает эти переменные, а также может устанавливать другие переменныеCLAUDE_RUNNER_, которых нет в таблице:
Скрипт должен оставить в
CLAUDE_RUNNER_CHECKOUT_PATH рабочее дерево, переключённое на запрошенную ревизию. Отсоединённый HEAD подходит, потому что runner создаёт рабочую ветку сессии поверх него.
После возврата вашего хука runner проверяет, что CLAUDE_RUNNER_CHECKOUT_PATH содержит .git. Если ваш хук материализует источник, не основанный на git, такой как Perforce или распакованный tarball, установите CLAUDE_RUNNER_SKIP_GIT_VERIFY=1 в окружении runner’а, чтобы пропустить эту проверку. Потоки на основе Git, такие как создание рабочей ветки и отправка результатов, требуют checkout git, поэтому экспортируйте результаты из деревьев, не основанных на git, с помощью хука post-session.
Получение учётных данных git в хуке
Runner не передаёт учётные данные git в хук. Подкомандаdecode-token здесь тоже недоступна, потому что CLAUDE_RUNNER_CLAUDE_BIN не установлена в окружении хука checkout. Вместо этого создайте учётные данные клонирования для каждой сессии на основе идентичности сессии или вернитесь к собственной аутентификации git хоста:
- Учётные данные клонирования для каждой сессии: проверьте
CLAUDE_CODE_SESSION_ACCESS_TOKENс помощью стандартной библиотеки JWT по эндпоинту JWKS подCLAUDE_RUNNER_API_BASE_URL, как описано в разделе Verify the token from your service. Затем попросите вашу службу учётных данных выдать краткосрочные учётные данные клонирования для идентичности из утвержденияactтокена. Привязывайте эти учётные данные кact.subи не требуйтеact.email. - Аутентификация git хоста: используйте любую аутентификацию git, которая уже есть на хосте, например SSH-агент, помощник учётных данных или
.netrc.
Когда хук завершается ошибкой
Хук считается завершившимся ошибкой, если он завершается с ненулевым кодом или завершается с кодом 0, не оставив пригодного для использования checkout:-
Репозиторий, в который сессия отправляет результаты: runner отмечает сессию как неудачную, и при ненулевом выходе выводит пользователю конец stderr скрипта. Запишите причину в stderr одной короткой строкой, сделайте это последним действием, а затем завершите работу с ненулевым кодом. Не выводите секреты в stderr и удалите
set -xперед развёртыванием хука. Если сообщение gitcouldn't find remote refвстречается примерно в последних 4 КБ stderr хука, runner вместо этого сообщает об удалении ветки. Тогда в сообщении об ошибке сессии говорится, что ветка больше не существует в удалённом репозитории, и пользователю предлагается восстановить её. Если эта ветка существует, см. Сессия завершается ошибкойno longer exists on the remoteилиcouldn't find remote ref. -
Репозиторий, из которого сессия только читает, например репозиторий, добавленный к запущенной сессии: runner пропускает его и продолжает работу с оставшимися репозиториями. Runner отправляет в сессию шаг
Skippedи удаляет всё, что хук оставил по пути checkout. Если шагSkippedупоминает хук checkout, подробности ошибки ищите в строке[runner:warn] checkout hook failedв логе runner’а. Если в шаге сказано, что исходная ссылкаno longer exists on remote, см. Сессия завершается ошибкойno longer exists on the remoteилиcouldn't find remote ref. Если после пропуска у сессии не остаётся ни одного репозитория, runner всё равно отмечает сессию как неудачную.
post-session
Запускается один раз на сессию, после выхода дочернего процесса Claude Code и перед тем, как runner разбирает рабочее пространство. Этот hook — ваш единственный шанс сохранить незафиксированную работу: при--capacity выше одного runner удаляет рабочие деревья для каждой сессии сразу после возврата hook’а, а при --capacity 1 переиспользуемый канонический клон жёстко сбрасывается при запуске следующей сессии, поэтому незафиксированные отслеживаемые изменения не сохраняются ни в одном случае. Типичные применения — отправка ветки снимка незафиксированных изменений, архивирование логов или отправка события завершения сессии в ваши собственные системы.
Hook срабатывает при каждом завершении сессии, где был порождён дочерний процесс, независимо от причины; значения CLAUDE_RUNNER_EXIT_REASON ниже перечисляют случаи. Он не может срабатывать, когда runner завершается неожиданно, такой как вытеснение VM или потеря питания; если вам нужны гарантии против неожиданного завершения, делайте снимки периодически изнутри сессии с помощью Claude Code PostToolUse hook вместо этого. Runner устанавливает:
CLAUDE_RUNNER_EXIT_REASON принимает одно из четырёх значений:
completed: сессия завершилась чисто. Процесс Claude Code завершился нормально или завершился сам после того, как сессия была архивирована или удалена.failed: процесс Claude Code упал, или настройка не удалась после его запуска.interrupted: runner остановил сессию в одном из следующих случаев:- Runner освободил сессию, чтобы освободить слот.
- Истекло время ожидания сессии при запуске.
- Сервер переместил сессию с этого runner’а.
- Опрос runner’а обнаружил архивирование или удаление до завершения процесса.
- Runner был в режиме дренирования.
- Сессия превысила свой лимит
--kill-session-after-min.
abandoned: зарезервировано для сессии, которую заявил другой runner. Hook в настоящее время не срабатывает в этом случае.
interrupted будут учтены там как completed. Счётчики считают освобождение, истечение времени при запуске, перемещение сервером, а также архивирование или удаление, которые первым обнаружил опрос runner’а, как completed, потому что runner чисто вернул слот.
Статус выхода hook’а никогда не влияет на результат сессии; отказ логируется и игнорируется. Runner ждёт до --post-session-hook-timeout-sec, 60 секунд по умолчанию, при каждом завершении сессии, включая завершение runner’а. Этот пример сохраняет незафиксированную работу в ветку спасения:
GIT_ALLOW_PROTOCOL в скрипте ограничивает git удалёнными репозиториями по HTTPS, HTTP и SSH. Если окружение runner’а уже задаёт собственный непустой список GIT_ALLOW_PROTOCOL, скрипт сохраняет этот список.
Хук выполняет push с любыми учётными данными git, доступными в его собственном окружении на хосте runner’а. При подходе без учётных данных в образе, в том числе когда встроенное клонирование проходит через git-прокси Anthropic, их нет, поэтому перед push создайте краткосрочные учётные данные для отправки внутри хука: обменяйте токен сессии, который хук получает в CLAUDE_CODE_SESSION_ACCESS_TOKEN, в вашей собственной службе токенов, предварительно проверив его, как описано в разделе Проверка идентичности сессии.
Относитесь к любым учётным данным, которые ваш хук передаёт git, как к таким, которые может получить сессия, и создавайте их так, чтобы с их помощью можно было выполнить только этот push. Git в вашем хуке читает файлы конфигурации, в которые может записывать сессия, а помощник учётных данных или драйвер фильтра, указанный в одном из них, запускается с привилегиями вашего хука. Настройки в этих файлах также могут изменить, куда уходит push, независимо от того, какой удалённый репозиторий вы указываете. О настройках git, которые runner фиксирует в вашем хуке, и о тех, которые он оставляет на усмотрение этих файлов, см. раздел Конфигурация git внутри хуков жизненного цикла.
Hook timing when the runner releases a session
Освобождённая сессия может возобновиться на другом runner’е. На runner’е версии 2.1.236 или позже то, что сессия делала при освобождении, решает, может ли она возобновиться до завершения этого hook’а:- Неактивна после хода или истекла при запуске: runner останавливает дочерний процесс и запускает этот hook до завершения. Только после этого он освобождает сессию. Сообщение пользователя, отправленное во время выполнения hook’а, не может возобновить сессию на другом runner’е до завершения hook’а.
- Ожидание ответа пользователя на подсказку, такую как подсказка разрешения: runner сначала освобождает сессию, затем запускает этот hook. Сообщение пользователя, отправленное во время выполнения hook’а, может возобновить сессию на другом runner’е до завершения hook’а.
--retire-at времени и, на runner’е версии 2.1.260 или позже, при лимите --kill-session-after-min сессии. Сессия, чей ход закончился и которая содержит только фоновые задачи, считается неактивной здесь. До v2.1.236 runner сначала освобождал сессию, а затем запускал этот hook в обоих случаях.
Во время дренирования SIGTERM runner держит аренду сессии до завершения hook’а; см. Shutdown timing.
Git configuration inside lifecycle hooks
Хукиcheckout и post-session запускаются с токеном доступа сессии в своём окружении, а git, который они запускают, читает файлы конфигурации, в которые могут записывать сессии, например ~/.gitconfig и .git/config в checkout. Перед запуском любого из этих хуков runner устанавливает в окружении хука настройки git, включая перечисленные ниже, в виде пар GIT_CONFIG_COUNT/GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n и переменных окружения git. Git ставит их выше любого файла конфигурации, и они применяются только к git, который запускают ваши хуки, но не к собственному git сессии. При запуске runner выводит строку [runner:git] lifecycle hooks:, в которой показаны действующие путь к хукам, разрешённые протоколы, программы gpg и режим подписи. Требуется Claude Code v2.1.280 или новее.
- Хуки git: если вы не укажете значение,
core.hooksPathравен/dev/null, поэтому git пропускает хуки в.git/hooksрепозитория и любую директорию хуков, указанную в~/.gitconfig. Чтобы указать своё значение, экспортируйтеcore.hooksPathкак паруGIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_nв окружении runner’а. Runner также читаетcore.hooksPathиз системной конфигурации git и использует его, только если пользователь runner’а не может записывать в этот файл, в указанную в нём директорию или в файлы хуков в ней. Когда runner игнорирует значение, строка[runner:warn]при запуске называет это значение и причину. - Монитор файловой системы:
core.fsmonitorпуст, поэтому git в вашем хуке не запускает программу-монитор, указанную в файле конфигурации. - Удалённые протоколы:
GIT_ALLOW_PROTOCOLравнаhttps:http:ssh. Clone, fetch или push с локальным путём, URLfile://или URLgit://завершается ошибкойfatal: transport 'file' not allowedилиfatal: transport 'git' not allowed. - Команда SSH и запрос учётных данных: git в вашем хуке игнорирует
core.sshCommandиcore.askPassиз файлов конфигурации. Чтобы использовать собственную команду SSH, установитеGIT_SSH_COMMANDв окружении runner’а. Чтобы использовать программу запроса учётных данных, установите тамGIT_ASKPASS. Сессии наследуют окружение runner’а, поэтому обе переменные также попадают в собственный git сессии. Не помещайте учётные данные ни в одну из них. - Программы gpg:
gpg.program,gpg.openpgp.program,gpg.x509.programиgpg.ssh.program— это пути, которые устанавливает runner, а не значения из файла конфигурации. - Подпись коммитов: с
--configure-gitкоммиты, которые вы делаете из хука, подписываются от имени сессии. Без этого флагаcommit.gpgsignиtag.gpgsignравныfalse.
git -c внутри хука:
- Пары конфигурации: пара
GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n, которую вы экспортируете в окружении runner’а, заменяет значение runner’а для того же ключа. Нумеруйте свои пары с0и установитеGIT_CONFIG_COUNTравной их количеству. Если последняя пара, заявленная этим количеством, отсутствует, runner игнорирует все ваши пары и при запуске записывает в лог строку[runner:warn]. - Переменные окружения git: runner оставляет
GIT_ALLOW_PROTOCOL,GIT_SSH_COMMANDиGIT_ASKPASSтакими, какими вы их установили в его окружении. - Опции
git -c: опцияgit -cвнутри хука переопределяет паруGIT_CONFIG_KEY_n— как runner’а, так и вашу. Она не изменяетGIT_ALLOW_PROTOCOL,GIT_SSH_COMMANDилиGIT_ASKPASS, которые git читает раньше любой конфигурации.
url.*.insteadOf и драйверы фильтров, из всех файлов конфигурации, включая те, в которые могут записывать сессии. Помощник учётных данных или драйвер фильтра, указанный в одном из этих файлов, запускается как программа с привилегиями вашего хука, а конфигурация в этих файлах по-прежнему может изменить, куда уходит push из вашего хука, в том числе push на URL, который вы передаёте в командной строке.
До v2.1.280 runner не устанавливал ни одну из этих настроек, а при --configure-git коммит из хука завершался ошибкой, если хук не передавал -c commit.gpgsign=false.
command
Запускается один раз на сессию после checkout, вместо встроенного порождения дочернего процесса. Hook получает то же окружение, что и wrapper script, и долженexec в "$CLAUDE_RUNNER_CLAUDE_BIN" таким же образом. Используйте command hook, чтобы держать всю настройку в одной директории hooks; используйте --exec-path, когда wrapper находится в другом месте. Если --exec-path также установлен, флаг имеет приоритет и command hook игнорируется.
Всегда exec собственный бинарный файл runner’а вместо разрешённого PATH claude; иначе вы нарушите закрепление версии.
Средства выполнения по требованию
Вместо запуска фиксированного флота вы можете загрузить одно средство выполнения за сеанс. Оркестратор — это отдельная, без состояния подкоманда, которая опрашивает Anthropic на предмет запросов на порождение, по одному за сеанс, который находится в очереди без доступного средства выполнения, и запускает ваш хукspawn-runner для каждого. Ваш хук отправляет рабочую нагрузку на вашу платформу: Kubernetes Job, экземпляр EC2, Nomad dispatch.
Средства выполнения по требованию улучшают гигиену учетных данных. На фиксированном флоте секрет окружения находится на каждом хосте средства выполнения, который является тем же хостом, на котором запускается код пользователя. С оркестратором секрет окружения остается только на хосте оркестратора, который никогда не запускает код пользователя; каждое порожденное средство выполнения получает одноразовый наряд на работу, который регистрирует ровно одно средство выполнения и затем истекает.
Чтобы запустить оркестратор, передайте секрет окружения и каталог хуков, содержащий исполняемый скрипт spawn-runner:
--expected-spawn-seconds; см. контракт хука.
Хук spawn-runner
Оркестратор запускает${hooks-dir}/spawn-runner один раз за запрос на порождение. Хук должен отправить работу асинхронно, без ожидания загрузки средства выполнения, и вернуться в течение --hook-timeout, 60 секунд по умолчанию. Хук получает:
Порожденное средство выполнения регистрируется с нарядом на работу вместо секрета окружения:
- Запустите его с нарядом на работу: укажите
--environment-secret-fileна файл, содержащий JWT наряда на работу, или установитеSELF_HOSTED_RUNNER_ENVIRONMENT_SECRETна значение JWT. - Скопируйте JWT перед выходом хука: оркестратор удаляет файл наряда на работу после выхода хука, поэтому скопируйте JWT в рабочую нагрузку, которую вы отправляете, такую как Kubernetes Secret на порожденном Job, вместо передачи пути файла.
- Используйте
--capacity 1на порожденных средствах выполнения: наряд на работу, привязанный к сеансу, регистрирует ровно одно средство выполнения, привязанное к этому сеансу, поэтому более высокая емкость добавляет слоты, которые никогда не получают работу, и средство выполнения логирует предупреждение при запуске. - Нарядные работы предварительного прогрева регистрируют без привязки: резервное средство выполнения не привязано к сеансу и заявляет поставленную в очередь работу, как средство выполнения фиксированного флота.
-
Будьте идемпотентны по
CLAUDE_RUNNER_ORDER_ID. Повторная доставка одного и того же запроса должна порождать не более одного средства выполнения. Выведите детерминированное имя ресурса из ID заказа и позвольте вашей платформе отклонить дубликат. Не используйте вместо этогоCLAUDE_RUNNER_SESSION_IDв качестве ключа. Каждый повторный запрос для сессии несёт тот же ID сессии с новым ID заказа. Рабочая нагрузка, названная или дедуплицированная по ID сессии, не создаётся снова, пока существует первая. -
Не повторяйте рабочую нагрузку. Один ID заказа означает не более одной созданной рабочей нагрузки. Если средство выполнения никогда не регистрируется, Anthropic повторно запрашивает с новым ID заказа после
--expected-spawn-seconds. -
Соблюдайте контракт кода выхода. Завершайтесь со статусом, соответствующим результату:
- Выход 0: отправлено.
- Выход 1 или хук всё ещё выполняется по истечении
--hook-timeout: сбой, допускающий повторную попытку. Сессия выжидает и предлагается снова. После 10 неудачных запросов подряд она блокируется так же, как после выхода 2 или выше. - Выход 2 или выше: сбой, не допускающий повторной попытки. Сессия блокируется от повторного порождения до тех пор, пока пользователь не отправит ей новое сообщение или Owner не выберет для неё Retry на вкладке Activity окружения.
--expected-spawn-seconds. -
Установите
--expected-spawn-secondsне меньше вашего времени p99 от запроса на порождение до регистрации средства выполнения. Отсчитывайте от момента, когда оркестратор получает запрос на порождение, и учитывайте как ожидание ресурсов на вашей платформе, так и время загрузки. Это значение является арендой на стороне сервера, и наряд на работу истекает вместе с ней, поэтому средство выполнения, рабочая нагрузка которого запускается дольше, не сможет зарегистрироваться. Все реплики оркестратора должны использовать одно и то же значение.
/healthz оркестратора на предмет количества в очереди, затем откройте вкладку Activity вашего окружения на странице администратора Cloud environments: разверните неудачный сеанс там для его ошибки порождения и выберите Retry для повторного запроса.
Исправьте повторно открытые сессии, которые остаются в статусе Queued или отображаются как Failed
Если повторно открытые сессии остаются в статусе Queued или отображаются как Failed, либо ваш хук завершается для них по таймауту, одна из возможных причин — хук, который называет или дедуплицирует рабочие нагрузки по ID сессии. Сессия открывается повторно, когда её пользователь отправляет ей сообщение после того, как её средство выполнения завершилось. Например, возьмём хук, который формирует имя каждой рабочей нагрузки так:- При первом запросе на порождение для сессии хук создаёт рабочую нагрузку с этим именем, и средство выполнения запускается
- Средство выполнения завершается, например после
--release-idle-session-min, а позже пользователь повторно открывает сессию - Следующий запрос на порождение для сессии содержит тот же ID сессии и новый ID заказа, поэтому хук формирует то же имя
- Если на вашей платформе всё ещё есть рабочая нагрузка от первого запроса, потому что ничто её не удалило при завершении её средства выполнения, хук не создаёт новую рабочую нагрузку, и средство выполнения не запускается
- Каждый последующий запрос на порождение для сессии заканчивается так же
В ошибке порождения из последней строки
60000 — это ваш --hook-timeout в миллисекундах. Когда хук ждёт, пока не истечёт --hook-timeout, в лог оркестратора также попадает строка timed out after.
Чтобы исправить хук, формируйте каждое имя или ключ из CLAUDE_RUNNER_ORDER_ID, как требует правило 1 контракта хука. В примере имя становится таким:
- Queued: сессия получит следующий запрос на порождение самостоятельно
- Failed: выберите для неё Retry как Owner или попросите её пользователя отправить ей новое сообщение
Настройте завершение средств выполнения по требованию при простое
По умолчанию средство выполнения, запущенное вашим хуком, не завершается, когда заканчивается ход его сессии, поэтому отправленная вами рабочая нагрузка продолжает работать. Чтобы средства выполнения завершались, пусть ваш хук передаёт следующие флаги средства выполнения:
Удаляйте рабочую нагрузку, когда средство выполнения завершается, например задав
ttlSecondsAfterFinished для Kubernetes Job или настроив стартовый скрипт VM так, чтобы он затем удалял VM.
Направляйте вывод фонового процесса в файл
Если ваш хукspawn-runner оставляет процесс работать в фоне, направляйте вывод этого процесса в файл, который может создать пользователь хука. Например, добавьте >> "$HOME/claude-runner.log" 2>&1 к команде, запускающей процесс, перед любым &. Эта строка хука показывает, где находится перенаправление, где your-background-command заменяет команду, которую ваш хук запускает в фоне:
- Примерно через секунду после выхода хука оркестратор записывает в лог
exited, but a process it started still has the hook's stdout or stderr open. Начиная с этой строки, он записывает в лог до 64 КБ последующего вывода под[runner:hook:spawn-runner:after-exit jti=<order id>], а остальное отбрасывает. - После завершения оркестратора запись процесса туда завершается сбоем.
--hook-timeout, пока запущенный им процесс держал открытыми stdout или stderr хука, что отображалось так:
- Вкладка Activity, в ошибке порождения неудачной сессии:
spawn-runner hook timed out after 60000msили последние строки stderr хука. - Лог оркестратора:
timed out after 60000ms, sending SIGTERM to process tree.
Завершайте хук, не дожидаясь средства выполнения
Если каждое средство выполнения по требованию останавливается примерно через 60 секунд после запуска его хука, а ваш хук снова запускается для той же сессии, проверьте, не ждёт ли хук средство выполнения. 60 секунд — это значение--hook-timeout по умолчанию, поэтому если вы задали собственное, средства выполнения останавливаются через это время после запуска хука.
Хук, который запускает claude self-hosted-runner на хосте оркестратора и ждёт его, на переднем плане или с помощью wait, всё ещё выполняется, когда истекает --hook-timeout. Тогда оркестратор останавливает хук и процессы, выполняющиеся под ним, включая это средство выполнения и процесс Claude сессии, поэтому ещё не завершившийся ход обрывается.
- Чтобы убедиться, что ваши средства выполнения останавливает именно таймаут: найдите в логе оркестратора
timed out after 60000ms, sending SIGTERM to process treeдля каждого запроса на порождение.60000— это ваш--hook-timeoutв миллисекундах. - Чтобы исправить это: пусть хук отправляет средство выполнения на вашу платформу и завершается с кодом 0. Если вместо этого увеличить
--hook-timeout, средство выполнения просто остановится позже.
Сохраняйте временные сбои повторяемыми в shell-хуке
В shell-хуке, использующемset -e, сбой, который могла бы устранить повторная попытка, может заблокировать сессию. Хук останавливается на сбойной команде и завершается с собственным статусом этой команды, а оркестратор применяет контракт кода выхода к этому статусу. Многие сбои возвращают статус 2 или выше, например 127, когда команда не установлена, и 22 от curl --fail при ошибке HTTP, поэтому они блокируют сессию при первом же сбое.
Сессия, которую хук уже заблокировал, остаётся заблокированной, пока пользователь не отправит ей новое сообщение или Owner не выберет для неё Retry на вкладке Activity окружения.
Чтобы вместо этого превращать такой сбой в выход 1, поместите эти строки сразу под строкой #! хука, перед всем, что может завершиться сбоем:
-
Просто
exit 2или выше: при установленной ловушке он превращается в выход 1. Для ошибки, которую не исправит никакая повторная попытка, вместо этого вызовитеpermanentс указанием причины, напримерpermanent "namespace claude-runners does not exist". Вызывайте её в основной оболочке, а не внутри$( ),( )или конвейера. -
exec: не начинайте последнюю команду хука сexec, потому чтоexecзаменяет оболочку, и ловушка не выполняется. -
Вторая ловушка
EXIT: второйtrap ... EXITзаменяет первый, поэтому объедините их в одну ловушку. Поместите команды очистки сразу послеrc=$?;и завершите каждую|| true;. Тогда очистка выполняется как при сбое, так и при успехе, а сбойная команда очистки не влияет на статус выхода хука. Эта объединённая ловушка показывает структуру, гдеyour-cleanup-commandзаменяет вашу собственную команду: -
Команды, которым разрешено завершаться сбоем: если хук раньше не использовал
set -e, теперь он останавливается на первой команде, вернувшей ненулевой статус, например на поиске, который ничего не нашёл. Если хук использует результат, сделайте эту команду условиемif. Если он игнорирует результат, добавьте после команды|| true. -
Команда, которая отправляет рабочую нагрузку: не добавляйте после неё
|| true. С|| trueхук завершается с кодом 0, даже когда ваша платформа отклоняет отправку, например из-за превышения квоты, и сессия ждёт средство выполнения, которое так и не было запущено. Называйте рабочую нагрузку поCLAUDE_RUNNER_ORDER_ID, как требует первое правило хука. Тогда ответ «уже существует» означает повторную доставку того же запроса, поэтому считайте успехом только этот ответ. Во всех остальных случаях выведите ответ в stderr и завершитесь с кодом 1 или вызовитеpermanent, если никакая повторная попытка не поможет. Этот пример делает это дляkubectl createс файломjob.yaml, который содержит только рабочую нагрузку:Тогда в логе оркестратора отображается это сообщение. Если хук создаёт другие объекты, называйте каждый по ID заказа, создавайте его отдельной командой и так же считайте успехом его ответ «уже существует». Для объекта, содержащего секрет, выводите фиксированное сообщение вместо вывода команды.
trap строку, вызывающую несуществующую команду, например no-such-command. Запустите файл хука из своей оболочки и убедитесь, что echo $? выводит 1, затем удалите эту строку.
Отправка запросов к модели в Bedrock или Agent Platform
Если ваша организация требует, чтобы запросы к модели проходили через её собственный аккаунт AWS или Google Cloud, настройте runner для Amazon Bedrock или Google Cloud’s Agent Platform, ранее Vertex AI. После этого каждая сессия, которую запускает этот runner, обращается к модели в вашем облачном аккаунте с вашими облачными учётными данными. Без этой конфигурации сессии отправляют запросы к модели в Anthropic API. Runner по-прежнему опрашивает Anthropic на предмет сессий, и каждая сессия по-прежнему отправляет свой поток событий наapi.anthropic.com. Поток событий содержит промпты, ответы и результаты инструментов. Требование к тарифному плану и исключение для Zero Data Retention из раздела Доступность и ограничения по-прежнему действуют.
Сессии направляются в окружение, а не на конкретный runner, и повторно поставленная в очередь или возобновлённая сессия может выполняться на другом runner. Настройте все runner в окружении одинаково. Прежде чем начать, прочитайте, чем отличаются сессии у этих провайдеров.
1
Подготовьте облачный аккаунт и правила исходящего трафика
Настройте доступ к моделям, политику или роль с узкими правами и сетевой доступ:
- Amazon Bedrock: отправьте сведения о сценарии использования, затем создайте политику из раздела Конфигурация IAM, ограничив
bedrock:InvokeModelиbedrock:InvokeModelWithResponseStreamпрофилями инференса, которые используют ваши сессии, и базовыми моделями, стоящими за ними - Agent Platform: включите API и запросите доступ к моделям, затем создайте пользовательскую роль, описанную в разделе Конфигурация IAM, только с разрешением
aiplatform.endpoints.predict - Исходящий трафик: разрешите эндпоинты вашего провайдера в правилах исходящего трафика. См. Сетевые требования. Если сессии не могут до них достучаться, Claude Code может повторять попытки часами, прежде чем в сессии появится ошибка.
2
Выдайте сессиям учётные данные с узкими правами
Привяжите политику или роль из шага 1 к идентификатору, который больше ничего не может делать. Способы, которые поддерживает Claude Code, описаны в разделах Настройка учётных данных AWS и Настройка учётных данных GCP.Сверьте выбранный способ со следующими особенностями поведения runner:
- Эндпоинт метаданных: если вы полностью запрещаете сессиям доступ к облачному эндпоинту метаданных, учётные данные, выдаваемые через него, например профиль экземпляра, тоже не дойдут до Claude Code. Файловая веб-идентификация, например IAM Roles for Service Accounts (IRSA) в Amazon EKS или файл учётных данных Workload Identity Federation, от него не зависит.
- Обновление: сессия может пережить срок действия учётных данных, поэтому используйте способ с автоматическим обновлением, например файловую веб-идентификацию
- Скрипт-обёртка: runner запускает ваш скрипт-обёртку один раз на сессию, поэтому экспортируемые им учётные данные не обновляются. Claude Code читает учётные данные AWS из своего окружения, поэтому если ваша обёртка уже экспортирует учётные данные AWS для других задач, Claude Code может подписывать ими запросы к модели.
3
Задайте переменные одного провайдера в окружении runner
Задайте переменные ровно одного провайдера там же, где вы задаёте остальные переменные окружения runner, например в спецификации контейнера или в service unit, а затем перезапустите runner. В примерах они показаны как shell-экспорты. При использовании runner по требованию задайте их для рабочей нагрузки, которую запускает ваш хук
spawn-runner.Запускайте эти runner с --confine-repo-settings enforce. Этот режим отклоняет сессии в репозиториях, закоммиченные настройки которых он помечает, поэтому сначала запустите runner в режиме по умолчанию warn и устраните то, что он записывает в лог.- Amazon Bedrock
- Google Cloud's Agent Platform
Замените регион на свой:О том, как Claude Code определяет регион, см. Настройка Claude Code. О том, какой префикс профиля инференса Claude Code использует для вашего региона, см. Префиксы межрегиональных профилей инференса.
4
Убедитесь, что переменные дошли до сессии
Ваша собственная оболочка на хосте — это другой процесс, поэтому проверяйте изнутри сессии. Запустите сессию в окружении и попросите Claude выполнить эту команду:Строка, в которой
CLAUDE_CODE_USE_BEDROCK или CLAUDE_CODE_USE_VERTEX имеет значение 1, означает, что переменная дошла до сессии. Если присутствуют обе, Claude Code использует Amazon Bedrock. Отсутствие вывода означает, что ни одна из них не дошла.Команда показывает конфигурацию, а не трафик. Чтобы подтвердить сами запросы, найдите их в метриках или логах запросов вашего облачного аккаунта. Если вместо этого первое сообщение завершается ошибкой, см. раздел об устранении неполадок для Amazon Bedrock или Agent Platform.Чем отличаются сессии от сессий в Anthropic API
Сессия, отправляющая запросы к модели в Amazon Bedrock или Google Cloud’s Agent Platform, отличается от сессии в Anthropic API следующим:- Политики из claude.ai: настройки, управляемые сервером, не доходят до этих сессий. Не доходят и политики организации, которые Owner задаёт в настройках администратора Claude Code, поэтому Claude Code не применяет их внутри сессии. Поместите правила, на которые вы полагаетесь, в файл управляемых настроек образа runner.
- Скиллы аккаунта: эти сессии не загружают скиллы, включённые для аккаунта claude.ai пользователя. См. Как собирается конфигурация каждой сессии.
- Файлы: файлы, которые пользователи прикрепляют к сессии в claude.ai, мобильном или десктопном приложении, до неё не доходят, и Claude не может отправлять файлы обратно с помощью инструмента
SendUserFile. Вместо этого размещайте входные файлы в репозитории или на runner. - Выбор модели: управляющий уровень Anthropic передаёт модель для каждой сессии, а если сессия запускается без неё, Claude Code использует модель по умолчанию для провайдера. Вы не можете выбрать модель с помощью
ANTHROPIC_MODELилиANTHROPIC_DEFAULT_MODELв окружении runner, но можете закрепить, во что разрешается псевдоним:ANTHROPIC_MODELиANTHROPIC_DEFAULT_MODEL: runner удаляет их из окружения, которое он передаёт сессиям, хотя в примерах на страницах провайдеров задаётсяANTHROPIC_MODEL.- Переменные закрепления для отдельных семейств: переменные из раздела «Закрепление версий моделей» для Amazon Bedrock и Agent Platform до сессий доходят. Они определяют, во что разрешается псевдоним, например
opus, но не полный идентификатор модели.
- Модели, которые ваш аккаунт не обслуживает: сессия может завершиться ошибкой на сообщении с указанием модели. Включите модели, которые могут выбирать ваши разработчики, фоновую модель, описанную в разделе «Закрепление версий моделей», и модель классификатора, которую использует авторежим. В Amazon Bedrock разрешите каждую из них в своей политике.
- Веб-поиск и быстрый режим: веб-поиск недоступен в Amazon Bedrock, а быстрый режим недоступен ни у одного из этих провайдеров. О других возможностях, различающихся в зависимости от провайдера, см. Возможности CLI, зависящие от провайдера.
MCP-серверы
Чтобы MCP-серверы были доступны в каждой сессии, добавьте их на этапе сборки образа той же командойclaude mcp add, что используется при установке на рабочий компьютер. Если ваш раннер работает как обычный процесс, а не в контейнере, выполните ту же команду на хосте от имени пользователя раннера, а затем перезапустите раннер: он считывает конфигурацию хоста один раз при запуске. Флаг --scope user обязателен: локальная область действия по умолчанию записывает данные под ключом для конкретного каталога, который раннер не передаёт в сессии. Например, в вашем Dockerfile:
mcpServers из файла .claude.json хоста, который находится рядом с ~/.claude/, а не внутри него, и раннер передаёт в изолированную конфигурацию каждой сессии только этот ключ; состояние учётной записи и история проектов отбрасываются. Чтобы убедиться, что серверы попали в сессии, запустите сессию в окружении и попросите Claude перечислить его инструменты MCP; кроме того, при запуске раннер записывает в лог предупреждение для каждой захваченной записи, type которой он не распознаёт, и отбрасывает такую запись, поэтому вы можете увидеть, почему этот сервер отсутствует в сессиях. Если задана переменная SELF_HOSTED_RUNNER_HOST_CONFIG_DIR, раннер считывает .claude.json из этого каталога, поэтому, указав в переменной пустой каталог, вы также отключите передачу MCP-серверов.
Claude Code также загружает MCP-серверы из других источников:
- Управляемый файл MCP корпоративного уровня по стандартному системному пути:
/etc/claude-code/managed-mcp.jsonна хостах раннеров под Linux,/Library/Application Support/ClaudeCode/managed-mcp.jsonна хостах под macOS. Используйте его для строго контролируемых парков машин, где могут загружаться только серверы из списка администратора. Правила приоритета описаны в разделе эксклюзивный контроль с помощью managed-mcp.json. Если этот файл находится на хосте раннера, Claude Code пропускает MCP-серверы, которые плоскость управления Anthropic передаёт в сессию, включая коннекторы claude.ai, и перечисляет их в предупреждении в stderr дочернего процесса сессии, которое раннер записывает в лог на уровнеdebug. До версии v2.1.229 такие сессии завершались при запуске с ошибкойYou cannot dynamically configure MCP servers when an enterprise MCP config is present. - Ключ
managedMcpServersв управляемых настройках на хосте раннера: предоставляет серверы HTTP и SSE без эксклюзивного контроля, поэтому серверы из других источников по-прежнему загружаются. Требуется Claude Code v2.1.259 или новее. <repo>/.mcp.json: область действия проекта. Вне треда проекта сделайте коммит этого файла в репозиторий, и его серверы будут автоматически подтверждаться в облачных сессиях. В сессии с несколькими репозиториями загружается файл не более чем из одного репозитория.- Файл
--mcp-configиз вашего скрипта-обёртки: добавьте флаг после"$@", напримерexec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --mcp-config /etc/claude/mcp.json. Чтобы серверы из этого файла продолжали работать, когда переданные в сессию серверы обновляются посреди сессии, ваша обёртка должна передавать дочернему процессу полное окружение, как это делаетexec. До версии v2.1.299 серверы stdio, HTTP и SSE из этого файла завершали работу при первом таком обновлении, в котором они не были указаны.
api.anthropic.com. Сессии, созданные программно, например запуски через CLI, не получают коннекторы; вместо этого предоставьте им MCP-серверы через любой из других источников, перечисленных в этом разделе. OAuth-токен дочернего процесса не содержит scope для прямого получения коннекторов, поэтому дочерний процесс сам не пытается их получить; передача управляется сервером.
В сессии, получившей коннектор, сервер с тем же именем из
.claude.json хоста или из .mcp.json репозитория не подключается. Чтобы использовать оба, дайте своему серверу другое имя.settings.json не содержит определений MCP-серверов, и в схеме настроек нет поля mcpServers верхнего уровня. В управляемых настройках вместо этого задавайте серверы с помощью ключа managedMcpServers.
Сессии наследуют окружение раннера, поэтому задайте там ENABLE_TOOL_SEARCH, чтобы управлять поиском инструментов MCP для каждой сессии, которую запускает раннер; допустимые значения описаны на странице MCP.
Ожидание MCP-серверов перед первым ходом
Сессия на собственном хостинге ненадолго ожидает MCP-серверы, которые ещё подключаются, в двух разных точках. Если сервер не успевает к моменту окончания ожидания, его инструменты отсутствуют в начале первого хода и становятся доступны позже без каких-либо действий с вашей стороны. Вот эти две точки ожидания:- Запуск сессии: до того как список инструментов будет получен в первый раз, сессия по умолчанию ждёт до 5 секунд сервер HTTP или SSE, в записи которого задано
alwaysLoad: true, или все серверы, если в окружении раннера заданоMCP_CONNECTION_NONBLOCKING=0. В остальных случаях серверы HTTP и SSE подключаются в фоновом режиме. Пока сессия ждёт на этом этапе, она инициализируется медленнее.MCP_CONNECT_TIMEOUT_MSизменяет значение по умолчанию в 5 секунд. - Первый ход: после поступления сообщения первый ход ждёт до 2 секунд stdio-серверы, которые ещё подключаются. Пока сессия ждёт на этом этапе, первый ответ приходит медленнее. Чтобы изменить длительность этого ожидания, задайте
CLAUDE_CODE_MCP_STARTUP_WAIT_MSв окружении раннера. Эта переменная не меняет, какие серверы охватывает ожидание. Требуется Claude Code v2.1.274 или новее.
claude mcp add нет флага alwaysLoad. Чтобы задать этот ключ, добавьте сервер командой claude mcp add-json, которая принимает его в JSON сервера и записывает в .claude.json. В вашем Dockerfile:
Отключение встроенных инструментов сессии
Плоскость управления Anthropic подключает к облачным сессиям собственный MCP-сервер с именем Claude Code Remote. Claude использует инструменты этого сервера, чтобы создавать облачные запланированные задачи, запускать другие облачные сессии и управлять ими, подключать дополнительные репозитории и отслеживать активность в pull request. Чтобы отключить весь сервер, добавьте в настройки правило запрета на уровне сервера. Плоскость управления регистрирует сервер под одним из трёх имён в зависимости от того, как была создана сессия. Claude Code сопоставляет имя в правиле точно, с учётом регистра, поэтому запишите правило для каждого имени, как показано ниже:mcp__Claude_Code_Remote__add_repo. Чтобы вообще запретить подключение сервера, а не удалять его инструменты, вместо этого добавьте три имени без префикса mcp__ как записи serverName в deniedMcpServers.
Поместите правила в настройки, управляемые сервером, чтобы они применялись к сессиям без изменений на раннере, или в ~/.claude/settings.json на раннере. На раннере, который отправляет запросы к модели в Bedrock или Agent Platform, используйте этот файл, поскольку настройки, управляемые сервером, не доходят до таких сессий. В разделе Разрешения и подтверждение инструментов объясняется, как настройки на раннере попадают в сессии.
Чтобы убедиться, что правила вступили в силу, запустите сессию в окружении и попросите Claude перечислить его инструменты MCP. Claude Code удаляет запрещённый инструмент из контекста Claude, поэтому запрещённые инструменты будут отсутствовать в его ответе.
Подсказка сеансов для отправки их работы
Размещенные Anthropic сеансы запускают хукStop, хук Claude Code, который запускается, когда Claude заканчивает отвечать, который подсказывает Claude зафиксировать и отправить его работу. Средство выполнения не устанавливает один. Без него сеанс, который заканчивается с незафиксированными изменениями, оставляет эту работу только на диске средства выполнения, и кнопка Create PR в claude.ai/code остается неактивной до тех пор, пока ветка не существует на удаленном.
Эталонная реализация ниже имеет две части. Объедините блок настроек в ~/.claude/settings.json на хосте средства выполнения, который средство выполнения заполняет в каждый сеанс, и сохраните скрипт как ~/.claude/hooks/stop-hook-nudge.sh на хосте средства выполнения и сделайте его исполняемым:
$CLAUDE_PROJECT_DIR.
Хук Stop только побуждает Claude к действию. Чтобы коммит и отправка сработали, настройте:
- Идентификацию git: задайте
user.nameиuser.emailв образе средства выполнения или запустите средство выполнения с флагом--configure-git - Учётные данные для отправки: используйте git под управлением Anthropic или предоставьте учётные данные, как описано в разделе Включение конфигурации git в образ
- Для возобновлённых сессий: задайте флаг
--push-outcome-on-releaseна каждом средстве выполнения в окружении. Тогда возобновлённая сессия начинается с коммитов, которые Claude отправил в ветку сессии. Об ограничениях флага см. Возобновлённые сессии теряют неотправленную работу.
timeout в настройках хука Stop и перезапустите средства выполнения. Команда git status в скрипте пропускает core.fsmonitor, поэтому может выполняться дольше заданных там 10 секунд. Хук, превысивший таймаут, завершает ход без промпта.
Подмодули и хук Stop
Хук Stop проверяет, на каком коммите находится каждый подмодуль, но не файлы внутри него:- Новый коммит в подмодуле: Claude получает промпт, пока родительский репозиторий не зафиксирует его, даже если
.gitmodulesзадаётignore = allдля этого подмодуля - Незафиксированные файлы в подмодуле: Claude не получает промпт, поэтому эта работа остаётся только на диске средства выполнения
- Неотправленные коммиты в подмодуле: хук Stop не проверяет, отправлены ли они, поэтому родительский репозиторий может указывать на коммит, который больше никто не может получить
Замена более ранней копии скрипта хука Stop
НайдитеGIT_OPTIONAL_LOCKS в вашей копии stop-hook-nudge.sh. Если этой строки нет, у вас более ранняя копия. Замените её текущим скриптом везде, где вы его установили, затем перезапустите средства выполнения. Сохраните настройки хука Stop, за исключением увеличения timeout для большого checkout.
- Когда: если ваши сессии работают с включённой песочницей Bash, замените скрипт сейчас. В противном случае замените его при следующей сборке образа или перезапуске средства выполнения.
- Что меняется: незафиксированные файлы в подмодуле больше не вызывают промпт для Claude. Новый коммит в подмодуле, который
.gitmodulesпомечает какignore = all, теперь вызывает.
Разрешения и подтверждение инструментов
К self-hosted сессии не подключён терминал, поэтому запрос разрешения, оставшийся без ответа, приостанавливает ход, пока пользователь не ответит в интерфейсе. Управляющий уровень Anthropic передаёт список инструментов и правила разрешений каждой сессии вместе с данными задания; конфигурация по умолчанию заранее разрешает рутинные вызовы инструментов, включаяBash, а облачные сессии заранее разрешают редактирование файлов независимо от режима. Для вызова, который ничто заранее не разрешает, запрос выводится в интерфейсе сессии.
Закрепляйте авторежим только в тех окружениях, где контейнеры сессий работают с запретом исходящего сетевого трафика по умолчанию и применены остальные меры из раздела об усилении защиты. Рутинные вызовы инструментов, включая сетевые запросы
Bash, выполняются без участия человека как с набором инструментов, разрешённых по умолчанию, так и в авторежиме, поэтому именно сетевая граница ограничивает, куда могут обращаться эти вызовы.command. Авторежим позволяет сессиям работать без рутинных запросов разрешений: отдельная модель-классификатор проверяет действия перед выполнением и блокирует отклонённые, а явные правила ask по-прежнему принудительно вызывают запрос; о том, что проверяет классификатор, рассказано на странице о режимах разрешений. Раннер добавляет вычисленные сервером флаги перед вызовом обёртки, а для флагов с одним значением, таких как --permission-mode, парсер учитывает последнее вхождение, поэтому флаг, добавленный после "$@", переопределяет значение, переданное сервером:
--allowed-tools со своими правилами, например --allowed-tools "Bash(bazel *) Bash(yarn *) mcp__internal__*". Списочные флаги, такие как --allowed-tools и --disallowed-tools, накапливаются между вхождениями, а не переопределяют друг друга, поэтому ваши правила применяются поверх любых правил, переданных управляющим уровнем. Чтобы сузить набор, добавьте --disallowed-tools: этот флаг запрещает инструменты, даже если другое правило их разрешает.
Как собирается конфигурация каждой сессии
Раннер выдаёт каждой сессии собственный каталог конфигурации, заполняемый из снимка~/.claude/ хоста, который раннер делает один раз при запуске: settings.json, CLAUDE.md, хуки, агенты, команды и скиллы в образе раннера применяются к каждой сессии как базовый пользовательский уровень. Если вы измените конфигурацию на работающем хосте, изменение вступит в силу только после перезапуска раннера.
Задайте SELF_HOSTED_RUNNER_HOST_CONFIG_DIR, чтобы заполнять каталог из другого пути, или укажите пустой каталог, чтобы отключить заполнение.
Сессии также читают следующие файлы настроек:
- Настройки проекта: вне треда проекта закоммиченный в репозиторий
.claude/settings.jsonнакладывается поверх базового пользовательского уровня. В сессии с несколькими репозиториями действует файл не более чем одного репозитория. - Управляемые настройки: сессии читают
managed-settings.jsonиз стандартного системного пути в образе раннера. О том, применяются ли его ключи наряду с настройками, управляемыми сервером, см. как Claude Code объединяет управляемые источники.
- Куда они попадают: раннер записывает каждый переданный скрипт хука в зарезервированный подкаталог
hooks/.ccr-launcher/каталога конфигурации сессии и регистрирует скрипты в отдельном файле настроек, который передаёт сессии через--settings, не затрагивая заполненныйsettings.jsonи ваши собственные скрипты вhooks/<name>. Раннер пересоздаёт зарезервированный подкаталог для каждой сессии и не переносит в сессии содержимое хоста из~/.claude/hooks/.ccr-launcher/. - Кто их создаёт: управляющий уровень заполняет скрипты из фиксированных констант собственного развёртывания, никогда — из данных конкретной сессии или сторонних данных.
- Что по-прежнему ими управляет: хуки, переданные через
--settings, входят в обычную объединённую конфигурацию хуков, а не в управляемый уровень, поэтому ваши управляемые настройки продолжают действовать.disableAllHooksотключает их, и они не входят в категории, которыеallowManagedHooksOnlyоставляет загруженными.
.claude/skills/ репозитория.
За пределами сессий Claude Tag сессия в self-hosted окружении по умолчанию работает с отключённой автоматической памятью. Для инструкций, которые должны сохраняться между сессиями, используйте CLAUDE.md в образе раннера или в репозитории.
Снимок ~/.claude/ хоста, который делает раннер, не включает каталог projects/. Место хранения автоматической памяти по умолчанию находится в этом каталоге. Если вы положите туда файлы памяти, раннер не перенесёт их в сессии, и они не включат автоматическую память.
Остановка сессий, которым не удалось получить настройки, управляемые сервером
Чтобы сессия, получающая настройки, управляемые сервером, не запускалась, если получить их не удалось, задайтеforceRemoteSettingsRefresh значение true в managed-settings.json в образе раннера. Ответ API Anthropic о том, что у вашей организации нет настроек, управляемых сервером, считается успешным получением.
Прежде чем добавлять ключ, проверьте, запускает ли окружение сессии каналов Claude Tag и отправляет ли оно запросы к модели в Bedrock или Agent Platform:
- Сессии каналов Claude Tag: не задавайте ключ на раннерах в окружении, на которое Owner направляет каналы Claude Tag. API Anthropic отклоняет запрос этих сессий на получение настроек, управляемых сервером, поэтому при заданном ключе они не запускаются. Поместите их правила в
managed-settings.jsonобраза раннера, который они по-прежнему читают. Чтобы использовать ключ для других сессий, запускайте их в отдельном окружении. - Bedrock или Agent Platform: сессии у этих провайдеров не получают настройки, управляемые сервером, поэтому они запускаются независимо от того, задан ли ключ. Вместо этого применяйте свои правила через тот же
managed-settings.json.
managed-settings.json задаёт ключ:
managed-settings.json
Your organization requires remote managed settings to load, but they could not be loaded.
Настройки репозиториев в сессиях с несколькими репозиториями
В треде проекта.claude/settings.json ни одного репозитория не действует как настройки проекта, а .mcp.json и скиллы ни одного репозитория не загружаются, где бы ни начинался тред. Чтобы дать таким тредам скилл, добавьте его в образ раннера.
В любой другой сессии с несколькими репозиториями Claude Code читает настройки проекта из каталога, в котором начинается сессия, поэтому как настройки проекта действует .claude/settings.json не более чем одного репозитория. Хук, определённый в файле другого репозитория, не выполняется, правило запрета в нём не применяется, а его env не задаётся.
--capacity 1(по умолчанию) со встроенным checkout: сессия начинается в первом репозитории из своего списка репозиториев..claude/settings.jsonэтого репозитория действует как настройки проекта, и его.mcp.jsonзагружается, а у остальных репозиториев — нет.--capacityбольше единицы или хукcheckout: сессия начинается в отдельном каталоге сессии, содержащем checkout-копии..claude/settings.jsonни одного репозитория не действует как настройки проекта,.mcp.jsonни одного репозитория не загружается, а$CLAUDE_PROJECT_DIRв команде хука указывает на этот каталог, а не на checkout-копию.
CLAUDE.md каждого репозитория загружается, где бы ни начиналась сессия. Вне треда проекта загружаются и его скиллы. Раннер передаёт каждый репозиторий в Claude Code как дополнительный каталог, поэтому Claude Code также читает ключи enabledPlugins и extraKnownMarketplaces из .claude/settings.json каждого репозитория.
Чтобы запускать хук или применять правило разрешений в каждой сессии, поместите его в ~/.claude/settings.json на хосте раннера. Раннер переносит файл хоста в каждую сессию, где бы она ни начиналась. Указывайте путь в правиле Read или Edit как абсолютный шаблон с // или относительный от домашнего каталога с ~/, потому что другие шаблоны привязываются к источнику настроек или текущему каталогу.
Правила разрешений, закоммиченные в репозиторий
Не добавляйте голую запись"Edit", "Write" или "NotebookEdit" в закоммиченный в репозиторий permissions.allow. Голое правило для файлового инструмента соответствует инструменту независимо от пути и разрешает запись в любом месте хоста, а не только в рабочем пространстве, поэтому защита раннера, ограничивающая область записи, помечает сессию; с --confine-repo-settings enforce раннер отказывается запускать сессию, вместо того чтобы записать предупреждение в лог и продолжить. См. раздел об усилении защиты.
Репозиторию вообще не нужно правило для файловых инструментов: облачные сессии заранее разрешают редактирование файлов независимо от режима. Если вы всё же делаете коммит правила, ограничьте его рабочим пространством, например "Edit(/**)"; одиночная косая черта в начале указывает путь относительно корня проекта, которым является рабочее пространство сессии. Голые правила для файловых инструментов допустимы в settings.json уровня хоста, который ведёт оператор, поскольку этот файл не коммитится в репозиторий.
defaultMode со значением auto учитывается только из файла настроек уровня образа или пользовательского уровня, поэтому извлечённый репозиторий не может сам предоставить себе авторежим. О том, какие режимы принимают облачные сессии, и о полном синтаксисе правил см. режимы разрешений.
Что дальше
- Reference: каждый флаг CLI, переменная окружения и метрика
- Verify session identity: проверьте токен сеанса из служб вне средства выполнения