Перейти к основному содержанию
Некоторые вызовы инструментов слишком значимы, чтобы разрешать вслепую, и слишком полезны, чтобы запрещать напрямую — запись в production-базу, банковский перевод, *.delete на реальных данных. Для них вы хотите человека в цикле: удержать вызов, дать человеку посмотреть, затем продолжить только по «да». Это ровно то, что делает вердикт pending_approval. Эта страница покрывает поток подтверждения агента человеком в цикле от начала до конца: как удержанный вызов всплывает, как ревьюер разрешает его из консоли или вебхука, и как агент повторно отправляет одобренный вызов. О том, где вердикт сидит в грамматике правил, см. Правила Firewall; о модели политик вокруг него см. обзор Firewall.

1. Как выглядит удержанный вызов

Когда правило разрешается в pending_approval, движок ставит запись подтверждения в очередь, и вызов не достигает инструмента. Ретрансляция возвращает HTTP 400 с error.code firewall_approval_pending; id подтверждения, по которому агент будет опрашивать, несётся в читаемом человеком error.message:
{
  "error": {
    "code": "firewall_approval_pending",
    "message": "tool \"db.write\" held for approval (…) — resolve approval 507f1f77bcf86cd799439011 and retry with header X-OrcaRouter-Firewall-Approval"
  }
}
Структурированный error.metadata (когда присутствует) несёт детали причины вердикта — reason_code, factors, risk_score — а не id подтверждения. Распарсите id из сообщения или получите его из SDK-хелпера ниже. Удержание немедленное — нет inline long-poll, блокирующего ваш запрос. Агент получает id обратно, вызов паркуется на стороне сервера в состоянии pending, и разрешение происходит вне основного канала.
Удержанный вызов записывается как событие firewall с вердиктом pending_approval, так что он фильтруется в журнале событий прямо рядом с событиями deny — вы всегда можете видеть, что было удержано и, через запись подтверждения, что было разрешено.

2. Один конкретный пример

Создайте правило, которое удерживает любую запись в production-подключение для человека:
{
  "label": "hold prod db writes",
  "tool_name_glob": "db.write",
  "verdict": "pending_approval",
  "args_match_json": "{\"clauses\":[{\"path\":\"$.connection\",\"op\":\"eq\",\"value\":\"prod\"}]}"
}
Теперь жизненный цикл:
1

Агент вызывает инструмент

Агент выпускает db.write против prod. Правило совпадает, движок удерживает вызов, и ретрансляция возвращает 400 firewall_approval_pending с approval_id.
2

Человек (или ваша система) проверяет

Ревьюер разрешает подтверждение — в консоли или через подписанный вебхук-колбэк (см. §3).
3

Агент опрашивает до разрешения

Агент опрашивает id подтверждения, пока его состояние не перестанет быть pending (см. §4).
4

Агент повторно отправляет с заголовком подтверждения

При approved агент повторно выпускает ровно тот же вызов один раз, неся одноразовый заголовок X-OrcaRouter-Firewall-Approval. Движок забирает подтверждение и пропускает этот один вызов.

3. Разрешение подтверждения

Есть два способа превратить pending-подтверждение в approved или rejected. Оба разделяют гарантию first-decision-wins — первое приземлившееся разрешение применяется атомарно, а любое более позднее разрешение (или дубликат) — идемпотентный no-op, возвращающий 200.
Вкладка Approvals перечисляет удержания pending старейшими-первыми, каждое с именем инструмента и строкой «Held because…», называющей политику и клаузу правила, которая сработала. (Сырые аргументы вызова не хранятся в записи подтверждения — только имя инструмента, происхождение и хеш аргументов — так что ревьюер решает по инструменту плюс совпавшей клаузе.) Ревьюер разрешает одно так:
PATCH /api/workspace/firewall/approvals/:id
{ "decision": "approved", "reason": "verified change ticket #4821" }
decision должен быть approved или rejected. Этот маршрут — UserAuth (консольная сессия ревьюера) и ограничен Developer+ — личность вашего ревьюера и есть авторизация, так что общий секрет не задействован. Разрешения пишутся в журнал аудита рабочего пространства.
Чтобы подключить подтверждения во внешнюю систему (одобрение в Slack, тикетный процесс), настройте секрет вебхука подтверждений для рабочего пространства, затем POST’ните решение обратно:
POST /api/v1/firewall/approvals/:id/callback
{ "decision": "approved", "reason": "auto-approved by change-control bot" }
Колбэк аутентифицируется через HMAC-SHA256: установите заголовок X-Orca-Signature: sha256=<hex> в HMAC от <approval_id>\n<raw_body>, ключённый секретом вебхука подтверждений вашего рабочего пространства. Id — часть подписанного материала, так что захваченную подпись нельзя воспроизвести против другого подтверждения. Без настроенного секрета разрешение через колбэк отклоняется — разрешайте через консольный PATCH вместо этого.
Настройка пути отклонения вебхука подтверждений — безопасный дефолт для прогонов без присмотра: если ни один человек не разрешает удержание, вызов просто остаётся припаркованным, а агент продолжает опрашивать. Удержанный вызов никогда молча не становится allow.

4. Опрос, затем повторная отправка

Сторона агента — это цикл опроса, за которым следует одна повторная отправка. Опрашивайте состояние подтверждения токеном с областью firewall-gateway:
GET /api/v1/firewall/approvals/:id
Этот маршрут требует токена с областью firewall-gateway (тот же выделенный ключ шлюза, используемый для /evaluate и MCP-шлюза); обычный ретрансляционный ключ получает 403. Он возвращает документ подтверждения — ждите, пока state не станет approved или rejected, а не pending. Кросс-workspace или неизвестный id возвращает 404, никогда не раскрывая другому тенанту, что он существует. Повторно отправьте, как только состояние — approved: повторно выпустите тот же вызов инструмента, неся id подтверждения в одноразовом заголовке:
X-OrcaRouter-Firewall-Approval: 507f1f77bcf86cd799439011
Движок атомарно забирает подтверждение — одноразовое. Первая повторная отправка, несущая его, пропускается этот один раз; повтор того же заголовка находит подтверждение уже потреблённым и удерживается снова, не пропускается. rejected-подтверждение никогда не забирается, так что агент должен трактовать отклонение как терминальный deny и выбрать другой путь.
HITL-хелпер OrcaRouter MCP SDK выполняет этот цикл опрос-затем-повтор за вас: когда evaluate возвращает pending_approval, он опрашивает GET /api/v1/firewall/approvals/:id и повторно отправляет с заголовком подтверждения при одобрении — вы только создаёте правило и обеспечиваете ревьюера.

5. Состояния и роли с одного взгляда

СостояниеЗначениеДействие агента
pendingУдержано, ожидает решенияПродолжать опрашивать
approvedРевьюер сказал даПовторить один раз с заголовком
rejectedРевьюер сказал нетТрактовать как deny
ДействиеМаршрутAuth · роль
Список очередиGET /api/workspace/firewall/approvalsUserAuth · Developer+
РазрешитьPATCH /api/workspace/firewall/approvals/:idUserAuth · Developer+
Вебхук-колбэкPOST /api/v1/firewall/approvals/:id/callbackПодписано HMAC
Опрос состоянияGET /api/v1/firewall/approvals/:idТокен шлюза

6. Куда вписываются подтверждения

Вердикт pending_approval — один из вердиктов firewall — он компонуется со всем остальным в политике. Два взаимодействия, которые стоит знать:
  • Карантин навыка эскалирует до удержания. Если удержанный вызов инструмента принадлежит карантинному навыку, всё, что меньше deny, автоматически эскалируется до pending_approval — карантин и подтверждения — это один и тот же шлюз разбора с двух направлений.
  • Shadow-режим уплощает его. В shadow-режиме вердикт pending_approval понижается до audit и логируется как [shadow] would …, так что вы можете измерить, как часто удержание сработало бы до того, как оно начнёт ограничивать реальный трафик.
Это правильный контроль для опасных вызовов инструментов и избыточной автономии — случаев, где вердикт «спросить человека» бьёт и allow, и deny.

Куда двигаться дальше

Вердикты

Все шесть вердиктов firewall и default-вердикт.

Ключи шлюза

Создайте токен firewall-gateway, используемый для опроса подтверждений.

Shadow-режим

Измерьте удержание до того, как оно ограничит реальный трафик.

Справочник правил

Создайте правило, которое производит вердикт pending_approval.