Skip to content

read_file: transcode UTF-16 text files to UTF-8 instead of refusing as binary (port from kimi-code) - #80717

Merged
teknium1 merged 1 commit into
mainfrom
kimi-code-port/utf16-read
Aug 17, 2026
Merged

read_file: transcode UTF-16 text files to UTF-8 instead of refusing as binary (port from kimi-code)#80717
teknium1 merged 1 commit into
mainfrom
kimi-code-port/utf16-read

Conversation

@teknium1

@teknium1 teknium1 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

read_file now reads UTF-16 text files by transcoding them to UTF-8 instead of refusing them as binary. Windows Notepad .txt files and PowerShell > redirects are UTF-16 by default; the terminal env decodes stdout as UTF-8 with errors="replace", so their content arrived as U+FFFD mojibake and tripped the binary guard.

Ported from MoonshotAI/kimi-code#2647 (detection derived from VS Code's encoding sniffer), adapted to Hermes' shell-backend architecture: the probe runs via python3 -c through the backend's own shell, so it works identically on local, docker, and ssh backends.

Changes

  • tools/file_operations.py: new ShellFileOperations._try_read_utf16() invoked when the binary guard fires — trusts a BOM first, then the zero-byte parity heuristic (zeros clustered at odd indices = UTF-16 LE, even = BE; placement-only so mixed Latin/CJK content still detects). Transcodes to UTF-8, strips BOM, normalizes CRLF, supports pagination, and appends a "Transcoded from UTF-16-LE" hint so edits re-encoding as UTF-8 aren't a surprise. Real binaries (zeros at both parities), binary extensions, >10 MiB files, and legacy 8-bit encodings (GBK/Big5) still refuse — a wrong silent guess is worse than a clear refusal.
  • tests/tools/test_utf16_read.py: 9 E2E tests against a real LocalEnvironment (BOM LE/BE, BOM-less, mixed CJK, CRLF, pagination, real-binary refusal, binary-extension skip, UTF-8 untouched).

Validation

Before After
UTF-16 LE/BE text file "Binary file - cannot display as text" numbered lines, transcoded hint
Real binary / GBK refused still refused
tests/tools/test_utf16_read.py 6/9 fail (sabotage run, fix disabled) 9/9 pass
Adjacent suites test_file_ops_cwd_tracking + test_file_write_safety: 32/32 pass

Infographic

UTF-16 text file rescue

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 8cfd852 — Port from MoonshotAI/kimi-code#2647: read UTF-16 text files

⚠️ Warnings

OSV vulnerability scan · View job

5 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 20m48s vs 35m26s (-41.3%). 7 job(s) slower, 14 faster, 3 unchanged.

  • Python tests / Run tests slice 6/12: +31.0s
  • Python tests / Run tests slice 10/12: +28.0s
  • Python tests / Run tests slice 4/12: -21.0s
  • Python tests / Run tests slice 3/12: -16.0s
  • Python tests / Run tests slice 8/12: -12.0s

@alt-glitch alt-glitch added type/bug Something isn't working tool/file File tools (read, write, patch, search) platform/windows Native Windows-specific behavior or breakage P2 Medium — degraded but workaround exists sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 7, 2026
@teknium1
teknium1 force-pushed the kimi-code-port/utf16-read branch from 6c564a8 to 53a6a00 Compare August 17, 2026 02:55
…oding to UTF-8

UTF-16 text files (Windows Notepad .txt, PowerShell > redirects) were
refused as binary: the terminal env decodes stdout as UTF-8 with
errors=replace, so their content arrived mangled with U+FFFD and
tripped the binary guard.

ShellFileOperations.read_file now probes the raw bytes via the
backend's Python when the binary guard fires: a BOM or the zero-byte
parity heuristic (derived from VS Code's encoding sniffer, tolerant of
mixed Latin/CJK content) identifies UTF-16 LE/BE, and the file is
transcoded to UTF-8 with CRLF normalized and the BOM stripped. Real
binaries (zeros at both parities), binary extensions, files over
10 MiB, and legacy 8-bit encodings (GBK, Big5) still refuse — a wrong
silent guess is worse than a clear refusal. Works on every shell
backend (local/docker/ssh) since the probe runs via python3 -c.

Tests run against a real LocalEnvironment (E2E, no mocks); sabotage
run confirmed 6/9 fail without the fix.
@teknium1
teknium1 force-pushed the kimi-code-port/utf16-read branch from 53a6a00 to 8cfd852 Compare August 17, 2026 03:30
@teknium1
teknium1 merged commit 341d5ae into main Aug 17, 2026
45 checks passed
@teknium1
teknium1 deleted the kimi-code-port/utf16-read branch August 17, 2026 05:08
teknium1 added a commit that referenced this pull request Aug 17, 2026
…scout-slate wave

Post-merge docs sweep for the Aug 16 scout slate. Two pages:

- mcp.md: tool-result sanitization section — invisible Unicode TAG chars
  (U+E0000-E007F) stripped from results/resources/descriptions (#80689);
  vendor _meta surfaced to the model minus protocol-reserved
  modelcontextprotocol/mcp prefixes (#80712)
- tools.md: tool result annotations section — signal-death exit notes
  (subprocess -signum definite, shell 128+signum hedged) (#78074); UTF-16
  read_file transcoding with disclosure hint and 10MB cap (#80717)

Security-policy docs (approvals/allowlist) intentionally untouched.
teknium1 added a commit that referenced this pull request Aug 17, 2026
…scout-slate wave

Post-merge docs sweep for the Aug 16 scout slate. Two pages:

- mcp.md: tool-result sanitization section — invisible Unicode TAG chars
  (U+E0000-E007F) stripped from results/resources/descriptions (#80689);
  vendor _meta surfaced to the model minus protocol-reserved
  modelcontextprotocol/mcp prefixes (#80712)
- tools.md: tool result annotations section — signal-death exit notes
  (subprocess -signum definite, shell 128+signum hedged) (#78074); UTF-16
  read_file transcoding with disclosure hint and 10MB cap (#80717)

Security-policy docs (approvals/allowlist) intentionally untouched.
lisajlau pushed a commit to lisajlau/hermes-agent that referenced this pull request Aug 20, 2026
…scout-slate wave

Post-merge docs sweep for the Aug 16 scout slate. Two pages:

- mcp.md: tool-result sanitization section — invisible Unicode TAG chars
  (U+E0000-E007F) stripped from results/resources/descriptions (NousResearch#80689);
  vendor _meta surfaced to the model minus protocol-reserved
  modelcontextprotocol/mcp prefixes (NousResearch#80712)
- tools.md: tool result annotations section — signal-death exit notes
  (subprocess -signum definite, shell 128+signum hedged) (NousResearch#78074); UTF-16
  read_file transcoding with disclosure hint and 10MB cap (NousResearch#80717)

Security-policy docs (approvals/allowlist) intentionally untouched.
prmartinow pushed a commit to prmartinow/hermes-agent that referenced this pull request Aug 26, 2026
…scout-slate wave

Post-merge docs sweep for the Aug 16 scout slate. Two pages:

- mcp.md: tool-result sanitization section — invisible Unicode TAG chars
  (U+E0000-E007F) stripped from results/resources/descriptions (NousResearch#80689);
  vendor _meta surfaced to the model minus protocol-reserved
  modelcontextprotocol/mcp prefixes (NousResearch#80712)
- tools.md: tool result annotations section — signal-death exit notes
  (subprocess -signum definite, shell 128+signum hedged) (NousResearch#78074); UTF-16
  read_file transcoding with disclosure hint and 10MB cap (NousResearch#80717)

Security-policy docs (approvals/allowlist) intentionally untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows tool/file File tools (read, write, patch, search) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants