Skip to content

fix(chat): restore fenced code blocks (placeholder used escaped backslashes)#70

Merged
xaspx merged 1 commit into
xaspx:mainfrom
tomekpanek:fix/code-block-placeholder-not-restored
Jun 4, 2026
Merged

fix(chat): restore fenced code blocks (placeholder used escaped backslashes)#70
xaspx merged 1 commit into
xaspx:mainfrom
tomekpanek:fix/code-block-placeholder-not-restored

Conversation

@tomekpanek

Copy link
Copy Markdown
Contributor

Problem

renderChatContent extracts ```fenced``` blocks and replaces each with a sentinel containing real NULL bytes:

return `\x00CODE${id}\x00`;     // \x00 = U+0000 (one byte)

The restore step searches for the literal-text version of that string instead:

html.replace(`\\x00CODE${i}\\x00`, '<pre>...</pre>');
//             ^^                ^^   inside a template literal these are
//                                    backslash + 'x' + '0' + '0', not U+0000

So the needle never matches the sentinel actually present in html. The unreplaced sentinel reaches element.innerHTML = ...; per the HTML spec the parser drops U+0000 characters, leaving the visible text CODE0, CODE1 … inside the rendered message.

fixCodeBlockContent can't recover because it looks for __CODE_CONTENT_${i}__ inside <pre><code> — but those wrappers are never produced when the upstream replace silently fails.

Repro (deterministic)

Any reply containing a fenced code block reproduces the bug. Verified with a small Node script:

INPUT : Has ```\n/path/foo\n``` inline
VISIBLE: Has CODE0 inline

INPUT : Multiple ```\nblock1\n``` and ```\nblock2\n```
VISIBLE: Multiple CODE0 and CODE1

Confused users then ask the model what CODE0 means; the model has no idea and starts hallucinating explanations from its own corrupted history.

The SQLite DB row content has the original markdown with backticks intact — this is purely a render-side regression, no data corruption.

Fix

Drop the extra backslashes so the search needle is the NULL-bracketed sentinel that step 1 actually inserts.

Regression source

Introduced in 6b54d8b (v3.6.0) when renderChatContent was extracted into src/js/chat/cli.js and the __CODE_CONTENT_${i}__ two-stage hand-off was added.

Verified by

Tested locally on top of v3.6.0 — historical sessions that previously showed CODE0/CODE1 now render the original code blocks correctly.

…lashes)

renderChatContent extracts ```fenced``` blocks and replaces each with a
sentinel containing literal NULL bytes:

    return `\x00CODE${id}\x00`;     // \x00 = one byte (U+0000)

The restore step then searched for a literal-text version of the same
string:

    html.replace(`\\x00CODE${i}\\x00`, '<pre>...</pre>');
                  ^^                ^^   in a template literal these are
                                         backslash + 'x' + '0' + '0',
                                         not the U+0000 escape.

So the search needle never matched the sentinel actually present in
`html`. The unreplaced sentinel reached `element.innerHTML = ...`; per
the HTML spec the parser drops U+0000 characters, leaving the visible
text "CODE0", "CODE1", … inside the message.

Repro:
1. Have Hermes return any reply containing a ```fenced``` code block
2. Notice the rendered bubble shows "CODE0" instead of the code
3. Confused users then ask the model what CODE0 means; the model has
   no idea and starts hallucinating explanations

Fix: drop the extra backslashes so the needle is the same NULL-bracketed
sentinel that was inserted in step 1.
@xaspx xaspx merged commit 471dc3b into xaspx:main Jun 4, 2026
0 of 3 checks passed
sidneysimas pushed a commit to sidneysimas/hermes-control-interface that referenced this pull request Jun 15, 2026
- Add office-kanban.js with Swarm Monitor (agent states + kanban board)
- Add Quick Actions popup (Unblock, Mark Done, Approve, Start, Reopen, Reassign)
- CSRF bridge: window.__csrfToken synced from main.js to office-kanban.js
- Read-write DB split: openKanbanDB() readonly, openKanbanDBWritable() for actions
- HCI color palette (amber, green, accent) for action buttons
- Hover-linked highlighting on agent tasks
- 18 modules, 478KB JS (131KB gzipped), 72KB CSS (12.6KB gzipped)
- Deployed and tested on staging at agent2.panji.me
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants