Compare commits
11 Commits
a99a1e3f54
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32cf69469c | ||
|
|
7c8891b99c | ||
|
|
3cc3442fda | ||
|
|
e95e7791f9 | ||
|
|
2bf1eb41d1 | ||
|
|
cf1352eefa | ||
|
|
6aea48e2e9 | ||
|
|
bd5a7ca8b9 | ||
|
|
8ada5f7daf | ||
|
|
4f2be831a1 | ||
|
|
cbfd137dcb |
@@ -21,6 +21,18 @@
|
|||||||
> 鍮꾩듂븳 臾몄젣媛 옱諛쒗븯硫 archive뿉꽌 寃깋븯꽭슂.
|
> 鍮꾩듂븳 臾몄젣媛 옱諛쒗븯硫 archive뿉꽌 寃깋븯꽭슂.
|
||||||
|
|
||||||
|
|
||||||
|
### [2026-04-19] [Observer] ★ Accept all 버튼이 `<span>`으로 렌더링 — Observer 감지 실패 (v0.5.101)
|
||||||
|
- **증상**: "Accept all" diff review 버튼이 화면에 보이지만 Observer가 감지하지 못함. Discord에 "Accept all" 자동 승인 알림이 안 옴.
|
||||||
|
- **원인**: AG Native UI 업데이트로 "Accept all"이 `<button>`이 아닌 `<span class="cursor-pointer">`로 렌더링됨. Observer의 `allBtns = querySelectorAll('button, [role="button"]')`에 span이 포함되지 않음. ACCEPT-SCAN 로그: `tag=SPAN cls=hover:text-ide-button-hover-color cursor-po txt=Accept all`.
|
||||||
|
- **해결 (v0.5.101)**: `allBtns` 선택자에 `span.cursor-pointer` 추가.
|
||||||
|
- **주의**: observer-dev-guide 섹션 3.3 "Accept all — Observer 접근 불가"는 outdated. UI 변경으로 chat panel footer에 Accept all이 표시됨. 가이드 업데이트 필요.
|
||||||
|
|
||||||
|
### [2026-04-19] [Bridge] ★ auto-approve response 파일에 `_from_ws` 마커 누락 — Observer polling 실패 (v0.5.103)
|
||||||
|
- **증상**: Observer가 "Accept all"을 감지하고 bridge가 자동 승인했지만, Observer의 `pollResponseGroup` GET `/response/{rid}`가 항상 `{waiting: true}` 반환. 버튼 클릭이 실행되지 않음.
|
||||||
|
- **원인**: http-bridge의 auto-approve 경로에서 response JSON 파일에 `_from_ws: true` 마커가 없음 → `processResponseFile`(response watcher)이 Observer보다 먼저 파일을 읽고 삭제 → Observer polling 시 파일 부재. known-issues [2026-04-18] WS response 삭제 버그와 동일 패턴.
|
||||||
|
- **해결 (v0.5.103)**: auto-approve response에 `_from_ws: true` + `_auto_approve_ttl` 마커 추가.
|
||||||
|
- **주의**: **response 디렉토리에 파일을 쓰는 모든 경로**는 반드시 `_from_ws: true` 마커를 포함해야 함. processResponseFile이 먼저 소비하는 race condition 항상 존재.
|
||||||
|
|
||||||
### [2026-04-18] [Extension] ★ WS response 파일이 processResponseFile에 의해 삭제 → Observer pollResponseGroup 실패 (v0.5.78)
|
### [2026-04-18] [Extension] ★ WS response 파일이 processResponseFile에 의해 삭제 → Observer pollResponseGroup 실패 (v0.5.78)
|
||||||
- **증상**: `!auto` Retry가 작동하지 않음. Observer가 `/response/{rid}`를 폴링하지만 항상 `{waiting: true}` 반환.
|
- **증상**: `!auto` Retry가 작동하지 않음. Observer가 `/response/{rid}`를 폴링하지만 항상 `{waiting: true}` 반환.
|
||||||
- **원인**: extension.ts의 WS 응답 핸들러가 `response/{rid}.json` 파일 작성 → 300ms 후 response watcher(`processResponseFile`)가 파일 감지 → pending 파일이 없어 `isDomObserver=false` → `fs.unlinkSync()` 실행 → Observer가 폴링할 때 파일이 이미 삭제됨.
|
- **원인**: extension.ts의 WS 응답 핸들러가 `response/{rid}.json` 파일 작성 → 300ms 후 response watcher(`processResponseFile`)가 파일 감지 → pending 파일이 없어 `isDomObserver=false` → `fs.unlinkSync()` 실행 → Observer가 폴링할 때 파일이 이미 삭제됨.
|
||||||
|
|||||||
@@ -105,9 +105,12 @@ Select-String -Path "$env:LOCALAPPDATA\Programs\Antigravity\resources\app\out\vs
|
|||||||
|
|
||||||
- "Retry" 버튼: button 태그, chat panel 내
|
- "Retry" 버튼: button 태그, chat panel 내
|
||||||
|
|
||||||
### 3.3 Diff Review (Accept all / Reject all) — Observer 접근 불가
|
### 3.3 Diff Review (Accept all / Reject all) — Observer 접근 가능 (v0.5.101+)
|
||||||
- 에디터 webview에 렌더링, Observer document에서 접근 불가
|
- **v0.5.101 이전**: 에디터 webview에 렌더링, Observer document에서 접근 불가
|
||||||
- 해결: Extension host에서 antigravity.acceptAgentStep 명령 실행
|
- **v0.5.101 이후**: AG UI 업데이트로 chat panel footer에 `<span class="cursor-pointer">` 태그로 렌더링
|
||||||
|
- Observer의 `allBtns` 선택자에 `span.cursor-pointer` 포함 필수
|
||||||
|
- `matchedType = 'diff_review'`로 분류됨 (L1120: `txt.includes('Accept')`)
|
||||||
|
- auto-approve response 파일에 `_from_ws: true` 마커 필수 (processResponseFile race condition 방지)
|
||||||
|
|
||||||
### 3.4 DOM 렌더링 타이밍
|
### 3.4 DOM 렌더링 타이밍
|
||||||
- "Always run" 버튼이 DOM에 나타날 때 명령어 div도 함께 렌더링됨
|
- "Always run" 버튼이 DOM에 나타날 때 명령어 div도 함께 렌더링됨
|
||||||
|
|||||||
26
docs/devlog/2026-04-19.md
Normal file
26
docs/devlog/2026-04-19.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Devlog 2026-04-19
|
||||||
|
|
||||||
|
## 작업 인덱스
|
||||||
|
|
||||||
|
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||||
|
|---|------|------|------|------|
|
||||||
|
| 001 | 21:22 | v30-32 Observer 명령어 추출 안정화 (터미널 프롬프트 조기감지) | `bd5a7ca` | ✅ |
|
||||||
|
| 002 | 23:16 | v33 Accept all 자동승인 — diff review auto-approve | `6aea48e` | ✅ |
|
||||||
|
| 003 | 00:18 | v34 Accept all 이중 보장 — agentAcceptAllInFile 직접 호출 | `cf1352e` | ✅ |
|
||||||
|
| 004 | 00:34 | v35 code_edit 자동 Accept — step-probe 경로 | `2bf1eb4` | ✅ |
|
||||||
|
| 005 | 04:26 | v36 Accept all span 감지 — 근본 원인 발견 (button→span) | `e95e779` | ✅ |
|
||||||
|
| 006 | 04:34 | v37 openReviewChanges 선호출 — agentAcceptAllInFile 보조 | `3cc3442` | ✅ |
|
||||||
|
| 007 | 04:43 | v38 _from_ws 마커 추가 — Observer polling 실패 근본 수정 | `7c8891b` | ✅ |
|
||||||
|
|
||||||
|
## v0.5.103 — Accept all (Diff Review) 자동 승인 복구
|
||||||
|
|
||||||
|
### 근본 원인 (2가지)
|
||||||
|
1. **Observer 감지 실패**: AG UI가 "Accept all"을 `<button>`이 아닌 `<span class="cursor-pointer">`로 렌더링. Observer의 `allBtns` 선택자가 `button`만 스캔하여 미감지.
|
||||||
|
2. **Response 파일 race condition**: auto-approve response 파일에 `_from_ws: true` 마커 없음 → `processResponseFile`이 Observer보다 먼저 파일 삭제 → Observer polling 무한 실패.
|
||||||
|
|
||||||
|
### 검증 결과
|
||||||
|
- Observer ACCEPT-SCAN: `tag=SPAN cls=cursor-pointer txt=Accept all` ✅
|
||||||
|
- `DETECTED diff_review: Accept all` ✅
|
||||||
|
- `response served to renderer: ...approved=true` (이전 0건 → 7건) ✅
|
||||||
|
- Discord "자동 승인됨 Accept all" 표시 ✅
|
||||||
|
- 화면에서 "Accept all" 버튼 자동 소멸 확인 ✅
|
||||||
4
extension/package-lock.json
generated
4
extension/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "gravity-bridge",
|
"name": "gravity-bridge",
|
||||||
"version": "0.5.93",
|
"version": "0.5.103",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "gravity-bridge",
|
"name": "gravity-bridge",
|
||||||
"version": "0.5.93",
|
"version": "0.5.103",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cheerio": "^1.2.0",
|
"cheerio": "^1.2.0",
|
||||||
"ws": "^8.19.0"
|
"ws": "^8.19.0"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "gravity-bridge",
|
"name": "gravity-bridge",
|
||||||
"displayName": "Gravity Bridge",
|
"displayName": "Gravity Bridge",
|
||||||
"description": "Discord-based unified approval system for Antigravity AI interactions.",
|
"description": "Discord-based unified approval system for Antigravity AI interactions.",
|
||||||
"version": "0.5.93",
|
"version": "0.5.103",
|
||||||
"publisher": "variet",
|
"publisher": "variet",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.100.0"
|
"vscode": "^1.100.0"
|
||||||
|
|||||||
22
extension/scratch/test_accept.js
Normal file
22
extension/scratch/test_accept.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Test: call agentAcceptAllInFile via extension's HTTP bridge trigger-click
|
||||||
|
// This simulates what the approval handler does
|
||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
const PORT = 34332; // from observer setup log
|
||||||
|
|
||||||
|
// Write a trigger-click file to make Observer click "Accept all"
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const bridgePath = path.join(process.env.USERPROFILE, '.gemini', 'antigravity', 'bridge');
|
||||||
|
|
||||||
|
// Check if there's a trigger_click.json
|
||||||
|
const triggerFile = path.join(bridgePath, 'trigger_click.json');
|
||||||
|
console.log('Writing trigger_click.json for accept...');
|
||||||
|
fs.writeFileSync(triggerFile, JSON.stringify({ action: 'approve', type: 'diff_review', ts: Date.now() }), 'utf-8');
|
||||||
|
console.log('Done. Check if Accept all was clicked.');
|
||||||
|
|
||||||
|
// Also check extension log for recent entries
|
||||||
|
const logFile = path.join(bridgePath, 'extension.log');
|
||||||
|
const lines = fs.readFileSync(logFile, 'utf-8').split('\n');
|
||||||
|
const recent = lines.slice(-5);
|
||||||
|
recent.forEach(l => console.log(l.substring(0, 200)));
|
||||||
114
extension/scratch/verify_final.js
Normal file
114
extension/scratch/verify_final.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// Final simulation: exact v0.5.96 flow with realistic DOM
|
||||||
|
const {generateApprovalObserverScript} = require('../out/observer-script');
|
||||||
|
let s = generateApprovalObserverScript(18080);
|
||||||
|
try { new Function(s); console.log('SYNTAX: OK'); } catch(e) { console.log('SYNTAX ERROR:', e.message); process.exit(1); }
|
||||||
|
|
||||||
|
let promptRe = /[\u003e\u00bb\u276f]\s+(.+)/;
|
||||||
|
let stripRe = /\s*(content_copy|content_paste|play_arrow|check_circle|keyboard_arrow[_a-z]*)\s*$/;
|
||||||
|
let iconFilterRe = /^(content_copy|content_paste|play_arrow|check_circle|chevron_|keyboard_arrow|more_horiz|more_vert|expand_|alternate_email|arrow_drop)/;
|
||||||
|
|
||||||
|
// Realistic scenario: "Running command" div has siblings including a copy button
|
||||||
|
// The actual DOM probably has a structure like:
|
||||||
|
// div "Running command"
|
||||||
|
// span/div with the copy icon (textContent = "> content_copy" or just "content_copy")
|
||||||
|
// div with the actual prompt+command
|
||||||
|
// div with the buttons
|
||||||
|
|
||||||
|
function v31_simulate(name, siblings) {
|
||||||
|
console.log('\n=== ' + name + ' ===');
|
||||||
|
|
||||||
|
// Step 1: Find "Running command" header
|
||||||
|
let rcIdx = -1;
|
||||||
|
for (let i = 0; i < siblings.length; i++) {
|
||||||
|
let t = siblings[i].trim();
|
||||||
|
if (t === 'Running command' || (t.indexOf('Running command') !== -1 && t.length < 30)) {
|
||||||
|
rcIdx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rcIdx < 0) { console.log(' NO RC HEADER'); return; }
|
||||||
|
|
||||||
|
// Step 2: Collect candidates (filter icons and buttons)
|
||||||
|
let cands = [];
|
||||||
|
for (let i = 0; i < siblings.length; i++) {
|
||||||
|
if (i === rcIdx) continue;
|
||||||
|
let t = siblings[i].trim();
|
||||||
|
if (t.length < 5) continue;
|
||||||
|
if (iconFilterRe.test(t)) { console.log(' FILTER icon: "' + t.substring(0,40) + '"'); continue; }
|
||||||
|
if (/^(Always|Run|Allow|Cancel|Deny|keyboard_arrow)/i.test(t)) { console.log(' FILTER btn: "' + t.substring(0,40) + '"'); continue; }
|
||||||
|
if (t.indexOf('Always run') !== -1 && t.indexOf('Cancel') !== -1) { console.log(' FILTER btn-bar: "' + t.substring(0,40) + '"'); continue; }
|
||||||
|
cands.push(t);
|
||||||
|
console.log(' CANDIDATE: "' + t.substring(0,60) + '" (len=' + t.length + ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Sort by length (longest first)
|
||||||
|
cands.sort((a,b) => b.length - a.length);
|
||||||
|
|
||||||
|
// Step 4: Extract command from best candidate
|
||||||
|
for (let cand of cands) {
|
||||||
|
let m = promptRe.exec(cand);
|
||||||
|
if (m && m[1].trim().length > 3) {
|
||||||
|
let cmdV = m[1].trim().replace(stripRe, '').trim();
|
||||||
|
if (cmdV.length < 3) { console.log(' SKIP (too short after strip): "' + cmdV + '"'); continue; }
|
||||||
|
if (/^(Always|Run|Allow|Cancel|Deny)/i.test(cmdV)) continue;
|
||||||
|
console.log(' EXTRACTED: "' + cmdV + '"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cand.length > 10 && /[\u276f\u003e]/.test(cand)) {
|
||||||
|
let raw = cand.replace(stripRe, '').trim();
|
||||||
|
console.log(' EXTRACTED (raw): "' + raw + '"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(' NO MATCH - will fallback to "Always run"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scenario A: What ACTUALLY happened (3 siblings, "content_copy" mixed in command text)
|
||||||
|
v31_simulate('A: Icon in command text', [
|
||||||
|
'Running command',
|
||||||
|
'\u276f gravity_control > Start-Sleep 12; $logFile content_copy',
|
||||||
|
'Always run keyboard_arrow_up Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Scenario B: Copy button as separate small div
|
||||||
|
v31_simulate('B: Icon as separate div + command div', [
|
||||||
|
'Running command',
|
||||||
|
'> content_copy',
|
||||||
|
'\u276f gravity_control > npm run compile',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Scenario C: Just "content_copy" standalone (no >)
|
||||||
|
v31_simulate('C: Standalone icon + command', [
|
||||||
|
'Running command',
|
||||||
|
'content_copy',
|
||||||
|
'\u276f gravity_control > git push origin main',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Scenario D: Multiple icons mixed
|
||||||
|
v31_simulate('D: Multiple icons + command', [
|
||||||
|
'Running command',
|
||||||
|
'play_arrow',
|
||||||
|
'> content_copy',
|
||||||
|
'\u276f gravity_control > node -e "console.log(1)" content_copy',
|
||||||
|
'Always run keyboard_arrow_up Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Scenario E: Edge - no command, only prompt
|
||||||
|
v31_simulate('E: Prompt only', [
|
||||||
|
'Running command',
|
||||||
|
'\u276f gravity_control > ',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Scenario F: The v0.5.95 cmdV=content_copy case
|
||||||
|
// This implies regex matched "content_copy" from a "> content_copy" sibling
|
||||||
|
// and there was no longer sibling
|
||||||
|
v31_simulate('F: Only icon sibling (worst case)', [
|
||||||
|
'Running command',
|
||||||
|
'> content_copy',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('\n=== SIMULATION COMPLETE ===');
|
||||||
43
extension/scratch/verify_junk.js
Normal file
43
extension/scratch/verify_junk.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
const {generateApprovalObserverScript} = require('../out/observer-script');
|
||||||
|
let s = generateApprovalObserverScript(18080);
|
||||||
|
|
||||||
|
// Extract the regex strings
|
||||||
|
let junkMatch = s.match(/JUNK_CODE_RE\s*=\s*(\/[^;]+)/);
|
||||||
|
let promptMatch = s.match(/PROMPT_ONLY_RE\s*=\s*(\/[^;]+)/);
|
||||||
|
|
||||||
|
console.log('JUNK_CODE_RE:', junkMatch[1].substring(0, 100));
|
||||||
|
console.log('PROMPT_ONLY_RE:', promptMatch[1]);
|
||||||
|
|
||||||
|
// Use eval to construct the actual regexes
|
||||||
|
let JUNK = eval(junkMatch[1]);
|
||||||
|
let PROMPT = eval(promptMatch[1]);
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
['\u276f gravity_control > ', 'prompt only (no command)'],
|
||||||
|
['\u276f extension > ', 'prompt only (extension)'],
|
||||||
|
['\u276f gravity_control > $logFile = Join-Path $env:USERPROFILE', 'PS var assignment'],
|
||||||
|
['\u276f extension > npm.cmd run compile', 'npm compile'],
|
||||||
|
['\u276f gravity_control > Start-Sleep 12', 'Start-Sleep'],
|
||||||
|
['\u276f gravity_control > git add -A; git commit -m "test"', 'git commit'],
|
||||||
|
['\u276f gravity_control > node -e "const {gen}=require()"', 'node with require'],
|
||||||
|
['\u276f gravity_control > Get-Content file.txt', 'Get-Content'],
|
||||||
|
['\u276f gravity_control > npm.cmd version patch', 'npm version'],
|
||||||
|
['function test() { return 1; }', 'JS function (should be JUNK)'],
|
||||||
|
['const x = require("fs")', 'JS const (should be JUNK)'],
|
||||||
|
['import { foo } from "bar"', 'JS import (should be JUNK)'],
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log('\n=== CODE ELEMENT FILTER ANALYSIS ===');
|
||||||
|
for (let [text, desc] of tests) {
|
||||||
|
let isJunk = JUNK.test(text);
|
||||||
|
let isPrompt = PROMPT.test(text.trim());
|
||||||
|
let junkPart = isJunk ? text.match(JUNK)[0] : null;
|
||||||
|
|
||||||
|
let status;
|
||||||
|
if (isPrompt) status = 'SKIP-PROMPT';
|
||||||
|
else if (isJunk) status = 'SKIP-JUNK (' + junkPart + ')';
|
||||||
|
else status = 'PASS';
|
||||||
|
|
||||||
|
let isBug = (isJunk || isPrompt) && text.indexOf('\u276f') !== -1 && text.trim().length > 25;
|
||||||
|
console.log((isBug ? 'BUG ' : ' ') + status.padEnd(40) + ' | ' + desc);
|
||||||
|
}
|
||||||
42
extension/scratch/verify_regex.js
Normal file
42
extension/scratch/verify_regex.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const {generateApprovalObserverScript} = require('../out/observer-script');
|
||||||
|
let s = generateApprovalObserverScript(18080);
|
||||||
|
|
||||||
|
// Find the regex used in v30 candidate matching
|
||||||
|
let idx = s.indexOf('candT.match(');
|
||||||
|
if (idx < 0) idx = s.indexOf('sibT.match(');
|
||||||
|
let reStr = s.substring(idx, s.indexOf(');', idx) + 1);
|
||||||
|
console.log('Match code:', reStr.substring(0, 60));
|
||||||
|
|
||||||
|
// Extract just the regex part
|
||||||
|
let reMatch = reStr.match(/\/(.*?)\//);
|
||||||
|
let reSource = reMatch ? reMatch[0] : 'NOT FOUND';
|
||||||
|
console.log('Regex source:', reSource);
|
||||||
|
|
||||||
|
// Build and test the actual regex
|
||||||
|
let re = new RegExp(reMatch[1]);
|
||||||
|
console.log('Regex object:', re);
|
||||||
|
|
||||||
|
// Test with the EXACT patterns from logs
|
||||||
|
let tests = [
|
||||||
|
['Normal', '\u276f gravity_control > Start-Sleep 12 content_copy'],
|
||||||
|
['Git cmd', '\u276f gravity_control > git add -A; git commit -m "test"'],
|
||||||
|
['Short', '> content_copy'],
|
||||||
|
['Prompt only', '\u276f gravity_control > '],
|
||||||
|
['Dir cmd', '\u276f gravity_control > dir content_copy'],
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log('\n=== REGEX TESTS ===');
|
||||||
|
let stripRe = /\s*(content_copy|content_paste|play_arrow|check_circle|keyboard_arrow[_a-z]*)\s*$/;
|
||||||
|
for (let [name, text] of tests) {
|
||||||
|
let m = re.exec(text);
|
||||||
|
if (m) {
|
||||||
|
let raw = m[1].trim();
|
||||||
|
let cleaned = raw.replace(stripRe, '').trim();
|
||||||
|
console.log(name + ':');
|
||||||
|
console.log(' raw match[1]: "' + raw + '"');
|
||||||
|
console.log(' after strip: "' + cleaned + '"');
|
||||||
|
console.log(' length ok: ' + (cleaned.length >= 3));
|
||||||
|
} else {
|
||||||
|
console.log(name + ': NO MATCH');
|
||||||
|
}
|
||||||
|
}
|
||||||
122
extension/scratch/verify_v096.js
Normal file
122
extension/scratch/verify_v096.js
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
const {generateApprovalObserverScript} = require('../out/observer-script');
|
||||||
|
let s = generateApprovalObserverScript(18080);
|
||||||
|
|
||||||
|
// 1. SYNTAX CHECK
|
||||||
|
try { new Function(s); console.log('[1] SYNTAX: OK'); } catch(e) { console.log('[1] SYNTAX ERROR:', e.message); process.exit(1); }
|
||||||
|
|
||||||
|
// 2. v30 block exists
|
||||||
|
let v30Start = s.indexOf('// v30:');
|
||||||
|
let v30End = s.indexOf('// v23:', v30Start);
|
||||||
|
console.log('[2] v30 block:', v30Start > 0 && v30End > v30Start ? 'OK' : 'MISSING');
|
||||||
|
|
||||||
|
// 3. Key features present
|
||||||
|
console.log('[3] rcCands:', s.indexOf('rcCands') > 0 ? 'OK' : 'MISSING');
|
||||||
|
console.log('[4] content_copy filter:', s.indexOf('content_copy|content_paste') > 0 ? 'OK' : 'MISSING');
|
||||||
|
console.log('[5] sort by length:', s.indexOf('.sort(') > 0 ? 'OK' : 'MISSING');
|
||||||
|
console.log('[6] icon strip replace:', (s.match(/content_copy/g)||[]).length >= 2 ? 'OK (filter+strip)' : 'CHECK');
|
||||||
|
|
||||||
|
// 4. Simulate exact DOM from BTN-DOM-DUMP + CONTEXT logs
|
||||||
|
let promptRe = /[\u003e\u00bb\u276f]\s+(.+)/;
|
||||||
|
let stripRe = /\s*(content_copy|content_paste|play_arrow|check_circle|keyboard_arrow[_a-z]*)\s*$/;
|
||||||
|
let iconFilterRe = /^(content_copy|content_paste|play_arrow|check_circle|chevron_|keyboard_arrow|more_horiz|more_vert|expand_|alternate_email|arrow_drop)/;
|
||||||
|
let btnFilterRe = /^(Always|Run|Allow|Cancel|Deny|keyboard_arrow)/i;
|
||||||
|
|
||||||
|
function simulate(name, siblings) {
|
||||||
|
console.log('\n=== ' + name + ' ===');
|
||||||
|
let rcFound = false;
|
||||||
|
for (let sib of siblings) {
|
||||||
|
if (sib === 'Running command' || (sib.indexOf('Running command') !== -1 && sib.length < 30)) {
|
||||||
|
rcFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rcFound) { console.log(' RC header NOT FOUND'); return null; }
|
||||||
|
|
||||||
|
let cands = [];
|
||||||
|
for (let sib of siblings) {
|
||||||
|
if (sib === 'Running command') continue;
|
||||||
|
if (sib.length < 5) continue;
|
||||||
|
if (iconFilterRe.test(sib)) { console.log(' SKIP icon: "' + sib.substring(0,30) + '"'); continue; }
|
||||||
|
if (btnFilterRe.test(sib)) { console.log(' SKIP btn: "' + sib.substring(0,30) + '"'); continue; }
|
||||||
|
if (sib.indexOf('Always run') !== -1 && sib.indexOf('Cancel') !== -1) { console.log(' SKIP btn-bar: "' + sib.substring(0,30) + '"'); continue; }
|
||||||
|
cands.push(sib);
|
||||||
|
}
|
||||||
|
cands.sort((a,b) => b.length - a.length);
|
||||||
|
console.log(' Candidates: ' + cands.length);
|
||||||
|
for (let i = 0; i < cands.length; i++) {
|
||||||
|
console.log(' [' + i + '] len=' + cands[i].length + ': "' + cands[i].substring(0,80) + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let cand of cands) {
|
||||||
|
let m = promptRe.exec(cand);
|
||||||
|
if (m && m[1].trim().length > 3) {
|
||||||
|
let cmdV = m[1].trim().replace(stripRe, '').trim();
|
||||||
|
if (cmdV.length < 3) continue;
|
||||||
|
console.log(' RESULT: "' + cmdV + '"');
|
||||||
|
return cmdV;
|
||||||
|
}
|
||||||
|
if (cand.length > 10 && /[\u276f\u003e]/.test(cand)) {
|
||||||
|
let raw = cand.replace(stripRe, '').trim();
|
||||||
|
console.log(' RESULT (raw): "' + raw + '"');
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(' RESULT: NO MATCH');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 1: From BTN-DOM-DUMP (3 siblings, command + content_copy icon)
|
||||||
|
simulate('Case1: Normal command with icon', [
|
||||||
|
'Running command',
|
||||||
|
'\u276f gravity_control > Start-Sleep 12; $logFile content_copy',
|
||||||
|
'Always run keyboard_arrow_up Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Case 2: content_copy as standalone sibling
|
||||||
|
simulate('Case2: Icon as separate div', [
|
||||||
|
'Running command',
|
||||||
|
'content_copy',
|
||||||
|
'\u276f gravity_control > npm run compile',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Case 3: No icon appended
|
||||||
|
simulate('Case3: Clean command', [
|
||||||
|
'Running command',
|
||||||
|
'\u276f gravity_control > git add -A; git commit -m "test"',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Case 4: Very long command
|
||||||
|
simulate('Case4: Long command', [
|
||||||
|
'Running command',
|
||||||
|
'\u276f gravity_control > Select-String -Path "$env:USERPROFILE\\.gemini\\antigravity\\bridge\\extension.log" -Pattern "CONTEXT" content_copy',
|
||||||
|
'Always run keyboard_arrow_up Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Case 5: Prompt only (no command yet)
|
||||||
|
simulate('Case5: Prompt only', [
|
||||||
|
'Running command',
|
||||||
|
'\u276f gravity_control > ',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Case 6: Multiple icon texts
|
||||||
|
simulate('Case6: Multiple icons', [
|
||||||
|
'Running command',
|
||||||
|
'play_arrow',
|
||||||
|
'content_copy',
|
||||||
|
'\u276f gravity_control > dir content_copy',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Case 7: Observed log pattern - "content_copy" was cmdV
|
||||||
|
// This means the regex matched on just "content_copy" with a > before it
|
||||||
|
// Possible: the sibling text is "> content_copy" (very short prompt)
|
||||||
|
simulate('Case7: Short prompt with icon only', [
|
||||||
|
'Running command',
|
||||||
|
'> content_copy',
|
||||||
|
'Always run Cancel'
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('\n=== ALL TESTS COMPLETE ===');
|
||||||
52
extension/scratch/verify_v32.js
Normal file
52
extension/scratch/verify_v32.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
const {generateApprovalObserverScript} = require('../out/observer-script');
|
||||||
|
let s = generateApprovalObserverScript(18080);
|
||||||
|
|
||||||
|
// Extract the terminal prompt regex from generated code
|
||||||
|
let idx = s.indexOf('_termPromptMatch');
|
||||||
|
let reCtx = s.substring(idx, s.indexOf(');', idx) + 1);
|
||||||
|
console.log('v32 code:', reCtx.substring(0, 80));
|
||||||
|
|
||||||
|
// Extract regex
|
||||||
|
let reMatch = reCtx.match(/\/(.+?)\//);
|
||||||
|
let termRe = new RegExp(reMatch[1]);
|
||||||
|
console.log('v32 regex:', termRe);
|
||||||
|
|
||||||
|
let stripRe = /\s*(content_copy|content_paste|play_arrow)\s*$/;
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
// Should MATCH (terminal commands)
|
||||||
|
['\u276f gravity_control > Start-Sleep 12', true, 'Start-Sleep'],
|
||||||
|
['\u276f gravity_control > npm.cmd run compile', true, 'npm compile'],
|
||||||
|
['\u276f gravity_control > $logFile = Join-Path $env:USERPROFILE', true, 'PS variable (had JUNK match)'],
|
||||||
|
['\u276f gravity_control > git add -A; git commit -m "test"', true, 'git commit'],
|
||||||
|
['\u276f gravity_control > node -e "const {gen}=require(\'./out\')"', true, 'node with const (was JUNK)'],
|
||||||
|
['\u276f extension > npm.cmd run compile', true, 'extension npm'],
|
||||||
|
['\u276f gravity_control > Start-Sleep 12 content_copy', true, 'with icon (strip)'],
|
||||||
|
['\u276f gravity_control > Get-Content f.txt | Select-Object -Last 5', true, 'Get-Content'],
|
||||||
|
// Should NOT match (prompt only, no command)
|
||||||
|
['\u276f gravity_control > ', false, 'prompt only'],
|
||||||
|
['\u276f extension > ', false, 'prompt only ext'],
|
||||||
|
// Should NOT match (not terminal - JS code)
|
||||||
|
['function test() { return 1; }', false, 'JS function'],
|
||||||
|
['const x = require("fs")', false, 'JS const'],
|
||||||
|
['import { foo } from "bar"', false, 'JS import'],
|
||||||
|
// Should NOT match (no prompt marker)
|
||||||
|
['gravity_control > dir', false, 'no ❯ marker'],
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log('\n=== v32 TERMINAL PROMPT REGEX TESTS ===');
|
||||||
|
let pass = 0, fail = 0;
|
||||||
|
for (let [text, shouldMatch, desc] of tests) {
|
||||||
|
let m = termRe.exec(text);
|
||||||
|
let matched = false;
|
||||||
|
let cmdV = null;
|
||||||
|
if (m && m[1] && m[1].trim().length > 2) {
|
||||||
|
cmdV = m[1].trim().replace(stripRe, '').trim();
|
||||||
|
matched = cmdV.length > 2;
|
||||||
|
}
|
||||||
|
let ok = matched === shouldMatch;
|
||||||
|
if (ok) pass++; else fail++;
|
||||||
|
console.log((ok ? 'PASS' : 'FAIL') + ' | ' + (matched ? 'MATCH' : 'SKIP ').padEnd(5) + ' | ' + desc);
|
||||||
|
if (matched && cmdV) console.log(' cmd: "' + cmdV + '"');
|
||||||
|
}
|
||||||
|
console.log('\nResult: ' + pass + '/' + (pass+fail) + ' passed' + (fail > 0 ? ' (' + fail + ' FAILED!)' : ' ALL OK'));
|
||||||
@@ -285,11 +285,13 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
|
|
||||||
// v19: "Always run" auto-approve — MUST run BEFORE any filter can reject it
|
// v19: "Always run" auto-approve — MUST run BEFORE any filter can reject it
|
||||||
// Detects from rawCmd OR from buttons array (Observer may detect sibling first)
|
// Detects from rawCmd OR from buttons array (Observer may detect sibling first)
|
||||||
let alwaysRunDetected = /^Always\s+run$/i.test(rawCmd);
|
// v33: Also auto-approve "Accept all" (diff review) and "Accept" buttons
|
||||||
|
const AUTO_APPROVE_RE = /^(Always\s+run|Accept\s+all|Accept)$/i;
|
||||||
|
let alwaysRunDetected = AUTO_APPROVE_RE.test(rawCmd);
|
||||||
let alwaysRunBtnIndex = alwaysRunDetected ? 0 : -1;
|
let alwaysRunBtnIndex = alwaysRunDetected ? 0 : -1;
|
||||||
if (!alwaysRunDetected && Array.isArray(data.buttons)) {
|
if (!alwaysRunDetected && Array.isArray(data.buttons)) {
|
||||||
for (let bi = 0; bi < data.buttons.length; bi++) {
|
for (let bi = 0; bi < data.buttons.length; bi++) {
|
||||||
if (/^Always\s+run$/i.test((data.buttons[bi].text || '').trim())) {
|
if (AUTO_APPROVE_RE.test((data.buttons[bi].text || '').trim())) {
|
||||||
alwaysRunDetected = true;
|
alwaysRunDetected = true;
|
||||||
alwaysRunBtnIndex = bi;
|
alwaysRunBtnIndex = bi;
|
||||||
break;
|
break;
|
||||||
@@ -297,6 +299,27 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (alwaysRunDetected) {
|
if (alwaysRunDetected) {
|
||||||
|
// v34: If this is "Accept all" / "Accept", also call agentAcceptAllInFile directly
|
||||||
|
const isAcceptAll = /^Accept/i.test(rawCmd) || (data.step_type === 'diff_review');
|
||||||
|
if (isAcceptAll) {
|
||||||
|
ctx.logToFile(`[HTTP] AUTO-APPROVE Accept all → opening review + agentAcceptAllInFile`);
|
||||||
|
try {
|
||||||
|
const vscode = require('vscode');
|
||||||
|
// v37: Must focus diff review panel BEFORE calling agentAcceptAllInFile
|
||||||
|
// Without this, the command succeeds but has no effect
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await vscode.commands.executeCommand('antigravity.openReviewChanges');
|
||||||
|
ctx.logToFile(`[HTTP] openReviewChanges OK`);
|
||||||
|
await new Promise((r: any) => setTimeout(r, 500));
|
||||||
|
await vscode.commands.executeCommand('antigravity.prioritized.agentAcceptAllInFile');
|
||||||
|
ctx.logToFile(`[HTTP] ✅ agentAcceptAllInFile SUCCESS`);
|
||||||
|
} catch (e: any) {
|
||||||
|
ctx.logToFile(`[HTTP] ❌ agentAcceptAllInFile: ${e.message?.substring(0, 100)}`);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e: any) { ctx.logToFile(`[HTTP] ❌ vscode require failed: ${e.message}`); }
|
||||||
|
}
|
||||||
// Try enrichment for better Discord display text
|
// Try enrichment for better Discord display text
|
||||||
let displayCmd = rawCmd;
|
let displayCmd = rawCmd;
|
||||||
ctx.logToFile(`[HTTP] AUTO-APPROVE raw: cmd="${rawCmd}" desc="${rawDesc.substring(0, 120)}" buttons=${JSON.stringify((data.buttons || []).map((b: any) => b.text)).substring(0, 200)}`);
|
ctx.logToFile(`[HTTP] AUTO-APPROVE raw: cmd="${rawCmd}" desc="${rawDesc.substring(0, 120)}" buttons=${JSON.stringify((data.buttons || []).map((b: any) => b.text)).substring(0, 200)}`);
|
||||||
@@ -356,6 +379,8 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
button_index: alwaysRunBtnIndex >= 0 ? alwaysRunBtnIndex : 0,
|
button_index: alwaysRunBtnIndex >= 0 ? alwaysRunBtnIndex : 0,
|
||||||
step_type: data.step_type || 'command',
|
step_type: data.step_type || 'command',
|
||||||
project_name: ctx.projectName,
|
project_name: ctx.projectName,
|
||||||
|
_from_ws: true, // v38: prevent processResponseFile from consuming before Observer polls
|
||||||
|
_auto_approve_ttl: Date.now() + 60_000, // auto-expire after 60s
|
||||||
};
|
};
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(responseDir, `${rid}.json`),
|
path.join(responseDir, `${rid}.json`),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
function log(m){
|
function log(m){
|
||||||
console.log('[GB Observer] '+m);
|
console.log('[GB Observer] '+m);
|
||||||
// v19: Relay important logs to extension via HTTP so they appear in extension.log
|
// v19: Relay important logs to extension via HTTP so they appear in extension.log
|
||||||
if (BASE && (m.indexOf('CV-CLASSES')!==-1 || m.indexOf('CV-CHILDREN')!==-1 || m.indexOf('child[')!==-1 || m.indexOf('CV found')!==-1 || m.indexOf('Conversation view')!==-1 || m.indexOf('BEACON')!==-1 || m.indexOf('ERROR')!==-1 || m.indexOf('chat relay')!==-1 || m.indexOf('user-cls')!==-1 || m.indexOf('CONTEXT')!==-1 || m.indexOf('BTN-DOM')!==-1 || m.indexOf('DEFERRED')!==-1 || m.indexOf('DETECTED')!==-1)) {
|
if (BASE && (m.indexOf('CV-CLASSES')!==-1 || m.indexOf('CV-CHILDREN')!==-1 || m.indexOf('child[')!==-1 || m.indexOf('CV found')!==-1 || m.indexOf('Conversation view')!==-1 || m.indexOf('BEACON')!==-1 || m.indexOf('ERROR')!==-1 || m.indexOf('chat relay')!==-1 || m.indexOf('user-cls')!==-1 || m.indexOf('CONTEXT')!==-1 || m.indexOf('BTN-DOM')!==-1 || m.indexOf('DEFERRED')!==-1 || m.indexOf('DETECTED')!==-1 || m.indexOf('ACCEPT')!==-1)) {
|
||||||
try { fetch(BASE+'/log', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({msg:m.substring(0,2000)})}); } catch(e){}
|
try { fetch(BASE+'/log', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({msg:m.substring(0,2000)})}); } catch(e){}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +155,20 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
if (!codeText || codeText.length <= 5) continue;
|
if (!codeText || codeText.length <= 5) continue;
|
||||||
if (/^Running\\s*\\d/i.test(codeText)) continue;
|
if (/^Running\\s*\\d/i.test(codeText)) continue;
|
||||||
_sawCodeEls = true;
|
_sawCodeEls = true;
|
||||||
|
// v32: Terminal prompt detection — extract command BEFORE JUNK/PROMPT filters
|
||||||
|
// PS/bash commands can contain JS keywords (return, function, const) → false JUNK matches
|
||||||
|
var _termPromptMatch = codeText.match(/^[\\u276f\\u00bb]\\s+[^\\n]*[\\u003e]\\s+(.+)/);
|
||||||
|
if (_termPromptMatch && _termPromptMatch[1].trim().length > 2) {
|
||||||
|
var _termCmd = _termPromptMatch[1].trim();
|
||||||
|
_termCmd = _termCmd.replace(/\\s*(content_copy|content_paste|play_arrow)\\s*$/, '').trim();
|
||||||
|
if (_termCmd.length > 2) {
|
||||||
|
_allSkipped = false;
|
||||||
|
_bestCodeText = 'Running command: ' + _termCmd;
|
||||||
|
log('CONTEXT-OK d='+depth+' src=terminal-prompt cmd='+_termCmd.substring(0,80));
|
||||||
|
_lastContextDebug = _debugTrail.join(' ');
|
||||||
|
return _bestCodeText;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (PROMPT_ONLY_RE.test(codeText.trim())) {
|
if (PROMPT_ONLY_RE.test(codeText.trim())) {
|
||||||
_debugTrail.push('skip_prompt_ci='+ci+':'+codeText.substring(0,30));
|
_debugTrail.push('skip_prompt_ci='+ci+':'+codeText.substring(0,30));
|
||||||
continue;
|
continue;
|
||||||
@@ -191,20 +205,51 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
}
|
}
|
||||||
// v30: Command text is in plain divs near "Running command" header, not pre/code
|
// v30: Command text is in plain divs near "Running command" header, not pre/code
|
||||||
var rcDivs = node.querySelectorAll('div');
|
var rcDivs = node.querySelectorAll('div');
|
||||||
|
// v30 diagnostic: log what we find at each depth where code was skipped
|
||||||
|
if (_sawCodeEls && rcDivs.length > 0 && depth <= 5) {
|
||||||
|
var rcSample = [];
|
||||||
|
for (var rdi = 0; rdi < Math.min(rcDivs.length, 8); rdi++) {
|
||||||
|
var rdt = (rcDivs[rdi].textContent || '').trim().substring(0,40);
|
||||||
|
var rdc = rcDivs[rdi].children ? rcDivs[rdi].children.length : 0;
|
||||||
|
rcSample.push('ch'+rdc+':"'+rdt+'"');
|
||||||
|
}
|
||||||
|
log('CONTEXT-v30-SCAN d='+depth+' divs='+rcDivs.length+' ['+rcSample.join(', ')+']');
|
||||||
|
}
|
||||||
for (var rci = 0; rci < rcDivs.length; rci++) {
|
for (var rci = 0; rci < rcDivs.length; rci++) {
|
||||||
var rcTxt = (rcDivs[rci].textContent || '').trim();
|
var rcEl = rcDivs[rci];
|
||||||
if (rcTxt === 'Running command' && rcDivs[rci].parentElement) {
|
var rcChildCount = rcEl.children ? rcEl.children.length : 0;
|
||||||
var rcP = rcDivs[rci].parentElement;
|
var rcTxt = (rcEl.textContent || '').trim();
|
||||||
|
if ((rcTxt === 'Running command' || (rcChildCount === 0 && rcTxt.indexOf('Running command') !== -1 && rcTxt.length < 30)) && rcEl.parentElement) {
|
||||||
|
var rcP = rcEl.parentElement;
|
||||||
|
var rcCands = [];
|
||||||
for (var rcsi = 0; rcsi < rcP.children.length; rcsi++) {
|
for (var rcsi = 0; rcsi < rcP.children.length; rcsi++) {
|
||||||
if (rcP.children[rcsi] === rcDivs[rci]) continue;
|
if (rcP.children[rcsi] === rcEl) continue;
|
||||||
var sibT = (rcP.children[rcsi].textContent || '').trim();
|
var sibT = (rcP.children[rcsi].textContent || '').trim();
|
||||||
var pM = sibT.match(/[\\u003e\\u00bb\\u276f]\\s+(.+)/);
|
if (sibT.length < 5) continue;
|
||||||
|
if (/^(content_copy|content_paste|play_arrow|check_circle|chevron_|keyboard_arrow|more_horiz|more_vert|expand_|alternate_email|arrow_drop)/.test(sibT)) continue;
|
||||||
|
if (/^(Always|Run|Allow|Cancel|Deny|keyboard_arrow)/i.test(sibT)) continue;
|
||||||
|
if (sibT.indexOf('Always run') !== -1 && sibT.indexOf('Cancel') !== -1) continue;
|
||||||
|
rcCands.push(sibT);
|
||||||
|
}
|
||||||
|
rcCands.sort(function(a,b){ return b.length - a.length; });
|
||||||
|
log('CONTEXT-v30 RC d='+depth+' cands='+rcCands.length+(rcCands.length>0?' best="'+rcCands[0].substring(0,60)+'"':''));
|
||||||
|
for (var rcci = 0; rcci < rcCands.length; rcci++) {
|
||||||
|
var candT = rcCands[rcci];
|
||||||
|
var pM = candT.match(/[\\u003e\\u00bb\\u276f]\\s+(.+)/);
|
||||||
if (pM && pM[1].trim().length > 3) {
|
if (pM && pM[1].trim().length > 3) {
|
||||||
var cmdV = pM[1].trim().substring(0, 300);
|
var cmdV = pM[1].trim();
|
||||||
|
cmdV = cmdV.replace(/\\s*(content_copy|content_paste|play_arrow|check_circle|keyboard_arrow[_a-z]*)\\s*$/, '').trim();
|
||||||
|
if (cmdV.length < 3) continue;
|
||||||
if (/^(Always|Run|Allow|Cancel|Deny)/i.test(cmdV)) continue;
|
if (/^(Always|Run|Allow|Cancel|Deny)/i.test(cmdV)) continue;
|
||||||
log('CONTEXT-OK d='+depth+' src=running-cmd trail='+_debugTrail.join(' '));
|
log('CONTEXT-OK d='+depth+' src=running-cmd cmdV='+cmdV.substring(0,80));
|
||||||
_lastContextDebug = _debugTrail.join(' ');
|
_lastContextDebug = _debugTrail.join(' ');
|
||||||
return 'Running command: ' + cmdV;
|
return 'Running command: ' + cmdV.substring(0, 300);
|
||||||
|
}
|
||||||
|
if (candT.length > 10 && /[\\u276f\\u003e]/.test(candT)) {
|
||||||
|
var rawC = candT.replace(/\\s*(content_copy|content_paste|play_arrow)\\s*$/, '').trim();
|
||||||
|
log('CONTEXT-OK d='+depth+' src=running-cmd-raw rawC='+rawC.substring(0,80));
|
||||||
|
_lastContextDebug = _debugTrail.join(' ');
|
||||||
|
return rawC.substring(0, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1035,7 +1080,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
if(!_ready)return;
|
if(!_ready)return;
|
||||||
scanChatBodies();
|
scanChatBodies();
|
||||||
var now=Date.now();
|
var now=Date.now();
|
||||||
var allBtns=document.querySelectorAll('button, [role="button"], a.monaco-button, .monaco-text-button, vscode-button');
|
var allBtns=document.querySelectorAll('button, [role="button"], a.monaco-button, .monaco-text-button, vscode-button, span.cursor-pointer');
|
||||||
if(!allBtns.length)return;
|
if(!allBtns.length)return;
|
||||||
|
|
||||||
// v25: One-shot debug ??find Accept/Reject elements in ANY tag (run once per 30s)
|
// v25: One-shot debug ??find Accept/Reject elements in ANY tag (run once per 30s)
|
||||||
|
|||||||
@@ -808,6 +808,19 @@ function setupMonitor() {
|
|||||||
source: 'step_probe_offset',
|
source: 'step_probe_offset',
|
||||||
safe_to_auto_run: isSafeToAutoRun,
|
safe_to_auto_run: isSafeToAutoRun,
|
||||||
});
|
});
|
||||||
|
// v35: Auto-accept code edits (offset path)
|
||||||
|
if (['write_to_file', 'replace_file_content', 'multi_replace_file_content'].includes(toolName)) {
|
||||||
|
ctx.logToFile(`[STEP-PROBE] v35: code_edit (offset) → auto-accepting in 500ms`);
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const vscode = require('vscode');
|
||||||
|
await vscode.commands.executeCommand('antigravity.prioritized.agentAcceptAllInFile');
|
||||||
|
ctx.logToFile(`[STEP-PROBE] ✅ agentAcceptAllInFile (offset) SUCCESS`);
|
||||||
|
} catch (e: any) {
|
||||||
|
ctx.logToFile(`[STEP-PROBE] ❌ agentAcceptAllInFile (offset): ${e.message?.substring(0, 100)}`);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NOTE: no break — process ALL parallel WAITING steps
|
// NOTE: no break — process ALL parallel WAITING steps
|
||||||
@@ -861,6 +874,20 @@ function setupMonitor() {
|
|||||||
source: 'step_probe',
|
source: 'step_probe',
|
||||||
safe_to_auto_run: isSafeToAutoRun,
|
safe_to_auto_run: isSafeToAutoRun,
|
||||||
});
|
});
|
||||||
|
// v35: Auto-accept code edits via agentAcceptAllInFile
|
||||||
|
// Observer can't see "Accept all" button (different DOM layer)
|
||||||
|
if (['write_to_file', 'replace_file_content', 'multi_replace_file_content'].includes(toolName)) {
|
||||||
|
ctx.logToFile(`[STEP-PROBE] v35: code_edit detected → auto-accepting in 500ms`);
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const vscode = require('vscode');
|
||||||
|
await vscode.commands.executeCommand('antigravity.prioritized.agentAcceptAllInFile');
|
||||||
|
ctx.logToFile(`[STEP-PROBE] ✅ agentAcceptAllInFile SUCCESS`);
|
||||||
|
} catch (e: any) {
|
||||||
|
ctx.logToFile(`[STEP-PROBE] ❌ agentAcceptAllInFile: ${e.message?.substring(0, 100)}`);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NOTE: no break — process ALL parallel WAITING steps
|
// NOTE: no break — process ALL parallel WAITING steps
|
||||||
|
|||||||
Reference in New Issue
Block a user