Como funcionam as sessões
Quando você cria uma sessão, a CLI do Copilot mantém o histórico da conversa, o estado da ferramenta e o contexto de planejamento. Por padrão, esse estado reside na memória e desaparece quando a sessão termina. Com a persistência habilitada, você pode retomar sessões entre reinicializações, migrações de contêiner ou até mesmo instâncias de cliente diferentes.

| State | O que acontece |
|---|---|
| Create | session_id Atribuído |
| Ativo | Enviar prompts, chamadas de ferramenta, respostas |
| Pausado | Estado salvo em disco |
| Resume | Estado carregado do disco |
Início rápido: criando uma sessão retomável
A chave para sessões que podem ser retomadas é fornecer sua própria session_id. Sem um, o SDK gera uma ID aleatória e a sessão não pode ser retomada mais tarde.
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
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
Retomando uma sessão
Mais tarde — minutos, horas ou até mesmo dias — você pode retomar a sessão de onde parou.

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
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)
// 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?" });
Opções de retomada
Ao retomar uma sessão, opcionalmente, você pode reconfigurar muitas configurações. Isso é útil quando você precisa alterar o modelo, atualizar as configurações da ferramenta ou modificar o comportamento.
| Opção | Description |
|---|---|
model | Alterar o modelo da sessão retomada |
systemMessage | Substituir ou estender o prompt do sistema |
availableTools | Restringir quais ferramentas estão disponíveis |
excludedTools | Desabilitar ferramentas específicas |
provider | Fornecer novamente as credenciais BYOK (necessárias para sessões BYOK) |
capi.autoTier | Sobrescrever a preferência persistente de roteamento Auto |
capi.enable | Escolha o transporte da API Responses para a sessão retomada |
reasoningEffort | Ajustar o nível de esforço de raciocínio |
streaming | Habilitar/desabilitar respostas de streaming |
workingDirectory | Alterar o diretório de trabalho |
configDir | Sobrescrever diretório de configuração |
mcpServers | Configurar servidores MCP |
customAgents | Configurar agentes personalizados |
agent | Pré-selecionar um agente personalizado pelo nome |
skillDirectories | Diretórios para carregar habilidades de |
disabledSkills | Funções a desativar |
infiniteSessions | Configurar o comportamento de sessão infinita |
Persistência da camada automática
Commodel: "auto", a configuração opcional capi.autoTier seleciona uma preferência de roteamento automático: efficiency, , balanceou intelligence``fast. Em Python, use capi={"auto_tier": "balance"}. Essa configuração se aplica ao roteamento automático V2; As solicitações automáticas V1 não são alteradas.
fast é uma predefinição de latência exclusiva para integradores, não uma preferência nativa de produto do GitHub Copilot. O SDK não determina a elegibilidade ao Fast, não inspeciona a identidade do cliente, não o escolhe como padrão nem recorre a outra camada quando um ambiente de execução não oferece suporte a ele — um ambiente de execução mais antigo retorna o erro nativo inalterado.
O ambiente de execução mantém o nível selecionado, portanto os aplicativos não precisam reenviá-lo a cada retomada:
- Omitir a camada ao criar uma sessão usa o comportamento de roteamento padrão do runtime.
- Uma retomada a frio restaura a camada persistente. Fornecer uma camada explícita substitui o valor restaurado para a nova ativação.
- Ao retomar uma sessão já residente no runtime, omitir a camada preserva a seleção atual e fornecer a mesma camada é um no-op. Fornecer um nível diferente solicita uma troca segura que o runtime aplica após a retomada ser bem-sucedida; ele não pode alterar uma interação que já está em andamento.
- Sessões mais antigas sem uma camada persistente mantêm o comportamento de roteamento padrão.
A seleção de nível não é uma operação de troca de modelo em tempo real. O SDK encaminha a preferência; o runtime possui persistência e validação.
Os eventos session.resume e session.start expõem a camada selecionada no campo opcional data.autoTier (data.auto_tier em Python). Quando nenhuma camada é selecionada, o campo é omitido.
Alterando o nível Auto durante uma sessão
Chame setAutoTier para alterar a preferência de roteamento em uma sessão ao vivo sem alterar o modelo selecionado. Passe null (PythonNone, Gonil) para retornar ao roteamento automático padrão do provedor.
const result = await session.setAutoTier("intelligence");
if (result.status === "pending") {
// Accepted, but not yet in effect.
}
O runtime não aplica a preferência imediatamente. Ele registra a solicitação e só a efetiva quando, em uma interação posterior do usuário usando o modelo auto, obtém com êxito um modelo utilizável do provedor. Um status pending, portanto, confirma que a solicitação foi aceita, não que tenha entrado em vigor. Apenas a solicitação mais recente é mantida: uma nova solicitação substitui qualquer solicitação anterior que ainda não tenha sido assumida por nenhuma iteração.
Observe o resultado por meio destes eventos:
session.model_changequando a preferência é confirmada.session.auto_tier_switch_failedquando isso não acontece. Esse evento é efêmero, portanto o runtime nunca o persiste nem o reproduz ao retomar. Seureasoncampo é um depolicy_rejected,request_failed,setup_failedouunsupported, e a preferência anteriormente efetiva permanece ativa.
Você também pode ler o estado autoritativo a qualquer momento por meio do método RPC da sessão model.getCurrent, que informa o autoTier confirmado, qualquer pendingAutoTier não reivindicado e o activatingAutoTier atualmente reivindicado por uma ativação em andamento.
| SDK | Alterar o nível | Retornar ao roteamento padrão do provedor |
|---|---|---|
| Node.js | session.set | session.set |
| Python | session.set_auto_tier("balance") | session.set_auto_tier(None) |
| Go | session.Set | session.Set |
| .NET | session.Set | session.Set |
| Rust | session.set_auto_tier(Some(Auto | session.set_auto_tier(None) |
| Java | session.set | session.set |
Para selecionar o modelo auto e sua preferência de roteamento em uma única chamada, em vez disso, defina o nível no seletor de modelo. O runtime rejeita essa opção quando o modelo é diferente de auto.
| SDK | Prepare um nível usando o seletor | Redefinir para roteamento padrão do provedor |
|---|---|---|
| Node.js | setModel("auto", { autoTier: "balance" }) | setModel("auto", { autoTier: null }) |
| Python | set_model("auto", auto_tier="balance") | set_model("auto", auto_tier=None) |
| Go | Set | Set |
| .NET | new SetModelOptions { AutoTier = AutoTier.Balance } | new SetModelOptions { ResetAutoTier = true } |
| Rust | Set | Set |
| Java | new Set | new Set |
Node.js, Python e Rust expressam os três estados em um único valor: Node.js e Python porque null/None é distinguível de um argumento omitido e Rust porque AutoTierPreference::Reset é uma variante distinta da mesma opção. Go, .NET e Java não têm como distinguir entre "resetado" e "não definido" em um mesmo valor, por isso usam um sinalizador separado de reset. Omitir ambos sempre significa "deixar a preferência atual em paz".
Transporte de respostas ao retomar
A configuração opcional capi.enableWebSocketResponses escolhe o transporte para a API de Respostas CAPI. O padrão é true, de modo que o transporte WebSocket seja usado sempre que o modelo selecionado disponibilizar o endpoint ws:/responses. Ao defini-lo como false, o sistema volta a usar o transporte HTTP. Em Python, use capi={"enable_web_socket_responses": False}.
Forneça isso na chamada de retomada quando necessário. Vale a pena configurar isso quando as conexões WebSocket falham ao passar por um proxy e quando uma sessão retomada indica 400 input item ID does not belong to this connection, o que é específico do transporte WebSocket.
const session = await client.resumeSession("user-123-task-456", {
capi: { enableWebSocketResponses: false },
});
Definir isso como false é equivalente à variável de ambiente COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES, que tem a polaridade oposta.
Exemplo: alterar o modelo no currículo
// 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
});
Usando BYOK (traga sua própria chave) com sessões retomadas
Ao usar suas próprias chaves de API, você deve fornecer novamente a configuração do provedor ao retomar. As chaves de API nunca são mantidas no disco por motivos de segurança.
// 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",
},
});
O que é persistente?
O estado da sessão é salvo em ~/.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
| Dados | Persistiu? | Notes |
|---|---|---|
| Histórico de conversas | ||
| ✅ Sim | Thread de mensagem completo | |
| Resultados da chamada de ferramenta | ||
| ✅ Sim | Armazenado em cache para contexto | |
| Estado de planejamento do agente | ||
| ✅ Sim | Arquivo plan.md | |
| Artefatos de sessão | ||
| ✅ Sim | No files/ diretório | |
| Chaves de provedor/API | ||
| ❌ Não | Segurança: deve ser fornecida novamente | |
| Estado da ferramenta na memória | ||
| ❌ Não | As ferramentas devem ser sem estado |
Práticas recomendadas de ID de sessão
Escolha IDs de sessão que codificam a propriedade e a finalidade. Isso facilita muito a auditoria e a limpeza.
| Pattern | Example | Caso de uso |
|---|---|---|
❌abc123 | IDs aleatórias | Difícil de auditar, sem informações de propriedade |
✅user-{user | user-alice-pr-review-42 | Aplicativos multiusuários |
✅tenant-{tenant | tenant-acme-onboarding | SaaS multilocatário |
✅{user | alice-deploy-1706932800 | Limpeza baseada em tempo |
Benefícios das IDs estruturadas:
- Fácil de auditar: "Mostrar todas as sessões para o usuário alice"
- Fácil de limpar: "Excluir todas as sessões mais antigas que X"
- Controle de acesso integrado: analisar a ID do usuário a partir da ID da sessão
Exemplo: gerando IDs de sessão
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"
Gerenciando o ciclo de vida da sessão
Listando sessões ativas
// 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" });
Limpar sessões antigas
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);
Desconectando de uma sessão (disconnect)
Quando uma tarefa é concluída, desconecte-se da sessão explicitamente em vez de aguardar o time-out. Isso libera recursos na memória, mas preserva os dados de sessão no disco, portanto, a sessão ainda pode ser retomada mais tarde:
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;
}
Cada SDK também fornece padrões de limpeza automática idiomática:
| Linguagem | Pattern | Example |
|---|---|---|
| TypeScript | Symbol.async | await using session = await client.create |
| Python | async with gerenciador de contexto | async with await client.create_session(on_ |
| C# | IAsyncDisposable | await using var session = await client.Create |
| Go | defer | defer session.Disconnect() |
Observação
destroy() foi descontinuado em favor de disconnect(). O código existente que usa destroy() continuará funcionando, mas deve ser migrado.
Excluindo permanentemente uma sessão (deleteSession)
Para remover permanentemente uma sessão e todos os seus dados do disco (histórico de conversa, estado de planejamento, artefatos), use deleteSession. Isso é irreversível: a sessão não pode ser retomada após a exclusão:
// Permanently remove session data
await client.deleteSession("user-123-task-456");
disconnect()vsdeleteSession():disconnect()libera recursos na memória, mas mantém os dados de sessão no disco para retomada posterior.deleteSession()remove permanentemente tudo, incluindo arquivos em disco.
Limpeza automática: tempo de inatividade
Por padrão, as sessões não têm tempo limite ocioso e vivem indefinidamente até serem explicitamente desconectadas ou excluídas. Opcionalmente, você pode configurar um tempo limite de inatividade para todo o servidor por meio de CopilotClientOptions.sessionIdleTimeoutSeconds:
const client = new CopilotClient({
sessionIdleTimeoutSeconds: 30 * 60, // 30 minutes
});
Quando um tempo limite é configurado, as sessões sem atividade para essa duração são limpas automaticamente. Defina como 0 ou omita para desabilitar.
Observação
Essa opção só se aplica quando o SDK gera o processo de runtime. Ao se conectar a um servidor existente por meio de cliUrl, aplica-se a configuração de tempo limite do próprio servidor.

Sessões com trabalho ativo (comandos em execução, agentes em segundo plano) são sempre protegidas contra limpeza ociosa, independentemente da configuração de tempo limite.
Detecte eventos de inatividade para reagir à inatividade da sessão:
session.on("session.idle", (event) => {
console.log(`Session idle for ${event.idleDurationMs}ms`);
});
Padrões de implantação
Padrão 1: um servidor da CLI por usuário (recomendado)
Melhor para: isolamento forte, ambientes multilocatários e Sessões Dinâmicas do Azure.

**Benefícios:**✅ Isolamento completo | ✅ Segurança simples | ✅ Dimensionamento fácil
Padrão 2: servidor da CLI compartilhada (eficiente em recursos)
Melhor para: ferramentas internas, ambientes confiáveis, configurações restritas a recursos.

Requisitos:
- ⚠️ IDs de sessão exclusivas por usuário
- ⚠️ Controle de acesso no nível do aplicativo
- ⚠️ Validação da ID da sessão antes das operações
// 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 sessões dinâmicas
Para implantações sem servidor/contêiner em que os contêineres podem reiniciar ou migrar:
Montar armazenamento persistente
O diretório de estado da sessão deve ser montado no armazenamento persistente:
# 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

A sessão sobrevive às reinicializações de contêiner!
Sessões infinitas para fluxos de trabalho de execução longa
Para fluxos de trabalho que podem exceder os limites de contexto, habilite sessões infinitas com compactação automática:
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
},
});
Observação
Os limites são taxas de utilização de contexto (0,0-1,0), não contagens absolutas de token. Consulte o Compatibilidade do SDK e da CLI para obter detalhes.
Limitações e considerações
| Limitation | Description | Atenuação |
|---|---|---|
| Autenticação de BYOK | As chaves de API não são mantidas | Armazene chaves no gerenciador de segredos; fornecer ao retomar |
| Armazenamento gravável | ~/ deve ser gravável | Montar volume persistente em contêineres |
| Nenhum bloqueio de sessão | O acesso simultâneo à mesma sessão é indefinido | Implementar bloqueio ou fila no nível do aplicativo |
| O estado da ferramenta não persistiu | O estado da ferramenta na memória é perdido | Ferramentas de design para serem sem estado ou persistirem seu próprio estado |
Manipulando o acesso simultâneo
O SDK não fornece bloqueio de sessão interno. Se vários clientes puderem acessar a mesma sessão:
// 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" });
});
Resumo
| Característica | Como usar |
|---|---|
| Criar sessão retomável | Forneça seu próprio sessionId |
| Retomar sessão | client.resume |
| Resumo BYOK | Fornecer novamente a configuração provider |
| Listar sessões | client.list |
| Desconectar-se da sessão ativa | session.disconnect()— libera recursos na memória; os dados de sessão no disco são preservados para retomada |
| Excluir sessão permanentemente | client.delete— remove permanentemente todos os dados de sessão do disco; não pode ser retomado |
| Implantação em contêineres | Montar ~/ no armazenamento persistente |
Próximas Etapas
- Ganchos de sessão – Personalizar o comportamento da sessão com ganchos
- Compatibilidade do SDK e da CLI – Comparação de recursos do SDK vs CLI
- Guia de depuração – Solucionar problemas de sessão