Session switch/fork/import
AgentSessionRuntime is the outer wrapper around AgentSession, responsible for session replacement. Within a running process, the AgentSession instance can be swapped out multiple times: /new opens a new session, /resume switches to an old one, /fork branches off a message, /import brings in an external jsonl. Each replacement has to tear down the old session, create new services, rebind extensions, and restore event subscriptions. This file is the container for that replacement flow.
Responsibilities
- Hold the current session: the
_sessionand_servicesfields store the live instances, exposed viasession/services/cwdgetters. Seepackages/coding-agent/src/core/agent-session-runtime.ts:67-97. - new / switch / fork / import: four replacement methods. Each emits a before-event (extensions can cancel), then
teardownCurrent, thenapplyof the new runtime. Seepackages/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, andpackages/coding-agent/src/core/agent-session-runtime.ts:329-364. - Event hooks: three extension events —
session_before_switch/session_before_fork/session_shutdown— that extensions can cancel. Seepackages/coding-agent/src/core/agent-session-runtime.ts:115-147. - Rebind callback: the host (InteractiveMode or rpc-mode) registers a
setRebindSessioncallback that runs after session replacement to rebind extension UI and resubscribe to events. Seepackages/coding-agent/src/core/agent-session-runtime.ts:99-113andpackages/coding-agent/src/core/agent-session-runtime.ts:166-173. - Factory reuse: the
createRuntimeclosure is passed in whencreateAgentSessionRuntimeruns and reused on every subsequent replacement, so cwd/agentDir/extension paths stay consistent. Seepackages/coding-agent/src/core/agent-session-runtime.ts:382-400.
Design Motivation
Why not just this.session = new AgentSession(...)? Because replacement involves three things: the old session has to emit a shutdown event so extensions can release resources; the new session has to load services from the same paths (settings, auth, resource loader); and the UI layer has to resubscribe to events and rebind extension command context. The order matters — extensions must receive shutdown before dispose, or they hold dangling references. The teardownCurrent → apply → finishSessionReplacement three-stage flow is a hard guarantee of that order.
The createRuntime factory reuse is also worth noting: re-resolving cwd, agentDir, and extension paths on every replacement is heavy and lets parameters drift. The closure captures the config parsed once at CLI time and reuses it, so extension paths don't suddenly go invalid after several /resumes.
Key Files
packages/coding-agent/src/core/agent-session-runtime.ts:67-97—class AgentSessionRuntimefields and getters.packages/coding-agent/src/core/agent-session-runtime.ts:149-164—teardownCurrentandapply, the two core steps of replacement.packages/coding-agent/src/core/agent-session-runtime.ts:166-173—finishSessionReplacement, triggers the host's rebind callback.packages/coding-agent/src/core/agent-session-runtime.ts:175-198—switchSession, resumes an existing jsonl.packages/coding-agent/src/core/agent-session-runtime.ts:200-232—newSession, supportsparentSessionto form a branch tree.packages/coding-agent/src/core/agent-session-runtime.ts:234-320—fork, withbefore/atposition semantics.packages/coding-agent/src/core/agent-session-runtime.ts:329-364—importFromJsonl, copies an external jsonl into sessionDir then switches.packages/coding-agent/src/core/agent-session-runtime.ts:382-400—createAgentSessionRuntime, the initial runtime factory entry.
The three-stage replacement:
// 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;
}When position: "at", fork takes the selected entry as the branch point; for before, it takes the parent entry and extracts the user message text to refill the 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);
}Data Flow
The unified flow for session replacement:
Edges and Failures
- Fork on non-user message: with
position: "before", forking is only allowed from a user message; otherwise it throwsInvalid entry ID for forking. Seepackages/coding-agent/src/core/agent-session-runtime.ts:254-256. - Fork failure rollback: when
sourceManager.createBranchedSessionreturns null, it throwsFailed to create forked session, but at that point the old session is already disposed — the caller has to handle this mid-state error. Seepackages/coding-agent/src/core/agent-session-runtime.ts:286-288. - Import file missing: after
existsSynccheck, throwsSessionImportFileNotFoundErrorinstead of failing silently. Seepackages/coding-agent/src/core/agent-session-runtime.ts:330-333. - Import cwd missing:
assertSessionCwdExistsvalidates that the imported jsonl's cwd is still accessible; in interactive mode it prompts the user to reselect. Seepackages/coding-agent/src/core/agent-session-runtime.ts:351-352. - Dispose order: the
quitreason also goes throughteardownCurrent, so extensions receive the shutdown event on process exit.
Summary
AgentSessionRuntime abstracts session replacement into a unified three-stage flow: teardown → apply → rebind. The four entry points (new/switch/fork/import) share the same teardown and factory closure; extensions can cancel via before-events. For the assembly factory internals, see createAgentSession Wiring; for the session being replaced, see AgentSession Orchestration Layer.