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
This commit is contained in:
2025-12-16 22:07:13 +08:00
parent e53035ffc0
commit 66ad1a1ec9
17 changed files with 3154 additions and 1959 deletions
@@ -0,0 +1,263 @@
/**
* 检查点存储
* 负责检查点的 CRUD 操作
*/
import { nanoid } from 'nanoid';
import type { ShadowGit } from './shadow-git.js';
import type { CheckpointLock } from './lock.js';
import type { CommitMessageGenerator } from './commit-message.js';
import type {
CheckpointMetadata,
CheckpointConfig,
CheckpointTrigger,
} from './types.js';
/**
* 检查点提交消息前缀
*/
const CHECKPOINT_PREFIX = 'checkpoint:';
/**
* 创建检查点选项
*/
export interface CreateCheckpointOptions {
name?: string;
description?: string;
trigger?: CheckpointTrigger;
toolCall?: { tool: string; params: Record<string, unknown> };
messageId?: string;
sessionId?: string;
turnIndex?: number;
}
/**
* 检查点存储
*/
export class CheckpointStore {
private shadowGit: ShadowGit;
private lock: CheckpointLock;
private commitMessageGenerator: CommitMessageGenerator;
private config: CheckpointConfig;
private index: Map<string, CheckpointMetadata> = new Map();
constructor(
shadowGit: ShadowGit,
lock: CheckpointLock,
commitMessageGenerator: CommitMessageGenerator,
config: CheckpointConfig
) {
this.shadowGit = shadowGit;
this.lock = lock;
this.commitMessageGenerator = commitMessageGenerator;
this.config = config;
}
/**
* 从 Git 历史加载检查点索引
*/
async loadIndex(): Promise<void> {
try {
const commits = await this.shadowGit.getCommits(this.config.maxCheckpoints);
for (const commit of commits) {
if (commit.message.startsWith(CHECKPOINT_PREFIX)) {
try {
const jsonStr = commit.message.slice(CHECKPOINT_PREFIX.length);
const metadata = JSON.parse(jsonStr) as CheckpointMetadata;
metadata.commitHash = commit.hash;
this.index.set(metadata.id, metadata);
} catch {
// 解析失败,跳过
}
}
}
} catch {
// 仓库可能是空的
}
}
/**
* 创建检查点
*/
async create(
options: CreateCheckpointOptions,
currentSessionId?: string | null,
onCheckpointCreated?: (id: string) => void
): Promise<CheckpointMetadata> {
return this.lock.withLock(async () => {
return this.createInternal(options, currentSessionId, onCheckpointCreated);
});
}
/**
* 创建内部检查点(不使用锁,供内部调用)
*/
async createInternal(
options: CreateCheckpointOptions,
currentSessionId?: string | null,
onCheckpointCreated?: (id: string) => void
): Promise<CheckpointMetadata> {
const id = nanoid(10);
const timestamp = Date.now();
const trigger = options.trigger || 'manual';
// 创建元数据
const metadata: CheckpointMetadata = {
id,
name: options.name,
description: options.description,
timestamp,
trigger,
toolCall: options.toolCall,
commitHash: '',
filesChanged: 0,
messageId: options.messageId,
sessionId: options.sessionId || currentSessionId || undefined,
turnIndex: options.turnIndex,
};
// 获取变更文件数
let filesChanged: Array<{ path: string; type: string }> = [];
try {
const diff = await this.shadowGit.getWorkingDirDiff();
metadata.filesChanged = diff.files.length;
filesChanged = diff.files;
} catch {
// 忽略
}
// 生成智能提交消息
const humanReadableMessage = this.commitMessageGenerator.generateMessage(
trigger,
options.toolCall,
filesChanged as Array<{ path: string; type: 'added' | 'modified' | 'deleted' | 'renamed' }>
);
// 创建 commit
const commitMessage = CHECKPOINT_PREFIX + JSON.stringify({
...metadata,
_readableMessage: humanReadableMessage,
});
const commitHash = await this.shadowGit.createCommit(commitMessage);
metadata.commitHash = commitHash;
// 更新索引
this.index.set(id, metadata);
// 通知回调
onCheckpointCreated?.(id);
return metadata;
}
/**
* 列出所有检查点
*/
list(): CheckpointMetadata[] {
return Array.from(this.index.values()).sort(
(a, b) => b.timestamp - a.timestamp
);
}
/**
* 获取检查点
*/
get(idOrHash: string): CheckpointMetadata | null {
// 先按 ID 查找
if (this.index.has(idOrHash)) {
return this.index.get(idOrHash)!;
}
// 再按 commit hash 查找
for (const checkpoint of this.index.values()) {
if (checkpoint.commitHash.startsWith(idOrHash)) {
return checkpoint;
}
}
return null;
}
/**
* 获取最新检查点
*/
getLatest(): CheckpointMetadata | null {
const checkpoints = this.list();
return checkpoints[0] || null;
}
/**
* 删除检查点
*/
delete(checkpointId: string): CheckpointMetadata | null {
const checkpoint = this.index.get(checkpointId);
if (!checkpoint) {
return null;
}
this.index.delete(checkpointId);
return checkpoint;
}
/**
* 判断是否应该为指定工具创建检查点
*/
shouldCreateForTool(tool: string): boolean {
if (!this.config.enabled) return false;
const { autoCheckpoint } = this.config;
switch (tool) {
case 'write_file':
return autoCheckpoint.beforeWrite;
case 'edit_file':
return autoCheckpoint.beforeEdit;
case 'delete_file':
return autoCheckpoint.beforeDelete;
case 'move_file':
case 'copy_file':
return autoCheckpoint.beforeMove;
case 'bash':
return autoCheckpoint.beforeBash;
default:
return false;
}
}
/**
* 生成检查点描述
*/
generateDescription(tool: string, params: Record<string, unknown>): string {
switch (tool) {
case 'write_file':
return `Write file: ${params.file_path || params.path}`;
case 'edit_file':
return `Edit file: ${params.file_path || params.path}`;
case 'delete_file':
return `Delete file: ${params.file_path || params.path}`;
case 'move_file':
return `Move: ${params.source} -> ${params.destination}`;
case 'copy_file':
return `Copy: ${params.source} -> ${params.destination}`;
case 'bash':
return `Bash: ${String(params.command).slice(0, 50)}`;
default:
return `Tool: ${tool}`;
}
}
/**
* 获取检查点数量
*/
get size(): number {
return this.index.size;
}
/**
* 获取配置
*/
getConfig(): CheckpointConfig {
return this.config;
}
}