Hooks

Mit Hooks können Sie benutzerdefinierte Skripts oder externe HTTP-Anfragen direkt vor oder nach der Ausführung von Code oder der Änderung von Dateien in der Remote-Sandbox des Agents ausführen. Mit Hooks können Sie die Agentenschleife mit automatisierten Schutzmaßnahmen und Hintergrund-Workflows erweitern, z. B.:

  • Sicherheits- und Zugriffsrichtlinien durchsetzen, bevor Shell-Befehle mit hohem Risiko oder eingeschränkte Dateilesevorgänge ausgeführt werden.
  • Automatisieren von Datapipeline-Transformationen direkt nach dem Erstellen oder Ändern von Dateien durch einen Agent.
  • Streaming von Telemetriedaten für Enterprise-Audits an externe Überwachungssysteme nach der Ausführung des Tools.

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

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.AgentOption;
import com.google.genai.gaos.models.interactions.CodeExecution;
import com.google.genai.gaos.models.interactions.CreateAgentInteraction;
import com.google.genai.gaos.models.interactions.CreateAgentInteractionEnvironment;
import com.google.genai.gaos.models.interactions.Environment;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Source;
import com.google.genai.gaos.models.interactions.SourceType;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import java.util.List;

Client client = new Client();

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

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

Environment env = Environment.builder()
    .sources(List.of(
        Source.builder()
            .type(SourceType.INLINE)
            .target(".agents/hooks.json")
            .content(hooksConfig)
            .build(),
        Source.builder()
            .type(SourceType.INLINE)
            .target(".agents/hooks-scripts/gate.py")
            .content(gateScript)
            .build()
    ))
    .build();

CreateAgentInteraction params = CreateAgentInteraction.builder()
    .agent(AgentOption.of("antigravity-preview-09-2026"))
    .input(InteractionsInput.of("Run `rm -rf /tmp/forbidden` using code_execution."))
    .tools(List.of(CodeExecution.builder().build()))
    .environment(CreateAgentInteractionEnvironment.of(env))
    .build();

Interaction interaction = client.interactions.create(CreateInteractionRequestBody.of(params)).interaction().get();
System.out.println(interaction.outputText().orElse(""));

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

Unterstützte Lebenszyklusereignisse

Hooks unterstützen zwei Ereignisse in der Sandbox:

Ereignis Wann wird sie ausgelöst? Funktion
pre_tool_execution Kurz bevor ein Tool ausgeführt wird Sie können das Tool genehmigen (allow) oder blockieren (deny), bevor es ausgeführt wird. Wenn das Modell blockiert wird, sieht es den Grund für die Ablehnung und passt sich an.
post_tool_execution Direkt nach Abschluss eines Tools Führt Folgeaufgaben wie das Formatieren von Code, das Ausführen von Unittests oder das Protokollieren von Telemetriedaten aus. Abgeschlossene Aktionen können nicht blockiert oder rückgängig gemacht werden.

pre_tool_execution

Wird direkt vor der Ausführung eines Tools ausgelöst. Ihr Skript liest die Details des Tool-Aufrufs aus stdin und gibt die JSON-Datei mit der Entscheidung (allow oder deny) in stdout aus.

Eingabe-Nutzlast (stdin):

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

Ausgabeantwort (stdout):

So genehmigen Sie den Tool-Aufruf:

{
  "decision": "allow"
}

So blockieren Sie den Toolaufruf und geben Feedback an das Modell zurück:

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

Wenn ein Hook einen Befehl ablehnt, wird der Tool-Aufruf sofort übersprungen. Der KI-Agent sieht in seinem aktuellen Zug ein Fehlerergebnis mit Ihrem Ablehnungsgrund. Das Modell kann sich dann selbst korrigieren, indem es einen alternativen Befehl auswählt oder dem Nutzer den Block erklärt.

Wenn Ihr Skript nicht erkanntes JSON, Nur-Text oder etwas anderes als {"decision": "deny"} ausgibt, behandelt die Laufzeit die Antwort als Genehmigung (allow).

post_tool_execution

Wird direkt nach Abschluss eines Tools ausgelöst. Ihr Skript liest die Ausführungsdetails und den Fehlerstatus aus stdin.

Eingabe-Nutzlast (stdin):

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

Wenn ein Shell-Befehl Fehler in den Standardfehler (stderr) ausgibt oder ein Dateisystemvorgang fehlschlägt, wird der Nutzlast ein Feld "error" mit dem Fehlertext hinzugefügt. Wenn der Befehl ohne Fehler ausgeführt wird, wird das Feld "error" vollständig ausgelassen.

Ausgabeantwort (stdout):

{}

Da Post-Tool-Hooks nur für Hintergrundaufgaben wie die Codeformatierung oder das Logging ausgeführt werden, ignoriert die Laufzeit alle Entscheidungs-Werte, die für stdout zurückgegeben werden.

Konfigurationserkennung

Die Laufzeit erkennt Hook-Definitionen aus .agents/hooks.json oder /.agents/hooks.json in der Sandbox-Umgebung automatisch. Sie können hooks.json zusammen mit Ihren benutzerdefinierten Skripts über eine beliebige unterstützte Umgebungsquelle bereitstellen:

  • Repository-Mount: Ein Git-Repository, das .agents/hooks.json und AGENTS.md enthält.
  • Cloud Storage (gcs): Ein GCS-Bucket mit hooks.json, die in die Umgebung kopiert werden.
  • Inline-Quellen: Roh-JSON-String und Scriptinhalte, die beim Aufrufen von client.interactions.create in environment.sources übergeben werden.

hooks.json Schema

In einer hooks.json-Datei werden Ereignisdefinitionen (pre_tool_execution oder post_tool_execution) unter benutzerdefinierten Namen gruppiert. Sie können jede Gruppe unabhängig voneinander aktivieren oder deaktivieren:

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

Syntax und Regeln für Matcher

In jeder Regelgruppe in hooks.json wird mit den Eigenschaften matcher und hooks festgelegt, wann und wie Handler ausgelöst werden:

Feld Typ Beschreibung
enabled boolean Optional. Legen Sie false fest, um die Gruppe zu deaktivieren (standardmäßig true).
matcher string Regulärer Ausdruck für den Abgleich von Zieltool-Namen im Container.
hooks array Geordnete Liste von Handlerdefinitionen (command oder http). Handler werden sequenziell in der Reihenfolge der Deklaration ausgeführt.

So funktioniert die Regex-Auswertung

Wenn der Agent ein Tool in der Sandbox aufruft, vergleicht die Laufzeit den Containernamen des Tools mit Ihrem matcher-Muster. Dabei werden standardmäßige RE2-reguläre Ausdrücke verwendet. Wenn der reguläre Ausdruck mit dem Toolnamen übereinstimmt, werden alle Handler im hooks-Array in der Reihenfolge ausgeführt. Wenn mehrere Regelgruppen mit demselben Tool übereinstimmen, werden alle entsprechenden Handler-Arrays ausgeführt.

Sie können auf einen beliebigen integrierten Containertool-Namen abzielen: Code-Ausführung (code_execution) oder Dateisystemvorgänge (view_file, write_to_file, replace_file_content, list_dir und delete_file).

Häufig verwendete Matcher-Ausdrücke

  • "code_execution": Exakte Stringübereinstimmung für Shell-Befehle und Skriptausführungen.
  • "write_to_file": Exakte Übereinstimmung für die Erstellung von Dateisystemdateien und Festplattenschreibvorgänge.
  • "view_file|write_to_file": Durch die Trennung mit einem senkrechten Strich werden mehrere bestimmte Toolnamen in einer einzelnen Regel abgeglichen.
  • ".*_file": Regulärer Ausdruck mit Platzhalter, der auf alle Tools passt, die mit _file enden, z. B. view_file, write_to_file oder delete_file. Dies deckt nur einen Teil der Dateisystem-Toolsets ab. replace_file_content und list_dir enden nicht mit _file. Nennen Sie sie daher explizit, wenn Sie sie benötigen. Für Standard-RE2-reguläre Ausdrücke ist .* erforderlich. Einfache Shell-Globs wie *_file sind keine gültige Regex-Syntax und führen zu keinen Übereinstimmungen.
  • ".*" oder "*" oder "": Catch-all-Muster, das jeden einzelnen Toolaufruf im Container abfängt.

Handler-Typen

Befehls-Hooks

Mit Befehlshooks wird ein Shell-Befehl oder ein Skript in der Sandbox ausgeführt. Das Script empfängt das Ereignis-JSON unter stdin und gibt das Entscheidungs-JSON unter stdout aus.

Feld Typ Beschreibung
type string Muss "command" lauten.
command string Befehlszeile, die in der Sandbox ausgeführt werden soll (z. B. python3 /.agents/hooks-scripts/gate.py).
timeout integer Zeitlimit in Sekunden. Standard: 30.

HTTP-Hooks

Bei HTTP-Hooks wird das Ereignis-JSON als POST-Anfrage direkt aus dem Sandbox-Netzwerk an eine externe HTTPS-URL gesendet. Der Zielserver gibt seine Entscheidung im HTTP-Antworttext im genau gleichen JSON-Format ({"decision": "allow"} oder {"decision": "deny", "reason": "..."}) zurück.

Feld Typ Beschreibung
type string Muss "http" lauten.
url string Externer HTTPS-Endpunkt, an den die Ereignisnutzlast gesendet werden soll.
headers object Optionale Schlüssel/Wert-Paare für nicht vertrauliche benutzerdefinierte Header (z. B. {"X-Event-Source": "agent-sandbox"}). Verwenden Sie für die Authentifizierung stattdessen Anmeldedaten in der Zulassungsliste für das Netzwerk.
timeout integer Zeitlimit in Sekunden. Standard: 30.

Proxy für ausgehenden Traffic und Token-Transformation

Da HTTP-Hooks direkt aus dem Sandbox-Netzwerk-Namespace ausgeführt werden, werden ausgehende Anfragen über den transparenten Egress-Proxy geleitet. Diese Architektur bietet zwei wichtige Sicherheitsvorteile:

  • Netzwerk-Allowlisting:Zielendpunkte müssen in der network.allowlist Ihrer Umgebung explizit zugelassen werden. Loopback-Traffic (127.0.0.1 oder localhost) wird vom Proxy blockiert. Verwenden Sie immer zugelassene externe Endpunkte als Ziel.
  • Einfügen von Anmeldedaten:Sie müssen API-Schlüssel oder geheime Bearer-Tokens nicht in .agents/hooks.json speichern oder in den Container einbinden. Speichern Sie das Secret einmal als Anmeldedaten und verweisen Sie über die ID aus der network.allowlist Ihrer Umgebung darauf. Der Egress-Proxy fängt automatisch ausgehenden HTTP-Hook-Traffic ab und fügt den echten Authentifizierungsheader in die Verbindung ein, bevor die Sandbox verlassen wird. Bei Inline-transform-Regeln werden Header auf dieselbe Weise festgelegt. Ein Anmeldedatum ist die richtige Wahl, wenn Sie das Secret im gesamten Projekt wiederverwenden und an einem Ort rotieren möchten. Weitere Informationen finden Sie unter Netzwerkkonfiguration.

So werden Entscheidungen und Fehler von der Laufzeitumgebung behandelt

  • Synchrones Warten:Der Agent pausiert und wartet, bis Ihre Hooks abgeschlossen sind, bevor er fortfährt.
  • Ausführung des Tools blockieren:Wenn Ihr Pre-Tool-Hook {"decision": "deny", "reason": "<your reason>"} zurückgibt, wird der Tool-Aufruf von der Laufzeit sofort abgebrochen. Das Modell sieht den Ablehnungsgrund in seinem Unterhaltungsverlauf und passt sich an, indem es eine sichere Alternative auswählt oder dem Nutzer den Grund für die Blockierung erklärt.
  • Behandlung von Scriptabstürzen, HTTP-Fehlern und Zeitüberschreitungen:Wenn ein Befehlsscript abstürzt (Exit-Status ungleich null), ein HTTP-Hook einen Statuscode ungleich 2xx zurückgibt (z. B. ein 4xx- oder 5xx-Serverfehler), ein Vorgang eine Zeitüberschreitung verursacht oder nicht erkannte JSON-Daten zurückgibt, wird er von der Laufzeit als Genehmigung (allow) behandelt. Die Toolausführung wird normal fortgesetzt, sodass ein fehlerhaftes Script oder ein nicht erreichbarer Telemetrieserver Ihre Anwendung niemals blockiert.

Gängige Anwendungsfälle

Mehrfachdialog-Wiederherstellung für Datenschutz und Compliance

Wenn ein Hook den Zugriff auf eingeschränkte Ressourcen wie Verzeichnisse mit personenidentifizierbaren Informationen (PII) oder vertraulichen Finanzdaten blockiert, können Sie previous_interaction_id im nächsten Aufruf übergeben, um den Turn in derselben Umgebung fortzusetzen. Der KI-Agent liest die Erklärung für die Ablehnung und stellt automatisch eine Verbindung zu genehmigten öffentlichen Tabellen her, um die Anfrage zu bearbeiten.

Python

import json
from google import genai

client = genai.Client()

hooks_config = {
    "privacy-gate": {
        "pre_tool_execution": [
            {
                "matcher": "view_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-09-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-09-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: "view_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-09-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-09-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);

Java

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.CreateAgentInteractionEnvironment;
import com.google.genai.gaos.models.interactions.Environment;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Source;
import com.google.genai.gaos.models.interactions.SourceType;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import java.util.List;

Client client = new Client();

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

String checkPrivacyScript = "#!/usr/bin/env python3\n"
    + "import sys, json\n"
    + "data = json.load(sys.stdin)\n"
    + "path = str(data.get(\"tool_call\", {}).get(\"args\", {}).get(\"path\", \"\"))\n"
    + "if \"/private/\" in path:\n"
    + "    resp = {\n"
    + "        \"decision\": \"deny\",\n"
    + "        \"reason\": \"Access to confidential `/private/` records is blocked by PII compliance policy. Query approved `/public/` summary tables instead.\"\n"
    + "    }\n"
    + "else:\n"
    + "    resp = {\"decision\": \"allow\"}\n"
    + "print(json.dumps(resp))\n";

Environment env = Environment.builder()
    .sources(List.of(
        Source.builder()
            .type(SourceType.INLINE)
            .target(".agents/hooks.json")
            .content(hooksConfig)
            .build(),
        Source.builder()
            .type(SourceType.INLINE)
            .target(".agents/hooks-scripts/check_privacy.py")
            .content(checkPrivacyScript)
            .build(),
        Source.builder()
            .type(SourceType.INLINE)
            .target("workspace/private/employees.json")
            .content("{\"employees\": [{\"id\": 1, \"salary\": 150000, \"ssn\": \"000-00-0000\"}]}")
            .build(),
        Source.builder()
            .type(SourceType.INLINE)
            .target("workspace/public/summary.json")
            .content("{\"department\": \"Engineering\", \"team_size\": 42, \"status\": \"active\"}")
            .build()
    ))
    .build();

// Step 1: Agent attempts to read confidential PII records and is intercepted
CreateAgentInteraction params1 = CreateAgentInteraction.builder()
    .agent(AgentOption.of("antigravity-preview-09-2026"))
    .input(InteractionsInput.of("Use your filesystem tool to read `/workspace/private/employees.json` and summarize the employee details."))
    .environment(CreateAgentInteractionEnvironment.of(env))
    .build();

Interaction int1 = client.interactions.create(CreateInteractionRequestBody.of(params1)).interaction().get();
System.out.println(int1.outputText().orElse(""));

// Step 2: Continue in the same environment using previous_interaction_id; agent recovers with public tables
CreateAgentInteraction params2 = CreateAgentInteraction.builder()
    .agent(AgentOption.of("antigravity-preview-09-2026"))
    .input(InteractionsInput.of("Understood. Please read the approved `/workspace/public/summary.json` file instead and provide the summary."))
    .environment(CreateAgentInteractionEnvironment.of(int1.environmentId().orElse("")))
    .previousInteractionId(int1.id().orElse(""))
    .build();

Interaction int2 = client.interactions.create(CreateInteractionRequestBody.of(params2)).interaction().get();
System.out.println(int2.outputText().orElse(""));

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-09-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\": \"view_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-09-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"'"
#   }'

Externes Audit-Logging und Telemetrie

Auditereignisse in Echtzeit aus der Sandbox an einen externen Überwachungsserver senden, wenn Dateien gelesen oder geändert werden.

  • Mehrere Tools abgleichen:Da für den Abgleich Standard-Regex verwendet werden, können Sie mehrere Tools in einer einzigen Regel mit senkrechten Strichen (view_file|write_to_file|replace_file_content) oder Platzhaltern (.*_file) kombinieren.
  • Secrets aus Ihrer Konfiguration entfernen:Speichern Sie das Authentifizierungstoken als Anmeldedaten und verweisen Sie anhand der ID aus der Netzwerkkonfiguration Ihrer Umgebung (network.allowlist.credential) darauf. Der Egress-Proxy fügt das tatsächliche Bearer-Token in ausgehende Anfragen ein. In diesem Beispiel wird der Header stattdessen inline mit transform festgelegt, das durch denselben Proxy geschützt ist und passt, wenn das Token zu dieser Konfiguration gehört.

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": "view_file|write_to_file|replace_file_content",
                "hooks": [
                    {
                        "type": "http",
                        "url": "https://telemetry.example.com/api/v1/agent-events",
                        "timeout": 10,
                    }
                ],
            }
        ]
    }
}

interaction = client.interactions.create(
    agent="antigravity-preview-09-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: "view_file|write_to_file|replace_file_content",
                hooks: [
                    {
                        type: "http",
                        url: "https://telemetry.example.com/api/v1/agent-events",
                        timeout: 10,
                    },
                ],
            },
        ],
    },
};

const interaction = await client.interactions.create({
    agent: "antigravity-preview-09-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);

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.AgentOption;
import com.google.genai.gaos.models.interactions.Allowlist;
import com.google.genai.gaos.models.interactions.AllowlistEntry;
import com.google.genai.gaos.models.interactions.CreateAgentInteraction;
import com.google.genai.gaos.models.interactions.CreateAgentInteractionEnvironment;
import com.google.genai.gaos.models.interactions.Environment;
import com.google.genai.gaos.models.interactions.EnvironmentNetworkEgressAllowlist;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Network;
import com.google.genai.gaos.models.interactions.Source;
import com.google.genai.gaos.models.interactions.SourceType;
import com.google.genai.gaos.models.interactions.Transform;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import java.util.List;
import java.util.Map;

Client client = new Client();

// Define hook without secrets; the egress proxy injects headers dynamically
String 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
          }
        ]
      }
    ]
  }
}
""";

Environment env = Environment.builder()
    .sources(List.of(
        Source.builder()
            .type(SourceType.INLINE)
            .target(".agents/hooks.json")
            .content(hooksConfig)
            .build()
    ))
    .network(Network.of(EnvironmentNetworkEgressAllowlist.of(
        Allowlist.builder()
            .allowlist(List.of(
                AllowlistEntry.builder()
                    .domain("telemetry.example.com")
                    .transform(Transform.of(Map.of(
                        "Authorization", "Bearer telemetry_secret_token_123"
                    )))
                    .build(),
                AllowlistEntry.builder().domain("*").build()
            ))
            .build()
    )))
    .build();

CreateAgentInteraction params = CreateAgentInteraction.builder()
    .agent(AgentOption.of("antigravity-preview-09-2026"))
    .input(InteractionsInput.of("Use your filesystem tool to create `/workspace/audit.log` containing 'event 1', then immediately read it back using your filesystem read tool."))
    .environment(CreateAgentInteractionEnvironment.of(env))
    .build();

Interaction interaction = client.interactions.create(CreateInteractionRequestBody.of(params)).interaction().get();
System.out.println(interaction.outputText().orElse(""));

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-09-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\": \"view_file|write_to_file|replace_file_content\", \"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": "*"}
              ]
          }
      }
  }'

Beschränkungen

  • Umfang des Sandbox-Tools:Hooks fangen integrierte Tools in der Sandbox ab: Code-Ausführung (code_execution) und Dateisystemvorgänge (view_file, write_to_file, replace_file_content, list_dir und delete_file). Sie werden nicht für benutzerdefinierte Funktionsaufrufe (function) oder externe MCP-Tools (Model Context Protocol, mcp_server) ausgelöst, die außerhalb des Containers verarbeitet werden.
  • Netzwerk-Zulassungslisten:HTTP-Hooks werden im Containernetzwerk ausgeführt. Sie müssen Ziel-URLs in der network.allowlist Ihrer Umgebung explizit zulassen. Loopback-Adressen (localhost, 127.0.0.1) werden vom Proxy blockiert.
  • Automatische Genehmigung bei Fehlern:Wenn ein Hook-Script abstürzt (Exit-Status ungleich null), das Zeitlimit überschritten wird oder es fehlschlägt, protokolliert die Laufzeit den Fehler und ermöglicht die Fortsetzung des Tool-Aufrufs. So wird sichergestellt, dass fehlerhafte Linter-Skripts oder Prozesse, die nicht beendet werden, Ihre Anwendungen nicht blockieren.
  • Schutz der Sandbox-Konfiguration:Da Hooks in der Container-Sandbox ausgeführt werden, können Agents mit Berechtigungen zum Schreiben in das Dateisystem oder zum Ausführen von Shell-Code lokale .agents/hooks.json oder Skripts in beschreibbaren Arbeitsbereichen ändern. Verwenden Sie Container-Hooks als automatisierte Richtlinien und operative Schutzmaßnahmen. Wenn ein strenger Manipulationsschutz gegen nicht vertrauenswürdige Modellläufe erforderlich ist, stellen Sie Konfigurationsquellen aus schreibgeschützten Repositorys bereit.

Nächste Schritte