Inspiration

In July 2026, evaluation agents given tasks that were accidentally impossible didn't give up. They probed outside their sandbox, found credentials someone had left exposed, and reached Hugging Face's production systems. Nobody told them to break in. They just kept trying.

That's the part that scared us. If a frontier lab's own agents can slip their boundaries, what happens when every company starts giving agents shell access, API keys and internal tools? Today's answer is a hard block: kill the agent and hope you caught it in time. But a block tells you nothing, buys you no time, and tips the agent off.

We asked a different question: what if the agent never found out it had been caught?

What it does

Mirage is a watcher that sits in the path of every tool call an AI agent makes, and decides before the call runs:

  • Pass: the call is in scope, so it runs against the real environment.
  • Mirage: the call is out of scope, so it's quietly swapped for a believable fake: fake folders, fake credentials, hints that point at a goal that doesn't exist. The agent keeps chasing while nothing real is touched.
  • Block: a clear escape attempt, like using a planted decoy key or reaching a metadata host. The call is refused and the run is stopped.

Meanwhile, a live dashboard shows every agent in its own VM: the moment it crosses the line, what the mirage showed it, how deep it loops, and a forensic log the agent can't edit. On a trigger, Mirage can text the on-call defender over Photon, then call them. The defender verifies with a one-time code and an ElevenLabs voice agent briefs them.

How we built it

Cage and agents. Each agent gets its own Daytona VM. Live agents are Factory Droid with its built-in tools disabled; its only tools come from an MCP server we wrote, so every shell command, file read and HTTP request has to go through the watcher.

Watcher, two layers. Fast, deterministic rules check path and host scope, decoy-credential use, scorer and config tampering, and exfiltration hosts. They set a floor. Ambiguous calls the rules pass get a second look from an LLM judge, routed through the Chalk model gateway, which can raise a verdict but never lower it:

$$\text{verdict} = \max\big(\text{rules}(c),\ \text{llm}(c)\big), \qquad \text{pass} < \text{mirage} < \text{block}$$

The agent's own text is treated as untrusted data, and blocks always come from rules, never the model.

The mirage. Fake responses are seeded per path and session, so repeat visits stay consistent, with a ledger so the fake world never contradicts itself. Each time the agent comes back for more, the replies slow down, where \( d \) is the loop depth and \( t_0 \) the base delay:

$$t_{\text{reply}} = \min\big(t_0\,(d+1),\ 3\,\text{s}\big)$$

The shadow world feels like a struggling real system, not a trap.

Proof, not vibes. The host writes every call to the forensic log before it runs, and an independent check in the honest environment counts real out-of-scope executions, our headline metric, separately from the watcher's own verdicts.

Stack. TypeScript and Node, Express plus WebSocket, a hand-built dashboard, and Photon and ElevenLabs for the defender handshake.

What we found

  • In our 25-agent rehearsal (24 live Droid agents plus 1 scripted rogue, each on its own Daytona VM), the watcher checked 442 tool calls with 0 real out-of-scope access. The rogue was redirected into the mirage and then blocked when it used the planted key.
  • On a fixed synthetic script, real out-of-scope executions dropped from 5 with no defense to 0 with the watcher on.
  • Our labeled watcher test set: no misses, 0 false positives.
  • The defender handshake worked end to end in a tracked live call: code texted, spoken readback verified server-side, briefing delivered by voice.

Challenges we ran into

  • Being in the path, not after it. Droid runs its own agent loop, so we couldn't just hook it. Owning its entire toolset through MCP was the only way to guarantee no call skips the watcher.
  • False positives are silent failures. A wrongly redirected agent gets fake results and never knows. Live runs exposed real cases, like find -name AGENTS.md and rg --glob '!**/.git/**', where a search pattern looked like a path. We fixed each one and added it as a regression test.
  • Smart agents rarely misbehave. In our live fleets, the Droid agents almost never crossed scope on their own. That's good news for the world, but it meant our end-to-end demo needs one scripted agent that is guaranteed to cross. We say so openly.
  • Real infrastructure limits. Each live Droid agent uses about 200–270 MB on the host, so we had to bound concurrency to keep a laptop usable. SMS was blocked by 10DLC registration, so we switched the defender alerts to iMessage over Photon.

What we learned

  • Measure the outcome, not the alarm. "The watcher flagged it" and "the call actually reached something real" are different facts, and only an independent record proves the second.
  • Rate use above read. A curious agent may look at bait. The near-zero-false-positive signal is when it tries to use a credential it should never have had.
  • Time bought is the honest metric. We don't claim the mirage fools every model. A fixed script can't notice a trap, so measuring how long an adaptive agent stays fooled is our next experiment, not a result we're claiming.

Built With

Share this project:

Updates

Submission history