Как работают сеансы
Когда вы создаёте сессию, CLI Copilot сохраняет историю разговоров, состояние инструмента и контекст планирования. По умолчанию это состояние остаётся в памяти и исчезает после окончания сессии. С включенной устойчивостью вы можете возобновлять сессии при перезапусках, миграциях контейнеров или даже в разных клиентских инстансах.

| Государство | Что происходит |
|---|---|
| Создать | |
session_id Назначено | |
| Активный | Отправляйте подсказки, призывы к инструментам, ответы |
| Приостановлено | Состояние сохранено на диск |
| Резюме | Состояние, загруженное с диска |
Быстрый старт: создание возобновляемой сессии
Ключ к возобновляемым сессиям — это предоставление собственных session_id. Без этого SDK генерирует случайный идентификатор, и сессию нельзя возобновить позже.
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
// Create a session with a meaningful ID
const session = await client.createSession({
sessionId: "user-123-task-456",
model: "gpt-5.2-codex",
});
// Do some work...
await session.sendAndWait({ prompt: "Analyze my codebase" });
// Session state is automatically persisted
// You can safely close the client
Python
from copilot import CopilotClient
from copilot.session import PermissionHandler
client = CopilotClient()
await client.start()
# Create a session with a meaningful ID
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.2-codex", session_id="user-123-task-456")
# Do some work...
await session.send_and_wait("Analyze my codebase")
# Session state is automatically persisted
Go
package main
import (
"context"
copilot "github.com/github/copilot-sdk/go"
"github.com/github/copilot-sdk/go/rpc"
)
func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
SessionID: "user-123-task-456",
Model: "gpt-5.2-codex",
OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
return &rpc.PermissionDecisionApproveOnce{}, nil
},
})
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Analyze my codebase"})
_ = session
}
ctx := context.Background()
client := copilot.NewClient(nil)
// Create a session with a meaningful ID
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
SessionID: "user-123-task-456",
Model: "gpt-5.2-codex",
})
// Do some work...
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Analyze my codebase"})
// Session state is automatically persisted
C# (.NET)
using GitHub.Copilot;
var client = new CopilotClient();
// Create a session with a meaningful ID
var session = await client.CreateSessionAsync(new SessionConfig
{
SessionId = "user-123-task-456",
Model = "gpt-5.2-codex",
});
// Do some work...
await session.SendAndWaitAsync(new MessageOptions { Prompt = "Analyze my codebase" });
// Session state is automatically persisted
Возобновление сессии
Позже — через минуты, часы или даже дни — вы сможете продолжить сессию с того места, где остановились.

TypeScript
// Resume from a different client instance (or after restart)
const session = await client.resumeSession("user-123-task-456");
// Continue where you left off
await session.sendAndWait({ prompt: "What did we discuss earlier?" });
Python
# Resume from a different client instance (or after restart)
session = await client.resume_session("user-123-task-456", on_permission_request=PermissionHandler.approve_all)
# Continue where you left off
await session.send_and_wait("What did we discuss earlier?")
Go
package main
import (
"context"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
session, _ := client.ResumeSession(ctx, "user-123-task-456", nil)
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "What did we discuss earlier?"})
_ = session
}
ctx := context.Background()
// Resume from a different client instance (or after restart)
session, _ := client.ResumeSession(ctx, "user-123-task-456", nil)
// Continue where you left off
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "What did we discuss earlier?"})
C# (.NET)
using GitHub.Copilot;
using GitHub.Copilot.Rpc;
public static class ResumeSessionExample
{
public static async Task Main()
{
await using var client = new CopilotClient();
var session = await client.ResumeSessionAsync("user-123-task-456", new ResumeSessionConfig
{
OnPermissionRequest = (req, inv) =>
Task.FromResult(PermissionDecision.ApproveOnce()),
});
await session.SendAndWaitAsync(new MessageOptions { Prompt = "What did we discuss earlier?" });
}
}
// Resume from a different client instance (or after restart)
var session = await client.ResumeSessionAsync("user-123-task-456");
// Continue where you left off
await session.SendAndWaitAsync(new MessageOptions { Prompt = "What did we discuss earlier?" });
Варианты резюме
При возобновлении сессии вы можете опционально перенастроить несколько настроек. Это полезно, когда нужно изменить модель, обновить конфигурацию инструментов или изменить поведение.
| Опция | Description |
|---|---|
model | Измените модель для возобновлённой сессии |
systemMessage | Отменить или расширить системный запрос |
availableTools | Ограничьте доступные инструменты |
excludedTools | Отключите определённые инструменты |
provider | Повторное предоставление учетных данных BYOK (требуется для сессий BYOK) |
reasoningEffort | Корректируйте уровень усилий по рассуждению |
streaming | Включить/отключить потоковые ответы |
workingDirectory | Изменить рабочий каталог |
configDir | Каталог конфигурации Override |
mcpServers | Настройка MCP-серверов |
customAgents | Настройка пользовательских агентов |
agent | Предварительный выбор пользовательского агента по имени |
skillDirectories | Каталоги для загрузки навыков |
disabledSkills | Навыки для отключения |
infiniteSessions | Настройка поведения бесконечных сессий |
Пример: изменение модели в резюме
// Resume with a different model
const session = await client.resumeSession("user-123-task-456", {
model: "claude-sonnet-4", // Switch to a different model
reasoningEffort: "high", // Increase reasoning effort
});
Использование BYOK (принеси свой ключ) с возобновленными сессиями
При использовании собственных API-ключей при возобновлении работы необходимо повторно указать конфигурацию провайдера. API-ключи никогда не сохраняются на диске по соображениям безопасности.
// Original session with 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",
},
});
// When resuming, you MUST re-provide the provider config
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, // Required again
deploymentId: "my-gpt-deployment",
},
});
Что настаивает?
Состояние сессии сохраняется в ~/.copilot/session-state/{sessionId}/виде:
~/.copilot/session-state/
└── user-123-task-456/
├── checkpoints/ # Conversation history snapshots
│ ├── 001.json # Initial state
│ ├── 002.json # After first interaction
│ └── ... # Incremental checkpoints
├── plan.md # Agent's planning state (if any)
└── files/ # Session artifacts
├── analysis.md # Files the agent created
└── notes.txt # Working documents
| Данные | Продолжался? | Notes |
|---|---|---|
| История беседы | ||
| ✅ Да | Полный поток сообщений | |
| Результаты вызова инструментов | ||
| ✅ Да | Кэшировано для контекста | |
| Состояние планирования агента | ||
| ✅ Да | Файл plan.md | |
| Артефакты сессии | ||
| ✅ Да | В files/ справочнике | |
| Ключи провайдера/API | ||
| ❌ Нет | Безопасность: необходимо повторно предоставить | |
| Состояние инструмента в памяти | ||
| ❌ Нет | Инструменты должны быть безсостоятельными |
Лучшие практики идентификатора сессии
Выбирайте идентификаторы сессий, которые кодируют принадлежность и назначение. Это значительно облегчает аудит и уборку.
| Pattern | Пример | Вариант использования |
|---|---|---|
| ❌ | ||
abc123 | ||
| Случайные идентификаторы | Сложно аудитировать, нет информации о владельце | |
| ✅ | ||
user-{userId}-{taskId} | ||
user-alice-pr-review-42 | Многопользовательские приложения | |
| ✅ | ||
tenant-{tenantId}-{workflow} | ||
tenant-acme-onboarding | Многопользовательский SaaS | |
| ✅ | ||
{userId}-{taskId}-{timestamp} | ||
alice-deploy-1706932800 | Очистка по времени |
Преимущества структурированных ID:
- Легко аудитировать: «Показать все сессии для пользователя Alice»
- Простое исправление: «Удалить все сессии старше X»
- Естественный контроль доступа: разбор user ID из session 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"
Управление жизненным циклом сессии
Список активных сессий
// List all sessions
const sessions = await client.listSessions();
console.log(`Found ${sessions.length} sessions`);
for (const session of sessions) {
console.log(`- ${session.sessionId} (created: ${session.createdAt})`);
}
// Filter sessions by repository
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(`Deleted expired session: ${session.sessionId}`);
}
}
}
// Clean up sessions older than 24 hours
await cleanupExpiredSessions(24 * 60 * 60 * 1000);
Отключение от сессии (disconnect)
Когда задача завершена, явно отключайтесь от сессии, а не ждите тайм-аутов. Это освобождает ресурсы в памяти, но сохраняет данные сессии на диске, так что сессию можно возобновить позже:
try {
// Do work...
await session.sendAndWait({ prompt: "Complete the task" });
// Task complete — release in-memory resources (session can be resumed later)
await session.disconnect();
} catch (error) {
// Clean up even on error
await session.disconnect();
throw error;
}
Каждый SDK также предоставляет идиоматические автоматические шаблоны очистки:
| Язык | Pattern | Пример |
|---|---|---|
| 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() |
Примечание.
Вместо PackageIconUrl теперь используется PackageIcon. Существующий код, использующий destroy() его, продолжит работать, но его следует перевести.
Постоянное удаление сессии (deleteSession)
Чтобы навсегда удалить сессию и все её данные с диска (история разговоров, состояние планирования, артефакты), используйте deleteSession. Это необратимо — сессия не может быть возобновлена после удаления:
// Permanently remove session data
await client.deleteSession("user-123-task-456");
disconnect()VSdeleteSession():disconnect()освобождает ресурсы в памяти, но сохраняет данные сессии на диске для последующего возобновления.deleteSession()Навсегда удаляет всё, включая файлы с диска.
Автоматическая очистка: тайм-аут на холостом ходу
По умолчанию сессии не имеют тайм-аута в простое и живут бесконечно, пока не будут явно отключены или удалены. По желанию вы можете настроить серверный тайм-аут простоя с помощью CopilotClientOptions.sessionIdleTimeoutSecondsследующих методов:
const client = new CopilotClient({
sessionIdleTimeoutSeconds: 30 * 60, // 30 minutes
});
Когда тайм-аут настроен, сессии без активности в течение этого времени автоматически очищаются. Настройте или 0 пропустите, чтобы отключить.
Примечание.
Эта опция действует только тогда, когда SDK запускает процесс выполнения. При подключении к существующему серверу через cliUrl, применяется собственная тайм-аут сервера.

Сессии с активной работой (выполняющие команды, фоновые агенты) всегда защищены от очистки в режиме простоя, независимо от настройки тайм-аута.
Прислушивайтесь к событиям простоя, которые могут реагировать на неактивность сессии:
session.on("session.idle", (event) => {
console.log(`Session idle for ${event.idleDurationMs}ms`);
});
Шаблоны развертывания
Шаблон 1: один CLI-сервер на пользователя (рекомендуется)
Лучше всего для: сильной изоляции, многоарендных сред, динамических сессий Azure.

**Преимущества:**✅ Полная изоляция | ✅ Простая безопасность | ✅ Простое масштабирование
Шаблон 2: общий CLI-сервер (ресурсоэффективный)
Лучше всего для: внутренних инструментов, доверенных сред, ограниченных ресурсами конфигураций.

Требования:
- ⚠️ Уникальные идентификаторы сессий для каждого пользователя
- ⚠️ Управление доступом на уровне приложений
- ⚠️ Проверка идентификатора сессии перед операциями
// Application-level access control for shared CLI
async function resumeSessionWithAuth(
client: CopilotClient,
sessionId: string,
currentUserId: string
): Promise<Session> {
// Parse user from session ID
const [sessionUserId] = sessionId.split("-");
if (sessionUserId !== currentUserId) {
throw new Error("Access denied: session belongs to another user");
}
return client.resumeSession(sessionId);
}
Azure dynamic sessions
Для серверных/контейнерных развертываний, где контейнеры могут перезапускаться или мигрировать:
Монтирование постоянного хранения
Каталог состояния сессии должен быть смонтирован в постоянное хранилище:
# Azure Container Instance example
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

Сессия переживает запуск в контейнере!
Бесконечные сессии для долгосрочных рабочих процессов
Для рабочих процессов, которые могут превышать ограничения контекста, включайте бесконечные сессии с автоматической компрессией:
const session = await client.createSession({
sessionId: "long-workflow-123",
infiniteSessions: {
enabled: true,
backgroundCompactionThreshold: 0.80, // Start compaction at 80% context
bufferExhaustionThreshold: 0.95, // Block at 95% if needed
},
});
Примечание.
Пороги — это коэффициенты использования контекста (0,0-1,0), а не абсолютное количество токенов. Подробности смотрите в АВТОЗАГОЛОВКЕ .
Ограничения и рекомендации
| Limitation | Description | Смягчение последствий |
|---|---|---|
| Повторная аутентификация BYOK | Ключи API не сохраняются | Ключи от хранилища в вашем секретном менеджере; Указывайте в резюме |
| Регистрируемое хранилище | ||
~/.copilot/session-state/ должно быть записано | Монтировать постоянный объём в контейнеры | |
| Нет блокировки сессии | Параллельный доступ к одной и той же сессии не определен | Реализовать блокировку или очередь на уровне приложения |
| Состояние инструмента не сохраняется | Состояние инструмента в памяти теряется | Проектируйте инструменты так, чтобы они были безсостоятельными или сохраняли своё собственное состояние |
Обработка параллельного доступа
SDK не обеспечивает встроенную блокировку сессий. Если несколько клиентов могут получить доступ к одной и той же сессии:
// Option 1: Application-level locking with 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("Session is in use by another client");
}
try {
return await fn();
} finally {
await redis.del(lockKey);
}
}
// Usage
await withSessionLock("user-123-task-456", async () => {
const session = await client.resumeSession("user-123-task-456");
await session.sendAndWait({ prompt: "Continue the task" });
});
Сводка
| Функция | Использование |
|---|---|
| Создать вособновляемую сессию | Предоставляйте свои собственные sessionId |
| Сессия резюме | client.resumeSession(sessionId) |
| Резюме BYOK | Повторное предоставление provider конфигурации |
| Перечисление сеансов | client.listSessions(filter?) |
| Отключение от активной сессии | |
session.disconnect()— выпускает ресурсы в памяти; Данные сессии на диске сохраняются для возобновления | |
| Навсегда удалить сессию | |
client.deleteSession(sessionId)—навсегда удаляет все данные сессии с диска; возобновить не может | |
| Контейнерное развертывание | Монтирование ~/.copilot/session-state/ на постоянное хранилище |
Дальнейшие действия
- Сессионные хуки — Настройка поведения сессии с помощью хуков
- Совместимость SDK и CLI — сравнение функций SDK и CLI
- Руководство по отладке - Проблемы с диагностикой сессии