Releases: javaside/springai-agentdemo
Release list
v1.9.0 —— 后台任务不再像卡死
springai-agentdemo v1.9.0
在 v1.8.3 基础上的功能版(minor)。核心交付物仍是终端编码智能体 springai-code-tui。
本版主题是**「后台任务不再像卡死」:v1.8.0 引入后台子 agent 之后,两个体验痛点被先后修掉——运行中的任务行加上波光动画**(耗时计数器 1 秒才跳一次,太静,看起来像卡死);后台任务完成通知改为明确要求模型继续推进 Todo 计划(此前模型常常只是确认一下结果就停住)。另外把 run_in_background 的使用条件写进了工具描述,从源头减少误用后台。无破坏性变更,无新增依赖。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.9.0-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.9.0-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.9.0。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.8.3 的变化。
✨ 新功能:后台任务面板 RUNNING 行波光动画
⏱ 后台任务面板与 /tasks 面板里运行中(▶)的任务行现在带一层波光动画——文字在亮黄加粗的基础上以帧为节律扫过一条光带,一眼就能看出「这条还在动」。
为什么需要它:面板上每行只有一个 1 秒跳一次的耗时计数器,其余全部静态。长时间跑一个没输出的任务(比如等一个外部 API、卡在一个慢工具上)时,那行看起来就像死了一样,你只能干瞪眼猜它是不是挂了。加动画之后,「在跑」和「死了」从视觉上分开了。
只对 RUNNING 生效:✓ 完成 / ✗ 失败 / ⊘ 已终止都是终态,保持原来的静态样式——终态不需要「动」,动起来反而添乱。
动画复用状态栏已有的 shimmer 实现(同一套波形),颜色仍是运行态专属的亮黄加粗(TODO_RUN),与完成/失败行的绿/红区分开。
🐛 修复:后台完成通知现在要求模型「继续干」
后台任务结果送达模型时,通知结尾的措辞改了:
- 之前:「以上是你先前派出的后台任务的结果,请据此继续。」——模型经常只回一句「好的,收到结果」就停住,根本没往下推进。
- 现在:「请检查你的 Todo 计划列表,找出下一项 pending 的任务并立即执行;如果所有任务已完成,向用户汇报最终结果。」——明确要求模型继续计划而非仅确认结果。
根因:通知没有告诉模型「接下来该做什么」。后台任务的意义是「派出去的活不占着回合」,主模型收到结果后理应接着跑计划里的下一项——但「据此继续」这四个字没把这件事说透,模型理解成「确认收到」也说得通。把期望动作写明之后,模型的行为就有了明确的锚点。
这是纯提示词改动:不涉及任何执行逻辑,只改 BackgroundNotifier 拼接的文本;既不影响后台任务的结果回收,也不影响权限判定。
🔧 工程:run_in_background 使用条件写进工具描述
Task / ParallelTasks 的工具描述(模型看到的说明文字)重写,把默认前台和后台的严格使用条件讲清楚:
- 默认前台(不传或传
false):大多数编码流程是顺序的——explore → plan → implement → test,每一步都需要上一步的结果,后台反而添乱。 - 仅当全部成立才用后台:① 任务真正独立(其输出不决定你的下一步);② 之后会通过
TaskOutput取结果;③ 任务只读或只用预批准工具(后台任务不能弹审批,需要批准的调用会被静默拒绝)。 - 不要因为任务慢就转后台:一个你最终需要的慢任务,前台等它比后台等它再取结果更干净。
为什么重要:此前描述对后台的适用场景说得太宽松(「长调查可以后台跑,你继续干别的」),模型容易把需要写文件、需要审批的任务也派到后台——然后所有 ASK 全被静默拒绝,模型对着失败结果反复重试直到耗光回合。把条件收紧到工具描述里,等于把 v1.8.0 发布说明里那条「后台任务 ASK 一律 DENY」的警告前置到模型决策那一刻。
🧪 质量
mvn -pl springai-code-tui test → 1387 个用例,0 失败 0 错误(与 v1.8.3 持平;本版三个改动均为界面/提示词层面,无新增单测)。
🔐 校验(SHA-256)
8e2eb1008aad70e33fb2d3fe4b70d49c5f1425797e0f3722926b05e306709fef springai-code-tui-1.9.0-dist.tar.gz
3f4d602aa7a5ce30a12a1c1ce2181f3f5588bf98ef9ac9b42e53dae0a48080fa springai-code-tui-1.9.0-dist.zip
shasum -a 256 -c <<'EOF'
8e2eb1008aad70e33fb2d3fe4b70d49c5f1425797e0f3722926b05e306709fef springai-code-tui-1.9.0-dist.tar.gz
3f4d602aa7a5ce30a12a1c1ce2181f3f5588bf98ef9ac9b42e53dae0a48080fa springai-code-tui-1.9.0-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖。
环境:JDK 17+,macOS / Linux / Windows。
v1.8.3 —— context 低估修复 + Terminal.app 崩溃缓解
springai-agentdemo v1.8.3
在 v1.8.2 基础上的缺陷修复版(patch)。核心交付物仍是终端编码智能体 springai-code-tui。本版修复两处低概率运行期缺陷,并降低 Terminal.app 崩溃频率。无破坏性变更,无新增依赖。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.8.3-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.8.3-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.8.3。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.8.2 的变化。
🐛 修复
/context 上下文用量严重低估
现象:/context 面板显示的 token 估算值远低于实际(有时只有真实用量的几十分之一),会话明显已经很长,面板却显示占用极低。
根因:CodingAgent.contextStats() 遍历消息时只调用 m.getText(),而 Spring AI 2.0 的 ToolResponseMessage 构造函数硬传空串给父类——getText() 永远返回 ""。工具调用结果(尤其是 BashOutput、Read 大文件等)的实际内容存在 getResponses()[i].responseData() 里,完全被漏算。一个含 1MB BashOutput 的会话,token 估算值可能接近于零。
修复:对 ToolResponseMessage 改走 getResponses() 遍历 responseData(),其余消息类型保持原有 getText() 路径。配套回归单测:构造含 100k 字符 responseData 的工具消息,断言 token 估算必须包含该内容。
Terminal.app 在处理大量输出时崩溃频率降低
现象:使用 macOS 自带 Terminal.app 时,模型执行工具(尤其 BashOutput 输出大量内容)期间,整个 Terminal.app 偶尔崩溃关闭,所有窗口/标签页同时消失。
根因:Terminal.app 在 _dispatch_kevent_mach_msg_recv(GCD kevent pty I/O 路径)中存在 use-after-free bug(EXC_BAD_ACCESS / SIGSEGV),触发条件是短时间内向 pty 写入大量数据。工具结果一次可产生数千行输出,此前全部在单个 33ms 帧内打出,形成 MB 级突发流量。v1.8.1 的 printWrapped 修复使完整内容被打印(而非截断),加剧了这一情况。
缓解:新增每帧 pty 写入行数上限(MAX_LINES_PER_DRAIN = 300),超出的行保留到下一帧继续打,将突发流量打散到多个 33ms 帧中。内容不丢失,只是渐进显示(体验类似流式回复);对正常大小的输出(≤300 行)无任何影响。
注意:这是降频缓解手段,不能 100% 消除崩溃——Terminal.app 的 bug 仍在。根治方案是改用 iTerm2。
🔧 工程
README 拆分为 docs/guide/ 专题文档
README.md 从 799 行精简到 111 行,保留简介、模块用途摘要与快速上手。8 个专题内容独立成 docs/guide/ 下的专题文档:
| 文档 | 内容 |
|---|---|
| security.md | 安全声明完整版 |
| permissions.md | 权限管理:审批面板、规则 DSL、内置底线 |
| background-agent.md | 后台子 agent:结果回收、权限矩阵、三个刹车 |
| vision.md | 视觉输入:贴图、模型列表、硬上限 |
| mcp.md | MCP 配置 |
| skills.md | 技能配置 |
| interjection.md | 回合中插话 |
| reference.md | 操作键、斜杠命令、已知限制 |
全量测试
springai-code-tui 1387 用例通过(新增 ToolResponseMessage token 漏算回归断言)。
🔐 校验(SHA-256)
5e67eea904302635ef3f3daf0469546eedf0f45fb6d60ecd312bd55beb421dc8 springai-code-tui-1.8.3-dist.tar.gz
6f32f25e73b89011592a4c455f889aa2dc0ba166a13c557885c187209e7682cb springai-code-tui-1.8.3-dist.zip
shasum -a 256 -c <<'EOF'
5e67eea904302635ef3f3daf0469546eedf0f45fb6d60ecd312bd55beb421dc8 springai-code-tui-1.8.3-dist.tar.gz
6f32f25e73b89011592a4c455f889aa2dc0ba166a13c557885c187209e7682cb springai-code-tui-1.8.3-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖。
环境:JDK 17+,macOS / Linux / Windows。
v1.8.2 —— 竞态修复 + 日志统一
springai-agentdemo v1.8.2
在 v1.8.1 基础上的缺陷修复版(patch)。核心交付物仍是终端编码智能体 springai-code-tui。本版修复一处低概率并发竞态(忙时提交在回合边界被错路由),并统一日志级别策略与系统提示工具访问边界措辞。无破坏性变更,无新增依赖。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.8.2-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.8.2-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.8.2。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.8.1 的变化。
🐛 修复
忙时提交在回合边界不再被误路由为「排队」
现象:在模型回复的最后一刻发送消息,偶尔该插话的消息被送进排队队列,要等到下一次用户主动发消息才被捎走——像是「Enter 没反应」。
根因:路由逻辑先读 busy()、再读 isIdle(),若 Reactor 线程恰好在两次读取之间结束回合,同一次 Enter 会先看到「忙」、随后又看到「空闲」,最终误走排队分支而非插话。
修复:新增 ConversationState.submissionSnapshot() 原子快照(synchronized,一次读取同时拿到 busy 与 activeTurn),路由决策全部基于这一份快照,消除竞态窗口。配套单测 submissionUsesOneRoutingSnapshot 钉住:快照之后回合结束,路由结果仍为插话。
🔧 工程
日志级别统一为 INFO
生产代码中原本散落的 debug(...) 调用(插话送达、权限审批、后台任务生命周期、软链失败、权限规则回写、建议规则放弃)统一升为 info,与文档声明的「生产最低 INFO」策略对齐。
CodingAgent错误路径(同步组装出错、回合 doOnError)补error级日志,便于按 turnId 排障。- Reactor
subscribe加空errorConsumer,消除onErrorDropped / ErrorCallbackNotImplemented噪音。 SubagentRunner后台任务生命周期新增三条 INFO(「已提交」「开始执行」「执行完成+耗时 ms」),配套单测断言:结果文本不夹带生命周期日志,日志不含 DEBUG。config.env.example、启动脚本说明补充CODETUI_BACKGROUND_CONCURRENCY/CODETUI_TASK_OUTPUT_TIMEOUT_SECONDS配置项。
系统提示工具访问边界措辞优化
原措辞「所有操作都应发生在当前项目根目录之内」过于严格,会拦住合理的跨目录访问(读另一个项目的文件、写系统临时目录等)。改为:根目录是默认工作目录而非强制访问边界;跨目录访问服从权限引擎与内置安全底线;操作范围以任务必需为限。配套单测钉住措辞不回退。
📊 测试
全量测试:springai-code-tui 1386 用例通过(较 v1.8.1 新增路由竞态单测、后台任务日志策略验证、系统提示措辞钉桩)。
🔐 校验(SHA-256)
b933fe009239ed610c2f8381f6d441e10e1abab6e9c32e5825f5a4e47fec794b springai-code-tui-1.8.2-dist.tar.gz
88448b43e1f46b6caeed1e22c7a36f9c434eeed1698f90e5681fa6b055a1f6de springai-code-tui-1.8.2-dist.zip
shasum -a 256 -c <<'EOF'
b933fe009239ed610c2f8381f6d441e10e1abab6e9c32e5825f5a4e47fec794b springai-code-tui-1.8.2-dist.tar.gz
88448b43e1f46b6caeed1e22c7a36f9c434eeed1698f90e5681fa6b055a1f6de springai-code-tui-1.8.2-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖。
环境:JDK 17+,macOS / Linux / Windows。
v1.8.1 —— 缩放窗口三连修
springai-agentdemo v1.8.1
在 v1.8.0 基础上的缺陷修复版(patch)。核心交付物仍是终端编码智能体 springai-code-tui。本版集中修复终端窗口缩放引发的三类显示缺陷:拖动窗口后信息流乱成一屏残骸、中文输入法拼音「错位」、模型回复超宽被截断不显示全。无破坏性变更,无新增依赖,建议所有 v1.8.0 用户升级。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.8.1-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.8.1-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.8.1。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.8.0 的变化。
🐛 修复
缩放窗口不再打烂界面:两级 resize 修复
现象:拖动终端窗口宽度,内联界面(输入框/状态栏)被撕成新旧几个框叠在一起,对话被一截截顶出可见屏;停手之后往上翻,回滚缓冲里堆着一份份过期界面的完整残骸(横幅+输入框+状态栏+整屏空白)——每拖一次多一份。
根因:会 reflow 的终端(Terminal.app 等)在改宽度时把屏上内容重新折行——整宽的旧帧被拆高,应用只按旧记账重画、拆出来的多余行没人擦;被拆高的部分还把真实对话顶进回滚缓冲,事后从应用侧永远够不着。
修复(两级,分工明确):
- 拖拽中:每个宽度变化事件,赶在库按新宽度重画之前,从显示区顶行清到屏尾,新帧原地盖回去——擦与画在同一个事件周期里,不漏半帧脏屏。内核合并 SIGWINCH 丢事件的情况由每帧列宽比对兜底。
- 停稳后(宽度 ≈300ms 不再变化):整屏连回滚缓冲一起抹掉(与
/clear同路),从应用自己的输出留底(最近 400 行)按新宽度全量重放。拖拽期间累积的一切伤害——鬼影、被顶走的对话、回滚缓冲里的残骸——一次性归零。重放是幂等的全量重建,多触发只是多重建一次。
代价(有意的取舍):停稳重放会抹掉回滚缓冲,「往上翻」的上限从「无限但全是残骸」变成「最近 400 行且干净」;应用启动前的 shell 输出也随之消失(/clear 早已是同样语义)。
中文输入法拼音不再「错位」到输入框边框上
现象:打中文时,拼音预编辑串和候选窗浮在输入框顶边框那一行上,字上屏后才落回输入行。
根因:终端把 IME 预编辑串画在硬件光标所在位置,而上一版为了保 resize 清扫的定位不变量,把硬件光标永久钉在了显示区第 0 行(正是框顶边框)。
修复:光标停放改成状态相关——平时停在输入文本行(拼音、候选窗回到正确位置),只在「resize 进行中」(首个宽度变化 → 停稳重放完成)临时钉到第 0 行保住清扫不变量。拖窗口的时候没人打字,两头都不吃亏。
模型回复超宽不再被截断
现象:回复里较长的行(长 URL、长句、无空格的中文长段)右边直接消失,看起来「文字没显示全」;缩放窗口后更明显。
根因(两层,叠加起来):内联显示的 println 是定宽渲染、超宽截断,而助手正文、错误行、工具摘要这些打印路径从未按终端宽度折过行(此前只有用户消息块折行);resize 停稳重放时又把按旧宽度成形的行按新宽度再截一刀。
修复:新增样式感知的显示宽度折行(中文按 2 列、宽字符不切半、样式跨拆分点保留),三处接入——
- 助手正文/流式整行:渲染后折行、逐段悬挂缩进下沉;
- 输出出口兜底:错误、工具行等所有路径的超宽行一律先折再打;
- resize 重放:留底存折行前的原始行,重放按当前宽度重新折——拖窄不丢内容,拖宽还会把之前折开的行接回一行。
代价(有意的取舍):欢迎横幅这类为 ≥52 列设计的框,在更窄的终端下折行后边框会断开(此前是右侧被静默截掉)——内容保全优先,拖宽回去自动复原。
🔧 工程
- 全模块版本号 1.8.0 → 1.8.1。
- 全量测试:
springai-code-tui1383 用例通过(新增清扫器/停稳判定器结构与契约断言、光标停放的离屏渲染断言、折行的内容保全契约等回归)。 - pty 实机冒烟
resize_smoke.py全绿,断言升级为协议契约级:停稳后必发抹回滚缓冲序列(ESC[3J)、硬件光标必回输入文本行、resize 前后历史内容一字不丢(重放合法地重折行数,故断内容不断行号)、78 列长 URL 行全程完整。 - 三项修复均经 Terminal.app 实机(AppleScript 驱动真窗口拖拽 + 抓屏/抓回滚缓冲)验证:三轮拖拽后回滚缓冲仅存一份干净对话,长行拖窄折两行不丢字、拖宽重新接回。
🔐 校验(SHA-256)
0663d530eda81c237e99e3da6faa4226fa7458589a67b1cc3046009776b2996d springai-code-tui-1.8.1-dist.tar.gz
7d55ee8613401c032d4495d82812c894a4b0618e9ff0ce3f2e1e38f1fce2df0c springai-code-tui-1.8.1-dist.zip
shasum -a 256 -c <<'EOF'
0663d530eda81c237e99e3da6faa4226fa7458589a67b1cc3046009776b2996d springai-code-tui-1.8.1-dist.tar.gz
7d55ee8613401c032d4495d82812c894a4b0618e9ff0ce3f2e1e38f1fce2df0c springai-code-tui-1.8.1-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖。
环境:JDK 17+,macOS / Linux / Windows。
v1.8.0 —— 后台子 agent + 回合中插话
springai-agentdemo v1.8.0
在 v1.7.0 基础上的功能版(minor)。核心交付物仍是终端编码智能体 springai-code-tui。
本版主题是**「不用干等」:两件事让你在 agent 忙的时候仍然能动——后台子 agent(派出去的活不占着回合)与回合中插话**(想说的话不用等它跑完)。第三件是记住上次用的模型。
⚠️ 先读这一条:权限行为又变了
BYPASS(跳过全部权限检查)现在进了Shift+Tab的循环,不再需要--dangerously-skip-permissions启动参数。四档平权:默认→自动接受编辑→计划模式→跳过全部权限检查。那个启动参数此前把两件事合并成了一个布尔:启动时能否起步于 BYPASS、运行期能否切进 BYPASS。前者该受启动参数管,后者不该——为了临时放开一次就得重启进程,代价不合理。参数本身保留,语义收窄为「启动即起步于该档」。
这是安全性的净下降:此前不带那个参数启动的进程,运行期无论如何按不进 BYPASS;本版起按三下
Shift+Tab就能进。而 v1.7.0 已经把这一档改成了真的跳过全部检查(内置底线与 ask 规则都不执行,只剩你自己写的 deny 规则)。顺带修掉一句流传在三份文档里的假话:「BYPASS 也盖不住内置底线」。v1.7.0 起它就盖得住了,文档没跟上。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.8.0-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.8.0-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.8.0。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.7.0 的变化。
✨ 新功能:后台子 agent(run_in_background)
Task 与 ParallelTasks 多了一个 run_in_background 参数(默认 false,不传时行为与从前逐字一致)。传 true 时它立刻返回一个 task id,子 agent 在一个常驻线程池里跑:主 agent 可以接着调别的工具、接着说话、接着追派任务,你也能立刻提交下一条消息——后台任务不计入 busy 闸门。
> 让 explore 在后台把整个权限层的判定顺序摸一遍,你先接着改 README
⏱ 后台任务已启动 task_3f9ac21b · explore · 摸清权限层判定顺序
好,我先改 README……
结果怎么回来(两条路,互斥)
靠一个「已消费」标记互斥,同一个结果绝不会送两遍:
- 模型主动取——
TaskOutput(task_id),可选阻塞等待。超时返回的是「任务仍在运行」,不是失败:把「还没跑完」说成失败,模型会去重派一个一模一样的任务。 - 自动送达——空闲且输入框为空时程序自动起一个新回合,把已完成任务的结果交给模型。多个任务合成一条通知。
「输入框为空」是这条判据里最实用的一半:你正在打字时它不会插进来抢走回合。想让它现在就送,把输入框清空即可。
界面
- ⏱ 后台任务面板常驻输入框上方,列出全部后台任务(
▶运行 /✓完成 /✗失败 /⊘已终止 + 耗时 + 当前工具)。零任务时不占行。 /tasks打开管理面板:↑↓ 选择、Enter 展开结果、k终止(先确认)、Esc 关闭。任何时候可开——后台任务的意义就在于「回合还在跑的时候也能看一眼」。- 后台任务只有起止各一行进滚动区,它的工具活动与结果正文都不进——否则会一行行插进你与主 agent 的对话里。
Esc取消当前回合不碰后台任务。它们的生命周期与回合无关,这正是它们的用处。要停它去/tasks按k。/clear与退出(/exit与 Ctrl+C)终止全部后台任务,清理有界 2s。
⚠️ 权限:后台任务的 ASK 一律 DENY
这是这个功能最容易踩的地方。 后台任务走同一个权限引擎(判定顺序、规则、内置底线一字不差),差别只在最后一步:引擎判出 ASK 时,前台弹审批面板,后台直接拒绝。
因为它的 turnId 早已过期,审批请求会被 UI 的迟到过滤当场静默拒绝——模型只看到一个没有理由的失败,然后对同一个操作反复重试直到耗光回合。所以这里主动拒绝,并在工具结果里写明原因、当前档位与正确的下一步。这与 BYPASS 档「永远不停下来等人」是同一条设计推理:一个不会停下来等人的执行路径,必须把「等不到人」变成一个模型看得懂、能据此改道的结果。
让后台任务真干活有三条路:事先写窄 allow 规则(精度最高)、切到「自动接受编辑」档(实用的中间档)、或只派只读任务(explore / plan 天然如此)。
/continue 与 ListTasks:让模型也知道有哪些任务
/continue会把当前进程正在跑、以及已完成但结果还没送出去的后台任务一并告诉模型,目的只有一个:别让同一批活跑两遍。ListTasks(新工具,仅主 agent)让模型随时列出本进程的后台任务及状态。为什么需要它:TaskOutput的 task id 只存在于会话历史里那条Task的返回值中,/compact之后就被压掉了——没有它,模型能派后台任务却看不见自己派了什么。- 后台任务不跨进程:
-c恢复上次会话时,上个进程的后台任务已经全部结束(只活在内存里)。此时/continue会提醒模型重新派发,而不是对着早已作废的 task id 干等。
✨ 新功能:回合中插话
一个回合可能有十几轮工具调用、跑好几分钟。而人在工具循环中途开口,十有八九是看它跑偏了想纠正——等事都做完再听,这句话就从「纠偏」降级成了「善后」。
过去只有两个出口:干等到底,或按 Esc 砍掉整个回合(连已经跑对的部分一起丢)。现在有第三条路:不打断回合,把消息插进下一次模型调用。
| 你想 | 怎么做 | 等待 |
|---|---|---|
| 尽快让模型看到 | 直接 Enter(默认) |
当前这一个工具跑完 |
| 等它忙完再说 | /queue <消息> |
整个回合结束 |
| 别跑了,听我的 | Esc |
立即;未送达的插话放回输入框 |
界面:没走的钉着,走了才滚动
和排队消息同一套:
› 帮我看看这个模块
⏺ Glob {"pattern": "*"}
⎿ Glob ✓
› 换个思路 ← 送达那一刻才打出来,位置正好在工具结果之后
好,那我改成……
⤷ 还没送出去的那句 ← 未送达:钉在输入框上方,随时看得见
› 等下回合再发的那句 ← /queue 排队的,在它下面
两个面板的上下顺序即送达先后,行首符号也不同(⤷ vs ›)——不然分不清自己那句话什么时候会被听见。状态栏另有一个 · 插话 N 条 实时计数作为第二条反馈。
输入那一刻刻意什么都不往对话区打。 那时它还没送达,而对话区里的行事后改不了;打下去就永远停在「输入时」这个位置上,而它的真实位置在后面那条工具结果之后。于是屏幕顺序 = 发给模型的消息表顺序 = -c 回放顺序,三者一致。
消息插在哪里
合法位置只有一个:所有工具结果之后。落在 assistant(tool_calls) 与 tool 之间就是悬空 tool_calls,下一次请求直接被网关 400。而会话存储层看不到这个位置——工具结果落库与「构建下一次 prompt」是同一步。所以注入挂在 ChatModel 装饰器层,那一层拿到的消息表已经配平。
插话送给模型时会被 [interjection] 包裹并附一句行为指引(大意:用户在任务执行中插话,未完成的工作仍在进行;与当前方向冲突就调整,否则先把手头的做完)。不包的话,模型在 tool 结果之后突然看到一条 user 消息,很容易判定「上一轮结束了,这是新任务」,于是丢下没做完的活。回合结束时这条插话会按锚点补进会话历史——否则 -c 恢复和压缩之后,历史上就会出现一次「无来由的转向」。
什么时候会自动回落成排队
- 压缩中、以及回合已被 Esc 取消、只剩子 agent 在收尾——这两种状态不会再有模型调用,插话进去等于石沉大海。
- 本条消息挂了技能(
/skill)——插话是一条纯 user 消息,带不了技能参数。
与后台任务的接缝
TaskOutput(block=true) 是故意等下去的(默认上限 300 秒),而这一等发生在主 agent 的工具线程上——期间主 agent 一次模型调用都不发,而模型调用是插话的唯一送达点。不处理的话,「后台」两个字会被 block=true 抵消得干干净净:界面一切正常(回合在跑、⤷ 面板照常显示),但你那句话要等满 5 分钟。故那个工具的轮询每 200ms 顺路查一次插话队列,有就提前收工。
已知边界:模型对那句行为指引的实际反应未经真实 provider 验证(冒烟用的是桩模型)。指引措辞目前偏保守,倾向让它先做完再理你。
✨ 新功能:记住上次用的模型
/model 选中的模型记在 <项目根>/.codetui/model.json(单键 lastModel,已被 .gitignore),下次启动自动恢复。
- 按项目隔离——不同仓库各记各的。你在这个项目里习惯用推理模型、在那个项目里用快模型,不用每次开机重选。
- 与会话恢复正交:不带
-c的默认启动照样恢复模型。这正是它存在的理由——「换个话题重开」和「换个模型」是两件事,不该被绑在一起。 - 只记 modelId,不记 provider,与
/model面板本身的选择粒度一致(面板也只能按 id 选)。*_MODELS环境变量造成跨家重名时命中列表序靠前的可用家——已知限制,不是疏忽。 - 该模型已不可用时(比如你临时注释掉了那家的 key),回退到首个可用 provider 的默认模型并提示一行,但盘上那条记录不清——于是不再选一次的话每次启动都会重复那句提示。这是有意的:清掉的代价是「你只是临时跑了一次没带 key,回头 key 加回来记忆已经没了」。
- 写盘失败不影响使用,只在对话区提示「仅本次运行生效」。
与权限档位的处理刻意相反:权限档每次启动都回到默认,模型选择则记住。前者记错了会放大权限,后者记错了最多是模型不合手、一个
/model就改回来。
🔧 其它变化
- 启动不再等 MCP:MCP 连接改到后台进行。此前配了较慢的 MCP server 时,界面要等全部连上才出来。现在立刻可用,状态栏用
⟳ MCP 连接中 N显示进度,连上后工具自动进入可用集。 /continue会把后台任务摘要拼进提示词:正在跑的、以及已完成但结果还没送出去的后台任务一并告诉模型,避免同一批活跑两遍。ListTasks(新工具):让模型随时列出本进程的后台任务及状态。没有它的话,/compact压掉历史之后模型就看不见自己派了什么。- 忙时 notice 降级为后缀:此前一条 notice(如「已取消当前回合」)会整条盖掉状态栏,把正在跑的回合指示一起挡住。现在忙时它退成后缀,运行指示始终可见。
- 修文档假话:「BYPASS 也盖不住内置底线」这句在三份文档里流传,而 v1.7.0 起它就盖得住了。三份副本一并改正。
🧪 质量
mvn -pl springai-code-tui test → 1353 个用例,0 失败 0 错误,9 跳过。另有 9 个 pty 实机冒烟全绿(插话、后台任务、/clear、记忆、权限、模型记忆、编辑快捷键、附件、MCP 管理)——内联 TUI 的渲染缺陷单测原理上抓不到,只能开真伪终端读屏。
本版两个新功能的验收都压在冒烟上,这点值得写明:插话的接线(InterjectingChatModel.wrap() 那一行)摘掉后 1322 个单测一个都不红,只有冒烟里断「桩模型实际收到的请求体」那条会红——因为接线断掉时插话面板和状态栏计数照样正常显示,肉眼看界面完全正常,功能却已经死了。
⚠️ 安全声明
本版的权限变化是净下降,请重读文首那一条。 BYPASS 现在无需启动参数即可在运行期按进去,而它自 v1.7.0 起是真的跳过全部检查——内置底线与 ask 规则都不执行,只剩你自己写的 deny 规则。
后台子 agent 带来的新面:
后台任务在没有人看着的时候执行工具。 它走同一个权限引擎,且
ASK一律DENY(见上),所以默认档下它做不了需要审批的事。但切到「自动接受编辑」或写了宽 allow 规则之后,它就是在你不看屏幕的时候改文件。给后台任务写规则时请比给前台写得更窄。
其余(权限层不是沙箱、贴图会原样发给第三方模型 API、引用块防伪造不防提示注入)与 v1.7.0 一致。详见 code-tui README 的安全声明 与 SECURITY.md。
⬆️ 升级须知
- 无破坏性变更:旧会话、旧
permissions.json、旧mcp.json、旧技能目录照常可用。 - 不传
run_in_background时,Task/ParallelTasks的行为与 v1.7.0 逐字一致。 Enter在忙时的行为变了:此前是排到下回合,现在默认插话(尽快送达)。想要旧行为用/queue <消息>。--dangerously-skip-permissions的语义收窄为「启动即起步于 BYPASS 档」;「运行期能否切进 BYPASS」不再受它管——现在任何进程都能切。- 新增两个可选环境变量:
CODETUI_TASK_OUTPUT_TIMEOUT_SECONDS(TaskOutput(block=true)的等待上限,默认 300,钳在[1, 3600])与CODETUI_BACKGROUND_CONCURRENCY(后台子 agent 并发上限,默认 4,钳在[1, 32])。都非法即回落默认,绝不崩启动。 .codetui/model.json是新文件,已被.gitignore。不想要这个记忆的话删掉即可,下次/model会重新写。
🔐 校验(SHA-256)
db06c6dcfbfc3bb2511f29c15bd4cd524e46d6d366a07eb24f6b4c6d94f381d7 springai-code-tui-1.8.0-dist.tar.gz
aef6b927ad4fb45584c39e025139b27dce9b1994d91423710a7b38243100f395 springai-code-tui-1.8.0-dist.zip
shasum -a 256 -c <<'EOF'
db06c6dcfbfc3bb2511f29c15bd4cd524e46d6d366a07eb24f6b4c6d94f381d7 springai-code-tui-1.8.0-dist.tar.gz
aef6b927ad4fb45584c39e025139b27dce9b1994d91423710a7b38243100f395 springai-code-tui-1.8.0-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖。
环境:JDK 17+,macOS / Linux / Windows。
v1.7.0 —— 视觉输入 + BYPASS 名副其实
springai-agentdemo v1.7.0
在 v1.6.0 基础上的功能版(minor)。核心交付物仍是终端编码智能体 springai-code-tui。
本版两件事:让模型真能看见图片,以及把 --dangerously-skip-permissions 改成名副其实。
⚠️ 先读这一条:权限行为变了v1.6.0 里
--dangerously-skip-permissions并不真的跳过全部检查——内置底线与 ask 规则仍会弹窗。
那份发版说明白纸黑字写着「deny 规则与内置底线在该模式下仍然生效,这是刻意的」。本版起它真的跳过全部检查:内置底线与 ask 规则都不再执行,只剩你自己写的 deny 规则。
这是安全性的净下降。 换来的是那个开关不再说谎、以及半无人值守场景可用。详见下方「权限」一节。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.7.0-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.7.0-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.7.0。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.6.0 的变化。
✨ 新功能:视觉输入(图片真正进模型)
支持视觉的模型现在能真正看见图片,而不只是收到一行文件路径。
两个入口
你自己贴的图——输入框里直接写路径,或把文件从访达/桌面拖进终端:
> 这个报错界面是怎么回事 docs/bug.png
⏎ 已附带 1 张图片(bug.png) · Ctrl+X 取消
拖拽白送支持——终端不传文件、只把路径当粘贴插到光标处,所以走的是同一条路。带空格的中文文件名也认(macOS 截图默认就带空格):反斜杠转义、单引号、双引号三种形态都吃。
工具产的图——Read 一张 png、MCP 截图工具的返回,都会作为图片交给模型。
图片从不进会话记忆
落盘的永远是一段结构化文本引用块,图片字节只在出站请求组装的最后一刻才挂上,且只挂当轮的。
因此聊多久都不会累积上下文:聊 100 轮、看过 50 张图,请求里也只有当轮那几张。想让模型重看历史图片,让它 Read 引用里的 path 即可——那条路本来就通,不需要额外命令。
硬上限(这是设计的核心)
每请求 用户贴图 ≤3 张 + 工具产图 ≤1 张(最新那张) + 6k 视觉 token
每回合 累计兑现 ≤12 张·次
单回合视觉花费的绝对上限因此约 21.6k token——跑飞的截图循环也就到这儿。
用户图先过预算且保底不淘汰:反过来会让「照这张稿子改」的稿子被随后 Read 的图挤掉。
同一回合内每次工具迭代都会重传当轮的图。 这是无状态请求的固有代价——正文与全部历史每次也在重传。只能靠缩图压单价 + 回合上限封顶,无法消除。这不是「已优化」,是承认它治不了。
会误附,所以给了 Ctrl+X
裸路径自动识别不可能只在你想附图时命中。把 docs/bug.png 复制到 tmp/ 这种句子里,路径独立成词、文件存在、魔数是图片——三条判据全中,必然被误认为附件。
这个代价是设计时明确接受的:由「附件行当场可见 + Ctrl+X 一键撤销」承担,不靠规则去消灭它。加语法标记能消灭误附,但也就同时消灭了「拖进来即可」。
(用 Ctrl+X 而非 Ctrl+D:本项目实现了 readline 键位,Ctrl+D 在 readline 里是「删除光标处字符」/ 空行时 EOF;Ctrl+X 在 readline 里是前缀键、单按无动作,不撞肌肉记忆。)
⚠️ 某些全局热键会抢占按键,撞上时症状是「按了没反应」或弹出别的窗口,不是本功能坏了。已知一例:Chrome 的 Gemini 扩展把Ctrl+G注册成 OS 级别的全局热键,键在任何终端应用看到它之前就被拿走——取消键原定Ctrl+G,正是因此改成了Ctrl+X。
图片处理
| 格式 | 处理 |
|---|---|
| PNG / JPEG / GIF | 长边 >1568px 等比缩后再发(磁盘原件不动) |
| BMP / TIFF | 解码后转码成 PNG |
| WebP | 原样发、不缩(JDK 解不了但各家 API 收);>4MB 不兑现 |
| HEIC / AVIF | 发不出去,只留引用(HEIC 是 iPhone 照片默认格式,会真遇到) |
超过 5000 万像素的图直接不兑现——判定只读文件头拿尺寸,绝不先解码再判断(200MB 的 PNG 解成 BufferedImage 是 GB 级)。
项目内指原文件,项目外复制一份
- 项目内的图 → 引用块里就是原路径,不复制。你回头更新了这个文件,模型再看到的就是新版
- 项目外的图(拖进来的桌面截图)→ 复制一份进
.codetui/artifacts/。这不是偏好:引用块解析器会拒掉一切指向项目外的路径(防外部内容注入path: ../../../etc/id_rsa),不复制的话那张图会无声地永远兑现不了
.codetui/artifacts/latest.png 是最近一张图的稳定软链,可直接 open 查看(内容寻址的文件名是 64 位 sha,人不好复制)。
逃生口与开销可见
export CODETUI_VISION=off # 全局关闭整条视觉链路/context 面板单列视觉占用(本回合几张、约多少 token、每回合上限多少),不混进文本估算——图片从不进会话存储,两个数字合并只会让「为什么请求比面板大」变得无法解释。
⚠️ 验证范围(别当成「五家都能用」)
「工具结果 → 合成一条 user 消息(带图)」这个消息序列,只在一个本地兼容中转网关 + gpt-5.6-sol 上做过真机验证:发纯红/绿/蓝三张图,模型三次全答对;对照组(同样序列但不挂图)明确回答「只看到图片文件引用,无法看到实际图像内容」。
api.openai.com 原生端点,以及 Anthropic / 千问 / 智谱三家,完全没有验证过(本机没有对应 API key)。它们第一次真用时仍可能返回 400。
另外:千问与智谱的内置模型清单里没有一个视觉模型(qwen3.7-max / glm-5.2 这些都不是),要在这两家用视觉得自己用 DASHSCOPE_MODELS / ZHIPU_MODELS 配一个 -vl / glm-4v 系的 id。
不支持的场景
- 终端里显示不了图片:模型截的图你只会看到一行路径,要看得自己开文件(系统提示已禁止模型写
这类 markdown——那在终端里只是一串原始文本) Ctrl+V粘贴剪贴板图片不支持(三个平台各一套外部命令,无头环境测不了)Bash生成图片文件不会自动产生引用:输出里出现一个路径不代表模型想看它;想看就Read它.codetui/artifacts/超过 500MB 时按最旧优先删除,只在启动时扫一次- 视频不投递:
supportsVideoInput是保留字段,恒为 false
⚠️ 权限:--dangerously-skip-permissions 现在名副其实
变了什么
| v1.6.0 | v1.7.0 | |
|---|---|---|
| deny 规则 | 硬拒 | 硬拒(不变) |
| 内置底线 | 强制询问(弹窗) | 不再执行 |
| ask 规则 | 询问(弹窗) | 不再执行 |
关键性质:这一档下永远不会停下来等人。 deny 命中时直接拒绝、把结果告诉模型(不弹窗),回合继续。
为什么改
v1.6.0 的设计原则是「护栏不是牢笼,人确认了就该能做」——那句话本身没错,但它默认了总有人在场。
有人在 TUI 里跑半无人值守的自动化开发(丢一个大任务给 agent 然后离开),发现即使开了这个参数仍会卡死等人应答:权限握手阻塞在无超时的队列上,只有人来点或回合被取消才能解开。
而更根本的问题是:一个名字里带 dangerously 的开关,用户已经承担了全部心理成本去打开它,结果发现它是假的。这比多弹几次窗伤害大得多。
为什么 deny 规则保留
三者来源不同:内置底线是本项目的意见(写死在代码里),deny / ask 规则是你写在 permissions.json 里的明令。BYPASS 的字面意思是「跳过审批」,不等于「无视我立过的禁令」。而且 <项目根>/.codetui/permissions.json 是仓库带来的——clone 别人的仓库时那些 deny 规则本来就是保护你的。
ask 一并跳过,是因为它的语义「每次都问我」与 BYPASS 的「不问」直接矛盾,此时按 BYPASS 走是唯一自洽的解释。
放弃拦截,但不放弃告知
BYPASS 放行一个踩到内置底线的操作时会留痕——即时打一行进对话区,回合末再汇总一次:
⚠ 本回合 BYPASS 放行了 3 个通常需要确认的操作:
· 写入 .git/ 内部:/p/.git/hooks/pre-commit
· 读取凭据:~/.aws/credentials
· rm -rf 变量目标:$BUILD_DIR
不阻塞、不询问,只让你回来时看得见不在期间发生了什么。
留痕只进对话区,/clear 或滚出屏幕就没了,不落盘。
想在无人值守下保留某些禁令怎么办
自己写 deny 规则进 permissions.json——那是这一档下唯一还拦得住的东西:
{ "deny": ["Write(~/.ssh/**)", "Bash(rm -rf ~:*)"] }另外三档一个字没变
默认 / 自动接受编辑 / 计划模式 三档的判定完全不变,内置底线在那里照常强制询问。
🔧 其它变化
- 联网测试改为显式开关门控(
CODETUI_LIVE_TESTS=1)。此前它们按「对应 API key 存在」放行,而 key 存在是正常安装的常态——于是每次mvn test都真的联网、花钱、看运气,其中一条还带 60 秒墙钟硬上限,约一半概率把构建打红、挡住打包。
⚠️ 安全声明(本版必须重读的部分)
上一版说过「权限层不是沙箱」,本版要再加一句:
--dangerously-skip-permissions下没有最后一道询问了。 提示注入把模型说服去rm -rf ~时,只有你自己写的 deny 规则能拦。留痕能让你事后发现,阻止不了。
视觉输入也带来一条新的外泄通道:
你贴的图会原样发给第三方模型 API。 截图里若有 API key、token、私密聊天记录、身份证件,它们就此离开这台机器——而且是以图像形式,本地任何基于文本的检查都看不出来。而路径是自动识别的,「只是提了一嘴文件名」的句子同样会附上。
引用块这条注入路径建了防线(path 须过项目根包含校验、只扫用户消息与工具结果、字段不齐或重复整块丢弃、文件名清洗控制字符),但它防的是「引用块被伪造」,不是「模型被网页里的话说服去做别的事」。
详见 code-tui README 的安全声明 与 SECURITY.md。
⬆️ 升级须知
- 无破坏性变更:旧会话、旧
permissions.json、旧mcp.json、旧技能目录照常可用。 - 但
--dangerously-skip-permissions的行为变了(见上)。如果你此前依赖「BYPASS 下危险操作仍会问我」,那个保障没有了——把需要的禁令写成 deny 规则。 - 视觉是加法:不用视觉模型、不贴图,行为与 v1.6.0 完全一致。
CODETUI_VISION=off可全局关闭。
🔐 校验(SHA-256)
0302e5066dd11357f295b27385c3d24ca3d5743681a7402b246e07e4b9423f17 springai-code-tui-1.7.0-dist.tar.gz
a6b7b62b124380857c2b6f7044d40ee994946bea2628903eb70bfd67143f9899 springai-code-tui-1.7.0-dist.zip
shasum -a 256 -c <<'EOF'
0302e5066dd11357f295b27385c3d24ca3d5743681a7402b246e07e4b9423f17 springai-code-tui-1.7.0-dist.tar.gz
a6b7b62b124380857c2b6f7044d40ee994946bea2628903eb70bfd67143f9899 springai-code-tui-1.7.0-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖——视觉链路全部用 JDK 自带的 ImageIO 与既有的 Apache Tika。
环境:JDK 17+,macOS / Linux / Windows。
v1.6.0 —— 权限管理
springai-agentdemo v1.6.0
在 v1.5.0 基础上的功能版(minor)。核心交付物仍是终端编码智能体 springai-code-tui。
本版只做一件事,但做得很深:权限管理。此前 code-tui 对模型开放文件系统与 shell 且零拦截——模型说改就改、说跑就跑。本版加入一层「执行前确认」:有副作用的工具调用在真正执行之前被拦下交给你拍板,并可用规则一次性放行常用动作。另加计划模式:模型只能读和查,产出计划经你批准后才动手。
⚠️ 这是本项目行为变化最大的一版。 升级后默认会弹审批面板——习惯「说一句就自动干完」的用户会明显感到多出确认步骤。若要回到旧行为,Shift+Tab切到「自动接受编辑」,或用--dangerously-skip-permissions启动(但 deny 规则与内置底线在该模式下仍然生效,这是刻意的)。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.6.0-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.6.0-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.6.0。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.5.0 的变化。
✨ 新功能:权限管理
审批面板
有副作用的调用在执行之前被拦下(拦截点在工具装饰链最外层,所以被拒的调用根本没开始跑,界面上也不会先冒出一行「工具运行中」再变失败):
⚠ 需要授权:Bash
git push origin main
↑ 命令的第 1 段 `git push origin main` 不在自动放行范围内
↳ 允许后将记下规则:Bash(git push origin main)
❯ 1. 允许一次
2. 允许,本会话不再问
3. 允许,永久
4. 拒绝,让模型换个做法
5. 拒绝并中断本回合
- 「拒绝」与「中断」是两件事:选 4 只把一条拒绝结果喂回模型、回合继续,模型据此换做法;选 5(或 Esc)才结束整个回合。
- 建议规则刻意取最窄形态:路径 → 该文件本身;命令 → 首词安全时给前缀(
Bash(mvn test:*)),首词能跑任意代码时(git/bash/python/rm…)只给整串字面量;网络 → 域名前缀。 - 命中内置底线时只有 3 项(去掉「本会话/永久」),并说明为什么——那两项在此处是谎言,任何 allow 规则都消不掉内置底线的询问。
计划模式(Shift+Tab 第三档)
Shift+Tab 在「默认 / 自动接受编辑 / 计划模式」间循环,当前档位常驻状态栏行首(默认档不占位)。
计划模式下只放行只读调查,写与命令一律拒绝(不是询问——能当场批准就等于没有计划模式)。模型改调 ExitPlanMode 交一份 markdown 计划,正文渲染进对话区,面板只放三个选项:批准并自动接受编辑 / 批准并逐个确认 / 打回继续完善(可附一句意见)。
也可用 --permission-mode plan|default|acceptEdits 启动。该参数不接受 bypass——全放行只能由 --dangerously-skip-permissions 进,否则那道显眼的开关就有了一个不显眼的同义词。
规则文件 permissions.json
两层取并集:~/.codetui/permissions.json(用户)+ <项目根>/.codetui/permissions.json(项目)。
{
"defaultMode": "default",
"allow": ["Bash(mvn test:*)", "WebFetch(https://docs.spring.io/:*)"],
"ask": ["Write(pom.xml)"],
"deny": ["Read(**/.env)", "Write(/etc/**)"]
}项目层只能收紧,不能放宽——~/.codetui/ 是你自己的配置,<项目根>/.codetui/ 是仓库带来的配置,clone 一个仓库不得让 agent 变得更宽松。据此关掉三条路径:defaultMode 两层都不接受 BYPASS(这文件 agent 自己写得到);项目层的 allow 不得是通配放行;项目层的 defaultMode 只能比用户层严。
/permissions 打开规则面板:↑↓ 选择、d 删除(先确认,删 deny 时提示明说「这会放宽权限」)、Esc 关闭。新增仍走审批面板的「允许,永久」。
内置底线(任何 allow 规则与 BYPASS 都盖不住)
排在 allow 规则之前,命中即强制询问:写 .ssh/.aws/.kube/.gnupg/.git/.codetui 配置、写 shell 启动文件与自动执行位置(LaunchAgents、.vscode/tasks.json、git hooks)、读私钥与凭据、rm -rf / 或 ~ 或变量目标(rm -rf $DIR 展开成什么无从得知)。检查穿透 sudo/env/xargs/bash -c 一类包装,也覆盖 cp/mv/curl -o/tar 等写目的地。
计划模式下这些操作直接拒绝而非询问——否则会出现「普通写操作被拒、而最危险的那批反倒是唯一能当场批准的」这种倒置。
同一个文件的多种写法
deny/ask 规则与内置底线会折叠大小写、并解析符号链接(含目标尚不存在的悬空链接),故 deny Write(/etc/**) 拦得住 /ETC/passwd(macOS APFS 上那是同一个文件),经链接指向 .ssh/ 的写入也认得出。
allow规则只认原写法,这是设计不是疏漏:deny 误拦你看得见、能调整;allow 误放行不可逆。所以放宽匹配只在「问一下无害」的方向上做。
子 agent 与 MCP
子 agent(Task/ParallelTasks)与 MCP 工具共用同一个权限引擎,因装饰链共用而自动纳管。并发触发时审批面板逐个弹。MCP 工具默认询问(未登记工具一律保守 ASK)。
计划模式下子 agent 会收到一段专属提示(说明自己处于只读调查阶段、把发现报告回主 agent 就是交付)——刻意不与主 agent 版统一,因为主 agent 版指向 ExitPlanMode,而子 agent 没有那个工具,照抄等于指一条走不通的路。
🔧 其它变化
- 输入框的
Shift+Tab与裸Tab现在都能到达应用:TamboUI 的焦点导航此前会吞掉它们,导致斜杠菜单的 Tab 补全与/mcp面板的 Tab 展开一直是死代码(无人发现,因为单测走的是绕过路由器的入口)。已解绑焦点导航。 - 「允许,永久」的确认行现在打出规则文件的绝对路径,且由真正写盘的那一侧在写完之后回报——此前是在写盘发生前就用完成时描述它,且写失败也不会更正。
⚠️ 权限层不是沙箱
必须分清它是什么、不是什么:
权限层管的是「要不要做这一步」,不是「能做到多远」。 一旦你按下允许,那次调用照样以你的用户权限执行、不受任何目录边界约束。
它降低的是「模型自作主张、或被提示注入后无声搞破坏」的风险;它不降低「你自己按了允许」之后的风险,也不是隔离容器。已知弱点(诚实列出,别高估它):
- 审批疲劳是最现实的风险——弹得多就容易一路按「允许」,那与没有权限层无异。
- 内置底线是黑名单不是白名单:没枚举到的自然漏过。
- 硬链接认不出:
ln ~/.ssh/id_rsa /tmp/x之后/tmp/x路径上没有任何可疑之处,文件系统也不提供同 inode 反查。 - 在
$HOME下直接运行时,「系统位置」检查对整个家目录失效(家目录整体豁免是设计如此)。 - 只读操作不询问(
Read/Grep/Glob),读私钥/凭据仍被内置底线拦下,但「读了什么」整体不在审批范围内。
详见 code-tui README 的安全声明 与 SECURITY.md。
⬆️ 升级须知
- 无破坏性变更:旧会话、旧
mcp.json、旧技能目录照常可用;不配任何permissions.json也能跑(那时一切按模式默认判定)。 - 但默认行为变了:升级后会开始弹审批。要回到接近旧行为,
Shift+Tab切到「自动接受编辑」(工作区内的改动自动放行),或--dangerously-skip-permissions(deny 规则与内置底线仍生效)。 - 建议:先在一个可随意丢弃的项目里跑几轮,把高频动作用「允许,永久」记进
permissions.json,之后就不会再被打扰。
🔐 校验(SHA-256)
4564837e8c5f8e82ed30deefb3541db80efc166e4edbb810cf600d6e4614699c springai-code-tui-1.6.0-dist.tar.gz
baa8a34e794253af127e90c28e3e62940784bd7a8e0fe57e6f19b3fdc3829607 springai-code-tui-1.6.0-dist.zip
shasum -a 256 -c <<'EOF'
4564837e8c5f8e82ed30deefb3541db80efc166e4edbb810cf600d6e4614699c springai-code-tui-1.6.0-dist.tar.gz
baa8a34e794253af127e90c28e3e62940784bd7a8e0fe57e6f19b3fdc3829607 springai-code-tui-1.6.0-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖——权限层全部是本模块自写代码。
环境:JDK 17+,macOS / Linux / Windows。
v1.5.0
springai-agentdemo v1.5.0
在 v1.4.0 基础上的功能版(minor)。核心交付物仍是终端编码智能体 springai-code-tui。本版三大新增:联网搜索(博查 + Brave 两家共存,模型按内容语言自选)、MCP 远程传输(Streamable HTTP,可连 Context7 一类远程 server)、Claude Opus 5 接入并设为 Anthropic 默认模型。无破坏性变更,建议所有用户升级。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.5.0-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.5.0-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.5.0。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.4.0 的变化。
✨ 新功能
联网搜索:BochaWebSearch + BraveWebSearch
智能体补上了「找 URL」这一步——此前只有 webFetch(已知网址抓正文),现在能先搜再抓。两家可共存,由模型按内容语言自选,各自独立门控:
| 工具 | 启用条件 | 定位 |
|---|---|---|
BochaWebSearch |
BOCHA_API_KEY |
中文内容、国内站点、中文技术社区;返回长摘要,常常一次就够 |
BraveWebSearch |
BRAVE_API_KEY |
英文技术文档、GitHub issue、英文新闻;返回简短描述 |
- 都不配则两个工具都不注册,系统提示里也不出现任何搜索相关字样——模型不会去调一个不存在的工具。行为与升级前完全一致。
- 条数由
BOCHA_SEARCH_COUNT(默认 8,范围[1,50])/BRAVE_SEARCH_COUNT(默认 5,范围[1,20])控制,不暴露给模型,避免模型乱开条数烧搜索额度。 - 系统提示的搜索指引按两家的注册状态四态渲染(都无 / 只有博查 / 只有 Brave / 两家都有)。
- 两家真机均已验证。
⚠️ BraveWebSearch的查询词发往 Brave(美国公司),属于数据出境,与博查(国内服务、内容合规过滤)性质不同。按需自行取舍是否配置该 key,详见 code-tui README 的安全边界章节。
MCP 远程传输:Streamable HTTP
MCP 从「只能连本地 stdio 子进程」扩展到可连远程 server:
{
"mcpServers": {
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp",
"headers": { "Authorization": "Bearer ${CONTEXT7_API_KEY}" }
}
}
}type认"http"与"streamable-http"两种拼写;"sse"(旧标准,官方已 deprecated)暂未支持,配了会记 WARN 并跳过。headers的值支持${ENV_VAR}插值:token 留在环境变量里,配置文件只写引用,便于多机共用同一份mcp.json。引用了未定义的变量 → 整条 server 跳过并记 WARN,而不是带着字面量${TOKEN}去请求(那只会换来一个看不懂的 401)。url写完整端点地址,内部拆成 baseUri + endpoint 两段;非http/https绝对地址会在解析期被跳过。- 已有的
/mcp运行期管理、工具名前缀、失败隔离、子 agent 注入等机制全部适用——McpClientManager/McpRegistry//mcp面板一行未改,印证了 v1.3.0 留下的传输扩展点。 - 真机验证:连 Context7 完成握手并拉到工具列表。
Claude Opus 5
Anthropic 新增 claude-opus-5 并设为该家默认模型(1M 上下文 / 128k 输出)。清单调整为 claude-opus-5(默认)/ claude-fable-5 / claude-sonnet-5 / claude-haiku-4-5 / claude-opus-4-8(上代,保留)。ANTHROPIC_MODELS 仍可自定义覆盖。
🔧 工程
-
全模块版本号 1.4.0 → 1.5.0。
-
全量测试:
springai-code-tui537 用例通过,相对 v1.4.0 的 460 条新增 77 条:来源 条数 博查搜索工具(解析/渲染/降级/钳制/错误透传) 26 搜索工具接线、注册名、系统提示指引四态 15 RenamedToolCallback+TimeLimitedToolCallback8 ${ENV_VAR}插值7 MCP 配置解析(http 变体、非法 URL、插值接入) +7 MCP 传输构造与 URL 拆分、鉴权头落头 +9 真机冒烟(博查 / Brave / MCP HTTP,均 env 门控) 3 MCP 工具快照注入回归( 8813a1b,随扩展分支合入)2 -
三组 spec + 实施计划入库(
docs/superpowers/):网络搜索接入、第二家搜索后端 Brave、MCP Streamable HTTP 传输。 -
新增两个通用工具装饰器:
RenamedToolCallback(改注册名 + 换描述)与TimeLimitedToolCallback(给没有自带超时的库工具兜总超时)。 -
-Pdist产出 1.5.0 运行包。
📦 仓库模块
| 模块 | 说明 |
|---|---|
| springai-code-tui ⭐ | 终端编码智能体(本次发布的可下载运行物)。 |
| springai-core-demo | Spring AI 原始 API 教学。 |
| springai-agent-demo | 智能体教学:工具调用、多步 agent、会话记忆等。 |
| springai-boot-demo | Spring Boot 自动装配版对照。 |
| springai-jline-demo | JLine 终端交互基础示例。 |
demo 模块请 clone 源码后
mvn运行,见各模块 README;下载包只含springai-code-tui。
仓库瘦身:此前短暂入库的
english-syntax-extension/(Chrome 英语句法学习扩展)已迁出到独立仓库,本版起不再包含在本仓库中。它从未随任何发布版分发过。
🔐 校验(SHA-256)
5945d693bda324dd02f2351f6e9b3ef72b0a95b3a1fe217cfea5dd81abe1d20b springai-code-tui-1.5.0-dist.tar.gz
5bc9db096d5cd7323dc809cf37364eea226c6112de3a356163d45dfcb774545f springai-code-tui-1.5.0-dist.zip
shasum -a 256 -c <<'EOF'
5945d693bda324dd02f2351f6e9b3ef72b0a95b3a1fe217cfea5dd81abe1d20b springai-code-tui-1.5.0-dist.tar.gz
5bc9db096d5cd7323dc809cf37364eea226c6112de3a356163d45dfcb774545f springai-code-tui-1.5.0-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入新的第三方依赖——两个搜索工具与 MCP HTTP 传输均复用既有依赖树(spring-web、spring-ai-agent-utils、mcp-core)。
环境:JDK 17+,macOS / Linux / Windows。
v1.4.0
springai-agentdemo v1.4.0
在 v1.3.1 基础上的功能版(minor)。核心交付物仍是终端编码智能体 springai-code-tui。本版三大新增:通义千问 provider(含流式工具调用分片修复)、*_MODELS 可配置模型清单、/mcp 运行期 MCP 管理(启用/禁用即时生效 + 配置回写)。无破坏性变更,建议所有用户升级。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.4.0-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.4.0-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.4.0。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.3.1 的变化。
✨ 新功能
通义千问 provider(百炼 OpenAI 兼容通路)
第 5 家对话模型接入:设 DASHSCOPE_API_KEY 即激活 QwenProvider(默认 qwen3.7-max,另有 3.7-plus/3.6-flash/qwen3-coder-next),复用 spring-ai-openai 走百炼 OpenAI 兼容端点(DASHSCOPE_BASE_URL 可自定义,默认 .../compatible-mode/v1)。/model 运行时切换、子 agent provider:model 跨 provider 路由、统一 read 超时等既有机制全部适用。
- 修复流式工具调用即崩:百炼兼容模式的流式
tool_calls增量分片带"id":""(空串而非缺省),会炸上游 ChunkMerger。以 SSE 归一化装饰器在传输层抹平(空串 id 视同延续分片),工具调用全链路可用。 - 真机验证:新增流式工具调用真机冒烟测试(
DASHSCOPE_API_KEY门控,默认跳过),防兼容通路回归。
*_MODELS 可配置模型清单
五家 provider 的模型清单不再写死:DEEPSEEK_MODELS / ZHIPU_MODELS / DASHSCOPE_MODELS / ANTHROPIC_MODELS / OPENAI_MODELS 环境变量(逗号分隔,首项为默认模型)即可增删排序 /model 可选项——新模型发布当天即可用,无需等版本更新。未设置时沿用内置清单,行为不变。
export DEEPSEEK_MODELS=deepseek-v4-pro,deepseek-v4-flash/mcp 运行期 MCP 管理
MCP server 从「启动定生死」升级为运行期可管理:
- 管理面板:空闲时输入
/mcp,列出两层配置(用户级~/.codetui/mcp.json+ 项目级)的全部 server——含enabled:false与连接失败项,标注来源层[项目级]/[用户级]、状态(已连接/已禁用/连接失败+原因)、工具数;Tab展开工具清单,↑↓/1-9选择,Esc关闭。 - Enter 启用/禁用,即时生效:禁用立刻摘除其工具并后台关连接;启用后台连接(不卡界面),成功即注入——下一回合模型即见/不见其工具(主 agent 每回合动态快照 + 子 agent 委派时同步生效,allow/deny 过滤照常适用)。连接失败的 server 可在面板直接重试启用。
- 持久化回写:切换原子回写该条目所属层
mcp.json的enabled字段,重启后保留;回写失败降级为「仅本次运行生效」并提示,绝不丢配置文件。
设计与实施记录见 docs/superpowers/specs/2026-07-16-mcp-runtime-management-design.md。已知边界:管理粒度为整 server;新增/删除条目或改 command/args 仍需重启(面板条目集在启动期定型)。
🔧 工程
- 全模块版本号 1.3.1 → 1.4.0。
- 全量测试:
springai-code-tui460 用例通过(新增千问 SSE 归一化 /*_MODELS解析与装配 / McpRegistry 启停与回写 / 子 agent 动态注入 //mcp面板交互等回归);pty 实机冒烟mcp_manage_smoke.py通过(真实 stdio server 启停 +mcp.json回写 + 无孤儿进程);-Pdist产出 1.4.0 运行包。 - 三组 spec + 实施计划入库(
docs/superpowers/):千问 provider、可配置模型清单、MCP 运行期管理。
📦 仓库模块
| 模块 | 说明 |
|---|---|
| springai-code-tui ⭐ | 终端编码智能体(本次发布的可下载运行物)。 |
| springai-core-demo | Spring AI 原始 API 教学。 |
| springai-agent-demo | 智能体教学:工具调用、多步 agent、会话记忆等。 |
| springai-boot-demo | Spring Boot 自动装配版对照。 |
| springai-jline-demo | JLine 终端交互基础示例。 |
demo 模块请 clone 源码后
mvn运行,见各模块 README;下载包只含springai-code-tui。
🔐 校验(SHA-256)
f75415bdeed1ff134402052e29104dfe8101e4d26e533a5403c284cfde6fce17 springai-code-tui-1.4.0-dist.tar.gz
8e3a9dcf1a5639bbccaa3a603e58553b8b3ba4c73621784efd337b32e852c676 springai-code-tui-1.4.0-dist.zip
shasum -a 256 -c <<'EOF'
f75415bdeed1ff134402052e29104dfe8101e4d26e533a5403c284cfde6fce17 springai-code-tui-1.4.0-dist.tar.gz
8e3a9dcf1a5639bbccaa3a603e58553b8b3ba4c73621784efd337b32e852c676 springai-code-tui-1.4.0-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。
环境:JDK 17+,macOS / Linux / Windows。
v1.3.1
springai-agentdemo v1.3.1
在 v1.3.0 基础上的缺陷修复 + 体验改进版(patch)。核心交付物仍是终端编码智能体 springai-code-tui。本版修复**子 agent 经代理网关时高概率「Error reading response」**的传输层问题,并为输入框补齐 readline 式编辑快捷键。无破坏性变更,建议所有 v1.3.0 用户升级。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.3.1-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.3.1-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.3.1。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.3.0 的变化。
🐛 修复
子 agent 改走流式传输,修复网关坏窗口致「Error reading response」
根因(curl 实测实锤):代理网关的非流式端点存在分钟级坏窗口,期间几乎 100% 请求返回 HTTP 200 + 空 body(一轮实测 8/8 全空),SDK 在 2xx 上直接反序列化即抛异常;同一窗口内流式端点持续正常(主 agent 走 .stream() 全天稳定)。call() 级重试无论退避多久都穿不过分钟级窗口——必须换传输路径。
- 流式桥接:
RetryingChatModel把子 agent 的阻塞call()桥接到delegate.stream(),用框架MessageAggregator聚合回单个ChatResponse(ToolCallingAdvisor流式路径同款聚合器,tool_calls完整保留)。在 ChatModel 层桥接对上层工具循环透明,重试保持单次 LLM call 粒度、不丢已完成的工具迭代。 - 空流守卫:聚合结果无文本且无工具调用视同瞬态失败重试,穷尽后抛出,绝不把空串静默交回主 agent。
- 重试降级为二道防线:按类名后缀
*InvalidDataException(provider 中立)+ Jackson「No content to map」匹配;cause 链上有 Interrupted/Cancellation 绝不重试(Esc 取消要立即退出)。 - 失败诊断不再丢根因:子 agent 失败时摊平整条 cause 链回给主 agent 模型,笼统顶层文本不再吞掉唯一有诊断价值的信息。
完整决策记录见 ADR subagent-streaming-bridge。
✨ 改进
输入框 readline 式编辑快捷键
长文本内光标不再一格格挪:
| 快捷键 | 作用 |
|---|---|
Ctrl+A / Ctrl+E |
跳到行首 / 行尾 |
Ctrl+← Alt+← / Ctrl+→ Alt+→,Alt+B / Alt+F |
按词左跳 / 右跳 |
Ctrl+W / Alt+Backspace |
删除前一个词 |
Ctrl+U / Ctrl+K |
删至行首 / 行尾(逻辑行为界) |
设计要点:移动按字母数字为词(可停标点两侧),删除按 readline unix-word-rubout 空白为界("-m" 整个删);CJK 按单字跳/删;刻意避开 Ctrl+H/I/J/M(控制字节与 Backspace/Tab/Enter 相同、终端下不可区分)。已知限制与操作键表见 springai-code-tui/README.md;设计决策见 ADR inline-input-editing。
🔧 工程
- 全模块版本号 1.3.0 → 1.3.1。
- 全量测试:
springai-code-tui413 用例通过(新增流式桥接聚合 / 空流重试 / 取消不重试 /getOptions()转发 / 词边界与端到端合成键等回归);pty 实机冒烟edit_shortcut_smoke.py四条路径通过;-Pdist产出 1.3.1 运行包。 - 新增两篇 ADR:
subagent-streaming-bridge、inline-input-editing。
📦 仓库模块
| 模块 | 说明 |
|---|---|
| springai-code-tui ⭐ | 终端编码智能体(本次发布的可下载运行物)。 |
| springai-core-demo | Spring AI 原始 API 教学。 |
| springai-agent-demo | 智能体教学:工具调用、多步 agent、会话记忆等。 |
| springai-boot-demo | Spring Boot 自动装配版对照。 |
| springai-jline-demo | JLine 终端交互基础示例。 |
demo 模块请 clone 源码后
mvn运行,见各模块 README;下载包只含springai-code-tui。
🔐 校验(SHA-256)
00071ba1928665d1228faf81dcb4156adbeca3ac96777b3405e6754815e5593c springai-code-tui-1.3.1-dist.tar.gz
eaeb6f72a0d5eee8b4c4056a3837993b3642903950f9239fba66a6c8bbdf5aab springai-code-tui-1.3.1-dist.zip
shasum -a 256 -c <<'EOF'
00071ba1928665d1228faf81dcb4156adbeca3ac96777b3405e6754815e5593c springai-code-tui-1.3.1-dist.tar.gz
eaeb6f72a0d5eee8b4c4056a3837993b3642903950f9239fba66a6c8bbdf5aab springai-code-tui-1.3.1-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。
环境:JDK 17+,macOS / Linux / Windows。