/loop 终于会实时倒计时了:CLI、TUI 和桌面端各看哪里

你挂了个 loop,让它每三分钟看一次部署状态,然后回去干活。十分钟后你瞟一眼终端,它显示的还是你刚开始时的那个词——next in 3m——于是你不得不问出每个 loop 用户最终都会问的问题:这东西还活着吗,还是已经悄悄停了?这个说不清的状态,就是 10 月 15 日合并的那个 PR 要消掉的东西,而且它一次性把三个界面都做了。
一段话讲清“挂着的 loop”是什么
/loop 会在你当前会话里按你给的节奏(或它自己挑的节奏)反复执行一段提示词:
/loop 5m check the deploy status and tell me if it's live yet
每次唤醒都是一轮普通的 agent turn,所以它读的是当前状态——最新的 CI 结果、队列深度、此刻的文件——而不是复述一份摘要。它能跑在 CLI、TUI、面板聊天、桌面端以及所有网关平台上,并且属于那个会话:结束条件可以是 agent 回的 LOOP_COMPLETE、--times N、--until <条件>、作为兜底预算的 loops.max_ticks(默认 100),或者你自己用 /loop stop 和 /loop pause。
问题所在:只画了一次的倒计时
旧行为是在 loop 被接受时打印一行记录——↻ Loop: next in 3m.——然后再也不动。桌面端的问题更隐蔽一点:loop 卡片的 next ~45m 是每次渲染时算一次的,所以那个数字就冻在卡片第一次画出来时的值上。你真正需要的信息(还有多久唤醒、现在是不是正在跑、是不是被暂停了)都存在会话里,只是没有任何东西去重绘它。
现在这三个界面各显示什么
CLI。 常驻工作停靠栏——本来就在跟踪 goal、子代理、进程和队列的那块面板——多了一行 loop:
↻ loop · every 3m · 1 run · next in 2m · <prompt>
倒计时按分钟跳,最后一分钟切到秒,所以空闲时的提示词最多每分钟重绘一次,而不是每秒一次。唤醒进行中显示 running now,被暂停时显示暂停原因。把停靠栏折叠起来,标题上会带一个精简徽标——↻ loop next in 2m——不用展开也能看到答案。停靠栏可见时,那行旧的静态 ↻ Loop: next in 3m. 记录会被去掉,因为两个一起留只是重复的噪音。
TUI。 hermes --tui 在 GoalBar 下面多了一条 LoopBar,文字一样,由会话快照驱动,配一个只在标签真正变化时才重绘的一秒时钟。
桌面端。 loop 卡片现在和 heartbeat 共用同一个一秒倒计时时钟——next 00:02:09——过了截止时间则显示 due — waiting for idle,而不是显示一个已经变成负数的数字。最后这个字符串是和 heartbeat 卡片共用的,所以它是被重命名成公共 key,而不是复制一份。
你不会注意到的那一半:网关必须先把它发出来
CLI 上做倒计时很直接——那个进程拥有自己的终端。TUI 里的 Ink 状态条和桌面端的卡片就不一样了:它们读的是网关发布的会话快照 session.control.update。而那个更新此前只在有人点桌面卡片上的按钮时才发布。你在聊天里敲一句 /loop 3m …,这个状态压根到不了本该渲染它的那些界面。
修复把发布动作挪到了 loop 状态真正发生变化的地方:owner 会在自动化斜杠命令之后、在本地会话回合后的 goal 与 loop 钩子之后、以及在 loop 唤醒触发或斜杠 tick 结算之后发布。旧的 tui_gateway 路径也补上了同样两处发布,让老客户端也能看到唤醒。
这个 PR 里最有意思的一课是:给一个活着的值加显示,很少只是“加显示”而已。如果这个值由别人发布,修复的一部分就是说服他在正确的时刻发布——否则你交付的是一个只对第一帧诚实的进度条。
怎么判断你手上有没有这个改动
这个改动在 10 月 15 日 UTC 15:34 合并,而 v0.22.2 那天早上就已经发布了,所以它目前只在 main 上——只存在于 main 的工作会随 v0.23.0 发布。实际检查方式:
hermes version
如果你在正式版上,停靠栏显示的还是那行静态文字。如果你今天就需要 loop 的可见性、又没法跑 main,可用的替代是 /loop status:它会按需报告节奏、已触发次数和距下次唤醒的时间——比瞟一眼多按一次键,但它确实回答了“还活着吗”。
在你依赖倒计时之前,有两个相关细节值得知道:唤醒是一轮普通的 user 角色回合,而 token 成本随节奏线性增长,所以一个没事可做的一分钟 loop 依然每分钟烧掉一轮——当你等的那个东西本来就按自己的节奏变化时,优先用自定节奏。另外,对于“必须熬过你关掉终端”的活儿,这是错误的工具:无人值守的排程属于 cron 任务,而 /loop 是给“你已经在其中的会话”做监控用的。
其他值得知道的
- 节奏背后的旋钮是
loops.min_interval_seconds(30)、loops.self_paced_floor_seconds(60)和loops.self_paced_ceiling_seconds(900);兜底预算是loops.max_ticks(100,0表示不限)。 - 活跃的
/goal拥有会话:当 goal 正被它的裁判推动时,loop 的唤醒会让位,等 goal 完成、暂停或停在等待栅栏上再恢复。 - 一个会话只能有一个 loop——设新的
/loop会替换掉旧的;要跑多个 loop 就得开多个会话(或者用 cron)。 - 在桌面端和面板上,带活跃 loop 的会话不会被“断连会话回收器”收走,所以你关掉窗口后唤醒仍在继续——前提是后端进程本身还活着。
- 在唤醒回合上按 Ctrl+C 是暂停 loop,而不是销毁它;
/loop resume可以接着跑。
如果你还不熟悉 /loop 本身,loop 命令指南讲清了这个倒计时所依附的基础。
来源:PR #139187(/loop shows a live countdown to the next wakeup on CLI, TUI and Desktop,2026 年 10 月 15 日合并)及其验证表;命令、节奏模式、配置项与 /loop//goal/cron 对比来自 main 分支上的 website/docs/user-guide/features/loops.md。