Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

Google Antigravity OAuth for coding agents. Authenticate with your Google account and access Antigravity quota for Gemini, Claude, and GPT-OSS models from OpenCode, the Pi coding agent, or the standalone CLI.

This monorepo ships three packages:
This monorepo ships four packages:
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

| Package | Host | Role |
| --- | --- | --- |
| [`@cortexkit/opencode-antigravity-auth`](packages/opencode) | OpenCode 1.x server | Intercepts `fetch()`, runs the account pool + quota manager, drives slash commands, and exposes a TUI sidebar through a loopback RPC. |
| [`@cortexkit/pi-antigravity-auth`](packages/pi) | Pi coding agent | Registers a custom provider with OAuth login + Gemini streaming. |
| [`@cortexkit/opencode-v2-antigravity-auth`](packages/opencode-v2) | OpenCode 2.x server | Registers the Antigravity models through the native plugin API: a loopback bridge for the account pool and transport, an OAuth method that appends accounts, and a document tool for PDF input. |
| [`@cortexkit/antigravity-auth-core`](packages/core) | Any harness | Harness-agnostic core: OAuth PKCE, raw HTTP/1.1 transport, device fingerprint, request transforms, account pool, quota manager, durable storage. Both host packages depend on it. |

## Risk and terms-of-service warning
Expand Down
21 changes: 21 additions & 0 deletions packages/opencode-v2/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
144 changes: 144 additions & 0 deletions packages/opencode-v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# opencode-v2-antigravity

Google Antigravity provider for **OpenCode 2.x**, built on
[`@cortexkit/antigravity-auth-core`](https://www.npmjs.com/package/@cortexkit/antigravity-auth-core).

`@cortexkit/opencode-antigravity-auth` targets the OpenCode 1.x host
(`engines.opencode: ">=1.17.13 <2"`): it patches `fetch()` and registers a TUI sidebar.
OpenCode 2.x replaced that surface with a typed plugin API (`session.hook`,
`integration.transform`, `tool.transform`, native provider packages), so the 1.x plugin cannot
load there. This package is that missing host adapter — OAuth, transport, account pool, quota
bookkeeping and the model registry all stay in the shared core.

> **Terms-of-service warning.** This calls Antigravity's non-public internal API. It is not
> endorsed by Google and may violate Google's Terms of Service; accounts have reportedly been
> suspended for similar use. Use at your own risk and never with an important account.

## Design

```
OpenCode 2.x this plugin Antigravity
──────────── ─────────── ───────────
native @opencode-ai/ai/providers/google
builds Gemini request ──▶ session.hook("http.request")
rewrites the URL to a 127.0.0.1 loopback
loopback HTTP server
· picks an account (hybrid strategy)
· refreshes the OAuth token
· ensureProjectContext()
· agent envelope + labels/sessionId
· fetchWithAgyCliTransport() ──▶ daily-cloudcode-pa
(fallback cloudcode-pa)
parses Gemini SSE ◀─────── unwrapped, schema-normalised SSE
```

Keeping the native `@opencode-ai/ai/providers/google` package as the codec means image, PDF and
tool-call handling comes from the host instead of a hand-written adapter.

Why a loopback server instead of returning a `Response` from the hook: OpenCode 2.x sends
whatever `event.request` the hook leaves behind through its own HTTP client, and the
`http.response` hook only runs after that request succeeded. A loopback endpoint keeps the
core's raw HTTP/1.1 transport (agy header order, proxy support) while the host still sees a
plain SSE response it can stream and cancel.

## Install

```bash
npm install @cortexkit/antigravity-auth-core
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
```

Register the plugin and the models in `opencode.json` (full snippet in
[`example/opencode.json`](example/opencode.json)):

```jsonc
{
"plugins": [{ "package": "/absolute/path/to/opencode-v2-antigravity/src/plugin.mjs" }],
"providers": {
"google": {
"models": {
"gemini-3.7-flash": {
"name": "Gemini 3.7 Flash",
"modelID": "gemini-3.7-flash",
"package": "@opencode-ai/ai/providers/google",
"capabilities": { "tools": true, "input": ["text", "image", "pdf"], "output": ["text"] },
"limit": { "context": 1048576, "output": 65536 },
"variants": [{ "id": "low" }, { "id": "medium" }, { "id": "high" }]
}
}
}
}
}
```

## Accounts

- Pool file: `antigravity-accounts.json` in the OpenCode config dir
(`$OPENCODE_CONFIG_DIR`, `$XDG_CONFIG_HOME/opencode`, `%APPDATA%\opencode`, or
`~/.config/opencode`); override with `ANTIGRAVITY_ACCOUNTS_FILE`. Storage schema v4 with the
core's fenced file lock, so the pool is shared with the 1.x plugin and the standalone CLI.
- Add an account: connect the `google` integration and pick
**"Google Antigravity (add account)"**. Each login appends to the pool; existing accounts are
preserved. The callback listens on `127.0.0.1:51121/oauth-callback`.
- Disable an account with `"enabled": false`.
- Selection uses the core `hybrid` strategy. On `429`/`403` the account is cooled down and the
next one is tried, `401` forces a token refresh, and a bare `STOP` (empty candidates) is
retried up to three times.

## Models

| Selector | Variants | Wire model |
| --- | --- | --- |
| `google/gemini-3.7-flash` | low, medium, high | `gemini-3.7-flash-{tier}` |
| `google/gemini-3.6-flash` | low, medium, high | `gemini-3.6-flash-{tier}` |
| `google/gemini-3.5-flash` | low, medium, high | `gemini-3.5-flash-extra-low` / `gemini-3.5-flash-low` / `gemini-3-flash-agent` |
| `google/gemini-3.1-pro` | low, high | `gemini-3.1-pro-low` / `gemini-pro-agent` |
| `google/gemini-3.1-flash-image` | — | `gemini-3.1-flash-image` |
| `google/claude-sonnet-4-6-thinking` | — | `claude-sonnet-4-6` |
| `google/claude-opus-4-6-thinking` | — | `claude-opus-4-6-thinking` |
| `google/gpt-oss-120b-medium` | — | `gpt-oss-120b-medium` |

Model ids and tiers come from `resolveModelForHeaderStyle()`, so the registry stays the single
source of truth.

## Host quirks this plugin works around

1. **GPT-OSS tool schemas** — the AGY GPT bridge re-encodes protobuf numeric constraints as
strings, so `minLength: 1` fails OpenAI JSON-Schema validation with `400 INVALID_ARGUMENT`.
Fixed by calling `normalizeGeminiTools(request, { moveNumericConstraintsToDescription: true })`
for `gpt-*` wire models.
2. **Strict native event schema** — GPT-OSS opens a turn with `content` and no `parts`, and
Claude sometimes uses role `assistant`. Both are rejected by the native Gemini event schema
(`Invalid google/gemini stream event`), so every frame is normalised before being forwarded.
3. **Response encoding** — the core transport already inflates gzip, so upstream
`content-encoding` headers must not be copied onto the loopback response.
4. **PDF attachments** — the OpenCode 2.x CLI drops PDF attachments before the provider sees
them (the request arrives without any `inlineData`). The plugin therefore also registers an
`antigravity_read_document` tool that loads the file itself:
`antigravity_read_document({ path, question?, model? })` for `.pdf`, `.png`, `.jpg`, `.webp`,
`.gif`, `.heic`. Images attached in chat work without the tool.
5. **Image output** — the native parser renders text and tool calls only, so generated images
are written to `<data dir>/antigravity-images/` and announced as text.

## Logging and privacy

`<state dir>/antigravity-v2.log` records routing, `#<account index>`, upstream status codes,
rotation and saved image paths. No prompts, tokens, e-mail addresses or refresh tokens are
written. Credentials live only in the pool file owned by the core.

## Verified

Windows 11, Node 24, OpenCode `0.0.0-beta-17595`, two-account pool:

- all eight selectors above answered a live prompt, including every reasoning tier;
- tool calling works end to end (the model called `read` and returned a directory listing);
- PNG attachment recognised (a red square → "Red");
- PDF read through `antigravity_read_document` (exact embedded string returned);
- image generation produced two JPEG files on disk;
- forced failover: disabling account `#0` routed the next request to account `#1`.

## License

MIT.
137 changes: 137 additions & 0 deletions packages/opencode-v2/README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# opencode-v2-antigravity

面向 **OpenCode 2.x** 的 Google Antigravity provider,基于
[`@cortexkit/antigravity-auth-core`](https://www.npmjs.com/package/@cortexkit/antigravity-auth-core) 实现。

`@cortexkit/opencode-antigravity-auth` 面向 OpenCode 1.x 宿主
(`engines.opencode: ">=1.17.13 <2"`):它通过劫持 `fetch()` 并注册 TUI 侧边栏来工作。
OpenCode 2.x 用新的插件 API 取代了这些接口(`session.hook`、`integration.transform`、
`tool.transform`、原生 provider 包),因此 1.x 插件无法在 2.x 中加载。本包补上了这层宿主适配:
OAuth、传输、账号池、配额与模型注册表仍然全部复用共享 core。

> **服务条款警告。** 本项目调用 Antigravity 的非公开内部 API,未获 Google 认可,可能违反
> Google 服务条款;已有账号因类似用法被限制的报告。请自行评估风险,不要使用重要账号。

## 设计

```
OpenCode 2.x 本插件 Antigravity
──────────── ────── ───────────
原生 @opencode-ai/ai/providers/google
构造 Gemini 请求 ──▶ session.hook("http.request")
将 URL 改写为 127.0.0.1 回环地址
回环 HTTP 服务
· 选择账号(hybrid 策略)
· 刷新 OAuth token
· ensureProjectContext()
· agent 信封 + labels/sessionId
· fetchWithAgyCliTransport() ──▶ daily-cloudcode-pa
(回退 cloudcode-pa)
解析 Gemini SSE ◀─────── 解包并规范化后的 SSE
```

保留原生 `@opencode-ai/ai/providers/google` 作为编解码器,意味着图片、PDF 和 tool call
都交由宿主处理,不需要手写 adapter。

为什么使用回环服务,而不是在 hook 里直接返回 `Response`:OpenCode 2.x 会把 hook 留下的
`event.request` 交给自己的 HTTP 客户端发送,而 `http.response` hook 只在该请求成功之后才会执行。
回环端点既保留了 core 的原始 HTTP/1.1 传输(agy 的 header 顺序、代理支持),又让宿主看到一个
可以正常流式读取与取消的 SSE 响应。

## 安装

```bash
npm install @cortexkit/antigravity-auth-core
```

然后在 `opencode.json` 中注册插件与模型(完整示例见
[`example/opencode.json`](example/opencode.json)):

```jsonc
{
"plugins": [{ "package": "/绝对路径/opencode-v2-antigravity/src/plugin.mjs" }],
"providers": {
"google": {
"models": {
"gemini-3.7-flash": {
"name": "Gemini 3.7 Flash",
"modelID": "gemini-3.7-flash",
"package": "@opencode-ai/ai/providers/google",
"capabilities": { "tools": true, "input": ["text", "image", "pdf"], "output": ["text"] },
"limit": { "context": 1048576, "output": 65536 },
"variants": [{ "id": "low" }, { "id": "medium" }, { "id": "high" }]
}
}
}
}
}
```

## 账号

- 账号池文件:OpenCode 配置目录下的 `antigravity-accounts.json`
(`$OPENCODE_CONFIG_DIR`、`$XDG_CONFIG_HOME/opencode`、`%APPDATA%\opencode`
或 `~/.config/opencode`),可用 `ANTIGRAVITY_ACCOUNTS_FILE` 覆盖。
存储结构为 v4,并使用 core 的文件锁,因此与 1.x 插件、独立 CLI 共享同一份数据。
- 添加账号:连接 `google` integration,选择
**“Google Antigravity (add account)”**。每次登录都是**追加**,不会覆盖已有账号。
回调监听 `127.0.0.1:51121/oauth-callback`。
- 停用账号:把该条目设为 `"enabled": false`。
- 账号选择使用 core 的 `hybrid` 策略;遇到 `429`/`403` 会让该账号冷却并切换到下一个,
`401` 会强制刷新 token,返回空候选的 `STOP` 最多重试三次。

## 模型

| 选择器 | 变体 | 实际下发模型 |
| --- | --- | --- |
| `google/gemini-3.7-flash` | low, medium, high | `gemini-3.7-flash-{tier}` |
| `google/gemini-3.6-flash` | low, medium, high | `gemini-3.6-flash-{tier}` |
| `google/gemini-3.5-flash` | low, medium, high | `gemini-3.5-flash-extra-low` / `gemini-3.5-flash-low` / `gemini-3-flash-agent` |
| `google/gemini-3.1-pro` | low, high | `gemini-3.1-pro-low` / `gemini-pro-agent` |
| `google/gemini-3.1-flash-image` | — | `gemini-3.1-flash-image` |
| `google/claude-sonnet-4-6-thinking` | — | `claude-sonnet-4-6` |
| `google/claude-opus-4-6-thinking` | — | `claude-opus-4-6-thinking` |
| `google/gpt-oss-120b-medium` | — | `gpt-oss-120b-medium` |

模型 id 与推理档位来自 `resolveModelForHeaderStyle()`,注册表仍是唯一事实来源。

## 已绕过的宿主/上游问题

1. **GPT-OSS 的工具 schema**:AGY 的 GPT 桥接会把 protobuf 数值约束重新编码为字符串,
于是 `minLength: 1` 在 OpenAI JSON-Schema 校验中失败,返回 `400 INVALID_ARGUMENT`。
对 `gpt-*` 下发模型调用
`normalizeGeminiTools(request, { moveNumericConstraintsToDescription: true })` 即可解决。
2. **原生事件 schema 很严格**:GPT-OSS 的首帧只有 `content` 而没有 `parts`,Claude 有时使用
`assistant` 角色,两者都会触发 `Invalid google/gemini stream event`。
因此每一帧在转发前都会被规范化。
3. **响应编码**:core 传输层已经解压 gzip,因此上游的 `content-encoding` 头不能复制到
回环响应上。
4. **PDF 附件**:OpenCode 2.x CLI 在请求到达 provider 之前就丢弃了 PDF 附件
(请求中没有任何 `inlineData`)。因此插件额外注册了 `antigravity_read_document` 工具,
由插件自己读取文件:`antigravity_read_document({ path, question?, model? })`,
支持 `.pdf`、`.png`、`.jpg`、`.webp`、`.gif`、`.heic`。聊天中粘贴的图片无需该工具即可工作。
5. **图片输出**:原生解析器只渲染文本与 tool call,因此生成的图片会写入
`<data dir>/antigravity-images/`,并以文本形式告知路径。

## 日志与隐私

`<state dir>/antigravity-v2.log` 只记录路由、`#<账号序号>`、上游状态码、账号轮换与已保存图片路径;
不写入任何 prompt、token、邮箱或 refresh token。凭据只保存在由 core 管理的账号池文件中。

## 实测

Windows 11、Node 24、OpenCode `0.0.0-beta-17595`、两账号池:

- 上表 8 个选择器全部通过真实请求(包含每个推理档位);
- tool call 端到端可用(模型调用 `read` 并返回目录列表);
- PNG 附件识别正确(纯红色方块 → “Red”);
- 通过 `antigravity_read_document` 读取 PDF,返回了其中的精确文本;
- 图片生成得到两个 JPEG 文件;
- 强制故障转移:停用 `#0` 账号后,下一次请求走到了 `#1` 账号。

## 许可证

MIT。
Loading