docs: replace vague provenance prose with recorded facts

This commit is contained in:
Turtle
2026-08-09 15:35:02 +08:00
parent 8c124f84b6
commit 9704749b01
380 changed files with 946 additions and 874 deletions

View File

@@ -8,7 +8,7 @@
## `SessionEventMap`:事件词汇
仅追加的事件类型。可通过声明合并扩展:插件通过 declaration merging 声明额外的事件类型。例如[压缩compaction seam](compaction.md) 添加了 `compact/start` / `compact/summary` / `compact/end``@deepseek-ai/dsh-hook-protocol` 添加了仅记录日志的 `hook/invoked` / `hook/result` 溯源事件,用于钩子桥接。与 `compact/*` 一样,这些都不是 `SurfaceEventType`(没有 `surfaceOp`)。生成的[持久化日志事件目录](../persistence-catalog.md)列举了所有成员(核心与合并扩展的),包含其 payload、surface 标记与声明位置。
仅追加的事件类型。可通过声明合并扩展:插件通过 declaration merging 声明额外的事件类型。例如[压缩compaction seam](compaction.md) 添加了 `compact/start` / `compact/summary` / `compact/end``@deepseek-ai/dsh-hook-protocol` 为钩子桥接添加了仅记录日志的 `hook/invoked` / `hook/result` 记录。与 `compact/*` 一样,这些都不是 `SurfaceEventType`(没有 `surfaceOp`)。生成的[持久化日志事件目录](../persistence-catalog.md)列举了所有成员(核心与合并扩展的),包含其 payload、surface 标记与声明位置。
```ts type-equiv
/** A user-role specialization of the one shared message representation. */
@@ -153,7 +153,7 @@ interface TodoItem {
### 请求头事件:`request/header`
请求信封(即 `EpochHeader`:调用配置 + 适配器默认值来源 + 渲染后的系统提示词 + 已组装的工具 schema会作为会话状态写入日志因此每个对话请求都是日志的纯函数见可重建性 Agent Note。带有 reason `'initial'` 或 `'resume'` 的完整 `request/header` 快照记录每个 agent loop 实例的边界;之后请求发生变化时,系统会以 reason `'change'` 记录另一份完整快照。`foldRequestHeader(events)` 通过选择最新快照重建请求头。该事件不是 `SurfaceEventType`,不产生 LLM 消息。
请求信封(即 `EpochHeader`:调用配置 + 适配器所提供默认值的标记 + 渲染后的系统提示词 + 已组装的工具 schema会作为会话状态写入日志因此每个对话请求都是日志的纯函数见可重建性 Agent Note。带有 reason `'initial'` 或 `'resume'` 的完整 `request/header` 快照记录每个 agent loop 实例的边界;之后请求发生变化时,系统会以 reason `'change'` 记录另一份完整快照。`foldRequestHeader(events)` 通过选择最新快照重建请求头。该事件不是 `SurfaceEventType`,不产生 LLM 消息。
```ts type-equiv
/**
@@ -219,11 +219,11 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
data: SessionEventMap[K]
} & (K extends SurfaceEventType ? {
/**
* Seq numbers of events that are provenance sources of this event
* Seq numbers of earlier events that this event cites as sources
* (e.g. the `assistant/chunk` seqs that built an `assistant/message`,
* or the surface nodes shadowed by a compaction replace node). An
* `assistant/message` may carry a present empty array for a known empty
* provider stream; omission means unrecorded provenance.
* provider stream; omission means the source stream was not recorded.
*/
sourceEventSeqs?: number[]
/** How this event entered the surface; absent for non-surface events. */
@@ -234,7 +234,7 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
`SessionEventType = keyof SessionEventMap`。由于 `SessionEventMap` 可通过合并扩展,对 `SessionEvent` 的 switch 语句禁止使用 `assertNever`:插件添加的变体是合法的未知值;处理已知 case 后在 `default` 中放行。
对于 `assistant/message`,存在的 `sourceEventSeqs: []` 表示提供方流已知且完整地为空;字段缺失则表示旧格式或其他未记录溯源信息的情况。agent loop 会为每次成功的模型调用写入该字段;其他 surface 事件只要包含该字段,其列表就必须非空。
对于 `assistant/message`,存在的 `sourceEventSeqs: []` 表示提供方流已知且完整地为空;字段缺失则表示旧格式或外部事件没有记录源流。agent loop 会为每次成功的模型调用写入该字段;其他 surface 事件只要包含该字段,其列表就必须非空。
## Surface 类型
@@ -280,15 +280,15 @@ type SurfaceOp =
```ts type-equiv
/**
* Surface placement and provenance for {@link Session.append}. Required on
* Surface placement and cited source-event seqs for {@link Session.append}. Required on
* message-producing events and forbidden on log-only events.
*/
interface SurfaceIntent {
surfaceOp: SurfaceOp
/**
* Complete known provenance source set. `assistant/message` may use a
* Complete set of known source-event seqs. `assistant/message` may use a
* present empty array for a known empty provider stream; omission means its
* provenance was not recorded. Other surface events require a non-empty set
* source stream was not recorded. Other surface events require a non-empty set
* when this field is present.
*/
sourceEventSeqs?: number[]
@@ -297,7 +297,7 @@ interface SurfaceIntent {
对 `SurfaceEventType` 事件必填:每个产生消息的事件都必须声明它如何加入 surface派生模型历史的唯一来源。面向人类的记录transcript是另一个投影读取的是日志中追加来源的事件因为 surface 会有意遮蔽替换所概括的范围(见 [dsh-session](../../packages/core/session/README.md) 的 `isAppendSurfaceEvent`)。非 surface 类型在编译期拒绝此参数。
此处适用相同的溯源区分:只有 `assistant/message` 可以携带存在但为空的 `sourceEventSeqs`;省略该字段并不表示源流为空。
只有 `assistant/message` 可以携带存在但为空的 `sourceEventSeqs`;省略该字段表示源流没有记录,而不是表示源流为空。
### `SessionSurface`:实时只读 surface 投影
@@ -432,8 +432,8 @@ declare class Session {
* @param type - The event type (key of {@link SessionEventMap}).
* @param data - The event payload; must be JSON-serializable.
* @param opts - Surface metadata: `surfaceOp` controls how the event enters
* the ordered surface; `sourceEventSeqs` records provenance (the seq
* numbers of events this one derives from). REQUIRED for
* the ordered surface; `sourceEventSeqs` lists the seq numbers of earlier
* events this one derives from. REQUIRED for
* {@link SurfaceEventType} events (every message-producing event must
* declare how it joins the surface, the sole source of derived model
* history) and
@@ -447,7 +447,7 @@ declare class Session {
* circular reference, sparse array, or an exotic object such as
* Map/Set/Date/class instance), or when the candidate violates the
* canonical surface contract (marker shape and eligibility, unique
* earlier provenance, positional replacement validity, and complete
* earlier source-event references, positional replacement validity, and complete
* shadowed-node coverage). One recursive pass reads, validates, and
* copies each nested value once, so a stateful getter cannot supply one value
* to validation and another to storage. The event log is the durable source
@@ -510,11 +510,11 @@ declare class Session {
`Session.deriveMessages()` 将事件日志投影为模型看到的 `Message[]`。它是缓存的(每个 surface 节点在首次出现时投影一次surface 重写触发重建)且冻结的(每次调用返回一个新数组,引用共享的深冻结消息,因此通过投影修改已记录的历史在类型上不可表达)。`deriveEventMessage(event)` 是折叠所应用的逐节点纯函数,公开暴露以便外部重建器和开发不变式检查能以完全相同的规则投影日志前缀,不会与缓存产生分歧。投影规则:
- `user/message` → 一条携带确切 `content` 的 user 消息;可选 envelope 仅作为日志中的展示元数据保留。
- `assistant/message` → 一条 assistant 消息,包含事件的提供方/模型溯源信息和可选的适配器私有回放状态。原始 `assistant/chunk` 事件属于回放/UI 数据,在派生时会被**跳过**(组装后的消息才是权威)。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 以承载用量和溯源信息,但无内容的 assistant 轮次不得进入提供方 transcript文本记录
- `assistant/message` → 一条 assistant 消息,包含生成它的提供方模型,以及可选的适配器私有回放状态。原始 `assistant/chunk` 事件属于回放/UI 数据,在派生时会被**跳过**(组装后的消息才是权威)。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 来保存用量、提供方和模型,但无内容的 assistant 轮次不得进入提供方 transcript文本记录
- `tool/result` → 一条携带 `tool-result` 块的 user 消息。
- `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`溯源信息与领域数据都在其类型化 source
- `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`;其类型化 source 标明生产方,并携带所有生产方专用数据
其余所有事件(`turn/*`、`step/*`、插件所有的 `llm/retry`均为结构信息不会投影为消息。token 记账读取每个步骤的 `assistant/chunk { type: 'usage' }` 记录;如果没有用量分片,则将 `assistant/message.usage` 作为已提交步骤的后备。失败的模型请求尝试没有 assistant 消息因此其用量分片是持久化的记账记录。由于这一尚未发布的格式有意不提供兼容性承诺seed/load 校验会拒绝缺少提供方模型的请求头,以及缺少提供方/模型溯源信息的 assistant 消息,而不会猜测历史数据应走的提供方路由。
其余所有事件(`turn/*`、`step/*`、插件所有的 `llm/retry`均为结构信息不会投影为消息。token 记账读取每个步骤的 `assistant/chunk { type: 'usage' }` 记录;如果没有用量分片,则将 `assistant/message.usage` 作为已提交步骤的后备。失败的模型请求尝试没有 assistant 消息因此其用量分片是持久化的记账记录。由于这一尚未发布的格式有意不提供兼容性承诺seed/load 校验会拒绝没有提供方模型的请求头 assistant 消息,而不会猜测历史数据应走的提供方路由。
## 活跃会话 fork API
@@ -581,9 +581,9 @@ interface TurnEndReasonMap {
## 插件贡献的仅日志事件
插件可以通过 declaration merging 添加额外的 `SessionEventMap` 类型。这些是**仅日志**事件:不是 `SurfaceEventType`(不携带 `surfaceOp`,不参与派生历史)。事件所有方决定它们属于一个开放的执行轮次,还是可以独立位于轮次之间,并在自己的不变量配套插件中强制所需关系。完整的逐事件枚举(核心与插件贡献的,含 payload 与溯源信息)见生成的[持久化日志事件目录](../persistence-catalog.md);压缩 seam 的 `compact/*` 语义在 [compaction.md](compaction.md) 中讨论。
插件可以通过 declaration merging 添加额外的 `SessionEventMap` 类型。这些是**仅日志**事件:不是 `SurfaceEventType`(不携带 `surfaceOp`,不参与派生历史)。事件所有方决定它们属于一个开放的执行轮次,还是可以独立位于轮次之间,并在自己的不变量配套插件中强制所需关系。生成的[持久化日志事件目录](../persistence-catalog.md)会列出每个核心或插件贡献的事件,以及其 payload、surface 标记、被引用的来源事件 seq 和声明位置;压缩 seam 的 `compact/*` 语义在 [compaction.md](compaction.md) 中讨论。
钩子桥接层的 `hook/invoked` / `hook/result` 溯源对(来自 `@deepseek-ai/dsh-hook-protocol`)通过 `handlerId` 关联。`UserPromptSubmit`、`PreToolUse`、`PostToolUse` 与 `Stop` 在 loop 已打开的轮次内触发,因此其 `hook/*` 记录天然位于轮次之内。`SessionStart` 不生成 `hook/*` 记录,因为它在轮次 1 之前运行;其上下文会在 inbox 中保持待处理,直到唤醒交付打开一个轮次(见[钩子桥接 Agent Note](../../.agents/notes/implemented/feature/2026-06-30-hook-bridges.md))。
钩子桥接层的 `hook/invoked` / `hook/result` 对(来自 `@deepseek-ai/dsh-hook-protocol`)通过 `handlerId` 关联。`UserPromptSubmit`、`PreToolUse`、`PostToolUse` 与 `Stop` 在 loop 已打开的轮次内触发,因此其 `hook/*` 记录天然位于轮次之内。`SessionStart` 不生成 `hook/*` 记录,因为它在轮次 1 之前运行;其上下文会在 inbox 中保持待处理,直到唤醒交付打开一个轮次(见[钩子桥接 Agent Note](../../.agents/notes/implemented/feature/2026-06-30-hook-bridges.md))。
## 持久性约定