From fadd39424ba3bfbc02d87f07d79c4839a5adf5ae Mon Sep 17 00:00:00 2001 From: Variet Worker Date: Fri, 10 Apr 2026 00:12:01 +0900 Subject: [PATCH] fix(bridge): fix Discord signal relay false-positives and empty body logic --- .agents/references/known-issues.md | 6 ++++++ bot.py | 4 ++-- docs/devlog/2026-04-09.md | 1 + docs/devlog/entries/20260409-004.md | 15 +++++++++++++++ extension/src/observer-script.ts | 16 ++++++++-------- extension/src/step-probe.ts | 6 +++--- 6 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 docs/devlog/entries/20260409-004.md diff --git a/.agents/references/known-issues.md b/.agents/references/known-issues.md index 534c39c..32019cd 100644 --- a/.agents/references/known-issues.md +++ b/.agents/references/known-issues.md @@ -197,3 +197,9 @@ | 16 | **익스텐션(Bridge)은 자의적 비즈니스 판단 절대 금지** — `SafeToAutoRun` 등의 조건 브랜치 분기는 모두 봇으로 위임 (Agnostic Bridge) | SafeToAutoRun Deadlock (v0.5.15) | | 17 | **package.json 빌드 스크립트 강제** — `vscode:prepublish` 추가로 낡은 소스 배포 원천 차단 | VSIX v0.5.15 빌드 누락 | | 18 | **동기식 `cp.execSync` 사용 금지** — Windows 환경에서 메인 이벤트루프 프리징 및 WS heartbeat 단절 유발 | detectProjectName 프리징 | + +### [2026-04-09] [Bot/Extension] Discord Signal Relay Failure & Empty Body +- **증상**: 디스코드 봇은 '자동 승인됨'을 띄우지만 실제 코드 본문이 표시되지 않고, 채널에 진짜 채팅 메시지나 알림이 스팸 큐 뒤에 밀려 전송되지 않음. +- **원인**: 1) observer-script.ts에서 버튼 텍스트 매칭 시 Run 단어의 경계(\b) 처리를 하지 않아 VS Code 하단의 'Running 1 command'를 가로채어 PENDING 스팸 무한 생성. 2) bot.py에서 자동 승인 Embed 생성 시 req.description을 그리지 않고 버튼 텍스트(req.command)만 표시. 3) step-probe.ts에서 세션 교체 시 최근 알림 인덱스 초기화를 잘못하여 세션의 첫 메시지를 무조건 드롭. +- **해결**: DOM 감지 정규식에 \b 강제 부여 (/Run\b/), bot.py의 Auto-Approve 쪽 Embed 본문에 req.description 렌더링 추가, step-probe.ts에서 session init 시 index를 -1로 리셋. +- **주의**: Native UI 텍스트 감지 시 단어 경계(\b)까지 검증해야 False Positive를 막을 수 있으며, Auto-Approve는 반드시 본문을 노출해야 함. diff --git a/bot.py b/bot.py index 05ce83b..e25b3c5 100644 --- a/bot.py +++ b/bot.py @@ -732,7 +732,7 @@ class GravityBot(commands.Bot): try: embed = discord.Embed( title="🤖 자동 승인됨", - description=f"```\n{req.command[:500]}\n```", + description=f"✅ **{req.command}**\n\n```\n{req.description[:2000]}\n```" if getattr(req, "description", "") else f"✅ **{req.command}**", color=discord.Color.green(), ) embed.set_footer(text=f"auto-approve | {req.request_id[:12]}") @@ -1158,7 +1158,7 @@ class GravityBot(commands.Bot): try: embed = discord.Embed( title="🤖 자동 승인됨", - description=f"```\n{request.command[:500]}\n```", + description=f"✅ **{request.command}**\n\n```\n{request.description[:2000]}\n```" if getattr(request, "description", "") else f"✅ **{request.command}**", color=discord.Color.green(), ) embed.set_footer(text=f"auto-approve | {request.request_id[:12]}") diff --git a/docs/devlog/2026-04-09.md b/docs/devlog/2026-04-09.md index db3e8eb..8924cb8 100644 --- a/docs/devlog/2026-04-09.md +++ b/docs/devlog/2026-04-09.md @@ -5,3 +5,4 @@ | 001 | 21:55 | Agent UI Tailwind/Native 마이그레이션 대응 (DOM 옵저버 구조 개편) | `HEAD` | ✅ | | 002 | 22:30 | Agent UI 버튼 무시 버그 긴급수정 (CodeLens 필터교정) | `HEAD` | ✅ | | 003 | 23:15 | Native UI 아이콘 글루잉 대응 스캐너 픽스 (DOM Regex 매칭 강화) | `HEAD` | ✅ | +| 004 | 00:10 | Discord Signal Relay & Auto-Approve Body Null 버그 수정 (False Positive 차단) | "HEAD" | ✅ | diff --git a/docs/devlog/entries/20260409-004.md b/docs/devlog/entries/20260409-004.md new file mode 100644 index 0000000..8a26dae --- /dev/null +++ b/docs/devlog/entries/20260409-004.md @@ -0,0 +1,15 @@ +# Discord Signal Relay & Auto-Approve Body Null 버그 수정 (False Positive 차단) + +- **시간**: 2026-04-10 00:00~00:10 +- **Commit**: `HEAD` +- **Vikunja**: #태스크번호 → done + +## 트러블슈팅 & 삽질 +- **DOM 정규식의 반란**: Native UI 패치 이후 버튼의 text-gluing 제거 때문에 정규식을 광범위하게 바꿨으나, 하필 `Run\s*` 조건에 단어 경계(`\b`)를 누락하는 바람에 VS Code 하단의 시스템 상태 버튼인 `Running 1 command`까지 AI의 `Run` 버튼으로 인식해버림. 무한 PENDING 스팸을 만들어 브릿지 큐를 폭파시킨 주범. +- **Auto-Approve 본문 누락**: 봇에서 자동 승인 Embed 생성 시 `req.description` (실행될 본문 코드)을 아예 그리지 않고 `req.command` (단순 버튼 라벨)만 출력하도록 코딩되어 있었음. 사용자는 '자동 승인' 알림을 받지만 정작 무엇이 승인되었는지는 전혀 알 수 없어 '본문 표시 자체가 안 된다'고 오해할 수밖에 없었음. +- **첫 알림 메시지 무시**: `step-probe.ts`에서 세션 전환 시 `lastNotifyStepIndex`를 초기화할 때 `-1` 로 리셋하지 않아 새 세션의 첫 안내 메시지가 매번 씹히는 증상 발견. + +## 결정 사항 +- `bot.py`의 Auto-Approve Embed 구문에 수동 승인처럼 본문을 표출하도록 렌더링 로직 통일. +- `observer-script.ts`의 `TerminalCommand` 정규식에 `\b`를 추가하여 시스템 버튼과의 혼선을 원천 차단함. +- `step-probe.ts` 의 index reset 초기값을 `-1` 로 명시화. diff --git a/extension/src/observer-script.ts b/extension/src/observer-script.ts index 4ee5161..6873cad 100644 --- a/extension/src/observer-script.ts +++ b/extension/src/observer-script.ts @@ -275,19 +275,19 @@ export function generateApprovalObserverScript(_port: number): string { // ONLY positive triggers should initiate a pending request group. // Negative/secondary buttons (Deny, Reject, Dismiss) will be collected as siblings. var PATS=[ - {re:/^(?:Always\s*)?Run/i, type:'terminal_command'}, - {re:/^(?:Always\s*)?Accept all/i, type:'diff_review'}, - {re:/^(?:Always\s*)?Accept/i, type:'agent_step'}, - {re:/^(?:Always\s*)?Allow/i, type:'permission'}, - {re:/^(?:Always\s*)?Approve/i, type:'agent_step'}, - {re:/^Retry/i, type:'error_recovery'}, + {re:/^(?:Always\s*)?Run\b/i, type:'terminal_command'}, + {re:/^(?:Always\s*)?Accept(?: all)?\b/i, type:'diff_review'}, + {re:/^(?:Always\s*)?Accept\b/i, type:'agent_step'}, + {re:/^(?:Always\s*)?Allow\b/i, type:'permission'}, + {re:/^(?:Always\s*)?Approve\b/i, type:'agent_step'}, + {re:/^Retry\b/i, type:'error_recovery'}, ]; // ALL actionable button patterns (for grouping siblings in same container) - var ALL_ACTION_RE=[/^(?:Always\s*)?Run/i,/^(?:Always\s*)?Accept/i,/^Reject/i,/^(?:Always\s*)?Allow/i,/^Deny/i,/^(?:Always\s*)?Approve/i,/^Cancel$/i,/^Retry$/i,/^Dismiss$/i,/^Stop$/i,/^Decline$/i]; + var ALL_ACTION_RE=[/^(?:Always\s*)?Run\b/i,/^(?:Always\s*)?Accept\b/i,/^Reject\b/i,/^(?:Always\s*)?Allow\b/i,/^Deny\b/i,/^(?:Always\s*)?Approve\b/i,/^Cancel\b/i,/^Retry\b/i,/^Dismiss\b/i,/^Stop\b/i,/^Decline\b/i]; // Reject button patterns for finding the counterpart - var REJECT_RE=[/^reject$/i,/^reject all$/i,/^cancel$/i,/^deny$/i,/^stop$/i,/^decline$/i,/^dismiss$/i]; + var REJECT_RE=[/^reject\b/i,/^cancel\b/i,/^deny\b/i,/^stop\b/i,/^decline\b/i,/^dismiss\b/i]; // ── Stable button fingerprint (no getBoundingClientRect — scroll-safe) ── function btnId(b,type){ diff --git a/extension/src/step-probe.ts b/extension/src/step-probe.ts index b511f13..94db34c 100644 --- a/extension/src/step-probe.ts +++ b/extension/src/step-probe.ts @@ -1,4 +1,4 @@ -/** +/** * Step Probe Module - SDK polling, response handling, approval strategies. * All shared state accessed through BridgeContext. * Extracted from extension.ts. @@ -269,8 +269,8 @@ function setupMonitor() { activeTrajectoryId = (bestSession as any).trajectoryId || ''; activeSessionTitle = currentTitle; lastKnownStepCount = currentCount; - lastNotifyStepIndex = bestSession.latestNotifyUserStep?.stepIndex ?? -1; - lastTaskStepIndex = bestSession.latestTaskBoundaryStep?.stepIndex ?? -1; + lastNotifyStepIndex = -1; + lastTaskStepIndex = -1; lastUserInputStepIdx = bestSession.lastUserInputStepIndex ?? -1; lastResponseCaptureStep = currentCount; // don't re-relay old responses ctx.lastPendingStepIndex = -1;