dsh-plugin-prompt-tool 0.6.2 → 0.7.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 +11 -9
- package/engine/anchor-match.mjs +38 -12
- package/engine/anchor-turn.mjs +68 -0
- package/engine/classify-task.mjs +35 -0
- package/engine/code-presentation.mjs +103 -0
- package/engine/compositions/library/anchor-turn.yml +10 -0
- package/engine/compositions/library/bootstrap-filesystem.yml +1 -1
- package/engine/compositions/library/code-presentation.yml +8 -0
- package/engine/compositions/library/command-goal.yml +11 -0
- package/engine/compositions/library/compaction.yml +1 -1
- package/engine/compositions/library/context-gate.yml +1 -2
- package/engine/compositions/library/cot-drip.yml +11 -0
- package/engine/compositions/library/custom-bash.yml +1 -1
- package/engine/compositions/library/delegation.yml +11 -7
- package/engine/compositions/library/deliberation-gate.yml +11 -0
- package/engine/compositions/library/persistent-shell.yml +25 -2
- package/engine/compositions/library/persona.yml +6 -2
- package/engine/compositions/library/planning.yml +1 -1
- package/engine/compositions/library/prompt-config-engine.yml +1 -1
- package/engine/compositions/library/run-code-env.yml +1 -1
- package/engine/compositions/library/skill-filesystem.yml +1 -1
- package/engine/compositions/library/skill-search.yml +1 -1
- package/engine/compositions/library/str-replace-editor.yml +2 -2
- package/engine/compositions/library/tool-ask-user.yml +1 -1
- package/engine/compositions/library/tool-bash.yml +1 -1
- package/engine/compositions/library/tool-bootstrap.yml +1 -3
- package/engine/compositions/library/tool-config-engine.yml +9 -0
- package/engine/compositions/library/tool-filter.yml +3 -7
- package/engine/compositions/library/tool-fs-search.yml +1 -1
- package/engine/compositions/library/tool-fs.yml +1 -1
- package/engine/compositions/library/tool-goal.yml +1 -9
- package/engine/compositions/library/tool-jobs.yml +1 -1
- package/engine/compositions/library/tool-pwsh.yml +1 -1
- package/engine/compositions/library/tool-todo.yml +1 -1
- package/engine/compositions/library/tool-web.yml +2 -2
- package/engine/compositions/source/local/code-presentation.yml +8 -0
- package/engine/compositions/source/local/context-gate.yml +0 -1
- package/engine/compositions/source/local/str-replace-editor.yml +10 -0
- package/engine/compositions/source/local/tool-bootstrap.yml +1 -3
- package/engine/compositions/source/local/tool-config-engine.yml +9 -0
- package/engine/compositions/source/local/tool-filter.yml +8 -0
- package/engine/cot-drip.mjs +137 -0
- package/engine/custom-bash.mjs +73 -21
- package/engine/deliberation-gate.mjs +127 -0
- package/engine/layers.mjs +223 -198
- package/engine/run-code-env.mjs +8 -2
- package/engine/schema.mjs +1 -1
- package/engine/strategies.mjs +25 -14
- package/engine/tool-bootstrap.mjs +185 -59
- package/engine/tool-config-engine.mjs +390 -0
- package/lib/client.js +3840 -2311
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +185 -79
- package/lib/index.mjs +1000 -356
- package/lib/preset-core.d.mts +1 -1
- package/lib/preset-core.mjs +29 -3
- package/lib/{prompt-configs-BgFw_bFf.mjs → prompt-configs-BnQKRHVb.mjs} +211 -136
- package/lib/prompt-configs-CN0sJTUL.d.mts +193 -0
- package/package.json +121 -105
- package/preset/anchored/preset.yml +262 -308
- package/preset/creative/preset.yml +34 -32
- package/preset/custom/preset.yml +0 -1
- package/preset/liangshen/preset.yml +53 -50
- package/preset/minimal/preset.yml +18 -17
- package/preset/ptc/preset.yml +35 -33
- package/preset/standard/preset.yml +34 -32
- package/templates/10-pre-step.yml +18 -15
- package/templates/14-first-turn-anchor.yml +13 -13
- package/templates/15-guide-auto.yml +13 -13
- package/templates/16-custom-fallback.yml +19 -18
- package/templates/18-placeholder-env-facts.yml +13 -2
- package/templates/30-runtime-context.yml +3 -0
- package/templates/40-agent-request.yml +9 -9
- package/templates/60-tool-pipeline.yml +9 -8
- package/templates/tools/10-shell-command.yml +20 -0
- package/templates/tools/20-http-fetch.yml +24 -0
- package/templates/tools/30-fs-read.yml +20 -0
- package/templates/tools/40-delegate-character.yml +15 -0
- package/templates/tools/50-delegate-worldbook.yml +15 -0
- package/templates/tools/60-delegate-session-var.yml +15 -0
- package/templates/tools/70-delegate-worldbook-upsert.yml +36 -0
- package/lib/prompt-configs-B4vH09wx.d.mts +0 -100
- package/templates/11-merged-a.yml +0 -11
- package/templates/13-anchor.yml +0 -19
- package/templates/17-instruction-hint.yml +0 -23
- package/templates/19-placeholder-skill-catalog.yml +0 -19
- package/templates/31-runtime-context-placeholder.yml +0 -18
package/lib/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { A as DEFAULT_SKILLS_DIR, C as resolvePresetParams, D as writePluginState, E as withPresetDoc, M as LEGACY_USER_PRESETS_DIR, O as DEFAULT_PRESET_DIR, S as resolvePresetDir, T as userPresetsDir, _ as packageEngineDir, a as applyModuleConfigs, b as removeUserPreset, c as buildModuleConfigsFromParams, d as ensurePresetSeed, f as listBuiltinTemplates, g as openPresetLocation, h as loadPresetSpec, i as MODEL_SEGMENT_MAP, j as LEGACY_CONTAINER_DIR, k as DEFAULT_RESIDENT_AGENTS_PATH, l as cloneBuiltinPreset, m as loadPresetContent, n as mergePromptConfigs, o as asString, p as listPresets, r as renderPromptConfigYaml, s as assertCompositionArray, t as loadPromptConfigFiles, u as duplicateUserPreset, v as parseImportedPresetId, w as savePresetParams, x as renderComposition, y as readPluginState } from "./prompt-configs-BnQKRHVb.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
|
-
import { parse, parseDocument, stringify } from "yaml";
|
|
5
4
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { parse, parseDocument, stringify } from "yaml";
|
|
7
7
|
import z from "@deepseek-ai/schemastery";
|
|
8
8
|
import { createHash, randomBytes } from "node:crypto";
|
|
9
9
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
@@ -551,23 +551,20 @@ function installSubagentModelRoute(ctx, isEnabled, provider, model) {
|
|
|
551
551
|
});
|
|
552
552
|
}
|
|
553
553
|
//#endregion
|
|
554
|
-
//#region src/shared/
|
|
554
|
+
//#region src/shared/engine-params.ts
|
|
555
555
|
/**
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
* 这些键在 UI 有专门编辑入口(模型设置 / 工具与深度 / 开关),writePreset 把
|
|
561
|
-
* 预设级 params 合并进每条 promptConfig 的 params 时排除——「params(高级参数
|
|
562
|
-
* JSON)」框只保留内容变量与配置自身策略参数,避免 UI 已管理的参数冗余回写。
|
|
563
|
-
* 放 shared:host(write-preset)与 config/settings-bridge 共用,单一来源。
|
|
556
|
+
* 引擎行为参数键唯一权威(数组字面量 = 运行时事实)。
|
|
557
|
+
* 与 EngineParams 接口双向相等断言:新增参数必须同时改接口与列表,漏改任一侧
|
|
558
|
+
* typecheck 失败(防历史事故:PARAM_KEYS 漏 25 键混入 variables.yml 污染注入)。
|
|
564
559
|
*/
|
|
565
|
-
const
|
|
560
|
+
const ENGINE_PARAM_KEYS = [
|
|
566
561
|
"firstTurnAnchor",
|
|
567
562
|
"firstTurnText",
|
|
568
563
|
"firstTurnCustom",
|
|
569
564
|
"guideText",
|
|
570
565
|
"guideCustom",
|
|
566
|
+
"guideEnabled",
|
|
567
|
+
"injectPrompt",
|
|
571
568
|
"modelProvider",
|
|
572
569
|
"modelName",
|
|
573
570
|
"subagentModelProvider",
|
|
@@ -578,7 +575,6 @@ const PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
|
578
575
|
"subagentReasoningEffort",
|
|
579
576
|
"subagentTemperature",
|
|
580
577
|
"subagentMaxTokens",
|
|
581
|
-
"subagentPersona",
|
|
582
578
|
"toolFilterAllow",
|
|
583
579
|
"toolFilterDeny",
|
|
584
580
|
"maxDepth",
|
|
@@ -586,9 +582,143 @@ const PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
|
586
582
|
"firstTurnWord",
|
|
587
583
|
"bootstrapMaxTokens",
|
|
588
584
|
"usePtcMode",
|
|
585
|
+
"promoteGate",
|
|
586
|
+
"promoteAfterFirstResponse",
|
|
587
|
+
"maxPromoteSteps",
|
|
588
|
+
"bootstrapTools",
|
|
589
|
+
"compactionTools",
|
|
590
|
+
"personaSectionsOnly",
|
|
591
|
+
"workspaceLine",
|
|
592
|
+
"phase1FirstCallInstruction",
|
|
593
|
+
"messageSources",
|
|
594
|
+
"deferredSources",
|
|
595
|
+
"deferredGraceSteps",
|
|
596
|
+
"instructionHint",
|
|
597
|
+
"stages",
|
|
598
|
+
"stagePreUnlock",
|
|
599
|
+
"stageAdvanceTool",
|
|
600
|
+
"stageAdvanceDescription",
|
|
601
|
+
"stageSectionTemplate",
|
|
602
|
+
"toolFilterSubagents",
|
|
603
|
+
"strReplaceEditorMaxOutputChars",
|
|
604
|
+
"anchorTurn",
|
|
605
|
+
"anchorTurnText",
|
|
606
|
+
"deliberationGate",
|
|
607
|
+
"deliberationMinChars",
|
|
608
|
+
"deliberationMaxGatesPerTurn",
|
|
609
|
+
"cotDrip",
|
|
610
|
+
"cotDripEvery",
|
|
611
|
+
"cotDripMaxPerTurn"
|
|
612
|
+
];
|
|
613
|
+
/** writePreset.runtimeOf 实际透传键(与 PresetWriterParams 双向相等断言,防 Pick 漏键)。 */
|
|
614
|
+
const WRITER_PARAM_KEYS = [
|
|
615
|
+
"firstTurnAnchor",
|
|
616
|
+
"firstTurnText",
|
|
617
|
+
"firstTurnCustom",
|
|
618
|
+
"guideText",
|
|
619
|
+
"guideCustom",
|
|
620
|
+
"guideEnabled",
|
|
589
621
|
"injectPrompt",
|
|
622
|
+
"modelProvider",
|
|
623
|
+
"modelName",
|
|
624
|
+
"subagentModelProvider",
|
|
625
|
+
"subagentModelName",
|
|
626
|
+
"modelReasoningEffort",
|
|
627
|
+
"modelTemperature",
|
|
628
|
+
"modelMaxTokens",
|
|
629
|
+
"subagentReasoningEffort",
|
|
630
|
+
"subagentTemperature",
|
|
631
|
+
"subagentMaxTokens",
|
|
632
|
+
"toolFilterAllow",
|
|
633
|
+
"toolFilterDeny",
|
|
634
|
+
"maxDepth",
|
|
635
|
+
"allowKinds",
|
|
636
|
+
"firstTurnWord",
|
|
637
|
+
"bootstrapMaxTokens",
|
|
638
|
+
"usePtcMode",
|
|
639
|
+
"anchorTurn",
|
|
640
|
+
"anchorTurnText",
|
|
641
|
+
"deliberationGate",
|
|
642
|
+
"deliberationMinChars",
|
|
643
|
+
"deliberationMaxGatesPerTurn",
|
|
644
|
+
"cotDrip",
|
|
645
|
+
"cotDripEvery",
|
|
646
|
+
"cotDripMaxPerTurn"
|
|
647
|
+
];
|
|
648
|
+
const NUMERIC_PARAM_RULES = {
|
|
649
|
+
modelTemperature: validateTemperature,
|
|
650
|
+
subagentTemperature: validateTemperature,
|
|
651
|
+
modelMaxTokens: validateMaxTokens,
|
|
652
|
+
subagentMaxTokens: validateMaxTokens
|
|
653
|
+
};
|
|
654
|
+
function validateTemperature(value) {
|
|
655
|
+
return Number.isFinite(value) ? void 0 : "必须是有限数字";
|
|
656
|
+
}
|
|
657
|
+
function validateMaxTokens(value) {
|
|
658
|
+
return Number.isSafeInteger(value) && value > 0 ? void 0 : "必须是正整数";
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* 校验参数键值(键集合合法性由调用方白名单先行校验)。返回逐字段错误;空数组 = 通过。
|
|
662
|
+
* 类型收窄:只接受 number(preset.yml 手写数字)与 string(UI 字符串标量);
|
|
663
|
+
* '' 是合法删键值,其余空白串/非数字串/越界值在保存层响亮失败。
|
|
664
|
+
*/
|
|
665
|
+
function validateEngineParamValues(overrides) {
|
|
666
|
+
const errors = [];
|
|
667
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
668
|
+
const rule = NUMERIC_PARAM_RULES[key];
|
|
669
|
+
if (rule === void 0) continue;
|
|
670
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
671
|
+
if (typeof value !== "number" && typeof value !== "string") {
|
|
672
|
+
errors.push({
|
|
673
|
+
key,
|
|
674
|
+
message: `${key}: 必须是数字或数字字符串(留空 = 不设置)`
|
|
675
|
+
});
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
const text = typeof value === "string" ? value.trim() : String(value);
|
|
679
|
+
if (text.length === 0) {
|
|
680
|
+
errors.push({
|
|
681
|
+
key,
|
|
682
|
+
message: `${key}: 必须是数字(留空 = 不设置)`
|
|
683
|
+
});
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
const numeric = Number(text);
|
|
687
|
+
const reason = Number.isNaN(numeric) ? "必须是数字" : rule(numeric);
|
|
688
|
+
if (reason !== void 0) errors.push({
|
|
689
|
+
key,
|
|
690
|
+
message: `${key}: ${reason}(留空 = 不设置)`
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
return errors;
|
|
694
|
+
}
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/shared/param-keys.ts
|
|
697
|
+
/**
|
|
698
|
+
* 引擎行为参数键(按预设存储:激活预设 preset.yml 的 params + promptConfigs)。
|
|
699
|
+
* 不进 Config schema、不进 settings namespace——每预设一份,随预设走(官方范式:
|
|
700
|
+
* Config = 部署轴,引擎行为在预设文件)。
|
|
701
|
+
*
|
|
702
|
+
* 派生自 ENGINE_PARAM_KEYS(唯一权威)+ 少量附加键:
|
|
703
|
+
* - 锚定/引导内容键:writePreset 映射进 near-anchor/router-guide 的 promptConfig
|
|
704
|
+
* params(策略消费),须排除出 variables.yml(避免同一键双落盘为模板变量);
|
|
705
|
+
* - promptConfigs:settings 提示词配置数组键。
|
|
706
|
+
* 注意:variables.yml 的占位键(spec.variables 空值登记,供世界书条目 {{key}} 动态
|
|
707
|
+
* 引用)走另一套通道,不属于 PARAM_KEYS——两套体系不互串。
|
|
708
|
+
* 放 shared:host(write-preset)与 config/settings-bridge 共用,单一来源。
|
|
709
|
+
*/
|
|
710
|
+
const EXTRA_PARAM_KEYS = [
|
|
711
|
+
"buildPattern",
|
|
712
|
+
"complexPattern",
|
|
713
|
+
"firstTurnBuild",
|
|
714
|
+
"firstTurnInspect",
|
|
715
|
+
"firstTurnDeep",
|
|
716
|
+
"guideComplexPattern",
|
|
717
|
+
"guideWeak",
|
|
718
|
+
"guideDeep",
|
|
590
719
|
"promptConfigs"
|
|
591
|
-
]
|
|
720
|
+
];
|
|
721
|
+
const PARAM_KEYS = /* @__PURE__ */ new Set([...ENGINE_PARAM_KEYS, ...EXTRA_PARAM_KEYS]);
|
|
592
722
|
//#endregion
|
|
593
723
|
//#region src/config.ts
|
|
594
724
|
/** 插件配置、settings 数据模型与默认常量(settings 接口层)。 */
|
|
@@ -730,6 +860,20 @@ function loadPromptTemplates() {
|
|
|
730
860
|
};
|
|
731
861
|
});
|
|
732
862
|
}
|
|
863
|
+
/** 工具模板库扫描(templates/tools/*.yml;损坏 fail loud——包内模板属于发布 bug)。 */
|
|
864
|
+
function loadToolTemplates() {
|
|
865
|
+
const dir = fileURLToPath(new URL("../templates/tools", import.meta.url));
|
|
866
|
+
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && /\.ya?ml$/i.test(entry.name)).sort((a, b) => a.name.localeCompare(b.name)).map((entry) => {
|
|
867
|
+
const content = readFileSync(join(dir, entry.name), "utf8");
|
|
868
|
+
const parsed = parse(content, { logLevel: "silent" });
|
|
869
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed) || typeof parsed.id !== "string" || parsed.id.length === 0 || typeof parsed.name !== "string" || parsed.execute === null || typeof parsed.execute !== "object") throw new Error(`tool template ${entry.name} must contain id/name/execute`);
|
|
870
|
+
return {
|
|
871
|
+
file: entry.name,
|
|
872
|
+
content,
|
|
873
|
+
spec: parsed
|
|
874
|
+
};
|
|
875
|
+
});
|
|
876
|
+
}
|
|
733
877
|
//#endregion
|
|
734
878
|
//#region src/runtime/skill-fix.ts
|
|
735
879
|
/**
|
|
@@ -850,6 +994,73 @@ function fixSkillEntry(skillsDir, folder) {
|
|
|
850
994
|
};
|
|
851
995
|
}
|
|
852
996
|
//#endregion
|
|
997
|
+
//#region src/host/worldbook.ts
|
|
998
|
+
/** 世界书条目 CRUD 服务:promptConfigs world-book 策略配置的增删改查。
|
|
999
|
+
* 与角色卡导入(characters.applyCharacterToPreset)共用同一存储(preset.yml
|
|
1000
|
+
* promptConfigs),模型工具(world_book_*)与未来 bridge 端点同源,
|
|
1001
|
+
* 不各自实现 parseDocument 往返。 */
|
|
1002
|
+
/**
|
|
1003
|
+
* 世界书条目结构工厂(能力归一)。
|
|
1004
|
+
* strategy/layer/position 固定值与 params 键集单一权威——ST 导入(sillytavern.ts)
|
|
1005
|
+
* 与模型工具(world_book_upsert)共用,不再各自手写条目结构。
|
|
1006
|
+
*/
|
|
1007
|
+
function buildWorldBookEntry(input) {
|
|
1008
|
+
return {
|
|
1009
|
+
...input.id !== void 0 ? { id: input.id } : {},
|
|
1010
|
+
name: input.name,
|
|
1011
|
+
strategy: "world-book",
|
|
1012
|
+
order: typeof input.order === "number" ? input.order : 100,
|
|
1013
|
+
text: input.text,
|
|
1014
|
+
layer: "pre-step",
|
|
1015
|
+
position: "before-all",
|
|
1016
|
+
...input.enabled !== void 0 ? { enabled: input.enabled } : {},
|
|
1017
|
+
params: {
|
|
1018
|
+
constant: input.constant,
|
|
1019
|
+
...input.keys !== void 0 && input.keys.length > 0 ? { keys: input.keys } : {},
|
|
1020
|
+
...input.secondaryKeys !== void 0 && input.secondaryKeys.length > 0 ? { secondaryKeys: input.secondaryKeys } : {},
|
|
1021
|
+
...input.caseSensitive === true ? { caseSensitive: true } : {},
|
|
1022
|
+
...input.wholeWords === true ? { wholeWords: true } : {},
|
|
1023
|
+
...typeof input.selectiveLogic === "number" ? { selectiveLogic: input.selectiveLogic } : {}
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
const isWorldBook = (config) => config !== null && typeof config === "object" && !Array.isArray(config) && config.strategy === "world-book";
|
|
1028
|
+
/** 当前预设全部世界书条目(保持文件顺序)。 */
|
|
1029
|
+
function listWorldBookEntries(presetDir) {
|
|
1030
|
+
const spec = loadPresetSpec(presetDir);
|
|
1031
|
+
return Array.isArray(spec.promptConfigs) ? spec.promptConfigs.filter(isWorldBook) : [];
|
|
1032
|
+
}
|
|
1033
|
+
/** 新增或更新一条世界书条目(按 id 定位;不存在则追加)。返回写入后条目总数。 */
|
|
1034
|
+
function upsertWorldBookEntry(presetDir, entry) {
|
|
1035
|
+
if (entry === null || typeof entry !== "object" || typeof entry.id !== "string" || entry.id.length === 0) throw new TypeError("世界书条目必须含非空字符串 id");
|
|
1036
|
+
let count = 0;
|
|
1037
|
+
withPresetDoc(presetDir, (doc) => {
|
|
1038
|
+
const current = doc.toJS();
|
|
1039
|
+
const configs = Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
1040
|
+
const existing = configs.findIndex((config) => String(config.id ?? "") === entry.id);
|
|
1041
|
+
if (existing >= 0) configs[existing] = entry;
|
|
1042
|
+
else configs.push(entry);
|
|
1043
|
+
doc.setIn(["promptConfigs"], configs);
|
|
1044
|
+
count = configs.filter(isWorldBook).length;
|
|
1045
|
+
});
|
|
1046
|
+
return count;
|
|
1047
|
+
}
|
|
1048
|
+
/** 删除一条世界书条目;不存在抛错。返回删除后条目总数。 */
|
|
1049
|
+
function deleteWorldBookEntry(presetDir, id) {
|
|
1050
|
+
let count = 0;
|
|
1051
|
+
let removed = false;
|
|
1052
|
+
withPresetDoc(presetDir, (doc) => {
|
|
1053
|
+
const current = doc.toJS();
|
|
1054
|
+
const configs = Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
1055
|
+
const kept = configs.filter((config) => !(isWorldBook(config) && String(config.id ?? "") === id));
|
|
1056
|
+
removed = kept.length !== configs.length;
|
|
1057
|
+
doc.setIn(["promptConfigs"], kept);
|
|
1058
|
+
count = kept.filter(isWorldBook).length;
|
|
1059
|
+
});
|
|
1060
|
+
if (!removed) throw new Error(`世界书条目 ${id} 不存在`);
|
|
1061
|
+
return count;
|
|
1062
|
+
}
|
|
1063
|
+
//#endregion
|
|
853
1064
|
//#region src/host/sillytavern.ts
|
|
854
1065
|
/**
|
|
855
1066
|
* SillyTavern 预设 → 本项目单文件预设转换引擎(纯函数,无文件 IO)。
|
|
@@ -1016,25 +1227,20 @@ function convertStToPreset(card, baseName) {
|
|
|
1016
1227
|
const secondaryKeys = Array.isArray(rawSecondary) ? rawSecondary.map(String).filter((key) => key.trim().length > 0) : [];
|
|
1017
1228
|
const constant = entry.constant === true || entry.add_always === true;
|
|
1018
1229
|
const enabled = entry.disable !== void 0 ? entry.disable !== true : entry.enabled !== false;
|
|
1019
|
-
|
|
1230
|
+
const stOrder = typeof entry.insertion_order === "number" ? entry.insertion_order : typeof entry.order === "number" ? entry.order : 100;
|
|
1231
|
+
configs.push(buildWorldBookEntry({
|
|
1020
1232
|
id: `lore-${String(entry.id ?? entry.uid ?? index)}`,
|
|
1021
1233
|
name: comment,
|
|
1022
1234
|
enabled,
|
|
1023
|
-
|
|
1024
|
-
order: typeof entry.insertion_order === "number" ? entry.insertion_order : typeof entry.order === "number" ? entry.order : 100,
|
|
1235
|
+
order: -stOrder,
|
|
1025
1236
|
text: content,
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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
|
-
});
|
|
1237
|
+
constant,
|
|
1238
|
+
keys: keys.length > 0 ? keys : void 0,
|
|
1239
|
+
secondaryKeys: secondaryKeys.length > 0 ? secondaryKeys : void 0,
|
|
1240
|
+
...(entry.case_sensitive ?? entry.caseSensitive) === true ? { caseSensitive: true } : {},
|
|
1241
|
+
...(entry.match_whole_words ?? entry.matchWholeWords) === true ? { wholeWords: true } : {},
|
|
1242
|
+
...typeof entry.selectiveLogic === "number" ? { selectiveLogic: entry.selectiveLogic } : typeof entry.selective_logic === "number" ? { selectiveLogic: entry.selective_logic } : {}
|
|
1243
|
+
}));
|
|
1038
1244
|
}
|
|
1039
1245
|
}
|
|
1040
1246
|
const firstMes = clean(bodyText("first_mes"));
|
|
@@ -1188,6 +1394,70 @@ function sortConfigs(configs) {
|
|
|
1188
1394
|
return (Number(a.order) || 0) - (Number(b.order) || 0);
|
|
1189
1395
|
});
|
|
1190
1396
|
}
|
|
1397
|
+
/** 角色卡记忆条目字段(不含 id;id 由调用方加 chara-<卡>- 前缀)。 */
|
|
1398
|
+
function buildCharacterMemoryEntry(spec, memory) {
|
|
1399
|
+
if (memory.trim().length === 0) return void 0;
|
|
1400
|
+
return buildWorldBookEntry({
|
|
1401
|
+
name: "角色记忆",
|
|
1402
|
+
enabled: true,
|
|
1403
|
+
order: 3e3,
|
|
1404
|
+
text: `【${spec.name} 的关系记忆】\n${memory}`,
|
|
1405
|
+
constant: true
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
/** 角色卡记忆变更后同步已导入预设的 chara-<id>-memory 注入条目
|
|
1409
|
+
* (world_book note 写入卡记忆后调用;未导入当前预设的卡返回 synced=false)。 */
|
|
1410
|
+
function syncImportedCharacterMemory(presetRoot, templateName, cardId) {
|
|
1411
|
+
if (!validCardId(cardId)) return {
|
|
1412
|
+
ok: false,
|
|
1413
|
+
message: `非法角色卡 id:${cardId}`
|
|
1414
|
+
};
|
|
1415
|
+
const spec = loadConverted(cardDir(presetRoot, cardId));
|
|
1416
|
+
if (spec === void 0) return {
|
|
1417
|
+
ok: false,
|
|
1418
|
+
message: `角色卡 ${cardId} 不存在或参数损坏`
|
|
1419
|
+
};
|
|
1420
|
+
const memory = readCharacterMemory(presetRoot, cardId);
|
|
1421
|
+
const memoryId = `chara-${cardId}-memory`;
|
|
1422
|
+
let synced = false;
|
|
1423
|
+
try {
|
|
1424
|
+
withPresetDoc(join(presetRoot, templateName), (doc) => {
|
|
1425
|
+
const current = doc.toJS();
|
|
1426
|
+
if (!(Array.isArray(current.meta?.importedCharacters) ? current.meta.importedCharacters.map(String) : []).includes(cardId)) return;
|
|
1427
|
+
const configs = Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
1428
|
+
const entry = buildCharacterMemoryEntry(spec, memory);
|
|
1429
|
+
const at = configs.findIndex((config) => config !== null && typeof config === "object" && String(config.id ?? "") === memoryId);
|
|
1430
|
+
if (entry === void 0) {
|
|
1431
|
+
if (at >= 0) {
|
|
1432
|
+
configs.splice(at, 1);
|
|
1433
|
+
synced = true;
|
|
1434
|
+
}
|
|
1435
|
+
} else if (at >= 0) {
|
|
1436
|
+
configs[at] = {
|
|
1437
|
+
...entry,
|
|
1438
|
+
id: memoryId
|
|
1439
|
+
};
|
|
1440
|
+
synced = true;
|
|
1441
|
+
} else {
|
|
1442
|
+
configs.push({
|
|
1443
|
+
...entry,
|
|
1444
|
+
id: memoryId
|
|
1445
|
+
});
|
|
1446
|
+
synced = true;
|
|
1447
|
+
}
|
|
1448
|
+
if (synced) doc.setIn(["promptConfigs"], sortConfigs(configs));
|
|
1449
|
+
});
|
|
1450
|
+
return {
|
|
1451
|
+
ok: true,
|
|
1452
|
+
synced
|
|
1453
|
+
};
|
|
1454
|
+
} catch (error) {
|
|
1455
|
+
return {
|
|
1456
|
+
ok: false,
|
|
1457
|
+
message: `同步失败:${error instanceof Error ? error.message : String(error)}`
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1191
1461
|
/** 角色卡库根:预设根下的点前缀目录(官方 roster 扫描跳过,与 .engine 同机制)。 */
|
|
1192
1462
|
function charactersDir(presetRoot) {
|
|
1193
1463
|
return join(presetRoot, ".characters");
|
|
@@ -1205,6 +1475,19 @@ function loadConverted(dir) {
|
|
|
1205
1475
|
return;
|
|
1206
1476
|
}
|
|
1207
1477
|
}
|
|
1478
|
+
/** 追加角色卡本地记忆(.characters/<id>/memory.md,跟随角色卡跨预设)。 */
|
|
1479
|
+
function appendCharacterMemory(presetRoot, cardId, note) {
|
|
1480
|
+
appendMemoryFile(join(cardDir(presetRoot, cardId), "memory.md"), note, "# 角色记忆");
|
|
1481
|
+
}
|
|
1482
|
+
/** 追加记忆文件(时间戳列表格式;header 标注文件类型)。世界书工具与角色卡共用。 */
|
|
1483
|
+
function appendMemoryFile(file, note, header = "# 本地记忆") {
|
|
1484
|
+
const content = note.trim();
|
|
1485
|
+
if (content.length === 0) return;
|
|
1486
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
1487
|
+
const line = `\n- [${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ")}] ${content}\n`;
|
|
1488
|
+
if (existsSync(file)) appendFileSync(file, line, "utf8");
|
|
1489
|
+
else writeFileSync(file, `${header}\n${line}`, "utf8");
|
|
1490
|
+
}
|
|
1208
1491
|
/** 读角色卡本地记忆文本(无记忆返回空串)。 */
|
|
1209
1492
|
function readCharacterMemory(presetRoot, cardId) {
|
|
1210
1493
|
try {
|
|
@@ -1303,13 +1586,6 @@ function deleteCharacterCard(presetRoot, id) {
|
|
|
1303
1586
|
};
|
|
1304
1587
|
}
|
|
1305
1588
|
}
|
|
1306
|
-
function withPresetDoc(presetRoot, templateName, mutate) {
|
|
1307
|
-
const file = join(presetRoot, templateName, "preset.yml");
|
|
1308
|
-
if (!existsSync(file)) throw new Error(`当前预设 ${templateName} 无 preset.yml`);
|
|
1309
|
-
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
1310
|
-
mutate(doc);
|
|
1311
|
-
writeFileSync(file, doc.toString(), "utf8");
|
|
1312
|
-
}
|
|
1313
1589
|
/** 角色卡参数导入当前预设:promptConfigs 合并(chara-<id>- 前缀防冲突)、
|
|
1314
1590
|
* params 合并(角色卡覆盖同键)、meta.importedCharacters 记录来源。 */
|
|
1315
1591
|
function applyCharacterToPreset(presetRoot, templateName, cardId) {
|
|
@@ -1323,9 +1599,31 @@ function applyCharacterToPreset(presetRoot, templateName, cardId) {
|
|
|
1323
1599
|
message: `角色卡 ${cardId} 不存在或参数损坏`
|
|
1324
1600
|
};
|
|
1325
1601
|
const prefix = `chara-${cardId}-`;
|
|
1602
|
+
const hasSystemSections = (spec.promptConfigs ?? []).some((config) => config !== null && typeof config === "object" && !Array.isArray(config) && config.layer === "system-section");
|
|
1603
|
+
let personaOpened = false;
|
|
1326
1604
|
try {
|
|
1327
|
-
withPresetDoc(presetRoot, templateName, (doc) => {
|
|
1605
|
+
withPresetDoc(join(presetRoot, templateName), (doc) => {
|
|
1328
1606
|
const current = doc.toJS();
|
|
1607
|
+
if (hasSystemSections) {
|
|
1608
|
+
const configs = Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
1609
|
+
const personaIdx = configs.findIndex((config) => {
|
|
1610
|
+
if (config === null || typeof config !== "object" || Array.isArray(config)) return false;
|
|
1611
|
+
const entry = config;
|
|
1612
|
+
const params = entry.params;
|
|
1613
|
+
return entry.id === "persona-main" || params?.sectionName === "deployment:persona";
|
|
1614
|
+
});
|
|
1615
|
+
if (personaIdx >= 0) {
|
|
1616
|
+
if ((configs[personaIdx]?.params)?.complete === true) {
|
|
1617
|
+
doc.setIn([
|
|
1618
|
+
"promptConfigs",
|
|
1619
|
+
personaIdx,
|
|
1620
|
+
"params",
|
|
1621
|
+
"complete"
|
|
1622
|
+
], false);
|
|
1623
|
+
personaOpened = true;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1329
1627
|
const existing = Array.isArray(current.promptConfigs) ? current.promptConfigs.filter((config) => {
|
|
1330
1628
|
return config !== null && typeof config === "object" && !String(config.id ?? "").startsWith(prefix);
|
|
1331
1629
|
}) : [];
|
|
@@ -1341,31 +1639,36 @@ function applyCharacterToPreset(presetRoot, templateName, cardId) {
|
|
|
1341
1639
|
for (const [key, value] of Object.entries(spec.params ?? {})) doc.setIn(["params", key], value);
|
|
1342
1640
|
const memory = readCharacterMemory(presetRoot, cardId);
|
|
1343
1641
|
const memoryId = `${prefix}memory`;
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1642
|
+
const memoryEntry = buildCharacterMemoryEntry(spec, memory);
|
|
1643
|
+
const merged = sortConfigs([
|
|
1644
|
+
...existing,
|
|
1645
|
+
...added,
|
|
1646
|
+
...memoryEntry !== void 0 ? [{
|
|
1647
|
+
...memoryEntry,
|
|
1648
|
+
id: memoryId
|
|
1649
|
+
}] : []
|
|
1650
|
+
]);
|
|
1651
|
+
if (hasSystemSections) {
|
|
1652
|
+
const persona = merged.find((config) => {
|
|
1653
|
+
if (config === null || typeof config !== "object" || Array.isArray(config)) return false;
|
|
1654
|
+
const entry = config;
|
|
1655
|
+
const params = entry.params;
|
|
1656
|
+
return entry.id === "persona-main" || params?.sectionName === "deployment:persona";
|
|
1657
|
+
});
|
|
1658
|
+
if ((persona?.params)?.complete === true && persona !== void 0) {
|
|
1659
|
+
persona.params.complete = false;
|
|
1660
|
+
personaOpened = true;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
doc.setIn(["promptConfigs"], merged);
|
|
1362
1664
|
const list = Array.isArray(current.meta?.importedCharacters) ? current.meta.importedCharacters : [];
|
|
1363
1665
|
if (!list.map(String).includes(cardId)) list.push(cardId);
|
|
1364
1666
|
doc.setIn(["meta", "importedCharacters"], list);
|
|
1365
1667
|
});
|
|
1366
1668
|
return {
|
|
1367
1669
|
ok: true,
|
|
1368
|
-
count: (spec.promptConfigs ?? []).length
|
|
1670
|
+
count: (spec.promptConfigs ?? []).length,
|
|
1671
|
+
...personaOpened ? { personaOpened: true } : {}
|
|
1369
1672
|
};
|
|
1370
1673
|
} catch (error) {
|
|
1371
1674
|
return {
|
|
@@ -1385,7 +1688,7 @@ function removeCharacterFromPreset(presetRoot, templateName, cardId) {
|
|
|
1385
1688
|
const prefix = `chara-${cardId}-`;
|
|
1386
1689
|
try {
|
|
1387
1690
|
let removed = 0;
|
|
1388
|
-
withPresetDoc(presetRoot, templateName, (doc) => {
|
|
1691
|
+
withPresetDoc(join(presetRoot, templateName), (doc) => {
|
|
1389
1692
|
const current = doc.toJS();
|
|
1390
1693
|
const kept = (Array.isArray(current.promptConfigs) ? current.promptConfigs : []).filter((config) => {
|
|
1391
1694
|
const isCard = config !== null && typeof config === "object" && String(config.id ?? "").startsWith(prefix);
|
|
@@ -1412,14 +1715,15 @@ function removeCharacterFromPreset(presetRoot, templateName, cardId) {
|
|
|
1412
1715
|
//#region src/shared/bridge-contract.ts
|
|
1413
1716
|
/**
|
|
1414
1717
|
* settings bridge 跨端契约(host 注册 / client 消费的唯一来源)。
|
|
1415
|
-
* 铁律:
|
|
1718
|
+
* 铁律:25 端点载荷统一成功 `{ ok: true, value }`、失败 `{ ok: false, code?, message? }`;
|
|
1416
1719
|
* 端点附加字段只能以 value 旁的可选扩展字段出现(describe)。
|
|
1417
1720
|
* 改路径或载荷形状必须同步更新 test/shared/bridge-contract.test.mjs。
|
|
1418
1721
|
*/
|
|
1419
1722
|
const SETTINGS_BRIDGE_PREFIX = "/api/prompt-tool/settings";
|
|
1420
|
-
/**
|
|
1723
|
+
/** 桥端点路径(相对前缀)。新增端点必须同时登记到契约测试。 */
|
|
1421
1724
|
const BRIDGE_ENDPOINTS = {
|
|
1422
1725
|
meta: "/meta",
|
|
1726
|
+
bootstrap: "/bootstrap",
|
|
1423
1727
|
describe: "/describe",
|
|
1424
1728
|
models: "/models",
|
|
1425
1729
|
mutate: "/mutate",
|
|
@@ -1431,6 +1735,7 @@ const BRIDGE_ENDPOINTS = {
|
|
|
1431
1735
|
importPreset: "/import-preset",
|
|
1432
1736
|
paramOverrides: "/param-overrides",
|
|
1433
1737
|
presetVariables: "/preset-variables",
|
|
1738
|
+
customTools: "/custom-tools",
|
|
1434
1739
|
importPresetPackage: "/import-preset-package",
|
|
1435
1740
|
exportPreset: "/export-preset",
|
|
1436
1741
|
presetDelete: "/preset-delete",
|
|
@@ -1524,6 +1829,7 @@ function restorePresetImport(targetDir, backupDir) {
|
|
|
1524
1829
|
}
|
|
1525
1830
|
/** 自建 loopback settings bridge:替代 registerConfigurableProviders,避免模型设置区出现插件条目。 */
|
|
1526
1831
|
function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngineStrategyDir, ensureRegistered, afterSkillFix, getPresetConfigsDir, afterPresetImport, afterOverridesChange, afterPresetPackageImport) {
|
|
1832
|
+
let invalidateCachedDescriptor = () => {};
|
|
1527
1833
|
ctx.inject(["settings", "webServer"], (sctx) => {
|
|
1528
1834
|
sctx.effect(() => {
|
|
1529
1835
|
let cachedDescriptor;
|
|
@@ -1540,6 +1846,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1540
1846
|
const invalidateDescriptor = () => {
|
|
1541
1847
|
cachedDescriptor = void 0;
|
|
1542
1848
|
};
|
|
1849
|
+
invalidateCachedDescriptor = invalidateDescriptor;
|
|
1543
1850
|
const guard = (req, res) => {
|
|
1544
1851
|
if (!isLoopbackRequest(req)) {
|
|
1545
1852
|
writeBridgeJson(res, 403, {
|
|
@@ -1559,19 +1866,150 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1559
1866
|
}
|
|
1560
1867
|
return true;
|
|
1561
1868
|
};
|
|
1869
|
+
/** 引擎能力矩阵(meta 端点与 /bootstrap 共用):动态 import 引擎 schema。 */
|
|
1870
|
+
const loadEngineMeta = async () => {
|
|
1871
|
+
const { getEngineMeta } = await import(new URL("../engine/schema.mjs", import.meta.url).href);
|
|
1872
|
+
const meta = getEngineMeta();
|
|
1873
|
+
meta.presets = listPresets();
|
|
1874
|
+
meta.builtinTemplates = listBuiltinTemplates();
|
|
1875
|
+
return meta;
|
|
1876
|
+
};
|
|
1877
|
+
/** describe 运行时事实(describe 端点与 /bootstrap 共用):检测状态、技能快照、
|
|
1878
|
+
* 宿主默认模型、模型目录缓存、激活预设参数。不触网(模型目录只读 10min 缓存)。 */
|
|
1879
|
+
const collectDescribeExtras = () => {
|
|
1880
|
+
const detection = getModelsState();
|
|
1881
|
+
const skillsState = getSkillsState();
|
|
1882
|
+
let hostDefaultModel;
|
|
1883
|
+
try {
|
|
1884
|
+
const selection = sctx.settings.get(settingsNamespace("agent-default-model"));
|
|
1885
|
+
if (selection !== null && typeof selection === "object") {
|
|
1886
|
+
const record = selection;
|
|
1887
|
+
hostDefaultModel = {
|
|
1888
|
+
...typeof record.provider === "string" && record.provider.length > 0 ? { provider: record.provider } : {},
|
|
1889
|
+
...typeof record.model === "string" && record.model.length > 0 ? { model: record.model } : {},
|
|
1890
|
+
...typeof record.reasoningEffort === "string" && record.reasoningEffort.length > 0 ? { reasoningEffort: record.reasoningEffort } : {}
|
|
1891
|
+
};
|
|
1892
|
+
if (Object.keys(hostDefaultModel).length === 0) hostDefaultModel = void 0;
|
|
1893
|
+
}
|
|
1894
|
+
} catch {}
|
|
1895
|
+
const modelCatalog = peekModelCatalog();
|
|
1896
|
+
let presetParams = {};
|
|
1897
|
+
let templatePreStepCount = 0;
|
|
1898
|
+
try {
|
|
1899
|
+
const activeDir = getPresetConfigsDir?.() ?? "";
|
|
1900
|
+
const templateName = activeDir.length > 0 ? basename(activeDir) : "anchored";
|
|
1901
|
+
const spec = loadPresetSpec(activeDir.length > 0 ? activeDir : resolvePresetDir(templateName));
|
|
1902
|
+
presetParams = resolvePresetParams(spec, {});
|
|
1903
|
+
if (Array.isArray(spec.promptConfigs)) presetParams.promptConfigs = spec.promptConfigs;
|
|
1904
|
+
templatePreStepCount = (spec.promptConfigs ?? []).filter((config) => {
|
|
1905
|
+
const layer = config.layer;
|
|
1906
|
+
return layer === void 0 || layer === "pre-step";
|
|
1907
|
+
}).length;
|
|
1908
|
+
} catch {
|
|
1909
|
+
templatePreStepCount = 0;
|
|
1910
|
+
}
|
|
1911
|
+
return {
|
|
1912
|
+
presetParams,
|
|
1913
|
+
hostDefaultModel,
|
|
1914
|
+
templatePreStepCount,
|
|
1915
|
+
modelsAvailable: detection.available,
|
|
1916
|
+
providers: detection.providers,
|
|
1917
|
+
modelCatalog,
|
|
1918
|
+
modelsError: detection.error,
|
|
1919
|
+
activeSkillsDirs: skillsState.activeSkillsDirs,
|
|
1920
|
+
skillsDirExists: Object.fromEntries(skillsState.activeSkillsDirs.map((dir) => [dir, existsSync(dir)])),
|
|
1921
|
+
skillCatalog: skillsState.skillCatalog
|
|
1922
|
+
};
|
|
1923
|
+
};
|
|
1924
|
+
/** 激活预设的引擎参数子集(/param-overrides 读取;preset.yml 按 mtime 缓存)。 */
|
|
1925
|
+
const readParamOverrides = (dir) => {
|
|
1926
|
+
try {
|
|
1927
|
+
const spec = loadPresetSpec(dir);
|
|
1928
|
+
const params = {};
|
|
1929
|
+
for (const key of PARAM_KEYS) {
|
|
1930
|
+
if (key === "promptConfigs") continue;
|
|
1931
|
+
if (spec.params !== null && typeof spec.params === "object" && key in spec.params) params[key] = spec.params[key];
|
|
1932
|
+
}
|
|
1933
|
+
return params;
|
|
1934
|
+
} catch {
|
|
1935
|
+
return {};
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
/** 预设级模板变量(/preset-variables 读取)。 */
|
|
1939
|
+
const readPresetVariables = (dir) => {
|
|
1940
|
+
try {
|
|
1941
|
+
const spec = loadPresetSpec(dir);
|
|
1942
|
+
const variables = {};
|
|
1943
|
+
for (const [key, value] of Object.entries(spec.params ?? {})) if (!PARAM_KEYS.has(key) && typeof value === "string") variables[key] = value;
|
|
1944
|
+
for (const [key, value] of Object.entries(spec.variables ?? {})) if (typeof value === "string") variables[key] = value;
|
|
1945
|
+
return {
|
|
1946
|
+
variables,
|
|
1947
|
+
enabled: spec.variablesEnabled !== false
|
|
1948
|
+
};
|
|
1949
|
+
} catch {
|
|
1950
|
+
return {
|
|
1951
|
+
variables: {},
|
|
1952
|
+
enabled: true
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
/** 生成目录实际生效配置(/prompt-configs 读取)。 */
|
|
1957
|
+
const readPromptConfigs = (dir) => {
|
|
1958
|
+
try {
|
|
1959
|
+
return dir.length > 0 ? loadPromptConfigFiles(join(dir, "prompt-configs")) : [];
|
|
1960
|
+
} catch {
|
|
1961
|
+
return [];
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1562
1964
|
const disposers = [
|
|
1965
|
+
sctx.webServer.register({
|
|
1966
|
+
kind: "exact",
|
|
1967
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.bootstrap,
|
|
1968
|
+
handler: async (req, res) => {
|
|
1969
|
+
if (!guard(req, res)) return;
|
|
1970
|
+
ensureRegistered(sctx);
|
|
1971
|
+
const descriptor = findDescriptor();
|
|
1972
|
+
if (descriptor === void 0) {
|
|
1973
|
+
writeBridgeJson(res, 404, {
|
|
1974
|
+
ok: false,
|
|
1975
|
+
code: "settings-not-exposed",
|
|
1976
|
+
message: "prompt-tool settings namespace is not registered"
|
|
1977
|
+
});
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
try {
|
|
1981
|
+
const meta = await loadEngineMeta();
|
|
1982
|
+
const dir = getPresetConfigsDir?.() ?? "";
|
|
1983
|
+
const extras = collectDescribeExtras();
|
|
1984
|
+
writeBridgeJson(res, 200, {
|
|
1985
|
+
ok: true,
|
|
1986
|
+
value: descriptor,
|
|
1987
|
+
meta: { meta },
|
|
1988
|
+
overrides: { overrides: dir.length > 0 ? readParamOverrides(dir) : {} },
|
|
1989
|
+
variables: dir.length > 0 ? readPresetVariables(dir) : {
|
|
1990
|
+
variables: {},
|
|
1991
|
+
enabled: true
|
|
1992
|
+
},
|
|
1993
|
+
promptConfigs: { promptConfigs: readPromptConfigs(dir) },
|
|
1994
|
+
...extras
|
|
1995
|
+
});
|
|
1996
|
+
} catch (error) {
|
|
1997
|
+
writeBridgeJson(res, 500, {
|
|
1998
|
+
ok: false,
|
|
1999
|
+
code: "bootstrap-failed",
|
|
2000
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}),
|
|
1563
2005
|
sctx.webServer.register({
|
|
1564
2006
|
kind: "exact",
|
|
1565
2007
|
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.meta,
|
|
1566
2008
|
handler: async (req, res) => {
|
|
1567
2009
|
if (!guard(req, res)) return;
|
|
1568
|
-
const { getEngineMeta } = await import(new URL("../engine/schema.mjs", import.meta.url).href);
|
|
1569
|
-
const meta = getEngineMeta();
|
|
1570
|
-
meta.presets = listPresets();
|
|
1571
|
-
meta.builtinTemplates = listBuiltinTemplates();
|
|
1572
2010
|
writeBridgeJson(res, 200, {
|
|
1573
2011
|
ok: true,
|
|
1574
|
-
value: { meta }
|
|
2012
|
+
value: { meta: await loadEngineMeta() }
|
|
1575
2013
|
});
|
|
1576
2014
|
}
|
|
1577
2015
|
}),
|
|
@@ -1590,50 +2028,10 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1590
2028
|
});
|
|
1591
2029
|
return;
|
|
1592
2030
|
}
|
|
1593
|
-
const detection = getModelsState();
|
|
1594
|
-
const skillsState = getSkillsState();
|
|
1595
|
-
let hostDefaultModel;
|
|
1596
|
-
try {
|
|
1597
|
-
const selection = sctx.settings.get(settingsNamespace("agent-default-model"));
|
|
1598
|
-
if (selection !== null && typeof selection === "object") {
|
|
1599
|
-
const record = selection;
|
|
1600
|
-
hostDefaultModel = {
|
|
1601
|
-
...typeof record.provider === "string" && record.provider.length > 0 ? { provider: record.provider } : {},
|
|
1602
|
-
...typeof record.model === "string" && record.model.length > 0 ? { model: record.model } : {},
|
|
1603
|
-
...typeof record.reasoningEffort === "string" && record.reasoningEffort.length > 0 ? { reasoningEffort: record.reasoningEffort } : {}
|
|
1604
|
-
};
|
|
1605
|
-
if (Object.keys(hostDefaultModel).length === 0) hostDefaultModel = void 0;
|
|
1606
|
-
}
|
|
1607
|
-
} catch {}
|
|
1608
|
-
const modelCatalog = peekModelCatalog();
|
|
1609
|
-
let presetParams = {};
|
|
1610
|
-
let templatePreStepCount = 0;
|
|
1611
|
-
try {
|
|
1612
|
-
const settingsValue = descriptor.value !== null && typeof descriptor.value === "object" ? descriptor.value : {};
|
|
1613
|
-
const templateName = typeof settingsValue.presetTemplate === "string" && settingsValue.presetTemplate.length > 0 ? settingsValue.presetTemplate : "anchored";
|
|
1614
|
-
const spec = loadPresetSpec(resolvePresetDir(templateName));
|
|
1615
|
-
presetParams = resolvePresetParams(spec, {});
|
|
1616
|
-
if (Array.isArray(spec.promptConfigs)) presetParams.promptConfigs = spec.promptConfigs;
|
|
1617
|
-
templatePreStepCount = (spec.promptConfigs ?? []).filter((config) => {
|
|
1618
|
-
const layer = config.layer;
|
|
1619
|
-
return layer === void 0 || layer === "pre-step";
|
|
1620
|
-
}).length;
|
|
1621
|
-
} catch {
|
|
1622
|
-
templatePreStepCount = 0;
|
|
1623
|
-
}
|
|
1624
2031
|
writeBridgeJson(res, 200, {
|
|
1625
2032
|
ok: true,
|
|
1626
2033
|
value: descriptor,
|
|
1627
|
-
|
|
1628
|
-
hostDefaultModel,
|
|
1629
|
-
templatePreStepCount,
|
|
1630
|
-
modelsAvailable: detection.available,
|
|
1631
|
-
providers: detection.providers,
|
|
1632
|
-
modelCatalog,
|
|
1633
|
-
modelsError: detection.error,
|
|
1634
|
-
activeSkillsDirs: skillsState.activeSkillsDirs,
|
|
1635
|
-
skillsDirExists: Object.fromEntries(skillsState.activeSkillsDirs.map((dir) => [dir, existsSync(dir)])),
|
|
1636
|
-
skillCatalog: skillsState.skillCatalog
|
|
2034
|
+
...collectDescribeExtras()
|
|
1637
2035
|
});
|
|
1638
2036
|
}
|
|
1639
2037
|
}),
|
|
@@ -1716,7 +2114,15 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1716
2114
|
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.configsValidate,
|
|
1717
2115
|
handler: async (req, res) => {
|
|
1718
2116
|
if (!guard(req, res)) return;
|
|
1719
|
-
const { body } = await readBridgeBody(req);
|
|
2117
|
+
const { body, tooLarge } = await readBridgeBody(req, PRESET_PACKAGE_MAX_BYTES);
|
|
2118
|
+
if (tooLarge) {
|
|
2119
|
+
writeBridgeJson(res, 413, {
|
|
2120
|
+
ok: false,
|
|
2121
|
+
code: "configs-too-large",
|
|
2122
|
+
message: `配置载荷超过 ${Math.round(PRESET_PACKAGE_MAX_BYTES / 1024 / 1024)}MB 上限`
|
|
2123
|
+
});
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
1720
2126
|
if (body === null || body === void 0 || typeof body !== "object") {
|
|
1721
2127
|
writeBridgeJson(res, 400, {
|
|
1722
2128
|
ok: false,
|
|
@@ -1815,7 +2221,10 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1815
2221
|
try {
|
|
1816
2222
|
writeBridgeJson(res, 200, {
|
|
1817
2223
|
ok: true,
|
|
1818
|
-
value: {
|
|
2224
|
+
value: {
|
|
2225
|
+
templates: loadPromptTemplates(),
|
|
2226
|
+
toolTemplates: loadToolTemplates()
|
|
2227
|
+
}
|
|
1819
2228
|
});
|
|
1820
2229
|
} catch (error) {
|
|
1821
2230
|
writeBridgeJson(res, 500, {
|
|
@@ -1831,18 +2240,11 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1831
2240
|
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.promptConfigs,
|
|
1832
2241
|
handler: async (req, res) => {
|
|
1833
2242
|
if (!guard(req, res)) return;
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
});
|
|
1840
|
-
} catch {
|
|
1841
|
-
writeBridgeJson(res, 200, {
|
|
1842
|
-
ok: true,
|
|
1843
|
-
value: { promptConfigs: [] }
|
|
1844
|
-
});
|
|
1845
|
-
}
|
|
2243
|
+
const dir = getPresetConfigsDir?.() ?? "";
|
|
2244
|
+
writeBridgeJson(res, 200, {
|
|
2245
|
+
ok: true,
|
|
2246
|
+
value: { promptConfigs: readPromptConfigs(dir) }
|
|
2247
|
+
});
|
|
1846
2248
|
}
|
|
1847
2249
|
}),
|
|
1848
2250
|
sctx.webServer.register({
|
|
@@ -1871,7 +2273,15 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1871
2273
|
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.importPreset,
|
|
1872
2274
|
handler: async (req, res) => {
|
|
1873
2275
|
if (!guard(req, res)) return;
|
|
1874
|
-
const { body } = await readBridgeBody(req);
|
|
2276
|
+
const { body, tooLarge } = await readBridgeBody(req, PRESET_PACKAGE_MAX_BYTES);
|
|
2277
|
+
if (tooLarge) {
|
|
2278
|
+
writeBridgeJson(res, 413, {
|
|
2279
|
+
ok: false,
|
|
2280
|
+
code: "preset-import-too-large",
|
|
2281
|
+
message: `内容资产超过 ${Math.round(PRESET_PACKAGE_MAX_BYTES / 1024 / 1024)}MB 上限`
|
|
2282
|
+
});
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
1875
2285
|
if (body === null || body === void 0 || typeof body !== "object") {
|
|
1876
2286
|
writeBridgeJson(res, 400, {
|
|
1877
2287
|
ok: false,
|
|
@@ -1881,8 +2291,27 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1881
2291
|
return;
|
|
1882
2292
|
}
|
|
1883
2293
|
const record = body;
|
|
1884
|
-
const
|
|
1885
|
-
|
|
2294
|
+
const contents = [];
|
|
2295
|
+
if (Array.isArray(record.contents)) for (const entry of record.contents) {
|
|
2296
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
2297
|
+
const item = entry;
|
|
2298
|
+
contents.push({
|
|
2299
|
+
scope: item.scope === "agents" ? "agents" : "preset",
|
|
2300
|
+
content: typeof item.content === "string" ? item.content : ""
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
else contents.push({
|
|
2304
|
+
scope: record.scope === "agents" ? "agents" : "preset",
|
|
2305
|
+
content: typeof record.content === "string" ? record.content : ""
|
|
2306
|
+
});
|
|
2307
|
+
if (contents.length === 0) {
|
|
2308
|
+
writeBridgeJson(res, 400, {
|
|
2309
|
+
ok: false,
|
|
2310
|
+
code: "settings-rejected",
|
|
2311
|
+
message: "unreadable JSON body"
|
|
2312
|
+
});
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
1886
2315
|
const dir = getPresetConfigsDir?.() ?? "";
|
|
1887
2316
|
if (dir.length === 0) {
|
|
1888
2317
|
writeBridgeJson(res, 400, {
|
|
@@ -1894,11 +2323,11 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1894
2323
|
}
|
|
1895
2324
|
try {
|
|
1896
2325
|
mkdirSync(dir, { recursive: true });
|
|
1897
|
-
writeFileSync(join(dir, scope === "preset" ? "preset.md" : "agents.md"), content, "utf8");
|
|
1898
|
-
afterPresetImport?.(scope);
|
|
2326
|
+
for (const entry of contents) writeFileSync(join(dir, entry.scope === "preset" ? "preset.md" : "agents.md"), entry.content, "utf8");
|
|
2327
|
+
afterPresetImport?.(contents.map((entry) => entry.scope));
|
|
1899
2328
|
writeBridgeJson(res, 200, {
|
|
1900
2329
|
ok: true,
|
|
1901
|
-
value: { scope }
|
|
2330
|
+
value: { scopes: contents.map((entry) => entry.scope) }
|
|
1902
2331
|
});
|
|
1903
2332
|
} catch (error) {
|
|
1904
2333
|
writeBridgeJson(res, 500, {
|
|
@@ -1925,31 +2354,47 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1925
2354
|
}
|
|
1926
2355
|
const presetRoot = dirname(dir);
|
|
1927
2356
|
const templateName = basename(dir);
|
|
1928
|
-
const { body } = await readBridgeBody(req);
|
|
2357
|
+
const { body, tooLarge } = await readBridgeBody(req, PRESET_PACKAGE_MAX_BYTES);
|
|
2358
|
+
if (tooLarge) {
|
|
2359
|
+
writeBridgeJson(res, 413, {
|
|
2360
|
+
ok: false,
|
|
2361
|
+
code: "overrides-too-large",
|
|
2362
|
+
message: `参数载荷超过 ${Math.round(PRESET_PACKAGE_MAX_BYTES / 1024 / 1024)}MB 上限`
|
|
2363
|
+
});
|
|
2364
|
+
return;
|
|
2365
|
+
}
|
|
1929
2366
|
const record = body ?? {};
|
|
1930
2367
|
if (record.overrides === void 0 && record.promptConfigs === void 0) {
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
value: { overrides: {} }
|
|
2368
|
+
writeBridgeJson(res, 200, {
|
|
2369
|
+
ok: true,
|
|
2370
|
+
value: { overrides: readParamOverrides(dir) }
|
|
2371
|
+
});
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
const rawOverrides = record.overrides !== null && typeof record.overrides === "object" && !Array.isArray(record.overrides) ? record.overrides : void 0;
|
|
2375
|
+
if (rawOverrides !== void 0) {
|
|
2376
|
+
const unknownKeys = Object.keys(rawOverrides).filter((key) => key === "promptConfigs" || !PARAM_KEYS.has(key));
|
|
2377
|
+
if (unknownKeys.length > 0) {
|
|
2378
|
+
writeBridgeJson(res, 400, {
|
|
2379
|
+
ok: false,
|
|
2380
|
+
code: "overrides-unknown-key",
|
|
2381
|
+
message: `未知引擎参数键:${unknownKeys.join(", ")}`
|
|
1946
2382
|
});
|
|
2383
|
+
return;
|
|
1947
2384
|
}
|
|
2385
|
+
}
|
|
2386
|
+
const valueErrors = rawOverrides !== void 0 ? validateEngineParamValues(rawOverrides) : [];
|
|
2387
|
+
if (valueErrors.length > 0) {
|
|
2388
|
+
writeBridgeJson(res, 400, {
|
|
2389
|
+
ok: false,
|
|
2390
|
+
code: "overrides-invalid-value",
|
|
2391
|
+
message: valueErrors.map((item) => item.message).join("; ")
|
|
2392
|
+
});
|
|
1948
2393
|
return;
|
|
1949
2394
|
}
|
|
1950
2395
|
try {
|
|
1951
|
-
savePresetParams(presetRoot, templateName,
|
|
1952
|
-
afterOverridesChange?.();
|
|
2396
|
+
savePresetParams(presetRoot, templateName, rawOverrides, Array.isArray(record.promptConfigs) ? record.promptConfigs : void 0);
|
|
2397
|
+
if (record.rebuild !== false) afterOverridesChange?.();
|
|
1953
2398
|
writeBridgeJson(res, 200, {
|
|
1954
2399
|
ok: true,
|
|
1955
2400
|
value: {
|
|
@@ -1985,41 +2430,83 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
1985
2430
|
const { body } = await readBridgeBody(req);
|
|
1986
2431
|
const record = body ?? {};
|
|
1987
2432
|
if (record.variables === void 0 && record.enabled === void 0) {
|
|
2433
|
+
writeBridgeJson(res, 200, {
|
|
2434
|
+
ok: true,
|
|
2435
|
+
value: readPresetVariables(dir)
|
|
2436
|
+
});
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
try {
|
|
2440
|
+
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);
|
|
2441
|
+
afterOverridesChange?.();
|
|
2442
|
+
writeBridgeJson(res, 200, {
|
|
2443
|
+
ok: true,
|
|
2444
|
+
value: { variables: record.variables }
|
|
2445
|
+
});
|
|
2446
|
+
} catch (error) {
|
|
2447
|
+
writeBridgeJson(res, 409, {
|
|
2448
|
+
ok: false,
|
|
2449
|
+
code: "preset-variables-rejected",
|
|
2450
|
+
message: `模板变量保存失败:${error instanceof Error ? error.message : String(error)}`
|
|
2451
|
+
});
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
}),
|
|
2455
|
+
sctx.webServer.register({
|
|
2456
|
+
kind: "exact",
|
|
2457
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.customTools,
|
|
2458
|
+
handler: async (req, res) => {
|
|
2459
|
+
if (!guard(req, res)) return;
|
|
2460
|
+
const dir = getPresetConfigsDir?.() ?? "";
|
|
2461
|
+
if (dir.length === 0) {
|
|
2462
|
+
writeBridgeJson(res, 400, {
|
|
2463
|
+
ok: false,
|
|
2464
|
+
code: "preset-dir-unavailable",
|
|
2465
|
+
message: "presetDir 未配置"
|
|
2466
|
+
});
|
|
2467
|
+
return;
|
|
2468
|
+
}
|
|
2469
|
+
const { body } = await readBridgeBody(req);
|
|
2470
|
+
const record = body ?? {};
|
|
2471
|
+
if (record.customTools === void 0) {
|
|
1988
2472
|
try {
|
|
1989
2473
|
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
2474
|
writeBridgeJson(res, 200, {
|
|
1994
2475
|
ok: true,
|
|
1995
|
-
value: {
|
|
1996
|
-
variables,
|
|
1997
|
-
enabled: spec.variablesEnabled !== false
|
|
1998
|
-
}
|
|
2476
|
+
value: { customTools: Array.isArray(spec.customTools) ? spec.customTools : [] }
|
|
1999
2477
|
});
|
|
2000
2478
|
} catch {
|
|
2001
2479
|
writeBridgeJson(res, 200, {
|
|
2002
2480
|
ok: true,
|
|
2003
|
-
value: {
|
|
2004
|
-
variables: {},
|
|
2005
|
-
enabled: true
|
|
2006
|
-
}
|
|
2481
|
+
value: { customTools: [] }
|
|
2007
2482
|
});
|
|
2008
2483
|
}
|
|
2009
2484
|
return;
|
|
2010
2485
|
}
|
|
2011
2486
|
try {
|
|
2012
|
-
|
|
2487
|
+
const customTools = record.customTools;
|
|
2488
|
+
if (!Array.isArray(customTools)) {
|
|
2489
|
+
writeBridgeJson(res, 400, {
|
|
2490
|
+
ok: false,
|
|
2491
|
+
code: "custom-tools-invalid",
|
|
2492
|
+
message: "customTools 必须是数组"
|
|
2493
|
+
});
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
withPresetDoc(dir, (doc) => {
|
|
2497
|
+
if (customTools.length === 0) doc.deleteIn(["customTools"]);
|
|
2498
|
+
else doc.setIn(["customTools"], customTools);
|
|
2499
|
+
});
|
|
2013
2500
|
afterOverridesChange?.();
|
|
2014
2501
|
writeBridgeJson(res, 200, {
|
|
2015
2502
|
ok: true,
|
|
2016
|
-
value: {
|
|
2503
|
+
value: { customTools }
|
|
2017
2504
|
});
|
|
2018
2505
|
} catch (error) {
|
|
2019
2506
|
writeBridgeJson(res, 409, {
|
|
2020
2507
|
ok: false,
|
|
2021
|
-
code: "
|
|
2022
|
-
message:
|
|
2508
|
+
code: "custom-tools-rejected",
|
|
2509
|
+
message: `自定义工具保存失败:${error instanceof Error ? error.message : String(error)}`
|
|
2023
2510
|
});
|
|
2024
2511
|
}
|
|
2025
2512
|
}
|
|
@@ -2583,6 +3070,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
2583
3070
|
};
|
|
2584
3071
|
}, "prompt-tool: settings bridge");
|
|
2585
3072
|
});
|
|
3073
|
+
return { invalidateDescriptor: () => invalidateCachedDescriptor() };
|
|
2586
3074
|
}
|
|
2587
3075
|
//#endregion
|
|
2588
3076
|
//#region src/runtime/character-tools.ts
|
|
@@ -2590,9 +3078,11 @@ const text$2 = (text) => [{
|
|
|
2590
3078
|
type: "text",
|
|
2591
3079
|
text
|
|
2592
3080
|
}];
|
|
3081
|
+
/** 注册角色卡库模型工具;返回 disposer(预设切换重挂用)。enabled=false 返回空操作。 */
|
|
2593
3082
|
function registerCharacterTools(ctx, host) {
|
|
2594
|
-
ctx.inject(["tools"], (toolsCtx) => {
|
|
2595
|
-
|
|
3083
|
+
const fiber = ctx.inject(["tools"], (toolsCtx) => {
|
|
3084
|
+
const disposers = [];
|
|
3085
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2596
3086
|
name: "character_list",
|
|
2597
3087
|
description: "列出 SillyTavern 角色卡库:每张卡(id / 名称 / 描述 / 是否已导入当前预设)。导入角色卡、应用到当前预设或移除前先调用本工具获取 id。",
|
|
2598
3088
|
parameters: {},
|
|
@@ -2627,8 +3117,8 @@ function registerCharacterTools(ctx, host) {
|
|
|
2627
3117
|
render: (_args, value) => text$2(JSON.stringify(value.characters))
|
|
2628
3118
|
},
|
|
2629
3119
|
execute: async () => ({ characters: listCharacterCards(host.presetRoot(), host.templateName()) })
|
|
2630
|
-
}));
|
|
2631
|
-
toolsCtx.tools.register(defineTool({
|
|
3120
|
+
})));
|
|
3121
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2632
3122
|
name: "character_import",
|
|
2633
3123
|
description: "导入 SillyTavern 角色卡(chara_card_v2/v3 JSON)到角色卡库:接收角色卡 JSON 文本内容(可先读取文件)。PNG 角色卡请让用户从 UI 角色管理页导入。导入后需调用 character_apply 应用到当前预设。",
|
|
2634
3124
|
parameters: {
|
|
@@ -2671,8 +3161,8 @@ function registerCharacterTools(ctx, host) {
|
|
|
2671
3161
|
name: result.name
|
|
2672
3162
|
};
|
|
2673
3163
|
}
|
|
2674
|
-
}));
|
|
2675
|
-
toolsCtx.tools.register(defineTool({
|
|
3164
|
+
})));
|
|
3165
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2676
3166
|
name: "character_apply",
|
|
2677
3167
|
description: "把角色卡库中一张角色卡的参数(角色设定 / 系统提示 / 开场白 / 世界书 / 提示词库 / 采样参数)合并进当前激活预设(promptConfigs 带 chara-<id>- 前缀防冲突,params 合并,meta.importedCharacters 记录),并立即重建生成目录。重复应用幂等。",
|
|
2678
3168
|
parameters: { id: {
|
|
@@ -2706,8 +3196,8 @@ function registerCharacterTools(ctx, host) {
|
|
|
2706
3196
|
count: result.count
|
|
2707
3197
|
};
|
|
2708
3198
|
}
|
|
2709
|
-
}));
|
|
2710
|
-
toolsCtx.tools.register(defineTool({
|
|
3199
|
+
})));
|
|
3200
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2711
3201
|
name: "character_remove",
|
|
2712
3202
|
description: "从当前激活预设移除一张已导入角色卡的参数(删 chara-<id>- 前缀配置、该卡声明的 params 键、meta.importedCharacters 除名),并立即重建生成目录。角色卡库条目不受影响。",
|
|
2713
3203
|
parameters: { id: {
|
|
@@ -2741,8 +3231,8 @@ function registerCharacterTools(ctx, host) {
|
|
|
2741
3231
|
count: result.count
|
|
2742
3232
|
};
|
|
2743
3233
|
}
|
|
2744
|
-
}));
|
|
2745
|
-
toolsCtx.tools.register(defineTool({
|
|
3234
|
+
})));
|
|
3235
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2746
3236
|
name: "character_delete",
|
|
2747
3237
|
description: "从角色卡库删除一张角色卡(含其转换参数与头像)。已导入当前预设的参数不受影响(如需清理请先调用 character_remove)。",
|
|
2748
3238
|
parameters: { id: {
|
|
@@ -2766,8 +3256,14 @@ function registerCharacterTools(ctx, host) {
|
|
|
2766
3256
|
if (!result.ok) throw new Error(result.message);
|
|
2767
3257
|
return { id: args.id };
|
|
2768
3258
|
}
|
|
2769
|
-
}));
|
|
3259
|
+
})));
|
|
3260
|
+
return () => {
|
|
3261
|
+
for (const dispose of disposers) dispose();
|
|
3262
|
+
};
|
|
2770
3263
|
});
|
|
3264
|
+
return () => {
|
|
3265
|
+
fiber.dispose();
|
|
3266
|
+
};
|
|
2771
3267
|
}
|
|
2772
3268
|
//#endregion
|
|
2773
3269
|
//#region src/runtime/world-book-tools.ts
|
|
@@ -2775,26 +3271,6 @@ const text$1 = (text) => [{
|
|
|
2775
3271
|
type: "text",
|
|
2776
3272
|
text
|
|
2777
3273
|
}];
|
|
2778
|
-
function readConfigs(dir) {
|
|
2779
|
-
const file = join(dir, "preset.yml");
|
|
2780
|
-
const current = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" }).toJS();
|
|
2781
|
-
return Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
2782
|
-
}
|
|
2783
|
-
function writeConfigs(dir, configs) {
|
|
2784
|
-
const file = join(dir, "preset.yml");
|
|
2785
|
-
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
2786
|
-
doc.setIn(["promptConfigs"], configs);
|
|
2787
|
-
writeFileSync(file, doc.toString(), "utf8");
|
|
2788
|
-
}
|
|
2789
|
-
/** 追加记忆文件(角色卡 memory.md 或预设 memory.md)。 */
|
|
2790
|
-
function appendMemory(file, note) {
|
|
2791
|
-
const content = note.trim();
|
|
2792
|
-
if (content.length === 0) return;
|
|
2793
|
-
mkdirSync(join(file, ".."), { recursive: true });
|
|
2794
|
-
const line = `\n- [${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ")}] ${content}\n`;
|
|
2795
|
-
if (existsSync(file)) appendFileSync(file, line, "utf8");
|
|
2796
|
-
else writeFileSync(file, `# 本地记忆\n${line}`, "utf8");
|
|
2797
|
-
}
|
|
2798
3274
|
/** 从条目 id 解析来源角色卡(chara-<cardId>- 前缀,cardId 可含连字符:遍历库目录取最长匹配)。 */
|
|
2799
3275
|
function sourceCardId(presetRoot, entryId) {
|
|
2800
3276
|
const root = join(presetRoot, ".characters");
|
|
@@ -2808,16 +3284,22 @@ function sourceCardId(presetRoot, entryId) {
|
|
|
2808
3284
|
} catch {}
|
|
2809
3285
|
return best;
|
|
2810
3286
|
}
|
|
3287
|
+
/** 注册世界书条目级模型工具;返回 disposer(预设切换重挂用)。enabled=false 返回空操作。 */
|
|
2811
3288
|
function registerWorldBookTools(ctx, host) {
|
|
2812
|
-
ctx.inject(["tools"], (toolsCtx) => {
|
|
3289
|
+
const fiber = ctx.inject(["tools"], (toolsCtx) => {
|
|
3290
|
+
const disposers = [];
|
|
2813
3291
|
/** note 归属写入:角色卡条目 → 卡记忆;其他 → 预设记忆。 */
|
|
2814
3292
|
const writeNote = (entryId, note) => {
|
|
2815
3293
|
if (note === void 0 || note.trim().length === 0) return;
|
|
2816
3294
|
const cardId = entryId !== void 0 ? sourceCardId(host.presetRoot(), entryId) : void 0;
|
|
2817
|
-
if (cardId !== void 0)
|
|
2818
|
-
|
|
3295
|
+
if (cardId !== void 0) {
|
|
3296
|
+
appendMemoryFile(join(host.presetRoot(), ".characters", cardId, "memory.md"), note, "# 角色记忆");
|
|
3297
|
+
try {
|
|
3298
|
+
syncImportedCharacterMemory(host.presetRoot(), basename(host.activeDir()), cardId);
|
|
3299
|
+
} catch {}
|
|
3300
|
+
} else appendMemoryFile(join(host.activeDir(), "memory.md"), note, "# 本地记忆");
|
|
2819
3301
|
};
|
|
2820
|
-
toolsCtx.tools.register(defineTool({
|
|
3302
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2821
3303
|
name: "world_book_list",
|
|
2822
3304
|
description: "列出当前预设的世界书条目(world-book 策略配置:id / 名称 / 关键字 / 常驻 / 启用)。世界书 = 上下文条目:无 keys 的全局条目每次注入,有 keys 条目命中聊天内容才注入。增删改前先调用本工具获取 id。",
|
|
2823
3305
|
parameters: {},
|
|
@@ -2853,7 +3335,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2853
3335
|
render: (_args, value) => text$1(JSON.stringify(value.entries))
|
|
2854
3336
|
},
|
|
2855
3337
|
execute: async () => {
|
|
2856
|
-
return { entries:
|
|
3338
|
+
return { entries: listWorldBookEntries(host.activeDir()).map((config) => ({
|
|
2857
3339
|
id: String(config.id ?? ""),
|
|
2858
3340
|
name: String(config.name ?? config.id ?? ""),
|
|
2859
3341
|
keys: Array.isArray(config.params && config.params.keys) ? config.params.keys : void 0,
|
|
@@ -2861,8 +3343,8 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2861
3343
|
enabled: config.enabled !== false
|
|
2862
3344
|
})) };
|
|
2863
3345
|
}
|
|
2864
|
-
}));
|
|
2865
|
-
toolsCtx.tools.register(defineTool({
|
|
3346
|
+
})));
|
|
3347
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2866
3348
|
name: "world_book_upsert",
|
|
2867
3349
|
description: "新增或更新当前预设的一条世界书条目(world-book 策略配置):按 id 更新(不存在则新增,id 自动生成 lore-<n>)。constant=true 常驻注入;否则命中 keys(或 secondaryKeys)任一关键字注入;无 keys 条目按全局每次注入。note 可选:写入来源角色卡的持久记忆(memory.md,条目 id 带 chara-<卡>- 前缀时)或预设记忆——持久记忆跨会话跟随角色卡(与 session_var 会话变量的临时状态不同,适合长期关系记录)。写盘后立即重建生成目录。",
|
|
2868
3350
|
parameters: {
|
|
@@ -2926,37 +3408,29 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2926
3408
|
},
|
|
2927
3409
|
execute: async (args) => {
|
|
2928
3410
|
const dir = host.activeDir();
|
|
2929
|
-
const configs = readConfigs(dir);
|
|
2930
3411
|
const targetId = args.id !== void 0 && args.id.length > 0 ? args.id : `lore-${Date.now().toString(36)}`;
|
|
2931
|
-
const
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
params
|
|
2945
|
-
};
|
|
2946
|
-
if (args.enabled === false) entry.enabled = false;
|
|
2947
|
-
const existing = configs.findIndex((config) => String(config.id ?? "") === targetId);
|
|
2948
|
-
if (existing >= 0) configs[existing] = entry;
|
|
2949
|
-
else configs.push(entry);
|
|
2950
|
-
writeConfigs(dir, configs);
|
|
3412
|
+
const count = upsertWorldBookEntry(dir, {
|
|
3413
|
+
...buildWorldBookEntry({
|
|
3414
|
+
id: targetId,
|
|
3415
|
+
name: args.name,
|
|
3416
|
+
text: args.content,
|
|
3417
|
+
order: typeof args.order === "number" ? args.order : void 0,
|
|
3418
|
+
enabled: args.enabled === false ? false : void 0,
|
|
3419
|
+
constant: args.constant === true,
|
|
3420
|
+
keys: Array.isArray(args.keys) && args.keys.length > 0 ? args.keys : void 0,
|
|
3421
|
+
secondaryKeys: Array.isArray(args.secondaryKeys) && args.secondaryKeys.length > 0 ? args.secondaryKeys : void 0
|
|
3422
|
+
}),
|
|
3423
|
+
id: targetId
|
|
3424
|
+
});
|
|
2951
3425
|
writeNote(targetId, typeof args.note === "string" ? args.note : "");
|
|
2952
3426
|
host.rebuild();
|
|
2953
3427
|
return {
|
|
2954
3428
|
id: targetId,
|
|
2955
|
-
count
|
|
3429
|
+
count
|
|
2956
3430
|
};
|
|
2957
3431
|
}
|
|
2958
|
-
}));
|
|
2959
|
-
toolsCtx.tools.register(defineTool({
|
|
3432
|
+
})));
|
|
3433
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
2960
3434
|
name: "world_book_delete",
|
|
2961
3435
|
description: "删除当前预设的一条世界书条目(world_book_list 获取 id)。note 可选:写入来源角色卡持久记忆(memory.md,跨会话跟随角色卡)或预设记忆。删除后立即重建生成目录。",
|
|
2962
3436
|
parameters: {
|
|
@@ -2988,20 +3462,22 @@ function registerWorldBookTools(ctx, host) {
|
|
|
2988
3462
|
render: (_args, value) => text$1(`世界书条目 ${value.id} 已删除(剩余 ${value.count} 条),生成目录已重建。`)
|
|
2989
3463
|
},
|
|
2990
3464
|
execute: async (args) => {
|
|
2991
|
-
const
|
|
2992
|
-
const configs = readConfigs(dir);
|
|
2993
|
-
const kept = configs.filter((config) => !(config.strategy === "world-book" && String(config.id ?? "") === args.id));
|
|
2994
|
-
if (kept.length === configs.length) throw new Error(`世界书条目 ${args.id} 不存在`);
|
|
2995
|
-
writeConfigs(dir, kept);
|
|
3465
|
+
const keptCount = deleteWorldBookEntry(host.activeDir(), args.id);
|
|
2996
3466
|
writeNote(args.id, typeof args.note === "string" ? args.note : "");
|
|
2997
3467
|
host.rebuild();
|
|
2998
3468
|
return {
|
|
2999
3469
|
id: args.id,
|
|
3000
|
-
count:
|
|
3470
|
+
count: keptCount
|
|
3001
3471
|
};
|
|
3002
3472
|
}
|
|
3003
|
-
}));
|
|
3473
|
+
})));
|
|
3474
|
+
return () => {
|
|
3475
|
+
for (const dispose of disposers) dispose();
|
|
3476
|
+
};
|
|
3004
3477
|
});
|
|
3478
|
+
return () => {
|
|
3479
|
+
fiber.dispose();
|
|
3480
|
+
};
|
|
3005
3481
|
}
|
|
3006
3482
|
//#endregion
|
|
3007
3483
|
//#region src/runtime/session-var-tools.ts
|
|
@@ -3009,9 +3485,11 @@ const text = (value) => [{
|
|
|
3009
3485
|
type: "text",
|
|
3010
3486
|
text: value
|
|
3011
3487
|
}];
|
|
3488
|
+
/** 注册会话变量模型工具;返回 disposer(预设切换重挂用)。enabled=false 返回空操作。 */
|
|
3012
3489
|
function registerSessionVarTools(ctx) {
|
|
3013
|
-
ctx.inject(["tools"], (toolsCtx) => {
|
|
3014
|
-
|
|
3490
|
+
const fiber = ctx.inject(["tools"], (toolsCtx) => {
|
|
3491
|
+
const disposers = [];
|
|
3492
|
+
disposers.push(toolsCtx.tools.register(defineTool({
|
|
3015
3493
|
name: "session_var",
|
|
3016
3494
|
description: "会话变量管理(SillyTavern setvar/getvar 语义):list 查看当前会话全部变量、get 读取、set 设置、clear 清除。提示词/世界书文本中的 {{变量名}} 会在注入时替换为会话变量值(会话级覆盖预设默认值);适合维护角色状态(如 {{心情}}、{{接受度}} 等)。注意:会话变量仅存于当前会话(结束即失);跨会话长期记忆请用 world_book 工具的 note 参数写入角色卡记忆(持久,跟随角色卡)。",
|
|
3017
3495
|
parameters: {
|
|
@@ -3106,8 +3584,14 @@ function registerSessionVarTools(ctx) {
|
|
|
3106
3584
|
error: `未知 action: ${action}`
|
|
3107
3585
|
};
|
|
3108
3586
|
}
|
|
3109
|
-
}));
|
|
3587
|
+
})));
|
|
3588
|
+
return () => {
|
|
3589
|
+
for (const dispose of disposers) dispose();
|
|
3590
|
+
};
|
|
3110
3591
|
});
|
|
3592
|
+
return () => {
|
|
3593
|
+
fiber.dispose();
|
|
3594
|
+
};
|
|
3111
3595
|
}
|
|
3112
3596
|
//#endregion
|
|
3113
3597
|
//#region src/runtime/tui.ts
|
|
@@ -3121,6 +3605,7 @@ const TUI_GLOBAL_SWITCHES = [
|
|
|
3121
3605
|
const TUI_PARAM_SWITCHES = [
|
|
3122
3606
|
["injectPrompt", "锚定确认后注入 preset.md"],
|
|
3123
3607
|
["firstTurnAnchor", "追加任务引导"],
|
|
3608
|
+
["guideEnabled", "每轮引导(独立开关;缺省跟随锚定)"],
|
|
3124
3609
|
["firstTurnCustom", "使用自定义引导(首句)"],
|
|
3125
3610
|
["guideCustom", "使用自定义引导(每轮)"],
|
|
3126
3611
|
["usePtcMode", "使用 PTC 模式"]
|
|
@@ -3291,18 +3776,56 @@ function parseTuiBoolean(token, current) {
|
|
|
3291
3776
|
if (token === "off") return false;
|
|
3292
3777
|
if (token === "toggle") return !current;
|
|
3293
3778
|
}
|
|
3779
|
+
function asCommandAction(token) {
|
|
3780
|
+
return token === "on" || token === "off" || token === "toggle" ? token : void 0;
|
|
3781
|
+
}
|
|
3782
|
+
/**
|
|
3783
|
+
* 解析可含空格的标识符与末尾动作。
|
|
3784
|
+
*
|
|
3785
|
+
* 斜杠命令没有 shell 引号语义,因此动作只认“最后一个独立 token”。config 的
|
|
3786
|
+
* 详情形态没有动作;只有去掉末尾动作后的候选 id 已存在时才把末尾 token 视为
|
|
3787
|
+
* 动作,避免把普通 id 误拆。
|
|
3788
|
+
*/
|
|
3789
|
+
function parseIdentifierAndAction(tokens, exists) {
|
|
3790
|
+
if (tokens.length === 0) return { id: "" };
|
|
3791
|
+
const last = tokens[tokens.length - 1];
|
|
3792
|
+
const action = asCommandAction(last ?? "");
|
|
3793
|
+
if (action !== void 0 && tokens.length >= 2) {
|
|
3794
|
+
const id = tokens.slice(0, -1).join(" ");
|
|
3795
|
+
if (exists(id)) return {
|
|
3796
|
+
id,
|
|
3797
|
+
action
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
return { id: tokens.join(" ") };
|
|
3801
|
+
}
|
|
3294
3802
|
/** 通过 DSH 命令注册表暴露 /prompt-tool,Web 与 dsh-tui 都能执行。 */
|
|
3295
3803
|
function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog, getPresetConfigsDir, savePresetParam) {
|
|
3296
3804
|
ctx.inject(["settings"], (sctx) => {
|
|
3297
3805
|
return sctx.commands.register({
|
|
3298
3806
|
name: "prompt-tool",
|
|
3299
3807
|
description: "提示词工具:查看或切换本插件开关",
|
|
3300
|
-
input: { hint: "status | on/off/toggle <开关> | skill <目录名> on/off | config <id> [on/off/toggle]" },
|
|
3808
|
+
input: { hint: "status | on/off/toggle <开关> | skill <目录名> on/off/toggle | config <id> [on/off/toggle]" },
|
|
3301
3809
|
handler: async (invocation) => {
|
|
3302
3810
|
const usage = () => ({
|
|
3303
3811
|
kind: "error",
|
|
3304
|
-
text: "用法:/prompt-tool status\n /prompt-tool on|off|toggle <writeAgents|writePreset|injectPrompt|injectAgentsPrompt|firstTurnAnchor|firstTurnCustom|guideCustom|usePtcMode>\n /prompt-tool skill <技能目录名> on|off|toggle
|
|
3812
|
+
text: "用法:/prompt-tool status\n /prompt-tool on|off|toggle <writeAgents|writePreset|injectPrompt|injectAgentsPrompt|firstTurnAnchor|firstTurnCustom|guideEnabled|guideCustom|usePtcMode>\n /prompt-tool skill <技能目录名> on|off|toggle(目录名可含空格)\n /prompt-tool config <id>(id 可含空格)\n /prompt-tool config <id> on|off|toggle\n /prompt-tool bootstrapMaxTokens <正整数|0(关闭)>"
|
|
3305
3813
|
});
|
|
3814
|
+
const persistPresetParam = async (key, value) => {
|
|
3815
|
+
if (savePresetParam === void 0) return {
|
|
3816
|
+
kind: "error",
|
|
3817
|
+
text: `无法保存 ${key}:预设参数保存回调不可用`
|
|
3818
|
+
};
|
|
3819
|
+
try {
|
|
3820
|
+
await savePresetParam(key, value);
|
|
3821
|
+
return;
|
|
3822
|
+
} catch (error) {
|
|
3823
|
+
return {
|
|
3824
|
+
kind: "error",
|
|
3825
|
+
text: `保存 ${key} 失败:${error instanceof Error ? error.message : String(error)}`
|
|
3826
|
+
};
|
|
3827
|
+
}
|
|
3828
|
+
};
|
|
3306
3829
|
const tokens = invocation.rawInput.trim().split(/\s+/).filter((token) => token.length > 0);
|
|
3307
3830
|
const source = getSource();
|
|
3308
3831
|
const presetDir = getPresetConfigsDir?.();
|
|
@@ -3320,10 +3843,9 @@ function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog,
|
|
|
3320
3843
|
};
|
|
3321
3844
|
}
|
|
3322
3845
|
if (tokens[0] === "skill") {
|
|
3323
|
-
const folder = tokens
|
|
3324
|
-
if (folder ===
|
|
3325
|
-
const
|
|
3326
|
-
const next = parseTuiBoolean(tokens[2], current);
|
|
3846
|
+
const { id: folder, action } = parseIdentifierAndAction(tokens.slice(1), () => true);
|
|
3847
|
+
if (folder.length === 0) return usage();
|
|
3848
|
+
const next = parseTuiBoolean(action, source.skillSwitches[folder] !== false);
|
|
3327
3849
|
if (next === void 0) return usage();
|
|
3328
3850
|
await sctx.settings.mutate(ns, [{
|
|
3329
3851
|
op: "set",
|
|
@@ -3338,25 +3860,24 @@ ${renderTuiStatus(getSource(), readPresetParams(getPresetConfigsDir?.()), resolv
|
|
|
3338
3860
|
};
|
|
3339
3861
|
}
|
|
3340
3862
|
if (tokens[0] === "config") {
|
|
3341
|
-
const id = tokens
|
|
3342
|
-
if (id ===
|
|
3863
|
+
const { id, action } = parseIdentifierAndAction(tokens.slice(1), (candidate) => promptConfigs.some((config) => config.id === candidate));
|
|
3864
|
+
if (id.length === 0) return usage();
|
|
3343
3865
|
const current = promptConfigs.find((config) => config.id === id);
|
|
3344
3866
|
if (current === void 0) return {
|
|
3345
3867
|
kind: "error",
|
|
3346
3868
|
text: `未找到提示词配置 ${id};用 /prompt-tool status 查看全部 id。`
|
|
3347
3869
|
};
|
|
3348
|
-
const action = tokens[2];
|
|
3349
3870
|
if (action === void 0) return {
|
|
3350
3871
|
kind: "success",
|
|
3351
3872
|
text: renderConfigDetail(source, id, promptConfigs)
|
|
3352
3873
|
};
|
|
3353
3874
|
const next = parseTuiBoolean(action, current.enabled !== false);
|
|
3354
3875
|
if (next === void 0) return usage();
|
|
3355
|
-
const
|
|
3876
|
+
const failure = await persistPresetParam("promptConfigs", promptConfigs.map((config) => config.id === id ? {
|
|
3356
3877
|
...config,
|
|
3357
3878
|
enabled: next
|
|
3358
|
-
} : config);
|
|
3359
|
-
|
|
3879
|
+
} : config));
|
|
3880
|
+
if (failure !== void 0) return failure;
|
|
3360
3881
|
return {
|
|
3361
3882
|
kind: "success",
|
|
3362
3883
|
text: `已把提示词配置 ${id} 设为 ${next ? "开" : "关"}
|
|
@@ -3371,7 +3892,8 @@ ${renderConfigDetail(getSource(), id, resolvePromptConfigs(getPresetConfigsDir?.
|
|
|
3371
3892
|
kind: "error",
|
|
3372
3893
|
text: "bootstrapMaxTokens 需要非负整数:0 关闭封顶,正整数设置首轮 maxTokens。"
|
|
3373
3894
|
};
|
|
3374
|
-
|
|
3895
|
+
const failure = await persistPresetParam("bootstrapMaxTokens", value);
|
|
3896
|
+
if (failure !== void 0) return failure;
|
|
3375
3897
|
return {
|
|
3376
3898
|
kind: "success",
|
|
3377
3899
|
text: `已把 bootstrapMaxTokens 设为 ${value === 0 ? "关闭(不设封顶)" : String(value)}
|
|
@@ -3396,7 +3918,10 @@ ${renderTuiStatus(getSource(), readPresetParams(getPresetConfigsDir?.()), resolv
|
|
|
3396
3918
|
path: [key],
|
|
3397
3919
|
value: next
|
|
3398
3920
|
}]);
|
|
3399
|
-
else
|
|
3921
|
+
else {
|
|
3922
|
+
const failure = await persistPresetParam(key, next);
|
|
3923
|
+
if (failure !== void 0) return failure;
|
|
3924
|
+
}
|
|
3400
3925
|
return {
|
|
3401
3926
|
kind: "success",
|
|
3402
3927
|
text: `已把 ${key} 设为 ${next ? "开" : "关"}
|
|
@@ -3547,8 +4072,9 @@ function runtimeOf(options, prompt) {
|
|
|
3547
4072
|
firstTurnText: typeof options.firstTurnText === "string" ? options.firstTurnText : "",
|
|
3548
4073
|
guideCustom: options.guideCustom === true,
|
|
3549
4074
|
guideText: typeof options.guideText === "string" ? options.guideText : "",
|
|
4075
|
+
guideEnabled: typeof options.guideEnabled === "boolean" ? options.guideEnabled : void 0,
|
|
3550
4076
|
injectPrompt: options.injectPrompt !== false,
|
|
3551
|
-
usePtcMode: options.usePtcMode
|
|
4077
|
+
usePtcMode: typeof options.usePtcMode === "boolean" ? options.usePtcMode : void 0,
|
|
3552
4078
|
bootstrapMaxTokens: Number.isSafeInteger(options.bootstrapMaxTokens) ? options.bootstrapMaxTokens : 0,
|
|
3553
4079
|
modelProvider: typeof options.modelProvider === "string" && options.modelProvider.length > 0 ? options.modelProvider : "",
|
|
3554
4080
|
modelName: typeof options.modelName === "string" && options.modelName.length > 0 ? options.modelName : "",
|
|
@@ -3560,12 +4086,19 @@ function runtimeOf(options, prompt) {
|
|
|
3560
4086
|
subagentReasoningEffort: typeof options.subagentReasoningEffort === "string" && options.subagentReasoningEffort.length > 0 ? options.subagentReasoningEffort : void 0,
|
|
3561
4087
|
subagentTemperature: typeof options.subagentTemperature === "string" && options.subagentTemperature.length > 0 ? options.subagentTemperature : void 0,
|
|
3562
4088
|
subagentMaxTokens: typeof options.subagentMaxTokens === "string" && options.subagentMaxTokens.length > 0 ? options.subagentMaxTokens : void 0,
|
|
3563
|
-
subagentPersona: typeof options.subagentPersona === "string" && options.subagentPersona.length > 0 ? options.subagentPersona : "",
|
|
3564
4089
|
toolFilterAllow: options.toolFilterAllow !== void 0 && (Array.isArray(options.toolFilterAllow) ? options.toolFilterAllow.length > 0 : String(options.toolFilterAllow).trim().length > 0) ? options.toolFilterAllow : void 0,
|
|
3565
4090
|
toolFilterDeny: options.toolFilterDeny !== void 0 && (Array.isArray(options.toolFilterDeny) ? options.toolFilterDeny.length > 0 : String(options.toolFilterDeny).trim().length > 0) ? options.toolFilterDeny : void 0,
|
|
3566
4091
|
maxDepth: options.maxDepth,
|
|
3567
4092
|
allowKinds: options.allowKinds,
|
|
3568
|
-
firstTurnWord: typeof options.firstTurnWord === "string" && options.firstTurnWord.length > 0 ? options.firstTurnWord : void 0
|
|
4093
|
+
firstTurnWord: typeof options.firstTurnWord === "string" && options.firstTurnWord.length > 0 ? options.firstTurnWord : void 0,
|
|
4094
|
+
anchorTurn: typeof options.anchorTurn === "boolean" ? options.anchorTurn : void 0,
|
|
4095
|
+
anchorTurnText: typeof options.anchorTurnText === "string" ? options.anchorTurnText : void 0,
|
|
4096
|
+
deliberationGate: typeof options.deliberationGate === "boolean" ? options.deliberationGate : void 0,
|
|
4097
|
+
deliberationMinChars: Number.isSafeInteger(options.deliberationMinChars) ? options.deliberationMinChars : void 0,
|
|
4098
|
+
deliberationMaxGatesPerTurn: Number.isSafeInteger(options.deliberationMaxGatesPerTurn) ? options.deliberationMaxGatesPerTurn : void 0,
|
|
4099
|
+
cotDrip: typeof options.cotDrip === "boolean" ? options.cotDrip : void 0,
|
|
4100
|
+
cotDripEvery: Number.isSafeInteger(options.cotDripEvery) ? options.cotDripEvery : void 0,
|
|
4101
|
+
cotDripMaxPerTurn: Number.isSafeInteger(options.cotDripMaxPerTurn) ? options.cotDripMaxPerTurn : void 0
|
|
3569
4102
|
};
|
|
3570
4103
|
}
|
|
3571
4104
|
/** 模型参数(思维程度/温度/输出上限)→ agent-request 提示词配置(官方 LlmCallConfig patch 浅合并)。
|
|
@@ -3609,6 +4142,8 @@ function writePreset(prompt, options) {
|
|
|
3609
4142
|
const presetDir = options.presetDir.trim().length > 0 ? options.presetDir : DEFAULT_PRESET_DIR;
|
|
3610
4143
|
const templateName = typeof options.presetTemplate === "string" && options.presetTemplate.trim().length > 0 ? options.presetTemplate.trim() : "anchored";
|
|
3611
4144
|
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-]*$/`);
|
|
4145
|
+
const outputId = typeof options.outputId === "string" && options.outputId.trim().length > 0 ? options.outputId.trim() : templateName;
|
|
4146
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(outputId)) throw new Error(`invalid outputId ${JSON.stringify(outputId)}: must match official agent-presets id /^[a-z0-9][a-z0-9-]*$/`);
|
|
3612
4147
|
const templateDir = resolvePresetDir(templateName);
|
|
3613
4148
|
const spec = loadPresetSpec(templateDir);
|
|
3614
4149
|
const worldBook = spec.worldBook;
|
|
@@ -3638,8 +4173,7 @@ function writePreset(prompt, options) {
|
|
|
3638
4173
|
...keys.length > 0 ? { keys } : {},
|
|
3639
4174
|
...secondaryKeys.length > 0 ? { secondaryKeys } : {},
|
|
3640
4175
|
...entry.caseSensitive === true ? { caseSensitive: true } : {},
|
|
3641
|
-
...entry.wholeWords === true ? { wholeWords: true } : {}
|
|
3642
|
-
...entry.useRegex === true ? { useRegex: true } : {}
|
|
4176
|
+
...entry.wholeWords === true ? { wholeWords: true } : {}
|
|
3643
4177
|
}
|
|
3644
4178
|
});
|
|
3645
4179
|
}
|
|
@@ -3655,27 +4189,37 @@ function writePreset(prompt, options) {
|
|
|
3655
4189
|
}
|
|
3656
4190
|
const runtime = runtimeOf(options, prompt);
|
|
3657
4191
|
const params = resolvePresetParams(spec, runtime);
|
|
3658
|
-
const targetDir = join(presetDir,
|
|
4192
|
+
const targetDir = join(presetDir, outputId);
|
|
3659
4193
|
mkdirSync(presetDir, { recursive: true });
|
|
3660
|
-
const tmpDir = mkdtempSync(join(presetDir, `.${
|
|
4194
|
+
const tmpDir = mkdtempSync(join(presetDir, `.${outputId}.tmp-`));
|
|
3661
4195
|
const outDir = tmpDir;
|
|
3662
4196
|
try {
|
|
3663
4197
|
const overridesSrc = join(targetDir, "prompt-tool.overrides.yml");
|
|
3664
4198
|
if (existsSync(overridesSrc)) cpSync(overridesSrc, join(outDir, "prompt-tool.overrides.yml"), { force: true });
|
|
3665
4199
|
const composition = renderComposition(spec, runtime, templateDir);
|
|
3666
4200
|
assertCompositionArray(composition, spec);
|
|
3667
|
-
const subComposition = composition.replaceAll("./engine/", "../.engine/").replaceAll("../prompt-configs", `../${
|
|
4201
|
+
const subComposition = composition.replaceAll("./engine/", "../.engine/").replaceAll("../prompt-configs", `../${outputId}/prompt-configs`);
|
|
3668
4202
|
writeFileSync(join(outDir, "agent.cordis.yml"), subComposition, "utf8");
|
|
4203
|
+
const aliasSuffix = "(旧会话兼容)";
|
|
4204
|
+
const aliasName = options.aliasOf === true ? typeof spec.name === "string" && spec.name.length > 0 ? spec.name : outputId : void 0;
|
|
4205
|
+
const displayName = aliasName !== void 0 && !aliasName.endsWith(aliasSuffix) ? aliasName + aliasSuffix : aliasName;
|
|
3669
4206
|
const meta = spec.meta !== null && typeof spec.meta === "object" ? spec.meta : {};
|
|
3670
|
-
const
|
|
4207
|
+
const sourceYamlPath = options.aliasOf === true ? join(presetDir, templateName, "preset.yml") : join(targetDir, "preset.yml");
|
|
4208
|
+
const existingPresetYaml = existsSync(sourceYamlPath) ? readFileSync(sourceYamlPath, "utf8") : void 0;
|
|
3671
4209
|
if (existingPresetYaml !== void 0 && existingPresetYaml.trim().length > 0) {
|
|
3672
4210
|
const doc = parseDocument(existingPresetYaml, { logLevel: "silent" });
|
|
3673
4211
|
doc.setIn(["order"], options.presetOrder);
|
|
3674
|
-
if (typeof spec.name === "string" && spec.name.length > 0) doc.setIn(["name"], spec.name);
|
|
4212
|
+
if (typeof spec.name === "string" && spec.name.length > 0) doc.setIn(["name"], displayName ?? spec.name);
|
|
4213
|
+
if (options.aliasOf === true) doc.setIn(["id"], outputId);
|
|
3675
4214
|
if (typeof spec.description === "string" && spec.description.length > 0) doc.setIn(["description"], spec.description);
|
|
3676
4215
|
if (Object.keys(meta).length > 0) doc.setIn(["meta"], meta);
|
|
3677
4216
|
writeFileSync(join(outDir, "preset.yml"), doc.toString(), "utf8");
|
|
3678
|
-
} else writeFileSync(join(outDir, "preset.yml"), stringify({
|
|
4217
|
+
} else writeFileSync(join(outDir, "preset.yml"), stringify(displayName !== void 0 ? {
|
|
4218
|
+
...meta,
|
|
4219
|
+
id: outputId,
|
|
4220
|
+
name: displayName,
|
|
4221
|
+
order: options.presetOrder
|
|
4222
|
+
} : {
|
|
3679
4223
|
...meta,
|
|
3680
4224
|
order: options.presetOrder
|
|
3681
4225
|
}) + "\n", "utf8");
|
|
@@ -3742,7 +4286,7 @@ function writePreset(prompt, options) {
|
|
|
3742
4286
|
next.params = {
|
|
3743
4287
|
...next.params,
|
|
3744
4288
|
useCustom: params.firstTurnCustom === true,
|
|
3745
|
-
|
|
4289
|
+
text: asString(params.firstTurnText),
|
|
3746
4290
|
buildPattern: asString(params.buildPattern),
|
|
3747
4291
|
complexPattern: asString(params.complexPattern),
|
|
3748
4292
|
firstTurnBuild: asString(params.firstTurnBuild),
|
|
@@ -3750,14 +4294,15 @@ function writePreset(prompt, options) {
|
|
|
3750
4294
|
firstTurnDeep: asString(params.firstTurnDeep)
|
|
3751
4295
|
};
|
|
3752
4296
|
} else if (next.id === "router-guide") {
|
|
3753
|
-
|
|
3754
|
-
|
|
4297
|
+
const guideEnabled = params.guideEnabled === void 0 ? params.firstTurnAnchor === true : params.guideEnabled === true;
|
|
4298
|
+
next.enabled = guideEnabled;
|
|
4299
|
+
const useCustom = guideEnabled && params.guideCustom === true;
|
|
3755
4300
|
next.modelScope = useCustom ? "all" : "flash";
|
|
3756
4301
|
next.params = {
|
|
3757
4302
|
...next.params,
|
|
3758
4303
|
useCustom,
|
|
3759
4304
|
text: asString(params.guideText),
|
|
3760
|
-
|
|
4305
|
+
complexPattern: asString(params.complexPattern) || asString(params.guideComplexPattern),
|
|
3761
4306
|
guideWeak: asString(params.guideWeak),
|
|
3762
4307
|
guideDeep: asString(params.guideDeep)
|
|
3763
4308
|
};
|
|
@@ -3769,10 +4314,10 @@ function writePreset(prompt, options) {
|
|
|
3769
4314
|
const presetVariables = {};
|
|
3770
4315
|
for (const [key, value] of Object.entries(spec.params ?? {})) {
|
|
3771
4316
|
if (PARAM_KEYS.has(key)) continue;
|
|
3772
|
-
|
|
3773
|
-
|
|
4317
|
+
if (value === void 0 || value === null) continue;
|
|
4318
|
+
presetVariables[key] = String(value);
|
|
3774
4319
|
}
|
|
3775
|
-
for (const [key, value] of Object.entries(spec.variables ?? {})) if (typeof value === "string"
|
|
4320
|
+
for (const [key, value] of Object.entries(spec.variables ?? {})) if (typeof value === "string") presetVariables[key] = value;
|
|
3776
4321
|
const variablesEnabled = spec.variablesEnabled !== false;
|
|
3777
4322
|
const presetVariableKeys = new Set(Object.keys(presetVariables));
|
|
3778
4323
|
if (variablesEnabled && presetVariableKeys.size > 0) writeFileSync(join(promptConfigsDir, "variables.yml"), stringify(presetVariables), "utf8");
|
|
@@ -3781,15 +4326,30 @@ function writePreset(prompt, options) {
|
|
|
3781
4326
|
delete config.text;
|
|
3782
4327
|
config.texts = [];
|
|
3783
4328
|
config.enabled = params.injectPrompt !== false && prompt.trim().length > 0;
|
|
4329
|
+
const explicitWord = asString(config.params?.firstTurnWord ?? params.firstTurnWord, "");
|
|
4330
|
+
const signalWord = (text) => {
|
|
4331
|
+
const sentence = /the exact sentence:\s*([A-Za-z]+)/i.exec(text);
|
|
4332
|
+
if (sentence !== null && sentence[1] !== void 0) return sentence[1].toLowerCase();
|
|
4333
|
+
const first = /^\P{L}*([\p{L}]+)/u.exec(text.trim());
|
|
4334
|
+
return first !== null && first[1] !== void 0 ? first[1].toLowerCase() : void 0;
|
|
4335
|
+
};
|
|
4336
|
+
const derivedWords = [...new Set([
|
|
4337
|
+
signalWord(asString(params.firstTurnText)),
|
|
4338
|
+
signalWord(asString(params.firstTurnBuild)),
|
|
4339
|
+
signalWord(asString(params.firstTurnInspect)),
|
|
4340
|
+
signalWord(asString(params.firstTurnDeep))
|
|
4341
|
+
].filter((word) => word !== void 0))];
|
|
4342
|
+
const anchorWords = explicitWord.length > 0 ? [explicitWord] : derivedWords;
|
|
3784
4343
|
config.params = {
|
|
3785
4344
|
...config.params,
|
|
3786
4345
|
text: prompt,
|
|
3787
|
-
firstTurnWord:
|
|
4346
|
+
firstTurnWord: explicitWord.length > 0 ? explicitWord : anchorWords[0] ?? "we",
|
|
4347
|
+
anchorWords
|
|
3788
4348
|
};
|
|
3789
4349
|
}
|
|
3790
4350
|
if (config.fill === "instruction-hint") config.params = {
|
|
3791
4351
|
...config.params,
|
|
3792
|
-
agentsInstructionPath: `../${
|
|
4352
|
+
agentsInstructionPath: `../${outputId}/agents-instruction.md`,
|
|
3793
4353
|
...options.injectAgentsPrompt === true ? { text: asString(options.agentsInstructionText) } : {}
|
|
3794
4354
|
};
|
|
3795
4355
|
if (!variablesEnabled && presetVariableKeys.size > 0) {
|
|
@@ -3803,6 +4363,18 @@ function writePreset(prompt, options) {
|
|
|
3803
4363
|
}
|
|
3804
4364
|
writeFileSync(join(promptConfigsDir, `${String(index * 10).padStart(2, "0")}-${config.id}.yml`), renderPromptConfigYaml(config), "utf8");
|
|
3805
4365
|
}
|
|
4366
|
+
const customToolsDir = join(outDir, "custom-tools");
|
|
4367
|
+
rmSync(customToolsDir, {
|
|
4368
|
+
recursive: true,
|
|
4369
|
+
force: true
|
|
4370
|
+
});
|
|
4371
|
+
mkdirSync(customToolsDir, { recursive: true });
|
|
4372
|
+
const customTools = Array.isArray(spec.customTools) ? spec.customTools : [];
|
|
4373
|
+
for (const [index, tool] of customTools.entries()) {
|
|
4374
|
+
if (tool === null || typeof tool !== "object" || Array.isArray(tool) || typeof tool.id !== "string" || String(tool.id).length === 0) continue;
|
|
4375
|
+
const toolId = String(tool.id);
|
|
4376
|
+
writeFileSync(join(customToolsDir, `${String(index + 1).padStart(2, "0")}-${toolId}.yml`), stringify(tool, { lineWidth: 0 }), "utf8");
|
|
4377
|
+
}
|
|
3806
4378
|
for (const legacy of spec.legacyCleanup ?? []) rmSync(join(outDir, legacy), { force: true });
|
|
3807
4379
|
const agentsInstructionPath = join(outDir, "agents-instruction.md");
|
|
3808
4380
|
if (typeof options.agentsInstructionText === "string" && options.agentsInstructionText.trim().length > 0) writeFileSync(agentsInstructionPath, options.agentsInstructionText, "utf8");
|
|
@@ -3855,9 +4427,10 @@ function writePreset(prompt, options) {
|
|
|
3855
4427
|
function migrateLegacyLayout(presetRoot, legacyUserPresets) {
|
|
3856
4428
|
const legacyContainer = join(presetRoot, "prompt-tool");
|
|
3857
4429
|
const sharedEngine = join(presetRoot, ".engine");
|
|
4430
|
+
const isLegacyContainer = existsSync(legacyContainer) && !existsSync(join(legacyContainer, "preset.yml"));
|
|
3858
4431
|
let migrated = false;
|
|
3859
4432
|
const legacyEngine = join(legacyContainer, "engine");
|
|
3860
|
-
if (existsSync(legacyEngine) && !existsSync(sharedEngine)) try {
|
|
4433
|
+
if (isLegacyContainer && existsSync(legacyEngine) && !existsSync(sharedEngine)) try {
|
|
3861
4434
|
mkdirSync(presetRoot, { recursive: true });
|
|
3862
4435
|
renameSync(legacyEngine, sharedEngine);
|
|
3863
4436
|
migrated = true;
|
|
@@ -3884,7 +4457,7 @@ function migrateLegacyLayout(presetRoot, legacyUserPresets) {
|
|
|
3884
4457
|
}
|
|
3885
4458
|
})();
|
|
3886
4459
|
const stamp = Date.now().toString(36);
|
|
3887
|
-
if (existsSync(sharedEngine) && existsSync(legacyContainer)) try {
|
|
4460
|
+
if (isLegacyContainer && existsSync(sharedEngine) && existsSync(legacyContainer)) try {
|
|
3888
4461
|
renameSync(legacyContainer, join(presetRoot, `prompt-tool.bak-${stamp}`));
|
|
3889
4462
|
migrated = true;
|
|
3890
4463
|
} catch {}
|
|
@@ -3900,6 +4473,43 @@ function normalizePresetRootDir(presetDir, presetRoot, legacyContainer) {
|
|
|
3900
4473
|
if (presetDir.endsWith("/prompt-tool") || presetDir.endsWith("\\prompt-tool")) return presetRoot;
|
|
3901
4474
|
return presetDir;
|
|
3902
4475
|
}
|
|
4476
|
+
/**
|
|
4477
|
+
* 旧版参数覆盖文件(prompt-tool.overrides.yml)→ 激活预设 preset.yml 一次性退役迁移。
|
|
4478
|
+
* 旧写入路径(阶段 1)把引擎参数写进该文件;阶段 2 收敛为 savePresetParams 直接写
|
|
4479
|
+
* preset.yml 并删除覆盖文件。本函数仅处理存量升级场景:
|
|
4480
|
+
* - 键白名单 = PARAM_KEYS(含 guideEnabled/injectPrompt/usePtcMode,补上旧手写列表
|
|
4481
|
+
* 缺失的 3 键;promptConfigs 不走覆盖文件,排除);
|
|
4482
|
+
* - 空值键按 savePresetParams 语义删除(回落模板/引擎默认);
|
|
4483
|
+
* - 成功 = preset.yml 已写入且覆盖文件已删(savePresetParams 删除);失败(preset.yml
|
|
4484
|
+
* 缺失等)抛错且覆盖文件保留,调用方下次重建重试。
|
|
4485
|
+
* 返回 true = 已迁移(调用方需重读参数);false = 无文件/解析失败/无有效键(文件已尽力清理)。
|
|
4486
|
+
*/
|
|
4487
|
+
function migrateParamOverridesFile(presetRoot, templateName) {
|
|
4488
|
+
const file = join(presetRoot, templateName, "prompt-tool.overrides.yml");
|
|
4489
|
+
let raw;
|
|
4490
|
+
try {
|
|
4491
|
+
raw = readFileSync(file, "utf8");
|
|
4492
|
+
} catch {
|
|
4493
|
+
return false;
|
|
4494
|
+
}
|
|
4495
|
+
if (raw.trim().length === 0) {
|
|
4496
|
+
rmSync(file, { force: true });
|
|
4497
|
+
return false;
|
|
4498
|
+
}
|
|
4499
|
+
let parsed;
|
|
4500
|
+
try {
|
|
4501
|
+
parsed = parse(raw, { logLevel: "silent" });
|
|
4502
|
+
} catch {
|
|
4503
|
+
return false;
|
|
4504
|
+
}
|
|
4505
|
+
const valid = Object.fromEntries(Object.entries(parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}).filter(([key]) => PARAM_KEYS.has(key) && key !== "promptConfigs"));
|
|
4506
|
+
if (Object.keys(valid).length === 0) {
|
|
4507
|
+
rmSync(file, { force: true });
|
|
4508
|
+
return false;
|
|
4509
|
+
}
|
|
4510
|
+
savePresetParams(presetRoot, templateName, valid, void 0);
|
|
4511
|
+
return true;
|
|
4512
|
+
}
|
|
3903
4513
|
//#endregion
|
|
3904
4514
|
//#region src/index.ts
|
|
3905
4515
|
const name = "prompt-tool";
|
|
@@ -3930,6 +4540,7 @@ function warn(ctx, message) {
|
|
|
3930
4540
|
} catch {}
|
|
3931
4541
|
}
|
|
3932
4542
|
function apply(ctx, configIn) {
|
|
4543
|
+
const hadLegacyContainer = existsSync(join(DEFAULT_PRESET_DIR, "prompt-tool")) && !existsSync(join(DEFAULT_PRESET_DIR, "prompt-tool", "preset.yml"));
|
|
3933
4544
|
let legacyMigrated = false;
|
|
3934
4545
|
try {
|
|
3935
4546
|
legacyMigrated = migrateLegacyLayout(DEFAULT_PRESET_DIR, LEGACY_USER_PRESETS_DIR);
|
|
@@ -3972,6 +4583,7 @@ function apply(ctx, configIn) {
|
|
|
3972
4583
|
runtime.firstTurnCustom = params.firstTurnCustom === true;
|
|
3973
4584
|
runtime.guideText = asString(params.guideText);
|
|
3974
4585
|
runtime.guideCustom = params.guideCustom === true;
|
|
4586
|
+
runtime.guideEnabled = typeof params.guideEnabled === "boolean" ? params.guideEnabled : void 0;
|
|
3975
4587
|
runtime.modelProvider = asString(params.modelProvider);
|
|
3976
4588
|
runtime.modelName = asString(params.modelName);
|
|
3977
4589
|
runtime.subagentModelProvider = asString(params.subagentModelProvider);
|
|
@@ -3983,9 +4595,8 @@ function apply(ctx, configIn) {
|
|
|
3983
4595
|
runtime.subagentTemperature = asString(params.subagentTemperature);
|
|
3984
4596
|
runtime.subagentMaxTokens = asString(params.subagentMaxTokens);
|
|
3985
4597
|
runtime.bootstrapMaxTokens = Number.isSafeInteger(params.bootstrapMaxTokens) && params.bootstrapMaxTokens >= 0 ? params.bootstrapMaxTokens : 0;
|
|
3986
|
-
runtime.usePtcMode = params.usePtcMode
|
|
4598
|
+
runtime.usePtcMode = typeof params.usePtcMode === "boolean" ? params.usePtcMode : void 0;
|
|
3987
4599
|
runtime.injectPrompt = params.injectPrompt !== false;
|
|
3988
|
-
runtime.subagentPersona = asString(params.subagentPersona) || void 0;
|
|
3989
4600
|
runtime.toolFilterAllow = params.toolFilterAllow;
|
|
3990
4601
|
runtime.toolFilterDeny = params.toolFilterDeny;
|
|
3991
4602
|
runtime.maxDepth = params.maxDepth;
|
|
@@ -3993,6 +4604,29 @@ function apply(ctx, configIn) {
|
|
|
3993
4604
|
runtime.firstTurnWord = asString(params.firstTurnWord) || void 0;
|
|
3994
4605
|
runtime.promptConfigs = Array.isArray(spec.promptConfigs) ? spec.promptConfigs : [];
|
|
3995
4606
|
};
|
|
4607
|
+
let legacyAliasHandledThisRun = readPluginState().legacyAliasHandled === true;
|
|
4608
|
+
const ensureLegacyAliasOnce = (presetPrompt, options) => {
|
|
4609
|
+
if (legacyAliasHandledThisRun) return;
|
|
4610
|
+
legacyAliasHandledThisRun = true;
|
|
4611
|
+
const aliasFile = join(runtime.presetDir, "prompt-tool", "preset.yml");
|
|
4612
|
+
try {
|
|
4613
|
+
if (!existsSync(aliasFile) && hadLegacyContainer) {
|
|
4614
|
+
writePreset(presetPrompt, {
|
|
4615
|
+
...options,
|
|
4616
|
+
outputId: "prompt-tool",
|
|
4617
|
+
aliasOf: true
|
|
4618
|
+
});
|
|
4619
|
+
warn(ctx, "prompt-tool: 已创建一次性旧会话兼容快照 prompt-tool;日常切换不再重建");
|
|
4620
|
+
}
|
|
4621
|
+
writePluginState({
|
|
4622
|
+
...readPluginState(),
|
|
4623
|
+
legacyAliasHandled: true
|
|
4624
|
+
});
|
|
4625
|
+
} catch (error) {
|
|
4626
|
+
legacyAliasHandledThisRun = false;
|
|
4627
|
+
warn(ctx, `prompt-tool: 创建旧会话兼容快照失败(下次重建重试):${error instanceof Error ? error.message : String(error)}`);
|
|
4628
|
+
}
|
|
4629
|
+
};
|
|
3996
4630
|
/** 重建生成目录(文本/组合/引擎/提示词配置);writePreset 关闭时移除旧目录。 */
|
|
3997
4631
|
const rebuildPreset = () => {
|
|
3998
4632
|
reloadPresetParams();
|
|
@@ -4005,6 +4639,7 @@ function apply(ctx, configIn) {
|
|
|
4005
4639
|
firstTurnCustom: runtime.firstTurnCustom,
|
|
4006
4640
|
guideText: runtime.guideText,
|
|
4007
4641
|
guideCustom: runtime.guideCustom,
|
|
4642
|
+
guideEnabled: runtime.guideEnabled,
|
|
4008
4643
|
injectPrompt: runtime.injectPrompt,
|
|
4009
4644
|
modelProvider: runtime.modelProvider,
|
|
4010
4645
|
modelName: runtime.modelName,
|
|
@@ -4016,7 +4651,6 @@ function apply(ctx, configIn) {
|
|
|
4016
4651
|
subagentReasoningEffort: runtime.subagentReasoningEffort,
|
|
4017
4652
|
subagentTemperature: runtime.subagentTemperature,
|
|
4018
4653
|
subagentMaxTokens: runtime.subagentMaxTokens,
|
|
4019
|
-
subagentPersona: runtime.subagentPersona,
|
|
4020
4654
|
toolFilterAllow: runtime.toolFilterAllow,
|
|
4021
4655
|
toolFilterDeny: runtime.toolFilterDeny,
|
|
4022
4656
|
maxDepth: runtime.maxDepth,
|
|
@@ -4047,6 +4681,7 @@ function apply(ctx, configIn) {
|
|
|
4047
4681
|
}
|
|
4048
4682
|
}
|
|
4049
4683
|
writePreset(presetPrompt, options);
|
|
4684
|
+
ensureLegacyAliasOnce(presetPrompt, options);
|
|
4050
4685
|
} else {
|
|
4051
4686
|
let cleaned = 0;
|
|
4052
4687
|
for (const preset of listPresets()) {
|
|
@@ -4054,6 +4689,7 @@ function apply(ctx, configIn) {
|
|
|
4054
4689
|
for (const name of [
|
|
4055
4690
|
"agent.cordis.yml",
|
|
4056
4691
|
"prompt-configs",
|
|
4692
|
+
"custom-tools",
|
|
4057
4693
|
"preset.md",
|
|
4058
4694
|
"agents.md",
|
|
4059
4695
|
"agents-instruction.md",
|
|
@@ -4082,49 +4718,15 @@ function apply(ctx, configIn) {
|
|
|
4082
4718
|
return true;
|
|
4083
4719
|
}
|
|
4084
4720
|
};
|
|
4085
|
-
/**
|
|
4721
|
+
/** 旧版参数覆盖文件(prompt-tool.overrides.yml)一次性退役迁移:白名单
|
|
4722
|
+
* (PARAM_KEYS,含 guideEnabled/injectPrompt/usePtcMode)过滤后并入 preset.yml。
|
|
4723
|
+
* 实现收敛到 migrateParamOverridesFile(migration.ts 单一权威)。 */
|
|
4086
4724
|
const applyParamOverrides = () => {
|
|
4087
|
-
const raw = readGeneratedContent(activePresetDir(), "prompt-tool.overrides.yml");
|
|
4088
|
-
if (raw.length === 0) return;
|
|
4089
|
-
let overrides;
|
|
4090
4725
|
try {
|
|
4091
|
-
|
|
4092
|
-
overrides = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
4726
|
+
if (migrateParamOverridesFile(runtime.presetDir, runtime.presetTemplate)) reloadPresetParams();
|
|
4093
4727
|
} catch {
|
|
4094
|
-
|
|
4728
|
+
warn(ctx, "prompt-tool: prompt-tool.overrides.yml 迁移到 preset.yml 失败(下次重建重试)");
|
|
4095
4729
|
}
|
|
4096
|
-
if (typeof overrides.firstTurnAnchor === "boolean") runtime.firstTurnAnchor = overrides.firstTurnAnchor;
|
|
4097
|
-
if (typeof overrides.firstTurnCustom === "boolean") runtime.firstTurnCustom = overrides.firstTurnCustom;
|
|
4098
|
-
if (typeof overrides.firstTurnText === "string") runtime.firstTurnText = overrides.firstTurnText;
|
|
4099
|
-
if (typeof overrides.guideCustom === "boolean") runtime.guideCustom = overrides.guideCustom;
|
|
4100
|
-
if (typeof overrides.guideText === "string") runtime.guideText = overrides.guideText;
|
|
4101
|
-
if (typeof overrides.modelProvider === "string") runtime.modelProvider = overrides.modelProvider;
|
|
4102
|
-
if (typeof overrides.modelName === "string") runtime.modelName = overrides.modelName;
|
|
4103
|
-
if (typeof overrides.subagentModelProvider === "string") runtime.subagentModelProvider = overrides.subagentModelProvider;
|
|
4104
|
-
if (typeof overrides.subagentModelName === "string") runtime.subagentModelName = overrides.subagentModelName;
|
|
4105
|
-
if (typeof overrides.modelReasoningEffort === "string") runtime.modelReasoningEffort = overrides.modelReasoningEffort;
|
|
4106
|
-
if (typeof overrides.modelTemperature === "string") runtime.modelTemperature = overrides.modelTemperature;
|
|
4107
|
-
if (typeof overrides.modelMaxTokens === "string") runtime.modelMaxTokens = overrides.modelMaxTokens;
|
|
4108
|
-
if (typeof overrides.subagentReasoningEffort === "string") runtime.subagentReasoningEffort = overrides.subagentReasoningEffort;
|
|
4109
|
-
if (typeof overrides.subagentTemperature === "string") runtime.subagentTemperature = overrides.subagentTemperature;
|
|
4110
|
-
if (typeof overrides.subagentMaxTokens === "string") runtime.subagentMaxTokens = overrides.subagentMaxTokens;
|
|
4111
|
-
if (typeof overrides.subagentPersona === "string") runtime.subagentPersona = overrides.subagentPersona;
|
|
4112
|
-
if (typeof overrides.firstTurnWord === "string") runtime.firstTurnWord = overrides.firstTurnWord;
|
|
4113
|
-
if (typeof overrides.bootstrapMaxTokens === "number") runtime.bootstrapMaxTokens = overrides.bootstrapMaxTokens;
|
|
4114
|
-
const listOf = (value) => {
|
|
4115
|
-
if (typeof value === "string") return value;
|
|
4116
|
-
if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
|
|
4117
|
-
};
|
|
4118
|
-
const tfAllow = listOf(overrides.toolFilterAllow);
|
|
4119
|
-
if (tfAllow !== void 0) runtime.toolFilterAllow = tfAllow;
|
|
4120
|
-
const tfDeny = listOf(overrides.toolFilterDeny);
|
|
4121
|
-
if (tfDeny !== void 0) runtime.toolFilterDeny = tfDeny;
|
|
4122
|
-
const kinds = listOf(overrides.allowKinds);
|
|
4123
|
-
if (kinds !== void 0 && (typeof kinds === "string" ? kinds.trim().length > 0 : kinds.length > 0)) runtime.allowKinds = kinds;
|
|
4124
|
-
const depth = overrides.maxDepth;
|
|
4125
|
-
if (depth === "provider-managed") runtime.maxDepth = "provider-managed";
|
|
4126
|
-
else if (typeof depth === "number" && Number.isSafeInteger(depth) && depth >= 0) runtime.maxDepth = depth;
|
|
4127
|
-
else if (typeof depth === "string" && depth.length > 0) runtime.maxDepth = depth;
|
|
4128
4730
|
};
|
|
4129
4731
|
const legacySkillsDir = typeof config.skillsDir === "string" && config.skillsDir.length > 0 && config.skillsDir !== DEFAULT_SKILLS_DIR ? config.skillsDir : "";
|
|
4130
4732
|
const userSkillsDirs = Array.isArray(config.skillsDirs) && config.skillsDirs.length > 0 ? config.skillsDirs.filter((dir) => typeof dir === "string" && dir.trim().length > 0) : legacySkillsDir.length > 0 ? [legacySkillsDir] : [];
|
|
@@ -4238,15 +4840,15 @@ function apply(ctx, configIn) {
|
|
|
4238
4840
|
}
|
|
4239
4841
|
};
|
|
4240
4842
|
});
|
|
4241
|
-
registerSettingsBridge(ctx, NS, getModelsState, () => ({
|
|
4843
|
+
const settingsBridge = registerSettingsBridge(ctx, NS, getModelsState, () => ({
|
|
4242
4844
|
activeSkillsDirs,
|
|
4243
4845
|
skillCatalog
|
|
4244
4846
|
}), () => "", (sctx) => ensureRegistered(sctx), () => {
|
|
4245
4847
|
skillCatalog = catalogOf(readAllSkillsChecked());
|
|
4246
4848
|
cachedSkills.invalidate();
|
|
4247
4849
|
invalidateSkills?.();
|
|
4248
|
-
}, () => activePresetDir(), (
|
|
4249
|
-
if (scope === "preset") current = readGeneratedContent(activePresetDir(), "preset.md");
|
|
4850
|
+
}, () => activePresetDir(), (scopes) => {
|
|
4851
|
+
for (const scope of scopes) if (scope === "preset") current = readGeneratedContent(activePresetDir(), "preset.md");
|
|
4250
4852
|
else currentAgents = readGeneratedContent(activePresetDir(), "agents.md");
|
|
4251
4853
|
try {
|
|
4252
4854
|
rebuildPreset();
|
|
@@ -4283,6 +4885,7 @@ function apply(ctx, configIn) {
|
|
|
4283
4885
|
firstTurnCustom: initialParams.firstTurnCustom === true,
|
|
4284
4886
|
guideText: asString(initialParams.guideText),
|
|
4285
4887
|
guideCustom: initialParams.guideCustom === true,
|
|
4888
|
+
guideEnabled: typeof initialParams.guideEnabled === "boolean" ? initialParams.guideEnabled : void 0,
|
|
4286
4889
|
injectPrompt: initialParams.injectPrompt !== false,
|
|
4287
4890
|
modelProvider: asString(initialParams.modelProvider),
|
|
4288
4891
|
modelName: asString(initialParams.modelName),
|
|
@@ -4295,8 +4898,7 @@ function apply(ctx, configIn) {
|
|
|
4295
4898
|
subagentTemperature: asString(initialParams.subagentTemperature),
|
|
4296
4899
|
subagentMaxTokens: asString(initialParams.subagentMaxTokens),
|
|
4297
4900
|
bootstrapMaxTokens: Number.isSafeInteger(initialParams.bootstrapMaxTokens) && initialParams.bootstrapMaxTokens >= 0 ? initialParams.bootstrapMaxTokens : 0,
|
|
4298
|
-
usePtcMode: initialParams.usePtcMode
|
|
4299
|
-
subagentPersona: asString(initialParams.subagentPersona) || void 0,
|
|
4901
|
+
usePtcMode: typeof initialParams.usePtcMode === "boolean" ? initialParams.usePtcMode : void 0,
|
|
4300
4902
|
toolFilterAllow: initialParams.toolFilterAllow,
|
|
4301
4903
|
toolFilterDeny: initialParams.toolFilterDeny,
|
|
4302
4904
|
maxDepth: initialParams.maxDepth,
|
|
@@ -4309,9 +4911,21 @@ function apply(ctx, configIn) {
|
|
|
4309
4911
|
fallbackText: config.fallbackText,
|
|
4310
4912
|
promptConfigs: Array.isArray(initialSpec?.promptConfigs) ? initialSpec.promptConfigs : []
|
|
4311
4913
|
};
|
|
4914
|
+
const agentPresetsNs = settingsNamespace("agent-presets");
|
|
4312
4915
|
let hostSettingsService;
|
|
4313
|
-
|
|
4314
|
-
|
|
4916
|
+
const readHostDefault = (value) => {
|
|
4917
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
4918
|
+
const candidate = value.default;
|
|
4919
|
+
return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
4920
|
+
};
|
|
4921
|
+
const managedPresetExists = (id) => {
|
|
4922
|
+
try {
|
|
4923
|
+
return listPresets().some((preset) => preset.id === id);
|
|
4924
|
+
} catch {
|
|
4925
|
+
return false;
|
|
4926
|
+
}
|
|
4927
|
+
};
|
|
4928
|
+
const syncHostDefault = (_reason) => {
|
|
4315
4929
|
const s = hostSettingsService;
|
|
4316
4930
|
if (s === void 0) return;
|
|
4317
4931
|
const template = runtime.presetTemplate;
|
|
@@ -4319,17 +4933,31 @@ function apply(ctx, configIn) {
|
|
|
4319
4933
|
warn(ctx, `prompt-tool: 预设 id ${JSON.stringify(template)} 不符合官方 agent-presets 命名(^[a-z0-9][a-z0-9-]*$),跳过宿主 default 同步;请将预设目录改名为合法 id`);
|
|
4320
4934
|
return;
|
|
4321
4935
|
}
|
|
4322
|
-
if (
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
value: template
|
|
4329
|
-
}]);
|
|
4330
|
-
} catch (error) {
|
|
4936
|
+
if (readHostDefault(s.get(agentPresetsNs)) === template) return;
|
|
4937
|
+
s.mutate(agentPresetsNs, [{
|
|
4938
|
+
op: "set",
|
|
4939
|
+
path: ["default"],
|
|
4940
|
+
value: template
|
|
4941
|
+
}]).catch((error) => {
|
|
4331
4942
|
warn(ctx, `prompt-tool: 同步宿主 agent-presets default 失败:${error instanceof Error ? error.message : String(error)}`);
|
|
4943
|
+
});
|
|
4944
|
+
};
|
|
4945
|
+
const syncTemplateFromHostDefault = (value) => {
|
|
4946
|
+
const s = hostSettingsService;
|
|
4947
|
+
if (s === void 0) return;
|
|
4948
|
+
const template = readHostDefault(value ?? s.get(agentPresetsNs));
|
|
4949
|
+
if (template === void 0 || template === runtime.presetTemplate) return;
|
|
4950
|
+
if (!managedPresetExists(template)) {
|
|
4951
|
+
warn(ctx, `prompt-tool: 官方 agent-presets default 已切换为 ${JSON.stringify(template)},但该预设不在提示词工具管理目录中,跳过反向同步`);
|
|
4952
|
+
return;
|
|
4332
4953
|
}
|
|
4954
|
+
s.mutate(NS, [{
|
|
4955
|
+
op: "set",
|
|
4956
|
+
path: ["presetTemplate"],
|
|
4957
|
+
value: template
|
|
4958
|
+
}]).catch((error) => {
|
|
4959
|
+
warn(ctx, `prompt-tool: 跟随官方 agent-presets default 失败:${error instanceof Error ? error.message : String(error)}`);
|
|
4960
|
+
});
|
|
4333
4961
|
};
|
|
4334
4962
|
installSubagentModelRoute(ctx, () => runtime.subagentModelProvider.length > 0 && runtime.subagentModelName.length > 0, () => runtime.subagentModelProvider, () => runtime.subagentModelName);
|
|
4335
4963
|
const applyDefaultModel = installDefaultModelRoute(ctx, () => runtime.modelProvider.length > 0 && runtime.modelName.length > 0, () => runtime.modelProvider, () => runtime.modelName, () => runtime.modelReasoningEffort);
|
|
@@ -4352,14 +4980,10 @@ function apply(ctx, configIn) {
|
|
|
4352
4980
|
presetTemplate: runtime.presetTemplate
|
|
4353
4981
|
});
|
|
4354
4982
|
registerTuiCommand(ctx, NS, () => currentSource(), getModelsState, () => listAdvertisedModels(ctx), () => activePresetDir(), (key, value) => {
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
rebuildPreset();
|
|
4360
|
-
} catch (error) {
|
|
4361
|
-
warn(ctx, `prompt-tool: TUI 参数保存失败:${error instanceof Error ? error.message : String(error)}`);
|
|
4362
|
-
}
|
|
4983
|
+
if (key === "promptConfigs") savePresetParams(runtime.presetDir, runtime.presetTemplate, void 0, Array.isArray(value) ? value : void 0);
|
|
4984
|
+
else savePresetParams(runtime.presetDir, runtime.presetTemplate, { [key]: value }, void 0);
|
|
4985
|
+
reloadPresetParams();
|
|
4986
|
+
rebuildPreset();
|
|
4363
4987
|
});
|
|
4364
4988
|
/**
|
|
4365
4989
|
* 物化导入的预设:用其自身 preset.yml(spec 参数 + modules + promptConfigs)渲染
|
|
@@ -4381,7 +5005,6 @@ function apply(ctx, configIn) {
|
|
|
4381
5005
|
modelName: "",
|
|
4382
5006
|
subagentModelProvider: "",
|
|
4383
5007
|
subagentModelName: "",
|
|
4384
|
-
subagentPersona: "",
|
|
4385
5008
|
injectAgentsPrompt: false,
|
|
4386
5009
|
bootstrapMaxTokens: 0,
|
|
4387
5010
|
usePtcMode: false,
|
|
@@ -4454,37 +5077,50 @@ function apply(ctx, configIn) {
|
|
|
4454
5077
|
if (!residentAgentsWritten) warn(ctx, `prompt-tool: failed to remove resident rules block from ${runtime.residentAgentsPath}`);
|
|
4455
5078
|
}
|
|
4456
5079
|
rebuildPreset();
|
|
4457
|
-
if (presetTemplateChanged)
|
|
5080
|
+
if (presetTemplateChanged) {
|
|
5081
|
+
syncHostDefault("switch");
|
|
5082
|
+
applyBuiltinTools();
|
|
5083
|
+
}
|
|
4458
5084
|
};
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
5085
|
+
let disposeBuiltinTools = [];
|
|
5086
|
+
const applyBuiltinTools = () => {
|
|
5087
|
+
for (const dispose of disposeBuiltinTools) dispose();
|
|
5088
|
+
disposeBuiltinTools = [];
|
|
5089
|
+
const push = (dispose) => {
|
|
5090
|
+
if (typeof dispose === "function") disposeBuiltinTools.push(dispose);
|
|
5091
|
+
};
|
|
5092
|
+
push(registerCharacterTools(ctx, {
|
|
5093
|
+
presetRoot: () => dirname(activePresetDir()),
|
|
5094
|
+
templateName: () => basename(activePresetDir()),
|
|
5095
|
+
rebuild: () => {
|
|
5096
|
+
try {
|
|
5097
|
+
rebuildPreset();
|
|
5098
|
+
} catch (error) {
|
|
5099
|
+
warn(ctx, `prompt-tool: character tool rebuild failed: ${String(error)}`);
|
|
5100
|
+
}
|
|
4467
5101
|
}
|
|
4468
|
-
}
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
5102
|
+
}));
|
|
5103
|
+
push(registerWorldBookTools(ctx, {
|
|
5104
|
+
activeDir: () => activePresetDir(),
|
|
5105
|
+
presetRoot: () => dirname(activePresetDir()),
|
|
5106
|
+
rebuild: () => {
|
|
5107
|
+
try {
|
|
5108
|
+
rebuildPreset();
|
|
5109
|
+
} catch (error) {
|
|
5110
|
+
warn(ctx, `prompt-tool: world-book tool rebuild failed: ${String(error)}`);
|
|
5111
|
+
}
|
|
4478
5112
|
}
|
|
4479
|
-
}
|
|
4480
|
-
|
|
4481
|
-
|
|
5113
|
+
}));
|
|
5114
|
+
push(registerSessionVarTools(ctx));
|
|
5115
|
+
};
|
|
5116
|
+
applyBuiltinTools();
|
|
4482
5117
|
const settingsEntry = currentSource();
|
|
4483
5118
|
const ensureRegistered = (sctx) => ensureSettingsRegistered(sctx, NS, PromptSettingsSchema, {
|
|
4484
5119
|
base: () => settingsEntry,
|
|
4485
5120
|
onRegistered: (scope) => {
|
|
4486
5121
|
currentSource = () => scope.get();
|
|
4487
5122
|
scope.watch(() => {
|
|
5123
|
+
settingsBridge.invalidateDescriptor();
|
|
4488
5124
|
try {
|
|
4489
5125
|
applyState();
|
|
4490
5126
|
} catch (error) {
|
|
@@ -4560,10 +5196,18 @@ function apply(ctx, configIn) {
|
|
|
4560
5196
|
warn(ctx, `prompt-tool: settings 参数迁移失败(下次启动重试):${error instanceof Error ? error.message : String(error)}`);
|
|
4561
5197
|
}
|
|
4562
5198
|
};
|
|
5199
|
+
ctx.inject(["settings", "agentPresets"], () => {
|
|
5200
|
+
syncTemplateFromHostDefault();
|
|
5201
|
+
});
|
|
4563
5202
|
ctx.inject(["settings"], (sctx) => {
|
|
4564
5203
|
hostSettingsService = sctx.settings;
|
|
5204
|
+
sctx.effect(() => sctx.on("settings/updated", (ns, next) => {
|
|
5205
|
+
if (String(ns) !== String(agentPresetsNs)) return;
|
|
5206
|
+
syncTemplateFromHostDefault(next);
|
|
5207
|
+
}), "prompt-tool: follow agent-presets default");
|
|
4565
5208
|
ensureRegistered(sctx);
|
|
4566
5209
|
if (legacyMigrated) syncHostDefault("migrate");
|
|
5210
|
+
else syncTemplateFromHostDefault();
|
|
4567
5211
|
migrateSettingsParamsToPreset(sctx);
|
|
4568
5212
|
sctx.effect(() => () => {
|
|
4569
5213
|
currentSource = () => settingsEntry;
|
|
@@ -4576,4 +5220,4 @@ function apply(ctx, configIn) {
|
|
|
4576
5220
|
});
|
|
4577
5221
|
}
|
|
4578
5222
|
//#endregion
|
|
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 };
|
|
5223
|
+
export { BRIDGE_ENDPOINTS, Config, ENGINE_PARAM_KEYS, MODEL_SEGMENT_MAP, PARAM_KEYS, PromptSettingsSchema, SETTINGS_BRIDGE_PREFIX, SKILL_NAME_RE, WRITER_PARAM_KEYS, appendCharacterMemory, appendMemoryFile, apply, applyCharacterToPreset, applyModuleConfigs, buildModuleConfigsFromParams, buildWorldBookEntry, cloneBuiltinPreset, convertStToPreset, createCachedSkillsReader, deleteCharacterCard, deleteWorldBookEntry, detectModels, ensurePresetSeed, ensureSettingsRegistered, ensureWebSurface, fixSkillEntry, importCharacterCard, inject, installDefaultModelRoute, isValidSkill, listAdvertisedModels, listBuiltinTemplates, listCharacterCards, listPresets, listSkillFolders, listWorldBookEntries, loadPresetSpec, loadPromptTemplates, loadToolTemplates, mergeSkillDirs, mergeStPresets, migrateLegacyLayout, name, normalizePresetRootDir, parseFrontmatter, processStText, readCharacterMemory, readPluginState, readSkills, registerCharacterTools, registerSettingsBridge, registerTuiCommand, registerWorldBookTools, removeCharacterFromPreset, removeUserPreset, renderComposition, resolvePresetDir, resolvePresetParams, resolveProfileDir, resolveProfileSkillsDir, savePresetParams, stPresetId, syncImportedCharacterMemory, toKebabName, upsertWorldBookEntry, userPresetsDir, validSkills, validatePromptConfigs, writePluginState, writePreset };
|