-
Notifications
You must be signed in to change notification settings - Fork 11.8k
Description
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:
SIGUSR2handler exists (reload only).- No handlers for
SIGINT,SIGTERM, orSIGHUP. - 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)