feat: opencode 式会话持久化 + /resume 一键恢复
此前只有手动的 /chat save|list|open,关终端就丢上下文。现升级为
opencode 式体验:每轮对话自动落盘、启动提示历史会话、/resume 一键恢复。
- 新增 xcli_core/session.py:Session 数据类(id/title/created_at/updated_at/
engine/model/messages)+ SessionManager(new/save/list/load/resolve,存
chat_history/<id>.json)。向后兼容旧 /chat 格式(无 id、用 conversation 键,
读取时回退用文件名作 id)。标题自动取首条 user 消息前若干字。
- HistoryMixin 升级(cli_history.py):新增 init_session_manager /
_autosave_session / handle_resume_command。自动保存首轮惰性建会话、
更新标题与 updated_at;/resume 列出(序号|ID)或按序号|ID|前缀恢复,
恢复时把 messages 载入 shared_conversation_history 并同步引擎。
- 集成:cli_base.__init__ 在 chat_history_dir 就绪后 init_session_manager;
cli_core 主循环 1395 行 process_conversation 后调用 _autosave_session;
新增 /resume|/sessions 路由(/sessions 等同 /resume)。
- 体验:启动时若有历史会话提示 /resume(含最近会话标题);run() 紧凑提示
与 /help 速查表均补 /resume(列出/恢复/开新会话)。
- 修复 cli_history.py 一处历史 typo:{Style.RESET型} -> {Style.RESET_ALL}
(旧 load_chat_history 在含消息时会抛 AttributeError)。
- 测试:新增 tests/test_session.py(12 项,覆盖兼容/新建/落盘/排序/解析/
自动保存/恢复/列表/未知token,全程临时目录不污染真实 config/chat_history)。
全量 242 passed(原 230)。
真实 CLI 冒烟:启动提示识别到历史会话、/resume 正确列出(含标题/时间/引擎/
条数)、/resume <id> 正确恢复并回显;已清理测试种入文件,chat_history 复原为空。
This commit is contained in:
1 parent
1383d32f8b
commit
07490c4d38
6 files changed
+480
-2
No files matched your search
+26
-1
@@ -1040,7 +1040,17 @@ multi 操作支持一次修改多处:
|
||||
|
||||
# 一行速览,完整命令清单收进 /help
|
||||
print(f"{Fore.CYAN}/help 全部命令 · /quit 退出 · /tui 图形界面 · "
|
||||
f"/model 换模型 · @文件路径 读文件给 AI{Style.RESET_ALL}")
|
||||
f"/model 换模型 · /resume 恢复会话 · @文件路径 读文件给 AI{Style.RESET_ALL}")
|
||||
|
||||
# 历史会话提示(opencode 式 /resume)
|
||||
try:
|
||||
sessions = self.session_manager.list_sessions()
|
||||
if sessions:
|
||||
latest = sessions[0]
|
||||
print(f"{Fore.YELLOW}有 {len(sessions)} 个历史会话,/resume 可恢复"
|
||||
f"(最近: {latest.id} · {latest.title}){Style.RESET_ALL}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
extras = []
|
||||
loaded_engines = list(self.engines.keys())
|
||||
@@ -1146,6 +1156,19 @@ multi 操作支持一次修改多处:
|
||||
self.handle_model_command(user_input[7:].strip())
|
||||
continue
|
||||
|
||||
# /resume 会话恢复(opencode 式自动持久化 + 一键恢复)
|
||||
if user_input in ('/resume', '/sessions') or \
|
||||
user_input.startswith('/resume ') or user_input.startswith('/sessions ') or \
|
||||
user_input.startswith('/session '):
|
||||
if user_input.startswith('/resume'):
|
||||
self.handle_resume_command(user_input[7:].strip())
|
||||
elif user_input.startswith('/sessions'):
|
||||
self.handle_resume_command(user_input[9:].strip())
|
||||
else: # /session <子命令>
|
||||
sub = user_input[8:].strip()
|
||||
self.handle_resume_command(sub if sub != 'new' else 'new')
|
||||
continue
|
||||
|
||||
# /manual 快捷命令 — 切换到 manual 引擎或执行 manual 子命令
|
||||
if user_input == '/manual' or user_input.startswith('/manual '):
|
||||
args = user_input[8:].strip() if user_input.startswith('/manual ') else ''
|
||||
@@ -1393,6 +1416,8 @@ multi 操作支持一次修改多处:
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
self.process_conversation(user_input)
|
||||
# 自动持久化当前会话(opencode 式 /resume 落盘)
|
||||
self._autosave_session()
|
||||
except KeyboardInterrupt:
|
||||
user_id_display = f"[用户ID: {self.user_id}]"
|
||||
print(f"\n{Fore.GREEN}再见! {user_id_display}{Style.RESET_ALL}")
|
||||
|
||||
Reference in new issue
Block a user