dsh-molbio-tools 0.21.0 → 0.21.1

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
@@ -10,6 +10,97 @@
10
10
  改为按**包名**引用后模块缓存问题随之消失——没有拷贝,也就没有需要保持同步的版本目录。
11
11
  下文历史条目里出现的 `vN` 目录保持原样,作为当时的记录。
12
12
 
13
+ ## [0.21.1] — 2026-10-25(DSH STORE 提交前置:一个 patch、逐版本兼容声明、一个 tarball 一个清单)
14
+
15
+ **工具数不变(60),插件运行时行为零改动。** 这一版不加任何计算能力,只把"这个包以什么形状提交"
16
+ 改成 DSH STORE 的提交预检与 DSH 自身的清单契约**都能接受、且不再互相矛盾**的那一种。三处改动都
17
+ 来自同一次上架提交:预检里能靠改包解决的两条已经解决,剩下两条的原理与请求另写在
18
+ [docs/store-preset-exemption.md](docs/store-preset-exemption.md)。
19
+
20
+ ### 1. 两层 patch 合并成单个 `cordis.patch.yml`
21
+
22
+ 0.21.0 的 `dsh.bundle.patch` 是**有序数组**:`["./cordis.patch.yml", "./preset/molbio-lab/preset.patch.yml"]`。
23
+ DSH 运行时接受这种写法(`@deepseek-ai/dsh-app-boot` 的 `bundlePatchPaths`:one file, or an
24
+ ordered list of files),官方 `@deepseek-ai/dsh-web-app` 自己就列了 5 个文件;但 DSH STORE 的提交
25
+ 预检(`scripts/check-plugin-submission.mjs` 的 `safePatchPath`)只接受字符串,数组直接判
26
+ `SUBMISSION_BUNDLE_PATCH_INVALID`。两边都自称官方契约,解法不必等预检放宽:
27
+
28
+ - `build/preset-patch.mjs` 现在直接产出**根目录的 `cordis.patch.yml`**,内容是**两个 insert**:
29
+ `molbio-client → ./host.mjs`(惰性宿主锚点)在前,`preset-molbio-lab`(由
30
+ `preset/molbio-lab/agent.cordis.yml` 生成的 preset 行)在后;顺序即应用顺序。
31
+ - 不再生成 `preset/molbio-lab/preset.patch.yml`(文件已删);`package.json` 的
32
+ `dsh.bundle.patch` 是**单字符串**。
33
+ - 四个读者改为跟单文件走:`test/preset-health.mjs`、`test/drift-probe.mjs`、
34
+ `preset/install.mjs`、`test/preset-rows.mjs`(后者在同一个文件的两个 insert 里找 preset 段,
35
+ 不假设"preset.patch.yml 才是 preset")。
36
+
37
+ > 这一条同时守住一条更早的教训:**锚点行不能因为"文件变少了"而消失**。0.15.2 的两个侧栏 tab
38
+ > 就是锚点丢失后静默消失的;`test/client-mount.mjs` 仍然逐包断言"被选中的包必须有一个宿主行
39
+ > 能解析到它",`test/contract.mjs` 仍然断言"产物里的每一处 `register` 都在 `inject` 里"。
40
+
41
+ ### 2. `engines.dsh` 与逐版本 `dshReleases`
42
+
43
+ 两个 manifest 各声明一份:
44
+
45
+ ```jsonc
46
+ "engines": { "node": ">=26", "dsh": ">=0.2.1-rc.2 <0.3.0-0" },
47
+ "dsh": { "compatibility": { "dshReleases": {
48
+ "0.2.1-rc.1": "unknown", "0.2.1-rc.2": "compatible", "0.2.1-rc.3": "unknown" } } }
49
+ ```
50
+
51
+ 上界写 `<0.3.0-0` 而不是 `<0.3.0`:DSH 用 `semver.satisfies(..., { includePrerelease: true })`
52
+ 判定(`@deepseek-ai/dsh-app-boot` 的 `unmetRequirements`),带 prerelease 选项时 `<0.3.0`
53
+ 会放行 `0.3.0-alpha.1`,`-0` 不会。`rc.1` 早于本包基线、`rc.3` 未取得也未验证,两者都只能
54
+ 标 `unknown`——**范围不等于验收**:`engines` 是安装时的粗筛,商城的 `dshReleases` 记录才是
55
+ 逐版本的验收结论。
56
+
57
+ 面板包随之 `0.1.3 → 0.1.4`:它的版本线独立于根包,但清单变了,不发这个版本号就没有能承载新
58
+ 声明的发布物。
59
+
60
+ ### 3. 根 tarball 不再内嵌面板包
61
+
62
+ `files` 白名单去掉 `packages`。此前根 tarball 里同时躺着 `package.json` 与
63
+ `packages/molbio-panel/package.json` 两个 DSH 插件清单,还重复携带 427 KB 相同的 `lib/client.js`;
64
+ 商城把"一个包里出现第二个插件清单"视为提交歧义是对的。现在根 tarball **只有一个插件清单**:
65
+ **118 → 115 个文件、约 1.1 MB → 约 999 kB**(多出的 1 个文件是本次提交的
66
+ `docs/store-preset-exemption-issue.md`)。面板没有因此消失——根包的 `lib/client.js` 本来就带着它
67
+ (第 4 节的启动回读可以作证);面板包继续按自己的地址单独发布。
68
+
69
+ `test/contract.mjs` 的打包断言随之改向:不再要求 `files` 必须包含 `packages/`,而是断言
70
+ **一个 tarball 里恰好只有一个 DSH 清单**(把 `packages` 加回白名单会立刻 FAIL)。
71
+
72
+ ### 4. 一次性 profile 证据(tarball 就是发布物)
73
+
74
+ 证据记录在 [docs/store-evidence.md](docs/store-evidence.md),用临时 `DSH_HOME` 重跑,不碰真实
75
+ `~/.dsh`:`npm pack` → `--from-default-profile web` 建 profile → `plugin add` tarball →
76
+ `--dump-config`(`molbio-client` 解析到已安装包内,preset 行带 `tool-molbio`)→
77
+ `--no-open --port 0` 启动 → 带 token 回读页面(**HTTP 200**,`__DSH_BOOT__` 图里有
78
+ `dsh-molbio-tools/client.js`)→ `plugin remove` → 再 `--dump-config`(`molbio` 引用 **0** 处)。
79
+ 装 tarball 而不是目录,是因为 tarball 同时证明 `files` 白名单带全了运行文件。
80
+
81
+ ### 5. 未做 / 边界
82
+
83
+ - **`0.2.1-rc.3` 没有验证**,因此标 `unknown`。拿到该版本后按
84
+ [docs/workflow.md](docs/workflow.md) 第 4 节的升级流程重跑 `preset-health` / `drift-probe` /
85
+ 一次性 profile,再把它改成 `compatible`。
86
+ - **预检的另外两条(保护命名空间、身份一致)不是改包能解决的**:本包已自行消掉
87
+ `SUBMISSION_BUNDLE_PATCH_INVALID`;剩下两条的证据、代码位置与三个可执行请求写在
88
+ [docs/store-preset-exemption.md](docs/store-preset-exemption.md),可直接贴到 issue 的短版在
89
+ [docs/store-preset-exemption-issue.md](docs/store-preset-exemption-issue.md)。
90
+ - **工具行为与 benchmark 一律不动**:60 个工具的输出与 0.21.0 相同,按规则 6 的"没变就不必跑
91
+ 模型";`bench --offline` 仍在 `npm test` 里,期望值照样逐条校验。
92
+
93
+ ### 6. 版本与验证
94
+
95
+ - 根包 `0.21.0 → 0.21.1`;面板 `0.1.4`(见 §2)。
96
+ - `npm test` 全绿:smoke、画面四套、客户端四套、contract 16 项、drift-probe 14 条突变、
97
+ preset-health 30 行、release 13 项、benchmark 四道守卫 + `bench --offline` 61 题。
98
+ - `node build/preset-patch.mjs --check` 与 `node build/client-bundle.mjs --check` 均报告与源一致。
99
+ - `node build/release-preflight.mjs --tag v0.21.1`:本地不变量全过;npm 上 `dsh-molbio-tools`
100
+ 已有 0.21.0、本版按 `publish` 处理;面板 0.1.4 尚未发布。
101
+ - 文档同步:maintainer 的版本号、roadmap 的当前状态、history 的版本一览与本段、store-evidence
102
+ 的 tarball 名与文件数。
103
+
13
104
  ## [0.21.0] — 2026-10-23(DSH 0.2.1-rc.2 适配;两条 benchmark 欠账清零)
14
105
 
15
106
  **工具数不变(60),插件行为零改动。** 这是一次"只跟上游走"的版本:0.20.1 之后本机 harness
package/README.md CHANGED
@@ -79,7 +79,7 @@ dsh plugin --profile web update dsh-molbio-tools
79
79
  > **0.7.1 起不再需要"注册 preset 根目录"这一步。**
80
80
  > DSH 0.1.7-alpha.1 移除了 `@deepseek-ai/dsh-agent-presets` 及其目录发现机制:preset 现在是
81
81
  > bundle patch 里的一个 `@deepseek-ai/dsh-agent-preset` **行**(本包的是
82
- > `preset/molbio-lab/preset.patch.yml`,由 `dsh.bundle.patch` 声明)。
82
+ > `cordis.patch.yml` 里的第二个 insert,由 `dsh.bundle.patch` 声明)。
83
83
  > 因此 preset 随 bundle 一起安装,**装完即出现**。
84
84
  >
85
85
  > 老版本遗留的两样东西可以清理(`node preset\install.mjs --profile web` 会替你查出来):
@@ -95,12 +95,12 @@ dsh plugin --profile web update dsh-molbio-tools
95
95
 
96
96
  ### 工具只在专属模式里出现(设计如此)
97
97
 
98
- 本包的 bundle patch 分两层,分工是固定的:
98
+ 本包的 bundle patch 是**一个文件、两层 insert**,分工是固定的:
99
99
 
100
- | 层 | 作用 |
100
+ | 层(同一个 `cordis.patch.yml` 里的两段) | 作用 |
101
101
  | --- | --- |
102
- | `cordis.patch.yml` | 只插一行**惰性宿主锚点**(`./host.mjs`),不注册任何工具 |
103
- | `preset/molbio-lab/preset.patch.yml` | 声明 **Molecular Biology Lab** 模式,60 个工具在这一层里 |
102
+ | `molbio-client` → `./host.mjs` | 只插一行**惰性宿主锚点**,不注册任何工具 |
103
+ | `preset-molbio-lab` → `@deepseek-ai/dsh-agent-preset` | 声明 **Molecular Biology Lab** 模式,60 个工具在这一层里 |
104
104
 
105
105
  所以 60 个工具**只在选中该模式的会话里加载**,其它模式(standard / ptc / minimal / cordis)
106
106
  不带它们,不占提示预算。bundle 安装 ≠ 工具全局可见:bundle 只是把"模式"和"面板"一起装进来。
@@ -741,11 +741,11 @@ dsh-molbio-tools/
741
741
  ├── lib/client.js # 客户端产物(exports["./client"],由 npm run build:client 生成)
742
742
  ├── packages/molbio-panel/ # 面板专用包(只面板、不带工具)
743
743
  ├── preset/molbio-lab/ # 专属模式 preset:agent.cordis.yml 是行清单(手改这里)
744
- │ # preset.patch.yml 由 build/preset-patch.mjs 生成
744
+ │ # preset.yml 是选择器文案;两者由生成器写进下面的 cordis.patch.yml
745
745
  ├── benchmark/ # 可用性评估:tasks/*.json + 判分器 + headless profile 推导(见其 README)
746
746
  ├── test/ # 冒烟 + 光栅化器 + 客户端/组合检查(含 preset 漂移与 benchmark profile 守卫)
747
747
  ├── docs/ # 维护者与实现文档(rules / workflow / roadmap / history 是本目录的骨架)
748
- └── cordis.patch.yml # bundle 的第一层补丁(当前为空列表;工具由 preset 层承载)
748
+ └── cordis.patch.yml # 唯一 bundle patch(生成物):锚点 insert + preset insert
749
749
  ```
750
750
 
751
751
  ---
@@ -222,7 +222,7 @@ session "…" runs under agent preset "molbio-lab", which the one-shot runner do
222
222
  Presets are an agent-plane feature the Web surface composes; the one-shot runner
223
223
  drives `agents.create()` directly and never joins one. So the tools must be
224
224
  mounted as **host rows**, which is the one shape this package otherwise refuses
225
- to ship (`cordis.patch.yml` is deliberately an empty list).
225
+ to ship (`cordis.patch.yml` only inserts the inert client anchor).
226
226
 
227
227
  `benchmark/profile.mjs` therefore generates a dedicated profile
228
228
  (`$DSH_HOME/profiles/molbio-bench`) whose patch layer is **derived** from
@@ -17,7 +17,8 @@
17
17
  * agent-plane feature the Web surface composes; the one-shot runner drives
18
18
  * `agents.create()` directly and never joins one. So the tools have to be
19
19
  * mounted as HOST rows, which is the one shape this package otherwise refuses
20
- * to ship (`cordis.patch.yml` is deliberately empty — see the preset's header).
20
+ * to ship (`cordis.patch.yml` only inserts the inert client anchor — see the
21
+ * preset's header).
21
22
  *
22
23
  * ── What keeps the reconstruction honest ────────────────────────────────────
23
24
  *
@@ -1,35 +1,55 @@
1
1
  /**
2
2
  * dsh-molbio-tools/build/preset-patch.mjs
3
3
  *
4
- * Generate the AGENT-PRESET bundle patch from the canonical row list.
4
+ * Generate the ONE bundle patch this package ships: the client anchor plus the
5
+ * "Molecular Biology Lab" agent preset.
5
6
  *
6
- * WHY THIS EXISTS
7
- * ---------------
8
- * DSH 0.1.7-alpha.1 replaced the preset DIRECTORY mechanism. Before it, a preset
9
- * was an `agent.cordis.yml` the harness discovered from `<dshHome>/.agent-presets`
10
- * or from a configured root (`@deepseek-ai/dsh-agent-presets` scanned
11
- * directories). That package is gone, and the registry that replaced it "neither
12
- * scans directories nor accepts preset paths" — a preset is now a
13
- * `@deepseek-ai/dsh-agent-preset` ROW inside a bundle patch:
7
+ * WHY ONE FILE
8
+ * ------------
9
+ * DSH's runtime accepts `dsh.bundle.patch` as a file path or an ordered list of
10
+ * file paths (`@deepseek-ai/dsh-app-boot` → `bundlePatchPaths`), and the shipped
11
+ * `@deepseek-ai/dsh-web-app` uses the list form. DSH STORE's submission precheck
12
+ * only accepts a single string (`safePatchPath`), so the two layers this package
13
+ * used to ship as a list are emitted as one file with two top-level `insert`
14
+ * operations, in the same order they were applied:
14
15
  *
15
- * - insert:
16
- * - id: preset-molbio-lab
17
- * name: '@deepseek-ai/dsh-agent-preset'
18
- * config:
19
- * id: molbio-lab
20
- * name: Molecular Biology Lab
21
- * description: ...
22
- * plugins: [ ...the rows that used to be the composition... ]
16
+ * 1. molbio-client → './host.mjs' (inert host anchor; see below)
17
+ * 2. preset-molbio-lab → '@deepseek-ai/dsh-agent-preset'
23
18
  *
24
- * The rows themselves did not change — only their container did. Keeping the
25
- * rows in ONE place and generating the container from them is what stops the two
26
- * from drifting, because `test/preset-health.mjs` validates every row against
27
- * the INSTALLED harness's own schemas.
19
+ * Nothing about either layer changes: a patch file is an ordered list of
20
+ * operations, and DSH applies them in file order exactly as it applied the two
21
+ * files in list order.
22
+ *
23
+ * WHY THE ANCHOR EXISTS
24
+ * ---------------------
25
+ * DSH's client-module scan walks the HOST Loader tree only. It reads each live
26
+ * host row's package manifest, takes `dsh.client` and `exports["./client"]` from
27
+ * it, and composes the browser graph that becomes `window.__DSH_BOOT__`. A
28
+ * preset's rows live in an isolated subtree that scan never visits.
29
+ *
30
+ * So when the tools moved into the preset, this package lost its only host-plane
31
+ * row, its bundle stopped reaching the page, and the two right-sidebar tabs
32
+ * (Molbio / Papers) silently disappeared. `host.mjs` restores exactly one inert
33
+ * row so the browser half is discoverable again — without putting any tool into
34
+ * a session that did not choose the molbio-lab mode.
35
+ *
36
+ * WHY THE PRESET IS GENERATED
37
+ * ---------------------------
38
+ * DSH 0.1.7-alpha.1 removed the `<dshHome>/.agent-presets` directory scan: a
39
+ * preset is now a `@deepseek-ai/dsh-agent-preset` ROW in a bundle patch, which
40
+ * is what the second insert below declares. Because the BUNDLE registers it,
41
+ * `dsh plugin --profile <p> add dsh-molbio-tools` puts the mode in the picker
42
+ * with no copy step and no preset-root patch — see README「安装」.
43
+ *
44
+ * Keeping the rows in ONE place and generating the container from them is what
45
+ * stops the two from drifting, because `test/preset-health.mjs` validates every
46
+ * row against the INSTALLED harness's own schemas.
28
47
  *
29
48
  * source : preset/molbio-lab/agent.cordis.yml (top-level row list, comments intact)
30
- * output : preset/molbio-lab/preset.patch.yml (the bundle patch, GENERATED)
49
+ * source : preset/molbio-lab/preset.yml (picker name + description)
50
+ * output : cordis.patch.yml (GENERATED; anchor + preset)
31
51
  *
32
- * The source is emitted as-is: no re-serialization, so the comments that explain
52
+ * The rows are emitted as-is: no re-serialization, so the comments that explain
33
53
  * each row survive and the file stays diffable against the shipped `standard`
34
54
  * patch it descends from.
35
55
  *
@@ -55,20 +75,56 @@ export function rowBlockOf(source) {
55
75
  /** Indent every line of a block by `width` spaces. */
56
76
  const indent = (block, width) => block.split('\n').map((line) => (line === '' ? line : ' '.repeat(width) + line)).join('\n');
57
77
 
78
+ const HEADER = [
79
+ '# dsh-molbio-tools bundle patch — the one file `dsh.bundle.patch` names.',
80
+ '#',
81
+ '# GENERATED by build/preset-patch.mjs. Edit the row list in',
82
+ '# preset/molbio-lab/agent.cordis.yml (and the picker strings in preset.yml), then',
83
+ '# re-run `node build/preset-patch.mjs`; hand edits here are reverted by the next',
84
+ '# run, and `node build/preset-patch.mjs --check` fails while the two disagree.',
85
+ '#',
86
+ '# Two top-level inserts, applied in this order:',
87
+ '# 1. molbio-client — inert host anchor for the browser half (see below)',
88
+ '# 2. preset-molbio-lab — the "Molecular Biology Lab" agent preset',
89
+ ].join('\n');
90
+
91
+ const ANCHOR = [
92
+ '# ── 1. the CLIENT-BUNDLE ANCHOR ────────────────────────────────────────────',
93
+ '#',
94
+ '# `host.mjs` registers no tool and publishes no service. Its only job is to be a',
95
+ "# loader row DSH's client-module scan can see: that scan walks the HOST Loader",
96
+ "# tree, reads the row's package manifest, and takes `dsh.client` +",
97
+ '# `exports["./client"]` from it to compose `window.__DSH_BOOT__`. A preset\'s rows',
98
+ '# live in an isolated subtree the scan never visits, so a `dsh.client` package',
99
+ '# referenced only from inside a preset loses its only discoverable row and its',
100
+ '# right-sidebar tabs silently disappear.',
101
+ '#',
102
+ '# Keep the tool in the PRESET and only the anchor here. An earlier version of',
103
+ "# this file inserted a host row for the tool itself (`name: dsh-molbio-tools`),",
104
+ "# which both registered all 60 tools globally and collided with the preset's own",
105
+ '# copy of that row:',
106
+ '#',
107
+ '# broken: "tool-molbio (...): never started"',
108
+ '- insert:',
109
+ ' - id: molbio-client',
110
+ " name: './host.mjs'",
111
+ ].join('\n');
112
+
113
+ /**
114
+ * Build the bundle patch: anchor first, preset second.
115
+ *
116
+ * @param {{rows: string, description: string, displayName?: string}} input
117
+ * @returns {string} the complete `cordis.patch.yml` text (single trailing newline)
118
+ */
58
119
  export function buildPatch({ rows, description, displayName = 'Molecular Biology Lab' }) {
59
- const header = [
60
- '# dsh-molbio-tools bundle layer: declares the "Molecular Biology Lab" agent preset.',
120
+ const presetHeader = [
121
+ '# ── 2. the MOLECULAR BIOLOGY LAB PRESET ───────────────────────────────────',
61
122
  '#',
62
- '# GENERATED by build/preset-patch.mjs from preset/molbio-lab/agent.cordis.yml.',
63
- '# Edit the ROW LIST there and re-run `node build/preset-patch.mjs`; editing the',
64
- "# `plugins:` block below by hand will be reverted by the next run, and",
65
- '# `node build/preset-patch.mjs --check` fails while the two disagree.',
66
- '#',
67
- '# DSH 0.1.7-alpha.1 removed the `<dshHome>/.agent-presets` directory scan: a preset is',
68
- '# now a `@deepseek-ai/dsh-agent-preset` ROW in a bundle patch, which is what this',
69
- '# layer inserts. Because the BUNDLE registers it, `dsh plugin --profile <p> add',
70
- '# dsh-molbio-tools` puts the mode in the picker with no copy step and no',
71
- '# preset-root patch — see README「安装」.',
123
+ '# DSH 0.1.7-alpha.1 removed the `<dshHome>/.agent-presets` directory scan: a',
124
+ '# preset is now a `@deepseek-ai/dsh-agent-preset` ROW in a bundle patch, which',
125
+ '# is what this insert declares. Because the BUNDLE registers it,',
126
+ '# `dsh plugin --profile <p> add dsh-molbio-tools` puts the mode in the picker',
127
+ '# with no copy step and no preset-root patch — see README「安装」.',
72
128
  '#',
73
129
  '# `order: 5` is deliberate: the shipped presets occupy 1..4 (standard, ptc,',
74
130
  '# minimal, cordis) and the registry sorts by `order` with an id tie-break, so a',
@@ -76,7 +132,11 @@ export function buildPatch({ rows, description, displayName = 'Molecular Biology
76
132
  ].join('\n');
77
133
 
78
134
  return [
79
- header,
135
+ HEADER,
136
+ '',
137
+ ANCHOR,
138
+ '',
139
+ presetHeader,
80
140
  '- insert:',
81
141
  ' - id: preset-molbio-lab',
82
142
  " name: '@deepseek-ai/dsh-agent-preset'",
@@ -95,7 +155,7 @@ function main() {
95
155
  const check = process.argv.includes('--check');
96
156
  const sourcePath = join(repoRoot, 'preset', 'molbio-lab', 'agent.cordis.yml');
97
157
  const metaPath = join(repoRoot, 'preset', 'molbio-lab', 'preset.yml');
98
- const outPath = join(repoRoot, 'preset', 'molbio-lab', 'preset.patch.yml');
158
+ const outPath = join(repoRoot, 'cordis.patch.yml');
99
159
 
100
160
  const rows = rowBlockOf(readFileSync(sourcePath, 'utf8'));
101
161
  if (rows === undefined || !rows.includes('- id:')) {
@@ -124,16 +184,16 @@ function main() {
124
184
  process.exit(1);
125
185
  }
126
186
  if (current !== text) {
127
- console.error('preset-patch: the committed preset patch is STALE — run `node build/preset-patch.mjs`');
187
+ console.error('preset-patch: the committed bundle patch is STALE — run `node build/preset-patch.mjs`');
128
188
  process.exit(1);
129
189
  }
130
- console.log('preset patch is current');
190
+ console.log('bundle patch is current');
131
191
  return;
132
192
  }
133
193
 
134
194
  writeFileSync(outPath, text, 'utf8');
135
- const rows_ = rows.split('\n').filter((line) => /^\s*- id:/.test(line)).length;
136
- console.log(`preset patch written: ${outPath} (${rows_} rows)`);
195
+ const rowCount = rows.split('\n').filter((line) => /^\s*- id:/.test(line)).length;
196
+ console.log(`bundle patch written: ${outPath} (${rowCount} preset rows + anchor)`);
137
197
  }
138
198
 
139
199
  if (process.argv[1] !== undefined && import.meta.url === pathToFileURLSafe(process.argv[1])) main();
package/cordis.patch.yml CHANGED
@@ -1,34 +1,196 @@
1
- # dsh-molbio-tools bundle layer: the CLIENT-BUNDLE ANCHOR.
1
+ # dsh-molbio-tools bundle patch — the one file `dsh.bundle.patch` names.
2
2
  #
3
- # ── WHAT THIS LAYER INSERTS, AND WHY IT IS NOT THE TOOL ────────────────────
3
+ # GENERATED by build/preset-patch.mjs. Edit the row list in
4
+ # preset/molbio-lab/agent.cordis.yml (and the picker strings in preset.yml), then
5
+ # re-run `node build/preset-patch.mjs`; hand edits here are reverted by the next
6
+ # run, and `node build/preset-patch.mjs --check` fails while the two disagree.
4
7
  #
5
- # - id: molbio-client
6
- # name: './host.mjs' # inert host half; see host.mjs
8
+ # Two top-level inserts, applied in this order:
9
+ # 1. molbio-client — inert host anchor for the browser half (see below)
10
+ # 2. preset-molbio-lab — the "Molecular Biology Lab" agent preset
11
+
12
+ # ── 1. the CLIENT-BUNDLE ANCHOR ────────────────────────────────────────────
7
13
  #
8
- # The 57 tools are NOT here. They load only inside the molbio-lab preset, which
9
- # is the point of that mode (see preset/molbio-lab/agent.cordis.yml, whose row is
10
- # generated into preset.patch.yml — the bundle's SECOND patch layer).
14
+ # `host.mjs` registers no tool and publishes no service. Its only job is to be a
15
+ # loader row DSH's client-module scan can see: that scan walks the HOST Loader
16
+ # tree, reads the row's package manifest, and takes `dsh.client` +
17
+ # `exports["./client"]` from it to compose `window.__DSH_BOOT__`. A preset's rows
18
+ # live in an isolated subtree the scan never visits, so a `dsh.client` package
19
+ # referenced only from inside a preset loses its only discoverable row and its
20
+ # right-sidebar tabs silently disappear.
11
21
  #
12
- # This row exists because DSH's client-module scan walks the HOST Loader tree
13
- # only. It reads each live host row's package manifest, takes `dsh.client` and
14
- # `exports["./client"]` from it, and composes the browser graph that becomes
15
- # `window.__DSH_BOOT__`. A preset's rows live in an isolated subtree that scan
16
- # never visits.
17
- #
18
- # So when the tools moved into the preset, this package lost its only host-plane
19
- # row, its bundle stopped reaching the page, and the two right-sidebar tabs
20
- # (Molbio / Papers) silently disappeared: the panel code was still in the
21
- # artifact, nothing ever loaded it. `host.mjs` restores exactly one inert row so
22
- # the browser half is discoverable again — without putting any tool into a
23
- # session that did not choose the molbio-lab mode.
24
- #
25
- # An earlier version of this file inserted a host row for the tool itself
26
- # (`name: dsh-molbio-tools`), which both registered all 57 tools globally and
27
- # collided with the preset's own copy of that row:
22
+ # Keep the tool in the PRESET and only the anchor here. An earlier version of
23
+ # this file inserted a host row for the tool itself (`name: dsh-molbio-tools`),
24
+ # which both registered all 60 tools globally and collided with the preset's own
25
+ # copy of that row:
28
26
  #
29
27
  # broken: "tool-molbio (...): never started"
30
- #
31
- # Keep the tool in the PRESET and only the anchor here.
32
28
  - insert:
33
29
  - id: molbio-client
34
30
  name: './host.mjs'
31
+
32
+ # ── 2. the MOLECULAR BIOLOGY LAB PRESET ───────────────────────────────────
33
+ #
34
+ # DSH 0.1.7-alpha.1 removed the `<dshHome>/.agent-presets` directory scan: a
35
+ # preset is now a `@deepseek-ai/dsh-agent-preset` ROW in a bundle patch, which
36
+ # is what this insert declares. Because the BUNDLE registers it,
37
+ # `dsh plugin --profile <p> add dsh-molbio-tools` puts the mode in the picker
38
+ # with no copy step and no preset-root patch — see README「安装」.
39
+ #
40
+ # `order: 5` is deliberate: the shipped presets occupy 1..4 (standard, ptc,
41
+ # minimal, cordis) and the registry sorts by `order` with an id tie-break, so a
42
+ # shared number would let the picker order depend on alphabetical accident.
43
+ - insert:
44
+ - id: preset-molbio-lab
45
+ name: '@deepseek-ai/dsh-agent-preset'
46
+ config:
47
+ id: molbio-lab
48
+ order: 5
49
+ name: Molecular Biology Lab
50
+ description: "分子生物学专属模式:标准编码能力 + 60 个 molbio_* 工具(序列分析、引物设计(含跨内含子 qPCR、盐/浓度旋钮与目标位置偏好)、TaqMan 水解探针(含 MGB 与订购 CSV)与多重 PCR 互扰/退火温度/浓度建议、克隆模拟与选酶(含 Golden Gate)、酶目录查询、甲基化敏感位点与双酶切 buffer 兼容检查、多序列比对(每个输入残基都会进比对结果)、保守性分析与比对后处理(共识/缺口修剪/同一性矩阵/逐列覆盖度,含 sequence logo 图)、CRISPR gRNA 设计(SpCas9/SaCas9/Cas12a/Cas13,家族特定 PAM/PFS/种子)与脱靶搜索、SnapGene/GenBank 解析与质粒图谱、Sanger 测序验证、蛋白性质/密码子优化/螺旋轮投影与疏水性图、qPCR 与绘图、虚拟凝胶、实验台分析(FASTQ 质控、密码子使用、系统发生树(长叶名自动折行)、in-silico PCR、CpG/GC 组成、批量文件分析/CSV)、静默酶切位点突变设计——生成的 SVG 图片自动用系统默认应用打开,文献/协议/实验记录)。"
51
+ plugins:
52
+ - id: persona
53
+ name: '@deepseek-ai/dsh-persona'
54
+ config:
55
+ prefix: You are a coding agent powered by the {{model}} model.
56
+ - id: agent-instructions
57
+ name: '@deepseek-ai/dsh-agent-instructions'
58
+ config:
59
+ maxBytes: 65536
60
+ - id: time-context
61
+ name: '@deepseek-ai/dsh-time-context'
62
+ - id: tool-bash
63
+ name: '@deepseek-ai/dsh-tool-bash'
64
+ disabled: !!js process.platform === 'win32'
65
+ - id: tool-pwsh
66
+ name: '@deepseek-ai/dsh-tool-pwsh'
67
+ disabled: !!js process.platform !== 'win32'
68
+ - id: tool-fs
69
+ name: '@deepseek-ai/dsh-tool-fs'
70
+ - id: tool-fs-search
71
+ name: '@deepseek-ai/dsh-tool-fs-search'
72
+ config:
73
+ sampleOverCapGlobResults: false
74
+ - id: tool-jobs
75
+ name: '@deepseek-ai/dsh-tool-jobs'
76
+ - id: tool-schedule
77
+ name: '@deepseek-ai/dsh-tool-schedule'
78
+ - id: skill-filesystem
79
+ name: '@deepseek-ai/dsh-skill-filesystem'
80
+ - id: tool-skill
81
+ name: '@deepseek-ai/dsh-tool-skill'
82
+ - id: command-goal
83
+ name: '@deepseek-ai/dsh-command-goal'
84
+ - id: tool-goal
85
+ name: '@deepseek-ai/dsh-tool-goal'
86
+ - id: planning
87
+ name: cordis:group
88
+ group: true
89
+ isolate:
90
+ planMode: true
91
+ config:
92
+ - id: plan-mode
93
+ name: '@deepseek-ai/dsh-plan-mode'
94
+ config:
95
+ section: |
96
+ You are in plan mode. Stay in plan mode until the user approves your plan through exit_plan_mode or switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
97
+
98
+ Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
99
+
100
+ The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
101
+
102
+ Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
103
+
104
+ Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
105
+
106
+ When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
107
+ - id: compaction
108
+ name: cordis:group
109
+ group: true
110
+ isolate:
111
+ compaction: true
112
+ toolResultPruner: true
113
+ config:
114
+ - id: compaction-basic
115
+ name: '@deepseek-ai/dsh-compaction-basic'
116
+ - id: command-compact
117
+ name: '@deepseek-ai/dsh-command-compact'
118
+ - id: tool-result-pruner
119
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
120
+ config:
121
+ thresholdChars: 8192
122
+ headChars: 4096
123
+ tailChars: 1024
124
+ - id: delegation
125
+ name: cordis:group
126
+ group: true
127
+ isolate:
128
+ workflowEngine: true
129
+ config:
130
+ - id: tool-subagent-control
131
+ name: '@deepseek-ai/dsh-tool-subagent-control'
132
+ - id: tool-subagent-list-agents
133
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
134
+ - id: tool-subagent
135
+ name: '@deepseek-ai/dsh-tool-subagent'
136
+ config:
137
+ provider: spawn
138
+ toolName: subagent
139
+ modelSelectionSettings: true
140
+ - id: tool-subagent-fork
141
+ name: '@deepseek-ai/dsh-tool-subagent'
142
+ config:
143
+ provider: fork
144
+ toolName: subagent_fork
145
+ - id: workflow-ptc
146
+ name: '@deepseek-ai/dsh-workflow-ptc'
147
+ config:
148
+ provider: spawn
149
+ - id: tool-workflow
150
+ name: '@deepseek-ai/dsh-tool-workflow'
151
+ - id: tool-ask-user
152
+ name: '@deepseek-ai/dsh-tool-ask-user'
153
+ - id: tool-todo
154
+ name: '@deepseek-ai/dsh-tool-todo'
155
+ config:
156
+ allowParallelInProgress: true
157
+ - id: tool-web
158
+ name: '@deepseek-ai/dsh-tool-web'
159
+ config:
160
+ fetch: true
161
+ searchTimeoutMs: 60000
162
+ - id: present
163
+ name: '@deepseek-ai/dsh-tool-present'
164
+ - id: tool-plugin-manager
165
+ name: '@deepseek-ai/dsh-plugin-manager/tools'
166
+ disabled: true
167
+ - id: tool-plugin-market
168
+ name: '@deepseek-ai/dsh-plugin-market/tools'
169
+ disabled: true
170
+
171
+ # dsh-molbio-tools: molecular-biology toolset (60 tools across sequence
172
+ # analysis, primers (incl. TaqMan probes and multiplex compatibility),
173
+ # in-silico PCR, cloning (incl. Golden Gate), enzyme catalog, methylation-aware
174
+ # and double digests, plasmids, Sanger verification, proteins (incl. helical
175
+ # wheel/hydropathy plots), qPCR, virtual gels, multiple sequence
176
+ # alignment/conservation (every supplied residue is conserved), sequence logos,
177
+ # codon-usage analysis, phylogenetic trees (bootstrap + Newick, long labels
178
+ # wrapped), CpG-island/GC-skew composition, read-level FASTQ QC, CRISPR gRNA
179
+ # design, and literature/protocol/experiment records).
180
+ # Registers into the host `tools` registry and publishes no service, so it
181
+ # needs no isolate realm.
182
+ #
183
+ # The specifier is the PACKAGE NAME, resolved by Node from the profile's
184
+ # node_modules — not a `./plugins/…` path. A relative specifier here does NOT
185
+ # work on DSH 0.1.7-alpha.1: the preset mounts, but the entry is created without
186
+ # ever being imported, so the roster reports the mode as broken and it loads
187
+ # zero tools:
188
+ #
189
+ # tool-molbio (./plugins/dsh-molbio-tools-v20/index.mjs): never started
190
+ #
191
+ # A bare name is also what makes upgrades automatic: `dsh plugin update` moves
192
+ # the package and this row follows it. The per-release `plugins/dsh-molbio-
193
+ # tools-vN/` copies existed only to work around ESM module-cache reuse of a
194
+ # relative file URL, which a resolved package name does not have.
195
+ - id: tool-molbio
196
+ name: 'dsh-molbio-tools'
package/docs/history.md CHANGED
@@ -16,6 +16,7 @@
16
16
 
17
17
  | 包版本 | preset 版本 | 内容 | 工具数 |
18
18
  | --- | --- | --- | --- |
19
+ | 0.21.1 | — | **DSH STORE 提交前置**:单文件 bundle patch、逐版本兼容声明;根 tarball 只带一个插件清单(工具行为零变化) | 60 |
19
20
  | 0.21.0 | — | **DSH 0.2.1-rc.2 适配**:preset 行清单逐行重抄;contract 两条断言重锚到行为;两条 suspended 记录用真实模型重录 | 60 |
20
21
  | 0.20.1 | — | 插件专属图标(透明底双螺旋 SVG),运行时零改动 | 60 |
21
22
  | 0.20.0 | — | **Cas 家族几何**(SpCas9/SaCas9/Cas12a/Cas13a/b/d)+ 反向链脱靶定位修复;TaqMan MGB/订购 CSV;multiplex 退火温度/浓度;批量分析 CSV;静默位点突变;并入 0.19.1 硬化 | 60 |
@@ -43,6 +44,29 @@
43
44
 
44
45
  ---
45
46
 
47
+ ## v0.21.1(2026-10-25,包 0.21.1)
48
+
49
+ 上架前置版:一个工具都没改,只改「提交的形状」。三条教训都关于**契约不止一份**。
50
+
51
+ **(1) 当两个官方契约互相矛盾时,先让包同时满足两边,而不是等其中一个放宽。**
52
+ `dsh.bundle.patch` 在 DSH 运行时是有序数组(官方 `dsh-web-app` 自己就列了 5 个文件),但 DSH STORE
53
+ 的提交预检只接受字符串。我们选了不依赖对方先改的解法:合并成一个 `cordis.patch.yml`,两个 insert
54
+ (锚点 + preset)放在同一个文件里,生成器直接产出它。代价是四个读者要跟着换路径——真正危险的不是
55
+ 改路径,而是**锚点行可能在合并时被当成重复内容删掉**:0.15.2 的两个侧栏 tab 就是这么消失的。所以
56
+ `client-mount` 的「每个被选中的包必须有一个可解析的宿主行」没有随合并松动。
57
+
58
+ **(2) 兼容性声明是两件事:安装范围与逐版本验收。**
59
+ `engines.dsh` 的 `>=0.2.1-rc.2 <0.3.0-0` 是粗筛(`-0` 是必要的:带 prerelease 的比较里
60
+ `<0.3.0` 会放行 `0.3.0-alpha.1`);`dsh.compatibility.dshReleases` 才是逐版本的结论。没有跑过的
61
+ 版本只能写 `unknown`,不能因为「补丁号在范围内」就写成 `compatible`——这正是把「范围」误读成
62
+ 「验收」时会发生的事。
63
+
64
+ **(3) 一个 tarball 只能有一个插件清单。**
65
+ 根包曾把 `packages/molbio-panel` 一起打包,于是同一个 tarball 里有两个 `dsh` 清单、两份相同的
66
+ 427 KB `lib/client.js`。删掉 `packages` 不改变任何用户可见行为(面板照样由根包的 `lib/client.js`
67
+ 提供),但「不改变行为」必须能被观察到:一次性 profile 的启动回读里,`window.__DSH_BOOT__` 仍然
68
+ 包含 `dsh-molbio-tools/client.js`,这才是证据;tarball 此时是 115 个文件、约 999 kB。
69
+
46
70
  ## v0.21.0(2026-10-23,包 0.21.0)
47
71
 
48
72
  纯适配版:DSH 0.2.0-rc.2 → 0.2.1-rc.2。工具行为零变化,但上游动了三样本仓库**以为不会动**的东西,