Verified baseline:
2026-11-14 22:07(Asia/Taipei, UTC+8),gemini-api 0.4.6, upstream snapshot2030ff1, supplemented by our live end-to-end orchestration test insidetmux(m1:0.1) on2026-11-15documented in Section 7. Full Stage 3 verification logs live inexperiments/05-part3-agy-integration/results.md.📚 Series Navigation
- 📘 Part 1 (Dev.to): https://dev.to/jimmyliao/gemini-api-cli-official-guide-part-1-giving-your-ai-agent-a-stateless-multimodal-cli-toolbox-36p3
- 📘 Part 2 (Dev.to): https://dev.to/jimmyliao/gemini-api-cli-official-guide-part-2-keyless-adc-oauth-scope-troubleshooting-user-tier-1nca
- 🇹🇼 Part 3 (Substack, Traditional Chinese): https://memo.jimmyliao.net/p/part-3agent-agent-tool-antigravity
- ▶️ YouTube 16:9 Walkthrough (Part 1): https://youtu.be/Rb5ij419w2Y
- 📝 Part 1 (Substack): https://memo.jimmyliao.net/p/gemini-api-cli-part-1-agent-api
- ⚡ YouTube Shorts (Part 1): https://youtube.com/shorts/82-Z_Jq-ezQ
1. The Question: If I Already Use agy, Why Call gemini-api CLI?
A natural reaction after switching to Antigravity CLI (agy) is: "Why wrap gemini-api inside another agent?" The answer lies in clean interface separation:
- Host Agent (
agy/ Codex / Claude Code): holds multi-file repository context, plans architecture, edits code, and iterates over tasks using your primary agent pool. - Tool Binary (
gemini-api): exposes specialized multimodal primitives (image,video,tts,music,transcribe), explicit GCP project attribution, and machine-discoverable CI contracts over deterministic shell invocations.
As clarified in Parts 1 & 2, the 2026-06-18 consumer transition moved personal Free and Google AI Pro/Ultra gemini-cli usage to Antigravity (official announcement). Under our Golden Hybrid Pattern, day-to-day coding stays on agy's subscription pool (5H rolling quota bounded by weekly caps), while specialized multimodal rendering or project-attributed API calls route explicitly through gemini-api.
2. Source Code Proof: Antigravity SDK Hosts gemini-api in Upstream CI Evals
This dual-layer design is not an ad-hoc hack—it is literally how upstream tests gemini-api-cli:
- In
evals/harness-comparison/README.md, upstream spins up a mock Interactions API server and launches an Antigravity SDK agent that must discover CLI flags purely through shell commands while predicates check HTTP method, path, and JSON bodies. - In
driver/antigravity_driver.py, the agent is configured with:
enabled_tools=[BuiltinTools.RUN_COMMAND, BuiltinTools.FINISH], enable_subagents=False, It strips inherited GEMINI_ / GOOGLE_ overrides so CLI calls hit the mock server without polluting the driver model's real credentials. Meanwhile, src/harnesses.ts pairs --server-url / GOOGLE_GEMINI_BASE_URL with system instructions demanding offline --help discovery and zero interactive blocking.
Agent Self-Discovery Machine Interface
Every clean wrapper/sandbox command runs with credentials scrubbed and stdin guarded:
env -u GEMINI_API_KEY -u GEMINI_ACCESS_TOKEN -u GEMINI_ENVIRONMENT_KEY -u GEMINI_USER_PROJECT GEMINI_NO_KEYRING=true gemini-api --agent-mode generate "hi" --dry-run </dev/null Observed stdout (exit code 0):
{"dry_run":true,"request":{"method":"POST","url":"https://generativelanguage.googleapis.com/v1beta/interactions","headers":{"Accept":["text/event-stream"],"Content-Type":["application/json"],"User-Agent":["google-genai-cli/0.4.6 gl-go/go1.26.10"]},"body":{"input":"hi","model":"gemini-3.8-flash","stream":true}}} Summary of machine-interface surfaces tested on v0.4.6:
| Interface / Command | Observed Output & Role |
|---|---|
--usage | 525-line KDL command tree (exit 0) for rapid subcommand/flag discovery |
generate --schema, agent run --schema | Single-line JSON Schema for request bodies (exit 0) |
-o toon models | 148-line local TOON catalog (exit 0; note local catalog is distinct from live models list) |
--agent-mode generate (missing prompt) | Empty stdout, structured JSON validation_error on stderr (exit 2) |
--agent-mode image (missing prompt) | Structured JSON validation_error (exit 2) |
--agent-mode generate hi --dry-run --stage3-invalid | Structured validation_error via PreparseRenderingFlags (exit 2) |
In internal/output/agentmode.go, shouldRenderStructuredError (L53–58) ensures callers parsing stdout, stderr, and exit codes (0 OK, 1 runtime/API, 2 usage/validation, 3 auth/authorization) receive clean machine diagnostics even on typoed flags.
3. Two-Way Architecture Panorama
Pattern A: Local-to-API
agy invokes bin/gapi-adc via the gemini-api-adc Skill, fetches an ephemeral OAuth token, attaches --user-project, and calls image, video, tts, music, transcribe, or analyze. Keyless OAuth avoids static API keys on disk while still keeping project billing, IAM, and data terms cleanly separated from agy's consumer pool.
Pattern B: API-to-Cloud/Sandbox
Conversely, gemini-api agent run can construct server-side requests targeting Managed Agents:
gemini-api agent run hi --agent antigravity-preview-09-2026 --dry-run </dev/null Combining AntigravityAgentConfig with docs/self-hosted/README.md (environments connect, environments poll, environments serve) and Agent Substrate (ate-gemini) lets remote Managed Agents execute local tools (execute_bash, view_file, write_to_file, replace_file_content, list_dir, grep_search, find_by_name) using short-lived interaction lease tokens so sandboxes never hold project API keys. Keep OS/container/VM sandboxing and end-user ACL checks enforced in your own architecture.
4. Production Trap: Why Every Shim Attaches </dev/null
Per evals/harness-comparison/setup.sh:20-31, Antigravity's command runner opens a stdin pipe even when no input is sent. If a CLI probes stdin and blocks waiting for EOF, the tool call hangs indefinitely. Upstream's shim guards against that:
for arg in "\$@"; do if [[ "\$arg" == "@-" || "\$arg" == *=@- ]]; then exec "$here/bin/generated/gemini-api-real" "\$@" fi done exec "$here/bin/generated/gemini-api-real" "\$@" </dev/null Notice one subtle gap we patched in bin/gapi-adc: upstream checks @- / *=@-, while generate --help also accepts --stdin. So bin/gapi-adc's exec_binary() preserves caller stdin for @-, *=@-, and --stdin, and closes stdin cleanly (</dev/null) for every other call (interactive --reauth bypasses exec_binary()).
5. Encapsulating the gemini-api-adc Antigravity Skill
skills/gemini-api-adc/SKILL.md captures four hard agent guardrails:
- Trigger Boundary: Keep normal coding/reasoning in
agy's native pool; route only specialized API artifacts or explicit API checks through the wrapper. - Auth Pre-Flight: Run
./bin/gapi-adc --print-auth-status </dev/null; if any required scope readsMISSING, instruct a human to run./bin/gapi-adc --reauth. Never treatexit 0from diagnostics as proof of authorization. - Dry-Run Gate: Preview request body, model, project, and local paths before spending quota or money.
- Tier-Aware Routing: Always pass explicit
--user-project:
| Route Tier | GCP Project(s) | Verified & Unverified Boundaries |
|---|---|---|
| Free Lightweight Text | jimmyliao-dev, io26-496910 | Unbilled in audit; online model eligibility requires per-endpoint verification |
| Paid Multimodal | gdg-kc-465418 | Billing enabled (0154CA-AF3D86-D7DE34); earlier ADC blocked at 403 scope |
GDE_Redeem Credits Candidates | gde-jimmyliao-redeem, ai-course-2026, helloworld2026-jimmyliao | Billing enabled (01FE1D-589853-A0397F); gde-jimmyliao-redeem verified live on 2026-11-15 |
| Do Not Route | leapcore-dev | gcloud services list showed 0 enabled services; skip until explicitly verified |
5.5 End-to-End Scenario (Part 3 Perspective): agy as AI Reels Director
In the full Director flow, agy authors storyboard.json and dialogue.txt locally on its Pro/Ultra subscription pool, dry-runs all 8 API calls via ./examples/reels-storyboard-pipeline/run-reels-pipeline.sh --dry-run (preserving stdin only on Step 1 --stdin and forcing </dev/null on Steps 2–6), gates on human approval + --print-auth-status, generates Scene 1 & Scene 2 keyframes first, and only proceeds to paid 720p video after human layout approval:
Runnable Part 3 Example (sample-part3-agent-harness-demo.sh)
Verified offline harness test (examples/part3-agy-skill-director/sample-part3-agent-harness-demo.sh):
bash <<'PART_SAMPLE' ( set +x set -euo pipefail cd examples/part3-agy-skill-director unset GEMINI_API_KEY GEMINI_ACCESS_TOKEN GEMINI_ENVIRONMENT_KEY GEMINI_USER_PROJECT export GEMINI_NO_KEYRING=true W=../../bin/gapi-adc R=../reels-storyboard-pipeline echo 'Step: isolate unexpected stdin pipe noise' >&2 printf 'UNEXPECTED_PIPE_NOISE\n' | "$W" --agent-mode generate 'Reels hook check' --dry-run | python3 -c 'import json,sys; d=json.load(sys.stdin); assert "UNEXPECTED_PIPE_NOISE" not in d["request"]["body"]["input"]; print("Generic stdin guard: PASS")' echo 'Step: preserve brief when --stdin is explicit' >&2 cat "$R/storyboard-brief.txt" | "$W" --agent-mode generate --stream=false --stdin 'Generate 3-scene storyboard:' --dry-run | python3 -c 'import json,sys,pathlib; d=json.load(sys.stdin); brief=pathlib.Path(sys.argv[1]).read_text().strip(); texts=[c.get("text", "") for item in d["request"]["body"]["input"] for c in item["content"]]; assert brief in texts; print("Explicit --stdin guard: PASS")' "$R/storyboard-brief.txt" echo 'Step: offline validation when prompt is missing (note: missing --out does NOT fail locally)' >&2 ERR=$(mktemp) trap 'rm -f "$ERR"' EXIT set +e "$W" --agent-mode image --dry-run </dev/null 2> "$ERR" rc=$? set -e [ "$rc" -eq 2 ] cat "$ERR" >&2 python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); assert d["exit_code"] == 2; assert d["error_type"] == "validation_error"; print("Observed Exit: 2"); print("error_type="+d["error_type"]); print("error_reason="+d["error_reason"]); print("message="+d["message"])' "$ERR" ) PART_SAMPLE 6. Trilogy Summary & Best-Practices Checklist
- [ ] Keep everyday coding on the host agent's native subscription pool; reserve API CLI calls for specialized artifacts.
- [ ] Pin and record the
gemini-apibinary version; inspect--usage,--help, and--schemabefore executing. - [ ] Separate identity, OAuth scope, quota project, data-privacy terms, API enablement, and billing/credits verification.
- [ ] Guard wrapper invocations with
</dev/null(allowing caller stdin only on@-,*=@-, and--stdin). - [ ] Preview every mutating/paid call with
--dry-run, while never mistaking local dry-run success for remote entitlement. - [ ] Parse
stdout,stderr, exit code, and HTTP status independently; never fail silently across billing pools. - [ ] Keep credentials out of logs and prompts, and enforce container/VM sandboxing plus end-user ACLs for self-hosted deployments.
7. Live Verification Addendum (2026-11-15): Part 1–3 End-to-End Orchestration in tmux
In our 2026-11-15 00:20:54 → 00:24:23 Asia/Taipei end-to-end execution via experiments/06-live-execution-and-cost-log/run-tmux-step-by-step.sh inside pane m1:0.1, all Part 1–3 stages ran seamlessly:
- End-to-End Evidence: 8 live Part 1 multimodal calls (
$0.5485 USDtotal,44,934tokens), localffmpeg 8.1subtitle burn-in & muxing into playableexamples/reels-storyboard-pipeline/output/final-reel.mp4(9.44s,720×1280), Part 2gapi-adc(Exit 3403 boundary / routerExit 0), and Part 3 stdin/EOF harness checks (Exit 0) were recorded automatically toexperiments/06-live-execution-and-cost-log/calls-ledger.jsonl. - Schema Lesson Caught & Patched Live:
gemini-api generate -o jsonplaces generated markdown inside Interactionssteps[].content[].text(type == "model_output"), whereasanalyze -o jsonreturns top-leveltext, and async media commands (image,video,music) require a read-onlygemini-api agent status <id>($0) lookup to capture full token usage.



