fix(codex): use UTF-16-safe truncation for approval display paths#100177
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed July 6, 2026, 4:03 PM ET / 20:03 UTC. Summary PR surface: Source +3, Tests +180. Total +183 across 5 files. Reproducibility: yes. Source inspection of current main shows raw String.slice at the Codex approval, elicitation, and gateway display caps; placing an emoji surrogate pair across those deterministic UTF-16 boundaries reproduces the lone-surrogate condition. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Keep the focused helper replacement and merge after a maintainer accepts the current proof or the contributor adds redacted live bridge proof for a spawned Codex app-server approval or elicitation request. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection of current main shows raw String.slice at the Codex approval, elicitation, and gateway display caps; placing an emoji surrogate pair across those deterministic UTF-16 boundaries reproduces the lone-surrogate condition. Is this the best way to solve the issue? Yes for the code shape. Reusing the existing plugin-sdk text helpers is the narrow owner-boundary fix and preserves existing caps, markers, redaction order, and approval routing; merge readiness depends on proof acceptance, not a different implementation. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 80537c1ba408. Label changesLabel justifications:
Evidence reviewedPR surface: Source +3, Tests +180. Total +183 across 5 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (11 earlier review cycles; latest 8 shown)
|
742aec8 to
817246b
Compare
c0404a8 to
520a127
Compare
51ae5ed to
e62b721
Compare
|
Land-ready at exact reviewed head
Direct dependency-contract inspection used Codex No docs or changelog change is required for this internal boundary correction. |
|
Merged via squash.
|
…enclaw#100177) * fix(codex): use UTF-16-safe truncation for approval display paths * fix(codex): resolve oxlint errors in approval-bridge and elicitation-bridge tests * test(codex): cover approval Unicode boundaries * test(codex): narrow approval command assertion * chore: remove generated changelog entry * chore: restore CHANGELOG.md to upstream/main baseline * test(codex): tighten UTF-16 approval assertions --------- Co-authored-by: Peter Steinberger <[email protected]>
…enclaw#100177) * fix(codex): use UTF-16-safe truncation for approval display paths * fix(codex): resolve oxlint errors in approval-bridge and elicitation-bridge tests * test(codex): cover approval Unicode boundaries * test(codex): narrow approval command assertion * chore: remove generated changelog entry * chore: restore CHANGELOG.md to upstream/main baseline * test(codex): tighten UTF-16 approval assertions --------- Co-authored-by: Peter Steinberger <[email protected]>
What Problem This Solves
The codex app-server approval and elicitation bridges truncate user-provided text (command previews, approval titles, display parameter values) using raw
String.prototype.slice. When the truncation boundary falls between the high and low surrogate of a supplementary-plane character (emoji, CJK extension, etc.), the output contains a lone surrogate — corrupted UTF-16.This is the same class of bug fixed in #99566 for
exec-approval-command-display.ts. The sharedtruncateUtf16Safe/sliceUtf16Safehelpers (fromopenclaw/plugin-sdk/text-utility-runtime) already back off from split surrogate pairs at the boundary; they are used by 20+ other extensions but were not yet adopted in these three codex bridge files.Relates to gap finding:
codex-utf16-safe-truncation(utf16-boundary, evidence level A)Location:
extensions/codex/src/app-server/approval-bridge.ts:1498Why This Change Was Made
Replace raw
String.prototype.slicewithtruncateUtf16Safe/sliceUtf16Safein all 6 call sites across 3 source files:approval-bridge.tstruncate()slice(0, max-3)→truncateUtf16Safe(value, max-3)approval-bridge.tspreviewSource()value.slice(0, 4096)→sliceUtf16Safe(value, 0, 4096)approval-bridge.tsappendPreviewPart()value.slice(0, 4096)→sliceUtf16Safe(value, 0, 4096)elicitation-bridge.tssanitizeDisplayText()value.slice(0, 4096)→sliceUtf16Safe(value, 0, 4096)elicitation-bridge.tstruncateDisplayText()slice(0, max-3)→truncateUtf16Safe(value, max-3)plugin-approval-roundtrip.tstruncateForGateway()slice(0, max-3)→truncateUtf16Safe(value, max-3)The SDK helpers are dependency-free, already exported via
openclaw/plugin-sdk/text-utility-runtime, and already in use within the same extension (node-cli-sessions.ts:698). Cap sizes, truncation markers, and redaction order are unchanged.User Impact
Affects codex app-server users whose command previews or approval display text contain emoji or other astral-plane characters near the truncation boundary. Before this fix, a dangling high surrogate could appear in approval prompts; after, the emoji is dropped as a unit and the preceding character is preserved.
Evidence
1. Rebased onto upstream/main (2026-07-05)
Branch rebased onto
upstream/main(e6ec74c254) to pick up latest doc snapshots. No merge conflicts. Commits cleanly apply.2. Real Codex app-server spawn + helper verification
Real
codexCLI v0.142.5 spawned ascodex app-server --listen stdio://. The process accepts JSON-RPCinitializerequests and responds with protocol handshake — confirming the binary is functional for app-server transport.Against the same built dist used by the running gateway, the imported
sliceUtf16Safe/truncateUtf16Safehelpers frompackages/normalization-core/dist/utf16-slice.mjsare exercised with surrogate-pair inputs matching all three PR-affected truncation boundaries:3. Handler-level unit tests — 115 passed
Two new regression tests assert zero lone surrogates in both approval events and gateway request payloads after
handleCodexAppServerApprovalRequestandhandleCodexAppServerElicitationRequestprocess emoji-boundary inputs.4. Transport-layer roundtrip — real JSON-RPC dispatch path
Transport-layer test uses
CodexAppServerClient.fromTransportForTestswith aPassThrough-backed fake transport to exercise the real JSON-RPC code path that a spawned codex app-server process traverses. The client'sreadline.createInterfaceparses frames,handleParsedMessage→handleServerRequestdispatches, handlers run the full approval/elicitation bridge with emoji at truncation boundaries, andwriteMessageroutes results back.5. Lint / format
oxlinton all changed files — cleanoxfmt --check— cleanSummary
Behavior addressed: every truncation call site in approval-bridge.ts, elicitation-bridge.ts, and plugin-approval-roundtrip.ts.
Real environment tested: Linux x64, Node 24.13.1, branch
fix/codex-utf16-safe-truncation, codex 0.142.5 installed and authenticated.Observed result after fix: the built helper functions correctly back off from split surrogate pairs at all 6 truncation boundaries. The raw
String.slicebug is reproduced and confirmed fixed. The real codex app-server binary spawns and handles JSON-RPC protocol handshake. Handler-level tests exercise the exact truncation functions used in production approval/elicitation dispatch.🤖 Generated with Claude Code