@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 +104 -42
- package/lib/types/constants.d.ts +1 -0
- package/lib/types/curator.d.ts +6 -0
- package/lib/types/memory-store.d.ts +8 -0
- package/lib/types/skill-store.d.ts +23 -8
- package/lib/types/usage.d.ts +8 -0
- package/package.json +1 -1
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 (
|
|
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:
|
|
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:
|
|
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))
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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))
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
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
|
-
|
|
1222
|
+
const markers = options.allowBundled ? ["hub-installed", "pinned"] : [
|
|
1167
1223
|
"bundled",
|
|
1168
1224
|
"hub-installed",
|
|
1169
1225
|
"pinned"
|
|
1170
|
-
]
|
|
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,
|
|
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 };
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -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;
|
package/lib/types/curator.d.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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;
|
package/lib/types/usage.d.ts
CHANGED
|
@@ -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.
|
|
4
|
+
"version": "0.1.0-rc.13",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|