Cómo funcionan las sesiones
Al crear una sesión, la CLI de Copilot mantiene el historial de conversaciones, el estado de la herramienta y el contexto de planificación. De forma predeterminada, este estado reside en la memoria y desaparece cuando finaliza la sesión. Con la persistencia habilitada, puede reanudar las sesiones entre reinicios, migraciones de contenedor o incluso instancias de cliente diferentes.

| Estado | ¿Qué ocurre? |
|---|---|
| Crear | |
session_id asignado | |
| Activo | Enviar avisos, llamadas a herramientas, respuestas |
| En pausa | Estado guardado en el disco |
| Resume | Estado cargado desde el disco |
Inicio rápido: creación de una sesión reanudable
La clave para las sesiones reanudables es proporcionar tu propio session_id. Sin uno, el SDK genera un identificador aleatorio y la sesión no se puede reanudar más adelante.
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
Ir
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
Reanudación de una sesión
Más tarde —minutos, horas o incluso días después— podrá reanudar la sesión desde donde la dejó.

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?")
Ir
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?" });
Opciones de reanudación
Al reanudar una sesión, puede volver a configurar muchas opciones de configuración. Esto resulta útil cuando necesita cambiar el modelo, actualizar configuraciones de herramientas o modificar el comportamiento.
| Opción | Description |
|---|---|
model | Cambiar el modelo de la sesión reanudada |
systemMessage | Anular o ampliar el mensaje del sistema |
availableTools | Restringir qué herramientas están disponibles |
excludedTools | Deshabilitar herramientas específicas |
provider | Volver a proporcionar las credenciales BYOK (necesarias para las sesiones BYOK) |
reasoningEffort | Ajuste del nivel de esfuerzo de razonamiento |
streaming | Habilitar o deshabilitar las respuestas de streaming |
workingDirectory | Cambiar el directorio de trabajo |
configDir | Invalidar el directorio de configuración |
mcpServers | Configuración de servidores MCP |
customAgents | Configuración de agentes personalizados |
agent | Selección previa de un agente personalizado por nombre |
skillDirectories | Directorios desde los que cargar habilidades |
disabledSkills | Habilidades para desactivar |
infiniteSessions | Configuración del comportamiento infinito de la sesión |
Ejemplo: cambio de modelo en la reanudación
// 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
});
Uso de BYOK (traiga su propia clave) con sesiones reanudadas
Al usar sus propias claves de API, debe volver a proporcionar la configuración del proveedor al reanudar la sesión. Las claves de API nunca se conservan en el disco por motivos de seguridad.
// 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",
},
});
¿Qué se conserva?
El estado de sesión se guarda en ~/.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
| Data | ¿Se conserva? | Notas |
|---|---|---|
| El historial de conversaciones | ||
| ✅ Sí | Hilo de mensajes completo | |
| Resultados de la llamada a la herramienta | ||
| ✅ Sí | Almacenado en caché para el contexto | |
| Estado de planificación del agente | ||
| ✅ Sí | Archivo plan.md | |
| Artefactos de sesión | ||
| ✅ Sí | En files/ el directorio | |
| Claves de proveedor o API | ||
| ❌ No | Seguridad: debe proporcionarse nuevamente | |
| Estado de la herramienta en memoria | ||
| ❌ No | Las herramientas deben ser sin estado |
Procedimientos recomendados de identificador de sesión
Elija identificadores de sesión que codifiquen la propiedad y el propósito. Esto facilita mucho la auditoría y la limpieza.
| Pattern | Example | Caso de uso |
|---|---|---|
| ❌ | ||
abc123 | ||
| Identificadores aleatorios | Difícil de auditar, sin información de propiedad | |
| ✅ | ||
user-{userId}-{taskId} | ||
user-alice-pr-review-42 | Aplicaciones multiusuario | |
| ✅ | ||
tenant-{tenantId}-{workflow} | ||
tenant-acme-onboarding | SaaS multicliente | |
| ✅ | ||
{userId}-{taskId}-{timestamp} | ||
alice-deploy-1706932800 | Limpieza basada en tiempo |
Ventajas de los identificadores estructurados:
- Fácil de auditar: "Mostrar todas las sesiones para el usuario alice"
- Fácil de limpiar: "Eliminar todas las sesiones anteriores a X"
- Control de acceso automatizado: extraer el identificador de usuario desde el identificador de sesión
Ejemplo: generación de identificadores de sesión
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"
Administración del ciclo de vida de la sesión
Enumeración de sesiones activas
// 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" });
Limpieza de sesiones antiguas
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);
Desconexión de una sesión (disconnect)
Cuando se complete una tarea, desconecte de la sesión explícitamente en lugar de esperar tiempos de espera. Esto libera recursos en memoria, pero conserva los datos de sesión en el disco, por lo que la sesión todavía se puede reanudar más adelante:
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 también proporciona patrones de limpieza automática idiomáticos:
| Language | Pattern | Example |
|---|---|---|
| TypeScript | Symbol.asyncDispose | await using session = await client.createSession(config); |
| Python | ||
async with administrador de contexto | 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() |
Nota:
destroy() ha quedado obsoleto en favor de disconnect(). El código existente que usa destroy() seguirá funcionando, pero se debe migrar.
Eliminación permanente de una sesión (deleteSession)
Para quitar permanentemente una sesión y todos sus datos del disco (historial de conversaciones, estado de planeación, artefactos), use deleteSession. Esto es irreversible: la sesión no se puede reanudar después de la eliminación:
// Permanently remove session data
await client.deleteSession("user-123-task-456");
disconnect()vsdeleteSession():disconnect()libera recursos en memoria, pero mantiene los datos de sesión en el disco para la reanudación posterior.deleteSession()quita permanentemente todo, incluidos los archivos en el disco.
Limpieza automática: tiempo de espera por inactividad
De forma predeterminada, las sesiones no tienen tiempo de espera de inactividad y se encuentran indefinidamente hasta que se desconectan o eliminan explícitamente. Opcionalmente, puede configurar un tiempo de espera de inactividad de todo el servidor mediante CopilotClientOptions.sessionIdleTimeoutSeconds:
const client = new CopilotClient({
sessionIdleTimeoutSeconds: 30 * 60, // 30 minutes
});
Cuando se configura un tiempo de espera, las sesiones sin actividad durante esa duración se limpian automáticamente. Establézcalo en 0 o déjelo en blanco para deshabilitarlo.
Nota:
Esta opción solo se aplica cuando el SDK genera el proceso en tiempo de ejecución. Al conectarse a un servidor existente a través de cliUrl, se aplica la configuración de tiempo de espera del propio servidor.

Las sesiones con trabajo activo (comandos en ejecución, agentes en segundo plano) siempre están protegidas contra la eliminación por inactividad, independientemente del ajuste de tiempo de espera.
Escuche eventos inactivos para reaccionar a la inactividad de sesión:
session.on("session.idle", (event) => {
console.log(`Session idle for ${event.idleDurationMs}ms`);
});
Patrones de implementación
Patrón 1: un servidor de la CLI por usuario (recomendado)
Ideal para: Aislamiento seguro, entornos multiinquilino, Azure sesiones dinámicas.

**Ventajas:**✅ Aislamiento completo | ✅ Seguridad simple | ✅ Escalado sencillo
Patrón 2: servidor de la CLI compartido (eficiente para recursos)
Ideal para: Herramientas internas, entornos de confianza, configuraciones restringidas a recursos.

Requisitos:
- ⚠️ Identificadores de sesión únicos por usuario
- ⚠️ Control de acceso de nivel de aplicación
- ⚠️ Validación del identificador de sesión antes de las operaciones
// 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 sesiones dinámicas
En el caso de las implementaciones sin servidor o contenedor en las que los contenedores pueden reiniciar o migrar:
Montaje del almacenamiento persistente
El directorio de estado de sesión debe montarse en almacenamiento 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

¡La sesión sobrevive a los reinicios del contenedor!
Sesiones ilimitadas para flujos de trabajo de larga duración
En el caso de los flujos de trabajo que pueden superar los límites de contexto, habilite sesiones infinitas con compactación 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
},
});
Nota:
Los umbrales son relaciones de uso de contexto (0,0-1,0), no recuentos absolutos de tokens. Consulte autotitle para obtener más información.
Limitaciones y consideraciones
| Limitación | Description | Mitigación |
|---|---|---|
| Volver a autenticar BYOK | Las claves de API no se conservan | Almacene claves en su gestor de secretos; proporciónelas al reanudar |
| Almacenamiento escribible | ||
~/.copilot/session-state/ debe ser escribible | Monte volumen persistente en contenedores | |
| Sin bloqueo de sesión | El acceso simultáneo a la misma sesión no está definido | Implementar el bloqueo o la cola a nivel de aplicación |
| El estado de la herramienta no se conserva | Se pierde el estado de la herramienta en memoria | Diseñe herramientas para que sean sin estado o conserven su propio estado. |
Control del acceso simultáneo
El SDK no proporciona bloqueo de sesión integrado. Si varios clientes pueden acceder a la misma sesión:
// 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" });
});
Resumen
| Feature | Cómo se usa |
|---|---|
| Creación de una sesión reanudable | Proporcione su propio sessionId |
| Reanudar sesión | client.resumeSession(sessionId) |
| Reanudación de BYOK | Volver a proporcionar provider la configuración |
| Enumerar las sesiones | client.listSessions(filter?) |
| Desconexión de la sesión activa | |
session.disconnect()— libera recursos en memoria; se conservan los datos de sesión en el disco para la reanudación | |
| Eliminar sesión permanentemente | |
client.deleteSession(sessionId)— quita permanentemente todos los datos de sesión del disco; no se puede reanudar | |
| Implementación en contenedores | Montar ~/.copilot/session-state/ a almacenamiento persistente |
Pasos siguientes
- Session hooks - Personaliza el comportamiento de la sesión con hooks
- Compatibilidad del SDK y la CLI : comparación de características del SDK frente a la CLI
- Guía de depuración : Solución de problemas de sesión