clearai-dsh 0.1.5 → 0.1.7
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 +58 -1
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/bin/clearai.mjs +17 -0
- package/lib/client.js +142 -105
- package/lib/fold.js +184 -52
- package/lib/host.js +57 -8
- package/lib/invariant.js +202 -0
- package/package.json +3 -2
- package/presets/clearai/agent.cordis.yml +1 -1
- package/presets/clearai/plugins/clearai-kernel.js +497 -74
- package/presets/clearai/plugins/ontology.js +23 -8
- package/presets/clearai/plugins/prompts.js +3 -3
- package/presets/clearai/preset.yml +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,15 +2,70 @@
|
|
|
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.1.7] — 2026-09-16
|
|
6
|
+
|
|
7
|
+
**同一件事实只有一个来源。** 一轮"按真值表逐条核对 → 按症状打补丁 → 发现自己在打补丁 →
|
|
8
|
+
按权威归属复核 → 删掉补丁"的完整收敛。净效果是**更少的机制、更少的字段、更少的分支**。
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **"Ended" is not "lost": audits now have the same recovery path as scouts.** When the host's subagent catalog says an evaluator's run has ended, the kernel first **recovers the verdict from the child's own session log** (`recoverVerdictFromChildSession`) — the same path `sweepScouts` has always had — and only records `unknown` when recovery fails. Three honest outcomes replace the old single "lost, this verdict will have no result" (which induced re-delivery ⇒ the same evaluation was redone while its result lay on disk): recovered (verdict + audit card land), ended-but-incomplete (`audit_incomplete`), and log-unreadable (`auditor_ended_uncollected`). The settlement text no longer gives advice — whether to retry is a plan-level decision, not the ledger's to make.
|
|
13
|
+
- **The ruler's scale must be a nameable reference, not prose.** `decide_by_scale_not_reference`: the right side of `量 = 口径` must reference **a file that actually exists in the workspace**; prose and dead paths are rejected. `评分 = 按本路线情况评分` passed the old format check and guaranteed nothing. Whether the branches actually *used* the measuring instrument remains the evaluator's job — the string check stops here and no longer pretends to verify.
|
|
14
|
+
- **`runEvaluator`'s three `unknown` exits now push `audit/settled`.** An evaluator that crashed, didn't finish normally, or whose card could not be written previously left only a `audit/dispatched` on the ledger — looking like "still running" when it had already ended. All three paths now settle: a bad ending is still an ending.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **The host-invariant companion now advances state with the production fold.** It previously folded its own index of plans/steps/forks/branches/audits/hypotheses (ten Maps) — a second interpreter that needed two repairs in its first hour because its shapes disagreed with the main projection. It now calls `applyEvent` from `fold.js` on the same events, keeping only the five contract predicates and one `admitted` set (the fold deliberately keeps `admission/checked` as ledger-only). 386 → 190 lines. Two real contract holes fixed in the same pass: dispatch+settle and admission+advance legitimately occur **in the same batch** (the kernel emits them that way), so the judge now accumulates as it iterates.
|
|
19
|
+
- **Turn-end bookkeeping shrank to a workspace snapshot.** The `clearai/turn-ended` event, `turnEnds` state, the in-flight list, and the run-state card's "their conclusions will not come back" (an inference with no evidence — a parent turn ending proves only that the parent turn ended) are all **deleted**. The closing beat (`agent/turn-stopping` / `agent/error`) now only records a ledger commit of the turn's writes — the one thing that belongs to us. `STATE_VERSION` 8 → 9.
|
|
20
|
+
- **Sub-run settlement texts no longer give advice.** "Re-delivering this step dispatches a fresh evaluator" (audits) and "if you need that material, dispatch another scout" (scouts) are gone. The ledger states facts; retry decisions belong to the plan layer.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **The authority map** (`docs/authority-map.zh-CN.md` + English): who produces each fact, where it lives, who consumes it, whether it can be derived — with the four confirmed findings (each now marked as fixed or under review) and the acceptance criterion: one failure class explained in one place; one fact one authority; the same run never re-executed because a read failed; the system can quietly say it does not know.
|
|
25
|
+
- **`subagent/end` as a settlement channel** (in-process): fires on the same promise settlement as the handle we already trust, so settlement is not lost when the handle is gone (restart, mode switch, early return). Unknown child ids are ignored — someone else's sub-run is not our fact.
|
|
26
|
+
|
|
27
|
+
## [0.1.6] — 2026-09-16
|
|
28
|
+
|
|
29
|
+
**机制不许再说自己没有的话。** 一轮「按真值表逐条核对文档 vs 代码」的清点,把三处
|
|
30
|
+
「文档写了、代码没有」补上了生产者;同时修掉四处在真跑里现形的缺陷——其中一个控件
|
|
31
|
+
**点了报成功、账上一字未改**,还有一把**只有方向、没有口径**的尺子。
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **A control that reported success and did nothing.** The inbox rendered the same `fork_adopt` gate twice: once by the worldline block (with each branch's reading) and again by the generic list, because `needs === 'click'` implied "give it a 裁决 button". The second button sent `fork: null`, so the fold's `forks.find(id === null)` matched nothing and the state did not change — while the host route answered `200 {ok:true}`. One criterion now drives both renderings, and the generic layer only offers what it can actually land: 采纳 for a skill candidate, 用提问卡决定 for a gate the worldline block cannot render, a sentence-prompt for word gates.
|
|
36
|
+
- **The ruler had a direction but no scale.** Two worldlines' `done_criteria` were byte-identical and each told the *branch* to publish its own 计分口径 — so two mutually invisible executors measured in different units (炉次 vs 等效炉次) and `min` compared the two conventions as if they were one quantity. `decide_by.metric` must now read `量 = 口径` (`decide_by_scale_required`), and the *sharing* is guaranteed by the existing "each branch's criteria must contain the metric verbatim" check — no new field, no new gate, refused at registration instead of after the work.
|
|
37
|
+
- **The declared evidence path was never told to the doer.** `ForkPlan` declares each branch's `artifacts`, and delivery requires those paths to exist inside the executor's worktree — but the executor's brief carried only criteria, approach and workspace. A fresh agent therefore wrote to `products/reports/` and delivery failed on the declaration, leaving "copy the file into the declared path" as the only way through: a copy in a place where the evidence was not produced. The brief now carries the declared paths, and the refusal names the two honest ways out instead of inviting the copy.
|
|
38
|
+
- **The delivery-point commit could be swallowed by an exploration snapshot.** A snapshot committed the tree, so the delivery commit became empty, `commitLedger` skipped it silently (its rule is "nothing changed → no commit") and the delivery point disappeared from the ledger. The delivery point is a *named* event ("what the workspace looked like when this step was delivered"): only it passes `allowEmpty`.
|
|
39
|
+
- **Receiving no verdict never escalated.** A lost or unavailable independent verdict failed closed forever: the model could re-deliver, fail closed, and repeat — the same action, no new fact — without ever reaching a person. It now shares the block counter with a failed admission, so repeating it blocks the plan and lands in the inbox door that already exists.
|
|
40
|
+
- **`retracted` had no producer** — the state was declared in the ontology, absent from it in code, and drawn in the panel. Refuting evidence now only *marks* a promoted fact (`refuted`, derived) and raises an inbox item; a human decides **撤回** or **维持原事实**, and both land as one `fact/reviewed` (retraction is terminal, the record is kept). "No decision" and "decided to keep" have to stay distinguishable, or the gate holds continuation forever.
|
|
41
|
+
- **Platform junk no longer enters the ledger.** `.DS_Store` is nobody's content, is binary, and changes whenever a directory is browsed — two worldlines' copies always differ, so a merge conflicts over something unrelated to the delivery (a person clicked adopt and the model spent a round aligning `.DS_Store` bytes). `LEDGER_JUNK` now goes into the same `info/exclude` (exclusion is per repository, so every worktree benefits), and files already tracked are unstaged with `git rm --cached` — index only, the file in the workspace is untouched.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **`untouchedLevels`.** A level measures how much a conclusion depends on trusting the doer; the compensation ladder (independent evaluator → human release) is the mechanism. "One level at a time" is an economic order, not a permission — and a reason for skipping cannot be falsified, so requiring one would be a field nobody can check. What is mechanical: the levels a hypothesis never used are derived and shown.
|
|
46
|
+
- **`confirm_provisional`.** A provisional adoption could only be acknowledged by talking, while an open gate holds continuation — so the system waited for an action that could never arrive. Approval is a decision and now has a button.
|
|
47
|
+
- **`VoidPlanStep`-style exits for the two gates that had none**, and two new human-gate verbs `retract_fact` / `keep_fact` (the whitelist is enumerated verbatim, and every gate is now checkable for both outcomes).
|
|
48
|
+
- **Exploration snapshots** (`git/snapshot`): work written between deliveries is recorded, so exploration output is recoverable without asking anyone to declare it.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- **The truth table tells the truth about itself.** Every `implemented` row must point at symbols that exist (`source.code` is now falsifiable and caught a dead identifier), every non-implemented row must name a destination, and the counts are 57 mechanisms: implemented 51 / partial 1 / design-only 1 / removed 4.
|
|
53
|
+
- **`verification-loop`'s state table is a landing-point record**, not a design target: each of the nine names says where it lives today (a fact / something `derive()` computes / deliberately unrepresentable), and a machine check goes red if a row is added without one. §6 now says what carries each rule and admits that rule 1 is a reading, not a gate.
|
|
54
|
+
- **Observation provenance declares only what has a producer** (`self`, `scout`); the type may not promise an origin nothing writes.
|
|
55
|
+
- Docs, counts and suites aligned: 13 suites, 1267 assertions, `verify-package` 31/0.
|
|
56
|
+
|
|
5
57
|
## [0.1.5] — 2026-09-16
|
|
6
58
|
|
|
59
|
+
**卡片读不出自己的名字。** 预设卡片显示成 `clearai` + 「暂无描述」,而不是 ClearAI 与它的说明 ——
|
|
60
|
+
根因不在界面,在文件:元数据根本没被读进去,而名册对读失败**静默降级**。
|
|
61
|
+
|
|
7
62
|
### Fixed
|
|
8
63
|
|
|
9
64
|
- **The preset card still read `clearai` with an empty description.** 0.1.2, 0.1.3 and 0.1.4 all shipped `preset.yml` with the description as a plain YAML scalar containing `English: state` — a colon followed by a space cannot appear in a plain scalar, so the file did not parse at all. DSH's preset roster treats *every* metadata read failure as "no metadata", silently, so the picker fell back to the directory id plus 「暂无描述」 and nothing on either side reported an error. The description is now a block scalar, and `verify-package` parses `preset.yml` with the host's own `yaml` library and requires a non-empty `name` and `description` — this can no longer ship silently.
|
|
10
65
|
|
|
11
66
|
## [0.1.4] — 2026-09-16
|
|
12
67
|
|
|
13
|
-
|
|
68
|
+
**子任务的交付链修好了。** 侦察与世界线执行者的结论此前只进账本、模型读不到
|
|
14
69
|
(账本里也有过「派出去就再也没人收」的挂空)。现在四类子任务(侦察 / 世界线执行者 /
|
|
15
70
|
评估者 / 横评仲裁)统一走原生 `subagents.start()` 的一次性句柄:账本只认本进程攥着的
|
|
16
71
|
`run.result`,结论正文由**收集那一刻的工具返回**交给模型,全文另落
|
|
@@ -64,6 +119,8 @@ All notable changes to this project are recorded here. The format follows [Keep
|
|
|
64
119
|
|
|
65
120
|
## [0.1.3] — 2026-09-15
|
|
66
121
|
|
|
122
|
+
**一条命令的安装路径,以及一条从没被走通的发布路径。**
|
|
123
|
+
|
|
67
124
|
### Added
|
|
68
125
|
|
|
69
126
|
- **`npx clearai-dsh install` — one command, and the only prerequisite left is DSH's own.** The published package has always carried an install-side tool, but it only *diagnosed*: `doctor`, `root-yaml`, `seed`, `unseed`. The installer that could actually place the package lived in `tools/install-native.mjs`, which is not in the published files — so a stranger had nothing to run but `dsh plugin … add`, a command whose first word assumes a `dsh` that an `npx`-launched harness never puts on `PATH`. The new `install` verb resolves the CLI (a `dsh` on `PATH`, else `npx --yes @deepseek-ai/dsh`), installs into the profile, and then reads the composed config back to show that the `clearai-host` row really landed. `--dist` / `--tarball` / `--spec` point it at a local build instead of the registry, which is what the lifecycle check now exercises.
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ If it stops because **pnpm is not on your `PATH`**: DSH manages a profile by dri
|
|
|
39
39
|
From a checkout (development, not the install path):
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
npm test #
|
|
42
|
+
npm test # 14 suites — the list lives in test/run.sh
|
|
43
43
|
node tools/build-package.mjs # assemble dist/ from source
|
|
44
44
|
node tools/verify-package.mjs # rebuild and compare byte-for-byte
|
|
45
45
|
node tools/verify-clean-install.mjs # install into an empty DSH_HOME through the real CLI
|
|
@@ -122,7 +122,7 @@ They are illustrations of the mechanism, not shipped run records.
|
|
|
122
122
|
- [Soul map: principle → mechanism → test](docs/soul-map.md)
|
|
123
123
|
- [Glossary](docs/glossary.md)
|
|
124
124
|
- [Loop philosophy](docs/loop-philosophy.md) · [Verification ontology](docs/verification-loop.md)
|
|
125
|
-
- [Known gaps](docs/known-gaps.md) · [Release verification](docs/release-verification.md)
|
|
125
|
+
- [Known gaps](docs/known-gaps.md) · [Authority map](docs/authority-map.md) · [Release verification](docs/release-verification.md)
|
|
126
126
|
- [Convergence and slimming plan](docs/optimization/plan.md) · [Full-coverage design](docs/optimization/epistemic-coverage.md) · [Execution progress](docs/optimization/progress.zh-CN.md)
|
|
127
127
|
|
|
128
128
|
## Work attribution
|
package/README.zh-CN.md
CHANGED
|
@@ -39,7 +39,7 @@ npx clearai-dsh install
|
|
|
39
39
|
从仓库开发(这是开发路径,不是安装路径):
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
npm test #
|
|
42
|
+
npm test # 14 份套件 —— 清单在 test/run.sh
|
|
43
43
|
node tools/build-package.mjs # 由源装配 dist/
|
|
44
44
|
node tools/verify-package.mjs # 现场重建并逐字节比对
|
|
45
45
|
node tools/verify-clean-install.mjs # 空 DSH_HOME + 真 CLI 装一遍(16 条断言)
|
|
@@ -122,7 +122,7 @@ ClearAI 把认识论层加在 DSH 的**组合面**上——一个宿主包、一
|
|
|
122
122
|
- [灵魂映射:原则 → 机制 → 测试](docs/soul-map.zh-CN.md)
|
|
123
123
|
- [术语表](docs/glossary.zh-CN.md)
|
|
124
124
|
- [循环哲学](docs/loop-philosophy.zh-CN.md) · [验证本体](docs/verification-loop.zh-CN.md)
|
|
125
|
-
- [已知缺口](docs/known-gaps.zh-CN.md) · [发布验收](docs/release-verification.zh-CN.md)
|
|
125
|
+
- [已知缺口](docs/known-gaps.zh-CN.md) · [权威归属](docs/authority-map.zh-CN.md) · [发布验收](docs/release-verification.zh-CN.md)
|
|
126
126
|
- [收敛与瘦身计划](docs/optimization/plan.zh-CN.md) · [认识论循环全覆盖设计](docs/optimization/epistemic-coverage.zh-CN.md) · [执行进度](docs/optimization/progress.zh-CN.md)
|
|
127
127
|
|
|
128
128
|
## 工作署名
|
package/bin/clearai.mjs
CHANGED
|
@@ -136,6 +136,23 @@ function doctor() {
|
|
|
136
136
|
const dir = join(root, PRESET_ID)
|
|
137
137
|
const present = existsSync(join(dir, 'agent.cordis.yml'))
|
|
138
138
|
rows.push(`用户根 ${dir} ${present ? '✓ 名册看得见' : '✗ 还没有(用 seed 播种,或把 root-yaml 那一行粘进 profile)'}`)
|
|
139
|
+
/**
|
|
140
|
+
* 用户根里那份**会不会被包的 root 遮住**。
|
|
141
|
+
*
|
|
142
|
+
* 名册按 root 顺序先到先得(自带 root → 配置 root → 用户根),所以包一装,用户根里
|
|
143
|
+
* 同 id 的那份副本就**再也读不到**——它会安静地烂在那里,还会把「部署出去的那份」的
|
|
144
|
+
* 自检引到你手改过的旧副本上。判据只取事实:两份 `preset.yml` 的字节是否一致。
|
|
145
|
+
*/
|
|
146
|
+
const mine = join(PRESET_SRC, 'preset.yml')
|
|
147
|
+
const theirs = join(dir, 'preset.yml')
|
|
148
|
+
if (!present || !existsSync(mine) || !existsSync(theirs)) continue
|
|
149
|
+
let same = false
|
|
150
|
+
try {
|
|
151
|
+
same = readFileSync(mine).equals(readFileSync(theirs))
|
|
152
|
+
} catch {
|
|
153
|
+
same = false
|
|
154
|
+
}
|
|
155
|
+
if (!same) rows.push(` ⚠️ 影子副本 ${theirs} 与包里的那份**不一致**,而它被包的 root 遮住、永远不会被读到(自检却会优先读它)。删掉它,或用它来承载你自己的改动并换一个 id。`)
|
|
139
156
|
}
|
|
140
157
|
const profileDir = join(DSH_HOME, 'profiles', profile)
|
|
141
158
|
rows.push(`profile ${profileDir}${existsSync(profileDir) ? '' : '(不存在:先跑一次 dsh --profile ' + profile + ')'}`)
|