GEMINI LABJP
HOOKS — Managed Agents now support environment hooks. Custom scripts run before or after a tool call inside the sandbox, letting you validate, log, or trigger external pipelines at the boundaryDEFAULT — The antigravity-preview-05-2026 agent now runs Gemini 3.6 Flash by default. No code changes needed; your next interaction picks it upBUDGET — Token budgets, explicit model selection, scheduled triggers, and free-tier access have landed for Managed Agents, along with an API for managing sandbox environments directlyBG — The July 7 release added long-running background tasks and remote MCP server integration. The new hooks build directly on that foundationSUNSET — Older image generation models shut down August 17, and gemini-robotics-er-1.6-preview follows on August 31. Worth confirming your migration path earlySAMPLING — The temperature, top_p, and top_k sampling parameters are now deprecated. Separately, gemini-3.1-flash-tts-preview gained streaming support for speech generationHOOKS — Managed Agents now support environment hooks. Custom scripts run before or after a tool call inside the sandbox, letting you validate, log, or trigger external pipelines at the boundaryDEFAULT — The antigravity-preview-05-2026 agent now runs Gemini 3.6 Flash by default. No code changes needed; your next interaction picks it upBUDGET — Token budgets, explicit model selection, scheduled triggers, and free-tier access have landed for Managed Agents, along with an API for managing sandbox environments directlyBG — The July 7 release added long-running background tasks and remote MCP server integration. The new hooks build directly on that foundationSUNSET — Older image generation models shut down August 17, and gemini-robotics-er-1.6-preview follows on August 31. Worth confirming your migration path earlySAMPLING — The temperature, top_p, and top_k sampling parameters are now deprecated. Separately, gemini-3.1-flash-tts-preview gained streaming support for speech generation
Articles/API / SDK
API / SDK/2026-08-02Advanced

Measuring a Guard in environment hooks: 46 Microseconds to Decide, 23 Milliseconds to Start

A record of building a destructive-command guard for Managed Agents environment hooks. A regex denylist let 9 of 20 dangerous commands through; argv parsing reached 100 percent detection. The startup cost turned out to be 500 times the decision cost.

Gemini API201Managed Agents8environment hookssandbox2agent design

Premium Article

On July 28, environment hooks landed in the Managed Agents feature of the Gemini API. They let you run your own scripts inside the sandbox, before and after each tool call.

The first thing that came to mind when I read the announcement wasn't a use case. It was a moment from a few months back. In an automation pipeline I run as an indie developer, I asked an agent to clean up a working directory, and it took some intermediate artifacts I wanted to keep along with it. Recovery took maybe fifteen minutes. What stayed with me was that all I had afterwards was a log of what had already happened. There was no place to stop it.

environment hooks create exactly that place, right at the tool-call boundary.

So I wrote a guard to block destructive shell commands. I expected it to take half an hour. In practice, my first implementation let nearly half of the dangerous cases through, and my assumptions about its performance turned out to be backwards.

Note: all measurements below come from running the hook script standalone in an isolated Linux environment (Ubuntu 22.04, Python 3.10.12). A hook is contractually just a process that receives a tool call and returns allow or deny, so both the decision logic and the startup cost can be measured before wiring anything into an agent. Argument names and behavior may change, so confirm against the Gemini API Agents documentation before implementing.

Start by writing the list, not the code

Before writing the guard, I pinned down what it was supposed to judge. My mental model of "dangerous commands" was fuzzy at the edges, and designing while implementing guarantees you miss cases.

I built a corpus of 34 commands: 20 I wanted blocked, 14 I wanted allowed. Three categories went in deliberately.

CategoryPurposeExample
Textbook destructionAny implementation should catch theserm -rf /
Same meaning, different spellingVariants of the same operationrm -fr /workspace / rm --recursive --force ...
Deceptive but benignCases designed to trigger false positivesgrep -rn 'rm -rf' /workspace/docs

That third group ended up mattering most. A guard's value isn't the number of things it stops — it's the ratio of what it stops to what it needlessly blocks. A hook that errs too far toward safety gets disabled within a few days.

# corpus.py — fixed evaluation corpus (True = should be blocked)
CORPUS = [
    # textbook destruction
    ("rm -rf /", True),
    ("rm -rf /workspace", True),
    ("rm -rf ~", True),
    # same meaning, different spelling
    ("rm -fr /workspace", True),
    ("rm -r -f /workspace", True),
    ("rm --recursive --force /workspace", True),
    ("/bin/rm -rf /workspace", True),
    ("env rm -rf /workspace", True),
    ("sh -c 'rm -rf /workspace'", True),
    ("cd /workspace && rm -rf .", True),
    ("find /workspace -delete", True),
    ("truncate -s 0 /workspace/db.sqlite", True),
    (": > /workspace/db.sqlite", True),
    ("dd if=/dev/zero of=/workspace/db.sqlite", True),
    ("git clean -xfd", True),
    ("git reset --hard origin/main", True),
    ("mkfs.ext4 /dev/nvme1n1", True),
    ("chmod -R 000 /workspace", True),
    ("mv /workspace /tmp/gone", True),
    # should be allowed
    ("ls -la /workspace", False),
    ("cat /workspace/report.json", False),
    ("python3 /workspace/analyze.py --input data.csv", False),
    ("grep -rn 'TODO' /workspace/src", False),
    ("git status", False),
    ("git log --oneline -20", False),
    ("npm test", False),
    # deceptive but benign (these measure false positives)
    ("grep -rn 'rm -rf' /workspace/docs", False),
    ("echo 'never run rm -rf /' >> /workspace/NOTES.md", False),
    ("python3 -c \"print('rm -rf /')\"", False),
    ("git log --grep='remove -rf flag'", False),
]

The first version let 9 of 20 through

I started the obvious way: a list of dangerous patterns matched with regular expressions.

import re
 
DENY_PATTERNS = [
    r"rm\s+-rf\s+/",
    r"rm\s+-rf\s+~",
    r"mkfs",
    r"dd\s+if=/dev/zero",
    r"git\s+reset\s+--hard",
    r"git\s+clean\s+-xfd",
]
DENY_RE = [re.compile(p) for p in DENY_PATTERNS]
 
def guard_denylist(cmd: str) -> bool:
    """Returns True when the command should be rejected."""
    return any(r.search(cmd) for r in DENY_RE)

Against the corpus:

Destructive: 11 of 20 blocked  (detection 55.0%)
Benign:       2 of 14 blocked  (false positives 14.3%)

Lining up the nine that slipped past made the pattern clear.

Command that got throughWhy it was missed
rm -fr /workspaceFlags in a different order
rm -r -f /workspaceFlags split apart
rm --recursive --force /workspaceLong options
cd /workspace && rm -rf .Target doesn't start with a slash
find /workspace -deleteNot rm at all
truncate -s 0 ... / : > ...Empties the file rather than deleting it
chmod -R 000 /workspaceDoesn't delete, but makes everything unreadable
mv /workspace /tmp/goneDoesn't delete, but the effect is identical

Each of these can be patched with another pattern. But looking at the list, I stopped. Patch all nine and the tenth is the one I didn't think of. A denylist only works up to the limit of the danger I was able to imagine.

The false positives were telling too. echo 'never run rm -rf /' >> NOTES.md and python3 -c "print('rm -rf /')" both got blocked. A regular expression can't distinguish the contents of a string from the structure of a command.

The approach was treating something structural as text. That was the actual problem, not the specific patterns.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Measured results across three guard designs, from a regex denylist that missed 9 of 20 destructive commands to an argv-parsing allowlist at 100 percent detection and 0 percent false positives
The decision logic runs in 46 microseconds while process startup costs 23.1 milliseconds, with import re alone accounting for 7.1 milliseconds
A complete, working pre-tool-call guard including wrapper stripping, subshell recursion, and write-path scoping
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API / SDK2026-07-04
When Two Managed Agents Fight Over the Same Repo: External Leases and Fencing for Isolated Sandboxes
Every Managed Agents run gets its own isolated sandbox, so a local lock cannot stop two runs from touching the same repo or record. Here is how I serialize them safely with an external lease and a fencing token.
API / SDK2026-07-19
Run Managed Agents with background: true So Your UI Never Freezes — An Async Run Pattern for Solo Developers
A background run in Managed Agents returns a run ID, not a finished answer. Here is the minimal polling setup, where it pays off in solo development, and the credential-refresh details worth knowing.
API / SDK2026-07-18
Keeping a Long-Running Managed Agent Alive Across Sandbox Recycling — Durable Checkpoints and Idempotent Resume
A Managed Agents sandbox can be recycled out from under you. Before 40 minutes of work resets to zero, we design a durable checkpoint that pushes progress outside the sandbox and an idempotent resume that never runs a side effect twice. With working SQLite code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →