@lmzhen/dsh-evolution-curator 0.1.0-rc.12 → 0.1.0-rc.14
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/lib/index.js +59 -13
- package/lib/types/index.d.ts +24 -7
- package/package.json +7 -7
package/lib/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
5
5
|
import z from "@deepseek-ai/schemastery";
|
|
6
|
-
import { CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_STALE_AFTER_DAYS, SkillLibrary, buildCuratorRunReport, computeLifecycleTransitions, evolutionHome, evolutionIoAdapter, loadUsage, saveUsage } from "@lmzhen/dsh-evolution-core";
|
|
6
|
+
import { CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MIN_IDLE_HOURS, DEFAULT_STALE_AFTER_DAYS, SkillLibrary, buildCuratorRunReport, computeLifecycleTransitions, emptyRecord, evolutionHome, evolutionIoAdapter, loadSuppressedNames, loadUsage, saveSuppressedNames, saveUsage } from "@lmzhen/dsh-evolution-core";
|
|
7
7
|
//#region lib/types/index.js
|
|
8
8
|
/**
|
|
9
9
|
* Deterministic skill lifecycle curator with interval gate and archive.
|
|
@@ -19,9 +19,10 @@ var EvolutionCurator = class extends Service {
|
|
|
19
19
|
llmReview: z.boolean().default(false),
|
|
20
20
|
curatorProvider: z.string().default("deepseek-official"),
|
|
21
21
|
qualityWarnStaleAfterDays: z.number().default(7),
|
|
22
|
-
minIdleHours: z.number().default(
|
|
22
|
+
minIdleHours: z.number().default(DEFAULT_MIN_IDLE_HOURS),
|
|
23
23
|
excludeSkillNames: z.array(z.string()).default([]),
|
|
24
24
|
manageUnmanaged: z.boolean().default(false),
|
|
25
|
+
pruneBuiltins: z.boolean().default(false),
|
|
25
26
|
curatorReviewMaxTokens: z.number().default(2048)
|
|
26
27
|
});
|
|
27
28
|
skills;
|
|
@@ -36,6 +37,7 @@ var EvolutionCurator = class extends Service {
|
|
|
36
37
|
minIdleHours;
|
|
37
38
|
excludeSkillNames;
|
|
38
39
|
manageUnmanaged;
|
|
40
|
+
pruneBuiltins;
|
|
39
41
|
curatorReviewMaxTokens;
|
|
40
42
|
lastRun = 0;
|
|
41
43
|
timer;
|
|
@@ -50,9 +52,10 @@ var EvolutionCurator = class extends Service {
|
|
|
50
52
|
this.llmReview = config.llmReview ?? false;
|
|
51
53
|
this.curatorProvider = config.curatorProvider ?? "deepseek-official";
|
|
52
54
|
this.qualityWarnStaleAfterDays = config.qualityWarnStaleAfterDays ?? 7;
|
|
53
|
-
this.minIdleHours = config.minIdleHours ??
|
|
55
|
+
this.minIdleHours = config.minIdleHours ?? DEFAULT_MIN_IDLE_HOURS;
|
|
54
56
|
this.excludeSkillNames = new Set(config.excludeSkillNames ?? []);
|
|
55
57
|
this.manageUnmanaged = config.manageUnmanaged ?? false;
|
|
58
|
+
this.pruneBuiltins = config.pruneBuiltins ?? false;
|
|
56
59
|
this.curatorReviewMaxTokens = config.curatorReviewMaxTokens ?? 2048;
|
|
57
60
|
this.lastRun = Date.now();
|
|
58
61
|
this.ctx.effect(() => {
|
|
@@ -140,20 +143,24 @@ var EvolutionCurator = class extends Service {
|
|
|
140
143
|
failed: []
|
|
141
144
|
});
|
|
142
145
|
}
|
|
143
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Run one curator pass. `ignoreGates` skips the interval and idle gates so an
|
|
148
|
+
* explicit `/evolution curator run` always executes (manual-run semantics).
|
|
149
|
+
*/
|
|
150
|
+
async run(options = {}) {
|
|
144
151
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
145
152
|
const runId = randomUUID();
|
|
146
153
|
const stateService = this.ctx.get("evolutionState");
|
|
147
154
|
const lifecycle = this.lifecycle();
|
|
148
155
|
const persisted = await stateService?.loadCuratorState();
|
|
149
|
-
if (persisted && Date.now() - persisted.lastRunAt < lifecycle.intervalHours * 36e5) return {
|
|
156
|
+
if (!options.ignoreGates && persisted && Date.now() - persisted.lastRunAt < lifecycle.intervalHours * 36e5) return {
|
|
150
157
|
stale: [],
|
|
151
158
|
archived: [],
|
|
152
159
|
errors: [],
|
|
153
160
|
report: this.skippedReport(runId, startedAt),
|
|
154
161
|
skipped: "interval"
|
|
155
162
|
};
|
|
156
|
-
if (this.minIdleHours > 0 && this.recentSessionActive()) return {
|
|
163
|
+
if (!options.ignoreGates && this.minIdleHours > 0 && this.recentSessionActive()) return {
|
|
157
164
|
stale: [],
|
|
158
165
|
archived: [],
|
|
159
166
|
errors: [],
|
|
@@ -163,28 +170,48 @@ var EvolutionCurator = class extends Service {
|
|
|
163
170
|
const root = this.skills.root;
|
|
164
171
|
const snapshotPath = await this.skills.snapshotAll("pre-curator-run");
|
|
165
172
|
const usage = await loadUsage(root, this.io);
|
|
173
|
+
const suppressedNames = new Set(await loadSuppressedNames(root, this.io));
|
|
174
|
+
const bundledNames = await this.seedBaseline(usage);
|
|
166
175
|
const result = computeLifecycleTransitions(usage, {
|
|
167
176
|
staleAfterDays: lifecycle.staleAfterDays,
|
|
168
177
|
archiveAfterDays: lifecycle.archiveAfterDays,
|
|
169
178
|
qualityWarnStaleAfterDays: this.qualityWarnStaleAfterDays,
|
|
170
179
|
excludeSkillNames: this.excludeSkillNames,
|
|
171
|
-
manageUnmanaged: this.manageUnmanaged
|
|
180
|
+
manageUnmanaged: this.manageUnmanaged,
|
|
181
|
+
pruneBuiltins: this.pruneBuiltins,
|
|
182
|
+
bundledNames,
|
|
183
|
+
suppressedNames
|
|
172
184
|
});
|
|
173
185
|
const errors = [];
|
|
174
186
|
const archivedSkills = [];
|
|
175
187
|
const llmNominations = this.llmReview ? await this.recommend(result.markStale) : [];
|
|
176
188
|
const archiveCandidates = [...new Set([...result.archive, ...llmNominations])];
|
|
189
|
+
let suppressedChanged = false;
|
|
177
190
|
for (const name of archiveCandidates) {
|
|
178
|
-
const archived = await this.skills.archive(name,
|
|
191
|
+
const archived = await this.skills.archive(name, {
|
|
192
|
+
reason: "Lifecycle: reached archive threshold",
|
|
193
|
+
allowBundled: this.pruneBuiltins
|
|
194
|
+
});
|
|
179
195
|
if (!archived.ok) {
|
|
180
196
|
const record = usage.get(name);
|
|
181
197
|
if (record) record.state = "active";
|
|
182
198
|
errors.push(`${name}: ${archived.message}`);
|
|
183
|
-
} else
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
199
|
+
} else {
|
|
200
|
+
archivedSkills.push({
|
|
201
|
+
name,
|
|
202
|
+
path: archived.path ?? "",
|
|
203
|
+
reason: "Lifecycle: reached archive threshold"
|
|
204
|
+
});
|
|
205
|
+
if (bundledNames.has(name)) {
|
|
206
|
+
suppressedNames.add(name);
|
|
207
|
+
suppressedChanged = true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (suppressedChanged) try {
|
|
212
|
+
await saveSuppressedNames(root, suppressedNames, this.io);
|
|
213
|
+
} catch {
|
|
214
|
+
this.ctx.logger.warn("evolution-curator: failed to persist suppressed names; archived built-ins may re-enter the lifecycle");
|
|
188
215
|
}
|
|
189
216
|
await saveUsage(root, usage, this.io);
|
|
190
217
|
this.lastRun = Date.now();
|
|
@@ -224,6 +251,19 @@ var EvolutionCurator = class extends Service {
|
|
|
224
251
|
report
|
|
225
252
|
};
|
|
226
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Seed baseline records for tree skills the sidecar has not seen yet, so
|
|
256
|
+
* their inactivity clock starts now (first-sight defer) and bundled skills
|
|
257
|
+
* become known candidates only when prune-builtins opts them in.
|
|
258
|
+
*/
|
|
259
|
+
async seedBaseline(usage) {
|
|
260
|
+
const bundledNames = /* @__PURE__ */ new Set();
|
|
261
|
+
for (const summary of await this.skills.list()) {
|
|
262
|
+
if (!usage.has(summary.name)) usage.set(summary.name, emptyRecord());
|
|
263
|
+
if (await this.skills.isBundled(summary.name)) bundledNames.add(summary.name);
|
|
264
|
+
}
|
|
265
|
+
return bundledNames;
|
|
266
|
+
}
|
|
227
267
|
recentSessionActive() {
|
|
228
268
|
const agents = this.ctx.get("agents");
|
|
229
269
|
if (!agents) return false;
|
|
@@ -281,6 +321,12 @@ var EvolutionCurator = class extends Service {
|
|
|
281
321
|
const record = usage.get(name);
|
|
282
322
|
if (record) record.state = "active";
|
|
283
323
|
await saveUsage(this.skills.root, usage, this.io);
|
|
324
|
+
const suppressed = new Set(await loadSuppressedNames(this.skills.root, this.io));
|
|
325
|
+
if (suppressed.delete(name)) try {
|
|
326
|
+
await saveSuppressedNames(this.skills.root, suppressed, this.io);
|
|
327
|
+
} catch {
|
|
328
|
+
this.ctx.logger.warn(`evolution-curator: failed to persist suppressed names after restoring ${name}`);
|
|
329
|
+
}
|
|
284
330
|
return result;
|
|
285
331
|
}
|
|
286
332
|
};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -27,9 +27,19 @@ export interface Config {
|
|
|
27
27
|
excludeSkillNames?: string[];
|
|
28
28
|
/** Include usage records whose created_by is not 'agent' in lifecycle decisions. */
|
|
29
29
|
manageUnmanaged?: boolean;
|
|
30
|
+
/** Archive long-unused bundled skills too (with suppression against re-seeds). */
|
|
31
|
+
pruneBuiltins?: boolean;
|
|
30
32
|
/** Max tokens for the optional LLM nomination pass. */
|
|
31
33
|
curatorReviewMaxTokens?: number;
|
|
32
34
|
}
|
|
35
|
+
/** Outcome of one curator run pass. */
|
|
36
|
+
export interface CuratorRunOutcome {
|
|
37
|
+
stale: string[];
|
|
38
|
+
archived: string[];
|
|
39
|
+
errors: string[];
|
|
40
|
+
report: CuratorRunReport;
|
|
41
|
+
skipped?: string;
|
|
42
|
+
}
|
|
33
43
|
export declare class EvolutionCurator extends Service {
|
|
34
44
|
static inject: string[];
|
|
35
45
|
static Config: Schema<Config>;
|
|
@@ -45,6 +55,7 @@ export declare class EvolutionCurator extends Service {
|
|
|
45
55
|
private readonly minIdleHours;
|
|
46
56
|
private readonly excludeSkillNames;
|
|
47
57
|
private readonly manageUnmanaged;
|
|
58
|
+
private readonly pruneBuiltins;
|
|
48
59
|
private readonly curatorReviewMaxTokens;
|
|
49
60
|
private lastRun;
|
|
50
61
|
private timer;
|
|
@@ -60,13 +71,19 @@ export declare class EvolutionCurator extends Service {
|
|
|
60
71
|
*/
|
|
61
72
|
recommend(candidates: string[]): Promise<string[]>;
|
|
62
73
|
private skippedReport;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Run one curator pass. `ignoreGates` skips the interval and idle gates so an
|
|
76
|
+
* explicit `/evolution curator run` always executes (manual-run semantics).
|
|
77
|
+
*/
|
|
78
|
+
run(options?: {
|
|
79
|
+
ignoreGates?: boolean;
|
|
80
|
+
}): Promise<CuratorRunOutcome>;
|
|
81
|
+
/**
|
|
82
|
+
* Seed baseline records for tree skills the sidecar has not seen yet, so
|
|
83
|
+
* their inactivity clock starts now (first-sight defer) and bundled skills
|
|
84
|
+
* become known candidates only when prune-builtins opts them in.
|
|
85
|
+
*/
|
|
86
|
+
private seedBaseline;
|
|
70
87
|
private recentSessionActive;
|
|
71
88
|
latestReport(): Promise<CuratorRunReport | null>;
|
|
72
89
|
/**
|
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.14",
|
|
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.14"
|
|
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.14",
|
|
43
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.14"
|
|
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.14",
|
|
49
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.14",
|
|
50
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.14"
|
|
51
51
|
}
|
|
52
52
|
}
|