GitHub Copilot SDK会话持久化:跨重启恢复会话的完整方案
【免费下载链接】copilot-sdkMulti-platform SDK for integrating GitHub Copilot Agent into apps and services项目地址: https://gitcode.com/GitHub_Trending/co/copilot-sdk
GitHub Copilot SDK是一款多平台软件开发工具包,专为将GitHub Copilot Agent集成到各类应用和服务中而设计。其中,会话持久化功能是其核心特性之一,它允许用户在应用重启、容器迁移甚至更换客户端实例后,仍能无缝恢复之前的会话状态,极大地提升了开发体验和工作连续性。
会话持久化基础:理解会话状态管理
会话的生命周期与状态转换
当创建一个会话时,Copilot CLI会维护对话历史、工具状态和规划上下文。默认情况下,这些状态仅存在于内存中,会话结束后便会消失。而启用持久化功能后,会话可以在暂停后 resume(恢复),实现跨重启的状态保持。
会话主要有以下几种状态:
- Create(创建):系统为会话分配唯一的
session_id - Active(活跃):可以发送提示、进行工具调用和接收响应
- Paused(暂停):会话状态被保存到磁盘
- Resume(恢复):从磁盘加载之前保存的会话状态
持久化会话的存储结构
会话状态会被保存到~/.copilot/session-state/{sessionId}/目录下,典型的存储结构如下:
~/.copilot/session-state/ └── user-123-task-456/ ├── checkpoints/ # 对话历史快照 │ ├── 001.json # 初始状态 │ ├── 002.json # 第一次交互后状态 │ └── ... # 增量检查点 ├── plan.md # 代理的规划状态(如有) └── files/ # 会话工件 ├── analysis.md # 代理创建的文件 └── notes.txt # 工作文档并非所有数据都会被持久化,以下是关键数据的持久化情况:
| 数据类型 | 是否持久化 | 说明 |
|---|---|---|
| 对话历史 | ✅ 是 | 完整的消息线程 |
| 工具调用结果 | ✅ 是 | 缓存用于上下文 |
| 代理规划状态 | ✅ 是 | 存储在plan.md文件中 |
| 会话工件 | ✅ 是 | 保存在files/目录下 |
| 提供商/API密钥 | ❌ 否 | 出于安全考虑,必须在恢复时重新提供 |
| 内存中的工具状态 | ❌ 否 | 工具应设计为无状态 |
快速上手:创建可恢复的会话
创建可恢复会话的关键在于提供自定义的session_id。如果不指定,SDK会生成随机ID,导致会话无法在后续恢复。
TypeScript实现
import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); // 使用有意义的ID创建会话 const session = await client.createSession({ sessionId: "user-123-task-456", model: "gpt-5.2-codex", }); // 执行一些操作... await session.sendAndWait({ prompt: "分析我的代码库" }); // 会话状态会自动持久化 // 你可以安全地关闭客户端Python实现
from copilot import CopilotClient from copilot.session import PermissionHandler client = CopilotClient() await client.start() # 使用有意义的ID创建会话 session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.2-codex", session_id="user-123-task-456") # 执行一些操作... await session.send_and_wait("分析我的代码库") # 会话状态会自动持久化C# (.NET)实现
using GitHub.Copilot; var client = new CopilotClient(); // 使用有意义的ID创建会话 var session = await client.CreateSessionAsync(new SessionConfig { SessionId = "user-123-task-456", Model = "gpt-5.2-codex", }); // 执行一些操作... await session.SendAndWaitAsync(new MessageOptions { Prompt = "分析我的代码库" }); // 会话状态会自动持久化恢复会话:跨重启继续工作
基本恢复操作
无论经过几分钟、几小时甚至几天,都可以从上次中断的地方恢复会话:
TypeScript恢复示例
// 从不同的客户端实例(或重启后)恢复 const session = await client.resumeSession("user-123-task-456"); // 继续之前的工作 await session.sendAndWait({ prompt: "我们之前讨论了什么?" });Python恢复示例
# 从不同的客户端实例(或重启后)恢复 session = await client.resume_session("user-123-task-456", on_permission_request=PermissionHandler.approve_all) # 继续之前的工作 await session.send_and_wait("我们之前讨论了什么?")恢复时的高级配置选项
恢复会话时,可以选择性地重新配置许多设置,这对于更改模型、更新工具配置或修改行为非常有用:
| 选项 | 描述 |
|---|---|
model | 更改恢复会话使用的模型 |
systemMessage | 覆盖或扩展系统提示 |
availableTools | 限制可用的工具 |
excludedTools | 禁用特定工具 |
provider | 重新提供BYOK凭据(BYOK会话必需) |
reasoningEffort | 调整推理努力级别 |
streaming | 启用/禁用流式响应 |
workingDirectory | 更改工作目录 |
示例:恢复时更改模型
// 使用不同的模型恢复 const session = await client.resumeSession("user-123-task-456", { model: "claude-sonnet-4", // 切换到不同的模型 reasoningEffort: "high", // 增加推理努力 });使用BYOK(自带密钥)恢复会话
使用自己的API密钥时,必须在恢复会话时重新提供提供商配置。出于安全原因,API密钥永远不会持久化到磁盘:
// 使用BYOK创建原始会话 const session = await client.createSession({ sessionId: "user-123-task-456", model: "gpt-5.2-codex", provider: { type: "azure", endpoint: "https://my-resource.openai.azure.com", apiKey: process.env.AZURE_OPENAI_KEY, deploymentId: "my-gpt-deployment", }, }); // 恢复时,必须重新提供提供商配置 const resumed = await client.resumeSession("user-123-task-456", { provider: { type: "azure", endpoint: "https://my-resource.openai.azure.com", apiKey: process.env.AZURE_OPENAI_KEY, // 再次需要 deploymentId: "my-gpt-deployment", }, });会话ID的最佳实践
选择能够编码所有权和用途的会话ID,这会使审计和清理变得更加容易。
推荐的命名模式
| 模式 | 示例 | 用例 |
|---|---|---|
❌abc123 | 随机ID | 难以审计,没有所有权信息 |
✅user-{userId}-{taskId} | user-alice-pr-review-42 | 多用户应用 |
✅tenant-{tenantId}-{workflow} | tenant-acme-onboarding | 多租户SaaS |
✅{userId}-{taskId}-{timestamp} | alice-deploy-1706932800 | 基于时间的清理 |
结构化ID的好处
- 易于审计:"显示用户alice的所有会话"
- 易于清理:"删除所有早于X的会话"
- 自然的访问控制:从会话ID解析用户ID
生成会话ID的示例代码
function createSessionId(userId: string, taskType: string): string { const timestamp = Date.now(); return `${userId}-${taskType}-${timestamp}`; } const sessionId = createSessionId("alice", "code-review"); // → "alice-code-review-1706932800000"import time def create_session_id(user_id: str, task_type: str) -> str: timestamp = int(time.time()) return f"{user_id}-{task_type}-{timestamp}" session_id = create_session_id("alice", "code-review") # → "alice-code-review-1706932800"会话生命周期管理
列出活跃会话
// 列出所有会话 const sessions = await client.listSessions(); console.log(`找到 ${sessions.length} 个会话`); for (const session of sessions) { console.log(`- ${session.sessionId} (创建时间: ${session.createdAt})`); } // 按仓库筛选会话 const repoSessions = await client.listSessions({ repository: "owner/repo" });清理旧会话
async function cleanupExpiredSessions(maxAgeMs: number) { const sessions = await client.listSessions(); const now = Date.now(); for (const session of sessions) { const age = now - new Date(session.createdAt).getTime(); if (age > maxAgeMs) { await client.deleteSession(session.sessionId); console.log(`已删除过期会话: ${session.sessionId}`); } } } // 清理超过24小时的会话 await cleanupExpiredSessions(24 * 60 * 60 * 1000);断开会话连接(disconnect)
任务完成后,显式断开会话连接而不是等待超时。这会释放内存资源,但保留磁盘上的会话数据,因此会话仍可在以后恢复:
try { // 执行工作... await session.sendAndWait({ prompt: "完成任务" }); // 任务完成 — 释放内存资源(会话可在以后恢复) await session.disconnect(); } catch (error) { // 即使出错也进行清理 await session.disconnect(); throw error; }各SDK还提供了惯用的自动清理模式:
| 语言 | 模式 | 示例 |
|---|---|---|
| TypeScript | Symbol.asyncDispose | await using session = await client.createSession(config); |
| Python | async with上下文管理器 | async with await client.create_session(on_permission_request=handler) as session: |
| C# | IAsyncDisposable | await using var session = await client.CreateSessionAsync(config); |
| Go | defer | defer session.Disconnect() |
注意:
destroy()已被disconnect()取代。使用destroy()的现有代码将继续工作,但应进行迁移。
永久删除会话(deleteSession)
要永久从磁盘删除会话及其所有数据(对话历史、规划状态、工件),请使用deleteSession。这是不可逆的 — 删除后无法恢复会话:
// 永久删除会话数据 await client.deleteSession("user-123-task-456");
disconnect()与deleteSession()的区别:disconnect()释放内存资源,但保留磁盘上的会话数据供以后恢复。deleteSession()永久删除所有内容,包括磁盘上的文件。
自动清理:空闲超时
默认情况下,会话没有空闲超时,会无限期存在,直到显式断开连接或删除。你可以通过CopilotClientOptions.sessionIdleTimeoutSeconds选择性地配置服务器范围的空闲超时:
const client = new CopilotClient({ sessionIdleTimeoutSeconds: 30 * 60, // 30分钟 });配置超时后,超过该持续时间无活动的会话将被自动清理。设置为0或省略以禁用超时。
可以监听空闲事件以响应对话不活动:
session.on("session.idle", (event) => { console.log(`会话已空闲 ${event.idleDurationMs}毫秒`); });部署模式
模式1:每个用户一个CLI服务器(推荐)
最适合:强隔离、多租户环境、Azure动态会话。
优点:✅ 完全隔离 | ✅ 简单安全 | ✅ 易于扩展
模式2:共享CLI服务器(资源高效)
最适合:内部工具、可信环境、资源受限的设置。
要求:
- ⚠️ 每个用户唯一的会话ID
- ⚠️ 应用级访问控制
- ⚠️ 操作前验证会话ID
// 共享CLI的应用级访问控制 async function resumeSessionWithAuth( client: CopilotClient, sessionId: string, currentUserId: string ): Promise<Session> { // 从会话ID解析用户 const [sessionUserId] = sessionId.split("-"); if (sessionUserId !== currentUserId) { throw new Error("访问被拒绝:会话属于其他用户"); } return client.resumeSession(sessionId); }容器化部署中的会话持久化
对于容器可能重启或迁移的无服务器/容器部署,需要将会话状态目录挂载到持久存储:
# Azure容器实例示例 containers: - name: copilot-agent image: my-agent:latest volumeMounts: - name: session-storage mountPath: /home/app/.copilot/session-state volumes: - name: session-storage azureFile: shareName: copilot-sessions storageAccountName: myaccount通过这种配置,会话可以在容器重启后继续存在!
处理并发访问
SDK不提供内置的会话锁定。如果多个客户端可能访问同一个会话,可以实现应用级锁定:
// 选项1:使用Redis进行应用级锁定 import Redis from "ioredis"; const redis = new Redis(); async function withSessionLock<T>( sessionId: string, fn: () => Promise<T> ): Promise<T> { const lockKey = `session-lock:${sessionId}`; const acquired = await redis.set(lockKey, "locked", "NX", "EX", 300); if (!acquired) { throw new Error("会话正被另一个客户端使用"); } try { return await fn(); } finally { await redis.del(lockKey); } } // 使用方法 await withSessionLock("user-123-task-456", async () => { const session = await client.resumeSession("user-123-task-456"); await session.sendAndWait({ prompt: "继续任务" }); });会话持久化功能摘要
| 功能 | 使用方法 |
|---|---|
| 创建可恢复会话 | 提供自定义sessionId |
| 恢复会话 | client.resumeSession(sessionId) |
| BYOK恢复 | 重新提供provider配置 |
| 列出会话 | client.listSessions(filter?) |
| 断开活动会话连接 | session.disconnect()—释放内存资源;磁盘上的会话数据保留用于恢复 |
| 永久删除会话 | client.deleteSession(sessionId)—永久删除所有会话数据;无法恢复 |
| 容器化部署 | 将~/.copilot/session-state/挂载到持久存储 |
总结与最佳实践
GitHub Copilot SDK的会话持久化功能为开发人员提供了强大的会话状态管理能力,通过合理使用这一功能,可以显著提升工作效率和连续性。以下是一些关键建议:
- 始终使用有意义的会话ID,包含用户标识、任务类型和时间戳等信息
- 在使用BYOK时,确保安全存储API密钥,并在恢复会话时重新提供
- 定期清理不再需要的会话,避免存储空间浪费
- 在容器化部署中,务必将会话状态目录挂载到持久存储
- 对于多用户环境,实现适当的访问控制,防止未授权访问会话
- 使用
disconnect()而非deleteSession(),除非确定不再需要该会话
通过遵循这些最佳实践,你可以充分利用GitHub Copilot SDK的会话持久化功能,构建更健壮、更可靠的Copilot集成应用。
更多详细信息,请参考官方文档:docs/features/session-persistence.md
【免费下载链接】copilot-sdkMulti-platform SDK for integrating GitHub Copilot Agent into apps and services项目地址: https://gitcode.com/GitHub_Trending/co/copilot-sdk
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考