diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md
index 2a08a1e3..a758cca0 100644
--- a/packages/coding-agent/CHANGELOG.md
+++ b/packages/coding-agent/CHANGELOG.md
@@ -2,6 +2,10 @@
## [Unreleased]
+### Fixed
+
+- Fixed exported session markdown to render Markdown while showing HTML-like message content such as `...` verbatim, so shared sessions match the TUI instead of letting the browser interpret message text ([#3484](https://github.com/badlogic/pi-mono/issues/3484))
+
## [0.68.0] - 2026-04-20
### New Features
diff --git a/packages/coding-agent/src/core/export-html/template.js b/packages/coding-agent/src/core/export-html/template.js
index 8042c1db..f03aecc3 100644
--- a/packages/coding-agent/src/core/export-html/template.js
+++ b/packages/coding-agent/src/core/export-html/template.js
@@ -1448,18 +1448,21 @@
// INITIALIZATION
// ============================================================
- // Escape HTML tags in text (but not code blocks)
- function escapeHtmlTags(text) {
- return text.replace(/<(?=[a-zA-Z\/])/g, '<');
- }
-
- // Configure marked with syntax highlighting and HTML escaping for text
+ // Configure marked with syntax highlighting and TUI-compatible HTML handling
const strictStrikethroughRegex = /^(~~)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/;
marked.use({
breaks: true,
gfm: true,
tokenizer: {
+ // Treat HTML-like input as plain text so tags are shown verbatim,
+ // matching the TUI markdown renderer.
+ html() {
+ return undefined;
+ },
+ tag() {
+ return undefined;
+ },
del(src) {
const match = strictStrikethroughRegex.exec(src);
if (!match) return undefined;
@@ -1493,10 +1496,6 @@
}
return `
${highlighted}
`;
},
- // Text content: escape HTML tags
- text(token) {
- return escapeHtmlTags(escapeHtml(token.text));
- },
// Inline code: escape HTML
codespan(token) {
return `${escapeHtml(token.text)}`;