fix(observer/bridge): v13 _promptOnlySkipped fallback guard + generic button no-context filter (v0.5.46) #task-619

This commit is contained in:
Variet Worker
2026-04-15 13:26:45 +09:00
parent 87c99c7243
commit 2e32be96fe
6 changed files with 80 additions and 10 deletions

View File

@@ -279,9 +279,20 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
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
// v13: Enrichment failed — description is prompt-only or empty
// If cmd is still generic button text, unconditionally filter it
ctx.logToFile(`[HTTP] enrichment skipped (prompt-only): "${rawCmd}" desc="${rawDesc.substring(0, 60)}"`);
ctx.logToFile(`[HTTP] filtered generic+prompt-only: "${rawCmd}"`);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: false, filtered: true, reason: 'generic_btn_prompt_only' }));
return;
}
} else if (GENERIC_BTN_RE.test(rawCmd) && (rawDesc.length <= 10 || rawDesc === rawCmd)) {
// v13: Generic button with no useful description (observer prompt-only context)
ctx.logToFile(`[HTTP] filtered generic button no-context: "${rawCmd}" desc="${rawDesc.substring(0, 30)}"`);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: false, filtered: true, reason: 'generic_btn_no_context' }));
return;
}
// ── Server-side false positive filter (uses enriched cmd) ──

View File

@@ -1,7 +1,7 @@
export function generateApprovalObserverScript(_port: number): string {
return `
// ── Gravity Bridge v12: Prompt-Skip + Multi-CodeEl Context Extraction ──
// v12: Skip prompt-only code text, try all codeEls, improved enrichment fallback
// ── Gravity Bridge v13: Prompt-Skip + Fallback Guard ──
// v13: When all code els are prompt-only, disable fallback text collection entirely
(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('v12 Script loaded — Prompt-Skip Context Extraction');
log('v13 Script loaded — Prompt-Skip + Fallback Guard');
// DIAGNOSTIC BEACON: immediate POST to confirm script execution in renderer
try {
@@ -109,30 +109,36 @@ export function generateApprovalObserverScript(_port: number): string {
return null;
}
// v12: Climb DOM tree to find context near the button
// v13: 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 = /^[^\\n]*[\\/>\xbb$#]\\s*$/;
// PROMPT_ONLY: matches terminal prompts like "...\project >" or "PS C:\...">" with no actual command
var PROMPT_ONLY_RE = /^[^\\n]*[\\/\>\xbb$#]\\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 = '';
var _sawCodeEls = false; // v13: did we encounter any code/pre elements?
var _promptOnlySkipped = false; // v13: were ALL code elements prompt-only?
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)
var codeEls = node.querySelectorAll('pre, code, [class*="terminal"]');
_debugTrail.push('d'+depth+':tag='+((node.tagName||'?').toLowerCase())+',cls='+(((typeof node.className==='string')?node.className:'').substring(0,60))+',codeEls='+codeEls.length);
var _depthHadCode = false;
var _depthAllPrompt = true;
for (var ci = 0; ci < codeEls.length; ci++) {
var codeText = cleanLines((codeEls[ci].textContent || '').trim().substring(0, 500));
if (!codeText || codeText.length <= 5) continue;
if (/^Running\\s*\\d/i.test(codeText)) continue;
_depthHadCode = true;
// 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;
}
_depthAllPrompt = false;
// This code element has actual content - capture it
if (!_bestCodeText || codeText.length > _bestCodeText.length) {
_bestCodeText = codeText;
@@ -148,6 +154,11 @@ export function generateApprovalObserverScript(_port: number): string {
}
}
}
// v13: Track whether code elements were seen and all were prompt-only
if (_depthHadCode) {
_sawCodeEls = true;
if (_depthAllPrompt) _promptOnlySkipped = true;
}
// v12: If we found a good code text, return it immediately at this depth
if (_bestCodeText) {
var parts = [];
@@ -157,8 +168,12 @@ export function generateApprovalObserverScript(_port: number): string {
_lastContextDebug = _debugTrail.join(' > ');
return parts.join(' — ');
}
// v11: Look for substantial text in span/div/p as fallback context
if (depth <= 8 && !_fallbackText) {
// v13: If we saw code elements but all were prompt-only, do NOT collect fallback text
// This prevents capturing chat messages / UI text from nearby DOM elements
if (_promptOnlySkipped) {
_debugTrail.push('fallback_blocked_prompt_only');
} else if (depth <= 8 && !_fallbackText) {
// v11: Look for substantial text in span/div/p as fallback context
var textEls = node.querySelectorAll('span, div, p');
for (var ti = 0; ti < Math.min(textEls.length, 20); ti++) {
var tText = (textEls[ti].textContent || '').trim();
@@ -175,6 +190,13 @@ export function generateApprovalObserverScript(_port: number): string {
}
node = node.parentElement;
}
// v13: If code elements existed but were all prompt-only, return just button text
// This ensures http-bridge's Run/Always run filter can handle it properly
if (_promptOnlySkipped) {
log('CONTEXT-PROMPT-ONLY trail='+_debugTrail.join(' > '));
_lastContextDebug = _debugTrail.join(' > ');
return cleanButtonText(btn);
}
// v11: Use fallback text from span/div/p if available
if (_fallbackText && _fallbackText.length > 5) {
log('CONTEXT-OK src=fallback trail='+_debugTrail.join(' > '));