
DeepSeek Harness 会话表层架构简化为每个 Session 只保留一个 SurfaceManager 的增量状态设计与实现【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness本文基于 DeepSeek Harness 仓库中的已归档 Agent Note.agents/notes/archived/simplification/2026-07-19-use-one-session-surface-manager.zh.md展开深入剖析事件溯源会话模型中表层Surface管理器的收敛设计从同一份仅追加事件日志上的双SurfaceManager实例收敛为每个Session唯一持有一个管理器。读完本文你将理解validateNext()的原子校验契约、session.surface的只读稳定视图、foldSurface()与活跃管理器的一致性关系以及这套设计如何在dsh-session中支撑派生消息、压缩compaction与工作区上下文共享同一份增量状态。背景事件溯源会话与表层投影Session实现于 packages/core/session/src/index.ts是 DeepSeek Harness 的内存事件溯源模型它由类型化SessionEvent组成一份仅追加日志是 agent 完整交互历史的唯一真源。LLM 消息历史从日志派生而来从不单独存储回放即从同一组事件重新派生。seq始终等于日志长度seq log.length连续性契约time为 epoch 毫秒。在全部事件类型中只有三种产生消息的类型SurfaceEventTypeuser/message、assistant/message、tool/result携带 surface 元数据声明它们如何加入有序的派生表层append常规尾部追加路径{ op: replace, start, end }将当前表层中start到end含两端的节点遮蔽并原位替换为新节点start end时仅替换单个节点且该事件的sourceEventSeqs必须覆盖每一个被遮蔽的节点。压缩compaction正是通过replace将一段对话压缩为一个摘要节点。SessionSurface是这个派生表层的只读实时投影契约如下源码见 packages/core/session/src/surface.ts/** Readonly live projection of the message-producing session events. */ export interface SessionSurface { /** Current surface event sequences in model-visible order. */ readonly nodes: readonly number[] /** Monotonic count of committed positional replacements. */ readonly replaceGeneration: number }s nodes给出当前模型可见的表层事件 seq 序列replaceGeneration是已提交的位置替换单调计数让增量消费方能够区分纯尾部增长与重写。问题双 SurfaceManager 带来的重复状态在本次简化之前Session针对同一份仅追加事件日志维护两个SurfaceManager实例一个实例负责校验种子事件与追加候选事件另一个延迟创建的实例独立折叠已提交事件供session.surface、派生消息、压缩和工作区上下文使用。后果是一旦某个消费方读取了公共表层之后的每个事件都会推进两份重复的节点状态与替换代数状态。两份状态既不构成独立真源source of truth也没有形成独立的失败边界——它们只是对同一份日志的重复增量折叠白白增加了内存与一致性负担。决策每个 Session 只持有一个 SurfaceManager新的设计将两者收敛为一个管理器每个Session在构造时主动创建一个SurfaceManager并唯一持有它接纳流程与公共读取共用同一份增量状态。源码中的落点非常清晰packages/core/session/src/index.tsexport class Session { private log: SessionEvent[] [] /** Single incremental owner of surface acceptance and projection state. */ private readonly surfaceManager new SurfaceManager(this.log) /** The ordered surface over this sessions event log. */ get surface(): SessionSurface { return this.surfaceManager } }validateNext()提交前的原子校验种子事件与追加事件的接纳流程在事件提交进日志之前调用管理器的validateNext()。以Session.append()为例事件在log.push()之前依次经历表面元数据的 JSON 快照 →surfaceManager.validateNext(event)→ 进入日志。种子seed路径同理index.ts每次构造种子事件都会先validateNext(snapshot)再log.push(...)注释明确写道A seed is accepted incrementally through the same transition as a live append and a full-log fold. The candidate is planned before it enterslog, so a failure cannot partially mutate the surface.候选事件校验保持原子性validateNext()可以同步已提交的日志事件delta 折叠但对尚未提交的候选事件只制定变更计划plan不执行变更。实现上SurfaceManager内部维护_pendingPlan字段保存事件 期望 seq 已校验的计划而planSurfaceEvent()是纯函数——它读取当前折叠状态、做全套校验seq 连续性、surfaceOp标记合法性、sourceEventSeqs来源引用、replace 范围、tool-result 重写规则但不修改状态只有applySurfacePlan()才会真正执行 append 或 splice 替换并递增replaceGenerationsurface.ts。候选事件只有在log.push()成功之后、下一次增量同步_processDelta()时才进入管理器状态当待处理计划对应的事件恰好落在日志尾部时_processDelta()直接应用_pendingPlan否则按普通事件走applySurfaceEvent()完整折叠surface.ts。由此保证表层校验失败不会留下虚假节点或替换代数提交前internal/dispatch否决如 scoped dispatch 抛错同样不会污染表层状态——事件从未进入日志自然也不进入管理器。测试 packages/core/session/tests/surface.spec.ts 专门验证了这一原子性对一个缺少sourceEventSeqs的非法 replace 候选s.append()抛错后s.events仍只有 1 条、s.surface对象标识不变、nodes仍为[0]、replaceGeneration仍为 0且与foldSurface全量回放结果一致。session.surface只读契约下的同一对象Session.surface通过只读契约返回同一个管理器对象。消费方可以观察节点成员关系与替换代次但不能调用校验——校验方法是类上的公开成员却因SessionSurface类型收窄而不可达。这样接纳流程、session.surface、派生消息、压缩和工作区上下文观察同一份增量状态不存在重复缓存Session.surface不暴露校验方法同时保持对象标识稳定nodes是借用的只读节点数组readonly number[]恶意类型断言仍可破坏借用状态——刻意绕过只读契约的 JavaScript 调用方不属于受支持的同进程边界。这是文档明确声明的信任模型只读是契约而非运行时防线。foldSurface()分离式的完整日志回放foldSurface(events)仍是离线校验与重建使用的分离式完整日志回放函数surface.ts。它从空状态出发对每个事件依次执行计划 应用返回独立的SurfaceFoldResultexport interface SurfaceFoldResult { /** Current surface event sequences in model-visible order. */ nodes: number[] /** Replacement operations in event order. */ replacements: SurfaceFoldReplacement[] }它使用与活跃管理器相同的状态转换共享planSurfaceEvent/applySurfaceEvent/applySurfacePlan等内部函数因此对每个已提交前缀都与活跃管理器一致但不共享可变状态也不保留活跃管理器所不保留的替换历史。增量管理器只维护{ nodes, replaceGeneration }两个字段测试 surface.spec.ts 断言了活跃管理器状态中不存在replacements字段而foldSurface会完整返回每次替换的实际遮蔽 seq 列表。这意味着系统同时存在两条等价的折叠路径活跃路径增量、有界内存、每次访问 O(新增事件)与回放路径全量、纯函数、可离线重建。测试 surface.spec.ts 验证了二者在共享会话上的完全一致foldSurface(s.events).nodes与s.surface.nodes相等替换元数据逐条吻合且修改回放结果不会影响活跃表层返回的nodes是拷贝。关键校验规则管理器与回放共享的同一套契约无论走增量还是全量路径每个表面事件都要经过planSurfaceEvent()的完整校验surface.ts规则包括seq 连续性event.seq必须严格等于期望位置否则抛seq is not contiguous; expected N标记合法性产生消息的类型user/message、assistant/message、tool/result必须携带surfaceOp标记非表面类型turn/start、assistant/chunk等不得携带surfaceOp或sourceEventSeqs来源引用provenancesourceEventSeqs必须是非负安全整数组成的稠密无重复数组、全部引用更早事件assistant/message允许显式空数组并且必须覆盖 replace 操作遮蔽的每一个表层节点replace 范围start与end必须是当前表层中存在的 seq且startIdx endIdxtool-result 重写限制tool/result的 replace 只能重写恰好一个当前tool/result节点且除content外其余字段必须保持深度结构相等isDeepEqualJson为保持浏览器安全而自行实现而非依赖node:util。这些规则在 surface.spec.ts 中有密集的负面用例覆盖非数组、空数组、重复、稀疏数组、负数、自引用、跨窗口替换、多节点遮蔽、篡改toolCallId/isError/meta等。备选方案与取舍Note 中记录了两个被否决的备选方案继续分离接纳状态与投影视图。两个独立实例看似能隔离公共读取与校验但调用方取得的本来就是借用的表层状态声明的只读契约已经阻止普通修改复制管理器并不能构成运行时信任边界——只读是类型层约束而多实例复制只会带来双份状态的维护成本。每次读取都根据完整日志重新计算公共表层。该方案能消除重复缓存状态但会放弃增量派生使每次请求构造都随完整会话历史线性增长O(整个日志)在长会话上不可接受。折中结果是保留foldSurface作为全量回放函数但活跃路径始终走增量折叠。影响与验证本次收敛的直接影响接纳流程、session.surface、派生消息、压缩和工作区上下文观察同一份增量状态deriveMessages()的缓存每个 surface 节点只投影一次、replaceGeneration变化时重建也建立在这份单一状态之上Session.surface保持对象标识稳定、nodes借用数组只读校验方法不对外暴露表层、种子、调度否决、请求重建、压缩和工作区上下文测试覆盖了共享管理器与分离回放路径两条路径见 surface.spec.ts 与 session.spec.ts。延伸阅读表层概念的完整设计 .agents/notes/implemented/architecture/2026-06-18-session-surface.zh.mdSurfaceManager与SessionSurface的官方文档章节docs/subsystems/session.zh.md相关简化决策注入内容 envelope 展开 .agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.md会话持久化约定与后端docs/subsystems/persistence.zh.md【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考