Коротко
- У каждого действия агента свой поводок. Повторяющееся агент делает сам, новую команду сначала смотрит LLM-ревьюер (модель, которая оценивает команду до запуска), отправку наружу и необратимое решаете вы.
- В OpenClaw это список разрешённых команд (allowlist) и постоянные разрешения для задач cron (standing grants), режим
tools.exec.mode: "auto"и хукbefore_tool_call, который требует вашего одобрения. Запрещённое насовсем снимает политика инструментов. - Где не держит: ревьюер смотрит только exec и сам решает, что рискованно; карточки одобрения от cron в Telegram не приходят; «разрешить всегда» у одобрений из плагина по умолчанию ничего не запоминает.
Кому и когда
Агент работает по расписанию или подолгу без присмотра. Режим ask шлёт карточку на каждую новую команду, и через день вы жмёте «разрешить», не читая. Режим full, умолчание хоста, не спрашивает ничего. Нужна середина: звать редко, но по делу.
Собрано по документации и исходникам OpenClaw 2026.9.8 (октябрь 2026); конфиги запуском не проверялись.
Три длины поводка
| Уровень | Что сюда | Чем выражается |
|---|---|---|
| Сам | Известные команды, одна и та же задача cron | allowlist, standing grant |
| С ревьюером | Новые команды exec, которых нет в allowlist | tools.exec.mode: "auto" |
| Только человек | Отправка сообщений, публикации, платежи; классы команд, названные заранее (git push, rm -r) |
хук before_tool_call с requireApproval для message и для exec |
Вокруг всех трёх забор: чего агент не должен делать никогда, убирается через tools.deny. Правило в AGENTS.md модель может не выполнить, политику инструментов Gateway исполняет, даже если агента уговорили. Сначала запреты, потом расписание.
Как выбрать уровень
- Эффект выходит наружу? Сообщение, пост, платёж — только человек: отправленное не отзовёшь.
- Откатывается одной командой? Правка файла под git — да.
rm -rf, миграция базы,git push --force— нет: только человек. - Повторяется в одном и том же виде? Тогда в «сам». Остальное, дешёвое по ошибке, — ревьюеру.
Уровень 2: ревьюер
В режиме auto команда из allowlist выполняется сразу, остальные получают вердикт ревьюера:
allow— команда низкого или среднего риска выполняется один раз, без запоминания;deny— причина уходит агенту, и он обязан выбрать заметно более безопасный путь или спросить вас;ask— карточка вам. Туда же идут сбой ревьюера, невалидный ответ, таймаут (до 30 секунд на каждый из двух этапов) и третий отказ подряд в одной сессии.
Ревьюер судит по выдержке из разговора, служит ли команда задаче, и границу риска проводит он, а не вы. По умолчанию это основная модель агента. Поставьте другую: модель склонна верить своим рассуждениям, подробнее в материале о враждебной проверке.
Для одной сессии похожее даёт режим workspace в меню Execution permissions в Control UI. Он ещё ограничивает корнем сессии файловые инструменты, но не exec.
Уровень 3: человек
ask и auto — значения одного поля tools.exec.mode, вместе их не включить. Поэтому команды, которые у агента в auto всегда должны идти к вам, назовите хуку before_tool_call с matcher: ["exec"]. Сообщения ревьюер не видит вообще, для них тот же хук на message. /exec ask=always действует только на одно ваше сообщение.
Наружу у message отправляет не только send, поэтому хук ловит явный набор действий. Где отправка агенту не нужна, надёжнее убрать message через tools.deny.
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
const OUTBOUND = new Set([
"send", "broadcast", "reply", "thread-reply", "thread-create",
"sendWithEffect", "sendAttachment", "upload-file", "poll", "sticker",
"edit", "delete",
]);
const RISKY_EXEC = [/\bgit\s+push\b/, /\brm\s+-\w*r/];
const ask = (title: string, description: string) => ({
requireApproval: {
title,
description: description.slice(0, 500),
severity: "critical" as const,
allowedDecisions: ["allow-once", "deny"],
timeoutMs: 600_000,
},
});
export default definePluginEntry({
id: "leash",
name: "Leash",
register(api) {
api.on(
"before_tool_call",
(event) => {
if (event.toolName === "message") {
const action = String(event.params.action ?? "");
if (OUTBOUND.has(action)) return ask("Отправка сообщения", `message.${action}`);
return;
}
const command = String(event.params.command ?? "");
if (RISKY_EXEC.some((re) => re.test(command))) return ask("Команда", command);
},
{ matcher: ["message", "exec"], priority: 50 },
);
},
});
Хук добавляет вас поверх ревьюера, но сверх exec-политики не разрешит. Регулярки — грубый фильтр: скрипт с git push внутри они не увидят.
Конфиг: поводок, ревьюер и карточки в Telegram. Это для интерактивных сессий, карточки cron туда не приходят.
{
tools: { exec: { grantExpiryDays: 30 } },
agents: {
entries: {
ops: {
tools: {
deny: ["browser"],
exec: {
mode: "auto",
reviewer: { model: "<другой провайдер>/<модель>", thinking: "low" },
},
},
},
},
},
approvals: {
exec: { enabled: true, mode: "targets", agentFilter: ["ops"], targets: [{ channel: "telegram", to: "<ваш chat id>" }] },
plugin: { enabled: true, mode: "targets", agentFilter: ["ops"], targets: [{ channel: "telegram", to: "<ваш chat id>" }] },
},
plugins: { load: { paths: ["~/.openclaw/policies/leash.ts"] } },
}
deny: ["browser"]убирает инструмент у агента совсем, браузер тут для примера.approvals.execиapprovals.pluginнезависимы, нужны оба.- В Telegram карточка приходит с кнопками. Ответ
/approve <id> allow-onceв том же чате сработает, если вы в списке approvers канала. Выдуманный id ничего не одобряет. - Изменили файл политики —
openclaw plugins reload leash.
Проверить: openclaw approvals get показывает запрошенную и итоговую политику, openclaw exec-policy show --agent ops — что видит агент. Затем попросите агента отправить сообщение и дождитесь карточки. Откатить: уберите exec.mode у агента и путь из plugins.load.paths. Учтите, что агент вернётся к умолчанию хоста, то есть к full без одобрений. Промежуточный вариант — mode: "ask".
Уровень 1: повторяющееся — в «сам»
Карточка на каждый ночной запуск приучит жать кнопку не глядя. Путей два.
Без клиента одобрений — allowlist. Шаблон — глоб пути к бинарнику или имени команды.
openclaw approvals allowlist add --agent ops "/usr/bin/rsync"
Запись разрешает бинарник с любыми аргументами, сузить их можно полем argPattern в файле одобрений.
Через карточку cron. Она приходит только в Control UI, приложения macOS, iOS и Android или API-клиент с поддержкой одобрений, не в Telegram. Откройте Control UI на время первого запуска и нажмите Always allow: Gateway выпустит standing grant на этого агента, эту задачу и именно эту команду с этой папкой и окружением. Грант слетает, если задачу удалили или изменили, команда отличается хоть на байт, грант отозвали или он истёк. Пауза задачи его не сбрасывает. По умолчанию грант бессрочный, grantExpiryDays: 30 заставит одобрять новые раз в месяц.
openclaw approvals grants list
openclaw approvals grants revoke <grant-id>
Где не держит
- Ревьюер только для exec. Браузер, сообщения и MCP-инструменты он не смотрит. У агента на Codex-харнессе ревьюер OpenClaw не запускается, команды идут человеку.
- Cron без клиента одобрений получает отказ сразу. Не подключены ни Control UI, ни приложение — запрос отклоняется. Exec на нодах из автоматизаций карточек не получает вообще.
- Обёртки требуют человека.
sh -c '…',env FOO=1 …,bash -lcнельзя надёжно привязать к исполняемому файлу, поэтому ревьюер их пропускает к вам на разовое одобрение, а в cron без клиента они получают отказ. Попросите вAGENTS.mdписать команды без обёрток. - «Разрешить всегда» из хука ничего не запоминает. Оба «разрешить» значат «на этот раз», поэтому в примере этой кнопки нет.
- Тишина значит «нет». Карточка плагина ждёт 2 минуты по умолчанию, максимум 10, потом вызов блокируется. Нет маршрута к человеку — блок сразу.
- Одобрения не разделяют людей. Любой авторизованный на Gateway для него доверенный оператор. Это защита от случайности, а не граница прав.
Когда не надо
- Вы и так сидите рядом. В короткой сессии
askпроще, ревьюер ничего не сэкономит. - Агент только читает и пишет черновики. Уберите у него отправку, и поводок не нужен.
- Есть только Telegram. Карточки cron не придут: держите ночные задачи на allowlist.