Skip to content

fix(poll-loop): don't mistake </message> in body text for the end-of-message tag#2541

Open
markbala wants to merge 3 commits into
nanocoai:mainfrom
markbala:fix/poll-loop-nested-message-tags
Open

fix(poll-loop): don't mistake </message> in body text for the end-of-message tag#2541
markbala wants to merge 3 commits into
nanocoai:mainfrom
markbala:fix/poll-loop-nested-message-tags

Conversation

@markbala
Copy link
Copy Markdown

Type of Change

  • Fix - bug fix or security fix to source code

Description

The bug

If an agent's reply contains </message> inside its body — e.g. in a
code example or while explaining the destination format to a user —
the parser mistakes it for the closing tag of the wrapping
<message to="..."> block and silently drops everything after.

Example

User asks Andy:

How does the destination wrapping work?

Andy replies (full text):

<message to="mark">
Every reply lives inside a `<message to="name">…</message>` block.
The "to" name picks which chat receives it.
</message>

Before the fix: the user receives only

Every reply lives inside a `<message to="name">…

— the parser hit the </message> inside the inline backticks and stopped
there, dropping the rest of the explanation.

After the fix: the user receives the full reply.

The fix

Replace the non-greedy ([\s\S]*?) regex in dispatchResultText with a
scanner that finds the opening tag, then walks forward looking for
</message> while skipping inline backtick spans and ``` fenced blocks.
Additive — same behavior for any input that doesn't contain </message>
inside code.

Verification

  • bun test container/agent-runner/src/poll-loop.test.ts — 6 new tests
    for findClosingMessageTag covering plain close, inline-backtick skip,
    fenced skip, outer-close-despite-inner-fenced-close, unclosed → -1,
    lone-backtick → -1. All 34 tests pass.

markbala and others added 3 commits May 19, 2026 01:24
The non-greedy regex matching <message to="...">…</message> blocks took
the first </message> it saw — including ones inside backtick spans or
fenced code blocks. Agents writing about NanoClaw's own destination
format (e.g. proposals or doc) would silently truncate their delivered
output at the inner closing tag.

Replace the non-greedy regex with a scanning parser that skips over
inline and fenced code regions when searching for the matching
</message>.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Exports findClosingMessageTag and adds focused tests:
- plain message close still matches
- </message> inside inline backticks is skipped
- </message> inside fenced code blocks is skipped
- outer close is found even when an inner close lives in a fence
- unclosed and inline-backtick-only cases return -1

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The first version of findClosingMessageTag walked one character at a
time, which is ~15x slower than the old regex on a 4KB body (~30 μs
vs ~2 μs in a microbenchmark — still single-digit μs but
unnecessarily wasteful).

Replace the char-by-char walk with two indexOf calls per iteration:
locate the next candidate `</message>` and the next backtick, take
whichever comes first. For plain prose with no backticks the close
is found in one indexOf jump, beating the old regex. Backtick-heavy
input still beats the old crawl by skipping past each span via
indexOf rather than scanning character-wise.

Behavior is unchanged — all stress-test cases (inline `</message>`,
fenced examples, unicode, CRLF, mismatched fences, nested opens, etc.)
produce the same blocks and scratchpad as the first version.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@github-actions github-actions Bot added follows-guidelines PR was created using the current contributing template PR: Fix Bug fix labels May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

follows-guidelines PR was created using the current contributing template PR: Fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant