From dad457978ad185b6139e0a11bf5faf8d93613387 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Thu, 29 Jan 2026 20:29:14 +0000 Subject: [PATCH] fix: filter DA2/DA3 terminal responses to prevent '1;10;0c' display in tmux When entering or refreshing a webterm session running tmux, the text '1;10;0c' was appearing on screen. This was caused by tmux sending a DA2 (Secondary Device Attributes) query (ESC[>c) to detect terminal capabilities. The filtering code only handled DA1 responses (ESC[?...c) but not DA2 (ESC[>...c) or DA3 (ESC[=...c) responses. Updated the regex patterns to match all three variants: - DA1 (Primary): ESC[?...c - already worked - DA2 (Secondary): ESC[>...c - now fixed (tmux uses this) - DA3 (Tertiary): ESC[=...c - added for completeness The fix applies to: - Live terminal output filtering - Replay buffer filtering on reconnect - Partial escape sequence buffering All 342 tests pass with the updated patterns. --- src/webterm/docker_exec_session.py | 13 ++++++++----- src/webterm/local_server.py | 5 +++-- src/webterm/terminal_session.py | 13 ++++++++----- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/webterm/docker_exec_session.py b/src/webterm/docker_exec_session.py index 26fdfcb..19549b6 100644 --- a/src/webterm/docker_exec_session.py +++ b/src/webterm/docker_exec_session.py @@ -29,14 +29,17 @@ DEFAULT_SCREEN_WIDTH = 132 DEFAULT_SCREEN_HEIGHT = 45 # Pattern to filter out terminal device attribute responses that cause display issues -# These are responses to queries that shouldn't be displayed as text. -# Matches complete DA1/DA2 responses like \x1b[?1;10;0c or \x1b[?64;1;2;...c -DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[\?[\d;]+c") +# These are responses to DA1/DA2/DA3 queries that shouldn't be displayed as text +# Matches complete responses like: +# \x1b[?1;10;0c (DA1 - Primary Device Attributes) +# \x1b[>1;10;0c (DA2 - Secondary Device Attributes, sent by tmux) +# \x1b[=1;0c (DA3 - Tertiary Device Attributes) +DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[[?>=][\d;]*c") # Pattern to detect partial DA responses at end of data (incomplete escape sequence) -# Matches: \x1b, \x1b[, \x1b[?, \x1b[?1, \x1b[?1;, \x1b[?1;10, etc. +# Matches: \x1b, \x1b[, \x1b[?, \x1b[>, \x1b[=, \x1b[?1, \x1b[>1;10, etc. # These need to be held back until more data arrives to see if they complete -DA_PARTIAL_PATTERN = re.compile(rb"\x1b(?:\[(?:\?[\d;]*)?)?$") +DA_PARTIAL_PATTERN = re.compile(rb"\x1b(?:\[(?:[?>=][\d;]*)?)?$") @dataclass(frozen=True) diff --git a/src/webterm/local_server.py b/src/webterm/local_server.py index 6605019..57112c3 100644 --- a/src/webterm/local_server.py +++ b/src/webterm/local_server.py @@ -27,10 +27,11 @@ from .session_manager import SessionManager from .svg_exporter import render_terminal_svg from .types import Meta, RouteKey, SessionID -# Pattern to filter terminal device attribute responses (DA1/DA2) from replay buffer. +# Pattern to filter terminal device attribute responses (DA1/DA2/DA3) from replay buffer. # These responses can appear as visible text like "1;10;0c" if split across reads. +# Matches: \x1b[?...c (DA1), \x1b[>...c (DA2 from tmux), \x1b[=...c (DA3) # See docker_exec_session.py and terminal_session.py for main filtering. -DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[\?[\d;]+c") +DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[[?>=][\d;]*c") if TYPE_CHECKING: from .config import Config diff --git a/src/webterm/terminal_session.py b/src/webterm/terminal_session.py index 0f51f76..2429b76 100644 --- a/src/webterm/terminal_session.py +++ b/src/webterm/terminal_session.py @@ -33,13 +33,16 @@ DEFAULT_SCREEN_WIDTH = 132 DEFAULT_SCREEN_HEIGHT = 45 # Pattern to filter out terminal device attribute responses that cause display issues -# These are responses to DA1/DA2 queries that shouldn't be displayed as text -# Matches complete responses like \x1b[?1;10;0c or \x1b[?64;1;2;...c -DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[\?[\d;]+c") +# These are responses to DA1/DA2/DA3 queries that shouldn't be displayed as text +# Matches complete responses like: +# \x1b[?1;10;0c (DA1 - Primary Device Attributes) +# \x1b[>1;10;0c (DA2 - Secondary Device Attributes, sent by tmux) +# \x1b[=1;0c (DA3 - Tertiary Device Attributes) +DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[[?>=][\d;]*c") # Pattern to detect partial DA responses at end of data (incomplete escape sequence) -# Matches: \x1b, \x1b[, \x1b[?, \x1b[?1, \x1b[?1;, etc. -DA_PARTIAL_PATTERN = re.compile(rb"\x1b(?:\[(?:\?[\d;]*)?)?$") +# Matches: \x1b, \x1b[, \x1b[?, \x1b[>, \x1b[=, \x1b[?1, \x1b[>1;10, etc. +DA_PARTIAL_PATTERN = re.compile(rb"\x1b(?:\[(?:[?>=][\d;]*)?)?$") class TerminalSession(Session):