Skip to main content

会话生命周期挂钩

会话生命周期挂钩使你能够响应会话开始和结束事件。 使用它们来:

  • 在会话开始时初始化上下文
  • 会话结束时清理资源
  • 跟踪会话指标和分析
  • 动态配置会话行为

会话启动钩子 {#session-start}

当会话开始时(新建或恢复),会调用 onSessionStart 钩子。

挂钩签名

代码语言 navigation

TypeScript
import type { SessionStartHookInput, HookInvocation, SessionStartHookOutput } from "@github/copilot-sdk";
type SessionStartHandler = (
  input: SessionStartHookInput,
  invocation: HookInvocation
) => Promise<SessionStartHookOutput | null | undefined>;
type SessionStartHandler = (
  input: SessionStartHookInput,
  invocation: HookInvocation
) => Promise<SessionStartHookOutput | null | undefined>;

输入

领域类型说明
timestampnumber触发挂钩时的 Unix 时间戳
cwd字符串当前工作目录
source
"startup"
|
"resume"
|
"new"
会话的启动方式
initialPrompt字符串 | 未定义提供的初始提示

输出

领域类型说明
additionalContext字符串在会话开始时添加的上下文
modifiedConfig对象替代会话配置

例子

在启动时添加项目上下文

代码语言 navigation

TypeScript
const session = await client.createSession({
  hooks: {
    onSessionStart: async (input, invocation) => {
      console.log(`Session ${invocation.sessionId} started (${input.source})`);
      
      const projectInfo = await detectProjectType(input.cwd);
      
      return {
        additionalContext: `
This is a ${projectInfo.type} project.
Main language: ${projectInfo.language}
Package manager: ${projectInfo.packageManager}
        `.trim(),
      };
    },
  },
});

处理会话恢复

const session = await client.createSession({
  hooks: {
    onSessionStart: async (input, invocation) => {
      if (input.source === "resume") {
        // Load previous session state
        const previousState = await loadSessionState(invocation.sessionId);
        
        return {
          additionalContext: `
Session resumed. Previous context:
- Last topic: ${previousState.lastTopic}
- Open files: ${previousState.openFiles.join(", ")}
          `.trim(),
        };
      }
      return null;
    },
  },
});

加载用户首选项

const session = await client.createSession({
  hooks: {
    onSessionStart: async () => {
      const preferences = await loadUserPreferences();
      
      const contextParts = [];
      
      if (preferences.language) {
        contextParts.push(`Preferred language: ${preferences.language}`);
      }
      if (preferences.codeStyle) {
        contextParts.push(`Code style: ${preferences.codeStyle}`);
      }
      if (preferences.verbosity === "concise") {
        contextParts.push("Keep responses brief and to the point.");
      }
      
      return {
        additionalContext: contextParts.join("\n"),
      };
    },
  },
});

会话结束钩子 {#session-end}

会话结束时调用onSessionEnd钩子。

挂钩签名

代码语言 navigation

TypeScript
type SessionEndHandler = (
  input: SessionEndHookInput,
  invocation: HookInvocation
) => Promise<SessionEndHookOutput | null | undefined>;

输入

领域类型说明
timestampnumber触发挂钩时的 Unix 时间戳
cwd字符串当前工作目录
reason字符串会话结束的原因(请参阅下文)
finalMessage字符串 | 未定义会话中的最后一条消息
error字符串 | 未定义会话因错误结束时的错误消息

结束原因

原因说明
"complete"会话正常完成
"error"会话因错误而结束
"abort"会话已由用户或代码中止
"timeout"会话超时
"user_exit"用户显式结束会话

输出

领域类型说明
suppressOutputboolean抑制最终会话输出
cleanupActionsstring[]要执行的清理操作列表
sessionSummary字符串日志记录/分析会话摘要

例子

跟踪会话指标

代码语言 navigation

TypeScript
const sessionStartTimes = new Map<string, number>();

const session = await client.createSession({
  hooks: {
    onSessionStart: async (input, invocation) => {
      sessionStartTimes.set(invocation.sessionId, input.timestamp);
      return null;
    },
    onSessionEnd: async (input, invocation) => {
      const startTime = sessionStartTimes.get(invocation.sessionId);
      const duration = startTime ? input.timestamp - startTime : 0;
      
      await recordMetrics({
        sessionId: invocation.sessionId,
        duration,
        endReason: input.reason,
      });
      
      sessionStartTimes.delete(invocation.sessionId);
      return null;
    },
  },
});

清理资源

const sessionResources = new Map<string, { tempFiles: string[] }>();

const session = await client.createSession({
  hooks: {
    onSessionStart: async (input, invocation) => {
      sessionResources.set(invocation.sessionId, { tempFiles: [] });
      return null;
    },
    onSessionEnd: async (input, invocation) => {
      const resources = sessionResources.get(invocation.sessionId);
      
      if (resources) {
        // Clean up temp files
        for (const file of resources.tempFiles) {
          await fs.unlink(file).catch(() => {});
        }
        sessionResources.delete(invocation.sessionId);
      }
      
      console.log(`Session ${invocation.sessionId} ended: ${input.reason}`);
      return null;
    },
  },
});

保存会话状态以供恢复

const session = await client.createSession({
  hooks: {
    onSessionEnd: async (input, invocation) => {
      if (input.reason !== "error") {
        // Save state for potential resume
        await saveSessionState(invocation.sessionId, {
          endTime: input.timestamp,
          cwd: input.cwd,
          reason: input.reason,
        });
      }
      return null;
    },
  },
});

日志会话摘要

const sessionData: Record<string, { prompts: number; tools: number; startTime: number }> = {};

const session = await client.createSession({
  hooks: {
    onSessionStart: async (input, invocation) => {
      sessionData[invocation.sessionId] = { 
        prompts: 0, 
        tools: 0, 
        startTime: input.timestamp 
      };
      return null;
    },
    onUserPromptSubmitted: async (_, invocation) => {
      sessionData[invocation.sessionId].prompts++;
      return null;
    },
    onPreToolUse: async (_, invocation) => {
      sessionData[invocation.sessionId].tools++;
      return { permissionDecision: "allow" };
    },
    onSessionEnd: async (input, invocation) => {
      const data = sessionData[invocation.sessionId];
      console.log(`
Session Summary:
  ID: ${invocation.sessionId}
  Duration: ${(input.timestamp - data.startTime) / 1000}s
  Prompts: ${data.prompts}
  Tool calls: ${data.tools}
  End reason: ${input.reason}
      `.trim());
      
      delete sessionData[invocation.sessionId];
      return null;
    },
  },
});

最佳做法

  1. 确保 onSessionStart 保持快速 - 用户正在等待会话就绪。

  2. 处理所有可能的结束原因 - 不要假定会话都会正常结束;要处理错误和中止情况。

  3. 清理资源 - 使用 onSessionEnd 释放会话期间分配的任何资源。

  4. 存储最小状态 - 如果跟踪会话数据,请保持轻量级。

  5. 使清理操作具有幂等性 - onSessionEnd 如果进程崩溃,则可能不会调用该清理操作。

另见