cleanup-hook.js
定义与身份
cleanup-hook.js 是 claude-mem Hook 生命周期中对应 SessionEnd 阶段的脚本。
它在 hooks 配置中由 SessionEnd 生命周期事件调用,命令路径为 ${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js。
源码映射关系为:src/hooks/cleanup-hook.ts → plugin/scripts/cleanup-hook.js。
从角色上看,它不是负责摘要生成,也不是负责保存 observation,而是在会话退出阶段收尾:把当前 session 标记为完成,并为后续异步处理留下自然结束的机会。
hooks 配置位置
原始配置形式如下:
{
"hooks": {
"SessionEnd": [{
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js"
}]
}]
}
}
这说明 cleanup-hook.js 是在 SessionEnd 阶段以 command hook 方式执行的,而不是在 SessionStart、UserPromptSubmit、PostToolUse 或 Stop 阶段执行。
角色职责
cleanup-hook.js 的职责有 3 项:
- 在数据库中把 session 标记为已完成。
- 允许 worker 把尚未完成的处理继续做完。
- 执行优雅清理(graceful cleanup),而不是粗暴中断。
这意味着它的目标不是“尽快杀掉后台工作”,而是“在会话结束后把状态收拢到正确终态,同时避免丢数据和竞争条件”。
触发时机与边界
触发时机是:Claude Code 会话结束时。
但有一个明确边界:不会在 /clear 时执行这类清理语义。原文明确说明 SessionEnd cleanup 是“Claude Code session ends (not on /clear)”。
因此,/clear 不应被当作真正的会话完成信号;cleanup-hook.js 的完成标记逻辑面向的是会话结束,而不是简单的上下文清空操作。
关键设计决策
与该脚本直接相关的关键决策包括:
- ✅ 自 v4.1.0+ 起采用优雅完成(Graceful completion)。
- ✅ 不再向 worker 发送 DELETE。
- ✅ 对
/clear跳过这类 cleanup 处理。 - ✅ 保留仍在进行中的处理链路,避免误杀进行中的 session。
这里最重要的变化,是从“主动停止 worker”转向“把 session 标成完成,让 worker 自然收尾”。
为什么要做优雅清理
旧方案(v3)
旧方案是激进清理:
SessionEnd → DELETE /worker/session → Worker stops immediately
这个方案的问题被明确列出为:
- 中断摘要生成。
- 丢失待处理 observation。
- 引发 race conditions(竞争条件)。
也就是说,只要 summary-hook.js 还没跑完、或 save-hook.js 先前排队的数据尚未处理完,直接删 worker / 直接停 worker 就会破坏流程完整性。
新方案(v4.1.0+)
新方案改为优雅完成:
SessionEnd → UPDATE sessions SET completed_at = NOW()
Worker sees completion → Finishes processing → Exits naturally
1 这说明 cleanup-hook.js 的核心动作首先是更新 session 完成状态,而不是直接终止工作进程。
worker 读取到 session 已完成后,会继续把手头重要操作执行完,再自然退出。
优雅清理带来的效果
新方案带来的收益原文列得很具体:
- Worker 能完成重要操作。
- 会话摘要可以成功完成。
- 状态迁移更干净、更一致。
结合整个 Hook 流程来看,这尤其保护了两类工作:
- PostToolUse 阶段经 save-hook.js 排队但尚未完全处理的 observation。
- Stop 阶段由 summary-hook.js 触发或相关 worker 继续完成的会话摘要。
因此,cleanup-hook.js 虽然名字叫 cleanup,但它实际上承担的是“完成态切换 + 异步收尾保护”的职责。
在执行流中的位置
在 Hook Timing 表中,SessionEnd 的执行特征是:
- 触发时机:On exit
- Blocking:No
- Timeout:120s
- Output Handling:Log only
这几个属性意味着:
- 它发生在退出时,而不是会话处理中途。
- 它不是阻塞式关键前置步骤。
- 它的超时时间是 120 秒。
- 它的输出只用于日志,不用于向用户上下文注入结构化内容。
与 summary-hook.js 不同,cleanup-hook.js 的结果不写成摘要结构;与 SessionStart 的 context hook 不同,它也不会通过 hookSpecificOutput.additionalContext 静默注入上下文。
细节与边界
它会做什么
- 将 session 标记为 completed。
- 给 worker 留出处理完成窗口。
- 在退出链路上执行温和、可收敛的状态清理。
它不会做什么
- 不再直接发送 DELETE 去强制停止 worker。
- 不把
/clear当作真正的 session end 来处理。 - 不负责生成摘要正文;那是 summary-hook.js 的职责。
- 不负责把工具使用记录排队;那是 save-hook.js 在 PostToolUse 阶段的职责。
版本边界
优雅清理方案明确是 v4.1.0+ 的设计。也就是说,如果对比更早版本,cleanup-hook.js 所代表的会话结束语义已经发生了架构变化:从“立即终止”迁移为“标记完成后自然退出”。
与相关条目的关系
- SessionEnd:cleanup-hook.js 对应的生命周期阶段。
- summary-hook.js:负责 Stop 阶段摘要生成;优雅清理会避免它的生成过程被提前打断。
- save-hook.js:负责 PostToolUse 后 observation 排队;优雅清理会降低待处理 observation 丢失的风险。
- Hooks architecture 摘要:用于理解它在五阶段 Hook 执行流中的位置。
- Worker Service Architecture:用于理解“标记 completed 后由 worker 自然收尾退出”的架构意图。
- Progressive Enhancement:如果该条目讨论系统如何在不破坏主流程的前提下逐步增强能力,那么 cleanup-hook.js 的非阻塞、优雅收尾方式与这种设计哲学相容。
- version-check.js:同属 hook 脚本,但职责不同;前者偏会话结束收尾,后者偏开始前环境/版本提示。
结论
cleanup-hook.js 是 SessionEnd 阶段的收尾脚本,配置命令为 ${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js,源码来自 src/hooks/cleanup-hook.ts 并构建到 plugin/scripts/cleanup-hook.js。
它最核心的价值不只是“清理”,而是以 v4.1.0+ 引入的优雅完成机制,把 session 标记为完成,让 worker 自然处理完剩余任务,避免旧式强制终止带来的摘要中断、observation 丢失和竞争条件。