diff --git a/apps/api/src/api/system/terminal.py b/apps/api/src/api/system/terminal.py index 4622aae..41eba1b 100644 --- a/apps/api/src/api/system/terminal.py +++ b/apps/api/src/api/system/terminal.py @@ -334,22 +334,20 @@ async def _write_loop(session_ref: SessionRef, websocket, instance_id: str) -> N await session.write_input(message["bytes"]) elif "text" in message: text = message["text"] - # Treat a text frame as a control message only when it - # is a JSON object carrying a known "type". Anything - # else — including JSON-shaped pastes — is forwarded as - # raw terminal input so multiline and bracketed-paste - # content is never silently swallowed or misrouted. + # A text frame that parses to a JSON object with a + # "type" field is a control message and must NEVER be + # written to the PTY (e.g. the heartbeat {"type":"pong"} + # must be consumed, not typed into the shell/pi). Handle + # known types and ignore unknown ones. Everything else + # (keystrokes, bracketed-paste content, plain text) is + # forwarded as raw terminal input. ctrl = None if text.startswith("{"): try: parsed = json.loads(text) except json.JSONDecodeError: parsed = None - if isinstance(parsed, dict) and parsed.get("type") in ( - "resize", - "ack", - "reset", - ): + if isinstance(parsed, dict) and "type" in parsed: ctrl = parsed if ctrl is None: