fix(extension): step_type mapping bug + diff_review handler refactor
- Separate read tools (file_permission) from write tools (code_edit) - write_to_file/replace_file_content now use AcknowledgeCascadeCodeEdit RPC - diff_review: 2-strategy approach (RPC first, openReviewChanges fallback) - Track modified_files and edit_step_indices in diff_review pending - known-issues: 3 new entries (pending accumulation, step_type bug, isDirty failure)
This commit is contained in:
@@ -539,4 +539,20 @@
|
||||
- **해결**: `recentPendingSteps` Map (TTL 60초) 추가. `${conversationId}:${stepIndex}`를 키로 사용. 파일 삭제 후에도 메모리에서 차단. delta>0에서 해당 세션 항목 클리어
|
||||
- **주의**: DOM observer HTTP `/pending` 경로(L738-812)는 `writePendingApproval()`을 우회하므로 이 메모리 dedup 미적용. "Run" 필터(L757)와 file_permission dedup(L786-800)이 대신 방어
|
||||
|
||||
### [2026-03-16] pending 파일 무한 누적 — Collector/Bot이 로컬 파일 삭제 안 함
|
||||
- **증상**: `bridge/pending/` 디렉토리에 56개 파일 누적 (auto_resolved 36개 + pending 20개)
|
||||
- **원인**: `remote` 모드에서 Bot은 Gateway 서버에서 동작하여 로컬 pending 파일 직접 삭제 불가. Collector는 pending을 Gateway로 전달하지만 auto_resolved/expired 상태 파일을 로컬에서 삭제하는 로직 없음
|
||||
- **해결**: (미완료) Collector에 로컬 pending cleanup 로직 추가 필요. 리팩토링 시 파일 기반 IPC 자체를 제거하면 근본 해결
|
||||
- **주의**: pending 파일 누적은 Collector `_forward_pending_loop`의 매 사이클 읽기/해싱을 느리게 하여 새 pending 전달 지연 유발
|
||||
|
||||
### [2026-03-16] step_type 매핑 버그 — write_to_file이 file_permission으로 잘못 매핑
|
||||
- **증상**: Discord에서 코드 편집 승인 시 `filePermission` RPC 전송 → 파일 읽기용 interaction이 전송됨
|
||||
- **원인**: L2060/L2121에서 `write_to_file`, `replace_file_content`, `multi_replace_file_content`가 읽기 도구와 함께 `file_permission`으로 매핑됨. `tryApprovalStrategies`의 `AcknowledgeCascadeCodeEdit` 경로(L2907)는 `typeLower.includes('write_to_file')` 체크 → 실제 값이 `file_permission`이라 영영 도달 불가
|
||||
- **해결**: 읽기 도구(`view_file`, `list_dir` 등)와 쓰기 도구(`write_to_file`, `replace_file_content`, `multi_replace_file_content`)를 분리. 쓰기 도구는 `code_edit` step_type 사용. `tryApprovalStrategies`에 `code_edit` 분기 추가
|
||||
- **주의**: AG는 대부분 파일 쓰기에 WAITING을 안 만듦 (한번 승인하면 이후 자동). 실제 WAITING은 새 대화 첫 파일 접근 시에만 발생
|
||||
|
||||
### [2026-03-16] diff_review isDirty 실패 — AG diff는 VS Code dirty 아님
|
||||
- **증상**: Discord diff_review Accept 클릭 → `agentAcceptAllInFile` 실행 → `isDirty` 문서 0개 → 대상 없이 실행 → 효과 없음
|
||||
- **원인**: AG의 stacked code review는 VS Code의 `isDirty` 상태와 무관. AG 자체 diff 시스템으로 관리되며, 파일은 이미 디스크에 저장됨
|
||||
- **해결**: (1차) `AcknowledgeCascadeCodeEdit` RPC로 직접 protocol 수락 시도 → (2차 fallback) `openReviewChanges` 패널 열기 + 수정 파일 focus + `agentAcceptAllInFile` 실행
|
||||
- **주의**: diff_review pending에 `modified_files` (전체 경로)와 `edit_step_indices` (step 번호) 포함 필수. `agentAcceptAllInFile`은 diff 에디터가 포커스된 상태에서만 동작할 수 있음
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 001 | 07:30~11:10 | 승인 상태 관리 근본 원인 분석 + v0.3.12 수정 (sawRunningAfterPending gate) + approval-flow.md 시스템 Flow 문서 + known-issues 2건 추가 | `2d9fe96` | ✅ |
|
||||
| 002 | 13:25~13:48 | DOM Observer 전체 분석 + step_type 매핑 버그 수정 (write→code_edit) + diff_review 핸들러 리팩토링 (AcknowledgeCascadeCodeEdit RPC + openReviewChanges fallback) + known-issues 3건 추가 | `` | 🔧 |
|
||||
|
||||
29
docs/devlog/entries/20260316-002.md
Normal file
29
docs/devlog/entries/20260316-002.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# DOM Observer 분석 + diff_review 수정
|
||||
|
||||
- **시간**: 2026-03-16 13:25~13:48
|
||||
- **Commit**: ``
|
||||
- **Vikunja**: 미정
|
||||
|
||||
## 분석 결과
|
||||
|
||||
extension.ts 3,166줄 전체를 5개 기능 영역으로 분류:
|
||||
1. AG 정보 추출 (SDK, 세션 모니터, step probe) ~700줄
|
||||
2. 승인 실행 (RPC) ~200줄
|
||||
3. 파일 기반 IPC (리팩토링 대상) ~500줄
|
||||
4. DOM Observer 인프라 (제거 검토) ~1,100줄
|
||||
5. 기타 유틸 ~200줄
|
||||
|
||||
**DOM Observer는 제거 가능하다** — RPC가 모든 승인을 커버하며, diff_review도 VS Code 명령으로 처리 가능.
|
||||
단, diff_review 원격 실행이 먼저 동작해야 DOM 제거에 동의 가능 (사용자 요구).
|
||||
|
||||
## 결정 사항
|
||||
- write 도구를 `code_edit` step_type으로 분리 (기존: `file_permission`에 혼재)
|
||||
- diff_review 핸들러를 2-strategy 방식으로 리팩토링:
|
||||
1. `AcknowledgeCascadeCodeEdit` RPC (UI 조작 불필요)
|
||||
2. `openReviewChanges` + 파일 포커스 + `agentAcceptAllInFile` (fallback)
|
||||
- pending에 `modified_files`(전체경로)와 `edit_step_indices`(step 번호) 포함
|
||||
|
||||
## 미완료
|
||||
- diff_review 실제 테스트 (AG 재시작 후 Accept all 버튼 동작 확인 필요)
|
||||
- `AcknowledgeCascadeCodeEdit` RPC가 stepIndices 없이도 전체 수락하는지 확인 필요
|
||||
- DOM Observer 제거 리팩토링 → diff_review 동작 확인 후 진행
|
||||
@@ -1746,6 +1746,8 @@ function setupMonitor() {
|
||||
let wasRunning = false; // track RUNNING→IDLE transition for response capture
|
||||
let lastUserInputStepIdx = -1; // track user input for response matching
|
||||
let pendingModifiedFiles = []; // accumulate modified files during RUNNING
|
||||
let pendingModifiedFilePaths = []; // full paths for diff review
|
||||
let pendingEditStepIndices = []; // step indices for AcknowledgeCascadeCodeEdit
|
||||
let lastResponseCaptureStep = -1; // dedup: don't capture same response twice
|
||||
setInterval(async () => {
|
||||
pollCount++;
|
||||
@@ -1885,6 +1887,8 @@ function setupMonitor() {
|
||||
const bn = tf.split(/[\\/]/).pop() || tf;
|
||||
if (!pendingModifiedFiles.includes(bn)) {
|
||||
pendingModifiedFiles.push(bn);
|
||||
pendingModifiedFilePaths.push(tf);
|
||||
pendingEditStepIndices.push(actualIdx);
|
||||
logToFile(`[DIFF-TRACK] + ${bn} (step ${actualIdx})`);
|
||||
}
|
||||
}
|
||||
@@ -2070,7 +2074,9 @@ function setupMonitor() {
|
||||
conversation_id: activeSessionId,
|
||||
command,
|
||||
description: `Step #${actualIndex} (${(oStep.type || '').replace('CORTEX_STEP_TYPE_', '')})`,
|
||||
step_type: ['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,
|
||||
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'
|
||||
: toolName,
|
||||
step_index: actualIndex,
|
||||
source: 'step_probe_offset',
|
||||
});
|
||||
@@ -2133,7 +2139,9 @@ function setupMonitor() {
|
||||
conversation_id: activeSessionId,
|
||||
command,
|
||||
description,
|
||||
step_type: ['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,
|
||||
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'
|
||||
: toolName,
|
||||
step_index: si,
|
||||
source: 'step_probe',
|
||||
});
|
||||
@@ -2437,8 +2445,12 @@ function setupMonitor() {
|
||||
{ text: 'Accept all', index: 0 },
|
||||
{ text: 'Reject all', index: 1 },
|
||||
],
|
||||
modified_files: pendingModifiedFilePaths.slice(0, 20),
|
||||
edit_step_indices: pendingEditStepIndices.slice(0, 20),
|
||||
});
|
||||
pendingModifiedFiles = []; // reset after notification
|
||||
pendingModifiedFilePaths = [];
|
||||
pendingEditStepIndices = [];
|
||||
}
|
||||
wasRunning = isRunning;
|
||||
}
|
||||
@@ -2587,39 +2599,96 @@ async function processResponseFile(filePath) {
|
||||
// DOM observer: renderer handles clicking via pollResponse
|
||||
// Step probe/stall: try RPC → VS Code commands → log results
|
||||
const approved = resp.approved;
|
||||
// ── diff_review: Accept all / Reject all via VS Code commands ──
|
||||
// ── diff_review: Accept all / Reject all ──
|
||||
if (pendingStepType === 'diff_review') {
|
||||
const btnIdx = resp.button_index ?? -1;
|
||||
const isAccept = btnIdx === 0 || (btnIdx === -1 && approved);
|
||||
const cmd = isAccept
|
||||
? 'antigravity.prioritized.agentAcceptAllInFile'
|
||||
: 'antigravity.prioritized.agentRejectAllInFile';
|
||||
logToFile(`[RESPONSE] diff_review → ${cmd} (btnIdx=${btnIdx})`);
|
||||
logToFile(`[RESPONSE] diff_review → ${isAccept ? 'ACCEPT' : 'REJECT'} (btnIdx=${btnIdx})`);
|
||||
let diffReviewDone = false;
|
||||
const targetSession = sessionId || activeSessionId;
|
||||
// ── Strategy 1: AcknowledgeCascadeCodeEdit RPC ──
|
||||
// Accept/reject all pending code edits via protocol (no UI interaction needed)
|
||||
if (sdk) {
|
||||
try {
|
||||
// Find dirty documents and focus each before executing
|
||||
const dirtyDocs = vscode.workspace.textDocuments.filter(d => d.isDirty);
|
||||
logToFile(`[RESPONSE] diff_review: ${dirtyDocs.length} dirty docs found`);
|
||||
if (dirtyDocs.length > 0) {
|
||||
for (const doc of dirtyDocs) {
|
||||
// Get tracked step indices from pending data (or use all recent edit steps)
|
||||
const trackedSteps = [];
|
||||
const pendingDir = path.join(bridgePath, 'pending');
|
||||
try {
|
||||
await vscode.window.showTextDocument(doc, { preview: false });
|
||||
await new Promise(r => setTimeout(r, 200)); // brief wait for focus
|
||||
await vscode.commands.executeCommand(cmd);
|
||||
logToFile(`[RESPONSE] diff_review: ${cmd} on ${doc.fileName.split(/[\\/]/).pop()} OK`);
|
||||
const pendingFile = path.join(pendingDir, `${resp.request_id}.json`);
|
||||
if (fs.existsSync(pendingFile)) {
|
||||
const pd = JSON.parse(fs.readFileSync(pendingFile, 'utf-8'));
|
||||
if (pd.edit_step_indices)
|
||||
trackedSteps.push(...pd.edit_step_indices);
|
||||
}
|
||||
catch (perFileErr) {
|
||||
logToFile(`[RESPONSE] diff_review per-file error: ${perFileErr.message?.substring(0, 80)}`);
|
||||
}
|
||||
catch { }
|
||||
// If no tracked steps, use the step_index from the pending
|
||||
if (trackedSteps.length === 0 && pendingStepIndex > 0) {
|
||||
trackedSteps.push(pendingStepIndex);
|
||||
}
|
||||
logToFile(`[DIFF-REVIEW-RPC] AcknowledgeCascadeCodeEdit(session=${targetSession.substring(0, 8)}, accept=${isAccept}, steps=[${trackedSteps.join(',')}])`);
|
||||
const ackResult = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
|
||||
cascadeId: targetSession,
|
||||
accept: isAccept,
|
||||
...(trackedSteps.length > 0 ? { stepIndices: trackedSteps } : {}),
|
||||
});
|
||||
logToFile(`[DIFF-REVIEW-RPC] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
|
||||
diffReviewDone = true;
|
||||
}
|
||||
catch (rpcErr) {
|
||||
logToFile(`[DIFF-REVIEW-RPC] ❌ ${rpcErr.message.substring(0, 200)}`);
|
||||
}
|
||||
}
|
||||
// ── Strategy 2: Open review panel + focus file + VS Code command ──
|
||||
if (!diffReviewDone) {
|
||||
try {
|
||||
// Step 2a: Open the Review Changes panel
|
||||
try {
|
||||
await vscode.commands.executeCommand('antigravity.openReviewChanges');
|
||||
logToFile(`[DIFF-REVIEW-CMD] openReviewChanges OK`);
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
}
|
||||
catch { }
|
||||
// Step 2b: Find modified files from pending data
|
||||
let modifiedFiles = [];
|
||||
try {
|
||||
const pendingFile = path.join(bridgePath, 'pending', `${resp.request_id}.json`);
|
||||
if (fs.existsSync(pendingFile)) {
|
||||
const pd = JSON.parse(fs.readFileSync(pendingFile, 'utf-8'));
|
||||
if (pd.modified_files)
|
||||
modifiedFiles = pd.modified_files;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
// Step 2c: Open and focus each modified file, then execute
|
||||
if (modifiedFiles.length > 0) {
|
||||
for (const filePath of modifiedFiles) {
|
||||
try {
|
||||
const uri = vscode.Uri.file(filePath);
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
await vscode.window.showTextDocument(doc, { preview: false });
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
await vscode.commands.executeCommand(cmd);
|
||||
logToFile(`[DIFF-REVIEW-CMD] ${cmd} on ${filePath.split(/[\\/]/).pop()} OK`);
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[DIFF-REVIEW-CMD] per-file error on ${filePath}: ${e.message?.substring(0, 80)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No dirty docs — try command anyway
|
||||
// No file list — just execute command (best effort)
|
||||
await vscode.commands.executeCommand(cmd);
|
||||
logToFile(`[RESPONSE] diff_review: no dirty docs, command executed anyway`);
|
||||
logToFile(`[DIFF-REVIEW-CMD] ${cmd} executed (no file list)`);
|
||||
}
|
||||
diffReviewDone = true;
|
||||
}
|
||||
catch (cmdErr) {
|
||||
logToFile(`[RESPONSE] diff_review command error: ${cmdErr.message}`);
|
||||
logToFile(`[DIFF-REVIEW-CMD] error: ${cmdErr.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isDomObserver) {
|
||||
@@ -2949,21 +3018,23 @@ async function tryApprovalStrategies(approved, sessionId, stepType = '', stepInd
|
||||
// Build interaction sub-message based on step_type
|
||||
const typeLower = stepType.toLowerCase().replace('cortex_step_type_', '');
|
||||
let interactionPayload = {};
|
||||
if (typeLower.includes('write_to_file') || typeLower.includes('propose_code') || typeLower.includes('write_cascade_edit')) {
|
||||
if (typeLower.includes('code_edit') || typeLower.includes('write_to_file') || typeLower.includes('propose_code') || typeLower.includes('write_cascade_edit')) {
|
||||
// CODE EDIT: Uses separate AcknowledgeCascadeCodeEdit RPC
|
||||
try {
|
||||
logToFile(`[APPROVAL-PROTO-ACK] AcknowledgeCascadeCodeEdit(cascadeId=${sessionId.substring(0, 8)}, accept=true, stepIndices=[${effectiveStepIndex}])`);
|
||||
logToFile(`[APPROVAL-CODE-EDIT] AcknowledgeCascadeCodeEdit(cascadeId=${sessionId.substring(0, 8)}, accept=${approved}, stepIndices=[${effectiveStepIndex}])`);
|
||||
const ackResult = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
|
||||
cascadeId: sessionId,
|
||||
accept: true,
|
||||
accept: approved,
|
||||
stepIndices: [effectiveStepIndex],
|
||||
});
|
||||
logToFile(`[APPROVAL-PROTO-ACK] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
|
||||
return `RPC-PROTO-ACK:AcknowledgeCascadeCodeEdit`;
|
||||
logToFile(`[APPROVAL-CODE-EDIT] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
|
||||
return `RPC:AcknowledgeCascadeCodeEdit(accept=${approved})`;
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[APPROVAL-PROTO-ACK] ❌ ${e.message.substring(0, 200)}`);
|
||||
// Fall through to HandleCascadeUserInteraction
|
||||
logToFile(`[APPROVAL-CODE-EDIT] ❌ ${e.message.substring(0, 200)}`);
|
||||
// Fallback: try HandleCascadeUserInteraction with runCommand
|
||||
logToFile(`[APPROVAL-CODE-EDIT] falling back to HandleCascadeUserInteraction`);
|
||||
interactionPayload = { runCommand: { confirm: true } };
|
||||
}
|
||||
}
|
||||
// Map step_type to interaction sub-message field
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1739,6 +1739,8 @@ function setupMonitor() {
|
||||
let wasRunning = false; // track RUNNING→IDLE transition for response capture
|
||||
let lastUserInputStepIdx = -1; // track user input for response matching
|
||||
let pendingModifiedFiles: string[] = []; // accumulate modified files during RUNNING
|
||||
let pendingModifiedFilePaths: string[] = []; // full paths for diff review
|
||||
let pendingEditStepIndices: number[] = []; // step indices for AcknowledgeCascadeCodeEdit
|
||||
let lastResponseCaptureStep = -1; // dedup: don't capture same response twice
|
||||
|
||||
setInterval(async () => {
|
||||
@@ -1884,6 +1886,8 @@ function setupMonitor() {
|
||||
const bn = tf.split(/[\\/]/).pop() || tf;
|
||||
if (!pendingModifiedFiles.includes(bn)) {
|
||||
pendingModifiedFiles.push(bn);
|
||||
pendingModifiedFilePaths.push(tf);
|
||||
pendingEditStepIndices.push(actualIdx);
|
||||
logToFile(`[DIFF-TRACK] + ${bn} (step ${actualIdx})`);
|
||||
}
|
||||
}
|
||||
@@ -2057,7 +2061,9 @@ function setupMonitor() {
|
||||
conversation_id: activeSessionId,
|
||||
command,
|
||||
description: `Step #${actualIndex} (${(oStep.type || '').replace('CORTEX_STEP_TYPE_', '')})`,
|
||||
step_type: ['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,
|
||||
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'
|
||||
: toolName,
|
||||
step_index: actualIndex,
|
||||
source: 'step_probe_offset',
|
||||
});
|
||||
@@ -2118,7 +2124,9 @@ function setupMonitor() {
|
||||
conversation_id: activeSessionId,
|
||||
command,
|
||||
description,
|
||||
step_type: ['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,
|
||||
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'
|
||||
: toolName,
|
||||
step_index: si,
|
||||
source: 'step_probe',
|
||||
});
|
||||
@@ -2406,8 +2414,12 @@ function setupMonitor() {
|
||||
{ text: 'Accept all', index: 0 },
|
||||
{ text: 'Reject all', index: 1 },
|
||||
],
|
||||
});
|
||||
modified_files: pendingModifiedFilePaths.slice(0, 20),
|
||||
edit_step_indices: pendingEditStepIndices.slice(0, 20),
|
||||
} as any);
|
||||
pendingModifiedFiles = []; // reset after notification
|
||||
pendingModifiedFilePaths = [];
|
||||
pendingEditStepIndices = [];
|
||||
}
|
||||
wasRunning = isRunning;
|
||||
} catch (e: any) {
|
||||
@@ -2556,36 +2568,94 @@ async function processResponseFile(filePath: string) {
|
||||
|
||||
const approved = resp.approved;
|
||||
|
||||
// ── diff_review: Accept all / Reject all via VS Code commands ──
|
||||
// ── diff_review: Accept all / Reject all ──
|
||||
if (pendingStepType === 'diff_review') {
|
||||
const btnIdx = resp.button_index ?? -1;
|
||||
const isAccept = btnIdx === 0 || (btnIdx === -1 && approved);
|
||||
const cmd = isAccept
|
||||
? 'antigravity.prioritized.agentAcceptAllInFile'
|
||||
: 'antigravity.prioritized.agentRejectAllInFile';
|
||||
logToFile(`[RESPONSE] diff_review → ${cmd} (btnIdx=${btnIdx})`);
|
||||
logToFile(`[RESPONSE] diff_review → ${isAccept ? 'ACCEPT' : 'REJECT'} (btnIdx=${btnIdx})`);
|
||||
|
||||
let diffReviewDone = false;
|
||||
const targetSession = sessionId || activeSessionId;
|
||||
|
||||
// ── Strategy 1: AcknowledgeCascadeCodeEdit RPC ──
|
||||
// Accept/reject all pending code edits via protocol (no UI interaction needed)
|
||||
if (sdk) {
|
||||
try {
|
||||
// Find dirty documents and focus each before executing
|
||||
const dirtyDocs = vscode.workspace.textDocuments.filter(d => d.isDirty);
|
||||
logToFile(`[RESPONSE] diff_review: ${dirtyDocs.length} dirty docs found`);
|
||||
if (dirtyDocs.length > 0) {
|
||||
for (const doc of dirtyDocs) {
|
||||
// Get tracked step indices from pending data (or use all recent edit steps)
|
||||
const trackedSteps: number[] = [];
|
||||
const pendingDir = path.join(bridgePath, 'pending');
|
||||
try {
|
||||
const pendingFile = path.join(pendingDir, `${resp.request_id}.json`);
|
||||
if (fs.existsSync(pendingFile)) {
|
||||
const pd = JSON.parse(fs.readFileSync(pendingFile, 'utf-8'));
|
||||
if (pd.edit_step_indices) trackedSteps.push(...pd.edit_step_indices);
|
||||
}
|
||||
} catch { }
|
||||
|
||||
// If no tracked steps, use the step_index from the pending
|
||||
if (trackedSteps.length === 0 && pendingStepIndex > 0) {
|
||||
trackedSteps.push(pendingStepIndex);
|
||||
}
|
||||
|
||||
logToFile(`[DIFF-REVIEW-RPC] AcknowledgeCascadeCodeEdit(session=${targetSession.substring(0, 8)}, accept=${isAccept}, steps=[${trackedSteps.join(',')}])`);
|
||||
const ackResult = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
|
||||
cascadeId: targetSession,
|
||||
accept: isAccept,
|
||||
...(trackedSteps.length > 0 ? { stepIndices: trackedSteps } : {}),
|
||||
});
|
||||
logToFile(`[DIFF-REVIEW-RPC] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
|
||||
diffReviewDone = true;
|
||||
} catch (rpcErr: any) {
|
||||
logToFile(`[DIFF-REVIEW-RPC] ❌ ${rpcErr.message.substring(0, 200)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Strategy 2: Open review panel + focus file + VS Code command ──
|
||||
if (!diffReviewDone) {
|
||||
try {
|
||||
// Step 2a: Open the Review Changes panel
|
||||
try {
|
||||
await vscode.commands.executeCommand('antigravity.openReviewChanges');
|
||||
logToFile(`[DIFF-REVIEW-CMD] openReviewChanges OK`);
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
} catch { }
|
||||
|
||||
// Step 2b: Find modified files from pending data
|
||||
let modifiedFiles: string[] = [];
|
||||
try {
|
||||
const pendingFile = path.join(bridgePath, 'pending', `${resp.request_id}.json`);
|
||||
if (fs.existsSync(pendingFile)) {
|
||||
const pd = JSON.parse(fs.readFileSync(pendingFile, 'utf-8'));
|
||||
if (pd.modified_files) modifiedFiles = pd.modified_files;
|
||||
}
|
||||
} catch { }
|
||||
|
||||
// Step 2c: Open and focus each modified file, then execute
|
||||
if (modifiedFiles.length > 0) {
|
||||
for (const filePath of modifiedFiles) {
|
||||
try {
|
||||
const uri = vscode.Uri.file(filePath);
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
await vscode.window.showTextDocument(doc, { preview: false });
|
||||
await new Promise(r => setTimeout(r, 200)); // brief wait for focus
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
await vscode.commands.executeCommand(cmd);
|
||||
logToFile(`[RESPONSE] diff_review: ${cmd} on ${doc.fileName.split(/[\\/]/).pop()} OK`);
|
||||
} catch (perFileErr: any) {
|
||||
logToFile(`[RESPONSE] diff_review per-file error: ${perFileErr.message?.substring(0, 80)}`);
|
||||
logToFile(`[DIFF-REVIEW-CMD] ${cmd} on ${filePath.split(/[\\/]/).pop()} OK`);
|
||||
} catch (e: any) {
|
||||
logToFile(`[DIFF-REVIEW-CMD] per-file error on ${filePath}: ${e.message?.substring(0, 80)}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No dirty docs — try command anyway
|
||||
// No file list — just execute command (best effort)
|
||||
await vscode.commands.executeCommand(cmd);
|
||||
logToFile(`[RESPONSE] diff_review: no dirty docs, command executed anyway`);
|
||||
logToFile(`[DIFF-REVIEW-CMD] ${cmd} executed (no file list)`);
|
||||
}
|
||||
diffReviewDone = true;
|
||||
} catch (cmdErr: any) {
|
||||
logToFile(`[RESPONSE] diff_review command error: ${cmdErr.message}`);
|
||||
logToFile(`[DIFF-REVIEW-CMD] error: ${cmdErr.message}`);
|
||||
}
|
||||
}
|
||||
} else if (isDomObserver) {
|
||||
// DOM observer path: ALSO try RPC strategies (renderer click is unreliable)
|
||||
@@ -2904,20 +2974,22 @@ async function tryApprovalStrategies(approved: boolean, sessionId: string, stepT
|
||||
const typeLower = stepType.toLowerCase().replace('cortex_step_type_', '');
|
||||
let interactionPayload: Record<string, any> = {};
|
||||
|
||||
if (typeLower.includes('write_to_file') || typeLower.includes('propose_code') || typeLower.includes('write_cascade_edit')) {
|
||||
if (typeLower.includes('code_edit') || typeLower.includes('write_to_file') || typeLower.includes('propose_code') || typeLower.includes('write_cascade_edit')) {
|
||||
// CODE EDIT: Uses separate AcknowledgeCascadeCodeEdit RPC
|
||||
try {
|
||||
logToFile(`[APPROVAL-PROTO-ACK] AcknowledgeCascadeCodeEdit(cascadeId=${sessionId.substring(0,8)}, accept=true, stepIndices=[${effectiveStepIndex}])`);
|
||||
logToFile(`[APPROVAL-CODE-EDIT] AcknowledgeCascadeCodeEdit(cascadeId=${sessionId.substring(0,8)}, accept=${approved}, stepIndices=[${effectiveStepIndex}])`);
|
||||
const ackResult = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
|
||||
cascadeId: sessionId,
|
||||
accept: true,
|
||||
accept: approved,
|
||||
stepIndices: [effectiveStepIndex],
|
||||
});
|
||||
logToFile(`[APPROVAL-PROTO-ACK] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
|
||||
return `RPC-PROTO-ACK:AcknowledgeCascadeCodeEdit`;
|
||||
logToFile(`[APPROVAL-CODE-EDIT] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
|
||||
return `RPC:AcknowledgeCascadeCodeEdit(accept=${approved})`;
|
||||
} catch (e: any) {
|
||||
logToFile(`[APPROVAL-PROTO-ACK] ❌ ${e.message.substring(0, 200)}`);
|
||||
// Fall through to HandleCascadeUserInteraction
|
||||
logToFile(`[APPROVAL-CODE-EDIT] ❌ ${e.message.substring(0, 200)}`);
|
||||
// Fallback: try HandleCascadeUserInteraction with runCommand
|
||||
logToFile(`[APPROVAL-CODE-EDIT] falling back to HandleCascadeUserInteraction`);
|
||||
interactionPayload = { runCommand: { confirm: true } };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user