@deepseek-ai/dsh-session-log-deepseek 0.1.7-alpha.2 → 0.1.7-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
@@ -1,6 +1,46 @@
1
- # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
- # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
- # after editing either side, bring the other along and re-record with:
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/session/session-log-deepseek/README.md
5
- README.md: 27396f96191e0f88268558a1426b28e0b7dc6f12
6
- README.zh.md: a2840223f043283b0f7f0176108d23a5c94213ef
5
+ /:
6
+ en: 29b2294131f1a592
7
+ zh: 6d7f4fa9e1f46943
8
+ /deepseek-ai-dsh-session-log-deepseek:
9
+ en: 6d991785cbfd70ae
10
+ zh: 0918e2462821cde4
11
+ /deepseek-ai-dsh-session-log-deepseek/summary:
12
+ en: 19c182dbd7d8e064
13
+ zh: 51c043d6ea52db0d
14
+ /deepseek-ai-dsh-session-log-deepseek/table-of-contents:
15
+ en: e2aa29e41bc101c4
16
+ zh: 2ef629f2a444bc13
17
+ /deepseek-ai-dsh-session-log-deepseek/configuration:
18
+ en: 1e8712ec4f0fefcc
19
+ zh: 5ffe754b2cefdd21
20
+ /deepseek-ai-dsh-session-log-deepseek/request-field:
21
+ en: 197814e2c48d05b5
22
+ zh: f4616fb23a3c7a32
23
+ /deepseek-ai-dsh-session-log-deepseek/acceptance-and-retry:
24
+ en: 3688592c474b3e95
25
+ zh: 5e83afeb9aa9e9e3
26
+ /deepseek-ai-dsh-session-log-deepseek/model-experience:
27
+ en: 215e7ba838619b7b
28
+ zh: a311da3843709f90
29
+ /deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata:
30
+ en: 468b9eaee350f6e2
31
+ zh: c192ad6228e7044d
32
+ /deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata/what-the-model-sees:
33
+ en: 89fef23a54a0f9d6
34
+ zh: fd17806f1df6083e
35
+ /deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata/token-effect:
36
+ en: 6a1bb7bbc7db7f06
37
+ zh: 94ddaa773b7538f7
38
+ /deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata/kv-cache-effect:
39
+ en: dcf00aa1af688f09
40
+ zh: df5851d66c3a6b4f
41
+ /deepseek-ai-dsh-session-log-deepseek/known-limitations-and-deferred-work:
42
+ en: 249ef8dbc237a0c2
43
+ zh: c169d64ce9ab984c
44
+ /deepseek-ai-dsh-session-log-deepseek/known-limitations-and-deferred-work/dev-note:
45
+ en: e67f633dc6fe7773
46
+ zh: e88aeb617ffb74ea
package/README.md CHANGED
@@ -28,18 +28,21 @@ Incremental canonical session-log upload for official DeepSeek LLM API requests.
28
28
  | Key | Default | Meaning |
29
29
  |---|---:|---|
30
30
  | `enabled` | `true` | Register the `dsh_session_log` contribution. Set it to `false` to stop Session-log upload. |
31
+ | `maxBytes` | 8 MiB | Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries. |
31
32
 
32
33
  Shipped profiles mount the plugin, so the default configuration registers the request field and appends the acceptance watermark; an overlay opts out with `enabled: false`.
33
34
 
34
35
  <a id="request-field"></a>
35
36
  ## Request field
36
37
 
37
- For a request carrying a live `sessionId`, the plugin folds the greatest accepted watermark for that exact Session format generation, snapshots `Session.events`, and sends the contiguous suffix after the watermark. A process-local fold scans each event once and consumes later appends incrementally; restart and HMR rebuild it from the durable log. The version-1 field contains `sessionFormatVersion`, a raw session header (`seedLength` is present only for a seeded Session), numeric `afterSeq` and `throughSeq`, and every complete canonical event translated to raw-number envelope fields. Forked sessions ignore inherited parent watermarks because both the recorded Session id and format generation must match the request source. Surface events require `surfaceOp`, with numeric `startSeq` and `endSeq` for replacements; only system, user, and tool events may carry `sourceEventSeqs`. Assistant provider metadata stays in the embedded stream, and log-only events carry neither metadata field.
38
+ For a request carrying a live `sessionId`, the plugin folds the greatest accepted watermark for that exact Session format generation, snapshots `Session.events`, and sends the longest contiguous run after the watermark that fits `maxBytes`. A process-local fold scans each event once and consumes later appends incrementally; restart and HMR rebuild it from the durable log. The version-1 field contains `sessionFormatVersion`, a raw session header (`seedLength` is present only for a seeded Session), numeric `afterSeq` and `throughSeq`, and every complete canonical event translated to raw-number envelope fields. Forked sessions ignore inherited parent watermarks because both the recorded Session id and format generation must match the request source. Surface events require `surfaceOp`, with numeric `startSeq` and `endSeq` for replacements; only system, user, and tool events may carry `sourceEventSeqs`. Assistant provider metadata stays in the embedded stream, and log-only events carry neither metadata field.
39
+
40
+ `maxBytes` bounds the complete serialized field in UTF-8 bytes, including the header and numeric envelope fields. A backlog above the limit drains across consecutive accepted requests, each continuing after the previous `throughSeq`. When the first pending event alone exceeds the limit, the request omits `dsh_session_log`, the plugin logs a warning, and the watermark stays before that event until `maxBytes` admits it. An event too large for the runtime to serialize at all is handled the same way, and no `maxBytes` value admits it.
38
41
 
39
42
  <a id="acceptance-and-retry"></a>
40
43
  ## Acceptance and retry
41
44
 
42
- The DeepSeek adapter calls the prepared contribution's `accept()` after HTTP 2xx, before it consumes the SSE body. Acceptance appends `session-log-deepseek/delivery-accepted` with the uploaded `throughSeq` and `sessionFormatVersion`; a record that omits the format field denotes v0. The next request uploads that event as part of its new suffix. Transport and non-2xx failures append no acceptance record, so later requests resend the uncertain range. Concurrent deliveries may be accepted out of order; folding the maximum matching `throughSeq` prevents cursor regression.
45
+ The DeepSeek adapter calls the prepared contribution's `accept()` after HTTP 2xx, before it consumes the SSE body. Acceptance appends `session-log-deepseek/delivery-accepted` with the uploaded `throughSeq` and `sessionFormatVersion`; a record that omits the format field denotes v0. The next request uploads that event as part of its new suffix. Transport failures, non-2xx failures, and requests that the DeepSeek adapter sends without extension fields after they fail to serialize append no acceptance record, so later requests resend the uncertain range. Concurrent deliveries may be accepted out of order; folding the maximum matching `throughSeq` prevents cursor regression.
43
46
 
44
47
  A crash after server acceptance but before the watermark reaches persistence can replay an accepted range after restart. This is the at-least-once failure direction: uncertainty creates duplicates, never a skipped sequence. The ordinary session checkpoint policy persists the watermark at the next semantic checkpoint; this plugin performs no independent I/O.
45
48
 
@@ -56,7 +59,7 @@ Nothing. `dsh_session_log` is a sibling of the DeepSeek request's model-input fi
56
59
 
57
60
  #### Token effect
58
61
 
59
- Zero model-input tokens; the field only increases HTTP request bytes.
62
+ Zero model-input tokens; the field only increases HTTP request bytes, bounded by `maxBytes`.
60
63
 
61
64
  #### KV Cache effect
62
65
 
@@ -68,7 +71,8 @@ None; the model-visible request prefix remains unchanged.
68
71
 
69
72
  - **Crash-window duplicates** — a 2xx followed by process loss before the acceptance watermark persists causes conservative replay on resume.
70
73
  - **No live Session means no field** — direct or stale-session calls have no canonical log to snapshot; explicit absence semantics remain deferred.
71
- - **No independent request-size cap** — complete delivery is fail-closed; provider rejection leaves the cursor unchanged instead of truncating the log.
74
+ - **An oversized event stalls upload** — an event whose field alone exceeds `maxBytes` is not sent, and later events wait behind it until the limit increases; an event too large to serialize at all stays blocked.
75
+ - **Provider rejection fails the request** — a provider that rejects the request because of this field fails the model request and leaves the cursor unchanged instead of truncating the log.
72
76
 
73
77
  <a id="dev-note"></a>
74
78
  ### Dev Note
package/README.zh.md CHANGED
@@ -28,18 +28,21 @@ kind: "package-reference"
28
28
  | 配置键 | 默认值 | 含义 |
29
29
  |---|---:|---|
30
30
  | `enabled` | `true` | 注册 `dsh_session_log` 贡献。将其设为 `false` 可停止会话日志上传。 |
31
+ | `maxBytes` | 8 MiB | 单次请求携带的 `dsh_session_log` 字段序列化后的最大 UTF-8 字节数。 |
31
32
 
32
33
  随附 profile 会挂载该插件,因此默认配置会注册请求字段并追加接受水位;overlay 可用 `enabled: false` 选择退出。
33
34
 
34
35
  <a id="request-field"></a>
35
36
  ## 请求字段
36
37
 
37
- 对于携带存活 `sessionId` 的请求,插件会折叠该确切会话格式代的最大已接受水位,对 `Session.events` 取快照,并发送水位之后的连续后缀。进程内 fold 会让每条事件只被扫描一次并增量消费后续追加;重启与 HMR(热模块替换)会从持久日志重建它。版本 1 字段包含 `sessionFormatVersion`、原始会话 header(仅 seeded Session 携带 `seedLength`)、数值型 `afterSeq` 与 `throughSeq`,以及每个已转换为原始数值 envelope 字段的完整规范事件。只有记录的会话 id 与格式代均匹配请求来源时水位才生效,因此 fork 会话会忽略从父会话继承的水位。表层事件必须携带 `surfaceOp`,替换范围使用数值型 `startSeq` 与 `endSeq`;仅 system、user 与 tool 事件可以携带 `sourceEventSeqs`。assistant 的提供方元数据保留在内嵌流中,只出现在日志中的事件不携带这两个元数据字段。
38
+ 对于携带存活 `sessionId` 的请求,插件会折叠该确切会话格式代的最大已接受水位,对 `Session.events` 取快照,并发送水位之后能放进 `maxBytes` 的最长连续事件段。进程内 fold 会让每条事件只被扫描一次并增量消费后续追加;重启与 HMR(热模块替换)会从持久日志重建它。版本 1 字段包含 `sessionFormatVersion`、原始会话 header(仅 seeded Session 携带 `seedLength`)、数值型 `afterSeq` 与 `throughSeq`,以及每个已转换为原始数值 envelope 字段的完整规范事件。只有记录的会话 id 与格式代均匹配请求来源时水位才生效,因此 fork 会话会忽略从父会话继承的水位。表层事件必须携带 `surfaceOp`,替换范围使用数值型 `startSeq` 与 `endSeq`;仅 system、user 与 tool 事件可以携带 `sourceEventSeqs`。assistant 的提供方元数据保留在内嵌流中,只出现在日志中的事件不携带这两个元数据字段。
39
+
40
+ `maxBytes` 按 UTF-8 字节限制完整的序列化字段,包括 header 与数值型 envelope 字段。超过上限的积压会分多次已接受的请求补传,每次都从上一次的 `throughSeq` 之后继续。第一条待发事件本身就超过上限时,请求会省略 `dsh_session_log`,插件记录一条告警,水位停在该事件之前,直到 `maxBytes` 能容纳它。运行时根本无法序列化的超大事件也按同样方式处理,且任何 `maxBytes` 都无法容纳它。
38
41
 
39
42
  <a id="acceptance-and-retry"></a>
40
43
  ## 接受与重试
41
44
 
42
- DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文前调用已准备贡献的 `accept()`。接受操作会追加 `session-log-deepseek/delivery-accepted`,其中包含已上传的 `throughSeq` 与 `sessionFormatVersion`;省略格式字段的记录表示 v0。下一次请求再把该事件作为新后缀的一部分上传。传输失败与非 2xx 失败不会追加接受记录,因此后续请求会重发不确定范围。并发交付可能乱序得到接受;折叠匹配记录中最大的 `throughSeq` 可以防止游标回退。
45
+ DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文前调用已准备贡献的 `accept()`。接受操作会追加 `session-log-deepseek/delivery-accepted`,其中包含已上传的 `throughSeq` 与 `sessionFormatVersion`;省略格式字段的记录表示 v0。下一次请求再把该事件作为新后缀的一部分上传。传输失败、非 2xx 失败,以及 DeepSeek 适配器在扩展字段序列化失败后不带扩展字段发出的请求,都不会追加接受记录,因此后续请求会重发不确定范围。并发交付可能乱序得到接受;折叠匹配记录中最大的 `throughSeq` 可以防止游标回退。
43
46
 
44
47
  服务端接受后、持久化水位前发生崩溃,可能让恢复后的进程重放已经接受的范围。这是至少一次交付的失败方向:不确定性会制造重复,绝不会跳过序列。普通会话检查点策略会在下一个语义检查点持久化水位;本插件不执行独立 I/O。
45
48
 
@@ -56,7 +59,7 @@ DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文
56
59
 
57
60
  #### Token 影响
58
61
 
59
- 模型输入 token 为零;该字段只会增加 HTTP 请求字节数。
62
+ 模型输入 token 为零;该字段只会增加 HTTP 请求字节数,且受 `maxBytes` 限制。
60
63
 
61
64
  #### KV Cache 影响
62
65
 
@@ -68,7 +71,8 @@ DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文
68
71
 
69
72
  - **崩溃窗口重复**——2xx 后、接受水位持久化前进程终止,会在恢复时触发保守重放。
70
73
  - **缺少存活会话就没有字段**——直接调用或陈旧会话调用没有可供快照的规范日志;显式缺失语义仍暂缓处理。
71
- - **没有独立请求大小上限**——完整交付采用 fail-closed 策略;提供方拒绝会保持游标不变,而非截断日志。
74
+ - **超限事件会让上传停住**——字段单独就超过 `maxBytes` 的事件不会发送,后续事件也排在它后面,直到上限调高;根本无法序列化的事件会一直阻塞上传。
75
+ - **提供方拒绝会让请求失败**——提供方因该字段拒绝请求时,模型请求失败,游标保持不变,而非截断日志。
72
76
 
73
77
  <a id="dev-note"></a>
74
78
  ### 开发备注
package/lib/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { Buffer } from "node:buffer";
1
2
  import z from "@deepseek-ai/schemastery";
2
3
  import { brandString } from "@deepseek-ai/dsh-brand";
3
4
  import { KNOWN_SESSION_EVENT_TYPES, SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
@@ -13,7 +14,10 @@ const name = "session-log-deepseek";
13
14
  /** Services required to resolve sessions and contribute the provider request field. */
14
15
  const inject = ["deepseekLlmApiExtensions", "sessions"];
15
16
  /** Validated Session-log request contribution configuration. */
16
- const Config = z.object({ enabled: z.boolean().default(true) });
17
+ const Config = z.object({
18
+ enabled: z.boolean().default(true),
19
+ maxBytes: z.number().step(1).min(1).default(8 * 1024 * 1024)
20
+ });
17
21
  const acceptanceFolds = /* @__PURE__ */ new WeakMap();
18
22
  /** Translate logical Session metadata to raw external request fields. */
19
23
  function wireHeader(session) {
@@ -78,6 +82,19 @@ function wireSurfaceOp(op) {
78
82
  };
79
83
  }
80
84
  /**
85
+ * UTF-8 length of one value's JSON text as the request body encodes it.
86
+ * @returns `Infinity` when the value fails to serialize, which counts as exceeding every limit.
87
+ */
88
+ function jsonBytes(value) {
89
+ let text;
90
+ try {
91
+ text = JSON.stringify(value);
92
+ } catch (_unserializable) {
93
+ return Number.POSITIVE_INFINITY;
94
+ }
95
+ return Buffer.byteLength(text);
96
+ }
97
+ /**
81
98
  * Highest confirmed sequence for this exact Session format generation.
82
99
  * @param session - canonical log whose matching acceptance events are folded.
83
100
  * @returns greatest accepted sequence, or `-1` before any accepted request.
@@ -117,22 +134,49 @@ function acceptedThrough(session) {
117
134
  */
118
135
  function apply(ctx, config) {
119
136
  if (config.enabled !== true) return;
137
+ const { maxBytes } = config;
120
138
  ctx.deepseekLlmApiExtensions.register("dsh_session_log", { prepare: (request) => {
121
139
  if (request.sessionId === void 0) return void 0;
122
140
  const session = ctx.sessions.get(brandString(request.sessionId));
123
141
  if (session === void 0) return void 0;
124
142
  const afterSeq = acceptedThrough(session);
125
- const throughSeq = session.snapshotEvents().at(-1)?.seq;
126
- if (throughSeq === void 0) return void 0;
127
- const suffix = session.snapshotEvents(SessionLogOffset(afterSeq + 1));
143
+ const pending = session.snapshotEvents(SessionLogOffset(afterSeq + 1));
144
+ const envelope = {
145
+ version: 1,
146
+ sessionFormatVersion: session.header.version,
147
+ session: wireHeader(session),
148
+ afterSeq: Number(afterSeq)
149
+ };
150
+ let bytes = jsonBytes({
151
+ ...envelope,
152
+ throughSeq: 0,
153
+ events: []
154
+ }) - 1;
155
+ const events = [];
156
+ let candidateBytes = 0;
157
+ for (const event of pending) {
158
+ const wire = wireEvent(event);
159
+ const next = bytes + (events.length === 0 ? 0 : 1) + jsonBytes(wire);
160
+ candidateBytes = next + String(event.seq).length;
161
+ if (candidateBytes > maxBytes) break;
162
+ bytes = next;
163
+ events.push(wire);
164
+ }
165
+ const last = events.length === 0 ? void 0 : pending[events.length - 1];
166
+ if (last === void 0) {
167
+ const first = pending[0];
168
+ if (first !== void 0) {
169
+ const seq = String(first.seq);
170
+ ctx.logger.warn(Number.isFinite(candidateBytes) ? `session-log-deepseek: event ${seq} of session "${session.id}" needs a ${String(candidateBytes)}-byte dsh_session_log field, above maxBytes ${String(maxBytes)}; this session's upload stays at event ${seq} until maxBytes admits it` : `session-log-deepseek: event ${seq} of session "${session.id}" is too large to serialize into a dsh_session_log field; this session's upload stays at event ${seq}`);
171
+ }
172
+ return;
173
+ }
174
+ const throughSeq = last.seq;
128
175
  return {
129
176
  value: {
130
- version: 1,
131
- sessionFormatVersion: session.header.version,
132
- session: wireHeader(session),
133
- afterSeq: Number(afterSeq),
177
+ ...envelope,
134
178
  throughSeq: Number(throughSeq),
135
- events: suffix.map(wireEvent)
179
+ events
136
180
  },
137
181
  accept: () => {
138
182
  session.append("session-log-deepseek/delivery-accepted", {
@@ -16,6 +16,12 @@ export declare const inject: string[];
16
16
  export interface Config {
17
17
  /** Contribute `dsh_session_log` to official DeepSeek requests. Defaults to `true`. */
18
18
  enabled?: boolean;
19
+ /**
20
+ * Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries.
21
+ * A request uploads the longest pending event prefix that fits; later requests continue
22
+ * after its acceptance. Defaults to 8 MiB.
23
+ */
24
+ maxBytes?: number;
19
25
  }
20
26
  /** Validated Session-log request contribution configuration. */
21
27
  export declare const Config: z<Config>;
@@ -4,6 +4,7 @@
4
4
  * can conservatively resend uncertain tails without maintaining another store.
5
5
  * @module @deepseek-ai/dsh-session-log-deepseek
6
6
  */
7
+ import { Buffer } from 'node:buffer';
7
8
  import z from '@deepseek-ai/schemastery';
8
9
  import { brandString } from '@deepseek-ai/dsh-brand';
9
10
  import { KNOWN_SESSION_EVENT_TYPES, SessionLogOffset, SessionSeq } from '@deepseek-ai/dsh-session';
@@ -14,6 +15,7 @@ export const inject = ['deepseekLlmApiExtensions', 'sessions'];
14
15
  /** Validated Session-log request contribution configuration. */
15
16
  export const Config = z.object({
16
17
  enabled: z.boolean().default(true),
18
+ maxBytes: z.number().step(1).min(1).default(8 * 1024 * 1024),
17
19
  });
18
20
  const acceptanceFolds = new WeakMap();
19
21
  /** Translate logical Session metadata to raw external request fields. */
@@ -71,6 +73,21 @@ function wireSurfaceOp(op) {
71
73
  ? op
72
74
  : { op: 'replace', startSeq: Number(op.startSeq), endSeq: Number(op.endSeq) };
73
75
  }
76
+ /**
77
+ * UTF-8 length of one value's JSON text as the request body encodes it.
78
+ * @returns `Infinity` when the value fails to serialize, which counts as exceeding every limit.
79
+ */
80
+ function jsonBytes(value) {
81
+ let text;
82
+ try {
83
+ text = JSON.stringify(value);
84
+ }
85
+ catch (_unserializable) {
86
+ // No request can carry a value that fails to serialize.
87
+ return Number.POSITIVE_INFINITY;
88
+ }
89
+ return Buffer.byteLength(text);
90
+ }
74
91
  /**
75
92
  * Highest confirmed sequence for this exact Session format generation.
76
93
  * @param session - canonical log whose matching acceptance events are folded.
@@ -124,6 +141,8 @@ export function acceptedThrough(session) {
124
141
  export function apply(ctx, config) {
125
142
  if (config.enabled !== true)
126
143
  return;
144
+ // Schemastery validates and fills the defaults before `apply` runs.
145
+ const { maxBytes } = config;
127
146
  ctx.deepseekLlmApiExtensions.register('dsh_session_log', {
128
147
  prepare: (request) => {
129
148
  // TODO: Define an explicit wire result for direct or stale-session calls if they become a supported product path.
@@ -134,20 +153,43 @@ export function apply(ctx, config) {
134
153
  return undefined;
135
154
  const afterSeq = acceptedThrough(session);
136
155
  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
137
- const snapshot = session.snapshotEvents();
138
- const throughSeq = snapshot.at(-1)?.seq;
139
- if (throughSeq === undefined)
140
- return undefined;
141
- // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
142
- const suffix = session.snapshotEvents(SessionLogOffset(afterSeq + 1));
143
- const value = {
156
+ const pending = session.snapshotEvents(SessionLogOffset(afterSeq + 1));
157
+ const envelope = {
144
158
  version: 1,
145
159
  sessionFormatVersion: session.header.version,
146
160
  session: wireHeader(session),
147
161
  afterSeq: Number(afterSeq),
148
- throughSeq: Number(throughSeq),
149
- events: suffix.map(wireEvent),
150
162
  };
163
+ // Field bytes without events or throughSeq digits; each candidate prefix adds its own.
164
+ let bytes = jsonBytes({ ...envelope, throughSeq: 0, events: [] }) - 1;
165
+ const events = [];
166
+ // Field bytes with the last examined event; when none fits, the first event's own field size.
167
+ let candidateBytes = 0;
168
+ for (const event of pending) {
169
+ const wire = wireEvent(event);
170
+ const next = bytes + (events.length === 0 ? 0 : 1) + jsonBytes(wire);
171
+ candidateBytes = next + String(event.seq).length;
172
+ if (candidateBytes > maxBytes)
173
+ break;
174
+ bytes = next;
175
+ events.push(wire);
176
+ }
177
+ const last = events.length === 0 ? undefined : pending[events.length - 1];
178
+ if (last === undefined) {
179
+ const first = pending[0];
180
+ if (first !== undefined) {
181
+ const seq = String(first.seq);
182
+ ctx.logger.warn(Number.isFinite(candidateBytes)
183
+ ? `session-log-deepseek: event ${seq} of session "${session.id}" needs a ${String(candidateBytes)}-byte`
184
+ + ` dsh_session_log field, above maxBytes ${String(maxBytes)}; this session's upload stays at event ${seq}`
185
+ + ' until maxBytes admits it'
186
+ : `session-log-deepseek: event ${seq} of session "${session.id}" is too large to serialize into a dsh_session_log field;`
187
+ + ` this session's upload stays at event ${seq}`);
188
+ }
189
+ return undefined;
190
+ }
191
+ const throughSeq = last.seq;
192
+ const value = { ...envelope, throughSeq: Number(throughSeq), events };
151
193
  return {
152
194
  value,
153
195
  accept: () => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-session-log-deepseek",
3
3
  "description": "Incremental lossless session-log request extension for the official DeepSeek LLM API",
4
- "version": "0.1.7-alpha.2",
4
+ "version": "0.1.7-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -37,28 +37,29 @@
37
37
  ],
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "@deepseek-ai/dsh-brand": "0.1.7-alpha.2",
41
- "@deepseek-ai/dsh-util-values": "0.1.7-alpha.2",
40
+ "@deepseek-ai/dsh-brand": "0.1.7-rc.2",
41
+ "@deepseek-ai/dsh-util-values": "0.1.7-rc.2",
42
42
  "@deepseek-ai/schemastery": "~3.18.4"
43
43
  },
44
44
  "peerDependencies": {
45
- "@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.7-alpha.2",
46
45
  "@deepseek-ai/cordis": "~4.0.4",
47
- "@deepseek-ai/dsh-invariants": "0.1.7-alpha.2",
48
- "@deepseek-ai/dsh-session": "0.1.7-alpha.2"
46
+ "@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.7-rc.2",
47
+ "@deepseek-ai/dsh-invariants": "0.1.7-rc.2",
48
+ "@deepseek-ai/dsh-session": "0.1.7-rc.2"
49
49
  },
50
50
  "devDependencies": {
51
+ "@deepseek-ai/cordis": "~4.0.4",
51
52
  "@deepseek-ai/cordis-plugin-include": "~1.0.9",
52
53
  "@deepseek-ai/cordis-plugin-loader": "~1.0.5",
53
- "@deepseek-ai/dsh-command-feedback": "0.1.7-alpha.2",
54
- "@deepseek-ai/cordis": "~4.0.4",
55
- "@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.7-alpha.2",
56
- "@deepseek-ai/dsh-invariants": "0.1.7-alpha.2",
57
- "@deepseek-ai/dsh-llm": "0.1.7-alpha.2",
58
- "@deepseek-ai/dsh-llm-deepseek": "0.1.7-alpha.2",
59
- "@deepseek-ai/dsh-llm-mock-server": "0.1.7-alpha.2",
60
- "@deepseek-ai/dsh-message-feedback": "0.1.7-alpha.2",
61
- "@deepseek-ai/dsh-session": "0.1.7-alpha.2",
62
- "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-alpha.2"
54
+ "@deepseek-ai/dsh-command-feedback": "0.1.7-rc.2",
55
+ "@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.7-rc.2",
56
+ "@deepseek-ai/dsh-invariants": "0.1.7-rc.2",
57
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
58
+ "@deepseek-ai/dsh-llm-deepseek": "0.1.7-rc.2",
59
+ "@deepseek-ai/dsh-llm-deepseek-api-key": "0.1.7-rc.2",
60
+ "@deepseek-ai/dsh-message-feedback": "0.1.7-rc.2",
61
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-rc.2",
62
+ "@deepseek-ai/dsh-session": "0.1.7-rc.2",
63
+ "@deepseek-ai/dsh-llm-mock-server": "0.1.7-rc.2"
63
64
  }
64
65
  }