mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 00:49:41 +02:00
fix: respawn controller idle detection - prevent premature firing
Three improvements to prevent the respawn controller from sending update prompts while Claude is still working: 1. Tighten COMPLETION_TIME_PATTERN to require "Worked for" prefix, avoiding false positives from bare time durations in regular text (e.g., "wait for 5s") 2. Increase completionConfirmMs default from 5s to 10s - Claude regularly pauses 5-8s between tool calls and during thinking 3. Cancel confirming_idle state when substantial output arrives (stripped of ANSI codes), instead of passively relying on the timer to self-restart Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -196,7 +196,7 @@ WATCHING → CONFIRMING_IDLE → SENDING_UPDATE → WAITING_UPDATE → SENDING_C
|
|||||||
|
|
||||||
Steps can be skipped via config (`sendClear: false`, `sendInit: false`). Optional `kickstartPrompt` triggers if `/init` doesn't start work. Multi-layer idle detection triggers state transitions.
|
Steps can be skipped via config (`sendClear: false`, `sendInit: false`). Optional `kickstartPrompt` triggers if `/init` doesn't start work. Multi-layer idle detection triggers state transitions.
|
||||||
|
|
||||||
**Step confirmation**: After sending each step (update, clear, init, kickstart), the controller waits for `completionConfirmMs` (5s) of output silence before proceeding to the next step. This prevents sending commands while Claude is still processing.
|
**Step confirmation**: After sending each step (update, clear, init, kickstart), the controller waits for `completionConfirmMs` (10s) of output silence before proceeding to the next step. This prevents sending commands while Claude is still processing.
|
||||||
|
|
||||||
### Spawn1337 Protocol (Autonomous Agents)
|
### Spawn1337 Protocol (Autonomous Agents)
|
||||||
|
|
||||||
@@ -386,7 +386,7 @@ curl -X POST localhost:3000/api/sessions/:id/input \
|
|||||||
|
|
||||||
**RespawnController (Claude Code 2024+)**: Multi-layer detection with confidence scoring:
|
**RespawnController (Claude Code 2024+)**: Multi-layer detection with confidence scoring:
|
||||||
1. **Completion message**: Primary signal - detects "for Xm Xs" time patterns (e.g., "Worked for 2m 46s")
|
1. **Completion message**: Primary signal - detects "for Xm Xs" time patterns (e.g., "Worked for 2m 46s")
|
||||||
2. **Output silence**: Confirms idle after `completionConfirmMs` (5s) of no new output
|
2. **Output silence**: Confirms idle after `completionConfirmMs` (10s) of no new output
|
||||||
3. **Token stability**: Tokens haven't changed
|
3. **Token stability**: Tokens haven't changed
|
||||||
4. **Working patterns absent**: No `Thinking`, `Writing`, spinner chars
|
4. **Working patterns absent**: No `Thinking`, `Writing`, spinner chars
|
||||||
|
|
||||||
@@ -572,7 +572,7 @@ Writes debounced (500ms) to `~/.claudeman/state.json`. The web server persists f
|
|||||||
| Input batch interval | 16ms | `app.js` (60fps) |
|
| Input batch interval | 16ms | `app.js` (60fps) |
|
||||||
| Idle activity timeout | 2s | `session.ts` |
|
| Idle activity timeout | 2s | `session.ts` |
|
||||||
| Respawn idle timeout | 10s default | `RespawnConfig.idleTimeoutMs` |
|
| Respawn idle timeout | 10s default | `RespawnConfig.idleTimeoutMs` |
|
||||||
| Respawn completion confirm | 5s | `RespawnConfig.completionConfirmMs` |
|
| Respawn completion confirm | 10s | `RespawnConfig.completionConfirmMs` |
|
||||||
| Respawn auto-accept delay | 8s | `RespawnConfig.autoAcceptDelayMs` |
|
| Respawn auto-accept delay | 8s | `RespawnConfig.autoAcceptDelayMs` |
|
||||||
| Respawn no-output fallback | 30s | `RespawnConfig.noOutputTimeoutMs` |
|
| Respawn no-output fallback | 30s | `RespawnConfig.noOutputTimeoutMs` |
|
||||||
| Spawn event debounce | 50ms | `spawn-detector.ts` |
|
| Spawn event debounce | 50ms | `spawn-detector.ts` |
|
||||||
|
|||||||
+21
-16
@@ -28,7 +28,7 @@
|
|||||||
* - `sendClear`: Whether to send /clear after update (default: true)
|
* - `sendClear`: Whether to send /clear after update (default: true)
|
||||||
* - `sendInit`: Whether to send /init after clear (default: true)
|
* - `sendInit`: Whether to send /init after clear (default: true)
|
||||||
* - `kickstartPrompt`: Optional prompt if /init doesn't trigger work
|
* - `kickstartPrompt`: Optional prompt if /init doesn't trigger work
|
||||||
* - `completionConfirmMs`: Time to wait after completion message (default: 5000)
|
* - `completionConfirmMs`: Time to wait after completion message (default: 10000)
|
||||||
* - `noOutputTimeoutMs`: Fallback timeout with no output at all (default: 30000)
|
* - `noOutputTimeoutMs`: Fallback timeout with no output at all (default: 30000)
|
||||||
*
|
*
|
||||||
* @module respawn-controller
|
* @module respawn-controller
|
||||||
@@ -54,11 +54,14 @@ const RESPAWN_BUFFER_TRIM_SIZE = 512 * 1024; // 512KB
|
|||||||
// ========== Constants ==========
|
// ========== Constants ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pattern to detect completion messages from Claude.
|
* Pattern to detect completion messages from Claude Code.
|
||||||
* Matches "for Xh Xm Xs" time duration patterns that appear at end of work.
|
* Requires "Worked for" prefix to avoid false positives from bare time durations
|
||||||
* Examples: "for 2m 46s", "for 46s", "for 1h 2m 3s", "for 5m"
|
* in regular text (e.g., "wait for 5s", "run for 2m").
|
||||||
|
*
|
||||||
|
* Matches: "✻ Worked for 2m 46s", "Worked for 46s", "Worked for 1h 2m 3s"
|
||||||
|
* Does NOT match: "wait for 5s", "run for 2m", "for 3s the system..."
|
||||||
*/
|
*/
|
||||||
const COMPLETION_TIME_PATTERN = /\bfor\s+\d+[hms](\s*\d+[hms])*/i;
|
const COMPLETION_TIME_PATTERN = /\bWorked\s+for\s+\d+[hms](\s*\d+[hms])*/i;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pattern to extract token count from Claude's status line.
|
* Pattern to extract token count from Claude's status line.
|
||||||
@@ -257,7 +260,7 @@ export interface RespawnConfig {
|
|||||||
/**
|
/**
|
||||||
* Time to wait after completion message before confirming idle (ms).
|
* Time to wait after completion message before confirming idle (ms).
|
||||||
* After seeing "for Xm Xs" pattern, waits this long with no new output.
|
* After seeing "for Xm Xs" pattern, waits this long with no new output.
|
||||||
* @default 5000 (5 seconds)
|
* @default 10000 (10 seconds)
|
||||||
*/
|
*/
|
||||||
completionConfirmMs: number;
|
completionConfirmMs: number;
|
||||||
|
|
||||||
@@ -329,7 +332,7 @@ const DEFAULT_CONFIG: RespawnConfig = {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
sendClear: true, // send /clear after update prompt
|
sendClear: true, // send /clear after update prompt
|
||||||
sendInit: true, // send /init after /clear
|
sendInit: true, // send /init after /clear
|
||||||
completionConfirmMs: 5000, // 5 seconds of silence after completion message
|
completionConfirmMs: 10000, // 10 seconds of silence after completion message
|
||||||
noOutputTimeoutMs: 30000, // 30 seconds fallback if no output at all
|
noOutputTimeoutMs: 30000, // 30 seconds fallback if no output at all
|
||||||
autoAcceptPrompts: true, // auto-accept plan mode prompts (not questions)
|
autoAcceptPrompts: true, // auto-accept plan mode prompts (not questions)
|
||||||
autoAcceptDelayMs: 8000, // 8 seconds before auto-accepting
|
autoAcceptDelayMs: 8000, // 8 seconds before auto-accepting
|
||||||
@@ -357,7 +360,7 @@ const DEFAULT_CONFIG: RespawnConfig = {
|
|||||||
* The pattern "for Xm Xs" indicates Claude finished work and reports duration.
|
* The pattern "for Xm Xs" indicates Claude finished work and reports duration.
|
||||||
*
|
*
|
||||||
* Confirmation: After seeing completion message, waits for output silence.
|
* Confirmation: After seeing completion message, waits for output silence.
|
||||||
* If no new output for `completionConfirmMs` (default 5s), confirms idle.
|
* If no new output for `completionConfirmMs` (default 10s), confirms idle.
|
||||||
*
|
*
|
||||||
* Fallback: If no output at all for `noOutputTimeoutMs` (default 30s), assumes idle.
|
* Fallback: If no output at all for `noOutputTimeoutMs` (default 30s), assumes idle.
|
||||||
*
|
*
|
||||||
@@ -377,7 +380,7 @@ const DEFAULT_CONFIG: RespawnConfig = {
|
|||||||
* ```typescript
|
* ```typescript
|
||||||
* const respawn = new RespawnController(session, {
|
* const respawn = new RespawnController(session, {
|
||||||
* updatePrompt: 'continue working on the task',
|
* updatePrompt: 'continue working on the task',
|
||||||
* completionConfirmMs: 5000, // Wait 5s after completion message
|
* completionConfirmMs: 10000, // Wait 10s after completion message
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* respawn.on('respawnCycleCompleted', (cycle) => {
|
* respawn.on('respawnCycleCompleted', (cycle) => {
|
||||||
@@ -839,14 +842,16 @@ export class RespawnController extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In confirming_idle state, any output (except completion) resets confirmation
|
// In confirming_idle state, substantial output cancels the confirmation.
|
||||||
|
// This prevents false triggers when Claude pauses briefly mid-work.
|
||||||
if (this._state === 'confirming_idle') {
|
if (this._state === 'confirming_idle') {
|
||||||
// Check if enough time has passed since completion message
|
// Strip ANSI escape codes to check if there's real content
|
||||||
const msSinceCompletion = this.completionMessageTime ? now - this.completionMessageTime : 0;
|
const stripped = data.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '').trim();
|
||||||
if (msSinceCompletion > 1000) {
|
if (stripped.length > 2) {
|
||||||
// New output more than 1s after completion message - might be new work
|
// Real content (not just escape codes or single chars) - cancel confirmation
|
||||||
this.log('New output during confirmation, checking if work resumed...');
|
this.log(`Substantial output during confirmation ("${stripped.substring(0, 40)}..."), cancelling idle detection`);
|
||||||
// Don't immediately cancel - the confirmation timer will handle it
|
this.cancelCompletionConfirm();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user