dsh-molbio-tools 0.20.1 → 0.21.0
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 +70 -0
- package/benchmark/README.md +1 -0
- package/benchmark/_freeze-trace.mjs +92 -18
- package/benchmark/tasks/06-analysis.json +2 -1
- package/build/release-preflight.mjs +1 -1
- package/docs/history.md +32 -0
- package/docs/maintainer.md +1 -1
- package/docs/roadmap.md +7 -2
- package/docs/workflow.md +6 -4
- package/package.json +1 -1
- package/preset/molbio-lab/agent.cordis.yml +31 -157
- package/preset/molbio-lab/preset.patch.yml +14 -144
- package/test/contract.mjs +55 -10
- package/test/drift-probe.mjs +14 -5
- package/test/fixtures/benchmark-traces.json +20 -9
- package/test/preset-health.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,76 @@
|
|
|
10
10
|
改为按**包名**引用后模块缓存问题随之消失——没有拷贝,也就没有需要保持同步的版本目录。
|
|
11
11
|
下文历史条目里出现的 `vN` 目录保持原样,作为当时的记录。
|
|
12
12
|
|
|
13
|
+
## [0.21.0] — 2026-10-23(DSH 0.2.1-rc.2 适配;两条 benchmark 欠账清零)
|
|
14
|
+
|
|
15
|
+
**工具数不变(60),插件行为零改动。** 这是一次"只跟上游走"的版本:0.20.1 之后本机 harness
|
|
16
|
+
升到 0.2.1-rc.2,`npm test` 立刻红了三处——组合漂移、契约断言、以及一条依赖旧 fixture 的
|
|
17
|
+
漂移探针。三处都不是插件退化,但每一处都属于**守卫在说谎**,所以按发布阻断处理。
|
|
18
|
+
|
|
19
|
+
### 1. preset 行清单按 0.2.1-rc.2 逐行重抄(12 处结构差异)
|
|
20
|
+
|
|
21
|
+
- 上游 `standard` **新增** `time-context`、`tool-schedule`、`tool-plugin-market`;
|
|
22
|
+
- **删除** `tool-subagent-codex` / `tool-subagent-claude-code`(两个 `disabled: true` 的可选
|
|
23
|
+
provider)与 `tool-ralph`;
|
|
24
|
+
- **去掉** persona 的 `suffix: Your working directory is {{cwd}}.`,以及 `tool-subagent` /
|
|
25
|
+
`tool-subagent-fork` 的 `backgroundMode: continuable`;
|
|
26
|
+
- plan-mode 的段落文案改为 "until the user approves your plan through exit_plan_mode…"。
|
|
27
|
+
|
|
28
|
+
`agent.cordis.yml` 现在是上游 `config.plugins:` 块的**整体去缩进** + 末尾一行 `tool-molbio`;
|
|
29
|
+
0.2.1 的上游文件不带行内注释,因此行内注释随之消失,来源与升级规则集中写在文件头部。
|
|
30
|
+
`preset.patch.yml` 由 `build/preset-patch.mjs` 重新生成。
|
|
31
|
+
|
|
32
|
+
### 2. `test/contract.mjs`:两条断言不再锚在 minified 字面量上
|
|
33
|
+
|
|
34
|
+
- **client 入口**:改为按包自己的 `exports["./client"]`(`browser` 条件)解析。
|
|
35
|
+
0.2.1-rc.2 把 `@deepseek-ai/dsh-client-connection` 移到 `lib/client.browser.js`,旧断言
|
|
36
|
+
硬编码 `lib/client.js`,会把一个加载正常的包判死;同时 Web shell 已是打包产物、不再出现
|
|
37
|
+
`require("包名")`,"seeded into the shell" 兜底只剩代码路径。新增两个对照:没有
|
|
38
|
+
`./client` 导出的包必须解析为 `undefined`,以及移动过的入口必须解析到真实文件。
|
|
39
|
+
- **`presentationMeta`**:0.2.1 删除了 `exec.parent === void 0` 闸门(投影移进
|
|
40
|
+
`createSuccessResult`,对每次成功执行都做)。断言改挂到卡片真正消费的东西:投影被
|
|
41
|
+
`snapshotProjection` 记录、并以 `meta` 附到成功结果上。
|
|
42
|
+
|
|
43
|
+
### 3. `drift-probe`:突变样本换成上游仍存在的行
|
|
44
|
+
|
|
45
|
+
上游删掉 `tool-ralph` 后,那条"上游 `disabled: true` 被丢掉"的突变在**空转**(`withRow`
|
|
46
|
+
找不到目标,什么也没改,守卫却"通过")。改用上游仍以 `disabled: true` 发布的
|
|
47
|
+
`tool-plugin-manager`;14 条突变全部仍能被抓住。
|
|
48
|
+
|
|
49
|
+
### 4. benchmark:profile 重建、两条欠账清零
|
|
50
|
+
|
|
51
|
+
- preset 新增的两行宿主可承载行(`time-context`、`tool-schedule`)进入 headless profile,
|
|
52
|
+
`benchmark/profile.mjs` 重建 `molbio-bench`:**12 行宿主行,仍注册 60 个工具**。
|
|
53
|
+
- `methylation-block` 与 `qpcr-primers` 的冻结响应停在 0.16.0 之前,`benchmark-score` 每次
|
|
54
|
+
都点名。本版用一次完整真实模型跑重录,并用 `_freeze-trace.mjs --replace` **只替换这两条**,
|
|
55
|
+
其余 47 条保持原样;`run.amendments` 记录每条替换来自哪一次跑分。fixture 现为
|
|
56
|
+
**49 条记录、0 条 suspended**。
|
|
57
|
+
- 完整跑分(2026-10-23,DSH 0.2.1-rc.2,61 题):**58/61** 通过,tool selection **60/61**、
|
|
58
|
+
arguments **60/61**、final answer **59/61**;11.2M tokens / 2161 s。
|
|
59
|
+
- `protein-props` 的失败是**断言不认千分位逗号**(答案写 `1,490`,断言写 `/1490/`)——
|
|
60
|
+
与 0.20.0 修过的 `/3935/` 同一类,当时漏了这一条。已改为 `1[,.]?490`,`--replay` 后
|
|
61
|
+
**59/61**。
|
|
62
|
+
- 剩余两条是**真实的模型遗漏**,不是产品缺陷:`dilution-math` 答案正确(50 µL + 950 µL)
|
|
63
|
+
却从未调用 `molbio_lab_math`;`batch-analysis` 写出了 CSV,但最终回答只说"报告已生成",
|
|
64
|
+
没有报出指令要求的长度/特征数。
|
|
65
|
+
- `_freeze-trace.mjs` 新增 `--replace id[,id…]`:只重录被修复作废的条目,其余记录与顺序原样
|
|
66
|
+
保留,并在 `run.amendments` 里写明每条替换的来源跑分——**替换的是真实录制,不是手改**。
|
|
67
|
+
|
|
68
|
+
### 5. 未做 / 边界
|
|
69
|
+
|
|
70
|
+
- **`png_path` 仍然不做**:`contract` 的"工作区仍不能存二进制文件"断言在 0.2.1-rc.2 上**仍然
|
|
71
|
+
通过**,说明上游 fs 缝依旧是 text-only;触发条件未到。
|
|
72
|
+
- **面板版本保持 0.1.3**:`lib/client.js` 逐字节未变,`release-preflight` 复核为
|
|
73
|
+
"already published with identical bytes",因此不 bump。
|
|
74
|
+
|
|
75
|
+
### 6. 版本与验证
|
|
76
|
+
|
|
77
|
+
- 根包 `0.20.1 → 0.21.0`;DSH 基线钉 **0.2.1-rc.2**(`.github/workflows/{test,release}.yml`、
|
|
78
|
+
preset 头注、`docs/workflow.md`、`docs/roadmap.md`、`docs/maintainer.md`)。
|
|
79
|
+
- `npm test` 全绿:contract 16/16、drift-probe 14 条突变、preset-health 30 行全过、release
|
|
80
|
+
13 项、benchmark profile/score/coverage/changed、`bench --offline` 61 题。
|
|
81
|
+
- 文档:roadmap 状态、history 的 v0.21.0 段(三条教训)、workflow 的升级提示同步。
|
|
82
|
+
|
|
13
83
|
## [0.20.1] — 2026-10-18(插件专属图标)
|
|
14
84
|
|
|
15
85
|
**工具数不变(60),运行时零改动;包内只多一个 746 B 的 SVG。**
|
package/benchmark/README.md
CHANGED
|
@@ -159,6 +159,7 @@ benchmark/
|
|
|
159
159
|
├── _probe.mjs record ground truth (original suite)
|
|
160
160
|
├── _probe-all.mjs record ground truth (the rest of the catalog)
|
|
161
161
|
└── _freeze-trace.mjs freeze a run's responses into the scorer's regression fixture
|
|
162
|
+
(`--replace id[,id]` re-records only the named entries and keeps the rest)
|
|
162
163
|
```
|
|
163
164
|
|
|
164
165
|
A task's expected text lives in `tasks/`; the input that produces it lives in
|
|
@@ -12,6 +12,20 @@
|
|
|
12
12
|
* Run this only when you WANT to move the golden fixture forward (for example
|
|
13
13
|
* after deliberately changing a task). Test failures point here when a recorded
|
|
14
14
|
* response stops satisfying its task.
|
|
15
|
+
*
|
|
16
|
+
* ── amending a subset (`--replace`) ─────────────────────────────────────────
|
|
17
|
+
*
|
|
18
|
+
* Re-recording the one or two entries a fix invalidated used to mean freezing a
|
|
19
|
+
* whole new run and discarding every other recording. `--replace` takes just
|
|
20
|
+
* those responses from the new report and keeps the rest, and records where each
|
|
21
|
+
* amended response came from in `run.amendments` — explicit provenance beats a
|
|
22
|
+
* fixture that pretends to be a single run:
|
|
23
|
+
*
|
|
24
|
+
* node benchmark/_freeze-trace.mjs benchmark/reports/<stamp>-suite.json \
|
|
25
|
+
* --replace methylation-block,qpcr-primers
|
|
26
|
+
*
|
|
27
|
+
* The replacement is a REAL recording from the named run, never a hand edit: the
|
|
28
|
+
* original ids, order and every untouched response survive.
|
|
15
29
|
*/
|
|
16
30
|
|
|
17
31
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
@@ -19,7 +33,16 @@ import { resolve } from 'node:path';
|
|
|
19
33
|
|
|
20
34
|
const source = process.argv[2];
|
|
21
35
|
if (source === undefined) {
|
|
22
|
-
console.error('usage: node benchmark/_freeze-trace.mjs <report.json>');
|
|
36
|
+
console.error('usage: node benchmark/_freeze-trace.mjs <report.json> [--replace id[,id…]]');
|
|
37
|
+
process.exit(2);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const replaceAt = process.argv.indexOf('--replace');
|
|
41
|
+
const replaceIds = replaceAt === -1
|
|
42
|
+
? undefined
|
|
43
|
+
: (process.argv[replaceAt + 1] ?? '').split(',').map((id) => id.trim()).filter((id) => id !== '');
|
|
44
|
+
if (replaceAt !== -1 && (replaceIds === undefined || replaceIds.length === 0)) {
|
|
45
|
+
console.error('--replace needs a comma-separated list of task ids');
|
|
23
46
|
process.exit(2);
|
|
24
47
|
}
|
|
25
48
|
|
|
@@ -29,23 +52,74 @@ if (!Array.isArray(report.results) || report.results.length === 0) {
|
|
|
29
52
|
process.exit(1);
|
|
30
53
|
}
|
|
31
54
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
harness_version: report.harnessVersion,
|
|
38
|
-
profile: report.profile,
|
|
39
|
-
model: report.model,
|
|
40
|
-
summary: { total: report.summary.total, passed: report.summary.passed },
|
|
41
|
-
responses: report.results.map((result) => ({
|
|
42
|
-
id: result.id,
|
|
43
|
-
tool_calls: result.toolCalls,
|
|
44
|
-
answer: result.answer,
|
|
45
|
-
})),
|
|
46
|
-
},
|
|
47
|
-
};
|
|
55
|
+
const responses = report.results.map((result) => ({
|
|
56
|
+
id: result.id,
|
|
57
|
+
tool_calls: result.toolCalls,
|
|
58
|
+
answer: result.answer,
|
|
59
|
+
}));
|
|
48
60
|
|
|
49
61
|
const target = resolve(import.meta.dirname, '..', 'test', 'fixtures', 'benchmark-traces.json');
|
|
62
|
+
|
|
63
|
+
/** The whole-fixture shape: one run, every response from it. */
|
|
64
|
+
function wholeRun() {
|
|
65
|
+
return {
|
|
66
|
+
$comment:
|
|
67
|
+
'Real responses from one benchmark run, frozen so the scorer can be regression-tested without spending API budget. See test/benchmark-score.mjs for why each defect these cover was invisible to synthetic traces.',
|
|
68
|
+
run: {
|
|
69
|
+
recorded_at: report.startedAt,
|
|
70
|
+
harness_version: report.harnessVersion,
|
|
71
|
+
profile: report.profile,
|
|
72
|
+
model: report.model,
|
|
73
|
+
summary: { total: report.summary.total, passed: report.summary.passed },
|
|
74
|
+
responses,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The amended shape: keep the fixture, swap the named entries, say so. */
|
|
80
|
+
async function amendedRun() {
|
|
81
|
+
const existing = JSON.parse(await readFile(target, 'utf8'));
|
|
82
|
+
const byId = new Map(existing.run.responses.map((response) => [response.id, response]));
|
|
83
|
+
// Replacing the same id twice must not pile up amendment entries or repeat
|
|
84
|
+
// the provenance sentence: this command is re-run whenever a report is rebuilt.
|
|
85
|
+
const amendments = (existing.run.amendments ?? []).filter((entry) => !replaceIds.includes(entry.id));
|
|
86
|
+
for (const id of replaceIds) {
|
|
87
|
+
const fresh = responses.find((response) => response.id === id);
|
|
88
|
+
if (fresh === undefined) {
|
|
89
|
+
console.error(`--replace: the report has no result for "${id}" — nothing was written`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
if (!byId.has(id)) console.log(` note: "${id}" was not in the fixture; appending it`);
|
|
93
|
+
byId.set(id, fresh);
|
|
94
|
+
amendments.push({
|
|
95
|
+
id,
|
|
96
|
+
recorded_at: report.startedAt,
|
|
97
|
+
harness_version: report.harnessVersion,
|
|
98
|
+
model: report.model,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const ordered = existing.run.responses.map((response) => byId.get(response.id));
|
|
103
|
+
for (const id of replaceIds) {
|
|
104
|
+
if (!existing.run.responses.some((response) => response.id === id)) ordered.push(byId.get(id));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
$comment: existing.$comment.includes('run.amendments names each one')
|
|
109
|
+
? existing.$comment
|
|
110
|
+
: `${existing.$comment} Some responses were re-recorded later; run.amendments names each one and the run it came from, and every other response is unchanged. A replaced entry loses any suspension marker it carried, because the replacement is a fresh recording.`,
|
|
111
|
+
run: {
|
|
112
|
+
...existing.run,
|
|
113
|
+
amendments,
|
|
114
|
+
responses: ordered,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const out = replaceIds === undefined ? wholeRun() : await amendedRun();
|
|
50
120
|
await writeFile(target, `${JSON.stringify(out, null, 2)}\n`, 'utf8');
|
|
51
|
-
console.log(
|
|
121
|
+
console.log(
|
|
122
|
+
replaceIds === undefined
|
|
123
|
+
? `froze ${out.run.responses.length} response(s) into ${target}`
|
|
124
|
+
: `amended ${replaceIds.length} response(s) (${replaceIds.join(', ')}) in ${target}; ${out.run.responses.length} total`,
|
|
125
|
+
);
|
|
@@ -201,7 +201,7 @@ export function tagVersion(tag) {
|
|
|
201
201
|
* `rc.10 < rc.2`, so a release of `rc.2` after `rc.10` would look like a
|
|
202
202
|
* DOWNGRADE and the gate would refuse a legitimate publish (or, in the other
|
|
203
203
|
* direction, wave through a stale one). This repository does cut prereleases
|
|
204
|
-
* (the harness baseline itself is `0.2.
|
|
204
|
+
* (the harness baseline itself is `0.2.1-rc.2`), so the rule is implemented
|
|
205
205
|
* rather than approximated.
|
|
206
206
|
*
|
|
207
207
|
* Semver's rule: split on dots; numeric identifiers compare numerically and rank
|
package/docs/history.md
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
| 包版本 | preset 版本 | 内容 | 工具数 |
|
|
18
18
|
| --- | --- | --- | --- |
|
|
19
|
+
| 0.21.0 | — | **DSH 0.2.1-rc.2 适配**:preset 行清单逐行重抄;contract 两条断言重锚到行为;两条 suspended 记录用真实模型重录 | 60 |
|
|
20
|
+
| 0.20.1 | — | 插件专属图标(透明底双螺旋 SVG),运行时零改动 | 60 |
|
|
19
21
|
| 0.20.0 | — | **Cas 家族几何**(SpCas9/SaCas9/Cas12a/Cas13a/b/d)+ 反向链脱靶定位修复;TaqMan MGB/订购 CSV;multiplex 退火温度/浓度;批量分析 CSV;静默位点突变;并入 0.19.1 硬化 | 60 |
|
|
20
22
|
| 0.19.0 | — | 环形/扇形的**旋转多行标签**(画面正确性);新增 `molbio_alignment_postprocess`(共识/修剪/同一性矩阵/覆盖度) | 58 |
|
|
21
23
|
| 0.16.0 | — | **修掉 benchmark 交出的前两条缺陷**:引物朝向、甲基化参考表(各带串联/来源回归) | 57 |
|
|
@@ -41,6 +43,36 @@
|
|
|
41
43
|
|
|
42
44
|
---
|
|
43
45
|
|
|
46
|
+
## v0.21.0(2026-10-23,包 0.21.0)
|
|
47
|
+
|
|
48
|
+
纯适配版:DSH 0.2.0-rc.2 → 0.2.1-rc.2。工具行为零变化,但上游动了三样本仓库**以为不会动**的东西,
|
|
49
|
+
每条教训对应一道守卫。
|
|
50
|
+
|
|
51
|
+
**(1) 上游可以在 patch 级版本里重写 preset 行清单。**
|
|
52
|
+
0.2.1-rc.2 的 `standard` 比 0.2.0-rc.2 少了 `tool-subagent-codex` / `tool-subagent-claude-code` /
|
|
53
|
+
`tool-ralph`,少了 persona 的 `suffix` 与两处 `backgroundMode`,多了 `time-context` /
|
|
54
|
+
`tool-schedule` / `tool-plugin-market`,还改了 plan-mode 的文案——12 处结构差异。`preset-health`
|
|
55
|
+
把这些当**发布阻断**,所以第一分钟就红了;但 `drift-probe` 里那条"上游 `disabled` 被丢掉"的
|
|
56
|
+
突变仍拿 `tool-ralph` 当样本,**守卫自己**先失效(上游已经没有这一行)。教训:**fixture 也要
|
|
57
|
+
跟着上游走**——突变样本必须取自当前上游仍然存在、且仍为 `disabled` 的行,否则"证明了守卫会失败"
|
|
58
|
+
会变成空话。
|
|
59
|
+
|
|
60
|
+
**(2) 把 contract 断言锚在 minified 字面量上,会把"上游改了写法"报成"产品坏了"。**
|
|
61
|
+
`dsh-tools` 的 `exec.parent === void 0` 在 0.2.1 消失:`presentationMeta` 的投影移进
|
|
62
|
+
`createSuccessResult`,不再按父调用过滤。面板依赖的东西(`meta` 落到成功结果上)没变,变的
|
|
63
|
+
是我们测量它的方式。同源的第二条:`@deepseek-ai/dsh-client-connection` 的 client 入口从
|
|
64
|
+
`lib/client.js` 改成条件导出 `lib/client.browser.js`,而 Web shell 变成打包产物、不再有
|
|
65
|
+
`require("包名")` 字样——"seeded into the shell"这条兜底判据因此整体失效。教训:断言要问**行为**
|
|
66
|
+
(入口文件存在、`meta` 附在结果上),不要问上游当前的拼写;文件名与导出形状应当**读包清单**,
|
|
67
|
+
而不是猜。
|
|
68
|
+
|
|
69
|
+
**(3) 两条欠账不是"记着",是"花一次 token 消掉"。**
|
|
70
|
+
`methylation-block` 与 `qpcr-primers` 的冻结响应停在 0.16.0 之前,`benchmark-score` 每次运行
|
|
71
|
+
都点名它们。这一版用一次完整真实模型跑(61 题)重录,并以 `_freeze-trace --replace` 只替换这两条
|
|
72
|
+
(其余 47 条不动,`run.amendments` 记下来源),悬挂条目归零;这次跑分同时作为 0.21.0 的发布证据。
|
|
73
|
+
**fs 侧仍然不能写二进制**,所以 `png_path` 依旧不做——`contract` 里那条断言仍然是绿的,
|
|
74
|
+
这就是"触发条件没到"的证据。
|
|
75
|
+
|
|
44
76
|
## v0.20.0(2026-10-17,包 0.20.0)
|
|
45
77
|
|
|
46
78
|
这一版把 0.19.1 的四项硬化并入 0.20.0,并落地 2.3 / 2.5 / 2.7 / 2.8 与 survey #8。**最重要的教训是
|
package/docs/maintainer.md
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
- **包**:`dsh-molbio-tools`,零依赖 DSH 插件包,**60 个 `molbio_*` 工具**(其中
|
|
34
34
|
`molbio_plot` 一个工具承载 8 种图表)。
|
|
35
|
-
- **基线**:DSH **0.2.
|
|
35
|
+
- **基线**:DSH **0.2.1-rc.2**;`package.json` 版本 0.21.0。
|
|
36
36
|
- **两条分发渠道**:根包(工具 + 面板)与 `packages/molbio-panel`(只面板),
|
|
37
37
|
**两个独立的 npm 条目、两条独立的版本线**,必须分别发布。
|
|
38
38
|
- **工具只在专属模式里出现**(设计如此):bundle 的 `cordis.patch.yml` 是**空列表**,
|
package/docs/roadmap.md
CHANGED
|
@@ -10,12 +10,12 @@ L = 需要新机制或外部资源)。
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
## 1. 当前状态(0.
|
|
13
|
+
## 1. 当前状态(0.21.0)
|
|
14
14
|
|
|
15
15
|
- 60 个 `molbio_*` 工具,零依赖,随 preset 分发(见 [README](../README.md))。
|
|
16
16
|
- 离线测试金字塔 + 组合漂移守卫全绿;`benchmark/` 提供**模型使用**维度的评估
|
|
17
17
|
(见 [benchmark/README.md](../benchmark/README.md))。
|
|
18
|
-
- 基线:DSH **0.2.
|
|
18
|
+
- 基线:DSH **0.2.1-rc.2**。
|
|
19
19
|
- **CI 已在位**(`.github/workflows/test.yml`,0.18.2 起):push / PR / 手动都跑全套;harness 装
|
|
20
20
|
**钉死版本**,并用 `npm prefix -g` 显式导出 `DSH_HARNESS_ROOT`。0.18.4 起**同时跑
|
|
21
21
|
`ubuntu-latest` 与 `windows-latest`**——3.6 已修,套件不再依赖宿主分隔符。
|
|
@@ -27,6 +27,11 @@ L = 需要新机制或外部资源)。
|
|
|
27
27
|
`molbio_alignment_postprocess`(共识/修剪/同一性矩阵/覆盖度)。
|
|
28
28
|
- **2.3 / 2.5 / 2.7 / 2.8 与 survey #8 已在 0.20.0 交付**:批量分析 CSV、Cas12a/Cas13 家族几何
|
|
29
29
|
(含反向链脱靶定位修复)、multiplex 退火温度/浓度建议、TaqMan MGB/订购 CSV、静默位点突变。
|
|
30
|
+
- **0.21.0 是纯适配版**:基线 0.2.0-rc.2 → 0.2.1-rc.2。preset 行清单按上游**逐行重抄**
|
|
31
|
+
(+`time-context` / `tool-schedule` / `tool-plugin-market`;−两个可选 subagent provider 与
|
|
32
|
+
`tool-ralph`;去掉 persona `suffix` 与两处 `backgroundMode`),`contract` 两条断言从上游
|
|
33
|
+
minified 字面量改挂到**行为**,两条 suspended 的 benchmark 记录用真实模型重录。
|
|
34
|
+
工具数与插件行为零变化。
|
|
30
35
|
|
|
31
36
|
---
|
|
32
37
|
|
package/docs/workflow.md
CHANGED
|
@@ -425,7 +425,7 @@ git status --short # lib/client.js 与 packages/molbio-panel/lib/clien
|
|
|
425
425
|
`standard`** 逐行对比——0.1.7-alpha.1 起上游基线是
|
|
426
426
|
`<harness>/node_modules/@deepseek-ai/dsh-web-app/presets/standard.patch.yml`
|
|
427
427
|
(旧的 `@deepseek-ai/dsh-agent-presets/presets/standard/agent.cordis.yml` 已随该包一并消失)。
|
|
428
|
-
只应剩 `skill-filesystem
|
|
428
|
+
只应剩 `tool-molbio` 这一行差异(0.2.1-rc.2 起上游自己也带 `skill-filesystem` 与 `tool-skill`)。
|
|
429
429
|
|
|
430
430
|
客户端半的改动还有两条**专门针对"会弄坏 GUI"**的确认:
|
|
431
431
|
|
|
@@ -456,15 +456,17 @@ git status --short # lib/client.js 与 packages/molbio-panel/lib/clien
|
|
|
456
456
|
### DSH 升级后:preset 组合的维护(必做)
|
|
457
457
|
|
|
458
458
|
`preset/molbio-lab/agent.cordis.yml` 是官方 `standard` 预设的**行清单**副本 + 末尾一行
|
|
459
|
-
`tool-molbio`(当前基线:**dsh 0.2.
|
|
459
|
+
`tool-molbio`(当前基线:**dsh 0.2.1-rc.2**)。它不会自动跟随 DSH 升级,因此每次升级
|
|
460
460
|
DSH 后:
|
|
461
461
|
|
|
462
462
|
1. 取新版的 shipped `standard`:
|
|
463
463
|
`<harness>/node_modules/@deepseek-ai/dsh-web-app/presets/standard.patch.yml`;
|
|
464
464
|
2. 与 `preset/molbio-lab/agent.cordis.yml` 对比(把上游的 `config.plugins:` 块与我们的行清单
|
|
465
465
|
对齐看),**逐行吸收上游改动**(新增/删除的行、注释、key 顺序、配置契约变化),只保留
|
|
466
|
-
`
|
|
467
|
-
|
|
466
|
+
`tool-molbio` 这一处有意差异与头部注释。**照抄上游文本,不要手写"看起来等价"的行**——
|
|
467
|
+
上游在 0.1.6-alpha.1 用 `workflow-worker-thread` 教过一次。上游这份文件**不一定带行内
|
|
468
|
+
注释**(0.2.1-rc.2 就是纯行清单):照抄即可,别把我们自己的解释混进去;`preset-health`
|
|
469
|
+
只比结构,注释漂移抓不住,但下一次人工 diff 会;
|
|
468
470
|
3. 跑 `node build/preset-patch.mjs` 重新生成 `preset/molbio-lab/preset.patch.yml`;
|
|
469
471
|
4. 跑 `node test/preset-health.mjs` 直到 `OK`(它把任何结构差异当**发布阻断**,不只是打印
|
|
470
472
|
note),再跑 `node test/drift-probe.mjs` 确认守卫仍能抓到漂移;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-molbio-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Zero-dependency molecular-biology plugin bundle for the DeepSeek Harness: sequence analysis, primer design (incl. cross-intron qPCR primers, TaqMan hydrolysis/MGB probes with ordering CSV and multiplex compatibility), in-silico PCR, cloning simulation, SnapGene .dna parsing and plasmid maps, Sanger verification, protein tools (incl. helical wheel and hydropathy plots), methylation-aware restriction analysis with double-digest buffer checks, qPCR analysis and plotting, multiple sequence alignment that conserves every input residue, conservation analysis, sequence logos, codon-usage analysis, phylogenetic trees with bootstrap support, Newick I/O and long-name label wrapping, CpG-island/GC-skew composition analysis, read-level FASTQ QC, batch file analysis with CSV export, silent restriction-site mutagenesis, family-aware CRISPR gRNA design (SpCas9/SaCas9/Cas12a/Cas13) with off-target search, an opt-in hand-off that shows a generated plot to the model as an attached image, plus a browser-side right-sidebar panel (plasmid maps and sequence logos drawn in the Web GUI) and a literature/protocol/experiment record system.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
@@ -41,16 +41,24 @@
|
|
|
41
41
|
#
|
|
42
42
|
# ── How this file is maintained ─────────────────────────────────────────────
|
|
43
43
|
#
|
|
44
|
-
# This row list is a COPY of the DSH-shipped `standard` preset with
|
|
45
|
-
# intentional
|
|
46
|
-
#
|
|
47
|
-
# config
|
|
48
|
-
#
|
|
49
|
-
#
|
|
44
|
+
# This row list is a COPY of the DSH-shipped `standard` preset with ONE
|
|
45
|
+
# intentional difference: the trailing `tool-molbio` row. Everything else —
|
|
46
|
+
# every row, every config key, in upstream's ORDER — is upstream's
|
|
47
|
+
# `config.plugins:` block, de-indented by ten spaces. This release's upstream
|
|
48
|
+
# list carries no comments of its own, so none appear below; provenance and the
|
|
49
|
+
# upgrade rules live in this header.
|
|
50
|
+
#
|
|
51
|
+
# Baseline: the `standard` preset of dsh 0.2.1-rc.2 (harness root
|
|
50
52
|
# `%APPDATA%\npm\node_modules\@deepseek-ai\dsh`, file
|
|
51
53
|
# `node_modules/@deepseek-ai/dsh-web-app/presets/standard.patch.yml`, whose
|
|
52
|
-
# rows live under the `preset-standard` row's `config.plugins:`).
|
|
53
|
-
#
|
|
54
|
+
# rows live under the `preset-standard` row's `config.plugins:`).
|
|
55
|
+
#
|
|
56
|
+
# 0.2.1-rc.2 is the first bump since 0.1.7-alpha.1 that changed the row list: it
|
|
57
|
+
# added `time-context`, `tool-schedule` and `tool-plugin-market`; dropped the two
|
|
58
|
+
# disabled optional subagent providers (`tool-subagent-codex`,
|
|
59
|
+
# `tool-subagent-claude-code`) and `tool-ralph`; dropped the persona `suffix` and
|
|
60
|
+
# both `backgroundMode: continuable` settings; and reworded the plan-mode
|
|
61
|
+
# section. Absorb such a bump verbatim — see workflow.md §4.
|
|
54
62
|
#
|
|
55
63
|
# Keeping the text verbatim is not cosmetic: a preset mounts DSH's OWN
|
|
56
64
|
# packages, so upstream drift here kills the whole preset at mount (the failure
|
|
@@ -66,98 +74,47 @@
|
|
|
66
74
|
# for the right reason;
|
|
67
75
|
# 2. re-run `node build/preset-patch.mjs`, then `node test/preset-health.mjs`,
|
|
68
76
|
# which validates every row's config against the INSTALLED package's own
|
|
69
|
-
# schema and reports drift; only the allowed
|
|
77
|
+
# schema and reports drift; only the allowed difference above may remain;
|
|
70
78
|
# 3. `node test/drift-probe.mjs` must still catch its mutations — a guard
|
|
71
79
|
# nobody has seen fail is not a guard.
|
|
72
80
|
#
|
|
73
81
|
# Do NOT "fix" `present` (or any other row) by hand to suit the toolset: if the
|
|
74
82
|
# upstream row is wrong for this preset, fix it upstream, not here.
|
|
75
83
|
|
|
76
|
-
# ── identity ────────────────────────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
# The preset's own persona, shadowing the deployment default for this agent.
|
|
79
|
-
# `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
|
|
80
84
|
- id: persona
|
|
81
85
|
name: '@deepseek-ai/dsh-persona'
|
|
82
86
|
config:
|
|
83
|
-
|
|
84
|
-
prefix: >-
|
|
85
|
-
You are a coding agent powered by the {{model}} model.
|
|
86
|
-
|
|
87
|
+
prefix: You are a coding agent powered by the {{model}} model.
|
|
87
88
|
- id: agent-instructions
|
|
88
89
|
name: '@deepseek-ai/dsh-agent-instructions'
|
|
89
90
|
config:
|
|
90
91
|
maxBytes: 65536
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
|
|
95
|
-
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
|
|
96
|
-
# the criterion for host-plane ownership — injection resolves before any session
|
|
97
|
-
# exists, so there is no agent to key by. Behind a preset realm those variables
|
|
98
|
-
# never reached the model's shell at all. Both shell tools consume the host
|
|
99
|
-
# registry from here; their executors (`bash-sandbox`/`pwsh-sandbox`) are
|
|
100
|
-
# host-plane too.
|
|
92
|
+
- id: time-context
|
|
93
|
+
name: '@deepseek-ai/dsh-time-context'
|
|
101
94
|
- id: tool-bash
|
|
102
95
|
name: '@deepseek-ai/dsh-tool-bash'
|
|
103
96
|
disabled: !!js process.platform === 'win32'
|
|
104
|
-
|
|
105
97
|
- id: tool-pwsh
|
|
106
98
|
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
107
99
|
disabled: !!js process.platform !== 'win32'
|
|
108
|
-
|
|
109
|
-
# ── filesystem ──────────────────────────────────────────────────────────────
|
|
110
|
-
|
|
111
|
-
# Both register into the host `tools` registry and provide nothing, so
|
|
112
|
-
# they need no realm. The `fs` service and its policy stay in the host.
|
|
113
100
|
- id: tool-fs
|
|
114
101
|
name: '@deepseek-ai/dsh-tool-fs'
|
|
115
|
-
|
|
116
102
|
- id: tool-fs-search
|
|
117
103
|
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
118
104
|
config:
|
|
119
105
|
sampleOverCapGlobResults: false
|
|
120
|
-
|
|
121
|
-
# ── background jobs ────────────────────────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
# Only the model-facing controls. The task REGISTRY stays on the host plane:
|
|
124
|
-
# its producers sit outside any realm this file could put it in — `tool-bash`
|
|
125
|
-
# above resolves it with `ctx.get`, and an entry-local realm here is invisible
|
|
126
|
-
# to every sibling row, so `run_in_background` would answer "background jobs
|
|
127
|
-
# unavailable" while these controls sat in the catalog. The registry is keyed by
|
|
128
|
-
# owning agent anyway, so one host instance serves every session. What a preset
|
|
129
|
-
# chooses is whether its agent can collect and stop background work at all.
|
|
130
106
|
- id: tool-jobs
|
|
131
107
|
name: '@deepseek-ai/dsh-tool-jobs'
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# The skill REGISTRY lives in the host composition and is layered per scope:
|
|
136
|
-
# these rows register into THIS preset's layer of it, so they need no realm.
|
|
137
|
-
# `skill-filesystem` contributes local-root discovery for agents on this preset, and
|
|
138
|
-
# `tool-skill` gives them the catalog and loader; the merged catalog also
|
|
139
|
-
# carries whatever the deployment registered globally (repository plugins).
|
|
108
|
+
- id: tool-schedule
|
|
109
|
+
name: '@deepseek-ai/dsh-tool-schedule'
|
|
140
110
|
- id: skill-filesystem
|
|
141
111
|
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
142
|
-
|
|
143
112
|
- id: tool-skill
|
|
144
113
|
name: '@deepseek-ai/dsh-tool-skill'
|
|
145
|
-
|
|
146
|
-
# ── goals ───────────────────────────────────────────────────────────────────
|
|
147
|
-
|
|
148
|
-
# The goal service and session driver stay on the host plane, where the Gateway
|
|
149
|
-
# can resolve them. The human command and model-facing tool register into this
|
|
150
|
-
# preset's scoped layers.
|
|
151
114
|
- id: command-goal
|
|
152
115
|
name: '@deepseek-ai/dsh-command-goal'
|
|
153
|
-
|
|
154
116
|
- id: tool-goal
|
|
155
117
|
name: '@deepseek-ai/dsh-tool-goal'
|
|
156
|
-
|
|
157
|
-
# ── plan mode ───────────────────────────────────────────────────────────────
|
|
158
|
-
|
|
159
|
-
# Plan state is per-agent by nature, so an entry-local realm is not a
|
|
160
|
-
# workaround here — it is the correct lifetime.
|
|
161
118
|
- id: planning
|
|
162
119
|
name: cordis:group
|
|
163
120
|
group: true
|
|
@@ -168,29 +125,17 @@
|
|
|
168
125
|
name: '@deepseek-ai/dsh-plan-mode'
|
|
169
126
|
config:
|
|
170
127
|
section: |
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
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.
|
|
128
|
+
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.
|
|
174
129
|
|
|
175
|
-
|
|
130
|
+
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.
|
|
176
131
|
|
|
177
|
-
|
|
132
|
+
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.
|
|
178
133
|
|
|
179
|
-
|
|
134
|
+
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.
|
|
180
135
|
|
|
181
|
-
|
|
136
|
+
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.
|
|
182
137
|
|
|
183
|
-
#
|
|
184
|
-
|
|
185
|
-
# `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
|
|
186
|
-
# share this realm rather than sit outside it.
|
|
187
|
-
#
|
|
188
|
-
# `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
|
|
189
|
-
# plane, and the rows here resolve that one instance. It takes no configuration,
|
|
190
|
-
# keys every fold by Session, and owns the context-meter projection units the
|
|
191
|
-
# browser reads for every session — behind a realm those units would come and go
|
|
192
|
-
# with whichever presets happen to be mounted. What a preset chooses is whether
|
|
193
|
-
# its agent compacts at all, which is `compaction-basic` below.
|
|
138
|
+
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.
|
|
194
139
|
- id: compaction
|
|
195
140
|
name: cordis:group
|
|
196
141
|
group: true
|
|
@@ -200,28 +145,14 @@
|
|
|
200
145
|
config:
|
|
201
146
|
- id: compaction-basic
|
|
202
147
|
name: '@deepseek-ai/dsh-compaction-basic'
|
|
203
|
-
|
|
204
148
|
- id: command-compact
|
|
205
149
|
name: '@deepseek-ai/dsh-command-compact'
|
|
206
|
-
|
|
207
150
|
- id: tool-result-pruner
|
|
208
151
|
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
209
152
|
config:
|
|
210
153
|
thresholdChars: 8192
|
|
211
154
|
headChars: 4096
|
|
212
155
|
tailChars: 1024
|
|
213
|
-
|
|
214
|
-
# ── delegation and workflows ────────────────────────────────────────────────
|
|
215
|
-
|
|
216
|
-
# The `subagents` registry and its spawn/fork backends live in the HOST
|
|
217
|
-
# composition: the registry is a process singleton whose cross-session queries
|
|
218
|
-
# the api-proxy serves to the browser, and a provider name may only be
|
|
219
|
-
# registered once. This preset contributes the delegation TOOLS, which resolve
|
|
220
|
-
# that host registry.
|
|
221
|
-
#
|
|
222
|
-
# `workflows` is different — nothing outside an agent reads it — so every row
|
|
223
|
-
# that reaches it shares one entry-local realm here, and a consumer left
|
|
224
|
-
# outside would resolve a host registry this preset does not populate.
|
|
225
156
|
- id: delegation
|
|
226
157
|
name: cordis:group
|
|
227
158
|
group: true
|
|
@@ -230,101 +161,44 @@
|
|
|
230
161
|
config:
|
|
231
162
|
- id: tool-subagent-control
|
|
232
163
|
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
233
|
-
|
|
234
164
|
- id: tool-subagent-list-agents
|
|
235
165
|
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
236
|
-
|
|
237
166
|
- id: tool-subagent
|
|
238
167
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
239
168
|
config:
|
|
240
169
|
provider: spawn
|
|
241
170
|
toolName: subagent
|
|
242
171
|
modelSelectionSettings: true
|
|
243
|
-
backgroundMode: continuable
|
|
244
|
-
|
|
245
|
-
# Fork omits model selection so provider/model stay equal to the parent and
|
|
246
|
-
# the inherited history remains eligible for KV Cache reuse. This preset
|
|
247
|
-
# keeps fork continuable; parent and child inherit the same messaging tool,
|
|
248
|
-
# while the parent id and return guidance follow the inherited history.
|
|
249
172
|
- id: tool-subagent-fork
|
|
250
173
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
251
174
|
config:
|
|
252
175
|
provider: fork
|
|
253
176
|
toolName: subagent_fork
|
|
254
|
-
backgroundMode: continuable
|
|
255
|
-
|
|
256
|
-
# Production dsh does not install these optional providers. Install the
|
|
257
|
-
# matching Bundle in this Profile and restart the Host, then copy this
|
|
258
|
-
# preset and remove `disabled` from the matching tool row. Host availability
|
|
259
|
-
# alone grants no tool.
|
|
260
|
-
- id: tool-subagent-codex
|
|
261
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
262
|
-
disabled: true
|
|
263
|
-
config:
|
|
264
|
-
provider: codex
|
|
265
|
-
toolName: subagent_codex
|
|
266
|
-
backgroundMode: one-shot
|
|
267
|
-
maxDepth: provider-managed
|
|
268
|
-
|
|
269
|
-
- id: tool-subagent-claude-code
|
|
270
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
271
|
-
disabled: true
|
|
272
|
-
config:
|
|
273
|
-
provider: claude-code
|
|
274
|
-
toolName: subagent_claude_code
|
|
275
|
-
backgroundMode: one-shot
|
|
276
|
-
maxDepth: provider-managed
|
|
277
|
-
|
|
278
177
|
- id: workflow-ptc
|
|
279
178
|
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
280
179
|
config:
|
|
281
180
|
provider: spawn
|
|
282
|
-
|
|
283
181
|
- id: tool-workflow
|
|
284
182
|
name: '@deepseek-ai/dsh-tool-workflow'
|
|
285
|
-
|
|
286
|
-
# Off by default with the base row it mirrors: the tool description
|
|
287
|
-
# restricts `ralph` to runs the human explicitly asked for, and completion
|
|
288
|
-
# is a worker self-report, not an independent evaluation. Preset files take
|
|
289
|
-
# no patches, so a session that wants it duplicates this preset under a new
|
|
290
|
-
# id and drops `disabled`; the shipped root shadows a copy that reuses this
|
|
291
|
-
# id.
|
|
292
|
-
- id: tool-ralph
|
|
293
|
-
name: '@deepseek-ai/dsh-tool-ralph'
|
|
294
|
-
disabled: true
|
|
295
|
-
config:
|
|
296
|
-
subagentProvider: spawn
|
|
297
|
-
maxRounds: 64
|
|
298
|
-
|
|
299
|
-
# ── remaining model-facing rows ─────────────────────────────────────────────
|
|
300
|
-
|
|
301
183
|
- id: tool-ask-user
|
|
302
184
|
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
303
|
-
|
|
304
185
|
- id: tool-todo
|
|
305
186
|
name: '@deepseek-ai/dsh-tool-todo'
|
|
306
187
|
config:
|
|
307
188
|
allowParallelInProgress: true
|
|
308
|
-
|
|
309
|
-
# The `web` service and its search provider stay in the host composition; only
|
|
310
|
-
# the model-facing tool is per-session.
|
|
311
189
|
- id: tool-web
|
|
312
190
|
name: '@deepseek-ai/dsh-tool-web'
|
|
313
191
|
config:
|
|
314
192
|
fetch: true
|
|
315
193
|
searchTimeoutMs: 60000
|
|
316
|
-
|
|
317
|
-
# Explicit workspace file delivery. Carried by every shipped preset that is not
|
|
318
|
-
# the fixed two-tool `minimal` training configuration, so this preset keeps the
|
|
319
|
-
# row: without it the agent cannot declare its generated maps/plots/gels as
|
|
320
|
-
# deliverables, which is most of what this toolset produces. It consumes only
|
|
321
|
-
# host-plane services (fs/session/tools) and publishes none.
|
|
322
194
|
- id: present
|
|
323
195
|
name: '@deepseek-ai/dsh-tool-present'
|
|
324
|
-
|
|
325
196
|
- id: tool-plugin-manager
|
|
326
197
|
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
327
198
|
disabled: true
|
|
199
|
+
- id: tool-plugin-market
|
|
200
|
+
name: '@deepseek-ai/dsh-plugin-market/tools'
|
|
201
|
+
disabled: true
|
|
328
202
|
|
|
329
203
|
# dsh-molbio-tools: molecular-biology toolset (60 tools across sequence
|
|
330
204
|
# analysis, primers (incl. TaqMan probes and multiplex compatibility),
|
|
@@ -26,84 +26,37 @@
|
|
|
26
26
|
- id: persona
|
|
27
27
|
name: '@deepseek-ai/dsh-persona'
|
|
28
28
|
config:
|
|
29
|
-
|
|
30
|
-
prefix: >-
|
|
31
|
-
You are a coding agent powered by the {{model}} model.
|
|
32
|
-
|
|
29
|
+
prefix: You are a coding agent powered by the {{model}} model.
|
|
33
30
|
- id: agent-instructions
|
|
34
31
|
name: '@deepseek-ai/dsh-agent-instructions'
|
|
35
32
|
config:
|
|
36
33
|
maxBytes: 65536
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
|
|
41
|
-
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
|
|
42
|
-
# the criterion for host-plane ownership — injection resolves before any session
|
|
43
|
-
# exists, so there is no agent to key by. Behind a preset realm those variables
|
|
44
|
-
# never reached the model's shell at all. Both shell tools consume the host
|
|
45
|
-
# registry from here; their executors (`bash-sandbox`/`pwsh-sandbox`) are
|
|
46
|
-
# host-plane too.
|
|
34
|
+
- id: time-context
|
|
35
|
+
name: '@deepseek-ai/dsh-time-context'
|
|
47
36
|
- id: tool-bash
|
|
48
37
|
name: '@deepseek-ai/dsh-tool-bash'
|
|
49
38
|
disabled: !!js process.platform === 'win32'
|
|
50
|
-
|
|
51
39
|
- id: tool-pwsh
|
|
52
40
|
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
53
41
|
disabled: !!js process.platform !== 'win32'
|
|
54
|
-
|
|
55
|
-
# ── filesystem ──────────────────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
# Both register into the host `tools` registry and provide nothing, so
|
|
58
|
-
# they need no realm. The `fs` service and its policy stay in the host.
|
|
59
42
|
- id: tool-fs
|
|
60
43
|
name: '@deepseek-ai/dsh-tool-fs'
|
|
61
|
-
|
|
62
44
|
- id: tool-fs-search
|
|
63
45
|
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
64
46
|
config:
|
|
65
47
|
sampleOverCapGlobResults: false
|
|
66
|
-
|
|
67
|
-
# ── background jobs ────────────────────────────────────────────────────────
|
|
68
|
-
|
|
69
|
-
# Only the model-facing controls. The task REGISTRY stays on the host plane:
|
|
70
|
-
# its producers sit outside any realm this file could put it in — `tool-bash`
|
|
71
|
-
# above resolves it with `ctx.get`, and an entry-local realm here is invisible
|
|
72
|
-
# to every sibling row, so `run_in_background` would answer "background jobs
|
|
73
|
-
# unavailable" while these controls sat in the catalog. The registry is keyed by
|
|
74
|
-
# owning agent anyway, so one host instance serves every session. What a preset
|
|
75
|
-
# chooses is whether its agent can collect and stop background work at all.
|
|
76
48
|
- id: tool-jobs
|
|
77
49
|
name: '@deepseek-ai/dsh-tool-jobs'
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
# The skill REGISTRY lives in the host composition and is layered per scope:
|
|
82
|
-
# these rows register into THIS preset's layer of it, so they need no realm.
|
|
83
|
-
# `skill-filesystem` contributes local-root discovery for agents on this preset, and
|
|
84
|
-
# `tool-skill` gives them the catalog and loader; the merged catalog also
|
|
85
|
-
# carries whatever the deployment registered globally (repository plugins).
|
|
50
|
+
- id: tool-schedule
|
|
51
|
+
name: '@deepseek-ai/dsh-tool-schedule'
|
|
86
52
|
- id: skill-filesystem
|
|
87
53
|
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
88
|
-
|
|
89
54
|
- id: tool-skill
|
|
90
55
|
name: '@deepseek-ai/dsh-tool-skill'
|
|
91
|
-
|
|
92
|
-
# ── goals ───────────────────────────────────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
# The goal service and session driver stay on the host plane, where the Gateway
|
|
95
|
-
# can resolve them. The human command and model-facing tool register into this
|
|
96
|
-
# preset's scoped layers.
|
|
97
56
|
- id: command-goal
|
|
98
57
|
name: '@deepseek-ai/dsh-command-goal'
|
|
99
|
-
|
|
100
58
|
- id: tool-goal
|
|
101
59
|
name: '@deepseek-ai/dsh-tool-goal'
|
|
102
|
-
|
|
103
|
-
# ── plan mode ───────────────────────────────────────────────────────────────
|
|
104
|
-
|
|
105
|
-
# Plan state is per-agent by nature, so an entry-local realm is not a
|
|
106
|
-
# workaround here — it is the correct lifetime.
|
|
107
60
|
- id: planning
|
|
108
61
|
name: cordis:group
|
|
109
62
|
group: true
|
|
@@ -114,29 +67,17 @@
|
|
|
114
67
|
name: '@deepseek-ai/dsh-plan-mode'
|
|
115
68
|
config:
|
|
116
69
|
section: |
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
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.
|
|
70
|
+
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.
|
|
120
71
|
|
|
121
|
-
|
|
72
|
+
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.
|
|
122
73
|
|
|
123
|
-
|
|
74
|
+
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.
|
|
124
75
|
|
|
125
|
-
|
|
76
|
+
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.
|
|
126
77
|
|
|
127
|
-
|
|
78
|
+
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.
|
|
128
79
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
|
|
132
|
-
# share this realm rather than sit outside it.
|
|
133
|
-
#
|
|
134
|
-
# `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
|
|
135
|
-
# plane, and the rows here resolve that one instance. It takes no configuration,
|
|
136
|
-
# keys every fold by Session, and owns the context-meter projection units the
|
|
137
|
-
# browser reads for every session — behind a realm those units would come and go
|
|
138
|
-
# with whichever presets happen to be mounted. What a preset chooses is whether
|
|
139
|
-
# its agent compacts at all, which is `compaction-basic` below.
|
|
80
|
+
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.
|
|
140
81
|
- id: compaction
|
|
141
82
|
name: cordis:group
|
|
142
83
|
group: true
|
|
@@ -146,28 +87,14 @@
|
|
|
146
87
|
config:
|
|
147
88
|
- id: compaction-basic
|
|
148
89
|
name: '@deepseek-ai/dsh-compaction-basic'
|
|
149
|
-
|
|
150
90
|
- id: command-compact
|
|
151
91
|
name: '@deepseek-ai/dsh-command-compact'
|
|
152
|
-
|
|
153
92
|
- id: tool-result-pruner
|
|
154
93
|
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
155
94
|
config:
|
|
156
95
|
thresholdChars: 8192
|
|
157
96
|
headChars: 4096
|
|
158
97
|
tailChars: 1024
|
|
159
|
-
|
|
160
|
-
# ── delegation and workflows ────────────────────────────────────────────────
|
|
161
|
-
|
|
162
|
-
# The `subagents` registry and its spawn/fork backends live in the HOST
|
|
163
|
-
# composition: the registry is a process singleton whose cross-session queries
|
|
164
|
-
# the api-proxy serves to the browser, and a provider name may only be
|
|
165
|
-
# registered once. This preset contributes the delegation TOOLS, which resolve
|
|
166
|
-
# that host registry.
|
|
167
|
-
#
|
|
168
|
-
# `workflows` is different — nothing outside an agent reads it — so every row
|
|
169
|
-
# that reaches it shares one entry-local realm here, and a consumer left
|
|
170
|
-
# outside would resolve a host registry this preset does not populate.
|
|
171
98
|
- id: delegation
|
|
172
99
|
name: cordis:group
|
|
173
100
|
group: true
|
|
@@ -176,101 +103,44 @@
|
|
|
176
103
|
config:
|
|
177
104
|
- id: tool-subagent-control
|
|
178
105
|
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
179
|
-
|
|
180
106
|
- id: tool-subagent-list-agents
|
|
181
107
|
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
182
|
-
|
|
183
108
|
- id: tool-subagent
|
|
184
109
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
185
110
|
config:
|
|
186
111
|
provider: spawn
|
|
187
112
|
toolName: subagent
|
|
188
113
|
modelSelectionSettings: true
|
|
189
|
-
backgroundMode: continuable
|
|
190
|
-
|
|
191
|
-
# Fork omits model selection so provider/model stay equal to the parent and
|
|
192
|
-
# the inherited history remains eligible for KV Cache reuse. This preset
|
|
193
|
-
# keeps fork continuable; parent and child inherit the same messaging tool,
|
|
194
|
-
# while the parent id and return guidance follow the inherited history.
|
|
195
114
|
- id: tool-subagent-fork
|
|
196
115
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
197
116
|
config:
|
|
198
117
|
provider: fork
|
|
199
118
|
toolName: subagent_fork
|
|
200
|
-
backgroundMode: continuable
|
|
201
|
-
|
|
202
|
-
# Production dsh does not install these optional providers. Install the
|
|
203
|
-
# matching Bundle in this Profile and restart the Host, then copy this
|
|
204
|
-
# preset and remove `disabled` from the matching tool row. Host availability
|
|
205
|
-
# alone grants no tool.
|
|
206
|
-
- id: tool-subagent-codex
|
|
207
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
208
|
-
disabled: true
|
|
209
|
-
config:
|
|
210
|
-
provider: codex
|
|
211
|
-
toolName: subagent_codex
|
|
212
|
-
backgroundMode: one-shot
|
|
213
|
-
maxDepth: provider-managed
|
|
214
|
-
|
|
215
|
-
- id: tool-subagent-claude-code
|
|
216
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
217
|
-
disabled: true
|
|
218
|
-
config:
|
|
219
|
-
provider: claude-code
|
|
220
|
-
toolName: subagent_claude_code
|
|
221
|
-
backgroundMode: one-shot
|
|
222
|
-
maxDepth: provider-managed
|
|
223
|
-
|
|
224
119
|
- id: workflow-ptc
|
|
225
120
|
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
226
121
|
config:
|
|
227
122
|
provider: spawn
|
|
228
|
-
|
|
229
123
|
- id: tool-workflow
|
|
230
124
|
name: '@deepseek-ai/dsh-tool-workflow'
|
|
231
|
-
|
|
232
|
-
# Off by default with the base row it mirrors: the tool description
|
|
233
|
-
# restricts `ralph` to runs the human explicitly asked for, and completion
|
|
234
|
-
# is a worker self-report, not an independent evaluation. Preset files take
|
|
235
|
-
# no patches, so a session that wants it duplicates this preset under a new
|
|
236
|
-
# id and drops `disabled`; the shipped root shadows a copy that reuses this
|
|
237
|
-
# id.
|
|
238
|
-
- id: tool-ralph
|
|
239
|
-
name: '@deepseek-ai/dsh-tool-ralph'
|
|
240
|
-
disabled: true
|
|
241
|
-
config:
|
|
242
|
-
subagentProvider: spawn
|
|
243
|
-
maxRounds: 64
|
|
244
|
-
|
|
245
|
-
# ── remaining model-facing rows ─────────────────────────────────────────────
|
|
246
|
-
|
|
247
125
|
- id: tool-ask-user
|
|
248
126
|
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
249
|
-
|
|
250
127
|
- id: tool-todo
|
|
251
128
|
name: '@deepseek-ai/dsh-tool-todo'
|
|
252
129
|
config:
|
|
253
130
|
allowParallelInProgress: true
|
|
254
|
-
|
|
255
|
-
# The `web` service and its search provider stay in the host composition; only
|
|
256
|
-
# the model-facing tool is per-session.
|
|
257
131
|
- id: tool-web
|
|
258
132
|
name: '@deepseek-ai/dsh-tool-web'
|
|
259
133
|
config:
|
|
260
134
|
fetch: true
|
|
261
135
|
searchTimeoutMs: 60000
|
|
262
|
-
|
|
263
|
-
# Explicit workspace file delivery. Carried by every shipped preset that is not
|
|
264
|
-
# the fixed two-tool `minimal` training configuration, so this preset keeps the
|
|
265
|
-
# row: without it the agent cannot declare its generated maps/plots/gels as
|
|
266
|
-
# deliverables, which is most of what this toolset produces. It consumes only
|
|
267
|
-
# host-plane services (fs/session/tools) and publishes none.
|
|
268
136
|
- id: present
|
|
269
137
|
name: '@deepseek-ai/dsh-tool-present'
|
|
270
|
-
|
|
271
138
|
- id: tool-plugin-manager
|
|
272
139
|
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
273
140
|
disabled: true
|
|
141
|
+
- id: tool-plugin-market
|
|
142
|
+
name: '@deepseek-ai/dsh-plugin-market/tools'
|
|
143
|
+
disabled: true
|
|
274
144
|
|
|
275
145
|
# dsh-molbio-tools: molecular-biology toolset (60 tools across sequence
|
|
276
146
|
# analysis, primers (incl. TaqMan probes and multiplex compatibility),
|
package/test/contract.mjs
CHANGED
|
@@ -67,6 +67,29 @@ function packageDir(pkg) {
|
|
|
67
67
|
return join(harnessRoot, 'node_modules', ...pkg.split('/'));
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a package's client entry the way the boot scan does: read its OWN
|
|
72
|
+
* `exports["./client"]` and take the `browser` condition (the shell is the
|
|
73
|
+
* browser), then `default`.
|
|
74
|
+
*
|
|
75
|
+
* The path is NOT assumed to be `lib/client.js`. 0.2.1-rc.2 moved
|
|
76
|
+
* `@deepseek-ai/dsh-client-connection` to `lib/client.browser.js`; a hardcoded
|
|
77
|
+
* filename reported a package the scanner loads fine, which is a test defect
|
|
78
|
+
* wearing a contract failure's clothes.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} dir the installed package directory.
|
|
81
|
+
* @returns {string|undefined} an absolute entry path, or undefined when the
|
|
82
|
+
* package declares no `./client` export (it is not a client package).
|
|
83
|
+
*/
|
|
84
|
+
function clientEntryPath(dir) {
|
|
85
|
+
const manifestPath = join(dir, 'package.json');
|
|
86
|
+
if (!existsSync(manifestPath)) return undefined;
|
|
87
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
88
|
+
const entry = manifest.exports?.['./client'];
|
|
89
|
+
const rel = typeof entry === 'string' ? entry : entry?.browser ?? entry?.default;
|
|
90
|
+
return typeof rel === 'string' ? join(dir, rel) : undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
70
93
|
/** Read one installed client bundle, or undefined when the package is absent. */
|
|
71
94
|
function clientBundle(pkg) {
|
|
72
95
|
const path = join(packageDir(pkg), 'lib', 'client.js');
|
|
@@ -263,21 +286,35 @@ check('the package manifest keeps the client declaration the scan keys on', () =
|
|
|
263
286
|
assert.equal(clientRel, './lib/client.js', 'exports["./client"] points at the artifact');
|
|
264
287
|
assert.ok(existsSync(join(packageRoot, clientRel)), 'the artifact exists');
|
|
265
288
|
|
|
266
|
-
// An injected client package must be a boot-graph row
|
|
267
|
-
// its
|
|
268
|
-
//
|
|
289
|
+
// An injected client package must be a boot-graph row. Ask the package where
|
|
290
|
+
// its client entry is (exports["./client"], browser condition) instead of
|
|
291
|
+
// assuming `lib/client.js`: the scan resolves the manifest, and 0.2.1-rc.2
|
|
292
|
+
// moved `dsh-client-connection` to `lib/client.browser.js`.
|
|
269
293
|
const shellDir = join(packagesDir, 'dsh-web-frontend', 'dist');
|
|
270
294
|
const shellText = existsSync(shellDir)
|
|
271
295
|
? filesUnder(shellDir).filter((path) => path.endsWith('.js')).map((path) => readFileSync(path, 'utf8')).join('\n')
|
|
272
296
|
: '';
|
|
273
297
|
for (const dependency of manifest.dsh.client.inject) {
|
|
274
|
-
const
|
|
275
|
-
const mounted = existsSync(
|
|
298
|
+
const entry = clientEntryPath(packageDir(dependency));
|
|
299
|
+
const mounted = entry !== undefined && existsSync(entry);
|
|
300
|
+
// A package genuinely seeded into the shell has no file of its own. That
|
|
301
|
+
// fallback is kept, but it is only evidence when the shell actually names
|
|
302
|
+
// the package — the current shell is a minified bundle that names none.
|
|
276
303
|
const seeded = shellText.includes(`require("${dependency}")`) || shellText.includes(`require('${dependency}')`);
|
|
277
|
-
assert.ok(mounted || seeded, `${dependency}
|
|
278
|
-
assert.ok(existsSync(join(dir, 'package.json')) || seeded, `${dependency} resolves as a package or comes from the shell`);
|
|
304
|
+
assert.ok(mounted || seeded, `${dependency} resolves a ./client entry (declared: ${entry ?? 'none'})`);
|
|
279
305
|
}
|
|
280
306
|
|
|
307
|
+
// The resolver must be able to say NO, and must find the entry of the package
|
|
308
|
+
// that moved — otherwise the loop above could pass on a directory that merely
|
|
309
|
+
// exists.
|
|
310
|
+
assert.equal(
|
|
311
|
+
clientEntryPath(packageDir('@deepseek-ai/dsh-tools')),
|
|
312
|
+
undefined,
|
|
313
|
+
'a package with no exports["./client"] resolves to no client entry',
|
|
314
|
+
);
|
|
315
|
+
const movedEntry = clientEntryPath(packageDir('@deepseek-ai/dsh-client-connection'));
|
|
316
|
+
assert.ok(movedEntry !== undefined && existsSync(movedEntry), 'the conditional client entry resolves to a file on disk');
|
|
317
|
+
|
|
281
318
|
// The panel-only package must carry the same declaration: it is the channel a
|
|
282
319
|
// shared profile installs.
|
|
283
320
|
const panelManifestPath = join(packageRoot, 'packages', 'molbio-panel', 'package.json');
|
|
@@ -328,13 +365,21 @@ check('the published tarball would carry the client half and the panel package',
|
|
|
328
365
|
});
|
|
329
366
|
|
|
330
367
|
check('the toolview card contract still holds (meta path + keyed seat)', () => {
|
|
331
|
-
// The card reads the tool-result block's `meta`,
|
|
332
|
-
//
|
|
368
|
+
// The card reads the tool-result block's `meta`, filled from a tool's
|
|
369
|
+
// `output.presentationMeta`. 0.2.1-rc.2 removed the `exec.parent === void 0`
|
|
370
|
+
// gate this check used to pin: the projection now runs for every successful
|
|
371
|
+
// execution, and the root call the card reads is a subset of that. So pin the
|
|
372
|
+
// path that still carries the value — consulted, snapshotted, attached as
|
|
373
|
+
// `meta` — rather than a literal upstream is free to drop (the panel does not
|
|
374
|
+
// depend on nested calls being excluded, only on the root call carrying it).
|
|
333
375
|
const tools = hostHalf('@deepseek-ai/dsh-tools');
|
|
334
376
|
assert.ok(tools !== undefined, 'dsh-tools is installed');
|
|
335
377
|
assert.ok(/output\.presentationMeta\s*!==\s*void 0/.test(tools.text), 'the tool layer still consults output.presentationMeta');
|
|
336
|
-
assert.ok(tools.text.includes('exec.parent === void 0'), 'and calls it for root calls');
|
|
337
378
|
assert.ok(tools.text.includes('snapshotProjection(tool.name, "presentationMeta"'), 'and records the projection on the result');
|
|
379
|
+
assert.ok(
|
|
380
|
+
/meta !== void 0 \? \{ meta \} : \{\}/.test(tools.text),
|
|
381
|
+
'and attaches the snapshot to the success result as `meta`, which is what the card reads',
|
|
382
|
+
);
|
|
338
383
|
assert.ok(/output \{ schema, render, presentationMeta\? \}/.test(tools.text), 'register() accepts presentationMeta on a raw output');
|
|
339
384
|
|
|
340
385
|
// The browser side: the seat exists and is keyed by tool name.
|
package/test/drift-probe.mjs
CHANGED
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* no released DSH installs. The row mounted nothing and the preset died at
|
|
14
14
|
* load. The id comparison saw a row named `workflow-worker-thread`, noted it
|
|
15
15
|
* as drift, printed a note, and exited 0.
|
|
16
|
-
* - The same composition enabled `tool-ralph`, which
|
|
17
|
-
*
|
|
16
|
+
* - The same composition enabled `tool-ralph`, which the then-shipped
|
|
17
|
+
* `standard` preset carried with `disabled: true`. Same row id, so the guard
|
|
18
|
+
* was blind to it. (0.2.1-rc.2 no longer ships that row; the probe mutates
|
|
19
|
+
* `tool-plugin-manager`, which it still ships disabled.)
|
|
18
20
|
*
|
|
19
21
|
* So this file mutates a copy of the composition and asserts the guard reports
|
|
20
22
|
* the mutation, then asserts a clean composition reports NOTHING (a guard that
|
|
@@ -125,9 +127,16 @@ check('a phantom provider package is caught (v17: dsh-workflow-worker-thread)',
|
|
|
125
127
|
assert.match(text, /extra row "workflow-worker-thread" \(not in shipped standard\)/, text);
|
|
126
128
|
});
|
|
127
129
|
|
|
128
|
-
check('an upstream `disabled: true` that was dropped is caught (v17: tool-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
check('an upstream `disabled: true` that was dropped is caught (v17: tool-plugin-manager)', () => {
|
|
131
|
+
// v17 mutates whichever row the shipped standard carries disabled: the
|
|
132
|
+
// mechanism is the same, and naming a row upstream has since dropped would
|
|
133
|
+
// make this probe pass by mutating nothing.
|
|
134
|
+
const text = driftText(withRow('tool-plugin-manager', { disabled: undefined }));
|
|
135
|
+
assert.match(
|
|
136
|
+
text,
|
|
137
|
+
/row "tool-plugin-manager": upstream ships it disabled and this preset enables it/,
|
|
138
|
+
text,
|
|
139
|
+
);
|
|
131
140
|
});
|
|
132
141
|
|
|
133
142
|
// ── the other ways a copied composition rots ───────────────────────────────
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "Real responses from one benchmark run, frozen so the scorer can be regression-tested without spending API budget. See test/benchmark-score.mjs for why each defect these cover was invisible to synthetic traces. Two
|
|
2
|
+
"$comment": "Real responses from one benchmark run, frozen so the scorer can be regression-tested without spending API budget. See test/benchmark-score.mjs for why each defect these cover was invisible to synthetic traces. Two responses (methylation-block, qpcr-primers) were recorded before the 0.16.0 primer-orientation and methylation-table fixes and were re-recorded on 2026-10-23 against DSH 0.2.1-rc.2; run.amendments names each one and the run it came from, and every other response is unchanged.",
|
|
3
3
|
"run": {
|
|
4
4
|
"recorded_at": "2026-09-23T12:03:15.526Z",
|
|
5
5
|
"harness_version": "0.1.7-alpha.2",
|
|
@@ -38,14 +38,13 @@
|
|
|
38
38
|
"molbio_extract_region",
|
|
39
39
|
"molbio_methylation_check",
|
|
40
40
|
"molbio_methylation_check",
|
|
41
|
+
"molbio_enzyme_lookup",
|
|
41
42
|
"molbio_methylation_check",
|
|
43
|
+
"pwsh",
|
|
42
44
|
"molbio_restriction_sites",
|
|
43
|
-
"
|
|
44
|
-
"web_fetch"
|
|
45
|
+
"molbio_restriction_sites"
|
|
45
46
|
],
|
|
46
|
-
"answer": "##
|
|
47
|
-
"answer_excluded_from_scoring": true,
|
|
48
|
-
"answer_excluded_reason": "recorded before the 0.16.0 methylation-table fix: the model correctly reported the tool's then-verdict (BamHI impaired by dam) and cross-checked the supplier. With the table corrected the tool reports BamHI as cutting, so this answer predates the behaviour it would now be graded against. Re-record with `node benchmark/run.mjs --model --task methylation-block` then `node benchmark/_freeze-trace.mjs benchmark/reports/<run>.json`."
|
|
47
|
+
"answer": "## Bottom line\n\n**Methylation is not your BamHI problem.** pUC118 carries 15 Dam sites and 5 Dcm sites, and the BamHI recognition site itself contains a Dam `GATC` — but BamHI is **not Dam- or Dcm-sensitive**, so the analysis classes it as usable and predicts a clean single cut.\n\n## The marks found (pUC118, 3162 bp circular)\n\n| Mark | Motif | Count | 1-based positions |\n|---|---|---|---|\n| Dam | GATC | 15 | 753, **906**, 1849, 1924, 1935, 1943, 2021, 2033, 2138, 2479, 2497, 2543, 2801, 2818, 2854 |\n| Dcm | CCWGG | 5 | 830, 1021, 1309, 1430, 1443 |\n\n## BamHI specifically\n\n- Site: **GGATCC at 905–910**, cut **G^GATCC at 906** → one fragment of 3162 bp (linearised plasmid).\n- That site *does* contain an embedded Dam site: `GGATCC` = G + **GATC** + C, with the `GATC` at 906–909. So the plasmid is methylated at the very base pair BamHI needs to cut.\n- Nevertheless the tool returns **BamHI: usable** — BamHI is insensitive to Dam methylation (and to Dcm). The embedded `GATC` is a red herring for this enzyme.\n- No enzyme in the 103-enzyme panel is fully **blocked**; the only flagged enzyme is **BstYI (impaired, Dam)**.\n\nThat last point is a useful positive control for the analysis: **BstYI (R^GATCY)** recognises the *same* `GGATCC` at 905–910 (plus six other RGATCC sites, all of which contain `GATC` at 1924, 1935, 2021, 2033, 2801, 2818) and is correctly flagged as Dam-impaired. So the tool is not simply waving everything through — it distinguishes BamHI from a genuinely Dam-sensitive enzyme at an identical site.\n\n## Enzymes the analysis considers safe here\n\n**46 of 103** panel enzymes are usable (0 blocked, 1 impaired, the rest have no site on this plasmid at all). The everyday ones:\n\n**BamHI, EcoRI, HindIII, KpnI, PstI, SacI, SalI, SmaI, XbaI** — all single-cutters in the MCS and all methylation-safe.\n\nFull usable list: AatII, Acc65I, AseI, BamHI, BfuAI, BglI, BsaAI, BsaI, BsmBI, BspQI, BtgZI, BveI, DraI, DraIII, EcoRI, Esp3I, FokI, FspI, HincII, HindIII, KasI, KpnI, LguI, NaeI, NarI, NgoMIV, PciI, PluTI, PsiI, PstI, PvuI, PvuII, SacI, SalI, SapI, SbfI, ScaI, SfoI, SmaI, SphI, SspI, TspMI, XbaI, XmaI, XmnI, ZraI.\n\nTwo caveats on that list: \"usable\" means *methylation-safe*, not *single-cutter* — e.g. **AseI cuts 3×** (1054, 1113, 2348 → 1868/1235/59 bp) and **BglI cuts 2×** (728, 2296 → 1594/1568 bp). And ClaI, BclI, BglII and XhoI are absent from every list because they have **no site** on pUC118 — a different reason for not working, unrelated to methylation.\n\n## Caveats\n\n- The sensitivity table is a curated REBASE/NEB-style quick reference; blocking can depend on the enzyme lot and the actual methylation state, so confirm against the supplier's current table before betting an experiment on it.\n- Dam/Dcm are the marks a standard `dam+`/`dcm+` *E. coli* host adds. If pUC118 was grown in a `dam−/dcm−` host, none of these marks are present.\n- CpG methylation is **not** modelled — relevant only if the DNA came from a mammalian source. BamHI *is* blocked by overlapping CpG methylation, but plasmid DNA from *E. coli* carries none.\n\nSo if the BamHI lane is empty or uncut, look elsewhere: buffer/incubation temperature and time, salt or EDTA carryover, DNA quality/contamination, enzyme lot or repeated freeze–thaw, and gel loading — not Dam/Dcm."
|
|
49
48
|
},
|
|
50
49
|
{
|
|
51
50
|
"id": "qpcr-ddct",
|
|
@@ -82,9 +81,7 @@
|
|
|
82
81
|
"tool_calls": [
|
|
83
82
|
"molbio_design_primers"
|
|
84
83
|
],
|
|
85
|
-
"answer": "**Best
|
|
86
|
-
"answer_excluded_from_scoring": true,
|
|
87
|
-
"answer_excluded_reason": "recorded before the 0.16.0 primer-orientation fix: the answer names amplicon 105-222 (118 bp) with the roles as the buggy designer printed them (F 201-222 downstream of R 105-124). The corrected designer reports 105-223 (119 bp) with F upstream, so this answer predates the behaviour it would now be graded against. Re-record with `node benchmark/run.mjs --model --task qpcr-primers` then freeze."
|
|
84
|
+
"answer": "**Best qPCR primer pair** (one candidate satisfied all constraints):\n\n| | Sequence (5'→3') | Position | Length | Tm* | GC |\n|---|---|---|---|---|---|\n| Forward | `GTGCGTACAGAGGAGTAGTG` | 105–124 | 20 nt | 58.9 °C | 55.0% |\n| Reverse | `CGTATGGGAAGAGCAGGAG` | 205–223 | 19 nt | 58.6 °C | 57.9% |\n\n- **Amplicon:** 105–223 = **119 bp** (inside your 80–150 bp window)\n- **Tm Δ:** 0.3 °C; no hairpins detected (0 °C), low self-complementarity (best score 7.0/2.0 for F, 4.0/2.5 for R) — no primer-dimer or hairpin red flags.\n\n*Tm is a SantaLucia-1998 nearest-neighbour estimate (50 mM Na⁺, 1.5 mM Mg²⁺, 0.8 mM dNTP, 200 nM primer), not an instrument calibration — confirm with your own melt curve.\n\nThe pair was ranked best of the candidates found; the search returned only this one pair meeting all four of your hard constraints, so there is no runner-up trade-off to weigh."
|
|
88
85
|
},
|
|
89
86
|
{
|
|
90
87
|
"id": "fastq-qc",
|
|
@@ -552,6 +549,20 @@
|
|
|
552
549
|
],
|
|
553
550
|
"answer": "## Parsed results\n\n| Property | pUC118.gb (GenBank) | pUC118.dna (SnapGene) |\n|---|---|---|\n| Name | **pUC118** | **pUC118** |\n| Accession | **U07649** (VERSION U07649.1) | label carries **U07649** |\n| Length | 3162 bp | 3162 bp |\n| Topology | **circular** (`LOCUS … circular SYN`) | **circular** |\n| AmpR feature | CDS **2102..2962**, `/label=\"AmpR\"`, `/gene=\"bla\"`, `/product=\"beta-lactamase\"` | CDS 2102‑2962, **AmpR** (annotated reverse) |\n\nBoth records also share the same source line (`synthetic construct`). The GenBank file is sparsely annotated (only `source` and the AmpR CDS); the SnapGene file carries 10 features including `ori`, `AmpR promoter`, `MCS`, `lac promoter`, `lac operator`, `M13 fwd/rev`, `lacZα` and the `M13 ori`.\n\n## AmpR coordinates\n\n- Span is identical in both files: **2102–2962, 861 bp**, encoding a 286‑aa beta-lactamase (TEM‑1-type: `MSIQHFRVALIPFFAAFCLPVFAHPETLVKVKDAEDQLGARVGYIELDLNSGKILESFRPEERFPMMSTFKVLLCGAVLSRIDAGQEQLGRRIHYSQNDLVEYSPVTEKHLTDGMTVRELCSAAITMSDNTAANLLLTTIGGPKELTAFLHNMGDHVTRLDRWEPELNEAIPNDERDTTMPVAMATTLRKLLTGELLTLASRQQLIDWMEADKVAGPLLRSALPAGWFIADKSGAGERGSRGIIAALGPDGKPSRIVVIYTTGSQATMDERNRQIAEIGASLIKHW`).\n- **The strand annotation disagrees.** SnapGene marks it reverse (minus) strand. The GenBank record writes a plain `2102..2962` (i.e. forward) with no `complement`. Translating confirms SnapGene is right: the reverse complement is a clean 286‑aa ORF, whereas the forward strand has 12 stops and no ORF ≥30 aa. So the `.gb` file's AmpR strand is a mis-annotation, not a different molecule.\n\n## Do the two files describe the same molecule?\n\n**Yes.** Aligning the full sequences gives **100% identity across all 3162 aligned columns**, with the same length and same circular topology. They are the same pUC118 U07649 molecule.\n\nThe only substantive difference is annotation, not DNA:\n1. The `.dna` file is richly annotated (10 features); the `.gb` file has only `source` + AmpR.\n2. For AmpR the coordinate span matches exactly, but the `.gb` file leaves it on the forward strand — the correct strand is reverse, as the SnapGene file (and the actual beta-lactamase ORF) shows. Worth fixing in the GenBank record if you plan to use it for cloning or maps.\n\nCaveat: the SnapGene parse exposed the label \"pUC118 (U07649)\" but no separate accession field, so the accession U07649 is explicitly present only in the GenBank record."
|
|
554
551
|
}
|
|
552
|
+
],
|
|
553
|
+
"amendments": [
|
|
554
|
+
{
|
|
555
|
+
"id": "methylation-block",
|
|
556
|
+
"recorded_at": "2026-10-23T12:35:37.379Z",
|
|
557
|
+
"harness_version": "0.2.1-rc.2",
|
|
558
|
+
"model": "the profile default (deepseek-flash unless the profile or session overrides it)"
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
"id": "qpcr-primers",
|
|
562
|
+
"recorded_at": "2026-10-23T12:35:37.379Z",
|
|
563
|
+
"harness_version": "0.2.1-rc.2",
|
|
564
|
+
"model": "the profile default (deepseek-flash unless the profile or session overrides it)"
|
|
565
|
+
}
|
|
555
566
|
]
|
|
556
567
|
}
|
|
557
568
|
}
|
package/test/preset-health.mjs
CHANGED
|
@@ -215,7 +215,7 @@ function toolCountDrift(description, registered) {
|
|
|
215
215
|
* order. Comparing ids alone (the v15–v17 behaviour) misses exactly the two
|
|
216
216
|
* defects that reached a release: a provider package the harness does not
|
|
217
217
|
* install (`workflow-worker-thread`) and an upstream `disabled: true` that had
|
|
218
|
-
* been dropped (`tool-ralph`).
|
|
218
|
+
* been dropped (v17's `tool-ralph`; 0.2.1-rc.2 ships `tool-plugin-manager` disabled).
|
|
219
219
|
*/
|
|
220
220
|
function compositionDrift(standardRows, mineRows) {
|
|
221
221
|
const drift = [];
|