fix(observer): regex → 문자열 비교로 isGenericDesc 수정 — template literal escaping 회피 (v0.5.84)

This commit is contained in:
Variet Worker
2026-04-19 07:02:04 +09:00
parent d027562f17
commit 1662ac4f6b
3 changed files with 51 additions and 45 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "gravity-bridge",
"version": "0.5.83",
"version": "0.5.84",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gravity-bridge",
"version": "0.5.83",
"version": "0.5.84",
"dependencies": {
"cheerio": "^1.2.0",
"ws": "^8.19.0"

View File

@@ -2,7 +2,7 @@
"name": "gravity-bridge",
"displayName": "Gravity Bridge",
"description": "Discord-based unified approval system for Antigravity AI interactions.",
"version": "0.5.83",
"version": "0.5.84",
"publisher": "variet",
"engines": {
"vscode": "^1.100.0"

View File

@@ -1,6 +1,6 @@
export function generateApprovalObserverScript(_port: number): string {
return `
// ── Gravity Bridge v17: Always Run Auto-Approve + Retry Detection ──
// ?€?€ Gravity Bridge v17: Always Run Auto-Approve + Retry Detection ?€?€
// v17: "Always run" auto-approve at bridge level + Retry button relay to Discord
(function(){
'use strict';
@@ -16,7 +16,7 @@ export function generateApprovalObserverScript(_port: number): string {
try { fetch(BASE+'/log', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({msg:m.substring(0,2000)})}); } catch(e){}
}
}
log('v17 Script loaded Always Run Auto-Approve + Retry Detection');
log('v17 Script loaded ??Always Run Auto-Approve + Retry Detection');
// DIAGNOSTIC BEACON: immediate POST to confirm script execution in renderer
try {
@@ -39,7 +39,7 @@ export function generateApprovalObserverScript(_port: number): string {
}
}
// ── Noise filter: lines that are UI artifacts, not real content ──
// ?€?€ Noise filter: lines that are UI artifacts, not real content ?€?€
var NOISE_RE = new RegExp(
'^(' +
'chevron_right|chevron_left|arrow_drop_down|arrow_drop_up|arrow_right|arrow_left|' +
@@ -114,10 +114,10 @@ export function generateApprovalObserverScript(_port: number): string {
return type+'|'+txt+'|'+idx;
}
// ══════════════════════════════════════════════════════════════════
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
// v7: STEP-AWARE CONTEXT EXTRACTION
// Find the closest [data-step-index] ancestor, extract step info
// ══════════════════════════════════════════════════════════════════
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
function getStepContainer(el) {
var node = el;
@@ -129,9 +129,9 @@ export function generateApprovalObserverScript(_port: number): string {
}
// v14: Climb DOM tree to find context near the button
// STRICT SCOPE: Only 5 levels up beyond that we're in unrelated UI territory.
// STRICT SCOPE: Only 5 levels up ??beyond that we're in unrelated UI territory.
// JUNK FILTERS: CSS rules, source code, Material icon gluing are all rejected.
// NO FALLBACK: span/div/p text collection is removed entirely it always grabs chat/UI text.
// NO FALLBACK: span/div/p text collection is removed entirely ??it always grabs chat/UI text.
var PROMPT_ONLY_RE = /^[^\\n]*[\\/\>\\xbb$#]\\s*$/;
// v14: Detect CSS rules, JS source code, or extension internals in code text
var JUNK_CODE_RE = /(!important|::selection|background-color:|var\\(--|font-size:|border-[a-z]|padding:|margin:|display:\\s|\\{[^}]*:[^}]*\\}|===|!==|\\|\\||\\bfunction\\s*\\(|\\bconst\\s+\\w+\\s*=|\\bvar\\s+\\w+\\s*=|\\bif\\s*\\(|\\breturn\\b|\\bimport\\s|\\bexport\\s|\\bclass\\s+\\w|\\bnew\\s+\\w|\\.test\\(|\\.match\\(|\\.replace\\(|_RE[.\\s]|\\brawDesc\\b|\\brawCmd\\b|\\benrichedCmd\\b|\\bquerySelector)/;
@@ -144,7 +144,7 @@ export function generateApprovalObserverScript(_port: number): string {
var _bestCodeHeader = '';
var _sawCodeEls = false;
var _allSkipped = true;
// v22: Increased from 5 to 10 AG Native command display (SRi) can be many levels up
// v22: Increased from 5 to 10 ??AG Native command display (SRi) can be many levels up
for (var depth = 0; depth < 10 && node; depth++) {
if (!node.querySelector) { _debugTrail.push('d'+depth+':noQS'); node = node.parentElement; continue; }
// v22: Prioritize pre.font-mono (AG Native command line display from SRi component)
@@ -205,7 +205,7 @@ export function generateApprovalObserverScript(_port: number): string {
_debugTrail.push('sibling_d'+depth+':tag='+siblings[si].tagName.toLowerCase()+',code='+sibCode.substring(0,40));
_bestCodeText = sibCode;
_allSkipped = false;
// Found in sibling return immediately
// Found in sibling ??return immediately
var sibParts = [];
var sibHdr = siblings[si].querySelector('h1, h2, h3, [class*="header"], [class*="title"], [class*="cursor-pointer"]');
if (sibHdr) sibParts.push(cleanLines((sibHdr.textContent || '').trim().substring(0, 200)));
@@ -234,7 +234,7 @@ export function generateApprovalObserverScript(_port: number): string {
function extractStepContext(btn) {
var stepEl = getStepContainer(btn);
if (!stepEl) {
// v9 FALLBACK: no data-step-index climb DOM for pre/code blocks
// v9 FALLBACK: no data-step-index ??climb DOM for pre/code blocks
return extractContextFromNearby(btn);
}
@@ -270,7 +270,7 @@ export function generateApprovalObserverScript(_port: number): string {
if (codeText && !headerText.includes(codeText.substring(0, 20))) parts.push(codeText);
if (ariaLabel && ariaLabel.length > 5 && !headerText.includes(ariaLabel)) parts.push(ariaLabel);
var result = parts.join(' ');
var result = parts.join(' ??');
if (!result) result = cleanButtonText(btn);
return 'Step #' + stepIdx + ': ' + result;
}
@@ -286,7 +286,7 @@ export function generateApprovalObserverScript(_port: number): string {
for(var i=0; i<ACTION_WORDS.length; i++) {
if(txt.indexOf(ACTION_WORDS[i]) !== -1) return true;
}
// v9: Removed "Running N commands" it's a group header, not an approval button
// v9: Removed "Running N commands" ??it's a group header, not an approval button
return false;
}
function isRejectBtn(txt) {
@@ -366,18 +366,18 @@ export function generateApprovalObserverScript(_port: number): string {
startObserver();
});
// ══════════════════════════════════════════════════════════════════
// v8: FULL DOM STRUCTURE DUMP (unconditional no selector dependency)
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
// v8: FULL DOM STRUCTURE DUMP (unconditional ??no selector dependency)
// Dumps entire document.body tree to /dump-html for real DOM analysis
// Auto-triggers at 5s, 15s, 60s after load to capture React-rendered state
// ══════════════════════════════════════════════════════════════════
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
var _dumpCount=0;
var MAX_DUMPS=8;
var _conversationDumpCount=0;
function walkNode(el, depth, maxDepth, maxChildren) {
if (depth > maxDepth) return {tag:'…',text:'depth limit'};
if (depth > maxDepth) return {tag:'??,text:'depth limit'};
if (!el || !el.tagName) return null;
var info = {
tag: el.tagName ? el.tagName.toLowerCase() : '#text',
@@ -413,7 +413,7 @@ export function generateApprovalObserverScript(_port: number): string {
if (childInfo) info.children.push(childInfo);
}
if (el.children.length > limit) {
info.children.push({tag: '…', text: '+' + (el.children.length - limit) + ' more children'});
info.children.push({tag: '??, text: '+' + (el.children.length - limit) + ' more children'});
}
}
return info;
@@ -508,11 +508,11 @@ export function generateApprovalObserverScript(_port: number): string {
setTimeout(function(){ log('Auto-dump @60s'); dumpDOMStructure(); }, 60000);
}
// ══════════════════════════════════════════════════════════════════
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
// v15: AG-NATIVE + CASCADE DUAL CHAT BODY SCANNING
// AG Native: #conversation > ... > .leading-relaxed.select-text
// Cascade: [data-testid="conversation-view"] > [data-step-index]
// ══════════════════════════════════════════════════════════════════
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
var _lastScrapedStepIndex = -1;
var _lastStepText = '';
@@ -684,7 +684,7 @@ export function generateApprovalObserverScript(_port: number): string {
}
}
// v19: Post-process wrap markdown table patterns in code blocks for Discord
// v19: Post-process ??wrap markdown table patterns in code blocks for Discord
// AG Native renders tables as divs, not <table> HTML, so DOM-level handler can't catch them.
// Detect consecutive lines with pipe separators (| col1 | col2 |) and wrap in code block fences
var bt = String.fromCharCode(96, 96, 96);
@@ -749,12 +749,12 @@ export function generateApprovalObserverScript(_port: number): string {
// One-time DOM dump
dumpDOMStructure();
// ── STRATEGY 1: AG Native #conversation or .antigravity-agent-side-panel ──
// ?€?€ STRATEGY 1: AG Native ??#conversation or .antigravity-agent-side-panel ?€?€
var cv = document.querySelector('#conversation');
if (!cv) {
cv = document.querySelector('.antigravity-agent-side-panel');
}
// v19: Fallback find conversation by tracing from known content elements
// v19: Fallback ??find conversation by tracing from known content elements
if (!cv) {
var probe = document.querySelector('.leading-relaxed.select-text') || document.querySelector('.text-ide-message-block-bot-color');
if (probe) {
@@ -818,11 +818,11 @@ export function generateApprovalObserverScript(_port: number): string {
}
// v22: AI response = .leading-relaxed.select-text, User message = .select-text.rounded-lg (Esn component, msn class)
// Source: jetskiAgent/main.js msn="bg-gray-500/10 border border-gray-500/20 p-2 rounded-lg w-full text-sm select-text"
// Source: jetskiAgent/main.js ??msn="bg-gray-500/10 border border-gray-500/20 p-2 rounded-lg w-full text-sm select-text"
var responseBlocks = cv.querySelectorAll('.leading-relaxed.select-text, .select-text.rounded-lg');
if (responseBlocks.length > 0) {
// v22: Filter out thinking/reasoning blocks they have ancestor with max-h-[200px]
// v22: Filter out thinking/reasoning blocks ??they have ancestor with max-h-[200px]
// These are internal AI reasoning and should NOT be relayed to Discord
var filteredBlocks = [];
for (var fbi = 0; fbi < responseBlocks.length; fbi++) {
@@ -847,7 +847,7 @@ export function generateApprovalObserverScript(_port: number): string {
if (lastBlock.dataset.agChatScraped === 'true' || lastBlock.dataset.agChatScraped === 'pending') {
// Check for NEW blocks since last scrape
if (filteredBlocks.length > _lastResponseBlockCount) {
// New block appeared process it
// New block appeared ??process it
for (var rbi = filteredBlocks.length - 1; rbi >= 0; rbi--) {
if (filteredBlocks[rbi].dataset.agChatScraped !== 'true' && filteredBlocks[rbi].dataset.agChatScraped !== 'pending') {
lastBlock = filteredBlocks[rbi];
@@ -896,7 +896,7 @@ export function generateApprovalObserverScript(_port: number): string {
var waitTime = isUser ? 500 : 3000;
if (Date.now() - _lastStepTextTime < waitTime) return; // Still waiting
// v21: DOM-based chat relay RE-ENABLED GetCascadeTrajectorySteps does NOT
// v21: DOM-based chat relay RE-ENABLED ??GetCascadeTrajectorySteps does NOT
// return steps for in-progress cascades, making Step Probe RT-CAPTURE useless.
// Observer DOM extraction is the ONLY real-time path for AI response relay.
_lastStepTextSent = true;
@@ -913,11 +913,11 @@ export function generateApprovalObserverScript(_port: number): string {
.catch(function(e) { el.dataset.agChatScraped = 'false'; log('AG-Native chat send error: ' + e.message); });
})(lastBlock, blockText, filteredBlocks.length, role);
}
return; // AG Native path handled don't fall through to Cascade path
return; // AG Native path handled ??don't fall through to Cascade path
}
}
// ── STRATEGY 2: Cascade [data-testid="conversation-view"] ──
// ?€?€ STRATEGY 2: Cascade ??[data-testid="conversation-view"] ?€?€
cv = document.querySelector('[data-testid="conversation-view"]');
if (!cv) {
// FALLBACK: Try older selectors
@@ -989,7 +989,7 @@ export function generateApprovalObserverScript(_port: number): string {
if (_lastStepTextSent) continue;
if (Date.now() - _lastStepTextTime < 3000) break; // Still waiting
// Content is stable send it
// Content is stable ??send it
_lastStepTextSent = true;
_lastScrapedStepIndex = stepIdx;
stepEl.dataset.agChatScraped = 'pending';
@@ -1007,9 +1007,9 @@ export function generateApprovalObserverScript(_port: number): string {
}
}
// ══════════════════════════════════════════════════════════════════
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
// BUTTON SCANNING (approval detection)
// ══════════════════════════════════════════════════════════════════
// ?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧?먥븧
function scan(){
if(!_ready)return;
@@ -1018,7 +1018,7 @@ export function generateApprovalObserverScript(_port: number): string {
var allBtns=document.querySelectorAll('button, [role="button"], a.monaco-button, .monaco-text-button, vscode-button');
if(!allBtns.length)return;
// v25: One-shot debug find Accept/Reject elements in ANY tag (run once per 30s)
// v25: One-shot debug ??find Accept/Reject elements in ANY tag (run once per 30s)
if (!scan._lastAcceptScan || now - scan._lastAcceptScan > 30000) {
scan._lastAcceptScan = now;
var allEls = document.querySelectorAll('button, a, div, span, [role="button"]');
@@ -1037,10 +1037,10 @@ export function generateApprovalObserverScript(_port: number): string {
var txt=cleanButtonText(b);
if(txt.length <= 1) continue;
// v9: Skip group header buttons not approval buttons
// v9: Skip group header buttons ??not approval buttons
if (/^Running\\s*\\d+\\s*commands?$/i.test(txt)) continue;
// v24: Relaxed visibility check Accept all/Reject all buttons in AG Native
// v24: Relaxed visibility check ??Accept all/Reject all buttons in AG Native
// editor bottom bar may have offsetParent===null (different rendering layer)
var isDiffReviewBtn = txt.includes('Accept') || txt === 'Reject all';
if(!isDiffReviewBtn && (b.disabled||b.hidden||(!b.offsetParent&&b.style.display!=='fixed')))continue;
@@ -1081,15 +1081,20 @@ export function generateApprovalObserverScript(_port: number): string {
_sent[groupKey]={rid:rid,ts:now};
for(var mk=0;mk<bidList.length;mk++)_sent[bidList[mk]]={rid:rid,ts:now};
// v26: Deferred context — if desc is generic ("Always run", button text only),
// v26: Deferred context (string match, not regex — avoids template literal escaping issues)
function _isGenericDesc(d) {
var t = d.trim().toLowerCase();
return t === 'always run' || t === 'run' || t === 'allow' || t === 'accept' || t === 'retry' || t === txt.toLowerCase();
}
// v26: Deferred context ??if desc is generic ("Always run", button text only),
// delay 500ms and re-extract to allow DOM rendering to complete
var isGenericDesc = /^(Always\\s+run|Run|Allow|Accept|Retry)$/i.test(desc.trim()) || desc === txt;
var isGenericDesc = _isGenericDesc(desc);
if (isGenericDesc && matchedType === 'command') {
log('DEFERRED-CONTEXT: desc="' + desc.substring(0,30) + '" waiting 500ms for DOM render');
log('DEFERRED-CONTEXT: desc="' + desc.substring(0,30) + '" ??waiting 500ms for DOM render');
(function(b2, rid2, btnRefs2, bidList2, groupKey2, txt2, type2, buttonsArr2) {
setTimeout(function() {
var retryDesc = extractContext(b2);
var finalDesc = /^(Always\\s+run|Run|Allow|Accept|Retry)$/i.test(retryDesc.trim()) ? desc : retryDesc;
var finalDesc = _isGenericDesc(retryDesc) ? desc : retryDesc;
log('DEFERRED-RESULT: "' + finalDesc.substring(0,80) + '"');
var payload = {
request_id: rid2,
@@ -1229,7 +1234,7 @@ export function generateApprovalObserverScript(_port: number): string {
function startObserver(){
if(_obs)return;
log('startObserver() scheduling auto-dumps and mutation observer');
log('startObserver() ??scheduling auto-dumps and mutation observer');
scheduleAutoDumps();
new MutationObserver(function(mutations){
for(var i=0;i<mutations.length;i++){
@@ -1241,7 +1246,7 @@ export function generateApprovalObserverScript(_port: number): string {
}).observe(document.body,{childList:true,subtree:true});
setInterval(scheduleScan,3000);
// ── TRIGGER-CLICK POLLING ──
// ?€?€ TRIGGER-CLICK POLLING ?€?€
(function pollTriggerClick(){
if(_ready&&BASE){
fetch(BASE+'/trigger-click?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
@@ -1264,12 +1269,12 @@ export function generateApprovalObserverScript(_port: number): string {
setTimeout(pollTriggerClick, 2000);
})();
// ── DEEP-INSPECT POLLING (v8: full body dump) ──
// ?€?€ DEEP-INSPECT POLLING (v8: full body dump) ?€?€
(function pollDeepInspect(){
if(_ready&&BASE){
fetch(BASE+'/deep-inspect-trigger?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
if(!d.inspect)return;
log('Deep inspect triggered full body dump');
log('Deep inspect triggered ??full body dump');
// Force a fresh DOM dump
_dumpCount = Math.max(0, _dumpCount - 1); // allow one more dump
dumpDOMStructure();
@@ -1319,3 +1324,4 @@ export function generateApprovalObserverScript(_port: number): string {
})();
`;
}