Appearance
onSessionStart 在会话创建时触发,适合注入项目信息、初始化追踪数据;onSessionEnd 在会话结束时触发(无论正常结束还是报错),适合清理资源和记录会话统计。保持 onSessionStart 快速,确保 onSessionEnd 处理所有结束原因。
GitHub Copilot SDK 会话生命周期 Hooks:onSessionStart 与 onSessionEnd
会话生命周期
createSession() → onSessionStart → [正常使用] → onSessionEnd
↑
(正常结束/超时/出错)onSessionStart
会话创建后立即触发,适合:
- 注入项目相关的全局上下文
- 初始化会话级别的追踪数据
- 设置初始权限或配置
typescript
const sessionMetadata = new Map<string, object>()
const session = await createSession({
hooks: {
onSessionStart: async ({ sessionId }) => {
// 记录开始时间
sessionMetadata.set(sessionId, {
startTime: Date.now(),
toolCallCount: 0
})
// 注入项目上下文(让 AI 了解当前项目)
return {
additionalContext: `
项目信息:
- 语言:TypeScript (strict mode)
- 框架:Next.js 15 + tRPC
- 数据库:PostgreSQL via Prisma
- 代码规范:遵循 ESLint + Prettier 配置
- 禁止直接修改 prisma/migrations/ 目录下的文件
`.trim()
}
}
}
})onSessionEnd
会话结束时触发,无论是正常结束、超时还是报错,一定会被调用。
typescript
const session = await createSession({
hooks: {
onSessionEnd: async ({ sessionId, reason, durationMs }) => {
const meta = sessionMetadata.get(sessionId)
// 记录会话统计
console.log(`[会话结束] ${sessionId}`, {
reason, // 'completed' | 'timeout' | 'error' | 'cancelled'
duration: `${durationMs}ms`,
toolCalls: (meta as any)?.toolCallCount ?? 0
})
// 上报指标
metrics.recordSession({
id: sessionId,
durationMs,
reason
})
// 清理资源
sessionMetadata.delete(sessionId)
return null
}
}
})处理不同的结束原因
onSessionEnd 的 reason 字段说明会话为何结束:
typescript
onSessionEnd: async ({ sessionId, reason }) => {
switch (reason) {
case 'completed':
// 正常完成,可以标记任务成功
await updateTaskStatus(sessionId, 'done')
break
case 'timeout':
// 超时,需要通知用户
await notifyUser(sessionId, '任务执行超时,部分步骤可能未完成')
break
case 'error':
// 出错,需要记录和告警
await logFailure(sessionId)
break
case 'cancelled':
// 用户主动取消
break
}
return null
}用 onSessionStart 追踪工具调用次数
结合 onPreToolUse,统计每个会话的工具调用数:
typescript
const sessionStats = new Map<string, { toolCalls: number }>()
const session = await createSession({
hooks: {
onSessionStart: async ({ sessionId }) => {
sessionStats.set(sessionId, { toolCalls: 0 })
return null
},
onPreToolUse: async ({ sessionId }) => {
const stats = sessionStats.get(sessionId)
if (stats) stats.toolCalls++
return null
},
onSessionEnd: async ({ sessionId, durationMs }) => {
const stats = sessionStats.get(sessionId)
console.log(`会话 ${sessionId}: ${stats?.toolCalls} 次工具调用,耗时 ${durationMs}ms`)
sessionStats.delete(sessionId)
return null
}
}
})最佳实践
onSessionStart 要快:会话启动时 AI 在等待,慢的 onSessionStart 会延迟第一个响应时间。避免数据库查询或网络请求。
onSessionEnd 要健壮:无论会话如何结束都会触发,确保处理所有 reason,避免在 Hook 中抛出异常(清理失败不应阻塞会话结束)。
清理逻辑要幂等:如果有重试机制,onSessionEnd 可能被调用多次,确保多次调用不会导致重复操作。
常见问题
Q: onSessionStart 注入的上下文会占用 token 吗?
A: 是的,additionalContext 会作为系统消息出现在对话上下文中,占用 token。控制注入内容的长度(建议 500 字以内),只注入最关键的背景信息。
Q: onSessionEnd 是同步还是异步?
A: 支持 async,但如果会话结束流程在等待 onSessionEnd 完成,则是阻塞的。内部的异步操作(如发送告警)建议不 await,使用 .catch() 处理错误。
Q: 如果 createSession 时出错(比如认证失败),onSessionStart 会触发吗?
A: 不会。onSessionStart 在会话成功创建后触发,createSession 本身失败时不会触发任何 Hook。