mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 07:59:42 +02:00
fix: restrict auto-accept to plan mode only, block AskUserQuestion prompts
The auto-accept feature was too aggressive - it would press Enter for any silence without a completion message, including AskUserQuestion prompts. Now uses the elicitation_dialog notification hook to detect when Claude is asking a question, and blocks auto-accept in that case. Only plan mode approvals (silence with no completion message AND no elicitation signal) trigger auto-accept. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+43
-18
@@ -269,17 +269,20 @@ export interface RespawnConfig {
|
||||
noOutputTimeoutMs: number;
|
||||
|
||||
/**
|
||||
* Whether to auto-accept prompts (plan mode approvals, question selections).
|
||||
* When Claude enters plan mode or asks a question, output stops without a completion
|
||||
* message. This feature detects that state and sends Enter to accept the default option.
|
||||
* Whether to auto-accept plan mode prompts by pressing Enter.
|
||||
* When Claude enters plan mode and presents a plan for approval, output stops
|
||||
* without a completion message. This feature detects that state and sends Enter
|
||||
* to accept the plan. Does NOT auto-accept AskUserQuestion prompts (those are
|
||||
* blocked via the elicitation_dialog hook signal).
|
||||
* @default true
|
||||
*/
|
||||
autoAcceptPrompts: boolean;
|
||||
|
||||
/**
|
||||
* Delay before auto-accepting prompts (ms).
|
||||
* After no output for this duration AND no completion message detected,
|
||||
* sends Enter to accept the current prompt. Must be shorter than noOutputTimeoutMs.
|
||||
* Delay before auto-accepting plan mode prompts (ms).
|
||||
* After no output for this duration AND no completion message detected
|
||||
* AND no elicitation dialog signaled, sends Enter to accept the plan.
|
||||
* Must be shorter than noOutputTimeoutMs.
|
||||
* @default 8000 (8 seconds)
|
||||
*/
|
||||
autoAcceptDelayMs: number;
|
||||
@@ -310,7 +313,7 @@ export interface RespawnEvents {
|
||||
stepCompleted: (step: string) => void;
|
||||
/** Detection status update for UI display */
|
||||
detectionUpdate: (status: DetectionStatus) => void;
|
||||
/** Auto-accept prompt sent (plan mode, question, etc.) */
|
||||
/** Auto-accept sent for plan mode approval */
|
||||
autoAcceptSent: () => void;
|
||||
/** Error occurred */
|
||||
error: (error: Error) => void;
|
||||
@@ -328,7 +331,7 @@ const DEFAULT_CONFIG: RespawnConfig = {
|
||||
sendInit: true, // send /init after /clear
|
||||
completionConfirmMs: 5000, // 5 seconds of silence after completion message
|
||||
noOutputTimeoutMs: 30000, // 30 seconds fallback if no output at all
|
||||
autoAcceptPrompts: true, // auto-accept plan mode and question prompts
|
||||
autoAcceptPrompts: true, // auto-accept plan mode prompts (not questions)
|
||||
autoAcceptDelayMs: 8000, // 8 seconds before auto-accepting
|
||||
};
|
||||
|
||||
@@ -409,12 +412,15 @@ export class RespawnController extends EventEmitter {
|
||||
/** Timer for periodic detection status updates */
|
||||
private detectionUpdateTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
/** Timer for auto-accepting prompts (plan mode, questions) */
|
||||
/** Timer for auto-accepting plan mode prompts */
|
||||
private autoAcceptTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
/** Whether any terminal output has been received since start/last-auto-accept */
|
||||
private hasReceivedOutput: boolean = false;
|
||||
|
||||
/** Whether an elicitation dialog (AskUserQuestion) was detected via hook signal */
|
||||
private elicitationDetected: boolean = false;
|
||||
|
||||
/** Number of completed respawn cycles */
|
||||
private cycleCount: number = 0;
|
||||
|
||||
@@ -782,6 +788,7 @@ export class RespawnController extends EventEmitter {
|
||||
if (isWorking) {
|
||||
this.workingDetected = true;
|
||||
this.promptDetected = false;
|
||||
this.elicitationDetected = false; // Clear on new work cycle
|
||||
this.lastWorkingPatternTime = now;
|
||||
this.clearIdleTimer();
|
||||
|
||||
@@ -1109,8 +1116,8 @@ export class RespawnController extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Start the auto-accept timer.
|
||||
* Fires after autoAcceptDelayMs of no output when no completion message was detected.
|
||||
* This handles plan mode approvals and question prompts by pressing Enter.
|
||||
* Fires after autoAcceptDelayMs of no output when no completion message
|
||||
* and no elicitation dialog was detected. Only handles plan mode approvals.
|
||||
*/
|
||||
private startAutoAcceptTimer(): void {
|
||||
if (this.autoAcceptTimer) {
|
||||
@@ -1134,15 +1141,16 @@ export class RespawnController extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to auto-accept a prompt by sending Enter.
|
||||
* Attempt to auto-accept a plan mode prompt by sending Enter.
|
||||
* Only fires when:
|
||||
* - In 'watching' state (not mid-cycle)
|
||||
* - No completion message was detected (Claude is waiting for input, not truly idle)
|
||||
* - No elicitation dialog was detected (not an AskUserQuestion prompt)
|
||||
* - autoAcceptPrompts is enabled
|
||||
*
|
||||
* This handles Claude's plan mode (waiting for approval) and
|
||||
* AskUserQuestion (waiting for option selection) by pressing Enter
|
||||
* to accept the default/currently-selected option.
|
||||
* This handles Claude's plan mode (waiting for approval) by pressing Enter
|
||||
* to accept the plan. It does NOT auto-accept AskUserQuestion prompts -
|
||||
* those require explicit user interaction.
|
||||
*
|
||||
* @fires autoAcceptSent
|
||||
*/
|
||||
@@ -1159,10 +1167,16 @@ export class RespawnController extends EventEmitter {
|
||||
// Don't auto-accept if we haven't received any output yet (prevents spurious Enter on fresh start)
|
||||
if (!this.hasReceivedOutput) return;
|
||||
|
||||
const msSinceOutput = Date.now() - this.lastOutputTime;
|
||||
this.log(`Auto-accepting prompt (${msSinceOutput}ms silence, no completion message)`);
|
||||
// Don't auto-accept if an elicitation dialog (AskUserQuestion) was detected
|
||||
if (this.elicitationDetected) {
|
||||
this.log('Skipping auto-accept: elicitation dialog detected (AskUserQuestion)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send Enter to accept the current prompt/selection
|
||||
const msSinceOutput = Date.now() - this.lastOutputTime;
|
||||
this.log(`Auto-accepting plan mode prompt (${msSinceOutput}ms silence, no completion message, no elicitation)`);
|
||||
|
||||
// Send Enter to accept the plan
|
||||
this.session.writeViaScreen('\r');
|
||||
this.emit('autoAcceptSent');
|
||||
|
||||
@@ -1170,6 +1184,17 @@ export class RespawnController extends EventEmitter {
|
||||
this.hasReceivedOutput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that an elicitation dialog (AskUserQuestion) was detected via hook.
|
||||
* This prevents auto-accept from firing, since the user needs to make a selection.
|
||||
* The flag is cleared when working patterns are detected (new turn starts).
|
||||
*/
|
||||
signalElicitation(): void {
|
||||
this.elicitationDetected = true;
|
||||
this.cancelAutoAcceptTimer();
|
||||
this.log('Elicitation dialog signaled - auto-accept blocked until next work cycle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Start completion confirmation timer.
|
||||
* After completion message, waits for output silence.
|
||||
|
||||
Reference in New Issue
Block a user