@lmzhen/dsh-evolution-core 0.23.0 → 0.25.0
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 +533 -213
- package/lib/types/constants.d.ts +8 -1
- package/lib/types/document-body.d.ts +33 -0
- package/lib/types/errors.d.ts +27 -27
- package/lib/types/health-baseline.d.ts +37 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/mutations.d.ts +3 -0
- package/lib/types/skill-health.d.ts +23 -1
- package/lib/types/skill-lint.d.ts +59 -0
- package/lib/types/skill-store.d.ts +6 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -473,6 +473,13 @@ const DEFAULT_CURATOR_INTERVAL_HOURS = 168;
|
|
|
473
473
|
const DEFAULT_MIN_IDLE_HOURS = 2;
|
|
474
474
|
const DEFAULT_STALE_AFTER_DAYS = 30;
|
|
475
475
|
const DEFAULT_ARCHIVE_AFTER_DAYS = 90;
|
|
476
|
+
/**
|
|
477
|
+
* Support-file count above which `references-sprawl` warns. **NOT CALIBRATED YET** — 60 is a
|
|
478
|
+
* placeholder: the platform corpus is empty (no platform skills available here), so the admission
|
|
479
|
+
* band cannot be checked. Until `gen-health-baseline` reports both corpora, the rule is
|
|
480
|
+
* REPORT-ONLY: its count must not be shown as a number on any surface (plan G4 ③).
|
|
481
|
+
*/
|
|
482
|
+
const SUPPORT_SPRAWL_FILES = 60;
|
|
476
483
|
const DEFAULT_MEMORY_CHAR_LIMIT = 2200;
|
|
477
484
|
const DEFAULT_USER_CHAR_LIMIT = 1375;
|
|
478
485
|
/** Consolidation-failure backoff cap, shared by MemoryStore and memory-files' Config default. */
|
|
@@ -531,7 +538,7 @@ const PLATFORM_CATALOG_DESCRIPTION_DEFAULT = 500;
|
|
|
531
538
|
* be copied into validateFrontmatter AND the patch path, and neither copy named
|
|
532
539
|
* where the content should go — the upstream cap message names the destination
|
|
533
540
|
* directories, and that is the part a model actually acts on. */
|
|
534
|
-
const CONTENT_SPLIT_HINT = "
|
|
541
|
+
const CONTENT_SPLIT_HINT = "建议拆成更小的 SKILL.md,把支撑文件放进 references/ 或 templates/。";
|
|
535
542
|
/** V27 G2.4: the largest millisecond delay a timer accepts. `AbortSignal.timeout`
|
|
536
543
|
* (and `setTimeout`) coerce anything larger to 1ms after a Node warning, so a
|
|
537
544
|
* timeout configured above this ceiling silently collapses to "immediately
|
|
@@ -1052,16 +1059,16 @@ function shrinksOverLimit(next, current, limit) {
|
|
|
1052
1059
|
}
|
|
1053
1060
|
function validateFrontmatter(content, expectedName, limits = DEFAULT_SKILL_LIMITS, current) {
|
|
1054
1061
|
const parsed = parseFrontmatter(content);
|
|
1055
|
-
if (!parsed) return "SKILL.md
|
|
1062
|
+
if (!parsed) return "SKILL.md 必须以 `---` 行开头,用另一个完全相同的 `---` 行结束 frontmatter(只容忍结尾的 `\\r`),并在其下包含正文。";
|
|
1056
1063
|
const unreadable = parsed.platformStringSplit.filter((entry) => entry.kind === "sequence" || entry.kind === "mapping");
|
|
1057
|
-
if (unreadable.length > 0) return `Frontmatter
|
|
1058
|
-
if (!parsed.frontmatter.name) return "Frontmatter
|
|
1059
|
-
if (!SKILL_NAME_RE.test(parsed.frontmatter.name)) return
|
|
1060
|
-
if (parsed.frontmatter.name.length > limits.maxNameLength) return
|
|
1061
|
-
if (expectedName && parsed.frontmatter.name !== expectedName) return `Frontmatter name
|
|
1062
|
-
if (!parsed.frontmatter.description) return "Frontmatter
|
|
1063
|
-
if (parsed.frontmatter.description.length > limits.maxDescriptionLength) return `
|
|
1064
|
-
if (exceedsContentLimit(content, limits.maxSkillContentChars) && !shrinksOverLimit(content, current, limits.maxSkillContentChars)) return `SKILL.md
|
|
1064
|
+
if (unreadable.length > 0) return `Frontmatter 字段 ${unreadable.map((entry) => `"${entry.key}" (a YAML ${entry.kind})`).join(", ")} 必须是字符串:平台技能目录会把这种字段当作缺失,并忽略整个文件。请写成纯文本,或用双引号包住取值以保持字面内容。`;
|
|
1065
|
+
if (!parsed.frontmatter.name) return "Frontmatter 必须包含 name 字段。";
|
|
1066
|
+
if (!SKILL_NAME_RE.test(parsed.frontmatter.name)) return `技能名「${parsed.frontmatter.name}」不合法:请只用小写字母、数字和连字符。`;
|
|
1067
|
+
if (parsed.frontmatter.name.length > limits.maxNameLength) return `技能名超过 ${limits.maxNameLength} 个字符。`;
|
|
1068
|
+
if (expectedName && parsed.frontmatter.name !== expectedName) return `Frontmatter 里的 name「${parsed.frontmatter.name}」与目标技能「${expectedName}」不一致。`;
|
|
1069
|
+
if (!parsed.frontmatter.description) return "Frontmatter 必须包含 description 字段。";
|
|
1070
|
+
if (parsed.frontmatter.description.length > limits.maxDescriptionLength) return `description 超过 ${limits.maxDescriptionLength} 个字符。`;
|
|
1071
|
+
if (exceedsContentLimit(content, limits.maxSkillContentChars) && !shrinksOverLimit(content, current, limits.maxSkillContentChars)) return `SKILL.md 内容超过 ${limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`;
|
|
1065
1072
|
return null;
|
|
1066
1073
|
}
|
|
1067
1074
|
/**
|
|
@@ -1076,8 +1083,8 @@ function authoringFeedback(frontmatter) {
|
|
|
1076
1083
|
const over60 = description.length > 60;
|
|
1077
1084
|
const hasColon = description.includes(":");
|
|
1078
1085
|
const lines = [];
|
|
1079
|
-
lines.push(over60 ?
|
|
1080
|
-
if (hasColon) lines.push("
|
|
1086
|
+
lines.push(over60 ? `描述为 ${description.length}/60 个字符 —— 超过 60 字符的作者规范上限(Hermes 标准;技能目录会按平台配置的上限截断)。` : `描述为 ${description.length}/60 个字符 —— 在作者规范上限之内。`);
|
|
1087
|
+
if (hasColon) lines.push("描述里含冒号 —— 请用双引号把整个取值包起来。");
|
|
1081
1088
|
return {
|
|
1082
1089
|
descriptionChars: description.length,
|
|
1083
1090
|
over60,
|
|
@@ -1157,6 +1164,213 @@ function bodyCost(content) {
|
|
|
1157
1164
|
};
|
|
1158
1165
|
}
|
|
1159
1166
|
//#endregion
|
|
1167
|
+
//#region lib/types/skill-lint.js
|
|
1168
|
+
const msg = (parts) => parts.map((p) => typeof p === "string" ? p : String(p)).join("");
|
|
1169
|
+
/** One rule yields AT MOST one finding (contract §3). `body-oversize-2x` is the only rule whose
|
|
1170
|
+
* severity can escalate a verdict to needs-restructure. */
|
|
1171
|
+
const RULES = [
|
|
1172
|
+
{
|
|
1173
|
+
id: "unreadable-skill",
|
|
1174
|
+
severity: "error",
|
|
1175
|
+
run: (facts) => facts.readable && facts.dims.bodyChars !== null ? null : {
|
|
1176
|
+
severity: "error",
|
|
1177
|
+
rule: "unreadable-skill",
|
|
1178
|
+
message: msg([facts.skillName, ": SKILL.md could not be read — no health claim is possible"]),
|
|
1179
|
+
count: 1
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
id: "body-oversize-2x",
|
|
1184
|
+
severity: "error",
|
|
1185
|
+
run: (facts) => facts.overTwiceSoftLine ? {
|
|
1186
|
+
severity: "error",
|
|
1187
|
+
rule: "body-oversize-2x",
|
|
1188
|
+
message: msg([
|
|
1189
|
+
facts.skillName,
|
|
1190
|
+
": body is at or past twice the soft line (",
|
|
1191
|
+
facts.softLineChars,
|
|
1192
|
+
" chars)"
|
|
1193
|
+
]),
|
|
1194
|
+
count: 1
|
|
1195
|
+
} : null
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
id: "body-oversize",
|
|
1199
|
+
severity: "warn",
|
|
1200
|
+
run: (facts) => !facts.overTwiceSoftLine && facts.overSoftLine ? {
|
|
1201
|
+
severity: "warn",
|
|
1202
|
+
rule: "body-oversize",
|
|
1203
|
+
message: msg([
|
|
1204
|
+
facts.skillName,
|
|
1205
|
+
": body is at or above the soft line (",
|
|
1206
|
+
facts.softLineChars,
|
|
1207
|
+
" chars)"
|
|
1208
|
+
]),
|
|
1209
|
+
count: 1
|
|
1210
|
+
} : null
|
|
1211
|
+
},
|
|
1212
|
+
{
|
|
1213
|
+
id: "stamp-density",
|
|
1214
|
+
severity: "warn",
|
|
1215
|
+
run: (facts) => {
|
|
1216
|
+
const density = facts.dims.stampDensityPerKb;
|
|
1217
|
+
const hits = facts.dims.stampHits;
|
|
1218
|
+
if (density === null || hits === null || density < facts.stampLimitPerKb) return null;
|
|
1219
|
+
return {
|
|
1220
|
+
severity: "warn",
|
|
1221
|
+
rule: "stamp-density",
|
|
1222
|
+
message: msg([
|
|
1223
|
+
facts.skillName,
|
|
1224
|
+
": stamp density ",
|
|
1225
|
+
density.toFixed(1),
|
|
1226
|
+
"/KB (rc/sha/date lines — log-like content)"
|
|
1227
|
+
]),
|
|
1228
|
+
count: hits
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
id: "body-without-support",
|
|
1234
|
+
severity: "warn",
|
|
1235
|
+
run: (facts) => facts.dims.supportGroups === 0 && facts.dims.bodyChars !== null && facts.dims.bodyChars >= facts.softLineChars / 2 ? {
|
|
1236
|
+
severity: "warn",
|
|
1237
|
+
rule: "body-without-support",
|
|
1238
|
+
message: msg([
|
|
1239
|
+
facts.skillName,
|
|
1240
|
+
": large body (",
|
|
1241
|
+
facts.dims.bodyChars,
|
|
1242
|
+
" chars) with NO support files"
|
|
1243
|
+
]),
|
|
1244
|
+
count: 1
|
|
1245
|
+
} : null
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
id: "references-sprawl",
|
|
1249
|
+
severity: "warn",
|
|
1250
|
+
run: (facts) => {
|
|
1251
|
+
const files = facts.dims.supportFiles;
|
|
1252
|
+
if (files === null || files < 60) return null;
|
|
1253
|
+
return {
|
|
1254
|
+
severity: "warn",
|
|
1255
|
+
rule: "references-sprawl",
|
|
1256
|
+
message: msg([
|
|
1257
|
+
facts.skillName,
|
|
1258
|
+
": ",
|
|
1259
|
+
files,
|
|
1260
|
+
" support files"
|
|
1261
|
+
]),
|
|
1262
|
+
count: files
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
id: "write-ghost",
|
|
1268
|
+
severity: "warn",
|
|
1269
|
+
run: (facts) => {
|
|
1270
|
+
const patches = facts.dims.churnPatches;
|
|
1271
|
+
const reads = facts.dims.churnReads;
|
|
1272
|
+
if (patches === null || reads === null || patches < facts.churnMinPatches || reads > 0) return null;
|
|
1273
|
+
return {
|
|
1274
|
+
severity: "warn",
|
|
1275
|
+
rule: "write-ghost",
|
|
1276
|
+
message: msg([
|
|
1277
|
+
facts.skillName,
|
|
1278
|
+
": ",
|
|
1279
|
+
patches,
|
|
1280
|
+
" patch(es), never read"
|
|
1281
|
+
]),
|
|
1282
|
+
count: patches
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
];
|
|
1287
|
+
function lintSkill(facts) {
|
|
1288
|
+
const out = [];
|
|
1289
|
+
for (const rule of RULES) {
|
|
1290
|
+
const hit = rule.run(facts);
|
|
1291
|
+
if (hit !== null) out.push(hit);
|
|
1292
|
+
}
|
|
1293
|
+
return out;
|
|
1294
|
+
}
|
|
1295
|
+
/** What a write introduced, what got worse, what got fixed. A rule whose count grew (one site to
|
|
1296
|
+
* nine) counts as worsened — comparing by rule name alone reported nothing. */
|
|
1297
|
+
function diffFindings(before, after) {
|
|
1298
|
+
const beforeByRule = new Map(before.map((f) => [f.rule, f]));
|
|
1299
|
+
const afterByRule = new Map(after.map((f) => [f.rule, f]));
|
|
1300
|
+
return {
|
|
1301
|
+
introduced: after.filter((f) => !beforeByRule.has(f.rule)),
|
|
1302
|
+
worsened: after.filter((f) => {
|
|
1303
|
+
const old = beforeByRule.get(f.rule);
|
|
1304
|
+
return old !== void 0 && f.count > old.count;
|
|
1305
|
+
}),
|
|
1306
|
+
resolved: before.filter((f) => !afterByRule.has(f.rule)).map((f) => f.rule)
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
/** Support files = markdown files under references/ (contract §4: unit frozen, underscore-prefixed
|
|
1310
|
+
* excluded). Paths are supplied by the caller — this module never touches the filesystem. */
|
|
1311
|
+
function countReferenceFiles(paths) {
|
|
1312
|
+
return paths.filter((p) => {
|
|
1313
|
+
const n = p.replace(/\\/g, "/");
|
|
1314
|
+
if (!(n.includes("/references/") || n.startsWith("references/")) || !n.endsWith(".md")) return false;
|
|
1315
|
+
return !n.slice(n.lastIndexOf("/") + 1).startsWith("_");
|
|
1316
|
+
}).length;
|
|
1317
|
+
}
|
|
1318
|
+
//#endregion
|
|
1319
|
+
//#region lib/types/health-baseline.js
|
|
1320
|
+
const DEFAULT_ADMISSION = {
|
|
1321
|
+
minUserHitRate: .05,
|
|
1322
|
+
maxUserHitRate: .25
|
|
1323
|
+
};
|
|
1324
|
+
function buildHealthBaseline(skills) {
|
|
1325
|
+
const values = {};
|
|
1326
|
+
for (const rule of RULES) values[rule.id] = {
|
|
1327
|
+
platformHits: 0,
|
|
1328
|
+
userHits: 0,
|
|
1329
|
+
userHitRate: 0
|
|
1330
|
+
};
|
|
1331
|
+
let user = 0;
|
|
1332
|
+
let platform = 0;
|
|
1333
|
+
for (const skill of skills) {
|
|
1334
|
+
if (skill.corpus === "user") user += 1;
|
|
1335
|
+
else platform += 1;
|
|
1336
|
+
for (const finding of lintSkill(skill.facts)) {
|
|
1337
|
+
const row = values[finding.rule];
|
|
1338
|
+
if (row === void 0) continue;
|
|
1339
|
+
if (skill.corpus === "user") row.userHits += 1;
|
|
1340
|
+
else row.platformHits += 1;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
for (const row of Object.values(values)) row.userHitRate = user === 0 ? 0 : row.userHits / user;
|
|
1344
|
+
return {
|
|
1345
|
+
unit: "chars",
|
|
1346
|
+
scope: "references markdown file count",
|
|
1347
|
+
corpora: {
|
|
1348
|
+
user,
|
|
1349
|
+
platform
|
|
1350
|
+
},
|
|
1351
|
+
values
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
/** The admission verdict for ONE rule id (contract §4): platform corpus must not trip it, and the
|
|
1355
|
+
* user-library hit rate must land inside the band. Every failure names the number it failed on. */
|
|
1356
|
+
function admissionVerdict(baseline, ruleId, thresholds = DEFAULT_ADMISSION) {
|
|
1357
|
+
const row = baseline.values[ruleId];
|
|
1358
|
+
const failures = [];
|
|
1359
|
+
if (row === void 0) return {
|
|
1360
|
+
admitted: false,
|
|
1361
|
+
failures: ["rule " + ruleId + " has no baseline row"]
|
|
1362
|
+
};
|
|
1363
|
+
if (baseline.corpora.platform === 0) failures.push("no platform-corpus skills measured — a vacuous 0-hit count admits any rule");
|
|
1364
|
+
else if (row.platformHits !== 0) failures.push("platform corpus trips " + ruleId + " " + String(row.platformHits) + " time(s) — a rule the shipped library violates is unsatisfiable");
|
|
1365
|
+
if (baseline.corpora.user === 0) failures.push("no user-library skills measured — a rate over an empty corpus proves nothing");
|
|
1366
|
+
else if (row.userHitRate < thresholds.minUserHitRate) failures.push(ruleId + " hits " + row.userHitRate.toFixed(3) + " of the user library, below " + String(thresholds.minUserHitRate) + " — too rare to be worth a rule");
|
|
1367
|
+
else if (row.userHitRate > thresholds.maxUserHitRate) failures.push(ruleId + " hits " + row.userHitRate.toFixed(3) + " of the user library, above " + String(thresholds.maxUserHitRate) + " — furniture, no signal");
|
|
1368
|
+
return {
|
|
1369
|
+
admitted: failures.length === 0,
|
|
1370
|
+
failures
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
//#endregion
|
|
1160
1374
|
//#region lib/types/guards.js
|
|
1161
1375
|
/**
|
|
1162
1376
|
* The family's plain-object predicate, in ONE place (v46 G7.1 / audit A8).
|
|
@@ -3102,33 +3316,33 @@ function allowRowCollisions(env = process.env) {
|
|
|
3102
3316
|
* @module @lmzhen/dsh-evolution-core
|
|
3103
3317
|
*/
|
|
3104
3318
|
const EVOLUTION_ERRORS = Object.freeze({
|
|
3105
|
-
"e-305-this-invocation-carries-no": "E-305:
|
|
3106
|
-
"e-306-this-deployment-stages-foreground": "E-306:
|
|
3107
|
-
"e-301-approval-service-not-mounted": "E-301:
|
|
3108
|
-
"e-301-approval-service-not-mounted-2": "E-301:
|
|
3109
|
-
"e-304-the-mounted-approval-service": "E-304:
|
|
3110
|
-
"e-302-curator-service-not-mounted": "E-302:
|
|
3111
|
-
"e-305-the-invocation-agent-exposes": "E-305:
|
|
3112
|
-
"e-306-this-deployment-stages-foreground-2": "E-306:
|
|
3113
|
-
"e-303-replay-service-not-mounted": "E-303:
|
|
3114
|
-
"e-307-the-settings-service-could": "E-307:
|
|
3115
|
-
"e-308-unknown-parameter-group-group": "E-308:
|
|
3116
|
-
"e-311-no-settings-service-is": "E-311:
|
|
3117
|
-
"e-312-error-instanceof-Error-error": "E-312: %a1%
|
|
3118
|
-
"e-313-unknown-parameter-id-evolution": "E-313:
|
|
3119
|
-
"e-314-id-is-a-deployment": "E-314:
|
|
3120
|
-
"e-315-id-has-no-user": "E-315:
|
|
3121
|
-
"e-307-the-settings-service-could-2": "E-307:
|
|
3122
|
-
"e-316-namespace-namespace-is-not": "E-316: namespace \"%a1%\"
|
|
3123
|
-
"e-309-revision-conflict-you-sent": "E-309:
|
|
3124
|
-
"e-309-revision-conflict-message-Re": "E-309:
|
|
3125
|
-
"e-310-the-settings-service-refused": "E-310:
|
|
3126
|
-
"e-317-skill-write-not-confirmed": "E-317:
|
|
3127
|
-
"e-318-skill-write-without-a-read": "E-318:
|
|
3128
|
-
"e-320-the-profile-home-is-not": "E-320:
|
|
3129
|
-
"e-322-a-parameter-would-break-a": "E-322: %a1%
|
|
3130
|
-
"e-321-no-io-provider-is": "E-321:
|
|
3131
|
-
"e-319-skill-write-confirm-timed-out": "E-319:
|
|
3319
|
+
"e-305-this-invocation-carries-no": "E-305: 本次调用没有携带 agent —— `%a1%` 需要由会话支撑的调用(请在 GUI 或 CLI 的会话里运行)。",
|
|
3320
|
+
"e-306-this-deployment-stages-foreground": "E-306: 本部署会暂存前台写入,但 `/evolution %a1%` 无法通过技能运行器回放 —— 没有可暂存的内容。请在批准策略为 'never' 的会话里运行,或在 evolution-approval 行上设置 `stageForeground: false`,然后重试该命令。",
|
|
3321
|
+
"e-301-approval-service-not-mounted": "E-301: 批准服务未挂载:待批准写入无法列出或回放。挂载 evolution-approval 行(随 evolution-host/evolution-all 提供)后,运行 /evolution doctor 可查看已挂载的服务。",
|
|
3322
|
+
"e-301-approval-service-not-mounted-2": "E-301: 批准服务未挂载。下一步:evolution-approval 行随 evolution-host/evolution-all 一起提供 —— 运行 /evolution doctor 查看已挂载的服务。",
|
|
3323
|
+
"e-304-the-mounted-approval-service": "E-304: 已挂载的批准服务早于本能力发布 —— 请改为驳回该记录。",
|
|
3324
|
+
"e-302-curator-service-not-mounted": "E-302: 策展服务未挂载。下一步:挂载 evolution-curator 行(evolution-host/evolution-all),并运行 /evolution doctor。",
|
|
3325
|
+
"e-305-the-invocation-agent-exposes": "E-305: 调用 agent 既没有暴露 `followup` 也没有 `inject` —— %a1% 没有投递通道。",
|
|
3326
|
+
"e-306-this-deployment-stages-foreground-2": "E-306: 本部署会暂存前台写入,但本次调用没有 agent 会话 —— `/evolution restructure` 会暂存一条没有会话归属的记录。请在 GUI 或 CLI 的会话里运行,或在 `stageForeground: false` 行上设置,然后重试该命令。",
|
|
3327
|
+
"e-303-replay-service-not-mounted": "E-303: 回放服务未挂载。下一步:挂载 evolution-replay 行(evolution-host/evolution-all),并运行 /evolution doctor。",
|
|
3328
|
+
"e-307-the-settings-service-could": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution params 需要用户层来区分覆盖值与部署值;运行 /evolution doctor 查看已挂载的服务。",
|
|
3329
|
+
"e-308-unknown-parameter-group-group": "E-308: 未知的参数分组「%a1%」—— 已知分组:%a2%。",
|
|
3330
|
+
"e-311-no-settings-service-is": "E-311: 没有挂载设置服务,因此没有可写入的用户层。请挂载 settings 行(packages/settings/settings-file)后重试;本命令从不修改 cordis.yml —— 部署值属于部署。/evolution doctor 会列出已挂载的服务。",
|
|
3331
|
+
"e-312-error-instanceof-Error-error": "E-312: %a1% —— /evolution params 会列出规范 id。",
|
|
3332
|
+
"e-313-unknown-parameter-id-evolution": "E-313: 未知参数「%a1%」—— /evolution params 会列出每个已注册 id 及其层级与用户层。",
|
|
3333
|
+
"e-314-id-is-a-deployment": "E-314: 「%a1%」是部署参数(层级 %a2%,归属 %a3%)—— 它写在 cordis.yml 的插件行或策略行上,而不是从会话写入。/evolution params 会显示每一行由谁可写。",
|
|
3334
|
+
"e-315-id-has-no-user": "E-315: 「%a1%」没有用户层 —— 归属 %a2% 没有发布设置命名空间,因此该值留在部署层。",
|
|
3335
|
+
"e-307-the-settings-service-could-2": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution policy set 需要当前修订号才能安全写入;运行 /evolution doctor 查看已挂载的服务。",
|
|
3336
|
+
"e-316-namespace-namespace-is-not": "E-316: namespace \"%a1%\" 未注册 —— 归属 %a2% 在本组合中未挂载,因此无法从这里写入它的参数。 /evolution params 会把那些行标为 'unregistered'.",
|
|
3337
|
+
"e-309-revision-conflict-you-sent": "E-309: 修订冲突 —— 你提交的是 %a1%,文档当前为 %a2%。请用 /evolution params --json 重新读取后重试。",
|
|
3338
|
+
"e-309-revision-conflict-message-Re": "E-309: 修订冲突 —— %a1%。请用 /evolution params --json 重新读取后重试。",
|
|
3339
|
+
"e-310-the-settings-service-refused": "E-310: 设置服务拒绝了这次写入:%a1% —— 归属插件的规则优先(跨字段成对约束,或只能收紧的上限)。/evolution params 会显示当前值。",
|
|
3340
|
+
"e-317-skill-write-not-confirmed": "E-317: 技能 \"%a1%\" 未被%a2% —— 确认提示被拒绝或关闭,因此没有写入任何内容。每次创建与裸删除都会出现该提示;只有操作者要求时才重复调用。",
|
|
3341
|
+
"e-318-skill-write-without-a-read": "E-318: 本会话没有读取技能 \"%a1%\",因此拒绝这次写入。请先用 `skill` 工具读取它(失败的读取不算),然后重复该写入。",
|
|
3342
|
+
"e-320-the-profile-home-is-not": "E-320: 本宿主没有报告 profile home,因此无法定位旧设置文档。/evolution migrate 读取 `<profile home>/settings.yaml.imported`;请在由 profile 启动的会话里运行。",
|
|
3343
|
+
"e-322-a-parameter-would-break-a": "E-322: %a1% 至少应为 %a2%,但这次写入把它设成 %a3%。未写入任何内容。",
|
|
3344
|
+
"e-321-no-io-provider-is": "E-321: 没有挂载 evolution-io 提供方,因此无法读取旧设置文档。请挂载 evolution-io 行(evolution-host/evolution-all)后重试。",
|
|
3345
|
+
"e-319-skill-write-confirm-timed-out": "E-319: 技能 \"%a1%\" 未被%a2% —— 确认提示在 %a3% 秒内无人应答,因此该写入被取消。没有人在场决定:把 `skillWriteConfirm` 设为 `auto` 可免提示直接写入,保留 `ask` 则一直等到有人应答,或提高 `skillWriteConfirmTimeoutSeconds`。"
|
|
3132
3346
|
});
|
|
3133
3347
|
/**
|
|
3134
3348
|
* Render one table entry, substituting its interpolation slots.
|
|
@@ -3821,7 +4035,7 @@ var MemoryStore = class {
|
|
|
3821
4035
|
const chars = entries.join(ENTRY_DELIMITER).length;
|
|
3822
4036
|
if (this.failureCount > this.maxFailures) return {
|
|
3823
4037
|
ok: false,
|
|
3824
|
-
message:
|
|
4038
|
+
message: `本轮记忆整理已失败 ${this.failureCount} 次。请停止重试记忆调用,继续处理用户的任务。${previewEntries(entries)}`,
|
|
3825
4039
|
entries,
|
|
3826
4040
|
chars,
|
|
3827
4041
|
limit: this.limitFor(target)
|
|
@@ -3884,10 +4098,10 @@ var MemoryStore = class {
|
|
|
3884
4098
|
const oversized = await this.oversizedFile(target);
|
|
3885
4099
|
if (!oversized) return null;
|
|
3886
4100
|
const backup = await this.backupFile(target);
|
|
3887
|
-
const suffix = backup ? `
|
|
4101
|
+
const suffix = backup ? ` 已把备份保存到 ${basename(backup)}。` : "";
|
|
3888
4102
|
return {
|
|
3889
4103
|
ok: false,
|
|
3890
|
-
message:
|
|
4104
|
+
message: `记忆文件为 ${oversized.size} 字节(上限 ${oversized.limit * READ_GUARD_FACTOR})—— 跳过读取。${suffix} 请手动修复该文件后重试。`,
|
|
3891
4105
|
entries: [],
|
|
3892
4106
|
chars: 0,
|
|
3893
4107
|
limit: this.limitFor(target)
|
|
@@ -3920,7 +4134,7 @@ var MemoryStore = class {
|
|
|
3920
4134
|
const size = await this.io.size?.(fileFor(this.root, target));
|
|
3921
4135
|
if (typeof size === "number" && size > SHRINK_LOAD_CEILING) return {
|
|
3922
4136
|
ok: false,
|
|
3923
|
-
message:
|
|
4137
|
+
message: `记忆文件为 ${size} 字节 —— 即使只做删除批处理也过大、无法加载。请手动修复该文件后重试。`,
|
|
3924
4138
|
entries: [],
|
|
3925
4139
|
chars: 0,
|
|
3926
4140
|
limit: this.limitFor(target)
|
|
@@ -3934,7 +4148,7 @@ var MemoryStore = class {
|
|
|
3934
4148
|
});
|
|
3935
4149
|
return outcome ?? {
|
|
3936
4150
|
ok: false,
|
|
3937
|
-
message: "
|
|
4151
|
+
message: "内部错误:记忆写事务没有调用任务;未执行任何写入",
|
|
3938
4152
|
entries: [],
|
|
3939
4153
|
chars: 0,
|
|
3940
4154
|
limit: this.limitFor(target)
|
|
@@ -3946,7 +4160,7 @@ var MemoryStore = class {
|
|
|
3946
4160
|
async addChained(target, facts) {
|
|
3947
4161
|
if (!facts.trim()) return {
|
|
3948
4162
|
ok: false,
|
|
3949
|
-
message: "
|
|
4163
|
+
message: "内容不能为空。",
|
|
3950
4164
|
entries: [],
|
|
3951
4165
|
chars: 0,
|
|
3952
4166
|
limit: this.limitFor(target)
|
|
@@ -3963,7 +4177,7 @@ var MemoryStore = class {
|
|
|
3963
4177
|
if (!content) return {
|
|
3964
4178
|
result: {
|
|
3965
4179
|
ok: false,
|
|
3966
|
-
message: "
|
|
4180
|
+
message: "内容不能为空。",
|
|
3967
4181
|
entries: [],
|
|
3968
4182
|
chars: 0,
|
|
3969
4183
|
limit: this.limitFor(target)
|
|
@@ -3990,7 +4204,7 @@ var MemoryStore = class {
|
|
|
3990
4204
|
if (hasEntryDelimiter(prefixed)) return {
|
|
3991
4205
|
result: {
|
|
3992
4206
|
ok: false,
|
|
3993
|
-
message: "
|
|
4207
|
+
message: "该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。",
|
|
3994
4208
|
entries: [],
|
|
3995
4209
|
chars: 0,
|
|
3996
4210
|
limit: this.limitFor(target)
|
|
@@ -4003,7 +4217,7 @@ var MemoryStore = class {
|
|
|
4003
4217
|
return {
|
|
4004
4218
|
result: {
|
|
4005
4219
|
ok: true,
|
|
4006
|
-
message:
|
|
4220
|
+
message: `该条目已存在(未重复添加)。${this.storageHint(target, entries.join(ENTRY_DELIMITER).length)}`,
|
|
4007
4221
|
entries,
|
|
4008
4222
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4009
4223
|
limit: this.limitFor(target)
|
|
@@ -4015,14 +4229,14 @@ var MemoryStore = class {
|
|
|
4015
4229
|
const total = next.join(ENTRY_DELIMITER).length;
|
|
4016
4230
|
const addLimit = this.limitFor(target);
|
|
4017
4231
|
if (addLimit > 0 && total > addLimit) return {
|
|
4018
|
-
result: this.failure(target,
|
|
4232
|
+
result: this.failure(target, `添加该条目会超过 ${addLimit} 字符上限。请先合并或删除过期条目,再重试。`, entries),
|
|
4019
4233
|
write: null
|
|
4020
4234
|
};
|
|
4021
4235
|
this.resetFailures();
|
|
4022
4236
|
return {
|
|
4023
4237
|
result: {
|
|
4024
4238
|
ok: true,
|
|
4025
|
-
message:
|
|
4239
|
+
message: `已添加条目。${this.storageHint(target, total)}`,
|
|
4026
4240
|
entries: next,
|
|
4027
4241
|
chars: total,
|
|
4028
4242
|
limit: this.limitFor(target)
|
|
@@ -4099,10 +4313,9 @@ var MemoryStore = class {
|
|
|
4099
4313
|
if (shrinkOnly) return null;
|
|
4100
4314
|
const limit = this.limitFor(target);
|
|
4101
4315
|
const entries = normalizeEntries(raw);
|
|
4102
|
-
const over = entries.filter((entry) => entry.length > limit).length;
|
|
4103
4316
|
return {
|
|
4104
4317
|
ok: false,
|
|
4105
|
-
message: `${
|
|
4318
|
+
message: `${entries.filter((entry) => entry.length > limit).length} 条记忆超过了配置的 ${target}CharLimit(${limit});它们是在更高上限下写入的。请提高上限或删除条目 —— 删除类操作仍然可用。`,
|
|
4106
4319
|
entries,
|
|
4107
4320
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4108
4321
|
limit
|
|
@@ -4111,7 +4324,7 @@ var MemoryStore = class {
|
|
|
4111
4324
|
const backup = await this.backupFile(target);
|
|
4112
4325
|
return {
|
|
4113
4326
|
ok: false,
|
|
4114
|
-
message:
|
|
4327
|
+
message: `检测到记忆文件被外部改动。${backup ? ` 已把备份保存到 ${basename(backup)}。` : ""} 请先处理该漂移再重试。`,
|
|
4115
4328
|
entries: [],
|
|
4116
4329
|
chars: 0,
|
|
4117
4330
|
limit: this.limitFor(target)
|
|
@@ -4123,7 +4336,7 @@ var MemoryStore = class {
|
|
|
4123
4336
|
async applyBatchChained(target, operations) {
|
|
4124
4337
|
if (operations.length === 0) return {
|
|
4125
4338
|
ok: false,
|
|
4126
|
-
message: "operations
|
|
4339
|
+
message: "operations 列表为空。",
|
|
4127
4340
|
entries: [],
|
|
4128
4341
|
chars: 0,
|
|
4129
4342
|
limit: this.limitFor(target)
|
|
@@ -4148,7 +4361,7 @@ var MemoryStore = class {
|
|
|
4148
4361
|
if (!body) return {
|
|
4149
4362
|
result: {
|
|
4150
4363
|
ok: false,
|
|
4151
|
-
message:
|
|
4364
|
+
message: `第 ${position} 个操作(add):必须提供 facts。未应用任何操作。${previewEntries(entries)}`,
|
|
4152
4365
|
entries,
|
|
4153
4366
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4154
4367
|
limit: this.limitFor(target)
|
|
@@ -4159,7 +4372,7 @@ var MemoryStore = class {
|
|
|
4159
4372
|
if (threat) return {
|
|
4160
4373
|
result: {
|
|
4161
4374
|
ok: false,
|
|
4162
|
-
message:
|
|
4375
|
+
message: `第 ${position} 个操作:${threat}${previewEntries(entries)}`,
|
|
4163
4376
|
entries,
|
|
4164
4377
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4165
4378
|
limit: this.limitFor(target)
|
|
@@ -4170,7 +4383,7 @@ var MemoryStore = class {
|
|
|
4170
4383
|
if (hasEntryDelimiter(entryBody)) return {
|
|
4171
4384
|
result: {
|
|
4172
4385
|
ok: false,
|
|
4173
|
-
message:
|
|
4386
|
+
message: `第 ${position} 个操作(add):该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。${previewEntries(entries)}`,
|
|
4174
4387
|
entries,
|
|
4175
4388
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4176
4389
|
limit: this.limitFor(target)
|
|
@@ -4184,7 +4397,7 @@ var MemoryStore = class {
|
|
|
4184
4397
|
if (rawAction !== "remove" && rawAction !== "replace") return {
|
|
4185
4398
|
result: {
|
|
4186
4399
|
ok: false,
|
|
4187
|
-
message:
|
|
4400
|
+
message: `第 ${position} 个操作:未知动作「${String(rawAction)}」(应为 add/remove/replace)。未应用任何操作。${previewEntries(entries)}`,
|
|
4188
4401
|
entries,
|
|
4189
4402
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4190
4403
|
limit: this.limitFor(target)
|
|
@@ -4195,7 +4408,7 @@ var MemoryStore = class {
|
|
|
4195
4408
|
if (!needle) return {
|
|
4196
4409
|
result: {
|
|
4197
4410
|
ok: false,
|
|
4198
|
-
message:
|
|
4411
|
+
message: `第 ${position} 个操作(${op.action}):必须提供 old_text。未应用任何操作。${previewEntries(entries)}`,
|
|
4199
4412
|
entries,
|
|
4200
4413
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4201
4414
|
limit: this.limitFor(target)
|
|
@@ -4207,13 +4420,13 @@ var MemoryStore = class {
|
|
|
4207
4420
|
matchIndex
|
|
4208
4421
|
})).filter(({ entry }) => entry.includes(needle));
|
|
4209
4422
|
if (matches.length === 0) return {
|
|
4210
|
-
result: this.failure(target,
|
|
4423
|
+
result: this.failure(target, `第 ${position} 个操作:找不到匹配 "${needle}" 的条目。未应用任何操作。`, entries),
|
|
4211
4424
|
write: null
|
|
4212
4425
|
};
|
|
4213
4426
|
if (new Set(matches.map((m) => m.entry)).size > 1) return {
|
|
4214
4427
|
result: {
|
|
4215
4428
|
ok: false,
|
|
4216
|
-
message:
|
|
4429
|
+
message: `第 ${position} 个操作:「${needle}」匹配到多条不同条目。未应用任何操作。${previewEntries(entries)}`,
|
|
4217
4430
|
entries,
|
|
4218
4431
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4219
4432
|
limit: this.limitFor(target)
|
|
@@ -4227,7 +4440,7 @@ var MemoryStore = class {
|
|
|
4227
4440
|
if (!body) return {
|
|
4228
4441
|
result: {
|
|
4229
4442
|
ok: false,
|
|
4230
|
-
message:
|
|
4443
|
+
message: `第 ${position} 个操作(replace):必须提供 facts。${previewEntries(entries)}`,
|
|
4231
4444
|
entries,
|
|
4232
4445
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4233
4446
|
limit: this.limitFor(target)
|
|
@@ -4238,7 +4451,7 @@ var MemoryStore = class {
|
|
|
4238
4451
|
if (threat) return {
|
|
4239
4452
|
result: {
|
|
4240
4453
|
ok: false,
|
|
4241
|
-
message:
|
|
4454
|
+
message: `第 ${position} 个操作:${threat}${previewEntries(entries)}`,
|
|
4242
4455
|
entries,
|
|
4243
4456
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4244
4457
|
limit: this.limitFor(target)
|
|
@@ -4249,7 +4462,7 @@ var MemoryStore = class {
|
|
|
4249
4462
|
if (hasEntryDelimiter(entryBody)) return {
|
|
4250
4463
|
result: {
|
|
4251
4464
|
ok: false,
|
|
4252
|
-
message:
|
|
4465
|
+
message: `第 ${position} 个操作(replace):该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。${previewEntries(entries)}`,
|
|
4253
4466
|
entries,
|
|
4254
4467
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4255
4468
|
limit: this.limitFor(target)
|
|
@@ -4262,14 +4475,14 @@ var MemoryStore = class {
|
|
|
4262
4475
|
const total = working.join(ENTRY_DELIMITER).length;
|
|
4263
4476
|
const batchLimit = this.limitFor(target);
|
|
4264
4477
|
if (batchLimit > 0 && total > batchLimit) return {
|
|
4265
|
-
result: this.failure(target,
|
|
4478
|
+
result: this.failure(target, `批处理结果(${total} 字符)超过 ${batchLimit} 上限。请在同一个批里再删除或缩短一些条目。`, entries),
|
|
4266
4479
|
write: null
|
|
4267
4480
|
};
|
|
4268
4481
|
this.resetFailures();
|
|
4269
4482
|
return {
|
|
4270
4483
|
result: {
|
|
4271
4484
|
ok: true,
|
|
4272
|
-
message:
|
|
4485
|
+
message: `已应用 ${operations.length} 个操作。${this.storageHint(target, total)}`,
|
|
4273
4486
|
entries: working,
|
|
4274
4487
|
chars: total,
|
|
4275
4488
|
limit: this.limitFor(target)
|
|
@@ -5961,7 +6174,7 @@ function newRunRegistry(options) {
|
|
|
5961
6174
|
const parsed = parseIndex(raw);
|
|
5962
6175
|
if (!parsed.ok) return {
|
|
5963
6176
|
ok: false,
|
|
5964
|
-
note: "
|
|
6177
|
+
note: "运行索引读不出来(不是 JSON,或没有 runs 数组)—— 上一次的运行历史未知(报告不受影响)"
|
|
5965
6178
|
};
|
|
5966
6179
|
const wasRunning = new Set([...foreign.values()].filter((row) => row.state === "running").map((row) => row.id));
|
|
5967
6180
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6672,13 +6885,35 @@ const HEALTH_STAMP_RE = new RegExp(String.raw`\brc\.\d+\b|\b(?=[0-9a-f]{7,40}\b)
|
|
|
6672
6885
|
* warned on a perfectly healthy body (audit 2026-08-31 X1).
|
|
6673
6886
|
*/
|
|
6674
6887
|
const MIN_STAMP_BODY_CHARS = 2e3;
|
|
6888
|
+
/**
|
|
6889
|
+
* The findings for ONE body, computed by the engine that owns the judgement (G4).
|
|
6890
|
+
*
|
|
6891
|
+
* Deliberately a thin call into `assessStructureHealth`: a later consumer (the write receipt) needs
|
|
6892
|
+
* findings for a body it is about to store, and re-deriving the facts anywhere else would be a
|
|
6893
|
+
* SECOND judgement — the token-scale line, the /1024 stamp density and the soft/2 support threshold
|
|
6894
|
+
* all live in one place and must stay there.
|
|
6895
|
+
* @param skillName - the skill the body belongs to (used in messages).
|
|
6896
|
+
* @param content - the WHOLE SKILL.md text, frontmatter included (the engine's own convention).
|
|
6897
|
+
* @param supportGroups - non-empty support dirs; pass the real count, never a guess.
|
|
6898
|
+
* @param thresholds - the configured thresholds; defaults to the shipped ones.
|
|
6899
|
+
* @returns the findings, in rule order.
|
|
6900
|
+
*/
|
|
6901
|
+
function lintBody(skillName, content, supportGroups, thresholds = DEFAULT_HEALTH_THRESHOLDS) {
|
|
6902
|
+
return assessStructureHealth({
|
|
6903
|
+
skillName,
|
|
6904
|
+
bodyChars: content.length,
|
|
6905
|
+
bodyText: content,
|
|
6906
|
+
supportGroups
|
|
6907
|
+
}, thresholds).findings;
|
|
6908
|
+
}
|
|
6675
6909
|
function assessStructureHealth(snapshot, thresholds = DEFAULT_HEALTH_THRESHOLDS) {
|
|
6676
|
-
const reasons = [];
|
|
6677
6910
|
const dims = {
|
|
6678
6911
|
bodyChars: snapshot.bodyChars,
|
|
6679
6912
|
bodyTokens: Math.round(snapshot.bodyChars / UPSTREAM_LIMIT_CHARS_PER_TOKEN),
|
|
6680
6913
|
stampDensityPerKb: null,
|
|
6914
|
+
stampHits: null,
|
|
6681
6915
|
supportGroups: snapshot.supportGroups,
|
|
6916
|
+
supportFiles: snapshot.supportFiles ?? null,
|
|
6682
6917
|
churnPatches: null,
|
|
6683
6918
|
churnReads: null
|
|
6684
6919
|
};
|
|
@@ -6688,25 +6923,30 @@ function assessStructureHealth(snapshot, thresholds = DEFAULT_HEALTH_THRESHOLDS)
|
|
|
6688
6923
|
dims.bodyTokens = cost === null ? Math.round(snapshot.bodyChars / UPSTREAM_LIMIT_CHARS_PER_TOKEN) : cost.tokens;
|
|
6689
6924
|
const overLine = cost === null || lineTokens === null ? snapshot.bodyChars >= thresholds.softBodyChars : cost.tokens >= lineTokens;
|
|
6690
6925
|
const overTwice = cost === null || lineTokens === null ? snapshot.bodyChars >= thresholds.softBodyChars * 2 : cost.tokens >= lineTokens * 2;
|
|
6691
|
-
const scale = cost === null || lineTokens === null ? `${snapshot.bodyChars} chars` : `${cost.tokens} tokens (${snapshot.bodyChars} chars)`;
|
|
6692
|
-
const lineText = cost === null || lineTokens === null ? `${thresholds.softBodyChars} chars` : `${thresholds.softBodyChars} chars = ${lineTokens} tokens`;
|
|
6693
|
-
if (overTwice) reasons.push(`body ${scale} is >= 2x the soft line (${lineText}) — consider splitting or offloading`);
|
|
6694
|
-
else if (overLine) reasons.push(`body ${scale} at or above the soft line (${lineText})`);
|
|
6695
6926
|
if (snapshot.bodyText && snapshot.bodyChars >= 2e3) {
|
|
6696
6927
|
const kb = Math.max(1, snapshot.bodyChars / 1024);
|
|
6697
|
-
|
|
6698
|
-
|
|
6928
|
+
const stamps = (snapshot.bodyText.match(HEALTH_STAMP_RE) ?? []).length;
|
|
6929
|
+
dims.stampDensityPerKb = stamps / kb;
|
|
6930
|
+
dims.stampHits = stamps;
|
|
6699
6931
|
}
|
|
6700
|
-
if (snapshot.supportGroups === 0 && snapshot.bodyChars >= thresholds.softBodyChars / 2) reasons.push(`large body (${snapshot.bodyChars} chars) with NO support files — session detail may belong in references/`);
|
|
6701
6932
|
if (snapshot.patchCount !== void 0 && snapshot.readCount !== void 0) {
|
|
6702
6933
|
dims.churnPatches = snapshot.patchCount;
|
|
6703
6934
|
dims.churnReads = snapshot.readCount;
|
|
6704
|
-
if (snapshot.patchCount >= thresholds.churnMinPatches && snapshot.readCount === 0) reasons.push(`patched ${snapshot.patchCount} times but never read (write-ghost — content may be dead)`);
|
|
6705
6935
|
}
|
|
6936
|
+
const findings = lintSkill({
|
|
6937
|
+
skillName: snapshot.skillName,
|
|
6938
|
+
dims,
|
|
6939
|
+
softLineChars: thresholds.softBodyChars,
|
|
6940
|
+
overSoftLine: overLine,
|
|
6941
|
+
overTwiceSoftLine: overTwice,
|
|
6942
|
+
stampLimitPerKb: thresholds.stampDensityPerKb,
|
|
6943
|
+
churnMinPatches: thresholds.churnMinPatches,
|
|
6944
|
+
readable: true
|
|
6945
|
+
});
|
|
6706
6946
|
return {
|
|
6707
|
-
verdict:
|
|
6947
|
+
verdict: findings.some((f) => f.rule === "body-oversize-2x") ? "needs-restructure" : findings.length > 0 ? "warn" : "healthy",
|
|
6708
6948
|
dims,
|
|
6709
|
-
|
|
6949
|
+
findings
|
|
6710
6950
|
};
|
|
6711
6951
|
}
|
|
6712
6952
|
//#endregion
|
|
@@ -7981,6 +8221,50 @@ function readingStats(full, shown) {
|
|
|
7981
8221
|
truncated: shown.length < full.length
|
|
7982
8222
|
};
|
|
7983
8223
|
}
|
|
8224
|
+
/**
|
|
8225
|
+
* The H2 sections of a body, in document order — the ONE scan (G3).
|
|
8226
|
+
*
|
|
8227
|
+
* Both the restructure candidate list and the store anchor validation call this function, so a
|
|
8228
|
+
* candidate can only name a heading that exists EXACTLY ONCE and has content: the two rejections a
|
|
8229
|
+
* hand-written anchor used to hit ("no such heading", "ambiguous anchor") stop being reachable.
|
|
8230
|
+
* H2 is the section boundary (a section spans to the next H2 or EOF), matching the store.
|
|
8231
|
+
* @param body - the markdown after the frontmatter block.
|
|
8232
|
+
* @returns one row per H2 heading, with `count` filled in a second pass.
|
|
8233
|
+
*/
|
|
8234
|
+
function h2Sections(body) {
|
|
8235
|
+
const lines = body.split(String.fromCharCode(10));
|
|
8236
|
+
const rows = [];
|
|
8237
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
8238
|
+
const match = /^#{2}\s+(.+?)\s*$/.exec(lines[i] ?? "");
|
|
8239
|
+
if (match === null) continue;
|
|
8240
|
+
const title = (match[1] ?? "").trim();
|
|
8241
|
+
let end = lines.length;
|
|
8242
|
+
for (let j = i + 1; j < lines.length; j += 1) if (/^#{2}\s/.test(lines[j] ?? "")) {
|
|
8243
|
+
end = j;
|
|
8244
|
+
break;
|
|
8245
|
+
}
|
|
8246
|
+
rows.push({
|
|
8247
|
+
title,
|
|
8248
|
+
start: i,
|
|
8249
|
+
end,
|
|
8250
|
+
count: 0
|
|
8251
|
+
});
|
|
8252
|
+
}
|
|
8253
|
+
for (const row of rows) row.count = rows.filter((other) => other.title === row.title).length;
|
|
8254
|
+
return rows;
|
|
8255
|
+
}
|
|
8256
|
+
/**
|
|
8257
|
+
* The sections a restructure proposal may name: heading unique, section non-empty. Derived from
|
|
8258
|
+
* `h2Sections`, i.e. from the SAME scan the store validates against.
|
|
8259
|
+
* @param body - the markdown after the frontmatter block.
|
|
8260
|
+
* @returns move-ready pairs in document order; empty when nothing qualifies.
|
|
8261
|
+
*/
|
|
8262
|
+
function restructureCandidates(body) {
|
|
8263
|
+
return h2Sections(body).filter((section) => section.count === 1 && section.end > section.start + 1).map((section) => ({
|
|
8264
|
+
heading: section.title,
|
|
8265
|
+
toFile: "references/" + section.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/g, "-").replace(/^-+|-+$/g, "") + ".md"
|
|
8266
|
+
}));
|
|
8267
|
+
}
|
|
7984
8268
|
//#endregion
|
|
7985
8269
|
//#region lib/types/drift-signals.js
|
|
7986
8270
|
/**
|
|
@@ -8579,7 +8863,7 @@ function contentRetentionFeedback(input) {
|
|
|
8579
8863
|
if (before === null || after === null) return null;
|
|
8580
8864
|
if (before.length < 200) return null;
|
|
8581
8865
|
if (after.length >= before.length * .5) return null;
|
|
8582
|
-
return
|
|
8866
|
+
return `新内容保留了上一版正文的 ${Math.round(after.length / before.length * 100)}%(${after.length}/${before.length} 字符);被替换的版本已保留在该技能的历史里。`;
|
|
8583
8867
|
}
|
|
8584
8868
|
/**
|
|
8585
8869
|
* Read one skill's versions, oldest first.
|
|
@@ -8947,7 +9231,7 @@ function anchorRefusal(name, verdict) {
|
|
|
8947
9231
|
ok: false,
|
|
8948
9232
|
stale: true,
|
|
8949
9233
|
anchor: verdict,
|
|
8950
|
-
message: verdict === "missing" ?
|
|
9234
|
+
message: verdict === "missing" ? `技能「${name}」不存在。` : `技能 "${name}" 在读取之后被改动过;为避免覆盖更新的内容,本次写入被拒绝。`
|
|
8951
9235
|
};
|
|
8952
9236
|
}
|
|
8953
9237
|
/**
|
|
@@ -8970,7 +9254,7 @@ function anchorUnverifiable(name, filePath) {
|
|
|
8970
9254
|
ok: false,
|
|
8971
9255
|
stale: true,
|
|
8972
9256
|
anchor: "drift",
|
|
8973
|
-
message: filePath === null ?
|
|
9257
|
+
message: filePath === null ? `无法读取技能 "${name}" 以校验暂存内容。` : `无法读取技能 "${name}" 的支撑文件 "${filePath}" 以校验暂存内容。`
|
|
8974
9258
|
};
|
|
8975
9259
|
}
|
|
8976
9260
|
function anchorRefusalFile(name, filePath, verdict) {
|
|
@@ -8978,7 +9262,7 @@ function anchorRefusalFile(name, filePath, verdict) {
|
|
|
8978
9262
|
ok: false,
|
|
8979
9263
|
stale: true,
|
|
8980
9264
|
anchor: verdict,
|
|
8981
|
-
message: verdict === "missing" ?
|
|
9265
|
+
message: verdict === "missing" ? `文件「${filePath}」不在技能「${name}」里。` : `技能 "${name}" 的支撑文件 "${filePath}" 在读取之后被改动过;为避免覆盖更新的内容,本次写入被拒绝。`
|
|
8982
9266
|
};
|
|
8983
9267
|
}
|
|
8984
9268
|
/**
|
|
@@ -8996,7 +9280,7 @@ function anchorRefusalFile(name, filePath, verdict) {
|
|
|
8996
9280
|
function unreadableTarget(label, error) {
|
|
8997
9281
|
return {
|
|
8998
9282
|
ok: false,
|
|
8999
|
-
message:
|
|
9283
|
+
message: `读不出「${label}」:${error?.code === "EISDIR" ? "the path is a DIRECTORY, not a file" : error instanceof Error ? error.message : String(error)}。没有写入任何内容。`
|
|
9000
9284
|
};
|
|
9001
9285
|
}
|
|
9002
9286
|
/** Upper bound of moves per restructure proposal (validator and core agree). */
|
|
@@ -9180,15 +9464,15 @@ function validateRestructureTarget(filePath) {
|
|
|
9180
9464
|
}
|
|
9181
9465
|
function validateSupportPath(filePath) {
|
|
9182
9466
|
const normalized = filePath.replace(/\\/g, "/");
|
|
9183
|
-
if (normalized.includes("..")) return "
|
|
9467
|
+
if (normalized.includes("..")) return "不允许路径穿越。";
|
|
9184
9468
|
const parts = normalized.split("/").filter(Boolean);
|
|
9185
9469
|
if (parts.length === 0 || !SUPPORT_DIRS.includes(parts[0])) return `file_path must be under one of: ${SUPPORT_DIRS.join(", ")}.`;
|
|
9186
|
-
if (parts.length < 2) return "
|
|
9470
|
+
if (parts.length < 2) return "请给出文件名,不能只给目录。";
|
|
9187
9471
|
for (const part of parts.slice(1)) {
|
|
9188
|
-
if (!SUPPORT_FILE_NAME_RE.test(part)) return
|
|
9189
|
-
if (part.toLowerCase().endsWith(".corrupt") || part.toLowerCase().endsWith(".tmp")) return
|
|
9472
|
+
if (!SUPPORT_FILE_NAME_RE.test(part)) return `不支持的文件名「${part}」:请只用小写字母、数字、点、连字符和下划线(首字符须为字母或数字)。`;
|
|
9473
|
+
if (part.toLowerCase().endsWith(".corrupt") || part.toLowerCase().endsWith(".tmp")) return `不支持的文件名「${part}」:.corrupt/.tmp 后缀由状态与 IO 协议保留。`;
|
|
9190
9474
|
const stem = part.split(".")[0]?.toLowerCase() ?? "";
|
|
9191
|
-
if (WIN32_RESERVED_DEVICE_NAMES.has(stem)) return
|
|
9475
|
+
if (WIN32_RESERVED_DEVICE_NAMES.has(stem)) return `不支持的文件名「${part}」:这是 Windows 保留设备名。`;
|
|
9192
9476
|
}
|
|
9193
9477
|
return null;
|
|
9194
9478
|
}
|
|
@@ -9214,19 +9498,16 @@ function supportRefs(content) {
|
|
|
9214
9498
|
}
|
|
9215
9499
|
function planRestructureSections(body, moves) {
|
|
9216
9500
|
const lines = body.split("\n");
|
|
9501
|
+
const sections = h2Sections(body);
|
|
9217
9502
|
const spans = [];
|
|
9218
9503
|
for (const move of moves) {
|
|
9219
9504
|
const wanted = move.heading.trim();
|
|
9220
|
-
const
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
if (
|
|
9225
|
-
|
|
9226
|
-
for (let i = start + 1; i < lines.length; i += 1) if (/^#{2}\s/.test(lines[i] ?? "")) {
|
|
9227
|
-
end = i;
|
|
9228
|
-
break;
|
|
9229
|
-
}
|
|
9505
|
+
const matching = sections.filter((section) => section.title === wanted);
|
|
9506
|
+
if (matching.length === 0) return { error: `no "## ${wanted}" heading in the body` };
|
|
9507
|
+
if (matching.length > 1) return { error: `heading "## ${wanted}" appears ${matching.length} times (ambiguous anchor)` };
|
|
9508
|
+
const section = matching[0];
|
|
9509
|
+
if (section === void 0) return { error: `no "## ${wanted}" heading in the body` };
|
|
9510
|
+
const { start, end } = section;
|
|
9230
9511
|
if (end === start + 1) return { error: `heading "## ${wanted}" has an empty section` };
|
|
9231
9512
|
if (spans.some((span) => start === span.start)) return { error: `heading "## ${wanted}" is moved twice` };
|
|
9232
9513
|
spans.push({
|
|
@@ -9337,7 +9618,7 @@ var SkillLibrary = class {
|
|
|
9337
9618
|
const o = outcome;
|
|
9338
9619
|
if (o === void 0 || typeof o !== "object" || !Object.prototype.hasOwnProperty.call(o, "write")) return {
|
|
9339
9620
|
ok: false,
|
|
9340
|
-
message: "
|
|
9621
|
+
message: "内部错误:写事务没有调用任务;未执行任何写入"
|
|
9341
9622
|
};
|
|
9342
9623
|
if (o.ghostDir === true) await this.cleanupGhostDir(path);
|
|
9343
9624
|
let result = o.result;
|
|
@@ -9357,7 +9638,7 @@ var SkillLibrary = class {
|
|
|
9357
9638
|
if (o.write !== null && o.event) this.notifyMutation(o.event);
|
|
9358
9639
|
return durabilityWarning === "" || !result.ok ? result : {
|
|
9359
9640
|
...result,
|
|
9360
|
-
message: `${result.message}
|
|
9641
|
+
message: `${result.message} (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`
|
|
9361
9642
|
};
|
|
9362
9643
|
}
|
|
9363
9644
|
/**
|
|
@@ -9520,7 +9801,7 @@ var SkillLibrary = class {
|
|
|
9520
9801
|
*/
|
|
9521
9802
|
badName(name, opts = {}) {
|
|
9522
9803
|
const normalized = name.trim();
|
|
9523
|
-
if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return
|
|
9804
|
+
if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return `技能名「${normalized}」不合法:请只用小写字母、数字和连字符(不超过 ${this.limits.maxNameLength} 个)。`;
|
|
9524
9805
|
if (!opts.allowReserved && WIN32_RESERVED_DEVICE_NAMES.has(normalized)) return `"${normalized}" is a Windows reserved device name and cannot be used as a skill name.`;
|
|
9525
9806
|
return null;
|
|
9526
9807
|
}
|
|
@@ -9573,6 +9854,32 @@ var SkillLibrary = class {
|
|
|
9573
9854
|
return await this.io.exists(markerPath(dir, "pinned"));
|
|
9574
9855
|
}
|
|
9575
9856
|
/** Count non-empty support subdirectories (richness input for quality scoring). */
|
|
9857
|
+
/** G4 ⑤-d: the dangling targets THIS write introduced, as a receipt note (empty when none).
|
|
9858
|
+
*
|
|
9859
|
+
* Uses the SAME resolver as the drift signal, so a receipt and a drift row can never disagree;
|
|
9860
|
+
* an absent or unknown listing reports nothing rather than inventing a dead link.
|
|
9861
|
+
*/
|
|
9862
|
+
async danglingNoteFor(skillName, before, after) {
|
|
9863
|
+
try {
|
|
9864
|
+
const listing = await this.listSupportFiles(skillName);
|
|
9865
|
+
if (!isPresent(listing)) return "";
|
|
9866
|
+
const files = listing.value;
|
|
9867
|
+
const owner = "SKILL.md";
|
|
9868
|
+
const seen = new Set(resolveCitations({
|
|
9869
|
+
content: before,
|
|
9870
|
+
file: owner,
|
|
9871
|
+
files
|
|
9872
|
+
}).dangling.map((ref) => ref.target ?? ref.raw));
|
|
9873
|
+
const introduced = resolveCitations({
|
|
9874
|
+
content: after,
|
|
9875
|
+
file: owner,
|
|
9876
|
+
files
|
|
9877
|
+
}).dangling.map((ref) => ref.target ?? ref.raw).filter((target) => !seen.has(target));
|
|
9878
|
+
return introduced.length > 0 ? " [dangling: " + introduced.join(",") + "]" : "";
|
|
9879
|
+
} catch {
|
|
9880
|
+
return "";
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9576
9883
|
async countSupportDirs(rawName) {
|
|
9577
9884
|
const name = rawName.trim();
|
|
9578
9885
|
if (this.badName(name) !== null) return 0;
|
|
@@ -9698,7 +10005,7 @@ var SkillLibrary = class {
|
|
|
9698
10005
|
* mutation ledger that points at it. The history failure is not silent — a deployment whose
|
|
9699
10006
|
* history stopped growing is a degradation worth seeing once (the ledger keeps hashes either
|
|
9700
10007
|
* way). */
|
|
9701
|
-
async audit(skillName, action, before, after, summary, path) {
|
|
10008
|
+
async audit(skillName, action, before, after, summary, path, origin) {
|
|
9702
10009
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
9703
10010
|
let versions = null;
|
|
9704
10011
|
try {
|
|
@@ -9718,6 +10025,18 @@ var SkillLibrary = class {
|
|
|
9718
10025
|
console.warn(`skill-store: content history not recorded for "${skillName}" (${error instanceof Error ? error.message : String(error)}); the mutation itself landed and the audit ledger keeps its hashes`);
|
|
9719
10026
|
}
|
|
9720
10027
|
}
|
|
10028
|
+
let healthNote = "";
|
|
10029
|
+
const danglingNote = before === null || after === null || before === after ? "" : await this.danglingNoteFor(skillName, before, after);
|
|
10030
|
+
if (before !== null && after !== null && before !== after) try {
|
|
10031
|
+
const groups = await this.countSupportDirs(skillName);
|
|
10032
|
+
const delta = diffFindings(lintBody(skillName, before, groups), lintBody(skillName, after, groups));
|
|
10033
|
+
const parts = [];
|
|
10034
|
+
if (delta.introduced.length > 0) parts.push("+" + delta.introduced.map((f) => f.rule).join(","));
|
|
10035
|
+
if (delta.worsened.length > 0) parts.push("^" + delta.worsened.map((f) => f.rule + " x" + String(f.count)).join(","));
|
|
10036
|
+
if (parts.length > 0) healthNote = " [health: " + parts.join(" ") + "]";
|
|
10037
|
+
} catch {
|
|
10038
|
+
healthNote = "";
|
|
10039
|
+
}
|
|
9721
10040
|
try {
|
|
9722
10041
|
await recordMutation(this.root, this.io, {
|
|
9723
10042
|
skillName,
|
|
@@ -9726,8 +10045,9 @@ var SkillLibrary = class {
|
|
|
9726
10045
|
...after === null ? {} : { afterHash: contentHash(after) },
|
|
9727
10046
|
...versions?.beforeVersion === void 0 ? {} : { beforeVersion: versions.beforeVersion },
|
|
9728
10047
|
...versions?.afterVersion === void 0 ? {} : { afterVersion: versions.afterVersion },
|
|
9729
|
-
summary,
|
|
9730
|
-
at
|
|
10048
|
+
summary: summary + healthNote + danglingNote,
|
|
10049
|
+
at,
|
|
10050
|
+
...origin === void 0 ? {} : { origin }
|
|
9731
10051
|
});
|
|
9732
10052
|
} catch {}
|
|
9733
10053
|
}
|
|
@@ -9779,7 +10099,7 @@ var SkillLibrary = class {
|
|
|
9779
10099
|
};
|
|
9780
10100
|
if (origin === "background_review") return {
|
|
9781
10101
|
ok: false,
|
|
9782
|
-
message: "
|
|
10102
|
+
message: "只有前台(用户或主智能体)可以固定或取消固定技能。"
|
|
9783
10103
|
};
|
|
9784
10104
|
let durabilityWarning = "";
|
|
9785
10105
|
const dir = this.dirOf(normalized);
|
|
@@ -9787,17 +10107,17 @@ var SkillLibrary = class {
|
|
|
9787
10107
|
const existing = await this.io.exists(marker);
|
|
9788
10108
|
if (pinned && existing) return {
|
|
9789
10109
|
ok: true,
|
|
9790
|
-
message:
|
|
10110
|
+
message: `技能「${normalized}」已被固定。`,
|
|
9791
10111
|
path: dir
|
|
9792
10112
|
};
|
|
9793
10113
|
if (!pinned && !existing) return {
|
|
9794
10114
|
ok: true,
|
|
9795
|
-
message:
|
|
10115
|
+
message: `技能「${normalized}」未被固定,无需处理。`,
|
|
9796
10116
|
path: dir
|
|
9797
10117
|
};
|
|
9798
10118
|
if (!await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
9799
10119
|
ok: false,
|
|
9800
|
-
message:
|
|
10120
|
+
message: `技能「${normalized}」不存在。`
|
|
9801
10121
|
};
|
|
9802
10122
|
if (pinned) {
|
|
9803
10123
|
try {
|
|
@@ -9811,14 +10131,14 @@ var SkillLibrary = class {
|
|
|
9811
10131
|
await this.cleanupGhostDir(join(dir, "SKILL.md"));
|
|
9812
10132
|
return {
|
|
9813
10133
|
ok: false,
|
|
9814
|
-
message:
|
|
10134
|
+
message: `固定过程中技能「${normalized}」被并发归档;残留标记已移除:等搬运完成后重试。`
|
|
9815
10135
|
};
|
|
9816
10136
|
}
|
|
9817
10137
|
} else await this.io.remove(marker);
|
|
9818
10138
|
await this.audit(normalized, pinned ? "pin" : "unpin", null, null, pinned ? "pinned" : "unpinned");
|
|
9819
10139
|
return {
|
|
9820
10140
|
ok: true,
|
|
9821
|
-
message: `${pinned ?
|
|
10141
|
+
message: `${pinned ? `技能 "${normalized}" 已固定:免受删除、后台复核与生命周期影响。` : `技能 "${normalized}" 已取消固定。`}${durabilityWarning === "" ? "" : ` (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`}`,
|
|
9822
10142
|
path: dir
|
|
9823
10143
|
};
|
|
9824
10144
|
}
|
|
@@ -9840,7 +10160,7 @@ var SkillLibrary = class {
|
|
|
9840
10160
|
const norm = normalizeFrontmatter(content);
|
|
9841
10161
|
if (norm.issues.length > 0) return {
|
|
9842
10162
|
ok: false,
|
|
9843
|
-
message:
|
|
10163
|
+
message: `技能「${normalized}」的 frontmatter 无法自动修复:${norm.issues[0]}:请用双引号包住取值后重试。`
|
|
9844
10164
|
};
|
|
9845
10165
|
const finalContent = norm.changed ? norm.content : content;
|
|
9846
10166
|
if (norm.changed) {
|
|
@@ -9858,7 +10178,7 @@ var SkillLibrary = class {
|
|
|
9858
10178
|
const dir = this.dirOf(normalized);
|
|
9859
10179
|
if (await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
9860
10180
|
ok: false,
|
|
9861
|
-
message:
|
|
10181
|
+
message: `技能「${normalized}」已存在。`
|
|
9862
10182
|
};
|
|
9863
10183
|
let rootEntries;
|
|
9864
10184
|
try {
|
|
@@ -9867,17 +10187,17 @@ var SkillLibrary = class {
|
|
|
9867
10187
|
const cause = error instanceof Error ? error.message : String(error);
|
|
9868
10188
|
return {
|
|
9869
10189
|
ok: false,
|
|
9870
|
-
message:
|
|
10190
|
+
message: `无法检查技能「${normalized}」是否与大小写变体冲突:列出 ${this.root} 失败(${cause})。拒绝创建 —— 仅大小写不同的第二个目录在跨平台时无法撤销。`
|
|
9871
10191
|
};
|
|
9872
10192
|
}
|
|
9873
10193
|
for (const entry of rootEntries) if (typeof entry === "string" && entry !== normalized && entry.toLowerCase() === normalized.toLowerCase()) return {
|
|
9874
10194
|
ok: false,
|
|
9875
|
-
message:
|
|
10195
|
+
message: `技能「${normalized}」与已存在的大小写变体目录「${entry}」冲突(技能名只允许小写):请重命名其中一个。`
|
|
9876
10196
|
};
|
|
9877
10197
|
const protection = await this.writeProtection(normalized, origin);
|
|
9878
10198
|
if (protection) return {
|
|
9879
10199
|
ok: false,
|
|
9880
|
-
message:
|
|
10200
|
+
message: `技能「${normalized}」受保护(${protection})。`
|
|
9881
10201
|
};
|
|
9882
10202
|
const onDisk = finalContent.trimEnd() + "\n";
|
|
9883
10203
|
const createPath = join(dir, "SKILL.md");
|
|
@@ -9912,18 +10232,18 @@ var SkillLibrary = class {
|
|
|
9912
10232
|
}
|
|
9913
10233
|
if (!taskRan) return {
|
|
9914
10234
|
ok: false,
|
|
9915
|
-
message: "
|
|
10235
|
+
message: "内部错误:创建事务没有调用任务;未写入任何文件"
|
|
9916
10236
|
};
|
|
9917
10237
|
if (existsAtCommit) return {
|
|
9918
10238
|
ok: false,
|
|
9919
|
-
message:
|
|
10239
|
+
message: `技能「${normalized}」已存在。`
|
|
9920
10240
|
};
|
|
9921
10241
|
let markerWarning = "";
|
|
9922
10242
|
if (origin !== "foreground") {
|
|
9923
10243
|
try {
|
|
9924
10244
|
await this.io.writeText(markerPath(dir, "hermes-managed"), "");
|
|
9925
10245
|
} catch (error) {
|
|
9926
|
-
if (isCommittedOnly(error)) markerWarning = `
|
|
10246
|
+
if (isCommittedOnly(error)) markerWarning = `hermes-managed 标记已落盘,但其目录 fsync 失败 —— 持久性未确认:${error instanceof Error ? error.message : String(error)}`;
|
|
9927
10247
|
else markerWarning = `hermes-managed marker write failed (${error instanceof Error ? error.message : String(error)}); the skill landed but is NOT lifecycle-managed — recreate or pin it manually`;
|
|
9928
10248
|
}
|
|
9929
10249
|
if (!await this.io.exists(createPath)) {
|
|
@@ -9931,7 +10251,7 @@ var SkillLibrary = class {
|
|
|
9931
10251
|
await this.cleanupGhostDir(createPath);
|
|
9932
10252
|
return {
|
|
9933
10253
|
ok: false,
|
|
9934
|
-
message:
|
|
10254
|
+
message: `创建过程中技能「${normalized}」被并发归档;残留标记已移除:等搬运完成后重试一次。`
|
|
9935
10255
|
};
|
|
9936
10256
|
}
|
|
9937
10257
|
}
|
|
@@ -9941,10 +10261,10 @@ var SkillLibrary = class {
|
|
|
9941
10261
|
name: normalized,
|
|
9942
10262
|
skillDir: dir
|
|
9943
10263
|
});
|
|
9944
|
-
const warnings = [createDurabilityWarning !== "" ?
|
|
10264
|
+
const warnings = [createDurabilityWarning !== "" ? `写入已落盘,但目录 fsync 失败 —— 持久性未确认:${createDurabilityWarning}` : "", markerWarning].filter((warning) => warning !== "");
|
|
9945
10265
|
return {
|
|
9946
10266
|
ok: true,
|
|
9947
|
-
message:
|
|
10267
|
+
message: `已创建技能「${normalized}」。${warnings.length === 0 ? "" : ` (警告:${warnings.join(";")})`}`,
|
|
9948
10268
|
path: dir,
|
|
9949
10269
|
...norm.changed ? { normalizedFrontmatterFields: norm.fields } : {}
|
|
9950
10270
|
};
|
|
@@ -9964,7 +10284,7 @@ var SkillLibrary = class {
|
|
|
9964
10284
|
const protection = await this.writeProtection(name, origin);
|
|
9965
10285
|
if (protection) return {
|
|
9966
10286
|
ok: false,
|
|
9967
|
-
message:
|
|
10287
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
9968
10288
|
};
|
|
9969
10289
|
const limitProbe = await probeText(this.io, path);
|
|
9970
10290
|
if (isUnknown(limitProbe)) console.warn(`skill-store: could not re-read ${path} for the shrink exemption (${limitProbe.reason}); validating without it — an over-limit repair is refused, not accepted`);
|
|
@@ -9977,7 +10297,7 @@ var SkillLibrary = class {
|
|
|
9977
10297
|
const norm = normalizeFrontmatter(content);
|
|
9978
10298
|
if (norm.issues.length > 0) return {
|
|
9979
10299
|
ok: false,
|
|
9980
|
-
message:
|
|
10300
|
+
message: `技能「${name}」的 frontmatter 无法自动修复:${norm.issues[0]}:请用双引号包住取值后重试。`
|
|
9981
10301
|
};
|
|
9982
10302
|
const finalContent = norm.changed ? norm.content : content;
|
|
9983
10303
|
if (norm.changed) {
|
|
@@ -10001,14 +10321,14 @@ var SkillLibrary = class {
|
|
|
10001
10321
|
if (current === null) return {
|
|
10002
10322
|
result: {
|
|
10003
10323
|
ok: false,
|
|
10004
|
-
message:
|
|
10324
|
+
message: `技能「${name}」不存在。`
|
|
10005
10325
|
},
|
|
10006
10326
|
write: null
|
|
10007
10327
|
};
|
|
10008
10328
|
if (finalContent.trimEnd() === current.trimEnd()) return {
|
|
10009
10329
|
result: {
|
|
10010
10330
|
ok: true,
|
|
10011
|
-
message:
|
|
10331
|
+
message: `技能「${name}」未变更:提供的内容与当前文件一致,未写入。`,
|
|
10012
10332
|
noop: true,
|
|
10013
10333
|
path: dir
|
|
10014
10334
|
},
|
|
@@ -10018,7 +10338,7 @@ var SkillLibrary = class {
|
|
|
10018
10338
|
return {
|
|
10019
10339
|
result: {
|
|
10020
10340
|
ok: true,
|
|
10021
|
-
message:
|
|
10341
|
+
message: `技能「${name}」已更新。`,
|
|
10022
10342
|
path: dir,
|
|
10023
10343
|
...norm.changed ? { normalizedFrontmatterFields: norm.fields } : {}
|
|
10024
10344
|
},
|
|
@@ -10052,12 +10372,12 @@ var SkillLibrary = class {
|
|
|
10052
10372
|
const skillMd = join(dir, "SKILL.md");
|
|
10053
10373
|
if (!await this.io.exists(skillMd)) return {
|
|
10054
10374
|
ok: false,
|
|
10055
|
-
message:
|
|
10375
|
+
message: `技能「${name}」不存在。`
|
|
10056
10376
|
};
|
|
10057
10377
|
const protection = await this.writeProtection(name, origin);
|
|
10058
10378
|
if (protection) return {
|
|
10059
10379
|
ok: false,
|
|
10060
|
-
message:
|
|
10380
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
10061
10381
|
};
|
|
10062
10382
|
let target = skillMd;
|
|
10063
10383
|
let patchLabel = "SKILL.md";
|
|
@@ -10075,7 +10395,7 @@ var SkillLibrary = class {
|
|
|
10075
10395
|
if (md === null) return {
|
|
10076
10396
|
result: {
|
|
10077
10397
|
ok: false,
|
|
10078
|
-
message:
|
|
10398
|
+
message: `文件不存在:${patchLabel}`
|
|
10079
10399
|
},
|
|
10080
10400
|
write: null
|
|
10081
10401
|
};
|
|
@@ -10097,7 +10417,7 @@ var SkillLibrary = class {
|
|
|
10097
10417
|
if (patched === null) return {
|
|
10098
10418
|
result: {
|
|
10099
10419
|
ok: false,
|
|
10100
|
-
message:
|
|
10420
|
+
message: `在「${name}/${patchLabel}」里找不到 old_string(或 replaceAll 的模糊匹配预算已用尽)。整篇重写请改用 update。`
|
|
10101
10421
|
},
|
|
10102
10422
|
write: null
|
|
10103
10423
|
};
|
|
@@ -10110,7 +10430,7 @@ var SkillLibrary = class {
|
|
|
10110
10430
|
if (validation) return {
|
|
10111
10431
|
result: {
|
|
10112
10432
|
ok: false,
|
|
10113
|
-
message:
|
|
10433
|
+
message: `补丁被拒绝:${validation}`
|
|
10114
10434
|
},
|
|
10115
10435
|
write: null
|
|
10116
10436
|
};
|
|
@@ -10118,7 +10438,7 @@ var SkillLibrary = class {
|
|
|
10118
10438
|
if (norm.issues.length > 0) return {
|
|
10119
10439
|
result: {
|
|
10120
10440
|
ok: false,
|
|
10121
|
-
message:
|
|
10441
|
+
message: `补丁被拒绝:frontmatter 无法自动修复(${norm.issues[0]})。`
|
|
10122
10442
|
},
|
|
10123
10443
|
write: null
|
|
10124
10444
|
};
|
|
@@ -10129,7 +10449,7 @@ var SkillLibrary = class {
|
|
|
10129
10449
|
if (revalidated) return {
|
|
10130
10450
|
result: {
|
|
10131
10451
|
ok: false,
|
|
10132
|
-
message:
|
|
10452
|
+
message: `补丁被拒绝:${revalidated}`
|
|
10133
10453
|
},
|
|
10134
10454
|
write: null
|
|
10135
10455
|
};
|
|
@@ -10138,7 +10458,7 @@ var SkillLibrary = class {
|
|
|
10138
10458
|
if (Buffer.byteLength(writeContent, "utf8") > this.limits.maxSkillFileBytes && target !== skillMd) return {
|
|
10139
10459
|
result: {
|
|
10140
10460
|
ok: false,
|
|
10141
|
-
message:
|
|
10461
|
+
message: `修补后的文件超过 ${this.limits.maxSkillFileBytes} 字节。`
|
|
10142
10462
|
},
|
|
10143
10463
|
write: null
|
|
10144
10464
|
};
|
|
@@ -10147,7 +10467,7 @@ var SkillLibrary = class {
|
|
|
10147
10467
|
if (overLimit && target === skillMd && !repairing) return {
|
|
10148
10468
|
result: {
|
|
10149
10469
|
ok: false,
|
|
10150
|
-
message:
|
|
10470
|
+
message: `修补后的内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
|
|
10151
10471
|
},
|
|
10152
10472
|
write: null
|
|
10153
10473
|
};
|
|
@@ -10156,7 +10476,7 @@ var SkillLibrary = class {
|
|
|
10156
10476
|
if (supportOverCap && this.supportFileCharPolicy() === "enforce") return {
|
|
10157
10477
|
result: {
|
|
10158
10478
|
ok: false,
|
|
10159
|
-
message:
|
|
10479
|
+
message: `修补后的文件内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
|
|
10160
10480
|
},
|
|
10161
10481
|
write: null
|
|
10162
10482
|
};
|
|
@@ -10171,7 +10491,7 @@ var SkillLibrary = class {
|
|
|
10171
10491
|
if (writeContent.trimEnd() === md.trimEnd()) return {
|
|
10172
10492
|
result: {
|
|
10173
10493
|
ok: true,
|
|
10174
|
-
message:
|
|
10494
|
+
message: `技能「${name}」未变更:old_string 与替换内容相同(${patchLabel}),未写入。`,
|
|
10175
10495
|
noop: true,
|
|
10176
10496
|
path: dir
|
|
10177
10497
|
},
|
|
@@ -10181,7 +10501,7 @@ var SkillLibrary = class {
|
|
|
10181
10501
|
return {
|
|
10182
10502
|
result: {
|
|
10183
10503
|
ok: true,
|
|
10184
|
-
message:
|
|
10504
|
+
message: `技能「${name}」已修补(${patchLabel})。${patchNote}${capAdvisory}`,
|
|
10185
10505
|
path: dir,
|
|
10186
10506
|
...normalizedFields ? { normalizedFrontmatterFields: normalizedFields } : {}
|
|
10187
10507
|
},
|
|
@@ -10226,9 +10546,9 @@ var SkillLibrary = class {
|
|
|
10226
10546
|
const reason = error instanceof Error ? error.message : String(error);
|
|
10227
10547
|
try {
|
|
10228
10548
|
await this.io.remove(dest);
|
|
10229
|
-
return
|
|
10549
|
+
return `复制成功,但源目录删不掉(${reason});已回滚复制出的目录树`;
|
|
10230
10550
|
} catch {
|
|
10231
|
-
return
|
|
10551
|
+
return `复制成功,但源目录删不掉(${reason}),且复制出的目录树也无法回滚 —— 该目录树现在同时存在于两处;请手动清理`;
|
|
10232
10552
|
}
|
|
10233
10553
|
}
|
|
10234
10554
|
}
|
|
@@ -10271,17 +10591,17 @@ var SkillLibrary = class {
|
|
|
10271
10591
|
const md = await this.io.readText(join(dir, "SKILL.md"));
|
|
10272
10592
|
if (md === null) return {
|
|
10273
10593
|
ok: false,
|
|
10274
|
-
message:
|
|
10594
|
+
message: `技能「${name}」不存在。`
|
|
10275
10595
|
};
|
|
10276
10596
|
const protection = await this.deleteProtection(name, options);
|
|
10277
10597
|
if (protection) return {
|
|
10278
10598
|
ok: false,
|
|
10279
|
-
message: protection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` :
|
|
10599
|
+
message: protection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `技能「${name}」受保护(${protection})。`
|
|
10280
10600
|
};
|
|
10281
10601
|
if (options.absorbedInto) {
|
|
10282
10602
|
if (options.absorbedInto.trim() === name) return {
|
|
10283
10603
|
ok: false,
|
|
10284
|
-
message: "absorbed_into
|
|
10604
|
+
message: "absorbed_into 不能是正在归档的技能本身(不能吸收到自己)。"
|
|
10285
10605
|
};
|
|
10286
10606
|
const intoBad = this.badName(options.absorbedInto.trim());
|
|
10287
10607
|
if (intoBad) return {
|
|
@@ -10303,22 +10623,22 @@ var SkillLibrary = class {
|
|
|
10303
10623
|
if (this.io.isSymlink) {
|
|
10304
10624
|
if (await this.io.isSymlink(dir) === true) return {
|
|
10305
10625
|
ok: false,
|
|
10306
|
-
message:
|
|
10626
|
+
message: `技能「${name}」是符号链接;拒绝归档。`
|
|
10307
10627
|
};
|
|
10308
10628
|
}
|
|
10309
10629
|
if (await this.supportDirsUnlistable(dir)) return {
|
|
10310
10630
|
ok: false,
|
|
10311
|
-
message:
|
|
10631
|
+
message: `无法检查技能「${name}」是否有写入方(有一个支撑目录列不出来):请重试归档。`
|
|
10312
10632
|
};
|
|
10313
10633
|
const commitProtection = await this.deleteProtection(name, options);
|
|
10314
10634
|
if (commitProtection) return {
|
|
10315
10635
|
ok: false,
|
|
10316
|
-
message: commitProtection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` :
|
|
10636
|
+
message: commitProtection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `技能「${name}」受保护(${commitProtection})。`
|
|
10317
10637
|
};
|
|
10318
10638
|
const moveFailure = await this.moveDir(dir, dest);
|
|
10319
10639
|
if (moveFailure !== void 0) return {
|
|
10320
10640
|
ok: false,
|
|
10321
|
-
message:
|
|
10641
|
+
message: `技能「${name}」归档失败:${moveFailure}。`
|
|
10322
10642
|
};
|
|
10323
10643
|
const reason = options.reason ?? (options.absorbedInto ? `Consolidated into ${options.absorbedInto}` : "Archived by self-evolution curator");
|
|
10324
10644
|
try {
|
|
@@ -10335,7 +10655,7 @@ var SkillLibrary = class {
|
|
|
10335
10655
|
});
|
|
10336
10656
|
return {
|
|
10337
10657
|
ok: true,
|
|
10338
|
-
message:
|
|
10658
|
+
message: `技能「${name}」已归档到 .archive。`,
|
|
10339
10659
|
path: dest
|
|
10340
10660
|
};
|
|
10341
10661
|
}
|
|
@@ -10433,7 +10753,7 @@ var SkillLibrary = class {
|
|
|
10433
10753
|
const mode = options.mode ?? "append";
|
|
10434
10754
|
if (normalizedSources.length === 0) return {
|
|
10435
10755
|
ok: false,
|
|
10436
|
-
message: "
|
|
10756
|
+
message: "合并至少需要一个互不相同的来源技能。"
|
|
10437
10757
|
};
|
|
10438
10758
|
for (const name of [targetName, ...normalizedSources]) {
|
|
10439
10759
|
const bad = this.badName(name);
|
|
@@ -10446,7 +10766,7 @@ var SkillLibrary = class {
|
|
|
10446
10766
|
const targetProtection = await this.writeProtection(targetName, origin);
|
|
10447
10767
|
if (targetProtection) return {
|
|
10448
10768
|
ok: false,
|
|
10449
|
-
message:
|
|
10769
|
+
message: `技能「${targetName}」受保护(${targetProtection})。`
|
|
10450
10770
|
};
|
|
10451
10771
|
return await this.serial(async () => {
|
|
10452
10772
|
const referenceWrites = [];
|
|
@@ -10458,17 +10778,17 @@ var SkillLibrary = class {
|
|
|
10458
10778
|
const protection = await this.deleteProtection(source);
|
|
10459
10779
|
if (protection) return {
|
|
10460
10780
|
ok: false,
|
|
10461
|
-
message:
|
|
10781
|
+
message: `技能「${source}」受保护(${protection})。`
|
|
10462
10782
|
};
|
|
10463
10783
|
const sourceMd = await this.io.readText(join(this.dirOf(source), "SKILL.md"));
|
|
10464
10784
|
if (!sourceMd) return {
|
|
10465
10785
|
ok: false,
|
|
10466
|
-
message:
|
|
10786
|
+
message: `技能「${source}」不存在。`
|
|
10467
10787
|
};
|
|
10468
10788
|
const parsed = parseFrontmatter(sourceMd);
|
|
10469
10789
|
if (!parsed) return {
|
|
10470
10790
|
ok: false,
|
|
10471
|
-
message:
|
|
10791
|
+
message: `技能「${source}」没有有效的 frontmatter;拒绝合并。`
|
|
10472
10792
|
};
|
|
10473
10793
|
const carriesSupport = await this.countSupportDirs(source) > 0;
|
|
10474
10794
|
const refs = supportRefs(parsed.body);
|
|
@@ -10477,7 +10797,7 @@ var SkillLibrary = class {
|
|
|
10477
10797
|
const decision = await this.planSourceRehoming(source, targetName, parsed.body, rehomed.map((file) => file.to));
|
|
10478
10798
|
if (decision.kind === "blocked") return {
|
|
10479
10799
|
ok: false,
|
|
10480
|
-
message:
|
|
10800
|
+
message: `合并被拒绝:${carriesSupport ? `source "${source}" carries support files` : `source "${source}" body references support files (${refs.join(", ")})`} 会被落下。请改用 mode:'reference',或直接归档整个包。${decision.note}`
|
|
10481
10801
|
};
|
|
10482
10802
|
rehomed.push(...decision.files);
|
|
10483
10803
|
appendedBody = decision.rewritten;
|
|
@@ -10489,17 +10809,17 @@ var SkillLibrary = class {
|
|
|
10489
10809
|
const protection = await this.deleteProtection(source);
|
|
10490
10810
|
if (protection) return {
|
|
10491
10811
|
ok: false,
|
|
10492
|
-
message:
|
|
10812
|
+
message: `技能「${source}」受保护(${protection})。`
|
|
10493
10813
|
};
|
|
10494
10814
|
const sourceMd = await this.io.readText(join(this.dirOf(source), "SKILL.md"));
|
|
10495
10815
|
if (!sourceMd) return {
|
|
10496
10816
|
ok: false,
|
|
10497
|
-
message:
|
|
10817
|
+
message: `技能「${source}」不存在。`
|
|
10498
10818
|
};
|
|
10499
10819
|
const parsed = parseFrontmatter(sourceMd);
|
|
10500
10820
|
if (!parsed) return {
|
|
10501
10821
|
ok: false,
|
|
10502
|
-
message:
|
|
10822
|
+
message: `技能「${source}」没有有效的 frontmatter;拒绝降级。`
|
|
10503
10823
|
};
|
|
10504
10824
|
const refs = supportRefs(parsed.body);
|
|
10505
10825
|
let demotedBody = parsed.body;
|
|
@@ -10507,7 +10827,7 @@ var SkillLibrary = class {
|
|
|
10507
10827
|
const decision = await this.planSourceRehoming(source, targetName, parsed.body, rehomed.map((file) => file.to));
|
|
10508
10828
|
if (decision.kind === "blocked") return {
|
|
10509
10829
|
ok: false,
|
|
10510
|
-
message:
|
|
10830
|
+
message: `合并被拒绝:来源技能「${source}」的正文引用了会被落下的支撑文件(${refs.join(", ")}):请改为归档整个包。${decision.note}`
|
|
10511
10831
|
};
|
|
10512
10832
|
rehomed.push(...decision.files);
|
|
10513
10833
|
demotedBody = decision.rewritten;
|
|
@@ -10524,7 +10844,7 @@ var SkillLibrary = class {
|
|
|
10524
10844
|
try {
|
|
10525
10845
|
if (!await this.io.readText(join(targetDir, "SKILL.md"))) return {
|
|
10526
10846
|
ok: false,
|
|
10527
|
-
message:
|
|
10847
|
+
message: `技能「${targetName}」不存在。`
|
|
10528
10848
|
};
|
|
10529
10849
|
for (const source of normalizedSources) {
|
|
10530
10850
|
if (plannedSourceBytes.has(source)) {
|
|
@@ -10587,7 +10907,7 @@ var SkillLibrary = class {
|
|
|
10587
10907
|
if (!result.ok) throw new Error(result.message);
|
|
10588
10908
|
return {
|
|
10589
10909
|
ok: true,
|
|
10590
|
-
message:
|
|
10910
|
+
message: `已把 ${normalizedSources.join(", ")} 合并进「${targetName}」。`,
|
|
10591
10911
|
path: targetDir
|
|
10592
10912
|
};
|
|
10593
10913
|
} catch (error) {
|
|
@@ -10600,11 +10920,11 @@ var SkillLibrary = class {
|
|
|
10600
10920
|
}
|
|
10601
10921
|
if (failedRestores.length > 0) return {
|
|
10602
10922
|
ok: false,
|
|
10603
|
-
message:
|
|
10923
|
+
message: `合并失败(${reason});已回滚,但以下未回滚:${failedRestores.join(", ")}:它们仍在 .archive 里,请用 /evolution skill restore 恢复。`
|
|
10604
10924
|
};
|
|
10605
10925
|
return {
|
|
10606
10926
|
ok: false,
|
|
10607
|
-
message:
|
|
10927
|
+
message: `合并失败,已回滚:${reason}`
|
|
10608
10928
|
};
|
|
10609
10929
|
}
|
|
10610
10930
|
});
|
|
@@ -10633,21 +10953,21 @@ var SkillLibrary = class {
|
|
|
10633
10953
|
};
|
|
10634
10954
|
if (moves.length === 0) return {
|
|
10635
10955
|
ok: false,
|
|
10636
|
-
message: "
|
|
10956
|
+
message: "重构至少需要一次章节搬移。"
|
|
10637
10957
|
};
|
|
10638
10958
|
if (moves.length > 5) return {
|
|
10639
10959
|
ok: false,
|
|
10640
|
-
message:
|
|
10960
|
+
message: `重构超过 5 次搬移。`
|
|
10641
10961
|
};
|
|
10642
10962
|
for (const move of moves) {
|
|
10643
10963
|
const raw = move;
|
|
10644
10964
|
if (raw === null || typeof raw !== "object") return {
|
|
10645
10965
|
ok: false,
|
|
10646
|
-
message: "
|
|
10966
|
+
message: "每次重构搬移都必须是带 heading 的对象。"
|
|
10647
10967
|
};
|
|
10648
10968
|
if (typeof move.heading !== "string" || !move.heading.trim()) return {
|
|
10649
10969
|
ok: false,
|
|
10650
|
-
message: "
|
|
10970
|
+
message: "每次重构搬移都需要非空的 heading。"
|
|
10651
10971
|
};
|
|
10652
10972
|
const targetIssue = validateRestructureTarget(move.toFile);
|
|
10653
10973
|
if (targetIssue) return {
|
|
@@ -10659,25 +10979,25 @@ var SkillLibrary = class {
|
|
|
10659
10979
|
const md = await this.io.readText(join(dir, "SKILL.md"));
|
|
10660
10980
|
if (!md) return {
|
|
10661
10981
|
ok: false,
|
|
10662
|
-
message:
|
|
10982
|
+
message: `技能「${name}」不存在。`
|
|
10663
10983
|
};
|
|
10664
10984
|
const block = frontmatterBlock(md);
|
|
10665
10985
|
if (!block) return {
|
|
10666
10986
|
ok: false,
|
|
10667
|
-
message: "SKILL.md
|
|
10987
|
+
message: "SKILL.md 没有有效的 frontmatter;拒绝重构。"
|
|
10668
10988
|
};
|
|
10669
10989
|
const header = block.lines.slice(0, block.end + 1).join("\n");
|
|
10670
10990
|
const plan = planRestructureSections(md.slice(header.length).replace(/\r\n/g, "\n"), moves);
|
|
10671
10991
|
if ("error" in plan) return {
|
|
10672
10992
|
ok: false,
|
|
10673
|
-
message:
|
|
10993
|
+
message: `重构被拒绝:${plan.error}`
|
|
10674
10994
|
};
|
|
10675
10995
|
const newMd = `${header}${plan.body}`.replace(/\r\n/g, "\n");
|
|
10676
10996
|
const finalMd = block.nl === "\r\n" ? newMd.replace(/\n/g, "\r\n") : newMd;
|
|
10677
10997
|
const newMdCheck = validateFrontmatter(finalMd, name, this.limits);
|
|
10678
10998
|
if (newMdCheck) return {
|
|
10679
10999
|
ok: false,
|
|
10680
|
-
message:
|
|
11000
|
+
message: `重构被拒绝:${newMdCheck}`
|
|
10681
11001
|
};
|
|
10682
11002
|
const listing = (this.limits.citationPolicy ?? "verify") === "refuse" ? null : await this.listSupportFiles(name);
|
|
10683
11003
|
const files = listing !== null && isPresent(listing) ? listing.value : null;
|
|
@@ -10686,7 +11006,7 @@ var SkillLibrary = class {
|
|
|
10686
11006
|
if (refs.length === 0) continue;
|
|
10687
11007
|
if (files === null) return {
|
|
10688
11008
|
ok: false,
|
|
10689
|
-
message:
|
|
11009
|
+
message: `重构被拒绝:章节「## ${section.heading}」引用了会留在原地的支撑文件(${refs.join(", ")}):请拆开该章节,或连同它的文件一起搬。`
|
|
10690
11010
|
};
|
|
10691
11011
|
const report = resolveCitations({
|
|
10692
11012
|
content: section.text,
|
|
@@ -10697,7 +11017,7 @@ var SkillLibrary = class {
|
|
|
10697
11017
|
const missing = report.dangling.map((ref) => ref.target ?? ref.raw).join(", ");
|
|
10698
11018
|
return {
|
|
10699
11019
|
ok: false,
|
|
10700
|
-
message:
|
|
11020
|
+
message: `重构被拒绝:章节「## ${section.heading}」引用的支撑文件不存在(${missing}):请修正引用,或先创建该文件。`
|
|
10701
11021
|
};
|
|
10702
11022
|
}
|
|
10703
11023
|
}
|
|
@@ -10738,7 +11058,7 @@ var SkillLibrary = class {
|
|
|
10738
11058
|
if (!result.ok) return result;
|
|
10739
11059
|
return {
|
|
10740
11060
|
ok: true,
|
|
10741
|
-
message:
|
|
11061
|
+
message: `已重构「${name}」:把 ${plan.sections.length} 个章节搬到 references/。`,
|
|
10742
11062
|
path: dir
|
|
10743
11063
|
};
|
|
10744
11064
|
}
|
|
@@ -10760,12 +11080,12 @@ var SkillLibrary = class {
|
|
|
10760
11080
|
const md = await this.io.readText(join(dir, "SKILL.md"));
|
|
10761
11081
|
if (!md) return {
|
|
10762
11082
|
ok: false,
|
|
10763
|
-
message:
|
|
11083
|
+
message: `技能「${name}」不存在。`
|
|
10764
11084
|
};
|
|
10765
11085
|
const protection = plan.protection === "write" ? await this.writeProtection(name, plan.origin) : plan.protection === "delete" ? await this.deleteProtection(name) : null;
|
|
10766
11086
|
if (protection) return {
|
|
10767
11087
|
ok: false,
|
|
10768
|
-
message:
|
|
11088
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
10769
11089
|
};
|
|
10770
11090
|
const landing = [];
|
|
10771
11091
|
for (const write of plan.writes) {
|
|
@@ -10775,12 +11095,12 @@ var SkillLibrary = class {
|
|
|
10775
11095
|
} catch (error) {
|
|
10776
11096
|
return {
|
|
10777
11097
|
ok: false,
|
|
10778
|
-
message:
|
|
11098
|
+
message: `拒绝目录树变更:无法安全预读 ${write.target}(${error instanceof Error ? error.message : String(error)});未执行任何写入`
|
|
10779
11099
|
};
|
|
10780
11100
|
}
|
|
10781
11101
|
if (Buffer.byteLength(write.content, "utf8") > this.limits.maxSkillFileBytes) return {
|
|
10782
11102
|
ok: false,
|
|
10783
|
-
message:
|
|
11103
|
+
message: `写入超过 ${this.limits.maxSkillFileBytes} 字节:${write.target}`
|
|
10784
11104
|
};
|
|
10785
11105
|
const threat = this.contentThreatBlock(write.content);
|
|
10786
11106
|
if (threat) return {
|
|
@@ -10835,17 +11155,17 @@ var SkillLibrary = class {
|
|
|
10835
11155
|
const reason = error instanceof Error ? error.message : String(error);
|
|
10836
11156
|
if (stuck.length === 0) return {
|
|
10837
11157
|
ok: false,
|
|
10838
|
-
message:
|
|
11158
|
+
message: `目录树变更失败,已回滚:${reason}`
|
|
10839
11159
|
};
|
|
10840
11160
|
return {
|
|
10841
11161
|
ok: false,
|
|
10842
|
-
message:
|
|
11162
|
+
message: `目录树变更失败:${reason}。回滚无法恢复 ${stuck.join(", ")}:请从 .backups 快照恢复这些目标(或重新应用这次变更)。`
|
|
10843
11163
|
};
|
|
10844
11164
|
}
|
|
10845
11165
|
const skillMdWrite = landing.find((entry) => entry.target.split(/[\\/]/).pop() === "SKILL.md");
|
|
10846
11166
|
const auditedBefore = skillMdWrite === void 0 ? md : skillMdWrite.previous;
|
|
10847
11167
|
const auditedAfter = skillMdWrite === void 0 ? md : skillMdWrite.content;
|
|
10848
|
-
await this.audit(name, plan.auditAction, auditedBefore, auditedAfter, plan.auditSummary);
|
|
11168
|
+
await this.audit(name, plan.auditAction, auditedBefore, auditedAfter, plan.auditSummary, void 0, plan.origin);
|
|
10849
11169
|
this.notifyMutation({
|
|
10850
11170
|
action: plan.eventAction,
|
|
10851
11171
|
name,
|
|
@@ -10853,7 +11173,7 @@ var SkillLibrary = class {
|
|
|
10853
11173
|
});
|
|
10854
11174
|
return {
|
|
10855
11175
|
ok: true,
|
|
10856
|
-
message: durabilityWarning === "" ? `${plan.eventAction} "${name}" succeeded.` : `${plan.eventAction} "${name}" succeeded
|
|
11176
|
+
message: durabilityWarning === "" ? `${plan.eventAction} "${name}" succeeded.` : `${plan.eventAction} "${name}" succeeded (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`,
|
|
10857
11177
|
path: dir
|
|
10858
11178
|
};
|
|
10859
11179
|
}
|
|
@@ -10872,7 +11192,7 @@ var SkillLibrary = class {
|
|
|
10872
11192
|
const dest = this.dirOf(name);
|
|
10873
11193
|
if (await this.io.exists(dest)) return {
|
|
10874
11194
|
ok: false,
|
|
10875
|
-
message: await this.io.exists(join(dest, "SKILL.md")) ?
|
|
11195
|
+
message: await this.io.exists(join(dest, "SKILL.md")) ? `技能「${name}」已存在于当前根目录;拒绝覆盖。` : `技能目录「${name}」已存在于当前根目录,但没有 SKILL.md;恢复前请先移除或修复它。`
|
|
10876
11196
|
};
|
|
10877
11197
|
let caseVariant;
|
|
10878
11198
|
try {
|
|
@@ -10880,7 +11200,7 @@ var SkillLibrary = class {
|
|
|
10880
11200
|
} catch {}
|
|
10881
11201
|
if (caseVariant !== void 0) return {
|
|
10882
11202
|
ok: false,
|
|
10883
|
-
message:
|
|
11203
|
+
message: `技能「${caseVariant}」(同名、仅大小写不同)已存在于当前根目录;把「${name}」恢复到它旁边会造成含义不清的重复:请恢复为「${caseVariant}」,或先移除该变体。`
|
|
10884
11204
|
};
|
|
10885
11205
|
const archiveRoot = join(this.root, ".archive");
|
|
10886
11206
|
let entries;
|
|
@@ -10889,7 +11209,7 @@ var SkillLibrary = class {
|
|
|
10889
11209
|
} catch {
|
|
10890
11210
|
return {
|
|
10891
11211
|
ok: false,
|
|
10892
|
-
message: "
|
|
11212
|
+
message: "没有可用的技能归档。"
|
|
10893
11213
|
};
|
|
10894
11214
|
}
|
|
10895
11215
|
const candidates = entries.filter((entry) => entry === name || entry.startsWith(`${name}-`)).sort().reverse();
|
|
@@ -10924,28 +11244,28 @@ var SkillLibrary = class {
|
|
|
10924
11244
|
if (!chosen) {
|
|
10925
11245
|
if (unreadable.length === 0) return {
|
|
10926
11246
|
ok: false,
|
|
10927
|
-
message:
|
|
11247
|
+
message: `技能「${name}」不在 .archive 里。`
|
|
10928
11248
|
};
|
|
10929
11249
|
return {
|
|
10930
11250
|
ok: false,
|
|
10931
|
-
message:
|
|
11251
|
+
message: `.archive 里没有「${name}」对应的 frontmatter 名,但有 ${unreadable.length} 个名字相似的归档项:${unreadable.join(", ")}。这些归档项没有可读的 SKILL.md(或名字对不上),按名字恢复无法区分它们:请把目录改成技能名后再次恢复,或改用快照恢复。`
|
|
10932
11252
|
};
|
|
10933
11253
|
}
|
|
10934
11254
|
const source = join(archiveRoot, chosen);
|
|
10935
11255
|
if (this.io.isSymlink) {
|
|
10936
11256
|
if (await this.io.isSymlink(source) === true) return {
|
|
10937
11257
|
ok: false,
|
|
10938
|
-
message:
|
|
11258
|
+
message: `归档项「${chosen}」是符号链接;拒绝恢复。`
|
|
10939
11259
|
};
|
|
10940
11260
|
}
|
|
10941
11261
|
if (await this.supportDirsUnlistable(dest)) return {
|
|
10942
11262
|
ok: false,
|
|
10943
|
-
message:
|
|
11263
|
+
message: `无法检查技能「${name}」是否有写入方(有一个支撑目录列不出来):请重试恢复。`
|
|
10944
11264
|
};
|
|
10945
11265
|
const moveFailure = await this.moveDir(source, dest);
|
|
10946
11266
|
if (moveFailure !== void 0) return {
|
|
10947
11267
|
ok: false,
|
|
10948
|
-
message:
|
|
11268
|
+
message: `从 .archive 恢复「${name}」失败:${moveFailure}`
|
|
10949
11269
|
};
|
|
10950
11270
|
for (const marker of [".archive-reason", ".archive-name"]) if (await this.io.exists(join(dest, marker))) await this.io.remove(join(dest, marker));
|
|
10951
11271
|
const restoredProbe = await probeText(this.io, join(dest, "SKILL.md"));
|
|
@@ -10958,7 +11278,7 @@ var SkillLibrary = class {
|
|
|
10958
11278
|
});
|
|
10959
11279
|
return {
|
|
10960
11280
|
ok: true,
|
|
10961
|
-
message:
|
|
11281
|
+
message: `技能「${name}」已从 .archive 恢复。`,
|
|
10962
11282
|
path: dest
|
|
10963
11283
|
};
|
|
10964
11284
|
}
|
|
@@ -10975,12 +11295,12 @@ var SkillLibrary = class {
|
|
|
10975
11295
|
const dir = this.dirOf(name);
|
|
10976
11296
|
if (!await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
10977
11297
|
ok: false,
|
|
10978
|
-
message:
|
|
11298
|
+
message: `技能「${name}」不存在。`
|
|
10979
11299
|
};
|
|
10980
11300
|
const protection = await this.writeProtection(name, origin);
|
|
10981
11301
|
if (protection) return {
|
|
10982
11302
|
ok: false,
|
|
10983
|
-
message:
|
|
11303
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
10984
11304
|
};
|
|
10985
11305
|
const validation = validateSupportPath(filePath);
|
|
10986
11306
|
if (validation) return {
|
|
@@ -10989,7 +11309,7 @@ var SkillLibrary = class {
|
|
|
10989
11309
|
};
|
|
10990
11310
|
if (Buffer.byteLength(content, "utf8") > this.limits.maxSkillFileBytes) return {
|
|
10991
11311
|
ok: false,
|
|
10992
|
-
message:
|
|
11312
|
+
message: `支撑文件超过 ${this.limits.maxSkillFileBytes} 字节。`
|
|
10993
11313
|
};
|
|
10994
11314
|
const threat = this.contentThreatBlock(content);
|
|
10995
11315
|
if (threat) return {
|
|
@@ -11009,14 +11329,14 @@ var SkillLibrary = class {
|
|
|
11009
11329
|
if (overCap && !repairing && this.supportFileCharPolicy() === "enforce") return {
|
|
11010
11330
|
result: {
|
|
11011
11331
|
ok: false,
|
|
11012
|
-
message:
|
|
11332
|
+
message: `支撑文件「${filePath}」的内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
|
|
11013
11333
|
},
|
|
11014
11334
|
write: null
|
|
11015
11335
|
};
|
|
11016
11336
|
if (current !== null && content.trimEnd() === current.trimEnd()) return {
|
|
11017
11337
|
result: {
|
|
11018
11338
|
ok: true,
|
|
11019
|
-
message:
|
|
11339
|
+
message: `支撑文件「${filePath}」未变更:提供的内容与当前文件一致,未写入。`,
|
|
11020
11340
|
noop: true,
|
|
11021
11341
|
path: target
|
|
11022
11342
|
},
|
|
@@ -11025,7 +11345,7 @@ var SkillLibrary = class {
|
|
|
11025
11345
|
return {
|
|
11026
11346
|
result: {
|
|
11027
11347
|
ok: true,
|
|
11028
|
-
message:
|
|
11348
|
+
message: `支撑文件「${filePath}」已写入「${name}」。${capAdvisory}`,
|
|
11029
11349
|
path: target
|
|
11030
11350
|
},
|
|
11031
11351
|
write: content,
|
|
@@ -11059,12 +11379,12 @@ var SkillLibrary = class {
|
|
|
11059
11379
|
const dir = this.dirOf(name);
|
|
11060
11380
|
if (!await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
11061
11381
|
ok: false,
|
|
11062
|
-
message:
|
|
11382
|
+
message: `技能「${name}」不存在。`
|
|
11063
11383
|
};
|
|
11064
11384
|
const protection = await this.writeProtection(name, origin);
|
|
11065
11385
|
if (protection) return {
|
|
11066
11386
|
ok: false,
|
|
11067
|
-
message:
|
|
11387
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
11068
11388
|
};
|
|
11069
11389
|
const validation = validateSupportPath(filePath);
|
|
11070
11390
|
if (validation) return {
|
|
@@ -11076,7 +11396,7 @@ var SkillLibrary = class {
|
|
|
11076
11396
|
if (anchor !== void 0) return anchorRefusalFile(name, filePath, "missing");
|
|
11077
11397
|
return {
|
|
11078
11398
|
ok: false,
|
|
11079
|
-
message:
|
|
11399
|
+
message: `文件「${filePath}」不在技能「${name}」里。`
|
|
11080
11400
|
};
|
|
11081
11401
|
}
|
|
11082
11402
|
const beforeProbe = await probeText(this.io, target);
|
|
@@ -11100,7 +11420,7 @@ var SkillLibrary = class {
|
|
|
11100
11420
|
});
|
|
11101
11421
|
if (!ran.done) return {
|
|
11102
11422
|
ok: false,
|
|
11103
|
-
message: "
|
|
11423
|
+
message: "内部错误:删除事务没有调用任务;没有删除任何东西"
|
|
11104
11424
|
};
|
|
11105
11425
|
} else if (verdict === "match") await this.io.remove(target);
|
|
11106
11426
|
if (verdict !== "match") return anchorRefusalFile(name, filePath, verdict);
|
|
@@ -11113,7 +11433,7 @@ var SkillLibrary = class {
|
|
|
11113
11433
|
});
|
|
11114
11434
|
return {
|
|
11115
11435
|
ok: true,
|
|
11116
|
-
message:
|
|
11436
|
+
message: `支撑文件「${filePath}」已从「${name}」移除。`,
|
|
11117
11437
|
path: target
|
|
11118
11438
|
};
|
|
11119
11439
|
}
|
|
@@ -11331,7 +11651,7 @@ var SkillLibrary = class {
|
|
|
11331
11651
|
out.push({
|
|
11332
11652
|
path: join(backupRoot, name),
|
|
11333
11653
|
createdAt: "",
|
|
11334
|
-
reason: "
|
|
11654
|
+
reason: "清单读不出来或缺失(孤儿快照)"
|
|
11335
11655
|
});
|
|
11336
11656
|
continue;
|
|
11337
11657
|
}
|
|
@@ -11373,12 +11693,12 @@ var SkillLibrary = class {
|
|
|
11373
11693
|
const listed = await this.listSnapshots();
|
|
11374
11694
|
if (isUnknown(listed)) return {
|
|
11375
11695
|
ok: false,
|
|
11376
|
-
message:
|
|
11696
|
+
message: `拒绝快照恢复:快照目录读不出来(${listed.reason})—— 未做任何改动。`
|
|
11377
11697
|
};
|
|
11378
11698
|
const latest = isPresent(listed) ? listed.value[0] : void 0;
|
|
11379
11699
|
if (!latest) return {
|
|
11380
11700
|
ok: false,
|
|
11381
|
-
message: "
|
|
11701
|
+
message: "没有可用的技能快照。"
|
|
11382
11702
|
};
|
|
11383
11703
|
let preRollbackPath;
|
|
11384
11704
|
try {
|
|
@@ -11386,7 +11706,7 @@ var SkillLibrary = class {
|
|
|
11386
11706
|
} catch (error) {
|
|
11387
11707
|
return {
|
|
11388
11708
|
ok: false,
|
|
11389
|
-
message:
|
|
11709
|
+
message: `在任何清理之前就拒绝了快照恢复:生成回滚前快照失败(${error instanceof Error ? error.message : String(error)})—— 活动目录树未改变。请解决快照写入失败后重试。`
|
|
11390
11710
|
};
|
|
11391
11711
|
}
|
|
11392
11712
|
let snapshotExtras;
|
|
@@ -11395,7 +11715,7 @@ var SkillLibrary = class {
|
|
|
11395
11715
|
} catch (error) {
|
|
11396
11716
|
return {
|
|
11397
11717
|
ok: false,
|
|
11398
|
-
message:
|
|
11718
|
+
message: `在任何清理之前就拒绝了快照恢复:读取快照清单或附属文件失败(${error instanceof Error ? error.message : String(error)})—— 活动目录树未改变。请解决读取失败后重试。`
|
|
11399
11719
|
};
|
|
11400
11720
|
}
|
|
11401
11721
|
try {
|
|
@@ -11406,17 +11726,17 @@ var SkillLibrary = class {
|
|
|
11406
11726
|
await this.restoreSnapshotIntoRoot(preRollbackPath);
|
|
11407
11727
|
return {
|
|
11408
11728
|
ok: false,
|
|
11409
|
-
message:
|
|
11729
|
+
message: `快照恢复失败(${reason});活动目录树已回滚到回滚前的快照。`
|
|
11410
11730
|
};
|
|
11411
11731
|
} catch (rollbackError) {
|
|
11412
11732
|
const rb = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
11413
11733
|
if (isPreClearRefusal(reason) && isPreClearRefusal(rb)) return {
|
|
11414
11734
|
ok: false,
|
|
11415
|
-
message:
|
|
11735
|
+
message: `在任何清理之前就拒绝了快照恢复; 活动目录树未改变。\n- 目标:${reason}\n- 回滚前快照:${rb}\n请解决原因(有存活的技能写入,或快照不完整)后重试 —— 无需手动抢救。`
|
|
11416
11736
|
};
|
|
11417
11737
|
return {
|
|
11418
11738
|
ok: false,
|
|
11419
|
-
message:
|
|
11739
|
+
message: `快照恢复失败(${reason}),且回滚前快照的恢复也失败(${rb})。请手动抢救:${preRollbackPath}(回滚前)、${latest.path}(目标)。`
|
|
11420
11740
|
};
|
|
11421
11741
|
}
|
|
11422
11742
|
}
|
|
@@ -11426,7 +11746,7 @@ var SkillLibrary = class {
|
|
|
11426
11746
|
});
|
|
11427
11747
|
return {
|
|
11428
11748
|
ok: true,
|
|
11429
|
-
message:
|
|
11749
|
+
message: `已从 ${latest.path} 恢复技能目录树`,
|
|
11430
11750
|
path: latest.path,
|
|
11431
11751
|
...snapshotExtras.length === 0 ? {} : { extras: snapshotExtras }
|
|
11432
11752
|
};
|
|
@@ -13074,7 +13394,7 @@ function planNamespaceMigration(documentText) {
|
|
|
13074
13394
|
skipped.push({
|
|
13075
13395
|
namespace,
|
|
13076
13396
|
key,
|
|
13077
|
-
reason: "row `" + rowId + "`
|
|
13397
|
+
reason: "row `" + rowId + "` 没有这个参数"
|
|
13078
13398
|
});
|
|
13079
13399
|
continue;
|
|
13080
13400
|
}
|
|
@@ -13126,7 +13446,7 @@ function classifyNamespaceMigration(plan, read) {
|
|
|
13126
13446
|
for (const value of entry.values) skipped.push({
|
|
13127
13447
|
namespace: entry.namespace,
|
|
13128
13448
|
key: value.source,
|
|
13129
|
-
reason: "row `" + entry.rowId + "`
|
|
13449
|
+
reason: "row `" + entry.rowId + "` 在本组合中不提供服务"
|
|
13130
13450
|
});
|
|
13131
13451
|
continue;
|
|
13132
13452
|
}
|
|
@@ -13439,4 +13759,4 @@ function foldUntrustedLines(text, limit) {
|
|
|
13439
13759
|
return limit > 0 ? marked.slice(0, limit) : marked;
|
|
13440
13760
|
}
|
|
13441
13761
|
//#endregion
|
|
13442
|
-
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_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, 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, 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, advanceReview, affinity, ageCopyKey, allowRowCollisions, anchorVerdict, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyNamespaceMigration, applyReferenceRewrite, asRecord, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, atomicWriteFiles, authoringFeedback, basePresetPlugins, blobPath, bodyCost, buildCuratorRunReport, 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, createGateSet, daysSinceIso, declaredReportStamp, denseBlockSequence, describeReferenceRewrite, 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, 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, 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, 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 };
|
|
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 };
|