قلاب

هوک‌ها به شما امکان می‌دهند اسکریپت‌های سفارشی یا درخواست‌های HTTP خارجی را درست قبل یا بعد از اجرای کد یا تغییر فایل‌های داخل جعبه شنی ریموت توسط عامل اجرا کنید. از هوک‌ها برای گسترش حلقه عامل با گاردریل‌های خودکار و گردش‌های کاری پس‌زمینه، مانند موارد زیر، استفاده کنید:

  • اعمال حفاظ‌های ایمنی و دسترسی قبل از اجرای دستورات shell پرخطر یا خواندن فایل‌های محدود.
  • خودکارسازی تبدیلات خط لوله داده بلافاصله پس از ایجاد یا تغییر فایل‌ها توسط یک عامل.
  • ارسال تله‌متری حسابرسی سازمانی به سیستم‌های نظارتی خارجی پس از اجرای ابزار.

پایتون

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)

جاوا اسکریپت

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);

جاوا

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.AgentOption;
import com.google.genai.gaos.models.interactions.CreateAgentInteraction;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;

Client client = new Client();

CreateAgentInteraction params =
    CreateAgentInteraction.builder()
        .agent(AgentOption.of("antigravity-preview-05-2026"))
        .input(InteractionsInput.of("Build a simple REST API server in Node.js."))
        .build();

Interaction interaction =
    client.interactions.create(CreateInteractionRequestBody.of(params)).interaction().get();

System.out.println(interaction.outputText().orElse(""));

استراحت

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"
              }
          ]
      }
  }'

رویدادهای چرخه عمر پشتیبانی شده

هوک‌ها از دو رویداد درون سندباکس پشتیبانی می‌کنند:

رویداد وقتی آتش می‌گیرد چه کاری انجام می‌دهد؟
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"
}

اگر یک دستور shell خطاها را در خطای استاندارد ( stderr ) چاپ کند یا یک عملیات سیستم فایل با شکست مواجه شود، یک فیلد "error" حاوی متن خطا در payload گنجانده می‌شود. هنگامی که دستور بدون خطا موفق شود، فیلد "error" به طور کامل حذف می‌شود.

پاسخ خروجی ( stdout ):

{}

از آنجا که قلاب‌های post-tool صرفاً برای وظایف پس‌زمینه مانند قالب‌بندی کد یا ثبت وقایع اجرا می‌شوند، زمان اجرا هرگونه مقدار تصمیم‌گیری برگردانده شده در stdout را نادیده می‌گیرد.

کشف پیکربندی

زمان اجرا به طور خودکار تعاریف هوک را از .agents/hooks.json یا /.agents/hooks.json در محیط sandbox کشف می‌کند. می‌توانید hooks.json در کنار اسکریپت‌های سفارشی خود با استفاده از هر منبع محیطی پشتیبانی شده ارائه دهید:

  • مخزن mount : یک مخزن گیت حاوی .agents/hooks.json در کنار AGENTS.md .
  • فضای ذخیره‌سازی ابری ( gcs ) : یک سطل GCS حاوی hooks.json که در محیط کپی شده است.
  • منابع درون‌خطی : رشته‌های خام JSON و محتوای اسکریپت که هنگام فراخوانی client.interactions.create در environment.sources ارسال می‌شوند.

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 زمان و نحوه‌ی اجرای handlerها را با استفاده از ویژگی‌های matcher و hooks تعریف می‌کند:

میدان نوع توضیحات
enabled boolean اختیاری. برای غیرفعال کردن گروه، روی false تنظیم کنید (به طور پیش‌فرض true ).
matcher string الگوی عبارت منظم که با نام ابزارهای هدف درون کانتینر مطابقت دارد.
hooks array فهرست مرتبی از تعاریف کنترل‌کننده‌ها ( command یا http ). کنترل‌کننده‌ها به ترتیب اعلان و به ترتیب اجرا می‌شوند.

نحوه عملکرد ارزیابی regex

وقتی عامل ابزاری را درون جعبه شنی فراخوانی می‌کند، زمان اجرا نام کانتینر ابزار را با استفاده از عبارات منظم استاندارد RE2 در برابر الگوی matcher شما ارزیابی می‌کند. اگر regex با نام ابزار مطابقت داشته باشد، تمام کنترل‌کننده‌های موجود در آرایه hooks به ترتیب اجرا می‌شوند. اگر چندین گروه قانون با یک ابزار مطابقت داشته باشند، تمام آرایه‌های کنترل‌کننده مربوطه اجرا می‌شوند.

شما می‌توانید هر نام ابزار کانتینر داخلی را هدف قرار دهید: اجرای کد ( code_execution ) یا عملیات سیستم فایل ( read_file ، write_file ، list_files و delete_file ).

عبارات تطبیقی ​​رایج

  • "code_execution" : تطابق دقیق رشته برای دستورات پوسته و اجرای اسکریپت‌ها.
  • "write_file" : تطابق دقیق برای ایجاد فایل سیستم فایل و نوشتن روی دیسک.
  • "read_file|write_file" : جداسازی لوله‌ها با چندین نام ابزار خاص در یک قانون واحد مطابقت دارد.
  • ".*_file" : علامت اختصاری Regex که با هر ابزاری که به _file ختم می‌شود (مانند read_file ، write_file یا delete_file ) مطابقت دارد. عبارات منظم استاندارد RE2 به .* نیاز دارند؛ globهای ساده shell مانند *_file سینتکس regex نامعتبر هستند و مطابقت نخواهند داشت.
  • ".*" یا "*" یا "" : الگویی فراگیر که تک تک فراخوانی‌های ابزار درون کانتینر را رهگیری می‌کند.

انواع هندلر

قلاب‌های فرمان

قلاب‌های فرمان، یک دستور shell یا اسکریپت را درون sandbox اجرا می‌کنند. اسکریپت، رویداد JSON را در stdin دریافت می‌کند و تصمیم خود را به صورت JSON در stdout خروجی می‌دهد.

میدان نوع توضیحات
type string باید "command" باشد.
command string خط فرمان برای اجرا در داخل جعبه شنی (برای مثال، python3 /.agents/hooks-scripts/gate.py ).
timeout integer زمان انقضا بر حسب ثانیه. پیش‌فرض: 30 .

قلاب‌های HTTP

قلاب‌های HTTP رویداد JSON را به عنوان یک درخواست POST مستقیماً از داخل شبکه sandbox به یک URL HTTPS خارجی ارسال می‌کنند. سرور هدف تصمیم خود را در بدنه پاسخ HTTP با استفاده از دقیقاً همان قالب JSON ( {"decision": "allow"} یا {"decision": "deny", "reason": "..."} ) برمی‌گرداند.

میدان نوع توضیحات
type string باید "http" باشد.
url string نقطه پایانی HTTPS خارجی برای ارسال بار رویداد به آن.
headers object جفت‌های کلید-مقدار اختیاری برای هدرهای سفارشی غیر حساس (مانند {"X-Event-Source": "agent-sandbox"} ). برای اعتبارنامه‌های احراز هویت، به جای آن از پروکسی شبکه استفاده کنید.
timeout integer زمان انقضا بر حسب ثانیه. پیش‌فرض: 30 .

پروکسی خروجی و تبدیل توکن

از آنجا که قلاب‌های HTTP مستقیماً از داخل فضای نام شبکه sandbox اجرا می‌شوند، درخواست‌های خروجی از طریق پروکسی خروجی شفاف عبور می‌کنند. این معماری دو مزیت امنیتی حیاتی به شما می‌دهد:

  • فهرست مجاز شبکه: نقاط انتهایی هدف باید صریحاً در network.allowlist محیط شما مجاز باشند. ترافیک حلقه برگشتی ( 127.0.0.1 یا localhost ) توسط پروکسی مسدود شده است؛ همیشه نقاط انتهایی خارجی مجاز را هدف قرار دهید.
  • تبدیل توکن: نیازی نیست کلیدهای API یا توکن‌های حامل مخفی را در داخل .agents/hooks.json ذخیره کنید یا آنها را در کانتینر قرار دهید. در عوض، قوانین تبدیل توکن را در پیکربندی شبکه خود ( network.allowlist.transform ) پیکربندی کنید. پروکسی خروجی به طور خودکار ترافیک خروجی HTTP hook را رهگیری می‌کند و هدرهای احراز هویت واقعی شما را قبل از خروج از sandbox به سیم تزریق می‌کند.

نحوه مدیریت تصمیمات و شکست‌ها توسط runtime

  • انتظار همزمان: عامل مکث می‌کند و منتظر می‌ماند تا قلاب‌های شما قبل از ادامه، تمام شوند.
  • مسدود کردن اجرای ابزار: اگر قلاب پیش از ابزار شما {"decision": "deny", "reason": "<your reason>"} را برگرداند، زمان اجرا بلافاصله فراخوانی ابزار را لغو می‌کند. مدل دلیل رد درخواست شما را در تاریخچه مکالمات خود می‌بیند و با انتخاب یک جایگزین امن یا توضیح بلوک به کاربر، خود را وفق می‌دهد.
  • مدیریت خرابی‌های اسکریپت، خطاهای HTTP و وقفه‌های زمانی: اگر یک اسکریپت دستوری خراب شود (وضعیت خروج غیر صفر)، یک قلاب HTTP یک کد وضعیت غیر 2xx (مانند خطای سرور 4xx یا 5xx) را برگرداند، یا یک عملیات به پایان برسد یا JSON ناشناخته‌ای را برگرداند، زمان اجرا آن را به عنوان یک تأیید ( allow ) در نظر می‌گیرد. اجرای ابزار به طور عادی ادامه می‌یابد، بنابراین یک اسکریپت خراب یا سرور تله‌متری غیرقابل دسترس هرگز برنامه شما را دچار بن‌بست نمی‌کند.

موارد استفاده رایج

بازیابی چند مرحله‌ای برای حفظ حریم خصوصی داده‌ها و انطباق با قوانین

وقتی یک قلاب دسترسی به منابع محدود - مانند دایرکتوری‌های حاوی اطلاعات شخصی قابل شناسایی (PII) یا سوابق مالی محرمانه - را مسدود می‌کند، می‌توانید previous_interaction_id در فراخوانی بعدی ارسال کنید تا نوبت در همان محیط ادامه یابد. عامل توضیح عدم پذیرش را می‌خواند و به جای آن، با پرس‌وجو از جداول عمومی تأیید شده، به طور خودکار بازیابی می‌کند.

پایتون

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)

جاوا اسکریپت

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: [
            {