fix(bridge): 5 bug fixes for approval signal drop and Discord relay

- DEDUP: add conversation_id guard to prevent cross-session step_index collision
- step_probe: suppress pending when projectName=default (empty window)
- watchCommandsDir: add 3s polling fallback (fs.watch silent fail on Windows)
- auto toggle: write chat_snapshot confirmation back to Discord
- bot on_message: add message ID dedup for Gateway event replay
This commit is contained in:
2026-03-15 08:18:26 +09:00
parent 1f96997831
commit 40e3cd550f
7 changed files with 136 additions and 25 deletions

View File

@@ -223,6 +223,10 @@ function processCommandFile(filePath) {
autoApproveEnabled = !autoApproveEnabled;
}
logToFile(`[AUTO] auto-approve toggled → ${autoApproveEnabled}`);
// Confirm back to Discord
const emoji = autoApproveEnabled ? '🟢' : '🔴';
const mode = autoApproveEnabled ? '자동 승인 활성' : '수동 승인 모드';
writeChatSnapshot(`${emoji} **Extension 확인**: ${mode} (project=${projectName})`);
}
else if (text) {
// Send message to Antigravity — use VS Code command (most reliable)
@@ -243,15 +247,18 @@ function processCommandFile(filePath) {
function watchCommandsDir() {
const cmdDir = path.join(bridgePath, 'commands');
// Process existing files
try {
for (const f of fs.readdirSync(cmdDir)) {
if (f.endsWith('.json')) {
processCommandFile(path.join(cmdDir, f));
const processAllCommands = () => {
try {
for (const f of fs.readdirSync(cmdDir)) {
if (f.endsWith('.json')) {
processCommandFile(path.join(cmdDir, f));
}
}
}
}
catch { }
// Watch for new files
catch { }
};
processAllCommands();
// Watch for new files (may not fire reliably on Windows)
try {
commandsWatcher = fs.watch(cmdDir, (event, filename) => {
if (filename && filename.endsWith('.json') && event === 'rename') {
@@ -263,6 +270,10 @@ function watchCommandsDir() {
});
}
catch { }
// Polling fallback: fs.watch on Windows can silently fail
setInterval(() => {
processAllCommands();
}, 3000);
}
// ─── SDK Integration ───
async function initSDK(context) {
@@ -1992,8 +2003,12 @@ function setupMonitor() {
lastPendingStepIndex = actualIndex;
lastPendingTime = Date.now();
sawRunningAfterPending = false;
// Auto-approve: skip Discord, approve directly
if (autoApproveEnabled) {
// Skip pending for workspace-less AG windows (project=default)
if (projectName === 'default') {
logToFile(`[STEP-PROBE] skip pending: projectName=default (no workspace)`);
}
else if (autoApproveEnabled) {
// Auto-approve: skip Discord, approve directly
logToFile(`[AUTO] auto-approving step=${actualIndex} cmd='${command.substring(0, 60)}'`);
tryApprovalStrategies(true, activeSessionId, ['view_file', 'list_dir', 'find_by_name', 'read_file', 'grep_search', 'replace_file_content', 'write_to_file', 'multi_replace_file_content'].includes(toolName) ? 'file_permission' : toolName, actualIndex);
}
@@ -2055,8 +2070,12 @@ function setupMonitor() {
lastPendingStepIndex = si;
lastPendingTime = Date.now();
sawRunningAfterPending = false;
// Auto-approve: skip Discord, approve directly
if (autoApproveEnabled) {
// Skip pending for workspace-less AG windows (project=default)
if (projectName === 'default') {
logToFile(`[STEP-PROBE] skip pending: projectName=default (no workspace)`);
}
else if (autoApproveEnabled) {
// Auto-approve: skip Discord, approve directly
logToFile(`[AUTO] auto-approving step=${si} cmd='${command.substring(0, 60)}'`);
tryApprovalStrategies(true, activeSessionId, ['view_file', 'list_dir', 'find_by_name', 'read_file', 'grep_search', 'replace_file_content', 'write_to_file', 'multi_replace_file_content'].includes(toolName) ? 'file_permission' : toolName, si);
}
@@ -2718,8 +2737,9 @@ function writePendingApproval(data) {
return;
}
}
// Dedup: skip if step_probe already created pending for same step_index (within window)
// Dedup: skip if step_probe already created pending for same step_index IN SAME SESSION (within window)
if (existing.status === 'pending' && existing.project_name === projectName
&& existing.conversation_id === data.conversation_id // CRITICAL: same session only
&& data.step_index !== undefined && existing.step_index === data.step_index) {
const age = nowMs - (existing.timestamp * 1000);
if (age < DEDUP_WINDOW_MS && age >= 0) {

File diff suppressed because one or more lines are too long

View File

@@ -188,6 +188,10 @@ function processCommandFile(filePath: string) {
autoApproveEnabled = !autoApproveEnabled;
}
logToFile(`[AUTO] auto-approve toggled → ${autoApproveEnabled}`);
// Confirm back to Discord
const emoji = autoApproveEnabled ? '🟢' : '🔴';
const mode = autoApproveEnabled ? '자동 승인 활성' : '수동 승인 모드';
writeChatSnapshot(`${emoji} **Extension 확인**: ${mode} (project=${projectName})`);
} else if (text) {
// Send message to Antigravity — use VS Code command (most reliable)
recentDiscordSentTexts.set(text.trim(), Date.now());
@@ -207,15 +211,19 @@ function watchCommandsDir() {
const cmdDir = path.join(bridgePath, 'commands');
// Process existing files
try {
for (const f of fs.readdirSync(cmdDir)) {
if (f.endsWith('.json')) {
processCommandFile(path.join(cmdDir, f));
const processAllCommands = () => {
try {
for (const f of fs.readdirSync(cmdDir)) {
if (f.endsWith('.json')) {
processCommandFile(path.join(cmdDir, f));
}
}
}
} catch { }
} catch { }
};
// Watch for new files
processAllCommands();
// Watch for new files (may not fire reliably on Windows)
try {
commandsWatcher = fs.watch(cmdDir, (event, filename) => {
if (filename && filename.endsWith('.json') && event === 'rename') {
@@ -226,6 +234,11 @@ function watchCommandsDir() {
}
});
} catch { }
// Polling fallback: fs.watch on Windows can silently fail
setInterval(() => {
processAllCommands();
}, 3000);
}
// ─── SDK Integration ───
@@ -1982,8 +1995,11 @@ function setupMonitor() {
lastPendingStepIndex = actualIndex;
lastPendingTime = Date.now();
sawRunningAfterPending = false;
// Auto-approve: skip Discord, approve directly
if (autoApproveEnabled) {
// Skip pending for workspace-less AG windows (project=default)
if (projectName === 'default') {
logToFile(`[STEP-PROBE] skip pending: projectName=default (no workspace)`);
} else if (autoApproveEnabled) {
// Auto-approve: skip Discord, approve directly
logToFile(`[AUTO] auto-approving step=${actualIndex} cmd='${command.substring(0, 60)}'`);
tryApprovalStrategies(true, activeSessionId, ['view_file', 'list_dir', 'find_by_name', 'read_file', 'grep_search', 'replace_file_content', 'write_to_file', 'multi_replace_file_content'].includes(toolName) ? 'file_permission' : toolName, actualIndex);
} else {
@@ -2043,8 +2059,11 @@ function setupMonitor() {
lastPendingStepIndex = si;
lastPendingTime = Date.now();
sawRunningAfterPending = false;
// Auto-approve: skip Discord, approve directly
if (autoApproveEnabled) {
// Skip pending for workspace-less AG windows (project=default)
if (projectName === 'default') {
logToFile(`[STEP-PROBE] skip pending: projectName=default (no workspace)`);
} else if (autoApproveEnabled) {
// Auto-approve: skip Discord, approve directly
logToFile(`[AUTO] auto-approving step=${si} cmd='${command.substring(0, 60)}'`);
tryApprovalStrategies(true, activeSessionId, ['view_file', 'list_dir', 'find_by_name', 'read_file', 'grep_search', 'replace_file_content', 'write_to_file', 'multi_replace_file_content'].includes(toolName) ? 'file_permission' : toolName, si);
} else {
@@ -2672,8 +2691,9 @@ function writePendingApproval(data: { conversation_id: string; command: string;
return;
}
}
// Dedup: skip if step_probe already created pending for same step_index (within window)
// Dedup: skip if step_probe already created pending for same step_index IN SAME SESSION (within window)
if (existing.status === 'pending' && existing.project_name === projectName
&& existing.conversation_id === data.conversation_id // CRITICAL: same session only
&& data.step_index !== undefined && existing.step_index === data.step_index) {
const age = nowMs - (existing.timestamp * 1000);
if (age < DEDUP_WINDOW_MS && age >= 0) {