@lmzhen/dsh-evolution-core 0.1.0-rc.28 → 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 +50 -22
- package/lib/types/curator.d.ts +3 -2
- package/lib/types/memory-store.d.ts +5 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -28,12 +28,17 @@ function evolutionIoAdapter(provider) {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
function nodeEvolutionIo() {
|
|
31
|
+
const isMissing = (error) => {
|
|
32
|
+
const code = error?.code;
|
|
33
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
34
|
+
};
|
|
31
35
|
return {
|
|
32
36
|
async readText(path) {
|
|
33
37
|
try {
|
|
34
38
|
return await readFile(path, "utf8");
|
|
35
|
-
} catch {
|
|
36
|
-
return null;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (isMissing(error)) return null;
|
|
41
|
+
throw error;
|
|
37
42
|
}
|
|
38
43
|
},
|
|
39
44
|
async writeText(path, content) {
|
|
@@ -59,8 +64,9 @@ function nodeEvolutionIo() {
|
|
|
59
64
|
try {
|
|
60
65
|
await stat(path);
|
|
61
66
|
return true;
|
|
62
|
-
} catch {
|
|
63
|
-
return false;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (isMissing(error)) return false;
|
|
69
|
+
throw error;
|
|
64
70
|
}
|
|
65
71
|
},
|
|
66
72
|
async rename(path, destination) {
|
|
@@ -77,8 +83,9 @@ function nodeEvolutionIo() {
|
|
|
77
83
|
async size(path) {
|
|
78
84
|
try {
|
|
79
85
|
return (await stat(path)).size;
|
|
80
|
-
} catch {
|
|
81
|
-
return null;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (isMissing(error)) return null;
|
|
88
|
+
throw error;
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
};
|
|
@@ -326,20 +333,22 @@ function lifecycleCandidate(name, record, config, bundled) {
|
|
|
326
333
|
/**
|
|
327
334
|
* Read-only scope classification, derived from the SAME gate the transition
|
|
328
335
|
* engine uses (`lifecycleCandidate`), so the view always predicts what a
|
|
329
|
-
* curator pass may touch.
|
|
336
|
+
* curator pass may touch. `protectedNames` carries the marker info the usage
|
|
337
|
+
* records lack (bundled / hub-installed / pinned from `SkillLibrary.list()`).
|
|
330
338
|
*/
|
|
331
|
-
function computeScopeView(usage, config) {
|
|
339
|
+
function computeScopeView(usage, config, protectedNames) {
|
|
332
340
|
const managed = [];
|
|
333
341
|
const watched = [];
|
|
334
342
|
const exempted = [];
|
|
335
|
-
const
|
|
343
|
+
const protectedSet = /* @__PURE__ */ new Set();
|
|
336
344
|
for (const [name, record] of usage) {
|
|
337
345
|
if (config.excludeSkillNames?.has(name) || config.referencedSkillNames?.has(name)) {
|
|
338
346
|
exempted.push(name);
|
|
339
347
|
continue;
|
|
340
348
|
}
|
|
341
349
|
const bundled = config.bundledNames?.has(name) === true;
|
|
342
|
-
|
|
350
|
+
const suppressed = config.suppressedNames?.has(name) === true;
|
|
351
|
+
if (record.pinned || bundled || suppressed || protectedNames?.has(name) === true) protectedSet.add(name);
|
|
343
352
|
if (lifecycleCandidate(name, record, config, bundled)) {
|
|
344
353
|
managed.push(name);
|
|
345
354
|
if (record.state === "stale" || record.quality_warn === true) watched.push(name);
|
|
@@ -349,7 +358,7 @@ function computeScopeView(usage, config) {
|
|
|
349
358
|
managed: managed.sort(),
|
|
350
359
|
watched: watched.sort(),
|
|
351
360
|
exempted: exempted.sort(),
|
|
352
|
-
protected:
|
|
361
|
+
protected: [...protectedSet].sort()
|
|
353
362
|
};
|
|
354
363
|
}
|
|
355
364
|
function daysSince(iso, created, now) {
|
|
@@ -554,9 +563,10 @@ const PROMPT_BUNDLE = createPromptBundle({
|
|
|
554
563
|
completion: COMPLETION_SKILL_REVIEW_PROMPT
|
|
555
564
|
});
|
|
556
565
|
function verifyPromptBundle(bundle = PROMPT_BUNDLE) {
|
|
566
|
+
if (bundle.id !== "dsh-evolution@2" || bundle.version !== 2) return false;
|
|
557
567
|
const canonical = JSON.stringify({
|
|
558
|
-
id:
|
|
559
|
-
version:
|
|
568
|
+
id: PROMPT_BUNDLE_ID,
|
|
569
|
+
version: 2,
|
|
560
570
|
prompts: Object.fromEntries(Object.entries(bundle.prompts).sort())
|
|
561
571
|
});
|
|
562
572
|
return bundle.sha256 === sha256(canonical);
|
|
@@ -950,11 +960,15 @@ var MemoryStore = class {
|
|
|
950
960
|
limit: this.limitFor(target)
|
|
951
961
|
};
|
|
952
962
|
}
|
|
953
|
-
/**
|
|
963
|
+
/**
|
|
964
|
+
* StorageHint percentage must clamp at 100 like the render header: a drifted
|
|
965
|
+
* entry can push chars past the limit, and "Storage at 125%" contradicts the
|
|
966
|
+
* clamped usage indicator.
|
|
967
|
+
*/
|
|
954
968
|
storageHint(target, chars) {
|
|
955
969
|
const limit = this.limitFor(target);
|
|
956
970
|
if (limit <= 0) return "";
|
|
957
|
-
const percent = Math.floor(chars * 100 / limit);
|
|
971
|
+
const percent = Math.min(100, Math.floor(chars * 100 / limit));
|
|
958
972
|
return percent >= 80 ? ` ⚠️ Storage at ${percent}% (${chars}/${limit} chars).` : "";
|
|
959
973
|
}
|
|
960
974
|
/**
|
|
@@ -966,10 +980,10 @@ var MemoryStore = class {
|
|
|
966
980
|
*/
|
|
967
981
|
async backupFile(target) {
|
|
968
982
|
const path = fileFor(this.root, target);
|
|
969
|
-
const
|
|
983
|
+
const unique = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 14)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
970
984
|
try {
|
|
971
|
-
await this.io.copy(path, `${path}.bak.${
|
|
972
|
-
return `${path}.bak.${
|
|
985
|
+
await this.io.copy(path, `${path}.bak.${unique}`);
|
|
986
|
+
return `${path}.bak.${unique}`;
|
|
973
987
|
} catch {
|
|
974
988
|
return null;
|
|
975
989
|
}
|
|
@@ -997,6 +1011,16 @@ var MemoryStore = class {
|
|
|
997
1011
|
async add(target, facts) {
|
|
998
1012
|
const refusal = await this.oversizedRefusal(target);
|
|
999
1013
|
if (refusal) return refusal;
|
|
1014
|
+
if (await this.detectDrift(target)) {
|
|
1015
|
+
const backup = await this.backupFile(target);
|
|
1016
|
+
return {
|
|
1017
|
+
ok: false,
|
|
1018
|
+
message: `External drift detected in memory file.${backup ? ` A backup was saved to ${basename(backup)}.` : ""} Resolve the drift before retrying.`,
|
|
1019
|
+
entries: [],
|
|
1020
|
+
chars: 0,
|
|
1021
|
+
limit: this.limitFor(target)
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1000
1024
|
const content = facts.trim();
|
|
1001
1025
|
if (!content) return {
|
|
1002
1026
|
ok: false,
|
|
@@ -1026,7 +1050,8 @@ var MemoryStore = class {
|
|
|
1026
1050
|
}
|
|
1027
1051
|
const next = [...entries, this.addDatePrefix ? `## ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}\n${content}` : content];
|
|
1028
1052
|
const total = next.join(ENTRY_DELIMITER).length;
|
|
1029
|
-
|
|
1053
|
+
const addLimit = this.limitFor(target);
|
|
1054
|
+
if (addLimit > 0 && total > addLimit) return this.failure(target, `Adding this entry would exceed the ${addLimit} char limit. Consolidate or remove stale entries, then retry.`, entries);
|
|
1030
1055
|
await this.write(target, next);
|
|
1031
1056
|
this.resetFailures();
|
|
1032
1057
|
return {
|
|
@@ -1100,7 +1125,8 @@ var MemoryStore = class {
|
|
|
1100
1125
|
if (action === "remove") next.splice(index, 1);
|
|
1101
1126
|
else next[index] = content;
|
|
1102
1127
|
const total = next.join(ENTRY_DELIMITER).length;
|
|
1103
|
-
|
|
1128
|
+
const mutateLimit = this.limitFor(target);
|
|
1129
|
+
if (mutateLimit > 0 && total > mutateLimit) return this.failure(target, `Resulting memory would exceed the ${mutateLimit} char limit.`, entries);
|
|
1104
1130
|
await this.write(target, next);
|
|
1105
1131
|
this.resetFailures();
|
|
1106
1132
|
return {
|
|
@@ -1198,7 +1224,8 @@ var MemoryStore = class {
|
|
|
1198
1224
|
}
|
|
1199
1225
|
}
|
|
1200
1226
|
const total = working.join(ENTRY_DELIMITER).length;
|
|
1201
|
-
|
|
1227
|
+
const batchLimit = this.limitFor(target);
|
|
1228
|
+
if (batchLimit > 0 && total > batchLimit) return this.failure(target, `Batch result (${total} chars) exceeds the ${batchLimit} limit. Remove or shorten more entries in the same batch.`, entries);
|
|
1202
1229
|
await this.write(target, working);
|
|
1203
1230
|
this.resetFailures();
|
|
1204
1231
|
return {
|
|
@@ -1262,7 +1289,8 @@ var MemoryStore = class {
|
|
|
1262
1289
|
const raw = await this.io.readText(fileFor(this.root, target));
|
|
1263
1290
|
if (raw === null) return false;
|
|
1264
1291
|
const entries = normalizeEntries(raw);
|
|
1265
|
-
|
|
1292
|
+
const limit = this.limitFor(target);
|
|
1293
|
+
if (limit > 0 && entries.some((entry) => entry.length > limit)) return true;
|
|
1266
1294
|
return render(entries) !== raw;
|
|
1267
1295
|
}
|
|
1268
1296
|
};
|
package/lib/types/curator.d.ts
CHANGED
|
@@ -106,8 +106,9 @@ export interface ScopeView {
|
|
|
106
106
|
/**
|
|
107
107
|
* Read-only scope classification, derived from the SAME gate the transition
|
|
108
108
|
* engine uses (`lifecycleCandidate`), so the view always predicts what a
|
|
109
|
-
* curator pass may touch.
|
|
109
|
+
* curator pass may touch. `protectedNames` carries the marker info the usage
|
|
110
|
+
* records lack (bundled / hub-installed / pinned from `SkillLibrary.list()`).
|
|
110
111
|
*/
|
|
111
|
-
export declare function computeScopeView(usage: UsageMap, config: CuratorConfig): ScopeView;
|
|
112
|
+
export declare function computeScopeView(usage: UsageMap, config: CuratorConfig, protectedNames?: ReadonlyMap<string, string>): ScopeView;
|
|
112
113
|
export declare function computeLifecycleTransitions(usage: UsageMap, config: CuratorConfig, now?: Date): CuratorResult;
|
|
113
114
|
//# sourceMappingURL=curator.d.ts.map
|
|
@@ -48,7 +48,11 @@ export declare class MemoryStore {
|
|
|
48
48
|
write(target: MemoryTarget, entries: string[]): Promise<void>;
|
|
49
49
|
resetFailures(): void;
|
|
50
50
|
private failure;
|
|
51
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* StorageHint percentage must clamp at 100 like the render header: a drifted
|
|
53
|
+
* entry can push chars past the limit, and "Storage at 125%" contradicts the
|
|
54
|
+
* clamped usage indicator.
|
|
55
|
+
*/
|
|
52
56
|
private storageHint;
|
|
53
57
|
/**
|
|
54
58
|
* Best-effort raw-copy backup of the on-disk file to `<file>.bak.<stamp>`
|
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.29",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|