|
|
|
|
@@ -4,12 +4,14 @@
|
|
|
|
|
> 디버깅이나 구현 전에 **반드시** 이 파일을 확인하세요.
|
|
|
|
|
> 세션 종료 시 새로 발견된 이슈를 이 파일에 추가합니다.
|
|
|
|
|
|
|
|
|
|
> [!TIP]
|
|
|
|
|
> 해결 완료된 과거 이슈는 [`known-issues-archive.md`](file:///c:/Users/Variet-Worker/Desktop/gravity_control/.agents/references/known-issues-archive.md)에 보관되어 있습니다.
|
|
|
|
|
> 비슷한 문제가 재발하면 archive에서 검색하세요.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 포맷
|
|
|
|
|
|
|
|
|
|
각 항목은 아래 형식을 따릅니다:
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
### [날짜] [키워드] — 한줄 요약
|
|
|
|
|
- **증상**: 무엇이 잘못되었는가
|
|
|
|
|
@@ -22,24 +24,6 @@
|
|
|
|
|
|
|
|
|
|
## 공통 이슈
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] Hub pending_owners 생명주기 — WS 재연결 시 승인 응답 소실
|
|
|
|
|
- **증상**: Discord에서 승인 클릭해도 AG Extension에서 아무 동작 안함 (이중 메시지 전달 이후 특히 발생)
|
|
|
|
|
- **원인**: `hub.py _disconnect()` 에서 Extension 연결이 끊기면 `pending_owners[rid]`를 **삭제**함. Extension이 재연결하면 새 conn_id 할당되지만, 기존 pending의 owner 정보는 이미 삭제됨. `send_response_to_pending_owner()`가 owner를 찾지 못해 `False` 반환. File bridge fallback은 Docker 컨테이너 볼륨에 파일 작성하므로 Extension 접근 불가.
|
|
|
|
|
- **해결**: (1) `_disconnect`에서 삭제 대신 같은 project 다른 연결로 재할당, (2) `_register_connection`에서 orphaned pending_owners를 새 conn_id로 재할당, (3) `send_response_to_pending_owner`에 같은 project 활성 연결 fallback 추가
|
|
|
|
|
- **주의**: Gateway 모드에서 file bridge fallback은 **완전히 무용** — Docker 볼륨에 파일 작성하지만 Extension은 로컬 bridge/ 감시. **WS 라우팅 실패 시 file bridge에 의존하지 말 것**.
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] diff_review Accept All WS 경로 regression (v0.4.5 fix)
|
|
|
|
|
- **증상**: Discord에서 Accept all 클릭해도 AG에서 반응 없음
|
|
|
|
|
- **원인**: v0.4.0 WS 전환 시 `onResponse` 핸들러에 `diff_review` step_type 분기가 누락됨. `processResponseFile` (파일 경로)에만 diff_review 로직이 있었고, WS `onResponse`는 무조건 `tryApprovalStrategies()`만 호출. `tryApprovalStrategies`는 diff_review를 처리하지 않음
|
|
|
|
|
- **해결**: `step-probe.ts`에 `handleDiffReviewResponse()` export 함수 추출. `extension.ts` `onResponse`에서 `stepType === 'diff_review'`일 때 이 함수 호출
|
|
|
|
|
- **주의**: **WS 경로 추가 시 file-bridge 경로의 모든 분기를 반드시 포팅할 것**. step_type별 분기(diff_review, file_permission 등)가 누락되기 쉬움
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] _auto_approve_via_hub 이중 쓰기 (v0.4.5 fix)
|
|
|
|
|
- **증상**: auto-approve 시 Extension에 응답이 2번 도착
|
|
|
|
|
- **원인**: `bot.py:_auto_approve_via_hub()` L1095-1100에서 Hub WS 전송 후 `return` 없이 file bridge에도 씀
|
|
|
|
|
- **해결**: `if self.hub:` → WS 전송 + `else:` → file bridge. if/else 구조로 변경
|
|
|
|
|
- **주의**: Hub WS와 file bridge는 **항상 상호 배타적**이어야 함. `return`이 아닌 `if/else` 사용 권장
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] PowerShell curl — Invoke-WebRequest 충돌
|
|
|
|
|
- **증상**: `curl` 명령이 예상과 다른 응답 형식을 반환
|
|
|
|
|
- **원인**: PowerShell에서 `curl`은 `Invoke-WebRequest`의 별칭
|
|
|
|
|
@@ -48,690 +32,81 @@
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] PowerShell npm — 실행 정책 오류
|
|
|
|
|
- **증상**: `npm run` 명령이 `실행 정책` 관련 오류로 실패
|
|
|
|
|
- **원인**: PowerShell 스크립트 실행 정책이 제한적으로 설정됨
|
|
|
|
|
- **원인**: PowerShell 스크립트 실행 정책이 제한적
|
|
|
|
|
- **해결**: `cmd /c npm run dev` 형식으로 cmd를 통해 실행
|
|
|
|
|
- **주의**: npm 관련 명령은 항상 `cmd /c` 접두어 사용 권장
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 프로젝트별 이슈
|
|
|
|
|
## 미해결 이슈
|
|
|
|
|
|
|
|
|
|
> 아래에 프로젝트 특화 이슈를 추가하세요.
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Antigravity Renderer Injection — Electron 캐시 차단
|
|
|
|
|
- **증상**: workbench.html, workbench-jetski-agent.html에 `<script>` 태그 추가 후 리로드해도 실행되지 않음
|
|
|
|
|
- **원인**: Electron의 V8 코드 캐시가 수정된 HTML을 무시하고 캐시된 버전을 서빙
|
|
|
|
|
- **해결**: 렌더러 인젝션 방식 **포기**. Extension Host에서 RPC 폴링 방식으로 전환
|
|
|
|
|
- **주의**: Antigravity는 `workbench-jetski-agent.html`을 사용 (Jetski = 내부 코드네임)
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Antigravity 승인 대기 = RUNNING (NOT IDLE)
|
|
|
|
|
- **증상**: IDLE 기반 승인 감지가 실제 승인 대기를 놓침
|
|
|
|
|
- **원인**: 승인 대기 시 세션 상태가 `CASCADE_RUN_STATUS_RUNNING` (IDLE 아님), `IDLE`은 대화 대기(notify_user 후)
|
|
|
|
|
- **해결**: `RUNNING + delta=0` (stall) 기반 감지로 전환. 6 polls (30초) 이상 FROZEN 시 pending 생성
|
|
|
|
|
- **주의**: Thinking/생성 중에도 `RUNNING + delta=0`이 발생 → `lastModifiedTime`으로 구분 시도했으나 불완전
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] ResolveOutstandingSteps RPC — 승인이 아닌 취소!
|
|
|
|
|
- **증상**: Discord 승인 → `ResolveOutstandingSteps` 호출 → step이 취소됨
|
|
|
|
|
- **원인**: `ResolveOutstandingSteps`는 blocking steps를 "resolve" = **REJECT/CANCEL**, approve가 아님
|
|
|
|
|
- **해결**: `ResolveOutstandingSteps` 제거. `HandleCascadeUserInteraction`은 `socket hang up`
|
|
|
|
|
- **주의**: KI에 "more reliable"로 기록되어 있으나 실제 동작은 cancel임. KI 업데이트 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] VS Code Accept Commands — Silent Success 문제
|
|
|
|
|
- **증상**: 4개 accept command 모두 OK(undefined) 반환하나 실제 승인 안 됨
|
|
|
|
|
- **원인**: webview에 활성 포커스가 필요. `panel.focus()`로는 충분하지 않음
|
|
|
|
|
- **해결**: **미해결**. Windows UI Automation 등 OS 레벨 접근 필요
|
|
|
|
|
- **주의**: reject commands는 동작함. accept만 focus 의존성 있음
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Multi-Window 세션 등록 경쟁 조건
|
|
|
|
|
- **증상**: 이 창(gravity_control)의 대화가 `#ag-variet_agent` 채널로 메시지 전달
|
|
|
|
|
- **원인**: `writeRegistration()`이 폴링 루프에서 호출 → 먼저 폴링한 확장이 세션을 자기 프로젝트로 등록
|
|
|
|
|
- **해결**: `writeRegistration`을 폴링에서 제거, `writeChatSnapshot`/`writePendingApproval`에서만 지연 호출
|
|
|
|
|
- **주의**: `GetAllCascadeTrajectories`는 모든 창의 세션을 반환하므로 세션→창 매핑은 불가능. 활동 기반 등록만 신뢰 가능
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] 공유 렌더러 스크립트 파일 덮어쓰기 문제
|
|
|
|
|
- **증상**: DOM Observer 렌더러 스크립트가 잘못된 HTTP bridge 포트에 연결
|
|
|
|
|
- **원인**: 두 확장이 동일한 `ag-sdk-variet-gravity-bridge.js` 파일에 각자 포트를 씀 → 마지막 확장 것만 남음
|
|
|
|
|
- **해결**: `ag-bridge-ports.json`에 모든 확장의 port를 JSON으로 기록, 렌더러가 all ports를 순회하며 ping
|
|
|
|
|
- **주의**: 렌더러 스크립트 파일 경로는 SDK patcher namespace에 의해 고정 — 변경 불가
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] workbench.html vs workbench-jetski-agent.html
|
|
|
|
|
- **증상**: 렌더러에서 `[GB Observer]` 로그가 전혀 안 나옴
|
|
|
|
|
- **원인**: DevTools가 `workbench.html`을 로드 — 스크립트 태그는 `workbench-jetski-agent.html`에만 패치됨
|
|
|
|
|
- **해결**: `workbench.html`에도 스크립트 태그 필요. Antigravity 재설치 후 SDK patcher가 올바르게 패치하도록 함
|
|
|
|
|
- **주의**: SDK patcher는 `both` HTML 파일을 패치하지만, 수동 수정은 Antigravity integrity check에 의해 되돌려질 수 있음
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] product.json 체크섬 불일치 → 렌더러 스크립트 미로딩
|
|
|
|
|
- **증상**: `<script>` 태그가 HTML에 존재하고 .js 파일도 디스크에 있으나, 렌더러 콘솔에 스크립트 로그가 전혀 없음
|
|
|
|
|
- **원인**: Antigravity 재설치 시 `product.json`의 SHA256 체크섬이 원본으로 리셋됨. Extension이 HTML을 패치하지만 `IntegrityManager.suppressCheck()`를 호출하지 않아 체크섬 불일치. `vscode-file://` 프로토콜이 체크섬 불일치 파일을 무시하고 **원본 캐시 HTML**을 서빙
|
|
|
|
|
- **해결**: `product.json`의 `checksums` 항목에서 수정된 파일(workbench.html, workbench-jetski-agent.html)의 SHA256 해시를 실제 파일 기준으로 업데이트. SDK `IntegrityManager.suppressCheck()` 호출 또는 수동 스크립트로 해결
|
|
|
|
|
- **주의**: Extension `setupApprovalObserver()`에 `suppressCheck()` 호출을 영구 추가해야 재설치마다 반복 안 됨. 해시 = `base64(sha256(file)).replace(/=+$/, '')`
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] vscode-file:// 프로토콜 — 커스텀 .js 파일 서빙 불가
|
|
|
|
|
- **증상**: `<script src="./ag-sdk-variet-gravity-bridge.js">` 태그가 HTML에 있으나 `net::ERR_FILE_NOT_FOUND` 발생, GB Observer 로그 전혀 없음
|
|
|
|
|
- **원인**: `vscode-file://` 프로토콜은 원본 배포에 포함된 파일만 서빙. Extension이 디스크에 쓴 커스텀 `.js` 파일은 프로토콜 레벨에서 차단됨
|
|
|
|
|
- **해결**: 외부 `<script src>` 참조 대신 **인라인 `<script>...코드...</script>`** 방식으로 HTML에 직접 삽입
|
|
|
|
|
- **주의**: `ag-bridge-ports.json`도 같은 이유로 XHR 로딩 불가. 모든 렌더러 스크립트/데이터는 HTML 인라인으로 전달해야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Renderer 포트 디스커버리 — ag-bridge-ports.json XHR 실패
|
|
|
|
|
- **증상**: `[GB Observer] Port discovery timeout after 2min` — 렌더러가 bridge 포트를 찾지 못함
|
|
|
|
|
- **원인**: 렌더러 스크립트가 `./ag-bridge-ports.json`을 동기 XHR로 읽으려 하나, `vscode-file://` 프로토콜이 `.json` 파일 서빙 거부
|
|
|
|
|
- **해결**: (1) 프로젝트명 해시 기반 **결정론적 포트** 사용 (`gravity_control→34332`), (2) 스크립트 생성 시 포트를 `HARDCODED_PORT=${port}`로 직접 삽입
|
|
|
|
|
- **주의**: `server.listen(0)` 랜덤 포트 → 매 재시작마다 변경되어 렌더러와 불일치. 결정론적 포트는 `EADDRINUSE` 시 랜덤 폴백 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Extension 컴파일 경로 != 설치 경로
|
|
|
|
|
- **증상**: `npm run compile` 후 Extension 동작이 변하지 않음
|
|
|
|
|
- **원인**: `npm run compile`은 `extension/out/extension.js`에만 빌드. Antigravity는 `~/.antigravity/extensions/variet.gravity-bridge-X.X.X/out/extension.js`에서 로드
|
|
|
|
|
- **해결**: 컴파일 후 반드시 설치 경로로 수동 복사하거나, `vsce package` → VSIX 재설치
|
|
|
|
|
- **주의**: `extension/out/` ≠ 실행 경로. 항상 설치 경로 확인 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Electron 메인 프로세스 체크섬 캐시 — Reload Window 불충분
|
|
|
|
|
- **증상**: product.json 체크섬 업데이트 + HTML 패치 후 `Reload Window` → 패치 미적용
|
|
|
|
|
- **원인**: Electron 메인 프로세스가 시작 시 product.json 체크섬을 메모리에 캐시. `Reload Window`는 렌더러만 재시작하므로 캐시된 구 체크섬 사용
|
|
|
|
|
- **해결**: Antigravity를 **완전 종료 후 재시작** 필요 (File → Exit 후 재실행)
|
|
|
|
|
- **주의**: `Reload Window` ≠ 앱 재시작. 체크섬 변경 시 항상 풀 재시작 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Renderer 동기 XHR — Electron 보안 정책 차단
|
|
|
|
|
- **증상**: `tryPing()` 함수가 동기 `XMLHttpRequest`로 HTTP bridge에 연결 시도 → 타임아웃
|
|
|
|
|
- **원인**: Electron 렌더러 프로세스에서 동기 XHR이 보안 정책에 의해 차단됨
|
|
|
|
|
- **해결**: `fetch()` + `AbortSignal.timeout(2000)` 비동기 방식으로 교체 (`tryPingAsync`)
|
|
|
|
|
- **주의**: `async/await` 사용 불가 (ES5 환경). `.then()` 체이닝으로 구현
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] DOM Observer — Run 버튼 감지 불가 (webview iframe 격리)
|
|
|
|
|
- **증상**: Allow Once/Allow This Conversation는 감지되나 Run/Accept 버튼은 감지 안 됨
|
|
|
|
|
- **원인**: Trust/permission 버튼은 워크벤치 외부 DOM에 렌더링, Run/Accept는 **Antigravity 채팅 webview iframe** 내부의 별도 DOM에 렌더링. 렌더러 스크립트의 `document.querySelector()`는 iframe 내부 접근 불가
|
|
|
|
|
- **해결**: Run 버튼은 DOM Observer가 아닌 **`latestToolCallStep` RPC 기반** 즉시 감지로 대체
|
|
|
|
|
- **주의**: webview iframe에 스크립트 주입은 Electron `executeJavaScript()`로 가능하나, 현재 RPC 방식이 더 안정적
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Accept all/Reject all 리뷰 바 — agent 패널 밖 DOM
|
|
|
|
|
- **증상**: 코드 변경 리뷰 바(Accept all/Reject all)가 DOM Observer에 감지 안 됨
|
|
|
|
|
- **원인**: `scan()` 함수가 `findPanel()` (`.antigravity-agent-side-panel` 등) 내부만 검색. 리뷰 바는 에디터/notification 영역에 렌더링되어 패널 밖에 있음
|
|
|
|
|
- **해결**: `scan()` 검색 범위를 `document.body` 전체로 확장, `Accept all` / `Reject all` 패턴 추가
|
|
|
|
|
- **주의**: 패널+body 이중 검색 시 dedupe 필요 (같은 버튼이 두 번 잡힐 수 있음)
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] GetCascadeTrajectorySteps — cascadeId 파라미터 발견
|
|
|
|
|
- **증상**: `GetCascadeTrajectorySteps`에 `trajectoryId` 파라미터 → 500 "trajectory not found"
|
|
|
|
|
- **원인**: 파라미터명이 `trajectoryId`가 아니라 **`cascadeId`**. 값은 `GetAllCascadeTrajectories.trajectorySummaries`의 맵 키(세션 ID)
|
|
|
|
|
- **해결**: `{ cascadeId: sessionId }`로 호출 → 전체 step 배열 반환 성공
|
|
|
|
|
- **주의**: `latestToolCallStep` 필드는 `GetAllCascadeTrajectories` 응답에 **존재하지 않음** (KI 오류)
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Step 구조 — CORTEX_STEP_STATUS_WAITING 즉시 감지
|
|
|
|
|
- **증상**: stall-based 감지(100초)가 너무 느림
|
|
|
|
|
- **원인**: 이제 `GetCascadeTrajectorySteps`로 최신 step의 status를 직접 확인 가능
|
|
|
|
|
- **해결**: stall 5초 후 step probe → `CORTEX_STEP_STATUS_WAITING` 확인 → 즉시 pending 생성
|
|
|
|
|
- **Step 구조**: `{type: "CORTEX_STEP_TYPE_RUN_COMMAND", status: "CORTEX_STEP_STATUS_WAITING", metadata: {toolCall: {name, argumentsJson}}, runCommand, requestedInteraction}`
|
|
|
|
|
- **주의**: 775-step 하드 리밋은 여전히 존재. 긴 세션에서는 fallback(40초) 사용
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Extension 재설치 안전성 — 자동 패치 메커니즘
|
|
|
|
|
- **증상**: Antigravity 삭제 후 재설치 시 렌더러 스크립트가 동작 안 함
|
|
|
|
|
- **원인**: 재설치 시 HTML/product.json이 원본으로 리셋됨
|
|
|
|
|
- **해결**: Extension의 `setupApprovalObserver()`가 **자동으로** 모든 패치를 수행:
|
|
|
|
|
1. `workbench.html` + `workbench-jetski-agent.html` 인라인 스크립트 삽입
|
|
|
|
|
2. `product.json` SHA256 체크섬 자동 업데이트
|
|
|
|
|
3. HTTP bridge 서버 시작 + 결정론적 포트
|
|
|
|
|
- **주의**: 패치 후 반드시 **Antigravity 풀 재시작** 필요 (Reload Window 불가). Extension VSIX만 설치하면 수동 패치 불필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Response 파일 Race Condition — DOM Observer 승인 실패
|
|
|
|
|
- **증상**: Discord에서 승인 → `[RESPONSE] renderer-handled approval` 로그 출력 → 실제 버튼 클릭 안 됨
|
|
|
|
|
- **원인**: `processResponseFile` (파일 감시자)이 response 파일을 즉시 삭제 → renderer의 `pollResponse`가 HTTP `GET /response/:rid`로 조회 시 파일 이미 없음
|
|
|
|
|
- **해결**: DOM observer 소스일 때는 response 파일을 삭제하지 않도록 수정. HTTP endpoint가 renderer에게 서빙한 후 삭제
|
|
|
|
|
- **주의**: non-DOM (stall/step_probe relay)는 watcher에서 삭제해도 됨
|
|
|
|
|
|
|
|
|
|
### [2026-03-08] Renderer 스크립트 소스 혼동 — 3곳의 코드
|
|
|
|
|
- **증상**: `extension.ts`에 BTN-DUMP 추가 → Reload 2번 → 콘솔에 안 나옴
|
|
|
|
|
- **원인**: renderer 코드가 **3곳**에 존재: (1) `extension.ts`의 `generateApprovalObserverScript()` (소스), (2) `ag-sdk-variet-gravity-bridge.js` (배포됨, Reload시 소스에서 재생성), (3) `workbench-jetski-agent.html` inline (HTML, JS파일과 중복로드 방지됨). 직접 JS파일 패치는 Reload시 소스에서 재생성되어 **덮어씌워짐**
|
|
|
|
|
- **해결**: 항상 `extension.ts`의 `generateApprovalObserverScript()` 함수를 수정 → 컴파일 → 배포 → Reload
|
|
|
|
|
- **주의**: HTML inline은 JS파일이 먼저 로드되어 `window.__agSDK` 가드에 의해 실행 안 됨. 실제 실행되는 것은 JS파일 경로의 스크립트
|
|
|
|
|
|
|
|
|
|
### [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 구현됨 (미검증)~~ → **v1 검증 실패: webview iframe 격리 확인**. v3 `deepFindButtons()`로 업그레이드 (iframe contentDocument + webview.executeJavaScript + shadow DOM). AG 완전 재시작 후 DOM-DUMP로 접근 가능 여부 확인 필요
|
|
|
|
|
- **주의**: `agentPanel.focus`도 미등록, `agentSidePanel.focus`만 존재
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] Renderer DOM — webview iframe 격리 확인 + v3 deep traversal
|
|
|
|
|
- **증상**: Renderer trigger-click이 `document.querySelectorAll('button')`으로 버튼 검색 → Run 버튼 미발견. 감지된 것은 외부 DOM의 trust-level 버튼(`RunAlt+?`)뿐
|
|
|
|
|
- **원인**: Run/Accept 버튼은 AG 채팅 webview iframe (`vscode-webview://` origin) 안에 렌더링. 외부 workbench DOM (`vscode-file://` origin)에서 cross-origin으로 접근 불가
|
|
|
|
|
- **해결**: Renderer v3 `deepFindButtons()` 구현:
|
|
|
|
|
1. Main document 검색 (기존)
|
|
|
|
|
2. `iframe.contentDocument` 접근 시도 (same-origin이면 성공)
|
|
|
|
|
3. `<webview>.executeJavaScript()` 접근 시도 (Electron API)
|
|
|
|
|
4. Shadow DOM 재귀 탐색
|
|
|
|
|
→ **미검증** (AG 재시작 후 DOM-DUMP 결과 필요)
|
|
|
|
|
- **주의**: CDP(Chrome DevTools Protocol)는 **사용자 결정에 의해 명시적으로 거부됨** (`--remote-debugging-port` 필요, 비표준 접근). 표준 DOM API/Electron API 범위 내에서만 해결할 것
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] Deep Inspect HTTP Endpoint — curl로 DOM 분석 트리거
|
|
|
|
|
- **증상**: AG DevTools 콘솔에 붙여넣기 불가 (Chromium 보안 정책)
|
|
|
|
|
- **원인**: Electron 렌더러 DevTools에서 `allow pasting`이 SyntaxError 발생 (`Unexpected identifier 'pasting'`)
|
|
|
|
|
- **해결**: Extension HTTP bridge에 `/deep-inspect` 엔드포인트 추가. `curl.exe http://127.0.0.1:34332/deep-inspect`로 bridge→renderer→재귀 DOM 분석→결과 JSON 반환. 결과는 `~/.gemini/antigravity/bridge/deep-inspect-result.json`에도 저장됨
|
|
|
|
|
- **주의**: 시작 3초 후 자동 실행 + curl로 재트리거 가능. renderer가 2초마다 `/deep-inspect-trigger` 폴링
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] workbench.html inline 스크립트 미삽입 — jetski만 패치한 버그
|
|
|
|
|
- **증상**: AG 재시작 후 `/deep-inspect` timeout — renderer v3 스크립트 미로딩
|
|
|
|
|
- **원인**: `setupApprovalObserver()`가 `workbench-jetski-agent.html`에만 inline 삽입하고, `workbench.html`에는 외부 `<script src>`만 추가. `vscode-file://` 프로토콜은 커스텀 .js 파일을 서빙하지 않으므로 사실상 미로딩
|
|
|
|
|
- **해결**: HTML 패치 로직을 `htmlFiles = ['workbench.html', 'workbench-jetski-agent.html']` 루프로 변경하여 **양쪽 모두 inline** 삽입. 수동 pre-patch 스크립트(`/tmp/patch_workbench.py`)로 즉시 적용 + product.json 체크섬 업데이트
|
|
|
|
|
- **주의**: **항상 양쪽 HTML을 동일하게 패치**. AG가 어느 HTML을 로드할지 런타임까지 알 수 없음. pre-patch 후 1회 풀 재시작이면 충분 (체크섬 사전 업데이트 완료)
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] V8 CachedData — 체크섬 정상이어도 스크립트 미실행
|
|
|
|
|
- **증상**: HTML 패치 + product.json 체크섬 일치 + Bridge 서버 정상 → 그런데도 renderer 스크립트 미실행 (`/deep-inspect` timeout)
|
|
|
|
|
- **원인**: AG 프로세스 인자 `--code-cache-schemes=vscode-webview,vscode-file`에 의해 V8 바이트코드 캐시가 `vscode-file://` 프로토콜에도 적용. product.json 체크섬 검증과 V8 코드 캐시는 **별도 메커니즘** — 체크섬이 맞아도 V8은 `%APPDATA%\Antigravity\CachedData`의 이전 컴파일된 바이트코드를 재사용
|
|
|
|
|
- **해결**: `%APPDATA%\Antigravity\CachedData\*` 전체 삭제 후 AG 풀 재시작. AG 실행 중에도 삭제 가능 (파일 잠금 없음 확인됨)
|
|
|
|
|
- **주의**: CachedData 삭제 후 첫 시작이 약간 느릴 수 있음 (V8이 모든 JS를 재컴파일). **HTML 패치 변경 시마다 CachedData 삭제 필수**. `product.json` 체크섬 업데이트만으로는 불충분
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 승인 전략 결정 체인 (다음 세션 필독)
|
|
|
|
|
|
|
|
|
|
> **이 섹션은 2026-03-08~09 전체 세션의 시행착오를 요약합니다.**
|
|
|
|
|
> **다음 세션은 이미 거부된 접근을 다시 시도하지 마세요.**
|
|
|
|
|
|
|
|
|
|
### ❌ 시도 후 거부된 접근 (재시도 금지)
|
|
|
|
|
|
|
|
|
|
| # | 접근 | 결과 | 거부 사유 |
|
|
|
|
|
|---|------|------|-----------|
|
|
|
|
|
| 1 | LS RPC `HandleCascadeUserInteraction` | `socket hang up` | AG 빌드에서 핸들러 미구현 |
|
|
|
|
|
| 2 | LS RPC `ResolveOutstandingSteps` | CANCEL 동작 (승인이 아님) | 명칭과 달리 step을 취소함 |
|
|
|
|
|
| 3 | VS Code 7개 승인 명령 (`terminalCommand.run` 등) | `command not found` | AG 런타임에 미등록 |
|
|
|
|
|
| 4 | 키보드 시뮬레이션 (`type {Enter}`) | 빈 메시지 전송 | Chat input에 캡처됨 |
|
|
|
|
|
| 5 | `sendChatActionMessage` / `executeCascadeAction` | 미등록 | 119개 명령에 없음 |
|
|
|
|
|
| 6 | pywinauto (OS 레벨) | 사용자 결정 폐기 | 크로스플랫폼 불가, 창 겹침 문제 |
|
|
|
|
|
| 7 | CDP (Chrome DevTools Protocol) | **사용자 명시적 거부** | 비표준, `--remote-debugging-port` 필요 |
|
|
|
|
|
| 8 | Renderer v1 DOM Click (flat scan) | Run 버튼 미발견 | webview iframe 격리 |
|
|
|
|
|
|
|
|
|
|
### ✅ 현재 진행 중인 접근
|
|
|
|
|
|
|
|
|
|
| # | 접근 | 상태 | 다음 단계 |
|
|
|
|
|
|---|------|------|-----------|
|
|
|
|
|
| 1 | **Renderer v3 Deep DOM Traversal** | 패치+체크섬 OK, **V8 CachedData 삭제 완료, AG 재시작 후 검증** | AG 재시작 → `curl.exe http://127.0.0.1:34332/deep-inspect` |
|
|
|
|
|
| 2 | `<webview>.executeJavaScript()` | v3에 포함, **미검증** | 위 결과에서 `hasExecJS=true`이면 유력 |
|
|
|
|
|
| 3 | TerminalExecutionPolicy.EAGER (Turbo) | 미시도, **최후 수단** | 통제권 포기이므로 사용자 승인 필요 |
|
|
|
|
|
|
|
|
|
|
### 🔑 핵심 전제
|
|
|
|
|
- Run/Accept 버튼은 **`vscode-webview://` origin의 격리된 iframe** 안에 있음
|
|
|
|
|
- 외부 workbench DOM에서 `querySelector`로 접근 **불가** (cross-origin)
|
|
|
|
|
- `<webview>.executeJavaScript()`가 **유일한 표준 관통 경로** (Electron API)
|
|
|
|
|
- AG 패치 후 **반드시 풀 프로세스 재시작** 필요 (Reload Window 불충분)
|
|
|
|
|
- **양쪽 HTML (workbench.html + workbench-jetski-agent.html) 모두 inline 패치** 필수
|
|
|
|
|
- HTML 패치 변경 시 **`%APPDATA%\Antigravity\CachedData` 삭제 필수** (V8 바이트코드 캐시 무효화)
|
|
|
|
|
- **CSP `script-src`에 `'unsafe-inline'` 패치 필수** (없으면 인라인 스크립트 무조건 차단)
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] CSP script-src — 인라인 스크립트 무조건 차단
|
|
|
|
|
- **증상**: HTML 패치 ✅, 체크섬 ✅, CachedData 삭제 ✅ — 그런데도 renderer v3 스크립트 미실행 (`/deep-inspect` timeout)
|
|
|
|
|
- **원인**: `workbench.html`의 CSP `<meta http-equiv="Content-Security-Policy">`에서 `script-src 'self' 'unsafe-eval' blob:` — **`'unsafe-inline'` 없음**. 브라우저가 인라인 `<script>` 태그를 무조건 차단
|
|
|
|
|
- **해결**: CSP의 `script-src`에 `'unsafe-inline'` 추가. Extension `setupApprovalObserver()`에 CSP 자동 패치 로직 영구 추가
|
|
|
|
|
- **주의**: `style-src`에는 `'unsafe-inline'`이 있어 스타일은 동작 → 스크립트만 차단되는 것이 함정. `connect-src`에 `http://127.0.0.1:*` 존재하여 HTTP fetch는 허용됨 (CSP 통과 시 통신은 문제없음). 이 CSP 이슈는 **V8 CachedData 이슈와 동시에 존재**하여 진단이 매우 어려웠음
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] 중복 승인 요청 — DOM scan + Step probe 동시 발동
|
|
|
|
|
- **증상**: Discord에 같은 명령에 대해 승인 요청이 2개 도착
|
|
|
|
|
- **원인**: DOM Observer가 `RunAlt+↵` 버튼을 감지하여 pending 생성 + Step probe가 `CORTEX_STEP_STATUS_WAITING` 감지하여 pending 생성 → 동일 step에 대해 2개 파일
|
|
|
|
|
- **해결**: `writePendingApproval()`에 15초 dedup 윈도우 추가 — 최근 DOM observer pending이 있으면 step probe pending 스킵
|
|
|
|
|
- **주의**: DOM scan은 제거 불가 — `Allow Once`, `Allow This Conversation` 등 step probe가 감지 못하는 UI 버튼 존재. 버튼 텍스트도 `(Alt|Ctrl|Shift|Meta)\+.*` 정규식으로 키보드 단축키 정제
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] Step probe reject → ResolveOutstandingSteps가 AI 작업 취소
|
|
|
|
|
- **증상**: Discord에서 거부 클릭 → AI의 현재 step뿐 아니라 진행 중인 작업 전체가 중단
|
|
|
|
|
- **원인**: step probe 경로의 `tryApprovalStrategies(approved=false)` → `ResolveOutstandingSteps` RPC 호출 → 이것은 step을 **CANCEL**하는 파괴적 동작
|
|
|
|
|
- **해결**: step probe 경로에서 reject 시 `tryApprovalStrategies` 호출 제거. reject은 로그만 남기고 파기적 동작 없음. DOM observer 경로만 실제 Reject 버튼 클릭 허용
|
|
|
|
|
- **주의**: `ResolveOutstandingSteps`는 이름과 달리 "해결"이 아닌 "취소". 승인에 절대 사용 금지 (이전 이슈 #55~59 참조)
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] Pending 파일 무한 누적 — write_response 후 미삭제
|
|
|
|
|
- **증상**: `bridge/pending/` 디렉토리에 79개 이상의 .json 파일이 쌓여 봇 시작 시 폭주
|
|
|
|
|
- **원인**: `write_response()`가 pending 파일 status만 변경하고 삭제하지 않음. `get_pending_requests()`에 age filter 없음
|
|
|
|
|
- **해결**: (1) `write_response()`에서 pending 파일 삭제, (2) `get_pending_requests()`에 5분 age filter, (3) 시작 시 `_cleanup_stale_pending()` 호출
|
|
|
|
|
- **주의**: 봇 재시작 시 자동 정리. age filter는 expired 마킹 후 skip
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] Discord 승인 "Run" 표시 — DOM/step_probe 타이밍 불일치
|
|
|
|
|
- **증상**: Discord에 상세 명령어 대신 "Run"만 표시
|
|
|
|
|
- **원인**: DOM observer가 "Run" pending 생성(t=0) → 봇이 3초 후 전송 → step_probe MERGE가 10초 후 완료 → 이미 전송 후
|
|
|
|
|
- **해결**: (1) step_probe가 기존 DOM pending에 MERGE (skip 대신 update), (2) 봇에서 짧은 명령어(≤15자) 4 cycles(12s) 대기, 매 cycle re-read하여 merge 즉시 전송
|
|
|
|
|
- **주의**: MERGE 타이밍은 step_probe poll interval(5s) + stall detection 필요하므로 최소 10초. defer는 이보다 길어야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] DOM observer false positive — Proceed/Continue/Open 버튼 오감지
|
|
|
|
|
- **증상**: 작업 전환 시(notify_user, task_boundary) 승인 요청 없는데도 Discord에 승인 요청 도착
|
|
|
|
|
- **원인**: DOM observer가 AG UI의 PathsToReview "Proceed"/"Open" 버튼, 파일 Open 버튼 등을 승인 버튼으로 오인
|
|
|
|
|
- **해결**: (1) HTTP POST /pending 핸들러에 false positive 필터 추가 (Proceed/Continue/Open/Close/OK 등), (2) "Run" 버튼은 `sessionStalled=true`일 때만 허용
|
|
|
|
|
- **주의**: renderer 인라인 스크립트(HTML)는 extension.js 배포로 안 바뀜 → 서버사이드 필터가 필수. PATS 패턴 수정은 HTML 재패치 시에만 적용
|
|
|
|
|
|
|
|
|
|
### [2026-03-09] Discord ApprovalView timeout — 5분 후 버튼 무응답
|
|
|
|
|
- **증상**: 시간이 지난 후 Discord 승인 버튼 클릭해도 반응 없음
|
|
|
|
|
- **원인**: Discord.py View의 기본 timeout이 300초(5분)로 설정됨
|
|
|
|
|
- **해결**: timeout을 1800초(30분)로 증가
|
|
|
|
|
- **주의**: Discord View timeout은 서버 재시작 후만 적용. 기존 메시지의 View는 이미 만료됨
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] DOM Observer 승인 ENOENT — response 파일 Race Condition
|
|
|
|
|
- **증상**: Discord에서 ✅승인 → extension.log에 `ENOENT: response/xxx.json` 에러 → AG에서 Run 미실행 (간헐적)
|
|
|
|
|
- **원인**: `processResponseFile()` (response watcher)이 response 파일을 읽고 즉시 `fs.unlinkSync()` → renderer `pollResponse()`가 `GET /response/:rid`로 조회할 때 파일 이미 없음
|
|
|
|
|
- **해결**: DOM observer 경로(`isDomObserver`)일 때 `processResponseFile()`에서 response 파일을 삭제하지 않고, HTTP handler(`GET /response/:rid`)가 renderer에 전달 후 삭제. step_probe 경로는 `clickTrigger`로 처리하므로 기존처럼 삭제 OK
|
|
|
|
|
- **주의**: DOM observer와 step_probe 두 경로가 독립적으로 pending을 생성할 수 있으므로, response 삭제 타이밍을 경로별로 구분해야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] Allow Once + Allow This Conversation — 별개 pending으로 분리되는 문제
|
|
|
|
|
- **증상**: 파일 접근 시 Allow Once와 Allow This Conversation이 Discord에 2개 별도 메시지로 도착. Deny 선택지 없음
|
|
|
|
|
- **원인**: renderer `scan()`이 한 사이클에 한 버튼만 처리 후 `return`. 각 버튼이 별도 pending으로 전송. Deny는 PATS 패턴에 미등록
|
|
|
|
|
- **해결**: (1) `scan()`에서 `findButtonContainer()`+`collectSiblingButtons()`로 같은 컨테이너의 관련 버튼을 그룹화하여 `buttons` 배열로 전송, (2) `bot.py` `ApprovalView`가 `buttons` 배열이 있으면 `clear_items()` 후 동적 Discord 버튼 추가, (3) `bridge.py` `UserResponse`에 `button_index` 추가, (4) renderer `pollResponseGroup()`에서 `button_index`에 따라 DOM 버튼 클릭
|
|
|
|
|
- **주의**: `buttons` 배열이 없는 legacy pending은 기존 2버튼(✅승인/❌거부)으로 표시. Deny를 PATS에 추가함
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] step_probe verbosity — argumentsJson 미포함
|
|
|
|
|
- **증상**: Discord 승인 메시지에 `list_dir: DirectoryPath` (파라미터 이름만 표시, 값 없음)
|
|
|
|
|
- **원인**: `GetCascadeTrajectorySteps` 기본 verbosity에서 `argumentsJson` 필드가 빈 문자열
|
|
|
|
|
- **해결**: `verbosity: 1` (DEBUG)을 모든 step_probe 호출에 추가 → argumentsJson 포함. 추출 로직에 `DirectoryPath`, `SearchPath`, `AbsolutePath`, `Url`, `Query` 등 값 우선 표시
|
|
|
|
|
- **주의**: verbosity 0=NORMAL (키만), 1=DEBUG (값 포함). `Object.keys(args)` fallback은 값 추출 실패 시에만
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] 파일 권한 응답 — "unexpected user interaction type: not file permission"
|
|
|
|
|
- **증상**: `.agents` 디렉토리 접근 시 "unexpected user interaction type: not file permission" 에러
|
|
|
|
|
- **원인**: 봇이 file_permission pending에 대해 `runCommand.confirm` RPC를 전송 (file_permission이 아닌 잘못된 interaction type). 또는 오래된 pending에 대해 봇이 자동 처리
|
|
|
|
|
- **해결**: (1) pending의 `step_type`이 `file_permission`일 때 extension이 `filePermission` RPC 사용 (이미 구현됨), (2) DOM Observer의 file_permission 감지 → `buttons` 배열 + `step_type: 'file_permission'` 명시
|
|
|
|
|
- **주의**: 봇이 오래된 pending을 먼저 처리하면 타이밍 불일치 발생 가능. file_permission과 run_command가 동시에 대기 시 올바른 RPC 라우팅 필수
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] active_project.lock — 멀티 프로젝트 동시 사용 차단
|
|
|
|
|
- **증상**: 동일 PC에서 여러 AG 프로젝트 실행 시 첫 번째 프로젝트만 bridge 연결, 나머지는 경고 후 비활성화 시도
|
|
|
|
|
- **원인**: `active_project.lock` 파일이 단일 프로젝트만 bridge 사용을 허용하는 설계. 그런데 `bridgeDisabled` 플래그 설정 후 실제로 아무것도 차단하지 않아 → 모든 인스턴스가 같은 pending/response/commands 디렉토리를 동시에 읽고 씀
|
|
|
|
|
- **해결**: (1) `active_project.lock` 메커니즘 완전 제거, (2) step_probe auto-resolve scan에 `project_name` 필터 추가, (3) response watcher의 기존 project_name 필터 유지
|
|
|
|
|
- **주의**: bridge 격리는 `project_name` 필드 기반 filtering으로 충분. 파일시스템 레벨 분리(옵션 B)는 불필요하게 복잡
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] step_probe file_permission — 3-button 미주입
|
|
|
|
|
- **증상**: AG에서 파일 접근 권한 요청 시 Discord에 3개 선택지(Allow Once/Allow This Conversation/Deny) 대신 2개(승인/거부)만 표시
|
|
|
|
|
- **원인**: `writePendingApproval()`이 `step_type: 'file_permission'`을 설정하지만 `buttons` 배열을 주입하지 않음. DOM observer의 `/pending` 핸들러는 `cmdLower.includes('allow')` 조건에서만 buttons 주입 → step_probe의 `view_file: /tmp/...` 형태 command에서는 trigger 안 됨
|
|
|
|
|
- **해결**: `writePendingApproval()`에서 `step_type === 'file_permission'` && `!buttons`일 때 자동으로 3-button 배열 주입
|
|
|
|
|
- **주의**: DOM observer 경로는 기존 command 텍스트 기반 감지 유지. step_probe 경로만 수정
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] GetAllCascadeTrajectories — 크로스 윈도우 세션 가로채기
|
|
|
|
|
- **증상**: Deriva AG에서 대화 시작 → Discord에 신호 안 잡힘, gravity_control 채널에 Deriva 내용이 릴레이됨
|
|
|
|
|
- **원인**: `GetAllCascadeTrajectories` RPC가 **모든 AG 인스턴스의 세션**을 반환. 기존 register 파일 기반 필터링은 새 세션(아직 register 없음)에 대해 먼저 폴링한 인스턴스가 `bestSession`으로 가져감. gravity_control extension이 Deriva 세션을 자기 것으로 등록
|
|
|
|
|
- **해결**: `trajectoryMetadata.workspaces[0].workspaceFolderAbsoluteUri`를 `vscode.workspace.workspaceFolders[0].uri.fsPath`와 비교하여 자기 workspace 세션만 처리. register 파일은 metadata 없는 레거시 세션용 fallback으로 유지
|
|
|
|
|
- **주의**: workspace URI는 `file:///c:/Users/...` 형식이므로 normalize 필수 (protocol strip, %3A decode, 슬래시 통일, lowercase). 양쪽 AG 풀 재시작 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] 크로스 프로젝트 Response Watcher 우회
|
|
|
|
|
- **증상**: Deriva 세션에 대한 승인 시도가 gravity_control에서 `stepIndex:-1`로 반복 실패
|
|
|
|
|
- **원인**: (1) pending 파일 삭제 후 response watcher가 project_name 체크 건너뜀, (2) `processResponseFile`이 전역 `activeSessionId` 사용
|
|
|
|
|
- **해결**: (1) response JSON의 `project_name`으로 fallback 필터, (2) 세션ID를 pending에서 우선 사용, (3) `logToFile`에 `[projectName]` 접두사, (4) `UserResponse`에 `project_name` 추가
|
|
|
|
|
- **주의**: response 데이터 자체에 project_name 필수. auto_resolve로 pending 삭제 시 우회됨
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] file_permission — write 도구 3-button 미주입
|
|
|
|
|
- **증상**: `replace_file_content` 등 파일 수정 시 AG에선 3개 버튼(Deny/Allow Once/Allow This Conversation) 표시, Discord에선 2개(승인/거부)만 표시
|
|
|
|
|
- **원인**: step_probe의 `file_permission` 도구 리스트에 read 도구만 포함 (`view_file`, `grep_search` 등). `replace_file_content`, `write_to_file`, `multi_replace_file_content` 누락
|
|
|
|
|
- **해결**: 세 write 도구를 file_permission 리스트에 추가 (2곳: offset/normal scan)
|
|
|
|
|
- **주의**: AG가 파일 접근 권한을 요청하는 모든 도구는 이 리스트에 포함되어야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] bestSession IDLE 고착 — RUNNING 세션 못 잡는 버그
|
|
|
|
|
- **증상**: Deriva AG에서 새 대화 시작 → bridge가 구 IDLE 세션만 추적, 새 RUNNING 세션 무시
|
|
|
|
|
- **원인**: `bestSession` 선택이 `lastModifiedTime`만 비교. 구 IDLE 세션의 modTime이 더 최신이면 새 RUNNING 세션보다 우선 선택됨
|
|
|
|
|
- **해결**: 2단계 비교 — (1) RUNNING 세션이 IDLE보다 항상 우선, (2) 동일 상태 내에서 `lastModifiedTime` 최신 순
|
|
|
|
|
- **주의**: Reload Window로도 해결되지만 근본적으로는 RUNNING 우선 로직이 필요. `[SESSION-FILTER]` 진단 로그도 추가됨
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] Bot IDLE 채널 자동 생성 — 불필요한 Discord 채널 증식
|
|
|
|
|
- **증상**: 봇 시작 시 IDLE 프로젝트까지 포함하여 모든 등록된 프로젝트의 채널을 자동 생성
|
|
|
|
|
- **원인**: `pending_approval_scanner`가 매 사이클마다 `conv_to_project` 전체를 순회하며 채널 생성
|
|
|
|
|
- **해결**: 자동 채널 생성 루프 제거. 채널은 실제 신호(snapshot/pending) 도착 시 `_get_channel()`로 on-demand 생성
|
|
|
|
|
- **주의**: `_get_channel()`은 snapshot scanner와 approval sender에서 이미 호출되므로 별도 사전 등록 불필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] Reload Window 후 세션 stale — GetAllCascadeTrajectories 미갱신
|
|
|
|
|
- **증상**: Reload Window 후 현재 대화의 세션이 `GetAllCascadeTrajectories`에 IDLE/구 stepCount로 고정. 새 메시지를 보내도 RUNNING으로 변경되지 않음
|
|
|
|
|
- **원인**: Reload Window 시 Extension은 리셋되지만 LS 프로세스는 유지됨. LS 내부 trajectory tracker가 Reload 이전 세션 상태를 캐시하고 갱신하지 않음
|
|
|
|
|
- **해결**: AG 완전 종료 → 재실행 (Full restart). Reload Window로는 해결 불가
|
|
|
|
|
- **주의**: Extension 코드 변경 후 배포 시 Reload Window 대신 Full restart 권장. 이건 AG LS 내부 동작이라 사용자 측에서 수정 불가
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] start_bot.bat — Windows Store Python 스텁 우선 실행
|
|
|
|
|
- **증상**: `start_bot.bat` 실행 시 `'...' is not recognized` 에러 후 `Python`만 출력되고 즉시 종료
|
|
|
|
|
- **원인**: `where python`이 Windows Store의 Python 스텁(AppAlias)을 먼저 찾음. 이 스텁은 실제 Python이 아니라 Store 페이지로 리다이렉트하는 더미
|
|
|
|
|
- **해결**: conda 경로(`C:\ProgramData\miniforge3\envs\gravity_control\python.exe`)를 `where python`보다 먼저 확인하도록 순서 변경
|
|
|
|
|
- **주의**: Windows 10/11에서 App Aliases의 `python.exe`가 PATH에 기본 포함됨. 항상 구체적 경로 우선 확인 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] VSIX 빌드 — SDK JS 파일 미포함 (require 실패)
|
|
|
|
|
- **증상**: Extension 활성화 후 `SDK not initialized`, `gravityBridge.connect` 클릭 시 에러. `extension.log`에 SDK init 성공 로그 없음
|
|
|
|
|
- **원인**: `src/sdk/index.js`는 순수 JavaScript 파일. TypeScript 컴파일러(`tsc`)는 `.ts`만 변환하고 **`.js` 파일을 `out/`에 복사하지 않음**. `require('./sdk/index')`가 `out/sdk/index.js`를 찾지 못해 SDK 로드 실패
|
|
|
|
|
- **해결**: `package.json`의 `compile` 스크립트에 `tsc` 후 `node -e "..."` 복사 단계 추가 (`src/sdk/` → `out/sdk/`)
|
|
|
|
|
- **주의**: VSIX 패키징은 `.vscodeignore`의 `!out/**` 규칙으로 `out/sdk/`를 포함함. **문제는 빌드 단계에서 복사 누락**이므로 `npm run compile` 후 `out/sdk/` 존재 여부 반드시 확인
|
|
|
|
|
|
|
|
|
|
### [2026-03-10] SDK _findLSProcess — 대소문자 구분 workspace hint 매칭 실패
|
|
|
|
|
- **증상**: variet-agent AG에서 활성 대화 진행 중인데 Discord에 신호 미도달. extension.log에 `[SESSION-FILTER] NO session matched! total=11`
|
|
|
|
|
- **원인**: SDK `_findLSProcess()`가 workspace hint(`desktop_variet_agent`, 소문자)를 LS 프로세스 command line의 `Desktop_variet_agent`(대문자 D)과 `String.includes()`(대소문자 구분)로 비교 → 매치 실패 → 첫 번째 LS(gravity_control)로 fallback → variet-agent 세션 없음. 각 AG 창은 **별도의 LS 프로세스**를 가지며(workspace_id로 구분), SDK가 잘못된 LS에 연결하면 해당 창의 세션을 볼 수 없음
|
|
|
|
|
- **해결**: `extension.ts`에 `fixLSConnection()` 함수 추가. SDK init 후 PowerShell로 모든 LS 프로세스를 조회, `--workspace_id`를 **대소문자 무시 비교**하여 올바른 LS 발견 → `sdk.ls.setConnection(port, csrfToken)`으로 재연결
|
|
|
|
|
- **주의**: 각 AG 창마다 별도 LS 프로세스 존재 확인됨 (workspace_id로 구분). SDK `_getWorkspaceHint()`는 `.toLowerCase()` 적용하지만 검색 대상(LS command line)은 원본 대소문자를 유지하여 불일치 발생. 이 fix는 모든 multi-window AG 환경에서 필수
|
|
|
|
|
|
|
|
|
|
### [2026-03-11] config.py BRAIN_PATH — `.env` 빈 문자열 → CWD 해석 버그
|
|
|
|
|
- **증상**: 봇이 Extension의 snapshot/pending을 전혀 읽지 못함. 로그에 `Brain path: .`, `Bridge protocol initialized: bridge` (상대경로)
|
|
|
|
|
- **원인**: `.env`에 `BRAIN_PATH=` (빈 값)이 있으면 `os.getenv("BRAIN_PATH", default)`가 빈 문자열 `""`를 반환 (default 미사용). `Path("")` → `Path(".")` → CWD로 해석. 봇의 bridge 디렉토리가 `CWD/bridge/`가 되어 Extension의 `~/.gemini/antigravity/bridge/`와 **다른 경로**
|
|
|
|
|
- **해결**: `os.getenv("BRAIN_PATH") or default` 패턴으로 빈 문자열도 처리
|
|
|
|
|
- **주의**: `os.getenv(key, default)`는 key가 존재하면 (빈 값이라도) default를 사용하지 않음. 환경변수의 빈 값을 fallback 처리하려면 반드시 `or` 사용
|
|
|
|
|
|
|
|
|
|
### [2026-03-11] Extension DEDUP MERGE — 크로스 프로젝트 pending 오염
|
|
|
|
|
- **증상**: `#ag-lifetimepd` 채널에 variet_agent의 `write_to_file` 승인 요청이 표시됨
|
|
|
|
|
- **원인**: `writePendingApproval()`의 DEDUP 로직이 `project_name`을 체크하지 않음. LifetimePD DOM observer가 "Deny" pending 생성 → 7초 후 variet_agent step_probe가 **같은 pending에 MERGE** → command가 덮어씌워짐. pending의 `project_name: lifetimepd`는 유지되어 잘못된 채널로 라우팅
|
|
|
|
|
- **해결**: 3곳 dedup 조건에 `existing.project_name === projectName` 가드 추가: (1) MERGE, (2) step_index 중복 스킵, (3) file_permission 중복 필터
|
|
|
|
|
- **주의**: 모든 Extension 인스턴스가 **동일한 `bridge/pending/` 디렉토리**를 공유하므로, pending 파일 간 상호작용 시 반드시 `project_name` 일치 여부 확인 필수
|
|
|
|
|
|
|
|
|
|
### [2026-03-11] Collector 동기 HTTP — aiohttp 전환
|
|
|
|
|
- **증상**: Collector가 Gateway에 동기 urllib 요청 시 이벤트 루프 전체 블로킹, 3초 폴링이 10초+로 늘어남
|
|
|
|
|
- **원인**: `RemoteTransport._request()`가 `urllib.request.urlopen()` 사용 (blocking I/O)
|
|
|
|
|
- **해결**: `aiohttp.ClientSession` 기반 비동기 전환 + 연결 모니터링 + 재시도 큐 (100건)
|
|
|
|
|
- **주의**: `import aiohttp`는 lazy (`_get_session()` 내부). 로컬 conda 환경에 `aiohttp>=3.9.0` 설치 필수
|
|
|
|
|
|
|
|
|
|
### [2026-03-11] Extension fs.watch — Collector response 파일 감지 누락
|
|
|
|
|
- **증상**: Discord에서 거부 → Collector가 `bridge/response/` 파일 정상 생성 → Extension `processResponseFile` 미호출 (`[RESPONSE]` 로그 0건)
|
|
|
|
|
- **원인**: `setupResponseWatcher()`가 `event === 'rename'`만 필터링. 파일 생성 방식에 따라 `change` 이벤트만 발생할 수 있음. Windows `fs.watch`는 빠른 write+delete 시 이벤트 누락 알려짐
|
|
|
|
|
- **해결**: **미해결** — `event` 필터 제거 또는 polling 전환 필요. 사이드이펙트 검증 필요하여 보류
|
|
|
|
|
- **주의**: 이 문제로 인해 **Discord에서의 모든 거부/정지 명령이 Extension에 전달되지 않음**
|
|
|
|
|
### [2026-03-11] fs.watch silent fail — Discord→Extension 명령 전달 불가
|
|
|
|
|
- **증상**: `!auto`, `!stop`, 텍스트 릴레이 등 Discord→Extension 방향 명령이 전부 작동 안 함. Extension log에 `[AUTO]` 로그 0건
|
|
|
|
|
- **원인**: `watchCommandsDir()`의 `fs.watch`가 Windows에서 silent fail. watcher 세팅은 되지만 이벤트가 실제로 fire 안 됨
|
|
|
|
|
- **해결**: `fs.watch` 유지 + 3초 `setInterval` 폴링 fallback 추가. `processAllCommands()` 함수로 공통화
|
|
|
|
|
- **주의**: `fs.watch`는 Windows에서 구조적으로 불안정. **새 watcher 추가 시 반드시 polling fallback 병행**
|
|
|
|
|
|
|
|
|
|
### [2026-03-11] rejectAgentStep — AG 미등록 VS Code 커맨드
|
|
|
|
|
- **증상**: `/stop` 및 거부 시 `antigravity.agent.rejectAgentStep` → `command not found`
|
|
|
|
|
- **원인**: AG IDE가 이 커맨드를 런타임에 등록하지 않음
|
|
|
|
|
- **해결**: **미해결** — AG가 실제 등록하는 커맨드 목록 조사 후 올바른 커맨드로 교체 필요
|
|
|
|
|
- **주의**: `[2026-03-09] VS Code Accept Commands` 이슈와 같은 근본 원인 (AG 커맨드 미등록)
|
|
|
|
|
|
|
|
|
|
### [2026-03-12] RemoteTransport 429 무한 루프 — Extension 크래시 + AG 먹통
|
|
|
|
|
- **증상**: Collector→Gateway HTTP 요청 시 `429 Rate limited` 로그가 초당 수십 건 무한 반복. Extension 꺼지고 AG 재시작 시 화면 먹통
|
|
|
|
|
- **원인**: 3가지 복합: (1) `RemoteTransport._arequest()`가 429 수신 시 백오프 없이 즉시 리턴 → 3초 후 다시 전체 재시도, (2) `_poll_responses_loop()`가 모든 forwarded pending에 개별 HTTP 요청 → pending 쌓이면 1초에 10개 초과, (3) Gateway rate limit이 10 req/1초로 너무 공격적
|
|
|
|
|
- **해결**: (1) `bridge.py` — 지수 백오프 추가 (1s→2s→4s…60s) + `Retry-After` 헤더 지원 + `is_rate_limited` 프로퍼티, (2) `gateway.py` — rate limit 10→30으로 완화 + `Retry-After` 헤더 응답, (3) `collector.py` — 모든 루프에서 `is_rate_limited` 체크 + response 폴링에 0.2초 인터-리퀘스트 딜레이
|
|
|
|
|
- **주의**: AG 먹통은 봇 자체가 유발한 문제. Extension이나 AG 내부를 건드린 것이 아님. 봇을 끄고 AG를 재시작하면 정상 복구 가능
|
|
|
|
|
|
|
|
|
|
### [2026-03-12] workbench.html 0-byte 파괴 — AG 새 창 먹통
|
|
|
|
|
- **증상**: AG 새 창 열면 화면 먹통 (빈 화면). 봇을 꺼도 복구 안 됨
|
|
|
|
|
- **원인**: 멀티 윈도우 환경(gravity_control + variet_agent + edf)에서 3개 Extension 인스턴스가 동시에 `workbench.html`을 읽고/패치/쓰기. 한 인스턴스가 `writeFileSync` 중에 다른 인스턴스가 `readFileSync` → 빈 문자열 반환 → 파이프라인 처리 후 0 bytes로 덮어쓰기
|
|
|
|
|
- **해결**: (1) pre-patch backup (.orig 파일) 생성, (2) 파일별 구조 검증 (requiredMarker), (3) 손상/잘못된 타입 감지 시 .orig에서 자동 복원
|
|
|
|
|
- **주의**: **AG 풀 재시작 필수**. 멀티 윈도우 환경에서 HTML 패치 race condition은 근본적으로 파일 잠금 없이는 완전 해결 불가 — 안전 가드로 피해 최소화
|
|
|
|
|
|
|
|
|
|
### [2026-03-12] workbench.html 크로스 복원 — CSS 미로딩으로 레이아웃 깨짐
|
|
|
|
|
- **증상**: AG 재시작 후 아이콘/요소는 보이지만 전부 왼쪽으로 쏠려서 정렬 안 됨. 레이아웃 완전 깨짐
|
|
|
|
|
- **원인**: `workbench.html`을 `workbench-jetski-agent.html`에서 복원할 때 JS만 교체 (`jetskiAgent.js` → `workbench.js`)하고 **CSS 참조를 교체하지 않음**. 두 파일은 완전히 다른 CSS를 사용:
|
|
|
|
|
- `workbench.html` → `workbench.desktop.main.css` (VS Code 기본 레이아웃)
|
|
|
|
|
- `workbench-jetski-agent.html` → `tw-base.tailwind.css` + `jetskiMain.tailwind.css` (Jetski Agent 전용)
|
|
|
|
|
잘못된 CSS가 로드되면 JS는 정상 작동하나 레이아웃이 완전히 무너짐
|
|
|
|
|
- **해결**: (1) Extension에 파일별 `requiredMarker` 검증 추가 (workbench.html은 `workbench.desktop.main.css`, jetski는 `jetskiMain.tailwind.css` 필수), (2) 첫 패치 전 `.orig` 백업 자동 생성, (3) 손상 또는 잘못된 타입 감지 시 `.orig`에서 자동 복원. **두 HTML 파일은 절대 크로스 복원 불가**
|
|
|
|
|
- **주의**: `workbench.html`과 `workbench-jetski-agent.html`은 **교환 불가능**. CSS 경로, JS 엔트리 포인트, CSP 세부 설정이 모두 다름. 수동 복원 시도 금지 — Extension의 자동 복원 로직에 의존할 것
|
|
|
|
|
|
|
|
|
|
### [2026-03-12] Collector 단일 프로젝트 폴링 — 멀티 프로젝트 command 전달 불가
|
|
|
|
|
- **증상**: Discord `ag-deriva` 채널에서 메시지 전송 → Deriva AG IDE에 전달되지 않음. Gateway에 command 정상 저장됨
|
|
|
|
|
- **원인**: `collector.py` `_poll_commands_loop()`이 `self.project_name` (= `.env`의 `PROJECT_NAME`, 기본 `gravity_control`)으로만 `GET /api/commands/{project}` 폴링. Gateway는 프로젝트별로 command를 저장(`_commands["deriva"]`)하지만 Collector가 `"gravity_control"`만 요청 → 다른 프로젝트 명령이 30분 TTL 후 만료
|
|
|
|
|
- **해결**: `_discover_local_projects()`로 `bridge/register/*.json`에서 로컬 프로젝트 목록을 자동 발견, 모든 프로젝트에 대해 폴링. Extension은 L131에서 `project_name` 필터링이 있어 자기 프로젝트만 소비
|
|
|
|
|
- **주의**: 멀티 PC 환경에서 `/api/commands/all` 엔드포인트는 크로스 PC 명령 오염을 유발하므로 사용 금지. 각 Collector가 자기 PC의 `register/` 기반으로 프로젝트를 발견하는 것이 올바른 설계
|
|
|
|
|
|
|
|
|
|
### [2026-03-12] RemoteTransport backing off 무한 반복 — IDLE에서 더 심함
|
|
|
|
|
- **증상**: AG 미사용(IDLE) 시 `backing off 1s` 경고가 ~4초마다 영구 반복. 사용 중보다 IDLE 시 더 자주 발생
|
|
|
|
|
- **원인**: 3가지 구조적 결함의 복합:
|
|
|
|
|
1. **백오프 즉시 리셋**: `_reset_backoff()`가 성공 1회에 `_backoff_seconds=0` → 7개 루프가 같은 transport 공유 → 첫 성공에 리셋 → 지수 백오프가 영원히 1s에서 멈춤
|
|
|
|
|
2. **IDLE 시 불필요한 요청**: 6개 프로젝트 command 폴링이 3초마다 빈 응답 반복
|
|
|
|
|
3. **asyncio burst**: 7개 루프가 같은 `asyncio.sleep(3)` → 같은 tick에 동시 깨어남 → 순간 burst로 Gateway 1초 윈도우(30 req) 초과
|
|
|
|
|
- **해결**: (1) `_on_request_success()` — 연속 5회 성공 후에만 백오프 절반 감소, (2) `_poll_commands_loop` adaptive 간격 (빈 응답 시 3s→10s→30s→60s), (3) Gateway 윈도우 1s/30→10s/100, (4) 루프 stagger (0~3.5s 오프셋)
|
|
|
|
|
- **주의**: `_reset_backoff()` [즉시 리셋] 패턴은 **다중 소비자가 같은 transport를 공유하는 환경에서 절대 사용 금지**. 단일 성공이 전체 백오프를 무효화함
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] DEDUP step_index 크로스 세션 충돌 — 승인 신호 누락
|
|
|
|
|
- **증상**: variet_agent에서 WAITING step 감지 → pending 미생성 → Discord 승인 요청 미전달 → 10분+ 대기
|
|
|
|
|
- **원인**: `writePendingApproval()`의 DEDUP 로직이 `step_index`로 중복 검사 시 `conversation_id`를 비교하지 않음. 세션 A(step=28)와 세션 B(step=28)가 동일시되어 DEDUP skip. 각 세션의 step_index는 0부터 시작하므로 크로스 세션 충돌 빈번
|
|
|
|
|
- **해결**: DEDUP 조건에 `existing.conversation_id === data.conversation_id` 가드 추가
|
|
|
|
|
- **주의**: `project_name` 가드만으로는 불충분 — 같은 Extension 인스턴스가 여러 세션을 볼 수 있음. 반드시 `conversation_id`까지 비교 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] fs.watch silent fail — Discord→Extension 명령 전달 불가
|
|
|
|
|
- **증상**: `!auto`, `!stop`, 텍스트 릴레이 등 Discord→Extension 방향 명령이 전부 작동 안 함. Extension log에 `[AUTO]` 로그 0건
|
|
|
|
|
- **원인**: `watchCommandsDir()`의 `fs.watch`가 Windows에서 silent fail. watcher 세팅은 되지만 이벤트가 실제로 fire 안 됨. 실측 테스트에서 command 파일 드롭 후 2초 대기 → 미소비 확인
|
|
|
|
|
- **해결**: `fs.watch` 유지 + 3초 `setInterval` 폴링 fallback 추가. `processAllCommands()` 함수로 공통화
|
|
|
|
|
- **주의**: `fs.watch`는 Windows에서 구조적으로 불안정 — 이 프로젝트의 response watcher (known-issue [2026-03-11])에서도 동일 문제. **새 watcher 추가 시 반드시 polling fallback 병행**
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] projectName=default 승인 오발 — workspace 없는 AG 창
|
|
|
|
|
- **증상**: workspace 없는 AG 창(Empty Window)이 step_probe로 다른 프로젝트의 WAITING step 감지 → `project_name: "default"` pending 생성 → `#ag-default` 채널로 전달 → 유저 미확인
|
|
|
|
|
- **원인**: `detectProjectName()`이 workspace 없으면 `"default"` 반환. step_probe는 LS의 `GetAllCascadeTrajectories`로 **모든** 세션을 볼 수 있으므로, 다른 workspace 세션의 WAITING을 감지하여 잘못된 project_name으로 pending 생성
|
|
|
|
|
- **해결**: step_probe 2곳(normal + offset)에서 `projectName === 'default'`이면 pending 생성/auto-approve 억제. 로그만 남김
|
|
|
|
|
- **주의**: `#ag-default` 채널이 생성되면 유저가 인지하지 못하므로 치명적. Empty Window에서는 bridge 기능을 최소화해야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] Discord Gateway MESSAGE_CREATE 중복 — embed 이중 전송
|
|
|
|
|
- **증상**: 텍스트 메시지, `!auto` 등 Discord 명령 시 동일 embed가 2개 전송
|
|
|
|
|
- **원인**: Discord Gateway가 WebSocket 불안정 시 `MESSAGE_CREATE` 이벤트를 중복 전달 (known discord.py issue). 봇 프로세스 1개, 코드상 `on_message` 1회 실행 로직이지만 이벤트 자체가 2번 도착
|
|
|
|
|
- **해결**: `on_message`에 `_processed_message_ids: set[int]` (bounded 200개) 중복 방지 추가
|
|
|
|
|
- **주의**: Gateway reconnection, RESUME 실패 시 발생 빈도 증가. message ID 기반 dedup이 가장 확실한 방어
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] HTML 패치 멀티 인스턴스 race condition — 화면 파괴
|
|
|
|
|
- **증상**: Extension 패치 후 AG 재시작 시 전체 화면 날아감 (빈 화면/깨진 레이아웃)
|
|
|
|
|
- **원인**: 2+ Extension 인스턴스가 `setupApprovalObserver()`에서 동시에 같은 `workbench.html`/`workbench-jetski-agent.html`에 `readFileSync`/`writeFileSync` → 0-byte 파일 또는 부분 데이터 → 영구 손상
|
|
|
|
|
- **해결**: `.patch-lock` 파일 기반 cross-instance lock 추가 (30초 stale 판정). Lock 취득 실패 시 패치 skip
|
|
|
|
|
- **주의**: Lock은 "방지"에 해당. 기존 `.orig` 백업은 "복구"에 해당. 둘 다 유지해야 함. Lock 파일 경로 = `scriptDir/.patch-lock`
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] 로컬 승인 ↔ Discord 승인 교차 race condition
|
|
|
|
|
- **증상**: AG에서 직접 Run 클릭 후 Discord 승인 요청이 "완료됨" 표시 안 됨. Discord에서도 뒤늦게 클릭 시 이미 완료된 step에 RPC 실행 → 에러 스팸
|
|
|
|
|
- **원인 1**: auto_resolve가 pending 상태만 변경하고 Discord에 알림 없음 → `writeChatSnapshot()` 추가
|
|
|
|
|
- **원인 2**: `processResponseFile()`이 pending의 `auto_resolved`/`expired` 상태를 체크하지 않음 → 상태 확인 후 skip 로직 추가
|
|
|
|
|
- **원인 3**: Bot의 auto_resolved 스캐너가 `discord_message_id`에만 의존 — Extension은 이 값을 모름 → `_approval_messages` dict (rid→msg_id) 추가, fallback 조회
|
|
|
|
|
- **주의**: `processResponseFile` L2534의 `lastPendingStepIndex = -1` 리셋이 Discord 승인 경로에서 auto_resolve 중복 진입을 방지하는 핵심 gate. 이 줄을 삭제하면 중복 알림 발생
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] Extension 버전 미배포 — source ≠ deployed
|
|
|
|
|
- **증상**: 소스(v0.3.10)에 수정한 코드가 실제 동작하지 않음. 로그에서 수정 전 동작 확인됨
|
|
|
|
|
- **원인**: Extension 빌드/배포 누락. `~/.antigravity/extensions/`에 구 버전(v0.3.8) 남아있음
|
|
|
|
|
- **해결**: VSIX 빌드 → 설치 → 구 버전 디렉토리 삭제 → AG 전체 재시작 (Reload Window 불충분)
|
|
|
|
|
- **주의**: Extension 코드 수정 후 **반드시** `npm run compile && npx vsce package` → 배포까지 확인. AG는 전체 File→Exit 후 재시작 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] 크로스 프로젝트 DEDUP MERGE — Deriva→gravity_control 오염
|
|
|
|
|
- **증상**: Deriva의 step_probe 데이터(step_index, command)가 gravity_control의 DOM observer pending에 MERGE됨. Discord에 Deriva 명령이 gravity_control 채널에 표시
|
|
|
|
|
- **원인**: `writePendingApproval()` DEDUP MERGE 조건에 `project_name` 가드 없음 — `source === 'dom_observer' && status === 'pending'`만 검사하므로 타 프로젝트 pending에도 MERGE
|
|
|
|
|
- **해결**: MERGE 조건에 `existing.project_name === projectName` 추가 (v0.3.10)
|
|
|
|
|
- **주의**: `bridge/pending/` 디렉토리는 모든 Extension 인스턴스가 공유. 파일 읽기/쓰기 시 반드시 `project_name` 기반 필터링 필수
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] Double-Fire Auto-Approve — AI 세션 중단
|
|
|
|
|
- **증상**: auto-approve ON 시 AI 세션이 간헐적으로 중단/멈춤
|
|
|
|
|
- **원인**: step_probe가 WAITING 감지 시 `autoApproveEnabled`면 직접 `tryApprovalStrategies()` 호출(경로A). 동시에 `writePendingApproval()` → Bot auto_approve_scanner → response 파일 → `processResponseFile()` → `tryApprovalStrategies()` 호출(경로B). 같은 step에 대해 2번 RPC 호출 → 충돌
|
|
|
|
|
- **해결**: Extension auto-approve 경로 A 제거. Bot만 auto-approve 담당 (v0.3.11). Extension은 항상 `writePendingApproval()` 경로 사용
|
|
|
|
|
- **주의**: 향후 Extension에서 직접 approve 로직을 추가할 때는 Bot auto-approve와의 경합을 반드시 고려. 단일 경로 원칙 유지
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] DOM Observer "Deny" False Positive — Auto-approve 세션 크래시
|
|
|
|
|
- **증상**: auto-approve ON 시 Discord에 "Deny" command가 자동 승인됨 → AI 세션 반복 크래시
|
|
|
|
|
- **원인**: AG file_permission UI에 [Allow Once] [Allow This Conversation] [Deny] 3개 버튼 표시 → DOM observer가 "Deny" 텍스트를 독립 pending으로 생성 (`FALSE_POSITIVE_RE`에 "Deny" 미포함). 이 pending에는 `step_type`이 없어 default 분기 `runCommand: { confirm: true }` RPC 전송 → 실제 대기 중인 `file_permission`과 타입 불일치 → 세션 크래시
|
|
|
|
|
- **해결**: (1) `FALSE_POSITIVE_RE`에 `Deny|Allow Once|Allow This Conversation|Dismiss|Decline` 추가, (2) bot.py auto-approve에 reject-word command 차단 가드, (3) smart button_index 선택 (방어)
|
|
|
|
|
- **주의**: `FALSE_POSITIVE_RE`는 렌더러 인라인 스크립트 안에 있으므로 **VSIX 빌드 → AG 풀 재시작** 필요. 새 UI 버튼 패턴 추가 시 반드시 이 필터 점검. **수정 시 해당 파일만 보지 말고 전체 데이터 플로우(producer→consumer→side effects) 분석 필수 (AGENT.md 규칙 #10)**
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] PATS 배열 Deny 트리거 — 근본 수정
|
|
|
|
|
- **증상**: DOM Observer가 "Deny"를 주 트리거로 사용하여 command="Deny" pending 생성. step_probe MERGE와 결합하여 디스코드에 Deny 더미 전달
|
|
|
|
|
- **원인**: `PATS` 배열에 `{re:/^Deny$/i, type:'permission'}`이 포함되어 있어 DOM 순서상 Deny가 먼저 스캔됨. `FALSE_POSITIVE_RE`가 HTTP POST 경로에서는 차단하지만 `writePendingApproval` 직접 파일 작성 경로를 우회
|
|
|
|
|
- **해결**: PATS에서 거절/보조 버튼(Deny, Reject all, Dismiss) 제거. 긍정 버튼만 그룹 트리거 → 보조 버튼은 `ALL_ACTION_RE` + `collectSiblingButtons`로 형제 수집
|
|
|
|
|
- **주의**: PATS = "그룹 생성 트리거", ALL_ACTION_RE = "형제 수집 패턴". 새 버튼 추가 시 이 2단계 구조를 반드시 이해하고 추가
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] Auto-Resolved 채팅 폭주 — 루프 내 writeChatSnapshot
|
|
|
|
|
- **증상**: "✅ AG에서 직접 승인됨" 메시지가 pending 파일 개수(4~5개)만큼 Discord에 반복 전송
|
|
|
|
|
- **원인**: auto_resolved 루프 내부에서 매 파일마다 `writeChatSnapshot()` 호출. 또한 `conversation_id` 미검증으로 타 세션 pending도 오염
|
|
|
|
|
- **해결**: (1) writeChatSnapshot을 루프 바깥으로 이동(resolvedCount > 0일 때 1회), (2) `pd.conversation_id === activeSessionId` 조건 추가, (3) primaryCommand에서 'Deny'/'Allow' 텍스트 제외
|
|
|
|
|
- **주의**: Bridge 파일 루프에서 외부 시스템(Discord)에 메시지를 보낼 때는 반드시 루프 바깥에서 집계 후 1회 발송
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] 멀티 프로젝트 동시 신호 정지 — Scanner O(N) Discord API + fetch_channels 병목
|
|
|
|
|
- **증상**: 여러 프로젝트(3+)가 동시에 pending을 생성하면 모든 프로젝트의 신호 전달이 정지, AI 멈춤
|
|
|
|
|
- **원인**: 2가지 복합: (1) `pending_approval_scanner`가 1 tick에 모든 pending(15건+)을 순차 처리 → 건당 `_get_channel()` + `channel.send/edit/fetch_message` Discord API 호출 → 20~40 API 호출이 Discord 429 rate limit 유발 → discord.py 내부 대기 → scanner tick 수십 초 블로킹 → 모든 프로젝트 신호 지연. (2) `_get_channel()`이 캐시 미스 시 `guild.fetch_channels()` (Discord API)를 asyncio Lock 내에서 호출 → Lock 대기 + API 호출 순차 발생
|
|
|
|
|
- **해결**: (1) `_get_channel()`을 `discord.utils.get(guild.channels)` 캐시 기반으로 변경 — API 호출 제거, (2) scanner에 per-tick cap 추가 (Phase 1: 5건, Phase 2: 5건) — 나머지는 다음 tick으로 이월
|
|
|
|
|
- **주의**: `guild.channels`는 discord.py 내부 캐시(Gateway 이벤트로 자동 갱신). 채널 삭제/생성은 캐시에 즉시 반영됨. per-tick cap은 최대 3초 지연을 유발하지만 전체 정지보다 훨씬 나음
|
|
|
|
|
|
|
|
|
|
### [2026-03-15] 이전 분석 오판(False Positive) — 교훈
|
|
|
|
|
- **증상**: 시스템 감사 시 P0/P1으로 보고한 문제들이 실제로는 코드 방어 로직(멱등성, try-catch, 의도된 exact-match)으로 이미 방어되고 있었음
|
|
|
|
|
- **원인**: 로컬 코드 스니펫만 보고 판단. 전체 데이터 생명주기를 끝까지 추적하지 않아 방어 로직을 놓침
|
|
|
|
|
- **해결**: 나노단위 전체 Flow 추적으로 교차 검증 후 진짜 결함만 추림 (P2 3건, P3 2건)
|
|
|
|
|
- **주의**: **코드 감사 시 반드시 producer→transport→consumer→side effects 전체 경로를 추적. 단편적 로컬 분석으로 위험도를 과장하지 말 것**
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] processResponseFile 상태 리셋 — 무한 루프 vs auto_resolve 회귀
|
|
|
|
|
- **증상**: (v0.3.11) Discord 승인 후 같은 step에 대해 pending이 반복 생성 → 무한 auto-approve 루프
|
|
|
|
|
- **원인**: processResponseFile이 `lastPendingStepIndex = -1` + `stallProbed = false`로 무조건 리셋 → step_probe가 같은 WAITING step을 새 step으로 착각 → pending 재생성 → 루프
|
|
|
|
|
- **1차 시도 실패**: 리셋 2줄을 완전 제거 → 무한 루프 해소, BUT **L479 회귀** (sawRunningAfterPending이 false 유지 → Discord 승인 후 AG 진행 시 auto_resolve 중복 알림 발생)
|
|
|
|
|
- **올바른 해결 (v0.3.12)**: `sawRunningAfterPending = true`만 설정. lastPendingStepIndex와 stallProbed는 유지(dedup 보호), sawRunningAfterPending=true로 auto_resolve gate 닫기. 3개 변수 모두 delta>0에서 자연 리셋
|
|
|
|
|
- **주의**: processResponseFile의 상태 리셋은 **sawRunningAfterPending = true만 설정**. lastPendingStepIndex와 stallProbed를 건드리려면 반드시 `docs/approval-flow.md`의 상태 전이 다이어그램을 확인하고, known-issues L479 회귀 및 무한 루프 시나리오를 검증할 것. 자세한 Flow는 `docs/approval-flow.md` 참조
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] recentPendingSteps 메모리 dedup — pending 파일 삭제 후 재생성 방지
|
|
|
|
|
- **증상**: Bot/Collector가 pending 파일을 삭제 → 파일 기반 dedup 통과 → 같은 step_index로 새 pending 생성
|
|
|
|
|
- **원인**: writePendingApproval()의 dedup이 파일 존재 여부에만 의존. write_response()가 pending 삭제 → dedup 무효화
|
|
|
|
|
- **해결**: `recentPendingSteps` Map (TTL 60초) 추가. `${conversationId}:${stepIndex}`를 키로 사용. 파일 삭제 후에도 메모리에서 차단. delta>0에서 해당 세션 항목 클리어
|
|
|
|
|
- **주의**: DOM observer HTTP `/pending` 경로(L738-812)는 `writePendingApproval()`을 우회하므로 이 메모리 dedup 미적용. "Run" 필터(L757)와 file_permission dedup(L786-800)이 대신 방어
|
|
|
|
|
- **주의**: AG 런타임 커맨드 미등록 문제의 근본 원인 (archive 참조: `[2026-03-09] VS Code Accept`)
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] pending 파일 무한 누적 — Collector/Bot이 로컬 파일 삭제 안 함
|
|
|
|
|
- **증상**: `bridge/pending/` 디렉토리에 56개 파일 누적 (auto_resolved 36개 + pending 20개)
|
|
|
|
|
- **원인**: `remote` 모드에서 Bot은 Gateway 서버에서 동작하여 로컬 pending 파일 직접 삭제 불가. Collector는 pending을 Gateway로 전달하지만 auto_resolved/expired 상태 파일을 로컬에서 삭제하는 로직 없음
|
|
|
|
|
- **해결**: (미완료) Collector에 로컬 pending cleanup 로직 추가 필요. 리팩토링 시 파일 기반 IPC 자체를 제거하면 근본 해결
|
|
|
|
|
- **주의**: pending 파일 누적은 Collector `_forward_pending_loop`의 매 사이클 읽기/해싱을 느리게 하여 새 pending 전달 지연 유발
|
|
|
|
|
- **증상**: `bridge/pending/` 디렉토리에 56개+ 파일 누적 (auto_resolved + pending)
|
|
|
|
|
- **원인**: `remote` 모드에서 Bot은 서버에서 동작하여 로컬 pending 파일 직접 삭제 불가
|
|
|
|
|
- **해결**: **(미완료)** WS Hub 전환으로 파일 기반 IPC 자체가 줄어들면 자연 해소. Collector 정리 로직 일부 추가됨
|
|
|
|
|
- **주의**: WS 연결 상태에서는 pending 파일을 아예 생성하지 않으므로 해당 없음
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] step_type 매핑 버그 — write_to_file이 file_permission으로 잘못 매핑
|
|
|
|
|
- **증상**: Discord에서 코드 편집 승인 시 `filePermission` RPC 전송 → 파일 읽기용 interaction이 전송됨
|
|
|
|
|
- **원인**: L2060/L2121에서 `write_to_file`, `replace_file_content`, `multi_replace_file_content`가 읽기 도구와 함께 `file_permission`으로 매핑됨. `tryApprovalStrategies`의 `AcknowledgeCascadeCodeEdit` 경로(L2907)는 `typeLower.includes('write_to_file')` 체크 → 실제 값이 `file_permission`이라 영영 도달 불가
|
|
|
|
|
- **해결**: 읽기 도구(`view_file`, `list_dir` 등)와 쓰기 도구(`write_to_file`, `replace_file_content`, `multi_replace_file_content`)를 분리. 쓰기 도구는 `code_edit` step_type 사용. `tryApprovalStrategies`에 `code_edit` 분기 추가
|
|
|
|
|
- **주의**: AG는 대부분 파일 쓰기에 WAITING을 안 만듦 (한번 승인하면 이후 자동). 실제 WAITING은 새 대화 첫 파일 접근 시에만 발생
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] diff_review isDirty 실패 — AG diff는 VS Code dirty 아님
|
|
|
|
|
- **증상**: Discord diff_review Accept 클릭 → `agentAcceptAllInFile` 실행 → `isDirty` 문서 0개 → 대상 없이 실행 → 효과 없음
|
|
|
|
|
- **원인**: AG의 stacked code review는 VS Code의 `isDirty` 상태와 무관. AG 자체 diff 시스템으로 관리되며, 파일은 이미 디스크에 저장됨
|
|
|
|
|
- **해결**: (1차) `AcknowledgeCascadeCodeEdit` RPC로 직접 protocol 수락 시도 → (2차 fallback) `openReviewChanges` 패널 열기 + 수정 파일 focus + `agentAcceptAllInFile` 실행
|
|
|
|
|
- **주의**: diff_review pending에 `modified_files` (전체 경로)와 `edit_step_indices` (step 번호) 포함 필수. `agentAcceptAllInFile`은 diff 에디터가 포커스된 상태에서만 동작할 수 있음
|
|
|
|
|
## 모니터링 중 이슈 (최근 수정, 재발 관찰 필요)
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] diff_review pending 순서 — AI 응답보다 먼저 Discord 도착
|
|
|
|
|
- **증상**: Discord에 diff_review 승인 버튼이 먼저 표시되고, AI 응답 텍스트가 나중에 도착. 유저가 승인 클릭 후에야 마지막 대화 내용을 확인할 수 있음
|
|
|
|
|
- **원인**: RUNNING→IDLE 전환 시 AI 응답 snapshot과 diff_review pending이 같은 poll tick에 생성. `pending_approval_scanner`(3초)가 `chat_snapshot_scanner`보다 먼저 fire하여 버튼이 먼저 Discord에 전달됨
|
|
|
|
|
- **해결**: diff_review pending 생성을 `setTimeout(8000)`으로 지연. 변수는 closure로 캡처 (`capturedSessionId`, `capturedStepCount`, 파일 목록). tracking 배열은 즉시 리셋
|
|
|
|
|
- **주의**: 8초는 snapshot_scanner + Collector + Gateway + Bot 전체 경로의 전파 시간을 고려한 값. 너무 짧으면 여전히 버튼이 먼저 도착
|
|
|
|
|
### [2026-03-17] Hub pending_owners 생명주기 — WS 재연결 시 승인 응답 소실
|
|
|
|
|
- **증상**: Discord에서 승인 클릭해도 AG Extension에서 아무 동작 안함
|
|
|
|
|
- **원인**: `_disconnect()`에서 Extension 연결 끊기면 `pending_owners[rid]` 삭제 → 재연결 후 owner 정보 소실
|
|
|
|
|
- **해결**: (1) 삭제 대신 같은 project 다른 연결로 재할당, (2) `_register_connection`에서 orphaned pending 재할당, (3) 같은 project 활성 연결 fallback
|
|
|
|
|
- **주의**: Gateway 모드에서 file bridge fallback은 **완전히 무용** — Docker 볼륨에 파일 작성하지만 Extension은 로컬 bridge/ 감시
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] diff_review AcknowledgeCascadeCodeEdit steps=[] — Collector pending 삭제 race
|
|
|
|
|
- **증상**: Discord에서 Accept all 클릭 → `AcknowledgeCascadeCodeEdit(steps=[])` → RPC SUCCESS `{}` 반환 → AG에서 diff review 바가 사라지지 않음 (실제 no-op)
|
|
|
|
|
- **원인**: Collector가 Gateway response를 받으면 로컬 pending 파일을 즉시 삭제 (`collector.py L259`). Extension `processResponseFile`이 pending 파일에서 `edit_step_indices`를 읽으려 할 때 파일 이미 없음 → `trackedSteps=[]`
|
|
|
|
|
- **해결**: `diffReviewMetadata` 인메모리 Map 추가 (extension.ts L57). `writePendingApproval`에서 diff_review pending 생성 시 `edit_step_indices` + `modified_files`를 메모리에 캐시. `processResponseFile`에서 메모리 먼저 조회, pending 파일은 fallback
|
|
|
|
|
- **주의**: 인메모리 캐시는 Extension Reload 시 소실됨. 그러나 diff_review pending 자체가 RUNNING→IDLE 전환 시 새로 생성되므로, 리로드 후에도 새 diff_review는 정상 동작. `bridge.py write_response()` L461의 pending 삭제도 동일 문제를 유발하지만 remote 모드에서는 Collector 경로만 해당
|
|
|
|
|
### [2026-03-17] diff_review Accept All WS 경로 regression (v0.4.5 fix)
|
|
|
|
|
- **증상**: Discord에서 Accept all 클릭해도 AG에서 반응 없음
|
|
|
|
|
- **원인**: WS 전환 시 `onResponse` 핸들러에 `diff_review` step_type 분기 누락
|
|
|
|
|
- **해결**: `handleDiffReviewResponse()` export + WS `onResponse`에서 호출
|
|
|
|
|
- **주의**: **WS 경로 추가 시 file-bridge 경로의 모든 분기를 반드시 포팅할 것**
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] AcknowledgeCascadeCodeEdit SUCCESS → diff review bar 미해제 — **잘못된 RPC 메서드명**
|
|
|
|
|
- **증상**: `AcknowledgeCascadeCodeEdit(session=xxx, accept=true, steps=[40])` → `SUCCESS: {}` 반환 → AG diff review bar 여전히 표시됨
|
|
|
|
|
- **원인**: **RPC 메서드명 자체가 틀렸음.** AG 내부 LS 메서드는 `AcknowledgeCascadeCodeEdit`가 아닌 **`acknowledgeCodeActionStep`**(proto ID 167). AG 소스 역분석으로 확인. 그러나 `acknowledgeCodeActionStep`도 ConnectRPC 404 반환 (LS의 `exa.language_server_pb.LanguageServerService`에 미등록). `submitCodeAcknowledgement` VS Code 커맨드도 런타임 미등록
|
|
|
|
|
- **해결**: ~~v0.3.14 3단계 RPC 전략~~ 모두 실패. **v0.3.15**: `agentAcceptAllInFile` / `agentRejectAllInFile` VS Code 커맨드 사용 (런타임에 등록 확인됨). `openReviewChanges` → 파일별 포커스 → 커맨드 실행
|
|
|
|
|
- **주의**: AG의 RPC는 잘못된 메서드명도 에러 없이 `{}` 반환함 (proto unknown field). `AcknowledgeCascadeCodeEdit`가 `{}` 반환 → `diffReviewDone=true` 설정 → 실제 동작하는 전략 차단이 핵심 버그였음. **RPC `{}`는 실패로 간주해야 함**
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] diff_review RPC 3개 전략 모두 dead-end — VS Code 커맨드만 유효
|
|
|
|
|
- **증상**: v0.3.14 E2E 테스트에서 3개 RPC 전략 모두 실패, diff review bar 해제 안 됨
|
|
|
|
|
- **원인**: (1) `submitCodeAcknowledgement`: AG 런타임 119개 명령에 미등록, (2) `acknowledgeCodeActionStep`: ConnectRPC 404 (LS 서비스에 미등록), (3) `AcknowledgeCascadeCodeEdit`: `{}` no-op 반환 후 `diffReviewDone=true` → Strategy 2 차단
|
|
|
|
|
- **해결**: v0.3.15에서 dead-end RPC 3개 제거. `agentAcceptAllInFile` / `agentRejectAllInFile` (런타임 등록 확인) + `openReviewChanges` + 파일별 포커스 방식으로 전환. Hunk 단위 fallback 추가 (`agentAcceptFocusedHunk`)
|
|
|
|
|
- **주의**: diff_review Accept/Reject를 RPC로 해결하려는 시도 **모두 실패 확정**. VS Code 커맨드 기반만 유효. 단, 이 커맨드도 diff editor 포커스 의존성 있을 수 있음 — 재시작 후 E2E 검증 필요
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] AG 소스 역분석 — diff review 내부 동작 체인
|
|
|
|
|
- **증상**: Accept all / Reject all 버튼의 내부 동작을 Extension에서 프로그래밍 방식으로 재현해야 함
|
|
|
|
|
- **원인**: AG 공식 API/문서 없음. VS Code 커맨드 중 많은 것이 미등록 상태
|
|
|
|
|
- **해결**: AG 설치 경로의 JS 소스에서 직접 역분석. 핵심 발견:
|
|
|
|
|
- **LS 메서드**: `acknowledgeCodeActionStep` (proto ID 167, 메시지 타입 `vKa`/`QLc`) — 단, ConnectRPC 404
|
|
|
|
|
- **VS Code 커맨드**: `antigravity.prioritized.submitCodeAcknowledgement` — 미등록
|
|
|
|
|
- **실제 동작 커맨드**: `antigravity.prioritized.agentAcceptAllInFile` / `agentRejectAllInFile` (런타임 등록 확인)
|
|
|
|
|
- **Accept all 체인**: UI 클릭 → `fireEvent({type:"accept-all-in-file"})` → 내부 처리
|
|
|
|
|
- **파일 위치**: `%LOCALAPPDATA%\Programs\Antigravity\resources\app\out\jetskiAgent\main.js` (LS 메서드 정의), `vs\workbench\workbench.desktop.main.js` (UI 커맨드 + 핸들러)
|
|
|
|
|
- **주의**: Python `re.finditer`로 대용량 JS 검색 시 PowerShell `IndexOf`보다 안정적. minified JS에서 변수명(`vKa`, `QLc` 등)은 버전마다 변경됨
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] diff_review 이중 승인 요청 — DOM observer가 Accept/Reject 버튼 캡처
|
|
|
|
|
- **증상**: Discord에 diff_review 승인 요청(Accept all/Reject all)과 별도로 "Accept" / "Reject" 단독 pending이 1건 더 도착
|
|
|
|
|
- **원인**: diff_review 처리 시 `openReviewChanges` 커맨드가 diff UI 패널을 열면 Accept/Reject 버튼이 DOM에 렌더링됨. DOM observer가 이 버튼을 감지하여 별도 pending 생성 (`cmd="Accept" btns=2 ctx="RejectShift+Alt+⌫"`)
|
|
|
|
|
- **해결**: `FALSE_POSITIVE_RE`에 `Accept|Reject|Accept all|Reject all` 추가 (v0.3.16)
|
|
|
|
|
- **주의**: diff review bar 버튼은 전용 `diff_review` pending 시스템에서 처리하므로 DOM observer가 별도로 캡처할 필요 없음
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] !auto 이중 메시지 — Extension echo + Bot embed
|
|
|
|
|
- **증상**: `!auto` 토글 시 Discord에 Auto 모드 변경 메시지가 2개 표시 (Bot embed + Extension echo)
|
|
|
|
|
- **원인**: Bot이 embed를 전송하고 + `!auto on/off` command를 Extension에 전달 → Extension이 `writeChatSnapshot()`으로 확인 메시지를 다시 Discord에 전송 → Bot의 `chat_snapshot_scanner`가 이것을 읽어 또 전송
|
|
|
|
|
- **해결**: Extension의 `!auto` command handler에서 `writeChatSnapshot()` echo 제거 (v0.3.16). Bot의 embed 1개만 표시
|
|
|
|
|
- **주의**: Extension 측 `autoApproveEnabled` 변수는 여전히 정상 토글됨 (로그에 기록). 단, Bot 재시작 시 `auto_approve_projects` set은 초기화되므로 Extension은 auto 상태여도 Bot이 auto-approve를 실행하지 않음 → 사실상 수동 모드로 복귀
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] 병렬 WAITING step 누락 — step_probe break문
|
|
|
|
|
- **증상**: AG가 병렬 tool call(예: run_command 3개)를 동시 실행 시 Discord에 1개만 승인 요청 도착, 나머지는 AG UI에만 표시
|
|
|
|
|
- **원인**: `extension.ts` step_probe 루프(L2082-2134)에서 `break`문이 첫 번째 WAITING step 처리 후 루프 종료. 나머지 병렬 WAITING step은 pending 생성 안 됨. offset retry 루프(L2029-2071)에도 동일 `break` 존재
|
|
|
|
|
- **해결**: `break` 제거하여 모든 WAITING step에 pending 생성. `lastPendingStepIndex`는 가장 큰 index로만 갱신. 중복 방지는 `writePendingApproval`의 `recentPendingSteps` Map이 처리
|
|
|
|
|
- **주의**: 병렬 pending이 동시에 생성되면 Bot이 여러 auto-approve를 거의 동시에 처리. Collector의 polling 주기(5초)와 Extension의 response watcher(3초) 사이 타이밍 이슈 가능
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] Bot chat_snapshot 전송 성공/실패 로깅 부재
|
|
|
|
|
- **증상**: Discord에 chat snapshot이 전달됐는지 Bot 로그에서 확인 불가. 전송 성공 시 아무 로그 없음
|
|
|
|
|
- **원인**: `bot.py` `chat_snapshot_scanner`(L895-988)에서 `channel.send()` 성공 후 INFO 로그 없음. 실패 시 `discord.NotFound`만 catch하고 일반 Exception은 catch 안 함
|
|
|
|
|
- **해결**: 전송 성공 시 `[SNAPSHOT] Sent to #채널명 (inline/file, N chars)` INFO 로그 추가. `Exception` catch도 추가하여 `[SNAPSHOT] Discord send failed` ERROR 로그 출력
|
|
|
|
|
- **주의**: `_get_channel()` 실패 시 `[SNAPSHOT] No Discord channel` WARNING은 이전에도 있었으나, channel이 정상이면 아무 단서 없었음
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] 크로스 프로젝트 이벤트 폭주 — Watcher/Collector 무필터
|
|
|
|
|
- **증상**: /start 실행 시 gravity_control 채널에 variet_agent, lifetimepd, deriva 등 타 프로젝트 알림 유입. 6개 프로젝트 42개 세션의 file_changed가 모두 전달됨
|
|
|
|
|
- **원인**: `watcher.py`가 brain/ 전체를 recursive 감시하면서 모든 세션의 .md 변경을 이벤트로 방출. `collector.py` `_forward_events_loop`도 프로젝트 필터 없이 모든 이벤트를 Gateway로 전달
|
|
|
|
|
- **해결**: (1) `watcher.py`에 `_is_my_session()` 필터 추가 — register/ 파일 기반으로 MY 프로젝트 세션만 이벤트 방출. (2) `collector.py`에 `_get_session_project()` + 이벤트 전달 필터 추가
|
|
|
|
|
- **주의**: 미등록 세션(새로 시작된 대화)은 allow-through 방식. register 파일은 60초 캐시로 I/O 절감
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] pending 파일 139개 누적 — 정리 로직 부재
|
|
|
|
|
- **증상**: `bridge/pending/` 디렉토리에 auto_resolved 109개 + pending 30개 = 139개 파일 누적. 새 pending 전달 지연
|
|
|
|
|
- **원인**: Extension이 `auto_resolved`/`expired`로 상태 변경한 pending 파일을 아무도 삭제 안 함. Bot은 gateway 모드라 로컬 파일 접근 불가, Collector는 response가 온 pending만 삭제
|
|
|
|
|
- **해결**: `collector.py` `_forward_pending_loop`에서: (1) auto_resolved/expired 상태 → Gateway 전달 후 즉시 로컬 삭제, (2) 10분 이상된 forwarded pending 주기적 자동 삭제
|
|
|
|
|
- **주의**: startup_pending은 정리 대상에서 제외 (mtime 기반 변경 감지가 필요하므로)
|
|
|
|
|
|
|
|
|
|
### [2026-03-16] diff_review가 brain/ artifact에도 트리거 — task.md "코드 리뷰"
|
|
|
|
|
- **증상**: AI가 task.md만 수정한 경우에도 "코드 리뷰: task.md" pending이 생성되어 사용자 혼동
|
|
|
|
|
- **원인**: Extension의 diff_review 감지 로직이 write_to_file/replace_file_content로 수정된 **모든** 파일을 추적. brain/ 경로의 task.md, implementation_plan.md 등 AG 내부 artifact도 포함됨
|
|
|
|
|
- **해결**: `extension.ts` diff_review 감지 시 `.gemini/antigravity/brain/` 경로 파일을 필터링하여 제외. 코드 파일만 남으면 diff_review 생성, brain artifact만이면 skip
|
|
|
|
|
- **주의**: 코드 파일 + brain artifact가 혼합된 경우에는 코드 파일에 대해서만 diff_review 생성
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] NPM WebSocket 프록시 — Upgrade 헤더 미전달
|
|
|
|
|
- **증상**: `wss://ag.variet.net/ws` 연결 시 HTTP 400 `No WebSocket UPGRADE hdr: None`
|
|
|
|
|
- **원인**: Nginx Proxy Manager(openresty) 프록시 호스트에 WebSocket Support 미활성화 → `Upgrade` 헤더가 백엔드(aiohttp)로 전달되지 않음
|
|
|
|
|
- **해결**: NPM 대시보드 → Proxy Hosts → `ag.variet.net` → Edit → **Websockets Support** 체크 → Save
|
|
|
|
|
- **주의**: NPM은 WebSocket 토글을 켜야 `proxy_set_header Upgrade $http_upgrade` + `proxy_set_header Connection $connection_upgrade`가 자동 추가됨. 새 프록시 호스트 생성 시 반드시 확인
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] Extension ws 모듈 미번들 — WS 연결 실패 가능
|
|
|
|
|
- **증상**: Extension WS 연결 시도 시 `[WS] WebSocket module not available` → 파일 fallback만 동작
|
|
|
|
|
- **원인**: `ws` npm 패키지가 `.vscodeignore`의 `node_modules/**`에 의해 VSIX에서 제외됨. AG Electron에 `ws` 미포함. `globalThis.WebSocket`은 Node.js 22+에서만 사용 가능
|
|
|
|
|
- **해결**: (1) `.vscodeignore`에 `!node_modules/ws/**` 추가하여 VSIX에 ws 번들, (2) 설치 후 수동으로 `node_modules/ws/`를 설치 경로에 복사
|
|
|
|
|
- **주의**: `package.json`에 `ws` dependency 추가 후 `npx vsce package` 시에도 `.vscodeignore`의 `!node_modules/ws/**` 없으면 번들 안 됨. `--no-dependencies` 플래그와 무관
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] WS Auth 실패 — 워크스페이스 settings 빈 값 override
|
|
|
|
|
- **증상**: Extension이 Hub에 연결 성공하지만 `Auth failed: Auth required` 반복. `registrationCode`가 항상 empty
|
|
|
|
|
- **원인**: `.vscode/settings.json`에 `"gravityBridge.registrationCode": ""`(빈 문자열)이 설정되어, 유저 설정(`%APPDATA%/Antigravity/User/settings.json`)의 올바른 값을 워크스페이스 설정이 **덮어쓰기**. VS Code 설정 우선순위: workspace > user
|
|
|
|
|
- **해결**: `.vscode/settings.json`의 `registrationCode`에 올바른 값 설정
|
|
|
|
|
- **주의**: VS Code `config.get()`은 워크스페이스에 빈 문자열이 있으면 유저 설정의 값을 무시함. 빈 값도 "설정됨"으로 취급. `hubUrl`은 워크스페이스에 정상 값이라 동작했지만 `registrationCode`만 빈 값이라 불일치 발생
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] WS auth_fail 무한 재연결 — _cleanup() close 이벤트
|
|
|
|
|
- **증상**: `Auth failed` 후 "Don't reconnect" 주석에도 불구하고 60초마다 재연결 반복
|
|
|
|
|
- **원인**: `_cleanup()`이 `ws.close()` → close 이벤트 → `_onDisconnect()` → `_scheduleReconnect()` 체인 트리거. `shouldReconnect`를 false로 설정하지 않아 재연결 차단 안 됨
|
|
|
|
|
- **해결**: `auth_fail` 핸들러에서 `this.shouldReconnect = false`를 `_cleanup()` **이전에** 설정
|
|
|
|
|
- **주의**: `_cleanup()`은 WS를 닫으면서 이벤트 핸들러를 트리거하므로, 상태 변경은 반드시 `_cleanup()` 호출 전에 수행할 것
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] initStepProbe workspaceUri 누락 — 세션 감지 완전 불능
|
|
|
|
|
- **증상**: POLL alive만 출력되고 SESSION-FILTER/SNAPSHOT 전혀 없음. Discord에 아무것도 도착 안 함
|
|
|
|
|
- **원인**: `initStepProbe()` 호출 시 `workspaceUri`와 `diffReviewMetadata` 미전달. `as BridgeContext` 캐스트가 누락 필드를 무시. `ctx.workspaceUri.replace()`에서 매 5초 TypeError. 에러가 `console.log`로만 기록되어 extension.log에 안 보임
|
|
|
|
|
- **해결**: `extension.ts` L1131에 `workspaceUri`, `diffReviewMetadata: new Map()` 추가
|
|
|
|
|
- **주의**: TypeScript `as` 캐스트는 런타임 검증 없음. 필수 필드 누락도 컴파일 에러 없이 통과. 에러 핸들러가 `ctx.logToFile` 대신 `console.log` 사용하면 extension.log에 안 남음
|
|
|
|
|
### [2026-03-17] _auto_approve_via_hub 이중 쓰기 (v0.4.5 fix)
|
|
|
|
|
- **증상**: auto-approve 시 Extension에 응답이 2번 도착
|
|
|
|
|
- **원인**: Hub WS 전송 후 `return` 없이 file bridge에도 씀
|
|
|
|
|
- **해결**: `if self.hub:` → WS 전송 + `else:` → file bridge. if/else 구조로 변경
|
|
|
|
|
- **주의**: Hub WS와 file bridge는 **항상 상호 배타적**이어야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] WS dual-write — 채팅/승인 이중 발송
|
|
|
|
|
- **증상**: Discord 메시지가 2개씩 도착 (동일 내용)
|
|
|
|
|
- **원인**: `writeChatSnapshot`/`writePendingApproval`이 WS 전송 후 파일도 동시에 씀 (Phase 0 dual-write). Watcher가 파일도 읽어서 Discord로 보냄 → 같은 메시지 2번 전달
|
|
|
|
|
- **해결**: WS 연결 시 `return`으로 파일 쓰기 건너뛰기. 파일은 fallback 전용으로 변경
|
|
|
|
|
- **증상**: Discord 메시지가 2개씩 도착
|
|
|
|
|
- **원인**: `writeChatSnapshot`/`writePendingApproval`이 WS 전송 후 파일도 동시에 씀
|
|
|
|
|
- **해결**: WS 연결 시 `return`으로 파일 쓰기 건너뛰기
|
|
|
|
|
- **주의**: `writePendingApproval` 수정 시 diff_review metadata 캐싱 + memory dedup 로직이 WS 경로에서도 실행되어야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] Hub WS auto-approve Discord 알림 누락
|
|
|
|
|
- **증상**: auto-approve 활성화 상태에서 승인 요청이 자동 처리되지만 Discord에 아무 알림 없음 (텍스트만 전달)
|
|
|
|
|
- **원인**: `_auto_approve_via_hub()` (WS 경로)에서 Discord embed 미전송. 파일 기반 auto-approve (L617)은 "🤖 자동 승인됨" embed 전송하지만 Hub WS 경로 누락
|
|
|
|
|
- **해결**: `_auto_approve_via_hub()`에 Discord embed 전송 추가 (파일 경로와 동일 포맷)
|
|
|
|
|
- **주의**: bot.py는 서버에서 실행됨. 로컬 수정 후 반드시 git push → 서버 pull + 재시작
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] Hub WS ApprovalRequest 생성 누락 필드 — 모든 승인 실패
|
|
|
|
|
- **증상**: Hub WS로 전달된 모든 pending 승인 요청이 Discord에 표시되지 않음 (채팅은 정상)
|
|
|
|
|
- **원인**: `_hub_on_pending()`에서 `ApprovalRequest()` 생성 시 `conversation_id`와 `timestamp` 필수 인자 누락 → TypeError. `try/except`에 잡혀 로그만 남고 무시
|
|
|
|
|
- **해결**: `data.get("conversation_id", "")`, `data.get("timestamp", time.time())` 추가
|
|
|
|
|
- **주의**: 서버 로그(`docker logs gravity-gateway`)에서만 에러 확인 가능. 로컬 extension.log에는 `[PENDING-WS] sent` 성공으로 보임. **서버 배포 후 반드시 서버 로그로 검증할 것**
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] ApprovalView Hub WS 응답 미전송 — Discord 승인 클릭 무효
|
|
|
|
|
- **증상**: Discord에서 승인/거부 버튼을 클릭해도 AG에 전달되지 않음. AG가 계속 대기 상태
|
|
|
|
|
- **원인**: `ApprovalView` 버튼 콜백이 `bridge.write_response()`만 호출 → Docker 컨테이너 내부 파일시스템에만 기록. Hub WS로 연결된 Extension은 이 파일을 읽을 수 없음
|
|
|
|
|
- **해결**: `ApprovalView`에 `hub` 파라미터 추가. 모든 버튼 콜백에서 `hub.send_response_to_pending_owner()` 호출하여 WS로 Extension에 직접 전달
|
|
|
|
|
- **주의**: ~~파일 기반 로컬 모드도 여전히 동작 (fallback). Hub가 None이면 파일만 사용. 양쪽 모두 호출하는 dual-write 방식~~ → **v0.4.4에서 수정**: Hub 연결 시 file bridge 스킵 (이전 dual-write가 이중 전달의 원인이었음)
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] WS 응답 파일 경유 — 자동승인이 AG에 안 도착
|
|
|
|
|
- **증상**: Discord "🤖 자동 승인됨" 표시되지만 AG는 계속 대기. Hub WS 응답이 Extension에 도착해도 AG가 진행 안 함
|
|
|
|
|
- **원인**: Extension `onResponse`가 WS 응답을 `response/{rid}.json` 파일로 쓴 뒤 `processResponseFile`이 읽는 구조. WS pending 경로는 `pending/{rid}.json` 파일을 안 쓰므로 `processResponseFile`이 `sessionId`/`stepIndex` 메타데이터를 못 찾아 `tryApprovalStrategies` 실패
|
|
|
|
|
- **해결**: `onResponse`에서 파일 경유 제거, `tryApprovalStrategies()` 직접 호출. `getApprovalContext()`/`resetPendingState()` export
|
|
|
|
|
- **주의**: WS 경로와 파일 경로의 pending/response 생명주기가 완전히 다름. WS pending은 파일 없음 → response 처리도 파일 없이 해야 함
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] WS+File Dual-Write — 명령어/응답 이중 전달
|
|
|
|
|
- **증상**: Discord 메시지가 AG에 2번 전달, 승인 응답 처리 후 새 승인 요청이 도착하지 않음
|
|
|
|
|
- **원인**: `bot.py:_write_command()`와 `ApprovalView` 콜백이 Hub WS **AND** file bridge 양쪽으로 동시 전송. Extension에서 WS callback + file watcher 양쪽으로 수신하여 `sendPromptToAgentPanel` 2회 실행. 응답도 `resetPendingState()`와 `processResponseFile()`가 동시 실행되어 state 충돌
|
|
|
|
|
- **해결**: Hub 연결 시 `return`으로 file bridge 스킵 (명령어, 응답 모두). `else` 분기로 fallback 처리
|
|
|
|
|
- **주의**: **Hub WS 연결 시 file bridge는 완전히 비활성화**해야 함. "양쪽 모두 쓰기"는 이중 전달의 근본 원인
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] WS 명령어 에코 릴레이 — Discord 메시지 2번 표시
|
|
|
|
|
- **증상**: Discord에서 메시지 입력 → 같은 메시지가 "👤 사용자 (AG 직접 입력)"으로 다시 Discord에 표시
|
|
|
|
|
- **원인**: `handleWSCommand`에서 AG에 텍스트 전달 시 `recentDiscordSentTexts`에 마킹하지 않음 → step-probe가 USER_INPUT step 감지 시 Discord 발신 메시지를 새 사용자 입력으로 오인하여 릴레이
|
|
|
|
|
- **해결**: `handleWSCommand`에 `ctx.recentDiscordSentTexts.set(text.trim(), Date.now())` 추가
|
|
|
|
|
- **주의**: 파일 기반 `_processCommandFile`에는 이미 마킹 있었음 (L178). WS 경로 추가 시 동일 패턴 적용 필수
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] writeRegistration 이중 쓰기 — WS 전송 후 파일도 작성
|
|
|
|
|
- **증상**: WS 연결 상태에서도 `bridge/register/` 디렉토리에 등록 파일이 계속 생성됨
|
|
|
|
|
- **원인**: `writeRegistration()` (step-probe.ts L162)이 WS 전송 후 `return` 없이 파일 쓰기도 실행. `writeChatSnapshot`/`writePendingApproval`은 WS→return 패턴인데 `writeRegistration`만 누락
|
|
|
|
|
- **해결**: WS 전송 후 `return` 추가하여 파일 쓰기 건너뛰기 (v0.4.5)
|
|
|
|
|
- **주의**: 새 WS 전송 함수 추가 시 반드시 file fallback과 상호 배타적 `return` 확인
|
|
|
|
|
|
|
|
|
|
### [2026-03-17] ApprovalView Hub 응답 실패 시 silent loss — fallback 없음
|
|
|
|
|
- **증상**: Extension이 pending 생성 후 WS 연결 끊김 → Discord에서 승인 클릭 → AG에 전달 안 됨 (무한 대기)
|
|
|
|
|
- **원인**: `ApprovalView` approve/reject/choice 콜백이 `if self.hub:` → `send_response_to_pending_owner()` 실행. 반환값 미확인. Hub가 있으면 file bridge를 아예 건너뛰므로 Hub 전달 실패 시 응답 소실
|
|
|
|
|
- **해결**: `delivered = await hub.send_response_to_pending_owner()` 반환값 확인 → `False`면 `bridge.write_response()` fallback (4곳 + `_auto_approve_via_hub` 1곳, 총 5곳)
|
|
|
|
|
- **주의**: `send_response_to_pending_owner`는 `pending_owners`에 conn_id가 없으면 `False` 반환 (Extension 재연결/disconnect 시). Hub 존재 ≠ 전달 성공
|
|
|
|
|
- **증상**: Extension이 pending 생성 후 WS 연결 끊김 → Discord 승인 클릭 → AG에 전달 안 됨
|
|
|
|
|
- **원인**: `ApprovalView`가 Hub 전달 실패 시 file bridge를 건너뜀
|
|
|
|
|
- **해결**: `send_response_to_pending_owner()` 반환값 확인 → `False`면 file bridge fallback (5곳)
|
|
|
|
|
- **주의**: `send_response_to_pending_owner`는 `pending_owners`에 conn_id가 없으면 `False` 반환. Hub 존재 ≠ 전달 성공
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 핵심 작업 규칙 (과거 이슈에서 반복된 패턴)
|
|
|
|
|
|
|
|
|
|
> 아래는 과거 이슈에서 반복적으로 나타난 패턴을 규칙으로 정리한 것입니다.
|
|
|
|
|
|
|
|
|
|
| # | 규칙 | 관련 이슈 (archive 참조) |
|
|
|
|
|
|---|------|--------------------------|
|
|
|
|
|
| 1 | **Hub WS와 file bridge는 상호 배타적** — `if hub: ws + return` / `else: file` | WS dual-write, _auto_approve 이중 쓰기 |
|
|
|
|
|
| 2 | **WS 경로 추가 시 file-bridge의 모든 분기를 포팅** | diff_review WS regression |
|
|
|
|
|
| 3 | **AG RPC `{}` 응답은 실패로 간주** — 메서드명 틀려도 에러 없이 `{}` 반환 | AcknowledgeCascadeCodeEdit |
|
|
|
|
|
| 4 | **ResolveOutstandingSteps는 CANCEL 동작** — 승인에 절대 사용 금지 | Step probe reject |
|
|
|
|
|
| 5 | **Extension 코드 수정 후 반드시 VSIX 빌드 + AG 풀 재시작** | Extension 버전 미배포 |
|
|
|
|
|
| 6 | **HTML 패치 변경 시 V8 CachedData 삭제 필수** | V8 CachedData, CSP |
|
|
|
|
|
| 7 | **`bridge/pending/` 조작 시 반드시 `project_name` + `conversation_id` 필터** | 크로스 프로젝트 DEDUP MERGE |
|
|
|
|
|
| 8 | **`processResponseFile` 상태 리셋은 `sawRunningAfterPending=true`만** | processResponseFile 무한 루프 |
|
|
|
|
|
| 9 | **fs.watch Windows 불안정 — 반드시 polling fallback 병행** | fs.watch silent fail |
|
|
|
|
|
| 10 | **diff_review는 VS Code 커맨드만 유효** — RPC 3개 전략 모두 실패 확정 | diff_review RPC dead-end |
|
|
|
|
|
|