Skip to content

fix(opencode): preserve Unicode in catalog tool input#109591

Merged
steipete merged 2 commits into
openclaw:mainfrom
ZengWen-DT:fix/opencode-catalog-tool-input-utf16
Jul 17, 2026
Merged

fix(opencode): preserve Unicode in catalog tool input#109591
steipete merged 2 commits into
openclaw:mainfrom
ZengWen-DT:fix/opencode-catalog-tool-input-utf16

Conversation

@ZengWen-DT

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes an issue where users viewing an OpenCode session catalog could receive malformed Unicode in long tool input when the 20,000-character display limit landed inside an emoji's UTF-16 surrogate pair.

Why This Change Was Made

The catalog now uses the existing UTF-16-safe truncation helper before appending its ellipsis. This changes only the over-limit boundary; CLI invocation, parsing, limits, and paging remain unchanged. The main risk is altering catalog text length by one code unit at a split pair, covered through the production reader and its real child-process boundary.

User Impact

Long OpenCode tool input retains valid Unicode at the catalog boundary instead of producing a replacement character when encoded for transport or display.

Evidence

I ran the same throwaway TypeScript probe against the real exported readLocalOpenCodeTranscriptPage function. It spawned a real child process at the OpenCode CLI seam, returned the official export JSON shape, and placed 🎉 across code-unit positions 19,999–20,000 in tool input; only the upstream CLI response was controlled.

Before the fix, with the production truncation line restored to slice(0, maxLength):

{"productionEntry":"readLocalOpenCodeTranscriptPage","realChildProcess":true,"unpaired":true,"utf8RoundTrip":false}
exit 1

After the fix:

{"productionEntry":"readLocalOpenCodeTranscriptPage","realChildProcess":true,"unpaired":false,"utf8RoundTrip":true}
exit 0

Focused regression suite after rebasing onto current upstream/main:

node scripts/run-vitest.mjs extensions/opencode/session-catalog.test.ts
Test Files  1 passed (1)
Tests  12 passed (12)

Targeted oxfmt --check, oxlint, and git diff --check passed. Full repository gates are left to fork CI. I did not use a persistent live OpenCode session; the proof drives the production child-process invocation, export parser, transcript mapper, and catalog output, while controlling only the CLI payload needed to place the exact boundary.

AI-assisted: Codex helped implement and review the change; the behavior proof and tests above were run manually.

@ZengWen-DT
ZengWen-DT force-pushed the fix/opencode-catalog-tool-input-utf16 branch from 24db632 to e29f737 Compare July 17, 2026 03:35
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 17, 2026
@clawsweeper

clawsweeper Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 17, 2026, 12:06 AM ET / 04:06 UTC.

Summary
Replaces raw JSON slicing for OpenCode catalog tool input with the shared UTF-16-safe truncation helper and adds a surrogate-pair boundary regression test.

PR surface: Source +1, Tests +20. Total +21 across 2 files.

Reproducibility: yes. at the source level. The production-reader probe demonstrates how raw slicing splits the surrogate pair at the exact 20,000-code-unit boundary, although it uses a controlled OpenCode payload.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: extensions/opencode/session-catalog.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦐 gold shrimp
Patch quality: 🐚 platinum hermit
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted before-and-after terminal output from an actual OpenCode session or export containing boundary-crossing non-BMP tool input.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The terminal probe reaches the production reader and a real child-process seam, but its executable and export payload are controlled rather than produced by an actual OpenCode session; add redacted live output, update the PR body, and request @clawsweeper re-review if review does not rerun automatically.

Risk before merge

  • [P1] The controlled executable proves the exact boundary through production code, but it does not demonstrate that an actual OpenCode export containing long non-BMP tool input reaches and renders through the same user-visible catalog path after the fix.

Maintainer options:

  1. Decide the mitigation before merge
    Keep the shared UTF-16-safe helper change and regression test, then add a redacted terminal transcript from an actual OpenCode session or export whose long tool input crosses the 20,000-code-unit boundary and shows valid catalog output.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] Do not dispatch an automated repair because no patch defect is identified; the remaining merge gate is contributor-supplied real OpenCode behavior evidence.

Security
Cleared: The focused diff adds no dependency, workflow, permission, secret, package-resolution, download, or new code-execution surface.

Review details

Best possible solution:

Keep the shared UTF-16-safe helper change and regression test, then add a redacted terminal transcript from an actual OpenCode session or export whose long tool input crosses the 20,000-code-unit boundary and shows valid catalog output.

Do we have a high-confidence way to reproduce the issue?

Yes at the source level. The production-reader probe demonstrates how raw slicing splits the surrogate pair at the exact 20,000-code-unit boundary, although it uses a controlled OpenCode payload.

Is this the best way to solve the issue?

Yes. Reusing the existing Plugin SDK UTF-16-safe truncation helper at the serialization boundary is the narrowest maintainable fix; duplicating surrogate logic locally or changing the catalog limit would be inferior.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 135aa7a0025d.

Label changes

Label justifications:

  • P2: This addresses malformed Unicode in a limited but real user-facing OpenCode catalog edge case without broader runtime or compatibility impact.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The terminal probe reaches the production reader and a real child-process seam, but its executable and export payload are controlled rather than produced by an actual OpenCode session; add redacted live output, update the PR body, and request @clawsweeper re-review if review does not rerun automatically.
Evidence reviewed

PR surface:

Source +1, Tests +20. Total +21 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 2 1 +1
Tests 1 21 1 +20
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 23 2 +21

What I checked:

  • Root cause: The catalog serializes tool input and truncates it at 20,000 UTF-16 code units; raw slicing can retain only the high surrogate when a non-BMP character crosses that boundary. (extensions/opencode/session-catalog.ts:356, 135aa7a0025d)
  • Implementation shape: The branch changes only the truncation operation to the existing public Plugin SDK helper, preserving the limit, ellipsis behavior, parsing, paging, and child-process invocation. (extensions/opencode/session-catalog.ts:356, 73ade32c2ae9)
  • Regression coverage: The added test places an emoji across the truncation boundary and verifies that catalog output ends with an ellipsis and contains no unpaired UTF-16 surrogate. (extensions/opencode/session-catalog.test.ts:303, 73ade32c2ae9)
  • Plugin boundary: The implementation consumes a documented openclaw/plugin-sdk runtime subpath rather than importing core internals or introducing another local truncation implementation. (extensions/AGENTS.md:264, 135aa7a0025d)
  • Proof limitation: The PR body reports execution through the production reader and a real child process, but explicitly states that the OpenCode executable payload was controlled and no persistent live OpenCode session was used. (73ade32c2ae9)
  • Re-review continuity: The previous review raised no implementation finding and requested actual OpenCode evidence; the refreshed head merges current main but retains the same synthetic upstream proof boundary. (73ade32c2ae9)

Likely related people:

  • fuller-stack-dev: Recent merged work on direct catalog-session creation is adjacent to the session-catalog product surface and provides the strongest available feature-history routing signal. (role: recent catalog-session contributor; confidence: medium; files: extensions/opencode/session-catalog.ts, extensions/opencode/session-catalog.test.ts)
  • vincentkoc: Recent paired-node terminal and session work is adjacent to the OpenCode catalog reader and its child-process boundary. (role: recent paired-node and terminal contributor; confidence: medium; files: extensions/opencode/session-catalog.ts)
  • steipete: Refreshed this branch onto current main and is a reasonable secondary routing candidate for the focused plugin integration change. (role: recent integration contributor; confidence: low; commits: 73ade32c2ae9; files: extensions/opencode/session-catalog.ts, extensions/opencode/session-catalog.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (1 earlier review cycle)
  • reviewed 2026-07-17T03:40:49.214Z sha e29f737 :: needs real behavior proof before merge. :: none

@steipete
steipete merged commit 5f56a54 into openclaw:main Jul 17, 2026
83 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: opencode P2 Normal backlog priority with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants