Skip to content

fix: reverse-map sanitized tokens split across SSE events - #63

Open
shortside wants to merge 1 commit into
zacdcook:masterfrom
shortside:fix/streaming-reverse-map-cc-2.1.235
Open

fix: reverse-map sanitized tokens split across SSE events#63
shortside wants to merge 1 commit into
zacdcook:masterfrom
shortside:fix/streaming-reverse-map-cc-2.1.235

Conversation

@shortside

Copy link
Copy Markdown

Per-event transformation assumed SSE events are self-contained, but upstream splits assistant text and tool arguments at arbitrary offsets: "OCPlatform" regularly arrives as "OCPlat" + "form" in two content_block_delta events. Neither half matches a reverse pattern, so sanitized tokens leaked to the client verbatim, and split tool/property names inside input_json_delta reached OpenClaw's tool runtime unmapped ("message required", cf. #11). Partial overlaps corrupted text outright: "skillhub" + ".example.com" reverted to "clawhub.example.com" instead of "clawhub.com".

The reverse pass now accumulates the decoded text_delta / input_json_delta payload per content block and releases only the bytes that cannot still be part of a match continuing into the next event (safeSplitPoint), flushing the remainder at content_block_stop, message_stop and stream end. Hold-back only triggers when a delta's tail is a prefix of some reverse pattern, so ordinary prose streams with unchanged event boundaries and latency; thinking and redacted_thinking blocks still pass through byte-identical.

Verified with a harness that boots the proxy against a mocked upstream: 9 targeted cases (mid-token splits, mid-event TCP splits, ping interleaving, per-character deltas, truncated stream) fail 6/9 before and pass 9/9 after, plus a 120-case fuzz asserting split streams produce byte-identical output to the same payload sent as one delta (5 mismatches before, 0 after).

Per-event transformation assumed SSE events are self-contained, but upstream
splits assistant text and tool arguments at arbitrary offsets: "OCPlatform"
regularly arrives as "OCPlat" + "form" in two content_block_delta events.
Neither half matches a reverse pattern, so sanitized tokens leaked to the
client verbatim, and split tool/property names inside input_json_delta reached
OpenClaw's tool runtime unmapped ("message required", cf. zacdcook#11). Partial
overlaps corrupted text outright: "skillhub" + ".example.com" reverted to
"clawhub.example.com" instead of "clawhub.com".

The reverse pass now accumulates the decoded text_delta / input_json_delta
payload per content block and releases only the bytes that cannot still be
part of a match continuing into the next event (safeSplitPoint), flushing the
remainder at content_block_stop, message_stop and stream end. Hold-back only
triggers when a delta's tail is a prefix of some reverse pattern, so ordinary
prose streams with unchanged event boundaries and latency; thinking and
redacted_thinking blocks still pass through byte-identical.

Verified with a harness that boots the proxy against a mocked upstream: 9
targeted cases (mid-token splits, mid-event TCP splits, ping interleaving,
per-character deltas, truncated stream) fail 6/9 before and pass 9/9 after,
plus a 120-case fuzz asserting split streams produce byte-identical output to
the same payload sent as one delta (5 mismatches before, 0 after).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant