mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
docs(session): separate experimental persistence catalogue
This commit is contained in:
1 parent
bb405d51fa
commit
c954a07535
23 files changed
+164
-68
No files matched your search
+4
-4
@@ -9,11 +9,11 @@
|
||||
en: d2df1eb3602af45d
|
||||
zh: fd162b1c214f1494
|
||||
/agent-note-retain-ignorable-session-events-for-external-plugins/decision:
|
||||
en: 1bf1cc3675ef0dbd
|
||||
zh: f716038fad00711f
|
||||
en: c78e92c0cc5a7eb2
|
||||
zh: 62b29ccc5cd142f2
|
||||
/agent-note-retain-ignorable-session-events-for-external-plugins/alternatives-considered:
|
||||
en: f17e0fadb751ec55
|
||||
zh: fa54b1bf42afdb76
|
||||
en: c91cdee1dda42176
|
||||
zh: 870dfbda591abb76
|
||||
/agent-note-retain-ignorable-session-events-for-external-plugins/consequences:
|
||||
en: b14df3e7f7489748
|
||||
zh: ac227743cf5c11f7
|
||||
+2
-2
@@ -18,7 +18,7 @@ The field is removable only after a replacement supports the current third-party
|
||||
|
||||
`appendPluginRecord()` in `@deepseek-ai/dsh-session` is the one first-party writer of the marker. It appends ignorable records whose type is in the `plugin:` namespace, which the V3-to-V4 edge already assigns to unknown ignorable V3 events, and only production source under `packages/experimental/` may call it; the `verify-plugin-record-callers` gate enforces that, and the persistence catalog generator rejects a `SessionEventMap` member in the namespace. Experimental packages keep state there that their owners can lose: a format migration retains records on a best-effort basis, so a release package declares its events instead.
|
||||
|
||||
Experimental packages declare record names and payload types in the separate `PluginRecordMap`. The map types writes and supplies a compact current-source section in the persistence catalog, making plugin-owned persistence discoverable without expanding its payload schemas or recording released type history. It does not add names to `KNOWN_SESSION_EVENT_TYPES` or change stored-event admission. The reader returns unknown payloads for owner validation, including records whose declarations have changed or disappeared.
|
||||
Experimental packages declare record names and payload types in the separate `PluginRecordMap`. The map types writes and supplies the dedicated [experimental persistence catalog](../../../../docs/experimental-persistence-catalog.md), making plugin-owned persistence discoverable from current source without expanding its payload schemas or recording released type history. It does not add names to `KNOWN_SESSION_EVENT_TYPES` or change stored-event admission. The reader returns unknown payloads for owner validation, including records whose declarations have changed or disappeared.
|
||||
|
||||
Historical format migration is deliberately stricter in the alpha implementation. The v0-to-v1 edge refuses every unknown v0 type, including an ignorable one, because an opaque payload may contain references that a format edge cannot validate. The [alpha historical-event decision](2026-08-31-alpha-historical-unknown-event-refusal.md) owns that bounded exception; equal-version append and reload continue to follow this note.
|
||||
|
||||
@@ -32,7 +32,7 @@ Historical format migration is deliberately stricter in the alpha implementation
|
||||
|
||||
**Declare a `SessionEventMap` member for each experimental package's state.** Rejected because each member is required-on-read and enters the released-schema inventory and type history: a build without the experimental package refuses the Session, and removing the package leaves a released type behind.
|
||||
|
||||
**Omit plugin records from the persistence catalog.** Rejected because maintainers need to find in-repository persistence writers. Current-source declarations provide that inventory while their payloads retain best-effort compatibility; a full schema history would impose the released-event obligations that these records deliberately avoid.
|
||||
**Keep no catalog of plugin records.** Rejected because maintainers need to find in-repository persistence writers. The dedicated experimental catalog provides that inventory from current-source declarations while their payloads retain best-effort compatibility; a full schema history would impose the released-event obligations that these records deliberately avoid.
|
||||
|
||||
**Let any package call the record writer.** Rejected because records survive a format migration only on a best-effort basis, and a release package must not keep state that an upgrade can drop.
|
||||
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ Status: implemented
|
||||
|
||||
`@deepseek-ai/dsh-session` 中的 `appendPluginRecord()` 是该标记唯一的第一方写入方。它追加类型位于 `plugin:` 命名空间的 ignorable 记录(V3-to-V4 迁移边已把未知的 ignorable V3 事件放入该命名空间),且只有 `packages/experimental/` 下的生产源码可以调用它;`verify-plugin-record-callers` 门禁强制这一点,持久化目录生成器也拒绝该命名空间中的 `SessionEventMap` 成员。实验性包在这里保存其所有者可以丢失的状态:格式迁移以尽力而为的方式保留记录,因此正式发布的包改为声明自己的事件。
|
||||
|
||||
实验性包在独立的 `PluginRecordMap` 中声明记录名称与 payload 类型。该映射约束写入类型,并为持久化目录提供精简的当前源码小节,让维护者能够找到插件自有的持久化数据,而无须展开其 payload schema 或记录已发布类型历史。它不向 `KNOWN_SESSION_EVENT_TYPES` 添加名称,也不改变已存事件的准入。读取方返回未知类型的 payload,由所属包校验,其中也包括声明已变更或消失的记录。
|
||||
实验性包在独立的 `PluginRecordMap` 中声明记录名称与 payload 类型。该映射约束写入类型,并生成独立的[实验性持久化目录](../../../../docs/experimental-persistence-catalog.zh.md),让维护者能够从当前源码找到插件自有的持久化数据,而无须展开其 payload schema 或记录已发布类型历史。它不向 `KNOWN_SESSION_EVENT_TYPES` 添加名称,也不改变已存事件的准入。读取方返回未知类型的 payload,由所属包校验,其中也包括声明已变更或消失的记录。
|
||||
|
||||
Alpha 实现中的历史格式迁移有意更严格。v0-to-v1 迁移边会拒绝每个未知 v0 类型,包括 ignorable 类型,因为不透明 payload 可能包含格式迁移边无法校验的引用。[Alpha 历史事件决策](2026-08-31-alpha-historical-unknown-event-refusal.zh.md)定义该有限例外;同版本 append 与 reload 继续遵循本记录。
|
||||
|
||||
@@ -32,7 +32,7 @@ Alpha 实现中的历史格式迁移有意更严格。v0-to-v1 迁移边会拒
|
||||
|
||||
**为每个实验性包的状态声明 `SessionEventMap` 成员。** 不予采用,因为每个成员都是读取必需的,并会进入已发布 schema 清单及类型历史:没有该实验性包的构建会拒绝该会话,移除该包后还会留下一个已发布的类型。
|
||||
|
||||
**不在持久化目录中列出插件记录。** 不予采用,因为维护者需要找到仓库内的持久化写入方。当前源码声明提供这份清单,而 payload 仍然只有尽力而为的兼容性;完整的 schema 历史会施加这些记录刻意不承担的已发布事件义务。
|
||||
**不维护插件记录目录。** 不予采用,因为维护者需要找到仓库内的持久化写入方。独立的实验性目录根据当前源码声明提供这份清单,而 payload 仍然只有尽力而为的兼容性;完整的 schema 历史会施加这些记录刻意不承担的已发布事件义务。
|
||||
|
||||
**允许任何包调用记录写入方。** 不予采用,因为记录只能以尽力而为的方式经受格式迁移,而正式发布的包不得保存升级可能丢弃的状态。
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
en: 77c9c26d94fb33ca
|
||||
zh: 313c54c45cfc7dbc
|
||||
/cookbook-adding-a-session-log-format-version/3-implement-per-artifact-stages-and-validation:
|
||||
en: d7741b19c2d4daa8
|
||||
zh: edc2971edc696008
|
||||
en: c8d879d5c9fb14ed
|
||||
zh: ea19e54c808fea0b
|
||||
/cookbook-adding-a-session-log-format-version/4-update-current-version-consumers:
|
||||
en: a285126945d02c12
|
||||
zh: e039c77cf7866ad2
|
||||
|
||||
@@ -58,7 +58,7 @@ Treat the inherited cut as a logical event count, not a physical row count. Expo
|
||||
|
||||
Define the new edge's event admission and transformation rules explicitly. The [V2-to-V3 source audit](../../packages/session/session-format-v2-to-v3/README.md#source-audit) and [alpha V0→V1 rule](../../.agents/notes/implemented/architecture/2026-08-31-alpha-historical-unknown-event-refusal.md) own the policies of those released edges, not the new edge. Do not generalize either to every edge. A change to structure or event positions requires classifying source events, payload members, and references, and explicitly deciding whether opaque data can remain valid. [Equal-version retention](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.md) alone does not prove a structural transformation safe. Validate target semantics and give each newly accepted case a rejecting counterexample; never widen older edges to hide an unsupported transformation.
|
||||
|
||||
Plugin records that `appendPluginRecord()` writes are ignorable events in the `plugin:` namespace, which experimental packages read back by type. Carry them forward under their names when the target format can represent them, and state in the edge README which records it drops otherwise; the [ignorable events decision](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.md) keeps them on a best-effort basis. Apply this policy even when a stored record has no declaration in the current-source catalog.
|
||||
Plugin records that `appendPluginRecord()` writes are ignorable events in the `plugin:` namespace, which experimental packages read back by type. Carry them forward under their names when the target format can represent them, and state in the edge README which records it drops otherwise; the [ignorable events decision](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.md) keeps them on a best-effort basis. Apply this policy even when a stored record has no declaration in the [experimental persistence catalog](../experimental-persistence-catalog.md).
|
||||
|
||||
Use native writer output as the conversion oracle: replay the same recorded LLM messages, tool outputs, and other inputs through the old and target writers. Compare migrated old output with raw target output before any format-normalizing helper; mask only documented volatile fields. For V4, ordinary tool results become flat tool-role messages with ordinary content blocks. A theoretically permitted old structure alone does not justify a new core content type.
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ pnpm run gen-session-format-catalog
|
||||
|
||||
显式定义新迁移边的事件准入与变换规则。[V2 到 V3 源审计](../../packages/session/session-format-v2-to-v3/README.zh.md#source-audit)和 [Alpha V0→V1 规则](../../.agents/notes/implemented/architecture/2026-08-31-alpha-historical-unknown-event-refusal.zh.md)分别负责对应已发布迁移边的策略,而非新迁移边的策略。不要将任一策略推广到所有迁移边。结构或事件位置变化时,必须分类源事件、载荷成员与引用,并显式判断不透明数据能否保持有效。[同版本保留](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.zh.md)本身不能证明结构变换安全。校验目标语义,并为每个新增可接受案例提供一个被拒绝的反例;绝不放宽旧迁移边来掩盖不受支持的转换。
|
||||
|
||||
`appendPluginRecord()` 写入的插件记录是 `plugin:` 命名空间中的 ignorable 事件,实验性包按类型把它们读回。目标格式能够表示它们时,以原名称继续携带;否则在迁移边 README 中说明它丢弃哪些记录。[ignorable 事件决策](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.zh.md)以尽力而为的方式保留这些记录。即使已存记录在当前源码目录中没有声明,也适用这一策略。
|
||||
`appendPluginRecord()` 写入的插件记录是 `plugin:` 命名空间中的 ignorable 事件,实验性包按类型把它们读回。目标格式能够表示它们时,以原名称继续携带;否则在迁移边 README 中说明它丢弃哪些记录。[ignorable 事件决策](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.zh.md)以尽力而为的方式保留这些记录。即使已存记录在[实验性持久化目录](../experimental-persistence-catalog.zh.md)中没有声明,也适用这一策略。
|
||||
|
||||
以原生 writer 输出作为转换对照:用相同录制的 LLM 消息、工具输出和其他输入回放旧 writer 与目标 writer。任何格式归一化 helper 运行前,比较迁移后的旧输出与目标原始输出,只屏蔽已说明的易变字段。V4 的普通工具结果变为包含普通内容块的平铺 tool-role 消息。旧类型理论上允许某种结构,本身不足以支持新增核心内容类型。
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Bilingual-pair consistency record for experimental-persistence-catalog.md (docs/i18n/README.md): per heading
|
||||
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/experimental-persistence-catalog.md
|
||||
/:
|
||||
en: a11f106978c9dce7
|
||||
zh: a11f106978c9dce7
|
||||
/experimental-plugin-record-catalog:
|
||||
en: 958fbffa8a71bc48
|
||||
zh: 01a673275685c0eb
|
||||
@@ -0,0 +1,14 @@
|
||||
<!-- Generated by scripts/gen-persistence-catalog.ts — do not edit by hand.
|
||||
Run `pnpm run gen-persistence-catalog` to regenerate. -->
|
||||
|
||||
# Experimental Plugin Record Catalog
|
||||
|
||||
English | [中文](experimental-persistence-catalog.zh.md)
|
||||
|
||||
These current-source PluginRecordMap declarations describe ignorable, log-only records owned by experimental packages. Payload annotations link to their owning source without expanding referenced types. Records stay outside the known-event set, persistence schema fingerprints, and compatibility history; format migrations retain them on a best-effort basis.
|
||||
|
||||
Run `pnpm run gen-persistence-catalog` to regenerate both persistence catalogs, their language pairs, and associated artifacts. `pnpm run verify-persistence-catalog` checks all generated files.
|
||||
|
||||
Shared Session event declarations and resolved schemas appear in the [Session persistence event catalog](persistence-catalog.md).
|
||||
|
||||
No production package currently declares a plugin record.
|
||||
@@ -0,0 +1,14 @@
|
||||
<!-- Generated by scripts/gen-persistence-catalog.ts — do not edit by hand.
|
||||
Run `pnpm run gen-persistence-catalog` to regenerate. -->
|
||||
|
||||
# 实验性插件记录目录
|
||||
|
||||
[English](experimental-persistence-catalog.md) | 中文
|
||||
|
||||
这些当前源码中的 PluginRecordMap 声明描述实验性包拥有的 ignorable、仅日志记录。载荷类型注解链接到所属源码,不展开引用类型。记录不进入已知事件集合、持久化 schema 指纹或兼容性历史;格式迁移以尽力而为的方式保留它们。
|
||||
|
||||
运行 `pnpm run gen-persistence-catalog` 可重新生成两份持久化目录及其中英配对和关联制品。`pnpm run verify-persistence-catalog` 检查所有生成文件。
|
||||
|
||||
共享 Session 事件声明与解析后的 schema 见[会话持久化事件目录](persistence-catalog.zh.md)。
|
||||
|
||||
当前没有生产包声明插件记录。
|
||||
@@ -6,8 +6,8 @@
|
||||
en: a11f106978c9dce7
|
||||
zh: a11f106978c9dce7
|
||||
/session-persistence-event-catalog:
|
||||
en: f7cbb5a4b1274855
|
||||
zh: 5661d5180fc83054
|
||||
en: 1387c4505dde2faf
|
||||
zh: 48570fa1c13d6910
|
||||
/session-persistence-event-catalog/persistence-type-fingerprints:
|
||||
en: 33dd28b1c768cd8b
|
||||
zh: 7256442baf2faed1
|
||||
@@ -290,9 +290,6 @@
|
||||
/session-persistence-event-catalog/events/workspace/workspace-changes-log-only:
|
||||
en: ee6b592f9e2193a7
|
||||
zh: 7a7a69ae52aa714a
|
||||
/session-persistence-event-catalog/experimental-plugin-records:
|
||||
en: 154c163feab69117
|
||||
zh: b43c46a6c953f63e
|
||||
/session-persistence-event-catalog/resolved-persistence-types:
|
||||
en: 3493ecf0993e5397
|
||||
zh: ec95688f593a4ed4
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
# Session Persistence Event Catalog
|
||||
|
||||
Every repository-declared durable Session event appears here with its source declaration and resolved types. The catalog covers the logical and physical headers, event envelopes, every SessionEventMap declaration merge, and a separate inventory of experimental plugin records. See [Session](subsystems/session.md) for replay and [persistence](subsystems/persistence.md) for storage.
|
||||
Every repository-declared durable Session event appears here with its source declaration and resolved types. The catalog covers the logical and physical headers, event envelopes, and every SessionEventMap declaration merge. See [Session](subsystems/session.md) for replay and [persistence](subsystems/persistence.md) for storage.
|
||||
|
||||
Experimental records appear in the separate [experimental plugin record catalog](experimental-persistence-catalog.md).
|
||||
|
||||
Run `pnpm run gen-persistence-catalog` to regenerate both catalog languages, their pairing record, the known-event module, and the machine schema inventory. `pnpm run verify-persistence-catalog` checks all generated files. Declaration fences preserve source JSDoc and type references; resolved definitions expose their transitive structure.
|
||||
|
||||
@@ -1281,12 +1283,6 @@ Source: [`packages/web/web-search-deepseek/src/provider.ts:82`](../packages/web/
|
||||
|
||||
Source: [`packages/deliverables/workspace-changes/src/types.ts:106`](../packages/deliverables/workspace-changes/src/types.ts)
|
||||
|
||||
## Experimental plugin records
|
||||
|
||||
These current-source PluginRecordMap declarations describe ignorable, log-only records owned by experimental packages. Payload annotations link to their owning source without expanding referenced types. Records stay outside the known-event set, persistence schema fingerprints, and compatibility history; format migrations retain them on a best-effort basis.
|
||||
|
||||
No production package currently declares a plugin record.
|
||||
|
||||
## Resolved persistence types
|
||||
|
||||
Each definition appears once. References preserve sharing and recursion; the digest beside a definition includes its complete reachable structure. Source names and locations identify its declarations but are excluded from its digest.
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
[English](persistence-catalog.md) | 中文
|
||||
|
||||
本目录列出仓库声明的每个持久化 Session 事件及其源码声明和解析类型,覆盖逻辑与物理 header、事件信封、所有 SessionEventMap 声明合并,以及单独列出的实验性插件记录。回放规则参见 [Session](subsystems/session.zh.md),存储规则参见[持久化](subsystems/persistence.zh.md)。
|
||||
本目录列出仓库声明的每个持久化 Session 事件及其源码声明和解析类型,覆盖逻辑与物理 header、事件信封及所有 SessionEventMap 声明合并。回放规则参见 [Session](subsystems/session.zh.md),存储规则参见[持久化](subsystems/persistence.zh.md)。
|
||||
|
||||
实验性记录单独列在[实验性插件记录目录](experimental-persistence-catalog.zh.md)中。
|
||||
|
||||
运行 `pnpm run gen-persistence-catalog` 可重新生成目录的两种语言、配对记录、已知事件模块和机器 schema 目录。`pnpm run verify-persistence-catalog` 检查所有生成文件。声明围栏保留源码 JSDoc 和类型引用;解析后的定义展开其传递引用结构。
|
||||
|
||||
@@ -1283,12 +1285,6 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
|
||||
来源:[`packages/deliverables/workspace-changes/src/types.ts:106`](../packages/deliverables/workspace-changes/src/types.ts)
|
||||
|
||||
## 实验性插件记录
|
||||
|
||||
这些当前源码中的 PluginRecordMap 声明描述实验性包拥有的 ignorable、仅日志记录。载荷类型注解链接到所属源码,不展开引用类型。记录不进入已知事件集合、持久化 schema 指纹或兼容性历史;格式迁移以尽力而为的方式保留它们。
|
||||
|
||||
当前没有生产包声明插件记录。
|
||||
|
||||
## 已解析的持久化类型
|
||||
|
||||
每个类型定义仅列出一次。引用保留共享和递归关系;定义旁的摘要涵盖其完整可达结构。源码名称和位置标识声明来源,但不参与摘要计算。
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
en: 0a014a755b75449f
|
||||
zh: 9d59d74490a1bcb2
|
||||
/sessions/plugin-contributed-log-only-events/plugin-records:
|
||||
en: 0cfce3844b6c554f
|
||||
zh: 7acf342c8e552a7c
|
||||
en: 15102fd0b937fbf3
|
||||
zh: 37ebf1d715db097c
|
||||
/sessions/durability-contract:
|
||||
en: 62e3e4fbcba292a3
|
||||
zh: cb6dcaf531621e85
|
||||
|
||||
@@ -754,7 +754,7 @@ The hook bridges' `hook/invoked` / `hook/result` pairs (from `@deepseek-ai/dsh-h
|
||||
|
||||
### Plugin records
|
||||
|
||||
Experimental packages declare plugin-owned state in `PluginRecordMap`: `appendPluginRecord()` uses each declaration to type its name and payload, and appends an event marked `ignorable: true` in the `plugin:` namespace. `pluginRecordOf()` reads stored records with unknown payloads regardless of current declarations. The [package README](../../packages/core/session/README.md#write-experimental-plugin-records) owns the caller restriction, current-source catalog, retention, and read path.
|
||||
Experimental packages declare plugin-owned state in `PluginRecordMap`: `appendPluginRecord()` uses each declaration to type its name and payload, and appends an event marked `ignorable: true` in the `plugin:` namespace. `pluginRecordOf()` reads stored records with unknown payloads regardless of current declarations. The [experimental persistence catalog](../experimental-persistence-catalog.md) lists current declarations. The [package README](../../packages/core/session/README.md#write-experimental-plugin-records) owns the caller restriction, retention, and read path.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
|
||||
@@ -758,7 +758,7 @@ interface TurnEndReasonMap {
|
||||
|
||||
### 插件记录
|
||||
|
||||
实验性包在 `PluginRecordMap` 中声明插件自有的状态:`appendPluginRecord()` 根据每项声明约束名称与 payload 类型,并在 `plugin:` 命名空间中追加一条带 `ignorable: true` 标记的事件。`pluginRecordOf()` 读取已存记录,其 payload 为未知类型,不受当前声明约束。调用方限制、当前源码目录、保留方式与读取路径见[包 README](../../packages/core/session/README.zh.md#write-experimental-plugin-records)。
|
||||
实验性包在 `PluginRecordMap` 中声明插件自有的状态:`appendPluginRecord()` 根据每项声明约束名称与 payload 类型,并在 `plugin:` 命名空间中追加一条带 `ignorable: true` 标记的事件。`pluginRecordOf()` 读取已存记录,其 payload 为未知类型,不受当前声明约束。[实验性持久化目录](../experimental-persistence-catalog.zh.md)列出当前声明。调用方限制、保留方式与读取路径见[包 README](../../packages/core/session/README.zh.md#write-experimental-plugin-records)。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
en: c4bf3103b9762b6c
|
||||
zh: 369c734bb20de2ea
|
||||
/deepseek-ai-dsh-session/use-this-package/write-experimental-plugin-records:
|
||||
en: 2a90965fca937d4a
|
||||
zh: 89966ab7961fa629
|
||||
en: 6c758f98535b734c
|
||||
zh: ac54342937477f37
|
||||
/deepseek-ai-dsh-session/use-this-package/fork-a-session:
|
||||
en: 1c40d17a786059d3
|
||||
zh: 81320ce118977c65
|
||||
|
||||
@@ -71,7 +71,7 @@ Session log positions use two numeric types. `SessionSeq` identifies an existing
|
||||
|
||||
Declare each record in `PluginRecordMap` through `@deepseek-ai/dsh-session/types`, with a description and an explicit payload type annotation in the owning experimental package's `src/`. Names use `plugin:<owner>/<record>`, where `owner` is the suffix of the package name `@deepseek-ai/dsh-experimental-<owner>`; for example, `plugin:pi-extensions/entry`. TypeScript checks each name and payload against its declaration; the writer's runtime JSON snapshot rejects values that cannot be preserved losslessly. Put extension-defined or other dynamic names inside the payload of a declared record.
|
||||
|
||||
The [persistence catalog](../../../docs/persistence-catalog.md) lists current plugin-record declarations with their owners, descriptions, payload type annotations, and source files. These declarations remain separate from `SessionEventMap`, expanded persistence schemas, released type history, and `KNOWN_SESSION_EVENT_TYPES`. Records carry `ignorable: true`: a build that does not recognize one retains and skips it on read. Records never enter the model-visible surface. Resume and fork carry them with the rest of the log; Session format migration keeps them on a best-effort basis.
|
||||
The [experimental persistence catalog](../../../docs/experimental-persistence-catalog.md) lists current plugin-record declarations with their owners, descriptions, payload type annotations, and source files. These declarations remain separate from `SessionEventMap`, expanded persistence schemas, released type history, and `KNOWN_SESSION_EVENT_TYPES`. Records carry `ignorable: true`: a build that does not recognize one retains and skips it on read. Records never enter the model-visible surface. Resume and fork carry them with the rest of the log; Session format migration keeps them on a best-effort basis.
|
||||
|
||||
`pluginRecordOf(event)` returns an event as a record, or `undefined` for any other event; a `ctx.sessionProjections` unit that passes each event to it rebuilds plugin state on resume. Its `data` remains `unknown` even when the current map declares the name. The owner validates it before use because earlier builds can write different payloads, and the V3-to-V4 format edge can rename an unknown ignorable V3 event into the same `plugin:` namespace. Removing or renaming a declaration does not discard stored records. The [ignorable events decision](../../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.md) owns the compatibility policy.
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ session.deriveMessages() // the derived model history
|
||||
|
||||
在所属实验性包的 `src/` 中,通过 `@deepseek-ai/dsh-session/types` 的 `PluginRecordMap` 为每条记录声明描述与显式 payload 类型注解。名称使用 `plugin:<owner>/<record>`,其中 `owner` 是包名 `@deepseek-ai/dsh-experimental-<owner>` 的后缀;例如 `plugin:pi-extensions/entry`。TypeScript 按声明检查每个名称与 payload;写入方运行时的 JSON 快照拒绝无法无损保留的值。扩展自定义名称或其他动态名称放在已声明记录的 payload 中。
|
||||
|
||||
[持久化目录](../../../docs/persistence-catalog.zh.md)列出当前插件记录声明及其所属包、描述、payload 类型注解与源文件。这些声明与 `SessionEventMap`、展开的持久化 schema、已发布类型历史及 `KNOWN_SESSION_EVENT_TYPES` 分开。记录带有 `ignorable: true`:不认识某条记录的构建在读取时保留并跳过它。记录从不进入模型可见的 surface。resume 与 fork 随日志其余部分一起携带它们;Session 格式迁移以尽力而为的方式保留它们。
|
||||
[实验性持久化目录](../../../docs/experimental-persistence-catalog.zh.md)列出当前插件记录声明及其所属包、描述、payload 类型注解与源文件。这些声明与 `SessionEventMap`、展开的持久化 schema、已发布类型历史及 `KNOWN_SESSION_EVENT_TYPES` 分开。记录带有 `ignorable: true`:不认识某条记录的构建在读取时保留并跳过它。记录从不进入模型可见的 surface。resume 与 fork 随日志其余部分一起携带它们;Session 格式迁移以尽力而为的方式保留它们。
|
||||
|
||||
`pluginRecordOf(event)` 把事件作为记录返回,对其他任何事件返回 `undefined`;把每个事件都交给它的 `ctx.sessionProjections` 单元在 resume 时重建插件状态。即使当前映射声明了该名称,其 `data` 仍为 `unknown`。所属包在使用前校验数据,因为早先构建可能写入不同的 payload,V3-to-V4 格式迁移边也可能把未知的 ignorable V3 事件改名放入同一个 `plugin:` 命名空间。删除或重命名声明不会丢弃已存记录。[ignorable 事件决策](../../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.zh.md)定义兼容性策略。
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* the owning event-envelope types. This is the durable-record vocabulary, not
|
||||
* the live Cordis bus. Event declarations must be unique, explicitly typed,
|
||||
* documented, inheritance-free, and free of Cordis-only `@mode` tags; every
|
||||
* surface-union member must resolve to one. `--check` verifies the artifact.
|
||||
* surface-union member must resolve to one. Experimental plugin records have
|
||||
* a separate current-source catalog. `--check` verifies every generated artifact.
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from 'node:fs'
|
||||
@@ -25,6 +26,7 @@ import { renderPersistenceSchemaDefinitions, renderPersistenceSchemaIndex } from
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
const OUT = 'docs/persistence-catalog.md'
|
||||
const OUT_PLUGIN_RECORDS = 'docs/experimental-persistence-catalog.md'
|
||||
const OUT_RUNTIME_TYPES = 'packages/core/session/src/known-event-types.ts'
|
||||
const OUT_SCHEMA = 'docs/persistence-schema.json'
|
||||
|
||||
@@ -88,17 +90,38 @@ function recordCode(value: string): string {
|
||||
.replaceAll('|', '|').replaceAll('`', '`').replace(/\r?\n/gu, '<br>')}</code>`
|
||||
}
|
||||
|
||||
/** Render the current plugin record inventory without expanding payload types. */
|
||||
function renderPluginRecords(records: readonly PluginRecordEntry[], locale: PersistenceCatalogLocale): string[] {
|
||||
/**
|
||||
* Render the standalone current-source plugin record catalog without expanding payload types.
|
||||
* @param records - validated experimental package record declarations.
|
||||
* @param locale - language of the page's headings and explanatory prose.
|
||||
* @returns the complete Markdown page, including its language switcher and main catalog link.
|
||||
*/
|
||||
export function renderPluginRecordCatalog(records: readonly PluginRecordEntry[], locale: PersistenceCatalogLocale = 'en'): string {
|
||||
const text = persistenceCatalogText[locale]
|
||||
const lines = [`## ${text.pluginRecords}`, '', text.pluginRecordsIntro, '']
|
||||
if (records.length === 0) return [...lines, text.pluginRecordsEmpty, '']
|
||||
const lines = [
|
||||
'<!-- Generated by scripts/gen-persistence-catalog.ts — do not edit by hand.',
|
||||
' Run `pnpm run gen-persistence-catalog` to regenerate. -->',
|
||||
'',
|
||||
`# ${text.pluginRecords}`,
|
||||
'',
|
||||
locale === 'en'
|
||||
? 'English | [中文](experimental-persistence-catalog.zh.md)'
|
||||
: '[English](experimental-persistence-catalog.md) | 中文',
|
||||
'',
|
||||
text.pluginRecordsIntro,
|
||||
'',
|
||||
text.pluginRecordsGeneration,
|
||||
'',
|
||||
text.pluginRecordsMainLink,
|
||||
'',
|
||||
]
|
||||
if (records.length === 0) return [...lines, text.pluginRecordsEmpty, ''].join('\n')
|
||||
lines.push(text.pluginRecordColumns, '|---|---|---|---|')
|
||||
for (const record of [...records].sort((left, right) => left.owner.localeCompare(right.owner) || left.name.localeCompare(right.name))) {
|
||||
const doc = record.doc.replaceAll('|', '\\|').replace(/\s*\n\s*/gu, ' ')
|
||||
lines.push(`| \`${record.name}\` | \`${record.owner}\` | ${doc} | ${recordCode(record.payload)} · [\`${record.source}\`](../${record.source}) |`)
|
||||
}
|
||||
return [...lines, '']
|
||||
return [...lines, ''].join('\n')
|
||||
}
|
||||
|
||||
/** Render the full catalog (pure, deterministic given the collected inputs). */
|
||||
@@ -107,7 +130,6 @@ export function render(
|
||||
envelopeTypes: EventEnvelopeTypeEntry[],
|
||||
schema?: PersistenceSchemaInventory,
|
||||
locale: PersistenceCatalogLocale = 'en',
|
||||
pluginRecords: readonly PluginRecordEntry[] = [],
|
||||
): string {
|
||||
const text = persistenceCatalogText[locale]
|
||||
const lines: string[] = [
|
||||
@@ -119,6 +141,8 @@ export function render(
|
||||
...(locale === 'zh' ? ['[English](persistence-catalog.md) | 中文', ''] : []),
|
||||
text.intro,
|
||||
'',
|
||||
text.pluginRecordsLink,
|
||||
'',
|
||||
text.generation,
|
||||
'',
|
||||
text.envelopeIntro,
|
||||
@@ -142,7 +166,6 @@ export function render(
|
||||
lines.push(...renderEvent(e, locale))
|
||||
}
|
||||
}
|
||||
lines.push(...renderPluginRecords(pluginRecords, locale))
|
||||
if (schema) lines.push(renderPersistenceSchemaDefinitions(schema, locale, undefined, 2, 'current'))
|
||||
return lines.join('\n')
|
||||
}
|
||||
@@ -192,14 +215,15 @@ export function renderKnownEventTypes(events: AnnotatedLogEventEntry[]): string
|
||||
* Render the complete generated persistence reference from one extracted inventory.
|
||||
* @param scanRoot - checkout whose source event declarations supply JSDoc.
|
||||
* @param schema - already extracted source schemas; shared with record validation.
|
||||
* @returns both catalog languages, pairing metadata, runtime names and machine schemas.
|
||||
* @returns both catalog pairs and their metadata, runtime names and machine schemas.
|
||||
*/
|
||||
export function persistenceCatalogArtifacts(scanRoot: string, schema: PersistenceSchemaInventory): PersistenceArtifact[] {
|
||||
const events = annotateSurface(collectLogEvents(scanRoot), collectSurfaceEventTypes(scanRoot))
|
||||
const envelope = collectEventEnvelopeTypes(scanRoot)
|
||||
const pluginRecords = collectPluginRecords(scanRoot)
|
||||
return [
|
||||
...renderPersistencePair(scanRoot, OUT, render(events, envelope, schema, 'en', pluginRecords), render(events, envelope, schema, 'zh', pluginRecords)),
|
||||
...renderPersistencePair(scanRoot, OUT, render(events, envelope, schema), render(events, envelope, schema, 'zh')),
|
||||
...renderPersistencePair(scanRoot, OUT_PLUGIN_RECORDS, renderPluginRecordCatalog(pluginRecords), renderPluginRecordCatalog(pluginRecords, 'zh')),
|
||||
{ path: OUT_RUNTIME_TYPES, content: renderKnownEventTypes(events) },
|
||||
{ path: OUT_SCHEMA, content: `${JSON.stringify(persistenceSchemaSnapshot(schema), null, 2)}\n` },
|
||||
]
|
||||
|
||||
@@ -5,12 +5,15 @@ export type PersistenceCatalogLocale = 'en' | 'zh'
|
||||
|
||||
const english = {
|
||||
title: 'Session Persistence Event Catalog',
|
||||
intro: 'Every repository-declared durable Session event appears here with its source declaration and resolved types. The catalog covers the logical and physical headers, event envelopes, every SessionEventMap declaration merge, and a separate inventory of experimental plugin records. See [Session](subsystems/session.md) for replay and [persistence](subsystems/persistence.md) for storage.',
|
||||
intro: 'Every repository-declared durable Session event appears here with its source declaration and resolved types. The catalog covers the logical and physical headers, event envelopes, and every SessionEventMap declaration merge. See [Session](subsystems/session.md) for replay and [persistence](subsystems/persistence.md) for storage.',
|
||||
generation: 'Run `pnpm run gen-persistence-catalog` to regenerate both catalog languages, their pairing record, the known-event module, and the machine schema inventory. `pnpm run verify-persistence-catalog` checks all generated files. Declaration fences preserve source JSDoc and type references; resolved definitions expose their transitive structure.',
|
||||
envelopeIntro: 'The envelope carries `type`, `seq`, `time`, `data`, optional `ignorable`, and conditional `surfaceOp` / `sourceEventSeqs`. A **surface** event produces model history; a **log-only** event does not. The inventory covers this repository; external plugin types require their own declarations and are outside this catalog.',
|
||||
envelope: 'Event envelope', events: 'Events', sources: 'Sources: ', source: 'Source: ', types: 'Types: ',
|
||||
pluginRecords: 'Experimental plugin records',
|
||||
pluginRecords: 'Experimental Plugin Record Catalog',
|
||||
pluginRecordsLink: 'Experimental records appear in the separate [experimental plugin record catalog](experimental-persistence-catalog.md).',
|
||||
pluginRecordsIntro: 'These current-source PluginRecordMap declarations describe ignorable, log-only records owned by experimental packages. Payload annotations link to their owning source without expanding referenced types. Records stay outside the known-event set, persistence schema fingerprints, and compatibility history; format migrations retain them on a best-effort basis.',
|
||||
pluginRecordsGeneration: 'Run `pnpm run gen-persistence-catalog` to regenerate both persistence catalogs, their language pairs, and associated artifacts. `pnpm run verify-persistence-catalog` checks all generated files.',
|
||||
pluginRecordsMainLink: 'Shared Session event declarations and resolved schemas appear in the [Session persistence event catalog](persistence-catalog.md).',
|
||||
pluginRecordsEmpty: 'No production package currently declares a plugin record.',
|
||||
pluginRecordColumns: '| Record | Owner | Purpose | Payload and source |',
|
||||
fingerprints: 'Persistence type fingerprints',
|
||||
@@ -30,12 +33,15 @@ const english = {
|
||||
|
||||
const chinese: Record<keyof typeof english, string> = {
|
||||
title: '会话持久化事件目录',
|
||||
intro: '本目录列出仓库声明的每个持久化 Session 事件及其源码声明和解析类型,覆盖逻辑与物理 header、事件信封、所有 SessionEventMap 声明合并,以及单独列出的实验性插件记录。回放规则参见 [Session](subsystems/session.zh.md),存储规则参见[持久化](subsystems/persistence.zh.md)。',
|
||||
intro: '本目录列出仓库声明的每个持久化 Session 事件及其源码声明和解析类型,覆盖逻辑与物理 header、事件信封及所有 SessionEventMap 声明合并。回放规则参见 [Session](subsystems/session.zh.md),存储规则参见[持久化](subsystems/persistence.zh.md)。',
|
||||
generation: '运行 `pnpm run gen-persistence-catalog` 可重新生成目录的两种语言、配对记录、已知事件模块和机器 schema 目录。`pnpm run verify-persistence-catalog` 检查所有生成文件。声明围栏保留源码 JSDoc 和类型引用;解析后的定义展开其传递引用结构。',
|
||||
envelopeIntro: '信封包含 `type`、`seq`、`time`、`data`、可选的 `ignorable` 以及条件字段 `surfaceOp` / `sourceEventSeqs`。**surface** 事件产生模型历史,**log-only** 事件不产生模型历史。目录覆盖本仓库;外部插件类型需要独立声明,不属于本目录。',
|
||||
envelope: '事件信封', events: '事件', sources: '来源:', source: '来源:', types: '类型:',
|
||||
pluginRecords: '实验性插件记录',
|
||||
pluginRecords: '实验性插件记录目录',
|
||||
pluginRecordsLink: '实验性记录单独列在[实验性插件记录目录](experimental-persistence-catalog.zh.md)中。',
|
||||
pluginRecordsIntro: '这些当前源码中的 PluginRecordMap 声明描述实验性包拥有的 ignorable、仅日志记录。载荷类型注解链接到所属源码,不展开引用类型。记录不进入已知事件集合、持久化 schema 指纹或兼容性历史;格式迁移以尽力而为的方式保留它们。',
|
||||
pluginRecordsGeneration: '运行 `pnpm run gen-persistence-catalog` 可重新生成两份持久化目录及其中英配对和关联制品。`pnpm run verify-persistence-catalog` 检查所有生成文件。',
|
||||
pluginRecordsMainLink: '共享 Session 事件声明与解析后的 schema 见[会话持久化事件目录](persistence-catalog.zh.md)。',
|
||||
pluginRecordsEmpty: '当前没有生产包声明插件记录。',
|
||||
pluginRecordColumns: '| 记录 | 所属包 | 用途 | 载荷与源码 |',
|
||||
fingerprints: '持久化类型指纹',
|
||||
|
||||
@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { collectPluginRecords } from './plugin-record-catalog.ts'
|
||||
import { annotateSurface, collectLogEvents, render, renderKnownEventTypes } from './gen-persistence-catalog.ts'
|
||||
import { persistenceCatalogArtifacts, render, renderPluginRecordCatalog } from './gen-persistence-catalog.ts'
|
||||
import { extractPersistenceSchema } from './persistence-schema.ts'
|
||||
import { removeFixtureSafely } from './test-fixture-cleanup.ts'
|
||||
|
||||
@@ -124,8 +124,26 @@ describe('plugin record declarations', () => {
|
||||
|
||||
describe('plugin record catalogue output', () => {
|
||||
it('renders honest empty states in both languages', () => {
|
||||
expect(render([], [])).toContain('No production package currently declares a plugin record.')
|
||||
expect(render([], [], undefined, 'zh')).toContain('当前没有生产包声明插件记录。')
|
||||
const english = renderPluginRecordCatalog([])
|
||||
const chinese = renderPluginRecordCatalog([], 'zh')
|
||||
expect(english).toContain('# Experimental Plugin Record Catalog')
|
||||
expect(english).toContain('English | [中文](experimental-persistence-catalog.zh.md)')
|
||||
expect(english).toContain('No production package currently declares a plugin record.')
|
||||
expect(english).toContain('[Session persistence event catalog](persistence-catalog.md)')
|
||||
expect(chinese).toContain('[English](experimental-persistence-catalog.md) | 中文')
|
||||
expect(chinese).toContain('当前没有生产包声明插件记录。')
|
||||
expect(chinese).toContain('[会话持久化事件目录](persistence-catalog.zh.md)')
|
||||
})
|
||||
|
||||
it('links the main catalogue to the experimental page without embedding its inventory', () => {
|
||||
const english = render([], [])
|
||||
const chinese = render([], [], undefined, 'zh')
|
||||
expect(english).toContain('[experimental plugin record catalog](experimental-persistence-catalog.md)')
|
||||
expect(chinese).toContain('[实验性插件记录目录](experimental-persistence-catalog.zh.md)')
|
||||
expect(english).not.toContain('No production package currently declares')
|
||||
expect(english).not.toContain('| Record | Owner |')
|
||||
expect(chinese).not.toContain('当前没有生产包声明')
|
||||
expect(chinese).not.toContain('| 记录 | 所属包 |')
|
||||
})
|
||||
|
||||
it('renders compact, sorted rows with escaped annotations and no line-number churn', () => {
|
||||
@@ -134,7 +152,7 @@ describe('plugin record catalogue output', () => {
|
||||
'plugin:bridge/state': { value: string | null; marker: 'a b' }
|
||||
${ENTRY}
|
||||
`) }))
|
||||
const output = render([], [], undefined, 'en', [...records].reverse())
|
||||
const output = renderPluginRecordCatalog([...records].reverse())
|
||||
expect(output).toContain('| `plugin:bridge/entry` | `@deepseek-ai/dsh-experimental-bridge` | Retains a bridge entry. | <code>SavedEntry</code>')
|
||||
expect(output).toContain('Retains state \\| metadata.')
|
||||
expect(output).toContain("<code>{ value: string | null; marker: 'a b'; }</code>")
|
||||
@@ -143,7 +161,7 @@ describe('plugin record catalogue output', () => {
|
||||
expect(output).not.toMatch(/records\.ts:\d/u)
|
||||
})
|
||||
|
||||
it('keeps record declarations and payload changes out of persisted schemas and runtime names', () => {
|
||||
it('limits record declaration changes to the experimental artifact pair and its metadata', () => {
|
||||
const root = fixture({
|
||||
'tsconfig.host.json': JSON.stringify({ compilerOptions: {
|
||||
target: 'es2024', module: 'esnext', moduleResolution: 'bundler', strict: true, types: [],
|
||||
@@ -155,23 +173,43 @@ export interface SessionEventMap {
|
||||
/** One ordinary log event. */
|
||||
'test/record': { value: string }
|
||||
}
|
||||
export type SurfaceEventType = never
|
||||
/** Declared ordinary event names. */
|
||||
export type SessionEventType = keyof SessionEventMap
|
||||
/** Surface placement. */
|
||||
export type SurfaceOp = 'append'
|
||||
/** Events which can produce model history. */
|
||||
export type SurfaceEventType = 'test/record'
|
||||
/** One persisted event envelope. */
|
||||
export type SessionEvent<T extends keyof SessionEventMap = keyof SessionEventMap> = {
|
||||
[K in keyof SessionEventMap]: { type: K; seq: number; time: number; data: SessionEventMap[K] }
|
||||
[K in keyof SessionEventMap]: { type: K; seq: number; time: number; data: SessionEventMap[K]; surfaceOp: SurfaceOp }
|
||||
}[T]
|
||||
`,
|
||||
'packages/session/session-persistence-jsonl/src/format.ts': "interface HeaderLine {type: 'session'; version: number; id: string; delegationDepth: number}\nexport {}\n",
|
||||
})
|
||||
const schema = extractPersistenceSchema(root)
|
||||
const known = renderKnownEventTypes(annotateSurface(collectLogEvents(root), []))
|
||||
const empty = render([], [], undefined, 'en', collectPluginRecords(root))
|
||||
const empty = persistenceCatalogArtifacts(root, schema)
|
||||
const experimentalPaths = [
|
||||
'docs/experimental-persistence-catalog.md',
|
||||
'docs/experimental-persistence-catalog.zh.md',
|
||||
'docs/experimental-persistence-catalog.i18n.yaml',
|
||||
]
|
||||
expect(empty.map(artifact => artifact.path)).toEqual([
|
||||
'docs/persistence-catalog.md', 'docs/persistence-catalog.zh.md', 'docs/persistence-catalog.i18n.yaml',
|
||||
...experimentalPaths,
|
||||
'packages/core/session/src/known-event-types.ts', 'docs/persistence-schema.json',
|
||||
])
|
||||
put(root, RECORDS, `interface SavedEntry { value: string }\n${augmentation(ENTRY)}`)
|
||||
const declared = render([], [], undefined, 'en', collectPluginRecords(root))
|
||||
expect(declared).not.toBe(empty)
|
||||
const declared = persistenceCatalogArtifacts(root, schema)
|
||||
expect(declared.filter((artifact, index) => artifact.content !== empty[index]?.content).map(artifact => artifact.path))
|
||||
.toEqual(experimentalPaths)
|
||||
expect(extractPersistenceSchema(root)).toEqual(schema)
|
||||
put(root, RECORDS, `interface SavedEntry { nested: { count: number } }\n${augmentation(ENTRY)}`)
|
||||
expect(render([], [], undefined, 'en', collectPluginRecords(root))).toBe(declared)
|
||||
const nullable = augmentation(ENTRY.replace(': SavedEntry', ': SavedEntry | null'))
|
||||
put(root, RECORDS, `interface SavedEntry { value: string }\n${nullable}`)
|
||||
const changedAnnotation = persistenceCatalogArtifacts(root, schema)
|
||||
expect(changedAnnotation.filter((artifact, index) => artifact.content !== declared[index]?.content).map(artifact => artifact.path))
|
||||
.toEqual(experimentalPaths)
|
||||
put(root, RECORDS, `interface SavedEntry { nested: { count: number } }\n${nullable}`)
|
||||
expect(persistenceCatalogArtifacts(root, schema)).toEqual(changedAnnotation)
|
||||
expect(extractPersistenceSchema(root)).toEqual(schema)
|
||||
expect(renderKnownEventTypes(annotateSurface(collectLogEvents(root), []))).toBe(known)
|
||||
})
|
||||
})
|
||||
@@ -2,7 +2,7 @@
|
||||
* Verify that only experimental packages call `appendPluginRecord` from production source.
|
||||
*
|
||||
* `appendPluginRecord` in `@deepseek-ai/dsh-session` appends an ignorable `plugin:` record that the
|
||||
* persistence catalogue lists separately and that a Session format migration keeps only on a best-effort
|
||||
* experimental persistence catalogue lists and that a Session format migration keeps only on a best-effort
|
||||
* basis. Experimental packages may rely on that; a release package must declare its events instead.
|
||||
* The owning module and test files, which exercise the operation, are exempt.
|
||||
*
|
||||
|
||||
@@ -409,6 +409,7 @@ const reference = [
|
||||
['docs/config-catalog.md', 'reference/config-catalog.md', '插件配置', 'Plugin configuration'],
|
||||
['docs/tool-catalog.md', 'reference/tool-catalog.md', 'Tool Schema', 'Tool schemas'],
|
||||
['docs/persistence-catalog.md', 'reference/persistence-catalog.md', '持久化事件', 'Persistence events', 'deep'],
|
||||
['docs/experimental-persistence-catalog.md', 'reference/experimental-persistence-catalog.md', '实验性持久化记录', 'Experimental persistence'],
|
||||
] as const).map(([source, route, rootLabel, enLabel, outline], order): PairedPage => ({
|
||||
source,
|
||||
route,
|
||||
|
||||
Reference in new issue
Block a user