@lmzhen/dsh-evolution-curator 0.1.0-rc.12 → 0.1.0-rc.13
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 +42 -13
- package/lib/types/index.d.ts +18 -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,29 +170,49 @@ 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 = /* @__PURE__ */ new Set();
|
|
175
|
+
for (const summary of await this.skills.list()) {
|
|
176
|
+
if (!usage.has(summary.name)) usage.set(summary.name, emptyRecord());
|
|
177
|
+
if (await this.skills.isBundled(summary.name)) bundledNames.add(summary.name);
|
|
178
|
+
}
|
|
166
179
|
const result = computeLifecycleTransitions(usage, {
|
|
167
180
|
staleAfterDays: lifecycle.staleAfterDays,
|
|
168
181
|
archiveAfterDays: lifecycle.archiveAfterDays,
|
|
169
182
|
qualityWarnStaleAfterDays: this.qualityWarnStaleAfterDays,
|
|
170
183
|
excludeSkillNames: this.excludeSkillNames,
|
|
171
|
-
manageUnmanaged: this.manageUnmanaged
|
|
184
|
+
manageUnmanaged: this.manageUnmanaged,
|
|
185
|
+
pruneBuiltins: this.pruneBuiltins,
|
|
186
|
+
bundledNames,
|
|
187
|
+
suppressedNames
|
|
172
188
|
});
|
|
173
189
|
const errors = [];
|
|
174
190
|
const archivedSkills = [];
|
|
175
191
|
const llmNominations = this.llmReview ? await this.recommend(result.markStale) : [];
|
|
176
192
|
const archiveCandidates = [...new Set([...result.archive, ...llmNominations])];
|
|
193
|
+
let suppressedChanged = false;
|
|
177
194
|
for (const name of archiveCandidates) {
|
|
178
|
-
const archived = await this.skills.archive(name,
|
|
195
|
+
const archived = await this.skills.archive(name, {
|
|
196
|
+
reason: "Lifecycle: reached archive threshold",
|
|
197
|
+
allowBundled: this.pruneBuiltins
|
|
198
|
+
});
|
|
179
199
|
if (!archived.ok) {
|
|
180
200
|
const record = usage.get(name);
|
|
181
201
|
if (record) record.state = "active";
|
|
182
202
|
errors.push(`${name}: ${archived.message}`);
|
|
183
|
-
} else
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
203
|
+
} else {
|
|
204
|
+
archivedSkills.push({
|
|
205
|
+
name,
|
|
206
|
+
path: archived.path ?? "",
|
|
207
|
+
reason: "Lifecycle: reached archive threshold"
|
|
208
|
+
});
|
|
209
|
+
if (bundledNames.has(name)) {
|
|
210
|
+
suppressedNames.add(name);
|
|
211
|
+
suppressedChanged = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
188
214
|
}
|
|
215
|
+
if (suppressedChanged) await saveSuppressedNames(root, suppressedNames, this.io);
|
|
189
216
|
await saveUsage(root, usage, this.io);
|
|
190
217
|
this.lastRun = Date.now();
|
|
191
218
|
const report = buildCuratorRunReport({
|
|
@@ -281,6 +308,8 @@ var EvolutionCurator = class extends Service {
|
|
|
281
308
|
const record = usage.get(name);
|
|
282
309
|
if (record) record.state = "active";
|
|
283
310
|
await saveUsage(this.skills.root, usage, this.io);
|
|
311
|
+
const suppressed = new Set(await loadSuppressedNames(this.skills.root, this.io));
|
|
312
|
+
if (suppressed.delete(name)) await saveSuppressedNames(this.skills.root, suppressed, this.io);
|
|
284
313
|
return result;
|
|
285
314
|
}
|
|
286
315
|
};
|
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,13 @@ 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>;
|
|
70
81
|
private recentSessionActive;
|
|
71
82
|
latestReport(): Promise<CuratorRunReport | null>;
|
|
72
83
|
/**
|
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.13",
|
|
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.13"
|
|
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.13",
|
|
43
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.13"
|
|
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.13",
|
|
49
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.13",
|
|
50
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.13"
|
|
51
51
|
}
|
|
52
52
|
}
|