Windows: terminal-agent watchdog respawn-loops every 60s — console flash + unbounded bun.exe leak; circuit-breaker can never trip
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
Description
## Summary
On Windows, the browse daemon's terminal-agent watchdog gets stuck in a permanent respawn loop: it spawns a fresh `bun run terminal-agent.ts` every 60s (`AGENT_WATCHDOG_TICK_MS`) even though the previous agent is alive and its pid record is fresh. Each spawn flashes a visible `bun.exe` console window (polyfill drops `windowsHide` — fix in PR #2294), old agents are never reaped, and the respawn circuit-breaker can mathematically never trip.
## Environment
- Windows 11 Pro, build 26200 (desktop)
- gstack @ `a3259400` (update-check reports `UP_TO_DATE 1.60.1.0`)
- Browse daemon running as `node.exe dist/server-node.mjs` with `bun-polyfill.cjs` (the documented Windows fallback)
## Measured behavior
**Respawn cadence** — process-creation watcher, one daemon (pid 35744), exact 60s beat:
```
13:19:43.206 START bun run ...\terminal-agent.ts (parent 35744)
13:20:43.368 START bun run ...\terminal-agent.ts (parent 35744)
13:21:43.500 START bun run ...\terminal-agent.ts (parent 35744)
```
A second daemon (pid 47248, different project state dir) interleaved its own 60s loop → a console flash every ~30s wall-clock.
**Leak** — 7 spawns vs 1 exit in a 6-minute capture; process count 3 → 9 in ~10 minutes, ~90 MB working set. One daemon had been orphaned (its parent process dead) and looping like this for **2 days**. The daemon-side kill path (`spawnTerminalAgent` kills the pid in the record before spawning) doesn't cover them: 9 agents alive with only 2 pid records in existence.
**The respawn happens despite a healthy agent.** At the time of the respawns, the `terminal-agent-pid` records existed, were freshly written (seconds-old), and pointed at running pids.
**`isProcessAlive` is exonerated.** I ran the exact implementation under `node --require bun-polyfill.cjs` (the daemon's real runtime): it returned `true` for a live agent pid and `false` for a dead one, with clean `tasklist` CSV output. So the mis-detection is somewhere else — possibly a stateDir/projectDir mismatch between where the daemon looks and where the agent writes (I did not isolate it; occasionally a tick is skipped, so the check does sometimes pass).
**The circuit-breaker is dead code.** `RESPAWN_GUARD_MAX = 3` within `RESPAWN_GUARD_WINDOW_MS = 60_000`, but the watchdog tick is also 60s — entries older than the window are pruned before counting, so `respawnHistory` can hold at most ~1 entry at each tick and the guard can never reach 3. The "manual restart required" log line is unreachable at default settings.
## Suggested directions
1. Merge PR #2294 (`windowsHide` in the polyfill) — stops the visible flashing; no-op off Windows.
2. Find why the watchdog considers a live, freshly-recorded agent dead (stateDir resolution seems the prime suspect).
3. Make the guard window meaningfully larger than the tick (e.g. 10 min window / tick 60s), so a genuine crash-loop actually trips it.
4. Reap: kill the previous agent by pid **before** spawning even when the record lookup misses, or track children directly — observed agents do self-exit when their daemon dies, but not when superseded.
5. Consider a daemon-side "am I orphaned?" check — the 2-day looper's parent was long dead.
Happy to provide the raw watcher logs or test a branch on this machine.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Contributor guide
Research direction
Start with the terminal-agent.ts watchdog entry point, spawnTerminalAgent, AGENT_WATCHDOG_TICK_MS, and the daemon runtime using bun-polyfill.cjs. Reproduce the 60-second Windows respawn loop while checking terminal-agent-pid records and process ownership. Done means the watchdog recognizes a healthy recorded agent, prevents unbounded duplicate agents, and allows the respawn circuit-breaker to trip during a genuine crash loop.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, node.js, typescript
- Domain
- devtools, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 32/100