Session switch/fork/import
AgentSessionRuntime est l'enveloppe extérieure d'AgentSession, responsable du « remplacement de session ». Dans un processus en cours, l'instance AgentSession peut être échangée plusieurs fois : /new ouvre une nouvelle session, /resume bascule sur une ancienne, /fork crée une branche depuis un message donné, /import importe un jsonl externe. Chaque remplacement implique de démonter l'ancienne session, de créer de nouveaux services, de re-lier les extensions et de restaurer les abonnements aux événements. Ce fichier est le conteneur de ce flux de remplacement.
Responsabilités
- Détention de la session courante : les champs
_sessionet_servicesstockent les instances vivantes, exposées via les getterssession/services/cwd. Voirpackages/coding-agent/src/core/agent-session-runtime.ts:67-97. - new / switch / fork / import : quatre méthodes de remplacement — elles émettent d'abord un événement before (que les extensions peuvent cancel), puis
teardownCurrent, puisapplydu nouveau runtime. Voirpackages/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-320etpackages/coding-agent/src/core/agent-session-runtime.ts:329-364. - Hooks d'événements : trois types d'événements d'extension —
session_before_switch/session_before_fork/session_shutdown— permettent aux extensions d'annuler le remplacement. Voirpackages/coding-agent/src/core/agent-session-runtime.ts:115-147. - Callback de rebind : l'host (
InteractiveModeou rpc-mode) enregistre viasetRebindSessionun callback appelé après le remplacement de la session, pour re-lier l'UI des extensions et ré-abonner les événements. Voirpackages/coding-agent/src/core/agent-session-runtime.ts:99-113etpackages/coding-agent/src/core/agent-session-runtime.ts:166-173. - Réutilisation de factory : la closure
createRuntimeest passée àcreateAgentSessionRuntime, puis réutilisée à chaque remplacement — garantit la cohérence des cwd/agentDir/chemins d'extensions. Voirpackages/coding-agent/src/core/agent-session-runtime.ts:382-400.
Motivation de design
Pourquoi ne pas faire directement this.session = new AgentSession(...) ? Parce que le remplacement touche à trois choses : l'ancienne session doit émettre un événement shutdown pour que les extensions nettoient leurs ressources ; la nouvelle session doit charger ses services depuis le même chemin (settings, auth, resource loader) ; l'UI doit se ré-abonner aux événements et re-lier le context des commandes d'extension. L'ordre de ces trois étapes est critique — les extensions doivent recevoir shutdown avant le dispose, sinon leurs références deviennent des pointeurs fous. La séquence en trois temps teardownCurrent → apply → finishSessionReplacement est la garantie dure de cet ordre.
La réutilisation de la factory createRuntime mérite aussi une explication : re-parser cwd, agentDir et les chemins d'extensions à chaque remplacement est lourd, et les paramètres dérivent. La closure capture la configuration résolue une fois au CLI, et les remplacements ultérieurs utilisent la même copie, garantissant qu'après plusieurs /resume le chemin d'extensions ne tombe pas soudainement en panne.
Fichiers clés
packages/coding-agent/src/core/agent-session-runtime.ts:67-97— champs et getters declass AgentSessionRuntime.packages/coding-agent/src/core/agent-session-runtime.ts:149-164—teardownCurrentetapply, les deux étapes centrales du remplacement.packages/coding-agent/src/core/agent-session-runtime.ts:166-173—finishSessionReplacement, déclenche le callback de rebind de l'host.packages/coding-agent/src/core/agent-session-runtime.ts:175-198—switchSession, pour resumer un jsonl existant.packages/coding-agent/src/core/agent-session-runtime.ts:200-232—newSession, supporte unparentSessionpour former un arbre de branches.packages/coding-agent/src/core/agent-session-runtime.ts:234-320—fork, deux sémantiques deposition:before/at.packages/coding-agent/src/core/agent-session-runtime.ts:329-364—importFromJsonl, copie un jsonl externe dans sessionDir puis switch.packages/coding-agent/src/core/agent-session-runtime.ts:382-400—createAgentSessionRuntime, entrée initiale de la factory runtime.
Le triptyque de remplacement :
// 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;
}Pour fork en position: "at", on prend directement l'entrée sélectionnée comme point de branche ; en before, on prend l'entrée parente, et on extrait le texte du message utilisateur pour pré-remplir l'éditeur :
// 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);
}Flux de données
Le flux unifié de remplacement de session :
Limites et échecs
- fork sur un message non utilisateur : en
position: "before", on ne peut forker qu'à partir d'un message utilisateur, sinon on throwInvalid entry ID for forking. Voirpackages/coding-agent/src/core/agent-session-runtime.ts:254-256. - Rollback en cas d'échec de fork : si
sourceManager.createBranchedSessionrenvoie null, on throwFailed to create forked session, mais l'ancienne session est déjà dispose — l'appelant doit gérer cette erreur « d'état intermédiaire ». Voirpackages/coding-agent/src/core/agent-session-runtime.ts:286-288. - Fichier d'import absent : après un check
existsSync, on throwSessionImportFileNotFoundError, pas d'échec silencieux. Voirpackages/coding-agent/src/core/agent-session-runtime.ts:330-333. - cwd d'import absent :
assertSessionCwdExistsvalide que le cwd du jsonl importé est toujours accessible ; en mode interactif, un prompt laisse l'utilisateur rechoisir. Voirpackages/coding-agent/src/core/agent-session-runtime.ts:351-352. - Ordre du dispose : la raison
quitpasse elle aussi parteardownCurrent, pour garantir que les extensions reçoivent l'événement shutdown à la sortie du processus.
Résumé
AgentSessionRuntime abstrait le remplacement de session en un triptyque unifié : teardown → apply → rebind. Les quatre entrées (new/switch/fork/import) partagent la même logique teardown et la même closure factory ; les extensions peuvent cancel via l'événement before. Pour les détails internes de la factory d'assemblage, voir Assemblage createAgentSession ; pour la session elle-même qui est remplacée, voir AgentSession : couche d'orchestration.