Skip to content

feat(cli): add agy (Antigravity CLI) as an agent backend#1430

Open
charliezong18 wants to merge 3 commits into
slopus:mainfrom
charliezong18:feat/agy-agent-backend
Open

feat(cli): add agy (Antigravity CLI) as an agent backend#1430
charliezong18 wants to merge 3 commits into
slopus:mainfrom
charliezong18:feat/agy-agent-backend

Conversation

@charliezong18

@charliezong18 charliezong18 commented Jun 23, 2026

Copy link
Copy Markdown

What & why

Adds agy — Google's Antigravity CLI — as a first-class agent backend, so it can be driven from Happy (terminal + phone/web app) as a fallback when Claude Code hits its usage limit.

Additive: the existing Gemini integration is untouched.

Design

agy has no ACP / JSON streaming mode (unlike Gemini = ACP, Codex = app-server JSON-RPC). Its only stable non-interactive surface is agy --print "<prompt>", which streams plain text and persists a per-cwd conversation for resume. So it can't reuse AcpBackend — it follows the bespoke (non-ACP) openclaw pattern instead: a backend that translates its own output into Happy's AgentMessage stream and rides the same session pipeline, app-selectable without the ACP / MessageAdapter / agentRegistry machinery.

New packages/happy-cli/src/agy/:

  • constants, types, cliArgs (buildAgyArgs), conversationStore
  • AgyBackend — spawns agy --print per turn, streams stdout → AgentMessage
  • runAgy — orchestrator mirroring runOpenClaw
  • ui/ink/AgyDisplay.tsx — terminal status view

Key details:

  • stdin EOF: agy --print blocks until stdin reaches EOF, so AgyBackend spawns with stdio: ['ignore', 'pipe', 'pipe']. An open stdin pipe hangs the turn forever (caught during testing).
  • Permissions: Happy yolo / safe-yolo / bypassPermissions / acceptEdits--dangerously-skip-permissions; otherwise --sandbox. (Print mode is one-shot and can't surface an interactive approval prompt, so non-skip modes defer to agy's own settings.json.)
  • Resume: agy doesn't print the conversation id; it's read back from agy's per-cwd cache and passed via --conversation.
  • Model: Happy's model string → --model "<display name>"; defaults to a Gemini model (the fallback exists because Claude is rate-limited).

Registration

'agy' added everywhere 'openclaw' already appears:

  • happy-cli: detectCLI, index.ts subcommand + help, daemon/run.ts dispatch, AgentId, BackendFlavor, controlServer zod enum, registerCommonHandlers.
  • happy-agent: SupportedAgent, SUPPORTED_AGENTS.
  • happy-app: new-session picker (agentDefaults, persistence, picker + icon, settings labels, storageTypes cliAvailability, ops spawn unions, AgentInput).

Tests

agy/cliArgs.test.ts + agy/AgyBackend.test.ts (10 tests, incl. the stdin-EOF guard). Full happy-cli unit suite passes (691/691); happy-cli, happy-agent, and happy-app all typecheck.

Notes / follow-ups (not done here)

  • The happy-app agy icon is a placeholder (copied from the Gemini icon) — wants a real asset.
  • Retiring the Gemini integration is intentionally out of scope (this PR is additive).
  • A richer integration via agy's embedded agentapi HTTP server (structured tool / permission events) is possible later, but it needs an undocumented running language-server — out of scope.

Visual proof

agy driven end-to-end from the Happy app, not just unit tests: a Happy session running the agy backend — online, answering a prompt and correctly identifying itself as Antigravity (Gemini 3.1 Pro). This shows the backend spawns, streams its output back into Happy's AgentMessage pipeline, and is usable from the app/browser.

Screenshot 2026-06-24 at 11 41 05 PM

Closes #1429
Refs #1313

Charlie Zong and others added 3 commits June 24, 2026 11:10
Add agy — Google's Antigravity CLI — as a first-class agent backend so it
can be used inside Happy as a fallback when Claude Code hits usage limits.

agy has no ACP/JSON streaming mode; its only non-interactive surface is
`agy --print`, which streams plain text and persists a per-cwd conversation
for resume. It therefore follows the bespoke (non-ACP) openclaw pattern
rather than the gemini ACP integration:

- src/agy/: constants, types, cliArgs (buildAgyArgs), conversationStore,
  AgyBackend (spawns `agy --print` per turn, streams stdout -> AgentMessage),
  runAgy (orchestrator mirroring runOpenClaw), plus ui/ink/AgyDisplay.tsx.
- AgyBackend spawns with stdio ['ignore','pipe','pipe']: agy --print blocks
  until stdin hits EOF, so an open stdin pipe hangs the turn forever.
- Permission mapping: yolo/safe-yolo/bypassPermissions/acceptEdits ->
  --dangerously-skip-permissions; otherwise --sandbox.
- Registered 'agy' everywhere 'openclaw' appears: detectCLI, index.ts
  subcommand + help, daemon/run.ts dispatch, AgentId, BackendFlavor,
  controlServer z.enum, registerCommonHandlers; happy-agent SupportedAgent
  + SUPPORTED_AGENTS; happy-app picker (agentDefaults, persistence,
  new-session picker + icon, settings labels, storageTypes cliAvailability,
  ops spawn unions, AgentInput).
- Tests: agy/cliArgs.test.ts + agy/AgyBackend.test.ts (10 tests).

Additive: the existing gemini integration is unchanged.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
The standalone gemini CLI is EOL and agy (Antigravity CLI) is its successor,
so steer users toward `happy agy`. Gemini stays fully functional — this only
adds deprecation signals:

- index.ts: console.warn on `happy gemini` startup pointing to agy; mark the
  help line "[deprecated — use agy]".
- happy-cli/README.md: mark `happy gemini` deprecated and document `happy agy`
  in the agents list, commands table, and requirements.
- happy-app new-session picker: label gemini "gemini (deprecated)".

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Multi-model review (Gemini via agy + DeepSeek) of the agy backend surfaced
three issues, now fixed in AgyBackend.sendPrompt:

- Guard against duplicate terminal handling: Node fires both 'error' and
  'close' on spawn failure, which previously emitted two status:'error'
  messages and rejected twice. A `settled` flag now processes only the first.
- Avoid the this.child overwrite race: only null `this.child` when it still
  points at this child (cleanup() guard), so a late close from a cancelled
  turn can't orphan a newer turn's process.
- Add a Node-side watchdog: `agy --print` is bounded by --print-timeout, but
  if it hangs the turn would never settle; kill the child after the parsed
  timeout + 30s buffer.

Tests: add no-double-emit (error→close) and cancel()-kills-child cases.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@charliezong18 charliezong18 force-pushed the feat/agy-agent-backend branch from 9b91d5e to 5ed0750 Compare June 24, 2026 18:16
@charliezong18

Copy link
Copy Markdown
Author

hey @bra1nDump — thanks again for the quick turnaround on #1440! 🙏 whenever you've got bandwidth, #1430 (agy / Antigravity CLI backend) is also ready for a look. it's bigger (~1.2k lines) but fully additive — the gemini provider is untouched, it just registers agy everywhere openclaw already appears. tests pass, all three packages typecheck, and there's end-to-end visual proof in the description (driven from the app).

i wrote up the design rationale in #1429 (tl;dr: agy has no ACP/JSON mode, so it follows the bespoke openclaw pattern rather than the gemini ACP one). totally open to splitting it up or reworking the approach if the size/shape is a concern — just let me know what'd make it easiest to review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(provider): add Antigravity (agy) CLI as a first-class agent provider

1 participant