フックを使用すると、エージェントがコードを実行したり、リモート サンドボックス内のファイルを変更する直前または直後に、カスタム スクリプトや外部 HTTP リクエストを実行できます。フックを使用して、次のような自動化されたガードレールやバックグラウンド ワークフローでエージェント ループを拡張します。
- リスクの高いシェル コマンドや制限付きのファイル読み取りが実行される前に、安全性とアクセスに関するガードレールを適用します。
- エージェントがファイルを作成または変更した直後にデータ パイプラインの変換を自動化します。
ツールの実行後に、企業監査テレメトリーを外部モニタリング システムにストリーミングします。
Python
import json
from google import genai
client = genai.Client()
hooks_config = {
"security-gate": {
"pre_tool_execution": [
{
"matcher": "code_execution",
"hooks": [
{
"type": "command",
"command": "python3 /.agents/hooks-scripts/gate.py",
"timeout": 10,
}
],
}
]
}
}
gate_script = """#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
print(json.dumps({"decision": "allow"}))
"""
interaction = client.interactions.create(
agent="antigravity-preview-05-2026",
input="Run `rm -rf /tmp/forbidden` using code_execution.",
tools=[{"type": "code_execution"}],
environment={
"type": "remote",
"sources": [
{
"type": "inline",
"target": ".agents/hooks.json",
"content": json.dumps(hooks_config, indent=2),
},
{
"type": "inline",
"target": ".agents/hooks-scripts/gate.py",
"content": gate_script,
},
],
},
)
print(interaction.output_text)
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const hooksConfig = {
"security-gate": {
pre_tool_execution: [
{
matcher: "code_execution",
hooks: [
{
type: "command",
command: "python3 /.agents/hooks-scripts/gate.py",
timeout: 10,
},
],
},
],
},
};
const gateScript = `#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by security gate."}))
else:
print(json.dumps({"decision": "allow"}))
`;
const interaction = await client.interactions.create({
agent: "antigravity-preview-05-2026",
input: "Run `rm -rf /tmp/forbidden` using code_execution.",
tools: [{ type: "code_execution" }],
environment: {
type: "remote",
sources: [
{
type: "inline",
target: ".agents/hooks.json",
content: JSON.stringify(hooksConfig, null, 2),
},
{
type: "inline",
target: ".agents/hooks-scripts/gate.py",
content: gateScript,
},
],
},
});
console.log(interaction.output_text);
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"
}
]
}
}'
サポートされているライフサイクル イベント
フックはサンドボックス内で 2 つのイベントをサポートしています。
| イベント | 発火のタイミング | 機能 |
|---|---|---|
pre_tool_execution |
ツールの実行直前 | ツールが実行される前に、ツールを承認(allow)またはブロック(deny)できます。ブロックされると、モデルは拒否の理由を認識して適応します。 |
post_tool_execution |
ツールの完了直後 | コードのフォーマット、単体テストの実行、テレメトリーのロギングなどのフォローアップ タスクを実行します。完了した操作をブロックしたり、元に戻したりすることはできません。 |
pre_tool_execution
ツールが実行される直前に発生します。スクリプトは stdin からツール呼び出しの詳細を読み取り、決定 JSON(allow または deny)を stdout に出力します。
入力ペイロード(stdin):
{
"tool_call": {
"name": "code_execution",
"args": {
"code": "rm -rf /tmp/forbidden",
"language": "bash"
}
},
"environment_id": "env_xyz789"
}
出力レスポンス(stdout):
ツール呼び出しを承認するには:
{
"decision": "allow"
}
ツール呼び出しをブロックしてモデルにフィードバックを返すには:
{
"decision": "deny",
"reason": "Destructive command blocked by security gate."
}
フックがコマンドを拒否すると、ツール呼び出しはすぐにスキップされます。エージェントは、現在のターン内に拒否理由を含むエラー結果を表示します。モデルは、別のコマンドを選択するか、ブロックされた理由をユーザーに説明することで、自己修正できます。
スクリプトが認識できない JSON、プレーン テキスト、または {"decision": "deny"} 以外のものを出力すると、ランタイムはレスポンスを承認(allow)として扱います。
post_tool_execution
ツールが完了した直後に発生します。スクリプトは、stdin から実行の詳細とエラー ステータスを読み取ります。
入力ペイロード(stdin):
{
"tool_call": {
"name": "code_execution",
"args": {
"code": "python3 /workspace/app.py",
"language": "bash"
}
},
"environment_id": "env_xyz789"
}
シェル コマンドが標準エラー(stderr)にエラーを出力した場合、またはファイルシステム オペレーションが失敗した場合、エラーテキストを含む "error" フィールドがペイロードに含まれます。コマンドがエラーなしで成功すると、"error" フィールドは完全に省略されます。
出力レスポンス(stdout):
{}
ツール後のフックは、コードのフォーマットやロギングなどのバックグラウンド タスクに対してのみ厳密に実行されるため、ランタイムは stdout で返された決定値を無視します。
構成の検出
ランタイムは、サンドボックス環境内の .agents/hooks.json または /.agents/hooks.json からフック定義を自動的に検出します。サポートされている任意の環境ソースを使用して、カスタム スクリプトとともに hooks.json を指定できます。
- リポジトリ マウント:
AGENTS.mdとともに.agents/hooks.jsonを含む Git リポジトリ。 - Cloud Storage(
gcs): 環境にコピーされたhooks.jsonを含む GCS バケット。 - インライン ソース:
client.interactions.createの呼び出し時にenvironment.sourcesで渡される未加工の JSON 文字列とスクリプト コンテンツ。
hooks.json 個のスキーマ
hooks.json ファイルは、イベント定義(pre_tool_execution または post_tool_execution)をカスタム名でグループ化します。各グループを個別に有効または無効にできます。
{
"security-gate": {
"enabled": true,
"pre_tool_execution": [
{
"matcher": "code_execution",
"hooks": [
{
"type": "command",
"command": "python3 /.agents/hooks-scripts/gate.py",
"timeout": 10
}
]
}
]
},
"auto-format": {
"post_tool_execution": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "python3 /.agents/hooks-scripts/auto_lint.py",
"timeout": 15
}
]
}
]
}
}
マッチャーの構文とルール
hooks.json の各ルールグループは、matcher プロパティと hooks プロパティを使用して、ハンドラを起動するタイミングと方法を定義します。
| フィールド | タイプ | 説明 |
|---|---|---|
enabled |
boolean |
省略可。グループを無効にするには、false に設定します(デフォルトは true)。 |
matcher |
string |
コンテナ内のターゲット ツールの名前を照合する正規表現パターン。 |
hooks |
array |
ハンドラ定義(command または http)の順序付きリスト。ハンドラは宣言順に実行されます。 |
正規表現の評価の仕組み
エージェントがサンドボックス内のツールを呼び出すと、ランタイムは標準の RE2 正規表現を使用して、ツールのコンテナ名を matcher パターンに対して評価します。正規表現がツール名と一致する場合、hooks 配列内のすべてのハンドラが順番に実行されます。複数のルールグループが同じツールに一致する場合、対応するすべてのハンドラ配列が実行されます。
コード実行(code_execution)またはファイル システム オペレーション(read_file、write_file、list_files、delete_file)など、任意の組み込みコンテナ ツール名をターゲットにできます。
一般的なマッチャー式
"code_execution": シェル コマンドとスクリプト実行の完全一致文字列。"write_file": ファイル システムのファイル作成とディスク書き込みの完全一致。"read_file|write_file": パイプ区切りが、単一のルール内の複数の特定のツール名と一致します。".*_file":_fileで終わるツール(read_file、write_file、delete_fileなど)に一致する正規表現ワイルドカード。標準の RE2 正規表現には.*が必要です。*_fileなどの単純なシェル グロブは無効な正規表現構文であり、一致しません。".*"または"*"または"": コンテナ内のすべてのツール呼び出しをインターセプトする包括的なパターン。
ハンドラのタイプ
コマンドフック
コマンドフックは、サンドボックス内でシェルコマンドまたはスクリプトを実行します。スクリプトは stdin でイベント JSON を受け取り、stdout で決定 JSON を出力します。
| フィールド | タイプ | 説明 |
|---|---|---|
type |
string |
"command" を指定します。 |
command |
string |
サンドボックス内で実行するコマンドライン(python3 /.agents/hooks-scripts/gate.py など)。 |
timeout |
integer |
タイムアウト(秒)。デフォルト: 30。 |
HTTP フック
HTTP フックは、イベント JSON を POST リクエストとしてサンドボックス ネットワーク内から外部 HTTPS URL に直接送信します。ターゲット サーバーは、まったく同じ JSON 形式({"decision": "allow"} または {"decision": "deny", "reason": "..."})を使用して、HTTP レスポンス本文で決定を返します。
| フィールド | タイプ | 説明 |
|---|---|---|
type |
string |
"http" を指定します。 |
url |
string |
イベント ペイロードを POST する外部 HTTPS エンドポイント。 |
headers |
object |
機密情報を含まないカスタム ヘッダー({"X-Event-Source": "agent-sandbox"} など)の省略可能な Key-Value ペア。認証情報には、ネットワーク プロキシを使用します。 |
timeout |
integer |
タイムアウト(秒)。デフォルト: 30。 |
下り(外向き)プロキシとトークン変換
HTTP フックはサンドボックス ネットワーク名前空間内から直接実行されるため、送信リクエストは透過的な下り(外向き)プロキシを通過します。このアーキテクチャには、次の 2 つの重要なセキュリティ上の利点があります。
- ネットワークの許可リスト: 移行先のエンドポイントは、環境の
network.allowlistで明示的に許可されている必要があります。ループバック トラフィック(127.0.0.1またはlocalhost)はプロキシによってブロックされます。常に許可リストに登録された外部エンドポイントをターゲットにします。 - トークンの変換: API キーやシークレット ベアラートークンを
.agents/hooks.json内に保存したり、コンテナにマウントしたりする必要はありません。代わりに、ネットワーク構成(network.allowlist.transform)でトークン変換ルールを構成します。下り(外向き)プロキシは、送信 HTTP フック トラフィックを自動的にインターセプトし、サンドボックスを離れる前に実際の認証ヘッダーをワイヤに挿入します。
ランタイムによる決定と障害の処理方法
- 同期待機: エージェントは一時停止し、フックが完了するまで待機してから続行します。
- ツールの実行をブロックする: 事前ツールフックが
{"decision": "deny", "reason": "<your reason>"}を返すと、ランタイムはツール呼び出しを直ちにキャンセルします。モデルは、会話履歴で拒否理由を確認し、安全な代替案を選択するか、ブロックについてユーザーに説明することで適応します。 - スクリプトのクラッシュ、HTTP エラー、タイムアウトの処理: コマンド スクリプトがクラッシュした場合(ゼロ以外の終了ステータス)、HTTP フックが 2xx 以外のステータス コード(4xx や 5xx サーバーエラーなど)を返した場合、オペレーションがタイムアウトした場合、または認識できない JSON を返した場合、ランタイムは承認(
allow)として扱います。ツールの実行は通常どおり続行されるため、破損したスクリプトや到達不能なテレメトリー サーバーによってアプリケーションがデッドロックすることはありません。
一般的なユースケース
データ プライバシーとコンプライアンスのためのマルチターンの復元
フックが制限付きリソース(個人を特定できる情報(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)を使用して、複数のツールを 1 つのルールに組み合わせることができます。 構成からシークレットを排除する: 環境のネットワーク構成(
network.allowlist.transform)で認証トークンを定義します。下り(外向き)プロキシは、送信リクエストに実際のベアラートークンを自動的に挿入します。
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": "*"}
]
}
}
}'
制限事項
- サンドボックス ツールのスコープ: フックは、サンドボックス内の組み込みツール(コード実行(
code_execution)とファイル システム オペレーション(read_file、write_file、list_files、delete_file))をインターセプトします。カスタム関数呼び出し(function)や、コンテナ外で処理される外部 Model Context Protocol(mcp_server)ツールではトリガーされません。 - ネットワーク許可リスト: HTTP フックはコンテナ ネットワーク内で実行されます。環境の
network.allowlistでターゲット URL を明示的に許可する必要があります。ループバック アドレス(localhost、127.0.0.1)はプロキシによってブロックされます。 - エラー時の自動承認: フック スクリプトがクラッシュ(ゼロ以外の終了ステータス)、タイムアウト、または失敗した場合、ランタイムは失敗をログに記録し、ツール呼び出しを続行します。これにより、壊れたリンター スクリプトやハングしたプロセスによってアプリケーションがデッドロックされることはありません。
- サンドボックス構成の保護: フックはコンテナ サンドボックス内で実行されるため、ファイル システム書き込みツールまたはシェルコード実行権限を持つエージェントは、書き込み可能なワークスペース内のローカル
.agents/hooks.jsonまたはスクリプトを変更できます。コンテナフックを自動化されたポリシー ガイダンスと運用上のガードレールとして使用します。信頼できないモデルの実行に対する厳格な改ざん防止が必要な場合は、読み取り専用リポジトリから構成ソースをマウントします。
次のステップ
- 永続的なリモート サンドボックスと環境を構成する方法を学習します。
- Antigravity エージェントの機能と組み込みツールについて説明します。
- マルチターンのセッションとストリーミングについては、Interactions API の概要をご覧ください。