SessionEnd
定义
SessionEnd 是 claude-mem 五阶段 Hook 生命周期中的第 5 个阶段,也是会话结束时的清理阶段。它对应的执行脚本是 cleanup-hook.js,用于在 Claude Code 会话结束时,把当前 session 标记为已完成,并执行 graceful cleanup,而不是粗暴中断仍在运行的后续处理。
在本文档中的语境
在本文档描述的 Hook 执行流中,SessionEnd 位于 Stop/摘要生成之后的最终收尾位置,属于整个 Session Lifecycle 的退出阶段。
它的触发条件非常明确:
- 在 Claude Code session 结束时触发。
- 不会在
/clear时触发。 - 它是退出时的清理钩子,不负责生成摘要,也不负责保存 observation;这些职责分别属于 summary-hook.js 和 PostToolUse 对应流程。
这意味着 SessionEnd 不是“清空上下文”的响应器,而是“结束会话”的完成标记器。
核心机制
1. 将 session 标记为 completed
SessionEnd 的首要动作,是在数据库中把当前 session 标记为 completed。原文给出的新方案可概括为:
SessionEnd → UPDATE sessions SET completed_at = NOW()
这里的关键不是立即删除任何运行中资源,而是通过写入完成状态,让后续组件能够感知“该会话已经结束,应进入自然收尾流程”。
2. 允许 worker 完成剩余处理
被标记完成后,worker 不会被立即强制终止;相反,它会看到该 session 已完成,然后继续把尚未处理完的工作做完,最后自然退出。
这正是文档强调的 graceful completion:
- worker 可以完成重要操作;
- 尚未落库或尚未汇总的处理有机会收尾;
- 退出不依赖暴力中断,而是依赖状态转换。
3. 执行 graceful cleanup
SessionEnd 的清理策略不是“立刻删、立刻停”,而是“标记完成 → 允许剩余处理结束 → 再退出”。这就是本文档中所谓的 graceful cleanup。
它的目标包括:
- 保持状态迁移干净;
- 避免处理中任务被截断;
- 为摘要生成和 observation 处理留出完成时间。
配置位置
SessionEnd 在 Hook 配置中通过命令方式调用 cleanup-hook.js:
{
"hooks": {
"SessionEnd": [{
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js"
}]
}]
}
}
在执行时序表里,SessionEnd 的特征是:
- Timing:On exit
- Blocking:No
- Timeout:120s
- Output Handling:Log only
也就是说,它发生在退出时,不阻塞主流程,超时时间为 120 秒,其输出主要用于日志而不是上下文注入或用户可见消息。
与旧方案的差异
旧方案(v3):激进清理
旧做法是:
SessionEnd → DELETE /worker/session → Worker stops immediately
这种 aggressive cleanup 会直接向 worker 发送删除/终止语义,导致 worker 立刻停下。
原文明确列出了这种做法的问题:
- 会中断摘要生成;
- 会丢失待处理 observation;
- 会引入 race conditions。
新方案(v4.1.0+):优雅完成
从 v4.1.0+ 开始,策略改为 graceful completion:
- 不再向 worker 发送 DELETE;
- 改为在数据库中将 session 标记完成;
- worker 检测到完成状态后,自行收尾并自然退出。
这项设计决策在文档中被明确总结为:
- ✅ Graceful completion(v4.1.0+)
- ✅ No longer sends DELETE to workers
- ✅ Skips cleanup on
/clearcommands - ✅ Preserves ongoing sessions
其中“Preserves ongoing sessions”指的是:如果只是 /clear,系统不会把它误当作会话真正结束,更不会提前清理掉仍应保留的在途处理。
细节与边界
不在 /clear 时触发
这是 SessionEnd 最重要的边界之一。文档明确写明:它在 session ends 时触发,not on /clear。
因此:
/clear不等于会话结束;/clear不应触发会话完成标记;/clear不应导致 worker 被错误清理。
这个边界直接避免了“用户只是清屏/清上下文,却被系统当成彻底结束会话”的误判。
不负责摘要生成本身
虽然 SessionEnd 的设计会保护摘要生成不被中断,但它自己不生成摘要。摘要生成属于 Stop 阶段,对应脚本是 summary-hook.js,并且会把结果存入 session_summaries。
换言之,SessionEnd 和摘要流程的关系是:
- SessionEnd 负责收尾与完成标记;
- summary-hook.js 负责 AI 摘要压缩;
- graceful cleanup 的一个重要收益,是让摘要流程更有机会顺利完成。
非阻塞退出钩子
在 Hook Timing 表中,SessionEnd 被标记为 Non-blocking。这说明它虽然发生在退出时,但不是用来卡住主交互流程的同步终止器,而是偏向后台收尾控制的生命周期信号。
设计收益
采用 SessionEnd 的优雅完成策略后,文档列出的收益包括:
- worker 能完成关键操作;
- 摘要可以成功完成;
- 状态转换更干净。
把这些收益放回整个架构里看,SessionEnd 的真正作用不是“结束得更快”,而是“结束得更正确”。它通过 completed 状态把退出动作从“强杀进程”改成“显式声明生命周期已结束”,从而与 Worker Service Architecture 更好协同。