目标与口径
所有 session 对话加时间戳,便于回看分析过程的时间点节奏。用户 2026-11-02 两次拍板:
- 正文统一前缀 [年-月-日 时:分:秒](本地时区);泡泡外左上方不加;泡泡外右下方必须加;思考与工具步骤每段都要。
- 底部戳不并排开始和结束,改为结束点加耗时:[2026-11-02 13:45:01] · 耗时1.3s;完整区间收进悬停提示。
- 去重:每块内容只留两处,行内前缀一处加右下一处。
前置事实(实测)
| 事实 | 实测值 |
|---|---|
| 上游 checkout 形态 | 独立 git 仓库,detached HEAD,无远端;裸改会被更新 wipe,需补丁包重打 |
| 总闸位置与旧默认 | display.timestamps,旧默认 False:桌面 store 初值、CLI 默认、config_defaults、TUI 默认四处全是 False,这是戳不显示的根因 |
| 桌面开关的数据源 | use-hermes-config.ts 从网关内存里的有效配置取值,不是读代码默认值 |
| 旧戳格式 | 顶部相对时间(Today at / Yesterday / Oct 12)与精确 TimelineTimestamp,底部聚合去重逻辑 suppressIfDuplicatePart |
| 回归基线 | CLI test_cli_modern_layout.py 本机 18 项失败与干净树同数(guard 环境旧问题);telegram 5 项旧失败;tsc --noEmit 被网关 lifecycle guard 拦截(_tsc.js 超 1MiB 扫描上限) |
实现过程
第零步|总闸翻默认(4 处)
- store/display-timestamps.ts:atom 初值 false 改 true。
- hermes_cli/config_defaults.py:display.timestamps False 改 True。
- ui-tui useUiStore / uiStore.ts:timestamps 默认改开启(含 Timestamps off 显式关闭仍优先)。
- 重中之重:运行中网关内存里还是旧默认,必须 hermes config set display.timestamps true 落盘,否则桌面每次同步配置又被关掉。
第一步|CLI(hermes_cli/)
- cli_modern_layout.py 新增 wall_ts_prefix(ts):datetime.fromtimestamp 本地时区格式化为 [年-月-日 时:分:秒],无条件返回(不跟随旧 timestamp_format 开关)。
- 所有回显与回答头、思考行、工具行调用它加前缀;cli_stream_mixin.py 用户回显、cli_chat_turn_mixin.py 回答正文与经典思考盒同理。
- kanban_output.py 与 kanban_db_context.py:_fmt_ts 与 _ctx_stamp 精确到秒。
- 测试镜像里的时间戳用 _notime() 剥前缀后再断言(tests/hermes_cli/test_cli_modern_layout.py 的改法)。
第二步|Telegram(plugins/platforms/telegram/adapter.py)
- 新增 _with_wall_ts(text, ts):首行已有同格式前缀就跳过(幂等),否则加 [年-月-日 时:分:秒] 前缀。
- 只包 send() 与 edit_message() 两个出口,思考、进度、最终回复同一链路全覆盖;format_message 本体不动。
第三步|看板(TUI 与桌面 drawer)
- ui-tui messageLine.tsx:全量格式分支加前缀,并放开部分 kind 限制、默认开启;messages.test.ts 加 full 与 short 两组单测。
- apps/desktop kanban drawer.tsx:评论正文加前缀,右下角加绝对时间戳。
第四步|桌面主会话(apps/desktop/src/components/assistant-ui/thread/)
- timestamp.ts 新增 formatWallPrefix(秒级 Unix 时间转 [年-月-日 时:分:秒],非法值回空串)、formatWallDuration(小于 1 秒显示 50ms 式毫秒,小于 60 秒显示 2.8s 式一位小数,否则 1m05s 或 2m)、wallPrefixNow(无戳行取当前时间)。
- timeline-timestamp.tsx 新增 WallTimestamp(data-slot 等于 wall-timestamp):行内模式只渲染开始点前缀;块模式渲染结束点加耗时,完整区间放 title 悬停;另有 MessageWallPrefix、MessageWallStamp 两个消息级封装。
- 挂载点:message-parts.tsx 正文与思考体、assistant-message.tsx 助手底部、user-message.tsx 用户前缀与底部、tool/fallback.tsx 工具标题前缀、工具底部、ToolRunHeader 折叠汇总行。
- 旧顶部 TimelineTimestamp 保留(删了崩现有断言),去重只拆与新戳重影的那几颗(见下一步)。
第五步|去重(用户看包验收后追加)
- 拆掉:助手正文顶部旧戳、助手与用户底部旧 MessageTimelineTimestamp、ThinkingDisclosure 头行旧戳、ToolEntry 标题行旧戳、ToolRunHeader 尾戳。
- 测试按新行为重写:assistant-message.test.ts 断言 wall 槽位(旧槽零个),展开 Thought 后断言耗时 50ms;tool-group.test.ts 断言工具行前缀与折叠行前缀。
- 思考体部戳只在展开后可见(折叠时连正文一起收起,无戳,与旧行为一致)。
实测证据
| 检查 | 结果 |
|---|---|
| desktop vitest(5 文件) | 58 项全过 |
| CLI pytest 纯函数 2 项 | 全过;其余 18 项失败与干净树同数,属 guard 环境旧问题 |
| python 编译检查 | 6 个改动文件 py_compile 全过 |
| 桌面构建与打包 | npm run build 加 npm run builder -- --dir --publish never 成功 |
| 拆包验证 | 拆 app.asar,bundle 内同时命中 wall-timestamp 与耗时 |
| TUI 构建 | ui-tui npm run build 成功 |
管理命令
| 用途 | 命令 |
|---|---|
| 一键重打(上游 wipe 后) | bash .hermes/patches/session-timestamps/reapply.sh(在补丁包目录内:工作树检查、git apply、py_compile、vitest、pytest、重建、验包) |
| 补丁自洽校验 | git apply --check --reverse session-timestamps.patch |
| 桌面相关单测 | 在 apps/desktop 内:../../node_modules/.bin/vitest run src/components/assistant-ui/thread/timeline-timestamp.test.tsx src/components/assistant-ui/thread/timestamp.test.ts src/components/assistant-ui/thread/assistant-message.test.tsx src/components/assistant-ui/thread/system-message.test.tsx src/components/assistant-ui/tool/tool-group.test.tsx |
| 拆包验戳 | node 脚本读 release/linux-unpacked/resources/app.asar,断言 bundle 含 wall-timestamp |
| 生效 | hermes desktop 启动器直读 linux-unpacked;重建后重启桌面客户端(或 Ctrl+R 重载窗口)即生效,网关不用重启 |
踩坑记录
| 现象 | 成因 | 处置 |
|---|---|---|
| 包是新的、桌面也重启了,气泡仍无戳 | 桌面开关取网关内存里的有效配置,网关进程是补丁前启动的,内存中仍是旧默认 False,每次同步又把开关关掉 | hermes config set display.timestamps true 落盘;窗口重载即现,已补进 reapply.sh |
| 右下与正文各出现两个时间戳 | 新戳与旧戳同位叠加 | 按去重结论拆旧戳,测试同步重写 |
| 断言耗时 50ms 的用例失败 | 思考块默认折叠,体部戳随正文一起收起,根本没渲染 | 用例里先点开展开 Thought 再断言 |
| vitest 从仓库根跑报 alias 解析失败 | 根 vitest.config 把 @ 指向了 packages 层,桌面有自己的 alias | 一律 cd apps/desktop 再用它的 node_modules/.bin/vitest 跑 |
| CLI 测试镜像大面积失败 | 旧版 ui-tui 全局 fake timers 把 setTimeout mock 掉,消息永不落盘 | 测试文件内 vi.useRealTimers,前后果断恢复;displayTimestamps.set(true) 同理 |
| tsc --noEmit 被 guard 拦截 | 网关 lifecycle 的 _tsc.js 扫描上限 1MiB,构建产物超限 | 不绕 guard,靠 npm run build 成功覆盖类型正确性 |
| pkill 按模式杀进程会自杀 | 模式串就在执行者自己的命令行里 | 用 python 扫 /proc/cmdline,排除自身 pid 与 ppid 后再杀 |
边界
- 网关 message_timestamps(给模型上下文加时间)刻意没动,避免污染 prompt 与缓存。
- 泡泡外左上方不加戳;图片、Clarify、Theme 等卡片部件的单颗顶部旧戳保留(不重影)。
- 完整 diff 与重打脚本在补丁包 .hermes/patches/session-timestamps/(session-timestamps.patch、reapply.sh、README.md);本地台账见 hermes-rules.md D5。
- 上游漂移时 git apply 失败就看 .rej 逐个手合:多为测试镜像的精确字符串断言,加 _notime() 剥前缀即可。