Files
ai-terminal-assistant/packages/core/src/session/manager.ts
T
kurihada 66ad1a1ec9 refactor(core): 拆分大型单体文件为模块化子组件
将三个超过 700 行的大型文件重构为模块化架构:

Agent (1033 → ~400 行):
- agent-tool-executor: 工具获取、过滤和执行
- agent-message-handler: 消息构建、流式处理
- agent-mode-manager: 模式切换和权限检查
- agent-vision-handler: 视觉处理委托

CheckpointManager (1015 → ~620 行):
- checkpoint-store: 检查点 CRUD 操作
- checkpoint-rollback: 回滚和撤销操作
- checkpoint-session: 会话跟踪
- checkpoint-events: 事件发射系统

SessionManager (768 → 356 行):
- message-converter: Part ↔ ModelMessage 转换
- session-store: 会话 CRUD 操作
- project-manager: 项目管理
- session-auto-save: 自动保存功能

重构原则: 单一职责、编排器模式、向后兼容 API
2025-12-16 22:07:13 +08:00

357 lines
9.7 KiB
TypeScript

/**
* 会话管理器
* 作为编排器,委托具体工作给各个子模块
*/
import type { ModelMessage } from 'ai';
import * as storage from './storage/index.js';
import type { TodoItem } from './storage/index.js';
// 子模块
import { SessionStore, type SessionData, type SessionSummary } from './session-store.js';
import { ProjectManager, type ProjectMetadata } from './project-manager.js';
import { SessionAutoSave } from './session-auto-save.js';
// 重新导出类型
export type { SessionData, SessionSummary, ProjectMetadata };
/**
* 会话管理器
* 提供高级会话操作接口,使用新的三层存储结构
*/
export class SessionManager {
private currentSession: SessionData | null = null;
private storageDir?: string;
// 子模块
private store: SessionStore;
private projectManager: ProjectManager;
private autoSave: SessionAutoSave;
constructor(storageDir?: string) {
this.storageDir = storageDir;
this.store = new SessionStore();
this.projectManager = new ProjectManager();
this.autoSave = new SessionAutoSave();
}
// ============================================================================
// 初始化
// ============================================================================
/**
* 初始化 - 尝试恢复或创建新会话
*/
async init(workdir: string): Promise<SessionData> {
// 初始化存储
await storage.initStorage(this.storageDir);
// 获取或创建项目
await this.projectManager.getOrCreate(workdir);
// 尝试加载当前会话
const currentSessionId = await this.getCurrentSessionId();
if (currentSessionId) {
const projectId = this.projectManager.getProjectId()!;
const existing = await this.store.load(projectId, currentSessionId);
if (existing && existing.workdir === workdir) {
this.currentSession = existing;
this.startAutoSave();
return this.currentSession;
}
}
// 创建新会话
const projectId = this.projectManager.getProjectId()!;
this.currentSession = await this.store.create(projectId, workdir);
await this.store.save(this.currentSession);
await this.setCurrentSessionPointer(this.currentSession.id);
this.startAutoSave();
return this.currentSession;
}
// ============================================================================
// 会话获取
// ============================================================================
/**
* 获取当前会话
*/
getSession(): SessionData | null {
return this.currentSession;
}
/**
* 获取当前项目
*/
getProject(): ProjectMetadata | null {
return this.projectManager.getProject();
}
/**
* 获取当前会话 ID
*/
getSessionId(): string | undefined {
return this.currentSession?.id;
}
// ============================================================================
// 会话操作
// ============================================================================
/**
* 保存当前会话
*/
async save(): Promise<void> {
if (!this.currentSession) return;
await this.store.save(this.currentSession);
}
/**
* 清空当前会话并创建新会话
*/
async newSession(workdir?: string): Promise<SessionData> {
if (!this.projectManager.isInitialized()) {
throw new Error('Project not initialized. Call init() first.');
}
const newWorkdir = workdir || this.currentSession?.workdir || process.cwd();
// 如果工作目录变化,需要切换项目
if (workdir && workdir !== this.projectManager.getProject()?.workdir) {
await this.projectManager.switchProject(workdir);
}
const projectId = this.projectManager.getProjectId()!;
this.currentSession = await this.store.create(projectId, newWorkdir);
await this.store.save(this.currentSession);
await this.setCurrentSessionPointer(this.currentSession.id);
return this.currentSession;
}
/**
* 恢复指定会话
*/
async restoreSession(sessionId: string): Promise<SessionData | null> {
if (!this.projectManager.isInitialized()) {
throw new Error('Project not initialized. Call init() first.');
}
const projectId = this.projectManager.getProjectId()!;
const session = await this.store.load(projectId, sessionId);
if (!session) return null;
this.currentSession = session;
await this.setCurrentSessionPointer(sessionId);
return session;
}
/**
* 列出当前项目的历史会话
*/
async listSessions(): Promise<SessionSummary[]> {
const projectId = this.projectManager.getProjectId();
if (!projectId) {
return this.listAllSessions();
}
return this.store.listByProject(projectId);
}
/**
* 列出所有项目的会话
*/
async listAllSessions(): Promise<SessionSummary[]> {
return this.store.listAll();
}
/**
* 删除历史会话
*/
async deleteSession(sessionId: string): Promise<boolean> {
const projectId = this.projectManager.getProjectId();
if (!projectId) return false;
return this.store.delete(projectId, sessionId);
}
// ============================================================================
// 消息操作
// ============================================================================
/**
* 批量设置消息(用于同步整个对话历史)
*/
async setMessages(messages: ModelMessage[]): Promise<void> {
if (!this.currentSession) return;
this.currentSession.messages = messages;
await this.store.syncMessages(this.currentSession.id, messages);
await this.store.save(this.currentSession);
}
/**
* 添加消息
*/
async addMessage(message: ModelMessage): Promise<void> {
if (!this.currentSession) return;
this.currentSession.messages.push(message);
await this.setMessages(this.currentSession.messages);
}
/**
* 获取对话历史
*/
getMessages(): ModelMessage[] {
return this.currentSession?.messages || [];
}
// ============================================================================
// 工具和待办操作
// ============================================================================
/**
* 设置已发现的工具
*/
async setDiscoveredTools(tools: string[]): Promise<void> {
if (!this.currentSession) return;
this.currentSession.discoveredTools = tools;
await this.store.save(this.currentSession);
}
/**
* 获取已发现的工具
*/
getDiscoveredTools(): string[] {
return this.currentSession?.discoveredTools || [];
}
/**
* 更新待办事项
*/
async setTodos(
todos: Array<{ content: string; status: 'pending' | 'in_progress' | 'completed' }>
): Promise<void> {
if (!this.currentSession) return;
const items = await this.store.setTodos(this.currentSession.id, todos);
this.currentSession.todos = items;
}
/**
* 获取待办事项
*/
getTodos(): TodoItem[] {
return this.currentSession?.todos || [];
}
// ============================================================================
// 子会话操作
// ============================================================================
/**
* 创建子会话(用于 Task 工具)
*/
createChildSession(parentId: string, agentName: string, title?: string): SessionData {
if (!this.projectManager.isInitialized()) {
throw new Error('Project not initialized. Call init() first.');
}
const projectId = this.projectManager.getProjectId()!;
const workdir = this.currentSession?.workdir || process.cwd();
return this.store.createChildSession(projectId, parentId, agentName, workdir, title);
}
/**
* 保存子会话
*/
async saveChildSession(session: SessionData): Promise<void> {
await this.store.saveChildSession(session);
}
// ============================================================================
// 自动保存
// ============================================================================
/**
* 启动自动保存
*/
private startAutoSave(): void {
this.autoSave.start(() => this.save());
}
/**
* 停止自动保存
*/
stopAutoSave(): void {
this.autoSave.stop();
}
/**
* 关闭管理器(保存并停止自动保存)
*/
async close(): Promise<void> {
this.stopAutoSave();
await this.save();
}
// ============================================================================
// 清理
// ============================================================================
/**
* 清理旧会话
*/
async cleanup(keepCount: number = 50): Promise<number> {
const sessions = await this.listAllSessions();
if (sessions.length <= keepCount) {
return 0;
}
const toDelete = sessions.slice(keepCount);
let deletedCount = 0;
for (const session of toDelete) {
if (await this.deleteSession(session.id)) {
deletedCount++;
}
}
return deletedCount;
}
// ============================================================================
// 辅助方法
// ============================================================================
/**
* 获取当前会话 ID(从存储)
*/
private async getCurrentSessionId(): Promise<string | null> {
try {
const pointer = await storage.read<{ sessionId: string }>(['current-session']);
return pointer.sessionId;
} catch {
return null;
}
}
/**
* 设置当前会话指针
*/
private async setCurrentSessionPointer(sessionId: string): Promise<void> {
await storage.write(['current-session'], { sessionId });
}
/**
* 获取存储目录
*/
getStorageDir(): string {
return this.storageDir || storage.getDefaultStorageDir();
}
}
// 导出默认实例
export const sessionManager = new SessionManager();