# Translator

[English](README.md) | 简体中文

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE)
[![npm](https://img.shields.io/npm/v/@orbit-labs-ai/translator?logo=npm&label=npm)](https://www.npmjs.com/package/@orbit-labs-ai/translator)
[![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.19-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
[![Integrations](https://img.shields.io/badge/integrations-Pi%20%7C%20Claude%20Code-8A2BE2)](#环境要求)

**用中文写提示词，向模型发送英文；需要时，再把回答翻译成中文阅读。**

Translator 是面向 **Pi** 编程 agent 和 **Claude Code** 的翻译层。提交含中文正文的提示词时，它会通过一个独立、隔离的请求把提示词译成英文，再提交英文版本。开启回答翻译后，它还会把每个最终回答译成中文，显示在原回答之后。模型会收到译后的提示词；原始提示词和中文回答译文仅供显示，不进入模型上下文。

它是 [Orbit Tools](../../README.zh-CN.md) 的一部分。

## 目录

- [功能介绍](#功能介绍)
- [环境要求](#环境要求)
- [快速开始](#快速开始)
- [使用方法](#使用方法)
- [翻译机制](#翻译机制)
- [配置](#配置)
- [数据与隐私](#数据与隐私)
- [安装详解](#安装详解)
- [命令行工具与其他宿主](#命令行工具与其他宿主)
- [故障排查](#故障排查)
- [已知限制](#已知限制)
- [卸载](#卸载)
- [开发与测试](#开发与测试)
- [延伸阅读](#延伸阅读)

## 功能介绍

例如，输入下面第一行（要求 agent 检查 `src/app.ts` 中的空指针问题，并且不要改动测试）时，模型收到的内容大致如第二行：

```text
你输入：        请检查 `src/app.ts` 中的空指针问题，不要修改测试。
模型收到：      Please check the null-pointer issue in `src/app.ts`; do not modify the tests.
```

**翻译**

- **中文输入，英文发送（Input）。** 含中文正文的提示词由一个单独的请求翻译，该请求只能看到这条提示词，随后发送的是译文。预检先排除确定的字面量和名称；若不再剩余中文正文（包括纯字面量输入），提示词直接放行，不调用翻译模型，也不产生翻译记录。引号内的中文正文仍可翻译。
- **以中文阅读回答（Output）。** 可选：把最终回答译成中文，仅供显示。Claude Code 还会翻译等待选择框旁的回答，不必等你做出选择。已有中文段落和字面量受到保护；其余全为中文的回答前若只有简短英文引言，则跳过。实质性英文仍翻译，普通的 `OK` 也仍翻译。
- **子 agent 任务与报告。** Pi 的单任务 / 并行 / 链式任务，以及 Claude Code 的 agent 启动和可明确识别的父到子后续任务，均支持输入翻译。已完成的子 agent 报告支持仅供显示的翻译；父模型可见的工具结果和链式传递保持原样。见[子 agent 与选择框](#子-agent-与选择框)。
- **语言要求（Instruction）。** 可选：在译文后附加要求，让模型用英文推理、用简体中文作答。
- **忠实、最小改动。** 翻译器只修正明显的错别字和语法错误，不改写、不作答、不总结、不扩写。
- **复用。** 再次提交完全相同的提示词时，直接复用之前的译文，不调用模型。

**精准**

- **共享预检遮蔽。** 两个方向都由同一个扫描器在翻译前识别确定的代码、公式、URL、邮箱、文件名（包括未加引号的中文文件名）、路径、类字面量标识符 / 设置 / 提及，以及 Markdown 链接目标。这些区间被遮蔽后逐字节恢复；有歧义的正文仍交给翻译器。
- **结构安全，而非语言审核。** 回复必须完成且非空，每个占位符必须恰好归还一次，受保护的输入代码块保持顺序。每次新执行的输入翻译或每个输出分段先进行一次 completion；API / 传输失败、空回复、不完整回复或请求超时最多自动重试一次。明确取消、本地配置 / 存储错误和字面量损坏不触发重试；不做 AI 审核或修复调用。翻译结果不会再次接受语言或语义质量检查。
- **请求精简。** 占位符很短，代码也不会发给翻译器，翻译请求因此保持低成本。

**安全**

- **失败时保守处理。** 无法翻译的提示词不会被发送：既不发原文，也不发不完整的译文。回答翻译失败时，原回答保持不变。
- **上下文互不混淆。** 原始提示词和回答译文是仅供显示的副本。模型上下文中保留实际提交的提示词和原回答，不包含这些显示记录。
- **随时取消。** Pi：Ctrl+Alt+C；Claude Code：翻译进行中点击 **Cancel translation** 按钮。取消不会发送任何内容，也不会改变任何设置。打断 agent 会停止提示词的翻译，但从不停止回答的翻译。

**设置**

- **一个命令。** `/translator` 打开原生设置面板，包含 Input、Instruction、Output、Model 和 Thinking；`/translator output on` 等子命令可直接设置每一项。在 Claude Code 中，它像你自己的 skill 一样列出，在桌面应用中会话开始之前的菜单里也有。
- **只作用于翻译器的模型与思考档位。** 翻译默认跟随会话模型，并使用自己的思考档位（`medium`）；也可以固定其他模型。两者都不影响主对话。
- **各宿主独立的默认值。** `/translator` 修改当前 Pi 分支或 Claude Code 会话。`translator --host pi …` 与 `translator --host claude …` 分别持久保存新会话的五项默认值，包括 Pi `/new` 或 Claude `/clear` 后的会话。
- **统一名称。** 两个 agent 使用相同的名称：Input、Instruction 和 Output。

**显示与记录**

- **原文与译文同屏，各显示一次。** 提示词显示实际发送的英文，回答保持英文；Translator 只补上中文：每条翻译后的提示词配上你的原文，每个回答配上中文译文。Pi 把它们显示在按 Markdown 渲染的卡片中。Claude Code 把它们绘制在对应行下方，在终端和它的桌面应用中都一样（包括它工作期间你发送的提示词）；恢复会话，或以新的 session ID 继续会话（分支、分叉、移到后台）时，会重新绘制。
- **安静的状态显示。** Claude Code 在输入框下方提示行的末尾显示简短状态；Pi 在编辑器下方显示开关栏。
- **历史记录保留你的原话。** 两个 agent 的提示词历史（Up / Down）保存你键入的中文，模型读到的则是英文。
- **本地记录。** 原文、译文和实际发送的文本以纯文本保存在本机，输入失败时也会留下诊断信息。仅内存模式不在磁盘上保留任何内容。

**宿主**

- **Pi 与 Claude Code。** 两者共用同一套翻译核心、字面量扫描器、输入缓存和名称，各自使用自己的模型。Claude Code 不需要 Pi。
- **agent 运行期间的更新。** Translator 的文件变化时，Claude Code 会自动重新加载它并继续翻译，即使重新加载发生在轮次进行中。正在运行的 Pi 会一直使用已加载的版本，直到执行 `/reload`。
- **其他宿主。** 独立的命令行工具可以在你把文本交给任何 agent 之前完成翻译。

各版本的变化见[更新日志](CHANGELOG.zh-CN.md)。

## 环境要求

- **Node.js 22.19 或更高版本**，并可在 `PATH` 中以 `node` 调用。本工具没有 npm 依赖。
- 至少一个宿主：**Pi 1.0.4 或更高版本**，或 **Claude Code 2.1.292 或更高版本**；这些仍是安装器对普通翻译的最低要求。**Pi 子 agent 支持要求 Pi 1.3.0 的公开工具契约。** **0.7.2** 于 **2026-10-28** 使用隔离的本地模型 fixture，在**未经修改的官方 Pi 1.4.0** 和 Claude Code CLI **2.1.311** 上验证。无需修改 Pi 核心或迁移配置。详见 [0.7.2 验证记录](docs/testing.md#verification-record-for-072)（英文），包含辅助用量收据及 Pi 原生对话框栈回归。Claude 使用官方 Mods API；远程渲染测试不等于人工 GUI 验收。
- **历史宿主。** [0.7.0 记录](docs/testing.md#verification-record-for-070)（英文）保留完整的 Pi **1.3.0**、CLI **2.1.302** 和桌面引擎 **2.1.299** 结果（应用 **2.42304.0**）。[0.7.1 记录](docs/testing.md#verification-record-for-071)（英文）保留其 Pi 1.3.0 针对性 UI 重跑结果。0.7.2 未重新测试 Pi 1.3.0、更早最低版本或桌面引擎；辅助用量仅在 Pi 1.4.0 上验证。
- **macOS 或 Linux。** 已在 macOS 上实际使用；不支持 Windows，安装器会拒绝在 Windows 上运行。不需要 `sudo`。
- agent 中已登录可用的模型。翻译使用 agent 自己的模型，因此走你平时的账号；安装器本身从不调用模型，也不接触凭据。

## 快速开始

**0.7.2 新增 Pi 辅助用量报告，不改变原生合计或你的偏好。** GitHub 与 npm 发布是独立步骤；本地验证不代表任一渠道已经可用。发布后，请使用下面的精确 npm 版本，或按[从早期版本升级](#从早期版本升级)使用对应的 GitHub 打包文件。不指定版本的 npm 安装可能选择更早版本。

1. npm 上的 0.7.2 可用后，安装本工具并运行它的安装器：

   ```sh
   npm install -g --ignore-scripts @orbit-labs-ai/translator@0.7.2
   translator install
   ```

   或者克隆仓库，在克隆目录中运行安装器：

   ```sh
   git clone https://github.com/Orbit-Labs-AI/Orbit-Tools.git "Orbit Tools"
   cd "Orbit Tools/Hooks/translator"
   ./install.sh
   ```

   安装器会查找本机的 Pi 和 Claude Code：只装了一个时直接配置它，两个都装了时询问使用哪个。`translator` 与 `./install.sh` 接受相同的选项，后者也可以在任意目录用完整路径运行；各选项见[安装详解](#安装详解)。请保留已安装的包或克隆下来的目录：agent 从这里加载本工具。

2. 重启 agent（Pi 中也可执行 `/reload`），然后运行 `/translator status` 确认工具已加载。在任一 agent 的终端 UI 中，不带参数的 `/translator` 都会打开其原生设置面板。

3. 用中文输入提示词。输入翻译默认开启。

4. 可按需为当前会话开启另外两项功能：

   ```text
   /translator output on         # 为每个最终回答显示中文译文
   /translator instruction on    # 要求模型用英文推理、用中文作答
   ```

要修改新会话的默认值，使用 [`translator`](#新会话的默认设置)。它安装在 `~/.local/bin` 中，该目录须在 `PATH` 里；安装器不会修改你的 shell 启动文件。

## 使用方法

### 命令与快捷键

**`/translator` 是本工具唯一的斜杠命令。** 不带参数时打开原生设置面板，包含五项：**Input**（输入）、**Instruction**（语言要求）、**Output**（输出）、**Model**（翻译模型）和 **Thinking**（思考档位）。修改设置不调用模型，也从不改变主对话的模型或思考档位。

| 操作 | 命令（Pi 与 Claude Code 通用） | Pi 快捷键 | Claude Code 输入框上方按钮 |
|---|---|---|---|
| 打开设置 | `/translator` | — | — |
| 显示全部设置 / 帮助 | `/translator status` / `/translator help` | — | — |
| **Input**：把提示词译为英文 | `/translator input [on\|off\|toggle\|status]` | Ctrl+Alt+T | Input |
| **Instruction**：附加“英文推理、中文回答”要求 | `/translator instruction [on\|off\|toggle\|status]` | Ctrl+Alt+L | Instruction |
| **Output**：以中文显示回答 | `/translator output [on\|off\|toggle\|status]` | Ctrl+Alt+Y | Output |
| **Model**（翻译模型） | `/translator model [follow\|MODEL]` | — | — |
| **Thinking**（思考档位） | `/translator thinking [low\|medium\|high\|xhigh\|max]` | — | — |

- 两个 agent 使用同一套名称，统一定义在 `shared/features.mjs` 中：开关显示为 `Input: ON`、`Instruction: OFF`、`Output: OFF`，完整状态行显示为 `Translator: Input ON · Instruction OFF · Output OFF`，通知以 `Translator · Input` 或 `Translator · Output` 开头。Claude Code 的提示行显示简短形式，例如 `Translator · Input + Output`。
- `input`、`instruction`、`output` 不带值时切换对应开关；其 `status` 操作只读。`model`、`thinking` 不带值时只报告设置。无效参数不会改变设置。
- **Pi** 的 `MODEL` 是注册表中的 `provider/model` ID；紧凑面板包含 ON/OFF 标记、同一行内的模型 / 思考档位反馈，以及可搜索的模型选择器。在设置列表中按 `?` 查看当前选项的帮助，再按一次即可回到仅显示控件的视图。**Claude Code 原生后端**接受 `sonnet` 等别名或完整模型 ID；面板提供常用别名及 ID 输入框。Claude 的可选 Pi 后端则使用 `provider/model`。Claude 后端的模型可用性和允许列表在 completion 时检查，不会为了保存设置而发起测试模型调用。
- `model follow` 显式覆盖配置文件中的模型固定值。它跟随 Pi 当前会话模型或 Claude 原生当前会话模型；Claude 的 Pi 后端则跟随 **Pi 配置的默认模型**，而不是 Claude 模型。思考档位独立，默认 `medium`；`follow` 不会采用主对话的思考档位。Pi 同时显示请求档位和实际生效档位，包括按模型能力限制或不支持推理的情况。
- 五项设置均作用于当前范围：Pi 按**对话分支**保存覆盖值，Claude 按 **session ID** 保存。它们优先于文件默认值，不修改共享配置。新会话使用文件默认值；持久化设置在恢复会话时还原，Pi 树导航会恢复活动分支的值。不持久化模式只在内存中保存。修改影响后续翻译，不改变正在进行的请求所持有的设置快照。
- 原有开关栏与面板、命令和快捷键保持同步。Pi fullscreen 模式下可点击输入框下方的 **Input**、**Instruction**、**Output**。按钮栏支持换行，不修改草稿或抢占焦点；regular 模式保留键盘控制。RPC / JSON / print 不创建终端控件或面板；无参数命令改为报告状态和帮助。全局默认值在输入 / agent 边界或重载时刷新；`TRANSLATOR_DISABLE=1` 会把输入显示为锁定关闭。
- 在 Claude Code 中，输入框上方的 **Input**、**Instruction** 和 **Output** 按钮没有自己的按键：在此版本之前，在空输入框中键入数字，稍停后就会按下对应按钮。请点击按钮，或按 **Ctrl+X，再按 Tab** 移到按钮行后按 Enter；已打开的设置面板保留按键 **1**–**3**。不覆盖全局 Ctrl 快捷键或已有绑定。按钮行或设置面板不可用时，使用直接子命令。Claude 按会话串行处理重叠的面板、按钮和命令更新，包括仅内存模式。

### Pi 辅助用量

**`/translator usage`（仅 Pi）**按输入、输出和子代理翻译分别显示 Translator 上报的 token 与费用，独立于 Pi 原生合计。每次实际隔离 completion 尝试都有自己的数值收据，包括重试、拒绝、验证失败、长回答分段、任务预览和子代理报告预览。跳过和缓存命中不新增尝试或费用。这些是提供方 / SDK 上报的值，不是账单审计或估算；未上报用量表示未知，不代表免费。命令还显示待完成 / 已取消请求及持久化警告，不调用模型。

收据是不进入上下文的 `orbit.translator.usage.v1` custom entry，只包含唯一请求 ID、提供方 / 模型、数值 Pi 用量（或 `null`）、结果状态和方向，以及 `nativeAccounting: false`。不包含提示词、回答或错误原文。合计在**整个会话**中按请求 ID 去重，包含已放弃的分支；不统计旧翻译卡片中的汇总用量。主模型合计、工具结果和上下文保持不变。Claude Code 与独立 CLI 不输出此 Pi 契约。

**持久化限制：**收据只使用 Pi 的公开会话条目 API，不新增旁路文件。在 Pi 1.4.0 中，首次对话消息保存之前（包括首次输入被拦截的情况），收据仅在内存中，退出或替换会话时可能丢失；之后的对话消息会将缓冲条目写入磁盘。`--no-session` 始终仅使用内存。收据写入失败时会警告，并尽力保留内存合计，不重试模型调用。取消 / 超时仍可能返回数值用量；尚无回复时，命令将其显示为待完成。导航、重载或替换会话会在旧分支上将未完成尝试关闭为 `interrupted`、`usage: null`；迟到回复不能覆盖这些收据，也不能写入新归属。直接 dispose 或进程终止可能丢失未完成的用量记录。详见 [Pi 收据契约与限制](pi/README.zh-CN.md#辅助用量统计)。

### 取消待处理的翻译

取消通过快捷键或按钮完成，**不是斜杠命令，也没有 `cancel` 子命令**。取消不会关闭任何设置：取消输入时既不提交原文，也不提交译文；取消输出时保留已完成的原回答。后续翻译仍按你的设置启用。

- **Pi：** **Ctrl+Alt+C** 取消待处理的**输入和输出**翻译，包括两者同时进行或设置面板获得焦点时。Esc 或对本次运行的其他 abort 会结束 Pi 对回答翻译的等待，但不会结束翻译本身，其卡片随后出现；运行停止时，仍在为 Pi 队列翻译的提示词会回到编辑器中。idle 输入预处理期间的普通 SDK / RPC `abort()` 不是可靠的取消方式。树导航、重载和会话替换会使待处理结果失效。直接调用 SDK `dispose()` 会阻止迟到写入，但请求可能持续到完成或超时。
- **Claude Code：** 翻译进行期间，输入框上方按钮行和已打开的设置面板中会显示 **Cancel translation** 按钮（设置面板中按键为 **4**）。点击它，或按 **Ctrl+X，再按 Tab** 移到按钮行。取消会传递至原生 completion 和 worker，包括其可选的 Pi 子进程。完整结果开始发布后，按钮即消失：取消不撤销已完成的翻译或不可逆的宿主记录 / 显示写入。打断 Claude（Esc，或桌面应用的 Stop）会停止提示词的翻译，就像它停止该提示词本身的提交一样；Claude 工作期间你输入的提示词会留在队列中，发送时再翻译。打断和切换到另一个会话都不会停止回答的翻译，后者在本轮结束后才运行。见 [Claude 适配器](claude-code/README.zh-CN.md#取消翻译)。

### 你会看到什么

- **你的提示词。** 英文提示词与你的中文原文一起显示，原文标注 `Translator · Input · original · display only`。Pi 只在译后用户行真正送达后追加原文卡片，不在翻译完成或入队时提前显示。Claude Code 保持原有的英文行下方同步原文展示，并保留仅供显示的 transcript 通知（Ctrl+O）。
- **agent 工作期间发送的提示词。** 提示词送达后才显示其卡片或绘制，中间的工具调用因此不会把它埋掉：在 Claude Code 中，Claude 一读到它就绘制在对应行下方，无论是 Ctrl+X Ctrl+S（或 Ctrl+Enter）送入当前轮次，还是 Enter 排到下一轮；在 Pi 中，steering 和 follow-up 都在其用户行之后显示，在此之前原文显示在编辑器上方。日志和已开启的输入缓存仍在提交前写入，不随展示一起延后。
- **回答译文。** 中文紧接着显示在英文回答下方，标注 `Translator · Output · display only`：Claude Code 把它绘制在回答行下方，在终端和它的桌面应用中都一样，Pi 把它显示在回答之后的卡片中。两种情况下模型都不会读取译文。Claude Code 在本轮结束后才翻译回答，你可以立即继续；中文译文准备好就会绘制，即使你打断了或新的一轮已经开始。Pi 会在结束本轮前等待译文，但只等到你打断或发送下一条提示词为止；随后卡片才出现，如果它落在后来的行之后，会注明它属于哪个回答。
- **Pi 卡片中的 Markdown。** 卡片中的链接、路径、行内代码、代码块和列表，用 Pi 自己的 Markdown 渲染器和主题绘制。按 Ctrl+O 展开卡片，可查看翻译模型和追踪信息。
- **重启之后。** 恢复会话时，Claude Code 会根据本地 journal 中保存的关联重新绘制原文和中文回答；对话以新的 session ID 继续时也会重新绘制：`/branch`、`--fork-session`、桌面应用的 Rewind 或 Fork，或终端中的 `← for agents`。它所继续的会话的 Translator 设置也会随之带过来。Pi 把两者保存在自己的会话文件中。
- **失败。** 输入翻译失败时，提示词被拦下，并立即显示原因和原文，不等待永远不会出现的用户行。成功送达后的展示失败不能撤销提交；Pi 改为报告 `Prompt sent; original display unavailable`。回答翻译失败时，会显示一条简短警告，回答本身保持不变。

### 提示词历史保留你的中文

**Claude Code 自己的提示词历史保存你键入的内容。** Translator 翻译你的提示词后，让你键入的文本进入，只在保存和发送前把存储的提示词行改写成英文。模型和 transcript 读到的是英文，而 Up / Down 和 Ctrl+R 会立即显示你的中文，不会先显示英文；重启之后、关闭缓存以及仅内存模式下也是如此。早期版本以英文保存在其中的条目，会在 Claude Code 下次启动时换回你的原文：如果某一行的文本恰好是同一会话在其前后十分钟内翻译的某条提示词的英文，这一行就换成该提示词的原文；其他行逐字节保持原样。Translator 只在持有 Claude Code 写入该文件时所用的锁、并且期间文件没有新增内容时才替换文件；此后每次按 Up 都显示你的中文。当 Claude Code 把已发送提示词的英文放回输入框时（发送后立即按 Esc，或回退），Translator 会在输入框从空变为有草稿时，用一次读取和一次填充换入已知原文；它从不改写你键入的文本。Claude Code 放回文本前不会先通知 mod，因此英文仍可能显示一帧。Claude 工作期间发送、附带图片的提示词会以英文保存：Claude Code 不保留对这种行的改写，因此由 Translator 直接发出英文；从空输入框按 Up 时会映射回原文，下次启动时也会在文件中恢复。

**原样发送**调回的提示词，**会重放已保存的译文**，应用当前语言要求设置，不再调用翻译模型；即使关闭自动缓存或记录使用旧缓存契约也是如此，只要保存的译文仍保留原文字面量。编辑过或重新键入的提示词走正常翻译流程，可能由缓存提供译文。关闭 Input 时按键入内容原样发送。Translator 知道本会话提示词的原文，也知道你在任何会话中最近发送的提示词的原文（输入缓存中最新的记录），因此文件中尚未恢复的英文条目也会被替换。

**Pi** 的编辑器历史同样保存键入的提示词。当它从存储的行（其中是英文）重新填充历史时（启动时、`pi -c`、`/tree` 之后以及切换会话时），Translator 会把每条译后的行映射回原文。`/tree` 或 `/fork` 把译过的提示词放回编辑器时，放回的也是你的原文。

**Claude Code 桌面应用**从它自己的对话消息列表调回提示词，mod 无法触及这个列表：当前会话的提示词和并入进行中轮次的提示词会调回你的中文；但应用重新加载对话后，Claude 空闲时你发送的提示词会调回英文。

### 新会话的默认设置

安装后的 `translator` shell 命令修改 `config.json` 中的默认值，不调用模型。指定宿主即可避免影响另一个 agent：

```sh
translator --host pi status
translator --host pi input on
translator --host pi instruction on
translator --host pi output off
translator --host pi model provider/model-id
translator --host pi thinking low
translator --host claude input on
translator --host claude instruction off
translator --host claude output on
translator --host claude model sonnet
translator --host claude thinking medium
translator --host claude cache off
translator --host pi model follow       # 即使共同默认值固定了模型，也显式跟随
translator --host pi model inherit      # 移除宿主固定值，继承共同默认值
translator --host claude reset output   # 恢复单个字段的继承
translator --host claude reset all      # 全部已知字段，包括后端与超时
translator                             # 显示共同默认值
translator output off                  # 原有的共同默认值命令
```

每个宿主都支持 `input|instruction|output|cache [on|off|toggle|status|inherit]`、`model [follow|MODEL|status|inherit]` 和 `thinking [low|medium|high|xhigh|max|status|inherit]`。Pi 模型使用 `provider/model`；原生 Claude 接受别名 / 完整 ID，Claude 的 Pi 后端则使用 `provider/model`。不指定宿主的 `model` 修改共同的 Pi 后端固定值。`reset` 接受 `all`、五项设置或 `cache`；`reset all` 保留未知字段。

宿主默认值是 `hosts.pi` / `hosts.claude` 下的局部覆盖；不带宿主的命令仍修改共同默认值。每次调用在规范化文件锁下只修改指定字段，保留符号链接、权限、其他宿主和未知字段。已有分支 / 会话覆盖仍优先，恢复会话时会恢复这些覆盖。真正的新会话、Pi `/new` 或 Claude `/clear` 从各自宿主默认值开始。斜杠命令仍只修改会话。外部写入者也必须遵守锁协议。

### 子 agent 与选择框

- **中文任务预览（Input）。** 含中文正文的任务先译成英文再交付；英文任务逐字节原样交付，不附加语言要求。两者都提供中文任务预览：保留已有中文，将英文正文译成仅供显示的中文，即使 Output 关闭也生效。混合任务保留原有中文段落。这可能增加一次独立的显示翻译调用；纯字面量任务仍跳过。回答适用的“简短英文引言”豁免不适用于任务指令。显示翻译失败会警告并保留有效英文任务；取消仍阻止尚未派发的工作。你自己的提示词历史始终保留实际输入的原文，不分中英文；生成的任务与预览绝不会成为用户历史回填条目。
- **Pi 1.3.0 / 1.4.0：** 父代理在执行前翻译受支持的 `subagent` 工具中的 `task`、每个 `tasks[].task` 或每个 `chain[].task`。中文转英文失败、记录失败或准备被取消，都会拦下整批。agent 名称、目录、信任标志和 `{previous}` 运算符保持原样；链式任务接收原始上一步结果。成功完成的子代理回答显示为带标签的卡片。Input 开启时，未知的自定义结构、无效批次，以及不符合 1–8 项限制的并行批次，会在任何预览模型调用前被说明原因后拦下，不猜测处理。通过显式的任务哈希交接协调匹配的直接 JSON 子进程，不抑制无关子会话，也不重复翻译链式原始结果。Input 开启时，链式任务必须在 `{previous}` 之外包含固定的非空白任务文字；只有占位符的任务会被拦下，不作为归属通配。见 [Pi 支持的契约](pi/README.zh-CN.md#子代理)。
- **Claude Code：** `agent.spawn` 处理前台 / 后台 / 嵌套 agent 和队友；`session.send` 处理可明确识别的父到子后续任务，包括恢复的子代理。已完成且未中止的报告可在子代理行、匹配的 Agent 结果或后台通知上显示译文。其他 peer 消息及子到父交付保持不变。不支持改写的工作流提示词失败时拦截；不提供本地完成事件的远程子代理无法显示本地译文。见 [Claude 任务契约](claude-code/README.zh-CN.md#子-agent-输入与仅供显示的输出)。
- **选择框：** 只调用 `AskUserQuestion` / `ExitPlanMode` 的回答在等待选择时翻译。问题文字 / 选项通过官方对话框渲染契约，在原生问题上方附加译文。审批控件、选项值和用户答案绝不改写。普通操作工具过程文本仍跳过。
- **中断与显示：** Cancel 停止翻译，不停止已经派发的子代理。失败保留原回答 / 结果。并行身份、重复事件和重载会去重；已完成的任务 / 选择框绘制可从日志恢复。Pi 导航 / 重载使待处理任务失效；Claude 已完成回答的翻译可跨越轮次或会话切换。退出仍会结束未完成任务。工具结果从不被替换，显示译文也不进入父 / 子模型上下文。

## 翻译机制

```text
你的提示词
  |-- 输入翻译关闭 --> 原样发送
  `-- 共享预检扫描器：识别确定的字面量，检查剩余正文
       |-- 没有中文正文 / 纯字面量 --> 原样通过，不调用翻译模型、不写翻译记录
       `-- 中文正文
            |-- 原文精确匹配 + 当前扫描器 / 提示词契约缓存 --> 字面量完整性检查后复用
            `-- 遮蔽已识别字面量 --> 隔离 completion（最多一次 API 重试）
                   --> 回复完成且非空 + 占位符次数 / 代码块顺序安全
                         |-- 失败 --> 拦下提示词（不调用修复）
                         `-- 恢复字面量 --> 记录 + 可选的语言附加要求
                                              |
                                译文发送给模型；原文显示在旁边，
                                仅供显示

模型的最终回答 ---------------------------------------> 保持不变
  |-- 输出翻译关闭 --> 不翻译
  `-- 共享预检扫描器
       |-- 没有英文正文 / 纯字面量 --> 不调用翻译模型、不写翻译记录
       `-- 英文正文 --> 遮蔽字面量 --> 每部分 completion（最多一次 API 重试）+ 回复 / 字面量安全
                                       --> 恢复后的译文仅显示并保存在本机
```

**输入翻译失败会拦下提示词**：本工具绝不会退而发送中文原文。确实要发送原文时，先执行 `/translator input off`。**回答翻译失败会保留原回答**：不改写任何内容，也不让模型重新回答。

### 本地语言判断

语言判断**只在翻译之前**进行，在本地完成，早于翻译调用和记录。两个方向都使用同一个预检扫描器，识别确定的字面量区间并检查剩余正文。如果需要翻译，这些区间会在请求前被遮蔽；文件名和类字面量名称不再只是被语言判断忽略，却仍原样暴露给模型。

- **普通提示词输入**：不再剩余中文正文时原样发送。`Read 报告.md` 不调用翻译模型，单独的 `报告.md` 现在也跳过。引号内的中文正文仍可翻译：`Change the title to “你好”` 会进入翻译路径，模型可以保留精确的引号内字面值。仅有引号并不能把任意正文变成受保护的字面量。
- **输出有意优先避免重复翻译。** 在保守扫描器之外，还遮蔽紧贴中文的 ASCII 文件名 / 路径（`已保存README.md文件`）、上下文中的缩写 / 标识符，以及精确标签 `Markdown`、`Mermaid`、`Downloads`、`Token`、`Tokens`。直接夹在汉字正文之间、允许空格的单个首字母大写名称（`已更新 Orbit 的文档`）视作名称。这些都是真正遮蔽的区间，而不只是语言判断例外。`已完成 API checks` 仍翻译；单独的普通 `OK`、`Thanks`、`Mermaid` 仍翻译。
- **已有中文段落不再重发。** 源文本单元只在未受保护的行 / 句边界切分。不含英文正文的单元与字面量一样遮蔽并逐字节恢复；相邻英文仍批量发送，而不是每行调用一次模型。含实质英文的混合句仍可翻译。若唯一英文是在至少 80 个未遮蔽汉字、其余全为中文的正文之前，不超过 12 个单词的引言，则整篇跳过。这项有意宽松的显示策略可能让简短英文引言原样保留。它不是“中文占多数就不翻译”：英文结尾或实质英文段落仍翻译，而中文正文不再重发。
- **纯字面量文本在两个方向都跳过**，即使完全没有另一种语言的正文。例子包括 `报告.md`、`./文档/报告.md`、`用户表_v2`、`LABEL=中文` 和 `@张三`。跳过时不调用翻译模型、不写翻译日志，也不附加语言要求。

中文输出中的已知技术缩写（如 `API`）仍受保护，但 `DO NOT DELETE BACKUPS` 之类的大写指令会保留给模型翻译。`./old.ts改为new.ts` 这种类似路径的文本也会保留中间的中文指令；如果它确实是文件名，请用反引号明确保护。

对于**输入**，有歧义的正文仍交给模型，而不是整体当作一个受保护名称：紧贴拉丁字母的中文（`请查看data.csv`、`…/docs然后`），用 `：` 或 `、` 连着名称的中文（`问题：src/app.ts`、`配置、README.md`），以中文结尾且没有文件扩展名的链接或路径（`https://example.com/文档里的第三节`、`D:\备份里面`），后面接着写成句子的提及（`@李四麻烦今天合并一下`），本地部分是中文的邮箱地址，以及只用斜杠或 `#` 连接的写法（`English/中文`、`src/组件/按钮`、`问题#3`）。拿不准时宁可翻译，也不漏翻。`./src/组件Panel.vue` 和 `C:\项目\My文档.md` 等前缀与末尾扩展名明确的路径会整体保护，包括混合文字的路径段。输出还会整体保护紧贴中文、扩展名明确的多语言相对路径，例如 `已保存src/中文Panel.vue并完成验证。`，而不再只遮蔽其中的 ASCII 尾部。不属于可识别语法、但需要精确保留的字符串（包括有歧义的名称和独立符号序列）可放进反引号以获得保护。

`请检查 报告.md` 会被翻译，文件名先被遮蔽再恢复。译稿和恢复后的文本都不会再次经过语言判断；字面量内容不能触发语言拒绝。

### 由哪个模型翻译

没有配置文件或会话固定值时，翻译使用**会话当前选择的模型**，每次请求都会重新读取，因此主模型切换时翻译模型也随之切换。翻译使用翻译器的 `thinkingLevel`（默认 `medium`），而不是会话自身的档位：会话为 `max` 时，只要未修改翻译设置，翻译仍请求 `medium`。

- **Pi** 会把请求档位限制在翻译模型支持的范围内；不支持推理的模型不会收到推理选项。面板在同一行内显示选择 / 解析后的模型，仅在思考档位存在差异时显示“请求值 → 实际值”；选中 Thinking 后按 `?` 可查看原因。`/translator status` 保留完整信息。用 `/translator model provider/model` 固定模型，或将 `provider` 和 `model` 设为文件默认值。固定模型不可用时，不会静默回退到主模型。
- **Claude Code** 默认使用自身的隔离 completion API。用 `/translator model sonnet`（或完整 ID）固定模型，或设置 `claudeModel` 文件默认值。请求的思考档位作为 effort 发送；宿主判断模型支持情况，对不接受 effort 的模型省略该值。也可设置 `claudeBackend: "pi"`，使用已安装的 Pi，并接受 `provider/model` 固定值。
- `/translator thinking high` 只改变**两个翻译方向**的思考档位。即使文件里固定了模型，`/translator model follow` 也会为当前分支 / 会话清除实际使用的翻译固定值，但不改变思考档位。这两个命令均不改变主模型、主模型的思考档位或共享配置文件。

### 轻微纠错，不改写

输入翻译可以对正文中明显的拼写、语法或输入错误做**最小纠正**。只有原文能明确确定缺失的措辞时，才会补全残缺片段；否则保留片段或歧义。

翻译不得改写、润色、扩写或猜测。原意、语气、细节、否定、约束、列表和 Markdown 都会保留。这些规则只作用于被翻译的输入：英文提示词仍原样放行，回答翻译也不受影响。它们是给模型的指令，并不保证任何特定模型的翻译质量。

### 本地保护的内容

共享扫描器的**输入**边界保持保守，避免把正文藏进猜测的名称中。输出额外采用上述宽松区间与已有中文段落保护；判断和实际请求遮蔽一致。下表描述共用的保守基础。两个方向都不是面向任意源文本的完整解析器。

| 本地遮蔽 | 例子与边界 |
|---|---|
| 代码 | 反引号和波浪线围栏（含引用块中的围栏和未闭合围栏）、反引号数量精确匹配的行内代码、`<pre>`、`<code>`、`<script>` 和 `<style>` 块。代码中的中文保持原样 |
| 数学公式 | `$…$`（单行，不包括 `$5 到 $10`）、`$$…$$`、`\(…\)`、`\[…\]` |
| URL 和邮箱 | ASCII URL（`https://…`、`www.…`、`mailto:`）和 ASCII 邮箱地址。`请阅读https://…，然后` 只遮蔽 URL；URL 直接连着中文时（如 `看看https://a.com/docs然后`）整体交给翻译器 |
| 文件名和路径 | 确定的裸文件名和相对路径（`README.md`、`报告.md`、`docs/报告.md`）、显式路径（`/`、`~/`、`./`、`../`、`C:\`、`\\server`、`@src/…`），以及引号内可含空格和中文的路径或文件名。含歧义的末尾中文仍按正文处理；文件扩展名或路径结尾分隔符提供明确边界。紧跟中文的 `/` 只有后面还有一个 `/` 时才算 ASCII 路径开头，因此 `项目/Orbit` 仍按正文处理 |
| 类字面量标识符、设置和提及 | `用户表_v2`、`LABEL=中文` 等确定名称，以及 `@张三` 等短提及；不包括任意混合正文、斜杠连接的短语，或后面接着整句的提及 |
| Markdown 链接 | 链接和图片目标、引用定义和标识符、脚注标识符以及 wikilink；链接文字和标题属于正文 |

**不作整体保护：** 有歧义的名称和路径、JSON、普通 HTML 标签和属性、任意命令行参数值、引号内正文、缩进文本，以及没有定界符的公式。其中可识别的字面量仍可能被遮蔽。翻译器收到保留精确值、翻译正文的指令，但这些指令不保证未遮蔽文本的逐字节完整性。

completion 之后，本地只检查回复和恢复过程的安全性：

- **回复完成且非空。** 不完整、为空或失败的回复会被拒绝。Claude 原生后端还要求回复包含唯一的末尾标记，才会接受。
- **每个占位符恰好归还一次。** 占位符很短（`__PT_` 加 8 位随机十六进制，保证不出现在你的文本中），以减少 token 用量。输入语序可以移动行内字面量；受保护的围栏代码块须保持相对顺序。仅供显示的回答译文可以移动任何字面量。通过这些检查后才恢复原始字节。
- **一次 API 重试，不做 AI 审核或修复。** API / 传输失败、空回复、不完整回复和已配置的请求超时，每条输入或每个输出分段最多重试一次。明确 Cancel、本地配置 / 存储错误和占位符失败不重试。最终失败时拦下输入或保留原回答。拒绝后的后备模型和重试共用最多两次尝试的预算；详见[配置](#配置)。

**翻译后没有语言或语义检查**：输入不会因为仍有中文而被拒绝，输出也不要求含有汉字。这同样适用于恢复的字面量和拼接后的长回答。字面量内容不能导致语言拒绝。通过结构检查的结果仍可能未被翻译、译错或质量不佳；Translator 不保证语义质量。

例如：

```text
请阅读https://claude.dev/blog/getting-started-with-claude-code-mods/，然后告诉我mods的工作原理
```

URL 被遮蔽；翻译器看到的是 `请阅读__PT_…_0__，然后告诉我mods的工作原理`，返回 `Please read __PT_…_0__, then tell me how mods work`。图片原样交给主模型，从不送去翻译；本工具不做 OCR，也不翻译附件内容。

Pi 和共享 worker 使用 `shared/literals.mjs`。Claude 沙箱无法导入插件外文件，因此使用 `claude-code/hooks/literals.js` 中生成的逐字节相同镜像。修改扫描器后运行 `npm run sync:shared`；`npm run check:shared` 只读检查一致性。不要维护两套算法。

### 长回答

翻译请求需要写出的内容越多，耗时越长。因此在 Pi、Claude Code（两种后端）和命令行工具中，遮蔽字面量后超过 2,000 个字符的回答会分成长度大致相等的几部分翻译，最多同时进行四部分：7,000 个字符的回答，所需时间与 2,000 个字符的回答相当。如果设置了 `timeoutMs`，它分别限制每一部分。

- 切分点位于段落之间；一个块长到一部分容纳不下时，才在块内的行与行之间切分，从不切在被遮蔽的代码内部。超过 2,000 个字符的单独一行保持完整。中文按原顺序拼接，保留原有的空白和缩进。
- 每个需要翻译的部分先进行一次 completion，API 失败最多重试一次（设置了 `timeoutMs` 时，每次尝试均有新的独立时限），要求回复完成且非空，并检查占位符的精确恢复。预检发现没有英文正文的部分（包括纯字面量部分）不发送请求，原样保留。各部分及拼接后的回答都不再检查语言，也不进行审核或修复调用。
- 任何一部分失败，其他部分随即停止，警告会指明是哪一部分，例如 `Translation timed out (part 2 of 4)`，回答保持不变。取消会停止所有部分。
- 每一部分都在看不到其他部分的情况下翻译，因此同一个术语在两部分中的译法可能不同。
- 在 Claude Code 中，被截断的部分会单独再向另一个模型系列请求一次；已翻译的部分保留。不设置 `timeoutMs` 时，请求没有自己的时限，但提供方仍会在约十分钟后结束单次回复。

### 译文复用

- 只有原文**完全相同**且扫描器 / 提示词契约一致时才复用缓存译文。空格、换行和标点都参与匹配，没有模糊匹配。
- 只缓存通过回复及字面量恢复检查的结果；不完整、取消或失败的结果从不缓存。0.7.0 的扫描器契约升级会隔离旧的带版本条目，不删除其历史。缓存条目不代表语义质量获得认可。
- Pi 与 Claude Code 共用配置文件旁的缓存。更换主模型、翻译模型、翻译思考档位或后端，不会清空或绕过缓存：相同的有效缓存输入仍可复用。要用新译文比较模型，请关闭缓存。
- 命中缓存后才附加语言要求，不会重复附加。复用的记录标记为 `reused`，不会重复计算用量。
- Pi 只复用当前活动分支的记录，不读取已放弃的分支。两个 agent 的提示词历史都保存你键入的内容，详见[提示词历史保留你的中文](#提示词历史保留你的中文)。
- 没有版本标记的旧 Pi 记录仍可导入或从当前分支作为候选。每个非空候选都必须精确保留扫描器在原文中识别出的字面量字节及出现次数；这仅检查原文字面量完整性，不重新检查缓存文本的顺序、语言或语义。修改了字面量的候选会触发重新翻译，通过检查的新结果可以替换它，但不抹去旧日志。要强制重新翻译，请关闭缓存。

## 配置

设置保存在工具目录中的 `config.json`；通过 npm 安装时，保存在 `~/.agents/translator/` 中。安装器从 `config.example.json` 生成该文件，Git 会忽略它。文件不存在时，使用以下内置默认值：

```json
{
  "enabled": true,
  "cacheEnabled": true,
  "appendLanguageInstruction": false,
  "outputEnabled": false,
  "provider": null,
  "model": null,
  "claudeBackend": "native",
  "claudeModel": null,
  "thinkingLevel": "medium",
  "timeoutMs": null
}
```

| 字段 | 类型与默认值 | 作用 |
|---|---|---|
| `enabled` | boolean，`true` | 输入翻译。关闭时原文原样发送，不附加语言要求 |
| `cacheEnabled` | boolean，`true` | 自动复用输入译文。关闭不会删除历史，也不阻止显式重放原样调回的 Claude 提示词 |
| `appendLanguageInstruction` | boolean，`false` | 为翻译或复用的输入附加语言要求 |
| `outputEnabled` | boolean，`false` | 把最终回答译成中文供显示 |
| `provider`、`model` | 非空字符串或 `null`，均为 `null` | **Pi 后端**的模型覆盖；须同时设置或同时为 `null` |
| `claudeBackend` | `"native"` 或 `"pi"`，`"native"` | Claude Code 使用自身 completion API 翻译，或改用已安装的 Pi |
| `claudeModel` | 非空字符串或 `null`，`null` | Claude Code 的翻译模型。`null` 跟随会话当前模型，包括 `/model` 切换；填写别名或完整 ID 则固定该模型，并受 Claude 模型允许列表约束 |
| `thinkingLevel` | `"low"`、`"medium"`、`"high"`、`"xhigh"` 或 `"max"`，`"medium"` | 翻译的思考档位，独立于会话自身设置：Claude Code 中为 effort，Pi 中为推理档位 |
| `hosts` | object，默认不存在 | 可选的 `pi` 和 `claude` 局部覆盖，使用上下列出的相同字段。模型 / 提供方须成对设置或重置；未知字段保留 |
| `timeoutMs` | `null` 或 `1` 到 `120000` 的整数，`null` | `null` 表示不设时限：翻译一直运行到完成或被你取消。填写数字则是每个翻译请求的时限（毫秒）；Pi 子进程后端包含启动时间；超过时限的请求以 `Translation timed out` 失败。[长回答](#长回答)的每一部分都是一个独立请求 |

例如，可在现有配置对象中加入 `"hosts": { "pi": { "appendLanguageInstruction": true, "outputEnabled": false }, "claude": { "appendLanguageInstruction": false, "outputEnabled": true, "claudeModel": "sonnet" } }`。已有平铺配置无需迁移。独立翻译 CLI 使用共同默认值，不使用任一宿主层。

设置按以下顺序生效，后者优先：内置默认值、顶层共同配置、对应的 `hosts.pi` 或 `hosts.claude` 层、分支 / 会话对三个开关**以及翻译模型和思考档位**的覆盖，最后是 `TRANSLATOR_DISABLE=1`（强制关闭输入翻译）。没有设置会话覆盖的字段保留文件默认值；显式的 `model follow` 为该范围清除文件中的模型固定值。配置文件在每次请求时读取，修改后无需重启。代码更新会自动到达 Claude Code，Pi 则在执行 `/reload` 时生效（见[移动或更新仓库副本](#移动或更新仓库副本)）；**注册方式的变化需要重启**。

Pi 的 `provider` 与 `model` 覆盖只改变 Pi 翻译所用的模型。独立命令行工具和 Claude Code 的 Pi 后端没有 Pi 会话模型，因此使用 **Pi 的全局默认模型**或文件覆盖值；Claude 的 Pi 后端还可使用会话的 `provider/model` 固定值。它们把 `thinkingLevel` 作为 `--thinking` 传入。Claude Code 原生后端忽略 Pi 模型默认值，使用实际生效的 `claudeModel`（包括会话固定值或显式 `follow`），也从不读取 Pi 的配置或凭据。使用 Pi 后端时，由 Pi 管理自己的登录。安装器不检查模型登录，安装后请确认 agent 本身能正常工作。

Claude Code 原生翻译不带工具和历史，以 `thinkingLevel` 作为 effort，输出上限 32,000 tokens（与思考共用；当 Claude Code 指明模型自身的上限更低时，改用该上限）。由于 API 不报告回复停止的原因，每个请求都要求一个唯一的结束标记，缺少标记的回复永远不会被采用，因此截断的译文永远不会到达模型。缺少标记说明回复被截断了：可能是达到输出上限，也可能是模型的安全防护中途停止了回复；自 2026-10-14 起，Opus 5.5 的安全防护会在中途停止部分翻译，即使是普通提示词也会遇到。这时 Translator 会把同一个遮蔽后的请求再发给另一个模型系列一次（Sonnet；如果翻译器本身已是 Sonnet 模型，则改用 Haiku），以这次的回答为最终结果；对于长回答，只重新请求被截断的部分。模型完全没能给出回复时（因为被限流、过载或不可用，即 HTTP 429、5xx 或 404，或者回复中没有文本），也会得到同样的一次后备请求。请求超时和其他 API / 传输失败（包括只返回结束标记的空回复）共用这一次重试。Pi 对这些回复失败向同一模型重试一次，只在拒绝时换模型系列。每部分最多两次 Translator 层 completion 尝试；Claude 内部仍可能重试 HTTP 传输，降低被拒绝的原生 token 上限也消耗同一预算。明确取消及本地配置、持久化或字面量完整性错误均不重试。日志在 `model` 中记录后备模型（即使长回答只有部分分段用到了它），在 `fallbackFrom` 中记录第一个模型，`usage` 包含所有回复的用量。在 Pi 中，提供方报告为拒绝的回复，会向同一提供方另一系列的最新模型请求一次。Claude Code 的 Pi 后端和命令行工具则在给出回复的提供方处按名称请求另一系列（`sonnet`；拒绝的是 Sonnet 模型时改用 `haiku`），由 Pi 解析为具体模型；Claude Code 的日志按上文方式记录。标记在显示或缓存前移除。很长的提示词可能在两个模型上都翻译失败，请拆分或改用 Pi 后端。不会提交不完整的译文。

### 语言附加要求

该要求固定为：

```text
Use English for intermediate reasoning. Write your final response in Simplified Chinese.
```

它只附加到翻译或复用的输入上；预检跳过的提示词（没有中文正文，包括纯字面量文本），以及关闭翻译时的输入都不附加。记录中 `translated` 只含译文，`submitted` 是实际发送的内容（含该要求）。它不改变思考档位，也不要求模型公开隐藏推理。

### 环境变量与插件选项

| 变量或选项 | 用途 |
|---|---|
| `TRANSLATOR_CONFIG` | 配置文件的绝对路径。输入缓存和 Claude Code 会话记录也随之移动，它们保存在配置文件旁 |
| `TRANSLATOR_DISABLE=1` | 无论会话开关如何都关闭输入翻译；不影响回答翻译 |
| `TRANSLATOR_PI_BYPASS=1` | 仅 Pi，0.6.8 起支持：在读取配置或注册 hook 之前，为本进程跳过整个 Pi 扩展；供网关和自动执行使用 |
| `TRANSLATOR_NO_PERSIST=1` | Claude Code：不写入本工具的历史、会话记录和设置存储，五项会话设置均只保存在内存中。应与 Claude Code 的 `--no-session-persistence` 一起使用 |
| `TRANSLATOR_PI` | Pi 可执行文件路径，仅供命令行工具和 Claude Code 的 Pi 后端使用 |
| `PI_CODING_AGENT_DIR` | Pi 的配置目录；安装器会遵循它 |
| `CLAUDE_CONFIG_DIR` | Claude Code 的配置目录；安装器会遵循它 |
| `HOME` | 安装链接、`translator` 命令、默认 agent 目录以及 npm 安装数据目录的基准位置 |
| Claude Code 插件选项 `bridgePath` | 默认为空，即插件自身目录中的 worker `claude-code/bridge.mjs`，因此每份 mod 副本都运行自己的 worker。自定义安装时可设为 worker 的绝对路径 |

设置 `bridgePath` 的方法（插件 ID 取决于插件的安装方式）：

```sh
printf '%s\n' '{"bridgePath":"/absolute/path/to/translator/claude-code/bridge.mjs"}' |
  claude plugin configure translator@skills-dir --values-stdin
```

### Pi 自动执行

网关或其他无人值守启动器可**仅在其 Pi 子进程环境中**设置 `TRANSLATOR_PI_BYPASS=1`。此时 Translator 不注册输入 / 输出 hook、控制项或渲染器：提示词和回答直接通过，不翻译、不附加语言要求、不调用翻译模型，也不新增翻译记录。翻译配置损坏、翻译模型不可用或已保存的分支开关，都不会因此阻塞或延迟本次执行。Pi 的其他扩展、工具、认证和项目信任保持不变。

```sh
TRANSLATOR_PI_BYPASS=1 pi --mode json "Run the saved task"
```

这是显式的进程级退出翻译机制，不是翻译失败后的回退。如果希望普通 Pi 聊天仍使用翻译，请勿在全局环境中导出它。没有设置精确值 `1` 时保留原有行为，包括 JSON / print / RPC 模式下输入翻译失败时拦下提示词；提示词内容从不触发跳过。Claude Code 和独立翻译 CLI 不受影响。与此机制不同，`TRANSLATOR_DISABLE=1` 只关闭输入翻译。启动器必须设置新变量，并使用支持它的 Translator 版本；只更新一方不足以生效。不会修改共享设置文件或已有会话记录。

## 数据与隐私

**隔离不等于离线。** 每个翻译请求都会发送给你配置的模型服务商，内容是待翻译的文本，其中代码等受保护字面量已被遮蔽。请求中从不包含对话历史、任意工具结果、隐藏推理或图片。子代理支持只发送选定的任务或已完成的公开报告，不发送子代理完整对话。任务原文共用输入缓存，但绝不用于用户提示词历史恢复或输入框调回。原回答始终保留在主对话中，它的中文译文只用于显示和保存。

本工具会在本机保存**明文记录**：

| 位置 | 内容 |
|---|---|
| 配置文件旁的 `history/input.jsonl` | 成功的译文（原文、英文、规则版本和来源），Pi 与 Claude Code 共用 |
| 配置文件旁的 `claude-sessions/<session-id>.jsonl` | Claude Code 输入与回答的写前记录：译文、实际提交的文本、翻译模型与思考档位、用量、请求 ID，以及每条译文显示在哪一 transcript 行 |
| Pi 自身的会话文件 | 模型不读取的条目：双语记录、分支设置（开关、翻译模型、思考档位）和数值辅助用量收据（受[首次输入持久化限制](#pi-辅助用量)约束） |
| Pi 会话文件旁的 `prompt-translations/` 和 `output-translations/` | 本工具自己的写前日志和结果；从不直接写入 Pi 的会话文件 |
| Claude Code 自身的 transcript | 双语通知，模型从不读取（它们不是系统提示词） |
| Claude Code 的私有插件存储 | 三个开关、翻译模型和思考档位覆盖值，按 session ID 保存 |
| Claude Code 自己的提示词历史（其配置目录中的 `history.jsonl`） | Claude Code 的文件，保存你键入的提示词，没有 Translator 时也一样；从 0.6.16 起，其中保存的是你的中文而不是英文。Translator 只改动保存了译文英文的行，把它们换回你的原文（见[提示词历史保留你的中文](#提示词历史保留你的中文)），并在配置文件旁的 `claude-sessions/.history-<hash>` 中记下它读到的最后一行 |
| 系统临时目录中的 `translator-leases/` | Claude Code 的回答租约：翻译某个回答期间保存一个记有进程 ID 的锁文件，翻译完成后留下一个空标记，确保 mod 的两份副本不会都去翻译它。文件名是哈希值，不含任何文本，一天后删除 |

新建的日志目录权限为 `0700`，文件为 `0600`，每次追加都会刷入磁盘。默认位置已被 Git 忽略；如果把配置或日志放进其他仓库，请自行添加忽略规则。

不在磁盘上留下任何记录：

- **Pi：** `--no-session` 只在内存中保留记录和缓存。
- **Claude Code：** 同时使用 `--no-session-persistence` **和** `TRANSLATOR_NO_PERSIST=1`；mod 无法读取前一个命令行参数。Claude Code 的翻译请求只通过内存和标准输入在插件与 worker 之间传递，从不生成额外文件。只有上表中不含文本的回答租约仍使用系统临时目录，Claude Code 的提示词历史保持原样。

只有 `started` 而没有结果的记录表示翻译被中断。`translated` 表示结果通过了回复及字面量恢复检查，不证明语言或语义质量，也不代表主模型的整轮回答成功。在 agent 中删除会话不会删除这些记录或缓存，升级和卸载也不会删除；请按上表自行清理。

不让译文进入模型上下文，并不等于把这些文件放进沙箱：如果你让 agent 读取本地日志，日志内容会像其他文件一样进入那次请求。

从旧 Pi 会话导入记录：

```sh
HOOK="$HOME/.agents/hooks/translator"
node "$HOOK/import-history.mjs"                    # 默认读取 ~/.pi/agent/sessions
node "$HOOK/import-history.mjs" "/path/to/sessions"
```

导入是幂等的，不会修改原会话。删除共享的 `history/input.jsonl` 不会移除 Pi 活动分支中的旧记录；要强制重新翻译，请执行 `translator cache off`。

## 安装详解

### 会配置哪些 agent

| 检测结果 | 安装器的行为 |
|---|---|
| 只有 Pi | 配置 Pi，不触碰任何 Claude Code 目录 |
| 只有 Claude Code | 配置 Claude Code；它的默认后端不需要 Pi |
| 两者都有 | 询问：`1` Pi、`2` Claude Code、`3` 两者、`0` 取消 |
| 两者都没有 | 以退出码 `1` 结束，不创建任何配置、链接、备份或目录 |

安装器在 `PATH` 中查找可执行文件，并额外检查 Pi 的 `~/.pi/agent/bin/pi` 以及 Claude Code 的 `~/.local/bin/claude` 或 `~/.claude/local/claude`。它用 `--version` 检查所选 agent 的版本；仅有配置目录不算已安装。版本过旧或无法识别时拒绝安装；未选择的 agent 既不会被安装，也不会被升级。

### 选项

```sh
./install.sh --agent pi                 # 只配置 Pi
./install.sh --agent claude             # 只配置 Claude Code
./install.sh --agent both               # 两者都配置，两者都必须已安装
./install.sh --yes                      # 两者都有时选两者，否则选找到的那个
./install.sh --agent both --dry-run     # 只打印计划，不写入任何内容
./install.sh --help

# 另一个 Claude Code 账号，不改动默认账号
./install.sh --agent claude --claude-dir "$HOME/.claude-work"

# 非默认的 Pi 配置目录
./install.sh --agent pi --pi-dir "/absolute/path/to/pi-agent"
```

没有可交互的终端且两个 agent 都已安装时，必须传入 `--agent` 或 `--yes`；安装器不会擅自选择。`--yes` **不会**跳过冲突或版本检查。

配置目录按以下顺序确定：`--pi-dir` 和 `--claude-dir` 选项，然后是 `PI_CODING_AGENT_DIR` 和 `CLAUDE_CONFIG_DIR`，最后是默认目录。要把配置文件放在别处，请将 `TRANSLATOR_CONFIG` 设为绝对路径，并确保之后的每个 agent 进程也设置了它。

### 安装器会修改什么

1. 写入任何内容之前，先检查 Node、agent 版本、源码是否完整以及每个目标位置。只要有冲突就中止整个安装，不会只配置好一个 agent。
2. 创建指向源码的稳定链接和 `translator` 命令：

   ```text
   ~/.agents/hooks/translator  ->  克隆目录中的 translator 目录
   ~/.local/bin/translator      ->  ~/.agents/hooks/translator/control.mjs
   ```

3. 仅在没有配置时，从 `config.example.json` 创建 `config.json`，权限为 `0600`。已有设置、历史和日志从不被覆盖。
4. **Pi：** 把 `<稳定链接>/pi/index.ts` 加入 Pi `settings.json` 的 `extensions` 列表，保留其他所有字段和扩展。已知的旧 `pi.ts` 注册会被迁移并去重。
5. **Claude Code：** 创建链接 `<claude-dir>/skills/translator -> <稳定链接>/claude-code`。不修改你的 settings hooks，也不替换已有的同名插件。
6. 修改 Pi 设置前，保存一份**逐字节备份**，文件名为 `settings.json.translator.bak.<时间戳>.<随机值>`。文件权限和已有的符号链接都会保留。
7. 重复安装无变化。文件更新锁住规范化目标及 Pi 的逻辑设置路径，最终替换前再核对基线。检测到修改会中止安装。回滚失败时保留原始备份并报告恢复路径，不宣称已完整恢复。

锁只能协调遵守锁协议的写入者，不能约束任意编辑器；最终比较与 rename 不是操作系统级 compare-and-swap。避免并发修改宿主设置，尤其是首次创建或通过未知符号链接别名访问时。争用锁最多等待五秒。崩溃后应检查报错中的 `.lock`，确认没有活动写入者后再移除；安装器不会抢占未知锁。

### 移动或更新仓库副本

agent 通过这些链接加载本工具，因此**安装后不要删除克隆目录**。要移动它，请把 `~/.agents/hooks/translator` 指向新位置，更新所有直接引用旧绝对路径的注册或环境变量，然后重载 Pi 或重启 Claude Code。`config.json`、历史和日志随克隆目录一起移动；保存在自定义外部路径中的数据可以留在原处。安装器拒绝替换链接位置上已存在的实体目录，或指向另一个克隆的链接：请先备份配置和数据，再手动解决。

**更新会到达正在运行的 agent。** agent 直接从克隆目录加载本工具，因此克隆目录中的内容就是正在运行的版本：

- **Claude Code** 会在 mod 的文件变化时自动重新加载它：在当前轮次结束时，或在你执行 `/translator` 时提前进行。翻译会跨越重新加载继续进行：进行中的轮次，其回答仍只翻译一次并完成绘制，设置和尚未显示的原文也会保留。如果 Claude Code 显示 `reload failed, the previous version stays loaded`，说明新文件有错误：之前的版本会继续翻译，文件下次变化时会再次尝试加载。
- **Pi** 会一直使用已加载的版本，直到你执行 `/reload` 或重启它。Pi 只允许命令重新加载扩展，因此无法自行应用更新；它也从不混用两个版本。
- **会切换分支的 checkout 不是稳定的安装。** agent 加载被链接克隆中的内容，因此检出其他分支（例如本仓库中其他工具的分支）会同时改变所有正在运行的 agent。日常使用请安装发布的 npm 包，它在 Git 之外保留自己的副本（见[通过 npm 安装](#通过-npm-安装)）；开发用的 checkout 只在测试时按[开发与测试](#开发与测试)中的方式用 `claude --plugin-dir` 或 `pi -e` 加载。

如果 Claude Code 提示 `The loaded translation mod is out of date`，说明仍加载着 0.4.0 之前的 mod：请重启 Claude Code。

### 通过 npm 安装

`npm install -g @orbit-labs-ai/translator` 会把本工具放进 npm 的全局包目录，并添加 `translator` 命令：`translator install` 运行与 `./install.sh` 相同的安装器，接受相同的选项；其他参数用于修改默认设置，见上文。此时 `~/.agents/hooks/translator` 链接指向 npm 的全局目录。

npm 每次更新都会替换包目录，因此通过 npm 安装时，`config.json`、翻译缓存和 Claude Code 会话记录保存在 `~/.agents/translator/` 中，而不是源码旁边。

- **更新：** 运行 `npm install -g @orbit-labs-ai/translator@latest`，然后在 Pi 中执行 `/reload`。Claude Code 会自行重新加载 mod；如果某个会话仍在运行旧版本，在该会话中执行 `/reload-plugins`。链接仍然有效，无需再次运行安装器。
- **切换 Node.js 安装**（例如使用版本管理器）会改变 npm 的全局目录。请删除 `~/.agents/hooks/translator` 链接，在新的 Node.js 下安装本包，再运行一次 `translator`。
- **不要使用 `npx`：** 它从临时缓存运行包，因此安装器会拒绝在那里运行。

### 从早期版本升级

**从 0.7.0 或 0.7.1 升级到 0.7.2。** Pi 新增 `/translator usage`，并在翻译前拒绝格式错误或超出上限的子代理批次。翻译规则、偏好、缓存契约及 Claude 运行时行为不变。无需修改 Pi 核心、重置缓存或迁移配置。新收据从重载后的实际尝试开始，不回填旧翻译记录。请阅读[收据持久化限制](#pi-辅助用量)，尤其是首次输入被拦截的情况。

GitHub Release 发布后，下载其打包文件（需要 GitHub CLI 访问权限），安装到全新稳定前缀，不覆盖活动副本：

```sh
gh release download translator-v0.7.2 --repo Orbit-Labs-AI/Orbit-Tools \
  --pattern orbit-labs-ai-translator-0.7.2.tgz --dir /path/to/downloads
npm install -g --prefix "$HOME/.local/share/orbit-tools/translator-0.7.2" \
  --ignore-scripts /path/to/downloads/orbit-labs-ai-translator-0.7.2.tgz
```

npm 发布后，等价的固定版本安装命令为：

```sh
npm install -g --prefix "$HOME/.local/share/orbit-tools/translator-0.7.2" \
  --ignore-scripts @orbit-labs-ai/translator@0.7.2
```

软件包位于该前缀的 `lib/node_modules/@orbit-labs-ai/translator`。首次安装请运行其中的 `install.sh`。已有 **npm 安装**请先完成／取消翻译，备份后仅将已有 `~/.agents/hooks/translator` 符号链接和 CLI 链接明确改指向此软件包；安装器会正确拒绝冲突的旧目标。保留 `~/.agents/translator/` 及已有 Pi／Claude 登记不变。重载 Pi 和 Claude 插件，或新建会话；只要仍有会话使用旧包，就保留旧包。本地安装打包文件不等于向 npm 发布。

**克隆安装的数据位置不同。** 如果 `config.json`、`history/` 和 `claude-sessions/` 仍位于旧源码旁，不要直接套用仅适用于 npm 的改链接步骤：`node_modules` 安装默认改用 `~/.agents/translator/`，会找不到这些记录。保留并备份旧源码／数据，切换前明确迁移，或继续使用源码安装并保留既有数据路径。在每个宿主中保留自定义 `TRANSLATOR_CONFIG` 路径。不要覆盖另一安装的数据，也不要静默启用空默认值。

**从 0.6.x 升级到 0.7.0。** 无需重新运行安装器或迁移配置；已有平铺默认值和会话设置仍有效。使用 `translator --host pi …` / `--host claude …` 配置独立默认值。子代理请使用 Pi 1.3.0 或 1.4.0。更新后执行 Pi `/reload` 和 Claude `/reload-plugins`（尤其是桌面应用），或新建会话。更新前先完成或取消正在进行的翻译：旧的进行中 continuation 契约可能被安全拒绝，不会提交不完整输入。扫描器契约让旧的带版本缓存条目不再自动复用，但不删除日志。安装已发布的软件包请运行 `npm install -g --ignore-scripts @orbit-labs-ai/translator@0.7.0`，然后重新加载宿主。

**从 0.6.0（`prompt-translate`）升级到 0.6.1（`translator`）。** 所有名称都已更改，安装器不会删除旧条目。请执行一次以下步骤，然后重启 Claude Code 并重载 Pi：

1. 删除旧条目：`~/.agents/hooks/prompt-translate` 和 `~/.local/bin/agent-translate` 链接、每个 Claude Code 账号中的 `skills/local-translation` 链接，以及 Pi `settings.json` 中的 `…/prompt-translate/pi/index.ts` 条目。
2. 获取新版本。**克隆**：执行 `git pull`，然后把 `config.json`、`history/` 和 `claude-sessions/` 从 `Hooks/prompt-translate/` 移到 `Hooks/translator/`；Git 不会移动被忽略的文件。**npm**：执行 `npm uninstall -g @orbit-labs-ai/prompt-translate` 和 `npm install -g @orbit-labs-ai/translator`，然后把 `~/.agents/prompt-translate/` 中的内容移到 `~/.agents/translator/`。
3. 重新运行安装器：在克隆目录中运行 `./install.sh`，或运行 `translator install`。
4. 重命名你设置过的环境变量：`AGENT_TRANSLATE_CONFIG`、`AGENT_TRANSLATE_DISABLE`、`AGENT_TRANSLATE_NO_PERSIST` 和 `AGENT_TRANSLATE_PI` 分别改为 `TRANSLATOR_CONFIG`、`TRANSLATOR_DISABLE`、`TRANSLATOR_NO_PERSIST` 和 `TRANSLATOR_PI`。旧名称不再被读取。

命令也已更改：`/translator` 取代 `/translate`、`/translate-language`、`/translate-output`、`/translation-status` 和 `/translate-cancel`，`translator` 命令取代 `agent-translate`（`agent-translate language` 现为 `translator instruction`）。Pi 已保存的分支设置仍然有效。Claude Code 按插件名称保存会话覆盖值，因此以 `local-translation` 名义保存的覆盖值不会迁移，恢复的旧会话将从文件默认值开始。[更新日志](CHANGELOG.zh-CN.md)列出了全部变化。

**更早的版本：**

- Pi 的入口已从旧的顶层 `pi.ts` 移到 **`pi/index.ts`**，所有 Pi 代码现在位于 `pi/` 中，顶层不再保留兼容 shim。安装器会迁移它所管理的 Pi 设置中的旧路径；其他手动注册、项目级注册和外部调用方需要你自行更新。
- Claude Code 的 worker 已从顶层移到 **`claude-code/bridge.mjs`**。默认插件设置无需修改，但请更新任何自定义的非空 `bridgePath`。`cli.mjs`、`control.mjs` 和 `import-history.mjs` 的路径不变。
- Claude Code 现在默认使用 `claudeBackend: "native"`。若要继续通过 Pi 翻译，请在配置中加入 `"claudeBackend": "pi"`；安装器从不覆盖已有的选择。

## 命令行工具与其他宿主

独立命令可以在把文本交给任何 agent 之前先翻译它。**它需要 Pi**：Claude Code 原生后端只能在其插件内部使用。下面的中文示例意为“请检查代码”：

```sh
HOOK="$HOME/.agents/hooks/translator"
printf '%s' '请检查代码。' | node "$HOOK/cli.mjs"
printf '%s' '请检查代码。' | node "$HOOK/cli.mjs" --json
node "$HOOK/cli.mjs" --help
```

纯文本模式下，待提交的文本写入标准输出，双语预览写入标准错误。JSON 模式下，标准输出只有 `{status,text?,error?,usage?}`。成功（包括本地直接放行）时退出码为 `0`；失败或取消时退出码非零，并且**没有可提交的文本**。调用方必须同时检查状态和退出码，不能把输出直接管道给 agent。该命令不附加语言要求，不维护会话历史，也不翻译回答。

要支持其他宿主，请调用 `shared/core.mjs` 中的 `translatePrompt` 和 `translateOutput`，并提供自己的隔离 completion 和存储。输入失败必须阻止提交，回答译文绝不能写入模型上下文。[架构文档](docs/architecture.md)（英文）记录了 Claude Code worker 协议和依赖规则。

## 故障排查

| 现象 | 处理方法 |
|---|---|
| 安装器找不到 agent | 检查 `PATH`、文件权限，以及 `pi --version` / `claude --version`。仅有配置目录不算已安装 |
| 安装器报告冲突 | 检查已有的文件、目录或链接，不要直接覆盖。先备份，再决定保留或迁移哪些内容 |
| Pi 仍引用旧的 `pi.ts` | 用你实际使用的 `--pi-dir` 重新运行安装器，并检查项目级或命令行中的注册 |
| Claude Code 找不到 worker | worker 现在是 `claude-code/bridge.mjs`；检查是否有旧的非空 `bridgePath` |
| 升级后仍出现旧的 `/translate…` 命令、`local-translation` 插件，或 Pi 报告与 `prompt-translate` 有关的错误 | 按[从早期版本升级](#从早期版本升级)删除 0.6.0 的条目，然后重启 Claude Code 并重载 Pi |
| 翻译失败，提示词被拦下 | 检查后端的登录、模型、超时和网络。只有确实想发送原文时才使用 `/translator input off` |
| Claude Code 原生模型不可用 | 固定一个允许的 `claudeModel`，或设置 `claudeBackend: "pi"` 并配置 Pi |
| `Prompt NOT sent: The loaded translation mod is out of date` | 仍加载着 0.4.0 之前的 mod。重启 Claude Code，包括其他使用同一链接的账号 |
| Claude Code 显示 `reload failed, the previous version stays loaded` | 更新后的文件有错误，或更新进行到一半时被读取。之前的版本会继续翻译；文件完整后，Claude Code 会在它们下次变化时重新加载 mod，也可以执行 `/reload-plugins` |
| 克隆目录切换到其他分支后翻译停止工作 | agent 直接从被链接的克隆加载 Translator，因此该分支中的版本（或缺少的文件）立即生效。把它切回去，并改用发布的 npm 包运行 agent，而不是会切换分支的 checkout；见[移动或更新仓库副本](#移动或更新仓库副本) |
| Claude 工作期间发送的提示词下方没有原文 | 更新到 0.6.2 或更高版本；更早的版本只在 Claude 空闲时发送的提示词下方绘制原文 |
| 在 Claude Code 桌面应用中，Claude 工作期间发送的提示词在 Claude 读取后中文消失，英文下方直到本轮结束都没有原文 | 更新到 0.6.15 或更高版本，然后在会话中执行 `/reload-plugins` 或新建会话。早期版本要等 Claude Code 把这条提示词的行写入会话文件后才绘制原文，而耗时较长的步骤会推迟这次写入 |
| 在 Claude Code 桌面应用中，提示词显示了两遍中文，或者回答、Claude 工作期间发送的提示词下方没有中文 | 更新到 0.6.6 或更高版本，然后在该会话中执行 `/reload-plugins` 或新建一个会话：桌面应用的会话不会自行重新加载 mod。桌面应用根据自己的副本绘制各行，并按自己的方式命名它们；更早的版本只为终端中的行绘制。会话中已有的行会在桌面应用下次显示它们时重新绘制 |
| 在 Claude Code 桌面应用中，新会话在发送第一条消息之前没有 Input、Instruction 和 Output 按钮行 | 对于项目会话，桌面应用要等第一条消息发送时才启动 Claude Code，mod 也随之启动。从 `/` 菜单中选择 `/translator`，或把某个设置命令（例如 `/translator input off`）作为第一条消息发送：mod 会在不调用模型的情况下回答，按钮行随之出现。详见[在桌面应用中](claude-code/README.zh-CN.md#在桌面应用中) |
| 在 Claude Code 桌面应用中，新会话的 `/` 菜单里没有 `/translator` | 桌面应用在关闭所有 hook 的情况下列出新会话的命令，而早期版本要等会话运行后才注册 `/translator`。请更新 Translator，然后过几分钟再新建会话，或重启桌面应用：它会把命令列表保留一段时间。只有通过安装器创建的 `skills/translator` 链接，该命令才会出现在那里；用 `--plugin-dir` 或 `CLAUDE_CODE_PLUGIN_DIRS` 加载的副本不会 |
| 翻译变慢或超时 | 检查 `/translator status`，把文件中的 `timeoutMs` 设为 `null`（不设时限，即默认值）或调大，使用 `/translator thinking low`，或通过 `/translator model MODEL` 固定更快的翻译模型。指明某一部分的警告（`part 2 of 4`）来自[长回答](#长回答)中的一部分。在 Pi 中，0.6.7 之前的版本用一个请求发送整个回答，长回答会超时：请更新，然后执行 `/reload` |
| Claude Code 报告 `Translation incomplete: the reply stopped before its completion marker` | 翻译模型和它的后备模型（消息中会写明）都没有完整回复：输出上限或两个模型的安全防护截断了回复。内容没有被提交；请拆分提示词、用 `/translator model MODEL` 指定其他模型，或更换后端。没有后备模型的早期版本报告的是 `missing completion marker` |
| 默认设置似乎不起作用 | 分支 / 会话设置优先；用 `/translator status` 检查。可用 `/translator model follow` 覆盖文件中的模型固定值 |
| 快捷键没有反应 | Pi 依赖终端上报组合键。Claude Code 输入框上方的按钮没有按键：点击它们，或用 Ctrl+X、Tab 移到按钮行。设置请用 `/translator`；取消通过下述控制项完成，不是斜杠命令 |
| 在 Claude Code 中，某个会话的 Input 或 Output 自己关闭了 | 在此版本之前，在空输入框中键入数字，稍停后会按下对应按钮（1 Input、2 Instruction、3 Output），这一更改会为该会话保存。按钮已不再有按键；请用 `/translator status` 检查并把设置改回来 |
| 在 Claude Code 中，Up 显示较早提示词的英文 | 此前版本保存的条目中是英文。如果 Translator 知道原文（本会话的提示词，以及输入缓存中你最近发送的提示词），在你从空输入框打开该条目时会换入原文；现在发送的提示词按键入内容保存 |
| 在 Claude Code 中，`/resume`、`/branch` 或 `← for agents` 之后之前的绘制消失了 | 请更新到此版本：它会加载对话所继续的会话的绘制和设置 |
| 以中文为主的回答仍被翻译 | 0.7.0 遮蔽路径、上下文名称和已有中文段落，并跳过有明确长度限制的英文引言。其他位置的实质英文仍翻译，中文正文不重发。有歧义的精确字符串请放进反引号 |
| Claude Desktop 最后的选择框让回答没有译文 | 使用 0.7.0，并执行 `/reload-plugins` 或新建会话。仅选择类工具的步骤现在会在等待选择时启动翻译；原生选项和审批控件保持原样 |
| 含中文名称或词语的英文回答被翻译 | 这是预期行为：只要还有英文正文，回答中的中文就不会阻止翻译，翻译器会保留名称和路径 |
| 引用中文的英文提示词被翻译 | 这是预期行为：引号内的中文算作正文。翻译器会保留引号内的字面值；放进反引号可让这条提示词不经翻译 |
| 译文仍有未翻译的正文，或使用了非预期语言 | 翻译后没有语言审核或修复调用。请检查结果，尝试其他翻译模型，并关闭输入缓存以强制重新翻译；适当时，将需要精确保留的字面字符串放进反引号 |
| 需要取消翻译 | Pi：Ctrl+Alt+C。Claude：翻译进行期间的 **Cancel translation** 按钮（用 Ctrl+X、Tab 可移到它；设置面板中按 4）。两者都取消输入 / 输出且不改变设置；打断 agent 只会停止提示词的翻译；Pi idle 输入预处理期间不要依赖普通 abort |
| 文件锁超时 / 回滚不完整 | 停止并发修改设置，检查报错中的锁和保留备份；不要移除活动锁或直接覆盖更新后的设置 |
| Claude Code 为插件显示“Hooks (0)” | 该计数只包含 classic settings hooks。运行 `claude plugin validate` 查看 mod 的事件 |

## 已知限制

- 成功原文必须有送达证据。Pi 的公开 API 没有从输入到队列项的关联 ID：文本和图片采用保守的精确匹配；不同原文产生无法区分的待送达译文、相冲突的跳过输入，或宿主改写图片 / 文本时，可能省略卡片，持久化日志和已开启的缓存仍可查阅。丢弃队列、导航、重载或替换会话后，不会从历史重建待显示卡片；已送达的卡片保留。

- 预检的中文检测依据汉字，因此日文汉字也会被识别。它只在翻译之前运行。回复完成且非空、字面量完整，并不保证原意、翻译完整度或目标语言；保留的字面量内容从不触发语言拒绝。
- 不翻译隐藏推理、任意工具输出、失败 / 中止的子代理报告和未完成回答。受支持的子代理任务 / 报告及 Claude 仅选择类步骤按上述说明处理；操作工具过程文本仍跳过。其他模板 / 插件之后添加的文本、自定义子代理结构，以及没有公开本地事件的远程工作流不在契约范围内。
- 斜杠命令、skill 和提示词模板按键入内容发出，包括参数：Claude Code 从不应用对命令行的改写，Pi 则自己拆分模板的参数。只是以斜杠开头的行，例如路径（`/tmp …`）或输错的命令，会被翻译。
- 在 Claude Code 中，设置里的 `UserPromptSubmit` 钩子看到的是英文；位于 Translator 之下的其他 mod 的 `prompt.submit` 钩子、`turn.start` 以及排队提示词的 `attachment.prompt` 看到的是你键入的文本。
- classic `UserPromptSubmit` hook 无法安全地替换原始提示词（它的 `additionalContext` 只能追加文本），因此本工具不提供这种退路，也没有 Codex hook。
- Claude Code 的 safe 和 bare 模式、已禁用的 hooks、未受信任的工作区或组织策略，都可能阻止插件加载。禁用 hooks 时，`/translator` 只来自 mod 的 `SKILL.md`，模型会回复说 Translator 没有运行；safe 和 bare 模式还会跳过你的 skills，因此那里没有 `/translator`。新安装后，请先用 `/translator status` 确认再依赖它。
- 在 Claude Code 中，transcript 里绘制的原文和中文回答在重启后只能从会话 journal 恢复，因此仅内存模式（`TRANSLATOR_NO_PERSIST=1`）只在 Claude Code 退出前显示它们。它们的通知仍在 transcript 视图中。
- 正在运行的 Pi 只有在执行 `/reload` 或重启后才会应用代码更新：Pi 只允许命令重新加载扩展。
- Claude Code 在本轮结束后翻译回答。翻译期间重载 mod 时，只要旧副本仍在运行，就由它完成翻译，否则由新副本重新开始，绝不会两份副本都翻译；切换到另一个会话（`/clear`、`/resume`、`/branch`）时翻译会继续完成；先退出 Claude Code 则会丢失这次翻译，日志中会留下一条没有结果的 `started` 记录。一次性的 `claude -p` 运行没有任何界面绘制，仍会等译文完成后才结束。无界面 SDK 的中断只释放 step 的等待，不停止会话持有的输出任务；但宿主仍可能等该任务结束后才接收下一条提示词。会话启动 runner 尚未初始化的副本会保留旧的 step 内等待路径及其派发取消边界。
- 以新 session ID 继续、却没有指明来源会话的对话（`--fork-session`、桌面应用的 Rewind），通过它绘制的行与来源会话关联：一旦它绘制了来源会话关联过的行，该会话的绘制和 Translator 设置就会随之带过来。在此之前、它显示的行都没有绘制时，以及在没有任何界面绘制的运行中（`claude -p`、没有界面的 SDK 宿主），它从文件默认值开始。
- 少数 Claude Code 回合在结束时没有常规的模型步骤。这些回答通过去重的 fallback 翻译，其 transcript 通知能否保存取决于宿主；详见 [Claude Code 适配器说明](claude-code/README.zh-CN.md)。

## 卸载

1. **Pi：** 从你所用 Pi 目录下 `settings.json` 的 `extensions` 中删除本工具的 `pi/index.ts` 条目，然后重载或重启 Pi。
2. **Claude Code：** 删除你配置过的每个账号下的 `skills/translator` **符号链接**，然后重启 Claude Code。
3. 不再需要时，删除 `~/.local/bin/translator` 链接。
4. 确认没有其他 agent 仍在使用后，再删除 `~/.agents/hooks/translator` 链接或源码本身。
5. 如果是通过 npm 安装的，用 `npm uninstall -g @orbit-labs-ai/translator` 删除该包。

不要删除整个 agent 配置目录，也不要用旧的设置备份覆盖已经改动过的文件。以上步骤会保留你的配置、翻译历史和日志；请参照[数据与隐私](#数据与隐私)单独删除。

## 开发与测试

不安装、只为单个会话加载本工具：把 `HOOK` 设为 `translator` 目录，然后运行 `pi -e "$HOOK/pi/index.ts"` 或 `claude --plugin-dir "$HOOK/claude-code"`。不要同时通过两个入口加载同一个集成。

```sh
npm run check:shared   # 校验生成的 Claude 扫描器与共享源码一致
npm test                 # Node 测试，以及真实 Pi 与 Claude Code 对本地 HTTP fixture 的测试
npm run test:claude      # 校验 Claude Code 插件并运行它的 mod 测试
npm run test:all         # 以上两项；完整验证需要两个 agent 都已安装
npm run test:coverage    # Node 模块的覆盖率；范围另有说明
npm pack --dry-run --json --ignore-scripts  # 检查运行时／许可证及私有数据排除项
bash -n install.sh
```

测试中的所有模型回复都来自本地 fixture，因此测试从不产生付费调用。安装器测试在临时的 home 和克隆目录中运行，覆盖四种已安装 agent 的组合、交互选择、重复运行、含空格的路径、迁移、冲突、格式错误的设置、备份和回滚。真实 agent 测试加载的是**安装器创建的入口**。

缺少 Pi SDK 或 Claude Code 时，相应的集成测试会跳过并注明；**跳过的测试不等于通过**。两个变量便于调试：

- `PI_SDK_PATH`：Pi SDK 的 `dist/index.js` 路径，用于 Pi 不在常规 managed 布局中的情况。
- `KEEP_TRANSLATION_TEST=1`：保留 Claude Code 集成测试的临时目录。

`test/claude-tui.test.mjs` 和 `test/pi-tui.test.mjs` 通过 tmux 驱动真实的 Claude Code 和 Pi 终端界面；未安装 tmux 或对应 agent 时会跳过并说明原因。

[0.7.2 验证记录](docs/testing.md#verification-record-for-072)（英文）覆盖未经修改的官方 Pi 1.4.0、Claude Code 2.1.311、辅助用量及打包：**1,387 项 Node 测试和 61 项原生 Claude 插件测试通过，零失败／跳过**。所有检查使用隔离的本地 fixture，不调用付费提供方。[0.7.1 记录](docs/testing.md#verification-record-for-071)（英文）保留此前兼容性证据；[0.7.0 记录](docs/testing.md#verification-record-for-070)（英文）保留 Pi 1.3.0 / CLI / 桌面引擎结果及 GUI 验收边界。0.7.2 未重新测量覆盖率。

## 延伸阅读

- [Claude Code 适配器](claude-code/README.zh-CN.md)：mod 在 Claude Code 中的工作方式。
- [Pi 适配器](pi/README.zh-CN.md)：扩展在 Pi 中的工作方式。
- [架构](docs/architecture.md)（英文）：源码布局、依赖规则和 worker 协议。
- [测试](docs/testing.md)（英文）：测试矩阵、记录结果和边界。
- [更新日志](CHANGELOG.zh-CN.md)：各版本的变化以及升级方法。
- [贡献指南](../../CONTRIBUTING.md)（英文）：如何提出修改；中英文 README 同步维护。

## 许可证

[MIT](../../LICENSE) © 2026 Orbit Labs contributors。
