claude-code - 💡(How to fix) Fix [FEATURE] External wake signal for interactive sessions

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

The ask is narrow: let an external process trigger a new turn in a running interactive session, via a secure, injection-safe channel that carries no payload. The session's existing configuration (hooks, CLAUDE.md, permission model) determines what happens in that turn — the signal just says "start." This unlocks multi-agent coordination, cron integration, build notifications, and file-watcher workflows — all without the pty corruption that plagues tmux send-keys.

Root Cause

The ask is narrow: let an external process trigger a new turn in a running interactive session, via a secure, injection-safe channel that carries no payload. The session's existing configuration (hooks, CLAUDE.md, permission model) determines what happens in that turn — the signal just says "start." This unlocks multi-agent coordination, cron integration, build notifications, and file-watcher workflows — all without the pty corruption that plagues tmux send-keys.

Fix Action

Fix / Workaround

Why this matters: Users running multiple Claude Code sessions (e.g., in tmux panes for multi-agent workflows) need sessions to notify each other. The current workaround — tmux send-keys -t <pane> "message" Enter — injects characters through the terminal's pty, which corrupts input if the user is typing in that pane at the same time. There is no alternative. We verified this exhaustively:

Prior art

  • tmux send-keys: Current workaround. Works but corrupts user input (the problem this feature solves).
  • VS Code extension API: VS Code extensions can programmatically send text to the terminal, but this has the same pty corruption issue.
  • MCP servers: Pull-only from Claude's perspective. No push mechanism from MCP server to Claude.
  • ScheduleWakeup tool: Exists in the tool list but unclear if it's functional or how it relates to external waking.

Code Example

claude notify --session-id <uuid> --type wake

---

{"token": "<base64>", "type": "wake"}
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing requests and this feature hasn't been requested yet
  • This is a single feature request (not multiple features)

Problem Statement

There is no way for an external process to trigger a new turn in a running Claude Code interactive session without typing into the terminal via tmux send-keys or similar pty injection

Why this matters: Users running multiple Claude Code sessions (e.g., in tmux panes for multi-agent workflows) need sessions to notify each other. The current workaround — tmux send-keys -t <pane> "message" Enter — injects characters through the terminal's pty, which corrupts input if the user is typing in that pane at the same time. There is no alternative. We verified this exhaustively:

  • All tmux methods (send-keys, paste-buffer, pipe-pane) go through the pty
  • TIOCSTI is disabled on modern kernels (6.2+)
  • --resume writes to the JSONL transcript but a running session keeps conversation state in memory and doesn't re-read it
  • --remote-control is for claude.ai/code and mobile app bridging, not local IPC
  • Agent Teams in-process mode bypasses the pty but requires the teams framework hierarchy — it can't be used between independently launched sessions
  • --input-format stream-json only works with --print (non-interactive)
  • There is no Unix socket, named pipe, signal handler, or any other IPC mechanism exposed by a running interactive session

Proposed Solution

Proposed solution

Add an external notification channel that lets a process wake a running session — triggering a new turn without injecting text through the terminal.

CLI surface

claude notify --session-id <uuid> --type wake

The session starts a new turn as if the user pressed Enter on an empty prompt. No text is injected. The session's existing hooks and context sources determine what Claude sees and does.

Use cases

  • Multi-agent coordination: Agent A finishes a task and writes results to a shared file. It then wakes Agent B so B picks up the results on its next turn (via existing file-reading hooks).
  • Build/CI notifications: A build completion hook wakes the session that submitted the build.
  • Cron-triggered check-ins: A cron job wakes a session periodically to check for pending work.
  • File watcher integration: inotifywait detects a file change and wakes the session that cares about it.

What the signal is NOT

The signal carries no payload, no message text, no instructions. It is a structured wake event, not a prompt. Content comes from whatever sources the session already reads (files, hooks, CLAUDE.md, etc.). This is the key design constraint — the wake channel must not become a prompt injection vector.

Security considerations

We ran a red team / blue team analysis on this proposal. The core tension: "trigger autonomous action" is exactly what an attacker wants. The signal itself is safe (no payload), but it can trigger turns that read from attacker-controlled files. Here's how to mitigate:

The critical attack chain to prevent

  1. Attacker modifies a file that hooks read (e.g., a shared config, a project file)
  2. Attacker sends a wake signal
  3. Session fires a turn, hooks read the modified file, Claude acts on attacker's instructions

Proposed security model

Authentication (3 layers):

LayerMechanismWhat it prevents
L1: FilesystemSocket at ~/.claude/sessions/<id>/notify.sock, permissions 0600, parent dir 0700Other users on shared machines
L2: Kernel UIDSO_PEERCRED / LOCAL_PEERCRED verifies sender UID matches session ownerPrivilege escalation, spoofing
L3: Capability token256-bit random token generated at session start, stored at ~/.claude/sessions/<id>/notify.token (0400). Required in every signal.Blind probing by same-UID processes that don't know the session exists
Signal schema (strict allowlist):
{"token": "<base64>", "type": "wake"}
  • Only allowlisted types (wake, optionally file_changed with a validated path)
  • No free-form text fields — no message, body, prompt, instructions, context
  • Unknown fields rejected (not ignored)
  • Max message size: 4KB
  • All values must be strings (no nested objects/arrays)

Turn isolation (defense in depth): Signal-triggered turns should be treated as a distinct event type, not as a UserPromptSubmit. This has several implications:

PropertyUser-initiated turnSignal-triggered turn
Fires UserPromptSubmit hooksYesConfigurable (new SignalReceived hook type)
Tool executionPer session permissionsConfigurable — could be restricted to read-only by default
--dangerously-skip-permissionsAppliesShould NOT apply — signal turns should always respect permissions
Visible in TUIYesYes (user should see when external turns fire)
The key decision is how much autonomy signal-triggered turns get. A phased approach:
  • Phase 1 (safest): Signal turns are passive — Claude acknowledges the wake, reads context, summarizes what changed, but takes no tool actions. The user decides what to do next.
  • Phase 2: Signal turns can execute tools within the session's normal permission model (user gets permission prompts as usual). This is equivalent to the user pressing Enter — same trust level, just triggered externally.
  • Phase 3: Signal turns with pre-authorized tool allowlists for specific signal types (opt-in, not default). Phase 2 is likely the right default for most users — it's equivalent to what tmux send-keys "" Enter does today, just without the pty corruption. The user has already configured their session's permission model; signal turns should respect it the same way user turns do.

Rate limiting and budget:

LimitValueRationale
Signals per minute30Prevents flooding
Signals per hour200Bounds sustained abuse
Deduplication window10 secondsCollapses identical rapid-fire signals
Turn coalescingBatch pending signals into one turnPrevents N signals = N API calls
Per-session signal budgetConfigurable (default: $1.00)Caps API cost from signal-triggered turns
Audit:
All signals (accepted and rejected) logged to ~/.claude/sessions/<id>/signals.log with sender UID, PID, timestamp, signal type, and auth result. No token values logged.

Interaction with existing features

  • Sandboxing: Signal turns inherit the session's sandbox. file_changed paths validated against sandbox boundaries.
  • Session persistence: Signal-triggered turns are persisted in the session transcript like any other turn.
  • Global kill switch: ~/.claude/disable-signals file disables signal processing for all sessions (emergency stop).

Prior art

  • tmux send-keys: Current workaround. Works but corrupts user input (the problem this feature solves).
  • VS Code extension API: VS Code extensions can programmatically send text to the terminal, but this has the same pty corruption issue.
  • MCP servers: Pull-only from Claude's perspective. No push mechanism from MCP server to Claude.
  • ScheduleWakeup tool: Exists in the tool list but unclear if it's functional or how it relates to external waking.

Alternatives considered

AlternativeWhy insufficient
Idle-gated tmux send-keys (check pane state before injecting)Fragile screen-scraping heuristic, race condition window
Headless sessions (--print mode with FIFO stdin)Loses interactive TUI, can't monitor sessions visually
Self-prompting loop (/loop skill)Burns API calls when idle, unclear interaction with user typing
Custom pty proxy (multiplex terminal + FIFO)~100 lines of C, changes session launch, fragile with TUI rendering
MCP "mailbox" serverPull-only — Claude decides when to check, no push notification
File watcher + hookHooks only fire on existing events, can't be externally triggered

Summary

The ask is narrow: let an external process trigger a new turn in a running interactive session, via a secure, injection-safe channel that carries no payload. The session's existing configuration (hooks, CLAUDE.md, permission model) determines what happens in that turn — the signal just says "start." This unlocks multi-agent coordination, cron integration, build notifications, and file-watcher workflows — all without the pty corruption that plagues tmux send-keys.

Alternative Solutions

While waiting on the feature request, the realistic interim options are:

  1. Keep tmux send-keys as-is — accept the occasional typing corruption. It works, it's just annoying.
  2. Drop push-wake entirely — pings land in the blackboard file; agents see them on their next natural turn (when you or the agent submits a prompt). The per-turn hook already surfaces everything. Latency goes up, interference goes to zero.
  3. Headless loops for autonomous agents — agents that don't need interactive typing run as inotifywait + claude -p --resume loops. You monitor via tail -f on the transcript or a tmux pane running the loop script. Interactive sessions stay as TUI.
  4. Self-prompting via /loop — each agent runs /loop 5m "check inbox" at session start. Burns ~$0.03/check but ensures agents poll without human intervention. Risk: unclear behavior if you're mid-type when the loop fires.

None of these are great — that's exactly why the feature request exists. Option 2 (drop push-wake, rely on per-turn hooks) is the safest and simplest if you can tolerate the latency.

Priority

Critical - Blocking my work

Feature Category

CLI commands and flags

Use Case Example

  • Multi-agent coordination: Agent A finishes a task and writes results to a shared file. It then wakes Agent B so B picks up the results on its next turn (via existing file-reading hooks).
  • Build/CI notifications: A build completion hook wakes the session that submitted the build.
  • Cron-triggered check-ins: A cron job wakes a session periodically to check for pending work.
  • File watcher integration: inotifywait detects a file change and wakes the session that cares about it.

Additional Context

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING