@lmzhen/dsh-evolution-curator 0.3.74 → 0.3.77

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 CHANGED
@@ -39,3 +39,5 @@ Independent of request-prefix construction. This package does not alter the asse
39
39
 
40
40
  - `autoStart` (default true) arms an hourly interval check plus a deferred catch-up check `bootGraceSeconds` (default 10) after host boot. Both decide due-ness from the **persisted** `lastRunAt`, so a restart with an overdue schedule runs the first pass within the boot grace instead of waiting a full interval. `bootGraceSeconds: 0` disables the deferral (not recommended: the check may run against a half-mounted host). All scheduling gates — interval, idle, first-run deferral, and the reentrancy guard — remain inside `run()`.
41
41
  - `autoStart: false` disables both automatic checks; `/evolution curator run` (manual, gate-skipping) still works.
42
+
43
+ **Runtime invariant:** No companion is published. The platform auto-assembles nothing and the family mounts no `<pkg>/invariant` cordis row, so a companion here would never execute (v37 S2.1 / I-3).
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_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MIN_IDLE_HOURS, DEFAULT_STALE_AFTER_DAYS, EvolutionGateSet, MAX_TIMER_DELAY_MS, SKILL_NAME_RE, SkillLibrary, buildCuratorRunReport, clampedNumber, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, emptyRecord, evolutionHome, evolutionIoAdapter, foldCuratorFields, loadSuppressedNames, loadUsage, markerEntryName, mutateUsage, parseCuratorNominations, parseFrontmatter, relatedSkillNames, renderCuratorReportMarkdown, resolveSkillsRoot, updateSuppressedNames, usageObserved } from "@lmzhen/dsh-evolution-core";
6
+ import { CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MIN_IDLE_HOURS, DEFAULT_STALE_AFTER_DAYS, EvolutionGateSet, MAX_TIMER_DELAY_MS, SKILL_NAME_RE, buildCuratorRunReport, clampedNumber, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, emptyRecord, evolutionHome, evolutionIoAdapter, foldCuratorFields, loadSuppressedNames, loadUsage, markerEntryName, mutateUsage, newSkillLibrary, parseCuratorNominations, parseFrontmatter, relatedSkillNames, renderCuratorReportMarkdown, updateSuppressedNames, usageObserved } 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.
@@ -95,8 +95,10 @@ var EvolutionCurator = class extends Service {
95
95
  constructor(ctx, config = {}) {
96
96
  super(ctx, "evolutionCurator");
97
97
  this.io = evolutionIoAdapter(() => ctx.evolutionIo.provider());
98
- this.skills = new SkillLibrary(resolveSkillsRoot({ root: config.root }), this.io, void 0, (event) => {
99
- this.ctx.emit("evolution/skill-mutated", event);
98
+ this.skills = newSkillLibrary({
99
+ config,
100
+ io: this.io,
101
+ ctx: this.ctx
100
102
  });
101
103
  this.enabled = config.enabled ?? true;
102
104
  const clamped = [];
@@ -524,7 +526,7 @@ var EvolutionCurator = class extends Service {
524
526
  suppressedNames,
525
527
  referencedSkillNames: this.referencedSkillNames
526
528
  }, /* @__PURE__ */ new Date(), gates, protectedNames);
527
- const recommendPool = [...new Set([...result.markStale, ...dedupMembers])];
529
+ const recommendPool = [...new Set([...result.markStale, ...dedupMembers])].filter((name) => SKILL_NAME_RE.test(name));
528
530
  const nominations = this.llmReview ? await this.recommend(recommendPool, { dryRun }) : {
529
531
  prunings: [],
530
532
  consolidations: [],
@@ -742,6 +744,7 @@ var EvolutionCurator = class extends Service {
742
744
  consolidated: []
743
745
  };
744
746
  }
747
+ const alreadyArchived = /* @__PURE__ */ new Set();
745
748
  for (const name of archiveCandidates) {
746
749
  if (!treeNames.has(name)) {
747
750
  const record = usage.get(name);
@@ -751,6 +754,10 @@ var EvolutionCurator = class extends Service {
751
754
  stateOwned.add(name);
752
755
  this.ctx.logger.warn(`evolution-curator: skill "${name}" left the tree without an archive move - its usage record was folded to archived; if the directory is still on disk, repair or remove it manually (it will not re-enter the lifecycle while the record is archived)`);
753
756
  }
757
+ if (!SKILL_NAME_RE.test(name)) {
758
+ this.ctx.logger.warn(`evolution-curator: usage sidecar key "${name}" is not a valid skill name - skipped (no archive/marker probe, no suppression write)`);
759
+ continue;
760
+ }
754
761
  let wasBundled = false;
755
762
  try {
756
763
  wasBundled = await this.io.exists(join(this.skills.root, ".archive", name, markerEntryName("bundled")));
@@ -802,6 +809,7 @@ var EvolutionCurator = class extends Service {
802
809
  path: archived.path ?? "",
803
810
  reason: "Lifecycle: reached archive threshold"
804
811
  });
812
+ alreadyArchived.add(name);
805
813
  if (bundledNames.has(name)) {
806
814
  suppressedNames.add(name);
807
815
  suppressedAdded.add(name);
@@ -809,7 +817,6 @@ var EvolutionCurator = class extends Service {
809
817
  }
810
818
  }
811
819
  }
812
- const alreadyArchived = new Set(archiveCandidates);
813
820
  let consolidationDisposed = false;
814
821
  if (this.isDisposed()) {
815
822
  consolidationDisposed = true;
@@ -840,7 +847,8 @@ var EvolutionCurator = class extends Service {
840
847
  alreadyArchived.add(nomination.from);
841
848
  executedConsolidations.push({
842
849
  from: nomination.from,
843
- into: nomination.into
850
+ into: nomination.into,
851
+ ...nomination.mode === void 0 ? {} : { mode: nomination.mode }
844
852
  });
845
853
  archivedSkills.push({
846
854
  name: nomination.from,
@@ -905,6 +913,7 @@ var EvolutionCurator = class extends Service {
905
913
  }
906
914
  const real = [];
907
915
  const errors = [];
916
+ let unorderableWarned = false;
908
917
  for (const name of entries.filter((entry) => entry.startsWith("curator-") && entry.endsWith(".json"))) try {
909
918
  const raw = await this.io.readText(join(reportsRoot, name));
910
919
  if (raw === null) continue;
@@ -914,10 +923,15 @@ var EvolutionCurator = class extends Service {
914
923
  const mtime = await this.io.mtime?.(join(reportsRoot, name)) ?? null;
915
924
  if (mtime !== null) startedAt = mtime;
916
925
  }
926
+ if (!Number.isFinite(startedAt) && typeof parsed.at === "string") startedAt = Date.parse(parsed.at);
917
927
  if (Number.isFinite(startedAt)) (name.startsWith("curator-error-") ? errors : real).push({
918
928
  name,
919
929
  startedAt
920
930
  });
931
+ else if (!unorderableWarned) {
932
+ unorderableWarned = true;
933
+ this.ctx.logger.warn(`evolution-curator: report "${name}" carries no usable timestamp and this backend has no mtime probe - it is kept outside the retention window`);
934
+ }
921
935
  } catch {}
922
936
  real.sort((a, b) => b.startedAt - a.startedAt);
923
937
  errors.sort((a, b) => b.startedAt - a.startedAt);
@@ -938,10 +952,26 @@ var EvolutionCurator = class extends Service {
938
952
  }
939
953
  async latestReport() {
940
954
  const reportsRoot = join(evolutionHome(), "reports");
941
- const names = (await this.io.list(reportsRoot)).filter((name) => name.startsWith("curator-") && name.endsWith(".json") && !name.startsWith("curator-error-"));
955
+ let names;
956
+ try {
957
+ names = (await this.io.list(reportsRoot)).filter((name) => name.startsWith("curator-") && name.endsWith(".json") && !name.startsWith("curator-error-"));
958
+ } catch (error) {
959
+ this.ctx.logger.warn(`evolution-curator: latestReport could not list the reports directory (${error instanceof Error ? error.message : String(error)}) - answering "no report"`);
960
+ return null;
961
+ }
942
962
  let latest = null;
963
+ let probeWarned = false;
943
964
  for (const name of names) {
944
- const mtime = await this.io.mtime?.(join(reportsRoot, name)) ?? null;
965
+ let mtime;
966
+ try {
967
+ mtime = await this.io.mtime?.(join(reportsRoot, name)) ?? null;
968
+ } catch (error) {
969
+ if (!probeWarned) {
970
+ probeWarned = true;
971
+ this.ctx.logger.warn(`evolution-curator: latestReport could not read a report mtime (${error instanceof Error ? error.message : String(error)}) - the affected report(s) are not eligible as the latest`);
972
+ }
973
+ continue;
974
+ }
945
975
  if (mtime === null) continue;
946
976
  if (latest === null || mtime > latest.mtime) latest = {
947
977
  name,
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { Context, Service } from '@deepseek-ai/cordis';
6
6
  import type Schema from '@deepseek-ai/schemastery';
7
- import { EvolutionGateSet, SkillLibrary } from '@lmzhen/dsh-evolution-core';
7
+ import { EvolutionGateSet, type SkillLibrary } from '@lmzhen/dsh-evolution-core';
8
8
  import { type CuratorConsolidation, type CuratorNominations, type CuratorRunReport, type ScopeView, type SkillActionResult, type SkillHealthVerdict } from '@lmzhen/dsh-evolution-core';
9
9
  import type { CuratorStateRecord } from '@lmzhen/dsh-evolution-state';
10
10
  declare module '@deepseek-ai/cordis' {
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.3.74",
4
+ "version": "0.3.77",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -18,10 +18,6 @@
18
18
  "types": "./lib/types/index.d.ts",
19
19
  "default": "./lib/index.js"
20
20
  },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
21
  "./package.json": "./package.json"
26
22
  },
27
23
  "files": [
@@ -31,22 +27,20 @@
31
27
  "license": "MIT",
32
28
  "dependencies": {
33
29
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-core": "^0.3.74"
30
+ "@lmzhen/dsh-evolution-core": "^0.3.77"
35
31
  },
36
32
  "peerDependencies": {
37
33
  "@deepseek-ai/cordis": "^4.0.1",
38
- "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
39
34
  "@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
40
35
  "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
41
- "@lmzhen/dsh-evolution-io": "^0.3.74",
42
- "@lmzhen/dsh-evolution-state": "^0.3.74"
36
+ "@lmzhen/dsh-evolution-io": "^0.3.77",
37
+ "@lmzhen/dsh-evolution-state": "^0.3.77"
43
38
  },
44
39
  "devDependencies": {
45
- "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
46
40
  "@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
47
41
  "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
48
- "@lmzhen/dsh-evolution-core": "^0.3.74",
49
- "@lmzhen/dsh-evolution-io": "^0.3.74",
50
- "@lmzhen/dsh-evolution-state": "^0.3.74"
42
+ "@lmzhen/dsh-evolution-core": "^0.3.77",
43
+ "@lmzhen/dsh-evolution-io": "^0.3.77",
44
+ "@lmzhen/dsh-evolution-state": "^0.3.77"
51
45
  }
52
46
  }
package/lib/invariant.js DELETED
@@ -1,8 +0,0 @@
1
- //#region lib/types/invariant.js
2
- const PACKAGE_NAME = "@lmzhen/dsh-evolution-curator";
3
- const name = "evolution-curator-invariant";
4
- const inject = ["invariants"];
5
- const install = () => {};
6
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
7
- //#endregion
8
- export { apply, inject, name };
@@ -1,5 +0,0 @@
1
- import type { Context } from '@deepseek-ai/cordis';
2
- export declare const name = "evolution-curator-invariant";
3
- export declare const inject: string[];
4
- export declare const apply: (ctx: Context) => Promise<() => void>;
5
- //# sourceMappingURL=invariant.d.ts.map