@ai-zen/cli 1.0.0-alpha.3 → 1.0.0-alpha.30
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/CHANGELOG.md +885 -0
- package/LICENSE +21 -0
- package/README.md +135 -30
- package/README.zh.md +135 -30
- package/dist/agent-creator.d.ts +11 -0
- package/dist/agent-creator.d.ts.map +1 -1
- package/dist/agent-creator.js +50 -20
- package/dist/agent-creator.js.map +1 -1
- package/dist/agent-switch.d.ts +61 -0
- package/dist/agent-switch.d.ts.map +1 -0
- package/dist/agent-switch.js +83 -0
- package/dist/agent-switch.js.map +1 -0
- package/dist/agents-store.d.ts +6 -1
- package/dist/agents-store.d.ts.map +1 -1
- package/dist/agents-store.js +46 -4
- package/dist/agents-store.js.map +1 -1
- package/dist/auto-migrate-confirm.d.ts +43 -0
- package/dist/auto-migrate-confirm.d.ts.map +1 -0
- package/dist/auto-migrate-confirm.js +47 -0
- package/dist/auto-migrate-confirm.js.map +1 -0
- package/dist/clipboard.d.ts +68 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +177 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/config-editor.d.ts +42 -5
- package/dist/config-editor.d.ts.map +1 -1
- package/dist/config-editor.js +80 -27
- package/dist/config-editor.js.map +1 -1
- package/dist/config.d.ts +76 -17
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +143 -39
- package/dist/config.js.map +1 -1
- package/dist/conversation-commands/registry.d.ts +127 -4
- package/dist/conversation-commands/registry.d.ts.map +1 -1
- package/dist/conversation-commands/registry.js +93 -14
- package/dist/conversation-commands/registry.js.map +1 -1
- package/dist/conversation-persist-plugin.d.ts +0 -1
- package/dist/conversation-persist-plugin.d.ts.map +1 -1
- package/dist/conversation-persist-plugin.js +7 -16
- package/dist/conversation-persist-plugin.js.map +1 -1
- package/dist/conversation-repository.d.ts +79 -18
- package/dist/conversation-repository.d.ts.map +1 -1
- package/dist/conversation-repository.js +96 -30
- package/dist/conversation-repository.js.map +1 -1
- package/dist/cwd-tracker-plugin.d.ts.map +1 -1
- package/dist/cwd-tracker-plugin.js +22 -7
- package/dist/cwd-tracker-plugin.js.map +1 -1
- package/dist/format-time.d.ts +17 -6
- package/dist/format-time.d.ts.map +1 -1
- package/dist/format-time.js +32 -14
- package/dist/format-time.js.map +1 -1
- package/dist/handoff-service.d.ts +19 -0
- package/dist/handoff-service.d.ts.map +1 -0
- package/dist/handoff-service.js +89 -0
- package/dist/handoff-service.js.map +1 -0
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +69 -15
- package/dist/hook.js.map +1 -1
- package/dist/i18n/en-wizard.d.ts +8 -0
- package/dist/i18n/en-wizard.d.ts.map +1 -0
- package/dist/i18n/en-wizard.js +303 -0
- package/dist/i18n/en-wizard.js.map +1 -0
- package/dist/i18n/en.d.ts +7 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +247 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/index.d.ts +57 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +109 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/preferences.d.ts +37 -0
- package/dist/i18n/preferences.d.ts.map +1 -0
- package/dist/i18n/preferences.js +73 -0
- package/dist/i18n/preferences.js.map +1 -0
- package/dist/i18n/zh-wizard.d.ts +11 -0
- package/dist/i18n/zh-wizard.d.ts.map +1 -0
- package/dist/i18n/zh-wizard.js +306 -0
- package/dist/i18n/zh-wizard.js.map +1 -0
- package/dist/i18n/zh.d.ts +10 -0
- package/dist/i18n/zh.d.ts.map +1 -0
- package/dist/i18n/zh.js +251 -0
- package/dist/i18n/zh.js.map +1 -0
- package/dist/index.js +72 -25
- package/dist/index.js.map +1 -1
- package/dist/mcp-command.d.ts +32 -0
- package/dist/mcp-command.d.ts.map +1 -0
- package/dist/mcp-command.js +37 -0
- package/dist/mcp-command.js.map +1 -0
- package/dist/mode.d.ts +31 -4
- package/dist/mode.d.ts.map +1 -1
- package/dist/mode.js +42 -7
- package/dist/mode.js.map +1 -1
- package/dist/model-switch.d.ts +58 -0
- package/dist/model-switch.d.ts.map +1 -0
- package/dist/model-switch.js +62 -0
- package/dist/model-switch.js.map +1 -0
- package/dist/reasoning-effort.d.ts +96 -0
- package/dist/reasoning-effort.d.ts.map +1 -0
- package/dist/reasoning-effort.js +164 -0
- package/dist/reasoning-effort.js.map +1 -0
- package/dist/reasoning-presets.d.ts +34 -0
- package/dist/reasoning-presets.d.ts.map +1 -0
- package/dist/reasoning-presets.js +85 -0
- package/dist/reasoning-presets.js.map +1 -0
- package/dist/sdk-log.d.ts +84 -0
- package/dist/sdk-log.d.ts.map +1 -0
- package/dist/sdk-log.js +167 -0
- package/dist/sdk-log.js.map +1 -0
- package/dist/session-pointer.d.ts.map +1 -1
- package/dist/session-pointer.js +10 -4
- package/dist/session-pointer.js.map +1 -1
- package/dist/stdio-runner.d.ts.map +1 -1
- package/dist/stdio-runner.js +32 -30
- package/dist/stdio-runner.js.map +1 -1
- package/dist/sub-agent-guard-plugin.d.ts +10 -7
- package/dist/sub-agent-guard-plugin.d.ts.map +1 -1
- package/dist/sub-agent-guard-plugin.js +12 -9
- package/dist/sub-agent-guard-plugin.js.map +1 -1
- package/dist/tui/chat-history.d.ts +29 -0
- package/dist/tui/chat-history.d.ts.map +1 -0
- package/dist/tui/chat-history.js +55 -0
- package/dist/tui/chat-history.js.map +1 -0
- package/dist/tui/chat-session.d.ts +177 -57
- package/dist/tui/chat-session.d.ts.map +1 -1
- package/dist/tui/chat-session.js +362 -126
- package/dist/tui/chat-session.js.map +1 -1
- package/dist/tui/components.d.ts +113 -7
- package/dist/tui/components.d.ts.map +1 -1
- package/dist/tui/components.js +177 -12
- package/dist/tui/components.js.map +1 -1
- package/dist/tui/config-wizard/detail.d.ts +18 -10
- package/dist/tui/config-wizard/detail.d.ts.map +1 -1
- package/dist/tui/config-wizard/detail.js +53 -46
- package/dist/tui/config-wizard/detail.js.map +1 -1
- package/dist/tui/config-wizard/engine.d.ts +2 -2
- package/dist/tui/config-wizard/engine.d.ts.map +1 -1
- package/dist/tui/config-wizard/engine.js +48 -26
- package/dist/tui/config-wizard/engine.js.map +1 -1
- package/dist/tui/config-wizard/fields.d.ts +3 -1
- package/dist/tui/config-wizard/fields.d.ts.map +1 -1
- package/dist/tui/config-wizard/fields.js +202 -167
- package/dist/tui/config-wizard/fields.js.map +1 -1
- package/dist/tui/config-wizard/frame.js +1 -1
- package/dist/tui/config-wizard/kv-editor.d.ts.map +1 -1
- package/dist/tui/config-wizard/kv-editor.js +13 -5
- package/dist/tui/config-wizard/kv-editor.js.map +1 -1
- package/dist/tui/config-wizard/list.d.ts +3 -1
- package/dist/tui/config-wizard/list.d.ts.map +1 -1
- package/dist/tui/config-wizard/list.js +4 -4
- package/dist/tui/config-wizard/list.js.map +1 -1
- package/dist/tui/config-wizard/mcp-helpers.d.ts.map +1 -1
- package/dist/tui/config-wizard/mcp-helpers.js +4 -3
- package/dist/tui/config-wizard/mcp-helpers.js.map +1 -1
- package/dist/tui/config-wizard/menu.d.ts.map +1 -1
- package/dist/tui/config-wizard/menu.js +33 -21
- package/dist/tui/config-wizard/menu.js.map +1 -1
- package/dist/tui/config-wizard/prompt-editor.d.ts +8 -1
- package/dist/tui/config-wizard/prompt-editor.d.ts.map +1 -1
- package/dist/tui/config-wizard/prompt-editor.js +11 -7
- package/dist/tui/config-wizard/prompt-editor.js.map +1 -1
- package/dist/tui/config-wizard/prompt.d.ts.map +1 -1
- package/dist/tui/config-wizard/prompt.js +8 -7
- package/dist/tui/config-wizard/prompt.js.map +1 -1
- package/dist/tui/config-wizard/step-agents.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-agents.js +50 -29
- package/dist/tui/config-wizard/step-agents.js.map +1 -1
- package/dist/tui/config-wizard/step-defaults.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-defaults.js +77 -27
- package/dist/tui/config-wizard/step-defaults.js.map +1 -1
- package/dist/tui/config-wizard/step-endpoints.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-endpoints.js +27 -14
- package/dist/tui/config-wizard/step-endpoints.js.map +1 -1
- package/dist/tui/config-wizard/step-mcp.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-mcp.js +29 -25
- package/dist/tui/config-wizard/step-mcp.js.map +1 -1
- package/dist/tui/config-wizard/step-models.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-models.js +98 -43
- package/dist/tui/config-wizard/step-models.js.map +1 -1
- package/dist/tui/config-wizard/step-new-endpoint.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-new-endpoint.js +16 -12
- package/dist/tui/config-wizard/step-new-endpoint.js.map +1 -1
- package/dist/tui/config-wizard/step-simple.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-simple.js +18 -17
- package/dist/tui/config-wizard/step-simple.js.map +1 -1
- package/dist/tui/config-wizard/types.d.ts +31 -3
- package/dist/tui/config-wizard/types.d.ts.map +1 -1
- package/dist/tui/config-wizard/types.js.map +1 -1
- package/dist/tui/file-index.d.ts +38 -0
- package/dist/tui/file-index.d.ts.map +1 -0
- package/dist/tui/file-index.js +117 -0
- package/dist/tui/file-index.js.map +1 -0
- package/dist/tui/index.d.ts +18 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +107 -49
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/input-editing.d.ts +52 -0
- package/dist/tui/input-editing.d.ts.map +1 -0
- package/dist/tui/input-editing.js +91 -0
- package/dist/tui/input-editing.js.map +1 -0
- package/dist/tui/key-debug.d.ts +62 -0
- package/dist/tui/key-debug.d.ts.map +1 -0
- package/dist/tui/key-debug.js +92 -0
- package/dist/tui/key-debug.js.map +1 -0
- package/dist/tui/mention.d.ts +47 -0
- package/dist/tui/mention.d.ts.map +1 -0
- package/dist/tui/mention.js +152 -0
- package/dist/tui/mention.js.map +1 -0
- package/dist/tui/mouse.d.ts +55 -0
- package/dist/tui/mouse.d.ts.map +1 -0
- package/dist/tui/mouse.js +74 -0
- package/dist/tui/mouse.js.map +1 -0
- package/dist/tui/phase.d.ts +64 -0
- package/dist/tui/phase.d.ts.map +1 -0
- package/dist/tui/phase.js +112 -0
- package/dist/tui/phase.js.map +1 -0
- package/dist/tui/screens/agent-tabs.d.ts +53 -0
- package/dist/tui/screens/agent-tabs.d.ts.map +1 -0
- package/dist/tui/screens/agent-tabs.js +81 -0
- package/dist/tui/screens/agent-tabs.js.map +1 -0
- package/dist/tui/screens/chat-commands.d.ts +82 -3
- package/dist/tui/screens/chat-commands.d.ts.map +1 -1
- package/dist/tui/screens/chat-commands.js +688 -160
- package/dist/tui/screens/chat-commands.js.map +1 -1
- package/dist/tui/screens/chat.d.ts +57 -5
- package/dist/tui/screens/chat.d.ts.map +1 -1
- package/dist/tui/screens/chat.js +799 -304
- package/dist/tui/screens/chat.js.map +1 -1
- package/dist/tui/screens/common-keys.d.ts +36 -0
- package/dist/tui/screens/common-keys.d.ts.map +1 -0
- package/dist/tui/screens/common-keys.js +67 -0
- package/dist/tui/screens/common-keys.js.map +1 -0
- package/dist/tui/screens/frame-buffer.d.ts +47 -0
- package/dist/tui/screens/frame-buffer.d.ts.map +1 -0
- package/dist/tui/screens/frame-buffer.js +78 -0
- package/dist/tui/screens/frame-buffer.js.map +1 -0
- package/dist/tui/screens/handoff-choice.d.ts +37 -0
- package/dist/tui/screens/handoff-choice.d.ts.map +1 -0
- package/dist/tui/screens/handoff-choice.js +42 -0
- package/dist/tui/screens/handoff-choice.js.map +1 -0
- package/dist/tui/screens/index.d.ts +11 -9
- package/dist/tui/screens/index.d.ts.map +1 -1
- package/dist/tui/screens/index.js +9 -8
- package/dist/tui/screens/index.js.map +1 -1
- package/dist/tui/screens/input-row.d.ts +64 -0
- package/dist/tui/screens/input-row.d.ts.map +1 -0
- package/dist/tui/screens/input-row.js +67 -0
- package/dist/tui/screens/input-row.js.map +1 -0
- package/dist/tui/screens/input.d.ts +77 -1
- package/dist/tui/screens/input.d.ts.map +1 -1
- package/dist/tui/screens/input.js +125 -8
- package/dist/tui/screens/input.js.map +1 -1
- package/dist/tui/screens/layout.d.ts +42 -3
- package/dist/tui/screens/layout.d.ts.map +1 -1
- package/dist/tui/screens/layout.js +43 -13
- package/dist/tui/screens/layout.js.map +1 -1
- package/dist/tui/screens/lines.d.ts +6 -36
- package/dist/tui/screens/lines.d.ts.map +1 -1
- package/dist/tui/screens/lines.js +8 -71
- package/dist/tui/screens/lines.js.map +1 -1
- package/dist/tui/screens/messages-view.d.ts +29 -0
- package/dist/tui/screens/messages-view.d.ts.map +1 -0
- package/dist/tui/screens/messages-view.js +162 -0
- package/dist/tui/screens/messages-view.js.map +1 -0
- package/dist/tui/screens/notice-bar.d.ts +5 -0
- package/dist/tui/screens/notice-bar.d.ts.map +1 -0
- package/dist/tui/screens/notice-bar.js +21 -0
- package/dist/tui/screens/notice-bar.js.map +1 -0
- package/dist/tui/screens/notice.d.ts +66 -0
- package/dist/tui/screens/notice.d.ts.map +1 -0
- package/dist/tui/screens/notice.js +106 -0
- package/dist/tui/screens/notice.js.map +1 -0
- package/dist/tui/screens/overlays.d.ts.map +1 -1
- package/dist/tui/screens/overlays.js +4 -3
- package/dist/tui/screens/overlays.js.map +1 -1
- package/dist/tui/screens/render.d.ts +2 -4
- package/dist/tui/screens/render.d.ts.map +1 -1
- package/dist/tui/screens/render.js +19 -50
- package/dist/tui/screens/render.js.map +1 -1
- package/dist/tui/screens/splash.d.ts +1 -1
- package/dist/tui/screens/splash.d.ts.map +1 -1
- package/dist/tui/screens/splash.js +12 -3
- package/dist/tui/screens/splash.js.map +1 -1
- package/dist/tui/screens/use-chat-viewport.d.ts +22 -0
- package/dist/tui/screens/use-chat-viewport.d.ts.map +1 -0
- package/dist/tui/screens/use-chat-viewport.js +50 -0
- package/dist/tui/screens/use-chat-viewport.js.map +1 -0
- package/dist/tui/screens/use-input-editing.d.ts +69 -0
- package/dist/tui/screens/use-input-editing.d.ts.map +1 -0
- package/dist/tui/screens/use-input-editing.js +220 -0
- package/dist/tui/screens/use-input-editing.js.map +1 -0
- package/dist/tui/screens/use-overlays.d.ts +25 -0
- package/dist/tui/screens/use-overlays.d.ts.map +1 -0
- package/dist/tui/screens/use-overlays.js +32 -0
- package/dist/tui/screens/use-overlays.js.map +1 -0
- package/dist/tui/screens/use-wizard-host.d.ts +46 -0
- package/dist/tui/screens/use-wizard-host.d.ts.map +1 -0
- package/dist/tui/screens/use-wizard-host.js +123 -0
- package/dist/tui/screens/use-wizard-host.js.map +1 -0
- package/dist/tui/status-segments.d.ts +37 -0
- package/dist/tui/status-segments.d.ts.map +1 -0
- package/dist/tui/status-segments.js +166 -0
- package/dist/tui/status-segments.js.map +1 -0
- package/dist/tui/text.d.ts +27 -7
- package/dist/tui/text.d.ts.map +1 -1
- package/dist/tui/text.js +116 -38
- package/dist/tui/text.js.map +1 -1
- package/dist/tui/theme.d.ts +2 -0
- package/dist/tui/theme.d.ts.map +1 -1
- package/dist/tui/theme.js +2 -0
- package/dist/tui/theme.js.map +1 -1
- package/dist/tui/token-rate.d.ts +83 -0
- package/dist/tui/token-rate.d.ts.map +1 -0
- package/dist/tui/token-rate.js +195 -0
- package/dist/tui/token-rate.js.map +1 -0
- package/dist/types.d.ts +7 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/user-content.d.ts +41 -0
- package/dist/user-content.d.ts.map +1 -0
- package/dist/user-content.js +37 -0
- package/dist/user-content.js.map +1 -0
- package/dist/version-compare.d.ts +37 -0
- package/dist/version-compare.d.ts.map +1 -0
- package/dist/version-compare.js +105 -0
- package/dist/version-compare.js.map +1 -0
- package/dist/version.js.map +1 -1
- package/package.json +5 -4
- package/dist/auto-migrate-confirm-plugin.d.ts +0 -58
- package/dist/auto-migrate-confirm-plugin.d.ts.map +0 -1
- package/dist/auto-migrate-confirm-plugin.js +0 -61
- package/dist/auto-migrate-confirm-plugin.js.map +0 -1
- package/dist/migration-service.d.ts +0 -15
- package/dist/migration-service.d.ts.map +0 -1
- package/dist/migration-service.js +0 -59
- package/dist/migration-service.js.map +0 -1
- package/dist/tui/screens/state.d.ts +0 -30
- package/dist/tui/screens/state.d.ts.map +0 -1
- package/dist/tui/screens/state.js +0 -100
- package/dist/tui/screens/state.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,888 @@
|
|
|
1
|
+
## [1.0.0-alpha.30] - 2026-11-02
|
|
2
|
+
|
|
3
|
+
> 本版把 22 条审查问题一次收口。有三处直接改变手感:补全菜单改为底部锚定、多行输入里
|
|
4
|
+
> `↑`/`↓` 先移光标、输入区限高 8 行;另外详情屏不再暴露 `custom`,换成了「恢复出厂设置」。
|
|
5
|
+
>
|
|
6
|
+
> 注:`1.0.0-alpha.28` / `.29` 的条目此前也只在本地(registry 上的 `alpha` 停在 alpha.27),
|
|
7
|
+
> 本版一并带出去。
|
|
8
|
+
|
|
9
|
+
### 💥 行为变更
|
|
10
|
+
|
|
11
|
+
- **补全菜单改为底部锚定** —— `/` 命令与 `@` 路径共用一套 `BottomMenu`:`items[0]` 紧贴输入框,
|
|
12
|
+
其余向上生长、装不下从顶端丢弃;行数上限由布局按剩余空间放行(不再固定 8 行),
|
|
13
|
+
渲染与 IME 光标读同一个 `layout.hintRows`。说明文字不再把命令名挤成一截。
|
|
14
|
+
- **`↑` / `↓` 先移动光标** —— 多行草稿里到首行 / 末行才翻历史(原来一按就跳历史)。
|
|
15
|
+
- **输入区限高 8 行**,窗口跟随光标(原来钉住末尾,光标退到第一行时会消失在框外)。
|
|
16
|
+
- **`↑` 翻历史带图片** —— 历史数据源统一到 `messages`,回到带图消息时图片一起恢复;
|
|
17
|
+
`file_id` 类型的历史图片只能恢复文本(本地没有副本)。
|
|
18
|
+
- **`/load` 列表只显示会话文件名** —— 不再逐个读会话文件解析摘要。
|
|
19
|
+
- **`/save <名字>`** —— 另存为指定文件名(旧文件名消失、last-session 指针跟随);
|
|
20
|
+
重名且不是当前会话时先二次确认。
|
|
21
|
+
- **详情屏不再有 `custom` 开关,改为「恢复出厂设置」** —— 出厂模型 / 图片模型用出厂定义
|
|
22
|
+
**整条替换**(`custom` 随之消失 = 重新归 SDK 托管);默认 Agent 只把提示词换回出厂,
|
|
23
|
+
名称 / 描述 / 模型 / 权限全部保留;出厂清单里没有同 id 的(自建)不显示该动作。
|
|
24
|
+
- **粘贴文本 256KB 上限** —— 超了报错且**不截断**(半截代码 / 半截 JSON 比不粘更坑)。
|
|
25
|
+
- **写句子不再乱弹 `@` 菜单** —— token 边界补上 `。 、 ! ? ( ) 《 》 【 】 〔 〕 「 」 『 』 … — ~`。
|
|
26
|
+
- **没有端点时禁止新建模型 / 图片模型** —— 以前能创建出 `endpointId: ""` 的空壳。
|
|
27
|
+
- **坏掉的 `mcp.json` 不再冒充空配置** —— MCP 屏写明「无法解析」、照样能编辑,
|
|
28
|
+
保存产出的正是一份格式正确的新文件(那是修复,不是覆盖丢数据)。
|
|
29
|
+
|
|
30
|
+
### 🐛 修复
|
|
31
|
+
|
|
32
|
+
- KV 编辑器删掉末项后回车崩溃(光标钳到新的末项)。
|
|
33
|
+
- `SelectList` 列表变短后回车崩溃(渲染与确认都走 `safeIndex`)。
|
|
34
|
+
- 自动落盘失败混在 info 语气里上报。
|
|
35
|
+
- `/back` 撤回后不落盘(存档里还留着被撤回的消息)。
|
|
36
|
+
- 删掉默认 Agent 时没清 `config.defaultAgent`,下次启动死在「Agent 未绑定模型」。
|
|
37
|
+
- stdout 被提前关闭时打 EPIPE 堆栈(按正常结束处理)。
|
|
38
|
+
- `/load` `/back` 选择器条数预算少扣 2 行,最后两条被吸底布局吃掉。
|
|
39
|
+
- `truncate` 按字素簇截断,不再把 emoji 劈成孤立代理项。
|
|
40
|
+
- e2e 断言与事实不符(`ai hook` 写 stdout、退出码 0),且被 `vitest.config.ts` 排除、
|
|
41
|
+
永远不会红 —— 断言修正后从 5 红变全绿。
|
|
42
|
+
|
|
43
|
+
### 🔒 安全
|
|
44
|
+
|
|
45
|
+
- **`@ai-zen/agents-sdk` 升到 `1.0.0-alpha.25`**:`EntityRepository.path()` 校验实体 id 不得逃出
|
|
46
|
+
仓储目录(`resolve` + `root + sep`,裸 `startsWith` 拦不住 `<root>-evil`)。`/save ../x`
|
|
47
|
+
现在明确报错,不再可能写到会话目录之外。这一版同时带上 SDK 的端点 `capabilities`
|
|
48
|
+
迁移回填(老的 `deepseek` 端点会补上 `filesApi`,图片改走 Files API)。
|
|
49
|
+
|
|
50
|
+
### ✅ 测试
|
|
51
|
+
|
|
52
|
+
- 单测 **57 文件 / 798 例通过**,e2e **9 passed / 3 skipped**(3 条需真 API key),
|
|
53
|
+
`tsc --noEmit` 干净。
|
|
54
|
+
- 发布门禁 `prepublishOnly` 改为 `npm run test:all`(单测 + 构建 + e2e);
|
|
55
|
+
`npm test` 语义不变,仍只跑类型 + 单测。
|
|
56
|
+
- 测试耗时 43.6s → ~8s:去掉 `isolate`、单 fork、依赖注入替换全部模块级 `vi.mock`。
|
|
57
|
+
|
|
58
|
+
## [1.0.0-alpha.29] - 2026-10-31
|
|
59
|
+
|
|
60
|
+
> `Ctrl+V` 从「弹一句未支持」变成**真的粘贴**:剪贴板里有图片就附在消息上,
|
|
61
|
+
> 没有就退回粘贴文本。
|
|
62
|
+
|
|
63
|
+
### ✨ 新功能
|
|
64
|
+
|
|
65
|
+
- **粘贴图片** —— `Ctrl+V`(kitty 协议下 `⌘V`)先问系统剪贴板要图片,有图就附到待发送
|
|
66
|
+
消息上,输入框上方出现 `📎 已附加 N 张图片 · Ctrl+V 再加 · Esc 清空`;没有图片则
|
|
67
|
+
当作普通粘贴,把剪贴板文本插到光标处。按平台探测:
|
|
68
|
+
|
|
69
|
+
| 平台 | 图片 | 文本 |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| macOS | `osascript -e "get the clipboard as «class PNGf»"`(stdout 是 `«data PNGf<HEX>»`) | `pbpaste` |
|
|
72
|
+
| Linux | `wl-paste --type image/png`(Wayland)→ `xclip -t image/png -o`(X11) | `wl-paste --no-newline` → `xclip -o` |
|
|
73
|
+
| Windows | PowerShell `Get-Clipboard -Format Image` → PNG → base64 | `Get-Clipboard -Raw` |
|
|
74
|
+
|
|
75
|
+
- **全程不落盘** —— 图片以 base64 直接交给 SDK 的 `fileToContentSection()`(它的
|
|
76
|
+
`FileSource` 本来就有 `{ base64, mime }` 变体):上传 Files API 还是内联 base64、
|
|
77
|
+
模型不支持视觉时怎么退化,全部由 SDK 按当前端点决定,CLI 不重复实现、也不硬编码端点。
|
|
78
|
+
- **一条消息最多 4 张**(`MAX_PASTED_IMAGES`):一张截图内联后常 1~3MB,再多一次请求
|
|
79
|
+
就是几十 MB。超限、图片过大(>20MB)、剪贴板为空、没有剪贴板工具,各有独立提示。
|
|
80
|
+
|
|
81
|
+
### 🎨 交互
|
|
82
|
+
|
|
83
|
+
- **图片优先、文本兑底**,顺序不能反:文本先粘进去就来不及了(草稿里已经多了一坨东西)。
|
|
84
|
+
- `Esc` 有附件时**先清附件、草稿保留** —— 误粘一张总有退路,又不必让 `ESC` 变成
|
|
85
|
+
「清空一切」的随机键。
|
|
86
|
+
- 附件提示行恒为一行,行数同时计入 `computeChatLayout`(占高)与
|
|
87
|
+
`computeInputCursorPosition`(IME 光标 y)—— 差一行输入框就整体位移,这条有前科。
|
|
88
|
+
- 乐观回显按「文本段 + 图片占位块」构造,与实际发送的结构一致(否则图片那几条会在
|
|
89
|
+
真实消息落地前闪一下不见);转录里内联图片只显示 `[图片]`,不把几十万字符的
|
|
90
|
+
data URL 铺到屏幕上(远程 URL 才带地址)。
|
|
91
|
+
|
|
92
|
+
### 🐛 修复
|
|
93
|
+
|
|
94
|
+
- **带图片的消息会丢掉 cwd 注入** —— `CwdTrackerPlugin` 原来只找 `content` 是字符串的
|
|
95
|
+
用户消息,换成内容块数组后会被直接跳过,那一轮模型看到的还是上一轮的工作目录。
|
|
96
|
+
现在追加到**最后一个 text 段**上;数组里没有 text 段时补一段。
|
|
97
|
+
- README / getting-started 里「`PgUp`/`PgDn`(或 `Alt+V`/`Ctrl+V`)翻页」是错的:
|
|
98
|
+
`Ctrl+V` 从未兼任翻页(`common-keys.ts` 只认 `Alt+V`),已修正。
|
|
99
|
+
|
|
100
|
+
### ✅ 测试
|
|
101
|
+
|
|
102
|
+
- 新增 33 例:`clipboard.test.ts` 15(三平台命令选择 / osascript 十六进制解析 / 缺工具 /
|
|
103
|
+
空输出 / 超限 / CRLF 归一)、`user-content.test.ts` 6(文本段在首位 / 部分失败 /
|
|
104
|
+
全失败退回纯文本 / warning 透传)、`cwd-tracker-plugin.test.ts` 4(字符串 / 内容块 /
|
|
105
|
+
只有图片 / 不重复注入)、`pasted-images.test.tsx` 3(恒一行 / 布局多占一行 / 光标 y 同步)、
|
|
106
|
+
`chat.test.tsx` +5(无图粘文本 / 有图出附件行 / Esc 清附件保草稿 / 发送带图 / 空剪贴板提示)。
|
|
107
|
+
- 全量 **57 文件 / 741 例通过**,`tsc --noEmit` 0 错误。
|
|
108
|
+
- 真产物验证(本机 Linux、无剪贴板工具):`readClipboardImage() → { kind: "none" }`、
|
|
109
|
+
`readClipboardText() → null`,11ms 不抛错;不打桩地跑真实 SDK 转换,`vision: true` 得到
|
|
110
|
+
`image_url` 内联块,`vision` 缺省得到文本提示 + warning。
|
|
111
|
+
|
|
112
|
+
## [1.0.0-alpha.28] - 2026-10-31
|
|
113
|
+
|
|
114
|
+
> 输入框里敲 `@` 唤起**文件路径补全** —— 补进去的是相对路径文本,模型用 `read` 工具自己去读。
|
|
115
|
+
|
|
116
|
+
### ✨ 新功能
|
|
117
|
+
|
|
118
|
+
- **`@` 文件路径补全** —— 敲 `@` 弹出项目内候选,`↑`/`↓` 选择、`Tab` 或 `Enter` 补进草稿、
|
|
119
|
+
`Esc` 只关菜单(不动草稿)。三层拆分,与仓库既有风格一致:
|
|
120
|
+
- `tui/mention.ts`(纯逻辑):`extractMention()` 判「光标在不在一个 `@token` 里」、
|
|
121
|
+
`rankPaths()` 子序列模糊排序、`menuWindow()` 视窗、`mentionMenuRows()` 行数;
|
|
122
|
+
- `tui/file-index.ts`(IO):`git ls-files -co --exclude-standard` 优先(尊重 `.gitignore`、
|
|
123
|
+
含未跟踪文件),非 git 目录回落 `fs.glob` 并排除 `node_modules` / `.git` / `dist`;
|
|
124
|
+
15s 缓存、20000 条封顶、5s 超时;
|
|
125
|
+
- `screens/chat.tsx`:按键 → 候选 → `replaceRange()` 落字的接线。
|
|
126
|
+
- **判据落在字符事实上**:从光标往回扫到空白 / 行首,途中遇到 `@` 且其前一个是行首、空白或
|
|
127
|
+
分隔符才算 mention。于是 `a@b.com` 不会弹菜单;`@src/tui`、`(@src/x)`、`"@src/x"` 都会;
|
|
128
|
+
光标停在 token 中间(`@src|/foo.ts`)时 query 截止于光标、`end` 向后吃掉整个 token ——
|
|
129
|
+
补全替换的是整段,不会留下半截残字。
|
|
130
|
+
- **排序**:贪婪子序列 + 跳跃惩罚 + 连续命中奖励;**整体前缀 > 文件名前缀**,同分时浅的 / 短的
|
|
131
|
+
优先。实测本仓:`@` → 182 条索引里取 50 条(1.1ms)、`@chat` 命中 19 条(0.2ms)、
|
|
132
|
+
`@file-index` 精确命中 2 条(0.1ms)。
|
|
133
|
+
- **目录可逐层往下钻**:选中目录补尾斜杠(`@src/tui/`),补完菜单重新过滤出该目录下内容,
|
|
134
|
+
于是能一路 `@src/` → `@src/tui/` → 目标文件。
|
|
135
|
+
|
|
136
|
+
### 🎨 交互
|
|
137
|
+
|
|
138
|
+
- `Tab` / `Enter` = **补全不发送**(选完继续接着写,避免误发半截消息);`Esc` 只关菜单,
|
|
139
|
+
不清草稿(原 `Esc` 语义是清空草稿,`@` 打一半按 Esc 会连正文一起没)。
|
|
140
|
+
- 候选可能几十条,菜单**最多占 8 行**并随高亮滚动;索引未就绪时先占一行「正在扫描项目文件…」。
|
|
141
|
+
- 行数有**唯一真源** `mentionMenuRows()`:`computeChatLayout` 的 `hintCount`、IME 光标的 y 坐标、
|
|
142
|
+
`MentionMenu` 实际渲染三处同源,并有用例断言 `mentionMenuRows(n) === menuWindow(n)` 的行数。
|
|
143
|
+
- 占位提示改为「输入消息,/ 命令,@ 文件…」,让这个入口能被看见。
|
|
144
|
+
|
|
145
|
+
### 🔧 重构
|
|
146
|
+
|
|
147
|
+
- `useInputEditing` 新增 `replaceRange(start, end, text)`:整段替换**并记撤销快照**,
|
|
148
|
+
补错了 `Ctrl+_` 能退回(用 `setInputText` 会绕过撤销栈)。
|
|
149
|
+
|
|
150
|
+
### ✅ 测试
|
|
151
|
+
|
|
152
|
+
- 新增 39 例:`mention.test.ts` 22(提取 / 插入 / 排序 / 视窗 / 行数一致)、
|
|
153
|
+
`file-index.test.ts` 9(git 清单归一 / 缓存 TTL / cwd 切换 / 兜底 / 异常收敛 / 真磁盘)、
|
|
154
|
+
`components-mention.test.tsx` 5(8 行封顶 / 滚动跟随 / 目录尾斜杠 / 扫描占位 / 无匹配不占行)、
|
|
155
|
+
`chat.test.tsx` +6(弹菜单 / Tab 补全不发送 / Enter 补全 / ↓ 换高亮 / Esc 只关菜单 / 无匹配照常发)、
|
|
156
|
+
`use-input-editing.test.tsx` +3(区间替换 / 可撤销 / 越界钳制)。
|
|
157
|
+
- 全量 **53 文件 / 708 例通过**,`tsc --noEmit` 0 错误。
|
|
158
|
+
|
|
159
|
+
## [1.0.0-alpha.27] - 2026-10-29
|
|
160
|
+
|
|
161
|
+
> SDK 的警告与错误现在会进通知栏——**通用通道**,不为某个功能单独接线。
|
|
162
|
+
|
|
163
|
+
### ✨ 新功能
|
|
164
|
+
|
|
165
|
+
- **SDK 日志 → 通知栏**(`sdk-log.ts` 从「只落盘」改成 tee)—— `installSdkLogger()`
|
|
166
|
+
过去把 SDK 日志**整体接管到文件**(不然 `console.log` 会撕坏 Ink 的帧),代价是
|
|
167
|
+
「SDK 说了什么」在界面上一个字都看不见,只能事后翻 `~/.ai-zen/logs/sdk.log`。
|
|
168
|
+
现在 `warn` / `error` 除了落盘,还会转给通知栏:
|
|
169
|
+
- **晚绑定**:`installSdkLogger()` 跑在 Ink 渲染之前,而通知栏活在 `Chat` 的 React
|
|
170
|
+
state 里。`Chat` 挂载时注册 sink;注册前攒最近 5 条、注册时补推 —— 启动期的
|
|
171
|
+
问题恰恰最该看见。
|
|
172
|
+
- **去重**:同一句话反复刷(例如每轮都失败一次)只上栏一次。
|
|
173
|
+
- **封顶 20 条**:到顶后补一句 `More SDK warnings suppressed — see <日志路径>`,
|
|
174
|
+
之后不再刷。SDK 的日志量按「每次请求 / 每次刷新」计,不封顶会把栏位和 React
|
|
175
|
+
状态一起冲垮。
|
|
176
|
+
- `info` 只进文件 —— SDK 打 info 很密(mcp manifest、工具发现),上栏等于刷屏。
|
|
177
|
+
- 以后 SDK 任何新特性只要打 `warn` / `error`,界面上自动就有,**不必再为它传回调**。
|
|
178
|
+
这条通道只用既有的 `setLogger`,与 SDK 版本解耦。
|
|
179
|
+
- **通知栏新增 `warn` 档** —— `NoticeTone` 由 `info | error` 扩为 `info | warn | error`,
|
|
180
|
+
`NoticeBar` 用 `theme.warn` 着色。警告既不该降级成普通提示(会漏看),也不该冒充
|
|
181
|
+
错误(会误导)。
|
|
182
|
+
|
|
183
|
+
### 🔧 依赖
|
|
184
|
+
|
|
185
|
+
- `@ai-zen/agents-sdk` `1.0.0-alpha.23` → `1.0.0-alpha.24`:`tools/` 目录的加载改为
|
|
186
|
+
fail-safe —— 无 `export` 的文件不再被求值、求值期抑制 `process.exit` / `abort`
|
|
187
|
+
并把 `console` 输出改指 stderr、单文件 import 加超时。
|
|
188
|
+
|
|
189
|
+
这修的是一次真实事故:一个无参数时 `process.exit(2)` 的开发脚本放进
|
|
190
|
+
`.ai-zen/tools/` 后,SDK 在建会话途中 `import()` 了它,于是**每次启动都在初始化
|
|
191
|
+
途中把 CLI 杀掉**,屏幕上只剩最后一帧,看着像「卡住不启动」;同目录另几个脚本还在
|
|
192
|
+
每次启动时静默重写项目里的 PNG 资产。现在这类文件会被跳过(或被抑制),并通过上面
|
|
193
|
+
那条通道在通知栏报出来。
|
|
194
|
+
|
|
195
|
+
### ✅ 测试
|
|
196
|
+
|
|
197
|
+
- 新增 `src/sdk-log-notice.test.ts` 6 例:`warn`/`error` 上栏、`info` 不上栏、
|
|
198
|
+
启动期缓冲补推、去重、封顶收尾、静默模式不上栏。
|
|
199
|
+
- 全量 **50 文件 / 663 例通过**(alpha.26 时 49 文件 / 657 例),`tsc --noEmit` 0 错误。
|
|
200
|
+
|
|
201
|
+
## [1.0.0-alpha.26] - 2026-10-29
|
|
202
|
+
|
|
203
|
+
> `/update` 从「一个转圈 + 一句结果」改成**每一步都说清在干什么**,
|
|
204
|
+
> 并补上了此前名不副实的「重启生效」——现在是真的重启进程。
|
|
205
|
+
|
|
206
|
+
### ✨ 新功能
|
|
207
|
+
|
|
208
|
+
- **装完可以一键重启** —— 装完弹确认框「已装好 vX,重启后才会生效…现在重启?」,
|
|
209
|
+
默认高亮「是」(`/migrate` 用的同一套 `Confirm` 浮层,回车即走);确认后先落盘当前
|
|
210
|
+
对话再重启,新进程按常规启动即**续接上一轮会话**。拒绝也不拦人,照旧给「重启 ai 后
|
|
211
|
+
生效」的提示。
|
|
212
|
+
- **过程信息不再缺席** —— 依次报出「正在查询最新版本…」→「发现 vX(当前 vY),
|
|
213
|
+
开始安装…」→「✅ 已安装 …」;移交终端给 npm 之前,先在真实 TTY 上打一段头部:
|
|
214
|
+
装什么包、当前版本、`npm root -g` 得到的安装位置、以及实际执行的命令。
|
|
215
|
+
|
|
216
|
+
### 🐛 修复
|
|
217
|
+
|
|
218
|
+
- **「重启生效」原本是空头支票** —— 旧实现只 `push` 一句「重启 ai 后生效」,
|
|
219
|
+
`/update` 自己也从不重启。而 `exit({ restart: true })` 那条路(`/model`、`/lang` 在用)
|
|
220
|
+
只是同进程重挂载 `<Chat/>`,内存里还是旧模块图,对「磁盘上的代码变了」这种升级
|
|
221
|
+
等于没装。现在改为**真·重新 exec 自己**:`spawn` 新进程跑新 dist,父进程在挂起态
|
|
222
|
+
等它退出后跟着退。
|
|
223
|
+
- **`/update` 期间的转圈是「冻住」的** —— `readDistTags()` 用的是 `spawnSync`,
|
|
224
|
+
同步阻塞事件循环,Ink 的动画定时器跟着停摆 1–3 秒。改为异步 `spawn`,并加 20 秒
|
|
225
|
+
超时兜底(断网时不会再无限期挂着)。
|
|
226
|
+
- **转圈上的文案是「处理中…」** —— 这个文案来自生成阶段 `phaseLabel(session.phase)`,
|
|
227
|
+
而 `/update` 根本没有生成轮次,`phase` 为 `null` 就兜底成了通用的「处理中…」,
|
|
228
|
+
与正在发生的事毫无关系。新增 `busySpinnerLabel()`:有生成阶段仍由 SDK 阶段驱动,
|
|
229
|
+
阻塞型命令期间改显 `/update 执行中…`。
|
|
230
|
+
|
|
231
|
+
### 🔧 改进
|
|
232
|
+
|
|
233
|
+
- **查询与安装都加 `--prefer-online`** —— npm 会缓存 packument,刚发版时
|
|
234
|
+
`npm view` 可能仍拿到旧 dist-tags,把「有新版」误判成「已是最新」(发布当天踩过);
|
|
235
|
+
安装侧同理。另加 `--no-fund` 减噪。
|
|
236
|
+
|
|
237
|
+
### ⚠️ 两个容易踩的终端细节(写在代码注释里)
|
|
238
|
+
|
|
239
|
+
- **重启必须在挂起态里退出**:`suspendTerminal` 的 `beginSuspend()` 关 raw 模式,
|
|
240
|
+
`endSuspend()` 会把它重新打开;若等 `suspendTerminal` 返回后再 `process.exit`,
|
|
241
|
+
终端就停在 raw 模式,回到 shell 后按键全是乱码。
|
|
242
|
+
- **重启前要先 `disableMouseTracking()`**:鼠标上报由 `mount()` 按 Ink 实例开关,
|
|
243
|
+
而挂起期间实例并未退出 —— 不关的话,子进程若异常死亡,终端会一直把滚轮写成
|
|
244
|
+
转义序列。
|
|
245
|
+
|
|
246
|
+
### 📖 文档
|
|
247
|
+
|
|
248
|
+
- `README.md` / `README.zh.md` / `docs/{zh,en}/getting-started.md` —— `/update` 的描述
|
|
249
|
+
订正:原文写「全局安装 alpha 通道」,实际是在**全部** dist-tags 里挑 semver 最大者
|
|
250
|
+
(所以 alpha 比 `latest` 新时会装 alpha),并补上「逐步骤显示进度、装完可一键重启」。
|
|
251
|
+
|
|
252
|
+
### ✅ 测试
|
|
253
|
+
|
|
254
|
+
- 全量 **49 文件 / 657 例通过**(alpha.25 时 649 例),`tsc --noEmit` 0 错误。
|
|
255
|
+
- `/update` 用例 6 → 11 例:新增「确认后重启 + 重启前落盘」「拒绝重启则不落盘也不重启」
|
|
256
|
+
「新会话无用户消息不落盘」「三条过程通知按序上报」「终端头部含版本/位置/命令」
|
|
257
|
+
「查询带 `--prefer-online`」。
|
|
258
|
+
- 新增 `busySpinnerLabel` 3 例,含回归守卫:命令执行期间**不得**回落成「处理中…」。
|
|
259
|
+
|
|
260
|
+
## [1.0.0-alpha.25] - 2026-10-29
|
|
261
|
+
|
|
262
|
+
> 升级到 SDK `1.0.0-alpha.23`(面向模型与用户的文案全面英文化),
|
|
263
|
+
> 并补上缺失的 `LICENSE` 文件。
|
|
264
|
+
|
|
265
|
+
### 🔧 依赖
|
|
266
|
+
|
|
267
|
+
- `@ai-zen/agents-sdk` `1.0.0-alpha.22` → `1.0.0-alpha.23`。
|
|
268
|
+
|
|
269
|
+
### 💬 行为变化(来自 SDK)
|
|
270
|
+
|
|
271
|
+
- **SDK 内置文案全部改为英文** —— 工具 schema 描述、出厂系统提示词、注入语、
|
|
272
|
+
工具返回值、错误信息、出厂 Skill 与 catalog 数据一律固定英文,SDK 内不再有
|
|
273
|
+
任何中文文案,也**不引入 i18n**(理由:`tokenThresholds` / `maxToolOutput`
|
|
274
|
+
都是绝对量,工具 schema 换语言就是另一组 token 数,性能基准会失去单一变量)。
|
|
275
|
+
- **回复语言改为「跟随用户」** —— 出厂系统提示词里「请用中文回复」被替换为
|
|
276
|
+
`Always reply in the same language the user writes in.`,按每条消息自适应。
|
|
277
|
+
- **对 CLI 无破坏性影响** —— CLI 自带 `i18n` 字典(`src/i18n`),界面文案与
|
|
278
|
+
通知全部由 CLI 自己渲染,不解析 SDK 的文案字符串;对 SDK 的用法仅限于类型导入
|
|
279
|
+
与 `AgentRepository` / `ConfigManager` / `setLogger` / `HandoffService` 等结构性
|
|
280
|
+
API。49 文件 / 649 例测试在升级前后逐项一致。
|
|
281
|
+
|
|
282
|
+
### 📄 许可证
|
|
283
|
+
|
|
284
|
+
- **新增 `LICENSE`** —— 此前 `package.json` 声明了 `"license": "MIT"`,但仓库里
|
|
285
|
+
没有许可证正文,npm 包内自然也没有。现补上 MIT 全文(与 `agents` 仓库根
|
|
286
|
+
`LICENSE` 一致,`Copyright (c) 2024 LZQCN`)。
|
|
287
|
+
`files` 字段未改动:npm 对 `LICENSE` 强制包含,`npm pack` 已确认 tarball 内含该文件。
|
|
288
|
+
|
|
289
|
+
### 📖 文档
|
|
290
|
+
|
|
291
|
+
- `docs/{zh,en}/getting-started.md` —— 运行时依赖清单的 SDK 版本从 `1.0.0-alpha.18`
|
|
292
|
+
订正为 `1.0.0-alpha.23`,`@ai-zen/agents-core` 从 `^4.5.2` 订正为 `^4.5.3`
|
|
293
|
+
(与实际 `package.json` 对齐)。
|
|
294
|
+
- `docs/{zh,en}/index.md` —— 许可证一节改为指向仓库根的 `LICENSE` 文件,并说明全文
|
|
295
|
+
随包一同发布。
|
|
296
|
+
- `README.md` / `README.zh.md` —— 启动界面示例中的版本号同步为 `1.0.0-alpha.25`。
|
|
297
|
+
|
|
298
|
+
### ✅ 测试
|
|
299
|
+
|
|
300
|
+
- 全量 **49 文件 / 649 例通过**,`tsc --noEmit` 0 错误,与 alpha.24 基线一致
|
|
301
|
+
(本次仅升依赖,无源码改动)。
|
|
302
|
+
|
|
303
|
+
## [1.0.0-alpha.24] - 2026-10-28
|
|
304
|
+
|
|
305
|
+
> 三件事:**看不见的上下文**现在看得见了(不进上下文的消息划删除线),
|
|
306
|
+
> 升级有了 `/update`,中断策略从写死的常量变成可配置项。
|
|
307
|
+
|
|
308
|
+
### ✨ 新功能
|
|
309
|
+
|
|
310
|
+
- **不进上下文的消息划删除线** —— 判据与 core 的 `formatHistory()` 同源
|
|
311
|
+
(`status == undefined || Completed` 且非 `omit`),而策略早已在 `Agent.run()` 收尾时
|
|
312
|
+
结算进字段(`keep`→`Completed`、`drop`→`Aborted`,工具轮次 `fill`→`Completed`、
|
|
313
|
+
`drop`→`omit`),渲染层不重复推导策略。
|
|
314
|
+
- 流式中那条(`Pending` / `Writing`)不划线:它在 `formatHistory` 里同样不在,但那是
|
|
315
|
+
「尚未」而不是「被丢弃」。
|
|
316
|
+
- `hidden` 仍彻底不渲染;`omit` 不再让消息凭空消失(原先 `hidden || omit` 一起
|
|
317
|
+
`return []`,用户中断一轮工具调用后那几条会无声蒸发)。
|
|
318
|
+
- **`/update`** —— 装 registry 上最新的发布版:读 `npm view @ai-zen/cli dist-tags --json`,
|
|
319
|
+
取**所有 tag 里 semver 最大**的那个。只认 `latest` 会把跑 alpha 的用户降级(实际发布
|
|
320
|
+
状态就是 `latest 0.9.0 < alpha 1.0.0-alpha.23`);目标不比当前进程新则不装(本地开发版
|
|
321
|
+
不会被「升级」降级)。安装走 `suspendTerminal` + `stdio: inherit`,npm 的进度与报错直接
|
|
322
|
+
打在真实终端上;装完读 `npm root -g` 下的 `package.json` 判定版本(`npm view` 查 registry
|
|
323
|
+
有几分钟延迟),并提示重启(内存里还是旧代码,不自动重启以免丢会话)。
|
|
324
|
+
- **中断策略可配置** —— SDK 升级到 `1.0.0-alpha.22`:`AppConfig` 新增
|
|
325
|
+
`abortedMessageStrategy`(`keep` / `drop`,出厂 `keep`)与 `abortedToolRoundStrategy`
|
|
326
|
+
(`fill` / `drop`,出厂 `fill`),`createAgent()` 不传参数时直接读 `scope.config`。
|
|
327
|
+
配置中心「默认项」新增两项即可切换;它们只在**建 Agent** 时读取,所以关掉配置中心时
|
|
328
|
+
宿主会自动重建会话(`WizardCloseResult.sessionOptionsChanged`),不必再手敲 `/reload`。
|
|
329
|
+
|
|
330
|
+
### ♻️ 重构
|
|
331
|
+
|
|
332
|
+
- **CLI 不再写死 `abortedMessageStrategy: "keep"`** —— 写死会盖掉用户的选择。现在配置即
|
|
333
|
+
事实:`config.json` 里写什么就用什么,`agent-creator` 只负责建 Agent。
|
|
334
|
+
- **配置迁移补齐策略字段**(SDK 侧)—— 缺失或非法值一律写回出厂默认并落盘一次,各端不必
|
|
335
|
+
各自 `?? "keep"` 兜底。
|
|
336
|
+
- **新增 `src/version-compare.ts`** —— semver 比较(核心段 / 预发布标识 / 忽略 build),
|
|
337
|
+
供 `/update` 从多个 dist-tag 里挑最新;不为此引 `semver` 依赖。
|
|
338
|
+
|
|
339
|
+
### ✅ 测试
|
|
340
|
+
|
|
341
|
+
- 全量 **49 文件 / 649 例通过**(alpha.23 时 626 例)。
|
|
342
|
+
- 新增:`version-compare.test.ts` 10 例、`chat-commands-update.test.ts` 6 例(tag 选择、
|
|
343
|
+
已是最新不安装、安装失败、npm 缺失、dist-tags 查不到),`messageToLines` 删除线 3 例,
|
|
344
|
+
策略 setter / 向导两屏 / 宿主重载 4 例。
|
|
345
|
+
- 端到端实测(临时 `AI_ZEN_DIR`):出厂默认 `config: keep fill → agent: keep fill`,
|
|
346
|
+
改成 drop 并重建后 `config: drop drop → agent: drop drop`。
|
|
347
|
+
|
|
348
|
+
### 🔧 依赖
|
|
349
|
+
|
|
350
|
+
- `@ai-zen/agents-sdk` `1.0.0-alpha.21` → `1.0.0-alpha.22`。
|
|
351
|
+
|
|
352
|
+
## [1.0.0-alpha.23] - 2026-10-28
|
|
353
|
+
|
|
354
|
+
> 子 Agent 从「通知栏里报一句运行中」升级为一条**可切换的栏目**:主 / 子 Agent
|
|
355
|
+
> 各自的消息分栏展示,执行状态内联在标签上。
|
|
356
|
+
|
|
357
|
+
### ✨ 新功能
|
|
358
|
+
|
|
359
|
+
- **主子 Agent 切换栏目** —— 对话中出现子 Agent 时,消息列表下方多出一条栏目
|
|
360
|
+
`主AGENT ◈子AGENT1 ◈子AGENT2`,当前项背景色高亮;`←` / `→` 切换后,消息列表
|
|
361
|
+
即换成该 Agent 自己的对话。
|
|
362
|
+
- **数据源**:core 的 `onSubAgentStart` 钩子给出子 Agent 实例;`AgentTool` /
|
|
363
|
+
`AgentToolLazy` 在委派结束后**不销毁**实例,`messages` 始终可读——所以只要存下
|
|
364
|
+
引用,就能回看子 Agent 的完整过程(思考、工具调用、多轮推理),而不只是最终结果。
|
|
365
|
+
- **生命周期与内循环对齐**:子 Agent 是内循环里工具调用的产物,本轮内循环全部结束时
|
|
366
|
+
(`send` 收尾;异常 / 中止走 `finally`,同样清)整条栏目清空,展示序号归零。
|
|
367
|
+
- **执行状态内联**:`◈` + 颜色(运行中蓝、完成绿、失败红),运行中的蓝点每 500ms
|
|
368
|
+
在亮 / 暗之间闪烁。**不再**往通知栏推「子任务运行中 / 已完成」——那会让通知栏
|
|
369
|
+
凭空多出一行,整帧高度跟着跳。
|
|
370
|
+
- **`←` / `→` 按状态分流**:输入框为空(光标停在 0,左右键本无事可做)或生成中
|
|
371
|
+
(输入框已被 Spinner 顶替)时切换栏目;输入框有内容时仍是移光标,不牺牲任何编辑能力。
|
|
372
|
+
- 发送消息时自动切回主 Agent 视图(否则用户看不到自己的提问)。
|
|
373
|
+
|
|
374
|
+
### 🐛 修复
|
|
375
|
+
|
|
376
|
+
- **`/new`、`/reload` 后子 Agent 残留** —— 这两个操作只换 `agent`、不重建 `ChatSession`
|
|
377
|
+
实例,子 Agent 视图不会自然消失;现随 `reset` / `reload` / `migrate` 显式清空。
|
|
378
|
+
- **视图 id 与展示序号混用** —— 序号每轮归零,若 id 也跟着归零,下一轮的 `sub-1` 会与
|
|
379
|
+
上一轮同名,而选中态按 id 匹配,残留的选中 id 会「接上」新出现的子 Agent。现拆成
|
|
380
|
+
两个计数器:id 永久递增、只让展示序号归零。
|
|
381
|
+
|
|
382
|
+
### ♻️ 重构
|
|
383
|
+
|
|
384
|
+
- **修正 `sub-agent-guard-plugin` 的过时注释** —— 原注释称 `call_skill_sub_agent`
|
|
385
|
+
「不分发 onSubAgentStart、暂不覆盖」,但 skill 子 Agent 统一到 `AgentToolLazy` 之后
|
|
386
|
+
它其实是被覆盖的(core 只有两条委派路径,均分发该钩子)。这条错注释在本次实现
|
|
387
|
+
前期直接误导了对覆盖面的判断。
|
|
388
|
+
|
|
389
|
+
### ✅ 测试
|
|
390
|
+
|
|
391
|
+
- 全量 **47 文件 / 626 例通过**(发布 alpha.22 时 604 例)。
|
|
392
|
+
- 新增护栏:栏目条目顺序与 1 空格 padding、符号 `◈` 与三色映射、超宽仍单行、
|
|
393
|
+
按序编号、结束转 `done`、出错转 `failed` 且不被 `end` 覆盖、进度不进通知栏、
|
|
394
|
+
执行期间有内容而收尾后清空、`reset` / `reload` 也清空、`←`/`→` 切换数据源与
|
|
395
|
+
输入非空时不切换、布局高度与 IME 偏移。
|
|
396
|
+
- 破坏性验证:去掉 `computeChatLayout` 里的 `agentTabLines` → 布局用例红
|
|
397
|
+
(`expected 4 to be 5`);命令说明写长 → 宽度护栏红(`expected 62 to be <= 36`)。
|
|
398
|
+
|
|
399
|
+
## [1.0.0-alpha.22] - 2026-10-28
|
|
400
|
+
|
|
401
|
+
> 这一版是**整体代码评审 + 治本修复**的成果:先通读 `src/` 全部非测试代码做全量评审
|
|
402
|
+
> (P0~P3 共 30 项,其中 3 项经实测查证为非问题、未改代码),再按「能查根因就查、
|
|
403
|
+
> 不加兜底/不吞异常/不做多余校验」的原则逐项落地,最后补上 Ctrl+C 中断交接。
|
|
404
|
+
|
|
405
|
+
### 🐛 修复
|
|
406
|
+
|
|
407
|
+
- **折行按字素簇计宽,修掉 emoji 撑破整帧布局(P0)** —— `toCells` / `wrapSpans` / `clipSpans` / `toChars` / `segmentsWidth` 原先按 **code point** 遍历文本:VS16 序列(`✌️` = `U+270C U+FE0F`)被算成 1 列而实际占 2 列 → 行超宽 → Ink 在渲染期二次折行 → 实际帧高比 `computeChatLayout` 预算多一行 → **输入框被顶出屏幕**;ZWJ 家族(`👨👩👧`)被算成 6 列而实际占 2 列 → 每两个簇就折一次。现在全部改走 `graphemes()` + `clusterWidth()`,与 Ink 排版同一把尺子。
|
|
408
|
+
- **阈值写入不再整对象替换** —— `updateModel` 是浅合并,此前调用方直接构造 `{ handoff, guard }`,会把 `tokenThresholds` 里其它字段(SDK 后续新增的压缩阈值等)一并清空。新增 `mergeTokenThresholds()`,所有阈值写入走它。
|
|
409
|
+
- **`/migrate` 之后屏幕一片空白** —— 迁移用 `prune` 策略把 `messages` 砍到 1~2 条,但 `transcriptFrom` 仍停在旧值,`messages.slice(transcriptFrom)` 恒为空:迁移成功、上下文有内容,屏幕上却什么都没有。现在迁移成功后重置转录显示起点(同 `/new` 的做法)。
|
|
410
|
+
- **通知栏只剩最后一条** —— 订阅端是单值 state,同一批摘要 / 分阶段进度会被逐条覆盖。改为保留最近 4 条的队列(`MAX_NOTICE_HISTORY`),并让同一批摘要合成**一条多行**,既全部显示又只占一个槽位;排版决策(折行 / 多条让位 / 条内截断)收进 `notice.ts` 纯逻辑,新增可独立渲染的 `NoticeBar`。
|
|
411
|
+
- **`ai hook --help` 以非零码退出** —— 「想看用法」与「写错了子动作」此前混为一谈。现在 `ai hook` / `ai hook --help` 正常输出、退出码 0;`ai hook boom` 报错并 exit 1。`ai help` / `ai version` 单词形式等价于对应选项,但**只在它是唯一参数时**判定(`ai help me refactor this` 仍是在提问)。
|
|
412
|
+
- **`ai hook install` 漏清重复块** —— rc 里可能残留多个钩子块(老版本 + 新版本各写一次),旧实现只看第一块,于是「第一块恰好是最新版 → 判定已安装 → 后面的旧块永远留着」。改为取全部块,只有「恰好一块且内容一致」才算已安装。
|
|
413
|
+
- **会话落盘不是原子写** —— SDK 的 `EntityRepository.write()` 是直接覆盖,进程在写一半时被杀(Ctrl+C / OOM / 断电)就留下半截 JSON,`read()` 解析失败返回 null,**整段历史就此丢失**(会话文件可达数 MB,写入窗口并不小)。改为「写 `<id>.json.tmp` → `rename`」,任何时刻读到的要么是旧文件、要么是完整新文件。
|
|
414
|
+
- **Ctrl+C 无法中断会话交接** —— 两处根因:`summary` 模式的独立模型请求没有任何取消点;`self` 模式虽能被 `agent.abort()` 打断,但 abort 不抛错,中止后被误判成「模型没写出文档」,于是**重试一轮、再降级 summary 再跑一轮**——用户以为取消了,实际多烧两轮全量输入。现在交接接受 `AbortSignal`:取消时抛 `HandoffAbortedError`、**不降级不重试**、清掉写了一半的文档、`migrate` 也不改动会话历史;TUI 里 Ctrl+C 会先走交接的取消通道,提示「已取消」而不是报错。(依赖 `@ai-zen/agents-sdk` 同名改动)
|
|
415
|
+
- **通知栏文案整体右移 2 列** —— 缩进被加了两遍:`NoticeBar` 的 `paddingX={1}` 一层,`notice.ts` 又硬拼 ` ` 一层,于是文案比同屏的分隔线与 `❯` 输入行多缩 2 列;迁移一次推 4 条通知时最刺眼。现在横向定位只归容器管,逻辑层只管折行。顺带去掉 `· ` / `✖ ` 前缀——语气已由 `tone` 着色表达(error 走红色),符号纯属占位;多行通知每行都从行首开始。配置更新的子项摘要改为「一个汉字宽度」(2 列)缩进,不再带项目符号。
|
|
416
|
+
|
|
417
|
+
### ✨ 新功能
|
|
418
|
+
|
|
419
|
+
- **`/load` 列表显示会话摘要** —— 会话 id 是时间戳(`chat_2026_10_28_00_56_34`),直接当标签列出来完全分不清哪条是哪条。现在取首条用户消息的第一行作为标签。实现上先只 `stat`、排序后**只为将要显示的那几条**解析内容,会话攒到上百条时 `/load` 也不会被拖慢。
|
|
420
|
+
|
|
421
|
+
### ♻️ 重构
|
|
422
|
+
|
|
423
|
+
- **`chat.tsx` 五步拆 hook:1085 → 830 行** —— 只留会话生命周期与渲染组装,其余拆成 5 个可单测的 hook:`use-chat-viewport`(视口与滚动换算,所有动作经 `clampViewTop()`)、`use-overlays`(确认框 / 选择器的 resolve 生命周期,关状态与结算在同一次函数式更新里完成)、`use-wizard-host`(向导宿主,依赖注入)、`use-input-editing`(文本 / 光标 / 历史 / kill ring / undo / stash)、`common-keys`(两个 `useInput` 共用的键位 + Ctrl+D 双击退出)。
|
|
424
|
+
- **斜杠命令改为表驱动** —— 557 行的 `switch` 换成 `HANDLERS: Record<CommandName, CommandHandler>` + 18 个处理器:「新增命令忘了写处理器」从运行时静默落到「未知命令」变成**编译错误**;命令名解析只剩 registry 一处(`parseCommandInput` / `parseCommandArgs` 共用同一套切分),`blocksInput` 成为命令元数据而不再是宿主白名单。
|
|
425
|
+
- **向导字段工厂去噪** —— `commit` / `toggle` 签名放宽为 `string | null | Promise<string | null>`,删掉 23 处 `Promise.resolve(...)` 噪音;`nameField()` / `endpointIdField()` / `customPatch()` 等收掉 8 处重复段;`DetailFieldKind` 的 `"map"` 改名 `"open"`(语义本就是「按 Enter 进子屏」)。
|
|
426
|
+
- **会话落盘收敛为唯一入口** —— 「读旧记录拿 `createdAt` → 写 → 更新指针」原先在 5 个地方各写一遍并且已经漂移(`saveConversation()` 漏写 `cwd`、stdio 的 `--save` 不更新指针、还写了一个已废弃的 `modelId`)。收敛到 `persistConversation()`,各处差异只剩「是否更新指针」一个显式开关。
|
|
427
|
+
- **初始化与单例改为缓存 Promise** —— `getScope()` / `ensureConfigDir()` 缓存实例时,`if (x) return x` 之后到赋值之间有一段 `await`,并发调用会一起穿透(建出两个 Scope → MCP 双连接、双份工具发现)。改缓存 Promise 让并发调用共享同一次初始化,失败不缓存。
|
|
428
|
+
- **删死代码与不可达兜底** —— 死字段 `ConversationContext.running|input|shouldSend`、从未启用的 i18n 复数机制(`pluralRules` + `key_one/_other`)、`noticeLines()` 末尾不可达的截断兜底(穷举 84 组证明从不执行)、`pick(direct, …)` 从没人传过的参数、`useChatViewport` 零引用的 `scrollTo` 导出、所有命令都空转一遍的 `parseHandoffMode`;`cwd` / `previewLimit` 从「可选 + 内部兜底」收成必填,兜底只保留在 SDK 可选字段的真实边界上。
|
|
429
|
+
- **命令说明文案精简** —— 斜杠菜单与 `/help` 都是一行一条,说明过长会把整行塞满。按「只写做什么、不写实现细节」重写:砍掉内部概念(`scope`、写入配置、重建会话)与冗余限定词,括号只留用户必须预期的后果(替换会话 / 重建 / 不切换会话)。中文最长 59 → **34**、英文 73 → **49**(平均 19.3 / 33.8),`/mcp` 从全场最长退到中游。
|
|
430
|
+
- **权限四维上屏走词条** —— `tools` / `skills` / `mcps` / `subagents` 是配置里的键,直接上屏等于把内部标识暴露给用户,映射到 `perm.*` 词条。
|
|
431
|
+
|
|
432
|
+
### ⚡ 性能
|
|
433
|
+
|
|
434
|
+
- **`graphemes()` 加快速路径** —— 折行是每帧热路径,但绝大多数文本(ASCII / CJK / 代码)每一簇就是一个 code point,不需要 `Intl.Segmenter`。先用一次正则判定「是否可能出现多 code point 合为一簇」(astral / CRLF / ZWNJ·ZWJ / 韩文组合字母 / `\p{M}` 组合附加符),命中才走分词器。一帧 60 行中文:**655 µs → 52 µs**(省 604 µs,帧预算 1%)。判定条件宁可误报不可漏报,新增 6 例护栏覆盖各类形态。
|
|
435
|
+
|
|
436
|
+
### 🔧 依赖
|
|
437
|
+
|
|
438
|
+
- 升级 `@ai-zen/agents-sdk` 至 `1.0.0-alpha.21`(交接取消能力)。
|
|
439
|
+
|
|
440
|
+
### ✅ 测试
|
|
441
|
+
|
|
442
|
+
- 全量 **46 文件 / 604 例通过**(评审前 36 文件 / 487 例,净增 10 文件 / 117 例护栏)。
|
|
443
|
+
- 重点新增:`use-chat-viewport` 7 例、`use-overlays` 6 例、`use-wizard-host` 9 例、`use-input-editing` 13 例、`common-keys` 10 例、`chat.test.tsx` 7 例集成护栏、`lines.test.ts` 12 例(字素簇折行)、`notice-bar` 4 例、`conversation-persist` 10 例(含 spy `fs.rename` 钉住原子写)。
|
|
444
|
+
- 破坏性验证(每项都确认护栏会红):折行改回 code point、`clampCursor` 改 `Array.from`、翻页步长改 `budget`、删掉 PgUp 分支、去掉 `\p{M}`、原子写改回直写、`clearDraft` 去掉历史游标复位。
|
|
445
|
+
|
|
446
|
+
## [1.0.0-alpha.21] - 2026-10-27
|
|
447
|
+
|
|
448
|
+
### ✨ 新功能
|
|
449
|
+
|
|
450
|
+
- **状态栏改为三个信息块,并统计生成速率** —— 此前状态栏是一条按优先级排列的文本流,窄屏折行时会把 `本轮 1284 tok` 这类单元从中间劈开,也让「哪段信息属于一类」看不出来。
|
|
451
|
+
- 现在分成三块:① 模型 · Agent · 思考强度 ② 本轮 / 累计 token 用量 · 生成速率 ③ 快捷键提示(垫底)。
|
|
452
|
+
- **换行以块为单位**:新增 `packSegments()`(`src/tui/status-segments.ts`)——放得下就并排(` · ` 连接),放不下就把**整块**挪到下一行;只有单块自身超过一整行时才退化成块内折行(优先空格断、CJK 硬断、行首孤儿 `·` 丢弃、行尾空格裁掉)。行边界只会落在块边界上。
|
|
453
|
+
- **行数确定**:`statusBarRows()` 在纯逻辑里量出实际行数并交给 `computeChatLayout({ statusBarLines })`,测量与渲染是同一个函数——窄屏下状态栏变高不会把输入框顶出屏幕。
|
|
454
|
+
- 数字与单位用 NBSP 绑定(`本轮␣1284␣tok`),块内也不会被拆成两行。
|
|
455
|
+
- **生成速率两口径,不混成一个数**:生成中 `⚡ 47 tok/s` 是估算值(按输出增量折算,CJK 0.7 / 字、其它非空白 0.25 / 字符、空白 0.1,取最近 2 秒滑动窗口);一轮结束后改用 `⚡ 均 38 tok/s`,分母是「首字 → 末字」跨度,分子是 API 返回的真实 `usage.completion_tokens`(多轮工具调用逐轮累加)。跨度 < 400ms 不显示,避免首包爆发把速率放成几百。
|
|
456
|
+
- **新增 `/agent`:会话内快速切换主 Agent** —— 选择器列出 `~/.ai-zen/agents/*.json` 的全部主 Agent,标注当前项与其绑定的模型名(换 Agent 等于连模型一起换)。
|
|
457
|
+
- 支持 `/agent <id>` 直切;无参弹选择器。
|
|
458
|
+
- 切换前做完整链路预检:Agent 存在 → 绑定了模型 → 模型还在配置里 → 端点在 → 端点有 Key。跑不起来的标「不可用」并给出原因,而不是切过去之后卡在启动错误屏。
|
|
459
|
+
- 写入 `config.defaultAgent`(与 `/config` →「默认 Agent」同一字段),随后重建会话;**消息与上下文保留**。不改 Agent 定义本身(提示词 / 权限 / `modelParams` 一概不碰)。
|
|
460
|
+
- **新增 `/mcp`:直达 MCP 管理界面** —— 不另建一套编辑器,直接把配置中心里已有的 MCP 屏打开(增 / 删 / 改 / 启停 / 全局⇄项目作用域切换都在那一屏)。关闭向导后沿用既有 `mcpChanged` 路径自动 `session.reload()`,新服务器当场生效。
|
|
461
|
+
- 默认落点:项目级有服务器就进 `project`,否则进 `global`(项目级为空时进去只会看到空列表)。`/mcp global`、`/mcp project`(可缩写 `g` / `p`)可指定。
|
|
462
|
+
- **新增 `/reload`:刷新运行环境** —— `session.reload()` 内含 `resetScope()`(断开旧 MCP 连接、丢掉配置快照)→ 重建 Agent → 重新发现工具 / Skill / MCP。手改 `mcp.json` / `skills/` / `tools/` 后一条命令生效,**消息与上下文保留**,无需重启 CLI。
|
|
463
|
+
- **中断时保留已输出的思考与内容** —— 按 `Ctrl+C` / `Esc` 停下后,模型**已经吐出来的那半句话不再凭空消失**。此前中断只是把在途消息标成 `Aborted`,而历史过滤器只放行 `completed`,于是屏幕上还留着的思考与回答在下一轮请求里整条不见,模型下一轮像失忆一样重来。
|
|
464
|
+
- 根因在上游:core 的 `abort()` 只管打标记、`formatHistory()` 只按状态过滤,两者都不关心「已经输出了什么」。core `4.5.3` 新增 `abortedMessageStrategy`(`drop` / `keep`,默认 `drop`),并导出 `reconcileAbortedMessages()`,在工具轮次归一**之后**收尾纯内容消息。
|
|
465
|
+
- CLI 显式设成 `keep`:确有输出的被中断 assistant 会被恢复为 `completed` 进入上下文。**空输出不保留**(避免空消息污染上下文)、**带工具调用的不归它管**(仍走「整轮进出」,否则会出现「有调用无结果」的非法历史)。
|
|
466
|
+
- SDK 依赖随之升级到 `1.0.0-alpha.20`(`createAgent` 新增第三参 `CreateAgentOptions`,顺带把此前完全没暴露的 `abortedToolRoundStrategy` / `abortedToolRoundReason` 一起接出来),core 升到 `4.5.3`。
|
|
467
|
+
- **界面国际化(中文 / English)** —— 首次启动按系统语言识别(`LC_ALL` → `LC_MESSAGES` → `LANG` → Node 的 `Intl` locale,**兜底英文**),识别结果写入 `~/.ai-zen/cli/preferences.json`,之后以偏好为准(改了系统语言也不会让界面语言悄悄漂移)。`/lang` 可随时切换,`/lang zh` / `/lang en` 直接指定。
|
|
468
|
+
- 词典编译进产物(`src/i18n/{zh,en}.ts` + 配置中心专区 `{zh,en}-wizard.ts`):key 扁平化,`{n}` 插值;切换语言只换一个引用,不重读磁盘、不重启。缺词条时返回 key 本身(不做跨语言静默回退,否则英文界面里会突然冒出中文)。`t()` 支持 `_one` / `_other` 复数变体(由 `Intl.PluralRules` 选),但 `i18n/index.test.ts` 要求**中英 key 集合完全一致**,故现有词条统一用 `(s)` 写法,不开变体。
|
|
469
|
+
- 语言偏好放 CLI 独立的 `preferences.json`,**不塞进共享的 `config.json`** —— 那是 CLI 与 Desktop 共用的 `AppConfig` 契约,塞进去会被桌面端当未知字段丢掉。
|
|
470
|
+
- 覆盖范围是**整套 TUI 界面**:主对话框(状态栏三块、输入占位与提示、通知、思考档位标签、生成中提示)、命令层(`/help`、斜杠菜单、各命令的选择器与反馈)、配置中心全部屏幕(端点 / 模型 / 图片模型 / MCP / Agent 定义 / 权限 / 默认项 / 工具输出上限 / 新建引导 / KV 与多行文本编辑器)、交接方式选择器与确认框、启动屏与启动错误屏,以及业务模块返回的 summary(`/model` `/agent` `/think` 的切换结果、交接进度与降级提示)。
|
|
471
|
+
- `COMMAND_REGISTRY` 里存的是 **i18n key 而不是文案** —— 它是纯数据模块、加载时机早于语言初始化,存文案会把「加载那一刻的语言」定死;渲染时由 `getCommandHints()` 现取。启动屏的 `TAGLINE` 常量同理改成了 `tagline()` 函数。
|
|
472
|
+
- **管道模式不做 i18n**:`ai <提示词>` / 管道输入的所有输出与报错**一律英文**(它是给脚本用的,不应随人的界面语言变),故 stdio 路径**不初始化语言**,行为与 `preferences.json` 彻底解耦。
|
|
473
|
+
- **`ai --help` 与 `ai hook install / uninstall` 走 i18n**:两者都在 TUI 之外打印,因此在各自分支里先 `initLocale()` 再取词条;`HELP_TEXT` 那个模块级常量相应改成 `helpText()` 现拼(常量会把加载那一刻的语言定死)。
|
|
474
|
+
- **会话 id 改名**:`对话_2025年07月01日14时30分00秒` → `chat_2026_10_28_00_56_34`(英文前缀 + 全下划线时间戳,中英一致、文件名安全)。它同时是文件名,跟着语言变会让历史文件与新文件混排,故时间戳由新增的 `formatFileStamp()` 生成;而**显示用**的 `formatShortTime()` 反而改为随语言切换。管道模式 `--save` 不再自己拼名字,改为调用 `generateConversationId()`,两处重复拼接一并消掉。
|
|
475
|
+
- **注入历史的「工作目录」提示改为英文并显式标记**:`[system-injected: working directory changed — not written by the user]` —— 翻存档或交接文档时一眼能分辨哪些内容不是用户敲的;`stripCwdNote()` 同时认新旧两代格式,避免旧存档把这段笔记渲染到屏幕上。
|
|
476
|
+
- 新建 Sub-agent 时写进 tool schema 的 `task` 参数描述也走词典(中 `完整的任务描述` / 英 `Full task description`)—— 这是唯一一处「协议内容也翻译」的例外,代价是同一份定义在不同界面语言下创建会得到不同的 schema 文本。
|
|
477
|
+
- 唯一不翻的注入内容就是上述工作目录提示;`hook.ts` 写进 rc 文件的三个代码块标记串(`# === ai hook (开始) ===` 等)也保持原样 —— 翻了就再也找不到旧块。
|
|
478
|
+
- 时间格式随语言切换 dayjs locale 与 pattern:中文 `7月1日 14:30` / `昨天 14:30`,英文 `Jul 1, 14:30` / `Yesterday 14:30`。
|
|
479
|
+
- 顺带排掉两个隐藏地雷:状态栏曾用**文案**判断思考强度是否置灰(`effort === "关"`),英文界面下永远不成立,改为档位语义的 `effortDimmed()`;输入区占位提示曾硬编码在 `inputLines()` 里(渲染层却走 `t()`),英文界面下量输入行高度会短一行、连带硬件光标 y 偏移,现在两处同源。
|
|
480
|
+
|
|
481
|
+
### 🔧 行为变更
|
|
482
|
+
|
|
483
|
+
- **消息栏的「思考中…」拆成六态:连接中 / 排队中 / 思考中 / 调用工具 / 等待工具返回 / 输出中** —— 原先那行只有「思考中…」一个词,但一轮对话里模型其实走过几个阶段,用同一个词概括在多数时候是**说错话**:等首 token 时并没有在思考,工具执行时模型根本没在跑。
|
|
484
|
+
- 判据只用**事件与 delta 的形状**(`src/tui/phase.ts`,纯逻辑可单测):`connecting` = 请求发出到响应头到达;`queued` = `open` 之后、首个内容 chunk 之前;`thinking` = 收到 `reasoning_content`;`tool-calling` = 收到 `tool_calls` / `function_call`(带工具名,后续只带参数分片时沿用首个 delta 里的名字);`tool-waiting` = `parsed` 且本轮带工具调用;`streaming` = 收到 `content`。一个 chunk 里同时出现时优先级是 tool_calls > content > reasoning。
|
|
485
|
+
- 「连接中」这一段靠新增的 `PhasePlugin`(`chat-session.ts`)提供:SDK 的 `open` 事件是在 `chat.completions.create()` **await 返回之后**才发的,只代表响应头到了,之前的 DNS / TCP / TLS / 服务端受理全静默。
|
|
486
|
+
- **子 Agent 的输出不推进主阶段**:委派期间主 Agent 正在等工具返回,若把子 Agent 的 delta 也算进主阶段,界面会跳成「输出中」,正好盖掉用户最需要知道的那句话。子 Agent 的 chunk 仍计入速率、仍触发重绘。
|
|
487
|
+
- spinner 那一行加了 `truncate`:busy 时输入区高度被 `computeChatLayout` 固定为 1 行(`inputHeight = 1`),标签一旦折行就会把输入框顶出屏幕。
|
|
488
|
+
- **状态栏去掉「⏳ 生成中」** —— 本轮到底在干什么已由消息栏 spinner 逐阶段说明,状态栏再挂一个笼统的「生成中」只是重复占宽度(窄屏下还把快捷键提示挤到下一行)。
|
|
489
|
+
|
|
490
|
+
- **shell 钩子改为进入 TUI(此前的管道模式是实现错误)** —— 钩子装在 `command_not_found_handle` / `command_not_found_handler` 上,触发场景是「人坐在交互终端前把命令敲错了」,但函数体写的是 `ai "$@"`,而 `decideMode()` 的规则是「有位置参数 → stdio」,于是钩子会话必然落到管道模式:吐一段文字到 stdout 就退出,**没有历史、不能追问**。`mode.ts` 头顶那张分类表把「脚本调用」与「人手动触发」混成了一个判据,所以这不是补丁能修的,是分类本身错了。
|
|
491
|
+
- 函数体现在是 `ai --tui --new "$@"`:新增 `--tui` 标志显式覆盖「有参数 → stdio」的判定,进 TUI 并把原命令**作为首条消息自动发送**(不预填到输入框——钩子的语义就是「这条命令跑不了,你帮我处理」,再让人敲一次回车没有意义)。
|
|
492
|
+
- 首条消息**强制当普通用户消息**发送,不经斜杠命令解析:敲错的可能是 `/opt/bin/foo` 这种以 `/` 开头的命令,否则会被当成 CLI 命令而报「未知命令」。`runSubmit()` 为此多了 `asMessage` 选项。
|
|
493
|
+
- `--tui` 在非 TTY 环境(脚本 / 管道)报错退出(退出码 2、英文提示),**不**降级成管道模式 —— 否则整屏界面内容会被写进管道。
|
|
494
|
+
- 首条消息只在首次挂载时发送:切模型 / `/lang` 等引起的重挂载不会重发。
|
|
495
|
+
- **钩子会话总是新开会话**:函数体多一个 `--new`(`ai --tui --new "$@"`)。否则敲错一次命令,那条命令就被追加进该目录原有的长对话里,上下文与会话名一起被搅乱。`--new` 是通用标志,手动用同样有效:`ai --new` 跳过 `last-session.json` 指针直接新建;它**只作用于 TUI 路径**,出现在 stdio 路径上时静默忽略(一次 stdio 调用本就是一次性会话,不存在续接)。
|
|
496
|
+
- **`ai hook install` 现在会自我升级**:此前只要 rc 文件里存在 `MARKER_START` 就打印「已安装」直接返回,于是函数体改过之后(比如本次加了 `--new`)老用户永远升不上去。现在会把已安装块与当前版本归一化比对(去空行、逐行 trim),不一致就原地覆盖并提示「已更新到最新版本」(新增词条 `hook.updated`)。
|
|
497
|
+
|
|
498
|
+
- **`/migration` 不再是 `/migrate` 的别名** —— 只保留 `/migrate`,`/migration` 现在报「未知命令」。registry、命令执行器、`blocksInput` 判定与四份文档同步。
|
|
499
|
+
- **SDK 全局日志默认写入 `~/.ai-zen/logs/sdk.log`** —— 以前默认**丢弃**,只有 `AIZ_DEBUG_SDK=1` 才落盘到 `/tmp`。但 SDK 的 `warn` / `error` 承载「MCP 连不上」「Skill 解析失败」这类关键诊断,默认丢弃等于故障无痕。现在默认落盘(单文件 2MB 封顶后轮转为 `sdk.log.1`),`AIZ_DEBUG_SDK=0` 可彻底关掉,`AIZ_SDK_LOG_FILE` 可改路径。界面上依旧一个字都不显示。
|
|
500
|
+
|
|
501
|
+
### 🐛 修复
|
|
502
|
+
|
|
503
|
+
- **修复 IME(输入法)候选框「有时候定位不准」** —— 硬件光标位置以前靠 `displayWidth()` 自己算。但宽度是个陷阱:Ink 排版时用 `string-width`,我们手写的表在若干字符上与它不一致(`⚡` `✅` `✨` `⭐` 我们算 1 列、Ink 算 2 列;ZWJ 组合 emoji `👨👩👧` 我们算 8 列、Ink 算 2 列;keycap `1️⃣` 我们算 3、Ink 算 2),输入里一出现这类字符,x 就会偏。现在改为**渲染后实测**:
|
|
504
|
+
- 光标所在字符包进独立 `<Box ref>`(行内嵌套的 `<Text>` 片段在 yoga 里没有节点,`measureElement()` 对它返回 `null`,实测),用 Ink 7 的 `measureElement()` 读它拓印到帧内的绝对坐标 —— 宽度由渲染器自己算,不再经过我们的模型。
|
|
505
|
+
- 拆行后原本整行 `truncate` 的兜底会丢:一旦内容超宽(如宽度模型把 `⚡` 算成 1 列)这行会被顶成两行,整帧高一行、底部被顶出屏幕。解法是把溢出交给**可截断的邻段**吸收——`before` / `after` 段 `flexShrink={1} minWidth={0}` + `wrap="truncate"`,光标块 `flexShrink={0}`;实测容器 40 列、`before` 60 列时仍是**一行**。
|
|
506
|
+
- 测量只能在渲染后(`useEffect`)做,而 Ink 的 `setCursorPosition` 只在 commit 阶段(`useInsertionEffect`)推送,所以存在一次 commit 的延迟:首次渲染先用旧手算值顶一帧,下一帧被实测值纠正(多一趟渲染,Ink 自带节流)。
|
|
507
|
+
- 占位提示行(未输入任何内容)不走实测,仍用前缀后的固定位置。
|
|
508
|
+
- **占位提示态不再自绘「假光标」** —— 输入框为空时会显示灰字提示「输入消息,/ 查看命令…」,末尾原本还画了一个 `▏`。但硬件光标这时就停在提示语开头(`x = 前缀宽`),终端自己会把它显示出来,再画一个方块等于屏幕上两个光标,而且位置还在提示语末尾,反而误导。现在占位态只留提示文字。
|
|
509
|
+
- **修复「输入 emoji 后反显光标消失 / 真实光标错位」** —— 根因有两个,都是把「字符」当成了 code point:
|
|
510
|
+
- **光标停在字素簇中间**:`✌️` 是 `U+270C U+FE0F` **两个 code point、一个字素簇**(`👨👩👧` 五个、`🇨🇳` 两个区域指示符)。按 code point 切分时,光标能停在 `✌` 与 `U+FE0F` 之间,于是反显的是一串**变体选择符** —— Ink 的输出里反显段直接消失(实测:`at=U+FE0F` 时帧里既无反显转义也没字符)。同时退格会只删掉半个 emoji、留下孤立的 `U+FE0F`。现在全部光标运算(折行、移动、删除、插入、撤销)改用 `Intl.Segmenter` 的**字素簇**为单位(`graphemes()`,Node 内置无需依赖)。
|
|
511
|
+
- **宽度表换成 Ink 自己的尺子**:`displayWidth()` 不再用手写的 `isWide` 区间表,直接调用 **`string-width`**(Ink 排版用的就是它,见 `ink/build/output.js`);单簇宽度带缓存,折行热路径几乎零成本。从此折行、状态栏装行、光标列号与渲染器同源 —— 状态栏那条「预留 1 列余量」也降级为纯安全边际。
|
|
512
|
+
- **光标单位改成字素簇**:`✌️` 是 `U+270C U+FE0F` **两个 code point、一个字素簇**(`👨👩👧` 五个、`🇨🇳` 两个区域指示符)。按 code point 切分时,光标能停在 `✌` 与 `U+FE0F` 之间,于是反显的是一串**变体选择符** —— Ink 的输出里反显段直接消失(实测:`at=U+FE0F` 时帧里既无反显转义也没字符)。同时退格会只删掉半个 emoji、留下孤立的 `U+FE0F`。现在全部光标运算(折行、移动、删除、插入、撤销)改用 `Intl.Segmenter` 的**字素簇**为单位(`graphemes()`,Node 内置无需依赖)。
|
|
513
|
+
- **新增直接依赖** `string-width@^8.3.0`(纯 ESM,本就在 Ink 的依赖树里,体积无变化)。
|
|
514
|
+
- **通知栏行数未计入光标 y** —— 渲染顺序是「对话区 → 通知栏 → 斜杠菜单 → 分隔线 → 输入行」,而 y 公式漏了通知栏一段。于是任何通知出现时硬件光标都会整体上浮那么多行;`/help` 一次塞最多 8 行通知,候选框能浮进对话区。现已补上 `noticeCount`(现作为实测未就绪时的兜底算法保留)。
|
|
515
|
+
- **修复 SDK 日志「接管了却照样糊界面」** —— 加载 MCP 时 `[load_mcp]` 连同整份 tools / resources 清单直接砸在渲染区外面。原因是 SDK 有 4 个模块在**模块顶层**执行 `const log = getLogger()`:模块求值那一刻就把 `console.log` 的函数引用冻进了常量,`setLogger()` 事后只替换内部变量,改不动已经抓走的引用;ESM 的静态 import 又保证这些模块在 `runTui()` 之前就已求值 —— 于是 CLI 无论怎么接管都会漏。
|
|
516
|
+
- 修在 SDK 侧(不改 CLI 侧骗 console 的 workaround):4 处全部改为**运行时取 logger**(`getLogger().info(...)` 等);`skillTools` 那处是从未被调用的死代码,连同 import 一并删除。至此 SDK 内 10 处日志调用全部经由 `getLogger()` 单一出口,不存在 console 旁路。
|
|
517
|
+
|
|
518
|
+
### 📝 文档
|
|
519
|
+
|
|
520
|
+
- README(中 / 英)与 `docs/{zh,en}/getting-started.md` 的命令表新增 `/agent`、`/reload`、`/mcp` 三行。
|
|
521
|
+
- README(中 / 英)与 `docs/{zh,en}/index.md` 的状态栏说明从「一条按优先级的文本流」改为「三个信息块 + 按块换行」。
|
|
522
|
+
- `docs/{zh,en}/mcp.md` 的「管理」章节补充 `/mcp` 直达入口与 `/reload` 刷新;`docs/{zh,en}/migration.md` 去掉「别名 `/migration`」。
|
|
523
|
+
- README(中 / 英)新增「界面语言」一节:识别优先级、`preferences.json` 与 `/lang`、覆盖范围,以及刻意不翻的三类内容。
|
|
524
|
+
- `docs/manifest.json` 与 README 中的版本号更新为 `1.0.0-alpha.21`。
|
|
525
|
+
- README(中 / 英)的「环境变量」补 `AIZ_DEBUG_SDK=0` / `AIZ_SDK_LOG_FILE`,「文件系统布局」补 `~/.ai-zen/logs/`,键位表下补一段「中断后已输出的思考与内容会保留」的说明。
|
|
526
|
+
- README(中 / 英)与 `docs/{zh,en}/getting-started.md` 的命令表补 `/lang`;「文件系统布局」与 `docs/{zh,en}/configuration.md` 的目录树补 `~/.ai-zen/cli/preferences.json`(CLI 本地偏好)。
|
|
527
|
+
|
|
528
|
+
### ⚠️ 已知取舍
|
|
529
|
+
|
|
530
|
+
- **emoji 的渲染宽度可能仍与终端对不上,这是终端层的事**:代码侧已经做到与渲染器同源(`displayWidth` = Ink 用的 `string-width`,光标位置用 `measureElement` 实测),但**终端自己对 emoji / East Asian Ambiguous 字符的列宽判定**仍可能与 Unicode 表不同(部分终端把 `✌️` 渲染成 1 列而不是 2 列,且没有查询接口)。那种情况下整行文本的相对位置就已经由终端决定了,光标只能跟着 Ink 的模型走。输入 / 删除 / 光标单位层面的 emoji 问题已在本次修完。
|
|
531
|
+
- **光标是「终端光标 + 字符反显」两套并存,这是故意的**:光标处的字符仍用 `<Text inverse>` 反显成一个方块,位置的真相源是实测出来的硬件光标。两者视觉上会叠成一个略重的方块——接受这个重影,因为任一层失效时另一层还能报位置(某些终端 / 多路复用器里硬件光标不显示,或 Ink 卸载时反显又没了)。以后别把反显当重复渲染删掉。
|
|
532
|
+
- **块不被劈开 ⇒ 行数随宽度变化**:80 列下三块各占一行(3 行),120 列可把身份与用量并成 1 行(共 2 行)。旧的「一条流」能压到 2 行,代价是把 `本轮 / 累计` 拆到两行——信息完整性优先。
|
|
533
|
+
- **生成中的速率是估算值**:SDK 只在每个请求的**最后一个 chunk** 才给 `usage`(`stream_options.include_usage`),拿不到逐 token 用量;把估算与精确值混成一个数字才是误导,因此分成「实时估算」与「本轮均速」两种写法。
|
|
534
|
+
- **`/mcp` 只覆盖配置层**:服务器当前是否连通、暴露了多少工具,在 SDK 里是插件私有状态(`McpScopePlugin` 的 `mcpManager`),没有公开 API,界面不展示连接状态。
|
|
535
|
+
- **`/reload` 有一两秒空档**:重建 Agent 且重连 MCP,服务器多的场景会有可感知延迟;期间消息与上下文不受影响。
|
|
536
|
+
|
|
537
|
+
### ✅ 测试
|
|
538
|
+
|
|
539
|
+
- 全量 **33 文件 / 440 例通过**,`npx tsc --noEmit -p tsconfig.test.json` 干净。
|
|
540
|
+
- 新增 `src/i18n/index.test.ts`(14 例:语言识别优先级 / `C`·`POSIX` 跳过 / 兜底英文 / 插值 / 缺词条回退 key / 中英词条一一对应)与 `src/i18n/preferences.test.ts`(9 例:文件缺失·损坏·非法值的容错、原子写、`initLocale` 只识别一次并落盘);`reasoning-effort.test.ts` 补 5 例「置灰不靠文案判断」。
|
|
541
|
+
- pty 真机验收:`LANG=en_US.UTF-8` 首次启动→英文并落盘 `en`;`/lang` 切中文→状态栏/输入框/通知全部跟随,且**重启后保持中文**(偏好先于系统语言);`/lang en` 切回英文正常。
|
|
542
|
+
- pty 真机验收(阶段 2):英文下斜杠菜单显示 `❯ /help Show this help`、`/help` 输出全英文列表;`/lang zh` 后斜杠菜单与 `/help` 整体切回中文。
|
|
543
|
+
- pty 真机验收(阶段 3 / 4,隔离的 `AI_ZEN_DIR` + `LANG=en_US.UTF-8`):首次启动识别为英文并落盘;启动屏 `The AI collaboration desk in your terminal`;主屏 `Type a message, / for commands…`、`thinking default · total 0 tok`;配置中心 `◆ Configuration` 与 `+ New model — marked as custom automatically` / `openai · factory-managed · vision`;模型详情 `◆ Model · GPT-6 Astra` / `factory-managed (editing switches it to custom)` / `Delete model`;新建模型与新建端点引导屏全英文。把 `preferences.json` 改回 `zh` 后**在 `LANG=en_US.UTF-8` 下重启仍为中文界面**(偏好优先于系统语言),配置中心与模型列表中文文案与改造前一致。
|
|
544
|
+
- **断言中文的测试一律显式锁语言**:`setLocale("zh")` 加在 `screens.test.tsx`、`config-wizard.test.tsx`、`reasoning-effort.test.ts`、`registry.test.ts`、`agent-prompt.test.tsx`、`handoff-choice.test.ts`、`config-editor.test.ts`、`agent-switch.test.ts`、`model-switch.test.ts`、`handoff-service.test.ts`、`auto-migrate-confirm.test.ts` —— 默认语言是 `en`,不锁就会在 CI 上莫名其妙地红。
|
|
545
|
+
- `mode.test.ts` 新增 6 例 `--tui`:有参数进 TUI 且参数作为首条消息、无参数时同不带 `--tui`、以 `/` 开头的命令不被当选项 / 子命令、`--tui --help` 时 help 优先、非 TTY 拒绝而非降级、`--` 之后的 `--tui` 是普通参数。
|
|
546
|
+
- `mode.test.ts` 新增 8 例 `--new`:单独用时进 TUI 且 `newSession: true`、`--tui --new <命令>` 同时带首条消息与新会话、与 `--tui` 顺序无关、有参数时落到 stdio 并被忽略、非 TTY 时落到 stdio 并被忽略、`--tui --new` 非 TTY 仍拒绝而非降级、`--new --help` 时 help 优先、`--` 之后的 `--new` 是普通参数。
|
|
547
|
+
- 新增 `src/hook.test.ts`(9 例,全部在临时 HOME 下跑,**不触碰真实 `~/.bashrc`**):写入的函数体含 `ai --tui --new "$@"` 且只有一个块、保留 rc 原有内容、重复安装幂等、**已装旧函数体(`ai --tui "$@"`)时原地升级**、历史 aiz 块迁移、zsh 走 `command_not_found_handler` 且写入 `.zshrc`;卸载移除块并保留其余内容、未安装时不动文件、rc 文件不存在时不报错。
|
|
548
|
+
- pty 真机验收(钩子路径):`ai --tui gti status` 进 TUI 并自动把 `gti status` 当用户消息发出(界面显示 `❯ 你 / gti status`),模型报错(故意的本地假端点)后界面仍存活可继续追问;`ai --tui /opt/bin/nonexistent --flag` 同样当**普通消息**发出,不被斜杠命令解析吃掉;`ai --tui` 在非 TTY 下退出码 2;写进 `~/.bashrc` 的函数体为 `ai --tui --new "$@"` 且 `bash -n` 语法通过。
|
|
549
|
+
- pty 真机验收(`--new`,隔离 `AI_ZEN_DIR` + 预置 `last-session.json` 指向旧会话):`ai` 续接并显示旧消息 `MARKER-OLD-QUESTION`;`ai --new` 停在**空对话**;`ai --tui --new gti status` 空对话且首条消息自动发出;`--help` 中英两个界面都列出 `--new`。
|
|
550
|
+
- 本次改动后全量 **36 文件 / 487 例通过**,`npx tsc --noEmit` 与 `-p tsconfig.test.json` 均干净。
|
|
551
|
+
- 新增 `src/tui/phase.test.ts`(15 例:六态各自的推进、工具名沿用、无 delta 的收尾 chunk 不改阶段、content 压过 reasoning、`reset` 归零、中英文案六者互不相同);`chat-session.test.ts` 补 3 例(按 SDK 事件时序推进、子 Agent 输出不覆盖主阶段、`send` 结束阶段归零)。
|
|
552
|
+
- pty 真机验收(本地 mock SSE 服务端按阶段分段吐流):依次抓到「连接中…」→「排队中…(等待首 token)」→「思考中…」→「输出中…」→「调用工具 no_such_tool…」→「等待 sub_agent_default 返回…」;状态栏同期只有 `⚡ 4.2 tok/s` 与本轮 / 累计 token,无「生成中」。
|
|
553
|
+
- 新增 `src/agent-switch.test.ts`(10 例)、`src/mcp-command.test.ts`(6 例)、`src/tui/token-rate.test.ts`(20 例)、`src/tui/status-segments.test.ts`(17 例)、`src/tui/screens/input-row.test.tsx`(7 例)、`src/tui/text.test.ts`(18 例);`input-editing.test.ts` 补 3 例「emoji 不被拆半个」的用例。
|
|
554
|
+
- `src/tui/screens.test.tsx` 增加状态栏三块顺序、块内不被拆、宽屏并排、以及「Ink 实际渲染行数 == `statusBarRows` 量出行数」的断言;`computeInputCursorPosition` 补「通知栏行数参与 y」的用例。
|
|
555
|
+
- `input-row.test.tsx` 里那条 `⚡` 用例同时断言「手算给 8、实测给 9」——本次改动的理由就在这两行里。
|
|
556
|
+
|
|
557
|
+
## [1.0.0-alpha.20] - 2026-10-27
|
|
558
|
+
|
|
559
|
+
### ✨ 新功能
|
|
560
|
+
|
|
561
|
+
- **对话屏支持鼠标滚轮滚动** —— 此前滚轮只能滚终端自己的回滚缓冲(`alternateScreen: false` 走内联渲染),滚上去看到的是历史帧快照,状态栏与输入框会直接滚出视野。
|
|
562
|
+
- 新增 `src/tui/mouse.ts`:挂载时下发 `DECSET 1000`(按钮事件,含滚轮)+ `1006`(SGR 扩展坐标);`mount()` 的退出、`farewell()`、信号与未捕获异常兜底**三处**都下发关闭序列,进程崩了也不会把 `^[[<64;…M` 喷进 shell。
|
|
563
|
+
- `parseWheel()` 剥掉 Shift(4) / Meta(8) / Ctrl(16) 修饰位后取滚轮码(`64` 上、`65` 下),横向滚轮 `66` / `67` 直接忽略;一格滚 3 行。
|
|
564
|
+
- 滚轮与 `PgUp`/`PgDn`、按行滚动共用同一套视口钳制 —— 抽出纯函数 `clampViewTop()`(`screens/layout.ts`),越界方向自然吃掉,返回 `null` 即贴底跟随最新内容。生成过程中滚动不会被拉回底部。
|
|
565
|
+
- **`isTerminalReply()` 纳入鼠标序列**:点击 / 拖拽 / 滚轮一律在源头丢弃。这一步是必须的 —— Ink 会把整条 SGR 序列当 `char` 交给 `useInput`(实测为 `"[<64;10;5M"`,所有键标志位 false),不拦就会落进输入框变成正文。
|
|
566
|
+
- 必须用 `1006`(SGR 坐标)而不是老的 X10 编码:后者被 Ink 的解析器拆成 `[M` + 三个普通字符两条事件,三个字符会直接被当正文插入(已实测)。
|
|
567
|
+
- 启动屏「任意键跳过」不再把鼠标事件当成按键。
|
|
568
|
+
|
|
569
|
+
### 📝 文档
|
|
570
|
+
|
|
571
|
+
- README(中 / 英)键位表新增「鼠标滚轮」一行,并写明两个坑:开启上报后**拖选复制需按住 `Shift`**(回退到终端原生选择);**`tmux` 用户需 `set -g mouse off`**,否则滚轮会先被 copy-mode 拦下。
|
|
572
|
+
- `docs/manifest.json`、`docs/{zh,en}/getting-started.md`、README 中的版本号与运行时依赖版本对齐(`agents-core ^4.5.2`、`agents-sdk 1.0.0-alpha.18`)。
|
|
573
|
+
|
|
574
|
+
### ⚠️ 已知取舍(终端机制使然,非缺陷)
|
|
575
|
+
|
|
576
|
+
- 鼠标上报是终端级开关:点击 / 拖拽不再交给终端做原生选择,复制需按住 `Shift`(iTerm2 / Windows Terminal / kitty 等均如此)。
|
|
577
|
+
- 应用内滚动只动视口,不产生终端 scrollback —— 想回看历史用滚轮 / `PgUp`,而不是终端的回滚条。
|
|
578
|
+
- 老版 Windows `conhost` 不支持 `1006`,滚轮在那种终端上表现为无反应。
|
|
579
|
+
|
|
580
|
+
### ✅ 测试
|
|
581
|
+
|
|
582
|
+
- 全量 **25 文件 / 316 例通过**(新增 `src/tui/mouse.test.ts` 1 文件 11 例;`screens.test.tsx` 新增滚轮解析、`clampViewTop` 钳制、启动屏鼠标守卫三组用例)。
|
|
583
|
+
- 真机(pty)端到端验证:滚轮方向解析、修饰位剥离、点击丢弃、普通打字不受影响、退出时下发关闭序列。
|
|
584
|
+
|
|
585
|
+
## [1.0.0-alpha.19] - 2026-10-22
|
|
586
|
+
|
|
587
|
+
### 🐛 修复
|
|
588
|
+
|
|
589
|
+
- **修复升级后首次启动报「Agent「default」未绑定模型」** —— alpha.18 把模型真相源改成 `AgentDefinition.modelId`,并把「为历史 Agent 补齐 modelId」放在 `bootstrap()` 里。但 TUI 的**续接会话**路径(`resumeLastSession()` 命中上一轮存档)根本不会经过 `resolveTarget()`,也就不会读取配置,于是 `bootstrap()` 从未执行:Agent 还是没绑模型、`config.defaultModel` 也还在,启动直接失败。
|
|
590
|
+
- `runTui()` 开头显式 `await ensureConfigDir()`,配置初始化早于任何 Agent 读取;
|
|
591
|
+
- `modelOf()` 自身也先读一次配置(双重保险),即使调用方漏了也不会再撞到这个错误。
|
|
592
|
+
- 新增回归测试:老配置(有 `defaultModel`、Agent 无 `modelId`)读一次配置后,模型被搬到 Agent 上、`config.json` 里的旧字段被清除。
|
|
593
|
+
|
|
594
|
+
### ✅ 测试
|
|
595
|
+
|
|
596
|
+
- 全量 **24 文件 / 299 例通过**。
|
|
597
|
+
|
|
598
|
+
## [1.0.0-alpha.18] - 2026-10-22
|
|
599
|
+
|
|
600
|
+
### 🐛 修复
|
|
601
|
+
|
|
602
|
+
- **修复「模型显示与实际请求不是同一个」导致的 401 死锁** —— 此前模型有三处来源:状态栏与会话存档里的 `modelId`、Agent 定义里的 `modelId`、以及配置里的 `defaultModel`。三者一旦分叉,就会:
|
|
603
|
+
- 状态栏写着 `deepseek-flash`,请求实际发给 GLM,而 bigmodel 的 key 无效 → 看到 401;
|
|
604
|
+
- 想切回 DeepSeek 时,`/model` 拿**显示层**的值判断,发现「已经是当前模型」直接返回,实际模型一个字都没改——于是怎么切都还是 401,只能删配置。
|
|
605
|
+
|
|
606
|
+
现在模型**只存在 `AgentDefinition.modelId` 一处**,状态栏、凭据预检、上下文阈值与 SDK 实发请求全部读它。
|
|
607
|
+
|
|
608
|
+
### 🔧 行为变更(不兼容)
|
|
609
|
+
|
|
610
|
+
- **升级 `@ai-zen/agents-sdk` 至 `1.0.0-alpha.17`**:配置层不再有 `defaultModel`,缺失 `modelId` 的 Agent 会直接报错(默认 Agent 出厂自带 `deepseek-flash`);历史配置升级时会把老的 `defaultModel` 搬进默认 Agent,不改变你正在用的模型。
|
|
611
|
+
- **会话存档不再记 `modelId`** —— 续接对话时模型完全由 Agent 定义决定;旧存档里的该字段读取时忽略。
|
|
612
|
+
- **`/model` 只写 Agent 定义** —— 不再有「Agent 未绑定就改全局默认模型」的分支。
|
|
613
|
+
- **切模型即重置思考强度** —— `AgentDefinition.modelParams` 在切模型时**直接清空**(不保留、不迁移、不逐键剔除),让目标模型走自己的 `defaultParams`。原因:思考参数是模型相关的,DeepSeek 关思考的 `thinking: {type:"disabled"}` 带到 GLM-5.3 上会被直接拒绝——此前会出现「一个不肯关思考的模型 + 一个说要关的参数」同时发出去。想调再用 `/think` 选一次。
|
|
614
|
+
- **配置中心移除「默认模型」项** —— 模型改在 Agent 详情页的「模型」字段里选(影响范围从「全局默认」变成「该 Agent」)。
|
|
615
|
+
|
|
616
|
+
### ✅ 测试
|
|
617
|
+
|
|
618
|
+
- 全量 **24 文件 / 298 例通过**。
|
|
619
|
+
- 新增 `model-switch` 用例:切换即清空 `modelParams`(不是只剔除思考键)、原本没有也不多写空对象。
|
|
620
|
+
|
|
621
|
+
## [1.0.0-alpha.17] - 2026-10-22
|
|
622
|
+
|
|
623
|
+
### 🔧 行为变更
|
|
624
|
+
|
|
625
|
+
- **升级 `@ai-zen/agents-sdk` 至 `1.0.0-alpha.16`(内含 core `4.5.2`):模型参数不再能覆盖请求体的固定字段** —— core 之前把模型参数 spread 在请求体最外层,于是 `Model.defaultParams` / `AgentDefinition.modelParams` 里若出现 `model` / `messages` / `tools` / `stream` / `stream_options`,会静默覆盖框架自己填的值。最惨的是 `stream: false`:流式被关掉而调用方仍按异步流解析,整轮直接失败。现在固定字段永远胜出。
|
|
626
|
+
- 对 `/think` 与思考档位**没有影响**:写入的都是 `thinking` / `reasoning_effort` 这类非固定键,照常透传。
|
|
627
|
+
- 唯一需要注意的是别在 `modelParams` 里手写上面那五个键——写了也不会生效。
|
|
628
|
+
|
|
629
|
+
## [1.0.0-alpha.16] - 2026-10-22
|
|
630
|
+
|
|
631
|
+
### 🔧 行为变更(不兼容)
|
|
632
|
+
|
|
633
|
+
- **升级 `@ai-zen/agents-sdk` 至 `1.0.0-alpha.15`:思考档位不再「存档位名」,改存展开后的参数** —— 上一版把档位名写在 Agent 定义的 `reasoningEffort` 上,运行期由 SDK 查表翻译成请求参数。那等于把「什么叫 `max`」这件产品语义塞进了运行时。现在改成**配置即事实**:
|
|
634
|
+
- `/think` 把选中档位的参数补丁直接写进 `AgentDefinition.modelParams`(如 `{"thinking":{"type":"enabled"},"reasoning_effort":"high"}`),SDK 只做一次 `{...defaultParams, ...modelParams}` 浅合并;
|
|
635
|
+
- **旧 `reasoningEffort` 自动迁移**:读 Agent 定义时展开成 `modelParams` 并写回;模型没关联策略的只删旧字段(不会瞎猜参数)。
|
|
636
|
+
- **思考策略表的归属调整为「SDK 提供、CLI 使用」** —— 表的类型、出厂内容与读写放在 SDK(CLI 与桌面端共享同一份「智谱 5.3 只认 low/high/max」的知识),CLI 只负责**利用**它:限制 `/think` 的可选档位、把档位展开成参数写进 Agent。`Scope` 不再需要注入策略表。
|
|
637
|
+
|
|
638
|
+
### 🚀 新功能
|
|
639
|
+
|
|
640
|
+
- **状态栏档位改为「从生效参数反查」** —— 显示的就是真正会发给 API 的那几个参数匹配到的档位(模型默认参数也算),不再依赖 Agent 上的档位声明。三种状态:命中某档 → 「低/中/高…」;有 Agent 覆盖但匹配不上任何档位(手改过参数)→「自定义」;完全匹配不上 →「默认」。
|
|
641
|
+
- **`/config` →「模型」详情页的「思考策略」字段**继续可用,显示该策略及其档位(如 `zhipu-5.3(low / high / max)`),写错名字当场拦下。
|
|
642
|
+
|
|
643
|
+
### ✅ 测试
|
|
644
|
+
|
|
645
|
+
- `src/reasoning-effort.test.ts` 重写(19 例):写入的是展开参数而非档位名、`max` → `off` 不残留 `reasoning_effort`、保留 `temperature` 等无关参数、`null` 清空后连 `modelParams` 一起删、往返一致(写进去能反查回原档位)。
|
|
646
|
+
- 新增 `src/agents-store.test.ts`(4 例):旧 `reasoningEffort` 展开并写回磁盘、无策略时只删字段、无旧字段时不写盘、迁移后能反查回原档位。
|
|
647
|
+
- 全量 **24 文件 / 299 例通过**。
|
|
648
|
+
|
|
649
|
+
## [1.0.0-alpha.15] - 2026-10-22
|
|
650
|
+
|
|
651
|
+
### 🔧 行为变更(不兼容)
|
|
652
|
+
|
|
653
|
+
- **升级 `@ai-zen/agents-sdk` 至 `1.0.0-alpha.14`:思考能力改为声明式策略表** —— 上一版用 `Model.reasoning: { efforts, disable? }` 表达厂商差异,`disable` 一词双义、`off` 靠拼接、还用全局枚举假装各家档位等价。现在协议差异全部搬进一张表:
|
|
654
|
+
- 新增 `~/.ai-zen/reasoning-presets.json`(首次启动自动释放,出厂三张:`openai` / `zhipu-5.3` / `deepseek`);模型只记 `reasoningPreset` 这个名字;
|
|
655
|
+
- `/think` 的可选档位改为读策略表 —— 表里有 `off` 才能关思考,没有就不给这个选项;
|
|
656
|
+
- 模型没关联策略、或策略名在表里不存在 → 一律视为不可调档(状态栏 `—`),绝不把来路不明的参数发出去;
|
|
657
|
+
- 档位中文标签改为「已知档位查表 + 未知档位名原样显示」,用户自建的档位名(如 `turbo`)不会显示成空白。
|
|
658
|
+
- `src/stdio-runner.ts` / `src/tui/chat-session.ts` 的 `TokenThresholds` 改为直接从 SDK 导入(此前是绕开 alpha.13 缺导出的派生类型)。
|
|
659
|
+
|
|
660
|
+
### 🚀 新功能
|
|
661
|
+
|
|
662
|
+
- **`/config` →「模型」详情页新增「思考策略」字段** —— 显示该模型当前引用的策略及其档位(如 `zhipu-5.3(low / high / max)`),可直接改成表里另一个策略名;写错名字当场拦下并列出可选策略,留空 = 不调档。自建模型从此能指向内置策略,不必手改 JSON。
|
|
663
|
+
- **策略表可自行编辑** —— 加一张表、调档位补丁都行;给某张表标 `"custom": true` 后 SDK 升级不再覆盖它,其余策略仍随 SDK 同步。
|
|
664
|
+
|
|
665
|
+
### ✅ 测试
|
|
666
|
+
|
|
667
|
+
- `src/reasoning-effort.test.ts` 重写(14 例):可选档位 = 策略表的键、未知档位名原样显示、未关联策略/策略名不存在时抛错、状态栏在非法声明时回退「默认」。
|
|
668
|
+
- 配置向导新增 2 例:写入合法策略名、非法策略名被拦下且不写盘。
|
|
669
|
+
- 全量 **23 文件 / 290 例通过**。
|
|
670
|
+
|
|
671
|
+
## [1.0.0-alpha.14] - 2026-10-22
|
|
672
|
+
|
|
673
|
+
### 🚀 新功能
|
|
674
|
+
|
|
675
|
+
- **输入框键位对齐 Claude Code / readline** —— 编辑快捷键统一为业界通行的一套:
|
|
676
|
+
- 按词操作:`Alt+B` / `Alt+F` 跨词移动(macOS 上 `Option+←/→` 发的就是 `ESC b` / `ESC f`,此前完全不响应);`Alt+Backspace`、`Alt+D`、`Ctrl+Delete` 按词删除
|
|
677
|
+
- 整段操作:`Ctrl+K` 删到行尾、`Ctrl+U` 删到行首、`Ctrl+W` **删到前一空白**(标点不切分,`src/utils/foo.ts` 一次删完)
|
|
678
|
+
- kill ring:`Ctrl+Y` 取回刚删的文本,`Alt+Y` 循环取回更早的;`Ctrl+_` 撤销上一次输入编辑;`Ctrl+S` 暂存 / 恢复草稿
|
|
679
|
+
- 移动与历史:`Ctrl+A` / `Ctrl+E` 行首 / 行尾,`Ctrl+P` / `Ctrl+N` 历史上下
|
|
680
|
+
- 新增纯函数层 `src/tui/input-editing.ts`(`deleteToPrevWhitespace` / `killedText` / kill ring / 撤销栈),12 例单测
|
|
681
|
+
- **macOS 按键诊断** —— `AIZ_DEBUG_KEYS=1` 时把终端环境(`TERM` / `TERM_PROGRAM`)与每次按键(`input` + 修饰位 + `raw` 状态)写入 `/tmp/aiz-key-debug.log`(`AIZ_KEYS_FILE` 可改路径),用于定位「某个键在我机器上没反应」:没新行=键被系统截走,`flags` 不符=匹配逻辑问题,`raw=false`=按键被终端自己解释。
|
|
682
|
+
- **`/model` 切换当前会话的模型并写入配置** —— 无参弹模型选择器(标注当前),`/model <id>` 直切。写入位置按「会话模型真正的来源」决定:Agent 定义显式绑定了模型 → 改该 Agent 的 `modelId`;没绑定(走默认模型)→ 改 `config.json` 的 `defaultModel`。切换后重建 Scope 与会话(消息与上下文保留),状态栏模型名随之更新。判定逻辑抽为 `src/model-switch.ts`(`resolveModelWriteTarget` / `switchModel`,7 例单测)。
|
|
683
|
+
- **思考强度展示与切换(`/think`,别名 `/effort`)** —— 状态栏在模型名后展示当前强度(`关` / `极低` / `低` / `中` / `高` / `极高` / `最高`;模型可调但 Agent 未声明时显示`默认`,模型不可调时显示 `—`)。`/think` 无参弹选择器(标注当前)、`/think high` 直设、`/think default` 清除声明恢复跟随模型默认。
|
|
684
|
+
- **档位存在 Agent 定义上**(`AgentDefinition.reasoningEffort`),不在模型上:同一个模型可以给编程 / 小说 / 闲聊三个 Agent 配不同强度,而模型的 `defaultParams` 不动。
|
|
685
|
+
- **可选档位由模型能力决定**(`Model.reasoning.efforts`,SDK 按厂商官方文档声明):智谱 GLM-5.3 系只有 `low/high/max` 且不能关(传 `medium` 或 `disabled` 会 400),DeepSeek 是 `off/low/high/max`,OpenAI 是 `off/minimal/low/medium/high`;模型没声明就一个档位都不给,绝不推测。
|
|
686
|
+
- 读写与校验抽为 `src/reasoning-effort.ts`(`effortChoices` / `describeEffort` / `setAgentEffort`,9 例单测)。
|
|
687
|
+
- **SDK 日志接管** —— TUI 启动即 `setLogger()`,关掉 SDK 的 `console.log` 兜底(否则 `load_mcp` 的 manifest 会直写 stdout 撕裂界面);`AIZ_DEBUG_SDK=1` 时改为落盘 `/tmp/aiz-sdk.log`(`AIZ_SDK_LOG_FILE` 可改路径),6 例单测。
|
|
688
|
+
|
|
689
|
+
### 🔧 行为变更(不兼容)
|
|
690
|
+
|
|
691
|
+
- **升级 `@ai-zen/agents-sdk` 至 `1.0.0-alpha.13`,上下文阈值改为结构体** —— `Model.maxContextTokens` 被 `Model.tokenThresholds: { handoff, guard }` 取代(`maxContextChars` 淘汰):
|
|
692
|
+
- `handoff`:交接软线,`prompt_tokens` 超过它触发任务交接;
|
|
693
|
+
- `guard`:强行阻断硬线,每次请求**前**超过它即中断本轮并报错。
|
|
694
|
+
- 两条线都是**绝对值**,`ContextGuardPlugin` 的 `ratio` / `suspendedRatio` / `isSuspended` 已从 SDK 删除——想调松就改数字。代价是交接回合不再有 2.0 倍额外放宽,出厂把 `guard` 配成 `handoff × 1.5`(375K)以留出缓冲。
|
|
695
|
+
- 旧配置**自动迁移**:SDK 的 `ConfigManager.read()` 读到即迁(`maxContextTokens` → `handoff`,`maxContextChars` 按 `/4` 折算,缺 `guard` 按 1.5 倍补一次,旧键删除),检测到差异才写回磁盘。用户无需手改 `config.json`。
|
|
696
|
+
- 配置中心里原来的「迁移阈值」一项拆成「交接阈值」与「阻断阈值」两行,并校验软线必须小于硬线。
|
|
697
|
+
|
|
698
|
+
- **`Ctrl+N` 由「换行」改为「历史下一条」** —— 换行改用 `Ctrl+J` / `Ctrl+Enter` / `Alt+Enter` / `Shift+Enter` / `\` + `Enter`
|
|
699
|
+
- **`Ctrl+V` 不再兼任「翻页」** —— 预留给 Claude Code 的「粘贴图片」;当前按下会提示暂未实现。翻页用 `PgUp` / `PgDn`,按行滚动用 `Shift+↑ ↓` / `Alt+↑ ↓`
|
|
700
|
+
- **`Ctrl+U` 由「清空整个输入框」改为「删到行首」** —— macOS 的 iTerm2 与 Terminal.app 会把 `⌘⌫` 映射成 `Ctrl+U`,原先的语义会让 Mac 用户一按 ⌘⌫ 就丢掉整段草稿;清空全部改由 `Esc` 负责
|
|
701
|
+
- **`Ctrl+C` 空闲时不再直接退出** —— 有草稿时先清空并提示,再按一次才退出
|
|
702
|
+
- **`Ctrl+D` 改为双击确认退出** —— 有草稿时改为 readline 语义(删光标后一个字符)
|
|
703
|
+
- **忙碌期(生成 / 迁移中)`Esc` 现在等于中断本轮**
|
|
704
|
+
|
|
705
|
+
### ⏳ 暂未实现
|
|
706
|
+
|
|
707
|
+
- **粘贴图片(`Ctrl+V`)** —— 需要先解决「文件 → 内容块」的端点能力探测(Files API 上传 vs base64 内联),不能把上传端点硬编码成某个 id;随后的 SDK 改造再做,本轮已把该功能代码移除,避免半成品上线。
|
|
708
|
+
|
|
709
|
+
### ✅ 测试
|
|
710
|
+
|
|
711
|
+
- 新增 `src/tui/input-editing.test.ts`(12 例)+ `src/tui/key-debug.test.ts`(4 例)+ `src/sdk-log.test.ts`(6 例)+ `src/model-switch.test.ts`(7 例)+ `src/reasoning-effort.test.ts`(9 例)+ 输入工具函数 8 例;全量 **23 文件 / 286 例** 通过,`tsc` 与 `build` 零错误。
|
|
712
|
+
|
|
713
|
+
### 📄 文档
|
|
714
|
+
|
|
715
|
+
- `README{,.zh}.md` 重写「键盘与终端兼容」小节:改为业界通行路线「Option 当 Meta + readline 系 Ctrl 键」,附 Apple Terminal / iTerm2 / VS Code / Ghostty 的具体设置路径;说明 `⌘` 需 kitty 键盘协议才会上报、`⌘⌫` 被终端映射为 `Ctrl+U` 的原因;补充按键诊断用法。`README{,.zh}.md` 与 `docs/{zh,en}/getting-started.md` 的命令表新增 `/model`、`/think`(含「配置文件来源」对照),`docs/manifest.json` 版本号同步。
|
|
716
|
+
|
|
717
|
+
## [1.0.0-alpha.13] - 2026-10-21
|
|
718
|
+
|
|
719
|
+
### 🚀 新功能
|
|
720
|
+
|
|
721
|
+
- **交接二次确认统一为「三选一」** —— 自动迁移、手动 `/migrate`、手动 `/handoff` 三条链路此前交互不一致(自动是 2 项选择器 + Esc 跳过;`/migrate` 是 yes/no 确认;`/handoff` 不询问)。现统一为同一个「交接方式」选择器:
|
|
722
|
+
- **当前会话自己写交接文档**(`self`,保真 · 可核实真实状态)
|
|
723
|
+
- **单独 Agent 写交接文档**(`summary`,独立摘要 · 快且省,不核实文件)
|
|
724
|
+
- **不交接**(跳过本次;`Esc` 同效)
|
|
725
|
+
|
|
726
|
+
命令后显式带 `self` / `summary`(如 `/migrate self`)时直接执行、不再询问。选择器抽为共用模块 `src/tui/screens/handoff-choice.ts`,三处复用同一份选项与文案。
|
|
727
|
+
|
|
728
|
+
### 🔧 修复
|
|
729
|
+
|
|
730
|
+
- **自动「不交接」的确认提示被覆盖** —— `notice` 通道只保留最新一条,原先「已跳过本次任务迁移」与「可随时 /migrate…」分两条推送,后者覆盖前者;现合并为单条,跳过确认可见。
|
|
731
|
+
|
|
732
|
+
### ✅ 测试
|
|
733
|
+
|
|
734
|
+
- 新增 `src/tui/screens/handoff-choice.test.ts`(5 例):三选一取值顺序、`self`/`summary` 原样返回、「不交接」/`Esc` → `null`、标题透传。全量 **18 文件 / 237 用例**通过,`tsc` 与 `build` 零错误。
|
|
735
|
+
|
|
736
|
+
### 📄 文档
|
|
737
|
+
|
|
738
|
+
- `docs/{zh,en}/migration.md`「触发方式」同步为统一的三选一交互(手动命令均会先询问,除非显式指定执行者);`docs/manifest.json`、`README{,.zh}.md` 版本号同步。
|
|
739
|
+
|
|
740
|
+
## [1.0.0-alpha.12] - 2026-10-20
|
|
741
|
+
|
|
742
|
+
### 🔧 修复
|
|
743
|
+
|
|
744
|
+
- **自动迁移后旧会话被覆盖、历史丢失** —— `ChatSession` 曾把会话 id 存两份(`this.currentId` 与 `ctx.currentId`)。手动 `/migrate` 与 `/new` 会同步两份,但**自动迁移**由 SDK 的 `AutoMigratePlugin` 直接驱动 `HandoffService.migrate()`,**不经过 `ChatSession` 的任何方法**,新 id 只写进了 `ctx.currentId`。于是 `ConversationPersistPlugin.getId()` 仍返回旧 id,迁移后第一次落盘就把 `prune` 产生的「系统提示 + 交接断点」写进**旧会话文件**,把已保存的完整历史覆盖掉(用户表现为「交接之后历史全没了,只剩交接文档」)。
|
|
745
|
+
- 修法:`currentId` / `currentName` 改为**代理 `ctx` 的 getter/setter**,消灭双真相源——谁更新 `ctx` 都立刻生效。
|
|
746
|
+
- `ConversationContext.currentId` 由 `string | undefined` 收紧为 `string`;`reset()` 中两行冗余双写随之删除。
|
|
747
|
+
- **测试污染**:`chat-session.test.ts` 的端到端用例会把交接文档写进 `<cwd>/.ai-zen/handoffs`,现改指临时目录并自动清理。
|
|
748
|
+
|
|
749
|
+
### ✅ 测试
|
|
750
|
+
|
|
751
|
+
- 新增 3 例:`ctx` 变更即时生效、赋值回写 `ctx`、端到端(真实 `handoffService.migrate()` 后 id 必须跟随;该用例在修复前会失败,已反向验证)。全量 **17 文件 / 232 用例**通过,`tsc`(含测试工程)与 `build` 零错误。
|
|
752
|
+
|
|
753
|
+
### 📄 文档
|
|
754
|
+
|
|
755
|
+
- `docs/{zh,en}/migration.md`「迁移链路」补充「会话 id 只有一个真相源」的约束与失效表现;`docs/manifest.json`、`README{,.zh}.md` 版本号同步。
|
|
756
|
+
- 依赖仍为 `@ai-zen/agents-sdk` `1.0.0-alpha.11`(本次无 SDK 改动)。
|
|
757
|
+
|
|
758
|
+
## [1.0.0-alpha.11] - 2026-10-20
|
|
759
|
+
|
|
760
|
+
### ⚠️ 依赖
|
|
761
|
+
|
|
762
|
+
- `@ai-zen/agents-sdk` `1.0.0-alpha.10 → 1.0.0-alpha.11`(`createCallMcpTool` 新增 `env` 参数为破坏性变更;CLI 不直接调用该工厂,无代码改动)。
|
|
763
|
+
|
|
764
|
+
### 🔧 变更(随 SDK)
|
|
765
|
+
|
|
766
|
+
- **`call_mcp_tool` 输出保护**:调用 MCP 工具时,文本输出超过 `maxToolOutput`(默认 32768 字符)会完整落盘为 `result.txt`,只返回警告 + 头尾预览,可用 `read` 分批读取——不再一次把上万字符灌进上下文。
|
|
767
|
+
- **图片 / 音频 / 二进制资源原样返回**:MCP 返回的 `image` / `audio` / `resource.blob` 转成 `image_url` / `file` 内容块透传(`resource.text` 并入正文、`resource_link` 转为可读线索),不再静默丢弃;媒体不受 `maxToolOutput` 约束。
|
|
768
|
+
|
|
769
|
+
### 📄 文档
|
|
770
|
+
|
|
771
|
+
- `docs/{zh,en}/mcp.md` 说明「仅文本受输出保护、媒体不受上限约束」;`docs/{zh,en}/getting-started.md`、`docs/manifest.json`、`README{,.zh}.md` 版本同步。
|
|
772
|
+
|
|
773
|
+
## [1.0.0-alpha.10] - 2026-10-20
|
|
774
|
+
|
|
775
|
+
### 💥 变更
|
|
776
|
+
|
|
777
|
+
- **交接与迁移拆成两条命令**(语义唯一,命名与实现对齐):
|
|
778
|
+
- **`/handoff`** —— 原 agent 停手、核实状态、把交接文档写到 `<项目>/.ai-zen/handoffs/`,**会话照旧**(不弹确认,可反复执行;交接回合留在 `agent.messages` 里,可继续要求细化同一份文档);
|
|
779
|
+
- **`/migrate`**(别名 `/migration`)—— 同一次交接 + 清历史 + 开新会话(需确认)。
|
|
780
|
+
- 两条都支持 `self` / `summary` 参数(如 `/handoff summary`、`/migrate self`)跳过默认选择。
|
|
781
|
+
- **自动迁移改为「执行者三选一」选择器**:上下文超阈值时不再只问「是否迁移」,而是选择 **原 agent 自交接(默认高亮,保真)/ 独立摘要(便宜)/ 跳过本次**。选择器复用既有 picker 浮层(`setPickerState`)。非交互环境(管道、重定向、e2e)无选择通道,按默认执行者迁移,不阻塞、不额外输出。
|
|
782
|
+
- **交接文档目录**:新增 `PROJECT_HANDOFFS_DIR = <cwd>/.ai-zen/handoffs`,交接文档落在项目内,可随仓库共享/评审。
|
|
783
|
+
|
|
784
|
+
### ♻️ 重构
|
|
785
|
+
|
|
786
|
+
- **`src/migration-service.ts` → `src/handoff-service.ts`**:`createMigrationService` → `createHandoffService`,钩子从 `onBeforeMigrate` / `onMigrated` 改为 `onBeforeHandoff` / `onHandoff` / `onMigrated`。存档旧会话移到 `onHandoff`(此时已包含交接回合,归档可审计);`/handoff` 不切换会话也因此不需要额外存档(`ConversationPersistPlugin` 每轮已落盘)。
|
|
787
|
+
- **`src/auto-migrate-confirm-plugin.ts` → `src/auto-migrate-confirm.ts`**:`AutoMigrateConfirmPlugin`(继承 + 重写 `onAfterSend`)退化为 `createAutoMigratePlugin()` 工厂——「何时迁移」仍在 SDK 插件里,「用谁交接」通过 SDK 新增的 `resolveMode` 回调注入,不再重复实现阈值判断。
|
|
788
|
+
- `src/agent-creator.ts` 抽出并导出 `listSkillDirs()`:Scope 装配与交接服务(读取 `handoff` Skill 正文)共用同一份目录优先级,避免两处各算一遍。
|
|
789
|
+
- `ChatSessionOptions.requestConfirm` → `requestMode`(返回 `HandoffMode | null`);`ChatSession.migrate()` / `handoff()` 返回文档路径;`ConversationContext.migrationService` → `handoffService`。
|
|
790
|
+
- `ContextGuardPlugin` 接线 `isSuspended: () => handoffService.busy`:交接回合护栏从 `× 1.5` 放宽到 `× 2`。
|
|
791
|
+
- 斜杠命令解析支持参数:命令名取首个 token(`/migrate summary` 不再被当成未知命令)。
|
|
792
|
+
|
|
793
|
+
### ⚠️ 依赖
|
|
794
|
+
|
|
795
|
+
- `@ai-zen/agents-sdk` `1.0.0-alpha.9 → 1.0.0-alpha.10`(`TaskMigrationService` → `HandoffService` 为破坏性变更)。
|
|
796
|
+
|
|
797
|
+
### ✅ 测试
|
|
798
|
+
|
|
799
|
+
- 新增 `src/handoff-service.test.ts`(迁移清 `lastUsage` / 新会话落盘 / `/handoff` 不切会话)、`src/auto-migrate-confirm.test.ts`(非交互默认值、选择透传、取消跳过、busy 让行)。全量 **17 文件 / 229 用例**通过,`tsc --noEmit`(含测试工程)与 `build` 零错误。
|
|
800
|
+
|
|
801
|
+
### 📄 文档
|
|
802
|
+
|
|
803
|
+
- `docs/{en,zh}/migration.md` 重写为「任务交接与迁移」:两条命令的差异、两种执行者的取舍、自交接的代码级保障(守卫/放宽/防递归)、文档位置与命名、模板由 `handoff` Skill 承载(`metadata.custom: "true"` 退出同步、可项目级覆盖)、成本说明。网站镜像同步。
|
|
804
|
+
- `README.md` / `README.zh.md` 命令表与「任务交接与迁移」章节、`docs/{en,zh}/index.md` 特性与导航同步。
|
|
805
|
+
|
|
806
|
+
## [1.0.0-alpha.9] - 2026-10-14
|
|
807
|
+
|
|
808
|
+
### 🔧 修复
|
|
809
|
+
|
|
810
|
+
- **自动迁移与 CLI 的交互适配(两处)**:
|
|
811
|
+
- **迁移期间输入框未真正拦住** —— 主输入 `useInput` 的 `isActive` 此前只挡「确认框 / 选择器 / 配置向导」,未挡「生成中」态。自动迁移在 `send()` 内 `await onAfterSend` 执行(`busy` 为真、输入框已被 Spinner 顶替),但按键仍会被静默吞进隐藏的输入缓冲、待本轮结束才浮现。现令主输入在 `busy` 时整体失活,另设「忙碌期」处理器仅放行 `Ctrl+C` 中止 / `Ctrl+D` 退出 / `PgUp`·`Shift+↑↓` 回看;`/migrate` 手动迁移同样纳入 `busy`。
|
|
812
|
+
- **迁移完成后仍被总上下文护栏拦截** —— 迁移(prune)会物理重置上下文,却遗留 `agent.lastUsage`(迁移前那一次请求的 `prompt_tokens`)。`ContextGuardPlugin`(`onInnerLoopStart`,硬上限 `maxTokens × 1.5`)据此误判,迁移后第一条消息即被 `ContextOverflowError` 中断。现于迁移后清空 `agent.lastUsage`,护栏首轮无据可查而放行,待下一次响应以真实用量回填。
|
|
813
|
+
|
|
814
|
+
### ✅ 测试
|
|
815
|
+
|
|
816
|
+
- 新增 `src/migration-service.test.ts`:断言迁移后 `agent.lastUsage` 被清空(移除修复后该用例确会失败)。全量单测 **17 文件 / 227 用例**通过,`tsc --noEmit` 与 `pnpm build` 零错误。
|
|
817
|
+
|
|
818
|
+
## [1.0.0-alpha.8] - 2026-10-14
|
|
819
|
+
|
|
820
|
+
### 🔧 修复
|
|
821
|
+
|
|
822
|
+
- **修复长时间 TUI 会话内存无界增长导致的 OOM(崩溃)** —— CLI 入口此前从未设定 `NODE_ENV`,React 因而按 `process.env.NODE_ENV !== "production"` 载入 **development** 版 `react-reconciler`;该开发构建每次渲染都会调用 `performance.measure()`(User Timing API),而 Node 的 performance 条目缓冲**无上限、从不逐出**,长时间流式会话下条目持续累积,约 55 分钟即将堆顶到 `heap_size_limit`(本机 ~1GB)触发 OOM。现于 `src/index.ts` 顶部设 `process.env.NODE_ENV ||= "production"`;因 ESM 静态 `import` 会被提升到模块体之前执行,对 TUI 入口 `./tui/index.js` 改用动态 `import()`,确保 React/Ink 在 `NODE_ENV` 设定**之后**才被求值,从而加载 production 版 reconciler(该版不调用 `performance.measure`)。显式 `NODE_ENV=development` 仍保留开发构建,便于排查。
|
|
823
|
+
|
|
824
|
+
### ✅ 测试
|
|
825
|
+
|
|
826
|
+
- **端到端实证** —— 真实 ESM 下经入口加载 Ink 并连续重渲染 300 次:修复后 `performance.measure` 条目 **0 → 0**(production reconciler),显式 `NODE_ENV=development` 对照组 **0 → 905**(dev 埋点仍在)。全量单测 **16 文件 / 226 用例**通过,`tsc --noEmit` 与 `pnpm build` 零错误。
|
|
827
|
+
|
|
828
|
+
## [1.0.0-alpha.7] - 2026-10-14
|
|
829
|
+
|
|
830
|
+
### 📄 文档
|
|
831
|
+
|
|
832
|
+
- **状态栏 token 用量纳入文档** —— `docs/zh|en/index.md` 的「主要特性」补上「状态栏实时展示 模型 · Agent · 本轮/累计 token 用量 · 生成状态」(此前仅在 README 提及),累计口径为 `sumUsage(agent.messages)`(含所有层级的子 Agent)。
|
|
833
|
+
- 修正 `TODO.md` 中已过时的路径引用(`src/tui/config-wizard.tsx` → 目录 `src/tui/config-wizard/`)。
|
|
834
|
+
|
|
835
|
+
### ⚠️ 依赖
|
|
836
|
+
|
|
837
|
+
- `@ai-zen/agents-core` `^4.5.0 → ^4.5.1`、`@ai-zen/agents-sdk` `1.0.0-alpha.8 → 1.0.0-alpha.9`
|
|
838
|
+
|
|
839
|
+
> 纯文档修正,无代码变更。
|
|
840
|
+
|
|
841
|
+
## [1.0.0-alpha.6] - 2026-10-14
|
|
842
|
+
|
|
843
|
+
### ♻️ 重构
|
|
844
|
+
|
|
845
|
+
- **累计用量改用 SDK 的 `sumUsage`,移除 CLI 自建的内存累加器** —— 依赖 `@ai-zen/agents-sdk` `1.0.0-alpha.8` / `@ai-zen/agents-core` `4.5.0`:除助手消息的 `usage` 外,委派产生的用量现由 core 在委派边界钉到工具消息的 `subAgentUsage` 上。故 `ChatSession.totalUsage` 直接 `sumUsage(agent.messages)` 即可(含所有层级的子 Agent),删掉 `src/usage.ts` 与 `onSubAgentEnd` 内存回灌——子 Agent 开销随消息持久化,续接 / `/load` 亦不丢,且不再有双重计数风险。
|
|
846
|
+
|
|
847
|
+
### ⚠️ 依赖
|
|
848
|
+
|
|
849
|
+
- `@ai-zen/agents-sdk` `1.0.0-alpha.7 → 1.0.0-alpha.8`、`@ai-zen/agents-core` `^4.4.0 → ^4.5.0`
|
|
850
|
+
|
|
851
|
+
### ✅ 测试
|
|
852
|
+
|
|
853
|
+
- 移除 `src/usage.test.ts`(逻辑下沉 core,已由 core 覆盖);`chat-session.test.ts` 的 `totalUsage` 用例改为覆盖「assistant 的 usage + 工具消息的 subAgentUsage」求和。全量单测 **16 文件 / 226 用例**通过,`tsc --noEmit` 与 `npm run build` 零错误。
|
|
854
|
+
|
|
855
|
+
## [1.0.0-alpha.5] - 2026-10-14
|
|
856
|
+
|
|
857
|
+
### 🚀 新功能
|
|
858
|
+
|
|
859
|
+
- **状态栏展示 token 累计消耗** —— 依托 `@ai-zen/agents-sdk` `1.0.0-alpha.7` / `@ai-zen/agents-core` `4.4.0` 为每条 assistant 消息钉上的 `usage`,新增 `src/usage.ts`(`addUsage` / `sumMessagesUsage` 纯函数)与 `ChatSession.totalUsage`:对本会话全部 assistant 消息的 `usage` 求和即计费口径的累计消耗;子 Agent 的 messages 不在父 Agent 中,故在 `onSubAgentEnd` 单独累加并入、`/new` 时归零。状态栏由「`{tokens} tok`」升级为「`本轮 x · 累计 y tok`」(本轮 = `lastUsage.total_tokens`,累计 = `totalUsage.total_tokens`)。
|
|
860
|
+
- **依赖升级** —— `@ai-zen/agents-sdk` `1.0.0-alpha.6 → 1.0.0-alpha.7`、`@ai-zen/agents-core` `^4.3.2 → ^4.4.0`(后者新增 `AgentNS.Message.usage` 逐条 token 用量)。
|
|
861
|
+
|
|
862
|
+
### ✅ 测试
|
|
863
|
+
|
|
864
|
+
- 新增 `src/usage.test.ts`(4 例);`chat-session.test.ts` 增补 `totalUsage` 求和用例;`screens.test.tsx` 的 `StatusBar` 用例更新。全量单测 **17 文件 / 230 用例**通过,`tsc --noEmit` 与 `npm run build` 零错误。
|
|
865
|
+
|
|
866
|
+
## [1.0.0-alpha.4] - 2026-10-13
|
|
867
|
+
|
|
868
|
+
### ♻️ 重构
|
|
869
|
+
|
|
870
|
+
- **TUI 渲染直接以 `agent.messages` 为唯一数据源** —— 移除 `ChatEvent` / `chatReducer` / `LiveAssistant` / `Block` / `messagesToBlocks` 等自定义中间模型。SDK 在流式期间会**实时更新 `agent.messages`**(`Agent.parseStreamData` 把每个 delta 增量写进当前那条 message),因此跨消息顺序(数组序)与单条 assistant 内部顺序(`思考 → 工具 → 正文`)天然保持,直接渲染即可,杜绝「第二套真相」的失真与维护负担。已完成消息的折行 / 高亮按「消息 + 列宽 + 内容签名」缓存(`cachedMessageToLines`),流式中那条靠内容签名自动失效。
|
|
871
|
+
- **notice / error 改为独立栏位** —— 系统级提示(命令反馈 / 迁移进度 / 报错)不再混入对话转录,也**绝不写进 `agent.messages`**(那会被 `formatHistory()` 原样发给模型、污染上下文);改在输入框上方的独立一行渲染(`src/tui/screens/notice.ts`)。
|
|
872
|
+
|
|
873
|
+
### 🎯 优化
|
|
874
|
+
|
|
875
|
+
- **流式增量渲染**:Ink 开启 `incrementalRendering` —— 从「每帧 erase 整屏 + 重写整屏」(`createStandard`)切到「只重绘变化的行」(`createIncremental`),每帧输出量由 O(屏幕大小) 降到 O(变化量),SSH / 弱网下流式更稳。
|
|
876
|
+
- **流式合帧**(`src/tui/screens/frame-buffer.ts`):内容变更先攒入缓冲,按 60ms(约 16fps,低于 Ink 默认 30fps 输出节流)批量触发重渲染,把上屏频率从「每个 delta 一次」降到固定帧率。
|
|
877
|
+
- **渲染缓存**:长对话下历史消息的折行 / 高亮免于每帧重算,成本不再随对话增长而恶化。
|
|
878
|
+
- **思考与工具调用之间补一个空行**,排版更清晰。
|
|
879
|
+
|
|
880
|
+
### ✅ 测试
|
|
881
|
+
|
|
882
|
+
- 新增 `frame-buffer.test.ts`、`notice.test.ts`;`screens.test.tsx` 重写为基于 `messageToLines` / `transcriptLines` / `computeChatLayout(transcript)`。
|
|
883
|
+
- `tsc` 零错误;全量单测 **16 文件 / 225 用例**通过;`npm run build` 通过。
|
|
884
|
+
- 真机(pty)验证:SSH / 弱网下流式稳定,长对话滚动流畅。
|
|
885
|
+
|
|
1
886
|
## [1.0.0-alpha.3] - 2026-10-13
|
|
2
887
|
|
|
3
888
|
### 💥 破坏性变更
|