fix(observer): v12 — skip prompt-only code text + enrichment validation (v0.5.44) #task-619
- extractContextFromNearby: PROMPT_ONLY_RE skips code elements containing only terminal prompts (e.g. '\\gravity_control >') - Multi-codeEl traversal: tries all code elements at each depth, picks longest non-prompt text - http-bridge enrichment: validates extracted command is not just a prompt fragment before enriching - Fixes: cmd='\\gravity_control >' (prompt-only) no longer sent to Discord as the command text
This commit is contained in:
@@ -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.43",
|
"version": "0.5.44",
|
||||||
"publisher": "variet",
|
"publisher": "variet",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.100.0"
|
"vscode": "^1.100.0"
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(body);
|
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
|
// Must run before filters so "Always run" with useful description isn't filtered out
|
||||||
const rawCmd = (data.command || '').trim();
|
const rawCmd = (data.command || '').trim();
|
||||||
const rawDesc = (data.description || '').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) {
|
if (promptMatch && promptMatch[1].trim().length > 3) {
|
||||||
extracted = promptMatch[1].trim();
|
extracted = promptMatch[1].trim();
|
||||||
}
|
}
|
||||||
|
// 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);
|
enrichedCmd = extracted.substring(0, 200);
|
||||||
enrichedDesc = `[${rawCmd}] ${rawDesc}`;
|
enrichedDesc = `[${rawCmd}] ${rawDesc}`;
|
||||||
ctx.logToFile(`[HTTP] command enriched: "${rawCmd}" → "${enrichedCmd.substring(0, 60)}"`);
|
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) ──
|
// ── Server-side false positive filter (uses enriched cmd) ──
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export function generateApprovalObserverScript(_port: number): string {
|
export function generateApprovalObserverScript(_port: number): string {
|
||||||
return `
|
return `
|
||||||
// ── Gravity Bridge v11: Enhanced Context Extraction ──
|
// ── Gravity Bridge v12: Prompt-Skip + Multi-CodeEl Context Extraction ──
|
||||||
// v11: Extended span/div/p fallback for context, 5-level sibling search, improved command display
|
// v12: Skip prompt-only code text, try all codeEls, improved enrichment fallback
|
||||||
(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('v11 Script loaded — Enhanced Context Extraction');
|
log('v12 Script loaded — Prompt-Skip Context Extraction');
|
||||||
|
|
||||||
// DIAGNOSTIC BEACON: immediate POST to confirm script execution in renderer
|
// DIAGNOSTIC BEACON: immediate POST to confirm script execution in renderer
|
||||||
try {
|
try {
|
||||||
@@ -109,12 +109,16 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// v11: Climb DOM tree to find context near the button
|
// v12: Climb DOM tree to find context near the button
|
||||||
// Priority: pre/code > [class*=terminal] > substantial span/div/p text > aria-label > button text
|
// 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) {
|
function extractContextFromNearby(btn) {
|
||||||
var node = btn;
|
var node = btn;
|
||||||
var _debugTrail = [];
|
var _debugTrail = [];
|
||||||
var _fallbackText = ''; // Best span/div/p text found (used if no pre/code)
|
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++) {
|
for (var depth = 0; depth < 20 && node; depth++) {
|
||||||
if (!node.querySelector) { _debugTrail.push('d'+depth+':noQS'); node = node.parentElement; continue; }
|
if (!node.querySelector) { _debugTrail.push('d'+depth+':noQS'); node = node.parentElement; continue; }
|
||||||
// Look for code/pre blocks (actual command text)
|
// 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);
|
_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++) {
|
for (var ci = 0; ci < codeEls.length; ci++) {
|
||||||
var codeText = cleanLines((codeEls[ci].textContent || '').trim().substring(0, 500));
|
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
|
// 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 headerEl = node.querySelector('h1, h2, h3, [class*="header"], [class*="title"], [class*="cursor-pointer"]');
|
||||||
var headerText = '';
|
|
||||||
if (headerEl) {
|
if (headerEl) {
|
||||||
var hClone = headerEl.cloneNode(true);
|
var hClone = headerEl.cloneNode(true);
|
||||||
var hRem = hClone.querySelectorAll('button, svg, [class*="icon"], .google-symbols');
|
var hRem = hClone.querySelectorAll('button, svg, [class*="icon"], .google-symbols');
|
||||||
for (var hi = 0; hi < hRem.length; hi++) {
|
for (var hi = 0; hi < hRem.length; hi++) {
|
||||||
if (hRem[hi].parentNode) hRem[hi].parentNode.removeChild(hRem[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));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// v12: If we found a good code text, return it immediately at this depth
|
||||||
|
if (_bestCodeText) {
|
||||||
var parts = [];
|
var parts = [];
|
||||||
if (headerText) parts.push(headerText);
|
if (_bestCodeHeader) parts.push(_bestCodeHeader);
|
||||||
parts.push(codeText);
|
parts.push(_bestCodeText);
|
||||||
log('CONTEXT-OK d='+depth+' src=code trail='+_debugTrail.join(' > '));
|
log('CONTEXT-OK d='+depth+' src=code trail='+_debugTrail.join(' > '));
|
||||||
_lastContextDebug = _debugTrail.join(' > ');
|
_lastContextDebug = _debugTrail.join(' > ');
|
||||||
return parts.join(' — ');
|
return parts.join(' — ');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// v11: Look for substantial text in span/div/p as fallback context
|
// v11: Look for substantial text in span/div/p as fallback context
|
||||||
if (depth <= 8 && !_fallbackText) {
|
if (depth <= 8 && !_fallbackText) {
|
||||||
var textEls = node.querySelectorAll('span, div, p');
|
var textEls = node.querySelectorAll('span, div, p');
|
||||||
|
|||||||
Reference in New Issue
Block a user