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
Open
fix(poll-loop): don't mistake </message> in body text for the end-of-message tag#2541markbala wants to merge 3 commits into
markbala wants to merge 3 commits into
Conversation
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]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type of Change
Description
The bug
If an agent's reply contains
</message>inside its body — e.g. in acode 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:
Andy replies (full text):
Before the fix: the user receives only
— the parser hit the
</message>inside the inline backticks and stoppedthere, dropping the rest of the explanation.
After the fix: the user receives the full reply.
The fix
Replace the non-greedy
([\s\S]*?)regex indispatchResultTextwith ascanner 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 testsfor
findClosingMessageTagcovering plain close, inline-backtick skip,fenced skip, outer-close-despite-inner-fenced-close, unclosed → -1,
lone-backtick → -1. All 34 tests pass.