@epoch-agent/infra 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +219 -0
- package/README.md +99 -0
- package/dist/index.d.ts +2468 -0
- package/dist/index.js +3099 -0
- package/dist/locales/en.yaml +2757 -0
- package/dist/locales/zh.yaml +2932 -0
- package/package.json +51 -0
|
@@ -0,0 +1,2932 @@
|
|
|
1
|
+
# epoch-agent 文案 catalog —— **基线 / 真源**(本项目的默认语言)
|
|
2
|
+
#
|
|
3
|
+
# 规矩四条,破一条根 __tests__/i18n-catalog.test.ts 就红:
|
|
4
|
+
#
|
|
5
|
+
# 1. 点分 key,嵌套只为可读;`{placeholder}` 运行时替换
|
|
6
|
+
# 2. 新增 key 必须**同一个提交里**加进 en.yaml,key 集合两边完全相同
|
|
7
|
+
# 3. 每条 value 里的 `{placeholder}` 集合,两边也必须完全相同
|
|
8
|
+
# 4. 这里的每个 key 都要被代码用到 —— **死 key 也红**(删了代码忘了删文案)
|
|
9
|
+
#
|
|
10
|
+
# 范围不是「所有中文」:JSDoc / 行内注释、日志、错误栈、工具输出、工具
|
|
11
|
+
# description 一律**不进**这里。判据和分批计划见 .agents/plans/40-i18n-plan.md §2.1。
|
|
12
|
+
|
|
13
|
+
# i18n 自己的健康问题(infra/src/i18n.ts 的 i18nDiagnostics)。
|
|
14
|
+
# 这几条是全仓第一批进 catalog 的文案,也是唯一一批「catalog 坏了的时候
|
|
15
|
+
# 自己也会退化成 key 路径」的文案 —— 那正是回落链第三层要保证的事。
|
|
16
|
+
i18n:
|
|
17
|
+
locales_missing: '找不到 locales 目录,界面文案将显示为 key 路径'
|
|
18
|
+
catalog_missing: '缺少 locales/{file},{lang} 文案将回落到默认语言'
|
|
19
|
+
catalog_invalid: 'locales/{file} 不是合法 YAML({reason}),已回落到内置默认'
|
|
20
|
+
|
|
21
|
+
# `epoch <query>` 带值 flag 的取值校验(cli/src/commands/run-overrides.ts,方案 28 收尾时修的那条)。
|
|
22
|
+
# 每一条都必须把**下一步该怎么敲**说出来(可选值清单、或者「任务是位置参数」)——
|
|
23
|
+
# 用户敲错的那一刻缺的不是「这个值不对」,是「那我该怎么写」。
|
|
24
|
+
flags:
|
|
25
|
+
provider_unknown: '--provider 不认识: {value}'
|
|
26
|
+
permission_unknown: '--permission 不认识: {value}'
|
|
27
|
+
choices: '可选: {choices}'
|
|
28
|
+
model_not_a_name: '{flag} 的值不像模型名: {value}'
|
|
29
|
+
model_not_a_name_hint: '模型 id 是 ASCII(字母数字和 - . _ : / @ 之类),不带空格也不带中文。要发给模型的话直接当位置参数给:epoch "{value}"'
|
|
30
|
+
base_url_invalid: '--base-url 不是合法的 http(s) 地址: {value}'
|
|
31
|
+
base_url_invalid_hint: '要形如 https://api.example.com/v1。要发给模型的话直接当位置参数给:epoch "{value}"'
|
|
32
|
+
|
|
33
|
+
# 子命令自己的 `--help` 文案。**整条命令一起搬**,不许只搬新加的那个开关:
|
|
34
|
+
# 一条命令的帮助里一半走 catalog 一半硬编码,在非中文机器上就是半句中文半句英文。
|
|
35
|
+
# 目前只有 `epoch web` 搬过来了(2026-08-14 加 --json 那次),其余命令仍是硬编码,
|
|
36
|
+
# 见 .agents/plans/40-i18n-plan.md 的分批计划。
|
|
37
|
+
cli:
|
|
38
|
+
web:
|
|
39
|
+
summary: '起本地 Web UI 服务(默认自动打开浏览器)'
|
|
40
|
+
opt_port: '监听端口(默认 {port},0 = 让系统分配)'
|
|
41
|
+
opt_host: '监听地址(默认 {host};非回环地址必须同时给 --token)'
|
|
42
|
+
opt_token: '鉴权 token(默认每次启动随机生成)'
|
|
43
|
+
opt_no_open: '不自动打开浏览器'
|
|
44
|
+
opt_json: 'stdout 只打一行 JSON(url / host / port / token),其余提示改走 stderr。宿主程序拉起时用,配 --port 0 就不必写死端口'
|
|
45
|
+
config:
|
|
46
|
+
summary: '管理 provider 和模型配置'
|
|
47
|
+
keys: 'provider / model / permission / shell / models.utility / budget.<字段>'
|
|
48
|
+
keys_unset: 'model / permission / shell / models.utility / budget.<字段>'
|
|
49
|
+
value: '值'
|
|
50
|
+
show: '显示当前配置(apiKey 等敏感字段脱敏)'
|
|
51
|
+
opt_raw: '不脱敏,原样输出'
|
|
52
|
+
path: '显示配置文件路径(多 profile 时用来确认写到哪)'
|
|
53
|
+
get: '读取单个配置项,未设置时退出码为 1'
|
|
54
|
+
set: '设置配置项'
|
|
55
|
+
unset: '删除配置项,回落到默认值'
|
|
56
|
+
secret: '查看凭据存储后端;--export 把凭据倒回 .env(明文,需二次确认)'
|
|
57
|
+
opt_export: '把凭据写回 ~/.epoch/.env(明文!)'
|
|
58
|
+
schema: '把设置文件的 JSON Schema 写进 <项目根>/.epoch/schemas/,编辑器据此补全与校验'
|
|
59
|
+
schema_paste_hint: '把这一行加到 .epoch/settings.json 顶上(相对路径,不联网):'
|
|
60
|
+
# `epoch doctor` 的沙箱一节(方案 46 PR-2,cli/src/commands/status.ts)。
|
|
61
|
+
# 沙箱是**实测值不是配置值**,所以这一节的每一行都来自一次真的探测。
|
|
62
|
+
# `code_exec` 和 `terminal` 分两行说:它们走同一批后端、**不同的边界**,
|
|
63
|
+
# 合成一句「沙箱:已启用」正是方案 46 要修的那个洞。
|
|
64
|
+
doctor:
|
|
65
|
+
sandbox_head: '沙箱:'
|
|
66
|
+
sandbox_absent_tools: 'code_exec 和 terminal 都没拿到 OS 级隔离——这台机器上探测不到可用后端'
|
|
67
|
+
sandbox_backend: '后端: {backend}({platform})'
|
|
68
|
+
sandbox_code_exec: 'code_exec: {detail}'
|
|
69
|
+
sandbox_terminal: 'terminal({mode}): 强制完整度 {enforcement}'
|
|
70
|
+
sandbox_partial_devnull: 'partial 的原因: POSIX shell 必须留着 /dev/null 这个写入口,否则它自己都起不来'
|
|
71
|
+
sandbox_writable: 'terminal 在 workspace-write 下可写的目录:'
|
|
72
|
+
sandbox_covers: '走沙箱的工具: {tools}'
|
|
73
|
+
sandbox_excludes: '不在沙箱里(容易被误以为在): {tools}'
|
|
74
|
+
# 清单空了时说的那一句(方案 46 PR-4/PR-5 之后这是常态)。
|
|
75
|
+
# ⚠️ 后半句不能省:这一行是这一屏唯一会被读成「terminal 从此安全了」的地方,
|
|
76
|
+
# 而这一档买到的只有 OS 强制的**写入**边界 —— 读取和网络都是全开的。
|
|
77
|
+
sandbox_excludes_none: 'terminal 的三条路径(管道 / 后台 / PTY)都在沙箱里。注意它只管写入: 读取和网络不受限'
|
|
78
|
+
|
|
79
|
+
# `epoch schedule` 命令族(方案 45 §六)。**整条命令一起搬** ——
|
|
80
|
+
# 一条命令的帮助里一半走 catalog 一半硬编码,在非中文机器上就是半句中文半句英文。
|
|
81
|
+
schedule:
|
|
82
|
+
summary: '管理定时任务(触发交给操作系统,epoch 不需要一直开着)'
|
|
83
|
+
add: '新建一条定时任务并注册进系统调度器'
|
|
84
|
+
list: '列出全部定时任务'
|
|
85
|
+
show: '看一条任务的全部字段,含「它将不经确认地做这些事」'
|
|
86
|
+
enable: '启用一条任务'
|
|
87
|
+
disable: '停用一条任务(任务还在系统里,只是停着)'
|
|
88
|
+
rm: '删掉一条任务,同时撤掉系统里的注册'
|
|
89
|
+
run: '立刻手动跑一次 —— 不用等到下一个触发点才知道任务写对没有'
|
|
90
|
+
logs: '看最近几次运行'
|
|
91
|
+
fire: '由系统调度器调用。人也可以跑,但它不做交互:所有需要确认的操作会被记成欠条'
|
|
92
|
+
doctor: '体检:库里的任务和系统里注册的那些对不对得上'
|
|
93
|
+
arg_id: '任务 id(可以只写前几位)'
|
|
94
|
+
opt_name: '任务名'
|
|
95
|
+
opt_prompt: '提示词 —— 到点了让 agent 做什么'
|
|
96
|
+
opt_daily: '每天这个时刻(HH:mm,24 小时制)'
|
|
97
|
+
opt_weekly: '每周这几天,逗号分隔(MON,WED,FRI),要配 --at'
|
|
98
|
+
opt_monthly: '每月这几号,逗号分隔(1,15),要配 --at'
|
|
99
|
+
opt_every: '按间隔:5m/10m/15m/20m/30m/1h/2h/3h/4h/6h/8h/12h,时刻锚在午夜'
|
|
100
|
+
opt_once: '只跑一次的日期(YYYY-MM-DD),要配 --at'
|
|
101
|
+
opt_at: '时刻(HH:mm)。--weekly / --monthly / --once 要用'
|
|
102
|
+
opt_start: '生效区间开始(含当天)'
|
|
103
|
+
opt_end: '生效区间结束(含当天)'
|
|
104
|
+
opt_workdir: '工作目录。不给就跑在 ~/.epoch/automation/<id>/ 里,不继承当前目录'
|
|
105
|
+
opt_model: '这条任务用哪个模型,不给就用默认的'
|
|
106
|
+
opt_permission: '权限档,和对话里那枚胶囊同一套:plan / default / acceptEdits / bypass'
|
|
107
|
+
opt_allow_tool: '无人值守时这个工具免确认,可重复给'
|
|
108
|
+
opt_allow_operation: '无人值守时这类操作免确认,可重复给'
|
|
109
|
+
opt_allow_rule: '无人值守时命中这条规则的操作免确认,可重复给(如 terminal(git status))'
|
|
110
|
+
opt_max_turns: '最多跑几轮(缺省 20,比交互式的 50 更紧)'
|
|
111
|
+
opt_budget: '必填:这一次运行最多花多少美元'
|
|
112
|
+
opt_timeout: '墙钟超时(分钟,缺省 15)—— 唯一能拦住「一条命令挂住了」的那把尺子'
|
|
113
|
+
opt_disabled: '建好之后先停着,不立刻启用'
|
|
114
|
+
opt_bypass_ack: '确认「跳过全部检查」这一档下任务将不经确认地做任何事'
|
|
115
|
+
opt_fix: '把这一轮记下的欠条直接追加进任务的授权清单'
|
|
116
|
+
opt_limit: '看最近几次'
|
|
117
|
+
opt_tail: '把最近那一次录像里的正文打出来'
|
|
118
|
+
opt_home: '这一次运行的数据目录。系统调度器起的进程读不到 EPOCH_HOME / EPOCH_PROFILE,所以注册时会把它固化进命令行'
|
|
119
|
+
opt_repair: '按库里那份重新注册(DB 是真源)'
|
|
120
|
+
opt_prune: '撤掉系统里那些库里已经没有的任务'
|
|
121
|
+
|
|
122
|
+
# 插件市场目录拉不到(core/src/plugin/marketplace.ts)。
|
|
123
|
+
# `fetch_failed_private` 单独一条而不是拼一句提示:私有仓库的 raw 地址回的正好是
|
|
124
|
+
# 404,和「地址打错了」长得一模一样,那句话是这条错误里最值钱的部分。
|
|
125
|
+
# 启动时把上一段会话批准过的那份计划接回来(runtime/src/build.ts,方案 35 PR-2)。
|
|
126
|
+
# 它是一条启动诊断:计划活得比进程长,而屏幕上那张卡片是上一个进程画的 ——
|
|
127
|
+
# 不说这一句,用户看到的就是「空屏 + 模型莫名其妙在按某个纲做事」。
|
|
128
|
+
plan:
|
|
129
|
+
restored: '已恢复上次批准的计划'
|
|
130
|
+
|
|
131
|
+
# 会话检索的两条启动诊断(runtime/src/tools.ts,方案 48 PR-1)。
|
|
132
|
+
# 「跨工作区已打开」那条**只在打开时**才打:那是一次扩大可读范围的决定,
|
|
133
|
+
# 用户该在 `epoch doctor` 里看得见自己开过它;默认那一档不打,一行是噪音。
|
|
134
|
+
session_search:
|
|
135
|
+
unavailable: '会话库不可用,session_search 未注册'
|
|
136
|
+
all_workspaces: '已允许跨工作区检索(config.sessionSearch.allWorkspaces)'
|
|
137
|
+
|
|
138
|
+
# `epoch web` 的服务端错误体(packages/server,方案 30)。
|
|
139
|
+
# 服务端能用 t() 是因为 runtime 把它转出来了(server 不许 import infra)——
|
|
140
|
+
# 不转的话这几句就是三处未抽取的中文,而 check:i18n 是只许降不许升的棘轮。
|
|
141
|
+
web:
|
|
142
|
+
session_store_unavailable: '会话持久化不可用(SQLite 没起来),这个操作做不了'
|
|
143
|
+
title_required: 'title 必须是非空字符串'
|
|
144
|
+
session_cooled: '这个会话已被冷却(同时活跃的会话超出上限),重新打开它再发'
|
|
145
|
+
# `POST /api/sessions` 建不出会话(方案 30 §6.3 的多会话工厂)。**503 不是 500**:
|
|
146
|
+
# 服务本身好着,是它依赖的那样东西还没就位。500 会让人去重启服务,而正确的下一步
|
|
147
|
+
# 是去配一把 key —— 所以这句话必须把下一步说出来
|
|
148
|
+
session_unavailable: '建不出新会话:这个进程没有可用的模型 provider(配一把 API key 再试,或跑 epoch model)'
|
|
149
|
+
# `POST /api/sessions` 带了一个不认识的身份名(决定 20 ② 的底座)。
|
|
150
|
+
# **400 不是 404**:那条 URL 好好的,是请求体里的一个值不对(同 `bad-workspace`)。
|
|
151
|
+
# **也绝不静默降级成不分角色** —— 判据逐字照 `--agent`:用户显式点了一个身份,
|
|
152
|
+
# 给他跑一个能力范围不同的 agent 是最坏的结果。
|
|
153
|
+
# 名字必须带进这句话:浏览器上那份身份表可能已经旧了(用户刚删掉一个 .md),
|
|
154
|
+
# 不带名字的话屏幕上只有一句「没有这个身份」,而列表里明明还列着它
|
|
155
|
+
session_unknown_role: '没有名为「{role}」的身份 —— 能力页那份列表可能已经旧了,刷新一下再选'
|
|
156
|
+
workspace_must_be_string: 'workspace 必须是非空字符串(服务端的绝对路径);不想绑工作区就整个别给这个字段'
|
|
157
|
+
workspace_locked: '这个会话已经绑定了工作区,不能改({detail})。要换目录请新建一个会话 —— 中途换地盘会让前面那些工具调用里的相对路径指向另一个目录,而记录里没有任何地方写着从哪条开始换的'
|
|
158
|
+
# `GET /api/sessions/:id/workspace` 唯一的那种拒绝(决定 18,2026-08-15)。
|
|
159
|
+
#
|
|
160
|
+
# ⚠️ 它和「这个会话不使用工作区」**不是一回事**,那正是这条端点存在的理由:
|
|
161
|
+
# 绑定活在进程内存里、不落盘,所以上一个进程留下的会话在这儿一条记录都没有。
|
|
162
|
+
# 把它答成「没绑」,界面上写出来的就是「不使用工作区」—— 而那对一段昨天真在某个
|
|
163
|
+
# 仓库里干了一整轮活的会话是假话。码复用 `not-live-session`(同 `rewind_not_live`):
|
|
164
|
+
# 下一步同样是「把这段会话接回这个进程」
|
|
165
|
+
workspace_not_live: '这个进程手里没有这段会话,说不出它绑在哪儿 —— 工作区绑定活在进程内存里、不落盘(把这段会话接回这个进程才答得上来)'
|
|
166
|
+
# `POST /api/sessions/:id/workspace` 那条新路上的拒绝(方案 55 PR-1,2026-08-17)。
|
|
167
|
+
#
|
|
168
|
+
# 前两句讲**请求体的形状**(两支二选一),所以它们和讲路径的
|
|
169
|
+
# `workspace_must_be_string` 分开:那一句的下一步是「换一条路径」,
|
|
170
|
+
# 这两句的下一步是「改你给了哪几个字段」。「两支都给」不许挑一支执行 ——
|
|
171
|
+
# 那两支通向的是两段完全不同的会话(一段有地盘、一段没有)
|
|
172
|
+
workspace_bind_both: 'root 和 none 只能给一个:前者是「绑到这个目录」,后者是「明确不使用工作区」,两件事互斥'
|
|
173
|
+
workspace_bind_neither: '要么给 root(服务端的绝对路径),要么给 none: true(明确不使用工作区)'
|
|
174
|
+
# ⚠️ 这一句是决定 18「绑定后不给改」在新端点上的复述,而它必须**同时**盖住
|
|
175
|
+
# 「已经绑了目录」和「已经选过不使用」两档 —— 后者看着像「还空着」,但它是用户
|
|
176
|
+
# 明确按过的一档(决定 18 侧栏「任务」组的成员)。让它还能被改,等于给一段
|
|
177
|
+
# 说过「不要地盘」的会话补一个地盘,而它前面那些相对路径已经按别处解析过了
|
|
178
|
+
workspace_already_decided: '这个会话的工作区已经定下来了(绑了目录,或者明确选过不使用),不能再改 —— 要换目录请新建一个会话'
|
|
179
|
+
# `POST /api/sessions` 上那个 `deferWorkspace` 的两种拒绝(方案 55 PR-1)。
|
|
180
|
+
# 两条都是**不静默降级**,判据同上面 `workspace_must_be_string`:
|
|
181
|
+
# 悄悄丢掉一个客户端明确给了的字段,它会拿到一段和它以为的不一样的会话
|
|
182
|
+
workspace_defer_with_root: 'workspace 和 deferWorkspace 只能给一个:前者是「建的时候就绑在这儿」,后者是「先建出来,地盘等会儿再选」'
|
|
183
|
+
workspace_defer_needs_create: 'deferWorkspace 要配 create: true —— 不带 create 的那一发是「给我现有的那个会话」,而它的工作区在装配时就定下来了'
|
|
184
|
+
# `GET /api/workspaces/dirs` 的四种拒绝(方案 55 PR-2,2026-08-17)。
|
|
185
|
+
#
|
|
186
|
+
# ⚠️ **「没权限看」和「不存在」分两句、也分两个状态码**(403 / 404):
|
|
187
|
+
# 这是这条端点唯一一处必须拆开的地方 —— 用户的下一步动作完全不同(前者去改权限
|
|
188
|
+
# 或换个入口,后者去找那个目录到底在哪儿)。合成一句之后界面只能给一条两头都
|
|
189
|
+
# 不对的提示。四句都带上那条路径:这是本机 UI,说清楚比含糊有用
|
|
190
|
+
dirs_denied: '没权限列这个目录:{path}(它在那儿,但这个服务进程读不了它)'
|
|
191
|
+
dirs_missing: '这条路径不存在:{path}'
|
|
192
|
+
dirs_not_absolute: '要列的目录必须是绝对路径:{path} —— 这条路上没有「当前目录」这个概念'
|
|
193
|
+
dirs_not_a_directory: '这是一个文件,不是目录:{path}(挑它所在的那个目录)'
|
|
194
|
+
# `POST /api/workspaces` 的五种拒绝(方案 55 PR-3,2026-08-17)。
|
|
195
|
+
#
|
|
196
|
+
# ⚠️ **「已经存在」是 409 而不是复用**,这是这条路上最要紧的一句:
|
|
197
|
+
# 「新建」按下去得到一个**别人的目录**是最贵的错 —— 那儿可能有一份 CLAUDE.md、
|
|
198
|
+
# 一个 git 仓库、一堆别的东西,而用户以为自己拿到的是一个空目录。
|
|
199
|
+
# 所以这句话得同时给出两条出路(换名字 / 直接挑它)
|
|
200
|
+
mkdir_exists: '这个目录已经有了:{path} —— 换个名字,或者回到列表里直接挑它(「新建」不会复用一个已经存在的目录)'
|
|
201
|
+
# 「没权限」原样说出来,不翻译成一句「新建失败」:用户下一步要么换个地方、
|
|
202
|
+
# 要么去改权限,而这两件事都得先知道是权限的问题
|
|
203
|
+
mkdir_denied: '没权限在这儿建目录:{path}'
|
|
204
|
+
mkdir_parent_missing: '上一层目录不存在,所以建不了 {path} —— 先把它建出来(这条路不会替你补建父目录)'
|
|
205
|
+
mkdir_parent_not_absolute: '要在哪儿建必须给一条绝对路径:{parent}'
|
|
206
|
+
# 只收单段名字。要建两层(a/b)的话那是两次操作 —— 收 a/b 的代价不是多写一行
|
|
207
|
+
# recursive,是「建出来的到底是一个还是两个目录」变成一件要读实现才知道的事
|
|
208
|
+
mkdir_bad_name: '目录名只能是一段名字,不能带路径分隔符,也不能是 . 或 ..:{name}'
|
|
209
|
+
# 认不出来的 errno。**当成 500** —— 把一个陌生的失败说成「路径不对」,
|
|
210
|
+
# 会让用户去改一个没问题的输入
|
|
211
|
+
mkdir_failed: '建不出这个目录:{path}'
|
|
212
|
+
# `POST /api/sessions/:id/plan` 的两种拒绝(方案 35 的写那一半)。
|
|
213
|
+
#
|
|
214
|
+
# ⚠️ **`plan_mode_bootstrap_only` 2026-08-16 删了**(连同 `permission_` /
|
|
215
|
+
# `model_` 两条同族的)。它说的是「只有最先打开的那个会话进得了 plan 模式」,
|
|
216
|
+
# 而 plan 模式这一轮变成了一个会话一份(方案 30 §十四)—— 那道 409 整个拆了,
|
|
217
|
+
# 于是这句话再也没有发出去的机会。**留着比删掉更坏**:一句没有调用点的文案,
|
|
218
|
+
# 下一个人只能靠全文搜索才知道它已经不成立。
|
|
219
|
+
plan_mode_unavailable: '这个进程没有 plan 模式(权限层没起来)'
|
|
220
|
+
plan_mode_bad_action: 'action 必须是 {actions} 之一'
|
|
221
|
+
# `POST /api/sessions/:id/permission` 的两种拒绝(决定 20 ③,2026-08-15)。
|
|
222
|
+
#
|
|
223
|
+
# ⚠️ `permission_bootstrap_only` 2026-08-16 删了,理由同上面 plan 那条。
|
|
224
|
+
# 顺带记一笔:上一版那段注释写着「**不合并成一个 key**……将来两边的作用域
|
|
225
|
+
# 各自收窄时,合并过的那一句会同时对两条路说假话」—— 那个判断押对了,
|
|
226
|
+
# 只是收窄来得比预想的整齐:两条同一天一起没了。
|
|
227
|
+
permission_unavailable: '这个进程没有权限层,档位改不了(它此刻显示的是配置里那一档)'
|
|
228
|
+
# ⚠️ **不在这句话里说「哪一档被托管禁了」** —— 那不是一次坏请求,是一次
|
|
229
|
+
# 合法请求被这台机器上的策略挡下,走的是 200 + `ok:false`(判据在
|
|
230
|
+
# `WirePermissionSetResponse` 上)。这一句只管「你发来的不是一个档位名」
|
|
231
|
+
permission_bad_level: 'level 必须是 {levels} 之一'
|
|
232
|
+
# 检查点与回退的四种拒绝(方案 27 的 web 那一半)。
|
|
233
|
+
#
|
|
234
|
+
# `not_live` 和上面那条**不是一回事,2026-08-15 起才分得开**:这一条说的是
|
|
235
|
+
# 「这个进程手里没有这段会话的运行时」(上个进程留下的、或者被挤下去冷却了),
|
|
236
|
+
# 而检查点是**一个会话一份**的东西 —— 只要这个进程还握着它,退得了。
|
|
237
|
+
# 用户的下一步因此是「把这段会话接回来」,与 plan / model 那条正相反
|
|
238
|
+
rewind_not_live: '这个进程手里没有这段会话,退不了 —— 检查点要由跑着它的那个进程来退(先把这段会话接回来再试)'
|
|
239
|
+
rewind_bad_turn: 'turnIndex 必须是非负整数(取检查点列表里的那个序号)'
|
|
240
|
+
rewind_bad_scope: 'scope 必须是 {scopes} 之一'
|
|
241
|
+
# 这条**不静默降级成「一个都不覆盖」**:一个把它拼错的客户端会以为自己覆盖
|
|
242
|
+
# 成功了,而用户手写的改动其实还在。「全部覆盖」在这条路上说不出口是刻意的
|
|
243
|
+
rewind_bad_overwrite: 'overwrite 必须是一组文件路径(用户逐个确认过的那几个);没有要覆盖的就整个别给这个字段'
|
|
244
|
+
rewind_no_checkpoint: '没有第 {turn} 轮的检查点'
|
|
245
|
+
# `POST /api/sessions/:id/settings` 的五种拒绝(方案 43 §七的写那一半)。
|
|
246
|
+
#
|
|
247
|
+
# ⚠️ 这里**没有** `not_live` 那一档,和上面 plan / rewind 刻意不同:那两条改的是
|
|
248
|
+
# 这个进程里正在跑的那个会话的状态,这一条改的是**盘上的一个配置文件** ——
|
|
249
|
+
# 它跟哪个会话在跑没关系。多加一道闸只会让人在历史会话里改不了设置。
|
|
250
|
+
settings_write_bad_request: '请求体要有 key(设置项的点分路径)、layer({layers} 之一)和 value(字符串 / 数字 / 布尔)'
|
|
251
|
+
settings_write_unknown_key: '这个键改不了。这条路只写 model 和 maxTurns —— 其余每一项都只有内置默认和用户级两层,写进任何设置文件都不生效'
|
|
252
|
+
settings_write_bad_value: '这个值不合这一项的值域(比如 maxTurns 要正整数),没有写进去'
|
|
253
|
+
# 「不覆盖」是刻意的:坏文件是用户正在改的东西,替他 stringify 一份新的过去
|
|
254
|
+
# 等于把它删了。这一句要给出路径,因为下一步只能是他自己去编辑那个文件
|
|
255
|
+
settings_write_unwritable: '{path} 这份文件这一层认不出它的写法(或者它已经坏了),没有覆盖它 —— 请自己编辑那个文件'
|
|
256
|
+
settings_write_io: '写 {path} 失败:可能是没有写权限,或者那个目录建不出来'
|
|
257
|
+
# `POST /api/sessions/:id/model` 的三种拒绝(方案 26 的 web 那一半)。
|
|
258
|
+
#
|
|
259
|
+
# ⚠️ 这里**只有请求本身不成立的那几档**。「换不成」(没凭据 / 不认图片 /
|
|
260
|
+
# 窗口装不下)**不在这儿,也不该在这儿**:那三种是 200 + `ok:false` 的
|
|
261
|
+
# 载荷,网线上走的是**码 + 参数**,措辞在浏览器的两份 catalog 里
|
|
262
|
+
# (`web.ui.model.rej_*`)。判据在 `server/src/model.ts` 的文件头。
|
|
263
|
+
#
|
|
264
|
+
# ⚠️ `model_bootstrap_only` 和 `model_unavailable` 2026-08-16 一起删了。
|
|
265
|
+
# 前者同上面 plan / permission 两条(模型选择变成一个会话一份,那道 409 拆了);
|
|
266
|
+
# 后者是 503 `no-model-control` 那句话,而那道闸也没了 —— provider 层起不来时
|
|
267
|
+
# 这个进程一个会话都建不出来,于是第一道闸先答 404。判据在
|
|
268
|
+
# `server/src/model.ts` 的文件头。
|
|
269
|
+
model_bad_request: 'model 必须是非空字符串(模型名,或 provider/模型名);要回到配置里那个就给 null'
|
|
270
|
+
# 能力页那两条动作端点各自唯一的那种拒绝(方案 56)。
|
|
271
|
+
#
|
|
272
|
+
# 两句都**必须带名字**,理由同上面 `session_unknown_role`:浏览器手上那份清单
|
|
273
|
+
# 可能已经旧了(改过 mcp.json / 挪走过一个技能目录,而这个标签页是那之前打开的),
|
|
274
|
+
# 不带名字的话屏幕上只有一句「没有这个东西」,而列表里明明还列着它。
|
|
275
|
+
#
|
|
276
|
+
# ⚠️ 这两句的下一步都是**刷新**而不是重试 —— 它们说的是「你手上那份清单和这个
|
|
277
|
+
# 进程走散了」。「这一下没成功」是另一件事,走 200 + `ok:false`,措辞在浏览器
|
|
278
|
+
# 自己那两份 catalog 里(判据在 `WireMcpReconnectResponse` 上)
|
|
279
|
+
mcp_unknown_server: '这个进程里没有名为「{name}」的 MCP server —— 连接是启动时按 mcp.json 建的,你手上这份清单可能已经旧了,刷新一下再看'
|
|
280
|
+
skill_unknown: '没有名为「{name}」的技能 —— 能力页那份列表可能已经旧了,刷新一下再看'
|
|
281
|
+
# `POST /api/providers/:type/models` 唯一那种拒绝。
|
|
282
|
+
#
|
|
283
|
+
# ⚠️ **这一句是「这条 URL 上的东西不在」,不是「探不到模型」。** 后者有四档
|
|
284
|
+
# (没配 key / 探测失败 / 探到了但是空的 / 还在探),一律 200 + 一个 `status`
|
|
285
|
+
# 码,措辞在浏览器自己那份 catalog 里(`web.ui.model.probe_*`)——
|
|
286
|
+
# 判据在 `server/src/providers.ts` 的文件头。
|
|
287
|
+
#
|
|
288
|
+
# 「支持的那几家」是一个闭合集(protocol 的 `PROVIDER_INFOS`,**唯一一份**),
|
|
289
|
+
# 所以走到这一句只有两种可能:客户端手拼了一个名字,或者它手上那份清单是
|
|
290
|
+
# 旧版本发的。两种的下一步都是刷新,同上面那两句
|
|
291
|
+
provider_unknown: '不支持名为「{name}」的 provider —— 可选的那几家是一份固定清单,你手上这份可能已经旧了,刷新一下再看'
|
|
292
|
+
|
|
293
|
+
# `POST /api/mcp` 那五档拒绝(2026-08-18)。措辞跟着 mkdir 那三条走
|
|
294
|
+
# (`mkdir_bad_name` 一族),**不跟** `/api/schedules` 那一族 —— 那边把业务校验
|
|
295
|
+
# 做成 200 + 一串**码**,判据是「同一条校验在 CLI 和表单上本来就该是两句话」,
|
|
296
|
+
# 而这条路**没有 CLI 对手方**(全仓没有 `epoch mcp add`)。
|
|
297
|
+
#
|
|
298
|
+
# ⚠️ `mcp_add_lan_exposed` 是这条端点上唯一有安全语义的那一句,三个硬要求逐字同
|
|
299
|
+
# `role_add_lan_exposed`(说清为什么 / 说清出路 / 别写成「权限不足」),
|
|
300
|
+
# 但**「为什么」那一半不是同一句,别照抄**:那边是「正文会进往后每一轮的上下文」,
|
|
301
|
+
# 这边是「往 mcp.json 里写一条能起进程的命令,而它影响这台机器上以后每一个
|
|
302
|
+
# epoch 进程」。照抄就少说一半 —— 而这一句正是屏幕上唯一说得出「为什么偏偏是
|
|
303
|
+
# 这一条不给」的东西。
|
|
304
|
+
#
|
|
305
|
+
# ⚠️ 它和 `mcp_config_lan_exposed` 也**不共用**(那三条是刻意共用的):那一句的
|
|
306
|
+
# 正题是「连看都不给,因为 env 是明文密钥」,而这条路一个字节都不往外发。
|
|
307
|
+
# 出路那两条倒是同一条 —— 那是因为它们确实是同一条。
|
|
308
|
+
#
|
|
309
|
+
# ⚠️ `mcp_add_unwritable` 里那个 {detail} 是**底层原样那句话**(「顶层不是一个
|
|
310
|
+
# JSON 对象」「EACCES: permission denied」…),这一层不改写它:那是屏幕上唯一
|
|
311
|
+
# 说得出「我的 mcp.json 到底哪儿不对」的东西。
|
|
312
|
+
#
|
|
313
|
+
# ⚠️ 撞名那一句要说清**三处都算**(文件里的、宿主的、插件的),否则用户会对着
|
|
314
|
+
# 一个在自己 mcp.json 里根本找不到那个名字的报错发愣
|
|
315
|
+
mcp_add_lan_exposed: '这一档下加不了 MCP server —— 服务是用 --host 绑到回环之外的,而我们没法确认发出这一下的浏览器就在这台机器上。加一台等于往 mcp.json 里写一条「启动哪个可执行文件」,而那台当场就会被起起来,往后这台机器上每一次跑 epoch(含 CLI 和 TUI)也都会照着它起。出路两条:到跑着服务的那台机器上直接改那个文件;或者去掉 --host 重启一次。'
|
|
316
|
+
mcp_add_bad_transport: '传输类型只能是 stdio / sse / http'
|
|
317
|
+
mcp_add_bad_name: 'server 名「{name}」不能用 —— 它会原样变成工具名的一部分,所以只收字母数字开头、由字母数字和 _ - 组成的名字(最长 48)。手写 mcp.json 不受这条管,这条只管从界面加的'
|
|
318
|
+
mcp_add_taken: '已经有一台叫「{name}」的 MCP server 了(可能在你的 mcp.json 里,也可能是宿主或某个插件带来的)—— 换个名字。这里不覆盖也不自动改名:覆盖要删掉你手写的那一段,改名会让工具名和你敲进去的对不上'
|
|
319
|
+
mcp_add_unwritable: '没往 mcp.json 里写任何东西 —— {detail}。先去那个文件那儿看一眼'
|
|
320
|
+
|
|
321
|
+
# `GET` / `PUT /api/mcp/config` 和 `POST /api/mcp/config/apply`(2026-08-18)。
|
|
322
|
+
#
|
|
323
|
+
# ⚠️ 这一族里**没有**「不是合法 JSON」那一句 —— 那句话是 runtime 那侧
|
|
324
|
+
# `mcp_config.invalid_json` 原样转发上来的(它里面带着 JSON.parse 说的「错在第几个
|
|
325
|
+
# 字符」,而这一层编不出更好的)。判据同上面 mcp_add 那族只有四句。
|
|
326
|
+
#
|
|
327
|
+
# ⚠️ `mcp_config_lan_exposed` **三条端点共用一句**,含那条 GET:出路对三条是同一
|
|
328
|
+
# 条,分成三句只会让同一个决定有三份措辞。而它必须说清「为什么连看都不给」——
|
|
329
|
+
# 那份文件里的 env 是明文密钥,而它同时写得下「启动哪个可执行文件」。
|
|
330
|
+
#
|
|
331
|
+
# ⚠️ `mcp_config_stale` 要说清**下一步不是重试**:盘上那份已经被别人(多半是他
|
|
332
|
+
# 自己的编辑器,或者「添加 server」那张表单)改过了,硬写下去会盖掉那一笔。
|
|
333
|
+
#
|
|
334
|
+
# ⚠️ `mcp_config_no_servers` 是这条路最要紧的一句:它拦下的是「一次手滑把所有
|
|
335
|
+
# MCP 全断掉」。所以它要点名最常见的那个成因(把 servers 写成了 mcpServers),
|
|
336
|
+
# 并且说清「真要清空就写成 servers: {}」——否则用户只会觉得这条路坏了
|
|
337
|
+
mcp_config_lan_exposed: '这一档下 mcp.json 看不了也改不了 —— 服务是用 --host 绑到回环之外的,而我们没法确认发出这一下的浏览器就在这台机器上。那份文件里的环境变量是明文存的(可能有密钥),而它同时写得下「启动哪个可执行文件」。出路两条:到跑着服务的那台机器上直接改那个文件;或者去掉 --host 重启一次。'
|
|
338
|
+
mcp_config_unreadable: '读不了 mcp.json —— {detail}。先去那个文件那儿看一眼'
|
|
339
|
+
mcp_config_bad_body: '这一发的请求体不对:text 和 revision 两格都得是字符串,而 revision 要用上一次读到的那个'
|
|
340
|
+
mcp_config_stale: '一个字节都没写 —— 盘上那份 mcp.json 已经不是你读到的那一份了(可能是你自己的编辑器刚存过,也可能是「添加 server」那张表单刚插了一台)。照着写下去会把那一笔盖掉,所以这里停住了:先重新读一遍,看清现在盘上是什么'
|
|
341
|
+
mcp_config_unwritable: '没往 mcp.json 里写任何东西 —— {detail}。先去那个文件那儿看一眼'
|
|
342
|
+
mcp_config_no_servers: '一台都没动 —— 盘上那份 mcp.json 读出来一台 server 都没有,而且它带着诊断:{detail}。照着应用会把你现在所有的 MCP 全部断开,所以这里停住了。最常见的原因是把 servers 这个键写成了 mcpServers。真要清空的话,写成 "servers": {} 再按一次'
|
|
343
|
+
|
|
344
|
+
# `POST /api/roles`(「新建一个身份」,2026-08-18)。
|
|
345
|
+
#
|
|
346
|
+
# ⚠️ 这一族只有**两句**,而那条端点有六档拒绝 —— 另外四档(名字 / 撞名 /
|
|
347
|
+
# 工具名 / 写不下去)的那句话是 runtime 那侧 `role_write.*` 原样转发上来的。
|
|
348
|
+
# 在这儿再写一份的下场是同一个拒绝有两种措辞,而它们迟早不一样。
|
|
349
|
+
#
|
|
350
|
+
# ⚠️ `role_add_lan_exposed` 是这条端点的正题,措辞有三个硬要求:
|
|
351
|
+
# 1. 说清**为什么**(那段文字会进每一轮的上下文,而这一档下我们不知道你是谁);
|
|
352
|
+
# 2. 说清**出路**(去那台机器上手写一份 / 不带 --host 再起一次)——
|
|
353
|
+
# 只说「不允许」的话,用户唯一的结论是「这个功能坏了」;
|
|
354
|
+
# 3. **不要写成「权限不足」**:他的凭据完全正确,是这条路在这一档下不开。
|
|
355
|
+
role_add_lan_exposed: '这一档下建不了身份 —— 服务是用 --host 绑到回环之外的,而我们没法确认发出这一下的浏览器就在这台机器上。身份的正文会进往后每一轮的上下文(描述那一格还会进每一次派活的工具描述),所以这条路只在默认的回环绑定下开。出路两条:到跑着服务的那台机器上,往 ~/.epoch/agents/ 里放一个 .md;或者去掉 --host 重启一次。'
|
|
356
|
+
role_add_bad_field: '这一格填不下去:{detail}'
|
|
357
|
+
|
|
358
|
+
# `/api/schedules*` 那九条(方案 45 PR-3)。
|
|
359
|
+
#
|
|
360
|
+
# ⚠️ **这一组只有两类:「这个 JSON 读不懂」和「这条 URL 上的东西不在」。**
|
|
361
|
+
# 一条业务校验都不在这儿 —— 名字空着、间隔填了 7 分钟、「跳过全部检查」没绑
|
|
362
|
+
# 工作区,那些走 200 + `issues`(一串**码**),措辞在浏览器自己那份 catalog 里
|
|
363
|
+
# (`web.ui.auto.issue.*`)。判据两条:服务端的中文一律不上网线(同
|
|
364
|
+
# `wire-settings`);以及同一条校验在 CLI 和表单上**本来就该是两句话** ——
|
|
365
|
+
# CLI 说「--budget 必填」,而表单上那句话里没有 --budget 这个东西
|
|
366
|
+
schedule_unknown: '没有 id 为 {id} 的定时任务 —— 你手上这份列表可能已经旧了(另一个标签页或者命令行删过它),刷新一下再看'
|
|
367
|
+
schedule_run_unknown: '这条任务的运行记录里没有 {runId} —— 运行记录有留存上限(每条任务最近 20 次),旧的那几次会被清掉'
|
|
368
|
+
schedule_bad_field: '{field} 这一格的类型不对(是这个 JSON 读不懂,不是这个值不合理)'
|
|
369
|
+
schedule_bad_permission: '权限档不认识 —— 这条路只收权限那枚胶囊上的那几档'
|
|
370
|
+
schedule_bad_trigger: '读不懂这个触发器:kind 要是 cron / interval / once,而且每一档得带上自己那几个字段'
|
|
371
|
+
schedule_bad_operation: 'allowOperations 里有认不出来的操作类别,只收这几个:{types}'
|
|
372
|
+
schedule_create_incomplete: '建一条定时任务至少要给 name / prompt / permission / trigger / maxBudgetUsd 五样'
|
|
373
|
+
# ⚠️ 这一句是全方案唯一一处**刻意增加的摩擦**在网线上那一面(§3.5):
|
|
374
|
+
# 服务端**不兜一个默认值**。一个假默认会让「一觉醒来 40 美元」变成我们的错,
|
|
375
|
+
# 所以宁可让一个漏发它的客户端拿到 400
|
|
376
|
+
schedule_budget_required: 'maxBudgetUsd 必填,而且没有默认值 —— 一个假默认会让「一觉醒来 40 美元」变成我们的错'
|
|
377
|
+
schedule_fix_needs_run: '要给 runId:欠条挂在某一次运行上,而规则从库里那一条取(不收请求体里回传的规则)'
|
|
378
|
+
|
|
379
|
+
# ---- 浏览器界面的字(方案 43 §2.9)----
|
|
380
|
+
#
|
|
381
|
+
# 这一组和上面那几条**不是一路人**:上面是服务端生成、随 wire 下去的错误体,
|
|
382
|
+
# 这里是**浏览器里前端自己写死的字**。它们由 vite 在构建期编进 web 的产物
|
|
383
|
+
# (`packages/web/vite/locales.ts`),走的是 web 自己那份 `t()`。
|
|
384
|
+
#
|
|
385
|
+
# ⚠️ 切成英文之后,服务端来的那半(上面那几条、诊断 detail、权限拒绝理由)
|
|
386
|
+
# **仍然跟着服务端进程的语言走**,所以中文系统上起的 server + 切成英文的浏览器
|
|
387
|
+
# = 半中半英。那是方案 43 验收 31 明写的**预期形态**,不是漏翻。
|
|
388
|
+
ui:
|
|
389
|
+
conn:
|
|
390
|
+
connecting: '连接中'
|
|
391
|
+
open: '已连接'
|
|
392
|
+
reconnecting: '重连中'
|
|
393
|
+
closed: '已断开'
|
|
394
|
+
|
|
395
|
+
topbar:
|
|
396
|
+
# 会话列表里查不到标题时也用这一句(方案 30 PR-2 起两处共用)
|
|
397
|
+
untitled: '未命名会话'
|
|
398
|
+
resyncing: '同步中'
|
|
399
|
+
# ⚠️ 这一句是**两处共用**:顶栏那枚开关的 aria-label/title,以及检视面板
|
|
400
|
+
# 那个 `<aside>` 的 aria-label(≤1180px 上它是一层抽屉,推出来那一下焦点
|
|
401
|
+
# 落在根元素上,读屏念的就是这一句)。开关和它开的那样东西同名是刻意的
|
|
402
|
+
inspector: '检视面板'
|
|
403
|
+
# 侧栏那枚开关 + 侧栏 `<aside>` 的 aria-label,两处共用,同上一条。
|
|
404
|
+
# ⚠️ 那枚开关**只在 ≤860px 上存在**(宽屏侧栏是常驻栏,按下去屏幕上不会
|
|
405
|
+
# 有任何变化 —— 决定 20 ①),判据在 `shell/topbar.tsx` 的 `TopbarProps.sidebar`。
|
|
406
|
+
# 说「会话侧栏」而不是「菜单」:那一栏里是会话列表、新建会话、能力页、
|
|
407
|
+
# 设置和用量,「菜单」既没说出装的是什么,也和 `.menu` 那套弹层撞名
|
|
408
|
+
sidebar: '会话侧栏'
|
|
409
|
+
# 顶栏那个模型名旁边那句话(方案 30 §10.4 第 2 条 → §六)。
|
|
410
|
+
#
|
|
411
|
+
# ⚠️ 2026-08-16 换了 key,也换了话。上一版(`model_one_per_process`)说的是
|
|
412
|
+
# 「模型这个进程只有一份,换模型要回到最先打开的那个会话」—— 模型选择这一轮
|
|
413
|
+
# 变成一个会话一份之后,那半句是**假话**,连同它引的那个码
|
|
414
|
+
# (`bootstrap-session-only`)一起撤掉。
|
|
415
|
+
#
|
|
416
|
+
# 留下来的是上一版那两件事里的第二件,而它才是真会害人的那一半:这一格退回
|
|
417
|
+
# `config.model`(这次启动的默认值)的条件收窄成了「这个进程没在跑这段会话」,
|
|
418
|
+
# 那时这段会话当年用的是哪个模型,顶栏答不上来。
|
|
419
|
+
# ⚠️ **不许说成「换模型要去别处」**:这段会话根本没在跑,没有哪儿能换。
|
|
420
|
+
model_not_live: '这段会话不在这个进程里跑,这里写的是这次启动的默认模型 —— 它当时用的可能不是这一个。'
|
|
421
|
+
|
|
422
|
+
# 顶栏那张换模型菜单(方案 26 的 web 那一半)。
|
|
423
|
+
#
|
|
424
|
+
# ⚠️ 这一组是**拒绝原因的措辞**,而网线上走的是码 + 参数
|
|
425
|
+
# (`WireModelRejection` / `WireModelCaveat`)—— 服务端一个中文字都不发,
|
|
426
|
+
# 判据在 `server/src/model.ts` 的文件头。三种拒绝**各说各的**,
|
|
427
|
+
# 一句都不许合并成「换不了」:它们的下一步动作完全不同(去配 key /
|
|
428
|
+
# 换个认图的模型 / 先清一段),而这几句话的全部用处就是说出那一步。
|
|
429
|
+
model:
|
|
430
|
+
# 这张菜单改的是**此刻这个会话**用哪个模型,不是设置页那个「下次启动的默认值」
|
|
431
|
+
label: '这个会话此刻用的模型'
|
|
432
|
+
trigger: '{model} —— 点开换模型'
|
|
433
|
+
current: '{provider} · 正在用'
|
|
434
|
+
reset: '回到配置里那个'
|
|
435
|
+
# **没有模型清单**,判据在 `shell/model-menu.tsx` 的文件头:
|
|
436
|
+
# provider 接受的模型名是开集,画一张闭合的单子会漏掉用户最需要手敲的那些
|
|
437
|
+
placeholder: '敲一个模型名,回车'
|
|
438
|
+
rej_no_credential: '{provider} 没有可用凭据,切不过去。设一个 {env},或者跑 epoch model 配置'
|
|
439
|
+
# `envVar` 为 null 那一档:那家没有约定的环境变量名,
|
|
440
|
+
# 硬把占位留在句子里会让用户去设一个叫「null」的变量
|
|
441
|
+
rej_no_credential_bare: '{provider} 没有可用凭据,切不过去。跑 epoch model 配置'
|
|
442
|
+
rej_images: '{model} 不认图片,而这个会话里已经有图片了。换模型不清空上下文,切过去下一轮必然被 provider 拒'
|
|
443
|
+
rej_context: '上下文已用 {used},超过 {model} 的窗口({window})。先开一段新会话,或者换个窗口更大的模型'
|
|
444
|
+
cav_context_tight: '换过去了,但上下文已用 {used},接近 {model} 的窗口({window}),可能撑不过几轮'
|
|
445
|
+
cav_no_tools: '换过去了,但 {model} 不支持工具调用 —— 它只能聊天,读不了也写不了文件'
|
|
446
|
+
# provider 选择 + 模型建议那一块(`web/src/model/picker.tsx`,两处界面共用)。
|
|
447
|
+
#
|
|
448
|
+
# ⚠️ **这一组是「探不到的时候说什么」,五档一句都不许合并成「没有可用模型」。**
|
|
449
|
+
# 判据是下一步动作不同:等一下 / 去配 key / 查网络 / 直接敲 / 挑一个。
|
|
450
|
+
# 合并之后这几句话就没有宾语了 —— 同上面那三种拒绝的判据,逐字。
|
|
451
|
+
pick_provider: 'Provider'
|
|
452
|
+
pick_provider_none: '挑一家…'
|
|
453
|
+
# 没配 key 的那家**在选中之前就标出来**:不标的话用户会挨个点过去,
|
|
454
|
+
# 每点一家等一次 10 秒超时,才发现自己一把 key 都没配。
|
|
455
|
+
# ⚠️ 不需要 key 的那家(Ollama)不挂这个后缀 —— 对它说「没配 key」是假话
|
|
456
|
+
provider_no_key: '{label}(没配 key)'
|
|
457
|
+
# ⚠️ **必须说「不用等」**:这一发是真的网络请求,而手敲那条路一个字都没被
|
|
458
|
+
# 它挡住(那个输入框在探测期间不许被禁用)。不说的话用户会对着它干等
|
|
459
|
+
probe_probing: '正在问 {provider} 有哪些模型…不用等,上面直接敲一个也行'
|
|
460
|
+
# 「先配一把 key」,不是「这个 provider 没有模型」—— 后者是假话:它有,
|
|
461
|
+
# 只是我们没资格问。空 key 进 discoverModels() 直接跳过 live 拉取
|
|
462
|
+
probe_no_key: '{provider} 还没配 key,问不了它的模型清单。设一个 {env},或者跑 epoch model 配一把'
|
|
463
|
+
# 那家没有约定的环境变量名时换一句说,同 `rej_no_credential_bare`
|
|
464
|
+
probe_no_key_bare: '{provider} 还没配 key,问不了它的模型清单。跑 epoch model 配一把'
|
|
465
|
+
# 探测失败分两句,因为**用户看到的东西不一样**:一份是上次探到的,
|
|
466
|
+
# 一份是我们写死的内置目录。合成一句的话「下面这些能不能用」答不了
|
|
467
|
+
probe_failed_cache: '没问通 {provider}(网络 / key 不对 / 那个地址没有 /models)。下面是上次探到的那一份,可能已经过期 —— 也可以直接敲'
|
|
468
|
+
probe_failed_static: '没问通 {provider}(网络 / key 不对 / 那个地址没有 /models)。下面是内置的那一份,可能已经过期 —— 也可以直接敲'
|
|
469
|
+
probe_empty: '{provider} 一个模型名都没给出来,内置清单里也是空的 —— 直接敲一个吧'
|
|
470
|
+
probe_ok: '从 {provider} 问到 {count} 个'
|
|
471
|
+
# ⚠️ 这一句是「模型那一层不是闭合单子」的**可见形态**,不是客套话:
|
|
472
|
+
# 一串名字底下不写它,读起来就是一张全集,而那正是老判据拦下来的事
|
|
473
|
+
suggest_note: '下面这些是探到的建议,不是全部 —— 新模型、自建的多半不在里面,照旧直接敲'
|
|
474
|
+
refresh: '重新探'
|
|
475
|
+
# 探测结果落盘缓存 24 小时。没有这一下,用户换了 key 或者 provider 上了
|
|
476
|
+
# 新模型,看到的永远是缓存那一份,而屏幕上没有任何东西说得出「你看的是旧的」
|
|
477
|
+
refresh_hint: '绕开缓存,重新问一次这家 provider'
|
|
478
|
+
|
|
479
|
+
# 侧栏最上面那一行「新建会话」+ 它身上那张工作区菜单(方案 30 验收 8)
|
|
480
|
+
newsession:
|
|
481
|
+
# ⚠️ **不带「⌘N」**,稿子上有。浏览器把 ⌘N / Ctrl+N 留给自己(开新窗口),
|
|
482
|
+
# 那个 keydown 到不了页面,preventDefault 也拦不住 —— 画一枚按不动的
|
|
483
|
+
# 键位芯片,和画一个点了没反应的按钮是同一件事(决定 20 ①)。
|
|
484
|
+
# 判据全文在 session/new-session.tsx 的文件头
|
|
485
|
+
open: '新建会话'
|
|
486
|
+
# ✅ 2026-08-17(方案 55 PR-1):这一行身上那张工作区菜单**整个搬去了输入框
|
|
487
|
+
# 底栏**(稿子决定 18 画它的位置),于是它的五句话跟着搬到了
|
|
488
|
+
# `web.ui.composer.workspace_pick*` 底下 —— `menu_label` / `here_current` /
|
|
489
|
+
# `no_workspace` / `no_workspace_detail` / `list_note`,措辞一个字没改。
|
|
490
|
+
#
|
|
491
|
+
# ⚠️ **别在这儿补一句「工作区」相关的新文案。** 这一行现在是一个纯动作
|
|
492
|
+
# (点一下就有一段新会话),它一个字都不说地盘的事 —— 那件事底栏那一格说。
|
|
493
|
+
# 搬走的判据和「为什么当初它在这儿」全文在 session/new-session.tsx 的文件头。
|
|
494
|
+
|
|
495
|
+
# 目录选择器(方案 55 PR-2,2026-08-17)—— 工作区菜单里那个二级视图。
|
|
496
|
+
# 稿子那张菜单里「打开本地文件夹」按下去看到的就是它。
|
|
497
|
+
dirpick:
|
|
498
|
+
back: '返回'
|
|
499
|
+
# 起点锚那一屏。**不说「根目录」** —— 它列的是三组「你多半想去的地方」
|
|
500
|
+
# (home / 服务进程 cwd / 已知工作区),而不是文件系统根。
|
|
501
|
+
# 别让人从 / 开始爬,那是这类 picker 唯一一个必然会被骂的地方
|
|
502
|
+
anchors: '常去的地方'
|
|
503
|
+
up: '往上一层'
|
|
504
|
+
# 深目录真的要时间,所以这一行画出来而不是留白 —— 留白读起来像「这儿是空的」
|
|
505
|
+
loading: '读取中…'
|
|
506
|
+
# 「这一层没有子目录」是一句真话,而且它有下一步(挑这一层本身)
|
|
507
|
+
empty: '这一层没有子目录 —— 可以直接挑它,或者在下面敲一条路径'
|
|
508
|
+
# ⚠️ **截断必须说出来**:静默截断在屏幕上读起来就是「这一层就这些」,
|
|
509
|
+
# 而用户会据此断定自己要的那个目录不在这儿。判据在服务端那个 omitted 字段上
|
|
510
|
+
omitted: '这一层还有 {count} 个没列出来(一层最多列 1000 个)—— 要那些的话在下面敲路径'
|
|
511
|
+
# 「显示隐藏目录」那个勾(2026-08-18)。⚠️ 两句话,因为它们说的不是一件事:
|
|
512
|
+
# 不带数字那一句是「这张清单按什么规则列」,带数字那一句还多说了一件**可核对
|
|
513
|
+
# 的事实** —— 这一层刚被藏掉了 N 个。判据逐字同上面 omitted:静默过滤在屏幕上
|
|
514
|
+
# 读起来就是「这台机器上没有那种目录」,而 ~/.claude 那种目录用户真的会去挑。
|
|
515
|
+
#
|
|
516
|
+
# ⚠️ 措辞用「隐藏目录」而不是「点目录」/「dotfile」:前者是 Finder 和资源
|
|
517
|
+
# 管理器里那个词,用户在别处已经学过它;后两个是开发者行话。
|
|
518
|
+
# 也**不说「显示隐藏文件」** —— 这条端点只列目录,一个文件都不列
|
|
519
|
+
show_hidden: '显示隐藏目录(. 开头的)'
|
|
520
|
+
show_hidden_count: '显示隐藏目录(. 开头的,这一层有 {count} 个)'
|
|
521
|
+
# 列不开的那一行**画出来但点不动**。这句话进 title:一个灰按钮只说「点不了」,
|
|
522
|
+
# 说不出「为什么」,而这个为什么来自服务进程那台机器上的文件权限
|
|
523
|
+
unreadable: '没权限进这个目录:{path}'
|
|
524
|
+
into: '进入 {name}'
|
|
525
|
+
# 「我以前在这儿干过活」。⚠️ 它**不是**「这儿是个 git 仓库」——
|
|
526
|
+
# 后者要跑一次 git,而这一层可能有一千个目录
|
|
527
|
+
known: '来过'
|
|
528
|
+
# 第二个入口。它零成本,而且覆盖两件点不到的事:深路径、以及用户本来就知道
|
|
529
|
+
# 自己要哪儿。(点目录原来也在这张单子上,2026-08-18 之后上面那个开关管它了 ——
|
|
530
|
+
# 手敲入口只有已经知道自己丢了什么的人才会想去用。)
|
|
531
|
+
# ⚠️ **这一侧不预校验** —— 校验交给服务端 bind() 那道现成的门,
|
|
532
|
+
# 预校验等于在两处各写一份判据,而它们迟早走散
|
|
533
|
+
manual_label: '或者直接敲一条绝对路径'
|
|
534
|
+
manual_hint: '例如 /Users/you/work/repo'
|
|
535
|
+
manual_go: '就用这个'
|
|
536
|
+
# 「新建工作区」(方案 55 PR-3)—— 稿子把它画成一个模态,这儿它是这一屏底下
|
|
537
|
+
# 的一格。判据是决定 5(全稿只剩一个模态,而那一个就是它):这张 picker
|
|
538
|
+
# 已经开着,而「在哪儿建」这个宾语正是屏幕上那一层,再叠一层遮罩只会遮住它
|
|
539
|
+
new_label: '在这一层里新建一个目录'
|
|
540
|
+
new_hint: '例如 my-project'
|
|
541
|
+
new_go: '新建'
|
|
542
|
+
# ⚠️ 这句话**必须在按之前**就在屏幕上。稿子那个模态原来写的是「新建的目录
|
|
543
|
+
# 默认信任」,而那一半判成不做(方案 55 §2.6,两条判据:信任的写入口刻意
|
|
544
|
+
# 不上网线;而且「刚建的是空目录」这条判据本身站不住 —— 空只在此刻成立,
|
|
545
|
+
# 而第一件要发生的事就是让 agent 往里写东西)。用户按下这一下时对信任状态的
|
|
546
|
+
# 预期,正是这句话要纠正的
|
|
547
|
+
new_trust_note: '新建的目录和挑一个既有目录同档:都未信任 —— 它的 .epoch/settings.json 和 CLAUDE.md 不会生效。要授信跑 epoch trust add。'
|
|
548
|
+
|
|
549
|
+
# 新会话那一屏(设计稿 tpl-blank)。⚠️ 空屏是一次邀请,不是一句「暂无数据」——
|
|
550
|
+
# 所以发不出消息的时候这一屏**一个字都不画**,判据在 session/blank.tsx 文件头
|
|
551
|
+
#
|
|
552
|
+
# ⚠️ 2026-08-18 这一屏收成**只有 title 一条**。撤掉的是眉标 / lede / 三条种子 /
|
|
553
|
+
# 底下那句会话作用域的话 —— 四样各自的账在 session/blank.tsx 文件头那张表里。
|
|
554
|
+
# 一句话版:三条种子「太有指向性」(用户原话),把「这个 agent 是干什么的」
|
|
555
|
+
# 窄成了「它是个改代码的」,而这一屏是第一眼。**别往回加。**
|
|
556
|
+
blank:
|
|
557
|
+
title: '今天想做点什么?'
|
|
558
|
+
|
|
559
|
+
sidebar:
|
|
560
|
+
search: '搜索会话'
|
|
561
|
+
# 三个词并列,因为这一行通往的就是三栏 —— 起一个概括的名字(「能力」)放在
|
|
562
|
+
# 侧栏的话,用户点进去之前不知道里面有什么
|
|
563
|
+
capability: '身份 · 技能 · 连接器'
|
|
564
|
+
# 一级导航的第四个去处(方案 45 §7.1)。**位置就是产品层级**(决定 14):
|
|
565
|
+
# 放进设置里,用户会把定时任务读成一个配置项
|
|
566
|
+
automation: '自动化'
|
|
567
|
+
settings: '设置'
|
|
568
|
+
session: '会话'
|
|
569
|
+
# ⚠️ 这两条是 `GET /api/config` 的 `usageScope` 两档,**不是同一句话的两种说法**:
|
|
570
|
+
# `session` 口径下这个数是 `BudgetStore` 恢复出来的会话累计;`run` 口径下
|
|
571
|
+
# store 没起来、每轮从零重算,那时「本会话」这个说法压根没有对应的数存在过
|
|
572
|
+
# (前八轮的账谁也没有)。所以不许把 `usage_run` 写成「本会话用量(本轮)」
|
|
573
|
+
# 之类的补丁 —— 那还是在承诺一个会话总量。判据全文在 shell/sidebar.tsx 文件头
|
|
574
|
+
usage_session: '本会话用量'
|
|
575
|
+
# 「最近一轮」而不是「本轮」:回合跑完之后这个数还挂着,那时候没有「本轮」
|
|
576
|
+
usage_run: '最近一轮用量'
|
|
577
|
+
# 金额(决定 8)。**只在配了 `budget.maxCostUsd` 时出现** —— 没有那个上限
|
|
578
|
+
# 就没有可对照的天花板,一个没人验得动的数字不该常驻在这儿。
|
|
579
|
+
# 「约」不是客气话:单价是一份写死在代码里的静态快照。
|
|
580
|
+
# ⚠️ 它**没有 `_run` 那一档**,而上面那两行有:`run` 口径下这一行整个不画。
|
|
581
|
+
# token 换个说法就诚实了,钱换不了 —— 比值问的是「离我设的天花板还有多远」,
|
|
582
|
+
# 而 `run` 口径就是「store 没起来」,会话总共花了多少**没有任何一处知道**
|
|
583
|
+
cost: '花费'
|
|
584
|
+
cost_of: '约 ${spent} / ${limit}'
|
|
585
|
+
# 徽标挂在金额上,**不挂在 token 数上**:token 是 provider 报的实测值,
|
|
586
|
+
# 而钱是我们拿本地那张价目表乘出来的
|
|
587
|
+
cost_est: '估算'
|
|
588
|
+
# 后台任务(方案 36 PR-2)。**一条都没有时整行不画**(决定 20 ①)。
|
|
589
|
+
# ⚠️ 不叫「本会话后台任务」:那张表是服务进程级的一张,不按会话分
|
|
590
|
+
# (判据见 server/src/tasks.ts 的文件头)。它紧挨着上面那行「本会话用量」,
|
|
591
|
+
# 作用域差一个字用户就会看串
|
|
592
|
+
tasks: '后台任务'
|
|
593
|
+
# 「跑着几个 / 一共几个」。已结束的任务仍然在表里,只报总数会让一屏跑完的
|
|
594
|
+
# 任务看着像还在烧 CPU;只报在跑的又答不上「刚才那条命令跑完没有」
|
|
595
|
+
tasks_of: '{running} 在跑 / {total}'
|
|
596
|
+
# 中文用全角冒号不带空格,英文用半角冒号带空格 —— 写死一个的话总有一边排版是错的
|
|
597
|
+
diagnostic: '{module}:{detail}'
|
|
598
|
+
|
|
599
|
+
# 侧栏那份会话列表(方案 30 PR-2,决定 7 / 18)
|
|
600
|
+
slist:
|
|
601
|
+
# 分三组,判据是「这一行说得出什么」,不是「重不重要」(见
|
|
602
|
+
# shell/session-list.tsx 文件头)。
|
|
603
|
+
#
|
|
604
|
+
# ⚠️ 「任务」这个词有定义,别拿它当兜底桶:它是决定 18 的 `none` 档
|
|
605
|
+
# (「明确选过不使用工作区」)。2026-08-18 之前上一个进程留下的会话也堆在
|
|
606
|
+
# 这一组里 —— 那等于替一段说不出地盘的会话宣布它「不使用工作区」,
|
|
607
|
+
# 而同一屏的底栏那一格写的是「工作区未知」。那两句话不能同时为真
|
|
608
|
+
tasks: '任务'
|
|
609
|
+
spaces: '空间'
|
|
610
|
+
# 第三组(2026-08-18):网线上那一格是 `unknown` 的那些。
|
|
611
|
+
# 组名只说得出「它们是早先的」,「为什么不在任何一个空间下面」全靠下面那句。
|
|
612
|
+
#
|
|
613
|
+
# ⚠️ 2026-08-19 这一组**收窄**了:判据从「`live: false` 且没有工作区」换成
|
|
614
|
+
# 网线上明写的 `unknown` 档,于是它只剩**会话库 v7 之前的老行**(那两列是
|
|
615
|
+
# NULL)。重启之后那些当初绑过空间 / 当初明确选了不使用的会话不再落进来 ——
|
|
616
|
+
# 前者回它自己的空间,后者进「任务」。下面那句话因此**一个字都没改**:
|
|
617
|
+
# 它对现在这一组来说反而更准了
|
|
618
|
+
unknown: '早先的会话'
|
|
619
|
+
# ⚠️ 这一句不是装饰,是这一组存在的全部理由 —— 少了它,这一组和它替掉的
|
|
620
|
+
# 那个「任务」在屏幕上是同一个误读。措辞和底栏那一格
|
|
621
|
+
# (`composer.workspace_unknown_hint`)同一套词:两处说的是同一件事,
|
|
622
|
+
# 各写一套的话用户会以为是两回事
|
|
623
|
+
unknown_hint: '这个进程说不出它们在哪儿干过活 —— 不是它们没有工作区'
|
|
624
|
+
# 一行三种状态互斥(决定 7):待裁决 > 跑动 > 空闲
|
|
625
|
+
awaiting: '待裁决'
|
|
626
|
+
# 只给读屏念,屏幕上那一格是个转子
|
|
627
|
+
running: '进行中'
|
|
628
|
+
# 侧栏那一格和跨会话待办条那一格**共用这一条**:两处画的是同一格数据
|
|
629
|
+
# (`WireSessionSummary.updatedAt`),各写一句的话迟早只改其中一句。
|
|
630
|
+
#
|
|
631
|
+
# ⚠️ 这条 key 2026-08-16 从 `started_at`(「开始时间」)换过来 —— 会话库 v4
|
|
632
|
+
# 加了 `updated_at` 列,那一格终于说得出「最后活动」。留着旧文案就是把一句
|
|
633
|
+
# 真话换成一句新的假话。
|
|
634
|
+
#
|
|
635
|
+
# ⚠️ 说的是「最近活动」,**不是「已等多久」**:一个卡在审批上的会话,那个
|
|
636
|
+
# 数字是它卡住*之前*最后一次动的时刻。设计稿画的「已等 2:07」今天仍然推不
|
|
637
|
+
# 出来(要 Hub 那半给一个挂起时刻),照抄那句话还是撒谎
|
|
638
|
+
updated_at: '最近活动'
|
|
639
|
+
# 折叠标题右边那个数 = 这个工作区下面有几条会话
|
|
640
|
+
count: '{count} 条会话'
|
|
641
|
+
empty: '还没有会话'
|
|
642
|
+
no_match: '没有匹配的会话'
|
|
643
|
+
|
|
644
|
+
# ---- 行尾那个「更多」:删除 / 重命名(验收 15 / 16,2026-08-15)----
|
|
645
|
+
# 六条判据在 design/web-ui/README.md 决定 7 的「会话行上的删除与重命名」那一节
|
|
646
|
+
#
|
|
647
|
+
# ⚠️ 触发器上带标题,因为一屏最多八个「更多」—— 读屏念八遍「会话操作」
|
|
648
|
+
# 分不出是哪一行的
|
|
649
|
+
acts: '会话「{title}」的操作'
|
|
650
|
+
rename: '重命名'
|
|
651
|
+
# 原地那个输入框(不是弹层,判据见稿子第 5 条)
|
|
652
|
+
rename_label: '会话标题'
|
|
653
|
+
# 行尾那枚 ⏎。**它不是装饰**:失焦按取消这条判据整个压在它身上 ——
|
|
654
|
+
# 没有它的话,不知道回车能提交的人会打完字、点到别处、然后什么都没发生
|
|
655
|
+
rename_do: '保存标题'
|
|
656
|
+
delete: '删除会话'
|
|
657
|
+
delete_ask: '删掉《{title}》?'
|
|
658
|
+
# ⚠️ 说的**不是**「你确定吗」,是这一下会连什么一起带走。判据是「删掉之后
|
|
659
|
+
# 拿不回来」而不是「破坏性操作都要确认」:服务端那个 handler 真删的是 Hub
|
|
660
|
+
# 注册、引擎侧的历史、工作区绑定、SQLite 那一行,以及
|
|
661
|
+
# ~/.epoch/checkpoints/<sid>/ —— 最后一样意味着它改过的文件从此退不回去了,
|
|
662
|
+
# 而那些改动**照旧留在盘上**。最后半句就是在说这件事,别顺手删掉
|
|
663
|
+
delete_what: '对话、检查点、它绑的工作区一起删掉,删完拿不回来 —— 它改过的文件还在盘上,但退不回去了。'
|
|
664
|
+
# 后端**不拦**正在跑的会话(hub.unregister 直接 abort 那一轮),所以界面上
|
|
665
|
+
# 也不灰,只把代价说出来
|
|
666
|
+
delete_running: '它这一轮正在跑,删掉会中止它。'
|
|
667
|
+
# ⚠️ 「放弃」不是「拒绝」:hub.unregister 走的是 relay.abandon(),等在上面的
|
|
668
|
+
# 工具调用收不到一个「拒绝」的答复。这两个字不许改成「拒绝」
|
|
669
|
+
delete_awaiting: '还有 {count} 条在等你裁决,删掉是放弃它们,不是拒绝。'
|
|
670
|
+
delete_do: '删除'
|
|
671
|
+
cancel: '取消'
|
|
672
|
+
# ---- 三个已知的失败码,在浏览器这一侧翻(判据在 session/manage.ts 文件头)----
|
|
673
|
+
# ⚠️ 这一句只给**重命名**用:删除那边的 404 按成功处理 —— 用户按那一下的
|
|
674
|
+
# 目的就是「让它没了」,而它已经没了
|
|
675
|
+
err_gone: '这段会话已经不在了。'
|
|
676
|
+
err_title: '标题不能为空。'
|
|
677
|
+
err_no_store: '这台机器上的会话库没起来,改不了也删不掉。'
|
|
678
|
+
|
|
679
|
+
inspector:
|
|
680
|
+
patch: '补丁'
|
|
681
|
+
output: '输出'
|
|
682
|
+
artifact: '产物'
|
|
683
|
+
plan: '计划'
|
|
684
|
+
close: '收起'
|
|
685
|
+
# ⚠️ 这里原来有一条 `empty: '这一格还没接上'`。2026-08-14「输出」那一格接上
|
|
686
|
+
# 之后(方案 36 PR-2),**四个 tab 全有真数据源了**,它一处都没人用 ——
|
|
687
|
+
# 留着一句没人用的「还没接上」,下一个人只会去找那个不存在的空格子。
|
|
688
|
+
# 真要再加第五个 tab 而它一时接不上,那时再把这条加回来。
|
|
689
|
+
# 这一句改过两次,两次都值得记着:
|
|
690
|
+
# 1. 原文「这一轮还没改过文件」在 2026-08-13 人眼验收当场被改掉 —— 那时候
|
|
691
|
+
# `diff` 事件**没有任何发射方**,写了文件也不会有东西进来,这句话是在
|
|
692
|
+
# 承诺一件不成立的事(决定 20 ①:画一个点了没反应的东西比不画更糟)。
|
|
693
|
+
# 2. 2026-08-14 引擎补上了发射方(core 的 `agent/file-diff.ts`),
|
|
694
|
+
# 于是改回这句 —— 现在它是**真空态**:真的没改过文件才会看见。
|
|
695
|
+
# 判据见方案 43 §12.5。**再改它之前先确认引擎那一侧还在发**。
|
|
696
|
+
no_patch: '这一轮还没改过文件'
|
|
697
|
+
# 点它把时间线滚到那一条事件上(seq 锚点的第一个真实用户)
|
|
698
|
+
locate: '定位'
|
|
699
|
+
# 够得着的判据是「那一行现在在不在 DOM 里」,够不着时按钮**灰着**并印这句话。
|
|
700
|
+
# ⚠️ 措辞先说**观察到的事实**(时间线上没有这一行)再说**多半的成因**
|
|
701
|
+
# (被折叠收起来了):成因是推断出来的,把推断说成事实是这一格自己在犯
|
|
702
|
+
# 它要治的那个毛病。判据全文在 web/src/inspector/locate.tsx
|
|
703
|
+
locate_folded: '时间线上现在没有这一行 —— 多半是被折叠条收起来了。展开那一段之后这个按钮就能用。'
|
|
704
|
+
|
|
705
|
+
# 「输出」那一格:后台任务 + 输出尾巴(方案 36 PR-2)。
|
|
706
|
+
#
|
|
707
|
+
# ⚠️ 这一组文案里最要紧的是三句「你看到的不是全部」。**别把它们压缩成一句**:
|
|
708
|
+
# 三种缺失的下一步动作完全不同(丢了的没救 / 去终端里翻 / 等一会儿再看),
|
|
709
|
+
# 判据见 web/src/inspector/output.tsx 的文件头那张表。
|
|
710
|
+
#
|
|
711
|
+
# ⚠️ 2026-08-15 **撤掉了一条 `note`**(「这里是整个服务进程的后台任务,
|
|
712
|
+
# 不只这个会话起的那些。」)。它当时是一句边界声明:任务表按进程走,两个会话
|
|
713
|
+
# 看到的是同一批。这一轮任务表按会话分了区,那句话不再成立 —— 而**留着一句
|
|
714
|
+
# 不再成立的边界声明,比没有它更坏**:它会让用户以为这一格里混着别人的东西,
|
|
715
|
+
# 于是不敢信自己看到的。别照着这条注释把它加回来。
|
|
716
|
+
output:
|
|
717
|
+
title: '后台任务'
|
|
718
|
+
loading: '正在取任务…'
|
|
719
|
+
# 说「这个会话」而不是「现在」:这一格 2026-08-15 起真按会话分了,
|
|
720
|
+
# 而空清单是它最常见的形态 —— 不点明宾语的话,一个空面板读起来像
|
|
721
|
+
# 「整台机器上什么都没在跑」
|
|
722
|
+
empty: '这个会话现在没有后台任务'
|
|
723
|
+
# 四档措辞逐字对着 plugin-terminal 的 describeTask():TUI 的 /tasks、模型收到
|
|
724
|
+
# 的那行摘要、这一格,三处必须是同一个词。换一套说法的话,同一个任务在对话里
|
|
725
|
+
# 叫「已停止」、在这儿叫「已取消」,用户会以为那是两件事
|
|
726
|
+
st_running: '运行中'
|
|
727
|
+
st_exited: '已结束,退出码 {code}'
|
|
728
|
+
st_killed: '已停止'
|
|
729
|
+
st_failed: '启动失败'
|
|
730
|
+
# 时长和字节数的单位(s / m / h / KB)不进 catalog:两份 catalog 里一模一样,
|
|
731
|
+
# 只会让 check:i18n 的分母白涨几格(同 art 那边 formatBytes 的判断)
|
|
732
|
+
dur: '用时 {value}'
|
|
733
|
+
out_bytes: '{bytes} 输出'
|
|
734
|
+
no_output: '这个任务还没有输出'
|
|
735
|
+
# 第一句:这次只给了末尾一段。**去终端里翻还能看到**
|
|
736
|
+
omitted: '只显示了末尾这一段,前面还有 {bytes} 没给。'
|
|
737
|
+
# 第二句:环形缓冲丢头。那一段**永远拿不回来了**,和上一句不是一回事
|
|
738
|
+
truncated: '这个任务的输出超过了上限,开头那一段已经被丢掉 —— 它拿不回来了,不在这儿也不在别处。'
|
|
739
|
+
# 第三句:跑动中的任务这里是一次取样。别把它画成活的终端
|
|
740
|
+
sampled: '还在跑的任务,这里是一次取样:回合状态变化时重取一次,中间新产出的输出要等下一次。'
|
|
741
|
+
|
|
742
|
+
# 产物那一格的两组 + 只读预览(方案 42 PR-4,决定 17)。
|
|
743
|
+
#
|
|
744
|
+
# ⚠️ 这些值是**纯文本**,会直接进文本节点 —— 别在里面写 `**加重**` 或者反引号,
|
|
745
|
+
# 那些字面量会原样显示出来(整个 catalog 里一处都没有,别从这里开头)。
|
|
746
|
+
art:
|
|
747
|
+
# 第一组:交付问题「我要的东西在哪」
|
|
748
|
+
files: '工作区改动'
|
|
749
|
+
files_note: '这个会话在工作区里新建或改过的文件。'
|
|
750
|
+
files_empty: '这个会话还没改过工作区里的文件'
|
|
751
|
+
# 没绑工作区不是「空」,是**不适用**:没有工作区就没有「工作区改动」这个问题。
|
|
752
|
+
# 后半句是必要的 —— 绑定只活在进程内存里,上一个进程留下的会话在这一格里
|
|
753
|
+
# 长得和「从没绑过」一模一样,不说清的话用户会去找一个不存在的设置
|
|
754
|
+
no_space: '这个会话没有工作区,所以没有「工作区改动」这回事。上一个进程留下的会话也会落在这儿 —— 工作区绑定只活在进程内存里。'
|
|
755
|
+
loading: '正在取清单…'
|
|
756
|
+
# 有行点不开时统一说一句为什么,**不逐行画一个灰按钮**(决定 20 ①)
|
|
757
|
+
thin: '刷新之后回放出来的那些行只剩路径和次数:文件内容不落盘,所以点不开。'
|
|
758
|
+
# 「新建 / 改动」的判据是有没有旧内容,只有这一轮活着看见的行说得出
|
|
759
|
+
created: '新建'
|
|
760
|
+
changed: '改动'
|
|
761
|
+
lines: '{count} 行'
|
|
762
|
+
writes: '写过 {count} 次'
|
|
763
|
+
# 第二组:成本问题「这坨字节进不进上下文」
|
|
764
|
+
tools: '工具产物'
|
|
765
|
+
tools_note: '工具吐出来的二进制:截图、音频、下载。'
|
|
766
|
+
# 这是**事实**不是偷懒:今天全仓只有 MCP 工具会产出 ToolArtifact
|
|
767
|
+
# (plugin-mcp 的 content.ts 转那三支),内置的 file / terminal / web 一个都不产
|
|
768
|
+
tools_empty: '这一轮没有。今天只有 MCP 工具会产出它(image / audio / resource.blob 三支)。'
|
|
769
|
+
# 这句要**显示出来**:不吃图的模型上降级是主路径,用户看见文件列在这儿却发现
|
|
770
|
+
# 模型说「我没看到图」时,唯一能解释这件事的就是它
|
|
771
|
+
tool_gate: '它要过四道闸门才进上下文:能力、尺寸、落盘、成本。不吃图的模型上降级是主路径 —— 这里照常列出文件,但进模型的只是一句说明。'
|
|
772
|
+
back: '返回列表'
|
|
773
|
+
# **事实陈述**,不是一个开关:这一格里没有编辑入口,也不打算有 ——
|
|
774
|
+
# 改文件是引擎的事,浏览器里改一笔会绕过权限层和检查点
|
|
775
|
+
readonly: '只读'
|
|
776
|
+
# 稿子右上角那个「打开所在文件夹」浏览器画不出来(决定 20 ①),换成这个
|
|
777
|
+
copy_path: '复制路径'
|
|
778
|
+
by_created: '由 {tool} 新建'
|
|
779
|
+
by_changed: '由 {tool} 改动'
|
|
780
|
+
# 内容到手之后的三句话(设计稿「六、这份稿子没画的东西」点名的编码探测 /
|
|
781
|
+
# 二进制兜底 / 超大文件截断)。三件的共同判据是同一条:
|
|
782
|
+
# **宁可说显示不了,不许显示一个看着像内容的东西**。
|
|
783
|
+
# 阈值、以及「为什么编码在浏览器这一侧探不出来」的全文在
|
|
784
|
+
# web/src/inspector/content.ts
|
|
785
|
+
#
|
|
786
|
+
# 元信息那行的两个短标签。不可显示时它**顶掉行数那一格** ——
|
|
787
|
+
# 一个二进制文件里 \n 的个数不是「行数」,印出来是一个看着像内容的数字
|
|
788
|
+
kind_binary: '二进制'
|
|
789
|
+
kind_charset: '编码探不出来'
|
|
790
|
+
# 内容位置上那一句「这儿为什么是空的」。⚠️ opaque_charset 刻意**不点名**是哪种
|
|
791
|
+
# 编码:原始字节没到浏览器这边,猜一个然后画出看着像对的东西是这三件里最坏的
|
|
792
|
+
# 一种失败
|
|
793
|
+
opaque_binary: '这看着是二进制:里面有 NUL 字节,或者不可打印字符占了很大一片。铺开来是一屏替换字符,所以这儿不展示内容。'
|
|
794
|
+
opaque_charset: '这份内容不是 UTF-8 —— 可能是 GBK、UTF-16 这类别的编码,也可能是二进制。原始字节没到浏览器这边(补丁帧里带的已经是解过一道的字符串),所以到底是哪一种,这儿探不出来。探不出来就不猜,也就不画。'
|
|
795
|
+
# 只丢了几个字节:照常显示,但把丢了多少说出来。那几处的原文拿不回来了
|
|
796
|
+
lossy: '有 {count} 处字节没解出来。这个文件多半不是 UTF-8,而那几处的原文已经拿不回来了。'
|
|
797
|
+
# 截断**必须说出来**,而且要说出截在哪儿:静默截断的代价是用户以为文件就这么长
|
|
798
|
+
clip_lines: '只显示了前 {shown} 行,这个文件一共 {total} 行。'
|
|
799
|
+
clip_chars: '在第 {line} 行上截断了 —— 这一行太长。这个文件一共 {total} 行。'
|
|
800
|
+
# 这条也要**显示出来**,不能只写在注释里:它是一个会被反复问起的边界
|
|
801
|
+
# ⚠️ 2026-08-18 精简。原文后半句(「要么拖一个重解析器进包体,要么把你的
|
|
802
|
+
# 文件传出去转一趟,而用系统自带的程序打开它是免费的」)是**我们的取舍账**,
|
|
803
|
+
# 用户要的只是「这儿看不了,去哪儿看」。
|
|
804
|
+
#
|
|
805
|
+
# ⚠️ 那笔账里有一条不能丢的**隐私**判据:不做转换的理由之一是「不把用户的
|
|
806
|
+
# 文件传出去」。它搬到了 workspace/diff.tsx 的注释里 ——
|
|
807
|
+
# 下一个想接一个云转换服务的人该在那儿撞见它。
|
|
808
|
+
binary_note: '这类文件这儿看不了,用系统自带的程序打开。'
|
|
809
|
+
|
|
810
|
+
# 检视面板「计划」那一格(方案 35 PR-2)—— 已批准的那份纲的**常驻**展示。
|
|
811
|
+
#
|
|
812
|
+
# ⚠️ 和 dock 那一支(`web.ui.dock.title_plan` 那几条)**不是一套文案**:那边问
|
|
813
|
+
# 「这份计划要不要批」、有四个出口;这一格问「现在照着哪份纲在走」、一个动作都没有。
|
|
814
|
+
# 两处的词凑到一起看着重复,合并的话这一格会长出一个「批准」按钮 —— 而它早就批过了。
|
|
815
|
+
plan:
|
|
816
|
+
# **真空态**,不是「这一格还没接上」(`web.ui.inspector.empty` 那句)。
|
|
817
|
+
# 判据:绝大多数会话从头到尾没进过 plan 模式,「没有计划」是正常状态而不是缺口
|
|
818
|
+
empty: '这个会话还没有被批准的计划'
|
|
819
|
+
# 空态得说清「怎么才会有」,否则用户会以为是这一格坏了去刷新
|
|
820
|
+
empty_hint: '模型交上来的计划要你在输入区那块板上批一次;批过的那一份才会留在这儿。'
|
|
821
|
+
loading: '正在取计划…'
|
|
822
|
+
# 这一格没有 `.tab__n`,所以「只有一份」这件事只能由这句话说出来
|
|
823
|
+
note: '这是当前生效的那一份 —— 计划改过就整份换掉,这儿不留历史。要看它怎么演进,翻时间线上那几次 exit_plan_mode。'
|
|
824
|
+
|
|
825
|
+
banner:
|
|
826
|
+
dismiss: '知道了'
|
|
827
|
+
# 404 `unknown-session` —— Hub 和 SQLite 都不认识手里这个会话 id
|
|
828
|
+
# (方案 43 §12.7 第 5 条)。**说出成因**:光说「会话不存在」会让人以为是自己
|
|
829
|
+
# 点错了链接,而十有八九是他刚在终端里 Ctrl+C 重启过服务端。
|
|
830
|
+
# 「这一段回不来了」也得直说 —— 那是事实,含糊一句「请重试」等于骗他再试一次
|
|
831
|
+
session_gone: '这段对话在服务端上已经没有了 —— 多半是服务端重启过,重启会换一个新会话。这一段回不来了,不过新的那个可以接着用。'
|
|
832
|
+
session_gone_open: '去当前会话'
|
|
833
|
+
# 探不到出口那一支(`store.tsx` 的 `recoverFromGone`):连「现在哪个会话能用」
|
|
834
|
+
# 都问不出来,说明服务端进程多半整个没了。这时候**一个按钮都不画**(决定 20 ①),
|
|
835
|
+
# 所以这句话得自己把下一步说完 —— 而下一步不在这张网页上,在终端里
|
|
836
|
+
session_gone_stuck: '这段对话在服务端上已经没有了,而且现在连「还有哪个会话能用」都问不出来 —— 多半是服务端进程已经停了。回终端看一眼它还在不在。'
|
|
837
|
+
|
|
838
|
+
# 对话流(方案 43 PR-2:导轨 + markdown + 工具卡 + 补丁 + 清单 + 状态行)
|
|
839
|
+
thread:
|
|
840
|
+
# 「深度思考」不带时长:稿子里那个「· 14 秒」要的是**这一段思考**的起止,
|
|
841
|
+
# 而事件流上只有一串 `reasoning-delta`,没有「思考开始 / 结束」两帧。
|
|
842
|
+
# 逐轮耗时 2026-08-14 有了(脚注末尾那个 `38s`),但那是整轮的,不是这一段的
|
|
843
|
+
thinking_label: '深度思考'
|
|
844
|
+
# ── 助手回合的两行头(`.mhead`,决定 8 的**第一个时刻**,2026-08-15)
|
|
845
|
+
#
|
|
846
|
+
# 这一格躺了两个月才画:它要「谁在跑」和「预计多少」两样真话,而顶层会话
|
|
847
|
+
# 以前没有角色、估算式子在中文场景低估 2–2.7 倍。两个前提这一轮都补上了。
|
|
848
|
+
#
|
|
849
|
+
# 数字写成 `~12k` 而不是 `12,431`(稿子就是这么画的):脚注那个数是 provider
|
|
850
|
+
# 报回来的实测值,写满是对的;这一个是**发之前**算的,写到个位是假精度
|
|
851
|
+
head_estimate: '预计 {tokens} token'
|
|
852
|
+
# ⚠️ **这枚徽标不许摘。** 稿子 index.html 里那条注释说「要拿掉这个徽标得先换
|
|
853
|
+
# 真 tokenizer」—— 那是必要条件不是充分条件,别读反。同一份稿子的 thread.css
|
|
854
|
+
# 里 `.est` 那条给的是充分条件:token 有三个时刻,只有**运行前那一个**是猜的。
|
|
855
|
+
# 换真分词器治好的是偏差有多大,治不好的是「它是不是猜的」
|
|
856
|
+
head_est_badge: '估算'
|
|
857
|
+
# 运行态那一行(决定 15):虚线导轨说「链没断」,这行字说「卡在哪」。
|
|
858
|
+
# 所以每一档写的是**状态名**,不是又一个「进行中…」
|
|
859
|
+
#
|
|
860
|
+
# ⚠️ 方案 42 的计划里是五档,这里是**六档** —— 多出来的是「运行工具中」。
|
|
861
|
+
# 把一个真在跑的工具画成「等待模型响应」是假话:那一刻模型早答完了,
|
|
862
|
+
# 在动的是我们这边。而用户想知道的恰恰是「卡在谁那儿」
|
|
863
|
+
live_thinking: '思考中'
|
|
864
|
+
live_running: '生成回复中'
|
|
865
|
+
live_tool: '运行工具中'
|
|
866
|
+
live_waiting: '等待模型响应'
|
|
867
|
+
live_approval: '等你裁决'
|
|
868
|
+
live_question: '等你回答'
|
|
869
|
+
# 第七档(2026-08-19):终止发出去了、这一轮还没停。盖掉另外六档 ——
|
|
870
|
+
# 那六档说的是「引擎在干什么」,而这一刻用户只想知道「我按的那一下算不算数」。
|
|
871
|
+
# ⚠️ **不写「已发出终止请求」这种话**:用户不关心有没有发出去一个请求,
|
|
872
|
+
# 他关心的是这一轮停没停。省略号是那句实话 —— 还没停
|
|
873
|
+
live_aborting: '正在终止…'
|
|
874
|
+
# 决定 8 的**第二个时刻**(跑动中已经花掉的)。服务端没报 `usage.run` 时
|
|
875
|
+
# 整段不出现 —— 不写「0 token」,那会读成「这一轮没花钱」
|
|
876
|
+
live_spent: '已消耗 {tokens} token'
|
|
877
|
+
# 与 TUI 的 thinking-indicator 逐字一致 —— 同一件事在两条路上不能有两种说法
|
|
878
|
+
queued: '{count} 条排队'
|
|
879
|
+
# 终止(决定 22 ②):从发送键上搬到运行态这一行,它是**这一轮**的属性。
|
|
880
|
+
# 2026-08-19 起按钮上**不写这两个字**(一枚方块图标),但这一句没跟着消失 ——
|
|
881
|
+
# 它是那枚图标的 `aria-label` + `title`,读屏念的、鼠标停住看到的都是它
|
|
882
|
+
stop: '终止'
|
|
883
|
+
# 折叠条(2026-08-14 人眼验收第 3 条):一轮收口之后把过程收起来。
|
|
884
|
+
#
|
|
885
|
+
# ⚠️ 这三句是这一行**存在的理由**,不是装饰。方案 43 那条「思考不折叠」
|
|
886
|
+
# 否掉的是「默认藏起来」,理由是「藏起来省下三行高度,换来的是用户永远
|
|
887
|
+
# 不知道它想过这件事」。折起来之后只画一个箭头的话,那件事原样又发生一次 ——
|
|
888
|
+
# 所以摘要必须让人**不用点开就能决定要不要点开**。
|
|
889
|
+
# 三个数各自为 0 时那一段不写(一个恒为 0 的计数在屏幕上是纯噪音)
|
|
890
|
+
fold_tools: '{count} 次调用'
|
|
891
|
+
fold_files: '{count} 个文件'
|
|
892
|
+
fold_thoughts: '思考 {count} 段'
|
|
893
|
+
fold_show: '展开'
|
|
894
|
+
fold_hide: '收起'
|
|
895
|
+
# 闸门要把**下一步**说出来:光说「等你确认」,用户不知道不确认会怎样
|
|
896
|
+
gate: '{tool} 停在这里等你裁决。下面那条链要过了闸才继续。'
|
|
897
|
+
tool_pending: '执行中'
|
|
898
|
+
tool_done: '完成'
|
|
899
|
+
tool_error: '失败'
|
|
900
|
+
# ── 工具卡里的产物(`tool__artifact`)。判定那一半见 thread/artifact.tsx 文件头
|
|
901
|
+
#
|
|
902
|
+
# 这一组存在的理由只有一条:**「模型看见了这张图」和「模型没看见」在界面上
|
|
903
|
+
# 必须长得不一样**。不说出来的话,用户看见文件明明列在这儿、模型却说
|
|
904
|
+
# 「我没看到图」,屏幕上没有任何东西能解释这件事。
|
|
905
|
+
art_in: '已进模型上下文'
|
|
906
|
+
# 被拦下**不是错误、不是失败**,是一个确切的事实。所以不写「被拒绝」这类词
|
|
907
|
+
art_out: '未进模型上下文'
|
|
908
|
+
# 第三档是**诚实**不是兜底:闸门结论没有独立的线上字段(挂账记在
|
|
909
|
+
# web/src/api/artifacts.ts 的文件头),认不出格式的图就是真判不出来
|
|
910
|
+
art_unknown: '进没进上下文,这条流上没说'
|
|
911
|
+
# 成本可见(闸门 4)。算不出来就整个不画 —— 印一个 0 会被读成「免费」
|
|
912
|
+
art_tokens: '约 {count} token'
|
|
913
|
+
# 「产物在」那半由图 / 播放器 / 下载行自己说,这一句只说另一半
|
|
914
|
+
art_out_note: '模型只收到下面这句话,产物本身它没拿到。'
|
|
915
|
+
art_unknown_note: '闸门的结论没上这条流,这里不替它猜。'
|
|
916
|
+
# 闸门 3 没写成。**不画一个点了没反应的下载按钮**(决定 20 ①)
|
|
917
|
+
art_no_file: '没能落盘 —— 这个产物打不开。'
|
|
918
|
+
art_download: '下载 {name}'
|
|
919
|
+
# 没落盘名却拿得到地址(`data` 直出那一支)时的说法:没有文件名可印
|
|
920
|
+
art_download_any: '下载这个产物'
|
|
921
|
+
expand: '展开剩余 {count} 行'
|
|
922
|
+
collapse: '收起'
|
|
923
|
+
todo_title: '交付清单'
|
|
924
|
+
# 分母不算「已取消」:取消掉的事不再是要交的东西,摊进分母会让完成度看着
|
|
925
|
+
# 比实际低,用户会以为还欠着什么
|
|
926
|
+
todo_count: '已完成 {done} / {total}'
|
|
927
|
+
patch_of: '补丁 {path}'
|
|
928
|
+
patch_too_big: '内容超过逐行比对上限,只报行数变化'
|
|
929
|
+
# 补丁卡的第四档(2026-08-15):有一侧不是能显示的文本。
|
|
930
|
+
#
|
|
931
|
+
# ⚠️ **和只读预览那一格不是同一件事**。那边是一整份文件,画不出来就不画;
|
|
932
|
+
# 这边是两侧对比,而**对比本身**在有一侧解坏了的时候会算错 —— 两行不同的
|
|
933
|
+
# 中文注释可能解成同一串替换字符,被 LCS 当成「没变」。所以这一档连
|
|
934
|
+
# `+N −M` 都不报。判据全文在 web/src/thread/patch.tsx 的文件头。
|
|
935
|
+
#
|
|
936
|
+
# 三句各说**哪一侧**:「改之前就坏」多半是这次改动修好了编码,「改完之后
|
|
937
|
+
# 才坏」是这次改动写坏了 —— 两句话对用户的下一步完全不同。
|
|
938
|
+
# 后面接的那句「为什么」直接用 web.ui.art.opaque_* —— 同一份内容在预览和
|
|
939
|
+
# 补丁卡上必须说同一句话,那正是这一档要治的病
|
|
940
|
+
patch_opaque_old: '改之前那一份画不出来,改完之后那一份是正常文本。'
|
|
941
|
+
patch_opaque_new: '改完之后那一份画不出来。'
|
|
942
|
+
patch_opaque_both: '这份改动的两侧都画不出来。'
|
|
943
|
+
# 围栏上没写语言时代码块头里显示这个词
|
|
944
|
+
code: '代码'
|
|
945
|
+
copy_code: '复制代码'
|
|
946
|
+
copied: '已复制'
|
|
947
|
+
note_model: '模型:{model}'
|
|
948
|
+
note_compression: '上下文压缩完成({count} 条)'
|
|
949
|
+
note_summary: '会话结束,耗时 {duration}'
|
|
950
|
+
|
|
951
|
+
# 回合脚注(方案 42 PR-2)。前三个是决定 12 那三个图标化的即时动作 ——
|
|
952
|
+
# 只剩一个 svg 的按钮对屏读器等于一个没有名字的按钮,所以这三句同时当
|
|
953
|
+
# `aria-label` 和 `title`,一句都不能省
|
|
954
|
+
act_copy: '复制这一轮的回复'
|
|
955
|
+
act_rerun: '重跑这一轮'
|
|
956
|
+
act_quote: '引用这一轮到输入框'
|
|
957
|
+
# 第四个**保持文字**:它不是即时动作,是一次跳转,而图标说不清「去哪」
|
|
958
|
+
act_inspect: '在检视面板打开'
|
|
959
|
+
# 决定 8 的**第三个时刻**(结束)。这个数是 provider 回来的实测值,
|
|
960
|
+
# 所以**不挂「估算」徽标** —— 挂了反倒是撒谎
|
|
961
|
+
facts_tokens: '共 {tokens} token'
|
|
962
|
+
# 稿子上这两个词是分开画的:`token` 是普通字,`未上报` 在一个虚线框里。
|
|
963
|
+
# 拆两个键就为了那个框(一个键的话,框要么套住整句、要么干脆没有)。
|
|
964
|
+
# 「未上报」**不是 0**:`cost/pricing.ts` 定的规矩是 undefined 是不知道、
|
|
965
|
+
# 0 是免费,把不知道显示成 0,用户会以为这一轮白嫖成功
|
|
966
|
+
facts_token_label: 'token'
|
|
967
|
+
facts_unknown: '未上报'
|
|
968
|
+
# 这一轮**实际**跑的模型和顶栏那个(配置里选的)不一样时挂在模型名后面。
|
|
969
|
+
# 不标出来的话,用户会拿降级后那个本地小模型的输出去评判主模型
|
|
970
|
+
facts_failover: '故障转移'
|
|
971
|
+
facts_tools: '{count} 次工具'
|
|
972
|
+
# 这个「无」是真的无,不是「不知道」—— 所以它有自己的词,不写「0 次工具」
|
|
973
|
+
facts_no_tools: '无工具'
|
|
974
|
+
# 第五个动作(方案 27 的 web 那一半)。**保持文字,而且不给它图标**:
|
|
975
|
+
# 它不是即时动作(点开是一层预览,还要选范围、逐个勾冲突文件),
|
|
976
|
+
# 而且它是这一行里唯一一个会删文件的。`rotate-ccw` 更不能拿来用 ——
|
|
977
|
+
# 那个图标在这一行里已经是「重跑这一轮」,两个一样的图标干两件相反的事
|
|
978
|
+
act_rewind: '回退到这一轮之前'
|
|
979
|
+
# 回退**之后**留在时间线上的那道痕(design/web-ui/README §六「回到某一节点」)。
|
|
980
|
+
#
|
|
981
|
+
# ⚠️ 「退到了哪一轮」在 WireRewindResponse 上**没有**这个字段。这句话里那段
|
|
982
|
+
# 原话取自时间线自己手里的那一轮(回退按钮当时就挂在它的脚注上),**不是**
|
|
983
|
+
# 从响应里编出来的。尤其不许写「第 N 轮」—— turnIndex 是引擎惰性分配的
|
|
984
|
+
# 检查点序号,不是「第几轮对话」。逐项报数那几句复用 web.ui.rewind.done_*,
|
|
985
|
+
# 和预览板退完那一刻显示的是同一组句子
|
|
986
|
+
rewound_to: '已回退到「{text}」那一轮之前'
|
|
987
|
+
# 拿不到那一轮的原话时只说位置。今天走不到(没有原话的回合配不上检查点,
|
|
988
|
+
# 也就压根不画回退按钮),留着是因为那个字段在类型上是可选的
|
|
989
|
+
rewound_here: '已回退到这一节点之前'
|
|
990
|
+
# ⚠️ **失败那一档专用**(2026-08-19):`files.failed` 非空 + `messagesRemoved`
|
|
991
|
+
# 为 null 时,那一下**什么都没退**(文件那半原子回滚了,对话那半压根没做)。
|
|
992
|
+
# 上面两句在这一档下是**假话** —— 而这道痕摆在时间线上,位置就是因果顺序
|
|
993
|
+
# (决定 2),印一句「已回退到」等于断言那一刀真的落下了。
|
|
994
|
+
# 所以这一档改口径:它讲的是「**试过、没成**」,逐项报数那两句
|
|
995
|
+
# (web.ui.rewind.failed / failed_atomic)紧跟在下面说清为什么
|
|
996
|
+
rewound_failed: '尝试回退到「{text}」那一轮之前 —— 没退成'
|
|
997
|
+
# 同上,拿不到原话那一档
|
|
998
|
+
rewound_failed_here: '尝试回退到这一节点之前 —— 没退成'
|
|
999
|
+
# 取消理由那个小框(`.todo__why`)的 `title`。**理由本身是模型写的原话,
|
|
1000
|
+
# 不经过这里** —— 这个键只是给那段原话安一个身份。
|
|
1001
|
+
#
|
|
1002
|
+
# 需要它是因为屏读器只会把两段文字连读成一句:「顺手升 vitest 你说了先别动
|
|
1003
|
+
# 版本」听着像任务名的一部分。视觉上那个框和它跟在一条划掉的事后面这件事
|
|
1004
|
+
# 已经说清了身份,听觉上没有。
|
|
1005
|
+
#
|
|
1006
|
+
# 没有理由的那一支**整个框都不画**,所以这句话也不会出现 ——
|
|
1007
|
+
# 不写「未说明原因」之类的兜底词(决定 20 ①)
|
|
1008
|
+
todo_why: '取消理由'
|
|
1009
|
+
|
|
1010
|
+
# 「快满了」—— 决定 8 的**第三个时刻**(2026-08-16),挂在回合头那枚
|
|
1011
|
+
# 「估算」徽标后面。四条判据在 web/src/thread/turn-head.tsx 的文件头。
|
|
1012
|
+
#
|
|
1013
|
+
# ⚠️ **两句都不许写成预言**(「接下来会压一次」「下一轮就会压」):
|
|
1014
|
+
# 引擎判压缩用的分子是这一次 run 逐步累加的输入+输出,恒大于等于这里判的
|
|
1015
|
+
# 那个提示词估算 —— 所以徽标亮着时引擎那边确实过线了,但**徽标不亮不等于
|
|
1016
|
+
# 不会压**。写成预言就等于让「没有徽标」被读成「不会压」,而那是假的。
|
|
1017
|
+
#
|
|
1018
|
+
# ⚠️ 也**不许带数字**。左边那个 `~64k` 就是被判的那个量,这一句说的是它的
|
|
1019
|
+
# 一个状态;那条线本身(`128,000 × 50% = 64,000`)归设置页那一块印
|
|
1020
|
+
head_ctx_line: '到压缩线'
|
|
1021
|
+
# 关掉自动压缩、或者压缩器压根没起来的那一档 —— 两者对到线的用户后果逐字
|
|
1022
|
+
# 相同:**没有自动补救**。所以是同一句话加一个后缀,不是另起一句
|
|
1023
|
+
head_ctx_off: '到压缩线 · 无自动压缩'
|
|
1024
|
+
|
|
1025
|
+
# 回退预览层(方案 27 的 web 那一半)。交互逐条照抄 TUI 的 RewindPicker ——
|
|
1026
|
+
# 两个宿主对同一件事给两种默认值才是真正的风险
|
|
1027
|
+
rewind:
|
|
1028
|
+
title: '回退到这一轮之前'
|
|
1029
|
+
loading: '正在读这一轮的检查点…'
|
|
1030
|
+
# 四种动作各用两个字说完。`skip` 不会出现在任何一张清单里(引擎那边就滤掉了),
|
|
1031
|
+
# 但它在类型里,所以这句得有 —— 少一条分支,TS 那个 switch 就不穷尽
|
|
1032
|
+
act_restore: '改回'
|
|
1033
|
+
act_delete: '删掉'
|
|
1034
|
+
act_recreate: '重建'
|
|
1035
|
+
act_skip: '不动'
|
|
1036
|
+
files_head: '会动这 {count} 个文件'
|
|
1037
|
+
# 一个都不用动也要说一句:空白读起来像「还没加载完」
|
|
1038
|
+
files_none: '文件没有需要改的(现状已经等于快照)'
|
|
1039
|
+
# 「默认一个都不覆盖」这半句是这一行的全部意义,不能省成「N 个文件有冲突」
|
|
1040
|
+
conflicts_head: '{count} 个文件在 agent 改完之后又被动过 —— 默认一个都不覆盖'
|
|
1041
|
+
conflict_hint: '勾上的会被盖掉 —— 那是你自己改的那一份,不是 agent 的'
|
|
1042
|
+
# 冲突的两种来源分开说:一种是「有人动过」,另一种是「我们不知道」。
|
|
1043
|
+
# 用户的判断依据完全不同 —— 后者只能靠他自己去看一眼那个文件
|
|
1044
|
+
drift_changed: '之后被动过'
|
|
1045
|
+
drift_unknown: '旧检查点,不确定'
|
|
1046
|
+
scope_legend: '退哪一半'
|
|
1047
|
+
scope_files: '只回退文件(对话保持现状,让它重来一遍)'
|
|
1048
|
+
scope_conversation: '只回退对话(文件保持现状)'
|
|
1049
|
+
scope_both: '两个都回退'
|
|
1050
|
+
# 变灰的时候要说清楚**为什么**,不是让那两项悄悄消失:这个出口本来存在,
|
|
1051
|
+
# 只是这条检查点缺游标(宿主没开轮 / 跑在没有持久化的内存模式里)
|
|
1052
|
+
scope_conversation_off: '这条检查点不支持'
|
|
1053
|
+
# 三条必须写在脸上的边界(TUI 那一版验收 21 / 22 的原话)。
|
|
1054
|
+
# 第一条永远显示:快照只在文件工具动手之前做,terminal 里跑的命令盖不住 ——
|
|
1055
|
+
# 一个声称能全退、实际漏了一半的安全网,比没有安全网更难收拾
|
|
1056
|
+
terminal_note: '只覆盖文件工具做的改动;terminal 里跑的命令改的文件不在范围内'
|
|
1057
|
+
incomplete: '这条检查点不完整(有文件没存进来),只能恢复一部分'
|
|
1058
|
+
# 对话回退多一步确认:它是**真删**,不可撤销。引擎侧没法替用户确认 ——
|
|
1059
|
+
# 确认只能发生在入口这一侧,所以这里多一个步骤而不是少一个
|
|
1060
|
+
conversation_warn: '我知道:对话回退是真删那之后的消息,不可撤销(不是隐藏起来,是删掉)'
|
|
1061
|
+
run: '回退'
|
|
1062
|
+
running: '正在回退…'
|
|
1063
|
+
cancel: '取消'
|
|
1064
|
+
close: '知道了'
|
|
1065
|
+
failed: '回退失败:{reason}'
|
|
1066
|
+
# 这一句不能省成「回退失败」:含糊的说法会让用户去手工「补救」一个根本
|
|
1067
|
+
# 没坏的工作区,而那比失败本身贵得多
|
|
1068
|
+
failed_atomic: '工作区一个字节都没改 —— 回退是原子的,失败就是什么都没动。'
|
|
1069
|
+
done_restored: '{count} 个文件改回'
|
|
1070
|
+
done_deleted: '{count} 个文件删掉'
|
|
1071
|
+
done_recreated: '{count} 个文件重建'
|
|
1072
|
+
done_nothing: '文件没有需要改的(现状已经等于快照)'
|
|
1073
|
+
done_skipped: '{count} 个你改过的没覆盖'
|
|
1074
|
+
done_messages: '对话删掉 {count} 条消息(不可撤销)'
|
|
1075
|
+
|
|
1076
|
+
# dock 三种 kind(方案 43 PR-3:决定 5 / 方案 35 / 方案 34)
|
|
1077
|
+
dock:
|
|
1078
|
+
title_permission: '需要确认'
|
|
1079
|
+
title_plan: '计划待批准'
|
|
1080
|
+
title_question: '需要你选一下'
|
|
1081
|
+
# 「排在后面」而不是「待确认」:dock 一次只画一个,后面那些是**排队**,
|
|
1082
|
+
# 不是同时摆在屏幕上等你挑
|
|
1083
|
+
queue: '还有 {count} 个排在后面'
|
|
1084
|
+
queue_questions: '还有 {count} 组问题'
|
|
1085
|
+
|
|
1086
|
+
# 操作类别的人话。与 TUI 的 TYPE_LABEL、cli 的 approval.ts 同义,都是纯展示
|
|
1087
|
+
act_file_read: '读文件'
|
|
1088
|
+
act_file_write: '写文件'
|
|
1089
|
+
act_command: '执行命令'
|
|
1090
|
+
act_network: '网络请求'
|
|
1091
|
+
act_code_exec: '在沙箱内执行代码'
|
|
1092
|
+
|
|
1093
|
+
fact_action: '动作'
|
|
1094
|
+
fact_target: '目标'
|
|
1095
|
+
fact_detail: '详情'
|
|
1096
|
+
fact_reason: '原因'
|
|
1097
|
+
target_none: '(无)'
|
|
1098
|
+
|
|
1099
|
+
# 四个出口。⚠️ 一个都不许砍:core 的 tool-executor 会把后两种写进审批缓存,
|
|
1100
|
+
# 砍成「同意 / 拒绝」的后果是每一次写文件都要重新点一次
|
|
1101
|
+
allow_once: '允许一次'
|
|
1102
|
+
allow_session: '本会话内始终允许'
|
|
1103
|
+
allow_session_hint: '退出即失效'
|
|
1104
|
+
allow_always: '永久允许'
|
|
1105
|
+
# 说清楚它落盘落到哪儿 —— 「永久」这两个字本身不告诉用户去哪儿撤销
|
|
1106
|
+
allow_always_hint: '写入 approvals.json'
|
|
1107
|
+
deny: '拒绝'
|
|
1108
|
+
|
|
1109
|
+
# 计划审批的四个出口(方案 35)。⚠️ 这四条的中文**必须与 protocol 的
|
|
1110
|
+
# PLAN_OUTCOME_LABELS 逐字相同**,`packages/web/__tests__/dock.test.tsx`
|
|
1111
|
+
# 有一条用例锁着。两份的存在理由:那个常量是 protocol 的(TUI / CLI 也在用、
|
|
1112
|
+
# 只有中文),而浏览器要能切英文。真正的收敛是把它也搬进 catalog,归方案 40
|
|
1113
|
+
plan_execute: '批准并执行'
|
|
1114
|
+
plan_readonly: '批准,但保持只读'
|
|
1115
|
+
plan_revise: '让我改一下'
|
|
1116
|
+
plan_execute_hint: '权限回到 {level}'
|
|
1117
|
+
plan_readonly_hint: '我自己动手'
|
|
1118
|
+
# 用户本来就在 plan 级别时,「批准并执行」整个不出现,这一句解释为什么
|
|
1119
|
+
plan_readonly_forced: '你要求了全程只读'
|
|
1120
|
+
note_label: '要改的话,说一句:'
|
|
1121
|
+
note_placeholder: '比如:先只动 core,别碰 tui'
|
|
1122
|
+
|
|
1123
|
+
# 提问那一支(方案 34)
|
|
1124
|
+
q_free: '都不对,我自己写:'
|
|
1125
|
+
q_free_placeholder: '(可选)填了就以这一句为答案'
|
|
1126
|
+
q_submit: '提交'
|
|
1127
|
+
q_submit_partial: '还有 {count} 个没答,会告诉它「跳过」'
|
|
1128
|
+
q_skip: '跳过'
|
|
1129
|
+
q_skip_hint: '模型会自己挑一个默认继续'
|
|
1130
|
+
|
|
1131
|
+
# 跨会话待办条(验收 18)—— dock 的第三档:「别的会话在等你」。
|
|
1132
|
+
#
|
|
1133
|
+
# ⚠️ **措辞必须带宾语。** 说「《X》在等你答一个审批」,不说「有 1 个待办」——
|
|
1134
|
+
# 一个不带宾语的计数器逼用户自己挨个点开去看,而那正是这条通知要省掉的
|
|
1135
|
+
# 那件事。判据在 web/src/dock/crossbar.tsx 的文件头。
|
|
1136
|
+
crossbar:
|
|
1137
|
+
# 单复数分开两条,不是「{count} 个」一条通吃:英文里 `1 approvals` 是错的,
|
|
1138
|
+
# 而 count 为 1 恰恰是绝大多数现场(同 relativeTime 那条判据)
|
|
1139
|
+
approval_one: '《{title}》在等你答一个审批'
|
|
1140
|
+
approval_n: '《{title}》在等你答 {count} 个审批'
|
|
1141
|
+
question_one: '《{title}》在等你答一个提问'
|
|
1142
|
+
question_n: '《{title}》在等你答 {count} 个提问'
|
|
1143
|
+
# 冷却的会话照样算待办(它的待办落在 DB 里,是真的),但那个会话此刻
|
|
1144
|
+
# **不在服务端手里** —— 如实标出来,别让人以为点过去还能接着跑
|
|
1145
|
+
cold: '已冷却'
|
|
1146
|
+
cold_hint: '服务端此刻不持有它,点过去是从历史回放'
|
|
1147
|
+
# 同时有好几个会话在等时只画一条(同 dock 的「一次只弹一个」),
|
|
1148
|
+
# 剩下的进这一格。**只在真有排队时出现**
|
|
1149
|
+
more: '还有 {count} 个'
|
|
1150
|
+
go: '去处理'
|
|
1151
|
+
# 条上那一格时间的口径(2026-08-16)。这一格**原来说的是「最近活动」**,
|
|
1152
|
+
# 和侧栏那一行共用 `slist.updated_at` —— 而它想说的一直是「已等」。
|
|
1153
|
+
# Hub 那半现在给了 `WireSessionSummary.pendingSince`(挂起那一刻),于是这一格
|
|
1154
|
+
# 换了数据也换了口径,也就不再和侧栏共用那一条了:两处画的不是同一格数据。
|
|
1155
|
+
# 口径必须跟着数据一起换,留着旧说明就是把一句真话换成一句新的假话。
|
|
1156
|
+
#
|
|
1157
|
+
# ⚠️ **不带占位符,别写成「已等 {age}」**:这一条是挂在那个数字上的 title,
|
|
1158
|
+
# 不是包着它的句子。那个数字是 `relativeTime()` 给的「3 小时前」(那份格式化
|
|
1159
|
+
# 全仓只许有一处,判据写在 crossbar.tsx 的 import 上面),拼进句子会变成
|
|
1160
|
+
# 「已等 3 小时前」。
|
|
1161
|
+
#
|
|
1162
|
+
# ⚠️ 粒度到**分钟**,不是设计稿画的「已等 2:07」那个秒 —— 印秒要一个每秒重画
|
|
1163
|
+
# 的计时器,而「不做定时刷新」那条判据一个字没动(shell/session-list.tsx 底部)。
|
|
1164
|
+
waited: '已等'
|
|
1165
|
+
|
|
1166
|
+
# 后台回执(决定 7 第三条)。**只给后台会话、只给终态**,判据在
|
|
1167
|
+
# web/src/shell/toasts.tsx 的文件头
|
|
1168
|
+
toast:
|
|
1169
|
+
# 必须带宾语,同跨会话待办条:说「《重构鉴权》跑完了」,不说「1 个会话完成」。
|
|
1170
|
+
# 一个不带宾语的计数器逼用户挨个点开去看,而那正是这条回执要省掉的事。
|
|
1171
|
+
#
|
|
1172
|
+
# ✅ 2026-08-15:下面「失败」那六句补上了 —— `WireSessionSummary` 加了
|
|
1173
|
+
# `lastFinish` 这一列,成败终于分得出来(之前这里写的是「只有这一句,
|
|
1174
|
+
# 因为网线上没有终局原因」,那句判据留档在 toasts.tsx 的文件头)。
|
|
1175
|
+
#
|
|
1176
|
+
# 这一句**仍然是中性的「跑完了」而不是「完成了」**,而且它现在担两份差事:
|
|
1177
|
+
# `reason: 'stop'`(配一枚绿勾)和**终局不知道**(那一轮没走到 `finish`,
|
|
1178
|
+
# 记号整枚不画)。一句话配两种记号是刻意的 —— 两种情形下「跑完了」都是真的,
|
|
1179
|
+
# 而「完成了」在后一种里是我们编的
|
|
1180
|
+
done: '《{title}》跑完了'
|
|
1181
|
+
# 失败那一半。**一档一句,别合并成一句「没跑完」**:这条回执的全部作用是
|
|
1182
|
+
# 让人不点进去就知道要不要管它,而「触了预算上限」和「模型拒答了」
|
|
1183
|
+
# 接下来要做的事完全不同。八种终局里除了 `stop` 都在这儿(`error` 和
|
|
1184
|
+
# `aborted` 各占一句),穷举表在 `toasts.tsx` 的 `OUTCOMES`
|
|
1185
|
+
max_turns: '《{title}》轮次用尽,没跑完'
|
|
1186
|
+
stalled: '《{title}》原地打转,已经停下'
|
|
1187
|
+
aborted: '《{title}》被中止了'
|
|
1188
|
+
filtered: '《{title}》被模型拒答了'
|
|
1189
|
+
# 与 TUI 那句「已触达预算上限,提前收工」同义,只是这里要带宾语
|
|
1190
|
+
budget: '《{title}》触了预算上限,提前收工'
|
|
1191
|
+
failed: '《{title}》出错了'
|
|
1192
|
+
go: '查看'
|
|
1193
|
+
|
|
1194
|
+
composer:
|
|
1195
|
+
label: '消息'
|
|
1196
|
+
placeholder_idle: '说点什么…(回车发送,Shift+回车换行)'
|
|
1197
|
+
# ⚠️ 这一句 2026-08-13(PR-4)改了口径:原来是「正在跑这一轮…」,那是在说
|
|
1198
|
+
# 「你不能发」。决定 22 之后跑动中照样能发,所以它改成说「发出去会怎样」
|
|
1199
|
+
placeholder_running: '这一轮还在跑,发出去的会排在它后面'
|
|
1200
|
+
# 断线是**唯一**还锁着输入框的状态(决定 22 末尾):发出去也到不了
|
|
1201
|
+
placeholder_offline: '和服务端断开了,正在重连…'
|
|
1202
|
+
placeholder_approval: '等你确认上面那个操作'
|
|
1203
|
+
# 「确认」和「回答」是两件事,措辞不能共用:用户看到「等你确认」会去找
|
|
1204
|
+
# 同意 / 拒绝两个按钮,而弹层上是一组选项(方案 34)
|
|
1205
|
+
placeholder_question: '等你回答上面那个问题'
|
|
1206
|
+
placeholder_no_session: '还没有会话'
|
|
1207
|
+
|
|
1208
|
+
# 写不了的三句话(2026-08-15 起四句,2026-08-19 撤掉一句,见下)。
|
|
1209
|
+
# ⚠️ **一档一句,别合并** —— 原来这里只有一句「这是一段历史会话」,
|
|
1210
|
+
# 而它对另外几档是一句具体的假话。
|
|
1211
|
+
# 每一句都要带着**下一步**,判据在 `composer/index.tsx` 的 `READONLY_KEY` 上
|
|
1212
|
+
#
|
|
1213
|
+
# 这一档最要命:它既不是历史会话、也不是上一个进程留下的,是一台**还没配好**
|
|
1214
|
+
# 的机器上一个**刚建的**会话。所以这句话除了说事实,还得把错的那一步堵掉
|
|
1215
|
+
# (「换个会话再试」解决不了任何问题 —— 没有 provider 就一个活着的会话都没有)。
|
|
1216
|
+
# 措辞和服务端建会话那句 503(上面的 `web.session_unavailable`)对齐:
|
|
1217
|
+
# 同一个成因,用户很可能两句都看见
|
|
1218
|
+
placeholder_no_provider: '这台机器还没有可用的模型 provider —— 换个会话也一样,去配一把 API key(或跑 epoch model)'
|
|
1219
|
+
# 上一个进程留下的、而且**说不出它当初在哪儿干活**的那些会话
|
|
1220
|
+
# (会话库 v7 之前建的行)。2026-08-19 起「上一个进程留下的」本身不再
|
|
1221
|
+
# 落这一档 —— 有那次工作区决定的,发一句话服务端就把它接回来了,输入框
|
|
1222
|
+
# 照常能写(判据在 web 的 `state/read-only.ts`)。
|
|
1223
|
+
#
|
|
1224
|
+
# ⚠️ 措辞跟着收窄了:原文是「这是一段历史会话,只能看不能发」,而**「历史
|
|
1225
|
+
# 会话」现在大多能发** —— 那句话留着就成了一句劝人白走一趟的假话。
|
|
1226
|
+
# 「说不出在哪儿干活」这半句和侧栏那一组(`slist.unknown_hint`)同一套词:
|
|
1227
|
+
# 屏幕上那两处说的是同一件事,各写一套的话用户会以为是两回事
|
|
1228
|
+
placeholder_history: '这段对话说不出它当初在哪儿干活,接不回来了 —— 只能看,或者开一段新的'
|
|
1229
|
+
# 会话在服务端整个没了。**不在这儿重复出口** —— 上面那条横幅有按钮,
|
|
1230
|
+
# 这里只说清「不是历史会话,是没了」,免得两处各说各的下一步
|
|
1231
|
+
placeholder_gone: '这段对话在服务端上已经没有了,发不出去'
|
|
1232
|
+
# 键旁边那句提示**只有跑动中这一句**(决定 22 ②:改的是提示不是键)。
|
|
1233
|
+
# 闲着时那句原来叫 `hint_send`,逐字也是「发送」—— 和键并排读成同一个词说了
|
|
1234
|
+
# 两遍,2026-08-18 删掉。「回车 = 发送」上面 `placeholder_idle` 里还有一份
|
|
1235
|
+
hint_queue: '排队发送'
|
|
1236
|
+
send: '发送'
|
|
1237
|
+
# 斜杠命令的补全面板(方案 23 的 web 那一半)。这是面板那个 listbox 的
|
|
1238
|
+
# 无障碍名 —— 屏幕阅读器播的就是它,界面上看不到
|
|
1239
|
+
commands_label: '自定义斜杠命令'
|
|
1240
|
+
# 展开时那几句话的抬头。**警告正文不在这里** —— 那是引擎原文
|
|
1241
|
+
# (随用户那份命令文件而变,没有有限的码可以枚举),判据写在
|
|
1242
|
+
# `WireCommandExpansion.warnings` 上
|
|
1243
|
+
command_warnings: '/{name} 展开时有几处要说明:'
|
|
1244
|
+
|
|
1245
|
+
# 加号与附件(决定 20 ① / 验收 21)
|
|
1246
|
+
attach: '添加图片'
|
|
1247
|
+
remove_attach: '去掉 {name}'
|
|
1248
|
+
# 拖拽落区。说的是「松手会发生什么」,不是「你正在拖」—— 后者是用户自己
|
|
1249
|
+
# 正在做的事,不需要屏幕告诉他
|
|
1250
|
+
drop_hint: '松手,把图片加进这条消息'
|
|
1251
|
+
# 读盘那一小段的占位(多选十几张时 `readAsDataURL` 是真要时间的)。
|
|
1252
|
+
# 它塞在一枚 chip 里跟着文件名,所以必须短
|
|
1253
|
+
reading: '读取中…'
|
|
1254
|
+
# 被拒那几条的手动出口。**不做成 3 秒自己消失的 toast**:用户这会儿正在打字,
|
|
1255
|
+
# 一条读到一半就没了的错误等于没说过
|
|
1256
|
+
reject_dismiss: '收起这几条'
|
|
1257
|
+
no_workspace: '不使用工作区'
|
|
1258
|
+
# 第三档(2026-08-15):**问过了,这个进程说它不知道**。
|
|
1259
|
+
# ⚠️ 别和上面那句合并 —— 「不使用工作区」是一句确切的话,而这一格上一轮的
|
|
1260
|
+
# 病正是把「不知道」说成了一句确切的话(当时说的是引导会话的地盘)。
|
|
1261
|
+
# 成因只有一种:这段会话是上一个进程留下的,而**这个进程还没把它接回来**
|
|
1262
|
+
# (服务端回 409 `not-live-session`)。「还没问到」不走这一档 —— 那时整格不画。
|
|
1263
|
+
#
|
|
1264
|
+
# ⚠️ 2026-08-19 措辞改了一处,因为原话里那句「工作区绑定不落盘」变成了假话:
|
|
1265
|
+
# 会话库 v7 落下了**用户当初的那次决定**,侧栏据此把这段会话画回它自己的空间。
|
|
1266
|
+
# 落盘的不是绑定本身 —— 绑定要过信任闸门、只在这个进程真把它接回来时才发生,
|
|
1267
|
+
# 所以这一格照旧答不出「它**此刻**绑在哪儿」。⚠️ 别把这一格改成读那份决定:
|
|
1268
|
+
# 它旁边就画着信任状态和指令文件清单,而那两样这个进程还没算过
|
|
1269
|
+
workspace_unknown: '工作区未知'
|
|
1270
|
+
workspace_unknown_hint: '这段会话是上一个进程留下的,这个进程还没把它接回来 —— 所以说不出它此刻绑在哪儿'
|
|
1271
|
+
# 这一格 2026-08-16 变成了工作区 diff 那一屏的门(方案 30 PR-3)。
|
|
1272
|
+
# ⚠️ 它跟在**绝对路径后面另起一行**,所以这句话不重复路径,只说去处 ——
|
|
1273
|
+
# 光标变个手形说不清点下去会发生什么,而这一格上一秒还只是一条路径
|
|
1274
|
+
workspace_diff_hint: '点开看这个目录现在有哪些改动'
|
|
1275
|
+
|
|
1276
|
+
# 第四档(方案 55 PR-1,2026-08-17):**建好了、还没选地盘**,这一格画成
|
|
1277
|
+
# 一张 picker —— 稿子决定 18 画它的位置就是这儿。
|
|
1278
|
+
#
|
|
1279
|
+
# ⚠️ 下面这五句里有四句是从 `web.ui.newsession.*` **搬过来的**(picker 从
|
|
1280
|
+
# 侧栏那一行搬到了这儿),措辞一个字没改。搬 key 而不是留在原地引用,
|
|
1281
|
+
# 判据是 key 的前缀在这个仓库里表示**它画在哪儿** —— 留在 `newsession`
|
|
1282
|
+
# 底下的话,下一个改这几句话的人会去侧栏找那张菜单,而那儿已经没有了。
|
|
1283
|
+
#
|
|
1284
|
+
# 触发器上那句。**说「选」不说「未选」**:这一格上另外三档写的都是事实,
|
|
1285
|
+
# 只有这一档是一个动作,而动词是它和那三档最明显的区别
|
|
1286
|
+
workspace_pick: '选择工作区'
|
|
1287
|
+
# 菜单本身没有可见标题,这一句是读屏念的那个名字
|
|
1288
|
+
workspace_pick_label: '这段会话在哪个工作区里干活'
|
|
1289
|
+
# 当前这个会话就在这个目录里。决定 7:「同一个仓库里同时开两件事是常态」
|
|
1290
|
+
workspace_here_current: '{name}(当前)'
|
|
1291
|
+
# 「不使用工作区」那一项的副行。标题复用上面那句 `no_workspace` ——
|
|
1292
|
+
# 菜单里那一项和按完之后这一格上的字必须逐字相同。
|
|
1293
|
+
#
|
|
1294
|
+
# ⚠️ 这一句纠正稿子空态那句「不选也能聊,要它动文件就得先挑一个」——
|
|
1295
|
+
# 那是假的。runtime/src/session-factory.ts 的原话:绑定为 null 时
|
|
1296
|
+
# 「`AgentLoop` 跑在装配时那个 `workDir` 上」。不绑工作区不等于碰不到文件,
|
|
1297
|
+
# 把它写成一个沙盒,用户会在那儿放心地让它改文件。
|
|
1298
|
+
#
|
|
1299
|
+
# 后半句是 2026-08-17 新加的:这一下**按下去就定了**(服务端对 `none`
|
|
1300
|
+
# 回 409,同已绑目录)。少了它,用户会以为这是一个可以再改的选择
|
|
1301
|
+
workspace_none_detail: '跑在服务进程启动的那个目录上 —— 不是一个碰不到文件的沙盒。按下去就定了,之后改不了'
|
|
1302
|
+
# 清单的来源和它的边界。「新建工作区」「打开本地文件夹」两条稿子里有、
|
|
1303
|
+
# PR-1 还没有(各归方案 55 的 PR-2 / PR-3)
|
|
1304
|
+
workspace_pick_note: '清单是本机最近用过的那几个目录。要在一个没来过的目录里开会话,先在那儿跑一次 epoch。'
|
|
1305
|
+
# 「打开本地文件夹…」(方案 55 PR-2,2026-08-17)—— 稿子那张菜单的第四项。
|
|
1306
|
+
#
|
|
1307
|
+
# ⚠️ 上一轮把这一项判成「浏览器里没有出口」,理由是浏览器给不出服务端的绝对
|
|
1308
|
+
# 路径。**前半句对,后半句把「浏览器自己挑」和「让服务端告诉浏览器有哪些
|
|
1309
|
+
# 目录」当成了同一件事** —— 后者是一条普通的读端点。
|
|
1310
|
+
#
|
|
1311
|
+
# 副行说的是**它列的是哪台机器** —— 这一条在 `--host 0.0.0.0` 那种远程场景下
|
|
1312
|
+
# 不是废话:用户面前这台和跑 agent 那台可以不是同一台
|
|
1313
|
+
workspace_browse: '打开本地文件夹…'
|
|
1314
|
+
workspace_browse_detail: '逐层挑一个目录,或者直接敲一条绝对路径(列的是跑 epoch 那台机器上的目录)'
|
|
1315
|
+
permission: '权限 {level}'
|
|
1316
|
+
|
|
1317
|
+
# plan 模式那个开关(方案 35 的 web 入口,2026-08-15)。
|
|
1318
|
+
# ⚠️ `plan_mode_why` 是这一行上唯一一句完整的话,而它必须回答的问题是
|
|
1319
|
+
# 「我为什么写不了文件」—— 一个只写着「计划模式」的芯片答不了它
|
|
1320
|
+
plan_mode: '计划模式'
|
|
1321
|
+
plan_on_hint: '进计划模式:模型先只读地调研、交一份计划给你批,期间不动文件'
|
|
1322
|
+
plan_off_hint: '退出计划模式,回到进入前那一档权限'
|
|
1323
|
+
plan_mode_why: '只读:模型能看能查,改不了文件;它交计划时你会收到一次审批'
|
|
1324
|
+
# 被拒的图**逐张说清楚是哪一张、为什么** —— 静默丢掉的代价是用户对着一条
|
|
1325
|
+
# 「你看这张图」的消息等回答,而模型压根没收到图
|
|
1326
|
+
reject_not_image: '{name} 不是图片,这一轮只收图片'
|
|
1327
|
+
reject_too_big: '{name} 有 {size} MB,超过 {max} MB 的上限'
|
|
1328
|
+
reject_unreadable: '{name} 读不出来'
|
|
1329
|
+
# 拖进来的是个文件夹。**「不是图片」在这儿是一句会把人带偏的真话** ——
|
|
1330
|
+
# 对着一个装满截图的 `shots/` 说「shots 不是图片」,人会以为这里不收图,
|
|
1331
|
+
# 而正确的下一步是打开它。这件事只有拖拽那一层拿得到(判据在 drop.tsx)
|
|
1332
|
+
reject_directory: '{name} 是个文件夹 —— 打开它,把里面的图拖进来'
|
|
1333
|
+
# 这条消息大到发不出去。这个上限**不是界面挑的数**,是服务端的请求体上限,
|
|
1334
|
+
# 而超了它那边是直接 destroy 连接 —— 所以这句话必须赶在按下发送**之前**说。
|
|
1335
|
+
# 两个出口都给:图那半和文字那半各是一条路(判据在 `MAX_MESSAGE_BYTES`)
|
|
1336
|
+
too_big: '这条消息 {size},一次最多发 {max}。去掉一张图,或者把粘进来的那段文字截短'
|
|
1337
|
+
|
|
1338
|
+
# 那一行上的身份 chip(决定 20 ② / 方案 57 §3.6,2026-08-17)。
|
|
1339
|
+
#
|
|
1340
|
+
# chip 上印的就是身份名本身(没有多余的字),所以这两句都是**说不出口的
|
|
1341
|
+
# 那一半**:它是什么、以及怎么取消。
|
|
1342
|
+
#
|
|
1343
|
+
# ⚠️ 两句都带 `{role}`:读屏顺着控件列表走的人只听见「移除」的话,
|
|
1344
|
+
# 分不清移除的是这枚 chip 还是旁边某一张附件 chip。
|
|
1345
|
+
#
|
|
1346
|
+
# `expert_chip_title` 必须说「**接下来每条**」而不是「这一条」——
|
|
1347
|
+
# 那正是这枚 chip 和别处所有 chip 唯一的语义差别(它粘住,直到点 ×)
|
|
1348
|
+
expert_chip_title: '接下来每条消息都由「{role}」来答,点左边的 × 取消'
|
|
1349
|
+
# 底栏那格**会话级**身份(2026-08-18)。⚠️ 和上面那枚 chip 是两件事,
|
|
1350
|
+
# 而这句 title 的全部作用就是说清区别:屏幕上它俩长得都是「一个身份名」,
|
|
1351
|
+
# 不说的话用户会去找这一格的 ×(而它没有 × —— 身份中途换不了)。
|
|
1352
|
+
identity_title: '这段会话以「{role}」的身份跑。它是建会话时定的,中途换不了。'
|
|
1353
|
+
expert_remove: '不再让「{role}」接手'
|
|
1354
|
+
|
|
1355
|
+
# 框外那一行上**权限档**那张菜单(决定 20 ③,2026-08-15)。
|
|
1356
|
+
#
|
|
1357
|
+
# ⚠️ 单开一个 `perm` 而不是并进上面的 `composer`:这几句话的主是**档位模型
|
|
1358
|
+
# 本身**(`core/src/permission/by-level.ts` 那张五档表),不是输入框。
|
|
1359
|
+
# 设置页那张分段器哪天真接上写端点,用的是同一批句子 —— 稿子在那两处
|
|
1360
|
+
# 逐字用的就是同一份文案,而两份走散的表现是同一档在两屏上写着两句不同的话。
|
|
1361
|
+
#
|
|
1362
|
+
# ⚠️ **`{level}` 那个原值照旧印在 chip 上**(`composer.permission`),
|
|
1363
|
+
# 这里这几句是**菜单里**的人话。两处不重复:chip 上要的是用户能拿去敲
|
|
1364
|
+
# `--permission` 的那个词,菜单里要的是「它到底放开了什么」。
|
|
1365
|
+
perm:
|
|
1366
|
+
# 菜单本身的无障碍名。屏幕阅读器播的就是它,界面上看不到
|
|
1367
|
+
menu_label: '权限档位'
|
|
1368
|
+
# 五档的名字与那句解释。**前四档逐字取自设计稿**
|
|
1369
|
+
# (design/web-ui/index.html 那张 #permMenu),`auto` 那一句照
|
|
1370
|
+
# core/src/permission/by-level.ts 那张表写的 —— 稿子上没有它,
|
|
1371
|
+
# 判据(为什么这里画五档)在 composer/permission-menu.tsx 的文件头
|
|
1372
|
+
lvl_plan: '只读'
|
|
1373
|
+
lvl_plan_detail: '只让它看和想。要动文件、跑命令,先给你一份计划'
|
|
1374
|
+
lvl_default: '按需确认'
|
|
1375
|
+
lvl_default_detail: '规则表说 ask 的那些逐条问你。默认档'
|
|
1376
|
+
lvl_accept_edits: '自动接受改动'
|
|
1377
|
+
lvl_accept_edits_detail: '改文件不再问,跑命令照旧问'
|
|
1378
|
+
# ⚠️ 这一句要说清它和上一档的差别到底在哪儿:不是「更自动一点」,
|
|
1379
|
+
# 是**工作区外的文件和网络也放开了**,只剩非只读命令还问
|
|
1380
|
+
lvl_auto: '信任它自己判断'
|
|
1381
|
+
lvl_auto_detail: '读写文件和联网都不问了,工作区外也一样;跑命令还是只问改动性的那些'
|
|
1382
|
+
# ⚠️ 「整条导轨会变红,就是要你难受」这句是稿子的原话,**别改软**:
|
|
1383
|
+
# 它是这一档唯一的刹车,而这一档点错了代价极大
|
|
1384
|
+
lvl_bypass: '跳过全部检查'
|
|
1385
|
+
lvl_bypass_detail: '什么都不问。整条导轨会变红,就是要你难受'
|
|
1386
|
+
# 这台机器上那一档不存在(企业托管的 disableBypassPermissionsMode)。
|
|
1387
|
+
#
|
|
1388
|
+
# ⚠️ 这一句**必须说出「为什么」和「去哪儿看」**,判据是稿子在
|
|
1389
|
+
# `.btn--pick[data-locked]` 那一段写的:灰掉一项只说明「点不了」,
|
|
1390
|
+
# 而这个「为什么」来自一台机器上的 managed-settings.json,用户在界面上
|
|
1391
|
+
# 别处查不到。托管文件在哪儿、还开了哪些开关,全在安全中心那一屏 ——
|
|
1392
|
+
# 所以这一句指路过去,而不是在一张 12px 的说明里把那些也念一遍
|
|
1393
|
+
blocked_managed: '这台机器装了企业托管设置,「跳过全部检查」那一档被它关掉了。详情见「设置 › 安全中心」'
|
|
1394
|
+
|
|
1395
|
+
fatal:
|
|
1396
|
+
unauthorized: '没有访问凭据'
|
|
1397
|
+
# token 每次启动随机生成,进程重启就换一个 —— 所以这里不能说「刷新重试」
|
|
1398
|
+
unauthorized_hint: '回到起 epoch web 的那个终端,用它打印的那条带 token 的地址重新打开。'
|
|
1399
|
+
offline: '连不上本地服务'
|
|
1400
|
+
|
|
1401
|
+
settings:
|
|
1402
|
+
title: '设置'
|
|
1403
|
+
back: '返回会话'
|
|
1404
|
+
security: '安全中心'
|
|
1405
|
+
pane_model: '模型与 Provider'
|
|
1406
|
+
pane_memory: '记忆与上下文'
|
|
1407
|
+
pane_budget: '预算'
|
|
1408
|
+
# 这两节的正文分别在下面的 `web.ui.tools.*` / `web.ui.about.*`。
|
|
1409
|
+
# 「工具」紧跟在安全中心后面(`settings/index.tsx` 的 `PANES`):
|
|
1410
|
+
# 那一屏是规则本身,这一屏是那些规则此刻落到每一个工具身上的结果
|
|
1411
|
+
pane_tools: '工具'
|
|
1412
|
+
pane_about: '关于'
|
|
1413
|
+
appearance: '外观'
|
|
1414
|
+
appearance_lede: '只影响这台机器上的这个浏览器。它不写进 config.yaml,也不跟着账号走 —— 我们没有账号。'
|
|
1415
|
+
# ⚠️ 2026-08-18 精简。原文四句话,全部在讲**我们为什么不做主题市场**
|
|
1416
|
+
# (「多一套自定义配色就意味着那四件事的含义要由用户负责,而『那条红的没人
|
|
1417
|
+
# 看见』是这个界面最贵的失败」)—— 那是一条产品决定的辩词,写给的是同行,
|
|
1418
|
+
# 不是坐在这一屏前面的人。
|
|
1419
|
+
#
|
|
1420
|
+
# 判据本身不许丢(**别加自定义配色**:颜色在这一稿里承载状态 / 语法 / 增删 /
|
|
1421
|
+
# 权限四种含义,交给用户调等于把那四件事的可读性也交出去),
|
|
1422
|
+
# 它住在 design/web-ui/README 的决定里,以及 tokens.css 那条「色彩只用来
|
|
1423
|
+
# 表达信息」。这一行只留用户用得上的那半句:就这两套,别找第三套。
|
|
1424
|
+
appearance_note: '只有浅色和深色两套。颜色在这一稿里是有含义的(状态、语法、增删、权限),所以不开放自定义。'
|
|
1425
|
+
theme: '主题'
|
|
1426
|
+
theme_field: '配色'
|
|
1427
|
+
theme_system: '跟随系统'
|
|
1428
|
+
theme_light: '浅色'
|
|
1429
|
+
theme_dark: '深色'
|
|
1430
|
+
# 「实时切换」这句是「跟随系统」区别于另外两档的**唯一**行为差别,不写的话
|
|
1431
|
+
# 用户会以为三个选项只是三个值
|
|
1432
|
+
theme_hint: '跟随系统时随 prefers-color-scheme 实时切换,不用刷新。现在系统是{now}。'
|
|
1433
|
+
lang: '语言'
|
|
1434
|
+
lang_field: '界面语言'
|
|
1435
|
+
lang_system: '跟随浏览器'
|
|
1436
|
+
# ⚠️ 下面两条**两份 catalog 里的值刻意完全相同**:一个语言选项要用它
|
|
1437
|
+
# **自己的**语言写出来。翻成 'Chinese' 的话,看不懂英文的人正好看不懂那一档,
|
|
1438
|
+
# 而他才是需要点它的那个人。
|
|
1439
|
+
lang_zh: '中文'
|
|
1440
|
+
lang_en: 'English'
|
|
1441
|
+
lang_hint: '只切前端自己的字。诊断和服务端返回的提示跟着 epoch web 那个进程的语言走,切成英文之后它们仍然是中文。'
|
|
1442
|
+
|
|
1443
|
+
# 设置项的来源层与改写(方案 43 §七):模型与 Provider / 记忆与上下文 / 预算。
|
|
1444
|
+
#
|
|
1445
|
+
# ⚠️ 这一整块的措辞规矩原来是**别承诺改得动**(那时一个写控件都没有)。
|
|
1446
|
+
# 2026-08-15 起 model / maxTurns 两行真能改了,规矩因此改成两条:
|
|
1447
|
+
#
|
|
1448
|
+
# 1. 改不动的那些行照旧只说「去哪儿改」,不说「点这里改」—— 它们占这一节的
|
|
1449
|
+
# 绝大多数(`SettingsFileSchema` 只放行三个键)
|
|
1450
|
+
# 2. 改得动的那两行,每一句话都要说**后果**:写进去会不会被压回去、
|
|
1451
|
+
# 写完什么时候生效、这个进程现在还在用哪个值。少说任何一句,
|
|
1452
|
+
# 用户就会以为「保存成功」等于「现在就是这个值了」,而那是假的
|
|
1453
|
+
set:
|
|
1454
|
+
# 三种「没有内容」的原因各说各的,同 sec 那三条
|
|
1455
|
+
no_session: '还没有会话。项目级那两层设置跟着工作区走,而工作区按会话绑 —— 没有会话就没有「哪一份 .epoch/settings.json」可说。'
|
|
1456
|
+
loading: '正在读取设置来源…'
|
|
1457
|
+
unset: '未设置'
|
|
1458
|
+
why: '为什么是这个值'
|
|
1459
|
+
chain_win: '生效'
|
|
1460
|
+
g_other: '其它'
|
|
1461
|
+
# 「哪些键能被项目覆盖是有名单的」——这一句必须在屏幕上。不写的话,用户看到
|
|
1462
|
+
# 别的行没有链条只会以为「这一行还没做」,然后去 .epoch/settings.json 里
|
|
1463
|
+
# 写一个永远不生效的键
|
|
1464
|
+
overridable: '只有这几个键能被项目级 / 项目本地 / 命令行 / 企业托管压过去:{keys}。其余每一项都只有内置默认和用户级两层 —— 写进 .epoch/settings.json 也不会生效,启动诊断里会报一条「未知配置项」。'
|
|
1465
|
+
overridable_perm: '名单里的 permissions 是三张列表(allow / ask / deny),合并时取并集而不是高层覆盖,所以它的来源标注是逐条的 —— 在「安全中心 › 权限规则」那一节。'
|
|
1466
|
+
# 托管锁定那一行**说的是为什么改不了**,不是把控件灰掉:灰掉只说明「点不了」,
|
|
1467
|
+
# 而这个「为什么」在别处根本查不到,它来自一个机器级文件
|
|
1468
|
+
managed_locked: '这一项由这台机器上的企业托管设置定,改用户级或项目级都不会生效。那份文件普通用户写不了,要改得找管理员。'
|
|
1469
|
+
|
|
1470
|
+
# ---- 改一行设置(方案 43 §七的写那一半)----
|
|
1471
|
+
#
|
|
1472
|
+
# 只有 model 和 maxTurns 两行画得出这几个控件,其余的行连「改」按钮都没有。
|
|
1473
|
+
edit: '改'
|
|
1474
|
+
cancel: '取消'
|
|
1475
|
+
save: '保存'
|
|
1476
|
+
new_value: '新的值'
|
|
1477
|
+
write_layer: '写进'
|
|
1478
|
+
# 脏态那个芯片。「按保存才算数」这条规矩的全部代价就在这三个字上 ——
|
|
1479
|
+
# 不写的话用户会以为输入框里的字已经落盘了
|
|
1480
|
+
dirty: '未保存'
|
|
1481
|
+
# ⚠️ 这两句是这一屏最要紧的话,而且它们**在按下保存之前**就在屏幕上。
|
|
1482
|
+
# 写完再说「刚才那下被压回去了」在时序上对、在用处上是零。
|
|
1483
|
+
# 两档措辞分开是因为下一步不一样:一个改写更高的层,一个去信任那个目录
|
|
1484
|
+
futile_shadowed: '写进{layer}也不会生效:{by}也写了这一项,它排在更上面,加载时会把你写的这个压回去。要让它生效,请改写{by}或更高的层。'
|
|
1485
|
+
futile_untrusted: '这个工作区还没被信任,{layer}里的设置在加载时会被整份丢掉(只有 deny 规则例外),所以写进去等于没写。先在「安全中心 › 工作区与信任」里信任这个目录,或者改写用户级 —— 用户级不过信任闸门。'
|
|
1486
|
+
# 写完那三句。第二句(本进程仍在用什么)不能省:上面那一行印的还是旧值,
|
|
1487
|
+
# 不说的话用户会以为界面没刷新
|
|
1488
|
+
wrote: '已写进{layer}:{value}'
|
|
1489
|
+
wrote_stale: '这个进程启动时读过一次配置就不再重读,所以它现在用的仍然是 {value} —— 重启 epoch web 之后才会换成刚写的那个值。新开一个会话也不行,配置是整个进程一份。'
|
|
1490
|
+
|
|
1491
|
+
# ---- 模型与 Provider ----
|
|
1492
|
+
#
|
|
1493
|
+
# ⚠️ 2026-08-18 精简过。原文是「值来自 ~/.epoch/config.yaml,项目仓库也能定
|
|
1494
|
+
# model —— 但只有目录 epoch trust add 过之后才生效。每一行右边标着它赢在
|
|
1495
|
+
# 哪一层。」用户报的一整类问题里它是头一条:**三句话有两句在讲这个界面自己
|
|
1496
|
+
# 怎么画的**(「每一行右边标着…」),而第三句把「信任闸门」这个内部概念摆到
|
|
1497
|
+
# 了第一屏。判据没丢,只是搬了家 ——
|
|
1498
|
+
#
|
|
1499
|
+
# - 「项目仓库也能定,且要 trust 过」:那件事每一行右边那枚来源徽标 + 点开
|
|
1500
|
+
# 的六层链条自己说得更准(`settings/config.tsx` 的 `Why`);
|
|
1501
|
+
# - 「每一行右边标着它赢在哪一层」:那是**教人认界面**,而那枚徽标就在
|
|
1502
|
+
# 视线里,不用一句话去指。
|
|
1503
|
+
lede_model: '这个进程此刻在用的模型和 provider。要改:编辑 ~/.epoch/config.yaml,或者跑 epoch model。'
|
|
1504
|
+
k_model: '主模型'
|
|
1505
|
+
k_models_utility: '副模型'
|
|
1506
|
+
h_models_utility: '压缩上下文、生成会话标题这类杂活用它。没配就一律走主模型。'
|
|
1507
|
+
k_fallback_model: '降级模型'
|
|
1508
|
+
k_fallback_providers: '降级链'
|
|
1509
|
+
h_fallback_providers: '主 provider 连不上时按这个顺序往下试。'
|
|
1510
|
+
|
|
1511
|
+
# ---- 记忆与上下文 ----
|
|
1512
|
+
lede_memory: '前两项决定一轮能装多少、能跑多少步;后两项决定装不下的时候怎么办。上下文窗口填大于模型真实上限的值只会换来一次 400。'
|
|
1513
|
+
k_context_length: '上下文窗口'
|
|
1514
|
+
# ⚠️ 2026-08-17 改过一次:原文是「配了就以配的为准」,那是**反的**。
|
|
1515
|
+
# 装配走 `getCapability(config.model).contextLength`,这一行只在元数据
|
|
1516
|
+
# 查不到时才顶上(runtime 的 `resolveModelCapability()` 的 catch 支)。
|
|
1517
|
+
# 照原文读的人会以为把这个数填大就能少压几次 —— 它一次都不生效,
|
|
1518
|
+
# 而下面那条压缩线印的从来是引擎真在比的那个窗口,不是这一行
|
|
1519
|
+
h_context_length: '只在模型元数据查不到时才用这个值兜底;查得到就以元数据为准,填了也不生效。下面那条压缩线算的是引擎真在用的那个窗口。'
|
|
1520
|
+
k_max_turns: '单轮最多几步'
|
|
1521
|
+
h_max_turns: '一轮里模型最多调用几次工具。到顶就停下来把话说完。'
|
|
1522
|
+
k_compression_enabled: '快满时自动压缩'
|
|
1523
|
+
# 「会重写早期消息」是关掉它的**唯一**正当理由,所以它排在最前面。
|
|
1524
|
+
# 只说「省 token」的话,不想被改写的用户读不出这一行和他有关
|
|
1525
|
+
h_compression_enabled: '压缩会把早期消息重写成一份摘要,原文仍在会话库里、模型能搜回来。压完在对话流里留一条记录。'
|
|
1526
|
+
k_compression_threshold: '压到几成开始'
|
|
1527
|
+
h_compression_threshold: '提示词占到上下文窗口的这个比例时开始压。0 到 1 之间。'
|
|
1528
|
+
|
|
1529
|
+
# 自动压缩那一块(`settings/context-memory.tsx`)。
|
|
1530
|
+
# ⚠️ 这几句的规矩同安全中心:**说后果,不说状态词**。
|
|
1531
|
+
ctx_line: '这份配置下,提示词涨到 {tokens} token 时开始压缩 —— 上下文窗口 {window} 的 {percent}%。'
|
|
1532
|
+
# 「到线」不等于「下一轮一定压」:冷却期和熔断只有引擎知道。
|
|
1533
|
+
# 不写这一句的话,用户会拿上面那个数当承诺,然后为一次没压的跨线报 bug
|
|
1534
|
+
ctx_line_caveat: '这是那条线本身。刚压过不久、或者连着几次压缩都失败时,跨线了也会先不压。'
|
|
1535
|
+
ctx_off: '自动压缩已关掉。长会话撞到上下文窗口上限时没有任何自动补救 —— 那一轮会直接失败,而且上下文不会自己变短。手动的 /compact 不受这个开关影响,仍然能用。'
|
|
1536
|
+
ctx_no_switch: '这两行在界面上改不了,和上面两行一样:能被项目级配置覆盖的键只有那三个(下面那份名单),压缩不在里面 —— 否则一个仓库就能让你在它里面开的长会话悄悄撞满窗口。改它要自己写 ~/.epoch/config.yaml:'
|
|
1537
|
+
ctx_restart: '改完要重启 epoch web 那个进程才生效 —— 这个进程装配时读过一次配置就不再重读。'
|
|
1538
|
+
|
|
1539
|
+
# ---- 预算 ----
|
|
1540
|
+
lede_budget: '金额只在配了上限之后才出现在界面上。没配上限时这几行都是「未设置」,那也是默认状态。'
|
|
1541
|
+
k_budget_max_cost: '花费上限'
|
|
1542
|
+
k_budget_max_tokens: 'Token 上限'
|
|
1543
|
+
h_budget_max_tokens: '两把尺子都配了的话,闸门报更紧的那一份。'
|
|
1544
|
+
k_budget_warn_at: '用到几成时提醒'
|
|
1545
|
+
h_budget_warn_at: '百分比。只在跨线那一次提醒,不每轮刷屏。'
|
|
1546
|
+
k_budget_unknown_pricing: '模型没有定价数据时'
|
|
1547
|
+
h_budget_unknown_pricing: 'block 是直接拦下,warn 是告警放行。没有定价数据时花费是「不知道」而不是 0,两者必须分开处理。'
|
|
1548
|
+
|
|
1549
|
+
# 安全中心(方案 42 PR-3 / 决定 16):沙箱 + 权限 + 工作区信任 + 策略,一屏。
|
|
1550
|
+
#
|
|
1551
|
+
# ⚠️ 这一整块的措辞规矩是**说后果,不说状态词**。「未信任」三个字用户读不出
|
|
1552
|
+
# 它意味着什么,「它的 .epoch/ 一个字都不读」才读得出。这一页存在的理由就是
|
|
1553
|
+
# 把四个做得最重、露得最少的底座摊开 —— 摊开之后还是术语的话等于没摊。
|
|
1554
|
+
sec:
|
|
1555
|
+
# ⚠️ 2026-08-17 改过一次。原文是「改这里任何一项的入口都在配置文件或者
|
|
1556
|
+
# 命令行」,而**权限档那一格不是** —— 它的控件就在这个界面的输入框那一行
|
|
1557
|
+
# (`composer/permission-menu.tsx`,决定 20 ③)。照原文读的人会去翻
|
|
1558
|
+
# `config.yaml`,而那个键改的是「下次启动从哪一档起」,不是他眼前这一档
|
|
1559
|
+
#
|
|
1560
|
+
# ⚠️ 2026-08-18 又精简了一次。上一版是「这一页全是只读。改每一项的入口都写在
|
|
1561
|
+
# 它那一节里 —— 多数在配置文件或者命令行,权限档那一格在输入框那一行。」
|
|
1562
|
+
# 三句话里有两句在**讲这一页的读写属性和索引方式**,而用户点进「安全中心」
|
|
1563
|
+
# 想知道的是「现在安不安全、谁管得住它」。上面那条 2026-08-17 的判据仍然算数
|
|
1564
|
+
# (权限档的入口特殊),落实的地方换成了权限档那一节自己的 `perm_where` ——
|
|
1565
|
+
# 那句话就在那一格旁边,比在页首列一张索引准。
|
|
1566
|
+
lede: '这个 agent 现在能碰什么、谁在管着它。'
|
|
1567
|
+
# 三种「没有内容」的原因各说各的。合成一句「加载失败」的话,这一条(其实
|
|
1568
|
+
# 什么都没坏)会被读成故障
|
|
1569
|
+
no_session: '还没有会话。安全状态里有三样东西按会话绑:工作区、它的信任判定、以及跟着信任走的项目级策略目录 —— 没有会话就没有「哪一个」可说。'
|
|
1570
|
+
loading: '正在读取安全状态…'
|
|
1571
|
+
|
|
1572
|
+
# ---- 状态总览的四张卡 ----
|
|
1573
|
+
card_sandbox: '沙箱'
|
|
1574
|
+
card_perm: '权限档位'
|
|
1575
|
+
card_space: '工作区'
|
|
1576
|
+
card_rules: '生效规则'
|
|
1577
|
+
perm_d_managed: '被企业托管层锁住,这台机器上改不动。'
|
|
1578
|
+
# ⚠️ 2026-08-17 改过一次。原文是「各层配置合并之后的结果」,只在
|
|
1579
|
+
# 「这个会话开出来之后没人动过它」时才成立 —— 换过一次档、或者进出过一次
|
|
1580
|
+
# plan 模式之后,那句话就在给一个**运行期值**挂配置来源层,而
|
|
1581
|
+
# `settings/security-perm.tsx` 第 3 条逐字写着这比不挂更糟
|
|
1582
|
+
perm_d_local: '这个会话此刻在跑的那一档,不是盘上写的那个。'
|
|
1583
|
+
space_none: '一个都不知道'
|
|
1584
|
+
space_all_trusted: '{count} 个 · 全已信任'
|
|
1585
|
+
space_untrusted: '{count} 个 · {untrusted} 个未信任'
|
|
1586
|
+
space_at: '当前绑在 {root}'
|
|
1587
|
+
space_unbound: '这个会话没绑工作区。'
|
|
1588
|
+
rules_n: '权限 {count} 条 · 策略 {policy} 条'
|
|
1589
|
+
rules_none: '一条都没有'
|
|
1590
|
+
|
|
1591
|
+
# ---- 沙箱 ----
|
|
1592
|
+
g_sandbox: '沙箱'
|
|
1593
|
+
sb_field: '隔离后端'
|
|
1594
|
+
sb_os: 'OS 级隔离({backend})'
|
|
1595
|
+
sb_process: '仅进程隔离({platform})'
|
|
1596
|
+
# ⚠️ 探测不到时**如实说探测不到**,不写「已启用」—— 那是这一屏最贵的一句假话
|
|
1597
|
+
sb_absent: '未探测'
|
|
1598
|
+
sb_absent_d: '隔离层自己没起来,连探测到了什么都说不出。这不等于「仅进程隔离」。'
|
|
1599
|
+
sb_absent_long: '隔离层没起来,所以连探测到了什么都说不出。这不等于「仅进程隔离」—— 那是一个已知结论,而这里是一个未知。'
|
|
1600
|
+
# 后三档是**三句不同的下一步**。`backend-missing` 那条刻意不插 {backend}:
|
|
1601
|
+
# 那时它的值就是 none,「装上 none」是一句会把人带沟里的话
|
|
1602
|
+
sb_os_isolated: '{platform} 上的 {backend} 真把它关起来了,这一档没什么要解释的。'
|
|
1603
|
+
sb_backend_unavailable: '{platform} 本该有实现,但探测没过 —— 后端在,调不起来。'
|
|
1604
|
+
sb_backend_missing: '{platform} 上有实现,但依赖没装:Linux 要 bubblewrap,也可能是内核禁了非特权用户命名空间。'
|
|
1605
|
+
sb_platform_unsupported: '{platform} 上压根没有实现(Windows 要 AppContainer,Node 没有绑定),装什么都没用。'
|
|
1606
|
+
sb_covers: '走沙箱的工具'
|
|
1607
|
+
# ⚠️ 2026-08-18 **整条撤了**。原文是「这份清单从服务端下发,不是浏览器写死的
|
|
1608
|
+
# —— 工具名是协议级的标识,编一份出来会在工具改名那天开始撒谎。」
|
|
1609
|
+
#
|
|
1610
|
+
# 这是**一句说给自己听的话**:用户看着一张工具名清单,不会问「这是前端写死的
|
|
1611
|
+
# 吗」。它真正的读者是下一个想在浏览器里补一份硬编码清单的人 ——
|
|
1612
|
+
# 而那个人该在代码里看到它。判据搬去了 settings/security-sandbox.tsx。
|
|
1613
|
+
sb_covers_none: '没有任何工具走隔离层。'
|
|
1614
|
+
sb_excludes: '明确不在沙箱里'
|
|
1615
|
+
sb_excludes_none: '没有点名在外的工具。'
|
|
1616
|
+
# ⚠️ 2026-08-18 精简。「这不是『其余全部』,是一份点名清单」那半句在解释
|
|
1617
|
+
# **这份清单的语义**,而下面就是清单本身、一眼看得出它很短。留下的是唯一
|
|
1618
|
+
# 那件用户必须知道的事:**终端不在沙箱里**,它靠另一套东西挡。
|
|
1619
|
+
sb_excludes_why: '终端不走沙箱 —— 挡它的是危险命令表和权限闸门,「沙箱已启用」不包它。'
|
|
1620
|
+
|
|
1621
|
+
# ---- 权限档位 ----
|
|
1622
|
+
g_perm: '权限档位'
|
|
1623
|
+
perm_field: '当前档位'
|
|
1624
|
+
perm_unknown: '读不出'
|
|
1625
|
+
# ⚠️ 2026-08-18 精简。原文是「档位管的是『拿不准的时候默认怎么办』。下面那张
|
|
1626
|
+
# 规则表压过它 —— 一条 deny 在任何档位下都拦,包括 bypass。」
|
|
1627
|
+
# 后半句(谁压过谁)是**这两块界面之间的关系**,写在档位这一格上等于要求用户
|
|
1628
|
+
# 先建立一个模型再看数。它搬到了下面规则表自己的 lede 上 ——
|
|
1629
|
+
# 「一条 deny 连 bypass 都拦」是那张表最该先说的事,而不是这一格的。
|
|
1630
|
+
perm_hint: '拿不准的时候默认怎么办。'
|
|
1631
|
+
# ⚠️ 这一句 2026-08-17 换过(方案 56 §1.3)。上一版是「各层配置合并完就把
|
|
1632
|
+
# 来源丢了,只剩一个值」—— **那句话现在是假的**:来源层拿得到,下面「默认」
|
|
1633
|
+
# 那一行就挂着它。
|
|
1634
|
+
#
|
|
1635
|
+
# ⚠️ 2026-08-18 **整条撤了**。上一版是「徽标只挂在『默认』那一行:来源层是
|
|
1636
|
+
# 配置值的属性,而『此刻』那一档是运行期改出来的 —— 给一个运行期值挂配置
|
|
1637
|
+
# 来源层,比不挂更糟。」那是一句**写给下一个改这个界面的人**的话:它解释的是
|
|
1638
|
+
# 「为什么这一行没有徽标」,而屏幕上根本没人在问这个问题 —— 少一枚徽标不会
|
|
1639
|
+
# 让任何人困惑,一句解释少一枚徽标的话反而会。
|
|
1640
|
+
#
|
|
1641
|
+
# 判据本身仍然要守(**别给「此刻」那一行挂来源徽标**,那会把一个运行期值
|
|
1642
|
+
# 说成来自某个配置文件),它现在住在 `settings/security-perm.tsx` 的注释里。
|
|
1643
|
+
managed_field: '企业托管'
|
|
1644
|
+
managed_on: '这台机器上有一份企业托管设置,它压过所有其他层。'
|
|
1645
|
+
# **`present: false` 是常态**。所以这一支是平淡的一句话,不是警告 ——
|
|
1646
|
+
# 一份默认就画成「已被 IT 锁定」的界面会让人以为权限档压根不能改
|
|
1647
|
+
managed_off: '这台机器上没有企业托管设置。这是常态。'
|
|
1648
|
+
managed_rules_only: '托管层打开了「只认托管的权限规则」:其余各层的三张表整份作废。'
|
|
1649
|
+
managed_bypass_off: '托管层禁掉了「跳过全部检查」那一档。'
|
|
1650
|
+
|
|
1651
|
+
# ---- 权限规则 ----
|
|
1652
|
+
g_rules: '权限规则'
|
|
1653
|
+
rules_empty: '一条权限规则都没有,所有决定都落到上面那个档位上。'
|
|
1654
|
+
rules_no_exec: 'allow 里放行任意代码执行的规则(比如 terminal(python:*))会在加载时被拒绝并告警:那等于把整个权限系统绕过去。'
|
|
1655
|
+
shadow_lede: '有规则被别的规则盖住了:'
|
|
1656
|
+
shadow_shadowed: '{rule} 永远不生效 —— {by} 把它整个盖住了。'
|
|
1657
|
+
shadow_partial: '{rule} 被 {by} 挖掉了一块。这往往是故意的。'
|
|
1658
|
+
shadow_redundant: '{rule} 写了等于没写 —— 同一张表里 {by} 已经更宽。'
|
|
1659
|
+
# 层号逐字照 core/src/config/sources.ts 那张表,不另起一套叫法
|
|
1660
|
+
layer_builtin: '① 内置默认'
|
|
1661
|
+
layer_plugin: '①ᐟ⁵ 插件'
|
|
1662
|
+
layer_user: '② 用户级'
|
|
1663
|
+
# 环境变量**不在文档那张层表里**,因为它不是一个「设置层」:它在配置加载
|
|
1664
|
+
# 那一相覆盖 config.yaml,位置和用户级同一格 —— 于是它会被项目级压回去
|
|
1665
|
+
layer_env: '环境变量'
|
|
1666
|
+
layer_project: '③ 项目级'
|
|
1667
|
+
layer_project_local: '④ 项目本地'
|
|
1668
|
+
layer_cli: '⑤ 命令行'
|
|
1669
|
+
layer_managed: '∞ 企业托管'
|
|
1670
|
+
# ⚠️ 两份 catalog 里这一条的值刻意相同:`unknown` 不是「查不到」,它有确切
|
|
1671
|
+
# 含义 —— 规则直接写在 config.yaml 里,不属于任何设置层。印文件名比印
|
|
1672
|
+
# 「未知」有用得多,用户下一步正是要去打开它
|
|
1673
|
+
layer_unknown: 'config.yaml'
|
|
1674
|
+
|
|
1675
|
+
# ---- 工作区与信任 ----
|
|
1676
|
+
g_space: '工作区与信任'
|
|
1677
|
+
known_field: '已知工作区'
|
|
1678
|
+
known_empty: '还没有任何目录被问过信任。'
|
|
1679
|
+
known_hint: '信任跟着目录走,不跟着会话走:同一个目录被两个会话绑上,信任状态也只有一份。'
|
|
1680
|
+
ws_current: '当前'
|
|
1681
|
+
# 四档,因为 level 和 trusted 能不一样,而差别处正是用户要看的。
|
|
1682
|
+
# 「你拒过它」和「还没问过」的下一步完全不同,不能合成一句「未信任」
|
|
1683
|
+
ws_trusted: '已信任,它的 .epoch/ 会被读。'
|
|
1684
|
+
ws_gate_off: '信任闸门整个没开(trust.enabled 是 false),所以它按已信任算。'
|
|
1685
|
+
ws_refused: '你拒绝过它。它的 .epoch/ 一个字都不读。'
|
|
1686
|
+
ws_unknown: '还没问过。epoch trust add 之后才会读它的 .epoch/。'
|
|
1687
|
+
bound_field: '当前工作区'
|
|
1688
|
+
unbound: '这个会话没绑工作区 —— 这是真的一档,不是还没查到。相对路径没有基准,项目级的配置、角色、技能一律不加载。'
|
|
1689
|
+
main_field: '主根 {name}'
|
|
1690
|
+
main_hint: '相对路径只按主根解析。项目级的 .epoch/settings.json、agents/、skills/、policies/ 也只从这里发现。'
|
|
1691
|
+
loaded_files: '已加载的约定文件:'
|
|
1692
|
+
loaded_none: '这个根里没有约定文件。'
|
|
1693
|
+
skipped_line: '这些约定文件在,但一个字都没读。'
|
|
1694
|
+
skipped_why: '它们进的是 system prompt,所以要先过信任闸门。信任这个目录之后它们就会加载:'
|
|
1695
|
+
extra_field: '额外目录'
|
|
1696
|
+
layer_add_dir: '--add-dir'
|
|
1697
|
+
extra_none: '没有额外目录。'
|
|
1698
|
+
extra_desc: '只放行路径'
|
|
1699
|
+
# ⚠️ 2026-08-18 精简。三件「它不是什么」(相对路径、配置发现、角色技能)
|
|
1700
|
+
# 收成一句,末句「所以这几行不标信任状态 —— 信任闸门根本没走到它头上」撤掉:
|
|
1701
|
+
# 那是在解释**这几行为什么少一枚徽标**(同 perm_no_layer 那一类)。
|
|
1702
|
+
# 判据在 settings/security-space.tsx 的注释里。
|
|
1703
|
+
extra_hint: '只是放宽了读写范围。它们不是第二个工作区:相对路径、项目配置、角色和技能都只认主根。'
|
|
1704
|
+
unloaded_extra: '额外目录里有约定文件,一律不加载:'
|
|
1705
|
+
unloaded_extra_way: '这一处和主根不同:信任了也不会加载,因为「额外目录的约定文件要单独过一次信任判定」这件事还没做。要它生效就把那个目录设成主根,或者把内容并进主根的约定文件。'
|
|
1706
|
+
trust_only_deny: '未信任的仓库里,权限规则只有 deny 那一张表生效 —— 它只能减,不能加。'
|
|
1707
|
+
trust_not_permission: '信任不改权限档位。它只回答「这个目录里的文件能不能影响我的指令」,不回答「这次操作要不要问你」—— 后者永远是上面那张规则表说了算。'
|
|
1708
|
+
|
|
1709
|
+
# ---- 策略规则 ----
|
|
1710
|
+
g_policy: '策略规则'
|
|
1711
|
+
policy_lede: '和上面那张权限规则表是两套东西:权限规则管「要不要问我」,策略规则管「这个工具碰这个路径算什么」,而且 deny 的时候能带一句自己写的话给用户。'
|
|
1712
|
+
policy_none: '没有任何策略目录参与扫描。'
|
|
1713
|
+
policy_user: '用户级,跟着这台机器走。'
|
|
1714
|
+
policy_project: '项目级,跟着当前工作区走 —— 换个会话就是另一个仓库的。'
|
|
1715
|
+
policy_n: '{count} 条'
|
|
1716
|
+
policy_skipped: '{dir} 整份没加载 —— 那个工作区不受信任。'
|
|
1717
|
+
policy_skipped_why: '不是「只留 deny」,是一条都没读。'
|
|
1718
|
+
policy_asym: '这里和权限那边不对称,值得单独说一句:权限在未信任时只留 deny,策略是整份不加载。一条 [[rule]] 里工具名、路径模式、判定是绑在一起的,deny_message 还会原样显示给用户 —— 想留「只有 deny 生效」的口子,就得逐条重写别人写的规则语义。'
|
|
1719
|
+
|
|
1720
|
+
# ---- 审计流水 ----
|
|
1721
|
+
#
|
|
1722
|
+
# 上面几节答「规矩是什么」,这一节答「按那些规矩实际发生了什么」。
|
|
1723
|
+
# ⚠️ 一条都没有时整节不画(决定 20 ①),所以这里没有空态文案,那不是漏了。
|
|
1724
|
+
g_audit: '判过什么'
|
|
1725
|
+
audit_lede: '权限层这一程的裁决,最新的在最前面。'
|
|
1726
|
+
# ⚠️ 这一句是这一节最重要的一句,别删:它说的是这本账记什么、不记什么。
|
|
1727
|
+
# 不写的话,用户看不见检查点回滚和斜杠命令的工具收窄,会以为那两件事没发生
|
|
1728
|
+
# ⚠️ 2026-08-18 精简。两个例子(检查点回滚、斜杠命令收窄工具表)连着一句
|
|
1729
|
+
# 「都不过权限层」是**在讲我们的分层**。用户要的是这张表的边界:
|
|
1730
|
+
# 它只记权限裁决。举例子的那半句搬进了 settings/security-audit.tsx。
|
|
1731
|
+
audit_scope: '只记权限层做过的裁决,不是所有危险动作。'
|
|
1732
|
+
audit_col_at: '时间'
|
|
1733
|
+
audit_col_tool: '工具'
|
|
1734
|
+
audit_col_type: '类型'
|
|
1735
|
+
audit_col_target: '目标'
|
|
1736
|
+
audit_col_outcome: '结论'
|
|
1737
|
+
audit_col_code: '原因'
|
|
1738
|
+
audit_granted: '放行'
|
|
1739
|
+
audit_denied: '拦截'
|
|
1740
|
+
audit_dropped: '更早的 {dropped} 条已经被挤掉了 —— 这张表最多留 500 条。你看到的不是全部。'
|
|
1741
|
+
audit_ephemeral: '这本账只活在这个服务进程里:不落盘、不导出、也不进遥测。进程一退就没了,所以它不是一份完整的历史。'
|
|
1742
|
+
# 十二个原因码。每一条都要答出「下一步该去看哪儿」,而不是把码翻译一遍
|
|
1743
|
+
audit_code_dangerous_command: '命中危险命令表'
|
|
1744
|
+
audit_code_sensitive_path: '命中敏感路径表'
|
|
1745
|
+
audit_code_obfuscated: '命令含混淆构造,非交互下无人可确认'
|
|
1746
|
+
audit_code_rule_match: '命中权限规则'
|
|
1747
|
+
audit_code_policy_rule: '命中策略规则'
|
|
1748
|
+
audit_code_approval_cache: '你以前对这个目标做过同样的决定'
|
|
1749
|
+
audit_code_sandbox_isolation: '按实测的沙箱隔离级别判'
|
|
1750
|
+
audit_code_level_default: '没有规则命中,落到权限档位上'
|
|
1751
|
+
audit_code_headless_preauthorized: '非交互,命中预授权白名单'
|
|
1752
|
+
audit_code_headless_unauthorized: '非交互,未预授权'
|
|
1753
|
+
audit_code_user_approved: '你批准了'
|
|
1754
|
+
audit_code_user_denied: '你拒绝了'
|
|
1755
|
+
|
|
1756
|
+
# ---- 末尾追加(2026-08-16)----
|
|
1757
|
+
#
|
|
1758
|
+
# 追加在这一段末尾而不是紧挨着 perm_hint:这一轮 web.ui.sec.* 只许追加,
|
|
1759
|
+
# 中间插一行会和别的分支撞车。屏幕上的顺序由 settings/security-perm.tsx
|
|
1760
|
+
# 说了算,和这里的行序无关。
|
|
1761
|
+
#
|
|
1762
|
+
# 这一句是「档位那一格该不该长出分段器」判完之后补的。结论是**不画**
|
|
1763
|
+
# (全文在 settings/security-perm.tsx「三处刻意不画」第 1 条:那个控件已经
|
|
1764
|
+
# 在输入框那一行上了,而稿子留给设置屏的分段器答的是另一个值)。
|
|
1765
|
+
# 这一屏不画控件时的通用形态是「如实陈述 + 说出改它的真正路径」,这就是那句话。
|
|
1766
|
+
#
|
|
1767
|
+
# ⚠️ 「立刻生效」这半句不能省:这一页其余每一行答的都是「盘上写的是什么、
|
|
1768
|
+
# 下次启动怎么样」,少了它,用户会把换档读成「改了默认档」(决定 20 ④)
|
|
1769
|
+
#
|
|
1770
|
+
# ⚠️ 2026-08-18 重写。原文是「这一格印的是这个会话此刻在跑的那一档。改它的
|
|
1771
|
+
# 地方在输入框那一行的档位那一格:改完立刻生效,不写盘,也不改下次启动从哪一档
|
|
1772
|
+
# 起。」现在这一句**同时管两行**(顺带把 `perm_two_rows` 整条吃掉了):
|
|
1773
|
+
# 用户要的是「各自去哪儿改」,不是「这一格印的是什么」——
|
|
1774
|
+
# 那件事上面那两个词(此刻 / 默认)已经说了。
|
|
1775
|
+
perm_where: '上面这一档在输入框那一行改,立刻生效;下面那一档是新会话的起点,改 ~/.epoch/config.yaml 后重启。'
|
|
1776
|
+
|
|
1777
|
+
# ---- 末尾追加(2026-08-17,方案 56 §1.3)----
|
|
1778
|
+
#
|
|
1779
|
+
# 权限档从一格变成两行。⚠️ **第二行恒画,不是「只在两者不同时才画」** ——
|
|
1780
|
+
# 只在不同时画的话,用户学不会「这里本来就有两个值」,第一次看见它冒出来
|
|
1781
|
+
# 会以为出了事。
|
|
1782
|
+
#
|
|
1783
|
+
# 「此刻 / 默认」这两个词短到能定宽对齐,而对齐正是这两行的全部意义:
|
|
1784
|
+
# 眼睛要一眼比出差别。别改成「当前档位 / 默认档位」那种长句。
|
|
1785
|
+
perm_now: '此刻'
|
|
1786
|
+
perm_default: '默认'
|
|
1787
|
+
# 只在两者真不同时才印 —— 它是**对这一次差异的解释**,不是那两行本身。
|
|
1788
|
+
# 恒画的话,绝大多数(没改过档的)会话上会挂着一句假话
|
|
1789
|
+
perm_now_changed: '(本会话改过)'
|
|
1790
|
+
# ⚠️ `perm_two_rows` 2026-08-18 撤了(「两行是两个值……它们刻意不同步」)。
|
|
1791
|
+
# 它讲的两件事现在各有更近的主人:两行分别是什么 → 上面那两个词自己说;
|
|
1792
|
+
# 各自去哪儿改 → `perm_where` 一句话说完。判据全文在
|
|
1793
|
+
# `settings/security-perm.tsx` 那一格的注释里。
|
|
1794
|
+
|
|
1795
|
+
# 能力页三栏(方案 42 PR-1)。
|
|
1796
|
+
#
|
|
1797
|
+
# `g_*` 是来源层的组标题,三栏共用;`g_*_roles` / `g_*_skills` / `g_*_conn`
|
|
1798
|
+
# 是那一组的注解,**按栏分开写** —— 用户在注解这一行找的永远是「这一组的
|
|
1799
|
+
# 东西放在磁盘哪儿」,而三栏的目录不是同一个。
|
|
1800
|
+
cap:
|
|
1801
|
+
title: '能力'
|
|
1802
|
+
back: '返回会话'
|
|
1803
|
+
experts: '身份'
|
|
1804
|
+
skills: '技能'
|
|
1805
|
+
connectors: '连接器'
|
|
1806
|
+
# 说清搜的是哪三处:用户真实的问法是「哪个身份能跑命令」,那个答案只在工具集里
|
|
1807
|
+
search: '搜名字、描述、工具名'
|
|
1808
|
+
# 「还没有会话」和「正在取」分开说:前者其实什么都没坏,合成一句
|
|
1809
|
+
# 「加载失败」会被读成故障
|
|
1810
|
+
no_session: '还没有会话。这三栏是跟着会话走的 —— 「项目」那一层要看它绑的是哪个工作区。'
|
|
1811
|
+
loading: '正在取…'
|
|
1812
|
+
|
|
1813
|
+
# 每一栏开头那段话回答「这东西到底是什么」,不是欢迎语
|
|
1814
|
+
experts_lede: '身份是一段 prompt、一个工具集、一个轮数上限。主 agent 用 delegate_task 把活派出去,每个子 agent 以自己的身份跑在独立的上下文里,只把结论交回来。'
|
|
1815
|
+
skills_lede: '技能是渐进披露的:平时只有下面那一行进上下文,模型觉得用得上才去读正文。所以行尾那个数就是它每一轮的常驻开销。'
|
|
1816
|
+
connectors_lede: '连接器是工具的来源。内置那几个随程序装好,不会连不上;MCP 那些是你自己配的,所以它们带状态。'
|
|
1817
|
+
|
|
1818
|
+
# 五层的顺序就是合并优先级,下面的盖上面的
|
|
1819
|
+
g_builtin: '内置'
|
|
1820
|
+
g_plugin: '插件'
|
|
1821
|
+
g_host: '宿主'
|
|
1822
|
+
g_user: '我的'
|
|
1823
|
+
g_project: '项目'
|
|
1824
|
+
g_mcp: 'MCP'
|
|
1825
|
+
|
|
1826
|
+
g_builtin_roles: '装好就有的,不用配。同名的可以被下面几层覆盖掉。'
|
|
1827
|
+
g_plugin_roles: '装的插件带进来的,在插件自己的 agents/ 目录下。'
|
|
1828
|
+
g_user_roles: '~/.epoch/agents/ 下的 .md,每台机器一份,所有项目共用。'
|
|
1829
|
+
g_project_roles: '这个仓库 .epoch/agents/ 下的 .md,跟着仓库走;同名会盖掉上面几层。'
|
|
1830
|
+
g_host_roles: '装着这个引擎的那个应用自己带的,名字前面会加上它给的前缀。epoch plugin list 里查不到它们 —— 你没装过,也只能在那个应用自己的界面上管。'
|
|
1831
|
+
|
|
1832
|
+
g_user_skills: '~/.epoch/skills/ 下每个技能一个目录,正文写在 SKILL.md 里。'
|
|
1833
|
+
g_plugin_skills: '装的插件带进来的,名字前面会加上插件名。'
|
|
1834
|
+
g_project_skills: '这个仓库 .epoch/skills/ 下的,跟着仓库走。'
|
|
1835
|
+
g_host_skills: '装着这个引擎的那个应用自己带的,名字前面会加上它给的前缀。它随那个应用一起升级,所以在这儿是只读的。'
|
|
1836
|
+
|
|
1837
|
+
g_builtin_conn: '编进程序里的工具插件。这里只说有几个工具,不逐个列名字 —— 三十来个摊开会把下面 MCP 那几台挤出屏幕。'
|
|
1838
|
+
g_mcp_note: '你在 ~/.epoch/mcp.json 里配的。它们的工具名带 mcp__<服务器名>__ 前缀。'
|
|
1839
|
+
# 宿主注入的那一组(方案 44 PR-2)。**必须说清「你改不掉它」** —— 这一屏上
|
|
1840
|
+
# 别的连接器都指向一个用户够得着的文件,而这一组不指向任何文件
|
|
1841
|
+
g_host_conn: '装着这个引擎的那个应用自己带的,不在你的 mcp.json 里,所以你既改不了也删不掉 —— 要管得去那个应用自己的界面。它们的名字带上了它给的前缀,工具名因此是 mcp__<前缀>__<服务器名>__ 开头。'
|
|
1842
|
+
# 插件带来的那一组(方案 44 PR-3)。和上面那条只差一处,而那一处就是这句话
|
|
1843
|
+
# 存在的理由:**这一批用户拿它有办法** —— 查得到是谁带来的,也停得掉。
|
|
1844
|
+
# 所以两条命令必须写出来,否则它读起来和「你管不了」那一组一模一样
|
|
1845
|
+
g_plugin_conn: '装的插件在自己根目录下那份 mcp.json 里带的。名字前面加了插件名,工具名因此是 mcp__<插件名>__<服务器名>__ 开头。内容改不了,但 epoch plugin list 查得到是谁带来的,epoch plugin disable 能整个停掉。'
|
|
1846
|
+
|
|
1847
|
+
# 未信任的工作区**连读都不读**,所以这一组连「有几个」都说不出来
|
|
1848
|
+
project_locked_roles: '这个工作区还没被信任,我们没有读它的 .epoch/agents/ —— 所以这一组的数量也算不出来。信任之后再回来看。'
|
|
1849
|
+
locked_line: '这个工作区还没被信任,它的技能一个都没读。'
|
|
1850
|
+
locked_why: '不是「读了之后禁用」,是从头到尾没打开过那个目录。所以这里连有几个都列不出来。'
|
|
1851
|
+
locked_gate: '闸门挡的是:clone 一个仓库下来,它 .epoch/ 里带的东西不该在你还没看过之前就进模型的上下文。'
|
|
1852
|
+
|
|
1853
|
+
# 身份卡
|
|
1854
|
+
summon: '使用'
|
|
1855
|
+
# 只在「它确实收窄了一个同名的内置角色」时才印 —— 照抄一个和组标题同义的词
|
|
1856
|
+
# 只是把「内置」印两遍
|
|
1857
|
+
overrides_builtin: '覆盖了同名的内置角色'
|
|
1858
|
+
stat_tools: '工具'
|
|
1859
|
+
stat_turns: '轮数上限'
|
|
1860
|
+
unlimited: '不限'
|
|
1861
|
+
turns_global: '按全局'
|
|
1862
|
+
toolset: '工具集'
|
|
1863
|
+
toolset_n: '工具集({count})'
|
|
1864
|
+
unlimited_hint: '没写 tools,所以它拿到的是这一轮全部可用的工具。'
|
|
1865
|
+
# 顿号 / 逗号在这儿是排版,不是文字 —— 中文不带空格,英文带
|
|
1866
|
+
list_sep: '、'
|
|
1867
|
+
narrowed: '{role} 声明的工具里有几个不在上一层的范围内,已经去掉:{cut}。工具白名单只能收窄,不能放宽。'
|
|
1868
|
+
|
|
1869
|
+
# 技能行
|
|
1870
|
+
skill_tokens: '{tokens} tokens'
|
|
1871
|
+
no_toggle: '没有「关掉一个技能」这个开关:只要文件在目录里,上面那一行就会进上下文。不想要它,把那个目录挪走。'
|
|
1872
|
+
|
|
1873
|
+
# 技能正文那一屏(方案 56 §1.2)。
|
|
1874
|
+
#
|
|
1875
|
+
# ⚠️ `skill_view_no_count` 是这一屏上最要紧的一句,而且**恒画**:
|
|
1876
|
+
# 用户最容易搞错的就是「我点开看了一眼,是不是被算成用过它了」。答案是没有 ——
|
|
1877
|
+
# 那条路(`GET .../skills/:name`)不动统计,评分那本账只记模型。
|
|
1878
|
+
# 说「不会影响它的评分」而不是说「不计数」:后者是我们的实现词,
|
|
1879
|
+
# 前者才是用户关心的后果
|
|
1880
|
+
# ---- 导入技能(方案 42 §六,2026-08-18)----
|
|
1881
|
+
# ⚠️ import_hint_local 是这一屏上最要紧的一句,而且**恒画、在输入框上面**:
|
|
1882
|
+
# 用户最容易搞错的一件事就是以为这是个上传框。为什么不做上传,判据全文在
|
|
1883
|
+
# core/src/skill/import.ts 的文件头(一句话:用户级技能目录连信任闸门都没有,
|
|
1884
|
+
# 而 --host 0.0.0.0 那一档下浏览器可能不在用户自己那台机器上)。
|
|
1885
|
+
import_entry: '导入技能'
|
|
1886
|
+
import_title: '导入技能'
|
|
1887
|
+
import_lede: '从这台跑着服务的机器上导入。接受一个含 SKILL.md 的文件夹、一棵「分类/名字」两层的技能树、一个打包好的 .zip,或者单独一份带 frontmatter 的 .md。'
|
|
1888
|
+
# ⚠️ 2026-08-18 精简。后半句(「技能正文会进模型的 system prompt,而用户级
|
|
1889
|
+
# 技能目录没有信任闸门,所以让浏览器往里传字节等于让它决定 system prompt 里
|
|
1890
|
+
# 有什么」)是**这条路为什么不收上传**的安全论证 —— 一条重要的判据,但它的
|
|
1891
|
+
# 读者是下一个想加上传功能的人。
|
|
1892
|
+
#
|
|
1893
|
+
# ⚠️ 判据原文搬进了 capability/skill-import.tsx 的文件头。**别在那儿也删掉**:
|
|
1894
|
+
# 「浏览器不许往 system prompt 里塞字节」是这条路的存在形态本身。
|
|
1895
|
+
import_hint_local: '填服务端那台机器上的路径 —— 这条路不收上传的文件。'
|
|
1896
|
+
import_placeholder: '/Users/you/skills/my-skill'
|
|
1897
|
+
import_path_label: '要导入的绝对路径'
|
|
1898
|
+
import_look: '看看有什么'
|
|
1899
|
+
import_browse: '浏览目录'
|
|
1900
|
+
import_browse_off: '收起'
|
|
1901
|
+
import_pick_here: '就用这一层'
|
|
1902
|
+
import_count: '将导入 {count} 个技能({form})'
|
|
1903
|
+
import_form_markdown: '单份 .md'
|
|
1904
|
+
import_form_skill-dir: '一个技能文件夹'
|
|
1905
|
+
import_form_tree: '一棵技能树'
|
|
1906
|
+
import_form_archive: '一个压缩包'
|
|
1907
|
+
import_files: '{files} 个附属文件 · {bytes} 字节'
|
|
1908
|
+
import_no_desc: '(没有 description —— 它进不了 <available_skills>,模型基本挑不中它)'
|
|
1909
|
+
import_conflict_refuse: ' ⚠️ 用户级已经有同名的,这一份不会导入'
|
|
1910
|
+
import_conflict_shadowed: ' ⚠️ 项目级有同名的,导进来也不生效(同名时项目级赢)'
|
|
1911
|
+
import_confirm: '确认导入这 {count} 个'
|
|
1912
|
+
import_issues: '这几条要看一眼:'
|
|
1913
|
+
import_skipped: '跳过了这几份:'
|
|
1914
|
+
import_done: '导入了 {count} 个,跳过 {skipped} 个'
|
|
1915
|
+
import_failed: '这条路径导不进来'
|
|
1916
|
+
import_again: '再导一份'
|
|
1917
|
+
skill_view: '查看正文'
|
|
1918
|
+
skill_back: '回到技能列表'
|
|
1919
|
+
skill_view_no_count: '看一眼不会影响它的评分 —— 那本账只记模型真的用过它的次数。'
|
|
1920
|
+
# 截断那一句**在正文上面**:读到一半才发现没读全已经晚了
|
|
1921
|
+
skill_truncated: '这份 SKILL.md 太大(一共 {bytes} 字节),下面只是开头那一截。要读全的话直接打开那个文件。'
|
|
1922
|
+
skill_bytes: '一共 {bytes} 字节'
|
|
1923
|
+
|
|
1924
|
+
# 连接器
|
|
1925
|
+
tool_count: '{count} 个工具'
|
|
1926
|
+
# ⚠️ 2026-08-18 改过:原来这句写的是「往 ~/.epoch/mcp.json 里加,或者跑
|
|
1927
|
+
# epoch mcp add」,而 **`epoch mcp add` 这条命令全仓不存在**(`epoch mcp`
|
|
1928
|
+
# 只有 list / status / login / logout 四条)—— 一句让人照着跑却跑不通的话。
|
|
1929
|
+
# 现在指向的是这一屏上真有的那枚按钮
|
|
1930
|
+
mcp_empty: '还没有配 MCP server。按上面那枚「添加 server」加一台,或者直接编辑 ~/.epoch/mcp.json。'
|
|
1931
|
+
mcp_connected: '已连接'
|
|
1932
|
+
mcp_failed: '连不上'
|
|
1933
|
+
mcp_login: '要登录'
|
|
1934
|
+
mcp_retries: '重试过 {count} 次。'
|
|
1935
|
+
mcp_failed_why: '它的工具这一轮整个不可用。改完 mcp.json 之后要重启 epoch web 才会重连。'
|
|
1936
|
+
mcp_login_why: '不是连不上,是没通过它那边的授权 —— 重连多少次都一样。在终端里跑下面这条:'
|
|
1937
|
+
|
|
1938
|
+
# 重连那一枚(方案 56 §1.1)。
|
|
1939
|
+
#
|
|
1940
|
+
# ⚠️ `mcp_reconnect_scope` **恒画,而且必须在按钮旁边** —— 这一屏是会话
|
|
1941
|
+
# 作用域的(能力页按 :id 取数),而 MCP 连接是进程级的。用户要在**按之前**
|
|
1942
|
+
# 知道这一下会动到别的会话,不是事后被告知(决定 20 ①:关于这一下的话,
|
|
1943
|
+
# 出现在做出这一下的地方)。
|
|
1944
|
+
#
|
|
1945
|
+
# ⚠️ 也说清「重连不重读 mcp.json」:底下走的是启动时建好的那个 client,
|
|
1946
|
+
# 所以这一下治的是「对面重启过 / 网线抖了一下」,不是「我刚改了配置」——
|
|
1947
|
+
# 后者仍然要重启,那正是上面 mcp_failed_why 说的
|
|
1948
|
+
mcp_reconnect: '重连'
|
|
1949
|
+
mcp_reconnecting: '重连中…'
|
|
1950
|
+
mcp_reconnect_scope: '这会影响这个进程里所有会话,不只是你正看着的这一段 —— MCP 连接是整个进程共用的一份。它按启动时读到的那份 mcp.json 重连,改过配置的话仍然要重启。'
|
|
1951
|
+
mcp_reconnect_after_login: '先跑上面那条 login,再按这里 —— 新凭据要重连一次才用得上。'
|
|
1952
|
+
|
|
1953
|
+
# 「添加 server」那一屏(2026-08-18)。
|
|
1954
|
+
#
|
|
1955
|
+
# ⚠️ `mcp_add_scope` **恒画,而且在提交按钮旁边** —— 判据同上面
|
|
1956
|
+
# `mcp_reconnect_scope`,但**这句话要说得更远一格**:重连改的是这个进程里
|
|
1957
|
+
# 的一个连接,添加改的是**落盘的 ~/.epoch/mcp.json**,影响这台机器上以后
|
|
1958
|
+
# 每一个 epoch 进程(CLI / TUI / 别的宿主)。照抄重连那一句就少说了一半。
|
|
1959
|
+
#
|
|
1960
|
+
# ⚠️ `mcp_add_env_hint` 说的是一句**不好听但为真**的话:env 明文落盘。
|
|
1961
|
+
# 那是 mcp.json 一直以来的形态,这条路没让它更糟、也没有悄悄让它变好 ——
|
|
1962
|
+
# 藏起来的话,用户会以为从界面填的密钥被加密了。
|
|
1963
|
+
#
|
|
1964
|
+
# ⚠️ `mcp_add_offline` 那一档**不是失败**:配置已经落盘了,只是对面没起来。
|
|
1965
|
+
# 说成失败会让用户以为要重来一遍,而重来一遍会撞上「名字已被占用」
|
|
1966
|
+
#
|
|
1967
|
+
# ⚠️ `mcp_add_locked`:服务端绑在回环之外时,这一组顶上画的**不是一枚灰按钮,
|
|
1968
|
+
# 是这一段带出口的说明**(那枚「添加 server」整个不画)。三个硬要求逐字同
|
|
1969
|
+
# `role_add_locked`,但**「为什么」那一半是这条路自己的**:
|
|
1970
|
+
# 1. 为什么 —— 加一台等于往 mcp.json 里写一条「启动哪个可执行文件」,
|
|
1971
|
+
# 而它影响这台机器上以后每一个 epoch 进程(那半句 `mcp_add_scope` 里
|
|
1972
|
+
# 已经有了,**别在这儿把它丢掉**);
|
|
1973
|
+
# 2. 出路两条(到那台机器上改那个文件 / 去掉 --host 重启)——
|
|
1974
|
+
# 只说「不允许」的话,用户唯一的结论是「这个功能坏了」;
|
|
1975
|
+
# 3. 别写成「权限不足」:他的凭据完全正确,是这条路在这一档下不开。
|
|
1976
|
+
#
|
|
1977
|
+
# ⚠️ **别把 role_add_locked 抄过来**(那句讲的是「往 system prompt 里写字」),
|
|
1978
|
+
# 也别把 mcp_cfg_locked 抄过来(那句的正题是「连看都不给,env 是明文密钥」)。
|
|
1979
|
+
# 这三句在屏幕上会同时出现两句,照抄的那一句会当场变成一句半对的话 ——
|
|
1980
|
+
# 同 capability.ts 文件头对「重连 / 添加」那两句点过名的那一类错
|
|
1981
|
+
mcp_add: '添加 server'
|
|
1982
|
+
mcp_add_title: '加一台 MCP server'
|
|
1983
|
+
mcp_add_name: 'server 名'
|
|
1984
|
+
mcp_add_name_hint: '会变成工具名的一部分(mcp__<名字>__…),所以只收字母数字和 _ -。'
|
|
1985
|
+
mcp_add_transport: '传输类型'
|
|
1986
|
+
mcp_add_command: '可执行文件'
|
|
1987
|
+
mcp_add_args: '参数(一行一个)'
|
|
1988
|
+
mcp_add_url: '地址'
|
|
1989
|
+
mcp_add_env: '环境变量(一行一个 KEY=值)'
|
|
1990
|
+
mcp_add_env_hint: '这几行会明文写进 mcp.json。要存密钥的话别放这儿 —— 那条路是 epoch mcp login,凭据是加密存的。'
|
|
1991
|
+
mcp_add_scope: '这会写进 ~/.epoch/mcp.json —— 不只是这一段对话,也不只是这个进程:这台机器上以后每一次跑 epoch(含 CLI 和 TUI)都会看到它。加完当场连一次,工具这一轮就能用上。'
|
|
1992
|
+
mcp_add_submit: '加进去'
|
|
1993
|
+
mcp_add_submitting: '连接中…'
|
|
1994
|
+
mcp_add_cancel: '取消'
|
|
1995
|
+
mcp_add_done: '已写进 {path}。'
|
|
1996
|
+
mcp_add_offline: '配置加进去了,但这台现在连不上 —— 上面那张卡里有原因。配置没问题的话,等对面起来按一下「重连」就行。'
|
|
1997
|
+
mcp_add_locked: '这一档下没法从界面加 MCP server:服务是用 --host 绑到回环之外的,而我们没法确认你的浏览器就在跑服务的那台机器上。加一台等于往 mcp.json 里写一条「启动哪个可执行文件」——那台当场就会被起起来,而且这台机器上以后每一次跑 epoch(含 CLI 和 TUI)都会照着它起,不只是这一段对话。要加的话:到那台机器上直接改 ~/.epoch/mcp.json,或者去掉 --host 重启一次。'
|
|
1998
|
+
|
|
1999
|
+
# 「看它的配置」那一屏 —— mcp.json 的原文编辑器(2026-08-18)。
|
|
2000
|
+
#
|
|
2001
|
+
# ⚠️ `mcp_cfg_scope` **恒画,而且在按钮上面**,判据同 mcp_add_scope;
|
|
2002
|
+
# 但这一句**又要多说一格**:那一屏删得掉 server,而删掉一台是「静默拿掉别的
|
|
2003
|
+
# 会话正依赖的一段能力」。所以它要同时说清两件事 —— 保存改的是盘上那份,
|
|
2004
|
+
# 应用改的是这个进程里所有会话。
|
|
2005
|
+
#
|
|
2006
|
+
# ⚠️ `mcp_cfg_saved` 后面**必须跟一句**(`mcp_cfg_not_live` 或
|
|
2007
|
+
# `mcp_cfg_apply_dirty`):单独一句「已写进 X」会被读成「生效了」,而这一发
|
|
2008
|
+
# 压根没碰这个进程里的连接。判据同设置页的 WriteReceipt。
|
|
2009
|
+
#
|
|
2010
|
+
# ⚠️ `mcp_cfg_locked` 照 role_add_locked 的三条硬要求写(为什么 / 出路 /
|
|
2011
|
+
# 不带指责语气),但**理由不同**:那一条是「正文会进上下文」,这一条是
|
|
2012
|
+
# 「这份文件里的 env 是明文密钥,而且它写得下任意一条命令」。
|
|
2013
|
+
#
|
|
2014
|
+
# ⚠️ 五档动作那几句是**说明书不是标签**:用户按完「应用」之后要能一眼看出
|
|
2015
|
+
# 哪台被断了、哪台压根没动。`mcp_cfg_act_skipped` 还要说清那个时差
|
|
2016
|
+
# (这个进程里没加,但下次启动时你这台会赢)
|
|
2017
|
+
mcp_cfg_open: '看它的配置'
|
|
2018
|
+
mcp_cfg_title: '编辑 mcp.json'
|
|
2019
|
+
mcp_cfg_path: '配置文件路径:'
|
|
2020
|
+
mcp_cfg_dirty: '未保存'
|
|
2021
|
+
mcp_cfg_loading: '正在读那个文件…'
|
|
2022
|
+
mcp_cfg_absent: '这个文件还不存在 —— 下面那几行是模板,不是你的内容。按「保存」才会真的建出来。'
|
|
2023
|
+
mcp_cfg_editor: 'mcp.json 的内容'
|
|
2024
|
+
mcp_cfg_locked: '这一档下看不了也改不了 mcp.json —— 服务是用 --host 绑到回环之外的,而我们没法确认发出这一下的浏览器就在这台机器上。那份文件里的环境变量是明文存的(可能有密钥),而它同时写得下「启动哪个可执行文件」。出路两条:到跑着服务的那台机器上直接改那个文件;或者去掉 --host 重启一次。'
|
|
2025
|
+
mcp_cfg_reload: '重新读一遍(会丢掉现在这份修改)'
|
|
2026
|
+
mcp_cfg_scope: '这一屏上有两个动作,改的不是同一样东西:「保存」只写 ~/.epoch/mcp.json —— 这台机器上以后每一次跑 epoch(含 CLI 和 TUI)都会读它;「应用到这个进程」才动正在跑的这些连接,而那影响这个进程里所有会话,不只是你正看着的这一段。删掉一台之后,正在用它的那些会话下一轮就调不到它的工具了。'
|
|
2027
|
+
mcp_cfg_save: '保存'
|
|
2028
|
+
mcp_cfg_saving: '正在写…'
|
|
2029
|
+
mcp_cfg_apply: '应用到这个进程'
|
|
2030
|
+
mcp_cfg_applying: '正在应用…'
|
|
2031
|
+
mcp_cfg_apply_dirty: '先保存 —— 「应用」读的是盘上那份,而你手上这份还没写下去。'
|
|
2032
|
+
mcp_cfg_close: '收起'
|
|
2033
|
+
mcp_cfg_saved: '已写进 {path}。'
|
|
2034
|
+
mcp_cfg_not_live: '这个进程里那几台还按启动时那份配置在跑 —— 按一下「应用到这个进程」,或者下次启动时自动生效。'
|
|
2035
|
+
mcp_cfg_applied: '这个进程里的 MCP 已经按盘上那份重排过了:'
|
|
2036
|
+
mcp_cfg_applied_none: '一台都没动 —— 盘上那份和这个进程里正在跑的那几台逐字一致。'
|
|
2037
|
+
mcp_cfg_act_added: '新连上了这一台'
|
|
2038
|
+
mcp_cfg_act_reconnected: '配置变了,按新配置重连过'
|
|
2039
|
+
mcp_cfg_act_unchanged: '没动(配置没变)'
|
|
2040
|
+
mcp_cfg_act_removed: '已断开,它的工具从这一轮起不可用'
|
|
2041
|
+
mcp_cfg_act_skipped: '这个名字被宿主或某个插件那批占着,所以这个进程里没加 —— 下次启动时你这台会赢'
|
|
2042
|
+
|
|
2043
|
+
# 「新建身份」那一屏(2026-08-18)—— 能力页三栏里最后一个补上的写口。
|
|
2044
|
+
#
|
|
2045
|
+
# ⚠️ `role_add_locked` 是这一族里最要紧的一句:服务端绑在回环之外时,
|
|
2046
|
+
# 这一栏顶上画的**不是一个灰按钮,是这一段带出口的说明**。三个硬要求:
|
|
2047
|
+
# 1. 说清**为什么**(那段文字会进往后每一轮的上下文,而这一档下我们没法
|
|
2048
|
+
# 确认发出这一下的浏览器就在那台机器上);
|
|
2049
|
+
# 2. 说清**出路**(去那台机器上手写一份 / 去掉 --host 重启)——
|
|
2050
|
+
# 只说「不允许」的话,用户唯一的结论是「这个功能坏了」;
|
|
2051
|
+
# 3. **别写成「权限不足」**:他的凭据完全正确,是这条路在这一档下不开。
|
|
2052
|
+
#
|
|
2053
|
+
# ⚠️ `role_add_scope` **恒画,而且在提交按钮上面**(同 mcp_add_scope)。
|
|
2054
|
+
# 它比那一句**多说一件只有这条路有的事**:写下去的那段文字会进模型的上下文。
|
|
2055
|
+
# 少了那半句,用户会把「建一个身份」读成「加一条本地配置」。
|
|
2056
|
+
#
|
|
2057
|
+
# ⚠️ `role_add_desc_hint` 必须点明这一格是**给主 agent 看的**。不说的话
|
|
2058
|
+
# 用户会往里写一段自我介绍,然后主 agent 一次都不派给它 —— 而屏幕上不会有
|
|
2059
|
+
# 任何东西解释为什么。
|
|
2060
|
+
#
|
|
2061
|
+
# ⚠️ `role_add_tools_hint` 必须点明**留空 = 不限**。一张空着的工具表在别处
|
|
2062
|
+
# (比如上面那张 env)意思是「没有」,在这儿意思正相反。
|
|
2063
|
+
role_add: '新建身份'
|
|
2064
|
+
role_add_title: '新建一个身份'
|
|
2065
|
+
role_add_name: '名字'
|
|
2066
|
+
role_add_name_hint: '只收小写字母、数字和连字符。它同时是文件名,也是派活时用的那个词。'
|
|
2067
|
+
role_add_desc: '什么时候派给它'
|
|
2068
|
+
role_add_desc_hint: '这一句是写给主智能体看的,不是自我介绍 —— 它照着这句话决定要不要把活派过来,所以要写「什么时候该用我」。它还会进每一次派活的工具描述里。'
|
|
2069
|
+
role_add_prompt: '正文'
|
|
2070
|
+
role_add_prompt_hint: '这个身份工作时读到的那段话。留空也行 —— 那样它只换一句身份行和工具边界。'
|
|
2071
|
+
role_add_tools: '工具白名单(一行一个)'
|
|
2072
|
+
role_add_tools_hint: '留空 = 不限,跟着当前这一轮能用的全部工具走。写了就只给这几样。名字要和设置 › 工具那一页上的对得上。'
|
|
2073
|
+
role_add_turns: '轮次上限'
|
|
2074
|
+
role_add_turns_hint: '留空 = 跟全局。1 到 200。'
|
|
2075
|
+
role_add_scope: '这会在 ~/.epoch/agents/ 里建一个 .md —— 不只是这一段对话,也不只是这个进程:这台机器上以后每一次跑 epoch(含 CLI 和 TUI)都会看到它。⚠️ 上面那两段文字会进模型的上下文(「什么时候派给它」那一句每一轮都进),所以写进去的东西和你在对话里说的话一样有分量。'
|
|
2076
|
+
role_add_submit: '建出来'
|
|
2077
|
+
role_add_submitting: '写入中…'
|
|
2078
|
+
role_add_cancel: '取消'
|
|
2079
|
+
role_add_done: '已写进 {path} —— 想接着改正文就去那个文件。'
|
|
2080
|
+
role_add_locked: '这一档下没法从界面新建身份:服务是用 --host 绑到回环之外的,而我们没法确认你的浏览器就在跑服务的那台机器上。身份的正文会进往后每一轮的上下文(「什么时候派给它」那一句还会进每一次派活的工具描述),所以这条路只在默认的回环绑定下开。要加的话:到那台机器上往 ~/.epoch/agents/ 里放一个 .md,或者去掉 --host 重启一次。'
|
|
2081
|
+
|
|
2082
|
+
# 复制命令那一对(另一半「已复制」和代码块共用 thread.copied)
|
|
2083
|
+
copy_cmd: '复制'
|
|
2084
|
+
|
|
2085
|
+
# 信任徽标。**必须印着路径**:项目那一层是跟着当前会话的工作区走的(决定 18),
|
|
2086
|
+
# 换一个会话它就是另一个仓库的 .epoch/
|
|
2087
|
+
trusted_at: '已信任 {root}'
|
|
2088
|
+
untrusted_at: '未信任 {root}'
|
|
2089
|
+
no_workspace: '这个会话没有绑工作区'
|
|
2090
|
+
|
|
2091
|
+
# 「使用」填进输入框的那句话。**故意是半句** —— 后面那半截(要它做什么)
|
|
2092
|
+
# 只有用户写得出,替他补一个宾语等于替他决定这一轮干什么
|
|
2093
|
+
summon_prefill: '让 {role} 来做:'
|
|
2094
|
+
|
|
2095
|
+
# 卡上第二个动作(2026-08-16):这一条是**会话级**的,一按就是一段新会话。
|
|
2096
|
+
# 判据(为什么两条路都给、建在哪个工作区、为什么建完立刻跳)全文在
|
|
2097
|
+
# capability/experts.tsx 的文件头
|
|
2098
|
+
open_session: '开一段新会话'
|
|
2099
|
+
# 按钮上那四个字说不完这一下的后果,所以 title / aria-label 用整句:
|
|
2100
|
+
# 谁来跑、建在哪儿、当前这段对话会怎么样。**两档不是一句** ——
|
|
2101
|
+
# 「不使用工作区」是真的一档(决定 18),拼一个 null 进去就是假话
|
|
2102
|
+
open_session_here: '用 {role} 的身份在 {root} 开一段新会话;当前这段对话原样留着'
|
|
2103
|
+
open_session_nows: '用 {role} 的身份开一段新会话,同样不绑工作区;当前这段对话原样留着'
|
|
2104
|
+
# 失败。{detail} 是**服务端原话**(它带着用户点的那条路径),外面这半句
|
|
2105
|
+
# 只说清是哪一个动作没成 —— 一屏卡片中间冒出一句「目录不存在」,
|
|
2106
|
+
# 读的人第一反应会是这张卡坏了
|
|
2107
|
+
open_session_failed: '开新会话没成:{detail}'
|
|
2108
|
+
|
|
2109
|
+
# 卡上第三个动作(方案 57 §3.6,2026-08-17):**逐条**那一档 —— 不预填、
|
|
2110
|
+
# 不建会话,只把这张卡挂到输入框那枚 chip 上。三条路的分工在
|
|
2111
|
+
# `ExpertsProps.canSummon` 的 JSDoc 上。
|
|
2112
|
+
#
|
|
2113
|
+
# 动词用「接手」是**照着历史里那条留痕挑的**(`agent_role.handoff` 的
|
|
2114
|
+
# 「从这一条起由「x」接手。」):按下这个按钮的人,过一会儿会在对话流里
|
|
2115
|
+
# 读到同一个词,两处用词不同的话他认不出那一行就是自己按出来的。
|
|
2116
|
+
take_over: '让它接手'
|
|
2117
|
+
# 按钮上四个字说不完这一下的后果,所以 title / aria-label 用整句。
|
|
2118
|
+
# **「这段对话不动」这半句不能省**:这一屏上另外两个动作一个改输入框、
|
|
2119
|
+
# 一个换一整段会话,而这一条是三条里唯一什么都不发生的
|
|
2120
|
+
take_over_title: '从下一条消息起由「{role}」来答;当前这段对话一个字都不动'
|
|
2121
|
+
# 已经是它了。按钮不消失、改成一句状态并灰掉 —— 这一屏整屏顶掉了对话,
|
|
2122
|
+
# 那枚 chip 在屏幕上根本看不见,「哪一个正在接手」只有这里说得出来
|
|
2123
|
+
take_over_on: '正在接手'
|
|
2124
|
+
# ⚠️ 后半句**必须有**:取消的出口只有输入框那枚 chip 上的 ×(全仓唯一一处),
|
|
2125
|
+
# 而站在这一屏上看不见它。少了它,用户会在这张卡上找一个不存在的开关
|
|
2126
|
+
take_over_on_title: '「{role}」正在接手接下来的消息 —— 要取消,回到对话里点输入框上那枚 chip 的 ×'
|
|
2127
|
+
|
|
2128
|
+
# 工作区 diff 那一屏(方案 30 PR-3,2026-08-16)。
|
|
2129
|
+
#
|
|
2130
|
+
# ⚠️ 这一组里**五条 note 一条都不许合并**,服务端自己就把它们分成了五个码。
|
|
2131
|
+
# 最容易被合掉的是这三对,每一对的下一步都完全不同:
|
|
2132
|
+
#
|
|
2133
|
+
# - `no_workspace`(这段会话没有地盘)vs `no_changes`(地盘里没东西):
|
|
2134
|
+
# 前者要新建一段绑目录的会话,后者是「那就对了」。
|
|
2135
|
+
# - `no_workspace_history`(这个进程手上没有这段会话的记录)vs `no_changes`:
|
|
2136
|
+
# 前者是**我们答不上来**,后者是**确实没有**。
|
|
2137
|
+
# - `not_a_git_repo` **不是错误**,是换了一条来路(退到「这一轮碰过的文件」),
|
|
2138
|
+
# 所以它和别的码同时出现是正常的。
|
|
2139
|
+
diff:
|
|
2140
|
+
title: '工作区改动'
|
|
2141
|
+
back: '返回会话'
|
|
2142
|
+
reload: '重新读'
|
|
2143
|
+
# 说清「不按就不会变」:这一屏不轮询(判据在 workspace/diff.tsx 文件头)
|
|
2144
|
+
reload_hint: '再读一遍这个目录 —— 这一屏不会自己刷新'
|
|
2145
|
+
loading: '正在读…'
|
|
2146
|
+
# ⚠️ 这一句只能说「这一发没打出去」。别的失败(没绑工作区、不是 git 仓库、
|
|
2147
|
+
# 读盘挂了)服务端一律回 200 加一条 note,落不到这儿
|
|
2148
|
+
failed: '读不到这个会话的工作区改动 —— 这一发没能打到本地服务。'
|
|
2149
|
+
pick: '左边挑一个文件。'
|
|
2150
|
+
# git 那条来路的作用域**比「这段会话改了什么」大**,而这句话必须说出来:
|
|
2151
|
+
# 用户看见一个自己刚在编辑器里改的文件时,第一反应会是「模型动了它」
|
|
2152
|
+
scope_git: '这里列的是这个目录相对 git HEAD 的全部未提交改动 —— 包括你自己改的、别的程序改的,不只是这段会话。'
|
|
2153
|
+
status_added: '新增'
|
|
2154
|
+
status_modified: '修改'
|
|
2155
|
+
status_deleted: '删除'
|
|
2156
|
+
|
|
2157
|
+
note_no_changes: '这个目录没有未提交的改动。'
|
|
2158
|
+
# 退到检查点那条来路时**一定**同时出现,所以它得把「那接下来看到的是什么」说完
|
|
2159
|
+
note_not_a_git_repo: '这个目录不是 git 仓库,所以这里改成列「这段会话经文件工具碰过的路径」。'
|
|
2160
|
+
# 「这个进程不知道」——**不是**「没有改动」
|
|
2161
|
+
note_no_workspace_history: '这个进程手上没有这段会话的文件记录(多半是上一个进程留下的会话),所以列不出改了哪些文件。'
|
|
2162
|
+
note_no_workspace: '这段会话没有绑工作区,没有目录可看。换目录要新建会话。'
|
|
2163
|
+
note_truncated: '改动的文件太多,这里只列了前 {count} 个。'
|
|
2164
|
+
|
|
2165
|
+
# 三档「画不出来」。**每一档的短标签和那句为什么是分开的两条** ——
|
|
2166
|
+
# 标签要短到能塞进文件树行尾那一格,而「为什么」要长到解释得清。
|
|
2167
|
+
#
|
|
2168
|
+
# ⚠️ `omit_binary` 那一句为什么不在这儿:它和只读预览那一格说的是同一件事,
|
|
2169
|
+
# 唯一真源是上面的 `web.ui.art.opaque_binary`(judge 在 inspector/content.ts)。
|
|
2170
|
+
omit_binary: '二进制'
|
|
2171
|
+
omit_binary_sized: '二进制,{bytes}'
|
|
2172
|
+
omit_too_large: '太大'
|
|
2173
|
+
omit_too_large_sized: '太大,{bytes}'
|
|
2174
|
+
omit_too_large_why: '这个文件超过了单文件 512 KB 的上限,两侧内容都没有取过来 —— 所以这儿画不出 diff,不是「它没改」。'
|
|
2175
|
+
# ⚠️ 这一句要说清缺的是**旧的那一侧**,而且要说清为什么不拿现状顶上:
|
|
2176
|
+
# 顶上去会画出一份「什么都没改」的假 diff(服务端为此专门标了这一档)
|
|
2177
|
+
omit_no_snapshot: '没有改前的样子'
|
|
2178
|
+
omit_no_snapshot_why: '这个目录不是 git 仓库,而这段会话动它之前的样子没有留底 —— 只知道它被改过,比不出改了哪几行。'
|
|
2179
|
+
|
|
2180
|
+
# 设置页第六节「工具」(`settings/tools.tsx`,`GET /api/sessions/:id/tools`)。
|
|
2181
|
+
#
|
|
2182
|
+
# 这一屏只答一个问题:**这一轮发给模型的工具,此刻各自被谁挡着**。
|
|
2183
|
+
#
|
|
2184
|
+
# ⚠️ 这一组里每一句都要守住同一条边界:三轴(规则 → 档位 → 沙箱)**不看参数**。
|
|
2185
|
+
# 敏感路径、危险命令那一层拦在三轴之前,而它们只在一次真调用带着真参数进来时
|
|
2186
|
+
# 才判得出话来。所以 `axes` 那一句不是免责声明,它是这张表的**定义** ——
|
|
2187
|
+
# 少了它,用户会拿这张表去回答「这次调用会不会被拦」,而那不是它能答的问题。
|
|
2188
|
+
#
|
|
2189
|
+
# 另一条:`v_*` 那四个词过 catalog,而安全中心规则表里那四个印英文原值。
|
|
2190
|
+
# 两处刻意不同 —— 那一屏印的是用户要敲进配置文件的 token,这一屏印的是结论,
|
|
2191
|
+
# 而第四档「看参数」压根不是任何配置里的词(判据在 tools.tsx 的 VERDICT_LABEL)。
|
|
2192
|
+
tools:
|
|
2193
|
+
no_session: '还没有会话,所以这张表算不出来 —— 判定要按某一段会话此刻的权限档来算。开一段会话之后再回来。'
|
|
2194
|
+
loading: '正在读…'
|
|
2195
|
+
lede: '这一轮发给模型的工具全在这儿:各是哪儿来的、干什么用的、这段会话调了几次,以及此刻谁在挡它。'
|
|
2196
|
+
axes: '「谁在挡它」按三轴现算:先看权限规则表里不带括号的整条规则,再看权限档,代码执行还要多看一道沙箱。三轴都不看参数 —— 敏感路径和危险命令那一层拦在更前面,只有真调用带着真参数进来才判得出来。所以某一次具体调用会不会被拦,这张表答不了。'
|
|
2197
|
+
# 这一屏取一次数就不再变(tools.tsx 文件头那条 ⚠️),所以「按哪一档算的」
|
|
2198
|
+
# 必须印出来:不印的话,一张过期的表和一张当下的表长得一模一样
|
|
2199
|
+
level_line: '按「{level}」这一档算的。这一屏读一次就停了 —— 在输入框那一行换过档之后,得退出这一节再进来才会重算。'
|
|
2200
|
+
level_unknown: '取不到这段会话的权限档,所以这一次「谁在挡它」那一列算不出来。'
|
|
2201
|
+
# 两种成因两句话。合成一句的话,第一种(换一段会话就好了)会被读成
|
|
2202
|
+
# 第二种(这个进程坏了)
|
|
2203
|
+
ungraded_no_session: '取不到这段会话的权限档(多半是上一个进程留下的会话),所以下面整张表没有判定,只列工具本身。'
|
|
2204
|
+
ungraded_no_layer: '这个进程没有把权限判定发下来(权限那一层没起来),所以下面整张表没有判定,只列工具本身。'
|
|
2205
|
+
# 没有判定时不按类型分组:类型也来自那一路,服务端拿不到时回退成「命令」,
|
|
2206
|
+
# 照它分组会画出一个「所有工具都是命令类」的假象
|
|
2207
|
+
g_all: '全部工具'
|
|
2208
|
+
|
|
2209
|
+
# 五类操作。分组顺序从松到紧(读 → 写 → 命令 → 网络 → 代码执行),
|
|
2210
|
+
# 判据在 tools.tsx 的 TYPE_ORDER
|
|
2211
|
+
t_file_read: '读文件'
|
|
2212
|
+
t_file_write: '写文件'
|
|
2213
|
+
t_command: '执行命令'
|
|
2214
|
+
t_network: '访问网络'
|
|
2215
|
+
t_code_exec: '执行代码'
|
|
2216
|
+
# 引擎那边加了一类而这一版还没认领它 —— 丢掉那几行的表现是「什么都没发生」,
|
|
2217
|
+
# 所以它们落进这一组,判据同配置那三节的 orphans
|
|
2218
|
+
t_other: '其它'
|
|
2219
|
+
|
|
2220
|
+
col_tool: '工具'
|
|
2221
|
+
col_source: '来源'
|
|
2222
|
+
col_desc: '说明'
|
|
2223
|
+
col_gate: '谁在挡它'
|
|
2224
|
+
col_calls: '调用'
|
|
2225
|
+
|
|
2226
|
+
v_allow: '放行'
|
|
2227
|
+
v_ask: '问一句'
|
|
2228
|
+
v_deny: '拒绝'
|
|
2229
|
+
# 第四档:这一档下这个工具的判定取决于参数。它是一句真话,不是一个警告 ——
|
|
2230
|
+
# 所以界面上走中性色不走警告黄(判据在 settings-tools.css 文件头)
|
|
2231
|
+
v_depends: '看参数'
|
|
2232
|
+
# 第五档:`gate` 整个缺席。「被挡住了」和「我们答不出」必须是两句话
|
|
2233
|
+
v_none: '算不出'
|
|
2234
|
+
|
|
2235
|
+
# 「谁拍的板」那半句,三轴三种说法。合成一句就没法解释了(判据在
|
|
2236
|
+
# `WireToolGateAxis` 的 JSDoc)
|
|
2237
|
+
by_none: '没有判定'
|
|
2238
|
+
by_sandbox: '沙箱隔离'
|
|
2239
|
+
by_level: '{level} 档'
|
|
2240
|
+
# 规则原文缺席时的兜底。编一条原文出来的代价很具体:用户会拿它去自己的
|
|
2241
|
+
# 配置文件里搜,搜不到
|
|
2242
|
+
by_rule: '一条规则'
|
|
2243
|
+
cond: '另有 {count} 条带参数的规则可能改判'
|
|
2244
|
+
|
|
2245
|
+
# 这一节唯一的出口。指的是「去哪儿改」不是「点这里改」:安全中心那一屏
|
|
2246
|
+
# 本身也是只读的,真要加规则路在配置文件里,那句话由 `perm_where` 说,
|
|
2247
|
+
# 这儿不抄第二份
|
|
2248
|
+
how_to_block: '要挡住一个工具,就在权限规则表里加一条 deny。规则写进配置文件才算数,这个界面从头到尾不改配置 —— 安全中心那一屏能告诉你现在有哪些规则、各是哪一层加的。'
|
|
2249
|
+
goto_rules: '去看规则表'
|
|
2250
|
+
mcp_note: 'MCP 服务器带来的工具也在这张表里,名字长这样:mcp__<服务器名>__<工具名>。「来源」那一列写着它是从哪台服务器来的。'
|
|
2251
|
+
|
|
2252
|
+
# 设置页第七节「关于」(`settings/about.tsx`)。数据搭首屏那一发
|
|
2253
|
+
# `GET /api/config` 的车下来,所以这一节没有 sessionId,也就没有「没有会话」那一档。
|
|
2254
|
+
#
|
|
2255
|
+
# 这一节存在的理由是一句话:**出问题时把这一屏截过来,比描述现象快**。
|
|
2256
|
+
# 所以每一条的取舍都从它推 —— 一个字段值不值得画,看的是「贴进 issue 之后
|
|
2257
|
+
# 能不能少问一轮」。
|
|
2258
|
+
about:
|
|
2259
|
+
loading: '正在读…'
|
|
2260
|
+
lede: '出问题时把这一屏截下来贴进 issue,比描述现象快。'
|
|
2261
|
+
|
|
2262
|
+
g_machine: '这台机器'
|
|
2263
|
+
k_version: '版本'
|
|
2264
|
+
# 三样并成一行而不是拆成三行:这一行的用途就是被整条复制走
|
|
2265
|
+
version_line: 'epoch {version} · Node {node} · {platform}'
|
|
2266
|
+
k_home: '数据目录'
|
|
2267
|
+
home_hint: '会话、配置、日志都在这儿。设 EPOCH_HOME 可以把它整个挪到别处。'
|
|
2268
|
+
k_db: '会话数据库'
|
|
2269
|
+
# 「为什么我的会话不见了」最常见的答案就在这一行:换了 profile
|
|
2270
|
+
db_hint: '所有会话的记录都在这一个文件里。它跟着数据目录和 profile 走 —— 换一个 profile 就是换一份互不相干的数据。'
|
|
2271
|
+
k_profile: 'Profile'
|
|
2272
|
+
# 等于默认值时网线上整个不发这个键,这一行也就不画。出现在这儿本身
|
|
2273
|
+
# 就是信息:你这台机器上的路径和别人的不一样
|
|
2274
|
+
profile_hint: '用 EPOCH_PROFILE 或 --profile 选的。默认那一档不画这一行 —— 它出现在这儿,就说明上面两个路径和别人的不一样。'
|
|
2275
|
+
|
|
2276
|
+
g_diag: '启动诊断'
|
|
2277
|
+
diag_lede: '这个进程起来的时候自检了这些。侧栏底下只画有问题的那几条,这儿是全部四档。'
|
|
2278
|
+
# 一条都没有**不是**「都正常」:正常的模块会记一条 ok。两者必须说成两句话
|
|
2279
|
+
diag_none: '这个进程一条诊断都没发下来。这不等于「一切正常」—— 自检过的模块即便没事也会记一条 ok。'
|
|
2280
|
+
# ⚠️ `detail` 是服务端生成的文案,走服务端进程自己的语言:浏览器切成英文
|
|
2281
|
+
# 之后这一句仍然是中文,预期形态(验收 31),判据同侧栏那一处。
|
|
2282
|
+
# 中文用全角冒号不带空格,英文用半角冒号带空格 —— 这就是这条 key 存在的理由
|
|
2283
|
+
diag_line: '{module}:{detail}'
|
|
2284
|
+
|
|
2285
|
+
# 不查更新,但要说出升级的路在哪儿:装的方式不一样、升级的命令就不一样,
|
|
2286
|
+
# 而认得出「你是怎么装的」的那段代码在 CLI 那一层,web 服务端够不着。
|
|
2287
|
+
# 所以这里给一条能敲的命令,不是一个转圈之后说不出话的按钮(决定 20 ①)
|
|
2288
|
+
# ⚠️ 2026-08-18 精简。原文中间那句「而认得出这件事的代码只在命令行那一层」
|
|
2289
|
+
# 是在解释**为什么这一屏没有升级按钮** —— 决定 20 ① 的账(不画一个点不动的
|
|
2290
|
+
# 按钮),而那条账的读者是改这个界面的人,不是想升级的人。
|
|
2291
|
+
update_note: '在终端里敲这一条,它会查一次版本,并告诉你该怎么升级:'
|
|
2292
|
+
|
|
2293
|
+
# 「自动化」那一屏(方案 45 PR-3)。两个 tab:定时任务 / 运行记录。
|
|
2294
|
+
#
|
|
2295
|
+
# ⚠️ 这一节里**没有**结局八档、频率摘要、星期、权限档四个词 —— 那四组走
|
|
2296
|
+
# `schedule.*` 和 `web.ui.perm.lvl_*`,和 CLI / 输入框底栏共用同一批 key。
|
|
2297
|
+
# 判据:同一件事在两条路上不能有两种说法(全文在
|
|
2298
|
+
# `packages/web/src/automation/labels.ts` 的文件头那张表上)。
|
|
2299
|
+
auto:
|
|
2300
|
+
title: '自动化'
|
|
2301
|
+
back: '返回会话'
|
|
2302
|
+
tab_tasks: '定时任务'
|
|
2303
|
+
tab_runs: '运行记录'
|
|
2304
|
+
loading: '正在读…'
|
|
2305
|
+
dismiss: '知道了'
|
|
2306
|
+
|
|
2307
|
+
# 顶部那条提示条。位置照截图留着,**文案换成真话** —— WorkBuddy 那句
|
|
2308
|
+
# 「请勿关闭电脑或退出客户端」正是我们要拆掉的东西(§2.4 末尾)。
|
|
2309
|
+
# 三档由 `capability` 探出来,不是写死的文本
|
|
2310
|
+
notice_os: '不需要一直开着 epoch —— 定时任务由系统调度器唤起。但电脑关机、或者用户没登录时到点的那几次会跳过,不会补跑。'
|
|
2311
|
+
notice_unsupported: '本平台暂不支持把定时任务注册进系统调度器(现阶段只有 Windows 和 macOS)。任务建得出来,但到点了不会自己跑。'
|
|
2312
|
+
# ⚠️ 这一句是 §2.6 唯一真正要守住的东西:宿主没有无界面入口时**必须**把
|
|
2313
|
+
# 「关掉应用就不跑」说出来,不许写成「已启用」
|
|
2314
|
+
# ⚠️ **不许写 markdown**:这一条走的是 `<span>{t(key)}</span>`(纯文本),
|
|
2315
|
+
# 星号会原样印在屏幕上。2026-08-17 截图里就是这么发现的。
|
|
2316
|
+
# 要强调只能靠措辞和位置 —— 这一句已经在一条 `warn` 语气的 note 里了。
|
|
2317
|
+
notice_host: '这个应用没有可以被系统调度器唤起的无界面入口。定时任务只在应用开着的时候跑:关掉应用,它们就不跑了。'
|
|
2318
|
+
|
|
2319
|
+
# 空状态。**一句话 + 一个按钮,没有模板卡**(模板六张归 PR-4)
|
|
2320
|
+
empty_title: '开启你的第一个自动化任务'
|
|
2321
|
+
empty_hint: '到点了让 agent 替你做一件事:巡一遍仓库、汇总这周的 PR、扫一遍过期文档。跑完留下一份能读的东西。'
|
|
2322
|
+
new: '新建任务'
|
|
2323
|
+
|
|
2324
|
+
mark_host: '只在应用开着时跑'
|
|
2325
|
+
never_run: '还没跑过'
|
|
2326
|
+
next_none: '不会再触发'
|
|
2327
|
+
next_at: '下次 {when}'
|
|
2328
|
+
last_at: '上次 {status} · {when}'
|
|
2329
|
+
run_now: '先跑一次'
|
|
2330
|
+
enable: '启用'
|
|
2331
|
+
disable: '停用'
|
|
2332
|
+
edit: '编辑'
|
|
2333
|
+
remove: '删除'
|
|
2334
|
+
# ⚠️ 这句确认要说清「同时撤掉系统里那条注册」:那份状态活在 epoch 之外,
|
|
2335
|
+
# 而用户在任务计划程序 / LaunchAgents 里看得到它
|
|
2336
|
+
remove_confirm: '删掉「{name}」?系统调度器里那条注册会一起撤掉。运行录像留着。'
|
|
2337
|
+
|
|
2338
|
+
# ---- 表单 ----
|
|
2339
|
+
form_new: '新建定时任务'
|
|
2340
|
+
form_edit: '编辑定时任务'
|
|
2341
|
+
f_name: '名称'
|
|
2342
|
+
f_prompt: '提示词'
|
|
2343
|
+
f_prompt_hint: '到点了让 agent 做什么。写清楚做完要留下什么 —— 没人在旁边追问。'
|
|
2344
|
+
f_workdir: '工作空间(可选)'
|
|
2345
|
+
f_workdir_hint: '服务端的绝对路径'
|
|
2346
|
+
# ⚠️ 「可选」在语义上必须落到一个**确定的目录**:不给不等于「服务进程的
|
|
2347
|
+
# 当前目录」——OS 调度器起进程时的 cwd 不由我们决定
|
|
2348
|
+
f_workdir_note: '不填就跑在 ~/.epoch/automation/<任务 id>/ 里。它**不会**继承任何一个终端的当前目录。'
|
|
2349
|
+
f_workdir_default: '~/.epoch/automation/<任务 id>/'
|
|
2350
|
+
f_model: '模型(可选)'
|
|
2351
|
+
f_model_hint: '不填就用配置里那个'
|
|
2352
|
+
f_kind: '执行频率'
|
|
2353
|
+
k_daily: '每天'
|
|
2354
|
+
k_weekly: '每周'
|
|
2355
|
+
k_monthly: '每月'
|
|
2356
|
+
k_interval: '按间隔'
|
|
2357
|
+
k_once: '只跑一次'
|
|
2358
|
+
f_at: '时刻'
|
|
2359
|
+
f_date: '日期'
|
|
2360
|
+
f_every: '间隔'
|
|
2361
|
+
f_weekdays: '星期几'
|
|
2362
|
+
f_days: '几号'
|
|
2363
|
+
f_days_hint: '1, 15'
|
|
2364
|
+
f_days_note: '31 号在小月不触发 —— 两个平台的原生行为都是这样。'
|
|
2365
|
+
f_start: '生效开始(可选)'
|
|
2366
|
+
f_end: '生效结束(可选)'
|
|
2367
|
+
f_permission: '权限档'
|
|
2368
|
+
# ⚠️ §3.4 第 3 条那次二次确认。**把工作区路径一起说出来** ——
|
|
2369
|
+
# 一句没有宾语的「我明白」什么都没确认到
|
|
2370
|
+
f_bypass_ack: '我明白:这个任务将在 {workDir} 里不经确认地做任何事,包括删文件和跑任意命令。'
|
|
2371
|
+
f_turns: '最多轮次'
|
|
2372
|
+
f_timeout: '超时(分钟)'
|
|
2373
|
+
# 前两把尺子拦不住「一条命令挂住了」—— 那种情况轮次和花费都不增长
|
|
2374
|
+
f_timeout_note: '唯一能拦住「一条命令挂住了」的那把尺子。'
|
|
2375
|
+
f_budget: '这一次最多花多少(美元)'
|
|
2376
|
+
# ⚠️ 这一句是全方案唯一一处**刻意增加的摩擦**的解释(§3.5 / §十二 第 2 条)。
|
|
2377
|
+
# 一个必填项没有理由撑着,用户只会随手填一个数 —— 而这条摩擦要的正好是
|
|
2378
|
+
# 「让他在创建时就想一次这个数」。**别把它改软**
|
|
2379
|
+
f_budget_why: '必填,而且我们不给默认值:这个任务没人看着,一个假默认会让「一觉醒来 40 美元」变成我们的错。'
|
|
2380
|
+
save_and_run: '保存并先跑一次'
|
|
2381
|
+
save_only: '只保存'
|
|
2382
|
+
cancel: '取消'
|
|
2383
|
+
# 按不动时说一句为什么(决定 20 ①)
|
|
2384
|
+
blocked_budget: '填一个大于 0 的预算才能保存'
|
|
2385
|
+
blocked_bypass: '勾上那句确认才能保存'
|
|
2386
|
+
|
|
2387
|
+
# ---- 无人值守清单 ----
|
|
2388
|
+
allow_title: '这个任务将不经确认地做这些事'
|
|
2389
|
+
allow_lede: '权限档回答「要不要人确认」,这份清单回答「要确认、但没人在,哪几条可以不问」。逐条列出来,是为了让你在保存前看得见。'
|
|
2390
|
+
# ⚠️ 这两句**理由正好相反**,而「只读」那一句还多半句:它判出来的拒绝
|
|
2391
|
+
# 不进欠条 —— 那是判定结果不是缺授权,加规则永远不会被查到
|
|
2392
|
+
allow_off_plan: '「只读」档天然零授权面,不需要清单。⚠️ 这一档判出来的拒绝**不是缺授权**:它不会记成欠条,往清单里加规则也不会有帮助 —— 要放开就换一档。'
|
|
2393
|
+
allow_off_bypass: '「跳过全部检查」下这份清单没有意义:任务会不经确认地做任何事。留着一份看起来在生效的清单,会让人以为自己还有一道边界。'
|
|
2394
|
+
allow_ops: '整类操作'
|
|
2395
|
+
allow_tools: '整个工具(一行一个)'
|
|
2396
|
+
allow_tools_hint: 'terminal'
|
|
2397
|
+
allow_rules: '规则(一行一条)'
|
|
2398
|
+
allow_rules_hint: "terminal(git status)\nfile_write(./reports/**)\nmcp__github__*"
|
|
2399
|
+
allow_rules_note: '连接器的免确认写成 mcp__<server>__*;「只许写这个目录」写成 file_write(<目录>/**)。'
|
|
2400
|
+
# §3.2 第一条实现约束:任务级清单**取代**全局非交互白名单,不是叠加
|
|
2401
|
+
allow_scope: '这份清单只对这一条任务生效,不受全局非交互白名单影响。'
|
|
2402
|
+
|
|
2403
|
+
# ---- 注册结果 ----
|
|
2404
|
+
reg_warn_source: '注册进去的是这个 checkout 的路径 —— 仓库移动或删除之后,这条任务会失败。'
|
|
2405
|
+
reg_unsupported: '本平台暂不支持注册定时任务(现阶段只有 Windows 和 macOS),所以它到点了不会自己跑。'
|
|
2406
|
+
reg_npx: '用 npx 跑的 epoch 不能注册定时任务:它的入口在临时缓存目录里,清一次缓存这条任务就废了。'
|
|
2407
|
+
reg_no_runner: '这个应用没有可以被系统调度器唤起的无界面入口,所以这条任务只在应用开着的时候跑。'
|
|
2408
|
+
reg_backend_error: '系统调度器那边报错了:{detail}'
|
|
2409
|
+
|
|
2410
|
+
# ---- 运行记录 ----
|
|
2411
|
+
# 两种空状态各说各的:一个都没建过 vs 建了但没跑过
|
|
2412
|
+
runs_empty_none: '还没有任何运行记录 —— 一条定时任务都还没建。去「定时任务」那个 tab 建一条。'
|
|
2413
|
+
runs_empty_never: '还没有任何运行记录。到点了它会自己跑;等不及的话,去「定时任务」那边点「先跑一次」。'
|
|
2414
|
+
run_manual: '手动'
|
|
2415
|
+
run_meta: '{turns} 轮 · {cost} 美元'
|
|
2416
|
+
# 这条运行属于一条已经被删掉的任务。**说出来**而不是留一个空名字
|
|
2417
|
+
run_orphan: '(任务已删除)'
|
|
2418
|
+
just_ran: '刚跑完:{status}'
|
|
2419
|
+
replay_show: '看录像'
|
|
2420
|
+
replay_hide: '收起录像'
|
|
2421
|
+
replay_loading: '正在读录像…'
|
|
2422
|
+
replay_none: '这一次没有录像(没起会话,或者已经被留存上限清掉了)。'
|
|
2423
|
+
replay_truncated: '这份录像太长,只画了前 {count} 帧。'
|
|
2424
|
+
|
|
2425
|
+
# ---- 欠条 ----
|
|
2426
|
+
# ⚠️ 这一段和上面 `allow_off_plan` 那句**刻意是两句不同的话**:
|
|
2427
|
+
# 欠条的下一步是加一条规则(下面那个按钮),而档位判出来的拒绝加规则
|
|
2428
|
+
# 永远不会被查到 —— 那时唯一有意义的动作是换一档
|
|
2429
|
+
pending_header: '有 {count} 条操作本该问你,但当时没人可问(欠条)'
|
|
2430
|
+
pending_rule: '→ {rule}'
|
|
2431
|
+
pending_no_rule: '(这一条反推不出精确规则)'
|
|
2432
|
+
# 按钮上的字**不是「批准」**:那一轮早结束了,唯一有意义的动作是把这次的
|
|
2433
|
+
# 阻塞变成下一次的授权(§3.3)
|
|
2434
|
+
pending_fix: '加进授权清单'
|
|
2435
|
+
pending_fixed: '✓ 已加 {count} 条:{rules}'
|
|
2436
|
+
pending_fixed_none: '一条都没加 —— 这几条反推不出精确规则,硬编一条只会往清单里塞个死规则。'
|
|
2437
|
+
|
|
2438
|
+
# ---- 校验结论(服务端回的是码,这里是这一屏自己的措辞)----
|
|
2439
|
+
#
|
|
2440
|
+
# ⚠️ **和 `schedule.issue.*` 刻意不共用**:那一组里有 `--budget` /
|
|
2441
|
+
# `--max-turns` 这种命令行参数名,而表单上那句话里没有那个东西。
|
|
2442
|
+
# 判据(core 的 `schedule/types.ts` 早就写下的):同一条校验将来要同时被
|
|
2443
|
+
# CLI 和 Web 表单用,而两边的措辞本来就该不一样
|
|
2444
|
+
issue:
|
|
2445
|
+
name_empty: '给它起个名字'
|
|
2446
|
+
prompt_empty: '写清楚到点了让它做什么 —— 一个没有提示词的定时任务什么都不会做'
|
|
2447
|
+
permission_unknown: '这个权限档不认识:{detail}'
|
|
2448
|
+
work_dir_missing: '这个目录不存在:{detail}'
|
|
2449
|
+
budget_missing: '填一个大于 0 的数(现在是 {detail})'
|
|
2450
|
+
max_turns_invalid: '要是 1 到 1000 之间的整数(现在是 {detail})'
|
|
2451
|
+
timeout_invalid: '要是正数,而且不超过 24 小时(现在是 {detail} 毫秒)'
|
|
2452
|
+
trigger_time_invalid: '时刻要写成 HH:mm(现在是 {detail})'
|
|
2453
|
+
trigger_weekdays_empty: '至少选一天'
|
|
2454
|
+
trigger_days_invalid: '几号要在 1 到 31 之间({detail})'
|
|
2455
|
+
trigger_interval_invalid: '这个间隔不行({detail} 分钟)—— 只能是 60 的约数或整小时数,因为 macOS 那侧只能列举时刻,除不尽的间隔在每小时边界上会漂'
|
|
2456
|
+
trigger_once_date_invalid: '日期要写成 YYYY-MM-DD,而且得是真实存在的一天(现在是 {detail})'
|
|
2457
|
+
trigger_once_past: '这个时刻已经过去了,存下来也永远不会触发({detail})'
|
|
2458
|
+
date_range_invalid: '生效区间不对:{detail}'
|
|
2459
|
+
date_range_on_once: '只跑一次的任务不能带生效区间 —— 它自己就是一个日期'
|
|
2460
|
+
rule_invalid: '{detail}'
|
|
2461
|
+
bypass_needs_workdir: '这一档必须绑定一个工作空间 —— 「跳过全部检查」加上一个不确定的目录,就是「无边界」的字面意思'
|
|
2462
|
+
bypass_workdir_too_broad: '这一档的工作空间不能是家目录或根目录(现在是 {detail})'
|
|
2463
|
+
bypass_needs_limits: '这一档下预算 / 轮次 / 超时三样都必须有值'
|
|
2464
|
+
bypass_not_confirmed: '这一档要先勾上那句确认'
|
|
2465
|
+
|
|
2466
|
+
# 工作区(决定 18:每个会话绑一个)。这两组都会被用户直接看到:
|
|
2467
|
+
# `recent_*` 进启动诊断,`bind_*` 直接进 `POST /api/sessions` 的 400 / 409 响应体。
|
|
2468
|
+
workspace:
|
|
2469
|
+
recent_unreadable: '最近使用的工作区 {path} 读不出来({reason}),本次按空清单处理'
|
|
2470
|
+
recent_not_object: '最近使用的工作区 {path} 顶层不是对象,按空清单处理'
|
|
2471
|
+
recent_bad_version: '最近使用的工作区 {path} 版本为 {version},本程序只认 {expected},按空清单处理'
|
|
2472
|
+
recent_not_array: '最近使用的工作区 {path} 的 workspaces 不是数组,按空清单处理'
|
|
2473
|
+
recent_dropped: '最近使用的工作区 {path} 有 {count} 条格式非法,已忽略'
|
|
2474
|
+
recent_write_failed: '最近使用的工作区 {path} 写入失败:{reason}'
|
|
2475
|
+
bind_not_absolute: '工作区必须是绝对路径:{path}'
|
|
2476
|
+
bind_missing: '目录不存在:{path}'
|
|
2477
|
+
bind_not_a_directory: '不是目录:{path}'
|
|
2478
|
+
bind_locked: '会话 {sessionId} 已绑定 {current},不能改成 {next}'
|
|
2479
|
+
# 落盘那一份没写成(会话库 v7,2026-08-19)。这一条**不挡绑定** ——
|
|
2480
|
+
# 工作区照样绑上了、这一轮照样能干活,丢的只是「下次启动还认得它在哪儿」。
|
|
2481
|
+
# 所以措辞要答出「什么没成、后果是什么」,不能只说一句写失败:
|
|
2482
|
+
# 用户下一次重启看到那段会话掉进「早先的会话」时,得能把两件事对上
|
|
2483
|
+
decision_write_failed: '会话 {sessionId} 的工作区决定没记进会话库({reason})—— 这一轮不受影响,但重启之后它会认不出自己属于哪个空间'
|
|
2484
|
+
|
|
2485
|
+
# 宿主预置(runtime/src/host-preset.ts,方案 54 §六)。整组都进启动诊断。
|
|
2486
|
+
#
|
|
2487
|
+
# 措辞的判据只有一条:**「配了但没生效」是这类东西唯一的失败模式**,而它在这一组
|
|
2488
|
+
# 存在之前完全静默 —— 宿主传了 `hostPreset`,然后看到一行 `Provider: 无可用凭证`,
|
|
2489
|
+
# 没有任何一处说得出「我明明预置了」。所以每条都要答「这一项到底怎么了」:
|
|
2490
|
+
# 写了(写进哪个文件、哪几个键)、没写(用户已经有了)、还是坏了(为什么)。
|
|
2491
|
+
#
|
|
2492
|
+
# `kept` 那几条是 **ok 不是 warn**:没覆盖用户的选择是这个功能的正确行为,
|
|
2493
|
+
# 不是一个问题。
|
|
2494
|
+
host_preset:
|
|
2495
|
+
wrote: '已写入 {path}:{keys}'
|
|
2496
|
+
kept: '{path} 里这几项用户已经配过,保持原样:{keys}'
|
|
2497
|
+
write_failed: '写 {path} 失败({reason}),这一项没有生效'
|
|
2498
|
+
home_unwritable: '数据目录 {path} 建不出来({reason}),预置整份没有生效'
|
|
2499
|
+
section_inline: '{path} 里的 {section} 是行内写法,往里插字段会写坏它 —— 这一段没有预置'
|
|
2500
|
+
nothing_to_do: '预置里的每一项都已经就位,一个字都没写'
|
|
2501
|
+
secret_stored: 'API key 已交给注入的凭据存储({name}),没有落盘'
|
|
2502
|
+
secret_kept: '凭据存储里已经有 {name},预置的那把没有覆盖它'
|
|
2503
|
+
secret_failed: 'API key 存进凭据存储失败({name}:{reason}),本次没有可用凭证'
|
|
2504
|
+
secret_not_needed: '{provider} 不需要 API key,预置里那把已忽略'
|
|
2505
|
+
mcp_wrote: '已写入 {path} 的 MCP server:{names}'
|
|
2506
|
+
mcp_kept: '{path} 里已经有同名的 MCP server,没有覆盖:{names}'
|
|
2507
|
+
mcp_unreadable: '{path} 读不出来({reason}),预置的 MCP server 一个都没写 —— 那份文件是你的,我们不会覆盖它'
|
|
2508
|
+
mcp_no_servers: '{path} 里没有 servers 段(是不是写成了 mcpServers?),预置的 MCP server 一个都没写'
|
|
2509
|
+
workspaces_wrote: '已知工作区清单新增:{roots}'
|
|
2510
|
+
workspaces_kept: '这几个工作区清单里已经有了,没有动它们的顺序:{roots}'
|
|
2511
|
+
workspace_not_absolute: '已知工作区必须是绝对路径,已跳过:{path}'
|
|
2512
|
+
|
|
2513
|
+
# `~/.epoch/keybindings.json` 的加载诊断(core/src/config/keybindings.ts,方案 31)。
|
|
2514
|
+
# 三条都是「你配的这一行没生效」,所以每条都必须说清**是哪一个键** —— 用户得拿着
|
|
2515
|
+
# 那个字符串回去改文件,印一句「配置有冲突」等于让他自己去猜。键名逐字用配置里的
|
|
2516
|
+
# 写法(`ctrl+l` 而不是 `Ctrl+L`),复制粘贴就能改。
|
|
2517
|
+
keybindings:
|
|
2518
|
+
bad_chord: '{key} 不是一个能识别的按键,这一条已忽略'
|
|
2519
|
+
reserved: '{key} 是保留键,不能重绑(它是卡住时的逃生通道),这一条已忽略'
|
|
2520
|
+
conflict: '{key} 已经绑给 {first},这一条已忽略'
|
|
2521
|
+
|
|
2522
|
+
marketplace:
|
|
2523
|
+
fetch_failed: '拉取 {url} 失败:HTTP {status}'
|
|
2524
|
+
fetch_failed_private: '拉取 {url} 失败:HTTP {status}。地址写错、或者它是私有的 —— 取市场目录不带任何凭据;私有的话先 clone 到本地,再 epoch plugin marketplace add ./那个目录'
|
|
2525
|
+
fetch_error: '拉取 {url} 失败:{reason}'
|
|
2526
|
+
|
|
2527
|
+
# 定时任务的运行期文案(方案 45)。命令帮助那一半在上面的 `cli.schedule`。
|
|
2528
|
+
#
|
|
2529
|
+
# 两条措辞上的硬规矩:
|
|
2530
|
+
# 1. 权限档那四个词**和对话里那枚胶囊逐字一致**(设计稿决定 20)——
|
|
2531
|
+
# 用户已经学过一次,再教一套新词等于让他学两遍同一件事的两种说法
|
|
2532
|
+
# 2. 「跳过」和「失败」不许混成一个记号。「上一次还在跑所以跳过了」显示成红叉,
|
|
2533
|
+
# 用户会去查一个不存在的故障
|
|
2534
|
+
schedule:
|
|
2535
|
+
list_header: '定时任务:'
|
|
2536
|
+
list_empty: '还没有定时任务。'
|
|
2537
|
+
list_empty_hint: '建一条:epoch schedule add --name "每日巡检" --prompt "…" --daily 09:00 --budget 0.50'
|
|
2538
|
+
col_next: '下次'
|
|
2539
|
+
col_last: '上次'
|
|
2540
|
+
disabled_mark: '已停用'
|
|
2541
|
+
yes: '是'
|
|
2542
|
+
no: '否'
|
|
2543
|
+
field_enabled: '启用'
|
|
2544
|
+
field_trigger: '频率'
|
|
2545
|
+
field_window: '生效区间'
|
|
2546
|
+
field_next: '下次运行'
|
|
2547
|
+
field_workdir: '工作目录'
|
|
2548
|
+
field_model: '模型'
|
|
2549
|
+
field_permission: '权限档'
|
|
2550
|
+
field_limits: '三把尺子'
|
|
2551
|
+
field_backend: '注册在'
|
|
2552
|
+
field_prompt: '提示词'
|
|
2553
|
+
field_last_run: '上次运行:{when}'
|
|
2554
|
+
limits_value: '最多 {turns} 轮 · 最多 {budget} 美元 · 超过 {minutes} 分钟就掐'
|
|
2555
|
+
allowlist_header: '这个任务将不经确认地做这些事:'
|
|
2556
|
+
allowlist_empty: '(清单是空的 —— 撞到需要确认的操作会记一条欠条然后收尾)'
|
|
2557
|
+
allowlist_scope_note: '这份清单只对这一条任务生效,不受全局非交互白名单影响。'
|
|
2558
|
+
allowlist_bypass: '权限档是「跳过全部检查」:清单在这一档下没有意义,任务会不经确认地做任何事。'
|
|
2559
|
+
allowlist_readonly: '权限档是「只读」:天然零授权面,不需要清单。'
|
|
2560
|
+
created: '✓ 已建好并注册:{name}({id})'
|
|
2561
|
+
created_next: '先跑一次看看,缺什么授权当场就知道:epoch schedule run {id}'
|
|
2562
|
+
ask_name: '任务名'
|
|
2563
|
+
ask_prompt: '到点了让 agent 做什么'
|
|
2564
|
+
ask_at: '每天几点跑(HH:mm)'
|
|
2565
|
+
ask_budget: '这一次运行最多花多少美元'
|
|
2566
|
+
ask_permission: '权限档'
|
|
2567
|
+
permission:
|
|
2568
|
+
plan: '只读'
|
|
2569
|
+
default: '按需确认'
|
|
2570
|
+
accept_edits: '自动接受改动'
|
|
2571
|
+
bypass: '跳过全部检查'
|
|
2572
|
+
auto: '自动放行'
|
|
2573
|
+
trigger:
|
|
2574
|
+
daily: '每天 {at}'
|
|
2575
|
+
weekly: '每周 {days} {at}'
|
|
2576
|
+
monthly: '每月 {days} 号 {at}'
|
|
2577
|
+
every_minutes: '每 {n} 分钟'
|
|
2578
|
+
every_hours: '每 {n} 小时'
|
|
2579
|
+
once: '{date} {at}(只跑一次)'
|
|
2580
|
+
weekday:
|
|
2581
|
+
sun: '周日'
|
|
2582
|
+
mon: '周一'
|
|
2583
|
+
tue: '周二'
|
|
2584
|
+
wed: '周三'
|
|
2585
|
+
thu: '周四'
|
|
2586
|
+
fri: '周五'
|
|
2587
|
+
sat: '周六'
|
|
2588
|
+
status:
|
|
2589
|
+
ok: '成功'
|
|
2590
|
+
failed: '失败'
|
|
2591
|
+
denied: '缺授权'
|
|
2592
|
+
timeout: '超时'
|
|
2593
|
+
budget: '触顶'
|
|
2594
|
+
skipped_overlap: '跳过(上一次还在跑)'
|
|
2595
|
+
skipped_window: '跳过(不在生效区间)'
|
|
2596
|
+
missed: '错过'
|
|
2597
|
+
drift:
|
|
2598
|
+
missing_in_os: '库里有、系统里没有(注册丢了)'
|
|
2599
|
+
orphan_in_os: '系统里有、库里没有(卸载残留)'
|
|
2600
|
+
spec_mismatch: '两边都有但对不上'
|
|
2601
|
+
never_registered: '库里那行说自己注册过,但没有系统侧标识'
|
|
2602
|
+
issue:
|
|
2603
|
+
name_empty: '名字不能是空的'
|
|
2604
|
+
prompt_empty: '提示词不能是空的 —— 一个没有提示词的定时任务什么都不会做'
|
|
2605
|
+
permission_unknown: '权限档不认识:{detail}'
|
|
2606
|
+
work_dir_missing: '工作目录不存在:{detail}'
|
|
2607
|
+
budget_missing: '--budget 必填,而且要是正数(现在是 {detail})'
|
|
2608
|
+
max_turns_invalid: '--max-turns 要是 1 到 1000 之间的整数(现在是 {detail})'
|
|
2609
|
+
timeout_invalid: '--timeout 要是正数且不超过 24 小时(现在是 {detail} 毫秒)'
|
|
2610
|
+
trigger_time_invalid: '时刻要写成 HH:mm(现在是 {detail})'
|
|
2611
|
+
trigger_weekdays_empty: '每周档至少要选一天'
|
|
2612
|
+
trigger_days_invalid: '每月档的日子要在 1 到 31 之间({detail})'
|
|
2613
|
+
trigger_interval_invalid: '间隔只能是 60 的约数或整小时数(现在是 {detail} 分钟)—— 判据是 launchd 那侧只能列举时刻,60 除不尽的间隔在每小时边界上会漂'
|
|
2614
|
+
trigger_once_date_invalid: '日期要写成 YYYY-MM-DD,而且得是真实存在的一天(现在是 {detail})'
|
|
2615
|
+
trigger_once_past: '这个时刻已经过去了,存下来也永远不会触发({detail})'
|
|
2616
|
+
date_range_invalid: '生效区间不对:{detail}'
|
|
2617
|
+
date_range_on_once: '只跑一次的任务不能带生效区间 —— 它自己就是一个日期'
|
|
2618
|
+
rule_invalid: '{detail}'
|
|
2619
|
+
bypass_needs_workdir: '「跳过全部检查」必须绑定一个工作区 —— 它加上一个不确定的目录就是「无边界」的字面意思'
|
|
2620
|
+
bypass_workdir_too_broad: '「跳过全部检查」的工作区不能是家目录或根目录(现在是 {detail})'
|
|
2621
|
+
bypass_needs_limits: '「跳过全部检查」下预算 / 轮次 / 超时三样都必须有值'
|
|
2622
|
+
bypass_not_confirmed: '「跳过全部检查」要显式确认:加 --i-understand-bypass'
|
|
2623
|
+
run_start: '开始跑:{name}'
|
|
2624
|
+
run_meta: '{turns} 轮 · {cost} 美元{manual}'
|
|
2625
|
+
run_manual: ' · 手动'
|
|
2626
|
+
fire_missing: '库里没有 {id} 这条任务,什么都没做'
|
|
2627
|
+
fire_disabled: '{id} 已停用,什么都没做'
|
|
2628
|
+
pending_header: '有 {n} 条操作因为没人可问而被拒了(欠条):'
|
|
2629
|
+
pending_line: '{tool} {target}'
|
|
2630
|
+
pending_fix: '加这条规则:{rule}'
|
|
2631
|
+
fix_hint: '把这些加进授权清单,下次到点就不会再卡:epoch schedule run {id} --fix'
|
|
2632
|
+
fix_done: '✓ 已追加 {n} 条规则进授权清单'
|
|
2633
|
+
fix_none: '没有可以自动追加的规则(这几条反推不出精确规则)'
|
|
2634
|
+
logs_empty: '还没有运行记录。'
|
|
2635
|
+
logs_recording: '── 最近一次的录像({path})'
|
|
2636
|
+
enabled_done: '✓ 已启用:{name}'
|
|
2637
|
+
disabled_done: '✓ 已停用(任务还在系统里,只是停着):{name}'
|
|
2638
|
+
removed: '✓ 已删除并撤掉系统注册:{name}'
|
|
2639
|
+
removed_logs_kept: '运行录像留着,在 {dir}/,要清自己删'
|
|
2640
|
+
doctor_clean: '✓ 查了 {n} 条,库里和系统里对得上'
|
|
2641
|
+
doctor_found: '发现 {n} 处对不上:'
|
|
2642
|
+
doctor_hint: '--repair 按库里那份重新注册(DB 是真源);--prune 撤掉系统里的孤儿'
|
|
2643
|
+
doctor_unsupported: '本平台暂不支持定时任务(现阶段只有 Windows 和 macOS)'
|
|
2644
|
+
doctor_repaired: '✓ 已重新注册:{name}'
|
|
2645
|
+
doctor_repair_failed: '✗ 重新注册失败:{name}({detail})'
|
|
2646
|
+
doctor_pruned: '✓ 已撤掉:{id}'
|
|
2647
|
+
doctor_prune_failed: '✗ 撤不掉:{id}({detail})'
|
|
2648
|
+
err_not_found: '找不到任务 {id}'
|
|
2649
|
+
err_not_found_hint: '用 epoch schedule list 看现有的'
|
|
2650
|
+
err_ambiguous: '{id} 这个前缀命中了多条,写全一点:'
|
|
2651
|
+
err_invalid: '这条任务有问题,一个字都没有保存:'
|
|
2652
|
+
err_no_trigger: '要给一个频率:--daily / --weekly / --monthly / --every / --once'
|
|
2653
|
+
err_many_triggers: '频率只能给一个'
|
|
2654
|
+
err_at_required: '--weekly / --monthly / --once 要配 --at HH:mm'
|
|
2655
|
+
err_weekday: '认不出星期:{value}'
|
|
2656
|
+
err_every: '--every 不认识:{value}'
|
|
2657
|
+
err_every_hint: '只能是这几个(它们同时是两个平台的公共能力):{values}'
|
|
2658
|
+
err_permission: '--permission 不认识:{value}'
|
|
2659
|
+
err_operation: '--allow-operation 不认识:{value}'
|
|
2660
|
+
err_budget: '--budget 要是一个正数:{value}'
|
|
2661
|
+
err_budget_hint: '不给默认值是刻意的:一个假默认会让「一觉醒来 40 美元」变成我们的错'
|
|
2662
|
+
err_missing_flags: '缺必填项,而且这里没有终端可以问'
|
|
2663
|
+
err_missing_flags_hint: '至少要给 --name --prompt --budget 和一个频率(--daily 之类)'
|
|
2664
|
+
err_npx: '用 npx 跑的 epoch 不能注册定时任务 —— 它的入口在临时缓存目录里,清一次缓存这条任务就废了'
|
|
2665
|
+
err_npx_hint: '先 npm i -g @epoch-agent/cli'
|
|
2666
|
+
err_no_host_runner: '这个宿主没有可以被系统调度器唤起的无界面入口,所以定时任务只能在应用开着的时候跑'
|
|
2667
|
+
err_no_host_runner_hint: '要让它在应用关掉之后也跑,宿主要提供 scheduleRunner(见 docs/EMBEDDING.md)'
|
|
2668
|
+
err_platform_unsupported: '本平台暂不支持注册定时任务(现阶段只有 Windows 和 macOS)'
|
|
2669
|
+
err_backend: '系统调度器那边报错了:{detail}'
|
|
2670
|
+
warn_source_entry: '⚠ 注册进去的是这个 checkout 的路径,仓库移动或删除之后这条任务会失败'
|
|
2671
|
+
|
|
2672
|
+
# 技能扫描(core/src/skill/scan.ts)。这一组只服务一句话:**跨来源同名覆盖**。
|
|
2673
|
+
#
|
|
2674
|
+
# 四个来源标签单独成条而不是拼死在那句话里,是因为它在那句话里出现**两次**
|
|
2675
|
+
# (谁覆盖了谁),而中英两边的语序不一样 —— 拼死的话英文只能写成中文的语序。
|
|
2676
|
+
#
|
|
2677
|
+
# 这一组 2026-08-17 从中文字面量抽上来(方案 44):那张表是
|
|
2678
|
+
# `Record<SkillDirSource, string>`,加 `host` 那一格时它当场编译不过,
|
|
2679
|
+
# 而在三条中文字面量里塞第四条 `t()` 比整张抽掉更难读。
|
|
2680
|
+
skill:
|
|
2681
|
+
scope_user: '用户级'
|
|
2682
|
+
scope_project: '项目级'
|
|
2683
|
+
scope_plugin: '插件'
|
|
2684
|
+
scope_host: '宿主注入'
|
|
2685
|
+
scope_override: '{scope}技能 "{name}" 覆盖了{previous}同名技能'
|
|
2686
|
+
# ---- 从本机目录导入(方案 42 §六,2026-08-18)----
|
|
2687
|
+
# 闸门判据在 core/src/skill/import.ts 的文件头:这条路**不收上传的字节**,
|
|
2688
|
+
# 只收一条服务端本机路径。下面这几条全都是「哪一份没进来、为什么」——
|
|
2689
|
+
# 静默跳过是这条路上最贵的错,用户会以为自己导进去了。
|
|
2690
|
+
import_no_system: '技能系统这次没起来 —— 导入没有落点。看一眼启动诊断里 Skill 那几条'
|
|
2691
|
+
import_not_absolute: '要一条绝对路径 —— 相对路径是相对服务进程的 cwd 算的,而那个值你在浏览器里看不见'
|
|
2692
|
+
import_missing: '找不到 {path}'
|
|
2693
|
+
import_empty: '{path} 里没有一份能导入的技能(要么是带 frontmatter 的 .md,要么是含 SKILL.md 的文件夹,要么是打包成 .zip 的它们)'
|
|
2694
|
+
import_stale: '这个目录在你看过预览之后变了 —— 已经取消。重新看一遍再确认'
|
|
2695
|
+
import_conflict: '用户级已经有一个叫「{name}」的技能了,这一份没有导入(不覆盖)。要换掉它,先把 ~/.epoch/skills 下那一份删掉'
|
|
2696
|
+
import_dup_in_batch: '这一批里有不止一个技能叫「{name}」,只留第一份 —— 技能名是全局唯一的,分类不同也算撞'
|
|
2697
|
+
import_skipped_entry: '{entry} 不在技能目录允许的内容里(只有 SKILL.md 和 references / templates / scripts / assets 四个子目录),已跳过它自己'
|
|
2698
|
+
import_skipped_special: '{entry} 不是普通文件(软链或别的),已跳过 —— 技能目录里的文件是会被读出来的'
|
|
2699
|
+
import_too_many_files: '这一份带的文件太多,后面的没有抄进来 —— 确认一下你指的是一个技能目录而不是一整个仓库'
|
|
2700
|
+
import_read_failed: '读取失败:{reason}'
|
|
2701
|
+
import_write_failed: '写入失败:{reason}'
|
|
2702
|
+
import_bad_name_empty: '技能名是空的'
|
|
2703
|
+
import_bad_name_long: '技能名太长(超过 {max} 个字符),它要当目录名用'
|
|
2704
|
+
import_bad_name_dots: '技能名不能是 . 或 ..'
|
|
2705
|
+
import_bad_name_char: '技能名「{name}」里有不能当目录名的字符(< > : " / \ | ? * 和控制字符)。其中冒号是插件 / 宿主技能的前缀分隔符,用户级技能占用它会冒充成插件技能'
|
|
2706
|
+
import_bad_name_trailing: '技能名「{name}」以点或空格结尾 —— Windows 上这样的目录建得出来却打不开'
|
|
2707
|
+
|
|
2708
|
+
# 嵌入宿主注入的能力清单(runtime/src/build.ts,方案 44 §2.1)。整组都进启动诊断。
|
|
2709
|
+
#
|
|
2710
|
+
# 措辞的判据同 host_preset 那一组:**「递进来了但没生效」是这类东西唯一的失败
|
|
2711
|
+
# 模式**,而宿主手上没有别的地方能看出来 —— 它把一批身份交出去,然后在能力页上
|
|
2712
|
+
# 找不到,屏幕上没有任何一处说得出为什么。所以坏的那条要指名道姓(是哪一条、
|
|
2713
|
+
# 坏在哪儿),好的那批要报出数目和前缀(宿主据此核对自己的 namespace 有没有写对)。
|
|
2714
|
+
host_capabilities:
|
|
2715
|
+
loaded: '{namespace}:身份 {roles} 个、技能目录 {skillDirs} 个、MCP server {mcpServers} 台(前两样的名字加 {namespace}: 前缀,server 加 {namespace}__ —— 它要进工具名,而工具名里不能有冒号)'
|
|
2716
|
+
bad_role_name: '身份 "{name}" 的名字不合法(只允许小写字母、数字、连字符,且不能以连字符开头),这一条已跳过;其余照常注入'
|
|
2717
|
+
duplicate_role: '身份 "{name}" 在这一批里出现了不止一次,只留第一条'
|
|
2718
|
+
empty_namespace: 'namespace 是空的,宿主注入整份没有生效 —— 没有它就没法拼前缀,而不带前缀的注入能顶掉内置角色'
|
|
2719
|
+
bad_namespace: 'namespace "{namespace}" 不合法(只允许小写字母、数字、连字符,且不能以连字符开头),宿主注入整份没有生效'
|
|
2720
|
+
skill_dir_relative: '技能目录 {path} 不是绝对路径,这一条已跳过 —— 相对路径是相对引擎进程的 cwd 算的,而那个值宿主控制不了'
|
|
2721
|
+
# 下面三条是方案 44 PR-2 加的(连接器那一样)。server 名比 mcp.json 收得严,
|
|
2722
|
+
# 理由是它会原样变成工具名的一部分发给 provider —— 说清这一点,否则宿主只会
|
|
2723
|
+
# 看到「我这台 server 没起来」而想不到是名字的问题
|
|
2724
|
+
bad_mcp_name: 'MCP server "{name}" 的名字不合法(只允许小写字母、数字、连字符,且不能以连字符开头),这一条已跳过;其余照常注入。这批名字比 mcp.json 收得严,因为它会原样变成工具名的一部分'
|
|
2725
|
+
duplicate_mcp: 'MCP server "{name}" 在这一批里出现了不止一次,只留第一条'
|
|
2726
|
+
mcp_name_taken: 'MCP server "{name}" 和用户 mcp.json 里已有的一台重名,宿主注入的这一台已跳过 —— 用户自己写的那一行说了算'
|
|
2727
|
+
|
|
2728
|
+
# Agent 角色表的启动诊断(runtime/src/build.ts 的 wireAgentRoles,方案 18)。
|
|
2729
|
+
#
|
|
2730
|
+
# 逐段拼而不是一句模板:段数随现场变(没装插件就没有那一段、没有宿主注入就没有
|
|
2731
|
+
# 宿主那一段),而这句话的全部作用是让**加法对得上** —— 总数和各段之和不一致时
|
|
2732
|
+
# 用户会以为自己的角色文件没加载。形状照 core/src/extensions/registry.ts 那句
|
|
2733
|
+
# 命令总览,两者是同一件事的两半。
|
|
2734
|
+
agent_roles:
|
|
2735
|
+
tally: '{total} 个可用({parts},同名已合并)'
|
|
2736
|
+
tally_builtin: '内置 {count}'
|
|
2737
|
+
tally_user: '用户 {count}'
|
|
2738
|
+
tally_project: '项目 {count}'
|
|
2739
|
+
tally_plugin: '插件 {count}'
|
|
2740
|
+
tally_host: '宿主 {count}'
|
|
2741
|
+
|
|
2742
|
+
# 逐条换身份那一刻,历史里留的那条系统消息(方案 57 §2.3 / §3.5)。
|
|
2743
|
+
#
|
|
2744
|
+
# ⚠️ **这两条只是正文,那个「系统在说话」的前缀不在这里。** 它住在
|
|
2745
|
+
# `packages/protocol/src/system-note.ts`(`SYSTEM_NOTE_PREFIX`),由
|
|
2746
|
+
# `runtime/src/agent-session.ts` 在读到这两条之后拼上去。2026-08-17 之前前缀是
|
|
2747
|
+
# **烤在这两句里**的,于是同一个前缀在全仓有四份副本(这两条 + `loop.ts` 的
|
|
2748
|
+
# verify 注入 + `task-notice.ts` 的后台任务通知),而其中两份会跟着译者走。
|
|
2749
|
+
#
|
|
2750
|
+
# 所以这里要守的**不再是「两份 catalog 里前缀逐字相同」,而是「一个字的前缀都
|
|
2751
|
+
# 别往这两句里加」** —— 加了就是双前缀(`[系统] [系统] …`),而那件事在屏幕上、
|
|
2752
|
+
# 在用例里、在 typecheck 里都看不出来。为什么前缀必须逐字节相同,全文在
|
|
2753
|
+
# protocol 那个文件的头注释里。
|
|
2754
|
+
#
|
|
2755
|
+
# 两句而不是一句:「换成了 x」和「不再指定身份」在回放时要说的是两件事,
|
|
2756
|
+
# 合成一句只能写出「由「」接手」这种半截话
|
|
2757
|
+
#
|
|
2758
|
+
# ⚠️ `handoff_cleared` 一句话里出现两次「身份」,那是**对的**:这一条改名之前
|
|
2759
|
+
# 前半句用的是旧叫法、后半句已经是「身份」—— 同一样东西在同一句话里两个称呼,
|
|
2760
|
+
# 读起来像两回事。改名把这道裂缝合上了,别再把其中一个换成同义词
|
|
2761
|
+
agent_role:
|
|
2762
|
+
handoff: '从这一条起由「{role}」接手。'
|
|
2763
|
+
handoff_cleared: '从这一条起不再指定身份,回到这段会话默认的身份。'
|
|
2764
|
+
|
|
2765
|
+
# 插件那一摊的用户可见文案(方案 44 PR-3 / PR-4)。
|
|
2766
|
+
#
|
|
2767
|
+
# 两组东西,都在这儿是因为它们**必须互相对得上**:`mcp_*` 是安装前预览扫
|
|
2768
|
+
# `mcp.json` 时报的问题(core/src/plugin/inventory.ts),`n_*` 是装完之后
|
|
2769
|
+
# `epoch plugin list` 印的那几个数(cli/src/commands/plugin.ts)。
|
|
2770
|
+
#
|
|
2771
|
+
# `n_*` 刻意短到没有单位后缀(「命令 3」而不是「命令 3 条」):这几格是拿 ` · `
|
|
2772
|
+
# 串成一行跟在插件名后面的,每格多两个字就把 80 列挤爆。真要看细目有 --verbose。
|
|
2773
|
+
plugin:
|
|
2774
|
+
mcp_not_json: 'mcp.json 不是合法 JSON({reason}),整份已忽略'
|
|
2775
|
+
mcp_not_object: 'mcp.json 顶层必须是对象,整份已忽略'
|
|
2776
|
+
# 从 Claude Desktop 抄配置的人一定会撞上这条。**措辞和 plugin-mcp 那份
|
|
2777
|
+
# parseMcpConfig 里的刻意一致** —— 同一个错,用户不该看到两种说法
|
|
2778
|
+
mcp_wrong_key: '检测到 `mcpServers` 字段;epoch 读的是 `servers`,请改键名'
|
|
2779
|
+
mcp_no_servers: 'mcp.json 缺少 `servers` 字段,没有可用的 MCP server'
|
|
2780
|
+
mcp_servers_not_object: '`servers` 必须是对象(server 名 → 配置),整份已忽略'
|
|
2781
|
+
# ⚠ 和 hook 那行同一个理由:**会起子进程**。刻意不写成「有安全风险」——
|
|
2782
|
+
# 一个能挂 hook 的插件本来就能起任意子进程,这一行要答的是
|
|
2783
|
+
# 「装完之后我这台机器上会多跑几个进程」
|
|
2784
|
+
preview_mcp: '⚠ MCP server {count} 台: {names} —— 每台都是一个独立进程,启动 epoch 时会被拉起来'
|
|
2785
|
+
n_commands: '命令 {count}'
|
|
2786
|
+
n_roles: '角色 {count}'
|
|
2787
|
+
n_skills: '技能 {count}'
|
|
2788
|
+
n_hooks: 'hook {count}'
|
|
2789
|
+
n_deny: 'deny {count}'
|
|
2790
|
+
n_mcp: 'MCP {count}'
|
|
2791
|
+
brings_nothing: '(装了等于没装)'
|
|
2792
|
+
|
|
2793
|
+
# 插件带来的 MCP server 在装配时的诊断(runtime/src/plugin-mcp-servers.ts,方案 44 PR-3)。
|
|
2794
|
+
#
|
|
2795
|
+
# 措辞的判据同 host_capabilities 那一组:**「带进来了但没起来」是这类东西唯一的
|
|
2796
|
+
# 失败模式**,而插件作者和用户都不在现场 —— 所以坏的那台要指名道姓,并且把
|
|
2797
|
+
# 「为什么这批名字比 mcp.json 严」说出来,否则作者只会看到「我这台没起来」。
|
|
2798
|
+
# 往用户手写的那份 mcp.json 里插一台时,那个文本扫描器看不懂在哪儿
|
|
2799
|
+
# (runtime/src/mcp-config-write.ts,2026-08-18)。
|
|
2800
|
+
#
|
|
2801
|
+
# ⚠️ 这五句**是我们自己的诊断,所以要翻**;而同一条路上另一半(`EACCES` /
|
|
2802
|
+
# `ENOENT` 那种)是 Node 抛出来的原文,**原样转发不翻**。两者一起落进
|
|
2803
|
+
# `web.mcp_add_unwritable` 的 {detail},那不是不一致 —— 判据是「这句话是谁说的」。
|
|
2804
|
+
#
|
|
2805
|
+
# 措辞要点:每一句都得让用户知道**去那个文件里看哪儿**。只说「格式不对」的话,
|
|
2806
|
+
# 他面对的是一份自己写的、几十行的 JSON,而我们刚拒绝了他一次操作。
|
|
2807
|
+
mcp_write:
|
|
2808
|
+
not_object: '顶层不是一个 JSON 对象'
|
|
2809
|
+
top_unreadable: '顶层这个对象读不下去(多半是漏了逗号或引号)'
|
|
2810
|
+
servers_not_object: '`servers` 不是一个对象'
|
|
2811
|
+
servers_unreadable: '`servers` 这个对象读不下去'
|
|
2812
|
+
mcp_servers_key: '这份配置用的是 `mcpServers` 键名,而 epoch 读的是 `servers`;先把键名改过来再加'
|
|
2813
|
+
invalid: '配置无效'
|
|
2814
|
+
|
|
2815
|
+
# 整份写 mcp.json 那条路(runtime/src/mcp-config-file.ts,2026-08-18)。
|
|
2816
|
+
#
|
|
2817
|
+
# ⚠️ 只有一句,而那正是这条路上**唯一**会拦下保存的内容问题:不是合法 JSON。
|
|
2818
|
+
# 别往这一组里加「缺 command」「键名拼错了」那类 —— 它们照样存得下去,那些话走
|
|
2819
|
+
# 响应里的 issues(判据在 protocol 的 WireMcpConfigIssue 上)。
|
|
2820
|
+
#
|
|
2821
|
+
# ⚠️ {why} 是 JSON.parse 抛出来的**英文原文**(「Unexpected token } in JSON at
|
|
2822
|
+
# position 42」),**原样转发不翻**:那是屏幕上唯一说得出「错在第几个字符」的
|
|
2823
|
+
# 东西。判据同上面 mcp_write 那组的分工。
|
|
2824
|
+
mcp_config:
|
|
2825
|
+
invalid_json: '一个字节都没写 —— 这份内容不是合法的 JSON:{why}'
|
|
2826
|
+
|
|
2827
|
+
# 新建一份角色定义文件(core 的 `agent-role/create.ts`,2026-08-18)。
|
|
2828
|
+
#
|
|
2829
|
+
# ⚠️ `roundtrip` 那句是这一组里最要紧的:它说的是「我生成出来的那份,自己用读口
|
|
2830
|
+
# 读回来读不对」—— 也就是这条路**一个字节都没写**,而不是写坏了。措辞要让用户
|
|
2831
|
+
# 知道下一步是「换句话说 / 去那个目录手写一份」,而不是「重试一次」。
|
|
2832
|
+
#
|
|
2833
|
+
# ⚠️ `taken` 那句要把「不覆盖也不自动改名」的理由带上,判据同 `web.mcp_add_taken`:
|
|
2834
|
+
# 角色文件的正文是他自己写的一段散文,覆盖等于删掉它;而这个名字会原样变成
|
|
2835
|
+
# 派活时用的那个词(delegate_task 的 role 入参)。
|
|
2836
|
+
role_write:
|
|
2837
|
+
bad_name: '身份名「{name}」不能用 —— 只允许小写字母、数字和连字符,而且不能以连字符开头。这个名字同时是文件名和派活时用的那个词(delegate_task 的 role、--agent 的取值),所以收得严'
|
|
2838
|
+
taken: '已经有一个叫「{name}」的身份了 —— 换个名字。这里不覆盖也不自动改名:覆盖要删掉那份文件里你自己写的正文,改名会让派活时用的那个词和你敲进去的对不上'
|
|
2839
|
+
roundtrip: '一个字节都没写 —— 生成出来的那份文件我自己读不回来({why})。多半是描述或正文里有一段把 YAML 顶坏了;换个写法再试,或者直接去 ~/.epoch/agents/ 里手写一份'
|
|
2840
|
+
roundtrip_generic: '读回来是空的'
|
|
2841
|
+
# ⚠️ 撞名那句要把**哪一层**占着这个名字说出来:撞内置的只能换名字,撞项目级的
|
|
2842
|
+
# 他还可以去改自己仓库里那一份,两条出路不同。只说「重名了」的话,一个撞上
|
|
2843
|
+
# general 的人会以为自己的 ~/.epoch/agents/ 里躺着一份看不见的文件
|
|
2844
|
+
dup_in_table: '已经有一个叫「{name}」的身份了(来自{layer}),这个名字用不了 —— 换一个。这里不覆盖也不自动改名:撞上内置身份时覆盖会连带换掉派活的默认人选,而改名会让派活时用的那个词和你敲进去的对不上'
|
|
2845
|
+
layer_builtin: '内置'
|
|
2846
|
+
layer_user: '你自己的 ~/.epoch/agents/'
|
|
2847
|
+
layer_project: '这个工作区的 .epoch/agents/'
|
|
2848
|
+
layer_plugin: '某个已装插件'
|
|
2849
|
+
layer_host: '装着这个引擎的应用'
|
|
2850
|
+
unknown_tools: '这几个工具名不存在:{tools}。工具表里没有它们 —— 拼错了的话改过来,不确定有哪些就去设置 › 工具那一页看一眼。(写进去也不会报错,只会在下次启动的诊断里躺一条警告,然后这个身份静默地少几样东西)'
|
|
2851
|
+
blocked_tools: '{tools} 任何身份都拿不到 —— 那几样是硬底线(防递归派活 / 越权),不是这一份的白名单没写对。把它们从工具那一格里去掉'
|
|
2852
|
+
|
|
2853
|
+
plugin_mcp:
|
|
2854
|
+
unreadable: '{path} 读不了({reason}),这个插件带的 MCP server 一台都没起'
|
|
2855
|
+
bad_name: '插件 {plugin} 的 MCP server "{name}" 名字不合法(只允许小写字母、数字、连字符,且不能以连字符开头),这一台已跳过;其余照常。这批名字比 ~/.epoch/mcp.json 收得严,因为它会原样变成工具名的一部分发给模型 —— 一个非法名字会让整份工具表被拒'
|
|
2856
|
+
name_taken: 'MCP server "{name}" 和已有的一台重名(用户 mcp.json 或宿主带的),插件这一台已跳过 —— 先到的那台说了算'
|
|
2857
|
+
|
|
2858
|
+
# 长任务的目标(方案 52)—— `/goal` 命令族的全部文案。
|
|
2859
|
+
#
|
|
2860
|
+
# ## 为什么这一族**在** catalog 里,而 `blocked` 的理由正文不在
|
|
2861
|
+
#
|
|
2862
|
+
# 两者读者不同,判据在 `core/src/goal/wording.ts` 的文件头:这一族是**界面**
|
|
2863
|
+
# (用户敲 `/goal` 看到的那几行),而那一份是**记录和模型输入**(落进 goals 表、
|
|
2864
|
+
# 进 system prompt)。后者跟着界面语言变的话,同一段会话前后两条 blocked
|
|
2865
|
+
# 会是两种语言,而它是要被 grep 的。
|
|
2866
|
+
#
|
|
2867
|
+
# ## 每一条拒绝都必须说出「下一步敲什么」
|
|
2868
|
+
#
|
|
2869
|
+
# `refuse_busy` 说 `/goal clear`、`refuse_no_goal` 说怎么建一个、
|
|
2870
|
+
# `refuse_bad_budget` 给出取值范围 —— 少了那半句,用户面对的是一个「不行」
|
|
2871
|
+
# 加一个他猜不出来的原因,然后照着原样再敲一次。
|
|
2872
|
+
goal:
|
|
2873
|
+
usage: |-
|
|
2874
|
+
/goal <目标> 给这个长任务定一条完成判据(默认 {rounds} 轮预算)
|
|
2875
|
+
/goal 看现在的目标、用了几轮、卡没卡住
|
|
2876
|
+
/goal edit <目标> 改措辞(不重置轮次,也不改状态)
|
|
2877
|
+
/goal budget <n> 改预算轮数(1 ~ {max})
|
|
2878
|
+
/goal pause / resume 按停 / 放行
|
|
2879
|
+
/goal done <依据> 你替它报完成,依据必填
|
|
2880
|
+
/goal clear 清掉这个目标
|
|
2881
|
+
# 没有目标时 `/goal` 印的那一段。**不是一句「没有目标」**:这条命令绝大多数
|
|
2882
|
+
# 用户是第一次见,这一屏要同时回答「它是干什么的」和「我该怎么用」
|
|
2883
|
+
none: |-
|
|
2884
|
+
这段会话还没有目标。
|
|
2885
|
+
|
|
2886
|
+
目标回答的是「**这个任务什么算完成、最多花多少轮**」—— 长任务、
|
|
2887
|
+
尤其是交代完就走开的任务用得上:跑到预算尽头会留下一条「卡在哪」的记录,
|
|
2888
|
+
而不是只剩「模型不说话了」。
|
|
2889
|
+
|
|
2890
|
+
{usage}
|
|
2891
|
+
# 有目标时那一屏。占位符全部由宿主填好(cli/src/goal-view.ts)
|
|
2892
|
+
status: |-
|
|
2893
|
+
目标 {objective}
|
|
2894
|
+
状态 {phase}
|
|
2895
|
+
轮次 {used} / {max}{extra}
|
|
2896
|
+
phase_active: '在跑'
|
|
2897
|
+
phase_paused: '已按停(轮次不推进)'
|
|
2898
|
+
phase_blocked: '卡住了'
|
|
2899
|
+
phase_complete: '已完成'
|
|
2900
|
+
# 状态那一屏底下那几行「所以呢」。每一条都接着说下一步
|
|
2901
|
+
extra_blocked: |-
|
|
2902
|
+
|
|
2903
|
+
卡在 [{code}] {message}
|
|
2904
|
+
补上之后敲 /goal resume 放它继续;要加预算敲 /goal budget <n>。
|
|
2905
|
+
extra_complete: |-
|
|
2906
|
+
|
|
2907
|
+
完成依据 {evidence}
|
|
2908
|
+
要接着干下一件事,直接 /goal <新目标> 就行(完成的目标会被替换掉)。
|
|
2909
|
+
extra_paused: |-
|
|
2910
|
+
|
|
2911
|
+
敲 /goal resume 放它继续。
|
|
2912
|
+
extra_active: ''
|
|
2913
|
+
created: '目标已定:{objective}(预算 {max} 轮)。每条你发的消息算一轮,用完会转成「卡住」而不是「完成」。'
|
|
2914
|
+
edited: '目标改成:{objective}。轮次和状态没动({used}/{max})—— 要重新开始就 /goal clear 再建一个。'
|
|
2915
|
+
budget_set: '预算改成 {max} 轮(已用 {used})。'
|
|
2916
|
+
budget_set_exhausted: '预算改成 {max} 轮,而已经用了 {used} —— 当场转成「卡住」了。要继续就把预算调大。'
|
|
2917
|
+
paused: '已按停。轮次不再推进,目标本身留着。'
|
|
2918
|
+
resumed: '已放行,继续按目标跑({used}/{max})。'
|
|
2919
|
+
completed: '已记为完成。依据:{evidence}'
|
|
2920
|
+
cleared: '目标已清除。'
|
|
2921
|
+
clear_noop: '本来就没有目标。'
|
|
2922
|
+
# 开机那一行(验收 8)。**只在 blocked 时出现** —— 人回来打开 TUI 的第一屏
|
|
2923
|
+
# 就该看见卡在哪,而不是要先想起来敲一条命令
|
|
2924
|
+
startup_blocked: '⚠ 上次的目标卡住了 [{code}]:{message}(敲 /goal 看详情,/goal resume 放行)'
|
|
2925
|
+
refuse_busy: '已经有一个还没结束的目标了({phase})。一个会话同时只有一个目标 —— 先 /goal clear,或者 /goal edit 改现在这个。'
|
|
2926
|
+
refuse_no_goal: '这段会话没有目标。先敲 /goal <目标> 定一条。'
|
|
2927
|
+
refuse_empty_objective: '目标不能是空的。写一句「什么算做完」,比如 /goal 把检视面板接上后台任务的输出。'
|
|
2928
|
+
refuse_objective_too_long: '目标太长了(上限 {max} 字)。它是**完成判据**不是计划正文 —— 步骤走 /plan。'
|
|
2929
|
+
refuse_bad_budget: '预算要是 1 到 {max} 之间的整数。'
|
|
2930
|
+
refuse_empty_evidence: '报完成必须给依据:/goal done <凭什么算完成>。说不出来就说明还没完成。'
|
|
2931
|
+
refuse_wrong_phase: '现在这个状态({phase})下做不了这件事。敲 /goal 看看它在哪一档。'
|
|
2932
|
+
refuse_unavailable: '目标存储不可用(会话库没起来),看 /diagnostics。'
|