CC源码剖析 #10 · 状态管理:memdir 与会话持久化

基于 2026-03-31 公开的源码快照,~512,000 行代码


一、memdir 定位

memdir(Memory Directory)是 Claude Code 的会话持久化与记忆系统——负责在会话之间保存上下文,让 Agent 能够"记住"之前的对话内容。

会话开始
    │
    ├─→ 读取 MEMORY.md
    │       │
    │       ▼
    │    系统提示 + 记忆内容
    │
    ├─→ 会话进行中...
    │
    └─→ 会话结束
            │
            ├─→ 更新 MEMORY.md(新增记忆)
            ├─→ 保存会话记录
            └─→ 保存会话元数据

二、目录结构

文件:src/memdir/paths.ts

2.1 路径解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
export function getMemoryBaseDir(): string {
  // 优先级:
  // 1. CLAUDE_CODE_REMOTE_MEMORY_DIR 环境变量(CCR 远程模式)
  // 2. ~/.claude(默认配置目录)
}

// 内存目录布局
~/.claude/
└── memory/
    └── projects/
        └── {sanitized-cwd}/
            └── memory/
                ├── MEMORY.md          # 主入口
                ├── {topic}.md        # 主题记忆文件
                └── {session}/        # 按会话的记录

2.2 路径安全验证

1
2
3
4
5
6
7
8
// SECURITY: 验证内存路径,防止路径遍历攻击
function validateMemoryPath(raw: string | undefined): string | undefined {
  // 拒绝:
  // - 相对路径(可能被解释为 CWD 相对)
  // - 根目录("/", "C:")
  // - UNC 路径(\\server\share)
  // - 含 null 字节
}

2.3 开关控制

1
2
3
4
5
6
7
export function isAutoMemoryEnabled(): boolean {
  // 1. CLAUDE_CODE_DISABLE_AUTO_MEMORY = 1 → 关闭
  // 2. CLAUDE_CODE_SIMPLE (--bare) → 关闭
  // 3. CCR 无持久化 → 关闭
  // 4. settings.json autoMemoryEnabled
  // 5. 默认:开启
}

三、MEMORY.md 入口

文件:src/memdir/memdir.ts

3.1 入口文件格式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
name: "memory"
description: "Auto memory for Claude Code sessions"
---

# Memory

## Index
- [[topic-a]] — Topic A summary
- [[topic-b]] — Topic B summary

## Individual

### Topic A
Topic A detail...

## What Not to Save
- API keys
- Passwords
- Tokens

## When to Access
Use for context on user's project and preferences.

3.2 大小限制

1
2
3
4
5
6
7
8
9
const MAX_ENTRYPOINT_LINES = 200
const MAX_ENTRYPOINT_BYTES = 25_000

// 超限时截断并添加警告
function truncateEntrypointContent(raw: string): EntrypointTruncation {
  // 1. 先按行截断(200行)
  // 2. 再按字节截断(25KB)
  // 3. 添加警告信息
}

四、记忆类型

文件:src/memdir/memoryTypes.ts

4.1 记忆分类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const MEMORY_FRONTMATTER_EXAMPLE = `
# 记忆类型说明

## Index(索引)
短条目,每个一行,格式:- [[topic]] — 摘要
用于快速浏览和检索

## Individual(个体记忆)
详细记忆,每个主题一个文件
格式:### Topic Name\n\n详细描述...

## What Not to Save(禁止保存)
敏感信息列表
- API 密钥
- 密码
- Token

## When to Access(访问时机)
说明何时应该访问这些记忆
`

4.2 记忆内容规则

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 应该保存:
// - 项目结构
// - 用户偏好
// - 重要的决策和结论
// - 上下文信息(当前任务、目标)

// 不应该保存:
// - API 密钥
// - 密码
// - 敏感凭证
// - 训练数据(版权问题)

五、记忆提取(Extract Memories)

5.1 背景 Agent 提取

1
2
3
4
5
// 会话结束时,背景 Agent 提取新记忆
export function isExtractModeActive(): boolean {
  // GrowthBook flag: tengu_passport_quail
  // 只有交互式会话才能提取
}

5.2 提取流程

1
2
3
4
// 1. 分析会话消息
// 2. 识别有价值的信息点
// 3. 更新 MEMORY.md 索引
// 4. 创建或更新主题文件

5.3 增量更新

1
2
// 只更新新增内容,不重复已有记忆
// hasMemoryWritesSince() 检查是否有新的写入

六、会话持久化

6.1 会话存储

文件:src/utils/sessionStorage.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
interface SessionStorage {
  sessionId: string
  messages: Message[]
  metadata: {
    title: string
    createdAt: number
    lastActiveAt: number
    model: string
    tools: string[]
  }
  attachments: Attachment[]
}

// 保存
saveSession(sessionId, messages, metadata)

// 加载
loadSession(sessionId): SessionStorage

// 搜索
searchSessions(query: string): SessionSummary[]

6.2 会话恢复

1
2
3
4
5
// resume 时重建消息历史
loadConversationForResume(sessionId: string): Message[]

// 处理恢复后的会话
processResumedConversation(sessionId: string): Promise<Message[]>

七、文件历史(File History)

文件:src/utils/fileHistory.ts

7.1 文件状态追踪

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
interface FileHistoryState {
  files: Map<string, FileState>
  sessions: SessionHistory[]
}

interface FileState = {
  path: string
  changes: FileChange[]
  lastSeen: number
}

interface FileChange = {
  type: 'edit' | 'create' | 'delete'
  timestamp: number
  sessionId: string
  diff?: string
}

7.2 文件重写追踪

1
2
3
4
5
// 检测文件是否被外部修改
// 如果是,提供重新加载选项

fileHistoryMakeSnapshot(): FileHistorySnapshot
fileHistoryRewind(snapshot: FileHistorySnapshot): void

八、团队记忆(Team Memory)

文件:src/memdir/teamMemPaths.ts

8.1 TEAMMEM Feature Flag

1
2
3
const teamMemPaths = feature('TEAMMEM')
  ? require('./teamMemPaths.js')
  : null

8.2 跨会话记忆同步

1
2
3
// 团队成员共享的记忆
// 存储在共享位置
// 所有团队成员可见

九、Memory 与 System Prompt

9.1 构建 Memory Prompt

1
2
3
4
5
6
7
8
// 将 MEMORY.md 内容注入系统提示
function buildMemoryPrompt(): string {
  const memoryFiles = getMemoryFiles()
  const memoryContent = memoryFiles
    .map(f => readFile(f))
    .join('\n\n')
  return `## Memory\n\n${memoryContent}`
}

9.2 访问时机

1
2
3
// 每次新会话开始时加载
// Turn 开始时加载相关记忆
// 显式请求时加载(/remember 命令)

十、相关命令

命令功能
/remember显示/搜索记忆
/forget删除特定记忆
/memory管理记忆设置

下一步

下一篇:11 - 插件系统:Plugin 架构与生命周期,深入 plugins/ 模块的插件架构与生命周期管理。


Claude Code 源码研究系列 · 2026 · skyseraph

SkySeraph
SkySeraph
AI for All & All for AI
留言 Comments