dsh-plugin-prompt-tool 1.0.0 → 2.0.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 +38 -29
- package/engine/actions/append-context.mjs +49 -0
- package/engine/actions/assembly.mjs +150 -0
- package/engine/actions/catalog.mjs +92 -0
- package/engine/actions/content.mjs +205 -0
- package/engine/actions/decision.mjs +54 -0
- package/engine/actions/examples.mjs +11 -0
- package/engine/actions/guard.mjs +80 -0
- package/engine/actions/inbox-prepend.mjs +37 -0
- package/engine/actions/index.mjs +100 -0
- package/engine/actions/inject-text.mjs +22 -0
- package/engine/actions/pre-step-filter.mjs +55 -0
- package/engine/actions/registration.mjs +168 -0
- package/engine/actions/request-params.mjs +32 -0
- package/engine/actions/sdk-strip.mjs +30 -0
- package/engine/actions/shared.mjs +71 -0
- package/engine/actions.mjs +2 -931
- package/engine/branch.mjs +44 -0
- package/engine/compaction-epoch.mjs +31 -13
- package/engine/compositions/source/local/dev-tool-search.yml +11 -0
- package/engine/compositions/source/local/instruction-hint.yml +22 -1
- package/engine/compositions/source/local/rule-engine.yml +6 -0
- package/engine/condition.mjs +16 -3
- package/engine/conditions/anchor.mjs +32 -0
- package/engine/conditions/availability.mjs +47 -0
- package/engine/conditions/composite.mjs +83 -0
- package/engine/conditions/count.mjs +140 -0
- package/engine/conditions/evaluation.mjs +41 -0
- package/engine/conditions/examples.mjs +11 -0
- package/engine/conditions/index.mjs +85 -0
- package/engine/conditions/names.mjs +21 -0
- package/engine/conditions/phase.mjs +54 -0
- package/engine/conditions/preset.mjs +94 -0
- package/engine/conditions/scope.mjs +18 -0
- package/engine/conditions/session.mjs +22 -0
- package/engine/conditions/source.mjs +63 -0
- package/engine/conditions/subject.mjs +121 -0
- package/engine/conditions/text.mjs +44 -0
- package/engine/conditions/values.mjs +32 -0
- package/engine/dev-tool-search.mjs +213 -0
- package/engine/executor.mjs +55 -6
- package/engine/fillers.mjs +23 -16
- package/engine/instruction-hint.mjs +34 -22
- package/engine/layers.mjs +135 -49
- package/engine/order.mjs +4 -1
- package/engine/predicates.mjs +2 -753
- package/engine/rule-engine.mjs +40 -0
- package/engine/rule-runtime.mjs +110 -0
- package/engine/rule-spec.mjs +187 -0
- package/engine/schema.mjs +16 -5
- package/engine/shared.mjs +1 -1
- package/engine/strategies.mjs +2 -235
- package/engine/subagent-tool-policy-core.mjs +7 -7
- package/engine/subagent-tool-policy.mjs +20 -10
- package/engine/tool-config-engine.mjs +53 -32
- package/engine/trigger-editor-meta.mjs +2 -21
- package/engine/trigger-spec.mjs +49 -133
- package/lib/client.js +10446 -10820
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +82 -139
- package/lib/index.mjs +826 -1087
- package/lib/migrate-rules.d.mts +1 -0
- package/lib/migrate-rules.mjs +39 -0
- package/lib/module-package-CHPFweqK.mjs +2 -0
- package/lib/module-package-Cnd1_mwx.mjs +2872 -0
- package/lib/{paths-BgL42sSM.d.mts → paths-CyP0IkaM.d.mts} +223 -108
- package/lib/preset-transfer.d.mts +2 -2
- package/lib/preset-transfer.mjs +3 -2
- package/lib/{module-package-Cm3fHHlp.mjs → rules-migration-DJ6vASB5.mjs} +13696 -15557
- package/modules/ponytail/module.yml +381 -147
- package/modules/skill-surface/module.yml +74 -0
- package/modules/tool-surface/module.yml +67 -0
- package/package.json +5 -1
- package/templates/10-pre-step.yml +26 -22
- package/templates/14-first-turn-anchor.yml +25 -21
- package/templates/15-guide-auto.yml +34 -19
- package/templates/16-custom-fallback.yml +27 -15
- package/templates/18-placeholder-env-facts.yml +23 -14
- package/templates/20-system-section.yml +16 -10
- package/templates/30-runtime-context.yml +13 -7
- package/templates/40-agent-request.yml +11 -8
- package/templates/50-llm-stream.yml +14 -6
- package/templates/60-tool-pipeline.yml +15 -9
- package/templates/65-turn-stop.yml +19 -12
- package/templates/66-subagent-start.yml +12 -7
- package/templates/70-subagent-maintenance.yml +35 -29
- package/templates/75-subagent-end.yml +10 -5
- package/templates/80-tool-surface.yml +66 -0
- package/templates/policies/legacy-defaults.yml +34 -0
- package/engine/compositions/source/local/prompt-config-engine.yml +0 -7
- package/engine/prompt-config-engine.mjs +0 -104
- package/modules/ponytail/configs/0000-ponytail-rules.yml +0 -102
- package/modules/ponytail/configs/0010-ponytail-level-full.yml +0 -19
- package/modules/ponytail/configs/0020-ponytail-level-lite.yml +0 -19
- package/modules/ponytail/configs/0030-ponytail-level-ultra.yml +0 -19
package/lib/index.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as wireTriggerObservers, A as packageEngineDir, At as INJECTION_POINT_ORDER, B as userModulesDir, Bt as validateEngineParamValues, C as ensureModuleSeed, Ct as assertModuleTree, D as loadModuleSpec, Dt as readPersonaSpec, E as listModules, F as resolveModuleDir, Ft as validateCustomToolIdentities, Gt as USER_SKILLS_DIR, H as editModuleRules, Ht as DSH_HOME, I as resolveModuleFacts, It as ENGINE_PARAM_DEFINITIONS, J as rulePromptConfigOptions, K as ensureModuleSlices, L as resolveModuleParams, Lt as ENGINE_PARAM_KEYS, M as removeModuleCapabilityDeclaration, Mt as engineRecipe, N as removeUserModule, Nt as isModuleCapabilityPresent, O as moduleDirExists, Ot as ENGINE_EDITOR_GROUP_MAP, P as renderComposition, Pt as moduleCapability, R as saveModuleParams, Rt as WRITER_PARAM_KEYS, S as duplicateUserModule, St as assertModuleId, T as listBuiltinTemplates, Tt as modulePathExists, U as readModuleRules, Ut as MODULES_DIR, V as withModuleDefinition, Vt as LEGACY_PROMPT_PARAM_KEYS, W as ModuleRulesError, Z as compileRules, _ as applyModuleConfigs, _t as RULE_OWNED_MODEL_PARAMS, at as runPreStepBatch, b as cloneBuiltinModule, bt as readModuleLayerSettings, c as readConfigOrder, ct as actionMatches, d as assertSafeConfigId, et as compileWhen, f as configFileName, g as appendModuleCapabilities, gt as atomicWriteTextFile, h as setModuleEnabled, ht as createWarnOnce, it as confirmDelivered, j as removeModuleCapability, jt as MODULE_CAPABILITIES, k as openModuleLocation, kt as ENGINE_RECIPES, l as readModuleConfigOrder, lt as ruleFrame, m as resolveEditDir, mt as PROMOTE_EVENTS, nt as prepareAction, o as ModuleConfigOrderError, ot as lastWorldBookDiagnostics, p as enabledModuleIds, pt as createEpochPromotion, q as loadModuleDefinition, rt as applyPromptConfigSources, s as appendModuleConfigOrder, st as wireLayers, tt as loadStandingMountFor, u as saveModuleConfigOrder, ut as compareConfigSequence, vt as ModuleLayerSettingsError, wt as canonicalModulesRoot, x as createModuleCapability, xt as assertModuleDirectory, y as buildModuleConfigsFromParams, z as saveModulePersona, zt as moduleParamFallbacks } from "./rules-migration-DJ6vASB5.mjs";
|
|
2
|
+
import { C as mergeStModulesWithReport, D as deleteWorldBookEntry, E as buildWorldBookEntry, M as DEFAULT_MODULE_ID, O as listWorldBookEntries, S as mergeStModules, T as stModuleId, _ as readModuleMemory, a as installModulePackage, b as convertStToModule, c as writeModule, d as validateCustomTools, f as safeAssetPath, g as mergeModuleIntoModule, h as importCharacterCardFile, i as exportModulePackage, j as ruleInjections, k as upsertWorldBookEntry, l as writePreset, m as importCharacterCard, o as moduleImportPreview, p as appendModuleMemory, r as expandModuleSource, s as ensureModuleReady, u as compileCustomTool, v as removeMergedModule, w as processStText, x as convertStToModuleWithReport, y as prepareImport } from "./module-package-Cnd1_mwx.mjs";
|
|
2
3
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
4
|
import { accessSync, chmodSync, constants, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
4
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
6
|
import { Document, isMap, isScalar, parse, parseDocument, visit } from "yaml";
|
|
6
7
|
import { homedir } from "node:os";
|
|
8
|
+
import { isDeepStrictEqual } from "node:util";
|
|
7
9
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
10
|
import { NamedEntries, ScopedLayers, scopeOf } from "@deepseek-ai/dsh-scope";
|
|
9
|
-
import { isDeepStrictEqual } from "node:util";
|
|
10
11
|
import z from "@deepseek-ai/schemastery";
|
|
11
12
|
import { Transform } from "node:stream";
|
|
12
13
|
import { pipeline } from "node:stream/promises";
|
|
@@ -327,107 +328,34 @@ const PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
|
327
328
|
...EXTRA_PARAM_KEYS
|
|
328
329
|
]);
|
|
329
330
|
//#endregion
|
|
331
|
+
//#region src/shared/module-settings.ts
|
|
332
|
+
/** 部署总闸的新旧字段只在读写边界归一。 */
|
|
333
|
+
function readModulesEnabled(value) {
|
|
334
|
+
const { modulesEnabled, writePreset } = value;
|
|
335
|
+
if (modulesEnabled !== void 0 && typeof modulesEnabled !== "boolean") throw new TypeError("modulesEnabled 必须是布尔值");
|
|
336
|
+
if (writePreset !== void 0 && typeof writePreset !== "boolean") throw new TypeError("旧 writePreset 必须是布尔值");
|
|
337
|
+
if (modulesEnabled !== void 0 && writePreset !== void 0 && modulesEnabled !== writePreset) throw new Error("modulesEnabled 与旧 writePreset 冲突");
|
|
338
|
+
return modulesEnabled ?? writePreset ?? true;
|
|
339
|
+
}
|
|
340
|
+
/** 一次官方 settings 事务完成改值与旧键退役,不产生新旧冲突中间态。 */
|
|
341
|
+
function modulesEnabledOps(enabled) {
|
|
342
|
+
return [{
|
|
343
|
+
op: "set",
|
|
344
|
+
path: ["modulesEnabled"],
|
|
345
|
+
value: enabled
|
|
346
|
+
}, {
|
|
347
|
+
op: "unset",
|
|
348
|
+
path: ["writePreset"]
|
|
349
|
+
}];
|
|
350
|
+
}
|
|
351
|
+
//#endregion
|
|
330
352
|
//#region src/config.ts
|
|
331
353
|
/** 插件配置、settings 数据模型与默认常量(settings 接口层)。 */
|
|
332
354
|
const NS = "prompt-tool";
|
|
333
|
-
const Config = z.object({
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const MANAGED_CONFIG_FIELDS = [{
|
|
338
|
-
configId: "near-anchor",
|
|
339
|
-
fields: [
|
|
340
|
-
{
|
|
341
|
-
path: "enabled",
|
|
342
|
-
sourceParam: "firstTurnAnchor"
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
path: "params.useCustom",
|
|
346
|
-
sourceParam: "firstTurnCustom"
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
path: "params.text",
|
|
350
|
-
sourceParam: "firstTurnText"
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
path: "params.buildPattern",
|
|
354
|
-
sourceParam: "buildPattern"
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
path: "params.complexPattern",
|
|
358
|
-
sourceParam: "complexPattern"
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
path: "params.firstTurnBuild",
|
|
362
|
-
sourceParam: "firstTurnBuild"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
path: "params.firstTurnInspect",
|
|
366
|
-
sourceParam: "firstTurnInspect"
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
path: "params.firstTurnDeep",
|
|
370
|
-
sourceParam: "firstTurnDeep"
|
|
371
|
-
}
|
|
372
|
-
]
|
|
373
|
-
}, {
|
|
374
|
-
configId: "router-guide",
|
|
375
|
-
fields: [
|
|
376
|
-
{
|
|
377
|
-
path: "enabled",
|
|
378
|
-
sourceParam: "guideEnabled"
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
path: "modelScope",
|
|
382
|
-
sourceParam: "guideCustom",
|
|
383
|
-
derived: true,
|
|
384
|
-
fallbackNote: "followsCustom"
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
path: "params.useCustom",
|
|
388
|
-
sourceParam: "guideCustom",
|
|
389
|
-
derived: true
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
path: "params.text",
|
|
393
|
-
sourceParam: "guideText"
|
|
394
|
-
},
|
|
395
|
-
{
|
|
396
|
-
path: "params.complexPattern",
|
|
397
|
-
sourceParam: "complexPattern"
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
path: "params.guideWeak",
|
|
401
|
-
sourceParam: "guideWeak"
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
path: "params.guideDeep",
|
|
405
|
-
sourceParam: "guideDeep"
|
|
406
|
-
}
|
|
407
|
-
]
|
|
408
|
-
}];
|
|
409
|
-
/** 严格投影白名单;未知路径、参数键和附加属性都不进入客户端。 */
|
|
410
|
-
function readConfigFieldSources(configId, raw) {
|
|
411
|
-
const spec = MANAGED_CONFIG_FIELDS.find((entry) => entry.configId === configId);
|
|
412
|
-
if (spec === void 0 || raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
413
|
-
const record = raw;
|
|
414
|
-
if (record.configId !== configId || !Array.isArray(record.fields)) return void 0;
|
|
415
|
-
return {
|
|
416
|
-
configId,
|
|
417
|
-
fields: spec.fields.flatMap(({ path }) => {
|
|
418
|
-
const source = record.fields.find((item) => item !== null && typeof item === "object" && item.path === path)?.source;
|
|
419
|
-
return source === "preset-param" || source === "prompt-config" ? [{
|
|
420
|
-
path,
|
|
421
|
-
source
|
|
422
|
-
}] : [];
|
|
423
|
-
})
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
/** 来源只用于读回;无论客户端传了什么来源,都不能写入模块定义。 */
|
|
427
|
-
function stripConfigFieldSources(config) {
|
|
428
|
-
const { fieldSources: _fieldSources, sequence: _sequence, ...definition } = config;
|
|
429
|
-
return definition;
|
|
430
|
-
}
|
|
355
|
+
const Config = z.object({
|
|
356
|
+
modulesEnabled: z.boolean().volatile(),
|
|
357
|
+
writePreset: z.boolean().volatile()
|
|
358
|
+
});
|
|
431
359
|
//#endregion
|
|
432
360
|
//#region src/shared/official-orders.ts
|
|
433
361
|
/** section 侧 6 组,覆盖官方 32 个档位名(顺序即官方数值升序)。 */
|
|
@@ -538,89 +466,6 @@ function readOfficialOrderSegments(lookup) {
|
|
|
538
466
|
};
|
|
539
467
|
}
|
|
540
468
|
//#endregion
|
|
541
|
-
//#region src/runtime/configs-validate.ts
|
|
542
|
-
/** 数组整体不是数组时返回单条 -1 错误;元素问题逐条收集。 */
|
|
543
|
-
function shapeErrors(value) {
|
|
544
|
-
if (!Array.isArray(value)) return [{
|
|
545
|
-
index: -1,
|
|
546
|
-
id: "",
|
|
547
|
-
message: "promptConfigs must be an array"
|
|
548
|
-
}];
|
|
549
|
-
const errors = [];
|
|
550
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
551
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
552
|
-
const item = value[index];
|
|
553
|
-
const label = `configs[${index}]`;
|
|
554
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
555
|
-
errors.push({
|
|
556
|
-
index,
|
|
557
|
-
id: "",
|
|
558
|
-
message: `${label} must be an object`
|
|
559
|
-
});
|
|
560
|
-
continue;
|
|
561
|
-
}
|
|
562
|
-
const id = item.id;
|
|
563
|
-
if (typeof id !== "string" || id.length === 0) errors.push({
|
|
564
|
-
index,
|
|
565
|
-
id: typeof id === "string" ? id : "",
|
|
566
|
-
message: `${label}.id must be a non-empty string`
|
|
567
|
-
});
|
|
568
|
-
else if (seenIds.has(id)) errors.push({
|
|
569
|
-
index,
|
|
570
|
-
id,
|
|
571
|
-
message: `${label} duplicate id ${JSON.stringify(id)} (后者覆盖前者会静默丢卡)`
|
|
572
|
-
});
|
|
573
|
-
else seenIds.add(id);
|
|
574
|
-
}
|
|
575
|
-
return errors;
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* 逐条调用引擎权威校验并渲染预览文件。
|
|
579
|
-
* 逐条(而非整组一次)校验保证一条坏配置不吞掉其余错误,且 index 可直接映射。
|
|
580
|
-
*/
|
|
581
|
-
async function validatePromptConfigs(value, options = {}) {
|
|
582
|
-
const errors = shapeErrors(value);
|
|
583
|
-
if (errors.length > 0 || !Array.isArray(value)) return {
|
|
584
|
-
valid: false,
|
|
585
|
-
errors
|
|
586
|
-
};
|
|
587
|
-
const specs = value;
|
|
588
|
-
const { createPromptConfigs } = await import(pathToFileURL(join(packageEngineDir(), "prompt-config-engine.mjs")).href);
|
|
589
|
-
const engineOptions = {
|
|
590
|
-
...options.strategyDir ? { strategyDir: options.strategyDir } : {},
|
|
591
|
-
...options.moduleDir ? { templateBaseUrl: pathToFileURL(join(dirname(options.moduleDir), ".engine", "schema.mjs")).href } : {}
|
|
592
|
-
};
|
|
593
|
-
const files = [];
|
|
594
|
-
for (let index = 0; index < specs.length; index += 1) {
|
|
595
|
-
const spec = specs[index];
|
|
596
|
-
const id = spec !== null && typeof spec === "object" && typeof spec.id === "string" ? spec.id : "";
|
|
597
|
-
try {
|
|
598
|
-
createPromptConfigs([spec], engineOptions);
|
|
599
|
-
files.push({
|
|
600
|
-
file: configFileName(index * 10, spec.id),
|
|
601
|
-
content: renderPromptConfigYaml(spec)
|
|
602
|
-
});
|
|
603
|
-
} catch (error) {
|
|
604
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
605
|
-
errors.push({
|
|
606
|
-
index,
|
|
607
|
-
id,
|
|
608
|
-
message
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
if (errors.length > 0) return {
|
|
613
|
-
valid: false,
|
|
614
|
-
errors
|
|
615
|
-
};
|
|
616
|
-
return {
|
|
617
|
-
valid: true,
|
|
618
|
-
errors: [],
|
|
619
|
-
configs: specs,
|
|
620
|
-
files
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
//#endregion
|
|
624
469
|
//#region src/host/templates.ts
|
|
625
470
|
/**
|
|
626
471
|
* 默认提示词配置模板库:包内 templates/ 目录的只读扫描与解析。
|
|
@@ -637,6 +482,7 @@ function loadPromptTemplates() {
|
|
|
637
482
|
const content = readFileSync(join(TEMPLATES_DIR, entry.name), "utf8");
|
|
638
483
|
const parsed = parse(content, { logLevel: "silent" });
|
|
639
484
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed) || typeof parsed.id !== "string" || parsed.id.length === 0) throw new Error(`prompt config template ${entry.name} must contain a single config object with a non-empty string id`);
|
|
485
|
+
compileRules([parsed]);
|
|
640
486
|
return {
|
|
641
487
|
file: entry.name,
|
|
642
488
|
content,
|
|
@@ -1358,7 +1204,6 @@ const BRIDGE_ENDPOINTS = {
|
|
|
1358
1204
|
models: "/models",
|
|
1359
1205
|
modelReasoning: "/model-reasoning",
|
|
1360
1206
|
mutate: "/mutate",
|
|
1361
|
-
configsValidate: "/configs-validate",
|
|
1362
1207
|
skillsList: "/skills-list",
|
|
1363
1208
|
skillPolicy: "/skill-policy",
|
|
1364
1209
|
skillRead: "/skill-read",
|
|
@@ -1372,17 +1217,18 @@ const BRIDGE_ENDPOINTS = {
|
|
|
1372
1217
|
promptConfigs: "/prompt-configs",
|
|
1373
1218
|
agentsFile: "/agents-file",
|
|
1374
1219
|
instructionsPolicy: "/instructions-policy",
|
|
1375
|
-
|
|
1220
|
+
moduleContent: "/module-content",
|
|
1376
1221
|
importPreset: "/import-preset",
|
|
1377
1222
|
paramOverrides: "/param-overrides",
|
|
1378
1223
|
triggers: "/triggers",
|
|
1224
|
+
rules: "/rules",
|
|
1379
1225
|
persona: "/persona",
|
|
1380
|
-
|
|
1226
|
+
moduleVariables: "/module-variables",
|
|
1381
1227
|
customTools: "/custom-tools",
|
|
1382
|
-
|
|
1228
|
+
importModulePackage: "/import-module-package",
|
|
1383
1229
|
assetUpload: "/asset-upload",
|
|
1384
1230
|
assetRelease: "/asset-release",
|
|
1385
|
-
|
|
1231
|
+
exportModule: "/export-module",
|
|
1386
1232
|
moduleDelete: "/module-delete",
|
|
1387
1233
|
moduleClone: "/module-clone",
|
|
1388
1234
|
moduleDuplicate: "/module-duplicate",
|
|
@@ -1391,18 +1237,16 @@ const BRIDGE_ENDPOINTS = {
|
|
|
1391
1237
|
moduleConfigOrder: "/module-config-order",
|
|
1392
1238
|
charactersImport: "/characters-import",
|
|
1393
1239
|
charactersImportStream: "/characters-import-stream",
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
charactersApply: "/characters-apply",
|
|
1397
|
-
charactersRemove: "/characters-remove",
|
|
1240
|
+
moduleMerge: "/module-merge",
|
|
1241
|
+
moduleUnmerge: "/module-unmerge",
|
|
1398
1242
|
subagentToolPolicy: "/subagent-tool-policy",
|
|
1399
1243
|
subagentToolPolicyPreview: "/subagent-tool-policy-preview",
|
|
1400
1244
|
toolSurface: "/tool-surface",
|
|
1401
|
-
|
|
1245
|
+
moduleCapability: "/module-capability",
|
|
1402
1246
|
worldBookDiagnostics: "/world-book-diagnostics"
|
|
1403
1247
|
};
|
|
1404
1248
|
/** 文件已保存,但宿主未采用新定义;调用方保留草稿并显示失败,不宣称已生效。 */
|
|
1405
|
-
const
|
|
1249
|
+
const MODULE_ACTIVATION_FAILED = "module-activation-failed";
|
|
1406
1250
|
const MODULE_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
1407
1251
|
/** 读编辑目标:形状不合法(空、超长、含非法字符)一律当「未声明」,由 host 回退启用表首项。 */
|
|
1408
1252
|
function readEditTarget(value) {
|
|
@@ -2600,17 +2444,20 @@ function installPreStepCoordinator(ctx, options = {}) {
|
|
|
2600
2444
|
const scope = isRecord$1(agent) && agent.ctx !== void 0 ? scopeOf(agent.ctx) : void 0;
|
|
2601
2445
|
const sources = [...layers.merge(scope, (layer) => layer.entries).values()];
|
|
2602
2446
|
const configs = sources.flatMap((source) => source.configs).filter((config) => !config.id.startsWith("agents-file-") && config.sourceKind !== "instruction-file").sort(compareConfigSequence);
|
|
2447
|
+
const ruleActions = sources.flatMap((source) => source.ruleActions ?? []);
|
|
2603
2448
|
const sessionId = isRecord$1(session) && typeof session.id === "string" ? session.id : void 0;
|
|
2604
2449
|
if (sessionId !== void 0) {
|
|
2605
2450
|
if (officialOwner.size >= MAX_TRACKED_OWNER_SESSIONS) officialOwner.clear();
|
|
2606
2451
|
if (sources.length > 0) officialOwner.set(sessionId, sources.some((source) => source.officialInstructions === true));
|
|
2607
2452
|
else officialOwner.delete(sessionId);
|
|
2608
2453
|
}
|
|
2609
|
-
return configs.length === 0 ? decision : await runPreStepBatch({
|
|
2454
|
+
return configs.length === 0 && ruleActions.length === 0 ? decision : await runPreStepBatch({
|
|
2610
2455
|
ctx,
|
|
2611
2456
|
agent,
|
|
2457
|
+
payload,
|
|
2612
2458
|
decision,
|
|
2613
2459
|
configs,
|
|
2460
|
+
ruleActions,
|
|
2614
2461
|
promotion,
|
|
2615
2462
|
memo,
|
|
2616
2463
|
warnOnce
|
|
@@ -2707,7 +2554,15 @@ function writeBridgeJson(res, status, body) {
|
|
|
2707
2554
|
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2708
2555
|
/** 当前层参数格式错误统一反馈,不能在读回时伪装成空配置。 */
|
|
2709
2556
|
function writeLayerSettingsError(res, error) {
|
|
2710
|
-
if (
|
|
2557
|
+
if (error instanceof ModuleRulesError) {
|
|
2558
|
+
writeBridgeJson(res, error.status, {
|
|
2559
|
+
ok: false,
|
|
2560
|
+
code: error.code,
|
|
2561
|
+
message: error.message
|
|
2562
|
+
});
|
|
2563
|
+
return true;
|
|
2564
|
+
}
|
|
2565
|
+
if (!(error instanceof ModuleLayerSettingsError)) return false;
|
|
2711
2566
|
writeBridgeJson(res, 400, {
|
|
2712
2567
|
ok: false,
|
|
2713
2568
|
code: error.code,
|
|
@@ -2900,10 +2755,10 @@ function withSnapshot(card, file) {
|
|
|
2900
2755
|
* 生成目录里属于其他工作区的文件卡不再展示(写盘白名单也不含它们);
|
|
2901
2756
|
* 当前工作区里没有对应生成卡的文件按默认文件卡补齐。
|
|
2902
2757
|
*/
|
|
2903
|
-
function mergeInstructionCards(
|
|
2758
|
+
function mergeInstructionCards(moduleCards, files) {
|
|
2904
2759
|
const byId = new Map(files.map((file) => [file.fileId, file]));
|
|
2905
2760
|
const kept = [];
|
|
2906
|
-
for (const card of
|
|
2761
|
+
for (const card of moduleCards) {
|
|
2907
2762
|
if (!isFileCardSpec(card)) {
|
|
2908
2763
|
kept.push(card);
|
|
2909
2764
|
continue;
|
|
@@ -2956,16 +2811,16 @@ function projectToolSchemas(schemas) {
|
|
|
2956
2811
|
}
|
|
2957
2812
|
return [...unique.values()].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
2958
2813
|
}
|
|
2959
|
-
function
|
|
2814
|
+
function isEditableModuleDir(dir, moduleRoot = userModulesDir()) {
|
|
2960
2815
|
const root = resolve(moduleRoot);
|
|
2961
2816
|
const target = resolve(dir);
|
|
2962
2817
|
return target !== root && target.startsWith(root + sep);
|
|
2963
2818
|
}
|
|
2964
|
-
function
|
|
2965
|
-
if (
|
|
2819
|
+
function guardEditableModuleDir(dir, res) {
|
|
2820
|
+
if (isEditableModuleDir(dir)) return true;
|
|
2966
2821
|
writeBridgeJson(res, 403, {
|
|
2967
2822
|
ok: false,
|
|
2968
|
-
code: "
|
|
2823
|
+
code: "module-readonly",
|
|
2969
2824
|
message: "system preset 只读,请先复制为用户模块"
|
|
2970
2825
|
});
|
|
2971
2826
|
return false;
|
|
@@ -3079,31 +2934,31 @@ function customToolModules(tools) {
|
|
|
3079
2934
|
return [...modules];
|
|
3080
2935
|
}
|
|
3081
2936
|
/** 自建 loopback settings bridge:替代 registerConfigurableProviders,避免模型设置区出现插件条目。 */
|
|
3082
|
-
function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState,
|
|
2937
|
+
function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, _getEngineStrategyDir, afterSkillsChange, getModuleDirectory, afterModuleContentImport, afterOverridesChange, afterModulePackageImport, afterCapabilityChange, afterModuleListChange) {
|
|
3083
2938
|
let invalidateCachedDescriptor = () => {};
|
|
3084
2939
|
let capabilityQueue = Promise.resolve();
|
|
3085
2940
|
let moduleOrderQueue = Promise.resolve();
|
|
3086
2941
|
/** 写盘已完成:等待重建,失败立即返回统一错误,不覆盖持久化结果。 */
|
|
3087
|
-
const
|
|
2942
|
+
const finishModuleChange = async (res, activate) => {
|
|
3088
2943
|
try {
|
|
3089
2944
|
await activate();
|
|
3090
2945
|
return true;
|
|
3091
2946
|
} catch (error) {
|
|
3092
2947
|
writeBridgeJson(res, 500, {
|
|
3093
2948
|
ok: false,
|
|
3094
|
-
code:
|
|
2949
|
+
code: MODULE_ACTIVATION_FAILED,
|
|
3095
2950
|
message: `更改已保存,但模块未生效;请重试:${String(error)}`
|
|
3096
2951
|
});
|
|
3097
2952
|
return false;
|
|
3098
2953
|
}
|
|
3099
2954
|
};
|
|
3100
|
-
const runOverridesChange = (res, moduleId) =>
|
|
2955
|
+
const runOverridesChange = (res, moduleId) => finishModuleChange(res, () => afterOverridesChange?.(moduleId));
|
|
3101
2956
|
/**
|
|
3102
2957
|
* 本次请求的编辑目标目录:读 `x-module-id` 头(形状不合法当未声明),交给注入的实现定位。
|
|
3103
2958
|
* 所有「读/写哪个模块的配置」都走这里,端点不再各自记一个全局目标。
|
|
3104
2959
|
*/
|
|
3105
|
-
const editDir = (req) =>
|
|
3106
|
-
const
|
|
2960
|
+
const editDir = (req) => getModuleDirectory?.(readEditTarget(req.headers["x-module-id"])) ?? "";
|
|
2961
|
+
const refreshModuleList = (res, id) => finishModuleChange(res, () => afterModuleListChange?.(id));
|
|
3107
2962
|
ctx.inject(["settings", "webServer"], (sctx) => {
|
|
3108
2963
|
sctx.effect(() => {
|
|
3109
2964
|
const assetSources = createAssetSources(join(DSH_HOME, ".prompt-tool-uploads"));
|
|
@@ -3118,12 +2973,16 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3118
2973
|
return cachedDescriptor;
|
|
3119
2974
|
};
|
|
3120
2975
|
/** 已解析的编辑目标只投影到本次响应,不改宿主设置或缓存描述符。 */
|
|
3121
|
-
const descriptorForTarget = (descriptor, dir) =>
|
|
3122
|
-
...descriptor
|
|
3123
|
-
value
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
2976
|
+
const descriptorForTarget = (descriptor, dir) => {
|
|
2977
|
+
const value = { ...asRecord(descriptor.value) };
|
|
2978
|
+
value.modulesEnabled = readModulesEnabled(value);
|
|
2979
|
+
delete value.writePreset;
|
|
2980
|
+
delete value.presetTemplate;
|
|
2981
|
+
if (dir.length > 0) value.moduleId = basename(dir);
|
|
2982
|
+
return {
|
|
2983
|
+
...descriptor,
|
|
2984
|
+
value
|
|
2985
|
+
};
|
|
3127
2986
|
};
|
|
3128
2987
|
/** mutate 成功后强制失效,下次 findDescriptor 重查宿主(响应必须带新 view)。 */
|
|
3129
2988
|
const invalidateDescriptor = () => {
|
|
@@ -3149,16 +3008,16 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3149
3008
|
}
|
|
3150
3009
|
return true;
|
|
3151
3010
|
};
|
|
3152
|
-
const
|
|
3011
|
+
const guardModuleFormat = (dir, res) => {
|
|
3153
3012
|
if (dir.length === 0) return true;
|
|
3154
3013
|
try {
|
|
3155
|
-
|
|
3014
|
+
readModuleLayerSettings(loadModuleDefinition(dir).source);
|
|
3156
3015
|
} catch (error) {
|
|
3157
3016
|
if (writeLayerSettingsError(res, error)) return false;
|
|
3158
3017
|
}
|
|
3159
3018
|
return true;
|
|
3160
3019
|
};
|
|
3161
|
-
const
|
|
3020
|
+
const guardModuleWrite = (dir, res) => guardEditableModuleDir(dir, res) && guardModuleFormat(dir, res);
|
|
3162
3021
|
const writeModuleOrderError = (res, error) => {
|
|
3163
3022
|
writeBridgeJson(res, error instanceof ModuleConfigOrderError ? error.status : 500, {
|
|
3164
3023
|
ok: false,
|
|
@@ -3167,25 +3026,33 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3167
3026
|
});
|
|
3168
3027
|
};
|
|
3169
3028
|
/** 模块身份只作一致性检查,绝不用客户端 ID 构造写入路径。 */
|
|
3170
|
-
const
|
|
3171
|
-
|
|
3029
|
+
const guardModuleIdentity = (req, record, dir, res) => {
|
|
3030
|
+
if (record.expectedModuleId !== void 0 && record.expectedPresetId !== void 0 && record.expectedModuleId !== record.expectedPresetId) {
|
|
3031
|
+
writeBridgeJson(res, 400, {
|
|
3032
|
+
ok: false,
|
|
3033
|
+
code: "module-identity-invalid",
|
|
3034
|
+
message: "新旧模块身份字段冲突"
|
|
3035
|
+
});
|
|
3036
|
+
return false;
|
|
3037
|
+
}
|
|
3038
|
+
const expected = record.expectedModuleId ?? record.expectedPresetId;
|
|
3172
3039
|
if (expected !== void 0 && (typeof expected !== "string" || expected.length === 0 || expected.length > 256)) {
|
|
3173
3040
|
writeBridgeJson(res, 400, {
|
|
3174
3041
|
ok: false,
|
|
3175
|
-
code: "
|
|
3176
|
-
message: "
|
|
3042
|
+
code: "module-identity-invalid",
|
|
3043
|
+
message: "expectedModuleId 必须是非空模块 ID"
|
|
3177
3044
|
});
|
|
3178
3045
|
return false;
|
|
3179
3046
|
}
|
|
3180
3047
|
if (editDir(req) !== dir || expected !== void 0 && expected !== basename(dir)) {
|
|
3181
3048
|
writeBridgeJson(res, 409, {
|
|
3182
3049
|
ok: false,
|
|
3183
|
-
code: "
|
|
3050
|
+
code: "module-changed",
|
|
3184
3051
|
message: "当前模块已切换;旧草稿未写入,请重新读取后保存"
|
|
3185
3052
|
});
|
|
3186
3053
|
return false;
|
|
3187
3054
|
}
|
|
3188
|
-
return
|
|
3055
|
+
return guardModuleFormat(dir, res);
|
|
3189
3056
|
};
|
|
3190
3057
|
/** 官方刻度取值失败只告警一次:本函数被 /meta 与 /bootstrap 共用,不能每次调用都刷屏。 */
|
|
3191
3058
|
let warnedOfficialOrders = false;
|
|
@@ -3193,16 +3060,13 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3193
3060
|
const loadEngineMeta = async () => {
|
|
3194
3061
|
const { getEngineMeta } = await import(pathToFileURL(join(packageEngineDir(), "schema.mjs")).href);
|
|
3195
3062
|
const meta = getEngineMeta();
|
|
3196
|
-
const registry = sctx.get?.("agentPresets");
|
|
3197
|
-
const roster = registry === void 0 ? [] : await registry.list();
|
|
3198
|
-
const diagnostics = new Map(roster.map(({ id, broken }) => [id, broken]));
|
|
3199
3063
|
const enabledSet = new Set(enabledModuleIds(userModulesDir()));
|
|
3200
|
-
meta.
|
|
3064
|
+
meta.modules = listModules().map((preset) => ({
|
|
3201
3065
|
...preset,
|
|
3202
|
-
enabled: enabledSet.has(preset.id)
|
|
3203
|
-
...diagnostics.get(preset.id) === void 0 ? {} : { broken: diagnostics.get(preset.id) }
|
|
3066
|
+
enabled: enabledSet.has(preset.id)
|
|
3204
3067
|
}));
|
|
3205
3068
|
meta.builtinTemplates = listBuiltinTemplates();
|
|
3069
|
+
if (modulePathExists(join(dirname(userModulesDir()), ".characters"))) meta.moduleWarnings = ["检测到旧 .characters 角色库;其内容尚未作为模块导入。请通过模块导入入口显式导入,旧目录不会自动迁移或删除。"];
|
|
3206
3070
|
meta.editorGroups = ENGINE_EDITOR_GROUP_MAP.map(({ id, displayLayer, relatedLayers, hook }) => ({
|
|
3207
3071
|
id,
|
|
3208
3072
|
displayLayer,
|
|
@@ -3272,15 +3136,15 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3272
3136
|
}
|
|
3273
3137
|
} catch {}
|
|
3274
3138
|
const modelCatalog = peekModelCatalog(sctx);
|
|
3275
|
-
let
|
|
3139
|
+
let moduleParams = {};
|
|
3276
3140
|
let moduleFacts;
|
|
3277
3141
|
let templatePreStepCount = 0;
|
|
3278
3142
|
try {
|
|
3279
3143
|
const templateName = activeDir.length > 0 ? basename(activeDir) : DEFAULT_MODULE_ID;
|
|
3280
3144
|
const spec = loadModuleSpec(activeDir.length > 0 ? activeDir : resolveModuleDir(templateName));
|
|
3281
|
-
|
|
3282
|
-
const resolvedFacts = resolveModuleFacts(spec, activeDir.length > 0 ? activeDir : void 0,
|
|
3283
|
-
mergeModuleConfigFallbacks(
|
|
3145
|
+
moduleParams = resolveModuleParams(spec, {});
|
|
3146
|
+
const resolvedFacts = resolveModuleFacts(spec, activeDir.length > 0 ? activeDir : void 0, isEditableModuleDir(activeDir.length > 0 ? activeDir : resolveModuleDir(templateName)));
|
|
3147
|
+
mergeModuleConfigFallbacks(moduleParams, resolvedFacts);
|
|
3284
3148
|
moduleFacts = {
|
|
3285
3149
|
declaredModules: resolvedFacts.declaredModules,
|
|
3286
3150
|
effectiveModules: resolvedFacts.effectiveModules,
|
|
@@ -3289,17 +3153,13 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3289
3153
|
editable: resolvedFacts.editable,
|
|
3290
3154
|
subagentToolPolicyEnabled: resolvedFacts.subagentToolPolicyEnabled === true
|
|
3291
3155
|
};
|
|
3292
|
-
|
|
3293
|
-
templatePreStepCount = (spec.promptConfigs ?? []).filter((config) => {
|
|
3294
|
-
const layer = config.layer;
|
|
3295
|
-
return layer === void 0 || layer === "pre-step";
|
|
3296
|
-
}).length;
|
|
3156
|
+
templatePreStepCount = (spec.rules ?? []).filter((rule) => rule.layer === "pre-step").length;
|
|
3297
3157
|
} catch (error) {
|
|
3298
|
-
if (error instanceof
|
|
3158
|
+
if (error instanceof ModuleLayerSettingsError || error instanceof ModuleRulesError) throw error;
|
|
3299
3159
|
templatePreStepCount = 0;
|
|
3300
3160
|
}
|
|
3301
3161
|
return {
|
|
3302
|
-
|
|
3162
|
+
moduleParams,
|
|
3303
3163
|
moduleFacts,
|
|
3304
3164
|
hostDefaultModel,
|
|
3305
3165
|
templatePreStepCount,
|
|
@@ -3313,7 +3173,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3313
3173
|
skillFolders: skillsState.folders
|
|
3314
3174
|
};
|
|
3315
3175
|
};
|
|
3316
|
-
/** 激活模块的引擎参数子集(/param-overrides 读取;
|
|
3176
|
+
/** 激活模块的引擎参数子集(/param-overrides 读取;module.yml 按 mtime 缓存)。 */
|
|
3317
3177
|
const readParamOverrides = (dir) => {
|
|
3318
3178
|
try {
|
|
3319
3179
|
const spec = loadModuleSpec(dir);
|
|
@@ -3324,45 +3184,18 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3324
3184
|
}
|
|
3325
3185
|
return params;
|
|
3326
3186
|
} catch (error) {
|
|
3327
|
-
if (error instanceof
|
|
3187
|
+
if (error instanceof ModuleLayerSettingsError || error instanceof ModuleRulesError) throw error;
|
|
3328
3188
|
return {};
|
|
3329
3189
|
}
|
|
3330
3190
|
};
|
|
3331
|
-
/**
|
|
3332
|
-
const
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
enabled: spec.variablesEnabled !== false
|
|
3340
|
-
};
|
|
3341
|
-
} catch (error) {
|
|
3342
|
-
if (error instanceof PresetLayerSettingsError) throw error;
|
|
3343
|
-
return {
|
|
3344
|
-
variables: {},
|
|
3345
|
-
enabled: true
|
|
3346
|
-
};
|
|
3347
|
-
}
|
|
3348
|
-
};
|
|
3349
|
-
/** 生成目录实际生效配置(/prompt-configs 读取)。 */
|
|
3350
|
-
const readPromptConfigs = (dir) => {
|
|
3351
|
-
try {
|
|
3352
|
-
const order = loadModuleSpec(dir).configOrder ?? {};
|
|
3353
|
-
return dir.length > 0 ? listPromptConfigSpecs(join(dir, MODULE_CONFIGS_DIR)).map((config) => {
|
|
3354
|
-
const raw = config;
|
|
3355
|
-
const definition = stripConfigFieldSources(raw);
|
|
3356
|
-
const fieldSources = readConfigFieldSources(config.id, raw.fieldSources);
|
|
3357
|
-
return {
|
|
3358
|
-
...definition,
|
|
3359
|
-
...fieldSources === void 0 ? {} : { fieldSources },
|
|
3360
|
-
...Object.hasOwn(order, config.id) ? { sequence: order[config.id] } : {}
|
|
3361
|
-
};
|
|
3362
|
-
}) : [];
|
|
3363
|
-
} catch {
|
|
3364
|
-
return [];
|
|
3365
|
-
}
|
|
3191
|
+
/** 完整已提交定义的只读变量快照,不因发布失败而再次触发切片恢复。 */
|
|
3192
|
+
const readModuleVariables = (dir) => {
|
|
3193
|
+
const snapshot = loadModuleDefinition(dir);
|
|
3194
|
+
return {
|
|
3195
|
+
variables: snapshot.variables,
|
|
3196
|
+
enabled: snapshot.variablesEnabled,
|
|
3197
|
+
revisions: snapshot.revisions
|
|
3198
|
+
};
|
|
3366
3199
|
};
|
|
3367
3200
|
const disposers = [
|
|
3368
3201
|
assetSources.dispose,
|
|
@@ -3396,16 +3229,18 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3396
3229
|
const meta = await loadEngineMeta();
|
|
3397
3230
|
const extras = await collectDescribeExtras(dir, session.sessionId);
|
|
3398
3231
|
const scope = resolveInstructionScope(sctx, session.sessionId);
|
|
3232
|
+
const variables = dir.length > 0 ? readModuleVariables(dir) : {
|
|
3233
|
+
variables: {},
|
|
3234
|
+
enabled: true
|
|
3235
|
+
};
|
|
3399
3236
|
writeBridgeJson(res, 200, {
|
|
3400
3237
|
ok: true,
|
|
3401
3238
|
value: descriptorForTarget(descriptor, dir),
|
|
3402
3239
|
meta: { meta },
|
|
3403
3240
|
overrides: { overrides: dir.length > 0 ? readParamOverrides(dir) : {} },
|
|
3404
|
-
variables
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
},
|
|
3408
|
-
promptConfigs: { promptConfigs: mergeInstructionCards(readPromptConfigs(dir), scope.files) },
|
|
3241
|
+
variables,
|
|
3242
|
+
..."revisions" in variables ? { variablesRevisions: variables.revisions } : {},
|
|
3243
|
+
promptConfigs: { promptConfigs: mergeInstructionCards([], scope.files) },
|
|
3409
3244
|
instructions: {
|
|
3410
3245
|
context: scope.context,
|
|
3411
3246
|
files: scope.files,
|
|
@@ -3554,7 +3389,21 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3554
3389
|
}
|
|
3555
3390
|
const expectedRevision = typeof record.expectedRevision === "number" ? record.expectedRevision : void 0;
|
|
3556
3391
|
try {
|
|
3557
|
-
|
|
3392
|
+
const ops = record.ops;
|
|
3393
|
+
const toggles = ops.filter((op) => op.path.length === 1 && ["modulesEnabled", "writePreset"].includes(String(op.path[0])));
|
|
3394
|
+
const values = Object.fromEntries(toggles.filter((op) => op.op === "set").map((op) => [op.path[0], op.value]));
|
|
3395
|
+
if (toggles.length > 0) {
|
|
3396
|
+
readModulesEnabled(values);
|
|
3397
|
+
const normalized = ops.map((op) => op.path.length === 1 && op.path[0] === "writePreset" ? {
|
|
3398
|
+
...op,
|
|
3399
|
+
path: ["modulesEnabled"]
|
|
3400
|
+
} : op);
|
|
3401
|
+
normalized.push({
|
|
3402
|
+
op: "unset",
|
|
3403
|
+
path: ["writePreset"]
|
|
3404
|
+
});
|
|
3405
|
+
await sctx.settings.mutate(ns, normalized, expectedRevision);
|
|
3406
|
+
} else await sctx.settings.mutate(ns, ops, expectedRevision);
|
|
3558
3407
|
} catch (error) {
|
|
3559
3408
|
writeBridgeJson(res, 409, {
|
|
3560
3409
|
ok: false,
|
|
@@ -3575,41 +3424,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3575
3424
|
}
|
|
3576
3425
|
writeBridgeJson(res, 200, {
|
|
3577
3426
|
ok: true,
|
|
3578
|
-
value: descriptor
|
|
3579
|
-
});
|
|
3580
|
-
}
|
|
3581
|
-
}),
|
|
3582
|
-
sctx.webServer.register({
|
|
3583
|
-
kind: "exact",
|
|
3584
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.configsValidate,
|
|
3585
|
-
handler: async (req, res) => {
|
|
3586
|
-
if (!guard(req, res)) return;
|
|
3587
|
-
const parsedBody = await readBridgeBodyForHandler(req, res);
|
|
3588
|
-
if (parsedBody === void 0) return;
|
|
3589
|
-
const { body } = parsedBody;
|
|
3590
|
-
if (body === null || body === void 0 || typeof body !== "object") {
|
|
3591
|
-
writeBridgeJson(res, 400, {
|
|
3592
|
-
ok: false,
|
|
3593
|
-
code: "settings-rejected",
|
|
3594
|
-
message: "unreadable JSON body"
|
|
3595
|
-
});
|
|
3596
|
-
return;
|
|
3597
|
-
}
|
|
3598
|
-
const record = body;
|
|
3599
|
-
if (!Array.isArray(record.promptConfigs)) {
|
|
3600
|
-
writeBridgeJson(res, 400, {
|
|
3601
|
-
ok: false,
|
|
3602
|
-
code: "prompt-configs-invalid",
|
|
3603
|
-
message: "promptConfigs must be an array"
|
|
3604
|
-
});
|
|
3605
|
-
return;
|
|
3606
|
-
}
|
|
3607
|
-
writeBridgeJson(res, 200, {
|
|
3608
|
-
ok: true,
|
|
3609
|
-
value: await validatePromptConfigs(record.promptConfigs, {
|
|
3610
|
-
strategyDir: getEngineStrategyDir(),
|
|
3611
|
-
moduleDir: editDir(req)
|
|
3612
|
-
})
|
|
3427
|
+
value: descriptorForTarget(descriptor, editDir(req))
|
|
3613
3428
|
});
|
|
3614
3429
|
}
|
|
3615
3430
|
}),
|
|
@@ -3971,12 +3786,12 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
3971
3786
|
return;
|
|
3972
3787
|
}
|
|
3973
3788
|
const dir = editDir(req) ?? "";
|
|
3974
|
-
if (!
|
|
3789
|
+
if (!guardModuleFormat(dir, res)) return;
|
|
3975
3790
|
const scope = resolveInstructionScope(sctx, session.sessionId);
|
|
3976
3791
|
writeBridgeJson(res, 200, {
|
|
3977
3792
|
ok: true,
|
|
3978
3793
|
value: {
|
|
3979
|
-
promptConfigs: mergeInstructionCards(
|
|
3794
|
+
promptConfigs: mergeInstructionCards([], scope.files),
|
|
3980
3795
|
instructions: {
|
|
3981
3796
|
context: scope.context,
|
|
3982
3797
|
files: scope.files,
|
|
@@ -4129,7 +3944,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4129
3944
|
}),
|
|
4130
3945
|
sctx.webServer.register({
|
|
4131
3946
|
kind: "exact",
|
|
4132
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.
|
|
3947
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.moduleContent,
|
|
4133
3948
|
handler: async (req, res) => {
|
|
4134
3949
|
if (!guard(req, res)) return;
|
|
4135
3950
|
const parsedBody = await readBridgeBodyForHandler(req, res);
|
|
@@ -4203,17 +4018,17 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4203
4018
|
if (dir.length === 0) {
|
|
4204
4019
|
writeBridgeJson(res, 400, {
|
|
4205
4020
|
ok: false,
|
|
4206
|
-
code: "
|
|
4021
|
+
code: "module-dir-unavailable",
|
|
4207
4022
|
message: "模块目录未配置"
|
|
4208
4023
|
});
|
|
4209
4024
|
return;
|
|
4210
4025
|
}
|
|
4211
|
-
if (!
|
|
4026
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
4212
4027
|
try {
|
|
4213
|
-
if (!
|
|
4028
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4214
4029
|
mkdirSync(dir, { recursive: true });
|
|
4215
4030
|
for (const entry of contents) writeFileSync(join(dir, entry.scope === "preset" ? "preset.md" : "agents.md"), entry.content, "utf8");
|
|
4216
|
-
if (!await
|
|
4031
|
+
if (!await finishModuleChange(res, () => afterModuleContentImport?.(contents.map((entry) => entry.scope), basename(dir)))) return;
|
|
4217
4032
|
writeBridgeJson(res, 200, {
|
|
4218
4033
|
ok: true,
|
|
4219
4034
|
value: { scopes: contents.map((entry) => entry.scope) }
|
|
@@ -4221,7 +4036,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4221
4036
|
} catch (error) {
|
|
4222
4037
|
writeBridgeJson(res, 500, {
|
|
4223
4038
|
ok: false,
|
|
4224
|
-
code: "
|
|
4039
|
+
code: "module-import-failed",
|
|
4225
4040
|
message: error instanceof Error ? error.message : String(error)
|
|
4226
4041
|
});
|
|
4227
4042
|
}
|
|
@@ -4230,13 +4045,25 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4230
4045
|
sctx.webServer.register({
|
|
4231
4046
|
kind: "exact",
|
|
4232
4047
|
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.triggers,
|
|
4048
|
+
handler: async (req, res) => {
|
|
4049
|
+
if (!guard(req, res)) return;
|
|
4050
|
+
writeBridgeJson(res, 410, {
|
|
4051
|
+
ok: false,
|
|
4052
|
+
code: "rules-route-retired",
|
|
4053
|
+
message: "声明规则已统一为 rules;请离线迁移旧模块并刷新客户端,使用 /rules 编辑。"
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
}),
|
|
4057
|
+
sctx.webServer.register({
|
|
4058
|
+
kind: "exact",
|
|
4059
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.rules,
|
|
4233
4060
|
handler: async (req, res) => {
|
|
4234
4061
|
if (!guard(req, res)) return;
|
|
4235
4062
|
const dir = editDir(req) ?? "";
|
|
4236
4063
|
if (dir.length === 0) {
|
|
4237
4064
|
writeBridgeJson(res, 400, {
|
|
4238
4065
|
ok: false,
|
|
4239
|
-
code: "
|
|
4066
|
+
code: "module-dir-unavailable",
|
|
4240
4067
|
message: "当前模块目录不可用"
|
|
4241
4068
|
});
|
|
4242
4069
|
return;
|
|
@@ -4244,95 +4071,139 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4244
4071
|
const parsed = await readBridgeBodyForHandler(req, res);
|
|
4245
4072
|
if (parsed === void 0) return;
|
|
4246
4073
|
const record = parsed.body ?? {};
|
|
4247
|
-
if (
|
|
4074
|
+
if (!isRecord(record) || Object.keys(record).some((key) => ![
|
|
4075
|
+
"expectedModuleId",
|
|
4248
4076
|
"expectedPresetId",
|
|
4249
|
-
"triggers",
|
|
4250
4077
|
"expectedRevision",
|
|
4251
|
-
"
|
|
4252
|
-
|
|
4078
|
+
"expectedRevisions",
|
|
4079
|
+
"edits",
|
|
4080
|
+
"activateRuleId",
|
|
4081
|
+
"validateOnly",
|
|
4082
|
+
"refreshOnly"
|
|
4083
|
+
].includes(key)) || typeof (record.expectedModuleId ?? record.expectedPresetId) !== "string" || (record.expectedModuleId ?? record.expectedPresetId) === "" || record.expectedRevision !== void 0 && (typeof record.expectedRevision !== "string" || !SHA256_HEX_RE.test(record.expectedRevision)) || record.edits !== void 0 && !Array.isArray(record.edits) || record.activateRuleId !== void 0 && (typeof record.activateRuleId !== "string" || record.activateRuleId.length === 0) || record.validateOnly !== void 0 && typeof record.validateOnly !== "boolean" || record.refreshOnly !== void 0 && typeof record.refreshOnly !== "boolean" || record.refreshOnly === true && (record.edits !== void 0 || record.activateRuleId !== void 0 || record.validateOnly !== void 0)) {
|
|
4253
4084
|
writeBridgeJson(res, 400, {
|
|
4254
4085
|
ok: false,
|
|
4255
|
-
code: "
|
|
4256
|
-
message: "
|
|
4086
|
+
code: "rules-invalid",
|
|
4087
|
+
message: "规则请求字段、模块身份或版本格式不合法"
|
|
4088
|
+
});
|
|
4089
|
+
return;
|
|
4090
|
+
}
|
|
4091
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4092
|
+
if (record.refreshOnly === true) {
|
|
4093
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
4094
|
+
const { getRuleEditorMeta } = await import(pathToFileURL(join(packageEngineDir(), "rule-spec.mjs")).href);
|
|
4095
|
+
try {
|
|
4096
|
+
await afterOverridesChange?.(basename(dir));
|
|
4097
|
+
} catch (error) {
|
|
4098
|
+
const committed = loadModuleDefinition(dir);
|
|
4099
|
+
writeBridgeJson(res, 200, {
|
|
4100
|
+
ok: true,
|
|
4101
|
+
value: {
|
|
4102
|
+
rules: committed.rules,
|
|
4103
|
+
revisions: committed.revisions,
|
|
4104
|
+
meta: getRuleEditorMeta(),
|
|
4105
|
+
persisted: true,
|
|
4106
|
+
publicationPending: true,
|
|
4107
|
+
publicationError: `运行刷新失败:${String(error)}`
|
|
4108
|
+
}
|
|
4109
|
+
});
|
|
4110
|
+
return;
|
|
4111
|
+
}
|
|
4112
|
+
const committed = loadModuleDefinition(dir);
|
|
4113
|
+
writeBridgeJson(res, 200, {
|
|
4114
|
+
ok: true,
|
|
4115
|
+
value: {
|
|
4116
|
+
rules: committed.rules,
|
|
4117
|
+
revisions: committed.revisions,
|
|
4118
|
+
meta: getRuleEditorMeta(),
|
|
4119
|
+
publicationPending: false
|
|
4120
|
+
}
|
|
4257
4121
|
});
|
|
4258
4122
|
return;
|
|
4259
4123
|
}
|
|
4260
|
-
|
|
4261
|
-
const writing =
|
|
4262
|
-
if (
|
|
4124
|
+
const editing = record.edits !== void 0 || record.activateRuleId !== void 0;
|
|
4125
|
+
const writing = editing && record.validateOnly !== true;
|
|
4126
|
+
if (editing && record.expectedRevisions === void 0 && typeof record.expectedRevision !== "string") {
|
|
4263
4127
|
writeBridgeJson(res, 400, {
|
|
4264
4128
|
ok: false,
|
|
4265
|
-
code: "
|
|
4266
|
-
message: "
|
|
4129
|
+
code: "rules-invalid",
|
|
4130
|
+
message: "修改规则必须提供读取时的 expectedRevisions"
|
|
4267
4131
|
});
|
|
4268
4132
|
return;
|
|
4269
4133
|
}
|
|
4270
|
-
if (writing && !
|
|
4134
|
+
if (writing && !guardModuleWrite(dir, res)) return;
|
|
4271
4135
|
try {
|
|
4272
|
-
const {
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4136
|
+
const { getRuleEditorMeta } = await import(pathToFileURL(join(packageEngineDir(), "rule-spec.mjs")).href);
|
|
4137
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4138
|
+
const candidate = editing ? editModuleRules(dir, {
|
|
4139
|
+
...record.expectedRevision === void 0 ? {} : { expectedRevision: record.expectedRevision },
|
|
4140
|
+
...record.expectedRevisions === void 0 ? {} : { expectedRevisions: record.expectedRevisions },
|
|
4141
|
+
...record.edits === void 0 ? {} : { edits: record.edits },
|
|
4142
|
+
...record.activateRuleId === void 0 ? {} : { activateRuleId: record.activateRuleId },
|
|
4143
|
+
...record.validateOnly === void 0 ? {} : { validateOnly: record.validateOnly }
|
|
4144
|
+
}) : isEditableModuleDir(dir) ? readModuleRules(dir) : ensureModuleSlices(dir, { writable: false });
|
|
4145
|
+
if (writing) try {
|
|
4146
|
+
await afterOverridesChange?.(basename(dir));
|
|
4279
4147
|
} catch (error) {
|
|
4280
|
-
writeBridgeJson(res,
|
|
4281
|
-
ok:
|
|
4282
|
-
|
|
4283
|
-
|
|
4148
|
+
writeBridgeJson(res, 200, {
|
|
4149
|
+
ok: true,
|
|
4150
|
+
value: {
|
|
4151
|
+
rules: candidate.rules,
|
|
4152
|
+
revisions: candidate.revisions,
|
|
4153
|
+
meta: getRuleEditorMeta(),
|
|
4154
|
+
persisted: true,
|
|
4155
|
+
publicationPending: true,
|
|
4156
|
+
publicationError: `规则已保存,但运行刷新失败:${String(error)}`
|
|
4157
|
+
}
|
|
4284
4158
|
});
|
|
4285
4159
|
return;
|
|
4286
4160
|
}
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
writeBridgeJson(res, 400, {
|
|
4290
|
-
ok: false,
|
|
4291
|
-
code: "triggers-composition-readonly",
|
|
4292
|
-
message: "当前组合没有可编辑的 modules 清单,无法自动装配声明引擎"
|
|
4293
|
-
});
|
|
4294
|
-
return;
|
|
4295
|
-
}
|
|
4296
|
-
if (!saveModuleTriggers(dir, record.triggers, record.expectedRevision)) {
|
|
4297
|
-
writeBridgeJson(res, 409, {
|
|
4298
|
-
ok: false,
|
|
4299
|
-
code: "triggers-conflict",
|
|
4300
|
-
message: "模块文件已变化;草稿未写入,请重新读取后保存"
|
|
4301
|
-
});
|
|
4302
|
-
return;
|
|
4303
|
-
}
|
|
4304
|
-
try {
|
|
4305
|
-
await afterOverridesChange?.(basename(dir));
|
|
4306
|
-
} catch (error) {
|
|
4307
|
-
writeBridgeJson(res, 500, {
|
|
4308
|
-
ok: false,
|
|
4309
|
-
code: "triggers-rebuild-failed",
|
|
4310
|
-
message: `声明已保存,但模块重建失败;请重新读取后重试:${String(error)}`
|
|
4311
|
-
});
|
|
4312
|
-
return;
|
|
4313
|
-
}
|
|
4314
|
-
}
|
|
4315
|
-
const result = writing ? readModuleTriggers(dir) : snapshot;
|
|
4316
|
-
if (writing && !isDeepStrictEqual(result.triggers, record.triggers)) {
|
|
4161
|
+
const result = writing ? readModuleRules(dir) : candidate;
|
|
4162
|
+
if (writing && !isDeepStrictEqual(result.rules, candidate.rules)) {
|
|
4317
4163
|
writeBridgeJson(res, 409, {
|
|
4318
4164
|
ok: false,
|
|
4319
|
-
code: "
|
|
4320
|
-
message: "
|
|
4165
|
+
code: "rules-conflict",
|
|
4166
|
+
message: "重建期间规则再次变化;请重新读取,当前草稿仍保留"
|
|
4321
4167
|
});
|
|
4322
4168
|
return;
|
|
4323
4169
|
}
|
|
4324
4170
|
writeBridgeJson(res, 200, {
|
|
4325
4171
|
ok: true,
|
|
4326
4172
|
value: {
|
|
4327
|
-
|
|
4328
|
-
|
|
4173
|
+
rules: result.rules,
|
|
4174
|
+
revisions: result.revisions,
|
|
4175
|
+
meta: getRuleEditorMeta()
|
|
4329
4176
|
}
|
|
4330
4177
|
});
|
|
4331
4178
|
} catch (error) {
|
|
4179
|
+
if (error instanceof ModuleRulesError) {
|
|
4180
|
+
if (error.persisted) {
|
|
4181
|
+
const committed = loadModuleDefinition(dir);
|
|
4182
|
+
const { getRuleEditorMeta } = await import(pathToFileURL(join(packageEngineDir(), "rule-spec.mjs")).href);
|
|
4183
|
+
writeBridgeJson(res, 200, {
|
|
4184
|
+
ok: true,
|
|
4185
|
+
value: {
|
|
4186
|
+
rules: committed.rules,
|
|
4187
|
+
revisions: committed.revisions,
|
|
4188
|
+
meta: getRuleEditorMeta(),
|
|
4189
|
+
persisted: true,
|
|
4190
|
+
publicationPending: true,
|
|
4191
|
+
publicationError: error.message
|
|
4192
|
+
}
|
|
4193
|
+
});
|
|
4194
|
+
return;
|
|
4195
|
+
}
|
|
4196
|
+
writeBridgeJson(res, error.status, {
|
|
4197
|
+
ok: false,
|
|
4198
|
+
code: error.code,
|
|
4199
|
+
message: error.message
|
|
4200
|
+
});
|
|
4201
|
+
return;
|
|
4202
|
+
}
|
|
4332
4203
|
if (writeLayerSettingsError(res, error)) return;
|
|
4333
4204
|
writeBridgeJson(res, 500, {
|
|
4334
4205
|
ok: false,
|
|
4335
|
-
code: "
|
|
4206
|
+
code: "rules-failed",
|
|
4336
4207
|
message: String(error.message ?? error)
|
|
4337
4208
|
});
|
|
4338
4209
|
}
|
|
@@ -4347,7 +4218,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4347
4218
|
if (dir.length === 0) {
|
|
4348
4219
|
writeBridgeJson(res, 400, {
|
|
4349
4220
|
ok: false,
|
|
4350
|
-
code: "
|
|
4221
|
+
code: "module-dir-unavailable",
|
|
4351
4222
|
message: "模块目录未配置"
|
|
4352
4223
|
});
|
|
4353
4224
|
return;
|
|
@@ -4358,7 +4229,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4358
4229
|
if (parsedBody === void 0) return;
|
|
4359
4230
|
const { body } = parsedBody;
|
|
4360
4231
|
const record = body ?? {};
|
|
4361
|
-
if (!
|
|
4232
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4362
4233
|
if (record.rebuild !== void 0 && typeof record.rebuild !== "boolean") {
|
|
4363
4234
|
writeBridgeJson(res, 400, {
|
|
4364
4235
|
ok: false,
|
|
@@ -4367,7 +4238,15 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4367
4238
|
});
|
|
4368
4239
|
return;
|
|
4369
4240
|
}
|
|
4370
|
-
if (record.
|
|
4241
|
+
if (record.promptConfigs !== void 0) {
|
|
4242
|
+
writeBridgeJson(res, 410, {
|
|
4243
|
+
ok: false,
|
|
4244
|
+
code: "rules-route-retired",
|
|
4245
|
+
message: "提示词配置已统一为 rules,请使用带版本的 /rules 事务编辑。"
|
|
4246
|
+
});
|
|
4247
|
+
return;
|
|
4248
|
+
}
|
|
4249
|
+
if (record.overrides === void 0) {
|
|
4371
4250
|
if (record.rebuild !== void 0) {
|
|
4372
4251
|
writeBridgeJson(res, 400, {
|
|
4373
4252
|
ok: false,
|
|
@@ -4390,17 +4269,16 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4390
4269
|
});
|
|
4391
4270
|
return;
|
|
4392
4271
|
}
|
|
4393
|
-
if (
|
|
4394
|
-
|
|
4272
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
4273
|
+
const rawOverrides = record.overrides;
|
|
4274
|
+
if (rawOverrides !== void 0 && RULE_OWNED_MODEL_PARAMS.some((key) => Object.hasOwn(rawOverrides, key))) {
|
|
4275
|
+
writeBridgeJson(res, 410, {
|
|
4395
4276
|
ok: false,
|
|
4396
|
-
code: "
|
|
4397
|
-
message: "
|
|
4277
|
+
code: "rules-route-retired",
|
|
4278
|
+
message: "模型路由和采样参数已归入 request-params 规则动作,请通过 /rules 编辑。"
|
|
4398
4279
|
});
|
|
4399
4280
|
return;
|
|
4400
4281
|
}
|
|
4401
|
-
if (!guardPresetWrite(dir, res)) return;
|
|
4402
|
-
if (Array.isArray(record.promptConfigs)) record.promptConfigs = record.promptConfigs.map((config) => isRecord(config) ? stripConfigFieldSources(config) : config);
|
|
4403
|
-
const rawOverrides = record.overrides;
|
|
4404
4282
|
if (rawOverrides !== void 0) {
|
|
4405
4283
|
const unknownKeys = Object.keys(rawOverrides).filter((key) => key === "promptConfigs" || !PARAM_KEYS.has(key));
|
|
4406
4284
|
if (unknownKeys.length > 0) {
|
|
@@ -4422,43 +4300,12 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4422
4300
|
return;
|
|
4423
4301
|
}
|
|
4424
4302
|
try {
|
|
4425
|
-
if (
|
|
4426
|
-
|
|
4427
|
-
strategyDir: getEngineStrategyDir(),
|
|
4428
|
-
moduleDir: dir
|
|
4429
|
-
});
|
|
4430
|
-
if (!validation.valid) {
|
|
4431
|
-
writeBridgeJson(res, 400, {
|
|
4432
|
-
ok: false,
|
|
4433
|
-
code: "prompt-configs-invalid",
|
|
4434
|
-
message: validation.errors.map(({ index, id, message }) => `[${index}] ${id}: ${message}`).join("; ")
|
|
4435
|
-
});
|
|
4436
|
-
return;
|
|
4437
|
-
}
|
|
4438
|
-
const spec = loadModuleSpec(dir);
|
|
4439
|
-
const conflicting = record.promptConfigs.some((config) => {
|
|
4440
|
-
if (config === null || typeof config !== "object" || Array.isArray(config)) return false;
|
|
4441
|
-
const entry = config;
|
|
4442
|
-
return entry.enabled !== false && entry.params?.complete === true;
|
|
4443
|
-
});
|
|
4444
|
-
if (spec.persona?.complete === true && conflicting) {
|
|
4445
|
-
writeBridgeJson(res, 400, {
|
|
4446
|
-
ok: false,
|
|
4447
|
-
code: "overrides-invalid-value",
|
|
4448
|
-
message: "顶层人设已开启「独占」;提示词配置的「独占」与之互斥,请先关闭其一"
|
|
4449
|
-
});
|
|
4450
|
-
return;
|
|
4451
|
-
}
|
|
4452
|
-
}
|
|
4453
|
-
if (!guardPresetIdentity(req, record, dir, res)) return;
|
|
4454
|
-
saveModuleParams(moduleRoot, templateName, rawOverrides, record.promptConfigs);
|
|
4303
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4304
|
+
saveModuleParams(moduleRoot, templateName, rawOverrides, void 0);
|
|
4455
4305
|
if (record.rebuild !== false && !await runOverridesChange(res, basename(dir))) return;
|
|
4456
4306
|
writeBridgeJson(res, 200, {
|
|
4457
4307
|
ok: true,
|
|
4458
|
-
value: {
|
|
4459
|
-
...record.overrides !== void 0 ? { overrides: record.overrides } : {},
|
|
4460
|
-
...record.promptConfigs !== void 0 ? { promptConfigs: record.promptConfigs } : {}
|
|
4461
|
-
}
|
|
4308
|
+
value: { overrides: record.overrides }
|
|
4462
4309
|
});
|
|
4463
4310
|
} catch (error) {
|
|
4464
4311
|
if (writeLayerSettingsError(res, error)) return;
|
|
@@ -4472,14 +4319,14 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4472
4319
|
}),
|
|
4473
4320
|
sctx.webServer.register({
|
|
4474
4321
|
kind: "exact",
|
|
4475
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.
|
|
4322
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.moduleVariables,
|
|
4476
4323
|
handler: async (req, res) => {
|
|
4477
4324
|
if (!guard(req, res)) return;
|
|
4478
4325
|
const dir = editDir(req) ?? "";
|
|
4479
4326
|
if (dir.length === 0) {
|
|
4480
4327
|
writeBridgeJson(res, 400, {
|
|
4481
4328
|
ok: false,
|
|
4482
|
-
code: "
|
|
4329
|
+
code: "module-dir-unavailable",
|
|
4483
4330
|
message: "模块目录未配置"
|
|
4484
4331
|
});
|
|
4485
4332
|
return;
|
|
@@ -4490,18 +4337,51 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4490
4337
|
if (parsedBody === void 0) return;
|
|
4491
4338
|
const { body } = parsedBody;
|
|
4492
4339
|
const record = body ?? {};
|
|
4493
|
-
if (!
|
|
4340
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4341
|
+
if (record.refreshOnly !== void 0 && (typeof record.refreshOnly !== "boolean" || record.refreshOnly === true && (record.variables !== void 0 || record.enabled !== void 0))) {
|
|
4342
|
+
writeBridgeJson(res, 400, {
|
|
4343
|
+
ok: false,
|
|
4344
|
+
code: "module-variables-invalid",
|
|
4345
|
+
message: "refreshOnly 不能与变量写入混用"
|
|
4346
|
+
});
|
|
4347
|
+
return;
|
|
4348
|
+
}
|
|
4349
|
+
if (record.refreshOnly === true) {
|
|
4350
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
4351
|
+
try {
|
|
4352
|
+
await afterOverridesChange?.(basename(dir));
|
|
4353
|
+
} catch (error) {
|
|
4354
|
+
writeBridgeJson(res, 200, {
|
|
4355
|
+
ok: true,
|
|
4356
|
+
value: {
|
|
4357
|
+
...readModuleVariables(dir),
|
|
4358
|
+
persisted: true,
|
|
4359
|
+
publicationPending: true,
|
|
4360
|
+
publicationError: `运行刷新失败:${String(error)}`
|
|
4361
|
+
}
|
|
4362
|
+
});
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
writeBridgeJson(res, 200, {
|
|
4366
|
+
ok: true,
|
|
4367
|
+
value: {
|
|
4368
|
+
...readModuleVariables(dir),
|
|
4369
|
+
publicationPending: false
|
|
4370
|
+
}
|
|
4371
|
+
});
|
|
4372
|
+
return;
|
|
4373
|
+
}
|
|
4494
4374
|
if (record.variables === void 0 && record.enabled === void 0) {
|
|
4495
4375
|
writeBridgeJson(res, 200, {
|
|
4496
4376
|
ok: true,
|
|
4497
|
-
value:
|
|
4377
|
+
value: readModuleVariables(dir)
|
|
4498
4378
|
});
|
|
4499
4379
|
return;
|
|
4500
4380
|
}
|
|
4501
4381
|
if (record.variables !== void 0 && (!isRecord(record.variables) || Object.values(record.variables).some((value) => typeof value !== "string"))) {
|
|
4502
4382
|
writeBridgeJson(res, 400, {
|
|
4503
4383
|
ok: false,
|
|
4504
|
-
code: "
|
|
4384
|
+
code: "module-variables-invalid",
|
|
4505
4385
|
message: "variables must be an object with string values"
|
|
4506
4386
|
});
|
|
4507
4387
|
return;
|
|
@@ -4509,25 +4389,64 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4509
4389
|
if (record.enabled !== void 0 && typeof record.enabled !== "boolean") {
|
|
4510
4390
|
writeBridgeJson(res, 400, {
|
|
4511
4391
|
ok: false,
|
|
4512
|
-
code: "
|
|
4392
|
+
code: "module-variables-invalid",
|
|
4513
4393
|
message: "enabled must be a boolean"
|
|
4514
4394
|
});
|
|
4515
4395
|
return;
|
|
4516
4396
|
}
|
|
4517
|
-
if (!
|
|
4397
|
+
if (!isRecord(record.expectedRevisions) || Object.keys(record.expectedRevisions).some((key) => ![
|
|
4398
|
+
"rules",
|
|
4399
|
+
"settings",
|
|
4400
|
+
"variables"
|
|
4401
|
+
].includes(key)) || record.variables !== void 0 && (typeof record.expectedRevisions.variables !== "string" || !SHA256_HEX_RE.test(record.expectedRevisions.variables)) || record.enabled !== void 0 && (typeof record.expectedRevisions.settings !== "string" || !SHA256_HEX_RE.test(record.expectedRevisions.settings))) {
|
|
4402
|
+
writeBridgeJson(res, 400, {
|
|
4403
|
+
ok: false,
|
|
4404
|
+
code: "module-variables-invalid",
|
|
4405
|
+
message: "保存变量或开关必须提供对应 expectedRevisions"
|
|
4406
|
+
});
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4409
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
4518
4410
|
try {
|
|
4519
|
-
if (!
|
|
4520
|
-
saveModuleParams(moduleRoot, templateName, void 0, void 0, record.variables, record.enabled);
|
|
4521
|
-
|
|
4411
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4412
|
+
saveModuleParams(moduleRoot, templateName, void 0, void 0, record.variables, record.enabled, record.expectedRevisions);
|
|
4413
|
+
const committed = readModuleVariables(dir);
|
|
4414
|
+
try {
|
|
4415
|
+
await afterOverridesChange?.(basename(dir));
|
|
4416
|
+
} catch (error) {
|
|
4417
|
+
writeBridgeJson(res, 200, {
|
|
4418
|
+
ok: true,
|
|
4419
|
+
value: {
|
|
4420
|
+
...committed,
|
|
4421
|
+
persisted: true,
|
|
4422
|
+
publicationPending: true,
|
|
4423
|
+
publicationError: `变量已保存,但运行刷新失败:${String(error)}`
|
|
4424
|
+
}
|
|
4425
|
+
});
|
|
4426
|
+
return;
|
|
4427
|
+
}
|
|
4522
4428
|
writeBridgeJson(res, 200, {
|
|
4523
4429
|
ok: true,
|
|
4524
|
-
value:
|
|
4430
|
+
value: committed
|
|
4525
4431
|
});
|
|
4526
4432
|
} catch (error) {
|
|
4527
|
-
|
|
4433
|
+
if (error instanceof ModuleRulesError && error.persisted) {
|
|
4434
|
+
writeBridgeJson(res, 200, {
|
|
4435
|
+
ok: true,
|
|
4436
|
+
value: {
|
|
4437
|
+
...readModuleVariables(dir),
|
|
4438
|
+
persisted: true,
|
|
4439
|
+
publicationPending: true,
|
|
4440
|
+
publicationError: error.message
|
|
4441
|
+
}
|
|
4442
|
+
});
|
|
4443
|
+
return;
|
|
4444
|
+
}
|
|
4445
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4446
|
+
writeBridgeJson(res, error instanceof ModuleRulesError ? error.status : 409, {
|
|
4528
4447
|
ok: false,
|
|
4529
|
-
code: "
|
|
4530
|
-
message: `模板变量保存失败:${
|
|
4448
|
+
code: error instanceof ModuleRulesError ? error.code : "module-variables-rejected",
|
|
4449
|
+
message: `模板变量保存失败:${message}`
|
|
4531
4450
|
});
|
|
4532
4451
|
}
|
|
4533
4452
|
}
|
|
@@ -4541,7 +4460,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4541
4460
|
if (dir.length === 0) {
|
|
4542
4461
|
writeBridgeJson(res, 400, {
|
|
4543
4462
|
ok: false,
|
|
4544
|
-
code: "
|
|
4463
|
+
code: "module-dir-unavailable",
|
|
4545
4464
|
message: "模块目录未配置"
|
|
4546
4465
|
});
|
|
4547
4466
|
return;
|
|
@@ -4550,7 +4469,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4550
4469
|
if (parsedBody === void 0) return;
|
|
4551
4470
|
const { body } = parsedBody;
|
|
4552
4471
|
const record = body ?? {};
|
|
4553
|
-
if (!
|
|
4472
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4554
4473
|
if (record.customTools === void 0) {
|
|
4555
4474
|
try {
|
|
4556
4475
|
const spec = loadModuleSpec(dir);
|
|
@@ -4587,13 +4506,13 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4587
4506
|
});
|
|
4588
4507
|
return;
|
|
4589
4508
|
}
|
|
4590
|
-
if (!
|
|
4591
|
-
if (!
|
|
4592
|
-
|
|
4509
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
4510
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4511
|
+
withModuleDefinition(dir, (doc) => {
|
|
4593
4512
|
if (customTools.length === 0) doc.deleteIn(["customTools"]);
|
|
4594
4513
|
else {
|
|
4595
4514
|
doc.setIn(["customTools"], customTools);
|
|
4596
|
-
|
|
4515
|
+
appendModuleCapabilities(doc, customToolModules(customTools));
|
|
4597
4516
|
}
|
|
4598
4517
|
});
|
|
4599
4518
|
if (!await runOverridesChange(res, basename(dir))) return;
|
|
@@ -4619,7 +4538,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4619
4538
|
if (dir.length === 0) {
|
|
4620
4539
|
writeBridgeJson(res, 400, {
|
|
4621
4540
|
ok: false,
|
|
4622
|
-
code: "
|
|
4541
|
+
code: "module-dir-unavailable",
|
|
4623
4542
|
message: "模块目录未配置"
|
|
4624
4543
|
});
|
|
4625
4544
|
return;
|
|
@@ -4627,7 +4546,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4627
4546
|
const parsedBody = await readBridgeBodyForHandler(req, res);
|
|
4628
4547
|
if (parsedBody === void 0) return;
|
|
4629
4548
|
const record = parsedBody.body ?? {};
|
|
4630
|
-
if (!
|
|
4549
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
4631
4550
|
if (record.persona === void 0) {
|
|
4632
4551
|
try {
|
|
4633
4552
|
writeBridgeJson(res, 200, {
|
|
@@ -4643,33 +4562,29 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4643
4562
|
}
|
|
4644
4563
|
return;
|
|
4645
4564
|
}
|
|
4646
|
-
if (!
|
|
4565
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
4647
4566
|
try {
|
|
4648
4567
|
const raw = record.persona;
|
|
4649
4568
|
const persona = raw === null ? null : readPersonaSpec(raw);
|
|
4650
4569
|
if (persona === void 0) {
|
|
4651
4570
|
writeBridgeJson(res, 400, {
|
|
4652
4571
|
ok: false,
|
|
4653
|
-
code: "
|
|
4572
|
+
code: "module-persona-invalid",
|
|
4654
4573
|
message: "persona 必须包含字符串 prefix(可选 suffix / complete / includeRuntimeContext)"
|
|
4655
4574
|
});
|
|
4656
4575
|
return;
|
|
4657
4576
|
}
|
|
4658
4577
|
if (persona?.complete === true) {
|
|
4659
|
-
if ((loadModuleSpec(dir).
|
|
4660
|
-
if (config === null || typeof config !== "object" || Array.isArray(config)) return false;
|
|
4661
|
-
const entry = config;
|
|
4662
|
-
return entry.enabled !== false && entry.params?.complete === true;
|
|
4663
|
-
})) {
|
|
4578
|
+
if (ruleInjections(loadModuleSpec(dir).rules).some(({ rule, config }) => rule.enabled !== false && config.enabled !== false && config.params?.complete === true)) {
|
|
4664
4579
|
writeBridgeJson(res, 400, {
|
|
4665
4580
|
ok: false,
|
|
4666
|
-
code: "
|
|
4581
|
+
code: "module-persona-complete-conflict",
|
|
4667
4582
|
message: "提示词配置已有「独占」段;顶层人设的「独占」与之互斥,请先关闭其一"
|
|
4668
4583
|
});
|
|
4669
4584
|
return;
|
|
4670
4585
|
}
|
|
4671
4586
|
}
|
|
4672
|
-
|
|
4587
|
+
saveModulePersona(dirname(dir), basename(dir), persona);
|
|
4673
4588
|
if (!await runOverridesChange(res, basename(dir))) return;
|
|
4674
4589
|
writeBridgeJson(res, 200, {
|
|
4675
4590
|
ok: true,
|
|
@@ -4678,7 +4593,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4678
4593
|
} catch (error) {
|
|
4679
4594
|
writeBridgeJson(res, 409, {
|
|
4680
4595
|
ok: false,
|
|
4681
|
-
code: "
|
|
4596
|
+
code: "module-persona-rejected",
|
|
4682
4597
|
message: `人设保存失败:${error instanceof Error ? error.message : String(error)}`
|
|
4683
4598
|
});
|
|
4684
4599
|
}
|
|
@@ -4736,7 +4651,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4736
4651
|
}),
|
|
4737
4652
|
sctx.webServer.register({
|
|
4738
4653
|
kind: "exact",
|
|
4739
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.
|
|
4654
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.importModulePackage,
|
|
4740
4655
|
handler: async (req, res) => {
|
|
4741
4656
|
if (!guard(req, res)) return;
|
|
4742
4657
|
const parsedBody = await readBridgeBodyForHandler(req, res);
|
|
@@ -4755,14 +4670,14 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4755
4670
|
if (!request.ok) {
|
|
4756
4671
|
writeBridgeJson(res, 400, {
|
|
4757
4672
|
ok: false,
|
|
4758
|
-
code: "
|
|
4673
|
+
code: "module-package-invalid",
|
|
4759
4674
|
message: request.message
|
|
4760
4675
|
});
|
|
4761
4676
|
return;
|
|
4762
4677
|
}
|
|
4763
4678
|
try {
|
|
4764
|
-
const files = await
|
|
4765
|
-
const preview =
|
|
4679
|
+
const files = await expandModuleSource(request.params.sourceId === void 0 ? readBridgeFiles(record.files) : assetSources.read(request.params.sourceId));
|
|
4680
|
+
const preview = moduleImportPreview(userModulesDir(), files, request.params);
|
|
4766
4681
|
if (!("prepared" in preview)) {
|
|
4767
4682
|
if (!request.params.preview) throw new Error("请先选择内容类型或提示顺序组并重新预览");
|
|
4768
4683
|
writeBridgeJson(res, 200, {
|
|
@@ -4789,9 +4704,9 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4789
4704
|
});
|
|
4790
4705
|
return;
|
|
4791
4706
|
}
|
|
4792
|
-
const installed = await
|
|
4707
|
+
const installed = await installModulePackage(userModulesDir(), files, request.params);
|
|
4793
4708
|
invalidateDescriptor();
|
|
4794
|
-
if (!await
|
|
4709
|
+
if (!await finishModuleChange(res, () => afterModulePackageImport?.(installed.id))) return;
|
|
4795
4710
|
writeBridgeJson(res, 200, {
|
|
4796
4711
|
ok: true,
|
|
4797
4712
|
value: {
|
|
@@ -4801,8 +4716,8 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4801
4716
|
}
|
|
4802
4717
|
});
|
|
4803
4718
|
} catch (error) {
|
|
4804
|
-
const code = typeof error.code === "string" ? String(error.code) : "
|
|
4805
|
-
writeBridgeJson(res, code === "
|
|
4719
|
+
const code = typeof error.code === "string" ? String(error.code) : "module-package-invalid";
|
|
4720
|
+
writeBridgeJson(res, code === "module-preview-stale" ? 409 : 400, {
|
|
4806
4721
|
ok: false,
|
|
4807
4722
|
code,
|
|
4808
4723
|
message: error instanceof Error ? error.message : String(error)
|
|
@@ -4812,7 +4727,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4812
4727
|
}),
|
|
4813
4728
|
sctx.webServer.register({
|
|
4814
4729
|
kind: "exact",
|
|
4815
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.
|
|
4730
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.exportModule,
|
|
4816
4731
|
handler: async (req, res) => {
|
|
4817
4732
|
if (!guard(req, res)) return;
|
|
4818
4733
|
const parsedBody = await readBridgeBodyForHandler(req, res);
|
|
@@ -4828,7 +4743,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4828
4743
|
"memoryChoices"
|
|
4829
4744
|
]);
|
|
4830
4745
|
if (Object.keys(record).some((key) => !allowed.has(key))) throw new Error("导出请求包含未知字段");
|
|
4831
|
-
|
|
4746
|
+
assertModuleId(record.id);
|
|
4832
4747
|
if (record.id.length > 128) throw new Error("模块 ID 过长");
|
|
4833
4748
|
if (record.mode !== void 0 && record.mode !== "definition" && record.mode !== "zip") throw new Error("mode 必须是 definition 或 zip");
|
|
4834
4749
|
if (record.preview !== void 0 && typeof record.preview !== "boolean") throw new Error("preview 必须是布尔值");
|
|
@@ -4836,11 +4751,11 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4836
4751
|
if (record.memoryChoices !== void 0 && (!isRecord(record.memoryChoices) || Object.keys(record.memoryChoices).length > 2048 || Object.entries(record.memoryChoices).some(([key, value]) => key.length === 0 || key.length > 256 || value !== "include" && value !== "exclude"))) throw new Error("memoryChoices 必须包含合法条目 ID 和 include/exclude 选择");
|
|
4837
4752
|
writeBridgeJson(res, 200, {
|
|
4838
4753
|
ok: true,
|
|
4839
|
-
value: await
|
|
4754
|
+
value: await exportModulePackage(userModulesDir(), record)
|
|
4840
4755
|
});
|
|
4841
4756
|
} catch (error) {
|
|
4842
4757
|
const message = error instanceof Error ? error.message : String(error);
|
|
4843
|
-
const code = typeof error.code === "string" ? String(error.code) : "
|
|
4758
|
+
const code = typeof error.code === "string" ? String(error.code) : "module-export-failed";
|
|
4844
4759
|
writeBridgeJson(res, code.includes("stale") ? 409 : 400, {
|
|
4845
4760
|
ok: false,
|
|
4846
4761
|
code,
|
|
@@ -4862,7 +4777,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4862
4777
|
if (id.length === 0) {
|
|
4863
4778
|
writeBridgeJson(res, 400, {
|
|
4864
4779
|
ok: false,
|
|
4865
|
-
code: "
|
|
4780
|
+
code: "module-delete-rejected",
|
|
4866
4781
|
message: "缺少模块 id"
|
|
4867
4782
|
});
|
|
4868
4783
|
return;
|
|
@@ -4871,21 +4786,21 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4871
4786
|
if (activeDir.length > 0 && basename(activeDir) === id) {
|
|
4872
4787
|
writeBridgeJson(res, 400, {
|
|
4873
4788
|
ok: false,
|
|
4874
|
-
code: "
|
|
4789
|
+
code: "module-in-use",
|
|
4875
4790
|
message: `模块「${id}」正在使用中,请先切换其他模块再删除`
|
|
4876
4791
|
});
|
|
4877
4792
|
return;
|
|
4878
4793
|
}
|
|
4879
|
-
const result =
|
|
4794
|
+
const result = removeUserModule(id);
|
|
4880
4795
|
if (!result.ok) {
|
|
4881
4796
|
writeBridgeJson(res, 400, {
|
|
4882
4797
|
ok: false,
|
|
4883
|
-
code: "
|
|
4798
|
+
code: "module-delete-rejected",
|
|
4884
4799
|
message: result.message
|
|
4885
4800
|
});
|
|
4886
4801
|
return;
|
|
4887
4802
|
}
|
|
4888
|
-
if (!await
|
|
4803
|
+
if (!await refreshModuleList(res, id)) return;
|
|
4889
4804
|
writeBridgeJson(res, 200, {
|
|
4890
4805
|
ok: true,
|
|
4891
4806
|
value: { id }
|
|
@@ -4905,21 +4820,21 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4905
4820
|
if (id.length === 0) {
|
|
4906
4821
|
writeBridgeJson(res, 400, {
|
|
4907
4822
|
ok: false,
|
|
4908
|
-
code: "
|
|
4823
|
+
code: "module-clone-rejected",
|
|
4909
4824
|
message: "缺少模块 id"
|
|
4910
4825
|
});
|
|
4911
4826
|
return;
|
|
4912
4827
|
}
|
|
4913
|
-
const result =
|
|
4828
|
+
const result = cloneBuiltinModule(id, record.autoSuffix === true);
|
|
4914
4829
|
if (!result.ok) {
|
|
4915
4830
|
writeBridgeJson(res, 400, {
|
|
4916
4831
|
ok: false,
|
|
4917
|
-
code: "
|
|
4832
|
+
code: "module-clone-rejected",
|
|
4918
4833
|
message: result.message
|
|
4919
4834
|
});
|
|
4920
4835
|
return;
|
|
4921
4836
|
}
|
|
4922
|
-
if (!await
|
|
4837
|
+
if (!await refreshModuleList(res, result.id)) return;
|
|
4923
4838
|
writeBridgeJson(res, 200, {
|
|
4924
4839
|
ok: true,
|
|
4925
4840
|
value: { id: result.id }
|
|
@@ -4939,7 +4854,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4939
4854
|
if (id.length === 0) {
|
|
4940
4855
|
writeBridgeJson(res, 400, {
|
|
4941
4856
|
ok: false,
|
|
4942
|
-
code: "
|
|
4857
|
+
code: "module-duplicate-rejected",
|
|
4943
4858
|
message: "缺少模块 id"
|
|
4944
4859
|
});
|
|
4945
4860
|
return;
|
|
@@ -4948,12 +4863,12 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4948
4863
|
if (!result.ok) {
|
|
4949
4864
|
writeBridgeJson(res, 400, {
|
|
4950
4865
|
ok: false,
|
|
4951
|
-
code: "
|
|
4866
|
+
code: "module-duplicate-rejected",
|
|
4952
4867
|
message: result.message
|
|
4953
4868
|
});
|
|
4954
4869
|
return;
|
|
4955
4870
|
}
|
|
4956
|
-
if (!await
|
|
4871
|
+
if (!await refreshModuleList(res, result.id)) return;
|
|
4957
4872
|
writeBridgeJson(res, 200, {
|
|
4958
4873
|
ok: true,
|
|
4959
4874
|
value: { id: result.id }
|
|
@@ -4973,7 +4888,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4973
4888
|
if (id.length === 0) {
|
|
4974
4889
|
writeBridgeJson(res, 400, {
|
|
4975
4890
|
ok: false,
|
|
4976
|
-
code: "
|
|
4891
|
+
code: "module-open-rejected",
|
|
4977
4892
|
message: "缺少模块 id"
|
|
4978
4893
|
});
|
|
4979
4894
|
return;
|
|
@@ -4982,7 +4897,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
4982
4897
|
if (!result.ok) {
|
|
4983
4898
|
writeBridgeJson(res, 400, {
|
|
4984
4899
|
ok: false,
|
|
4985
|
-
code: "
|
|
4900
|
+
code: "module-open-rejected",
|
|
4986
4901
|
message: `${result.message}(${result.path})`
|
|
4987
4902
|
});
|
|
4988
4903
|
return;
|
|
@@ -5006,7 +4921,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5006
4921
|
if (id.length === 0) {
|
|
5007
4922
|
writeBridgeJson(res, 400, {
|
|
5008
4923
|
ok: false,
|
|
5009
|
-
code: "
|
|
4924
|
+
code: "module-enable-rejected",
|
|
5010
4925
|
message: "缺少模块 id"
|
|
5011
4926
|
});
|
|
5012
4927
|
return;
|
|
@@ -5014,7 +4929,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5014
4929
|
if (typeof record.enabled !== "boolean") {
|
|
5015
4930
|
writeBridgeJson(res, 400, {
|
|
5016
4931
|
ok: false,
|
|
5017
|
-
code: "
|
|
4932
|
+
code: "module-enable-rejected",
|
|
5018
4933
|
message: "enabled 必须是布尔值"
|
|
5019
4934
|
});
|
|
5020
4935
|
return;
|
|
@@ -5022,7 +4937,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5022
4937
|
if (record.enabled && !moduleDirExists(userModulesDir(), id)) {
|
|
5023
4938
|
writeBridgeJson(res, 400, {
|
|
5024
4939
|
ok: false,
|
|
5025
|
-
code: "
|
|
4940
|
+
code: "module-enable-rejected",
|
|
5026
4941
|
message: `模块 ${id} 不存在`
|
|
5027
4942
|
});
|
|
5028
4943
|
return;
|
|
@@ -5094,7 +5009,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5094
5009
|
if (dir.length === 0) {
|
|
5095
5010
|
writeBridgeJson(res, 400, {
|
|
5096
5011
|
ok: false,
|
|
5097
|
-
code: "
|
|
5012
|
+
code: "module-dir-unavailable",
|
|
5098
5013
|
message: "模块目录未配置"
|
|
5099
5014
|
});
|
|
5100
5015
|
return;
|
|
@@ -5120,9 +5035,9 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5120
5035
|
});
|
|
5121
5036
|
return;
|
|
5122
5037
|
}
|
|
5123
|
-
if (!
|
|
5038
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
5124
5039
|
try {
|
|
5125
|
-
const files = await
|
|
5040
|
+
const files = await expandModuleSource(request.params.sourceId === void 0 ? readBridgeFiles(record.files) : assetSources.read(request.params.sourceId));
|
|
5126
5041
|
if (editDir(req) !== dir) {
|
|
5127
5042
|
writeBridgeJson(res, 409, {
|
|
5128
5043
|
ok: false,
|
|
@@ -5143,98 +5058,40 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5143
5058
|
});
|
|
5144
5059
|
return;
|
|
5145
5060
|
}
|
|
5146
|
-
const root =
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
if (request.params.targetId === void 0 && request.params.overwrite !== true) {
|
|
5151
|
-
const wanted = targetId;
|
|
5152
|
-
for (let n = 1; presetPathExists(join(cards, targetId)); n++) targetId = `${wanted}-copy${n === 1 ? "" : "-" + n}`;
|
|
5153
|
-
}
|
|
5154
|
-
assertPresetId(targetId);
|
|
5155
|
-
const options = {
|
|
5156
|
-
...request.params,
|
|
5157
|
-
targetId
|
|
5158
|
-
};
|
|
5159
|
-
const converted = previewCharacterCard(files, options);
|
|
5160
|
-
if (!converted.ok) throw new Error(converted.message);
|
|
5161
|
-
const version = directoryVersionOf(join(cards, targetId));
|
|
5162
|
-
const previewRevision = computePreviewRevision({
|
|
5163
|
-
files: [{
|
|
5164
|
-
path: "source",
|
|
5165
|
-
content: converted.sourceDigest
|
|
5166
|
-
}, {
|
|
5167
|
-
path: "choices",
|
|
5168
|
-
content: JSON.stringify([
|
|
5169
|
-
targetId,
|
|
5170
|
-
request.params.targetName ?? "",
|
|
5171
|
-
request.params.overwrite === true,
|
|
5172
|
-
request.params.sourceKind ?? null
|
|
5173
|
-
])
|
|
5174
|
-
}],
|
|
5175
|
-
...request.params.promptOrderCharacterId === void 0 ? {} : { orderCharacterId: request.params.promptOrderCharacterId },
|
|
5176
|
-
converter: "asset-character/1",
|
|
5177
|
-
target: {
|
|
5178
|
-
kind: "character-card",
|
|
5179
|
-
targetId,
|
|
5180
|
-
targetVersion: version,
|
|
5181
|
-
ownerPreset: JSON.stringify([
|
|
5182
|
-
root,
|
|
5183
|
-
basename(dir),
|
|
5184
|
-
directoryVersionOf(dir)
|
|
5185
|
-
])
|
|
5186
|
-
}
|
|
5187
|
-
});
|
|
5061
|
+
const root = userModulesDir();
|
|
5062
|
+
const preview = moduleImportPreview(root, files, request.params);
|
|
5063
|
+
if (!("prepared" in preview)) throw new Error("角色来源未形成有效模块候选");
|
|
5064
|
+
const { summary, sourceDigest, previewRevision } = preview;
|
|
5188
5065
|
if (request.params.preview) {
|
|
5189
|
-
const summary = {
|
|
5190
|
-
sourceName: converted.sourceName,
|
|
5191
|
-
kind: converted.kind,
|
|
5192
|
-
targetId,
|
|
5193
|
-
targetName: converted.name,
|
|
5194
|
-
exists: version !== null,
|
|
5195
|
-
files: files.map((file) => ({
|
|
5196
|
-
path: file.path,
|
|
5197
|
-
bytes: decodeAssetFile(file).length
|
|
5198
|
-
})),
|
|
5199
|
-
configCount: prepared.spec.promptConfigs?.length ?? 0,
|
|
5200
|
-
warnings: []
|
|
5201
|
-
};
|
|
5202
5066
|
writeBridgeJson(res, 200, {
|
|
5203
5067
|
ok: true,
|
|
5204
5068
|
value: {
|
|
5205
5069
|
preview: true,
|
|
5206
5070
|
state: "ready",
|
|
5207
|
-
name:
|
|
5071
|
+
name: summary.targetName,
|
|
5208
5072
|
summary,
|
|
5209
|
-
sourceDigest
|
|
5073
|
+
sourceDigest,
|
|
5210
5074
|
previewRevision,
|
|
5211
|
-
...
|
|
5075
|
+
...prepared.report === void 0 ? {} : { report: prepared.report }
|
|
5212
5076
|
}
|
|
5213
5077
|
});
|
|
5214
5078
|
return;
|
|
5215
5079
|
}
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
code: "characters-preview-stale",
|
|
5220
|
-
message: "预览已过期:来源、选择或目标版本已变化,请重新预览"
|
|
5221
|
-
});
|
|
5222
|
-
return;
|
|
5223
|
-
}
|
|
5224
|
-
if (version !== null && request.params.overwrite !== true) throw new Error("角色已存在,请明确选择更新或另存");
|
|
5225
|
-
const result = importCharacterCard(root, files, options);
|
|
5226
|
-
if (!result.ok) throw new Error(result.message);
|
|
5080
|
+
const result = await installModulePackage(root, files, request.params, { preserveAssets: true });
|
|
5081
|
+
invalidateDescriptor();
|
|
5082
|
+
if (!await finishModuleChange(res, () => afterModulePackageImport?.(result.id))) return;
|
|
5227
5083
|
writeBridgeJson(res, 200, {
|
|
5228
5084
|
ok: true,
|
|
5229
5085
|
value: {
|
|
5230
5086
|
id: result.id,
|
|
5231
|
-
name:
|
|
5087
|
+
name: summary.targetName
|
|
5232
5088
|
}
|
|
5233
5089
|
});
|
|
5234
5090
|
} catch (error) {
|
|
5235
|
-
|
|
5091
|
+
const code = typeof error.code === "string" ? String(error.code) : "characters-rejected";
|
|
5092
|
+
writeBridgeJson(res, code.includes("stale") ? 409 : 400, {
|
|
5236
5093
|
ok: false,
|
|
5237
|
-
code
|
|
5094
|
+
code,
|
|
5238
5095
|
message: error instanceof Error ? error.message : String(error)
|
|
5239
5096
|
});
|
|
5240
5097
|
}
|
|
@@ -5254,34 +5111,14 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5254
5111
|
}),
|
|
5255
5112
|
sctx.webServer.register({
|
|
5256
5113
|
kind: "exact",
|
|
5257
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.
|
|
5114
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.moduleMerge,
|
|
5258
5115
|
handler: async (req, res) => {
|
|
5259
5116
|
if (!guard(req, res)) return;
|
|
5260
5117
|
const dir = editDir(req) ?? "";
|
|
5261
5118
|
if (dir.length === 0) {
|
|
5262
5119
|
writeBridgeJson(res, 400, {
|
|
5263
5120
|
ok: false,
|
|
5264
|
-
code: "
|
|
5265
|
-
message: "模块目录未配置"
|
|
5266
|
-
});
|
|
5267
|
-
return;
|
|
5268
|
-
}
|
|
5269
|
-
writeBridgeJson(res, 200, {
|
|
5270
|
-
ok: true,
|
|
5271
|
-
value: { characters: listCharacterCards(dirname(dir), basename(dir)) }
|
|
5272
|
-
});
|
|
5273
|
-
}
|
|
5274
|
-
}),
|
|
5275
|
-
sctx.webServer.register({
|
|
5276
|
-
kind: "exact",
|
|
5277
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.charactersDelete,
|
|
5278
|
-
handler: async (req, res) => {
|
|
5279
|
-
if (!guard(req, res)) return;
|
|
5280
|
-
const dir = editDir(req) ?? "";
|
|
5281
|
-
if (dir.length === 0) {
|
|
5282
|
-
writeBridgeJson(res, 400, {
|
|
5283
|
-
ok: false,
|
|
5284
|
-
code: "preset-dir-unavailable",
|
|
5121
|
+
code: "module-dir-unavailable",
|
|
5285
5122
|
message: "模块目录未配置"
|
|
5286
5123
|
});
|
|
5287
5124
|
return;
|
|
@@ -5299,51 +5136,8 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5299
5136
|
});
|
|
5300
5137
|
return;
|
|
5301
5138
|
}
|
|
5302
|
-
if (!
|
|
5303
|
-
const result =
|
|
5304
|
-
if (!result.ok) {
|
|
5305
|
-
writeBridgeJson(res, 400, {
|
|
5306
|
-
ok: false,
|
|
5307
|
-
code: "characters-rejected",
|
|
5308
|
-
message: result.message
|
|
5309
|
-
});
|
|
5310
|
-
return;
|
|
5311
|
-
}
|
|
5312
|
-
writeBridgeJson(res, 200, {
|
|
5313
|
-
ok: true,
|
|
5314
|
-
value: { id }
|
|
5315
|
-
});
|
|
5316
|
-
}
|
|
5317
|
-
}),
|
|
5318
|
-
sctx.webServer.register({
|
|
5319
|
-
kind: "exact",
|
|
5320
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.charactersApply,
|
|
5321
|
-
handler: async (req, res) => {
|
|
5322
|
-
if (!guard(req, res)) return;
|
|
5323
|
-
const dir = editDir(req) ?? "";
|
|
5324
|
-
if (dir.length === 0) {
|
|
5325
|
-
writeBridgeJson(res, 400, {
|
|
5326
|
-
ok: false,
|
|
5327
|
-
code: "preset-dir-unavailable",
|
|
5328
|
-
message: "模块目录未配置"
|
|
5329
|
-
});
|
|
5330
|
-
return;
|
|
5331
|
-
}
|
|
5332
|
-
const parsedBody = await readBridgeBodyForHandler(req, res);
|
|
5333
|
-
if (parsedBody === void 0) return;
|
|
5334
|
-
const { body } = parsedBody;
|
|
5335
|
-
const record = body ?? {};
|
|
5336
|
-
const id = typeof record.id === "string" ? record.id.trim() : "";
|
|
5337
|
-
if (id.length === 0) {
|
|
5338
|
-
writeBridgeJson(res, 400, {
|
|
5339
|
-
ok: false,
|
|
5340
|
-
code: "characters-rejected",
|
|
5341
|
-
message: "缺少角色卡 id"
|
|
5342
|
-
});
|
|
5343
|
-
return;
|
|
5344
|
-
}
|
|
5345
|
-
if (!guardPresetWrite(dir, res)) return;
|
|
5346
|
-
const result = applyCharacterToPreset(dirname(dir), basename(dir), id);
|
|
5139
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
5140
|
+
const result = mergeModuleIntoModule(dirname(dir), basename(dir), id);
|
|
5347
5141
|
if (!result.ok) {
|
|
5348
5142
|
writeBridgeJson(res, 400, {
|
|
5349
5143
|
ok: false,
|
|
@@ -5364,14 +5158,14 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5364
5158
|
}),
|
|
5365
5159
|
sctx.webServer.register({
|
|
5366
5160
|
kind: "exact",
|
|
5367
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.
|
|
5161
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.moduleUnmerge,
|
|
5368
5162
|
handler: async (req, res) => {
|
|
5369
5163
|
if (!guard(req, res)) return;
|
|
5370
5164
|
const dir = editDir(req) ?? "";
|
|
5371
5165
|
if (dir.length === 0) {
|
|
5372
5166
|
writeBridgeJson(res, 400, {
|
|
5373
5167
|
ok: false,
|
|
5374
|
-
code: "
|
|
5168
|
+
code: "module-dir-unavailable",
|
|
5375
5169
|
message: "模块目录未配置"
|
|
5376
5170
|
});
|
|
5377
5171
|
return;
|
|
@@ -5389,8 +5183,8 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5389
5183
|
});
|
|
5390
5184
|
return;
|
|
5391
5185
|
}
|
|
5392
|
-
if (!
|
|
5393
|
-
const result =
|
|
5186
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
5187
|
+
const result = removeMergedModule(dirname(dir), basename(dir), id);
|
|
5394
5188
|
if (!result.ok) {
|
|
5395
5189
|
writeBridgeJson(res, 400, {
|
|
5396
5190
|
ok: false,
|
|
@@ -5418,7 +5212,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5418
5212
|
if (dir.length === 0) {
|
|
5419
5213
|
writeBridgeJson(res, 400, {
|
|
5420
5214
|
ok: false,
|
|
5421
|
-
code: "
|
|
5215
|
+
code: "module-dir-unavailable",
|
|
5422
5216
|
message: "模块目录未配置"
|
|
5423
5217
|
});
|
|
5424
5218
|
return;
|
|
@@ -5427,7 +5221,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5427
5221
|
if (parsedBody === void 0) return;
|
|
5428
5222
|
const { body } = parsedBody;
|
|
5429
5223
|
const record = body ?? {};
|
|
5430
|
-
if (!
|
|
5224
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
5431
5225
|
if (record.policy === void 0) {
|
|
5432
5226
|
try {
|
|
5433
5227
|
writeBridgeJson(res, 200, {
|
|
@@ -5443,7 +5237,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5443
5237
|
}
|
|
5444
5238
|
return;
|
|
5445
5239
|
}
|
|
5446
|
-
if (!
|
|
5240
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
5447
5241
|
try {
|
|
5448
5242
|
const policy = record.policy;
|
|
5449
5243
|
const core = await import(pathToFileURL(join(packageEngineDir(), "subagent-tool-policy-core.mjs")).href);
|
|
@@ -5458,12 +5252,12 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5458
5252
|
});
|
|
5459
5253
|
return;
|
|
5460
5254
|
}
|
|
5461
|
-
if (!
|
|
5462
|
-
|
|
5255
|
+
if (!guardModuleIdentity(req, record, dir, res)) return;
|
|
5256
|
+
withModuleDefinition(dir, (doc) => {
|
|
5463
5257
|
if (isEmpty) doc.deleteIn(["subagentToolPolicy"]);
|
|
5464
5258
|
else {
|
|
5465
5259
|
doc.setIn(["subagentToolPolicy"], policy);
|
|
5466
|
-
|
|
5260
|
+
appendModuleCapabilities(doc, ["subagent-tool-policy"]);
|
|
5467
5261
|
}
|
|
5468
5262
|
});
|
|
5469
5263
|
if (!await runOverridesChange(res, basename(dir))) return;
|
|
@@ -5492,7 +5286,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5492
5286
|
if (dir.length === 0) {
|
|
5493
5287
|
writeBridgeJson(res, 400, {
|
|
5494
5288
|
ok: false,
|
|
5495
|
-
code: "
|
|
5289
|
+
code: "module-dir-unavailable",
|
|
5496
5290
|
message: "模块目录未配置"
|
|
5497
5291
|
});
|
|
5498
5292
|
return;
|
|
@@ -5538,14 +5332,14 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5538
5332
|
}),
|
|
5539
5333
|
sctx.webServer.register({
|
|
5540
5334
|
kind: "exact",
|
|
5541
|
-
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.
|
|
5335
|
+
path: SETTINGS_BRIDGE_PREFIX + BRIDGE_ENDPOINTS.moduleCapability,
|
|
5542
5336
|
handler: async (req, res) => {
|
|
5543
5337
|
if (!guard(req, res)) return;
|
|
5544
5338
|
const dir = editDir(req) ?? "";
|
|
5545
5339
|
if (dir.length === 0) {
|
|
5546
5340
|
writeBridgeJson(res, 400, {
|
|
5547
5341
|
ok: false,
|
|
5548
|
-
code: "
|
|
5342
|
+
code: "module-dir-unavailable",
|
|
5549
5343
|
message: "模块目录未配置"
|
|
5550
5344
|
});
|
|
5551
5345
|
return;
|
|
@@ -5553,7 +5347,7 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5553
5347
|
const parsedBody = await readBridgeBodyForHandler(req, res);
|
|
5554
5348
|
if (parsedBody === void 0) return;
|
|
5555
5349
|
const body = asRecord(parsedBody.body);
|
|
5556
|
-
if (!
|
|
5350
|
+
if (!guardModuleIdentity(req, body, dir, res)) return;
|
|
5557
5351
|
const action = body.action;
|
|
5558
5352
|
const id = action === "create" || action === "remove" ? body.capabilityId : action === "create-recipe" ? body.recipeId : void 0;
|
|
5559
5353
|
if (action !== "create" && action !== "remove" && action !== "create-recipe" || typeof id !== "string" || id.trim().length === 0 || id.length > 128) {
|
|
@@ -5564,30 +5358,31 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5564
5358
|
});
|
|
5565
5359
|
return;
|
|
5566
5360
|
}
|
|
5567
|
-
if (!
|
|
5361
|
+
if (!guardModuleWrite(dir, res)) return;
|
|
5568
5362
|
const run = capabilityQueue.then(async () => {
|
|
5569
|
-
if (!
|
|
5570
|
-
const file = join(dir, MODULE_DEFINITION_FILE);
|
|
5571
|
-
let original;
|
|
5363
|
+
if (!guardModuleIdentity(req, body, dir, res)) return;
|
|
5572
5364
|
try {
|
|
5573
|
-
|
|
5574
|
-
const result = action === "remove" ? removeEngineCapabilityFromPreset(dir, id.trim()) : createEngineCapabilityInPreset(dir, action === "create" ? {
|
|
5365
|
+
const result = action === "remove" ? removeModuleCapability(dir, id.trim()) : createModuleCapability(dir, action === "create" ? {
|
|
5575
5366
|
action: "create",
|
|
5576
5367
|
capabilityId: id.trim()
|
|
5577
5368
|
} : {
|
|
5578
5369
|
action: "create-recipe",
|
|
5579
5370
|
recipeId: id.trim()
|
|
5580
5371
|
});
|
|
5581
|
-
if (result.changed && !await
|
|
5372
|
+
if (result.changed && !await finishModuleChange(res, () => afterCapabilityChange?.(basename(dir)))) return;
|
|
5582
5373
|
writeBridgeJson(res, 200, {
|
|
5583
5374
|
ok: true,
|
|
5584
5375
|
value: result
|
|
5585
5376
|
});
|
|
5586
5377
|
} catch (error) {
|
|
5587
|
-
if (
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5378
|
+
if (error instanceof ModuleRulesError && error.persisted) {
|
|
5379
|
+
writeBridgeJson(res, 500, {
|
|
5380
|
+
ok: false,
|
|
5381
|
+
code: MODULE_ACTIVATION_FAILED,
|
|
5382
|
+
message: `能力变更已保存,但切片未发布;请重试刷新:${error.message}`
|
|
5383
|
+
});
|
|
5384
|
+
return;
|
|
5385
|
+
}
|
|
5591
5386
|
writeBridgeJson(res, 409, {
|
|
5592
5387
|
ok: false,
|
|
5593
5388
|
code: "engine-capability-rejected",
|
|
@@ -5802,6 +5597,61 @@ function registerSettingsBridge(ctx, ns, getModelsState, getSkillsState, getEngi
|
|
|
5802
5597
|
return { invalidateDescriptor: () => invalidateCachedDescriptor() };
|
|
5803
5598
|
}
|
|
5804
5599
|
//#endregion
|
|
5600
|
+
//#region src/runtime/character-tools.ts
|
|
5601
|
+
function registerCharacterTools(ctx, host) {
|
|
5602
|
+
const fiber = ctx.inject(["tools"], (toolsCtx) => toolsCtx.tools.register(defineTool({
|
|
5603
|
+
name: "character_import",
|
|
5604
|
+
description: "把一张 SillyTavern 角色卡或自包含原生角色片段直接导入为普通模块,接收 JSON / YAML 文本。PNG 从模块页导入。默认同名另存,不自动启用;导入后通过模块管理选择启用,不再使用角色卡库或应用步骤。",
|
|
5605
|
+
parameters: {
|
|
5606
|
+
name: {
|
|
5607
|
+
type: "string",
|
|
5608
|
+
required: true,
|
|
5609
|
+
description: "来源文件名(不含扩展名),作为普通模块 id 的基础。"
|
|
5610
|
+
},
|
|
5611
|
+
content: {
|
|
5612
|
+
type: "string",
|
|
5613
|
+
required: true,
|
|
5614
|
+
description: "角色卡 JSON / YAML 文本;原生片段含 id/name/rules,规则注入动作使用内嵌 text/texts,不支持外部文件。"
|
|
5615
|
+
}
|
|
5616
|
+
},
|
|
5617
|
+
output: {
|
|
5618
|
+
schema: {
|
|
5619
|
+
type: "object",
|
|
5620
|
+
additionalProperties: false,
|
|
5621
|
+
properties: {
|
|
5622
|
+
id: {
|
|
5623
|
+
type: "string",
|
|
5624
|
+
required: true
|
|
5625
|
+
},
|
|
5626
|
+
name: {
|
|
5627
|
+
type: "string",
|
|
5628
|
+
required: true
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
},
|
|
5632
|
+
render: (_args, value) => [{
|
|
5633
|
+
type: "text",
|
|
5634
|
+
text: `已导入普通模块:${value.name}(id=${value.id})。在模块管理中启用后参与装配。`
|
|
5635
|
+
}]
|
|
5636
|
+
},
|
|
5637
|
+
execute: async (args, exec) => {
|
|
5638
|
+
const target = host.target(exec);
|
|
5639
|
+
const result = await importCharacterCard(target.root, [{
|
|
5640
|
+
path: `${args.name}.json`,
|
|
5641
|
+
content: args.content
|
|
5642
|
+
}]);
|
|
5643
|
+
if (!result.ok) throw new Error(result.message);
|
|
5644
|
+
return {
|
|
5645
|
+
id: result.id,
|
|
5646
|
+
name: result.name
|
|
5647
|
+
};
|
|
5648
|
+
}
|
|
5649
|
+
})));
|
|
5650
|
+
return () => {
|
|
5651
|
+
fiber.dispose();
|
|
5652
|
+
};
|
|
5653
|
+
}
|
|
5654
|
+
//#endregion
|
|
5805
5655
|
//#region src/host/module-tool-target.ts
|
|
5806
5656
|
/**
|
|
5807
5657
|
* 工具目标来自执行 Agent 的**运行时配装记录**:这个 Agent 实际装上了哪几层提示词。
|
|
@@ -5824,7 +5674,7 @@ function resolveModuleToolTarget(exec, root, moduleIdsOf) {
|
|
|
5824
5674
|
};
|
|
5825
5675
|
}
|
|
5826
5676
|
/** 定义已经保存时,不把重建失败伪装成整个写入未发生。 */
|
|
5827
|
-
async function
|
|
5677
|
+
async function refreshModuleAfterSave(host, id) {
|
|
5828
5678
|
try {
|
|
5829
5679
|
await host.rebuild(id);
|
|
5830
5680
|
} catch (error) {
|
|
@@ -5832,243 +5682,35 @@ async function rebuildSavedPreset(host, id) {
|
|
|
5832
5682
|
}
|
|
5833
5683
|
}
|
|
5834
5684
|
//#endregion
|
|
5835
|
-
//#region src/runtime/
|
|
5836
|
-
const text$
|
|
5685
|
+
//#region src/runtime/world-book-tools.ts
|
|
5686
|
+
const text$1 = (text) => [{
|
|
5837
5687
|
type: "text",
|
|
5838
5688
|
text
|
|
5839
5689
|
}];
|
|
5840
|
-
/**
|
|
5841
|
-
function
|
|
5690
|
+
/** 注册世界书条目级模型工具;返回 disposer,随 world-book-tools 模块生命周期清理。 */
|
|
5691
|
+
function registerWorldBookTools(ctx, host) {
|
|
5842
5692
|
const fiber = ctx.inject(["tools"], (toolsCtx) => {
|
|
5843
5693
|
const disposers = [];
|
|
5694
|
+
const writeNote = (target, note) => {
|
|
5695
|
+
appendModuleMemory(target.dir, note);
|
|
5696
|
+
};
|
|
5844
5697
|
disposers.push(toolsCtx.tools.register(defineTool({
|
|
5845
|
-
name: "
|
|
5846
|
-
description: "
|
|
5698
|
+
name: "world_book_read_memory",
|
|
5699
|
+
description: "读取当前模块的持久记忆 memory.md。每次调用读取最新文件;没有记忆时返回空文本。记忆不会自动注入,读取失败会报错。",
|
|
5847
5700
|
parameters: {},
|
|
5848
5701
|
output: {
|
|
5849
5702
|
schema: {
|
|
5850
5703
|
type: "object",
|
|
5851
5704
|
additionalProperties: false,
|
|
5852
|
-
properties: {
|
|
5853
|
-
type: "array",
|
|
5854
|
-
required: true,
|
|
5855
|
-
items: {
|
|
5856
|
-
type: "object",
|
|
5857
|
-
additionalProperties: false,
|
|
5858
|
-
properties: {
|
|
5859
|
-
id: {
|
|
5860
|
-
type: "string",
|
|
5861
|
-
required: true
|
|
5862
|
-
},
|
|
5863
|
-
name: {
|
|
5864
|
-
type: "string",
|
|
5865
|
-
required: true
|
|
5866
|
-
},
|
|
5867
|
-
description: { type: "string" },
|
|
5868
|
-
hasAvatar: {
|
|
5869
|
-
type: "boolean",
|
|
5870
|
-
required: true
|
|
5871
|
-
},
|
|
5872
|
-
imported: {
|
|
5873
|
-
type: "boolean",
|
|
5874
|
-
required: true
|
|
5875
|
-
}
|
|
5876
|
-
}
|
|
5877
|
-
}
|
|
5878
|
-
} }
|
|
5879
|
-
},
|
|
5880
|
-
render: (_args, value) => text$2(JSON.stringify(value.characters))
|
|
5881
|
-
},
|
|
5882
|
-
execute: async (_args, exec) => {
|
|
5883
|
-
const target = host.target(exec);
|
|
5884
|
-
return { characters: listCharacterCards(target.root, target.id) };
|
|
5885
|
-
}
|
|
5886
|
-
})));
|
|
5887
|
-
disposers.push(toolsCtx.tools.register(defineTool({
|
|
5888
|
-
name: "character_import",
|
|
5889
|
-
description: "导入一张 SillyTavern 角色卡或自包含原生角色片段到角色卡库:接收 JSON / YAML 文本内容(可先读取文件)。PNG 角色卡请让用户从 UI 角色管理页导入。导入后需调用 character_apply 应用到当前模块。",
|
|
5890
|
-
parameters: {
|
|
5891
|
-
name: {
|
|
5892
|
-
type: "string",
|
|
5893
|
-
required: true,
|
|
5894
|
-
description: "角色卡文件名(不含 .json 扩展名),将作为模块/角色卡 id 基础。"
|
|
5895
|
-
},
|
|
5896
|
-
content: {
|
|
5897
|
-
type: "string",
|
|
5898
|
-
required: true,
|
|
5899
|
-
description: "角色卡 JSON / YAML 文本:SillyTavern chara_card_v2/v3,或含 id/name/promptConfigs 的原生片段。原生片段只支持内嵌 text/texts 和控制配置,不支持外部文件。"
|
|
5900
|
-
}
|
|
5901
|
-
},
|
|
5902
|
-
output: {
|
|
5903
|
-
schema: {
|
|
5904
|
-
type: "object",
|
|
5905
|
-
additionalProperties: false,
|
|
5906
|
-
properties: {
|
|
5907
|
-
id: {
|
|
5908
|
-
type: "string",
|
|
5909
|
-
required: true
|
|
5910
|
-
},
|
|
5911
|
-
name: {
|
|
5912
|
-
type: "string",
|
|
5913
|
-
required: true
|
|
5914
|
-
}
|
|
5915
|
-
}
|
|
5916
|
-
},
|
|
5917
|
-
render: (_args, value) => text$2(`角色卡已入库:${value.name}(id=${value.id})。调用 character_apply 可应用到当前模块。`)
|
|
5918
|
-
},
|
|
5919
|
-
execute: async (args, exec) => {
|
|
5920
|
-
const target = host.target(exec);
|
|
5921
|
-
const result = importCharacterCard(target.root, [{
|
|
5922
|
-
path: `${args.name}.json`,
|
|
5923
|
-
content: args.content
|
|
5924
|
-
}]);
|
|
5925
|
-
if (!result.ok) throw new Error(result.message);
|
|
5926
|
-
return {
|
|
5927
|
-
id: result.id,
|
|
5928
|
-
name: result.name
|
|
5929
|
-
};
|
|
5930
|
-
}
|
|
5931
|
-
})));
|
|
5932
|
-
disposers.push(toolsCtx.tools.register(defineTool({
|
|
5933
|
-
name: "character_apply",
|
|
5934
|
-
description: "把角色卡库中一张角色卡的参数(角色设定 / 系统提示 / 开场白 / 世界书 / 提示词配置)合并进当前会话绑定的模块(promptConfigs 带 chara-<id>- 前缀防冲突,params 合并,meta.importedCharacters 记录),并立即重建生成目录。重复应用幂等。",
|
|
5935
|
-
parameters: { id: {
|
|
5936
|
-
type: "string",
|
|
5937
|
-
required: true,
|
|
5938
|
-
description: "角色卡 id(character_list 返回)。"
|
|
5939
|
-
} },
|
|
5940
|
-
output: {
|
|
5941
|
-
schema: {
|
|
5942
|
-
type: "object",
|
|
5943
|
-
additionalProperties: false,
|
|
5944
|
-
properties: {
|
|
5945
|
-
id: {
|
|
5946
|
-
type: "string",
|
|
5947
|
-
required: true
|
|
5948
|
-
},
|
|
5949
|
-
count: {
|
|
5950
|
-
type: "integer",
|
|
5951
|
-
required: true
|
|
5952
|
-
}
|
|
5953
|
-
}
|
|
5954
|
-
},
|
|
5955
|
-
render: (_args, value) => text$2(`已导入到当前模块(${value.count} 条配置),生成目录已重建。`)
|
|
5956
|
-
},
|
|
5957
|
-
execute: async (args, exec) => {
|
|
5958
|
-
const target = host.target(exec);
|
|
5959
|
-
const result = applyCharacterToPreset(target.root, target.id, args.id);
|
|
5960
|
-
if (!result.ok) throw new Error(result.message);
|
|
5961
|
-
await rebuildSavedPreset(host, target.id);
|
|
5962
|
-
return {
|
|
5963
|
-
id: args.id,
|
|
5964
|
-
count: result.count
|
|
5965
|
-
};
|
|
5966
|
-
}
|
|
5967
|
-
})));
|
|
5968
|
-
disposers.push(toolsCtx.tools.register(defineTool({
|
|
5969
|
-
name: "character_remove",
|
|
5970
|
-
description: "从当前会话绑定的模块移除一张已导入角色卡的参数(删 chara-<id>- 前缀配置、该卡声明的 params 键、meta.importedCharacters 除名),并立即重建生成目录。角色卡库条目不受影响。",
|
|
5971
|
-
parameters: { id: {
|
|
5972
|
-
type: "string",
|
|
5973
|
-
required: true,
|
|
5974
|
-
description: "角色卡 id(character_list 返回,imported=true 的卡)。"
|
|
5975
|
-
} },
|
|
5976
|
-
output: {
|
|
5977
|
-
schema: {
|
|
5978
|
-
type: "object",
|
|
5979
|
-
additionalProperties: false,
|
|
5980
|
-
properties: {
|
|
5981
|
-
id: {
|
|
5982
|
-
type: "string",
|
|
5983
|
-
required: true
|
|
5984
|
-
},
|
|
5985
|
-
count: {
|
|
5986
|
-
type: "integer",
|
|
5987
|
-
required: true
|
|
5988
|
-
}
|
|
5989
|
-
}
|
|
5990
|
-
},
|
|
5991
|
-
render: (_args, value) => text$2(`已从当前模块移除(${value.count} 条配置),生成目录已重建。`)
|
|
5992
|
-
},
|
|
5993
|
-
execute: async (args, exec) => {
|
|
5994
|
-
const target = host.target(exec);
|
|
5995
|
-
const result = removeCharacterFromPreset(target.root, target.id, args.id);
|
|
5996
|
-
if (!result.ok) throw new Error(result.message);
|
|
5997
|
-
await rebuildSavedPreset(host, target.id);
|
|
5998
|
-
return {
|
|
5999
|
-
id: args.id,
|
|
6000
|
-
count: result.count
|
|
6001
|
-
};
|
|
6002
|
-
}
|
|
6003
|
-
})));
|
|
6004
|
-
disposers.push(toolsCtx.tools.register(defineTool({
|
|
6005
|
-
name: "character_delete",
|
|
6006
|
-
description: "从角色卡库删除一张角色卡(含其转换参数与头像)。已导入当前模块的参数不受影响(如需清理请先调用 character_remove)。",
|
|
6007
|
-
parameters: { id: {
|
|
6008
|
-
type: "string",
|
|
6009
|
-
required: true,
|
|
6010
|
-
description: "角色卡 id(character_list 返回)。"
|
|
6011
|
-
} },
|
|
6012
|
-
output: {
|
|
6013
|
-
schema: {
|
|
6014
|
-
type: "object",
|
|
6015
|
-
additionalProperties: false,
|
|
6016
|
-
properties: { id: {
|
|
5705
|
+
properties: { memory: {
|
|
6017
5706
|
type: "string",
|
|
6018
5707
|
required: true
|
|
6019
5708
|
} }
|
|
6020
5709
|
},
|
|
6021
|
-
render: (_args, value) => text$
|
|
5710
|
+
render: (_args, value) => text$1(value.memory)
|
|
6022
5711
|
},
|
|
6023
|
-
execute: async (
|
|
6024
|
-
const result = deleteCharacterCard(host.target(exec).root, args.id);
|
|
6025
|
-
if (!result.ok) throw new Error(result.message);
|
|
6026
|
-
return { id: args.id };
|
|
6027
|
-
}
|
|
5712
|
+
execute: async (_args, exec) => ({ memory: readModuleMemory(host.target(exec).dir) })
|
|
6028
5713
|
})));
|
|
6029
|
-
return () => {
|
|
6030
|
-
for (const dispose of disposers) dispose();
|
|
6031
|
-
};
|
|
6032
|
-
});
|
|
6033
|
-
return () => {
|
|
6034
|
-
fiber.dispose();
|
|
6035
|
-
};
|
|
6036
|
-
}
|
|
6037
|
-
//#endregion
|
|
6038
|
-
//#region src/runtime/world-book-tools.ts
|
|
6039
|
-
const text$1 = (text) => [{
|
|
6040
|
-
type: "text",
|
|
6041
|
-
text
|
|
6042
|
-
}];
|
|
6043
|
-
/** 从条目 id 解析来源角色卡(并入前缀 + cardId,cardId 可含连字符:遍历库目录取最长匹配)。
|
|
6044
|
-
* 前缀**两种都认**:新写入是 `module-<id>-`,用户既有的老条目仍是 `chara-<id>-`。 */
|
|
6045
|
-
function sourceCardId(moduleRoot, entryId) {
|
|
6046
|
-
const root = charactersDir(moduleRoot);
|
|
6047
|
-
if (!entryId.startsWith("module-") && !entryId.startsWith("chara-")) return void 0;
|
|
6048
|
-
let best;
|
|
6049
|
-
try {
|
|
6050
|
-
for (const dir of readdirSync(root, { withFileTypes: true })) {
|
|
6051
|
-
if (!dir.isDirectory() || dir.name.startsWith(".")) continue;
|
|
6052
|
-
if ((entryId.startsWith(`module-${dir.name}-`) || entryId.startsWith(`chara-${dir.name}-`)) && (best === void 0 || dir.name.length > best.length)) best = dir.name;
|
|
6053
|
-
}
|
|
6054
|
-
} catch {}
|
|
6055
|
-
return best;
|
|
6056
|
-
}
|
|
6057
|
-
/** 注册世界书条目级模型工具;返回 disposer,随 world-book-tools 模块生命周期清理。 */
|
|
6058
|
-
function registerWorldBookTools(ctx, host) {
|
|
6059
|
-
const fiber = ctx.inject(["tools"], (toolsCtx) => {
|
|
6060
|
-
const disposers = [];
|
|
6061
|
-
/** note 归属写入:角色卡条目 → 卡记忆;其他 → 模块记忆。 */
|
|
6062
|
-
const writeNote = (target, entryId, note) => {
|
|
6063
|
-
if (note === void 0 || note.trim().length === 0) return;
|
|
6064
|
-
const cardId = entryId !== void 0 ? sourceCardId(target.root, entryId) : void 0;
|
|
6065
|
-
if (cardId !== void 0) {
|
|
6066
|
-
appendMemoryFile(join(target.root, ".characters", cardId, "memory.md"), note, "# 角色记忆");
|
|
6067
|
-
try {
|
|
6068
|
-
syncImportedCharacterMemory(target.root, target.id, cardId);
|
|
6069
|
-
} catch {}
|
|
6070
|
-
} else appendMemoryFile(join(target.dir, "memory.md"), note, "# 本地记忆");
|
|
6071
|
-
};
|
|
6072
5714
|
disposers.push(toolsCtx.tools.register(defineTool({
|
|
6073
5715
|
name: "world_book_list",
|
|
6074
5716
|
description: "列出当前模块的世界书条目(world-book 策略配置:id / 名称 / 关键字 / 常驻 / 启用)。世界书 = 上下文条目:无 keys 的全局条目每次注入,有 keys 条目命中聊天内容才注入。增删改前先调用本工具获取 id。",
|
|
@@ -6120,7 +5762,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
6120
5762
|
})));
|
|
6121
5763
|
disposers.push(toolsCtx.tools.register(defineTool({
|
|
6122
5764
|
name: "world_book_upsert",
|
|
6123
|
-
description: "新增或更新当前模块的一条世界书条目(world-book 策略配置):按 id 更新(不存在则新增,id 自动生成 lore-<n>)。constant=true 常驻注入;否则命中 keys(或 secondaryKeys)任一关键字注入;无 keys 条目按全局每次注入。note
|
|
5765
|
+
description: "新增或更新当前模块的一条世界书条目(world-book 策略配置):按 id 更新(不存在则新增,id 自动生成 lore-<n>)。constant=true 常驻注入;否则命中 keys(或 secondaryKeys)任一关键字注入;无 keys 条目按全局每次注入。note 可选:写入当前模块的持久记忆 memory.md,使用 world_book_read_memory 按需读取。写盘后立即重新装配。",
|
|
6124
5766
|
parameters: {
|
|
6125
5767
|
id: {
|
|
6126
5768
|
type: "string",
|
|
@@ -6160,7 +5802,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
6160
5802
|
},
|
|
6161
5803
|
note: {
|
|
6162
5804
|
type: "string",
|
|
6163
|
-
description: "
|
|
5805
|
+
description: "可选:操作笔记,写入当前模块的 memory.md,不自动注入。"
|
|
6164
5806
|
}
|
|
6165
5807
|
},
|
|
6166
5808
|
output: {
|
|
@@ -6197,8 +5839,8 @@ function registerWorldBookTools(ctx, host) {
|
|
|
6197
5839
|
...entry,
|
|
6198
5840
|
id: targetId
|
|
6199
5841
|
});
|
|
6200
|
-
writeNote(target,
|
|
6201
|
-
await
|
|
5842
|
+
writeNote(target, typeof args.note === "string" ? args.note : "");
|
|
5843
|
+
await refreshModuleAfterSave(host, target.id);
|
|
6202
5844
|
return {
|
|
6203
5845
|
id: targetId,
|
|
6204
5846
|
count
|
|
@@ -6207,7 +5849,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
6207
5849
|
})));
|
|
6208
5850
|
disposers.push(toolsCtx.tools.register(defineTool({
|
|
6209
5851
|
name: "world_book_delete",
|
|
6210
|
-
description: "删除当前模块的一条世界书条目(world_book_list 获取 id)。note
|
|
5852
|
+
description: "删除当前模块的一条世界书条目(world_book_list 获取 id)。note 可选:写入当前模块的 memory.md,使用 world_book_read_memory 按需读取。删除后立即重新装配。",
|
|
6211
5853
|
parameters: {
|
|
6212
5854
|
id: {
|
|
6213
5855
|
type: "string",
|
|
@@ -6216,7 +5858,7 @@ function registerWorldBookTools(ctx, host) {
|
|
|
6216
5858
|
},
|
|
6217
5859
|
note: {
|
|
6218
5860
|
type: "string",
|
|
6219
|
-
description: "
|
|
5861
|
+
description: "可选:操作笔记,写入当前模块的 memory.md,不自动注入。"
|
|
6220
5862
|
}
|
|
6221
5863
|
},
|
|
6222
5864
|
output: {
|
|
@@ -6239,8 +5881,8 @@ function registerWorldBookTools(ctx, host) {
|
|
|
6239
5881
|
execute: async (args, exec) => {
|
|
6240
5882
|
const target = host.target(exec);
|
|
6241
5883
|
const keptCount = deleteWorldBookEntry(target.dir, args.id);
|
|
6242
|
-
writeNote(target,
|
|
6243
|
-
await
|
|
5884
|
+
writeNote(target, typeof args.note === "string" ? args.note : "");
|
|
5885
|
+
await refreshModuleAfterSave(host, target.id);
|
|
6244
5886
|
return {
|
|
6245
5887
|
id: args.id,
|
|
6246
5888
|
count: keptCount
|
|
@@ -6372,92 +6014,22 @@ function registerSessionVarTools(ctx) {
|
|
|
6372
6014
|
//#endregion
|
|
6373
6015
|
//#region src/runtime/tui.ts
|
|
6374
6016
|
/** dsh-tui 全局开关:键名与 settings 路径一致(settings mutate)。 */
|
|
6375
|
-
const TUI_GLOBAL_SWITCHES = [["
|
|
6376
|
-
/** dsh-tui 参数开关:写激活模块
|
|
6017
|
+
const TUI_GLOBAL_SWITCHES = [["modulesEnabled", "模块运行总开关"]];
|
|
6018
|
+
/** dsh-tui 参数开关:写激活模块 module.yml(settings 不再承载引擎参数)。 */
|
|
6377
6019
|
const TUI_PARAM_SWITCHES = Object.entries(ENGINE_PARAM_DEFINITIONS).filter(([, definition]) => definition.kind === "boolean").map(([key]) => [key, key]);
|
|
6378
|
-
/** 参数显示行(从激活模块 preset.yml params 读)。 */
|
|
6379
|
-
const TUI_PARAM_TEXT_LINES = [
|
|
6380
|
-
[
|
|
6381
|
-
"modelProvider",
|
|
6382
|
-
"modelProvider",
|
|
6383
|
-
"(空 = 不设置)"
|
|
6384
|
-
],
|
|
6385
|
-
[
|
|
6386
|
-
"modelName",
|
|
6387
|
-
"modelName",
|
|
6388
|
-
"(空 = 不设置)"
|
|
6389
|
-
],
|
|
6390
|
-
[
|
|
6391
|
-
"subagentModelProvider",
|
|
6392
|
-
"subagentModelProvider",
|
|
6393
|
-
"(空 = 不设置)"
|
|
6394
|
-
],
|
|
6395
|
-
[
|
|
6396
|
-
"subagentModelName",
|
|
6397
|
-
"subagentModelName",
|
|
6398
|
-
"(空 = 不设置)"
|
|
6399
|
-
],
|
|
6400
|
-
[
|
|
6401
|
-
"modelReasoningEffort",
|
|
6402
|
-
"modelReasoningEffort",
|
|
6403
|
-
"(空 = 不设置)"
|
|
6404
|
-
],
|
|
6405
|
-
[
|
|
6406
|
-
"modelTemperature",
|
|
6407
|
-
"modelTemperature",
|
|
6408
|
-
"(空 = 不设置)"
|
|
6409
|
-
],
|
|
6410
|
-
[
|
|
6411
|
-
"modelMaxTokens",
|
|
6412
|
-
"modelMaxTokens",
|
|
6413
|
-
"(空 = 不设置)"
|
|
6414
|
-
],
|
|
6415
|
-
[
|
|
6416
|
-
"subagentReasoningEffort",
|
|
6417
|
-
"subagentReasoningEffort",
|
|
6418
|
-
"(空 = 不设置)"
|
|
6419
|
-
],
|
|
6420
|
-
[
|
|
6421
|
-
"subagentTemperature",
|
|
6422
|
-
"subagentTemperature",
|
|
6423
|
-
"(空 = 不设置)"
|
|
6424
|
-
],
|
|
6425
|
-
[
|
|
6426
|
-
"subagentMaxTokens",
|
|
6427
|
-
"subagentMaxTokens",
|
|
6428
|
-
"(空 = 不设置)"
|
|
6429
|
-
]
|
|
6430
|
-
];
|
|
6431
|
-
/** 把布尔开关渲染成 dsh-tui 命令输出。 */
|
|
6432
|
-
/** 实际生效配置:生成目录优先(引擎加载源),settings 覆盖层作回退。 */
|
|
6433
|
-
function resolvePromptConfigs(moduleDir, fallback) {
|
|
6434
|
-
if (moduleDir === void 0 || moduleDir.length === 0) return fallback;
|
|
6435
|
-
try {
|
|
6436
|
-
const actual = listPromptConfigSpecs(join(moduleDir, MODULE_CONFIGS_DIR));
|
|
6437
|
-
return actual.length > 0 ? actual : fallback;
|
|
6438
|
-
} catch {
|
|
6439
|
-
return fallback;
|
|
6440
|
-
}
|
|
6441
|
-
}
|
|
6442
6020
|
/** 激活模块参数(status 显示与参数开关来源;settings 不再承载引擎参数)。 */
|
|
6443
|
-
function
|
|
6021
|
+
function readModuleParams(moduleDir) {
|
|
6444
6022
|
if (moduleDir === void 0 || moduleDir.length === 0) return {};
|
|
6445
6023
|
try {
|
|
6446
6024
|
const spec = loadModuleSpec(moduleDir);
|
|
6447
|
-
|
|
6448
|
-
if (Array.isArray(spec.promptConfigs)) params.promptConfigs = spec.promptConfigs;
|
|
6449
|
-
return params;
|
|
6025
|
+
return resolveModuleParams(spec, {});
|
|
6450
6026
|
} catch {
|
|
6451
6027
|
return {};
|
|
6452
6028
|
}
|
|
6453
6029
|
}
|
|
6454
|
-
function renderTuiStatus(source, params,
|
|
6030
|
+
function renderTuiStatus(source, params, rules) {
|
|
6455
6031
|
const onOff = (value) => value ? "开" : "关";
|
|
6456
6032
|
const paramBoolean = (key) => (params[key] ?? ENGINE_PARAM_DEFINITIONS[key]?.defaultValue) === true;
|
|
6457
|
-
const paramText = (key) => {
|
|
6458
|
-
const value = params[key];
|
|
6459
|
-
return typeof value === "string" && value.length > 0 ? value : "";
|
|
6460
|
-
};
|
|
6461
6033
|
const lines = [
|
|
6462
6034
|
"提示词工具开关",
|
|
6463
6035
|
...TUI_GLOBAL_SWITCHES.map(([key, label]) => {
|
|
@@ -6467,16 +6039,11 @@ function renderTuiStatus(source, params, promptConfigs) {
|
|
|
6467
6039
|
...TUI_PARAM_SWITCHES.map(([key, label]) => {
|
|
6468
6040
|
return `${key.padEnd(22)}${onOff(paramBoolean(key))} ${label}(模块)`;
|
|
6469
6041
|
}),
|
|
6470
|
-
"模型请求参数:",
|
|
6471
|
-
...TUI_PARAM_TEXT_LINES.map(([key, label, emptyText]) => {
|
|
6472
|
-
const text = paramText(key);
|
|
6473
|
-
return ` ${label.padEnd(26)}${text.length > 0 ? text : emptyText}`;
|
|
6474
|
-
}),
|
|
6475
6042
|
` modelsAvailable ${source.modelsAvailable ? "是" : "否(未检测到模型服务商)"}`,
|
|
6476
6043
|
` activeSkillsDirs ${source.activeSkillsDirs.length > 0 ? source.activeSkillsDirs.join(" → ") : "(未解析到技能目录)"}`,
|
|
6477
|
-
"
|
|
6044
|
+
"行为规则(条件 → 动作):"
|
|
6478
6045
|
];
|
|
6479
|
-
for (const
|
|
6046
|
+
for (const rule of rules) lines.push(`${("config " + rule.id).padEnd(22)}${onOff(rule.enabled !== false)} ${rule.then.map((action) => action.kind).join(" → ")}`);
|
|
6480
6047
|
lines.push("技能开关:");
|
|
6481
6048
|
for (const skill of source.skillCatalog) {
|
|
6482
6049
|
const value = skill.modelInvocable || skill.userInvocable;
|
|
@@ -6485,49 +6052,10 @@ function renderTuiStatus(source, params, promptConfigs) {
|
|
|
6485
6052
|
}
|
|
6486
6053
|
return lines.join("\n");
|
|
6487
6054
|
}
|
|
6488
|
-
/**
|
|
6489
|
-
function renderConfigDetail(
|
|
6490
|
-
const
|
|
6491
|
-
|
|
6492
|
-
const lines = [`提示词配置 ${config.id}`, ""];
|
|
6493
|
-
const fields = [
|
|
6494
|
-
["name", config.name],
|
|
6495
|
-
["enabled", config.enabled !== false],
|
|
6496
|
-
["layer", config.layer],
|
|
6497
|
-
["strategy", config.strategy],
|
|
6498
|
-
["position", config.position],
|
|
6499
|
-
["dedupe", config.dedupe],
|
|
6500
|
-
["promotion", config.promotion],
|
|
6501
|
-
["audience", config.audience ?? void 0],
|
|
6502
|
-
["modelScope", config.modelScope],
|
|
6503
|
-
["configKind", config.configKind],
|
|
6504
|
-
["order", config.order],
|
|
6505
|
-
["role", config.role],
|
|
6506
|
-
["mergeMode", config.mergeMode],
|
|
6507
|
-
["sourceKind", config.sourceKind],
|
|
6508
|
-
["form", config.form],
|
|
6509
|
-
["fill", config.fill],
|
|
6510
|
-
["templateFile", config.templateFile],
|
|
6511
|
-
["variables", config.variables],
|
|
6512
|
-
["params", config.params]
|
|
6513
|
-
];
|
|
6514
|
-
for (const [key, value] of fields) {
|
|
6515
|
-
if (value === void 0 || value === "" || Array.isArray(value) && value.length === 0) continue;
|
|
6516
|
-
if (key === "variables" || key === "params") {
|
|
6517
|
-
lines.push(`${key.padEnd(14)}${JSON.stringify(value)}`);
|
|
6518
|
-
continue;
|
|
6519
|
-
}
|
|
6520
|
-
lines.push(`${key.padEnd(14)}${String(value)}`);
|
|
6521
|
-
}
|
|
6522
|
-
if (typeof config.text === "string" && config.text.length > 0) {
|
|
6523
|
-
lines.push("text:");
|
|
6524
|
-
lines.push(config.text);
|
|
6525
|
-
}
|
|
6526
|
-
if (Array.isArray(config.texts) && config.texts.length > 0) {
|
|
6527
|
-
lines.push("texts:");
|
|
6528
|
-
for (const item of config.texts) lines.push(` - ${item}`);
|
|
6529
|
-
}
|
|
6530
|
-
return lines.join("\n");
|
|
6055
|
+
/** 详情呈现唯一声明,不把动作投影误当可独立启停的配置。 */
|
|
6056
|
+
function renderConfigDetail(id, rules) {
|
|
6057
|
+
const rule = rules.find((item) => item.id === id);
|
|
6058
|
+
return rule === void 0 ? `未找到规则 ${id}` : JSON.stringify(rule, null, 2);
|
|
6531
6059
|
}
|
|
6532
6060
|
/** 解析 on/off/toggle 三种输入。 */
|
|
6533
6061
|
function parseTuiBoolean(token, current) {
|
|
@@ -6559,7 +6087,7 @@ function parseIdentifierAndAction(tokens, exists) {
|
|
|
6559
6087
|
return { id: tokens.join(" ") };
|
|
6560
6088
|
}
|
|
6561
6089
|
/** 通过 DSH 命令注册表暴露 /prompt-tool,Web 与 dsh-tui 都能执行。 */
|
|
6562
|
-
function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog,
|
|
6090
|
+
function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog, getModuleDirectory, saveModuleParam, toggleSkillState, refreshModule) {
|
|
6563
6091
|
ctx.inject(["settings"], (sctx) => {
|
|
6564
6092
|
return sctx.commands.register({
|
|
6565
6093
|
name: "prompt-tool",
|
|
@@ -6573,13 +6101,13 @@ function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog,
|
|
|
6573
6101
|
/prompt-tool config <id>(id 可含空格)
|
|
6574
6102
|
/prompt-tool config <id> on|off|toggle`
|
|
6575
6103
|
});
|
|
6576
|
-
const
|
|
6577
|
-
if (
|
|
6104
|
+
const persistModuleParam = async (key, value) => {
|
|
6105
|
+
if (saveModuleParam === void 0) return {
|
|
6578
6106
|
kind: "error",
|
|
6579
6107
|
text: `无法保存 ${key}:模块参数保存回调不可用`
|
|
6580
6108
|
};
|
|
6581
6109
|
try {
|
|
6582
|
-
await
|
|
6110
|
+
await saveModuleParam(key, value);
|
|
6583
6111
|
return;
|
|
6584
6112
|
} catch (error) {
|
|
6585
6113
|
return {
|
|
@@ -6590,9 +6118,18 @@ function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog,
|
|
|
6590
6118
|
};
|
|
6591
6119
|
const tokens = invocation.rawInput.trim().split(/\s+/).filter((token) => token.length > 0);
|
|
6592
6120
|
const source = getSource();
|
|
6593
|
-
const moduleDir =
|
|
6594
|
-
const params =
|
|
6595
|
-
|
|
6121
|
+
const moduleDir = getModuleDirectory?.();
|
|
6122
|
+
const params = readModuleParams(moduleDir);
|
|
6123
|
+
let snapshot;
|
|
6124
|
+
try {
|
|
6125
|
+
if (moduleDir) snapshot = readModuleRules(moduleDir);
|
|
6126
|
+
} catch (error) {
|
|
6127
|
+
return {
|
|
6128
|
+
kind: "error",
|
|
6129
|
+
text: `读取规则失败:${error instanceof Error ? error.message : String(error)}`
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
const rules = snapshot?.rules ?? [];
|
|
6596
6133
|
if (tokens.length === 0 || tokens[0] === "status") {
|
|
6597
6134
|
const detection = getModelsState();
|
|
6598
6135
|
const catalog = await getModelCatalog();
|
|
@@ -6601,7 +6138,7 @@ function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog,
|
|
|
6601
6138
|
const modelsLine = catalogEntries.length > 0 ? `检测到的模型名: ${catalogEntries.map(([provider, models]) => `${provider} → ${models.join(", ")}`).join(";")}` : "未检测到模型名(adapter 未公布或查询失败)";
|
|
6602
6139
|
return {
|
|
6603
6140
|
kind: "success",
|
|
6604
|
-
text: renderTuiStatus(source, params,
|
|
6141
|
+
text: renderTuiStatus(source, params, rules) + "\n" + providersLine + "\n" + modelsLine
|
|
6605
6142
|
};
|
|
6606
6143
|
}
|
|
6607
6144
|
if (tokens[0] === "skill") {
|
|
@@ -6632,33 +6169,55 @@ function registerTuiCommand(ctx, ns, getSource, getModelsState, getModelCatalog,
|
|
|
6632
6169
|
kind: "success",
|
|
6633
6170
|
text: `已把技能 ${id} 设为 ${next ? "开" : "关"}
|
|
6634
6171
|
|
|
6635
|
-
${renderTuiStatus(getSource(),
|
|
6172
|
+
${renderTuiStatus(getSource(), readModuleParams(moduleDir), rules)}`
|
|
6636
6173
|
};
|
|
6637
6174
|
}
|
|
6638
6175
|
if (tokens[0] === "config") {
|
|
6639
|
-
const { id, action } = parseIdentifierAndAction(tokens.slice(1), (candidate) =>
|
|
6176
|
+
const { id, action } = parseIdentifierAndAction(tokens.slice(1), (candidate) => rules.some((rule) => rule.id === candidate));
|
|
6640
6177
|
if (id.length === 0) return usage();
|
|
6641
|
-
const current =
|
|
6178
|
+
const current = rules.find((rule) => rule.id === id);
|
|
6642
6179
|
if (current === void 0) return {
|
|
6643
6180
|
kind: "error",
|
|
6644
|
-
text:
|
|
6181
|
+
text: `未找到规则 ${id};用 /prompt-tool status 查看全部 id。`
|
|
6645
6182
|
};
|
|
6646
6183
|
if (action === void 0) return {
|
|
6647
6184
|
kind: "success",
|
|
6648
|
-
text: renderConfigDetail(
|
|
6185
|
+
text: renderConfigDetail(id, rules)
|
|
6649
6186
|
};
|
|
6650
6187
|
const next = parseTuiBoolean(action, current.enabled !== false);
|
|
6651
6188
|
if (next === void 0) return usage();
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
}
|
|
6656
|
-
|
|
6189
|
+
if (!moduleDir || !snapshot || !refreshModule) return {
|
|
6190
|
+
kind: "error",
|
|
6191
|
+
text: "规则保存通道不可用"
|
|
6192
|
+
};
|
|
6193
|
+
try {
|
|
6194
|
+
editModuleRules(moduleDir, {
|
|
6195
|
+
expectedRevision: snapshot.revision,
|
|
6196
|
+
...next ? { activateRuleId: id } : { edits: [{
|
|
6197
|
+
previousId: id,
|
|
6198
|
+
rule: {
|
|
6199
|
+
...current,
|
|
6200
|
+
enabled: false
|
|
6201
|
+
}
|
|
6202
|
+
}] }
|
|
6203
|
+
});
|
|
6204
|
+
} catch (error) {
|
|
6205
|
+
return {
|
|
6206
|
+
kind: "error",
|
|
6207
|
+
text: `保存规则失败:${error instanceof Error ? error.message : String(error)}`
|
|
6208
|
+
};
|
|
6209
|
+
}
|
|
6210
|
+
try {
|
|
6211
|
+
await refreshModule(basename(moduleDir));
|
|
6212
|
+
} catch (error) {
|
|
6213
|
+
return {
|
|
6214
|
+
kind: "error",
|
|
6215
|
+
text: `规则已保存,但重新装配失败:${error instanceof Error ? error.message : String(error)}`
|
|
6216
|
+
};
|
|
6217
|
+
}
|
|
6657
6218
|
return {
|
|
6658
6219
|
kind: "success",
|
|
6659
|
-
text:
|
|
6660
|
-
|
|
6661
|
-
${renderConfigDetail(getSource(), id, resolvePromptConfigs(getPresetConfigsDir?.(), []))}`
|
|
6220
|
+
text: `已把规则 ${id} 设为 ${next ? "开" : "关"}\n\n${renderConfigDetail(id, readModuleRules(moduleDir).rules)}`
|
|
6662
6221
|
};
|
|
6663
6222
|
}
|
|
6664
6223
|
const action = tokens[0];
|
|
@@ -6673,20 +6232,16 @@ ${renderConfigDetail(getSource(), id, resolvePromptConfigs(getPresetConfigsDir?.
|
|
|
6673
6232
|
};
|
|
6674
6233
|
const next = parseTuiBoolean(action, currentValue);
|
|
6675
6234
|
if (next === void 0) return usage();
|
|
6676
|
-
if (globalSwitch) await sctx.settings.mutate(ns,
|
|
6677
|
-
op: "set",
|
|
6678
|
-
path: [key],
|
|
6679
|
-
value: next
|
|
6680
|
-
}]);
|
|
6235
|
+
if (globalSwitch) await sctx.settings.mutate(ns, modulesEnabledOps(next));
|
|
6681
6236
|
else {
|
|
6682
|
-
const failure = await
|
|
6237
|
+
const failure = await persistModuleParam(key, next);
|
|
6683
6238
|
if (failure !== void 0) return failure;
|
|
6684
6239
|
}
|
|
6685
6240
|
return {
|
|
6686
6241
|
kind: "success",
|
|
6687
6242
|
text: `已把 ${key} 设为 ${next ? "开" : "关"}
|
|
6688
6243
|
|
|
6689
|
-
${renderTuiStatus(getSource(),
|
|
6244
|
+
${renderTuiStatus(getSource(), readModuleParams(moduleDir), moduleDir ? readModuleRules(moduleDir).rules : [])}`
|
|
6690
6245
|
};
|
|
6691
6246
|
}
|
|
6692
6247
|
});
|
|
@@ -7023,6 +6578,162 @@ function createSkillsRuntime(ctx, options = {}) {
|
|
|
7023
6578
|
};
|
|
7024
6579
|
}
|
|
7025
6580
|
//#endregion
|
|
6581
|
+
//#region engine/rule-runtime.mjs
|
|
6582
|
+
const noNext = /* @__PURE__ */ new Set([
|
|
6583
|
+
"agent/inbox/inserted",
|
|
6584
|
+
"agent/turn-stopping",
|
|
6585
|
+
"subagent/start",
|
|
6586
|
+
"subagent/end"
|
|
6587
|
+
]);
|
|
6588
|
+
const compare = (a, b) => a.action.channelOrder - b.action.channelOrder || a.moduleId.localeCompare(b.moduleId) || a.ruleIndex - b.ruleIndex || a.action.actionIndex - b.action.actionIndex;
|
|
6589
|
+
/** 所有来源只进入这个装配入口;注入保留原有批处理和协调器所有权。 */
|
|
6590
|
+
function mountRuleSources(ctx, sources, options = {}) {
|
|
6591
|
+
const warnOnce = options.warnOnce ?? createWarnOnce(ctx, options.plugin ?? "rule-engine");
|
|
6592
|
+
const releases = [];
|
|
6593
|
+
const points = /* @__PURE__ */ new Map();
|
|
6594
|
+
const injections = new Map(sources.map((source) => [source.moduleId, {
|
|
6595
|
+
sourceId: `module:${source.moduleId}`,
|
|
6596
|
+
configs: [],
|
|
6597
|
+
ruleActions: [],
|
|
6598
|
+
officialInstructions: source.officialInstructions === true
|
|
6599
|
+
}]));
|
|
6600
|
+
const rules = sources.flatMap((source) => source.rules.filter((rule) => rule.enabled !== false));
|
|
6601
|
+
const observer = wireTriggerObservers(ctx, rules, {
|
|
6602
|
+
plugin: options.plugin ?? "rule-engine",
|
|
6603
|
+
warnOnce
|
|
6604
|
+
});
|
|
6605
|
+
if (observer) releases.push(observer);
|
|
6606
|
+
let active = true;
|
|
6607
|
+
const bind = (item, on) => prepareAction(item.action, {
|
|
6608
|
+
plugin: options.plugin,
|
|
6609
|
+
warnOnce,
|
|
6610
|
+
promptConfigOptions: item.rule.promptConfigOptions,
|
|
6611
|
+
on
|
|
6612
|
+
})(ctx);
|
|
6613
|
+
try {
|
|
6614
|
+
for (const source of sources) for (const [ruleIndex, rule] of source.rules.entries()) {
|
|
6615
|
+
if (rule.enabled === false) continue;
|
|
6616
|
+
for (const action of rule.actions) {
|
|
6617
|
+
const item = {
|
|
6618
|
+
moduleId: source.moduleId,
|
|
6619
|
+
ruleIndex,
|
|
6620
|
+
rule,
|
|
6621
|
+
action
|
|
6622
|
+
};
|
|
6623
|
+
if (action.execution.lifecycle === "registration" && action.kind === "guard") {
|
|
6624
|
+
releases.push(bind(item));
|
|
6625
|
+
continue;
|
|
6626
|
+
}
|
|
6627
|
+
const point = action.execution;
|
|
6628
|
+
const key = `${point.channel}:${point.phase}:${action.waterfallPosition}`;
|
|
6629
|
+
const bucket = points.get(key) ?? {
|
|
6630
|
+
...point,
|
|
6631
|
+
waterfallPosition: action.waterfallPosition,
|
|
6632
|
+
items: []
|
|
6633
|
+
};
|
|
6634
|
+
bucket.items.push(item);
|
|
6635
|
+
points.set(key, bucket);
|
|
6636
|
+
}
|
|
6637
|
+
}
|
|
6638
|
+
for (const point of points.values()) {
|
|
6639
|
+
point.items.sort(compare);
|
|
6640
|
+
const handlers = [];
|
|
6641
|
+
let textConfigs = [];
|
|
6642
|
+
const flushLayers = () => {
|
|
6643
|
+
if (!textConfigs.length) return;
|
|
6644
|
+
releases.push(wireLayers(ctx, textConfigs, warnOnce, { on: (channel, handler) => {
|
|
6645
|
+
if (channel !== point.channel) throw new TypeError(`rule action registered outside ${point.channel}: ${channel}`);
|
|
6646
|
+
handlers.push({ handler });
|
|
6647
|
+
return () => {};
|
|
6648
|
+
} }));
|
|
6649
|
+
textConfigs = [];
|
|
6650
|
+
};
|
|
6651
|
+
for (const item of point.items) {
|
|
6652
|
+
const { action, rule, moduleId } = item;
|
|
6653
|
+
if (action.kind === "inject-text") {
|
|
6654
|
+
const config = {
|
|
6655
|
+
...action.compiledConfig,
|
|
6656
|
+
rule,
|
|
6657
|
+
sourceModuleId: moduleId,
|
|
6658
|
+
group: void 0,
|
|
6659
|
+
exclusive: false,
|
|
6660
|
+
enabled: true,
|
|
6661
|
+
actionWhen: action.actionWhen,
|
|
6662
|
+
bypassRuleWhen: action.bypassRuleWhen
|
|
6663
|
+
};
|
|
6664
|
+
if (config.layer === "pre-step") injections.get(moduleId).configs.push(config);
|
|
6665
|
+
else textConfigs.push(config);
|
|
6666
|
+
continue;
|
|
6667
|
+
}
|
|
6668
|
+
flushLayers();
|
|
6669
|
+
const on = (channel, handler) => {
|
|
6670
|
+
if (channel !== point.channel) throw new TypeError(`rule action registered outside ${point.channel}: ${channel}`);
|
|
6671
|
+
const entry = {
|
|
6672
|
+
rule,
|
|
6673
|
+
handler,
|
|
6674
|
+
sequence: action.channelOrder,
|
|
6675
|
+
sourceModuleId: moduleId,
|
|
6676
|
+
ruleId: rule.id,
|
|
6677
|
+
ruleActionIndex: action.actionIndex,
|
|
6678
|
+
id: action.id,
|
|
6679
|
+
actionWhen: action.actionWhen,
|
|
6680
|
+
bypassRuleWhen: action.bypassRuleWhen
|
|
6681
|
+
};
|
|
6682
|
+
if (channel === "agent/pre-step" && point.waterfallPosition === "default") injections.get(moduleId).ruleActions.push(entry);
|
|
6683
|
+
else handlers.push(entry);
|
|
6684
|
+
return () => {};
|
|
6685
|
+
};
|
|
6686
|
+
releases.push(bind(item, on));
|
|
6687
|
+
}
|
|
6688
|
+
flushLayers();
|
|
6689
|
+
if (!handlers.length) continue;
|
|
6690
|
+
releases.push(ctx.on(point.channel, (...args) => {
|
|
6691
|
+
const free = noNext.has(point.channel);
|
|
6692
|
+
const payload = free ? args : args.slice(0, -1);
|
|
6693
|
+
const next = free ? () => void 0 : args[args.length - 1];
|
|
6694
|
+
if (!active) return next();
|
|
6695
|
+
const frame = ruleFrame(point.channel, payload, warnOnce, ctx);
|
|
6696
|
+
if (free) {
|
|
6697
|
+
let pending;
|
|
6698
|
+
for (const entry of handlers) {
|
|
6699
|
+
const invoke = () => active && actionMatches(entry, frame) ? entry.handler(...payload, void 0, frame) : void 0;
|
|
6700
|
+
pending = pending?.then ? pending.then(invoke) : invoke();
|
|
6701
|
+
}
|
|
6702
|
+
return pending;
|
|
6703
|
+
}
|
|
6704
|
+
if (point.phase === "after-next") return Promise.resolve(next()).then(async (initial) => {
|
|
6705
|
+
let result = initial;
|
|
6706
|
+
for (const entry of handlers) {
|
|
6707
|
+
if (!active || !actionMatches(entry, frame)) continue;
|
|
6708
|
+
result = await entry.handler(...payload, () => result, frame);
|
|
6709
|
+
}
|
|
6710
|
+
return result;
|
|
6711
|
+
});
|
|
6712
|
+
const invoke = (index) => {
|
|
6713
|
+
if (!active || index === handlers.length) return next();
|
|
6714
|
+
const entry = handlers[index];
|
|
6715
|
+
return actionMatches(entry, frame) ? entry.handler(...payload, () => invoke(index + 1), frame) : invoke(index + 1);
|
|
6716
|
+
};
|
|
6717
|
+
return invoke(0);
|
|
6718
|
+
}, point.waterfallPosition === "outermost" ? { prepend: true } : void 0));
|
|
6719
|
+
}
|
|
6720
|
+
const preStep = [...injections.values()].filter((source) => source.configs.length || source.ruleActions.length);
|
|
6721
|
+
if (preStep.length) releases.push(applyPromptConfigSources(ctx, preStep, { layers: false }));
|
|
6722
|
+
} catch (error) {
|
|
6723
|
+
active = false;
|
|
6724
|
+
for (const dispose of releases.reverse()) try {
|
|
6725
|
+
dispose?.();
|
|
6726
|
+
} catch {}
|
|
6727
|
+
throw error;
|
|
6728
|
+
}
|
|
6729
|
+
return () => {
|
|
6730
|
+
active = false;
|
|
6731
|
+
for (const dispose of releases.splice(0).reverse()) try {
|
|
6732
|
+
dispose?.();
|
|
6733
|
+
} catch {}
|
|
6734
|
+
};
|
|
6735
|
+
}
|
|
6736
|
+
//#endregion
|
|
7026
6737
|
//#region src/runtime/agent-assembly.ts
|
|
7027
6738
|
/**
|
|
7028
6739
|
* agent-assembly — 运行时配装通道。
|
|
@@ -7035,8 +6746,7 @@ function createSkillsRuntime(ctx, options = {}) {
|
|
|
7035
6746
|
* 不再声明 `agent.cordis.yml` 这类「给宿主 Loader 用的组合本体」;
|
|
7036
6747
|
* - 官方工具行(`dsh-tool-*` 等)由宿主提供;模块人设通过 systemPrompt 注册,
|
|
7037
6748
|
* 本通道**不**装第二棵官方插件树——那些包也不在插件包的解析面内;
|
|
7038
|
-
* -
|
|
7039
|
-
* 缺失时回退 `module.yml` 内嵌,两条路径的切片同源(`resolveModuleFacts`)。
|
|
6749
|
+
* - 规则从校验发布的 rules/ 快照编译;工具与子代理策略从模块定义内联装配。
|
|
7040
6750
|
*
|
|
7041
6751
|
* 与官方挂载并存时不会重复:官方树里没有引擎行,引擎贡献只由本通道提供。
|
|
7042
6752
|
*/
|
|
@@ -7045,7 +6755,7 @@ const MANAGED_FIELDS = [
|
|
|
7045
6755
|
"configsDir",
|
|
7046
6756
|
"strategyDir",
|
|
7047
6757
|
"policyFile",
|
|
7048
|
-
"
|
|
6758
|
+
"rulesFile"
|
|
7049
6759
|
];
|
|
7050
6760
|
/** 私有工具适配器依赖的服务;适配器仍复用包内引擎入口。 */
|
|
7051
6761
|
const PRIVATE_SERVICES = {
|
|
@@ -7054,7 +6764,7 @@ const PRIVATE_SERVICES = {
|
|
|
7054
6764
|
"session-var-tools": "pt-session-var-tools"
|
|
7055
6765
|
};
|
|
7056
6766
|
/**
|
|
7057
|
-
* 受管字段的路径换算:`configsDir` / `strategyDir` / `policyFile` / `
|
|
6767
|
+
* 受管字段的路径换算:`configsDir` / `strategyDir` / `policyFile` / `rulesFile`
|
|
7058
6768
|
* 一律解析到**当前模块目录内的真实位置**——声明怎么写都按模块目录作基准,三种形态同结果:
|
|
7059
6769
|
* - `./configs`(新形态)→ `<模块目录>/configs`;
|
|
7060
6770
|
* - `../<id>/triggers.yml`(历史形态:`ENGINE_MANAGED_PATHS` 的改写成这样,语义是
|
|
@@ -7085,34 +6795,37 @@ function engineModuleFile(engineDir, id) {
|
|
|
7085
6795
|
* 纯读缝(只依赖包内引擎与模块目录),供挂载与回归测试共用;`hasService` 是宿主能力探针。
|
|
7086
6796
|
*/
|
|
7087
6797
|
async function prepareAssembly(moduleRoot, moduleId, hasService) {
|
|
7088
|
-
|
|
6798
|
+
assertModuleId(moduleId);
|
|
7089
6799
|
const moduleDir = resolveModuleDir(moduleId, moduleRoot);
|
|
7090
6800
|
const spec = loadModuleSpec(moduleDir);
|
|
6801
|
+
if (spec.customTools !== void 0 && !Array.isArray(spec.customTools)) throw new TypeError("customTools must be an array");
|
|
6802
|
+
const toolErrors = validateCustomTools(spec.customTools ?? []);
|
|
6803
|
+
if (toolErrors.length > 0) throw new Error(`invalid customTools: ${toolErrors.join("; ")}`);
|
|
7091
6804
|
const facts = resolveModuleFacts(spec, moduleDir);
|
|
7092
6805
|
if (facts.effectiveModules === null) throw new Error(`模块 ${moduleId} 的模块声明无效,无法配装`);
|
|
7093
6806
|
const configsByModule = facts.effectiveConfigs ?? {};
|
|
7094
|
-
const
|
|
7095
|
-
const
|
|
7096
|
-
|
|
7097
|
-
const configs = createPromptConfigs(specs, {
|
|
7098
|
-
sourceModuleId: moduleId,
|
|
6807
|
+
const ruleConfig = absolutizeManagedFields(configsByModule["rule-engine"] ?? {}, moduleDir);
|
|
6808
|
+
const rules = compileRules(spec.rules ?? [], {
|
|
6809
|
+
moduleId,
|
|
7099
6810
|
configOrder: readConfigOrder(spec.configOrder),
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
});
|
|
7104
|
-
const exclusiveConfigs = configs.filter((config) => {
|
|
7105
|
-
if (config === null || typeof config !== "object") return false;
|
|
7106
|
-
const entry = config;
|
|
7107
|
-
return entry.enabled !== false && entry.params?.complete === true;
|
|
6811
|
+
variables: spec.variables,
|
|
6812
|
+
variablesEnabled: spec.variablesEnabled,
|
|
6813
|
+
promptConfigOptions: rulePromptConfigOptions(moduleDir, ruleConfig.strategyDir)
|
|
7108
6814
|
});
|
|
6815
|
+
const ruleConditions = new Map((spec.rules ?? []).map((rule) => [rule.id, structuredClone(rule.if)]));
|
|
6816
|
+
const exclusiveConfigs = (spec.rules ?? []).filter((rule) => rule.enabled !== false).flatMap((rule) => rule.then.filter((action) => {
|
|
6817
|
+
const config = action.config;
|
|
6818
|
+
return action.kind === "inject-text" && config?.params?.complete === true;
|
|
6819
|
+
}));
|
|
7109
6820
|
const personaComplete = spec.persona?.complete === true;
|
|
7110
6821
|
if (exclusiveConfigs.length > 1 || personaComplete && exclusiveConfigs.length > 0) throw new Error(`模块 ${moduleId} 有多个生效的「独占」段(顶层人设${personaComplete ? "已" : "未"}开启),同一模块只能有一个:请先关闭其一`);
|
|
7111
6822
|
const engineDir = packageEngineDir();
|
|
7112
6823
|
const services = /* @__PURE__ */ new Set();
|
|
7113
6824
|
const modules = [];
|
|
7114
6825
|
for (const id of /* @__PURE__ */ new Set([...facts.effectiveModules, ...facts.rowIds])) {
|
|
7115
|
-
if (id === "
|
|
6826
|
+
if (id === "rule-engine") continue;
|
|
6827
|
+
if (id === "subagent-tool-policy" && (spec.subagentToolPolicy === void 0 || spec.subagentToolPolicy === null)) continue;
|
|
6828
|
+
if (id === "prompt-config-engine" || id === "declared-triggers") throw new Error(`模块 ${moduleId} 仍声明旧规则引擎 ${id},请先离线迁移`);
|
|
7116
6829
|
const privateService = PRIVATE_SERVICES[id];
|
|
7117
6830
|
if (privateService !== void 0) {
|
|
7118
6831
|
if (!hasService(privateService)) throw new Error(`配装所需能力不可用:${privateService}`);
|
|
@@ -7124,7 +6837,12 @@ async function prepareAssembly(moduleRoot, moduleId, hasService) {
|
|
|
7124
6837
|
if (typeof module.apply !== "function") throw new Error(`引擎能力缺少 apply:${id}`);
|
|
7125
6838
|
for (const dependency of module.inject ?? []) services.add(dependency);
|
|
7126
6839
|
const config = absolutizeManagedFields(configsByModule[id] ?? {}, moduleDir);
|
|
7127
|
-
if (id === "tool-config-engine"
|
|
6840
|
+
if (id === "tool-config-engine") {
|
|
6841
|
+
config.tools = (spec.customTools ?? []).map((tool) => compileCustomTool(tool));
|
|
6842
|
+
config.resourceRoot = pathToFileURL(moduleRoot + sep).href;
|
|
6843
|
+
delete config.presetRoot;
|
|
6844
|
+
}
|
|
6845
|
+
if (id === "subagent-tool-policy") config.policy = structuredClone(spec.subagentToolPolicy);
|
|
7128
6846
|
module.configContract?.parse?.(config, id);
|
|
7129
6847
|
modules.push({
|
|
7130
6848
|
id,
|
|
@@ -7132,7 +6850,7 @@ async function prepareAssembly(moduleRoot, moduleId, hasService) {
|
|
|
7132
6850
|
config
|
|
7133
6851
|
});
|
|
7134
6852
|
}
|
|
7135
|
-
if (
|
|
6853
|
+
if (rules.length > 0) {
|
|
7136
6854
|
services.add("systemPrompt");
|
|
7137
6855
|
services.add("tools");
|
|
7138
6856
|
services.add("llm");
|
|
@@ -7140,7 +6858,8 @@ async function prepareAssembly(moduleRoot, moduleId, hasService) {
|
|
|
7140
6858
|
if (spec.persona !== void 0) services.add("systemPrompt");
|
|
7141
6859
|
for (const name of services) if (!hasService(name)) throw new Error(`配装所需宿主能力不可用:${name}`);
|
|
7142
6860
|
return {
|
|
7143
|
-
|
|
6861
|
+
rules,
|
|
6862
|
+
ruleConditions,
|
|
7144
6863
|
modules,
|
|
7145
6864
|
services,
|
|
7146
6865
|
moduleId,
|
|
@@ -7180,6 +6899,20 @@ function createAgentAssembly(ctx, options) {
|
|
|
7180
6899
|
name: "prompt-tool-assembly",
|
|
7181
6900
|
inject: [...services],
|
|
7182
6901
|
apply: async (scopeCtx) => {
|
|
6902
|
+
const standingMountFor = await loadStandingMountFor();
|
|
6903
|
+
const ruleSources = prepared.filter((item) => item.rules.length > 0).map((item) => ({
|
|
6904
|
+
moduleId: item.moduleId,
|
|
6905
|
+
rules: item.rules.map((value) => {
|
|
6906
|
+
const rule = value;
|
|
6907
|
+
return {
|
|
6908
|
+
...rule,
|
|
6909
|
+
when: compileWhen(item.ruleConditions.get(rule.id), {
|
|
6910
|
+
ctx: scopeCtx,
|
|
6911
|
+
standingMountFor
|
|
6912
|
+
})
|
|
6913
|
+
};
|
|
6914
|
+
})
|
|
6915
|
+
}));
|
|
7183
6916
|
for (const item of prepared) if (item.persona !== void 0) {
|
|
7184
6917
|
const persona = item.persona;
|
|
7185
6918
|
scopeCtx.systemPrompt.section({
|
|
@@ -7195,10 +6928,7 @@ function createAgentAssembly(ctx, options) {
|
|
|
7195
6928
|
});
|
|
7196
6929
|
if (persona.includeRuntimeContext === false) scopeCtx.systemPrompt.suppressRuntimeContext();
|
|
7197
6930
|
}
|
|
7198
|
-
scopeCtx.effect(() =>
|
|
7199
|
-
sourceId: `module:${item.moduleId}`,
|
|
7200
|
-
configs: item.configs
|
|
7201
|
-
})), { prepend: true }));
|
|
6931
|
+
scopeCtx.effect(() => mountRuleSources(scopeCtx, ruleSources, { prepend: true }));
|
|
7202
6932
|
for (const item of prepared) for (const module of item.modules) {
|
|
7203
6933
|
const result = await module.apply(scopeCtx, module.config);
|
|
7204
6934
|
if (typeof result === "function") scopeCtx.effect(() => result, module.id);
|
|
@@ -7300,22 +7030,25 @@ function warn(ctx, message) {
|
|
|
7300
7030
|
} catch {}
|
|
7301
7031
|
}
|
|
7302
7032
|
function apply(ctx, configIn) {
|
|
7303
|
-
const
|
|
7304
|
-
const readConfig = () => ({
|
|
7033
|
+
const seededModules = new Set(ensureModuleSeed(MODULES_DIR).created);
|
|
7034
|
+
const readConfig = () => ({ modulesEnabled: readModulesEnabled({
|
|
7035
|
+
modulesEnabled: configIn.modulesEnabled?.get(),
|
|
7036
|
+
writePreset: configIn.writePreset?.get()
|
|
7037
|
+
}) });
|
|
7305
7038
|
const config = readConfig();
|
|
7306
7039
|
/** 运行时配装通道的运行态:工具写入目标按它报告的「本 Agent 装了哪几层提示词」解析。 */
|
|
7307
7040
|
let assembly;
|
|
7308
7041
|
const modelsState = () => detectModels(ctx);
|
|
7309
7042
|
const getModelsState = () => modelsState();
|
|
7310
7043
|
const runtime = { ...config };
|
|
7311
|
-
const
|
|
7312
|
-
|
|
7313
|
-
|
|
7044
|
+
const refreshModuleSlices = (id) => {
|
|
7045
|
+
ensureModuleReady(id, {
|
|
7046
|
+
modulesRoot: MODULES_DIR,
|
|
7314
7047
|
warn: (message) => warn(ctx, message)
|
|
7315
7048
|
});
|
|
7316
7049
|
};
|
|
7317
|
-
const
|
|
7318
|
-
|
|
7050
|
+
const refreshModuleRuntime = async (id = basename(activeModuleDir())) => {
|
|
7051
|
+
refreshModuleSlices(id);
|
|
7319
7052
|
await assembly?.refresh(id);
|
|
7320
7053
|
};
|
|
7321
7054
|
/** 无请求目标时供TUI使用的模块;编辑器的显式ID始终单独解析。 */
|
|
@@ -7333,10 +7066,10 @@ function apply(ctx, configIn) {
|
|
|
7333
7066
|
readSkillContent: skillsRuntime.readContent,
|
|
7334
7067
|
writeSkillContent: skillsRuntime.writeContent,
|
|
7335
7068
|
patchSkillFolders: skillsRuntime.setFolders
|
|
7336
|
-
}), () => "", skillsRuntime.invalidate, (moduleId) => moduleId === void 0 ? activeModuleDir() : resolveEditDir(MODULES_DIR, moduleId), (_scopes, id) =>
|
|
7069
|
+
}), () => "", skillsRuntime.invalidate, (moduleId) => moduleId === void 0 ? activeModuleDir() : resolveEditDir(MODULES_DIR, moduleId), (_scopes, id) => refreshModuleRuntime(id), (id) => id === void 0 ? assembly?.refresh() : refreshModuleRuntime(id), async (id) => {
|
|
7337
7070
|
skillsRuntime.invalidate();
|
|
7338
7071
|
await assembly?.refresh(id);
|
|
7339
|
-
}, (id) =>
|
|
7072
|
+
}, (id) => refreshModuleRuntime(id), () => assembly?.refresh());
|
|
7340
7073
|
scheduleWebSurfaceRepair(ctx, (message) => warn(ctx, message));
|
|
7341
7074
|
ctx.effect(() => ctx.on("llm/adapters-updated", () => invalidateModelCatalog(ctx)));
|
|
7342
7075
|
const currentSource = () => ({
|
|
@@ -7348,9 +7081,8 @@ function apply(ctx, configIn) {
|
|
|
7348
7081
|
skillCatalog: skillsRuntime.listSkills(),
|
|
7349
7082
|
activeSkillsDirs: [skillsRuntime.skillsRoot]
|
|
7350
7083
|
}), getModelsState, () => listAdvertisedModels(ctx), () => activeModuleDir(), async (key, value) => {
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
await rebuildPreset();
|
|
7084
|
+
saveModuleParams(MODULES_DIR, basename(activeModuleDir()), { [key]: value }, void 0);
|
|
7085
|
+
await refreshModuleRuntime();
|
|
7354
7086
|
}, (name, enabled) => {
|
|
7355
7087
|
const matches = skillsRuntime.listSkills().filter((skill) => skill.name === name);
|
|
7356
7088
|
if (matches.length !== 1) return {
|
|
@@ -7367,34 +7099,41 @@ function apply(ctx, configIn) {
|
|
|
7367
7099
|
ok: false,
|
|
7368
7100
|
message: result.message
|
|
7369
7101
|
};
|
|
7370
|
-
});
|
|
7102
|
+
}, (id) => refreshModuleRuntime(id));
|
|
7371
7103
|
let needsInitialApply = true;
|
|
7372
7104
|
const applyState = async () => {
|
|
7373
|
-
const
|
|
7374
|
-
const
|
|
7375
|
-
|
|
7376
|
-
if (!needsInitialApply && !writePresetChanged) return;
|
|
7105
|
+
const nextRuntime = { modulesEnabled: currentSource().modulesEnabled };
|
|
7106
|
+
const modulesEnabledChanged = runtime.modulesEnabled !== nextRuntime.modulesEnabled;
|
|
7107
|
+
if (!needsInitialApply && !modulesEnabledChanged) return;
|
|
7377
7108
|
const initial = needsInitialApply;
|
|
7378
7109
|
needsInitialApply = false;
|
|
7379
7110
|
Object.assign(runtime, nextRuntime);
|
|
7380
7111
|
if (initial) {
|
|
7381
|
-
for (const id of
|
|
7382
|
-
|
|
7112
|
+
for (const id of seededModules) refreshModuleSlices(id);
|
|
7113
|
+
seededModules.clear();
|
|
7383
7114
|
}
|
|
7384
7115
|
await assembly?.refresh();
|
|
7385
7116
|
};
|
|
7386
7117
|
const moduleToolHost = {
|
|
7387
7118
|
target: (exec) => resolveModuleToolTarget(exec, MODULES_DIR, (sessionId) => assembly?.moduleIds(sessionId) ?? []),
|
|
7388
|
-
rebuild: (id) =>
|
|
7119
|
+
rebuild: (id) => refreshModuleRuntime(id)
|
|
7389
7120
|
};
|
|
7390
7121
|
ctx.provide("pt-character-tools", { mount: (scopeCtx) => registerCharacterTools(scopeCtx, moduleToolHost) });
|
|
7391
7122
|
ctx.provide("pt-world-book-tools", { mount: (scopeCtx) => registerWorldBookTools(scopeCtx, moduleToolHost) });
|
|
7392
7123
|
ctx.provide("pt-session-var-tools", { mount: (scopeCtx) => registerSessionVarTools(scopeCtx) });
|
|
7124
|
+
import(new URL("../engine/skill-search.mjs", import.meta.url).href).then((mod) => {
|
|
7125
|
+
if (typeof mod.apply !== "function") return;
|
|
7126
|
+
ctx.inject(mod.inject ?? ["tools"], (skillCtx) => {
|
|
7127
|
+
mod.apply?.(skillCtx);
|
|
7128
|
+
});
|
|
7129
|
+
}).catch((error) => {
|
|
7130
|
+
warn(ctx, `prompt-tool: skill-search 未挂载(skill_search/skill_load 不可用):${error instanceof Error ? error.message : String(error)}`);
|
|
7131
|
+
});
|
|
7393
7132
|
installPreStepCoordinator(ctx);
|
|
7394
7133
|
ctx.inject(["agents"], (actx) => {
|
|
7395
7134
|
const mounted = createAgentAssembly(actx, {
|
|
7396
7135
|
moduleRoot: MODULES_DIR,
|
|
7397
|
-
enabledModules: () => runtime.
|
|
7136
|
+
enabledModules: () => runtime.modulesEnabled ? enabledModuleIds(MODULES_DIR).filter((id) => moduleDirExists(MODULES_DIR, id)) : [],
|
|
7398
7137
|
warn: (message) => warn(ctx, message)
|
|
7399
7138
|
});
|
|
7400
7139
|
assembly = mounted;
|
|
@@ -7418,4 +7157,4 @@ function apply(ctx, configIn) {
|
|
|
7418
7157
|
});
|
|
7419
7158
|
}
|
|
7420
7159
|
//#endregion
|
|
7421
|
-
export { BRIDGE_ENDPOINTS,
|
|
7160
|
+
export { BRIDGE_ENDPOINTS, Config, DEFAULT_MODULE_ID, MODULE_CAPABILITIES as ENGINE_CAPABILITIES, MODULE_CAPABILITIES, ENGINE_PARAM_KEYS, ENGINE_RECIPES, INJECTION_POINT_ORDER, INSTRUCTION_FILE_STATUSES, MAX_BRIDGE_BODY_BYTES, MAX_CHARACTER_CARD_STREAM_BYTES, MAX_INSTRUCTION_FILE_BYTES, ModuleLayerSettingsError, PARAM_KEYS, PRE_STEP_COORDINATOR_SERVICE, PRE_STEP_COORDINATOR_VERSION, SETTINGS_BRIDGE_PREFIX, SKILL_NAME_PATTERN, USER_SKILLS_DIR, WRITER_PARAM_KEYS, agentsFileCardSpecs, agentsFileId, appendModuleCapabilities, appendModuleMemory, apply, applyModuleConfigs, assertModuleDirectory, assertModuleId, assertModuleTree, assertSafeConfigId, buildModuleConfigsFromParams, buildWorldBookEntry, canonicalModulesRoot, catalogFromScan, cloneBuiltinModule, cloneBuiltinModule as cloneBuiltinPreset, configFileName, convertStToModule, convertStToModule as convertStToPreset, convertStToModuleWithReport, createModuleCapability as createEngineCapabilityInPreset, createModuleCapability, createSkillsReloader, createSkillsRuntime, deleteWorldBookEntry, detectAgentsFileSnapshots, detectAgentsFiles, detectModels, moduleCapability as engineCapability, moduleCapability, engineRecipe, ensureModuleReady, ensureModuleSeed, ensureModuleSeed as ensurePresetSeed, ensureWebSurface, expandModuleSource, expandModuleSource as expandPresetSource, exportModulePackage, exportModulePackage as exportPresetPackage, importCharacterCard, importCharacterCardFile, importSkillsPackage, inject, installModulePackage, installModulePackage as installPresetPackage, installPreStepCoordinator, invalidateModelCatalog, invocationForScope, isModuleCapabilityPresent as isEngineCapabilityPresent, isModuleCapabilityPresent, listAdvertisedModels, listBuiltinTemplates, listModules, listWorldBookEntries, loadModuleSpec, loadPromptTemplates, loadToolTemplates, mergeInstructionCards, mergeModuleIntoModule, mergeStModules, mergeStModules as mergeStPresets, mergeStModulesWithReport, moduleDirExists, moduleImportPreview, moduleImportPreview as presetImportPreview, modulePathExists, name, parseFrontmatter, peekModelCatalog, processStText, readAgentsFileSnapshot, readModuleLayerSettings, readModuleMemory, readSkillInvocation, readSkillsState, registerCharacterTools, registerSettingsBridge, registerTuiCommand, registerWorldBookTools, removeModuleCapability as removeEngineCapabilityFromPreset, removeModuleCapability, removeMergedModule, removeModuleCapabilityDeclaration, removeModuleCapabilityDeclaration as removePresetModule, removeUserModule, removeUserModule as removeUserPreset, renderComposition, resolveModuleDir, resolveModuleFacts, resolveModuleParams, resolveModuleParams as resolvePresetParams, resolveProfileDir, resolveProjectRoot, resolveSubagentStartOptions, saveModuleParams, saveModulePersona, saveModulePersona as savePresetPersona, scanRoot, scanRoots, scheduleWebSurfaceRepair, scopeOfInvocation, setSkillInvocation, skillRoots, skillsStatePath, stModuleId, stModuleId as stPresetId, upsertWorldBookEntry, userModulesDir, validateCustomToolIdentities, validateEngineParamValues, withModuleDefinition, writeAgentsFile, writeAgentsFileChecked, writeModule, writePreset, writeSkillsState };
|