Skip to content
源码分析手册

会话收口生命周期:Stop 钩子与后台异步治理

当 Claude 完成一轮推理并停止输出时,系统并不会直接进入闲置状态,而是进入了一个名为 Stop 的生命周期收口阶段。这是运行时进行状态结算、异步背景任务调度以及下一步预测的关键时机。

从定义开始

Stop 钩子是 推理轮次结束后的治理逻辑与状态锚定点

它不是一个简单的“生成结束回调”,而是一个复杂的异步调度引擎。每当模型停止生成(无论是正常完成、因 Token 耗尽被迫停止、还是被用户通过 Ctrl+C 中断),Stop 阶段都会被激活。它的核心价值是在用户拿回终端控制权之前,完成以下三件事:对本轮输出进行“治理”(如根据 lint 错误阻断流程)、对当前环境进行“镜像保存”(为侧边问题或重绕做准备)、以及在后台悄悄开启“推理余温”任务(如记忆提取和下步预测)。

实现细节

收口协议的核心逻辑封装在 claude-code-opensource/src/query/stopHooks.tshandleStopHooks 函数中。其运行时行为可归纳为以下四个并行执行的分支:

  1. 快照与缓存对齐(Snapshotting): 为了支持 /btw 和侧边问题(Side Questions),系统会在 Stop 阶段通过 saveCacheSafeParams 保存当前会话的完整上下文快照。这样做是为了确保即便后续主会话继续推进,临时开启的侧边会话也能复用此时此刻的 Prompt Cache,从而极大地节省首屏 Token 成本。

  2. 治理钩子执行(Governance Hooks): 调用 executeStopHooks 并行运行用户或系统定义的治理脚本。这些钩子具有极高的干预权限:

    • 流程阻断:如果钩子发现环境状态异常(如核心单元测试失败),它可以向主循环抛出 blockingError
    • 控制流拦截:通过 preventContinuation 信号,钩子可以强行停止 Claude 的自动化执行流水线,要求用户必须介入确认。
  3. 背景异步清理(Background Bookkeeping): 在非 --bare 模式下,系统会以“Fire-and-forget”模式并行启动多项后台工作:

    • Prompt Suggestion:基于主会话的 Cache 前缀,在后台启动一个轻量级的 Fork Agent 预测用户可能的下一句输入。
    • Memory Extraction:从当前对话中提取重要事实并更新本地持久化记忆层。
    • Auto Dream:在后台进行深度状态整理或逻辑自洽推演。
  4. Teammate 协议同步: 如果当前是以“队友(Teammate)”身份运行,Stop 阶段还会额外触发任务状态同步钩子,用于更新共享的 mailbox 或同步全局任务账本。

限制与陷阱

  • 并行无关性Stop 阶段的所有钩子和背景任务都是高度并行的。这意味着它们之间不应存在先后依赖关系,否则会导致不可预测的竞态冲突。
  • 非阻塞背景任务:内存提取和建议预测虽然会消耗一定的资源,但它们的设计原则是“不影响用户输入”,即输入框会出现,后台任务异步执行。
  • 中断也是一种 Stop:即便用户按了 Ctrl+C 强行停止输出,handleStopHooks 仍会被触发。此时系统会优先执行“紧急清理”逻辑,而非深度背景分析。
  • Bare 模式的隔离:在 --bare 模式下,绝大部分耗时的背景任务(如 Memory 提取和 Suggestion 生成)会被硬性跳过。

接下来看什么

源码锚点

  • claude-code-opensource/src/query/stopHooks.tshandleStopHooksexecuteStopHooks 的主入口逻辑。
📄 src/query/stopHooks.ts — `handleStopHooks` 与 `executeStopHooks` 的主入口逻辑。L65-69 of 474
typescript
export async function* handleStopHooks(
  messagesForQuery: Message[],
  assistantMessages: AssistantMessage[],
  systemPrompt: SystemPrompt,
  userContext: { [k: string]: string },
  • claude-code-opensource/src/services/extractMemories/extractMemories.ts — 异步记忆提取任务的实现细节。
📄 src/services/extractMemories/extractMemories.ts — 异步记忆提取任务的实现细节。L65-80 of 616
typescript
const teamMemPaths = feature('TEAMMEM')
  ? (require('../../memdir/teamMemPaths.js') as typeof import('../../memdir/teamMemPaths.js'))
  : null
/* eslint-enable @typescript-eslint/no-require-imports */

// ============================================================================
// Helpers
// ============================================================================

/**
 * Returns true if a message is visible to the model (sent in API calls).
 * Excludes progress, system, and attachment messages.
 */
function isModelVisibleMessage(message: Message): boolean {
  return message.type === 'user' || message.type === 'assistant'
}
  • claude-code-opensource/src/utils/forkedAgent.ts — 用于缓存对齐的快照保存机制。
📄 src/utils/forkedAgent.ts — 用于缓存对齐的快照保存机制。L41-44 of 690
typescript
  type ContentReplacementState,
  cloneContentReplacementState,
} from './toolResultStorage.js'
import { createAgentId } from './uuid.js'

基于 Claude Code v2.1.88 开源快照的深度分析