From 1832df038d6798c36dba01934d449b96665f71d8 Mon Sep 17 00:00:00 2001 From: imccyu Date: Wed, 5 Aug 2026 00:24:26 +0800 Subject: [PATCH] docs(session): document creation ownership boundaries --- docs/config-catalog.md | 2 +- docs/cordis-catalog/events.md | 2 +- docs/cordis-catalog/services.md | 4 ++-- docs/core-data-structures/session.i18n.yaml | 4 ++-- docs/core-data-structures/session.md | 15 ++++++++++++--- docs/core-data-structures/session.zh.md | 15 ++++++++++++--- docs/event-producer-consumer.md | 2 +- packages/cordis/tool-cordis/src/api-catalog.ts | 2 +- packages/core/session/README.i18n.yaml | 4 ++-- packages/core/session/README.md | 4 +++- packages/core/session/README.zh.md | 4 +++- packages/core/session/src/index.ts | 4 ++-- 12 files changed, 42 insertions(+), 20 deletions(-) diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 5d238761d9..20eb558154 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -108,7 +108,7 @@ export interface Config { Depends on: [`AgentOptions`](core-data-structures/core.md) · [`SessionId`](core-data-structures/core.md) -Source: [`packages/core/agent-loop/src/index.ts:211`](../packages/core/agent-loop/src/index.ts) +Source: [`packages/core/agent-loop/src/index.ts:212`](../packages/core/agent-loop/src/index.ts) ## `@deepseek-ai/dsh-agent-spine-demo` diff --git a/docs/cordis-catalog/events.md b/docs/cordis-catalog/events.md index 8f4385938c..b1578717fd 100644 --- a/docs/cordis-catalog/events.md +++ b/docs/cordis-catalog/events.md @@ -398,7 +398,7 @@ A declarative agent entry failed before it could publish a live agent. Consumers Types: [SessionId](../core-data-structures/core.md) -Source: [`packages/core/agent-loop/src/index.ts:157`](../../packages/core/agent-loop/src/index.ts) +Source: [`packages/core/agent-loop/src/index.ts:158`](../../packages/core/agent-loop/src/index.ts) ## `approval/*` diff --git a/docs/cordis-catalog/services.md b/docs/cordis-catalog/services.md index 93d6d267a4..37ed3dc1fa 100644 --- a/docs/cordis-catalog/services.md +++ b/docs/cordis-catalog/services.md @@ -44,7 +44,7 @@ async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [\n opts: SurfaceIntent\n ] : [\n ]): SessionEvent;\n requestHeader(): EpochHeader | undefined;\n requestContext(): RequestContext | undefined;\n deriveMessages(): Message[];\n deriveEventMessage(event: SessionEvent): Message | null;\n}', + declaration: 'export class Session {\n get surface(): SessionSurface;\n readonly header: SessionHeader;\n get id(): SessionId;\n readonly firstLiveSeq: number;\n static create(id: SessionId, seed?: readonly SessionEvent[], header?: SessionHeader): Session;\n get events(): readonly SessionEvent[];\n get seq(): number;\n append(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [\n opts: SurfaceIntent\n ] : [\n ]): SessionEvent;\n requestHeader(): EpochHeader | undefined;\n requestContext(): RequestContext | undefined;\n deriveMessages(): Message[];\n deriveEventMessage(event: SessionEvent): Message | null;\n}', }, { name: 'SessionAvailability', diff --git a/packages/core/session/README.i18n.yaml b/packages/core/session/README.i18n.yaml index 00c1f688ff..61b48dc404 100644 --- a/packages/core/session/README.i18n.yaml +++ b/packages/core/session/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/core/session/README.md -README.md: d78dc5bcfe1df2edd01280208f3859eb1b2d6763 -README.zh.md: 9ed9cab5c1c0a3eae6ac8b7bea6bb82b8c668e22 +README.md: ae4f0ccaa3ac3b3742a0856de4445cbd8412ea24 +README.zh.md: ae4adb6afa48e67a3274231d6c9bf303a0086470 diff --git a/packages/core/session/README.md b/packages/core/session/README.md index d78dc5bcfe..ae4f0ccaa3 100644 --- a/packages/core/session/README.md +++ b/packages/core/session/README.md @@ -35,7 +35,7 @@ The store pairs announced creation with disposal, publishes post-commit append n ### Class: `Session` -Plain class (not a Cordis Service). Create via `ctx.sessions.create()`. +Plain class (not a Cordis Service). Create live sessions through `ctx.sessions.create()` and detached replay or inspection sessions through `Session.create()`; the detached factory does not publish lifecycle events or bind the session to a fiber. - `session.append(type, data, opts?)` snapshots and freezes durable data and surface metadata, validates marker shape, provenance, complete replacement coverage, and content-only single-result `tool/result` rewrites, commits synchronously, then notifies observers with independent failure containment. Reentrant attached-session appends reject, and runtime checks cover widened unions and loaded logs. - `session.deriveMessages()` incrementally projects each new surface entry once and returns a fresh array over the complete identified, frozen messages stored by those entries. Assistant messages preserve provider/model provenance and adapter-private replay state in their model source. A surface rewrite rebuilds the projection; there is no raw-log fallback. @@ -49,6 +49,8 @@ Plain class (not a Cordis Service). Create via `ctx.sessions.create()`. Durable values need one accepted representation, not a check followed by a second read. `isJsonValue(value)` is the boolean predicate; `snapshotJsonValue(value)` iteratively validates and copies a plain value in one pass, returning `undefined` for invalid input and propagating a throwing getter. The snapshot helper accepts finite JSON numbers except `-0` (JSON rewrites it to `0`), dense ordinary arrays, and plain or null-prototype objects; it rejects cycles, unsupported scalars, and exotic prototypes before normalization without imposing a call-stack depth limit. +Session-event import separates ownership from message validation. `snapshotSessionEvent(event)` clones a borrowed event before validating and freezing its identified message. `adoptSessionEvent(event)` performs the same message work in place and returns the original event; callers may use it only when they transfer an exclusively owned object graph with no mutable child shared with another event. + ### Chunk-row storage codec (`chunk-rows.ts`) Providers stream token-sized deltas, so a raw log stores hundreds of `assistant/chunk` lines whose JSON envelopes dwarf their payloads. `packChunkRuns(events)` packs each run of ≥3 consecutive same-block delta chunks into one storage row — `text-chunks`, `reasoning-chunks`, or `tool-call-chunks` (bare slash-less tags: storage vocabulary, not `SessionEventMap` members) — and `decodeStorageRecord(value)` expands a parsed line back into its exact events (`seq0`/`time0` + per-member `dt` gaps reconstruct every `seq`/`time`). The encoder whitelists exact shapes and stores anything unrecognized verbatim; the decoder validates row-tagged values and throws on malformation. Owned here so the JSONL backend and the fixture readers (`dsh-llm-replay`, `dsh-acp-snapshot`) share one codec; the backend's default-enabled `packChunks` config controls writes only. diff --git a/packages/core/session/README.zh.md b/packages/core/session/README.zh.md index 9ed9cab5c1..ae4adb6afa 100644 --- a/packages/core/session/README.zh.md +++ b/packages/core/session/README.zh.md @@ -35,7 +35,7 @@ ### 类:`Session` -普通类(不是 Cordis 服务)。通过 `ctx.sessions.create()` 创建。 +普通类(不是 Cordis 服务)。活跃会话通过 `ctx.sessions.create()` 创建,脱离态的回放或检查会话通过 `Session.create()` 创建;脱离态工厂不会发布生命周期事件,也不会将会话绑定到 fiber。 - `session.append(type, data, opts?)` 会为持久数据和 surface 元数据制作快照并冻结它们,校验标记形态、溯源信息、替换覆盖完整性,以及仅修改内容的单个 `tool/result` 重写,随后同步提交,再在彼此独立的失败收容下通知观察者。对已附加会话的重入追加会被拒绝,运行时检查也覆盖扩宽后的联合类型和已加载日志。 - `session.deriveMessages()` 对每个新的 surface 条目只做一次增量投影,并返回一个新数组,其中包含这些条目存储的完整、带标识且冻结的消息。assistant 消息会在其模型来源中保留提供方/模型溯源信息及适配器私有回放状态。surface 重写会重建投影;不存在原始日志回退。 @@ -49,6 +49,8 @@ 持久值需要一种已接受的表示,不能先检查再二次读取。`isJsonValue(value)` 是布尔判断函数;`snapshotJsonValue(value)` 在一趟迭代中校验并复制普通值,无效输入返回 `undefined`,getter 抛出的异常则向外传播。快照辅助函数接受除 `-0` 外的有限 JSON 数值(JSON 会将其改写为 `0`)、稠密普通数组、普通对象或 null 原型对象;它会在规范化前拒绝循环引用、不支持的标量和特殊原型,同时不施加调用栈深度限制。 +会话事件导入将所有权与消息校验分开处理。`snapshotSessionEvent(event)` 会先克隆借用的事件,再校验并冻结其中带标识的消息。`adoptSessionEvent(event)` 原地执行相同的消息处理并返回原事件;调用方只有在移交独占的对象图,且该对象图没有与其他事件共享可变子对象时,才可以使用此函数。 + ### 分片行存储编解码器(`chunk-rows.ts`) 提供方以 token 大小的增量流式输出,因此原始日志会存储数百行 `assistant/chunk`,其 JSON 封装远大于载荷。`packChunkRuns(events)` 将每段至少 3 个连续、同块的增量分片打包为一个存储行:`text-chunks`、`reasoning-chunks` 或 `tool-call-chunks`(不含斜杠的裸标签,属于存储词汇而不是 `SessionEventMap` 成员)。`decodeStorageRecord(value)` 则将已解析行展开回完全一致的事件(`seq0`/`time0` 加上每个成员的 `dt` 间隔,可重建每个 `seq`/`time`)。编码器只允许精确形态,并逐字存储任何无法识别的内容;解码器校验带行标签的值,形态错误时抛出异常。编解码器由此包所有,使 JSONL 后端和 fixture(测试前置数据)读取器(`dsh-llm-replay`、`dsh-acp-snapshot`)共享同一编解码器;后端默认启用的 `packChunks` 配置只控制写入。 diff --git a/packages/core/session/src/index.ts b/packages/core/session/src/index.ts index 4293d83c58..fe36162a22 100644 --- a/packages/core/session/src/index.ts +++ b/packages/core/session/src/index.ts @@ -455,8 +455,8 @@ export class Session { readonly firstLiveSeq: number /** - * Create a detached session with the same validation and snapshot semantics - * formerly provided by direct construction. + * Create a detached session by validating and snapshotting borrowed seed + * events and storage metadata. * @param id - session identity. * @param seed - optional borrowed replay or fork events. * @param header - optional borrowed storage metadata.