import { RuntimeAgentService } from './runtime-agent.service.abstract.js'; import { RuntimeStartupBlockedError } from './runtime-startup-blocked.error.js'; import { SessionCommandHelper } from '../session/index.js'; import { type RuntimeType } from '../../constants.js'; import { type AntigravityProviderResult } from '../../utils/antigravity-settings.utils.js'; /** Injectable dependencies (tests). */ export interface AntigravityRuntimeDeps { /** The Gemini API key the session will run with, or null when there is none */ resolveApiKey?: () => Promise; /** Switch agy to the Gemini API key provider and trust the given folders */ ensureProvider?: (trustedPaths: string[]) => Promise; /** Crewly home dir (prompt files and skills live there) */ crewlyHome?: () => string; /** System temp dir (skills write screenshots and artifacts there) */ tmpDir?: () => string; } /** * The Gemini API key an Antigravity session gets: the key saved for * Antigravity in Settings → Runtimes, else a Gemini key from Crewly settings * (global or an antigravity-cli override), else GEMINI_API_KEY in the * backend's environment. * * @returns The key, or null when none is available */ export declare function resolveAntigravityApiKey(): Promise; /** * Whether a marker is on screen, also when the terminal wrapped it: agy's * plain-text messages (printed before the TUI starts) wrap at the PTY width, * so the marker is searched in the text as shown and with line breaks * removed. * * @param screen - Captured screen (ANSI already stripped) * @param marker - Text to find * @returns True when found */ export declare function antigravityScreenIncludes(screen: string, marker: string): boolean; /** * Classify a screen that agy cannot get past without the user, or that * Crewly must refuse (an account sign-in). * * @param screen - Captured screen * @returns The blocking error to throw, or null */ export declare function detectAntigravityStartupBlocker(screen: string): RuntimeStartupBlockedError | null; /** * Whether agy started on something other than the Gemini API key: its * banner (`Antigravity CLI 1.2.11`) is on screen but the `Gemini API key` * line that replaces the account email is not. Only meaningful once the * screen is fully painted (the idle footer is up): the two lines are drawn * together, but a capture can land between them while agy boots. * * @param screen - Captured screen (ANSI stripped) * @returns True for an account session */ export declare function isAntigravityAccountSession(screen: string): boolean; /** * Whether agy's folder-trust screen is showing. * * @param screen - Captured screen * @returns True when the "Do you trust the contents of this project?" screen is up */ export declare function isAntigravityTrustPrompt(screen: string): boolean; /** * The text currently typed in agy's prompt box: the lines between the last * two horizontal rules, without the `>` marker. The accept-edits * placeholder counts as empty. * * ``` * ───────────────────────────── ← top rule * > Read the file at /x.md ← input (may wrap onto more lines) * ───────────────────────────── ← bottom rule * ? for shortcuts … ← footer * ``` * * @param screen - Captured screen * @returns The typed text (whitespace-collapsed), '' when empty, or null when no prompt box is visible */ export declare function getAntigravityInputBoxText(screen: string): string | null; /** * Whether a message is still sitting in agy's prompt box (typed, not * submitted). Only the box counts: agy echoes every submitted message above * it as `> text`, which must not read as "stuck". * * @param screen - Captured screen * @param message - Message that was sent * @returns True when its start is still in the prompt box */ export declare function isTextInAntigravityInputBox(screen: string, message: string): boolean; /** * Google Antigravity CLI (`agy`) runtime service. * * Crewly drives agy's interactive TUI in a PTY, like the other harnesses. * The one hard rule: agy runs **only on a Gemini API key**. Google does not * allow third-party tools to use Antigravity (or Gemini CLI) product OAuth, * so before every launch Crewly * * 1. refuses to launch without a Gemini API key (RuntimeStartupBlockedError * `api_key_required`), and * 2. writes `"modelProvider": "gemini"` into agy's settings file (with the * agent's folder in `trustedWorkspaces`). The key reaches the session as * `GEMINI_API_KEY` in its spawn environment (harnessEnvForAgents). * * With that provider agy "never establishes an account session", even when * the machine has an account login in its keyring. If agy nevertheless shows * an account sign-in screen, or starts without the `Gemini API key` header, * the launch is refused (`account_login_refused`) instead of letting the * owner sign in. * * Launch: `agy --dangerously-skip-permissions --mode=accept-edits`, plus * `--add-dir` for the Crewly home (the init prompt file lives there) and the * temp dir (skill artifacts), `--model` / `--effort` for the member, and * `--conversation=` to resume. `AGY_CLI_DISABLE_AUTO_UPDATE=true` keeps * the self-updater from replacing the binary mid-task. * * Screen anatomy (agy 1.2.11, captured in a PTY): * * ``` * Antigravity CLI 1.2.11 * Gemini API key ← provider header * Gemini 3.1 Pro (Low) * ───────────────────────────────────────────── * > Accept-edits mode: file edits auto-approved (shift+tab to cycle) * ───────────────────────────────────────────── * ? for shortcuts accept-edits · Gemini 3.1 Pro · low ← idle footer * * > say hello ← echo of a submitted message * ⣯ Generating... ← busy * ──────────────── > ──────────────── * esc to cancel accept-edits · … ← busy footer * ``` * * Detection is a passive screen scrape: Ctrl+C at the prompt arms "press * ctrl+c again to exit" and `/` opens the command menu, so no key probes. */ export declare class AntigravityRuntimeService extends RuntimeAgentService { private readonly resolveApiKey; private readonly ensureProvider; private readonly crewlyHome; private readonly tmpDir; /** * @param sessionHelper - PTY session helper * @param projectRoot - Crewly install dir * @param deps - Injectable dependencies (tests) */ constructor(sessionHelper: SessionCommandHelper, projectRoot: string, deps?: AntigravityRuntimeDeps); protected getRuntimeType(): RuntimeType; /** * Folders agy gets with `--add-dir` at launch: the Crewly home (the init * prompt file and skills) and the temp dir (skill artifacts). agy's file * tools otherwise stay inside the workspace. * * @returns Absolute paths */ getLaunchWorkspaceDirs(): string[]; /** * Make sure agy can only start on the Gemini API key, then launch it. * * Claude-only launch arguments (`--agent`, a prompt file) are dropped: * agy has its own, unrelated `--agent` flag. * * @param sessionName - PTY session name * @param targetPath - Working directory for the session * @param runtimeFlags - Model / effort / resume flags * @param _promptFilePath - Ignored (Claude Code only) * @param _agentName - Ignored (Claude Code only) * @param resumeSessionId - Conversation being resumed (its flag is in runtimeFlags) * @throws RuntimeStartupBlockedError when no key is available or agy's settings cannot be set */ executeRuntimeInitScript(sessionName: string, targetPath?: string, runtimeFlags?: string[], _promptFilePath?: string, _agentName?: string, resumeSessionId?: string): Promise; /** * Pre-launch guard: a Gemini API key must exist and agy's settings must * select the Gemini provider (trusting the agent's folders). * * @param targetPath - The session's working directory * @throws RuntimeStartupBlockedError `api_key_required` | `settings_unreadable` */ prepareLaunch(targetPath?: string): Promise; /** * Wait for agy's idle prompt. * * Screens only the user may resolve (first-run terms, a missing key) and * screens Crewly must refuse (account sign-in) fail fast with a * RuntimeStartupBlockedError; an account session is also exited so it * does no work. The folder-trust screen is answered with its * pre-selected "Yes, I trust this folder" (the folder is normally * pre-trusted, so this is a fallback). * * @param sessionName - PTY session name * @param timeout - Overall timeout (ms) * @param checkInterval - Poll interval (ms) * @returns True when ready, false on timeout or a startup error * @throws RuntimeStartupBlockedError when start-up is blocked on the user */ waitForRuntimeReady(sessionName: string, timeout: number, checkInterval?: number): Promise; /** * Leave agy (Ctrl+C twice: the first arms "press ctrl+c again to exit"). * * @param sessionName - PTY session name */ private exitAntigravity; /** * Passive detection: agy's footer (idle or busy) is the last thing on * screen, with no exit hint after it. * * @param sessionName - PTY session name * @returns True when agy is running */ protected detectRuntimeSpecific(sessionName: string): Promise; /** * Text on screen once agy is up and taking input (or already busy, e.g. * a resumed conversation): the idle footer, the empty accept-edits * placeholder, or the busy footer. * * @returns Ready patterns */ protected getRuntimeReadyPatterns(): string[]; /** * Markers that veto readiness while the idle footer may still be painted. * * @returns Lower-case markers */ protected getNotReadyMarkers(): readonly string[]; /** * Exit patterns for RuntimeExitMonitorService (matched against the raw * output stream, which is not wrapped). agy prints its resume hint on * every clean exit, and refuses to start without the key. * * @returns Exit patterns */ protected getRuntimeExitPatterns(): RegExp[]; /** * Start-up error patterns (fail waitForRuntimeReady fast). Kept short so * an 80-column wrap cannot split them. * * @returns Error patterns */ protected getRuntimeErrorPatterns(): string[]; /** * Add folders to a running agy's workspace with `/add-dir ` (the * orchestrator's project folders). The Crewly home and temp dir were * added at launch. Waits for the idle prompt around each command so the * registration kickoff cannot land in the middle of one. * * @param sessionName - PTY session name * @param targetProjectPath - The session's working directory (already the workspace) * @param additionalAllowlistPaths - Extra folders to add */ postInitialize(sessionName: string, targetProjectPath?: string, additionalAllowlistPaths?: string[]): Promise; /** * Add one folder to a running agy's workspace, but only while it is idle * at its prompt: a slash command typed during a turn would be queued as a * message. Used when a project is added while the orchestrator runs. * * @param sessionName - PTY session name * @param dir - Folder to add * @returns True when the command was sent, false when agy was busy or the path is unusable */ addWorkspaceDir(sessionName: string, dir: string): Promise; /** * Wait (bounded) until agy shows its idle prompt. * * @param sessionName - PTY session name * @returns True when idle, false on timeout */ private waitForIdlePrompt; } //# sourceMappingURL=antigravity-runtime.service.d.ts.map