feat(worldline): persistent checkpoint-tree rewind for tui_v2 (/rewind + /worldline) - #667
Merged
Merged
Conversation
- tuiapp_v2.py: /rewind durable inline picker + /worldline three-pane checkpoint tree (tree UI inlined; colors follow the v2 theme) with a per-node diff viewport; conv-only /rewind restores via the persistent store. - worldline.py (new): UI-agnostic backend — RewindStore (persistent checkpoint tree + content-addressed blobs), reconcile, restore_plan, node_diff, native-log projection. - continue_cmd.py: gated worldline support — list_sessions(rewind_root=), continue_inplace/copy(allow_empty=); default-off, other UIs byte-identical. - tui_v3.py: status line shows concrete model id (unrelated minor tweak).
After picking a turn, /rewind now opens the same RestoreModeScreen as /worldline to choose conversation / code / both, then restores via the persistent store — making /rewind a true lite view of /worldline (linear list, no tree/diff) over the identical restore backend.
树节点除 conv 外,再存 history_info 增量(与 conv 同构)+ key_info 快照。 restore_plan 重建二者,_rw_sync_working_memory 在 rewind 时就地写回 handler.history_info / working['key_info'],使注入的 WORKING MEMORY 与 回退后的对话一致,消除旧 rewind「历史回退、纪要串味」缺陷。 向后兼容:老树无 WM 记录 → path_has_wm=False → 恢复时跳过 WM 同步, 不动现场纪要。单测 21/21 + 真机(363580)端到端验证(call_013→014 v2 痕迹 ×3→×0)通过。
… working memory on absorb - reconcile now takes the log-parsed full history, never the compressed backend.history. Compressed memory is a lossy derivative; reconciling the full tree against it made the tree adopt a front-trimmed suffix as a new trunk (false divergence). Contract pinned in the docstring. - drop in-session reconcile from /rewind and /worldline: the tree is kept current every turn by _rw_commit, and live history there is compressed. reconcile now runs only on /continue, where backend.history is the freshly loaded full log. - reconcile rebuilds history_info/key_info from the absorbed log so resumed and external-UI sessions also carry working memory on rewind.
…'s real length _rw_commit fed the compressed backend.history, so once a long session front-trims, commit's absolute hist_len delta drops turns. Now: - _rw_commit feeds the log-parsed full history (fallback to backend.history). - commit derives the delta start from len(rebuild_history(parent)) / len(rebuild_hist_info(parent)) instead of the stored hist_len, so a poisoned count can't drop turns and a previously-lost turn self-heals on the next commit (the log is append-only, always a superset of the tree).
…raded fallback It chops the compressed live history (can't reach front-trimmed turns, keeps truncated remnants, restores neither files nor working memory). Only used when there is no checkpoint tree; the durable tree-rebuild path is always preferred.
…dler state /continue resets agent.history (handler.history_info) to empty. _rw_commit previously fed that live list as hist_info, so after a continue the per-turn WM delta misaligned with the tree's log-derived WM and new turns' summaries were dropped. commit now derives hist_info/key_info from the history it stores (the full log) when not given, matching reconcile; _rw_commit stops passing the live list. Tree WM is uniformly log-derived and survives the continue reset.
…ed from the log) continue_cmd clears handler.history_info on restore; the TUI's continue path now derives history_info from the loaded log (same _derive_hist_info as the tree) and writes it back to agent.history, so a continued session keeps its working memory, consistent with the tree and live sessions. Scoped to tuiapp_v2 only — shared continue_cmd is untouched, so telegram/discord/streamlit frontends are unaffected.
…store_wm Move the WM restore from the TUI into continue_cmd as an opt-in. _load_history_into derives history_info from the loaded log and writes it back to agent.history when restore_wm=True; continue_inplace/continue_copy thread the flag and the worldline TUI passes True. Default False keeps every other frontend (telegram/discord/ streamlit/shared chat) byte-for-byte unchanged. _derive_hist_info is self-contained (no worldline dependency), same extraction口径 as the tree.
…is detected _rw_commit re-parsed the whole log every segment to feed commit a full history; for very long sessions (multi-MB logs) that adds a multi-second lag per turn. Now it uses the fast in-memory backend.history by default and only falls back to parsing the log when front-trim is detected — RewindStore.first_question() vs the live history's first question (front-trim pops the first question, so they diverge). Common case (no trim) pays nothing; correctness under trim is preserved.
- worldline: commit_bridge() + restore_plan() three-way (both/conv/code) with a new rw_tag field. conv bridges fork at parent(target); code bridges mount at parent(old_head) as a sibling of the current conversation node (same conversation, older code). - tuiapp_v2: _rw_commit now reads the native log (parse_native_log) instead of the compressible live backend.history; stat-cached _rw_log_history; title from the real log delta; skip commit when there is no new complete pair. Cursor points at the bridge for conv/code; rw_tag rendered after the title. - continue_cmd: add parse_native_log() public wrapper (complete Prompt->Response pairs only; [] for native-but-empty, None for non-native).
- key()/cwd 改用 realpath 解析 junction/symlink,同一物理文件只产生一个 tracked key(修 workspace 下 @提及绝对路径与相对写入产生双 key、回退误覆写当前内容) - 新增 node_line_delta:按 node_diff 计每节点相对父节点的 +新增/-删除 行数, 供 /rewind、worldline 显示代码变动量 - 重建历史时剥离 project-mode 注入块与自动注入标记(worldline._msg_user_text + continue_cmd._derive_hist_info),标题/提问只留用户原话
worldline 面板: - 右栏改用原生 OptionList(整项高亮/滑窗跟踪/点击映射,取代手写 ClickableList) - 左右栏比例 3:2;详情面板精简(删掉与状态条/diff 重复的操作提示、时间、回退说明) - 状态条重写为完整动宾短语,Enter 随聚焦切文案;右栏聚焦高亮改用更亮的 alt_bg (对齐左树,修此前聚焦态反而更淡的问题) /rewind: - 三列:用户 prompt · 上次时间 · 代码 +新增/-删除(绿/红,无改动显示「(无改动)」); 倒序、聚焦底部、超长 prompt 省略号截断 - 延迟折叠(defer_collapse):选中先弹模式窗,确认才回退并清卡片;Esc 取消保留列表、 焦点回到列表,不再留误导性「✓ 已选中」 其他: - Ctrl+D 关会话时释放日志锁(abort + release_current),之后可原地 /continue
restore_plan 的 prefill/_prompt_of 走未清洗的 user_msg_text(树里的 user 消息取自 含注入的 native 日志),把 --- [PROJECT MODE:...] --- 一起塞进输入框。统一过 _strip_project_mode(幂等)。回归:temp/test_rewind_prefill_no_projectmode.py 9/9。
- 删旧 /rewind 整簇:RewindScreen 类 + _on_rewind_pick + _rw_* 9 个 helper (已被内联 choice 卡 + RestoreModeScreen / RewindTreeScreen 取代,无 live 入口) - 删未用 import next_same_depth/CheckpointTree、只写不读的 _diff_for/assistant_len - 净 -276 行;import tuiapp_v2 通过、worldline 回归全绿
apply_code 直接写用户工作区文件,中途失败(权限/磁盘)会留下半回退状态。原先 restore_plan 吞成 changed=[],UI 显示「无变更」,用户毫不知情——全 PR 里失败半径 最大却唯一被静默的点。改为 _apply_code_safe 回传 (changed, code_error), _rw_restore_node 据此追加「⚠️ 代码回退未完成…请检查」告警。
lsdefine
approved these changes
Jul 7, 2026
lsdefine
left a comment
Owner
There was a problem hiding this comment.
✅ Reviewed & approved.
审核项全绿:
- 改动仅限 tuiapp_v2.py / continue_cmd.py / 新增 worldline.py,未触碰 tui_v3/stapp/tgapp/desktop 等其他前端。
- 无内置工具/tools_schema 注入,符合工具集固定约束;无危险系统调用;依赖仅标准库+rich(已有)。
- 共享模块 continue_cmd.py 的改动全部是默认关闭的可选参数(rewind_root/allow_empty/restore_wm)+ 新增函数,其他前端不传即逐字节不变。
- 唯一公共行为变更 _PM_BLOCK_RE(尾部→任意位置)语义正确且更健壮,正常正文不含该注入标记。
- 本地 py_compile 7 个相关前端全部通过。
opt-in 默认关、对其它前端零影响,合并。
lsdefine
pushed a commit
that referenced
this pull request
Jul 23, 2026
…d + /worldline) (#667) * feat(worldline): integrate checkpoint-tree rewind into tui_v2 - tuiapp_v2.py: /rewind durable inline picker + /worldline three-pane checkpoint tree (tree UI inlined; colors follow the v2 theme) with a per-node diff viewport; conv-only /rewind restores via the persistent store. - worldline.py (new): UI-agnostic backend — RewindStore (persistent checkpoint tree + content-addressed blobs), reconcile, restore_plan, node_diff, native-log projection. - continue_cmd.py: gated worldline support — list_sessions(rewind_root=), continue_inplace/copy(allow_empty=); default-off, other UIs byte-identical. - tui_v3.py: status line shows concrete model id (unrelated minor tweak). * feat(worldline): /rewind reuses the restore-mode picker (conv/code/both) After picking a turn, /rewind now opens the same RestoreModeScreen as /worldline to choose conversation / code / both, then restores via the persistent store — making /rewind a true lite view of /worldline (linear list, no tree/diff) over the identical restore backend. * feat(worldline): sync working memory (history_info/key_info) on rewind 树节点除 conv 外,再存 history_info 增量(与 conv 同构)+ key_info 快照。 restore_plan 重建二者,_rw_sync_working_memory 在 rewind 时就地写回 handler.history_info / working['key_info'],使注入的 WORKING MEMORY 与 回退后的对话一致,消除旧 rewind「历史回退、纪要串味」缺陷。 向后兼容:老树无 WM 记录 → path_has_wm=False → 恢复时跳过 WM 同步, 不动现场纪要。单测 21/21 + 真机(363580)端到端验证(call_013→014 v2 痕迹 ×3→×0)通过。 * fix(worldline): reconcile against the log, only on /continue; rebuild working memory on absorb - reconcile now takes the log-parsed full history, never the compressed backend.history. Compressed memory is a lossy derivative; reconciling the full tree against it made the tree adopt a front-trimmed suffix as a new trunk (false divergence). Contract pinned in the docstring. - drop in-session reconcile from /rewind and /worldline: the tree is kept current every turn by _rw_commit, and live history there is compressed. reconcile now runs only on /continue, where backend.history is the freshly loaded full log. - reconcile rebuilds history_info/key_info from the absorbed log so resumed and external-UI sessions also carry working memory on rewind. * fix(worldline): feed commit the full log; derive deltas from the tree's real length _rw_commit fed the compressed backend.history, so once a long session front-trims, commit's absolute hist_len delta drops turns. Now: - _rw_commit feeds the log-parsed full history (fallback to backend.history). - commit derives the delta start from len(rebuild_history(parent)) / len(rebuild_hist_info(parent)) instead of the stored hist_len, so a poisoned count can't drop turns and a previously-lost turn self-heals on the next commit (the log is append-only, always a superset of the tree). * chore(worldline): mark in-memory _do_rewind as the non-persistent degraded fallback It chops the compressed live history (can't reach front-trimmed turns, keeps truncated remnants, restores neither files nor working memory). Only used when there is no checkpoint tree; the durable tree-rebuild path is always preferred. * fix(worldline): derive tree working memory from the log, not live handler state /continue resets agent.history (handler.history_info) to empty. _rw_commit previously fed that live list as hist_info, so after a continue the per-turn WM delta misaligned with the tree's log-derived WM and new turns' summaries were dropped. commit now derives hist_info/key_info from the history it stores (the full log) when not given, matching reconcile; _rw_commit stops passing the live list. Tree WM is uniformly log-derived and survives the continue reset. * feat(worldline): restore working memory on /continue (TUI-only, derived from the log) continue_cmd clears handler.history_info on restore; the TUI's continue path now derives history_info from the loaded log (same _derive_hist_info as the tree) and writes it back to agent.history, so a continued session keeps its working memory, consistent with the tree and live sessions. Scoped to tuiapp_v2 only — shared continue_cmd is untouched, so telegram/discord/streamlit frontends are unaffected. * refactor(continue): restore working memory on /continue via opt-in restore_wm Move the WM restore from the TUI into continue_cmd as an opt-in. _load_history_into derives history_info from the loaded log and writes it back to agent.history when restore_wm=True; continue_inplace/continue_copy thread the flag and the worldline TUI passes True. Default False keeps every other frontend (telegram/discord/ streamlit/shared chat) byte-for-byte unchanged. _derive_hist_info is self-contained (no worldline dependency), same extraction口径 as the tree. * perf(worldline): only re-parse the log in _rw_commit when front-trim is detected _rw_commit re-parsed the whole log every segment to feed commit a full history; for very long sessions (multi-MB logs) that adds a multi-second lag per turn. Now it uses the fast in-memory backend.history by default and only falls back to parsing the log when front-trim is detected — RewindStore.first_question() vs the live history's first question (front-trim pops the first question, so they diverge). Common case (no trim) pays nothing; correctness under trim is preserved. * feat(worldline): conv/code bridge nodes + align _rw_commit to native log - worldline: commit_bridge() + restore_plan() three-way (both/conv/code) with a new rw_tag field. conv bridges fork at parent(target); code bridges mount at parent(old_head) as a sibling of the current conversation node (same conversation, older code). - tuiapp_v2: _rw_commit now reads the native log (parse_native_log) instead of the compressible live backend.history; stat-cached _rw_log_history; title from the real log delta; skip commit when there is no new complete pair. Cursor points at the bridge for conv/code; rw_tag rendered after the title. - continue_cmd: add parse_native_log() public wrapper (complete Prompt->Response pairs only; [] for native-but-empty, None for non-native). * fix(worldline): realpath 去重 key + 节点行数增删 + 剥离 project-mode - key()/cwd 改用 realpath 解析 junction/symlink,同一物理文件只产生一个 tracked key(修 workspace 下 @提及绝对路径与相对写入产生双 key、回退误覆写当前内容) - 新增 node_line_delta:按 node_diff 计每节点相对父节点的 +新增/-删除 行数, 供 /rewind、worldline 显示代码变动量 - 重建历史时剥离 project-mode 注入块与自动注入标记(worldline._msg_user_text + continue_cmd._derive_hist_info),标题/提问只留用户原话 * feat(worldline/rewind): 面板与 rewind UI 精简重设计 worldline 面板: - 右栏改用原生 OptionList(整项高亮/滑窗跟踪/点击映射,取代手写 ClickableList) - 左右栏比例 3:2;详情面板精简(删掉与状态条/diff 重复的操作提示、时间、回退说明) - 状态条重写为完整动宾短语,Enter 随聚焦切文案;右栏聚焦高亮改用更亮的 alt_bg (对齐左树,修此前聚焦态反而更淡的问题) /rewind: - 三列:用户 prompt · 上次时间 · 代码 +新增/-删除(绿/红,无改动显示「(无改动)」); 倒序、聚焦底部、超长 prompt 省略号截断 - 延迟折叠(defer_collapse):选中先弹模式窗,确认才回退并清卡片;Esc 取消保留列表、 焦点回到列表,不再留误导性「✓ 已选中」 其他: - Ctrl+D 关会话时释放日志锁(abort + release_current),之后可原地 /continue * fix(worldline): 回填输入框的 prefill 剥离 project-mode 注入 restore_plan 的 prefill/_prompt_of 走未清洗的 user_msg_text(树里的 user 消息取自 含注入的 native 日志),把 --- [PROJECT MODE:...] --- 一起塞进输入框。统一过 _strip_project_mode(幂等)。回归:temp/test_rewind_prefill_no_projectmode.py 9/9。 * refactor(tui): 删除被取代的旧 rewind 面板与死代码 - 删旧 /rewind 整簇:RewindScreen 类 + _on_rewind_pick + _rw_* 9 个 helper (已被内联 choice 卡 + RestoreModeScreen / RewindTreeScreen 取代,无 live 入口) - 删未用 import next_same_depth/CheckpointTree、只写不读的 _diff_for/assistant_len - 净 -276 行;import tuiapp_v2 通过、worldline 回归全绿 * fix(worldline): apply_code 回退失败不再静默,回传错误由 UI 提示 apply_code 直接写用户工作区文件,中途失败(权限/磁盘)会留下半回退状态。原先 restore_plan 吞成 changed=[],UI 显示「无变更」,用户毫不知情——全 PR 里失败半径 最大却唯一被静默的点。改为 _apply_code_safe 回传 (changed, code_error), _rw_restore_node 据此追加「⚠️ 代码回退未完成…请检查」告警。
Imzl-zl
pushed a commit
to Imzl-zl/GenericAgent
that referenced
this pull request
Aug 5, 2026
…d + /worldline) (lsdefine#667) * feat(worldline): integrate checkpoint-tree rewind into tui_v2 - tuiapp_v2.py: /rewind durable inline picker + /worldline three-pane checkpoint tree (tree UI inlined; colors follow the v2 theme) with a per-node diff viewport; conv-only /rewind restores via the persistent store. - worldline.py (new): UI-agnostic backend — RewindStore (persistent checkpoint tree + content-addressed blobs), reconcile, restore_plan, node_diff, native-log projection. - continue_cmd.py: gated worldline support — list_sessions(rewind_root=), continue_inplace/copy(allow_empty=); default-off, other UIs byte-identical. - tui_v3.py: status line shows concrete model id (unrelated minor tweak). * feat(worldline): /rewind reuses the restore-mode picker (conv/code/both) After picking a turn, /rewind now opens the same RestoreModeScreen as /worldline to choose conversation / code / both, then restores via the persistent store — making /rewind a true lite view of /worldline (linear list, no tree/diff) over the identical restore backend. * feat(worldline): sync working memory (history_info/key_info) on rewind 树节点除 conv 外,再存 history_info 增量(与 conv 同构)+ key_info 快照。 restore_plan 重建二者,_rw_sync_working_memory 在 rewind 时就地写回 handler.history_info / working['key_info'],使注入的 WORKING MEMORY 与 回退后的对话一致,消除旧 rewind「历史回退、纪要串味」缺陷。 向后兼容:老树无 WM 记录 → path_has_wm=False → 恢复时跳过 WM 同步, 不动现场纪要。单测 21/21 + 真机(363580)端到端验证(call_013→014 v2 痕迹 ×3→×0)通过。 * fix(worldline): reconcile against the log, only on /continue; rebuild working memory on absorb - reconcile now takes the log-parsed full history, never the compressed backend.history. Compressed memory is a lossy derivative; reconciling the full tree against it made the tree adopt a front-trimmed suffix as a new trunk (false divergence). Contract pinned in the docstring. - drop in-session reconcile from /rewind and /worldline: the tree is kept current every turn by _rw_commit, and live history there is compressed. reconcile now runs only on /continue, where backend.history is the freshly loaded full log. - reconcile rebuilds history_info/key_info from the absorbed log so resumed and external-UI sessions also carry working memory on rewind. * fix(worldline): feed commit the full log; derive deltas from the tree's real length _rw_commit fed the compressed backend.history, so once a long session front-trims, commit's absolute hist_len delta drops turns. Now: - _rw_commit feeds the log-parsed full history (fallback to backend.history). - commit derives the delta start from len(rebuild_history(parent)) / len(rebuild_hist_info(parent)) instead of the stored hist_len, so a poisoned count can't drop turns and a previously-lost turn self-heals on the next commit (the log is append-only, always a superset of the tree). * chore(worldline): mark in-memory _do_rewind as the non-persistent degraded fallback It chops the compressed live history (can't reach front-trimmed turns, keeps truncated remnants, restores neither files nor working memory). Only used when there is no checkpoint tree; the durable tree-rebuild path is always preferred. * fix(worldline): derive tree working memory from the log, not live handler state /continue resets agent.history (handler.history_info) to empty. _rw_commit previously fed that live list as hist_info, so after a continue the per-turn WM delta misaligned with the tree's log-derived WM and new turns' summaries were dropped. commit now derives hist_info/key_info from the history it stores (the full log) when not given, matching reconcile; _rw_commit stops passing the live list. Tree WM is uniformly log-derived and survives the continue reset. * feat(worldline): restore working memory on /continue (TUI-only, derived from the log) continue_cmd clears handler.history_info on restore; the TUI's continue path now derives history_info from the loaded log (same _derive_hist_info as the tree) and writes it back to agent.history, so a continued session keeps its working memory, consistent with the tree and live sessions. Scoped to tuiapp_v2 only — shared continue_cmd is untouched, so telegram/discord/streamlit frontends are unaffected. * refactor(continue): restore working memory on /continue via opt-in restore_wm Move the WM restore from the TUI into continue_cmd as an opt-in. _load_history_into derives history_info from the loaded log and writes it back to agent.history when restore_wm=True; continue_inplace/continue_copy thread the flag and the worldline TUI passes True. Default False keeps every other frontend (telegram/discord/ streamlit/shared chat) byte-for-byte unchanged. _derive_hist_info is self-contained (no worldline dependency), same extraction口径 as the tree. * perf(worldline): only re-parse the log in _rw_commit when front-trim is detected _rw_commit re-parsed the whole log every segment to feed commit a full history; for very long sessions (multi-MB logs) that adds a multi-second lag per turn. Now it uses the fast in-memory backend.history by default and only falls back to parsing the log when front-trim is detected — RewindStore.first_question() vs the live history's first question (front-trim pops the first question, so they diverge). Common case (no trim) pays nothing; correctness under trim is preserved. * feat(worldline): conv/code bridge nodes + align _rw_commit to native log - worldline: commit_bridge() + restore_plan() three-way (both/conv/code) with a new rw_tag field. conv bridges fork at parent(target); code bridges mount at parent(old_head) as a sibling of the current conversation node (same conversation, older code). - tuiapp_v2: _rw_commit now reads the native log (parse_native_log) instead of the compressible live backend.history; stat-cached _rw_log_history; title from the real log delta; skip commit when there is no new complete pair. Cursor points at the bridge for conv/code; rw_tag rendered after the title. - continue_cmd: add parse_native_log() public wrapper (complete Prompt->Response pairs only; [] for native-but-empty, None for non-native). * fix(worldline): realpath 去重 key + 节点行数增删 + 剥离 project-mode - key()/cwd 改用 realpath 解析 junction/symlink,同一物理文件只产生一个 tracked key(修 workspace 下 @提及绝对路径与相对写入产生双 key、回退误覆写当前内容) - 新增 node_line_delta:按 node_diff 计每节点相对父节点的 +新增/-删除 行数, 供 /rewind、worldline 显示代码变动量 - 重建历史时剥离 project-mode 注入块与自动注入标记(worldline._msg_user_text + continue_cmd._derive_hist_info),标题/提问只留用户原话 * feat(worldline/rewind): 面板与 rewind UI 精简重设计 worldline 面板: - 右栏改用原生 OptionList(整项高亮/滑窗跟踪/点击映射,取代手写 ClickableList) - 左右栏比例 3:2;详情面板精简(删掉与状态条/diff 重复的操作提示、时间、回退说明) - 状态条重写为完整动宾短语,Enter 随聚焦切文案;右栏聚焦高亮改用更亮的 alt_bg (对齐左树,修此前聚焦态反而更淡的问题) /rewind: - 三列:用户 prompt · 上次时间 · 代码 +新增/-删除(绿/红,无改动显示「(无改动)」); 倒序、聚焦底部、超长 prompt 省略号截断 - 延迟折叠(defer_collapse):选中先弹模式窗,确认才回退并清卡片;Esc 取消保留列表、 焦点回到列表,不再留误导性「✓ 已选中」 其他: - Ctrl+D 关会话时释放日志锁(abort + release_current),之后可原地 /continue * fix(worldline): 回填输入框的 prefill 剥离 project-mode 注入 restore_plan 的 prefill/_prompt_of 走未清洗的 user_msg_text(树里的 user 消息取自 含注入的 native 日志),把 --- [PROJECT MODE:...] --- 一起塞进输入框。统一过 _strip_project_mode(幂等)。回归:temp/test_rewind_prefill_no_projectmode.py 9/9。 * refactor(tui): 删除被取代的旧 rewind 面板与死代码 - 删旧 /rewind 整簇:RewindScreen 类 + _on_rewind_pick + _rw_* 9 个 helper (已被内联 choice 卡 + RestoreModeScreen / RewindTreeScreen 取代,无 live 入口) - 删未用 import next_same_depth/CheckpointTree、只写不读的 _diff_for/assistant_len - 净 -276 行;import tuiapp_v2 通过、worldline 回归全绿 * fix(worldline): apply_code 回退失败不再静默,回传错误由 UI 提示 apply_code 直接写用户工作区文件,中途失败(权限/磁盘)会留下半回退状态。原先 restore_plan 吞成 changed=[],UI 显示「无变更」,用户毫不知情——全 PR 里失败半径 最大却唯一被静默的点。改为 _apply_code_safe 回传 (changed, code_error), _rw_restore_node 据此追加「⚠️ 代码回退未完成…请检查」告警。
Imzl-zl
pushed a commit
to Imzl-zl/GenericAgent
that referenced
this pull request
Aug 5, 2026
…d + /worldline) (lsdefine#667) * feat(worldline): integrate checkpoint-tree rewind into tui_v2 - tuiapp_v2.py: /rewind durable inline picker + /worldline three-pane checkpoint tree (tree UI inlined; colors follow the v2 theme) with a per-node diff viewport; conv-only /rewind restores via the persistent store. - worldline.py (new): UI-agnostic backend — RewindStore (persistent checkpoint tree + content-addressed blobs), reconcile, restore_plan, node_diff, native-log projection. - continue_cmd.py: gated worldline support — list_sessions(rewind_root=), continue_inplace/copy(allow_empty=); default-off, other UIs byte-identical. - tui_v3.py: status line shows concrete model id (unrelated minor tweak). * feat(worldline): /rewind reuses the restore-mode picker (conv/code/both) After picking a turn, /rewind now opens the same RestoreModeScreen as /worldline to choose conversation / code / both, then restores via the persistent store — making /rewind a true lite view of /worldline (linear list, no tree/diff) over the identical restore backend. * feat(worldline): sync working memory (history_info/key_info) on rewind 树节点除 conv 外,再存 history_info 增量(与 conv 同构)+ key_info 快照。 restore_plan 重建二者,_rw_sync_working_memory 在 rewind 时就地写回 handler.history_info / working['key_info'],使注入的 WORKING MEMORY 与 回退后的对话一致,消除旧 rewind「历史回退、纪要串味」缺陷。 向后兼容:老树无 WM 记录 → path_has_wm=False → 恢复时跳过 WM 同步, 不动现场纪要。单测 21/21 + 真机(363580)端到端验证(call_013→014 v2 痕迹 ×3→×0)通过。 * fix(worldline): reconcile against the log, only on /continue; rebuild working memory on absorb - reconcile now takes the log-parsed full history, never the compressed backend.history. Compressed memory is a lossy derivative; reconciling the full tree against it made the tree adopt a front-trimmed suffix as a new trunk (false divergence). Contract pinned in the docstring. - drop in-session reconcile from /rewind and /worldline: the tree is kept current every turn by _rw_commit, and live history there is compressed. reconcile now runs only on /continue, where backend.history is the freshly loaded full log. - reconcile rebuilds history_info/key_info from the absorbed log so resumed and external-UI sessions also carry working memory on rewind. * fix(worldline): feed commit the full log; derive deltas from the tree's real length _rw_commit fed the compressed backend.history, so once a long session front-trims, commit's absolute hist_len delta drops turns. Now: - _rw_commit feeds the log-parsed full history (fallback to backend.history). - commit derives the delta start from len(rebuild_history(parent)) / len(rebuild_hist_info(parent)) instead of the stored hist_len, so a poisoned count can't drop turns and a previously-lost turn self-heals on the next commit (the log is append-only, always a superset of the tree). * chore(worldline): mark in-memory _do_rewind as the non-persistent degraded fallback It chops the compressed live history (can't reach front-trimmed turns, keeps truncated remnants, restores neither files nor working memory). Only used when there is no checkpoint tree; the durable tree-rebuild path is always preferred. * fix(worldline): derive tree working memory from the log, not live handler state /continue resets agent.history (handler.history_info) to empty. _rw_commit previously fed that live list as hist_info, so after a continue the per-turn WM delta misaligned with the tree's log-derived WM and new turns' summaries were dropped. commit now derives hist_info/key_info from the history it stores (the full log) when not given, matching reconcile; _rw_commit stops passing the live list. Tree WM is uniformly log-derived and survives the continue reset. * feat(worldline): restore working memory on /continue (TUI-only, derived from the log) continue_cmd clears handler.history_info on restore; the TUI's continue path now derives history_info from the loaded log (same _derive_hist_info as the tree) and writes it back to agent.history, so a continued session keeps its working memory, consistent with the tree and live sessions. Scoped to tuiapp_v2 only — shared continue_cmd is untouched, so telegram/discord/streamlit frontends are unaffected. * refactor(continue): restore working memory on /continue via opt-in restore_wm Move the WM restore from the TUI into continue_cmd as an opt-in. _load_history_into derives history_info from the loaded log and writes it back to agent.history when restore_wm=True; continue_inplace/continue_copy thread the flag and the worldline TUI passes True. Default False keeps every other frontend (telegram/discord/ streamlit/shared chat) byte-for-byte unchanged. _derive_hist_info is self-contained (no worldline dependency), same extraction口径 as the tree. * perf(worldline): only re-parse the log in _rw_commit when front-trim is detected _rw_commit re-parsed the whole log every segment to feed commit a full history; for very long sessions (multi-MB logs) that adds a multi-second lag per turn. Now it uses the fast in-memory backend.history by default and only falls back to parsing the log when front-trim is detected — RewindStore.first_question() vs the live history's first question (front-trim pops the first question, so they diverge). Common case (no trim) pays nothing; correctness under trim is preserved. * feat(worldline): conv/code bridge nodes + align _rw_commit to native log - worldline: commit_bridge() + restore_plan() three-way (both/conv/code) with a new rw_tag field. conv bridges fork at parent(target); code bridges mount at parent(old_head) as a sibling of the current conversation node (same conversation, older code). - tuiapp_v2: _rw_commit now reads the native log (parse_native_log) instead of the compressible live backend.history; stat-cached _rw_log_history; title from the real log delta; skip commit when there is no new complete pair. Cursor points at the bridge for conv/code; rw_tag rendered after the title. - continue_cmd: add parse_native_log() public wrapper (complete Prompt->Response pairs only; [] for native-but-empty, None for non-native). * fix(worldline): realpath 去重 key + 节点行数增删 + 剥离 project-mode - key()/cwd 改用 realpath 解析 junction/symlink,同一物理文件只产生一个 tracked key(修 workspace 下 @提及绝对路径与相对写入产生双 key、回退误覆写当前内容) - 新增 node_line_delta:按 node_diff 计每节点相对父节点的 +新增/-删除 行数, 供 /rewind、worldline 显示代码变动量 - 重建历史时剥离 project-mode 注入块与自动注入标记(worldline._msg_user_text + continue_cmd._derive_hist_info),标题/提问只留用户原话 * feat(worldline/rewind): 面板与 rewind UI 精简重设计 worldline 面板: - 右栏改用原生 OptionList(整项高亮/滑窗跟踪/点击映射,取代手写 ClickableList) - 左右栏比例 3:2;详情面板精简(删掉与状态条/diff 重复的操作提示、时间、回退说明) - 状态条重写为完整动宾短语,Enter 随聚焦切文案;右栏聚焦高亮改用更亮的 alt_bg (对齐左树,修此前聚焦态反而更淡的问题) /rewind: - 三列:用户 prompt · 上次时间 · 代码 +新增/-删除(绿/红,无改动显示「(无改动)」); 倒序、聚焦底部、超长 prompt 省略号截断 - 延迟折叠(defer_collapse):选中先弹模式窗,确认才回退并清卡片;Esc 取消保留列表、 焦点回到列表,不再留误导性「✓ 已选中」 其他: - Ctrl+D 关会话时释放日志锁(abort + release_current),之后可原地 /continue * fix(worldline): 回填输入框的 prefill 剥离 project-mode 注入 restore_plan 的 prefill/_prompt_of 走未清洗的 user_msg_text(树里的 user 消息取自 含注入的 native 日志),把 --- [PROJECT MODE:...] --- 一起塞进输入框。统一过 _strip_project_mode(幂等)。回归:temp/test_rewind_prefill_no_projectmode.py 9/9。 * refactor(tui): 删除被取代的旧 rewind 面板与死代码 - 删旧 /rewind 整簇:RewindScreen 类 + _on_rewind_pick + _rw_* 9 个 helper (已被内联 choice 卡 + RestoreModeScreen / RewindTreeScreen 取代,无 live 入口) - 删未用 import next_same_depth/CheckpointTree、只写不读的 _diff_for/assistant_len - 净 -276 行;import tuiapp_v2 通过、worldline 回归全绿 * fix(worldline): apply_code 回退失败不再静默,回传错误由 UI 提示 apply_code 直接写用户工作区文件,中途失败(权限/磁盘)会留下半回退状态。原先 restore_plan 吞成 changed=[],UI 显示「无变更」,用户毫不知情——全 PR 里失败半径 最大却唯一被静默的点。改为 _apply_code_safe 回传 (changed, code_error), _rw_restore_node 据此追加「⚠️ 代码回退未完成…请检查」告警。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
为 tui_v2 增加持久化 checkpoint 树 rewind(「世界线」):每轮对话落一个 checkpoint 节点,支持
/rewind(内联卡片,三列:用户提问 · 上次时间 · 代码增删)与/worldline(全屏压缩树浏览器 + 逐行 diff)回退,可选 对话 / 代码 / 两者 三种恢复模式,/continue后不复活(落盘到temp/.ga_rewind/)。改动范围
仅 3 个文件,
+2606 / -48:frontends/worldline.py(新增)— UI 无关的核心:RewindStore持久层(content-addressable blob + delta 链)/CheckpointTree+CompressedTree视图模型 /restore_plan恢复编排 / native 日志读写。frontends/tuiapp_v2.py— TUI 集成:/rewind卡片、/worldline面板、_rw_commit/_rw_restore_node只做「赋 backend + 刷新」,编排全在 worldline。frontends/continue_cmd.py—parse_native_log公共封装、/continue的 opt-inrestore_wm。关键设计
model_responses_*.txt全量重建,天然免疫上下文压缩/删头;对账(reconcile)只在/continue(未压缩日志刚载入)时做,会话进行中不拿被压缩的 live history 对账,避免误判分歧而弃树。_turn_sig前缀比对把「树 ⊇ 日志」做成可执行安全闸。apply_code中途出错回传code_error,UI 提示「代码回退未完成,请检查」,而非当作无变更悄悄放过。兼容性
store is None处处兜底 → 对其它前端(tui_v3 / desktop / stapp)零影响。realpath归一化 junction/symlink,同一物理文件只产生一个 checkpoint key(修双 key 导致的误覆写)。测试
本地回归:世界线包含语义 29/29、project-mode 剥离 10/10、rewind prefill 无注入 9/9、workspace 双 key 6/6。三前端文件
py_compile+import通过。