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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Only write entries that are worth mentioning to users.

## Unreleased

- Tool: Add `ReadMediaFile` tool for reading image/video files; `ReadFile` now focuses on text files only

## 0.82 (2026-01-21)

- Tool: Allow `WriteFile` and `StrReplaceFile` tools to edit/write files outside the working directory when using absolute paths
Expand Down
17 changes: 13 additions & 4 deletions docs/en/customization/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ kimi --agent okabe

The default agent, suitable for general use. Enabled tools:

`Task`, `SetTodoList`, `Shell`, `ReadFile`, `Glob`, `Grep`, `WriteFile`, `StrReplaceFile`, `SearchWeb`, `FetchURL`
`Task`, `SetTodoList`, `Shell`, `ReadFile`, `ReadMediaFile`, `Glob`, `Grep`, `WriteFile`, `StrReplaceFile`, `SearchWeb`, `FetchURL`

### `okabe`

Expand Down Expand Up @@ -191,13 +191,22 @@ The following are all built-in tools in Kimi CLI.
### `ReadFile`

- **Path**: `kimi_cli.tools.file:ReadFile`
- **Description**: Read file content. Supports text, image, and video files. For text files, max 1000 lines per read, max 2000 characters per line; for image/video files, max 80MB. Files outside working directory require absolute paths.
- **Description**: Read text file content. Max 1000 lines per read, max 2000 characters per line. Files outside working directory require absolute paths.

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | File path |
| `line_offset` | int | Starting line number, default 1 |
| `n_lines` | int | Number of lines to read, default/max 1000 |

### `ReadMediaFile`

- **Path**: `kimi_cli.tools.file:ReadMediaFile`
- **Description**: Read image or video files. Max file size 100MB. Only available when the model supports image/video input. Files outside working directory require absolute paths.

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | File path |
| `line_offset` | int | Starting line number, default 1 (text files only) |
| `n_lines` | int | Number of lines to read, default/max 1000 (text files only) |

### `Glob`

Expand Down
2 changes: 2 additions & 0 deletions docs/en/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This page documents the changes in each Kimi CLI release.

## Unreleased

- Tool: Add `ReadMediaFile` tool for reading image/video files; `ReadFile` now focuses on text files only

## 0.82 (2026-01-21)

- Tool: Allow `WriteFile` and `StrReplaceFile` tools to edit/write files outside the working directory when using absolute paths
Expand Down
17 changes: 13 additions & 4 deletions docs/zh/customization/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ kimi --agent okabe

默认 Agent,适合通常情况使用。启用的工具:

`Task`、`SetTodoList`、`Shell`、`ReadFile`、`Glob`、`Grep`、`WriteFile`、`StrReplaceFile`、`SearchWeb`、`FetchURL`
`Task`、`SetTodoList`、`Shell`、`ReadFile`、`ReadMediaFile`、`Glob`、`Grep`、`WriteFile`、`StrReplaceFile`、`SearchWeb`、`FetchURL`

### `okabe`

Expand Down Expand Up @@ -191,13 +191,22 @@ agent:
### `ReadFile`

- **路径**:`kimi_cli.tools.file:ReadFile`
- **描述**:读取文件内容。支持文本、图片和视频文件。文本文件单次最多读取 1000 行,每行最多 2000 字符;图片/视频文件最大 80MB。工作目录外的文件需使用绝对路径。
- **描述**:读取文本文件内容。单次最多读取 1000 行,每行最多 2000 字符。工作目录外的文件需使用绝对路径。

| 参数 | 类型 | 说明 |
|------|------|------|
| `path` | string | 文件路径 |
| `line_offset` | int | 起始行号,默认 1 |
| `n_lines` | int | 读取行数,默认/最大 1000 |

### `ReadMediaFile`

- **路径**:`kimi_cli.tools.file:ReadMediaFile`
- **描述**:读取图片或视频文件。文件最大 100MB。仅当模型支持图片/视频输入时可用。工作目录外的文件需使用绝对路径。

| 参数 | 类型 | 说明 |
|------|------|------|
| `path` | string | 文件路径 |
| `line_offset` | int | 起始行号,默认 1(仅适用于文本文件) |
| `n_lines` | int | 读取行数,默认/最大 1000(仅适用于文本文件) |

### `Glob`

Expand Down
2 changes: 2 additions & 0 deletions docs/zh/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## 未发布

- Tool:添加 `ReadMediaFile` 工具用于读取图片/视频文件;`ReadFile` 现在仅用于读取文本文件

## 0.82 (2026-01-21)

- Tool:`WriteFile` 和 `StrReplaceFile` 工具支持使用绝对路径编辑/写入工作目录外的文件
Expand Down
2 changes: 1 addition & 1 deletion klips/klip-12-wire-initialize-external-tools.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
Author: "@stdrc"
Updated: 2026-01-14
Status: Draft
Status: Implemented
---

# KLIP-12: Wire 初始化协商与外部工具调用
Expand Down
1 change: 1 addition & 0 deletions src/kimi_cli/acp/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
- When the client advertises `fs.readTextFile` / `fs.writeTextFile`, `ACPKaos` routes
reads and writes through ACP `fs/*` methods.
- `ReadFile` uses `KaosPath.read_lines`, which `ACPKaos` implements via ACP reads.
- `ReadMediaFile` uses `KaosPath.read_bytes` to load image/video payloads through ACP reads.
- `WriteFile` uses `KaosPath.read_text/write_text/append_text` and still generates diffs
and approvals in the tool layer.

Expand Down
1 change: 1 addition & 0 deletions src/kimi_cli/agents/default/agent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ agent:
- "kimi_cli.tools.todo:SetTodoList"
- "kimi_cli.tools.shell:Shell"
- "kimi_cli.tools.file:ReadFile"
- "kimi_cli.tools.file:ReadMediaFile"
- "kimi_cli.tools.file:Glob"
- "kimi_cli.tools.file:Grep"
- "kimi_cli.tools.file:WriteFile"
Expand Down
1 change: 1 addition & 0 deletions src/kimi_cli/agents/okabe/agent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ agent:
- "kimi_cli.tools.todo:SetTodoList"
- "kimi_cli.tools.shell:Shell"
- "kimi_cli.tools.file:ReadFile"
- "kimi_cli.tools.file:ReadMediaFile"
- "kimi_cli.tools.file:Glob"
- "kimi_cli.tools.file:Grep"
- "kimi_cli.tools.file:WriteFile"
Expand Down
4 changes: 4 additions & 0 deletions src/kimi_cli/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ def extract_key_argument(json_content: str | streamingjson.Lexer, tool_name: str
if not isinstance(curr_args, dict) or not curr_args.get("path"):
return None
key_argument = _normalize_path(str(curr_args["path"]))
case "ReadMediaFile":
if not isinstance(curr_args, dict) or not curr_args.get("path"):
return None
key_argument = _normalize_path(str(curr_args["path"]))
case "Glob":
if not isinstance(curr_args, dict) or not curr_args.get("pattern"):
return None
Expand Down
2 changes: 2 additions & 0 deletions src/kimi_cli/tools/file/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ class FileActions(str, Enum):
from .glob import Glob # noqa: E402
from .grep_local import Grep # noqa: E402
from .read import ReadFile # noqa: E402
from .read_media import ReadMediaFile # noqa: E402
from .replace import StrReplaceFile # noqa: E402
from .write import WriteFile # noqa: E402

__all__ = (
"ReadFile",
"ReadMediaFile",
"Glob",
"Grep",
"WriteFile",
Expand Down
30 changes: 6 additions & 24 deletions src/kimi_cli/tools/file/read.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
Read content from a file.
Read text content from a file.

**Tips:**
- Make sure you follow the description of each tool parameter.
- A `<system>` tag will be given before the read file content.
- The system will notify you when there is anything wrong when reading the file.
- This tool is a tool that you typically want to use in parallel. Always read multiple files in one response when possible.
- This tool can only read text, image and video files. To list directories, you must use the Glob tool or `ls` command via the Shell tool. To read other file types, use appropriate commands via the Shell tool.
- This tool can only read text files. To read images or videos, use other appropriate tools. To list directories, use the Glob tool or `ls` command via the Shell tool. To read other file types, use appropriate commands via the Shell tool.
- If the file doesn't exist or path is invalid, an error will be returned.
- If you want to search for a certain content/pattern, prefer Grep tool over ReadFile.
- For text files:
- Content will be returned with a line number before each line like `cat -n` format.
- Use `line_offset` and `n_lines` parameters when you only need to read a part of the file.
- The maximum number of lines that can be read at once is ${MAX_LINES}.
- Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated, ending with "...".
{% if "image_in" in capabilities and "video_in" in capabilities %}
- For image and video files:
- Content will be returned in a form that you can view and understand. Feel confident to read image/video files with this tool.
- The maximum size that can be read is ${MAX_MEDIA_BYTES} bytes. An error will be returned if the file is larger than this limit.
{% elif "image_in" in capabilities %}
- For image files:
- Content will be returned in a form that you can view and understand. Feel confident to read image files with this tool.
- The maximum size that can be read is ${MAX_MEDIA_BYTES} bytes. An error will be returned if the file is larger than this limit.
- Other media files (e.g., video, PDFs) are not supported by this tool. Use other proper tools to process them.
{% elif "video_in" in capabilities %}
- For video files:
- Content will be returned in a form that you can view and understand. Feel confident to read video files with this tool.
- The maximum size that can be read is ${MAX_MEDIA_BYTES} bytes. An error will be returned if the file is larger than this limit.
- Other media files (e.g., image, PDFs) are not supported by this tool. Use other proper tools to process them.
{% else %}
- Media files (e.g., image, video, PDFs) are not supported by this tool. Use other proper tools to process them.
{% endif %}
- Content will be returned with a line number before each line like `cat -n` format.
- Use `line_offset` and `n_lines` parameters when you only need to read a part of the file.
- The maximum number of lines that can be read at once is ${MAX_LINES}.
- Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated, ending with "...".
63 changes: 8 additions & 55 deletions src/kimi_cli/tools/file/read.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
import base64
from pathlib import Path
from typing import override

from kaos.path import KaosPath
from kosong.chat_provider.kimi import Kimi
from kosong.tooling import CallableTool2, ToolError, ToolOk, ToolReturnValue
from pydantic import BaseModel, Field

from kimi_cli.soul.agent import Runtime
from kimi_cli.tools.file.utils import MEDIA_SNIFF_BYTES, FileType, detect_file_type
from kimi_cli.tools.file.utils import MEDIA_SNIFF_BYTES, detect_file_type
from kimi_cli.tools.utils import load_desc_jinja, truncate_line
from kimi_cli.utils.path import is_within_directory
from kimi_cli.wire.types import ImageURLPart, VideoURLPart

MAX_LINES = 1000
MAX_LINE_LENGTH = 2000
MAX_BYTES = 100 << 10 # 100KB
MAX_MEDIA_BYTES = 80 << 20 # 80MB


def _to_data_url(mime_type: str, data: bytes) -> str:
encoded = base64.b64encode(data).decode("ascii")
return f"data:{mime_type};base64,{encoded}"


class Params(BaseModel):
Expand Down Expand Up @@ -56,15 +47,12 @@ class ReadFile(CallableTool2[Params]):
params: type[Params] = Params

def __init__(self, runtime: Runtime) -> None:
capabilities = runtime.llm.capabilities if runtime.llm else set[str]()
description = load_desc_jinja(
Path(__file__).parent / "read.md",
{
"MAX_LINES": MAX_LINES,
"MAX_LINE_LENGTH": MAX_LINE_LENGTH,
"MAX_BYTES": MAX_BYTES,
"MAX_MEDIA_BYTES": MAX_MEDIA_BYTES,
"capabilities": capabilities,
},
)
super().__init__(description=description)
Expand All @@ -87,47 +75,6 @@ async def _validate_path(self, path: KaosPath) -> ToolError | None:
)
return None

async def _read_media(self, path: KaosPath, file_type: FileType) -> ToolReturnValue:
assert file_type.kind in ("image", "video")

stat = await path.stat()
size = stat.st_size
if size == 0:
return ToolError(
message=f"`{path}` is empty.",
brief="Empty file",
)
if size > MAX_MEDIA_BYTES:
return ToolError(
message=(
f"`{path}` is {size} bytes, which exceeds the max "
f"{MAX_MEDIA_BYTES} bytes for media files."
),
brief="File too large",
)

match file_type.kind:
case "image":
data = await path.read_bytes()
data_url = _to_data_url(file_type.mime_type, data)
part = ImageURLPart(image_url=ImageURLPart.ImageURL(url=data_url))
case "video":
data = await path.read_bytes()
if (llm := self._runtime.llm) and isinstance(llm.chat_provider, Kimi):
part = await llm.chat_provider.files.upload_video(
data=data,
mime_type=file_type.mime_type,
)
else:
data_url = _to_data_url(file_type.mime_type, data)
part = VideoURLPart(video_url=VideoURLPart.VideoURL(url=data_url))
return ToolOk(
output=part,
message=(
f"Loaded {file_type.kind} file `{path}` ({file_type.mime_type}, {size} bytes)."
),
)

@override
async def __call__(self, params: Params) -> ToolReturnValue:
# TODO: checks:
Expand Down Expand Up @@ -159,7 +106,13 @@ async def __call__(self, params: Params) -> ToolReturnValue:
header = await p.read_bytes(MEDIA_SNIFF_BYTES)
file_type = detect_file_type(str(p), header=header)
if file_type.kind in ("image", "video"):
return await self._read_media(p, file_type)
return ToolError(
message=(
f"`{params.path}` is a {file_type.kind} file. "
"Use other appropriate tools to read image or video files."
),
brief="Unsupported file type",
)

if file_type.kind == "unknown":
return ToolError(
Expand Down
24 changes: 24 additions & 0 deletions src/kimi_cli/tools/file/read_media.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Read media content from a file.

**Tips:**
- Make sure you follow the description of each tool parameter.
- A `<system>` tag will be given before the read file content.
- The system will notify you when there is anything wrong when reading the file.
- This tool is a tool that you typically want to use in parallel. Always read multiple files in one response when possible.
- This tool can only read image or video files. To read other types of files, use the ReadFile tool. To list directories, use the Glob tool or `ls` command via the Shell tool.
- If the file doesn't exist or path is invalid, an error will be returned.
- The maximum size that can be read is ${MAX_MEDIA_MEGABYTES}MB. An error will be returned if the file is larger than this limit.
- The media content will be returned in a form that you can directly view and understand.

**Capabilities**
{% if "image_in" in capabilities and "video_in" in capabilities %}
- This tool supports image and video files for the current model.
{% elif "image_in" in capabilities %}
- This tool supports image files for the current model.
- Video files are not supported by the current model.
{% elif "video_in" in capabilities %}
- This tool supports video files for the current model.
- Image files are not supported by the current model.
{% else %}
- The current model does not support image or video input.
{% endif %}
Loading
Loading