Skip to content
源码分析手册

会话启动生命周期:首轮推理前的环境装配与指令注入

在 Claude Code 的运行时模型中,会话的启动并非简单的进程开启,而是一个多阶段的环境装配过程。SessionStart 钩子(Hook)是这一过程中的核心扩展点,它定义了模型在接收首轮用户输入前,本地环境如何完成上下文的预热与指令的注入。

一句话讲清楚

SessionStart 本质上是 首轮推理前的环境拦截协议

它不是一个简单的“启动脚本”,而是一个运行时门控。当会话因初次启动、恢复(Resume)、清空(Clear)或上下文压缩(Compact)而重新初始化时,系统会触发此钩子。它的核心使命是:在模型看到用户的第一句话之前,允许本地逻辑向会话中注入额外的上下文、指令、甚至模拟用户的初始提问。它确保了 Claude 不是带着“白板”大脑进入对话,而是已经预装了项目规则和环境快照。

实现机制

整个启动生命周期的装配逻辑主要由 claude-code-opensource/src/utils/sessionStart.ts 中的 processSessionStartHooks 函数驱动。其执行流程遵循严格的先后顺序:

  1. 模式裁决(The Bare Gate): 系统首先检查是否处于 --bare(极简)模式。如果是,为了追求极致的启动响应,系统会物理切断所有钩子的加载与执行。这意味着在 Bare 模式下,没有任何预置逻辑能干扰模型的纯净状态。

  2. 插件钩子加载(Plugin Loading): 在非受限模式下,系统会异步加载 ~/.claude/plugins/ 目录下的第三方钩子。源码在这里做了健壮性处理:如果插件加载因语法错误或权限问题失败,系统仅会记录诊断日志并降级运行,绝不阻塞主会话的开启。

  3. 核心执行流(Execution & Injection): 通过 executeSessionStartHooks 并行触发所有已注册的钩子。在这个阶段,钩子可以对未来的会话产生四种维度的影响:

    • 消息注入:直接向消息序列中插入 HookResultMessage
    • 上下文补丁:通过 additionalContexts 提供非对话形式的背景(如环境探测结果)。
    • 首轮提问模拟:设置 pendingInitialUserMessage。这是一个非常巧妙的 side channel 设计,允许钩子在 REPL 循环正式开始前,代表用户发出“第一击”。
    • 文件监听:返回 watchPaths,告知运行时需要重点监控哪些文件。
  4. 状态回填与副作用生效: 钩子运行的结果会被转化为 hook_additional_context 类型的附件,并同步给 fileChangedWatcher。随后,控制权才会正式交给 main.tsx 中的交互主循环。

使用时的关键约束

  • 性能硬约束:源码中有一行非常严肃的注释警告:do not add ANY "warmup" logic。这意味着 SessionStart 严禁添加任何高耗时的操作(如深度的网络请求或大规模全盘扫描),因为它直接阻断了用户看到输入框的首屏时间。
  • 非交互性:在 SessionStart 钩子运行期间,终端处于锁定状态,钩子无法通过终端与用户进行任何实时交互。
  • 多场景触发:这一钩子不只在 claude 命令敲下时。当你执行 /compact 进行上下文重组,或者 /clear 重置会话时,SessionStart 都会被重新触发,以确保环境补丁的连续性。
  • 安全沙箱:在受管钩子(Managed Hooks)模式下,所有来自第三方插件目录的 SessionStart 逻辑都会被切断,仅允许内建逻辑生效。

相关主题

源码锚点

  • claude-code-opensource/src/utils/sessionStart.tsprocessSessionStartHooks 主逻辑与装配顺序。
📄 src/utils/sessionStart.ts — `processSessionStartHooks` 主逻辑与装配顺序。L35-42 of 233
typescript
export async function processSessionStartHooks(
  source: 'startup' | 'resume' | 'clear' | 'compact',
  {
    sessionId,
    agentType,
    model,
    forceSyncExecution,
  }: SessionStartHooksOptions = {},
  • claude-code-opensource/src/utils/hooks.ts — 钩子执行引擎的底层并发实现。
📄 src/utils/hooks.ts — 钩子执行引擎的底层并发实现。L56-58 of 5023
typescript
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
} from 'src/services/analytics/index.js'
import { logOTelEvent } from './telemetry/events.js'
  • claude-code-opensource/src/main.tsx — 会话启动时触发钩子的顶层调用点。
📄 src/main.tsx — 会话启动时触发钩子的顶层调用点。L70-82 of 4684
tsx
const getTeammateUtils = () => require('./utils/teammate.js') as typeof import('./utils/teammate.js');
const getTeammatePromptAddendum = () => require('./utils/swarm/teammatePromptAddendum.js') as typeof import('./utils/swarm/teammatePromptAddendum.js');
const getTeammateModeSnapshot = () => require('./utils/swarm/backends/teammateModeSnapshot.js') as typeof import('./utils/swarm/backends/teammateModeSnapshot.js');
/* eslint-enable @typescript-eslint/no-require-imports */
// Dead code elimination: conditional import for COORDINATOR_MODE
/* eslint-disable @typescript-eslint/no-require-imports */
const coordinatorModeModule = feature('COORDINATOR_MODE') ? require('./coordinator/coordinatorMode.js') as typeof import('./coordinator/coordinatorMode.js') : null;
/* eslint-enable @typescript-eslint/no-require-imports */
// Dead code elimination: conditional import for KAIROS (assistant mode)
/* eslint-disable @typescript-eslint/no-require-imports */
const assistantModule = feature('KAIROS') ? require('./assistant/index.js') as typeof import('./assistant/index.js') : null;
const kairosGate = feature('KAIROS') ? require('./assistant/gate.js') as typeof import('./assistant/gate.js') : null;
import { relative, resolve } from 'path';

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