Skip to content

會話 switch/fork/import

源码版本v0.73.1

AgentSessionRuntimeAgentSession 的外層包裝器,負責「會話替換」。一個執行中的行程裡,AgentSession 實例可能被換掉多次:/new 開新會話、/resume 切到舊會話、/fork 從某條訊息分岔、/import 匯入外部 jsonl。每次替換都要拆掉舊 session、建立新服務、重新綁擴充、恢復事件訂閱。這個檔案就是這套替換流程的容器。

職責

  1. 持有當前 session:_session_services 欄位存當前活的實例,透過 session/services/cwd getter 暴露。見 packages/coding-agent/src/core/agent-session-runtime.ts:67-97
  2. new / switch / fork / import:四個替換方法,先發 before 事件(擴充可 cancel),再 teardownCurrent,再 apply 新 runtime。見 packages/coding-agent/src/core/agent-session-runtime.ts:175-198packages/coding-agent/src/core/agent-session-runtime.ts:200-232packages/coding-agent/src/core/agent-session-runtime.ts:234-320packages/coding-agent/src/core/agent-session-runtime.ts:329-364
  3. 事件鉤子:session_before_switch / session_before_fork / session_shutdown 三類擴充事件,擴充可 cancel 替換。見 packages/coding-agent/src/core/agent-session-runtime.ts:115-147
  4. rebind 回呼:host(InteractiveMode 或 rpc-mode)透過 setRebindSession 註冊一個在 session 替換後呼叫的回呼,用來重新綁擴充 UI、重訂閱事件。見 packages/coding-agent/src/core/agent-session-runtime.ts:99-113packages/coding-agent/src/core/agent-session-runtime.ts:166-173
  5. 工廠重用:createRuntime 閉包在 createAgentSessionRuntime 時傳入,之後每次替換都重用同一個工廠,保證 cwd/agentDir/擴充路徑一致。見 packages/coding-agent/src/core/agent-session-runtime.ts:382-400

設計動機

為什麼不直接 this.session = new AgentSession(...)?因為替換涉及三件事:舊 session 要發 shutdown 事件讓擴充清理資源;新 session 要從同一路徑載入服務(settings、auth、resource loader);UI 層要重新訂閱事件、重綁擴充命令上下文。這三件事的順序很關鍵——擴充先收到 shutdown 再 dispose,否則擴充持有的參照會變成野指標。teardownCurrentapplyfinishSessionReplacement 的三段式是這個順序的硬保證。

createRuntime 工廠重用也值得說:每次替換都重新解析一遍 cwdagentDir、擴充路徑太重,而且參數會漂移。閉包捕獲 CLI 一次解析出的設定,後續替換都用同一份,保證多次 /resume 後擴充路徑不會忽然失效。

關鍵檔案

替換三段式:

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;
}

forkposition: "at" 時直接拿選中條目作分岔點,before 時拿父條目,並提取使用者訊息文字回填到編輯器:

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);
}

資料流

session 替換的統一流程:

邊界與失敗

小結

AgentSessionRuntime 把會話替換抽象成統一三段式:teardown → apply → rebind。四個入口(new/switch/fork/import)共用同一套 teardown 和工廠閉包,擴充透過 before 事件可 cancel。組裝工廠內部細節看 createAgentSession 組裝,被替換的 session 本身看 AgentSession 編排層