@lmzhen/dsh-evolution-core 0.3.16 → 0.3.18
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 +123 -24
- package/lib/types/constants.d.ts +7 -0
- package/lib/types/events.d.ts +12 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/io.d.ts +8 -0
- package/lib/types/serial.d.ts +10 -0
- package/lib/types/skill-health.d.ts +7 -0
- package/lib/types/skill-store.d.ts +11 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -45,6 +45,10 @@ function evolutionIoAdapter(provider) {
|
|
|
45
45
|
isSymlink: (path) => {
|
|
46
46
|
const io = provider();
|
|
47
47
|
return io.isSymlink ? io.isSymlink(path) : Promise.resolve(null);
|
|
48
|
+
},
|
|
49
|
+
mtime: (path) => {
|
|
50
|
+
const io = provider();
|
|
51
|
+
return io.mtime ? io.mtime(path) : Promise.resolve(null);
|
|
48
52
|
}
|
|
49
53
|
};
|
|
50
54
|
}
|
|
@@ -64,45 +68,81 @@ function nodeEvolutionIo() {
|
|
|
64
68
|
};
|
|
65
69
|
/**
|
|
66
70
|
* Cross-process write lock (claw `withFileLock` parity): an O_EXCL lock file
|
|
67
|
-
* guards the atomic write. A >
|
|
71
|
+
* guards the atomic write. A >1s-old lock is taken over ONLY after probing
|
|
68
72
|
* the holder pid it carries (rc.66): a LIVE holder is never stolen, so a
|
|
69
|
-
* slow writer no longer loses its lock to a peer at the
|
|
73
|
+
* slow writer no longer loses its lock to a peer at the threshold (the
|
|
70
74
|
* takeover is the only best-effort surface; the retry budget fails loud —
|
|
71
75
|
* rc.65 — instead of ever proceeding unlocked). Budget = 40 * 50ms (~2s,
|
|
72
76
|
* rc.69): 8-writer contention bursts on a loaded CI runner exceed 10
|
|
73
77
|
* attempts (500ms), and a fail-loud throw was observed instead of a clean
|
|
74
78
|
* serialization.
|
|
79
|
+
* 0.3.17 (E-8): acquisition and the task are now SEPARATE try blocks — a
|
|
80
|
+
* task error (win32 rename/EBUSY surfaces as EPERM) used to be mistaken for
|
|
81
|
+
* lock contention, retried up to 40x and finally reported as
|
|
82
|
+
* "could not acquire write lock" while the real cause was hidden.
|
|
83
|
+
* 0.3.17 (E-8a): the takeover threshold (1000ms) now fits INSIDE the ~2s
|
|
84
|
+
* retry budget (budget >= 2 x threshold), so a dead holder's lock is
|
|
85
|
+
* actually recoverable within one budget instead of being arithmetically
|
|
86
|
+
* unreachable.
|
|
75
87
|
*/
|
|
76
88
|
const withWriteLock = async (path, task) => {
|
|
77
89
|
const lock = `${path}.lock`;
|
|
78
|
-
for (let attempt = 0; attempt < 40; attempt += 1)
|
|
79
|
-
await writeFile(lock, String(process.pid), { flag: "wx" });
|
|
90
|
+
for (let attempt = 0; attempt < 40; attempt += 1) {
|
|
80
91
|
try {
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
} catch {}
|
|
96
|
-
continue;
|
|
92
|
+
await writeFile(lock, String(process.pid), { flag: "wx" });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
const code = error?.code;
|
|
95
|
+
if (code !== "EEXIST" && code !== "EPERM") throw error;
|
|
96
|
+
try {
|
|
97
|
+
const st = await stat(lock);
|
|
98
|
+
if (Date.now() - st.mtimeMs > 1e3) {
|
|
99
|
+
const holder = Number(await readFile(lock, "utf8").catch(() => ""));
|
|
100
|
+
if (!(Number.isInteger(holder) && holder > 0 && isAlive(holder))) {
|
|
101
|
+
try {
|
|
102
|
+
await rm(lock, { force: true });
|
|
103
|
+
} catch {}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
97
106
|
}
|
|
107
|
+
} catch {
|
|
108
|
+
continue;
|
|
98
109
|
}
|
|
99
|
-
|
|
110
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
100
111
|
continue;
|
|
101
112
|
}
|
|
102
|
-
|
|
113
|
+
try {
|
|
114
|
+
return await task();
|
|
115
|
+
} finally {
|
|
116
|
+
await rm(lock, { force: true }).catch(() => {});
|
|
117
|
+
}
|
|
103
118
|
}
|
|
104
119
|
throw new Error(`could not acquire write lock for ${path} after 40 attempts`);
|
|
105
120
|
};
|
|
121
|
+
/** 0.3.17 (E-8b): sweep tmp files a crashed writer left behind — same
|
|
122
|
+
* `<target>.<pid>.<rand>.tmp` shape, older than 1h AND held by a dead pid.
|
|
123
|
+
* Lazy: only the directory a write is about to touch gets swept, once per
|
|
124
|
+
* write, inside the write lock. */
|
|
125
|
+
const sweepStaleTmps = async (path) => {
|
|
126
|
+
const dir = dirname(path);
|
|
127
|
+
const base = basename(path);
|
|
128
|
+
let entries;
|
|
129
|
+
try {
|
|
130
|
+
entries = await readdir(dir);
|
|
131
|
+
} catch {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const prefix = `${base}.`;
|
|
135
|
+
for (const name of entries) {
|
|
136
|
+
if (!name.startsWith(prefix) || !name.endsWith(".tmp")) continue;
|
|
137
|
+
const tmpPath = join(dir, name);
|
|
138
|
+
const holder = Number(name.slice(prefix.length, name.length - 4).split(".")[0] ?? "");
|
|
139
|
+
try {
|
|
140
|
+
const st = await stat(tmpPath);
|
|
141
|
+
const deadHolder = !Number.isInteger(holder) || holder <= 0 || !isAlive(holder);
|
|
142
|
+
if (Date.now() - st.mtimeMs > 36e5 && deadHolder) await rm(tmpPath, { force: true });
|
|
143
|
+
} catch {}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
106
146
|
return {
|
|
107
147
|
async readText(path) {
|
|
108
148
|
try {
|
|
@@ -115,6 +155,7 @@ function nodeEvolutionIo() {
|
|
|
115
155
|
async writeText(path, content) {
|
|
116
156
|
await mkdir(dirname(path), { recursive: true });
|
|
117
157
|
await withWriteLock(path, async () => {
|
|
158
|
+
await sweepStaleTmps(path);
|
|
118
159
|
const tmp = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
119
160
|
await writeFile(tmp, content, "utf8");
|
|
120
161
|
await rename(tmp, path);
|
|
@@ -123,6 +164,7 @@ function nodeEvolutionIo() {
|
|
|
123
164
|
async transact(path, task) {
|
|
124
165
|
await mkdir(dirname(path), { recursive: true });
|
|
125
166
|
await withWriteLock(path, async () => {
|
|
167
|
+
await sweepStaleTmps(path);
|
|
126
168
|
let current;
|
|
127
169
|
try {
|
|
128
170
|
current = await readFile(path, "utf8");
|
|
@@ -188,6 +230,14 @@ function nodeEvolutionIo() {
|
|
|
188
230
|
} catch {
|
|
189
231
|
return null;
|
|
190
232
|
}
|
|
233
|
+
},
|
|
234
|
+
async mtime(path) {
|
|
235
|
+
try {
|
|
236
|
+
return (await stat(path)).mtimeMs;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (isMissing(error)) return null;
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
191
241
|
}
|
|
192
242
|
};
|
|
193
243
|
}
|
|
@@ -494,6 +544,19 @@ const DEFAULT_USER_CHAR_LIMIT = 1375;
|
|
|
494
544
|
/** Consolidation-failure backoff cap, shared by MemoryStore and memory-files' Config default. */
|
|
495
545
|
const DEFAULT_CONSOLIDATION_FAILURES = 3;
|
|
496
546
|
const DEFAULT_SKILL_CONTENT_CHARS = 1e5;
|
|
547
|
+
/** 0.3.17 (S3.10, T-1): control-plane fields a model-facing write call may
|
|
548
|
+
* never carry — single source for plan-validator, evolution-policy and the
|
|
549
|
+
* threat scanner (they used to each hardcode the list). */
|
|
550
|
+
const FORBIDDEN_CONTROL_KEYS = [
|
|
551
|
+
"policy",
|
|
552
|
+
"threshold",
|
|
553
|
+
"prompt_hash",
|
|
554
|
+
"model_route",
|
|
555
|
+
"evolution_config"
|
|
556
|
+
];
|
|
557
|
+
/** 0.3.17 (S3.10): the model-facing write tools the policy guard and threat
|
|
558
|
+
* scanner cover. */
|
|
559
|
+
const EVOLUTION_WRITE_TOOLS = ["memory", "skill_manage"];
|
|
497
560
|
/** Hermes authoring quality bar for descriptions (the 60-char Rule). The
|
|
498
561
|
* platform's own index limit stays in validateFrontmatter; this bar is the
|
|
499
562
|
* target the authoring standard names, enforced as ADVISORY feedback.
|
|
@@ -2362,6 +2425,24 @@ function redactSecrets(text) {
|
|
|
2362
2425
|
return out;
|
|
2363
2426
|
}
|
|
2364
2427
|
//#endregion
|
|
2428
|
+
//#region lib/types/serial.js
|
|
2429
|
+
/**
|
|
2430
|
+
* A process-local serial task queue: each task starts only after the previous
|
|
2431
|
+
* one settles (success or failure), so read-modify-write sequences that share
|
|
2432
|
+
* one file never interleave inside this process. The durable cross-process
|
|
2433
|
+
* serialization layer is the IO backend's transact lock; this chain is the
|
|
2434
|
+
* second layer (0.3.17 S2.8, T-1: the shape was duplicated in state-json and
|
|
2435
|
+
* memory-files — one factory now).
|
|
2436
|
+
*/
|
|
2437
|
+
function makeSerialQueue() {
|
|
2438
|
+
let chain = Promise.resolve();
|
|
2439
|
+
return (task) => {
|
|
2440
|
+
const run = chain.then(task, task);
|
|
2441
|
+
chain = run.then(() => void 0, () => void 0);
|
|
2442
|
+
return run;
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
//#endregion
|
|
2365
2446
|
//#region lib/types/skill-health.js
|
|
2366
2447
|
/**
|
|
2367
2448
|
* Skill structure-health domain (rc.73 A1, 008 design): a SECOND assessment
|
|
@@ -2400,7 +2481,7 @@ function assessStructureHealth(snapshot, thresholds = DEFAULT_HEALTH_THRESHOLDS)
|
|
|
2400
2481
|
const needs = snapshot.bodyChars >= thresholds.softBodyChars * 2;
|
|
2401
2482
|
if (needs) reasons.push(`body ${snapshot.bodyChars} chars is >= 2x the soft limit (${thresholds.softBodyChars}) — consider splitting or offloading`);
|
|
2402
2483
|
else if (snapshot.bodyChars >= thresholds.softBodyChars) reasons.push(`body ${snapshot.bodyChars} chars above the soft limit (${thresholds.softBodyChars})`);
|
|
2403
|
-
if (snapshot.bodyText && snapshot.bodyChars >=
|
|
2484
|
+
if (snapshot.bodyText && snapshot.bodyChars >= 2e3) {
|
|
2404
2485
|
const kb = Math.max(1, snapshot.bodyChars / 1024);
|
|
2405
2486
|
dims.stampDensityPerKb = (snapshot.bodyText.match(HEALTH_STAMP_RE) ?? []).length / kb;
|
|
2406
2487
|
if (dims.stampDensityPerKb >= thresholds.stampDensityPerKb) reasons.push(`stamp density ${dims.stampDensityPerKb.toFixed(1)}/KB (rc/sha/date lines — log-like content in the body)`);
|
|
@@ -2683,6 +2764,14 @@ const SNAPSHOT_EXTRA_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
|
|
|
2683
2764
|
function skillsRoot(env = process.env) {
|
|
2684
2765
|
return join(env.DSH_HOME || join(homedir(), ".dsh"), "skills");
|
|
2685
2766
|
}
|
|
2767
|
+
/** 0.3.18 (S4.1, E-30): the ONE root resolution for every member that reads
|
|
2768
|
+
* the skills tree — tool-skill-manage / evolution-skill-catalog / skill-usage
|
|
2769
|
+
* / evolution-learning-graph used to each resolve `config.root || skillsRoot()`
|
|
2770
|
+
* (and the graph ignored config entirely). Empty/whitespace config falls
|
|
2771
|
+
* through to the default; callers pass their raw Config. */
|
|
2772
|
+
function resolveSkillsRoot(config = {}) {
|
|
2773
|
+
return (config.root ?? "").trim() || skillsRoot();
|
|
2774
|
+
}
|
|
2686
2775
|
/**
|
|
2687
2776
|
* Map a requesting session onto the two origin surfaces (rc.44 plan M2-2.3):
|
|
2688
2777
|
* the APPROVAL surface treats every delegated subagent as the autonomous
|
|
@@ -3511,6 +3600,12 @@ var SkillLibrary = class {
|
|
|
3511
3600
|
ok: false,
|
|
3512
3601
|
message: threat
|
|
3513
3602
|
};
|
|
3603
|
+
if (writeContent.trimEnd() + "\n" === md) return {
|
|
3604
|
+
ok: true,
|
|
3605
|
+
message: `Skill "${name}" unchanged: old_string already equals the replacement (${patchLabel}); nothing written.`,
|
|
3606
|
+
noop: true,
|
|
3607
|
+
path: dir
|
|
3608
|
+
};
|
|
3514
3609
|
await this.io.writeText(target, writeContent.trimEnd() + "\n");
|
|
3515
3610
|
await this.audit(name, "patch", md, writeContent, `patched ${patchLabel}`);
|
|
3516
3611
|
this.notifyMutation({
|
|
@@ -3544,6 +3639,10 @@ var SkillLibrary = class {
|
|
|
3544
3639
|
message: `Skill "${name}" is protected (${protection}).`
|
|
3545
3640
|
};
|
|
3546
3641
|
if (options.absorbedInto) {
|
|
3642
|
+
if (options.absorbedInto.trim() === name) return {
|
|
3643
|
+
ok: false,
|
|
3644
|
+
message: "absorbed_into cannot be the skill being archived (cannot absorb into itself)."
|
|
3645
|
+
};
|
|
3547
3646
|
if (!await this.io.readText(join(this.dirOf(options.absorbedInto), "SKILL.md"))) return {
|
|
3548
3647
|
ok: false,
|
|
3549
3648
|
message: `absorbed_into="${options.absorbedInto}" does not exist.`
|
|
@@ -4301,4 +4400,4 @@ function evolutionHome(env = process.env) {
|
|
|
4301
4400
|
return join(env.DSH_HOME || join(homedir(), ".dsh"), "evolution");
|
|
4302
4401
|
}
|
|
4303
4402
|
//#endregion
|
|
4304
|
-
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, EvolutionGateSet, HEALTH_STAMP_RE, LOW_QUALITY_THRESHOLD, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MUTATIONS_FILE_VERSION, MemoryStore, 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, advanceReview, appendEvolutionEvent, applyCuratorFields, applyCuratorLifecycleFields, applyCuratorMetaFields, assessStructureHealth, authoringFeedback, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, composePresetComposition, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, createGateSet, duplicateHeadings, emptyRecord, evaluateThreat, eventsFile, evolutionHome, evolutionIoAdapter, findDriftSignal, foldCuratorFields, foldTurn, frontmatterBlock, frontmatterYamlUnsafeValues, getRecord, latestActivityAt, lifecycleCandidate, listEventArchives, loadMutations, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, observeEvent, overlongLines, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, readEvolutionEvents, readEvolutionTimeline, recordMutation, redactSecrets, relatedSkillNames, renderCuratorReportMarkdown, resolveOrigins, retainEventArchives, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, transactIo, updateSuppressedNames, usageFile, usageObserved, validateFrontmatter, verifyPromptBundle, yamlPlainScalarNeedsQuotes };
|
|
4403
|
+
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, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FORBIDDEN_CONTROL_KEYS, HEALTH_STAMP_RE, LOW_QUALITY_THRESHOLD, 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, 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, advanceReview, appendEvolutionEvent, applyCuratorFields, applyCuratorLifecycleFields, applyCuratorMetaFields, assessStructureHealth, authoringFeedback, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, composePresetComposition, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, createGateSet, duplicateHeadings, emptyRecord, evaluateThreat, eventsFile, evolutionHome, evolutionIoAdapter, findDriftSignal, foldCuratorFields, foldTurn, frontmatterBlock, frontmatterYamlUnsafeValues, getRecord, latestActivityAt, lifecycleCandidate, listEventArchives, loadMutations, loadSuppressedNames, loadUsage, makeSerialQueue, markAgentCreated, memoryRoot, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, observeEvent, overlongLines, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, readEvolutionEvents, readEvolutionTimeline, recordMutation, redactSecrets, relatedSkillNames, renderCuratorReportMarkdown, resolveOrigins, resolveSkillsRoot, retainEventArchives, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, transactIo, updateSuppressedNames, usageFile, usageObserved, validateFrontmatter, verifyPromptBundle, yamlPlainScalarNeedsQuotes };
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -50,6 +50,13 @@ export declare const DEFAULT_USER_CHAR_LIMIT = 1375;
|
|
|
50
50
|
/** Consolidation-failure backoff cap, shared by MemoryStore and memory-files' Config default. */
|
|
51
51
|
export declare const DEFAULT_CONSOLIDATION_FAILURES = 3;
|
|
52
52
|
export declare const DEFAULT_SKILL_CONTENT_CHARS = 100000;
|
|
53
|
+
/** 0.3.17 (S3.10, T-1): control-plane fields a model-facing write call may
|
|
54
|
+
* never carry — single source for plan-validator, evolution-policy and the
|
|
55
|
+
* threat scanner (they used to each hardcode the list). */
|
|
56
|
+
export declare const FORBIDDEN_CONTROL_KEYS: readonly ["policy", "threshold", "prompt_hash", "model_route", "evolution_config"];
|
|
57
|
+
/** 0.3.17 (S3.10): the model-facing write tools the policy guard and threat
|
|
58
|
+
* scanner cover. */
|
|
59
|
+
export declare const EVOLUTION_WRITE_TOOLS: readonly ["memory", "skill_manage"];
|
|
53
60
|
/** Hermes authoring quality bar for descriptions (the 60-char Rule). The
|
|
54
61
|
* platform's own index limit stays in validateFrontmatter; this bar is the
|
|
55
62
|
* target the authoring standard names, enforced as ADVISORY feedback.
|
package/lib/types/events.d.ts
CHANGED
|
@@ -42,11 +42,23 @@ export interface EvolutionSkillMutatedEvent {
|
|
|
42
42
|
file?: string;
|
|
43
43
|
archivedPath?: string;
|
|
44
44
|
}
|
|
45
|
+
/** 0.3.18 (E-6): a turn-end review pipeline failure was caught (never an
|
|
46
|
+
* unhandled rejection); this event lets operators/observability see it. The
|
|
47
|
+
* reason is already logged by the emitter — the event is a timestamped signal. */
|
|
48
|
+
export interface EvolutionReviewErrorEvent {
|
|
49
|
+
sessionId: string;
|
|
50
|
+
}
|
|
45
51
|
declare module '@deepseek-ai/cordis' {
|
|
46
52
|
interface Events {
|
|
47
53
|
'evolution/review-scheduled'(event: EvolutionReviewScheduledEvent): void;
|
|
48
54
|
'evolution/plan-applied'(event: EvolutionPlanAppliedEvent): void;
|
|
49
55
|
'evolution/skill-mutated'(event: EvolutionSkillMutatedEvent): void;
|
|
56
|
+
/** 0.3.18 (E-71): explicit catalog refresh request (`/evolution skills
|
|
57
|
+
* refresh`). Out-of-band tree edits (manual, git) may bypass the mutation
|
|
58
|
+
* event; listeners drop caches and invalidate downstream catalogs. No
|
|
59
|
+
* payload — it is a bare "re-read" signal, never a mutation record. */
|
|
60
|
+
'evolution/skills-refresh'(): void;
|
|
61
|
+
'evolution/review-error'(event: EvolutionReviewErrorEvent): void;
|
|
50
62
|
}
|
|
51
63
|
}
|
|
52
64
|
//# sourceMappingURL=events.d.ts.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export * from './preset-composition.ts';
|
|
|
19
19
|
export * from './prompts.ts';
|
|
20
20
|
export * from './quality.ts';
|
|
21
21
|
export * from './redact.ts';
|
|
22
|
+
export * from './serial.ts';
|
|
22
23
|
export * from './skill-health.ts';
|
|
23
24
|
export * from './signals.ts';
|
|
24
25
|
export * from './drift-signals.ts';
|
package/lib/types/io.d.ts
CHANGED
|
@@ -36,6 +36,14 @@ export interface EvolutionIoLike {
|
|
|
36
36
|
* the path does not exist). Consumers treat `null` as "let it through".
|
|
37
37
|
*/
|
|
38
38
|
isSymlink?(this: void, path: string): Promise<boolean | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Optional mtime-generation probe (0.3.18, E-71): the path's mtime in
|
|
41
|
+
* milliseconds since epoch, or `null` when unknown (unsupported backend,
|
|
42
|
+
* missing path, stat failure). Consumers use it as a cheap invalidation
|
|
43
|
+
* stamp for a cached directory listing; a backend without it keeps
|
|
44
|
+
* event-driven invalidation only.
|
|
45
|
+
*/
|
|
46
|
+
mtime?(this: void, path: string): Promise<number | null>;
|
|
39
47
|
}
|
|
40
48
|
/**
|
|
41
49
|
* Run `task` inside `io.transact` when the backend provides it; otherwise fall
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A process-local serial task queue: each task starts only after the previous
|
|
3
|
+
* one settles (success or failure), so read-modify-write sequences that share
|
|
4
|
+
* one file never interleave inside this process. The durable cross-process
|
|
5
|
+
* serialization layer is the IO backend's transact lock; this chain is the
|
|
6
|
+
* second layer (0.3.17 S2.8, T-1: the shape was duplicated in state-json and
|
|
7
|
+
* memory-files — one factory now).
|
|
8
|
+
*/
|
|
9
|
+
export declare function makeSerialQueue(): <T>(task: () => Promise<T>) => Promise<T>;
|
|
10
|
+
//# sourceMappingURL=serial.d.ts.map
|
|
@@ -24,6 +24,13 @@ export interface SkillHealthThresholds {
|
|
|
24
24
|
export declare const DEFAULT_HEALTH_THRESHOLDS: SkillHealthThresholds;
|
|
25
25
|
/** Stamp regex shared by health assessment and the maintenance probe (single source, 011). */
|
|
26
26
|
export declare const HEALTH_STAMP_RE: RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* Bodies below this size skip stamp-density assessment: a few dates or shas
|
|
29
|
+
* in a short body are ordinary documentation, not log-like content. With the
|
|
30
|
+
* 1KB density floor a 3-date sentence in a small skill measured 3.0/KB and
|
|
31
|
+
* warned on a perfectly healthy body (audit 2026-08-31 X1).
|
|
32
|
+
*/
|
|
33
|
+
export declare const MIN_STAMP_BODY_CHARS = 2000;
|
|
27
34
|
export type SkillHealthVerdict = 'healthy' | 'warn' | 'needs-restructure';
|
|
28
35
|
/** Facts a caller already has; assessors never do IO. */
|
|
29
36
|
export interface SkillHealthSnapshot {
|
|
@@ -32,6 +32,9 @@ export interface SkillActionResult {
|
|
|
32
32
|
/** Frontmatter keys auto-quoted for catalog-loadable YAML (0.3.11) — set
|
|
33
33
|
* only when the write path modified the block. */
|
|
34
34
|
normalizedFrontmatterFields?: string[];
|
|
35
|
+
/** 0.3.18 (E-68): patch produced byte-identical content (old===new) — no
|
|
36
|
+
* write, no audit, no mutation event; callers must not count a patch. */
|
|
37
|
+
noop?: boolean;
|
|
35
38
|
}
|
|
36
39
|
/**
|
|
37
40
|
* One section move of a restructure proposal (008 batch B): a body section
|
|
@@ -81,6 +84,14 @@ export interface ArchiveOptions {
|
|
|
81
84
|
allowBundled?: boolean;
|
|
82
85
|
}
|
|
83
86
|
export declare function skillsRoot(env?: NodeJS.ProcessEnv): string;
|
|
87
|
+
/** 0.3.18 (S4.1, E-30): the ONE root resolution for every member that reads
|
|
88
|
+
* the skills tree — tool-skill-manage / evolution-skill-catalog / skill-usage
|
|
89
|
+
* / evolution-learning-graph used to each resolve `config.root || skillsRoot()`
|
|
90
|
+
* (and the graph ignored config entirely). Empty/whitespace config falls
|
|
91
|
+
* through to the default; callers pass their raw Config. */
|
|
92
|
+
export declare function resolveSkillsRoot(config?: {
|
|
93
|
+
root?: string;
|
|
94
|
+
}): string;
|
|
84
95
|
/**
|
|
85
96
|
* Map a requesting session onto the two origin surfaces (rc.44 plan M2-2.3):
|
|
86
97
|
* the APPROVAL surface treats every delegated subagent as the autonomous
|
package/package.json
CHANGED