# @epoch-agent/plugin-file 中文文档

规范地址：https://npmcn.com/package/%40epoch-agent/plugin-file/

版本：6\.2\.0
npm 元数据更新时间：2026-11-13T08:30:05.789Z
中文译文更新时间：2026-11-13T19:50:35.556Z
软件包许可：Apache-2\.0

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40epoch-agent/plugin-file
原文对照：https://npmcn.com/package/%40epoch-agent/plugin-file/?lang=original
项目主页：https://github.com/Ddbor/epoch-agent#readme
源码仓库：https://github.com/Ddbor/epoch-agent

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：61.5 kB，5 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.0\.0
- 运行依赖：3 个
- 首次发布：2026-08-19T10:31:50.959Z
- 最新版本发布：2026-11-13T08:30:05.496Z
- 近 30 天下载量：4340（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：57（近 12 个月 57 个）

## 中文简介

epoch-agent 文件系统操作插件

## 安装

```sh
npm install @epoch-agent/plugin-file
```

也可以使用：`pnpm add @epoch-agent/plugin-file`、`yarn add @epoch-agent/plugin-file`、`bun add @epoch-agent/plugin-file`

## README

# @epoch-agent/plugin-file

文件工具插件。**8 个工具**。

-   ✅ **做**：读 / 写 / 精确编辑（单处、多处、跨文件）/ 搜索 / 列目录
-   ❌ **不做**：git 操作、跨机同步、任何引擎逻辑
-   **依赖**：`diff`。[protocol](https://npmcn.com/package/protocol) 和 [infra](https://npmcn.com/package/infra) 都是 `dependencies` （本包**没有 `peerDependencies`** —— 2026-11-03 核）

## 工具

只读五个：`file_read` `file_multi_read` `file_search` `file_list` `file_diff_preview`； 破坏性三个：`file_write` `file_patch` `file_multi_patch`。

**每个工具干什么、分页与 `output_mode` 那些边界在 [docs/TOOLS.md](https://npmcn.com/docs/TOOLS.md#%E6%96%87%E4%BB%B6)** —— 那份是工具清单的唯一真源， 这里只写这个包自己的实现取舍。

`file_search` 用 **ripgrep**，但**不要求用户自己装**：`rg` 从哪来由 infra 的 [`resolveRipgrep()`](https://npmcn.com/package/infra/src/ripgrep.ts) 三级决定 —— 系统 PATH 上有就用系统的， 没有就用随包带的 vendor 二进制，两个都没有才退回「工具在、一调才报错 + 给安装提示」 的老行为（启动诊断一条 warn，其余七个工具不受影响）。

**命中顺序由我们钉死**（`--sort path`）：rg 默认并行遍历，多文件命中按到达顺序打印， 同一条查询两次可能给出不同顺序。这不只是「看着别扭」—— 模型会把顺序变化读成结果变化， 而 `evals/` 的指纹把工具结果原文算进哈希，于是它会变成偶发误报（2026-11-13 实测 200 遍翻 1 遍，现场是一条 eval 的第 1、2 发漂了一次）。代价是 rg 退化成单线程遍历； 搜索输出本来就有 `limit` 和截断兜着。

本插件**不再导出** ripgrep 的解析与安装提示，它们搬去 infra 了（今天叫 `resolveRipgrep()` / `ripgrepInstallHint()`，见 `infra/src/ripgrep.ts`）： `file_list` 和将来的 `@` 文件补全也要用同一份解析，而 plugin 之间不许互相依赖。 这里只管**参数怎么拼**和**输出怎么收**。

只读的四个（`file_read` / `file_multi_read` / `file_search` / `file_list`）标了 `supportsParallel: true`，引擎可以并发调。⚠️ `file_diff_preview` **没有**这一格 （2026-11-03 核：`supportsParallel` 全包只有四处 `true`，它不在内）。

## 处理的几类脏输入

-   **二进制**：扩展名黑名单拦截（`.exe` / `.zip` / `.pdf` 等），不把乱码喂给模型
-   **图片**（2026-10-15）：`file_read` 读到 png / jpeg / gif / webp 时交一张**图**（`kind: 'image'` 的 artifact），不再回「二进制，无法读取」。进不进模型上下文不归这个包判 —— 模型认不认图、 字节 / 边长 / 像素上限是 core 的 `artifact-gate.ts` 那四道闸门（MCP 截图走的也是那一道）， 过不了的模型拿到的是闸门那句「为什么没给你图」。OpenAI Chat 协议的**部分**接口（openai-compatible / groq 等） 的 tool 消息收不了图，图会被挪进下一条 user 消息；deepseek / xai 自 2026-10-19 升级后的 SDK 版本起原生收图，不再挪 —— 逐家判据在 core 的 registry 的 `TOOL_RESULT_MEDIA`、 挪法在 `sdk-adapter.ts` 的 `hoistToolMedia` 上。 超过 32 MB 的「图」不读进内存。**工具说明没改**，eval 指纹不动
-   **BOM**：剥掉 Windows 编辑器留的 UTF-8 BOM，否则第一行的 patch 匹配永远不中
-   **行尾**：纯 CRLF 文件归一成 LF 去匹配、写回时整份还原；混合行尾的文件原样不动（归一再还原会改掉用户没让动的那些行）
-   **路径**：`~` 和 `~user` 展开
-   **工作区边界**：`resolveSafePath` 防路径穿越——resolve 之后用 `relative` 判， 不是 `startsWith`。判定本身是 infra 的 [`isInWorkspace()`](https://npmcn.com/package/infra/src/workspace.ts)（全仓唯一一份，权限层和 plugin-terminal 共用），这里只管 `~` 展开和抛 `PathEscapeError`。 `ctx.extraRoots`（`epoch --add-dir`）一并透传：落在额外根里的**绝对路径** 不算越界，但**相对路径仍然只按 `ctx.workDir` 解析**——否则 `src/a.ts` 会在每个 根里各找一遍，同一个相对路径有多个答案，而模型无从知道命中的是哪一个
-   **`ctx.grantedPaths`：权限层这一次放行的路径**（2026-10-08）。它和 `extraRoots` 并列当根用，但**来源完全不同**：那个是你事先 `--add-dir` 声明的地盘，这个是 权限层这次判定现算的（档位放行 / 你刚在审批框里点了「允许」）。 ⚠️ **工具不许再自己看 `ctx.permissionLevel` 判一次档位** —— 档位表只有权限层 那一张，这里多判一次的症状是「界面说放行了、工具说越界」，而那正是这一格 要修的东西。判据全文在 protocol 的 `ToolContext.grantedPaths` 上
-   **读类工具多认一格：本会话的 artifact 目录**（方案 47）。`file_read` / `file_search` / `file_list` / `file_multi_read` 走的是 `resolveReadablePath`， 它在上面那套之外再认 `artifactsDir(ctx.sessionId)`。理由是大输出落盘之后 取回提示会告诉模型「用 `file_search` 在这个路径里搜」，而那个路径在工作区外—— 不放开这一格，那句提示就是一句**必定失败**的指令。 口子收窄到三条：**只放开读**（`file_write` / `file_patch` 仍走 `resolveSafePath`， 模型没有理由改写「命令当时输出了什么」这份证据）、**只放开本会话那一个子目录** （不是 `~/.epoch/artifacts`，更不是 `~/.epoch`——后者等于把 `.env` 里的 API key 和 `sessions.db` 一起交出去）、**`sessionId` 由引擎给且 `artifactsDir()` 内部再洗一次**。 这三条各有一条否定用例钉着，见 `__tests__/artifact-readable.test.ts`

## 编辑的匹配判据：三个工具一份（2026-10-12）

`file_patch`、`file_multi_patch`、`file_diff_preview` 都走 [`edit-match.ts`](https://npmcn.com/package/%40epoch-agent/plugin-file/src/tools/edit-match.ts)：先精确子串，不中再退到「按行、忽略 行尾空白」「按行、忽略行首尾空白」（后者命中时新内容按文件的缩进平移）。**每一档都 要求唯一命中**，多处而没给 `replace_all` 就报歧义。

`file_multi_patch` 的参数是 `edits: [{ path, old_string, new_string, replace_all? }]`， 同一文件的多条按顺序作用。**先在内存里全部算完、任何一条不中就一个文件都不写**， 回执点名第几条、哪个文件；`file_diff_preview` 吃同一份参数、跑同一个计算，只是不写盘。 只改已有文件 —— 新建用 `file_write`。

这一版之前那两条路各有一种**静默写坏文件**的方式（`file_patch` 的模糊匹配把命中点 之后整个文件换掉、`file_multi_patch` 的 V4A 解析剥掉缩进且没命中也报成功），取证和 「为什么不修 V4A 而是换成结构化参数」写在两个文件的文件头。

## 四个读类工具自己收进 `ctx.outputBudget`（2026-09-08）

预算从 `ctx.outputBudget` 现拿 —— **那是引擎将要用的同一个数**，所以自己截完不会 再被引擎切一次，工具说的那句「还有多少、怎么接着读」也就是真的。 [`MAX_OUTPUT_CHARS`](https://npmcn.com/package/%40epoch-agent/plugin-file/src/utils.ts)（50\_000）退化成**兜底**，只在宿主自己拼 `ToolContext` 时用（那条路上也没有引擎在截）。

改之前是两处各量一遍、而且两个数不一样：这四个按 50\_000 截并明说截了多少， 引擎再按自己那个数切一刀 —— 模型看到的既不是全文，也不是我们说的那句话。

`file_read` 那一格更具体：它的分页单位是**行**（`offset` / `limit`），预算的单位是 **字符**。两个单位不换算的后果是那条工具描述里的假话「一次读 500 行」—— 500 行长行早就越过预算被引擎从中间切掉，而它报的 `nextOffset` 是按行算的， 模型照它翻页正好**跳过被切掉的那一段，两侧都不报错**。现在按行收进预算 （`fitLines`），报的是真正读到的那一行。

⚠️ **有一档翻不动，而它是有意留着的**（2026-10-21 修）：第一行自己就长过整页预算 （minified 产物 / 单行 JSON / 导出的 CSV）时，这一页交的是**截断的那一行**并明说 「只给了前 N 字符」。**余下部分没有下一页可翻** —— `offset` 是按行走的，一行之内 不存在下一个偏移量。工具会直接指去 `run_code` 按字符切，而不是给一个翻回原地 的 `offset`（那样模型会原地打转，而两侧都不报错）。`file_multi_read` 也截在同一个 字符预算上，它腾不出第二个偏移量，所以那一档只有 `run_code` 答得了。

## `file_diff_preview` 的 operation 是故意填错的

它的真实语义是只读（只算 diff 不落盘），`annotations` 照实写。但 `operation` 仍填 `file_write`：老白名单把它归在 `FILE_WRITE_TOOLS` 里，只改一边会让 `recordApproval` 那条路径与这里分叉。往严的方向偏，代价只是多一次确认。

## 开发

```bash
npx vitest run --project=plugin-file
```

`path-boundary.test.ts` 记的是具体的穿越手法，改 `resolveSafePath` 之前先读它。

## 常见问题

### @epoch-agent/plugin-file 是什么？

epoch-agent 文件系统操作插件

### 如何安装 @epoch-agent/plugin-file？

在项目目录运行 npm install @epoch-agent/plugin-file，或使用 pnpm add / yarn add / bun add。

### @epoch-agent/plugin-file 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @epoch-agent/plugin-file 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @epoch-agent/plugin-file 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @epoch-agent/plugin-file 可以商用吗？

@epoch-agent/plugin-file 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @epoch-agent/plugin-file 的下载量有多少？

根据 npm 官方下载统计，@epoch-agent/plugin-file 在 2026/10/14 – 2026/11/12 这 30 天内被下载 4,340 次，日均约 145 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @epoch-agent/plugin-file 还在维护吗？

最新版本 6\.2\.0 发布于 2026/11/13（今天）；至今共发布 57 个版本，其中过去 12 个月发布了 57 个；首次发布于 2026/08/19。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

