@shgroup/dsh-serenity-hooks 1.77.0 → 1.78.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/dsh.plugin.json +1 -1
- package/lib/{ccc-roots-rwgzt_0F.js → ccc-roots-D3ty1C8P.js} +1 -1
- package/lib/index.js +588 -2607
- package/lib/{message-source-CYa8LbbM.js → message-source-BOG0piL8.js} +240 -4
- package/lib/{profile-DplLGGqo.js → profile-C7BuyZK7.js} +63 -5
- package/lib/{route-COOFLTpU.js → route-Dr0SFD8A.js} +2 -279
- package/lib/service-DDmHSglV.js +282 -0
- package/lib/session-BxDVrxPH.js +2496 -0
- package/lib/settings-Bb4OnEdI.js +145 -0
- package/lib/tools/trajectory-constraint-admin.d.ts +8 -1
- package/lib/tools/trajectory-self-docs.d.ts +35 -0
- package/lib/trajectory/core.d.ts +4 -1
- package/lib/trajectory/model-runtime.d.ts +97 -0
- package/lib/trajectory/policy/profile.d.ts +50 -5
- package/lib/trajectory/policy/rules.d.ts +17 -4
- package/lib/trajectory/resident.d.ts +57 -0
- package/package.json +1 -1
- package/lib/settings-f9WZXcgi.js +0 -368
package/lib/settings-f9WZXcgi.js
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
import { f as hostSettings } from "./ccc-roots-rwgzt_0F.js";
|
|
2
|
-
import { _ as resolveInside, g as readUtf8 } from "./ccc-CyYJL4F2.js";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
//#region src/kernel/constants.ts
|
|
7
|
-
/** 常量(纯模块,零 DSH 依赖) */
|
|
8
|
-
/** 本包名 —— 上溯定位包根时的**身份核对**(同名目录家的邻居不会误命中) */
|
|
9
|
-
const PACKAGE_NAME = "@shgroup/dsh-serenity-hooks";
|
|
10
|
-
/** 上溯层数上限(源码态最深 `src/<域>/<子域>/` / 发布态 `lib/` ⇒ 8 层足够,且**保证有界**) */
|
|
11
|
-
const MAX_ROOT_ASCENT = 8;
|
|
12
|
-
/**
|
|
13
|
-
* 包根 = 含**本包** `package.json` 的目录。
|
|
14
|
-
*
|
|
15
|
-
* 🔴 为什么不写 `join(here, '..')`:本模块自身的位置**随目录重构变化**
|
|
16
|
-
* (曾 `src/`,现 `src/kernel/`),而发布态是从打包入口 `lib/index.js` 反推 ——
|
|
17
|
-
* 两种布局的深度不同,写死层数**必然在其中一个布局下静默错位**。
|
|
18
|
-
* 实测代价:`src/` → `src/kernel/` 之后,`join(here,'..')` 指到 `src/`(无 package.json)
|
|
19
|
-
* ⇒ `ACC_VERSION` 落进 catch 分支变 `0.0.0`,**没有任何门禁报警**。
|
|
20
|
-
*
|
|
21
|
-
* ⇒ 判据改成**内容**(找带本包名的 package.json)而不是**层数**。
|
|
22
|
-
*
|
|
23
|
-
* @param start 起点目录(缺省 = 本模块所在目录;测试可注入)
|
|
24
|
-
*/
|
|
25
|
-
function findPackageRoot(start) {
|
|
26
|
-
let dir = start ?? dirname(fileURLToPath(import.meta.url));
|
|
27
|
-
for (let i = 0; i < MAX_ROOT_ASCENT; i += 1) {
|
|
28
|
-
try {
|
|
29
|
-
if (JSON.parse(readFileSync(join(dir, "package.json"), "utf-8")).name === PACKAGE_NAME) return dir;
|
|
30
|
-
} catch {}
|
|
31
|
-
const parent = dirname(dir);
|
|
32
|
-
if (parent === dir) return null;
|
|
33
|
-
dir = parent;
|
|
34
|
-
}
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* ACC 版本:自动从 package.json 读取(单一真相源,消除与 CHANGELOG 的漂移)。
|
|
39
|
-
* 发布时只需改 package.json 的 version。
|
|
40
|
-
*/
|
|
41
|
-
const ACC_VERSION = (() => {
|
|
42
|
-
try {
|
|
43
|
-
const root = findPackageRoot();
|
|
44
|
-
if (root === null) return "0.0.0";
|
|
45
|
-
return JSON.parse(readFileSync(join(root, "package.json"), "utf-8")).version ?? "0.0.0";
|
|
46
|
-
} catch {
|
|
47
|
-
return "0.0.0";
|
|
48
|
-
}
|
|
49
|
-
})();
|
|
50
|
-
//#endregion
|
|
51
|
-
//#region src/trajectory/policy/rules.ts
|
|
52
|
-
/**
|
|
53
|
-
* trajectory/policy/rules.ts — 轨迹约束面的**纯逻辑助手**(零 DSH 依赖,可独立单测)
|
|
54
|
-
*
|
|
55
|
-
* ## 2026-10-19 重做后的职责边界(R↓,别再混起来)
|
|
56
|
-
*
|
|
57
|
-
* | 模块 | 管什么 |
|
|
58
|
-
* |---|---|
|
|
59
|
-
* | `./profile.ts` | **判据 + 读盘**("这是不是 轨迹约束"、"约束面是什么") |
|
|
60
|
-
* | `./rules.ts`(本文件) | **从约束面派生出的纯函数**(白名单集合、提示词读取器、基础提示词文本) |
|
|
61
|
-
* | `../session-create.ts` | 会话创建/复用、提问、守门查询 |
|
|
62
|
-
*
|
|
63
|
-
* ⇒ 本文件**不再读 `serenity.json`**:`轨迹约束.roles` 整段已退役(owner 2026-10-19 重做)。
|
|
64
|
-
* 它只接收一个已解析好的 `TrajectoryConstraint`,因此**不涉及任何 IO**(除提示词文件,那是刻意懒读的)。
|
|
65
|
-
*
|
|
66
|
-
* ## S12(2026-10-19,轨迹中心重构)
|
|
67
|
-
*
|
|
68
|
-
* 原名 `role/轨迹约束/config.ts`。改名理由(框架 §6.3「角色不是一个概念,是一段历史」):
|
|
69
|
-
* 它派生的是**某条轨迹的约束面**,与"配置读取"无关(本文件零配置读取,见上一段)
|
|
70
|
-
* ⇒ `config` 这个名字描述的是**它曾经的样子**,`rules` 描述它**现在做的事**。
|
|
71
|
-
*/
|
|
72
|
-
/** 白名单推导 ────────────────────────────────────────────────────────────── */
|
|
73
|
-
/** 提示词来源提示串(`apply` 回执与面板共用;避免两处措辞漂) */
|
|
74
|
-
const TRAJECTORY_CONSTRAINT_HINT = "systemPromptFile hot-reloads by mtime; the rest of trajectory-constraint.json is re-read every turn";
|
|
75
|
-
/**
|
|
76
|
-
* 该 轨迹约束会话**可见**的工具面(tools ∪ `msm`)。
|
|
77
|
-
*
|
|
78
|
-
* 🔴 `msm` 为什么自动加入:`msms` 非空意味着这个 轨迹约束 **有 MSM 通道**,
|
|
79
|
-
* 而 MSM 的唯一合法调用入口是 `msm` 工具(单入口纪律)——不给它 `msm` 就等于
|
|
80
|
-
* 声明了一堆调不动的能力。⚠️ 这是**并集**,不是覆盖:`tools` 里显式写的照旧生效。
|
|
81
|
-
*/
|
|
82
|
-
function profileToolWhitelist(profile) {
|
|
83
|
-
const out = /* @__PURE__ */ new Set();
|
|
84
|
-
for (const t of profile?.tools ?? []) out.add(t);
|
|
85
|
-
if ((profile?.msms?.length ?? 0) > 0) out.add("msm");
|
|
86
|
-
return out;
|
|
87
|
-
}
|
|
88
|
-
/** 该 轨迹约束会话可 exec 的 MSM 白名单(`msm` 工具内部再过滤;独立于工具白名单) */
|
|
89
|
-
function profileMsmWhitelist(profile) {
|
|
90
|
-
return new Set(profile?.msms ?? []);
|
|
91
|
-
}
|
|
92
|
-
/** 轨迹纪律参与项(`profile` 缺省已在 `readTrajectoryConstraintAt` 归一为全 true) */
|
|
93
|
-
function constraintCognitiveTrajectory(profile) {
|
|
94
|
-
return {
|
|
95
|
-
session: profile?.cognitiveTrajectory.session === true,
|
|
96
|
-
keeper: profile?.cognitiveTrajectory.keeper === true,
|
|
97
|
-
rebuild: profile?.cognitiveTrajectory.rebuild === true
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* 解析约束面声明的**系统提示词全文**(一次性读取;`create` / `validate` / `list` 用)。
|
|
102
|
-
*
|
|
103
|
-
* 顺序:`systemPromptFile`(**推荐**:超长提示词在 JSON 里内嵌不可读)→ 内嵌 `systemPrompt` → 空串。
|
|
104
|
-
* 文件缺失/逃逸 ⇒ **抛错**(调用方 catch 降级;`validate` 据此报 issue)。
|
|
105
|
-
*
|
|
106
|
-
* ⚠️ 热路径**不用**它(那会用每次工具调用去读盘)——热路径用 `createProfilePromptReader`。
|
|
107
|
-
*/
|
|
108
|
-
function resolveConstraintSystemPrompt(root, profile) {
|
|
109
|
-
if (!profile) return "";
|
|
110
|
-
const file = profile.systemPromptFile?.trim();
|
|
111
|
-
if (file) {
|
|
112
|
-
const abs = resolveInside(root, file);
|
|
113
|
-
if (!existsSync(abs)) throw new Error(`轨迹约束 "${profile.code}": systemPromptFile "${file}" not found (resolved: ${abs})`);
|
|
114
|
-
return readUtf8(abs).trim();
|
|
115
|
-
}
|
|
116
|
-
return profile.systemPrompt ?? "";
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* 提示词来源(`list`/`validate` 展示用)。
|
|
120
|
-
*
|
|
121
|
-
* 🔴 **2026-10-19 修(T4 的漏网面)**:v1.72.0 起提示词有两代来源——**新形态**
|
|
122
|
-
* 是轨迹目录里的 `PROMPT.md`(`trajectory/prompt.ts`,**优先**),旧形态才是
|
|
123
|
-
* `systemPromptFile` / `systemPrompt`。本函数原先只认旧形态 ⇒ 两条只写了 `PROMPT.md`
|
|
124
|
-
* 的轨迹在 `container_admin config list` 里恒显示 `promptSource: 'none'`。
|
|
125
|
-
*
|
|
126
|
-
* 那是**读数器过时**,不是配置缺失:`validate` 侧早已按两条来源判(见
|
|
127
|
-
* `trajectory-constraint-admin.ts` 的 `hasTrajectoryPromptOrLegacy`),而 `list` 侧漏了。
|
|
128
|
-
* ⇒ 返 **四种**值,把"有章程但来源是新形态"这一格单独说出来。
|
|
129
|
-
*
|
|
130
|
-
* @param hasPromptFile 该轨迹目录下**是否真有** `PROMPT.md`(调用方查,本函数不做 IO)
|
|
131
|
-
*/
|
|
132
|
-
function constraintSystemPromptSource(profile, hasPromptFile = false) {
|
|
133
|
-
if (hasPromptFile) return "trajectory-file";
|
|
134
|
-
if (profile?.systemPromptFile?.trim()) return "file";
|
|
135
|
-
if (profile?.systemPrompt?.trim()) return "inline";
|
|
136
|
-
return "none";
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* 提示词**读取器**(长期热更新;改文件即生效,无需重启)。
|
|
140
|
-
*
|
|
141
|
-
* 为什么返回函数而不是字符串(R↓,实证代价):旧实现在创建时把提示词**快照**进系统提示词段闭包
|
|
142
|
-
* ⇒ CCC 改了提示词文件必须重启 dsh web 才生效(实测:改完 18KB 提示词,live agent 仍用旧文本)。
|
|
143
|
-
* 现形态由系统提示词段的 `text()` **每轮调用**本读取器:
|
|
144
|
-
* - `systemPromptFile`:按 **mtime + size** 缓存重读(命中缓存零 IO;未变不解析)
|
|
145
|
-
* - 内嵌 `systemPrompt`:静态(改 JSON 仍需重载配置,这与文件热更的差别是**必然**的 —— 文件有 mtime,JSON 没有)
|
|
146
|
-
* - 读取失败:返回**上次成功内容**(⛔ 绝不返回空——空提示词会让角色失去人格与纪律),并按标签去重告警一次
|
|
147
|
-
*
|
|
148
|
-
* @param root CCC 根
|
|
149
|
-
* @param profile 约束面
|
|
150
|
-
*/
|
|
151
|
-
function createProfilePromptReader(root, profile) {
|
|
152
|
-
const file = profile?.systemPromptFile?.trim();
|
|
153
|
-
if (!file) {
|
|
154
|
-
const inline = profile?.systemPrompt ?? "";
|
|
155
|
-
return () => inline;
|
|
156
|
-
}
|
|
157
|
-
let abs = null;
|
|
158
|
-
try {
|
|
159
|
-
abs = resolveInside(root, file);
|
|
160
|
-
} catch (err) {
|
|
161
|
-
console.warn(`[serenity-hooks] ✗ 轨迹约束提示词路径非法(${file}): ${String(err?.message ?? err)}`);
|
|
162
|
-
}
|
|
163
|
-
if (abs === null) return () => "";
|
|
164
|
-
const absPath = abs;
|
|
165
|
-
let cachedMtime = -1;
|
|
166
|
-
let cachedSize = -1;
|
|
167
|
-
let cachedText = "";
|
|
168
|
-
let warned = false;
|
|
169
|
-
return () => {
|
|
170
|
-
try {
|
|
171
|
-
const st = statSync(absPath);
|
|
172
|
-
if (st.mtimeMs === cachedMtime && st.size === cachedSize) return cachedText;
|
|
173
|
-
const text = readUtf8(absPath).trim();
|
|
174
|
-
const isReload = cachedMtime !== -1;
|
|
175
|
-
cachedMtime = st.mtimeMs;
|
|
176
|
-
cachedSize = st.size;
|
|
177
|
-
cachedText = text;
|
|
178
|
-
warned = false;
|
|
179
|
-
if (isReload) console.info(`[serenity-hooks] ↻ 轨迹约束提示词已热重载(${file},${text.length} 字符)`);
|
|
180
|
-
return text;
|
|
181
|
-
} catch (err) {
|
|
182
|
-
if (!warned) {
|
|
183
|
-
warned = true;
|
|
184
|
-
console.warn(`[serenity-hooks] ✗ 轨迹约束提示词读取失败(${file})——沿用上次成功内容: ${String(err?.message ?? err)}`);
|
|
185
|
-
}
|
|
186
|
-
return cachedText;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* 轨迹约束 基础提示词(dsp 只给这部分;CCC 的 `systemPrompt` 段由调用方拼接)。
|
|
192
|
-
*
|
|
193
|
-
* ## 🔴 为什么**不再枚举**清单(owner 2026-10-19 令)
|
|
194
|
-
*
|
|
195
|
-
* 旧版在这里抄了两份清单(`MSMs: a, b, c` / `Tools: read, grep, …`)。那是**第二真相源**,
|
|
196
|
-
* 而真相源本来就在模型自己手里:
|
|
197
|
-
* · **工具清单** = 这一轮实际注册给它的 tool schema —— 模型已经看得见,抄一遍只会漂
|
|
198
|
-
* · **MSM 清单** = `msm()` 无参调用返回的目录 —— 已按白名单过滤,且**每次实时读配置**
|
|
199
|
-
*
|
|
200
|
-
* ⚠️ **漂移不是理论风险,是已发生的缺陷**(2026-10-19):角色配置早就长到 8 个工具,
|
|
201
|
-
* 而这个函数是从**创建时冻结的快照**求值的 ⇒ 提示词里一直写着旧的 4 个
|
|
202
|
-
* ⇒ 模型照着提示词办事,**它自己以为没有的能力就不去调** ⇒ 症状是"工具用不了",
|
|
203
|
-
* 而守卫那边(每调用实时读配置)其实是放行的。两个真相源打架,输的总是提示词。
|
|
204
|
-
*
|
|
205
|
-
* ⇒ 现在只声明**边界与发现路径**,不抄内容:清单在哪,怎么取,越界会怎样。
|
|
206
|
-
*
|
|
207
|
-
* @param code 轨迹编码(轨迹约束的身份 —— 不是"角色名",那个概念已退役)
|
|
208
|
-
*/
|
|
209
|
-
function buildTrajectoryBasePrompt(code) {
|
|
210
|
-
return [
|
|
211
|
-
"=== Serenity 轨迹约束 ===",
|
|
212
|
-
`Cognitive trajectory: ${code} (this is the trajectory you ARE, not a role you play)`,
|
|
213
|
-
"Your capability boundary is enforced mechanically by this CCC — anything outside it is rejected at the call site, not by your judgment.",
|
|
214
|
-
"Discover your surface, do not memorize it (both are generated from the live config on every turn):",
|
|
215
|
-
" · Your tools are exactly the tool schemas you see in this session — if a tool is not in your schema, it is not available.",
|
|
216
|
-
" · Your MSMs are listed by calling msm() with no arguments — that directory is already filtered to this trajectory.",
|
|
217
|
-
" · Usage for any MSM: msm(\"<name>\", [], inspect=true) or pass \"--help\" as the first argument.",
|
|
218
|
-
"Do not infer unavailability from anything you remember or from an earlier list: this surface changes when the CCC configuration changes, and it takes effect without restarting.",
|
|
219
|
-
"",
|
|
220
|
-
"---",
|
|
221
|
-
""
|
|
222
|
-
].join("\n");
|
|
223
|
-
}
|
|
224
|
-
//#endregion
|
|
225
|
-
//#region src/surface/settings.ts
|
|
226
|
-
/**
|
|
227
|
-
* 设置面板命名空间 = **profile 条目 id**(v0.1.7 起;与旧 `settings.yaml` 段名逐字相同)。
|
|
228
|
-
*/
|
|
229
|
-
const SERENITY_SETTINGS_NS = "serenity-hooks";
|
|
230
|
-
/** 进程级默认(无 Config 可读时的兜底;与 Config 各字段的缺省一致) */
|
|
231
|
-
function defaultSimpleSettings() {
|
|
232
|
-
return {
|
|
233
|
-
gatewayEnabled: false,
|
|
234
|
-
rebuildThresholdK: 400,
|
|
235
|
-
croEnabled: true,
|
|
236
|
-
diagramEnabled: false,
|
|
237
|
-
humanChannel: defaultHumanChannelSettings()
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* volatile 字段的**结构化解包**(v1.47.2;本文件读取语义的第一道)。
|
|
242
|
-
*
|
|
243
|
-
* ## 为什么必须有它(两个事实的合成,缺一即错)
|
|
244
|
-
* ① **0.1.7 的面板层字段必须标 `.volatile()`** —— 否则本插件命名空间整个不进宿主的
|
|
245
|
-
* `settings.describe()`(`volatileForm()` 只认 `meta.volatile`)⇒ **设置面板那一节消失**。
|
|
246
|
-
* ② **标了之后,运行期拿到的是引用包装,不是裸值** —— schemastery 的 `Schema.resolve()` 对
|
|
247
|
-
* `meta.volatile` **无条件**包 `createVolatile(value)`,且该分支排在"缺省值回退"那一段
|
|
248
|
-
* **之前** ⇒ **"用户没设过"的字段也是恒真对象**(`.get()` 返回 `undefined`),**不是 `undefined`**。
|
|
249
|
-
*
|
|
250
|
-
* ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 / 轨迹约束 会被
|
|
251
|
-
* **无意打开**(比"面板不显示"更坏:静默改变对外暴露面)。
|
|
252
|
-
*
|
|
253
|
-
* ## 为什么用结构判定
|
|
254
|
-
* 可观察形状就是"有 `get()`"(cosmokit 的 `isVolatile` 也这么判);本模块不引它的运行时依赖。
|
|
255
|
-
*
|
|
256
|
-
* @param value - volatile 引用 | 裸值 | undefined
|
|
257
|
-
* @returns 解包后的裸值(引用 ⇒ `.get()`;裸值原样;缺省 ⇒ `undefined`)
|
|
258
|
-
*/
|
|
259
|
-
function unwrapVolatile(value) {
|
|
260
|
-
if (value === void 0 || value === null) return void 0;
|
|
261
|
-
if (typeof value.get === "function") return value.get();
|
|
262
|
-
return value;
|
|
263
|
-
}
|
|
264
|
-
/** human-channel 的内建缺省(与 `Config` 的 schema 缺省**同源**:这里是读取面,那里是解析面) */
|
|
265
|
-
function defaultHumanChannelSettings() {
|
|
266
|
-
return {
|
|
267
|
-
accounts: [],
|
|
268
|
-
subscriptions: [],
|
|
269
|
-
relayCcc: null,
|
|
270
|
-
sendingAllow: ["*"]
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
/** 取"有内容的字符串"(首尾空白剥掉;空白串 ⇒ `undefined`)—— 面板空文本框会写 `''`,那是"没填"不是"填了个空名" */
|
|
274
|
-
function nonEmptyText(value) {
|
|
275
|
-
if (typeof value !== "string") return void 0;
|
|
276
|
-
const trimmed = value.trim();
|
|
277
|
-
return trimmed === "" ? void 0 : trimmed;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* `humanChannel` 片段 → 运行时归一形态。**优先级:显式值 > 内建缺省**(与 `simpleSettingsFromConfig` 同族)。
|
|
281
|
-
*
|
|
282
|
-
* 🔴 三处"缺省 vs 显式"的语义差别,别写反:
|
|
283
|
-
* ① `sending.allow: []` ⇒ **`[]`**("谁都不许"),**不是**回退到 `['*']` —— 故用 `??`
|
|
284
|
-
* (只对 `undefined`/`null` 回退)。⚠️ **别拿 `??`→`||` 当变异探针**:空数组在 JS 里是
|
|
285
|
-
* **真值** ⇒ 两种写法在这个输入上**恰好同效**,那是**非变异**(会得出"测试不承重"的假结论)。
|
|
286
|
-
* ② `relay.ccc` 空白 ⇒ `null`("没填"与"没这一项"同义),**不是**把空白当 CCC 名;
|
|
287
|
-
* ③ 账号 `enabled` 缺省 **true**(老账号表里 `enabled` 不写就是开)。
|
|
288
|
-
*
|
|
289
|
-
* 🔴 **旧 CCC 侧 `weixin.*` 不在这里、也不该在这里**:机器级面**只**认 `humanChannel`
|
|
290
|
-
* (硬切无别名,同 D61 先例)⇒ 传进来的旧键**一律不进结构**(回归钉见
|
|
291
|
-
* `tests/human-channel-config.test.ts`;CCC 侧旧读取面的退役在 P0-2~P0-4)。
|
|
292
|
-
* @param fragment - `Config.humanChannel`(可能整个不存在 —— 那是**合法**初始态)
|
|
293
|
-
* @returns 归一形态(永不 `undefined`)
|
|
294
|
-
*/
|
|
295
|
-
function humanChannelSettingsFromConfig(fragment) {
|
|
296
|
-
const d = defaultHumanChannelSettings();
|
|
297
|
-
if (!fragment) return d;
|
|
298
|
-
return {
|
|
299
|
-
accounts: (fragment.accounts ?? []).map((a) => ({
|
|
300
|
-
id: nonEmptyText(a?.id),
|
|
301
|
-
channel: nonEmptyText(a?.channel),
|
|
302
|
-
enabled: a?.enabled !== false,
|
|
303
|
-
token: a?.token,
|
|
304
|
-
userId: a?.userId
|
|
305
|
-
})),
|
|
306
|
-
subscriptions: (fragment.subscriptions ?? []).map((s) => ({
|
|
307
|
-
account: nonEmptyText(s?.account),
|
|
308
|
-
user: nonEmptyText(s?.user),
|
|
309
|
-
ccc: nonEmptyText(s?.ccc),
|
|
310
|
-
role: nonEmptyText(s?.role)
|
|
311
|
-
})),
|
|
312
|
-
relayCcc: nonEmptyText(fragment.relay?.ccc) ?? d.relayCcc,
|
|
313
|
-
sendingAllow: fragment.sending?.allow ?? d.sendingAllow
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Config(宿主 Loader 校验后的解析值)→ 扁平简单配置。**本文件唯一的读取语义**。
|
|
318
|
-
*
|
|
319
|
-
* 优先级:**面板层(扁平键)> 部署层(嵌套段)> 内建缺省**。
|
|
320
|
-
* @param config - 插件 Config(`fiber.config` 或 apply 收到的 config)
|
|
321
|
-
* @returns 扁平开关集合
|
|
322
|
-
*/
|
|
323
|
-
function simpleSettingsFromConfig(config) {
|
|
324
|
-
const d = defaultSimpleSettings();
|
|
325
|
-
return {
|
|
326
|
-
gatewayEnabled: unwrapVolatile(config.gatewayEnabled) ?? config.gateway?.enabled ?? d.gatewayEnabled,
|
|
327
|
-
rebuildThresholdK: unwrapVolatile(config.rebuildThresholdK) ?? config.rebuild?.thresholdK ?? d.rebuildThresholdK,
|
|
328
|
-
croEnabled: unwrapVolatile(config.croEnabled) ?? d.croEnabled,
|
|
329
|
-
diagramEnabled: unwrapVolatile(config.diagramEnabled) ?? config.diagram?.enabled ?? d.diagramEnabled,
|
|
330
|
-
humanChannel: humanChannelSettingsFromConfig(config.humanChannel)
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
/** 运行时源(`registerSettingsSection` 装配:读本插件 fiber 的**当前** Config) */
|
|
334
|
-
let simpleSource = null;
|
|
335
|
-
/**
|
|
336
|
-
* 读取当前简单配置(各功能 gateway/rebuild/轨迹约束/… 的启动与运行时判断都经此)。
|
|
337
|
-
*
|
|
338
|
-
* 源 = 本插件 fiber 的 `config`(宿主在配置变更时走 `fiber.update()` ⇒ `config` 始终最新,
|
|
339
|
-
* **不必**依赖 apply 重跑,也不必缓存)。
|
|
340
|
-
* @returns 扁平开关集合
|
|
341
|
-
*/
|
|
342
|
-
function readSimpleSettings() {
|
|
343
|
-
return simpleSource ? simpleSource() : defaultSimpleSettings();
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* 装配简单配置读取面(v1.21 分层;**v0.1.7 起不再注册任何 section**)。
|
|
347
|
-
*
|
|
348
|
-
* 两件事:
|
|
349
|
-
* 1. 把"本插件自己的 Config"接成简单配置源 —— 读 `ctx.fiber.config`(宿主 `fiber.update()`
|
|
350
|
-
* 保证最新),拿不到 fiber 时退回 apply 收到的 `config`。
|
|
351
|
-
* 2. 关掉宿主按 Config schema **自动生成**的通用页(`settings.configure({ auto: false })`)——
|
|
352
|
-
* 门面是我们的自定义页 `client/SettingsSection.tsx`(同一个命名空间,同一份数据)。
|
|
353
|
-
*
|
|
354
|
-
* ⚠️ 两个失败都不阻断插件装载(面板是附属能力,绝不能成为启动单点)。
|
|
355
|
-
* @param ctx - 插件上下文
|
|
356
|
-
* @param config - apply 收到的 Config(fiber.config 不可用时的兜底)
|
|
357
|
-
*/
|
|
358
|
-
function registerSettingsSection(ctx, config) {
|
|
359
|
-
const fiber = ctx.fiber;
|
|
360
|
-
const readConfig = () => fiber?.config ?? config;
|
|
361
|
-
simpleSource = () => simpleSettingsFromConfig(readConfig());
|
|
362
|
-
const settings = hostSettings(ctx);
|
|
363
|
-
try {
|
|
364
|
-
settings?.configure?.({ auto: false }, fiber);
|
|
365
|
-
} catch {}
|
|
366
|
-
}
|
|
367
|
-
//#endregion
|
|
368
|
-
export { buildTrajectoryBasePrompt as a, createProfilePromptReader as c, resolveConstraintSystemPrompt as d, ACC_VERSION as f, TRAJECTORY_CONSTRAINT_HINT as i, profileMsmWhitelist as l, readSimpleSettings as n, constraintCognitiveTrajectory as o, findPackageRoot as p, registerSettingsSection as r, constraintSystemPromptSource as s, SERENITY_SETTINGS_NS as t, profileToolWhitelist as u };
|