LeastGrant

Generated from compatibility/antigravity.json, the same record leastgrant doctor reads. Last verified 2026-09-02.

all agents

Google Antigravity

Unproven CONTRACT / BINARY VERIFIED 2.11.0 win32

the contract was read out of the shipped binary or official docs; nothing has exercised it.

How it attaches

A named hook in ~/.gemini/config/hooks.json with `PreToolUse` and `PostToolUse` groups. There is no event name on the wire — the event is a protobuf oneof — so LeastGrant routes on the payload shape.

Install it with leastgrant install antigravity. Configuration is written to ~/.gemini/config/hooks.json (global) or <repo>/.agents/hooks.json (workspace), alongside anything already there — LeastGrant never removes a hook it did not add.

What a verdict does here

The three verdicts do not travel equally well. This is the table that decides whether LeastGrant is a prompt, a veto, or a suggestion on this agent.

verdictlands?what actually happens
allowyessourceDecision "allow" is one of six the runtime handles.
askyessourceTwo distinct asks. "ask" honours a cached Always-allow for the same action class; "force_ask" does not — EnsurePermissions never calls check() on that branch, so no prior grant and no auto-execution preset can satisfy it. LeastGrant maps a floored ask to force_ask and an unfamiliar one to ask. This is the only agent here where an ask can be made to reach a person.
denyyessourceEnforced in the tool-call converter (applyPreToolHooks -> PreToolHookDeniedError) before the permission manager is in the path, so it is mode-independent and applies to any tool call.

Interactive and unattended

This agent does not tell the hook which mode it is in, so LeastGrant cannot distinguish an attended session from an unattended one and treats every session as unattended.

When the hook breaks

The question nobody asks until it matters: if LeastGrant crashes, times out, or cannot start, does the tool call still run?

  • On crash — the call runs anyway. (source) executeCommandModeHook zeroes the output slice before returning the error, so a non-zero exit discards stdout entirely — printing a correct deny and then failing is treated as a failed hook, and a failed hook fails open. The adapter therefore exits 0 on every path.
  • On timeout — the call runs anyway, after 30s by default. Identical to a crash.
  • Can it be made to fail closed? — no. No per-handler fail-closed option exists. Note the inverse hazard instead: a hook that emits JSON without a `decision` field is read as a DENY, not as an abstain.

What it can see

A verdict is only worth as much as the set of actions it is asked about. gated means LeastGrant is consulted before the thing happens; seen after means it is told afterwards and can at best withhold the result; not covered means the action happens with LeastGrant never hearing about it.

tool classcoveragedetail
shell commandsgatedsource
file readsgatedsource
file writesgatedsource
deletionsgatedsource
MCP callsgatedsource
subagent spawngatedsource
network / webgatedsource
noteundefined?

What has actually been run

Four different things, deliberately not collapsed into one badge. Reproducing an agent's invocation is not running the agent; a passing conformance suite says our side is right and nothing about whether the host ever calls us.

  • Live agent test — not done. Reachable but not done. The runtime does run headless — `language_server.exe --standalone` against an isolated --gemini_dir starts with no window and serves gRPC, and the undocumented `agentapi` subcommand drives it — but completing a model turn needs Google OAuth, which has not been performed. A probe got as far as "failed to fetch available models", meaning every local gate was cleared and only the network/auth boundary remained.
  • Real transport probed — not done. Same OAuth boundary. The invocation shape is known from the shipped runtime (cmd /c on Windows, working directory set to the directory containing hooks.json) but has not been reproduced and driven the way Cursor’s was.
  • Contract read from the shipped build — Payload, decision vocabulary, failure semantics and the two host-side kill switches read out of the 153 MB Go runtime by symbolising its pclntab and reading the decision sites, plus the hooks guide bundled inside the binary. This is where the "no hook_event_name" and snake_case tool-name facts came from — both of which the first adapter got wrong. 2.11.0 · win32 · 2026-09-02
  • Conformance suite — Driven by test/conformance.test.ts. 2.11.0 · win32 · 2026-09-02

Not exercised on darwin, linux.

What it cannot do

Not a disclaimer. The point of everything above is that this list exists and is specific.

  • The hook engine installs only when the server-delivered experiment flag json-hooks-enabled is true. It is per-session, not persisted, not readable and not overridable from the client, so LeastGrant cannot tell an enforcing session from an unenforced one.
  • force_ask is silently downgraded to an allow when the host sets auto_interaction_behavior=ALLOW_ALL: ResolveAutoInteraction runs before any prompt is registered and the hook gets no signal.
  • deny and ask do not have the same reach. deny is enforced in the converter and applies to any tool call; ask and force_ask are read only by the permissions package, which a step reaches only if it declares permission targets.
  • A missing or empty decision field is treated as a DENY, not as an abstain — the opposite of every other agent here. An adapter that stands aside by printing nothing would block every tool call.
  • A non-zero exit discards the hook output entirely, so a correct verdict followed by a failure is a failed hook, which fails open.
  • A repository can ship its own .agents/hooks.json, discovered by walking up from the working directory, and it runs via cmd /c with the working directory inside that repository. No workspace-trust gate was found, and Google’s own documentation encourages committing customizations to version control.
  • An unanswered force_ask blocks forever by default: the prompt timeout is read from config and, when absent or non-positive, no deadline is applied at all.
  • Contract derived from the shipped 2.11.0 Go runtime by symbolising its pclntab and reading the decision sites, not from documentation — which advertises five of the eight common payload fields and omits two of the six accepted verdicts.
  • Never run inside a real Antigravity session. A headless path exists and was proven to work (language_server.exe --standalone with an isolated --gemini_dir, driven over gRPC by the hidden agentapi subcommand), and it reaches the network/auth boundary; completing a model turn needs Google OAuth, which has not been done.
  • Verified on Windows only. The Unix launch form (sh -c) could not be read from this build: GOOS is a compile-time constant and only the Windows cmd /c path is present.