6.9 KiB
| title | created | updated | type | tags | sources | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Session Tree 运行时 | 2026-04-21 | 2026-04-26 | concept |
|
|
Session Tree 运行时
Session Tree 是 NetaClaw 新 Agent runtime 的会话状态模型。它把对话从线性消息列表升级为“节点树 + 当前叶子 + 活动路径 + 运行时上下文”的结构,使刷新恢复、分支、压缩、子 Agent 批次、标签、模型切换都能进入同一套持久化和投影机制。
它是 agent-runtime、context-compaction、subagent-session 和 frontend-architecture 的共同底座。
Provider 抽象
SessionTreeProvider 定义统一接口,当前有 file 和 mysql 两种 provider。
主要能力包括:
createSessiongetSessionupdateSessiondeleteSessionlistEntriesappendEntryappendMessageappendThinkingLevelChangeappendModelChangeappendCompactionappendBranchSummaryappendLabelChangeappendSessionInfoupdateEntryswitchLeafresetLeafcreateBranchedSessiongetActivePathgetSnapshot
这层抽象让运行时不直接绑定 MySQL 或本地文件,也为未来多 workspace、本地优先存储、导入导出保留空间。
Snapshot 结构
SessionTreeSnapshot 是前后端对齐的核心投影:
| 字段 | 含义 |
|---|---|
session |
会话元信息,包括 provider、rootEntryId、leafEntryId、cwd、agentId 等 |
entries |
当前会话所有树节点 |
activePath |
从 root 到 leaf 的当前上下文路径 |
childrenByParentId |
前端重建树结构所需的父子索引 |
labelsByEntryId |
节点标签状态 |
runtimeContext |
供模型调用使用的消息、thinking level、模型引用 |
前端刷新恢复应优先加载 snapshot,而不是依赖 localStorage 保存完整聊天记录。
Entry 类型
当前树节点类型包括:
| 类型 | 用途 |
|---|---|
message |
system/user/assistant/tool 模型消息 |
thinking_level_change |
会话内 thinking level 切换 |
model_change |
会话内模型切换 |
compaction |
压缩摘要节点,详见 context-compaction |
branch_summary |
分支摘要 |
custom |
不直接展示的自定义数据 |
custom_message |
可展示的自定义消息 |
label |
对目标节点设置或清除标签 |
session_info |
会话信息变更 |
subagent_batch |
子 Agent 批次节点,详见 subagent-session |
subagent_result |
子 Agent 批次结果节点 |
Active Path 与上下文构建
模型上下文不再等于“所有历史消息”。运行时会从当前 leafEntryId 回溯得到 activePath,再由 context_builder.ts 转成 runtimeContext.messages。
这带来几个结果:
- 分支切换只需要切换 leaf。
- 压缩摘要可以替代被压缩的历史段。
- 子 Agent、分支摘要、custom message 可以参与展示,但不一定进入模型上下文。
- thinking/model 变更可以沿路径生效。
持久化位置
Session Tree 支持两类持久化:
- MySQL provider:适合生产和多端共享。
- File provider:适合本地优先、单机 workspace、调试和导入导出。
浏览器 localStorage 只保存最近 session/agent 之类的轻量指针,不是对话历史主存储。
删除语义
Session Tree 删除必须按会话实际所属的 provider 执行:
- 前端
chat.ts删除请求优先携带会话列表中该 session 的agentId。 - 后端
gateway/session.ts在删除前先从netaclaw_agent_session反查sessionId所属agentId,再解析该 Agent 的 session backend。 - MySQL provider 删除
netaclaw_agent_session_entry和netaclaw_agent_session;file provider 删除对应 JSONL 文件。 - 随后后端再清理 legacy session/message、subagent_session 和 session-tree 兼容表记录。
这避免了 MySQL 会话在当前选中 Agent 不一致、或前端缺少 agentId 时走默认 file provider,导致“点击删除没有效果”。
前端消费
packages/frontend/src/modules/agent/store/chat.ts 负责消费 snapshot:
sessionMeta保存会话元信息。entries和entryById保存节点。childrenByParentId支持树结构恢复。activePathIds控制当前可见路径。visibleEntries将树节点投影成对话 UI。subagentRuntimeByBatchId和toolRuntimeRoutesByBatchId记录子 Agent 工具路由展示数据。
对话页滚动、刷新恢复、历史批次展示都应该围绕这些状态实现。
相关页面
- agent-runtime
- subagent-session
- context-compaction
- frontend-architecture
- websocket-gateway
- netaclaw-module
- frontend-architecture
2026-04-22 Subagent Result Metadata
subagent_result carries durable metadata needed to replay subagent execution after refresh.
metadata.processEvents: normalized worker event timeline for UI replay.metadata.evidenceSummaries: structured evidence projection built from subagent tool results.metadata.toolRuntimeRoutes: tool routing diagnostics for the delegated batch.
The frontend store should project these fields into dedicated maps keyed by entry id, rather than letting view components parse raw metadata directly.
2026-04-23 Continue-From-Entry And Projection Contract
Session Tree 现在不仅负责“存什么”,还决定“从哪里继续对话”:
- 前端
chat.ts维护selectedEntryId、switchingLeafEntryId、pendingLeafConfirmation,允许用户选中任意节点后继续发送,而不是只能沿当前 leaf 末尾追加。 - 如果选中的是普通 user 节点,继续发送会基于它的父节点重放该条 user message,形成新的分支。
- 如果选中的是
branch_summary、compaction或非当前 leaf 节点,前端会先切换 leaf,再沿该路径继续。 - 这些交互都以
session.leafEntryId、activePath和 snapshot 中的树结构为依据,不依赖前端自行缓存一份线性历史。
对子 Agent 相关节点,当前 snapshot 契约也更明确:
subagent_result.metadata.subagentProjection是会话树边界输出的 canonical projection。- projection 内的
diagnostics.selectedSource、inputSources、fallbackUsed用来说明当前展示究竟来自subagent_result、旧 tool message,还是历史 metadata fallback。 - 这让 Session Tree 不再只是原始数据容器,而是把“兼容历史记录”和“为当前 UI 供给稳定形态”一起纳入运行时模型。