mirror of https://github.com/garrytan/gstack.git
fix: terminal-agent respawn guard window spans guard-max ticks
The crash-loop guard (3 respawns inside RESPAWN_GUARD_WINDOW_MS -> stop and require manual restart) could never trip: respawn attempts land at most once per watchdog tick, and the window (60s) equaled the tick interval (60s), so the rolling history never held more than one entry. A crash-looping agent respawned once a minute for the daemon lifetime (#2151). Size the window as max(60s, tick * guard-max) so three consecutive failed ticks trip the guard at any tick setting. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f48623ecac
commit
b4c22b04dc
|
|
@ -1504,9 +1504,9 @@ export function buildFetchHandler(cfg: ServerConfig): ServerHandle {
|
|||
// (which would create split-brain — two agents writing the port file,
|
||||
// tokens diverging between them, mystery PTY upgrade failures).
|
||||
//
|
||||
// Crash-loop guard: 3 respawn attempts inside 60s → stop trying and emit
|
||||
// a one-line error. Manual `forceRestart` from the sidebar clears the
|
||||
// history (the user is the explicit signal to retry).
|
||||
// Crash-loop guard: 3 respawn attempts inside 3 ticks → stop trying and
|
||||
// emit a one-line error. Manual `forceRestart` from the sidebar clears
|
||||
// the history (the user is the explicit signal to retry).
|
||||
//
|
||||
// Only active when ownsTerminalAgent === true. Embedders that pre-launch
|
||||
// their own PTY server (gbrowser phoenix overlay) must not be auto-respawned
|
||||
|
|
@ -1517,8 +1517,11 @@ export function buildFetchHandler(cfg: ServerConfig): ServerHandle {
|
|||
process.env.GSTACK_AGENT_WATCHDOG_TICK_MS || '60000',
|
||||
10,
|
||||
);
|
||||
const RESPAWN_GUARD_WINDOW_MS = 60_000;
|
||||
const RESPAWN_GUARD_MAX = 3;
|
||||
// The guard must span at least RESPAWN_GUARD_MAX ticks or it can never
|
||||
// trip: respawn attempts land at most once per tick, so a 60s window
|
||||
// over a 60s tick sees a single attempt forever (see #2151).
|
||||
const RESPAWN_GUARD_WINDOW_MS = Math.max(60_000, AGENT_WATCHDOG_TICK_MS * RESPAWN_GUARD_MAX);
|
||||
let agentRespawnGuardTripped = false;
|
||||
|
||||
if (ownsTerminalAgent) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,12 @@ describe('terminal-agent watchdog (v1.44+)', () => {
|
|||
test('4. crash-loop guard with rolling window', () => {
|
||||
const src = fs.readFileSync(SERVER_TS, 'utf-8');
|
||||
const block = sliceBetween(src, '─── Terminal-Agent Watchdog', 'Factory-scoped validateAuth');
|
||||
expect(block).toContain('RESPAWN_GUARD_WINDOW_MS = 60_000');
|
||||
// The window must span at least RESPAWN_GUARD_MAX ticks. Respawn
|
||||
// attempts land at most once per tick, so a fixed 60s window over a
|
||||
// 60s tick would see a single attempt forever and never trip (#2151).
|
||||
expect(block).toContain(
|
||||
'RESPAWN_GUARD_WINDOW_MS = Math.max(60_000, AGENT_WATCHDOG_TICK_MS * RESPAWN_GUARD_MAX)',
|
||||
);
|
||||
expect(block).toContain('RESPAWN_GUARD_MAX = 3');
|
||||
expect(block).toContain('respawnHistory');
|
||||
expect(block).toContain('agentRespawnGuardTripped');
|
||||
|
|
|
|||
Loading…
Reference in New Issue