Skip to content

fix: model-aware sentinels, cache optimization, session alignment, and image model support#5

Closed
expiren wants to merge 5 commits into
cortexkit:mainfrom
expiren:pr/fixes-and-improvements
Closed

fix: model-aware sentinels, cache optimization, session alignment, and image model support#5
expiren wants to merge 5 commits into
cortexkit:mainfrom
expiren:pr/fixes-and-improvements

Conversation

@expiren

@expiren expiren commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports critical bug fixes and feature improvements from MITM-verified analysis against the real Antigravity IDE protocol.

Bug Fixes

  • Model-aware thinking sentinels: Gemini gets empty string sentinels (prevents dot echo), Claude gets dot sentinels (prevents proxy validation errors). Applied in both wrapped and unwrapped request paths.
  • Wrapped path sanitization: Added missing sanitizeRequestPayloadForAntigravity call in the wrapped request path — Claude requests were receiving empty string sentinels instead of dots.
  • Header stripping: Clear ALL inherited headers for Antigravity requests, rebuild with only Authorization, Content-Type, and User-Agent to prevent proxy fingerprint detection.
  • FNV-1a session ID: Replace UUID-based session IDs with deterministic FNV-1a 64-bit hash of project directory, matching the real IDE format. Applied in both request paths and search.ts.
  • Labels and toolConfig injection: Inject last_execution_id, trajectory_id, used_claude labels and toolConfig.functionCallingConfig.mode: VALIDATED inside requestPayload, not the outer envelope. Applied in both wrapped and unwrapped paths.
  • providerOptions strip: Delete providerOptions from request payload after extracting thinking config to prevent proxy fingerprint detection.
  • Error message prefix: All 11 createSyntheticErrorResponse call sites now prefixed with [Antigravity Error] so users can distinguish plugin errors from AI responses.
  • Opus cache optimization: Gate isClaudeThinking on headerStyle !== "antigravity" — the proxy handles all thinking management server-side. Client-side thinking mutations were busting prefix cache (60-70% hit rate). After fix: 97-100% cache hit rate.

Features

  • Ineligible account auto-disable: 403 responses containing "not eligible" / "ineligible" / "access denied" permanently disable the account and persist to disk. Prevents infinite retry loops against dead accounts.
  • Dead code cleanup: Removed unused CAPACITY_BACKOFF_TIERS_MS, getCapacityBackoffDelay, and resetAllRateLimitStateForAccount.
  • Decoupled model definitions: Multi-source model loading from ~/.config/opencode/antigravity-models.json(c) (user-level) and .opencode/antigravity-models.json(c) (project-level) with JSONC support. Fixed merge order: built-in defaults < decoupled models < user opencode.json (highest priority). Dynamic whitelist via Object.keys().
  • Image model whitelist: Added antigravity-gemini-3.1-flash-image to OpenCode model IDs (definition and image-saver already existed).

Verification

  • All changes MITM-verified in production against real Antigravity proxy
  • Claude Opus cache: 60-70% -> 97-100% hit rate
  • Claude Sonnet cache: 99% (unchanged)
  • Gemini cache: 93-98% (unchanged)
  • All 856 tests pass (1 pre-existing agy-transport timeout failure)
  • TypeScript strict mode clean across all 3 packages

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Aligns request formatting and session handling with the real Antigravity IDE to improve reliability and cache hit rates. Adds decoupled model loading and enables image model support, plus auto-disabling of ineligible accounts.

  • Bug Fixes

    • Model-aware sentinels: Gemini uses empty text (""), Claude uses "."; applied in wrapped and unwrapped paths.
    • Wrapped path now sanitizes payloads to apply the correct sentinel.
    • Antigravity mode headers rebuilt from scratch: only Authorization, Content-Type, and User-Agent (no inherited/SDK headers).
    • Deterministic FNV‑1a 64‑bit sessionId from the project directory; used across requests and search.
    • Inject labels and toolConfig.functionCallingConfig.mode: VALIDATED inside the request payload; stripped providerOptions after reading thinking config.
    • Prefixed all synthetic errors with "[Antigravity Error]" for easier user debugging.
    • Gated Claude thinking to gemini-cli only; prevents cache busting and restores 97–100% Opus prefix cache hit rate.
  • New Features

    • Auto-disable ineligible accounts on 403 responses mentioning "not eligible"/"ineligible"/"access denied"; persists to disk and surfaces a toast once.
    • Decoupled model definitions loaded from ~/.config/opencode/antigravity-models.json(c) and .opencode/antigravity-models.json(c); merge order: built-ins < decoupled models < opencode.json.
    • Provider catalog now whitelists the union of all registered models to avoid pruning.
    • Added antigravity-gemini-3.1-flash-image to the OpenCode model IDs.

Written for commit 1d61562. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 11 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/src/plugin/config/loader.ts">

<violation number="1" location="packages/opencode/src/plugin/config/loader.ts:127">
P2: Model metadata containing `,}` or `,]` is silently changed while loading JSONC files because this replacement also matches inside quoted values. Strip trailing commas only outside JSON strings (or use a JSONC parser) so valid model fields remain intact.</violation>
</file>

<file name="packages/opencode/src/plugin/accounts.ts">

<violation number="1" location="packages/opencode/src/plugin/accounts.ts:162">
P2: Ineligibility metadata is lost on the first disk save, so after restart `isAccountIneligible()` returns false and the permanent-disable reason/timestamp cannot be recovered. Include these three fields in both the storage snapshot and loaded-account mapping.</violation>
</file>

<file name="packages/opencode/src/plugin.ts">

<violation number="1" location="packages/opencode/src/plugin.ts:2554">
P2: Some recoverable 403 responses can now permanently disable an account because the ineligibility matcher includes the generic phrase `access denied`. Tightening that check to explicit ineligibility phrases avoids false permanent disablement and preserves normal retry/fallback behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

/\\"|"(?:\\"|[^"])*"|(\/{2}.*|\/\*[\s\S]*?\*\/)/g,
(match: string, group: string | undefined) => (group ? "" : match)
)
.replace(/,(\s*[}\]])/g, "$1");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Model metadata containing ,} or ,] is silently changed while loading JSONC files because this replacement also matches inside quoted values. Strip trailing commas only outside JSON strings (or use a JSONC parser) so valid model fields remain intact.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/plugin/config/loader.ts, line 127:

<comment>Model metadata containing `,}` or `,]` is silently changed while loading JSONC files because this replacement also matches inside quoted values. Strip trailing commas only outside JSON strings (or use a JSONC parser) so valid model fields remain intact.</comment>

<file context>
@@ -118,6 +118,56 @@ function mergeConfigs(
+      /\\"|"(?:\\"|[^"])*"|(\/{2}.*|\/\*[\s\S]*?\*\/)/g,
+      (match: string, group: string | undefined) => (group ? "" : match)
+    )
+    .replace(/,(\s*[}\]])/g, "$1");
+}
+
</file context>

verificationRequiredReason?: string;
verificationUrl?: string;
/** Account permanently ineligible for Antigravity */
ineligible?: boolean;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Ineligibility metadata is lost on the first disk save, so after restart isAccountIneligible() returns false and the permanent-disable reason/timestamp cannot be recovered. Include these three fields in both the storage snapshot and loaded-account mapping.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/plugin/accounts.ts, line 162:

<comment>Ineligibility metadata is lost on the first disk save, so after restart `isAccountIneligible()` returns false and the permanent-disable reason/timestamp cannot be recovered. Include these three fields in both the storage snapshot and loaded-account mapping.</comment>

<file context>
@@ -158,6 +158,10 @@ export interface ManagedAccount {
   verificationRequiredReason?: string;
   verificationUrl?: string;
+  /** Account permanently ineligible for Antigravity */
+  ineligible?: boolean;
+  ineligibleAt?: number;
+  ineligibleReason?: string;
</file context>

lowerBody.includes("not eligible") ||
lowerBody.includes("ineligible") ||
lowerBody.includes("not available for your account") ||
lowerBody.includes("access denied")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Some recoverable 403 responses can now permanently disable an account because the ineligibility matcher includes the generic phrase access denied. Tightening that check to explicit ineligibility phrases avoids false permanent disablement and preserves normal retry/fallback behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/plugin.ts, line 2554:

<comment>Some recoverable 403 responses can now permanently disable an account because the ineligibility matcher includes the generic phrase `access denied`. Tightening that check to explicit ineligibility phrases avoids false permanent disablement and preserves normal retry/fallback behavior.</comment>

<file context>
@@ -2558,6 +2544,35 @@ export const createAntigravityPlugin = (providerId: string) => async (
+                    lowerBody.includes("not eligible") ||
+                    lowerBody.includes("ineligible") ||
+                    lowerBody.includes("not available for your account") ||
+                    lowerBody.includes("access denied")
+                  );
+
</file context>

@ualtinok

Copy link
Copy Markdown
Contributor

Thanks for the captures and detailed write-up. I audited the branch commit by commit and reimplemented the parts confirmed by fresh agy CLI 1.1.3 and live OpenCode traffic, rather than merging the branch wholesale.

Landed separately:

  • clean AGY headers, cache-field sanitization, VALIDATED tool config, labels, and structured request IDs: e32b688
  • per-session conversation/trajectory state, payload-derived step indexes, account pinning, and parent/child isolation: 0327ecd
  • narrow persistent account-ineligibility handling with explicit recovery: 7d635d7
  • live-verified image and GPT-OSS routes: f5bc836

The remaining branch behavior was not retained because it used request state that was unsafe under concurrent sessions, included labels/model metadata that did not match the fresh captures, disabled accounts using broad text matching, and added duplicate model-configuration sources. I also kept the OpenCode Claude cache-boundary adaptation: a live Opus tool-turn A/B measured 98.6% cached with it versus 89.2% after removing it. The proposed sentinel changes likewise did not match live signed Claude/Gemini replay.

All replacement changes passed the full workspace suite and live OpenCode/Pi verification.

@ualtinok ualtinok closed this Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants