Skip to content

Add Antigravity provider support#134

Draft
sak0a wants to merge 1 commit into
mainfrom
feat/add-antigravity-provider-support
Draft

Add Antigravity provider support#134
sak0a wants to merge 1 commit into
mainfrom
feat/add-antigravity-provider-support

Conversation

@sak0a

@sak0a sak0a commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a new antigravity provider driver and adapter that runs the Antigravity CLI, projects its output into Ryco runtime events, and supports session lifecycle, interruption, rollback, and resume cursor handling.
  • Add provider snapshot, model discovery, runtime, and text-generation support for Antigravity, including version/advisory checks and native event logging.
  • Update shared contracts and UI surfaces so Antigravity appears in provider management, model picking, icons, and session logic.
  • Add focused tests for Antigravity model parsing and runtime/provider behavior.

Testing

  • Not run - bun fmt
  • Not run - bun lint
  • Not run - bun typecheck
  • Not run - bun run test

Summary by CodeRabbit

Release Notes

  • New Features
    • Added Antigravity as a new AI provider option for text generation and model selection.
    • Configure Antigravity instances with custom binary paths, home directories, and model settings.
    • Support in-session model switching within Antigravity provider sessions.
    • Integrated Antigravity into provider selection UI and settings management (marked as Early Access).

- Add Antigravity driver, adapter, and runtime integration
- Expose Antigravity models and provider settings in the UI
- Update contracts and tests for Antigravity sessions
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a complete Antigravity provider to the Ryco system, spanning runtime CLI execution, session management, streaming text deltas, text generation for multiple content types, driver orchestration, and web UI integration.

Changes

Antigravity Provider Implementation

Layer / File(s) Summary
Contracts and settings schema
packages/contracts/src/settings.ts, packages/contracts/src/model.ts, packages/contracts/src/providerRuntime.ts
New AntigravitySettings schema defines binary path, home directory, CLI args, print timeout, and custom models. Model defaults and display names are wired into contract exports. Runtime event sources are extended with "antigravity.cli".
Runtime foundation: CLI execution and delta streaming
apps/server/src/provider/antigravityRuntime.ts, apps/server/src/provider/antigravityRuntime.test.ts
Core orchestrator runAntigravityPrompt spawns the Antigravity CLI, polls its SQLite conversation database for step deltas, extracts embedded text, applies narration filtering, handles stdout fallback, and emits callbacks for streaming. Includes protobuf-like field parsing, conversation ID discovery, and comprehensive error typing. Tests validate model parsing, CLI arg building, stdout fallback, and streamed delta aggregation.
Provider status checking and model discovery
apps/server/src/provider/Layers/AntigravityProvider.ts, apps/server/src/provider/Layers/AntigravityProvider.test.ts
Probes Antigravity CLI version and model availability, validates version against minimum requirement, categorizes failures (missing CLI, execution, timeout, auth), and builds ServerProviderDraft with discovered models merged with custom models. Test verifies model deduplication and auto model ordering.
Adapter: session and turn management
apps/server/src/provider/Layers/AntigravityAdapter.ts, apps/server/src/provider/Services/AntigravityAdapter.ts
Session-based adapter manages per-thread state including resume cursors, turn snapshots, and conversation state. Implements turn execution via runAntigravityPrompt, streaming content.delta events, session/thread lifecycle events, interrupt handling, and conversation history rollback. Validates instance binding and emits comprehensive runtime events.
Text generation: multi-operation support
apps/server/src/textGeneration/AntigravityTextGeneration.ts, apps/server/src/textGeneration/TextGeneration.ts
Text generation for commit messages, PR content, branch names, thread titles, and issue content. Centralizes JSON result parsing and schema validation in runAntigravityJson, sanitizes output fields, and supports mode-dependent issue content generation (title-only or title+body). Extended TextGenerationProvider union to include "antigravity".
Driver: orchestrate adapter, text generation, and snapshots
apps/server/src/provider/Drivers/AntigravityDriver.ts
Composes adapter, text generation, and provider status checking into a managed ProviderInstance. Handles instance stamping with display metadata, environment merging, maintenance capability resolution, and snapshot enrichment via version advisory. Wraps snapshot-building failures into ProviderDriverError.
Built-in driver registration and system integration
apps/server/src/provider/builtInDrivers.ts, apps/server/src/provider/providerSnapshot.ts
Registers AntigravityLazyDriver in the built-in drivers list with dynamic import error handling. Updates spawnAndCollect to drain child process stdin alongside stdout/stderr collection.
UI: provider icons, definitions, and options
apps/web/src/components/chat/providerIconUtils.ts, apps/web/src/components/settings/providerDriverMeta.ts, apps/web/src/session-logic.ts, apps/web/src/components/chat/ModelPickerSidebar.tsx, apps/web/src/components/settings/AddProviderInstanceDialog.tsx, apps/web/src/components/KeybindingsToast.browser.tsx
Adds Antigravity icon mapping, client provider definition with Early Access badge, provider option with new sidebar badge, removes Gemini coming-soon UI, and updates test fixtures with Antigravity configuration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • sak0a/ryco#71: This PR wires the Antigravity driver to provider maintenance capabilities and snapshot enrichment infrastructure that was introduced in that PR.

Suggested labels

vouch:trusted, size:XXL

Poem

🐰 A fluffy new provider bounds in with grace,
Antigravity swiftly takes its rightful place,
From CLI to sessions, with deltas that stream,
Text generation blooms like a developer's dream,
UI sparkles bright with an "Early Access" sheen—
The finest provider addition we've seen! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description provides a clear summary of changes, but the testing checklist shows all tests marked as 'Not run', indicating they were not executed before submission. Run the listed test commands (bun fmt, bun lint, bun typecheck, bun run test) before merging to ensure code quality and correctness.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Antigravity provider support' clearly summarizes the main change - adding a new Antigravity provider implementation across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-antigravity-provider-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Jun 9, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/server/src/provider/antigravityRuntime.test.ts`:
- Line 214: Remove the leftover debug logging by deleting the console.log({
deltas, activities, result }) statement in the antigravityRuntime.test.ts test;
if you still need to inspect values during local debugging, replace it with a
conditional debug/logger call (e.g., a dev-only logger or wrap in a flag) or add
proper assertions against deltas, activities, and result so tests remain silent
in CI.

In `@apps/server/src/provider/antigravityRuntime.ts`:
- Around line 378-433: Replace the unsafe directory-diff binding
(beforeSnapshot/newConversationId against conversationsDir inside
pollForConversationDelta) with a deterministic per-run binding: generate a
unique run-scoped identifier at prompt start (e.g., runId) and either (A)
create/use an isolated per-run conversations directory (derived from runId) and
pass it into buildAntigravityPromptArgs and ChildProcess.spawn env, or (B) pass
runId through the Antigravity CLI args/env and use that to associate deltas
(store/run observedConversationId from the CLI-provided identifier rather than
discovering via newConversationId). Update locations referencing beforeSnapshot,
newConversationId, conversationsDir, and observedConversationId to use the new
run-scoped identifier and remove the snapshot/diff logic.

In `@apps/server/src/provider/Layers/AntigravityAdapter.ts`:
- Around line 815-830: The rollbackThread implementation truncates context.turns
but doesn't update continuation state; after performing
context.turns.splice(nextLength) recompute the continuation based on the new
tail (or set to { lastStepIdx: 0 } if context.turns is now empty) and persist
that continuation into both context.conversationState (e.g.
conversationState.continuation) and context.session.resumeCursor so subsequent
sendTurn uses the truncated state; update the code in rollbackThread to derive
the new continuation from the last remaining turn (or default) and assign it to
context.conversationState and context.session.resumeCursor before returning.
- Around line 736-751: interruptTurn currently emits turn.aborted immediately
and for arbitrary turnIds, causing duplicates with sendTurn; change
interruptTurn to validate that the target turn equals context.activeTurnId
before mutating state or killing the process and do not emit the turn.aborted
event here. Specifically, in interruptTurn use targetTurnId = turnId ??
context.activeTurnId, only add to context.interruptedTurnIds and invoke yield*
activeProcess.kill when targetTurnId === context.activeTurnId (and activeProcess
exists), and remove the yield* emit({ ... buildEventBase(...), type:
"turn.aborted", ... }) block; instead ensure sendTurn reads
context.interruptedTurnIds/context.activeTurnId and is the single place that
emits the turn.aborted event once when an interrupted active turn fails.

In `@packages/contracts/src/settings.ts`:
- Around line 376-378: The provider description text under the
providerSettingsForm object incorrectly references ".gemini auth"; update the
description string (the description property near providerSettingsForm) to the
correct term for Antigravity (e.g., ".antigravity auth" or a neutral phrase like
"instance auth and conversation state") so the UI/help text accurately reflects
Antigravity settings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e5c5429c-f23b-4ce2-a3ce-b8c766733b82

📥 Commits

Reviewing files that changed from the base of the PR and between c0b7951 and 7d2c43b.

📒 Files selected for processing (20)
  • apps/server/src/provider/Drivers/AntigravityDriver.ts
  • apps/server/src/provider/Layers/AntigravityAdapter.ts
  • apps/server/src/provider/Layers/AntigravityProvider.test.ts
  • apps/server/src/provider/Layers/AntigravityProvider.ts
  • apps/server/src/provider/Services/AntigravityAdapter.ts
  • apps/server/src/provider/antigravityRuntime.test.ts
  • apps/server/src/provider/antigravityRuntime.ts
  • apps/server/src/provider/builtInDrivers.ts
  • apps/server/src/provider/providerSnapshot.ts
  • apps/server/src/textGeneration/AntigravityTextGeneration.ts
  • apps/server/src/textGeneration/TextGeneration.ts
  • apps/web/src/components/KeybindingsToast.browser.tsx
  • apps/web/src/components/chat/ModelPickerSidebar.tsx
  • apps/web/src/components/chat/providerIconUtils.ts
  • apps/web/src/components/settings/AddProviderInstanceDialog.tsx
  • apps/web/src/components/settings/providerDriverMeta.ts
  • apps/web/src/session-logic.ts
  • packages/contracts/src/model.ts
  • packages/contracts/src/providerRuntime.ts
  • packages/contracts/src/settings.ts

}),
}).pipe(Effect.provide(NodeServices.layer));

console.log({ deltas, activities, result });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove leftover debug logging from test.

This console.log adds noisy CI/test output without assertion value.

Suggested cleanup
-      console.log({ deltas, activities, result });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log({ deltas, activities, result });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/antigravityRuntime.test.ts` at line 214, Remove the
leftover debug logging by deleting the console.log({ deltas, activities, result
}) statement in the antigravityRuntime.test.ts test; if you still need to
inspect values during local debugging, replace it with a conditional
debug/logger call (e.g., a dev-only logger or wrap in a flag) or add proper
assertions against deltas, activities, and result so tests remain silent in CI.

Comment on lines +378 to +433
const beforeSnapshot =
input.state.conversationId === undefined ? conversationSnapshot(conversationsDir) : undefined;
const args = buildAntigravityPromptArgs({
settings: input.settings,
cwd: input.cwd,
prompt: input.prompt,
runtimeMode: input.runtimeMode,
model: input.model,
conversationId: input.state.conversationId,
});

const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
const child = yield* spawner
.spawn(
ChildProcess.make(input.settings.binaryPath || "agy", [...args], {
cwd: input.cwd,
env: environment,
shell: process.platform === "win32",
}),
)
.pipe(
Effect.mapError(
(cause) =>
new AntigravityRuntimeError({
operation: "spawn",
detail: `Failed to spawn Antigravity CLI: ${cause.message}`,
cause,
}),
),
);
const processHandle: AntigravityPromptProcess = {
kill: child.kill({ killSignal: "SIGTERM", forceKillAfter: "1 second" }).pipe(Effect.ignore),
};
if (input.onProcessStart) {
yield* input.onProcessStart(processHandle);
}
yield* Effect.addFinalizer(() =>
processHandle.kill.pipe(Effect.andThen(input.onProcessExit ?? Effect.void)),
);

const emitActivity = (summary: string) =>
input.onActivity
? input.onActivity({
summary,
elapsedMs: Math.max(0, Date.now() - startedAt),
...(observedConversationId ? { conversationId: observedConversationId } : {}),
})
: Effect.void;

const pollForConversationDelta = Effect.gen(function* () {
const now = Date.now();
if (observedConversationId === undefined && beforeSnapshot !== undefined) {
const discoveredConversationId = newConversationId(conversationsDir, beforeSnapshot);
if (discoveredConversationId !== undefined) {
observedConversationId = discoveredConversationId;
yield* emitActivity("Antigravity conversation started");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Conversation auto-discovery is not safe under concurrent runs.

beforeSnapshot/newConversationId(...) can bind this run to another run’s new .db when multiple prompts share the same conversations dir. That can route deltas to the wrong thread and leak content across sessions.

Please switch to a deterministic per-run binding strategy (for example: isolated per-session HOME/conversations dir, or another unique run-scoped identifier propagated through the run lifecycle) instead of directory diffing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/antigravityRuntime.ts` around lines 378 - 433,
Replace the unsafe directory-diff binding (beforeSnapshot/newConversationId
against conversationsDir inside pollForConversationDelta) with a deterministic
per-run binding: generate a unique run-scoped identifier at prompt start (e.g.,
runId) and either (A) create/use an isolated per-run conversations directory
(derived from runId) and pass it into buildAntigravityPromptArgs and
ChildProcess.spawn env, or (B) pass runId through the Antigravity CLI args/env
and use that to associate deltas (store/run observedConversationId from the
CLI-provided identifier rather than discovering via newConversationId). Update
locations referencing beforeSnapshot, newConversationId, conversationsDir, and
observedConversationId to use the new run-scoped identifier and remove the
snapshot/diff logic.

Comment on lines +736 to +751
const targetTurnId = turnId ?? context.activeTurnId;
if (targetTurnId) {
context.interruptedTurnIds.add(targetTurnId);
}
const activeProcess = context.activeProcess;
if (activeProcess) {
yield* activeProcess.kill;
}
if (targetTurnId) {
yield* emit({
...(yield* buildEventBase({ threadId, turnId: targetTurnId })),
type: "turn.aborted",
payload: {
reason: "Interrupted by user.",
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

interruptTurn can emit incorrect/duplicate abort events.

This method emits turn.aborted immediately, while sendTurn also emits turn.aborted on interrupted failure. It also allows emitting abort for an arbitrary non-active turnId.

Validate interruption against the active turn and centralize abort emission to one path.

Suggested direction
 const interruptTurn: AntigravityAdapterShape["interruptTurn"] = Effect.fn("interruptTurn")(
   function* (threadId, turnId) {
     const context = yield* requireSession(threadId);
-    const targetTurnId = turnId ?? context.activeTurnId;
-    if (targetTurnId) {
-      context.interruptedTurnIds.add(targetTurnId);
-    }
+    const targetTurnId = context.activeTurnId;
+    if (!targetTurnId) return;
+    if (turnId !== undefined && turnId !== targetTurnId) return;
+    context.interruptedTurnIds.add(targetTurnId);
     const activeProcess = context.activeProcess;
     if (activeProcess) {
       yield* activeProcess.kill;
     }
-    if (targetTurnId) {
-      yield* emit({
-        ...(yield* buildEventBase({ threadId, turnId: targetTurnId })),
-        type: "turn.aborted",
-        payload: {
-          reason: "Interrupted by user.",
-        },
-      });
-    }
   },
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const targetTurnId = turnId ?? context.activeTurnId;
if (targetTurnId) {
context.interruptedTurnIds.add(targetTurnId);
}
const activeProcess = context.activeProcess;
if (activeProcess) {
yield* activeProcess.kill;
}
if (targetTurnId) {
yield* emit({
...(yield* buildEventBase({ threadId, turnId: targetTurnId })),
type: "turn.aborted",
payload: {
reason: "Interrupted by user.",
},
});
const targetTurnId = context.activeTurnId;
if (!targetTurnId) return;
if (turnId !== undefined && turnId !== targetTurnId) return;
context.interruptedTurnIds.add(targetTurnId);
const activeProcess = context.activeProcess;
if (activeProcess) {
yield* activeProcess.kill;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/AntigravityAdapter.ts` around lines 736 -
751, interruptTurn currently emits turn.aborted immediately and for arbitrary
turnIds, causing duplicates with sendTurn; change interruptTurn to validate that
the target turn equals context.activeTurnId before mutating state or killing the
process and do not emit the turn.aborted event here. Specifically, in
interruptTurn use targetTurnId = turnId ?? context.activeTurnId, only add to
context.interruptedTurnIds and invoke yield* activeProcess.kill when
targetTurnId === context.activeTurnId (and activeProcess exists), and remove the
yield* emit({ ... buildEventBase(...), type: "turn.aborted", ... }) block;
instead ensure sendTurn reads context.interruptedTurnIds/context.activeTurnId
and is the single place that emits the turn.aborted event once when an
interrupted active turn fails.

Comment on lines +815 to +830
const rollbackThread: AntigravityAdapterShape["rollbackThread"] = Effect.fn("rollbackThread")(
function* (threadId, numTurns) {
const context = yield* requireSession(threadId);
if (!Number.isInteger(numTurns) || numTurns < 1) {
return yield* new ProviderAdapterValidationError({
provider: PROVIDER,
operation: "rollbackThread",
issue: "numTurns must be an integer >= 1.",
});
}
const nextLength = Math.max(0, context.turns.length - numTurns);
context.turns.splice(nextLength);
return {
threadId,
turns: context.turns,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Rollback truncates turns but does not rollback continuation cursor/session state.

After context.turns.splice(...), context.conversationState and context.session.resumeCursor still reference the pre-rollback position. The next sendTurn will continue from removed state.

Please recompute continuation from the new tail turn (or reset to { lastStepIdx: 0 } when empty) and persist it back into both context.conversationState and ProviderSession.resumeCursor.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/AntigravityAdapter.ts` around lines 815 -
830, The rollbackThread implementation truncates context.turns but doesn't
update continuation state; after performing context.turns.splice(nextLength)
recompute the continuation based on the new tail (or set to { lastStepIdx: 0 }
if context.turns is now empty) and persist that continuation into both
context.conversationState (e.g. conversationState.continuation) and
context.session.resumeCursor so subsequent sendTurn uses the truncated state;
update the code in rollbackThread to derive the new continuation from the last
remaining turn (or default) and assign it to context.conversationState and
context.session.resumeCursor before returning.

Comment on lines +376 to +378
description:
"Custom HOME used when running this Antigravity instance. Keeps .gemini auth and conversation state separate.",
providerSettingsForm: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix provider description text mismatch for Antigravity.

Line 377 mentions “.gemini auth” inside Antigravity settings, which is misleading in the UI/help text and likely a copy/paste artifact.

Suggested diff
-          "Custom HOME used when running this Antigravity instance. Keeps .gemini auth and conversation state separate.",
+          "Custom HOME used when running this Antigravity instance. Keeps Antigravity auth and conversation state separate.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
description:
"Custom HOME used when running this Antigravity instance. Keeps .gemini auth and conversation state separate.",
providerSettingsForm: {
description:
"Custom HOME used when running this Antigravity instance. Keeps Antigravity auth and conversation state separate.",
providerSettingsForm: {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/contracts/src/settings.ts` around lines 376 - 378, The provider
description text under the providerSettingsForm object incorrectly references
".gemini auth"; update the description string (the description property near
providerSettingsForm) to the correct term for Antigravity (e.g., ".antigravity
auth" or a neutral phrase like "instance auth and conversation state") so the
UI/help text accurately reflects Antigravity settings.

@sak0a sak0a marked this pull request as draft June 10, 2026 02:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant