chore(bridge): update known-issues and prep for DOM Observer MD restoration (#634)

This commit is contained in:
Variet Worker
2026-04-17 06:25:22 +09:00
parent b2f17a086b
commit 13e569f426
4 changed files with 48 additions and 9 deletions

View File

@@ -33,6 +33,13 @@
- **주의**: AG Native 마크다운 렌더링은 `<style>` 블록을 응답 DOM 내부에 인라인으로 삽입함. DOM 텍스트 추출 시 반드시 style/script 태그를 먼저 제거해야 함.
- **Vikunja**: #632
### [2026-04-16] [Extension] ★ AG Native Observer innerText로 인한 마크다운 포맷 유실 및 사용자 요청 누락
- **증상**: Discord로 릴레이되는 AI 응답에서 리스트 번호(`1. 2.`)나 불릿(`-`) 등 마크다운 포맷이 완전히 유실되고 텍스트만 이어져서 나옴. 그리고 사용자가 입력한 메시지(요청)는 아예 릴레이되지 않음.
- **원인 1**: `extractCleanStepText()`에서 `innerText`를 사용하여 텍스트를 추출할 때, 렌더링되지 않은 DOM이나 CSS 카운터로 생성된 list-item 마커가 무시됨.
- **원인 2**: `scanChatBodies()` 로직이 `.leading-relaxed.select-text` (AI 응답 블록)만을 타겟팅하여 사용자의 메시지 박스(예: `.text-ide-message-block-user-color`)는 추출 대상에서 제외됨.
- **해결 (계획 중)**: `innerText` 대신 HTML DOM 노드를 순회하며 `convertNodeToMarkdown` 변환 함수를 통해 마크다운 문법을 보존하도록 개선. User 블록도 함께 감지하여 브릿지에 `role: 'user'` 플래그를 추가 전송하도록 수정 예정.
- **주의**: Webview 내에서 텍스트 노드를 파싱할 때 `innerText`는 브라우저 레이아웃 엔진에 의존하므로 완전한 마크다운 보존을 위해서는 Node Type 순회를 활용한 구조 복원이 보장되어야 함.
### [2026-04-16] [Extension] 터미널 출력(stdout) 텍스트가 명령어로 Discord에 전송 (v0.5.50)
- **증상**: Discord에 `cmd="No extension.log found"`, `cmd="AG CLI not found..."`, `cmd="Log found: C:\..."` 등 터미널 **출력** 텍스트가 명령어로 전송됨
- **원인**: Observer가 code 블록 2개를 감지: (1) 프롬프트+명령어 → JUNK_CODE_RE로 스킵, (2) 터미널 출력 → 유효한 code로 판단 → description에 포함. http-bridge enrichment에서 description에 prompt marker(`>`)가 없으면 rawDesc 전체를 enrichedCmd로 채택

View File

@@ -7,3 +7,5 @@
| 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 배포 | `62ee081` | ✅ |
| 005 | 22:07 | Observer v17 Always run 자동승인 + Retry 릴레이 — "Always run" 브릿지 레벨 자동승인, Retry 버튼 Discord 전달, v0.5.53 배포 | `7dbf73a` | ✅ |
| 006 | 21:28 | AG Native <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Markdown <20><><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD>Ʈ/<2F><>) <20><><EFBFBD><EFBFBD> <20><> User <20><>û <20>̼<EFBFBD><CCBC><EFBFBD> <20>м<EFBFBD>, <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ȹ<EFBFBD><C8B9> <20>ۼ<EFBFBD> (v0.5.54 <20><><EFBFBD><EFBFBD> <20><>) | ? | ?? |

View File

@@ -2,7 +2,7 @@
"name": "gravity-bridge",
"displayName": "Gravity Bridge",
"description": "Discord-based unified approval system for Antigravity AI interactions.",
"version": "0.5.53",
"version": "0.5.54",
"publisher": "variet",
"engines": {
"vscode": "^1.100.0"

View File

@@ -49,8 +49,8 @@ export function generateApprovalObserverScript(_port: number): string {
var NOISE_CODE_RE = /^(declare\\s+(class|function|interface|type|enum|const|var|let)\\s|(import|export|from)\\s|\\s*[{}()\\[\\];]\\s*$|\\.ts:\\d+:|extension.*src.*sdk)/i;
function isNoiseLine(line) {
if (!line || line.trim().length < 2) return true;
var trimmed = line.trim();
if (trimmed.length < 2 && !/^[-*+>]$|^[0-9]$/.test(trimmed)) return true;
if (NOISE_RE.test(trimmed)) return true;
if (NOISE_CODE_RE.test(trimmed)) return true;
// Single-word Material icon names (all lowercase, no spaces)
@@ -66,10 +66,23 @@ export function generateApprovalObserverScript(_port: number): string {
text = text.replace(/Thought for a few seconds/gi, '');
var lines = text.split('\\n');
var clean = [];
var lastWasEmpty = false;
for (var i = 0; i < lines.length; i++) {
if (!isNoiseLine(lines[i])) clean.push(lines[i].trim());
var line = lines[i];
if (line.trim().length === 0) {
if (!lastWasEmpty && clean.length > 0) {
clean.push('');
lastWasEmpty = true;
}
return clean.join('\\n').trim();
continue;
}
if (!isNoiseLine(line)) {
clean.push(line.replace(/\\s+$/, ''));
lastWasEmpty = false;
}
}
while (clean.length > 0 && clean[clean.length - 1] === '') clean.pop();
return clean.join('\\n');
}
function cleanButtonText(btn) {
@@ -506,12 +519,29 @@ export function generateApprovalObserverScript(_port: number): string {
// Try to get text from markdown rendering area first
// AG Native uses .leading-relaxed.select-text, Cascade uses .markdown-body/.prose
var mdEl = clone.querySelector('.markdown-body, .prose, [class*="markdown"], [class*="rendered"]');
// v18 FIX: Temporarily attach to DOM to force layout computation for .innerText
// Without this, .innerText on unattached node behaves exactly like .textContent (loses block newlines)
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.left = '-9999px';
container.style.top = '-9999px';
container.style.opacity = '0';
container.style.width = '800px';
container.appendChild(clone);
document.body.appendChild(container);
var targetEl = mdEl || clone;
var rawText = '';
if (mdEl && mdEl.innerText && mdEl.innerText.trim().length > 10) {
rawText = mdEl.innerText.trim();
try {
if (targetEl.innerText && targetEl.innerText.trim().length > 10) {
rawText = targetEl.innerText.trim();
} else {
// Fallback: get all text but filter aggressively
rawText = (clone.innerText || clone.textContent || '').trim();
rawText = (targetEl.innerText || targetEl.textContent || '').trim();
}
} finally {
if (container.parentNode) container.parentNode.removeChild(container);
}
// Apply line-by-line noise filter