
DeepSeek Harness 会话 Surface事件日志上的有序投影与历史操纵机制深度解析【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness导读本文基于 DeepSeek Harness 仓库中的 Agent Note.agents/notes/implemented/architecture/2026-06-18-session-surface.zh.md深入讲解会话模块新增的surface有序投影机制它如何在追加式事件日志之上维护一个产出 LLM 消息的事件的可派生、可缓存的有序投影如何通过surfaceOp标记记录历史操纵上下文压缩、工具结果改写并实现崩溃恢复、持久化与严格的接纳校验。读完本文你将理解 DeepSeek Harness 中deriveMessages()的派生路径、SurfaceManager的增量实现原理以及压缩类插件如何基于 surface 落地替换阴影语义。一、背景事件日志权威但历史操纵缺少持久化机制在 DeepSeek Harness 的会话模型中事件日志event log是权威数据源。会话的每一次交互都被记录为顺序追加的SessionEvent包括user/message、assistant/message、assistant/chunk、tool/call、tool/result、context/message以及各类生命周期边界事件turn/end、step/end等。然而在引入 surface 之前存在一个系统性缺口历史操纵history manipulation没有持久化的共享机制。具体表现为上下文压缩context compaction等插件通过顺序敏感的监听器listener改写派生请求但不记录每次替换使用了哪些事件每新增一种历史操纵方式都必须修改核心的deriveMessages()函数——它负责把事件日志线性扫描为发往 LLM 的消息序列而操纵语义的多样性让这条唯一的派生路径不断膨胀、难以维护。要解决这个问题需要回答几个关键问题被压缩遮蔽的旧消息事件如何被标记为不再参与派生而不是被物理删除压缩生成的新事件如何准确表达我替换了哪些旧事件回放replay时如何确定性地重建出与请求时完全一致的消息序列surface 机制正是为回答这些问题而设计。二、核心决策一个派生并缓存的有序投影surface决策的核心表述是新增一个surface——事件 seq 的派生并缓存的有序投影即产出 LLM 消息的事件子集通过事件日志中的surfaceOp标记维护。关键设计理念日志仍然权威事件一旦写入就永不删除被遮蔽shadowed的事件仍然留在日志中只是不再出现在 surface 上。这让回放成为可能——任何时候都可以从完整日志重建当前投影。surface 是投影而非副本它不复制消息内容只维护参与派生的事件的seq 有序数组派生消息时按 seq 从日志中取回事件再投影为消息。增量维护SurfaceManager只处理上次同步之后新提交的事件而不是每次全量重扫日志。从源码看消息产出事件只有三类定义于 packages/core/session/src/types.tsexport type SurfaceEventType | user/message | assistant/message | tool/resultcontext/message注入的上下文也属于可进入 surface 的类型。运行时判定见 packages/core/session/src/surface.ts其中SURFACE_EVENT_TYPES集合包含user/message、assistant/message、tool/result三类。三、SessionEvent新增的两个顶层字段每个SessionEvent获得两个可选字段结构性元数据与seq/time同级。源码定义见 packages/core/session/src/types.ts1.sourceEventSeqs?: number[]被引用为数据来源的早期事件 seq 编号。典型场景构成某个assistant/message的各assistant/chunk的 seq被压缩标记遮蔽的 surface 节点的 seq。语义细节出现的[]空数组只在assistant/message上有效表示已知为空的提供方流旧格式或外部事件缺少该字段时不记录这条消息由哪些早期事件产生——这是兼容旧日志的显式放宽其他 surface 事件一旦出现此字段就必须是非空列表关键约束如果没有这些引用的 seq回放就无法验证 replace-range 操作是否列出了它移除的每个事件。也就是说sourceEventSeqs是 replace 操作可验证性的根基。2.surfaceOp?: SurfaceOp该事件如何进入 surface。非 surface 事件边界、chunk、usage、error 等不携带此字段。源码中的SurfaceIntent接口packages/core/session/src/types.ts将二者组合为Session.append()的必传参数export interface SurfaceIntent { surfaceOp: SurfaceOp sourceEventSeqs?: number[] }SessionEvent类型上这两个字段被限定为仅存在于 surface 事件类型变体上通过条件类型K extends SurfaceEventType ? { ... } : object实现编译器在Session.append()调用点即强制非 surface 事件永不携带 surface 元数据。四、SurfaceOpappend 与 replace 两种操作SurfaceOp的类型定义packages/core/session/src/types.tsexport type SurfaceOp | append | { op: replace; start: number; end: number }4.1 Append普通尾部追加在尾部追加新事件的 seq。适用事件类型user/messageassistant/messagetool/resultcontext/messageagent loop智能体循环在所有此类追加上传入surfaceOp: append并在适用时记录sourceEventSeqs每个成功的assistant/message都记录完整的assistant/chunk来源集合包括空[]用于标注已知为空的流每个tool/result记录其tool/call来源。4.2 Replace阴影替换移除从start到end两端包含闭区间的条目并在其位置插入新事件的 seq。约束start和end都必须存在于当前 surface按数组位置定位而非按 seq 值比较大小start end表示替换单个条目该事件的sourceEventSeqs必须包含所有被遮蔽的 surface seq被遮蔽的事件仍留在日志中但不再出现在 surface 上——即阴影shadow语义。为什么选闭区间因为 replace 端点由 surface 事件 seq 命名单条目替换start end在闭区间语义下读起来更自然。这也是替代方案评估中否决半开区间[start, endExclusive)的原因。五、SurfaceManager增量折叠而非全量重建5.1 单一所有者一个Session拥有一个SurfaceManagerpackages/core/session/src/index.ts后者维护事件 seq 的有序number[]。Session.surface通过只读的SessionSurface约定暴露同一个管理器export interface SessionSurface { /** Current surface event sequences in model-visible order. */ readonly nodes: readonly number[] /** Monotonic count of committed positional replacements. */ readonly replaceGeneration: number }见 packages/core/session/src/surface.ts这意味着接纳admission、派生历史、压缩与工作区上下文共享同一份增量状态——不存在多份可能互相漂移的投影副本。5.2 增量处理算法SurfaceManager的增量策略packages/core/session/src/surface.ts构造时接收完整日志或加载的事件窗口以及窗口首事件的绝对 seqbaseSeq_lastProcessedSeq记录上次已处理的绝对 seq每次访问nodes/replaceGeneration或调用validateNext时若日志尾部有新事件则调用_processDelta()只折叠新追加的部分validateNext会在不改变已提交 surface 状态的前提下先对下一个候选事件做完整校验并生成待定计划_pendingPlan待事件真正写入日志后再应用该计划。复杂度无新事件时增量处理为 O(1)有新事件到达时为 O(新事件数)。5.3 单数组表达顺序Replace 按数组位置定位两个端点均包含在范围内并把替换 seq splice 到该范围。不会用第二个管理器、链接对象或 seq 到节点的 map 来重复表达顺序。这是替代方案评估中的明确取舍生产代码不读取前驱链接唯一的后继用途就是数组中的下一个位置而替换本来就需要线性indexOf查找。单个 seq 数组在保留相同渐进复杂度的同时只留下一个需要校验的表示降低了状态漂移风险。5.4 完整的折叠函数与浏览器安全除了增量管理器surface 模块还导出了纯函数foldSurfacepackages/core/session/src/surface.ts完整回放一段连续日志返回{ nodes, replacements }——当前 surface 事件 seq 序列以及按事件顺序记录的替换操作元数据SurfaceFoldReplacementseq/start/end/shadowedSeqs。外部重建器与纯投影可以利用它从日志前缀重建任意请求构建时的精确消息序列。值得注意的是该模块头注释明确标注Browser-safeWeb 客户端消费该子路径导出因此实现中避免了node:导入例如用自实现的isDeepEqualJson深度相等替代node:util的isDeepStrictEqual见 packages/core/session/src/surface.ts以保证 vite 打包不中断。六、deriveMessages()surface 优先旧会话线性回退deriveMessages()在存在 surface 标记时使用 surface作为唯一派生路径对没有标记的会话回退到既有的线性扫描向后兼容旧格式日志。逐节点投影规则由纯函数deriveEventMessage定义packages/core/session/src/surface.tsuser/message按原样投影为 user 角色的消息不再添加 per-type 框架如context包裹——框架由生产者烘焙进content例如 agent-instructions 用system-reminderassistant/message跳过空 content 的消息——它只用于承载 max-tokens 步骤的 usage不能把空 assistant 轮注入提供方转录tool/result投影其message其余事件类型boundary、chunk、log-only 记录投影为null不产生消息。Session的实例方法deriveMessagespackages/core/session/src/index.ts在 surface 序列基础上折叠deriveEventMessage缓存基于replaceGeneration失效——surface 被替换后重建从而保证压缩后的派生结果与回放一致。七、持久化JSONL 零改动SQLite 新增两列7.1 JSONL 后端新字段作为顶层 JSON 属性序列化。JSONL 后端无需任何改动JSON.stringify/JSON.parse透明地保留一切见 packages/session/session-persistence-jsonl 目录抽象接口不变见 packages/session/session-persistence。7.2 SQLite 后端events表新增两个可空 TEXT 列packages/session/session-persistence-sqlite/resources/sql/schema.sqlsource_event_seqs ANY, surface_op TEXT,surface_op以 TEXT 存 JSON 字符串如append或{op:replace,start:..,end:..}解码时JSON.parse还原source_event_seqs使用带 tag 的 delta / run 编码见 packages/session/session-persistence-sqlite/src/compression.ts压缩效率高于普通 JSON 数组解码端对畸形值截断 varint、越界 seq、非规范 run 等逐项抛错见 packages/session/session-persistence-sqlite/src/compression.ts所有 SELECT 语句select-events.sql、select-tail-events.sql等均已带上新列。7.3 Schema 版本策略磁盘上的SCHEMA_VERSION递增以反映列集变化当前为19见 packages/session/session-persistence-sqlite/src/schema.ts采用预发布的 bump-and-reject 策略由其他构建写入的数据库在打开时被拒绝而非迁移打开时校验user_version不匹配即抛错见 packages/session/session-persistence-sqlite/src/schema.ts。依据是没有需要升级的持久化用户数据会话格式version固定为SESSION_FORMAT_VERSION 0不稳定/预发布立场可选的 surface 字段被吸收而不递增版本号。八、崩溃恢复repair.ts合成 surface 感知的闭合事件repair.ts模块在崩溃后为孤立的工具调用合成tool/result闭合事件packages/core/session/src/repair.ts。这些闭合事件携带surfaceOp: append若工具调用已记录为 started携带指向孤立tool/call事件的sourceEventSeqs: [callSeq]内容上区分两种中断情形ToolNotStartedError调用前中断可安全重试与ToolOutcomeUnknownError结果未持久记录需先核实外部状态再决定是否重试。从而确保重建的 surface 有效——崩溃恢复不会产生游离在 surface 之外的消息或悬空的工具结果。九、不变式单记录接纳与存储投影规则surface 的校验由Session在始终启用的 seed/append 边界强制执行核心校验函数在 packages/core/session/src/surface.ts空源限制只有assistant/message可以使用空的sourceEventSeqs列表引用合法性引用必须唯一无重复、更早source event.seq且为已知事件非负安全整数替换端点存在性replace 的start/end必须存在于当前 surface 顺序中且start的索引不晚于end的索引replacementRange校验覆盖性sourceEventSeqs必须覆盖每个被遮蔽的节点assertProvenance中missing检查类型限制非 surface-eligible 事件携带surfaceOp或sourceEventSeqs会被拒绝surface-eligible 事件缺少surfaceOp也会被拒绝surfaceOpOf。这些是单记录接纳与存储投影规则由Session内建执行不依赖可选的诊断插件服务。9.1 类型级强制 运行时兜底类型系统强制类型化的append重载对字面事件类型强制执行surface 事件必须携带SurfaceIntentpackages/core/session/src/index.ts运行时兜底append和种子构造函数中的运行时检查覆盖宽化联合类型和加载的日志按照预发布格式策略无效的种子被拒绝而非升级——绝不静默地修复历史数据。9.2 tool/result 替换的特殊规则一次tool/result替换只能改写当前的一个tool/result并且必须保留除content以外的每个数据字段。assertToolResultRewritepackages/core/session/src/surface.ts在把 content 置空后做深度结构相等比较任何额外字段差异都会拒绝该替换。Session 接纳会与位置范围和引用的源事件校验一起强制这条规则不依赖可选的诊断插件。十、生产调用链agent loop 与压缩插件的落地10.1 agent loop逐事件记录来源packages/core/agent-loop/src/agent.ts 中的追加调用展示了 append 语义的完整落地user/message追加{ surfaceOp: append }assistant/message追加{ surfaceOp: append, sourceEventSeqs: chunkSeqs }——收集本轮所有assistant/chunk的 seq 作为来源tool/result追加{ surfaceOp: append, sourceEventSeqs: [callSeq] }见 packages/core/agent-loop/src/tool-calls.ts引用其对应的tool/callseq。packages/core/agent-loop/src/runtime-context.ts 还展示了反向使用通过event.sourceEventSeqs?.includes(seq)判断某个事件是否被后续消息引用用于运行时上下文管理。10.2 压缩插件replace 的实际消费方surface 是历史操纵赖以落地的基础——dsh-compaction 的压缩就搭载于其上。压缩或 tool-result-pruner 插件的操作模式是追加一个既有的消息产出事件类型例如一条携带摘要的user/message附带surfaceOp: { op: replace, start, end }附带覆盖被遮蔽条目的sourceEventSeqs。新事件在 surface 上取代该范围的位置而插件自身的 trace 事件如compaction/start、compaction/end不进入 surface。回放以确定性方式保留该决策。真实实现见 packages/compaction/compaction-basic/src/region.tssession.append(user/message, checkpointMessage, { surfaceOp: { op: replace, start, end }, sourceEventSeqs: [startEvent.seq, summaryEvent.seq, ...shadowedSeqs], })注意sourceEventSeqs同时包含被遮蔽的shadowedSeqs以及压缩摘要事件本身的 seq——精确记录了新消息的完整来源链。同一模块还通过session.surface.replaceGeneration的单调递增检测并发替换若在摘要生成期间 surface 被再次替换则从替换后的 surface 重试见 packages/compaction/compaction-basic/src/index.ts避免基于过期投影提交替换。十一、曾考虑的替代方案及否决理由文档明确记录了四项备选设计与否决理由理解它们有助于把握 surface 的边界替代方案否决理由逐插件的agent/request包装surface 之前的历史操纵模式监听器排序脆弱、无法持久记录改动内容且每种新操纵都迫使核心deriveMessages()再次修改半开区间[start, endExclusive)的 replace 范围端点由 surface 事件 seq 命名单条目替换start end在闭区间语义下读起来更自然链接节点对象加 seq map生产代码不读取前驱链接唯一的后继用途就是数组中的下一个位置而替换本来就需要线性indexOf查找单个 seq 数组保留相同渐进复杂度且只有一个表示需要校验脏标记后全量重建替代增量处理在会话生命周期内为 O(N²)每次单事件追加都要重新扫描所有先前事件十二、影响面跨包改动一览surface 落地触及的包与职责划分均与 Agent Note 的后果一节对应packages/core/sessionsurface.tsSurfaceManager维护用于候选接纳和实时投影的有序 seq 数组SessionSurface是其只读公共视图SurfaceOp/SurfaceIntent与顶层会话事件字段记录条目如何加入它append()要求 surface 事件携带SurfaceIntentderiveMessages()以遍历 surface 作为唯一派生路径repair.ts发出 surface 感知的闭合事件种子构造函数拒绝缺少surfaceOp标记的可进入 surface 的种子事件packages/core/agent-loop所有涉及 surface 事件的追加操作都传入 surface 选项每个assistant/message引用产生它的分片 seq每个tool/result引用它的tool/callseqpackages/session/session-persistence-sqliteevents表新增两个可空 TEXT 列source_event_seqs、surface_opSCHEMA_VERSION递增bump-and-reject无迁移packages/session/session-persistence-jsonl无需改动packages/session/session-persistence抽象接口不变。对应测试覆盖集中在 packages/core/session/tests/surface.spec.tssurface 折叠与校验、packages/core/session/tests/repair.spec.ts崩溃恢复闭合事件以及 packages/session/session-persistence-sqlite/tests/compression.spec.tssource_event_seqs编码解码的畸形输入防护。结语Surface 机制为 DeepSeek Harness 的会话历史管理提供了一个关键抽象日志是事实surface 是共识。通过surfaceOpappend / replace与sourceEventSeqs来源引用两个结构性字段上下文压缩、工具结果改写等历史操纵第一次拥有了持久化、可回放、可验证的表达方式SurfaceManager的增量折叠保证运行期开销可控O(1) / O(新事件数)而deriveMessages()的 surface 优先 线性回退策略让新旧格式会话无缝共存。对于想扩展会话历史操纵能力的开发者surface 就是新操纵赖以落地的标准接口。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考