refactor(session): 升级存储系统架构

- 添加读写锁机制防止并发写入冲突
- 实现数据迁移框架支持版本化升级
- 分层存储结构:项目/会话/消息独立存储
- 使用 Git root commit hash 作为稳定项目 ID
- 增量消息同步避免重复写入
- 每条消息独立文件,按序号命名 (0001.json, 0002.json)
This commit is contained in:
2025-12-13 10:41:36 +08:00
parent 26e8646518
commit 9ff2934089
11 changed files with 1277 additions and 487 deletions
+84 -30
View File
@@ -1,5 +1,5 @@
import type { ModelMessage } from 'ai';
import type { SessionData, Todo, SessionSummary } from './types.js';
import type { SessionData, Todo, SessionSummary, ProjectMetadata } from './types.js';
import { SessionStorage, sessionStorage } from './storage.js';
/**
@@ -9,7 +9,9 @@ import { SessionStorage, sessionStorage } from './storage.js';
export class SessionManager {
private storage: SessionStorage;
private currentSession: SessionData | null = null;
private currentProject: ProjectMetadata | null = null;
private autoSaveInterval: ReturnType<typeof setInterval> | null = null;
private lastSyncedCount: number = 0;
constructor(storage?: SessionStorage) {
this.storage = storage || sessionStorage;
@@ -19,21 +21,30 @@ export class SessionManager {
* 初始化 - 尝试恢复或创建新会话
*/
async init(workdir: string): Promise<SessionData> {
// 尝试加载当前会话
const existing = await this.storage.loadCurrentSession();
// 获取或创建项目
this.currentProject = await this.storage.getOrCreateProject(workdir);
if (existing && existing.workdir === workdir) {
// 同一工作目录,恢复会话
this.currentSession = existing;
} else {
// 不同目录或无会话,归档旧会话并创建新的
if (existing) {
await this.storage.archiveCurrentSession();
// 尝试加载当前会话
const currentSessionId = await this.storage.getCurrentSessionId();
if (currentSessionId) {
// 尝试加载会话
const existing = await this.storage.loadSession(this.currentProject.id, currentSessionId);
if (existing && existing.workdir === workdir) {
// 同一工作目录,恢复会话
this.currentSession = existing;
this.lastSyncedCount = existing.messages.length;
this.startAutoSave();
return this.currentSession;
}
this.currentSession = this.createNewSession(workdir);
await this.save();
}
// 创建新会话
this.currentSession = this.createNewSession(workdir);
this.lastSyncedCount = 0;
await this.save();
// 启动自动保存
this.startAutoSave();
@@ -44,8 +55,13 @@ export class SessionManager {
* 创建新会话
*/
private createNewSession(workdir: string): SessionData {
if (!this.currentProject) {
throw new Error('Project not initialized. Call init() first.');
}
return {
id: this.storage.generateSessionId(),
projectId: this.currentProject.id,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
workdir,
@@ -63,12 +79,26 @@ export class SessionManager {
}
/**
* 保存当前会话
* 获取当前项目
*/
getProject(): ProjectMetadata | null {
return this.currentProject;
}
/**
* 保存当前会话(增量保存消息)
*/
async save(): Promise<void> {
if (this.currentSession) {
await this.storage.saveCurrentSession(this.currentSession);
}
if (!this.currentSession) return;
// 增量保存消息
await this.storage.saveSession(this.currentSession, this.lastSyncedCount);
// 更新当前会话指针
await this.storage.setCurrentSession(this.currentSession.id);
// 更新同步计数
this.lastSyncedCount = this.currentSession.messages.length;
}
/**
@@ -132,14 +162,20 @@ export class SessionManager {
* 清空当前会话并创建新会话
*/
async newSession(workdir?: string): Promise<SessionData> {
// 归档当前会话
if (this.currentSession && this.currentSession.messages.length > 0) {
await this.storage.archiveCurrentSession();
if (!this.currentProject) {
throw new Error('Project not initialized. Call init() first.');
}
// 创建新会话
const newWorkdir = workdir || this.currentSession?.workdir || process.cwd();
// 如果工作目录变化,需要切换项目
if (workdir && workdir !== this.currentProject.workdir) {
this.currentProject = await this.storage.getOrCreateProject(workdir);
}
this.currentSession = this.createNewSession(newWorkdir);
this.lastSyncedCount = 0;
await this.save();
return this.currentSession;
@@ -152,9 +188,14 @@ export class SessionManager {
* @param title 会话标题
*/
createChildSession(parentId: string, agentName: string, title?: string): SessionData {
if (!this.currentProject) {
throw new Error('Project not initialized. Call init() first.');
}
const workdir = this.currentSession?.workdir || process.cwd();
const childSession: SessionData = {
id: this.storage.generateSessionId(),
projectId: this.currentProject.id,
parentId,
agentName,
createdAt: new Date().toISOString(),
@@ -172,7 +213,7 @@ export class SessionManager {
* 保存子会话
*/
async saveChildSession(session: SessionData): Promise<void> {
await this.storage.saveSession(session);
await this.storage.saveSession(session, 0);
}
/**
@@ -186,32 +227,45 @@ export class SessionManager {
* 恢复指定会话
*/
async restoreSession(sessionId: string): Promise<SessionData | null> {
const session = await this.storage.loadSession(sessionId);
if (!session) return null;
// 归档当前会话
if (this.currentSession && this.currentSession.messages.length > 0) {
await this.storage.archiveCurrentSession();
if (!this.currentProject) {
throw new Error('Project not initialized. Call init() first.');
}
const session = await this.storage.loadSession(this.currentProject.id, sessionId);
if (!session) return null;
this.currentSession = session;
await this.save();
this.lastSyncedCount = session.messages.length;
await this.storage.setCurrentSession(sessionId);
return session;
}
/**
* 列出历史会话
* 列出当前项目的历史会话
*/
async listSessions(): Promise<SessionSummary[]> {
return this.storage.listSessions();
if (!this.currentProject) {
return this.storage.listAllSessions();
}
return this.storage.listSessionsByProject(this.currentProject.id);
}
/**
* 列出所有项目的会话
*/
async listAllSessions(): Promise<SessionSummary[]> {
return this.storage.listAllSessions();
}
/**
* 删除历史会话
*/
async deleteSession(sessionId: string): Promise<boolean> {
return this.storage.deleteSession(sessionId);
if (!this.currentProject) {
return false;
}
return this.storage.deleteSession(this.currentProject.id, sessionId);
}
/**