@lmzhen/dsh-evolution-curator 0.1.0-rc.38 → 0.1.0-rc.39

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
@@ -30,3 +30,8 @@ Independent of request-prefix construction. This package does not alter the asse
30
30
  - `restore(name)` (service) / `/evolution skill restore <name>` brings one archived skill back to the active root and resets its usage state.
31
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
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.
33
+
34
+ ## Automatic scheduling
35
+
36
+ - `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()`.
37
+ - `autoStart: false` disables both automatic checks; `/evolution curator run` (manual, gate-skipping) still works.
package/lib/index.js CHANGED
@@ -35,6 +35,7 @@ var EvolutionCurator = class extends Service {
35
35
  pruneBuiltins: z.boolean().default(false),
36
36
  referencedSkillNames: z.array(z.string()).default([]),
37
37
  autoStart: z.boolean().default(true),
38
+ bootGraceSeconds: z.number().default(10),
38
39
  curatorReviewMaxTokens: z.number().default(2048)
39
40
  });
40
41
  skills;
@@ -51,9 +52,11 @@ var EvolutionCurator = class extends Service {
51
52
  manageUnmanaged;
52
53
  pruneBuiltins;
53
54
  referencedSkillNames;
55
+ bootGraceSeconds;
54
56
  curatorReviewMaxTokens;
55
57
  lastRun = 0;
56
58
  timer;
59
+ bootCheck;
57
60
  running = false;
58
61
  constructor(ctx, config = {}) {
59
62
  super(ctx, "evolutionCurator");
@@ -71,6 +74,7 @@ var EvolutionCurator = class extends Service {
71
74
  this.manageUnmanaged = config.manageUnmanaged ?? false;
72
75
  this.pruneBuiltins = config.pruneBuiltins ?? false;
73
76
  this.referencedSkillNames = new Set(config.referencedSkillNames ?? []);
77
+ this.bootGraceSeconds = config.bootGraceSeconds ?? 10;
74
78
  this.curatorReviewMaxTokens = config.curatorReviewMaxTokens ?? 2048;
75
79
  this.lastRun = Date.now();
76
80
  this.ctx.effect(() => {
@@ -90,14 +94,30 @@ var EvolutionCurator = class extends Service {
90
94
  }
91
95
  start() {
92
96
  if (!this.enabled || this.timer) return;
93
- this.timer = setInterval(() => {
94
- if (Date.now() - this.lastRun >= this.lifecycle().intervalHours * 36e5) this.run();
95
- }, 3600 * 1e3);
97
+ this.bootCheck = setTimeout(() => {
98
+ this.bootCheck = void 0;
99
+ this.autoCheck();
100
+ }, this.bootGraceSeconds * 1e3);
101
+ this.bootCheck.unref();
102
+ this.timer = setInterval(() => void this.autoCheck(), 3600 * 1e3);
96
103
  this.timer.unref();
97
104
  }
98
105
  stop() {
99
106
  if (this.timer) clearInterval(this.timer);
100
107
  this.timer = void 0;
108
+ if (this.bootCheck) clearTimeout(this.bootCheck);
109
+ this.bootCheck = void 0;
110
+ }
111
+ /**
112
+ * One automatic schedule check: run a pass when the persisted curator state
113
+ * (falling back to the in-memory clock for state-less compositions) is at
114
+ * least one interval old. All gates — interval, idle, first-run defer,
115
+ * reentrancy — stay inside `run()`, so this method only decides whether to
116
+ * wake it, and never duplicates gate logic.
117
+ */
118
+ async autoCheck() {
119
+ const last = (await this.curatorStateService()?.loadCuratorState())?.lastRunAt ?? this.lastRun;
120
+ if (Date.now() - last >= this.lifecycle().intervalHours * 36e5) await this.run();
101
121
  }
102
122
  /**
103
123
  * Optional Hermes-curator LLM pass. The model may only NOMINATE pruning and
@@ -33,6 +33,8 @@ export interface Config {
33
33
  referencedSkillNames?: string[];
34
34
  /** Start the interval timer on context ready (auto-curation). Default true. */
35
35
  autoStart?: boolean;
36
+ /** Seconds between host boot and the first automatic schedule check (restart catch-up). */
37
+ bootGraceSeconds?: number;
36
38
  /** Max tokens for the optional LLM nomination pass. */
37
39
  curatorReviewMaxTokens?: number;
38
40
  }
@@ -82,14 +84,24 @@ export declare class EvolutionCurator extends Service {
82
84
  private readonly manageUnmanaged;
83
85
  private readonly pruneBuiltins;
84
86
  private readonly referencedSkillNames;
87
+ private readonly bootGraceSeconds;
85
88
  private readonly curatorReviewMaxTokens;
86
89
  private lastRun;
87
90
  private timer;
91
+ private bootCheck;
88
92
  private running;
89
93
  constructor(ctx: Context, config?: Config);
90
94
  private lifecycle;
91
95
  start(): void;
92
96
  stop(): void;
97
+ /**
98
+ * One automatic schedule check: run a pass when the persisted curator state
99
+ * (falling back to the in-memory clock for state-less compositions) is at
100
+ * least one interval old. All gates — interval, idle, first-run defer,
101
+ * reentrancy — stay inside `run()`, so this method only decides whether to
102
+ * wake it, and never duplicates gate logic.
103
+ */
104
+ private autoCheck;
93
105
  /**
94
106
  * Optional Hermes-curator LLM pass. The model may only NOMINATE pruning and
95
107
  * consolidation; every move stays a control-plane operation and each
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.38",
4
+ "version": "0.1.0-rc.39",
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.38"
36
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.39"
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.38",
43
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.38"
42
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.39",
43
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.39"
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.38",
49
- "@lmzhen/dsh-evolution-io": "^0.1.0-rc.38",
50
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.38"
48
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.39",
49
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.39",
50
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.39"
51
51
  }
52
52
  }