feat: SIGUSR1 handler to trigger immediate channel check in headless sessions

Resolved 💬 4 comments Opened Mar 20, 2026 by langdon Closed Apr 18, 2026

Summary

Add a SIGUSR1 handler that triggers an immediate check of configured channels without waiting for the next polling interval. This is not an alternative to --channels — it's the missing headless trigger that makes --channels useful outside interactive TTY sessions.

Motivation

We built file-based inbox polling as a workaround for this — an external process writes a message to a watched file, the session polls on a timer and processes it. It works. But it requires the agent to burn turns and context polling even when there's nothing to process, and it's entirely inert in headless mode.

We weren't alone: the community converged on the same approach independently (see the workarounds documented in #24947). The next step we were planning was using inotify + SIGUSR1 as a trigger layer — external process writes to the inbox file, inotify detects the change, sends SIGUSR1 to Claude, Claude checks immediately instead of waiting for the next poll interval.

Then --channels shipped in v2.1.80, and it's the right architecture — MCP server-initiated push is better than anything file-based. But --channels still needs a headless trigger. The channel polling mechanism is currently implemented as a React/Ink hook that only fires when the TUI is rendering, making it completely inactive in non-TTY sessions (--print, --sdk-url, piped input). See #26426.

SIGUSR1 is that missing trigger. An orchestrator can signal an immediate channel check with:

kill -USR1 <claude-pid>

Zero polling overhead. The session is idle until signaled, checks once, returns to idle.

Proposed behavior

On receiving SIGUSR1:

  1. Immediately trigger the channel polling mechanism (the same path --channels uses for scheduled delivery)
  2. If a message is available, deliver it into the session as a <channel> event (consistent with existing --channels behavior)
  3. If no message is available, no-op silently
  4. If the session is mid-turn, queue the check for after the current turn completes

Implementation sketch

if (process.platform !== 'win32') {
  process.on('SIGUSR1', () => {
    // Call the same channel poll/deliver path used by --channels
    // Queue if mid-turn, execute immediately if idle
  });
}

Relationship to --channels

This is not a replacement for --channels — it's a trigger mechanism for it. Sessions with --channels configured get immediate delivery instead of waiting for the next poll interval. Sessions without --channels no-op.

The --channels feature validates the concept of external message delivery. SIGUSR1 extends it to the headless case where the existing polling infrastructure doesn't fire.

Windows

POSIX signals don't exist on Windows. Register conditionally (process.platform !== 'win32'). A Windows alternative (named pipe or stdin-based trigger) could be a follow-up.

Related

  • #26426 — channel polling is a React/Ink hook, never fires in headless/non-interactive mode
  • #24947 — claude inject canonical feature request; community workarounds documented there
  • #27545 — delivery semantics RFC (delivery modes, idempotency) — relevant if this goes further
  • --channels (v2.1.80) — the MCP notification infrastructure this would leverage

---

Drafted with AI assistance. AI-C — human-directed, collaboratively written.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗