Skip to main content

Возобновление сессии и сохранение

Это руководство проведёт вас через возможности сохранения сессий SDK — как поставить работу на паузу, возобновить её позже и управлять сессиями в производственных средах.

Как работают сеансы

Когда вы создаёте сессию, 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Пример
TypeScriptSymbol.asyncDisposeawait using session = await client.createSession(config);
Python
async with диспетчер контекстовasync with await client.create_session(on_permission_request=handler) as session:
C#IAsyncDisposableawait using var session = await client.CreateSessionAsync(config);
Godeferdefer session.Disconnect()

Примечание.

Вместо PackageIconUrl теперь используется PackageIcon. Существующий код, использующий destroy() его, продолжит работать, но его следует перевести.

Постоянное удаление сессии (deleteSession)

Чтобы навсегда удалить сессию и все её данные с диска (история разговоров, состояние планирования, артефакты), используйте deleteSession. Это необратимо — сессия не может быть возобновлена после удаления:

// Permanently remove session data
await client.deleteSession("user-123-task-456");

disconnect() VS deleteSession():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`);
});

Шаблоны развертывания

Лучше всего для: сильной изоляции, многоарендных сред, динамических сессий 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), а не абсолютное количество токенов. Подробности смотрите в АВТОЗАГОЛОВКЕ .

Ограничения и рекомендации

LimitationDescriptionСмягчение последствий
Повторная аутентификация 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/ на постоянное хранилище

Дальнейшие действия