W
AI-Wiki
CONCEPT

SessionStart

定义

SessionStartclaude-mem 的 5 阶段 Hook 生命周期中的第 1 阶段(Stage 1 is SessionStart)。

它对应的脚本钩子是 context-hook.js,核心用途不是保存新数据,而是把此前会话的上下文静默注入到当前会话的初始上下文中。

在生命周期总表里,它的触发条件写明为:用户打开 Claude Code;其目的写明为:Inject prior context silently,即“静默注入先前上下文”。

在本文档中的语境

本文档讨论的是 claude-mem 的 Hook 生命周期与系统架构。该系统采用两进程架构:扩展侧不阻塞,工作进程异步处理;会话状态可跨 IDE 重启持续存在。

在这个语境里,SessionStart 负责在一次新打开或恢复的 Claude Code 会话最早阶段,把历史记忆取回并放入模型可见上下文,从而让后续的 UserPromptSubmitPostToolUsesummary-hook.jssave-hook.jscleanup-hook.js 等阶段建立在已有上下文之上。

换言之,SessionStart 在本文档里不是泛指“会话开始”这一抽象概念,而是一个有明确事件名、明确脚本、明确输入输出格式、明确配置项的 Hook 阶段。

触发时机与匹配条件

文档明确说明,SessionStart 的时机是:当用户打开 Claude Code,或恢复既有会话时触发。

hooks.json 中,这一阶段的 matcherstartup|clear|compact。这说明它不只在最直观的 startup 场景出现,也会在 clearcompact 这些匹配到的场景中触发同一组 SessionStart hooks。

因此,它的触发边界应理解为:不仅仅是“首次启动应用”,而是凡是命中该 matcher、并被系统归入 SessionStart 事件的情况,都可能执行这一阶段。

配置与执行顺序

在 Hook 配置中,SessionStart 下挂了两个命令型 hooks,而且有严格顺序:

  1. worker-service.cjs start
  2. context-hook.js

文档明确写明该顺序是按序触发的:先启动 worker service,再执行上下文注入脚本。

这意味着 context-hook.js 并不是孤立运行;它依赖前面的 worker 服务已可用,才能去请求上下文注入接口。

对应配置中,这两个命令都设置了 timeout: 60

核心机制

1. 先确保工作进程可用

context-hook.js 的处理流程第一步不是直接返回上下文,而是等待 worker 可用。文档给出的行为是进行健康检查,最长等待 10 秒。

这一步体现了 SessionStart 的关键依赖:上下文不是本地脚本直接拼出来的,而是通过工作进程提供的 HTTP 能力取回。

2. 调用上下文注入接口

worker 可用后,context-hook.js 会调用:

GET http://127.0.0.1:<worker-port>/api/context/inject?project={project}

也就是说,SessionStart 注入的是按 project 维度组织的先前上下文,而不是无条件把所有历史内容都塞回当前会话。

3. 通过 hookSpecificOutput.additionalContext 返回

context-hook.js 最关键的输出方式,是把格式化后的上下文作为 hookSpecificOutput.additionalContext 返回。

文档给出的标准输出结构为:

{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "<<formatted context markdown>>"
}
}

这也是本条目的核心定义点:SessionStart 是通过 hookSpecificOutput 返回附加上下文的钩子事件名,其中承载具体内容的字段是 additionalContext

输入与输出细节

文档给出的 context-hook.js 输入示例来自标准输入,结构如下:

{
"session_id": "claude-session-123",
"cwd": "/path/to/project",
"source": "startup"
}

从这个输入可以看出,SessionStart 至少感知三个信息:当前会话标识 session_id、当前工作目录 cwd、以及触发来源 source

其中 source 示例是 startup,这与 matcher 中的 startup|clear|compact 对应,说明不同来源都可能汇入同一个 SessionStart 处理框架。

输出则不是继续执行类的简单布尔结果,而是专门返回给宿主用于上下文注入的 hookSpecificOutput 对象。与 UserPromptSubmitPostToolUse 常见的 { "continue": true, "suppressOutput": true } 形式不同,SessionStart 的价值在于产出额外上下文,而非仅仅静默放行。

与用户可见性的关系

文档特别说明:自 Claude Code 2.1.0(ultrathink update)起,SessionStart hooks 不再显示用户可见消息。

这意味着当前实现中的 SessionStart 是“静默注入”而不是“向用户打印提示”。上下文进入 Claude 的方式,是通过 hookSpecificOutput.additionalContext 注入,而不是通过可见聊天消息、提示条或终端文本展示给用户。

这也是 Inject prior context silently 这一定义的具体实现含义,而不是泛泛描述。

相关实现位置

文档中把运行的命令脚本写为 context-hook.js,同时又注明实现位置为 src/hooks/context-hook.ts

这说明概念上对应的 Hook 名是 context-hook.js,而源码实现位于 TypeScript 文件中;二者描述的是同一阶段的不同层次,不应混淆为两个独立 Hook。

细节与边界

不是 Setup 阶段

SessionStart 虽然发生得很早,但它不是 Setup。文档把 Setup 单独列为另一个配置段,其作用是运行 version-check.js,用于检查安装状态并在 .install-version 标记过期时提示修复。

运行时安装与修复由外部安装/修复流程处理,而不是由 SessionStart 自身负责。

不是保存 Prompt 的阶段

SessionStart 不负责创建或保存用户 prompt;那是 UserPromptSubmit 的职责。后者在用户真正提交 prompt 后,才会创建或获取 session、递增 prompt counter、清洗隐私标签、保存 prompt,并初始化 worker 侧会话。

因此,SessionStartUserPromptSubmit 的边界很清楚:前者注入旧上下文,后者处理新输入。

不是工具观察记录阶段

SessionStart 也不负责采集 Read、Bash、Write 等工具调用后的观察数据;那是 PostToolUsesave-hook.js 的职责。后者会把工具输入输出通过 HTTP 发给 worker,再由异步队列压缩并入库。

依赖 worker,但自身目标是上下文返回

虽然 SessionStart 的第一步会先启动 worker-service.cjs startcontext-hook.js 也会等待 worker 健康,但这个阶段最终交付物不是“worker 已启动”本身,而是经 additionalContext 返回的格式化上下文。

如果只启动 worker 而没有后续上下文注入,就不构成本文定义下完整的 SessionStart 阶段行为。

与整体生命周期的关系

在 5 阶段生命周期中,SessionStart 排在最前:

  1. SessionStartcontext-hook.js,用户打开 Claude Code,静默注入先前上下文。