@lmzhen/dsh-evolution-core 0.25.7 → 0.25.9
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/README.md +7 -0
- package/lib/index.js +249 -47
- package/lib/types/curator.d.ts +12 -0
- package/lib/types/frontmatter.d.ts +6 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/skill-discovery.d.ts +19 -0
- package/lib/types/skill-history.d.ts +23 -0
- package/lib/types/skill-store.d.ts +1 -1
- package/lib/types/unit-members.d.ts +73 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,3 +95,10 @@ consumer — so the `@mode` target stays at six (decision 10, provisional: keep)
|
|
|
95
95
|
| `E-322` | a cross-field parameter constraint would be broken (the field, its floor and the attempted value are included) | raise the value to the named floor, or lower the sibling first |
|
|
96
96
|
| `E-319` | the confirmation before a create or bare delete went unanswered for `skillWriteConfirmTimeoutSeconds` (`skillWriteConfirm: 'timeout'`), so the write was cancelled | set `skillWriteConfirm` to `auto` (write without asking), keep `ask` (wait until answered), or raise the timeout |
|
|
97
97
|
Symptom → next step; the exact text of every code (`E-301`–`E-322`) is stored once in
|
|
98
|
+
|
|
99
|
+
## 单元与撤销(本包负责的契约)
|
|
100
|
+
|
|
101
|
+
- 本包持有单元契约的**类型与折叠**:SkillVersion.unitHead 两形态互斥(代表片 { moves, sources }/路标 { skill }),判别式固定为 moves !== undefined;读端折叠必须带上新字段,否则下一次写就丢。
|
|
102
|
+
- 成员解析与孤路标判定在 src/unit-members.ts:candidateSkills(代表片首位、去重保序)、resolveUnitMembersFromIndexes(只按已知 key、三向校验)、landmarkVerdict 与 staleRouteStubs(可跳 ⟺ 目标存在 ∧ 其索引仍持该 unitId;输出按 skill/version/owner 确定性排序)。
|
|
103
|
+
- 写作端的硬规则:kind 判 previous === null(不是 undefined);path 抄循环里既有的 rel;同一目标写两次时清单保留最后一次写入的 afterRef。
|
|
104
|
+
- 面向读者的完整说明见仓库 docs/units-and-undo.md。
|
package/lib/index.js
CHANGED
|
@@ -577,6 +577,34 @@ const MEMORY_GUIDANCE_SECTION_ORDER = 11e3;
|
|
|
577
577
|
/** Order of the `evolution-skills-guidance` section (last of the two). */
|
|
578
578
|
const SKILLS_GUIDANCE_SECTION_ORDER = 11100;
|
|
579
579
|
//#endregion
|
|
580
|
+
//#region lib/types/guards.js
|
|
581
|
+
/**
|
|
582
|
+
* The family's plain-object predicate, in ONE place (v46 G7.1 / audit A8).
|
|
583
|
+
*
|
|
584
|
+
* The audit found the same guard — "a non-null object that is not an array" — rewritten fifteen
|
|
585
|
+
* times across nine packages, four times inside core itself, and with two byte-identical `asRecord`
|
|
586
|
+
* copies in evolution-policy and evolution-threat. This module is the single definition; callers
|
|
587
|
+
* import it instead of spelling the clause again.
|
|
588
|
+
*
|
|
589
|
+
* @module @lmzhen/dsh-evolution-core
|
|
590
|
+
*/
|
|
591
|
+
/**
|
|
592
|
+
* Is this a non-null, non-array object (what JSON calls an object)?
|
|
593
|
+
* @param value - the value to test.
|
|
594
|
+
* @returns whether it is a plain object.
|
|
595
|
+
*/
|
|
596
|
+
function isRecord(value) {
|
|
597
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Read a value as a record, or undefined when it is not one.
|
|
601
|
+
* @param value - the value to read.
|
|
602
|
+
* @returns the value typed as a record, or undefined.
|
|
603
|
+
*/
|
|
604
|
+
function asRecord(value) {
|
|
605
|
+
return isRecord(value) ? value : void 0;
|
|
606
|
+
}
|
|
607
|
+
//#endregion
|
|
580
608
|
//#region lib/types/limits.js
|
|
581
609
|
/**
|
|
582
610
|
* Skill content limits: the byte/char budgets every write path validates against.
|
|
@@ -766,6 +794,7 @@ function strictFrontmatterValues(block) {
|
|
|
766
794
|
if (typeof loaded !== "object" || Array.isArray(loaded)) return null;
|
|
767
795
|
const values = /* @__PURE__ */ new Map();
|
|
768
796
|
const split = [];
|
|
797
|
+
let metadata;
|
|
769
798
|
for (const [key, value] of Object.entries(loaded)) {
|
|
770
799
|
if (typeof value !== "string" && PLATFORM_STRING_FIELDS.includes(key)) split.push({
|
|
771
800
|
key,
|
|
@@ -783,10 +812,15 @@ function strictFrontmatterValues(block) {
|
|
|
783
812
|
values.set(key, `[${value.map((item) => String(item)).join(", ")}]`);
|
|
784
813
|
continue;
|
|
785
814
|
}
|
|
815
|
+
if (key === "metadata" && isRecord(value)) {
|
|
816
|
+
metadata = value;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
786
819
|
}
|
|
787
820
|
return {
|
|
788
821
|
values,
|
|
789
|
-
split
|
|
822
|
+
split,
|
|
823
|
+
metadata
|
|
790
824
|
};
|
|
791
825
|
}
|
|
792
826
|
/**
|
|
@@ -858,7 +892,8 @@ function readFrontmatterBlock(content) {
|
|
|
858
892
|
body,
|
|
859
893
|
unsafeValues: unsafeFrontmatterEntries(found.block),
|
|
860
894
|
strictFailed: strict === null,
|
|
861
|
-
platformStringSplit: strict?.split ?? []
|
|
895
|
+
platformStringSplit: strict?.split ?? [],
|
|
896
|
+
metadata: strict?.metadata
|
|
862
897
|
};
|
|
863
898
|
}
|
|
864
899
|
/**
|
|
@@ -878,6 +913,7 @@ function parseFrontmatter(content) {
|
|
|
878
913
|
frontmatter: read.frontmatter,
|
|
879
914
|
body: read.body,
|
|
880
915
|
unsafeValues: read.unsafeValues,
|
|
916
|
+
metadata: read.metadata,
|
|
881
917
|
catalogInvalid: read.strictFailed || read.unsafeValues.length > 0 || read.platformStringSplit.length > 0,
|
|
882
918
|
platformStringSplit: read.platformStringSplit
|
|
883
919
|
};
|
|
@@ -1028,7 +1064,10 @@ function normalizeFrontmatter(content) {
|
|
|
1028
1064
|
function relatedSkillNames(content, exclude) {
|
|
1029
1065
|
const parsed = parseFrontmatter(content);
|
|
1030
1066
|
if (!parsed) return [];
|
|
1031
|
-
const
|
|
1067
|
+
const nestedHost = parsed.metadata?.["hermes"];
|
|
1068
|
+
const nestedRaw = isRecord(nestedHost) ? nestedHost["related_skills"] : void 0;
|
|
1069
|
+
const flat = parsed.frontmatter["related_skills"];
|
|
1070
|
+
const raw = typeof flat === "string" ? flat : Array.isArray(nestedRaw) ? `[${nestedRaw.map((item) => String(item)).join(", ")}]` : void 0;
|
|
1032
1071
|
if (typeof raw !== "string") return [];
|
|
1033
1072
|
const names = /* @__PURE__ */ new Set();
|
|
1034
1073
|
for (const match of Array.from(raw.matchAll(/(?<![A-Za-z0-9_-])[a-z0-9][a-z0-9-]*(?![A-Za-z0-9_-])/g))) {
|
|
@@ -1371,34 +1410,6 @@ function admissionVerdict(baseline, ruleId, thresholds = DEFAULT_ADMISSION) {
|
|
|
1371
1410
|
};
|
|
1372
1411
|
}
|
|
1373
1412
|
//#endregion
|
|
1374
|
-
//#region lib/types/guards.js
|
|
1375
|
-
/**
|
|
1376
|
-
* The family's plain-object predicate, in ONE place (v46 G7.1 / audit A8).
|
|
1377
|
-
*
|
|
1378
|
-
* The audit found the same guard — "a non-null object that is not an array" — rewritten fifteen
|
|
1379
|
-
* times across nine packages, four times inside core itself, and with two byte-identical `asRecord`
|
|
1380
|
-
* copies in evolution-policy and evolution-threat. This module is the single definition; callers
|
|
1381
|
-
* import it instead of spelling the clause again.
|
|
1382
|
-
*
|
|
1383
|
-
* @module @lmzhen/dsh-evolution-core
|
|
1384
|
-
*/
|
|
1385
|
-
/**
|
|
1386
|
-
* Is this a non-null, non-array object (what JSON calls an object)?
|
|
1387
|
-
* @param value - the value to test.
|
|
1388
|
-
* @returns whether it is a plain object.
|
|
1389
|
-
*/
|
|
1390
|
-
function isRecord(value) {
|
|
1391
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1392
|
-
}
|
|
1393
|
-
/**
|
|
1394
|
-
* Read a value as a record, or undefined when it is not one.
|
|
1395
|
-
* @param value - the value to read.
|
|
1396
|
-
* @returns the value typed as a record, or undefined.
|
|
1397
|
-
*/
|
|
1398
|
-
function asRecord(value) {
|
|
1399
|
-
return isRecord(value) ? value : void 0;
|
|
1400
|
-
}
|
|
1401
|
-
//#endregion
|
|
1402
1413
|
//#region lib/types/probe.js
|
|
1403
1414
|
function probePresent(value) {
|
|
1404
1415
|
return {
|
|
@@ -2491,6 +2502,7 @@ function renderCuratorReportMarkdown(report) {
|
|
|
2491
2502
|
...report.aborted === void 0 ? [] : [`- **Aborted**: ${report.aborted}`],
|
|
2492
2503
|
...report.unattributed === void 0 || report.unattributed.length === 0 ? [] : [`- **Unattributed errors**: ${report.unattributed.length}`],
|
|
2493
2504
|
...report.snapshotPath === void 0 ? [] : [`- **Snapshot**: ${report.snapshotPath}`],
|
|
2505
|
+
...report.routeStubs === void 0 ? [] : [`- **Stale route stubs**: ${report.routeStubs.stubs}`],
|
|
2494
2506
|
...report.llmReviewEnabled === void 0 ? [] : [`- **llmReview**: ${report.llmReviewEnabled}`],
|
|
2495
2507
|
...report.wouldPrune === void 0 ? [] : [report.wouldPrune === null ? "- **Would prune (archiveRetention=report)**: unknown (archive listing unreadable)" : `- **Would prune (archiveRetention=report)**: ${report.wouldPrune.length}`],
|
|
2496
2508
|
...report.nominationsWarnings === void 0 || report.nominationsWarnings.length === 0 ? [] : [`- **Nomination warnings**: ${report.nominationsWarnings.join("; ")}`]
|
|
@@ -2506,6 +2518,7 @@ function renderCuratorReportMarkdown(report) {
|
|
|
2506
2518
|
...section("Archived", report.archived.map((item) => `${item.name} (${item.reason})`)),
|
|
2507
2519
|
...section("Failed", report.failed.map((item) => `${item.name}: ${item.reason}`)),
|
|
2508
2520
|
...section("Unattributed", report.unattributed ?? []),
|
|
2521
|
+
...section("Stale route stubs", (report.routeStubs?.detail ?? []).map((stub) => stub.skill + " v" + String(stub.v) + " -> " + stub.owner + " (" + stub.reason + ")")),
|
|
2509
2522
|
...section("Stale candidates", report.staleCandidates),
|
|
2510
2523
|
...section("LLM nominations", report.llmNominations),
|
|
2511
2524
|
...section("Retention (kept, not deleted)", report.wouldPrune ?? []),
|
|
@@ -8717,6 +8730,8 @@ function readVersionEntry(entry) {
|
|
|
8717
8730
|
if (typeof candidate.chars !== "number") return null;
|
|
8718
8731
|
const summary = typeof candidate.summary === "string" && candidate.summary !== "" ? candidate.summary : void 0;
|
|
8719
8732
|
const path = typeof candidate.path === "string" && candidate.path !== "" ? candidate.path : void 0;
|
|
8733
|
+
const unitId = typeof candidate.unitId === "string" && candidate.unitId !== "" ? candidate.unitId : void 0;
|
|
8734
|
+
const unitHead = candidate.unitHead;
|
|
8720
8735
|
const base = {
|
|
8721
8736
|
v: candidate.v,
|
|
8722
8737
|
at: candidate.at,
|
|
@@ -8724,7 +8739,9 @@ function readVersionEntry(entry) {
|
|
|
8724
8739
|
hash: candidate.hash,
|
|
8725
8740
|
chars: candidate.chars,
|
|
8726
8741
|
...summary === void 0 ? {} : { summary },
|
|
8727
|
-
...path === void 0 ? {} : { path }
|
|
8742
|
+
...path === void 0 ? {} : { path },
|
|
8743
|
+
...unitId === void 0 ? {} : { unitId },
|
|
8744
|
+
...unitHead === void 0 ? {} : { unitHead }
|
|
8728
8745
|
};
|
|
8729
8746
|
return typeof candidate.beforeHash === "string" && candidate.beforeHash !== "" ? {
|
|
8730
8747
|
...base,
|
|
@@ -8796,11 +8813,14 @@ function nextHistoryIndex(versions, input, keep) {
|
|
|
8796
8813
|
action: BASELINE_ACTION,
|
|
8797
8814
|
hash: beforeHash,
|
|
8798
8815
|
chars: input.before?.length ?? 0,
|
|
8799
|
-
...pathField
|
|
8816
|
+
...pathField,
|
|
8817
|
+
...input.unitId === void 0 ? {} : { unitId: input.unitId }
|
|
8800
8818
|
});
|
|
8801
8819
|
const removalHash = afterHash === null ? beforeHash : null;
|
|
8802
8820
|
if (afterHash !== null) {
|
|
8803
8821
|
if (grown[grown.length - 1]?.hash !== afterHash) grown.push({
|
|
8822
|
+
...input.unitId === void 0 ? {} : { unitId: input.unitId },
|
|
8823
|
+
...input.unitHead === void 0 ? {} : { unitHead: input.unitHead },
|
|
8804
8824
|
v: nextVersion(grown),
|
|
8805
8825
|
at: input.at,
|
|
8806
8826
|
action: input.action,
|
|
@@ -8820,10 +8840,39 @@ function nextHistoryIndex(versions, input, keep) {
|
|
|
8820
8840
|
hash: removalHash,
|
|
8821
8841
|
chars: input.before?.length ?? 0,
|
|
8822
8842
|
...pathField,
|
|
8843
|
+
...input.unitId === void 0 ? {} : { unitId: input.unitId },
|
|
8844
|
+
...input.unitHead === void 0 ? {} : { unitHead: input.unitHead },
|
|
8823
8845
|
...input.summary === void 0 ? {} : { summary: input.summary }
|
|
8824
8846
|
});
|
|
8825
8847
|
}
|
|
8826
|
-
const
|
|
8848
|
+
const budget = clampKeep(keep);
|
|
8849
|
+
const groups = [];
|
|
8850
|
+
const byUnit = /* @__PURE__ */ new Map();
|
|
8851
|
+
for (const entry of grown) {
|
|
8852
|
+
const unit = entry.unitId;
|
|
8853
|
+
if (unit === void 0) {
|
|
8854
|
+
groups.push([entry]);
|
|
8855
|
+
continue;
|
|
8856
|
+
}
|
|
8857
|
+
const found = byUnit.get(unit);
|
|
8858
|
+
if (found !== void 0) {
|
|
8859
|
+
found.push(entry);
|
|
8860
|
+
continue;
|
|
8861
|
+
}
|
|
8862
|
+
const made = [entry];
|
|
8863
|
+
byUnit.set(unit, made);
|
|
8864
|
+
groups.push(made);
|
|
8865
|
+
}
|
|
8866
|
+
const keptIndexes = /* @__PURE__ */ new Set();
|
|
8867
|
+
let keptCount = 0;
|
|
8868
|
+
for (let i = groups.length - 1; i >= 0; i -= 1) {
|
|
8869
|
+
const group = groups[i];
|
|
8870
|
+
if (group === void 0) continue;
|
|
8871
|
+
if (keptCount > 0 && keptCount + group.length > budget) break;
|
|
8872
|
+
for (const entry of group) keptIndexes.add(grown.indexOf(entry));
|
|
8873
|
+
keptCount += group.length;
|
|
8874
|
+
}
|
|
8875
|
+
const trimmed = grown.filter((_, index) => keptIndexes.has(index));
|
|
8827
8876
|
const recorded = {};
|
|
8828
8877
|
if (beforeHash !== null) {
|
|
8829
8878
|
const v = lastVersionOf(trimmed, beforeHash);
|
|
@@ -9133,6 +9182,141 @@ function elapsedSince(at, nowMs) {
|
|
|
9133
9182
|
};
|
|
9134
9183
|
}
|
|
9135
9184
|
//#endregion
|
|
9185
|
+
//#region lib/types/unit-members.js
|
|
9186
|
+
/** Candidate skills: head first, then `sources[]` order, DEDUPLICATED (sources may name the head). */
|
|
9187
|
+
function candidateSkills(head) {
|
|
9188
|
+
const out = [head.skill];
|
|
9189
|
+
for (const source of head.sources) if (!out.includes(source.skill)) out.push(source.skill);
|
|
9190
|
+
return out;
|
|
9191
|
+
}
|
|
9192
|
+
/** @param indexes - skill name -> that skill's index text, or null when the index file is absent. */
|
|
9193
|
+
/** The `path` of one manifest move, or the empty string when the record carries no string one.
|
|
9194
|
+
* Kept as a function so the read never stringifies an object (the linter refuses
|
|
9195
|
+
* `String(unknown)` for exactly that reason) and every call site reads the same way. */
|
|
9196
|
+
function movePath(move) {
|
|
9197
|
+
const raw = move.path;
|
|
9198
|
+
return typeof raw === "string" ? raw : "";
|
|
9199
|
+
}
|
|
9200
|
+
function resolveUnitMembersFromIndexes(head, indexes, readEntries) {
|
|
9201
|
+
const members = [];
|
|
9202
|
+
const missing = [];
|
|
9203
|
+
for (const skill of candidateSkills(head)) {
|
|
9204
|
+
const text = indexes.get(skill);
|
|
9205
|
+
if (text === void 0 || text === null) {
|
|
9206
|
+
missing.push({
|
|
9207
|
+
skill,
|
|
9208
|
+
reason: "index-absent"
|
|
9209
|
+
});
|
|
9210
|
+
continue;
|
|
9211
|
+
}
|
|
9212
|
+
const entries = readEntries(text).filter((entry) => entry.unitId === head.unitId);
|
|
9213
|
+
if (entries.length === 0) {
|
|
9214
|
+
missing.push({
|
|
9215
|
+
skill,
|
|
9216
|
+
reason: "unit-absent"
|
|
9217
|
+
});
|
|
9218
|
+
continue;
|
|
9219
|
+
}
|
|
9220
|
+
members.push({
|
|
9221
|
+
skill,
|
|
9222
|
+
entries
|
|
9223
|
+
});
|
|
9224
|
+
}
|
|
9225
|
+
const conflicts = [];
|
|
9226
|
+
const manifest = head.unitHead?.moves ?? void 0;
|
|
9227
|
+
if (manifest !== void 0) {
|
|
9228
|
+
const shardPaths = [];
|
|
9229
|
+
for (const slice of members) for (const entry of slice.entries) for (const move of entry.unitHead?.moves ?? []) {
|
|
9230
|
+
const path = movePath(move);
|
|
9231
|
+
if (path !== "") shardPaths.push(path);
|
|
9232
|
+
}
|
|
9233
|
+
const wanted = manifest.map((move) => movePath(move)).filter((p) => p !== "");
|
|
9234
|
+
for (const path of wanted) if (!shardPaths.includes(path)) conflicts.push({
|
|
9235
|
+
kind: "missing",
|
|
9236
|
+
path
|
|
9237
|
+
});
|
|
9238
|
+
for (const path of shardPaths) if (!wanted.includes(path)) conflicts.push({
|
|
9239
|
+
kind: "foreign",
|
|
9240
|
+
path
|
|
9241
|
+
});
|
|
9242
|
+
for (const path of wanted) if (shardPaths.filter((p) => p === path).length > 1) conflicts.push({
|
|
9243
|
+
kind: "duplicated",
|
|
9244
|
+
path
|
|
9245
|
+
});
|
|
9246
|
+
}
|
|
9247
|
+
return {
|
|
9248
|
+
members,
|
|
9249
|
+
missing,
|
|
9250
|
+
conflicts
|
|
9251
|
+
};
|
|
9252
|
+
}
|
|
9253
|
+
/** A unit's members are trustworthy only when nothing is missing and nothing conflicts. */
|
|
9254
|
+
function unitMembersAreComplete(members) {
|
|
9255
|
+
return members.missing.length === 0 && members.conflicts.length === 0;
|
|
9256
|
+
}
|
|
9257
|
+
/**
|
|
9258
|
+
* The io shell (A19.2): open ONLY the known keys -- never enumerate a directory. A skill whose index
|
|
9259
|
+
* file is unreadable becomes `null` (index-absent), which the core then reports by name.
|
|
9260
|
+
*/
|
|
9261
|
+
async function resolveUnitMembers(root, io, head, indexPath, readEntries) {
|
|
9262
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
9263
|
+
for (const skill of candidateSkills(head)) {
|
|
9264
|
+
let text = null;
|
|
9265
|
+
try {
|
|
9266
|
+
text = await io.readText(indexPath(root, skill));
|
|
9267
|
+
} catch {
|
|
9268
|
+
text = null;
|
|
9269
|
+
}
|
|
9270
|
+
indexes.set(skill, text);
|
|
9271
|
+
}
|
|
9272
|
+
return resolveUnitMembersFromIndexes(head, indexes, (raw) => readEntries(raw));
|
|
9273
|
+
}
|
|
9274
|
+
/** G3b: a LANDMARK carries the owning skill and no manifest (the two forms never coexist). */
|
|
9275
|
+
function isLandmark(entry) {
|
|
9276
|
+
const head = entry.unitHead;
|
|
9277
|
+
return head !== void 0 && head.moves === void 0 && head.skill !== void 0;
|
|
9278
|
+
}
|
|
9279
|
+
/** @param entry - the entry being judged. @param ownerEntries - the owner skill's index, or null when
|
|
9280
|
+
* the owner has no readable index. */
|
|
9281
|
+
function landmarkVerdict(entry, ownerEntries) {
|
|
9282
|
+
const head = entry.unitHead;
|
|
9283
|
+
if ((isLandmark(entry) ? head?.skill : void 0) === void 0 || entry.unitId === void 0) return {
|
|
9284
|
+
jumpable: false,
|
|
9285
|
+
reason: "not-a-landmark"
|
|
9286
|
+
};
|
|
9287
|
+
if (ownerEntries === null) return {
|
|
9288
|
+
jumpable: false,
|
|
9289
|
+
reason: "owner-absent"
|
|
9290
|
+
};
|
|
9291
|
+
return ownerEntries.some((candidate) => candidate.unitId === entry.unitId) ? { jumpable: true } : {
|
|
9292
|
+
jumpable: false,
|
|
9293
|
+
reason: "unit-absent"
|
|
9294
|
+
};
|
|
9295
|
+
}
|
|
9296
|
+
/**
|
|
9297
|
+
* Scan a whole corpus (skill name -> index entries, or null when unreadable) for stale route stubs.
|
|
9298
|
+
* Sorted by (skill, version, owner): a function of the corpus, not of the caller's map order.
|
|
9299
|
+
*/
|
|
9300
|
+
function staleRouteStubs(indexes) {
|
|
9301
|
+
const out = [];
|
|
9302
|
+
for (const [skill, entries] of indexes) {
|
|
9303
|
+
if (entries === null) continue;
|
|
9304
|
+
for (const entry of entries) {
|
|
9305
|
+
const head = entry.unitHead;
|
|
9306
|
+
const owner = isLandmark(entry) ? head?.skill : void 0;
|
|
9307
|
+
if (owner === void 0) continue;
|
|
9308
|
+
const verdict = landmarkVerdict(entry, indexes.get(owner) ?? null);
|
|
9309
|
+
if (!verdict.jumpable && verdict.reason !== "not-a-landmark") out.push({
|
|
9310
|
+
skill,
|
|
9311
|
+
v: entry.v,
|
|
9312
|
+
owner,
|
|
9313
|
+
reason: verdict.reason
|
|
9314
|
+
});
|
|
9315
|
+
}
|
|
9316
|
+
}
|
|
9317
|
+
return out.sort((a, b) => a.skill.localeCompare(b.skill) || a.v - b.v || a.owner.localeCompare(b.owner));
|
|
9318
|
+
}
|
|
9319
|
+
//#endregion
|
|
9136
9320
|
//#region lib/types/skill-store.js
|
|
9137
9321
|
/**
|
|
9138
9322
|
* Skill library management for the self-evolution plugin.
|
|
@@ -9206,6 +9390,7 @@ function pointerBlock(rel, sectionText) {
|
|
|
9206
9390
|
function collisionsNote(collisions) {
|
|
9207
9391
|
return collisions.length === 0 ? "" : ` collisions: ${collisions.slice(0, 5).join(", ")}`;
|
|
9208
9392
|
}
|
|
9393
|
+
let nextUnitSeq = 0;
|
|
9209
9394
|
/**
|
|
9210
9395
|
* Evaluate a stage-time anchor against the bytes a locked read observed.
|
|
9211
9396
|
* @param anchor - the caller's anchor, or `undefined` for an unanchored write.
|
|
@@ -10005,7 +10190,7 @@ var SkillLibrary = class {
|
|
|
10005
10190
|
* mutation ledger that points at it. The history failure is not silent — a deployment whose
|
|
10006
10191
|
* history stopped growing is a degradation worth seeing once (the ledger keeps hashes either
|
|
10007
10192
|
* way). */
|
|
10008
|
-
async audit(skillName, action, before, after, summary, path, origin) {
|
|
10193
|
+
async audit(skillName, action, before, after, summary, path, unitId, unitHead, origin) {
|
|
10009
10194
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10010
10195
|
let versions = null;
|
|
10011
10196
|
try {
|
|
@@ -10016,7 +10201,9 @@ var SkillLibrary = class {
|
|
|
10016
10201
|
before,
|
|
10017
10202
|
after,
|
|
10018
10203
|
at,
|
|
10019
|
-
...path === void 0 ? {} : { path }
|
|
10204
|
+
...path === void 0 ? {} : { path },
|
|
10205
|
+
...unitId === void 0 ? {} : { unitId },
|
|
10206
|
+
...unitHead === void 0 ? {} : { unitHead }
|
|
10020
10207
|
};
|
|
10021
10208
|
versions = await recordVersions(this.root, this.io, input, keep);
|
|
10022
10209
|
} catch (error) {
|
|
@@ -11077,8 +11264,7 @@ var SkillLibrary = class {
|
|
|
11077
11264
|
message: badName
|
|
11078
11265
|
};
|
|
11079
11266
|
const dir = this.dirOf(name);
|
|
11080
|
-
|
|
11081
|
-
if (!md) return {
|
|
11267
|
+
if (!await this.io.readText(join(dir, "SKILL.md"))) return {
|
|
11082
11268
|
ok: false,
|
|
11083
11269
|
message: `技能「${name}」不存在。`
|
|
11084
11270
|
};
|
|
@@ -11162,10 +11348,26 @@ var SkillLibrary = class {
|
|
|
11162
11348
|
message: `目录树变更失败:${reason}。回滚无法恢复 ${stuck.join(", ")}:请从 .backups 快照恢复这些目标(或重新应用这次变更)。`
|
|
11163
11349
|
};
|
|
11164
11350
|
}
|
|
11165
|
-
const
|
|
11166
|
-
const
|
|
11167
|
-
|
|
11168
|
-
|
|
11351
|
+
const unitId = plan.unitId ?? "u-" + Date.now().toString(36) + "-" + (nextUnitSeq += 1).toString(36);
|
|
11352
|
+
for (const land of landing) {
|
|
11353
|
+
const rel = land.target.split(/[\\/]/).pop() === "SKILL.md" ? void 0 : land.target.slice(dir.length + 1).split("\\").join("/");
|
|
11354
|
+
const unitHead = land === landing[0] ? {
|
|
11355
|
+
moves: landing.filter((l) => l.target.split(/[\\/]/).pop() !== "SKILL.md").map((l) => {
|
|
11356
|
+
const p = l.target.slice(dir.length + 1).split(String.fromCharCode(92)).join(String.fromCharCode(47));
|
|
11357
|
+
return {
|
|
11358
|
+
path: p,
|
|
11359
|
+
kind: l.previous === null ? "created" : "moved",
|
|
11360
|
+
to: p,
|
|
11361
|
+
...l.previous === null ? {} : { beforeRef: contentHash(l.previous) },
|
|
11362
|
+
afterRef: contentHash(l.content)
|
|
11363
|
+
};
|
|
11364
|
+
}).filter((m, idx, all) => all.map((x) => x.path).lastIndexOf(m.path) === idx),
|
|
11365
|
+
sources: [{ skill: name }]
|
|
11366
|
+
} : void 0;
|
|
11367
|
+
const auditedBefore = land.previous;
|
|
11368
|
+
const auditedAfter = land.content;
|
|
11369
|
+
await this.audit(name, plan.auditAction, auditedBefore, auditedAfter, plan.auditSummary, rel, unitId, unitHead, plan.origin);
|
|
11370
|
+
}
|
|
11169
11371
|
this.notifyMutation({
|
|
11170
11372
|
action: plan.eventAction,
|
|
11171
11373
|
name,
|
|
@@ -11366,11 +11568,11 @@ var SkillLibrary = class {
|
|
|
11366
11568
|
};
|
|
11367
11569
|
}, anchor !== void 0 ? () => anchorUnverifiable(name, filePath) : (error) => unreadableTarget(name + "/" + filePath, error));
|
|
11368
11570
|
}
|
|
11369
|
-
async removeSupportFile(rawName, filePath, origin = "foreground", anchor) {
|
|
11571
|
+
async removeSupportFile(rawName, filePath, origin = "foreground", anchor, unitId) {
|
|
11370
11572
|
const name = rawName.trim();
|
|
11371
|
-
return await this.serial(() => this.removeSupportFileCore(name, filePath, origin, anchor));
|
|
11573
|
+
return await this.serial(() => this.removeSupportFileCore(name, filePath, origin, anchor, unitId));
|
|
11372
11574
|
}
|
|
11373
|
-
async removeSupportFileCore(name, filePath, origin, anchor) {
|
|
11575
|
+
async removeSupportFileCore(name, filePath, origin, anchor, unitId) {
|
|
11374
11576
|
const badName = this.badName(name);
|
|
11375
11577
|
if (badName) return {
|
|
11376
11578
|
ok: false,
|
|
@@ -11424,7 +11626,7 @@ var SkillLibrary = class {
|
|
|
11424
11626
|
};
|
|
11425
11627
|
} else if (verdict === "match") await this.io.remove(target);
|
|
11426
11628
|
if (verdict !== "match") return anchorRefusalFile(name, filePath, verdict);
|
|
11427
|
-
await this.audit(name, "remove_file", lockedBefore, null, `removed ${filePath}`, filePath);
|
|
11629
|
+
await this.audit(name, "remove_file", lockedBefore, null, `removed ${filePath}`, filePath, unitId);
|
|
11428
11630
|
this.notifyMutation({
|
|
11429
11631
|
action: "remove_file",
|
|
11430
11632
|
name,
|
|
@@ -13759,4 +13961,4 @@ function foldUntrustedLines(text, limit) {
|
|
|
13759
13961
|
return limit > 0 ? marked.slice(0, limit) : marked;
|
|
13760
13962
|
}
|
|
13761
13963
|
//#endregion
|
|
13762
|
-
export { AGENT_PRESET_PACKAGE, AGENT_PRESET_ROOT_HINT, AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ADMISSION, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_PROBE_TIMEOUT_MS, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_RUN_RECORDS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, EMPTY_PATCH_SEED, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, FUZZY_MAX_PATTERN_CHARS, FUZZY_MAX_WORK, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, INSTANCE_KEYS, KEEP_LINE_RE, LONG_RUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_REQUEST_BODY_BYTES, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, NAMESPACE_MIGRATIONS, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, PLATFORM_SERVICE_PROBES, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, RULES, RUNS_SCHEMA_VERSION, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPORT_SPRAWL_FILES, SUPPRESSED_FILE_VERSION, SkillLibrary, TEXT_DIFF_MAX_CHARS, TEXT_DIFF_MAX_LINES, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UNTRUSTED_LINE_BREAK, UNTRUSTED_LINE_MARK, UPSTREAM_LIMIT_CHARS_PER_TOKEN, VERSION_SUMMARY_MAX_CHARS, absentHostServices, admissionVerdict, advanceReview, affinity, ageCopyKey, allowRowCollisions, anchorVerdict, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyNamespaceMigration, applyReferenceRewrite, asRecord, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, atomicWriteFiles, authoringFeedback, basePresetPlugins, blobPath, bodyCost, buildCuratorRunReport, buildHealthBaseline, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, canonicalWriteId, claimInstance, clampOnce, clampedNumber, classifyNamespaceMigration, clearReviewChannel, collectReadSkillNames, composePresetComposition, composePresetEntry, composePresetInsert, composePresetRow, compositionRowIds, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, countReferenceFiles, createGateSet, daysSinceIso, declaredReportStamp, denseBlockSequence, describeReferenceRewrite, diffFindings, dispatchKeyOf, dispatchStepOf, displayBodyOf, duplicateHeadings, elapsedSince, emptyRecord, entryTarget, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionActivityProjection, evolutionEventPayloadIssue, evolutionEvidenceProjection, evolutionHome, evolutionIoAdapter, evolutionReadsProjection, evolutionRoot, evolutionSignalsProjection, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurnEvent, foldTurnEvents, foldUntrustedLines, frontmatterBlock, frontmatterCatalogInvalid, fuzzyPatch, getRecord, h2Sections, historyIndexFile, historyRoot, identity, idleDays, initialTurnFold, installEvolutionProjections, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isBookkeepingFrame, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isLoopbackRequest, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isRecord, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, latestActivityAt, latestVersionAt, lifecycleCandidate, lintBody, lintSkill, listDirectories, listEventArchives, liveBool, liveEnum, liveNumber, liveString, loadMutations, loadRowOverrides, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, mergePresetRow, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, neutralizePromptVariables, newRunRegistry, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, normalizeVersionSummary, observeEvent, orderVersions, overlongLines, paramNamespace, paramRowId, paramSettingsId, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, partitionVersions, payloadText, persistedWriteSite, persistedWriteSites, pickWithPolicy, planNamespaceMigration, planReferenceRewrite, planRehoming, policyStageLimits, presetPatchText, presetRowBlock, presetRowBody, presetRowId, probeAbsent, probeDesktopScope, probeList, probeMtime, probePresent, probeReason, probeText, probeUnknown, projectionText, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readJsonBody, readJsonObject, readNumberParam, readParam, readingStats, recordMutation, recordVersions, redactSecrets, relatedSkillNames, releaseInstance, removePresetRow, renameWithRetry, renderCuratorReportMarkdown, renderLongRunTable, reportTime, reportsSweepLockTarget, resolveBasePresetPatch, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, restructureCandidates, retainEventArchives, retirementReport, reviewPrompt, rowBlockSpan, rowSiblingEnd, runsFile, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopedProbeReport, sessionAudited, sessionEvidenceIndex, sessionLastEventTime, sessionReadNames, sessionReadSkillNames, sessionSeesFamilyTools, sessionTurnSignals, skillReadNameOf, skillRowCells, skillStateKey, skillsRoot, supportFileReadOf, suppressedFile, sweepReports, sweepReviewChannelSessions, textDiffFacts, tokenLineFor, transactIo, transactTaskGuard, trimPatternBoundaries, trimTrailingBlankLines, unhookedSupportPointers, unwrapLive, updateSuppressedNames, usageFile, usageObserved, userSetKeys, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionActionKind, versionRowCells, versionSummaryPrompt, versionTarget, vocabulary, writeDurableTmp, writeJson, writePathSymbols, yamlPlainScalarNeedsQuotes };
|
|
13964
|
+
export { AGENT_PRESET_PACKAGE, AGENT_PRESET_ROOT_HINT, AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ADMISSION, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_PROBE_TIMEOUT_MS, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_RUN_RECORDS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, EMPTY_PATCH_SEED, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, FUZZY_MAX_PATTERN_CHARS, FUZZY_MAX_WORK, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, INSTANCE_KEYS, KEEP_LINE_RE, LONG_RUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_REQUEST_BODY_BYTES, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, NAMESPACE_MIGRATIONS, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, PLATFORM_SERVICE_PROBES, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, RULES, RUNS_SCHEMA_VERSION, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPORT_SPRAWL_FILES, SUPPRESSED_FILE_VERSION, SkillLibrary, TEXT_DIFF_MAX_CHARS, TEXT_DIFF_MAX_LINES, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UNTRUSTED_LINE_BREAK, UNTRUSTED_LINE_MARK, UPSTREAM_LIMIT_CHARS_PER_TOKEN, VERSION_SUMMARY_MAX_CHARS, absentHostServices, admissionVerdict, advanceReview, affinity, ageCopyKey, allowRowCollisions, anchorVerdict, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyNamespaceMigration, applyReferenceRewrite, asRecord, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, atomicWriteFiles, authoringFeedback, basePresetPlugins, blobPath, bodyCost, buildCuratorRunReport, buildHealthBaseline, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, candidateSkills, canonicalWriteId, claimInstance, clampOnce, clampedNumber, classifyNamespaceMigration, clearReviewChannel, collectReadSkillNames, composePresetComposition, composePresetEntry, composePresetInsert, composePresetRow, compositionRowIds, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, countReferenceFiles, createGateSet, daysSinceIso, declaredReportStamp, denseBlockSequence, describeReferenceRewrite, diffFindings, dispatchKeyOf, dispatchStepOf, displayBodyOf, duplicateHeadings, elapsedSince, emptyRecord, entryTarget, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionActivityProjection, evolutionEventPayloadIssue, evolutionEvidenceProjection, evolutionHome, evolutionIoAdapter, evolutionReadsProjection, evolutionRoot, evolutionSignalsProjection, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurnEvent, foldTurnEvents, foldUntrustedLines, frontmatterBlock, frontmatterCatalogInvalid, fuzzyPatch, getRecord, h2Sections, historyIndexFile, historyRoot, identity, idleDays, initialTurnFold, installEvolutionProjections, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isBookkeepingFrame, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isLandmark, isLoopbackRequest, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isRecord, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, landmarkVerdict, latestActivityAt, latestVersionAt, lifecycleCandidate, lintBody, lintSkill, listDirectories, listEventArchives, liveBool, liveEnum, liveNumber, liveString, loadMutations, loadRowOverrides, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, mergePresetRow, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, neutralizePromptVariables, newRunRegistry, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, normalizeVersionSummary, observeEvent, orderVersions, overlongLines, paramNamespace, paramRowId, paramSettingsId, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, partitionVersions, payloadText, persistedWriteSite, persistedWriteSites, pickWithPolicy, planNamespaceMigration, planReferenceRewrite, planRehoming, policyStageLimits, presetPatchText, presetRowBlock, presetRowBody, presetRowId, probeAbsent, probeDesktopScope, probeList, probeMtime, probePresent, probeReason, probeText, probeUnknown, projectionText, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readJsonBody, readJsonObject, readNumberParam, readParam, readingStats, recordMutation, recordVersions, redactSecrets, relatedSkillNames, releaseInstance, removePresetRow, renameWithRetry, renderCuratorReportMarkdown, renderLongRunTable, reportTime, reportsSweepLockTarget, resolveBasePresetPatch, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, resolveUnitMembers, resolveUnitMembersFromIndexes, restructureCandidates, retainEventArchives, retirementReport, reviewPrompt, rowBlockSpan, rowSiblingEnd, runsFile, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopedProbeReport, sessionAudited, sessionEvidenceIndex, sessionLastEventTime, sessionReadNames, sessionReadSkillNames, sessionSeesFamilyTools, sessionTurnSignals, skillReadNameOf, skillRowCells, skillStateKey, skillsRoot, staleRouteStubs, supportFileReadOf, suppressedFile, sweepReports, sweepReviewChannelSessions, textDiffFacts, tokenLineFor, transactIo, transactTaskGuard, trimPatternBoundaries, trimTrailingBlankLines, unhookedSupportPointers, unitMembersAreComplete, unwrapLive, updateSuppressedNames, usageFile, usageObserved, userSetKeys, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionActionKind, versionRowCells, versionSummaryPrompt, versionTarget, vocabulary, writeDurableTmp, writeJson, writePathSymbols, yamlPlainScalarNeedsQuotes };
|
package/lib/types/curator.d.ts
CHANGED
|
@@ -54,6 +54,18 @@ export interface CuratorFailedSkill {
|
|
|
54
54
|
reason: string;
|
|
55
55
|
}
|
|
56
56
|
export interface CuratorRunReport {
|
|
57
|
+
/** G3b: cross-index orphan landmarks -- report-only, never a verdict. */
|
|
58
|
+
routeStubs?: {
|
|
59
|
+
readonly skills: number;
|
|
60
|
+
readonly stubs: number;
|
|
61
|
+
readonly reason: string;
|
|
62
|
+
readonly detail: readonly {
|
|
63
|
+
readonly skill: string;
|
|
64
|
+
readonly v: number;
|
|
65
|
+
readonly owner: string;
|
|
66
|
+
readonly reason: string;
|
|
67
|
+
}[];
|
|
68
|
+
};
|
|
57
69
|
/** Report shape version; readers may ignore unknown fields on later versions. */
|
|
58
70
|
schemaVersion: 1;
|
|
59
71
|
runId: string;
|
|
@@ -25,6 +25,12 @@ export declare function frontmatterBlock(content: string): {
|
|
|
25
25
|
*/
|
|
26
26
|
export interface FrontmatterRead {
|
|
27
27
|
frontmatter: Frontmatter;
|
|
28
|
+
/** P0′: the structured `metadata:` mapping, when the strict parser loaded one.
|
|
29
|
+
* Published as an OBJECT (never stringified): nested mappings used to be dropped
|
|
30
|
+
* because a lossy string could be picked up by a routing field — keeping it
|
|
31
|
+
* structured removes that risk while giving the one consumer the nested
|
|
32
|
+
* `metadata.hermes.related_skills` form the real corpus actually writes. */
|
|
33
|
+
metadata?: Record<string, unknown> | undefined;
|
|
28
34
|
body: string;
|
|
29
35
|
/** Raw entries whose UNQUOTED value the strict catalog cannot load as
|
|
30
36
|
* written. Quotes are included, so a value already normalized by the write
|
package/lib/types/index.d.ts
CHANGED
|
@@ -72,6 +72,7 @@ export * from './evidence.ts';
|
|
|
72
72
|
export * from './session-projection.ts';
|
|
73
73
|
export * from './drift-signals.ts';
|
|
74
74
|
export * from './skill-history.ts';
|
|
75
|
+
export * from './unit-members.ts';
|
|
75
76
|
export * from './skill-store.ts';
|
|
76
77
|
export * from './state-store.ts';
|
|
77
78
|
export * from './threats.ts';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** 技能名文法(与平台 `isSkillName` 等价:kebab-case,点前缀与下划线一律不是技能名)。 */
|
|
2
|
+
export declare const SKILL_NAME_RE: RegExp;
|
|
3
|
+
/** 名字是否是技能名 —— `._x`、`Bad_Name`、`.archive` 都返回 false。 */
|
|
4
|
+
export declare function isSkillName(name: string): boolean;
|
|
5
|
+
/** 相对技能根的路径是否落在平台会发现的位置(depth<=2 且最后一段是 SKILL.md 或 `<name>.md`)。 */
|
|
6
|
+
export declare function isPotentialSkillPath(relPath: string): boolean;
|
|
7
|
+
export interface DiscoveredSkill {
|
|
8
|
+
name: string;
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 扫描一个技能根,返回平台**真正会发现**的技能(非递归、depth<=2、含 `SKILL.md` 的目录、
|
|
13
|
+
* 名字过 kebab-case、跳过点前缀目录)。忽略目录里的边不得计入,也不得算悬空。
|
|
14
|
+
*
|
|
15
|
+
* @param root - 技能根目录。
|
|
16
|
+
* @returns 按名字排序的技能列表(根缺失时返回空数组)。
|
|
17
|
+
*/
|
|
18
|
+
export declare function discoverSkillFiles(root: string): DiscoveredSkill[];
|
|
19
|
+
//# sourceMappingURL=skill-discovery.d.ts.map
|
|
@@ -34,11 +34,30 @@ export declare const HISTORY_INDEX_VERSION = 1;
|
|
|
34
34
|
/** The action label for a predecessor the index had never seen (content found on disk before this
|
|
35
35
|
* feature existed, or a hand-edited tree): it says "this is what was there", not what wrote it. */
|
|
36
36
|
export declare const BASELINE_ACTION = "baseline";
|
|
37
|
+
/** G3b: one unit's head. Exactly one of the two forms is present (they never coexist). */
|
|
38
|
+
export interface UnitHead {
|
|
39
|
+
/** Representative shard: the unit's WHOLE manifest of moves (one entry per written path). */
|
|
40
|
+
moves?: readonly Readonly<Record<string, unknown>>[];
|
|
41
|
+
/** Representative shard: the skills this unit wrote, so members resolve without a directory scan. */
|
|
42
|
+
sources?: readonly {
|
|
43
|
+
readonly skill: string;
|
|
44
|
+
readonly dest?: string;
|
|
45
|
+
}[];
|
|
46
|
+
/** Cross-skill landmark: the skill that owns the unit (route-only; never carries `moves`). */
|
|
47
|
+
skill?: string;
|
|
48
|
+
}
|
|
37
49
|
/** One recorded version of one skill. */
|
|
38
50
|
export interface SkillVersion {
|
|
39
51
|
/** Monotonic per skill; the identity, unlike {@link SkillVersion.at}. */
|
|
40
52
|
v: number;
|
|
41
53
|
at: string;
|
|
54
|
+
/** G3a: the unit this entry belongs to (one tree change = one unit); absent on entries recorded before it. */
|
|
55
|
+
unitId?: string;
|
|
56
|
+
/** G3b: the unit HEAD. A representative shard carries `{ moves, sources }` (`moves` is the whole
|
|
57
|
+
* manifest of the unit, `sources` names the skills it wrote); a cross-skill landmark carries
|
|
58
|
+
* `{ skill }` only, and the two forms are mutually exclusive. Discriminate on `moves !== undefined`
|
|
59
|
+
* (a landmark also carries `unitId`, so that field can never discriminate). */
|
|
60
|
+
unitHead?: UnitHead;
|
|
42
61
|
/** The mutation that produced it (`create`/`update`/`patch`/…), or {@link BASELINE_ACTION}. */
|
|
43
62
|
action: string;
|
|
44
63
|
/** sha256 of the stored content. */
|
|
@@ -75,6 +94,10 @@ export interface VersionRecordInput {
|
|
|
75
94
|
summary?: string;
|
|
76
95
|
/** The support file this write touched, relative to the skill directory; undefined for content. */
|
|
77
96
|
path?: string;
|
|
97
|
+
/** G3a: the unit this write belongs to — one tree change writes N paths under ONE id. */
|
|
98
|
+
unitId?: string;
|
|
99
|
+
/** G3b: present only on the unit's representative shard (see {@link SkillVersion.unitHead}). */
|
|
100
|
+
unitHead?: UnitHead;
|
|
78
101
|
}
|
|
79
102
|
/** The version numbers one write produced; a side with no content contributes nothing. */
|
|
80
103
|
export interface RecordedVersions {
|
|
@@ -570,7 +570,7 @@ export declare class SkillLibrary {
|
|
|
570
570
|
restoreFromArchive(rawName: string): Promise<SkillActionResult>;
|
|
571
571
|
writeSupportFile(rawName: string, filePath: string, content: string, origin?: WriteOrigin, anchor?: WriteAnchor): Promise<SkillActionResult>;
|
|
572
572
|
private writeSupportFileCore;
|
|
573
|
-
removeSupportFile(rawName: string, filePath: string, origin?: WriteOrigin, anchor?: WriteAnchor): Promise<SkillActionResult>;
|
|
573
|
+
removeSupportFile(rawName: string, filePath: string, origin?: WriteOrigin, anchor?: WriteAnchor, unitId?: string): Promise<SkillActionResult>;
|
|
574
574
|
private removeSupportFileCore;
|
|
575
575
|
/**
|
|
576
576
|
* v23 (ML-1): `.archive` retention. Archived skills are recoverable history,
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* G3b: resolve a unit's members from the skills named in its head's `sources[]`.
|
|
3
|
+
*
|
|
4
|
+
* Pure core (no io): the caller hands in each candidate skill's index TEXT, so this function can be
|
|
5
|
+
* tested with synthetic indexes. Membership is decided ONLY by the known keys -- never by scanning a
|
|
6
|
+
* directory -- and the result order is the `sources[]` order with the head skill first (deterministic:
|
|
7
|
+
* no Map/Set iteration order leaks into the output).
|
|
8
|
+
*/
|
|
9
|
+
import type { SkillVersion, UnitHead } from './skill-history.ts';
|
|
10
|
+
export interface UnitMemberSlice {
|
|
11
|
+
readonly skill: string;
|
|
12
|
+
readonly entries: readonly SkillVersion[];
|
|
13
|
+
}
|
|
14
|
+
export interface UnitMemberIssue {
|
|
15
|
+
readonly skill: string;
|
|
16
|
+
readonly reason: 'index-absent' | 'unit-absent';
|
|
17
|
+
}
|
|
18
|
+
export interface UnitConflict {
|
|
19
|
+
readonly kind: 'missing' | 'foreign' | 'duplicated';
|
|
20
|
+
readonly path: string;
|
|
21
|
+
}
|
|
22
|
+
export interface UnitMembers {
|
|
23
|
+
readonly members: readonly UnitMemberSlice[];
|
|
24
|
+
readonly missing: readonly UnitMemberIssue[];
|
|
25
|
+
readonly conflicts: readonly UnitConflict[];
|
|
26
|
+
}
|
|
27
|
+
export interface UnitHeadInput {
|
|
28
|
+
readonly skill: string;
|
|
29
|
+
readonly unitId: string;
|
|
30
|
+
readonly sources: readonly {
|
|
31
|
+
readonly skill: string;
|
|
32
|
+
}[];
|
|
33
|
+
readonly unitHead?: UnitHead;
|
|
34
|
+
}
|
|
35
|
+
/** Candidate skills: head first, then `sources[]` order, DEDUPLICATED (sources may name the head). */
|
|
36
|
+
export declare function candidateSkills(head: UnitHeadInput): string[];
|
|
37
|
+
export declare function resolveUnitMembersFromIndexes(head: UnitHeadInput, indexes: ReadonlyMap<string, string | null>, readEntries: (text: string) => readonly SkillVersion[]): UnitMembers;
|
|
38
|
+
/** A unit's members are trustworthy only when nothing is missing and nothing conflicts. */
|
|
39
|
+
export declare function unitMembersAreComplete(members: UnitMembers): boolean;
|
|
40
|
+
/** Minimal io view the shell needs (structural, so this module imports no io type). */
|
|
41
|
+
export interface UnitMemberIo {
|
|
42
|
+
readText(path: string): Promise<string | null>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The io shell (A19.2): open ONLY the known keys -- never enumerate a directory. A skill whose index
|
|
46
|
+
* file is unreadable becomes `null` (index-absent), which the core then reports by name.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveUnitMembers(root: string, io: UnitMemberIo, head: UnitHeadInput, indexPath: (root: string, name: string) => string, readEntries: (raw: string | null) => readonly SkillVersion[]): Promise<UnitMembers>;
|
|
49
|
+
/** G3b: a LANDMARK carries the owning skill and no manifest (the two forms never coexist). */
|
|
50
|
+
export declare function isLandmark(entry: SkillVersion): boolean;
|
|
51
|
+
/** A landmark is jumpable only when its owner exists AND that index still holds the unit. */
|
|
52
|
+
export type LandmarkVerdict = {
|
|
53
|
+
readonly jumpable: true;
|
|
54
|
+
} | {
|
|
55
|
+
readonly jumpable: false;
|
|
56
|
+
readonly reason: 'not-a-landmark' | 'owner-absent' | 'unit-absent';
|
|
57
|
+
};
|
|
58
|
+
/** @param entry - the entry being judged. @param ownerEntries - the owner skill's index, or null when
|
|
59
|
+
* the owner has no readable index. */
|
|
60
|
+
export declare function landmarkVerdict(entry: SkillVersion, ownerEntries: readonly SkillVersion[] | null): LandmarkVerdict;
|
|
61
|
+
/** One cross-index orphan: a landmark whose owner or unit can no longer be reached. */
|
|
62
|
+
export interface StaleRouteStub {
|
|
63
|
+
readonly skill: string;
|
|
64
|
+
readonly v: number;
|
|
65
|
+
readonly owner: string;
|
|
66
|
+
readonly reason: 'owner-absent' | 'unit-absent';
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Scan a whole corpus (skill name -> index entries, or null when unreadable) for stale route stubs.
|
|
70
|
+
* Sorted by (skill, version, owner): a function of the corpus, not of the caller's map order.
|
|
71
|
+
*/
|
|
72
|
+
export declare function staleRouteStubs(indexes: ReadonlyMap<string, readonly SkillVersion[] | null>): StaleRouteStub[];
|
|
73
|
+
//# sourceMappingURL=unit-members.d.ts.map
|
package/package.json
CHANGED