基于 Claude Code npm 包 source map 泄露快照 (2026-03-31) 的源码分析。
适用于团队内部分享,理解 AI Agent 长对话上下文管理的工程范式。
Claude Code 是一个多轮对话 Agent。用户在一次会话中可能进行 10-50+ 轮工具调用,每轮的工具结果(文件内容、grep 输出、bash 输出)都留在上下文中。
矛盾:上下文窗口有限(Sonnet 4.6: 200K tokens),但对话历史无限增长。
传统做法:截断旧消息或简单摘要。
Claude Code 的做法:一条五层流水线,每层针对不同场景,从最轻量到最重量级逐层升级。
文件:src/query.ts
依赖注入(src/query/deps.ts):
export type QueryDeps = {
callModel: typeof queryModelWithStreaming,
microcompact: typeof microcompactMessages, // ← Layer 1
autocompact: typeof autoCompactIfNeeded, // ← Layer 3
uuid: () => string,
}
文件:src/services/compact/microCompact.ts
export async function microcompactMessages(
messages: Message[],
toolUseContext?: ToolUseContext,
querySource?: QuerySource,
): Promise<MicrocompactResult> {
// ① 优先尝试 Time-based(cache 已冷)
const timeBasedResult = maybeTimeBasedMicrocompact(messages, querySource)
if (timeBasedResult) return timeBasedResult
// ② 其次尝试 Cached MC(cache 仍热)
if (feature('CACHED_MICROCOMPACT')) {
if (isCachedMicrocompactEnabled() && isModelSupportedForCacheEditing(model)
&& isMainThreadSource(querySource)) {
return await cachedMicrocompactPath(messages, querySource)
}
}
// ③ 都不满足 → 不压缩
return { messages }
}
触发条件:距上次助手回复超过 60 分钟(假设 API 端 cache 已过期)。
function maybeTimeBasedMicrocompact(messages, querySource) {
const trigger = evaluateTimeBasedTrigger(messages, querySource)
if (!trigger) return null
// 找出所有可压缩工具的 tool_use ID
const toolIds = collectCompactableToolIds(messages)
// 保留最近 N 个(config.keepRecent,最少 1 个)
const toKeep = toolIds.slice(-config.keepRecent)
const toClear = toolIds.filter(id => !toKeep.includes(id))
// 直接修改消息内容
for (const msg of messages) {
if (msg.type === 'user') {
for (const block of msg.message.content) {
if (block.type === 'tool_result' && toClear.includes(block.tool_use_id)) {
block.content = TIME_BASED_MC_CLEARED_MESSAGE
// → '[Old tool result content cleared]'
}
}
}
}
// 重置 Cached MC 状态(server 端引用已失效)
resetMicrocompactState()
notifyCacheDeletion() // 通知 cache-break 检测器
}
可压缩工具集合:
const COMPACTABLE_TOOLS = new Set([
'Read', // FileReadTool
'Bash', // BashTool (+ PowerShell)
'Grep', // GrepTool
'Glob', // GlobTool
'WebSearch', // WebSearchTool
'WebFetch', // WebFetchTool
'Edit', // FileEditTool
'Write', // FileWriteTool
])
触发条件:工具结果数量超过阈值(GrowthBook 配置)。
核心设计:不修改本地消息,而是生成 cache_edits 指令发给 API。
async function cachedMicrocompactPath(messages, querySource) {
// 收集所有可压缩工具 ID
const toolIds = collectCompactableToolIds(messages)
// 按用户消息分组注册
registerToolResultsGroupedByUserMessage(toolIds)
// 根据配置决定删除哪些
const toDelete = getToolResultsToDelete(config)
// 生成 cache_edits 块(不改本地消息!)
const cacheEditsBlock = createCacheEditsBlock(toDelete)
// 存入 pending 状态,等 API 调用时附带
pendingCacheEdits = cacheEditsBlock
// 通知 cache-break 检测器
notifyCacheDeletion()
// 返回原始消息(未修改)
return { messages, compactionInfo: { pendingCacheEdits } }
}
API 端消费:
// 在 API 调用前
const edits = consumePendingCacheEdits() // 取出并清空
if (edits) {
// 附加到 API 请求中
requestBody.cache_edits = edits
}
两种变体的效果图:
| 维度 | Time-based | Cached |
|---|---|---|
| 触发条件 | 距上次回复 > 60 分钟 | 工具结果数 > 阈值 |
| 假设 | API cache 已过期 | API cache 仍有效 |
| 修改方式 | 直接改消息内容 | 生成 cache_edits 指令 |
| Cache 影响 | 破坏(但反正已过期) | 不破坏 |
| 适用场景 | 隔夜/午休后恢复对话 | 持续对话中的增量清理 |
| Feature gate | 默认启用 | CACHED_MICROCOMPACT(内部用户) |
文件:src/services/compact/autoCompact.ts
// 常量
const MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20_000 // 预留给摘要输出
const AUTOCOMPACT_BUFFER_TOKENS = 13_000 // 触发缓冲区
const WARNING_THRESHOLD_BUFFER_TOKENS = 20_000 // UI 警告缓冲
const ERROR_THRESHOLD_BUFFER_TOKENS = 20_000 // 错误态缓冲
// 有效窗口 = 上下文窗口 - 摘要输出预留
export function getEffectiveContextWindowSize(model: string): number {
const contextWindow = getContextWindowForModel(model)
const maxOutputTokens = Math.min(getMaxOutputTokensForModel(model), 20_000)
return contextWindow - maxOutputTokens
}
// 触发阈值 = 有效窗口 - 缓冲区
export function getAutoCompactThreshold(model: string): number {
return getEffectiveContextWindowSize(model) - AUTOCOMPACT_BUFFER_TOKENS
}
以 Sonnet 4.6 (200K context) 为例:
上下文窗口: 200,000 tokens
- 摘要预留: - 20,000 tokens
= 有效窗口: 180,000 tokens
- 缓冲区: - 13,000 tokens
= 触发阈值: 167,000 tokens (83.5%)
警告阈值: 167,000 - 20,000 = 147,000 tokens (73.5%)
错误阈值: 167,000 - 20,000 = 147,000 tokens (73.5%)
export async function shouldAutoCompact(
messages: Message[],
model: string,
querySource?: QuerySource,
snipTokensFreed = 0,
): Promise<boolean> {
// 守卫检查(按顺序短路)
if (querySource === 'session_memory' || querySource === 'compact')
return false // 防递归:压缩/Session Memory 的 fork agent 不触发压缩
if (isDisableAutoCompact() || isDisableCompact())
return false // 环境变量 kill switch
if (feature('REACTIVE_COMPACT') && tengu_cobalt_raccoon)
return false // Reactive Compact 模式:等 API 返回 413 再处理
if (feature('CONTEXT_COLLAPSE') && isContextCollapseEnabled())
return false // Context Collapse 自己管理上下文
// 实际计算
const tokenCount = tokenCountWithEstimation(messages) - snipTokensFreed
const { isAboveAutoCompactThreshold } = calculateTokenWarningState(tokenCount, model)
return isAboveAutoCompactThreshold
}
export async function autoCompactIfNeeded(
messages, toolUseContext, cacheSafeParams, querySource, tracking, snipTokensFreed
) {
// 熔断器:连续失败 3 次后停止尝试
if (tracking?.consecutiveFailures >= MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES) {
return { wasCompacted: false }
}
if (!(await shouldAutoCompact(messages, model, querySource, snipTokensFreed))) {
return { wasCompacted: false }
}
// ① 优先尝试 Session Memory Compact(零 API 调用)
const sessionMemoryResult = await trySessionMemoryCompaction(
messages, toolUseContext, cacheSafeParams, querySource
)
if (sessionMemoryResult) {
setLastSummarizedMessageId(undefined)
runPostCompactCleanup(querySource)
return { wasCompacted: true, compactionResult: sessionMemoryResult }
}
// ② 降级到 Full Compact(调用 API 生成摘要)
try {
const compactionResult = await compactConversation(
messages, toolUseContext, cacheSafeParams,
true, // suppressFollowUpQuestions
undefined, // customInstructions
true, // isAutoCompact
recompactionInfo,
)
runPostCompactCleanup(querySource)
return { wasCompacted: true, compactionResult, consecutiveFailures: 0 }
} catch (error) {
// 熔断器计数
return {
wasCompacted: false,
consecutiveFailures: (tracking?.consecutiveFailures ?? 0) + 1,
}
}
}
文件:src/services/compact/compact.ts
export async function compactConversation(
messages, context, cacheSafeParams, suppressFollowUpQuestions,
customInstructions, isAutoCompact, recompactionInfo
): Promise<CompactionResult> {
// ① 预处理
const preCompactTokenCount = tokenCountWithEstimation(messages)
const hookResult = await executePreCompactHooks(...)
// ② 预处理消息:移除图片、移除已重新注入的附件
let messagesToSummarize = stripImagesFromMessages(messages)
messagesToSummarize = stripReinjectedAttachments(messagesToSummarize)
// ③ 调用 API 生成摘要(带 PTL 重试)
for (let attempt = 0; attempt < MAX_PTL_RETRIES; attempt++) {
summaryResponse = await streamCompactSummary({
messages: messagesToSummarize,
summaryRequest,
appState,
context,
preCompactTokenCount,
cacheSafeParams,
})
summary = getAssistantMessageText(summaryResponse)
// 摘要请求本身也可能触发 prompt-too-long
if (!summary?.startsWith(PROMPT_TOO_LONG_ERROR_MESSAGE)) break
// 丢弃最早的 API 轮次重试
const truncated = truncateHeadForPTLRetry(messagesToSummarize, summaryResponse)
if (!truncated) throw new Error(ERROR_MESSAGE_PROMPT_TOO_LONG)
messagesToSummarize = truncated
}
// ④ 清理状态
context.readFileState.clear()
context.loadedNestedMemoryPaths?.clear()
// ⑤ 创建压缩后附件(并行)
const [fileAttachments, asyncAgentAttachments] = await Promise.all([
createPostCompactFileAttachments(context.readFileState, context, 5, []),
createAsyncAgentAttachmentsIfNeeded(context),
])
// ⑥ 保存跨压缩状态
const boundaryMarker = createCompactBoundaryMessage(
isAutoCompact ? 'auto' : 'manual',
preCompactTokenCount,
messages.at(-1)?.uuid,
)
// 保存已发现的延迟工具
const preCompactDiscovered = extractDiscoveredToolNames(messages)
if (preCompactDiscovered.size > 0) {
boundaryMarker.compactMetadata.preCompactDiscoveredTools =
[...preCompactDiscovered].sort()
}
// ⑦ 创建摘要消息
const summaryMessages = [
createUserMessage({
content: getCompactUserSummaryMessage(summary, ...),
isCompactSummary: true,
isVisibleInTranscriptOnly: true,
}),
]
// ⑧ 恢复附件
const planAttachment = createPlanAttachmentIfNeeded(context.agentId)
const skillAttachment = createSkillAttachmentIfNeeded(context.agentId)
const planModeAttachment = await createPlanModeAttachmentIfNeeded(context)
// ⑨ 执行 session_start hooks(恢复 CLAUDE.md 等)
const hookMessages = await executeSessionStartHooks(...)
// ⑩ 执行 post_compact hooks
await executePostCompactHooks(...)
return {
boundaryMarker,
summaryMessages,
attachments: [...fileAttachments, ...skillAttachment, ...planAttachment, ...],
hookResults: hookMessages,
preCompactTokenCount,
postCompactTokenCount: compactionCallTotalTokens,
truePostCompactTokenCount,
}
}
文件:src/services/compact/prompt.ts
摘要请求包含严格的 9 节结构,每节有 <analysis>(思考草稿)和 <summary>(最终输出):
1. Primary Request and Intent — 用户的根本目标
2. Key Technical Concepts — 讨论过的关键技术概念
3. Files and Code Sections — 涉及的文件和代码片段(含代码)
4. Errors and Fixes — 遇到的错误和修复方案
5. Problem Solving — 问题解决过程
6. All User Messages — 所有用户消息(保留反馈的关键)
7. Pending Tasks — 未完成的任务
8. Current Work — 当前工作状态(最详细的一节)
9. Optional Next Step — 建议的下一步(含直接引用)
关键约束:
const NO_TOOLS_PREAMBLE =
`CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.
Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool.
You already have all the context you need in this conversation.`
当摘要请求本身触发 prompt-too-long:
export function truncateHeadForPTLRetry(
messages: Message[],
ptlResponse: AssistantMessage,
): Message[] | null {
// 按 API 轮次分组
const groups = groupMessagesByApiRound(messages)
// 计算需要释放的 token 数
const tokenGap = extractTokenGapFromPTL(ptlResponse)
// 从最旧的组开始丢弃,直到释放足够空间
let freedTokens = 0
let dropCount = 0
for (const group of groups) {
freedTokens += estimateGroupTokens(group)
dropCount++
if (freedTokens >= tokenGap) break
}
// 如果丢弃了第一组(含系统消息),插入占位标记
if (dropCount > 0 && droppedGroup0) {
remaining.unshift(createUserMessage({
content: PTL_RETRY_MARKER,
// '[earlier conversation truncated for compaction retry]'
}))
}
return remaining
}
文件:src/utils/messages.ts
export function createCompactBoundaryMessage(
trigger: 'manual' | 'auto',
preTokens: number,
lastPreCompactMessageUuid?: UUID,
userContext?: string,
messagesSummarized?: number,
): SystemCompactBoundaryMessage {
return {
type: 'system',
subtype: 'compact_boundary',
content: 'Conversation compacted',
uuid: randomUUID(),
timestamp: new Date().toISOString(),
compactMetadata: {
trigger,
preTokens,
userContext,
messagesSummarized,
preCompactDiscoveredTools: undefined,
preservedSegment: undefined,
},
logicalParentUuid: lastPreCompactMessageUuid,
}
}
| 保护项 | 保存位置 | 恢复方式 |
|---|---|---|
| 已发现的延迟工具 | compactMetadata.preCompactDiscoveredTools | extractDiscoveredToolNames() 扫描 boundary |
| 保留消息段 | compactMetadata.preservedSegment | dedup-skip 重链 |
| 消息链续接 | logicalParentUuid | UI 线程化展示 |
| 最近读过的文件 | Post-compact file attachments | 重新注入(5 文件 / 50K token) |
| 已调用的技能 | Post-compact skill attachment | 重新注入(25K token) |
| Plan 文件 | Post-compact plan attachment | 完整重新注入 |
| 异步 agent 状态 | Post-compact agent attachments | 完整重新注入 |
| CLAUDE.md 内容 | session_start hooks | Hook 重新执行 |
| 工具可用性 delta | deferred_tools_delta attachment | 重新通知 |
| MCP 指令 | mcp_instructions_delta attachment | 重新通知 |
export function buildPostCompactMessages(result: CompactionResult): Message[] {
return [
result.boundaryMarker, // Compact Boundary(含 metadata)
...result.summaryMessages, // 摘要内容
...(result.messagesToKeep ?? []), // 保留的最近消息(SM Compact 路径)
...result.attachments, // 文件/技能/计划附件
...result.hookResults, // Hook 结果(CLAUDE.md 等)
]
}
export async function createPostCompactFileAttachments(
readFileState, toolUseContext, maxFiles, preservedMessages
) {
// 从 readFileState 中找出最近读过的文件
const candidates = Object.entries(readFileState)
.filter(([path]) =>
!isPlanFile(path) && // 排除 plan 文件(单独恢复)
!isMemoryFile(path) && // 排除 memory 文件(CLAUDE.md 单独恢复)
!isInPreservedMessages(path) // 排除保留消息中已有的文件
)
.sort((a, b) => b.timestamp - a.timestamp) // 最近读的优先
.slice(0, maxFiles) // 最多 5 个
// 逐个读取,受 token 预算控制
let totalTokens = 0
for (const [path, state] of candidates) {
const content = await readFile(path)
const tokens = estimateTokens(content)
if (tokens > POST_COMPACT_MAX_TOKENS_PER_FILE) continue // 单文件 > 5K → 跳过
if (totalTokens + tokens > POST_COMPACT_TOKEN_BUDGET) break // 总计 > 50K → 停止
attachments.push(createFileAttachment(path, content))
totalTokens += tokens
}
return attachments
}
Session Memory 是一个后台 hook 持续维护的「会议纪要」。当需要压缩时,直接用它替代 API 摘要——省去一次完整的 API 调用。
文件:src/services/compact/sessionMemoryCompact.ts
export function calculateMessagesToKeepIndex(
messages: Message[],
lastSummarizedIndex: number,
config: SessionMemoryCompactConfig,
): number {
// 从 lastSummarizedIndex + 1 开始,向后扩展
let startIndex = lastSummarizedIndex + 1
let totalTokens = 0
let textBlockMessageCount = 0
// 向前扩展,直到满足最小要求
while (startIndex > 0) {
startIndex--
totalTokens += estimateMessageTokens(messages[startIndex])
if (hasTextBlock(messages[startIndex])) textBlockMessageCount++
// 同时满足:最少 10K token + 最少 5 条文本消息
if (totalTokens >= config.minTokens &&
textBlockMessageCount >= config.minTextBlockMessages)
break
// 硬上限:40K token
if (totalTokens >= config.maxTokens) break
}
// 不越过上一个 compact boundary
const lastBoundaryIndex = findLastCompactBoundaryIndex(messages)
if (lastBoundaryIndex >= 0) startIndex = Math.max(startIndex, lastBoundaryIndex + 1)
return startIndex
}
export function adjustIndexToPreserveAPIInvariants(
messages: Message[],
startIndex: number,
): number {
// ① 防止 tool_result 失去对应的 tool_use
while (startIndex > 0) {
const msg = messages[startIndex]
if (msg.type === 'user' &&
hasOrphanedToolResult(msg, messages.slice(0, startIndex))) {
startIndex--
} else break
}
// ② 防止 thinking block 与 kept assistant 消息共享 message.id
return startIndex
}
type SessionMemoryCompactConfig = {
minTokens: 10_000, // 最少保留的 token 数
minTextBlockMessages: 5, // 最少保留的文本消息数
maxTokens: 40_000, // 最多保留的 token 数(硬上限)
}
// GrowthBook key: tengu_sm_compact_config
文件:src/services/compact/apiMicrocompact.ts
// 策略:清除旧工具结果
{
type: 'clear_tool_uses_20250919',
trigger: { token_count: 180_000 }, // 输入达 180K 时触发
keep: { tail_tool_use_count: N }, // 保留最近 N 个
clear_tool_inputs: boolean, // 是否也清除工具输入
}
// 策略:清除 thinking blocks
{
type: 'clear_thinking_20251015',
keep: 'all' | { type: 'thinking_turns', value: N },
}
工具清除分类:
// 结果可清除的工具(只清 tool_result 内容)
TOOLS_CLEARABLE_RESULTS = [Read, Bash, Grep, Glob, WebSearch, WebFetch, Edit, Write]
// 使用记录可清除的工具(清 tool_use + tool_result)
TOOLS_CLEARABLE_USES = [Edit, Write]
默认阈值:
| 参数 | 默认值 | 环境变量覆盖 |
|---|---|---|
| 触发阈值 | 180,000 tokens | API_MAX_INPUT_TOKENS |
| 目标 token | 40,000 tokens | API_TARGET_INPUT_TOKENS |
在 query.ts 中,当 API 返回 413 或 media 错误:
if ((isWithheld413 || isWithheldMedia) && reactiveCompact) {
const compacted = await reactiveCompact.tryReactiveCompact({...})
if (compacted) {
const postCompactMessages = buildPostCompactMessages(compacted)
yield* postCompactMessages
messagesForQuery = postCompactMessages
autoCompactTracking = undefined // 重置追踪
}
}
启用 Reactive Compact 后,Auto-compact 被抑制:
// autoCompact.ts
if (feature('REACTIVE_COMPACT')) {
if (getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_raccoon', false)) {
return false // 不主动压缩
}
}
设计意图:Reactive Compact 从消息尾部剥离,保留 prompt cache 前缀。而 Auto-compact 替换全部消息为摘要,会破坏 cache。在 cache 命中率高的场景下,Reactive 更划算。
文件:src/services/compact/postCompactCleanup.ts
export function runPostCompactCleanup(querySource?: QuerySource): void {
const isMainThread = querySource === undefined
|| querySource.startsWith('repl_main_thread')
|| querySource === 'sdk'
// 始终重置
resetMicrocompactState() // Cached MC 追踪状态
clearSystemPromptSections() // 系统提示缓存
clearClassifierApprovals() // Auto-mode 分类器审批缓存
clearSpeculativeChecks() // Bash 权限推测缓存
clearBetaTracingState() // Beta 遥测状态
clearSessionMessagesCache() // 会话消息缓存
// 仅主线程重置(防止 fork agent 破坏共享状态)
if (isMainThread) {
resetContextCollapse() // Context Collapse 状态
getUserContext.cache.clear() // 记忆化的上下文缓存
resetGetMemoryFilesCache('compact') // Memory 文件缓存
}
// 条件重置
if (feature('COMMIT_ATTRIBUTION')) {
sweepFileContentCache() // 文件归因缓存
}
// 有意不重置的:
// - invoked skill content(跨多次压缩存活,用于 skill attachment)
// - sent skill names(重新注入成本低)
}
主线程保护:fork agent(如 Session Memory、extractMemories)执行压缩时,不重置主线程的模块级状态。否则会导致 getUserContext 缓存被意外清空。
export interface CompactionResult {
boundaryMarker: SystemMessage // Compact Boundary 消息
summaryMessages: UserMessage[] // 摘要内容
attachments: AttachmentMessage[] // 恢复附件(文件/技能/计划)
hookResults: HookResultMessage[] // Hook 执行结果
messagesToKeep?: Message[] // 保留的原始消息(SM Compact 路径)
userDisplayMessage?: string // 给用户看的提示
// Token 计量
preCompactTokenCount?: number // 压缩前 token 数
postCompactTokenCount?: number // 摘要 API 调用消耗
truePostCompactTokenCount?: number // 压缩后真实上下文大小
compactionUsage?: TokenUsage // 详细 token 用量
}
export type AutoCompactTrackingState = {
compacted: boolean // 本轮是否已压缩
turnCounter: number // 距上次压缩的轮次数
turnId: string // 当前轮次 ID
consecutiveFailures?: number // 连续失败次数(熔断器计数)
}
| 常量 | 值 | 用途 |
|---|---|---|
AUTOCOMPACT_BUFFER_TOKENS | 13,000 | 触发阈值与有效窗口的间距 |
WARNING_THRESHOLD_BUFFER_TOKENS | 20,000 | UI 警告与触发阈值的间距 |
ERROR_THRESHOLD_BUFFER_TOKENS | 20,000 | 错误态与触发阈值的间距 |
MANUAL_COMPACT_BUFFER_TOKENS | 3,000 | /compact 手动命令的阻塞限制 |
MAX_OUTPUT_TOKENS_FOR_SUMMARY | 20,000 | 预留给摘要输出的 token |
POST_COMPACT_TOKEN_BUDGET | 50,000 | 文件附件总预算 |
POST_COMPACT_MAX_TOKENS_PER_FILE | 5,000 | 单文件预算 |
POST_COMPACT_SKILLS_TOKEN_BUDGET | 25,000 | 技能附件总预算 |
POST_COMPACT_MAX_TOKENS_PER_SKILL | 5,000 | 单技能预算 |
POST_COMPACT_MAX_FILES_TO_RESTORE | 5 | 最多恢复文件数 |
MAX_PTL_RETRIES | 3 | PTL 重试次数 |
MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES | 3 | 熔断器阈值 |
MAX_COMPACT_STREAMING_RETRIES | 2 | 流式请求重试次数 |
| Flag | 控制 |
|---|---|
CACHED_MICROCOMPACT | 启用 cache_edits 微压缩 |
HISTORY_SNIP | 启用消息丢弃 |
CONTEXT_COLLAPSE | 启用细粒度上下文管理 |
REACTIVE_COMPACT | 启用被动 413 压缩 |
PROMPT_CACHE_BREAK_DETECTION | Cache 断裂检测 |
COMMIT_ATTRIBUTION | 提交归因缓存 |
| Flag | 控制 |
|---|---|
tengu_cobalt_raccoon | 启用 Reactive Compact 抑制 Auto-compact |
tengu_compact_cache_prefix | 启用 cache 前缀共享压缩 |
tengu_compact_streaming_retry | 启用流式压缩重试 |
tengu_sm_compact_config | Session Memory Compact 配置 |
tengu_slate_heron | Time-based MC 配置 |
| 变量 | 作用 |
|---|---|
DISABLE_COMPACT | 完全禁用所有压缩 |
DISABLE_AUTO_COMPACT | 仅禁用自动压缩 |
CLAUDE_CODE_AUTO_COMPACT_WINDOW | 覆盖上下文窗口大小 |
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE | 覆盖触发阈值百分比 |
CLAUDE_CODE_BLOCKING_LIMIT_OVERRIDE | 覆盖 /compact 阻塞限制 |
API_MAX_INPUT_TOKENS | 覆盖 API MC 触发阈值 |
API_TARGET_INPUT_TOKENS | 覆盖 API MC 目标 token |
| 模式 | Claude Code 做法 | 可借鉴场景 |
|---|---|---|
| 分层压缩流水线 | 5 层从轻到重逐级升级 | 任何需要上下文管理的长对话 Agent |
| Cache 感知压缩 | Cached MC 不改本地消息,只发 cache_edits | 高 cache 命中率场景 |
| 冷热判断 | Time-based MC 检测 gap > 60min 判断 cache 已冷 | 有 cache TTL 的 API 调用 |
| 预计算摘要 | Session Memory 替代 API 摘要调用 | 省 API 调用成本 |
| 熔断器 | 连续 3 次失败后停止尝试 | 任何可能反复失败的后台操作 |
| PTL 分组重试 | 按 API 轮次分组丢弃,保留结构完整性 | 处理 prompt-too-long 的通用策略 |
| Boundary 状态快照 | 在边界消息中保存跨压缩状态 | 任何有状态的消息流处理 |
| Post-compact 恢复 | 带 token 预算的文件/技能重新注入 | 压缩后的上下文重建 |
| 主线程保护 | fork agent 不重置主线程模块级状态 | 多线程/多 agent 共享状态场景 |
| 依赖注入 | microcompact/autocompact 通过 QueryDeps 注入 | 测试隔离、策略替换 |
文件来源:Claude Code npm 包 source map 泄露快照 (2026-03-31)