fix(pipeline): resolve SafeToAutoRun deadlock and sync freezing (v0.5.20) (#589)

This commit is contained in:
Variet Worker
2026-04-08 07:28:48 +09:00
parent 13f13ee243
commit 2eb1fbb6b7
16 changed files with 821 additions and 61 deletions

View File

@@ -105,12 +105,13 @@ function detectProjectName() {
if (folders && folders.length > 0) {
const cwd = folders[0].uri.fsPath;
try {
const remoteUrl = cp.execSync('git remote get-url origin', {
cwd, encoding: 'utf-8', timeout: 2000, windowsHide: true, stdio: ['ignore', 'pipe', 'ignore']
}).toString().trim();
const match = remoteUrl.match(/\/([^\/]+?)(?:\.git)?$/);
if (match && match[1]) {
return match[1].toLowerCase().replace(/[\s\-]+/g, '_');
const gitConfigPath = path.join(cwd, '.git', 'config');
if (fs.existsSync(gitConfigPath)) {
const configContent = fs.readFileSync(gitConfigPath, 'utf8');
const match = configContent.match(/url\s*=\s*.*\/([^\/]+?)(?:\.git)?$/m);
if (match && match[1]) {
return match[1].toLowerCase().replace(/[\s\-]+/g, '_');
}
}
}
catch { }

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
"name": "gravity-bridge",
"displayName": "Gravity Bridge",
"description": "Antigravity ↔ Discord 브리지 연동 확장",
"version": "0.5.14",
"version": "0.5.20",
"publisher": "variet",
"engines": {
"vscode": "^1.100.0"
@@ -16,6 +16,7 @@
],
"main": "./out/extension.js",
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./ && node -e \"const fs=require('fs'),p=require('path');const s=p.join('src','sdk'),d=p.join('out','sdk');if(fs.existsSync(s)){fs.mkdirSync(d,{recursive:true});fs.readdirSync(s).forEach(f=>fs.copyFileSync(p.join(s,f),p.join(d,f)));console.log('SDK copied to out/sdk')};\"",
"watch": "tsc -watch -p ./"
},

View File

@@ -70,12 +70,13 @@ function detectProjectName(): string {
if (folders && folders.length > 0) {
const cwd = folders[0].uri.fsPath;
try {
const remoteUrl = cp.execSync('git remote get-url origin', {
cwd, encoding: 'utf-8', timeout: 2000, windowsHide: true, stdio: ['ignore', 'pipe', 'ignore']
}).toString().trim();
const match = remoteUrl.match(/\/([^\/]+?)(?:\.git)?$/);
if (match && match[1]) {
return match[1].toLowerCase().replace(/[\s\-]+/g, '_');
const gitConfigPath = path.join(cwd, '.git', 'config');
if (fs.existsSync(gitConfigPath)) {
const configContent = fs.readFileSync(gitConfigPath, 'utf8');
const match = configContent.match(/url\s*=\s*.*\/([^\/]+?)(?:\.git)?$/m);
if (match && match[1]) {
return match[1].toLowerCase().replace(/[\s\-]+/g, '_');
}
}
} catch { }
return path.basename(cwd).toLowerCase().replace(/[\s\-]+/g, '_');

View File

@@ -545,10 +545,13 @@ function setupMonitor() {
const toolName = toolCall?.name || stepType.replace('CORTEX_STEP_TYPE_', '').toLowerCase();
let command = toolName;
let isSafeToAutoRun = false;
// Parse argumentsJson for command details
if (toolCall?.argumentsJson) {
try {
const args = JSON.parse(toolCall.argumentsJson);
isSafeToAutoRun = args.SafeToAutoRun === true;
if (args.CommandLine) {
command = `${toolName}: ${args.CommandLine.substring(0, 1500)}`;
} else if (args.TargetFile) {
@@ -563,33 +566,38 @@ function setupMonitor() {
const description = `Step #${si} (${stepType.replace('CORTEX_STEP_TYPE_', '')})`;
ctx.logToFile(`[STEP-PROBE] ★ WAITING! step=${si} type=${stepType} cmd='${command}'`);
if (si !== ctx.lastPendingStepIndex) {
ctx.stallProbed = true; // found WAITING — stop retrying
// Track highest step index for auto-resolve
if (si > ctx.lastPendingStepIndex) {
ctx.lastPendingStepIndex = si;
if (si !== ctx.lastPendingStepIndex) {
ctx.stallProbed = true; // found WAITING — stop retrying
// Track highest step index for auto-resolve
if (si > ctx.lastPendingStepIndex) {
ctx.lastPendingStepIndex = si;
}
lastPendingTime = Date.now();
ctx.sawRunningAfterPending = false;
// Skip pending for workspace-less AG windows (project=default)
if (ctx.projectName === 'default') {
ctx.logToFile(`[STEP-PROBE] skip pending: ctx.projectName=default (no workspace)`);
} else {
// Always write pending — Bot decides auto-approve (prevents double-fire)
writePendingApproval({
conversation_id: ctx.activeSessionId,
command,
description,
step_type: ['view_file', 'list_dir', 'find_by_name', 'read_file', 'grep_search'].includes(toolName) ? 'file_permission'
: ['write_to_file', 'replace_file_content', 'multi_replace_file_content'].includes(toolName) ? 'code_edit'
: ['browser_subagent', 'open_browser_url'].includes(toolName) ? 'browser_subagent'
: toolName,
step_index: si,
source: 'step_probe',
safe_to_auto_run: isSafeToAutoRun,
});
if (isSafeToAutoRun) {
const truncatedCmd = command.length > 500 ? command.substring(0, 500) + '\n...(이하 생략)' : command;
ctx.writeChatSnapshot(`⚡ **자동 실행됨** (step ${si})\n\n\`\`\`\n${truncatedCmd}\n\`\`\``);
}
}
}
lastPendingTime = Date.now();
ctx.sawRunningAfterPending = false;
// Skip pending for workspace-less AG windows (project=default)
if (ctx.projectName === 'default') {
ctx.logToFile(`[STEP-PROBE] skip pending: ctx.projectName=default (no workspace)`);
} else {
// Always write pending — Bot decides auto-approve (prevents double-fire)
writePendingApproval({
conversation_id: ctx.activeSessionId,
command,
description,
step_type: ['view_file', 'list_dir', 'find_by_name', 'read_file', 'grep_search'].includes(toolName) ? 'file_permission'
: ['write_to_file', 'replace_file_content', 'multi_replace_file_content'].includes(toolName) ? 'code_edit'
: ['browser_subagent', 'open_browser_url'].includes(toolName) ? 'browser_subagent'
: toolName,
step_index: si,
source: 'step_probe',
});
}
}
// NOTE: no break — process ALL parallel WAITING steps
// NOTE: no break — process ALL parallel WAITING steps
}
}
if (!foundWaiting) {
@@ -626,9 +634,11 @@ function setupMonitor() {
const toolCall = oStep?.metadata?.toolCall;
const toolName = toolCall?.name || (oStep.type || '').replace('CORTEX_STEP_TYPE_', '').toLowerCase();
let command = toolName;
let isSafeToAutoRun = false;
if (toolCall?.argumentsJson) {
try {
const args = JSON.parse(toolCall.argumentsJson);
isSafeToAutoRun = args.SafeToAutoRun === true;
if (args.CommandLine) command = `${toolName}: ${args.CommandLine.substring(0, 1500)}`;
else if (args.TargetFile) command = `${toolName}: ${args.TargetFile}`;
else {
@@ -655,7 +665,12 @@ function setupMonitor() {
: toolName,
step_index: actualIndex,
source: 'step_probe_utf8_offset',
safe_to_auto_run: isSafeToAutoRun,
});
if (isSafeToAutoRun) {
const truncatedCmd = command.length > 500 ? command.substring(0, 500) + '\n...(이하 생략)' : command;
ctx.writeChatSnapshot(`⚡ **자동 실행됨** (step ${actualIndex})\n\n\`\`\`\n${truncatedCmd}\n\`\`\``);
}
}
}
// NOTE: no break — process ALL parallel WAITING steps
@@ -1024,7 +1039,7 @@ function setupMonitor() {
/** Write a pending approval file matching Bot's ApprovalRequest dataclass. */
export function writePendingApproval(data: { conversation_id: string; command: string; description: string; step_type?: string; step_index?: number; source?: string; buttons?: Array<{ text: string; index: number }>; modified_files?: string[]; edit_step_indices?: number[] }) {
export function writePendingApproval(data: { conversation_id: string; command: string; description: string; step_type?: string; step_index?: number; source?: string; buttons?: Array<{ text: string; index: number }>; modified_files?: string[]; edit_step_indices?: number[]; safe_to_auto_run?: boolean }) {
try {
const pendingDir = path.join(ctx.bridgePath, 'pending');
if (!fs.existsSync(pendingDir)) { fs.mkdirSync(pendingDir, { recursive: true }); }
@@ -1063,6 +1078,7 @@ export function writePendingApproval(data: { conversation_id: string; command: s
existing.description = data.description;
if (data.step_type) existing.step_type = data.step_type;
if (data.step_index !== undefined) existing.step_index = data.step_index;
if (data.safe_to_auto_run !== undefined) existing.safe_to_auto_run = data.safe_to_auto_run;
existing.source = 'dom_observer+step_probe'; // mark as merged
fs.promises.writeFile(efPath, JSON.stringify(existing, null, 2), 'utf-8').catch(e => {
ctx.logToFile(`[DEDUP] merge write error: ${e.message}`);