fix(cli): strip OSC 8 hyperlink sequences in ChatConsole output

prompt_toolkit's ANSI parser does not handle OSC escape sequences
(\x1b]...\x07 / \x1b]...\x1b\), which caused Rich's [link=...] markup
to leak raw OSC 8 payload into the banner title after /clear.

Added _OSC_ESCAPE_RE to strip OSC sequences in ChatConsole.print()
before routing through _cprint(). CSI/SGR color sequences are
preserved. Visible text between OSC sequences is kept intact.
This commit is contained in:
Robert Ban 2026-05-25 22:55:36 +02:00 committed by Teknium
parent 8e4c447e5f
commit 4129092fda

10
cli.py
View file

@ -2801,6 +2801,12 @@ def _collect_query_images(query: str | None, image_arg: str | None = None) -> tu
return message, deduped
# Strip OSC escape sequences (e.g. OSC-8 hyperlinks) that prompt_toolkit's
# ANSI parser can't handle — it strips \x1b but passes the payload through
# as literal text, garbling the TUI output.
_OSC_ESCAPE_RE = re.compile(r"\x1b\][\s\S]*?(?:\x07|\x1b\\)")
class ChatConsole:
"""Rich Console adapter for prompt_toolkit's patch_stdout context.
@ -2827,6 +2833,10 @@ class ChatConsole:
self._inner.width = shutil.get_terminal_size((80, 24)).columns
self._inner.print(*args, **kwargs)
output = self._buffer.getvalue()
# Strip OSC escape sequences (e.g. OSC-8 hyperlinks) before
# routing through prompt_toolkit's ANSI parser, which only
# handles CSI/SGR and passes OSC payload through as literal text.
output = _OSC_ESCAPE_RE.sub("", output)
for line in output.rstrip("\n").split("\n"):
_cprint(line)