dsh-plugin-prompt-tool 2.0.1 → 2.1.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.
Files changed (60) hide show
  1. package/README.md +6 -6
  2. package/engine/actions/catalog.mjs +1 -1
  3. package/engine/actions/inbox-prepend.mjs +1 -2
  4. package/engine/actions/shared.mjs +1 -2
  5. package/engine/branch.mjs +1 -1
  6. package/engine/compositions/source/local/instruction-hint.yml +2 -3
  7. package/engine/conditions/composite.mjs +1 -1
  8. package/engine/conditions/count.mjs +1 -2
  9. package/engine/conditions/phase.mjs +1 -2
  10. package/engine/conditions/subject.mjs +1 -1
  11. package/engine/layers.mjs +3 -4
  12. package/engine/rule-spec.mjs +1 -1
  13. package/engine/shared.mjs +1 -13
  14. package/engine/subagent-tool-policy.mjs +2 -2
  15. package/engine/tool-config-engine.mjs +1 -1
  16. package/engine/trigger-spec.mjs +3 -3
  17. package/engine/trigger.mjs +2 -2
  18. package/lib/index.d.mts +1380 -1
  19. package/lib/index.mjs +33 -3
  20. package/lib/migrate-rules.mjs +572 -1
  21. package/lib/{module-package-CHPFweqK.mjs → module-package-DqJZaE71.mjs} +1 -1
  22. package/lib/{module-package-Cnd1_mwx.mjs → module-package-q2gDRu9H.mjs} +19 -22
  23. package/lib/{rules-migration-DJ6vASB5.mjs → rule-builder-DMrJten6.mjs} +28 -595
  24. package/package.json +4 -2
  25. package/skills/dsh-module/SKILL.md +199 -0
  26. package/skills/dsh-module/reference.md +158 -0
  27. package/templates/61-deny-tools-presentation.yml +27 -0
  28. package/templates/62-guard-deny-tools.yml +20 -0
  29. package/templates/63-append-context.yml +22 -0
  30. package/templates/64-pre-step-filter.yml +23 -0
  31. package/templates/67-inbox-prepend.yml +22 -0
  32. package/templates/80-tool-surface.yml +6 -6
  33. package/engine/compositions/library/agent-instructions.yml +0 -9
  34. package/engine/compositions/library/command-goal.yml +0 -7
  35. package/engine/compositions/library/compaction.yml +0 -22
  36. package/engine/compositions/library/delegation-ptc.yml +0 -58
  37. package/engine/compositions/library/delegation.yml +0 -56
  38. package/engine/compositions/library/persistent-shell.yml +0 -50
  39. package/engine/compositions/library/planning.yml +0 -26
  40. package/engine/compositions/library/skill-filesystem-cordis.yml +0 -10
  41. package/engine/compositions/library/skill-filesystem.yml +0 -7
  42. package/engine/compositions/library/tool-ask-user.yml +0 -7
  43. package/engine/compositions/library/tool-bash.yml +0 -8
  44. package/engine/compositions/library/tool-cordis.yml +0 -7
  45. package/engine/compositions/library/tool-fs-search.yml +0 -9
  46. package/engine/compositions/library/tool-fs.yml +0 -7
  47. package/engine/compositions/library/tool-goal.yml +0 -7
  48. package/engine/compositions/library/tool-jobs.yml +0 -7
  49. package/engine/compositions/library/tool-plugin-manager-disabled.yml +0 -8
  50. package/engine/compositions/library/tool-plugin-manager.yml +0 -8
  51. package/engine/compositions/library/tool-present.yml +0 -7
  52. package/engine/compositions/library/tool-presentation.yml +0 -9
  53. package/engine/compositions/library/tool-pwsh.yml +0 -8
  54. package/engine/compositions/library/tool-skill.yml +0 -7
  55. package/engine/compositions/library/tool-todo.yml +0 -9
  56. package/engine/compositions/library/tool-web.yml +0 -10
  57. package/engine/compositions/source/local/declared-triggers.yml +0 -12
  58. package/lib/paths-CyP0IkaM.d.mts +0 -1390
  59. package/lib/preset-transfer.d.mts +0 -2
  60. package/lib/preset-transfer.mjs +0 -3
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
2
+ import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
3
3
  import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from "node:fs";
4
4
  import { spawn } from "node:child_process";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -24,8 +24,8 @@ function resolveDshHome() {
24
24
  return resolve(expanded);
25
25
  }
26
26
  const DSH_HOME = resolveDshHome();
27
- /** 用户技能根:官方 `user-dsh` 技能根,也是本插件创建、复制导入与回收站的落点。
28
- * 插件不再内置任何技能:包内没有 skills 目录,也不再有安装副本与内容账本。 */
27
+ /** 用户技能根:官方 `user-dsh` 技能根,也是本插件创建、复制导入、回收站与内置技能种子化的落点。
28
+ * 内置技能按「只补缺失」补建(见 `ensureSkillSeed`):已有目录的正文、资源与调用策略保持原样。 */
29
29
  const USER_SKILLS_DIR = join(DSH_HOME, "skills");
30
30
  /**
31
31
  * 插件自有**存储根**(DSH_HOME/.prompt-tool)。
@@ -657,7 +657,7 @@ var ModuleLayerSettingsError = class extends Error {
657
657
  this.name = "ModuleLayerSettingsError";
658
658
  }
659
659
  };
660
- const isRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
660
+ const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
661
661
  function engineParamPath(key) {
662
662
  if (!Object.prototype.hasOwnProperty.call(ENGINE_PARAM_LAYERS, key)) throw new Error(`未知引擎参数:${key}`);
663
663
  return [
@@ -672,11 +672,11 @@ function readLayerSettings(value) {
672
672
  throw new ModuleLayerSettingsError(message);
673
673
  };
674
674
  if (value === void 0) return {};
675
- if (!isRecord$1(value)) return invalid("layerSettings 必须是对象");
675
+ if (!isRecord(value)) return invalid("layerSettings 必须是对象");
676
676
  const params = {};
677
677
  for (const [layer, settings] of Object.entries(value)) {
678
678
  if (!INJECTION_POINT_ORDER.includes(layer)) invalid(`未知插入点:layerSettings.${layer}`);
679
- if (!isRecord$1(settings)) return invalid(`layerSettings.${layer} 必须是对象`);
679
+ if (!isRecord(settings)) return invalid(`layerSettings.${layer} 必须是对象`);
680
680
  for (const [key, entry] of Object.entries(settings)) {
681
681
  if (!Object.prototype.hasOwnProperty.call(ENGINE_PARAM_LAYERS, key)) continue;
682
682
  const expected = ENGINE_PARAM_LAYERS[key];
@@ -688,7 +688,7 @@ function readLayerSettings(value) {
688
688
  }
689
689
  /** 仅消费当前 layerSettings;其余段作为未知字段留存,不投影为运行时参数。 */
690
690
  function readModuleLayerSettings(source) {
691
- if (!isRecord$1(source)) throw new ModuleLayerSettingsError("module.yml 必须是对象");
691
+ if (!isRecord(source)) throw new ModuleLayerSettingsError("module.yml 必须是对象");
692
692
  return readLayerSettings(source.layerSettings);
693
693
  }
694
694
  //#endregion
@@ -816,8 +816,7 @@ const MAX_TRACKED_SESSIONS = 4096;
816
816
  *
817
817
  * 收敛「每个模块自己维护会话态 Map、自己决定上限与清空策略、自己决定复位时机」,
818
818
  * 但**不改变任何既有策略**:键类型、淘汰策略、复位语义逐项由调用方按现状声明,
819
- * 本函数只把它们收进同一套读写入口。它**不替代** `sessionMapGet`(后者仍是
820
- * 「按 id + 超限全清」这一档的实现,本函数在 `evict` 缺省且按 id 索引时逐字复用它)。
819
+ * 本函数只把它们收进同一套读写入口。
821
820
  *
822
821
  * ## 三个必答项(正好对应现状的三处差异)
823
822
  *
@@ -978,8 +977,7 @@ function createMask(source, label, plugin, allowExtra) {
978
977
  allowExtra,
979
978
  /**
980
979
  * fail-open 兜底开关(**可选**,缺省 false):原 `tool-bootstrap` 的语义是
981
- * 「keep 名单里的工具在本次装配目录里一个都不存在 → 放弃裁剪、暴露完整目录」(声明侧见
982
- * `test/engine/declarations/tool-bootstrap.yml` 的 `requireMatch: true`),而原
980
+ * 「keep 名单里的工具在本次装配目录里一个都不存在 → 放弃裁剪、暴露完整目录」,而原
983
981
  * `tool-filter` 的同名场景是**照名单裁成空目录**。两种语义都真实存在,所以这里
984
982
  * **不设默认**——要兜底的声明自己写 `requireMatch: true`。把某一个模块的特有语义当成
985
983
  * 通用默认,会让另一个声明路径的行为被悄悄改掉(这正是对拍抓出来的)。
@@ -1012,7 +1010,7 @@ const ACTION_DEGRADE = Object.freeze({
1012
1010
  silent: "silent"
1013
1011
  });
1014
1012
  /**
1015
- * 七类动作的声明:**合法事件通道** + **降级语义** + 触发时机。
1013
+ * 九类动作的声明:**合法事件通道** + **降级语义** + 触发时机。
1016
1014
  * `events` 是动作允许注册的官方事件(注册到声明外的事件在挂载期 fail loud);
1017
1015
  * `services` 是它允许触碰的宿主服务方法(同一纪律的文档面)。
1018
1016
  */
@@ -9911,8 +9909,7 @@ function wireRuntimeContexts(ctx, configs, registry, warnOnce, keep, on) {
9911
9909
  * - `replace: true`:整体替换为 `patch`(不叠加 base,故 `unset` 无比较对象);
9912
9910
  * - `unset`:**按值条件删键** —— 仅当合并结果里的值**恰好等于**声明值时删除该键
9913
9911
  * (`Object.is` 比较)。这来自原 `tool-bootstrap` 剥离 `bootstrapMaxTokens` 的语义
9914
- * (「仅当解析后的 maxTokens 恰好等于本声明注入的那个值时才删」;声明侧见
9915
- * `test/engine/declarations/tool-bootstrap.yml` 声明 4 的 `do.unset`),不是"存在即删"
9912
+ * (「仅当解析后的 maxTokens 恰好等于本声明注入的那个值时才删」),不是"存在即删"
9916
9913
  * ——模型或其它插件设置的同名参数不会被删。
9917
9914
  * 浅合并做不到删键,故删键必须走显式通道。
9918
9915
  * @param params 提示词配置的 `params`(或动作的等价声明)。
@@ -10094,7 +10091,7 @@ function createTurnStopBudget() {
10094
10091
  /**
10095
10092
  * 会话预算条目(`sessionState`:统一访问接口,策略逐条声明)。
10096
10093
  *
10097
- * 键类型 `session.id`;淘汰策略超限 `clear()` 全清(与迁移前的 `sessionMapGet` 同档:
10094
+ * 键类型 `session.id`;淘汰策略超限 `clear()` 全清(与迁移前的会话态记账同档:
10098
10095
  * 第 `MAX_TRACKED_SESSIONS` 个会话仍会清空整个容器);**不声明复位**——预算是纯增量、
10099
10096
  * 不可从事件流重建的计数(与 `progress-reminder` 同档),丢失时机必须与迁移前逐字一致,
10100
10097
  * compaction 也不重置它。
@@ -12578,7 +12575,7 @@ function wireTriggerObservers(ctx, triggers, { plugin, warnOnce } = {}) {
12578
12575
  //#endregion
12579
12576
  //#region engine/branch.mjs
12580
12577
  /**
12581
- * 分支语法的唯一展开器:`if`/`then`/`else` 是当前名,`when`/`do` 是兼容输入。
12578
+ * 分支语法的唯一展开器:只认 `if`/`then`/`else`,旧名 `when`/`do` 由各入口显式拒绝。
12582
12579
  *
12583
12580
  * 规则(`rule-spec.mjs`)与触发器声明(`trigger-spec.mjs`)共用这一份实现——
12584
12581
  * 两套入口必须是同一套语义,否则同一个模块换个入口就换行为。
@@ -12629,7 +12626,7 @@ function expandActions(list, options = {}, label) {
12629
12626
  //#endregion
12630
12627
  //#region engine/rule-spec.mjs
12631
12628
  /** module.yml.rules 的唯一运行时编译入口。旧声明仅由离线迁移器读取。 */
12632
- /** 规则字段:`if`/`then`/`else` 是当前名,`when`/`do` 保留为兼容输入(见 pickAlias)。 */
12629
+ /** 规则字段:只有 `if`/`then`/`else`;旧名 `when`/`do` 留在名单里,只为把 unknown fields 换成一句「已退役,改用 X」。 */
12633
12630
  const RULE_FIELDS = /* @__PURE__ */ new Set([
12634
12631
  "id",
12635
12632
  "name",
@@ -12842,7 +12839,7 @@ function stable(value) {
12842
12839
  if (Array.isArray(value)) return value.map(stable);
12843
12840
  return record$2(value) ? Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])])) : value;
12844
12841
  }
12845
- const digest$1 = (value) => revisionOf(JSON.stringify(stable(value)));
12842
+ const digest = (value) => revisionOf(JSON.stringify(stable(value)));
12846
12843
  const yaml = (value) => new Document(value).toString();
12847
12844
  var ModuleRulesError = class extends Error {
12848
12845
  status;
@@ -12942,7 +12939,7 @@ function projection(source, directory, sourceRevision, validation = {}) {
12942
12939
  ...exclusive === void 0 ? {} : { exclusive }
12943
12940
  };
12944
12941
  files[`${rule.id}.yml`] = yaml(content);
12945
- ruleRevisions[rule.id] = digest$1(content);
12942
+ ruleRevisions[rule.id] = digest(content);
12946
12943
  }
12947
12944
  files[RULES_VARIABLES_FILE] = yaml(variables);
12948
12945
  const state = {
@@ -12951,8 +12948,8 @@ function projection(source, directory, sourceRevision, validation = {}) {
12951
12948
  };
12952
12949
  const revisions = {
12953
12950
  rules: ruleRevisions,
12954
- settings: digest$1(state),
12955
- variables: digest$1(variables)
12951
+ settings: digest(state),
12952
+ variables: digest(variables)
12956
12953
  };
12957
12954
  files[RULES_SETTINGS_FILE] = yaml({
12958
12955
  ...state,
@@ -13941,26 +13938,20 @@ function buildModuleConfigsFromParams(params, options = {}) {
13941
13938
  return out;
13942
13939
  }
13943
13940
  /**
13944
- * 组合模块目录分工:
13945
- * - source/local:本项目自有模块的唯一源文件;
13946
- * - library:随包分发的官方切块与官方预设变体快照(版本化产物,不手工编辑);
13947
- * 生成它的 `rebuild-composition` 脚本随内置模块目录 `modules/` 一并退场——输入源已不存在,
13948
- * 快照本体保留为既成事实,不再重建。
13949
- * 同名文件禁止同时存在,避免 source 与产物漂移。
13941
+ * 组合模块目录:本项目自有模块的唯一源是 `source/local/`。官方切块已随「与预设彻底解耦」
13942
+ * 全部清理——官方工具由会话原有预设提供,模块不需要也不应该重新声明它们。
13950
13943
  */
13951
13944
  function compositionModuleDirs() {
13952
- const root = join(packageEngineDir(), "compositions");
13953
- return [join(root, "source", "local"), join(root, "library")];
13945
+ return [join(packageEngineDir(), "compositions", "source", "local")];
13954
13946
  }
13955
13947
  function assertBareModuleName(name) {
13956
13948
  if (name.includes("/") || name.includes("\\") || name === "." || name === ".." || name.includes("..")) throw new Error(`composition module name ${JSON.stringify(name)} must be a bare library name`);
13957
13949
  }
13958
13950
  function moduleFile(name) {
13959
13951
  assertBareModuleName(name);
13960
- const existing = compositionModuleDirs().map((dir) => join(dir, `${name}.yml`)).filter((file) => existsSync(file));
13961
- if (existing.length > 1) throw new Error(`composition module ${name} is duplicated across source/local and library: ${existing.join(", ")}`);
13962
- if (existing.length === 0) throw new Error(`composition module ${name} not found in source/local or library`);
13963
- return existing[0];
13952
+ const file = join(compositionModuleDirs()[0], `${name}.yml`);
13953
+ if (!existsSync(file)) throw new Error(`composition module ${name} not found in source/local`);
13954
+ return file;
13964
13955
  }
13965
13956
  function assembleModules(spec) {
13966
13957
  const parts = [];
@@ -14629,117 +14620,11 @@ function saveModuleConfigOrder(root, expectedRevision, entries, moduleId) {
14629
14620
  return writeOrders(root, inputs, orders);
14630
14621
  }
14631
14622
  //#endregion
14632
- //#region src/host/legacy-prompt-params.ts
14633
- /** 旧层参数到规则实例的唯一兼容投影;读取、导入物化与旧 API 保存共用。 */
14634
- const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
14635
- const text = (value) => value === void 0 || value === null ? "" : String(value);
14636
- function readLegacyPromptParams(source) {
14637
- const params = {};
14638
- if (!isRecord(source.layerSettings)) return params;
14639
- for (const [layer, settings] of Object.entries(source.layerSettings)) {
14640
- if (!isRecord(settings)) continue;
14641
- for (const [key, value] of Object.entries(settings)) {
14642
- if (!isLegacyPromptParam(key)) continue;
14643
- if (layer !== "pre-step") throw new ModuleLayerSettingsError(`${key} 必须位于 layerSettings.pre-step`);
14644
- params[key] = value === "on" ? true : value === "off" ? false : value;
14645
- }
14646
- }
14647
- return params;
14648
- }
14649
- function legacyBody(source, moduleDir, supplied) {
14650
- if (supplied !== void 0) return supplied;
14651
- if (moduleDir !== void 0) try {
14652
- const file = join(moduleDir, "preset.md");
14653
- if (lstatSync(file).isSymbolicLink()) throw new Error(`旧模块正文不能是链接:${file}`);
14654
- return readFileSync(file, "utf8");
14655
- } catch (error) {
14656
- if (error.code !== "ENOENT") throw error;
14657
- }
14658
- return text(source.content?.presetText);
14659
- }
14660
- function resolveLegacyPromptConfigs(source, options = {}) {
14661
- const params = readLegacyPromptParams(source);
14662
- for (const [key, value] of Object.entries(options.overrides ?? {})) if (isLegacyPromptParam(key) && value !== void 0 && value !== null) params[key] = value;
14663
- const keys = LEGACY_PROMPT_PARAM_KEYS.filter((key) => Object.hasOwn(params, key));
14664
- const applies = (id) => keys.some((key) => LEGACY_PROMPT_PARAM_DEFINITIONS[key].targets.includes(id));
14665
- const provided = (key) => options.partial !== true || Object.hasOwn(params, key);
14666
- const projected = (source.promptConfigs ?? []).map((config) => {
14667
- if (!isRecord(config) || !applies(config.id)) return config;
14668
- const next = {
14669
- ...config,
14670
- params: { ...config.params }
14671
- };
14672
- if (config.id === "near-anchor") {
14673
- if (provided("firstTurnAnchor")) next.enabled = params.firstTurnAnchor === true;
14674
- if (provided("firstTurnCustom")) next.params.useCustom = params.firstTurnCustom === true;
14675
- if (provided("firstTurnText")) next.params.text = text(params.firstTurnText);
14676
- for (const key of [
14677
- "buildPattern",
14678
- "complexPattern",
14679
- "firstTurnBuild",
14680
- "firstTurnInspect",
14681
- "firstTurnDeep"
14682
- ]) if (provided(key)) next.params[key] = text(params[key]);
14683
- } else if (config.id === "router-guide") {
14684
- if (provided("guideEnabled")) next.enabled = params.guideEnabled === true;
14685
- if (provided("guideCustom") || provided("guideEnabled")) {
14686
- const useCustom = provided("guideCustom") ? params.guideCustom === true : next.params.useCustom === true;
14687
- next.params.useCustom = useCustom;
14688
- next.modelScope = useCustom ? "all" : "flash";
14689
- }
14690
- if (provided("guideText")) next.params.text = text(params.guideText);
14691
- for (const key of [
14692
- "complexPattern",
14693
- "guideWeak",
14694
- "guideDeep"
14695
- ]) if (provided(key)) next.params[key] = text(params[key]);
14696
- }
14697
- return next;
14698
- });
14699
- const configs = mergePromptConfigs(projected, options.promptConfigs).map((config) => {
14700
- if (config.id !== "prompt-injector" || !applies(config.id)) return config;
14701
- const body = legacyBody(source, options.moduleDir, options.prompt);
14702
- const explicitWord = text(options.partial === true && Object.hasOwn(params, "firstTurnWord") ? params.firstTurnWord : config.params?.firstTurnWord ?? params.firstTurnWord);
14703
- const anchor = projected.find((entry) => entry.id === "near-anchor");
14704
- const signalWord = (value) => {
14705
- const sentence = /the exact sentence:\s*([A-Za-z]+)/i.exec(value);
14706
- const first = /^\P{L}*([\p{L}]+)/u.exec(value.trim());
14707
- return (sentence?.[1] ?? first?.[1])?.toLowerCase();
14708
- };
14709
- const derivedWords = [...new Set([
14710
- "firstTurnText",
14711
- "firstTurnBuild",
14712
- "firstTurnInspect",
14713
- "firstTurnDeep"
14714
- ].map((key) => signalWord(text(Object.hasOwn(params, key) || options.partial !== true ? params[key] : anchor?.params?.[key === "firstTurnText" ? "text" : key]))).filter((word) => word !== void 0))];
14715
- const { text: _text, texts: _texts, ...rest } = config;
14716
- return {
14717
- ...rest,
14718
- enabled: body.trim().length > 0 && (provided("injectPrompt") ? params.injectPrompt !== false : config.enabled !== false),
14719
- params: {
14720
- ...config.params,
14721
- text: body,
14722
- firstTurnWord: explicitWord,
14723
- anchorWords: explicitWord.length > 0 ? [explicitWord] : derivedWords
14724
- }
14725
- };
14726
- });
14727
- const ids = new Set(configs.map((config) => config.id));
14728
- const consumedKeys = keys.filter((key) => LEGACY_PROMPT_PARAM_DEFINITIONS[key].targets.some((id) => ids.has(id)));
14729
- const unhandled = keys.filter((key) => !consumedKeys.includes(key));
14730
- return {
14731
- configs,
14732
- consumedKeys,
14733
- active: keys.length > 0,
14734
- warnings: unhandled.length === 0 ? [] : [`旧规则参数没有承接配置,原值保留:${unhandled.join(", ")}`]
14735
- };
14736
- }
14737
- //#endregion
14738
- //#region src/host/rules-migration.ts
14739
- /** 旧规则只在显式离线迁移或外部导入时转换;运行时不调用此模块。 */
14623
+ //#region src/host/rule-builder.ts
14624
+ /** 提示词配置 → 规则的纯构造函数:外部导入(SillyTavern / 角色卡 / 世界书)与离线迁移共用,运行时不调用。 */
14740
14625
  const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
14741
14626
  const condition = (items) => items.length === 0 ? void 0 : items.length === 1 ? items[0] : { all: items };
14742
- /** 旧非空行为由显式模板快照保存,迁移器不再持有另一份业务文案。 */
14627
+ /** 旧非空行为由显式模板快照保存,转换器不再持有另一份业务文案。 */
14743
14628
  function legacyPolicyDefaults() {
14744
14629
  const file = join(dirname(packageEngineDir()), "templates", "policies", "legacy-defaults.yml");
14745
14630
  const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
@@ -14787,33 +14672,6 @@ function migrateFillerDefaults(config) {
14787
14672
  if (Object.keys(params).length > 0) next.params = params;
14788
14673
  return next;
14789
14674
  }
14790
- /** 只递归条件语法树;正文、正则字符串及未知对象字段均不扫描或改写。 */
14791
- function migrateConditionDefaults(value) {
14792
- if (!record(value)) return value;
14793
- if (Object.keys(value).length !== 1) return value;
14794
- if (record(value.phase) && value.phase.promoteGate === true) {
14795
- const phase = structuredClone(value.phase);
14796
- fillMissing(phase, legacyPolicyDefaults().promotionGate);
14797
- return { phase };
14798
- }
14799
- for (const key of [
14800
- "all",
14801
- "any",
14802
- "notAny"
14803
- ]) if (Array.isArray(value[key])) return { [key]: value[key].map(migrateConditionDefaults) };
14804
- if (Object.hasOwn(value, "not")) return { not: migrateConditionDefaults(value.not) };
14805
- return value;
14806
- }
14807
- /** 参数桥优先于行配置;仅旧定义里确已打开的指令提示才携入旧非空模板。 */
14808
- function instructionHintPolicyPatch(source) {
14809
- const params = readModuleLayerSettings(source);
14810
- const modules = record(source.moduleConfigs) ? source.moduleConfigs : {};
14811
- const current = record(modules["instruction-hint"]) ? modules["instruction-hint"] : {};
14812
- if (!(Object.hasOwn(params, "instructionHint") ? params.instructionHint === true : current.enabled === true)) return void 0;
14813
- const patched = { ...current };
14814
- fillMissing(patched, legacyPolicyDefaults().instructionHint);
14815
- return JSON.stringify(current) === JSON.stringify(patched) ? void 0 : patched;
14816
- }
14817
14675
  /** 可供外部导入/模板创建复用的纯转换;既有投递身份、策略和官方位置原样保留。 */
14818
14676
  function promptConfigToRule(config) {
14819
14677
  if (!record(config) || typeof config.id !== "string" || config.id.length === 0) throw new TypeError("旧提示词配置必须有 id");
@@ -14905,430 +14763,5 @@ function promptConfigToRule(config) {
14905
14763
  then: actions
14906
14764
  };
14907
14765
  }
14908
- function rebaseTemplate(config, directory, previousBase) {
14909
- if (typeof config.templateFile !== "string" || config.templateFile.length === 0) return config;
14910
- const resolved = isAbsolute(config.templateFile) ? resolve(config.templateFile) : fileURLToPath(new URL(config.templateFile, previousBase));
14911
- const path = relative(directory, resolved);
14912
- if (path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path)) throw new Error(`模板路径不在模块目录,无法安全迁移:${config.templateFile}`);
14913
- if (!existsSync(resolved)) throw new Error(`模板文件缺失,无法验证迁移:${config.templateFile}`);
14914
- return {
14915
- ...config,
14916
- templateFile: `./${path.replaceAll(sep, "/")}`
14917
- };
14918
- }
14919
- function convertLegacyModuleRules(source, options = {}) {
14920
- if (!record(source)) throw new TypeError("模块定义必须是对象");
14921
- const directory = options.directory;
14922
- const legacy = resolveLegacyPromptConfigs(source, { moduleDir: directory });
14923
- if (legacy.warnings.length > 0) throw new Error(`旧快捷参数没有无损承接规则:${legacy.warnings.join("; ")}`);
14924
- if (source.promptConfigs !== void 0 && !Array.isArray(source.promptConfigs)) throw new Error("promptConfigs 必须是数组");
14925
- if (source.triggers !== void 0 && !Array.isArray(source.triggers)) throw new Error("triggers 必须是数组");
14926
- if (source.rules !== void 0 && !Array.isArray(source.rules)) throw new Error("rules 必须是数组");
14927
- const root = directory === void 0 ? void 0 : dirname(directory);
14928
- let configs = mergePromptConfigs(modelRequestConfigs(readModuleLayerSettings(source)), legacy.configs);
14929
- const rules = structuredClone(source.rules ?? []);
14930
- const configOrder = readConfigOrder(source.configOrder);
14931
- if (directory !== void 0 && existsSync(join(directory, "configs"))) {
14932
- const actual = loadPromptConfigFiles(pathToFileURL(join(directory, "configs") + sep));
14933
- for (const [index, config] of actual.entries()) if (configOrder[config.id] === void 0) configOrder[config.id] = config[FILE_SEQUENCE] ?? index * 10;
14934
- if (!Object.hasOwn(source, "promptConfigs") && configs.length === 0) configs = actual;
14935
- else {
14936
- const options = {
14937
- sourceModuleId: source.id,
14938
- configOrder,
14939
- templateBaseUrl: pathToFileURL(join(root, ".engine", "prompt-config-engine.mjs")),
14940
- templateModuleRoot: pathToFileURL(root + sep)
14941
- };
14942
- const prepared = configs.map((config) => injectionConfigSpec({ id: config.id }, {
14943
- id: "inject",
14944
- kind: "inject-text",
14945
- config
14946
- }, source));
14947
- const comparable = (config) => config.strategy === "custom-fallback" ? {
14948
- ...config,
14949
- strategy: "anchor-notice"
14950
- } : config;
14951
- const expected = createPromptConfigs(prepared.map(comparable), options);
14952
- const running = createPromptConfigs(actual.map(comparable), options);
14953
- const fingerprint = (entries) => contentHash(entries.map((config) => {
14954
- const value = { ...config };
14955
- if (value.enabled === false) delete value.texts;
14956
- return value;
14957
- }));
14958
- if (fingerprint(expected) !== fingerprint(running)) throw new Error(`模块 ${String(source.id ?? directory)} 的定义与旧实际物化内容不一致,无法无损迁移;请先明确选择来源`);
14959
- }
14960
- }
14961
- const append = (rule, sequence) => {
14962
- if (rules.some((item) => item.id === rule.id)) throw new Error(`规则身份冲突,须先明确改名:${rule.id}`);
14963
- rules.push(rule);
14964
- if (configOrder[rule.id] === void 0) configOrder[rule.id] = sequence;
14965
- };
14966
- for (const [index, config] of configs.entries()) append(promptConfigToRule(directory === void 0 ? config : rebaseTemplate(config, directory, pathToFileURL(join(root, ".engine", "prompt-config-engine.mjs")))), index * 10);
14967
- for (const [index, raw] of (source.triggers ?? []).entries()) {
14968
- if (!record(raw) || typeof raw.id !== "string" || raw.id.length === 0) throw new Error("旧声明必须有唯一 id");
14969
- const rawActions = Array.isArray(raw.do) ? raw.do : [raw.do];
14970
- let enabled;
14971
- const gates = raw.when == null ? [] : [migrateConditionDefaults(raw.when)];
14972
- const liftCondition = (gate) => {
14973
- if (gate === void 0) return;
14974
- if (rawActions.length !== 1) throw new Error(`声明 ${raw.id} 的动作局部条件不能无损提升为整卡条件;请先拆分声明`);
14975
- gates.push(gate);
14976
- };
14977
- const actions = rawActions.map((action, actionIndex) => {
14978
- if (!record(action) || typeof action.kind !== "string") throw new Error(`旧声明 ${raw.id} 的动作不合法`);
14979
- const point = actionExecutionPoint(action);
14980
- if (raw.channel !== point.channel || raw.phase !== void 0 && raw.phase !== point.phase) throw new Error(`旧声明 ${raw.id} 的执行点不合法`);
14981
- const next = structuredClone(action);
14982
- if (next.kind === "inject-text" && record(next.config)) {
14983
- const converted = promptConfigToRule(next.config);
14984
- if (converted.enabled === false && rawActions.length !== 1) throw new Error(`声明 ${raw.id} 的动作局部停用不能无损提升为整卡开关;请先拆分声明`);
14985
- if (rawActions.length === 1) enabled = converted.enabled;
14986
- liftCondition(converted.if);
14987
- const payload = converted.then[0]?.config;
14988
- const config = {
14989
- ...next.config,
14990
- ...record(payload) ? payload : {}
14991
- };
14992
- delete config.enabled;
14993
- const params = record(config.params) ? config.params : {};
14994
- if (params.complete !== true && params.suppressRuntimeContext !== true) for (const key of [
14995
- "audience",
14996
- "modelScope",
14997
- "promotion",
14998
- "subject",
14999
- "match"
15000
- ]) delete config[key];
15001
- next.config = config;
15002
- }
15003
- if (next.kind === "request-params") {
15004
- const modelScope = Object.hasOwn(next, "modelScope") ? next.modelScope : "pro";
15005
- const scope = {
15006
- ...next.audience == null || next.audience === "" ? {} : { audience: next.audience },
15007
- ...modelScope == null || modelScope === "" || modelScope === "all" ? {} : { modelScope }
15008
- };
15009
- if (Object.keys(scope).length > 0) liftCondition({ scope });
15010
- delete next.audience;
15011
- delete next.modelScope;
15012
- }
15013
- if (next.kind === "inject-text" && record(next.config) && directory !== void 0) next.config = rebaseTemplate(next.config, directory, pathToFileURL(join(directory, "triggers.yml")));
15014
- return {
15015
- ...next,
15016
- id: typeof next.id === "string" && next.id.length > 0 ? next.id : `action-${actionIndex + 1}`,
15017
- kind: action.kind,
15018
- channelOrder: typeof raw.channelOrder === "number" ? raw.channelOrder : 0,
15019
- ...raw.waterfallPosition === void 0 ? {} : { waterfallPosition: raw.waterfallPosition }
15020
- };
15021
- });
15022
- if (actions.length > 1 && actions.some((action) => actionExecutionPoint(action).phase === "after-next")) throw new Error(`声明 ${raw.id} 含多个 after-next 动作,须先核对实际执行顺序再迁移`);
15023
- const layer = actions.find((action) => action.kind === "inject-text" && record(action.config))?.config;
15024
- append({
15025
- id: raw.id,
15026
- ...enabled === void 0 ? {} : { enabled },
15027
- ...layer?.layer === void 0 ? {} : { layer: layer.layer },
15028
- ...condition(gates) === void 0 ? {} : { if: condition(gates) },
15029
- then: actions
15030
- }, (configs.length + index) * 10);
15031
- }
15032
- compileRules(rules, {
15033
- configOrder,
15034
- ...directory === void 0 ? {} : {
15035
- moduleId: source.id,
15036
- promptConfigOptions: rulePromptConfigOptions(directory)
15037
- }
15038
- });
15039
- return {
15040
- rules,
15041
- configOrder,
15042
- consumedLegacyKeys: Object.keys(readLegacyPromptParams(source))
15043
- };
15044
- }
15045
- const MODEL_KEYS = RULE_OWNED_MODEL_PARAMS;
15046
- const OLD_ENGINES = /* @__PURE__ */ new Set(["prompt-config-engine", "declared-triggers"]);
15047
- const digest = (value) => createHash("sha256").update(value).digest("hex");
15048
- const sortedValue = (value) => Array.isArray(value) ? value.map(sortedValue) : record(value) ? Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortedValue(value[key])])) : value;
15049
- const contentHash = (value) => digest(JSON.stringify(sortedValue(value)));
15050
- /** 原证明仍匹配旧卡时才推进角色记忆哈希;用户改过的卡保持未认领。 */
15051
- function migrateMemoryProofs(doc, source, rules) {
15052
- const proofs = record(source.meta) && record(source.meta.characterMemories) ? source.meta.characterMemories : {};
15053
- const configs = Array.isArray(source.promptConfigs) ? source.promptConfigs : [];
15054
- for (const [cardId, proof] of Object.entries(proofs)) {
15055
- if (!record(proof) || typeof proof.configId !== "string") continue;
15056
- const old = configs.find((config) => record(config) && config.id === proof.configId);
15057
- const rule = rules.find((item) => item.id === proof.configId);
15058
- if (old !== void 0 && rule !== void 0 && proof.contentHash === contentHash(old)) doc.setIn([
15059
- "meta",
15060
- "characterMemories",
15061
- cardId,
15062
- "contentHash"
15063
- ], contentHash(rule));
15064
- }
15065
- }
15066
- /** 旧字段移入动作后仍把原注释保留在所属规则上,不把用户说明随旧段删除。 */
15067
- function setMigratedRules(doc, rules) {
15068
- const comments = (node) => {
15069
- const own = record(node) ? [node.commentBefore, node.comment].filter((value) => typeof value === "string" && value.length > 0) : [];
15070
- if (isMap(node)) return [...own, ...node.items.flatMap((pair) => [...comments(pair.key), ...comments(pair.value)])];
15071
- if (isSeq(node)) return [...own, ...node.items.flatMap(comments)];
15072
- return own;
15073
- };
15074
- const previous = [
15075
- "rules",
15076
- "promptConfigs",
15077
- "triggers"
15078
- ].map((key) => doc.get(key, true)).filter(isSeq);
15079
- const nodes = doc.createNode(rules);
15080
- if (isSeq(nodes)) {
15081
- const groupComments = previous.flatMap((node) => [node.commentBefore, node.comment].filter((value) => typeof value === "string"));
15082
- if (groupComments.length > 0) nodes.commentBefore = groupComments.join("\n");
15083
- for (const node of nodes.items) if (isMap(node)) {
15084
- const text = comments(previous.flatMap((item) => item.items).find((item) => isMap(item) && item.get("id") === node.get("id"))).join("\n");
15085
- if (text.length > 0) node.commentBefore = text;
15086
- }
15087
- }
15088
- doc.set("rules", nodes);
15089
- }
15090
- function assertAssetDirectory(root, id, definitionFile) {
15091
- if (definitionFile !== "module.yml") throw new Error("迁移只接受模块定义");
15092
- return assertModuleDirectory(root, id);
15093
- }
15094
- /** 包括资产和生成物的整个模块指纹;回滚不覆盖迁移后出现的任意用户改动。 */
15095
- function treeHash(directory) {
15096
- assertModuleTree(directory);
15097
- const hash = createHash("sha256");
15098
- const visit = (dir) => {
15099
- for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
15100
- const file = join(dir, entry.name);
15101
- const key = relative(directory, file).replaceAll(sep, "/");
15102
- hash.update(JSON.stringify([
15103
- key,
15104
- entry.isDirectory() ? "directory" : "file",
15105
- lstatSync(file).mode
15106
- ]));
15107
- if (entry.isDirectory()) visit(file);
15108
- else hash.update(readFileSync(file));
15109
- }
15110
- };
15111
- visit(directory);
15112
- return hash.digest("hex");
15113
- }
15114
- /** 全量只读预检。root 必须显式提供,不从真实 DSH_HOME 推断。 */
15115
- function planRulesMigration(root, options = {}) {
15116
- if (typeof root !== "string" || root.trim().length === 0 || !isAbsolute(root)) throw new Error("迁移必须显式指定绝对 --root(模块根目录)");
15117
- const canonical = canonicalModulesRoot(root);
15118
- const definitionFile = MODULE_DEFINITION_FILE;
15119
- const items = [];
15120
- for (const entry of readdirSync(canonical, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
15121
- if (entry.name.startsWith(".")) continue;
15122
- if (entry.isSymbolicLink() && existsSync(join(canonical, entry.name, definitionFile))) throw new Error(`迁移目录不能是链接:${entry.name}`);
15123
- if (!entry.isDirectory()) continue;
15124
- if (!existsSync(join(canonical, entry.name, definitionFile))) continue;
15125
- const directory = assertAssetDirectory(canonical, entry.name, definitionFile);
15126
- assertModuleTree(directory);
15127
- const bytes = readFileSync(join(directory, definitionFile));
15128
- const doc = parseDocument(new TextDecoder("utf-8", { fatal: true }).decode(bytes), { logLevel: "silent" });
15129
- if (doc.errors.length > 0) throw new Error(`模块 ${entry.name} YAML 无效:${doc.errors[0].message}`);
15130
- const source = doc.toJS();
15131
- if (!record(source)) throw new Error(`模块 ${entry.name} 定义必须是对象`);
15132
- const layerParams = readModuleLayerSettings(source);
15133
- const oldModules = Array.isArray(source.modules) && source.modules.some((id) => OLD_ENGINES.has(String(id)));
15134
- const oldRules = Object.hasOwn(source, "promptConfigs") || Object.hasOwn(source, "triggers") || Object.keys(readLegacyPromptParams(source)).length > 0 || MODEL_KEYS.some((key) => Object.hasOwn(layerParams, key)) || oldModules;
15135
- const hintPatch = oldRules || !Object.hasOwn(source, "rules") ? instructionHintPolicyPatch(source) : void 0;
15136
- if (!(oldRules || hintPatch !== void 0)) {
15137
- validateModuleDefinitionText(directory, doc.toString());
15138
- if (options.decompose) items.push({
15139
- moduleId: entry.name,
15140
- directory,
15141
- definitionFile,
15142
- originalHash: treeHash(directory),
15143
- sourceHash: digest(bytes),
15144
- nextDefinition: new TextDecoder("utf-8", { fatal: true }).decode(bytes)
15145
- });
15146
- continue;
15147
- }
15148
- if (options.decompose) throw new Error(`模块 ${entry.name} 仍含旧规则来源,请先运行 --apply 迁移再分解`);
15149
- if (!Array.isArray(source.modules)) throw new Error(`模块 ${entry.name} 使用手写组合,无法证明旧引擎装配替换;请先显式整理 modules`);
15150
- if (record(source.params) && LEGACY_PROMPT_PARAM_KEYS.some((key) => Object.hasOwn(source.params, key))) throw new Error(`模块 ${entry.name} 含曾被运行时忽略的顶层旧参数,无法推测激活语义`);
15151
- const converted = convertLegacyModuleRules(source, { directory });
15152
- if (hintPatch !== void 0) {
15153
- for (const [key, value] of Object.entries(hintPatch)) if (!doc.hasIn([
15154
- "moduleConfigs",
15155
- "instruction-hint",
15156
- key
15157
- ])) doc.setIn([
15158
- "moduleConfigs",
15159
- "instruction-hint",
15160
- key
15161
- ], value);
15162
- }
15163
- migrateMemoryProofs(doc, source, converted.rules);
15164
- setMigratedRules(doc, converted.rules);
15165
- doc.set("configOrder", converted.configOrder);
15166
- doc.delete("promptConfigs");
15167
- doc.delete("triggers");
15168
- if (Array.isArray(source.modules)) doc.set("modules", [...new Set(source.modules.map((id) => OLD_ENGINES.has(String(id)) ? "rule-engine" : id))]);
15169
- if (record(source.layerSettings)) {
15170
- for (const [layer, values] of Object.entries(source.layerSettings)) if (record(values)) {
15171
- for (const key of [...converted.consumedLegacyKeys, ...MODEL_KEYS]) if (Object.hasOwn(values, key)) doc.deleteIn([
15172
- "layerSettings",
15173
- layer,
15174
- key
15175
- ]);
15176
- }
15177
- }
15178
- if (record(source.moduleConfigs)) {
15179
- if ([source.moduleConfigs["prompt-config-engine"], source.moduleConfigs["declared-triggers"]].filter(record).map((value) => value.strategyDir).filter((value) => typeof value === "string" && value.length > 0).length > 0) throw new Error(`模块 ${entry.name} 使用自定义策略目录,需先确认统一基准后迁移`);
15180
- for (const id of OLD_ENGINES) doc.deleteIn(["moduleConfigs", id]);
15181
- }
15182
- validateModuleDefinitionText(directory, doc.toString());
15183
- items.push({
15184
- moduleId: entry.name,
15185
- directory,
15186
- definitionFile,
15187
- originalHash: treeHash(directory),
15188
- sourceHash: digest(bytes),
15189
- nextDefinition: doc.toString()
15190
- });
15191
- }
15192
- return {
15193
- root: canonical,
15194
- kind: "modules",
15195
- items
15196
- };
15197
- }
15198
- const BACKUP_DIR = ".rules-migration-backup";
15199
- /** 先完整生成所有候选,再复核全根;每个模块以目录 rename 原子交换并保留原树备份。 */
15200
- async function applyRulesMigration(plan) {
15201
- if (canonicalModulesRoot(plan.root) !== plan.root) throw new Error("迁移根目录身份已变化");
15202
- if (plan.items.length === 0) return { changed: [] };
15203
- const backup = join(plan.root, BACKUP_DIR);
15204
- if (existsSync(backup)) throw new Error(`已存在迁移备份,请先处理:${backup}`);
15205
- const workspace = mkdtempSync(join(plan.root, ".rules-migration-stage-"));
15206
- const staged = /* @__PURE__ */ new Map();
15207
- const manifest = {
15208
- version: 1,
15209
- root: plan.root,
15210
- status: "applying",
15211
- entries: []
15212
- };
15213
- const manifestFile = join(backup, "manifest.json");
15214
- const verify = (item) => {
15215
- if (assertAssetDirectory(plan.root, item.moduleId, item.definitionFile) !== item.directory || treeHash(item.directory) !== item.originalHash || digest(readFileSync(join(item.directory, item.definitionFile))) !== item.sourceHash) throw new Error(`来源 ${item.moduleId} 在预检后已变化,未覆盖`);
15216
- };
15217
- try {
15218
- for (const item of plan.items) {
15219
- verify(item);
15220
- const source = join(workspace, item.moduleId);
15221
- cpSync(item.directory, source, {
15222
- recursive: true,
15223
- errorOnExist: true,
15224
- force: false
15225
- });
15226
- atomicWriteTextFile(join(source, item.definitionFile), item.nextDefinition);
15227
- decomposeModule(source);
15228
- for (const path of [
15229
- "rules.yml",
15230
- "configs",
15231
- "agent.cordis.yml",
15232
- "custom-tools",
15233
- "subagent-tools"
15234
- ]) rmSync(join(source, path), {
15235
- recursive: true,
15236
- force: true
15237
- });
15238
- const generated = source;
15239
- staged.set(item.moduleId, generated);
15240
- manifest.entries.push({
15241
- moduleId: item.moduleId,
15242
- definitionFile: item.definitionFile,
15243
- before: item.originalHash,
15244
- after: treeHash(generated),
15245
- written: false
15246
- });
15247
- }
15248
- for (const item of plan.items) verify(item);
15249
- mkdirSync(backup);
15250
- atomicWriteTextFile(manifestFile, JSON.stringify(manifest, null, 2) + "\n");
15251
- for (const item of plan.items) withModuleLock(plan.root, item.moduleId, () => {
15252
- verify(item);
15253
- const old = join(backup, item.moduleId);
15254
- renameSync(item.directory, old);
15255
- try {
15256
- renameSync(staged.get(item.moduleId), item.directory);
15257
- } catch (error) {
15258
- if (!existsSync(item.directory)) renameSync(old, item.directory);
15259
- throw error;
15260
- }
15261
- manifest.entries.find((entry) => entry.moduleId === item.moduleId).written = true;
15262
- invalidateModuleSpec(item.directory);
15263
- atomicWriteTextFile(manifestFile, JSON.stringify(manifest, null, 2) + "\n");
15264
- });
15265
- manifest.status = "applied";
15266
- atomicWriteTextFile(manifestFile, JSON.stringify(manifest, null, 2) + "\n");
15267
- return {
15268
- changed: plan.items.map((item) => item.moduleId),
15269
- backup
15270
- };
15271
- } catch (error) {
15272
- for (const entry of [...manifest.entries].reverse()) if (entry.written) {
15273
- const current = join(plan.root, entry.moduleId);
15274
- const original = join(backup, entry.moduleId);
15275
- withModuleLock(plan.root, entry.moduleId, () => {
15276
- if (existsSync(current) && treeHash(current) === entry.after && existsSync(original)) {
15277
- const retired = join(workspace, `failed-${entry.moduleId}`);
15278
- renameSync(current, retired);
15279
- renameSync(original, current);
15280
- entry.written = false;
15281
- invalidateModuleSpec(current);
15282
- }
15283
- });
15284
- }
15285
- if (existsSync(manifestFile)) atomicWriteTextFile(manifestFile, JSON.stringify(manifest, null, 2) + "\n");
15286
- throw error;
15287
- } finally {
15288
- if (dirname(workspace) === plan.root && workspace.startsWith(join(plan.root, ".rules-migration-stage-"))) rmSync(workspace, {
15289
- recursive: true,
15290
- force: true
15291
- });
15292
- }
15293
- }
15294
- function rollbackRulesMigration(root, options = {}) {
15295
- if (typeof root !== "string" || !isAbsolute(root)) throw new Error("回滚必须显式指定绝对 --root");
15296
- const canonical = canonicalModulesRoot(root);
15297
- const backup = join(canonical, BACKUP_DIR);
15298
- if (lstatSync(backup).isSymbolicLink()) throw new Error("迁移备份不能是链接");
15299
- const file = join(backup, "manifest.json");
15300
- const manifest = JSON.parse(readFileSync(file, "utf8"));
15301
- if (manifest.version !== 1 || manifest.root !== canonical || !Array.isArray(manifest.entries)) throw new Error("迁移备份不属于当前根");
15302
- if (manifest.status === "rolled-back") return { restored: [] };
15303
- const targets = manifest.entries.filter((entry) => entry.written);
15304
- for (const entry of targets) {
15305
- if (entry.definitionFile !== "module.yml") throw new Error("迁移备份定义类型无效");
15306
- const current = assertAssetDirectory(canonical, entry.moduleId, entry.definitionFile);
15307
- const original = assertAssetDirectory(backup, entry.moduleId, entry.definitionFile);
15308
- if (treeHash(current) !== entry.after || treeHash(original) !== entry.before) throw new Error(`模块 ${entry.moduleId} 或备份已变化,拒绝回滚覆盖`);
15309
- if (existsSync(join(backup, `${entry.moduleId}.migrated`))) throw new Error(`模块 ${entry.moduleId} 的回滚保留目录已存在`);
15310
- }
15311
- if (options.checkOnly === true) return { restored: targets.map((entry) => entry.moduleId) };
15312
- for (const entry of targets) {
15313
- const current = join(canonical, entry.moduleId);
15314
- const retired = join(backup, `${entry.moduleId}.migrated`);
15315
- withModuleLock(canonical, entry.moduleId, () => {
15316
- if (treeHash(current) !== entry.after || treeHash(join(backup, entry.moduleId)) !== entry.before) throw new Error(`模块 ${entry.moduleId} 在回滚期间变化,未覆盖`);
15317
- renameSync(current, retired);
15318
- try {
15319
- renameSync(join(backup, entry.moduleId), current);
15320
- } catch (error) {
15321
- if (!existsSync(current)) renameSync(retired, current);
15322
- throw error;
15323
- }
15324
- entry.written = false;
15325
- invalidateModuleSpec(current);
15326
- atomicWriteTextFile(file, JSON.stringify(manifest, null, 2) + "\n");
15327
- });
15328
- }
15329
- manifest.status = "rolled-back";
15330
- atomicWriteTextFile(file, JSON.stringify(manifest, null, 2) + "\n");
15331
- return { restored: targets.map((entry) => entry.moduleId) };
15332
- }
15333
14766
  //#endregion
15334
- export { wireTriggerObservers as $, packageEngineDir as A, INJECTION_POINT_ORDER as At, userModulesDir as B, validateEngineParamValues as Bt, ensureModuleSeed as C, assertModuleTree as Ct, loadModuleSpec as D, readPersonaSpec as Dt, listModules as E, setModuleDefinitionId as Et, resolveModuleDir as F, validateCustomToolIdentities as Ft, assertCanonicalRuleSource as G, USER_SKILLS_DIR as Gt, editModuleRules as H, DSH_HOME as Ht, resolveModuleFacts as I, ENGINE_PARAM_DEFINITIONS as It, rulePromptConfigOptions as J, ensureModuleSlices as K, resolveModuleParams as L, ENGINE_PARAM_KEYS as Lt, removeModuleCapabilityDeclaration as M, engineRecipe as Mt, removeUserModule as N, isModuleCapabilityPresent as Nt, moduleDirExists as O, ENGINE_EDITOR_GROUP_MAP as Ot, renderComposition as P, moduleCapability as Pt, injectionConfigSpec as Q, saveModuleParams as R, WRITER_PARAM_KEYS as Rt, duplicateUserModule as S, assertModuleId as St, listBuiltinTemplates as T, modulePathExists as Tt, readModuleRules as U, MODULES_DIR as Ut, withModuleDefinition as V, LEGACY_PROMPT_PARAM_KEYS as Vt, ModuleRulesError as W, MODULE_DEFINITION_FILE as Wt, withModuleLock as X, validateModuleDefinitionText as Y, compileRules as Z, applyModuleConfigs as _, RULE_OWNED_MODEL_PARAMS as _t, rollbackRulesMigration as a, runPreStepBatch as at, cloneBuiltinModule as b, readModuleLayerSettings as bt, readConfigOrder as c, actionMatches as ct, assertSafeConfigId as d, prepareStText as dt, compileWhen as et, configFileName as f, renderStText as ft, appendModuleCapabilities as g, atomicWriteTextFile as gt, setModuleEnabled as h, createWarnOnce as ht, promptConfigToRule as i, confirmDelivered as it, removeModuleCapability as j, MODULE_CAPABILITIES as jt, openModuleLocation as k, ENGINE_RECIPES as kt, readModuleConfigOrder as l, ruleFrame as lt, resolveEditDir as m, PROMOTE_EVENTS as mt, convertLegacyModuleRules as n, prepareAction as nt, ModuleConfigOrderError as o, lastWorldBookDiagnostics as ot, enabledModuleIds as p, createEpochPromotion as pt, loadModuleDefinition as q, planRulesMigration as r, applyPromptConfigSources as rt, appendModuleConfigOrder as s, wireLayers as st, applyRulesMigration as t, loadStandingMountFor as tt, saveModuleConfigOrder as u, compareConfigSequence as ut, assertCompositionArray as v, ModuleLayerSettingsError as vt, invalidateModuleSpec as w, canonicalModulesRoot as wt, createModuleCapability as x, assertModuleDirectory as xt, buildModuleConfigsFromParams as y, engineParamPath as yt, saveModulePersona as z, moduleParamFallbacks as zt };
14767
+ export { injectionConfigSpec as $, packageEngineDir as A, modulePathExists as At, userModulesDir as B, validateCustomToolIdentities as Bt, ensureModuleSeed as C, ModuleLayerSettingsError as Ct, loadModuleSpec as D, assertModuleId as Dt, listModules as E, assertModuleDirectory as Et, resolveModuleDir as F, INJECTION_POINT_ORDER as Ft, assertCanonicalRuleSource as G, validateEngineParamValues as Gt, editModuleRules as H, ENGINE_PARAM_KEYS as Ht, resolveModuleFacts as I, MODULE_CAPABILITIES as It, loadModuleDefinition as J, isLegacyPromptParam as Jt, decomposeModule as K, LEGACY_PROMPT_PARAM_DEFINITIONS as Kt, resolveModuleParams as L, engineRecipe as Lt, removeModuleCapabilityDeclaration as M, readPersonaSpec as Mt, removeUserModule as N, ENGINE_EDITOR_GROUP_MAP as Nt, moduleDirExists as O, assertModuleTree as Ot, renderComposition as P, ENGINE_RECIPES as Pt, compileRules as Q, USER_SKILLS_DIR as Qt, saveModuleParams as R, isModuleCapabilityPresent as Rt, duplicateUserModule as S, RULE_OWNED_MODEL_PARAMS as St, listBuiltinTemplates as T, readModuleLayerSettings as Tt, readModuleRules as U, WRITER_PARAM_KEYS as Ut, withModuleDefinition as V, ENGINE_PARAM_DEFINITIONS as Vt, ModuleRulesError as W, moduleParamFallbacks as Wt, validateModuleDefinitionText as X, MODULES_DIR as Xt, rulePromptConfigOptions as Y, DSH_HOME as Yt, withModuleLock as Z, MODULE_DEFINITION_FILE as Zt, applyModuleConfigs as _, createEpochPromotion as _t, appendModuleConfigOrder as a, confirmDelivered as at, cloneBuiltinModule as b, createWarnOnce as bt, saveModuleConfigOrder as c, wireLayers as ct, mergePromptConfigs as d, createPromptConfigs as dt, wireTriggerObservers as et, modelRequestConfigs as f, loadPromptConfigFiles as ft, appendModuleCapabilities as g, renderStText as gt, setModuleEnabled as h, prepareStText as ht, ModuleConfigOrderError as i, applyPromptConfigSources as it, removeModuleCapability as j, setModuleDefinitionId as jt, openModuleLocation as k, canonicalModulesRoot as kt, assertSafeConfigId as l, actionMatches as lt, resolveEditDir as m, compareConfigSequence as mt, legacyPolicyDefaults as n, loadStandingMountFor as nt, readConfigOrder as o, runPreStepBatch as ot, enabledModuleIds as p, FILE_SEQUENCE as pt, ensureModuleSlices as q, LEGACY_PROMPT_PARAM_KEYS as qt, promptConfigToRule as r, prepareAction as rt, readModuleConfigOrder as s, lastWorldBookDiagnostics as st, fillMissing as t, compileWhen as tt, configFileName as u, ruleFrame as ut, assertCompositionArray as v, actionExecutionPoint as vt, invalidateModuleSpec as w, engineParamPath as wt, createModuleCapability as x, atomicWriteTextFile as xt, buildModuleConfigsFromParams as y, PROMOTE_EVENTS as yt, saveModulePersona as z, moduleCapability as zt };