Skip to content

Session switch/fork/import

源码版本v0.73.1

AgentSessionRuntime ist der äußere Wrapper um AgentSession und zuständig für "Session-Austausch". In einem laufenden Prozess kann die AgentSession-Instanz mehrfach ausgetauscht werden: /new öffnet eine neue Session, /resume schaltet auf eine alte, /fork zweigt von einer Nachricht ab, /import importiert eine externe jsonl. Bei jedem Austausch muss die alte Session abgerissen, neue Services erzeugt, Extensions neu gebunden und Event-Subscriptions wiederhergestellt werden. Diese Datei ist der Container für diesen Austausch-Prozess.

Verantwortung

  1. Aktuelle Session halten: _session und _services speichern die aktuell lebenden Instanzen, über session/services/cwd-Getter freigegeben. Siehe packages/coding-agent/src/core/agent-session-runtime.ts:67-97.
  2. new / switch / fork / import: Vier Austausch-Methoden, senden zuerst ein before-Event (Extensions können abbrechen), dann teardownCurrent, dann apply der neuen Runtime. Siehe 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. Event-Hooks: Drei Extension-Events session_before_switch / session_before_fork / session_shutdown, Extensions können den Austausch abbrechen. Siehe packages/coding-agent/src/core/agent-session-runtime.ts:115-147.
  4. rebind-Callback: Der Host (InteractiveMode oder rpc-Modus) registriert über setRebindSession einen Callback, der nach dem Session-Austausch aufgerufen wird, um Extension-UI neu zu binden und Events neu zu abonnieren. Siehe packages/coding-agent/src/core/agent-session-runtime.ts:99-113, packages/coding-agent/src/core/agent-session-runtime.ts:166-173.
  5. Fabrik-Wiederverwendung: Das createRuntime-Closure wird beim createAgentSessionRuntime übergeben und bei jedem Austausch wiederverwendet, um cwd/agentDir/Extension-Pfade konsistent zu halten. Siehe packages/coding-agent/src/core/agent-session-runtime.ts:382-400.

Entwurfsmotivation

Warum nicht einfach this.session = new AgentSession(...)? Weil der Austausch drei Dinge berührt: Die alte Session muss ein shutdown-Event senden, damit Extensions Ressourcen freigeben; die neue Session muss Services aus demselben Pfad laden (settings, auth, resource loader); die UI-Schicht muss Events neu abonnieren und Extension-Befehlskontexte neu binden. Die Reihenfolge dieser drei ist kritisch - Extensions müssen shutdown empfangen, bevor sie disposed werden, sonst werden ihre Referenzen zu wilden Pointern. Die dreiteilige Sequenz teardownCurrentapplyfinishSessionReplacement ist die harte Garantie für diese Reihenfolge.

Die Wiederverwendung der createRuntime-Fabrik ist auch erwähnenswert: Bei jedem Austausch cwd, agentDir und Extension-Pfade neu aufzulösen wäre zu schwer und die Parameter würden driften. Das Closure fängt die einmal vom CLI geparste Konfiguration ein, spätere Austausche verwenden dieselbe, damit nach mehreren /resume die Extension-Pfade nicht plötzlich ungültig werden.

Wichtige Dateien

Die dreiteilige Austausch-Sequenz:

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 nimmt bei position: "at" den ausgewählten Eintrag direkt als Verzweigungspunkt, bei before den Eltern-Eintrag und extrahiert den User-Nachricht-Text zum Zurückfüllen in den 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);
}

Datenfluss

Der einheitliche Ablauf eines Session-Austauschs:

Grenzen und Fehler

Zusammenfassung

AgentSessionRuntime abstrahiert den Session-Austausch als einheitliche dreiteilige Sequenz: teardown → apply → rebind. Die vier Eingänge (new/switch/fork/import) teilen sich denselben teardown und dasselbe Fabrik-Closure, Extensions können über before-Events abbrechen. Wie die Zusammenbau-Fabrik intern arbeitet, siehe createAgentSession Zusammenbau; die ausgetauschte Session selbst siehe AgentSession Orchestrierungsschicht.