hermes - 💡(How to fix) Fix OSC 11 background probe leaks reply into prompt_toolkit input on macOS Terminal.app [1 pull requests]

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…

Root Cause

_query_osc11_background() in cli.py writes \x1b]11;?\x1b\\ to stdout and then reads the response from stdin using select() + os.read().

On terminals that handle OSC 11 correctly (iTerm2, kitty, WezTerm, ghostty), the terminal intercepts the response before it reaches the application's stdin. On macOS Terminal.app, the response is written directly into the input buffer and is read by prompt_toolkit as keyboard input.

Fix Action

Fixed

Code Example

\x1b]11;?\x1b\\

---

admin-operator ❯ ]11;rgb:213d/2743/33e7

---

_OSC11_SAFE_TERM_PROGRAMS = frozenset({"iTerm.app", "WezTerm", "ghostty", "Hyper"})
_OSC11_SAFE_TERM_PREFIXES = ("xterm-kitty", "foot", "alacritty", "tmux", "screen")

def _osc11_probe_is_safe() -> bool:
    """Return True only for terminals known to handle OSC 11 replies internally."""
    # Manual override
    if os.environ.get("HERMES_DISABLE_OSC11", "").strip() in {"1", "true", "yes"}:
        return False
    term_program = os.environ.get("TERM_PROGRAM", "").strip()
    term = os.environ.get("TERM", "").strip()
    if term_program in _OSC11_SAFE_TERM_PROGRAMS:
        return True
    if any(term.startswith(p) for p in _OSC11_SAFE_TERM_PREFIXES):
        return True
    # Default: unsafe (covers Apple_Terminal and any unrecognised terminal)
    return False

---

# Before:
bg_color = _query_osc11_background()

# After:
bg_color = _query_osc11_background() if _osc11_probe_is_safe() else None
RAW_BUFFERClick to expand / collapse

Bug Description

Hermes CLI sends an OSC 11 terminal background color query on startup to detect light/dark mode:

\x1b]11;?\x1b\\

macOS Terminal.app (TERM_PROGRAM=Apple_Terminal) does not consume this reply internally. Instead, it injects the response string directly into the terminal's input buffer, where prompt_toolkit reads it as user-typed input.

Symptom

The raw escape sequence appears verbatim in the Hermes prompt immediately after startup:

admin-operator ❯ ]11;rgb:213d/2743/33e7

The user sees this as if they typed it. Pressing Enter submits it as a chat message.

Steps to Reproduce

  1. Open macOS Terminal.app (not iTerm2, not kitty)
  2. Run hermes (any profile)
  3. Observe the prompt immediately shows ]11;rgb:RRRR/GGGG/BBBB as pre-filled input

Root Cause

_query_osc11_background() in cli.py writes \x1b]11;?\x1b\\ to stdout and then reads the response from stdin using select() + os.read().

On terminals that handle OSC 11 correctly (iTerm2, kitty, WezTerm, ghostty), the terminal intercepts the response before it reaches the application's stdin. On macOS Terminal.app, the response is written directly into the input buffer and is read by prompt_toolkit as keyboard input.

Affected Terminal

  • macOS Terminal.app (TERM_PROGRAM=Apple_Terminal)
  • Likely affects other terminals that do not intercept OSC escape responses

Terminals NOT Affected (handle OSC 11 safely)

  • iTerm2 (TERM_PROGRAM=iTerm.app)
  • kitty (TERM=xterm-kitty)
  • WezTerm (TERM_PROGRAM=WezTerm)
  • Ghostty (TERM_PROGRAM=ghostty)
  • foot, alacritty, tmux, screen

Proposed Fix

Add a _osc11_probe_is_safe() guard function with an allow-list approach. Only probe on terminals known to handle the reply internally; default to skipping on unknown/unrecognised terminals.

_OSC11_SAFE_TERM_PROGRAMS = frozenset({"iTerm.app", "WezTerm", "ghostty", "Hyper"})
_OSC11_SAFE_TERM_PREFIXES = ("xterm-kitty", "foot", "alacritty", "tmux", "screen")

def _osc11_probe_is_safe() -> bool:
    """Return True only for terminals known to handle OSC 11 replies internally."""
    # Manual override
    if os.environ.get("HERMES_DISABLE_OSC11", "").strip() in {"1", "true", "yes"}:
        return False
    term_program = os.environ.get("TERM_PROGRAM", "").strip()
    term = os.environ.get("TERM", "").strip()
    if term_program in _OSC11_SAFE_TERM_PROGRAMS:
        return True
    if any(term.startswith(p) for p in _OSC11_SAFE_TERM_PREFIXES):
        return True
    # Default: unsafe (covers Apple_Terminal and any unrecognised terminal)
    return False

Callsite change in _detect_light_mode():

# Before:
bg_color = _query_osc11_background()

# After:
bg_color = _query_osc11_background() if _osc11_probe_is_safe() else None

Env Override

Support HERMES_DISABLE_OSC11=1 so users on any terminal can manually disable the probe without code changes.

Design Notes

  • Conservative default: unknown terminals are treated as unsafe (skip probe). This prevents prompt pollution on any terminal not in the allow-list.
  • Safe terminals unchanged: iTerm2, kitty, WezTerm, ghostty etc. retain full OSC 11 background detection for light/dark theme auto-detection.
  • _query_osc11_background() is correct: the function implementation is fine; the issue is only the callsite not being gated.
  • No regressions: prompt_toolkit startup and TUI rendering are unaffected on safe terminals.

Environment

  • OS: macOS (any version)
  • Terminal: Terminal.app (TERM_PROGRAM=Apple_Terminal)
  • Hermes version: 0.14.0
  • Python: 3.12

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

hermes - 💡(How to fix) Fix OSC 11 background probe leaks reply into prompt_toolkit input on macOS Terminal.app [1 pull requests]