@deepseek-ai/dsh-session 0.0.1-rc.1 → 0.0.1-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/core/session/README.md
5
- README.md: db477d94037d3463870fc8e66ea35d5e607fb6fe
6
- README.zh.md: 1ce1e823a7e0fdbcf7b6898764a89c52b74adf6a
5
+ README.md: 57569e9c0dbfa7cb696e3a561a9ff108c2ac981f
6
+ README.zh.md: 16629dc70c79ca838ba7088aeafcc5b38b124f87
package/README.md CHANGED
@@ -76,10 +76,11 @@ Also defines `TurnEndReasonMap`, the merge-extensible `kind`-tagged sum type for
76
76
 
77
77
  An interrupted live turn ends with `{ kind: 'aborted', reason: AgentCancelCause }`, preserving the typed cancellation cause in the durable transcript. Persistence imports the coarse aborted outcome from the supported older format as `{ kind: 'aborted', reason: { kind: 'legacy' } }`, because that record did not retain its caller. A turn failure carries `{ kind: 'error', error }`; crash recovery alone synthesizes `{ kind: 'interrupted' }`.
78
78
 
79
- Every `SessionEvent` carries two optional top-level fields (structural metadata):
79
+ Every `SessionEvent` carries three optional top-level fields (structural metadata):
80
80
 
81
81
  - `sourceEventSeqs?: number[]` — seq numbers of earlier events cited as sources (e.g., the `assistant/chunk` seqs behind an `assistant/message`, or the shadowed entries behind a compaction replacement entry). On `assistant/message`, a present `[]` records a known empty provider stream, while omission means a legacy or foreign event did not record the source stream; other surface events require a non-empty list when this field is present.
82
82
  - `surfaceOp?: SurfaceOp` — how this event entered the surface. Absent for non-surface events (boundaries, chunks, usage, errors).
83
+ - `ignorable?: true` — marks an event a reader may safely skip when it does not recognize the type; absent means required, so an unknown-type event refuses session reconstruction ([mechanism](../../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md)).
83
84
 
84
85
  ### Metadata types (`types.ts`)
85
86
 
@@ -139,5 +140,5 @@ Logging causes no invalidation, and exact reconstruction preserves request-prefi
139
140
 
140
141
  - **Session branching/tree** (pi-style entry tree) — deferred unless needed beyond boundary-based `fork()`.
141
142
  - **`fork()` cuts only at stable boundaries of live sessions** — the selected prefix must end outside an open turn and the source must be in the store; forking a persisted-but-unloaded session is excluded from the [fork API](../../../.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.md).
142
- - **`SESSION_FORMAT_VERSION` stays pinned at `0`** — pre-release, no broad compatibility implied: `Session` accepts only current seed shapes and a backend rejects any other version. Narrow storage import upgrades belong to the persistence boundary ([policy](../../../AGENTS.md), [pre-identity message recovery](../../../.agents/notes/implemented/bug-fix/2026-07-28-load-pre-identity-session-messages.md)).
143
+ - **`SESSION_FORMAT_VERSION` stays pinned at `0`** — pre-release, no broad compatibility implied: `Session` accepts only current seed shapes, and a backend refuses any other version naming the direction (newer: "written by a newer harness — upgrade"; older: no upgrade path ships yet). Unknown event types refuse the same way unless marked `ignorable` in the envelope; the versioning mechanism is the [session-log-version-mechanism note](../../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md). Narrow storage import upgrades belong to the persistence boundary ([policy](../../../AGENTS.md), [pre-identity message recovery](../../../.agents/notes/implemented/bug-fix/2026-07-28-load-pre-identity-session-messages.md)).
143
144
  - **`TurnEndReasonMap` omits the ACP-named `refusal` / `max_turn_requests` variants** — producer-gated: they land when an adapter or the loop first emits them.
package/README.zh.md CHANGED
@@ -76,10 +76,11 @@
76
76
 
77
77
  被中断的实时轮次以 `{ kind: 'aborted', reason: AgentCancelCause }` 结束,在持久 transcript(文本记录)中保留类型化取消原因。持久化会将受支持旧格式中的粗粒度中止结果导入为 `{ kind: 'aborted', reason: { kind: 'legacy' } }`,因为该记录没有保留调用方。轮次失败携带 `{ kind: 'error', error }`;只有崩溃恢复会合成 `{ kind: 'interrupted' }`。
78
78
 
79
- 每个 `SessionEvent` 都有两个可选顶层字段(结构元数据):
79
+ 每个 `SessionEvent` 都有三个可选顶层字段(结构元数据):
80
80
 
81
81
  - `sourceEventSeqs?: number[]`:被引用为来源的较早事件 seq(例如 `assistant/message` 引用的 `assistant/chunk` seq,或压缩替换条目引用的已遮蔽条目)。对于 `assistant/message`,存在的 `[]` 表示已知提供方流为空;省略则表示旧版或外部事件没有记录源流。其他 surface 事件若有此字段,则要求非空列表。
82
82
  - `surfaceOp?: SurfaceOp`:事件进入 surface 的方式。非 surface 事件(边界、分片、用量、错误)不含该字段。
83
+ - `ignorable?: true`:标记读取器在不认识事件类型时可以安全跳过该事件;缺失表示必需,不认识的事件类型会使会话重建被拒绝([机制](../../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md))。
83
84
 
84
85
  ### 元数据类型(`types.ts`)
85
86
 
@@ -139,5 +140,5 @@
139
140
 
140
141
  - **会话分支/树**(pi 风格条目树):除非需要超越基于边界的 `fork()` 能力,否则暂缓。
141
142
  - **`fork()` 仅在实时会话的稳定边界处切分**:所选前缀结束时不得有开放轮次,且源会话必须位于存储中;[fork API](../../../.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.md) 不支持对已持久化但未加载的会话进行 fork。
142
- - **`SESSION_FORMAT_VERSION` 固定为 `0`**:预发布阶段不承诺广泛兼容性;`Session` 只接受当前 seed 形状,后端会拒绝其他任何版本。范围受限的存储导入升级应由持久化边界负责([政策](../../../AGENTS.md)、[消息标识机制引入前的消息恢复](../../../.agents/notes/implemented/bug-fix/2026-07-28-load-pre-identity-session-messages.md))。
143
+ - **`SESSION_FORMAT_VERSION` 固定为 `0`**:预发布阶段不承诺广泛兼容性;`Session` 只接受当前 seed 形状,后端拒绝其他任何版本并说明方向(更新的版本提示"由更新的 harness 写入,请升级";更旧的版本说明尚无升级路径)。不认识的事件类型同样被拒绝,除非信封带 `ignorable` 标记;版本机制见 [session-log 版本机制 Agent Note](../../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md)。范围受限的存储导入升级应由持久化边界负责([政策](../../../AGENTS.md)、[消息标识机制引入前的消息恢复](../../../.agents/notes/implemented/bug-fix/2026-07-28-load-pre-identity-session-messages.md))。
143
144
  - **`TurnEndReasonMap` 不含 ACP(Agent Client Protocol)命名的 `refusal`/`max_turn_requests` 变体**:受生产方约束;只有当适配器或循环首次产生这些变体时才加入。
package/lib/index.js CHANGED
@@ -16,8 +16,23 @@ function SessionId(id) {
16
16
  * and enforced by every persistence backend on load. The single source of truth for the
17
17
  * version — write sites and the load-time check all read it.
18
18
  * While the harness is unreleased it is pinned at `0`: no compatibility is
19
- * implied, incompatible logs are rejected, and no migration is provided. A
20
- * monotonic version policy starts with the first tagged release.
19
+ * implied, incompatible logs are rejected, and no migration is provided.
20
+ *
21
+ * The version is a single monotonic integer with no major/minor split. Whether
22
+ * a bump is needed is decided by what the WRITER emits, never by what a newer
23
+ * reader can accept: bump exactly when an older runtime could no longer handle
24
+ * a new log with full semantic correctness ("parses without error" is not
25
+ * correctness — silently skipping content that shapes reconstruction is a
26
+ * wrong read). Only structural changes reach that bar: the header shape, the
27
+ * {@link SessionEvent} envelope, core event semantics, or the surface
28
+ * mechanism (the {@link SurfaceEventType} set and {@link SurfaceOp} variants).
29
+ * Adding an ordinary event type does not bump — the per-event
30
+ * {@link SessionEvent.ignorable} guard covers vocabulary growth instead. When
31
+ * in doubt, bump: a near-identity upgrade step is almost free, a missed bump
32
+ * makes older runtimes read new logs wrong silently. The full mechanism
33
+ * (upgrade-step chain, in-memory view conversion, migrate-on-continue) is
34
+ * recorded in the session-log-version-mechanism Agent Note
35
+ * (`.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md`).
21
36
  */
22
37
  const SESSION_FORMAT_VERSION = 0;
23
38
  //#endregion
@@ -1036,6 +1051,71 @@ function decodeStorageRecord(value) {
1036
1051
  return expandRow(validateRow(value, tag));
1037
1052
  }
1038
1053
  //#endregion
1054
+ //#region lib/types/known-event-types.js
1055
+ /**
1056
+ * GENERATED by `scripts/gen-persistence-catalog.ts` — do not edit by hand; run
1057
+ * `pnpm run gen-persistence-catalog` to regenerate (verified fresh by
1058
+ * `pnpm run verify-persistence-catalog`, part of `doc-sync`).
1059
+ * @module @deepseek-ai/dsh-session/known-event-types
1060
+ */
1061
+ /**
1062
+ * Every `SessionEventMap` member declared in this repository — the event
1063
+ * vocabulary this build understands. The persistence read path refuses to
1064
+ * interpret a log containing a type outside this set unless the event
1065
+ * carries the envelope's `ignorable` marker (see `SessionEvent.ignorable`
1066
+ * in `./types.ts`): such a log was likely written by a newer harness, and
1067
+ * silently skipping a required event would reconstruct a wrong session.
1068
+ * Downstream (out-of-repo) plugin events are outside this list by
1069
+ * construction; a registration surface for them is deferred until such a
1070
+ * consumer exists.
1071
+ */
1072
+ const KNOWN_SESSION_EVENT_TYPES = new Set([
1073
+ "agent-preset/selected",
1074
+ "agent/inbox/spliced",
1075
+ "approval/asked",
1076
+ "approval/decided",
1077
+ "approval/policy",
1078
+ "assistant/chunk",
1079
+ "assistant/message",
1080
+ "command/done",
1081
+ "command/run",
1082
+ "compact/end",
1083
+ "compact/prune",
1084
+ "compact/start",
1085
+ "compact/summary",
1086
+ "feedback/record",
1087
+ "goal/change",
1088
+ "hook/invoked",
1089
+ "hook/result",
1090
+ "llm/retry",
1091
+ "llm/retry-started",
1092
+ "permission/preset",
1093
+ "plan/mode",
1094
+ "request/context",
1095
+ "request/header",
1096
+ "sandbox/mode",
1097
+ "schedule/change",
1098
+ "session/end-seed",
1099
+ "session/title",
1100
+ "session/title-llm-request",
1101
+ "step/end",
1102
+ "step/start",
1103
+ "subagent/descriptor",
1104
+ "todo/write",
1105
+ "tool-workflow/agent-end",
1106
+ "tool-workflow/agent-start",
1107
+ "tool-workflow/run-end",
1108
+ "tool-workflow/run-start",
1109
+ "tool/call",
1110
+ "tool/code-dispatch",
1111
+ "tool/code-dispatch-start",
1112
+ "tool/result",
1113
+ "turn/end",
1114
+ "turn/start",
1115
+ "user/message",
1116
+ "web/deepseek-search-llm-request"
1117
+ ]);
1118
+ //#endregion
1039
1119
  //#region lib/types/index.js
1040
1120
  /**
1041
1121
  * Event-sourced session service: append-only session log, in-memory store, and
@@ -1044,24 +1124,6 @@ function decodeStorageRecord(value) {
1044
1124
  *
1045
1125
  * @module @deepseek-ai/dsh-session
1046
1126
  */
1047
- /**
1048
- * Find the latest closed turn that entered at least one model step, ignoring
1049
- * balanced no-step turns produced by rejection, empty input, or cancellation.
1050
- * @param events - session events, or an owned suffix, to inspect.
1051
- * @returns the latest matching turn end, or `undefined`.
1052
- */
1053
- function findLastMessageTurnEnd(events) {
1054
- const steppedTurns = /* @__PURE__ */ new Set();
1055
- let latest;
1056
- for (const event of events) {
1057
- if (event.type === "step/start") {
1058
- steppedTurns.add(event.data.turn);
1059
- continue;
1060
- }
1061
- if (event.type === "turn/end" && steppedTurns.delete(event.data.turn)) latest = event;
1062
- }
1063
- return latest;
1064
- }
1065
1127
  /** Validate and freeze one detached creation header in place. */
1066
1128
  function validateSessionHeader(id, input) {
1067
1129
  if (input === null || typeof input !== "object" || Array.isArray(input)) throw new Error("session header is not a plain JSON record");
@@ -1151,13 +1213,14 @@ function assertSessionEventEnvelope(value, index) {
1151
1213
  case "time":
1152
1214
  case "data":
1153
1215
  case "surfaceOp":
1154
- case "sourceEventSeqs": break;
1216
+ case "sourceEventSeqs":
1217
+ case "ignorable": break;
1155
1218
  default: throw new Error(`seed event at index ${index} has an invalid event envelope`);
1156
1219
  }
1157
1220
  const type = event["type"];
1158
1221
  const seq = event["seq"];
1159
1222
  const time = event["time"];
1160
- if (typeof type !== "string" || typeof seq !== "number" || !Number.isSafeInteger(seq) || seq < 0 || typeof time !== "number" || !Number.isSafeInteger(time) || event["data"] === void 0) throw new Error(`seed event at index ${index} has an invalid event envelope`);
1223
+ if (typeof type !== "string" || typeof seq !== "number" || !Number.isSafeInteger(seq) || seq < 0 || typeof time !== "number" || !Number.isSafeInteger(time) || event["data"] === void 0 || event["ignorable"] !== void 0 && event["ignorable"] !== true) throw new Error(`seed event at index ${index} has an invalid event envelope`);
1161
1224
  switch (type) {
1162
1225
  case "request/header":
1163
1226
  case "user/message":
@@ -1838,4 +1901,4 @@ var SessionStore = class extends Service {
1838
1901
  }
1839
1902
  };
1840
1903
  //#endregion
1841
- export { SESSION_FORMAT_VERSION, Session, SessionForkError, SessionId, SessionPreparation, SessionStore, SessionStore as default, TOOL_NOT_STARTED, TOOL_OUTCOME_UNKNOWN, adoptSessionEvent, canonicalHeader, decodeStorageRecord, deriveEventMessage, findLastMessageTurnEnd, foldRequestHeader, foldSurface, headerEquals, interruptedTurnClosers, isAppendSurfaceEvent, isJsonValue, isReplacementSurfaceEvent, isSurfaceEligibleType, isSurfaceEvent, lastActivityTime, packChunkRuns, snapshotJsonValue, snapshotSessionEvent };
1904
+ export { KNOWN_SESSION_EVENT_TYPES, SESSION_FORMAT_VERSION, Session, SessionForkError, SessionId, SessionPreparation, SessionStore, SessionStore as default, TOOL_NOT_STARTED, TOOL_OUTCOME_UNKNOWN, adoptSessionEvent, canonicalHeader, decodeStorageRecord, deriveEventMessage, foldRequestHeader, foldSurface, headerEquals, interruptedTurnClosers, isAppendSurfaceEvent, isJsonValue, isReplacementSurfaceEvent, isSurfaceEligibleType, isSurfaceEvent, lastActivityTime, packChunkRuns, snapshotJsonValue, snapshotSessionEvent };
@@ -24,13 +24,7 @@ export type { ChunkRow, StorageRecord } from './chunk-rows.ts';
24
24
  export type { SessionSurface, SurfaceFoldReplacement, SurfaceFoldResult } from './surface.ts';
25
25
  export { deriveEventMessage, foldSurface, isAppendSurfaceEvent, isReplacementSurfaceEvent, isSurfaceEvent, isSurfaceEligibleType } from './surface.ts';
26
26
  export { canonicalHeader, foldRequestHeader, headerEquals } from './request-header.ts';
27
- /**
28
- * Find the latest closed turn that entered at least one model step, ignoring
29
- * balanced no-step turns produced by rejection, empty input, or cancellation.
30
- * @param events - session events, or an owned suffix, to inspect.
31
- * @returns the latest matching turn end, or `undefined`.
32
- */
33
- export declare function findLastMessageTurnEnd(events: readonly SessionEvent[]): SessionEvent<'turn/end'> | undefined;
27
+ export { KNOWN_SESSION_EVENT_TYPES } from './known-event-types.ts';
34
28
  declare module '@deepseek-ai/cordis' {
35
29
  interface Context {
36
30
  sessions: SessionStore;
@@ -20,25 +20,7 @@ export { interruptedTurnClosers, lastActivityTime, TOOL_NOT_STARTED, TOOL_OUTCOM
20
20
  export { decodeStorageRecord, packChunkRuns } from "./chunk-rows.js";
21
21
  export { deriveEventMessage, foldSurface, isAppendSurfaceEvent, isReplacementSurfaceEvent, isSurfaceEvent, isSurfaceEligibleType } from "./surface.js";
22
22
  export { canonicalHeader, foldRequestHeader, headerEquals } from "./request-header.js";
23
- /**
24
- * Find the latest closed turn that entered at least one model step, ignoring
25
- * balanced no-step turns produced by rejection, empty input, or cancellation.
26
- * @param events - session events, or an owned suffix, to inspect.
27
- * @returns the latest matching turn end, or `undefined`.
28
- */
29
- export function findLastMessageTurnEnd(events) {
30
- const steppedTurns = new Set();
31
- let latest;
32
- for (const event of events) {
33
- if (event.type === 'step/start') {
34
- steppedTurns.add(event.data.turn);
35
- continue;
36
- }
37
- if (event.type === 'turn/end' && steppedTurns.delete(event.data.turn))
38
- latest = event;
39
- }
40
- return latest;
41
- }
23
+ export { KNOWN_SESSION_EVENT_TYPES } from "./known-event-types.js";
42
24
  /** Validate and freeze one detached creation header in place. */
43
25
  function validateSessionHeader(id, input) {
44
26
  if (input === null || typeof input !== 'object' || Array.isArray(input)) {
@@ -164,6 +146,7 @@ function assertSessionEventEnvelope(value, index) {
164
146
  case 'data':
165
147
  case 'surfaceOp':
166
148
  case 'sourceEventSeqs':
149
+ case 'ignorable':
167
150
  break;
168
151
  default:
169
152
  throw new Error(`seed event at index ${index} has an invalid event envelope`);
@@ -175,7 +158,8 @@ function assertSessionEventEnvelope(value, index) {
175
158
  if (typeof type !== 'string'
176
159
  || typeof seq !== 'number' || !Number.isSafeInteger(seq) || seq < 0
177
160
  || typeof time !== 'number' || !Number.isSafeInteger(time)
178
- || event['data'] === undefined) {
161
+ || event['data'] === undefined
162
+ || (event['ignorable'] !== undefined && event['ignorable'] !== true)) {
179
163
  throw new Error(`seed event at index ${index} has an invalid event envelope`);
180
164
  }
181
165
  switch (type) {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * GENERATED by `scripts/gen-persistence-catalog.ts` — do not edit by hand; run
3
+ * `pnpm run gen-persistence-catalog` to regenerate (verified fresh by
4
+ * `pnpm run verify-persistence-catalog`, part of `doc-sync`).
5
+ * @module @deepseek-ai/dsh-session/known-event-types
6
+ */
7
+ /**
8
+ * Every `SessionEventMap` member declared in this repository — the event
9
+ * vocabulary this build understands. The persistence read path refuses to
10
+ * interpret a log containing a type outside this set unless the event
11
+ * carries the envelope's `ignorable` marker (see `SessionEvent.ignorable`
12
+ * in `./types.ts`): such a log was likely written by a newer harness, and
13
+ * silently skipping a required event would reconstruct a wrong session.
14
+ * Downstream (out-of-repo) plugin events are outside this list by
15
+ * construction; a registration surface for them is deferred until such a
16
+ * consumer exists.
17
+ */
18
+ export declare const KNOWN_SESSION_EVENT_TYPES: ReadonlySet<string>;
19
+ //# sourceMappingURL=known-event-types.d.ts.map
@@ -0,0 +1,64 @@
1
+ /**
2
+ * GENERATED by `scripts/gen-persistence-catalog.ts` — do not edit by hand; run
3
+ * `pnpm run gen-persistence-catalog` to regenerate (verified fresh by
4
+ * `pnpm run verify-persistence-catalog`, part of `doc-sync`).
5
+ * @module @deepseek-ai/dsh-session/known-event-types
6
+ */
7
+ /**
8
+ * Every `SessionEventMap` member declared in this repository — the event
9
+ * vocabulary this build understands. The persistence read path refuses to
10
+ * interpret a log containing a type outside this set unless the event
11
+ * carries the envelope's `ignorable` marker (see `SessionEvent.ignorable`
12
+ * in `./types.ts`): such a log was likely written by a newer harness, and
13
+ * silently skipping a required event would reconstruct a wrong session.
14
+ * Downstream (out-of-repo) plugin events are outside this list by
15
+ * construction; a registration surface for them is deferred until such a
16
+ * consumer exists.
17
+ */
18
+ export const KNOWN_SESSION_EVENT_TYPES = new Set([
19
+ 'agent-preset/selected',
20
+ 'agent/inbox/spliced',
21
+ 'approval/asked',
22
+ 'approval/decided',
23
+ 'approval/policy',
24
+ 'assistant/chunk',
25
+ 'assistant/message',
26
+ 'command/done',
27
+ 'command/run',
28
+ 'compact/end',
29
+ 'compact/prune',
30
+ 'compact/start',
31
+ 'compact/summary',
32
+ 'feedback/record',
33
+ 'goal/change',
34
+ 'hook/invoked',
35
+ 'hook/result',
36
+ 'llm/retry',
37
+ 'llm/retry-started',
38
+ 'permission/preset',
39
+ 'plan/mode',
40
+ 'request/context',
41
+ 'request/header',
42
+ 'sandbox/mode',
43
+ 'schedule/change',
44
+ 'session/end-seed',
45
+ 'session/title',
46
+ 'session/title-llm-request',
47
+ 'step/end',
48
+ 'step/start',
49
+ 'subagent/descriptor',
50
+ 'todo/write',
51
+ 'tool-workflow/agent-end',
52
+ 'tool-workflow/agent-start',
53
+ 'tool-workflow/run-end',
54
+ 'tool-workflow/run-start',
55
+ 'tool/call',
56
+ 'tool/code-dispatch',
57
+ 'tool/code-dispatch-start',
58
+ 'tool/result',
59
+ 'turn/end',
60
+ 'turn/start',
61
+ 'user/message',
62
+ 'web/deepseek-search-llm-request',
63
+ ]);
64
+ //# sourceMappingURL=known-event-types.js.map
@@ -1,6 +1,7 @@
1
1
  import type { Branded } from '@deepseek-ai/dsh-brand';
2
2
  import type { AssistantMessage, CallId, LlmCallConfig, LlmCallConfigAdapterDefaults, LlmFailure, StreamChunk, TokenUsage, ToolResultMessage, ToolSchema, UserMessage } from '@deepseek-ai/dsh-llm';
3
3
  import type { JsonValue } from './json.ts';
4
+ export type { JsonValue } from './json.ts';
4
5
  /** Identifies one session in the store (and its persistence artifacts). */
5
6
  export type SessionId = Branded<'SessionId'>;
6
7
  /**
@@ -14,8 +15,23 @@ export declare function SessionId(id: string): SessionId;
14
15
  * and enforced by every persistence backend on load. The single source of truth for the
15
16
  * version — write sites and the load-time check all read it.
16
17
  * While the harness is unreleased it is pinned at `0`: no compatibility is
17
- * implied, incompatible logs are rejected, and no migration is provided. A
18
- * monotonic version policy starts with the first tagged release.
18
+ * implied, incompatible logs are rejected, and no migration is provided.
19
+ *
20
+ * The version is a single monotonic integer with no major/minor split. Whether
21
+ * a bump is needed is decided by what the WRITER emits, never by what a newer
22
+ * reader can accept: bump exactly when an older runtime could no longer handle
23
+ * a new log with full semantic correctness ("parses without error" is not
24
+ * correctness — silently skipping content that shapes reconstruction is a
25
+ * wrong read). Only structural changes reach that bar: the header shape, the
26
+ * {@link SessionEvent} envelope, core event semantics, or the surface
27
+ * mechanism (the {@link SurfaceEventType} set and {@link SurfaceOp} variants).
28
+ * Adding an ordinary event type does not bump — the per-event
29
+ * {@link SessionEvent.ignorable} guard covers vocabulary growth instead. When
30
+ * in doubt, bump: a near-identity upgrade step is almost free, a missed bump
31
+ * makes older runtimes read new logs wrong silently. The full mechanism
32
+ * (upgrade-step chain, in-memory view conversion, migrate-on-continue) is
33
+ * recorded in the session-log-version-mechanism Agent Note
34
+ * (`.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md`).
19
35
  */
20
36
  export declare const SESSION_FORMAT_VERSION = 0;
21
37
  /**
@@ -409,6 +425,17 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
409
425
  /** Unix epoch milliseconds. */
410
426
  time: number;
411
427
  data: SessionEventMap[K];
428
+ /**
429
+ * Marks an event a reader may safely skip when it does not recognize
430
+ * `type`. Absent means required: a reader meeting an unrecognized type
431
+ * without this marker MUST refuse to reconstruct the session instead of
432
+ * silently dropping the event, because an unrecognized required event may
433
+ * change how the rest of the log is interpreted. A writer sets `true` only
434
+ * on purely informational records whose loss cannot affect reconstruction;
435
+ * defaulting to required means a forgotten marker over-refuses (an
436
+ * inconvenience) rather than silently resuming a gutted session.
437
+ */
438
+ ignorable?: true;
412
439
  } & (K extends SurfaceEventType ? {
413
440
  /**
414
441
  * Seq numbers of earlier events that this event cites as sources
@@ -11,8 +11,23 @@ export function SessionId(id) {
11
11
  * and enforced by every persistence backend on load. The single source of truth for the
12
12
  * version — write sites and the load-time check all read it.
13
13
  * While the harness is unreleased it is pinned at `0`: no compatibility is
14
- * implied, incompatible logs are rejected, and no migration is provided. A
15
- * monotonic version policy starts with the first tagged release.
14
+ * implied, incompatible logs are rejected, and no migration is provided.
15
+ *
16
+ * The version is a single monotonic integer with no major/minor split. Whether
17
+ * a bump is needed is decided by what the WRITER emits, never by what a newer
18
+ * reader can accept: bump exactly when an older runtime could no longer handle
19
+ * a new log with full semantic correctness ("parses without error" is not
20
+ * correctness — silently skipping content that shapes reconstruction is a
21
+ * wrong read). Only structural changes reach that bar: the header shape, the
22
+ * {@link SessionEvent} envelope, core event semantics, or the surface
23
+ * mechanism (the {@link SurfaceEventType} set and {@link SurfaceOp} variants).
24
+ * Adding an ordinary event type does not bump — the per-event
25
+ * {@link SessionEvent.ignorable} guard covers vocabulary growth instead. When
26
+ * in doubt, bump: a near-identity upgrade step is almost free, a missed bump
27
+ * makes older runtimes read new logs wrong silently. The full mechanism
28
+ * (upgrade-step chain, in-memory view conversion, migrate-on-continue) is
29
+ * recorded in the session-log-version-mechanism Agent Note
30
+ * (`.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md`).
16
31
  */
17
32
  export const SESSION_FORMAT_VERSION = 0;
18
33
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-session",
3
3
  "description": "Event-sourced session store for the DeepSeek Harness",
4
- "version": "0.0.1-rc.1",
4
+ "version": "0.0.1-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -41,20 +41,20 @@
41
41
  ],
42
42
  "license": "BSD-3-Clause",
43
43
  "peerDependencies": {
44
- "@deepseek-ai/dsh-brand": "^0.0.1-rc.1",
45
- "@deepseek-ai/dsh-llm": "^0.0.1-rc.1",
46
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
47
- "@deepseek-ai/dsh-scope": "^0.0.1-rc.1",
48
- "@deepseek-ai/dsh-type-meta": "^0.0.1-rc.1",
44
+ "@deepseek-ai/dsh-brand": "^0.0.1-rc.2",
45
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
46
+ "@deepseek-ai/dsh-llm": "^0.0.1-rc.2",
47
+ "@deepseek-ai/dsh-scope": "^0.0.1-rc.2",
48
+ "@deepseek-ai/dsh-type-meta": "^0.0.1-rc.2",
49
49
  "@deepseek-ai/cordis": "^4.0.1-rc.1"
50
50
  },
51
51
  "devDependencies": {
52
- "@deepseek-ai/dsh-brand": "^0.0.1-rc.1",
53
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
54
- "@deepseek-ai/dsh-llm": "^0.0.1-rc.1",
55
- "@deepseek-ai/dsh-type-meta": "^0.0.1-rc.1",
56
- "@deepseek-ai/dsh-typert-registry": "^0.0.1-rc.1",
57
- "@deepseek-ai/dsh-scope": "^0.0.1-rc.1",
52
+ "@deepseek-ai/dsh-brand": "^0.0.1-rc.2",
53
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
54
+ "@deepseek-ai/dsh-llm": "^0.0.1-rc.2",
55
+ "@deepseek-ai/dsh-scope": "^0.0.1-rc.2",
56
+ "@deepseek-ai/dsh-type-meta": "^0.0.1-rc.2",
57
+ "@deepseek-ai/dsh-typert-registry": "^0.0.1-rc.2",
58
58
  "@deepseek-ai/cordis": "^4.0.1-rc.1"
59
59
  }
60
60
  }