Skip to content

Sesión switch/fork/import

源码版本v0.73.1

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

  1. Mantener la sesión actual: los campos _session y _services guardan las instancias vivas; se exponen vía getters session/services/cwd. Ver packages/coding-agent/src/core/agent-session-runtime.ts:67-97.
  2. new / switch / fork / import: cuatro métodos de reemplazo; primero emiten un evento before (la extensión puede cancelar), luego teardownCurrent, luego apply del nuevo runtime. Ver packages/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.
  3. Hooks de eventos: tres tipos de eventos de extensión session_before_switch / session_before_fork / session_shutdown; las extensiones pueden cancelar el reemplazo. Ver packages/coding-agent/src/core/agent-session-runtime.ts:115-147.
  4. Callback de rebind: el host (InteractiveMode o rpc-mode) registra con setRebindSession un callback que se invoca tras el reemplazo, para reenganchar la UI de extensiones y volver a suscribirse a eventos. Ver packages/coding-agent/src/core/agent-session-runtime.ts:99-113 y packages/coding-agent/src/core/agent-session-runtime.ts:166-173.
  5. Reutilización de fábrica: el closure createRuntime se pasa al construir createAgentSessionRuntime, y los reemplazos posteriores reutilizan la misma fábrica, garantizando que cwd/agentDir/rutas de extensión sean consistentes. Ver packages/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 teardownCurrentapplyfinishSessionReplacement 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

El tres-pasos de reemplazo:

typescript
// 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:

typescript
// 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

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.