fix(traex): 兼容新版活动状态避免假空闲 - #1075
Conversation
感谢这个 PR——方向我认为是对的: 🔴 1. arm-2 会让已 idle 的会话被命令历史误唤醒(PRE/POST 已对照)PR 里说 arm-2 的结构约束能让 因为同一个 pattern 也接到 PRE/POST 只换 🟠 2. arm-1 只认
|
| 0.201.5 | 0.201.6 | |
|---|---|---|
" to interrupt" |
14 | 14 |
keymap/chords.rs |
1 | 1 |
⋄✧◇✦◈❖ |
1 | 1 |
PreToolUse |
74 | 73 |
所以「0.201.6 reintroduced / added」更像是这两种形态一直存在、只是此前未被 adapter 覆盖。建议改成「补齐既有未覆盖形态」,以免下次 review 依赖一个不准的版本边界。
补充一点可能对写注释有用:整句 esc to interrupt / Running N PreToolUse hooks 在二进制里 grep -F 都是 0 命中,因为 Rust 在运行时 format! 拼接,只以碎片 + 模板 + 来源路径存在(keymap/chords.rs、符号 status_indicator_widget::fmt_elapsed_compact)。这也意味着 master 注释里那句「grep 返回 0,所以 TraeX 删掉了 esc to interrupt」本身就是坏探针得出的结论——你推翻它是对的,只是理由可以更硬。顺带提醒:strings 会切掉多字节字符,验图元要直接 grep 原始二进制。
✅ 已验证没问题的部分
- 有牙:删掉两条 arm → 3 red;把 arm-2 削成裸
esc to interrupt→ 2 red(负向断言真的在防误触发)。 - 修法在目标形态上确实生效:带 composer 的整屏重绘、2 空格缩进、行内 ANSI 颜色、CJK 描述超 240 字符,全部判 busy。
- 无 ReDoS:120K 字符对抗输入 < 0.2ms(有界量词有效)。
- 无残留唤醒:真 idle 后 tail 里的旧 footer 不会误唤醒(
feed会清 tail)。 - 回归面:只动 TraeX 专属常量,未触碰共用
IdleDetector或其它 adapter。 - 全量 unit:8 failed / 18927 passed,失败集中在
mojo-launcher-env-quarantine/plugin-mcp-sandbox/setup-open-platform-automation;把traex.ts换回 master 后同样 3 个文件同样失败 ⟹ 与本 PR 无关(宿主 env 泄漏类既有问题)。
|
[自动评审·复审补充](机器评审初步意见,以维护者审阅为准) 首审指出 本地复核(0.201.6 二进制):
另确认:hook 事件枚举实际包含 改动面很小(一个字符类 + 一个括号约束 + 两条断言),不影响 PR 的其余设计与验证。建议作者按此微调后合入。 |
复审那条修正我复核后认同:我原先建议的「同括号」约束确实修不掉
|
| 变体 | 误命中 | 真命中 |
|---|---|---|
| A 原样(PR 现状) | 4/4 | 3/3 |
B 只去 ◆ |
2/4 | 2/3 |
| C 只加同括号 | 3/4 | 3/3 |
D 去 ◆ + 同括号 |
2/4 | 2/3 |
把 D(即复审建议的完整补丁)打进去后,两条幸存者走完整 IdleDetector 仍真的唤醒已 idle 会话:
◇ diff (12s): -◈ old (1m 1s • esc to interrupt) → REVIVED
◈ note: the footer reads (36m 20s • esc to interrupt) now → REVIVED
它们要求轮换图元出现在行首的非指示器内容里(diff/引用/正文),比 ◆ Ran 少见,但 --no-alt-screen 下正文与历史同屏,并非不可能。若要收干净,判据应该锚定「图元 + 紧随其后的活动描述 + 同括号 footer」这一整体,而不是只排除某个图元。
📌 一处容易漏掉的强制联动
「去掉 ◆」会让 PR 自带的正向断言 ◆ Evaluating task configurations (34m 19s …) 变红——所以这不是可选项,必须同时把该断言改成真实轮换图元(复审补丁里确实一起改了,这里显式点出,避免只改正则导致测试红)。我按 D + 断言联动实跑复现:494 passed。
✅ 顺带排除一条我本来想报的(避免误伤作者)
--no-alt-screen 下 scrollback 里旧的 indicator 行被重绘时会唤醒 idle 会话——但我在 master 上用 spinner arm 测同形态一样会唤醒(busy=1),所以这是既有的架构特性(viewport 探针 + 历史同屏),与本 PR 无关,不应计入本次改动。
|
[自动评审·复审收敛](机器评审初步意见,以维护者审阅为准) 接前两条评论:首审的 🔴(历史行误命中)与复审的修正(「同括号」不足、需去 arm-2 建议改为锚定整体语义单元(行首轮换图元 + 无括号活动描述 + 同括号 footer + 行尾收束): 相对原版的四点变化及其针对性:
验证:18 条对抗矩阵(含上述全部误命中形态 + 真 footer + 缩进/长 CJK/行尾 CRLF/尾随 ANSI 变体)全符合预期;对抗输入 <0.2ms 无 ReDoS;替换后 遗留请作者确认(不影响上述修复):arm-1 hook 行的 |
v3 我独立跑了矩阵:误命中侧 6/6 全封死、真 footer 侧 7/7 全保留(0 mismatch),含缩进 / CRLF / 长 CJK / footer 后接 composer 换行。前两轮的 🔴 与两条幸存者确实都被关掉了,收敛方向我认同。 但补两点,供作者决定取舍。
|
|
[自动评审·真机目击证据(改变前几轮的部分结论)](机器评审初步意见,以维护者审阅为准) 前几轮(首审🔴 → 同括号修正 → 去◆/v3/v4 讨论)全部建立在静态分析上。本轮我们在已登录的本机用 tmux PTY 真跑起了 TraeX 0.201.6 TUI,抓到了 turn 进行中的原始字节流。三个事实推翻或修正了此前共识: 1. 2. PR 现有图元类缺 3. 严格的行尾锚在真实 PTY 路径上不可用(撤回 v3 的 基于真机证据的 arm-2 收敛版(已在本仓库 worktree 验证 494 全绿): 相对 PR 原版的三点变化及依据:
已记录的接受残差: 验证:23 条对抗矩阵(真机目击形态正向 ×12 + 结构性误命中负向 ×8 + 边界)全符合预期;对抗输入 <0.2ms;替换后 仍无法本地目击、留作者确认:hook-runner 行(本机未配置 hooks)——arm-1 的 |
我独立把 TraeX 0.201.6 真跑起来复现了一遍(tmux PTY + 真机实测(stripAnsi 后的真实 PTY 流,14 次 footer 采样)关键: 所以「把 三点可直接落地的量化结论1. PR 现有图元类漏 2. 任何 三版正则在这条真流上的命中数:PR 原版 = 1,v3( (补一个细节:本流里 strip 后存活的转义只有 3. v6 在真机形态上全部符合预期。 我用真机抓到的 8 条形态(含 3 种轮换帧正向、
|
|
[自动评审·收口补充——独立捕获上的命中率数据](机器评审初步意见,以维护者审阅为准) 评审已收敛(见上一条评论),本条只补一组我方独立捕获的量化数据,供维护者校准对修复生效面的预期,不新增议题。 两段独立抓取的原始 PTY 字节流(已登录 0.201.6,真实 turn,共 22 次 footer 渲染),按 IdleDetector 同款 stripAnsi 处理后统计各版本对真实 footer 的命中数:
结论与上条评论一致且更强化:
(另注:我方捕获中 strip 后存活的转义为 合前清单维持:arm-2 换 v6 形态 + 联动改断言;arm-1 hook 行请作者给出真机证据或标注未验证。评审双方已无分歧,等待维护者。 |
评审双方已无分歧。补最后一组数据:把「生效面主要在屏幕快照路径」这个判断在屏幕路径上直接量化,并核对了它在代码里的依据。 屏幕快照路径实测:PR 80% → v6 100%
同一批样本里 7 种图元全部出现: 所以对维护者的取舍可以说得很干脆:屏幕路径上 v6 是把 80% 提到 100%,且不引入误命中( 关于「strip 时把 合前清单(双方一致,未变)
补充一句 |
Co-authored-by: TRAE CLI <traecli@bytedance.com>
2f8ef73 to
4ebaf41
Compare
|
感谢完整复核,已按最新真机证据收敛并更新本 PR。 真机验证我在独立临时 Git 目录中配置了两个各延迟 4 秒、无业务副作用的同步 hook,通过独立 tmux 启动 TraeX 0.201.6,并在 turn 前挂 实测确认:
实验后已关闭独立 tmux、删除临时目录/raw PTY/screen/hook 日志,并移除 TraeX 自动写入的临时项目 trust 记录。未修改全局 hook,未重启 Botmux;无新增软件安装。 代码调整
测试
更新 commit: |
新 commit 把前几轮的评审意见基本吸收了,方向我认同。先说结论:三条合前必修里有两条已修好,剩一条从"必修"降级为"请补一句依据"。 ✅ 已解决
|
| 形态 | 新版 | 旧 arm-1 |
|---|---|---|
⋄ Running 4 PreToolUse hooks (7s • esc to interrupt) |
✅ BUSY | ❌ MISS |
✦ Running 2 PreToolUse hooks(无 footer) |
❌ MISS | ✅ BUSY |
也就是说:hook 行只要有那个 footer,新版更好;一旦存在「hook 行不带 footer」的瞬间(例如 footer 尚未渲染的第一帧),新版就完全看不到它,而这正是旧 arm-1 覆盖的形态、也是 PR 最初描述里写的形态(✦ Running N PreToolUse hooks,无括号耗时)。
我自己没能目击 hook 行来判定:本机 5 条 hook 配置在册,但我用带 --dangerously-bypass-hook-trust 的真跑 + 自建慢 hook(sleep 9)都没能让 hook-runner 行出现(自建 hook 的 fire 标记始终没落地,说明我的探针没生效,不构成对 PR 的反证)。所以想请作者补一句:
- 那条
⋄ Running 4 PreToolUse hooks (7s • esc to interrupt)的目击方式(截图 / raw 抓包片段即可); - 以及是否见过不带 footer 的 hook 行。若见过,建议把旧 arm-1 以「7 帧图元 + 事件名通配」的形式保留一条(例如
[⋄✧◇✦❖◈◆][ \t]+Running[ \t]+(?:\d+[ \t]+)?\w+[ \t]+hooks?\b)作为纵深;若确认 footer 恒在,当前删除是合理简化,注释已写清楚就够了。
PR 描述里现在仍留着最初那句「新增 ✦ Running N PreToolUse hooks」的问题陈述,与实现已不一致(实现改成了统一 footer grammar),建议同步更新描述,避免后来人按描述去找那条 arm。
📌 一条明确记录的残差(我同意作者的取舍)
◈ note: the footer reads (36m 20s • esc to interrupt) now 现在断言为 true(会判 busy)。作者在注释里写明了原因:收紧它会误拒真实 PTY footer 重绘。我认同这个方向——误拒的代价(提前打 DONE,即本 PR 要修的原 bug)重于这类少见误唤醒,而且断言把它显式钉成了「已知接受」而非默默放过,这比留个隐式漏洞好。
🧪 验证
- 本地 rebase 到最新
origin/master(a421b4021)零冲突(未 force-push,未改作者提交;本地 rebase 结果cee503936仅用于验证) test/cli-adapters.test.ts+test/idle-detector.test.ts:494 passed- 全量 unit:19577 passed / 5 failed。失败为
mojo-close-failclosed、mojo-launcher-env-quarantine、plugin-mcp-sandbox、worker-terminal-read-auth.integration—— 无一与 TraeX 相关;这 4 个文件在干净 master 上单独跑同样 4 红(PR 侧单独跑也是同样 4 红),全量里多出的第 5 个是高负载下的时序 flake,与本 PR 无关 - 变异:图元类回退 → 3 red;删引号阻断 → 2 red(新断言有区分力)
- 无 ReDoS:240K 对抗输入 4.3ms
除上面那句 arm-1 的依据外,我这边没有阻塞项。
|
感谢增量复审。关于删除初版 arm-1,我补充如下证据和兼容性边界:
实际 hook 行的 glyph 会轮换,事件名也不限于 Pre/PostToolUse,但均与普通 activity row 共用 elapsed /
因此当前取舍是:保留 master 的全部旧规则,并用一条真机验证的 footer grammar 覆盖所有已观察到的 hook 事件与 7 个 glyph。PR 描述已同步更新,删除了“0.201.6 新增形态”和独立 |
|
[自动评审·最终确认——hook 行已目击,arm-1 删除安全](机器评审初步意见,以维护者审阅为准) 唯一开放项(hook 行是否带 footer)现已通过真机原始字节流目击关闭。在已登录 0.201.6 上配置慢 PreToolUse hook(sleep 15s)触发工具调用,pipe-pane 抓到的原始渲染序列: 即完整行形态为
最终验证(作者 head
评审双方零分歧,合前清单全部满足:① arm-2 统一 footer grammar(7 帧 + 结构约束,无行尾锚)② 断言联动有牙 ③ arm-1 已有真机证据。等待维护者审阅。 |
问题
TraeX 0.201.6 的真实 TUI 在 turn 仍运行时会保留 composer/context bar,同时渲染带活动 glyph、耗时及
esc to interrupt的 activity footer;hook-runner 也使用同一 footer 形态,例如:⋄ Running 4 PreToolUse hooks (7s • esc to interrupt)◆ Running 2 Stop hooks (21s • ↓ 73 tokens • esc to interrupt)◈ Working… (2s • esc to interrupt)现有 TraeX
readyPattern会命中仍可见的 composer 或Context N% left,而原有busyPattern只覆盖旧 spinner 文案和 capacity queue,未覆盖上述 activity footer。PTY 短暂静默后,Botmux 可能提前把仍在执行的 TraeX session 标记为 idle,并触发 DONE reaction。根因
这是既有 TUI 形态未被 adapter 完整覆盖,而不是已确认的 0.201.6 新增版本边界。旧
busyPattern无法抵消仍在屏幕上的readyPattern证据,导致活跃 turn 被误判为 idle。这与 #928 修复的 capacity queue 假 Idle 属于同一类 UI 形态漂移:
readyPattern仍可见,但对应的 busy 证据未被识别。改动
busyPattern/idleToBusyPattern,新增统一 activity-footer grammar:⋄✧◇✦❖◈◆;esc to interrupt;◆ Ran ...、echo、grep、diff 等历史行误唤醒;不使用严格行尾锚,兼容真实 PTY 重绘把分隔线/后续内容拼到 footer 后的情况。readyPattern,避免重新引入首次 prompt 注入延迟/丢失问题。IdleDetector、structured lifecycle gate 或其他 CLI adapter。关于初版 hook 专用 arm
初版 PR 曾增加只匹配固定
✦与PreToolUse/PostToolUse、且允许无 footer 的候选 arm;它并非 master 中的既有兼容逻辑。随后通过 raw PTY 和每 100ms screen capture 复测,实际捕获到的
PreToolUse、PostToolUse、UserPromptSubmit、Stop、Notificationhook-runner 行均带 elapsed /esc to interruptfooter,glyph 也会轮换;本次采样未观察到 footer-less hook frame。因此当前实现用统一 footer grammar 替代该候选 arm:覆盖事件和 glyph 更完整,也避免无 footer 的宽松历史行触发idleToBusyPattern。这不构成对低版本的回退:master 从未包含该 arm,且 master 原有 spinner/queue 规则均完整保留。如果未来获得某版本 footer-less hook frame 的真实捕获,可再基于该证据增加有边界的兼容分支。
取舍
◈ note: the footer reads (36m 20s • esc to interrupt) now这类极少见、恰好以活动 glyph 开头并包含完整 footer 形态的正文仍可能判 busy。当前选择 recall-first:更严格的行尾/尾随文本约束会漏掉已经观察到的真实 PTY footer 重绘,重新引入本 PR 要修复的提前 DONE。对应行为已用测试显式记录。影响面
真机验证
在独立临时 Git 目录中配置两个各延迟 4 秒、无业务副作用的同步 hook,通过独立 tmux 启动 TraeX 0.201.6,并在 turn 前挂
pipe-pane抓 raw PTY,同时每 100ms 采集 screen。实测确认:
⋄✧◇✦❖◈◆;esc to interruptfooter;PreToolUse、PostToolUse、UserPromptSubmit、Stop、Notificationhook 行;实验后已关闭独立 tmux、删除临时目录/raw PTY/screen/hook 日志,并移除 TraeX 自动写入的临时项目 trust 记录。未修改全局 hook,未重启 Botmux;无新增软件安装。
测试
◆ Ran、单双引号/backtick echo、grep 前置括号、diff 多括号;bun x vitest run --project unit test/cli-adapters.test.ts test/idle-detector.test.ts:494 passed;bun run build:通过;git diff --check:通过;