fix: decode TUI input before routing
Some checks failed
tamq-ci / test (push) Failing after 7s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a03397-4d51-7fd1-8ff2-946eb22ea2bc
This commit is contained in:
tegwick 2026-08-25 00:46:25 +02:00
parent 2009d01ae4
commit 452235a01f
6 changed files with 133 additions and 8 deletions

View file

@ -85,6 +85,9 @@ The tap observes this complete line and queues the same durable route as
`#repo:` is therefore an agent/tap convention, not a shell command—at an
ordinary shell it remains a comment. Existing windows are never respawned, so
recreate a session that was originally started without pushy observation.
Terminal protocol replies and local backspace editing are removed only from
tamq's observation copy, so strict routing remains reliable in TUIs such as
Codex while the wrapped program still receives the original byte stream.
The submitted target line is sanitized, sender-labelled, deliberately
non-routable, and then followed by exactly one Enter key:

View file

@ -98,7 +98,7 @@ Not yet suitable:
and stronger process-supervision evidence.
- Cross-host messaging or use as a general-purpose broker.
The suite currently has 120 passing tests and 77% statement coverage. Coverage
The suite currently has 123 passing tests and 77% statement coverage. Coverage
is strongest in durable storage and registry handling, and weakest in the PTY
tap and CLI orchestration; PTY statement coverage increased from 23% to 33%,
while subprocess behavior is primarily proven by the real-tmux test. The

View file

@ -9,6 +9,7 @@
| Kind | ID | Status | Lane | Source |
| --- | --- | --- | --- | --- |
| workplan | TAMQ-WP-ADHOC-2026-08-24 | finished | — | workplans/ADHOC-2026-08-24.md |
| workplan | TAMQ-WP-ADHOC-2026-08-25 | active | — | workplans/ADHOC-2026-08-25.md |
| workplan | TAMQ-WP-0001 | finished | — | workplans/TAMQ-WP-0001-statehub-bootstrap.md |
| workplan | TAMQ-WP-0002 | active | — | workplans/TAMQ-WP-0002-coordination-engine-adapter.md |
| workplan | TAMQ-WP-0003 | active | — | workplans/TAMQ-WP-0003-delivery-reliability.md |
@ -21,6 +22,7 @@
| workplan | TAMQ-WP-0010 | finished | — | workplans/TAMQ-WP-0010-experimental-pushy-delivery.md |
| workplan | TAMQ-WP-0011 | finished | — | workplans/TAMQ-WP-0011-hash-routing-for-pushy-agents.md |
| task | TAMQ-WP-ADHOC-2026-08-24-T01 | done | — | workplans/ADHOC-2026-08-24.md |
| task | TAMQ-WP-ADHOC-2026-08-25-T01 | progress | — | workplans/ADHOC-2026-08-25.md |
| task | TAMQ-WP-0001-T01 | done | — | workplans/TAMQ-WP-0001-statehub-bootstrap.md |
| task | TAMQ-WP-0001-T02 | done | — | workplans/TAMQ-WP-0001-statehub-bootstrap.md |
| task | TAMQ-WP-0001-T03 | done | — | workplans/TAMQ-WP-0001-statehub-bootstrap.md |

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import fcntl
import os
import pty
import re
import select
import signal
import sys
@ -13,6 +14,35 @@ from collections.abc import Callable, Sequence
from .broker import InputBroker
ENHANCED_ENTER = re.compile(
rb"\x1b(?:\[13(?:;[0-9:]+)*u|\[27;[0-9:]+;13~|OM)"
)
TERMINAL_INPUT_SEQUENCE = re.compile(
rb"\x1b(?:"
rb"\[[0-?]*[ -/]*[@-~]"
rb"|O."
rb"|P.*?(?:\x1b\\)"
rb"|\].*?(?:\x07|\x1b\\)"
rb")",
re.DOTALL,
)
def observed_line(raw: bytes) -> str:
"""Return typed text without terminal replies or local editing controls."""
decoded = TERMINAL_INPUT_SEQUENCE.sub(b"", raw).decode(
"utf-8", errors="replace"
)
text: list[str] = []
for character in decoded:
if character in ("\b", "\x7f"):
if text:
text.pop()
elif character == "\t" or character >= " ":
text.append(character)
return "".join(text)
class PtyTap:
"""Full-duplex PTY proxy. Input is observed, never rewritten."""
@ -24,13 +54,19 @@ class PtyTap:
def _observe_input(self, buffer: bytearray, data: bytes) -> None:
buffer.extend(data)
while True:
delimiters = [index for marker in (b"\r", b"\n") if (index := buffer.find(marker)) >= 0]
if not delimiters:
boundaries = [
(index, index + 1)
for marker in (b"\r", b"\n")
if (index := buffer.find(marker)) >= 0
]
if match := ENHANCED_ENTER.search(buffer):
boundaries.append(match.span())
if not boundaries:
return
index = min(delimiters)
raw = bytes(buffer[:index])
del buffer[: index + 1]
line = raw.decode("utf-8", errors="replace")
start, end = min(boundaries)
raw = bytes(buffer[:start])
del buffer[:end]
line = observed_line(raw)
routed = self.broker.inspect_line(line)
if routed and self.on_line:
self.on_line(line)

View file

@ -4,7 +4,7 @@ import pty
import struct
import termios
from tamq.ptytap import PtyTap, copy_winsize, write_all
from tamq.ptytap import PtyTap, copy_winsize, observed_line, write_all
class RecordingBroker:
@ -29,6 +29,48 @@ def test_input_observer_accepts_raw_terminal_carriage_returns():
assert observed == ["#activity-core: hello"]
def test_input_observer_accepts_enhanced_terminal_enter_sequences():
broker = RecordingBroker()
observed = []
tap = PtyTap(["true"], broker, on_line=observed.append)
buffer = bytearray()
tap._observe_input(buffer, b"#activity-core: CSI-u\x1b[13;1")
assert broker.lines == []
tap._observe_input(buffer, b":1u#other: modify\x1b[27;1;13~")
tap._observe_input(buffer, b"plain\x1bOM")
assert broker.lines == [
"#activity-core: CSI-u",
"#other: modify",
"plain",
]
assert observed == ["#activity-core: CSI-u", "#other: modify"]
def test_input_observer_ignores_terminal_replies_before_typed_line():
broker = RecordingBroker()
observed = []
tap = PtyTap(["true"], broker, on_line=observed.append)
buffer = bytearray()
tap._observe_input(buffer, b"\x1b[9;1R\x1b[?1;2;4c")
tap._observe_input(buffer, b"#activity-core: Helol\x7f\x7flo\r")
assert broker.lines == ["#activity-core: Hello"]
assert observed == ["#activity-core: Hello"]
def test_observed_line_removes_common_terminal_protocol_wrappers():
raw = (
b"\x1b[200~#target: pasted\x1b[201~"
b"\x1b]10;rgb:ffff/ffff/ffff\x07"
b"\x1bP1$r0m\x1b\\"
)
assert observed_line(raw) == "#target: pasted"
def test_copy_winsize_preserves_rows_columns_and_pixels():
source_master, source_slave = pty.openpty()
target_master, target_slave = pty.openpty()

View file

@ -0,0 +1,42 @@
---
id: TAMQ-WP-ADHOC-2026-08-25
type: workplan
title: "Recognize enhanced terminal submission keys"
domain: communication
repo: tmux-amq
status: finished
owner: codex
topic_slug: coulomb-social
created: "2026-08-25"
updated: "2026-08-25"
---
# Recognize enhanced terminal submission keys
## Decode Codex TUI line submission in the PTY tap
```task
id: TAMQ-WP-ADHOC-2026-08-25-T01
status: done
priority: high
```
Reproduce the missing `#repo:` route with a real isolated Codex TUI, extend
input observation to recognize enhanced terminal Enter encodings without
rewriting pane input, add regression and real-Codex smoke evidence, install the
fix, and update the operator documentation.
## Completion evidence
- Reproduced the failure with two isolated real Codex 0.149.1 panes: Codex
submitted the source line but no durable message was created.
- Raw input capture showed CSI cursor/device replies preceding the typed route;
these contaminated the tap's observation buffer before the terminating CR.
- The observer now removes terminal protocol sequences and applies local
backspace editing only to its parsing copy while forwarding source bytes
unchanged.
- A network-disabled two-Codex rerun created one durable `#audit-core:` message,
transitioned it to `injected`, and showed the `# from flex-auth:` prompt in
the target's queued follow-up inputs.
- Full checks and installed-package verification passed. No user session or
durable user message was changed during diagnosis.