@lmzhen/dsh-evolution-curator 0.1.0-rc.27 → 0.1.0-rc.29

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 CHANGED
@@ -9,6 +9,16 @@ import { CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEF
9
9
  * Deterministic skill lifecycle curator with interval gate and archive.
10
10
  * @module @lmzhen/dsh-evolution-curator
11
11
  */
12
+ /**
13
+ * Block LLM-nominated consolidations that would touch a gate-protected name:
14
+ * exclude / referenced / suppressed skills must never merge (neither as the
15
+ * source being archived nor as the umbrella being edited). Mirrors the control
16
+ * plane's `consolidate()` guard; automatic nominations must pass the same gate.
17
+ */
18
+ function gateConsolidations(consolidations, gates) {
19
+ const blocked = (name) => gates.exclude?.has(name) === true || gates.referenced?.has(name) === true || gates.suppressed?.has(name) === true;
20
+ return consolidations.filter((n) => !blocked(n.from) && !blocked(n.into));
21
+ }
12
22
  var EvolutionCurator = class extends Service {
13
23
  static inject = ["evolutionIo"];
14
24
  static Config = z.object({
@@ -219,17 +229,26 @@ var EvolutionCurator = class extends Service {
219
229
  prunings: [],
220
230
  consolidations: []
221
231
  };
222
- const llmNominations = nominations.prunings;
232
+ const gatedNominations = {
233
+ ...nominations,
234
+ consolidations: gateConsolidations(nominations.consolidations, {
235
+ exclude: this.excludeSkillNames,
236
+ referenced: this.referencedSkillNames,
237
+ suppressed: suppressedNames
238
+ })
239
+ };
240
+ const llmNominations = gatedNominations.prunings;
223
241
  const archiveCandidates = [...new Set([...result.archive, ...llmNominations])];
224
242
  const { archivedSkills, errors } = await this.applyMutations({
225
243
  dryRun,
226
244
  archiveCandidates,
227
- nominations,
245
+ nominations: gatedNominations,
228
246
  treeNames,
229
247
  usage,
230
248
  bundledNames,
231
249
  suppressedNames,
232
- root
250
+ root,
251
+ failedFrom: new Map(result.transitions.filter((t) => t.to === "archived").map((t) => [t.name, t.from]))
233
252
  });
234
253
  if (!dryRun) this.lastRun = Date.now();
235
254
  const report = buildCuratorRunReport({
@@ -240,7 +259,7 @@ var EvolutionCurator = class extends Service {
240
259
  llmNominations,
241
260
  archiveCandidates,
242
261
  archived: archivedSkills,
243
- failed: [...new Set([...archiveCandidates, ...nominations.consolidations.map((item) => item.from)])].filter((name) => errors.some((error) => error.startsWith(`${name}:`))).map((name) => {
262
+ failed: [...new Set([...archiveCandidates, ...gatedNominations.consolidations.map((item) => item.from)])].filter((name) => errors.some((error) => error.startsWith(`${name}:`))).map((name) => {
244
263
  return {
245
264
  name,
246
265
  reason: errors.find((item) => item.startsWith(`${name}:`))?.slice(name.length + 2) ?? "unknown"
@@ -257,7 +276,7 @@ var EvolutionCurator = class extends Service {
257
276
  this.ctx.logger.warn(error);
258
277
  }
259
278
  const llmHint = !this.llmReview && result.markStale.length > 0 ? " (llmReview: off - deterministic archive only; set llmReview: true for the LLM merge channel)" : "";
260
- const summary = `${dryRun ? "dry-run" : "auto"}: stale:${result.markStale.length} archived:${archivedSkills.length} consolidated:${nominations.consolidations.length}${llmHint}`;
279
+ const summary = `${dryRun ? "dry-run" : "auto"}: stale:${result.markStale.length} archived:${archivedSkills.length} consolidated:${gatedNominations.consolidations.length}${llmHint}`;
261
280
  await stateService?.saveCuratorState({
262
281
  schemaVersion: 1,
263
282
  lastRunAt: dryRun ? persisted?.lastRunAt ?? this.lastRun : this.lastRun,
@@ -270,7 +289,7 @@ var EvolutionCurator = class extends Service {
270
289
  archived: archivedSkills.map((item) => item.name),
271
290
  errors,
272
291
  report,
273
- ...this.llmReview ? { nominations } : {}
292
+ ...this.llmReview ? { nominations: gatedNominations } : {}
274
293
  };
275
294
  }
276
295
  /**
@@ -346,7 +365,7 @@ var EvolutionCurator = class extends Service {
346
365
  errors: [],
347
366
  suppressedChanged: false
348
367
  };
349
- const { archiveCandidates, nominations, treeNames, usage, bundledNames, suppressedNames, root } = input;
368
+ const { archiveCandidates, nominations, treeNames, usage, bundledNames, suppressedNames, root, failedFrom } = input;
350
369
  const errors = [];
351
370
  const archivedSkills = [];
352
371
  let suppressedChanged = false;
@@ -357,7 +376,8 @@ var EvolutionCurator = class extends Service {
357
376
  });
358
377
  if (!archived.ok) {
359
378
  const record = usage.get(name);
360
- if (record) record.state = "active";
379
+ const from = failedFrom?.get(name);
380
+ if (record && (from === "stale" || from === "active")) record.state = from;
361
381
  errors.push(`${name}: ${archived.message}`);
362
382
  } else {
363
383
  archivedSkills.push({
@@ -391,7 +411,7 @@ var EvolutionCurator = class extends Service {
391
411
  alreadyArchived.add(nomination.from);
392
412
  archivedSkills.push({
393
413
  name: nomination.from,
394
- path: consolidated.path ?? "",
414
+ path: join(this.skills.root, ".archive", nomination.from),
395
415
  reason: `Consolidated into ${nomination.into}`
396
416
  });
397
417
  }
@@ -453,7 +473,13 @@ var EvolutionCurator = class extends Service {
453
473
  manageUnmanaged: this.manageUnmanaged,
454
474
  pruneBuiltins: this.pruneBuiltins,
455
475
  bundledNames
456
- });
476
+ }, await this.protectedNameMap());
477
+ }
478
+ /** marker info (pinned/bundled/hub-installed) per skill, from the library list. */
479
+ async protectedNameMap() {
480
+ const map = /* @__PURE__ */ new Map();
481
+ for (const summary of await this.skills.list()) if (summary.protectedBy !== null) map.set(summary.name, summary.protectedBy);
482
+ return map;
457
483
  }
458
484
  /**
459
485
  * Control-plane consolidation: merge source skill bodies into `target`,
@@ -473,6 +499,7 @@ var EvolutionCurator = class extends Service {
473
499
  for (const source of sources) {
474
500
  const record = usage.get(source);
475
501
  if (record) record.state = "archived";
502
+ if (record) record.archived_at = (/* @__PURE__ */ new Date()).toISOString();
476
503
  }
477
504
  await saveUsage(this.skills.root, usage, this.io);
478
505
  return result;
@@ -488,6 +515,7 @@ var EvolutionCurator = class extends Service {
488
515
  const usage = await loadUsage(this.skills.root, this.io);
489
516
  const record = usage.get(name);
490
517
  if (record) record.state = "active";
518
+ if (record) record.archived_at = null;
491
519
  await saveUsage(this.skills.root, usage, this.io);
492
520
  const suppressed = new Set(await loadSuppressedNames(this.skills.root, this.io));
493
521
  if (suppressed.delete(name)) try {
@@ -499,4 +527,4 @@ var EvolutionCurator = class extends Service {
499
527
  }
500
528
  };
501
529
  //#endregion
502
- export { EvolutionCurator, EvolutionCurator as default };
530
+ export { EvolutionCurator, EvolutionCurator as default, gateConsolidations };
@@ -5,7 +5,7 @@
5
5
  import { Context, Service } from '@deepseek-ai/cordis';
6
6
  import type Schema from '@deepseek-ai/schemastery';
7
7
  import { SkillLibrary } from '@deepseek-ai/dsh-evolution-core';
8
- import { type CuratorNominations, type CuratorRunReport, type ScopeView, type SkillActionResult } from '@deepseek-ai/dsh-evolution-core';
8
+ import { type CuratorConsolidation, type CuratorNominations, type CuratorRunReport, type ScopeView, type SkillActionResult } from '@deepseek-ai/dsh-evolution-core';
9
9
  declare module '@deepseek-ai/cordis' {
10
10
  interface Context {
11
11
  evolutionCurator: EvolutionCurator;
@@ -54,6 +54,17 @@ export interface CuratorStateRecordShape {
54
54
  lastSummary: string;
55
55
  paused: boolean;
56
56
  }
57
+ /**
58
+ * Block LLM-nominated consolidations that would touch a gate-protected name:
59
+ * exclude / referenced / suppressed skills must never merge (neither as the
60
+ * source being archived nor as the umbrella being edited). Mirrors the control
61
+ * plane's `consolidate()` guard; automatic nominations must pass the same gate.
62
+ */
63
+ export declare function gateConsolidations(consolidations: CuratorConsolidation[], gates: {
64
+ exclude?: ReadonlySet<string>;
65
+ referenced?: ReadonlySet<string>;
66
+ suppressed?: ReadonlySet<string>;
67
+ }): CuratorConsolidation[];
57
68
  export declare class EvolutionCurator extends Service {
58
69
  static inject: string[];
59
70
  static Config: Schema<Config>;
@@ -131,6 +142,8 @@ export declare class EvolutionCurator extends Service {
131
142
  * so the view always predicts what a curator pass may touch.
132
143
  */
133
144
  scopeView(): Promise<ScopeView>;
145
+ /** marker info (pinned/bundled/hub-installed) per skill, from the library list. */
146
+ private protectedNameMap;
134
147
  /**
135
148
  * Control-plane consolidation: merge source skill bodies into `target`,
136
149
  * archive the sources with an absorbed-into marker, and fold their usage
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.27",
4
+ "version": "0.1.0-rc.29",
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.27"
36
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.29"
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.27",
43
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.27"
42
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.29",
43
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.29"
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.27",
49
- "@lmzhen/dsh-evolution-io": "^0.1.0-rc.27",
50
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.27"
48
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.29",
49
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.29",
50
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.29"
51
51
  }
52
52
  }