feat(opencode-v2): OpenCode 2.x host adapter (reuses the existing core) - #13
feat(opencode-v2): OpenCode 2.x host adapter (reuses the existing core)#13Vladimir-Human wants to merge 5 commits into
Conversation
OpenCode 2.x replaced the plugin surface the 1.x package relies on: there is no
fetch() patch and no TUI sidebar, and providers are declared with native packages
in config. packages/opencode declares engines.opencode '>=1.17.13 <2', so 2.x
users currently have no Antigravity access.
This package is the missing host adapter. All logic still comes from
@cortexkit/antigravity-auth-core: OAuth, transport, account pool, quota and the
model registry are untouched.
- session.hook('http.request') reroutes Antigravity model calls to a loopback
server owned by the plugin, which performs the real request through
fetchWithAgyCliTransport so the agy wire format and proxy support survive
- account selection via getCurrentOrNextForFamily (hybrid), refreshAntigravityToken,
ensureProjectContext, agent envelope from buildAgyAgentRequestMetadata plus
orderAgyRequestPayloadInPlace, endpoint fallback list
- 429/403 cools the account down and rotates, 401 forces a refresh, an
empty-candidate STOP is retried three times
- the native @opencode-ai/ai/providers/google package stays the codec, so image,
PDF and tool-call handling comes from the host
- normalizeGeminiTools with moveNumericConstraintsToDescription for gpt-* models,
which fixes 400 INVALID_ARGUMENT on GPT-OSS tool schemas
- stream frames are normalised for the strict native Gemini event schema
(GPT-OSS opens a turn without parts, Claude may use role assistant)
- antigravity_read_document tool, because the 2.x CLI drops PDF attachments
- generated images are written to the data dir and announced as text
- logs carry account indexes only: no prompts, tokens or e-mail addresses
There was a problem hiding this comment.
1 issue found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/opencode-v2/package.json">
<violation number="1" location="packages/opencode-v2/package.json:30">
P3: This new package ships no `scripts` and is not wired into the root workspace scripts: root `build`/`typecheck`/`test` cover only `packages/core`, `packages/opencode`, and `packages/pi`. As a result nothing builds, lints, or tests the opencode-v2 source in CI, even though the `packages/*` workspace glob pulls it into `bun install`. If this stays in the monorepo, add at least a `lint`/`build`/`test` script and reference it from the root; if it is meant to be a standalone package, that is fine, but the decision should be explicit rather than leaving a workspace member with zero CI coverage.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| @@ -0,0 +1,45 @@ | |||
| { | |||
There was a problem hiding this comment.
P3: This new package ships no scripts and is not wired into the root workspace scripts: root build/typecheck/test cover only packages/core, packages/opencode, and packages/pi. As a result nothing builds, lints, or tests the opencode-v2 source in CI, even though the packages/* workspace glob pulls it into bun install. If this stays in the monorepo, add at least a lint/build/test script and reference it from the root; if it is meant to be a standalone package, that is fine, but the decision should be explicit rather than leaving a workspace member with zero CI coverage.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode-v2/package.json, line 30:
<comment>This new package ships no `scripts` and is not wired into the root workspace scripts: root `build`/`typecheck`/`test` cover only `packages/core`, `packages/opencode`, and `packages/pi`. As a result nothing builds, lints, or tests the opencode-v2 source in CI, even though the `packages/*` workspace glob pulls it into `bun install`. If this stays in the monorepo, add at least a `lint`/`build`/`test` script and reference it from the root; if it is meant to be a standalone package, that is fine, but the decision should be explicit rather than leaving a workspace member with zero CI coverage.</comment>
<file context>
@@ -0,0 +1,45 @@
+ "node": ">=20.0.0",
+ "opencode": ">=2 <3"
+ },
+ "main": "./src/plugin.mjs",
+ "exports": {
+ ".": "./src/plugin.mjs",
</file context>
There was a problem hiding this comment.
Fixed in 0660034: the package ships lint/format:check/typecheck/test scripts plus a smoke test (packages/opencode-v2/test/plugin.test.mjs), and is wired into the root typecheck (node --check) and test chains. bun.lock is regenerated. Note: this package is plain ESM by design — the typecheck step is node --check rather than tsc.
- P1: allow only one forced OAuth refresh per account/request; a repeated 401 now excludes the account and continues pool selection instead of looping - P2: mark request execution complete after a terminal stream, a collected tool response or a non-stream answer, so follow-up tool-result requests keep the core's last_execution_id trajectory metadata - P2: answer non-streaming generateContent calls with a single JSON response (frames are merged) instead of an SSE body the host cannot parse - P2: restrict antigravity_read_document to the user's home directory by default, block well-known credential/secret paths always, and allow narrowing via the readDocumentRoots plugin option; README documents the prompt-injection/exfiltration risk - P2: OAuth callback page only reports that the authorization was received; the final result is reported by OpenCode after exchange + persistence - P2: fix the Install instructions (the adapter package, not just the core) and explain how the plugin path should be set - P3: add the package to the release publish matrix and fix the package-count references in the root README - P3: add lint/format/typecheck/test scripts, a smoke test and wire the package into the root typecheck/test chains; update bun.lock
There was a problem hiding this comment.
1 issue found across 12 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/release.yml">
<violation number="1" location=".github/workflows/release.yml:82">
P2: Adding `@cortexkit/opencode-v2-antigravity-auth` to the publish matrix breaks the release because its version is never synchronized. The workflow's `Sync version` step runs `scripts/version-sync.mjs <tag-version>`, whose `packageJsonPaths` array only covers `packages/{core,opencode,pi}/package.json` and omits `packages/opencode-v2/package.json`. So opencode-v2 always publishes its hardcoded `2.1.0`, which diverges from the release tag and, on the second release, collides with the already-published version and fails the `npm publish` job (which blocks the downstream `github-release`). Add the opencode-v2 path to `version-sync.mjs` alongside this matrix entry.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| workspace: "@cortexkit/opencode-antigravity-auth" | ||
| - package: "@cortexkit/pi-antigravity-auth" | ||
| workspace: "@cortexkit/pi-antigravity-auth" | ||
| - package: "@cortexkit/opencode-v2-antigravity-auth" |
There was a problem hiding this comment.
P2: Adding @cortexkit/opencode-v2-antigravity-auth to the publish matrix breaks the release because its version is never synchronized. The workflow's Sync version step runs scripts/version-sync.mjs <tag-version>, whose packageJsonPaths array only covers packages/{core,opencode,pi}/package.json and omits packages/opencode-v2/package.json. So opencode-v2 always publishes its hardcoded 2.1.0, which diverges from the release tag and, on the second release, collides with the already-published version and fails the npm publish job (which blocks the downstream github-release). Add the opencode-v2 path to version-sync.mjs alongside this matrix entry.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yml, line 82:
<comment>Adding `@cortexkit/opencode-v2-antigravity-auth` to the publish matrix breaks the release because its version is never synchronized. The workflow's `Sync version` step runs `scripts/version-sync.mjs <tag-version>`, whose `packageJsonPaths` array only covers `packages/{core,opencode,pi}/package.json` and omits `packages/opencode-v2/package.json`. So opencode-v2 always publishes its hardcoded `2.1.0`, which diverges from the release tag and, on the second release, collides with the already-published version and fails the `npm publish` job (which blocks the downstream `github-release`). Add the opencode-v2 path to `version-sync.mjs` alongside this matrix entry.</comment>
<file context>
@@ -79,6 +79,8 @@ jobs:
workspace: "@cortexkit/opencode-antigravity-auth"
- package: "@cortexkit/pi-antigravity-auth"
workspace: "@cortexkit/pi-antigravity-auth"
+ - package: "@cortexkit/opencode-v2-antigravity-auth"
+ workspace: "@cortexkit/opencode-v2-antigravity-auth"
steps:
</file context>
There was a problem hiding this comment.
Fixed in b9136d9: scripts/version-sync.mjs now includes packages/opencode-v2/package.json in packageJsonPaths, so release tags bump the adapter version together with core/opencode/pi and the publish matrix cannot collide with a stale hardcoded version.
|
All eight review comments are addressed in 0660034 (push
Verified locally before pushing: |
The path allowlist used path.resolve() but only imported { join } from
node:path, so the document tool would throw a ReferenceError on every call
in a runtime without an ambient path global. Import resolve explicitly.
- P1: enforce the document boundary on the real path too — symlinks and junctions inside an allowed root could bypass the check because readFile follows them; realpath() is resolved and re-checked before reading - P2: add packages/opencode-v2/package.json to scripts/version-sync.mjs so release tags actually bump the adapter version (the publish matrix alone would publish a stale hardcoded 2.1.0 and collide on the second release) - P3: node --check only validates its first argument, so oauth-callback.mjs was never checked — typecheck scripts now run the check per file - P3: package test script uses bun test --isolate (repo convention) via the root composition that the bundled test runner accepts
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
… check A configured root that is itself a symlink or Windows junction would never match the resolved document path, rejecting every file under it. Roots are now canonicalized (realpathSync, lexical fallback) so junction-configured roots stay usable while the boundary is still enforced on the real path.
Title
Add an OpenCode 2.x host adapter (reuses the existing core, no changes to the 1.x package)
Background
OpenCode 2.x (
@opencode-ai/cli@0.0.0-beta-*, binaryopencode2) replaced the plugin surface:fetch()patching; insteadsession.hook("http.request" | "http.response");"package": "@opencode-ai/ai/providers/google";integration.transform, tools throughtool.transform;@cortexkit/opencode-antigravity-authdeclaresengines.opencode: ">=1.17.13 <2"and cannot load on 2.x (neither the TUI sidebar nor thefetchpatch exist there).So 2.x users currently have no Antigravity access. I wrote the host adapter; all logic still
comes from
@cortexkit/antigravity-auth-core@2.1.0— OAuth, transport, account pool, quota andthe model registry are untouched.
Implementation
session.hook("http.request")intercepts Antigravity model calls and rewrites the URL to aloopback server owned by the plugin.
Why a loopback server: 2.x sends whatever
event.requestthe hook leaves behind through itsown HTTP client, and
http.responseonly runs after that request succeeded, so a customResponsecannot be returned from the hook. The loopback keeps the core'sfetchWithAgyCliTransport(agy header order, proxy support) while the host still sees aplain, streamable, cancellable SSE.
getCurrentOrNextForFamily(hybrid) →refreshAntigravityToken→ensureProjectContext→buildAgyAgentRequestMetadata+orderAgyRequestPayloadInPlace→ANTIGRAVITY_ENDPOINT_FALLBACKS.429/403cools the account down viamarkRateLimitedWithReasonand rotates,401forces arefresh, and an empty-candidate
STOPis retried up to three times.@opencode-ai/ai/providers/googlepackage stays the codec, so image, PDF andtool-call handling comes from the host rather than a hand-written adapter.
Four issues found and worked around (may matter for 1.x / pi too)
strings, so
minLength: 1returns400 INVALID_ARGUMENT. Fixed withnormalizeGeminiTools(request, { moveNumericConstraintsToDescription: true })forgpt-*wire models (the flag already exists in core; it just needs to be enabled for that family).
contentand noparts, andClaude sometimes uses role
assistant; 2.x rejects both withInvalid google/gemini stream event. Frames must be normalised before forwarding.content-encoding: gzipto the host makes it inflate twice and fail.inlineDatain the request). I therefore register anantigravity_read_documenttool thatloads the local PDF/image itself and asks a multimodal model. Images pasted into chat work
natively. Generated images are written to disk and returned as a text path, because the 2.x
native parser only renders text and tool calls.
Verified (Windows 11 / Node 24 / OpenCode 0.0.0-beta-17595 / two-account pool)
Gemini 3.7 / 3.6 / 3.5 Flash, Gemini 3.1 Pro, Gemini 3.1 Flash Image,
Claude Sonnet 4.6 Thinking, Claude Opus 4.6 Thinking, GPT-OSS 120B;
tool; image generation written to disk; disabling account
#0failed over to#1.Privacy and credentials
#<account index>, upstream status codes and routing — no prompts, tokensor e-mail addresses.
antigravity-accounts.json(v4 schema + fenced lock),shared with the 1.x plugin and the standalone CLI.
How you may want to take it
packages/opencode-v2(or any name you prefer) and open a PR, includingthe bilingual README, an example config and the MIT licence;
split them into small PRs;
Whatever suits you. Thanks for the core — its transport and account-pool design made this
adapter straightforward.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Adds
@cortexkit/opencode-v2-antigravity-auth, a native OpenCode 2.x adapter that restores Antigravity access by rerouting Google model calls to a loopback that reuses the shared core. 2.x previously had no Antigravity support; 1.x behavior is unchanged. Non‑streamgenerateContentnow returns a merged JSON response, and generated images are saved to disk./models/*:(stream)generateContentviasession.hook("http.request"), forwards to127.0.0.1, unwraps SSE frames, normalizes events for the strict 2.x schema, retries bareSTOPup to 3 times, and merges non‑stream calls into one JSON response.@opencode-ai/ai/providers/googleas the codec; enablesnormalizeGeminiTools(..., { moveNumericConstraintsToDescription: true })for GPT‑OSS tool schemas.retry-after); endpoint fallbacks viaANTIGRAVITY_ENDPOINT_FALLBACKS.antigravity_read_documentfor PDFs/images; by default only files under the user’s home directory are readable, sensitive paths are always denied, the boundary is enforced on the real path (prevents symlink/junction escape), andreadDocumentRootsare canonicalized and can narrow access.antigravity-accounts.json, v4). Callback listens on127.0.0.1:51121/oauth-callback; the callback page only acknowledges receipt.<state dir>/antigravity-v2.logwithout prompts/tokens. Adds example config and tests; includes the package in the publish matrix andscripts/version-sync.mjs; typechecks both plugin and callback withnode --check; tests usebun test --isolate.resolveexplicitly in the path allowlist to avoid a runtimeReferenceError.Rollout
@cortexkit/antigravity-auth-core@2.1.0.opencode.jsonpointing tosrc/plugin.mjs(absolute path ornode_modules/@cortexkit/opencode-v2-antigravity-auth/src/plugin.mjs), and declare models under thegoogleprovider using@opencode-ai/ai/providers/google.googleintegration and select “Google Antigravity (add account)”; allow localhost access and port 51121.ANTIGRAVITY_ACCOUNTS_FILE; setreadDocumentRootsto restrict document access.Written for commit ccd60d9. Summary will update on new commits.