@lmzhen/dsh-evolution-core 0.23.0 → 0.24.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 +186 -187
- package/lib/types/constants.d.ts +1 -1
- package/lib/types/errors.d.ts +27 -27
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -531,7 +531,7 @@ const PLATFORM_CATALOG_DESCRIPTION_DEFAULT = 500;
|
|
|
531
531
|
* be copied into validateFrontmatter AND the patch path, and neither copy named
|
|
532
532
|
* where the content should go — the upstream cap message names the destination
|
|
533
533
|
* directories, and that is the part a model actually acts on. */
|
|
534
|
-
const CONTENT_SPLIT_HINT = "
|
|
534
|
+
const CONTENT_SPLIT_HINT = "建议拆成更小的 SKILL.md,把支撑文件放进 references/ 或 templates/。";
|
|
535
535
|
/** V27 G2.4: the largest millisecond delay a timer accepts. `AbortSignal.timeout`
|
|
536
536
|
* (and `setTimeout`) coerce anything larger to 1ms after a Node warning, so a
|
|
537
537
|
* timeout configured above this ceiling silently collapses to "immediately
|
|
@@ -1052,16 +1052,16 @@ function shrinksOverLimit(next, current, limit) {
|
|
|
1052
1052
|
}
|
|
1053
1053
|
function validateFrontmatter(content, expectedName, limits = DEFAULT_SKILL_LIMITS, current) {
|
|
1054
1054
|
const parsed = parseFrontmatter(content);
|
|
1055
|
-
if (!parsed) return "SKILL.md
|
|
1055
|
+
if (!parsed) return "SKILL.md 必须以 `---` 行开头,用另一个完全相同的 `---` 行结束 frontmatter(只容忍结尾的 `\\r`),并在其下包含正文。";
|
|
1056
1056
|
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
|
|
1057
|
+
if (unreadable.length > 0) return `Frontmatter 字段 ${unreadable.map((entry) => `"${entry.key}" (a YAML ${entry.kind})`).join(", ")} 必须是字符串:平台技能目录会把这种字段当作缺失,并忽略整个文件。请写成纯文本,或用双引号包住取值以保持字面内容。`;
|
|
1058
|
+
if (!parsed.frontmatter.name) return "Frontmatter 必须包含 name 字段。";
|
|
1059
|
+
if (!SKILL_NAME_RE.test(parsed.frontmatter.name)) return `技能名「${parsed.frontmatter.name}」不合法:请只用小写字母、数字和连字符。`;
|
|
1060
|
+
if (parsed.frontmatter.name.length > limits.maxNameLength) return `技能名超过 ${limits.maxNameLength} 个字符。`;
|
|
1061
|
+
if (expectedName && parsed.frontmatter.name !== expectedName) return `Frontmatter 里的 name「${parsed.frontmatter.name}」与目标技能「${expectedName}」不一致。`;
|
|
1062
|
+
if (!parsed.frontmatter.description) return "Frontmatter 必须包含 description 字段。";
|
|
1063
|
+
if (parsed.frontmatter.description.length > limits.maxDescriptionLength) return `description 超过 ${limits.maxDescriptionLength} 个字符。`;
|
|
1064
|
+
if (exceedsContentLimit(content, limits.maxSkillContentChars) && !shrinksOverLimit(content, current, limits.maxSkillContentChars)) return `SKILL.md 内容超过 ${limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`;
|
|
1065
1065
|
return null;
|
|
1066
1066
|
}
|
|
1067
1067
|
/**
|
|
@@ -1076,8 +1076,8 @@ function authoringFeedback(frontmatter) {
|
|
|
1076
1076
|
const over60 = description.length > 60;
|
|
1077
1077
|
const hasColon = description.includes(":");
|
|
1078
1078
|
const lines = [];
|
|
1079
|
-
lines.push(over60 ?
|
|
1080
|
-
if (hasColon) lines.push("
|
|
1079
|
+
lines.push(over60 ? `描述为 ${description.length}/60 个字符 —— 超过 60 字符的作者规范上限(Hermes 标准;技能目录会按平台配置的上限截断)。` : `描述为 ${description.length}/60 个字符 —— 在作者规范上限之内。`);
|
|
1080
|
+
if (hasColon) lines.push("描述里含冒号 —— 请用双引号把整个取值包起来。");
|
|
1081
1081
|
return {
|
|
1082
1082
|
descriptionChars: description.length,
|
|
1083
1083
|
over60,
|
|
@@ -3102,33 +3102,33 @@ function allowRowCollisions(env = process.env) {
|
|
|
3102
3102
|
* @module @lmzhen/dsh-evolution-core
|
|
3103
3103
|
*/
|
|
3104
3104
|
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:
|
|
3105
|
+
"e-305-this-invocation-carries-no": "E-305: 本次调用没有携带 agent —— `%a1%` 需要由会话支撑的调用(请在 GUI 或 CLI 的会话里运行)。",
|
|
3106
|
+
"e-306-this-deployment-stages-foreground": "E-306: 本部署会暂存前台写入,但 `/evolution %a1%` 无法通过技能运行器回放 —— 没有可暂存的内容。请在批准策略为 'never' 的会话里运行,或在 evolution-approval 行上设置 `stageForeground: false`,然后重试该命令。",
|
|
3107
|
+
"e-301-approval-service-not-mounted": "E-301: 批准服务未挂载:待批准写入无法列出或回放。挂载 evolution-approval 行(随 evolution-host/evolution-all 提供)后,运行 /evolution doctor 可查看已挂载的服务。",
|
|
3108
|
+
"e-301-approval-service-not-mounted-2": "E-301: 批准服务未挂载。下一步:evolution-approval 行随 evolution-host/evolution-all 一起提供 —— 运行 /evolution doctor 查看已挂载的服务。",
|
|
3109
|
+
"e-304-the-mounted-approval-service": "E-304: 已挂载的批准服务早于本能力发布 —— 请改为驳回该记录。",
|
|
3110
|
+
"e-302-curator-service-not-mounted": "E-302: 策展服务未挂载。下一步:挂载 evolution-curator 行(evolution-host/evolution-all),并运行 /evolution doctor。",
|
|
3111
|
+
"e-305-the-invocation-agent-exposes": "E-305: 调用 agent 既没有暴露 `followup` 也没有 `inject` —— %a1% 没有投递通道。",
|
|
3112
|
+
"e-306-this-deployment-stages-foreground-2": "E-306: 本部署会暂存前台写入,但本次调用没有 agent 会话 —— `/evolution restructure` 会暂存一条没有会话归属的记录。请在 GUI 或 CLI 的会话里运行,或在 `stageForeground: false` 行上设置,然后重试该命令。",
|
|
3113
|
+
"e-303-replay-service-not-mounted": "E-303: 回放服务未挂载。下一步:挂载 evolution-replay 行(evolution-host/evolution-all),并运行 /evolution doctor。",
|
|
3114
|
+
"e-307-the-settings-service-could": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution params 需要用户层来区分覆盖值与部署值;运行 /evolution doctor 查看已挂载的服务。",
|
|
3115
|
+
"e-308-unknown-parameter-group-group": "E-308: 未知的参数分组「%a1%」—— 已知分组:%a2%。",
|
|
3116
|
+
"e-311-no-settings-service-is": "E-311: 没有挂载设置服务,因此没有可写入的用户层。请挂载 settings 行(packages/settings/settings-file)后重试;本命令从不修改 cordis.yml —— 部署值属于部署。/evolution doctor 会列出已挂载的服务。",
|
|
3117
|
+
"e-312-error-instanceof-Error-error": "E-312: %a1% —— /evolution params 会列出规范 id。",
|
|
3118
|
+
"e-313-unknown-parameter-id-evolution": "E-313: 未知参数「%a1%」—— /evolution params 会列出每个已注册 id 及其层级与用户层。",
|
|
3119
|
+
"e-314-id-is-a-deployment": "E-314: 「%a1%」是部署参数(层级 %a2%,归属 %a3%)—— 它写在 cordis.yml 的插件行或策略行上,而不是从会话写入。/evolution params 会显示每一行由谁可写。",
|
|
3120
|
+
"e-315-id-has-no-user": "E-315: 「%a1%」没有用户层 —— 归属 %a2% 没有发布设置命名空间,因此该值留在部署层。",
|
|
3121
|
+
"e-307-the-settings-service-could-2": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution policy set 需要当前修订号才能安全写入;运行 /evolution doctor 查看已挂载的服务。",
|
|
3122
|
+
"e-316-namespace-namespace-is-not": "E-316: namespace \"%a1%\" 未注册 —— 归属 %a2% 在本组合中未挂载,因此无法从这里写入它的参数。 /evolution params 会把那些行标为 'unregistered'.",
|
|
3123
|
+
"e-309-revision-conflict-you-sent": "E-309: 修订冲突 —— 你提交的是 %a1%,文档当前为 %a2%。请用 /evolution params --json 重新读取后重试。",
|
|
3124
|
+
"e-309-revision-conflict-message-Re": "E-309: 修订冲突 —— %a1%。请用 /evolution params --json 重新读取后重试。",
|
|
3125
|
+
"e-310-the-settings-service-refused": "E-310: 设置服务拒绝了这次写入:%a1% —— 归属插件的规则优先(跨字段成对约束,或只能收紧的上限)。/evolution params 会显示当前值。",
|
|
3126
|
+
"e-317-skill-write-not-confirmed": "E-317: 技能 \"%a1%\" 未被%a2% —— 确认提示被拒绝或关闭,因此没有写入任何内容。每次创建与裸删除都会出现该提示;只有操作者要求时才重复调用。",
|
|
3127
|
+
"e-318-skill-write-without-a-read": "E-318: 本会话没有读取技能 \"%a1%\",因此拒绝这次写入。请先用 `skill` 工具读取它(失败的读取不算),然后重复该写入。",
|
|
3128
|
+
"e-320-the-profile-home-is-not": "E-320: 本宿主没有报告 profile home,因此无法定位旧设置文档。/evolution migrate 读取 `<profile home>/settings.yaml.imported`;请在由 profile 启动的会话里运行。",
|
|
3129
|
+
"e-322-a-parameter-would-break-a": "E-322: %a1% 至少应为 %a2%,但这次写入把它设成 %a3%。未写入任何内容。",
|
|
3130
|
+
"e-321-no-io-provider-is": "E-321: 没有挂载 evolution-io 提供方,因此无法读取旧设置文档。请挂载 evolution-io 行(evolution-host/evolution-all)后重试。",
|
|
3131
|
+
"e-319-skill-write-confirm-timed-out": "E-319: 技能 \"%a1%\" 未被%a2% —— 确认提示在 %a3% 秒内无人应答,因此该写入被取消。没有人在场决定:把 `skillWriteConfirm` 设为 `auto` 可免提示直接写入,保留 `ask` 则一直等到有人应答,或提高 `skillWriteConfirmTimeoutSeconds`。"
|
|
3132
3132
|
});
|
|
3133
3133
|
/**
|
|
3134
3134
|
* Render one table entry, substituting its interpolation slots.
|
|
@@ -3821,7 +3821,7 @@ var MemoryStore = class {
|
|
|
3821
3821
|
const chars = entries.join(ENTRY_DELIMITER).length;
|
|
3822
3822
|
if (this.failureCount > this.maxFailures) return {
|
|
3823
3823
|
ok: false,
|
|
3824
|
-
message:
|
|
3824
|
+
message: `本轮记忆整理已失败 ${this.failureCount} 次。请停止重试记忆调用,继续处理用户的任务。${previewEntries(entries)}`,
|
|
3825
3825
|
entries,
|
|
3826
3826
|
chars,
|
|
3827
3827
|
limit: this.limitFor(target)
|
|
@@ -3884,10 +3884,10 @@ var MemoryStore = class {
|
|
|
3884
3884
|
const oversized = await this.oversizedFile(target);
|
|
3885
3885
|
if (!oversized) return null;
|
|
3886
3886
|
const backup = await this.backupFile(target);
|
|
3887
|
-
const suffix = backup ? `
|
|
3887
|
+
const suffix = backup ? ` 已把备份保存到 ${basename(backup)}。` : "";
|
|
3888
3888
|
return {
|
|
3889
3889
|
ok: false,
|
|
3890
|
-
message:
|
|
3890
|
+
message: `记忆文件为 ${oversized.size} 字节(上限 ${oversized.limit * READ_GUARD_FACTOR})—— 跳过读取。${suffix} 请手动修复该文件后重试。`,
|
|
3891
3891
|
entries: [],
|
|
3892
3892
|
chars: 0,
|
|
3893
3893
|
limit: this.limitFor(target)
|
|
@@ -3920,7 +3920,7 @@ var MemoryStore = class {
|
|
|
3920
3920
|
const size = await this.io.size?.(fileFor(this.root, target));
|
|
3921
3921
|
if (typeof size === "number" && size > SHRINK_LOAD_CEILING) return {
|
|
3922
3922
|
ok: false,
|
|
3923
|
-
message:
|
|
3923
|
+
message: `记忆文件为 ${size} 字节 —— 即使只做删除批处理也过大、无法加载。请手动修复该文件后重试。`,
|
|
3924
3924
|
entries: [],
|
|
3925
3925
|
chars: 0,
|
|
3926
3926
|
limit: this.limitFor(target)
|
|
@@ -3934,7 +3934,7 @@ var MemoryStore = class {
|
|
|
3934
3934
|
});
|
|
3935
3935
|
return outcome ?? {
|
|
3936
3936
|
ok: false,
|
|
3937
|
-
message: "
|
|
3937
|
+
message: "内部错误:记忆写事务没有调用任务;未执行任何写入",
|
|
3938
3938
|
entries: [],
|
|
3939
3939
|
chars: 0,
|
|
3940
3940
|
limit: this.limitFor(target)
|
|
@@ -3946,7 +3946,7 @@ var MemoryStore = class {
|
|
|
3946
3946
|
async addChained(target, facts) {
|
|
3947
3947
|
if (!facts.trim()) return {
|
|
3948
3948
|
ok: false,
|
|
3949
|
-
message: "
|
|
3949
|
+
message: "内容不能为空。",
|
|
3950
3950
|
entries: [],
|
|
3951
3951
|
chars: 0,
|
|
3952
3952
|
limit: this.limitFor(target)
|
|
@@ -3963,7 +3963,7 @@ var MemoryStore = class {
|
|
|
3963
3963
|
if (!content) return {
|
|
3964
3964
|
result: {
|
|
3965
3965
|
ok: false,
|
|
3966
|
-
message: "
|
|
3966
|
+
message: "内容不能为空。",
|
|
3967
3967
|
entries: [],
|
|
3968
3968
|
chars: 0,
|
|
3969
3969
|
limit: this.limitFor(target)
|
|
@@ -3990,7 +3990,7 @@ var MemoryStore = class {
|
|
|
3990
3990
|
if (hasEntryDelimiter(prefixed)) return {
|
|
3991
3991
|
result: {
|
|
3992
3992
|
ok: false,
|
|
3993
|
-
message: "
|
|
3993
|
+
message: "该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。",
|
|
3994
3994
|
entries: [],
|
|
3995
3995
|
chars: 0,
|
|
3996
3996
|
limit: this.limitFor(target)
|
|
@@ -4003,7 +4003,7 @@ var MemoryStore = class {
|
|
|
4003
4003
|
return {
|
|
4004
4004
|
result: {
|
|
4005
4005
|
ok: true,
|
|
4006
|
-
message:
|
|
4006
|
+
message: `该条目已存在(未重复添加)。${this.storageHint(target, entries.join(ENTRY_DELIMITER).length)}`,
|
|
4007
4007
|
entries,
|
|
4008
4008
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4009
4009
|
limit: this.limitFor(target)
|
|
@@ -4015,14 +4015,14 @@ var MemoryStore = class {
|
|
|
4015
4015
|
const total = next.join(ENTRY_DELIMITER).length;
|
|
4016
4016
|
const addLimit = this.limitFor(target);
|
|
4017
4017
|
if (addLimit > 0 && total > addLimit) return {
|
|
4018
|
-
result: this.failure(target,
|
|
4018
|
+
result: this.failure(target, `添加该条目会超过 ${addLimit} 字符上限。请先合并或删除过期条目,再重试。`, entries),
|
|
4019
4019
|
write: null
|
|
4020
4020
|
};
|
|
4021
4021
|
this.resetFailures();
|
|
4022
4022
|
return {
|
|
4023
4023
|
result: {
|
|
4024
4024
|
ok: true,
|
|
4025
|
-
message:
|
|
4025
|
+
message: `已添加条目。${this.storageHint(target, total)}`,
|
|
4026
4026
|
entries: next,
|
|
4027
4027
|
chars: total,
|
|
4028
4028
|
limit: this.limitFor(target)
|
|
@@ -4099,10 +4099,9 @@ var MemoryStore = class {
|
|
|
4099
4099
|
if (shrinkOnly) return null;
|
|
4100
4100
|
const limit = this.limitFor(target);
|
|
4101
4101
|
const entries = normalizeEntries(raw);
|
|
4102
|
-
const over = entries.filter((entry) => entry.length > limit).length;
|
|
4103
4102
|
return {
|
|
4104
4103
|
ok: false,
|
|
4105
|
-
message: `${
|
|
4104
|
+
message: `${entries.filter((entry) => entry.length > limit).length} 条记忆超过了配置的 ${target}CharLimit(${limit});它们是在更高上限下写入的。请提高上限或删除条目 —— 删除类操作仍然可用。`,
|
|
4106
4105
|
entries,
|
|
4107
4106
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4108
4107
|
limit
|
|
@@ -4111,7 +4110,7 @@ var MemoryStore = class {
|
|
|
4111
4110
|
const backup = await this.backupFile(target);
|
|
4112
4111
|
return {
|
|
4113
4112
|
ok: false,
|
|
4114
|
-
message:
|
|
4113
|
+
message: `检测到记忆文件被外部改动。${backup ? ` 已把备份保存到 ${basename(backup)}。` : ""} 请先处理该漂移再重试。`,
|
|
4115
4114
|
entries: [],
|
|
4116
4115
|
chars: 0,
|
|
4117
4116
|
limit: this.limitFor(target)
|
|
@@ -4123,7 +4122,7 @@ var MemoryStore = class {
|
|
|
4123
4122
|
async applyBatchChained(target, operations) {
|
|
4124
4123
|
if (operations.length === 0) return {
|
|
4125
4124
|
ok: false,
|
|
4126
|
-
message: "operations
|
|
4125
|
+
message: "operations 列表为空。",
|
|
4127
4126
|
entries: [],
|
|
4128
4127
|
chars: 0,
|
|
4129
4128
|
limit: this.limitFor(target)
|
|
@@ -4148,7 +4147,7 @@ var MemoryStore = class {
|
|
|
4148
4147
|
if (!body) return {
|
|
4149
4148
|
result: {
|
|
4150
4149
|
ok: false,
|
|
4151
|
-
message:
|
|
4150
|
+
message: `第 ${position} 个操作(add):必须提供 facts。未应用任何操作。${previewEntries(entries)}`,
|
|
4152
4151
|
entries,
|
|
4153
4152
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4154
4153
|
limit: this.limitFor(target)
|
|
@@ -4159,7 +4158,7 @@ var MemoryStore = class {
|
|
|
4159
4158
|
if (threat) return {
|
|
4160
4159
|
result: {
|
|
4161
4160
|
ok: false,
|
|
4162
|
-
message:
|
|
4161
|
+
message: `第 ${position} 个操作:${threat}${previewEntries(entries)}`,
|
|
4163
4162
|
entries,
|
|
4164
4163
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4165
4164
|
limit: this.limitFor(target)
|
|
@@ -4170,7 +4169,7 @@ var MemoryStore = class {
|
|
|
4170
4169
|
if (hasEntryDelimiter(entryBody)) return {
|
|
4171
4170
|
result: {
|
|
4172
4171
|
ok: false,
|
|
4173
|
-
message:
|
|
4172
|
+
message: `第 ${position} 个操作(add):该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。${previewEntries(entries)}`,
|
|
4174
4173
|
entries,
|
|
4175
4174
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4176
4175
|
limit: this.limitFor(target)
|
|
@@ -4184,7 +4183,7 @@ var MemoryStore = class {
|
|
|
4184
4183
|
if (rawAction !== "remove" && rawAction !== "replace") return {
|
|
4185
4184
|
result: {
|
|
4186
4185
|
ok: false,
|
|
4187
|
-
message:
|
|
4186
|
+
message: `第 ${position} 个操作:未知动作「${String(rawAction)}」(应为 add/remove/replace)。未应用任何操作。${previewEntries(entries)}`,
|
|
4188
4187
|
entries,
|
|
4189
4188
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4190
4189
|
limit: this.limitFor(target)
|
|
@@ -4195,7 +4194,7 @@ var MemoryStore = class {
|
|
|
4195
4194
|
if (!needle) return {
|
|
4196
4195
|
result: {
|
|
4197
4196
|
ok: false,
|
|
4198
|
-
message:
|
|
4197
|
+
message: `第 ${position} 个操作(${op.action}):必须提供 old_text。未应用任何操作。${previewEntries(entries)}`,
|
|
4199
4198
|
entries,
|
|
4200
4199
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4201
4200
|
limit: this.limitFor(target)
|
|
@@ -4207,13 +4206,13 @@ var MemoryStore = class {
|
|
|
4207
4206
|
matchIndex
|
|
4208
4207
|
})).filter(({ entry }) => entry.includes(needle));
|
|
4209
4208
|
if (matches.length === 0) return {
|
|
4210
|
-
result: this.failure(target,
|
|
4209
|
+
result: this.failure(target, `第 ${position} 个操作:找不到匹配 "${needle}" 的条目。未应用任何操作。`, entries),
|
|
4211
4210
|
write: null
|
|
4212
4211
|
};
|
|
4213
4212
|
if (new Set(matches.map((m) => m.entry)).size > 1) return {
|
|
4214
4213
|
result: {
|
|
4215
4214
|
ok: false,
|
|
4216
|
-
message:
|
|
4215
|
+
message: `第 ${position} 个操作:「${needle}」匹配到多条不同条目。未应用任何操作。${previewEntries(entries)}`,
|
|
4217
4216
|
entries,
|
|
4218
4217
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4219
4218
|
limit: this.limitFor(target)
|
|
@@ -4227,7 +4226,7 @@ var MemoryStore = class {
|
|
|
4227
4226
|
if (!body) return {
|
|
4228
4227
|
result: {
|
|
4229
4228
|
ok: false,
|
|
4230
|
-
message:
|
|
4229
|
+
message: `第 ${position} 个操作(replace):必须提供 facts。${previewEntries(entries)}`,
|
|
4231
4230
|
entries,
|
|
4232
4231
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4233
4232
|
limit: this.limitFor(target)
|
|
@@ -4238,7 +4237,7 @@ var MemoryStore = class {
|
|
|
4238
4237
|
if (threat) return {
|
|
4239
4238
|
result: {
|
|
4240
4239
|
ok: false,
|
|
4241
|
-
message:
|
|
4240
|
+
message: `第 ${position} 个操作:${threat}${previewEntries(entries)}`,
|
|
4242
4241
|
entries,
|
|
4243
4242
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4244
4243
|
limit: this.limitFor(target)
|
|
@@ -4249,7 +4248,7 @@ var MemoryStore = class {
|
|
|
4249
4248
|
if (hasEntryDelimiter(entryBody)) return {
|
|
4250
4249
|
result: {
|
|
4251
4250
|
ok: false,
|
|
4252
|
-
message:
|
|
4251
|
+
message: `第 ${position} 个操作(replace):该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。${previewEntries(entries)}`,
|
|
4253
4252
|
entries,
|
|
4254
4253
|
chars: entries.join(ENTRY_DELIMITER).length,
|
|
4255
4254
|
limit: this.limitFor(target)
|
|
@@ -4262,14 +4261,14 @@ var MemoryStore = class {
|
|
|
4262
4261
|
const total = working.join(ENTRY_DELIMITER).length;
|
|
4263
4262
|
const batchLimit = this.limitFor(target);
|
|
4264
4263
|
if (batchLimit > 0 && total > batchLimit) return {
|
|
4265
|
-
result: this.failure(target,
|
|
4264
|
+
result: this.failure(target, `批处理结果(${total} 字符)超过 ${batchLimit} 上限。请在同一个批里再删除或缩短一些条目。`, entries),
|
|
4266
4265
|
write: null
|
|
4267
4266
|
};
|
|
4268
4267
|
this.resetFailures();
|
|
4269
4268
|
return {
|
|
4270
4269
|
result: {
|
|
4271
4270
|
ok: true,
|
|
4272
|
-
message:
|
|
4271
|
+
message: `已应用 ${operations.length} 个操作。${this.storageHint(target, total)}`,
|
|
4273
4272
|
entries: working,
|
|
4274
4273
|
chars: total,
|
|
4275
4274
|
limit: this.limitFor(target)
|
|
@@ -5961,7 +5960,7 @@ function newRunRegistry(options) {
|
|
|
5961
5960
|
const parsed = parseIndex(raw);
|
|
5962
5961
|
if (!parsed.ok) return {
|
|
5963
5962
|
ok: false,
|
|
5964
|
-
note: "
|
|
5963
|
+
note: "运行索引读不出来(不是 JSON,或没有 runs 数组)—— 上一次的运行历史未知(报告不受影响)"
|
|
5965
5964
|
};
|
|
5966
5965
|
const wasRunning = new Set([...foreign.values()].filter((row) => row.state === "running").map((row) => row.id));
|
|
5967
5966
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8579,7 +8578,7 @@ function contentRetentionFeedback(input) {
|
|
|
8579
8578
|
if (before === null || after === null) return null;
|
|
8580
8579
|
if (before.length < 200) return null;
|
|
8581
8580
|
if (after.length >= before.length * .5) return null;
|
|
8582
|
-
return
|
|
8581
|
+
return `新内容保留了上一版正文的 ${Math.round(after.length / before.length * 100)}%(${after.length}/${before.length} 字符);被替换的版本已保留在该技能的历史里。`;
|
|
8583
8582
|
}
|
|
8584
8583
|
/**
|
|
8585
8584
|
* Read one skill's versions, oldest first.
|
|
@@ -8947,7 +8946,7 @@ function anchorRefusal(name, verdict) {
|
|
|
8947
8946
|
ok: false,
|
|
8948
8947
|
stale: true,
|
|
8949
8948
|
anchor: verdict,
|
|
8950
|
-
message: verdict === "missing" ?
|
|
8949
|
+
message: verdict === "missing" ? `技能「${name}」不存在。` : `技能 "${name}" 在读取之后被改动过;为避免覆盖更新的内容,本次写入被拒绝。`
|
|
8951
8950
|
};
|
|
8952
8951
|
}
|
|
8953
8952
|
/**
|
|
@@ -8970,7 +8969,7 @@ function anchorUnverifiable(name, filePath) {
|
|
|
8970
8969
|
ok: false,
|
|
8971
8970
|
stale: true,
|
|
8972
8971
|
anchor: "drift",
|
|
8973
|
-
message: filePath === null ?
|
|
8972
|
+
message: filePath === null ? `无法读取技能 "${name}" 以校验暂存内容。` : `无法读取技能 "${name}" 的支撑文件 "${filePath}" 以校验暂存内容。`
|
|
8974
8973
|
};
|
|
8975
8974
|
}
|
|
8976
8975
|
function anchorRefusalFile(name, filePath, verdict) {
|
|
@@ -8978,7 +8977,7 @@ function anchorRefusalFile(name, filePath, verdict) {
|
|
|
8978
8977
|
ok: false,
|
|
8979
8978
|
stale: true,
|
|
8980
8979
|
anchor: verdict,
|
|
8981
|
-
message: verdict === "missing" ?
|
|
8980
|
+
message: verdict === "missing" ? `文件「${filePath}」不在技能「${name}」里。` : `技能 "${name}" 的支撑文件 "${filePath}" 在读取之后被改动过;为避免覆盖更新的内容,本次写入被拒绝。`
|
|
8982
8981
|
};
|
|
8983
8982
|
}
|
|
8984
8983
|
/**
|
|
@@ -8996,7 +8995,7 @@ function anchorRefusalFile(name, filePath, verdict) {
|
|
|
8996
8995
|
function unreadableTarget(label, error) {
|
|
8997
8996
|
return {
|
|
8998
8997
|
ok: false,
|
|
8999
|
-
message:
|
|
8998
|
+
message: `读不出「${label}」:${error?.code === "EISDIR" ? "the path is a DIRECTORY, not a file" : error instanceof Error ? error.message : String(error)}。没有写入任何内容。`
|
|
9000
8999
|
};
|
|
9001
9000
|
}
|
|
9002
9001
|
/** Upper bound of moves per restructure proposal (validator and core agree). */
|
|
@@ -9180,15 +9179,15 @@ function validateRestructureTarget(filePath) {
|
|
|
9180
9179
|
}
|
|
9181
9180
|
function validateSupportPath(filePath) {
|
|
9182
9181
|
const normalized = filePath.replace(/\\/g, "/");
|
|
9183
|
-
if (normalized.includes("..")) return "
|
|
9182
|
+
if (normalized.includes("..")) return "不允许路径穿越。";
|
|
9184
9183
|
const parts = normalized.split("/").filter(Boolean);
|
|
9185
9184
|
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 "
|
|
9185
|
+
if (parts.length < 2) return "请给出文件名,不能只给目录。";
|
|
9187
9186
|
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
|
|
9187
|
+
if (!SUPPORT_FILE_NAME_RE.test(part)) return `不支持的文件名「${part}」:请只用小写字母、数字、点、连字符和下划线(首字符须为字母或数字)。`;
|
|
9188
|
+
if (part.toLowerCase().endsWith(".corrupt") || part.toLowerCase().endsWith(".tmp")) return `不支持的文件名「${part}」:.corrupt/.tmp 后缀由状态与 IO 协议保留。`;
|
|
9190
9189
|
const stem = part.split(".")[0]?.toLowerCase() ?? "";
|
|
9191
|
-
if (WIN32_RESERVED_DEVICE_NAMES.has(stem)) return
|
|
9190
|
+
if (WIN32_RESERVED_DEVICE_NAMES.has(stem)) return `不支持的文件名「${part}」:这是 Windows 保留设备名。`;
|
|
9192
9191
|
}
|
|
9193
9192
|
return null;
|
|
9194
9193
|
}
|
|
@@ -9337,7 +9336,7 @@ var SkillLibrary = class {
|
|
|
9337
9336
|
const o = outcome;
|
|
9338
9337
|
if (o === void 0 || typeof o !== "object" || !Object.prototype.hasOwnProperty.call(o, "write")) return {
|
|
9339
9338
|
ok: false,
|
|
9340
|
-
message: "
|
|
9339
|
+
message: "内部错误:写事务没有调用任务;未执行任何写入"
|
|
9341
9340
|
};
|
|
9342
9341
|
if (o.ghostDir === true) await this.cleanupGhostDir(path);
|
|
9343
9342
|
let result = o.result;
|
|
@@ -9357,7 +9356,7 @@ var SkillLibrary = class {
|
|
|
9357
9356
|
if (o.write !== null && o.event) this.notifyMutation(o.event);
|
|
9358
9357
|
return durabilityWarning === "" || !result.ok ? result : {
|
|
9359
9358
|
...result,
|
|
9360
|
-
message: `${result.message}
|
|
9359
|
+
message: `${result.message} (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`
|
|
9361
9360
|
};
|
|
9362
9361
|
}
|
|
9363
9362
|
/**
|
|
@@ -9520,7 +9519,7 @@ var SkillLibrary = class {
|
|
|
9520
9519
|
*/
|
|
9521
9520
|
badName(name, opts = {}) {
|
|
9522
9521
|
const normalized = name.trim();
|
|
9523
|
-
if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return
|
|
9522
|
+
if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return `技能名「${normalized}」不合法:请只用小写字母、数字和连字符(不超过 ${this.limits.maxNameLength} 个)。`;
|
|
9524
9523
|
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
9524
|
return null;
|
|
9526
9525
|
}
|
|
@@ -9779,7 +9778,7 @@ var SkillLibrary = class {
|
|
|
9779
9778
|
};
|
|
9780
9779
|
if (origin === "background_review") return {
|
|
9781
9780
|
ok: false,
|
|
9782
|
-
message: "
|
|
9781
|
+
message: "只有前台(用户或主智能体)可以固定或取消固定技能。"
|
|
9783
9782
|
};
|
|
9784
9783
|
let durabilityWarning = "";
|
|
9785
9784
|
const dir = this.dirOf(normalized);
|
|
@@ -9787,17 +9786,17 @@ var SkillLibrary = class {
|
|
|
9787
9786
|
const existing = await this.io.exists(marker);
|
|
9788
9787
|
if (pinned && existing) return {
|
|
9789
9788
|
ok: true,
|
|
9790
|
-
message:
|
|
9789
|
+
message: `技能「${normalized}」已被固定。`,
|
|
9791
9790
|
path: dir
|
|
9792
9791
|
};
|
|
9793
9792
|
if (!pinned && !existing) return {
|
|
9794
9793
|
ok: true,
|
|
9795
|
-
message:
|
|
9794
|
+
message: `技能「${normalized}」未被固定,无需处理。`,
|
|
9796
9795
|
path: dir
|
|
9797
9796
|
};
|
|
9798
9797
|
if (!await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
9799
9798
|
ok: false,
|
|
9800
|
-
message:
|
|
9799
|
+
message: `技能「${normalized}」不存在。`
|
|
9801
9800
|
};
|
|
9802
9801
|
if (pinned) {
|
|
9803
9802
|
try {
|
|
@@ -9811,14 +9810,14 @@ var SkillLibrary = class {
|
|
|
9811
9810
|
await this.cleanupGhostDir(join(dir, "SKILL.md"));
|
|
9812
9811
|
return {
|
|
9813
9812
|
ok: false,
|
|
9814
|
-
message:
|
|
9813
|
+
message: `固定过程中技能「${normalized}」被并发归档;残留标记已移除:等搬运完成后重试。`
|
|
9815
9814
|
};
|
|
9816
9815
|
}
|
|
9817
9816
|
} else await this.io.remove(marker);
|
|
9818
9817
|
await this.audit(normalized, pinned ? "pin" : "unpin", null, null, pinned ? "pinned" : "unpinned");
|
|
9819
9818
|
return {
|
|
9820
9819
|
ok: true,
|
|
9821
|
-
message: `${pinned ?
|
|
9820
|
+
message: `${pinned ? `技能 "${normalized}" 已固定:免受删除、后台复核与生命周期影响。` : `技能 "${normalized}" 已取消固定。`}${durabilityWarning === "" ? "" : ` (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`}`,
|
|
9822
9821
|
path: dir
|
|
9823
9822
|
};
|
|
9824
9823
|
}
|
|
@@ -9840,7 +9839,7 @@ var SkillLibrary = class {
|
|
|
9840
9839
|
const norm = normalizeFrontmatter(content);
|
|
9841
9840
|
if (norm.issues.length > 0) return {
|
|
9842
9841
|
ok: false,
|
|
9843
|
-
message:
|
|
9842
|
+
message: `技能「${normalized}」的 frontmatter 无法自动修复:${norm.issues[0]}:请用双引号包住取值后重试。`
|
|
9844
9843
|
};
|
|
9845
9844
|
const finalContent = norm.changed ? norm.content : content;
|
|
9846
9845
|
if (norm.changed) {
|
|
@@ -9858,7 +9857,7 @@ var SkillLibrary = class {
|
|
|
9858
9857
|
const dir = this.dirOf(normalized);
|
|
9859
9858
|
if (await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
9860
9859
|
ok: false,
|
|
9861
|
-
message:
|
|
9860
|
+
message: `技能「${normalized}」已存在。`
|
|
9862
9861
|
};
|
|
9863
9862
|
let rootEntries;
|
|
9864
9863
|
try {
|
|
@@ -9867,17 +9866,17 @@ var SkillLibrary = class {
|
|
|
9867
9866
|
const cause = error instanceof Error ? error.message : String(error);
|
|
9868
9867
|
return {
|
|
9869
9868
|
ok: false,
|
|
9870
|
-
message:
|
|
9869
|
+
message: `无法检查技能「${normalized}」是否与大小写变体冲突:列出 ${this.root} 失败(${cause})。拒绝创建 —— 仅大小写不同的第二个目录在跨平台时无法撤销。`
|
|
9871
9870
|
};
|
|
9872
9871
|
}
|
|
9873
9872
|
for (const entry of rootEntries) if (typeof entry === "string" && entry !== normalized && entry.toLowerCase() === normalized.toLowerCase()) return {
|
|
9874
9873
|
ok: false,
|
|
9875
|
-
message:
|
|
9874
|
+
message: `技能「${normalized}」与已存在的大小写变体目录「${entry}」冲突(技能名只允许小写):请重命名其中一个。`
|
|
9876
9875
|
};
|
|
9877
9876
|
const protection = await this.writeProtection(normalized, origin);
|
|
9878
9877
|
if (protection) return {
|
|
9879
9878
|
ok: false,
|
|
9880
|
-
message:
|
|
9879
|
+
message: `技能「${normalized}」受保护(${protection})。`
|
|
9881
9880
|
};
|
|
9882
9881
|
const onDisk = finalContent.trimEnd() + "\n";
|
|
9883
9882
|
const createPath = join(dir, "SKILL.md");
|
|
@@ -9912,18 +9911,18 @@ var SkillLibrary = class {
|
|
|
9912
9911
|
}
|
|
9913
9912
|
if (!taskRan) return {
|
|
9914
9913
|
ok: false,
|
|
9915
|
-
message: "
|
|
9914
|
+
message: "内部错误:创建事务没有调用任务;未写入任何文件"
|
|
9916
9915
|
};
|
|
9917
9916
|
if (existsAtCommit) return {
|
|
9918
9917
|
ok: false,
|
|
9919
|
-
message:
|
|
9918
|
+
message: `技能「${normalized}」已存在。`
|
|
9920
9919
|
};
|
|
9921
9920
|
let markerWarning = "";
|
|
9922
9921
|
if (origin !== "foreground") {
|
|
9923
9922
|
try {
|
|
9924
9923
|
await this.io.writeText(markerPath(dir, "hermes-managed"), "");
|
|
9925
9924
|
} catch (error) {
|
|
9926
|
-
if (isCommittedOnly(error)) markerWarning = `
|
|
9925
|
+
if (isCommittedOnly(error)) markerWarning = `hermes-managed 标记已落盘,但其目录 fsync 失败 —— 持久性未确认:${error instanceof Error ? error.message : String(error)}`;
|
|
9927
9926
|
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
9927
|
}
|
|
9929
9928
|
if (!await this.io.exists(createPath)) {
|
|
@@ -9931,7 +9930,7 @@ var SkillLibrary = class {
|
|
|
9931
9930
|
await this.cleanupGhostDir(createPath);
|
|
9932
9931
|
return {
|
|
9933
9932
|
ok: false,
|
|
9934
|
-
message:
|
|
9933
|
+
message: `创建过程中技能「${normalized}」被并发归档;残留标记已移除:等搬运完成后重试一次。`
|
|
9935
9934
|
};
|
|
9936
9935
|
}
|
|
9937
9936
|
}
|
|
@@ -9941,10 +9940,10 @@ var SkillLibrary = class {
|
|
|
9941
9940
|
name: normalized,
|
|
9942
9941
|
skillDir: dir
|
|
9943
9942
|
});
|
|
9944
|
-
const warnings = [createDurabilityWarning !== "" ?
|
|
9943
|
+
const warnings = [createDurabilityWarning !== "" ? `写入已落盘,但目录 fsync 失败 —— 持久性未确认:${createDurabilityWarning}` : "", markerWarning].filter((warning) => warning !== "");
|
|
9945
9944
|
return {
|
|
9946
9945
|
ok: true,
|
|
9947
|
-
message:
|
|
9946
|
+
message: `已创建技能「${normalized}」。${warnings.length === 0 ? "" : ` (警告:${warnings.join(";")})`}`,
|
|
9948
9947
|
path: dir,
|
|
9949
9948
|
...norm.changed ? { normalizedFrontmatterFields: norm.fields } : {}
|
|
9950
9949
|
};
|
|
@@ -9964,7 +9963,7 @@ var SkillLibrary = class {
|
|
|
9964
9963
|
const protection = await this.writeProtection(name, origin);
|
|
9965
9964
|
if (protection) return {
|
|
9966
9965
|
ok: false,
|
|
9967
|
-
message:
|
|
9966
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
9968
9967
|
};
|
|
9969
9968
|
const limitProbe = await probeText(this.io, path);
|
|
9970
9969
|
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 +9976,7 @@ var SkillLibrary = class {
|
|
|
9977
9976
|
const norm = normalizeFrontmatter(content);
|
|
9978
9977
|
if (norm.issues.length > 0) return {
|
|
9979
9978
|
ok: false,
|
|
9980
|
-
message:
|
|
9979
|
+
message: `技能「${name}」的 frontmatter 无法自动修复:${norm.issues[0]}:请用双引号包住取值后重试。`
|
|
9981
9980
|
};
|
|
9982
9981
|
const finalContent = norm.changed ? norm.content : content;
|
|
9983
9982
|
if (norm.changed) {
|
|
@@ -10001,14 +10000,14 @@ var SkillLibrary = class {
|
|
|
10001
10000
|
if (current === null) return {
|
|
10002
10001
|
result: {
|
|
10003
10002
|
ok: false,
|
|
10004
|
-
message:
|
|
10003
|
+
message: `技能「${name}」不存在。`
|
|
10005
10004
|
},
|
|
10006
10005
|
write: null
|
|
10007
10006
|
};
|
|
10008
10007
|
if (finalContent.trimEnd() === current.trimEnd()) return {
|
|
10009
10008
|
result: {
|
|
10010
10009
|
ok: true,
|
|
10011
|
-
message:
|
|
10010
|
+
message: `技能「${name}」未变更:提供的内容与当前文件一致,未写入。`,
|
|
10012
10011
|
noop: true,
|
|
10013
10012
|
path: dir
|
|
10014
10013
|
},
|
|
@@ -10018,7 +10017,7 @@ var SkillLibrary = class {
|
|
|
10018
10017
|
return {
|
|
10019
10018
|
result: {
|
|
10020
10019
|
ok: true,
|
|
10021
|
-
message:
|
|
10020
|
+
message: `技能「${name}」已更新。`,
|
|
10022
10021
|
path: dir,
|
|
10023
10022
|
...norm.changed ? { normalizedFrontmatterFields: norm.fields } : {}
|
|
10024
10023
|
},
|
|
@@ -10052,12 +10051,12 @@ var SkillLibrary = class {
|
|
|
10052
10051
|
const skillMd = join(dir, "SKILL.md");
|
|
10053
10052
|
if (!await this.io.exists(skillMd)) return {
|
|
10054
10053
|
ok: false,
|
|
10055
|
-
message:
|
|
10054
|
+
message: `技能「${name}」不存在。`
|
|
10056
10055
|
};
|
|
10057
10056
|
const protection = await this.writeProtection(name, origin);
|
|
10058
10057
|
if (protection) return {
|
|
10059
10058
|
ok: false,
|
|
10060
|
-
message:
|
|
10059
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
10061
10060
|
};
|
|
10062
10061
|
let target = skillMd;
|
|
10063
10062
|
let patchLabel = "SKILL.md";
|
|
@@ -10075,7 +10074,7 @@ var SkillLibrary = class {
|
|
|
10075
10074
|
if (md === null) return {
|
|
10076
10075
|
result: {
|
|
10077
10076
|
ok: false,
|
|
10078
|
-
message:
|
|
10077
|
+
message: `文件不存在:${patchLabel}`
|
|
10079
10078
|
},
|
|
10080
10079
|
write: null
|
|
10081
10080
|
};
|
|
@@ -10097,7 +10096,7 @@ var SkillLibrary = class {
|
|
|
10097
10096
|
if (patched === null) return {
|
|
10098
10097
|
result: {
|
|
10099
10098
|
ok: false,
|
|
10100
|
-
message:
|
|
10099
|
+
message: `在「${name}/${patchLabel}」里找不到 old_string(或 replaceAll 的模糊匹配预算已用尽)。整篇重写请改用 update。`
|
|
10101
10100
|
},
|
|
10102
10101
|
write: null
|
|
10103
10102
|
};
|
|
@@ -10110,7 +10109,7 @@ var SkillLibrary = class {
|
|
|
10110
10109
|
if (validation) return {
|
|
10111
10110
|
result: {
|
|
10112
10111
|
ok: false,
|
|
10113
|
-
message:
|
|
10112
|
+
message: `补丁被拒绝:${validation}`
|
|
10114
10113
|
},
|
|
10115
10114
|
write: null
|
|
10116
10115
|
};
|
|
@@ -10118,7 +10117,7 @@ var SkillLibrary = class {
|
|
|
10118
10117
|
if (norm.issues.length > 0) return {
|
|
10119
10118
|
result: {
|
|
10120
10119
|
ok: false,
|
|
10121
|
-
message:
|
|
10120
|
+
message: `补丁被拒绝:frontmatter 无法自动修复(${norm.issues[0]})。`
|
|
10122
10121
|
},
|
|
10123
10122
|
write: null
|
|
10124
10123
|
};
|
|
@@ -10129,7 +10128,7 @@ var SkillLibrary = class {
|
|
|
10129
10128
|
if (revalidated) return {
|
|
10130
10129
|
result: {
|
|
10131
10130
|
ok: false,
|
|
10132
|
-
message:
|
|
10131
|
+
message: `补丁被拒绝:${revalidated}`
|
|
10133
10132
|
},
|
|
10134
10133
|
write: null
|
|
10135
10134
|
};
|
|
@@ -10138,7 +10137,7 @@ var SkillLibrary = class {
|
|
|
10138
10137
|
if (Buffer.byteLength(writeContent, "utf8") > this.limits.maxSkillFileBytes && target !== skillMd) return {
|
|
10139
10138
|
result: {
|
|
10140
10139
|
ok: false,
|
|
10141
|
-
message:
|
|
10140
|
+
message: `修补后的文件超过 ${this.limits.maxSkillFileBytes} 字节。`
|
|
10142
10141
|
},
|
|
10143
10142
|
write: null
|
|
10144
10143
|
};
|
|
@@ -10147,7 +10146,7 @@ var SkillLibrary = class {
|
|
|
10147
10146
|
if (overLimit && target === skillMd && !repairing) return {
|
|
10148
10147
|
result: {
|
|
10149
10148
|
ok: false,
|
|
10150
|
-
message:
|
|
10149
|
+
message: `修补后的内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
|
|
10151
10150
|
},
|
|
10152
10151
|
write: null
|
|
10153
10152
|
};
|
|
@@ -10156,7 +10155,7 @@ var SkillLibrary = class {
|
|
|
10156
10155
|
if (supportOverCap && this.supportFileCharPolicy() === "enforce") return {
|
|
10157
10156
|
result: {
|
|
10158
10157
|
ok: false,
|
|
10159
|
-
message:
|
|
10158
|
+
message: `修补后的文件内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
|
|
10160
10159
|
},
|
|
10161
10160
|
write: null
|
|
10162
10161
|
};
|
|
@@ -10171,7 +10170,7 @@ var SkillLibrary = class {
|
|
|
10171
10170
|
if (writeContent.trimEnd() === md.trimEnd()) return {
|
|
10172
10171
|
result: {
|
|
10173
10172
|
ok: true,
|
|
10174
|
-
message:
|
|
10173
|
+
message: `技能「${name}」未变更:old_string 与替换内容相同(${patchLabel}),未写入。`,
|
|
10175
10174
|
noop: true,
|
|
10176
10175
|
path: dir
|
|
10177
10176
|
},
|
|
@@ -10181,7 +10180,7 @@ var SkillLibrary = class {
|
|
|
10181
10180
|
return {
|
|
10182
10181
|
result: {
|
|
10183
10182
|
ok: true,
|
|
10184
|
-
message:
|
|
10183
|
+
message: `技能「${name}」已修补(${patchLabel})。${patchNote}${capAdvisory}`,
|
|
10185
10184
|
path: dir,
|
|
10186
10185
|
...normalizedFields ? { normalizedFrontmatterFields: normalizedFields } : {}
|
|
10187
10186
|
},
|
|
@@ -10226,9 +10225,9 @@ var SkillLibrary = class {
|
|
|
10226
10225
|
const reason = error instanceof Error ? error.message : String(error);
|
|
10227
10226
|
try {
|
|
10228
10227
|
await this.io.remove(dest);
|
|
10229
|
-
return
|
|
10228
|
+
return `复制成功,但源目录删不掉(${reason});已回滚复制出的目录树`;
|
|
10230
10229
|
} catch {
|
|
10231
|
-
return
|
|
10230
|
+
return `复制成功,但源目录删不掉(${reason}),且复制出的目录树也无法回滚 —— 该目录树现在同时存在于两处;请手动清理`;
|
|
10232
10231
|
}
|
|
10233
10232
|
}
|
|
10234
10233
|
}
|
|
@@ -10271,17 +10270,17 @@ var SkillLibrary = class {
|
|
|
10271
10270
|
const md = await this.io.readText(join(dir, "SKILL.md"));
|
|
10272
10271
|
if (md === null) return {
|
|
10273
10272
|
ok: false,
|
|
10274
|
-
message:
|
|
10273
|
+
message: `技能「${name}」不存在。`
|
|
10275
10274
|
};
|
|
10276
10275
|
const protection = await this.deleteProtection(name, options);
|
|
10277
10276
|
if (protection) return {
|
|
10278
10277
|
ok: false,
|
|
10279
|
-
message: protection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` :
|
|
10278
|
+
message: protection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `技能「${name}」受保护(${protection})。`
|
|
10280
10279
|
};
|
|
10281
10280
|
if (options.absorbedInto) {
|
|
10282
10281
|
if (options.absorbedInto.trim() === name) return {
|
|
10283
10282
|
ok: false,
|
|
10284
|
-
message: "absorbed_into
|
|
10283
|
+
message: "absorbed_into 不能是正在归档的技能本身(不能吸收到自己)。"
|
|
10285
10284
|
};
|
|
10286
10285
|
const intoBad = this.badName(options.absorbedInto.trim());
|
|
10287
10286
|
if (intoBad) return {
|
|
@@ -10303,22 +10302,22 @@ var SkillLibrary = class {
|
|
|
10303
10302
|
if (this.io.isSymlink) {
|
|
10304
10303
|
if (await this.io.isSymlink(dir) === true) return {
|
|
10305
10304
|
ok: false,
|
|
10306
|
-
message:
|
|
10305
|
+
message: `技能「${name}」是符号链接;拒绝归档。`
|
|
10307
10306
|
};
|
|
10308
10307
|
}
|
|
10309
10308
|
if (await this.supportDirsUnlistable(dir)) return {
|
|
10310
10309
|
ok: false,
|
|
10311
|
-
message:
|
|
10310
|
+
message: `无法检查技能「${name}」是否有写入方(有一个支撑目录列不出来):请重试归档。`
|
|
10312
10311
|
};
|
|
10313
10312
|
const commitProtection = await this.deleteProtection(name, options);
|
|
10314
10313
|
if (commitProtection) return {
|
|
10315
10314
|
ok: false,
|
|
10316
|
-
message: commitProtection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` :
|
|
10315
|
+
message: commitProtection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `技能「${name}」受保护(${commitProtection})。`
|
|
10317
10316
|
};
|
|
10318
10317
|
const moveFailure = await this.moveDir(dir, dest);
|
|
10319
10318
|
if (moveFailure !== void 0) return {
|
|
10320
10319
|
ok: false,
|
|
10321
|
-
message:
|
|
10320
|
+
message: `技能「${name}」归档失败:${moveFailure}。`
|
|
10322
10321
|
};
|
|
10323
10322
|
const reason = options.reason ?? (options.absorbedInto ? `Consolidated into ${options.absorbedInto}` : "Archived by self-evolution curator");
|
|
10324
10323
|
try {
|
|
@@ -10335,7 +10334,7 @@ var SkillLibrary = class {
|
|
|
10335
10334
|
});
|
|
10336
10335
|
return {
|
|
10337
10336
|
ok: true,
|
|
10338
|
-
message:
|
|
10337
|
+
message: `技能「${name}」已归档到 .archive。`,
|
|
10339
10338
|
path: dest
|
|
10340
10339
|
};
|
|
10341
10340
|
}
|
|
@@ -10433,7 +10432,7 @@ var SkillLibrary = class {
|
|
|
10433
10432
|
const mode = options.mode ?? "append";
|
|
10434
10433
|
if (normalizedSources.length === 0) return {
|
|
10435
10434
|
ok: false,
|
|
10436
|
-
message: "
|
|
10435
|
+
message: "合并至少需要一个互不相同的来源技能。"
|
|
10437
10436
|
};
|
|
10438
10437
|
for (const name of [targetName, ...normalizedSources]) {
|
|
10439
10438
|
const bad = this.badName(name);
|
|
@@ -10446,7 +10445,7 @@ var SkillLibrary = class {
|
|
|
10446
10445
|
const targetProtection = await this.writeProtection(targetName, origin);
|
|
10447
10446
|
if (targetProtection) return {
|
|
10448
10447
|
ok: false,
|
|
10449
|
-
message:
|
|
10448
|
+
message: `技能「${targetName}」受保护(${targetProtection})。`
|
|
10450
10449
|
};
|
|
10451
10450
|
return await this.serial(async () => {
|
|
10452
10451
|
const referenceWrites = [];
|
|
@@ -10458,17 +10457,17 @@ var SkillLibrary = class {
|
|
|
10458
10457
|
const protection = await this.deleteProtection(source);
|
|
10459
10458
|
if (protection) return {
|
|
10460
10459
|
ok: false,
|
|
10461
|
-
message:
|
|
10460
|
+
message: `技能「${source}」受保护(${protection})。`
|
|
10462
10461
|
};
|
|
10463
10462
|
const sourceMd = await this.io.readText(join(this.dirOf(source), "SKILL.md"));
|
|
10464
10463
|
if (!sourceMd) return {
|
|
10465
10464
|
ok: false,
|
|
10466
|
-
message:
|
|
10465
|
+
message: `技能「${source}」不存在。`
|
|
10467
10466
|
};
|
|
10468
10467
|
const parsed = parseFrontmatter(sourceMd);
|
|
10469
10468
|
if (!parsed) return {
|
|
10470
10469
|
ok: false,
|
|
10471
|
-
message:
|
|
10470
|
+
message: `技能「${source}」没有有效的 frontmatter;拒绝合并。`
|
|
10472
10471
|
};
|
|
10473
10472
|
const carriesSupport = await this.countSupportDirs(source) > 0;
|
|
10474
10473
|
const refs = supportRefs(parsed.body);
|
|
@@ -10477,7 +10476,7 @@ var SkillLibrary = class {
|
|
|
10477
10476
|
const decision = await this.planSourceRehoming(source, targetName, parsed.body, rehomed.map((file) => file.to));
|
|
10478
10477
|
if (decision.kind === "blocked") return {
|
|
10479
10478
|
ok: false,
|
|
10480
|
-
message:
|
|
10479
|
+
message: `合并被拒绝:${carriesSupport ? `source "${source}" carries support files` : `source "${source}" body references support files (${refs.join(", ")})`} 会被落下。请改用 mode:'reference',或直接归档整个包。${decision.note}`
|
|
10481
10480
|
};
|
|
10482
10481
|
rehomed.push(...decision.files);
|
|
10483
10482
|
appendedBody = decision.rewritten;
|
|
@@ -10489,17 +10488,17 @@ var SkillLibrary = class {
|
|
|
10489
10488
|
const protection = await this.deleteProtection(source);
|
|
10490
10489
|
if (protection) return {
|
|
10491
10490
|
ok: false,
|
|
10492
|
-
message:
|
|
10491
|
+
message: `技能「${source}」受保护(${protection})。`
|
|
10493
10492
|
};
|
|
10494
10493
|
const sourceMd = await this.io.readText(join(this.dirOf(source), "SKILL.md"));
|
|
10495
10494
|
if (!sourceMd) return {
|
|
10496
10495
|
ok: false,
|
|
10497
|
-
message:
|
|
10496
|
+
message: `技能「${source}」不存在。`
|
|
10498
10497
|
};
|
|
10499
10498
|
const parsed = parseFrontmatter(sourceMd);
|
|
10500
10499
|
if (!parsed) return {
|
|
10501
10500
|
ok: false,
|
|
10502
|
-
message:
|
|
10501
|
+
message: `技能「${source}」没有有效的 frontmatter;拒绝降级。`
|
|
10503
10502
|
};
|
|
10504
10503
|
const refs = supportRefs(parsed.body);
|
|
10505
10504
|
let demotedBody = parsed.body;
|
|
@@ -10507,7 +10506,7 @@ var SkillLibrary = class {
|
|
|
10507
10506
|
const decision = await this.planSourceRehoming(source, targetName, parsed.body, rehomed.map((file) => file.to));
|
|
10508
10507
|
if (decision.kind === "blocked") return {
|
|
10509
10508
|
ok: false,
|
|
10510
|
-
message:
|
|
10509
|
+
message: `合并被拒绝:来源技能「${source}」的正文引用了会被落下的支撑文件(${refs.join(", ")}):请改为归档整个包。${decision.note}`
|
|
10511
10510
|
};
|
|
10512
10511
|
rehomed.push(...decision.files);
|
|
10513
10512
|
demotedBody = decision.rewritten;
|
|
@@ -10524,7 +10523,7 @@ var SkillLibrary = class {
|
|
|
10524
10523
|
try {
|
|
10525
10524
|
if (!await this.io.readText(join(targetDir, "SKILL.md"))) return {
|
|
10526
10525
|
ok: false,
|
|
10527
|
-
message:
|
|
10526
|
+
message: `技能「${targetName}」不存在。`
|
|
10528
10527
|
};
|
|
10529
10528
|
for (const source of normalizedSources) {
|
|
10530
10529
|
if (plannedSourceBytes.has(source)) {
|
|
@@ -10587,7 +10586,7 @@ var SkillLibrary = class {
|
|
|
10587
10586
|
if (!result.ok) throw new Error(result.message);
|
|
10588
10587
|
return {
|
|
10589
10588
|
ok: true,
|
|
10590
|
-
message:
|
|
10589
|
+
message: `已把 ${normalizedSources.join(", ")} 合并进「${targetName}」。`,
|
|
10591
10590
|
path: targetDir
|
|
10592
10591
|
};
|
|
10593
10592
|
} catch (error) {
|
|
@@ -10600,11 +10599,11 @@ var SkillLibrary = class {
|
|
|
10600
10599
|
}
|
|
10601
10600
|
if (failedRestores.length > 0) return {
|
|
10602
10601
|
ok: false,
|
|
10603
|
-
message:
|
|
10602
|
+
message: `合并失败(${reason});已回滚,但以下未回滚:${failedRestores.join(", ")}:它们仍在 .archive 里,请用 /evolution skill restore 恢复。`
|
|
10604
10603
|
};
|
|
10605
10604
|
return {
|
|
10606
10605
|
ok: false,
|
|
10607
|
-
message:
|
|
10606
|
+
message: `合并失败,已回滚:${reason}`
|
|
10608
10607
|
};
|
|
10609
10608
|
}
|
|
10610
10609
|
});
|
|
@@ -10633,21 +10632,21 @@ var SkillLibrary = class {
|
|
|
10633
10632
|
};
|
|
10634
10633
|
if (moves.length === 0) return {
|
|
10635
10634
|
ok: false,
|
|
10636
|
-
message: "
|
|
10635
|
+
message: "重构至少需要一次章节搬移。"
|
|
10637
10636
|
};
|
|
10638
10637
|
if (moves.length > 5) return {
|
|
10639
10638
|
ok: false,
|
|
10640
|
-
message:
|
|
10639
|
+
message: `重构超过 5 次搬移。`
|
|
10641
10640
|
};
|
|
10642
10641
|
for (const move of moves) {
|
|
10643
10642
|
const raw = move;
|
|
10644
10643
|
if (raw === null || typeof raw !== "object") return {
|
|
10645
10644
|
ok: false,
|
|
10646
|
-
message: "
|
|
10645
|
+
message: "每次重构搬移都必须是带 heading 的对象。"
|
|
10647
10646
|
};
|
|
10648
10647
|
if (typeof move.heading !== "string" || !move.heading.trim()) return {
|
|
10649
10648
|
ok: false,
|
|
10650
|
-
message: "
|
|
10649
|
+
message: "每次重构搬移都需要非空的 heading。"
|
|
10651
10650
|
};
|
|
10652
10651
|
const targetIssue = validateRestructureTarget(move.toFile);
|
|
10653
10652
|
if (targetIssue) return {
|
|
@@ -10659,25 +10658,25 @@ var SkillLibrary = class {
|
|
|
10659
10658
|
const md = await this.io.readText(join(dir, "SKILL.md"));
|
|
10660
10659
|
if (!md) return {
|
|
10661
10660
|
ok: false,
|
|
10662
|
-
message:
|
|
10661
|
+
message: `技能「${name}」不存在。`
|
|
10663
10662
|
};
|
|
10664
10663
|
const block = frontmatterBlock(md);
|
|
10665
10664
|
if (!block) return {
|
|
10666
10665
|
ok: false,
|
|
10667
|
-
message: "SKILL.md
|
|
10666
|
+
message: "SKILL.md 没有有效的 frontmatter;拒绝重构。"
|
|
10668
10667
|
};
|
|
10669
10668
|
const header = block.lines.slice(0, block.end + 1).join("\n");
|
|
10670
10669
|
const plan = planRestructureSections(md.slice(header.length).replace(/\r\n/g, "\n"), moves);
|
|
10671
10670
|
if ("error" in plan) return {
|
|
10672
10671
|
ok: false,
|
|
10673
|
-
message:
|
|
10672
|
+
message: `重构被拒绝:${plan.error}`
|
|
10674
10673
|
};
|
|
10675
10674
|
const newMd = `${header}${plan.body}`.replace(/\r\n/g, "\n");
|
|
10676
10675
|
const finalMd = block.nl === "\r\n" ? newMd.replace(/\n/g, "\r\n") : newMd;
|
|
10677
10676
|
const newMdCheck = validateFrontmatter(finalMd, name, this.limits);
|
|
10678
10677
|
if (newMdCheck) return {
|
|
10679
10678
|
ok: false,
|
|
10680
|
-
message:
|
|
10679
|
+
message: `重构被拒绝:${newMdCheck}`
|
|
10681
10680
|
};
|
|
10682
10681
|
const listing = (this.limits.citationPolicy ?? "verify") === "refuse" ? null : await this.listSupportFiles(name);
|
|
10683
10682
|
const files = listing !== null && isPresent(listing) ? listing.value : null;
|
|
@@ -10686,7 +10685,7 @@ var SkillLibrary = class {
|
|
|
10686
10685
|
if (refs.length === 0) continue;
|
|
10687
10686
|
if (files === null) return {
|
|
10688
10687
|
ok: false,
|
|
10689
|
-
message:
|
|
10688
|
+
message: `重构被拒绝:章节「## ${section.heading}」引用了会留在原地的支撑文件(${refs.join(", ")}):请拆开该章节,或连同它的文件一起搬。`
|
|
10690
10689
|
};
|
|
10691
10690
|
const report = resolveCitations({
|
|
10692
10691
|
content: section.text,
|
|
@@ -10697,7 +10696,7 @@ var SkillLibrary = class {
|
|
|
10697
10696
|
const missing = report.dangling.map((ref) => ref.target ?? ref.raw).join(", ");
|
|
10698
10697
|
return {
|
|
10699
10698
|
ok: false,
|
|
10700
|
-
message:
|
|
10699
|
+
message: `重构被拒绝:章节「## ${section.heading}」引用的支撑文件不存在(${missing}):请修正引用,或先创建该文件。`
|
|
10701
10700
|
};
|
|
10702
10701
|
}
|
|
10703
10702
|
}
|
|
@@ -10738,7 +10737,7 @@ var SkillLibrary = class {
|
|
|
10738
10737
|
if (!result.ok) return result;
|
|
10739
10738
|
return {
|
|
10740
10739
|
ok: true,
|
|
10741
|
-
message:
|
|
10740
|
+
message: `已重构「${name}」:把 ${plan.sections.length} 个章节搬到 references/。`,
|
|
10742
10741
|
path: dir
|
|
10743
10742
|
};
|
|
10744
10743
|
}
|
|
@@ -10760,12 +10759,12 @@ var SkillLibrary = class {
|
|
|
10760
10759
|
const md = await this.io.readText(join(dir, "SKILL.md"));
|
|
10761
10760
|
if (!md) return {
|
|
10762
10761
|
ok: false,
|
|
10763
|
-
message:
|
|
10762
|
+
message: `技能「${name}」不存在。`
|
|
10764
10763
|
};
|
|
10765
10764
|
const protection = plan.protection === "write" ? await this.writeProtection(name, plan.origin) : plan.protection === "delete" ? await this.deleteProtection(name) : null;
|
|
10766
10765
|
if (protection) return {
|
|
10767
10766
|
ok: false,
|
|
10768
|
-
message:
|
|
10767
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
10769
10768
|
};
|
|
10770
10769
|
const landing = [];
|
|
10771
10770
|
for (const write of plan.writes) {
|
|
@@ -10775,12 +10774,12 @@ var SkillLibrary = class {
|
|
|
10775
10774
|
} catch (error) {
|
|
10776
10775
|
return {
|
|
10777
10776
|
ok: false,
|
|
10778
|
-
message:
|
|
10777
|
+
message: `拒绝目录树变更:无法安全预读 ${write.target}(${error instanceof Error ? error.message : String(error)});未执行任何写入`
|
|
10779
10778
|
};
|
|
10780
10779
|
}
|
|
10781
10780
|
if (Buffer.byteLength(write.content, "utf8") > this.limits.maxSkillFileBytes) return {
|
|
10782
10781
|
ok: false,
|
|
10783
|
-
message:
|
|
10782
|
+
message: `写入超过 ${this.limits.maxSkillFileBytes} 字节:${write.target}`
|
|
10784
10783
|
};
|
|
10785
10784
|
const threat = this.contentThreatBlock(write.content);
|
|
10786
10785
|
if (threat) return {
|
|
@@ -10835,11 +10834,11 @@ var SkillLibrary = class {
|
|
|
10835
10834
|
const reason = error instanceof Error ? error.message : String(error);
|
|
10836
10835
|
if (stuck.length === 0) return {
|
|
10837
10836
|
ok: false,
|
|
10838
|
-
message:
|
|
10837
|
+
message: `目录树变更失败,已回滚:${reason}`
|
|
10839
10838
|
};
|
|
10840
10839
|
return {
|
|
10841
10840
|
ok: false,
|
|
10842
|
-
message:
|
|
10841
|
+
message: `目录树变更失败:${reason}。回滚无法恢复 ${stuck.join(", ")}:请从 .backups 快照恢复这些目标(或重新应用这次变更)。`
|
|
10843
10842
|
};
|
|
10844
10843
|
}
|
|
10845
10844
|
const skillMdWrite = landing.find((entry) => entry.target.split(/[\\/]/).pop() === "SKILL.md");
|
|
@@ -10853,7 +10852,7 @@ var SkillLibrary = class {
|
|
|
10853
10852
|
});
|
|
10854
10853
|
return {
|
|
10855
10854
|
ok: true,
|
|
10856
|
-
message: durabilityWarning === "" ? `${plan.eventAction} "${name}" succeeded.` : `${plan.eventAction} "${name}" succeeded
|
|
10855
|
+
message: durabilityWarning === "" ? `${plan.eventAction} "${name}" succeeded.` : `${plan.eventAction} "${name}" succeeded (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`,
|
|
10857
10856
|
path: dir
|
|
10858
10857
|
};
|
|
10859
10858
|
}
|
|
@@ -10872,7 +10871,7 @@ var SkillLibrary = class {
|
|
|
10872
10871
|
const dest = this.dirOf(name);
|
|
10873
10872
|
if (await this.io.exists(dest)) return {
|
|
10874
10873
|
ok: false,
|
|
10875
|
-
message: await this.io.exists(join(dest, "SKILL.md")) ?
|
|
10874
|
+
message: await this.io.exists(join(dest, "SKILL.md")) ? `技能「${name}」已存在于当前根目录;拒绝覆盖。` : `技能目录「${name}」已存在于当前根目录,但没有 SKILL.md;恢复前请先移除或修复它。`
|
|
10876
10875
|
};
|
|
10877
10876
|
let caseVariant;
|
|
10878
10877
|
try {
|
|
@@ -10880,7 +10879,7 @@ var SkillLibrary = class {
|
|
|
10880
10879
|
} catch {}
|
|
10881
10880
|
if (caseVariant !== void 0) return {
|
|
10882
10881
|
ok: false,
|
|
10883
|
-
message:
|
|
10882
|
+
message: `技能「${caseVariant}」(同名、仅大小写不同)已存在于当前根目录;把「${name}」恢复到它旁边会造成含义不清的重复:请恢复为「${caseVariant}」,或先移除该变体。`
|
|
10884
10883
|
};
|
|
10885
10884
|
const archiveRoot = join(this.root, ".archive");
|
|
10886
10885
|
let entries;
|
|
@@ -10889,7 +10888,7 @@ var SkillLibrary = class {
|
|
|
10889
10888
|
} catch {
|
|
10890
10889
|
return {
|
|
10891
10890
|
ok: false,
|
|
10892
|
-
message: "
|
|
10891
|
+
message: "没有可用的技能归档。"
|
|
10893
10892
|
};
|
|
10894
10893
|
}
|
|
10895
10894
|
const candidates = entries.filter((entry) => entry === name || entry.startsWith(`${name}-`)).sort().reverse();
|
|
@@ -10924,28 +10923,28 @@ var SkillLibrary = class {
|
|
|
10924
10923
|
if (!chosen) {
|
|
10925
10924
|
if (unreadable.length === 0) return {
|
|
10926
10925
|
ok: false,
|
|
10927
|
-
message:
|
|
10926
|
+
message: `技能「${name}」不在 .archive 里。`
|
|
10928
10927
|
};
|
|
10929
10928
|
return {
|
|
10930
10929
|
ok: false,
|
|
10931
|
-
message:
|
|
10930
|
+
message: `.archive 里没有「${name}」对应的 frontmatter 名,但有 ${unreadable.length} 个名字相似的归档项:${unreadable.join(", ")}。这些归档项没有可读的 SKILL.md(或名字对不上),按名字恢复无法区分它们:请把目录改成技能名后再次恢复,或改用快照恢复。`
|
|
10932
10931
|
};
|
|
10933
10932
|
}
|
|
10934
10933
|
const source = join(archiveRoot, chosen);
|
|
10935
10934
|
if (this.io.isSymlink) {
|
|
10936
10935
|
if (await this.io.isSymlink(source) === true) return {
|
|
10937
10936
|
ok: false,
|
|
10938
|
-
message:
|
|
10937
|
+
message: `归档项「${chosen}」是符号链接;拒绝恢复。`
|
|
10939
10938
|
};
|
|
10940
10939
|
}
|
|
10941
10940
|
if (await this.supportDirsUnlistable(dest)) return {
|
|
10942
10941
|
ok: false,
|
|
10943
|
-
message:
|
|
10942
|
+
message: `无法检查技能「${name}」是否有写入方(有一个支撑目录列不出来):请重试恢复。`
|
|
10944
10943
|
};
|
|
10945
10944
|
const moveFailure = await this.moveDir(source, dest);
|
|
10946
10945
|
if (moveFailure !== void 0) return {
|
|
10947
10946
|
ok: false,
|
|
10948
|
-
message:
|
|
10947
|
+
message: `从 .archive 恢复「${name}」失败:${moveFailure}`
|
|
10949
10948
|
};
|
|
10950
10949
|
for (const marker of [".archive-reason", ".archive-name"]) if (await this.io.exists(join(dest, marker))) await this.io.remove(join(dest, marker));
|
|
10951
10950
|
const restoredProbe = await probeText(this.io, join(dest, "SKILL.md"));
|
|
@@ -10958,7 +10957,7 @@ var SkillLibrary = class {
|
|
|
10958
10957
|
});
|
|
10959
10958
|
return {
|
|
10960
10959
|
ok: true,
|
|
10961
|
-
message:
|
|
10960
|
+
message: `技能「${name}」已从 .archive 恢复。`,
|
|
10962
10961
|
path: dest
|
|
10963
10962
|
};
|
|
10964
10963
|
}
|
|
@@ -10975,12 +10974,12 @@ var SkillLibrary = class {
|
|
|
10975
10974
|
const dir = this.dirOf(name);
|
|
10976
10975
|
if (!await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
10977
10976
|
ok: false,
|
|
10978
|
-
message:
|
|
10977
|
+
message: `技能「${name}」不存在。`
|
|
10979
10978
|
};
|
|
10980
10979
|
const protection = await this.writeProtection(name, origin);
|
|
10981
10980
|
if (protection) return {
|
|
10982
10981
|
ok: false,
|
|
10983
|
-
message:
|
|
10982
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
10984
10983
|
};
|
|
10985
10984
|
const validation = validateSupportPath(filePath);
|
|
10986
10985
|
if (validation) return {
|
|
@@ -10989,7 +10988,7 @@ var SkillLibrary = class {
|
|
|
10989
10988
|
};
|
|
10990
10989
|
if (Buffer.byteLength(content, "utf8") > this.limits.maxSkillFileBytes) return {
|
|
10991
10990
|
ok: false,
|
|
10992
|
-
message:
|
|
10991
|
+
message: `支撑文件超过 ${this.limits.maxSkillFileBytes} 字节。`
|
|
10993
10992
|
};
|
|
10994
10993
|
const threat = this.contentThreatBlock(content);
|
|
10995
10994
|
if (threat) return {
|
|
@@ -11009,14 +11008,14 @@ var SkillLibrary = class {
|
|
|
11009
11008
|
if (overCap && !repairing && this.supportFileCharPolicy() === "enforce") return {
|
|
11010
11009
|
result: {
|
|
11011
11010
|
ok: false,
|
|
11012
|
-
message:
|
|
11011
|
+
message: `支撑文件「${filePath}」的内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
|
|
11013
11012
|
},
|
|
11014
11013
|
write: null
|
|
11015
11014
|
};
|
|
11016
11015
|
if (current !== null && content.trimEnd() === current.trimEnd()) return {
|
|
11017
11016
|
result: {
|
|
11018
11017
|
ok: true,
|
|
11019
|
-
message:
|
|
11018
|
+
message: `支撑文件「${filePath}」未变更:提供的内容与当前文件一致,未写入。`,
|
|
11020
11019
|
noop: true,
|
|
11021
11020
|
path: target
|
|
11022
11021
|
},
|
|
@@ -11025,7 +11024,7 @@ var SkillLibrary = class {
|
|
|
11025
11024
|
return {
|
|
11026
11025
|
result: {
|
|
11027
11026
|
ok: true,
|
|
11028
|
-
message:
|
|
11027
|
+
message: `支撑文件「${filePath}」已写入「${name}」。${capAdvisory}`,
|
|
11029
11028
|
path: target
|
|
11030
11029
|
},
|
|
11031
11030
|
write: content,
|
|
@@ -11059,12 +11058,12 @@ var SkillLibrary = class {
|
|
|
11059
11058
|
const dir = this.dirOf(name);
|
|
11060
11059
|
if (!await this.io.exists(join(dir, "SKILL.md"))) return {
|
|
11061
11060
|
ok: false,
|
|
11062
|
-
message:
|
|
11061
|
+
message: `技能「${name}」不存在。`
|
|
11063
11062
|
};
|
|
11064
11063
|
const protection = await this.writeProtection(name, origin);
|
|
11065
11064
|
if (protection) return {
|
|
11066
11065
|
ok: false,
|
|
11067
|
-
message:
|
|
11066
|
+
message: `技能「${name}」受保护(${protection})。`
|
|
11068
11067
|
};
|
|
11069
11068
|
const validation = validateSupportPath(filePath);
|
|
11070
11069
|
if (validation) return {
|
|
@@ -11076,7 +11075,7 @@ var SkillLibrary = class {
|
|
|
11076
11075
|
if (anchor !== void 0) return anchorRefusalFile(name, filePath, "missing");
|
|
11077
11076
|
return {
|
|
11078
11077
|
ok: false,
|
|
11079
|
-
message:
|
|
11078
|
+
message: `文件「${filePath}」不在技能「${name}」里。`
|
|
11080
11079
|
};
|
|
11081
11080
|
}
|
|
11082
11081
|
const beforeProbe = await probeText(this.io, target);
|
|
@@ -11100,7 +11099,7 @@ var SkillLibrary = class {
|
|
|
11100
11099
|
});
|
|
11101
11100
|
if (!ran.done) return {
|
|
11102
11101
|
ok: false,
|
|
11103
|
-
message: "
|
|
11102
|
+
message: "内部错误:删除事务没有调用任务;没有删除任何东西"
|
|
11104
11103
|
};
|
|
11105
11104
|
} else if (verdict === "match") await this.io.remove(target);
|
|
11106
11105
|
if (verdict !== "match") return anchorRefusalFile(name, filePath, verdict);
|
|
@@ -11113,7 +11112,7 @@ var SkillLibrary = class {
|
|
|
11113
11112
|
});
|
|
11114
11113
|
return {
|
|
11115
11114
|
ok: true,
|
|
11116
|
-
message:
|
|
11115
|
+
message: `支撑文件「${filePath}」已从「${name}」移除。`,
|
|
11117
11116
|
path: target
|
|
11118
11117
|
};
|
|
11119
11118
|
}
|
|
@@ -11331,7 +11330,7 @@ var SkillLibrary = class {
|
|
|
11331
11330
|
out.push({
|
|
11332
11331
|
path: join(backupRoot, name),
|
|
11333
11332
|
createdAt: "",
|
|
11334
|
-
reason: "
|
|
11333
|
+
reason: "清单读不出来或缺失(孤儿快照)"
|
|
11335
11334
|
});
|
|
11336
11335
|
continue;
|
|
11337
11336
|
}
|
|
@@ -11373,12 +11372,12 @@ var SkillLibrary = class {
|
|
|
11373
11372
|
const listed = await this.listSnapshots();
|
|
11374
11373
|
if (isUnknown(listed)) return {
|
|
11375
11374
|
ok: false,
|
|
11376
|
-
message:
|
|
11375
|
+
message: `拒绝快照恢复:快照目录读不出来(${listed.reason})—— 未做任何改动。`
|
|
11377
11376
|
};
|
|
11378
11377
|
const latest = isPresent(listed) ? listed.value[0] : void 0;
|
|
11379
11378
|
if (!latest) return {
|
|
11380
11379
|
ok: false,
|
|
11381
|
-
message: "
|
|
11380
|
+
message: "没有可用的技能快照。"
|
|
11382
11381
|
};
|
|
11383
11382
|
let preRollbackPath;
|
|
11384
11383
|
try {
|
|
@@ -11386,7 +11385,7 @@ var SkillLibrary = class {
|
|
|
11386
11385
|
} catch (error) {
|
|
11387
11386
|
return {
|
|
11388
11387
|
ok: false,
|
|
11389
|
-
message:
|
|
11388
|
+
message: `在任何清理之前就拒绝了快照恢复:生成回滚前快照失败(${error instanceof Error ? error.message : String(error)})—— 活动目录树未改变。请解决快照写入失败后重试。`
|
|
11390
11389
|
};
|
|
11391
11390
|
}
|
|
11392
11391
|
let snapshotExtras;
|
|
@@ -11395,7 +11394,7 @@ var SkillLibrary = class {
|
|
|
11395
11394
|
} catch (error) {
|
|
11396
11395
|
return {
|
|
11397
11396
|
ok: false,
|
|
11398
|
-
message:
|
|
11397
|
+
message: `在任何清理之前就拒绝了快照恢复:读取快照清单或附属文件失败(${error instanceof Error ? error.message : String(error)})—— 活动目录树未改变。请解决读取失败后重试。`
|
|
11399
11398
|
};
|
|
11400
11399
|
}
|
|
11401
11400
|
try {
|
|
@@ -11406,17 +11405,17 @@ var SkillLibrary = class {
|
|
|
11406
11405
|
await this.restoreSnapshotIntoRoot(preRollbackPath);
|
|
11407
11406
|
return {
|
|
11408
11407
|
ok: false,
|
|
11409
|
-
message:
|
|
11408
|
+
message: `快照恢复失败(${reason});活动目录树已回滚到回滚前的快照。`
|
|
11410
11409
|
};
|
|
11411
11410
|
} catch (rollbackError) {
|
|
11412
11411
|
const rb = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
11413
11412
|
if (isPreClearRefusal(reason) && isPreClearRefusal(rb)) return {
|
|
11414
11413
|
ok: false,
|
|
11415
|
-
message:
|
|
11414
|
+
message: `在任何清理之前就拒绝了快照恢复; 活动目录树未改变。\n- 目标:${reason}\n- 回滚前快照:${rb}\n请解决原因(有存活的技能写入,或快照不完整)后重试 —— 无需手动抢救。`
|
|
11416
11415
|
};
|
|
11417
11416
|
return {
|
|
11418
11417
|
ok: false,
|
|
11419
|
-
message:
|
|
11418
|
+
message: `快照恢复失败(${reason}),且回滚前快照的恢复也失败(${rb})。请手动抢救:${preRollbackPath}(回滚前)、${latest.path}(目标)。`
|
|
11420
11419
|
};
|
|
11421
11420
|
}
|
|
11422
11421
|
}
|
|
@@ -11426,7 +11425,7 @@ var SkillLibrary = class {
|
|
|
11426
11425
|
});
|
|
11427
11426
|
return {
|
|
11428
11427
|
ok: true,
|
|
11429
|
-
message:
|
|
11428
|
+
message: `已从 ${latest.path} 恢复技能目录树`,
|
|
11430
11429
|
path: latest.path,
|
|
11431
11430
|
...snapshotExtras.length === 0 ? {} : { extras: snapshotExtras }
|
|
11432
11431
|
};
|
|
@@ -13074,7 +13073,7 @@ function planNamespaceMigration(documentText) {
|
|
|
13074
13073
|
skipped.push({
|
|
13075
13074
|
namespace,
|
|
13076
13075
|
key,
|
|
13077
|
-
reason: "row `" + rowId + "`
|
|
13076
|
+
reason: "row `" + rowId + "` 没有这个参数"
|
|
13078
13077
|
});
|
|
13079
13078
|
continue;
|
|
13080
13079
|
}
|
|
@@ -13126,7 +13125,7 @@ function classifyNamespaceMigration(plan, read) {
|
|
|
13126
13125
|
for (const value of entry.values) skipped.push({
|
|
13127
13126
|
namespace: entry.namespace,
|
|
13128
13127
|
key: value.source,
|
|
13129
|
-
reason: "row `" + entry.rowId + "`
|
|
13128
|
+
reason: "row `" + entry.rowId + "` 在本组合中不提供服务"
|
|
13130
13129
|
});
|
|
13131
13130
|
continue;
|
|
13132
13131
|
}
|