- Replaced time-based sleeps and polling loops with event-driven promise resolvers and fake timers across agent and tool tests.
- Migrated test suites to share in-memory auth storage and fixtures using lifecycle hooks.
- Updated catalog model definitions, metadata, and configurations.
brush-core's Process::wait calls tokio::signal::unix::signal(SIGTSTP) to
detect when its children get stopped. Per tokio's documented contract,
the first call for a SignalKind permanently replaces the kernel-default
handler for the lifetime of the process. So once omp has executed any
bash tool call — even /usr/bin/true — SIGTSTP's default "stop" action
is gone, and InputController.handleCtrlZ's process.kill(0, "SIGTSTP")
gets swallowed by tokio. The TUI tore down via ui.stop() but the process
kept running in Sl+ state, leaving the user with a dead terminal that
only kill -9 could recover.
Send SIGSTOP to our own PID instead. SIGSTOP can't be caught, blocked,
or ignored — it stops the process at the kernel regardless of installed
handlers. Targeting self (not pgid=0) also leaves long-lived children
(MCP stdio servers, the persistent brush native shell) running across
the suspend, so they no longer freeze mid-IPC during a quick fg/bg
detour.
Fixes#3461
handleCtrlZ called process.kill(0, "SIGTSTP") unconditionally; on
Windows the runtime rejects the signal name with TypeError, which
propagated out of the TUI input dispatcher and crashed the agent
with an [Uncaught Exception]. Even on POSIX the call could fail
(sandboxes that block sending to pid=0, runtime-reserved signals),
and a thrown error after we already stopped the TUI and registered
a one-shot SIGCONT listener left the UI stranded with a leaked
handler that would fire on the next unrelated continue and try to
re-start() an already-running TUI.
Branch on process.platform first so Windows shows a status notice
and never calls process.kill, and wrap the POSIX kill in try/catch
that removes the SIGCONT listener and re-starts the TUI on failure.
Fixes#2036