dsh-plugin-prompt-tool 0.7.2 → 0.8.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 (169) hide show
  1. package/README.md +185 -46
  2. package/engine/THIRD_PARTY_LICENSES +25 -0
  3. package/engine/actions.mjs +931 -0
  4. package/engine/anchor-match.mjs +6 -3
  5. package/engine/character-tools.mjs +11 -0
  6. package/engine/classify-task.mjs +18 -0
  7. package/engine/compaction-epoch.mjs +41 -20
  8. package/engine/compositions/library/agent-instructions.yml +9 -0
  9. package/engine/compositions/library/command-goal.yml +2 -6
  10. package/engine/compositions/library/compaction.yml +2 -32
  11. package/engine/compositions/library/delegation-ptc.yml +58 -0
  12. package/engine/compositions/library/delegation.yml +5 -38
  13. package/engine/compositions/library/persistent-shell.yml +4 -13
  14. package/engine/compositions/library/planning.yml +8 -23
  15. package/engine/compositions/library/skill-filesystem-cordis.yml +10 -0
  16. package/engine/compositions/library/skill-filesystem.yml +2 -8
  17. package/engine/compositions/library/tool-ask-user.yml +2 -3
  18. package/engine/compositions/library/tool-bash.yml +4 -12
  19. package/engine/compositions/library/tool-cordis.yml +7 -0
  20. package/engine/compositions/library/tool-fs-search.yml +2 -11
  21. package/engine/compositions/library/tool-fs.yml +2 -5
  22. package/engine/compositions/library/tool-goal.yml +2 -6
  23. package/engine/compositions/library/tool-jobs.yml +2 -18
  24. package/engine/compositions/library/tool-plugin-manager-disabled.yml +8 -0
  25. package/engine/compositions/library/tool-plugin-manager.yml +8 -0
  26. package/engine/compositions/library/tool-present.yml +7 -0
  27. package/engine/compositions/library/tool-presentation.yml +9 -0
  28. package/engine/compositions/library/tool-pwsh.yml +2 -6
  29. package/engine/compositions/library/tool-skill.yml +7 -0
  30. package/engine/compositions/library/tool-todo.yml +2 -4
  31. package/engine/compositions/library/tool-web.yml +2 -1
  32. package/engine/compositions/source/local/character-tools.yml +5 -0
  33. package/engine/compositions/source/local/declared-triggers.yml +12 -0
  34. package/engine/compositions/source/local/filesystem-editor.yml +30 -0
  35. package/engine/compositions/source/local/instruction-hint.yml +15 -0
  36. package/engine/compositions/{library/official-persistent-shell.yml → source/local/persistent-shell-posix.yml} +10 -8
  37. package/engine/compositions/source/local/prompt-config-engine.yml +3 -9
  38. package/engine/compositions/source/local/run-code-env.yml +4 -7
  39. package/engine/compositions/source/local/session-var-tools.yml +5 -0
  40. package/engine/compositions/source/local/skill-search.yml +3 -9
  41. package/engine/compositions/source/local/subagent-tool-policy.yml +21 -0
  42. package/engine/compositions/source/local/tool-bash-disabled.yml +9 -0
  43. package/engine/compositions/source/local/tool-config-engine.yml +8 -0
  44. package/engine/compositions/source/local/tool-git-bash.yml +15 -0
  45. package/engine/compositions/source/local/world-book-tools.yml +5 -0
  46. package/engine/condition.mjs +83 -0
  47. package/engine/declared-triggers.mjs +93 -0
  48. package/engine/executor.mjs +364 -185
  49. package/engine/fields.mjs +219 -0
  50. package/engine/fillers.mjs +12 -141
  51. package/engine/host-package.mjs +51 -0
  52. package/engine/instruction-hint.mjs +285 -0
  53. package/engine/interpolate.mjs +157 -29
  54. package/engine/layers.mjs +495 -75
  55. package/engine/predicates.mjs +753 -0
  56. package/engine/prompt-config-engine.mjs +63 -8
  57. package/engine/run-code-env.mjs +48 -31
  58. package/engine/schema.mjs +444 -103
  59. package/engine/sdk-strip.mjs +365 -0
  60. package/engine/session-var-tools.mjs +11 -0
  61. package/engine/session-vars.mjs +8 -6
  62. package/engine/shared.mjs +137 -0
  63. package/engine/skill-search.mjs +34 -21
  64. package/engine/st-macros.d.mts +22 -0
  65. package/engine/st-macros.mjs +208 -0
  66. package/engine/st-render.mjs +105 -0
  67. package/engine/st-world-book.d.mts +10 -0
  68. package/engine/st-world-book.mjs +259 -0
  69. package/engine/strategies.mjs +31 -20
  70. package/engine/subagent-tool-policy-core.mjs +400 -0
  71. package/engine/subagent-tool-policy.mjs +327 -0
  72. package/engine/tool-config-engine.mjs +207 -145
  73. package/engine/tool-definition.mjs +143 -0
  74. package/engine/{custom-bash.mjs → tool-git-bash.mjs} +38 -13
  75. package/engine/trigger-editor-meta.mjs +48 -0
  76. package/engine/trigger-spec.mjs +257 -0
  77. package/engine/trigger.mjs +304 -0
  78. package/engine/world-book-tools.mjs +11 -0
  79. package/lib/client.js +16495 -5456
  80. package/lib/client.js.map +1 -1
  81. package/lib/index.d.mts +628 -382
  82. package/lib/index.mjs +5391 -2942
  83. package/lib/paths-CY5psBqE.d.mts +1233 -0
  84. package/lib/preset-package-BnJqrU4R.mjs +16792 -0
  85. package/lib/preset-transfer.d.mts +2 -0
  86. package/lib/preset-transfer.mjs +2 -0
  87. package/package.json +91 -49
  88. package/preset/pt-cordis/preset.yml +43 -0
  89. package/preset/pt-cordis/skills/cordis-composition-reference/SKILL.md +29 -0
  90. package/preset/pt-cordis/skills/cordis-composition-reference/references/packages.md +501 -0
  91. package/preset/pt-cordis/skills/cordis-plugin-development/SKILL.md +38 -0
  92. package/preset/pt-cordis/skills/cordis-plugin-development/references/host-plugin.md +60 -0
  93. package/preset/pt-cordis/skills/cordis-plugin-development/references/mcp-bundle.md +5 -0
  94. package/preset/pt-cordis/skills/cordis-plugin-development/references/ui-plugin.md +13 -0
  95. package/preset/pt-cordis/skills/cordis-plugin-development/references/verification.md +5 -0
  96. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/client.js +22 -0
  97. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/cordis.patch.yml +3 -0
  98. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/index.js +2 -0
  99. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/package.json +15 -0
  100. package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/cordis.patch.yml +8 -0
  101. package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/package.json +7 -0
  102. package/preset/pt-cordis/skills/editing-cordis-compositions/SKILL.md +84 -0
  103. package/preset/pt-custom/preset.yml +15 -0
  104. package/preset/pt-minimal/preset.yml +27 -0
  105. package/preset/pt-ptc/preset.yml +45 -0
  106. package/preset/pt-standard/preset.yml +43 -0
  107. package/templates/14-first-turn-anchor.yml +4 -4
  108. package/templates/15-guide-auto.yml +3 -3
  109. package/templates/16-custom-fallback.yml +1 -0
  110. package/templates/18-placeholder-env-facts.yml +3 -3
  111. package/templates/20-system-section.yml +3 -2
  112. package/templates/65-turn-stop.yml +18 -0
  113. package/templates/66-subagent-start.yml +14 -0
  114. package/templates/75-subagent-end.yml +15 -0
  115. package/templates/tools/10-shell-command.yml +1 -1
  116. package/templates/tools/40-delegate-character.yml +1 -1
  117. package/templates/tools/50-delegate-worldbook.yml +1 -1
  118. package/templates/tools/60-delegate-session-var.yml +1 -1
  119. package/templates/tools/70-delegate-worldbook-upsert.yml +1 -1
  120. package/engine/anchor-turn.mjs +0 -68
  121. package/engine/code-presentation.mjs +0 -103
  122. package/engine/compositions/library/anchor-turn.yml +0 -10
  123. package/engine/compositions/library/bootstrap-filesystem.yml +0 -19
  124. package/engine/compositions/library/code-presentation.yml +0 -8
  125. package/engine/compositions/library/context-gate.yml +0 -39
  126. package/engine/compositions/library/cot-drip.yml +0 -11
  127. package/engine/compositions/library/custom-bash.yml +0 -14
  128. package/engine/compositions/library/deliberation-gate.yml +0 -11
  129. package/engine/compositions/library/official-agent-instructions.yml +0 -17
  130. package/engine/compositions/library/official-skill-filesystem-cordis.yml +0 -10
  131. package/engine/compositions/library/official-tool-bash.yml +0 -6
  132. package/engine/compositions/library/official-tool-cordis.yml +0 -13
  133. package/engine/compositions/library/official-tool-presentation.yml +0 -7
  134. package/engine/compositions/library/official-tool-skill.yml +0 -14
  135. package/engine/compositions/library/persona.yml +0 -13
  136. package/engine/compositions/library/prompt-config-engine.yml +0 -16
  137. package/engine/compositions/library/run-code-env.yml +0 -15
  138. package/engine/compositions/library/skill-search.yml +0 -14
  139. package/engine/compositions/library/str-replace-editor.yml +0 -10
  140. package/engine/compositions/library/tool-bootstrap.yml +0 -14
  141. package/engine/compositions/library/tool-config-engine.yml +0 -9
  142. package/engine/compositions/library/tool-filter.yml +0 -8
  143. package/engine/compositions/source/local/code-presentation.yml +0 -8
  144. package/engine/compositions/source/local/context-gate.yml +0 -36
  145. package/engine/compositions/source/local/custom-bash.yml +0 -11
  146. package/engine/compositions/source/local/str-replace-editor.yml +0 -10
  147. package/engine/compositions/source/local/tool-bootstrap.yml +0 -11
  148. package/engine/compositions/source/local/tool-filter.yml +0 -8
  149. package/engine/context-gate.mjs +0 -305
  150. package/engine/cot-drip.mjs +0 -137
  151. package/engine/deliberation-gate.mjs +0 -127
  152. package/engine/tool-bootstrap.mjs +0 -476
  153. package/engine/tool-filter.mjs +0 -80
  154. package/lib/preset-core.d.mts +0 -10
  155. package/lib/preset-core.mjs +0 -77
  156. package/lib/prompt-configs-BnQKRHVb.mjs +0 -884
  157. package/lib/prompt-configs-CN0sJTUL.d.mts +0 -193
  158. package/preset/anchored/preset.yml +0 -296
  159. package/preset/creative/preset.yml +0 -67
  160. package/preset/creative/skills/cordis-plugin-development/SKILL.md +0 -420
  161. package/preset/creative/skills/editing-cordis-compositions/SKILL.md +0 -165
  162. package/preset/custom/preset.yml +0 -12
  163. package/preset/liangshen/preset.yml +0 -75
  164. package/preset/minimal/preset.yml +0 -45
  165. package/preset/ptc/preset.yml +0 -58
  166. package/preset/standard/preset.yml +0 -57
  167. package/skills/manifest.json +0 -7
  168. package/skills/sandboxmod/SKILL.md +0 -49
  169. package/skills/web ui/SKILL.md +0 -42
@@ -1,884 +0,0 @@
1
- import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { basename, dirname, join, resolve, sep } from "node:path";
3
- import { spawn } from "node:child_process";
4
- import { fileURLToPath } from "node:url";
5
- import { Pair, Scalar, YAMLMap, YAMLSeq, parse, parseDocument } from "yaml";
6
- import { homedir } from "node:os";
7
- //#region src/host/paths.ts
8
- /** host 数据层的部署路径与序数常量(settings 层与运行时共用,避免 host→config 反向依赖)。 */
9
- /**
10
- * 包内 skills 目录。
11
- * 源码位于 src/host/(../../skills = 包根/skills),构建后内联进 lib/(层级变浅)。
12
- * 统一向上查找最近包含 skills/manifest.json 的包根,两种形态都正确。
13
- */
14
- const SKILLS_DIR = (() => {
15
- let dir = dirname(fileURLToPath(import.meta.url));
16
- for (let depth = 0; depth < 6; depth += 1) {
17
- if (existsSync(join(dir, "skills", "manifest.json"))) return join(dir, "skills");
18
- const parent = dirname(dir);
19
- if (parent === dir) break;
20
- dir = parent;
21
- }
22
- return join(dir, "skills");
23
- })();
24
- /**
25
- * 部署路径默认值;凡是不同部署可能需要不同值的参数都通过 Config 暴露,
26
- * cordis.yml 可以覆盖,无需改代码。
27
- * 与官方 dsh-home-paths.resolveDshHome 语义对齐:
28
- * 未设置 / 空串 / 纯空白回退 OS home 下的 .dsh;支持 ~、~/、~\;
29
- * 相对路径按进程 cwd 解析为绝对路径,避免生成目录随运行目录漂移。
30
- */
31
- function resolveDshHome() {
32
- const ambient = process.env.DSH_HOME;
33
- if (typeof ambient !== "string" || ambient.trim().length === 0) return join(homedir(), ".dsh");
34
- const expanded = ambient === "~" ? homedir() : ambient.startsWith("~/") || ambient.startsWith("~\\") ? join(homedir(), ambient.slice(2)) : ambient;
35
- return resolve(expanded);
36
- }
37
- const DSH_HOME = resolveDshHome();
38
- const DEFAULT_RESIDENT_AGENTS_PATH = join(DSH_HOME, "AGENTS.md");
39
- /**
40
- * 预设根:官方 USER_PRESET_DIR(DSH_HOME/.agent-presets)。
41
- * 每个预设一个官方预设目录(agent.cordis.yml 组合本体 + preset.yml 参数),
42
- * 宿主 agent-presets 直接可挂载;共享引擎在 .engine(点前缀 → discovery 跳过)。
43
- */
44
- const DEFAULT_PRESET_DIR = join(DSH_HOME, ".agent-presets");
45
- join(DEFAULT_PRESET_DIR, ".engine");
46
- /** 旧版用户预设目录(~/.dsh/presets;迁移后归档为 presets.bak-*)。 */
47
- const LEGACY_USER_PRESETS_DIR = join(DSH_HOME, "presets");
48
- /** 旧版容器根(.agent-presets/prompt-tool;迁移后归档为 prompt-tool.bak-*)。 */
49
- const LEGACY_CONTAINER_DIR = join(DEFAULT_PRESET_DIR, "prompt-tool");
50
- const DEFAULT_SKILLS_DIR = SKILLS_DIR;
51
- //#endregion
52
- //#region src/host/manifest.ts
53
- /**
54
- * manifest — 预设模板单一参数 YAML(preset.yml)的加载与引擎参数解析。
55
- *
56
- * 一个预设 = 一个 preset.yml:
57
- * - modules/params/content/meta 全部是直读参数,无模板语法;
58
- * - 默认提示词配置由引擎按 params 生成(见 write-preset),promptConfigs 仅为可选覆盖;
59
- * - 组合模块的行级 config 由参数桥 buildModuleConfigsFromParams 按 params
60
- * 构造对象合并(取代旧 __TOKEN__ 文本渲染,无占位符、无文本往返),
61
- * params(UI/基础层)优先于 moduleConfigs 行级直写(旧作者锁定语义已移除)。
62
- * 本模块负责参数归一化与引擎模块配置装配;所有 anchored 专属行为都在引擎内部。
63
- */
64
- /** 包根 preset/ 目录(配置/模板文件夹):兼容源码运行(src/host)与打包运行(lib/)。 */
65
- function packagePresetDir() {
66
- const candidates = [new URL("../preset/", import.meta.url), new URL("../../preset/", import.meta.url)];
67
- for (const candidate of candidates) {
68
- const dir = fileURLToPath(candidate);
69
- if (existsSync(dir)) return dir;
70
- }
71
- throw new Error("prompt-tool: cannot locate package preset/ directory");
72
- }
73
- /** 包根 engine/ 目录(插件引擎,与配置文件夹分离):兼容源码与打包运行。 */
74
- function packageEngineDir() {
75
- const candidates = [new URL("../engine/", import.meta.url), new URL("../../engine/", import.meta.url)];
76
- for (const candidate of candidates) {
77
- const dir = fileURLToPath(candidate);
78
- if (existsSync(join(dir, "prompt-config-engine.mjs"))) return dir;
79
- }
80
- throw new Error("prompt-tool: cannot locate package engine/ directory");
81
- }
82
- /**
83
- * preset.yml 读缓存:按 mtime+size 签名失效。load()/describe/param-overrides 等
84
- * 每次请求读盘解析,加缓存后同一文件只解析一次;写盘路径(savePresetParams /
85
- * withPresetDoc)与外部编辑(stat 签名变化)都会正确失效。
86
- */
87
- const presetSpecCache = /* @__PURE__ */ new Map();
88
- /** 写盘后失效缓存(调用方在写完 preset.yml 后调用;不调用也安全——stat 签名兜底)。 */
89
- function invalidatePresetSpec(dir) {
90
- presetSpecCache.delete(join(dir, "preset.yml"));
91
- }
92
- /** 加载某个预设模板的单一参数文件 preset/<name>/preset.yml。 */
93
- function loadPresetSpec(dir) {
94
- const file = join(dir, "preset.yml");
95
- const stat = statSync(file);
96
- const cached = presetSpecCache.get(file);
97
- if (cached !== void 0 && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) return cached.spec;
98
- const raw = readFileSync(file, "utf8");
99
- const parsed = parse(raw, { logLevel: "silent" });
100
- if (parsed === null || typeof parsed !== "object") throw new Error(`preset ${join(dir, "preset.yml")} is not a YAML map`);
101
- if (typeof parsed.id !== "string" || parsed.id.length === 0) parsed.id = basename(dir);
102
- const flattenModelGroup = (source, mapping) => {
103
- if (source === null || typeof source !== "object" || Array.isArray(source)) return;
104
- if (parsed.params === null || typeof parsed.params !== "object" || Array.isArray(parsed.params)) parsed.params = {};
105
- for (const [segmentKey, flatKey] of Object.entries(mapping)) {
106
- const value = source[segmentKey];
107
- if (value !== void 0) parsed.params[flatKey] = value;
108
- }
109
- };
110
- const flattenMapping = (segmentName) => {
111
- const mapping = {};
112
- for (const [flatKey, [segment, segmentKey]] of Object.entries(MODEL_SEGMENT_MAP)) if (segment === segmentName) mapping[segmentKey] = flatKey;
113
- return mapping;
114
- };
115
- flattenModelGroup(parsed.model, flattenMapping("model"));
116
- flattenModelGroup(parsed.subagentModel, flattenMapping("subagentModel"));
117
- presetSpecCache.set(file, {
118
- mtimeMs: stat.mtimeMs,
119
- size: stat.size,
120
- spec: parsed
121
- });
122
- return parsed;
123
- }
124
- /** 读取预设模板内容资产(presetText / agentsText);模板缺失时静默降级。
125
- * 模板目录按 resolvePresetDir 解析(用户自定义预设优先,包内模板回退)。 */
126
- function loadPresetContent(template = "anchored") {
127
- try {
128
- const spec = loadPresetSpec(resolvePresetDir(template));
129
- return {
130
- presetText: typeof spec.content?.presetText === "string" ? spec.content.presetText : "",
131
- agentsText: typeof spec.content?.agentsText === "string" ? spec.content.agentsText : ""
132
- };
133
- } catch {
134
- return {
135
- presetText: "",
136
- agentsText: ""
137
- };
138
- }
139
- }
140
- const asString = (value, fallback = "") => {
141
- if (typeof value === "string") return value;
142
- if (value === void 0 || value === null) return fallback;
143
- return String(value);
144
- };
145
- /**
146
- * 顶层模型段 ↔ 扁平参数键双向映射(单一来源)。
147
- * loadPresetSpec 展平(段 → 扁平键)与 savePresetParams 迁移(扁平键 → 段)
148
- * 共用本常量,杜绝两处字面量漂移。段结构 = 官方 agent-default-model 同构。
149
- */
150
- const MODEL_SEGMENT_MAP = {
151
- modelProvider: ["model", "provider"],
152
- modelName: ["model", "name"],
153
- modelReasoningEffort: ["model", "reasoningEffort"],
154
- modelTemperature: ["model", "temperature"],
155
- modelMaxTokens: ["model", "maxTokens"],
156
- subagentModelProvider: ["subagentModel", "provider"],
157
- subagentModelName: ["subagentModel", "name"],
158
- subagentReasoningEffort: ["subagentModel", "reasoningEffort"],
159
- subagentTemperature: ["subagentModel", "temperature"],
160
- subagentMaxTokens: ["subagentModel", "maxTokens"]
161
- };
162
- /** 预设根:官方 USER_PRESET_DIR(~/.dsh/.agent-presets),导入/新建/种子化的预设都放这里。 */
163
- function userPresetsDir() {
164
- return DEFAULT_PRESET_DIR;
165
- }
166
- /** 在指定扫描目录内按 template 定位预设目录:目录名精确匹配优先,preset.yml 的 id 匹配兜底。 */
167
- function findPresetDir(scanDir, template) {
168
- const exact = join(scanDir, template);
169
- if (existsSync(join(exact, "preset.yml"))) return exact;
170
- try {
171
- for (const entry of readdirSync(scanDir, { withFileTypes: true })) {
172
- if (!entry.isDirectory()) continue;
173
- const dir = join(scanDir, entry.name);
174
- try {
175
- if (loadPresetSpec(dir).id === template) return dir;
176
- } catch {}
177
- }
178
- } catch {}
179
- }
180
- /**
181
- * 解析预设模板目录:用户自定义优先,包内模板回退。
182
- * 目录名与 preset.yml id 双匹配(UI 切换值=目录名;旧 settings 存量值=id 也兼容)。
183
- */
184
- function resolvePresetDir(template) {
185
- return findPresetDir(userPresetsDir(), template) ?? findPresetDir(packagePresetDir(), template) ?? join(packagePresetDir(), template);
186
- }
187
- /** 可用预设清单:全部来自预设根 ~/.dsh/.agent-presets(官方预设目录,含 agent.cordis.yml
188
- * 即被宿主挂载;点前缀目录与无 preset.yml 的官方目录跳过,不占本插件列表)。 */
189
- function listPresets() {
190
- const scan = (dir) => {
191
- try {
192
- return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).filter((entry) => entry.name !== "prompt-tool").flatMap((entry) => {
193
- try {
194
- const spec = loadPresetSpec(join(dir, entry.name));
195
- if (typeof spec.id !== "string" || spec.id.length === 0) return [];
196
- return [{
197
- id: entry.name,
198
- name: spec.name,
199
- user: true,
200
- ...typeof spec.description === "string" && spec.description.length > 0 ? { description: spec.description } : {},
201
- ...spec.meta !== void 0 && spec.meta !== null ? { meta: spec.meta } : {}
202
- }];
203
- } catch {
204
- return [];
205
- }
206
- });
207
- } catch {
208
- return [];
209
- }
210
- };
211
- const byId = /* @__PURE__ */ new Map();
212
- for (const preset of scan(userPresetsDir())) byId.set(preset.id, preset);
213
- return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
214
- }
215
- /** 插件目录模板清单(「新建」选择器与首次种子化数据源;不直接出现在预设列表)。 */
216
- function listBuiltinTemplates() {
217
- try {
218
- return readdirSync(packagePresetDir(), { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => entry.name !== "custom").flatMap((entry) => {
219
- try {
220
- const spec = loadPresetSpec(join(packagePresetDir(), entry.name));
221
- return [{
222
- id: entry.name,
223
- name: spec.name
224
- }];
225
- } catch {
226
- return [];
227
- }
228
- }).sort((a, b) => a.id.localeCompare(b.id));
229
- } catch {
230
- return [];
231
- }
232
- }
233
- /** 插件状态文件(DSH_HOME 下,预设根之外):与用户资产解耦,删除/备份/迁移预设根不影响状态。
234
- * 原子写(tmp+rename),避免半写文件。 */
235
- function stateFilePath() {
236
- return join(DSH_HOME, ".prompt-tool-state.json");
237
- }
238
- /** 读插件状态;文件缺失/损坏返回空对象(按未标记处理,触发首次动作)。 */
239
- function readPluginState() {
240
- try {
241
- const parsed = JSON.parse(readFileSync(stateFilePath(), "utf8"));
242
- return parsed !== null && typeof parsed === "object" ? parsed : {};
243
- } catch {
244
- return {};
245
- }
246
- }
247
- /** 原子写插件状态(tmp+rename)。 */
248
- function writePluginState(state) {
249
- const file = stateFilePath();
250
- const tmp = `${file}.tmp`;
251
- writeFileSync(tmp, JSON.stringify(state, null, 2), "utf8");
252
- renameSync(tmp, file);
253
- }
254
- /** 首次启动种子化:把插件目录全部内置模板复制到预设根(state.seeded 后不再自动补)。
255
- * 用户删除的预设不会自动复活;升级新增的模板用「新建」按需复制。
256
- * 兼容旧版预设根内 .pt-seeded 标记:存在即视为已种子化,并迁入状态文件后删除。 */
257
- function ensurePresetSeed() {
258
- const root = userPresetsDir();
259
- const legacyMark = join(root, ".pt-seeded");
260
- const state = readPluginState();
261
- if (existsSync(legacyMark)) try {
262
- writePluginState({
263
- ...state,
264
- seeded: true
265
- });
266
- rmSync(legacyMark, { force: true });
267
- } catch {}
268
- const created = [];
269
- try {
270
- mkdirSync(root, { recursive: true });
271
- for (const entry of readdirSync(packagePresetDir(), { withFileTypes: true })) {
272
- if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
273
- const target = join(root, entry.name);
274
- if (existsSync(target)) continue;
275
- cpSync(join(packagePresetDir(), entry.name), target, { recursive: true });
276
- created.push(entry.name);
277
- }
278
- writePluginState({
279
- ...readPluginState(),
280
- seeded: true
281
- });
282
- } catch {}
283
- return { created };
284
- }
285
- /** 从插件目录复制内置预设到预设根(新建/还原)。
286
- * autoSuffix=true(自定义预设入口)时同名自动递增(custom → custom-2 → …);否则同名拒绝。 */
287
- function cloneBuiltinPreset(id, autoSuffix = false) {
288
- if (typeof id !== "string" || id.length === 0 || id === "." || id === ".." || id.includes("/") || id.includes("\\")) return {
289
- ok: false,
290
- message: `非法预设 id:${id}`
291
- };
292
- const builtin = findPresetDir(packagePresetDir(), id);
293
- if (builtin === void 0) return {
294
- ok: false,
295
- message: `预设 ${id} 不是包内置预设`
296
- };
297
- let targetId = id;
298
- let target = join(userPresetsDir(), targetId);
299
- if (existsSync(target)) {
300
- if (!autoSuffix) return {
301
- ok: false,
302
- message: `用户目录已存在同名预设 ${id},请先删除再新建`
303
- };
304
- for (let suffix = 2;; suffix++) {
305
- targetId = `${id}-${suffix}`;
306
- target = join(userPresetsDir(), targetId);
307
- if (!existsSync(target)) break;
308
- }
309
- }
310
- try {
311
- mkdirSync(userPresetsDir(), { recursive: true });
312
- cpSync(builtin, target, {
313
- recursive: true,
314
- force: true
315
- });
316
- return {
317
- ok: true,
318
- id: targetId
319
- };
320
- } catch (error) {
321
- return {
322
- ok: false,
323
- message: `新建预设失败:${error instanceof Error ? error.message : String(error)}`
324
- };
325
- }
326
- }
327
- /** 复制用户预设目录为新预设(id 自动递增:<id>-copy / <id>-copy-2 / …)。
328
- * 复制的是用户目录完整副本(preset.yml / agent.cordis.yml / prompt-configs /
329
- * 内容资产 / 覆盖文件),与「从内置模板新建」互补:后者还原模板,前者备份现状。 */
330
- function duplicateUserPreset(id, presetRoot = userPresetsDir()) {
331
- if (typeof id !== "string" || id.length === 0 || id === "." || id === ".." || id.includes("/") || id.includes("\\")) return {
332
- ok: false,
333
- message: `非法预设 id:${id}`
334
- };
335
- const root = resolve(presetRoot);
336
- const source = resolve(join(root, id));
337
- const rootResolved = resolve(root);
338
- if (source !== rootResolved && !source.startsWith(rootResolved + sep)) return {
339
- ok: false,
340
- message: `预设路径越界:${id}`
341
- };
342
- if (!existsSync(source)) return {
343
- ok: false,
344
- message: `预设 ${id} 不存在`
345
- };
346
- let targetId = `${id}-copy`;
347
- let target = join(root, targetId);
348
- for (let suffix = 2; existsSync(target); suffix++) {
349
- targetId = `${id}-copy${suffix}`;
350
- target = join(root, targetId);
351
- }
352
- try {
353
- mkdirSync(root, { recursive: true });
354
- cpSync(source, target, {
355
- recursive: true,
356
- force: true
357
- });
358
- return {
359
- ok: true,
360
- id: targetId
361
- };
362
- } catch (error) {
363
- return {
364
- ok: false,
365
- message: `复制预设失败:${error instanceof Error ? error.message : String(error)}`
366
- };
367
- }
368
- }
369
- /** 在系统文件管理器中打开预设目录(尽力而为:无桌面环境时打开失败也返回路径供 UI 展示)。 */
370
- function openPresetLocation(id, presetRoot = userPresetsDir()) {
371
- const isBareId = typeof id === "string" && id.length > 0 && id !== "." && id !== ".." && !id.includes("/") && !id.includes("\\");
372
- const isCardPath = typeof id === "string" && /^\/\.[a-zA-Z0-9_-]+\/[^/\\]+$/.test(id);
373
- if (!isBareId && !isCardPath) return {
374
- ok: false,
375
- message: `非法预设 id:${id}`,
376
- path: ""
377
- };
378
- const dir = resolve(join(presetRoot, id));
379
- const rootResolved = resolve(presetRoot);
380
- if (dir !== rootResolved && !dir.startsWith(rootResolved + sep)) return {
381
- ok: false,
382
- message: `预设路径越界:${id}`,
383
- path: dir
384
- };
385
- if (!existsSync(dir)) return {
386
- ok: false,
387
- message: `预设 ${id} 不存在`,
388
- path: dir
389
- };
390
- try {
391
- const command = process.platform === "win32" ? "explorer" : process.platform === "darwin" ? "open" : "xdg-open";
392
- spawn(command, [dir], {
393
- detached: true,
394
- stdio: "ignore"
395
- }).unref();
396
- return {
397
- ok: true,
398
- path: dir
399
- };
400
- } catch (error) {
401
- return {
402
- ok: false,
403
- message: `打开目录失败:${error instanceof Error ? error.message : String(error)}`,
404
- path: dir
405
- };
406
- }
407
- }
408
- /** 删除 yaml 两级键;父集合缺失时不触发 deleteIn 对不存在集合的异常。 */
409
- function deleteYamlPath(doc, path) {
410
- if (doc.hasIn(path)) doc.deleteIn(path);
411
- }
412
- /** 删除扁平 params 键。 */
413
- function deleteFlatParam(doc, key) {
414
- deleteYamlPath(doc, ["params", key]);
415
- }
416
- /**
417
- * 保存预设参数:写激活预设目录 preset.yml 的 params(merge)/ promptConfigs(整体替换)。
418
- * parseDocument 保留注释与未知键(preset.yml 模板含大量注释);空值键删除('' / [],
419
- * 回落模板/引擎默认;0 与 false 照常写入--语义与函数内注释、docs §3 一致)。写入后删除 prompt-tool.overrides.yml(旧参数覆盖
420
- * 通道残留——参数已并入 preset.yml,避免旧值覆盖新值)。
421
- */
422
- function savePresetParams(presetRoot, templateName, params, promptConfigs, variables, variablesEnabled) {
423
- const file = join(presetRoot, templateName, "preset.yml");
424
- if (!existsSync(file)) throw new Error(`preset ${templateName} 无 preset.yml`);
425
- const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
426
- if (params !== void 0) for (const [key, value] of Object.entries(params)) {
427
- if (value === void 0 || value === null || key.trim().length === 0) continue;
428
- const isEmpty = value === "" || Array.isArray(value) && value.length === 0;
429
- const segment = MODEL_SEGMENT_MAP[key];
430
- if (segment !== void 0) {
431
- if (isEmpty) deleteYamlPath(doc, [segment[0], segment[1]]);
432
- else doc.setIn([segment[0], segment[1]], value);
433
- deleteFlatParam(doc, key);
434
- } else if (isEmpty) deleteFlatParam(doc, key);
435
- else doc.setIn(["params", key], value);
436
- }
437
- if (promptConfigs !== void 0) {
438
- const cleaned = promptConfigs.map((config) => {
439
- if (config === null || typeof config !== "object" || Array.isArray(config)) return config;
440
- const vars = config.variables;
441
- if (vars === null || typeof vars !== "object" || Array.isArray(vars)) return config;
442
- const kept = Object.fromEntries(Object.entries(vars).filter(([key]) => key.trim().length > 0));
443
- const next = { ...config };
444
- if (Object.keys(kept).length > 0) next.variables = kept;
445
- else delete next.variables;
446
- return next;
447
- });
448
- doc.setIn(["promptConfigs"], cleaned);
449
- }
450
- if (variables !== void 0) {
451
- const kept = Object.fromEntries(Object.entries(variables).filter(([key, value]) => key.trim().length > 0 && typeof value === "string"));
452
- if (Object.keys(kept).length > 0) doc.setIn(["variables"], kept);
453
- else doc.deleteIn(["variables"]);
454
- for (const [key, value] of Object.entries(variables)) {
455
- if (typeof value !== "string") continue;
456
- deleteFlatParam(doc, key);
457
- }
458
- }
459
- if (variablesEnabled !== void 0) {
460
- if (variablesEnabled) doc.deleteIn(["variablesEnabled"]);
461
- else doc.setIn(["variablesEnabled"], false);
462
- }
463
- writeFileSync(file, doc.toString(), "utf8");
464
- invalidatePresetSpec(join(presetRoot, templateName));
465
- rmSync(join(presetRoot, templateName, "prompt-tool.overrides.yml"), { force: true });
466
- }
467
- /** 预设文件读-改-写(parseDocument 保留注释与未知键;mutate 内 setIn/deleteIn)。
468
- * 角色卡库(characters)与世界书工具(world-book-tools)共用此入口,避免
469
- * 各自实现 parseDocument 往返。 */
470
- function withPresetDoc(presetDir, mutate) {
471
- const file = join(presetDir, "preset.yml");
472
- if (!existsSync(file)) throw new Error(`${presetDir} 无 preset.yml`);
473
- const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
474
- mutate(doc);
475
- writeFileSync(file, doc.toString(), "utf8");
476
- invalidatePresetSpec(presetDir);
477
- }
478
- /** 删除预设目录(预设根/<id>;含同名导入的 .bak-* 备份目录)。
479
- * 仅作用于预设根(官方 USER_PRESET_DIR),包内置模板天然不受影响;路径越界与非法 id 拒绝。
480
- * 删除后宿主 agent-presets 目录列表自然不再出现该预设(官方 roster 即目录列表)。 */
481
- function removeUserPreset(id, presetRoot = userPresetsDir()) {
482
- if (typeof id !== "string" || id.length === 0 || id === "." || id === ".." || id.includes("/") || id.includes("\\")) return {
483
- ok: false,
484
- message: `非法预设 id:${id}`
485
- };
486
- const root = presetRoot;
487
- const target = resolve(join(root, id));
488
- const rootResolved = resolve(root);
489
- if (target !== rootResolved && !target.startsWith(rootResolved + sep)) return {
490
- ok: false,
491
- message: `预设路径越界:${id}`
492
- };
493
- if (existsSync(target)) try {
494
- rmSync(target, {
495
- recursive: true,
496
- force: true
497
- });
498
- return { ok: true };
499
- } catch (error) {
500
- return {
501
- ok: false,
502
- message: `删除失败:${error instanceof Error ? error.message : String(error)}`
503
- };
504
- }
505
- return {
506
- ok: false,
507
- message: `预设 ${id} 不存在(预设根 ~/.dsh/.agent-presets)`
508
- };
509
- }
510
- /** 从导入的 preset.yml 文本解析预设 id(非法/缺失回退目录名)。 */
511
- function parseImportedPresetId(presetYaml, fallback) {
512
- try {
513
- const parsed = parse(presetYaml, { logLevel: "silent" });
514
- return typeof parsed?.id === "string" && /^[a-zA-Z0-9\u4e00-\u9fff][a-zA-Z0-9\u4e00-\u9fff-]*$/.test(parsed.id) ? parsed.id : fallback;
515
- } catch {
516
- return fallback;
517
- }
518
- }
519
- /** on/off 等字面开关归一化为布尔。 */
520
- function normalizeParam(value) {
521
- if (value === "on") return true;
522
- if (value === "off") return false;
523
- return value;
524
- }
525
- /** 预设 params(默认参数)与运行时 settings 合并;settings 值优先。 */
526
- function resolvePresetParams(spec, runtime) {
527
- const params = {};
528
- for (const [key, value] of Object.entries(spec.params ?? {})) params[key] = normalizeParam(value);
529
- for (const [key, value] of Object.entries(runtime)) if (value !== void 0) params[key] = value;
530
- if (typeof params.promptText !== "string" || params.promptText.length === 0) params.promptText = typeof runtime.promptText === "string" ? runtime.promptText : "";
531
- return params;
532
- }
533
- /** 逗号分隔 / YAML flow 数组 / 空格分隔的字符串列表 → 字符串数组。 */
534
- function parseListParam(value) {
535
- if (Array.isArray(value)) return value.map((item) => String(item)).filter((item) => item.length > 0);
536
- if (typeof value !== "string") return [];
537
- const trimmed = value.trim();
538
- if (trimmed.length === 0) return [];
539
- return (trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed).split(",").map((item) => item.trim()).filter((item) => item.length > 0);
540
- }
541
- /**
542
- * 参数桥:params 扁平键 → 引擎模块行 config 对象(取代旧 __TOKEN__ 文本渲染)。
543
- *
544
- * 原则:
545
- * - 未声明的键不合并(composition 行默认 / 引擎默认生效),与旧"空值删行"语义等价;
546
- * - 值类型直达(布尔/数字/数组不再字符串化再解析);
547
- * - 合并优先级:参数桥(UI/基础层)> moduleConfigs(模板/ST 行级直写)> 行默认。
548
- * 模型路由/委派参数统一扁平键(modelProvider/subagentModelProvider/toolFilterAllow/maxDepth 等),
549
- * 与官方 AgentOptions{provider,model} / toolFilter{allow,deny} / maxDepth 对齐。
550
- */
551
- function buildModuleConfigsFromParams(params) {
552
- const out = {};
553
- const merge = (module, cfg) => {
554
- if (Object.keys(cfg).length === 0) return;
555
- out[module] = {
556
- ...out[module],
557
- ...cfg
558
- };
559
- };
560
- const bootstrap = {};
561
- if (typeof params.bootstrapMaxTokens === "number" && params.bootstrapMaxTokens > 0) bootstrap.bootstrapMaxTokens = params.bootstrapMaxTokens;
562
- if (params.bootstrapTools !== void 0) bootstrap.bootstrapTools = parseListParam(params.bootstrapTools);
563
- if (params.promoteGate !== void 0) bootstrap.promoteGate = params.promoteGate === true;
564
- if (params.promoteAfterFirstResponse !== void 0) bootstrap.promoteAfterFirstResponse = params.promoteAfterFirstResponse === true;
565
- if (params.maxPromoteSteps !== void 0 && Number.isSafeInteger(params.maxPromoteSteps)) bootstrap.maxPromoteSteps = params.maxPromoteSteps;
566
- if (params.compactionTools !== void 0) bootstrap.compactionTools = parseListParam(params.compactionTools);
567
- if (params.personaSectionsOnly !== void 0) bootstrap.personaSectionsOnly = params.personaSectionsOnly === true;
568
- if (params.workspaceLine !== void 0) bootstrap.workspaceLine = params.workspaceLine === true;
569
- if (typeof params.phase1FirstCallInstruction === "string" && params.phase1FirstCallInstruction.length > 0) bootstrap.phase1FirstCallInstruction = params.phase1FirstCallInstruction;
570
- if (params.stages !== void 0) bootstrap.stages = params.stages;
571
- if (params.stagePreUnlock !== void 0 && Number.isSafeInteger(params.stagePreUnlock)) bootstrap.stagePreUnlock = params.stagePreUnlock;
572
- if (typeof params.stageAdvanceTool === "string" && params.stageAdvanceTool.length > 0) bootstrap.stageAdvanceTool = params.stageAdvanceTool;
573
- if (typeof params.stageAdvanceDescription === "string" && params.stageAdvanceDescription.length > 0) bootstrap.stageAdvanceDescription = params.stageAdvanceDescription;
574
- if (typeof params.stageSectionTemplate === "string" && params.stageSectionTemplate.length > 0) bootstrap.stageSectionTemplate = params.stageSectionTemplate;
575
- merge("tool-bootstrap", bootstrap);
576
- const gate = {};
577
- if (params.allowKinds !== void 0) gate.allowKinds = parseListParam(params.allowKinds);
578
- if (params.messageSources !== void 0) gate.messageSources = parseListParam(params.messageSources);
579
- if (params.deferredSources !== void 0) gate.deferredSources = parseListParam(params.deferredSources);
580
- if (params.deferredGraceSteps !== void 0 && Number.isSafeInteger(params.deferredGraceSteps)) gate.deferredGraceSteps = params.deferredGraceSteps;
581
- if (params.instructionHint !== void 0) gate.instructionHint = params.instructionHint === true;
582
- merge("context-gate", gate);
583
- const presentation = {};
584
- if (params.usePtcMode !== void 0) presentation.usePtcMode = params.usePtcMode === true;
585
- merge("code-presentation", presentation);
586
- const anchorTurn = {};
587
- if (params.anchorTurn !== void 0) anchorTurn.enabled = params.anchorTurn === true;
588
- if (typeof params.anchorTurnText === "string" && params.anchorTurnText.length > 0) anchorTurn.text = params.anchorTurnText;
589
- merge("anchor-turn", anchorTurn);
590
- const gate2 = {};
591
- if (params.deliberationGate !== void 0) gate2.enabled = params.deliberationGate === true;
592
- if (typeof params.deliberationMinChars === "number" && params.deliberationMinChars > 0) gate2.minChars = params.deliberationMinChars;
593
- if (typeof params.deliberationMaxGatesPerTurn === "number" && params.deliberationMaxGatesPerTurn > 0) gate2.maxGatesPerTurn = params.deliberationMaxGatesPerTurn;
594
- merge("deliberation-gate", gate2);
595
- const drip = {};
596
- if (params.cotDrip !== void 0) drip.enabled = params.cotDrip === true;
597
- if (typeof params.cotDripEvery === "number" && params.cotDripEvery > 0) drip.every = params.cotDripEvery;
598
- if (typeof params.cotDripMaxPerTurn === "number" && params.cotDripMaxPerTurn > 0) drip.maxPerTurn = params.cotDripMaxPerTurn;
599
- merge("cot-drip", drip);
600
- const editor = {};
601
- editor.maxOutputChars = typeof params.strReplaceEditorMaxOutputChars === "number" && Number.isSafeInteger(params.strReplaceEditorMaxOutputChars) && params.strReplaceEditorMaxOutputChars > 0 ? params.strReplaceEditorMaxOutputChars : 16e3;
602
- merge("str-replace-editor", editor);
603
- const filter = {};
604
- const mainAllow = parseListParam(params.toolFilterAllow);
605
- const mainDeny = parseListParam(params.toolFilterDeny);
606
- if (mainAllow.length > 0) filter.allow = mainAllow;
607
- if (mainDeny.length > 0) filter.deny = mainDeny;
608
- if (params.toolFilterSubagents === true) filter.includeSubagents = true;
609
- merge("tool-filter", filter);
610
- const subagent = {};
611
- const provider = asString(params.subagentModelProvider, "");
612
- const model = asString(params.subagentModelName, "");
613
- if (provider.length > 0 && model.length > 0) subagent.agentOptions = {
614
- provider,
615
- model
616
- };
617
- const subAllow = parseListParam(params.toolFilterAllow);
618
- const subDeny = parseListParam(params.toolFilterDeny);
619
- if (subAllow.length > 0 || subDeny.length > 0) subagent.toolFilter = {
620
- ...subAllow.length > 0 ? { allow: subAllow } : {},
621
- ...subDeny.length > 0 ? { deny: subDeny } : {}
622
- };
623
- const rawMaxDepth = params.maxDepth;
624
- if (rawMaxDepth === "provider-managed") subagent.maxDepth = "provider-managed";
625
- else if (Number.isSafeInteger(rawMaxDepth) && rawMaxDepth >= 0) subagent.maxDepth = rawMaxDepth;
626
- if (Object.keys(subagent).length > 0) {
627
- out["tool-subagent"] = subagent;
628
- out["tool-subagent-fork"] = subagent;
629
- }
630
- return out;
631
- }
632
- /** 按参数文件的 modules 清单从引擎模块库装配组合文本。 */
633
- /** 空模块清单兜底骨架(自定义预设空白起点:模块集与 minimal 一致,参数/配置全空)。 */
634
- const FALLBACK_MODULES = [
635
- "official-persistent-shell",
636
- "bootstrap-filesystem",
637
- "context-gate",
638
- "tool-bootstrap",
639
- "code-presentation",
640
- "prompt-config-engine",
641
- "run-code-env",
642
- "custom-bash",
643
- "skill-search"
644
- ];
645
- function assembleModules(spec, library) {
646
- const parts = [];
647
- for (const name of spec.modules ?? []) {
648
- if (typeof name !== "string" || name.length === 0) throw new Error(`preset ${spec.id}: modules must be non-empty strings`);
649
- try {
650
- parts.push(readFileSync(join(library, `${name}.yml`), "utf8"));
651
- } catch (error) {
652
- throw new Error(`preset ${spec.id}: module ${name} not found in engine library: ${String(error.message ?? error)}`);
653
- }
654
- }
655
- return parts.join("\n");
656
- }
657
- /**
658
- * 加载预设声明的组合:
659
- * 1. `modules:` 清单 → 引擎模块库按序装配(最终形态);
660
- * 2. 兼容 `composition:` 内联文本或组合清单名。
661
- */
662
- /**
663
- * 行级 config 合并:仅支持 map 型 config 浅合并;
664
- * 数组型 config(如 delegation 组)按子行 id 嵌套合并;未声明模块原样保留。
665
- * 未声明 configs 时返回原文(零开销);parseDocument 往返保留注释。
666
- * 本函数是参数桥产物落位组合行的唯一机制(含 delegation 组子行嵌套),不是预设覆盖通道。
667
- */
668
- function applyModuleConfigs(raw, configs) {
669
- if (configs === void 0 || Object.keys(configs).length === 0) return raw;
670
- const doc = parseDocument(raw, { logLevel: "silent" });
671
- if (!(doc.contents instanceof YAMLSeq)) return raw;
672
- const rows = doc.contents;
673
- let changed = false;
674
- /** 把 cfg 的键写入一个 config 节点(无 config 时创建)。 */
675
- const mergeInto = (item, cfg) => {
676
- const existingConfig = item.get("config", true);
677
- let configNode;
678
- if (existingConfig instanceof YAMLMap) configNode = existingConfig;
679
- else if (existingConfig === null || existingConfig === void 0) {
680
- configNode = new YAMLMap();
681
- item.items.push(new Pair(new Scalar("config"), configNode));
682
- } else return false;
683
- for (const [key, value] of Object.entries(cfg)) configNode.set(key, value);
684
- return true;
685
- };
686
- /** 从行节点读 id(ParsedNode 值包装兼容)。 */
687
- const rowId = (item) => {
688
- const idNode = item.get("id", true);
689
- return idNode !== null && typeof idNode === "object" && "value" in idNode ? String(idNode.value) : void 0;
690
- };
691
- for (const item of rows.items) {
692
- if (!(item instanceof YAMLMap)) continue;
693
- const id = rowId(item);
694
- const existingConfig = item.get("config", true);
695
- if (existingConfig instanceof YAMLSeq && id !== void 0 && !Object.prototype.hasOwnProperty.call(configs, id)) {
696
- for (const child of existingConfig.items) {
697
- if (!(child instanceof YAMLMap)) continue;
698
- const childId = rowId(child);
699
- if (childId === void 0 || !Object.prototype.hasOwnProperty.call(configs, childId)) continue;
700
- if (mergeInto(child, configs[childId])) changed = true;
701
- }
702
- continue;
703
- }
704
- if (id === void 0 || !Object.prototype.hasOwnProperty.call(configs, id)) continue;
705
- if (mergeInto(item, configs[id])) changed = true;
706
- }
707
- return changed ? doc.toString() : raw;
708
- }
709
- /**
710
- * 加载预设声明的组合(原始 token 文本,未渲染)。
711
- * - `modules:` 清单 → 引擎模块库按序装配;
712
- * - `composition: ./xxx.yml` → 预设模板目录内组合文件(官方预设直用);
713
- * - `composition:` 内联文本或组合清单名。
714
- */
715
- function loadCompositionText(spec, templateDir) {
716
- const library = join(packageEngineDir(), "compositions", "library");
717
- let raw;
718
- const modules = Array.isArray(spec.modules) && spec.modules.length === 0 ? FALLBACK_MODULES : spec.modules;
719
- if (Array.isArray(modules)) raw = assembleModules({
720
- ...spec,
721
- modules
722
- }, library);
723
- else {
724
- const name = typeof spec.composition === "string" ? spec.composition : "";
725
- if (name.includes("\n")) raw = name;
726
- else if (name.startsWith("./")) {
727
- if (templateDir === void 0) throw new Error(`preset ${spec.id}: composition relative path needs a templateDir (${JSON.stringify(name)})`);
728
- try {
729
- raw = readFileSync(join(templateDir, name.slice(2)), "utf8");
730
- } catch (error) {
731
- throw new Error(`preset ${spec.id}: composition file not found (${join(templateDir, name.slice(2))}): ${String(error.message ?? error)}`);
732
- }
733
- } else if (name.length > 0) {
734
- const file = join(dirname(library), `${name}.yml`);
735
- try {
736
- raw = readFileSync(file, "utf8");
737
- } catch (error) {
738
- throw new Error(`preset ${spec.id}: engine composition ${name} not found (${file}): ${String(error.message ?? error)}`);
739
- }
740
- } else if (templateDir !== void 0) try {
741
- raw = readFileSync(join(templateDir, "agent.cordis.yml"), "utf8");
742
- } catch (error) {
743
- throw new Error(`preset ${spec.id}: no modules/composition and no agent.cordis.yml in template dir (${templateDir}): ${String(error.message ?? error)}`);
744
- }
745
- else throw new Error(`preset ${spec.id}: no modules list and no composition declared`);
746
- }
747
- return raw;
748
- }
749
- /**
750
- * 预设组合渲染完整链路:模块装配 → 参数桥(moduleConfigs + params)行级合并。
751
- * 合并优先级:参数桥(params/UI 基础层) > moduleConfigs(模板/ST 行级直写) > 行默认。
752
- * moduleConfigs 仅补充参数桥未覆盖的行级 config(如 ST 导入的 tool-web.fetch),
753
- * 不再锁定覆盖 UI 可管理参数(旧作者锁定语义已移除)。
754
- */
755
- function renderComposition(spec, runtime, templateDir) {
756
- const merged = buildModuleConfigsFromParams(resolvePresetParams(spec, runtime));
757
- for (const [id, cfg] of Object.entries(spec.moduleConfigs ?? {})) merged[id] = {
758
- ...cfg,
759
- ...merged[id]
760
- };
761
- return applyModuleConfigs(loadCompositionText(spec, templateDir), merged);
762
- }
763
- /** 组合文本基础校验(模板无关):无未解析 token,且必须是 YAML 数组。 */
764
- function assertCompositionArray(raw, spec) {
765
- const unresolved = raw.match(/__[A-Za-z0-9_]+__/g);
766
- if (unresolved !== null) throw new Error(`generated agent.cordis.yml has unresolved variables: ${unresolved.join(", ")}`);
767
- const parsed = parse(raw, { logLevel: "silent" });
768
- if (!Array.isArray(parsed)) throw new Error(`generated agent.cordis.yml is not a YAML array (preset ${spec.id})`);
769
- return parsed;
770
- }
771
- //#endregion
772
- //#region src/host/prompt-configs.ts
773
- /**
774
- * 提示词配置数据接口:默认提示词配置构建、通用 YAML 渲染、多源合并与目录加载。
775
- *
776
- * 这是「用户自定义注入内容 + 自定义注入层级位置」的功能层:
777
- * 1. settings.promptConfigs 数组(UI 设置最终消费此接口)
778
- * 2. 生成目录 prompt-configs/(yml/json 提示词配置文件)
779
- * 3. 默认四条提示词配置
780
- * 三者按此优先级合并,同名 id 后者覆盖,新 id 追加在默认提示词配置之后。
781
- * 引擎(engine/prompt-config-engine.mjs)在运行时对生成 yml 做权威校验。
782
- */
783
- /** 文本块缩进 n 个空格(YAML block scalar)。 */
784
- function indentBy(level, text) {
785
- const pad = " ".repeat(level);
786
- return text.split(/\r?\n/).map((line) => line.length === 0 ? "" : pad + line).join("\n");
787
- }
788
- /** YAML 标量渲染:字符串非空用 block scalar,空串 quoted,布尔/数字原样。 */
789
- function yamlScalar(key, level, value) {
790
- const pad = " ".repeat(level);
791
- if (typeof value === "string") return value.length === 0 ? `${pad}${key}: ''` : `${pad}${key}: |-\n${indentBy(level + 2, value)}`;
792
- if (typeof value === "boolean" || typeof value === "number") return `${pad}${key}: ${String(value)}`;
793
- if (value === null || value === void 0) return "";
794
- return `${pad}${key}: ${JSON.stringify(value)}`;
795
- }
796
- /** 嵌套 map 渲染(variables / params / identity)。 */
797
- function yamlMap(level, value) {
798
- const pad = " ".repeat(level);
799
- const lines = [];
800
- for (const [key, item] of Object.entries(value)) {
801
- if (item === null || item === void 0) continue;
802
- if (typeof item === "object" && !Array.isArray(item)) {
803
- lines.push(`${pad}${key}:`);
804
- lines.push(...yamlMap(level + 2, item));
805
- } else {
806
- const line = yamlScalar(key, level, item);
807
- if (line.length > 0) lines.push(line);
808
- }
809
- }
810
- return lines;
811
- }
812
- /** 把任意提示词配置 spec 渲染为独立提示词配置模块 yml(全部字段开放可配置)。 */
813
- function renderPromptConfigYaml(spec) {
814
- const lines = [`id: ${spec.id}`];
815
- if (typeof spec.name === "string" && spec.name.length > 0 && spec.name !== spec.id) lines.push(`name: ${spec.name}`);
816
- if (spec.configKind !== void 0) lines.push(`configKind: ${spec.configKind}`);
817
- if (spec.layer !== void 0) lines.push(`layer: ${spec.layer}`);
818
- if (spec.order !== void 0) lines.push(`order: ${spec.order}`);
819
- if (spec.role !== void 0) lines.push(`role: ${spec.role}`);
820
- lines.push(`enabled: ${spec.enabled !== false}`);
821
- lines.push(`strategy: ${spec.strategy ?? "static"}`);
822
- if (spec.position !== void 0) lines.push(`position: ${spec.position}`);
823
- if (spec.dedupe !== void 0) lines.push(`dedupe: ${spec.dedupe}`);
824
- if (spec.promotion !== void 0) lines.push(`promotion: ${spec.promotion}`);
825
- if (spec.audience !== void 0 && spec.audience !== null) lines.push(`audience: ${spec.audience}`);
826
- if (spec.modelScope !== void 0) lines.push(`modelScope: ${spec.modelScope}`);
827
- if (typeof spec.group === "string" && spec.group.length > 0) lines.push(`group: ${spec.group}`);
828
- if (spec.exclusive === true) lines.push("exclusive: true");
829
- if (typeof spec.sourceKind === "string" && spec.sourceKind.length > 0 && spec.sourceKind !== spec.id) lines.push(`sourceKind: ${spec.sourceKind}`);
830
- if (typeof spec.form === "string" && spec.form.length > 0 && spec.form !== "notice") lines.push(`form: ${spec.form}`);
831
- if (typeof spec.summary === "string" && spec.summary.length > 0) lines.push(yamlScalar("summary", 0, spec.summary));
832
- if (typeof spec.templateFile === "string" && spec.templateFile.length > 0) lines.push(`templateFile: ${spec.templateFile}`);
833
- if (typeof spec.fill === "string" && spec.fill.length > 0) lines.push(`fill: ${spec.fill}`);
834
- const texts = [...typeof spec.text === "string" && spec.text.length > 0 ? [spec.text] : [], ...Array.isArray(spec.texts) ? spec.texts : []];
835
- if (texts.length > 0) lines.push(`texts: ${JSON.stringify(texts)}`);
836
- if (spec.mergeMode !== void 0 && spec.mergeMode !== "separate") lines.push(`mergeMode: ${spec.mergeMode}`);
837
- if (spec.identity !== void 0 && spec.identity.value !== spec.id) lines.push("identity:", ` field: ${spec.identity.field}`, ` value: ${spec.identity.value}`);
838
- if (spec.variables !== void 0 && Object.keys(spec.variables).length > 0) {
839
- lines.push("variables:");
840
- lines.push(...yamlMap(2, spec.variables));
841
- }
842
- if (spec.params !== void 0 && Object.keys(spec.params).length > 0) {
843
- lines.push("params:");
844
- lines.push(...yamlMap(2, spec.params));
845
- }
846
- return lines.join("\n").trimEnd();
847
- }
848
- /** 多源提示词配置合并:同名 id 后者覆盖(保留默认提示词配置位置),新 id 追加在末尾。 */
849
- function mergePromptConfigs(...sources) {
850
- const ordered = [];
851
- const byId = /* @__PURE__ */ new Map();
852
- for (const source of sources) for (const spec of source ?? []) {
853
- if (spec === null || typeof spec !== "object" || typeof spec.id !== "string" || spec.id.length === 0) throw new TypeError("every prompt config must have a non-empty string id");
854
- const existing = byId.get(spec.id);
855
- if (existing === void 0) {
856
- byId.set(spec.id, ordered.length);
857
- ordered.push(spec);
858
- } else ordered[existing] = spec;
859
- }
860
- return ordered;
861
- }
862
- /** 从用户提示词配置目录加载 yml/json 提示词配置(文件名排序;内容必须能解析)。 */
863
- function loadPromptConfigFiles(dir) {
864
- if (dir.length === 0) return [];
865
- let entries;
866
- try {
867
- entries = readdirSync(dir, { withFileTypes: true });
868
- } catch (error) {
869
- throw new Error(`提示词配置目录 ${JSON.stringify(dir)} 不可读: ${String(error.message ?? error)}`);
870
- }
871
- const specs = [];
872
- const files = entries.filter((entry) => entry.isFile() && /\.(ya?ml|json)$/i.test(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
873
- for (const entry of files) {
874
- if (entry.name === "variables.yml") continue;
875
- const raw = readFileSync(join(dir, entry.name), "utf8");
876
- const parsed = /\.json$/i.test(entry.name) ? JSON.parse(raw) : parse(raw, { logLevel: "silent" });
877
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`prompt config file ${entry.name} must contain a single config object`);
878
- if (typeof parsed.id !== "string") throw new Error(`prompt config file ${entry.name} must declare a string id`);
879
- specs.push(parsed);
880
- }
881
- return specs;
882
- }
883
- //#endregion
884
- export { DEFAULT_SKILLS_DIR as A, resolvePresetParams as C, writePluginState as D, withPresetDoc as E, LEGACY_USER_PRESETS_DIR as M, DEFAULT_PRESET_DIR as O, resolvePresetDir as S, userPresetsDir as T, packageEngineDir as _, applyModuleConfigs as a, removeUserPreset as b, buildModuleConfigsFromParams as c, ensurePresetSeed as d, listBuiltinTemplates as f, openPresetLocation as g, loadPresetSpec as h, MODEL_SEGMENT_MAP as i, LEGACY_CONTAINER_DIR as j, DEFAULT_RESIDENT_AGENTS_PATH as k, cloneBuiltinPreset as l, loadPresetContent as m, mergePromptConfigs as n, asString as o, listPresets as p, renderPromptConfigYaml as r, assertCompositionArray as s, loadPromptConfigFiles as t, duplicateUserPreset as u, parseImportedPresetId as v, savePresetParams as w, renderComposition as x, readPluginState as y };