Feature request: AssistantMessageDelta hook for streaming-aware tooling (TTS, telemetry, mirror)

Resolved 💬 1 comment Opened May 19, 2026 by eli-newman Closed Jun 19, 2026

Motivation

The current hook surface fires only at boundary events: UserPromptSubmit, PreToolUse, PostToolUse, Stop, etc. There is no way to react to assistant text while it is being generated.

For an entire class of downstream tooling — local TTS, transcript mirroring, on-the-fly translation, accessibility narrators, telemetry — that boundary-only model means the user has to wait for the full response to finish before any side-effect can begin. On a 500-word response that is a 15–30s wait before any narration starts.

I just shipped a local Kokoro-TTS daemon (Null-Phnix/claude-voice#3) that drops warm time-to-first-audio from ~6s to ~0.6s. The bottleneck is no longer the model; it is that the Stop hook only fires after the assistant turn completes.

I verified by polling the JSONL transcript file every 100ms during an assistant response: the file grew exactly once, with the entire 7.2KB assistant message written in a single flush at end-of-turn. So tailing the transcript can't substitute either.

Proposed hook

A new hook event fired periodically during assistant generation with the in-progress text:

{
  "event": "AssistantMessageDelta",
  "session_id": "...",
  "message_id": "...",
  "delta": {
    "text": "...the text added since the last delta..."
  },
  "cumulative_text": "...full assistant text so far..."  // optional but very useful
}

Configuration would be the same as other hooks in ~/.claude/settings.json:

"hooks": {
  "AssistantMessageDelta": [
    { "matcher": "", "hooks": [{ "type": "command", "command": "...", "async": true, "timeout": 5 }] }
  ]
}

Implementation notes / requests

A few things would make this hook actually usable for streaming consumers:

  1. async: true should be the only sensible mode. A sync hook on every delta would tank generation throughput. Document it clearly.
  2. Debounce/batch. Firing per-token is overkill. Firing every ~100ms or on every sentence-terminator (.!? followed by whitespace) would cover almost every consumer's needs without flooding hook processes.
  3. Either delta or cumulative_text, not both required. Consumers can derive one from the other. Sending only delta is fine if you flag the order; sending cumulative_text is fine if you accept the redundancy.
  4. Final delta marker. A flag like "is_final": true on the last delta of a turn (or a separate AssistantMessageEnd event) helps consumers flush their last sentence cleanly.

Use cases this unlocks

  • Real-time TTS — start speaking sentence 1 while Claude is still generating sentence 2. The reason I filed this.
  • Accessibility — screen readers that narrate as the assistant types, the way visually-impaired users already use screen readers with chat apps.
  • Live transcript mirror — push the response to a second device / dashboard as it's being typed, instead of post-hoc.
  • Per-sentence translation — pipe deltas to a translator and emit in the user's language as fast as the assistant emits in English.
  • Content moderation / safety filters — abort or rewrite a response mid-stream when an external rule fires, instead of after the user has already seen it.
  • Token-level telemetry for tools that need finer-grained data than Stop provides.

Workarounds considered (and why they don't suffice)

  • Tail the JSONL transcript — file is flushed once at end-of-turn (verified empirically). No streaming visibility.
  • Wrap Claude Code's stdout via PTY — fragile, breaks every time the TUI redraws or cursor-moves, and forces users to launch via a wrapper.
  • PostToolUse — fires only on tool use, not on plain-text assistant turns.
  • Polling the API directly via a custom client — duplicates Claude Code's session, auth, MCP plumbing.

A first-class hook avoids all of those failure modes.

Happy to help

If a delta hook is on the roadmap, I'd be happy to:

  • Test against any preview build and give feedback before stable.
  • Update Null-Phnix/claude-voice as a reference consumer the moment it ships.
  • Write user-facing docs once the contract is settled.

Thanks for building Claude Code.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗