Читать 7 мин

Ошибки инструментов агентов, которые помогают, не раскрывая секреты

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

Ошибки инструментов агентов, которые помогают, не раскрывая секреты

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

Я видел, как проблема начиналась с безобидного поля для отладки. Кто-то добавлял request_headers, потому что агент постоянно получал ответы 401. Агент копировал ошибку в рабочие заметки. Затем эти заметки попадали в pull request, систему поддержки или сохранённую трассировку оценки. Исходный сбой проходил. Секрет оставался.

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

Ответ об ошибке входит в границу безопасности

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

У вызова инструмента две аудитории. Агенту нужны операционные факты: выполнилось ли действие, можно ли повторить его, требуется ли согласие и какие входные данные нужно изменить. Оператору нужны факты для расследования: какие учётные данные выбраны, какой точный маршрут вызван, какой DNS-резолвер не сработал и что ответил удалённый узел. Не удовлетворяйте вторую аудиторию, передавая её доказательства первой.

Разница особенно важна, когда агент может многократно вызывать инструменты. Человек, один раз увидев подробную ошибку, может заметить в ней bearer-токен. Агент способен сохранить его на десятки ходов, а затем включить в сгенерированный код или тестовый шаблон. Для этого модели не нужны злые намерения.

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

Давайте агентам решения, а не строки исключений

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

Используйте небольшой документированный набор кодов. Каждый код должен соответствовать конкретному поведению вызывающей стороны. Не делайте коды настолько подробными, чтобы в итоге воспроизвести каждую возможную ошибку исходной системы.

Практическая форма ответа может выглядеть так:

{
  "ok": false,
  "code": "AUTH_FAILED",
  "message": "The remote service rejected the stored credential.",
  "action": "stop_and_report",
  "retryable": false,
  "request_id": "act_7f3c2a91",
  "http_status": 401
}

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

Поле action полезнее одного расплывчатого логического значения. Boolean сообщает, может ли время исправить проблему. Действие говорит агенту, что именно делать. Держите список значений коротким:

  • retry_after_delay для временных операций, которые безопасно повторить.
  • repair_input для запроса, который агент может исправить без новых полномочий.
  • request_approval, когда действие должен разрешить человек.
  • stop_and_report для ошибок, требующих работы оператора.
  • inspect_outcome, когда запись могла дойти до удалённого сервиса.

Последний вариант требует особого внимания. Тайм-аут после отправки записи отличается от тайм-аута до отправки. Если свести оба случая к NETWORK_ERROR, агент повторит действие, которое, возможно, уже завершилось. Так появляются дубликаты тикетов, развёртываний и записей, а также повторные разрушительные команды.

Возвращайте http_status только тогда, когда он действительно полезен и безопасен. Для HTTP-вызовов это часто хороший контекст, но он не даёт полного ответа. Код 403 может означать отказ авторизации на стороне сервиса, ограничение конкретного ресурса или решение шлюза. Код инструмента должен отдельно указывать поведение агента.

Держите таксономию кодов небольшой и стабильной

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

Начните с категорий, которые позволяют вызывающей стороне выбрать другое действие:

КодЗначениеПоведение агента
INVALID_INPUTИнструмент отклонил переданные поля до внешнего действия.Исправить ввод.
VAULT_LOCKEDШлюз не может использовать ни один сохранённый секрет.Попросить человека разблокировать хранилище.
USER_DENIEDЧеловек отклонил это действие.Остановиться. Не переформулировать и не отправлять снова.
AUTH_FAILEDУдалённый сервис отклонил выбранные учётные данные.Остановиться и сообщить.
REMOTE_FORBIDDENЗапрос прошёл аутентификацию, но не имеет удалённого разрешения.Остановиться и сообщить.
RATE_LIMITEDУдалённый сервис попросил снизить частоту вызовов.Подождать, если известна безопасная задержка.
TEMPORARY_FAILUREВременная ошибка повторяемого запроса.Повторить в пределах ограниченного бюджета.
OUTCOME_UNKNOWNЗапись могла завершиться до возникновения ошибки.Проверить результат до любого повтора.
NETWORK_UNREACHABLEШлюз не смог связаться с удалённой конечной точкой.Повторять только безопасные для повтора действия.
INTERNAL_FAILUREШлюз завершился без безопасного способа исправления на стороне вызывающего объекта.Остановиться и сообщить идентификатор запроса.

Не используйте ERROR, FAILED или EXCEPTION как основной контракт. Такие ярлыки перекладывают интерпретацию на агента, который начнёт выводить способ исправления из текста. Он может сделать это неправильно.

Сохраняйте значения кодов стабильными. Можно улучшить сообщение для человека, добавить поле retry_after_seconds или включить новое безопасное значение состояния. Но не меняйте смысл AUTH_FAILED, превращая его одновременно в ошибку учётных данных и отказ локального подтверждения. Агент и окружающая оркестрация со временем начнут ветвиться по этому коду.

RFC 9457, «Problem Details for HTTP APIs», даёт полезную основу: ответы могут содержать стабильный тип проблемы, заголовок, статус, подробности и ссылку на экземпляр. Важнее всего предупреждение, которое команды часто пропускают. RFC указывает, что поле detail должно помогать исправить проблему, а сведения о проблеме могут раскрывать чувствительные данные. Для инструментов агентов сделайте стабильный тип или код контрактом, держите подробности короткими, а поле instance или идентификатор запроса используйте для связи оператора с защищёнными доказательствами.

Заголовки запросов и ошибки соединения являются доказательствами, а не контекстом

Команды часто называют исходные заголовки и транспортные сообщения «контекстом». На самом деле это доказательства. Доказательства должны находиться в аудиторской записи с контролем доступа, а не в ответе агенту.

Представьте неудачный API-запрос. Типичное исключение HTTP-библиотеки может содержать полный запрошенный URL, адрес перенаправления и прокси, заголовки ответа и часть тела ответа. Любое из этих полей может нести секреты. В старых API ключи всё ещё передаются в параметрах запроса. Заголовки Location часто содержат подписанные URL для скачивания. Cookies и пользовательские заголовки авторизации очевидно опасны. Менее очевидные поля, например X-Request-Id, могут быть безопасны, тогда как X-Forwarded-Host или внутренний заголовок сервиса способен раскрыть инфраструктуру, которая агенту не нужна.

Для SSH нужна такая же дисциплина. Не возвращайте командную строку с закрытым адресом, путём к known_hosts, предложенным файлом ключа или исходным текстом ошибки о несовпадении ключа хоста. Для вызывающей стороны важен смысл: соединение заблокировано, потому что удалённую личность нельзя проверить. Верните этот смысл. Сравнение отпечатков, пути и диагностику библиотеки оставьте оператору.

OAuth 2.0 особенно ясно показывает проблему. RFC 6750 говорит, что bearer-токен даёт доступ тому, кто им владеет, и требует защищать токены от раскрытия при хранении и передаче. Обработчик ошибок, копирующий bearer-токен в трассировку, нарушает это требование, даже если исходный запрос корректно использовал TLS.

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

Используйте список разрешённых полей для данных, которые переходят к агенту. Список запрещённых полей однажды пропустит x-api-token, подписанный параметр, пользовательское поле сессии или новое свойство библиотеки. Список разрешённых полей начинается с полного запрета на раскрытие и добавляет только поля с понятной целью для вызывающей стороны.

Разделяйте причину сбоя и факт выполнения действия

Подтверждайте процесс, а не текст запроса
Для новых процессов агента требуется одно подтверждение с указанием центра сертификации, подписавшего запрос.

Безопасный дизайн ошибки должен сообщать агенту, могло ли удалённое устройство выполнить действие. Именно здесь большинство рекомендаций по повторам становятся опасными.

Предположим, агент отправляет POST /deployments, а соединение завершается по тайм-ауту. Шлюз знает, что попытался выполнить вызов. Но он не знает, получил ли запрос удалённый сервис, создал ли тот развёртывание или исчез ли ответ по пути обратно. Возврат TEMPORARY_FAILURE подтолкнёт агента отправить ещё один запрос на развёртывание. Возврат AUTH_FAILED будет просто ложью. Правильное состояние: OUTCOME_UNKNOWN.

Ответ должен прямо сообщать об этом:

{
  "ok": false,
  "code": "OUTCOME_UNKNOWN",
  "message": "The connection ended after the request started. The remote action may have completed.",
  "action": "inspect_outcome",
  "retryable": false,
  "request_id": "act_9b18d4e0",
  "operation": "create_deployment"
}

operation обозначает общий класс действия, а не полный маршрут или содержимое запроса. Теперь агент может использовать отдельный доступный только для чтения вызов состояния, если интеграция его предоставляет. Если API поддерживает ссылки идемпотентности, шлюз может связать безопасную ссылку с операцией и проверить её внутри. Не раскрывайте значение идемпотентности, если в целевой системе оно одновременно служит материалом для доступа.

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

Установите бюджет повторов в шлюзе. Ответ может содержать ограниченную задержку, например retry_after_seconds: 30, но только если безопасное значение предоставил исходный сервис или лимит контролирует сам шлюз. Не позволяйте агенту повторять запрос бесконечно из-за сообщения со словом «временная». Повторяющиеся сбои создают шум, расходуют лимиты и затрудняют последующее расследование.

Отказ человека должен иметь отдельный смысл

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

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

Верните USER_DENIED с коротким сообщением вроде «Запрошенное действие не было одобрено и не выполнялось». Не упоминайте имя секрета, выбранную учётную запись или точную цель, если эти сведения уже не входят в безопасный контракт входных данных инструмента. Вызывающей стороне нужно остановиться. Человек сам решит, следует ли создать новый явный запрос.

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

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

Не пытайтесь компенсировать усталость от подтверждений дополнительной диагностикой. Если пользователи регулярно одобряют вызовы, не проверяя их, исправьте группировку действий, область доступа и идентичность процесса, отображаемые при подтверждении. Более подробные отказы не делают поспешное согласие безопаснее.

Создайте путь диагностики из двух записей

Оставляйте доказательства, которые агент не может изменить
Представления Activity и Sessions строятся на основе одного зашифрованного журнала с цепочкой хешей.

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

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

Идентификатор запроса должен быть непрозрачным. Создавайте его независимо от учётных данных и адресов назначения. Не кодируйте в нём имя хоста, имя пользователя, временную метку, раскрывающую закономерности активности, или последовательный идентификатор базы данных, если эти сведения важны в вашей среде. Идентификатор позволяет пользователю сказать «проверьте act_9b18d4e0», не передавая ему исходную диагностику.

Для важных действий записывайте, какой границы достиг шлюз: проверка ввода, выбор учётных данных, авторизация пользователя, начало соединения, отправка байтов запроса, получение ответа и возврат результата. Такая последовательность даёт оператору обоснованное объяснение OUTCOME_UNKNOWN, не показывая агенту исходный запрос.

Для внутреннего пути важна защита от изменения данных. Если кто-то может незаметно удалить неудачные попытки авторизации или переписать причину блокировки действия, журнал аудита превращается в обычный служебный лог. Sallyport строит представления сессий и вызовов на основе зашифрованного журнала с цепочкой хешей, а sp audit verify проверяет цепочку офлайн по шифротексту. Это помогает оператору доверять записи, не предоставляя агенту доступ к её содержимому.

Защищённая запись может выглядеть так. Это намеренно не ответ агенту:

{
  "request_id": "act_9b18d4e0",
  "event": "http_call_failed",
  "credential_record": "cred_42",
  "destination": "api.internal.example",
  "method": "POST",
  "path_template": "/deployments",
  "bytes_sent": true,
  "response_received": false,
  "exception_class": "ReadTimeout",
  "result_code": "OUTCOME_UNKNOWN"
}

Даже здесь внимательно проверяйте поля. Полный путь может раскрыть идентификаторы ресурсов. Тело запроса обычно нужно превращать в односторонний отпечаток, имя схемы или строго очищенное представление. Операторам часто достаточно сравнить две попытки, а не прочитать каждое переданное значение.

Для сообщений об ошибках нужна продуманная политика очистки

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

Сначала определите прямые секреты: bearer-токены, пароли, закрытые ключи, cookies, подписанные URL, заголовки авторизации и клиентские сертификаты. Затем определите чувствительный контекст: внутренние DNS-имена, локальные пути, имена пользователей и репозиториев, идентификаторы ресурсов, тела запросов и заголовки, раскрывающие топологию развёртывания. Вторая категория может быть допустима в защищённой записи, но редко должна появляться в ошибке, видимой агенту.

Не возвращайте сообщение «учётные данные, заканчивающиеся на 7KQ2, отклонены». Команды добавляют такие детали, потому что учётных записей несколько и оператору нужно знать, какая из них не сработала. Но так создаётся постоянный идентификатор, который можно связать между трассировками. Агенту достаточно AUTH_FAILED. Оператор проверит запись учётных данных по защищённому идентификатору запроса.

По умолчанию не повторяйте входные данные инструмента. Агент уже знает, что пытался сделать, но шлюз не может считать сами входные данные безопасными для повторного вывода. URL может содержать подписанную строку параметров. Команда может включать присваивание переменной окружения. JSON может содержать временные учётные данные, полученные агентом от другой системы. Возвращайте ошибку проверки полей, например invalid_fields: ["repository"], а не скопированное неверное значение.

Проверяйте очистку на специально подготовленных опасных тестовых данных. Добавляйте секреты в необычном регистре, повторяющиеся заголовки, данные пользователя в URL, процентно-кодированные параметры, вложенный JSON, причины исключений и аргументы SSH-команд. Затем проверяйте, что ни один секрет из набора не появляется в сериализованном ответе инструмента, обычных логах, метках метрик или данных о падении. Тест только успешного пути ничего не доказывает о сбоях.

Считайте сообщения удалённых сервисов ненадёжными входными данными

Отправляйте HTTP-запросы без передачи токенов
Выполняйте HTTP-запросы с автоматической подстановкой учётных данных, не передавая агенту bearer-, basic- или пользовательские заголовки.

Удалённый API может вернуть полезное тело ошибки, HTML-страницу входа или текст, предназначенный для влияния на того, кто его прочитает. Шлюз не должен передавать этот текст напрямую в контекст агента.

Это отчасти вопрос секретов. Серверы иногда возвращают в страницах ошибок исходные значения запроса. Неверный заголовок Authorization, cookie, параметр или поле JSON может вернуться в диагностическом ответе. Передача такого ответа превращает удалённое отражение в утечку учётных данных.

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

Извлекайте из ответа сервиса только известные безопасные поля. Например, числовой HTTP-статус и документированная задержка при ограничении частоты могут быть полезны. Свободный текст тела считайте защищённым доказательством, если только у вас нет парсера для конкретного формата и ясного списка разрешённых полей. Если интеграции нужно передать человеку понятную причину от удалённого сервиса, нормализуйте её в формулировку шлюза, например «Сервис отклонил запрошенный ресурс», вместо копирования исходного текста.

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

Подкрепите контракт тестами, ориентированными на сбои

Большинство команд проверяет, что корректный запрос возвращает полезные данные. Проверяйте также, что каждый неверный или прерванный запрос возвращает только сведения, которые должен видеть агент.

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

Используйте тестовый секрет, который трудно случайно преобразовать, и вызывайте сбой на каждом этапе. Матрица должна охватывать проверку данных до работы с учётными данными, заблокированное хранилище, отклонённое согласие, ответы 401 и 403 от сервиса, ограничение частоты, сбой DNS, ошибку проверки TLS, тайм-аут до отправки байтов, тайм-аут после отправки байтов, некорректный JSON от сервиса и исключение самого шлюза.

Для каждого случая проверяйте четыре условия:

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

Не ограничивайтесь проверками регулярными выражениями. Ищите точный тестовый секрет, его URL-кодированную и, если уместно, Base64-форму, а также распространённые начала и окончания. После обновления HTTP-, SSH-, телеметрической или системы отчётов о сбоях вручную проверяйте сохранённый ответ об ошибке. Библиотеки меняют формат исключений без вашего разрешения.

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

Вопросы и ответы

Что должно содержать сообщение об ошибке инструмента AI-агента?

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

Достаточно ли HTTP-статусов для инструментов агентов?

Обычно нет. HTTP-статус описывает результат на уровне протокола, а агенту нужно знать, следует ли повторить запрос, запросить подтверждение, исправить ввод или остановиться. Сохраните статус как безопасный контекст и добавьте код инструмента с операционным смыслом.

Можно ли включить в ошибку первые несколько символов токена для отладки?

Не передавайте его агенту. Шлюз может сохранить внутри отпечаток токена или идентификатор записи учётных данных и вернуть общий ответ AUTH_FAILED с идентификатором запроса. Даже начало токена остаётся материалом, связанным с учётными данными, и часто попадает в расшифровки, тикеты и историю командной строки.

Какие сбои агент может повторять автоматически?

Автоматический повтор уместен для явно временной ошибки, например тайм-аута до получения ответа, сброса соединения или ответа 503 от сервиса, если операция идемпотентна. Не повторяйте ошибки авторизации, неверного ввода, отклонённого подтверждения и неоднозначного результата записи, пока сначала не установите состояние операции на удалённой стороне.

Как инструменту обрабатывать тайм-аут во время записи?

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

Должны ли сообщения об ошибках оставаться неизменными между версиями инструмента?

Сохраняйте стабильность сообщений для пользователя и проверяйте их тестами. Со временем можно добавлять поля, но изменение смысла кодов или превращение коротких сообщений в диагностические дампы ломает поведение агентов и может раскрыть сведения, которые старые клиенты сохраняют без проверки.

Безопасно ли передавать агенту идентификатор запроса?

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

Может ли агент читать логи для диагностики сбоя инструмента?

Только если источник изначально предназначен для просмотра агентом, а его поля проверены. В обычных логах часто встречаются заголовки авторизации, подписанные URL, cookies, значения SQL, внутренние имена хостов и стеки вызовов. Отдельная очищенная запись активности безопаснее, чем фильтрация общего отладочного лога постфактум.

Какие сетевые сведения опасно возвращать агенту?

Никогда не передавайте агенту напрямую необработанные ошибки библиотек TCP, TLS, DNS или SSH. Преобразуйте их в ограниченный код, например NETWORK_UNREACHABLE, TLS_VALIDATION_FAILED или SSH_HOST_UNVERIFIED, а исходную ошибку оставляйте в защищённой диагностике.

Как инструменту агента сообщать об отклонённом подтверждении?

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

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov