Skip to content

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
    }
  }
})

处理不同的结束原因

onSessionEndreason 字段说明会话为何结束:

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。