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.
This commit is contained in:
GitHub Copilot
2026-01-29 20:29:14 +00:00
parent af2f7f5084
commit dad457978a
3 changed files with 19 additions and 12 deletions
+8 -5
View File
@@ -29,14 +29,17 @@ DEFAULT_SCREEN_WIDTH = 132
DEFAULT_SCREEN_HEIGHT = 45 DEFAULT_SCREEN_HEIGHT = 45
# Pattern to filter out terminal device attribute responses that cause display issues # Pattern to filter out terminal device attribute responses that cause display issues
# These are responses to queries that shouldn't be displayed as text. # These are responses to DA1/DA2/DA3 queries that shouldn't be displayed as text
# Matches complete DA1/DA2 responses like \x1b[?1;10;0c or \x1b[?64;1;2;...c # Matches complete responses like:
DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[\?[\d;]+c") # \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) # 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 # 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) @dataclass(frozen=True)
+3 -2
View File
@@ -27,10 +27,11 @@ from .session_manager import SessionManager
from .svg_exporter import render_terminal_svg from .svg_exporter import render_terminal_svg
from .types import Meta, RouteKey, SessionID 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. # 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. # 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: if TYPE_CHECKING:
from .config import Config from .config import Config
+8 -5
View File
@@ -33,13 +33,16 @@ DEFAULT_SCREEN_WIDTH = 132
DEFAULT_SCREEN_HEIGHT = 45 DEFAULT_SCREEN_HEIGHT = 45
# Pattern to filter out terminal device attribute responses that cause display issues # 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 # These are responses to DA1/DA2/DA3 queries that shouldn't be displayed as text
# Matches complete responses like \x1b[?1;10;0c or \x1b[?64;1;2;...c # Matches complete responses like:
DA_RESPONSE_PATTERN = re.compile(rb"\x1b\[\?[\d;]+c") # \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) # Pattern to detect partial DA responses at end of data (incomplete escape sequence)
# Matches: \x1b, \x1b[, \x1b[?, \x1b[?1, \x1b[?1;, etc. # Matches: \x1b, \x1b[, \x1b[?, \x1b[>, \x1b[=, \x1b[?1, \x1b[>1;10, etc.
DA_PARTIAL_PATTERN = re.compile(rb"\x1b(?:\[(?:\?[\d;]*)?)?$") DA_PARTIAL_PATTERN = re.compile(rb"\x1b(?:\[(?:[?>=][\d;]*)?)?$")
class TerminalSession(Session): class TerminalSession(Session):