Skip to content
This repository was archived by the owner on Mar 30, 2026. It is now read-only.

Latest commit

 

History

History
144 lines (112 loc) · 6.4 KB

File metadata and controls

144 lines (112 loc) · 6.4 KB

AGENTS.md

Guidance for AI agents working with this repository.

Overview

OpenCode plugin for Google Antigravity OAuth. Intercepts fetch() calls to generativelanguage.googleapis.com, transforms them to Antigravity format, and handles auth, quota, recovery, and multi-account rotation.

Build & Test Commands

npm install                          # Install dependencies
npm run build                        # Compile (tsc -p tsconfig.build.json)
npm run typecheck                    # Type-check only (tsc --noEmit)
npm test                             # Run all tests (vitest run)
npx vitest run src/plugin/auth.test.ts          # Single test file
npx vitest run -t "test name here"              # Single test by name
npx vitest --watch src/plugin/auth.test.ts      # Watch mode, single file
npm run test:coverage                # Coverage report
npm run test:e2e:models              # E2E: model availability check
npm run test:e2e:regression          # E2E: regression suite

No linter or formatter is configured. Style is enforced by convention (see below).

TypeScript Configuration

  • strict: true with extra strictness: noUncheckedIndexedAccess, noImplicitOverride, noFallthroughCasesInSwitch
  • verbatimModuleSyntax: true — use import type for type-only imports
  • target: ESNext, module: Preserve, moduleResolution: bundler
  • allowImportingTsExtensions: true — use .ts extensions in imports
  • No path aliases — all imports are relative

Code Style

Imports

  • Use import type { ... } for type-only imports (enforced by verbatimModuleSyntax)
  • Named imports only — no default imports in src/
  • Relative paths with .ts extensions: import { foo } from "./bar.ts"
  • Order: node builtins > external packages > local modules

Exports

  • Named exports only in src/ — no default exports
  • Barrel files (index.ts) for module surfaces

Naming

  • camelCase for functions, variables, parameters
  • PascalCase for types, interfaces, classes, enums
  • UPPER_SNAKE_CASE for constants
  • kebab-case for file names (e.g., request-helpers.ts, thinking-recovery.ts)
  • Test files: *.test.ts colocated with source

Types

  • No I prefix on interfaces, no Type suffix
  • Use z.infer<typeof Schema> for Zod-derived types
  • Extract to types.ts when shared, inline when local
  • Discriminated unions preferred over boolean flags
  • Never use as any, @ts-ignore, or @ts-expect-error

Functions

  • export function for public APIs
  • Arrow functions for callbacks, factories, and inline closures
  • Async functions with targeted try/catch (not blanket)

Error Handling

  • Defensive try/catch with graceful degradation (fallback values, not crashes)
  • Custom error classes with metadata when domain-specific
  • Catch unknown, log, and convert to domain errors — never empty catch blocks
  • Rate limit / quota errors trigger account rotation, not failure

Formatting

  • 2-space indentation
  • Double quotes for strings
  • Trailing commas in multiline constructs
  • No semicolons (project convention)

Logging

  • createLogger("module-name") for structured logging
  • console.log only for CLI/user-facing output

Module Structure

src/
├── plugin.ts                # Main entry, fetch interceptor
├── constants.ts             # Endpoints, headers, API config, system prompts
├── antigravity/oauth.ts     # OAuth token exchange
└── plugin/
    ├── auth.ts              # Token validation & refresh
    ├── request.ts           # Request transformation (core logic)
    ├── request-helpers.ts   # Schema cleaning, thinking filters
    ├── thinking-recovery.ts # Turn boundary detection
    ├── recovery.ts          # Session recovery (tool_result_missing)
    ├── quota.ts             # Quota checking (API usage stats)
    ├── cache.ts             # Auth & signature caching
    ├── accounts.ts          # Multi-account management & storage
    ├── storage.ts           # Persistent storage schemas (Zod)
    ├── fingerprint.ts       # Device fingerprint generation & headers
    ├── project.ts           # Managed project context resolution
    └── debug.ts             # Debug logging utilities

Key Design Patterns

1. Request Interception

Plugin intercepts fetch() for generativelanguage.googleapis.com, transforms to Antigravity format. Two header styles: antigravity (Electron-style UA + fingerprint) and gemini-cli (nodejs-client UA).

2. Claude Thinking Blocks

ALL thinking blocks are stripped from outgoing requests for Claude models. Claude generates fresh thinking each turn. This eliminates signature validation errors.

3. Session Recovery

When tool execution is interrupted (ESC/timeout), the plugin injects synthetic tool_result blocks to recover the session without starting over.

4. Schema Sanitization

Tool schemas are cleaned via allowlist. Unsupported fields (const, $ref, $defs) are removed or converted to Antigravity-compatible format.

5. Multi-Account Load Balancing

Accounts rotate on rate limits. Gemini has dual quota pools (Antigravity headers + Gemini CLI headers). Fingerprints are per-account and regenerated on capacity exhaustion.

6. Fingerprint System

Per-account device fingerprints stored in antigravity-accounts.json. Each fingerprint includes deviceId, sessionToken, userAgent, and a reduced clientMetadata (ideType, platform, pluginType — no osVersion, arch, or sqmId). The only header composed is User-Agent, built by buildFingerprintHeaders() in fingerprint.ts and applied on the antigravity request path in request.ts. History tracked (max 5), restorable.

Dependencies

  • zod ^4 — schema validation (NOT zod v3)
  • @opencode-ai/plugin — OpenCode plugin interface
  • @openauthjs/openauth — OAuth client
  • proper-lockfile — file locking for concurrent access
  • xdg-basedir — XDG directory resolution

Testing

  • Framework: Vitest 3 with native ESM
  • Config: vitest.config.ts
  • Tests colocated: src/plugin/foo.test.ts next to src/plugin/foo.ts
  • Use describe/it/expect — standard Vitest API
  • Mock with vi.fn(), vi.spyOn(), vi.mock()

Documentation