Hooks

ה-Hooks מאפשרים להריץ סקריפטים בהתאמה אישית או בקשות HTTP חיצוניות ממש לפני שהסוכן מריץ קוד או משנה קבצים בארגז החול המרוחק שלו, או ממש אחרי. אפשר להשתמש ב-hooks כדי להרחיב את לולאת הסוכן עם אמצעי הגנה אוטומטיים ותהליכי עבודה ברקע, כמו:

  • אכיפת אמצעי הגנה על בטיחות וגישה לפני הפעלה של פקודות shell בסיכון גבוה או קריאות קבצים מוגבלות.
  • אוטומציה של טרנספורמציות בצינור נתונים מיד אחרי שהסוכן יוצר או משנה קבצים.
  • הזרמת טלמטריית ביקורת של הארגון למערכות ניטור חיצוניות אחרי הפעלת הכלי.

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

REST

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

אירועים נתמכים במחזור החיים

ה-Hooks תומכים ב-2 אירועים בתוך ארגז החול:

אירוע מתי הוא מופעל תיאור
pre_tool_execution רגע לפני הפעלת כלי יכולים לאשר (allow) או לחסום (deny) את הכלי לפני שהוא מופעל. כשחוסמים את המודל, הוא רואה את הסיבה לדחייה ומתאים את עצמו.
post_tool_execution מיד אחרי ששימוש בכלי מסוים מסתיים מריץ משימות המשך כמו עיצוב קוד, הרצת בדיקות יחידה או רישום טלמטריה. אי אפשר לחסום פעולות שהושלמו או לבטל אותן.

pre_tool_execution

מופעל ממש לפני שכלי מבצע פעולה. הסקריפט קורא את פרטי הקריאה לכלי מ-stdin ומפיק את ה-JSON של ההחלטה (allow או deny) ל-stdout.

מטען ייעודי (payload) של קלט (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."
}

אם פונקציית ה-hook דוחה פקודה, הכלי לא יופעל. הנציג רואה תוצאת שגיאה שמכילה את סיבת הדחייה שלו בתוך התור הנוכחי. לאחר מכן המודל יכול לתקן את עצמו על ידי בחירת פקודה חלופית או הסבר למשתמש על החסימה.

אם הסקריפט מוציא פלט של JSON לא מוכר, טקסט פשוט או כל דבר אחר מלבד {"decision": "deny"}, זמן הריצה מתייחס לתשובה כאל אישור (allow).

post_tool_execution

מופעל מיד אחרי שכלי משלים את הפעולה. הסקריפט קורא את פרטי ההרצה ואת סטטוס השגיאה מ-stdin.

מטען ייעודי (payload) של קלט (stdin):

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

אם פקודת מעטפת מדפיסה שגיאות לשגיאה רגילה (stderr) או אם פעולה במערכת הקבצים נכשלת, מטען הייעודי (payload) כולל שדה "error" שמכיל את טקסט השגיאה. אם הפקודה מצליחה ללא שגיאות, השדה "error" לא מופיע בכלל.

תשובת הפלט (stdout):

{}

הסיבה לכך היא ש-hooks של אחרי כלי פועלים רק למשימות ברקע, כמו עיצוב קוד או רישום ביומן, ולכן זמן הריצה מתעלם מכל ערכי ההחלטה שמוחזרים ב-stdout.

גילוי הגדרות

סביבת זמן הריצה מגלה באופן אוטומטי הגדרות של ווים מ-.agents/hooks.json או מ-/.agents/hooks.json בתוך סביבת ארגז החול. אפשר לספק את hooks.json לצד הסקריפטים המותאמים אישית באמצעות כל מקור סביבה נתמך:

  • Repository mount: מאגר 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 מגדירה מתי ואיך מופעלים ה-handlers באמצעות המאפיינים matcher ו-hooks:

שדה סוג תיאור
enabled boolean אופציונלי. מגדירים את הערך false כדי להשבית את הקבוצה (true כברירת מחדל).
matcher string הכלי שבו מתבצעת ההתאמה של תבנית הביטוי הרגולרי לשמות של כלים בתוך המאגר.
hooks array רשימה מסודרת של הגדרות של רכיבי handler ‏ (command או http). רכיבי ה-handler מופעלים ברצף לפי סדר ההצהרה.

איך מתבצעת הערכה של ביטויים רגולריים

כשהסוכן מפעיל כלי בתוך ארגז החול, סביבת זמן הריצה מעריכה את שם מאגר התגים של הכלי בהשוואה לתבנית matcher באמצעות ביטויים רגולריים סטנדרטיים של RE2. אם הביטוי הרגולרי תואם לשם הכלי, כל הפונקציות במערך hooks מופעלות לפי הסדר. אם כמה קבוצות כללים תואמות לאותו כלי, כל מערכי ה-handler המתאימים מופעלים.

אפשר לטרגט כל שם של כלי מובנה למאגר תגים: הפעלת קוד (code_execution) או פעולות במערכת הקבצים (read_file, ‏write_file, ‏list_files ו-delete_file).

ביטויים נפוצים של התאמה

  • "code_execution": התאמה מדויקת של מחרוזות לפקודות מעטפת ולהרצת סקריפטים.
  • "write_file": התאמה מדויקת ליצירת קבצים במערכת הקבצים ולכתיבה לדיסק.
  • "read_file|write_file": הפרדה באמצעות קו אנכי מאפשרת התאמה של כמה שמות ספציפיים של כלים בכלל אחד.
  • ".*_file": התאמה של wildcard לביטוי רגולרי לכל כלי שמסתיים ב-_file (כמו read_file, ‏ write_file או delete_file). ביטויים רגולריים רגילים של RE2 דורשים .*. ביטויי glob פשוטים של shell כמו *_file הם תחביר לא חוקי של ביטוי רגולרי, ולא תהיה התאמה.
  • ".*" או "*" או "": תבנית כללית שמיירטת כל קריאה לכלי בתוך הקונטיינר.

סוגי תוכניות לטיפול

Command hooks

ה-hooks של הפקודות מריצים פקודת Shell או סקריפט בתוך ארגז החול. הסקריפט מקבל את ה-JSON של האירוע ב-stdin ומפיק את ה-JSON של ההחלטה ב-stdout.

שדה סוג תיאור
type string חייב להיות "command".
command string שורת פקודה להרצה בתוך ארגז החול (לדוגמה, python3 /.agents/hooks-scripts/gate.py).
timeout integer זמן קצוב לתפוגה בשניות. ברירת מחדל: 30.

ווים של HTTP

‫HTTP hooks שולחים את ה-JSON של האירוע כבקשת POST לכתובת URL חיצונית של HTTPS ישירות מתוך רשת ארגז החול. שרת היעד מחזיר את ההחלטה שלו בגוף תגובת ה-HTTP באמצעות אותו פורמט JSON בדיוק ({"decision": "allow"} או {"decision": "deny", "reason": "..."}).

שדה סוג תיאור
type string חייב להיות "http".
url string נקודת קצה (endpoint) חיצונית מסוג HTTPS שאליה יישלח המטען הייעודי (payload) של האירוע באמצעות POST.
headers object צמדי מפתח/ערך אופציונליים עבור כותרות מותאמות אישית לא רגישות (כמו {"X-Event-Source": "agent-sandbox"}). כדי להשתמש בפרטי אימות, צריך להשתמש בשרת ה-proxy של הרשת.
timeout integer זמן קצוב לתפוגה בשניות. ברירת מחדל: 30.

שרת Proxy ליציאה וטרנספורמציה של טוקנים

ה-hooks של HTTP מופעלים ישירות מתוך מרחב השמות של הרשת בארגז החול, ולכן בקשות יוצאות עוברות דרך פרוקסי היציאה השקוף. הארכיטקטורה הזו מספקת 2 יתרונות אבטחה חשובים:

  • הוספה לרשימת ההיתרים של הרשת: צריך לאשר במפורש את נקודות הקצה לטירגוט ב-network.allowlist של הסביבה. התעבורה של ה-loopback (127.0.0.1 או localhost) נחסמת על ידי ה-proxy. תמיד צריך לכוון לנקודות קצה חיצוניות ברשימת ההיתרים.
  • שינוי טוקן: אין צורך לאחסן מפתחות API או טוקנים סודיים של Bearer בתוך .agents/hooks.json או לטעון אותם לקונטיינר. במקום זאת, מגדירים כללי המרה של אסימונים בהגדרות הרשת (network.allowlist.transform). שרת ה-proxy ליציאה מיירט באופן אוטומטי תנועת HTTP יוצאת של ווים, ומזריק את כותרות האימות האמיתיות שלכם בחיבור לפני היציאה מהארגז.

איך זמן הריצה מטפל בהחלטות ובכשלים

  • המתנה סינכרונית: הסוכן מושהה וממתין לסיום הפעולה של ה-hooks לפני שהוא ממשיך.
  • חסימת ההפעלה של הכלי: אם ה-hook שלפני הכלי מחזיר {"decision": "deny", "reason": "<your reason>"}, סביבת זמן הריצה מבטלת מיד את הקריאה לכלי. המודל רואה את הסיבה לדחייה בהיסטוריית השיחות שלו, ומתאים את עצמו על ידי בחירת חלופה בטוחה או הסבר למשתמש על החסימה.
  • טיפול בקריסות של סקריפטים, בשגיאות HTTP ובפסק זמן: אם סקריפט של פקודה קורס (סטטוס יציאה שאינו אפס), אם ווֹבּוּק HTTP מחזיר קוד סטטוס שאינו 2xx (למשל שגיאת שרת 4xx או 5xx), או אם פעולה מסוימת חורגת מהזמן הקצוב לתגובה או מחזירה JSON לא מזוהה, סביבת זמן הריצה מתייחסת לכך כאל אישור (allow). הביצוע של הכלי ממשיך כרגיל, כך שסקריפט פגום או שרת טלמטריה שלא ניתן להגיע אליו אף פעם לא גורמים לקיפאון של האפליקציה.

תרחישים נפוצים לדוגמה

שחזור רב-שלבי לשמירה על פרטיות הנתונים ועמידה בדרישות התאימות

כש-Hook חוסם גישה למשאבים מוגבלים – כמו ספריות שמכילות פרטים אישיים מזהים (PII) או רשומות פיננסיות סודיות – אפשר להעביר 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);

REST

# 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). שרת ה-proxy ליציאה מוסיף באופן אוטומטי את אסימוני הגישה האמיתיים לבקשות יוצאות.

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

REST

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

מגבלות

  • היקף השימוש בכלי ארגז החול: ה-Hooks מיירטים כלים מובנים בתוך ארגז החול: הפעלת קוד (code_execution) ופעולות במערכת הקבצים (read_file,‏ write_file,‏ list_files ו-delete_file). הם לא מופעלים עבור קריאות לפונקציות בהתאמה אישית (function) או עבור כלים חיצוניים של Model Context Protocol‏ (mcp_server) שמטופלים מחוץ לקונטיינר.
  • רשימות היתרים ברשת: וובי HTTP פועלים בתוך רשת מאגר התגים. צריך להגדיר במפורש כתובות URL לטירגוט ב-network.allowlist של הסביבה. כתובות לולאה חוזרת (localhost, ‏ 127.0.0.1) חסומות על ידי השרת הפרוקסי.
  • אישור אוטומטי במקרה של שגיאות: אם סקריפט של ווֹק קורס (סטטוס יציאה שאינו אפס), מגיע לזמן קצוב או נכשל, זמן הריצה מתעד את הכשל ומאפשר להמשך הקריאה לכלי. כך אפליקציות לא ייתקעו בגלל סקריפטים פגומים של כלי Linter או תהליכים שנתקעים.
  • הגנה על הגדרות ארגז החול (Sandbox): מכיוון שה-hooks מופעלים בתוך ארגז החול של הקונטיינר, סוכנים עם כלי כתיבה למערכת הקבצים או הרשאות להפעלת קוד מעטפת יכולים לשנות את .agents/hooks.json המקומי או סקריפטים בסביבות עבודה שניתן לכתוב בהן. השתמשו ב-container hooks כהנחיות מדיניות אוטומטיות וכאמצעי הגנה תפעולי. אם נדרשת עמידות קפדנית בפני שיבוש נתונים מפני ביצועים של מודלים לא מהימנים, צריך להגדיר מקורות תצורה ממאגרים לקריאה בלבד.

המאמרים הבאים