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 编排层