aicodeman 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-checker-base.d.ts.map +1 -1
- package/dist/ai-checker-base.js +3 -2
- package/dist/ai-checker-base.js.map +1 -1
- package/dist/bash-tool-parser.d.ts +6 -0
- package/dist/bash-tool-parser.d.ts.map +1 -1
- package/dist/bash-tool-parser.js +87 -101
- package/dist/bash-tool-parser.js.map +1 -1
- package/dist/file-stream-manager.d.ts.map +1 -1
- package/dist/file-stream-manager.js +2 -1
- package/dist/file-stream-manager.js.map +1 -1
- package/dist/hooks-config.d.ts +5 -0
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +25 -0
- package/dist/hooks-config.js.map +1 -1
- package/dist/orchestrator-loop.d.ts +2 -0
- package/dist/orchestrator-loop.d.ts.map +1 -1
- package/dist/orchestrator-loop.js +27 -22
- package/dist/orchestrator-loop.js.map +1 -1
- package/dist/orchestrator-verifier.d.ts +1 -1
- package/dist/orchestrator-verifier.d.ts.map +1 -1
- package/dist/orchestrator-verifier.js +3 -2
- package/dist/orchestrator-verifier.js.map +1 -1
- package/dist/plan-orchestrator.d.ts +4 -1
- package/dist/plan-orchestrator.d.ts.map +1 -1
- package/dist/plan-orchestrator.js +66 -88
- package/dist/plan-orchestrator.js.map +1 -1
- package/dist/ralph-status-parser.d.ts +2 -0
- package/dist/ralph-status-parser.d.ts.map +1 -1
- package/dist/ralph-status-parser.js +98 -102
- package/dist/ralph-status-parser.js.map +1 -1
- package/dist/respawn-controller.d.ts +4 -1
- package/dist/respawn-controller.d.ts.map +1 -1
- package/dist/respawn-controller.js +165 -131
- package/dist/respawn-controller.js.map +1 -1
- package/dist/session.d.ts +18 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +129 -117
- package/dist/session.js.map +1 -1
- package/dist/state-store.d.ts +2 -0
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +22 -28
- package/dist/state-store.js.map +1 -1
- package/dist/subagent-watcher.d.ts +6 -0
- package/dist/subagent-watcher.d.ts.map +1 -1
- package/dist/subagent-watcher.js +145 -139
- package/dist/subagent-watcher.js.map +1 -1
- package/dist/tunnel-manager.d.ts.map +1 -1
- package/dist/tunnel-manager.js +2 -1
- package/dist/tunnel-manager.js.map +1 -1
- package/dist/web/public/api-client.3adebdc2.js.gz +0 -0
- package/dist/web/public/{app.3b7ff137.js → app.16290ae3.js} +2 -2
- package/dist/web/public/app.16290ae3.js.br +0 -0
- package/dist/web/public/app.16290ae3.js.gz +0 -0
- package/dist/web/public/constants.64161167.js.gz +0 -0
- package/dist/web/public/index.html +26 -8
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.88082175.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.9fb81db6.js.gz +0 -0
- package/dist/web/public/mobile-handlers.1e2a8ef8.js.gz +0 -0
- package/dist/web/public/mobile.9a61290c.css +1 -0
- package/dist/web/public/mobile.9a61290c.css.br +0 -0
- package/dist/web/public/mobile.9a61290c.css.gz +0 -0
- package/dist/web/public/notification-manager.2d5ea8ec.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.8204db1e.js → panels-ui.2d5b9703.js} +1 -1
- package/dist/web/public/panels-ui.2d5b9703.js.br +0 -0
- package/dist/web/public/panels-ui.2d5b9703.js.gz +0 -0
- package/dist/web/public/{ralph-panel.a2733fd5.js → ralph-panel.61076370.js} +1 -1
- package/dist/web/public/ralph-panel.61076370.js.br +0 -0
- package/dist/web/public/ralph-panel.61076370.js.gz +0 -0
- package/dist/web/public/ralph-wizard.f31ab90e.js.gz +0 -0
- package/dist/web/public/{respawn-ui.372c6ea7.js → respawn-ui.60be6ef5.js} +1 -1
- package/dist/web/public/respawn-ui.60be6ef5.js.br +0 -0
- package/dist/web/public/respawn-ui.60be6ef5.js.gz +0 -0
- package/dist/web/public/session-ui.ab189b7f.js +16 -0
- package/dist/web/public/session-ui.ab189b7f.js.br +0 -0
- package/dist/web/public/session-ui.ab189b7f.js.gz +0 -0
- package/dist/web/public/settings-ui.50a8018e.js +55 -0
- package/dist/web/public/settings-ui.50a8018e.js.br +0 -0
- package/dist/web/public/settings-ui.50a8018e.js.gz +0 -0
- package/dist/web/public/styles.111ff326.css.gz +0 -0
- package/dist/web/public/subagent-windows.a366a4ad.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.474f79df.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/dist/web/respawn-event-wiring.d.ts +51 -0
- package/dist/web/respawn-event-wiring.d.ts.map +1 -0
- package/dist/web/respawn-event-wiring.js +280 -0
- package/dist/web/respawn-event-wiring.js.map +1 -0
- package/dist/web/route-helpers.d.ts +9 -0
- package/dist/web/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +15 -0
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/orchestrator-routes.d.ts.map +1 -1
- package/dist/web/routes/orchestrator-routes.js +23 -30
- package/dist/web/routes/orchestrator-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +5 -1
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +12 -30
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +1 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +2 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +4 -51
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +98 -941
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +89 -0
- package/dist/web/session-listener-wiring.d.ts.map +1 -0
- package/dist/web/session-listener-wiring.js +290 -0
- package/dist/web/session-listener-wiring.js.map +1 -0
- package/dist/web/sse-stream-manager.d.ts +91 -0
- package/dist/web/sse-stream-manager.d.ts.map +1 -0
- package/dist/web/sse-stream-manager.js +426 -0
- package/dist/web/sse-stream-manager.js.map +1 -0
- package/package.json +1 -1
- package/dist/web/public/app.3b7ff137.js.br +0 -0
- package/dist/web/public/app.3b7ff137.js.gz +0 -0
- package/dist/web/public/mobile.0b213796.css +0 -1
- package/dist/web/public/mobile.0b213796.css.br +0 -0
- package/dist/web/public/mobile.0b213796.css.gz +0 -0
- package/dist/web/public/panels-ui.8204db1e.js.br +0 -0
- package/dist/web/public/panels-ui.8204db1e.js.gz +0 -0
- package/dist/web/public/ralph-panel.a2733fd5.js.br +0 -0
- package/dist/web/public/ralph-panel.a2733fd5.js.gz +0 -0
- package/dist/web/public/respawn-ui.372c6ea7.js.br +0 -0
- package/dist/web/public/respawn-ui.372c6ea7.js.gz +0 -0
- package/dist/web/public/session-ui.554092ae.js +0 -16
- package/dist/web/public/session-ui.554092ae.js.br +0 -0
- package/dist/web/public/session-ui.554092ae.js.gz +0 -0
- package/dist/web/public/settings-ui.bd3eaadb.js +0 -55
- package/dist/web/public/settings-ui.bd3eaadb.js.br +0 -0
- package/dist/web/public/settings-ui.bd3eaadb.js.gz +0 -0
|
@@ -53,6 +53,7 @@ import { RespawnAdaptiveTiming } from './respawn-adaptive-timing.js';
|
|
|
53
53
|
import { RespawnCycleMetricsTracker } from './respawn-metrics.js';
|
|
54
54
|
import { calculateHealthScore, shouldSkipClear } from './respawn-health.js';
|
|
55
55
|
import { AI_CHECK_MODEL, AI_IDLE_CHECK_MAX_CONTEXT, AI_PLAN_CHECK_MAX_CONTEXT, AI_IDLE_CHECK_TIMEOUT_MS, AI_IDLE_CHECK_COOLDOWN_MS, AI_PLAN_CHECK_TIMEOUT_MS, AI_PLAN_CHECK_COOLDOWN_MS, } from './config/ai-defaults.js';
|
|
56
|
+
import { getErrorMessage, } from './types.js';
|
|
56
57
|
// ========== Constants ==========
|
|
57
58
|
// COMPLETION_TIME_PATTERN moved to ./respawn-patterns.ts
|
|
58
59
|
/** Pre-filter: numbered option pattern for plan mode detection */
|
|
@@ -310,24 +311,27 @@ export class RespawnController extends EventEmitter {
|
|
|
310
311
|
c[field] = DEFAULT_CONFIG[field];
|
|
311
312
|
}
|
|
312
313
|
};
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
314
|
+
const REQUIRED_TIMEOUT_FIELDS = [
|
|
315
|
+
'idleTimeoutMs',
|
|
316
|
+
'completionConfirmMs',
|
|
317
|
+
'noOutputTimeoutMs',
|
|
318
|
+
'interStepDelayMs',
|
|
319
|
+
'aiIdleCheckTimeoutMs',
|
|
320
|
+
'aiIdleCheckMaxContext',
|
|
321
|
+
'aiPlanCheckTimeoutMs',
|
|
322
|
+
'aiPlanCheckMaxContext',
|
|
323
|
+
];
|
|
324
|
+
for (const field of REQUIRED_TIMEOUT_FIELDS) {
|
|
325
|
+
validatePositiveTimeout(field);
|
|
326
|
+
}
|
|
327
|
+
const ALLOW_ZERO_FIELDS = ['autoAcceptDelayMs', 'aiIdleCheckCooldownMs', 'aiPlanCheckCooldownMs'];
|
|
328
|
+
for (const field of ALLOW_ZERO_FIELDS) {
|
|
329
|
+
validatePositiveTimeout(field, true);
|
|
330
|
+
}
|
|
319
331
|
// Ensure completion confirm doesn't exceed no-output timeout
|
|
320
332
|
if (c.completionConfirmMs > c.noOutputTimeoutMs) {
|
|
321
333
|
c.completionConfirmMs = c.noOutputTimeoutMs;
|
|
322
334
|
}
|
|
323
|
-
// Ensure AI check timeouts are positive
|
|
324
|
-
validatePositiveTimeout('aiIdleCheckTimeoutMs');
|
|
325
|
-
validatePositiveTimeout('aiIdleCheckCooldownMs', true);
|
|
326
|
-
validatePositiveTimeout('aiIdleCheckMaxContext');
|
|
327
|
-
// Ensure plan check timeouts are positive
|
|
328
|
-
validatePositiveTimeout('aiPlanCheckTimeoutMs');
|
|
329
|
-
validatePositiveTimeout('aiPlanCheckCooldownMs', true);
|
|
330
|
-
validatePositiveTimeout('aiPlanCheckMaxContext');
|
|
331
335
|
}
|
|
332
336
|
/** Wire up AI checker events to controller events (removes existing listeners first to prevent duplicates) */
|
|
333
337
|
setupAiCheckerListeners() {
|
|
@@ -796,88 +800,13 @@ export class RespawnController extends EventEmitter {
|
|
|
796
800
|
this.lastTokenCount = tokenCount;
|
|
797
801
|
this.lastTokenChangeTime = now;
|
|
798
802
|
}
|
|
799
|
-
//
|
|
800
|
-
|
|
801
|
-
// the work is done, even if working patterns are still in the rolling window
|
|
802
|
-
if (isCompletionMessage(data)) {
|
|
803
|
-
// Clear the rolling window - completion marks a transition point
|
|
804
|
-
this.clearWorkingPatternWindow();
|
|
805
|
-
this.workingDetected = false;
|
|
806
|
-
this.completionMessageTime = now;
|
|
807
|
-
this.cancelAutoAcceptTimer(); // Normal idle flow handles this
|
|
808
|
-
this.log(`Completion message detected: "${data.trim().substring(0, 50)}..."`);
|
|
809
|
-
// In watching state, start completion confirmation timer
|
|
810
|
-
if (this._state === 'watching') {
|
|
811
|
-
this.startCompletionConfirmTimer();
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
// In waiting states, also use confirmation timer (same detection logic)
|
|
815
|
-
// This ensures we wait for Claude to finish before proceeding
|
|
816
|
-
// Note: 'watching' is already handled above and returns early
|
|
817
|
-
switch (this._state) {
|
|
818
|
-
case 'waiting_update':
|
|
819
|
-
this.startStepConfirmTimer('update');
|
|
820
|
-
break;
|
|
821
|
-
case 'waiting_clear':
|
|
822
|
-
this.checkClearComplete(); // /clear is quick, no need to wait
|
|
823
|
-
break;
|
|
824
|
-
case 'waiting_init':
|
|
825
|
-
this.startStepConfirmTimer('init');
|
|
826
|
-
break;
|
|
827
|
-
case 'waiting_kickstart':
|
|
828
|
-
this.startStepConfirmTimer('kickstart');
|
|
829
|
-
break;
|
|
830
|
-
// Non-waiting states: completion message is ignored
|
|
831
|
-
case 'confirming_idle':
|
|
832
|
-
case 'ai_checking':
|
|
833
|
-
case 'sending_update':
|
|
834
|
-
case 'sending_clear':
|
|
835
|
-
case 'sending_init':
|
|
836
|
-
case 'monitoring_init':
|
|
837
|
-
case 'sending_kickstart':
|
|
838
|
-
case 'stopped':
|
|
839
|
-
// Completion message during these states is ignored
|
|
840
|
-
break;
|
|
841
|
-
default:
|
|
842
|
-
assertNever(this._state, `Unhandled RespawnState in completion detection: ${this._state}`);
|
|
843
|
-
}
|
|
803
|
+
// Layer 1: Completion message (PRIMARY) — checked before working patterns
|
|
804
|
+
if (this._detectCompletionMessage(data, now))
|
|
844
805
|
return;
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
const isWorking = this.checkWorkingPattern(data);
|
|
848
|
-
if (isWorking) {
|
|
849
|
-
this.workingDetected = true;
|
|
850
|
-
this.promptDetected = false;
|
|
851
|
-
this.elicitationDetected = false; // Clear on new work cycle
|
|
852
|
-
this.resetHookState(); // Clear hook signals on new work
|
|
853
|
-
this.lastWorkingPatternTime = now;
|
|
854
|
-
// Cancel hook confirmation timer if running
|
|
855
|
-
this.cancelTrackedTimer('hook-confirm', 'working patterns detected');
|
|
856
|
-
// Cancel any pending completion confirmation
|
|
857
|
-
this.cancelCompletionConfirm();
|
|
858
|
-
// Cancel any pending step confirmation (Claude is still working)
|
|
859
|
-
this.cancelStepConfirm();
|
|
860
|
-
// If AI check is running, cancel it (Claude is working)
|
|
861
|
-
if (this._state === 'ai_checking') {
|
|
862
|
-
this.log('Working patterns detected during AI check, cancelling');
|
|
863
|
-
this.aiChecker.cancel();
|
|
864
|
-
this.setState('watching');
|
|
865
|
-
}
|
|
866
|
-
// Cancel plan check if running (Claude started working)
|
|
867
|
-
if (this.planChecker.status === 'checking') {
|
|
868
|
-
this.log('Working patterns detected during plan check, cancelling');
|
|
869
|
-
this.planChecker.cancel();
|
|
870
|
-
}
|
|
871
|
-
// If we're monitoring init and work started, go to watching (no kickstart needed)
|
|
872
|
-
if (this._state === 'monitoring_init') {
|
|
873
|
-
this.log('/init triggered work, skipping kickstart');
|
|
874
|
-
this.emit('stepCompleted', 'init');
|
|
875
|
-
this.completeCycle();
|
|
876
|
-
}
|
|
806
|
+
// Layer 4: Working patterns
|
|
807
|
+
if (this._detectWorkingPattern(data, now))
|
|
877
808
|
return;
|
|
878
|
-
|
|
879
|
-
// In confirming_idle or ai_checking state, substantial output cancels the flow.
|
|
880
|
-
// This prevents false triggers when Claude pauses briefly mid-work.
|
|
809
|
+
// Substantial output during confirming_idle/ai_checking cancels the flow
|
|
881
810
|
if (this._state === 'confirming_idle' || this._state === 'ai_checking') {
|
|
882
811
|
// Strip ANSI escape codes to check if there's real content
|
|
883
812
|
ANSI_ESCAPE_PATTERN_SIMPLE.lastIndex = 0;
|
|
@@ -896,42 +825,125 @@ export class RespawnController extends EventEmitter {
|
|
|
896
825
|
return;
|
|
897
826
|
}
|
|
898
827
|
}
|
|
899
|
-
// Legacy fallback:
|
|
828
|
+
// Legacy fallback: prompt detection
|
|
829
|
+
this._detectPrompt(data);
|
|
830
|
+
}
|
|
831
|
+
_detectCompletionMessage(data, now) {
|
|
832
|
+
if (!isCompletionMessage(data))
|
|
833
|
+
return false;
|
|
834
|
+
// Clear the rolling window - completion marks a transition point
|
|
835
|
+
this.clearWorkingPatternWindow();
|
|
836
|
+
this.workingDetected = false;
|
|
837
|
+
this.completionMessageTime = now;
|
|
838
|
+
this.cancelAutoAcceptTimer(); // Normal idle flow handles this
|
|
839
|
+
this.log(`Completion message detected: "${data.trim().substring(0, 50)}..."`);
|
|
840
|
+
// In watching state, start completion confirmation timer
|
|
841
|
+
if (this._state === 'watching') {
|
|
842
|
+
this.startCompletionConfirmTimer();
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
// In waiting states, also use confirmation timer (same detection logic)
|
|
846
|
+
// This ensures we wait for Claude to finish before proceeding
|
|
847
|
+
// Note: 'watching' is already handled above and returns early
|
|
848
|
+
switch (this._state) {
|
|
849
|
+
case 'waiting_update':
|
|
850
|
+
this.startStepConfirmTimer('update');
|
|
851
|
+
break;
|
|
852
|
+
case 'waiting_clear':
|
|
853
|
+
this.checkClearComplete(); // /clear is quick, no need to wait
|
|
854
|
+
break;
|
|
855
|
+
case 'waiting_init':
|
|
856
|
+
this.startStepConfirmTimer('init');
|
|
857
|
+
break;
|
|
858
|
+
case 'waiting_kickstart':
|
|
859
|
+
this.startStepConfirmTimer('kickstart');
|
|
860
|
+
break;
|
|
861
|
+
// Non-waiting states: completion message is ignored
|
|
862
|
+
case 'confirming_idle':
|
|
863
|
+
case 'ai_checking':
|
|
864
|
+
case 'sending_update':
|
|
865
|
+
case 'sending_clear':
|
|
866
|
+
case 'sending_init':
|
|
867
|
+
case 'monitoring_init':
|
|
868
|
+
case 'sending_kickstart':
|
|
869
|
+
case 'stopped':
|
|
870
|
+
// Completion message during these states is ignored
|
|
871
|
+
break;
|
|
872
|
+
default:
|
|
873
|
+
assertNever(this._state, `Unhandled RespawnState in completion detection: ${this._state}`);
|
|
874
|
+
}
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
_detectWorkingPattern(data, now) {
|
|
878
|
+
const isWorking = this.checkWorkingPattern(data);
|
|
879
|
+
if (!isWorking)
|
|
880
|
+
return false;
|
|
881
|
+
this.workingDetected = true;
|
|
882
|
+
this.promptDetected = false;
|
|
883
|
+
this.elicitationDetected = false; // Clear on new work cycle
|
|
884
|
+
this.resetHookState(); // Clear hook signals on new work
|
|
885
|
+
this.lastWorkingPatternTime = now;
|
|
886
|
+
// Cancel hook confirmation timer if running
|
|
887
|
+
this.cancelTrackedTimer('hook-confirm', 'working patterns detected');
|
|
888
|
+
// Cancel any pending completion confirmation
|
|
889
|
+
this.cancelCompletionConfirm();
|
|
890
|
+
// Cancel any pending step confirmation (Claude is still working)
|
|
891
|
+
this.cancelStepConfirm();
|
|
892
|
+
// If AI check is running, cancel it (Claude is working)
|
|
893
|
+
if (this._state === 'ai_checking') {
|
|
894
|
+
this.log('Working patterns detected during AI check, cancelling');
|
|
895
|
+
this.aiChecker.cancel();
|
|
896
|
+
this.setState('watching');
|
|
897
|
+
}
|
|
898
|
+
// Cancel plan check if running (Claude started working)
|
|
899
|
+
if (this.planChecker.status === 'checking') {
|
|
900
|
+
this.log('Working patterns detected during plan check, cancelling');
|
|
901
|
+
this.planChecker.cancel();
|
|
902
|
+
}
|
|
903
|
+
// If we're monitoring init and work started, go to watching (no kickstart needed)
|
|
904
|
+
if (this._state === 'monitoring_init') {
|
|
905
|
+
this.log('/init triggered work, skipping kickstart');
|
|
906
|
+
this.emit('stepCompleted', 'init');
|
|
907
|
+
this.completeCycle();
|
|
908
|
+
}
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
_detectPrompt(data) {
|
|
900
912
|
const hasPrompt = PROMPT_PATTERNS.some((pattern) => data.includes(pattern));
|
|
901
|
-
if (hasPrompt)
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
913
|
+
if (!hasPrompt)
|
|
914
|
+
return;
|
|
915
|
+
this.promptDetected = true;
|
|
916
|
+
this.workingDetected = false;
|
|
917
|
+
// Handle legacy detection in waiting states - also use confirmation timers
|
|
918
|
+
switch (this._state) {
|
|
919
|
+
case 'waiting_update':
|
|
920
|
+
this.startStepConfirmTimer('update');
|
|
921
|
+
break;
|
|
922
|
+
case 'waiting_clear':
|
|
923
|
+
this.checkClearComplete(); // /clear is quick, no need to wait
|
|
924
|
+
break;
|
|
925
|
+
case 'waiting_init':
|
|
926
|
+
this.startStepConfirmTimer('init');
|
|
927
|
+
break;
|
|
928
|
+
case 'monitoring_init':
|
|
929
|
+
this.checkMonitoringInitIdle();
|
|
930
|
+
break;
|
|
931
|
+
case 'waiting_kickstart':
|
|
932
|
+
this.startStepConfirmTimer('kickstart');
|
|
933
|
+
break;
|
|
934
|
+
// Non-waiting states: prompt detection is informational only
|
|
935
|
+
case 'watching':
|
|
936
|
+
case 'confirming_idle':
|
|
937
|
+
case 'ai_checking':
|
|
938
|
+
case 'sending_update':
|
|
939
|
+
case 'sending_clear':
|
|
940
|
+
case 'sending_init':
|
|
941
|
+
case 'sending_kickstart':
|
|
942
|
+
case 'stopped':
|
|
943
|
+
// Prompt detection during these states doesn't trigger action
|
|
944
|
+
break;
|
|
945
|
+
default:
|
|
946
|
+
assertNever(this._state, `Unhandled RespawnState in prompt detection: ${this._state}`);
|
|
935
947
|
}
|
|
936
948
|
}
|
|
937
949
|
/**
|
|
@@ -1375,6 +1387,17 @@ export class RespawnController extends EventEmitter {
|
|
|
1375
1387
|
this.logAction('detection', 'Skipped AI check: Session is working');
|
|
1376
1388
|
return;
|
|
1377
1389
|
}
|
|
1390
|
+
// Check for active child processes (bash tools, test suites, builds, etc.)
|
|
1391
|
+
// These may produce no terminal output, so restart timers to retry periodically.
|
|
1392
|
+
const activeProcesses = this.session.getActiveChildProcesses();
|
|
1393
|
+
if (activeProcesses.length > 0) {
|
|
1394
|
+
const names = activeProcesses.map((p) => p.command).join(', ');
|
|
1395
|
+
this.log(`Skipping AI check - ${activeProcesses.length} active child process(es): ${names}`);
|
|
1396
|
+
this.logAction('detection', `Skipped AI check: child processes running (${names})`);
|
|
1397
|
+
this.startNoOutputTimer();
|
|
1398
|
+
this.startPreFilterTimer();
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1378
1401
|
// If AI check is disabled or errored out, fall back to direct idle confirmation
|
|
1379
1402
|
if (!this.config.aiIdleCheckEnabled || this.aiChecker.status === 'disabled') {
|
|
1380
1403
|
this.log(`AI check unavailable (${this.aiChecker.status}), confirming idle directly via: ${reason}`);
|
|
@@ -1459,7 +1482,7 @@ export class RespawnController extends EventEmitter {
|
|
|
1459
1482
|
if (this._state === 'stopped')
|
|
1460
1483
|
return; // Guard against stopped state
|
|
1461
1484
|
if (this._state === 'ai_checking') {
|
|
1462
|
-
const errorMsg =
|
|
1485
|
+
const errorMsg = getErrorMessage(err);
|
|
1463
1486
|
this.logAction('ai-check', `Failed: ${errorMsg.substring(0, 50)}`);
|
|
1464
1487
|
this.emit('aiCheckFailed', errorMsg);
|
|
1465
1488
|
this.setState('watching');
|
|
@@ -1637,7 +1660,7 @@ export class RespawnController extends EventEmitter {
|
|
|
1637
1660
|
}
|
|
1638
1661
|
})
|
|
1639
1662
|
.catch((err) => {
|
|
1640
|
-
const errorMsg =
|
|
1663
|
+
const errorMsg = getErrorMessage(err);
|
|
1641
1664
|
this.emit('planCheckFailed', errorMsg);
|
|
1642
1665
|
this.logAction('plan-check', `Failed: ${errorMsg.substring(0, 50)}`);
|
|
1643
1666
|
});
|
|
@@ -1899,6 +1922,17 @@ export class RespawnController extends EventEmitter {
|
|
|
1899
1922
|
this.startPreFilterTimer();
|
|
1900
1923
|
return;
|
|
1901
1924
|
}
|
|
1925
|
+
// Safety check: if child processes are running (bash tools, test suites, builds, etc.)
|
|
1926
|
+
const activeProcesses = this.session.getActiveChildProcesses();
|
|
1927
|
+
if (activeProcesses.length > 0) {
|
|
1928
|
+
const names = activeProcesses.map((p) => p.command).join(', ');
|
|
1929
|
+
this.log(`Idle confirmation rejected - ${activeProcesses.length} active child process(es): ${names}`);
|
|
1930
|
+
this.logAction('detection', `Rejected: child processes running (${names})`);
|
|
1931
|
+
this.setState('watching');
|
|
1932
|
+
this.startNoOutputTimer();
|
|
1933
|
+
this.startPreFilterTimer();
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1902
1936
|
this.log(`Idle confirmed via: ${reason}`);
|
|
1903
1937
|
const status = this.getDetectionStatus();
|
|
1904
1938
|
this.log(`Detection status: confidence=${status.confidenceLevel}%, ` +
|