Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/read-character-budgets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Add configurable character limits and resumable long-line file reads without repeated output truncation.
5 changes: 5 additions & 0 deletions .changeset/read-invalid-utf16.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Read malformed UTF-16 files with an explicit lossy-decoding warning.
5 changes: 5 additions & 0 deletions .changeset/read-tail-consistency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Avoid repeated scanning for common tail reads and report file changes detected during tail rereads.
17 changes: 17 additions & 0 deletions docs/en/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,23 @@ disabled = ["EnterPlanMode", "ExitPlanMode", "mcp__github__*"]
Like the `tools` / `disallowedTools` fields of an agent file, this section shapes the tools shown to the model and is enforced again before execution. [Permission rules](#permission) remain a separate control for operations that require approval.
:::

## `read`

`read` controls the character limits for the [`Read` tool](../reference/tools.md). The limit includes file content, line numbers, and the status block; it does not impose a separate line-count or UTF-8 byte limit.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `default_max_chars` | `integer` | `100000` | Character budget when the tool call omits `max_chars` |
| `max_chars` | `integer` | `500000` | Maximum character budget a tool call may request |

```toml
[read]
default_max_chars = 100000
max_chars = 500000
```

Both values must be positive integers. A call's `max_chars` overrides the default, but is capped at the configured maximum; the result reports the effective budget. If the configured default exceeds the maximum, the maximum also limits default reads. Raise `default_max_chars` when you want larger documents to be returned in one call without the agent requesting a larger budget.

## `image`

`image` controls how images are compressed before being sent to the model, across every ingestion point (pasted images, `ReadMediaFile` reads, images in MCP tool results, and so on).
Expand Down
6 changes: 5 additions & 1 deletion docs/en/reference/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ File tools handle reading, writing, and searching the local filesystem — the f
| `Glob` | Auto-allow | Find files by glob pattern |
| `ReadMediaFile` | Auto-allow | Read an image or video file |

**`Read`** accepts a file path (`path`) plus optional `line_offset` (starting line number; negative values count from the end) and `n_lines` (maximum number of lines to read). Returns at most 1000 lines or 100 KB per call; content beyond that limit is accompanied by a truncation notice. If the file is an image or video, the tool suggests using `ReadMediaFile` instead.
**`Read`** accepts a file path (`path`) plus optional `line_offset` (starting line number; negative values count from the end), `column_offset` (zero-based position within the first line of a forward read), `n_lines` (requested number of source lines), and `max_chars` (maximum characters in the result, including line numbers and status). Omitting `n_lines` reads toward the end of the file. The default is 100,000 characters, and calls can request up to 500,000; both values can be changed in the [`read` configuration](../configuration/config-files.md#read). Characters and column offsets use JavaScript string length in the displayed text, excluding the line-number prefix for column offsets: common letters and Chinese characters count as one, while many emoji count as two.

`Read` prefers complete lines and its results are not shortened again by the general tool-output limit. A line that cannot fit on its own page is returned in fragments; the status reports the column range and `Next Read` arguments to retrieve the rest without raising the budget. Join fragments of the same line without adding a newline. A partial line remains in the requested `n_lines` range until its ending is returned. Invalid column positions return an error rather than skipping content.

Tail reads return the newest complete lines in the requested range first. If no complete line fits, the result includes forward `Next Read` arguments for the unread range; `column_offset` cannot be combined with a negative `line_offset`. Continuation positions refer to the current file contents, so start a new read if the file changes. If a tail read reports that the file changed during reading, retry against the updated file. UTF-16 LE/BE files up to 10 MiB are checked with strict decoding first. If decoding fails, `Read` returns readable text with malformed sequences replaced by `�`, and every page warns that decoding was lossy and the text may differ from the original. The warning counts toward the character budget; a literal `�` in a valid file does not trigger it. Use `ReadMediaFile` for images or videos.

**`Write`** accepts `path`, `content`, and an optional `mode` (`overwrite` or `append`; defaults to overwrite). Missing parent directories are created automatically; `append` mode appends content to the end of the file without automatically adding a newline. Writing to an existing file — in either `overwrite` or `append` mode — requires a prior `Read` of that file in the session; the write is rejected if the file changed on disk since the last read, while creating a new file is exempt.

Expand Down
17 changes: 17 additions & 0 deletions docs/zh/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,23 @@ disabled = ["EnterPlanMode", "ExitPlanMode", "mcp__github__*"]
与 Agent 文件中的 `tools` / `disallowedTools` 一样,本节不仅决定模型能"看到"哪些工具,还会在执行前再次强制检查。[权限规则](#permission)仍是独立的控制层,用于决定哪些操作需要审批。
:::

## `read`

`read` 控制 [`Read` 工具](../reference/tools.md) 的字符额度,包含文件正文、行号和状态信息,不额外叠加行数或 UTF-8 字节数上限。

| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `default_max_chars` | `integer` | `100000` | 工具调用未指定 `max_chars` 时的字符额度 |
| `max_chars` | `integer` | `500000` | 单次工具调用可申请的最大字符额度 |

```toml
[read]
default_max_chars = 100000
max_chars = 500000
```

两个值都必须是正整数。调用中的 `max_chars` 覆盖默认值,但不会超过配置的最大值;结果会说明实际生效的额度。如果配置的默认值超过最大值,默认读取也会按最大值执行。如果希望较大的文档默认就能一次返回,无需 Agent 主动申请更大额度,可以提高 `default_max_chars`。

## `image`

`image` 控制图片发送给模型前的压缩行为,对所有图片入口生效(粘贴图片、`ReadMediaFile` 读图、MCP 工具结果里的图片等)。
Expand Down
6 changes: 5 additions & 1 deletion docs/zh/reference/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
| `Glob` | 自动放行 | 按 glob 模式查找文件 |
| `ReadMediaFile` | 自动放行 | 读取图片或视频文件 |

**`Read`** 接受文件路径(`path`)以及可选的 `line_offset`(起始行号,支持负数从末尾倒数)和 `n_lines`(读取行数上限)。单次最多返回 1000 行或 100 KB,超出部分会附带截断提示。如果文件是图片或视频,工具会提示改用 `ReadMediaFile`。
**`Read`** 接受文件路径(`path`)以及可选的 `line_offset`(起始行号,支持负数从末尾倒数)、`column_offset`(正向读取时,起始行内从 0 开始的位置)、`n_lines`(请求读取的源文件行数)和 `max_chars`(结果的字符上限,包含行号和状态信息)。省略 `n_lines` 时向文件末尾读取。默认上限为 100,000 字符,调用可申请到 500,000 字符;两个值都可通过 [`read` 配置](../configuration/config-files.md#read) 修改。字符数和列偏移按显示文本的 JavaScript 字符串长度计算,列偏移不包含行号前缀:常见字母和汉字各计 1,许多 emoji 计 2。

`Read` 优先返回完整行,结果不会再被通用工具输出限制缩短。如果单独一行也无法在一页中容纳,工具会返回片段,并在状态中说明列范围及 `Next Read` 参数,无需提高额度即可继续读取。拼接同一行的片段时不要额外插入换行。一行只返回部分内容时,仍会计入剩余的 `n_lines` 范围,直到行尾返回为止。非法列偏移会明确报错,不会跳过内容。

尾读优先返回请求范围中较新的完整行。如果连一条完整行都无法容纳,结果会给出未读范围的正向 `Next Read` 参数;`column_offset` 不能与负数 `line_offset` 同时使用。续读位置针对文件的当前内容,文件变化后应重新读取。如果尾读提示读取期间文件发生变化,请基于更新后的文件重试。10 MiB 以内的 UTF-16 LE/BE 文件会先尝试严格解码;失败后,`Read` 会将损坏序列替换为 `�` 并返回可读文本,同时在每一页提示发生了有损解码、文本可能与原文不同。告警也计入字符额度;有效文件中原本就有的 `�` 不会触发告警。图片和视频请使用 `ReadMediaFile`。

**`Write`** 接受 `path`、`content` 和可选的 `mode`(`overwrite` 或 `append`,默认覆盖)。缺失的父目录会自动创建;`append` 模式将内容追加到文件末尾,不自动添加换行。写入已存在的文件(无论 `overwrite` 还是 `append` 模式)要求本会话中先用 `Read` 读过该文件——若文件自上次读取后在磁盘上发生变化,写入会被拒绝;新建文件不受此限。

Expand Down
13 changes: 12 additions & 1 deletion packages/agent-core-v2/docs/config-manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# commented "# field: type" lines describe the remaining schema fields.
# Values resolve as: default -> config.toml -> env overlay -> memory.

# Index (27 sections · 2 overlay(s))
# Index (28 sections · 2 overlay(s))
# background src/agent/task/configSection.ts
# builtinProductSkills src/features/skill/catalog/configSection.ts
# cron src/features/cron/configSection.ts
Expand All @@ -28,6 +28,7 @@
# models src/app/kosongConfig/configSection.ts
# permission src/agent/permissionRules/configSection.ts
# providers src/app/kosongConfig/configSection.ts
# read src/agent/tools/os/read/configSection.ts
# secondaryModel src/session/subagent/configSection.ts
# services src/app/auth/configSection.ts
# subagent src/session/subagent/configSection.ts
Expand Down Expand Up @@ -338,6 +339,16 @@ merge_all_available_skills = true
# env: record<string, string>
# source: record<string, any>

# ##########################################################################
# read
# owner: src/agent/tools/os/read/configSection.ts
# scope: core
# ##########################################################################

[read]
# default_max_chars: integer
# max_chars: integer

# ##########################################################################
# secondaryModel (config.toml: secondary_model)
# owner: src/session/subagent/configSection.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ If you need exact command output, file contents, error text, or the wording of a
- Layout: one file per agent. agents/main/ is the main agent; each subagent has its own agents/<agentId>/wire.jsonl. A parent's log holds only the Agent tool call and the subagent's returned result — the subagent's own steps are in its own file.
- Format: one JSON record per line, append-only; `type` says what it is. The conversation is in `context.append_message` (user prompts) and `context.append_loop_event` (event.type: step.begin | content.part [text|think] | tool.call | tool.result | step.end). Every other type (llm.request, usage.record, token_counting.measured, metadata, profile.bind, …) is bookkeeping — skip it.
- Boundaries: `context.apply_compaction` marks a compaction (older lines stay in the file; grep for it to find exact boundaries). `context.undo` count=N retracts the previous N messages — treat retracted content as never having happened. `context.clear` resets the conversation.
- Externalized content: tool results over 50k chars are stored truncated, with an `output_path` to a tool-results/*.txt file holding the full text. Media parts are blob references, not inline.
- Reading: lines are long JSON (often 10k+ chars). Grep the file for a keyword to get line numbers, then Read exactly that line (line_offset=N, n_lines=1) — Read returns wire.jsonl lines whole up to ~150k chars. To pull one field with real newlines: sed -n 'Np' wire.jsonl | jq -r '.event.result.output'. Never Read large ranges — a handful of records can exceed the per-call byte cap.
- Externalized content: tool results over 50k chars may contain an `output_path` pointing to saved output; check the result's preservation notice before assuming the file contains everything. Read results are bounded by their own character budget and are not spilled again. Media parts are blob references, not inline.
- Reading: lines are long JSON (often 10k+ chars). Grep the file for a keyword to get line numbers, then Read exactly that line (line_offset=N, n_lines=1). Long records can span several Read results: follow Next Read with its column_offset until the line is complete, joining fragments without inserting newlines. To pull one field with real newlines when Bash is available: sed -n 'Np' wire.jsonl | jq -r '.event.result.output'. Prefer individual records over large ranges.
14 changes: 14 additions & 0 deletions packages/agent-core-v2/src/agent/tools/os/read/configSection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from 'zod';

import { registerConfigSection } from '#/app/config/configSectionContributions';

export const READ_SECTION = 'read';

export const ReadConfigSchema = z.object({
defaultMaxChars: z.number().int().positive().optional(),
maxChars: z.number().int().positive().optional(),
});

export type ReadConfig = z.infer<typeof ReadConfigSchema>;

registerConfigSection(READ_SECTION, ReadConfigSchema, { defaultValue: {} });
15 changes: 9 additions & 6 deletions packages/agent-core-v2/src/agent/tools/os/read/read.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ If the user provides a concrete file path to a text file, call Read directly. Do
When you need several files, prefer to read them in parallel: emit multiple `Read` calls in a single response instead of reading one file per turn.

- Relative paths resolve against the working directory; a path outside the working directory must be absolute.
- Returns up to ${MAX_LINES} lines or ${MAX_BYTES_KB} KB per call, whichever comes first; lines longer than ${MAX_LINE_LENGTH} chars are truncated mid-line (recover the elided content with Bash, e.g. `cut` or `sed`).
- Page larger files with `line_offset` (1-based start line) and `n_lines`. Omit `n_lines` to read up to the ${MAX_LINES}-line cap.
- Kimi Code agent event logs (`wire.jsonl` under the sessions directory) are returned with whole lines (up to ~150k chars per record); read them one record at a time with `n_lines=1` after locating the line with Grep.
- Returns text within `max_chars`, including line numbers and the status block, preferring complete lines. The configured default is ${DEFAULT_MAX_CHARS} characters; calls can request up to ${MAX_CHARS}. Characters use JavaScript string length, not UTF-8 bytes or tokens. Read results are not spilled or shortened again by the general tool-output limit.
- Omit `n_lines` to read toward the end of the file. There is no fixed line-count cap. When the task requires the full text of a large file, request a larger `max_chars`, up to ${MAX_CHARS}, in the first call.
- Page larger files with `line_offset` (1-based start line) and `n_lines`. If the result is incomplete, copy the `Next Read` arguments in the status block to continue without gaps or overlaps. Do not answer from a partial page when the task requires the remaining content.
- If a single line cannot fit on its own page, Read returns a fragment and reports its column range. Continue on the same line with the supplied `column_offset`; do not insert a newline between fragments of one source line. A partial line still counts toward the remaining `n_lines` until its ending is returned.
- `column_offset` is a zero-based position in the first line's displayed text, excluding its line-number prefix. It is supported only for forward reads. Offsets past the line or inside a Unicode surrogate pair return an error. Continuation refers to the current file contents; start a new read if the file changed.
- Kimi Code agent event logs (`wire.jsonl` under the sessions directory) follow the same character budget; locate a record with Grep, read it with `n_lines=1`, and follow `Next Read` to retrieve every fragment of a long record.
- Sensitive files (`.env` files, credential stores, SSH private keys, and similar secrets) are refused to protect secrets; do not attempt to read them. Templates and public keys are exempt: `.env.example` / `.env.sample` / `.env.template` and public SSH keys such as `id_rsa.pub` read normally.
- UTF-8 text files are read directly. UTF-16 LE/BE text files (with or without a BOM) are detected automatically and transcoded to UTF-8 for display; the status block notes the detected encoding, and Edit/Write on such a file still expect UTF-8 — convert its encoding first (e.g. with `iconv`). Other encodings (e.g. GBK), binary files, and files containing NUL bytes are refused.
- Negative line_offset reads from the end of the file (for example, -100 reads the last 100 lines); the absolute value cannot exceed ${MAX_LINES}.
- UTF-8 text files are read directly. UTF-16 LE/BE text files (with or without a BOM) are detected automatically and checked with strict decoding first. If malformed sequences are found, Read returns readable text with U+FFFD replacements and a lossy-decoding warning on every page; do not treat this view as exact original text. The status block notes the detected encoding, and Edit/Write on such a file still expect UTF-8 — convert its encoding first (e.g. with `iconv`). Other encodings (e.g. GBK), binary files, and files containing NUL bytes are refused.
- Negative `line_offset` reads from the end of the file (for example, -100 reads the last 100 lines). If the requested tail range exceeds the character budget, the newest complete lines in that range are returned first; `Next Read` covers the omitted earlier range. If no complete line fits, Read reports this and supplies forward `Next Read` arguments for the entire unread range. Omit `column_offset` when using a negative `line_offset`.
- Output format: `<line-number>\t<content>` per line.
- A `<system>...</system>` status block is appended after the file content; it summarizes how much was read (line and byte counts, truncation, line-ending notes) and is not part of the file itself.
- A `<system>...</system>` status block is appended after the file content. It reports the actual returned range, total lines, effective character budget, whether the requested range is complete, and whether EOF was reached. The block is not part of the file itself.
- Pure CRLF files are displayed with LF line endings; `Edit` matches this output and preserves CRLF when writing back.
- Mixed or lone carriage-return line endings are shown as `\r` and require exact `Edit.old_string` escapes.
- After a successful `Edit`/`Write`, do not re-read solely to prove the write landed. When the task depends on an exact file, API, or output shape, inspect the final external contract before finishing.
Loading
Loading