Крючки

Хуки позволяют запускать пользовательские скрипты или внешние HTTP-запросы непосредственно до или после выполнения агентом кода или изменения файлов в удаленной песочнице. Используйте хуки для расширения цикла работы агента с помощью автоматизированных механизмов защиты и фоновых рабочих процессов, таких как:

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

Python

import json
from google import genai

client = genai.Client()

hooks_config = {
    "security-gate": {
        "pre_tool_execution": [
            {
                "matcher": "code_execution",
                "hooks": [
                    {
                        "type": "command",
                        "command": "python3 /.agents/hooks-scripts/gate.py",
                        "timeout": 10,
                    }
                ],
            }
        ]
    }
}

gate_script = """#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
    print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
    print(json.dumps({"decision": "allow"}))
"""

interaction = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Run `rm -rf /tmp/forbidden` using code_execution.",
    tools=[{"type": "code_execution"}],
    environment={
        "type": "remote",
        "sources": [
            {
                "type": "inline",
                "target": ".agents/hooks.json",
                "content": json.dumps(hooks_config, indent=2),
            },
            {
                "type": "inline",
                "target": ".agents/hooks-scripts/gate.py",
                "content": gate_script,
            },
        ],
    },
)
print(interaction.output_text)

JavaScript

import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});

const hooksConfig = {
    "security-gate": {
        pre_tool_execution: [
            {
                matcher: "code_execution",
                hooks: [
                    {
                        type: "command",
                        command: "python3 /.agents/hooks-scripts/gate.py",
                        timeout: 10,
                    },
                ],
            },
        ],
    },
};

const gateScript = `#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
    print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
    print(json.dumps({"decision": "allow"}))
`;

const interaction = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Run `rm -rf /tmp/forbidden` using code_execution.",
    tools: [{ type: "code_execution" }],
    environment: {
        type: "remote",
        sources: [
            {
                type: "inline",
                target: ".agents/hooks.json",
                content: JSON.stringify(hooksConfig, null, 2),
            },
            {
                type: "inline",
                target: ".agents/hooks-scripts/gate.py",
                content: gateScript,
            },
        ],
    },
});
console.log(interaction.output_text);

ОТДЫХ

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "agent": "antigravity-preview-05-2026",
      "input": [{"type": "text", "text": "Run `rm -rf /tmp/forbidden` using code_execution."}],
      "tools": [{"type": "code_execution"}],
      "environment": {
          "type": "remote",
          "sources": [
              {
                  "type": "inline",
                  "target": ".agents/hooks.json",
                  "content": "{\"security-gate\": {\"pre_tool_execution\": [{\"matcher\": \"code_execution\", \"hooks\": [{\"type\": \"command\", \"command\": \"python3 /.agents/hooks-scripts/gate.py\", \"timeout\": 10}]}]}}"
              },
              {
                  "type": "inline",
                  "target": ".agents/hooks-scripts/gate.py",
                  "content": "#!/usr/bin/env python3\nimport sys, json\ndata = json.load(sys.stdin)\ncmd = str(data.get(\"tool_call\", {}).get(\"args\", {}))\nif \"rm -rf\" in cmd:\n    print(json.dumps({\"decision\": \"deny\", \"reason\": \"Destructive command blocked by security gate.\"}))\nelse:\n    print(json.dumps({\"decision\": \"allow\"}))\n"
              }
          ]
      }
  }'

Поддерживаемые события жизненного цикла

В песочнице поддерживаются 2 события:

Событие Когда он выстрелит Что это делает
pre_tool_execution Непосредственно перед запуском инструмента Инструмент может быть одобрен ( allow ) или заблокирован ( deny ) до его выполнения. В случае блокировки модель видит причину отказа и вносит необходимые изменения.
post_tool_execution Сразу после завершения работы инструмента Выполняет последующие задачи, такие как форматирование кода, запуск модульных тестов или запись телеметрии. Не может блокировать или отменять выполненные действия.

pre_tool_execution

Этот скрипт срабатывает непосредственно перед выполнением инструмента. Ваш скрипт считывает сведения о вызове инструмента из stdin и выводит решение в формате JSON ( allow или deny ) в stdout .

Входные данные ( stdin ):

{
  "tool_call": {
    "name": "code_execution",
    "args": {
      "code": "rm -rf /tmp/forbidden",
      "language": "bash"
    }
  },
  "environment_id": "env_xyz789"
}

Вывод ответа ( stdout ):

Для утверждения инструмента позвоните по телефону:

{
  "decision": "allow"
}

Чтобы заблокировать вызов инструмента и вернуть обратную связь модели:

{
  "decision": "deny",
  "reason": "Destructive command blocked by security gate."
}

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

Если ваш скрипт выводит нераспознанный JSON, обычный текст или что-либо, кроме {"decision": "deny"} , среда выполнения рассматривает ответ как одобрение ( allow ).

post_tool_execution

Запускается сразу после завершения работы инструмента. Ваш скрипт считывает сведения о выполнении и любые сообщения об ошибках из stdin .

Входные данные ( stdin ):

{
  "tool_call": {
    "name": "code_execution",
    "args": {
      "code": "python3 /workspace/app.py",
      "language": "bash"
    }
  },
  "environment_id": "env_xyz789"
}

Если команда оболочки выводит ошибки в стандартный поток ошибок ( stderr ) или операция с файловой системой завершается с ошибкой, в полезную нагрузку включается поле "error" содержащее текст ошибки. Если команда выполняется успешно без ошибок, поле "error" полностью опускается.

Вывод ответа ( stdout ):

{}

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

Обнаружение конфигурации

Среда выполнения автоматически обнаруживает определения хуков в файлах .agents/hooks.json или /.agents/hooks.json внутри среды песочницы. Вы можете предоставить hooks.json вместе со своими пользовательскими скриптами, используя любой поддерживаемый источник среды :

  • Смонтированный репозиторий : репозиторий Git, содержащий .agents/hooks.json и AGENTS.md .
  • Cloud Storage ( gcs ) : Корзина GCS, содержащая hooks.json , скопированный в среду.
  • Встроенные источники : необработанная JSON-строка и содержимое скрипта, передаваемые в environment.sources при вызове client.interactions.create .

схема hooks.json

В файле hooks.json определения событий ( pre_tool_execution или post_tool_execution ) группируются под пользовательскими именами. Вы можете включать или отключать каждую группу независимо:

{
  "security-gate": {
    "enabled": true,
    "pre_tool_execution": [
      {
        "matcher": "code_execution",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /.agents/hooks-scripts/gate.py",
            "timeout": 10
          }
        ]
      }
    ]
  },
  "auto-format": {
    "post_tool_execution": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /.agents/hooks-scripts/auto_lint.py",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

Синтаксис и правила сопоставления

Каждая группа правил в hooks.json определяет, когда и как срабатывают обработчики, используя свойства matcher и hooks :

Поле Тип Описание
enabled boolean Необязательный параметр. Установите значение false , чтобы отключить группу (по умолчанию true ).
matcher string Сопоставление шаблонов регулярных выражений с целевыми именами инструментов внутри контейнера.
hooks array Упорядоченный список определений обработчиков ( command или http ). Обработчики выполняются последовательно в порядке их объявления.

Как работает оценка регулярных выражений

Когда агент вызывает инструмент внутри песочницы, среда выполнения сравнивает имя контейнера инструмента с вашим шаблоном matcher , используя стандартные регулярные выражения RE2. Если регулярное выражение совпадает с именем инструмента, все обработчики в массиве hooks выполняются последовательно. Если несколько групп правил соответствуют одному и тому же инструменту, выполняются все соответствующие массивы обработчиков.

Вы можете указать в качестве цели любой встроенный инструмент контейнера: выполнение кода ( code_execution ) или операции с файловой системой ( read_file , write_file , list_files и delete_file ).

Общие выражения сопоставления

  • "code_execution" : Точное совпадение строк для команд оболочки и выполнения скриптов.
  • "write_file" : Точное совпадение для создания файлов в файловой системе и записи на диск.
  • "read_file|write_file" : Разделение каналов соответствует нескольким конкретным именам инструментов в одном правиле.
  • ".*_file" : Подстановочный знак регулярного выражения, соответствующий любому инструменту, заканчивающемуся на _file (например, read_file , write_file или delete_file ). Стандартные регулярные выражения RE2 требуют .* ; простые шаблоны оболочки, такие как *_file являются недопустимым синтаксисом регулярных выражений и не будут найдены.
  • ".*" или "*" или "" : Обобщенный шаблон, перехватывающий каждый вызов инструмента внутри контейнера.

Типы обработчиков

Перехватчики команд

Перехватчики команд выполняют команду оболочки или скрипт внутри песочницы. Скрипт получает JSON-сообщение о событии через stdin и выводит JSON-сообщение о принятом решении через stdout .

Поле Тип Описание
type string Должно быть "command" .
command string Командная строка для запуска внутри песочницы (например, python3 /.agents/hooks-scripts/gate.py ).
timeout integer Время ожидания в секундах. По умолчанию: 30 .

HTTP-хуки

HTTP-хуки отправляют JSON-событие в виде POST-запроса на внешний HTTPS-адрес непосредственно из изолированной сети. Целевой сервер возвращает свое решение в теле HTTP-ответа, используя тот же самый JSON-формат ( {"decision": "allow"} или {"decision": "deny", "reason": "..."} ).

Поле Тип Описание
type string Должно быть "http" .
url string Внешний HTTPS-адрес для отправки данных события методом POST.
headers object Необязательные пары ключ-значение для неконфиденциальных пользовательских заголовков (например, {"X-Event-Source": "agent-sandbox"} ). Для учетных данных аутентификации используйте сетевой прокси.
timeout integer Время ожидания в секундах. По умолчанию: 30 .

Прокси для исходящего трафика и преобразование токенов

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

  • Сетевые разрешения: Целевые конечные точки должны быть явно разрешены в network.allowlist вашей среды. Трафик обратной связи ( 127.0.0.1 или localhost ) блокируется прокси-сервером; всегда используйте в качестве целевых внешних конечных точек указанные в списке разрешенные адреса.
  • Преобразование токенов: Вам не нужно хранить ключи API или секретные токены bearer в файле .agents/hooks.json или монтировать их в контейнер. Вместо этого настройте правила преобразования токенов в конфигурации вашей сети ( network.allowlist.transform ). Исходящий прокси автоматически перехватывает исходящий трафик HTTP-хуков и внедряет ваши реальные заголовки аутентификации в сеть перед выходом из песочницы.

Как среда выполнения обрабатывает решения и ошибки

  • Синхронное ожидание: агент делает паузу и ждет завершения обработки ваших запросов, прежде чем продолжить.
  • Блокировка выполнения инструмента: если ваш пре-инструмент возвращает {"decision": "deny", "reason": "<your reason>"} , среда выполнения немедленно отменяет вызов инструмента. Модель видит причину вашего отказа в истории диалога и адаптируется, выбирая безопасную альтернативу или объясняя блокировку пользователю.
  • Обработка сбоев скриптов, HTTP-ошибок и тайм-аутов: если скрипт команды завершается с ошибкой (ненулевой код завершения), HTTP-хук возвращает код состояния, отличный от 2xx (например, ошибка сервера 4xx или 5xx), или операция завершается по тайм-ауту или возвращает неопознанный JSON, среда выполнения рассматривает это как подтверждение ( allow ). Выполнение инструмента продолжается в обычном режиме, поэтому неработающий скрипт или недоступный сервер телеметрии никогда не приведут к взаимоблокировке вашего приложения.

Типичные сценарии использования

Многоэтапное восстановление данных для обеспечения конфиденциальности и соответствия нормативным требованиям.

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

Python

import json
from google import genai

client = genai.Client()

hooks_config = {
    "privacy-gate": {
        "pre_tool_execution": [
            {
                "matcher": "read_file",
                "hooks": [
                    {
                        "type": "command",
                        "command": "python3 /.agents/hooks-scripts/check_privacy.py",
                        "timeout": 5,
                    }
                ],
            }
        ]
    }
}

check_privacy_script = """#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
path = str(data.get("tool_call", {}).get("args", {}).get("path", ""))

if "/private/" in path:
    resp = {
        "decision": "deny",
        "reason": "Access to confidential `/private/` records is blocked by PII compliance policy. Query approved `/public/` summary tables instead."
    }
else:
    resp = {"decision": "allow"}

print(json.dumps(resp))
"""

# Step 1: Agent attempts to read confidential PII records and is intercepted
int_1 = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Use your filesystem tool to read `/workspace/private/employees.json` and summarize the employee details.",
    environment={
        "type": "remote",
        "sources": [
            {
                "type": "inline",
                "target": ".agents/hooks.json",
                "content": json.dumps(hooks_config, indent=2),
            },
            {
                "type": "inline",
                "target": ".agents/hooks-scripts/check_privacy.py",
                "content": check_privacy_script,
            },
            {
                "type": "inline",
                "target": "workspace/private/employees.json",
                "content": '{"employees": [{"id": 1, "salary": 150000, "ssn": "000-00-0000"}]}',
            },
            {
                "type": "inline",
                "target": "workspace/public/summary.json",
                "content": '{"department": "Engineering", "team_size": 42, "status": "active"}',
            },
        ],
    },
)
print(int_1.output_text)

# Step 2: Continue in the same environment using previous_interaction_id; agent recovers with public tables
int_2 = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Understood. Please read the approved `/workspace/public/summary.json` file instead and provide the summary.",
    environment=int_1.environment_id,
    previous_interaction_id=int_1.id,
)
print(int_2.output_text)

JavaScript

import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});

const hooksConfig = {
    "privacy-gate": {
        pre_tool_execution: [
            {
                matcher: "read_file",
                hooks: [
                    {
                        type: "command",
                        command: "python3 /.agents/hooks-scripts/check_privacy.py",
                        timeout: 5,
                    },
                ],
            },
        ],
    },
};

const checkPrivacyScript = `#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
path = str(data.get("tool_call", {}).get("args", {}).get("path", ""))

if "/private/" in path:
    resp = {
        "decision": "deny",
        "reason": "Access to confidential \`/private/\` records is blocked by PII compliance policy. Query approved \`/public/\` summary tables instead."
    }
else:
    resp = {"decision": "allow"}

print(json.dumps(resp))
`;

const int1 = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Use your filesystem tool to read `/workspace/private/employees.json` and summarize the employee details.",
    environment: {
        type: "remote",
        sources: [
            {
                type: "inline",
                "target": ".agents/hooks.json",
                content: JSON.stringify(hooksConfig, null, 2),
            },
            {
                type: "inline",
                "target": ".agents/hooks-scripts/check_privacy.py",
                content: checkPrivacyScript,
            },
            {
                type: "inline",
                "target": "workspace/private/employees.json",
                content: '{"employees": [{"id": 1, "salary": 150000, "ssn": "000-00-0000"}]}',
            },
            {
                type: "inline",
                "target": "workspace/public/summary.json",
                content: '{"department": "Engineering", "team_size": 42, "status": "active"}',
            },
        ],
    },
});
console.log(int1.output_text);

const int2 = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Understood. Please read the approved `/workspace/public/summary.json` file instead and provide the summary.",
    environment: int1.environment_id,
    previous_interaction_id: int1.id,
});
console.log(int2.output_text);

ОТДЫХ

# Step 1: Attempt to access restricted PII directory (blocked by hook)
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "agent": "antigravity-preview-05-2026",
      "input": [{"type": "text", "text": "Use your filesystem tool to read /workspace/private/employees.json and summarize the employee details."}],
      "environment": {
          "type": "remote",
          "sources": [
              {
                  "type": "inline",
                  "target": ".agents/hooks.json",
                  "content": "{\"privacy-gate\": {\"pre_tool_execution\": [{\"matcher\": \"read_file\", \"hooks\": [{\"type\": \"command\", \"command\": \"python3 /.agents/hooks-scripts/check_privacy.py\", \"timeout\": 5}]}]}}"
              },
              {
                  "type": "inline",
                  "target": ".agents/hooks-scripts/check_privacy.py",
                  "content": "#!/usr/bin/env python3\nimport sys, json\ndata = json.load(sys.stdin)\npath = str(data.get(\"tool_call\", {}).get(\"args\", {}).get(\"path\", \"\"))\nif \"/private/\" in path:\n    resp = {\"decision\": \"deny\", \"reason\": \"Access to confidential `/private/` records is blocked by PII compliance policy. Query approved `/public/` summary tables instead.\"}\nelse:\n    resp = {\"decision\": \"allow\"}\nprint(json.dumps(resp))\n"
              },
              {
                  "type": "inline",
                  "target": "workspace/private/employees.json",
                  "content": "{\"employees\": [{\"id\": 1, \"salary\": 150000, \"ssn\": \"000-00-0000\"}]}"
              },
              {
                  "type": "inline",
                  "target": "workspace/public/summary.json",
                  "content": "{\"department\": \"Engineering\", \"team_size\": 42, \"status\": \"active\"}"
              }
          ]
      }
  }'

# Step 2: Continue in the same environment using $ENV_ID and $INTERACTION_ID from the previous response
# curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
#   -H "Content-Type: application/json" \
#   -H "x-goog-api-key: $GEMINI_API_KEY" \
#   -d '{
#       "agent": "antigravity-preview-05-2026",
#       "input": [{"type": "text", "text": "Understood. Please read the approved /workspace/public/summary.json file instead and provide the summary."}],
#       "environment": "'"$ENV_ID"'",
#       "previous_interaction_id": "'"$INTERACTION_ID"'"
#   }'

Внешнее ведение журналов аудита и телеметрия

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

  • Сопоставление нескольких инструментов: Поскольку сопоставители используют стандартные регулярные выражения, вы можете объединить несколько инструментов в одном правиле, используя конвейеры ( read_file|write_file ) или подстановочные знаки ( .*_file ).
  • Не скрывайте секреты в конфигурации: определите токены аутентификации в сетевой конфигурации вашей среды ( network.allowlist.transform ). Исходящий прокси автоматически внедряет ваши реальные токены Bearer в исходящие запросы.

Python

import json
from google import genai

client = genai.Client()

# Define hook without secrets; the egress proxy injects headers dynamically
hooks_config = {
    "audit-logging": {
        "post_tool_execution": [
            {
                "matcher": "read_file|write_file",
                "hooks": [
                    {
                        "type": "http",
                        "url": "https://telemetry.example.com/api/v1/agent-events",
                        "timeout": 10,
                    }
                ],
            }
        ]
    }
}

interaction = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Use your filesystem tool to create `/workspace/audit.log` containing 'event 1', then immediately read it back using your filesystem read tool.",
    environment={
        "type": "remote",
        "sources": [
            {
                "type": "inline",
                "target": ".agents/hooks.json",
                "content": json.dumps(hooks_config, indent=2),
            }
        ],
        "network": {
            "allowlist": [
                {
                    "domain": "telemetry.example.com",
                    "transform": {
                        "Authorization": "Bearer telemetry_secret_token_123",
                    },
                },
                {"domain": "*"},
            ]
        },
    },
)
print(interaction.output_text)

JavaScript

import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});

// Define hook without secrets; the egress proxy injects headers dynamically
const hooksConfig = {
    "audit-logging": {
        post_tool_execution: [
            {
                matcher: "read_file|write_file",
                hooks: [
                    {
                        type: "http",
                        url: "https://telemetry.example.com/api/v1/agent-events",
                        timeout: 10,
                    },
                ],
            },
        ],
    },
};

const interaction = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Use your filesystem tool to create `/workspace/audit.log` containing 'event 1', then immediately read it back using your filesystem read tool.",
    environment: {
        type: "remote",
        sources: [
            {
                type: "inline",
                target: ".agents/hooks.json",
                content: JSON.stringify(hooksConfig, null, 2),
            },
        ],
        network: {
            allowlist: [
                {
                    domain: "telemetry.example.com",
                    transform: {
                        Authorization: "Bearer telemetry_secret_token_123",
                    },
                },
                { domain: "*" },
            ],
        },
    },
});
console.log(interaction.output_text);

ОТДЫХ

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "agent": "antigravity-preview-05-2026",
      "input": [{"type": "text", "text": "Use your filesystem tool to create /workspace/audit.log containing event 1, then immediately read it back using your filesystem read tool."}],
      "environment": {
          "type": "remote",
          "sources": [
              {
                  "type": "inline",
                  "target": ".agents/hooks.json",
                  "content": "{\"audit-logging\": {\"post_tool_execution\": [{\"matcher\": \"read_file|write_file\", \"hooks\": [{\"type\": \"http\", \"url\": \"https://telemetry.example.com/api/v1/agent-events\", \"timeout\": 10}]}]}}"
              }
          ],
          "network": {
              "allowlist": [
                  {
                      "domain": "telemetry.example.com",
                      "transform": {
                          "Authorization": "Bearer telemetry_secret_token_123"
                      }
                  },
                  {"domain": "*"}
              ]
          }
      }
  }'

Ограничения

  • Область действия инструментов песочницы: Хуки перехватывают встроенные инструменты внутри песочницы: выполнение кода ( code_execution ) и операции с файловой системой ( read_file , write_file , list_files и delete_file ). Они не срабатывают при вызове пользовательских функций ( function ) или внешних инструментов протокола контекста модели ( mcp_server ), обрабатываемых вне контейнера.
  • Списки разрешенных адресов сети: HTTP-хуки работают внутри контейнерной сети. Необходимо явно разрешить целевые URL-адреса в network.allowlist вашей среды. Адреса обратной связи ( localhost , 127.0.0.1 ) блокируются прокси-сервером.
  • Автоматическое подтверждение ошибок: если скрипт-перехватчик завершается с ошибкой (ненулевой код завершения), истекает время ожидания или происходит сбой, среда выполнения регистрирует ошибку и позволяет продолжить вызов инструмента. Это гарантирует, что неисправные скрипты линтера или зависшие процессы никогда не приведут к взаимоблокировке ваших приложений.
  • Защита конфигурации в песочнице: поскольку хуки выполняются внутри песочницы контейнера, агенты с правами на запись в файловую систему или выполнение шелл-кода могут изменять локальные файлы .agents/hooks.json или скрипты в рабочих областях с правами на запись. Используйте хуки контейнера в качестве автоматизированных руководств по политикам и оперативных средств защиты; если требуется строгая защита от несанкционированного выполнения моделей, монтируйте источники конфигурации из репозиториев только для чтения.

Что дальше?

,

Хуки позволяют запускать пользовательские скрипты или внешние HTTP-запросы непосредственно до или после выполнения агентом кода или изменения файлов в удаленной песочнице. Используйте хуки для расширения цикла работы агента с помощью автоматизированных механизмов защиты и фоновых рабочих процессов, таких как:

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

Python

import json
from google import genai

client = genai.Client()

hooks_config = {
    "security-gate": {
        "pre_tool_execution": [
            {
                "matcher": "code_execution",
                "hooks": [
                    {
                        "type": "command",
                        "command": "python3 /.agents/hooks-scripts/gate.py",
                        "timeout": 10,
                    }
                ],
            }
        ]
    }
}

gate_script = """#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
    print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
    print(json.dumps({"decision": "allow"}))
"""

interaction = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Run `rm -rf /tmp/forbidden` using code_execution.",
    tools=[{"type": "code_execution"}],
    environment={
        "type": "remote",
        "sources": [
            {
                "type": "inline",
                "target": ".agents/hooks.json",
                "content": json.dumps(hooks_config, indent=2),
            },
            {
                "type": "inline",
                "target": ".agents/hooks-scripts/gate.py",
                "content": gate_script,
            },
        ],
    },
)
print(interaction.output_text)

JavaScript

import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});

const hooksConfig = {
    "security-gate": {
        pre_tool_execution: [
            {
                matcher: "code_execution",
                hooks: [
                    {
                        type: "command",
                        command: "python3 /.agents/hooks-scripts/gate.py",
                        timeout: 10,
                    },
                ],
            },
        ],
    },
};

const gateScript = `#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
    print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
    print(json.dumps({"decision": "allow"}))
`;

const interaction = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Run `rm -rf /tmp/forbidden` using code_execution.",
    tools: [{ type: "code_execution" }],
    environment: {
        type: "remote",
        sources: [
            {
                type: "inline",
                target: ".agents/hooks.json",
                content: JSON.stringify(hooksConfig, null, 2),
            },
            {
                type: "inline",
                target: ".agents/hooks-scripts/gate.py",
                content: gateScript,
            },
        ],
    },
});
console.log(interaction.output_text);

ОТДЫХ

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "agent": "antigravity-preview-05-2026",
      "input": [{"type": "text", "text": "Run `rm -rf /tmp/forbidden` using code_execution."}],
      "tools": [{"type": "code_execution"}],
      "environment": {
          "type": "remote",
          "sources": [
              {
                  "type": "inline",
                  "target": ".agents/hooks.json",
                  "content": "{\"security-gate\": {\"pre_tool_execution\": [{\"matcher\": \"code_execution\", \"hooks\": [{\"type\": \"command\", \"command\": \"python3 /.agents/hooks-scripts/gate.py\", \"timeout\": 10}]}]}}"
              },
              {
                  "type": "inline",
                  "target": ".agents/hooks-scripts/gate.py",
                  "content": "#!/usr/bin/env python3\nimport sys, json\ndata = json.load(sys.stdin)\ncmd = str(data.get(\"tool_call\", {}).get(\"args\", {}))\nif \"rm -rf\" in cmd:\n    print(json.dumps({\"decision\": \"deny\", \"reason\": \"Destructive command blocked by security gate.\"}))\nelse:\n    print(json.dumps({\"decision\": \"allow\"}))\n"
              }
          ]
      }
  }'

Поддерживаемые события жизненного цикла

В песочнице поддерживаются 2 события:

Событие Когда он выстрелит Что это делает
pre_tool_execution Непосредственно перед запуском инструмента Инструмент может быть одобрен ( allow ) или заблокирован ( deny ) до его выполнения. В случае блокировки модель видит причину отказа и вносит необходимые изменения.
post_tool_execution Сразу после завершения работы инструмента Выполняет последующие задачи, такие как форматирование кода, запуск модульных тестов или запись телеметрии. Не может блокировать или отменять выполненные действия.

pre_tool_execution

Этот скрипт срабатывает непосредственно перед выполнением инструмента. Ваш скрипт считывает сведения о вызове инструмента из stdin и выводит решение в формате JSON ( allow или deny ) в stdout .

Входные данные ( stdin ):

{
  "tool_call": {
    "name": "code_execution",
    "args": {
      "code": "rm -rf /tmp/forbidden",
      "language": "bash"
    }
  },
  "environment_id": "env_xyz789"
}

Вывод ответа ( stdout ):

Для утверждения инструмента позвоните по телефону:

{
  "decision": "allow"
}

Чтобы заблокировать вызов инструмента и вернуть обратную связь модели:

{
  "decision": "deny",
  "reason": "Destructive command blocked by security gate."
}

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

Если ваш скрипт выводит нераспознанный JSON, обычный текст или что-либо, кроме {"decision": "deny"} , среда выполнения рассматривает ответ как одобрение ( allow ).

post_tool_execution

Запускается сразу после завершения работы инструмента. Ваш скрипт считывает сведения о выполнении и любые сообщения об ошибках из stdin .

Входные данные ( stdin ):

{
  "tool_call": {
    "name": "code_execution",
    "args": {
      "code": "python3 /workspace/app.py",
      "language": "bash"
    }
  },
  "environment_id": "env_xyz789"
}

Если команда оболочки выводит ошибки в стандартный поток ошибок ( stderr ) или операция с файловой системой завершается с ошибкой, в полезную нагрузку включается поле "error" содержащее текст ошибки. Если команда выполняется успешно без ошибок, поле "error" полностью опускается.

Вывод ответа ( stdout ):

{}

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

Обнаружение конфигурации

Среда выполнения автоматически обнаруживает определения хуков в файлах .agents/hooks.json или /.agents/hooks.json внутри среды песочницы. Вы можете предоставить hooks.json вместе со своими пользовательскими скриптами, используя любой поддерживаемый источник среды :

  • Смонтированный репозиторий : репозиторий Git, содержащий .agents/hooks.json и AGENTS.md .
  • Cloud Storage ( gcs ) : Корзина GCS, содержащая hooks.json , скопированный в среду.
  • Встроенные источники : необработанная JSON-строка и содержимое скрипта, передаваемые в environment.sources при вызове client.interactions.create .

схема hooks.json

В файле hooks.json определения событий ( pre_tool_execution или post_tool_execution ) группируются под пользовательскими именами. Вы можете включать или отключать каждую группу независимо:

{
  "security-gate": {
    "enabled": true,
    "pre_tool_execution": [
      {
        "matcher": "code_execution",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /.agents/hooks-scripts/gate.py",
            "timeout": 10
          }
        ]
      }
    ]
  },
  "auto-format": {
    "post_tool_execution": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /.agents/hooks-scripts/auto_lint.py",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

Синтаксис и правила сопоставления

Каждая группа правил в hooks.json определяет, когда и как срабатывают обработчики, используя свойства matcher и hooks :

Поле Тип Описание
enabled boolean Необязательный параметр. Установите значение false , чтобы отключить группу (по умолчанию true ).
matcher string Сопоставление шаблонов регулярных выражений с целевыми именами инструментов внутри контейнера.
hooks array Упорядоченный список определений обработчиков ( command или http ). Обработчики выполняются последовательно в порядке их объявления.

Как работает оценка регулярных выражений

Когда агент вызывает инструмент внутри песочницы, среда выполнения сравнивает имя контейнера инструмента с вашим шаблоном matcher , используя стандартные регулярные выражения RE2. Если регулярное выражение совпадает с именем инструмента, все обработчики в массиве hooks выполняются последовательно. Если несколько групп правил соответствуют одному и тому же инструменту, выполняются все соответствующие массивы обработчиков.

Вы можете указать в качестве цели любой встроенный инструмент контейнера: выполнение кода ( code_execution ) или операции с файловой системой ( read_file , write_file , list_files и delete_file ).

Общие выражения сопоставления

  • "code_execution" : Точное совпадение строк для команд оболочки и выполнения скриптов.
  • "write_file" : Точное совпадение для создания файлов в файловой системе и записи на диск.
  • "read_file|write_file" : Разделение каналов соответствует нескольким конкретным именам инструментов в одном правиле.
  • ".*_file" : Подстановочный знак регулярного выражения, соответствующий любому инструменту, заканчивающемуся на _file (например, read_file , write_file или delete_file ). Стандартные регулярные выражения RE2 требуют .* ; простые шаблоны оболочки, такие как *_file являются недопустимым синтаксисом регулярных выражений и не будут найдены.
  • ".*" или "*" или "" : Обобщенный шаблон, перехватывающий каждый вызов инструмента внутри контейнера.

Типы обработчиков

Перехватчики команд

Перехватчики команд выполняют команду оболочки или скрипт внутри песочницы. Скрипт получает JSON-сообщение о событии через stdin и выводит JSON-сообщение о принятом решении через stdout .

Поле Тип Описание
type string Должно быть "command" .
command string Командная строка для запуска внутри песочницы (например, python3 /.agents/hooks-scripts/gate.py ).
timeout integer Время ожидания в секундах. По умолчанию: 30 .

HTTP-хуки

HTTP-хуки отправляют JSON-событие в виде POST-запроса на внешний HTTPS-адрес непосредственно из изолированной сети. Целевой сервер возвращает свое решение в теле HTTP-ответа, используя тот же самый JSON-формат ( {"decision": "allow"} или {"decision": "deny", "reason": "..."} ).

Поле Тип Описание
type string Должно быть "http" .
url string Внешний HTTPS-адрес для отправки данных события методом POST.
headers object Необязательные пары ключ-значение для неконфиденциальных пользовательских заголовков (например, {"X-Event-Source": "agent-sandbox"} ). Для учетных данных аутентификации используйте сетевой прокси.
timeout integer Время ожидания в секундах. По умолчанию: 30 .

Прокси для исходящего трафика и преобразование токенов

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

  • Сетевые разрешения: Целевые конечные точки должны быть явно разрешены в network.allowlist вашей среды. Трафик обратной связи ( 127.0.0.1 или localhost ) блокируется прокси-сервером; всегда используйте в качестве целевых внешних конечных точек указанные в списке разрешенные адреса.
  • Преобразование токенов: Вам не нужно хранить ключи API или секретные токены bearer в файле .agents/hooks.json или монтировать их в контейнер. Вместо этого настройте правила преобразования токенов в конфигурации вашей сети ( network.allowlist.transform ). Исходящий прокси автоматически перехватывает исходящий трафик HTTP-хуков и внедряет ваши реальные заголовки аутентификации в сеть перед выходом из песочницы.

Как среда выполнения обрабатывает решения и ошибки

  • Синхронное ожидание: агент делает паузу и ждет завершения обработки ваших запросов, прежде чем продолжить.
  • Блокировка выполнения инструмента: если ваш пре-инструмент возвращает {"decision": "deny", "reason": "<your reason>"} , среда выполнения немедленно отменяет вызов инструмента. Модель видит причину вашего отказа в истории диалога и адаптируется, выбирая безопасную альтернативу или объясняя блокировку пользователю.
  • Обработка сбоев скриптов, HTTP-ошибок и тайм-аутов: если скрипт команды завершается с ошибкой (ненулевой код завершения), HTTP-хук возвращает код состояния, отличный от 2xx (например, ошибка сервера 4xx или 5xx), или операция завершается по тайм-ауту или возвращает неопознанный JSON, среда выполнения рассматривает это как подтверждение ( allow ). Выполнение инструмента продолжается в обычном режиме, поэтому неработающий скрипт или недоступный сервер телеметрии никогда не приведут к взаимоблокировке вашего приложения.

Типичные сценарии использования

Многоэтапное восстановление данных для обеспечения конфиденциальности и соответствия нормативным требованиям.

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

Python

import json
from google import genai

client = genai.Client()

hooks_config = {
    "privacy-gate": {
        "pre_tool_execution": [
            {
                "matcher": "read_file",
                "hooks": [
                    {
                        "type": "command",
                        "command": "python3 /.agents/hooks-scripts/check_privacy.py",
                        "timeout": 5,
                    }
                ],
            }
        ]
    }
}

check_privacy_script = """#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
path = str(data.get("tool_call", {}).get("args", {}).get("path", ""))

if "/private/" in path:
    resp = {
        "decision": "deny",
        "reason": "Access to confidential `/private/` records is blocked by PII compliance policy. Query approved `/public/` summary tables instead."
    }
else:
    resp = {"decision": "allow"}

print(json.dumps(resp))
"""

# Step 1: Agent attempts to read confidential PII records and is intercepted
int_1 = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Use your filesystem tool to read `/workspace/private/employees.json` and summarize the employee details.",
    environment={
        "type": "remote",
        "sources": [
            {
                "type": "inline",
                "target": ".agents/hooks.json",
                "content": json.dumps(hooks_config, indent=2),
            },
            {
                "type": "inline",
                "target": ".agents/hooks-scripts/check_privacy.py",
                "content": check_privacy_script,
            },
            {
                "type": "inline",
                "target": "workspace/private/employees.json",
                "content": '{"employees": [{"id": 1, "salary": 150000, "ssn": "000-00-0000"}]}',
            },
            {
                "type": "inline",
                "target": "workspace/public/summary.json",
                "content": '{"department": "Engineering", "team_size": 42, "status": "active"}',
            },
        ],
    },
)
print(int_1.output_text)

# Step 2: Continue in the same environment using previous_interaction_id; agent recovers with public tables
int_2 = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Understood. Please read the approved `/workspace/public/summary.json` file instead and provide the summary.",
    environment=int_1.environment_id,
    previous_interaction_id=int_1.id,
)
print(int_2.output_text)

JavaScript

import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});

const hooksConfig = {
    "privacy-gate": {
        pre_tool_execution: [
            {
                matcher: "read_file",
                hooks: [
                    {
                        type: "command",
                        command: "python3 /.agents/hooks-scripts/check_privacy.py",
                        timeout: 5,
                    },
                ],
            },
        ],
    },
};

const checkPrivacyScript = `#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
path = str(data.get("tool_call", {}).get("args", {}).get("path", ""))

if "/private/" in path:
    resp = {
        "decision": "deny",
        "reason": "Access to confidential \`/private/\` records is blocked by PII compliance policy. Query approved \`/public/\` summary tables instead."
    }
else:
    resp = {"decision": "allow"}

print(json.dumps(resp))
`;

const int1 = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Use your filesystem tool to read `/workspace/private/employees.json` and summarize the employee details.",
    environment: {
        type: "remote",
        sources: [
            {
                type: "inline",
                "target": ".agents/hooks.json",
                content: JSON.stringify(hooksConfig, null, 2),
            },
            {
                type: "inline",
                "target": ".agents/hooks-scripts/check_privacy.py",
                content: checkPrivacyScript,
            },
            {
                type: "inline",
                "target": "workspace/private/employees.json",
                content: '{"employees": [{"id": 1, "salary": 150000, "ssn": "000-00-0000"}]}',
            },
            {
                type: "inline",
                "target": "workspace/public/summary.json",
                content: '{"department": "Engineering", "team_size": 42, "status": "active"}',
            },
        ],
    },
});
console.log(int1.output_text);

const int2 = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Understood. Please read the approved `/workspace/public/summary.json` file instead and provide the summary.",
    environment: int1.environment_id,
    previous_interaction_id: int1.id,
});
console.log(int2.output_text);

ОТДЫХ

# Step 1: Attempt to access restricted PII directory (blocked by hook)
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "agent": "antigravity-preview-05-2026",
      "input": [{"type": "text", "text": "Use your filesystem tool to read /workspace/private/employees.json and summarize the employee details."}],
      "environment": {
          "type": "remote",
          "sources": [
              {
                  "type": "inline",
                  "target": ".agents/hooks.json",
                  "content": "{\"privacy-gate\": {\"pre_tool_execution\": [{\"matcher\": \"read_file\", \"hooks\": [{\"type\": \"command\", \"command\": \"python3 /.agents/hooks-scripts/check_privacy.py\", \"timeout\": 5}]}]}}"
              },
              {
                  "type": "inline",
                  "target": ".agents/hooks-scripts/check_privacy.py",
                  "content": "#!/usr/bin/env python3\nimport sys, json\ndata = json.load(sys.stdin)\npath = str(data.get(\"tool_call\", {}).get(\"args\", {}).get(\"path\", \"\"))\nif \"/private/\" in path:\n    resp = {\"decision\": \"deny\", \"reason\": \"Access to confidential `/private/` records is blocked by PII compliance policy. Query approved `/public/` summary tables instead.\"}\nelse:\n    resp = {\"decision\": \"allow\"}\nprint(json.dumps(resp))\n"
              },
              {
                  "type": "inline",
                  "target": "workspace/private/employees.json",
                  "content": "{\"employees\": [{\"id\": 1, \"salary\": 150000, \"ssn\": \"000-00-0000\"}]}"
              },
              {
                  "type": "inline",
                  "target": "workspace/public/summary.json",
                  "content": "{\"department\": \"Engineering\", \"team_size\": 42, \"status\": \"active\"}"
              }
          ]
      }
  }'

# Step 2: Continue in the same environment using $ENV_ID and $INTERACTION_ID from the previous response
# curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
#   -H "Content-Type: application/json" \
#   -H "x-goog-api-key: $GEMINI_API_KEY" \
#   -d '{
#       "agent": "antigravity-preview-05-2026",
#       "input": [{"type": "text", "text": "Understood. Please read the approved /workspace/public/summary.json file instead and provide the summary."}],
#       "environment": "'"$ENV_ID"'",
#       "previous_interaction_id": "'"$INTERACTION_ID"'"
#   }'

Внешнее ведение журналов аудита и телеметрия

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

  • Сопоставление нескольких инструментов: Поскольку сопоставители используют стандартные регулярные выражения, вы можете объединить несколько инструментов в одном правиле, используя конвейеры ( read_file|write_file ) или подстановочные знаки ( .*_file ).
  • Не скрывайте секреты в конфигурации: определите токены аутентификации в сетевой конфигурации вашей среды ( network.allowlist.transform ). Исходящий прокси автоматически внедряет ваши реальные токены Bearer в исходящие запросы.

Python

import json
from google import genai

client = genai.Client()

# Define hook without secrets; the egress proxy injects headers dynamically
hooks_config = {
    "audit-logging": {
        "post_tool_execution": [
            {
                "matcher": "read_file|write_file",
                "hooks": [
                    {
                        "type": "http",
                        "url": "https://telemetry.example.com/api/v1/agent-events",
                        "timeout": 10,
                    }
                ],
            }
        ]
    }
}

interaction = client.interactions.create(
    agent="antigravity-preview-05-2026",
    input="Use your filesystem tool to create `/workspace/audit.log` containing 'event 1', then immediately read it back using your filesystem read tool.",
    environment={
        "type": "remote",
        "sources": [
            {
                "type": "inline",
                "target": ".agents/hooks.json",
                "content": json.dumps(hooks_config, indent=2),
            }
        ],
        "network": {
            "allowlist": [
                {
                    "domain": "telemetry.example.com",
                    "transform": {
                        "Authorization": "Bearer telemetry_secret_token_123",
                    },
                },
                {"domain": "*"},
            ]
        },
    },
)
print(interaction.output_text)

JavaScript

import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});

// Define hook without secrets; the egress proxy injects headers dynamically
const hooksConfig = {
    "audit-logging": {
        post_tool_execution: [
            {
                matcher: "read_file|write_file",
                hooks: [
                    {
                        type: "http",
                        url: "https://telemetry.example.com/api/v1/agent-events",
                        timeout: 10,
                    },
                ],
            },
        ],
    },
};

const interaction = await client.interactions.create({
    agent: "antigravity-preview-05-2026",
    input: "Use your filesystem tool to create `/workspace/audit.log` containing 'event 1', then immediately read it back using your filesystem read tool.",
    environment: {
        type: "remote",
        sources: [
            {
                type: "inline",
                target: ".agents/hooks.json",
                content: JSON.stringify(hooksConfig, null, 2),
            },
        ],
        network: {
            allowlist: [
                {
                    domain: "telemetry.example.com",
                    transform: {
                        Authorization: "Bearer telemetry_secret_token_123",
                    },
                },
                { domain: "*" },
            ],
        },
    },
});
console.log(interaction.output_text);

ОТДЫХ

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "agent": "antigravity-preview-05-2026",
      "input": [{"type": "text", "text": "Use your filesystem tool to create /workspace/audit.log containing event 1, then immediately read it back using your filesystem read tool."}],
      "environment": {
          "type": "remote",
          "sources": [
              {
                  "type": "inline",
                  "target": ".agents/hooks.json",
                  "content": "{\"audit-logging\": {\"post_tool_execution\": [{\"matcher\": \"read_file|write_file\", \"hooks\": [{\"type\": \"http\", \"url\": \"https://telemetry.example.com/api/v1/agent-events\", \"timeout\": 10}]}]}}"
              }
          ],
          "network": {
              "allowlist": [
                  {
                      "domain": "telemetry.example.com",
                      "transform": {
                          "Authorization": "Bearer telemetry_secret_token_123"
                      }
                  },
                  {"domain": "*"}
              ]
          }
      }
  }'

Ограничения

  • Область действия инструментов песочницы: Хуки перехватывают встроенные инструменты внутри песочницы: выполнение кода ( code_execution ) и операции с файловой системой ( read_file , write_file , list_files и delete_file ). Они не срабатывают при вызове пользовательских функций ( function ) или внешних инструментов протокола контекста модели ( mcp_server ), обрабатываемых вне контейнера.
  • Списки разрешенных адресов сети: HTTP-хуки работают внутри контейнерной сети. Необходимо явно разрешить целевые URL-адреса в network.allowlist вашей среды. Адреса обратной связи ( localhost , 127.0.0.1 ) блокируются прокси-сервером.
  • Автоматическое подтверждение ошибок: если скрипт-перехватчик завершается с ошибкой (ненулевой код завершения), истекает время ожидания или происходит сбой, среда выполнения регистрирует ошибку и позволяет продолжить вызов инструмента. Это гарантирует, что неисправные скрипты линтера или зависшие процессы никогда не приведут к взаимоблокировке ваших приложений.
  • Защита конфигурации в песочнице: поскольку хуки выполняются внутри песочницы контейнера, агенты с правами на запись в файловую систему или выполнение шелл-кода могут изменять локальные файлы .agents/hooks.json или скрипты в рабочих областях с правами на запись. Используйте хуки контейнера в качестве автоматизированных руководств по политикам и оперативных средств защиты; если требуется строгая защита от несанкционированного выполнения моделей, монтируйте источники конфигурации из репозиториев только для чтения.

Что дальше?