@mars-sea/dsh-commandcode-provider 0.12.9 → 0.12.10

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,27 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.12.10] - 2026-10-10
8
+
9
+ ### Changed
10
+
11
+ - **引擎兼容范围增加 `0.2.1-alpha.2`,业务代码零改动。** 15 个 `@deepseek-ai/dsh-*` peer 的析取由 `0.2.0-rc.2 || 0.2.1-alpha.1` 扩为 `0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2`,`dsh.compatibility.dsh`、`engines.dsh`、`devDependencies`、`package-lock.json` 与 `dsh.compatibility.dshReleases` 同批同步;既有两个分支保留,`@deepseek-ai/cordis`(`~4.0.4 || ~4.0.5-alpha.1`)与 `@deepseek-ai/schemastery`(`~3.18.4 || ~3.18.5-alpha.1`)不用改——alpha.2 与 alpha.1 同批发布的正是这两个版本。逐文件对比 alpha.1 → alpha.2 的公开源码后确认无需改代码:`RequestMessage`/`GenerateOptions`/`ContentBlock`/`LlmError` 结构未变,Agent 事件、设置、凭据、附件与 Typert 严格描述符未变,五个客户端插槽的声明与 Web 预置模块表也未变。
12
+ - alpha.2 有三处宿主内部行为变化,插件按现有接口继续工作,边界已记入参考文档:模型调用准备新增可选回调 `prepareCall(config, signal?, configure?)`(普通适配器不必调用)、请求准备完成后会刷新运行时上下文、代理卸载改为先停流再排空事件。上游 `idleWatchdog` 那处「挂起读取也能按期结束」的修复**不覆盖**插件自己的读流实现,因此本轮按同一目标单独补齐(见下方 Fixed)。
13
+ - `npm run test:install` 改为**逐引擎矩阵**:每个已声明兼容版本各建一个全新的 pnpm 10.34.5 generation,安装时把该版本的必需 Harness peer 一起钉死,再断言锁文件解析出的 `@deepseek-ai/dsh-llm` 恰好是目标版本。此前单次安装只会命中范围里最新的那个分支,全绿并不能证明另外两个引擎装得上。
14
+
15
+ ### Fixed
16
+
17
+ - **读取卡住的响应不再拖住收束。** 读循环每次读取改用一个「唤醒闸门」:空闲期限到期与调用者取消都先解开闸门,再尽力取消底层读取。此前只有 `reader.cancel()` 能把挂起的 `read()` 唤醒,遇到被包装或被垫片替换、既不结算读取也不响应取消的响应体时,空闲期限、调用者取消和消费方 `return()` 三条路径会一起卡死——期限到了不报错,取消也不返回。现在闸门自己结束等待:调用者取消原样传播(归类 `aborted`),否则以既有 `TIMEOUT` 失败;被放弃的读取结果丢弃,其迟到拒绝就地吞掉(真实读取器在释放锁时会以 `Invalid state: Releasing reader` 拒绝挂起的读取),读取锁仍只释放一次。`tests/adapter.test.ts` 新增三个用例固定这三种收束。
18
+
19
+ ### 已知边界
20
+
21
+ - 真实引擎检查通过的是发布入口加载、静态具名导入、客户端 `require()` 种子、易变设置契约与 22 个流终态场景;浏览器实际渲染、真实账号请求、卸载期间最终事件排空与长期运行仍需实际环境验收。
22
+
23
+ ### 刻意未采纳
24
+
25
+ - `systemPromptUpdate: 'in-history'`(历史内系统提示替换)与原生工具延迟激活语义:插件继续用前导系统提示与 `addition-only`(仅新增)工具声明,每次请求发送完整活跃工具列表。能力标记与实际 wire 行为必须一致,未实现前不宣告。
26
+ - 新版 `prepareCall` 的可选准备回调:它是给需要注入辅助调用元数据的场景用的,本适配器不使用第三参数即可保持既有行为。
27
+
7
28
  ## [0.12.9] - 2026-10-09
8
29
 
9
30
  ### Changed
package/README.md CHANGED
@@ -35,11 +35,13 @@ See [Screenshots](#screenshots) below for what the UI looks like.
35
35
 
36
36
  ## Install
37
37
 
38
- This release supports **dsh 0.2.0-rc.2 and dsh 0.2.1-alpha.1, and nothing else** —
39
- the plugin's peer range is exactly those two verified versions, and its
38
+ This release supports **dsh 0.2.0-rc.2, dsh 0.2.1-alpha.1 and dsh 0.2.1-alpha.2, and nothing else** —
39
+ the plugin's peer range is exactly those three verified versions, and its
40
40
  compatibility records name them alone. `0.2.0-rc.2` is what
41
41
  `npm i -g @deepseek-ai/dsh@latest` and the official desktop build ship, so it is
42
- the version most users are on; `0.2.1-alpha.1` is the upstream `alpha` channel:
42
+ the version most users are on; `0.2.1-alpha.2` is the upstream `alpha` channel
43
+ today, and `0.2.1-alpha.1` stays in the range because it was already verified, so
44
+ a profile pinned to it keeps working:
43
45
 
44
46
  ```sh
45
47
  dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
@@ -66,14 +68,14 @@ dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.15
66
68
 
67
69
  The same applies to every profile you install into, including the terminal UI below.
68
70
 
69
- Fresh pnpm 10 marketplace generations are supported directly. Every Harness package — including `@deepseek-ai/cordis` and `@deepseek-ai/schemastery` — is declared by the plugin over the same two-version range as its `@deepseek-ai/dsh-*` peers as a Host peer, so the active dsh profile remains their owner; do not add a separate dependency for any of them. Upstream removed the `@deepseek-ai/dsh-invariants` package in `0.2.1-alpha.1`, so the plugin no longer declares it.
71
+ Fresh pnpm 10 marketplace generations are supported directly. Every Harness package is declared by the plugin as a Host peer, so the active dsh profile remains their owner; do not add a separate dependency for any of them. The `@deepseek-ai/dsh-*` packages share one exact-version disjunction; `@deepseek-ai/cordis` and `@deepseek-ai/schemastery` carry the paired ranges `~4.0.4 || ~4.0.5-alpha.1` and `~3.18.4 || ~3.18.5-alpha.1` instead, because publishing an engine rewrites both to the version that shipped alongside it. Upstream removed the `@deepseek-ai/dsh-invariants` package in `0.2.1-alpha.1`, so the plugin no longer declares it.
70
72
 
71
73
  ## Updating
72
74
 
73
75
  Update with the same tag you installed with:
74
76
 
75
77
  ```sh
76
- dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2 or 0.2.1-alpha.1
78
+ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2, 0.2.1-alpha.1 or 0.2.1-alpha.2
77
79
  dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@0.9.1 # older dsh (0.5.0 line, unmaintained)
78
80
  ```
79
81
 
@@ -147,7 +149,7 @@ cmd login # writes ~/.commandcode/auth.json
147
149
  cwd: !!js process.cwd()
148
150
  ```
149
151
 
150
- **Engine version.** The plugin is maintained against exactly two engines: **dsh 0.2.0-rc.2** and **dsh 0.2.1-alpha.1**. Its `@deepseek-ai/dsh-*` peers use the disjunction `0.2.0-rc.2 || 0.2.1-alpha.1`, `@deepseek-ai/cordis` is pinned to `~4.0.4 || ~4.0.5-alpha.1` and `@deepseek-ai/schemastery` to `~3.18.4 || ~3.18.5-alpha.1` (each branch shipped with its engine), and `dsh.compatibility.dshReleases` records exactly those two releases. That disjunction is deliberate and load-bearing: unlike a caret or a `>=` range it admits **no** adjacent prerelease, release candidate, or stable patch version, every branch is a bare version, and `npm run test:engine` runs the whole suite against **both** engines before a release. DSH itself refuses to load a plugin whose `@deepseek-ai/dsh*` peers do not admit the running engine, so a range that silently admitted an unverified neighbour would be worse than no range at all. On any other engine the settings page, the message envelope, or the request-image budget may not line up — install the last release that supported your engine (see [Install](#install)) instead of forcing this one.
152
+ **Engine version.** The plugin is maintained against exactly three engines: **dsh 0.2.0-rc.2**, **dsh 0.2.1-alpha.1** and **dsh 0.2.1-alpha.2**. Its `@deepseek-ai/dsh-*` peers use the disjunction `0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2`, `@deepseek-ai/cordis` is pinned to `~4.0.4 || ~4.0.5-alpha.1` and `@deepseek-ai/schemastery` to `~3.18.4 || ~3.18.5-alpha.1` (each branch shipped with its engine), and `dsh.compatibility.dshReleases` records exactly those three releases. That disjunction is deliberate and load-bearing: unlike a caret or a `>=` range it admits **no** adjacent prerelease, release candidate, or stable patch version, every branch is a bare version, and `npm run test:engine` runs the whole suite against **all three** engines before a release. DSH itself refuses to load a plugin whose `@deepseek-ai/dsh*` peers do not admit the running engine, so a range that silently admitted an unverified neighbour would be worse than no range at all. On any other engine the settings page, the message envelope, or the request-image budget may not line up — install the last release that supported your engine (see [Install](#install)) instead of forcing this one.
151
153
 
152
154
  ## Usage dashboard
153
155
 
package/README.zh-CN.md CHANGED
@@ -33,7 +33,7 @@
33
33
 
34
34
  ## 安装
35
35
 
36
- 本版本**只支持 dsh 0.2.0-rc.2 与 dsh 0.2.1-alpha.1 这两个版本**:插件的 peer 范围就是这两个已验证版本的析取,兼容性记录里也只列它们。`0.2.0-rc.2` 正是 `npm i -g @deepseek-ai/dsh@latest` 与官方桌面端放出的版本,多数用户都在它上面;`0.2.1-alpha.1` 是上游的 `alpha` 通道:
36
+ 本版本**只支持 dsh 0.2.0-rc.2、dsh 0.2.1-alpha.1 与 dsh 0.2.1-alpha.2 这三个版本**:插件的 peer 范围就是这三个已验证版本的析取,兼容性记录里也只列它们。`0.2.0-rc.2` 正是 `npm i -g @deepseek-ai/dsh@latest` 与官方桌面端放出的版本,多数用户都在它上面;`0.2.1-alpha.2` 是上游 `alpha` 通道当前提供的版本,`0.2.1-alpha.1` 因为已经验证过而保留在范围内,钉在它上面的 profile 可以继续使用:
37
37
 
38
38
  ```sh
39
39
  dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
@@ -55,14 +55,14 @@ dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.15
55
55
 
56
56
  这一点对每个 profile 都成立,包括下面的终端界面。
57
57
 
58
- 插件可直接在 pnpm 10 的全新插件市场 generation 中安装。所有 Harness 包(含 `@deepseek-ai/cordis`、`@deepseek-ai/schemastery`)都由插件按与 `@deepseek-ai/dsh-*` 相同的双版本范围声明为 Host peer,由当前 dsh profile 统一管理,不要另行添加 dependency。上游曾在 `0.2.1-alpha.1` 删除 `@deepseek-ai/dsh-invariants` 这个包,因此插件不再声明它。
58
+ 插件可直接在 pnpm 10 的全新插件市场 generation 中安装。所有 Harness 包都由插件声明为 Host peer,由当前 dsh profile 统一管理,不要另行添加 dependency。其中 `@deepseek-ai/dsh-*` 共用同一个精确版本析取;`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 则分别写成配对范围 `~4.0.4 || ~4.0.5-alpha.1` 与 `~3.18.4 || ~3.18.5-alpha.1`——上游每次发引擎都会把这两个包重写成随引擎发布的版本。上游曾在 `0.2.1-alpha.1` 删除 `@deepseek-ai/dsh-invariants` 这个包,因此插件不再声明它。
59
59
 
60
60
  ## 更新
61
61
 
62
62
  用与安装时相同的 tag 更新:
63
63
 
64
64
  ```sh
65
- dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2 或 0.2.1-alpha.1
65
+ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2、0.2.1-alpha.1 或 0.2.1-alpha.2
66
66
  dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@0.9.1 # 更早的 dsh(0.5.0 线,不再维护)
67
67
  ```
68
68
 
@@ -136,7 +136,7 @@ cmd login # 写入 ~/.commandcode/auth.json
136
136
  cwd: !!js process.cwd()
137
137
  ```
138
138
 
139
- **引擎版本要求。** 插件针对两个引擎维护:**dsh 0.2.0-rc.2** 与 **dsh 0.2.1-alpha.1**。它的 `@deepseek-ai/dsh-*` 对等依赖写成两者的析取 `0.2.0-rc.2 || 0.2.1-alpha.1`,`@deepseek-ai/cordis` 固定为 `~4.0.4 || ~4.0.5-alpha.1`、`@deepseek-ai/schemastery` 固定为 `~3.18.4 || ~3.18.5-alpha.1`(每个分支都与对应引擎同批发布),`dsh.compatibility.dshReleases` 也只记录这两个版本。这个析取写法是刻意的、且是承重的:它不像 `^` 或 `>=` 那样放行任何相邻的预发布版、候选发布或稳定补丁版,每个分支都是一个裸版本号,且发版前 `npm run test:engine` 会对**两个引擎各跑一遍**完整套件。DSH 本身会拒绝加载 `@deepseek-ai/dsh*` peer 不接纳当前引擎的插件——所以一个静默放行未验证邻居的范围,比没有范围更糟。在其它引擎上,设置页、消息封装或请求图片预算总有一处对不上——请改装支持你所用引擎的最后一个版本,而不是硬装这一个。
139
+ **引擎版本要求。** 插件针对三个引擎维护:**dsh 0.2.0-rc.2**、**dsh 0.2.1-alpha.1** 与 **dsh 0.2.1-alpha.2**。它的 `@deepseek-ai/dsh-*` 对等依赖写成三者的析取 `0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2`,`@deepseek-ai/cordis` 固定为 `~4.0.4 || ~4.0.5-alpha.1`、`@deepseek-ai/schemastery` 固定为 `~3.18.4 || ~3.18.5-alpha.1`(每个分支都与对应引擎同批发布),`dsh.compatibility.dshReleases` 也只记录这三个版本。这个析取写法是刻意的、且是承重的:它不像 `^` 或 `>=` 那样放行任何相邻的预发布版、候选发布或稳定补丁版,每个分支都是一个裸版本号,且发版前 `npm run test:engine` 会对**三个引擎各跑一遍**完整套件。DSH 本身会拒绝加载 `@deepseek-ai/dsh*` peer 不接纳当前引擎的插件——所以一个静默放行未验证邻居的范围,比没有范围更糟。在其它引擎上,设置页、消息封装或请求图片预算总有一处对不上——请改装支持你所用引擎的最后一个版本,而不是硬装这一个。
140
140
 
141
141
  ## 用量面板
142
142
 
package/lib/client.js CHANGED
@@ -3273,7 +3273,7 @@ window.__ModuleLoader__.load({
3273
3273
  }
3274
3274
  //#endregion
3275
3275
  //#region package.json
3276
- var version = "0.12.9";
3276
+ var version = "0.12.10";
3277
3277
  var repository = {
3278
3278
  "type": "git",
3279
3279
  "url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
package/lib/index.js CHANGED
@@ -3970,6 +3970,11 @@ function openStreamTrace(scope, options = {}) {
3970
3970
  /** 仅供本次消费生命周期使用,不作为服务商错误或用户取消向外传播。 */
3971
3971
  var ConsumerStreamClosed = class extends Error {};
3972
3972
  /**
3973
+ * 读循环唤醒闸门的标记值:底层 read() 既不结算、取消也叫不醒时,由闸门结束这次等待。
3974
+ * 它不是错误,只在读循环内部出现,用来把「读取完成」与「读取卡住」区分开。
3975
+ */
3976
+ const READ_STALLED = Symbol("readStalled");
3977
+ /**
3973
3978
  * 异步生成器的 return 会排在正在等待的 next 后面,因此必须先通过独立信号
3974
3979
  * 唤醒当前读取,再让生成器执行 finally。消费方已结束时不再交付用量或错误。
3975
3980
  */
@@ -4031,7 +4036,12 @@ async function* readResponse(response, context) {
4031
4036
  reader.cancel().catch(() => void 0);
4032
4037
  } catch {}
4033
4038
  };
4034
- const onAbort = () => cancelReader();
4039
+ let openGate;
4040
+ const wakeRead = () => openGate?.();
4041
+ const onAbort = () => {
4042
+ wakeRead();
4043
+ cancelReader();
4044
+ };
4035
4045
  const decoder = new TextDecoder();
4036
4046
  let buffer = "";
4037
4047
  const streamStartedAt = Date.now();
@@ -4044,6 +4054,7 @@ async function* readResponse(response, context) {
4044
4054
  if (idleTimer !== void 0) clearTimeout(idleTimer);
4045
4055
  idleTimer = setTimeout(() => {
4046
4056
  idleFired = true;
4057
+ wakeRead();
4047
4058
  cancelReader();
4048
4059
  }, streamIdleTimeoutMs);
4049
4060
  };
@@ -4053,12 +4064,24 @@ async function* readResponse(response, context) {
4053
4064
  idleTimer = void 0;
4054
4065
  }
4055
4066
  };
4067
+ /** 空闲期限结束读循环时的唯一错误文本;读取结算与读取卡住两条路径共用。 */
4068
+ const idleTimeoutError = () => bilingual("TIMEOUT", `Command Code API stream from ${apiBase} was idle for ${streamIdleTimeoutMs}ms (no events) and was treated as a dead connection`, `Command Code API 流式响应已 ${streamIdleTimeoutMs} 毫秒无任何事件,被判定为死连接——长思考模型可在设置中调大流空闲超时`);
4056
4069
  const asm = createBlockAssembler();
4057
4070
  let finish;
4058
4071
  let usage;
4059
4072
  let usageEmitted = false;
4060
4073
  let endRecorded = false;
4061
4074
  let protocolStopped = false;
4075
+ /** 读循环收束(读到 EOF 或读取卡住)共用的诊断事实,两处记录字段保持一致。 */
4076
+ const eofFacts = () => ({
4077
+ chunks: chunkCount,
4078
+ bytes: totalBytes,
4079
+ silentMs: Date.now() - lastChunkAt,
4080
+ totalMs: Date.now() - streamStartedAt,
4081
+ finishSeen: finish !== void 0,
4082
+ idleFired,
4083
+ buffered: buffer.length
4084
+ });
4062
4085
  function* handle(event) {
4063
4086
  captureResponseIdentifiers(responseMetadata, event);
4064
4087
  if (isRecord(event) && (protocol === "messages" && event.type === "message_stop" || protocol === "cli" && event.type === "finish")) protocolStopped = true;
@@ -4084,9 +4107,14 @@ async function* readResponse(response, context) {
4084
4107
  for (;;) {
4085
4108
  signal?.throwIfAborted();
4086
4109
  let read;
4110
+ const gate = new Promise((resolve) => {
4111
+ openGate = () => resolve(READ_STALLED);
4112
+ });
4087
4113
  armIdle();
4088
4114
  try {
4089
- read = await reader.read();
4115
+ const pending = reader.read();
4116
+ pending.catch(() => void 0);
4117
+ read = await Promise.race([pending, gate]);
4090
4118
  } catch (error) {
4091
4119
  signal?.throwIfAborted();
4092
4120
  trace.record("read-error", {
@@ -4098,20 +4126,21 @@ async function* readResponse(response, context) {
4098
4126
  throw bilingual("TRANSPORT", `Command Code API stream from ${apiBase} failed while reading: ${errorChain(error)}`, "Command Code API 流式响应中途断开——网络波动所致,重试通常可恢复", { cause: error });
4099
4127
  } finally {
4100
4128
  clearIdle();
4129
+ openGate = void 0;
4130
+ }
4131
+ if (read === READ_STALLED) {
4132
+ trace.record("eof", {
4133
+ ...eofFacts(),
4134
+ readStalled: true
4135
+ });
4136
+ signal?.throwIfAborted();
4137
+ throw idleTimeoutError();
4101
4138
  }
4102
4139
  signal?.throwIfAborted();
4103
4140
  const { done, value } = read;
4104
4141
  if (done) {
4105
- trace.record("eof", {
4106
- chunks: chunkCount,
4107
- bytes: totalBytes,
4108
- silentMs: Date.now() - lastChunkAt,
4109
- totalMs: Date.now() - streamStartedAt,
4110
- finishSeen: finish !== void 0,
4111
- idleFired,
4112
- buffered: buffer.length
4113
- });
4114
- if (idleFired) throw bilingual("TIMEOUT", `Command Code API stream from ${apiBase} was idle for ${streamIdleTimeoutMs}ms (no events) and was treated as a dead connection`, `Command Code API 流式响应已 ${streamIdleTimeoutMs} 毫秒无任何事件,被判定为死连接——长思考模型可在设置中调大流空闲超时`);
4142
+ trace.record("eof", eofFacts());
4143
+ if (idleFired) throw idleTimeoutError();
4115
4144
  if (buffer.trim()) yield* handleLine(buffer);
4116
4145
  break;
4117
4146
  }
@@ -9922,8 +9951,8 @@ function transportBudgetMessage(failureMessage, maxRetries) {
9922
9951
  /**
9923
9952
  * Mark one schema field volatile.
9924
9953
  *
9925
- * `.volatile()` is unconditional: it is a schemastery 3.18.3 feature and the
9926
- * only supported engine (`dsh 0.2.1-alpha.1`) pins `~3.18.5-alpha.1`, so the
9954
+ * `.volatile()` is unconditional: it is a schemastery 3.18.3 feature and every
9955
+ * supported engine ships `~3.18.4` or `~3.18.5-alpha.1`, so the
9927
9956
  * method is always present. It is reached through a structural member so the caller's
9928
9957
  * declared field type survives — the mark changes what the schema PARSES to (a
9929
9958
  * live reference), not the plugin-facing `Config` shape.