@lmzhen/dsh-evolution-curator 0.1.0-rc.37 → 0.1.0-rc.38
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 +2 -1
- package/lib/index.js +63 -4
- package/lib/types/index.d.ts +25 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -28,4 +28,5 @@ Independent of request-prefix construction. This package does not alter the asse
|
|
|
28
28
|
|
|
29
29
|
- `archive` never deletes: skills move to `.archive/` with a `.archive-reason` marker.
|
|
30
30
|
- `restore(name)` (service) / `/evolution skill restore <name>` brings one archived skill back to the active root and resets its usage state.
|
|
31
|
-
- `consolidate(target, sources)` (service) / `/evolution consolidate` merges source bodies into the target, archives the sources with an absorbed-into marker, and folds their usage records into `archived` state. Both operations snapshot the
|
|
31
|
+
- `consolidate(target, sources)` (service) / `/evolution consolidate` merges source bodies into the target, archives the sources with an absorbed-into marker, and folds their usage records into `archived` state. Both operations snapshot the full state first (`pre-consolidate` / `pre-restore`).
|
|
32
|
+
- `restoreSnapshot` (service) / `/evolution restore` rolls the FULL state back to the latest snapshot: active tree, usage/suppression sidecars, `.archive/` and the curator state carried in the snapshot (`curator-state.json`), so the interval gate does not immediately re-fire after a rollback. The restore itself is undoable — the pre-rollback safety snapshot preserves the current tree plus its state.
|
package/lib/index.js
CHANGED
|
@@ -54,6 +54,7 @@ var EvolutionCurator = class extends Service {
|
|
|
54
54
|
curatorReviewMaxTokens;
|
|
55
55
|
lastRun = 0;
|
|
56
56
|
timer;
|
|
57
|
+
running = false;
|
|
57
58
|
constructor(ctx, config = {}) {
|
|
58
59
|
super(ctx, "evolutionCurator");
|
|
59
60
|
this.io = evolutionIoAdapter(() => ctx.evolutionIo.provider());
|
|
@@ -151,6 +152,47 @@ var EvolutionCurator = class extends Service {
|
|
|
151
152
|
return empty;
|
|
152
153
|
}
|
|
153
154
|
}
|
|
155
|
+
/** Optional curator-state service (evolution-state-json / storage-domain). */
|
|
156
|
+
curatorStateService() {
|
|
157
|
+
return this.ctx.get("evolutionState");
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Full-state snapshot: the skills tree plus the current curator state as an
|
|
161
|
+
* `extras/curator-state.json` side file. Every pre-mutation snapshot in the
|
|
162
|
+
* curator goes through here so a later `restoreSnapshot()` can rewind both
|
|
163
|
+
* the tree and the state (Hermes curator_backup backs up `.curator_state`).
|
|
164
|
+
*/
|
|
165
|
+
async snapshotFull(reason = "pre-mutation") {
|
|
166
|
+
const state = await this.curatorStateService()?.loadCuratorState();
|
|
167
|
+
const extras = state === null || state === void 0 ? [] : [{
|
|
168
|
+
name: "curator-state.json",
|
|
169
|
+
content: JSON.stringify(state, null, 2)
|
|
170
|
+
}];
|
|
171
|
+
return await this.skills.snapshotAll(reason, extras);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Full-state rollback: restore the latest snapshot's tree/sidecars/archive
|
|
175
|
+
* AND the curator state it carried. The pre-rollback safety snapshot keeps
|
|
176
|
+
* the current tree plus current state (as extras), so the rollback itself
|
|
177
|
+
* is reversible.
|
|
178
|
+
*/
|
|
179
|
+
async restoreSnapshot() {
|
|
180
|
+
const stateService = this.curatorStateService();
|
|
181
|
+
const currentState = await stateService?.loadCuratorState();
|
|
182
|
+
const extras = currentState === null || currentState === void 0 ? [] : [{
|
|
183
|
+
name: "curator-state.json",
|
|
184
|
+
content: JSON.stringify(currentState, null, 2)
|
|
185
|
+
}];
|
|
186
|
+
const result = await this.skills.restoreLatestSnapshot(extras);
|
|
187
|
+
if (!result.ok) return result;
|
|
188
|
+
const stateExtra = result.extras?.find((extra) => extra.name === "curator-state.json");
|
|
189
|
+
if (stateExtra && stateService) try {
|
|
190
|
+
await stateService.saveCuratorState(JSON.parse(stateExtra.content));
|
|
191
|
+
} catch (error) {
|
|
192
|
+
this.ctx.logger.warn(`evolution-curator: failed to restore curator state: ${error instanceof Error ? error.message : String(error)}`);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
154
196
|
skippedReport(runId, startedAt) {
|
|
155
197
|
return buildCuratorRunReport({
|
|
156
198
|
runId,
|
|
@@ -169,12 +211,29 @@ var EvolutionCurator = class extends Service {
|
|
|
169
211
|
* explicit `/evolution curator run` always executes (manual-run semantics):
|
|
170
212
|
* `dryRun` computes the lifecycle and the LLM nominations but performs no
|
|
171
213
|
* mutation, reports what WOULD happen, and does not push out the next run.
|
|
214
|
+
* Reentrant calls (autoStart timer + manual command at the same instant) are
|
|
215
|
+
* skipped with an explicit `already-running` outcome.
|
|
172
216
|
*/
|
|
173
217
|
async run(options = {}) {
|
|
218
|
+
if (this.running) return {
|
|
219
|
+
stale: [],
|
|
220
|
+
archived: [],
|
|
221
|
+
errors: [],
|
|
222
|
+
report: this.skippedReport("already-running", (/* @__PURE__ */ new Date()).toISOString()),
|
|
223
|
+
skipped: "already-running"
|
|
224
|
+
};
|
|
225
|
+
this.running = true;
|
|
226
|
+
try {
|
|
227
|
+
return await this.runCore(options);
|
|
228
|
+
} finally {
|
|
229
|
+
this.running = false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async runCore(options = {}) {
|
|
174
233
|
const { ignoreGates = false, dryRun = false } = options;
|
|
175
234
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
176
235
|
const runId = randomUUID();
|
|
177
|
-
const stateService = this.
|
|
236
|
+
const stateService = this.curatorStateService();
|
|
178
237
|
const lifecycle = this.lifecycle();
|
|
179
238
|
const persisted = await stateService?.loadCuratorState();
|
|
180
239
|
if (!ignoreGates && persisted && Date.now() - persisted.lastRunAt < lifecycle.intervalHours * 36e5) return {
|
|
@@ -210,7 +269,7 @@ var EvolutionCurator = class extends Service {
|
|
|
210
269
|
const root = this.skills.root;
|
|
211
270
|
const rawUsage = await loadUsage(root, this.io);
|
|
212
271
|
const usage = dryRun ? new Map([...rawUsage].map(([name, record]) => [name, { ...record }])) : rawUsage;
|
|
213
|
-
const snapshotPath = dryRun ? void 0 : await this.
|
|
272
|
+
const snapshotPath = dryRun ? void 0 : await this.snapshotFull("pre-curator-run");
|
|
214
273
|
const suppressedNames = new Set(await loadSuppressedNames(root, this.io));
|
|
215
274
|
const { bundledNames, treeNames } = await this.seedBaseline(usage);
|
|
216
275
|
const result = computeLifecycleTransitions(usage, {
|
|
@@ -501,7 +560,7 @@ var EvolutionCurator = class extends Service {
|
|
|
501
560
|
ok: false,
|
|
502
561
|
message: `Skill(s) excluded from lifecycle management: ${blocked.join(", ")}`
|
|
503
562
|
};
|
|
504
|
-
await this.
|
|
563
|
+
await this.snapshotFull("pre-consolidate");
|
|
505
564
|
const result = await this.skills.consolidate(target, sources);
|
|
506
565
|
if (!result.ok) return result;
|
|
507
566
|
const usage = await loadUsage(this.skills.root, this.io);
|
|
@@ -518,7 +577,7 @@ var EvolutionCurator = class extends Service {
|
|
|
518
577
|
* and reset its usage state, keeping the recoverable-archive invariant.
|
|
519
578
|
*/
|
|
520
579
|
async restore(name) {
|
|
521
|
-
await this.
|
|
580
|
+
await this.snapshotFull("pre-restore");
|
|
522
581
|
const result = await this.skills.restoreFromArchive(name);
|
|
523
582
|
if (!result.ok) return result;
|
|
524
583
|
const usage = await loadUsage(this.skills.root, this.io);
|
package/lib/types/index.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ export declare class EvolutionCurator extends Service {
|
|
|
85
85
|
private readonly curatorReviewMaxTokens;
|
|
86
86
|
private lastRun;
|
|
87
87
|
private timer;
|
|
88
|
+
private running;
|
|
88
89
|
constructor(ctx: Context, config?: Config);
|
|
89
90
|
private lifecycle;
|
|
90
91
|
start(): void;
|
|
@@ -98,17 +99,41 @@ export declare class EvolutionCurator extends Service {
|
|
|
98
99
|
recommend(candidates: string[], options?: {
|
|
99
100
|
dryRun?: boolean;
|
|
100
101
|
}): Promise<CuratorNominations>;
|
|
102
|
+
/** Optional curator-state service (evolution-state-json / storage-domain). */
|
|
103
|
+
private curatorStateService;
|
|
104
|
+
/**
|
|
105
|
+
* Full-state snapshot: the skills tree plus the current curator state as an
|
|
106
|
+
* `extras/curator-state.json` side file. Every pre-mutation snapshot in the
|
|
107
|
+
* curator goes through here so a later `restoreSnapshot()` can rewind both
|
|
108
|
+
* the tree and the state (Hermes curator_backup backs up `.curator_state`).
|
|
109
|
+
*/
|
|
110
|
+
snapshotFull(reason?: string): Promise<string>;
|
|
111
|
+
/**
|
|
112
|
+
* Full-state rollback: restore the latest snapshot's tree/sidecars/archive
|
|
113
|
+
* AND the curator state it carried. The pre-rollback safety snapshot keeps
|
|
114
|
+
* the current tree plus current state (as extras), so the rollback itself
|
|
115
|
+
* is reversible.
|
|
116
|
+
*/
|
|
117
|
+
restoreSnapshot(): Promise<SkillActionResult & {
|
|
118
|
+
extras?: Array<{
|
|
119
|
+
name: string;
|
|
120
|
+
content: string;
|
|
121
|
+
}>;
|
|
122
|
+
}>;
|
|
101
123
|
private skippedReport;
|
|
102
124
|
/**
|
|
103
125
|
* Run one curator pass. `ignoreGates` skips the interval and idle gates so an
|
|
104
126
|
* explicit `/evolution curator run` always executes (manual-run semantics):
|
|
105
127
|
* `dryRun` computes the lifecycle and the LLM nominations but performs no
|
|
106
128
|
* mutation, reports what WOULD happen, and does not push out the next run.
|
|
129
|
+
* Reentrant calls (autoStart timer + manual command at the same instant) are
|
|
130
|
+
* skipped with an explicit `already-running` outcome.
|
|
107
131
|
*/
|
|
108
132
|
run(options?: {
|
|
109
133
|
ignoreGates?: boolean;
|
|
110
134
|
dryRun?: boolean;
|
|
111
135
|
}): Promise<CuratorRunOutcome>;
|
|
136
|
+
private runCore;
|
|
112
137
|
/**
|
|
113
138
|
* Seed baseline records for tree skills the sidecar has not seen yet, so
|
|
114
139
|
* their inactivity clock starts now (first-sight defer) and bundled skills
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-curator",
|
|
3
3
|
"description": "Deterministic skill lifecycle and recovery (community build)",
|
|
4
|
-
"version": "0.1.0-rc.
|
|
4
|
+
"version": "0.1.0-rc.38",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -33,20 +33,20 @@
|
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
36
|
-
"@lmzhen/dsh-evolution-core": "^0.1.0-rc.
|
|
36
|
+
"@lmzhen/dsh-evolution-core": "^0.1.0-rc.38"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
40
40
|
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
41
41
|
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
42
|
-
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.
|
|
43
|
-
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.
|
|
42
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.38",
|
|
43
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.38"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
47
47
|
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
48
|
-
"@lmzhen/dsh-evolution-core": "^0.1.0-rc.
|
|
49
|
-
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.
|
|
50
|
-
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.
|
|
48
|
+
"@lmzhen/dsh-evolution-core": "^0.1.0-rc.38",
|
|
49
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.38",
|
|
50
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.38"
|
|
51
51
|
}
|
|
52
52
|
}
|