dsh-plugin-prompt-tool 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -12
- package/engine/anchor-match.mjs +117 -0
- package/engine/compositions/library/context-gate.yml +2 -2
- package/engine/compositions/library/delegation.yml +2 -2
- package/engine/compositions/library/str-replace-editor.yml +1 -1
- package/engine/compositions/library/tool-bootstrap.yml +2 -2
- package/engine/compositions/library/tool-filter.yml +3 -3
- package/engine/compositions/source/local/context-gate.yml +2 -2
- package/engine/compositions/source/local/tool-bootstrap.yml +3 -3
- package/engine/executor.mjs +9 -2
- package/engine/interpolate.mjs +66 -0
- package/engine/layers.mjs +26 -22
- package/engine/schema.mjs +18 -0
- package/engine/session-vars.mjs +52 -0
- package/engine/shared.mjs +0 -19
- package/engine/strategies.mjs +28 -38
- package/lib/client.js +471 -162
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +35 -9
- package/lib/index.mjs +376 -92
- package/lib/preset-core.d.mts +2 -2
- package/lib/preset-core.mjs +3 -6
- package/lib/{prompt-configs-BcP6miu9.d.mts → prompt-configs-B4vH09wx.d.mts} +1 -3
- package/lib/{prompt-configs-k7wuZs0C.mjs → prompt-configs-ThS4iXPg.mjs} +85 -33
- package/package.json +105 -104
- package/preset/anchored/preset.yml +40 -42
- package/preset/creative/preset.yml +33 -30
- package/preset/creative/skills/cordis-plugin-development/SKILL.md +420 -0
- package/preset/creative/skills/editing-cordis-compositions/SKILL.md +165 -0
- package/preset/liangshen/preset.yml +24 -18
- package/preset/minimal/preset.yml +27 -24
- package/preset/ptc/preset.yml +23 -17
- package/preset/standard/preset.yml +23 -17
- package/templates/20-system-section.yml +5 -0
- package/engine/compositions/library/router-first-turn.yml +0 -9
- package/engine/compositions/source/local/router-first-turn.yml +0 -5
- package/engine/router-first-turn.mjs +0 -85
package/lib/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as userPresetsDir, D as DEFAULT_SKILLS_DIR, E as DEFAULT_RESIDENT_AGENTS_PATH, O as LEGACY_CONTAINER_DIR, S as savePresetParams, T as DEFAULT_PRESET_DIR, _ as readPluginState, a as asString, b as resolvePresetDir, c as duplicateUserPreset, d as listPresets, f as loadPresetContent, g as parseImportedPresetId, h as packageEngineDir, i as applyModuleConfigs, k as LEGACY_USER_PRESETS_DIR, l as ensurePresetSeed, m as openPresetLocation, n as mergePromptConfigs, o as assertCompositionArray, p as loadPresetSpec, r as renderPromptConfigYaml, s as cloneBuiltinPreset, t as loadPromptConfigFiles, u as listBuiltinTemplates, v as removeUserPreset, w as writePluginState, x as resolvePresetParams, y as renderComposition } from "./prompt-configs-ThS4iXPg.mjs";
|
|
2
2
|
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
3
|
import { appendFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
|
|
4
4
|
import { parse, parseDocument, stringify } from "yaml";
|
|
5
5
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import z from "@deepseek-ai/schemastery";
|
|
8
|
-
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
9
9
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
10
10
|
//#region src/runtime/skills-watcher.ts
|
|
11
11
|
/** 技能目录文件 watcher(多目录):任一目录变化防抖 300ms 后触发 onRefresh;单目录不可 watch 时跳过。 */
|
|
@@ -551,13 +551,16 @@ function installSubagentModelRoute(ctx, isEnabled, provider, model) {
|
|
|
551
551
|
});
|
|
552
552
|
}
|
|
553
553
|
//#endregion
|
|
554
|
-
//#region src/
|
|
555
|
-
/** 插件配置、settings 数据模型与默认常量(settings 接口层)。 */
|
|
556
|
-
const NS = settingsNamespace("prompt-tool");
|
|
554
|
+
//#region src/shared/param-keys.ts
|
|
557
555
|
/**
|
|
558
556
|
* 引擎行为参数键(按预设存储:激活预设 preset.yml 的 params + promptConfigs)。
|
|
559
557
|
* 不进 Config schema、不进 settings namespace——每预设一份,随预设走(官方范式:
|
|
560
558
|
* Config = 部署轴,引擎行为在预设文件)。
|
|
559
|
+
*
|
|
560
|
+
* 这些键在 UI 有专门编辑入口(模型设置 / 工具与深度 / 开关),writePreset 把
|
|
561
|
+
* 预设级 params 合并进每条 promptConfig 的 params 时排除——「params(高级参数
|
|
562
|
+
* JSON)」框只保留内容变量与配置自身策略参数,避免 UI 已管理的参数冗余回写。
|
|
563
|
+
* 放 shared:host(write-preset)与 config/settings-bridge 共用,单一来源。
|
|
561
564
|
*/
|
|
562
565
|
const PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
563
566
|
"firstTurnAnchor",
|
|
@@ -575,7 +578,6 @@ const PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
|
575
578
|
"subagentReasoningEffort",
|
|
576
579
|
"subagentTemperature",
|
|
577
580
|
"subagentMaxTokens",
|
|
578
|
-
"mainPersona",
|
|
579
581
|
"subagentPersona",
|
|
580
582
|
"toolFilterAllow",
|
|
581
583
|
"toolFilterDeny",
|
|
@@ -587,6 +589,10 @@ const PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
|
587
589
|
"injectPrompt",
|
|
588
590
|
"promptConfigs"
|
|
589
591
|
]);
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region src/config.ts
|
|
594
|
+
/** 插件配置、settings 数据模型与默认常量(settings 接口层)。 */
|
|
595
|
+
const NS = settingsNamespace("prompt-tool");
|
|
590
596
|
const Config = z.object({
|
|
591
597
|
injectAgentsPrompt: z.boolean().default(false),
|
|
592
598
|
writeAgents: z.boolean().default(true),
|
|
@@ -845,26 +851,42 @@ function fixSkillEntry(skillsDir, folder) {
|
|
|
845
851
|
}
|
|
846
852
|
//#endregion
|
|
847
853
|
//#region src/host/sillytavern.ts
|
|
854
|
+
/**
|
|
855
|
+
* SillyTavern 预设 → 本项目单文件预设转换引擎(纯函数,无文件 IO)。
|
|
856
|
+
*
|
|
857
|
+
* 按需转换原则:只按注入层级映射 SillyTavern 实际内容,不注入本项目默认内容。
|
|
858
|
+
* - prompts[] → promptConfigs:system_prompt+role=system → system-section(可拼接);
|
|
859
|
+
* 其余 → pre-step(role/position/dedupe 按 ST 语义);
|
|
860
|
+
* - 角色卡正文(chara_card_v3:data 内层;旧版顶层直存)→ promptConfigs:
|
|
861
|
+
* description/personality/scenario 拼接为「角色设定」system-section,
|
|
862
|
+
* system_prompt / post_history_instructions → system-section,
|
|
863
|
+
* first_mes → pre-step 开场白(dedupe=session 每会话一次);
|
|
864
|
+
* - 采样参数(temperature/openai_max_tokens/reasoning_effort)**不转译**:
|
|
865
|
+
* 模型参数由「模型设置」UI 统一管理(预设级 params.model*,writePreset 渲染
|
|
866
|
+
* agent-request patch),ST 卡固化值会覆盖用户在模型设置里的设置,故剥离;
|
|
867
|
+
* - modules 按需组装:prompt-config-engine 始终,system-section 注入需要
|
|
868
|
+
* persona(complete: false 允许 system-section 生效)。
|
|
869
|
+
*/
|
|
848
870
|
/** ST 运行时指令(渲染时执行、不发送给模型):setvar/getvar/ERA/trim/注释 → 剥离。 */
|
|
849
871
|
const ST_DIRECTIVE = /\{\{(setvar|getvar|ERA|trim|\/\/)[^}]*\}\}/gi;
|
|
850
872
|
/**
|
|
851
873
|
* 处理 SillyTavern 文本,ST 变量语义 → 本项目 params fallback 插值:
|
|
852
874
|
* {{setvar::k::v}} → 收集 k=v 进 params(会话变量初始值 = fallback 基准),指令剥离;
|
|
853
875
|
* {{getvar::k}} → 改写为 {{k}}(引擎按 params 插值,无值保留原样);
|
|
854
|
-
* {{getvar::k::default}} → 改写为 {{k}} 且
|
|
876
|
+
* {{getvar::k::default}} → 改写为 {{k}} 且 variables 缺 k 时写入 default(fallback 落顶层 variables);
|
|
855
877
|
* {{trim}}/{{//注释}}/{{ERA:...}} → 剥离(格式化/注释/第三方运行时);
|
|
856
878
|
* {{user}}/{{char}} → 替换占位符。
|
|
857
879
|
*/
|
|
858
|
-
function processStText(text, cardName,
|
|
859
|
-
let cleaned = text.replace(/\{\{
|
|
860
|
-
|
|
861
|
-
return `{{${key}}}`;
|
|
862
|
-
});
|
|
863
|
-
cleaned = cleaned.replace(/\{\{getvar::([A-Za-z0-9_.\u4e00-\u9fff-]+)\}\}/g, (_whole, key) => `{{${key}}}`);
|
|
864
|
-
cleaned = cleaned.replace(/\{\{setvar::([A-Za-z0-9_.\u4e00-\u9fff-]+)::([^}]*)\}\}/g, (_whole, key, value) => {
|
|
865
|
-
params[key] = value;
|
|
880
|
+
function processStText(text, cardName, variables) {
|
|
881
|
+
let cleaned = text.replace(/\{\{setvar::([A-Za-z0-9_.\u4e00-\u9fff-]+)::([^}]*)\}\}/g, (_whole, key, value) => {
|
|
882
|
+
variables[key] = value;
|
|
866
883
|
return "";
|
|
867
884
|
});
|
|
885
|
+
cleaned = cleaned.replace(/\{\{getvar::([A-Za-z0-9_.\u4e00-\u9fff-]+)::([^}]*)\}\}/g, (_whole, key, fallback) => {
|
|
886
|
+
if (!Object.prototype.hasOwnProperty.call(variables, key) && fallback.length > 0) variables[key] = fallback;
|
|
887
|
+
return `{{${key}}}`;
|
|
888
|
+
});
|
|
889
|
+
cleaned = cleaned.replace(/\{\{getvar::([A-Za-z0-9_.\u4e00-\u9fff-]+)\}\}/g, (_whole, key) => Object.prototype.hasOwnProperty.call(variables, key) ? `{{${key}}}` : "");
|
|
868
890
|
cleaned = cleaned.replace(ST_DIRECTIVE, "");
|
|
869
891
|
cleaned = cleaned.replace(/\{\{char\}\}/gi, cardName.trim().length > 0 ? cardName.trim() : "角色").replace(/\{\{user\}\}/gi, "用户").replace(/\n{3,}/g, "\n\n").trim();
|
|
870
892
|
return cleaned;
|
|
@@ -877,6 +899,7 @@ function mergeStPresets(specs) {
|
|
|
877
899
|
if (config === null || typeof config !== "object" || Array.isArray(config)) continue;
|
|
878
900
|
const entry = config;
|
|
879
901
|
const base = String(entry.id ?? "");
|
|
902
|
+
if (base.length === 0) continue;
|
|
880
903
|
let id = base;
|
|
881
904
|
for (let suffix = 2; seen.has(id); suffix++) id = `${base}-${suffix}`;
|
|
882
905
|
seen.add(id);
|
|
@@ -933,8 +956,8 @@ function convertStToPreset(card, baseName) {
|
|
|
933
956
|
}
|
|
934
957
|
const bodyText = (key) => typeof body[key] === "string" ? body[key].trim() : "";
|
|
935
958
|
const cardName = typeof record.name === "string" && record.name.trim().length > 0 ? record.name.trim() : typeof body.name === "string" && body.name.trim().length > 0 ? body.name.trim() : "";
|
|
936
|
-
const
|
|
937
|
-
const clean = (text) => processStText(text, cardName,
|
|
959
|
+
const variables = {};
|
|
960
|
+
const clean = (text) => processStText(text, cardName, variables);
|
|
938
961
|
const characterDefinitionClean = clean([
|
|
939
962
|
bodyText("description"),
|
|
940
963
|
bodyText("personality"),
|
|
@@ -979,32 +1002,40 @@ function convertStToPreset(card, baseName) {
|
|
|
979
1002
|
systemSectionCount += 1;
|
|
980
1003
|
}
|
|
981
1004
|
const book = body.character_book;
|
|
982
|
-
if (book !== null && typeof book === "object" && !Array.isArray(book)
|
|
983
|
-
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1005
|
+
if (book !== null && typeof book === "object" && !Array.isArray(book)) {
|
|
1006
|
+
const rawEntries = book.entries;
|
|
1007
|
+
const entryList = Array.isArray(rawEntries) ? rawEntries : rawEntries !== null && typeof rawEntries === "object" ? Object.values(rawEntries).filter((e) => e !== null && typeof e === "object" && !Array.isArray(e)) : [];
|
|
1008
|
+
for (const [index, entry] of entryList.entries()) {
|
|
1009
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
1010
|
+
const content = clean(typeof entry.content === "string" ? entry.content : "");
|
|
1011
|
+
if (content.length === 0) continue;
|
|
1012
|
+
const comment = typeof entry.comment === "string" && entry.comment.trim().length > 0 ? entry.comment.trim() : `世界书 ${String(entry.id ?? entry.uid ?? index)}`;
|
|
1013
|
+
const rawKeys = entry.keys ?? entry.key;
|
|
1014
|
+
const keys = Array.isArray(rawKeys) ? rawKeys.map(String).filter((key) => key.trim().length > 0) : [];
|
|
1015
|
+
const rawSecondary = entry.secondary_keys ?? entry.keysecondary;
|
|
1016
|
+
const secondaryKeys = Array.isArray(rawSecondary) ? rawSecondary.map(String).filter((key) => key.trim().length > 0) : [];
|
|
1017
|
+
const constant = entry.constant === true || entry.add_always === true;
|
|
1018
|
+
const enabled = entry.disable !== void 0 ? entry.disable !== true : entry.enabled !== false;
|
|
1019
|
+
configs.push({
|
|
1020
|
+
id: `lore-${String(entry.id ?? entry.uid ?? index)}`,
|
|
1021
|
+
name: comment,
|
|
1022
|
+
enabled,
|
|
1023
|
+
strategy: "world-book",
|
|
1024
|
+
order: typeof entry.insertion_order === "number" ? entry.insertion_order : typeof entry.order === "number" ? entry.order : 100,
|
|
1025
|
+
text: content,
|
|
1026
|
+
layer: "pre-step",
|
|
1027
|
+
position: "before-all",
|
|
1028
|
+
params: {
|
|
1029
|
+
constant,
|
|
1030
|
+
...keys.length > 0 ? { keys } : {},
|
|
1031
|
+
...secondaryKeys.length > 0 ? { secondaryKeys } : {},
|
|
1032
|
+
...(entry.case_sensitive ?? entry.caseSensitive) === true ? { caseSensitive: true } : {},
|
|
1033
|
+
...(entry.match_whole_words ?? entry.matchWholeWords) === true ? { wholeWords: true } : {},
|
|
1034
|
+
...(entry.use_regex ?? entry.useRegex) === true ? { useRegex: true } : {},
|
|
1035
|
+
...typeof entry.selectiveLogic === "number" ? { selectiveLogic: entry.selectiveLogic } : typeof entry.selective_logic === "number" ? { selectiveLogic: entry.selective_logic } : {}
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1008
1039
|
}
|
|
1009
1040
|
const firstMes = clean(bodyText("first_mes"));
|
|
1010
1041
|
if (firstMes.length > 0) configs.push({
|
|
@@ -1042,9 +1073,9 @@ function convertStToPreset(card, baseName) {
|
|
|
1042
1073
|
const base = {
|
|
1043
1074
|
id,
|
|
1044
1075
|
name: typeof prompt.name === "string" && prompt.name.length > 0 ? prompt.name : id,
|
|
1045
|
-
enabled: orderDisabled.has(
|
|
1076
|
+
enabled: orderDisabled.has(rawId) ? false : prompt.enabled !== false,
|
|
1046
1077
|
strategy: "static",
|
|
1047
|
-
order: (orderIndex.get(
|
|
1078
|
+
order: ((rawId.length > 0 ? orderIndex.get(rawId) : void 0) ?? index) * 10,
|
|
1048
1079
|
text: content
|
|
1049
1080
|
};
|
|
1050
1081
|
if (role === "system") {
|
|
@@ -1063,9 +1094,6 @@ function convertStToPreset(card, baseName) {
|
|
|
1063
1094
|
dedupe: "none"
|
|
1064
1095
|
});
|
|
1065
1096
|
}
|
|
1066
|
-
if (typeof record.temperature === "number") params.modelTemperature = String(record.temperature);
|
|
1067
|
-
if (typeof record.openai_max_tokens === "number" && record.openai_max_tokens > 0) params.modelMaxTokens = String(record.openai_max_tokens);
|
|
1068
|
-
if (typeof record.reasoning_effort === "string" && record.reasoning_effort.trim().length > 0) params.modelReasoningEffort = record.reasoning_effort.trim();
|
|
1069
1097
|
const modules = ["prompt-config-engine"];
|
|
1070
1098
|
const moduleConfigs = {};
|
|
1071
1099
|
if (systemSectionCount > 0) {
|
|
@@ -1082,18 +1110,57 @@ function convertStToPreset(card, baseName) {
|
|
|
1082
1110
|
deny: ["web_search", "web_fetch"]
|
|
1083
1111
|
};
|
|
1084
1112
|
}
|
|
1113
|
+
const presetId = stPresetId(baseName);
|
|
1114
|
+
const RUNTIME_MACROS = /* @__PURE__ */ new Set([
|
|
1115
|
+
"lastusermessage",
|
|
1116
|
+
"lastcharmessage",
|
|
1117
|
+
"charifnotgroup"
|
|
1118
|
+
]);
|
|
1119
|
+
const BUILTIN_KEYS = /* @__PURE__ */ new Set([
|
|
1120
|
+
"DSH_HOME",
|
|
1121
|
+
"WORKSPACE",
|
|
1122
|
+
"CWD"
|
|
1123
|
+
]);
|
|
1124
|
+
const MACRO_RE = /\{\{([A-Za-z0-9_.\u4e00-\u9fff-]+)\}\}/g;
|
|
1125
|
+
const knownKeys = new Set(Object.keys(variables).map((key) => key.toLowerCase()));
|
|
1126
|
+
for (const config of configs) {
|
|
1127
|
+
const configRecord = config;
|
|
1128
|
+
const texts = [
|
|
1129
|
+
...typeof config.text === "string" && config.text.length > 0 ? [config.text] : [],
|
|
1130
|
+
...Array.isArray(config.texts) ? config.texts : [],
|
|
1131
|
+
...typeof configRecord.params?.text === "string" ? [configRecord.params.text] : []
|
|
1132
|
+
];
|
|
1133
|
+
for (const raw of texts) {
|
|
1134
|
+
const text = String(raw);
|
|
1135
|
+
MACRO_RE.lastIndex = 0;
|
|
1136
|
+
for (const match of text.matchAll(MACRO_RE)) {
|
|
1137
|
+
const key = match[1];
|
|
1138
|
+
const lower = key.toLowerCase();
|
|
1139
|
+
if (knownKeys.has(lower) || RUNTIME_MACROS.has(lower) || BUILTIN_KEYS.has(key)) continue;
|
|
1140
|
+
knownKeys.add(lower);
|
|
1141
|
+
variables[key] = "";
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1085
1145
|
return {
|
|
1086
|
-
id:
|
|
1146
|
+
id: presetId,
|
|
1087
1147
|
name: `${cardName || baseName}(SillyTavern 转换)`,
|
|
1088
1148
|
version: "1.0.0",
|
|
1089
1149
|
engineCompat: ">=0.4.2",
|
|
1090
1150
|
meta: { source: "sillytavern" },
|
|
1091
|
-
...Object.keys(
|
|
1151
|
+
...Object.keys(variables).length > 0 ? { variables } : {},
|
|
1092
1152
|
modules,
|
|
1093
1153
|
moduleConfigs,
|
|
1094
1154
|
promptConfigs: configs
|
|
1095
1155
|
};
|
|
1096
1156
|
}
|
|
1157
|
+
/** ST 导入预设 id:必须是官方 agent-presets 可发现的目录名(PRESET_ID =
|
|
1158
|
+
* /^[a-z0-9][a-z0-9-]*$/——含中文的目录会被宿主 discovery 静默跳过,会话
|
|
1159
|
+
* resume 报 preset not found)。文件名 slug 化(去中文);纯中文名退化为
|
|
1160
|
+
* st-<文件名短哈希>(唯一且合法);显示名 name 仍保留中文原名。 */
|
|
1161
|
+
function stPresetId(baseName) {
|
|
1162
|
+
return baseName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || `st-${createHash("sha1").update(baseName).digest("hex").slice(0, 6)}`;
|
|
1163
|
+
}
|
|
1097
1164
|
//#endregion
|
|
1098
1165
|
//#region src/host/characters.ts
|
|
1099
1166
|
/** 角色卡库:独立于预设根的素材+参数存储(预设根下点前缀目录,官方 discovery 跳过)。
|
|
@@ -1326,7 +1393,7 @@ function removeCharacterFromPreset(presetRoot, templateName, cardId) {
|
|
|
1326
1393
|
return !isCard;
|
|
1327
1394
|
});
|
|
1328
1395
|
doc.setIn(["promptConfigs"], kept);
|
|
1329
|
-
for (const key of Object.
|
|
1396
|
+
for (const [key, value] of Object.entries(spec?.params ?? {})) if (doc.getIn(["params", key]) === value) doc.deleteIn(["params", key]);
|
|
1330
1397
|
const list = Array.isArray(current.meta?.importedCharacters) ? current.meta.importedCharacters : [];
|
|
1331
1398
|
doc.setIn(["meta", "importedCharacters"], list.filter((entry) => String(entry) !== cardId));
|
|
1332
1399
|
});
|
|
@@ -1363,6 +1430,7 @@ const BRIDGE_ENDPOINTS = {
|
|
|
1363
1430
|
presetContent: "/preset-content",
|
|
1364
1431
|
importPreset: "/import-preset",
|
|
1365
1432
|
paramOverrides: "/param-overrides",
|
|
1433
|
+
presetVariables: "/preset-variables",
|
|
1366
1434
|
importPresetPackage: "/import-preset-package",
|
|
1367
1435
|
exportPreset: "/export-preset",
|
|
1368
1436
|
presetDelete: "/preset-delete",
|
|
@@ -1898,6 +1966,64 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1898
1966
|
}
|
|
1899
1967
|
}
|
|
1900
1968
|
}),
|
|
1969
|
+
sctx.webServer.register({
|
|
1970
|
+
kind: "exact",
|
|
1971
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.presetVariables,
|
|
1972
|
+
handler: async (req, res) => {
|
|
1973
|
+
if (!guard(req, res)) return;
|
|
1974
|
+
const dir = getPresetConfigsDir?.() ?? "";
|
|
1975
|
+
if (dir.length === 0) {
|
|
1976
|
+
writeBridgeJson(res, 400, {
|
|
1977
|
+
ok: false,
|
|
1978
|
+
code: "preset-dir-unavailable",
|
|
1979
|
+
message: "presetDir 未配置"
|
|
1980
|
+
});
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
const presetRoot = dirname(dir);
|
|
1984
|
+
const templateName = basename(dir);
|
|
1985
|
+
const { body } = await readBridgeBody(req);
|
|
1986
|
+
const record = body ?? {};
|
|
1987
|
+
if (record.variables === void 0 && record.enabled === void 0) {
|
|
1988
|
+
try {
|
|
1989
|
+
const spec = loadPresetSpec(dir);
|
|
1990
|
+
const variables = {};
|
|
1991
|
+
for (const [key, value] of Object.entries(spec.params ?? {})) if (!PARAM_KEYS.has(key) && typeof value === "string") variables[key] = value;
|
|
1992
|
+
for (const [key, value] of Object.entries(spec.variables ?? {})) if (typeof value === "string") variables[key] = value;
|
|
1993
|
+
writeBridgeJson(res, 200, {
|
|
1994
|
+
ok: true,
|
|
1995
|
+
value: {
|
|
1996
|
+
variables,
|
|
1997
|
+
enabled: spec.variablesEnabled !== false
|
|
1998
|
+
}
|
|
1999
|
+
});
|
|
2000
|
+
} catch {
|
|
2001
|
+
writeBridgeJson(res, 200, {
|
|
2002
|
+
ok: true,
|
|
2003
|
+
value: {
|
|
2004
|
+
variables: {},
|
|
2005
|
+
enabled: true
|
|
2006
|
+
}
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
try {
|
|
2012
|
+
savePresetParams(presetRoot, templateName, void 0, void 0, record.variables !== null && typeof record.variables === "object" && !Array.isArray(record.variables) ? record.variables : void 0, typeof record.enabled === "boolean" ? record.enabled : void 0);
|
|
2013
|
+
afterOverridesChange?.();
|
|
2014
|
+
writeBridgeJson(res, 200, {
|
|
2015
|
+
ok: true,
|
|
2016
|
+
value: { variables: record.variables }
|
|
2017
|
+
});
|
|
2018
|
+
} catch (error) {
|
|
2019
|
+
writeBridgeJson(res, 409, {
|
|
2020
|
+
ok: false,
|
|
2021
|
+
code: "preset-variables-rejected",
|
|
2022
|
+
message: `模板变量保存失败:${error instanceof Error ? error.message : String(error)}`
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}),
|
|
1901
2027
|
sctx.webServer.register({
|
|
1902
2028
|
kind: "exact",
|
|
1903
2029
|
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.importPresetPackage,
|
|
@@ -2270,7 +2396,8 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
2270
2396
|
const path = typeof f.path === "string" && f.path.length > 0 ? f.path : "";
|
|
2271
2397
|
const content = typeof f.content === "string" ? f.content : "";
|
|
2272
2398
|
if (path.length === 0) return [];
|
|
2273
|
-
if (
|
|
2399
|
+
if (/^[a-zA-Z]:/.test(path) || path.startsWith("/") || path.startsWith("\\")) return [];
|
|
2400
|
+
if (path.split(/[\\/]/).some((segment) => segment === "..")) return [];
|
|
2274
2401
|
return [{
|
|
2275
2402
|
path,
|
|
2276
2403
|
content
|
|
@@ -2459,7 +2586,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
2459
2586
|
}
|
|
2460
2587
|
//#endregion
|
|
2461
2588
|
//#region src/runtime/character-tools.ts
|
|
2462
|
-
const text$
|
|
2589
|
+
const text$2 = (text) => [{
|
|
2463
2590
|
type: "text",
|
|
2464
2591
|
text
|
|
2465
2592
|
}];
|
|
@@ -2497,7 +2624,7 @@ function registerCharacterTools(ctx, host) {
|
|
|
2497
2624
|
}
|
|
2498
2625
|
} }
|
|
2499
2626
|
},
|
|
2500
|
-
render: (_args, value) => text$
|
|
2627
|
+
render: (_args, value) => text$2(JSON.stringify(value.characters))
|
|
2501
2628
|
},
|
|
2502
2629
|
execute: async () => ({ characters: listCharacterCards(host.presetRoot(), host.templateName()) })
|
|
2503
2630
|
}));
|
|
@@ -2531,7 +2658,7 @@ function registerCharacterTools(ctx, host) {
|
|
|
2531
2658
|
}
|
|
2532
2659
|
}
|
|
2533
2660
|
},
|
|
2534
|
-
render: (_args, value) => text$
|
|
2661
|
+
render: (_args, value) => text$2(`角色卡已入库:${value.name}(id=${value.id})。调用 character_apply 可应用到当前预设。`)
|
|
2535
2662
|
},
|
|
2536
2663
|
execute: async (args) => {
|
|
2537
2664
|
const result = importCharacterCard(host.presetRoot(), [{
|
|
@@ -2568,7 +2695,7 @@ function registerCharacterTools(ctx, host) {
|
|
|
2568
2695
|
}
|
|
2569
2696
|
}
|
|
2570
2697
|
},
|
|
2571
|
-
render: (_args, value) => text$
|
|
2698
|
+
render: (_args, value) => text$2(`已导入到当前预设(${value.count} 条配置),生成目录已重建。`)
|
|
2572
2699
|
},
|
|
2573
2700
|
execute: async (args) => {
|
|
2574
2701
|
const result = applyCharacterToPreset(host.presetRoot(), host.templateName(), args.id);
|
|
@@ -2603,7 +2730,7 @@ function registerCharacterTools(ctx, host) {
|
|
|
2603
2730
|
}
|
|
2604
2731
|
}
|
|
2605
2732
|
},
|
|
2606
|
-
render: (_args, value) => text$
|
|
2733
|
+
render: (_args, value) => text$2(`已从当前预设移除(${value.count} 条配置),生成目录已重建。`)
|
|
2607
2734
|
},
|
|
2608
2735
|
execute: async (args) => {
|
|
2609
2736
|
const result = removeCharacterFromPreset(host.presetRoot(), host.templateName(), args.id);
|
|
@@ -2632,7 +2759,7 @@ function registerCharacterTools(ctx, host) {
|
|
|
2632
2759
|
required: true
|
|
2633
2760
|
} }
|
|
2634
2761
|
},
|
|
2635
|
-
render: (_args, value) => text$
|
|
2762
|
+
render: (_args, value) => text$2(`角色卡 ${value.id} 已从库中删除。`)
|
|
2636
2763
|
},
|
|
2637
2764
|
execute: async (args) => {
|
|
2638
2765
|
const result = deleteCharacterCard(host.presetRoot(), args.id);
|
|
@@ -2644,7 +2771,7 @@ function registerCharacterTools(ctx, host) {
|
|
|
2644
2771
|
}
|
|
2645
2772
|
//#endregion
|
|
2646
2773
|
//#region src/runtime/world-book-tools.ts
|
|
2647
|
-
const text = (text) => [{
|
|
2774
|
+
const text$1 = (text) => [{
|
|
2648
2775
|
type: "text",
|
|
2649
2776
|
text
|
|
2650
2777
|
}];
|
|
@@ -2723,7 +2850,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2723
2850
|
}
|
|
2724
2851
|
} }
|
|
2725
2852
|
},
|
|
2726
|
-
render: (_args, value) => text(JSON.stringify(value.entries))
|
|
2853
|
+
render: (_args, value) => text$1(JSON.stringify(value.entries))
|
|
2727
2854
|
},
|
|
2728
2855
|
execute: async () => {
|
|
2729
2856
|
return { entries: readConfigs(host.activeDir()).filter((config) => config.strategy === "world-book").map((config) => ({
|
|
@@ -2737,7 +2864,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2737
2864
|
}));
|
|
2738
2865
|
toolsCtx.tools.register(defineTool({
|
|
2739
2866
|
name: "world_book_upsert",
|
|
2740
|
-
description: "新增或更新当前预设的一条世界书条目(world-book 策略配置):按 id 更新(不存在则新增,id 自动生成 lore-<n>)。constant=true 常驻注入;否则命中 keys(或 secondaryKeys)任一关键字注入;无 keys 条目按全局每次注入。note
|
|
2867
|
+
description: "新增或更新当前预设的一条世界书条目(world-book 策略配置):按 id 更新(不存在则新增,id 自动生成 lore-<n>)。constant=true 常驻注入;否则命中 keys(或 secondaryKeys)任一关键字注入;无 keys 条目按全局每次注入。note 可选:写入来源角色卡的持久记忆(memory.md,条目 id 带 chara-<卡>- 前缀时)或预设记忆——持久记忆跨会话跟随角色卡(与 session_var 会话变量的临时状态不同,适合长期关系记录)。写盘后立即重建生成目录。",
|
|
2741
2868
|
parameters: {
|
|
2742
2869
|
id: {
|
|
2743
2870
|
type: "string",
|
|
@@ -2777,7 +2904,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2777
2904
|
},
|
|
2778
2905
|
note: {
|
|
2779
2906
|
type: "string",
|
|
2780
|
-
description: "
|
|
2907
|
+
description: "可选:操作笔记,写入来源角色卡持久记忆(memory.md,跨会话跟随角色卡)或预设记忆。"
|
|
2781
2908
|
}
|
|
2782
2909
|
},
|
|
2783
2910
|
output: {
|
|
@@ -2795,7 +2922,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2795
2922
|
}
|
|
2796
2923
|
}
|
|
2797
2924
|
},
|
|
2798
|
-
render: (_args, value) => text(`世界书条目 ${value.id} 已保存(当前共 ${value.count} 条),生成目录已重建。`)
|
|
2925
|
+
render: (_args, value) => text$1(`世界书条目 ${value.id} 已保存(当前共 ${value.count} 条),生成目录已重建。`)
|
|
2799
2926
|
},
|
|
2800
2927
|
execute: async (args) => {
|
|
2801
2928
|
const dir = host.activeDir();
|
|
@@ -2831,7 +2958,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2831
2958
|
}));
|
|
2832
2959
|
toolsCtx.tools.register(defineTool({
|
|
2833
2960
|
name: "world_book_delete",
|
|
2834
|
-
description: "删除当前预设的一条世界书条目(world_book_list 获取 id)。note
|
|
2961
|
+
description: "删除当前预设的一条世界书条目(world_book_list 获取 id)。note 可选:写入来源角色卡持久记忆(memory.md,跨会话跟随角色卡)或预设记忆。删除后立即重建生成目录。",
|
|
2835
2962
|
parameters: {
|
|
2836
2963
|
id: {
|
|
2837
2964
|
type: "string",
|
|
@@ -2840,7 +2967,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2840
2967
|
},
|
|
2841
2968
|
note: {
|
|
2842
2969
|
type: "string",
|
|
2843
|
-
description: "
|
|
2970
|
+
description: "可选:操作笔记,写入来源角色卡持久记忆(memory.md)或预设记忆。"
|
|
2844
2971
|
}
|
|
2845
2972
|
},
|
|
2846
2973
|
output: {
|
|
@@ -2858,7 +2985,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2858
2985
|
}
|
|
2859
2986
|
}
|
|
2860
2987
|
},
|
|
2861
|
-
render: (_args, value) => text(`世界书条目 ${value.id} 已删除(剩余 ${value.count} 条),生成目录已重建。`)
|
|
2988
|
+
render: (_args, value) => text$1(`世界书条目 ${value.id} 已删除(剩余 ${value.count} 条),生成目录已重建。`)
|
|
2862
2989
|
},
|
|
2863
2990
|
execute: async (args) => {
|
|
2864
2991
|
const dir = host.activeDir();
|
|
@@ -2877,6 +3004,112 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2877
3004
|
});
|
|
2878
3005
|
}
|
|
2879
3006
|
//#endregion
|
|
3007
|
+
//#region src/runtime/session-var-tools.ts
|
|
3008
|
+
const text = (value) => [{
|
|
3009
|
+
type: "text",
|
|
3010
|
+
text: value
|
|
3011
|
+
}];
|
|
3012
|
+
function registerSessionVarTools(ctx) {
|
|
3013
|
+
ctx.inject(["tools"], (toolsCtx) => {
|
|
3014
|
+
toolsCtx.tools.register(defineTool({
|
|
3015
|
+
name: "session_var",
|
|
3016
|
+
description: "会话变量管理(SillyTavern setvar/getvar 语义):list 查看当前会话全部变量、get 读取、set 设置、clear 清除。提示词/世界书文本中的 {{变量名}} 会在注入时替换为会话变量值(会话级覆盖预设默认值);适合维护角色状态(如 {{心情}}、{{接受度}} 等)。注意:会话变量仅存于当前会话(结束即失);跨会话长期记忆请用 world_book 工具的 note 参数写入角色卡记忆(持久,跟随角色卡)。",
|
|
3017
|
+
parameters: {
|
|
3018
|
+
action: {
|
|
3019
|
+
type: "string",
|
|
3020
|
+
enum: [
|
|
3021
|
+
"list",
|
|
3022
|
+
"get",
|
|
3023
|
+
"set",
|
|
3024
|
+
"clear"
|
|
3025
|
+
],
|
|
3026
|
+
required: true,
|
|
3027
|
+
description: "操作:list / get / set / clear"
|
|
3028
|
+
},
|
|
3029
|
+
key: {
|
|
3030
|
+
type: "string",
|
|
3031
|
+
description: "变量名(get / set / clear 时必填;支持中文与下划线)"
|
|
3032
|
+
},
|
|
3033
|
+
value: {
|
|
3034
|
+
type: "string",
|
|
3035
|
+
description: "变量值(set 时必填;空串表示清除该变量)"
|
|
3036
|
+
}
|
|
3037
|
+
},
|
|
3038
|
+
output: {
|
|
3039
|
+
schema: {
|
|
3040
|
+
type: "object",
|
|
3041
|
+
additionalProperties: false,
|
|
3042
|
+
properties: {
|
|
3043
|
+
ok: {
|
|
3044
|
+
type: "boolean",
|
|
3045
|
+
required: true
|
|
3046
|
+
},
|
|
3047
|
+
error: { type: "string" },
|
|
3048
|
+
variables: {
|
|
3049
|
+
type: "object",
|
|
3050
|
+
additionalProperties: true
|
|
3051
|
+
},
|
|
3052
|
+
key: { type: "string" },
|
|
3053
|
+
value: { type: "string" }
|
|
3054
|
+
}
|
|
3055
|
+
},
|
|
3056
|
+
render: (_args, value) => text(JSON.stringify(value))
|
|
3057
|
+
},
|
|
3058
|
+
execute: async (args, exec) => {
|
|
3059
|
+
const session = exec.agent?.session;
|
|
3060
|
+
if (session === void 0) return {
|
|
3061
|
+
ok: false,
|
|
3062
|
+
error: "no active session"
|
|
3063
|
+
};
|
|
3064
|
+
const { sessionVarsSnapshot, getSessionVar, setSessionVar, clearSessionVars } = await import(new URL("../engine/session-vars.mjs", import.meta.url).href);
|
|
3065
|
+
const record = args ?? {};
|
|
3066
|
+
const action = typeof record.action === "string" ? record.action : "";
|
|
3067
|
+
const key = typeof record.key === "string" ? record.key : "";
|
|
3068
|
+
if (action === "list") return {
|
|
3069
|
+
ok: true,
|
|
3070
|
+
variables: sessionVarsSnapshot(session)
|
|
3071
|
+
};
|
|
3072
|
+
if (action === "get") {
|
|
3073
|
+
if (key.length === 0) return {
|
|
3074
|
+
ok: false,
|
|
3075
|
+
error: "get 需要 key"
|
|
3076
|
+
};
|
|
3077
|
+
return {
|
|
3078
|
+
ok: true,
|
|
3079
|
+
key,
|
|
3080
|
+
value: getSessionVar(session, key) ?? ""
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
3083
|
+
if (action === "set") {
|
|
3084
|
+
if (key.length === 0) return {
|
|
3085
|
+
ok: false,
|
|
3086
|
+
error: "set 需要 key"
|
|
3087
|
+
};
|
|
3088
|
+
const value = typeof record.value === "string" ? record.value : "";
|
|
3089
|
+
if (value.length === 0) clearSessionVars(session, key);
|
|
3090
|
+
else setSessionVar(session, key, value);
|
|
3091
|
+
return {
|
|
3092
|
+
ok: true,
|
|
3093
|
+
key,
|
|
3094
|
+
value
|
|
3095
|
+
};
|
|
3096
|
+
}
|
|
3097
|
+
if (action === "clear") {
|
|
3098
|
+
clearSessionVars(session, key.length > 0 ? key : void 0);
|
|
3099
|
+
return {
|
|
3100
|
+
ok: true,
|
|
3101
|
+
key
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
return {
|
|
3105
|
+
ok: false,
|
|
3106
|
+
error: `未知 action: ${action}`
|
|
3107
|
+
};
|
|
3108
|
+
}
|
|
3109
|
+
}));
|
|
3110
|
+
});
|
|
3111
|
+
}
|
|
3112
|
+
//#endregion
|
|
2880
3113
|
//#region src/runtime/tui.ts
|
|
2881
3114
|
/** dsh-tui 全局开关:键名与 settings 路径一致(settings mutate)。 */
|
|
2882
3115
|
const TUI_GLOBAL_SWITCHES = [
|
|
@@ -3292,6 +3525,11 @@ function engineFingerprint() {
|
|
|
3292
3525
|
return parts.sort().join("|");
|
|
3293
3526
|
}
|
|
3294
3527
|
const ENGINE_FINGERPRINT_MARKER = ".pt-engine-fingerprint";
|
|
3528
|
+
/** 剥离文本中的预设级变量引用({{key}} → 空串);内置变量({{DSH_HOME}} 等)保留。
|
|
3529
|
+
* 模板变量插值停用时由 writePreset 调用,避免 {{key}} 残留导致官方渲染 unknown variable。 */
|
|
3530
|
+
function stripVariableRefs(text, keys) {
|
|
3531
|
+
return text.replace(/\{\{([A-Za-z0-9_.\u4e00-\u9fff-]+)\}\}/g, (whole, key) => keys.has(key) ? "" : whole);
|
|
3532
|
+
}
|
|
3295
3533
|
/** Windows 瞬时文件锁(杀软/宿主短读)下的重试:rename/写文件失败最多重试 3 次、间隔 400ms。 */
|
|
3296
3534
|
function withLockRetry(action, retries = 3) {
|
|
3297
3535
|
for (let attempt = 0;; attempt++) try {
|
|
@@ -3326,7 +3564,6 @@ function runtimeOf(options, prompt) {
|
|
|
3326
3564
|
toolFilterAllow: options.toolFilterAllow !== void 0 && (Array.isArray(options.toolFilterAllow) ? options.toolFilterAllow.length > 0 : String(options.toolFilterAllow).trim().length > 0) ? options.toolFilterAllow : void 0,
|
|
3327
3565
|
toolFilterDeny: options.toolFilterDeny !== void 0 && (Array.isArray(options.toolFilterDeny) ? options.toolFilterDeny.length > 0 : String(options.toolFilterDeny).trim().length > 0) ? options.toolFilterDeny : void 0,
|
|
3328
3566
|
maxDepth: options.maxDepth,
|
|
3329
|
-
mainPersona: typeof options.mainPersona === "string" && options.mainPersona.trim().length > 0 ? options.mainPersona : void 0,
|
|
3330
3567
|
allowKinds: options.allowKinds,
|
|
3331
3568
|
firstTurnWord: typeof options.firstTurnWord === "string" && options.firstTurnWord.length > 0 ? options.firstTurnWord : void 0
|
|
3332
3569
|
};
|
|
@@ -3371,7 +3608,7 @@ function modelRequestConfigs(params) {
|
|
|
3371
3608
|
function writePreset(prompt, options) {
|
|
3372
3609
|
const presetDir = options.presetDir.trim().length > 0 ? options.presetDir : DEFAULT_PRESET_DIR;
|
|
3373
3610
|
const templateName = typeof options.presetTemplate === "string" && options.presetTemplate.trim().length > 0 ? options.presetTemplate.trim() : "anchored";
|
|
3374
|
-
if (!/^[a-
|
|
3611
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(templateName)) throw new Error(`invalid presetTemplate ${JSON.stringify(templateName)}: must match official agent-presets id /^[a-z0-9][a-z0-9-]*$/`);
|
|
3375
3612
|
const templateDir = resolvePresetDir(templateName);
|
|
3376
3613
|
const spec = loadPresetSpec(templateDir);
|
|
3377
3614
|
const worldBook = spec.worldBook;
|
|
@@ -3529,11 +3766,17 @@ function writePreset(prompt, options) {
|
|
|
3529
3766
|
});
|
|
3530
3767
|
else templateDefaults = [];
|
|
3531
3768
|
const merged = mergePromptConfigs(modelRequestConfigs(params), templateDefaults, options.promptConfigs);
|
|
3769
|
+
const presetVariables = {};
|
|
3770
|
+
for (const [key, value] of Object.entries(spec.params ?? {})) {
|
|
3771
|
+
if (PARAM_KEYS.has(key)) continue;
|
|
3772
|
+
const text = String(value);
|
|
3773
|
+
if (text.length > 0) presetVariables[key] = text;
|
|
3774
|
+
}
|
|
3775
|
+
for (const [key, value] of Object.entries(spec.variables ?? {})) if (typeof value === "string" && value.length > 0) presetVariables[key] = value;
|
|
3776
|
+
const variablesEnabled = spec.variablesEnabled !== false;
|
|
3777
|
+
const presetVariableKeys = new Set(Object.keys(presetVariables));
|
|
3778
|
+
if (variablesEnabled && presetVariableKeys.size > 0) writeFileSync(join(promptConfigsDir, "variables.yml"), stringify(presetVariables), "utf8");
|
|
3532
3779
|
for (const [index, config] of merged.entries()) {
|
|
3533
|
-
config.params = {
|
|
3534
|
-
...params,
|
|
3535
|
-
...config.params
|
|
3536
|
-
};
|
|
3537
3780
|
if (config.id === "prompt-injector") {
|
|
3538
3781
|
delete config.text;
|
|
3539
3782
|
config.texts = [];
|
|
@@ -3549,6 +3792,15 @@ function writePreset(prompt, options) {
|
|
|
3549
3792
|
agentsInstructionPath: `../${templateName}/agents-instruction.md`,
|
|
3550
3793
|
...options.injectAgentsPrompt === true ? { text: asString(options.agentsInstructionText) } : {}
|
|
3551
3794
|
};
|
|
3795
|
+
if (!variablesEnabled && presetVariableKeys.size > 0) {
|
|
3796
|
+
const strip = (item) => stripVariableRefs(item, presetVariableKeys);
|
|
3797
|
+
config.texts = (config.texts ?? []).map(strip);
|
|
3798
|
+
if (typeof config.text === "string") config.text = strip(config.text);
|
|
3799
|
+
if (typeof config.params?.text === "string") config.params = {
|
|
3800
|
+
...config.params,
|
|
3801
|
+
text: strip(config.params.text)
|
|
3802
|
+
};
|
|
3803
|
+
}
|
|
3552
3804
|
writeFileSync(join(promptConfigsDir, `${String(index * 10).padStart(2, "0")}-${config.id}.yml`), renderPromptConfigYaml(config), "utf8");
|
|
3553
3805
|
}
|
|
3554
3806
|
for (const legacy of spec.legacyCleanup ?? []) rmSync(join(outDir, legacy), { force: true });
|
|
@@ -3733,7 +3985,6 @@ function apply(ctx, configIn) {
|
|
|
3733
3985
|
runtime.bootstrapMaxTokens = Number.isSafeInteger(params.bootstrapMaxTokens) && params.bootstrapMaxTokens >= 0 ? params.bootstrapMaxTokens : 0;
|
|
3734
3986
|
runtime.usePtcMode = params.usePtcMode !== false;
|
|
3735
3987
|
runtime.injectPrompt = params.injectPrompt !== false;
|
|
3736
|
-
runtime.mainPersona = asString(params.mainPersona) || void 0;
|
|
3737
3988
|
runtime.subagentPersona = asString(params.subagentPersona) || void 0;
|
|
3738
3989
|
runtime.toolFilterAllow = params.toolFilterAllow;
|
|
3739
3990
|
runtime.toolFilterDeny = params.toolFilterDeny;
|
|
@@ -3765,7 +4016,6 @@ function apply(ctx, configIn) {
|
|
|
3765
4016
|
subagentReasoningEffort: runtime.subagentReasoningEffort,
|
|
3766
4017
|
subagentTemperature: runtime.subagentTemperature,
|
|
3767
4018
|
subagentMaxTokens: runtime.subagentMaxTokens,
|
|
3768
|
-
mainPersona: runtime.mainPersona,
|
|
3769
4019
|
subagentPersona: runtime.subagentPersona,
|
|
3770
4020
|
toolFilterAllow: runtime.toolFilterAllow,
|
|
3771
4021
|
toolFilterDeny: runtime.toolFilterDeny,
|
|
@@ -3797,13 +4047,26 @@ function apply(ctx, configIn) {
|
|
|
3797
4047
|
}
|
|
3798
4048
|
}
|
|
3799
4049
|
writePreset(presetPrompt, options);
|
|
3800
|
-
} else
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
4050
|
+
} else {
|
|
4051
|
+
let cleaned = 0;
|
|
4052
|
+
for (const preset of listPresets()) {
|
|
4053
|
+
const dir = join(runtime.presetDir, preset.id);
|
|
4054
|
+
for (const name of [
|
|
4055
|
+
"agent.cordis.yml",
|
|
4056
|
+
"prompt-configs",
|
|
4057
|
+
"preset.md",
|
|
4058
|
+
"agents.md",
|
|
4059
|
+
"agents-instruction.md",
|
|
4060
|
+
"engine"
|
|
4061
|
+
]) try {
|
|
4062
|
+
rmSync(join(dir, name), {
|
|
4063
|
+
recursive: true,
|
|
4064
|
+
force: true
|
|
4065
|
+
});
|
|
4066
|
+
} catch {}
|
|
4067
|
+
cleaned += 1;
|
|
4068
|
+
}
|
|
4069
|
+
warn(ctx, `prompt-tool: writePreset 已关闭,清理 ${cleaned} 个预设目录的生成物(preset.yml 参数保留)`);
|
|
3807
4070
|
}
|
|
3808
4071
|
};
|
|
3809
4072
|
/** 激活预设目录(内容按预设根 presetDir/<template>/ 隔离;非法名回退 anchored)。 */
|
|
@@ -3845,7 +4108,6 @@ function apply(ctx, configIn) {
|
|
|
3845
4108
|
if (typeof overrides.subagentReasoningEffort === "string") runtime.subagentReasoningEffort = overrides.subagentReasoningEffort;
|
|
3846
4109
|
if (typeof overrides.subagentTemperature === "string") runtime.subagentTemperature = overrides.subagentTemperature;
|
|
3847
4110
|
if (typeof overrides.subagentMaxTokens === "string") runtime.subagentMaxTokens = overrides.subagentMaxTokens;
|
|
3848
|
-
if (typeof overrides.mainPersona === "string" && overrides.mainPersona.trim().length > 0) runtime.mainPersona = overrides.mainPersona;
|
|
3849
4111
|
if (typeof overrides.subagentPersona === "string") runtime.subagentPersona = overrides.subagentPersona;
|
|
3850
4112
|
if (typeof overrides.firstTurnWord === "string") runtime.firstTurnWord = overrides.firstTurnWord;
|
|
3851
4113
|
if (typeof overrides.bootstrapMaxTokens === "number") runtime.bootstrapMaxTokens = overrides.bootstrapMaxTokens;
|
|
@@ -4034,7 +4296,6 @@ function apply(ctx, configIn) {
|
|
|
4034
4296
|
subagentMaxTokens: asString(initialParams.subagentMaxTokens),
|
|
4035
4297
|
bootstrapMaxTokens: Number.isSafeInteger(initialParams.bootstrapMaxTokens) && initialParams.bootstrapMaxTokens >= 0 ? initialParams.bootstrapMaxTokens : 0,
|
|
4036
4298
|
usePtcMode: initialParams.usePtcMode !== false,
|
|
4037
|
-
mainPersona: asString(initialParams.mainPersona) || void 0,
|
|
4038
4299
|
subagentPersona: asString(initialParams.subagentPersona) || void 0,
|
|
4039
4300
|
toolFilterAllow: initialParams.toolFilterAllow,
|
|
4040
4301
|
toolFilterDeny: initialParams.toolFilterDeny,
|
|
@@ -4054,6 +4315,10 @@ function apply(ctx, configIn) {
|
|
|
4054
4315
|
const s = hostSettingsService;
|
|
4055
4316
|
if (s === void 0) return;
|
|
4056
4317
|
const template = runtime.presetTemplate;
|
|
4318
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(template)) {
|
|
4319
|
+
warn(ctx, `prompt-tool: 预设 id ${JSON.stringify(template)} 不符合官方 agent-presets 命名(^[a-z0-9][a-z0-9-]*$),跳过宿主 default 同步;请将预设目录改名为合法 id`);
|
|
4320
|
+
return;
|
|
4321
|
+
}
|
|
4057
4322
|
if (reason === "switch" && lastSyncedHostDefault === template) return;
|
|
4058
4323
|
lastSyncedHostDefault = template;
|
|
4059
4324
|
try {
|
|
@@ -4116,7 +4381,6 @@ function apply(ctx, configIn) {
|
|
|
4116
4381
|
modelName: "",
|
|
4117
4382
|
subagentModelProvider: "",
|
|
4118
4383
|
subagentModelName: "",
|
|
4119
|
-
mainPersona: "",
|
|
4120
4384
|
subagentPersona: "",
|
|
4121
4385
|
injectAgentsPrompt: false,
|
|
4122
4386
|
bootstrapMaxTokens: 0,
|
|
@@ -4214,6 +4478,7 @@ function apply(ctx, configIn) {
|
|
|
4214
4478
|
}
|
|
4215
4479
|
}
|
|
4216
4480
|
});
|
|
4481
|
+
registerSessionVarTools(ctx);
|
|
4217
4482
|
const settingsEntry = currentSource();
|
|
4218
4483
|
const ensureRegistered = (sctx) => ensureSettingsRegistered(sctx, NS, PromptSettingsSchema, {
|
|
4219
4484
|
base: () => settingsEntry,
|
|
@@ -4234,11 +4499,21 @@ function apply(ctx, configIn) {
|
|
|
4234
4499
|
},
|
|
4235
4500
|
onError: (message) => warn(ctx, `prompt-tool: settings register failed: ${message}`)
|
|
4236
4501
|
});
|
|
4237
|
-
/** 阶段 2 迁移:旧全局 settings 引擎参数 → 激活预设 preset.yml
|
|
4238
|
-
|
|
4502
|
+
/** 阶段 2 迁移:旧全局 settings 引擎参数 → 激活预设 preset.yml(一次性,state.paramsMigrated 后跳过)。
|
|
4503
|
+
* 兼容旧版预设根内 .pt-params-migrated 标记:存在即视为已迁移,并迁入状态文件后删除。 */
|
|
4239
4504
|
const migrateSettingsParamsToPreset = (sctx) => {
|
|
4240
|
-
const
|
|
4241
|
-
|
|
4505
|
+
const legacyMark = join(runtime.presetDir, ".pt-params-migrated");
|
|
4506
|
+
const state = readPluginState();
|
|
4507
|
+
if (state.paramsMigrated === true || existsSync(legacyMark)) {
|
|
4508
|
+
if (existsSync(legacyMark)) try {
|
|
4509
|
+
writePluginState({
|
|
4510
|
+
...state,
|
|
4511
|
+
paramsMigrated: true
|
|
4512
|
+
});
|
|
4513
|
+
rmSync(legacyMark, { force: true });
|
|
4514
|
+
} catch {}
|
|
4515
|
+
return;
|
|
4516
|
+
}
|
|
4242
4517
|
let userSection = {};
|
|
4243
4518
|
try {
|
|
4244
4519
|
const descriptor = sctx.settings.describe({ redactSecrets: true }).find((entry) => String(entry.ns) === String(NS));
|
|
@@ -4252,7 +4527,10 @@ function apply(ctx, configIn) {
|
|
|
4252
4527
|
if (Object.keys(params).length === 0 && promptConfigs === void 0) {
|
|
4253
4528
|
try {
|
|
4254
4529
|
mkdirSync(runtime.presetDir, { recursive: true });
|
|
4255
|
-
|
|
4530
|
+
writePluginState({
|
|
4531
|
+
...readPluginState(),
|
|
4532
|
+
paramsMigrated: true
|
|
4533
|
+
});
|
|
4256
4534
|
} catch {}
|
|
4257
4535
|
return;
|
|
4258
4536
|
}
|
|
@@ -4265,12 +4543,18 @@ function apply(ctx, configIn) {
|
|
|
4265
4543
|
written++;
|
|
4266
4544
|
} catch {}
|
|
4267
4545
|
if (written === 0) {
|
|
4268
|
-
|
|
4546
|
+
writePluginState({
|
|
4547
|
+
...readPluginState(),
|
|
4548
|
+
paramsMigrated: true
|
|
4549
|
+
});
|
|
4269
4550
|
return;
|
|
4270
4551
|
}
|
|
4271
4552
|
reloadPresetParams();
|
|
4272
4553
|
rebuildPreset();
|
|
4273
|
-
|
|
4554
|
+
writePluginState({
|
|
4555
|
+
...readPluginState(),
|
|
4556
|
+
paramsMigrated: true
|
|
4557
|
+
});
|
|
4274
4558
|
warn(ctx, `prompt-tool: 已把旧全局 settings 参数迁移到 ${written} 个预设 preset.yml(每预设独立存储)`);
|
|
4275
4559
|
} catch (error) {
|
|
4276
4560
|
warn(ctx, `prompt-tool: settings 参数迁移失败(下次启动重试):${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -4292,4 +4576,4 @@ function apply(ctx, configIn) {
|
|
|
4292
4576
|
});
|
|
4293
4577
|
}
|
|
4294
4578
|
//#endregion
|
|
4295
|
-
export { BRIDGE_ENDPOINTS, Config, PARAM_KEYS, PromptSettingsSchema, SETTINGS_BRIDGE_PREFIX, SKILL_NAME_RE, apply, applyModuleConfigs, cloneBuiltinPreset, createCachedSkillsReader, detectModels, ensurePresetSeed, ensureSettingsRegistered, ensureWebSurface, fixSkillEntry, inject, installDefaultModelRoute, isValidSkill, listAdvertisedModels, listBuiltinTemplates, listPresets, listSkillFolders, loadPresetSpec, loadPromptTemplates, mergeSkillDirs, migrateLegacyLayout, name, normalizePresetRootDir, parseFrontmatter, readSkills, registerCharacterTools, registerSettingsBridge, registerTuiCommand, registerWorldBookTools, removeUserPreset, resolvePresetDir, resolvePresetParams, resolveProfileDir, resolveProfileSkillsDir, savePresetParams, toKebabName, userPresetsDir, validSkills, validatePromptConfigs, writePreset };
|
|
4579
|
+
export { BRIDGE_ENDPOINTS, Config, PARAM_KEYS, PromptSettingsSchema, SETTINGS_BRIDGE_PREFIX, SKILL_NAME_RE, apply, applyModuleConfigs, cloneBuiltinPreset, createCachedSkillsReader, detectModels, ensurePresetSeed, ensureSettingsRegistered, ensureWebSurface, fixSkillEntry, inject, installDefaultModelRoute, isValidSkill, listAdvertisedModels, listBuiltinTemplates, listPresets, listSkillFolders, loadPresetSpec, loadPromptTemplates, mergeSkillDirs, migrateLegacyLayout, name, normalizePresetRootDir, parseFrontmatter, readPluginState, readSkills, registerCharacterTools, registerSettingsBridge, registerTuiCommand, registerWorldBookTools, removeUserPreset, resolvePresetDir, resolvePresetParams, resolveProfileDir, resolveProfileSkillsDir, savePresetParams, stPresetId, toKebabName, userPresetsDir, validSkills, validatePromptConfigs, writePluginState, writePreset };
|