
1. 一句话结论
AgentSession 是 interactive、print、rpc 三种运行模式共享的"单会话编排器"。它持有并驱动一个 Agent,把 Agent 的底层事件转换为三类结果:
- 写入
SessionManager的持久化条目; - 分发给扩展的
ExtensionRunner事件; - 分发给宿主 UI 的
AgentSessionEvent。
在此之上,它负责一次 prompt 从准备、执行、重试、压缩到结算的完整生命周期,并管理队列、工具装载、模型/thinking、bash 执行、树导航和会话统计。
核心设计取向是:会话树投影(session projection)是模型上下文的权威来源 。Agent 的 state.messages 只是当前投影的一份可执行副本,AgentSession 在压缩、上下文编辑、边界写入、树导航、自定义消息追加等位置主动用投影刷新它。
2. 在整体架构中的位置
┌──────────────────────────────────────────────────────────────┐
│ InteractiveMode / PrintMode / RpcMode / SDK 调用方 │
│ - prompt / steer / followUp / sendCustomMessage │
│ - subscribe(AgentSessionEvent) │
│ - bindExtensions(...) │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ AgentSession │
│ - 运行状态、队列、压缩、重试、工具注册、扩展绑定 │
│ - 事件转换、持久化、投影刷新 │
└───────┬──────────────────────┬──────────────────────┬────────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Agent │ │ SessionManager │ │ ExtensionRunner │
│ - 状态/循环 │ │ - JSONL 条目 │ │ - 事件/命令/工具 │
│ - 事件流 │ │ - 树/投影 │ │ - UI/资源 │
└───────────────┘ └──────────────────┘ └──────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ModelRuntime / SettingsManager / ResourceLoader / CacheWarmer │
└──────────────────────────────────────────────────────────────┘
AgentSession 不创建 Agent,而是接收已经构造好的 Agent(sdk.ts:411)。创建顺序是:
createAgentSession()
→ new Agent({ initialState, convertToLlm, streamFn, ... }) sdk.ts:411
→ 恢复/写入 model_change、thinking_level_change sdk.ts:448-459
→ new AgentSession({ agent, sessionManager, ... }) sdk.ts:461
AgentSessionRuntime 则负责在 /new、/resume、/fork、/import 时销毁旧 AgentSession 并重建新的;它不参与单会话内部逻辑。两者关系见 AGENT_SESSION_RUNTIME_ANALYSIS.md。
3. 公开契约与状态模型
3.1 构造配置
AgentSessionConfig(agent-session.ts:252-300)的关键字段:
| 字段 | 作用 |
|---|---|
agent |
已构造的 Agent 实例 |
sessionManager |
会话树与 JSONL 持久化 |
settingsManager |
全局/项目设置 |
cwd |
会话工作目录,用于工具和系统提示 |
modelRuntime |
认证、模型目录、虚拟模型路由 |
resourceLoader |
扩展、skills、prompts、themes、context files |
scopedModels |
--models 指定的模型循环范围 |
customTools |
SDK 注入的工具定义 |
initialActiveToolNames |
初始激活工具 |
usesDefaultTools |
是否从 defaultTools 设置恢复装载 |
defaultToolModifiers |
--tools 的 +name/-name 修饰 |
allowedToolNames / excludedToolNames |
工具允许/排除模式 |
baseToolsOverride |
替换内置工具集合 |
extensionRunnerRef |
让 Agent 侧读取当前 ExtensionRunner 的可变引用 |
sessionStartEvent |
绑定扩展时 emit 的 session_start 元数据 |
cacheWarmer |
提示缓存预热器 |
3.2 内部状态分组
| 分组 | 代表字段 | 说明 |
|---|---|---|
| 运行状态 | _isAgentRunActive、_agentRunAbortRequested、_idleWaitPromise |
一次 run 从 _startRun 到 agent_settled |
| 队列 | _steeringMessages、_followUpMessages、_pendingNextTurnMessages、_pendingCustomMessages |
显示用文本与待注入消息 |
| 压缩 | _compactionAbortController、_autoCompactionAbortController、_branchSummaryAbortController、_overflowRecoveryAttempted |
手动/自动压缩与分支摘要 |
| 重试 | _retryAbortController、_retryAttempt、_failedResponse |
指数退避与"被省略的失败响应" |
| Bash | _bashAbortControllers、_pendingBashMessages |
流式期间延迟写入 |
| 扩展 | _extensionRunner、_extensionUIContext、_extensionMode、命令/中止/关闭/错误回调 |
宿主绑定 |
| 工具 | _toolRegistry、_toolDefinitions、_pendingToolNames、_hiddenDeclarations |
定义优先的注册表 |
| 提示词 | _baseSystemPromptOptions、_runSystemPromptOptions |
基础提示与本次 run 的覆盖 |
| 事件 | _eventListeners、_entryIdsByMessage、_boundaryDispatchedMessages |
公共监听与消息到条目映射 |
| 缓存 | _cacheWarmer |
onWarmed 时发出 entry_appended |
3.3 关键 getter 的语义
| Getter | 语义 |
|---|---|
isStreaming |
_isAgentRunActive,包含 before_agent_start、运行、重试等待、压缩后的继续 |
isIdle |
!isStreaming && !isCompacting;不包含正在运行的 bash |
isCompacting |
手动压缩、自动压缩、分支摘要任一进行中 |
model / thinkingLevel |
直接读 Agent state |
routedModel |
虚拟模型选择下,最近一次成功响应使用的物理模型 |
systemPrompt |
用 _runSystemPromptOptions(若存在)或基础选项实时构建 |
messages |
Agent state 中的消息,即当前投影 |
sessionFile / sessionId / sessionName |
转发 SessionManager |
4. 构造流程
构造函数(agent-session.ts:486-527)按固定顺序完成:
- 保存配置字段,初始化队列、工具、扩展引用;
- 订阅 Agent 事件:
agent.subscribe(this._handleAgentEvent); - 安装工具钩子
_installAgentToolHooks(); - 安装下一轮刷新
_installAgentNextTurnRefresh(); - 安装请求投影
_installAgentRequestProjection(); - 安装边界钩子
_installAgentBoundaryHooks(); - 安装隐藏声明投影
_installHiddenDeclarationsProjection(); - 安装强制提示投影
_installAgentForcedPromptProjection(); _buildRuntime()创建 ExtensionRunner、工具定义与注册表;- 如果
usesDefaultTools,从 transcript 恢复工具装载。
这些"安装"本质上是对 Agent 的可选回调做包装:
| Agent 回调 | AgentSession 用途 |
|---|---|
beforeToolCall / afterToolCall |
扩展 tool_call / tool_result 钩子与图像归一化 |
prepareRequest |
用会话投影替换请求上下文、虚拟模型路由、溢出前压缩 |
prepareNextTurnWithContext |
下一轮 assistant 请求前压缩、刷新提示与工具装载 |
finishTurn |
在 turn_end 前分发可操作边界 |
transformContext |
隐藏声明过滤、强制系统提示投影 |
5. 事件管线
5.1 单次 Agent 事件的处理顺序
_handleAgentEvent(agent-session.ts:1104-1200)是唯一入口:
Agent event
│
├─ 1. 特殊状态处理
│ - agent_start 且已请求 abort → agent.abort()
│ - toolResult message_start → 合并 nested tool call 记录与 usage
│ - agent_end → 清空 nested 记录
│ - user message_start → 从 steering/followUp 显示队列移除
│
├─ 2. _emitExtensionEvent(event) // 先扩展
│
├─ 3. _emit(public event) // 后宿主;agent_end 附带 willRetry
│
└─ 4. 持久化
- message_end: custom → custom_message;其余 → message
- assistant message_end: 更新 lastAssistant、重置重试计数
- turn_end: 记录 toolResults、flush pending custom messages
顺序上的两个关键点:
- 扩展先于公共监听器 。测试
emits extension events before public event subscribers覆盖。 - 扩展 handler 是 await 的 ,因此
message_end的替换会在持久化之前完成。测试keeps persisted assistant/toolResult message order when extension message_end handlers yield覆盖了慢 handler 下的顺序。
5.2 事件类型
AgentSessionEvent(agent-session.ts:196-237)在 AgentEvent 之上增加:
| 事件 | 触发点 |
|---|---|
agent_settled |
run 完全结算后(扩展与公共各一次) |
queue_update |
steering/followUp 队列变化 |
compaction_start / compaction_end |
手动或自动压缩 |
entry_appended |
绕过 message 事件直接写入条目时 |
session_info_changed |
会话重命名 |
thinking_level_changed |
thinking 等级实际变化 |
auto_retry_start / auto_retry_end |
模型请求重试 |
summarization_retry_* |
压缩/分支摘要内部重试 |
bash_execution_update |
bash 输出增量 |
agent_end 被重写为带 willRetry 的版本;willRetry 由 _willRetryAfterAgentEnd(:1202)计算:未 abort、重试已启用、未超过上限、最后一条 assistant 是可重试错误。
5.3 持久化映射
message_end(agent-session.ts:1148-1189)的映射规则:
| 消息 role | 写入方法 |
|---|---|
custom |
appendCustomMessageEntry(customType, content, display, details) |
system / user / assistant / toolResult |
appendMessage(message) |
bashExecution / compactionSummary / branchSummary |
由各自路径写入,不走这里 |
写入后把消息对象映射到条目 ID,供边界事件和恢复省略使用(_entryIdsByMessage)。
5.4 message_end 替换的原地语义
扩展可以通过 message_end 返回替换消息。_replaceMessageInPlace(:1271-1285)不是替换引用,而是:
- 删除目标对象的所有自有属性;
- 把替换对象的属性赋给目标对象。
原因在注释里写明:Agent core 在 emit message_end 之前已经把最终消息对象放进 state;后续 turn/agent 事件、监听器以及 appendMessage(event.message) 都引用同一个对象。原地修改能保证所有引用看到同一份替换结果。
替换内容若 content 为 null/缺失,会被归一化为 [],避免未类型化扩展把非法消息写入历史。
5.5 条目 ID 解析
_findPersistedMessageEntryId(:1218-1239)依次尝试:
_entryIdsByMessage弱映射;- 反向扫描当前分支中的
message条目,比较对象身份; - 通过 Agent state 中的消息索引,在投影条目中逐条计数定位源条目。
解析失败时,边界事件会报错,恢复省略会抛错,避免"以为持久化了但实际没有"。
5.6 结算
_emitAgentSettled(:1079-1101):
- 通知 cache warmer;
- 清除
_isAgentRunActive; - 先扩展、后公共地 emit
agent_settled; - 执行
_deferredSettledActions(在结算期间调用prompt()或带triggerTurn的sendCustomMessage()会被延迟到这里); - 如果空闲,resolve
waitForIdle()。
prompt() 在 _isEmittingAgentSettled 为 true 时直接把自己加入延迟队列(:2059-2062),避免在结算过程中启动新 run。
6. Prompt 生命周期
6.1 prompt() 前置处理
prompt(text, options)(agent-session.ts:2058-2141)按顺序做:
- 结算期延迟 :如果正在 emit
agent_settled,延迟到结算完成。 - 扩展命令 :文本以
/开头且允许展开时,先尝试_tryExecuteExtensionCommand。命中则立即执行,不消耗模型请求。 - 手动压缩互斥 :
_compactionAbortController存在时直接抛错,要求等待压缩结束。 - input 扩展钩子 :
_runInputHandlers可handled、transform或放行。source默认interactive;流式时会带上streamingBehavior。 - skill 与模板展开 :
/skill:name args读取 skill 文件,去掉 frontmatter,包成<skill name location>...</skill>;- 其余文本走
expandPromptTemplate。
- 流式排队 :如果
isStreaming:- 未指定
streamingBehavior抛错; followUp走_queueFollowUp;steer走_queueSteer;- 返回前调用
preflightResult("queued")。
- 未指定
- 模型与认证校验:无模型、无认证、OAuth 失效分别给出明确错误。
- 启动 run :
_startRun({ messageType: "user", prompt, images }, ...)。
preflightResult 是 RPC 模式观察"被接受后如何分发"的内部钩子,被拒绝时不会调用。
6.2 _startRun():一次 run 的外层循环
_startRun(:1837-1882)的结构:
ts
this._isAgentRunActive = true;
this._agentRunAbortRequested = false;
messages = await this._prepareRun(trigger); // 失败则复位 active 并抛出
flush pending bash/custom;
onStarted?.();
this._failedResponse = undefined;
this._recordSelection();
this._pendingToolNames.clear();
try {
await this.agent.prompt(messages);
while (!this._agentRunAbortRequested) {
if (await this._handlePostAgentRun()) {
if (abort) break;
await this.agent.continue();
continue;
}
if (abort || !(await this._runBeforeSettleBoundary())) break;
if (abort) break;
await this.agent.continue();
}
} finally {
if (abort) this._finishCancelledRetry();
this._failedResponse = undefined;
this._runSystemPromptOptions = undefined;
flush pending bash/custom;
await this._emitAgentSettled();
}
要点:
- run 在
_prepareRun之前就算"忙",所以before_agent_start期间发送的消息会进入队列而不是启动第二个 run;期间请求的 abort 会在agent_start时生效。 _handlePostAgentRun返回 true 表示"需要再跑一次":可重试错误、压缩后的继续、或仍有排队消息。agent_before_settle边界只在 post-run 不再继续时执行;它也可以要求继续。- 无论成功、失败还是 abort,finally 都会结算并 resolve idle。
6.3 _prepareRun():构造起始消息
_prepareRun(:1890-1941):
- 如果存在最后一条 assistant,先
_checkCompaction(lastAssistant, false)。skipAbortedCheck=false表示 aborted 响应也参与检查,用于捕获"上次被中止导致上下文过大"的情况。 emitBeforeAgentStart(trigger, baseOptions):- 扩展可以修改
systemPromptOptions、返回messages、返回systemPrompt。
- 扩展可以修改
- 工具选择处理:
- 如果 handler 显式修改了
selectedTools,以 handler 为准; - 否则以当前活动工具为准,避免
setActiveTools()被回滚。
- 如果 handler 显式修改了
- 构造起始消息:
customtrigger:直接放入自定义消息;- 普通 prompt:先归一化图片,再把图片提示拼到文本后,构造 user message;
- 追加
_pendingNextTurnMessages并清空; - 追加 handler 返回的 custom messages;
_preparePromptAndToolLoadout生成系统提示补丁;如果有补丁,放在消息最前面;- 保存
_runSystemPromptOptions供本轮请求投影和systemPromptgetter 使用。
图片归一化放在 before_agent_start 之后,是为了让扩展选择的模型决定 resize profile(:1912-1914 注释)。
6.4 _handlePostAgentRun():每次循环的决策
_handlePostAgentRun(:1943-1981)顺序:
- 取出并清空
_lastAssistantMessage/_lastAssistantToolResults; - 已 abort:结束取消中的重试,返回 false;
- 没有 assistant:返回
agent.hasQueuedMessages(); - 可重试错误:
_prepareRetry成功则设置_failedResponse并返回 true; - 非可重试错误且
_retryAttempt > 0:emitauto_retry_end { success: false }并清零; _checkCompaction(message, true, toolResults)返回 true 则继续;- 否则返回是否有排队消息。
注意第 6 步的 skipAbortedCheck=true:post-run 不会因为 aborted 消息触发压缩。
6.5 _runBeforeSettleBoundary()
agent_before_settle 是一个"run 即将结束但还可以继续"的扩展边界(:1983-2005):
- 无 handler 时直接返回是否有排队消息;
- 设置
_isBeforeSettle; - emit boundary,携带
outcome(completed / aborted / error); - 提交 drafts、flush pending custom;
- 如果 hook 期间 abort,返回 false;
- 如果要求继续但最终上下文不可运行(没有非 system 消息、最后一条是 assistant,且没有 pending custom 或排队消息),报错并停止。
6.6 一次 prompt 的完整时序
prompt(text)
│
├─ 扩展命令? ──是──> 执行并返回
├─ 手动压缩中? ──是──> 抛错
├─ input handlers(handled/transform)
├─ skill / template 展开
├─ isStreaming? ──是──> steer/followUp 入队并返回
├─ 模型/认证校验
└─ _startRun
│
├─ _prepareRun
│ ├─ 预压缩检查
│ ├─ before_agent_start
│ ├─ 图片归一化
│ ├─ 组装 user/custom 消息
│ └─ 系统提示补丁
│
├─ agent.prompt(messages)
│ └─ agent-loop:流式响应、工具执行、turn_end
│
├─ 循环:
│ ├─ 可重试错误 → 退避 → agent.continue()
│ ├─ 溢出/阈值 → 压缩 → agent.continue()
│ ├─ 有排队消息 → agent.continue()
│ └─ agent_before_settle 要求继续 → agent.continue()
│
└─ finally:flush、清状态、agent_settled
7. 系统提示与工具装载
7.1 定义优先的工具注册表
_buildRuntime(:3636-3688)建立:
_baseToolDefinitions:内置工具或baseToolsOverride转换出的定义;_extensionRunner:扩展运行时;_toolDefinitions/_toolRegistry:_refreshToolRegistry构建的定义表和可执行工具表;- 初始活动工具:
initialActiveToolNames或默认read/bash/edit/write。
_refreshToolRegistry(:3525-3623)的输入有三类:
- 扩展注册的工具(
runner.getAllRegisteredTools()); - SDK
customTools; - 内置工具定义。
它同时生成:
_toolPromptSnippets:一行工具说明,进入系统提示;_toolPromptGuidelines:工具使用规则;_toolRegistry:包装后的AgentTool,供 Agent 执行。
7.2 工具的五个曝光级别
ToolExposure(extensions/types.ts:516):
| 级别 | 含义 |
|---|---|
direct |
激活后声明给模型,可直接调用 |
model-only |
声明给模型,但不一定直接执行 |
codemode |
不声明,只能通过 ctx.executeTool() 被其他工具调用 |
deferred |
不声明,按需由 tool_search 等工具加载 |
hidden |
完全不可见,也不能激活 |
_getCallableTools(:1567-1572)的规则:
codemode和deferred始终可调用;direct只有在当前活动集合中才可调用;model-only/hidden不在其中。
7.3 允许/排除与 MCP 特例
_isAllowedTool(:1542-1546):
- 命中
excludedToolNames→ 不允许; - 没有 allowlist 或命中 allowlist → 允许;
- 否则,如果 allowlist 不针对 MCP,则 MCP 工具仍保留注册(供 codemode/tool_search 使用)。
_isActivatable(:1554-1557)进一步限制:未被 allowlist 命中的 MCP 工具,只有在"不是 direct 曝光且 tool_search 已注册"时才能被激活。
这解释了为什么 --tools 没写 mcp__* 时,MCP 工具仍然存在但不会直接出现在模型工具列表里。
7.4 装载(loadout)与 prepareLoadout
_applyToolLoadout(:1580-1625):
- 过滤未知、
hidden、不可激活的工具; - 收集带
prepareLoadout的活动工具; - 构建
ToolLoadout(declared / callable / registered / exposure / namespace / guidelines); - 调用每个 hook,收集描述覆盖与
hiddenDeclarations; - 设置
agent.state.tools。
hook 抛错会通过 emitError 报出,但不会中断装载。
_hiddenDeclarations 是"声明给模型的工具列表中需要隐藏的项"。_installHiddenDeclarationsProjection(:1772-1790)在 transformContext 中过滤系统消息里的 toolsAdded / toolsRemoved,保证请求中的工具声明与装载一致。
7.5 系统提示的增量更新
_preparePromptAndToolLoadout(:1743-1755):
- 先应用工具装载;
- 用当前消息中的系统提示 sections 与目标 sections 做 diff;
- 有变化时返回一个
role: "system"、content: ""、带sections的消息。
这条系统消息不是完整提示,而是"与模型当前持有的提示之间的补丁"。Agent loop 会把系统消息作为上下文的一部分发送,buildSystemPrompt 在读取时重建完整文本。
7.6 强制提示投影
before_agent_start 可以返回 systemPrompt,要求本轮请求以该文本开头。直接把它作为中途系统消息会保留原来的头部提示,因此 _installAgentForcedPromptProjection(:1792-1807)在 transformContext 末尾:
- 如果
_runSystemPromptOptions.forceSystemPrompt有值; - 构造一个 head system message,内容为强制文本,携带当前
toolsAdded; - 过滤掉其余所有 system message。
这样 transcript 仍保存结构化 sections,只有实际请求被投影成强制文本。
7.7 从 transcript 恢复工具
_restoreToolsFromTranscript(:1815-1825):
- 清空 pending;
- 读取当前系统消息的
toolsAdded; - 应用
+name/-name修饰; - 过滤 allowlist;
- 设为 pending 并
_setActiveTools(names)。
未注册的工具会留在 _pendingToolNames,等注册时由 _refreshToolRegistry 激活;_startRun 在发起请求前清空 pending,避免永远等待一个不会出现的工具。
7.8 嵌套工具调用
_executeNestedToolCall(:731-765)是 ctx.executeTool() 的实现:
- 惰性创建
NestedToolCallRunner; - 复用
runToolCall,但传入父调用 ID; beforeToolCall/afterToolCall都带parentToolCallId;- 事件同时发给扩展和公共监听器;
- 嵌套调用的 usage 与调用记录在
toolResult的message_start时合并到结果消息。
这保证 codemode、tool_search 等"工具调用工具"的场景仍然走统一的权限、钩子和事件管线。
8. 队列、消息注入与并发
8.1 steering 与 followUp
| 方法 | 语义 | 交付时机 |
|---|---|---|
steer() |
打断当前方向 | 当前 assistant turn 的工具调用完成后、下一次 LLM 请求前 |
followUp() |
追加后续任务 | 当前 run 没有更多工具调用和 steering 后 |
两者都经过 _queueUserInput(:2203-2230):
- 拒绝扩展命令(扩展命令不能排队);
- input handlers;
- skill/template 展开;
- 写入显示队列并调用
agent.steer/agent.followUp。
显示队列在对应 user message 的 message_start 时移除,保证 UI 先看到队列更新。
队列模式来自设置:
steeringMode/followUpMode:all或one-at-a-time;setSteeringMode/setFollowUpMode会写回设置。
clearQueue 同时清空显示队列和 Agent 队列。
8.2 sendCustomMessage() 的四种情况
sendCustomMessage(:2323-2359)的分支:
| 状态 | triggerTurn |
行为 |
|---|---|---|
| 流式中 | 未显式 false | deliverAs: followUp → followUp;否则 steer |
| 流式中 | false | 放入 _pendingCustomMessages,等 turn 结束(工具结果之后)再追加 |
| 空闲 | true | _startRun({ messageType: "custom" });若正在结算则延迟 |
| 空闲 | false | 立即追加,emit 公共 message_start / message_end |
deliverAs: "nextTurn" 优先于以上分支,放入 _pendingNextTurnMessages,在下一次 _prepareRun 中与起始消息一起发送。
流式中延迟追加的原因在注释中写明:立即追加会把消息插在 assistant tool call 与 tool result 之间,部分 provider 在重放时会拒绝这种顺序。
8.3 自定义消息的两条事件路径
- 进入 Agent loop 的 custom message:走完整 Agent 事件管线,扩展和公共监听器都会收到。
- 空闲且不触发 turn 的 custom message:
_appendCustomMessage(:2361-2371)直接写 SessionManager 并只 emit 公共message_start/message_end,不经过扩展事件分发。
这是代码中的一处不对称,宿主若依赖扩展观察所有消息事件,需要注意。
8.4 bash 消息的延迟写入
recordBashResult(:3909-3930):
- 流式中:放入
_pendingBashMessages; - 空闲:立即
appendMessage并刷新投影。
_startRun 在开始前和 finally 中都会 flush,保证 bash 输出不会插进工具调用与结果之间。
8.5 并发保护
AgentSession 没有内部锁,但有几个约束:
prompt()在流式中必须指定streamingBehavior,否则抛错;- 手动压缩期间禁止
prompt(); navigateTree()在流式或压缩期间抛错;abort()会等待 idle;- 结算期间的
prompt()/ 触发型 custom message 被延迟。
测试 AgentSession concurrent prompt guard 覆盖了并发 prompt、流式 steer/followUp、慢扩展 handler 下的持久化顺序。
9. 压缩
9.1 手动压缩 compact()
compact(customInstructions?)(:2794-2939):
- 先
await abort(),中止当前 run 并等待 idle; - 创建
_compactionAbortController,emitcompaction_start { reason: "manual" }; - 需要模型;
prepareCompaction(branchEntries, settings):- 最后一条已经是 compaction → 抛 "Already compacted";
- 无法切分 → 抛 "Nothing to compact";
session_before_compact扩展钩子:cancel→ 抛 "Compaction cancelled";- 提供
compaction→ 使用扩展结果,fromExtension = true;
- 否则走
_runDefaultCompaction; - 检查 abort;
appendCompaction(...);_refreshFinalizedContext();- emit
session_compact; - 先清除手动压缩状态,再 emit
compaction_end,允许监听器在事件里提交新 prompt; - 失败时 emit
compaction_end与session_compact_failed,然后重新抛出。
手动压缩不会自动重试或继续被打断的 turn。
9.2 自动压缩的三个入口
_checkCompaction(:2977-3115)区分三种情况:
| 情况 | 条件 | 行为 |
|---|---|---|
| 溢出 + 重试 | 上下文溢出或可恢复的 length stop,且响应未正常结束 | 省略失败响应,压缩,agent.continue() 重试一次 |
| 溢出不重试 | 响应正常结束但上下文溢出 | 压缩,保留已完成响应,不重试 |
| 阈值不重试 | 上下文 token 超过 contextWindow - reserveTokens |
压缩,不重试 |
关键保护:
- 溢出与 length stop 判定要求消息来自当前模型。切换模型后,旧模型的溢出错误不会触发新模型的溢出恢复。
- 消息不能早于最近一次压缩。避免压缩后第一条 prompt 被旧 usage 再次触发。
- 存在
context_edit时改用投影估算;错误响应或零 usage 时改用消息估算,避免直接使用不可信的 usage。 - 溢出恢复只尝试一次 。
_overflowRecoveryAttempted为 true 时直接报失败,避免无限循环。 - 恢复省略是持久的 。
_omitRecoveryAttempt(:1241-1257)为失败响应及其工具结果追加context_edit(replacement = null),让它们保留在原始历史中但从模型投影中消失。
阈值计算:
ts
contextTokens > contextWindow - reserveTokens
默认 reserveTokens = 16384,keepRecentTokens = 20000(compaction.ts:126-130)。
9.3 自动压缩执行
_runAutoCompaction(:3127-3274):
- 取模型和设置;无模型直接返回 false;
prepareCompaction;无法准备返回 false;- 创建
_autoCompactionAbortController,emitcompaction_start; session_before_compact可取消或提供结果;- 默认压缩或扩展结果;
appendCompaction、刷新投影、emitsession_compact;- emit
compaction_end; - 返回:
willRetry为 true → 让外层继续;- 否则如果有排队消息 → 继续一次以交付消息;
- 失败时 emit
compaction_end(带错误)和session_compact_failed,返回 false。
9.4 下一轮请求前的压缩
_compactBeforeNextAssistantResponse(:777-786)在 prepareNextTurnWithContext 中执行:
- 只对非虚拟模型做阈值检查;
- 虚拟模型在
prepareRequest中按实际路由模型检查; - 压缩后用新的投影消息替换上下文。
9.5 分支摘要
navigateTree(:3994-4186)在树导航时可选摘要:
- 流式或压缩中禁止;
- 目标就是当前 leaf → no-op;
collectEntriesForBranchSummary收集旧 leaf 到公共祖先之间的条目;session_before_tree可取消、提供 summary、覆盖指令/label;- 否则
generateBranchSummary,使用与压缩相同的重试策略; - 目标如果是 user message 或 custom message,leaf 设为父节点,文本回填编辑器;
- 有摘要时
branchWithSummary,无摘要时branch或resetLeaf; _refreshFinalizedContext()和_restoreToolsFromTranscript();- emit
session_tree。
10. 重试
10.1 判定
_isRetryableError(:3739-3743):
- 先排除上下文溢出(由压缩处理);
- 其余交给
isRetryableAssistantError。
10.2 退避与省略
_prepareRetry(:3792-3832):
- 读取
retry设置;未启用返回 false; _retryAttempt++;超过maxRetries时回退计数并返回 false;retryDelayMs(settings, attempt)计算延迟;- emit
auto_retry_start; _omitRecoveryAttempt(message)把失败响应从模型投影中省略;- 用
_retryAbortController可中止地 sleep; - 成功返回 true,调用方
agent.continue()。
_failedResponse 保存被省略的失败响应,_installAgentRequestProjection 在重试请求中把它作为 failed 传给虚拟模型路由(:823-828)。
10.3 结束与取消
- 非
error的 assistant 响应且_retryAttempt > 0→ emitauto_retry_end { success: true }并清零(:1178-1187)。 - 非可重试错误且 attempt > 0 → emit 失败事件并清零(
:1964-1972)。 - abort →
_finishCancelledRetryemit "Retry cancelled"。 abortRetry()只中止 sleep。
10.4 摘要重试
压缩和分支摘要共享 _summarizationRetryCallbacks(:3751-3774),把重试过程映射为:
summarization_retry_scheduledsummarization_retry_attempt_start(带 source)summarization_retry_finished
使用与 agent turn 相同的 retry 预算和退避,避免一次瞬时流错误让整个压缩失败。
11. 模型与 thinking
11.1 认证
_getRequiredRequestAuth(:533-571):
- 调
modelRuntime.getAuth; - 有
apiKey或headers时,允许baseUrl覆盖; - 过滤值为
null的 header(表示删除); - OAuth 失败给出
/login指引; - 否则给出
formatNoApiKeyFoundMessage。
_getSummarizationRequestAuth(:573-610):
- 虚拟模型先路由到物理模型;
- 如果 Agent 使用自定义
streamFunction,认证失败时允许继续(自定义实现可能自带认证); - 否则返回解析后的模型、认证和 thinking level。
11.2 setModel() 与 cycleModel()
setModel(:2507-2527):
checkAuth,失败抛错;- 计算新模型的 thinking level;
- 写
agent.state.model; appendModelChange(始终写入会话);options.persist时写全局默认,并加入非空 scoped 列表;setThinkingLevel;- emit
model_select。
cycleModel(:2549-2630):
- 有 scoped models 时只在 scoped 中循环,并保留每个 scoped 的 thinking 偏好;
- 否则在可用模型快照中循环;
- 只有一个模型时返回 undefined;
- 默认只改会话,不写全局。
11.3 thinking level
setThinkingLevel(:2642-2665):
- 取模型支持等级;
- 不在支持列表时
clampThinkingLevel; - 只有实际变化才
appendThinkingLevelChange、emit 公共thinking_level_changed与扩展thinking_level_select; options.persist时把"请求值"写入全局默认(不是 clamp 后的值)。
_getThinkingLevelForModelSwitch(:2699-2711)优先级:
- 显式传入;
- 目标模型的 per-model 设置;
- 全局默认;
- 当前会话值;
DEFAULT_THINKING_LEVEL。
11.4 虚拟模型路由
虚拟模型不是真实 provider/model,而是路由器。相关逻辑:
routedModel(:1456-1461):从最近成功响应找物理模型;_limitsModel(:641-643):压缩、图片等限制使用路由后的模型;_modelForMessage(:616-620):判断一条 assistant 消息是否属于当前模型;_recordSelection(:629-638):当分支隐含的选择与当前不同,且涉及虚拟模型时,写入model_change,保证 resume 能恢复;_installAgentRequestProjection(:788-845):- 用投影替换请求上下文;
- 调用
resolveModel得到实际模型与 thinking; - 持久化路由器状态变化(
VIRTUAL_MODEL_STATE_ENTRY); - 如果路由后的模型超过阈值,先压缩再重新准备请求。
只有用户消息开始新 turn 时使用 reason: "user";扩展消息跟随其后时使用 continuation;重试使用 retry 并带 failed 响应。
12. 扩展系统集成
12.1 绑定
bindExtensions(bindings)(:3288-3312):
- 只在字段非 undefined 时更新绑定,允许部分更新;
_applyExtensionBindings设置 UI context、命令上下文、错误监听;- emit
_sessionStartEvent; reportUnhandledMcpServers();extendResourcesFromExtensions(...)。
_sessionStartEvent 默认 { type: "session_start", reason: "startup" }。运行时替换后由宿主重新绑定,因此新会话会收到自己的 session_start。
12.2 注入给扩展的核心能力
_bindExtensionCore(:3390-3523)把 AgentSession 的能力绑定给 ExtensionRunner:
| 能力 | 行为 |
|---|---|
sendMessage / sendUserMessage |
转发到 AgentSession,错误通过 runner 报出 |
appendEntry |
写 custom entry 并 emit entry_appended |
setSessionName / getSessionName |
会话命名 |
setLabel |
给条目加 label |
getActiveTools / getAllTools / setActiveTools / refreshTools |
工具查询与修改 |
setModel / getThinkingLevel / setThinkingLevel |
模型控制 |
abort / shutdown |
中止或请求宿主退出 |
compact |
异步手动压缩,带 onComplete/onError |
getSystemPrompt / getSystemPromptOptions |
读取当前提示 |
executeTool / getCallableTools |
嵌套工具调用 |
registerProvider / registerNativeProvider / registerVirtualModel 等 |
动态注册,随后刷新当前模型 |
12.3 重载 reload()
reload(:3690-3729)不替换会话对象,只替换扩展运行时和工具:
- 记录旧 runner 与 flag values;
- emit
session_shutdown { reason: "reload" }; - invalidate 旧 runner;
settingsManager.reload();- 同步队列模式;
resetApiProviders();resourceLoader.reload();- 计算新增的 default tools;
- 把当前活动工具加入 pending;
_buildRuntime(...)重建 runner 和工具;- 如果有绑定,执行
beforeSessionStart,emitsession_start { reason: "reload" },报告 MCP,扩展资源。
重载期间旧扩展上下文会失效,扩展必须把后续工作放到重载之后。
12.4 扩展命令与资源
_tryExecuteExtensionCommand(:2146-2170):解析命令名,取createCommandContext(),执行;错误通过emitError报出,但仍视为已处理。_expandSkillCommand(:2177-2201):读取 skill 文件并包装。extendResourcesFromExtensions(:3314-3336):resources_discover返回的 skill/prompt/theme 路径以 temporary 作用域加入 ResourceLoader,并重建系统提示。
13. 持久化与上下文投影
13.1 投影是权威来源
_refreshFinalizedContext(:939-945):
sessionManager.buildSessionProjection();- 建立"消息对象 → 源条目 ID"映射;
agent.state.messages = projection.messages。
调用点包括:
| 场景 | 位置 |
|---|---|
| 手动压缩后 | compact() |
| 自动压缩后 | _runAutoCompaction() |
| 恢复省略 | _omitRecoveryAttempt() |
| 边界 drafts 提交 | _commitBoundaryDrafts() |
| 直接追加 custom message | _appendCustomMessage() |
| bash 消息 flush | _flushPendingBashMessages() |
| 树导航 | navigateTree() |
| 运行时 setup 后 | AgentSessionRuntime 调 refreshContext() |
refreshContext() 是公开入口,供会话替换的 setup 阶段重建 transcript。
13.2 投影模型
buildSessionProjection(session-manager.ts:543-573):
- 从 leaf 沿父链构建路径;
- 取路径上的 thinking level 和 model;
buildContextEntries处理压缩边界、分支摘要、context edit;- 对每个源条目生成
ProjectedSessionEntry:sourceEntry:原始条目;messages:投影后的消息(可能为空);
- 返回
entries、扁平化messages、thinkingLevel、model。
这使压缩、上下文编辑、恢复省略都不需要修改原始历史,只影响投影。
13.3 条目类型与写入 API
SessionManager 的条目包括:
messagethinking_level_changemodel_changeusagecompactionbranch_summarycustomcustom_messagelabelsession_infocontext_edit
AgentSession 使用的主要写入 API:
| API | 用途 |
|---|---|
appendMessage |
普通消息与 bashExecution |
appendCustomMessageEntry |
扩展 custom message |
appendCustomEntry |
扩展 custom 数据 |
appendCompaction |
压缩摘要 |
appendModelChange |
模型切换 |
appendThinkingLevelChange |
thinking 切换 |
appendContextEdit |
恢复省略或边界替换 |
appendLabelChange |
条目标签 |
appendSessionInfo |
会话名称 |
13.4 树导航
navigateTree 的 leaf 规则:
- 目标是 user message:leaf = 父节点,文本回填编辑器;
- 目标是 custom message:leaf = 父节点,文本回填编辑器;
- 其他目标:leaf = 目标本身。
有摘要时摘要条目挂在新 leaf 位置;无摘要且目标为根时 resetLeaf()。
14. Bash、统计、导出与其它能力
14.1 Bash
executeBash(:3871-3903):
- 创建独立的 abort controller;
- 应用
shellCommandPrefix; - 用
sessionManager.getCwd()作为工作目录; - 支持注入
BashOperations(远程执行); - 输出增量通过回调与
bash_execution_update事件发出; - 结果交给
recordBashResult。
abortBash 中止所有活动 bash;isBashRunning / hasPendingBashMessages 提供状态。
14.2 统计与上下文使用
getSessionStats(:4213-4265):
- 遍历所有条目,不只当前分支,因此包含被压缩掉的历史;
- 统计 user/assistant/toolResult/toolCalls;
- 累加
usage条目、branch summary/compaction 的 usage、toolResult usage、assistant usage; contextUsage来自getContextUsage()。
getContextUsage(:4267-4310):
- 无模型或无 contextWindow → undefined;
- 最近一次压缩之后没有有效 assistant usage →
{ tokens: null, percent: null }; - 否则用
estimateProjectedContextTokens计算。
14.3 导出与报告
| 方法 | 作用 |
|---|---|
exportToHtml |
导出 HTML,支持扩展工具渲染器和主题 |
exportToJsonl |
导出当前分支为 JSONL |
summarizeForBugReport |
用当前模型生成 bug report 摘要 |
getLastAssistantText |
供 /copy 使用 |
14.4 缓存预热
cacheWarmingStatus读取 warmer 状态;setCacheWarmingMode写设置并立即协调;onWarmed时 emitentry_appended;dispose时取消并清空回调。
15. 生命周期、失败语义与边界条件
15.1 abort()
abort(:2464-2474):
- 如果 run 活跃,设置
_agentRunAbortRequested; - 中止 retry、compaction、branch summary;
- 如果在 before-settle 中,标记
_abortDuringBeforeSettle; agent.abort();await waitForIdle()。
15.2 dispose()
dispose(:1397-1418):
- 尽力中止 retry/compaction/branch/bash/agent;
extensionRunner.invalidate(...),错误信息明确要求使用替换后的新 ctx;- 断开 Agent 订阅;
- 清空公共监听器;
- 取消 cache warmer;
cleanupSessionResources(sessionId)。
它不等待 idle;运行时替换路径会先 await session.abort() 再 dispose,退出路径则直接 dispose。因此退出时未完成的 turn 不保证像替换路径那样完成持久化。
15.3 失败语义
| 场景 | 结果 |
|---|---|
_prepareRun 失败 |
_isAgentRunActive 复位,flush 队列,抛出 |
agent.prompt / 循环失败 |
finally 结算后抛出 |
| 手动压缩失败 | emit compaction_end + session_compact_failed,重新抛出 |
| 自动压缩失败 | emit 事件,返回 false,run 继续按失败响应处理 |
| 重试耗尽 | emit auto_retry_end { success: false } |
| 溢出恢复失败 | emit compaction_end + session_compact_failed,结束 |
| 扩展 handler 抛错 | tool_call 直接抛;命令错误通过 emitError;边界错误记录后按不可继续处理 |
15.4 值得注意的边界
isIdle不包含 bash 。用户可以运行!命令而会话仍被视为 idle;abort()不会调用abortBash(),取消用户主动运行的 bash 需要显式调用abortBash()或dispose()。- 流式中的 custom message 默认是 steer 。
sendCustomMessage未显式triggerTurn: false时,默认按 steer 排队。 - pending 工具只等一次 run 。
_startRun在请求前清空_pendingToolNames,未注册的工具不会永久挂起。 - 直接追加的 custom message 不走扩展 message 事件。见 8.3。
_runSystemPromptOptions在 run 结束后清空 。systemPromptgetter 之后回落到基础提示。_failedResponse只用于下一次请求 。_installAgentRequestProjection读取后立即清空。- 虚拟模型的
model_change记录有条件。物理选择通常由响应本身记录,只有涉及虚拟模型时才额外写入。 - 上下文溢出恢复只一次。第二次仍溢出会明确报错,要求减少上下文或换更大窗口模型。
- 压缩后上下文使用量未知 。直到压缩后出现新的有效 assistant usage,
getContextUsage返回 null。 - 树导航会恢复工具装载 。导航后调用
_restoreToolsFromTranscript(),因此分支记录的工具列表会重新生效。
16. 设计评价
优点
-
职责集中但不越界
AgentSession 管理会话编排,不负责 CLI 参数、认证存储、项目信任;这些留在
AgentSessionRuntime和工厂闭包中。 -
投影作为单一事实来源
压缩、上下文编辑、恢复省略都通过 SessionManager 条目表达,Agent state 在固定点刷新。这样持久化、恢复和请求投影共用同一套逻辑。
-
事件顺序有明确契约
扩展先于公共监听器;
message_end替换在持久化前完成;agent_settled在所有 handler 之后。多个回归测试保护这些顺序。 -
压缩与重试的边界处理细致
区分"溢出但有响应""溢出且失败""阈值压缩""length stop 恢复",并用
context_edit持久化省略,避免重放时再次触发。 -
工具装载模型表达力强
五种曝光级别、allowlist/exclude、pending 工具、
prepareLoadout、从 transcript 恢复,覆盖了内置工具、MCP、codemode、tool_search 的组合。 -
扩展能力完整
扩展可以观察事件、修改消息、拦截工具、注入消息、注册模型/工具/资源、执行嵌套工具,并有明确的 stale 上下文保护。
代价与风险
-
状态机复杂
_isAgentRunActive、_isBeforeSettle、_isEmittingAgentSettled、多个 abort controller 和 pending 队列共同决定行为。理解一次 run 的完整时序需要跨多个方法阅读。 -
隐式顺序约束多
例如:
- 先 abort 再压缩;
- 先 flush 再结算;
- 先扩展事件再持久化;
- 先提交边界 drafts 再判断能否继续;
- 先清除压缩状态再 emit
compaction_end。
这些顺序大多靠注释和测试维持,类型系统无法强制。
-
部分路径存在不对称
直接追加的 custom message 不走扩展 message 事件;bash 消息不 emit
message_end;不同来源的消息经过不同的事件路径。 -
没有内部并发锁
并发
prompt/navigateTree靠显式检查和调用方约定,而不是状态机拒绝。扩展或 SDK 误用可能造成交错。 -
投影刷新是手动的
新增一条绕过
message_end的写入路径时,必须记得调用_refreshFinalizedContext(),否则 Agent state 与 SessionManager 会不一致。 -
dispose与abort语义不同退出路径不保证未完成 turn 的持久化结算,宿主需要显式先 abort。
总体而言,AgentSession 是一个"事件驱动的会话状态机 + 投影刷新器":它把 Agent 的通用循环适配成 coding agent 需要的持久化、压缩、重试、扩展和工具语义。复杂度主要来自这些横切关注点的交织,而不是单个算法。
17. 关键不变量
以下是代码中反复依赖、修改时必须保持的约束:
- 一条消息在持久化前必须已经完成扩展
message_end处理。 message_end替换必须原地修改对象,否则 Agent state、事件监听器和持久化会看到不同对象。- 压缩、上下文编辑、恢复省略之后必须刷新投影。
- 任何会写入条目的路径都必须推进 leaf 或显式刷新(SessionManager 的 append 会推进 leaf)。
_pendingCustomMessages只能在 turn 结束后 flush,否则会插在 tool call 与 tool result 之间。_pendingBashMessages只能在 run 开始前或结束后 flush。_startRun的 finally 必须结算 ,否则isIdle永远为 false,waitForIdle()会挂起。- 扩展 runner 失效后不能继续使用旧 ctx ;
dispose/reload都会 invalidate。 - 工具声明与可执行工具必须一致 :
_hiddenDeclarations要同步到系统提示补丁和请求投影。 - 溢出恢复最多一次,且省略必须持久化,不能只改内存。
18. 关键代码索引
| 位置 | 内容 |
|---|---|
agent-session.ts:166 |
ParsedSkillBlock |
agent-session.ts:177 |
parseSkillBlock |
agent-session.ts:196 |
AgentSessionEvent |
agent-session.ts:252 |
AgentSessionConfig |
agent-session.ts:302 |
ExtensionBindings |
agent-session.ts:315 |
PromptOptions |
agent-session.ts:343 |
SessionStats |
agent-session.ts:379 |
AgentSession 类 |
agent-session.ts:486 |
构造函数 |
agent-session.ts:533 |
_getRequiredRequestAuth |
agent-session.ts:573 |
_getSummarizationRequestAuth |
agent-session.ts:616 |
_modelForMessage |
agent-session.ts:629 |
_recordSelection |
agent-session.ts:653 |
_installAgentToolHooks |
agent-session.ts:659 |
_beforeToolCall |
agent-session.ts:685 |
_afterToolCall |
agent-session.ts:731 |
_executeNestedToolCall |
agent-session.ts:768 |
_exceedsCompactionThreshold |
agent-session.ts:777 |
_compactBeforeNextAssistantResponse |
agent-session.ts:788 |
_installAgentRequestProjection |
agent-session.ts:847 |
_dispatchTurnEndBoundary |
agent-session.ts:887 |
_installAgentBoundaryHooks |
agent-session.ts:899 |
_installAgentNextTurnRefresh |
agent-session.ts:939 |
_refreshFinalizedContext |
agent-session.ts:947 |
_applyBoundaryDrafts |
agent-session.ts:988 |
_createBoundaryPreviewManager |
agent-session.ts:1000 |
_buildBoundaryContext |
agent-session.ts:1079 |
_emitAgentSettled |
agent-session.ts:1104 |
_handleAgentEvent |
agent-session.ts:1202 |
_willRetryAfterAgentEnd |
agent-session.ts:1218 |
_findPersistedMessageEntryId |
agent-session.ts:1241 |
_omitRecoveryAttempt |
agent-session.ts:1271 |
_replaceMessageInPlace |
agent-session.ts:1288 |
_emitExtensionEvent |
agent-session.ts:1373 |
subscribe |
agent-session.ts:1397 |
dispose |
agent-session.ts:1425 |
refreshContext |
agent-session.ts:1456 |
routedModel |
agent-session.ts:1464 |
isStreaming |
agent-session.ts:1469 |
isIdle |
agent-session.ts:1523 |
setActiveToolsByName |
agent-session.ts:1542 |
_isAllowedTool |
agent-session.ts:1554 |
_isActivatable |
agent-session.ts:1567 |
_getCallableTools |
agent-session.ts:1580 |
_applyToolLoadout |
agent-session.ts:1705 |
_rebuildSystemPrompt |
agent-session.ts:1743 |
_preparePromptAndToolLoadout |
agent-session.ts:1772 |
_installHiddenDeclarationsProjection |
agent-session.ts:1792 |
_installAgentForcedPromptProjection |
agent-session.ts:1815 |
_restoreToolsFromTranscript |
agent-session.ts:1837 |
_startRun |
agent-session.ts:1890 |
_prepareRun |
agent-session.ts:1943 |
_handlePostAgentRun |
agent-session.ts:1983 |
_runBeforeSettleBoundary |
agent-session.ts:2007 |
_runInputHandlers |
agent-session.ts:2027 |
_normalizePromptImages |
agent-session.ts:2058 |
prompt |
agent-session.ts:2146 |
_tryExecuteExtensionCommand |
agent-session.ts:2177 |
_expandSkillCommand |
agent-session.ts:2203 |
_queueUserInput |
agent-session.ts:2241 |
steer |
agent-session.ts:2257 |
followUp |
agent-session.ts:2323 |
sendCustomMessage |
agent-session.ts:2361 |
_appendCustomMessage |
agent-session.ts:2377 |
_flushPendingCustomMessages |
agent-session.ts:2395 |
sendUserMessage |
agent-session.ts:2432 |
clearQueue |
agent-session.ts:2464 |
abort |
agent-session.ts:2476 |
waitForIdle |
agent-session.ts:2507 |
setModel |
agent-session.ts:2549 |
cycleModel |
agent-session.ts:2642 |
setThinkingLevel |
agent-session.ts:2699 |
_getThinkingLevelForModelSwitch |
agent-session.ts:2794 |
compact |
agent-session.ts:2944 |
abortCompaction |
agent-session.ts:2977 |
_checkCompaction |
agent-session.ts:3127 |
_runAutoCompaction |
agent-session.ts:3288 |
bindExtensions |
agent-session.ts:3314 |
extendResourcesFromExtensions |
agent-session.ts:3390 |
_bindExtensionCore |
agent-session.ts:3525 |
_refreshToolRegistry |
agent-session.ts:3636 |
_buildRuntime |
agent-session.ts:3690 |
reload |
agent-session.ts:3739 |
_isRetryableError |
agent-session.ts:3751 |
_summarizationRetryCallbacks |
agent-session.ts:3776 |
_finishCancelledRetry |
agent-session.ts:3792 |
_prepareRetry |
agent-session.ts:3871 |
executeBash |
agent-session.ts:3909 |
recordBashResult |
agent-session.ts:3935 |
abortBash |
agent-session.ts:3994 |
navigateTree |
agent-session.ts:4213 |
getSessionStats |
agent-session.ts:4267 |
getContextUsage |
agent-session.ts:4318 |
exportToHtml |
agent-session.ts:4344 |
exportToJsonl |
agent-session.ts:4352 |
summarizeForBugReport |
agent-session.ts:4405 |
createReplacedSessionContext |
sdk.ts:411 |
new Agent |
sdk.ts:461 |
new AgentSession |
session-manager.ts:543 |
buildSessionProjection |
session-manager.ts:1204 |
appendMessage |
session-manager.ts:1261 |
appendCompaction |
session-manager.ts:1339 |
appendCustomMessageEntry |
session-manager.ts:1360 |
appendContextEdit |
compaction.ts:267 |
shouldCompact |
compaction.ts:859 |
prepareCompaction |
compaction.ts:952 |
compact |
messages.ts:148 |
convertToLlm |
19. 测试覆盖
与 AgentSession 直接相关的测试:
| 测试文件 | 覆盖内容 |
|---|---|
test/suite/agent-session-prompt.test.ts |
普通 prompt、工具 turn、多工具、图片、skill/template 展开、扩展命令、流式校验、自定义消息启动的 run |
test/suite/agent-session-queue.test.ts |
steering/followUp 顺序、模式、custom message 投递、nextTurn、队列计数、扩展命令拒绝 |
test/suite/agent-session-retry-events.test.ts |
重试成功/耗尽/取消、事件顺序、流式增量、错误与中止的 agent_end |
test/suite/agent-session-compaction.test.ts |
手动/自动压缩、扩展摘要、排队 prompt、溢出恢复、length stop、中止 |
test/suite/agent-session-boundaries.test.ts |
turn_end / agent_before_settle 边界、drafts、恢复省略、length recovery、结算延迟 |
test/suite/agent-session-tool-orchestration.test.ts |
嵌套工具调用、codemode/tool_search 激活 |
test/suite/agent-session-mcp.test.ts |
MCP 工具的 allowlist/exclude/exposure、resume/reload |
test/suite/agent-session-model-extension.test.ts |
模型/thinking 切换与持久化、扩展钩子、bindExtensions / reload 事件 |
test/suite/agent-session-bash-persistence.test.ts |
bash 记录、流式延迟、取消、消息持久化顺序 |
test/suite/agent-session-tool-result-images.test.ts |
工具结果图片归一化 |
test/suite/agent-session-compaction-model-overrides.test.ts |
压缩使用新选模型、模型身份捕获 |
test/suite/agent-session-runtime.test.ts |
message_end 替换持久化、替换前结算、运行时替换 |
test/suite/regressions/1717-2113-agent-session-event-settlement.test.ts |
慢扩展 handler 下的持久化顺序、tool_call 在消息结算后执行 |
test/agent-session-concurrent.test.ts |
并发 prompt 守卫、流式 steer/followUp、慢 handler 顺序 |
test/agent-session-auto-compaction-queue.test.ts |
阈值压缩后的队列恢复、溢出只重试一次、陈旧 usage |
test/agent-session-branching.test.ts |
fork/分支 |
test/agent-session-tree-navigation.test.ts |
树导航与分支摘要 |
test/agent-session-stats.test.ts |
统计与上下文使用量 |
test/agent-session-dynamic-tools.test.ts |
动态工具注册与刷新 |
test/agent-session-dynamic-provider.test.ts |
动态 provider 注册与当前模型刷新 |
test/agent-session-runtime-events.test.ts |
运行时事件顺序、取消、rebind 顺序 |