feat: step_type routing for all approval interaction types
This commit is contained in:
@@ -1775,12 +1775,16 @@ async function processResponseFile(filePath) {
|
||||
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 { }
|
||||
}
|
||||
@@ -1794,9 +1798,9 @@ async function processResponseFile(filePath) {
|
||||
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}`);
|
||||
}
|
||||
logToFile(`[RESPONSE] ${approved ? 'approve' : 'reject'} done (${isDomObserver ? 'dom' : 'step_probe'})`);
|
||||
@@ -2010,9 +2014,10 @@ function writePendingApproval(data) {
|
||||
* 2. VS Code accept/reject commands (focus-dependent)
|
||||
* 3. Log failure for manual intervention
|
||||
*/
|
||||
async function tryApprovalStrategies(approved, sessionId) {
|
||||
async function tryApprovalStrategies(approved, sessionId, stepType = '', stepIndex = -1) {
|
||||
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 = [];
|
||||
try {
|
||||
@@ -2034,67 +2039,96 @@ async function tryApprovalStrategies(approved, sessionId) {
|
||||
}
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 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 = {};
|
||||
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) {
|
||||
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) {
|
||||
// Capture FULL error message (critical for diagnostics)
|
||||
logToFile(`[APPROVAL-PROTO-${i}] ❌ ${e.message.substring(0, 300)}`);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user