@epoch-agent/tui 1.0.0 → 2.0.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/README.md +31 -29
- package/dist/index.js +6 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -68,24 +68,24 @@ web 也用同一份。⚠️ **别在模块级调 `t()`**:插槽是 `renderApp
|
|
|
68
68
|
|
|
69
69
|
## 结构
|
|
70
70
|
|
|
71
|
-
| 目录 / 文件 | 内容
|
|
72
|
-
| ---------------------- |
|
|
73
|
-
| `app.tsx` | 根组件:`<Static>` 历史 + 限高 pending + 底栏 + 全局键位
|
|
74
|
-
| `render.tsx` | `renderApp()`——provider 树 + ink render options
|
|
75
|
-
| `contexts/` | app / config / session / streaming / ui-state 五个 context
|
|
76
|
-
| `components/` | banner、history-item、input-box、status-bar、suggestions、toast、thinking-indicator、shortcuts-help
|
|
77
|
-
| `components/dialogs/` | tool-confirmation(审批)、plan-confirmation、command-palette、file-palette、search-palette(Ctrl+R)、rewind-picker(Esc Esc)、picker(单选 / 多选 / 自由输入)、select-list
|
|
78
|
-
| `components/messages/` | 按类型分发的消息渲染:user / model / diff / error / warning / info / hint
|
|
79
|
-
| `commands/` | 斜杠命令:`builtin.ts` / `session.ts` / `inspect.ts` / `workspace.ts` / `rewind.ts` / `plugins.ts` + `parse.ts` + `prefix.ts` + `types.ts`(`HostActions` 契约)
|
|
80
|
-
| `hooks/` | `use-agent-run.ts`(跑一轮 + 中断)、`use-slash-commands.ts`、`use-rewind.ts`(面板 + Esc Esc 窗口)、`use-question.ts`(结构化提问逐个走完)。⚠️ 2026-09-29 起,某一问带 `page`(插件在本机起的一张页)时副标题末尾补一句地址 —— **终端画不出 iframe
|
|
81
|
-
| `keybindings/` | 键位层(方案 31):`actions.ts` / `parser.ts`(ink 的 Key → chord)/ `defaults.ts`(默认表 = 原来的硬编码)/ `resolver.ts` / `use-keybinding.ts` / `sequences.ts` + `use-key-sequences.ts`(序列)
|
|
82
|
-
| `vim/` | Vim 模式(方案 31 PR-3):`motions.ts`(`w` / `$` / `f,` 落在哪)+ `machine.ts`(`(state, key) → (state, edits)` 纯状态机)+ `use-vim.ts`(React 外壳,只存 ref / 喂渲染)
|
|
83
|
-
| `streaming/` | 工具调用与 artifact 的展示格式化
|
|
84
|
-
| `text/` | `ops.ts` 多行 buffer 的纯状态迁移 + `use-text-buffer.ts` React 外壳
|
|
85
|
-
| `markdown/` | Markdown 解析、行内样式、代码高亮(lowlight)
|
|
86
|
-
| `theme/` | 语义色 + 颜色工具
|
|
87
|
-
| `types/` | 消息 / 状态类型、`checkpoint-types.ts`(core 那几个回退类型的结构性镜像)、`host-info.ts`(宿主算好的展示形状)
|
|
88
|
-
| `debug.ts` | debug 日志
|
|
71
|
+
| 目录 / 文件 | 内容 |
|
|
72
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
73
|
+
| `app.tsx` | 根组件:`<Static>` 历史 + 限高 pending + 底栏 + 全局键位 |
|
|
74
|
+
| `render.tsx` | `renderApp()`——provider 树 + ink render options |
|
|
75
|
+
| `contexts/` | app / config / session / streaming / ui-state 五个 context |
|
|
76
|
+
| `components/` | banner、history-item、input-box、status-bar、suggestions、toast、thinking-indicator、shortcuts-help |
|
|
77
|
+
| `components/dialogs/` | tool-confirmation(审批)、plan-confirmation、command-palette、file-palette、search-palette(Ctrl+R)、rewind-picker(Esc Esc)、picker(单选 / 多选 / 自由输入)、select-list |
|
|
78
|
+
| `components/messages/` | 按类型分发的消息渲染:user / model / diff / error / warning / info / hint |
|
|
79
|
+
| `commands/` | 斜杠命令:`builtin.ts` / `session.ts` / `inspect.ts` / `workspace.ts` / `rewind.ts` / `plugins.ts` + `parse.ts` + `prefix.ts` + `types.ts`(`HostActions` 契约) |
|
|
80
|
+
| `hooks/` | `use-agent-run.ts`(跑一轮 + 中断)、`use-slash-commands.ts`、`use-rewind.ts`(面板 + Esc Esc 窗口)、`use-question.ts`(结构化提问逐个走完)。⚠️ 2026-09-29 起,某一问带 `page`(插件在本机起的一张页)时副标题末尾补一句地址 —— **终端画不出 iframe,所以不开新组件**。2026-10-15 起带 `page` 的那一问可以没有选项:那时副标题换成「选项在这个页面里 …」,列表里只剩「其他…」,并且一律按单选走(判据在 `use-question.ts` 那一处) |
|
|
81
|
+
| `keybindings/` | 键位层(方案 31):`actions.ts` / `parser.ts`(ink 的 Key → chord)/ `defaults.ts`(默认表 = 原来的硬编码)/ `resolver.ts` / `use-keybinding.ts` / `sequences.ts` + `use-key-sequences.ts`(序列) |
|
|
82
|
+
| `vim/` | Vim 模式(方案 31 PR-3):`motions.ts`(`w` / `$` / `f,` 落在哪)+ `machine.ts`(`(state, key) → (state, edits)` 纯状态机)+ `use-vim.ts`(React 外壳,只存 ref / 喂渲染) |
|
|
83
|
+
| `streaming/` | 工具调用与 artifact 的展示格式化 |
|
|
84
|
+
| `text/` | `ops.ts` 多行 buffer 的纯状态迁移 + `use-text-buffer.ts` React 外壳 |
|
|
85
|
+
| `markdown/` | Markdown 解析、行内样式、代码高亮(lowlight) |
|
|
86
|
+
| `theme/` | 语义色 + 颜色工具 |
|
|
87
|
+
| `types/` | 消息 / 状态类型、`checkpoint-types.ts`(core 那几个回退类型的结构性镜像)、`host-info.ts`(宿主算好的展示形状) |
|
|
88
|
+
| `debug.ts` | debug 日志 |
|
|
89
89
|
|
|
90
90
|
## 布局约束(改这里之前先读)
|
|
91
91
|
|
|
@@ -207,25 +207,25 @@ Ink 的 `log-update` **只能擦除它上一帧写过的行数**。一旦动态
|
|
|
207
207
|
|
|
208
208
|
```
|
|
209
209
|
/model 看当前模型 / provider / 凭据 / 窗口
|
|
210
|
-
/model
|
|
211
|
-
/model anthropic/claude-opus-4-6 provider 和模型一起换
|
|
210
|
+
/model @my-relay 换到「我的模型」里那一条(端点 / key 跟着它走)
|
|
212
211
|
/model --reset 回到配置里那个
|
|
213
212
|
```
|
|
214
213
|
|
|
215
214
|
**换模型不清空上下文**——那正是它的用处(对着同一段对话换个更强或更便宜的脑子)。
|
|
216
215
|
代价是新模型得接得住已经积累的东西,所以有三道前置检查,任何一道不过就**一个字段都不动**:
|
|
217
216
|
|
|
218
|
-
| 拦住的情况 | 为什么
|
|
219
|
-
| ------------------------ |
|
|
220
|
-
|
|
|
221
|
-
| 会话里有图片,新模型不认 | 下一轮把带图历史发出去必然 400,而 400 是**已经计费**的
|
|
222
|
-
| 已用上下文超过新窗口 | 同上。先 `/clear` 或换个大窗口的模型
|
|
217
|
+
| 拦住的情况 | 为什么 |
|
|
218
|
+
| ------------------------ | ---------------------------------------------------------- |
|
|
219
|
+
| 目标那一条没有 key | 请求根本发不出去,理由里点名是哪一条(去设置页给它填一把) |
|
|
220
|
+
| 会话里有图片,新模型不认 | 下一轮把带图历史发出去必然 400,而 400 是**已经计费**的 |
|
|
221
|
+
| 已用上下文超过新窗口 | 同上。先 `/clear` 或换个大窗口的模型 |
|
|
223
222
|
|
|
224
223
|
装得下但已经很挤、或者新模型不支持工具调用,则是**换成 + 出声**(warning),不拦。
|
|
225
224
|
|
|
226
225
|
判据全在 core 的 `evaluateSelection`,TUI 只负责把结果念出来 —— 拒绝的理由逐字透传,
|
|
227
|
-
不在 TUI
|
|
228
|
-
|
|
226
|
+
不在 TUI 里重编一句。取值只认 `@<id>`(「我的模型」里的一条),解析走 protocol 的
|
|
227
|
+
`parseModelRef` —— **2.0.0 起裸模型名 / `<provider>/<model>` 一律报「看不懂」**,
|
|
228
|
+
那一轮仍跑在当前模型上。加条目用设置页或 `epoch model`。
|
|
229
229
|
|
|
230
230
|
> ⚠️ 已知边界(PR-2 复核后仍在):自动压缩的预算仍是**启动时**那个模型的窗口,
|
|
231
231
|
> 换到小窗口模型后压缩不会跟着提前触发 —— 这正是上面第三道检查直接拒绝而不是
|
|
@@ -241,8 +241,10 @@ Ink 的 `log-update` **只能擦除它上一帧写过的行数**。一旦动态
|
|
|
241
241
|
- **命令 frontmatter 的 `model:`** —— 一条自定义命令可以声明自己用哪个模型
|
|
242
242
|
(`model: utility` 解析成配置里的工具模型),**只在那一轮生效,收尾无条件还原**。
|
|
243
243
|
Esc 中断走的是生成器的 `return()`,还原照样发生
|
|
244
|
-
- **降级** ——
|
|
245
|
-
|
|
244
|
+
- **降级** —— 主模型这个落点没救了就换 `fallback.model` 指向的那一条(2.0.0 起
|
|
245
|
+
只有这一级,条目自带端点,可以跨家)。404 / 模型不存在、401/403 / key 被拒、
|
|
246
|
+
402 / 额度耗尽**当场换**;429 / 5xx / 超时那一类先在原地把重试预算跑完,**耗尽
|
|
247
|
+
再换**(2026-10-16 起 —— 在那之前这一族一次都降不到)。
|
|
246
248
|
下一轮又会**从你选的那个模型重新起步**:降级是这一次请求的事,不改你的选择
|
|
247
249
|
|
|
248
250
|
同一个模型**连续三次**把请求拖垮,本会话就不再自动用它了,并在那一轮结束时给一条
|
package/dist/index.js
CHANGED
|
@@ -4589,7 +4589,10 @@ function useQuestion({ prompt }) {
|
|
|
4589
4589
|
i: stepRef.current + 1,
|
|
4590
4590
|
n: questions.length
|
|
4591
4591
|
})} \xB7 ${current.header}` : current.header;
|
|
4592
|
-
const
|
|
4592
|
+
const pageOnly = current.options.length === 0;
|
|
4593
|
+
const subtitle2 = current.page ? `${head} \xB7 ${t(pageOnly ? "tui.question.page_only_hint" : "tui.question.page_hint", {
|
|
4594
|
+
url: current.page.url
|
|
4595
|
+
})}` : head;
|
|
4593
4596
|
const base = {
|
|
4594
4597
|
title: current.question,
|
|
4595
4598
|
subtitle: subtitle2,
|
|
@@ -4601,7 +4604,7 @@ function useQuestion({ prompt }) {
|
|
|
4601
4604
|
allowFreeInput: true,
|
|
4602
4605
|
onCancel: () => settle({}, true)
|
|
4603
4606
|
};
|
|
4604
|
-
const picker = current.multiSelect ? { ...base, multiSelect: true, onPick: pick } : { ...base, onPick: pick };
|
|
4607
|
+
const picker = current.multiSelect && !pageOnly ? { ...base, multiSelect: true, onPick: pick } : { ...base, onPick: pick };
|
|
4605
4608
|
return { picker, step: step2 };
|
|
4606
4609
|
}
|
|
4607
4610
|
// src/hooks/use-rewind.ts
|
|
@@ -5575,6 +5578,7 @@ function switchModel(ctx, arg) {
|
|
|
5575
5578
|
ctx.push({
|
|
5576
5579
|
type: "info",
|
|
5577
5580
|
text: t("tui.model.switched", {
|
|
5581
|
+
entry: selection.entry,
|
|
5578
5582
|
provider: selection.provider,
|
|
5579
5583
|
model: selection.model,
|
|
5580
5584
|
k: (contextLength / 1e3).toFixed(0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epoch-agent/tui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "epoch-agent Ink TUI 界面",
|
|
6
6
|
"repository": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"mnemonist": "0",
|
|
30
30
|
"string-width": "8",
|
|
31
31
|
"strip-ansi": "7",
|
|
32
|
-
"@epoch-agent/protocol": "
|
|
33
|
-
"@epoch-agent/view": "
|
|
32
|
+
"@epoch-agent/protocol": "2.0.0",
|
|
33
|
+
"@epoch-agent/view": "2.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^26.1.2",
|