dsh-openwrite 0.2.25 → 0.2.26

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.md CHANGED
@@ -10,7 +10,7 @@
10
10
  <p align="center">在 DeepSeek Harness 中规划、写作、审稿和管理长篇小说。</p>
11
11
 
12
12
  <p align="center">
13
- <a href="package.json"><img src="https://img.shields.io/badge/dsh-0.2.1--rc.2-2563eb" alt="dsh 0.2.1-rc.2"></a>
13
+ <a href="package.json"><img src="https://img.shields.io/badge/dsh-0.2.1--rc.3-2563eb" alt="dsh 0.2.1-rc.3"></a>
14
14
  <a href="package.json"><img src="https://img.shields.io/badge/Node-%E2%89%A522.19-15803d" alt="Node >= 22.19"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-0f766e" alt="Apache-2.0"></a>
16
16
  <a href="https://dsh-plugin.org/plugins/lipu-jpg/openwrite"><img src="https://dsh-plugin.org/badges/listed.svg" alt="Listed on dsh-plugin.org"></a>
@@ -27,14 +27,21 @@
27
27
  这是 [OpenWrite](https://github.com/LiPu-jpg/Openwrite/tree/native-core) 的 dsh 插件版本。
28
28
  [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 提供 Agent 与交互界面,OpenWrite Core 提供小说领域能力;人物、设定、大纲、正文和评审保存在本地作品目录。
29
29
 
30
+ ## 0.2.26 更新
31
+
32
+ 本版捆绑 Core **5.8.14**,包含旧会话恢复、精确 DSH `0.2.1-rc.3` 兼容、
33
+ Android/ARM 外部 Studio 诊断,以及连载写作可靠性、完整上下文预览和 Embedding 配置修复。
34
+ 详见 [#105–#113 修复说明](docs/ISSUES_105_113.md)、[连载报告修复](docs/ISSUE_109_REPAIRS.md)
35
+ 及 [OpenCode Go 配置及适配](docs/OPENCODE_GO.md)。
36
+
30
37
  ## 快速安装
31
38
 
32
- **0.2.24 标准插件包**兼容精确 dsh 版本 **0.1.2-rc.1 / 0.1.5-alpha.1 / 0.2.0-rc.2 / 0.2.1-rc.2**。四个版本均已完成 Web 工作台渲染与 DoG 数据读取验证,Windows Electron 桌面端启动/恢复链路尚未实机复测。可下载版本、各平台验收结果和 SHA-256 以 [GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/latest) 附件为准;验收方法见 [发布验收](docs/RELEASE_ACCEPTANCE.md)。商城所需的精确预览版兼容性、验证范围和安装限制见 [DSH STORE 接入](docs/DSH_STORE.md)。
39
+ **0.2.26 标准插件包**兼容精确 dsh 版本 **0.1.2-rc.1 / 0.1.5-alpha.1 / 0.2.0-rc.2 / 0.2.1-rc.2 / 0.2.1-rc.3**。五个版本均已完成 Web 工作台渲染与 DoG 数据读取验证,Windows Electron 桌面端启动/恢复链路尚未实机复测。可下载版本、各平台验收结果和 SHA-256 以 [GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/latest) 附件为准;验收方法见 [发布验收](docs/RELEASE_ACCEPTANCE.md)。商城所需的精确预览版兼容性、验证范围和安装限制见 [DSH STORE 接入](docs/DSH_STORE.md)。
33
40
 
34
- npm `dsh-openwrite@0.2.24` 与对应 GitHub Release 均已发布,`latest` 已更新到 0.2.24,使用同一份验收产物。也可安装精确版本的 Release 包:
41
+ 正式发布状态以 [npm](https://www.npmjs.com/package/dsh-openwrite) 的 `latest` 与对应 GitHub Release 为准;两处使用同一份验收产物。也可安装精确版本的 Release 包:
35
42
 
36
43
  ```sh
37
- dsh plugin --profile web add -w https://github.com/LiPu-jpg/Openwrite/releases/download/v0.2.24/dsh-openwrite-0.2.24.tgz
44
+ dsh plugin --profile web add -w https://github.com/LiPu-jpg/Openwrite/releases/download/v0.2.26/dsh-openwrite-0.2.26.tgz
38
45
  dsh web
39
46
  ```
40
47
 
@@ -64,7 +71,9 @@ dsh web
64
71
 
65
72
  ### dsh 0.2 桌面端升级
66
73
 
67
- `0.2.25`(Core 5.8.12)为本分支的未发布候选,新增连载体检、分段续写、共享术语/范本、章组审计和漂移裁决,并修复验收与任务可观测性。范围、离线升级工具及宿主侧限制见 [#98–#100 实现说明](docs/ISSUES_98_100.md)。不要把候选版本当作已上架版本。
74
+ `0.2.26`(Core 5.8.14)包含 #105–#114 修复,以及 #103/#104 的 Embedding 维度和配置改进。升级前完全退出 dsh/桌面客户端;安装后重新启动并刷新浏览器。原有作品、凭据和会话继续保留,历史版本预设用于恢复旧会话。受限平台使用外部 Studio,OpenCode Go 的服务商使用范围和配置要求见上述文档。
75
+
76
+ `0.2.25`(Core 5.8.12)已在 [GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/tag/v0.2.25) 与 [npm](https://www.npmjs.com/package/dsh-openwrite/v/0.2.25) 发布,新增连载体检、分段续写、共享术语/范本、章组审计和漂移裁决,并修复验收与任务可观测性。范围、离线升级工具及宿主侧限制见 [#98–#100 实现说明](docs/ISSUES_98_100.md),完整验收见 [发布验收](docs/RELEASE_ACCEPTANCE.md#025)。
68
77
 
69
78
  `0.2.24`(Core 5.8.11)修复 [#88–#95](docs/ISSUES_88_95.md):删章验收账本、结算降级、长文本流式生成、后台写章进度、统一字数门限、草稿发现与恢复及诊断/路由语义。[GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/tag/v0.2.24) 与 [npm](https://www.npmjs.com/package/dsh-openwrite/v/0.2.24) 均已发布,完整验收见 [发布验收](docs/RELEASE_ACCEPTANCE.md#024)。升级后停止并重新启动 dsh,再刷新浏览器页面。
70
79
 
package/docs/DSH_STORE.md CHANGED
@@ -15,6 +15,12 @@ OpenWrite 是一个完整的小说创作插件套件。商城安装目标是仓
15
15
 
16
16
  源码验收必须固定到完整 Commit;`prepare` 会自包含构建三个内部插件,包含 Core wheel、编辑器静态资源、预设和许可证,不依赖相邻源码。构建授权仍遵循用户的 pnpm 设置。普通用户优先使用 npm 预编译包:`dsh plugin --profile web add -w dsh-openwrite@latest`。latest 只在执行安装或升级时解析,运行中不自动更新。市场的版本、兼容和验收字段应记录实际核验的精确版本;不能把已有验收结果自动套用到未来版本。精确 npm 版本及对应 GitHub Release `.tgz` 继续用于复现与回退,见 README。
17
17
 
18
+ ## 0.2.26
19
+
20
+ 0.2.26 在根包及全部内嵌子包加入精确 `0.2.1-rc.3` 声明,真实 Web 客户端验收通过。
21
+ 市场请按实际 npm/Release 制品及其验收报告记录版本和兼容信息。
22
+ 验证范围见 [#105–#113 修复说明](ISSUES_105_113.md)。
23
+
18
24
  ## 兼容声明与证据
19
25
 
20
26
  根 `package.json` 的 `dsh.compatibility.dshReleases` 是精确版本声明;没有实际验证的版本保持 `unknown`。Node.js 下限是 22.19.0,完整基础平台矩阵使用 Node 24,Node 22.19/26 作兼容检查;系统与架构限制见安装指南。
@@ -23,7 +29,7 @@ OpenWrite 是一个完整的小说创作插件套件。商城安装目标是仓
23
29
 
24
30
  alpha 浏览器验收进一步发现 persona 配置由 `text` 改成了必填 `prefix`;预设使用 YAML 锚点将同一份身份文本提供给两个字段,并以两版实际 Config / apply 验证身份只注册一次。
25
31
 
26
- 构建基线仍是 `0.1.2-rc.1`。兼容声明覆盖精确版本 `0.1.2-rc.1`、`0.1.5-alpha.1`、`0.2.0-rc.2`、`0.2.1-rc.2`,并分别保留已完成的 Web 客户端验收;`0.1.3-alpha.1`、`0.1.3-alpha.2` 保持未知。这不自动升级用户宿主,也不承诺整个 alpha 或 0.2 范围都兼容。`0.2.23` 已发布到 npm `latest` 与 GitHub Release,完整平台矩阵、源码安装和四份宿主客户端报告见[发布验收](RELEASE_ACCEPTANCE.md#023);本机另复验了 `0.2.1-rc.2` 的安装闸门、客户端激活、预设选择、工作台入口与三页签、DoG 面板及其他插件共存。
32
+ 构建基线仍是 `0.1.2-rc.1`。兼容声明覆盖精确版本 `0.1.2-rc.1`、`0.1.5-alpha.1`、`0.2.0-rc.2`、`0.2.1-rc.2`、`0.2.1-rc.3`,并分别保留已完成的 Web 客户端验收;`0.1.3-alpha.1`、`0.1.3-alpha.2` 保持未知。这不自动升级用户宿主,也不承诺整个 alpha 或 0.2 范围都兼容。`0.2.23` 已发布到 npm `latest` 与 GitHub Release,完整平台矩阵、源码安装和四份宿主客户端报告见[发布验收](RELEASE_ACCEPTANCE.md#023);本机另复验了 `0.2.1-rc.2` 的安装闸门、客户端激活、预设选择、工作台入口与三页签、DoG 面板及其他插件共存。
27
33
 
28
34
  2026-09-09,从官方 npm Registry 直接安装 `@deepseek-ai/dsh@0.1.5-alpha.1` 时,上游范围依赖选中了正在发布的 alpha.2,其中 `@deepseek-ai/dsh-tool-pwsh-persistent@^0.1.5-alpha.2` 当时缺失,安装失败(ETARGET)。兼容测试使用 npm 的 `--before=2026-09-09T00:00:00Z` 解析 alpha.1 发布时的依赖,并保留解析锁。没有修改官方包源码或用 replacements/overrides 冒充官方组件。此证据仅适用于报告中的宿主依赖图,不代表后续浮动依赖安装也通过。
29
35
 
@@ -0,0 +1,40 @@
1
+ # Gitee AI Embedding dimensions 兼容性修复(开发分支)
2
+
3
+ ## 根因
4
+
5
+ 用户在 OpenWrite「模型 → Embedding」中把 Qwen3-Embedding-8B 配置为 4096 维,但原
6
+ `tools/embedding_runtime.py` 调用 `client.embeddings.create()` 时只发送
7
+ `model`、`input` 和 `encoding_format`。界面中的 Dimension 原本只用于本地输出校验,
8
+ **没有传给提供商**。
9
+
10
+ 2026-10-23 使用单独的临时开发凭据验证 Gitee AI `https://ai.gitee.com/v1/embeddings`:
11
+
12
+ | 请求 | HTTP | 返回向量 | 每条实际维度 |
13
+ | --- | --- | --- | --- |
14
+ | 不传 dimensions | 200 | 2 | 1024 |
15
+ | `dimensions: 4096` | 200 | 2 | 4096 |
16
+ | `dimension: 4096`(单数,非有效参数) | 200 | 2 | 1024 |
17
+
18
+ 因此旧 OpenWrite 将正确的 1024 维响应判定为配置 4096 维不匹配,错误码为
19
+ `MODEL_TEST_INVALID_RESPONSE`,前端只能展示抽象提示。**并不是 Gitee AI 不支持 4096 维**。
20
+
21
+ ## 修复
22
+
23
+ - OpenWrite Core 5.8.13(基于上游 5.8.12):发送 `dimensions=settings.dimension`,同时保留
24
+ `encoding_format=float`;只有 400/422 响应明确指出不支持 `dimensions`
25
+ 参数时,才回退到旧格式,回退结果仍必须通过严格的维度校验。
26
+ - Core 连接测试:维度不匹配时,保留 `MODEL_TEST_INVALID_RESPONSE` 的兼容错误码,
27
+ 但向界面返回安全的实际维度与配置维度,以及下一步处理建议。不会向客户端回显 API Key 或上游响应正文。
28
+ - Studio 模型管理:向量参数本地化,说明配置维度如何用于云端请求及改变索引维度的影响;
29
+ Embedding 测试成功后直接展示维度、返回向量数量和延迟;错误码对应排查提示。
30
+ - Core 源码追踪:`kelriclink/Openwrite` 的 `fix/gitee-embedding-dimensions-core`,新 wheel 和
31
+ `release/runtime-manifest.json` 的来源提交/SHA-256 保持一致。
32
+
33
+ ## 验证与使用边界
34
+
35
+ - 真实 Gitee AI OpenAI SDK 调用:`run_embedding_probe()` 返回 2 条 × 4096 维(1153 ms),成功。
36
+ - Core 定向自动化测试:35 项通过,包括维度参数、旧服务商回退、不支持的维度与错误信息。
37
+ - 前端模型及 DTO 测试包括两种 Embedding 适配器下拉选择、本地字段切换及凭据不泄露;请以当前分支的 `npm run check` 结果为准。
38
+ - 临时凭据只用于进程内诊断,未写入源码、文档、测试断言或构建制品。
39
+ - 本分支仅是**开发修复**,不是 npm/Release 正式版;用户现有插件不会自动更新。
40
+ 修改了实际向量维度时,需要根据当前索引的维度/模型重建向量索引,不能混用旧向量。
package/docs/INSTALL.md CHANGED
@@ -97,7 +97,7 @@ dsh web
97
97
  dsh plugin --profile web remove -w dsh-openwrite
98
98
  ```
99
99
 
100
- 正常停止时卸载本插件服务、界面、连接与进程。作品、配置、凭据、会话和缓存默认保留。关闭全部 dsh 进程后,可单独删除 `$DSH_HOME/openwrite/cache/` 清理下载缓存;不要删除整个目录,其中也有配置与管理记录。异常强制退出留下的版本预设可保留排查,重装同版本并正常退出会清理未修改副本。
100
+ 正常停止时卸载本插件服务、界面、连接与进程。作品、配置、凭据、会话和缓存默认保留。关闭全部 dsh 进程后,可单独删除 `$DSH_HOME/openwrite/cache/` 清理下载缓存;不要删除整个目录,其中也有配置与管理记录。开发版从 #105 修复起保留版本预设目录,供旧宿主恢复历史会话。
101
101
 
102
102
  ## 自定义预设和高级配置
103
103
 
@@ -107,6 +107,26 @@ dsh plugin --profile web remove -w dsh-openwrite
107
107
 
108
108
  bridge 的高级 `mode: external` / `baseUrl` 配置继续支持外部 Core,不接管外部进程。默认 `managed` 不需要配置端口。旧显式地址配置仍按外部模式解释。
109
109
 
110
+ ### External Studio
111
+
112
+ Android / Termux(bionic)和未列入 runtime manifest 的 ARM Linux 无托管 Python/ONNX 产物。
113
+ 开发版对此返回 `RUNTIME_PLATFORM_UNSUPPORTED`(503),并提示配置外部 Studio;不再归类为网络故障。
114
+ 这项修复不包含 Android 原生运行时。无需 Studio 的文件工具仍可使用。
115
+
116
+ 在受支持的电脑上运行 Studio,通过私有网络或 SSH 隧道连接,在 dsh 当前 profile 的
117
+ `cordis.patch.yml` 中修改已有 `openwrite-bridge` 行的配置(不要另加同 ID 的行):
118
+
119
+ ```yaml
120
+ config:
121
+ mode: external
122
+ baseUrl: http://127.0.0.1:4567
123
+ # authToken: <外部 Studio 实例凭据;仅在服务端启用认证时填写>
124
+ ```
125
+
126
+ 上面的回环地址可指向 SSH 本地转发端口;需要实例凭据时通过私有配置中的 `authToken`
127
+ 提供,勿贴入 issue。作品目录必须在 Studio 所在机器上存在,Workspace 路径也须与服务端
128
+ 一致,插件不会同步手机与电脑的文件。重启 dsh 后生效;外部进程由作者管理。
129
+
110
130
  ## GitHub 源码安装
111
131
 
112
132
  源码安装使用 dsh 官方 GitHub source 机制:`dsh plugin --profile web add -w github:LiPu-jpg/Openwrite#v0.2.8`。仓库 `prepare` 自行构建三个插件,Core wheel 随固定提交提供,不访问相邻工作区。源码安装需要 Git 和构建依赖;建议普通用户优先使用已验收 Release 包。
@@ -0,0 +1,15 @@
1
+ # 升级恢复与宿主兼容修复(0.2.26)
2
+
3
+ - #105:0.2 宿主的新会话使用稳定 ID `openwrite`。为历史 `openwrite-0-2-1` 至
4
+ `openwrite-0-2-25`、当前版本及现存版本目录注册兼容定义;已有同 ID 自定义定义优先。
5
+ 会话日志与缓存不改写。0.1 所需的版本目录在宿主退出后保留,避免下次恢复悬空引用。
6
+ 历史兼容项会出现在宿主预设列表中,这是当前 registry 无 alias API 的限制。
7
+ - #106 / #107 / #108 / #110 / #112 / #113:根包、bridge、panel、DoG 的精确兼容声明、
8
+ peer 和 lock 元数据同步加入 `0.2.1-rc.3`,保留旧宿主构建基线;发布矩阵加入该版本。
9
+ - #111:不支持的平台返回 `RUNTIME_PLATFORM_UNSUPPORTED`(503),包含平台与下一步;
10
+ 工具与 Web 代理保留此错误码。外部 Studio 支持可选实例凭据,详见 [安装指南](INSTALL.md#external-studio)。
11
+
12
+ 验证:预设生命周期测试覆盖两宿主、旧身份解析、未来版本稳定 ID、自定义定义和原日志不变。
13
+ DSH `0.2.1-rc.3` 隔离 Web 浏览器验收通过:版本闸门、客户端激活、预设选择、作品导航、
14
+ 工作台三页签、实际渲染、DoG 快照与其他插件共存;模型调用 0。
15
+ 未在 Windows Electron 或 Android 真机复测;完整发布状态与制品验收以 [GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/tag/v0.2.26) 和 [发布验收](RELEASE_ACCEPTANCE.md) 为准。
@@ -1,12 +1,12 @@
1
1
  # #98–#100:连载写作与运行可靠性
2
2
 
3
- 本分支为 dsh-openwrite 0.2.25 / Core 5.8.12 候选,尚未发布。Core 代码位于同仓库 native-core 分支;安装包将捆绑对应 wheel。完整数据契约见 Core `docs/SERIAL_WRITING.md`。
3
+ dsh-openwrite 0.2.25 / Core 5.8.12 已在 [GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/tag/v0.2.25) 与 [npm](https://www.npmjs.com/package/dsh-openwrite/v/0.2.25) 发布。Core 代码已合并到同仓库 native-core 分支;安装包捆绑对应 wheel。完整数据契约见 Core `docs/SERIAL_WRITING.md`。
4
4
 
5
5
  ## 覆盖范围
6
6
 
7
7
  | Issue | 实现 |
8
8
  | --- | --- |
9
- | #98.1 插件升级破坏 bundle | 新增停机离线旁路升级工具,复制整个 profile 后安装、校验、切换,保留旧目录和日志;支持修复不完整 OpenWrite bundle 与中断恢复。原生 dsh 安装器和宿主启动降级未修改,见下方限制。 |
9
+ | #98.1 插件升级破坏 bundle | 新增停机离线旁路升级工具,复制整个 profile 后安装、校验、切换,保留旧目录和日志;支持修复不完整 OpenWrite bundle 与中断恢复。原生宿主安装器补丁单独保留在 fork PR,未随插件包发布,见下方限制。 |
10
10
  | #98.2 外部验收失败 | `no_model=true` / `mode=fast` 承认正文版本,派生事实明确 stale,可稍后完整对账。 |
11
11
  | #98.3 误连实例 | status/响应头标识 root、instance、port;Bridge 校验实际根,不一致返回 409。 |
12
12
  | #98.4、#99.1 长章 | 按 beats 分段,携带 1,500 字尾文续写,拼接去重;不足时续写,预算耗尽保留草稿。 |
@@ -25,13 +25,13 @@
25
25
 
26
26
  先完整退出 DeepSeek Harness / DSH Desktop 并停止 dsh、OpenWrite managed runtime。整个升级过程中保持停机。工具会在安装前及切换前检查进程;未知进程状态也拒绝修改。宿主原生命令不受此检查保护。
27
27
 
28
- 从解压的候选/发行包运行(或已安装版本提供的 `openwrite-safe-upgrade`):
28
+ 从解压的发行包运行(或已安装版本提供的 `openwrite-safe-upgrade`):
29
29
 
30
30
  ```sh
31
31
  node /path/to/package/scripts/safe-upgrade.mjs --profile desktop --package /absolute/path/dsh-openwrite-0.2.25.tgz
32
32
  ```
33
33
 
34
- 候选版本未上架前不要使用 `dsh-openwrite@0.2.25` 下载命令。`DSH_HOME` 必须指向实际宿主的 home;默认 `~/.dsh`。Windows 另设 `DSH_BIN` 为已验收宿主运行时的绝对 `lib/bin.js` 路径,使用 Node 直接执行而非 `.cmd` shell。其余平台可通过 DSH_BIN 指定同一受信任 CLI。
34
+ 可用 `npm pack dsh-openwrite@0.2.25` 下载官方包,或从对应 GitHub Release 获取相同字节的附件。`DSH_HOME` 必须指向实际宿主的 home;默认 `~/.dsh`。Windows 另设 `DSH_BIN` 为已验收宿主运行时的绝对 `lib/bin.js` 路径,使用 Node 直接执行而非 `.cmd` shell。其余平台可通过 DSH_BIN 指定同一受信任 CLI。
35
35
 
36
36
  安装在旁路 profile 中进行;整个旧 profile(包括其他插件、锁文件及设置)在切换前保持可用。只有 OpenWrite 的必要文件和 manifest 校验成功才进入目录切换。切换为两次同盘 rename,**不是整个目录树的单次原子交换**;停机状态避免读取窗口,中断由持久事务记录恢复。锁覆盖本工具的并发操作,不能阻止未经协调的原生安装器或用户重新启动宿主。
37
37
 
@@ -54,6 +54,8 @@ node /path/to/package/scripts/safe-upgrade.mjs --profile desktop --repair --appl
54
54
 
55
55
  这些测试不调用付费模型。真实模型达到 10,000 字的稳定率仍由模型决定;系统保证有限续写与草稿留存,不保证模型质量。具象名词密度依赖作者词典;承诺是否兑现未做语义判断。历史横评使用当前正典,不声称复原当时章前状态。伏笔声明边与 DAG 已确认状态分别报告,不自动篡改已收状态。
56
56
 
57
- Windows Desktop 的文件句柄冲突与恢复需 Windows 实机补验;本次在 macOS 完成实现和本地回归。原生 `dsh plugin add/update` 与「损坏 bundle 自动跳过并提供修复入口」属于 deepseek-harness 宿主代码,未在这个仓库修复,因此 #98 不应标为全部解决。现有宿主用户必须使用本工具的停机路径,或等宿主更新后再走原生更新。
57
+ 原生宿主修复保留在 [deepseek-harness fork PR #1](https://github.com/LiPu-jpg/deepseek-harness/pull/1),代码提交 `906e10574b7cc2875348cc31c43aa779a5c8e652`:Desktop 记录外壳进程、原生 CLI 在停机后暂存并校验整个 profile、拒绝运行中替换、保留真实 stdout/stderr 和中断恢复记录。该补丁的 [Windows/Linux 原生专项 CI](https://github.com/LiPu-jpg/deepseek-harness/actions/runs/39901088607) 通过:Windows 185 passed / 3 skipped,Linux 187 passed / 1 skipped;macOS 187 passed / 1 skipped,构建、类型检查及 43 项文档检查通过。Windows 用例实际运行 profile 内的程序并检查存活与退出后的升级行为,未执行交互式 Electron 人工验收。
58
58
 
59
- 本地回归记录(2026-10-23,macOS arm64):Core 全量 1,424 passed / 32 skipped;后续超时配置校验等定向回归 38 passed、规划转换回归 36 passed;前端 301 passed;managed runtime/策略 24 passed;离线升级 5 passed;跨端契约检查通过。Core wheel 已从独立解压目录验证版本、体检模块及 HTTP 路由。真实服务商调用、Windows 实机和发布操作均未执行。
59
+ 当前上游 master 已有损坏 bundle 的跳过加载及插件管理恢复能力,相关现有用例已重跑通过。上游仓库所有者已关闭 PR 提交入口,API 与网页均无法创建上游 PR;fork 补丁尚未合入上游或成为已发布宿主。它不包含在 OpenWrite 0.2.25 包内,因此 #98.1 仍需追踪宿主发布及 Windows Desktop 人工验收。现有宿主用户继续使用本工具的停机升级路径。
60
+
61
+ 本地回归记录(2026-10-23,macOS arm64):Core 全量 1,424 passed / 32 skipped;后续超时配置校验等定向回归 38 passed、规划转换回归 36 passed;前端 301 passed;managed runtime/策略 24 passed;离线升级 5 passed;跨端契约检查通过。Core wheel 已从独立解压目录验证版本、体检模块及 HTTP 路由。真实服务商调用与 Windows Electron 人工验收未执行。跨平台发布 CI 的 8 份原生安装/源码报告和 4 份精确宿主客户端报告均通过;发布状态与哈希见 [发布验收](RELEASE_ACCEPTANCE.md#025)。
@@ -0,0 +1,40 @@
1
+ # Core 5.8.14:连载报告逐项处理
2
+
3
+ 对应 #109(B-01–B-17、S-01–S-15),并包含 #114 的 OpenCode Go 请求头适配。
4
+ 这是源码修复;插件是否发布,以 npm 和 GitHub Release 为准。
5
+
6
+ | 条目 | 处理与边界 |
7
+ | --- | --- |
8
+ | B-01 / S-01 | style 默认份额 12%,可用 `context_style_share` 配置;在总体预算内优先保留最多 4000 字符的作者风格,最多占可压缩预算一半。各文件按实际长度分配。移除第二层 600/700 字符限额,仍保留整体提示词上限;返回 `style_profile_truncated`,不隐藏截断。 |
9
+ | B-02 / S-10 | 补救续写按剩余字数重新计算分段数量,段目标涵盖全部剩余字数时才要求章末钩子;节拍按已完成字数推进。支持关闭分段补救。 |
10
+ | B-03 / S-11 | 长段落相似度去重,默认 0.96,可设 0.9–1.0;短对话与数字不同的段落保留。不声称能够识别任意语义复述。 |
11
+ | B-04 / S-02 | 网络中断、输出截断至多重试两次,退避 1/2 秒;每次重试前检查取消。不重试认证/格式等错误;已知用量合并,缺失费用保持未知。 |
12
+ | B-05 / S-03 | coverage 小于 1 时 quality_score 与旧 score 均为 null,状态仍为 partial / inconclusive;CLI 与章节摘要显示未评分。未改变已完成维度的评审标准。 |
13
+ | B-06 / S-15 | `PROJECT_BUSY` 返回可恢复状态、建议 30 秒重试与重新读取 revision 的提示。不假称能预测长任务结束时间,也不放宽并发写锁。 |
14
+ | B-07 / S-04 | 大纲审计把无法解析的 payoff_of 列为 `UNKNOWN_PAYOFF_REFERENCE` 错误,保留原始引用与章节。 |
15
+ | B-08 / S-05 | 大纲树 `parse_report` 返回逐项元数据的 node_id、行、原键值、是否识别、识别数量和忽略明细。识别字段数与章节完整性必检项数是不同指标,不再靠固定数字推断。 |
16
+ | B-09 / S-12 | world/terminology.md 与 world/timeline.md 加入 writer 输入;完整预览附文档投影清单,原 manifest 继续提供来源、预算与排除项。 |
17
+ | B-10 / S-06 | related 同时支持目标到说明的映射及标准数组;默认按目标合并,raw_text 更新也保留关系,`related_mode: replace` 显式替换/清空。修正 TOML 根字段落入最后一个关系表的问题。 |
18
+ | B-11 / S-07 | 范围编辑提供 `end_inclusive: false` 保留结束锚点(例如下一节标题);默认 true 保持原接口兼容。 |
19
+ | B-12 / S-14 | retry、apply、undo 消费的凭据保留校验后的墓碑,可 reject 清理;消费凭据不能再次写入。相同内容 retry 不会误删新预览。 |
20
+ | B-13 | 逐字锚点在源码中没有行首语义;新增行中 `> ` 的范围定位及 old_text 实际写入回归,均通过。未复现为独立缺陷,不引入模糊替换来掩盖不匹配。 |
21
+ | B-14 / S-08 | resume / acknowledge / abandon 同时接受 operation_id 或可解析到现有操作的 chapter_id,缺少目标时给统一参数提示。external 创建操作仍需 chapter_id;baseline 是整书建基线。保留已有接口与错误码。 |
22
+ | B-15 / S-09 | 未完成接纳返回 completed:false、status:partial、blocking 与下一步 resume 参数;no_model 只接纳正文,明确不解除事实门禁。 |
23
+ | B-16 | 事实未更新的门禁错误明确说明尚未调用模型,提供 external / resume、operation_id 与受影响章节,保留稳定错误码。 |
24
+ | B-17 | `GET /api/context?chapter=ch_001&include_agent_payload=true` 提供完整 canonical writer payload、初始提示词及文档投影清单,不调用模型。后续生成 guidance、分段、重试和传输预算会改变具体请求,预览不冒充历史服务商逐次请求日志。 |
25
+ | S-13 | 写章结果 `context_summary` 返回已报告用量、压缩级别、style 实际字符数、客户端模型调用次数与二次截断标记;未报告值为 null。 |
26
+
27
+ 配置示例(`novel_config.yaml`):
28
+
29
+ ```yaml
30
+ context_style_share: 0.12
31
+ writing_options:
32
+ segmented_write: false
33
+ segment_similarity_threshold: 0.96
34
+ ```
35
+
36
+ `segmented_write: false` 同时关闭长章自动分段与首次不足的续写补救,长度修复仍按既有有界流程处理。
37
+ 相似度去重属于保守文本比较,无法替代人工判断故事是否从头复述。
38
+
39
+ 验证包括新增回归、既有预算/受保护约束、完整审稿与不完整审稿、资产、编辑凭据、接纳门禁、
40
+ 模型请求路由及全量 pytest。自动测试使用隔离目录和 ProjectRegistry,不使用作者模型凭据。
@@ -0,0 +1,31 @@
1
+ # OpenCode Go:DeepSeek V4.1 Flash
2
+
3
+ #114 的配置应使用 OpenAI 兼容 **Chat Completions** 协议。
4
+ 按 [OpenCode Go 官方文档](https://opencode.ai/v2/docs/console/go/)(2026-10-26 核对):
5
+
6
+ | OpenWrite 模型档案字段 | 值 |
7
+ | --- | --- |
8
+ | Provider / 协议适配器 | `openai` |
9
+ | API format | `chat` |
10
+ | Base URL | `https://opencode.ai/inference/go/openai/v1` |
11
+ | Model ID | `deepseek-v4.1-flash` |
12
+ | API Key | 当前 OpenCode Go 工作区的 Key |
13
+
14
+ 不要把 OpenCode 自己的 `opencode-go/` 模型选择前缀放入原始 API Model ID。
15
+ 完整 `/chat/completions` 地址也会被 Core 规范化为 API 根路径;旧 `/zen/go/v1`
16
+ 地址仅作为原有配置保留,不作为当前官方地址推荐。
17
+
18
+ Core 5.8.14 对 Go 请求自动发送真实 `OpenWrite/<版本>` User-Agent 与同一客户端内稳定的
19
+ `x-opencode-session`,符合官方客户端标识说明;不会冒充其他应用,也不发送作品路径。
20
+ 这覆盖 OpenWrite Core 的模型调用,DSH 对话模型另由宿主自己的适配器处理。
21
+
22
+ 官方说明 DeepSeek 模型可能需要工作区 Settings → Privacy → Regions 允许 Global。
23
+ Go 面向编码客户端,服务端的用途、地区、权限或额度策略仍可能拒绝请求;若订阅不适用写作,
24
+ 应选用服务商允许的 API 产品。这里不会尝试绕过服务端限制。
25
+
26
+ 先在「任务 → 模型」保存档案并运行连接测试,再将其选为写作/评审路由。
27
+ 认证、授权、模型不存在及限流分别保留对应错误码。若仍失败,反馈脱敏错误码、HTTP 状态、
28
+ 所用 API 路径和模型名即可,不需要提供 API Key。
29
+
30
+ 自动化测试核对新旧 Go 根路径、Chat 协议、原始模型名和稳定请求头;未使用用户的 Go 凭据,
31
+ 未执行收费请求,也不保证特定账户的授权状态。该修复是否已发布,以 npm/Release 为准。
@@ -43,7 +43,7 @@ Intel Mac cryptography wheel 由独立原生构建工作流从校验过的上游
43
43
 
44
44
  受管理后端仅使用自己的进程、动态回环端口和实例凭据。外部模式不接管用户进程。资源、请求、SSE、计时器、watcher 和 DoG 脚本都必须在创建它们的 scope 卸载时释放。
45
45
 
46
- 官方预设按版本发布,多宿主使用租约保护。安装前检查同名未管理目录,禁止合并写入作者目录。迁移先备份,再通过宿主 `dsh plugin remove` 删除仍在 `dependencies` 或 bundle 列表中的三个旧包;失败恢复备份。作者修改过的预设按无包装标记或校验不一致识别,不会删除。卸载默认保留作品、配置、凭据和历史。
46
+ 官方预设在 0.2 宿主中使用稳定 ID,并为历史版本注册兼容定义;旧宿主所需的版本目录会保留,多宿主使用租约保护。安装前检查同名未管理目录,禁止合并写入作者目录。迁移先备份,再通过宿主 `dsh plugin remove` 删除仍在 `dependencies` 或 bundle 列表中的三个旧包;失败恢复备份。作者修改过的预设按无包装标记或校验不一致识别,不会删除。卸载默认保留作品、配置、凭据和历史。
47
47
 
48
48
  受管理后端状态区分为:首次未启动、准备中、就绪、异常退出、恢复中、已取消、已卸载。dsh 运行期间已启动后端异常退出会有上限的退避重启,启动失败也计入该上限;恢复失败不得变成未处理拒绝、不得退出宿主。取消、宿主退出和卸载不复活。外部 `mode: external` 不接管进程。恢复不得重放付费任务。子进程环境为允许列表,不是 `process.env` 整包复制。
49
49
 
@@ -6,6 +6,14 @@
6
6
 
7
7
  新建作品的验收请求最多等待 60 秒(原为 15 秒),保留成功耗时和失败诊断;不重试写请求,超时仍失败。此调整容纳慢速运行器,不代表初始化耗时问题已被修复。
8
8
 
9
+ ## 0.2.25
10
+
11
+ [GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/tag/v0.2.25) 与 [npm `dsh-openwrite@0.2.25`](https://www.npmjs.com/package/dsh-openwrite/v/0.2.25) 均已发布,npm `latest` 已更新到 0.2.25。使用 [PR 完整发布 CI](https://github.com/LiPu-jpg/Openwrite/actions/runs/39893387812) 验收通过的原始产物,没有重新打包。该运行的来源提交为 `1c04d97c9571f09f9a49567c95b7c38c0507ddbc`,源码 tree `3db86dcdb88f69fd6d92f5881fee0db3c2f0bd8c` 与主分支合并提交 `f0785f2c331f2d090450b1e127e5750c64dc4487` 一致。包 SHA-256 为 `ca73ca424844ddee62f37c2725591bf44ff7701f25d1d12693fa270ee0c26049`;官方 registry 无认证下载的完整 10,141,143 字节与 CI 包一致,GitHub Release 的服务端附件 digest 也一致。
12
+
13
+ 8 份原生安装/源码报告和 4 份精确宿主客户端报告均通过,覆盖 Linux、macOS arm64/x64、Windows、Node 22.19/24/26、源码安装、alpha 兼容及宿主 `0.1.2-rc.1` / `0.1.5-alpha.1` / `0.2.0-rc.2` / `0.2.1-rc.2`。合并后的 [主分支发布 CI](https://github.com/LiPu-jpg/Openwrite/actions/runs/39895883025) 同样通过。核心 [PR #101](https://github.com/LiPu-jpg/Openwrite/pull/101) 与插件 [PR #102](https://github.com/LiPu-jpg/Openwrite/pull/102) 已合并。Core wheel 5.8.12 的 SHA-256 为 `fefd03aebfb0ebf084fd40f4897af309a532111903a3813f01276daa1c36c345`。
14
+
15
+ Core 全量 1424 passed / 32 skipped;后续超时配置校验等定向回归 38 passed、规划转换回归 36 passed;前端 301 passed;managed runtime/策略 24 passed;离线升级 5 passed;跨端契约通过。手工浏览器检查只使用 `~/my_novel`。真实模型调用为 0,Windows Electron 人工验收尚未执行。#98.1 的原生宿主补丁见 [fork PR #1](https://github.com/LiPu-jpg/deepseek-harness/pull/1),[专项 CI](https://github.com/LiPu-jpg/deepseek-harness/actions/runs/39901088607) 的 Windows 185 passed / 3 skipped、Linux 187 passed / 1 skipped。上游关闭 PR 提交入口,补丁尚未成为已发布宿主;详情见 [实现与限制](ISSUES_98_100.md)。
16
+
9
17
  ## 0.2.24
10
18
 
11
19
  [GitHub Release](https://github.com/LiPu-jpg/Openwrite/releases/tag/v0.2.24) 与 [npm `dsh-openwrite@0.2.24`](https://www.npmjs.com/package/dsh-openwrite/v/0.2.24) 均已发布,npm `latest` 已更新到 0.2.24。直接使用[完整发布 CI](https://github.com/LiPu-jpg/Openwrite/actions/runs/39716542888) 验收通过的原始产物,未重新打包。来源提交 `d16623c6b0d2a84d45dffe23c24e641df790175f`,源码 tree `e0b2fd0647114e46e6fd188f4fb32b13fadc6b3d` 与主分支合并提交 `32166de6a3f66d8ebb2e3b5bd3912c1c1236f07b` 一致。npm 包 SHA-256 为 `a9fb766e8277b0f7dfecd9ea2284720512583e766e82515b70a529c967aa0170`;从官方 registry 无认证重新下载后,包字节、SHA-1、SHA-512 integrity 与 CI 产物和 registry 元数据均一致。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-openwrite",
3
- "version": "0.2.25",
3
+ "version": "0.2.26",
4
4
  "description": "OpenWrite 长篇小说领域后端接入 DeepSeek Harness:统一创作 Agent 预设 + 原生创作工作台",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -54,7 +54,7 @@
54
54
  "displayName": "小说创作(OpenWrite)",
55
55
  "pluginType": "suite",
56
56
  "compatibility": {
57
- "dsh": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
57
+ "dsh": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3",
58
58
  "profiles": [
59
59
  "web",
60
60
  "desktop"
@@ -65,7 +65,8 @@
65
65
  "0.1.3-alpha.2": "unknown",
66
66
  "0.1.5-alpha.1": "compatible",
67
67
  "0.2.0-rc.2": "compatible",
68
- "0.2.1-rc.2": "compatible"
68
+ "0.2.1-rc.2": "compatible",
69
+ "0.2.1-rc.3": "compatible"
69
70
  }
70
71
  }
71
72
  },
@@ -115,9 +116,9 @@
115
116
  },
116
117
  "peerDependencies": {
117
118
  "@deepseek-ai/cordis": "4.0.2 || ~4.0.4",
118
- "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
119
- "@deepseek-ai/dsh-settings": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
120
- "@deepseek-ai/dsh-tools": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2"
119
+ "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3",
120
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3",
121
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3"
121
122
  },
122
123
  "bin": {
123
124
  "openwrite-maintenance": "scripts/maintenance.mjs",
@@ -1,3 +1,4 @@
1
+ import { UnsupportedRuntimePlatformError } from './managed-runtime.js';
1
2
  import { mkdir, writeFile } from 'node:fs/promises';
2
3
  import { join, resolve, win32 } from 'node:path';
3
4
  import { randomUUID } from 'node:crypto';
@@ -206,6 +207,9 @@ export class StudioClient {
206
207
  catch (error) {
207
208
  if (error instanceof StudioError)
208
209
  throw error;
210
+ if (error instanceof UnsupportedRuntimePlatformError) {
211
+ throw new StudioError(error.message, error.status, error.code, { platform: error.platform });
212
+ }
209
213
  if (this.context?.workspaceRoot) {
210
214
  try {
211
215
  const root = join(this.context.workspaceRoot, '.openwrite', 'transport-errors');
@@ -504,8 +504,14 @@ export class NovelDomainService extends Service {
504
504
  }
505
505
  }
506
506
  catch (error) {
507
- if (!lifetime.aborted)
508
- sendJson(res, 502, { error: '写作环境暂不可用,请打开环境与诊断重试' });
507
+ if (!lifetime.aborted) {
508
+ const failure = error;
509
+ if (failure.code === 'RUNTIME_PLATFORM_UNSUPPORTED') {
510
+ sendJson(res, 503, { error: failure.message, code: failure.code });
511
+ }
512
+ else
513
+ sendJson(res, 502, { error: '写作环境暂不可用,请打开环境与诊断重试' });
514
+ }
509
515
  }
510
516
  finally {
511
517
  responses.delete(res);
@@ -8,6 +8,8 @@ export interface Config {
8
8
  artifacts?: string;
9
9
  /** Base URL of the local OpenWrite Studio server. */
10
10
  baseUrl: string;
11
+ /** Optional instance token of an externally managed Studio server. */
12
+ authToken?: string;
11
13
  /** Per-tool cooperative timeout in ms; chapter writing/review can take minutes. */
12
14
  timeoutMs: number;
13
15
  /** Directory `novel_export` saves exported files into. */
@@ -17,6 +17,7 @@ export const Config = Schema.object({
17
17
  runtimeRoot: Schema.string(),
18
18
  artifacts: Schema.string(),
19
19
  baseUrl: Schema.string().default('http://127.0.0.1:4567'),
20
+ authToken: Schema.string().role('secret'),
20
21
  timeoutMs: Schema.number().default(600000),
21
22
  outputDir: Schema.string().default(join(tmpdir(), 'openwrite-exports')),
22
23
  });
@@ -38,7 +39,8 @@ export function apply(ctx, config) {
38
39
  return connection ? connection.requestRejection({ headers }) : 503;
39
40
  };
40
41
  const domain = new NovelDomainService(ctx, { authorizeRequest, baseUrl: config.baseUrl, timeoutMs: config.timeoutMs, outputDir: config.outputDir, resolveWorkspace,
41
- ...(runtime ? { connect: () => runtime.ensure() } : {}),
42
+ ...(runtime ? { connect: () => runtime.ensure() }
43
+ : config.authToken ? { connect: async () => ({ baseUrl: config.baseUrl, token: config.authToken }) } : {}),
42
44
  });
43
45
  ctx.effect(() => () => runtime?.dispose(), 'openwrite: owned runtime cleanup');
44
46
  ctx.inject(['webServer'], webCtx => webCtx.effect(() => webCtx.webServer.register({
@@ -40,6 +40,12 @@ export interface BackendConnection {
40
40
  baseUrl: string;
41
41
  token?: string;
42
42
  }
43
+ export declare class UnsupportedRuntimePlatformError extends Error {
44
+ readonly platform: string;
45
+ readonly code = "RUNTIME_PLATFORM_UNSUPPORTED";
46
+ readonly status = 503;
47
+ constructor(platform: string);
48
+ }
43
49
  export type SpawnFn = (command: string, args: readonly string[], options: SpawnOptions) => ChildProcess;
44
50
  export interface RuntimeOptions {
45
51
  spawn?: SpawnFn;
@@ -8,6 +8,16 @@ import { createInterface } from 'node:readline';
8
8
  import { x as untar } from 'tar';
9
9
  import { unzipSync } from 'fflate';
10
10
  import { lock as acquireLock } from 'proper-lockfile';
11
+ export class UnsupportedRuntimePlatformError extends Error {
12
+ platform;
13
+ code = 'RUNTIME_PLATFORM_UNSUPPORTED';
14
+ status = 503;
15
+ constructor(platform) {
16
+ super(`OpenWrite Studio managed runtime is unavailable on ${platform}. Configure the bridge with mode: external and baseUrl pointing to a supported Studio server; see docs/INSTALL.md#external-studio. File tools that do not require Studio remain available.`);
17
+ this.platform = platform;
18
+ this.name = 'UnsupportedRuntimePlatformError';
19
+ }
20
+ }
11
21
  export const DEFAULT_RESTART_LIMIT = 5;
12
22
  export const DEFAULT_BACKOFF_MS = [1_000, 2_000, 4_000, 8_000, 16_000];
13
23
  /** Budget for the per-request liveness probe of a previously-ready backend. */
@@ -538,7 +548,7 @@ export class ManagedRuntime {
538
548
  const manifest = JSON.parse(await readFile(join(this.artifacts, 'runtime-manifest.json'), 'utf8'));
539
549
  const platform = manifest.platforms[`${process.platform}-${process.arch}`];
540
550
  if (!platform)
541
- throw new Error(`Unsupported platform: ${process.platform}/${process.arch}`);
551
+ throw new UnsupportedRuntimePlatformError(`${process.platform}/${process.arch}`);
542
552
  if (!manifest.wheel || !manifest.requirements)
543
553
  throw new Error('The package is missing Core or dependency checksums');
544
554
  const wheel = join(this.artifacts, manifest.wheel.file);
@@ -350,16 +350,17 @@ export function registerNovelTools(ctx, clientFactory, options) {
350
350
  }));
351
351
  ctx.tools.register(defineTool({
352
352
  name: 'novel_context_preview',
353
- description: 'Preview the context packet OpenWrite would assemble for writing a chapter (relevant outline, assets, foreshadowing, and prior-chapter memory).',
353
+ description: 'Preview the context packet OpenWrite would assemble for writing a chapter (relevant outline, assets, foreshadowing, and prior-chapter memory). Set include_agent_payload to inspect canonical writer inputs and document inclusion without calling a model.',
354
354
  parameters: {
355
355
  chapter: { type: 'string', description: 'Chapter id (e.g. "ch_0007"); omit or pass "next" for the next chapter to write.' },
356
+ include_agent_payload: { type: 'boolean', description: 'Include the full writer payload, rendered initial messages and document inventory. Later segment/retry messages depend on the write request.' },
356
357
  },
357
358
  output: { schema: JSON_OUTPUT_SCHEMA, render: (_args, value) => renderJson(value) },
358
359
  timeoutMs,
359
360
  isConcurrencySafe: () => true,
360
361
  async execute(args, exec) {
361
362
  const client = clientFor(exec);
362
- return await client.getJson('/api/context', { chapter: args.chapter?.trim() || 'next' }, exec.signal);
363
+ return await client.getJson('/api/context', { chapter: args.chapter?.trim() || 'next', ...(args.include_agent_payload ? { include_agent_payload: 'true' } : {}) }, exec.signal);
363
364
  },
364
365
  }));
365
366
  ctx.tools.register(defineTool({
@@ -803,6 +804,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
803
804
  data: { type: 'json', description: 'Object of asset fields to merge (e.g. name, summary, aliases).' },
804
805
  body_markdown: { type: 'string', description: 'Replacement markdown body (character/world assets).' },
805
806
  raw_text: { type: 'string', description: 'Full replacement document text, validated by the server.' },
807
+ related_mode: { type: 'string', enum: ['merge', 'replace'], description: 'Preserve and merge existing related entries by default; replace explicitly clears or replaces relations.' },
806
808
  },
807
809
  output: { schema: JSON_OUTPUT_SCHEMA, render: (_args, value) => renderJson(value) },
808
810
  timeoutMs,
@@ -815,6 +817,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
815
817
  body['body_markdown'] = args.body_markdown;
816
818
  if (args.raw_text !== undefined)
817
819
  body['raw_text'] = args.raw_text;
820
+ if (args.related_mode !== undefined)
821
+ body['related_mode'] = args.related_mode;
818
822
  return await client.postJson('/api/assets/update', body, exec.signal);
819
823
  },
820
824
  }));
@@ -888,7 +892,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
888
892
  action: { type: 'string', required: true, enum: ['preview', 'apply', 'reject', 'retry', 'undo'], description: 'Plan lifecycle action.' },
889
893
  confirm: { type: 'boolean', description: 'Required true for apply and undo after author acceptance.' },
890
894
  path: { type: 'string', description: 'Existing document path for preview, relative to the novel root.' },
891
- edits: { type: 'json', description: 'Preview edits: [{old_text,new_text}] or [{start_text,end_text,new_text}].' },
895
+ edits: { type: 'json', description: 'Preview edits: [{old_text,new_text}] or [{start_text,end_text,new_text,end_inclusive}]. Range end defaults to inclusive for compatibility; end_inclusive:false preserves the end anchor (e.g. the next heading).' },
892
896
  preview_token: { type: 'string', description: 'Immutable preview or undo token for apply/reject/retry/undo.' },
893
897
  },
894
898
  output: { schema: JSON_OUTPUT_SCHEMA, render: (_args, value) => renderJson(value) },
@@ -28,12 +28,13 @@
28
28
  "patch": "./cordis.patch.yml"
29
29
  },
30
30
  "compatibility": {
31
- "dsh": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
31
+ "dsh": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3",
32
32
  "dshReleases": {
33
33
  "0.1.2-rc.1": "compatible",
34
34
  "0.1.5-alpha.1": "compatible",
35
35
  "0.2.0-rc.2": "compatible",
36
- "0.2.1-rc.2": "compatible"
36
+ "0.2.1-rc.2": "compatible",
37
+ "0.2.1-rc.3": "compatible"
37
38
  }
38
39
  }
39
40
  },
@@ -45,9 +46,9 @@
45
46
  },
46
47
  "peerDependencies": {
47
48
  "@deepseek-ai/cordis": "4.0.2 || ~4.0.4",
48
- "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
49
- "@deepseek-ai/dsh-tools": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2",
50
- "@deepseek-ai/dsh-workspace": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2"
49
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3",
50
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3",
51
+ "@deepseek-ai/dsh-workspace": "0.1.2-rc.1 || 0.1.5-alpha.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3"
51
52
  },
52
53
  "peerDependenciesMeta": {
53
54
  "@deepseek-ai/dsh-host-webserver": {