Skip to content

Latest commit

 

History

History
465 lines (384 loc) · 59.8 KB

File metadata and controls

465 lines (384 loc) · 59.8 KB

Codebase Structure

This document is the file-system map of the @cortexkit/antigravity-auth* monorepo at the v2.0 parity refactor. It is written from the tracked source tree — every listed file is the actual path shipped on main (c362269); directories such as dist/, node_modules/, packages/opencode/src/tui-compiled/, and per-agent working roots are tracked as .gitignored but called out where relevant.

The stack has one business purpose (talk to the Google Antigravity agy CLI from non-Google harnesses) and three runtime surfaces (OpenCode server plugin, OpenTUI sidebar, Pi extension). These paragraphs are the only thing you have to read to navigate the tree:

  1. Account, rotation, quota, transport, OAuth, and persistence live in packages/core. That package has zero host-runtime imports — it only knows fetch, node:fs, node:net, and a fetchAccountQuota callback — and it owns the durable account-storage.ts engine and the account-manager.ts selection/rotation logic. The OpenCode side owns a thin adapter that wraps it (path resolution + .gitignore sync) and a fetch interceptor that drives it.
  2. packages/opencode/src/plugin/index.ts is the only composition root the host calls. Its default factories are aliased from packages/opencode/index.ts as {AntigravityCLIOAuthPlugin, GoogleOAuthPlugin}. Everything else under packages/opencode/src/plugin/ is a subsystem wired by that root.
  3. packages/opencode/src/tui.tsx and the packages/opencode/src/tui-compiled/ twin are the OpenTUI sidebar. They are read-only consumers of the on-disk sidebar-state.json and the loopback HTTP RPC. They never import from packages/opencode/src/plugin/storage.ts, accounts.ts, or any OAuth code; that boundary is enforced by the import graph documented in ARCHITECTURE.md.

Repository tree

antigravity-auth/
├── AGENTS.MD                                # operator-facing agent manual
├── ARCHITECTURE.md                          # system-of-record architecture doc
├── STRUCTURE.md                             # this file — file-system map
├── README.md                                # project landing page
├── biome.json                               # Biome formatter + linter config
├── bunfig.toml                              # Bun test preload (test/setup.ts + OpenTUI preload)
├── bun.lock                                 # Bun lockfile (committed)
├── lefthook.yml                             # pre-commit Biome check
├── mise.toml                                # Bun 1.3 + Node 24 toolchain pin
├── tsconfig.scripts.json                    # tsconfig for scripts/, test/, e2e-tests
├── package.json                             # root monorepo (npm workspaces: packages/*)
├── models                                   # JSON snapshot of upstream Antigravity model list
├── .github/                                 # GitHub config: workflows, issue templates, FUNDING
├── scripts/                                 # root build/dev/release/version-sync tooling
├── test/                                    # repo-root test preload + cross-package sanity tests
├── test-fixtures/                           # frozen Antigravity CLI JSON fixtures used by tests
└── packages/
    ├── core/                                # @cortexkit/antigravity-auth-core — harness-agnostic
    ├── opencode/                            # @cortexkit/opencode-antigravity-auth — server + tui
    ├── pi/                                  # @cortexkit/pi-antigravity-auth — Pi extension
    └── e2e-tests/                           # private deterministic e2e workspace

The models blob at the repo root is a tracked JSON snapshot of the upstream Antigravity model inventory; the e2e tests assert it stays in sync with the metadata fixtures. Treat the packages/opencode/src/tui-compiled/ directory as a build artifact: it is gitignored and regenerated by packages/opencode/scripts/build-tui.ts. Files are listed under Generated and ignored artifacts below.

Workspace responsibilities

Workspace Runtime responsibility Imports from
packages/core All harness-agnostic logic: OAuth PKCE, transport, account pool, rotation, quota, persistence. Node built-ins only.
packages/opencode OpenCode server plugin (fetch interceptor, account runtime, slash commands, RPC) and the OpenTUI sidebar. @cortexkit/antigravity-auth-core, @opencode-ai/plugin, @opentui/*, solid-js.
packages/pi Pi extension — a custom OAuth provider that bridges Pi's pi.registerProvider API to the core transport + transforms. @cortexkit/antigravity-auth-core, @earendil-works/pi-* (peer).
packages/e2e-tests Private black-box flows against a mock Antigravity loopback server (@cortexkit/antigravity-auth-e2e, private: true). packages/opencode, packages/core.
scripts/ Workspace-level tooling: dev symlink lifecycle, version sync, release script. Node built-ins + child process.
test/ Preload + global types + a small handful of repo-root sanity tests (environment.test.ts, dev.test.ts). Bun test harness.
test-fixtures/ Frozen JSON snapshots of captured agy CLI metadata and streaming requests, through version 1.1.6. None.
.github/ Workflows (CI, release, issue triage), issue templates, FUNDING. GitHub Actions.

packages/core inventory

The harness-agnostic core. Every export under packages/core/src/index.ts:1-30 is grouped by concern; the test files live next to the production code (*.test.ts co-location). The package ships to npm as @cortexkit/antigravity-auth-core@2.0.0 and is the only dependency packages/opencode and packages/pi are allowed to take against internal code.

  • packages/core/bunfig.tomlpreload = ["../../test/setup.ts"]. Test isolation root.
  • packages/core/package.json — declares main/exports on ./dist/, lists peer deps (@openauthjs/openauth, xdg-basedir, zod).
  • packages/core/tsconfig.json — library tsconfig (extends @tsconfig/bun).
  • packages/core/tsconfig.build.json — emit + declaration tsconfig used by bun run build (tsc -p tsconfig.build.json).
  • packages/core/LICENSE, packages/core/README.md — MIT + repo readme.

Public surface (packages/core/src/index.ts:1-30)

  • packages/core/src/account-manager.tsAccountManager: per-account selection, rate-limit state, fingerprint, soft-quota, health score. Hosts call getCurrentOrNextForFamily, markAccountCoolingDown, markRateLimitedWithReason.
  • packages/core/src/account-storage.ts — durable JSON shape AccountStorageV4 with accounts[], activeIndex, activeIndexByFamily; migration v1→v4; lock-held read-modify-write via the fenced file lock.
  • packages/core/src/account-types.ts — pure types (ManagedAccount, AccountSessionIdentity, RateLimitReason, CooldownReason, AccountStorageV4).
  • packages/core/src/agy-request-metadata.tsbuildAgyRequestMetadata (the labels block — last_step_index, model_enum, trajectory_id, used_claude*, used_non_gemini_model) and the per-workspace AgyRequestSessionStore registry.
  • packages/core/src/agy-transport.ts — bounded TLS socket pool, chunked + gzip body decode, idle-timeout watchdog. Hosts pass a connectTlsWithAbort factory through the dependency seam.
  • packages/core/src/antigravity/oauth.tsauthorizeAntigravity, exchangeAntigravity, refreshAntigravityToken. Owns PKCE pack/unpack, the 51121 callback URL constant, and the client metadata header.
  • packages/core/src/auth.tsparseRefreshParts, formatRefreshParts, accessTokenExpired (60s buffer), refresh-token validity.
  • packages/core/src/auth-types.ts — packed/refresh token wire types.
  • packages/core/src/constants.ts — endpoints (ANTIGRAVITY_ENDPOINT_FALLBACKS = [DAILY, PROD]), scopes, request headers, sentinel values (SKIP_THOUGHT_SIGNATURE), CLAUDE_TOOL_SYSTEM_INSTRUCTION, OAuth prompt copy.
  • packages/core/src/fetch-timeout.tsfetchWithActiveTimeout (15s header abort, stream-safe: the abort listener is removed once the body streams).
  • packages/core/src/file-lock.ts — renovating fenced file lock (acquireFencedFileLock + releaseFencedFileLock), eviction marker with pid / createdAt (MARKER_TTL_MS = 30_000) for stale-claim reclamation, assertOwned ownership check, whenLost() / hasLost() ownership-loss signals, idempotent terminal release, and TOCTOU-safe renewal that stages a temp file before rename(2).
  • packages/core/src/fingerprint.ts — per-account device fingerprint + fingerprint history persistence helpers.
  • packages/core/src/logger.tscreateLogger('module-name') thin wrapper used by every other module.
  • packages/core/src/model-registry.ts — canonical model definitions, quota groups, header-style per-model metadata (mirrored to packages/opencode/src/plugin/model-registry.ts which adds harness-specific overrides).
  • packages/core/src/model-types.tsQuotaGroupSummary, family enum, header style enum, model specification types.
  • packages/core/src/project.tsloadCodeAssist, ensureProjectContext. Provisions the Antigravity projectId for an account.
  • packages/core/src/quota-manager.tsQuotaManager: per-account quota cache, exponential backoff, in-flight dedupe (refreshAccount returns the existing promise), disposal honours AbortSignal.
  • packages/core/src/quota-types.ts — quota result shapes (AccountQuotaResult).
  • packages/core/src/rotation.tsHealthScoreTracker, TokenBucketTracker, selectHybridAccount (the hybrid scoring formula used by account-manager.getCurrentOrNextForFamily), calculateBackoffMs, computeSoftQuotaCacheTtlMs.
  • packages/core/src/atomic-write.ts — temp-file + rename writer (mode 0o600, no copy fallback); raw node:fs only.
  • packages/core/src/version.ts — exported VERSION constant; the build pipeline uses it for cache busting.

Transforms (packages/core/src/transform/)

  • packages/core/src/transform/index.ts — barrel re-export of all transforms and types.
  • packages/core/src/transform/types.tsTransformContext, TransformResult, ModelFamily, ThinkingTier, ResolvedModel, GoogleSearchConfig, ThinkingConfig, TransformDebugInfo.
  • packages/core/src/transform/claude.tsapplyClaudeTransforms, appendClaudeThinkingHint, buildClaudeThinkingConfig, computeClaudeMaxOutputTokens, ensureClaudeMaxOutputTokens, isClaudeModel, isClaudeThinkingModel, normalizeClaudeTools, configureClaudeToolConfig.
  • packages/core/src/transform/gemini.tsapplyGeminiTransforms, buildGemini3ThinkingConfig, buildGemini25ThinkingConfig, buildImageGenerationConfig, isGemini3Model, isGemini25Model, isGeminiModel, isImageGenerationModel, normalizeGeminiTools, toGeminiSchema.
  • packages/core/src/transform/cross-model-sanitizer.ts — payload cleanup before family switch (sanitizeCrossModelPayload, sanitizeCrossModelPayloadInPlace, stripClaudeThinkingFields, stripGeminiThinkingMetadata, getModelFamily).
  • packages/core/src/transform/model-resolver.tsresolveModelWithTier, resolveModelWithVariant, resolveModelForHeaderStyle, MODEL_ALIASES, GEMINI_3_THINKING_LEVELS, THINKING_TIER_BUDGETS.

Co-located tests

Every production file has a corresponding *.test.ts sibling. The dense transform families (Claude, Gemini, cross-model sanitizer, model resolver) and the storage/rotation files each have a focused test file. Standalone tests: account-manager.test.ts, account-storage.test.ts, agy-request-metadata.test.ts, agy-transport.test.ts, antigravity/oauth.test.ts, atomic-write.test.ts, fetch-timeout.test.ts, file-lock.test.ts, fingerprint.test.ts, model-registry.test.ts, project.test.ts, quota-manager.test.ts, rotation.test.ts, plus all transforms: transform/claude.test.ts, transform/gemini.test.ts, transform/cross-model-sanitizer.test.ts, transform/cross-model-integration.test.ts, transform/model-resolver.test.ts. Run via bun run --cwd packages/core test (which delegates to bun test --isolate ./src).

packages/opencode inventory

@cortexkit/opencode-antigravity-auth. Exposes two exports subpaths ("." and "./tui") and pins the supported host range through engines.opencode (>=1.17.13 <2); the host installer (opencode plugin) reads the subpaths and writes a server entry to opencode.json and a TUI entry to tui.json. Production layout:

packages/opencode/
├── index.ts                                 # barrel: re-exports AntigravityCLIOAuthPlugin + GoogleOAuthPlugin
├── assets/antigravity.schema.json           # generated JSON schema for the plugin config (built by script/build-schema.ts)
├── bunfig.toml                              # test preload
├── package.json                             # peerDependencies on @opencode-ai/plugin + @opentui/*
├── tsconfig.json / tsconfig.build.json      # library + build tsconfigs
├── docs/                                    # ANTIGRAVITY_API_SPEC, ARCHITECTURE, CONFIGURATION, MODEL-VARIANTS, MULTI-ACCOUNT, TROUBLESHOOTING
├── script/                                  # one-off CLI tests + the schema builder (see Root tooling and CI below)
├── scripts/                                 # build-tui.ts, build-tui.test.ts, smoke-tui-pack-install.ts
├── src/                                     # all TypeScript source (see OpenCode TUI and RPC inventory below)
└── dist/                                    # build output (gitignored)

The src/tui-compiled/ directory lives under src/ but is gitignored — see Generated and ignored artifacts.

Server plugin (packages/opencode/src/)

  • packages/opencode/src/index.ts — top-level barrel re-exporting AntigravityCLIOAuthPlugin and GoogleOAuthPlugin from plugin/index.ts.
  • packages/opencode/src/cli.ts — standalone CLI binary entry (the bin field of package.json); the build step bundles it to dist/cli.js.
  • packages/opencode/src/antigravity/oauth.ts — host-thin re-export / extension shim around core/antigravity/oauth.ts (used by the CLI's OAuthLoginRequest).
  • packages/opencode/src/constants.ts — plugin-level constants: command names (ANTIGRAVITY_*_COMMAND_NAME), modal command constants, MODAL_COMMANDS.
  • packages/opencode/src/shims.d.ts — ambient type shims for host globals.
  • packages/opencode/src/sidebar-state.tsSidebarStateV1 snapshot schema, setSidebarMachineState writer, mergeMachineState, upsertSidebarActiveRouting, drainSidebarWrites, fenced-lock + atomic-write chain. The single source of truth both the server and the TUI read.
  • packages/opencode/src/rpc/ — the loopback HTTP RPC; see OpenCode TUI and RPC inventory below.

Plugin composition root (packages/opencode/src/plugin/)

  • packages/opencode/src/plugin/index.tscreateAntigravityPlugin(providerId, options) factory. The single composition root.
  • packages/opencode/src/plugin/types.tsPluginInput, PluginContext, PluginResult, GetAuth, host types.
  • packages/opencode/src/plugin/dependencies.tsPluginDependencyOverrides (the seam the e2e workspace uses to swap fetchImpl, agyTransport, filesystemRoots, oauth, clock) + resolvePluginDependencies default-resolver.
  • packages/opencode/src/plugin/lifecycle.tscreatePluginLifecycle — the disposal root that coordinates disposeAccountRuntime, shutdownDiskSignatureCache, sessionRegistry.clear(), clearFetchState, drainSidebarWrites, and the registered disposables (RPC server, file logger, auto-update checker). Disposables are registered with a LifecyclePhase of 'producer' or 'consumer'; producers (e.g. the auth loader's fetch-interceptor runtime) are disposed before the sidebar drain, consumers after.
  • packages/opencode/src/plugin/auth-loader.tscreateAuthLoader — the function the host invokes on every auth.loader(...) call. Detects auth drift, rebuilds the AccountManager, and returns a fresh fetch closure bound to a new fetch interceptor.
  • packages/opencode/src/plugin/auth.ts — token validation helpers used by the loader (refresh-parts checks, expiry buffer).
  • packages/opencode/src/plugin/auth-doctor.ts — self-healing diagnostics for an unrecoverable auth store.
  • packages/opencode/src/plugin/auth-drift.tsdetectAuthStorageDrift: compares the host's getAuth() result against the on-disk pool and surfaces a restorable path.
  • packages/opencode/src/plugin/accounts.ts — host-side AccountManager factory. Stores pool via plugins/opencode/storage.ts, exports getCurrentOrNextForFamily, markAccountCoolingDown, markRateLimitedWithReason.
  • packages/opencode/src/plugin/account-access.ts — CLI-facing account prompts (promptAccountIndexForVerification, promptOpenVerificationUrl, createAccountAccessService).
  • packages/opencode/src/plugin/storage.ts — host-path adapter: resolves OPENCODE_CONFIG_DIR, handles %APPDATA% on Windows, syncs the on-disk .gitignore. Delegates all data operations to core/account-storage.ts.
  • packages/opencode/src/plugin/persist-account-pool.ts — lock-held append-and-rewrite of antigravity-accounts.json used after a fresh OAuth login.
  • packages/opencode/src/plugin/fetch-interceptor.tscreateFetchInterceptor — the largest file in the tree. Outer loop picks an account, inner loop walks ANTIGRAVITY_ENDPOINT_FALLBACKS, retry/quota/routing pipeline, soft-quota + killswitch gates (killswitch evaluation is model-aware: a gemini-pro request checks ONLY the gemini-pro quota group via a precomputed eligibleIndexes Set that is reused after core selection and after the quota-fallback re-selection), sideline setSidebarMachineState writes, transformAntigravityResponse reverse-transform.
  • packages/opencode/src/plugin/fetch/retry-state.ts — per-interceptor RetryState (rate-limit toast debounce, retry counts).
  • packages/opencode/src/plugin/fetch/warmup.tsWarmupState (probe accounts under load to detect soft-quota cliffs before they surprise the dispatcher).
  • packages/opencode/src/plugin/fetch-routing.tsresolveHeaderRoutingDecision, resolveQuotaFallbackHeaderStyle, getCurrentRoutingDecision (live override from operator settings).
  • packages/opencode/src/plugin/request.ts — outbound transform pipeline (sanitize → resolve → inject metadata → order payload → harden).
  • packages/opencode/src/plugin/request-helpers.tscleanGenerativeLanguageRequest, thinking-block filtering, tool-id orphan recovery glue.
  • packages/opencode/src/plugin/agy-request-metadata.ts — host-side adapter around core/agy-request-metadata.ts with the session registry host binding.
  • packages/opencode/src/plugin/agy-transport.ts — host-side adapter around core/agy-transport.ts; thin wrapper for the dependency-injected transport.
  • packages/opencode/src/plugin/transform/cross-model-sanitizer.ts — host-side re-export of the core sanitizer (kept separate so a future OpenCode-specific override can fork without touching core).
  • packages/opencode/src/plugin/transform/model-resolver.ts — host-side re-export.
  • packages/opencode/src/plugin/transform/types.ts — host-side re-export.
  • packages/opencode/src/plugin/transform/index.ts — host-side barrel.
  • packages/opencode/src/plugin/quota.tscreateOpenCodeQuotaManager (the host wrapper over core/quota-manager.ts). Hands a getAccountsForSidebar closure to the core manager so quota refreshes push redacted sidebar snapshots.
  • packages/opencode/src/plugin/refresh-queue.tscreateProactiveRefreshQueue — background timer that refreshes tokens before the access token expires.
  • packages/opencode/src/plugin/rotation.ts — host thin wrapper around core/rotation.ts; pushes initHealthTracker and initTokenTracker per the user config.
  • packages/opencode/src/plugin/token.tsrefreshAccessToken — called by the fetch interceptor when a token is within the 60s expiry buffer.
  • packages/opencode/src/plugin/oauth-methods.ts — interactive OAuth menu (Add another, Refresh, Check quotas, Verify accounts, Doctor, Manage, Cancel); also drives authorizeAntigravity and exchangeAntigravity from core.
  • packages/opencode/src/plugin/oauth-login.tsperformOAuthLogin: orchestrates the startOAuthListener + exchangeAntigravity flow for the CLI path.
  • packages/opencode/src/plugin/server.tsstartOAuthListener (binds http://localhost:51121 for the redirect, falls back to manual paste for headless / WSL2).
  • packages/opencode/src/plugin/catalog.tsapplyAntigravityProviderCatalog + registerAntigravityCommands — handles config.provider.* and config.command.* injection.
  • packages/opencode/src/plugin/commands.tsapplyCommand (RPC apply callback), createCommandExecuteBefore, createSidebarRefresher. The single wiring point for /antigravity-* slash commands.
  • packages/opencode/src/plugin/cli.ts — small terminal prompts (promptProjectId, promptAddAnotherAccount) used by the OAuth method flow.
  • packages/opencode/src/plugin/ui/auth-menu.ts — full-screen interactive auth menu renderer (showAuthMenu, showAccountDetails, showFingerprintHistory).
  • packages/opencode/src/plugin/ui/model-status.ts — per-model quota status rendering helpers.
  • packages/opencode/src/plugin/ui/quota-status.ts — quota summary rendering helpers.
  • packages/opencode/src/plugin/ui/select.ts — primitive select-with-description list (kept free of the host's hide-property so invalid options stay visible).
  • packages/opencode/src/plugin/ui/confirm.ts — primitive confirm dialog.
  • packages/opencode/src/plugin/ui/ansi.ts — ANSI helpers shared by the UI primitives.
  • packages/opencode/src/plugin/event-handler.tscreateEventHandler — host event hook (session.created / session.idle etc.).
  • packages/opencode/src/plugin/recovery.tscreateSessionRecoveryHook — auto-recovers sessions from tool_result_missing errors.
  • packages/opencode/src/plugin/recovery/index.ts — recovery subsystem barrel.
  • packages/opencode/src/plugin/recovery/types.ts — recovery state types.
  • packages/opencode/src/plugin/recovery/constants.ts — recovery limits (max attempts, retry delays).
  • packages/opencode/src/plugin/recovery/storage.ts — durable on-disk state for in-flight recovery attempts.
  • packages/opencode/src/plugin/thinking-recovery.ts — recovery for thinking-block signature mismatches (Gemini skip_thought_signature_validator path).
  • packages/opencode/src/plugin/killswitch.tsevaluateKillswitchForAccount, throwIfAllKilled — operator-tuned per-family/minimum-remaining-percent gate. Evaluation is model-aware via the model/quotaModel option: a gemini-pro request checks ONLY the gemini-pro quota group, not the max of pro+flash.
  • packages/opencode/src/plugin/operator-settings.tscreateOperatorSettingsController: the runtime-mutable settings store (routing flags, killswitch, log level). Persists through the fenced-lock writer.
  • packages/opencode/src/plugin/config/schema.ts — Zod schema for antigravity.json (the public config schema). Schema description source for script/build-schema.ts.
  • packages/opencode/src/plugin/config/loader.tsloadConfig(directory), loadConfigFile. Computes the legacy Windows migration fallback. Swallows malformed JSON.
  • packages/opencode/src/plugin/config/writer.ts — fenced-lock writer for the user config (used by the operator settings controller).
  • packages/opencode/src/plugin/config/updater.tsupdateOpencodeConfig — generic writer helper for the host's opencode.json.
  • packages/opencode/src/plugin/config/models.tsOPENCODE_MODEL_DEFINITIONS literal copy + the host Model adapter.
  • packages/opencode/src/plugin/config/operator-settings-schema.ts — Zod schema for the operator.* subtree.
  • packages/opencode/src/plugin/config/index.ts — barrel for the config subtree; also calls loadConfig + initRuntimeConfig.
  • packages/opencode/src/plugin/cache/index.ts — barrel for the cache subtree.
  • packages/opencode/src/plugin/cache.ts — backwards-compatible thin re-export of cache/index.ts (kept for legacy callers).
  • packages/opencode/src/plugin/cache/signature-cache.ts — disk-backed cache for thinking-block signatures.
  • packages/opencode/src/plugin/stores/signature-store.tsSignatureStore persistence interface + memory-backed default.
  • packages/opencode/src/plugin/core/streaming/index.ts — barrel for the streaming subsubsystem.
  • packages/opencode/src/plugin/core/streaming/transformer.ts — the SSE reverse-transform: strips Antigravity envelope, splices thoughtSignature, aggregates usageMetadata, produces a ReadableStream<Uint8Array>.
  • packages/opencode/src/plugin/core/streaming/types.tsSignatureStore, StreamingCallbacks, StreamingOptions, StreamingUsageMetadata, ThoughtBuffer.
  • packages/opencode/src/plugin/debug.ts — debug file logger for the server plugin (writes to the user-configured log_dir). Project IDs and fingerprint-form User-Agents are masked via redactSensitive before emission, and the logged request-body preview is field-redacted via redactBodyForLog so a project ID embedded in the body never leaks verbatim.
  • packages/opencode/src/plugin/errors.ts — domain error classes (AntigravityKillswitchError, AntigravityTokenRefreshError, LiveNetworkDeniedError, etc.).
  • packages/opencode/src/plugin/fingerprint.ts — host thin wrapper that pins the FingerprintVersion for an account.
  • packages/opencode/src/plugin/gemini-dump.ts/gemini-dump modal command — saves raw payload/response to disk for debugging. Session IDs and project IDs are masked via maskIdentifier; header User-Agents are redacted by redactForDump; the request body written to disk is field-redacted via redactJsonBodyString so project IDs embedded in the body are masked.
  • packages/opencode/src/plugin/google-search-tool.ts — server-side google_search tool implementation.
  • packages/opencode/src/plugin/image-saver.ts — writes Gemini image-generation output to disk and returns the file path back to the caller.
  • packages/opencode/src/plugin/logger.ts — structured logger (createLogger, initLogger, setRuntimeLogLevel).
  • packages/opencode/src/plugin/logging-utils.ts — small ANSI/console helpers plus redactSensitive / redactSensitiveFields (case-insensitive walk over token|refresh|access|project|fingerprint|deviceId|sessionId|sessionToken|secret|password|apiKey|clientSecret keys — project catches bare project / managedProjectId body fields), and redactJsonBodyString / redactBodyForLog which field-redact a serialized JSON request body before it reaches a debug log or dump file.
  • packages/opencode/src/plugin/model-registry.ts — host overlay on core/model-registry.ts (OpenCode Model shape + providerOptions defaults).
  • packages/opencode/src/plugin/project.ts — host thin wrapper around core/project.ts.
  • packages/opencode/src/plugin/prompt-context.ts — extracts the active prompt context (workspace + system prompt) for cross-model sanitizer decisions.
  • packages/opencode/src/plugin/search.tsexecuteSearch Google Search grounding tool implementation.
  • packages/opencode/src/plugin/session-context.tsAgySessionRegistry, per-session AgyRequestSessionStore registration.
  • packages/opencode/src/plugin/version.ts — runs once at plugin boot to publish the installed plugin version (used by the auto-update checker).
  • packages/opencode/src/plugin/host-api-compatibility.test.ts — pins the opencode host SDK to a stable property subset (so a host bump that moves a name does not break us silently).

Host lifecycle hooks

packages/opencode/src/hooks/auto-update-checker/ — a self-contained module that runs in the background of the host:

  • packages/opencode/src/hooks/auto-update-checker/index.ts — barrel; createAutoUpdateCheckerHook is the host-side hook entry point.
  • packages/opencode/src/hooks/auto-update-checker/checker.ts — the actual update check: npm view @cortexkit/opencode-antigravity-auth version + auto-pin routine.
  • packages/opencode/src/hooks/auto-update-checker/cache.ts — on-disk cache of the last known good version.
  • packages/opencode/src/hooks/auto-update-checker/constants.ts — check interval, npm registry URL.
  • packages/opencode/src/hooks/auto-update-checker/logging.ts — internal logger.
  • packages/opencode/src/hooks/auto-update-checker/types.ts — types for the hook's report.

Co-located tests under packages/opencode/src/

The plugin directory mirrors the source tree with *.test.ts siblings for nearly every file: account-access, account-ineligibility.persistence, account-ineligibility, accounts, agy-transport, antigravity-first-fallback, auth-doctor, auth-drift, auth-loader, auth, cache, catalog, commands, core/streaming/transformer, debug, errors, event-handler, fetch-interceptor, fetch/retry-state, fetch-routing, fetch/warmup, gemini-dump, google-search-tool, host-api-compatibility, image-saver, killswitch, lifecycle, logger, logging-utils, model-specific-quota, oauth-methods, operator-settings, persist-account-pool, quota-fallback, quota, recovery, reexports, refresh-queue, request-helpers, request, rotation, search, session-context, storage, stores/signature-store, thinking-recovery, token, plus the UI subtree (ui/ansi, ui/auth-menu.actions, ui/auth-menu, ui/model-status, ui/quota-status), version, and the host-SDK pins.

Top-level co-located tests under packages/opencode/src/: cli.test.ts, constants.test.ts, plugin-entry.test.ts, sidebar-state.test.ts, rpc/notifications.test.ts, rpc/port-file.test.ts, rpc/rpc-client.test.ts, rpc/rpc-server.test.ts, and the TUI tests tui/command-dialogs.test.tsx, tui/file-logger.test.ts, tui.test.tsx. Run via bun run --cwd packages/opencode test (delegates to bun test --isolate ./src).

OpenCode TUI and RPC inventory

The TUI is a thin Solid sidebar that polls a redacted snapshot file and a loopback HTTP RPC. Two halves of the contract: the read seam (sidebar file) and the bidirectional seam (RPC + bearer token).

Sidebar snapshot

  • packages/opencode/src/sidebar-state.tsSidebarStateV1 schema, getSidebarStateFile(), setSidebarMachineState, mergeMachineState, upsertSidebarActiveRouting, buildSidebarMachineStateFromAccounts, drainSidebarWrites. Header (lines 1-38) documents the split between server-side reads/writes and TUI-side reads.

packages/opencode/src/rpc/ — the loopback HTTP RPC

  • packages/opencode/src/rpc/protocol.ts — wire types: CommandModalName (six modal commands), OpenDialogPayload, RpcNotification, ApplyRequest, ApplyResult.
  • packages/opencode/src/rpc/rpc-server.tsstartRpcServer({ dir, apply, drain }). Binds 127.0.0.1:0, mints a 32-byte hex bearer token, writes the port file, serves POST /rpc/apply (server timeout 120s) and POST /rpc/pending-notifications (request timeout 2s). Uses timingSafeEqual for the bearer check.
  • packages/opencode/src/rpc/rpc-client.tscreateRpcClient(dir, expectedPid) — discovers the port file, posts JSON with Authorization: Bearer <token>, enforces a 2s default via fetchWithActiveTimeout.
  • packages/opencode/src/rpc/port-file.tswritePortFile / discoverPortFile. <dir>/port-<pid>.json with { pid, port, token }, mode 0o600, atomic via renameSync from a .tmp sibling.
  • packages/opencode/src/rpc/rpc-dir.tsgetRpcDir: ANTIGRAVITY_AUTH_RPC_DIR env wins, else <XDG_STATE_HOME>/cortexkit/antigravity-auth/rpc/<sha256(directory)[:16]>.
  • packages/opencode/src/rpc/notifications.ts — bounded queue (max 100 entries), pushNotification, drainNotifications(lastReceivedId, sessionId?). isTuiConnected reflects whether the TUI polled within CONNECTION_TTL_MS = 5000.
  • Tests: notifications.test.ts, port-file.test.ts, rpc-client.test.ts, rpc-server.test.ts.

TUI render tree (packages/opencode/src/tui/, packages/opencode/src/tui.tsx)

  • packages/opencode/src/tui.tsx@jsxImportSource @opentui/solid. The TuiPlugin default export registers the slots.sidebar_content slot. Hard rules for the file are documented in its module header (lines 1-22).
  • packages/opencode/src/tui/entry.mjs — host-aware loader. Probes opentui:runtime-module:<encodeURIComponent('@opentui/solid')>; falls back to the raw tui.tsx if the host has no runtime module resolver.
  • packages/opencode/src/tui/command-dialogs.tsxDialogSelect / DialogConfirm / DialogPrompt trees mounted from RPC notifications. Every MODAL_COMMANDS option has a stable key and an explanatory description.
  • packages/opencode/src/tui/file-logger.ts — write-only file logger under <xdg-state>/cortexkit/antigravity-auth/tui.log (mode 0o600, 1 MB tail-truncated). Parent dir is forced to 0o700 after every write and existing log files are re-chmod-ed to 0o600 to repair any leaked permissions. Never writes to stdout/stderr.
  • packages/opencode/src/tui/ansi.ts — small ANSI helpers used by the dialog tree.
  • Tests: tui.test.tsx, tui/command-dialogs.test.tsx, tui/file-logger.test.ts.

packages/pi inventory

@cortexkit/pi-antigravity-auth. A small extension that bridges the Pi runtime's registerProvider API to the core transport. Layout:

  • packages/pi/bunfig.tomlpreload = ["../../test/setup.ts", "@opentui/solid/preload"]. (OpenTUI preload is harmless here; the Pi extension itself does not use @opentui/solid.)
  • packages/pi/package.json — declares main on ./dist/index.js, peerDependencies on @earendil-works/pi-*, runtime dependency only on @cortexkit/antigravity-auth-core.
  • packages/pi/tsconfig.json, packages/pi/tsconfig.build.json — library + build configs.
  • packages/pi/LICENSE, packages/pi/README.md — MIT + readme.

Source (packages/pi/src/)

  • packages/pi/src/index.ts — package entry. providerId = 'google-antigravity'; registers the provider with pi.registerProvider; defines the OAuth login + refresh flows against core/antigravity/oauth.ts.
  • packages/pi/src/stream.tsstreamCortexKitAntigravity: streams through core/agy-transport.ts, preserves signed thinking/tool events, and maintains session-scoped native AGY execution metadata.
  • packages/pi/src/convert.ts — Pi Context-to-Gemini conversion with native AGY tool-call IDs, signature replay, cross-model sanitization, and same-target function-response roles.
  • packages/pi/src/credential-cache.ts — stashes the packed refreshToken|projectId|managedProjectId triple so the stream can rejoin project context after the access token is stripped.
  • packages/pi/src/paths.ts — resolves the PI_AGENT_DIR and PI_ANTIGRAVITY_AUTH_FILE paths on every platform (%APPDATA% on Windows, ~/.pi/agent/ elsewhere).
  • packages/pi/src/index.test.ts, convert.test.ts, credential-cache.test.ts, stream.test.ts — co-located tests.

packages/e2e-tests inventory

@cortexkit/antigravity-auth-e2eprivate: true, not published. Houses deterministic black-box flows against a mock Antigravity loopback server.

  • packages/e2e-tests/bunfig.tomlpreload = ["./src/setup.ts"], root = "./src" so the e2e-runner only loads this workspace's tests.
  • packages/e2e-tests/tsconfig.json — extends @tsconfig/bun.
  • packages/e2e-tests/package.jsonprivate, depends on workspace:* core + opencode packages.

Source (packages/e2e-tests/src/)

  • packages/e2e-tests/src/setup.ts — per-test temp root + env reset (HOME, XDG_*, APPDATA, LOCALAPPDATA, OPENCODE_CONFIG_DIR, PI_AGENT_DIR, PI_ANTIGRAVITY_AUTH_FILE, OPENCODE_ANTIGRAVITY_GEMINI_DUMP_DIR, ANTIGRAVITY_AUTH_RPC_DIR, ANTIGRAVITY_AUTH_SIDEBAR_STATE_FILE), plus a loopback-only globalThis.fetch guard (installFetchGuard / restoreFetchGuard, line 51-84) that throws LiveNetworkDeniedError for any non-loopback URL. The guard is installed in beforeEach and restored in afterEach so a stray live URL surfaces as a real exception that points at the offending call site. Each isolated test file's final afterAll disposes harnesses scoped to its roots before deleting them; a post-delete existsSync check throws if a root survives. The opt-in orphan sweep (AGY_E2E_SWEEP_ORPHANS=1) only removes agy-e2e-* entries older than 24h by mtime.
  • packages/e2e-tests/src/harness.tsE2eHarness: builds a mock loopback server, a PluginDependencyOverrides bag that points the production plugin at 127.0.0.1, and createPlugin() / runCli() helpers. The single seam every e2e test uses to talk to the plugin under test.
  • packages/e2e-tests/src/fetch-router.tsinstallFetchRouter — routes globalThis.fetch through the mock when the URL targets the loopback server.
  • packages/e2e-tests/src/mock-antigravity-server.tsstartMockAntigravityServer + MockServerHandle. Configurable routes, scripted responses, request recorder.
  • packages/e2e-tests/src/process-runner.tsprocess-runner: spawns the antigravity-auth CLI binary in a temp dir and pins stdout/stderr for assertions.
  • packages/e2e-tests/src/cli-flow.e2e.test.ts — CLI flows (login, refresh, quota check) against the mock.
  • packages/e2e-tests/src/plugin-flow.e2e.test.ts — full plugin instance lifecycle against the mock.
  • packages/e2e-tests/src/rpc-tui-flow.e2e.test.ts — RPC server boot + notification drain end-to-end.
  • packages/e2e-tests/src/fetch-guard.test.ts — pins the loopback-only globalThis.fetch guard: loopback URLs pass through, non-loopback URLs throw LiveNetworkDeniedError, the guard is restored in afterEach.
  • Unit tests under this workspace: mock-antigravity-server.test.ts, process-runner.test.ts.
  • Run via bun run test:e2e at the repo root (delegates to bun test --isolate ./packages/e2e-tests/src/plugin-flow.e2e.test.ts ./packages/e2e-tests/src/cli-flow.e2e.test.ts ./packages/e2e-tests/src/rpc-tui-flow.e2e.test.ts ./packages/e2e-tests/src/fetch-guard.test.ts).

Root tooling and CI

scripts/

  • scripts/dev.tsbun scripts/dev.ts. Resolves dev paths, symlinks packages/opencode/dist/index.js into .opencode/plugins/antigravity-auth.js, then runs tsc --watch on core and esbuild --watch on opencode in parallel until SIGINT/SIGTERM. --check validates the symlink and exits.
  • scripts/dev-clean.ts — inverse of dev.ts; removes the development symlink.
  • scripts/version-sync.mjs — synchronized version bump for packages/{core,opencode,pi}/package.json (and the @cortexkit/antigravity-auth-core pin in dependents). Supports --dry-run and --from-tag (driven by GITHUB_REF_NAME in CI).
  • scripts/release.sh — semver validate + dry-run flag, pre-flight bun run typecheck / bun run test / bun run build, sync versions, commit, tag v$X.Y.Z, push to origin. GitHub Actions then runs the publish matrix.

test/ (repo-root preload + sanity)

  • test/setup.ts — installs mkdtempSync test root + env overrides on every package workspace, defines globalThis.stubbed / unstubAllGlobals / freshImport, and patches jest.setSystemTime / jest.useRealTimers to also spy on Date.now().
  • test/global.d.ts — type declarations for the helpers test/setup.ts installs.
  • test/environment.test.ts — pins the env-isolation contract (HOME, XDG_*, APPDATA, OPENCODE_CONFIG_DIR, PI_AGENT_DIR, etc.).
  • test/dev.test.ts — pins scripts/dev.ts's resolveDevPaths / createDevSymlink / removeDevSymlink so a symlink-layout regression fails before the next release.
  • test-fixtures/agy-cli-1.1.3-model-metadata.json and test-fixtures/agy-cli-1.1.3-stream-request.json — frozen snapshots of the upstream agy CLI for comparator tests.
  • test-fixtures/agy-cli-1.1.5-* and test-fixtures/agy-cli-1.1.6-* — versioned frozen wire snapshots.

tsconfig.scripts.json

Project-wide tsconfig used by bun run typecheck (root script typecheck) for scripts/**/*.ts, test/**/*.ts, and packages/e2e-tests/src/**/*.ts.

Root level

  • package.json — root monorepo (@cortexkit/antigravity-auth, private: true, workspaces: [packages/*]). Exposes build, typecheck, test, test:e2e, test:e2e:models, test:e2e:regression, dev, dev:clean, format, format:check, lint, pack:core:dry, pack:opencode-dry, pack:pi-dry.
  • biome.json — formatter + linter config (indent: 2 spaces, semicolons: asNeeded, quoteStyle: single). Test files (**/*.test.ts) and packages/opencode/src/plugin/ui/select.ts carry override files that disable a small set of rules where the test naturally needs them.
  • bunfig.toml — root: preload = ["./test/setup.ts", "@opentui/solid/preload"].
  • lefthook.yml — pre-commit: biome check --staged --no-errors-on-unmatched on *.{ts,tsx,js,mjs,json,jsonc,yml,yaml}.
  • mise.toml — pins bun = "1.3" and node = "24".
  • bun.lock — committed; CI uses bun install --frozen-lockfile.
  • models — tracked JSON snapshot of the upstream model inventory (referenced by the e2e tests for drift assertions).

.github/

  • .github/workflows/ci.yml — push/PR to main: Setup Node 24 + Bun 1.3, bun install --frozen-lockfile, bun run typecheck, bun run build, bun run --cwd packages/opencode smoke:tui, bun test, bun run test:e2e, bun run format:check, bun run lint.
  • .github/workflows/release.yml — on v* tag push + workflow_dispatch: runs the same checks (test/build/e2e/format/lint) before publishing via OIDC trusted publishing. publish-npm is a matrix job that publishes core, then opencode, then pi, each from a fresh job so npm obtains a per-package OIDC token. Closes with a softprops/action-gh-release GitHub Release.
  • .github/workflows/issue-triage.yml — self-hosted Opencode Triage agent: parses label output, applies bug/enhancement/documentation/question/invalid + area/auth/area/models/area/config/area/compat, posts an automated reply, and closes duplicates.
  • .github/FUNDING.yml, .github/ISSUE_TEMPLATE/bug_report.yml, .github/ISSUE_TEMPLATE/feature_request.yml, .github/ISSUE_TEMPLATE/config.yml.

Entry points and package exports

Package File Symbol Purpose
@cortexkit/antigravity-auth-core packages/core/src/index.ts (barrel) All harness-agnostic exports — re-exported per file.
@cortexkit/opencode-antigravity-auth (server) packages/opencode/index.ts AntigravityCLIOAuthPlugin, GoogleOAuthPlugin Host plugin registrations (bound factory with ANTIGRAVITY_PROVIDER_ID = 'google').
@cortexkit/opencode-antigravity-auth (server — raw) packages/opencode/src/plugin/index.ts createAntigravityPlugin, PluginResult Direct composition root for tests + non-host callers.
@cortexkit/opencode-antigravity-auth (server — CLI) packages/opencode/src/cli.ts default export Standalone antigravity-auth CLI entry. Build emits dist/cli.js.
@cortexkit/opencode-antigravity-auth (tui) packages/opencode/src/tui/entry.mjs { id: 'cortexkit.antigravity-auth', tui } OpenTUI sidebar entry. Resolves through dist/src/tui.d.ts (types) + src/tui/entry.mjs (loader).
@cortexkit/opencode-antigravity-auth (server — fallback) packages/opencode/src/plugin-entry.test.ts test entry Verifies the public barrel exports the two stable plugin names.
@cortexkit/pi-antigravity-auth packages/pi/src/index.ts default function Pi extension. Resolves as pi.extensions: ['./dist/index.js'] per packages/pi/package.json:34-38.

The exports map at packages/opencode/package.json declares both . (the bundled server root) and ./tui (the OpenTUI loader). The host's opencode plugin installer reads the subpaths and writes a server entry to opencode.json plus a TUI entry to tui.json; the host then resolves them as two independent plugin registrations.

Generated and ignored artifacts

These directories exist at build time but are gitignored (see .gitignore). They are not part of the source tree:

  • node_modules/ — Bun-installed dependencies, workspace-wide. Ignored.
  • dist/packages/core/dist/, packages/opencode/dist/, packages/pi/dist/. Per-package tsc --build + esbuild output. Ignored. The published files allowlist (packages/opencode/package.json:46-61, etc.) packages dist/ explicitly so the build output is what ships on npm.
  • *.tgzbun pm pack --dry-run output staging. Ignored.
  • coverage/, *.lcov, logs/, *.log, _.log, antigravity-debug-*.log — coverage + log output. Ignored.
  • *.tsbuildinfo — TypeScript incremental compile cache. Ignored.
  • .opencode/ — IDE + local OpenCode plugin dir. The root .gitignore ignores it; scripts/dev.ts makes .opencode/plugins/antigravity-auth.js a symlink during dev.
  • Agent working directories: .hive/, .omo/, .opencode/, .sisyphus/, .alfonso/ — session state, historian dumps, run continuation, council runs. All ignored.
  • test-file.ts — temporary scratch file used by the dev loop. Ignored.
  • package-lock.json — npm optional-dep native-binary workaround. Ignored.

The precompiled OpenTUI tree

packages/opencode/src/tui-compiled/ is the only directory in the tree that ships in the npm tarball but is gitignored at the source level. It is rebuilt from source every release.

  • Source manifest: packages/opencode/scripts/build-tui.tsbuildTui({ packageRoot }) walks the relative static import graph rooted at src/tui.tsx, transforms each .tsx through @opentui/solid/scripts/solid-transform (rewriting virtual runtime modules to opentui:runtime-module:<encoded>), and copies non-TSX sibling files as-is.
  • Shipped source allowlist: packages/opencode/scripts/build-tui.ts:91-102 (SHIPPED_SOURCE_FILES) is the canonical allowlist that ships — src/tui.tsx, src/tui/entry.mjs, src/tui/ansi.ts, src/tui/command-dialogs.tsx, src/tui/file-logger.ts, src/sidebar-state.ts, src/rpc/rpc-client.ts, src/rpc/rpc-dir.ts, src/rpc/port-file.ts, src/rpc/protocol.ts.
  • Behavior: the loader src/tui/entry.mjs probes opentui:runtime-module:<encodeURIComponent('@opentui/solid')>; if that succeeds it loads ../tui-compiled/tui.tsx (the host has a runtime-module resolver), otherwise it loads the dev fallback ../tui.tsx via @opentui/solid/preload. src/tui/entry.mjs is intentionally NOT copied into the compiled tree — it ships via the src/tui/ directory entry in package.json files.
  • Test: packages/opencode/scripts/build-tui.test.ts pins the SHIPPED_SOURCE_FILES list, the import-graph traversal, the transformed output, and the TUI's import graph against the credential-module allowlist (account-manager, account-storage, oauth, quota-manager, rotation — the latter via FORBIDDEN_PATTERNS).
  • Pack smoke test: packages/opencode/scripts/smoke-tui-pack-install.ts packs core + opencode via bun pm pack, installs them into a real consumer workspace with bun install --no-save, then resolves @cortexkit/opencode-antigravity-auth/. and @cortexkit/opencode-antigravity-auth/tui through the export map and asserts the file shipped by the tarball lines up with the path the loader expects. Run via bun run --cwd packages/opencode smoke:tui.
  • Schema JSON: packages/opencode/assets/antigravity.schema.json is also a generated artifact, produced by packages/opencode/script/build-schema.ts from packages/opencode/src/plugin/config/schema.ts (the Zod schema). Run via bun run --cwd packages/opencode build:schema.

Naming and test conventions

  • Files / directories: kebab-case.tsaccount-manager.ts, fetch-interceptor.ts, auto-update-checker/, core/streaming/. No exceptions in tracked source.
  • Test files: co-located *.test.ts siblings (e.g. account-storage.tsaccount-storage.test.ts). The single exception is the e2e workspace, which uses *.e2e.test.ts for the three black-box flows so the root bun test and bun test:e2e selectors can target them precisely.
  • Types / interfaces: PascalCaseAccountManager, AntigravityConfig, SidebarStateV1, PluginResult.
  • Functions / variables: camelCasegetCurrentOrNextForFamily, resolveModelWithTier, setSidebarMachineState.
  • Constants: UPPER_SNAKE_CASEANTIGRAVITY_PROVIDER_ID, CLAUDE_TOOL_SYSTEM_INSTRUCTION, SKIP_THOUGHT_SIGNATURE, DEFAULT_AGY_RESPONSE_HEADER_TIMEOUT_MS.
  • Test framework: Bun's bundled test runner (bun test); tests can spell helpers as bun:test (describe, it, expect, beforeEach, afterEach, jest namespace). bunfig.toml in every workspace preloads ./test/setup.ts for env isolation.
  • Assertion style: expect(actual).toBe(...), .toEqual(...), .toMatchObject(...); no third-party assertion library.
  • Test isolation env vars: ANTIGRAVITY_TEST_ROOT, HOME, USERPROFILE, XDG_CONFIG_HOME, XDG_CACHE_HOME, XDG_DATA_HOME, XDG_STATE_HOME, APPDATA, LOCALAPPDATA, OPENCODE_CONFIG_DIR, OPENCODE_ANTIGRAVITY_GEMINI_DUMP_DIR, PI_AGENT_DIR, PI_ANTIGRAVITY_AUTH_FILE, ANTIGRAVITY_AUTH_RPC_DIR, ANTIGRAVITY_AUTH_SIDEBAR_STATE_FILE. Set automatically by test/setup.ts.
  • Formatting: Biome 2.5.3, two-space indent, semicolons omitted, single quotes, trailing commas. The root runner is bun run format:check and bun run lint.

Dependency-direction rules

                 ┌─────────────────┐
                 │ packages/core   │  ← harness-agnostic. Node built-ins only.
                 │                 │  Owns: OAuth, transport, account pool,
                 │                 │  rotation, quota, persistence, transforms.
                 └────────▲────────┘
                          │
        ┌─────────────────┴─────────────────┐
        │                                   │
   ┌────┴─────────┐                ┌────────┴────────┐
   │ packages/    │                │ packages/pi     │  ← depends only on core.
   │ opencode     │                │  Pi runtime via │
   │   server     │                │  peerDeps only. │
   │   plugin     │                └─────────────────┘
   │       │      │
   │       ├──────┴──┐ TUI split is via file + RPC, not imports.
   │       │ sidebar │
   │       │ state   │
   │       │   +     │
   │       │   RPC   │
   │       ▼         ▼
   │  ┌────────┐ ┌──────────┐
   │  │ rpc/   │ │ tui.tsx  │
   │  │ (server)│ │ (TUI)    │
   │  └────────┘ └──────────┘
   └─────────────────────────┘
                              ▲
                              │ (e2e workspace consumes server plugin under test)
                       ┌──────┴───────┐
                       │ packages/    │  ← private, depends on core + opencode.
                       │ e2e-tests    │
                       └──────────────┘

Holds:

  • core must not import from opencode, pi, or e2e-tests. It only knows fetch, node:fs, node:net, and a fetchAccountQuota callback — it must not import @opencode-ai/plugin or @earendil-works/pi-ai. The corollary: every host-runtime call is injected through PluginDependencyOverrides (packages/opencode/src/plugin/dependencies.ts).
  • pi depends only on core and Pi's peer deps. It never reaches into opencode. The Pi extension has no concept of an account pool — core/auth.ts treats the refresh token as opaque so both Pi and OpenCode can reuse it.
  • opencode server → OpenTUI communication is async and out-of-process. The TUI imports nothing from packages/opencode/src/plugin/{storage,accounts,fetch-interceptor,oauth-*} — only src/sidebar-state.ts, src/rpc/protocol.ts, src/rpc/rpc-client.ts, src/rpc/rpc-dir.ts, and the local src/tui/ folder. The compiler would let it, so the contract is enforced by the import-graph review at packages/opencode/src/tui.tsx:1-22, the import-graph gate in packages/opencode/scripts/build-tui.test.ts, and the e2e fixture in packages/e2e-tests/src/rpc-tui-flow.e2e.test.ts.
  • e2e-tests does not take a peer on the host. It loads the plugin source as an internal import so the production factory is exercised as-is.

Where to add code

Pick the path that lines up with the smallest possible blast radius. The list below is in the same shape as the verification gates.

  • New core primitive (transport rule, persistence primitive, OAuth step): add or edit a file under packages/core/src/. Public surface changes go through packages/core/src/index.ts; co-locate a *.test.ts next to it. Type additions live in the matching *-types.ts file (e.g. account-types.ts, auth-types.ts, model-types.ts, quota-types.ts).
  • New model transform or sanitization rule: packages/core/src/transform/. Add a focused export to transform/index.ts (or re-export transform/types.ts for the type). Update the focused test sibling; if the change touches model resolution, also update transform/model-resolver.ts and its model-resolver.test.ts.
  • New provider endpoint or sentinel value: packages/core/src/constants.ts. If the value is per-account (overridable via config), the override goes in packages/opencode/src/plugin/config/schema.ts instead, and the fallback hard-codes the constant.
  • New OpenCode hook (host lifecycle — e.g. tool registration, event hook): add a module under packages/opencode/src/plugin/, wire it from packages/opencode/src/plugin/index.ts's factory, register it as a disposable in packages/opencode/src/plugin/lifecycle.ts, and co-locate a *.test.ts. Examples: event-handler.ts, recovery/recovery.ts, hooks/auto-update-checker/index.ts, google-search-tool.ts.
  • New modal command (slash command that opens a dialog):
    1. Add the constant to packages/opencode/src/constants.ts (ANTIGRAVITY_*_COMMAND_NAME).
    2. Add the entry to MODAL_COMMANDS in packages/opencode/src/plugin/commands.ts.
    3. Register the canonical and (where applicable) alias names in packages/opencode/src/plugin/catalog.ts's registerAntigravityCommands and applyAntigravityProviderCatalog.
    4. Add the modal name to packages/opencode/src/rpc/protocol.ts's CommandModalName union.
    5. Build the dialog in packages/opencode/src/tui/command-dialogs.tsx (and tui/command-dialogs.test.tsx). The three-wirings invariant is pinned by a dedicated test in packages/opencode/src/plugin/commands.test.ts.
  • New sidebar field: extend SidebarStateV1 in packages/opencode/src/sidebar-state.ts (the schema) and the buildSidebarMachineStateFromAccounts + mergeMachineState writers; render the new field in packages/opencode/src/tui.tsx (and packages/opencode/src/tui.test.tsx). Remember that sidebar state is public — it lands in <xdg-state>/cortexkit/antigravity-auth/sidebar-state.json and must contain no tokens or project IDs.
  • New RPC method or notification shape: packages/opencode/src/rpc/protocol.ts (wire types) + packages/opencode/src/rpc/rpc-server.ts (server handler) + packages/opencode/src/rpc/rpc-client.ts (TUI caller). Bump the bearer check if you add an unauthenticated path. Add a sibling *.test.ts per file.
  • New CLI command: packages/opencode/src/cli.ts (the bundled CLI binary). Update scripts/dev.ts only if the new command affects the dev symlink lifecycle, which it should not. Co-locate a CLI-level test under packages/e2e-tests/src/cli-flow.e2e.test.ts.
  • New Pi behavior (provider shape change, new path resolver, conversion rule): live under packages/pi/src/. The package surface is src/index.ts; stream adapters live in src/stream.ts; request/response converters live in src/convert.ts; path resolvers live in src/paths.ts. Co-locate a *.test.ts next to each.
  • New E2E fixture: packages/e2e-tests/src/mock-antigravity-server.ts for the server side, then a scenario in one of the three *.e2e.test.ts files. The harness contract is E2eHarness from packages/e2e-tests/src/harness.ts.
  • New config field: add the Zod field in packages/opencode/src/plugin/config/schema.ts (and the operator runtime override in config/operator-settings-schema.ts if the field is mutable from /antigravity-*). Re-run bun run --cwd packages/opencode build:schema so assets/antigravity.schema.json stays in sync. The loader tolerates a missing field — loadConfigFile in config/loader.ts swallows malformed JSON and falls back to defaults.

Change-together matrices

These are the file clusters where one logical change needs to touch more than one path. A coordinated change should land in the same PR.

Change Files that must move
Rename or change a core export packages/core/src/index.ts + the source file + the per-file test + the re-exporter in packages/opencode/src/plugin/transform/{index.ts,...} (if it's a transform) + every .test.ts that imports the old name.
Add a new @cortexkit/antigravity-auth-core dependency in packages/opencode or packages/pi Bump the published version, then re-run scripts/version-sync.mjs so dependents pick up the new pin.
Add a modal command constants.ts (ANTIGRAVITY_*_COMMAND_NAME), plugin/commands.ts (MODAL_COMMANDS, applyCommand), plugin/catalog.ts (registerAntigravityCommands, applyAntigravityProviderCatalog), rpc/protocol.ts (CommandModalName), tui/command-dialogs.tsx (dialog payload builder) + the matching *.test.ts siblings. The pinning test in plugin/commands.test.ts will fail if any one drifts.
Change SidebarStateV1 schema packages/opencode/src/sidebar-state.ts (schema + writers + merge), packages/opencode/src/tui.tsx (render), packages/opencode/src/tui.test.tsx (render assertions). The TUI is a polling consumer — version bumps are additive, not breaking.
Change a session-shape contract packages/core/src/agy-request-metadata.ts (storage, buildAgyRequestMetadata), packages/opencode/src/plugin/agy-request-metadata.ts (host adapter), packages/opencode/src/plugin/session-context.ts (registry), every *.test.ts that pins trajectory / labels.
Change an OAuth step packages/core/src/antigravity/oauth.ts (the truth), packages/opencode/src/antigravity/oauth.ts (host re-export), packages/opencode/src/plugin/server.ts (callback listener), packages/opencode/src/plugin/oauth-methods.ts (UI flow), packages/pi/src/index.ts (Pi adapter — uses authorizeAntigravity + exchangeAntigravity directly).
Change the refresh queue cadence packages/core/src/auth.ts (buffer), packages/core/src/rotation.ts (backoff), packages/opencode/src/plugin/refresh-queue.ts (cadence), the operator settings override in config/operator-settings-schema.ts, the dialog in packages/opencode/src/plugin/commands.ts.
Bump a published package scripts/release.sh (one command) + scripts/version-sync.mjs (writes the new version + cross-package pin). GitHub Actions re-tests, then publishes via OIDC.
Change the loopback RPC contract packages/opencode/src/rpc/protocol.ts + rpc-server.ts + rpc-client.ts + rpc/notifications.ts + rpc/port-file.ts. The TUI's entry.mjs loader does not change.
Add a TUI-rendered sidebar field sidebar-state.ts (schema + writer), tui.tsx (renderer). The TUI uses Bun's OpenTUI preload during dev (bunfig.toml), so no separate rebuild for dev iteration — only the scripts/build-tui.ts step for shipping.