dsh-continual-evolve 0.2.0 → 0.3.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/README.md +45 -7
- package/README.zh.md +44 -7
- package/lib/apply.js +1 -1
- package/lib/approval.d.ts +6 -0
- package/lib/approval.js +9 -1
- package/lib/auto.d.ts +38 -4
- package/lib/auto.js +58 -5
- package/lib/benchmark-command.d.ts +9 -0
- package/lib/benchmark-command.js +331 -0
- package/lib/benchmark.d.ts +70 -0
- package/lib/benchmark.js +107 -1
- package/lib/command.js +25 -442
- package/lib/evaluate.d.ts +7 -0
- package/lib/evaluate.js +22 -7
- package/lib/evolve-event.d.ts +38 -0
- package/lib/evolve-event.js +49 -0
- package/lib/failures.d.ts +39 -0
- package/lib/failures.js +170 -0
- package/lib/fate.d.ts +3 -1
- package/lib/fate.js +8 -4
- package/lib/goal-command.d.ts +7 -0
- package/lib/goal-command.js +37 -0
- package/lib/index.d.ts +29 -25
- package/lib/index.js +14 -0
- package/lib/inject.d.ts +8 -0
- package/lib/inject.js +51 -4
- package/lib/llm-text.d.ts +30 -0
- package/lib/llm-text.js +49 -0
- package/lib/mount-command.d.ts +10 -0
- package/lib/mount-command.js +48 -0
- package/lib/plan.js +5 -0
- package/lib/planner.d.ts +1 -1
- package/lib/planner.js +13 -39
- package/lib/render.d.ts +1 -3
- package/lib/render.js +0 -4
- package/lib/review.d.ts +4 -1
- package/lib/review.js +10 -38
- package/lib/rollback.d.ts +1 -3
- package/lib/rollback.js +0 -8
- package/lib/score.d.ts +15 -0
- package/lib/score.js +74 -5
- package/lib/service.d.ts +2 -2
- package/lib/service.js +5 -2
- package/lib/skill-render.d.ts +15 -0
- package/lib/skill-render.js +30 -0
- package/lib/skill.d.ts +2 -5
- package/lib/skill.js +2 -29
- package/lib/skillquality.d.ts +1 -2
- package/lib/skillquality.js +2 -2
- package/lib/store.d.ts +1 -3
- package/lib/store.js +0 -7
- package/lib/tool.js +22 -1
- package/lib/types.d.ts +8 -0
- package/lib/usage.d.ts +32 -0
- package/lib/usage.js +84 -0
- package/lib/validate.d.ts +12 -2
- package/lib/validate.js +26 -1
- package/lib/wrapup-command.d.ts +8 -0
- package/lib/wrapup-command.js +211 -0
- package/lib/wrapup.d.ts +14 -9
- package/lib/wrapup.js +24 -36
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](package.json)
|
|
10
|
-
[]()
|
|
11
11
|
[]()
|
|
12
12
|
|
|
13
13
|
Continual self-evolution for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): a versioned, auditable, rollback-safe layer of harness state — prompt notes, memories, skills, and subagent specs — refined from session trajectories.
|
|
@@ -101,8 +101,13 @@ dsh-continual-evolve/
|
|
|
101
101
|
│ ├── rollback.ts # deterministic inverse-op rollback
|
|
102
102
|
│ ├── plan.ts # proposal JSON parsing (truncation-aware)
|
|
103
103
|
│ ├── tool.ts # evolve_* model-facing tools (5)
|
|
104
|
-
│ ├── command.ts # /evolve command
|
|
104
|
+
│ ├── command.ts # /evolve command dispatcher + shared utilities
|
|
105
|
+
│ ├── goal-command.ts # /evolve goal subcommand handler
|
|
106
|
+
│ ├── mount-command.ts # /evolve mount + unmount subcommand handlers
|
|
107
|
+
│ ├── benchmark-command.ts # /evolve benchmark subcommand handler
|
|
108
|
+
│ ├── wrapup-command.ts # /evolve wrapup subcommand handler
|
|
105
109
|
│ ├── planner.ts # ctx.llm planner
|
|
110
|
+
│ ├── llm-text.ts # unified streaming-text helper (BlockAssembler + finish check)
|
|
106
111
|
│ ├── render.ts # bounded prompt rendering
|
|
107
112
|
│ ├── inject.ts # dynamic system-prompt section (prompt notes + delegation specs, ranked injection)
|
|
108
113
|
│ ├── source.ts # trajectory citations (sessionId + event seqs of distilled entries)
|
|
@@ -113,18 +118,21 @@ dsh-continual-evolve/
|
|
|
113
118
|
│ ├── review.ts # gate LLM judgment (declines local duplicates of globally covered topics)
|
|
114
119
|
│ ├── approval.ts # human approval for global edits
|
|
115
120
|
│ ├── skill.ts # skill materialization ($DSH_HOME/skills/)
|
|
121
|
+
│ ├── skill-render.ts # shared skill rendering (skillNameOf + renderSkillMarkdown, breaks circular dependency)
|
|
116
122
|
│ ├── skillquality.ts # skill standard in the loop (skill-creator template reading + frontmatter code checks)
|
|
117
123
|
│ ├── mount.ts # hot-mounted skill plugins (loader.create + boot restore)
|
|
118
|
-
│ ├── benchmark.ts # benchmark store
|
|
124
|
+
│ ├── benchmark.ts # benchmark store + CellScore types (with runtime evidence fields)
|
|
119
125
|
│ ├── rubric.ts # rubric ACL (AES-256-GCM envelopes, auto-generated local key)
|
|
120
126
|
│ ├── logfile.ts # plugin-owned file logging (JSONL exporter + rotation)
|
|
121
127
|
│ ├── score.ts # code-owned aggregation + acceptance rule
|
|
122
|
-
│ ├── evaluate.ts # two-stage evaluation runner (executor evidence → independent reviewer) + failure-cell protocol
|
|
128
|
+
│ ├── evaluate.ts # two-stage evaluation runner (executor evidence → independent reviewer) + failure-cell protocol + runtime verification
|
|
123
129
|
│ ├── pool.ts # bounded-concurrency worker pool for evaluation runs
|
|
124
130
|
│ ├── store.ts # store layout + snapshots + result history
|
|
125
131
|
│ ├── service.ts # evolution engine (onApplied hook)
|
|
126
|
-
│
|
|
127
|
-
|
|
132
|
+
│ ├── usage.ts # entry injection usage tracking (durable counts, staleness detection)
|
|
133
|
+
│ ├── failures.ts # failure-signature aggregation (gate + benchmark failures by class, /evolve failures)
|
|
134
|
+
│ └── wrapup.ts # session wrap-up lifecycle (promote / split-promote → global, guarded archive; shared proposal builders; staleness signal)
|
|
135
|
+
└── test/ # 28 files, 401 tests
|
|
128
136
|
```
|
|
129
137
|
|
|
130
138
|
## Install
|
|
@@ -152,6 +160,7 @@ Swap `web` for your profile name (`headless`, or a custom profile).
|
|
|
152
160
|
/evolve archive <id> hide an entry from injection (data kept, restorable)
|
|
153
161
|
/evolve unarchive <id> restore an archived entry
|
|
154
162
|
/evolve log [tail N] [session <id>] show the recent plugin log (default 50 lines; optional per-session filter)
|
|
163
|
+
/evolve failures aggregated failure counts (review-gate + benchmark, by class — D1 observation layer)
|
|
155
164
|
/evolve export <path> backup the local store to JSON
|
|
156
165
|
/evolve import <path> restore a store from an export file
|
|
157
166
|
/evolve mount <skillId> hot-mount a skill entry as a live cordis plugin (tool: skill_<name>)
|
|
@@ -290,6 +299,10 @@ profile (optional; the file log remains the baseline that always exists).
|
|
|
290
299
|
/evolve benchmark status <bid> scoreboard + decisions
|
|
291
300
|
/evolve benchmark run <bid> evaluate current state → reference
|
|
292
301
|
/evolve benchmark run <bid> candidate <refinementId> evaluate post-refinement state → decide
|
|
302
|
+
/evolve benchmark casecheck <bid> quality-gate check all cases
|
|
303
|
+
/evolve benchmark pilot <bid> <cid> single pilot run for calibration
|
|
304
|
+
/evolve benchmark freeze <bid> <cid> freeze a case as formal baseline
|
|
305
|
+
/evolve benchmark meta <bid> <cid> <field> <value> set case metadata (capability/distinguisher/shortcuts)
|
|
293
306
|
```
|
|
294
307
|
|
|
295
308
|
The loop: freeze a reference score → evolve a candidate (`/evolve plan`) →
|
|
@@ -325,6 +338,9 @@ never decrypts); a rejection is recorded in the scoreboard and the
|
|
|
325
338
|
refinement is rolled back automatically (`autoRollbackOnReject`, on by
|
|
326
339
|
default).
|
|
327
340
|
|
|
341
|
+
Ready-to-use seed cases are in [`examples/`](examples/) — copy-paste the
|
|
342
|
+
statement and rubric to get started in under a minute.
|
|
343
|
+
|
|
328
344
|
### Real recorded run (ACCEPT)
|
|
329
345
|
|
|
330
346
|
A live `dsh web` session, one case, one candidate — the first genuine
|
|
@@ -364,6 +380,8 @@ where the baseline was already perfect).
|
|
|
364
380
|
| `autoRollbackOnReject` | `true` | after a benchmark decision rejects a candidate, roll the refinement back automatically (same engine path as `/evolve rollback` — deterministic, snapshotted, audited) |
|
|
365
381
|
| `localFate` | `true` | gate local-fate dimension: the gate audits the session's local entries on its own cadence and proposes promote/archive — consulted first, never written silently (only meaningful with `autoReview`) |
|
|
366
382
|
| `fateIntervalTurns` | follows `reviewIntervalTurns` | minimum turns between local-fate assessments on the turn-interval path (compaction is unconditional) |
|
|
383
|
+
| `goalBlockedWrapupTurns` | `3` | D3: after this many consecutive gate runs observing the goal phase `blocked`, run one local-fate assessment (`0` disables) |
|
|
384
|
+
| `reviewModel` | (agent's own) | optional model override for the review gate (cheaper model); format: `"provider/model"` or just `"model"` |
|
|
367
385
|
|
|
368
386
|
Example (profile `cordis.patch.yml`):
|
|
369
387
|
|
|
@@ -388,7 +406,7 @@ pnpm lint # oxlint src test
|
|
|
388
406
|
|
|
389
407
|
Hit a wall? See [`docs/FAQ.md`](docs/FAQ.md) — real failure/fix records (service planes, schema DSL, structured output, gate counting, verifying prompt injection).
|
|
390
408
|
|
|
391
|
-
Where we still lag behind prime-agent `/refine` and penguin-harness — and what to build next: [`docs/gap-analysis.md`](docs/gap-analysis.md) (P0 shipped: evaluator/scorer separation, failure-cell protocol
|
|
409
|
+
Where we still lag behind prime-agent `/refine` and penguin-harness — and what to build next: [`docs/gap-analysis.md`](docs/gap-analysis.md) (P0+P1+P2+P3 shipped: evaluator/scorer separation, failure-cell protocol, runtime provenance verification + material-drift detection, usage statistics, auto-decay, case lifecycle + quality gate, entry directory view, review model separation, blast-radius annotations, duration tracking, evolve_complete events, seed benchmark; D1 observation layer + D3 goal-blocked trigger shipped; remaining: cross-process sync on demand + D1/D2 full engineering pending experiment data).
|
|
392
410
|
|
|
393
411
|
## Roadmap
|
|
394
412
|
|
|
@@ -408,10 +426,30 @@ Where we still lag behind prime-agent `/refine` and penguin-harness — and what
|
|
|
408
426
|
- **2026-08-17 wrap-up wave (done)**:
|
|
409
427
|
- **`/evolve wrapup`** — a session's local entries get a real exit at session end: mechanical audit (local candidates + global-coverage detection; coverage judges **title similarity only** — a bare id collision with a different title is deliberately NOT coverage, and the real matching global titles are shown to the assessor) → LLM classification (`promote` / `archive` / `keep` + A-form split promotion: archive a mixed entry while promoting a cleaned durable sub-object) → deterministic guards re-checked at apply time (promote can never write a global duplicate; the symmetric archive guard requires user confirmation before an uncovered, user-sourced archive hides content; splits that duplicate a global topic drop to plain archive) → one human approval gate for every global create
|
|
410
428
|
- **gate local-fate dimension** — the wrap-up machinery now runs inside the auto-review gate on its own cadence (`fateIntervalTurns`, compaction unconditional): local entries are audited, classified and partitioned while the session is still running; governed actions are consulted first (one dialog, decline cooldown), covered/operational entries archive silently, compaction applies only silent archives and defers governed actions with an audit record; every decision lands in `reviews.jsonl` and applied actions get a follow-up notice. Apply writes are shared with the wrap-up command (byte-identical proposals)
|
|
429
|
+
- **2026-08-19 research-wave precursors (done)**:
|
|
430
|
+
- **goal-blocked wrap-up (D3)** — a goal stuck in `blocked` for `goalBlockedWrapupTurns` consecutive gate runs (default 3) triggers one local-fate assessment, so the blocked encounter is distilled before the session moves on; the streak resets on any non-blocked run and after each assessment, and declined proposals follow the normal fate cooldown (never nagged). Disable with `goalBlockedWrapupTurns: 0`
|
|
431
|
+
- **failure-signature aggregation (D1 observation layer)** — `/evolve failures` counts every failed review-gate record and benchmark failed cell by deterministic failure class (`rubric-decrypt` / `executor` / `reviewer` / `material-drift` / `gate` / `max-tokens` / …), the data layer a future failure-signature Refiner would route on
|
|
432
|
+
- **bootstrap-update experiment scaffold (D2)** — [`docs/experiment-bootstrap.md`](docs/experiment-bootstrap.md) designs a ≤3-round controlled experiment (fixed reference → evolve harness → candidate) to test whether a refined harness accelerates the next run; `scripts/benchmark-trend.sh` extracts the per-run trend table (overall / totalDurationMs / failed / case-hash consistency) from scoreboards
|
|
411
433
|
- **2026-08-17 gap P0 (done)**:
|
|
412
434
|
- **evaluator/scorer separation** — benchmark evaluation is now two-stage (gap A1): the executor performs the task and records concrete evidence without ever seeing the rubric; an independent reviewer grades that evidence against the rubric (the only branch that decrypts it). The assessed agent can no longer optimize toward or self-grade against the criteria.
|
|
413
435
|
- **failure-cell protocol** — cells carry `status: ok|failed` (gap A2): failed units are excluded from every mean and counted, and the acceptance rule rejects rounds with failures beyond `maxFailedCells` (0 default) instead of averaging a zero into the mean. Scoreboard status/run surfaces failed counts and per-cell reasons.
|
|
414
436
|
- **trace evidence pointer** — each cell records the executor's session id (gap A4), so a score drills back to the exact transcript that earned it
|
|
437
|
+
- **2026-08-18 gap P1 (done)**:
|
|
438
|
+
- **runtime evidence verification (A3)** — cells now record actual `provider`, `model`, and `caseHash` (SHA-256 prefix of statement + rubric) written by the host, not the model; material changes between reference and candidate runs are detected and re-mark the affected candidate cells as failed (version_changed semantics, `score.flagMaterialDrift`), so a drifted round can never be accepted
|
|
439
|
+
- **entry usage statistics (B1)** — injection counts are durably tracked per entry in `<baseDir>/evolve/usage.json`; `evolve_list` shows usage counts; `zeroUsageEntries()` surfaces never-injected local entries as archive candidates
|
|
440
|
+
- **automatic staleness detection (B2)** — entries with zero injection usage AND old recency are flagged `stale` in wrap-up candidates; the LLM assessor is instructed to prefer "archive" for stale entries
|
|
441
|
+
- **2026-08-18 gap P2 (done)**:
|
|
442
|
+
- **case lifecycle + quality gate (A5)** — cases follow a `draft → calibrating → frozen` state machine; `casecheck` runs mechanical quality validation (capability contract, distinguisher, shortcuts); `pilot` performs a single-run calibration; `freeze` locks a case as a formal baseline (requires quality gate pass); `meta` sets case metadata fields
|
|
443
|
+
- **entry directory view (B3)** — the injection block now includes a lightweight directory of ALL non-archived entries (id + title, one line each) when entries exceed the curated cap, giving the model a zero-cost overview
|
|
444
|
+
- **review model separation (C1)** — `reviewModel` config option lets the review gate use a cheaper model than the main agent
|
|
445
|
+
- **blast-radius annotations (C2)** — every edit now carries a `blastRadius` field (`general` / `project` / `session`); the planner is instructed to annotate it and the parser validates values
|
|
446
|
+
- **duration tracking (C3)** — each evaluation cell records `durationMs` (wall-clock time); aggregate totals and decision reports show timing comparison
|
|
447
|
+
- **2026-08-18 code refactoring (done)**:
|
|
448
|
+
- **circular dependency break (P1-1)** — extracted `skill-render.ts` to decouple `skill.ts ↔ skillquality.ts`
|
|
449
|
+
- **LLM call deduplication (P1-2)** — extracted `llm-text.ts` with shared `streamText()` (~107 lines removed from review/planner/wrapup)
|
|
450
|
+
- **config type derivation (P2-1)** — `EvolveConfig` now derived from schemastery schema via `Schemastery.TypeT` (eliminated 20-line handwritten interface)
|
|
451
|
+
- **command.ts split (P2-2)** — 860-line god file split into `goal-command.ts`, `mount-command.ts`, `benchmark-command.ts`, `wrapup-command.ts`
|
|
452
|
+
- **P3 cleanups** — `questionServiceOf()` cast dedup (4 sites), dead exports removed, contradictory comments fixed
|
|
415
453
|
|
|
416
454
|
The upcoming/candidates list is empty for now — future work is driven by real usage.
|
|
417
455
|
|
package/README.zh.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](package.json)
|
|
10
|
-
[]()
|
|
11
11
|
[]()
|
|
12
12
|
|
|
13
13
|
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)的持续自进化插件:一套**版本化、可审计、可回滚**的 harness 状态层——提示词补充、记忆、技能、子代理规格——从会话轨迹中沉淀而来。
|
|
@@ -75,8 +75,13 @@ dsh-continual-evolve/
|
|
|
75
75
|
│ ├── rollback.ts # 确定性逆操作回滚
|
|
76
76
|
│ ├── plan.ts # 提案 JSON 解析(截断诊断)
|
|
77
77
|
│ ├── tool.ts # evolve_* 模型工具(5 个)
|
|
78
|
-
│ ├── command.ts # /evolve
|
|
78
|
+
│ ├── command.ts # /evolve 命令分发器 + 共享工具
|
|
79
|
+
│ ├── goal-command.ts # /evolve goal 子命令处理
|
|
80
|
+
│ ├── mount-command.ts # /evolve mount + unmount 子命令处理
|
|
81
|
+
│ ├── benchmark-command.ts # /evolve benchmark 子命令处理
|
|
82
|
+
│ ├── wrapup-command.ts # /evolve wrapup 子命令处理
|
|
79
83
|
│ ├── planner.ts # ctx.llm 规划器
|
|
84
|
+
│ ├── llm-text.ts # 统一流式文本助手(BlockAssembler + finish 检查)
|
|
80
85
|
│ ├── render.ts # 有界提示词渲染
|
|
81
86
|
│ ├── inject.ts # 动态系统提示词段(prompt 补充 + 委派规格,打分排序注入)
|
|
82
87
|
│ ├── source.ts # 轨迹引用(沉淀条目的 sessionId + 事件 seq)
|
|
@@ -87,18 +92,21 @@ dsh-continual-evolve/
|
|
|
87
92
|
│ ├── review.ts # 门禁 LLM 判断(拒绝 global 已覆盖主题的 local 重复沉淀)
|
|
88
93
|
│ ├── approval.ts # 全局写入人工审批
|
|
89
94
|
│ ├── skill.ts # 技能物化($DSH_HOME/skills/)
|
|
95
|
+
│ ├── skill-render.ts # 共享技能渲染(skillNameOf + renderSkillMarkdown,打破循环依赖)
|
|
90
96
|
│ ├── skillquality.ts # 自进化环中的技能标准(skill-creator 模板读取 + frontmatter 代码校验)
|
|
91
97
|
│ ├── mount.ts # 技能热挂载插件(loader.create + 启动恢复)
|
|
92
|
-
│ ├── benchmark.ts # benchmark 存储
|
|
98
|
+
│ ├── benchmark.ts # benchmark 存储 + CellScore 类型(含运行时实证字段)
|
|
93
99
|
│ ├── rubric.ts # rubric ACL(AES-256-GCM 密文信封,自动生成本地密钥)
|
|
94
100
|
│ ├── logfile.ts # 插件自带文件日志(JSONL exporter + 轮转)
|
|
95
101
|
│ ├── score.ts # 代码所有聚合 + 接受规则
|
|
96
|
-
│ ├── evaluate.ts # 两段式评估执行器(执行者产证据 → 独立评审者评分)+ 失败格协议
|
|
102
|
+
│ ├── evaluate.ts # 两段式评估执行器(执行者产证据 → 独立评审者评分)+ 失败格协议 + 运行时实证校验
|
|
97
103
|
│ ├── pool.ts # 评估运行的有界并发工作池
|
|
98
104
|
│ ├── store.ts # store 布局 + 快照 + 结果历史
|
|
99
105
|
│ ├── service.ts # 进化引擎(onApplied 钩子)
|
|
100
|
-
│
|
|
101
|
-
|
|
106
|
+
│ ├── usage.ts # 条目注入使用率追踪(持久计数、陈旧检测)
|
|
107
|
+
│ ├── failures.ts # 失败签名聚合(门禁 + benchmark 失败按类统计,/evolve failures)
|
|
108
|
+
│ └── wrapup.ts # 会话收尾生命周期(提升/拆解提升到 global、带守卫的归档;共享 proposal 构造器;陈旧信号)
|
|
109
|
+
└── test/ # 28 个文件,401 个测试
|
|
102
110
|
```
|
|
103
111
|
|
|
104
112
|
## 安装
|
|
@@ -126,6 +134,7 @@ dsh plugin --profile web add github:ZK-Andy/dsh-continual-evolve
|
|
|
126
134
|
/evolve archive <id> 归档条目——不再注入(数据保留,可恢复)
|
|
127
135
|
/evolve unarchive <id> 恢复已归档条目
|
|
128
136
|
/evolve log [tail N] [session <id>] 查看最近插件日志(默认 50 行;可加会话过滤)
|
|
137
|
+
/evolve failures 失败聚合统计(门禁 + benchmark 失败按类计数——D1 观察层)
|
|
129
138
|
/evolve export <path> 备份局部 store 为 JSON
|
|
130
139
|
/evolve import <path> 从导出文件恢复 store
|
|
131
140
|
/evolve mount <skillId> 热挂载 skill 条目为实时 cordis 插件(工具:skill_<name>)
|
|
@@ -180,6 +189,10 @@ tail -f ~/.dsh/evolve/plugin.log # 实时跟随
|
|
|
180
189
|
/evolve benchmark reset <bid> 清空计分板(重跑参考线)
|
|
181
190
|
/evolve benchmark run <bid> 评估当前状态 → 参考线
|
|
182
191
|
/evolve benchmark run <bid> candidate <refinementId> 评估进化后状态 → 决策
|
|
192
|
+
/evolve benchmark casecheck <bid> 质量门禁检查所有 case
|
|
193
|
+
/evolve benchmark pilot <bid> <cid> 单次 pilot 运行(校准用)
|
|
194
|
+
/evolve benchmark freeze <bid> <cid> 冻结 case 为正式基线
|
|
195
|
+
/evolve benchmark meta <bid> <cid> <field> <value> 设置 case 元数据(capability/distinguisher/shortcuts)
|
|
183
196
|
```
|
|
184
197
|
|
|
185
198
|
闭环:冻结参考分 → 进化候选(`/evolve plan`)→ 用同一 case × run 矩阵复测进化后状态 → **代码所有**的接受规则只在总体均值严格提高且无 case 退化时保留候选(Self-Harness 风格)。
|
|
@@ -194,6 +207,8 @@ tail -f ~/.dsh/evolve/plugin.log # 实时跟随
|
|
|
194
207
|
|
|
195
208
|
聚合与决策都在 `src/score.ts`。rubric 隔离靠构造(规划器的提示词永远不含 rubric 文件、执行者分支永不解密);拒绝会记录进 scoreboard 并自动回滚该 refinement(`autoRollbackOnReject`,默认开)。
|
|
196
209
|
|
|
210
|
+
开箱即用的种子 case 在 [`examples/`](examples/)——复制粘贴 statement 和 rubric 即可在一分钟内上手。
|
|
211
|
+
|
|
197
212
|
### 真实运行记录(ACCEPT)
|
|
198
213
|
|
|
199
214
|
一次真实的 `dsh web` 会话,一个 case、一个候选——第一次真正的接受:
|
|
@@ -227,6 +242,8 @@ tail -f ~/.dsh/evolve/plugin.log # 实时跟随
|
|
|
227
242
|
| `autoRollbackOnReject` | `true` | benchmark 决策拒绝候选后自动回滚该 refinement(与 `/evolve rollback` 同一引擎路径——确定性、快照、审计) |
|
|
228
243
|
| `localFate` | `true` | 门禁 local 归宿维度:门禁按自有节奏审计本会话 local 条目并提议提升/归档——先征询、绝不静默写入(仅 `autoReview` 开启时有效) |
|
|
229
244
|
| `fateIntervalTurns` | 跟随 `reviewIntervalTurns` | 回合间隔路径上两次 local 归宿评估的最小间隔(压缩时刻无条件触发) |
|
|
245
|
+
| `goalBlockedWrapupTurns` | `3` | D3:连续多少次门禁运行观察到 goal 处于 `blocked` 后触发一次 local 归宿评估(`0` 关闭) |
|
|
246
|
+
| `reviewModel` | (使用 agent 自身模型) | review 门禁的可选模型覆盖(更便宜的模型);格式:`"provider/model"` 或仅 `"model"` |
|
|
230
247
|
|
|
231
248
|
示例(profile `cordis.patch.yml`):
|
|
232
249
|
|
|
@@ -251,7 +268,7 @@ pnpm lint # oxlint src test
|
|
|
251
268
|
|
|
252
269
|
遇到问题先看 [`docs/FAQ.md`](docs/FAQ.md)(真实踩坑记录:服务平面、schema DSL、结构化输出、门禁计数、注入验证等)。
|
|
253
270
|
|
|
254
|
-
对照 prime-agent `/refine` 与 penguin-harness 的差距与下一步实施项(P0
|
|
271
|
+
对照 prime-agent `/refine` 与 penguin-harness 的差距与下一步实施项(P0+P1+P2+P3 已交付:评估者/评分者分离、失败格协议、运行时实证校验+材料漂移检测、使用率统计、自动衰减、case 生命周期+质检、条目目录视图、review 模型分离、blast-radius 标注、耗时追踪、evolve_complete 事件、种子 benchmark;D1 观察层 + D3 goal-blocked 触发已交付;剩余:跨进程同步按需实现 + D1/D2 完整工程化待实验数据):[`docs/gap-analysis.md`](docs/gap-analysis.md)。
|
|
255
272
|
|
|
256
273
|
## 路线图
|
|
257
274
|
|
|
@@ -271,10 +288,30 @@ pnpm lint # oxlint src test
|
|
|
271
288
|
- **2026-08-17 收尾 wave(完成)**:
|
|
272
289
|
- **`/evolve wrapup`**——会话结束时 local 条目有了真正的归宿:先机械审计(local 候选 + 全局覆盖检测;**覆盖只看标题相似**——裸同 id 但标题迥异**不算**覆盖,真正命中的全局标题会展示给分类器)→ LLM 分类(`promote` / `archive` / `keep` + A 形拆解提升:混合条目整体归档、同时提升清洗出的持久子对象)→ 应用时刻确定性守卫复检(promote 永不写出全局重复;对称归档守卫要求用户确认后才隐藏未被覆盖、源自真实对话的条目;清洗标题撞全局主题的拆解降级为普通归档)→ 所有全局 create 走一个人工审批门
|
|
273
290
|
- **门禁 local 归宿维度**——wrap-up 机制现在以内置节奏(`fateIntervalTurns`,压缩时刻无条件)跑在自动 review 门禁里:local 条目在会话进行中被审计、分类、划分;治理动作先征询(一个弹窗、拒绝冷却),被覆盖/操作性条目静默归档,压缩时刻只做静默归档并以审计记录推迟治理动作;每次决策落进 `reviews.jsonl`,已执行动作发后续通知。应用写入与 wrapup 命令共享构造器(逐字节一致)
|
|
291
|
+
- **2026-08-19 研究项先导(完成)**:
|
|
292
|
+
- **goal-blocked 触发收尾(D3)**——goal 连续 `goalBlockedWrapupTurns` 次门禁运行(默认 3)处于 `blocked` 时触发一次 local 归宿评估,把卡住的原因沉淀下来再继续;连胜在任意非 blocked 运行与每次触发后被重置,被拒提案走正常 fate 冷却(绝不打扰);`goalBlockedWrapupTurns: 0` 关闭
|
|
293
|
+
- **失败签名聚合(D1 观察层)**——`/evolve failures` 将门禁失败记录与 benchmark 失败格按确定性失败类(`rubric-decrypt` / `executor` / `reviewer` / `material-drift` / `gate` / `max-tokens` 等)统计——未来 failure-signature Refiner 的底层数据
|
|
294
|
+
- **bootstrap 加速实验脚手架(D2)**——[`docs/experiment-bootstrap.md`](docs/experiment-bootstrap.md) 设计 ≤3 轮对照实验(固定 reference → 沉淀 harness → 候选评估)验证"被提高的 harness 加速下一跑";`scripts/benchmark-trend.sh` 从 scoreboard 提取每轮趋势表(overall / totalDurationMs / failed / case-hash 一致性)
|
|
274
295
|
- **2026-08-17 差距 P0(完成)**:
|
|
275
296
|
- **评估者/评分者分离**——benchmark 评估改为两段式(差距 A1):执行者完成任务并记录具体证据、**永远看不到 rubric**;独立评审者按 rubric 给证据评分(唯一解密 rubric 的分支)。被测 agent 无法朝评分标准优化、也无法自评
|
|
276
297
|
- **失败格协议**——cell 带 `status: ok|failed`(差距 A2):失败格从所有均值中排除并计数,接受规则在失败格超过 `maxFailedCells`(默认 0)时拒绝整轮,而不是把 0 平均进均值。scoreboard status/run 展示失败数与逐格原因
|
|
277
298
|
- **Trace 证据指针**——每个 cell 记录执行者会话 id(差距 A4),分数可下钻回产生它的确切会话轨迹
|
|
299
|
+
- **2026-08-18 差距 P1(完成)**:
|
|
300
|
+
- **运行时实证校验(A3)**——cell 现在记录宿主写入的实际 `provider`、`model` 和 `caseHash`(statement + rubric 的 SHA-256 前缀);参考线与候选运行之间的材料变化会被检出并把受影响候选格重标为失败(version_changed 语义,`score.flagMaterialDrift`),材料漂移的轮次绝不可能被接受
|
|
301
|
+
- **条目使用率统计(B1)**——注入计数持久追踪(`<baseDir>/evolve/usage.json`);`evolve_list` 展示使用次数;`zeroUsageEntries()` 筛选从未注入的 local 条目作为归档候选
|
|
302
|
+
- **自动陈旧检测(B2)**——零注入且低新鲜度的条目标记为 `stale`;LLM 分类器被指示优先归档陈旧条目
|
|
303
|
+
- **2026-08-18 差距 P2(完成)**:
|
|
304
|
+
- **case 生命周期 + 质量门禁(A5)**——case 遵循 `draft → calibrating → frozen` 状态机;`casecheck` 运行机械质量校验(能力合约、区分点、快捷方式);`pilot` 执行单次校准运行;`freeze` 将 case 锁定为正式基线(需通过质量门禁);`meta` 设置 case 元数据字段
|
|
305
|
+
- **条目目录视图(B3)**——注入块现在包含所有非归档条目的轻量目录(id + title,每条一行),在条目超出精选封顶时自动展示,为模型提供零成本全局概览
|
|
306
|
+
- **review 模型分离(C1)**——`reviewModel` 配置项让 review 门禁可使用比主 agent 更便宜的模型
|
|
307
|
+
- **blast-radius 标注(C2)**——每条编辑现在携带 `blastRadius` 字段(`general` / `project` / `session`);规划器被要求标注该字段,解析器验证取值
|
|
308
|
+
- **耗时追踪(C3)**——每个评估单元格记录 `durationMs`(墙钟时间);聚合总计和决策报告展示耗时对比
|
|
309
|
+
- **2026-08-18 代码重构(完成)**:
|
|
310
|
+
- **循环依赖拆解(P1-1)**——抽出 `skill-render.ts` 解耦 `skill.ts ↔ skillquality.ts`
|
|
311
|
+
- **LLM 调用去重(P1-2)**——抽出 `llm-text.ts` 共享 `streamText()`(review/planner/wrapup 删除 ~107 行重复)
|
|
312
|
+
- **config 类型推导(P2-1)**——`EvolveConfig` 改为 `Schemastery.TypeT` 推导(消除 20 行手写接口)
|
|
313
|
+
- **command.ts 拆分(P2-2)**——860 行 god file 拆为 `goal-command.ts`、`mount-command.ts`、`benchmark-command.ts`、`wrapup-command.ts`
|
|
314
|
+
- **P3 清理**——`questionServiceOf()` cast 去重(4 处)、死导出清理、矛盾注释修复
|
|
278
315
|
|
|
279
316
|
候选/待办清单暂时为空——后续工作随真实使用驱动。
|
|
280
317
|
|
package/lib/apply.js
CHANGED
|
@@ -8,7 +8,7 @@ export function applyRefinementProposal(state, proposal, options) {
|
|
|
8
8
|
for (const edit of proposal.edits) {
|
|
9
9
|
const computedId = edit.id ?? (edit.action === "create" ? slug(edit.title ?? edit.kind, edit.kind) : undefined);
|
|
10
10
|
const id = computedId ?? "";
|
|
11
|
-
const validationError = validateEdit(edit, computedId);
|
|
11
|
+
const validationError = validateEdit(edit, computedId, options.scope);
|
|
12
12
|
if (validationError) {
|
|
13
13
|
appliedEdits.push({ ...edit, id, applied: false, error: validationError });
|
|
14
14
|
continue;
|
package/lib/approval.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ export interface QuestionService {
|
|
|
25
25
|
}[];
|
|
26
26
|
}>;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Lazily resolve the userQuestions service from the context.
|
|
30
|
+
* Returns undefined when the service is not loaded — callers decide
|
|
31
|
+
* whether that is an error or a fallback.
|
|
32
|
+
*/
|
|
33
|
+
export declare function questionServiceOf(ctx: Context): QuestionService | undefined;
|
|
28
34
|
/**
|
|
29
35
|
* Ask the user to approve a global edit. Throws when the service is missing,
|
|
30
36
|
* the user declines, or the question cannot be answered.
|
package/lib/approval.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazily resolve the userQuestions service from the context.
|
|
3
|
+
* Returns undefined when the service is not loaded — callers decide
|
|
4
|
+
* whether that is an error or a fallback.
|
|
5
|
+
*/
|
|
6
|
+
export function questionServiceOf(ctx) {
|
|
7
|
+
return ctx.userQuestions;
|
|
8
|
+
}
|
|
1
9
|
/**
|
|
2
10
|
* Ask the user to approve a global edit. Throws when the service is missing,
|
|
3
11
|
* the user declines, or the question cannot be answered.
|
|
4
12
|
*/
|
|
5
13
|
export async function requireGlobalApproval(ctx, agent, signal, what) {
|
|
6
|
-
const userQuestions = ctx
|
|
14
|
+
const userQuestions = questionServiceOf(ctx);
|
|
7
15
|
if (!userQuestions) {
|
|
8
16
|
throw new Error("global evolution edits require the userQuestions service (load @deepseek-ai/dsh-user-questions)");
|
|
9
17
|
}
|
package/lib/auto.d.ts
CHANGED
|
@@ -22,6 +22,22 @@ export interface AutoReviewConfig {
|
|
|
22
22
|
* round.
|
|
23
23
|
*/
|
|
24
24
|
fateIntervalTurns: number;
|
|
25
|
+
/**
|
|
26
|
+
* Gap C1: optional model override for the review gate (cheaper model).
|
|
27
|
+
* Format: "provider/model" or just "model" (same provider as the agent).
|
|
28
|
+
* When absent, the review gate uses the agent's own provider/model.
|
|
29
|
+
*/
|
|
30
|
+
reviewModel?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Goal-blocked trigger (D3): after this many CONSECUTIVE gate runs that
|
|
33
|
+
* observe the session goal in phase "blocked", run one local-fate
|
|
34
|
+
* assessment (the same audit → classify → consult → apply pipeline as the
|
|
35
|
+
* gate's normal fate dimension) so the blocked encounter is distilled
|
|
36
|
+
* before the session moves on. 0 disables. The streak resets on any
|
|
37
|
+
* non-blocked run and after each triggered assessment; a declined
|
|
38
|
+
* proposal then follows the normal fate cooldown.
|
|
39
|
+
*/
|
|
40
|
+
goalBlockedWrapupTurns: number;
|
|
25
41
|
}
|
|
26
42
|
export interface GateState {
|
|
27
43
|
turns: number;
|
|
@@ -41,6 +57,12 @@ export interface GateState {
|
|
|
41
57
|
* window (the consultSkillEdits pattern — no nagging).
|
|
42
58
|
*/
|
|
43
59
|
fateRejects: Map<string, number>;
|
|
60
|
+
/**
|
|
61
|
+
* Consecutive gate runs that observed the goal phase "blocked" (D3).
|
|
62
|
+
* Reset to 0 by any non-blocked run and after a triggered assessment —
|
|
63
|
+
* see runGoalBlockedFate.
|
|
64
|
+
*/
|
|
65
|
+
goalBlockStreak: number;
|
|
44
66
|
}
|
|
45
67
|
/** Turns a rejected skill candidate stays silent before being offered again. */
|
|
46
68
|
export declare const SKILL_CONSULT_COOLDOWN_TURNS = 10;
|
|
@@ -54,10 +76,9 @@ export interface ReviewRecord {
|
|
|
54
76
|
refinementId?: string;
|
|
55
77
|
}
|
|
56
78
|
/**
|
|
57
|
-
* Count completed turns from agent/status transitions
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* not reliably carry the agent, so it is NOT used for counting.
|
|
79
|
+
* Count completed turns from agent/status transitions (running → idle).
|
|
80
|
+
* Exported for unit testing; production counting uses agent/turn-stopping
|
|
81
|
+
* (see registerAutoReview) which empirically carries the agent subject.
|
|
61
82
|
*/
|
|
62
83
|
export declare function advanceGateState(state: GateState, status: string): boolean;
|
|
63
84
|
export declare function registerAutoReview(ctx: Context, engine: EvolutionEngine, config: AutoReviewConfig): void;
|
|
@@ -71,6 +92,19 @@ export declare function registerAutoReview(ctx: Context, engine: EvolutionEngine
|
|
|
71
92
|
* local state (baseline checks compare local entries only).
|
|
72
93
|
*/
|
|
73
94
|
export declare function loadGateHarnessView(engine: EvolutionEngine, sessionId: string): HarnessState;
|
|
95
|
+
/**
|
|
96
|
+
* D3 (goal blocked → wrap-up coupling, reverse direction): count consecutive
|
|
97
|
+
* gate runs whose goal is in phase "blocked"; when the streak reaches
|
|
98
|
+
* `goalBlockedWrapupTurns`, run ONE local-fate assessment (same pipeline as
|
|
99
|
+
* the normal fate dimension — audit, classify, consult, apply deterministically).
|
|
100
|
+
* The streak resets on any non-blocked run and after a triggered assessment;
|
|
101
|
+
* a declined proposal is then protected by the normal fate cooldown, so a
|
|
102
|
+
* blocked session can never be nagged into another dialog.
|
|
103
|
+
*
|
|
104
|
+
* Exported for unit testing (the advanceGateState precedent); production runs
|
|
105
|
+
* it from runGate.
|
|
106
|
+
*/
|
|
107
|
+
export declare function runGoalBlockedFate(ctx: Context, engine: EvolutionEngine, agent: Agent, config: AutoReviewConfig, state: GateState, _reason: AutoRefineReason, record: (entry: Omit<ReviewRecord, "timestamp">) => void): Promise<void>;
|
|
74
108
|
/**
|
|
75
109
|
* Split a proposal into skill edits and everything else. Skill edits are the
|
|
76
110
|
* governed part: they need explicit user consent before the gate applies
|
package/lib/auto.js
CHANGED
|
@@ -25,13 +25,14 @@ import { notifyAutoReview } from "./notify.js";
|
|
|
25
25
|
import { runLocalFatePhase } from "./fate.js";
|
|
26
26
|
import { entrySourceOf } from "./source.js";
|
|
27
27
|
import { mergeHarnessStates } from "./state.js";
|
|
28
|
+
import { questionServiceOf } from "./approval.js";
|
|
29
|
+
import { buildEvolveCompleteEvent, emitEvolveComplete } from "./evolve-event.js";
|
|
28
30
|
/** Turns a rejected skill candidate stays silent before being offered again. */
|
|
29
31
|
export const SKILL_CONSULT_COOLDOWN_TURNS = 10;
|
|
30
32
|
/**
|
|
31
|
-
* Count completed turns from agent/status transitions
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* not reliably carry the agent, so it is NOT used for counting.
|
|
33
|
+
* Count completed turns from agent/status transitions (running → idle).
|
|
34
|
+
* Exported for unit testing; production counting uses agent/turn-stopping
|
|
35
|
+
* (see registerAutoReview) which empirically carries the agent subject.
|
|
35
36
|
*/
|
|
36
37
|
export function advanceGateState(state, status) {
|
|
37
38
|
if (status === "running") {
|
|
@@ -134,6 +135,19 @@ export function registerAutoReview(ctx, engine, config) {
|
|
|
134
135
|
});
|
|
135
136
|
});
|
|
136
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Gap C1: parse a "provider/model" or "model" string into its components.
|
|
140
|
+
* Returns undefined when the input is empty (no override).
|
|
141
|
+
*/
|
|
142
|
+
function parseReviewModel(reviewModel, fallbackProvider) {
|
|
143
|
+
if (!reviewModel || reviewModel.trim().length === 0)
|
|
144
|
+
return undefined;
|
|
145
|
+
const slash = reviewModel.indexOf("/");
|
|
146
|
+
if (slash > 0) {
|
|
147
|
+
return { provider: reviewModel.slice(0, slash), model: reviewModel.slice(slash + 1) };
|
|
148
|
+
}
|
|
149
|
+
return { provider: fallbackProvider ?? "deepseek", model: reviewModel };
|
|
150
|
+
}
|
|
137
151
|
function stateFor(map, sessionId) {
|
|
138
152
|
let state = map.get(sessionId);
|
|
139
153
|
if (!state) {
|
|
@@ -144,6 +158,7 @@ function stateFor(map, sessionId) {
|
|
|
144
158
|
skillRejects: new Map(),
|
|
145
159
|
lastFateAt: 0,
|
|
146
160
|
fateRejects: new Map(),
|
|
161
|
+
goalBlockStreak: 0,
|
|
147
162
|
};
|
|
148
163
|
map.set(sessionId, state);
|
|
149
164
|
}
|
|
@@ -171,8 +186,41 @@ export function loadGateHarnessView(engine, sessionId) {
|
|
|
171
186
|
*/
|
|
172
187
|
async function runGate(ctx, engine, agent, config, state, reason, record) {
|
|
173
188
|
await runReviewPhase(ctx, engine, agent, config, state, reason, record);
|
|
189
|
+
// D3: a goal stuck in "blocked" for consecutive gate runs gets one
|
|
190
|
+
// local-fate assessment (the pipeline below), so whatever led the goal
|
|
191
|
+
// astray is distilled before the session moves on.
|
|
192
|
+
await runGoalBlockedFate(ctx, engine, agent, config, state, reason, record);
|
|
174
193
|
await runLocalFatePhase(ctx, engine, agent, config, state, reason, record);
|
|
175
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* D3 (goal blocked → wrap-up coupling, reverse direction): count consecutive
|
|
197
|
+
* gate runs whose goal is in phase "blocked"; when the streak reaches
|
|
198
|
+
* `goalBlockedWrapupTurns`, run ONE local-fate assessment (same pipeline as
|
|
199
|
+
* the normal fate dimension — audit, classify, consult, apply deterministically).
|
|
200
|
+
* The streak resets on any non-blocked run and after a triggered assessment;
|
|
201
|
+
* a declined proposal is then protected by the normal fate cooldown, so a
|
|
202
|
+
* blocked session can never be nagged into another dialog.
|
|
203
|
+
*
|
|
204
|
+
* Exported for unit testing (the advanceGateState precedent); production runs
|
|
205
|
+
* it from runGate.
|
|
206
|
+
*/
|
|
207
|
+
export async function runGoalBlockedFate(ctx, engine, agent, config, state, _reason, record) {
|
|
208
|
+
if (config.goalBlockedWrapupTurns <= 0)
|
|
209
|
+
return;
|
|
210
|
+
const goal = goalServiceOf(ctx)?.get(agent);
|
|
211
|
+
if (goal?.phase !== "blocked") {
|
|
212
|
+
state.goalBlockStreak = 0;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
state.goalBlockStreak += 1;
|
|
216
|
+
if (state.goalBlockStreak < config.goalBlockedWrapupTurns) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
state.goalBlockStreak = 0; // one assessment per streak; declines follow the fate cooldown
|
|
220
|
+
const logger = ctx.logger("continual-evolve");
|
|
221
|
+
logger.info(`auto-review goal-blocked trigger [${agent.id}]: ${config.goalBlockedWrapupTurns} consecutive blocked gate runs → local-fate assessment`);
|
|
222
|
+
await runLocalFatePhase(ctx, engine, agent, config, state, "goal_blocked", record);
|
|
223
|
+
}
|
|
176
224
|
async function runReviewPhase(ctx, engine, agent, config, state, reason, record) {
|
|
177
225
|
const sessionId = agent.id;
|
|
178
226
|
const turnsSinceLastReview = state.turns - state.lastReviewAt;
|
|
@@ -192,6 +240,8 @@ async function runReviewPhase(ctx, engine, agent, config, state, reason, record)
|
|
|
192
240
|
const localState = engine.load("local", sessionId);
|
|
193
241
|
const harnessState = loadGateHarnessView(engine, sessionId);
|
|
194
242
|
const history = engine.history("local", sessionId);
|
|
243
|
+
// Gap C1: resolve optional review model override.
|
|
244
|
+
const reviewRoute = parseReviewModel(config.reviewModel, agent.options.provider);
|
|
195
245
|
const review = await reviewAutoRefine(ctx, {
|
|
196
246
|
agent,
|
|
197
247
|
state: harnessState,
|
|
@@ -199,6 +249,7 @@ async function runReviewPhase(ctx, engine, agent, config, state, reason, record)
|
|
|
199
249
|
trajectory,
|
|
200
250
|
context: { reason, turnsSinceLastReview },
|
|
201
251
|
budgetTokens: config.budgetTokens,
|
|
252
|
+
...(reviewRoute ? { overrideProvider: reviewRoute.provider, overrideModel: reviewRoute.model } : {}),
|
|
202
253
|
});
|
|
203
254
|
state.lastReviewAt = state.turns;
|
|
204
255
|
if (!review.shouldRefine) {
|
|
@@ -243,6 +294,8 @@ async function runReviewPhase(ctx, engine, agent, config, state, reason, record)
|
|
|
243
294
|
});
|
|
244
295
|
logger.info(`auto-review approved (${reason}) [${sessionId}] after ${turnsSinceLastReview} turns; auto-refine ${result.id}: ${result.appliedEdits.filter((e) => e.applied).length} applied, ${result.appliedEdits.filter((e) => !e.applied).length} failed — ${review.rationale}`);
|
|
245
296
|
record({ sessionId, reason, turnsSinceLastReview, outcome: "approved", rationale: review.rationale, refinementId: result.id });
|
|
297
|
+
// Gap C4: emit structured evolve_complete event for third-party consumers.
|
|
298
|
+
emitEvolveComplete(engine.baseDir, buildEvolveCompleteEvent(result, `auto_review:${reason}`, sessionId));
|
|
246
299
|
// Visibility: tell the user what the gate just persisted. Only the
|
|
247
300
|
// turn-interval path notifies — a compaction-triggered gate must not wake
|
|
248
301
|
// the agent mid-compaction — and only when something was actually applied
|
|
@@ -288,7 +341,7 @@ export async function consultSkillEdits(ctx, agent, skillEdits, gate) {
|
|
|
288
341
|
if (lastReject !== undefined && gate.turns - lastReject < SKILL_CONSULT_COOLDOWN_TURNS) {
|
|
289
342
|
return false;
|
|
290
343
|
}
|
|
291
|
-
const userQuestions = ctx
|
|
344
|
+
const userQuestions = questionServiceOf(ctx);
|
|
292
345
|
if (!userQuestions) {
|
|
293
346
|
return false;
|
|
294
347
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/evolve benchmark` subcommand handler. Extracted from command.ts (P2-2).
|
|
3
|
+
*/
|
|
4
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
5
|
+
import type { CommandInvocation, CommandResult } from "@deepseek-ai/dsh-commands";
|
|
6
|
+
import type { EvolutionEngine } from "./service.js";
|
|
7
|
+
import type { CommandRuntimeOptions } from "./command.js";
|
|
8
|
+
export declare function executeBenchmarkCommand(ctx: Context, engine: EvolutionEngine, invocation: CommandInvocation, rest: string[], runtime: CommandRuntimeOptions): Promise<CommandResult>;
|
|
9
|
+
//# sourceMappingURL=benchmark-command.d.ts.map
|