fix(http-bridge): move command enrichment before Run filter — fixes context loss (v0.5.43) #task-619
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.42",
|
"version": "0.5.43",
|
||||||
"publisher": "variet",
|
"publisher": "variet",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.100.0"
|
"vscode": "^1.100.0"
|
||||||
|
|||||||
@@ -256,9 +256,28 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(body);
|
const data = JSON.parse(body);
|
||||||
|
|
||||||
// ── Server-side false positive filter ──
|
// ── v11: Command enrichment FIRST — extract actual command from description ──
|
||||||
const cmd = (data.command || '').trim();
|
// Must run before filters so "Always run" with useful description isn't filtered out
|
||||||
// Removed valid AI buttons (Accept, Reject, Allow, Deny) which are now structurally protected by the observer script
|
const rawCmd = (data.command || '').trim();
|
||||||
|
const rawDesc = (data.description || '').trim();
|
||||||
|
const GENERIC_BTN_RE = /^(?:Always\s*)?(?:Run|Allow|Accept|Approve)$/i;
|
||||||
|
let enrichedCmd = rawCmd;
|
||||||
|
let enrichedDesc = rawDesc;
|
||||||
|
if (GENERIC_BTN_RE.test(rawCmd) && rawDesc.length > 10 && rawDesc !== rawCmd) {
|
||||||
|
// Extract the actual command from description (often includes terminal prompt)
|
||||||
|
// Pattern: "…\project_name > actual_command"
|
||||||
|
let extracted = rawDesc;
|
||||||
|
const promptMatch = rawDesc.match(/[>»]\s*(.+)/);
|
||||||
|
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)}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Server-side false positive filter (uses enriched cmd) ──
|
||||||
|
const cmd = enrichedCmd;
|
||||||
const FALSE_POSITIVE_RE = /^(Proceed|Continue|Open|Close|OK|Yes|No|Save|Undo|Redo|Back|Next|More|Less|Got it|Dismiss)$/i;
|
const FALSE_POSITIVE_RE = /^(Proceed|Continue|Open|Close|OK|Yes|No|Save|Undo|Redo|Back|Next|More|Less|Got it|Dismiss)$/i;
|
||||||
if (FALSE_POSITIVE_RE.test(cmd)) {
|
if (FALSE_POSITIVE_RE.test(cmd)) {
|
||||||
ctx.logToFile(`[HTTP] filtered false positive: "${cmd}"`);
|
ctx.logToFile(`[HTTP] filtered false positive: "${cmd}"`);
|
||||||
@@ -267,7 +286,7 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// "Run" button → step_probe handles these with full command detail
|
// "Run" button → step_probe handles these with full command detail
|
||||||
// Only filter when step_probe IS actively tracking a session
|
// Only filter when step_probe IS actively tracking AND cmd is still generic button text
|
||||||
if (/^(?:Always\s*)?Run\b/i.test(cmd)) {
|
if (/^(?:Always\s*)?Run\b/i.test(cmd)) {
|
||||||
if (ctx.activeSessionId && (!ctx.sessionStalled || ctx.lastPendingStepIndex >= 0)) {
|
if (ctx.activeSessionId && (!ctx.sessionStalled || ctx.lastPendingStepIndex >= 0)) {
|
||||||
ctx.logToFile(`[HTTP] filtered "Run" — ${!ctx.sessionStalled ? 'not stalled' : 'step_probe pending exists'} (session=${ctx.activeSessionId.substring(0, 8)})`);
|
ctx.logToFile(`[HTTP] filtered "Run" — ${!ctx.sessionStalled ? 'not stalled' : 'step_probe pending exists'} (session=${ctx.activeSessionId.substring(0, 8)})`);
|
||||||
@@ -283,16 +302,20 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
const pending: Record<string, any> = {
|
const pending: Record<string, any> = {
|
||||||
...data,
|
...data,
|
||||||
request_id: rid,
|
request_id: rid,
|
||||||
|
command: enrichedCmd,
|
||||||
|
description: enrichedDesc,
|
||||||
conversation_id: ctx.activeSessionId || '',
|
conversation_id: ctx.activeSessionId || '',
|
||||||
timestamp: Date.now() / 1000,
|
timestamp: Date.now() / 1000,
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
project_name: ctx.projectName,
|
project_name: ctx.projectName,
|
||||||
auto_detected: true,
|
auto_detected: true,
|
||||||
source: 'dom_observer',
|
source: 'dom_observer',
|
||||||
|
step_type: data.step_type,
|
||||||
|
buttons: data.buttons,
|
||||||
step_index: ctx.lastPendingStepIndex >= 0 ? ctx.lastPendingStepIndex : undefined,
|
step_index: ctx.lastPendingStepIndex >= 0 ? ctx.lastPendingStepIndex : undefined,
|
||||||
};
|
};
|
||||||
// File permission: inject multi-choice buttons
|
// File permission: inject multi-choice buttons
|
||||||
const cmdLower = (data.command || '').toLowerCase();
|
const cmdLower = enrichedCmd.toLowerCase();
|
||||||
if (cmdLower.includes('allow') && !pending.buttons) {
|
if (cmdLower.includes('allow') && !pending.buttons) {
|
||||||
// Dedup: skip if another file_permission pending was created within 10s
|
// Dedup: skip if another file_permission pending was created within 10s
|
||||||
const nowMs = Date.now();
|
const nowMs = Date.now();
|
||||||
@@ -311,25 +334,8 @@ function _handlePending(req: any, res: any, ctx: HttpBridgeContext) {
|
|||||||
];
|
];
|
||||||
pending.step_type = 'file_permission';
|
pending.step_type = 'file_permission';
|
||||||
// Clean description: remove button labels from text
|
// Clean description: remove button labels from text
|
||||||
const rawDesc = (data.description || data.command || '').replace(/Deny|Allow Once|Allow This Conversation/gi, '').trim();
|
const cleanDesc = enrichedDesc.replace(/Deny|Allow Once|Allow This Conversation/gi, '').trim();
|
||||||
pending.command = `파일 접근 권한${rawDesc ? ': ' + rawDesc : ''}`;
|
pending.command = `파일 접근 권한${cleanDesc ? ': ' + cleanDesc : ''}`;
|
||||||
}
|
|
||||||
// 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()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user