@lmzhen/dsh-evolution-core 0.3.60 → 0.3.62
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 +90 -23
- package/lib/types/constants.d.ts +7 -0
- package/lib/types/env.d.ts +7 -3
- package/lib/types/skill-health.d.ts +7 -1
- package/lib/types/skill-store.d.ts +14 -1
- package/lib/types/threats.d.ts +5 -3
- package/lib/types/usage.d.ts +3 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -117,7 +117,7 @@ async function renameWithRetry(tmp, target, fn = rename) {
|
|
|
117
117
|
async function writeDurableTmp(target, content, openImpl = open) {
|
|
118
118
|
const tmp = `${target}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
119
119
|
try {
|
|
120
|
-
const handle = await openImpl(tmp, "wx");
|
|
120
|
+
const handle = await openImpl(tmp, "wx", process.platform === "win32" ? void 0 : await stat(target).then((value) => value.mode & 511).catch(() => void 0));
|
|
121
121
|
try {
|
|
122
122
|
await handle.writeFile(content, "utf8");
|
|
123
123
|
await handle.sync();
|
|
@@ -312,8 +312,16 @@ function nodeEvolutionIo(lockAttempts = 40) {
|
|
|
312
312
|
}).catch(async () => {
|
|
313
313
|
const body = await readFile(lock, "utf8").catch(() => "");
|
|
314
314
|
if (pendingSelfCleanup.size >= 64) {
|
|
315
|
-
|
|
316
|
-
|
|
315
|
+
let droppable;
|
|
316
|
+
for (const candidate of pendingSelfCleanup.keys()) {
|
|
317
|
+
if (candidate === lock) continue;
|
|
318
|
+
if (await readFile(candidate, "utf8").then(() => false, () => true)) {
|
|
319
|
+
droppable = candidate;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const victim = droppable ?? pendingSelfCleanup.keys().next().value;
|
|
324
|
+
if (victim !== void 0) pendingSelfCleanup.delete(victim);
|
|
317
325
|
}
|
|
318
326
|
pendingSelfCleanup.set(lock, body);
|
|
319
327
|
});
|
|
@@ -452,8 +460,9 @@ function nodeEvolutionIo(lockAttempts = 40) {
|
|
|
452
460
|
async isSymlink(path) {
|
|
453
461
|
try {
|
|
454
462
|
return (await lstat(path)).isSymbolicLink();
|
|
455
|
-
} catch {
|
|
456
|
-
return null;
|
|
463
|
+
} catch (error) {
|
|
464
|
+
if (isMissing(error)) return null;
|
|
465
|
+
throw error;
|
|
457
466
|
}
|
|
458
467
|
},
|
|
459
468
|
async mtime(path) {
|
|
@@ -614,7 +623,9 @@ function foldCuratorFields(disk, curated, stateOwned) {
|
|
|
614
623
|
/** Whole-file usage write (V6-37, 0.3.37): this is the ONE path that bypasses
|
|
615
624
|
* the malformed-defense and the transact lock — prefer `mutateUsage` for any
|
|
616
625
|
* read-modify-write so a concurrent writer cannot lose its update and a
|
|
617
|
-
* malformed sidecar stays recoverable. Kept for fixture/test seeding.
|
|
626
|
+
* malformed sidecar stays recoverable. Kept for fixture/test seeding.
|
|
627
|
+
* @internal P3-16 (v14): no production caller (verified by grep); exported for
|
|
628
|
+
* the family's tests only. Do not use it to write the sidecar in new code. */
|
|
618
629
|
async function saveUsage(root, map, io = nodeEvolutionIo()) {
|
|
619
630
|
const obj = Object.fromEntries(map.entries());
|
|
620
631
|
await io.writeText(usageFile(root), JSON.stringify(obj, null, 2));
|
|
@@ -778,6 +789,13 @@ const DEFAULT_USER_CHAR_LIMIT = 1375;
|
|
|
778
789
|
/** Consolidation-failure backoff cap, shared by MemoryStore and memory-files' Config default. */
|
|
779
790
|
const DEFAULT_CONSOLIDATION_FAILURES = 3;
|
|
780
791
|
const DEFAULT_SKILL_CONTENT_CHARS = 1e5;
|
|
792
|
+
/** P3-19 (v14): defaults that were written twice (schema `.default()` AND the
|
|
793
|
+
* clamp fallback literal) now have one home per value. */
|
|
794
|
+
const DEFAULT_REVIEW_TIMEOUT_MS = 12e4;
|
|
795
|
+
const DEFAULT_REVIEW_CONTEXT_MESSAGES = 60;
|
|
796
|
+
const DEFAULT_REVIEW_MESSAGE_CHARS = 2e3;
|
|
797
|
+
const DEFAULT_CURATOR_BOOT_GRACE_SECONDS = 10;
|
|
798
|
+
const DEFAULT_CURATOR_REVIEW_MAX_TOKENS = 2048;
|
|
781
799
|
/** 0.3.17 (S3.10, T-1): control-plane fields a model-facing write call may
|
|
782
800
|
* never carry — single source for plan-validator, evolution-policy and the
|
|
783
801
|
* threat scanner (they used to each hardcode the list). */
|
|
@@ -1339,10 +1357,14 @@ async function readEvolutionTimeline(io, path) {
|
|
|
1339
1357
|
* reads inside patch YAML `!!js` expressions (session-query path/openAt) stay
|
|
1340
1358
|
* in the profile config evaluation — they are NOT migrated (they are resolved
|
|
1341
1359
|
* at cordis config time, not plugin time) but are documented in the README
|
|
1342
|
-
* env table.
|
|
1360
|
+
* env table. `EVOLUTION_SCOPE` is read by the source installers only
|
|
1361
|
+
* (`packages/scripts/install-layered.mjs`, `packages/test-support/row-contract.ts`),
|
|
1362
|
+
* never by plugin runtime code.
|
|
1363
|
+
*
|
|
1364
|
+
* v14 P3-1: the former `EVOLUTION_ENV_KEYS` export was deleted — nothing read
|
|
1365
|
+
* it, so the "generated env reference" it claimed to source was never
|
|
1366
|
+
* generated (the README table is maintained by hand and now lists every key).
|
|
1343
1367
|
*/
|
|
1344
|
-
/** Plugin-side DSH_EVOLUTION_* keys (config-layer keys are documented separately). */
|
|
1345
|
-
const EVOLUTION_ENV_KEYS = ["DSH_EVOLUTION_ALLOW_ROW_COLLISIONS"];
|
|
1346
1368
|
const ALLOW_ROW_COLLISIONS = "1";
|
|
1347
1369
|
/** N-5 escape: `DSH_EVOLUTION_ALLOW_ROW_COLLISIONS=1` downgrades a delta-row
|
|
1348
1370
|
* collision from fail-loud to warn+keep-both. Any other value (including an
|
|
@@ -1977,7 +1999,7 @@ const PATTERNS = [
|
|
|
1977
1999
|
label: "read_secrets",
|
|
1978
2000
|
category: "exfiltration",
|
|
1979
2001
|
scope: "all",
|
|
1980
|
-
regex: /\bcat\s+[^\n]{0,512}(?:\.env|
|
|
2002
|
+
regex: /\bcat\s+[^\n]{0,512}(?:\.env(?!\w)|(?:\bcredentials\b)|\.netrc|\.pgpass|\.npmrc|\.pypirc)/i
|
|
1981
2003
|
},
|
|
1982
2004
|
{
|
|
1983
2005
|
label: "ssh_backdoor",
|
|
@@ -2027,6 +2049,12 @@ const PATTERNS = [
|
|
|
2027
2049
|
scope: "strict",
|
|
2028
2050
|
regex: /(?:api[_-]?key|token|secret|password)\s*[=:]\s*["'][a-z0-9+/=_-]{20,}["']/i
|
|
2029
2051
|
},
|
|
2052
|
+
{
|
|
2053
|
+
label: "jwt_like_secret",
|
|
2054
|
+
category: "hardcoded_secrets",
|
|
2055
|
+
scope: "strict",
|
|
2056
|
+
regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/
|
|
2057
|
+
},
|
|
2030
2058
|
{
|
|
2031
2059
|
label: "private_key_block",
|
|
2032
2060
|
category: "hardcoded_secrets",
|
|
@@ -2060,12 +2088,13 @@ const PATTERN_OVERLAP = 4096;
|
|
|
2060
2088
|
function scanThreats(text, scope = "strict", maxScanChars = 65536, options = NO_SCAN_OPTIONS) {
|
|
2061
2089
|
const windowSize = clampedNumber(maxScanChars, 65536, { min: 4097 });
|
|
2062
2090
|
const findings = [];
|
|
2063
|
-
|
|
2091
|
+
const excluded = new Set(options.excludeLabels ?? []);
|
|
2092
|
+
if (ZERO_WIDTH_CHARS.test(text) && !excluded.has("unicode_zero_width")) findings.push({
|
|
2064
2093
|
label: "unicode_zero_width",
|
|
2065
2094
|
category: "unicode_obfuscation",
|
|
2066
2095
|
scope
|
|
2067
2096
|
});
|
|
2068
|
-
if (BIDI_CHARS.test(text)) findings.push({
|
|
2097
|
+
if (BIDI_CHARS.test(text) && !excluded.has("unicode_bidi_override")) findings.push({
|
|
2069
2098
|
label: "unicode_bidi_override",
|
|
2070
2099
|
category: "unicode_obfuscation",
|
|
2071
2100
|
scope
|
|
@@ -2077,7 +2106,6 @@ function scanThreats(text, scope = "strict", maxScanChars = 65536, options = NO_
|
|
|
2077
2106
|
const step = Math.max(Math.floor(windowSize / 2), 1);
|
|
2078
2107
|
for (let start = 0; start < normalized.length; start += step) windows.push(normalized.slice(start, start + windowSize));
|
|
2079
2108
|
}
|
|
2080
|
-
const excluded = new Set(options.excludeLabels ?? []);
|
|
2081
2109
|
const seen = /* @__PURE__ */ new Set();
|
|
2082
2110
|
for (const window of windows) for (const pattern of PATTERNS) {
|
|
2083
2111
|
if (SCOPE_ORDER[pattern.scope] > SCOPE_ORDER[scope]) continue;
|
|
@@ -2121,9 +2149,11 @@ function scanContentThreats(text, maxScanChars = 65536, options = NO_SCAN_OPTION
|
|
|
2121
2149
|
* V10-03 (P2-18): suffix the SkillLibrary/MemoryStore write gates append to a
|
|
2122
2150
|
* block message — the hit label is already embedded by scanContentThreats /
|
|
2123
2151
|
* scanMemoryThreats, this names the deployable self-heal path so the model
|
|
2124
|
-
* (or operator) can allowlist a known-benign label.
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
2152
|
+
* (or operator) can allowlist a known-benign label. P2-4 (v14): the
|
|
2153
|
+
* evolution-threat guard channel now carries `threatExemptLabels` too, so its
|
|
2154
|
+
* block message (which embeds the same exemption sentence) is accurate there
|
|
2155
|
+
* as well; this suffix stays store-side because the guard returns the scan
|
|
2156
|
+
* message verbatim.
|
|
2127
2157
|
*/
|
|
2128
2158
|
const THREAT_EXEMPT_HINT = " If this is a legitimate false positive, the deployment can allow its label via the threatExemptLabels store option.";
|
|
2129
2159
|
//#endregion
|
|
@@ -2300,11 +2330,14 @@ var MemoryStore = class {
|
|
|
2300
2330
|
async backupFile(target) {
|
|
2301
2331
|
const path = fileFor(this.root, target);
|
|
2302
2332
|
const backup = `${path}.bak`;
|
|
2333
|
+
const staging = `${backup}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
2303
2334
|
try {
|
|
2304
|
-
await this.io.remove(
|
|
2305
|
-
await this.io.copy(path,
|
|
2335
|
+
await this.io.remove(staging).catch(() => {});
|
|
2336
|
+
await this.io.copy(path, staging);
|
|
2337
|
+
await this.io.rename(staging, backup);
|
|
2306
2338
|
return backup;
|
|
2307
2339
|
} catch {
|
|
2340
|
+
await this.io.remove(staging).catch(() => {});
|
|
2308
2341
|
return null;
|
|
2309
2342
|
}
|
|
2310
2343
|
}
|
|
@@ -2650,7 +2683,7 @@ var MemoryStore = class {
|
|
|
2650
2683
|
const note = safe.length === entries.length ? "" : ` (${entries.length - safe.length} threat-matched entries filtered)`;
|
|
2651
2684
|
const usage = limit > 0 ? ` [${Math.min(100, Math.floor(body.length * 100 / limit))}% — ${body.length}/${limit} chars]` : "";
|
|
2652
2685
|
parts.push(`## ${label} (${safe.length} entries)${usage}${note}\n${body}`);
|
|
2653
|
-
}
|
|
2686
|
+
} else if (entries.length > 0) parts.push(`## ${label} — ${entries.length} entries withheld by the security scan; none injected`);
|
|
2654
2687
|
}
|
|
2655
2688
|
return parts.join("\n\n");
|
|
2656
2689
|
}
|
|
@@ -3982,14 +4015,29 @@ var SkillLibrary = class {
|
|
|
3982
4015
|
dirOf(name) {
|
|
3983
4016
|
return skillDir(this.root, name.trim());
|
|
3984
4017
|
}
|
|
3985
|
-
/**
|
|
4018
|
+
/**
|
|
4019
|
+
* Name-format guard for every path-building entry point (P1-1/v14 closed the
|
|
4020
|
+
* one gap: `patch`). Write paths, protection probes and support-file
|
|
4021
|
+
* enumeration all call it before `dirOf`, so no directory path is ever built
|
|
4022
|
+
* from a name that could escape the skills root. `list()` and `snapshotAll()`
|
|
4023
|
+
* are the deliberate exceptions — their names come from `listNames()`, i.e.
|
|
4024
|
+
* from the tree itself, never from caller input.
|
|
4025
|
+
*/
|
|
3986
4026
|
badName(name) {
|
|
3987
4027
|
const normalized = name.trim();
|
|
3988
4028
|
if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return `Invalid skill name "${normalized}". Use lowercase letters, digits, and hyphens (<= ${this.limits.maxNameLength}).`;
|
|
3989
4029
|
return null;
|
|
3990
4030
|
}
|
|
4031
|
+
/**
|
|
4032
|
+
* Refusal reason for a write to `rawName`, or null when the write may
|
|
4033
|
+
* proceed: a protection marker on the directory, or an invalid name
|
|
4034
|
+
* (P1-1/v14 — the name guard lives HERE as well as at every entry point, so
|
|
4035
|
+
* no caller can build a path from an unvalidated name).
|
|
4036
|
+
*/
|
|
3991
4037
|
async writeProtection(rawName, origin = "foreground") {
|
|
3992
4038
|
const name = rawName.trim();
|
|
4039
|
+
const badName = this.badName(name);
|
|
4040
|
+
if (badName) return badName;
|
|
3993
4041
|
const dir = this.dirOf(name);
|
|
3994
4042
|
for (const marker of ["bundled", "hub-installed"]) if (await this.io.exists(markerPath(dir, marker))) return marker;
|
|
3995
4043
|
if (origin === "background_review" && await this.io.exists(markerPath(dir, "pinned"))) return "pinned";
|
|
@@ -3997,6 +4045,8 @@ var SkillLibrary = class {
|
|
|
3997
4045
|
}
|
|
3998
4046
|
async deleteProtection(rawName, options = {}) {
|
|
3999
4047
|
const name = rawName.trim();
|
|
4048
|
+
const badName = this.badName(name);
|
|
4049
|
+
if (badName) return badName;
|
|
4000
4050
|
const dir = this.dirOf(name);
|
|
4001
4051
|
const markers = options.allowBundled ? ["hub-installed", "pinned"] : [
|
|
4002
4052
|
"bundled",
|
|
@@ -4008,6 +4058,7 @@ var SkillLibrary = class {
|
|
|
4008
4058
|
}
|
|
4009
4059
|
async isManaged(rawName) {
|
|
4010
4060
|
const name = rawName.trim();
|
|
4061
|
+
if (this.badName(name) !== null) return false;
|
|
4011
4062
|
const dir = this.dirOf(name);
|
|
4012
4063
|
return await this.io.exists(markerPath(dir, "hermes-managed"));
|
|
4013
4064
|
}
|
|
@@ -4051,7 +4102,9 @@ var SkillLibrary = class {
|
|
|
4051
4102
|
* Used by the maintenance enrichment (011 §7) and probe reads.
|
|
4052
4103
|
*/
|
|
4053
4104
|
async listSupportFiles(rawName) {
|
|
4054
|
-
const
|
|
4105
|
+
const name = rawName.trim();
|
|
4106
|
+
if (this.badName(name) !== null) return [];
|
|
4107
|
+
const dir = this.dirOf(name);
|
|
4055
4108
|
let entries;
|
|
4056
4109
|
try {
|
|
4057
4110
|
entries = await this.io.list(dir);
|
|
@@ -4295,6 +4348,11 @@ var SkillLibrary = class {
|
|
|
4295
4348
|
return await this.serial(() => this.patchCore(name, oldString, newString, filePath, replaceAll, origin));
|
|
4296
4349
|
}
|
|
4297
4350
|
async patchCore(name, oldString, newString, filePath, replaceAll, origin) {
|
|
4351
|
+
const badName = this.badName(name);
|
|
4352
|
+
if (badName) return {
|
|
4353
|
+
ok: false,
|
|
4354
|
+
message: badName
|
|
4355
|
+
};
|
|
4298
4356
|
const dir = this.dirOf(name);
|
|
4299
4357
|
const skillMd = join(dir, "SKILL.md");
|
|
4300
4358
|
if (!await this.io.exists(skillMd)) return {
|
|
@@ -4333,6 +4391,13 @@ var SkillLibrary = class {
|
|
|
4333
4391
|
},
|
|
4334
4392
|
write: null
|
|
4335
4393
|
};
|
|
4394
|
+
if (oldString === "" || trimPatternBoundaries(oldString) === "") return {
|
|
4395
|
+
result: {
|
|
4396
|
+
ok: false,
|
|
4397
|
+
message: `old_string is empty (or whitespace-only) — a patch of "${name}/${patchLabel}" needs an anchor; use update for a full rewrite.`
|
|
4398
|
+
},
|
|
4399
|
+
write: null
|
|
4400
|
+
};
|
|
4336
4401
|
const patched = fuzzyPatch(md, oldString, newString, replaceAll);
|
|
4337
4402
|
if (patched === null) return {
|
|
4338
4403
|
result: {
|
|
@@ -4499,7 +4564,9 @@ var SkillLibrary = class {
|
|
|
4499
4564
|
}
|
|
4500
4565
|
}
|
|
4501
4566
|
const reason = options.reason ?? (options.absorbedInto ? `Consolidated into ${options.absorbedInto}` : "Archived by self-evolution curator");
|
|
4502
|
-
|
|
4567
|
+
try {
|
|
4568
|
+
await this.io.writeText(join(dest, ".archive-reason"), `${(/* @__PURE__ */ new Date()).toISOString()}: ${reason}\n`);
|
|
4569
|
+
} catch {}
|
|
4503
4570
|
await this.audit(name, "archive", md, null, reason);
|
|
4504
4571
|
this.notifyMutation({
|
|
4505
4572
|
action: "archive",
|
|
@@ -5256,4 +5323,4 @@ var SkillLibrary = class {
|
|
|
5256
5323
|
}
|
|
5257
5324
|
};
|
|
5258
5325
|
//#endregion
|
|
5259
|
-
export { AUTHORING_DESCRIPTION_BAR, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION,
|
|
5326
|
+
export { AUTHORING_DESCRIPTION_BAR, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FORBIDDEN_CONTROL_KEYS, HEALTH_STAMP_RE, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, PATTERN_OVERLAP, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, QUALITY_WEIGHTS, RESTRUCTURE_TARGET_RE, SKILLS_GUIDANCE, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, THREAT_EXEMPT_HINT, advanceReview, allowRowCollisions, appendEvolutionEvent, applyCuratorFields, applyCuratorLifecycleFields, applyCuratorMetaFields, assessStructureHealth, authoringFeedback, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, clampedNumber, composePresetComposition, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, createGateSet, duplicateHeadings, emptyRecord, evaluateThreat, eventsFile, evolutionHome, evolutionIoAdapter, evolutionRoot, findDriftSignal, foldCuratorFields, foldTurn, frontmatterBlock, frontmatterYamlUnsafeValues, getRecord, latestActivityAt, lifecycleCandidate, listEventArchives, loadMutations, loadSuppressedNames, loadUsage, makeSerialQueue, markAgentCreated, markerEntryName, memoryRoot, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, observeEvent, overlongLines, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, pendingSelfCleanup, readEvolutionEvents, readEvolutionTimeline, recordMutation, redactSecrets, relatedSkillNames, renameWithRetry, renderCuratorReportMarkdown, resolveOrigins, resolveSkillsRoot, retainEventArchives, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, transactIo, updateSuppressedNames, usageFile, usageObserved, validateFrontmatter, verifyPromptBundle, writeDurableTmp, yamlPlainScalarNeedsQuotes };
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -54,6 +54,13 @@ export declare const DEFAULT_USER_CHAR_LIMIT = 1375;
|
|
|
54
54
|
/** Consolidation-failure backoff cap, shared by MemoryStore and memory-files' Config default. */
|
|
55
55
|
export declare const DEFAULT_CONSOLIDATION_FAILURES = 3;
|
|
56
56
|
export declare const DEFAULT_SKILL_CONTENT_CHARS = 100000;
|
|
57
|
+
/** P3-19 (v14): defaults that were written twice (schema `.default()` AND the
|
|
58
|
+
* clamp fallback literal) now have one home per value. */
|
|
59
|
+
export declare const DEFAULT_REVIEW_TIMEOUT_MS = 120000;
|
|
60
|
+
export declare const DEFAULT_REVIEW_CONTEXT_MESSAGES = 60;
|
|
61
|
+
export declare const DEFAULT_REVIEW_MESSAGE_CHARS = 2000;
|
|
62
|
+
export declare const DEFAULT_CURATOR_BOOT_GRACE_SECONDS = 10;
|
|
63
|
+
export declare const DEFAULT_CURATOR_REVIEW_MAX_TOKENS = 2048;
|
|
57
64
|
/** 0.3.17 (S3.10, T-1): control-plane fields a model-facing write call may
|
|
58
65
|
* never carry — single source for plan-validator, evolution-policy and the
|
|
59
66
|
* threat scanner (they used to each hardcode the list). */
|
package/lib/types/env.d.ts
CHANGED
|
@@ -7,10 +7,14 @@
|
|
|
7
7
|
* reads inside patch YAML `!!js` expressions (session-query path/openAt) stay
|
|
8
8
|
* in the profile config evaluation — they are NOT migrated (they are resolved
|
|
9
9
|
* at cordis config time, not plugin time) but are documented in the README
|
|
10
|
-
* env table.
|
|
10
|
+
* env table. `EVOLUTION_SCOPE` is read by the source installers only
|
|
11
|
+
* (`packages/scripts/install-layered.mjs`, `packages/test-support/row-contract.ts`),
|
|
12
|
+
* never by plugin runtime code.
|
|
13
|
+
*
|
|
14
|
+
* v14 P3-1: the former `EVOLUTION_ENV_KEYS` export was deleted — nothing read
|
|
15
|
+
* it, so the "generated env reference" it claimed to source was never
|
|
16
|
+
* generated (the README table is maintained by hand and now lists every key).
|
|
11
17
|
*/
|
|
12
|
-
/** Plugin-side DSH_EVOLUTION_* keys (config-layer keys are documented separately). */
|
|
13
|
-
export declare const EVOLUTION_ENV_KEYS: readonly ["DSH_EVOLUTION_ALLOW_ROW_COLLISIONS"];
|
|
14
18
|
/** N-5 escape: `DSH_EVOLUTION_ALLOW_ROW_COLLISIONS=1` downgrades a delta-row
|
|
15
19
|
* collision from fail-loud to warn+keep-both. Any other value (including an
|
|
16
20
|
* EMPTY/whitespace string — a set-but-unset variable) keeps the fail-loud. */
|
|
@@ -47,7 +47,13 @@ export declare const HEALTH_STAMP_RE: RegExp;
|
|
|
47
47
|
*/
|
|
48
48
|
export declare const MIN_STAMP_BODY_CHARS = 2000;
|
|
49
49
|
export type SkillHealthVerdict = 'healthy' | 'warn' | 'needs-restructure';
|
|
50
|
-
/** Facts a caller already has; assessors never do IO.
|
|
50
|
+
/** Facts a caller already has; assessors never do IO.
|
|
51
|
+
*
|
|
52
|
+
* `bodyChars`/`bodyText` are the WHOLE `SKILL.md` text as read from disk
|
|
53
|
+
* (frontmatter included), matching both callers: `SkillLibrary.assessHealth`
|
|
54
|
+
* and `snapshotFromLibrary` feed `read()` verbatim. The field names predate
|
|
55
|
+
* that convention; the thresholds are calibrated against the whole file, so a
|
|
56
|
+
* caller must not strip frontmatter before measuring. */
|
|
51
57
|
export interface SkillHealthSnapshot {
|
|
52
58
|
skillName: string;
|
|
53
59
|
bodyChars: number;
|
|
@@ -280,8 +280,21 @@ export declare class SkillLibrary {
|
|
|
280
280
|
|
|
281
281
|
*/
|
|
282
282
|
private dirOf;
|
|
283
|
-
/**
|
|
283
|
+
/**
|
|
284
|
+
* Name-format guard for every path-building entry point (P1-1/v14 closed the
|
|
285
|
+
* one gap: `patch`). Write paths, protection probes and support-file
|
|
286
|
+
* enumeration all call it before `dirOf`, so no directory path is ever built
|
|
287
|
+
* from a name that could escape the skills root. `list()` and `snapshotAll()`
|
|
288
|
+
* are the deliberate exceptions — their names come from `listNames()`, i.e.
|
|
289
|
+
* from the tree itself, never from caller input.
|
|
290
|
+
*/
|
|
284
291
|
private badName;
|
|
292
|
+
/**
|
|
293
|
+
* Refusal reason for a write to `rawName`, or null when the write may
|
|
294
|
+
* proceed: a protection marker on the directory, or an invalid name
|
|
295
|
+
* (P1-1/v14 — the name guard lives HERE as well as at every entry point, so
|
|
296
|
+
* no caller can build a path from an unvalidated name).
|
|
297
|
+
*/
|
|
285
298
|
writeProtection(rawName: string, origin?: WriteOrigin): Promise<string | null>;
|
|
286
299
|
deleteProtection(rawName: string, options?: {
|
|
287
300
|
allowBundled?: boolean | undefined;
|
package/lib/types/threats.d.ts
CHANGED
|
@@ -52,9 +52,11 @@ export declare function scanContentThreats(text: string, maxScanChars?: number,
|
|
|
52
52
|
* V10-03 (P2-18): suffix the SkillLibrary/MemoryStore write gates append to a
|
|
53
53
|
* block message — the hit label is already embedded by scanContentThreats /
|
|
54
54
|
* scanMemoryThreats, this names the deployable self-heal path so the model
|
|
55
|
-
* (or operator) can allowlist a known-benign label.
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* (or operator) can allowlist a known-benign label. P2-4 (v14): the
|
|
56
|
+
* evolution-threat guard channel now carries `threatExemptLabels` too, so its
|
|
57
|
+
* block message (which embeds the same exemption sentence) is accurate there
|
|
58
|
+
* as well; this suffix stays store-side because the guard returns the scan
|
|
59
|
+
* message verbatim.
|
|
58
60
|
*/
|
|
59
61
|
export declare const THREAT_EXEMPT_HINT = " If this is a legitimate false positive, the deployment can allow its label via the threatExemptLabels store option.";
|
|
60
62
|
//# sourceMappingURL=threats.d.ts.map
|
package/lib/types/usage.d.ts
CHANGED
|
@@ -76,7 +76,9 @@ export declare function foldCuratorFields(disk: UsageMap, curated: UsageMap, sta
|
|
|
76
76
|
/** Whole-file usage write (V6-37, 0.3.37): this is the ONE path that bypasses
|
|
77
77
|
* the malformed-defense and the transact lock — prefer `mutateUsage` for any
|
|
78
78
|
* read-modify-write so a concurrent writer cannot lose its update and a
|
|
79
|
-
* malformed sidecar stays recoverable. Kept for fixture/test seeding.
|
|
79
|
+
* malformed sidecar stays recoverable. Kept for fixture/test seeding.
|
|
80
|
+
* @internal P3-16 (v14): no production caller (verified by grep); exported for
|
|
81
|
+
* the family's tests only. Do not use it to write the sidecar in new code. */
|
|
80
82
|
export declare function saveUsage(root: string, map: UsageMap, io?: EvolutionIoLike): Promise<void>;
|
|
81
83
|
export declare function getRecord(map: UsageMap, name: string): UsageRecord;
|
|
82
84
|
export declare function bumpView(map: UsageMap, name: string, when?: Date): void;
|
package/package.json
CHANGED