fix(extension): Retry auto-approve 흐름 복구 + Observer 형제 탐색 + thinking 필터링 (v0.5.79)
- WS response 파일에 _from_ws 마커 추가하여 processResponseFile 삭제 방지 - extractContextFromNearby에 sibling 탐색 추가 (AG Native DOM 구조 대응) - thinking 블록 (max-h-[200px]) 필터링으로 내부 사고 릴레이 차단 - DOM 탐색 depth 5→10 확대 + pre.font-mono 우선 탐색 - 사용자 메시지 셀렉터 (.select-text.rounded-lg) 추가
This commit is contained in:
@@ -21,6 +21,44 @@
|
||||
> 鍮꾩듂븳 臾몄젣媛 옱諛쒗븯硫 archive뿉꽌 寃깋븯꽭슂.
|
||||
|
||||
|
||||
### [2026-04-18] [Extension] ★ WS response 파일이 processResponseFile에 의해 삭제 → Observer pollResponseGroup 실패 (v0.5.78)
|
||||
- **증상**: `!auto` Retry가 작동하지 않음. Observer가 `/response/{rid}`를 폴링하지만 항상 `{waiting: true}` 반환.
|
||||
- **원인**: extension.ts의 WS 응답 핸들러가 `response/{rid}.json` 파일 작성 → 300ms 후 response watcher(`processResponseFile`)가 파일 감지 → pending 파일이 없어 `isDomObserver=false` → `fs.unlinkSync()` 실행 → Observer가 폴링할 때 파일이 이미 삭제됨.
|
||||
- **해결 (v0.5.78)**: WS 응답 파일에 `_from_ws: true` 마커 추가. `processResponseFile`에서 `_from_ws` 파일 스킵 (WS 핸들러에서 이미 `tryApprovalStrategies` 실행하므로 중복 방지도 함께 해결).
|
||||
- **주의**: http-bridge의 `_handlePending`는 pending 파일을 생성하지 않음 (WS 전송만 수행). 따라서 `processResponseFile`의 `isDomObserver` 판별이 실패함. WS 경로로 들어오는 모든 response는 반드시 마커로 구분해야 함.
|
||||
|
||||
### [2026-04-18] [Extension] ★ extractContextFromNearby 조상 탐색만으로는 명령어 추출 불가 (v0.5.79)
|
||||
- **증상**: Discord auto-approve 알림에 "Always run"만 표시되고 실제 명령어가 안 보임. depth를 10으로 늘려도 동일.
|
||||
- **원인**: AG Native DOM 구조에서 "Always run" 버튼은 `footer` 내부에 있고, 실제 명령어(`pre.font-mono`)는 `footer`의 **형제(sibling)** 요소에 있음. 조상 탐색(parentElement)으로는 도달 불가. trail: `d0:button → d1:div → d2:footer` (footer.parentElement가 null).
|
||||
- **해결 (v0.5.79)**: `extractContextFromNearby`에 형제 탐색 로직 추가. 각 depth에서 `node.parentElement.children`을 순회하며 `pre.font-mono, pre, code`를 찾음 → `CONTEXT-OK src=sibling` 성공.
|
||||
- **주의**: Observer 코드 변경은 HTML 인라인 스크립트이므로 AG 2번 재시작(Quit + Relaunch × 2) 필요.
|
||||
|
||||
### [2026-04-18] [Extension] Thinking 블록이 AI 응답으로 릴레이됨
|
||||
- **증상**: AI의 내부 사고 과정(thinking/reasoning)이 Discord에 릴레이됨.
|
||||
- **원인**: Observer의 `scanChatBodies`가 `.leading-relaxed.select-text` 블록을 전부 캡처하는데, thinking 블록도 이 셀렉터에 매칭됨.
|
||||
- **해결**: thinking 블록의 조상에 `max-h-[200px]` 클래스가 있는지 확인하여 필터링.
|
||||
- **주의**: AG UI 업데이트로 thinking 블록의 클래스가 변경될 수 있음.
|
||||
|
||||
|
||||
- **증상**: Step Probe의 RT-CAPTURE, HB-CAPTURE가 현재 대화 중에 전혀 발동하지 않음. POLL에서 `status=IDLE, steps=928, delta=0` 고정. Heartbeat probe에서도 `real=928 known=928` 불변.
|
||||
- **원인**: AG Native의 `GetCascadeTrajectorySteps` API는 **cascade가 완전히 종료(IDLE 전환)된 후에만** 새 step을 반환합니다. 진행 중인 cascade에서는 step count가 동결됩니다. `GetAllCascadeTrajectories`의 `stepCount`도 마찬가지.
|
||||
- **영향**: Step Probe 기반의 모든 실시간 캡처(RT-CAPTURE, HB-CAPTURE, USER_INPUT 감지)가 **구조적으로 불가능**. Observer DOM이 유일한 실시간 데이터 경로.
|
||||
- **해결**: Observer DOM 기반 chat relay를 재활성화 (v0.5.72). Step Probe는 cascade 완료 후 보완 용도로만 사용.
|
||||
- **주의**: 이 제약은 AG Native 아키텍처의 근본적 특성. Polling 주기나 heartbeat 빈도를 변경해도 해결 불가. **실시간 릴레이는 반드시 Observer DOM 경로를 사용해야 함.**
|
||||
- **참조**: `.agents/references/relay-architecture.md` (상세 분석)
|
||||
|
||||
### [2026-04-18] [Extension] Observer의 /pending POST에 명령어 컨텍스트가 없음 (Always run만 전달)
|
||||
- **증상**: Discord auto-approve 알림에 "Always run"만 표시되고 실제 명령어가 안 보임.
|
||||
- **원인**: Observer가 `/pending` POST 시 `command: "Always run"`, `description: "Always run"`만 보냄. `extractContextFromNearby(btn)`이 버튼 주변 DOM에서 유의미한 텍스트를 찾지 못함.
|
||||
- **해결 (부분)**: v0.5.69에서 bridge/pending/ 디렉토리의 최신 Step Probe pending 파일에서 명령어를 읽는 fallback 추가. Step Probe pending이 있을 때만 작동.
|
||||
- **주의**: Step Probe WAITING 감지가 진행 중 cascade에서 불가하므로 (위 이슈 참조), 현재 대부분의 경우 fallback도 실패. Observer의 DOM 컨텍스트 추출 개선이 필요.
|
||||
|
||||
### [2026-04-18] [Extension] Observer의 사용자 메시지 셀렉터 미매칭
|
||||
- **증상**: 사용자가 AG에서 입력한 메시지가 Discord에 전혀 전달되지 않음.
|
||||
- **원인**: Observer의 셀렉터(`.text-ide-message-block-user-color`, `[data-message-role="user"]` 등)가 AG Native DOM에서 매칭되지 않음. AI 응답만 `.leading-relaxed.select-text`로 매칭됨.
|
||||
- **해결**: DOM 덤프에서 사용자 메시지 블록의 실제 CSS 클래스를 식별 후 셀렉터 추가 필요.
|
||||
- **주의**: Step Probe의 USER_INPUT 캡처도 진행 중 cascade에서 불가 (위 이슈 참조).
|
||||
|
||||
### [2026-04-16] [Extension] ★ AG Native 세션 AI 응답이 Discord에 CSS로 전달됨 (v0.5.52 수정, #632)
|
||||
- **증상**: Discord에 AI 대화 응답 대신 **CSS 스타일시트 코드** (`remark-github-blockquote-alert/alert.css`)가 전달됨. `scanChatBodies()` → POST /chat 경로는 작동하지만 내용이 CSS.
|
||||
- **원인**: `extractCleanStepText()`에서 clone한 DOM에서 버튼/SVG/아이콘은 제거하지만 **`<style>` 태그는 제거하지 않음**. AG Native 마크다운 렌더러가 `.leading-relaxed.select-text` 내부에 `<style>` 블록을 주입하는데, 이 CSS textContent가 AI 응답 텍스트로 추출됨.
|
||||
|
||||
207
.agents/references/relay-architecture.md
Normal file
207
.agents/references/relay-architecture.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# AG Native 릴레이 아키텍처 분석
|
||||
|
||||
> **이 문서는 AG Native ↔ Discord 릴레이의 데이터 흐름 SSOT입니다.**
|
||||
> 구현/디버깅 전 반드시 확인합니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 데이터 경로 요약
|
||||
|
||||
AG Native에서 Discord로 메시지를 전달하는 경로는 크게 2개:
|
||||
|
||||
| # | 경로 | 소스 | 실시간? | 상태 |
|
||||
|---|------|------|---------|------|
|
||||
| 1 | **Observer DOM** | workbench.html 인라인 스크립트 → DOM 관찰 → HTTP POST → http-bridge | ✅ 실시간 | AI 응답: ✅ 작동 (v0.5.72+), 사용자 메시지: ✅ 작동 (v0.5.74+) |
|
||||
| 2 | **Step Probe (trajectory API)** | LS RPC `GetCascadeTrajectorySteps` → step 분석 | ❌ cascade 완료 후에만 | AI 응답: ❌ 실시간 불가, 사용자 메시지: ❌ 실시간 불가 |
|
||||
|
||||
### 1.1 핵심 API 제약 (2026-04-18 확인)
|
||||
|
||||
> [!CAUTION]
|
||||
> **`GetCascadeTrajectorySteps`는 진행 중인 cascade의 step을 실시간으로 반환하지 않습니다.**
|
||||
> step count는 cascade가 **완전히 종료**(IDLE 전환)된 후에만 업데이트됩니다.
|
||||
> 따라서 Step Probe의 RT-CAPTURE, HB-CAPTURE 모두 **현재 진행 중인 대화에서는 작동하지 않습니다.**
|
||||
|
||||
**검증 데이터**:
|
||||
- POLL에서 `status=CASCADE_RUN_STATUS_IDLE`, `steps=928`, `delta=0` 고정
|
||||
- HEARTBEAT probe: `offset=927 got=1 real=928 known=928` → 변함 없음
|
||||
- 실제로 수십 개의 tool call이 실행되었지만 step count 불변
|
||||
- Cascade 종료 후 다음 poll에서 step count가 점프 (예: 733 → 865 → 928)
|
||||
|
||||
### 1.2 AG Native SDK EventMonitor
|
||||
|
||||
SDK에 이벤트 시스템이 있으나 **모두 polling 기반**:
|
||||
- `EventMonitor.onStepCountChanged` — getDiagnostics 기반 polling
|
||||
- `EventMonitor.onActiveSessionChanged` — state.vscdb 기반 polling
|
||||
- **실시간 push (WebSocket/SSE)는 없음**
|
||||
- 현재 상태: ERR_CONNECTION_REFUSED 문제로 비활성화됨
|
||||
|
||||
---
|
||||
|
||||
## 2. Observer DOM 경로 상세
|
||||
|
||||
### 2.1 Observer 스크립트 삽입 체인
|
||||
|
||||
```
|
||||
extension.ts activate()
|
||||
→ html-patcher.ts setupApprovalObserver()
|
||||
→ observer-script.ts generateApprovalObserverScript(port)
|
||||
→ workbench-jetski-agent.html에 인라인 <script> 삽입
|
||||
→ AG 재시작 시 렌더러가 로드
|
||||
```
|
||||
|
||||
### 2.2 Observer 주요 함수
|
||||
|
||||
| 함수 | 역할 |
|
||||
|------|------|
|
||||
| `scanChatBodies()` | 3초마다 실행, conversation view에서 메시지 블록 탐색 |
|
||||
| `extractCleanStepText(el)` | DOM 클론 → style/script/button 제거 → textContent 추출 |
|
||||
| `extractContextFromNearby(btn)` | 승인 버튼 주변 DOM에서 명령어 텍스트 추출 |
|
||||
| `pollResponseGroup(rid, btnRefs)` | response 파일 polling → 버튼 자동 클릭 |
|
||||
|
||||
### 2.3 AI 응답 감지 셀렉터
|
||||
|
||||
```javascript
|
||||
var responseBlocks = cv.querySelectorAll(
|
||||
'.leading-relaxed.select-text, ' // ← AI 응답 마크다운 블록 (주력)
|
||||
+ '.text-ide-message-block-user-color, ' // ← 사용자 메시지 (미매칭)
|
||||
+ '.text-ide-message-block-bot-color, ' // ← NUX tooltip 전용 (오매칭)
|
||||
+ '.bg-ide-message-block-user-background, '// ← 사용자 메시지 (미매칭)
|
||||
+ '[data-message-role="user"], ' // ← 사용자 메시지 (미매칭)
|
||||
+ '[data-role="user"]' // ← 사용자 메시지 (미매칭)
|
||||
);
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> **v0.5.74에서 사용자 메시지 셀렉터가 추가되었습니다.**
|
||||
> AG Native 소스(`jetskiAgent/main.js`)의 `Esn` 컴포넌트 분석으로
|
||||
> 사용자 메시지 CSS 클래스(`msn = "bg-gray-500/10 border border-gray-500/20 p-2 rounded-lg w-full text-sm select-text"`)를 식별.
|
||||
> 셀렉터: `.select-text.rounded-lg`, 역할 판별: `rounded-lg` 있고 `leading-relaxed` 없으면 → user
|
||||
|
||||
### 2.4 AI 응답 추출 흐름
|
||||
|
||||
```
|
||||
scanChatBodies() 3초 간격
|
||||
→ cv = document.querySelector('#conversation')
|
||||
→ responseBlocks = cv.querySelectorAll('.leading-relaxed.select-text, ...')
|
||||
→ lastBlock = responseBlocks[last] (가장 최근 블록)
|
||||
→ 이미 scrape 됐으면 skip
|
||||
→ blockText = extractCleanStepText(lastBlock)
|
||||
→ 안정화 대기 (3초 동안 텍스트 변경 없으면)
|
||||
→ POST /chat { text, source, block_index, role }
|
||||
→ http-bridge → writeChatSnapshot() → WS → Discord
|
||||
```
|
||||
|
||||
### 2.5 Observer 업데이트 제약
|
||||
|
||||
> [!CAUTION]
|
||||
> **Observer 코드는 workbench.html에 인라인 삽입됩니다.**
|
||||
> extension reload만으로는 Observer 코드가 업데이트되지 않습니다.
|
||||
> **AG 재시작 + V8 CachedData 삭제**가 필요합니다.
|
||||
> (단, product.json 체크섬이 맞으면 CachedData 삭제 없이 AG 재시작만으로 충분할 수 있음)
|
||||
|
||||
---
|
||||
|
||||
## 3. 승인 버튼 (Auto-Approve) 경로
|
||||
|
||||
### 3.1 "Always run" 자동 승인 흐름
|
||||
|
||||
```
|
||||
Observer DOM scan
|
||||
→ "Always run" 버튼 텍스트 감지
|
||||
→ POST /pending { command: "Always run", description: "...", buttons: [...] }
|
||||
→ http-bridge _handlePending()
|
||||
→ alwaysRunDetected = true
|
||||
→ enrichment 시도:
|
||||
1. rawDesc에서 > 프롬프트 마커 찾기 → ✅ 성공 (buttons=2일 때 desc에 프롬프트 포함)
|
||||
2. rawDesc 최장 라인 사용 → buttons=1일 때 desc="Always run"이라 실패
|
||||
3. v20 fallback: bridge/pending/ 최신 파일에서 command 읽기 → Step Probe pending 있을 때만
|
||||
→ response 파일 작성 → Observer pollResponseGroup → 버튼 클릭
|
||||
→ WS sendPending { status: 'auto_approved', command: displayCmd }
|
||||
→ Discord embed 표시
|
||||
```
|
||||
|
||||
### 3.2 명령어 enrichment 현황 (2026-04-18 검증)
|
||||
|
||||
| 조건 | 결과 | 빈도 |
|
||||
|------|------|------|
|
||||
| Observer가 buttons=2 (`["Always run","Cancel"]`)이고 desc에 `>` 포함 | ✅ 명령어 표시 | ~50% |
|
||||
| Observer가 buttons=1 (`["Always run"]`)이고 desc="Always run" | ❌ "Always run" 표시 | ~50% |
|
||||
|
||||
**로그 증거** (04:25:59):
|
||||
```
|
||||
AUTO-APPROVE raw: cmd="Always run" desc="…\extension > npm.cmd run compile..." buttons=["Always run","Cancel"]
|
||||
→ cmd="npm.cmd run compile 2>&1; npm.cmd version patch..." ✅ 성공
|
||||
|
||||
AUTO-APPROVE raw: cmd="Always run" desc="Always run" buttons=["Always run"]
|
||||
→ cmd="Always run" ❌ 실패
|
||||
```
|
||||
|
||||
> buttons=2인 경우("Always run" + "Cancel")는 Observer가 code 블록을 찾아 description에 포함.
|
||||
> buttons=1인 경우는 code 블록이 DOM에서 아직 렌더링되지 않았거나 접근 불가.
|
||||
|
||||
---
|
||||
|
||||
## 4. 사용자 메시지 릴레이 상태
|
||||
|
||||
### 4.1 현재 상태: ✅ 작동 (v0.5.74+)
|
||||
|
||||
| 경로 | 상태 | 비고 |
|
||||
|------|------|------|
|
||||
| Observer DOM | ✅ | `.select-text.rounded-lg` 셀렉터로 캡처 (v0.5.74) |
|
||||
| Step Probe (trajectory API) | ❌ | cascade 진행 중 step 조회 불가 |
|
||||
| Step Probe (observer [USER-MSG]) | ❌ | `lastUserInputStepIndex`가 갱신되지 않음 |
|
||||
|
||||
### 4.2 해결 방안
|
||||
|
||||
1. **DOM 덤프에서 사용자 메시지 클래스 식별** → Observer 셀렉터 추가
|
||||
2. **Cascade 완료 후** Step Probe HB-CAPTURE에서 `USER_INPUT` step 캡처 (지연 릴레이)
|
||||
|
||||
---
|
||||
|
||||
## 5. 파일/포트 매핑
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|-----|
|
||||
| Observer 삽입 대상 | `workbench-jetski-agent.html` |
|
||||
| HTTP Bridge 포트 | `getDeterministicPort('gravity_control')` = **18080** |
|
||||
| Extension 로그 | `~/.gemini/antigravity/bridge/extension.log` |
|
||||
| Pending 파일 | `~/.gemini/antigravity/bridge/pending/*.json` |
|
||||
| Response 파일 | `~/.gemini/antigravity/bridge/response/*.json` |
|
||||
| Chat Snapshot 파일 | `~/.gemini/antigravity/bridge/chat_snapshots/*.json` |
|
||||
| Discord 채널 | `#ag-gravity_control` (ID: 1483082084540223663) |
|
||||
| Discord Bot 토큰 | `.env` → `DISCORD_TOKEN` |
|
||||
|
||||
---
|
||||
|
||||
## 6. 디버깅 도구
|
||||
|
||||
| 도구 | 경로 | 용도 |
|
||||
|------|------|------|
|
||||
| Discord 메시지 읽기 | `extension/scratch/discord_read.js` | API로 채널 최근 메시지 조회 |
|
||||
| Discord 채널 목록 | `extension/scratch/discord_channels.js` | 서버 채널 목록 조회 |
|
||||
| Extension 로그 확인 | `Select-String -Path $logFile -Pattern "패턴"` | 실시간 로그 분석 |
|
||||
| DOM 구조 덤프 | Observer 자동 (CV-CHILDREN 로그) | AG Native DOM 클래스 식별 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 버전 히스토리 (v0.5.67~)
|
||||
|
||||
| 버전 | 변경 | 결과 |
|
||||
|------|------|------|
|
||||
| v0.5.67 | Observer DOM relay 비활성화, Step Probe RT-CAPTURE로 전환 | ❌ API가 진행중 step 미반환 |
|
||||
| v0.5.68 | auto-approve enrichment 디버그 로그 추가, 조건 >10 → >3 완화 | Observer가 desc="Always run" 보냄 확인 |
|
||||
| v0.5.69 | pending 파일 fallback으로 auto-approve 명령어 enrichment | 일부 개선 (Step Probe pending 있을 때만) |
|
||||
| v0.5.70 | heartbeat 로깅 강화 | API step count 동결 확인 |
|
||||
| v0.5.71 | heartbeat 3 poll마다 실행, HB-CAPTURE 추가 | API가 진행중 step 미반환 재확인 |
|
||||
| v0.5.72 | Observer DOM relay 재활성화 | AG 재시작 필요 (Observer HTML 캐시) |
|
||||
|
||||
---
|
||||
|
||||
## 8. 남은 작업 (TODO)
|
||||
|
||||
- [x] AG 재시작하여 Observer 반영 확인 — ✅ v0.5.72 작동 확인
|
||||
- [x] Observer의 AI 응답 릴레이가 작동하는지 Discord에서 확인 — ✅ 작동
|
||||
- [ ] v0.5.73 설치 (MSG-BLOCKS 로그 추가) → AG 재시작 → 사용자 메시지 DOM 클래스 식별
|
||||
- [ ] 사용자 메시지 셀렉터 추가 후 테스트
|
||||
- [ ] "Always run" enrichment 개선 — buttons=1일 때 code 블록 추출 개선
|
||||
- [ ] AI 응답이 마지막 블록만 캡처되는 문제 개선 (전문 캡처)
|
||||
Reference in New Issue
Block a user