dsh-continual-evolve 0.6.0 → 0.7.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 +10 -9
- package/README.zh.md +10 -9
- package/lib/auto.d.ts +6 -4
- package/lib/auto.js +9 -6
- package/lib/command.js +51 -24
- package/lib/fate.js +3 -2
- package/lib/index.d.ts +47 -47
- package/lib/index.js +1 -1
- package/lib/inject.d.ts +51 -12
- package/lib/inject.js +104 -38
- package/lib/llm-text.js +2 -1
- package/lib/message-source.d.ts +23 -0
- package/lib/message-source.js +12 -0
- package/lib/notify.d.ts +5 -4
- package/lib/notify.js +2 -1
- package/lib/planner.d.ts +1 -1
- package/lib/planner.js +8 -2
- package/lib/project.d.ts +28 -0
- package/lib/project.js +66 -0
- package/lib/service.js +5 -0
- package/lib/source.d.ts +5 -4
- package/lib/source.js +3 -2
- package/lib/state.d.ts +8 -4
- package/lib/state.js +14 -7
- package/lib/store.d.ts +6 -0
- package/lib/store.js +11 -3
- package/lib/tool.d.ts +4 -1
- package/lib/tool.js +84 -29
- package/lib/types.d.ts +14 -2
- package/lib/types.js +12 -0
- package/lib/validate.js +30 -0
- package/lib/wrapup-command.js +1 -1
- package/lib/wrapup.d.ts +2 -2
- package/lib/wrapup.js +15 -1
- package/package.json +12 -12
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
|
Continual self-evolution for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): a versioned, auditable, rollback-safe harness state layer — prompt notes, memories, skills, subagent specs — refined from session trajectories.
|
|
13
13
|
|
|
@@ -17,7 +17,8 @@ Continual self-evolution for [DeepSeek Harness](https://github.com/deepseek-ai/d
|
|
|
17
17
|
|
|
18
18
|
Agents accumulate reusable experience (repeated failures, durable facts, reusable procedures) and forget it next session. This plugin turns that experience into first-class state:
|
|
19
19
|
|
|
20
|
-
- **
|
|
20
|
+
- **Three scopes** with merge semantics (global < project < local): **local** per-session staging, **project** per-workspace cross-session store, **global** cross-project — plus mechanical promotion guards so only portable, substantial, non-duplicate knowledge reaches global
|
|
21
|
+
- **Typed one-fact memories**: every memory entry carries a recall type (`user | feedback | project | reference`); pitfalls (`feedback`) must include Why + How to apply
|
|
21
22
|
- **Deterministic rollback**: inverse edits generated from applied results — no LLM re-guessing
|
|
22
23
|
- **Benchmark loop**: candidate refinements are evaluated against frozen cases by a separate scorer before acceptance (rubric encrypted at rest)
|
|
23
24
|
- **Store hygiene**: `/evolve consolidate` turns write-time conflict hints and zero-use staleness into one approved, fully reversible batch of archives — with `merge`, near-duplicate content folds into the surviving original
|
|
@@ -26,8 +27,8 @@ Agents accumulate reusable experience (repeated failures, durable facts, reusabl
|
|
|
26
27
|
|
|
27
28
|
1. **Sediment** — the model creates entries via `evolve_add`, or the automatic review gate proposes them from the session trajectory (turn-interval + compaction checkpoints).
|
|
28
29
|
2. **Guard** — code-enforced validation: edit schema, blast-radius/scope coherence, and the promotion policy (project-scoped markers, thin content, near-duplicate detection, credential screening keep the global store clean — secrets are rejected at every write sink, including mount materialization). Global creates that near-duplicate an existing entry are rejected at write time (≥0.8 similarity); moderate overlaps carry a `conflictHint` for later consolidation.
|
|
29
|
-
3. **Approve** — global writes require explicit human approval; local-fate proposals are consulted before they land.
|
|
30
|
-
4. **Apply & inject** — atomic apply with snapshot + audit event. Prompt notes and delegation specs inject into the system prompt (capped, relevance-ranked, contradicted entries demoted, zero tokens when empty); memories/skills appear as a capped directory index.
|
|
30
|
+
3. **Approve** — global and project writes require explicit human approval; local-fate proposals are consulted before they land.
|
|
31
|
+
4. **Apply & inject** — atomic apply with snapshot + audit event. Prompt notes and delegation specs inject into the system prompt (capped, relevance-ranked, contradicted entries demoted, zero tokens when empty); memories/skills appear as a relevance-ordered capped directory index (`- [memory:type:id] title` hooks, full text one `evolve_list` away).
|
|
31
32
|
5. **Validate & roll back** — benchmarks score candidates against frozen cases; rejected candidates roll back deterministically and are captured as draft regression cases (`auto_regression` benchmark).
|
|
32
33
|
|
|
33
34
|
## Install
|
|
@@ -49,7 +50,7 @@ Commands (in-session):
|
|
|
49
50
|
| Command | Effect |
|
|
50
51
|
|---|---|
|
|
51
52
|
| `/evolve` | help + current local store |
|
|
52
|
-
| `/evolve list · history · rollback <id>` | inspect and revert (add `global` for the cross-
|
|
53
|
+
| `/evolve list · history · rollback <id>` | inspect and revert (add `project` for this project's store, `global` for the cross-project store) |
|
|
53
54
|
| `/evolve plan [msg]` | run the LLM planner against the store |
|
|
54
55
|
| `/evolve wrapup` | assess this session's local entries: promote / archive / keep |
|
|
55
56
|
| `/evolve archive · unarchive · demote <id>` | hide from injection (data kept, restorable) — `demote` targets global noise |
|
|
@@ -65,7 +66,7 @@ Model tools: `evolve_list / add / update / delete / rollback`.
|
|
|
65
66
|
|
|
66
67
|
For third-party consumers: every applied evolution (gate or manual) appends a structured `evolve_complete` event to `reviews.jsonl` (`src/evolve-event.ts` defines the shape) alongside the human-readable audit records.
|
|
67
68
|
|
|
68
|
-
Injection shape: prompt notes and delegation specs inject with content (≤6/kind × 180 chars, relevance-ranked). Memories and skills appear as a directory index (`[
|
|
69
|
+
Injection shape: prompt notes and delegation specs inject with content (≤6/kind × 180 chars, relevance-ranked). Memories and skills appear as a relevance-ordered directory index (`[memory:type:id] title` hooks, capped at 15 lines with a fold counter) — full text via `evolve_list`. Empty store = zero injected tokens.
|
|
69
70
|
|
|
70
71
|
## Configuration
|
|
71
72
|
|
|
@@ -77,7 +78,7 @@ Injection shape: prompt notes and delegation specs inject with content (≤6/kin
|
|
|
77
78
|
| `maxReviewInputChars` | `40000` | trajectory slice handed to the gate |
|
|
78
79
|
| `reviewBudgetTokens` | `4096` | output budget for the gate call |
|
|
79
80
|
| `notifyOnAutoReview` | `true` | visible follow-up notice after an applied gate run |
|
|
80
|
-
| `requireGlobalApproval` | `true` | global edits ask for explicit approval |
|
|
81
|
+
| `requireGlobalApproval` | `true` | global and project edits ask for explicit approval |
|
|
81
82
|
| `localFate` | `true` | gate audits local entries and proposes promote/archive (consulted, never silent) |
|
|
82
83
|
| `fateIntervalTurns` | follows `reviewIntervalTurns` | minimum turns between fate assessments |
|
|
83
84
|
| `goalBlockedWrapupTurns` | `3` | consecutive blocked-goal gate runs trigger one fate assessment (`0` disables) |
|
|
@@ -105,7 +106,7 @@ Example profile patch:
|
|
|
105
106
|
|
|
106
107
|
```bash
|
|
107
108
|
pnpm install && pnpm build # deps + tsc -> lib/
|
|
108
|
-
pnpm test # vitest (
|
|
109
|
+
pnpm test # vitest (600 tests)
|
|
109
110
|
pnpm test:coverage # v8 coverage, thresholds enforced in CI
|
|
110
111
|
pnpm lint # oxlint src test
|
|
111
112
|
```
|
|
@@ -114,7 +115,7 @@ Project layout:
|
|
|
114
115
|
|
|
115
116
|
```
|
|
116
117
|
├── src/ # engine, tools, commands, gate, fate, benchmark, usage…
|
|
117
|
-
├── test/ # vitest suites (
|
|
118
|
+
├── test/ # vitest suites (37 files)
|
|
118
119
|
├── lib/ # build output (tsc)
|
|
119
120
|
├── docs/
|
|
120
121
|
│ ├── design.md # full design doc (hardening matrix)
|
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
|
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)的持续自进化插件:一套**版本化、可审计、可回滚**的 harness 状态层——提示词补充、记忆、技能、子代理规格——从会话轨迹中沉淀而来。
|
|
13
13
|
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
Agent 在每个会话里积累可复用经验(重复失败、持久事实、可复用流程),下个会话就忘掉。本插件把这些经验变成一等公民的持久状态:
|
|
19
19
|
|
|
20
|
-
- **
|
|
20
|
+
- **三作用域**与合并语义(global < project < local):**local** 本会话暂存、**project** 本项目跨会话库、**global** 跨项目——配合机械化晋升守卫,只有可携带、有分量、非重复的知识才能进全局
|
|
21
|
+
- **类型化单条记忆**:每条 memory 带召回类型(`user | feedback | project | reference`);踩坑(`feedback`)必须含 Why + How to apply
|
|
21
22
|
- **确定性回滚**:逆操作编辑由已应用结果生成——不靠 LLM 重新猜测
|
|
22
23
|
- **benchmark 闭环**:候选沉淀先经冻结用例 + 独立评分者评估再接受(rubric 加密落盘)
|
|
23
24
|
- **store 卫生**:`/evolve consolidate` 把写入时冲突提示与零使用陈旧条目变成一次批准、完全可逆的批量归档——加 `merge` 可将近重复内容并入幸存原条目
|
|
@@ -26,8 +27,8 @@ Agent 在每个会话里积累可复用经验(重复失败、持久事实、
|
|
|
26
27
|
|
|
27
28
|
1. **沉淀**——模型经 `evolve_add` 创建条目,或自动 review 门禁从会话轨迹提议(回合间隔 + 压缩检查点)。
|
|
28
29
|
2. **守卫**——代码强制校验:编辑 schema、blast-radius 与作用域一致性、晋升政策(项目专属标记 / 过薄内容 / 近似重复检测 / 凭据筛查保持全局库干净——密钥类内容在所有写入出口被拒,含 mount 物化)。全局 create 与既有条目高度相似(≥0.8)时写入即拒;中等重叠带 `conflictHint` 供后续合并。
|
|
29
|
-
3.
|
|
30
|
-
4. **应用与注入**——原子应用带快照与审计事件。prompt 补充与委派规格注入系统提示词(封顶、按相关性排序、被证伪条目降权、空 store 零 token);memory/skill
|
|
30
|
+
3. **审批**——全局与项目写入需明确人工批准;local 归宿提议先征询后落地。
|
|
31
|
+
4. **应用与注入**——原子应用带快照与审计事件。prompt 补充与委派规格注入系统提示词(封顶、按相关性排序、被证伪条目降权、空 store 零 token);memory/skill 以按相关性排序的目录索引出现(`[memory:type:id] 标题`钩子)。
|
|
31
32
|
5. **验证与回滚**——benchmark 用冻结用例为候选打分;被拒候选确定性回滚,并自动沉淀为 draft 回归用例(`auto_regression` 基准)。
|
|
32
33
|
|
|
33
34
|
## 安装
|
|
@@ -49,7 +50,7 @@ dsh plugin add ZK-Andy/dsh-continual-evolve
|
|
|
49
50
|
| 命令 | 效果 |
|
|
50
51
|
|---|---|
|
|
51
52
|
| `/evolve` | 帮助 + 当前 local store |
|
|
52
|
-
| `/evolve list · history · rollback <id>` | 查看与回滚(加 `global`
|
|
53
|
+
| `/evolve list · history · rollback <id>` | 查看与回滚(加 `project` 操作本项目库,`global` 操作跨项目库) |
|
|
53
54
|
| `/evolve plan [msg]` | 对 store 运行 LLM 规划器 |
|
|
54
55
|
| `/evolve wrapup` | 收尾本会话 local 条目:晋升 / 归档 / 保留 |
|
|
55
56
|
| `/evolve archive · unarchive · demote <id>` | 从注入中隐藏(数据保留可恢复)——`demote` 针对全局噪声 |
|
|
@@ -65,7 +66,7 @@ dsh plugin add ZK-Andy/dsh-continual-evolve
|
|
|
65
66
|
|
|
66
67
|
第三方消费:每次进化落地(门禁或手动)都会向 `reviews.jsonl` 追加结构化 `evolve_complete` 事件(shape 见 `src/evolve-event.ts`),与人类可读的审计记录并存。
|
|
67
68
|
|
|
68
|
-
注入形态:prompt 补充与委派规格带内容注入(每 kind ≤6 条 × 180 字符,按相关性排序)。memory/skill
|
|
69
|
+
注入形态:prompt 补充与委派规格带内容注入(每 kind ≤6 条 × 180 字符,按相关性排序)。memory/skill 以按相关性排序的目录索引出现(`[memory:type:id] 标题`钩子,15 行封顶 + 折叠计数行)——全文经 `evolve_list` 获取。空 store = 零注入 token。
|
|
69
70
|
|
|
70
71
|
## 配置
|
|
71
72
|
|
|
@@ -77,7 +78,7 @@ dsh plugin add ZK-Andy/dsh-continual-evolve
|
|
|
77
78
|
| `maxReviewInputChars` | `40000` | 交给门禁的轨迹切片 |
|
|
78
79
|
| `reviewBudgetTokens` | `4096` | 门禁调用输出预算 |
|
|
79
80
|
| `notifyOnAutoReview` | `true` | 门禁应用后发可见跟进通知 |
|
|
80
|
-
| `requireGlobalApproval` | `true` |
|
|
81
|
+
| `requireGlobalApproval` | `true` | 全局与项目写入需明确批准 |
|
|
81
82
|
| `localFate` | `true` | 门禁审计本地条目并提议晋升/归档(先征询,绝不静默) |
|
|
82
83
|
| `fateIntervalTurns` | 跟随 `reviewIntervalTurns` | 归宿评估的最小回合间隔 |
|
|
83
84
|
| `goalBlockedWrapupTurns` | `3` | 连续阻塞目标的门禁轮数触发一次归宿评估(`0` 关闭) |
|
|
@@ -105,7 +106,7 @@ profile patch 示例:
|
|
|
105
106
|
|
|
106
107
|
```bash
|
|
107
108
|
pnpm install && pnpm build # 依赖 + tsc -> lib/
|
|
108
|
-
pnpm test # vitest(
|
|
109
|
+
pnpm test # vitest(600 例)
|
|
109
110
|
pnpm test:coverage # v8 覆盖率,CI 强制阈值
|
|
110
111
|
pnpm lint # oxlint src test
|
|
111
112
|
```
|
|
@@ -114,7 +115,7 @@ pnpm lint # oxlint src test
|
|
|
114
115
|
|
|
115
116
|
```
|
|
116
117
|
├── src/ # 引擎、工具、命令、门禁、fate、benchmark、usage…
|
|
117
|
-
├── test/ # vitest 测试套件(
|
|
118
|
+
├── test/ # vitest 测试套件(37 个文件)
|
|
118
119
|
├── lib/ # 构建产物(tsc)
|
|
119
120
|
├── docs/
|
|
120
121
|
│ ├── design.md # 完整设计文档(硬化矩阵)
|
package/lib/auto.d.ts
CHANGED
|
@@ -103,14 +103,16 @@ export declare function parseReviewModel(reviewModel: string | undefined, fallba
|
|
|
103
103
|
} | undefined;
|
|
104
104
|
/**
|
|
105
105
|
* The state view the gate and planner judge: the session's local entries
|
|
106
|
-
* merged with the global
|
|
107
|
-
* the global half the gate cannot see that a topic is already
|
|
108
|
-
* cross-session and happily re-sediments a local duplicate of it.
|
|
106
|
+
* merged with the project and global stores, each entry carrying its real
|
|
107
|
+
* scope. Without the global half the gate cannot see that a topic is already
|
|
108
|
+
* covered cross-session and happily re-sediments a local duplicate of it.
|
|
109
109
|
*
|
|
110
110
|
* The merged view is read-only context — applying still targets the raw
|
|
111
111
|
* local state (baseline checks compare local entries only).
|
|
112
112
|
*/
|
|
113
|
-
export declare function loadGateHarnessView(engine: EvolutionEngine, sessionId: string
|
|
113
|
+
export declare function loadGateHarnessView(engine: EvolutionEngine, sessionId: string, opts?: {
|
|
114
|
+
projectKey?: string;
|
|
115
|
+
}): HarnessState;
|
|
114
116
|
/**
|
|
115
117
|
* D3 (goal blocked → wrap-up coupling, reverse direction): count consecutive
|
|
116
118
|
* gate runs whose goal is in phase "blocked"; when the streak reaches
|
package/lib/auto.js
CHANGED
|
@@ -25,6 +25,7 @@ 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 { projectKeyOf } from "./project.js";
|
|
28
29
|
import { questionServiceOf } from "./approval.js";
|
|
29
30
|
import { buildEvolveCompleteEvent, emitEvolveComplete } from "./evolve-event.js";
|
|
30
31
|
import { captureAutoCase } from "./autocase.js";
|
|
@@ -153,15 +154,16 @@ function stateFor(map, sessionId) {
|
|
|
153
154
|
}
|
|
154
155
|
/**
|
|
155
156
|
* The state view the gate and planner judge: the session's local entries
|
|
156
|
-
* merged with the global
|
|
157
|
-
* the global half the gate cannot see that a topic is already
|
|
158
|
-
* cross-session and happily re-sediments a local duplicate of it.
|
|
157
|
+
* merged with the project and global stores, each entry carrying its real
|
|
158
|
+
* scope. Without the global half the gate cannot see that a topic is already
|
|
159
|
+
* covered cross-session and happily re-sediments a local duplicate of it.
|
|
159
160
|
*
|
|
160
161
|
* The merged view is read-only context — applying still targets the raw
|
|
161
162
|
* local state (baseline checks compare local entries only).
|
|
162
163
|
*/
|
|
163
|
-
export function loadGateHarnessView(engine, sessionId) {
|
|
164
|
-
|
|
164
|
+
export function loadGateHarnessView(engine, sessionId, opts) {
|
|
165
|
+
const projectState = opts?.projectKey ? engine.load("project", opts.projectKey) : undefined;
|
|
166
|
+
return mergeHarnessStates(engine.load("global", undefined), engine.load("local", sessionId), projectState ? { projectState } : undefined);
|
|
165
167
|
}
|
|
166
168
|
/**
|
|
167
169
|
* One gate run = review phase + local-fate phase (#11 P2). The review phase
|
|
@@ -239,7 +241,8 @@ async function runReviewPhase(ctx, engine, agent, config, state, reason, record)
|
|
|
239
241
|
// can recognize topics already covered globally and decline duplicates;
|
|
240
242
|
// applying still targets the raw local store.
|
|
241
243
|
const localState = engine.load("local", sessionId);
|
|
242
|
-
const
|
|
244
|
+
const projectKey = projectKeyOf(agent);
|
|
245
|
+
const harnessState = loadGateHarnessView(engine, sessionId, projectKey ? { projectKey } : undefined);
|
|
243
246
|
const history = engine.history("local", sessionId);
|
|
244
247
|
// Gap C1: resolve optional review model override.
|
|
245
248
|
const reviewRoute = parseReviewModel(config.reviewModel, agent.options.provider);
|
package/lib/command.js
CHANGED
|
@@ -13,11 +13,12 @@ import { executeGoalCommand } from "./goal-command.js";
|
|
|
13
13
|
import { executeMountCommand, executeUnmountCommand } from "./mount-command.js";
|
|
14
14
|
import { executeBenchmarkCommand } from "./benchmark-command.js";
|
|
15
15
|
import { executeWrapupCommand } from "./wrapup-command.js";
|
|
16
|
+
import { projectKeyOf } from "./project.js";
|
|
16
17
|
import { loadUsage } from "./usage.js";
|
|
17
18
|
import { planConsolidation } from "./consolidate.js";
|
|
18
19
|
const USAGE = `Usage:
|
|
19
20
|
/evolve show this help and the current local store
|
|
20
|
-
/evolve list [global] list entries (
|
|
21
|
+
/evolve list [project|global] list entries (default local staging; "project" = this project's cross-session store, "global" = cross-project store)
|
|
21
22
|
/evolve history [global] show applied refinements (rollback ids)
|
|
22
23
|
/evolve rollback <id> [global] deterministically revert a refinement
|
|
23
24
|
/evolve plan [msg] run the LLM planner against the current store
|
|
@@ -52,8 +53,29 @@ function scopeArg(tokens) {
|
|
|
52
53
|
if (tokens[0] === "global") {
|
|
53
54
|
return { scope: "global", rest: tokens.slice(1) };
|
|
54
55
|
}
|
|
56
|
+
if (tokens[0] === "project") {
|
|
57
|
+
return { scope: "project", rest: tokens.slice(1) };
|
|
58
|
+
}
|
|
55
59
|
return { scope: "local", rest: tokens };
|
|
56
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The store id a scope reads/writes from the human command: live session id
|
|
63
|
+
* for local, derived project key for project, undefined for global. Throws
|
|
64
|
+
* for project when the session cwd is unavailable.
|
|
65
|
+
*/
|
|
66
|
+
function storeIdForCommand(scope, invocation) {
|
|
67
|
+
if (scope === "local") {
|
|
68
|
+
return invocation.agent.id;
|
|
69
|
+
}
|
|
70
|
+
if (scope === "project") {
|
|
71
|
+
const key = projectKeyOf(invocation.agent);
|
|
72
|
+
if (!key) {
|
|
73
|
+
throw new Error("project scope needs the session cwd (unavailable here) — use local or global instead");
|
|
74
|
+
}
|
|
75
|
+
return key;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
57
79
|
/**
|
|
58
80
|
* Tokenize a command's raw input with shell-like quoting:
|
|
59
81
|
* - a `#` outside quotes starts a comment (rest of the line is dropped);
|
|
@@ -129,11 +151,11 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
129
151
|
return success(`${USAGE}\n\n${formatHarnessStateForPrompt(engine.load("local", sessionId))}`);
|
|
130
152
|
case "list": {
|
|
131
153
|
const { scope } = scopeArg(rest);
|
|
132
|
-
return success(formatHarnessStateForPrompt(engine.load(scope,
|
|
154
|
+
return success(formatHarnessStateForPrompt(engine.load(scope, storeIdForCommand(scope, invocation))));
|
|
133
155
|
}
|
|
134
156
|
case "history": {
|
|
135
157
|
const { scope } = scopeArg(rest);
|
|
136
|
-
const history = engine.history(scope,
|
|
158
|
+
const history = engine.history(scope, storeIdForCommand(scope, invocation));
|
|
137
159
|
return success(historyForPrompt(history) || "(no refinements yet)");
|
|
138
160
|
}
|
|
139
161
|
case "rollback": {
|
|
@@ -142,7 +164,7 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
142
164
|
if (!id) {
|
|
143
165
|
return error(`rollback requires a refinement id.\n${USAGE}`);
|
|
144
166
|
}
|
|
145
|
-
const result = engine.rollback(scope,
|
|
167
|
+
const result = engine.rollback(scope, storeIdForCommand(scope, invocation), id);
|
|
146
168
|
return success(renderResult(result));
|
|
147
169
|
}
|
|
148
170
|
case "archive":
|
|
@@ -154,9 +176,9 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
154
176
|
return error(`${sub} requires an entry id.\n${USAGE}`);
|
|
155
177
|
}
|
|
156
178
|
if (sub === "demote") {
|
|
157
|
-
return demoteEntry(engine, id, sessionId);
|
|
179
|
+
return demoteEntry(engine, id, sessionId, projectKeyOf(invocation.agent));
|
|
158
180
|
}
|
|
159
|
-
const state = engine.load(scope,
|
|
181
|
+
const state = engine.load(scope, storeIdForCommand(scope, invocation));
|
|
160
182
|
const found = findEntryById(state, id);
|
|
161
183
|
if (!found) {
|
|
162
184
|
return error(`entry ${id} not found in the ${scope} store`);
|
|
@@ -170,7 +192,7 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
170
192
|
delete metadata[ARCHIVED_AT_KEY];
|
|
171
193
|
}
|
|
172
194
|
const archived = sub === "archive";
|
|
173
|
-
const result = engine.apply(scope,
|
|
195
|
+
const result = engine.apply(scope, storeIdForCommand(scope, invocation), {
|
|
174
196
|
summary: `${archived ? "Archive" : "Unarchive"} entry ${kind}:${id}`,
|
|
175
197
|
rationale: "Human-invoked archive/unarchive via the /evolve command.",
|
|
176
198
|
expectedOutcome: `Entry ${archived ? "is hidden from injection (data kept, restorable)" : "is injected again"}.`,
|
|
@@ -260,8 +282,8 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
260
282
|
if (!path) {
|
|
261
283
|
return error(`export requires an output path.\n${USAGE}`);
|
|
262
284
|
}
|
|
263
|
-
const state = engine.load(scope,
|
|
264
|
-
const history = engine.history(scope,
|
|
285
|
+
const state = engine.load(scope, storeIdForCommand(scope, invocation));
|
|
286
|
+
const history = engine.history(scope, storeIdForCommand(scope, invocation));
|
|
265
287
|
const payload = {
|
|
266
288
|
version: 1,
|
|
267
289
|
scope,
|
|
@@ -293,7 +315,7 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
293
315
|
},
|
|
294
316
|
refinements: Array.isArray(payload["refinements"]) ? payload["refinements"] : [],
|
|
295
317
|
};
|
|
296
|
-
const paths = storePaths(engine.baseDir, scope,
|
|
318
|
+
const paths = storePaths(engine.baseDir, scope, storeIdForCommand(scope, invocation));
|
|
297
319
|
saveHarnessState(paths.stateDir, state);
|
|
298
320
|
if (Array.isArray(payload["history"])) {
|
|
299
321
|
for (const result of payload["history"]) {
|
|
@@ -307,8 +329,8 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
307
329
|
case "plan": {
|
|
308
330
|
const { scope, rest: after } = scopeArg(rest);
|
|
309
331
|
const instructions = after.length > 0 ? after.join(" ") : undefined;
|
|
310
|
-
const state = engine.load(scope,
|
|
311
|
-
const history = engine.history(scope,
|
|
332
|
+
const state = engine.load(scope, storeIdForCommand(scope, invocation));
|
|
333
|
+
const history = engine.history(scope, storeIdForCommand(scope, invocation));
|
|
312
334
|
const proposal = await planWithLlm(ctx, {
|
|
313
335
|
agent: invocation.agent,
|
|
314
336
|
state,
|
|
@@ -319,10 +341,10 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
319
341
|
// skill-creator template facts (fallback: builtin guide).
|
|
320
342
|
skillsRoot: join(engine.baseDir, "skills"),
|
|
321
343
|
});
|
|
322
|
-
if (scope === "global" && opts.requireGlobalApproval && proposal.edits.length > 0) {
|
|
323
|
-
await requireGlobalApproval(ctx, invocation.agent, invocation.signal, `/evolve plan
|
|
344
|
+
if ((scope === "global" || scope === "project") && opts.requireGlobalApproval && proposal.edits.length > 0) {
|
|
345
|
+
await requireGlobalApproval(ctx, invocation.agent, invocation.signal, `/evolve plan ${scope} 将应用 ${proposal.edits.length} 条编辑到${scope === "project" ? "本项目" : "跨会话"} store:${proposal.summary}`);
|
|
324
346
|
}
|
|
325
|
-
const result = engine.apply(scope,
|
|
347
|
+
const result = engine.apply(scope, storeIdForCommand(scope, invocation), proposal, {
|
|
326
348
|
scope,
|
|
327
349
|
baselineState: state,
|
|
328
350
|
...(entrySourceOf(invocation.agent, sessionId) ? { source: entrySourceOf(invocation.agent, sessionId) } : {}),
|
|
@@ -354,18 +376,23 @@ async function executeEvolveCommand(ctx, engine, invocation, opts, runtime) {
|
|
|
354
376
|
}
|
|
355
377
|
/**
|
|
356
378
|
* Demote (2026-08-22): hide an entry from injection WITHOUT deleting it —
|
|
357
|
-
* the one-command remedy for
|
|
358
|
-
*
|
|
359
|
-
* local store. The data stays; `/evolve unarchive` restores it.
|
|
379
|
+
* the one-command remedy for store pollution. Searches the global store
|
|
380
|
+
* first (the primary target: cross-project noise), then the project store,
|
|
381
|
+
* then the session's local store. The data stays; `/evolve unarchive` restores it.
|
|
360
382
|
*/
|
|
361
|
-
function demoteEntry(engine, id, sessionId) {
|
|
362
|
-
|
|
363
|
-
|
|
383
|
+
function demoteEntry(engine, id, sessionId, projectKey) {
|
|
384
|
+
const targets = [
|
|
385
|
+
{ scope: "global", storeId: undefined },
|
|
386
|
+
...(projectKey ? [{ scope: "project", storeId: projectKey }] : []),
|
|
387
|
+
{ scope: "local", storeId: sessionId },
|
|
388
|
+
];
|
|
389
|
+
for (const { scope, storeId } of targets) {
|
|
390
|
+
const state = engine.load(scope, storeId);
|
|
364
391
|
const found = findEntryById(state, id);
|
|
365
392
|
if (!found)
|
|
366
393
|
continue;
|
|
367
394
|
const [kind, entry] = found;
|
|
368
|
-
const result = engine.apply(scope,
|
|
395
|
+
const result = engine.apply(scope, storeId, {
|
|
369
396
|
summary: `demote: archive ${kind}:${id} from the ${scope} store`,
|
|
370
397
|
rationale: "Human-invoked demote via the /evolve command.",
|
|
371
398
|
expectedOutcome: "The entry is hidden from injection in every scope it touched; data is kept and restorable.",
|
|
@@ -380,10 +407,10 @@ function demoteEntry(engine, id, sessionId) {
|
|
|
380
407
|
},
|
|
381
408
|
],
|
|
382
409
|
}, { scope });
|
|
383
|
-
const restoreScope = scope === "global" ? " global" : "";
|
|
410
|
+
const restoreScope = scope === "global" ? " global" : scope === "project" ? " project" : "";
|
|
384
411
|
return success(`demoted ${kind}:${id} from the ${scope} store (archived — restore with /evolve unarchive ${id}${restoreScope})\n${renderResult(result)}`);
|
|
385
412
|
}
|
|
386
|
-
return error(`entry ${id} not found in the global or local store`);
|
|
413
|
+
return error(`entry ${id} not found in the global, project, or local store`);
|
|
387
414
|
}
|
|
388
415
|
function renderResult(result) {
|
|
389
416
|
const applied = result.appliedEdits.filter((e) => e.applied);
|
package/lib/fate.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
2
|
+
import { EVOLVE_MESSAGE_SOURCE } from "./message-source.js";
|
|
2
3
|
import { DEFAULT_PROMOTION_POLICY } from "./promotion.js";
|
|
3
4
|
import { questionServiceOf } from "./approval.js";
|
|
4
5
|
import { assessLocalEntries, candidateKey, filterPromotable, listLocalCandidates, splitArchiveGuards, splitPromoteBlocked, splitPromoteProposals, wholePromoteProposals, } from "./wrapup.js";
|
|
@@ -25,7 +26,7 @@ export function planLocalFates(items, candidates, globalState, policy = DEFAULT_
|
|
|
25
26
|
splitSkipped.push({ key: item.key, reason: "not in the audited candidate list" });
|
|
26
27
|
continue;
|
|
27
28
|
}
|
|
28
|
-
const blocked = splitPromoteBlocked(item, globalState, candidate.kind, policy);
|
|
29
|
+
const blocked = splitPromoteBlocked(item, globalState, candidate.kind, policy, candidate.metadata);
|
|
29
30
|
if (blocked) {
|
|
30
31
|
splitSkipped.push({ key: item.key, reason: blocked });
|
|
31
32
|
continue;
|
|
@@ -331,7 +332,7 @@ function notifyFateApplied(ctx, agent, applied) {
|
|
|
331
332
|
try {
|
|
332
333
|
agent.followup(createUserMessage({
|
|
333
334
|
content: [{ type: "text", text: buildFateNotice(applied) }],
|
|
334
|
-
source:
|
|
335
|
+
source: EVOLVE_MESSAGE_SOURCE,
|
|
335
336
|
}));
|
|
336
337
|
}
|
|
337
338
|
catch (cause) {
|
package/lib/index.d.ts
CHANGED
|
@@ -5,145 +5,145 @@ export declare const name = "continual-evolve";
|
|
|
5
5
|
/** Service key under which the evolution engine is published. */
|
|
6
6
|
export declare const EVOLUTION_SERVICE = "evolution";
|
|
7
7
|
export declare const inject: string[];
|
|
8
|
-
export declare const Config: z<Schemastery.ObjectS<{
|
|
8
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
9
9
|
/** Root for evolution stores; defaults to the resolved DSH home. */
|
|
10
|
-
baseDir: z<string, string>;
|
|
10
|
+
baseDir: z<string, string, "plain">;
|
|
11
11
|
/** System-prompt section order for the evolution guidance. */
|
|
12
|
-
sectionOrder: z<number, number>;
|
|
12
|
+
sectionOrder: z<number, number, "defined">;
|
|
13
13
|
/** Enable the automatic review gate (off by default: it costs model calls). */
|
|
14
|
-
autoReview: z<boolean, boolean>;
|
|
14
|
+
autoReview: z<boolean, boolean, "defined">;
|
|
15
15
|
/** Gate runs when this many turns have passed since the last review. */
|
|
16
|
-
reviewIntervalTurns: z<number, number>;
|
|
16
|
+
reviewIntervalTurns: z<number, number, "defined">;
|
|
17
17
|
/** Trajectory slice handed to the gate, in characters. */
|
|
18
|
-
maxReviewInputChars: z<number, number>;
|
|
18
|
+
maxReviewInputChars: z<number, number, "defined">;
|
|
19
19
|
/** Output budget for the cheap gate call. */
|
|
20
|
-
reviewBudgetTokens: z<number, number>;
|
|
20
|
+
reviewBudgetTokens: z<number, number, "defined">;
|
|
21
21
|
/** After an approved gate run with applied edits, queue a visible follow-up notice. */
|
|
22
|
-
notifyOnAutoReview: z<boolean, boolean>;
|
|
22
|
+
notifyOnAutoReview: z<boolean, boolean, "defined">;
|
|
23
23
|
/** Cross-session (global) edits require an explicit human approval. */
|
|
24
|
-
requireGlobalApproval: z<boolean, boolean>;
|
|
24
|
+
requireGlobalApproval: z<boolean, boolean, "defined">;
|
|
25
25
|
/** Skills root for materialized skill entries; defaults to <dshHome>/skills. */
|
|
26
|
-
skillsDir: z<string, string>;
|
|
26
|
+
skillsDir: z<string, string, "plain">;
|
|
27
27
|
/** Passphrase for rubric encryption; falls back to DSH_EVOLVE_RUBRIC_KEY, then a local key file. */
|
|
28
|
-
rubricKey: z<string, string>;
|
|
28
|
+
rubricKey: z<string, string, "plain">;
|
|
29
29
|
/** Write all cordis log messages to <baseDir>/evolve/plugin.log (JSONL). */
|
|
30
|
-
logToFile: z<boolean, boolean>;
|
|
30
|
+
logToFile: z<boolean, boolean, "defined">;
|
|
31
31
|
/** File log level: 0=error, 1=info, 2=warn, 3=debug. */
|
|
32
|
-
logLevel: z<number, number>;
|
|
32
|
+
logLevel: z<number, number, "defined">;
|
|
33
33
|
/** Rotate the file log when it exceeds this many bytes. */
|
|
34
|
-
logMaxBytes: z<number, number>;
|
|
34
|
+
logMaxBytes: z<number, number, "defined">;
|
|
35
35
|
/** After a benchmark decision rejects a candidate, roll the refinement back automatically. */
|
|
36
|
-
autoRollbackOnReject: z<boolean, boolean>;
|
|
36
|
+
autoRollbackOnReject: z<boolean, boolean, "defined">;
|
|
37
37
|
/**
|
|
38
38
|
* P1 auto-case capture: failed evolution attempts (benchmark-rejected
|
|
39
39
|
* candidates, gate proposals without consent) land as draft cases in the
|
|
40
40
|
* auto-regression container benchmark, seeding the regression loop.
|
|
41
41
|
*/
|
|
42
|
-
autoCase: z<boolean, boolean>;
|
|
42
|
+
autoCase: z<boolean, boolean, "defined">;
|
|
43
43
|
/**
|
|
44
44
|
* Gap C1: optional model override for the review gate (cheaper model).
|
|
45
45
|
* Format: "provider/model" or just "model" (same provider as the agent).
|
|
46
46
|
* When absent, the review gate uses the agent's own provider/model.
|
|
47
47
|
*/
|
|
48
|
-
reviewModel: z<string, string>;
|
|
48
|
+
reviewModel: z<string, string, "plain">;
|
|
49
49
|
/**
|
|
50
50
|
* Gate local-fate dimension (#11 P2): the gate audits the session's local
|
|
51
51
|
* entries on its own cadence and proposes promote/archive — consulted
|
|
52
52
|
* first, never written silently. Only meaningful with autoReview on.
|
|
53
53
|
*/
|
|
54
|
-
localFate: z<boolean, boolean>;
|
|
54
|
+
localFate: z<boolean, boolean, "defined">;
|
|
55
55
|
/**
|
|
56
56
|
* Minimum turns between local-fate assessments on the turn-interval path
|
|
57
57
|
* (compaction is unconditional). Absent → follows reviewIntervalTurns.
|
|
58
58
|
*/
|
|
59
|
-
fateIntervalTurns: z<number, number>;
|
|
59
|
+
fateIntervalTurns: z<number, number, "plain">;
|
|
60
60
|
/**
|
|
61
61
|
* Goal-blocked trigger (D3): after this many CONSECUTIVE gate runs that
|
|
62
62
|
* observe the session goal in phase "blocked", run one local-fate
|
|
63
63
|
* assessment so the encounter is distilled. 0 disables.
|
|
64
64
|
*/
|
|
65
|
-
goalBlockedWrapupTurns: z<number, number>;
|
|
65
|
+
goalBlockedWrapupTurns: z<number, number, "defined">;
|
|
66
66
|
/**
|
|
67
67
|
* Promotion policy (2026-08-22): regex sources whose match in a
|
|
68
68
|
* candidate's title/content marks it project-scoped — such entries are
|
|
69
69
|
* never promoted to the cross-session global store. Replaces the built-in
|
|
70
70
|
* defaults when set.
|
|
71
71
|
*/
|
|
72
|
-
promotionBlockPatterns: z<string[], string[]>;
|
|
72
|
+
promotionBlockPatterns: z<string[], string[], "plain">;
|
|
73
73
|
/** Whole promotions below this content length stay local (chars). */
|
|
74
|
-
promotionMinChars: z<number, number>;
|
|
74
|
+
promotionMinChars: z<number, number, "defined">;
|
|
75
75
|
/** Entry-directory lines injected per build before folding into a counter. */
|
|
76
|
-
injectionDirectoryLines: z<number, number>;
|
|
77
|
-
}
|
|
76
|
+
injectionDirectoryLines: z<number, number, "defined">;
|
|
77
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
78
78
|
/** Root for evolution stores; defaults to the resolved DSH home. */
|
|
79
|
-
baseDir: z<string, string>;
|
|
79
|
+
baseDir: z<string, string, "plain">;
|
|
80
80
|
/** System-prompt section order for the evolution guidance. */
|
|
81
|
-
sectionOrder: z<number, number>;
|
|
81
|
+
sectionOrder: z<number, number, "defined">;
|
|
82
82
|
/** Enable the automatic review gate (off by default: it costs model calls). */
|
|
83
|
-
autoReview: z<boolean, boolean>;
|
|
83
|
+
autoReview: z<boolean, boolean, "defined">;
|
|
84
84
|
/** Gate runs when this many turns have passed since the last review. */
|
|
85
|
-
reviewIntervalTurns: z<number, number>;
|
|
85
|
+
reviewIntervalTurns: z<number, number, "defined">;
|
|
86
86
|
/** Trajectory slice handed to the gate, in characters. */
|
|
87
|
-
maxReviewInputChars: z<number, number>;
|
|
87
|
+
maxReviewInputChars: z<number, number, "defined">;
|
|
88
88
|
/** Output budget for the cheap gate call. */
|
|
89
|
-
reviewBudgetTokens: z<number, number>;
|
|
89
|
+
reviewBudgetTokens: z<number, number, "defined">;
|
|
90
90
|
/** After an approved gate run with applied edits, queue a visible follow-up notice. */
|
|
91
|
-
notifyOnAutoReview: z<boolean, boolean>;
|
|
91
|
+
notifyOnAutoReview: z<boolean, boolean, "defined">;
|
|
92
92
|
/** Cross-session (global) edits require an explicit human approval. */
|
|
93
|
-
requireGlobalApproval: z<boolean, boolean>;
|
|
93
|
+
requireGlobalApproval: z<boolean, boolean, "defined">;
|
|
94
94
|
/** Skills root for materialized skill entries; defaults to <dshHome>/skills. */
|
|
95
|
-
skillsDir: z<string, string>;
|
|
95
|
+
skillsDir: z<string, string, "plain">;
|
|
96
96
|
/** Passphrase for rubric encryption; falls back to DSH_EVOLVE_RUBRIC_KEY, then a local key file. */
|
|
97
|
-
rubricKey: z<string, string>;
|
|
97
|
+
rubricKey: z<string, string, "plain">;
|
|
98
98
|
/** Write all cordis log messages to <baseDir>/evolve/plugin.log (JSONL). */
|
|
99
|
-
logToFile: z<boolean, boolean>;
|
|
99
|
+
logToFile: z<boolean, boolean, "defined">;
|
|
100
100
|
/** File log level: 0=error, 1=info, 2=warn, 3=debug. */
|
|
101
|
-
logLevel: z<number, number>;
|
|
101
|
+
logLevel: z<number, number, "defined">;
|
|
102
102
|
/** Rotate the file log when it exceeds this many bytes. */
|
|
103
|
-
logMaxBytes: z<number, number>;
|
|
103
|
+
logMaxBytes: z<number, number, "defined">;
|
|
104
104
|
/** After a benchmark decision rejects a candidate, roll the refinement back automatically. */
|
|
105
|
-
autoRollbackOnReject: z<boolean, boolean>;
|
|
105
|
+
autoRollbackOnReject: z<boolean, boolean, "defined">;
|
|
106
106
|
/**
|
|
107
107
|
* P1 auto-case capture: failed evolution attempts (benchmark-rejected
|
|
108
108
|
* candidates, gate proposals without consent) land as draft cases in the
|
|
109
109
|
* auto-regression container benchmark, seeding the regression loop.
|
|
110
110
|
*/
|
|
111
|
-
autoCase: z<boolean, boolean>;
|
|
111
|
+
autoCase: z<boolean, boolean, "defined">;
|
|
112
112
|
/**
|
|
113
113
|
* Gap C1: optional model override for the review gate (cheaper model).
|
|
114
114
|
* Format: "provider/model" or just "model" (same provider as the agent).
|
|
115
115
|
* When absent, the review gate uses the agent's own provider/model.
|
|
116
116
|
*/
|
|
117
|
-
reviewModel: z<string, string>;
|
|
117
|
+
reviewModel: z<string, string, "plain">;
|
|
118
118
|
/**
|
|
119
119
|
* Gate local-fate dimension (#11 P2): the gate audits the session's local
|
|
120
120
|
* entries on its own cadence and proposes promote/archive — consulted
|
|
121
121
|
* first, never written silently. Only meaningful with autoReview on.
|
|
122
122
|
*/
|
|
123
|
-
localFate: z<boolean, boolean>;
|
|
123
|
+
localFate: z<boolean, boolean, "defined">;
|
|
124
124
|
/**
|
|
125
125
|
* Minimum turns between local-fate assessments on the turn-interval path
|
|
126
126
|
* (compaction is unconditional). Absent → follows reviewIntervalTurns.
|
|
127
127
|
*/
|
|
128
|
-
fateIntervalTurns: z<number, number>;
|
|
128
|
+
fateIntervalTurns: z<number, number, "plain">;
|
|
129
129
|
/**
|
|
130
130
|
* Goal-blocked trigger (D3): after this many CONSECUTIVE gate runs that
|
|
131
131
|
* observe the session goal in phase "blocked", run one local-fate
|
|
132
132
|
* assessment so the encounter is distilled. 0 disables.
|
|
133
133
|
*/
|
|
134
|
-
goalBlockedWrapupTurns: z<number, number>;
|
|
134
|
+
goalBlockedWrapupTurns: z<number, number, "defined">;
|
|
135
135
|
/**
|
|
136
136
|
* Promotion policy (2026-08-22): regex sources whose match in a
|
|
137
137
|
* candidate's title/content marks it project-scoped — such entries are
|
|
138
138
|
* never promoted to the cross-session global store. Replaces the built-in
|
|
139
139
|
* defaults when set.
|
|
140
140
|
*/
|
|
141
|
-
promotionBlockPatterns: z<string[], string[]>;
|
|
141
|
+
promotionBlockPatterns: z<string[], string[], "plain">;
|
|
142
142
|
/** Whole promotions below this content length stay local (chars). */
|
|
143
|
-
promotionMinChars: z<number, number>;
|
|
143
|
+
promotionMinChars: z<number, number, "defined">;
|
|
144
144
|
/** Entry-directory lines injected per build before folding into a counter. */
|
|
145
|
-
injectionDirectoryLines: z<number, number>;
|
|
146
|
-
}
|
|
145
|
+
injectionDirectoryLines: z<number, number, "defined">;
|
|
146
|
+
}>>, "plain">;
|
|
147
147
|
/**
|
|
148
148
|
* Structurally typed resolved config (loader passes the validated object).
|
|
149
149
|
* Derived from the schemastery schema — single source of truth, no manual sync.
|
package/lib/index.js
CHANGED
|
@@ -116,7 +116,7 @@ export function apply(ctx, config) {
|
|
|
116
116
|
ctx.systemPrompt.section({
|
|
117
117
|
name: "tool:continual-evolve",
|
|
118
118
|
order: config.sectionOrder ?? 118,
|
|
119
|
-
text: "You have a continual harness: versioned, persistent prompt notes, memories, skills, and subagent specs. Prompt notes and delegation specs are injected below; use evolve_list for the full state. Create an entry (evolve_add) after a repeated failure, a reusable tactic, a durable fact or preference, a repeated procedure, or a repeated delegation role. Keep edits small and evidence-backed; prefer local scope, use global: true only for stable cross-
|
|
119
|
+
text: "You have a continual harness: versioned, persistent prompt notes, memories, skills, and subagent specs. Memories are ONE FACT per entry, typed user/feedback/project/reference (feedback = pitfall + Why + How to apply). Stores: local (this session's staging), project (this project's cross-session store), global (cross-project). Prompt notes and delegation specs are injected below; use evolve_list for the full state. Create an entry (evolve_add) after a repeated failure, a reusable tactic, a durable fact or preference, a repeated procedure, or a repeated delegation role. Keep edits small and evidence-backed; prefer local scope, use project for repo-specific lessons, global: true only for stable cross-project lessons. Update or delete (evolve_update / evolve_delete) when an entry is wrong or obsolete; roll back faulty refinements with evolve_rollback. Every edit is snapshotted, versioned, and recorded — no edit can be silently lost.",
|
|
120
120
|
});
|
|
121
121
|
// Phase 2: make prompt entries real system-prompt content and subagent
|
|
122
122
|
// entries real delegation specs. The text is a provider evaluated at every
|