mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
fix(antigravity): publish captured command approvals to Chat
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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[2J[m[H]0;C:\Users\user\AppData\Local\agy\bin\agy.exe[?25h[?1049h[?2004h[>4;2m[>1u[?u[2J[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄[K[m
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m[K
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m[K
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K
|
||||
Welcome to the [38;2;66;133;244mAntigravity CLI[m. You are currently not signed in.[K
|
||||
[K
|
||||
[31mNo authentication methods available.[K[m
|
||||
[K
|
||||
Press ctrl+c or ctrl+d twice to exit.[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[?25l[120C[>4m[<1u[?1049l[>4;2m[>1u[?u[0 q[H[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄ [94m[1mAntigravity CLI 1.2.7[m[K
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m [90mGemini 3.1 Pro (Low)[K[m
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m [90mC:/orca-agy-verify-0920[K[m
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[9;3H[?25h[?25l[H[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄ [94m[1mAntigravity CLI 1.2.7[m[K
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m [90mGemini 3.1 Pro (Low)[K[m
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m [90mC:/orca-agy-verify-0920[K[m
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[9;3H[?25h[?25lUs[11;1H[100X[2m[100CG[9;5H[?25h[22me the terminal tool to print[1CORCA_PERMISSION_ALLOW_OK.[1CDo[1Cnot modify files.[?25l[9;3H[K[?25h[?25l[90m[11;1H? for shortcuts[9;3H[?25h[?25l[m[8;61H[K[34m[1m
|
||||
> Use the terminal tool to print ORCA_PERMISSION_ALLOW_OK. Do not modify files.[m
|
||||
⣾ [94mGen[90merating...[K
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[12;3H[?25h[22m[?25l[10;1H⣷ [12;3H[?25h[?25l[10;1H⣯ [94mGene[12;3H[?25h[m[?25l[10;1H⣟ [90mG[94menera[12;3H[?25h[?25l[m[10;1H⡿ [90mGe[94mnerat[12;3H[?25h[?25l[m[10;1H⢿ [90mGen[94merati[12;3H[?25h[m[?25l[10;1H⣻ [90mGener[94mating[12;3H[?25h[?25l[m[10;1H⣽ [90mGenera[94mting.[12;3H[?25h[?25l[m[10;1H⣾ [90mGenerat[94ming..[12;3H[?25h[?25l[m[10;1H⣷ [90mGenerati[94mng...[12;3H[?25h[?25l[m[10;1H⣯ [90mGenerating[12;3H[?25h[?25l[m[10;1H⣟ [90mGenerating.[12;3H[?25h[m[?25l[10;1H⡿ [90mGenerating..[12;3H[?25h[m[?25l[10;1H⢿ [90mGenerating...[12;3H[?25h[m[?25l[10;1H⣻ [12;3H[?25h[?25l[10;1H⣽ [12;3H[?25h[?25l[10;1H⣾ [12;3H[?25h[?25l[10;1H⣷ [12;3H[?25h[?25l[10;1H⣯ [12;3H[?25h[?25l[10;1H⣟ [94mGe[12;3H[?25h[m[?25l[10;1H⡿ [94mGen[12;3H[?25h[m[?25l[94m[10;7He[90mr[12;3H[?25h[?25l[m[10;1H⢿ [12;3H[?25h[?25l[10;1H⣻ [94mGener[12;3H[?25h[?25l[m[10;1H⣽ [90mGe[94mnerat[12;3H[?25h[?25l[m[10;1H⣾ [90mGen[94merati[12;3H[?25h[m[?25l[10;1H⣷ [90mGene[94mratin[12;3H[?25h[m[?25l[10;1H⣯ [90mGener[94mating[12;3H[?25h[?25l[m[10;1H⣟ [90mGenerat[94ming..[12;3H[?25h[m[?25l[10;1H⡿ [90mGenerati[94mng...[12;3H[?25h[?25l[m[10;1H⢿ [90mGeneratin[12;3H[?25h[m[?25l[10;3H [90mI'm focusing intently on tool selection, always opting for the most specialized option [94mavail[90mable. I'm actively avo...
|
||||
└ Tip: Use /skills to browse and manage agent skills.[K
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[13;3H[?25h[?25l[22m[10;1H⣻ [90mI'm focusing intently on tool selection, always opting for the most specialized option [94mavai[90ml[13;3H[?25h[?25l[m[94m[10;89Hn ava[90mi[13;3H[?25h[m[?25l[10;1H⣽ [90mI'm focusing intently on tool selection, always opting for the most specialized opti[94mon av[90ma[13;3H[?25h[m[?25l[94m[10;87Hion a[90mv[13;3H[?25h[m[?25l[94m[10;86Htion [90ma[13;3H[?25h[?25l[m[10;1H⣾ [90mI'm focusing intently on tool selection, always opting for the most specialized o[94mption [13;3H[?25h[?25l[m[94m[10;84Hoptio[90mn[13;3H[?25h[m[?25l[94m[10;83H opti[90mo[13;3H[?25h[?25l[m[10;1H⣷[13;3H[?25h[?25l[94m[10;82Hd opt[90mi[13;3H[?25h[m[?25l[94m[10;81Hed op[90mt[13;3H[?25h[m[?25l[10;1H⣯ [90mI'm focusing intently on tool selection, always opting for the most speciali[94mzed o[90mp[13;3H[?25h[m[?25l[94m[10;79Hized [90mo[13;3H[?25h[m[?25l[10;1H⣟ [90mI'm focusing intently on tool selection, always opting for the most specia[94mlized [13;3H[?25h[m[?25l[94m[10;77Halize[90md[13;3H[?25h[?25l[m[94m[10;76Hializ[90me[13;3H[?25h[m[?25l[94m[10;75Hciali[90mz[13;3H[?25h[?25l[m[10;1H⡿[13;3H[?25h[?25l[94m[10;74Hecial[90mi[13;3H[?25h[m[?25l[94m[10;73Hpecia[90ml[13;3H[?25h[?25l[m[10;1H⢿[13;3H[?25h[?25l[94m[10;72Hspeci[90ma[13;3H[?25h[?25l[m[94m[10;71H spec[90mi[13;3H[?25h[m[?25l[10;1H⣻ [90mI'm focusing intently on tool selection, always opting for the mos[94mt spe[90mc[13;3H[?25h[m[?25l[94m[10;69Hst sp[90me[13;3H[?25h[m[?25l[10;1H[K[37m
|
||||
▸ Thought for 3s, 246 tokens[K
|
||||
I'm focusing intently on tool selection, always opting for the most specialized option available. I'm actively avo... [m
|
||||
[K[33m
|
||||
● [1mBash[37m[22m(Write-Host "ORCA_PERMISSION_ALLOW_OK") (ctrl+o to expand)[K[m
|
||||
⣻ [90mR[94munnin[90mg command...[K
|
||||
└ Tip: Use /skills to browse and manage agent skills.[K
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[18;3H[?25h[0 q[?25l[22m[15;1H[K[33m[1m
|
||||
Command[m[K[18;1H[K[37m[1m
|
||||
Requesting permission for:[m[K[37m
|
||||
Write-Host "ORCA_PERMISSION_ALLOW_OK"[K[m
|
||||
[K[1m
|
||||
Run this command?[22m[K[94m
|
||||
> 1. Yes, run command[K[m
|
||||
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[K
|
||||
settings.json)[K
|
||||
4. No, cancel[K
|
||||
[K
|
||||
[94m[1m↑/↓[90m[22m Navigate · [94m[1mtab[90m[22m Amend · [94m[1mctrl+g[90m[22m edit/expand command[K
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[22m[0 q[33m[14;1H○ [m
|
||||
⣽ [90mRunning comman[94md...[90m
|
||||
└ Tip: Use /diff to view uncommitted changes in your workspace.[94m[18;1H>[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[18;3H[?25h[?25l[15;1H⣾ [18;3H[?25h[?25l[15;1H⣷ [90mRunning command[18;3H[?25h[m[?25l[15;1H⣯ [90mRunning command.[18;3H[?25h[?25l[m[33m[14;1H● [90m[15;20H.[94m.[18;3H[?25h[?25l[m[32m[14;1H● [18;3H[?25h[m[?25l[15;1H⣟ [90mRunning command...[18;3H[?25h[?25l[m[15;1H⡿ [18;3H[?25h[?25l[15;1H⢿ [18;3H[?25h[?25l[15;1H⣻ [18;3H[?25h[?25l[15;1H⣽ [18;3H[?25h[?25l[15;1H⣾ [18;3H[?25h[?25l[15;1H⣷ [94mR[18;3H[?25h[?25l[m[94m[15;5Hu[90mn[18;3H[?25h[?25l[m[15;1H⣯ [18;3H[?25h[?25l[15;1H⣟ [94mRun[18;3H[?25h[?25l[m[15;1H⡿ [94mRunni[18;3H[?25h[m[?25l[15;1H⢿ [90mR[94munnin[18;3H[?25h[?25l[m[15;1H⣻ [90mRu[94mnning[18;3H[?25h[?25l[m[15;1H⣽ [90mRunn[94ming c[18;3H[?25h[m[?25l[15;1H⣾ [90mRunni[94mng co[18;3H[?25h[?25l[m[15;1H⣷ [90mRunnin[94mg com[18;3H[?25h[m[?25l[15;1H⣯ [90mRunning [94mcomm[18;3H[?25h[?25l[m[15;1H⣟ [90mRunning c[94momman[18;3H[?25h[m[?25l[90m[15;13Ho[94mmmand[18;3H[?25h[?25l[m[15;1H⡿ [18;3H[?25h[?25l[15;1H⢿ [90mRunning com[94mmand.[18;3H[?25h[?25l[m[15;1H⣻ [90mRunning comm[94mand..[18;3H[?25h[?25l[m[15;1H⣽ [90mRunning comman[94md...[18;3H[?25h[?25l[m[15;1H⣾ [90mRunning command[18;3H[?25h[?25l[m[15;1H⣷ [90mRunning command.[18;3H[?25h[m[?25l[15;1H⣯ [90mRunning command...[18;3H[?25h[m[?25l[15;1H⣟ [18;3H[?25h[?25l[15;1H⡿ [18;3H[?25h[?25l[15;1H⢿ [18;3H[?25h[?25l[15;1H[K
|
||||
I have executed the comm[K
|
||||
⢿ [90mRunning command...[K
|
||||
└ Tip: Use /diff to view uncommitted changes in your workspace.[94m[20;1H>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[20;3H[?25h[?25l[22m[16;27Hand in the t[20;3H[?25h[?25l[16;39Herminal,[20;3H[?25h[?25l[17;1H⣻ [20;3H[?25h[?25l[16;48Hand it successfully printed [94mORCA_PERMISSION_ALLOW_OK[m. Let me know if
|
||||
you need anything else!
|
||||
⣻ [90mRunning command...[K
|
||||
└ Tip: Use /diff to view uncommitted changes in your workspace.[K
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[21;3H[?25h[22m[?25l[18;1H⣽ [21;3H[?25h[?25l[18;1H[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[20;3H[?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[2J[m[H]0;C:\Users\user\AppData\Local\agy\bin\agy.exe[?25h[?1049h[?2004h[>4;2m[>1u[?u[2J[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄[K[m
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m[K
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m[K
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K
|
||||
Welcome to the [38;2;66;133;244mAntigravity CLI[m. You are currently not signed in.[K
|
||||
[K
|
||||
[31mNo authentication methods available.[K[m
|
||||
[K
|
||||
Press ctrl+c or ctrl+d twice to exit.[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[?25l[120C[>4m[<1u[?1049l[>4;2m[>1u[?u[0 q[H[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄ [94m[1mAntigravity CLI 1.2.7[m[K
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m [90mGemini 3.1 Pro (Low)[K[m
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m [90mC:/orca-agy-verify-0920[K[m
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[9;3H[?25h[?25l[H[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄ [94m[1mAntigravity CLI 1.2.7[m[K
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m [90mGemini 3.1 Pro (Low)[K[m
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m [90mC:/orca-agy-verify-0920[K[m
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[9;3H[?25h[?25lUse[11;1H[100X[2m[100CG[9;6H[?25h[22m the[1Cterminal tool to print ORCA_PERMISSION_CAPTURE_OK.[1CDo not modify files.[?25l[9;3H[K[?25h[?25l[90m[11;1H? for shortcuts[9;3H[?25h[m[?25l[8;61H[K[34m[1m
|
||||
> Use the terminal tool to print ORCA_PERMISSION_CAPTURE_OK. Do not modify files.[m
|
||||
[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[12;3H[?25h[?25l[22m[10;1H⣾ [90mGenerating...[14;1Hesc to cancel [12;3H[?25h[?25l[m[10;1H⣷ [12;3H[?25h[?25l[10;1H⣯ [12;3H[?25h[?25l[10;1H⣟ [12;3H[?25h[?25l[10;1H⡿ [12;3H[?25h[?25l[10;1H⢿ [12;3H[?25h[?25l[10;1H⣻ [94mGe[12;3H[?25h[?25l[m[10;1H⣽ [94mGen[12;3H[?25h[?25l[m[10;1H⣾ [94mGene[12;3H[?25h[?25l[m[10;1H⣷ [94mGener[12;3H[?25h[m[?25l[10;1H⣯ [90mGe[94mnerat[12;3H[?25h[m[?25l[10;1H⣟ [90mGen[94merati[12;3H[?25h[m[?25l[10;1H⡿ [90mGene[94mratin[12;3H[?25h[?25l[m[10;1H⢿ [90mGener[94mating[12;3H[?25h[?25l[m[10;1H⣻ [90mGenera[94mting.[12;3H[?25h[?25l[m[10;1H⣽ [90mGenerati[94mng...[12;3H[?25h[?25l[m[10;1H⣾ [90mGeneratin[12;3H[?25h[?25l[m[10;1H⣷ [90mGenerating[12;3H[?25h[?25l[m[10;1H⣯ [90mGenerating.[12;3H[?25h[?25l[m[10;1H⣟ [90mGenerating...[12;3H[?25h[m[?25l[10;1H⡿ [12;3H[?25h[?25l[10;1H⢿ [12;3H[?25h[?25l[10;1H⣻ [12;3H[?25h[?25l[10;1H⣽ [12;3H[?25h[?25l[10;1H⣾ [12;3H[?25h[?25l[10;1H⣷ [94mG[12;3H[?25h[m[?25l[10;1H⣯ [94mGe[12;3H[?25h[?25l[m[10;1H⣟ [94mGene[12;3H[?25h[m[?25l[10;1H⡿ [94mGener[12;3H[?25h[m[?25l[10;1H⢿ [90mG[94menera[12;3H[?25h[m[?25l[10;1H⣻ [90mGe[94mnerat[12;3H[?25h[m[?25l[90m[10;6Hne[94mratin[12;3H[?25h[?25l[m[10;1H⣽ [12;3H[?25h[?25l[10;3H [90mI've zeroed in on the `run_command` tool. My goal is to use [94mit to [90mprint `ORCA_PERMISSION_CAPTURE_OK` without alter...[12;3H[?25h[?25l[m[10;1H⣾ [90mI've zeroed in on the `run_command` tool. My goal is to use [94mit t[90mo[12;3H[?25h[?25l[m[94m[10;62He it [90mt[12;3H[?25h[m[?25l[94m[10;61Hse it [12;3H[?25h[m[?25l[10;1H⣷[12;3H[?25h[?25l[94m[10;60Huse i[90mt[12;3H[?25h[?25l[m[94m[10;59H use [90mi[12;3H[?25h[?25l[m[94m[10;58Ho use [12;3H[?25h[?25l[m[10;1H⣯[12;3H[?25h[?25l[94m[10;57Hto us[90me[12;3H[?25h[m[?25l[94m[10;56H to u[90ms[12;3H[?25h[?25l[m[10;1H⣟[12;3H[?25h[?25l[94m[10;55Hs to [90mu[12;3H[?25h[?25l[m[94m[10;54His to [12;3H[?25h[m[?25l[10;1H⡿ [90mI've zeroed in on the `run_command` tool. My goal [94mis t[90mo[12;3H[?25h[m[?25l[94m[10;52Hl is [90mt[12;3H[?25h[?25l[m[94m[10;51Hal is [12;3H[?25h[m[?25l[10;1H⢿[12;3H[?25h[?25l[94m[10;50Hoal i[90ms[12;3H[?25h[?25l[m[94m[10;49Hgoal [90mi[12;3H[?25h[m[?25l[94m[10;48H goal [12;3H[?25h[m[?25l[10;1H⣻[12;3H[?25h[?25l[94m[10;47Hy goa[90ml[12;3H[?25h[m[?25l[94m[10;46HMy go[90ma[12;3H[?25h[?25l[m[10;1H⣽ [90mI've zeroed in on the `run_command` tool. [94mMy g[90mo[12;3H[?25h[m[?25l[94m[10;44H. My [90mg[12;3H[?25h[?25l[m[10;1H⣾ [90mI've zeroed in on the `run_command` too[94ml. My [12;3H[?25h[m[?25l[94m[10;42Hol. M[90my[12;3H[?25h[m[?25l[94m[10;41Hool. [90mM[12;3H[?25h[?25l[m[10;1H⣷[12;3H[?25h[?25l[94m[10;40Htool. [12;3H[?25h[m[?25l[94m[10;39H tool[90m.[12;3H[?25h[?25l[m[10;1H⣯ [90mI've zeroed in on the `run_command[94m` too[90ml[12;3H[?25h[?25l[m[94m[10;37Hd` to[90mo[12;3H[?25h[m[?25l[94m[10;36Hnd` t[90mo[12;3H[?25h[m[?25l[10;1H⣟[12;3H[?25h[?25l[94m[10;35Hand` [90mt[12;3H[?25h[m[?25l[94m[10;34Hmand` [12;3H[?25h[?25l[m[10;1H⡿[12;3H[?25h[?25l[94m[10;33Hmmand[90m`[12;3H[?25h[?25l[m[94m[10;32Homman[90md[12;3H[?25h[m[?25l[10;1H⢿ [90mI've zeroed in on the `run_[94mcomma[90mn[12;3H[?25h[m[?25l[94m[10;30H_comm[90ma[12;3H[?25h[m[?25l[94m[10;29Hn_com[90mm[12;3H[?25h[m[?25l[10;1H⣻[12;3H[?25h[?25l[94m[10;28Hun_co[90mm[12;3H[?25h[?25l[m[94m[10;27Hrun_c[90mo[12;3H[?25h[m[?25l[94m[10;26H`run_[90mc[12;3H[?25h[?25l[m[10;1H⣽[12;3H[?25h[?25l[94m[10;25H `run[90m_[12;3H[?25h[m[?25l[94m[10;24He `ru[90mn[12;3H[?25h[?25l[m[94m[10;23Hhe `r[90mu[12;3H[?25h[m[?25l[10;1H[K[37m
|
||||
▸ Thought for 4s, 310 tokens[K
|
||||
I've zeroed in on the `run_command` tool. My goal is to use it to print `ORCA_PERMISSION_CAPTURE_OK` without alter...[m
|
||||
[K[33m
|
||||
○ [1mBash[37m[22m(echo "ORCA_PERMISSION_CAPTURE_OK") (ctrl+o to expand)[K[m
|
||||
⣾ [90mRunning command...[K
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[17;3H[?25h[0 q[?25l[22m[33m[14;1H● [m
|
||||
[K[33m[1m
|
||||
Command[m[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[m
|
||||
[K[37m[1m
|
||||
Requesting permission for:[m[K[37m
|
||||
echo "ORCA_PERMISSION_CAPTURE_OK"[K[m
|
||||
[K[1m
|
||||
Run this command?[22m[K[94m
|
||||
> 1. Yes, run command[K[m
|
||||
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[K
|
||||
[K
|
||||
[94m[1m↑/↓[90m[22m Navigate · [94m[1mtab[90m[22m Amend · [94m[1mctrl+g[90m[22m edit/expand command[K
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[22m
|
||||
@@ -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[2J[m[H]0;C:\Users\user\AppData\Local\agy\bin\agy.exe[?25h[?1049h[?2004h[>4;2m[>1u[?u[2J[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄[K[m
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m[K
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m[K
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K
|
||||
Welcome to the [38;2;66;133;244mAntigravity CLI[m. You are currently not signed in.[K
|
||||
[K
|
||||
[31mNo authentication methods available.[K[m
|
||||
[K
|
||||
Press ctrl+c or ctrl+d twice to exit.[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[?25l[120C[>4m[<1u[?1049l[>4;2m[>1u[?u[0 q[H[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄ [94m[1mAntigravity CLI 1.2.7[m[K
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m [90mGemini 3.1 Pro (Low)[K[m
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m [90mC:/orca-agy-verify-0920[K[m
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[9;3H[?25h[?25l[H[K
|
||||
[38;2;219;177;49m▄[38;2;242;146;46m[48;2;246;145;46m▀[38;2;240;114;54m[48;2;243;115;55m▀[38;2;240;88;59m[49m▄ [94m[1mAntigravity CLI 1.2.7[m[K
|
||||
[38;2;158;195;69m[48;2;134;198;78m▀[38;2;181;180;62m[48;2;117;180;94m▀[38;2;226;153;61m[48;2;204;149;77m▀[38;2;246;122;52m[48;2;239;121;71m▀[38;2;248;106;53m[48;2;225;102;82m▀[38;2;239;84;66m[48;2;225;79;89m▀[m [90mGemini 3.1 Pro (Low)[K[m
|
||||
[38;2;124;194;81m[48;2;128;198;84m▀[38;2;113;194;92m[48;2;84;184;129m▀[38;2;92;169;143m[48;2;64;151;222m▀[38;2;92;145;179m[49m▀[38;2;131;115;176m▀[38;2;116;111;195m[48;2;74;126;228m▀[38;2;153;93;168m[48;2;112;110;206m▀[38;2;156;91;151m[48;2;143;100;180m▀[m [90mC:/orca-agy-verify-0920[K[m
|
||||
[38;2;109;198;148m▄[38;2;97;195;125m[48;2;98;186;213m▀[38;2;67;174;171m[48;2;71;168;220m▀[m [38;2;74;128;234m[48;2;61;137;251m▀[38;2;108;115;216m[48;2;74;129;240m▀[38;2;101;121;225m[49m▄[K[m
|
||||
[38;2;103;185;244m▄[38;2;107;199;163m[48;2;100;182;246m▀[38;2;100;182;246m[49m▀ [38;2;56;134;251m▀[38;2;72;129;244m[48;2;56;131;249m▀[38;2;61;133;252m[49m▄[K[m
|
||||
[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[9;3H[?25hUs[?25le[11;1H[100X[2m[100CG[9;7H[?25h[22mthe[1Cterminal tool to[1Cprint ORCA_PERMISSION_CANCEL_OK. Do not modify files.[?25l[9;3H[K[90m[11;1H? for shortcuts[9;3H[?25h[m[?25l[8;61H[K[34m[1m
|
||||
> Use the terminal tool to print ORCA_PERMISSION_CANCEL_OK. Do not modify files.[m
|
||||
⣾ [90mGeneratin[94mg...[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[12;3H[?25h[22m[?25l[10;1H⣷ [12;3H[?25h[?25l[10;1H⣯ [90mGenerating[12;3H[?25h[?25l[m[10;1H⣟ [90mGenerating..[12;3H[?25h[m[?25l[10;1H⡿ [90mGenerating...[12;3H[?25h[?25l[m[10;1H⢿ [12;3H[?25h[?25l[10;1H⣻ [12;3H[?25h[?25l[10;1H⣽ [12;3H[?25h[?25l[10;1H⣾ [12;3H[?25h[?25l[10;1H⣷ [12;3H[?25h[?25l[10;1H⣯ [94mG[12;3H[?25h[m[?25l[10;1H⣟ [94mGe[12;3H[?25h[m[?25l[94m[10;6Hn[90me[12;3H[?25h[m[?25l[10;1H⡿ [94mGene[12;3H[?25h[?25l[m[10;1H⢿ [94mGener[12;3H[?25h[m[?25l[10;1H⣻ [90mG[94menera[12;3H[?25h[?25l[m[10;1H⣽ [90mGe[94mnerat[12;3H[?25h[m[?25l[10;1H⣾ [90mGene[94mratin[12;3H[?25h[m[?25l[10;1H⣷ [90mGener[94mating[12;3H[?25h[?25l[m[10;1H⣯ [90mGenera[94mting.[12;3H[?25h[?25l[m[10;1H⣟ [90mGenerat[94ming..[12;3H[?25h[m[?25l[10;1H⡿ [90mGeneratin[94mg...[12;3H[?25h[m[?25l[10;1H⢿ [90mGenerating[12;3H[?25h[m[?25l[10;1H⣻ [90mGenerating.[12;3H[?25h[?25l[m[10;1H⣽ [90mGenerating..[12;3H[?25h[m[?25l[10;1H⣾ [90mGenerating...[12;3H[?25h[?25l[m[10;1H⣷ [12;3H[?25h[?25l[10;1H⣯ [12;3H[?25h[?25l[10;1H⣟ [12;3H[?25h[?25l[10;1H⡿ [12;3H[?25h[?25l[10;1H⢿ [94mG[12;3H[?25h[?25l[m[10;1H⣻ [94mGe[12;3H[?25h[?25l[m[94m[10;6Hn[90me[12;3H[?25h[?25l[m[10;1H⣽ [90mI'm focusi[94mng in[90mtently on tool specificity. I've been refining my approach to avoid unnecessary use of `cat` within...
|
||||
└ Tip: Use /settings to configure your environment.[K
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[13;3H[?25h[?25l[22m[90m[10;14Hn[94mg int[13;3H[?25h[m[?25l[10;1H⣾ [90mI'm focusing [94minte[13;3H[?25h[?25l[m[90m[10;16H [94minten[13;3H[?25h[m[?25l[90m[10;17Hi[94mntent[13;3H[?25h[m[?25l[10;1H⣷[13;3H[?25h[?25l[90m[10;18Hn[94mtentl[13;3H[?25h[?25l[m[90m[10;19Ht[94mently[13;3H[?25h[m[?25l[90m[10;20He[94mntly [13;3H[?25h[?25l[m[10;1H⣯[13;3H[?25h[?25l[90m[10;21Hn[94mtly o[13;3H[?25h[m[?25l[90m[10;22Ht[94mly on[13;3H[?25h[m[?25l[10;1H⣟ [90mI'm focusing intentl[94my on [13;3H[?25h[m[?25l[90m[10;24Hy [94mon t[13;3H[?25h[m[?25l[10;1H⡿ [90mI'm focusing intently [94mon to[13;3H[?25h[m[?25l[90m[10;26Ho[94mn too[13;3H[?25h[m[?25l[90m[10;27Hn [94mtool[13;3H[?25h[?25l[m[10;1H⢿ [90mI'm focusing intently on [94mtool [13;3H[?25h[m[?25l[90m[10;29Ht[94mool s[13;3H[?25h[m[?25l[90m[10;30Ho[94mol sp[13;3H[?25h[?25l[m[10;1H⣻[13;3H[?25h[?25l[90m[10;31Ho[94ml spe[13;3H[?25h[?25l[m[90m[10;32Hl [94mspec[13;3H[?25h[m[?25l[10;1H⣽ [90mI'm focusing intently on tool [94mspeci[13;3H[?25h[?25l[m[90m[10;34Hs[94mpecif[13;3H[?25h[m[?25l[90m[10;35Hp[94mecifi[13;3H[?25h[m[?25l[10;1H⣾[13;3H[?25h[?25l[90m[10;36He[94mcific[13;3H[?25h[m[?25l[90m[10;37Hc[94mifici[13;3H[?25h[?25l[m[10;1H⣷[13;3H[?25h[?25l[90m[10;38Hi[94mficit[13;3H[?25h[?25l[m[90m[10;39Hf[94micity[13;3H[?25h[?25l[m[90m[10;40Hi[94mcity.[13;3H[?25h[?25l[m[10;1H⣯[13;3H[?25h[?25l[90m[10;41Hc[94mity. [13;3H[?25h[m[?25l[90m[10;42Hi[94mty. I[13;3H[?25h[?25l[m[10;1H⣟ [90mI'm focusing intently on tool specificit[94my. I'[13;3H[?25h[m[?25l[90m[10;44Hy[94m. I'v[13;3H[?25h[m[?25l[10;1H⡿ [90mI'm focusing intently on tool specificity. [94mI've[13;3H[?25h[m[?25l[90m[10;46H [94mI've [13;3H[?25h[m[?25l[90m[10;47HI[94m've b[13;3H[?25h[m[?25l[10;1H⢿ [90mI'm focusing intently on tool specificity. I'[94mve be[13;3H[?25h[m[?25l[90m[10;49Hv[94me bee[13;3H[?25h[?25l[m[10;1H[K[37m
|
||||
▸ Thought for 4s, 365 tokens[K
|
||||
I'm focusing intently on tool specificity. I've been refining my approach to avoid unnecessary use of `cat` within... [m
|
||||
[K[33m
|
||||
● [1mBash[37m[22m(echo ORCA_PERMISSION_CANCEL_OK) (ctrl+o to expand)[K[m
|
||||
⢿ [90mRunning command...[K
|
||||
└ Tip: Use /settings to configure your environment.[K
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[94m
|
||||
>[K[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[18;3H[?25h[0 q[?25l[22m[15;1H[K[33m[1m
|
||||
Command[m[K[18;1H[K[37m[1m
|
||||
Requesting permission for:[m[K[37m
|
||||
echo ORCA_PERMISSION_CANCEL_OK[K[m
|
||||
[K[1m
|
||||
Run this command?[22m[K[94m
|
||||
> 1. Yes, run command[K[m
|
||||
2. Yes, and always allow in this conversation for commands that start with 'echo ORCA_PERMISSION_CANCEL_OK'[K
|
||||
3. Yes, and always allow for commands that start with 'echo ORCA_PERMISSION_CANCEL_OK' (Persist to settings.json)
|
||||
4. No, cancel[K
|
||||
[K
|
||||
[94m[1m↑/↓[90m[22m Navigate · [94m[1mtab[90m[22m Amend · [94m[1mctrl+g[90m[22m edit/expand command[K
|
||||
esc to cancel [K[m[2mGemini 3.1 Pro · low[22m[0 q[90m[14;1H● [16;1H ⎿ Interrupted · What should Antigravity CLI do instead?[94m[18;1H>[90m
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
? for shortcuts [K[m[2mGemini 3.1 Pro · low[22m
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K
|
||||
[K[18;3H[?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()) {
|
||||
|
||||
Reference in New Issue
Block a user