# IC Coder 会话存储技术文档

## 1. 概述

IC Coder 的会话存储系统负责持久化保存用户与 AI 的对话历史，支持多项目、多任务的会话管理。系统采用文件系统存储方案，将会话数据按项目和任务组织，便于管理和检索。

### 1.1 核心特性

- **多项目支持**：不同项目的会话数据独立存储
- **任务级管理**：每个会话作为独立任务进行管理
- **分页加载**：支持历史会话的分页查询，提升性能
- **实时更新**：会话数据实时保存，防止数据丢失
- **统计信息**：记录 Token 使用量、对话轮次等统计数据

### 1.2 技术栈

- **存储方式**：文件系统（JSON/JSONL 格式）
- **存储位置**：`~/.iccoder/projects/{项目路径编码}/{taskId}/`
- **数据格式**：
  - `meta.json`：任务元数据
  - `conversation.json`：完整对话历史
  - `conversation_meta.jsonl`：对话轮次元数据（JSONL 格式）

---

## 2. 架构设计

### 2.1 目录结构

```
~/.iccoder/
└── projects/
    └── {项目路径编码}/
        └── {taskId}/
            ├── meta.json              # 任务元数据
            ├── conversation.json      # 对话历史
            └── conversation_meta.jsonl # 对话元数据
```

**项目路径编码规则**：
- 移除冒号 `:`
- 将斜杠 `/` 和反斜杠 `\` 替换为 `--`
- 示例：`C:\Users\admin\Documents\Project` → `C--Users--admin--Documents--Project`

**任务 ID 格式**：
- 格式：`task_{date}_{sequence}`
- 示例：`task_20231226_a3f9k2`
- `date`：8 位日期（YYYYMMDD）
- `sequence`：6 位随机字符串

### 2.2 核心类：ChatHistoryManager

`ChatHistoryManager` 是会话存储的核心管理类，采用单例模式设计。

**主要职责**：
1. 管理会话存储目录
2. 创建和切换任务
3. 保存和加载对话历史
4. 记录统计信息
5. 提供会话历史查询接口

**关键属性**：
```typescript
private static instance: ChatHistoryManager;
private baseDir: string;                    // ~/.iccoder
private currentTaskId: string | null;       // 当前任务 ID
private currentProjectPath: string | null;  // 当前项目路径
```

---

## 3. 数据模型

### 3.1 TaskMeta（任务元数据）

存储在 `meta.json` 文件中，记录任务的基本信息和统计数据。

```typescript
interface TaskMeta {
  taskId: string;           // 任务 ID
  taskName: string;         // 任务名称
  projectPath: string;      // 项目路径
  createdAt: string;        // 创建时间（ISO 8601）
  updatedAt: string;        // 更新时间（ISO 8601）
  stats: {
    credits: number;        // 消耗的积分
    totalTokens: number;    // 总 Token 数
    inputTokens: number;    // 输入 Token 数
    outputTokens: number;   // 输出 Token 数
  };
}
```

**示例**：
```json
{
  "taskId": "task_20231226_a3f9k2",
  "taskName": "实现计数器功能",
  "projectPath": "C:\\Users\\admin\\Documents\\Project",
  "createdAt": "2023-12-26T10:30:00.000Z",
  "updatedAt": "2023-12-26T11:45:00.000Z",
  "stats": {
    "credits": 0,
    "totalTokens": 15420,
    "inputTokens": 8200,
    "outputTokens": 7220
  }
}
```

### 3.2 ChatMessage（对话消息）

存储在 `conversation.json` 文件中，记录完整的对话历史。

**消息类型枚举**：
```typescript
enum MessageType {
  USER = "USER",                           // 用户消息
  AI = "AI",                               // AI 消息
  SYSTEM = "SYSTEM",                       // 系统消息
  TOOL_EXECUTION_RESULT = "TOOL_EXECUTION_RESULT"  // 工具执行结果
}
```

**用户消息**：
```typescript
interface UserMessage {
  type: MessageType.USER;
  contents: Array<{
    type: "TEXT";
    text: string;
  }>;
}
```

**AI 消息**：
```typescript
interface AiMessage {
  type: MessageType.AI;
  text: string;
  toolExecutionRequests?: Array<{
    id: string;
    toolName: string;
    parameters: any;
  }>;
}
```

**系统消息**：
```typescript
interface SystemMessage {
  type: MessageType.SYSTEM;
  text: string;
}
```

**工具执行结果消息**：
```typescript
interface ToolExecutionResultMessage {
  type: MessageType.TOOL_EXECUTION_RESULT;
  id: string;
  toolName: string;
  text: string;
}
```

### 3.3 ConversationMeta（对话轮次元数据）

存储在 `conversation_meta.jsonl` 文件中，每行一条记录（JSONL 格式）。

```typescript
interface ConversationMeta {
  turnId: number;           // 对话轮次 ID
  timestamp: string;        // 时间戳（ISO 8601）
  usage?: {
    inputTokens?: number;
    outputTokens?: number;
    totalTokens?: number;
  };
  model?: string;           // 使用的模型
  duration?: number;        // 耗时（毫秒）
}
```

**示例**：
```jsonl
{"turnId":1,"timestamp":"2023-12-26T10:30:15.000Z","usage":{"inputTokens":120,"outputTokens":350,"totalTokens":470},"model":"gpt-4","duration":2500}
{"turnId":2,"timestamp":"2023-12-26T10:32:30.000Z","usage":{"inputTokens":200,"outputTokens":450,"totalTokens":650},"model":"gpt-4","duration":3200}
```

---

## 4. 核心功能实现

### 4.1 任务创建

**方法**：`createTask(projectPath: string, taskName: string): Promise<TaskMeta>`

**流程**：
1. 生成唯一的任务 ID
2. 创建任务元数据对象
3. 创建任务目录
4. 保存 `meta.json`
5. 初始化空的 `conversation.json`
6. 设置为当前任务

**代码位置**：`chatHistoryManager.ts:114-146`

```typescript
public async createTask(projectPath: string, taskName: string): Promise<TaskMeta> {
  const taskId = this.generateTaskId();
  const now = new Date().toISOString();

  const meta: TaskMeta = {
    taskId,
    taskName,
    projectPath,
    createdAt: now,
    updatedAt: now,
    stats: {
      credits: 0,
      totalTokens: 0,
      inputTokens: 0,
      outputTokens: 0
    }
  };

  this.currentTaskId = taskId;
  this.currentProjectPath = projectPath;

  // 创建任务目录
  const taskDir = this.getTaskDir(projectPath, taskId);
  await this.ensureTaskDir(taskDir);

  // 保存 meta.json
  await this.saveTaskMeta(meta);

  // 初始化空的 conversation.json
  await this.saveConversation([]);

  return meta;
}
```

### 4.2 消息保存

系统提供了四种消息保存方法：

#### 4.2.1 添加用户消息

**方法**：`addUserMessage(text: string): Promise<void>`

**代码位置**：`chatHistoryManager.ts:285-299`

```typescript
public async addUserMessage(text: string): Promise<void> {
  await this.ensureCurrentTask();
  const messages = await this.loadConversation();

  const userMessage: UserMessage = {
    type: MessageType.USER,
    contents: [{ type: "TEXT", text }]
  };

  messages.push(userMessage);
  await this.saveConversation(messages);

  // 更新任务元数据
  await this.updateTaskTimestamp();
}
```

#### 4.2.2 添加 AI 消息

**方法**：`addAiMessage(text: string, toolRequests?: any[]): Promise<void>`

**代码位置**：`chatHistoryManager.ts:304-319`

#### 4.2.3 添加系统消息

**方法**：`addSystemMessage(text: string): Promise<void>`

**代码位置**：`chatHistoryManager.ts:324-335`

#### 4.2.4 添加工具执行结果

**方法**：`addToolExecutionResult(id: string, toolName: string, result: string): Promise<void>`

**代码位置**：`chatHistoryManager.ts:340-353`

### 4.3 对话元数据记录

**方法**：`recordTurnMeta(turnId, usage?, model?, duration?): Promise<void>`

**功能**：记录每轮对话的元数据，包括 Token 使用量、模型信息、耗时等。

**代码位置**：`chatHistoryManager.ts:358-378`

```typescript
public async recordTurnMeta(
  turnId: number,
  usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number },
  model?: string,
  duration?: number
): Promise<void> {
  const meta: ConversationMeta = {
    turnId,
    timestamp: new Date().toISOString(),
    usage,
    model,
    duration
  };

  await this.appendConversationMeta(meta);

  // 更新任务统计
  if (usage) {
    await this.updateTaskStats(usage);
  }
}
```

### 4.4 会话历史查询

**方法**：`getConversationHistoryList(projectPath, offset, limit): Promise<{items, total, hasMore}>`

**功能**：分页查询项目的会话历史列表。

**参数**：
- `projectPath`：项目路径
- `offset`：偏移量（从第几条开始，默认 0）
- `limit`：每页数量（默认 10）

**返回值**：
```typescript
{
  items: Array<{
    id: string;        // 任务 ID
    title: string;     // 会话标题（第一句用户消息）
    timestamp: string; // 创建时间
  }>;
  total: number;       // 总数
  hasMore: boolean;    // 是否还有更多
}
```

**代码位置**：`chatHistoryManager.ts:525-590`

**实现逻辑**：
1. 获取项目的所有任务列表（按更新时间倒序）
2. 根据 offset 和 limit 进行分页
3. 读取每个任务的 `conversation.json`
4. 提取第一条用户消息作为标题（截取前 50 个字符）
5. 返回分页结果

---

## 5. 前端集成

### 5.1 会话历史栏组件

**文件**：`conversationHistoryBar.ts`

**组件结构**：
- 下拉按钮：显示 "Past Conversations"
- 下拉菜单：显示会话历史列表
- 新建按钮：创建新会话

**关键功能**：

#### 5.1.1 加载会话历史

```javascript
function loadMoreHistory() {
  if (isLoadingHistory || (currentOffset > 0 && !hasMoreHistory)) {
    return;
  }

  // 检查是否已达到最大数量（100 条）
  if (currentOffset >= MAX_HISTORY_ITEMS) {
    return;
  }

  isLoadingHistory = true;
  vscode.postMessage({
    command: 'loadConversationHistory',
    offset: currentOffset,
    limit: HISTORY_PAGE_SIZE
  });
}
```

#### 5.1.2 渲染会话列表

```javascript
function renderConversationHistory(data) {
  isLoadingHistory = false;

  // 追加新数据
  conversationHistory = conversationHistory.concat(data.items);
  totalHistory = data.total;
  hasMoreHistory = data.hasMore;
  currentOffset += data.items.length;

  // 渲染所有历史记录
  historyList.innerHTML = conversationHistory.map(item => `
    <div class="history-item" onclick="selectConversation('${item.id}')">
      <div class="history-item-title">${item.title || '未命名会话'}</div>
      <div class="history-item-time">${formatTime(item.timestamp)}</div>
    </div>
  `).join('');

  // 如果还有更多数据，添加"加载更多"提示
  if (hasMoreHistory && currentOffset < MAX_HISTORY_ITEMS) {
    historyList.innerHTML += `
      <div class="history-load-more">
        <span>滚动加载更多...</span>
      </div>
    `;
  }
}
```

#### 5.1.3 滚动加载

```javascript
historyDropdownMenu.addEventListener('scroll', () => {
  const menu = historyDropdownMenu;
  const scrollTop = menu.scrollTop;
  const scrollHeight = menu.scrollHeight;
  const clientHeight = menu.clientHeight;

  // 当滚动到距离底部 50px 时，加载更多
  if (scrollHeight - scrollTop - clientHeight < 50) {
    loadMoreHistory();
  }
});
```

#### 5.1.4 时间格式化

```javascript
function formatTime(timestamp) {
  const date = new Date(timestamp);
  const now = new Date();
  const diff = now - date;

  if (diff < 60000) return '刚刚';
  if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前';
  if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前';
  if (diff < 604800000) return Math.floor(diff / 86400000) + '天前';

  // 超过7天显示具体日期
  return date.toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit'
  });
}
```

### 5.2 后端消息处理

**文件**：`ICHelperPanel.ts`

**消息处理流程**：

```typescript
case "loadConversationHistory":
  // 加载会话历史（支持分页）
  loadConversationHistory(panel, message.offset || 0, message.limit || 10);
  break;

case "selectConversation":
  // 选择会话（暂未实现）
  break;

case "createNewConversation":
  // 创建新会话 - 在当前编辑器组中打开新标签页
  showICHelperPanel(context, panel.viewColumn);
  break;
```

**加载会话历史实现**：

```typescript
async function loadConversationHistory(
  panel: vscode.WebviewPanel,
  offset: number = 0,
  limit: number = 10
) {
  try {
    const historyManager = ChatHistoryManager.getInstance();
    const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;

    if (!workspacePath) {
      // 没有打开的工作区，返回空历史
      panel.webview.postMessage({
        command: "conversationHistory",
        items: [],
        total: 0,
        hasMore: false,
      });
      return;
    }

    // 获取会话历史列表（支持分页）
    const result = await historyManager.getConversationHistoryList(
      workspacePath,
      offset,
      limit
    );

    // 发送会话历史到前端
    panel.webview.postMessage({
      command: "conversationHistory",
      items: result.items,
      total: result.total,
      hasMore: result.hasMore,
    });
  } catch (error) {
    console.error("加载会话历史失败:", error);
    // 发生错误时返回空历史
    panel.webview.postMessage({
      command: "conversationHistory",
      items: [],
      total: 0,
      hasMore: false,
    });
  }
}
```

---

## 6. 使用示例

### 6.1 创建新任务并保存对话

```typescript
const historyManager = ChatHistoryManager.getInstance();

// 创建新任务
const task = await historyManager.createTask(
  'C:\\Users\\admin\\Documents\\Project',
  '实现计数器功能'
);

// 添加用户消息
await historyManager.addUserMessage('请帮我生成一个4位计数器');

// 添加 AI 消息
await historyManager.addAiMessage(
  '好的，我来帮你生成一个4位计数器...',
  [{ id: '1', toolName: 'generateCode', parameters: {} }]
);

// 添加工具执行结果
await historyManager.addToolExecutionResult(
  '1',
  'generateCode',
  '代码生成成功'
);

// 记录对话元数据
await historyManager.recordTurnMeta(
  1,
  { inputTokens: 120, outputTokens: 350, totalTokens: 470 },
  'gpt-4',
  2500
);
```

### 6.2 查询会话历史

```typescript
const historyManager = ChatHistoryManager.getInstance();

// 获取第一页（前10条）
const page1 = await historyManager.getConversationHistoryList(
  'C:\\Users\\admin\\Documents\\Project',
  0,
  10
);

console.log('总数:', page1.total);
console.log('是否还有更多:', page1.hasMore);
console.log('会话列表:', page1.items);

// 获取第二页（第11-20条）
const page2 = await historyManager.getConversationHistoryList(
  'C:\\Users\\admin\\Documents\\Project',
  10,
  10
);
```

### 6.3 切换任务

```typescript
const historyManager = ChatHistoryManager.getInstance();

// 切换到指定任务
const success = await historyManager.switchTask(
  'C:\\Users\\admin\\Documents\\Project',
  'task_20231226_a3f9k2'
);

if (success) {
  // 获取当前任务会话
  const session = await historyManager.getCurrentTaskSession();
  console.log('任务元数据:', session.meta);
  console.log('对话历史:', session.messages);
  console.log('对话元数据:', session.conversationMeta);
}
```

---

## 7. 性能优化

### 7.1 分页加载

- 前端默认每页加载 10 条记录
- 最多显示 100 条历史记录
- 滚动到底部时自动加载下一页

### 7.2 懒加载

- 只在打开下拉菜单时才加载会话历史
- 避免不必要的文件读取操作

### 7.3 缓存机制

- 前端缓存已加载的会话列表
- 避免重复请求相同数据

### 7.4 文件格式优化

- 使用 JSONL 格式存储对话元数据，支持追加写入
- 避免频繁读写整个文件

---

## 8. 错误处理

### 8.1 目录不存在

系统会自动创建不存在的目录：

```typescript
private async ensureTaskDir(taskDir: string): Promise<void> {
  try {
    const uri = vscode.Uri.file(taskDir);
    try {
      await vscode.workspace.fs.stat(uri);
    } catch {
      // 目录不存在，创建它
      await vscode.workspace.fs.createDirectory(uri);
      console.log(`创建任务目录: ${taskDir}`);
    }
  } catch (error) {
    console.error("创建任务目录失败:", error);
    throw error;
  }
}
```

### 8.2 文件读取失败

读取失败时返回默认值：

```typescript
private async loadConversation(): Promise<ChatMessage[]> {
  try {
    const uri = vscode.Uri.file(conversationPath);
    const content = await vscode.workspace.fs.readFile(uri);
    const data = Buffer.from(content).toString('utf-8');
    return JSON.parse(data);
  } catch (error) {
    // 文件不存在或读取失败，返回空数组
    return [];
  }
}
```

### 8.3 无工作区处理

没有打开工作区时，自动创建默认任务：

```typescript
private async ensureCurrentTask(): Promise<void> {
  if (!this.currentTaskId || !this.currentProjectPath) {
    const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
    if (workspacePath) {
      await this.createTask(workspacePath, "默认任务");
    } else {
      throw new Error("没有打开的工作区，无法创建任务");
    }
  }
}
```

---

## 9. 未来扩展

### 9.1 会话切换功能

目前 `selectConversation` 功能暂未实现，未来可以支持：
- 点击历史会话，加载该会话的完整对话历史
- 在新标签页中打开历史会话
- 继续历史会话的对话

### 9.2 会话搜索

- 支持按关键词搜索会话
- 支持按时间范围筛选
- 支持按 Token 使用量排序

### 9.3 会话导出

- 导出为 Markdown 格式
- 导出为 JSON 格式
- 导出为 PDF 格式

### 9.4 会话统计

- 显示总对话轮次
- 显示总 Token 使用量
- 显示平均响应时间

### 9.5 云端同步

- 支持将会话数据同步到云端
- 支持多设备访问
- 支持团队协作

---

## 10. 总结

IC Coder 的会话存储系统采用文件系统存储方案，具有以下优势：

1. **简单可靠**：无需额外的数据库依赖
2. **易于备份**：直接复制文件即可备份
3. **跨平台**：支持 Windows、macOS、Linux
4. **可扩展**：易于添加新的数据字段
5. **高性能**：分页加载，避免一次性加载大量数据

系统已经实现了核心的会话管理功能，包括任务创建、消息保存、历史查询等，为用户提供了完整的会话历史管理体验。
