fix(observer): v16 style/script strip in extractCleanStepText — CSS가 AI 응답으로 Discord 전달되는 버그 수정 (v0.5.52) #task-632

This commit is contained in:
Variet Worker
2026-04-16 21:08:52 +09:00
parent 60a2a97868
commit 62ee081ffe
4 changed files with 23 additions and 19 deletions

View File

@@ -21,21 +21,16 @@
> 鍮꾩듂븳 臾몄젣媛 옱諛쒗븯硫 archive뿉꽌 寃깋븯꽭슂. > 鍮꾩듂븳 臾몄젣媛 옱諛쒗븯硫 archive뿉꽌 寃깋븯꽭슂.
### [2026-04-16] [Extension] ★ AG Native 세션 AI 응답이 Discord에 전혀 전달되지 않음 (미해결, #632) ### [2026-04-16] [Extension] ★ AG Native 세션 AI 응답이 Discord에 CSS로 전달됨 (v0.5.52 수정, #632)
- **증상**: Discord에 **명령 승인 신호만 전달**되고, AI 대화 응답/답변 텍스트는 전혀 전달되지 않음. 수십 세션에 걸쳐 지속 발생. - **증상**: Discord에 AI 대화 응답 대신 **CSS 스타일시트 코드** (`remark-github-blockquote-alert/alert.css`)가 전달됨. `scanChatBodies()` → POST /chat 경로는 작동하지만 내용이 CSS.
- **원인 1 (SDK 경로)**: `GetCascadeTrajectorySteps(cascadeId=세션ID)``500 trajectory not found`. AG Native 세션은 Cascade trajectory API에 등록되지 않아 step-probe의 RT-CAPTURE가 불가능. - **원인**: `extractCleanStepText()`에서 clone한 DOM에서 버튼/SVG/아이콘은 제거하지만 **`<style>` 태그는 제거하지 않음**. AG Native 마크다운 렌더러가 `.leading-relaxed.select-text` 내부에 `<style>` 블록을 주입하는데, 이 CSS textContent가 AI 응답 텍스트로 추출됨.
- **원인 2 (DOM 경로, v15에서 해결 완료)**: Observer v15 `scanChatBodies()`로 AG Native DOM 셀렉터 추가 (`#conversation``.leading-relaxed.select-text`). HTTP bridge `/chat` 핸들러도 정상 구현 (라인117-121, 501-519). - **해결 (v0.5.52)**: `extractCleanStepText()` 최상단에 `clone.querySelectorAll('style, script, noscript, link[rel="stylesheet"]')` 제거 로직 추가. CSS/JS가 텍스트로 포함되는 것을 원천 차단.
- **현재 블로커**: Observer 인라인 스크립트가 렌더러에서 실행되지 않음 (BEACON=0). **원인은 AG 프로세스 미재시작** — html-patcher가 디스크에 HTML 패치 후 extension이 활성화되지만, Electron 렌더러는 이미 패치 전 HTML을 로드한 상태. "Developer: Reload Window"는 Extension Host만 재시작하고 렌더러 HTML은 캐시 유지. - **배포**: v0.5.52 VSIX 설치 + v0.5.50/out/ JS 직접 복사 + V8 CachedData 삭제. AG File→Quit 재시작 필요.
- **코드 검증 완료 (2026-04-16)**: - **이전 블로커 해결 이력**:
- ✅ workbench.html 인라인 스크립트 존재 (46,747 chars) - SDK 경로: AG Native는 Cascade trajectory API 미등록 → step-probe RT-CAPTURE 불가 (구조적 한계)
- ✅ CSP `unsafe-inline` + `connect-src 127.0.0.1` 패치 정상 - DOM 경로: v15에서 `#conversation` + `.leading-relaxed.select-text` 셀렉터 추가로 해결
- ✅ product.json 체크섬 3개 파일 모두 일치 - BEACON=0: AG 프로세스 완전 재시작으로 해결 (Reload Window로는 렌더러 HTML 캐시 유지)
- ✅ JS 문법 오류 없음 (`new Function()` 검증) - **주의**: AG Native 마크다운 렌더링은 `<style>` 블록을 응답 DOM 내부에 인라인으로 삽입함. DOM 텍스트 추출 시 반드시 style/script 태그를 먼저 제거해야 함.
- ✅ HTTP bridge `/chat` 핸들러 → `writeChatSnapshot()` → WS bridge 경로 정상
- ✅ v0.5.50 디렉토리에 v15 JS 파일 직접 복사 완료
- **해결**: AG를 **File → Quit으로 완전 종료 후 재시작** 필요 (not Reload Window). 재시작하면 패치된 HTML이 로드되어 BEACON → scanChatBodies → POST /chat → Discord 전체 경로 작동 예상.
- **배포 참고**: AG는 `~/.antigravity/extensions/` (v0.5.51)보다 `~/.vscode/extensions/` (v0.5.50)을 우선 로드. v0.5.50/out/에 v15 JS 직접 복사로 대응 완료.
- **주의**: AG Native 렌더러는 `data-testid`, `data-step-index` 등 Cascade 전용 속성을 사용하지 않음. DOM 분석 시 반드시 AG 패널이 활성화된 상태에서 dump를 취득해야 함.
- **Vikunja**: #632 - **Vikunja**: #632
### [2026-04-16] [Extension] 터미널 출력(stdout) 텍스트가 명령어로 Discord에 전송 (v0.5.50) ### [2026-04-16] [Extension] 터미널 출력(stdout) 텍스트가 명령어로 Discord에 전송 (v0.5.50)

View File

@@ -5,3 +5,4 @@
| 001 | 04:52 | v16 터미널 출력 필터 + v15 stale LS 자동복구 + heartbeat probe — stdout가 enrichedCmd로 채택되는 버그 수정, VSIX v0.5.50 빌드/배포 | `7ade31e` | 🔧 | | 001 | 04:52 | v16 터미널 출력 필터 + v15 stale LS 자동복구 + heartbeat probe — stdout가 enrichedCmd로 채택되는 버그 수정, VSIX v0.5.50 빌드/배포 | `7ade31e` | 🔧 |
| 002 | 05:28 | AG Native AI 응답 Discord 미전달 근본원인 분석 + Observer v15 scanChatBodies 이중전략 (#conversation + .leading-relaxed.select-text) 구현, v0.5.51 배포 | `729875f` | 🔧 | | 002 | 05:28 | AG Native AI 응답 Discord 미전달 근본원인 분석 + Observer v15 scanChatBodies 이중전략 (#conversation + .leading-relaxed.select-text) 구현, v0.5.51 배포 | `729875f` | 🔧 |
| 003 | 17:13 | Observer v15 E2E 코드 검증 — CSP/체크섬/문법/핸들러 전수 확인 OK. BEACON=0 원인: AG 미재시작(렌더러 HTML 캐시). v0.5.50/out에 v15 JS 직접 배포 | — | 🔧 | | 003 | 17:13 | Observer v15 E2E 코드 검증 — CSP/체크섬/문법/핸들러 전수 확인 OK. BEACON=0 원인: AG 미재시작(렌더러 HTML 캐시). v0.5.50/out에 v15 JS 직접 배포 | — | 🔧 |
| 004 | 21:07 | Observer v16 CSS 추출 버그 수정 — `<style>` 태그 textContent가 AI 응답으로 Discord 전달. extractCleanStepText()에 style/script strip 추가, v0.5.52 배포 | — | 🔧 |

View File

@@ -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.51", "version": "0.5.52",
"publisher": "variet", "publisher": "variet",
"engines": { "engines": {
"vscode": "^1.100.0" "vscode": "^1.100.0"

View File

@@ -1,7 +1,7 @@
export function generateApprovalObserverScript(_port: number): string { export function generateApprovalObserverScript(_port: number): string {
return ` return `
// ── Gravity Bridge v15: AG Native Chat Relay ── // ── Gravity Bridge v16: AG Native Chat Relay + Style Strip ──
// v15: AG Native #conversation + .leading-relaxed.select-text chat body scanning // v16: AG Native style/script strip + #conversation + .leading-relaxed.select-text chat body scanning
(function(){ (function(){
'use strict'; 'use strict';
var BASE='',_obs=false,_sent={},_ready=false; var BASE='',_obs=false,_sent={},_ready=false;
@@ -10,7 +10,7 @@ export function generateApprovalObserverScript(_port: number): string {
var CLEANUP_MS=300000; var CLEANUP_MS=300000;
function log(m){console.log('[GB Observer] '+m);} function log(m){console.log('[GB Observer] '+m);}
log('v15 Script loaded — AG Native Chat Relay'); log('v16 Script loaded — AG Native Chat Relay + Style Strip');
// DIAGNOSTIC BEACON: immediate POST to confirm script execution in renderer // DIAGNOSTIC BEACON: immediate POST to confirm script execution in renderer
try { try {
@@ -477,6 +477,14 @@ export function generateApprovalObserverScript(_port: number): string {
// Clone the step element so we can strip UI elements without affecting the DOM // Clone the step element so we can strip UI elements without affecting the DOM
var clone = stepEl.cloneNode(true); var clone = stepEl.cloneNode(true);
// v16: Remove style/script/noscript elements FIRST — AG Native markdown injects <style> blocks
// that contain CSS rules (e.g. remark-github-blockquote-alert/alert.css) whose textContent
// gets captured as AI response text
var styleEls = clone.querySelectorAll('style, script, noscript, link[rel="stylesheet"]');
for (var si = 0; si < styleEls.length; si++) {
if (styleEls[si].parentNode) styleEls[si].parentNode.removeChild(styleEls[si]);
}
// Remove all buttons (Run, Allow, Cancel, etc.) // Remove all buttons (Run, Allow, Cancel, etc.)
var buttons = clone.querySelectorAll('button'); var buttons = clone.querySelectorAll('button');
for (var bi = 0; bi < buttons.length; bi++) { for (var bi = 0; bi < buttons.length; bi++) {