Description
The TUI entrypoint (tui/thread.ts) only runs cleanup through onExit, which
calls client.call("shutdown"). Signal paths (SIGINT, SIGTERM, SIGHUP)
have no handlers, so worker cleanup is skipped and child resources can survive.
Same lifecycle class as #14091, different command path.
Code evidence
packages/opencode/src/cli/cmd/tui/thread.ts:
SIGUSR2 handler exists (reload only).
- No handlers for
SIGINT, SIGTERM, or SIGHUP.
- Cleanup only here:
onExit: async () => {
await client.call("shutdown", undefined)
}
worker.ts already has proper cleanup in rpc.shutdown() (event stream abort,
Instance.disposeAll() with timeout, server.stop(true)), but thread.ts does
not guarantee it runs on signal exit.
Steps to reproduce
# 1) Start TUI under a terminal host
script -q -c "opencode /tmp/repro-project" /tmp/tui.log &
# 2) Send SIGHUP to TUI process
kill -HUP "$(pgrep -f 'opencode /tmp/repro-project')"
sleep 2
# 3) Check for surviving children
pgrep -fa 'fake-mcp' # child MCP process still alive
(Full harness with a deterministic fake MCP server available on request.)
Expected behavior
TUI should run worker shutdown on signal/terminal-close paths so child resources
are disposed before process exit.
Actual behavior
Child resources can survive after signal exit because shutdown is not invoked.
OpenCode version
dev branch at 3a416f6f3
Operating System
Linux (signal semantics apply cross-platform; Windows uses different exit paths
already handled by win32 guards in the same file)
Description
The TUI entrypoint (
tui/thread.ts) only runs cleanup throughonExit, whichcalls
client.call("shutdown"). Signal paths (SIGINT,SIGTERM,SIGHUP)have no handlers, so worker cleanup is skipped and child resources can survive.
Same lifecycle class as #14091, different command path.
Code evidence
packages/opencode/src/cli/cmd/tui/thread.ts:SIGUSR2handler exists (reload only).SIGINT,SIGTERM, orSIGHUP.worker.tsalready has proper cleanup inrpc.shutdown()(event stream abort,Instance.disposeAll()with timeout,server.stop(true)), butthread.tsdoesnot guarantee it runs on signal exit.
Steps to reproduce
(Full harness with a deterministic fake MCP server available on request.)
Expected behavior
TUI should run worker shutdown on signal/terminal-close paths so child resources
are disposed before process exit.
Actual behavior
Child resources can survive after signal exit because shutdown is not invoked.
OpenCode version
devbranch at3a416f6f3Operating System
Linux (signal semantics apply cross-platform; Windows uses different exit paths
already handled by
win32guards in the same file)