From a72c522ab5bf523cd1317ed11923f65a16fabcb3 Mon Sep 17 00:00:00 2001 From: Variet Worker Date: Sat, 21 Mar 2026 17:50:45 +0900 Subject: [PATCH] =?UTF-8?q?fix(extension):=20v0.5.4=20=EC=8B=A0=ED=98=B8?= =?UTF-8?q?=20=EA=B0=90=EC=A7=80=203=EC=A4=91=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=E2=80=94=20=EC=84=B8=EC=85=98=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EC=A6=89=EC=8B=9C=20probe,=20reviewAbsoluteUris=20?= =?UTF-8?q?=ED=95=84=EB=93=9C,=20stepIndex=20uint32=20clamp=20+=20permissi?= =?UTF-8?q?on=20=EB=A7=A4=ED=95=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/references/known-issues.md | 19 +++++++++++++++++++ docs/devlog/2026-03-21.md | 5 +++++ extension/package-lock.json | 4 ++-- extension/package.json | 4 ++-- extension/src/approval-handler.ts | 6 +++++- extension/src/step-probe.ts | 27 ++++++++++++++++++++++----- 6 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 docs/devlog/2026-03-21.md diff --git a/.agents/references/known-issues.md b/.agents/references/known-issues.md index 22d20ba..c203f4d 100644 --- a/.agents/references/known-issues.md +++ b/.agents/references/known-issues.md @@ -65,6 +65,24 @@ > v0.4.5 수정 사항(Hub pending_owners, diff_review WS, auto_approve 이중쓰기, WS dual-write, ApprovalView fallback)은 > 코드 수정 완료됨. E2E 통합 검증은 Vikunja #410에서 추적 중. +### [2026-03-21] stepIndex=-1 — AG proto uint32 에러 +- **증상**: DOM observer가 Allow 버튼 감지 → Discord 승인 → RPC `HandleCascadeUserInteraction` 400 에러 +- **원인**: DOM observer 경로는 step index를 모름 → `stepIndex=-1` 전달 → AG proto `uint32` 필드에 음수 불가 +- **해결**: `Math.max(0, ...)` 로 clamp. `permission` type → `runExtensionCode.confirm` 매핑 추가 (v0.5.4) +- **주의**: DOM observer 경로의 step_type은 항상 `stepIndex=-1`일 수 있으므로 proto 전달 전 양수 보장 필수 + +### [2026-03-21] reviewAbsoluteUris — latestNotifyUserStep 필드명 불일치 +- **증상**: `notify_user`의 PathsToReview 파일 릴레이가 한 번도 작동하지 않음 +- **원인**: AG 실제 필드명 `reviewAbsoluteUris` vs 코드 `pathsToReview`/`paths_to_review`/`filePaths` +- **해결**: `reviewAbsoluteUris` 를 첫 번째 후보로 추가 (v0.5.3) +- **주의**: AG RPC 필드명은 extension.log `[NOTIFY-STEP] keys=` 로 확인 가능. 추측 금지 + +### [2026-03-21] 세션 전환 — 첫 WAITING 감지 20-25s 지연 +- **증상**: 새 대화 시작 후 첫 run_command 승인이 Discord에 안 오고 AG에서 직접 승인해야 함 +- **원인**: `lastModTime=''` 리셋 → `modTimeChanged=true` → THINKING 분기 반복 → probe 15-25s 지연 +- **해결**: `lastModTime=currentModTime` + `return` 제거 + 즉시 probe 강제 + 회귀 가드 추가 (v0.5.3) +- **주의**: 세션 전환 시 `wasRunning`/`pendingModifiedFiles` 리셋 필수 (이전 세션 잔여물로 false diff_review 방지) + --- ## 핵심 작업 규칙 (과거 이슈에서 반복된 패턴) @@ -86,3 +104,4 @@ | 11 | **HttpBridgeContext에 프리미티브 by-value 복사 금지** — 별도 객체 생성 시 getter 사용 | HttpBridgeContext stale primitive | | 12 | **새 AG 도구 추가 시 step-probe step_type 매핑 + approval-handler RPC payload 매핑 양쪽 필수** | browser_subagent Allow | | 13 | **WS `onConnected`에서 step-probe 상태 리셋 필수** — `stallProbed`/`lastPendingStepIndex`는 TTL 없는 영구 값 | Idle→Resume 신호 소실 | +| 14 | **AG proto `uint32` 필드에 음수 전달 금지** — `stepIndex` 등은 `Math.max(0, ...)` 필수 | stepIndex=-1 RPC 400 | diff --git a/docs/devlog/2026-03-21.md b/docs/devlog/2026-03-21.md new file mode 100644 index 0000000..e610c35 --- /dev/null +++ b/docs/devlog/2026-03-21.md @@ -0,0 +1,5 @@ +# 2026-03-21 Devlog + +| # | 시간 | 작업 | 커밋 | 상태 | +|---|------|------|------|------| +| 1 | 17:48 | v0.5.3~v0.5.4 신호 감지 3중 버그 수정: 세션 전환 즉시 probe (20-25s→5s), reviewAbsoluteUris 필드 수정, stepIndex=-1 uint32 에러 수정 + permission 매핑 | `0fb33a9` | ✅ | diff --git a/extension/package-lock.json b/extension/package-lock.json index 52fdc25..8a888f2 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "gravity-bridge", - "version": "0.4.3", + "version": "0.5.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gravity-bridge", - "version": "0.4.3", + "version": "0.5.4", "dependencies": { "ws": "^8.19.0" }, diff --git a/extension/package.json b/extension/package.json index 35443ff..3c039d1 100644 --- a/extension/package.json +++ b/extension/package.json @@ -2,7 +2,7 @@ "name": "gravity-bridge", "displayName": "Gravity Bridge", "description": "Antigravity ↔ Discord 브리지 연동 확장", - "version": "0.5.2", + "version": "0.5.4", "publisher": "variet", "engines": { "vscode": "^1.100.0" @@ -85,4 +85,4 @@ "dependencies": { "ws": "^8.19.0" } -} \ No newline at end of file +} diff --git a/extension/src/approval-handler.ts b/extension/src/approval-handler.ts index 0efcb9d..264a583 100644 --- a/extension/src/approval-handler.ts +++ b/extension/src/approval-handler.ts @@ -313,7 +313,7 @@ async function processResponseFile(filePath: string) { */ export async function tryApprovalStrategies(approved: boolean, sessionId: string, stepType: string = '', stepIndex: number = -1): Promise { const action = approved ? 'APPROVE' : 'REJECT'; - const effectiveStepIndex = stepIndex >= 0 ? stepIndex : ctx.lastPendingStepIndex; + const effectiveStepIndex = Math.max(0, stepIndex >= 0 ? stepIndex : ctx.lastPendingStepIndex); ctx.logToFile(`[APPROVAL] Starting ${action} strategies for session ${sessionId.substring(0, 8)} stepType=${stepType} stepIndex=${effectiveStepIndex}`); // ── Dynamic Command Discovery (log what's available during WAITING state) ── @@ -388,6 +388,10 @@ export async function tryApprovalStrategies(approved: boolean, sessionId: string interactionPayload = { filePermission: { allow: true, scope } }; } else if (typeLower.includes('elicitation')) { interactionPayload = { elicitation: {} }; + } else if (typeLower === 'permission' || typeLower.includes('permission')) { + // DOM observer 'permission' type: browser_subagent Allow/Deny dialog + // Try runExtensionCode first (most common for JS execution permission) + interactionPayload = { runExtensionCode: { confirm: true } }; } else { // Default: try run_command (most common) interactionPayload = { runCommand: { confirm: true } }; diff --git a/extension/src/step-probe.ts b/extension/src/step-probe.ts index 1bf63a5..cd8a246 100644 --- a/extension/src/step-probe.ts +++ b/extension/src/step-probe.ts @@ -258,6 +258,8 @@ function setupMonitor() { const currentTitle = (bestSession.summary || 'Untitled').substring(0, 50); const isRunning = String(bestSession.status || '').includes('RUNNING'); + const currentModTime = bestSession.lastModifiedTime || (bestSession as any).lastModifiedTimestamp || (bestSession as any).modifiedTime || ''; + // Session changed? if (bestSessionId !== ctx.activeSessionId) { ctx.activeSessionId = bestSessionId; @@ -270,6 +272,14 @@ function setupMonitor() { lastResponseCaptureStep = currentCount; // don't re-relay old responses ctx.lastPendingStepIndex = -1; ctx.stallProbed = false; + ctx.sawRunningAfterPending = true; // prevent stale auto_resolve from previous session + consecutiveIdleCount = 0; + lastModTime = currentModTime; // use currentModTime → prevents THINKING branch on first poll + // Reset transition/diff state from previous session (regression guard for fall-through) + wasRunning = isRunning; + pendingModifiedFiles = []; + pendingModifiedFilePaths = []; + pendingEditStepIndices = []; // Don't register here — registration happens lazily in ctx.writeChatSnapshot/writePendingApproval // to avoid race conditions between multiple extension instances // Dump session keys + trajectoryMetadata on session change @@ -280,7 +290,9 @@ function setupMonitor() { ctx.logToFile(`[SESSION-INIT] trajectoryMetadata=${JSON.stringify(trajMeta).substring(0, 500)}`); } console.log(`Gravity Bridge: [POLL#${pollCount}] session: ${ctx.activeSessionId.substring(0, 8)} "${currentTitle}" steps=${currentCount} ${isRunning ? 'RUNNING' : 'idle'}`); - return; + // Fall through to WAITING detection — immediate probe on session change. + // Previously, `return` here caused 20-25s delay before first WAITING + // detection (confirmed via extension.log). Now we immediately check. } const delta = currentCount - lastKnownStepCount; @@ -356,10 +368,15 @@ function setupMonitor() { // ── STALL-BASED approval detection with step probe ── - const currentModTime = bestSession.lastModifiedTime || (bestSession as any).lastModifiedTimestamp || (bestSession as any).modifiedTime || ''; const modTimeChanged = currentModTime !== lastModTime; const isStall = isRunning && delta === 0; + // Session-change immediate probe: bypass stall debounce on first poll + // Without this, consecutiveIdleCount=0 prevents probe condition (>=1) + if (isStall && consecutiveIdleCount === 0 && !ctx.stallProbed && !modTimeChanged) { + consecutiveIdleCount = 1; // force probe condition satisfied + } + // Log modTime on stalls for debugging if (isStall && consecutiveIdleCount < 8) { ctx.logToFile(`[STALL-DBG] idle=${consecutiveIdleCount} modTime='${currentModTime}' changed=${modTimeChanged}`); @@ -661,9 +678,9 @@ function setupMonitor() { } // ── PathsToReview: read and relay referenced artifact files ── - const pathsToReview: string[] = notifyData.pathsToReview - || notifyData.paths_to_review - || notifyData.filePaths + // AG field name is `reviewAbsoluteUris` (confirmed via extension.log NOTIFY-STEP keys) + const pathsToReview: string[] = notifyData.reviewAbsoluteUris + || notifyData.pathsToReview || []; if (pathsToReview.length > 0) { ctx.logToFile(`[NOTIFY-STEP] PathsToReview: ${pathsToReview.length} files`);