Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions packages/cli/src/telemetry/agent_runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,97 @@ describe("detectAgentRuntime — Replit / Hermes / openclaw / Pi", () => {
});
});

describe("detectAgentRuntime — Gemini managed agent", () => {
// Gemini managed agent is detected via filesystem markers (`/.agents/AGENTS.md`)
// and the gVisor kernel string, NOT env vars — so these tests mock node:fs
// and node:os rather than mutating process.env.
beforeEach(() => {
vi.resetModules();
stripVendorEnv();
});

afterEach(() => {
vi.resetModules();
vi.restoreAllMocks();
});

it("reports gemini_managed_agent when /.agents/AGENTS.md exists AND the kernel is gVisor", async () => {
vi.doMock("node:os", async () => {
const actual = await vi.importActual<typeof import("node:os")>("node:os");
return { ...actual, release: () => "4.19.0-gvisor", platform: () => "linux" };
});
vi.doMock("node:fs", async () => {
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
return {
...actual,
existsSync: (path: string) => path === "/.agents/AGENTS.md" || actual.existsSync(path),
};
});
const { detectAgentRuntime } = await import("./agent_runtime.js");
expect(detectAgentRuntime()).toBe("gemini_managed_agent");
});

it("does NOT report gemini_managed_agent when /.agents/AGENTS.md is absent (even on gVisor)", async () => {
// A generic gVisor surface (GKE Sandbox / Cloud Run gen2) that doesn't
// mount the managed-agent layout must fall through to env-var rules.
vi.doMock("node:os", async () => {
const actual = await vi.importActual<typeof import("node:os")>("node:os");
return { ...actual, release: () => "4.19.0-gvisor", platform: () => "linux" };
});
vi.doMock("node:fs", async () => {
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
return {
...actual,
existsSync: () => false,
};
});
const { detectAgentRuntime } = await import("./agent_runtime.js");
expect(detectAgentRuntime()).toBeNull();
});

it("does NOT report gemini_managed_agent when /.agents/AGENTS.md exists but the kernel is not gVisor", async () => {
// A dev box that happens to have a stray /.agents/AGENTS.md must not
// false-positive — the gVisor conjunction is what makes the signal safe.
vi.doMock("node:os", async () => {
const actual = await vi.importActual<typeof import("node:os")>("node:os");
return { ...actual, release: () => "6.8.0-100-generic", platform: () => "linux" };
});
vi.doMock("node:fs", async () => {
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
return {
...actual,
existsSync: (path: string) => path === "/.agents/AGENTS.md" || actual.existsSync(path),
readFileSync: (path: string) =>
path === "/proc/version"
? "Linux version 6.8.0-100-generic (buildd@lcy01)"
: actual.readFileSync(path),
};
});
const { detectAgentRuntime } = await import("./agent_runtime.js");
expect(detectAgentRuntime()).toBeNull();
});

it("returns gemini_managed_agent over an env-var rule when both signals match", async () => {
// If a user happens to set CLAUDECODE=1 inside a Gemini sandbox (or any
// odd config), the filesystem+kernel signal wins — Gemini is more
// specific than a generic env-var marker.
process.env["CLAUDECODE"] = "1";
vi.doMock("node:os", async () => {
const actual = await vi.importActual<typeof import("node:os")>("node:os");
return { ...actual, release: () => "4.19.0-gvisor", platform: () => "linux" };
});
vi.doMock("node:fs", async () => {
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
return {
...actual,
existsSync: (path: string) => path === "/.agents/AGENTS.md" || actual.existsSync(path),
};
});
const { detectAgentRuntime } = await import("./agent_runtime.js");
expect(detectAgentRuntime()).toBe("gemini_managed_agent");
});
});

describe("detectSandboxRuntime — file-system path", () => {
beforeEach(() => {
vi.resetModules();
Expand Down
54 changes: 52 additions & 2 deletions packages/cli/src/telemetry/agent_runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type AgentRuntime =
| "hermes"
| "openclaw"
| "pi"
| "gemini_managed_agent"
| null;

interface VendorRule {
Expand Down Expand Up @@ -144,10 +145,17 @@ export function detectSandboxRuntime(): SandboxRuntime {

/**
* Identify the coding-agent vendor that spawned this process, if any.
* Returns null on a regular interactive shell. Only checks for the
* EXISTENCE of well-known env vars — never reads their values.
* Returns null on a regular interactive shell. Most rules only check the
* EXISTENCE of well-known env vars (never their values), but a few agents
* are best identified by filesystem markers — those run via dedicated
* detector functions ahead of the env-var rule loop.
*/
export function detectAgentRuntime(): AgentRuntime {
// Gemini managed agent — keyed on a filesystem mount (`/.agents/AGENTS.md`)
// with a gVisor guard against false positives. See `isGeminiManagedAgent`
// for the load-bearing-vs-guard split. Env vars alone are insufficient
// (`GEMINI_API_KEY` is user-settable), so this runs ahead of VENDOR_RULES.
if (isGeminiManagedAgent()) return "gemini_managed_agent";
for (const rule of VENDOR_RULES) {
if (rule.check(process.env)) return rule.name;
}
Expand Down Expand Up @@ -216,3 +224,45 @@ function isKVM(): boolean {
return false;
}
}

/**
* Gemini managed-agent sandbox — Google's managed runtime that mounts the
* agent's repo under `/.agents/` (AGENTS.md + skills/ + workspace/) and runs
* inside a gVisor kernel.
*
* Signal hierarchy (the two checks are NOT co-equal):
* - `/.agents/AGENTS.md` is the *uniqueness anchor*: definitionally a
* managed-agent artifact (the platform injects it per-run; its mtime
* tracks the interaction, not the base image). Nothing in the generic
* Google-Cloud-on-gVisor universe (Cloud Run gen2, GKE Sandbox) mounts
* `/.agents/`. This single check carries essentially all uniqueness.
* - `isGVisor()` is a *guard*, not a co-uniqueness signal. gVisor itself
* is shared with GKE Sandbox + Cloud Run gen2 — it does not discriminate
* Antigravity from those. Its job here is to rule out the unlikely case
* of a human creating `/.agents/AGENTS.md` on a non-sandbox host.
*
* Things deliberately NOT keyed on (each fails the uniqueness test —
* shared across the broader Google-Cloud-on-gVisor universe or trivially
* user-settable on any host):
* - gVisor alone
* - `Google Compute Engine` DMI (entire GCP reports this)
* - `job` cgroup (Google-internal but broadly present)
* - egress-proxy env / CA-cert env cluster (any MITM container sets these)
* - `/.google/` base-image overlay
* - `GEMINI_API_KEY` (user-settable on any host)
*
* Source: empirical introspection of live Gemini managed-agent sandboxes by
* gemini-agent (2026-06-09) — stability verified across 3 independent fresh
* sandbox spins (spike + `b9db4e56` + `d59d6361`); uniqueness analysis ruled
* out the corroborating signals above by FS-root + cgroup + netns + DMI +
* PID-1 introspection.
*/
function isGeminiManagedAgent(): boolean {
if (platform() !== "linux") return false;
// The uniqueness anchor: managed-agent platform mount. Nothing else on
// gVisor (Cloud Run, GKE Sandbox, Fly.io) creates this path.
if (!existsSync("/.agents/AGENTS.md")) return false;
// The guard: rule out a stray user-created `/.agents/AGENTS.md` on a
// non-sandbox host. Not a second uniqueness signal — gVisor isn't unique.
return isGVisor();
}
Loading