fix(antigravity): publish captured command approvals to Chat

This commit is contained in:
Neil
2026-09-20 02:36:04 -07:00
parent b4e4c264e9
commit 634ca8fc49
22 changed files with 1133 additions and 3 deletions
@@ -371,3 +371,31 @@ without transcript truncation. The final identity-checked implementation passed
another delayed launch with an exact 920-character first-turn match. Generation
still failed with the existing 401 authentication error; this verifies delivery,
not a successful continuation task. Real Windows/WSL/SSH runs remain unverified.
## Native Windows command approval — 1.2.7 (2026-09-20)
`antigravity-windows-command-approval.txt` records an actual authenticated tool
turn, stopped while the four-choice “Run this command?” dialog owns the screen.
`antigravity-windows-command-cancelled.txt` records another harmless command turn
and Escape dismissing that dialog back to the empty composer. Both used the
repository PTY recorder on native Windows at 120×40, with account information
hidden. The command executable's OS username is replaced with a same-length
placeholder in the transcript and sidecar. Both pass the transcript secret scan.
The approval screen ends with the navigation hint and `esc to cancel`; its four
choices distinguish it from ordinary working output. The runtime regression
previously timed out without a blocked reason. The current screen classifier
reports `agent-approval-prompt`; the cancelled capture must become ready even
though its retained output contains the earlier menu.
This establishes readiness detection only. Hook-owned permission publication,
Chat approval controls, selection changes, narrower widths and other platforms
still need verification. Do not infer permission from `PreToolUse`: agy can
execute an already-allowed tool without waiting for a user decision.
`antigravity-windows-command-allow-key.txt` additionally records the pending menu,
sending the single key `1` (no Enter), successful harmless command execution and
return to the empty composer. This verifies the existing Chat approval card's
one-time acceptance key on native Windows 1.2.7. The recorder metadata confirms
completion; the capture was transferred as raw UTF-8 bytes without newline
normalization and its username was replaced with a same-length placeholder.
@@ -0,0 +1,126 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AgentHookServer, _internals } from './server'
import { PANE } from './server.test-fixtures'
vi.mock('../telemetry/client', () => ({ track: vi.fn() }))
vi.mock('../telemetry/cohort-classifier', () => ({ getCohortAtEmit: vi.fn(() => ({})) }))
beforeEach(() => _internals.resetCachesForTests())
function workingPane(
agentType = 'antigravity',
connectionId: string | null = null
): AgentHookServer {
const server = new AgentHookServer()
server.ingestTerminalStatus({
paneKey: PANE,
terminalHandle: 'term-screen',
connectionId,
payload: { state: 'working', agentType, prompt: 'print marker', toolName: 'run_command' }
})
return server
}
function baseline(server: AgentHookServer) {
const row = server.getStatusSnapshot()[0]
if (!row) {
throw new Error('Missing test status row')
}
return row
}
describe('host-owned Antigravity screen permission', () => {
it('publishes permission through the canonical store and clears only its own prompt', () => {
const server = workingPane()
const listener = vi.fn()
server.setListener(listener)
listener.mockClear()
expect(
server.ingestAntigravityScreenPermission({ baseline: baseline(server), command: 'echo OK' })
).toBe(true)
expect(baseline(server)).toMatchObject({
state: 'waiting',
toolName: 'run_command',
observation: { origin: 'process' }
})
expect(JSON.parse(baseline(server).interactivePrompt ?? '')).toEqual({
approval: { source: 'antigravity-screen', tool: 'run_command', summary: 'echo OK' }
})
expect(listener).toHaveBeenCalledTimes(1)
expect(
server.ingestAntigravityScreenPermission({ baseline: baseline(server), command: null })
).toBe(true)
expect(baseline(server)).toMatchObject({ state: 'working', prompt: 'print marker' })
expect(baseline(server).interactivePrompt).toBeUndefined()
expect(listener).toHaveBeenCalledTimes(2)
})
it('does not overwrite a newer provider state with a late snapshot', () => {
const server = workingPane()
const old = baseline(server)
server.ingestTerminalStatus({
paneKey: PANE,
terminalHandle: 'term-screen',
payload: { state: 'done', agentType: 'antigravity', prompt: 'print marker' }
})
expect(server.ingestAntigravityScreenPermission({ baseline: old, command: 'echo OK' })).toBe(
false
)
expect(baseline(server).state).toBe('done')
})
it('rejects another terminal incarnation and missing observation proof', () => {
const server = workingPane()
const row = baseline(server)
expect(
server.ingestAntigravityScreenPermission({
baseline: { ...row, terminalHandle: 'replaced' },
command: 'echo OK'
})
).toBe(false)
expect(
server.ingestAntigravityScreenPermission({
baseline: { ...row, observation: undefined },
command: 'echo OK'
})
).toBe(false)
})
it.each([
['claude', null],
['antigravity', 'ssh-connection']
] as const)('does not infer permission for %s on %s', (agent, connection) => {
const server = workingPane(agent, connection)
expect(
server.ingestAntigravityScreenPermission({ baseline: baseline(server), command: 'echo OK' })
).toBe(false)
})
it('does not clear a permission prompt from another producer', () => {
const server = workingPane()
server.ingestTerminalStatus({
paneKey: PANE,
terminalHandle: 'term-screen',
payload: {
state: 'waiting',
agentType: 'antigravity',
prompt: 'print marker',
interactivePrompt: '{"questions":[]}'
}
})
expect(
server.ingestAntigravityScreenPermission({ baseline: baseline(server), command: null })
).toBe(false)
expect(baseline(server).state).toBe('waiting')
})
it('coalesces an unchanged permission observation without refreshing its revision', () => {
const server = workingPane()
server.ingestAntigravityScreenPermission({ baseline: baseline(server), command: 'echo OK' })
const waiting = baseline(server)
expect(
server.ingestAntigravityScreenPermission({ baseline: waiting, command: 'echo OK' })
).toBe(true)
expect(baseline(server).observation).toEqual(waiting.observation)
})
})
@@ -0,0 +1,78 @@
import type { AgentStatusIpcPayload } from '../../../shared/agent-status-types'
import type { EnrichedAgentHookEventPayload } from './server-types'
import { AgentHookServerIngestNormalization } from './server-ingest-normalization'
export type AntigravityScreenPermissionObservation = {
baseline: Pick<AgentStatusIpcPayload, 'paneKey' | 'terminalHandle' | 'observation'>
command: string | null
}
const SCREEN_APPROVAL_PREFIX = '{"approval":{"source":"antigravity-screen",'
export abstract class AgentHookServerIngestAntigravityScreen extends AgentHookServerIngestNormalization {
ingestAntigravityScreenPermission(request: AntigravityScreenPermissionObservation): boolean {
const { baseline, command } = request
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: This server's canonical row writer stores enriched events in the shared listener map.
const previous = this.state.lastStatusByPaneKey.get(baseline.paneKey) as
| EnrichedAgentHookEventPayload
| undefined
const expected = baseline.observation
const current = previous?.observation
if (
!previous ||
previous.restoredUnconfirmed ||
previous.connectionId ||
previous.payload.agentType !== 'antigravity' ||
!baseline.terminalHandle ||
previous.terminalHandle !== baseline.terminalHandle ||
!expected ||
!current ||
current.authorityId !== expected.authorityId ||
current.incarnation !== expected.incarnation ||
current.revision !== expected.revision ||
this.getAgentStatusDisposition(baseline.paneKey) !== 'accept'
) {
return false
}
const ownsPrompt =
current.origin === 'process' &&
previous.payload.state === 'waiting' &&
previous.payload.interactivePrompt?.startsWith(SCREEN_APPROVAL_PREFIX) === true
if (command === null ? !ownsPrompt : previous.payload.state !== 'working' && !ownsPrompt) {
return false
}
const interactivePrompt =
command === null
? undefined
: JSON.stringify({
approval: {
source: 'antigravity-screen',
tool: previous.payload.toolName ?? 'run_command',
summary: command
}
})
if (interactivePrompt === previous.payload.interactivePrompt) {
return true
}
// A cleared dialog proves only that permission ended; completion still comes from the provider.
return (
this.applyNormalizedStatus(
{
paneKey: previous.paneKey,
tabId: previous.tabId,
worktreeId: previous.worktreeId,
connectionId: previous.connectionId,
terminalHandle: previous.terminalHandle,
providerSession: previous.providerSession,
payload: {
...previous.payload,
state: command === null ? 'working' : 'waiting',
interactivePrompt
}
},
undefined,
'process'
) !== undefined
)
}
}
@@ -4,9 +4,9 @@ import { parseLegacyNumericPaneKey, parsePaneKey } from '../../../shared/stable-
import { terminalStatusPayloadMatchesHook } from '../../../shared/agent-terminal-status-equivalence'
import type { ParsedAgentStatusPayload } from '../../../shared/agent-status-types'
import type { EnrichedAgentHookEventPayload } from './server-types'
import { AgentHookServerIngestNormalization } from './server-ingest-normalization'
import { AgentHookServerIngestAntigravityScreen } from './server-ingest-antigravity-screen'
export abstract class AgentHookServerIngestTerminal extends AgentHookServerIngestNormalization {
export abstract class AgentHookServerIngestTerminal extends AgentHookServerIngestAntigravityScreen {
ingestTerminalStatus(event: {
ptyId?: string
paneKey: string
+1
View File
@@ -233,6 +233,7 @@ async function startOrcadRuntime(
// PTY agent on this host, and the store is the only place `worktree.ps` and the mobile
// projection read from — unwired, orcad lists no PTY agents at all.
onTerminalAgentStatus: (event) => agentHookServer.ingestTerminalStatus(event),
onTerminalScreenPermission: (event) => agentHookServer.ingestAntigravityScreenPermission(event),
// Why here too and not only on the desktop: orcad serves `worktree.ps` and `agentSession.*`,
// so without these a headless host publishes its structured chats nowhere and lists no agents.
getAgentStatusSnapshot: () =>
@@ -0,0 +1,9 @@
{
"capturedAt": "2026-09-20T09:26:40.702Z",
"platform": "win32",
"command": ["C:\\Users\\user\\AppData\\Local\\agy\\bin\\agy.exe"],
"cols": 120,
"rows": 40,
"note": "Native Windows agy 1.2.7; test one-time approval key 1; 120x40.",
"exitCode": 1
}
@@ -0,0 +1,209 @@
[?9001h[?1004h[?25l[>4m]0;C:\Users\user\AppData\Local\agy\bin\agy.exe[?25h[?1049h[?2004h[>4;2m[>1u[?u
▄▀▀▄
▀▀▀▀▀▀
▀▀▀▀▀▀▀▀
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

Welcome to the Antigravity CLI. You are currently not signed in.

No authentication methods available.

Press ctrl+c or ctrl+d twice to exit.



























[?25l[>4m[<1u[?1049l[>4;2m[>1u[?u[0 q
▄▀▀▄ Antigravity CLI 1.2.7
▀▀▀▀▀▀ Gemini 3.1 Pro (Low)
▀▀▀▀▀▀▀▀ C:/orca-agy-verify-0920
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low




























[?25h[?25l
▄▀▀▄ Antigravity CLI 1.2.7
▀▀▀▀▀▀ Gemini 3.1 Pro (Low)
▀▀▀▀▀▀▀▀ C:/orca-agy-verify-0920
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low




























[?25h[?25lUsG[?25he the terminal tool to printORCA_PERMISSION_ALLOW_OK.Donot modify files.[?25l[?25h[?25l? for shortcuts[?25h[?25l
> Use the terminal tool to print ORCA_PERMISSION_ALLOW_OK. Do not modify files.
Generating...
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[?25l⣷ [?25h[?25l⣯ Gene[?25h[?25l⣟ Genera[?25h[?25l⡿ Generat[?25h[?25l⢿ Generati[?25h[?25l⣻ Generating[?25h[?25l⣽ Generating.[?25h[?25l⣾ Generating..[?25h[?25l⣷ Generating...[?25h[?25l⣯ Generating[?25h[?25l⣟ Generating.[?25h[?25l⡿ Generating..[?25h[?25l⢿ Generating...[?25h[?25l⣻ [?25h[?25l⣽ [?25h[?25l⣾ [?25h[?25l⣷ [?25h[?25l⣯ [?25h[?25l⣟ Ge[?25h[?25l⡿ Gen[?25h[?25ler[?25h[?25l⢿ [?25h[?25l⣻ Gener[?25h[?25l⣽ Generat[?25h[?25l⣾ Generati[?25h[?25l⣷ Generatin[?25h[?25l⣯ Generating[?25h[?25l⣟ Generating..[?25h[?25l⡿ Generating...[?25h[?25l⢿ Generatin[?25h[?25l I'm focusing intently on tool selection, always opting for the most specialized option available. I'm actively avo...
└ Tip: Use /skills to browse and manage agent skills.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[?25l⣻ I'm focusing intently on tool selection, always opting for the most specialized option avail[?25h[?25ln avai[?25h[?25l⣽ I'm focusing intently on tool selection, always opting for the most specialized option ava[?25h[?25lion av[?25h[?25ltion a[?25h[?25l⣾ I'm focusing intently on tool selection, always opting for the most specialized option [?25h[?25loption[?25h[?25l optio[?25h[?25l⣷[?25h[?25ld opti[?25h[?25led opt[?25h[?25l⣯ I'm focusing intently on tool selection, always opting for the most specialized op[?25h[?25lized o[?25h[?25l⣟ I'm focusing intently on tool selection, always opting for the most specialized [?25h[?25lalized[?25h[?25lialize[?25h[?25lcializ[?25h[?25l⡿[?25h[?25leciali[?25h[?25lpecial[?25h[?25l⢿[?25h[?25lspecia[?25h[?25l speci[?25h[?25l⣻ I'm focusing intently on tool selection, always opting for the most spec[?25h[?25lst spe[?25h[?25l
▸ Thought for 3s, 246 tokens
I'm focusing intently on tool selection, always opting for the most specialized option available. I'm actively avo... 

Bash(Write-Host "ORCA_PERMISSION_ALLOW_OK") (ctrl+o to expand)
Running command...
└ Tip: Use /skills to browse and manage agent skills.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[0 q[?25l
Command
Requesting permission for:
Write-Host "ORCA_PERMISSION_ALLOW_OK"

Run this command?
> 1. Yes, run command
2. Yes, and always allow in this conversation for commands that start with 'Write-Host "ORCA_PERMISSION_ALLOW_OK"'
3. Yes, and always allow for commands that start with 'Write-Host "ORCA_PERMISSION_ALLOW_OK"' (Persist to
settings.json)
4. No, cancel

↑/↓ Navigate · tab Amend · ctrl+g edit/expand command
esc to cancel Gemini 3.1 Pro · low[0 q○ 
Running command...
└ Tip: Use /diff to view uncommitted changes in your workspace.>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low


















[?25h[?25l⣾ [?25h[?25l⣷ Running command[?25h[?25l⣯ Running command.[?25h[?25l● ..[?25h[?25l● [?25h[?25l⣟ Running command...[?25h[?25l⡿ [?25h[?25l⢿ [?25h[?25l⣻ [?25h[?25l⣽ [?25h[?25l⣾ [?25h[?25l⣷ R[?25h[?25lun[?25h[?25l⣯ [?25h[?25l⣟ Run[?25h[?25l⡿ Runni[?25h[?25l⢿ Runnin[?25h[?25l⣻ Running[?25h[?25l⣽ Running c[?25h[?25l⣾ Running co[?25h[?25l⣷ Running com[?25h[?25l⣯ Running comm[?25h[?25l⣟ Running comman[?25h[?25lommand[?25h[?25l⡿ [?25h[?25l⢿ Running command.[?25h[?25l⣻ Running command..[?25h[?25l⣽ Running command...[?25h[?25l⣾ Running command[?25h[?25l⣷ Running command.[?25h[?25l⣯ Running command...[?25h[?25l⣟ [?25h[?25l⡿ [?25h[?25l⢿ [?25h[?25l
I have executed the comm
Running command...
└ Tip: Use /diff to view uncommitted changes in your workspace.>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[?25land in the t[?25h[?25lerminal,[?25h[?25l⣻ [?25h[?25land it successfully printed ORCA_PERMISSION_ALLOW_OK. Let me know if
you need anything else!
Running command...
└ Tip: Use /diff to view uncommitted changes in your workspace.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[?25l⣽ [?25h[?25l
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low















[?25h
@@ -0,0 +1,9 @@
{
"capturedAt": "2026-09-20T09:20:25.623Z",
"platform": "win32",
"command": ["C:\\Users\\user\\AppData\\Local\\agy\\bin\\agy.exe"],
"cols": 120,
"rows": 40,
"note": "Native Windows agy 1.2.7; authenticated profile; harmless command approval; 120x40.",
"exitCode": 1
}
@@ -0,0 +1,148 @@
[?9001h[?1004h[?25l[>4m]0;C:\Users\user\AppData\Local\agy\bin\agy.exe[?25h[?1049h[?2004h[>4;2m[>1u[?u
▄▀▀▄
▀▀▀▀▀▀
▀▀▀▀▀▀▀▀
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

Welcome to the Antigravity CLI. You are currently not signed in.

No authentication methods available.

Press ctrl+c or ctrl+d twice to exit.



























[?25l[>4m[<1u[?1049l[>4;2m[>1u[?u[0 q
▄▀▀▄ Antigravity CLI 1.2.7
▀▀▀▀▀▀ Gemini 3.1 Pro (Low)
▀▀▀▀▀▀▀▀ C:/orca-agy-verify-0920
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low




























[?25h[?25l
▄▀▀▄ Antigravity CLI 1.2.7
▀▀▀▀▀▀ Gemini 3.1 Pro (Low)
▀▀▀▀▀▀▀▀ C:/orca-agy-verify-0920
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low




























[?25h[?25lUseG[?25h theterminal tool to print ORCA_PERMISSION_CAPTURE_OK.Do not modify files.[?25l[?25h[?25l? for shortcuts[?25h[?25l
> Use the terminal tool to print ORCA_PERMISSION_CAPTURE_OK. Do not modify files.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low[?25h[?25l⣾ Generating...esc to cancel [?25h[?25l⣷ [?25h[?25l⣯ [?25h[?25l⣟ [?25h[?25l⡿ [?25h[?25l⢿ [?25h[?25l⣻ Ge[?25h[?25l⣽ Gen[?25h[?25l⣾ Gene[?25h[?25l⣷ Gener[?25h[?25l⣯ Generat[?25h[?25l⣟ Generati[?25h[?25l⡿ Generatin[?25h[?25l⢿ Generating[?25h[?25l⣻ Generating.[?25h[?25l⣽ Generating...[?25h[?25l⣾ Generatin[?25h[?25l⣷ Generating[?25h[?25l⣯ Generating.[?25h[?25l⣟ Generating...[?25h[?25l⡿ [?25h[?25l⢿ [?25h[?25l⣻ [?25h[?25l⣽ [?25h[?25l⣾ [?25h[?25l⣷ G[?25h[?25l⣯ Ge[?25h[?25l⣟ Gene[?25h[?25l⡿ Gener[?25h[?25l⢿ Genera[?25h[?25l⣻ Generat[?25h[?25lneratin[?25h[?25l⣽ [?25h[?25l I've zeroed in on the `run_command` tool. My goal is to use it to print `ORCA_PERMISSION_CAPTURE_OK` without alter...[?25h[?25l⣾ I've zeroed in on the `run_command` tool. My goal is to use it to[?25h[?25le it t[?25h[?25lse it [?25h[?25l⣷[?25h[?25luse it[?25h[?25l use i[?25h[?25lo use [?25h[?25l⣯[?25h[?25lto use[?25h[?25l to us[?25h[?25l⣟[?25h[?25ls to u[?25h[?25lis to [?25h[?25l⡿ I've zeroed in on the `run_command` tool. My goal is to[?25h[?25ll is t[?25h[?25lal is [?25h[?25l⢿[?25h[?25loal is[?25h[?25lgoal i[?25h[?25l goal [?25h[?25l⣻[?25h[?25ly goal[?25h[?25lMy goa[?25h[?25l⣽ I've zeroed in on the `run_command` tool. My go[?25h[?25l. My g[?25h[?25l⣾ I've zeroed in on the `run_command` tool. My [?25h[?25lol. My[?25h[?25lool. M[?25h[?25l⣷[?25h[?25ltool. [?25h[?25l tool.[?25h[?25l⣯ I've zeroed in on the `run_command` tool[?25h[?25ld` too[?25h[?25lnd` to[?25h[?25l⣟[?25h[?25land` t[?25h[?25lmand` [?25h[?25l⡿[?25h[?25lmmand`[?25h[?25lommand[?25h[?25l⢿ I've zeroed in on the `run_comman[?25h[?25l_comma[?25h[?25ln_comm[?25h[?25l⣻[?25h[?25lun_com[?25h[?25lrun_co[?25h[?25l`run_c[?25h[?25l⣽[?25h[?25l `run_[?25h[?25le `run[?25h[?25lhe `ru[?25h[?25l
▸ Thought for 4s, 310 tokens
I've zeroed in on the `run_command` tool. My goal is to use it to print `ORCA_PERMISSION_CAPTURE_OK` without alter...

Bash(echo "ORCA_PERMISSION_CAPTURE_OK") (ctrl+o to expand)
Running command...
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[0 q[?25l● 

Command
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Requesting permission for:
echo "ORCA_PERMISSION_CAPTURE_OK"

Run this command?
> 1. Yes, run command
2. Yes, and always allow in this conversation for commands that start with 'echo "ORCA_PERMISSION_CAPTURE_OK"'
3. Yes, and always allow for commands that start with 'echo "ORCA_PERMISSION_CAPTURE_OK"' (Persist to settings.json)
4. No, cancel

↑/↓ Navigate · tab Amend · ctrl+g edit/expand command
esc to cancel Gemini 3.1 Pro · low
@@ -0,0 +1,9 @@
{
"capturedAt": "2026-09-20T09:23:21.869Z",
"platform": "win32",
"command": ["C:\\Users\\user\\AppData\\Local\\agy\\bin\\agy.exe"],
"cols": 120,
"rows": 40,
"note": "Native Windows agy 1.2.7; command approval dismissed with Escape; 120x40.",
"exitCode": 1
}
@@ -0,0 +1,173 @@
[?9001h[?1004h[?25l[>4m]0;C:\Users\user\AppData\Local\agy\bin\agy.exe[?25h[?1049h[?2004h[>4;2m[>1u[?u
▄▀▀▄
▀▀▀▀▀▀
▀▀▀▀▀▀▀▀
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

Welcome to the Antigravity CLI. You are currently not signed in.

No authentication methods available.

Press ctrl+c or ctrl+d twice to exit.



























[?25l[>4m[<1u[?1049l[>4;2m[>1u[?u[0 q
▄▀▀▄ Antigravity CLI 1.2.7
▀▀▀▀▀▀ Gemini 3.1 Pro (Low)
▀▀▀▀▀▀▀▀ C:/orca-agy-verify-0920
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low




























[?25h[?25l
▄▀▀▄ Antigravity CLI 1.2.7
▀▀▀▀▀▀ Gemini 3.1 Pro (Low)
▀▀▀▀▀▀▀▀ C:/orca-agy-verify-0920
▄▀▀ ▀▀▄
▄▀▀ ▀▀▄

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low




























[?25hUs[?25leG[?25htheterminal tool toprint ORCA_PERMISSION_CANCEL_OK. Do not modify files.[?25l? for shortcuts[?25h[?25l
> Use the terminal tool to print ORCA_PERMISSION_CANCEL_OK. Do not modify files.
Generating...
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[?25l⣷ [?25h[?25l⣯ Generating[?25h[?25l⣟ Generating..[?25h[?25l⡿ Generating...[?25h[?25l⢿ [?25h[?25l⣻ [?25h[?25l⣽ [?25h[?25l⣾ [?25h[?25l⣷ [?25h[?25l⣯ G[?25h[?25l⣟ Ge[?25h[?25lne[?25h[?25l⡿ Gene[?25h[?25l⢿ Gener[?25h[?25l⣻ Genera[?25h[?25l⣽ Generat[?25h[?25l⣾ Generatin[?25h[?25l⣷ Generating[?25h[?25l⣯ Generating.[?25h[?25l⣟ Generating..[?25h[?25l⡿ Generating...[?25h[?25l⢿ Generating[?25h[?25l⣻ Generating.[?25h[?25l⣽ Generating..[?25h[?25l⣾ Generating...[?25h[?25l⣷ [?25h[?25l⣯ [?25h[?25l⣟ [?25h[?25l⡿ [?25h[?25l⢿ G[?25h[?25l⣻ Ge[?25h[?25lne[?25h[?25l⣽ I'm focusing intently on tool specificity. I've been refining my approach to avoid unnecessary use of `cat` within...
└ Tip: Use /settings to configure your environment.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[?25lng int[?25h[?25l⣾ I'm focusing inte[?25h[?25l inten[?25h[?25lintent[?25h[?25l⣷[?25h[?25lntentl[?25h[?25ltently[?25h[?25lently [?25h[?25l⣯[?25h[?25lntly o[?25h[?25ltly on[?25h[?25l⣟ I'm focusing intently on [?25h[?25ly on t[?25h[?25l⡿ I'm focusing intently on to[?25h[?25lon too[?25h[?25ln tool[?25h[?25l⢿ I'm focusing intently on tool [?25h[?25ltool s[?25h[?25lool sp[?25h[?25l⣻[?25h[?25lol spe[?25h[?25ll spec[?25h[?25l⣽ I'm focusing intently on tool speci[?25h[?25lspecif[?25h[?25lpecifi[?25h[?25l⣾[?25h[?25lecific[?25h[?25lcifici[?25h[?25l⣷[?25h[?25lificit[?25h[?25lficity[?25h[?25licity.[?25h[?25l⣯[?25h[?25lcity. [?25h[?25lity. I[?25h[?25l⣟ I'm focusing intently on tool specificity. I'[?25h[?25ly. I'v[?25h[?25l⡿ I'm focusing intently on tool specificity. I've[?25h[?25l I've [?25h[?25lI've b[?25h[?25l⢿ I'm focusing intently on tool specificity. I've be[?25h[?25lve bee[?25h[?25l
▸ Thought for 4s, 365 tokens
I'm focusing intently on tool specificity. I've been refining my approach to avoid unnecessary use of `cat` within... 

Bash(echo ORCA_PERMISSION_CANCEL_OK) (ctrl+o to expand)
Running command...
└ Tip: Use /settings to configure your environment.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
esc to cancel Gemini 3.1 Pro · low[?25h[0 q[?25l
Command
Requesting permission for:
echo ORCA_PERMISSION_CANCEL_OK

Run this command?
> 1. Yes, run command
2. Yes, and always allow in this conversation for commands that start with 'echo ORCA_PERMISSION_CANCEL_OK'
3. Yes, and always allow for commands that start with 'echo ORCA_PERMISSION_CANCEL_OK' (Persist to settings.json)
4. No, cancel

↑/↓ Navigate · tab Amend · ctrl+g edit/expand command
esc to cancel Gemini 3.1 Pro · low[0 q●  ⎿ Interrupted · What should Antigravity CLI do instead?>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts Gemini 3.1 Pro · low


















[?25h
@@ -20,6 +20,9 @@ export function makeAgentStatusStoreWiring(): {
statusStore: AgentHookServer
deps: {
onTerminalAgentStatus: (event: Parameters<AgentHookServer['ingestTerminalStatus']>[0]) => void
onTerminalScreenPermission: (
event: Parameters<AgentHookServer['ingestAntigravityScreenPermission']>[0]
) => boolean
getAgentStatusSnapshot: () => ReturnType<AgentHookServer['getStatusSnapshot']>
getAgentProviderSessionSnapshot: () => ReturnType<AgentHookServer['getStatusSnapshot']>
getAgentProviderSessionRowsForPane: (
@@ -37,6 +40,7 @@ export function makeAgentStatusStoreWiring(): {
statusStore,
deps: {
onTerminalAgentStatus: (event) => statusStore.ingestTerminalStatus(event),
onTerminalScreenPermission: (event) => statusStore.ingestAntigravityScreenPermission(event),
getAgentStatusSnapshot: () =>
statusStore.getStatusSnapshot().filter((entry) => entry.providerSessionOnly !== true),
getAgentProviderSessionSnapshot: () => statusStore.getStatusSnapshot(),
@@ -0,0 +1,23 @@
/** Matches the captured agy command dialog only while it owns the bottom of the screen. */
export function isAntigravityCommandApprovalScreen(rows: readonly string[]): boolean {
const lines = rows.map((row) => row.trim()).filter(Boolean)
const footer = lines.at(-1) ?? ''
const navigation = lines.at(-2) ?? ''
if (
!footer.startsWith('esc to cancel') ||
navigation !== '↑/↓ Navigate · tab Amend · ctrl+g edit/expand command'
) {
return false
}
const question = lines.lastIndexOf('Run this command?')
if (question === -1) {
return false
}
const choices = lines.slice(question + 1, -2).map((line) => line.replace(/^>\s*/, ''))
return (
choices[0] === '1. Yes, run command' &&
choices.some((line) => line.startsWith('2. Yes, and always allow in this conversation')) &&
choices.some((line) => line.startsWith('3. Yes, and always allow for commands')) &&
choices.at(-1) === '4. No, cancel'
)
}
@@ -0,0 +1,94 @@
import { makeAgentStatusStoreWiring } from './agent-status-store-wiring.test-fixture'
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { describe, expect, it, vi } from 'vitest'
import { createTranscriptPane, TRANSCRIPT_PANE_PTY_ID } from './agent-transcript-pane-test-harness'
import { extractLastOscTitle } from '../../shared/osc-title-extraction'
vi.mock('electron', () => ({
BrowserWindow: { fromId: vi.fn(() => null) },
webContents: { fromId: vi.fn(() => null) },
ipcMain: { on: vi.fn(), removeListener: vi.fn() },
app: { getPath: vi.fn(() => '/tmp') }
}))
describe('captured native Windows Antigravity command approval', () => {
it.each([
['approval', false, 'agent-approval-prompt'],
['cancelled', true, undefined],
['allow-key', true, undefined]
] as const)(
'classifies the recorded %s screen',
async (name, satisfied, blockedReason) => {
const transcript = readFileSync(
join(__dirname, `__fixtures__/antigravity-windows-command-${name}.txt`),
'utf8'
)
const { runtime, handle } = await createTranscriptPane({
paneTitle: extractLastOscTitle(transcript) ?? 'agy',
foregroundProcess: 'agy',
size: { cols: 120, rows: 40 },
data: transcript
})
const result = await runtime.waitForTerminal(handle, {
condition: 'tui-idle',
timeoutMs: 3500
})
expect(result.satisfied).toBe(satisfied)
if (!satisfied) {
expect(result).toMatchObject({ blockedReason })
}
},
10000
)
})
it('publishes the captured permission and cancellation to the canonical hook store', async () => {
const wiring = makeAgentStatusStoreWiring()
const { runtime, handle } = await createTranscriptPane(
{
paneTitle: 'agy',
foregroundProcess: 'agy',
size: { cols: 120, rows: 40 },
data: ''
},
wiring.deps
)
const paneKey = runtime.getTerminalPaneKey(handle)
if (!paneKey) {
throw new Error('Missing test pane key')
}
wiring.statusStore.ingestTerminalStatus({
paneKey,
terminalHandle: handle,
payload: {
state: 'working',
agentType: 'antigravity',
prompt: 'print marker',
toolName: 'run_command'
}
})
const approval = readFileSync(
join(__dirname, '__fixtures__/antigravity-windows-command-approval.txt'),
'utf8'
)
runtime.onPtyData(TRANSCRIPT_PANE_PTY_ID, approval, Date.now())
await vi.waitFor(() =>
expect(wiring.statusStore.getStatusSnapshot()[0]).toMatchObject({
state: 'waiting',
observation: { origin: 'process' }
})
)
expect(wiring.statusStore.getStatusSnapshot()[0].interactivePrompt).toContain(
'ORCA_PERMISSION_CAPTURE_OK'
)
const cancelled = readFileSync(
join(__dirname, '__fixtures__/antigravity-windows-command-cancelled.txt'),
'utf8'
)
runtime.onPtyData(TRANSCRIPT_PANE_PTY_ID, cancelled, Date.now())
await vi.waitFor(() =>
expect(wiring.statusStore.getStatusSnapshot()[0]).toMatchObject({ state: 'working' })
)
expect(wiring.statusStore.getStatusSnapshot()[0].interactivePrompt).toBeUndefined()
})
@@ -0,0 +1,110 @@
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { Terminal } from '@xterm/headless'
import { beforeAll, describe, expect, it, vi } from 'vitest'
import type { AgentStatusIpcPayload } from '../../shared/agent-status-types'
import type { RuntimeVisibleTerminalState } from './runtime-terminal-state-records'
import { AntigravityScreenPermissionPublisher } from './antigravity-screen-permission-publisher'
const baseline: AgentStatusIpcPayload = {
paneKey: 'pane',
terminalHandle: 'term-1',
connectionId: null,
agentType: 'antigravity',
state: 'working',
prompt: 'print marker',
receivedAt: 1,
stateStartedAt: 1
}
let approval: RuntimeVisibleTerminalState
beforeAll(async () => {
const terminal = new Terminal({ cols: 120, rows: 40, allowProposedApi: true })
try {
const raw = readFileSync(
join(__dirname, '__fixtures__/antigravity-windows-command-approval.txt'),
'utf8'
)
await new Promise<void>((resolve) => terminal.write(raw, resolve))
const buffer = terminal.buffer.active
approval = {
lines: Array.from(
{ length: 40 },
(_, row) => buffer.getLine(buffer.baseY + row)?.translateToString(true) ?? ''
),
generation: 1,
sequence: 1,
isAlternateScreen: true
}
} finally {
terminal.dispose()
}
})
describe('Antigravity screen publication scheduling', () => {
it('coalesces output during a read and retries the newest frame', async () => {
const frame = Promise.withResolvers<RuntimeVisibleTerminalState | null>()
const readScreen = vi.fn().mockReturnValueOnce(frame.promise).mockResolvedValue(approval)
const publish = vi.fn(() => true)
const publisher = new AntigravityScreenPermissionPublisher({
baseline: () => baseline,
readScreen,
isCurrent: () => true,
publish
})
publisher.schedule('pty')
for (let index = 0; index < 100; index += 1) {
publisher.schedule('pty')
}
expect(readScreen).toHaveBeenCalledTimes(1)
frame.resolve(null)
await vi.waitFor(() => expect(publish).toHaveBeenCalledTimes(1))
expect(readScreen).toHaveBeenCalledTimes(2)
expect(publish).toHaveBeenCalledWith({ baseline, command: 'echo "ORCA_PERMISSION_CAPTURE_OK"' })
})
it('does not publish a frame rejected by generation, sequence or liveness validation', async () => {
const isCurrent = vi.fn(() => false)
const publish = vi.fn(() => true)
const publisher = new AntigravityScreenPermissionPublisher({
baseline: () => baseline,
readScreen: async () => approval,
isCurrent,
publish
})
publisher.schedule('pty')
await vi.waitFor(() => expect(isCurrent).toHaveBeenCalled())
expect(publish).not.toHaveBeenCalled()
})
it('does not interpret another provider or a direct SSH mirror', async () => {
const readScreen = vi.fn(async () => approval)
const publisher = new AntigravityScreenPermissionPublisher({
baseline: (id) =>
id === 'ssh'
? { ...baseline, connectionId: 'remote' }
: { ...baseline, agentType: 'claude' },
readScreen,
isCurrent: () => true,
publish: () => true
})
publisher.schedule('ssh')
publisher.schedule('claude')
await Promise.resolve()
expect(readScreen).not.toHaveBeenCalled()
})
it('does not lose output queued between a synchronous empty read and its cleanup', async () => {
const readBaseline = vi.fn().mockReturnValueOnce(null).mockReturnValue(baseline)
const publish = vi.fn(() => true)
const publisher = new AntigravityScreenPermissionPublisher({
baseline: readBaseline,
readScreen: async () => approval,
isCurrent: () => true,
publish
})
publisher.schedule('pty')
publisher.schedule('pty')
await vi.waitFor(() => expect(publish).toHaveBeenCalledTimes(1))
})
})
@@ -0,0 +1,68 @@
import type { AgentStatusIpcPayload } from '../../shared/agent-status-types'
import type { AntigravityScreenPermissionObservation } from '../agent-hooks/server/server-ingest-antigravity-screen'
import type { RuntimeVisibleTerminalState } from './runtime-terminal-state-records'
import { isAntigravityCommandApprovalScreen } from './antigravity-command-approval-screen'
import { isKnownReadyTerminalScreen } from './terminal-screen-readiness'
type Dependencies = {
baseline(ptyId: string): AgentStatusIpcPayload | null
readScreen(ptyId: string): Promise<RuntimeVisibleTerminalState | null>
isCurrent(ptyId: string, screen: RuntimeVisibleTerminalState): boolean
publish(observation: AntigravityScreenPermissionObservation): boolean
}
export class AntigravityScreenPermissionPublisher {
private readonly pending = new Map<string, { dirty: boolean }>()
constructor(private readonly deps: Dependencies) {}
schedule(ptyId: string): void {
const existing = this.pending.get(ptyId)
if (existing) {
existing.dirty = true
return
}
const pending = { dirty: true }
this.pending.set(ptyId, pending)
void this.drain(ptyId, pending).finally(() => {
this.pending.delete(ptyId)
if (pending.dirty) {
this.schedule(ptyId)
}
})
}
private async drain(ptyId: string, pending: { dirty: boolean }): Promise<void> {
while (pending.dirty) {
pending.dirty = false
const baseline = this.deps.baseline(ptyId)
if (!baseline || baseline.agentType !== 'antigravity' || baseline.connectionId) {
continue
}
try {
const screen = await this.deps.readScreen(ptyId)
if (!screen || !this.deps.isCurrent(ptyId, screen)) {
continue
}
if (isAntigravityCommandApprovalScreen(screen.lines)) {
const lines = screen.lines.map((line) => line.trim())
const start = lines.lastIndexOf('Requesting permission for:')
const end = lines.lastIndexOf('Run this command?')
if (start !== -1 && end > start) {
const command = lines
.slice(start + 1, end)
.filter(Boolean)
.join('\n')
if (command) {
this.deps.publish({ baseline, command })
}
}
} else if (isKnownReadyTerminalScreen({ tail: screen.lines, draft: screen.draft })) {
this.deps.publish({ baseline, command: null })
}
} catch {
// An unreadable screen is not evidence that a permission prompt disappeared.
}
}
}
}
@@ -66,6 +66,9 @@ export class OrcaRuntimeWithOnPtyData extends OrcaRuntimeWithPreparePtyExecution
forwardQueryReplies
)
captureModelReceipt?.(modelCompletion)
if (this.onTerminalScreenPermission) {
this.antigravityScreenPermissions.schedule(ptyId)
}
const pty = this.getOrCreatePtyWorktreeRecord(ptyId)
const ptyTailBefore = pty
@@ -1,4 +1,5 @@
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
import type { AntigravityScreenPermissionObservation } from '../agent-hooks/server/server-ingest-antigravity-screen'
import { OrcaRuntimeWithTerminalDrivers } from './orca-runtime-terminal-drivers'
import { RuntimePreservedBranchCleanup } from './runtime-preserved-branch-cleanup'
import type { IPtyProvider } from '../providers/types'
@@ -55,6 +56,10 @@ export class OrcaRuntimeWithPreservedBranchCleanup extends OrcaRuntimeWithTermin
protected readonly onPtyStopped: ((ptyId: string) => void) | null
protected readonly onTerminalScreenPermission:
| ((event: AntigravityScreenPermissionObservation) => boolean)
| null
protected readonly onTerminalAgentStatus:
| ((event: RuntimeTerminalAgentStatusEvent) => void)
| null
@@ -1,4 +1,5 @@
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
import type { AntigravityScreenPermissionObservation } from '../agent-hooks/server/server-ingest-antigravity-screen'
import { OrcaRuntimeWithLinearCommands } from './orca-runtime-linear-commands'
import type { RuntimeStore } from './runtime-store-contract'
import type { StatsCollector } from '../stats/collector'
@@ -52,6 +53,7 @@ export class OrcaRuntimeWithStateFields extends OrcaRuntimeWithLinearCommands {
getSshProvider?: (connectionId: string) => IPtyProvider | undefined
prepareClaudeAuth?: PrepareClaudeAuth
onPtyStopped?: (ptyId: string) => void
onTerminalScreenPermission?: (event: AntigravityScreenPermissionObservation) => boolean
onTerminalAgentStatus?: (event: RuntimeTerminalAgentStatusEvent) => void
onTerminalSideEffects?: (batch: TerminalSideEffectBatch) => void
// Why: agent status mostly arrives via hooks (agent-hooks/server), not OSC
@@ -242,6 +244,7 @@ export class OrcaRuntimeWithStateFields extends OrcaRuntimeWithLinearCommands {
this.getLocalProviderFn = deps?.getLocalProvider ?? null
this.getSshProviderFn = deps?.getSshProvider ?? null
this.onPtyStopped = deps?.onPtyStopped ?? null
this.onTerminalScreenPermission = deps?.onTerminalScreenPermission ?? null
this.onTerminalAgentStatus = deps?.onTerminalAgentStatus ?? null
this.buildAgentHookPtyEnv = deps?.buildAgentHookPtyEnv ?? null
this.getDesktopWindowStatusFn = deps?.getDesktopWindowStatus ?? (() => 'openable')
@@ -1,4 +1,5 @@
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
import { AntigravityScreenPermissionPublisher } from './antigravity-screen-permission-publisher'
import { OrcaRuntimeWithCaptureProviderTerminalBuffer } from './orca-runtime-capture-provider-terminal-buffer'
import type { RuntimeTerminalProjection } from './orca-runtime-core'
import { buildPreview } from './terminal-tail-state'
@@ -16,6 +17,29 @@ import {
import { withTimeout } from './runtime-async-boundaries'
export class OrcaRuntimeWithVisibleSnapshotPreview extends OrcaRuntimeWithCaptureProviderTerminalBuffer {
protected readonly antigravityScreenPermissions = new AntigravityScreenPermissionPublisher({
baseline: (ptyId) => {
const pty = this.ptysById.get(ptyId)
if (!pty?.connected || pty.connectionId || !pty.paneKey) {
return null
}
return (
this.getAgentProviderSessionRowsForPaneFn?.(pty.paneKey)?.find(
(row) => row.providerSessionOnly !== true && row.agentType === 'antigravity'
) ?? null
)
},
readScreen: (ptyId) => this.readVisibleTerminalState(ptyId),
isCurrent: (ptyId, screen) =>
this.ptysById.get(ptyId)?.connected === true &&
this.getPtyLivenessVerdict(ptyId)?.status !== 'unverifiable' &&
screen.generation === this.getPtyLifecycleGeneration(ptyId) &&
screen.sequence >= this.getPtyOutputSequence(ptyId) &&
(!screen.headlessWriteChain ||
screen.headlessWriteChain === this.headlessTerminals.get(ptyId)?.writeChain),
publish: (event) => this.onTerminalScreenPermission?.(event) ?? false
})
protected getTerminalScreenReadiness(
ptyId: string | null | undefined,
retainedText: string
@@ -1,3 +1,4 @@
import { isAntigravityCommandApprovalScreen } from './antigravity-command-approval-screen'
import {
detectTerminalWaitBlockedReason,
isKnownReadyPromptPreview
@@ -47,6 +48,10 @@ export function classifyTerminalScreenReadiness(screen: {
const ready = isKnownReadyTerminalScreen(screen)
return {
ready,
blockedReason: ready ? null : detectTerminalWaitBlockedReason(screen.tail.join('\n'))
blockedReason: ready
? null
: isAntigravityCommandApprovalScreen(screen.tail)
? 'agent-approval-prompt'
: detectTerminalWaitBlockedReason(screen.tail.join('\n'))
}
}
@@ -85,6 +85,7 @@ export function initializeMainProcessRuntime(): OrcaRuntimeService {
getSshProvider: (connectionId) => getSshPtyProvider(connectionId),
onPtyStopped: clearProviderPtyState,
onTerminalAgentStatus: (event) => agentHookServer.ingestTerminalStatus(event),
onTerminalScreenPermission: (event) => agentHookServer.ingestAntigravityScreenPermission(event),
// Why: serve can be promoted in place, so wire the listener from startup; runtime enables desktop-only scanners only for a ready renderer.
onTerminalSideEffects: (batch: TerminalSideEffectBatch) => {
if (state.mainWindow && !state.mainWindow.isDestroyed()) {