@lmzhen/dsh-evolution-core 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 CHANGED
@@ -1,4 +1,4 @@
1
- import { dirname, join } from "node:path";
1
+ import { basename, dirname, join } from "node:path";
2
2
  import { cp, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  import { homedir } from "node:os";
@@ -151,6 +151,28 @@ function latestActivityAt(record) {
151
151
  if (values.length === 0) return null;
152
152
  return values.sort().reverse()[0] ?? null;
153
153
  }
154
+ /**
155
+ * Curator suppression sidecar: built-in skills the curator has archived stay
156
+ * suppressed across re-seeds, so the lifecycle never fights a re-created
157
+ * bundled skill. Best-effort load/save, mirroring the usage sidecar posture.
158
+ */
159
+ function suppressedFile(root) {
160
+ return join(root, ".curator-suppressed.json");
161
+ }
162
+ async function loadSuppressedNames(root, io = nodeEvolutionIo()) {
163
+ const raw = await io.readText(suppressedFile(root));
164
+ if (raw === null) return /* @__PURE__ */ new Set();
165
+ try {
166
+ const parsed = JSON.parse(raw);
167
+ if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
168
+ return new Set(parsed.filter((entry) => typeof entry === "string"));
169
+ } catch {
170
+ return /* @__PURE__ */ new Set();
171
+ }
172
+ }
173
+ async function saveSuppressedNames(root, names, io = nodeEvolutionIo()) {
174
+ await io.writeText(suppressedFile(root), JSON.stringify([...names].sort(), null, 2));
175
+ }
154
176
  //#endregion
155
177
  //#region lib/types/constants.js
156
178
  /**
@@ -198,6 +220,7 @@ const DEFAULT_SUBSTANTIVE_MIN_USER_CHARS = 200;
198
220
  const DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS = 500;
199
221
  const DEFAULT_MAX_OPS_PER_PLAN = 32;
200
222
  const DEFAULT_CURATOR_INTERVAL_HOURS = 168;
223
+ const DEFAULT_MIN_IDLE_HOURS = 2;
201
224
  const DEFAULT_STALE_AFTER_DAYS = 30;
202
225
  const DEFAULT_ARCHIVE_AFTER_DAYS = 90;
203
226
  const DEFAULT_MEMORY_CHAR_LIMIT = 2200;
@@ -235,7 +258,9 @@ function computeLifecycleTransitions(usage, config, now = /* @__PURE__ */ new Da
235
258
  for (const [name, record] of usage) {
236
259
  if (record.pinned) continue;
237
260
  if (config.excludeSkillNames?.has(name)) continue;
238
- if (record.created_by !== "agent" && config.manageUnmanaged !== true) continue;
261
+ if (config.suppressedNames?.has(name)) continue;
262
+ const bundled = config.bundledNames?.has(name) === true;
263
+ if (!(record.created_by === "agent" || config.manageUnmanaged === true) && !(config.pruneBuiltins === true && bundled)) continue;
239
264
  if (PROTECTED_BUILTIN_SKILLS.has(name)) continue;
240
265
  if (record.state === "archived") continue;
241
266
  const age = daysSince(null, record.created_at, now.getTime());
@@ -584,6 +609,30 @@ var MemoryStore = class {
584
609
  limit: this.limitFor(target)
585
610
  };
586
611
  }
612
+ /** Percent-based storage hint appended to success message once the target is ≥80% full. */
613
+ storageHint(target, chars) {
614
+ const limit = this.limitFor(target);
615
+ if (limit <= 0) return "";
616
+ const percent = Math.floor(chars * 100 / limit);
617
+ return percent >= 80 ? ` ⚠️ Storage at ${percent}% (${chars}/${limit} chars).` : "";
618
+ }
619
+ /**
620
+ * Best-effort copy of the drifted on-disk file to `<file>.bak.<stamp>` before
621
+ * refusing the write, so an external edit stays recoverable. Failure to back
622
+ * up does not change the refusal semantics.
623
+ */
624
+ async backupDrift(target) {
625
+ const path = fileFor(this.root, target);
626
+ const raw = await this.io.readText(path);
627
+ if (raw === null) return null;
628
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 14);
629
+ try {
630
+ await this.io.writeText(`${path}.bak.${stamp}`, raw);
631
+ return `${path}.bak.${stamp}`;
632
+ } catch {
633
+ return null;
634
+ }
635
+ }
587
636
  async add(target, facts) {
588
637
  const content = facts.trim();
589
638
  if (!content) return {
@@ -606,7 +655,7 @@ var MemoryStore = class {
606
655
  this.resetFailures();
607
656
  return {
608
657
  ok: true,
609
- message: "Entry already exists (no duplicate added).",
658
+ message: `Entry already exists (no duplicate added).${this.storageHint(target, entries.join(ENTRY_DELIMITER).length)}`,
610
659
  entries,
611
660
  chars: entries.join(ENTRY_DELIMITER).length,
612
661
  limit: this.limitFor(target)
@@ -619,7 +668,7 @@ var MemoryStore = class {
619
668
  this.resetFailures();
620
669
  return {
621
670
  ok: true,
622
- message: "Entry added.",
671
+ message: `Entry added.${this.storageHint(target, total)}`,
623
672
  entries: next,
624
673
  chars: total,
625
674
  limit: this.limitFor(target)
@@ -658,13 +707,16 @@ var MemoryStore = class {
658
707
  limit: this.limitFor(target)
659
708
  };
660
709
  }
661
- if (await this.detectDrift(target)) return {
662
- ok: false,
663
- message: "External drift detected in memory file. Resolve the drift before retrying.",
664
- entries: [],
665
- chars: 0,
666
- limit: this.limitFor(target)
667
- };
710
+ if (await this.detectDrift(target)) {
711
+ const backup = await this.backupDrift(target);
712
+ return {
713
+ ok: false,
714
+ message: `External drift detected in memory file.${backup ? ` A backup was saved to ${basename(backup)}.` : ""} Resolve the drift before retrying.`,
715
+ entries: [],
716
+ chars: 0,
717
+ limit: this.limitFor(target)
718
+ };
719
+ }
668
720
  const entries = await this.read(target);
669
721
  const matches = entries.map((entry, index) => ({
670
722
  entry,
@@ -688,7 +740,7 @@ var MemoryStore = class {
688
740
  this.resetFailures();
689
741
  return {
690
742
  ok: true,
691
- message: `Entry ${action === "remove" ? "removed" : "replaced"}.`,
743
+ message: `Entry ${action === "remove" ? "removed" : "replaced"}.${this.storageHint(target, total)}`,
692
744
  entries: next,
693
745
  chars: total,
694
746
  limit: this.limitFor(target)
@@ -702,13 +754,16 @@ var MemoryStore = class {
702
754
  chars: 0,
703
755
  limit: this.limitFor(target)
704
756
  };
705
- if (await this.detectDrift(target)) return {
706
- ok: false,
707
- message: "External drift detected in memory file. Resolve the drift before retrying.",
708
- entries: [],
709
- chars: 0,
710
- limit: this.limitFor(target)
711
- };
757
+ if (await this.detectDrift(target)) {
758
+ const backup = await this.backupDrift(target);
759
+ return {
760
+ ok: false,
761
+ message: `External drift detected in memory file.${backup ? ` A backup was saved to ${basename(backup)}.` : ""} Resolve the drift before retrying.`,
762
+ entries: [],
763
+ chars: 0,
764
+ limit: this.limitFor(target)
765
+ };
766
+ }
712
767
  const entries = await this.read(target);
713
768
  const working = [...entries];
714
769
  for (const [index, op] of operations.entries()) {
@@ -781,7 +836,7 @@ var MemoryStore = class {
781
836
  this.resetFailures();
782
837
  return {
783
838
  ok: true,
784
- message: `Applied ${operations.length} operation(s).`,
839
+ message: `Applied ${operations.length} operation(s).${this.storageHint(target, total)}`,
785
840
  entries: working,
786
841
  chars: total,
787
842
  limit: this.limitFor(target)
@@ -1156,24 +1211,31 @@ var SkillLibrary = class {
1156
1211
  async read(name) {
1157
1212
  return this.io.readText(join(skillDir(this.root, name), "SKILL.md"));
1158
1213
  }
1159
- async writeProtection(name) {
1214
+ async writeProtection(name, origin = "foreground") {
1160
1215
  const dir = skillDir(this.root, name);
1161
1216
  for (const marker of ["bundled", "hub-installed"]) if (await this.io.exists(markerPath(dir, marker))) return marker;
1217
+ if (origin === "background_review" && await this.io.exists(markerPath(dir, "pinned"))) return "pinned";
1162
1218
  return null;
1163
1219
  }
1164
- async deleteProtection(name) {
1220
+ async deleteProtection(name, options = {}) {
1165
1221
  const dir = skillDir(this.root, name);
1166
- for (const marker of [
1222
+ const markers = options.allowBundled ? ["hub-installed", "pinned"] : [
1167
1223
  "bundled",
1168
1224
  "hub-installed",
1169
1225
  "pinned"
1170
- ]) if (await this.io.exists(markerPath(dir, marker))) return marker;
1226
+ ];
1227
+ for (const marker of markers) if (await this.io.exists(markerPath(dir, marker))) return marker;
1171
1228
  return null;
1172
1229
  }
1173
1230
  async isManaged(name) {
1174
1231
  const dir = skillDir(this.root, name);
1175
1232
  return await this.io.exists(markerPath(dir, "hermes-managed"));
1176
1233
  }
1234
+ /** Whether the skill carries the bundled marker (curator prune-builtins eligibility). */
1235
+ async isBundled(name) {
1236
+ const dir = skillDir(this.root, name);
1237
+ return await this.io.exists(markerPath(dir, "bundled"));
1238
+ }
1177
1239
  async create(name, content, origin) {
1178
1240
  const normalized = name.trim();
1179
1241
  if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return {
@@ -1203,13 +1265,13 @@ var SkillLibrary = class {
1203
1265
  path: dir
1204
1266
  };
1205
1267
  }
1206
- async update(name, content) {
1268
+ async update(name, content, origin = "foreground") {
1207
1269
  const dir = skillDir(this.root, name);
1208
1270
  if (!await this.io.readText(join(dir, "SKILL.md"))) return {
1209
1271
  ok: false,
1210
1272
  message: `Skill "${name}" not found.`
1211
1273
  };
1212
- const protection = await this.writeProtection(name);
1274
+ const protection = await this.writeProtection(name, origin);
1213
1275
  if (protection) return {
1214
1276
  ok: false,
1215
1277
  message: `Skill "${name}" is protected (${protection}).`
@@ -1231,14 +1293,14 @@ var SkillLibrary = class {
1231
1293
  path: dir
1232
1294
  };
1233
1295
  }
1234
- async patch(name, oldString, newString, filePath = "", replaceAll = false) {
1296
+ async patch(name, oldString, newString, filePath = "", replaceAll = false, origin = "foreground") {
1235
1297
  const dir = skillDir(this.root, name);
1236
1298
  const skillMd = join(dir, "SKILL.md");
1237
1299
  if (!await this.io.exists(skillMd)) return {
1238
1300
  ok: false,
1239
1301
  message: `Skill "${name}" not found.`
1240
1302
  };
1241
- const protection = await this.writeProtection(name);
1303
+ const protection = await this.writeProtection(name, origin);
1242
1304
  if (protection) return {
1243
1305
  ok: false,
1244
1306
  message: `Skill "${name}" is protected (${protection}).`
@@ -1291,21 +1353,21 @@ var SkillLibrary = class {
1291
1353
  path: dir
1292
1354
  };
1293
1355
  }
1294
- async archive(name, absorbedInto = "") {
1356
+ async archive(name, options = {}) {
1295
1357
  const dir = skillDir(this.root, name);
1296
1358
  if (!await this.io.readText(join(dir, "SKILL.md"))) return {
1297
1359
  ok: false,
1298
1360
  message: `Skill "${name}" not found.`
1299
1361
  };
1300
- const protection = await this.deleteProtection(name);
1362
+ const protection = await this.deleteProtection(name, options.allowBundled === void 0 ? {} : { allowBundled: options.allowBundled });
1301
1363
  if (protection) return {
1302
1364
  ok: false,
1303
1365
  message: `Skill "${name}" is protected (${protection}).`
1304
1366
  };
1305
- if (absorbedInto) {
1306
- if (!await this.io.readText(join(skillDir(this.root, absorbedInto), "SKILL.md"))) return {
1367
+ if (options.absorbedInto) {
1368
+ if (!await this.io.readText(join(skillDir(this.root, options.absorbedInto), "SKILL.md"))) return {
1307
1369
  ok: false,
1308
- message: `absorbed_into="${absorbedInto}" does not exist.`
1370
+ message: `absorbed_into="${options.absorbedInto}" does not exist.`
1309
1371
  };
1310
1372
  }
1311
1373
  const archiveRoot = join(this.root, ".archive");
@@ -1317,7 +1379,7 @@ var SkillLibrary = class {
1317
1379
  await this.io.copy(dir, dest);
1318
1380
  await this.io.remove(dir);
1319
1381
  }
1320
- const reason = absorbedInto ? `Consolidated into ${absorbedInto}` : "Archived by self-evolution curator";
1382
+ const reason = options.reason ?? (options.absorbedInto ? `Consolidated into ${options.absorbedInto}` : "Archived by self-evolution curator");
1321
1383
  await this.io.writeText(join(dest, ".archive-reason"), `${(/* @__PURE__ */ new Date()).toISOString()}: ${reason}\n`);
1322
1384
  return {
1323
1385
  ok: true,
@@ -1330,7 +1392,7 @@ var SkillLibrary = class {
1330
1392
  * an absorbed-into marker. Hermes-style consolidation: overlapping skills
1331
1393
  * collapse into one, and the originals stay recoverable under `.archive/`.
1332
1394
  */
1333
- async consolidate(target, sources) {
1395
+ async consolidate(target, sources, origin = "foreground") {
1334
1396
  const normalizedSources = [...new Set(sources)].filter((name) => name !== target);
1335
1397
  if (normalizedSources.length === 0) return {
1336
1398
  ok: false,
@@ -1346,7 +1408,7 @@ var SkillLibrary = class {
1346
1408
  ok: false,
1347
1409
  message: `Skill "${target}" not found.`
1348
1410
  };
1349
- const targetProtection = await this.writeProtection(target);
1411
+ const targetProtection = await this.writeProtection(target, origin);
1350
1412
  if (targetProtection) return {
1351
1413
  ok: false,
1352
1414
  message: `Skill "${target}" is protected (${targetProtection}).`
@@ -1384,7 +1446,7 @@ var SkillLibrary = class {
1384
1446
  const archived = [];
1385
1447
  try {
1386
1448
  for (const source of normalizedSources) {
1387
- const result = await this.archive(source, target);
1449
+ const result = await this.archive(source, { absorbedInto: target });
1388
1450
  if (!result.ok) return result;
1389
1451
  archived.push(source);
1390
1452
  }
@@ -1447,13 +1509,13 @@ var SkillLibrary = class {
1447
1509
  path: dest
1448
1510
  };
1449
1511
  }
1450
- async writeSupportFile(name, filePath, content) {
1512
+ async writeSupportFile(name, filePath, content, origin = "foreground") {
1451
1513
  const dir = skillDir(this.root, name);
1452
1514
  if (!await this.io.exists(join(dir, "SKILL.md"))) return {
1453
1515
  ok: false,
1454
1516
  message: `Skill "${name}" not found.`
1455
1517
  };
1456
- const protection = await this.writeProtection(name);
1518
+ const protection = await this.writeProtection(name, origin);
1457
1519
  if (protection) return {
1458
1520
  ok: false,
1459
1521
  message: `Skill "${name}" is protected (${protection}).`
@@ -1480,13 +1542,13 @@ var SkillLibrary = class {
1480
1542
  path: target
1481
1543
  };
1482
1544
  }
1483
- async removeSupportFile(name, filePath) {
1545
+ async removeSupportFile(name, filePath, origin = "foreground") {
1484
1546
  const dir = skillDir(this.root, name);
1485
1547
  if (!await this.io.exists(join(dir, "SKILL.md"))) return {
1486
1548
  ok: false,
1487
1549
  message: `Skill "${name}" not found.`
1488
1550
  };
1489
- const protection = await this.writeProtection(name);
1551
+ const protection = await this.writeProtection(name, origin);
1490
1552
  if (protection) return {
1491
1553
  ok: false,
1492
1554
  message: `Skill "${name}" is protected (${protection}).`
@@ -1629,4 +1691,4 @@ var JsonState = class JsonState {
1629
1691
  }
1630
1692
  };
1631
1693
  //#endregion
1632
- export { COMBINED_REVIEW_PROMPT, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, JsonState, MAX_DESCRIPTION_LENGTH, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MemoryStore, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROTECTED_BUILTIN_SKILLS, SKILL_NAME_RE, SKILL_REVIEW_PROMPT, SUPPORT_DIRS, SkillLibrary, advanceReview, buildCuratorRunReport, bumpPatch, bumpUse, bumpView, computeLifecycleTransitions, emptyRecord, evaluateThreat, evolutionHome, evolutionIoAdapter, foldTurn, getRecord, latestActivityAt, loadUsage, markAgentCreated, memoryRoot, nodeEvolutionIo, observeEvent, parseFrontmatter, reviewPrompt, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, usageFile, validateFrontmatter, verifyPromptBundle };
1694
+ export { COMBINED_REVIEW_PROMPT, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MIN_IDLE_HOURS, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, JsonState, MAX_DESCRIPTION_LENGTH, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MemoryStore, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROTECTED_BUILTIN_SKILLS, SKILL_NAME_RE, SKILL_REVIEW_PROMPT, SUPPORT_DIRS, SkillLibrary, advanceReview, buildCuratorRunReport, bumpPatch, bumpUse, bumpView, computeLifecycleTransitions, emptyRecord, evaluateThreat, evolutionHome, evolutionIoAdapter, foldTurn, getRecord, latestActivityAt, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, nodeEvolutionIo, observeEvent, parseFrontmatter, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, usageFile, validateFrontmatter, verifyPromptBundle };
@@ -38,6 +38,7 @@ export declare const DEFAULT_SUBSTANTIVE_MIN_USER_CHARS = 200;
38
38
  export declare const DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS = 500;
39
39
  export declare const DEFAULT_MAX_OPS_PER_PLAN = 32;
40
40
  export declare const DEFAULT_CURATOR_INTERVAL_HOURS = 168;
41
+ export declare const DEFAULT_MIN_IDLE_HOURS = 2;
41
42
  export declare const DEFAULT_STALE_AFTER_DAYS = 30;
42
43
  export declare const DEFAULT_ARCHIVE_AFTER_DAYS = 90;
43
44
  export declare const DEFAULT_MEMORY_CHAR_LIMIT = 2200;
@@ -13,6 +13,12 @@ export interface CuratorConfig {
13
13
  excludeSkillNames?: ReadonlySet<string>;
14
14
  /** When true, usage records without created_by='agent' also enter the lifecycle. */
15
15
  manageUnmanaged?: boolean;
16
+ /** When true, bundled skills (in `bundledNames`) are curation candidates like agent-created ones. */
17
+ pruneBuiltins?: boolean;
18
+ /** Skill names carrying the bundled marker; only read when `pruneBuiltins` is true. */
19
+ bundledNames?: ReadonlySet<string>;
20
+ /** Skill names the curator archived once and must not fight across re-seeds. */
21
+ suppressedNames?: ReadonlySet<string>;
16
22
  }
17
23
  export interface CuratorTransition {
18
24
  name: string;
@@ -41,6 +41,14 @@ export declare class MemoryStore {
41
41
  write(target: MemoryTarget, entries: string[]): Promise<void>;
42
42
  resetFailures(): void;
43
43
  private failure;
44
+ /** Percent-based storage hint appended to success message once the target is ≥80% full. */
45
+ private storageHint;
46
+ /**
47
+ * Best-effort copy of the drifted on-disk file to `<file>.bak.<stamp>` before
48
+ * refusing the write, so an external edit stays recoverable. Failure to back
49
+ * up does not change the refusal semantics.
50
+ */
51
+ private backupDrift;
44
52
  add(target: MemoryTarget, facts: string): Promise<MemoryApplyResult>;
45
53
  replace(target: MemoryTarget, oldText: string, facts: string): Promise<MemoryApplyResult>;
46
54
  remove(target: MemoryTarget, oldText: string): Promise<MemoryApplyResult>;
@@ -27,6 +27,17 @@ export interface SkillActionResult {
27
27
  message: string;
28
28
  path?: string;
29
29
  }
30
+ /** Who is writing: a foreground user-directed tool call, or the autonomous review/curator pipeline. */
31
+ export type WriteOrigin = 'foreground' | 'background_review';
32
+ /** Options for `SkillLibrary.archive`. The absorbed-into name and the archival reason are distinct fields. */
33
+ export interface ArchiveOptions {
34
+ /** Umbrella skill this one was consolidated into; when set it must exist (consolidate semantics). */
35
+ absorbedInto?: string;
36
+ /** Human-readable reason written to `.archive-reason`; default derives from `absorbedInto`. */
37
+ reason?: string;
38
+ /** Permit archiving a bundled skill (curator prune-builtins only; hub-installed and pinned stay protected). */
39
+ allowBundled?: boolean;
40
+ }
30
41
  export declare function skillsRoot(env?: NodeJS.ProcessEnv): string;
31
42
  export interface Frontmatter {
32
43
  name?: string;
@@ -45,27 +56,31 @@ export declare class SkillLibrary {
45
56
  constructor(root?: string, io?: EvolutionIoLike, limits?: SkillLimits);
46
57
  list(): Promise<SkillSummary[]>;
47
58
  read(name: string): Promise<string | null>;
48
- writeProtection(name: string): Promise<string | null>;
49
- deleteProtection(name: string): Promise<string | null>;
59
+ writeProtection(name: string, origin?: WriteOrigin): Promise<string | null>;
60
+ deleteProtection(name: string, options?: {
61
+ allowBundled?: boolean;
62
+ }): Promise<string | null>;
50
63
  isManaged(name: string): Promise<boolean>;
64
+ /** Whether the skill carries the bundled marker (curator prune-builtins eligibility). */
65
+ isBundled(name: string): Promise<boolean>;
51
66
  create(name: string, content: string, origin: 'foreground' | 'background_review'): Promise<SkillActionResult>;
52
- update(name: string, content: string): Promise<SkillActionResult>;
53
- patch(name: string, oldString: string, newString: string, filePath?: string, replaceAll?: boolean): Promise<SkillActionResult>;
54
- archive(name: string, absorbedInto?: string): Promise<SkillActionResult>;
67
+ update(name: string, content: string, origin?: WriteOrigin): Promise<SkillActionResult>;
68
+ patch(name: string, oldString: string, newString: string, filePath?: string, replaceAll?: boolean, origin?: WriteOrigin): Promise<SkillActionResult>;
69
+ archive(name: string, options?: ArchiveOptions): Promise<SkillActionResult>;
55
70
  /**
56
71
  * Merge the bodies of `sources` into `target` and archive the sources with
57
72
  * an absorbed-into marker. Hermes-style consolidation: overlapping skills
58
73
  * collapse into one, and the originals stay recoverable under `.archive/`.
59
74
  */
60
- consolidate(target: string, sources: string[]): Promise<SkillActionResult>;
75
+ consolidate(target: string, sources: string[], origin?: WriteOrigin): Promise<SkillActionResult>;
61
76
  /**
62
77
  * Restore one skill from `.archive/` back to the active root. Hermes-style
63
78
  * recoverability: archival never deletes, and this is the control-plane
64
79
  * path back. The `.archive-reason` marker is dropped on restore.
65
80
  */
66
81
  restoreFromArchive(name: string): Promise<SkillActionResult>;
67
- writeSupportFile(name: string, filePath: string, content: string): Promise<SkillActionResult>;
68
- removeSupportFile(name: string, filePath: string): Promise<SkillActionResult>;
82
+ writeSupportFile(name: string, filePath: string, content: string, origin?: WriteOrigin): Promise<SkillActionResult>;
83
+ removeSupportFile(name: string, filePath: string, origin?: WriteOrigin): Promise<SkillActionResult>;
69
84
  snapshotAll(reason?: string): Promise<string>;
70
85
  listSnapshots(): Promise<Array<{
71
86
  path: string;
@@ -30,4 +30,12 @@ export declare function bumpUse(map: UsageMap, name: string, when?: Date): void;
30
30
  export declare function bumpPatch(map: UsageMap, name: string, when?: Date): void;
31
31
  export declare function markAgentCreated(map: UsageMap, name: string): void;
32
32
  export declare function latestActivityAt(record: UsageRecord): string | null;
33
+ /**
34
+ * Curator suppression sidecar: built-in skills the curator has archived stay
35
+ * suppressed across re-seeds, so the lifecycle never fights a re-created
36
+ * bundled skill. Best-effort load/save, mirroring the usage sidecar posture.
37
+ */
38
+ export declare function suppressedFile(root: string): string;
39
+ export declare function loadSuppressedNames(root: string, io?: EvolutionIoLike): Promise<ReadonlySet<string>>;
40
+ export declare function saveSuppressedNames(root: string, names: ReadonlySet<string>, io?: EvolutionIoLike): Promise<void>;
33
41
  //# sourceMappingURL=usage.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.1.0-rc.12",
4
+ "version": "0.1.0-rc.13",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },