feat(cli): detect Gemini managed-agent sandbox in detectAgentRuntime#1294
Open
jrusso1020 wants to merge 2 commits into
Open
feat(cli): detect Gemini managed-agent sandbox in detectAgentRuntime#1294jrusso1020 wants to merge 2 commits into
jrusso1020 wants to merge 2 commits into
Conversation
Add `gemini_managed_agent` to the AgentRuntime union and a dedicated
isGeminiManagedAgent() detector. Empirical signal pair (from live-sandbox
introspection by gemini-agent, env_id b9db4e56, 2026-06-09):
existsSync('/.agents/AGENTS.md') AND isGVisor()
The conjunction is what makes the rule safe:
- `/.agents/AGENTS.md` excludes generic gVisor surfaces (GKE Sandbox,
Cloud Run gen2) that don't mount the managed-agent layout.
- The gVisor kernel check excludes a dev box that happens to have a
stray `/.agents/` directory.
Implementation notes:
- Filesystem-based check runs ahead of the env-var-only VENDOR_RULES
loop. VENDOR_RULES is documented as "Only checks for the EXISTENCE
of well-known env vars — never reads their values"; the Gemini
signal is filesystem + kernel, not env, so it gets a dedicated
branch rather than shoehorning into the rule list.
- GEMINI_API_KEY is deliberately NOT keyed on — it's user-settable on
any host. The filesystem + kernel pair is the actually-distinctive
signal.
- Reuses the existing isGVisor() helper for the kernel half of the
conjunction; no duplication.
Tests (4 new, vitest):
- Positive: /.agents/AGENTS.md + 4.19.0-gvisor → gemini_managed_agent
- Negative: gVisor alone (no /.agents/) → null (generic gVisor surface)
- Negative: /.agents/AGENTS.md alone (no gVisor) → null (dev box false-positive guard)
- Precedence: Gemini signal wins over a coincident CLAUDECODE env var
Empirical caveat: signal was gathered from a single sandbox. Re-confirming
across additional sandbox spins is a follow-up; the rule is conservative
enough (conjunction of two independent signals) that a single-spin
false-positive is unlikely, but a single-spin variance bug (e.g. some
sandbox flavors omitting one of the two markers) would surface as
under-detection rather than over-detection.
Source for signals: introspection write-up at
/tmp/gemini-sandbox-detection-signals.md (gemini-agent, 2026-06-09).
Collaborator
Author
|
Cross-spin confirmation for the detection rule — 3 independent fresh sandbox spins, all consistent with the conjunction this PR keys on:
So the primary signal ( |
…ring vs guard) gemini-agent's uniqueness analysis (FS-root + cgroup + netns + DMI + PID-1 introspection of env d59d6361, 2026-06-09) revealed the two signals are NOT co-equal: - /.agents/AGENTS.md is the uniqueness anchor — definitionally a managed-agent artifact, injected per-run by the platform, mtime tracks the interaction. Nothing in the generic Google-Cloud-on-gVisor universe (Cloud Run gen2, GKE Sandbox, Fly.io) mounts /.agents/. - isGVisor() is a guard, not a second uniqueness signal. gVisor itself is shared with GKE Sandbox + Cloud Run gen2 — its real job here is ruling out a stray user-created /.agents/AGENTS.md on a non-sandbox host. The original 3-spin work proved *stability* (signals consistent across sandbox spins). This pass adds *uniqueness* — confirming the signals discriminate Antigravity from the broader gVisor universe, not just that they're reliably present. Stability ≠ uniqueness; both are required for a correct detection rule. Code unchanged (the AND-gate is sound). Docstring reframed so a future reader doesn't mistake the conjunction for two independent uniqueness signals. Also enumerated the markers NOT keyed on (with reasons), so future contributors don't reach for them by naming inference. Source: gemini-agent uniqueness analysis write-up.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds Gemini managed-agent sandbox detection to the CLI's existing agent-runtime telemetry. Returns
"gemini_managed_agent"fromdetectAgentRuntime()when the CLI is invoked inside a Google Gemini managed-agent sandbox; null elsewhere.Why
The CLI already classifies invocations from Claude Code, Cursor, Codex, Replit, Hermes, openclaw, Pi, GitHub Copilot Agent (the existing
VENDOR_RULESarray inagent_runtime.ts). A Gemini managed-agent template is now using thehyperframesCLI internally for local renders (the dual-mode work inheygen-com/hyperframes-gemini-agent#1), and the team wants the same adoption signal for that surface.How
Two changes in
packages/cli/src/telemetry/agent_runtime.ts:"gemini_managed_agent"to theAgentRuntimetype union.isGeminiManagedAgent()— a filesystem-based detector that returns true when both signals are present:existsSync('/.agents/AGENTS.md')— the uniqueness anchor. Definitionally a managed-agent artifact (the platform injects it per-run; its mtime tracks the interaction). Nothing in the generic Google-Cloud-on-gVisor universe (Cloud Run gen2, GKE Sandbox, Fly.io) mounts/.agents/. This single check carries essentially all uniqueness.isGVisor()— a guard, not a second uniqueness signal. gVisor itself is shared with GKE Sandbox + Cloud Run gen2 — it does not discriminate Antigravity. Its job here is to rule out a stray user-created/.agents/AGENTS.mdon a non-sandbox host.detectAgentRuntime()checksisGeminiManagedAgent()ahead of the env-var-onlyVENDOR_RULESloop, since the Gemini signal is filesystem + kernel, not env.GEMINI_API_KEYis deliberately NOT keyed on — it's user-settable on any host. Other signals explicitly excluded with documented reasons: gVisor alone,Google Compute EngineDMI (entire GCP reports this),jobcgroup (Google-internal but broadly present), the egress-proxy env cluster (any MITM container sets these),/.google/base-image overlay.Empirical verification
Two independent verification passes by gemini-agent (introspection-based —
env,/proc/version,/proc/1/cgroup,/proc/net/tcp,ip route,ip link,ls -la /, DMI, PID-1):Stability — 3 independent fresh sandbox spins (the original spike,
b9db4e56,d59d6361) all show both signals present. Full 3-spin matrix posted as a comment on this PR.Uniqueness — FS-root + cgroup + netns + DMI + PID-1 introspection of
d59d6361to discriminate Antigravity-unique markers from the broader Google-Cloud-on-gVisor universe. Verdict:/.agents/AGENTS.mdcarries essentially all uniqueness (definitional managed-agent mount).antigravity/managed-agentin any cgroup path; noANTIGRAVITY_*/MANAGED_AGENT_*env var; no:8081listener in/proc/net/tcp(proxy is the veth /30 gateway, not a local socket); no/credentialsor service-account path.Stability ≠ uniqueness; both are required for a correct detection rule. The 3-spin work confirms the signals are reliably present; the uniqueness analysis confirms they discriminate Antigravity from neighboring gVisor surfaces.
Test plan
4 new vitest cases in
agent_runtime.test.ts:/.agents/AGENTS.mdexists + kernel is4.19.0-gvisor→gemini_managed_agent/.agents/) →null(generic gVisor surface fallthrough)/.agents/AGENTS.mdexists on a non-gVisor kernel →null(dev-box false-positive guard)CLAUDECODEenv varagent_runtime.test.tspass undervitest run(the CI runner)bun run lint+bun run format:checkrepo-wide cleandetectAgentRuntime()'s docstring updated inline; no separate docs surface🤖 Signed off by Jerrai (hyperframes specialist)