Context
Headless runs have no SIGTERM/SIGINT lifecycle handler. A supervising executor can terminate a running model call before the CLI cancels the session or emits a terminal NDJSON event.
A signal does not itself prove a timeout: SIGTERM may come from an executor timeout, deployment, operator, or process supervisor.
Problem / Goal
On the first termination signal, cancel the active session, emit the most accurate terminal lifecycle available, flush output within a bounded grace period, and exit using conventional signal-derived codes.
Proposed Approach
Register signal handling around the active headless run:
- SIGINT →
interrupted, exit code 130;
- SIGTERM →
terminated, exit code 143;
- cancel through the existing session cancellation path;
- allow the event loop to reach idle and emit terminal records;
- await the NDJSON writer flush;
- treat a second signal or expired shutdown grace period as immediate hard termination.
The executor remains responsible for recording whether it sent SIGTERM because of its own timeout.
Acceptance Criteria
Out of Scope
Roadmap Alignment
- Pillar: EXEC
- Quarter: Q3 2026
- Priority: P1
- Theme fit: Pilot feedback: executor reliability.
- Decision gate impact: Indirect support for reliable Code Review and Bug-Fix executions.
References
Upstream OpenCode References
- anomalyco/opencode#24658 — auto-closed after inactivity, not resolved; SIGTERM bypasses the graceful TUI cleanup path and leaves the process hanging.
- anomalyco/opencode#32932 — open; server shutdown leaves sockets and the listening port behind on Windows.
These issues support explicit signal-driven cleanup, but neither defines the headless terminal-event contract or signal-specific exit semantics required here.
Context
Headless runs have no SIGTERM/SIGINT lifecycle handler. A supervising executor can terminate a running model call before the CLI cancels the session or emits a terminal NDJSON event.
A signal does not itself prove a timeout: SIGTERM may come from an executor timeout, deployment, operator, or process supervisor.
Problem / Goal
On the first termination signal, cancel the active session, emit the most accurate terminal lifecycle available, flush output within a bounded grace period, and exit using conventional signal-derived codes.
Proposed Approach
Register signal handling around the active headless run:
interrupted, exit code 130;terminated, exit code 143;The executor remains responsible for recording whether it sent SIGTERM because of its own timeout.
Acceptance Criteria
timeoutwithout an explicit CLI-owned timeout.session_completewhen the grace period permits.Out of Scope
Roadmap Alignment
References
packages/cli/src/session/prompt.tspackages/cli/src/headless.tspackages/cli/src/index.tsUpstream OpenCode References
These issues support explicit signal-driven cleanup, but neither defines the headless terminal-event contract or signal-specific exit semantics required here.