分为 Session Memory和 Auto Memory (跨 session).
Session Memory
每个 session 维护一个 summary.md 文件, 后台用 forked subagent (并且限制权限只能编辑这个 summary.md 文件) 达到条件 (距离上次更新后新增 token 数以及 tool calls 数等) 后更新.
/**
* Session Memory automatically maintains a markdown file with notes about the current conversation.
* It runs periodically in the background using a forked subagent to extract key information
* without interrupting the main conversation flow.
*/
/**
* Configuration for session memory extraction thresholds
*/
export type SessionMemoryConfig = {
/** Minimum context window tokens before initializing session memory.
* Uses the same token counting as autocompact (input + output + cache tokens)
* to ensure consistent behavior between the two features. */
minimumMessageTokensToInit: number
/** Minimum context window growth (in tokens) between session memory updates.
* Uses the same token counting as autocompact (tokenCountWithEstimation)
* to measure actual context growth, not cumulative API usage. */
minimumTokensBetweenUpdate: number
/** Number of tool calls between session memory updates */
toolCallsBetweenUpdates: number
}
export const DEFAULT_SESSION_MEMORY_CONFIG: SessionMemoryConfig = {
minimumMessageTokensToInit: 10000,
minimumTokensBetweenUpdate: 5000,
toolCallsBetweenUpdates: 3,
}
// Trigger extraction when:
// 1. Both thresholds are met (tokens AND tool calls), OR
// 2. No tool calls in last turn AND token threshold is met
// (to ensure we extract at natural conversation breaks)
//
// IMPORTANT: The token threshold (minimumTokensBetweenUpdate) is ALWAYS required.
// Even if the tool call threshold is met, extraction won't happen until the
// token threshold is also satisfied. This prevents excessive extractions.
/**
* Returns the session memory directory path for the current session with trailing separator.
* Path format: {projectDir}/{sessionId}/session-memory/
*/
export function getSessionMemoryDir(): string {
return join(getProjectDir(getCwd()), getSessionId(), 'session-memory') + sep
}
/**
* Returns the session memory file path for the current session.
* Path format: {projectDir}/{sessionId}/session-memory/summary.md
*/
export function getSessionMemoryPath(): string {
return join(getSessionMemoryDir(), 'summary.md')
}
Prompt 模板:
# Session Title
_A short and distinctive 5-10 word descriptive title for the session. Super info dense, no filler_
# Current State
_What is actively being worked on right now? Pending tasks not yet completed. Immediate next steps._
# Task specification
_What did the user ask to build? Any design decisions or other explanatory context_
后面还有:
Files and FunctionsWorkflowErrors & CorrectionsCodebase and System DocumentationLearningsKey resultsWorklog
- The file must maintain its exact structure with all sections, headers, and italic descriptions intact
-- NEVER modify, delete, or add section headers
-- NEVER modify or delete the italic _section description_ lines
-- ONLY update the actual content that appears BELOW the italic _section descriptions_
更新动作由一个 forked subagent 执行.
// Run session memory extraction using runForkedAgent for prompt caching
// runForkedAgent creates an isolated context to prevent mutation of parent state
// Pass setupContext.readFileState so the forked agent can edit the memory file
/**
* Creates a canUseTool function that only allows Edit for the exact memory file.
*/
if (
tool.name === FILE_EDIT_TOOL_NAME &&
typeof input === 'object' &&
input !== null &&
'file_path' in input
) {
const filePath = input.file_path
if (typeof filePath === 'string' && filePath === memoryPath) {
return { behavior: 'allow' as const, updatedInput: input }
}
}
用途: 给 autocompact 提前备料
一旦满足 autocompact 条件, 先试 session memory compaction.
// EXPERIMENT: Try session memory compaction first
const sessionMemoryResult = await trySessionMemoryCompaction(
messages,
toolUseContext.agentId,
recompactionInfo.autoCompactThreshold,
)
/**
* Try to use session memory for compaction instead of traditional compaction.
* Returns null if session memory compaction cannot be used.
*
* Handles two scenarios:
* 1. Normal case: lastSummarizedMessageId is set, keep only messages after that ID
* 2. Resumed session: lastSummarizedMessageId is not set but session memory has content,
* keep all messages but use session memory as the summary
*/
保留最近一段原始消息
/**
* Calculate the starting index for messages to keep after compaction.
* Starts from lastSummarizedMessageId, then expands backwards to meet minimums:
* - At least config.minTokens tokens
* - At least config.minTextBlockMessages messages with text blocks
* Stops expanding if config.maxTokens is reached.
* Also ensures tool_use/tool_result pairs are not split.
*/
export const DEFAULT_SM_COMPACT_CONFIG: SessionMemoryCompactConfig = {
minTokens: 10_000,
minTextBlockMessages: 5,
maxTokens: 40_000,
}
避免切断 tool use 和 tool result 配对.
* Adjust the start index to ensure we don't split tool_use/tool_result pairs
* or thinking blocks that share the same message.id with kept assistant messages.
*
* If ANY message we're keeping contains tool_result blocks, we need to
* include the preceding assistant message(s) that contain the matching tool_use blocks.
* API error: orphan tool_result references non-existent tool_use
预算控制, 避免 summary.md 本身太大.
const MAX_SECTION_LENGTH = 2000
const MAX_TOTAL_SESSION_MEMORY_TOKENS = 12000
更新 prompt 里明确提醒
- Keep each section under ~${MAX_SECTION_LENGTH} tokens/words
- IMPORTANT: Always update "Current State" to reflect the most recent work
如果总量已经超了, 追加压缩提醒
CRITICAL: The session memory file is currently ~${totalTokens} tokens, which exceeds the maximum of ${MAX_TOTAL_SESSION_MEMORY_TOKENS} tokens. You MUST condense the file to fit within this budget.
进入 compact 流程时, 也会再做一次截断保护
/**
* Truncate session memory sections that exceed the per-section token limit.
* Used when inserting session memory into compact messages to prevent
* oversized session memory from consuming the entire post-compact token budget.
*/
另一个用途: 给 skillify 提供会话背景
把当前 session 炼成 skill.
## Your Session Context
Here is the session memory summary:
<session_memory>
</session_memory>
const sessionMemory =
(await getSessionMemoryContent()) ?? 'No session memory available.'
然后它再把本次 session 的 user messages 补进去
const userMessages = extractUserMessages(
getMessagesAfterCompactBoundary(context.messages),
)
Auto Memory
需要打开 feature, 见 文档. 跨 session, 支持召回.
/**
* Whether auto-memory features are enabled (memdir, agent memory, past session search).
*/
MEMORY.md 是入口索引
const AUTO_MEM_DIRNAME = 'memory'
const AUTO_MEM_ENTRYPOINT_NAME = 'MEMORY.md'
默认目录解析
/**
* Returns the auto-memory directory path.
*
* Resolution order:
* 1. CLAUDE_COWORK_MEMORY_PATH_OVERRIDE env var
* 2. autoMemoryDirectory in settings.json
* 3. <memoryBase>/projects/<sanitized-git-root>/memory/
*/
这里的 MEMORY.md 负责做入口索引, durable memory 写进各自的 topic file.
`MEMORY.md` is an index, not a memory
each entry should be one line, under ~150 characters
Saving a memory is a two-step process:
**Step 1** — write the memory to its own file
**Step 2** — add a pointer to that file in `MEMORY.md`
/**
* Extracts durable memories from the current session transcript
* and writes them to the auto-memory directory (~/.claude/projects/<path>/memory/).
*
* It runs once at the end of each complete query loop
*/
MEMORY.md 会被自动注入上下文
" (user's auto-memory, persists across conversations)"
支持按 query 检索 (通过读 topic files 的 frontmatter) 相关 memories
/**
* Find memory files relevant to a query by scanning memory file headers
* and asking Sonnet to select the most relevant ones.
*
* Returns absolute file paths + mtime of the most relevant memories
* (up to 5). Excludes MEMORY.md (already loaded in system prompt).
*/
