Skip to content

fix(agent-core-v2): unify text/binary classification for UTF-8 multibyte files - #2972

Merged
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:bug-031-08-16-log-file-preview
Aug 17, 2026
Merged

fix(agent-core-v2): unify text/binary classification for UTF-8 multibyte files#2972
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:bug-031-08-16-log-file-preview

Conversation

@sailist

@sailist sailist commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

None — the problem is explained below.

Problem

Opening a UTF-8 log/text file containing Chinese or emoji in the web file preview shows "Binary file · application/octet-stream · preview unavailable" instead of the text content.

Root cause: two independent classifiers disagreed. The byte-level binary heuristic counts every byte ≥ 0x7F as non-printable, so a UTF-8 sample full of multibyte characters easily crosses the 30% threshold and is deemed binary. The separate encoding detector only ran on the fs read path (and only helped UTF-16), so the preview (fs:content) and download paths had no encoding awareness at all — and even utf-8 reads of such files were rejected with FS_IS_BINARY.

What changed

  • Added one authoritative sample classifier in agent-core-v2: BOM sniffing → BOM-less UTF-16 zero-byte parity heuristic → strict UTF-8 decode (tolerating a multi-byte sequence truncated at the sample tail) → character-level control-char ratio (C0 except \t\n\r, DEL, C1; same 30% threshold) → binary. Legacy 8-bit encodings (GBK, …) are still refused rather than guessed.
  • detectBinary and detectTextEncoding are now thin derived views over that classifier, and the read path's manual "trust encoding detection over the binary heuristic" patch block is deleted — read, fs:content, and download now share a single conclusion.
  • Raw-byte serving routes (fs:content, download resolution) label text/plain only when the sample classifies as UTF-8 text; UTF-16 files keep their previous application/octet-stream behavior on those routes since the raw bytes are served untranscoded.
  • Tests: classifier cases (UTF-8 CJK/emoji text, NUL bytes, over-threshold control chars, invalid UTF-8, UTF-16 BOM/parity, truncated tail), fs read of a UTF-8 Chinese log (no more FS_IS_BINARY; is_binary=false, mime='text/plain'), download mime for the same file, and fs:content serving a UTF-8 Chinese .log as text/plain.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Aug 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5d35cca

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 16, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@5d35cca
npx https://pkg.pr.new/@moonshot-ai/kimi-code@5d35cca

commit: 5d35cca

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ca841694b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +127 to +128
if (cp === 9 || cp === 10 || cp === 13) continue;
if (cp < 32 || (cp >= 0x7f && cp <= 0x9f)) nonPrintable++;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat every sampled NUL as a binary signal

When a NUL occurs after byte 511, sniffTextEncoding does not see it because its parity scan is limited to 512 bytes, and this loop merely counts it toward the 30% control-character threshold. Consequently, an otherwise ASCII 4 KiB sample containing a sparse NUL is classified as UTF-8 text, whereas the previous classifier treated any NUL as binary; WorkspaceFs read/download and kap-server fs:content can therefore expose such binary files as text. Preserve the unconditional NUL check across the full classification sample after ruling out UTF-16.

Useful? React with 👍 / 👎.

Comment on lines +108 to +113
for (let i = Math.max(0, sample.length - 3); i < sample.length; i++) {
const b = sample[i]!;
const expected = b < 0x80 ? 1 : b < 0xc2 ? 0 : b < 0xe0 ? 2 : b < 0xf0 ? 3 : 4;
if (expected > 0 && i + expected > sample.length) {
end = i;
break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate a trailing UTF-8 sequence before discarding it

When the final three bytes contain any apparent multibyte lead whose nominal width extends past the sample, this code discards the entire suffix without validating the lead or the continuation bytes. Thus samples ending in an impossible UTF-8 lead such as 0xff, or a provably malformed suffix such as 0xe4 0x41, are classified as text and served as such by the WorkspaceFs and fs:content paths. Only trim a suffix when it is a valid prefix of a legal UTF-8 sequence; otherwise retain it so the fatal decoder rejects the sample.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 9ca841694b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sailist
sailist force-pushed the bug-031-08-16-log-file-preview branch from 9ca8416 to 5d35cca Compare August 16, 2026 14:19
@sailist
sailist merged commit 04d23e2 into MoonshotAI:main Aug 17, 2026
15 checks passed
@github-actions github-actions Bot mentioned this pull request Aug 17, 2026
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