Compare commits
2 Commits
3b1bb9246e
...
5e64860c3f
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e64860c3f | |||
| 4497e966b9 |
@@ -164,8 +164,15 @@
|
||||
- **해결**: 항상 `extension.ts`의 `generateApprovalObserverScript()` 함수를 수정 → 컴파일 → 배포 → Reload
|
||||
- **주의**: HTML inline은 JS파일이 먼저 로드되어 `window.__agSDK` 가드에 의해 실행 안 됨. 실제 실행되는 것은 JS파일 경로의 스크립트
|
||||
|
||||
### [2026-03-09] VS Code Accept — Run 버튼에 잘못된 명령 사용
|
||||
- **증상**: Discord 승인 → `acceptAgentStep` 실행 → "Silent Success" (실제 승인 안 됨)
|
||||
- **원인**: `acceptAgentStep`은 **코드 변경** 승인 전용. Run 버튼 = **터미널 명령** 승인으로 `terminalCommand.run` 또는 `terminalCommand.accept`가 올바른 명령
|
||||
- **해결**: SDK 7개 승인 명령을 step type별로 분기 시도 (`terminalCommand.run` → `terminalCommand.accept` → `command.accept` → `acceptAgentStep`)
|
||||
- **주의**: `terminalCommand.run`의 개별 동작 결과는 아직 미검증. devlog-004에서 순차 시도만 언급됨. AG 재시작 후 E2E 테스트 필요
|
||||
### [2026-03-09] VS Code Accept — SDK 승인 명령이 AG에 미등록
|
||||
- **증상**: Discord 승인 → `antigravity.terminalCommand.run` 등 7개 명령 → 모두 `command not found`
|
||||
- **원인**: SDK(command-bridge.ts)에 정의된 7개 승인 명령이 현재 AG 빌드에 **등록되어 있지 않음**. 활성 시 72개, 세션 중 119개로 동적 등록되지만 승인 관련 명령은 없음
|
||||
- **검증**:
|
||||
- `HandleCascadeUserInteraction` RPC 3 variants → 모두 `socket hang up`
|
||||
- `ResolveOutstandingSteps` → `run state not found` (500 에러, 실제로는 CANCEL 동작)
|
||||
- `sendChatActionMessage`, `executeCascadeAction` → 119개 명령 중 미등록
|
||||
- 존재하는 approval-like 명령: `agentAcceptAllInFile` (코드 diff), `agentAcceptFocusedHunk` (hunk), `acceptCompletion` (자동완성) — 터미널 승인과 무관
|
||||
- **해결**: Renderer DOM Click 구현됨 (미검증). Extension→Renderer HTTP `/trigger-click` 엔드포인트. **AG 완전 재시작 필요** (Reload Window 불가). 실패 시 → `TerminalExecutionPolicy.EAGER` 탐색
|
||||
- **주의**: `agentPanel.focus`도 미등록, `agentSidePanel.focus`만 존재. Renderer가 webview iframe 내부 버튼에 접근 가능한지 미확인
|
||||
|
||||
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
|
||||
| # | 시간 | 작업 설명 | 커밋 | 상태 |
|
||||
|---|------|----------|------|------|
|
||||
| 001 | 08:00~09:17 | 승인 실행 메커니즘 연구 + step-type별 VS Code 명령 분기 구현 | `pending` | 🔧 |
|
||||
| 001 | 08:00~09:17 | 승인 실행 메커니즘 연구 + step-type별 VS Code 명령 분기 구현 | included in 002 | 🔧 |
|
||||
| 002 | 09:21~15:07 | SDK 승인 명령 미등록 확정 + Renderer DOM Click 구현 | `4497e96` | 🔧 |
|
||||
|
||||
49
docs/devlog/entries/20260309-002.md
Normal file
49
docs/devlog/entries/20260309-002.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 승인 명령 탐색 & Renderer DOM Click 구현
|
||||
|
||||
- **시작**: 09:21 KST
|
||||
- **종료**: 15:07 KST
|
||||
- **상태**: 🔧 미완료 (AG 재시작 후 E2E 검증 필요)
|
||||
|
||||
## 핵심 발견
|
||||
|
||||
### 1. SDK 승인 명령 미등록 (Critical)
|
||||
AG 런타임에서 `vscode.commands.getCommands(true)` 로 확인:
|
||||
- **활성화 시**: 72개 `antigravity.*` 명령
|
||||
- **세션 활성 중**: 119개 (47개 동적 등록)
|
||||
- **SDK 7개 승인 명령**: 전부 **미등록**
|
||||
- `antigravity.terminalCommand.run/accept/reject` ❌
|
||||
- `antigravity.command.accept/reject` ❌
|
||||
- `antigravity.agent.acceptAgentStep/rejectAgentStep` ❌
|
||||
- `antigravity.agentPanel.focus` ❌ (`agentSidePanel.focus`만 존재)
|
||||
|
||||
### 2. RPC 전략 실패
|
||||
- `HandleCascadeUserInteraction` 3 variants → `socket hang up`
|
||||
- `ResolveOutstandingSteps` → `run state not found` (500)
|
||||
|
||||
### 3. 키보드 시뮬레이션 실패
|
||||
- `toggleChatFocus` + `type {Enter}` → 채팅 입력창에 엔터 전송 (빈 메시지 전송 ❌)
|
||||
- `sendPromptToAgentPanel('')` → 빈 프롬프트 전송 됨 (승인이 아님 ❌)
|
||||
- `agentSidePanel.focus` → 성공하지만 승인 안 됨
|
||||
|
||||
### 4. sendChatActionMessage / executeCascadeAction 미등록
|
||||
- 119개 명령 중 이 두 명령도 존재하지 않음
|
||||
|
||||
### 5. Renderer DOM Click 구현 완료 (미검증)
|
||||
- Extension HTTP bridge에 `GET /trigger-click` 엔드포인트 추가
|
||||
- `tryApprovalStrategies()` Strategy 2 → `clickTrigger` 플래그 설정
|
||||
- Renderer 스크립트에 1초 폴링 추가 → `/trigger-click` 수신 시 DOM에서 Run/Accept 버튼 `btn.click()`
|
||||
- **미검증**: Reload Window로는 workbench HTML 재파싱 안 됨. **AG 완전 재시작 필요**
|
||||
|
||||
## 다음 단계
|
||||
|
||||
1. **AG 완전 재시작** → renderer 스크립트 활성화 확인
|
||||
2. **봇 시작 + Discord 승인** → `[HTTP] trigger-click consumed` 로그 확인
|
||||
3. **webview iframe 접근 여부** 확인 — renderer가 iframe 안의 Run 버튼에 접근 가능한지
|
||||
4. 실패 시 → AG Settings에서 `TerminalExecutionPolicy.EAGER` 모드 탐색
|
||||
|
||||
## 파일 변경
|
||||
|
||||
| 파일 | 변경 |
|
||||
|------|------|
|
||||
| `extension/src/extension.ts` | CMD-DISCOVERY 진단, APPROVAL-CMD-CHECK(동적), clickTrigger + /trigger-click, Strategy 2 교체, renderer trigger-click 폴링 |
|
||||
| `.agents/references/known-issues.md` | #19 SDK 승인 명령 미등록 기록 |
|
||||
@@ -409,6 +409,8 @@ function updateProductChecksums() {
|
||||
// ─── HTTP Bridge Server (Extension Host → Renderer communication) ───
|
||||
let observerHttpServer = null;
|
||||
const pendingResponses = new Map();
|
||||
// Click trigger: extension sets this, renderer polls and clicks button
|
||||
let clickTrigger = null;
|
||||
/** Derive a deterministic port from project name (range 10000-60000) */
|
||||
function getDeterministicPort(name) {
|
||||
let hash = 0;
|
||||
@@ -490,6 +492,21 @@ function startObserverHttpBridge() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// GET /trigger-click — renderer polls to check if extension wants a click
|
||||
if (req.method === 'GET' && url.pathname === '/trigger-click') {
|
||||
if (clickTrigger && (Date.now() - clickTrigger.timestamp) < 30000) {
|
||||
const trigger = clickTrigger;
|
||||
clickTrigger = null; // consume once
|
||||
logToFile(`[HTTP] trigger-click consumed: ${trigger.action}`);
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ action: trigger.action }));
|
||||
}
|
||||
else {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ action: null }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// GET /ping — health check
|
||||
if (url.pathname === '/ping') {
|
||||
res.writeHead(200);
|
||||
@@ -852,8 +869,51 @@ function generateApprovalObserverScript(_port) {
|
||||
// FALLBACK: periodic scan every 3s for any missed mutations
|
||||
setInterval(scheduleScan,3000);
|
||||
|
||||
// ── TRIGGER-CLICK: Extension→Renderer bridge for programmatic button clicks ──
|
||||
// Extension sets clickTrigger via tryApprovalStrategies → renderer polls and clicks
|
||||
setInterval(function(){
|
||||
if(!_ready||!BASE)return;
|
||||
fetch(BASE+'/trigger-click?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||||
if(!d.action)return;
|
||||
log('🔔 TRIGGER-CLICK received: action='+d.action);
|
||||
// Find first visible approve or reject button
|
||||
var allBtns=document.querySelectorAll('button');
|
||||
if(d.action==='approve'){
|
||||
// Click first Run/Accept/Allow/Continue button
|
||||
var approveRe=[/^Run/i,/^Accept/i,/^Allow/i,/^Approve/i,/^Continue$/i,/^Proceed$/i];
|
||||
for(var i=0;i<allBtns.length;i++){
|
||||
var b=allBtns[i];
|
||||
if(b.disabled||b.hidden||!b.offsetParent)continue;
|
||||
var txt=(b.textContent||'').trim();
|
||||
for(var p=0;p<approveRe.length;p++){
|
||||
if(approveRe[p].test(txt)){
|
||||
log('✅ TRIGGER-CLICK: clicking "'+txt+'"');
|
||||
b.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
log('⚠️ TRIGGER-CLICK: no approve button found in DOM');
|
||||
} else if(d.action==='reject'){
|
||||
for(var j=0;j<allBtns.length;j++){
|
||||
var b2=allBtns[j];
|
||||
if(b2.disabled||b2.hidden||!b2.offsetParent)continue;
|
||||
var txt2=(b2.textContent||'').trim();
|
||||
for(var r=0;r<REJECT_RE.length;r++){
|
||||
if(REJECT_RE[r].test(txt2)){
|
||||
log('❌ TRIGGER-CLICK: clicking "'+txt2+'"');
|
||||
b2.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
log('⚠️ TRIGGER-CLICK: no reject button found in DOM');
|
||||
}
|
||||
}).catch(function(){});
|
||||
},1000);
|
||||
|
||||
_obs=true;
|
||||
log('v2 Observer active — MutationObserver + 3s fallback');
|
||||
log('v2 Observer active — MutationObserver + 3s fallback + trigger-click polling');
|
||||
}
|
||||
})();
|
||||
`;
|
||||
@@ -1420,6 +1480,28 @@ function writePendingApproval(data) {
|
||||
async function tryApprovalStrategies(approved, sessionId) {
|
||||
const action = approved ? 'APPROVE' : 'REJECT';
|
||||
logToFile(`[APPROVAL] Starting ${action} strategies for session ${sessionId.substring(0, 8)}`);
|
||||
// ── Dynamic Command Discovery (check if approval commands appear when step is WAITING) ──
|
||||
try {
|
||||
const allCmds = await vscode.commands.getCommands(true);
|
||||
const agCmds = allCmds.filter((c) => c.startsWith('antigravity.'));
|
||||
const approvalCmds = agCmds.filter((c) => {
|
||||
const lower = c.toLowerCase();
|
||||
return lower.includes('accept') || lower.includes('reject') || lower.includes('approve')
|
||||
|| lower.includes('terminal') || lower.includes('run') || lower.includes('step');
|
||||
});
|
||||
logToFile(`[APPROVAL-CMD-CHECK] ${agCmds.length} total, ${approvalCmds.length} approval-related:`);
|
||||
for (const c of approvalCmds) {
|
||||
logToFile(`[APPROVAL-CMD-CHECK] → ${c}`);
|
||||
}
|
||||
// Dump ALL antigravity.* commands for full comparison
|
||||
logToFile(`[APPROVAL-CMD-CHECK] FULL LIST (${agCmds.length}):`);
|
||||
for (const c of agCmds) {
|
||||
logToFile(`[APPROVAL-CMD-FULL] ${c}`);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[APPROVAL-CMD-CHECK] error: ${e.message}`);
|
||||
}
|
||||
// ── Strategy 1: HandleCascadeUserInteraction RPC ──
|
||||
if (sdk) {
|
||||
// Try variant A: { cascadeId, approved }
|
||||
@@ -1464,54 +1546,18 @@ async function tryApprovalStrategies(approved, sessionId) {
|
||||
logToFile(`[APPROVAL-1C] ❌ FAIL: ${e.message}`);
|
||||
}
|
||||
}
|
||||
// ── Strategy 2: VS Code Commands — step-type-specific (focus-dependent) ──
|
||||
// Per SDK research (2026-03-09):
|
||||
// Run button = terminal command → terminalCommand.run / terminalCommand.accept
|
||||
// Code changes = agent step → agent.acceptAgentStep
|
||||
// General commands = command.accept
|
||||
// Previously only tried acceptAgentStep (Silent Success) — now tries ALL 7
|
||||
// Try to focus the panel first (required for command.accept / acceptAgentStep)
|
||||
// ── Strategy 2: Renderer DOM Click via HTTP Bridge ──
|
||||
// 2026-03-09: All SDK approval commands NOT REGISTERED (119 cmds tested).
|
||||
// Keyboard simulation sends Enter to chat input (sends empty message — WRONG).
|
||||
// New approach: set clickTrigger flag → renderer polls /trigger-click → clicks button.
|
||||
try {
|
||||
logToFile(`[APPROVAL-2] focusing panel...`);
|
||||
await vscode.commands.executeCommand('antigravity.agentPanel.focus');
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
await vscode.commands.executeCommand('antigravity.agentSidePanel.focus');
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
logToFile(`[APPROVAL-2] panel focus attempted (agentPanel + agentSidePanel)`);
|
||||
const triggerAction = approved ? 'approve' : 'reject';
|
||||
logToFile(`[APPROVAL-2] Setting clickTrigger=${triggerAction} for renderer DOM click`);
|
||||
clickTrigger = { action: triggerAction, timestamp: Date.now() };
|
||||
logToFile(`[APPROVAL-2] ✅ clickTrigger set — renderer will poll and click within 2s`);
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[APPROVAL-2] panel focus failed: ${e.message}`);
|
||||
}
|
||||
// All 7 approval commands in priority order (terminal first for Run button)
|
||||
const commands = approved
|
||||
? [
|
||||
// Terminal commands (Run button) — UNTESTED INDIVIDUALLY (devlog-004)
|
||||
'antigravity.terminalCommand.run', // SDK: TERMINAL_RUN
|
||||
'antigravity.terminalCommand.accept', // SDK: TERMINAL_ACCEPT
|
||||
// General command approval
|
||||
'antigravity.command.accept', // SDK: COMMAND_ACCEPT
|
||||
// Agent step approval (known: Silent Success with focus)
|
||||
'antigravity.agent.acceptAgentStep', // SDK: ACCEPT_AGENT_STEP
|
||||
// Cascade action (experimental)
|
||||
// 'antigravity.executeCascadeAction', // SDK: needs action param — skip
|
||||
]
|
||||
: [
|
||||
'antigravity.terminalCommand.reject', // SDK: TERMINAL_REJECT
|
||||
'antigravity.command.reject', // SDK: COMMAND_REJECT
|
||||
'antigravity.agent.rejectAgentStep', // SDK: REJECT_AGENT_STEP
|
||||
];
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
const cmd = commands[i];
|
||||
try {
|
||||
const t0 = Date.now();
|
||||
logToFile(`[APPROVAL-2${String.fromCharCode(65 + i)}] executing: ${cmd}`);
|
||||
const result = await vscode.commands.executeCommand(cmd);
|
||||
const dt = Date.now() - t0;
|
||||
logToFile(`[APPROVAL-2${String.fromCharCode(65 + i)}] returned: ${JSON.stringify(result)} (${dt}ms)`);
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[APPROVAL-2${String.fromCharCode(65 + i)}] ❌ FAIL: ${e.message}`);
|
||||
}
|
||||
logToFile(`[APPROVAL-2] ❌ FAIL: ${e.message}`);
|
||||
}
|
||||
// ── Strategy 3: ResolveOutstandingSteps (REJECT ONLY — this CANCELS!) ──
|
||||
if (!approved && sdk) {
|
||||
@@ -1551,6 +1597,28 @@ async function activate(context) {
|
||||
// Initialize SDK
|
||||
const sdkReady = await initSDK(context);
|
||||
if (sdkReady) {
|
||||
// ── Command Discovery Diagnostic ──
|
||||
// Enumerate ALL antigravity.* commands to find correct approval command names
|
||||
try {
|
||||
const allCmds = await vscode.commands.getCommands(true);
|
||||
const agCmds = allCmds.filter((c) => c.startsWith('antigravity.'));
|
||||
logToFile(`[CMD-DISCOVERY] Total antigravity.* commands: ${agCmds.length}`);
|
||||
// Log approval-related commands specifically
|
||||
const approvalKeywords = ['accept', 'reject', 'approve', 'terminal', 'agent', 'cascade', 'step', 'run', 'command.'];
|
||||
const relevantCmds = agCmds.filter((c) => approvalKeywords.some(kw => c.toLowerCase().includes(kw)));
|
||||
logToFile(`[CMD-DISCOVERY] Approval-related commands (${relevantCmds.length}):`);
|
||||
for (const cmd of relevantCmds) {
|
||||
logToFile(`[CMD-DISCOVERY] → ${cmd}`);
|
||||
}
|
||||
// Also dump ALL commands for full reference
|
||||
logToFile(`[CMD-DISCOVERY] ALL antigravity.* commands:`);
|
||||
for (const cmd of agCmds) {
|
||||
logToFile(`[CMD-DISCOVERY] ${cmd}`);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[CMD-DISCOVERY] error: ${e.message}`);
|
||||
}
|
||||
setupMonitor(); // Now just logs that monitor is disabled
|
||||
setupApprovalObserver(); // DOM observer via SDK IntegrationManager
|
||||
statusBar.text = '$(check) Bridge';
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -382,6 +382,9 @@ function updateProductChecksums() {
|
||||
let observerHttpServer: any = null;
|
||||
const pendingResponses = new Map<string, { approved: boolean } | null>();
|
||||
|
||||
// Click trigger: extension sets this, renderer polls and clicks button
|
||||
let clickTrigger: { action: 'approve' | 'reject'; timestamp: number } | null = null;
|
||||
|
||||
/** Derive a deterministic port from project name (range 10000-60000) */
|
||||
function getDeterministicPort(name: string): number {
|
||||
let hash = 0;
|
||||
@@ -457,6 +460,21 @@ function startObserverHttpBridge(): Promise<number> {
|
||||
return;
|
||||
}
|
||||
|
||||
// GET /trigger-click — renderer polls to check if extension wants a click
|
||||
if (req.method === 'GET' && url.pathname === '/trigger-click') {
|
||||
if (clickTrigger && (Date.now() - clickTrigger.timestamp) < 30000) {
|
||||
const trigger = clickTrigger;
|
||||
clickTrigger = null; // consume once
|
||||
logToFile(`[HTTP] trigger-click consumed: ${trigger.action}`);
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ action: trigger.action }));
|
||||
} else {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ action: null }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// GET /ping — health check
|
||||
if (url.pathname === '/ping') {
|
||||
res.writeHead(200); res.end('pong');
|
||||
@@ -823,8 +841,51 @@ function generateApprovalObserverScript(_port: number): string {
|
||||
// FALLBACK: periodic scan every 3s for any missed mutations
|
||||
setInterval(scheduleScan,3000);
|
||||
|
||||
// ── TRIGGER-CLICK: Extension→Renderer bridge for programmatic button clicks ──
|
||||
// Extension sets clickTrigger via tryApprovalStrategies → renderer polls and clicks
|
||||
setInterval(function(){
|
||||
if(!_ready||!BASE)return;
|
||||
fetch(BASE+'/trigger-click?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||||
if(!d.action)return;
|
||||
log('🔔 TRIGGER-CLICK received: action='+d.action);
|
||||
// Find first visible approve or reject button
|
||||
var allBtns=document.querySelectorAll('button');
|
||||
if(d.action==='approve'){
|
||||
// Click first Run/Accept/Allow/Continue button
|
||||
var approveRe=[/^Run/i,/^Accept/i,/^Allow/i,/^Approve/i,/^Continue$/i,/^Proceed$/i];
|
||||
for(var i=0;i<allBtns.length;i++){
|
||||
var b=allBtns[i];
|
||||
if(b.disabled||b.hidden||!b.offsetParent)continue;
|
||||
var txt=(b.textContent||'').trim();
|
||||
for(var p=0;p<approveRe.length;p++){
|
||||
if(approveRe[p].test(txt)){
|
||||
log('✅ TRIGGER-CLICK: clicking "'+txt+'"');
|
||||
b.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
log('⚠️ TRIGGER-CLICK: no approve button found in DOM');
|
||||
} else if(d.action==='reject'){
|
||||
for(var j=0;j<allBtns.length;j++){
|
||||
var b2=allBtns[j];
|
||||
if(b2.disabled||b2.hidden||!b2.offsetParent)continue;
|
||||
var txt2=(b2.textContent||'').trim();
|
||||
for(var r=0;r<REJECT_RE.length;r++){
|
||||
if(REJECT_RE[r].test(txt2)){
|
||||
log('❌ TRIGGER-CLICK: clicking "'+txt2+'"');
|
||||
b2.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
log('⚠️ TRIGGER-CLICK: no reject button found in DOM');
|
||||
}
|
||||
}).catch(function(){});
|
||||
},1000);
|
||||
|
||||
_obs=true;
|
||||
log('v2 Observer active — MutationObserver + 3s fallback');
|
||||
log('v2 Observer active — MutationObserver + 3s fallback + trigger-click polling');
|
||||
}
|
||||
})();
|
||||
`;
|
||||
@@ -1390,6 +1451,28 @@ async function tryApprovalStrategies(approved: boolean, sessionId: string): Prom
|
||||
const action = approved ? 'APPROVE' : 'REJECT';
|
||||
logToFile(`[APPROVAL] Starting ${action} strategies for session ${sessionId.substring(0, 8)}`);
|
||||
|
||||
// ── Dynamic Command Discovery (check if approval commands appear when step is WAITING) ──
|
||||
try {
|
||||
const allCmds = await vscode.commands.getCommands(true);
|
||||
const agCmds = allCmds.filter((c: string) => c.startsWith('antigravity.'));
|
||||
const approvalCmds = agCmds.filter((c: string) => {
|
||||
const lower = c.toLowerCase();
|
||||
return lower.includes('accept') || lower.includes('reject') || lower.includes('approve')
|
||||
|| lower.includes('terminal') || lower.includes('run') || lower.includes('step');
|
||||
});
|
||||
logToFile(`[APPROVAL-CMD-CHECK] ${agCmds.length} total, ${approvalCmds.length} approval-related:`);
|
||||
for (const c of approvalCmds) {
|
||||
logToFile(`[APPROVAL-CMD-CHECK] → ${c}`);
|
||||
}
|
||||
// Dump ALL antigravity.* commands for full comparison
|
||||
logToFile(`[APPROVAL-CMD-CHECK] FULL LIST (${agCmds.length}):`);
|
||||
for (const c of agCmds) {
|
||||
logToFile(`[APPROVAL-CMD-FULL] ${c}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
logToFile(`[APPROVAL-CMD-CHECK] error: ${e.message}`);
|
||||
}
|
||||
|
||||
// ── Strategy 1: HandleCascadeUserInteraction RPC ──
|
||||
if (sdk) {
|
||||
// Try variant A: { cascadeId, approved }
|
||||
@@ -1434,55 +1517,17 @@ async function tryApprovalStrategies(approved: boolean, sessionId: string): Prom
|
||||
}
|
||||
}
|
||||
|
||||
// ── Strategy 2: VS Code Commands — step-type-specific (focus-dependent) ──
|
||||
// Per SDK research (2026-03-09):
|
||||
// Run button = terminal command → terminalCommand.run / terminalCommand.accept
|
||||
// Code changes = agent step → agent.acceptAgentStep
|
||||
// General commands = command.accept
|
||||
// Previously only tried acceptAgentStep (Silent Success) — now tries ALL 7
|
||||
|
||||
// Try to focus the panel first (required for command.accept / acceptAgentStep)
|
||||
// ── Strategy 2: Renderer DOM Click via HTTP Bridge ──
|
||||
// 2026-03-09: All SDK approval commands NOT REGISTERED (119 cmds tested).
|
||||
// Keyboard simulation sends Enter to chat input (sends empty message — WRONG).
|
||||
// New approach: set clickTrigger flag → renderer polls /trigger-click → clicks button.
|
||||
try {
|
||||
logToFile(`[APPROVAL-2] focusing panel...`);
|
||||
await vscode.commands.executeCommand('antigravity.agentPanel.focus');
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
await vscode.commands.executeCommand('antigravity.agentSidePanel.focus');
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
logToFile(`[APPROVAL-2] panel focus attempted (agentPanel + agentSidePanel)`);
|
||||
const triggerAction = approved ? 'approve' : 'reject';
|
||||
logToFile(`[APPROVAL-2] Setting clickTrigger=${triggerAction} for renderer DOM click`);
|
||||
clickTrigger = { action: triggerAction as 'approve' | 'reject', timestamp: Date.now() };
|
||||
logToFile(`[APPROVAL-2] ✅ clickTrigger set — renderer will poll and click within 2s`);
|
||||
} catch (e: any) {
|
||||
logToFile(`[APPROVAL-2] panel focus failed: ${e.message}`);
|
||||
}
|
||||
|
||||
// All 7 approval commands in priority order (terminal first for Run button)
|
||||
const commands = approved
|
||||
? [
|
||||
// Terminal commands (Run button) — UNTESTED INDIVIDUALLY (devlog-004)
|
||||
'antigravity.terminalCommand.run', // SDK: TERMINAL_RUN
|
||||
'antigravity.terminalCommand.accept', // SDK: TERMINAL_ACCEPT
|
||||
// General command approval
|
||||
'antigravity.command.accept', // SDK: COMMAND_ACCEPT
|
||||
// Agent step approval (known: Silent Success with focus)
|
||||
'antigravity.agent.acceptAgentStep', // SDK: ACCEPT_AGENT_STEP
|
||||
// Cascade action (experimental)
|
||||
// 'antigravity.executeCascadeAction', // SDK: needs action param — skip
|
||||
]
|
||||
: [
|
||||
'antigravity.terminalCommand.reject', // SDK: TERMINAL_REJECT
|
||||
'antigravity.command.reject', // SDK: COMMAND_REJECT
|
||||
'antigravity.agent.rejectAgentStep', // SDK: REJECT_AGENT_STEP
|
||||
];
|
||||
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
const cmd = commands[i];
|
||||
try {
|
||||
const t0 = Date.now();
|
||||
logToFile(`[APPROVAL-2${String.fromCharCode(65 + i)}] executing: ${cmd}`);
|
||||
const result = await vscode.commands.executeCommand(cmd);
|
||||
const dt = Date.now() - t0;
|
||||
logToFile(`[APPROVAL-2${String.fromCharCode(65 + i)}] returned: ${JSON.stringify(result)} (${dt}ms)`);
|
||||
} catch (e: any) {
|
||||
logToFile(`[APPROVAL-2${String.fromCharCode(65 + i)}] ❌ FAIL: ${e.message}`);
|
||||
}
|
||||
logToFile(`[APPROVAL-2] ❌ FAIL: ${e.message}`);
|
||||
}
|
||||
|
||||
// ── Strategy 3: ResolveOutstandingSteps (REJECT ONLY — this CANCELS!) ──
|
||||
@@ -1530,6 +1575,30 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
const sdkReady = await initSDK(context);
|
||||
|
||||
if (sdkReady) {
|
||||
// ── Command Discovery Diagnostic ──
|
||||
// Enumerate ALL antigravity.* commands to find correct approval command names
|
||||
try {
|
||||
const allCmds = await vscode.commands.getCommands(true);
|
||||
const agCmds = allCmds.filter((c: string) => c.startsWith('antigravity.'));
|
||||
logToFile(`[CMD-DISCOVERY] Total antigravity.* commands: ${agCmds.length}`);
|
||||
// Log approval-related commands specifically
|
||||
const approvalKeywords = ['accept', 'reject', 'approve', 'terminal', 'agent', 'cascade', 'step', 'run', 'command.'];
|
||||
const relevantCmds = agCmds.filter((c: string) =>
|
||||
approvalKeywords.some(kw => c.toLowerCase().includes(kw))
|
||||
);
|
||||
logToFile(`[CMD-DISCOVERY] Approval-related commands (${relevantCmds.length}):`);
|
||||
for (const cmd of relevantCmds) {
|
||||
logToFile(`[CMD-DISCOVERY] → ${cmd}`);
|
||||
}
|
||||
// Also dump ALL commands for full reference
|
||||
logToFile(`[CMD-DISCOVERY] ALL antigravity.* commands:`);
|
||||
for (const cmd of agCmds) {
|
||||
logToFile(`[CMD-DISCOVERY] ${cmd}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
logToFile(`[CMD-DISCOVERY] error: ${e.message}`);
|
||||
}
|
||||
|
||||
setupMonitor(); // Now just logs that monitor is disabled
|
||||
setupApprovalObserver(); // DOM observer via SDK IntegrationManager
|
||||
statusBar.text = '$(check) Bridge';
|
||||
|
||||
Reference in New Issue
Block a user