diff --git a/extension/package.json b/extension/package.json index a50fa49..a866ef9 100644 --- a/extension/package.json +++ b/extension/package.json @@ -2,7 +2,7 @@ "name": "gravity-bridge", "displayName": "Gravity Bridge", "description": "Discord-based unified approval system for Antigravity AI interactions.", - "version": "0.5.43", + "version": "0.5.44", "publisher": "variet", "engines": { "vscode": "^1.100.0" diff --git a/extension/src/http-bridge.ts b/extension/src/http-bridge.ts index 7936da4..db69e90 100644 --- a/extension/src/http-bridge.ts +++ b/extension/src/http-bridge.ts @@ -256,7 +256,7 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) { try { const data = JSON.parse(body); - // ── v11: Command enrichment FIRST — extract actual command from description ── + // ── v12: Command enrichment FIRST — extract actual command from description ── // Must run before filters so "Always run" with useful description isn't filtered out const rawCmd = (data.command || '').trim(); const rawDesc = (data.description || '').trim(); @@ -271,9 +271,18 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) { if (promptMatch && promptMatch[1].trim().length > 3) { extracted = promptMatch[1].trim(); } - enrichedCmd = extracted.substring(0, 200); - enrichedDesc = `[${rawCmd}] ${rawDesc}`; - ctx.logToFile(`[HTTP] command enriched: "${rawCmd}" → "${enrichedCmd.substring(0, 60)}"`); + // v12: Validate extracted text is not just a prompt fragment + // Skip enrichment if extracted is the same as rawDesc (no > found) or looks like a bare prompt + const PROMPT_ONLY_RE = /^[\s\\\/]*[\w_.-]+\s*[>»$#]\s*$/; + const isPromptOnly = PROMPT_ONLY_RE.test(extracted) || extracted === rawDesc; + if (!isPromptOnly && extracted.length > 3) { + enrichedCmd = extracted.substring(0, 200); + enrichedDesc = `[${rawCmd}] ${rawDesc}`; + ctx.logToFile(`[HTTP] command enriched: "${rawCmd}" → "${enrichedCmd.substring(0, 60)}"`); + } else { + // Keep original button text as command, but use desc as-is + ctx.logToFile(`[HTTP] enrichment skipped (prompt-only): "${rawCmd}" desc="${rawDesc.substring(0, 60)}"`); + } } // ── Server-side false positive filter (uses enriched cmd) ── diff --git a/extension/src/observer-script.ts b/extension/src/observer-script.ts index 4104fee..4441fab 100644 --- a/extension/src/observer-script.ts +++ b/extension/src/observer-script.ts @@ -1,7 +1,7 @@ export function generateApprovalObserverScript(_port: number): string { return ` -// ── Gravity Bridge v11: Enhanced Context Extraction ── -// v11: Extended span/div/p fallback for context, 5-level sibling search, improved command display +// ── Gravity Bridge v12: Prompt-Skip + Multi-CodeEl Context Extraction ── +// v12: Skip prompt-only code text, try all codeEls, improved enrichment fallback (function(){ 'use strict'; var BASE='',_obs=false,_sent={},_ready=false; @@ -10,7 +10,7 @@ export function generateApprovalObserverScript(_port: number): string { var CLEANUP_MS=300000; function log(m){console.log('[GB Observer] '+m);} - log('v11 Script loaded — Enhanced Context Extraction'); + log('v12 Script loaded — Prompt-Skip Context Extraction'); // DIAGNOSTIC BEACON: immediate POST to confirm script execution in renderer try { @@ -109,12 +109,16 @@ export function generateApprovalObserverScript(_port: number): string { return null; } - // v11: Climb DOM tree to find context near the button - // Priority: pre/code > [class*=terminal] > substantial span/div/p text > aria-label > button text + // v12: Climb DOM tree to find context near the button + // Priority: pre/code (skip prompt-only) > substantial span/div/p text > aria-label > button text + // PROMPT_ONLY: matches terminal prompts like "...\project >" or "PS C:\...>" with no actual command + var PROMPT_ONLY_RE = /^(.*[\\/>»$#]\\s*)$/; function extractContextFromNearby(btn) { var node = btn; var _debugTrail = []; var _fallbackText = ''; // Best span/div/p text found (used if no pre/code) + var _bestCodeText = ''; // Best code text found across all depths + var _bestCodeHeader = ''; for (var depth = 0; depth < 20 && node; depth++) { if (!node.querySelector) { _debugTrail.push('d'+depth+':noQS'); node = node.parentElement; continue; } // Look for code/pre blocks (actual command text) @@ -122,26 +126,37 @@ export function generateApprovalObserverScript(_port: number): string { _debugTrail.push('d'+depth+':tag='+((node.tagName||'?').toLowerCase())+',cls='+(((typeof node.className==='string')?node.className:'').substring(0,60))+',codeEls='+codeEls.length); for (var ci = 0; ci < codeEls.length; ci++) { var codeText = cleanLines((codeEls[ci].textContent || '').trim().substring(0, 500)); - if (codeText && codeText.length > 5 && !/^Running\\s*\\d/i.test(codeText)) { + if (!codeText || codeText.length <= 5) continue; + if (/^Running\\s*\\d/i.test(codeText)) continue; + // v12: Skip prompt-only text (e.g. "...\gravity_control >") - no actual command + if (PROMPT_ONLY_RE.test(codeText.trim())) { + _debugTrail.push('skip_prompt_ci='+ci+':'+codeText.substring(0,30)); + continue; + } + // This code element has actual content - capture it + if (!_bestCodeText || codeText.length > _bestCodeText.length) { + _bestCodeText = codeText; // Also try to get a header/title near this container var headerEl = node.querySelector('h1, h2, h3, [class*="header"], [class*="title"], [class*="cursor-pointer"]'); - var headerText = ''; if (headerEl) { var hClone = headerEl.cloneNode(true); var hRem = hClone.querySelectorAll('button, svg, [class*="icon"], .google-symbols'); for (var hi = 0; hi < hRem.length; hi++) { if (hRem[hi].parentNode) hRem[hi].parentNode.removeChild(hRem[hi]); } - headerText = cleanLines((hClone.textContent || '').trim().substring(0, 200)); + _bestCodeHeader = cleanLines((hClone.textContent || '').trim().substring(0, 200)); } - var parts = []; - if (headerText) parts.push(headerText); - parts.push(codeText); - log('CONTEXT-OK d='+depth+' src=code trail='+_debugTrail.join(' > ')); - _lastContextDebug = _debugTrail.join(' > '); - return parts.join(' — '); } } + // v12: If we found a good code text, return it immediately at this depth + if (_bestCodeText) { + var parts = []; + if (_bestCodeHeader) parts.push(_bestCodeHeader); + parts.push(_bestCodeText); + log('CONTEXT-OK d='+depth+' src=code trail='+_debugTrail.join(' > ')); + _lastContextDebug = _debugTrail.join(' > '); + return parts.join(' — '); + } // v11: Look for substantial text in span/div/p as fallback context if (depth <= 8 && !_fallbackText) { var textEls = node.querySelectorAll('span, div, p');