قلاب

هوک‌ها به شما امکان می‌دهند اسکریپت‌های سفارشی یا درخواست‌های 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);

استراحت

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: [
            {
                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"'"
#   }'

ثبت وقایع ممیزی خارجی و تله‌متری

ارسال رویدادهای حسابرسی در لحظه از داخل سندباکس به یک سرور مانیتورینگ خارجی، هر زمان که فایل‌ها خوانده یا تغییر داده می‌شوند.

  • تطبیق چندین ابزار: از آنجایی که تطبیق‌دهنده‌ها از عبارات منظم استاندارد استفاده می‌کنند، می‌توانید چندین ابزار را در یک قانون واحد با استفاده از pipeها ( read_file|write_file ) یا wildcardها ( .*_file ) ترکیب کنید.
  • اسرار را از پیکربندی خود دور نگه دارید: توکن‌های احراز هویت را در پیکربندی شبکه محیط خود ( network.allowlist.transform ) تعریف کنید. پروکسی خروجی به طور خودکار توکن‌های حامل واقعی شما را در درخواست‌های خروجی تزریق می‌کند.

پایتون

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)

جاوا اسکریپت

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 محیط خود مجاز کنید. آدرس‌های Loopback ( localhost ، 127.0.0.1 ) توسط پروکسی مسدود می‌شوند.
  • تأیید خودکار خطاها: اگر یک اسکریپت هوک از کار بیفتد (وضعیت خروج غیر صفر)، زمان آن تمام شود یا با شکست مواجه شود، زمان اجرا، شکست را ثبت می‌کند و به فراخوانی ابزار اجازه می‌دهد تا ادامه یابد. این امر تضمین می‌کند که اسکریپت‌های linter خراب یا فرآیندهای معلق هرگز برنامه‌های شما را به بن‌بست نمی‌رسانند.
  • محافظت از پیکربندی Sandbox: از آنجا که قلاب‌ها درون جعبه شنی کانتینر اجرا می‌شوند، عامل‌هایی که ابزارهای نوشتن سیستم فایل یا مجوزهای اجرای کد شل دارند می‌توانند فایل .agents/hooks.json محلی یا اسکریپت‌ها را درون فضاهای کاری قابل نوشتن تغییر دهند. از قلاب‌های کانتینر به عنوان راهنمای خودکار سیاست و محافظ عملیاتی استفاده کنید. اگر مقاومت شدید در برابر دستکاری در برابر اجرای مدل‌های غیرقابل اعتماد مورد نیاز است، منابع پیکربندی را از مخازن فقط خواندنی mount کنید.

قدم بعدی چیست؟

،

هوک‌ها به شما امکان می‌دهند اسکریپت‌های سفارشی یا درخواست‌های 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);

استراحت

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: [
            {
                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"'"
#   }'

ثبت وقایع ممیزی خارجی و تله‌متری

ارسال رویدادهای حسابرسی در لحظه از داخل سندباکس به یک سرور مانیتورینگ خارجی، هر زمان که فایل‌ها خوانده یا تغییر داده می‌شوند.

  • تطبیق چندین ابزار: از آنجایی که تطبیق‌دهنده‌ها از عبارات منظم استاندارد استفاده می‌کنند، می‌توانید چندین ابزار را در یک قانون واحد با استفاده از pipeها ( read_file|write_file ) یا wildcardها ( .*_file ) ترکیب کنید.
  • اسرار را از پیکربندی خود دور نگه دارید: توکن‌های احراز هویت را در پیکربندی شبکه محیط خود ( network.allowlist.transform ) تعریف کنید. پروکسی خروجی به طور خودکار توکن‌های حامل واقعی شما را در درخواست‌های خروجی تزریق می‌کند.

پایتون

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)

جاوا اسکریپت

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 محیط خود مجاز کنید. آدرس‌های Loopback ( localhost ، 127.0.0.1 ) توسط پروکسی مسدود می‌شوند.
  • تأیید خودکار خطاها: اگر یک اسکریپت هوک از کار بیفتد (وضعیت خروج غیر صفر)، زمان آن تمام شود یا با شکست مواجه شود، زمان اجرا، شکست را ثبت می‌کند و به فراخوانی ابزار اجازه می‌دهد تا ادامه یابد. این امر تضمین می‌کند که اسکریپت‌های linter خراب یا فرآیندهای معلق هرگز برنامه‌های شما را به بن‌بست نمی‌رسانند.
  • محافظت از پیکربندی Sandbox: از آنجا که قلاب‌ها درون جعبه شنی کانتینر اجرا می‌شوند، عامل‌هایی که ابزارهای نوشتن سیستم فایل یا مجوزهای اجرای کد شل دارند می‌توانند فایل .agents/hooks.json محلی یا اسکریپت‌ها را درون فضاهای کاری قابل نوشتن تغییر دهند. از قلاب‌های کانتینر به عنوان راهنمای خودکار سیاست و محافظ عملیاتی استفاده کنید. اگر مقاومت شدید در برابر دستکاری در برابر اجرای مدل‌های غیرقابل اعتماد مورد نیاز است، منابع پیکربندی را از مخازن فقط خواندنی mount کنید.

قدم بعدی چیست؟