feat: step_type routing for all approval interaction types
This commit is contained in:
@@ -1753,12 +1753,16 @@ async function processResponseFile(filePath: string) {
|
||||
const pendingFile = path.join(pendingDir, `${resp.request_id}.json`);
|
||||
let sessionId = '';
|
||||
let isDomObserver = false;
|
||||
let pendingStepType = '';
|
||||
let pendingStepIndex = -1;
|
||||
if (fs.existsSync(pendingFile)) {
|
||||
try {
|
||||
const pending = JSON.parse(fs.readFileSync(pendingFile, 'utf-8'));
|
||||
sessionId = pending.conversation_id || '';
|
||||
isDomObserver = pending.auto_detected === true
|
||||
|| pending.source === 'dom_observer';
|
||||
pendingStepType = pending.step_type || '';
|
||||
pendingStepIndex = pending.step_index ?? lastPendingStepIndex;
|
||||
} catch { }
|
||||
}
|
||||
|
||||
@@ -1773,9 +1777,9 @@ async function processResponseFile(filePath: string) {
|
||||
// DOM observer path: renderer polls /response/:rid and clicks directly
|
||||
logToFile(`[RESPONSE] renderer-handled approval (rid=${resp.request_id})`);
|
||||
} else {
|
||||
// Step probe path: run ALL approval strategies (5 vectors → 30+ methods)
|
||||
logToFile(`[RESPONSE] step_probe → running tryApprovalStrategies(${approved}, ${activeSessionId.substring(0, 8)})`);
|
||||
const strategyResult = await tryApprovalStrategies(approved, activeSessionId);
|
||||
// Step probe path: run ALL approval strategies
|
||||
logToFile(`[RESPONSE] step_probe → tryApprovalStrategies(${approved}, ${activeSessionId.substring(0, 8)}, type=${pendingStepType}, step=${pendingStepIndex})`);
|
||||
const strategyResult = await tryApprovalStrategies(approved, activeSessionId, pendingStepType, pendingStepIndex);
|
||||
logToFile(`[RESPONSE] strategy result: ${strategyResult}`);
|
||||
}
|
||||
|
||||
@@ -1976,9 +1980,10 @@ function writePendingApproval(data: { conversation_id: string; command: string;
|
||||
* 2. VS Code accept/reject commands (focus-dependent)
|
||||
* 3. Log failure for manual intervention
|
||||
*/
|
||||
async function tryApprovalStrategies(approved: boolean, sessionId: string): Promise<string> {
|
||||
async function tryApprovalStrategies(approved: boolean, sessionId: string, stepType: string = '', stepIndex: number = -1): Promise<string> {
|
||||
const action = approved ? 'APPROVE' : 'REJECT';
|
||||
logToFile(`[APPROVAL] Starting ${action} strategies for session ${sessionId.substring(0, 8)}`);
|
||||
const effectiveStepIndex = stepIndex >= 0 ? stepIndex : lastPendingStepIndex;
|
||||
logToFile(`[APPROVAL] Starting ${action} strategies for session ${sessionId.substring(0, 8)} stepType=${stepType} stepIndex=${effectiveStepIndex}`);
|
||||
|
||||
// ── Dynamic Command Discovery (log what's available during WAITING state) ──
|
||||
let approvalCmdList: string[] = [];
|
||||
@@ -2001,66 +2006,91 @@ async function tryApprovalStrategies(approved: boolean, sessionId: string): Prom
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// STRATEGY 0-PROTO: Correct proto-based RPC (decoded from AG source)
|
||||
// HandleCascadeUserInteractionRequest:
|
||||
// cascade_id: string
|
||||
// interaction: CascadeUserInteraction {
|
||||
// trajectory_id, step_index,
|
||||
// oneof: { run_command: CascadeRunCommandInteraction { confirm: bool } }
|
||||
// }
|
||||
// ConnectRPC uses camelCase for JSON encoding of snake_case proto fields
|
||||
// Routes interaction sub-message by step_type:
|
||||
// run_command → CascadeRunCommandInteraction { confirm }
|
||||
// write_to_file → AcknowledgeCascadeCodeEdit RPC (separate)
|
||||
// open_browser_url → CascadeOpenBrowserUrlInteraction { confirm }
|
||||
// send_command_input → CascadeSendCommandInputInteraction { confirm }
|
||||
// read_url_content → CascadeReadUrlContentInteraction { confirm }
|
||||
// mcp_tool → CascadeMcpInteraction { confirm }
|
||||
// invoke_subagent → CascadeRunExtensionCodeInteraction { confirm }
|
||||
// ══════════════════════════════════════════════════════════
|
||||
if (sdk && approved) {
|
||||
// Build interaction sub-message based on step_type
|
||||
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')) {
|
||||
// CODE EDIT: Uses separate AcknowledgeCascadeCodeEdit RPC
|
||||
try {
|
||||
logToFile(`[APPROVAL-PROTO-ACK] AcknowledgeCascadeCodeEdit(cascadeId=${sessionId.substring(0,8)}, accept=true, stepIndices=[${effectiveStepIndex}])`);
|
||||
const ackResult = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
|
||||
cascadeId: sessionId,
|
||||
accept: true,
|
||||
stepIndices: [effectiveStepIndex],
|
||||
});
|
||||
logToFile(`[APPROVAL-PROTO-ACK] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
|
||||
return `RPC-PROTO-ACK:AcknowledgeCascadeCodeEdit`;
|
||||
} catch (e: any) {
|
||||
logToFile(`[APPROVAL-PROTO-ACK] ❌ ${e.message.substring(0, 200)}`);
|
||||
// Fall through to HandleCascadeUserInteraction
|
||||
}
|
||||
}
|
||||
|
||||
// Map step_type to interaction sub-message field
|
||||
if (typeLower.includes('run_command') || typeLower.includes('shell_exec')) {
|
||||
interactionPayload = { runCommand: { confirm: true } };
|
||||
} else if (typeLower.includes('open_browser')) {
|
||||
interactionPayload = { openBrowserUrl: { confirm: true } };
|
||||
} else if (typeLower.includes('send_command_input')) {
|
||||
interactionPayload = { sendCommandInput: { confirm: true } }; // guess field name
|
||||
} else if (typeLower.includes('read_url')) {
|
||||
interactionPayload = { readUrlContent: { confirm: true } }; // guess
|
||||
} else if (typeLower.includes('mcp')) {
|
||||
interactionPayload = { mcpTool: { confirm: true } }; // guess
|
||||
} else if (typeLower.includes('invoke_subagent') || typeLower.includes('extension_code')) {
|
||||
interactionPayload = { runExtensionCode: { confirm: true } };
|
||||
} else {
|
||||
// Default: try run_command (most common)
|
||||
interactionPayload = { runCommand: { confirm: true } };
|
||||
}
|
||||
|
||||
const protoVariants = [
|
||||
// Variant A: camelCase JSON (ConnectRPC default)
|
||||
// Variant A: camelCase with trajectoryId (proven working for run_command)
|
||||
{
|
||||
cascadeId: sessionId,
|
||||
interaction: {
|
||||
trajectoryId: activeTrajectoryId || sessionId,
|
||||
stepIndex: lastPendingStepIndex,
|
||||
runCommand: { confirm: true },
|
||||
stepIndex: effectiveStepIndex,
|
||||
...interactionPayload,
|
||||
},
|
||||
},
|
||||
// Variant B: snake_case JSON (proto native)
|
||||
// Variant B: snake_case
|
||||
{
|
||||
cascade_id: sessionId,
|
||||
interaction: {
|
||||
trajectory_id: activeTrajectoryId || sessionId,
|
||||
step_index: lastPendingStepIndex,
|
||||
run_command: { confirm: true },
|
||||
step_index: effectiveStepIndex,
|
||||
...interactionPayload,
|
||||
},
|
||||
},
|
||||
// Variant C: camelCase, without trajectoryId (maybe optional)
|
||||
// Variant C: minimal (no trajectoryId)
|
||||
{
|
||||
cascadeId: sessionId,
|
||||
interaction: {
|
||||
stepIndex: lastPendingStepIndex,
|
||||
runCommand: { confirm: true },
|
||||
},
|
||||
},
|
||||
// Variant D: camelCase, confirm only (minimal)
|
||||
{
|
||||
cascadeId: sessionId,
|
||||
interaction: {
|
||||
runCommand: { confirm: true },
|
||||
},
|
||||
},
|
||||
// Variant E: snake_case minimal
|
||||
{
|
||||
cascade_id: sessionId,
|
||||
interaction: {
|
||||
run_command: { confirm: true },
|
||||
stepIndex: effectiveStepIndex,
|
||||
...interactionPayload,
|
||||
},
|
||||
},
|
||||
];
|
||||
for (let i = 0; i < protoVariants.length; i++) {
|
||||
try {
|
||||
const payload = protoVariants[i];
|
||||
logToFile(`[APPROVAL-PROTO-${i}] HandleCascadeUserInteraction(${JSON.stringify(payload).substring(0, 200)})`);
|
||||
logToFile(`[APPROVAL-PROTO-${i}] HandleCascadeUserInteraction(${JSON.stringify(payload).substring(0, 250)})`);
|
||||
const rpcResult = await sdk.ls.rawRPC('HandleCascadeUserInteraction', payload);
|
||||
logToFile(`[APPROVAL-PROTO-${i}] ✅ SUCCESS: ${JSON.stringify(rpcResult).substring(0, 200)}`);
|
||||
return `RPC-PROTO-${i}:HandleCascadeUserInteraction`;
|
||||
return `RPC-PROTO-${i}:HandleCascadeUserInteraction(${typeLower})`;
|
||||
} catch (e: any) {
|
||||
// Capture FULL error message (critical for diagnostics)
|
||||
logToFile(`[APPROVAL-PROTO-${i}] ❌ ${e.message.substring(0, 300)}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user