clearai-dsh 0.2.1 → 0.2.4

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
@@ -2,6 +2,48 @@
2
2
 
3
3
  All notable changes to this project are recorded here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
4
 
5
+ ## [0.2.4] — 2026-09-28
6
+
7
+ **跟上了宿主的预设换代。** 宿主 `0.1.7-alpha.1` 起把 agent 预设的注册从「root 目录扫描」换成了「组合里的声明行」,而 clearai-dsh 一直靠一条覆盖 `agent-presets` 行的补丁,把名册的 root 指到包内 `presets/`。那行 id 在新宿主里**已经不存在**,补丁没有落点——包照样装得上、宿主行照样起得来,但 **ClearAI 不进模式选择器**。这一版把它接上。
8
+
9
+ **`0.2.3` 没有发布。** 它以 `v0.2.3` 触发了发布流水线,在「干净安装」那道门被拦下(拦的正是上面这个断裂),publish、registry 回查、建 Release 三步全部 skipped——npm 上半点副作用都没有。它原本要带的三条文档改动(版本号、中英 README)并入本版,所以这一版也包含 0.2.3 的账。
10
+
11
+ > ⚠️ **宿主支持边界:本版要求宿主 ≥ `0.1.7-alpha.1`。** 在更早的宿主(≤ `0.1.6-alpha.2`,包括曾被当作 `latest` 的 `0.1.5-rc.3`)上,本版会因为找不到 `@deepseek-ai/dsh-agent-preset` 而**让 profile 起不来**。仍留在旧宿主的部署请继续用 `0.2.2`。
12
+
13
+ ### Added
14
+
15
+ - **预设声明行**:`presets/clearai/clearai.patch.yml` —— 一条 `- id: preset-clearai` 声明行,`config.plugins` 里放整份插件列表。它由 `preset/agent.cordis.yml` **构建期派生**(与 `ui/vendor/*.js` 同一条纪律:生成物进仓库,包 = 源的纯函数),不手抄第二份。`package.json` 的 `dsh.bundle.patch` 随之由单文件改为**数组**。
16
+ - **干净安装验收新增两条运行态断言**:boot 一次 profile,直接读 `agentPresets.list()`,要求 `clearai` 在列表里**且没有 `broken`**。静态的 `--dump-config` 看不出这件事——探针实测过:preset 里放一个**根本不存在的插件**,boot 依然完全正常,只有名册记一条 broken,界面就不显示这个预设。
17
+
18
+ ### Changed
19
+
20
+ - **包内插件改用包内子路径**:`clearai-kernel` 与 `clearai-commands` 由 `./plugins/*.js` 改为 `clearai-dsh/presets/clearai/plugins/*.js`(`exports` 里加 `"./presets/*"` 放行)。声明行 `plugins` 的相对基准与原来的 `agent.cordis.yml` 不同,不改就会在名册里一直记着「never started」。
21
+ - **workflow 引擎换包**:预设里那条 `@deepseek-ai/dsh-workflow-worker-thread` 在新宿主里**已经下线**,改为同 group 内的 `@deepseek-ai/dsh-workflow-ptc`(与官方 standard 预设同形,且必须与 `tool-workflow` / `tool-ralph` 同处那个 `isolate: { workflowEngine: true }` 的 realm,否则两条工具会一直「waiting for workflowEngine」)。
22
+ - `pack/cordis.patch.yml` 里那段 `- id: agent-presets` 覆盖**已删除**:它在新宿主上没有目标行,留着只会让下一个人以为预设还靠目录扫描。
23
+ - 文档与版本信息:项目版本更新至 `0.2.4`,中英 README 更新(原 0.2.3 的三条改动)。
24
+
25
+ ## [0.2.3] — 2026-09-23(未发布)
26
+
27
+ > 本版**从未发布到 npm**。它是纯文档版本(版本号 + 中英 README),在发布流水线上被宿主换代造成的断裂拦下——原样发出去的话,用户在新宿主上装到的包不进预设选择器。改动已并入 [0.2.4]。
28
+
29
+
30
+ **文档与版本信息更新。**
31
+
32
+ ### Changed
33
+
34
+ - 更新项目版本至 `0.2.3`。
35
+ - 更新中文 README。
36
+ - 更新 README。
37
+
38
+ ## [0.2.2] — 2026-09-18
39
+
40
+ **装的时候不再吓人。** 0.2.1 的 `npx clearai-dsh install` 会打出一串 peer 警告(react / graphology-types …),读起来像装坏了——而它们一个字都不影响运行。这一版把安装面收窄到运行时真正需要的那一个依赖,并让安装侧 CLI 按系统语言出话。
41
+
42
+ ### Changed
43
+
44
+ - **安装面只剩一个运行时依赖。** `@xyflow/react` / `graphology` / `graphology-layout-forceatlas2` / `docx` 挪进 `devDependencies`:前三个只在构建期打 vendor(`lib/client.js` 里是**内联**的,装机后不解析 npm),`docx` 只给营销 docx 脚本用。于是 profile 里不再多装一批包,也不会再有那些注定填不上的 peer 警告——React 由 **DSH 宿主**提供(客户端半 `require('react')` 是问宿主拿的),`graphology-types` 只是类型包。运行时唯一保留的是 `zod`(宿主半 `lib/host.js` 真的 `from 'zod'`)。
45
+ - **安装侧 CLI 跟系统语言走。** `doctor` / `install` / `root-yaml` / `seed` / `unseed` 的每一句都在中英两份文案表里(并排放在一处,改的时候不会只改一边);判据是 `--lang zh|en` > `CLEARAI_LANG` > `LC_ALL` / `LC_MESSAGES` / `LANG` > ICU 的默认 locale,`C` / `POSIX` 当「没有语言信息」按英文处理。0.2.1 之前是无论系统是什么都说中文。
46
+
5
47
  ## [0.2.1] — 2026-09-18
6
48
 
7
49
  **知识任务是循环的原生行为,不是另一个模式。** 本体、实体、认识论早就在,但普通研究的最短路径仍然是「检索 → 总结 → 写报告」——要建本体得用户先想起来说一句。这一版修的是**接线缺口**:把知识任务的判据做成结构的(目标还开着 + 带着登记过的假设),系统自己进知识模式;并把图从手写 SVG 换成 React Flow,给了它一个真正的全屏工作区。
package/README.md CHANGED
@@ -16,6 +16,14 @@ ClearAI is an **ontology discovery and exploration platform**, built on two core
16
16
 
17
17
  > Other knowledge graphs pile up edges by extraction and assertion; here every edge has to be earned through the loop.
18
18
 
19
+ ```bash
20
+ # Install (npm package, prebuilt — no build step, no allowBuilds prompt)
21
+ dsh plugin --profile web add clearai-dsh
22
+ ```
23
+
24
+ Restart `dsh web`, then pick **ClearAI** in the preset picker at the top of a new session. That is the whole setup. [Full install notes ↓](#install-and-use)
25
+
26
+
19
27
  <picture>
20
28
  <source media="(prefers-color-scheme: dark)" srcset="docs/diagrams/ontology-hero-dark.png">
21
29
  <img src="docs/diagrams/ontology-hero.png" alt="The epistemic loop (left) growing a domain ontology (right)" width="1200">
@@ -61,10 +69,32 @@ ClearAI does **not** claim recursive self-improvement. It provides the epistemic
61
69
 
62
70
  ## Install and use
63
71
 
72
+ **Recommended — install from npm:**
73
+
74
+ ```bash
75
+ dsh plugin --profile web add clearai-dsh
76
+ ```
77
+
78
+ This installs the prebuilt package from the npm registry. Nothing is compiled on your machine, so there is no `allowBuilds` grant to approve — the plugin is ready the moment the command returns.
79
+
80
+ **Also available — one-command installer:**
81
+
64
82
  ```bash
65
83
  npx clearai-dsh install
66
84
  ```
67
85
 
86
+ Same install underneath; it resolves the DSH CLI from your PATH (or through npx), installs into the `web` profile, and reads the composed config back so you are not taking "success" on faith. Use this if you prefer a guided path, or `--lang zh|en` to force the installer's output language.
87
+
88
+ **Install from source (for development, not the normal path):**
89
+
90
+ ```bash
91
+ dsh plugin --profile web add github:Clearailhc/clearai-dsh
92
+ ```
93
+
94
+ Git fetches source rather than build artifacts, so pnpm ≥10 will refuse to run the `prepare` script until you add an `allowBuilds` entry to the profile's `pnpm-workspace.yaml`. That grant means *permission for this package's code to execute on your machine at install time* — grant it only if you have read the source, and pin a commit. If you just want to use ClearAI, use the npm install above.
95
+
96
+ The installer's output follows your system language (`--lang zh|en` overrides it, `doctor` / `seed` / `unseed` take the same flag). Its only runtime dependency is `zod`; the graph stack is bundled into the client half at build time.
97
+
68
98
  Restart `dsh web` afterwards (`npx @deepseek-ai/dsh web`), then **create a session and switch to the `ClearAI` mode in the picker at the top**:
69
99
 
70
100
  1. Open `dsh web` and click "New session";
package/README.zh-CN.md CHANGED
@@ -16,6 +16,14 @@ ClearAI 是一个**本体发现与探索平台**,核心由两个概念支撑
16
16
 
17
17
  > 别的知识图谱靠抽取与断言堆边;这里的每一条边都要通过循环挣得。
18
18
 
19
+ ```bash
20
+ # 安装(npm 包,预构建——无需构建步骤,不会触发 allowBuilds 授权)
21
+ dsh plugin --profile web add clearai-dsh
22
+ ```
23
+
24
+ 重启 `dsh web`,在新建会话顶部的模式选择器里选 **ClearAI** 即可。这就是全部步骤。[完整安装说明 ↓](#安装与使用)
25
+
26
+
19
27
  <picture>
20
28
  <source media="(prefers-color-scheme: dark)" srcset="docs/diagrams/ontology-hero-dark.zh-CN.png">
21
29
  <img src="docs/diagrams/ontology-hero.zh-CN.png" alt="认识论循环(左)长出领域本体(右)" width="1200">
@@ -61,10 +69,33 @@ ClearAI **不**声称递归自我改进。它提供的是自我改进系统所
61
69
 
62
70
  ## 安装与使用
63
71
 
72
+ **推荐——从 npm 安装:**
73
+
74
+ ```bash
75
+ dsh plugin --profile web add clearai-dsh
76
+ ```
77
+
78
+ 从 npm registry 装预构建产物。本机不跑任何编译,因此不需要批准 `allowBuilds` 授权——命令返回时插件就已经可用。
79
+
80
+ **也提供——一条命令的安装器:**
81
+
64
82
  ```bash
65
83
  npx clearai-dsh install
66
84
  ```
67
85
 
86
+ 底层是同一个安装;它会从 PATH(或经 npx)解析出 DSH CLI,装进 `web` profile,再把合成后的配置读回来验证,所以「成功」不是靠信。想走引导式流程就用它,`--lang zh|en` 可指定安装器输出语言。
87
+
88
+ **从源码安装(开发用,不是常规路径):**
89
+
90
+ ```bash
91
+ dsh plugin --profile web add github:Clearailhc/clearai-dsh
92
+ ```
93
+
94
+ Git 拉的是源码而不是构建产物,所以 pnpm ≥10 会拒绝运行 `prepare` 脚本,直到你在该 profile 的 `pnpm-workspace.yaml` 里加上 `allowBuilds` 条目。那条授权的含义是**允许该包代码在安装时于你机器上执行**——只在你读过源码后再授权,并且固定 commit。如果你只是想用 ClearAI,请用上面的 npm 安装。
95
+
96
+ 安装侧的输出**跟系统语言走**(`--lang zh|en` 可覆盖;`doctor` / `seed` / `unseed` 同样认这个开关)。运行时依赖只有 `zod`——图谱那套栈在构建期就打进客户端了。
97
+
98
+
68
99
  装完重启 `dsh web`(`npx @deepseek-ai/dsh web`),然后**新建会话,在顶部的模式选择器里切换到 `ClearAI`**:
69
100
 
70
101
  1. 打开 `dsh web`,点「新建会话」;
package/bin/clearai.mjs CHANGED
@@ -44,6 +44,143 @@ const value = (name, fallback) => {
44
44
  }
45
45
  const profile = value('profile', 'web')
46
46
 
47
+ /**
48
+ * 语言:**跟系统走,不猜**。
49
+ *
50
+ * 判据顺序:`--lang zh|en` > `CLEARAI_LANG` > `LC_ALL` / `LC_MESSAGES` / `LANG` > ICU 的默认 locale。
51
+ * `C` / `POSIX` 是「没有语言信息」,按英文处理(它们是 CI 与最小容器的默认值,不代表中文)。
52
+ * 为什么认不出来时说英文:这是发给陌生人的第一条命令输出,不该假设对方读中文。
53
+ * 两边的文案**并排放在一张表里**——同一句话的两个版本挨着,改的时候不会只改一边。
54
+ */
55
+ const lang = (() => {
56
+ const candidates = [value('lang', null), process.env.CLEARAI_LANG, process.env.LC_ALL, process.env.LC_MESSAGES, process.env.LANG, Intl.DateTimeFormat().resolvedOptions().locale]
57
+ for (const candidate of candidates) {
58
+ if (typeof candidate !== 'string' || candidate === '') continue
59
+ const tag = candidate.toLowerCase()
60
+ if (tag.startsWith('zh')) return 'zh'
61
+ if (tag === 'c' || tag === 'posix' || tag.startsWith('en')) return 'en'
62
+ }
63
+ return 'en'
64
+ })()
65
+
66
+ const TEXT = {
67
+ zh: {
68
+ pkgDir: '包目录 ',
69
+ presetSrc: '预设源 ',
70
+ presetOk: '(agent.cordis.yml ✓)',
71
+ presetMissing: '(缺 agent.cordis.yml ✗)',
72
+ dshHome: 'DSH_HOME ',
73
+ userRoot: '用户根 ',
74
+ rosterVisible: '✓ 名册看得见',
75
+ rosterAbsent: '✗ 还没有(用 seed 播种,或把 root-yaml 那一行粘进 profile)',
76
+ shadowCopy: ' ⚠️ 影子副本 {path} 与包里的那份**不一致**,而它被包的 root 遮住、永远不会被读到(自检却会优先读它)。删掉它,或用它来承载你自己的改动并换一个 id。',
77
+ profileRow: 'profile ',
78
+ profileAbsent: '(不存在:先跑一次 dsh --profile {profile})',
79
+ composeRow: '组合 ',
80
+ composeUnreachable: '(问不到:dsh 不可用或超时 —— 下面两条无法判定)',
81
+ hostRow: '宿主行 ',
82
+ hostIn: '在组合里 ✓',
83
+ hostOut: '不在组合里(先 dsh plugin --profile {profile} add <本包>)',
84
+ rosterRootRow: '名册 root ',
85
+ rosterNoRoots: '组合里没看到 roots(这个部署可能不挂名册)',
86
+ rosterExpr: '{expr}(dump 打的是表达式原文,求值在装载时)',
87
+ seedLedgerRow: '播种记账 ',
88
+ ledgerEmpty: '(空)',
89
+ ledgerCount: '{count} 个条目 · {path}',
90
+ doctorHint: '\n提示:doctor 只读;它不会替你改 profile,也不会替你播种。',
91
+ installHeader: '【安装】{name}@{version}',
92
+ installWhat: '装什么 ',
93
+ installWhere: '装到哪 ',
94
+ installWho: '谁来跑 ',
95
+ hostRowShort: '宿主行 ',
96
+ sourceSpec: '你给的 spec',
97
+ sourceTarball: '本地 tarball',
98
+ sourceDir: '本地目录',
99
+ sourceRegistry: 'registry',
100
+ routeNpx: 'npx --yes @deepseek-ai/dsh(PATH 上没有 dsh)',
101
+ pnpmMissing: '\n✗ PATH 上没有 pnpm,而 DSH 管理一个 profile 就是靠它:`dsh plugin …` 把参数转发给 pnpm。\n 装一个再来:npm install -g pnpm(或用系统包管理器,如 brew install pnpm)。\n 别用 corepack enable 抄近路 —— 它装的是版本**转发器**而不是 pnpm,而当前 Node 自带的\n 那份 corepack 可能下载一个它自己启动不了的 pnpm。\n 这里刻意不手工改 profile:那等于把宿主的 reconcile 抄成第二份实现,与宿主漂移时坏的是你的部署。',
102
+ installFailed: '\n✗ 安装失败:见上面的输出。',
103
+ hostUnknown: '(问不到组合:CLI 不可用或超时 —— 装没装进去,从这里确认不了;用 doctor 再看)',
104
+ hostAbsent: '**不在组合里** —— 装是装上了,但组合里没看到它(用 doctor 查)',
105
+ nextStep: '\n 下一步 重启 dsh web(两半都在进程里按模块 URL 缓存,只刷新浏览器不够),然后在预设选择器里选 ClearAI。',
106
+ uninstall: ' 卸载 dsh plugin --profile {profile} remove {name}(同样可以冠 npx)',
107
+ rootYamlComment1: '# ClearAI 预设的 root(由 clearai-dsh 的 bin 打印,路径已算成绝对路径)',
108
+ rootYamlComment2: ' # ⚠️ 补丁层会**替换整份 config**:下面这些键必须与你部署里那份一致,否则会丢。',
109
+ rootYamlComment3: ' # 先 `dsh --profile <p> --dump-config | grep -A 20 agent-presets` 看一眼当前值再粘。',
110
+ noPresetInPackage: '✗ 包里没有预设:{path}',
111
+ seededTo: ' 播种到 {dest}',
112
+ seedCounts: ' 新增 {seeded} · 刷新 {refreshed}',
113
+ seedDrifted: ' · **你改过、没覆盖** {count}({list})',
114
+ seedNext: ' 下一步:重启 dsh(或刷新页面)后,预设选择器里应出现「ClearAI」。',
115
+ noSeedLedger: ' 没有播种记账:什么都不做(不知道哪些是我们播的,就不删)。',
116
+ unseedRemoved: ' 删掉 {count} 个「我们播的、没被改过」的文件',
117
+ unseedKept: ' **保留** {count} 个你改过的文件(它们现在是你的):{list}',
118
+ unknownCommand: 'unknown command: {command}',
119
+ usage: '用法:clearai-dsh [doctor|install|root-yaml|seed|unseed] [--profile web] [--home <dir>] [--lang zh|en]\n install 还可以:--dist <dir> | --tarball <tgz> | --spec <spec>',
120
+ },
121
+ en: {
122
+ pkgDir: 'package ',
123
+ presetSrc: 'preset src ',
124
+ presetOk: '(agent.cordis.yml ✓)',
125
+ presetMissing: '(no agent.cordis.yml ✗)',
126
+ dshHome: 'DSH_HOME ',
127
+ userRoot: 'user root ',
128
+ rosterVisible: '✓ visible to the roster',
129
+ rosterAbsent: '✗ not there yet (seed it, or paste the root-yaml line into the profile)',
130
+ shadowCopy: ' ⚠️ shadow copy {path} differs from the copy in the package, and the package root shadows it — it will never be read (while the self-check reads it first). Delete it, or keep your changes there under a different id.',
131
+ profileRow: 'profile ',
132
+ profileAbsent: '(does not exist yet: run dsh --profile {profile} once)',
133
+ composeRow: 'composition ',
134
+ composeUnreachable: '(could not ask: dsh unavailable or timed out — the next two rows cannot be decided)',
135
+ hostRow: 'host row ',
136
+ hostIn: 'is in the composition ✓',
137
+ hostOut: 'is NOT in the composition (run dsh plugin --profile {profile} add <this package>)',
138
+ rosterRootRow: 'roster root ',
139
+ rosterNoRoots: 'no roots in the composition (this deployment may not mount the roster)',
140
+ rosterExpr: '{expr}(the dump prints the expression verbatim; it is evaluated at load time)',
141
+ seedLedgerRow: 'seed ledger ',
142
+ ledgerEmpty: '(empty)',
143
+ ledgerCount: '{count} entries · {path}',
144
+ doctorHint: '\ndoctor only reads. It will not change your profile and will not seed anything for you.',
145
+ installHeader: '[install] {name}@{version}',
146
+ installWhat: 'what ',
147
+ installWhere: 'where ',
148
+ installWho: 'who runs it ',
149
+ hostRowShort: 'host row ',
150
+ sourceSpec: 'the spec you gave',
151
+ sourceTarball: 'local tarball',
152
+ sourceDir: 'local directory',
153
+ sourceRegistry: 'registry',
154
+ routeNpx: 'npx --yes @deepseek-ai/dsh (no dsh on PATH)',
155
+ pnpmMissing: '\n✗ pnpm is not on PATH, and it is how DSH manages a profile: `dsh plugin …` forwards to it.\n Install one and come back: npm install -g pnpm (or your package manager, e.g. brew install pnpm).\n Do not take the corepack enable shortcut — that installs a version *forwarder*, not pnpm, and the\n corepack bundled with current Node may fetch a pnpm it is unable to launch.\n This verb deliberately does not edit the profile by hand: that would be a second implementation of the host\'s reconcile, and yours is the deployment that breaks when it drifts.',
156
+ installFailed: '\n✗ install failed: see the output above.',
157
+ hostUnknown: '(could not ask the composition: CLI unavailable or timed out — whether it landed cannot be decided here; run doctor)',
158
+ hostAbsent: '**NOT in the composition** — it installed, but the composition does not show it (run doctor)',
159
+ nextStep: '\n next restart dsh web (both halves are cached in the running process by module URL, so a browser refresh is not enough), then pick ClearAI in the preset picker.',
160
+ uninstall: ' uninstall dsh plugin --profile {profile} remove {name}(npx works too)',
161
+ rootYamlComment1: '# ClearAI preset root (printed by clearai-dsh; the path is already absolute)',
162
+ rootYamlComment2: ' # ⚠️ a patch layer **replaces the whole config**: these keys must match the ones in your deployment, or they are lost.',
163
+ rootYamlComment3: ' # Run `dsh --profile <p> --dump-config | grep -A 20 agent-presets` first and paste over the current values.',
164
+ noPresetInPackage: '✗ no preset in the package: {path}',
165
+ seededTo: ' seeded to {dest}',
166
+ seedCounts: ' added {seeded} · refreshed {refreshed}',
167
+ seedDrifted: ' · **kept your edits** {count}({list})',
168
+ seedNext: ' next: restart dsh (or refresh the page) — ClearAI should now appear in the preset picker.',
169
+ noSeedLedger: ' nothing was seeded: doing nothing (without the ledger there is no way to tell which files are ours, so nothing is deleted).',
170
+ unseedRemoved: ' removed {count} file(s) we seeded and you did not edit',
171
+ unseedKept: ' **kept** {count} file(s) you edited (they are yours now): {list}',
172
+ unknownCommand: 'unknown command: {command}',
173
+ usage: 'usage: clearai-dsh [doctor|install|root-yaml|seed|unseed] [--profile web] [--home <dir>] [--lang zh|en]\n install also takes: --dist <dir> | --tarball <tgz> | --spec <spec>',
174
+ },
175
+ }
176
+
177
+ /** `{name}` 是占位符;需要拼计数与列表的句子写成函数(见 doctor / seed / unseed)。 */
178
+ function t(key, params) {
179
+ const text = TEXT[lang][key] ?? TEXT.zh[key]
180
+ if (text === undefined) throw new Error(`missing text: ${key}`)
181
+ return typeof text === 'function' ? text(params ?? {}) : text.replace(/\{(\w+)\}/g, (_, name) => String(params?.[name] ?? ''))
182
+ }
183
+
47
184
  /** 递归列出文件(相对路径 → sha256 前 16 位)。只为记账,不为校验完整性。 */
48
185
  function hashTree(root) {
49
186
  const out = {}
@@ -128,14 +265,14 @@ function composeQuery(profileName, env) {
128
265
 
129
266
  function doctor() {
130
267
  const rows = []
131
- rows.push(`包目录 ${PKG_DIR}`)
132
- rows.push(`预设源 ${PRESET_SRC}${existsSync(join(PRESET_SRC, 'agent.cordis.yml')) ? '(agent.cordis.yml ✓)' : '(缺 agent.cordis.yml ✗)'}`)
133
- rows.push(`DSH_HOME ${DSH_HOME}`)
268
+ rows.push(`${t('pkgDir')}${PKG_DIR}`)
269
+ rows.push(`${t('presetSrc')}${PRESET_SRC}${existsSync(join(PRESET_SRC, 'agent.cordis.yml')) ? t('presetOk') : t('presetMissing')}`)
270
+ rows.push(`${t('dshHome')}${DSH_HOME}`)
134
271
  const roots = rosterRoots()
135
272
  for (const root of roots) {
136
273
  const dir = join(root, PRESET_ID)
137
274
  const present = existsSync(join(dir, 'agent.cordis.yml'))
138
- rows.push(`用户根 ${dir} ${present ? '✓ 名册看得见' : '✗ 还没有(用 seed 播种,或把 root-yaml 那一行粘进 profile)'}`)
275
+ rows.push(`${t('userRoot')}${dir} ${present ? t('rosterVisible') : t('rosterAbsent')}`)
139
276
  /**
140
277
  * 用户根里那份**会不会被包的 root 遮住**。
141
278
  *
@@ -152,10 +289,10 @@ function doctor() {
152
289
  } catch {
153
290
  same = false
154
291
  }
155
- if (!same) rows.push(` ⚠️ 影子副本 ${theirs} 与包里的那份**不一致**,而它被包的 root 遮住、永远不会被读到(自检却会优先读它)。删掉它,或用它来承载你自己的改动并换一个 id。`)
292
+ if (!same) rows.push(t('shadowCopy', { path: theirs }))
156
293
  }
157
294
  const profileDir = join(DSH_HOME, 'profiles', profile)
158
- rows.push(`profile ${profileDir}${existsSync(profileDir) ? '' : '(不存在:先跑一次 dsh --profile ' + profile + ')'}`)
295
+ rows.push(`${t('profileRow')}${profileDir}${existsSync(profileDir) ? '' : t('profileAbsent', { profile })}`)
159
296
  /**
160
297
  * 宿主行与名册 root **问组合**,不问某个文件(2026-09-11 修:装出来的形态下这两行来自
161
298
  * **包的补丁层**,不在 profile 的 cordis.patch.yml 里 —— 读文件的写法会误报「还没挂」)。
@@ -163,18 +300,18 @@ function doctor() {
163
300
  */
164
301
  const composed = composeQuery(profile, { ...process.env, DSH_HOME })
165
302
  if (composed === null) {
166
- rows.push('组合 (问不到:dsh 不可用或超时 —— 下面两条无法判定)')
303
+ rows.push(`${t('composeRow')}${t('composeUnreachable')}`)
167
304
  } else {
168
305
  const rowIds = [...composed.matchAll(/^- id: (\S+)$/gm)].map((match) => match[1])
169
- rows.push(`宿主行 ${rowIds.includes('clearai-host') ? '在组合里 ✓' : '不在组合里(先 dsh plugin --profile ' + profile + ' add <本包>)'}`)
306
+ rows.push(`${t('hostRow')}${rowIds.includes('clearai-host') ? t('hostIn') : t('hostOut', { profile })}`)
170
307
  const roster = /- id: agent-presets[\s\S]{0,600}?\n\s*- path:[^\n]*/.exec(composed)
171
308
  const expr = roster === null ? null : roster[0].split('\n').pop().trim()
172
- rows.push(`名册 root ${expr === null ? '组合里没看到 roots(这个部署可能不挂名册)' : `${expr}(dump 打的是表达式原文,求值在装载时)`}`)
309
+ rows.push(`${t('rosterRootRow')}${expr === null ? t('rosterNoRoots') : t('rosterExpr', { expr })}`)
173
310
  }
174
311
  const ledger = readLedger()
175
- rows.push(`播种记账 ${Object.keys(ledger).length === 0 ? '(空)' : `${Object.keys(ledger).length} 个条目 · ${LEDGER}`}`)
312
+ rows.push(`${t('seedLedgerRow')}${Object.keys(ledger).length === 0 ? t('ledgerEmpty') : t('ledgerCount', { count: Object.keys(ledger).length, path: LEDGER })}`)
176
313
  for (const row of rows) console.log(` ${row}`)
177
- console.log('\n提示:doctor 只读;它不会替你改 profile,也不会替你播种。')
314
+ console.log(t('doctorHint'))
178
315
  }
179
316
 
180
317
  /**
@@ -204,28 +341,24 @@ function install() {
204
341
  * npx 缓存还在。`--dist` / `--tarball` / `--spec` 是给开发与 E2E 用的另一条入口。
205
342
  */
206
343
  const spec = explicit ?? (tarball !== null ? resolve(tarball) : dist !== null ? `file:${resolve(dist)}` : `${manifest.name}@${manifest.version}`)
207
- const source = explicit !== null ? '你给的 spec' : tarball !== null ? '本地 tarball' : dist !== null ? '本地目录' : 'registry'
344
+ const source = explicit !== null ? t('sourceSpec') : tarball !== null ? t('sourceTarball') : dist !== null ? t('sourceDir') : t('sourceRegistry')
208
345
  /** PATH 上有 `dsh` 就用它;没有就用 npx 取官方 CLI(`--yes`:一键安装不该卡在一个确认提示上)。 */
209
346
  const dshPath = findOnPath('dsh')
210
- const route = dshPath === null ? 'npx --yes @deepseek-ai/dsh(PATH 上没有 dsh)' : dshPath
347
+ const route = dshPath === null ? t('routeNpx') : dshPath
211
348
  /**
212
349
  * 子进程**继承 stdio**:安装进度、以及 CLI 那句 `initialized profile …` 都如实流到用户眼前;
213
350
  * 失败时他看到的也是真实报错,而不是我截出来的尾巴。
214
351
  */
215
352
  const dsh = (args) => (dshPath === null ? spawnSync('npx', ['--yes', '@deepseek-ai/dsh', ...args], { env, stdio: 'inherit', timeout: 900000 }) : spawnSync(dshPath, args, { env, stdio: 'inherit', timeout: 900000 }))
216
353
 
217
- console.log(`【安装】${manifest.name}@${manifest.version}`)
218
- console.log(` 装什么 ${spec}(${source})`)
219
- console.log(` 装到哪 ${profileDir}`)
220
- console.log(` 谁来跑 ${route}`)
354
+ console.log(t('installHeader', { name: manifest.name, version: manifest.version }))
355
+ console.log(` ${t('installWhat')}${spec}(${source})`)
356
+ console.log(` ${t('installWhere')}${profileDir}`)
357
+ console.log(` ${t('installWho')}${route}`)
221
358
 
222
359
  const pnpmPath = findOnPath('pnpm')
223
360
  if (pnpmPath === null) {
224
- console.error('\n✗ PATH 上没有 pnpm,而 DSH 管理一个 profile 就是靠它:`dsh plugin …` 把参数转发给 pnpm。')
225
- console.error(' 装一个再来:npm install -g pnpm(或用系统包管理器,如 brew install pnpm)。')
226
- console.error(' 别用 corepack enable 抄近路 —— 它装的是版本**转发器**而不是 pnpm,而当前 Node 自带的')
227
- console.error(' 那份 corepack 可能下载一个它自己启动不了的 pnpm。')
228
- console.error(' 这里刻意不手工改 profile:那等于把宿主的 reconcile 抄成第二份实现,与宿主漂移时坏的是你的部署。')
361
+ console.error(t('pnpmMissing'))
229
362
  process.exit(1)
230
363
  }
231
364
 
@@ -238,28 +371,28 @@ function install() {
238
371
  */
239
372
  const added = dsh(['plugin', '--profile', profile, 'add', spec])
240
373
  if (added.status !== 0) {
241
- console.error('\n✗ 安装失败:见上面的输出。')
374
+ console.error(t('installFailed'))
242
375
  process.exit(1)
243
376
  }
244
377
 
245
378
  const composed = composeQuery(profile, env)
246
379
  if (composed === null) {
247
- console.log(' 宿主行 (问不到组合:CLI 不可用或超时 —— 装没装进去,从这里确认不了;用 doctor 再看)')
380
+ console.log(` ${t('hostRowShort')}${t('hostUnknown')}`)
248
381
  } else {
249
382
  const inCompose = /^- id: clearai-host$/m.test(composed)
250
- console.log(` 宿主行 ${inCompose ? '在组合里 ✓' : '**不在组合里** —— 装是装上了,但组合里没看到它(用 doctor 查)'}`)
383
+ console.log(` ${t('hostRowShort')}${inCompose ? t('hostIn') : t('hostAbsent')}`)
251
384
  }
252
- console.log('\n 下一步 重启 dsh web(两半都在进程里按模块 URL 缓存,只刷新浏览器不够),然后在预设选择器里选 ClearAI。')
253
- console.log(` 卸载 dsh plugin --profile ${profile} remove ${manifest.name}(同样可以冠 npx)`)
385
+ console.log(t('nextStep'))
386
+ console.log(t('uninstall', { profile, name: manifest.name }))
254
387
  }
255
388
 
256
389
  function rootYaml() {
257
390
  const line = [
258
- '# ClearAI 预设的 root(由 clearai-dsh 的 bin 打印,路径已算成绝对路径)',
391
+ t('rootYamlComment1'),
259
392
  '- id: \'@deepseek-ai/dsh-agent-presets\'',
260
393
  ' config:',
261
- ' # ⚠️ 补丁层会**替换整份 config**:下面这些键必须与你部署里那份一致,否则会丢。',
262
- ' # 先 `dsh --profile <p> --dump-config | grep -A 20 agent-presets` 看一眼当前值再粘。',
394
+ t('rootYamlComment2'),
395
+ t('rootYamlComment3'),
263
396
  ' default: standard',
264
397
  ' includeShippedRoot: true',
265
398
  ' includeUserRoot: true',
@@ -272,7 +405,7 @@ function rootYaml() {
272
405
 
273
406
  function seed() {
274
407
  if (!existsSync(join(PRESET_SRC, 'agent.cordis.yml'))) {
275
- console.error(`✗ 包里没有预设:${PRESET_SRC}`)
408
+ console.error(t('noPresetInPackage', { path: PRESET_SRC }))
276
409
  process.exit(1)
277
410
  }
278
411
  const root = value('root', rosterRoots()[0])
@@ -303,16 +436,16 @@ function seed() {
303
436
  }
304
437
  before[PRESET_ID] = { files: next, at: new Date().toISOString(), dest }
305
438
  writeLedger(before)
306
- console.log(` 播种到 ${dest}`)
307
- console.log(` 新增 ${seeded.length} · 刷新 ${refreshed.length}${drifted.length === 0 ? '' : ` · **你改过、没覆盖** ${drifted.length}(${drifted.slice(0, 5).join('、')})`}`)
308
- console.log(' 下一步:重启 dsh(或刷新页面)后,预设选择器里应出现「ClearAI」。')
439
+ console.log(t('seededTo', { dest }))
440
+ console.log(`${t('seedCounts', { seeded: seeded.length, refreshed: refreshed.length })}${drifted.length === 0 ? '' : t('seedDrifted', { count: drifted.length, list: drifted.slice(0, 5).join(lang === 'zh' ? '、' : ', ') })}`)
441
+ console.log(t('seedNext'))
309
442
  }
310
443
 
311
444
  function unseed() {
312
445
  const ledger = readLedger()
313
446
  const entry = ledger[PRESET_ID]
314
447
  if (entry === undefined) {
315
- console.log(' 没有播种记账:什么都不做(不知道哪些是我们播的,就不删)。')
448
+ console.log(t('noSeedLedger'))
316
449
  return
317
450
  }
318
451
  const dest = entry.dest ?? join(rosterRoots()[0], PRESET_ID)
@@ -331,8 +464,8 @@ function unseed() {
331
464
  }
332
465
  delete ledger[PRESET_ID]
333
466
  writeLedger(ledger)
334
- console.log(` 删掉 ${removed.length} 个「我们播的、没被改过」的文件`)
335
- if (kept.length > 0) console.log(` **保留** ${kept.length} 个你改过的文件(它们现在是你的):${kept.slice(0, 5).join('、')}`)
467
+ console.log(t('unseedRemoved', { count: removed.length }))
468
+ if (kept.length > 0) console.log(t('unseedKept', { count: kept.length, list: kept.slice(0, 5).join(lang === 'zh' ? '、' : ', ') }))
336
469
  // 空目录收掉(只收我们知道的那些;非空绝不动)
337
470
  try {
338
471
  if (existsSync(dest) && readdirSync(dest).length === 0) rmSync(dest, { recursive: true })
@@ -348,10 +481,6 @@ else if (command === 'seed') seed()
348
481
  else if (command === 'unseed') unseed()
349
482
  else if (command === 'version' || flag('version')) console.log(JSON.parse(readFileSync(join(PKG_DIR, 'package.json'), 'utf8')).version)
350
483
  else {
351
- console.error(
352
- `unknown command: ${command}\n` +
353
- '用法:clearai-dsh [doctor|install|root-yaml|seed|unseed] [--profile web] [--home <dir>]\n' +
354
- ' install 还可以:--dist <dir> | --tarball <tgz> | --spec <spec>',
355
- )
484
+ console.error(`${t('unknownCommand', { command })}\n${t('usage')}`)
356
485
  process.exit(2)
357
486
  }
package/cordis.patch.yml CHANGED
@@ -12,28 +12,21 @@
12
12
  - id: clearai-host
13
13
  name: 'clearai-dsh'
14
14
 
15
- # 预设(agent.cordis.yml)不在补丁层 —— 它由名册(roster)从 preset root 扫描。
16
- # 预设的注册方式有三种候选(补丁层重述名册行 / 播种到用户根 / 打印待粘贴的行),
17
- # 由 S2 的干净 profile 探针决定,不在这里拍脑袋。
18
-
19
- # ── 预设的 root:现场算出来,零安装期写入(S2 探针的结论)──────────────────────
15
+ # ── 预设不在这里声明 root:宿主 ≥0.1.7-alpha.1 起名册不再扫目录 ────────────────
16
+ #
17
+ # 0.2.3 及以前走的是「root 目录」机制:宿主自带 `- id: agent-presets` 行,本文件覆盖它的
18
+ # config,把 root 指到包内 presets/,归属落在 system 信任层。
19
+ #
20
+ # 0.1.7-alpha.1 起宿主把这一整套换成了**组合里的声明行**:
21
+ # · `- id: agent-preset-registry`(@deepseek-ai/dsh-agent-preset-registry)只剩
22
+ # default / selectedDefault —— `includeShippedRoot` / `includeUserRoot` / `roots` 全部消失;
23
+ # · 每个预设是一条 `- id: preset-<id>` 行(@deepseek-ai/dsh-agent-preset),
24
+ # `config.plugins` 里放整份插件列表。
20
25
  #
21
- # 名册(roster)只从 **root 目录**扫预设,而后端包没法自己声明一个 root —— 这是整个打包里
22
- # 唯一没有现成通道的一环。S2 在干净 profile 上把三件事都量过了:
23
- # · `!!js` 在**补丁行**里确实会被求值(与自带补丁用 `!!js dshHomePath(...)` 同一条路);
24
- # · 但 `baseUrl` 在补丁层里是**profile 目录**,不是本包目录(`resolvedRoots` 实测:
25
- # `path:"BASEURL=file:///…/profiles/clearai-test/"`)——所以「拿 baseUrl 当包目录」不成立;
26
- # · 而包在 profile 里的位置是**可预测的**:`dsh plugin add` 就是把它装进 profile 的
27
- # `node_modules/`。于是 root 可以现场算出来:`<profile>/node_modules/clearai-dsh/presets/`。
26
+ # 所以旧的那段 `- id: agent-presets` 覆盖在这里**没有落点**(该 id 已不存在于新宿主),
27
+ # 已于 2026-09-28 删掉 —— 留着只会让下一个人以为预设还靠目录扫描。
28
+ # 依据与实测:`lab/release/0.2.3-release-blocker.md`、`lab/adapter/0.2.4-design.md`。
28
29
  #
29
- # 于是不需要任何安装期写入(不改用户的文件、不需要 bin 先跑一遍),归属也落在 system 信任层。
30
- # 想改预设的人仍然有两条路:`clearai-dsh seed`(播种到用户根,可改可回滚)或复制成新 id。
31
- - id: agent-presets
32
- config:
33
- # ⚠️ 补丁层替换**整份** config:下面这些键要与部署里那份保持一致(上游将来加字段要跟上)。
34
- default: standard
35
- includeShippedRoot: true
36
- includeUserRoot: true
37
- roots:
38
- - path: !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('node_modules/clearai-dsh/presets/', baseUrl))"
39
- trust: system
30
+ # 预设本体现在由 `presets/clearai/clearai.patch.yml` 承载(构建期由 preset/agent.cordis.yml
31
+ # 生成,经 package.json 的 `dsh.bundle.patch` 数组挂上)。想改预设的人仍有两条路:
32
+ # `clearai-dsh seed`(播种到用户根,可改可回滚)或复制成新 id。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clearai-dsh",
3
- "version": "0.2.1",
3
+ "version": "0.2.4",
4
4
  "description": "ClearAI: The Epistemic Loop, native to DSH.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -15,7 +15,8 @@
15
15
  "./client": "./lib/client.js",
16
16
  "./cordis.patch.yml": "./cordis.patch.yml",
17
17
  "./package.json": "./package.json",
18
- "./invariant": "./lib/invariant.js"
18
+ "./invariant": "./lib/invariant.js",
19
+ "./presets/*": "./presets/*"
19
20
  },
20
21
  "bin": {
21
22
  "clearai-dsh": "bin/clearai.mjs"
@@ -35,15 +36,14 @@
35
36
  "node": ">=22"
36
37
  },
37
38
  "dependencies": {
38
- "@xyflow/react": "^12.11.6",
39
- "docx": "^9.7.1",
40
- "graphology": "^0.26.0",
41
- "graphology-layout-forceatlas2": "^0.10.1",
42
39
  "zod": "^4.6.1"
43
40
  },
44
41
  "dsh": {
45
42
  "bundle": {
46
- "patch": "./cordis.patch.yml"
43
+ "patch": [
44
+ "./cordis.patch.yml",
45
+ "./presets/clearai/clearai.patch.yml"
46
+ ]
47
47
  },
48
48
  "client": {
49
49
  "platform": "web",
@@ -55,8 +55,11 @@
55
55
  }
56
56
  },
57
57
  "keywords": [
58
+ "dsh",
58
59
  "dsh-plugin",
59
60
  "deepseek-harness",
61
+ "cordis",
62
+ "plugin",
60
63
  "clearai",
61
64
  "agent-preset",
62
65
  "evidence",
@@ -77,7 +80,11 @@
77
80
  "shots": "node tools/graph-shots.mjs"
78
81
  },
79
82
  "devDependencies": {
83
+ "@xyflow/react": "^12.11.6",
84
+ "docx": "^9.7.1",
80
85
  "esbuild": "^0.28.2",
86
+ "graphology": "^0.26.0",
87
+ "graphology-layout-forceatlas2": "^0.10.1",
81
88
  "playwright-core": "^1.63.0"
82
89
  }
83
90
  }
@@ -132,7 +132,12 @@
132
132
  # 达成→收回,触礁/放弃→置阻塞,重启后自动补防。
133
133
  # 配置项就是机制的旋钮:
134
134
  - id: clearai-kernel
135
- name: ./plugins/clearai-kernel.js
135
+ # 用**包内子路径**而不是 `./plugins/…`:宿主 ≥0.1.7-alpha.1 起,预设是声明行
136
+ # (presets/clearai/clearai.patch.yml)里的 plugins 列表,相对基准不再是本文件所在目录 ——
137
+ # 实测 `./plugins/clearai-kernel.js` 会以「never started」出现在名册的 broken 里。
138
+ # 包名 + 子路径从 profile 的 node_modules 解析,与宿主解析其它插件名同一条路,
139
+ # 由 package.json 的 `"./presets/*"` 导出放行。
140
+ name: 'clearai-dsh/presets/clearai/plugins/clearai-kernel.js'
136
141
  config:
137
142
  # 连拦阈值:同一件事连续冲闸这么多次没过,计划置 blocked、停下等人。
138
143
  # 默认 3;这里写 2 是产品立场「人就在旁边,早点回来问」。它是**质量闸,不随运行档变**。
@@ -217,7 +222,8 @@
217
222
  # 一个字都不落账) + 一个呈审捷径(plan-review,把「重新呈审」steer 给模型,
218
223
  # 授权记号仍走 RequestPlanReview 工具落账——命令处理器没有变更通道,这是权威边界)。
219
224
  - id: clearai-commands
220
- name: ./plugins/commands.js
225
+ # 同上:改用包内子路径(见 clearai-kernel 那一行的说明)。
226
+ name: 'clearai-dsh/presets/clearai/plugins/commands.js'
221
227
 
222
228
  # ── 工作方式(非权威能力,交还原生) ──────────────────────────────────────────
223
229
 
@@ -260,8 +266,13 @@
260
266
  toolName: subagent_fork
261
267
  backgroundMode: continuable
262
268
 
263
- - id: workflow-worker-thread
264
- name: '@deepseek-ai/dsh-workflow-worker-thread'
269
+ # 2026-09-28:workflow 引擎换代 —— 旧宿主是 `dsh-workflow-worker-thread`,
270
+ # 宿主 ≥0.1.7-alpha.1 里换成 `dsh-workflow-ptc`(前者已下线,是另一支实现)。
271
+ # 它必须与下面两条工具**同处这个 `isolate: { workflowEngine: true }` 的 realm**,
272
+ # 否则 tool-workflow / tool-ralph 会一直「waiting for workflowEngine」——
273
+ # 名册会给这个预设记一条 broken(实测实录)。
274
+ - id: workflow-ptc
275
+ name: '@deepseek-ai/dsh-workflow-ptc'
265
276
  config:
266
277
  provider: spawn
267
278
 
@@ -0,0 +1,332 @@
1
+ # 自动生成,不要手改 —— 源是 preset/agent.cordis.yml 与 preset/preset.yml,
2
+ # 由 tools/build-package.mjs 装配(包 = 源的纯函数;verify-package 会现场重建再比对)。
3
+ #
4
+ # 宿主 ≥0.1.7-alpha.1 的 agent-preset-registry 读的就是它:
5
+ # 预设 = 组合里的一条声明行,plugins 是下面这份插件列表。
6
+ # 0.2.3 及以前靠 root 目录扫描,那条路在新宿主上已经不存在了。
7
+
8
+ - insert:
9
+ - id: preset-clearai
10
+ name: '@deepseek-ai/dsh-agent-preset'
11
+ config:
12
+ id: clearai
13
+ name: ClearAI
14
+ description: |-
15
+ 利用认识论循环构建可信本体。Build a trustworthy ontology through the epistemic loop.
16
+ order: 10
17
+ plugins:
18
+ # The `clearai` agent preset: ClearAI 的「单循环 + 事实边界」认识论,作为一个可挂载的会话组合。
19
+ #
20
+ # 这份文件是一份 AGENT-PLANE 组合。名册(roster)把它挂在**一个 standing scope** 下,
21
+ # 每个选择它的会话以 scope 父子关系加入;因此这里的工具与提示词段覆盖每个加入的 agent,
22
+ # 而每个会话自己的状态按 Session/Agent 键在插件内部区分。
23
+ #
24
+ # 平面规则(为什么这样分):
25
+ # · 认识论属于「一个会话的能力集」→ 预设平面(本文件 + ./plugins/clearai-kernel.js)
26
+ # · 沙箱、审批、文件策略、模型路由、子代理注册表、持久化 → 宿主平面,**本文件一行都不碰**
27
+ # (DSH 的硬约束:预设 exactly as privileged as the plugins it names;让它放宽自己的关押就废掉了关押)
28
+ # · 循环面板的浏览器 UI 必须在宿主平面(客户端模块扫描只扫宿主 Loader 的行),
29
+ # 所以它不在本文件里,而在 profile 的 cordis.patch.yml 一行。
30
+ #
31
+ # 本内核**不发布任何服务**(工具、guard、提示词段、web 路由都由同一个插件在自身 fiber 内注册),
32
+ # 因此这一行可以坦然坐在预设里,不需要 isolate realm。realm 是给「预设真的拥有一个服务」用的。
33
+
34
+ # ── 身份 ────────────────────────────────────────────────────────────────────
35
+
36
+ # 预设自己的 persona,覆盖部署默认。`{{model}}`/`{{cwd}}` 由 agent 自己的路由与工作区解析。
37
+ - id: persona
38
+ name: '@deepseek-ai/dsh-persona'
39
+ config:
40
+ prefix: |-
41
+ 你是 ClearAI 的主人格,运行在 DeepSeek Harness 上。
42
+
43
+ 你以**单循环**工作:计划 → 执行 → 观察 → 反思,一个循环推进,不做多 Agent 编排。子角色(评估者)由系统按触发派生,不是自由委派。
44
+
45
+ 你的判断是智能的部分:理解材料、提出假设、选择路线、判断哪条证据更可信。事实的部分由系统持有:什么算完成、进度是多少、一个事实能不能写进知识库、谁的裁决有效——这些都不由你声明,只由系统算出来。
46
+
47
+ 你不靠提示词约束自己:完成要过观测准入,推进只能通过唯一完成动词,L3 以上的裁决由独立评估者写。你要做的是把智能用在判断上,而不是用在描述状态上。
48
+ suffix: 你的工作目录是 {{cwd}}。工作区就是用户的文件夹;`clear/` 是系统与外脑的目录,其余目录都是用户的。
49
+
50
+ - id: agent-instructions
51
+ name: '@deepseek-ai/dsh-agent-instructions'
52
+ config:
53
+ maxBytes: 65536
54
+ # 项目章程走**原生指令文件**这一条:空工作区铺下的 `PROJECT.md` 会被宿主当作
55
+ # 工作区指令基线注入(内容变了整份替换、字节有预算),我们一行代码都不用写。
56
+ # 候选名按 ClearAI 的 `taxonomy.json:special_files.constitution` 补一个。
57
+ instructionFileCandidates:
58
+ - PROJECT.md
59
+ - AGENTS.md
60
+ - CLAUDE.md
61
+
62
+ # ── shell ───────────────────────────────────────────────────────────────────
63
+
64
+ - id: tool-bash
65
+ name: '@deepseek-ai/dsh-tool-bash'
66
+ disabled: !!js process.platform === 'win32'
67
+
68
+ - id: tool-pwsh
69
+ name: '@deepseek-ai/dsh-tool-pwsh'
70
+ disabled: !!js process.platform !== 'win32'
71
+
72
+ # ── 文件系统 ────────────────────────────────────────────────────────────────
73
+
74
+ # 两行都只注册进宿主 `tools` 注册表、不发布任何服务,所以不需要 realm。
75
+ # `fs` 服务与它的观察策略留在宿主:读前先看(observation policy)是宿主不变量,不是本预设的选择。
76
+ - id: tool-fs
77
+ name: '@deepseek-ai/dsh-tool-fs'
78
+
79
+ - id: tool-fs-search
80
+ name: '@deepseek-ai/dsh-tool-fs-search'
81
+ config:
82
+ sampleOverCapGlobResults: false
83
+
84
+ # ── 后台任务 ────────────────────────────────────────────────────────────────
85
+
86
+ # 只挂模型面的控制工具。任务注册表留在宿主(bash 工具用 ctx.get 解析它,预设内部的作用域看不见),
87
+ # 它本来就按 owning agent 键,一个宿主实例服务所有会话。
88
+ - id: tool-jobs
89
+ name: '@deepseek-ai/dsh-tool-jobs'
90
+
91
+ # ── 技能(外脑) ─────────────────────────────────────────────────────────────
92
+
93
+ # 技能注册表在宿主并按 scope 分层:这两行注册进**本预设的层**,所以不需要 realm。
94
+ # `customSkillDirs` 指向本预设自己的 skills/ —— 哲学文本随预设走,`baseUrl` 就是预设目录,
95
+ # 因此整份预设被复制到任何部署,技能都跟着到位。
96
+ - id: skill-filesystem
97
+ name: '@deepseek-ai/dsh-skill-filesystem'
98
+ config:
99
+ customSkillDirs:
100
+ - !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('skills/', baseUrl))"
101
+
102
+ - id: tool-skill
103
+ name: '@deepseek-ai/dsh-tool-skill'
104
+
105
+ # ── 压缩(上下文是受控资源) ─────────────────────────────────────────────────
106
+
107
+ # `compaction-basic` 通过 ctx.get 读 `toolResultPruner`,所以剪枝器必须与它同一个 realm。
108
+ # `tokenMeter` 刻意在 realm 之外:它在宿主,按 Session 折叠,拥有浏览器读的上下文计量投影单元。
109
+ - id: compaction
110
+ name: cordis:group
111
+ group: true
112
+ isolate:
113
+ compaction: true
114
+ toolResultPruner: true
115
+ config:
116
+ - id: compaction-basic
117
+ name: '@deepseek-ai/dsh-compaction-basic'
118
+
119
+ - id: command-compact
120
+ name: '@deepseek-ai/dsh-command-compact'
121
+
122
+ - id: tool-result-pruner
123
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
124
+ config:
125
+ thresholdChars: 8192
126
+ headChars: 4096
127
+ tailChars: 1024
128
+
129
+ # ── 认识论内核(本预设的灵魂) ───────────────────────────────────────────────
130
+
131
+ # 29 件意图工具(目标 2 + 计划 8 + 世界线 6 + 侦察 2 + 外脑 2 + 账本 2 + 领域语言 7,见 `MECHANISM_TOOLS`)
132
+ # + 一个 guard + 每回合派生的运行态卡 + 只增不删的台账 + 面板数据路由。
133
+ # 逐条对照 docs/loop-philosophy.md 的五条哲学,见 ./plugins/clearai-kernel.js 的文件头。
134
+ #
135
+ # 外脑:**读侧全走宿主原生**——`./plugins/brain.js` 只把工作区投影成技能条目
136
+ # (`clear/skills/**` → 目录条目;`clear/memory/**` → 一个**虚拟条目** `project-memory`,
137
+ # 正文现算所以没有「索引过期」),目录注入与按需加载由宿主的 `tool-skill` 承担(digest 变了才注入)。
138
+ # 自建的只有写侧两件:`SaveSkill`(默认候选态)与 `WriteMemory`(字段校验 + 标题去重)。
139
+ #
140
+ # 世界线(ForkPlan):互斥方案各占一份自己的工作副本,收敛是**算术**——拿事先登记的尺子
141
+ # 对读数排序,采纳最优的,落选的全部保留。算不出来就停下问人,绝不退化成随便挑一条。
142
+ # git 世界线已落地(A+B 两档):用户仓库可用就地开分支 + worktree,不可用退到旁路账本仓库。
143
+ #
144
+ # 装配是**清单驱动**的:工具面、提示词段、机制开关全部查表,代码里不出现模式名。
145
+ #
146
+ # 续跑:需要继续时,内核在宿主 `goals` 服务上布防一枚**续跑令牌**——计划收尾而目标未达成时,
147
+ # 由宿主的回合驱动自己开下一轮。**要不要继续由门状态决定**(未授权 / 等裁决 / 有人在等 → 停),
148
+ # 与运行档无关。宿主目标只当**驱动器**,内核从不读它做判断(事实仍然只从投影里算出来);
149
+ # 达成→收回,触礁/放弃→置阻塞,重启后自动补防。
150
+ # 配置项就是机制的旋钮:
151
+ - id: clearai-kernel
152
+ # 用**包内子路径**而不是 `./plugins/…`:宿主 ≥0.1.7-alpha.1 起,预设是声明行
153
+ # (presets/clearai/clearai.patch.yml)里的 plugins 列表,相对基准不再是本文件所在目录 ——
154
+ # 实测 `./plugins/clearai-kernel.js` 会以「never started」出现在名册的 broken 里。
155
+ # 包名 + 子路径从 profile 的 node_modules 解析,与宿主解析其它插件名同一条路,
156
+ # 由 package.json 的 `"./presets/*"` 导出放行。
157
+ name: 'clearai-dsh/presets/clearai/plugins/clearai-kernel.js'
158
+ config:
159
+ # 连拦阈值:同一件事连续冲闸这么多次没过,计划置 blocked、停下等人。
160
+ # 默认 3;这里写 2 是产品立场「人就在旁边,早点回来问」。它是**质量闸,不随运行档变**。
161
+ #
162
+ # 注意:同一个键在这个 config 里只能出现一次——YAML 的重复键会让 yaml 包直接抛错,
163
+ # 而 DSH 的加载器是**静默取值**的,所以重复键会以"另一个值生效"的形式骗过所有人。
164
+ blockedThreshold: 2
165
+ # brief 质量门:低于此长度只警告不阻断(brief.py)
166
+ minBriefChars: 280
167
+ # L4 必须有人放行:用宿主审批瀑布的 ask 实现(ClearAI 文档写了、代码未实现的那一条)
168
+ l4RequiresHumanRelease: true
169
+ # L4 只认外部来源:做的人自己写过的路径不算观测
170
+ l4RejectSelfWritten: true
171
+ # 假设数量下限:首次立目标至少登记 2 条候选假设(0 条一样拦)。候选对比是检验的前提——
172
+ # 只有一个猜想时,「验证」容易退化成找证据支持自己。修订目标不受此限。
173
+ # 内核缺省是 0(=机制中立);**这里写 2 是产品立场**,所以它是硬门,不是文案。
174
+ minHypotheses: 2
175
+ # 知识门:将要升格的命题必须已有断言的形态,否则结案被拒(在派评估者**之前**拦)。
176
+ # 为什么要有它:断言一直是「加法,不是门槛」,于是模型的最优策略就是
177
+ # 「检索 → 总结 → 写报告」——本体图、实体图、认识论三张图都长不出来,
178
+ # 因为**完成函数里没有它们**。让缺口进卡只解决「看得见」,这一道解决「绕不过」。
179
+ # 它与 minHypotheses 是两条不同的立场(开工要有候选对比 / 结论要有形态),所以是两个键。
180
+ # 内核缺省 false(= 断言始终是加法);这里写 true 是产品立场。
181
+ requireTypedPromotion: true
182
+ # deny_rules 十条里可移植的九条(路径越狱那条不搬:宿主沙箱已经拥有它,ClearAI 自己也说「不重复」)
183
+ bashDenyRules: true
184
+ # 独立评估者:spawn = fresh context(fork 会继承历史,做的人与判的人就分不开了)
185
+ auditProvider: spawn
186
+ auditTimeoutMs: 240000
187
+ # 评估者只读:工具面只给 read/glob/grep;provider 不支持时会降级,降级事实写进台账
188
+ auditToolFilter:
189
+ - read
190
+ - glob
191
+ - grep
192
+ # 看图也是读:评估者要能核图表类产物(只在挂了 attachments 的部署里存在,
193
+ # 内核会按工具注册表把它过滤掉,见 resolveToolFace)
194
+ - read_image
195
+ # 每回合派生的运行态卡(只在状态变化时注入:前缀稳定是硬约束)
196
+ runtimeCard: true
197
+ # 运行档:人在场 / 无人值守。**它只决定一件事**:澄清协议装哪一段
198
+ # (槽位 clarification 收敛;两套措辞互斥,永不同时在场)。
199
+ #
200
+ # 它**不决定**下面这些——写清楚是因为这四处曾经被误认为跟着档走:
201
+ # · 计划授权 —— 计划**永远**要人在原生审阅卡上批准(CreatePlan 里没有自动确认分支,
202
+ # confirmed_by 只有 'user' 与 'progress');
203
+ # · 续跑策略 —— 要不要继续由 turnDemand 从**门状态**算出来(未授权/裁决在飞/门开着 → hold);
204
+ # · 续跑轮数 —— 只有一个默认值 128,不按档取;
205
+ # · 面板开关 —— 人门动词 set_autonomy 已摘除,当前没有切换入口。
206
+ #
207
+ # 所以这里写的是**部署初值**,不是"人此刻在不在场"的表示。
208
+ # 真正表达"要不要人"的是**门**:计划待确认 / 等裁决 / 有人在等。
209
+ autonomy: attended
210
+ # 续跑轮数上限。**刻意不写**:不写就回落到 DEFAULT_MAX_AUTO_TURNS(128),在布防点现算。
211
+ # 显式写一个数就两档都用它。这个键存在的意义是**保险丝**:够长到能跑完一件真活,
212
+ # 又短到不会无声烧掉一整夜。
213
+ # 执行者是**原生**:数字传到宿主目标的 maxGoalRounds,到限由 `dsh-goal-round-driver`
214
+ # 自己 block(code='round-limit')——上限真的是机制在执行,不是一句嘱咐。
215
+ # 上下文预算不在这里:那是原生 `dsh-token-meter` + `dsh-compaction-basic` 的活。
216
+ # 贡献表(ClearAI `composition.py` 的装配语义):装配根遍历清单,清单里出现表外的名字
217
+ # 当场抛错——未知机制 / 未知工具 / 未知段 / 已关机制却仍列着它的工具,四种错法都在装配期炸,
218
+ # 而不是静默少装一件工具、等某一轮才发现。
219
+ #
220
+ # 这里只写**机制开关**;tools / sections 缺省 = 目录全量(29 件意图工具、24 段提示词定义、
221
+ # 同一时刻 23 段在场——澄清协议那一段由 autonomy 在两套措辞里收敛)。
222
+ # 这些数字不靠人眼维持:`node tools/verify-truth-table.mjs` 会拿代码算出来的数核对它们。
223
+ # 要裁剪就把 tools 或 sections 显式写出来:
224
+ # tools: [SetGoal, CreatePlan, AdvancePlan, ...] # 名字必须都在工具目录里
225
+ # sections: [clearai/foundation, clearai/loop-contract, clarification, ...]
226
+ # `clarification` 是槽位名;直接写两套措辞里的哪一个会被拒绝(同时在场不可表示)。
227
+ contributions:
228
+ mechanisms:
229
+ goal: true
230
+ plan: true
231
+ worldline: true
232
+ scout: true
233
+ brain: true
234
+ ledger: true
235
+
236
+ # ── 人侧命令(/ 菜单) ────────────────────────────────────────────────────────
237
+
238
+ # 五个命令都是**人侧界面**:四个只读状态窗(goal/plan/evidence/worldline,从账本现算,
239
+ # 一个字都不落账) + 一个呈审捷径(plan-review,把「重新呈审」steer 给模型,
240
+ # 授权记号仍走 RequestPlanReview 工具落账——命令处理器没有变更通道,这是权威边界)。
241
+ - id: clearai-commands
242
+ # 同上:改用包内子路径(见 clearai-kernel 那一行的说明)。
243
+ name: 'clearai-dsh/presets/clearai/plugins/commands.js'
244
+
245
+ # ── 工作方式(非权威能力,交还原生) ──────────────────────────────────────────
246
+
247
+ # todo / 子代理 / workflow / ralph 是**工作方式**,不是认识论:它们一件 clearai 变更都
248
+ # 产不出来(权威边界测试钉死:变更字面量只在内核、入口被标记把守)。模型干活不设限,
249
+ # 但结论要进权威账本,只能由主线自己过观测准入与唯一完成动词。
250
+ - id: tool-todo
251
+ name: '@deepseek-ai/dsh-tool-todo'
252
+ config:
253
+ allowParallelInProgress: true
254
+
255
+ # `subagents` 注册表与 spawn/fork 后端在宿主平面;`workflows` 是谁都不在外面读的服务,
256
+ # 所以到达它的行共享这一个 entry-local realm(与 standard 预设同形)。
257
+ - id: delegation
258
+ name: cordis:group
259
+ group: true
260
+ isolate:
261
+ workflowEngine: true
262
+ config:
263
+ - id: tool-subagent-control
264
+ name: '@deepseek-ai/dsh-tool-subagent-control'
265
+
266
+ - id: tool-subagent-list-agents
267
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
268
+
269
+ # 执行者自选模型:重活可以换模型跑(modelSelectionSettings 出面板设置)。
270
+ - id: tool-subagent
271
+ name: '@deepseek-ai/dsh-tool-subagent'
272
+ config:
273
+ provider: spawn
274
+ toolName: subagent
275
+ modelSelectionSettings: true
276
+ backgroundMode: continuable
277
+
278
+ # fork 不开模型选择:provider/model 与父保持一致,继承的历史才能继续吃 KV Cache。
279
+ - id: tool-subagent-fork
280
+ name: '@deepseek-ai/dsh-tool-subagent'
281
+ config:
282
+ provider: fork
283
+ toolName: subagent_fork
284
+ backgroundMode: continuable
285
+
286
+ # 2026-09-28:workflow 引擎换代 —— 旧宿主是 `dsh-workflow-worker-thread`,
287
+ # 宿主 ≥0.1.7-alpha.1 里换成 `dsh-workflow-ptc`(前者已下线,是另一支实现)。
288
+ # 它必须与下面两条工具**同处这个 `isolate: { workflowEngine: true }` 的 realm**,
289
+ # 否则 tool-workflow / tool-ralph 会一直「waiting for workflowEngine」——
290
+ # 名册会给这个预设记一条 broken(实测实录)。
291
+ - id: workflow-ptc
292
+ name: '@deepseek-ai/dsh-workflow-ptc'
293
+ config:
294
+ provider: spawn
295
+
296
+ - id: tool-workflow
297
+ name: '@deepseek-ai/dsh-tool-workflow'
298
+
299
+ - id: tool-ralph
300
+ name: '@deepseek-ai/dsh-tool-ralph'
301
+ config:
302
+ subagentProvider: spawn
303
+ maxRounds: 64
304
+
305
+ # ── 其余模型面 ──────────────────────────────────────────────────────────────
306
+
307
+ - id: tool-ask-user
308
+ name: '@deepseek-ai/dsh-tool-ask-user'
309
+
310
+ # `web` 服务与它的搜索提供者在宿主:这里只挂模型面的工具。
311
+ - id: tool-web
312
+ name: '@deepseek-ai/dsh-tool-web'
313
+ config:
314
+ fetch: true
315
+ searchTimeoutMs: 60000
316
+
317
+ - id: present
318
+ name: '@deepseek-ai/dsh-tool-present'
319
+
320
+ # ── 刻意不挂的行(是决策,不是遗漏) ─────────────────────────────────────────
321
+ #
322
+ # · `tool-goal` / `command-goal`:DSH 的 goals 服务已经被内核用作**续跑驱动器**
323
+ # (布防/收兵都走它);再挂上模型面的 goal 工具,模型就能自己声明目标状态——
324
+ # 那是第二本账。ClearAI 的目标账只有一本,由内核持有,面板显示的就是它。
325
+ # · `plan-mode`:那是 DSH 的计划模式,与本内核的 CreatePlan/AdvancePlan 是两套计划纪律。
326
+ # 同时挂上就是第二本账。要它就得先决定哪一本是唯一账本。
327
+ #
328
+ # 曾经不挂、现在**挂回来了**的(阶段 5,权威边界由 test/authority-boundary.test.mjs 钉死):
329
+ # · `tool-todo` / `tool-subagent` / `tool-workflow` / `tool-ralph`:这些是**工作方式**,
330
+ # 产不出一条 clearai 变更。「不挂」当年防的是「自己派一个来判自己」——评估者的派遣
331
+ # 在内核里(auditProvider),与模型面的 subagent 是两层;后者派出去的只是干活的,
332
+ # 它的结论要进账本,只能回到主线过观测准入。