feat: auto-WAITING detection via stall + step query
This commit is contained in:
@@ -286,6 +286,9 @@ function setupMonitor() {
|
|||||||
let lastKnownStepCount = 0;
|
let lastKnownStepCount = 0;
|
||||||
let lastNotifyStepIndex = -1;
|
let lastNotifyStepIndex = -1;
|
||||||
let lastTaskStepIndex = -1;
|
let lastTaskStepIndex = -1;
|
||||||
|
// WAITING detection
|
||||||
|
let stalledPolls = 0;
|
||||||
|
let lastPendingStepIndex = -1; // dedup: don't re-create pending for same step
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
pollCount++;
|
pollCount++;
|
||||||
try {
|
try {
|
||||||
@@ -317,16 +320,82 @@ function setupMonitor() {
|
|||||||
lastKnownStepCount = currentCount;
|
lastKnownStepCount = currentCount;
|
||||||
lastNotifyStepIndex = bestSession.latestNotifyUserStep?.stepIndex ?? -1;
|
lastNotifyStepIndex = bestSession.latestNotifyUserStep?.stepIndex ?? -1;
|
||||||
lastTaskStepIndex = bestSession.latestTaskBoundaryStep?.stepIndex ?? -1;
|
lastTaskStepIndex = bestSession.latestTaskBoundaryStep?.stepIndex ?? -1;
|
||||||
|
stalledPolls = 0;
|
||||||
|
lastPendingStepIndex = -1;
|
||||||
writeRegistration(activeSessionId);
|
writeRegistration(activeSessionId);
|
||||||
console.log(`Gravity Bridge: [POLL#${pollCount}] session: ${activeSessionId.substring(0, 8)} "${currentTitle}" steps=${currentCount} ${isRunning ? 'RUNNING' : 'idle'}`);
|
console.log(`Gravity Bridge: [POLL#${pollCount}] session: ${activeSessionId.substring(0, 8)} "${currentTitle}" steps=${currentCount} ${isRunning ? 'RUNNING' : 'idle'}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// No change?
|
// ── WAITING Detection ──
|
||||||
if (currentCount <= lastKnownStepCount && pollCount > 1) {
|
// stepCount frozen + session still RUNNING = likely WAITING for user approval
|
||||||
if (pollCount % 30 === 0) {
|
if (currentCount === lastKnownStepCount && isRunning) {
|
||||||
console.log(`Gravity Bridge: [POLL#${pollCount}] idle steps=${currentCount}`);
|
stalledPolls++;
|
||||||
|
if (stalledPolls >= 2) {
|
||||||
|
// Query last steps to check for WAITING/PENDING
|
||||||
|
try {
|
||||||
|
const stepsResp = await sdk.ls.rawRPC('GetCascadeTrajectorySteps', { cascadeId: bestSessionId });
|
||||||
|
const steps = stepsResp?.steps || [];
|
||||||
|
if (steps.length > 0) {
|
||||||
|
const lastStep = steps[steps.length - 1];
|
||||||
|
const stepStatus = (lastStep.status || '').replace('CORTEX_STEP_STATUS_', '');
|
||||||
|
const stepType = (lastStep.type || '').replace('CORTEX_STEP_TYPE_', '');
|
||||||
|
const stepIdx = lastStep.metadata?.sourceTrajectoryStepInfo?.stepIndex ?? steps.length - 1;
|
||||||
|
// Non-DONE step while session is RUNNING = user action needed
|
||||||
|
if (stepStatus !== 'DONE' && stepStatus !== 'REJECTED' && stepIdx > lastPendingStepIndex) {
|
||||||
|
// Extract command info from step
|
||||||
|
let cmd = 'unknown';
|
||||||
|
let desc = `${stepType} (${stepStatus})`;
|
||||||
|
const toolName = lastStep.metadata?.toolCall?.name || '';
|
||||||
|
if (lastStep.terminalCommand?.command) {
|
||||||
|
cmd = lastStep.terminalCommand.command;
|
||||||
|
desc = `Terminal: ${cmd}`;
|
||||||
}
|
}
|
||||||
// Still process notify/task below (stepIndex may update without stepCount change)
|
else if (toolName === 'run_command') {
|
||||||
|
// Extract from toolCall arguments
|
||||||
|
try {
|
||||||
|
const args = JSON.parse(lastStep.metadata.toolCall.argumentsJson || '{}');
|
||||||
|
cmd = args.CommandLine || args.command || toolName;
|
||||||
|
desc = `Command: ${cmd}`;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
cmd = toolName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (toolName) {
|
||||||
|
cmd = toolName;
|
||||||
|
desc = `Tool: ${toolName}`;
|
||||||
|
}
|
||||||
|
// Auto-create pending file
|
||||||
|
const rid = Date.now().toString();
|
||||||
|
const pending = {
|
||||||
|
request_id: rid,
|
||||||
|
conversation_id: bestSessionId,
|
||||||
|
command: cmd.substring(0, 200),
|
||||||
|
description: desc.substring(0, 200),
|
||||||
|
timestamp: Date.now() / 1000,
|
||||||
|
status: 'pending',
|
||||||
|
project_name: projectName,
|
||||||
|
step_index: stepIdx,
|
||||||
|
step_type: stepType,
|
||||||
|
step_status: stepStatus,
|
||||||
|
auto_detected: true,
|
||||||
|
};
|
||||||
|
const pendingDir = path.join(bridgePath, 'pending');
|
||||||
|
fs.writeFileSync(path.join(pendingDir, `${rid}.json`), JSON.stringify(pending, null, 2));
|
||||||
|
lastPendingStepIndex = stepIdx;
|
||||||
|
stalledPolls = 0; // reset stall counter after pending created
|
||||||
|
logToFile(`[WAITING] detected step=${stepIdx} type=${stepType} status=${stepStatus} cmd=${cmd.substring(0, 60)}`);
|
||||||
|
console.log(`Gravity Bridge: [POLL#${pollCount}] WAITING detected! step=${stepIdx} cmd=${cmd.substring(0, 40)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
logToFile(`[WAITING] step query failed: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
stalledPolls = 0;
|
||||||
}
|
}
|
||||||
const delta = currentCount - lastKnownStepCount;
|
const delta = currentCount - lastKnownStepCount;
|
||||||
lastKnownStepCount = currentCount;
|
lastKnownStepCount = currentCount;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -254,6 +254,9 @@ function setupMonitor() {
|
|||||||
let lastKnownStepCount = 0;
|
let lastKnownStepCount = 0;
|
||||||
let lastNotifyStepIndex = -1;
|
let lastNotifyStepIndex = -1;
|
||||||
let lastTaskStepIndex = -1;
|
let lastTaskStepIndex = -1;
|
||||||
|
// WAITING detection
|
||||||
|
let stalledPolls = 0;
|
||||||
|
let lastPendingStepIndex = -1; // dedup: don't re-create pending for same step
|
||||||
|
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
pollCount++;
|
pollCount++;
|
||||||
@@ -287,17 +290,79 @@ function setupMonitor() {
|
|||||||
lastKnownStepCount = currentCount;
|
lastKnownStepCount = currentCount;
|
||||||
lastNotifyStepIndex = bestSession.latestNotifyUserStep?.stepIndex ?? -1;
|
lastNotifyStepIndex = bestSession.latestNotifyUserStep?.stepIndex ?? -1;
|
||||||
lastTaskStepIndex = bestSession.latestTaskBoundaryStep?.stepIndex ?? -1;
|
lastTaskStepIndex = bestSession.latestTaskBoundaryStep?.stepIndex ?? -1;
|
||||||
|
stalledPolls = 0;
|
||||||
|
lastPendingStepIndex = -1;
|
||||||
writeRegistration(activeSessionId);
|
writeRegistration(activeSessionId);
|
||||||
console.log(`Gravity Bridge: [POLL#${pollCount}] session: ${activeSessionId.substring(0, 8)} "${currentTitle}" steps=${currentCount} ${isRunning ? 'RUNNING' : 'idle'}`);
|
console.log(`Gravity Bridge: [POLL#${pollCount}] session: ${activeSessionId.substring(0, 8)} "${currentTitle}" steps=${currentCount} ${isRunning ? 'RUNNING' : 'idle'}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No change?
|
// ── WAITING Detection ──
|
||||||
if (currentCount <= lastKnownStepCount && pollCount > 1) {
|
// stepCount frozen + session still RUNNING = likely WAITING for user approval
|
||||||
if (pollCount % 30 === 0) {
|
if (currentCount === lastKnownStepCount && isRunning) {
|
||||||
console.log(`Gravity Bridge: [POLL#${pollCount}] idle steps=${currentCount}`);
|
stalledPolls++;
|
||||||
|
if (stalledPolls >= 2) {
|
||||||
|
// Query last steps to check for WAITING/PENDING
|
||||||
|
try {
|
||||||
|
const stepsResp = await sdk.ls.rawRPC('GetCascadeTrajectorySteps', { cascadeId: bestSessionId });
|
||||||
|
const steps = stepsResp?.steps || [];
|
||||||
|
if (steps.length > 0) {
|
||||||
|
const lastStep = steps[steps.length - 1];
|
||||||
|
const stepStatus = (lastStep.status || '').replace('CORTEX_STEP_STATUS_', '');
|
||||||
|
const stepType = (lastStep.type || '').replace('CORTEX_STEP_TYPE_', '');
|
||||||
|
const stepIdx = lastStep.metadata?.sourceTrajectoryStepInfo?.stepIndex ?? steps.length - 1;
|
||||||
|
|
||||||
|
// Non-DONE step while session is RUNNING = user action needed
|
||||||
|
if (stepStatus !== 'DONE' && stepStatus !== 'REJECTED' && stepIdx > lastPendingStepIndex) {
|
||||||
|
// Extract command info from step
|
||||||
|
let cmd = 'unknown';
|
||||||
|
let desc = `${stepType} (${stepStatus})`;
|
||||||
|
const toolName = lastStep.metadata?.toolCall?.name || '';
|
||||||
|
|
||||||
|
if (lastStep.terminalCommand?.command) {
|
||||||
|
cmd = lastStep.terminalCommand.command;
|
||||||
|
desc = `Terminal: ${cmd}`;
|
||||||
|
} else if (toolName === 'run_command') {
|
||||||
|
// Extract from toolCall arguments
|
||||||
|
try {
|
||||||
|
const args = JSON.parse(lastStep.metadata.toolCall.argumentsJson || '{}');
|
||||||
|
cmd = args.CommandLine || args.command || toolName;
|
||||||
|
desc = `Command: ${cmd}`;
|
||||||
|
} catch { cmd = toolName; }
|
||||||
|
} else if (toolName) {
|
||||||
|
cmd = toolName;
|
||||||
|
desc = `Tool: ${toolName}`;
|
||||||
}
|
}
|
||||||
// Still process notify/task below (stepIndex may update without stepCount change)
|
|
||||||
|
// Auto-create pending file
|
||||||
|
const rid = Date.now().toString();
|
||||||
|
const pending = {
|
||||||
|
request_id: rid,
|
||||||
|
conversation_id: bestSessionId,
|
||||||
|
command: cmd.substring(0, 200),
|
||||||
|
description: desc.substring(0, 200),
|
||||||
|
timestamp: Date.now() / 1000,
|
||||||
|
status: 'pending',
|
||||||
|
project_name: projectName,
|
||||||
|
step_index: stepIdx,
|
||||||
|
step_type: stepType,
|
||||||
|
step_status: stepStatus,
|
||||||
|
auto_detected: true,
|
||||||
|
};
|
||||||
|
const pendingDir = path.join(bridgePath, 'pending');
|
||||||
|
fs.writeFileSync(path.join(pendingDir, `${rid}.json`), JSON.stringify(pending, null, 2));
|
||||||
|
lastPendingStepIndex = stepIdx;
|
||||||
|
stalledPolls = 0; // reset stall counter after pending created
|
||||||
|
logToFile(`[WAITING] detected step=${stepIdx} type=${stepType} status=${stepStatus} cmd=${cmd.substring(0, 60)}`);
|
||||||
|
console.log(`Gravity Bridge: [POLL#${pollCount}] WAITING detected! step=${stepIdx} cmd=${cmd.substring(0, 40)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
logToFile(`[WAITING] step query failed: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stalledPolls = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const delta = currentCount - lastKnownStepCount;
|
const delta = currentCount - lastKnownStepCount;
|
||||||
|
|||||||
Reference in New Issue
Block a user