Coverage for cli/commands/session.py: 18%

1"""

2/session 命令处理模块

3"""

4 from rich.console import Console

5 from rich.table import Table

6 from qrclaw.memory.session import Session, list_sessions, delete_session

7 from qrclaw.workspace import Workspace

8 from qrclaw.logger import setup_logger

9 from qrclaw.config import LOG_LEVEL, LOG_MAX_DAYS, LOG_TO_FILE, LOG_TO_CONSOLE, LOG_CONSOLE_LEVEL

10

11

12 def handle(args: str, current_session: Session, console: Console, workspace: Workspace) -> Session:

13 """

14 处理 /session 子命令，返回（可能切换后的）Session。

15

16 子命令：

17 list 列出当前 agent 的所有会话

18 new [id] 新建并切换（不传 id 则自动生成）

19 switch <id> 切换到已有会话

20 delete <id> 删除指定会话

21 """

22 parts = args.strip().split(maxsplit=1)

23

24 if not parts or not parts[0]:

25 _print_help(console)

26 return current_session

27

28 subcommand = parts[0]

29 sub_args = parts[1].strip() if len(parts) > 1 else ""

30

31 if subcommand == "list":

32 return _cmd_list(current_session, console, workspace)

33 elif subcommand == "new":

34 return _cmd_new(sub_args, current_session, console, workspace)

35 elif subcommand == "switch":

36 return _cmd_switch(sub_args, current_session, console, workspace)

37 elif subcommand == "delete":

38 return _cmd_delete(sub_args, current_session, console, workspace)

39 else:

40 console.print(f"[red]未知子命令: {subcommand}[/red]")

41 _print_help(console)

42 return current_session

43

44

45 def _cmd_list(current_session: Session, console: Console, workspace: Workspace) -> Session:

46 sessions = list_sessions(workspace.sessions_dir)

47 if not sessions:

48 console.print("[yellow]没有已保存的会话[/yellow]")

49 return current_session

50

51 table = Table(title=f"会话列表 (agent: {workspace.agent_id})")

52 table.add_column("会话 ID", style="cyan")

53 table.add_column("消息数", style="green", justify="right")

54 table.add_column("最后更新", style="dim")

55 table.add_column("", style="bold yellow")

56

57 for s in sessions:

58 marker = "← 当前" if s["id"] == current_session.session_id else ""

59 table.add_row(s["id"], str(s["message_count"]), s["updated_at"], marker)

60

61 console.print(table)

62 return current_session

63

64

65 def _cmd_new(session_id: str, current_session: Session, console: Console, workspace: Workspace) -> Session:

66 _reinit_logger(session_id or None, workspace)

67 # 关键修复：必须传入 resume=False，否则会恢复最近的会话

68 new_session = Session(sessions_dir=workspace.sessions_dir, session_id=session_id or None, resume=False)

69 console.print(f"[bold cyan]已新建并切换到会话: {new_session.session_id}[/bold cyan]")

70 return new_session

71

72

73 def _cmd_switch(session_id: str, current_session: Session, console: Console, workspace: Workspace) -> Session:

74 if not session_id:

75 console.print("[red]用法: /session switch <会话ID>[/red]")

76 return current_session

77

78 _reinit_logger(session_id, workspace)

79 target = Session(sessions_dir=workspace.sessions_dir, session_id=session_id)

80 msg = f"已切换到会话: {session_id}"

81 if target.messages:

82 msg += f"（{len(target.messages)} 条历史消息）"

83 console.print(f"[bold cyan]{msg}[/bold cyan]")

84 return target

85

86

87 def _cmd_delete(session_id: str, current_session: Session, console: Console, workspace: Workspace) -> Session:

88 if not session_id:

89 console.print("[red]用法: /session delete <会话ID>[/red]")

90 return current_session

91

92 if session_id == current_session.session_id:

93 console.print("[red]不能删除当前正在使用的会话，请先切换到其他会话[/red]")

94 return current_session

95

96 if delete_session(session_id, workspace.sessions_dir):

97 console.print(f"[dim]会话 {session_id} 已删除[/dim]")

98 else:

99 console.print(f"[yellow]会话 {session_id} 不存在[/yellow]")

100

101 return current_session

102

103

104 def _reinit_logger(session_id: str | None, workspace: Workspace) -> None:

105 setup_logger(

106 session_id=session_id or "tmp",

107 log_level=LOG_LEVEL,

108 log_to_file=LOG_TO_FILE,

109 log_to_console=LOG_TO_CONSOLE,

110 log_max_days=LOG_MAX_DAYS,

111 console_level=LOG_CONSOLE_LEVEL,

112 log_dir=workspace.logs_dir,

113 )

114

115

116 def _print_help(console: Console) -> None:

117 console.print("[dim]用法: /session <list|new|switch|delete> [会话ID][/dim]")

118 console.print("[dim] list 列出所有会话[/dim]")

119 console.print("[dim] new [id] 新建会话（不传 id 则自动生成）[/dim]")

120 console.print("[dim] switch <id> 切换会话[/dim]")

121 console.print("[dim] delete <id> 删除会话[/dim]")