DEV Community

Cover image for Agent Calling Agent Tools (Part 3): Integrating gemini-api CLI & Keyless ADC Skills inside Antigravity CLI (agy)
JimmyLiao
JimmyLiao

Posted on Originally published at memo.jimmyliao.net

Agent Calling Agent Tools (Part 3): Integrating gemini-api CLI & Keyless ADC Skills inside Antigravity CLI (agy)

Verified baseline: 2026-11-14 22:07 (Asia/Taipei, UTC+8), gemini-api 0.4.6, upstream snapshot 2030ff1, supplemented by our live end-to-end orchestration test inside tmux (m1:0.1) on 2026-11-15 documented in Section 7. Full Stage 3 verification logs live in experiments/05-part3-agy-integration/results.md.

πŸ“š Series Navigation

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:

enabled_tools=[BuiltinTools.RUN_COMMAND, BuiltinTools.FINISH],
enable_subagents=False,
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}}}
Enter fullscreen mode Exit fullscreen mode

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

Part 3 Two-Way Architecture Flowchart

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Trigger Boundary: Keep normal coding/reasoning in agy's native pool; route only specialized API artifacts or explicit API checks through the wrapper.
  2. Auth Pre-Flight: Run ./bin/gapi-adc --print-auth-status </dev/null; if any required scope reads MISSING, instruct a human to run ./bin/gapi-adc --reauth. Never treat exit 0 from diagnostics as proof of authorization.
  3. Dry-Run Gate: Preview request body, model, project, and local paths before spending quota or money.
  4. 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:

Part 3 agy AI Reels Director Sequence Diagram

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
Enter fullscreen mode Exit fullscreen mode

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-api binary version; inspect --usage, --help, and --schema before 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 USD total, 44,934 tokens), local ffmpeg 8.1 subtitle burn-in & muxing into playable examples/reels-storyboard-pipeline/output/final-reel.mp4 (9.44s, 720Γ—1280), Part 2 gapi-adc (Exit 3 403 boundary / router Exit 0), and Part 3 stdin/EOF harness checks (Exit 0) were recorded automatically to experiments/06-live-execution-and-cost-log/calls-ledger.jsonl.
  • Schema Lesson Caught & Patched Live: gemini-api generate -o json places generated markdown inside Interactions steps[].content[].text (type == "model_output"), whereas analyze -o json returns top-level text, and async media commands (image, video, music) require a read-only gemini-api agent status <id> ($0) lookup to capture full token usage.

Top comments (0)