# Agent Note: 跨会话引用

Status: implemented

[English](2026-07-21-cross-session-references.md) | 中文

## 问题

TUI 用户需要把另一场对话中的相关工作带入一条新消息，但不恢复、不 fork，也不让源 transcript（文本记录）对当前会话拥有权威性。harness 已经提供准确的会话枚举与原始事件检查，但若每个宿主都独立解析日志，就会重复实现压缩（compaction）折叠、按被引用来源事件 seq 过滤、大小限制、错误行为和持久化。把宿主标记直接编码进 agent（智能体）消息约定，还会让核心循环绑定某一种 UI 语法。

## 决策

`@deepseek-ai/dsh-session-reference` 是注册在 `ctx.sessionReferenceResolver` 上的单一上下文消费服务。宿主先把各自的协议规范化为 `SessionReferenceInput[]`，并在交付前调用 `prepare()`。该服务返回分离的可读内容和一份可选的、带标识且冻结的 `UserMessage` 快照；核心 agent 包既不解析会话 URI，也不读取其他日志。

`dsh-session:<base64url(JSON.stringify(sessionId))>` 是与宿主无关的规范标识符。系统先执行 JSON 字符串编码，再执行 base64url 编码，因此引号、正斜杠、反斜杠、Unicode、换行符以及其他任意 JavaScript 字符串值都能无损往返，不会因分隔符产生歧义。TUI 把该 URI 渲染到 `@[label](uri)` 中；纯文本客户端可以使用同一种行内提及标记。显式 Markdown 提及标记会拒绝格式错误的 URI。裸文本只有在负载非空且形状符合 base64url 时才会成为引用，而且解码过程仍须通过规范性校验；空负载或只含标点符号的用法仍按普通讨论文本处理。

该服务使用 `ctx.sessionQuery.readSurface(sessionId)`：它优先从实时会话加载一次语料观察结果，使用会话包的规范表层算法执行折叠，并返回与源数据分离的会话头、捕获序号和当前节点。FTS 不是依赖项：候选发现会匹配 id、cwd 或最新折叠后的标题，而消息主体不进入候选层。非空查询会对可见语料中的标题观察结果执行批处理，以有界并发读取持久化日志，并支持取消；专用标题索引可以替换这条发现路径，而无需改变引用标识或准备过程。

## 快照与投影

准备过程按首次出现的顺序去重、拒绝目标会话自身的 id，并且执行可配置的数量限制，但引用硬上限为三个，所有读取均并行执行。该过程不会返回部分完成的上下文：任何读取、取消、校验或预算错误都会在调用 `followup()` 或 `steer()` 前拒绝本次操作。取消会与进行中的候选发现和精确读取竞速，因此即使持久化后端无法中断待处理操作，宿主也能及时结束等待；后端迟到的完成结果仍会被观察，但不能让消息入队。源会话在入队前完成读取，因此源会话后续新增消息、执行压缩、被删除或替换持久化内容，都无法改变目标会话中的快照。

投影会保留直接用户消息与 steering（中途引导）、已完成的 assistant 文本，以及携带由 `dsh-compaction` 导出的规范来源标记的检查点用户消息。该标记属于压缩能力约定的一部分，而非某个后端包名称。当源提示词已包含合并写入的前缀上下文时，投影只读取其模型不可见的显示内容，因此后续引用该目标不会递归传播先前的快照。投影会排除压缩前已被遮蔽的节点、工具及其结果、推理（reasoning）、注入的上下文、其他插件用户消息、仅用于日志的记录，以及尚未完成的 assistant 分片。因此，重复压缩只会暴露当前表层仍保留的最新折叠检查点谱系及其尾部消息；系统不提供 raw/current 开关，也不恢复被遮蔽的内容。

系统把一个聚合上下文序列化为 JSON，并置于固定的不可信背景警告之后。该警告要求模型不要遵循被引用会话中的指令、权限声明或工具请求，除非当前用户再次提出这些内容。标签安全序列化会把数据中的每个 `<` 无损转义为 JSON `\u003c`；因此源字符串无法拼出外围类似 XML 的标签，也无法逃逸数据区域。同一个序列化器会独立核算每个源的字节数。AgentLoop 会把快照持久化为一条带来源信息的 `user/message`，紧接在直接 `user/message` 之前。因此，目标回放无需新增事件类型、放置模式或提示词封套，也能满足「模型可见／日志可重建」不变量。

## 消息所有权

TUI 负责快照／直接消息事务，不扩展通用收件箱记录。agent 空闲时，它会在调用 `followup()` 前安装一次性的外层 `agent/pre-step` 监听器；enter 决策会把快照作为另一条消息接收，而 reject 或更早的普通丢弃会释放监听器，并且不写入任何消息。agent 运行时，TUI 会依次调用 `inject(snapshot)` 和 `steer(prompt)`，把两者放入 next-step inbox，等待后续同一次领取。pre-step reject 或失败会使这对已领取消息维持已移除状态；领取后插入的消息继续等待。这一通用交付边界由[上下文分离决策](../architecture/2026-07-24-separate-context-injection-from-turn-execution.md)规定。

引用准备过程不是新的 steering 协议，本身也不会创建轮次。空闲交付使用 `followup()` 和 pre-step 进入决策；运行期间的交付使用共享 next-step inbox，并保持快照顺序。

## 宿主适配器

TUI 把会话候选与现有 `@` 文件提供方组合在一起。候选查询会对 session id、cwd 或最新折叠后的标题执行不区分大小写的子串匹配，显示该标题，并在没有标题观察结果或标题观察失败时回退到 session id。候选查询遵循编辑器的取消信号；session id、cwd 和提及标签中的外部终端控制字符会被转义，但规范 URI 仍保留原始 id。TUI 只准备包含结构化提及标记的提交；等待快照时禁用重复提交；失败时恢复输入；它把可读的直接内容渲染为用户消息，并把会话引用来源元数据渲染为精简的来源列表，不在终端中暴露完整 JSON。

[仅面向自动化的 ACP（Agent Client Protocol）传输层](../simplification/2026-07-23-acp-automation-only-protocol.md)有意不挂载会话查询或会话引用服务。

## 预算与保留策略

最多三个引用中的每一个默认独立限制在 65,536 个 UTF-8 字节以内。保留策略会优先保留当前压缩检查点和最新的对话单元，再丢弃较旧的非检查点消息。若保留文本过大，系统使用 `dsh-output-retention` 进行首尾切片并记录准确的省略字节数；若某个源的固定序列化字段无法装入其上限，整个准备过程会失败，不会输出部分上下文。

## 考虑过的替代方案

- **等待 SQLite FTS5**：不予采纳，因为快照正确性依赖按准确 id 读取和规范表层折叠，而不是内容搜索。FTS 只改进候选发现。
- **把提及标记语法放入 agent 投递方法**：不予采纳，因为这会迫使核心协议解析某个宿主的展示语法，并阻止带类型的非文本宿主复用同一语义层。
- **在每个宿主中分别实现引用**：不予采纳，因为投影、安全警告、保留策略和持久化会在不同宿主之间逐渐偏离。
- **把上下文附加到 `SendOptions` 和直接提示词的收件箱记录**：不予采纳，因为通用投递将不得不负责贯穿准入、steering、取消和观察的领域事务。领域专用的准入包装层和现有 next-step inbox 可以保持所需配对，而无需扩大每条直接提示词。
- **在调用 `followup()` 前由宿主合并前缀**：不予采纳，因为 `agent/pre-step` 必须只检查和改写直接提示词。将快照保留为独立的带来源消息，可以维持该边界，并让 TUI 从直接用户气泡中隐藏背景字节。
- **回放原始源日志或恢复被遮蔽的事件**：不予采纳，因为压缩定义了当前模型表层，并且可能有意淘汰敏感或开销高昂的历史内容。
- **恢复或 fork 源会话**：不予采纳，因为本功能只为一条目标消息提供只读背景，不提供身份或生命周期连续性。
- **在请求时重新读取源会话并注入**：不予采纳，因为这会让引用变得不确定，取消竞态可能改变其字节内容，目标回放也会依赖可变的外部状态。

## 验证

单元与集成测试覆盖 URI 无损往返与文本边界标点、显式格式错误的引用、按 id／cwd／标题进行候选匹配与排序、标题观察失败时的回退、候选查询取消、终端控制字符转义、投影排除规则、快照的非递归投影、与后端无关的压缩检查点、标签安全封套、去重、自引用、数量限制、读取的全有或全无、存储读取不结束时取消提示词、逐源独立字节保留、提示词阻止、准入期间的暂存、send／steer 放置方式、标题隔离、能力缺失和压缩场景下的 TUI 回放。一个无密钥终端快照会在会话 id 不透明的情况下输入一个只与标题匹配的子串，并固定渲染出的候选项。另一个无密钥 TUI 快照会运行真实的 agent loop（智能体循环）：源表层用一个压缩检查点替换旧的用户／assistant 历史，目标会话提交一个提及标记，捕获到的模型请求包含一条带来源的快照消息，后面跟随可读的当前提示词，并且不包含任一被遮蔽的字符串。

## 后果

新插件构成稳定的语义边界，不会新增持久化 schema、事件类型、FTS 依赖、源会话订阅或对压缩所遮蔽内容的访问。标准 TUI 演示组合包会显式挂载它，并在自身配置中暴露引用数量和逐源字节上限；自定义宿主在挂载该服务并适配输入前保持不变。引用上下文会在配置的界限内增大目标历史，随后可由目标会话的普通压缩进行摘要；完成压缩后，源会话便不再相关。
