Cross-session message bodies arrive HTML-entity-encoded: Desktop escapes and stamps encoded=1, the CLI never decodes
## Summary
Claude Desktop HTML-escapes the body of a cross-session message and marks it with encoded="1". The CLI has no decode step and does not read that attribute, so &, <, and > reach the receiving model as &, <, >. It affects both directions of a conversation.
Environment: Windows 11 Pro 26200 (VMware VM) · Claude Desktop 1.34493.1 (MSIX/Store) · engine 2.1.237 · stock install (settings.json is {}).
Reproduction — one message
From session A, send to session B:
Token P3ENC-FFF666. Echo this line back verbatim: a & b < c > d
Then read B's transcript .jsonl as bytes. Read the stored file, not the console — a cp1252 terminal will manufacture unrelated mangling and confuse the result.
Observed
Both records that the receiving model reads store the escaped form:
| record | stored payload |
|---|---|
| queue-operation / enqueue | a &amp; b &lt; c &gt; d |
| user — what the model reads | a &amp; b &lt; c &gt; d |
The stored envelope:
<cross-session-message from="local_132844a6-…" name="Test Runner" encoded="1">
Expected
a & b < c > d
Cause
Desktop escapes the body and stamps the attribute:
u = `<cross-session-message from="${cr(r.sessionId)}"${l} encoded="1">\n${lr(a)}\n</cross-session-message>`
// lr(e) = e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")
On the CLI side, encoded= has 0 occurrences in claude.exe (330 MB, plaintext-greppable — cross-session = 56, SendMessage = 64, timed out after = 111 in the same binary, so the zero is real). Body extraction strips the tag with no decode step.
Worth noting the CLI's strict envelope parser captures only:
from= from-session= hop-chain= from-name= from-mode=
in that order, with no slot for name or encoded. Delivery still succeeds because inbound is handled by the looser ^<cross-session-message\b[^>]*> stripper. So the envelope is not silently unparsed — it is parsed loosely and left encoded.
Impact
Any payload containing &, <, or > is corrupted in transit: code, shell commands, HTML/XML/JSX snippets, comparison operators, URLs with query strings. The receiving model sees the entities as literal text and will often faithfully reproduce the corruption downstream.
It is bidirectional — replies come back carrying the same encoded="1" stamp and the same escaping.
Suggested fix
Either drop the escaping and the encoded="1" stamp, or have the CLI honour the attribute and decode when it is present. The attribute is already on the wire, so the second option is backward-compatible with senders that don't set it.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗