dsh-lcx-codex 0.3.1 → 0.3.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/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.3.2 - 2026-08-21
8
+
9
+ ### Fixed
10
+
11
+ - Native V2 compact 与同路由 replay 现在和 DSH/Pi 普通 Responses 请求复用相同的 session `prompt_cache_key`、缓存 retention 与会话 header,避免压缩边界额外切换 NewAPI/Sub2API 的缓存和账号粘性域;`cacheRetention: none` 时同时省略这些缓存信号。
12
+ - 移除未被插件直接导入或注入的 `@deepseek-ai/dsh-compaction-basic` peer 声明,避免 DSH/pnpm 安装时出现误导性的宿主依赖警告;Basic fallback 继续通过 DSH `llm/stream` 的 `next()` 链调用宿主实现。
13
+
7
14
  ## 0.3.1 - 2026-08-21
8
15
 
9
16
  ### Fixed
package/README.md CHANGED
@@ -78,7 +78,7 @@ dsh plugin --profile web add dsh-lcx-codex
78
78
  也可以下载 GitHub Release 中的 `.tgz` 安装指定版本:
79
79
 
80
80
  ```powershell
81
- dsh plugin --profile web add .\dsh-lcx-codex-0.3.1.tgz
81
+ dsh plugin --profile web add .\dsh-lcx-codex-0.3.2.tgz
82
82
  ```
83
83
 
84
84
  安装后启动 DSH:
package/README_EN.md CHANGED
@@ -78,7 +78,7 @@ dsh plugin --profile web add dsh-lcx-codex
78
78
  You can also install a specific `.tgz` file from a GitHub Release:
79
79
 
80
80
  ```powershell
81
- dsh plugin --profile web add .\dsh-lcx-codex-0.3.1.tgz
81
+ dsh plugin --profile web add .\dsh-lcx-codex-0.3.2.tgz
82
82
  ```
83
83
 
84
84
  Start DSH after installation:
package/lib/compact-v2.js CHANGED
@@ -156,7 +156,7 @@ export function mergeFeatureHeader(headers) {
156
156
  return result
157
157
  }
158
158
 
159
- export function buildNativeCompactionBody({ model, input, instructions, promptCacheKey, tools }) {
159
+ export function buildNativeCompactionBody({ model, input, instructions, promptCacheKey, promptCacheRetention, tools }) {
160
160
  if (!Array.isArray(input)) throw protocolError('Native compaction input must be an array', 'LCX_COMPACT_INVALID_INPUT')
161
161
  if (input.some((item) => isObject(item) && item.type === 'compaction_trigger')) {
162
162
  throw protocolError('Native compaction input already contains compaction_trigger', 'LCX_COMPACT_DUPLICATE_TRIGGER')
@@ -170,6 +170,7 @@ export function buildNativeCompactionBody({ model, input, instructions, promptCa
170
170
  ...(nativeTools !== undefined ? { tools: nativeTools } : {}),
171
171
  ...(instructions !== undefined ? { instructions: structuredClone(instructions) } : {}),
172
172
  ...(promptCacheKey !== undefined && promptCacheKey !== null ? { prompt_cache_key: String(promptCacheKey) } : {}),
173
+ ...(promptCacheRetention !== undefined && promptCacheRetention !== null ? { prompt_cache_retention: String(promptCacheRetention) } : {}),
173
174
  }
174
175
  }
175
176
 
@@ -273,6 +274,7 @@ export async function requestNativeCompaction({
273
274
  input,
274
275
  instructions,
275
276
  promptCacheKey,
277
+ promptCacheRetention,
276
278
  idempotencyKey,
277
279
  tools,
278
280
  headers,
@@ -281,7 +283,7 @@ export async function requestNativeCompaction({
281
283
  maxAttempts,
282
284
  maxResponseBytes,
283
285
  }) {
284
- const body = buildNativeCompactionBody({ model, input, instructions, promptCacheKey, tools })
286
+ const body = buildNativeCompactionBody({ model, input, instructions, promptCacheKey, promptCacheRetention, tools })
285
287
  const requestHeaders = { ...(headers ?? {}) }
286
288
  if (idempotencyKey !== undefined && idempotencyKey !== null && !Object.keys(requestHeaders).some((name) => name.toLowerCase() === 'idempotency-key')) {
287
289
  requestHeaders['idempotency-key'] = String(idempotencyKey)
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import z from '@deepseek-ai/schemastery'
2
2
  import { attributionHeaders, resolveRetryPolicy } from '@deepseek-ai/dsh-llm'
3
3
  import { settingsNamespace } from '@deepseek-ai/dsh-settings'
4
+ import { clampOpenAIPromptCacheKey } from '@earendil-works/pi-ai/api/openai-prompt-cache'
4
5
  import { randomUUID } from 'node:crypto'
5
6
  import { homedir } from 'node:os'
6
7
  import { join } from 'node:path'
@@ -135,6 +136,7 @@ function normalizeCompactTransport(value) {
135
136
  }
136
137
 
137
138
  function normalizeConfig(input = {}) {
139
+ const cacheRetention = ['none', 'short', 'long'].includes(input.cacheRetention) ? input.cacheRetention : 'short'
138
140
  return {
139
141
  provider: input.provider || 'lcx',
140
142
  baseURL: String(input.baseURL || 'https://api.lcxbot.com/v1').replace(/\/+$/u, ''),
@@ -142,6 +144,8 @@ function normalizeConfig(input = {}) {
142
144
  model: input.model || 'gpt-5.6-sol',
143
145
  compactTransport: normalizeCompactTransport(input.compactTransport),
144
146
  headers: input.headers && typeof input.headers === 'object' ? { ...input.headers } : {},
147
+ cacheRetention,
148
+ supportsLongCacheRetention: input.supportsLongCacheRetention !== false,
145
149
  checkpointPath: input.checkpointPath || defaultCheckpointPath(),
146
150
  portableCheckpointPath: input.portableCheckpointPath || portableCheckpointPath(input.checkpointPath || defaultCheckpointPath()),
147
151
  alphaCapabilityPath: input.alphaCapabilityPath || defaultAlphaCapabilityPath(),
@@ -186,6 +190,8 @@ function resolveResponsesRouteConfig(ctx, options, fallbackConfig) {
186
190
  baseURL,
187
191
  apiKeyEnv,
188
192
  headers: profile.headers ?? fallbackConfig.headers,
193
+ cacheRetention: profile.cacheRetention ?? fallbackConfig.cacheRetention,
194
+ supportsLongCacheRetention: profile.compat?.supportsLongCacheRetention ?? fallbackConfig.supportsLongCacheRetention,
189
195
  model: options.model,
190
196
  compactTransport: profile.compactTransport ?? fallbackConfig.compactTransport,
191
197
  timeoutMs: profile.timeoutMs ?? fallbackConfig.timeoutMs,
@@ -301,7 +307,7 @@ async function authenticatedHeaders(ctx, config, sessionId, clientRequestId) {
301
307
  ...config.headers,
302
308
  ...attributionHeaders(),
303
309
  authorization: `Bearer ${await resolveApiKey(ctx, config)}`,
304
- 'x-client-request-id': clientRequestId ?? randomUUID(),
310
+ ...(clientRequestId === null ? {} : { 'x-client-request-id': clientRequestId ?? (sessionId ? String(sessionId) : randomUUID()) }),
305
311
  ...(sessionId ? { 'session-id': String(sessionId) } : {}),
306
312
  }
307
313
  }
@@ -361,19 +367,35 @@ function routeWithSessionAncestry(ctx, route) {
361
367
  return { ...route, ancestorSessionIds: sessionAncestry(ctx, route.sessionId) }
362
368
  }
363
369
 
364
- function promptCacheKey(route) {
365
- return route?.sessionId ? routeFingerprint(route) : undefined
370
+ function promptCacheSessionId(route, config) {
371
+ if (config?.cacheRetention === 'none') return undefined
372
+ return route?.sessionId ? String(route.sessionId) : undefined
373
+ }
374
+
375
+ function promptCacheKey(route, config) {
376
+ const sessionId = promptCacheSessionId(route, config)
377
+ return sessionId ? clampOpenAIPromptCacheKey(sessionId) : undefined
378
+ }
379
+
380
+ function promptCacheRetention(config) {
381
+ return config?.cacheRetention === 'long' && config.supportsLongCacheRetention !== false ? '24h' : undefined
382
+ }
383
+
384
+ function promptCacheHeaders(ctx, config, route) {
385
+ const sessionId = promptCacheSessionId(route, config)
386
+ return authenticatedHeaders(ctx, config, sessionId, sessionId === undefined ? null : undefined)
366
387
  }
367
388
 
368
389
  async function requestRemoteCompaction(options, input, config, signal, ctx, route, preflightHeaders) {
369
390
  try {
370
- const headers = preflightHeaders ?? await authenticatedHeaders(ctx, config, options.sessionId)
391
+ const headers = preflightHeaders ?? await promptCacheHeaders(ctx, config, route)
371
392
  return await requestNativeCompaction({
372
393
  baseURL: config.baseURL,
373
394
  model: options.model,
374
395
  input,
375
396
  instructions: options.system,
376
- promptCacheKey: promptCacheKey(route),
397
+ promptCacheKey: promptCacheKey(route, config),
398
+ promptCacheRetention: promptCacheRetention(config),
377
399
  idempotencyKey: randomUUID(),
378
400
  tools: options.tools,
379
401
  headers,
@@ -731,7 +753,7 @@ function checkpointReplayRecord(messages, store) {
731
753
  return { marker, record }
732
754
  }
733
755
 
734
- function nativeReplayBody(options, route, nativeOutput, tailInput) {
756
+ function nativeReplayBody(options, route, config, nativeOutput, tailInput) {
735
757
  return {
736
758
  model: options.model,
737
759
  input: [...structuredClone(nativeOutput), ...structuredClone(tailInput)],
@@ -739,7 +761,8 @@ function nativeReplayBody(options, route, nativeOutput, tailInput) {
739
761
  store: false,
740
762
  ...(options.tools !== undefined ? { tools: responsesTools(options.tools) } : {}),
741
763
  ...(options.system !== undefined ? { instructions: options.system } : {}),
742
- ...(promptCacheKey(route) ? { prompt_cache_key: promptCacheKey(route) } : {}),
764
+ ...(promptCacheKey(route, config) ? { prompt_cache_key: promptCacheKey(route, config) } : {}),
765
+ ...(promptCacheRetention(config) ? { prompt_cache_retention: promptCacheRetention(config) } : {}),
743
766
  }
744
767
  }
745
768
 
@@ -762,8 +785,8 @@ async function* nativeCheckpointReplayStream(options, config, portableStore, ctx
762
785
  try {
763
786
  const response = await fetchSse(
764
787
  `${config.baseURL}/responses`,
765
- nativeReplayBody(options, route, nativeOutput, tailInput),
766
- mergeFeatureHeader(await authenticatedHeaders(ctx, config, options.sessionId)),
788
+ nativeReplayBody(options, route, config, nativeOutput, tailInput),
789
+ mergeFeatureHeader(await promptCacheHeaders(ctx, config, route)),
767
790
  replaySignals.signal,
768
791
  config.timeoutMs + 1000,
769
792
  { maxResponseBytes: config.maxResponseBytes },
@@ -822,7 +845,7 @@ async function prepareRemoteCompaction(options, config, portableStore, ctx, sess
822
845
  const route = currentRoute(options, config)
823
846
  const lease = sessionTracker?.capture(route.sessionId)
824
847
  lease?.assert('request-start')
825
- const headers = await authenticatedHeaders(ctx, config, options.sessionId)
848
+ const headers = await promptCacheHeaders(ctx, config, route)
826
849
  responsesTools(options.tools)
827
850
  const llm = ctx?.llm ?? ctx?.get?.('llm')
828
851
  const imageSupport = typeof llm?.resolveModelInfo === 'function'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-lcx-codex",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "DSH web search and Responses native V2 compaction for Sub2API-proxied or NewAPI-relayed GPT models",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -62,7 +62,6 @@
62
62
  "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
63
63
  "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
64
64
  "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
65
- "@deepseek-ai/dsh-compaction-basic": "^0.1.0-rc.8",
66
65
  "@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
67
66
  "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
68
67
  "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",