feat(observer): v11 — enhanced context extraction + 5-level sibling search + command enrichment (v0.5.42) #task-619
- extractContextFromNearby: span/div/p text fallback when pre/code not found (depth 0-8) - collectSiblingButtons: extended to 5 parent levels, stop only when both action+reject found - http-bridge: command enrichment — swap generic button text with actual command from description - Fixes: cmd='Always run' now shows actual command text, Cancel button detection improved
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.41",
|
"version": "0.5.42",
|
||||||
"publisher": "variet",
|
"publisher": "variet",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.100.0"
|
"vscode": "^1.100.0"
|
||||||
|
|||||||
@@ -314,6 +314,23 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
const rawDesc = (data.description || data.command || '').replace(/Deny|Allow Once|Allow This Conversation/gi, '').trim();
|
const rawDesc = (data.description || data.command || '').replace(/Deny|Allow Once|Allow This Conversation/gi, '').trim();
|
||||||
pending.command = `파일 접근 권한${rawDesc ? ': ' + rawDesc : ''}`;
|
pending.command = `파일 접근 권한${rawDesc ? ': ' + rawDesc : ''}`;
|
||||||
}
|
}
|
||||||
|
// v11: Command enrichment — if command is just button text but description has actual content,
|
||||||
|
// swap them so Discord shows what's actually being executed
|
||||||
|
const descText = (pending.description || data.description || '').trim();
|
||||||
|
const cmdText = (pending.command || data.command || '').trim();
|
||||||
|
const GENERIC_BTN_RE = /^(?:Always\s*)?(?:Run|Allow|Accept|Approve)$/i;
|
||||||
|
if (GENERIC_BTN_RE.test(cmdText) && descText.length > 10 && descText !== cmdText) {
|
||||||
|
// Extract the actual command from description (often includes terminal prompt)
|
||||||
|
// Pattern: "…\project_name > actual_command"
|
||||||
|
let enrichedCmd = descText;
|
||||||
|
const promptMatch = descText.match(/[>»]\s*(.+)/);
|
||||||
|
if (promptMatch && promptMatch[1].trim().length > 3) {
|
||||||
|
enrichedCmd = promptMatch[1].trim();
|
||||||
|
}
|
||||||
|
pending.command = enrichedCmd.substring(0, 200);
|
||||||
|
pending.description = `[${cmdText}] ${descText}`;
|
||||||
|
ctx.logToFile(`[HTTP] command enriched: "${cmdText}" → "${enrichedCmd.substring(0, 60)}"`);
|
||||||
|
}
|
||||||
// WS dispatch
|
// WS dispatch
|
||||||
if (ctx.wsBridge && ctx.wsBridge.isConnected()) {
|
if (ctx.wsBridge && ctx.wsBridge.isConnected()) {
|
||||||
ctx.wsBridge.sendPending({
|
ctx.wsBridge.sendPending({
|
||||||
@@ -327,7 +344,8 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
});
|
});
|
||||||
ctx.logToFile(`[HTTP-WS] pending sent via WS: ${rid}`);
|
ctx.logToFile(`[HTTP-WS] pending sent via WS: ${rid}`);
|
||||||
}
|
}
|
||||||
ctx.logToFile(`[HTTP] pending created: ${rid} cmd="${data.command}" btns=${(data.buttons || []).length} ctx="${(data.description || '').substring(0, 50)}"`);
|
ctx.logToFile(`[HTTP] pending created: ${rid} cmd="${pending.command || data.command}" btns=${(pending.buttons || data.buttons || []).length} ctx="${(pending.description || data.description || '').substring(0, 80)}"`);
|
||||||
|
|
||||||
if (data._debug_trail) {
|
if (data._debug_trail) {
|
||||||
ctx.logToFile(`[HTTP-DIAG] trail: ${data._debug_trail.substring(0, 500)}`);
|
ctx.logToFile(`[HTTP-DIAG] trail: ${data._debug_trail.substring(0, 500)}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export function generateApprovalObserverScript(_port: number): string {
|
export function generateApprovalObserverScript(_port: number): string {
|
||||||
return `
|
return `
|
||||||
// ── Gravity Bridge v9: Context-Aware AG Native Parser ──
|
// ── Gravity Bridge v11: Enhanced Context Extraction ──
|
||||||
// v9: Fixed 'Running N commands' false trigger + DOM-climbing context extraction
|
// v11: Extended span/div/p fallback for context, 5-level sibling search, improved command display
|
||||||
(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('v9 Script loaded — Context-Aware AG Native Parser');
|
log('v11 Script loaded — Enhanced 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,11 +109,12 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// v9: Climb DOM tree to find pre/code content near the button (no data-step-index needed)
|
// v11: Climb DOM tree to find context near the button
|
||||||
// v10-diag: Added diagnostic trail logging
|
// Priority: pre/code > [class*=terminal] > substantial span/div/p text > aria-label > button text
|
||||||
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)
|
||||||
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)
|
||||||
@@ -136,30 +137,38 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
var parts = [];
|
var parts = [];
|
||||||
if (headerText) parts.push(headerText);
|
if (headerText) parts.push(headerText);
|
||||||
parts.push(codeText);
|
parts.push(codeText);
|
||||||
log('CONTEXT-OK d='+depth+' trail='+_debugTrail.join(' > '));
|
log('CONTEXT-OK d='+depth+' src=code trail='+_debugTrail.join(' > '));
|
||||||
|
_lastContextDebug = _debugTrail.join(' > ');
|
||||||
return parts.join(' — ');
|
return parts.join(' — ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// v10-diag: also look for any text-bearing elements (span, div, p) with substantial text
|
// v11: Look for substantial text in span/div/p as fallback context
|
||||||
if (depth <= 5) {
|
if (depth <= 8 && !_fallbackText) {
|
||||||
var textEls = node.querySelectorAll('span, div, p');
|
var textEls = node.querySelectorAll('span, div, p');
|
||||||
var foundTexts = [];
|
for (var ti = 0; ti < Math.min(textEls.length, 20); ti++) {
|
||||||
for (var ti = 0; ti < Math.min(textEls.length, 10); ti++) {
|
|
||||||
var tText = (textEls[ti].textContent || '').trim();
|
var tText = (textEls[ti].textContent || '').trim();
|
||||||
if (tText.length > 10 && tText.length < 300 && !isNoiseLine(tText)) {
|
if (tText.length > 10 && tText.length < 500 && !isNoiseLine(tText)) {
|
||||||
foundTexts.push(tText.substring(0, 80));
|
// Skip if it's just the button text itself
|
||||||
|
var btnTxt = cleanButtonText(btn);
|
||||||
|
if (tText !== btnTxt && tText.indexOf(btnTxt) !== 0) {
|
||||||
|
_fallbackText = tText.substring(0, 300);
|
||||||
|
_debugTrail.push('fallback_d='+depth+':'+_fallbackText.substring(0,40));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (foundTexts.length > 0) {
|
|
||||||
_debugTrail.push('texts=['+foundTexts.join('|')+']');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
node = node.parentElement;
|
node = node.parentElement;
|
||||||
}
|
}
|
||||||
|
// v11: Use fallback text from span/div/p if available
|
||||||
|
if (_fallbackText && _fallbackText.length > 5) {
|
||||||
|
log('CONTEXT-OK src=fallback trail='+_debugTrail.join(' > '));
|
||||||
|
_lastContextDebug = _debugTrail.join(' > ');
|
||||||
|
return cleanLines(_fallbackText);
|
||||||
|
}
|
||||||
// Last resort: try aria-label or title on the button
|
// Last resort: try aria-label or title on the button
|
||||||
var ariaLabel = btn.getAttribute('aria-label') || btn.getAttribute('title') || '';
|
var ariaLabel = btn.getAttribute('aria-label') || btn.getAttribute('title') || '';
|
||||||
log('CONTEXT-FAIL trail='+_debugTrail.join(' > '));
|
log('CONTEXT-FAIL trail='+_debugTrail.join(' > '));
|
||||||
// v10-diag: dump button ancestor chain as diagnostic
|
|
||||||
_lastContextDebug = _debugTrail.join(' > ');
|
_lastContextDebug = _debugTrail.join(' > ');
|
||||||
if (ariaLabel && ariaLabel.length > 5) return ariaLabel;
|
if (ariaLabel && ariaLabel.length > 5) return ariaLabel;
|
||||||
return cleanButtonText(btn);
|
return cleanButtonText(btn);
|
||||||
@@ -233,12 +242,13 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
|
|
||||||
function collectSiblingButtons(container,triggerBtn){
|
function collectSiblingButtons(container,triggerBtn){
|
||||||
if(!container)return [];
|
if(!container)return [];
|
||||||
// v9: Try multiple container levels (parent → grandparent → great-grandparent)
|
// v11: Try 5 container levels to find Cancel and other approval buttons
|
||||||
// to find all related approval buttons in wider DOM context
|
|
||||||
var containers = [container];
|
var containers = [container];
|
||||||
if (container.parentElement) containers.push(container.parentElement);
|
var cur = container;
|
||||||
if (container.parentElement && container.parentElement.parentElement)
|
for (var lvl = 0; lvl < 5 && cur.parentElement; lvl++) {
|
||||||
containers.push(container.parentElement.parentElement);
|
cur = cur.parentElement;
|
||||||
|
containers.push(cur);
|
||||||
|
}
|
||||||
var result=[];
|
var result=[];
|
||||||
var seen={};
|
var seen={};
|
||||||
for(var ci=0;ci<containers.length;ci++){
|
for(var ci=0;ci<containers.length;ci++){
|
||||||
@@ -256,8 +266,13 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
seen[stxt]=true;
|
seen[stxt]=true;
|
||||||
result.push({btn:sb,text:stxt,isPrimary:(sb===triggerBtn)});
|
result.push({btn:sb,text:stxt,isPrimary:(sb===triggerBtn)});
|
||||||
}
|
}
|
||||||
// If we found action buttons at this level, don't go wider
|
// v11: Only stop if we found BOTH action AND reject buttons at this level
|
||||||
if(result.length > 0) break;
|
var hasAction = false, hasReject = false;
|
||||||
|
for (var ri=0;ri<result.length;ri++) {
|
||||||
|
if (isActionBtn(result[ri].text)) hasAction = true;
|
||||||
|
if (isRejectBtn(result[ri].text)) hasReject = true;
|
||||||
|
}
|
||||||
|
if(hasAction && hasReject) break;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user