Sesión switch/fork/import
AgentSessionRuntime es la envoltura exterior de AgentSession, responsable del "reemplazo de sesión". Dentro de un proceso en marcha, la instancia de AgentSession puede reemplazarse varias veces: /new abre sesión nueva, /resume cambia a una sesión vieja, /fork bifurca desde un mensaje, /import importa un jsonl externo. Cada reemplazo exige desmontar la sesión vieja, crear servicios nuevos, reenganchar extensiones y restaurar la suscripción a eventos. Este archivo es el contenedor de ese flujo de reemplazo.
Responsabilidades
- Mantener la sesión actual: los campos
_sessiony_servicesguardan las instancias vivas; se exponen vía getterssession/services/cwd. Verpackages/coding-agent/src/core/agent-session-runtime.ts:67-97. - new / switch / fork / import: cuatro métodos de reemplazo; primero emiten un evento before (la extensión puede cancelar), luego
teardownCurrent, luegoapplydel nuevo runtime. Verpackages/coding-agent/src/core/agent-session-runtime.ts:175-198,packages/coding-agent/src/core/agent-session-runtime.ts:200-232,packages/coding-agent/src/core/agent-session-runtime.ts:234-320,packages/coding-agent/src/core/agent-session-runtime.ts:329-364. - Hooks de eventos: tres tipos de eventos de extensión
session_before_switch/session_before_fork/session_shutdown; las extensiones pueden cancelar el reemplazo. Verpackages/coding-agent/src/core/agent-session-runtime.ts:115-147. - Callback de rebind: el host (InteractiveMode o rpc-mode) registra con
setRebindSessionun callback que se invoca tras el reemplazo, para reenganchar la UI de extensiones y volver a suscribirse a eventos. Verpackages/coding-agent/src/core/agent-session-runtime.ts:99-113ypackages/coding-agent/src/core/agent-session-runtime.ts:166-173. - Reutilización de fábrica: el closure
createRuntimese pasa al construircreateAgentSessionRuntime, y los reemplazos posteriores reutilizan la misma fábrica, garantizando que cwd/agentDir/rutas de extensión sean consistentes. Verpackages/coding-agent/src/core/agent-session-runtime.ts:382-400.
Motivación de diseño
¿Por qué no this.session = new AgentSession(...) directamente? Porque el reemplazo involucra tres cosas: la sesión vieja debe emitir shutdown para que las extensiones liberen recursos; la sesión nueva debe cargar servicios desde la misma ruta (settings, auth, resource loader); la UI debe volver a suscribirse a eventos y reenganchar el contexto de comandos de extensión. El orden de las tres importa: la extensión recibe shutdown antes de dispose, si no las referencias que mantiene se vuelven punteros colgantes. El tres-pasos teardownCurrent → apply → finishSessionReplacement garantiza ese orden.
La reutilización de la fábrica createRuntime también merece comentario: re-parsed cwd, agentDir y rutas de extensión en cada reemplazo sería caro y los parámetros derivarían. El closure captura la configuración parseada por el CLI una vez, y los reemplazos posteriores usan la misma copia, garantizando que tras varios /resume las rutas de extensión no se vuelvan inválidas de golpe.
Archivos clave
packages/coding-agent/src/core/agent-session-runtime.ts:67-97— Campos y getters declass AgentSessionRuntime.packages/coding-agent/src/core/agent-session-runtime.ts:149-164—teardownCurrentyapply, los dos pasos núcleo del reemplazo.packages/coding-agent/src/core/agent-session-runtime.ts:166-173—finishSessionReplacement, dispara el callback rebind del host.packages/coding-agent/src/core/agent-session-runtime.ts:175-198—switchSession, retoma un jsonl existente.packages/coding-agent/src/core/agent-session-runtime.ts:200-232—newSession, admiteparentSessionpara formar un árbol de bifurcaciones.packages/coding-agent/src/core/agent-session-runtime.ts:234-320—fork, semánticabefore/atsegún position.packages/coding-agent/src/core/agent-session-runtime.ts:329-364—importFromJsonl, copia un jsonl externo al sessionDir y luego switch.packages/coding-agent/src/core/agent-session-runtime.ts:382-400—createAgentSessionRuntime, entrada inicial de la fábrica de runtime.
El tres-pasos de reemplazo:
// packages/coding-agent/src/core/agent-session-runtime.ts:149-164
private async teardownCurrent(reason: SessionShutdownEvent["reason"], targetSessionFile?: string): Promise<void> {
await emitSessionShutdownEvent(this.session.extensionRunner, {
type: "session_shutdown",
reason,
targetSessionFile,
});
this.beforeSessionInvalidate?.();
this.session.dispose();
}
private apply(result: CreateAgentSessionRuntimeResult): void {
this._session = result.session;
this._services = result.services;
this._diagnostics = result.diagnostics;
this._modelFallbackMessage = result.modelFallbackMessage;
}fork con position: "at" toma la entrada seleccionada como punto de bifurcación; con before toma la entrada padre y extrae el texto del mensaje de usuario para rellenar el editor:
// packages/coding-agent/src/core/agent-session-runtime.ts:251-258
if (position === "at") {
targetLeafId = selectedEntry.id;
} else {
if (selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
throw new Error("Invalid entry ID for forking");
}
targetLeafId = selectedEntry.parentId;
selectedText = extractUserMessageText(selectedEntry.message.content);
}Flujo de datos
Flujo unificado de reemplazo de sesión:
Límites y fallos
- Fork sobre entrada no usuario: con
position: "before"sólo se puede bifurcar desde un mensaje de usuario; si no, lanzaInvalid entry ID for forking. Verpackages/coding-agent/src/core/agent-session-runtime.ts:254-256. - Rollback ante fallo de fork: si
sourceManager.createBranchedSessiondevuelve null, lanzaFailed to create forked session; pero la sesión vieja ya está disposed, el llamador debe gestionar este error de "estado intermedio". Verpackages/coding-agent/src/core/agent-session-runtime.ts:286-288. - Import con archivo inexistente:
existsSyncchequea y lanzaSessionImportFileNotFoundError, sin fallo silencioso. Verpackages/coding-agent/src/core/agent-session-runtime.ts:330-333. - Cwd de import ausente:
assertSessionCwdExistsvalida que el cwd del jsonl importado siga accesible; interactive abre un prompt para re-elegir. Verpackages/coding-agent/src/core/agent-session-runtime.ts:351-352. - Orden de dispose: el reason
quittambién pasa porteardownCurrent, garantizando que las extensiones reciban shutdown al salir del proceso.
Resumen
AgentSessionRuntime abstrae el reemplazo de sesión en un tres-pasos unificado: teardown → apply → rebind. Las cuatro entradas (new/switch/fork/import) comparten el mismo teardown y el mismo closure de fábrica; las extensiones pueden cancelar vía evento before. Los detalles internos de la fábrica de ensamblaje en ensamblaje createAgentSession; la sesión que se reemplaza en capa de orquestación AgentSession.