@shgroup/dsh-serenity-hooks 1.70.0 → 1.72.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 +21 -19
- package/dsh.plugin.json +2 -2
- package/lib/{ccc-C33QWoWZ.js → ccc-Dkoa2oW3.js} +16 -2
- package/lib/{ccc-roots-RzasspMr.js → ccc-roots-jZYR2GVE.js} +16 -10
- package/lib/client.js +2 -2
- package/lib/{gateway-auth.d.ts → gateway/auth.d.ts} +3 -3
- package/lib/{gateway-dsh-auth.d.ts → gateway/dsh-auth.d.ts} +1 -1
- package/lib/{gateway-proxy.d.ts → gateway/proxy.d.ts} +3 -3
- package/lib/{gateway.d.ts → gateway/server.d.ts} +5 -5
- package/lib/{totp.d.ts → gateway/totp.d.ts} +1 -1
- package/lib/{output-guard.d.ts → guard/filter.d.ts} +2 -2
- package/lib/{output-guard-seam.d.ts → guard/seam.d.ts} +2 -2
- package/lib/{weixin-api-BrRcPiQs.js → ilink-DEpmE3yi.js} +6 -6
- package/lib/im/accounts.d.ts +105 -0
- package/lib/{weixin-bridge.d.ts → im/bridge.d.ts} +16 -9
- package/lib/{im-bridge.d.ts → im/channels.d.ts} +3 -3
- package/lib/{weixin-send-endpoint.d.ts → im/endpoint.d.ts} +2 -2
- package/lib/{weixin-output-guard.d.ts → im/guard.d.ts} +1 -1
- package/lib/{weixin-hook.d.ts → im/hook.d.ts} +1 -1
- package/lib/{weixin-api.d.ts → im/ilink.d.ts} +3 -3
- package/lib/{human-channel.d.ts → im/inbound.d.ts} +4 -4
- package/lib/{weixin-route.d.ts → im/route.d.ts} +52 -11
- package/lib/{weixin-send-api.d.ts → im/send.d.ts} +3 -3
- package/lib/im/weixin-account.d.ts +45 -0
- package/lib/{im-weixin.d.ts → im/weixin-channel.d.ts} +2 -2
- package/lib/index.js +1008 -815
- package/lib/invariant.js +2 -2
- package/lib/{ccc-roots.d.ts → kernel/ccc-roots.d.ts} +10 -10
- package/lib/{ccc.d.ts → kernel/ccc.d.ts} +114 -44
- package/lib/{config-ops.d.ts → kernel/config.d.ts} +2 -2
- package/lib/kernel/constants.d.ts +20 -0
- package/lib/{invariant.d.ts → kernel/invariant.d.ts} +1 -1
- package/lib/{json.d.ts → kernel/json.d.ts} +1 -1
- package/lib/{message-source.d.ts → kernel/message-source.d.ts} +1 -1
- package/lib/{ports.d.ts → kernel/ports.d.ts} +1 -1
- package/lib/{time.d.ts → kernel/time.d.ts} +1 -1
- package/lib/message-source-kVoNK0C5.js +432 -0
- package/lib/{deepseek-vision-patch.d.ts → patches/deepseek-vision.d.ts} +3 -3
- package/lib/{opencode-provider.d.ts → patches/opencode-provider.d.ts} +1 -1
- package/lib/{web-fetch-provider.d.ts → patches/web-fetch.d.ts} +1 -1
- package/lib/{message-source-DhA1dVBi.js → profile-DyMJ_bfr.js} +277 -463
- package/lib/role/skiff/config.d.ts +79 -0
- package/lib/role/skiff/core.d.ts +155 -0
- package/lib/{skiff-debug.d.ts → role/skiff/debug.d.ts} +4 -4
- package/lib/role/skiff/profile.d.ts +115 -0
- package/lib/role/skiff/registry.d.ts +41 -0
- package/lib/{cheap-worker-ops.d.ts → role/worker/core.d.ts} +1 -1
- package/lib/{agent-idle.d.ts → role/worker/idle.d.ts} +2 -2
- package/lib/{cheap-worker-preset-inherit.d.ts → role/worker/preset.d.ts} +2 -2
- package/lib/{weixin-route-D_DvgPqy.js → route-f7JlinIt.js} +132 -47
- package/lib/seams/context.d.ts +3 -3
- package/lib/seams/guards.d.ts +18 -12
- package/lib/seams/keeper.d.ts +1 -1
- package/lib/seams/system-prompt.d.ts +3 -1
- package/lib/settings-lt1i3OZv.js +403 -0
- package/lib/{skills-discovery.d.ts → skills/discovery.d.ts} +1 -1
- package/lib/skills/resource-index.d.ts +2 -2
- package/lib/{face-host.d.ts → surface/faces.d.ts} +4 -4
- package/lib/{api.d.ts → surface/http.d.ts} +2 -2
- package/lib/{container-status.d.ts → surface/observation.d.ts} +16 -16
- package/lib/{settings-section.d.ts → surface/settings.d.ts} +1 -1
- package/lib/{status.d.ts → surface/status.d.ts} +2 -2
- package/lib/{usage-stats.d.ts → surface/usage.d.ts} +4 -4
- package/lib/{voyage-page.d.ts → surface/voyage/page.d.ts} +2 -2
- package/lib/{voyage-vocab.d.ts → surface/voyage/vocab.d.ts} +1 -1
- package/lib/tools/cognitive-trajectory.d.ts +2 -2
- package/lib/tools/{acc-diag.d.ts → diag/index.d.ts} +2 -2
- package/lib/{diag-ops.d.ts → tools/diag/service.d.ts} +7 -7
- package/lib/tools/eap.d.ts +1 -1
- package/lib/tools/fs/index.d.ts +7 -0
- package/lib/{fs-ops.d.ts → tools/fs/service.d.ts} +2 -2
- package/lib/tools/git/index.d.ts +4 -0
- package/lib/{git-ops.d.ts → tools/git/service.d.ts} +2 -2
- package/lib/tools/im-bridge.d.ts +2 -2
- package/lib/tools/{kit.d.ts → kit/index.d.ts} +1 -1
- package/lib/{kit-ops.d.ts → tools/kit/service.d.ts} +6 -6
- package/lib/tools/{localstore.d.ts → localstore/index.d.ts} +1 -1
- package/lib/{localstore-ops.d.ts → tools/localstore/service.d.ts} +2 -2
- package/lib/tools/{msm.d.ts → msm/index.d.ts} +1 -1
- package/lib/{msm-ops.d.ts → tools/msm/service.d.ts} +2 -2
- package/lib/tools/skiff-admin.d.ts +19 -14
- package/lib/{cognitive-trajectory-assistant.d.ts → trajectory/assistant.d.ts} +2 -2
- package/lib/{cognitive-trajectory-bound.d.ts → trajectory/bound.d.ts} +2 -2
- package/lib/{session-cleanup.d.ts → trajectory/cleanup.d.ts} +1 -1
- package/lib/{cognitive-trajectory-ops.d.ts → trajectory/core.d.ts} +55 -25
- package/lib/{live-sessions.d.ts → trajectory/live.d.ts} +1 -1
- package/lib/trajectory/prompt.d.ts +101 -0
- package/lib/{rebuild.d.ts → trajectory/rebuild.d.ts} +1 -1
- package/lib/{cognitive-trajectory-skills.d.ts → trajectory/skills.d.ts} +1 -1
- package/lib/{clock-runtime.d.ts → wake/clock.d.ts} +3 -3
- package/lib/{cro-guide.d.ts → wake/cro-guide.d.ts} +1 -1
- package/lib/{cro-log.d.ts → wake/cro-log.d.ts} +3 -3
- package/lib/{cro-turns.d.ts → wake/cro-turns.d.ts} +5 -5
- package/lib/{cro.d.ts → wake/cro.d.ts} +6 -6
- package/lib/{wake-registry.d.ts → wake/registry.d.ts} +2 -2
- package/lib/{wake-scheduler.d.ts → wake/scheduler.d.ts} +3 -3
- package/package.json +2 -2
- package/lib/constants.d.ts +0 -6
- package/lib/settings-section-0TPYWug9.js +0 -238
- package/lib/skiff-core.d.ts +0 -145
- package/lib/skiff-registry.d.ts +0 -26
- package/lib/skiff-role-cwxAt0gE.js +0 -203
- package/lib/skiff-role.d.ts +0 -89
- package/lib/tools/cc-fs.d.ts +0 -7
- package/lib/tools/git.d.ts +0 -4
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { f as hostSettings } from "./ccc-roots-jZYR2GVE.js";
|
|
2
|
+
import { _ as resolveInside, g as readUtf8 } from "./ccc-Dkoa2oW3.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/role/skiff/config.ts
|
|
52
|
+
/**
|
|
53
|
+
* role/skiff/config.ts — skiff 约束面的**纯逻辑助手**(零 DSH 依赖,可独立单测)
|
|
54
|
+
*
|
|
55
|
+
* ## 2026-10-19 重做后的职责边界(R↓,别再混起来)
|
|
56
|
+
*
|
|
57
|
+
* | 模块 | 管什么 |
|
|
58
|
+
* |---|---|
|
|
59
|
+
* | `./profile.ts` | **判据 + 读盘**("这是不是 skiff"、"约束面是什么") |
|
|
60
|
+
* | `./config.ts`(本文件) | **从约束面派生出的纯函数**(白名单集合、提示词读取器、基础提示词文本) |
|
|
61
|
+
* | `./core.ts` | 会话创建/复用、提问、守门查询 |
|
|
62
|
+
*
|
|
63
|
+
* ⇒ 本文件**不再读 `serenity.json`**:`skiff.roles` 整段已退役(owner 2026-10-19 重做)。
|
|
64
|
+
* 它只接收一个已解析好的 `SkiffProfile`,因此**不涉及任何 IO**(除提示词文件,那是刻意懒读的)。
|
|
65
|
+
*/
|
|
66
|
+
/** 白名单推导 ────────────────────────────────────────────────────────────── */
|
|
67
|
+
/** 提示词来源提示串(`apply` 回执与面板共用;避免两处措辞漂) */
|
|
68
|
+
const SKIFF_PROFILE_HINT = "systemPromptFile hot-reloads by mtime; the rest of skiff.json is re-read every turn";
|
|
69
|
+
/**
|
|
70
|
+
* 该 skiff 会话**可见**的工具面(tools ∪ `msm`)。
|
|
71
|
+
*
|
|
72
|
+
* 🔴 `msm` 为什么自动加入:`msms` 非空意味着这个 skiff **有 MSM 通道**,
|
|
73
|
+
* 而 MSM 的唯一合法调用入口是 `msm` 工具(单入口纪律)——不给它 `msm` 就等于
|
|
74
|
+
* 声明了一堆调不动的能力。⚠️ 这是**并集**,不是覆盖:`tools` 里显式写的照旧生效。
|
|
75
|
+
*/
|
|
76
|
+
function profileToolWhitelist(profile) {
|
|
77
|
+
const out = /* @__PURE__ */ new Set();
|
|
78
|
+
for (const t of profile?.tools ?? []) out.add(t);
|
|
79
|
+
if ((profile?.msms?.length ?? 0) > 0) out.add("msm");
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** 该 skiff 会话可 exec 的 MSM 白名单(`msm` 工具内部再过滤;独立于工具白名单) */
|
|
83
|
+
function profileMsmWhitelist(profile) {
|
|
84
|
+
return new Set(profile?.msms ?? []);
|
|
85
|
+
}
|
|
86
|
+
/** 轨迹纪律参与项(`profile` 缺省已在 `readSkiffProfileAt` 归一为全 true) */
|
|
87
|
+
function profileCognitiveTrajectory(profile) {
|
|
88
|
+
return {
|
|
89
|
+
session: profile?.cognitiveTrajectory.session === true,
|
|
90
|
+
keeper: profile?.cognitiveTrajectory.keeper === true,
|
|
91
|
+
rebuild: profile?.cognitiveTrajectory.rebuild === true
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 解析约束面声明的**系统提示词全文**(一次性读取;`create` / `validate` / `list` 用)。
|
|
96
|
+
*
|
|
97
|
+
* 顺序:`systemPromptFile`(**推荐**:超长提示词在 JSON 里内嵌不可读)→ 内嵌 `systemPrompt` → 空串。
|
|
98
|
+
* 文件缺失/逃逸 ⇒ **抛错**(调用方 catch 降级;`validate` 据此报 issue)。
|
|
99
|
+
*
|
|
100
|
+
* ⚠️ 热路径**不用**它(那会用每次工具调用去读盘)——热路径用 `createProfilePromptReader`。
|
|
101
|
+
*/
|
|
102
|
+
function resolveProfileSystemPrompt(root, profile) {
|
|
103
|
+
if (!profile) return "";
|
|
104
|
+
const file = profile.systemPromptFile?.trim();
|
|
105
|
+
if (file) {
|
|
106
|
+
const abs = resolveInside(root, file);
|
|
107
|
+
if (!existsSync(abs)) throw new Error(`skiff "${profile.code}": systemPromptFile "${file}" not found (resolved: ${abs})`);
|
|
108
|
+
return readUtf8(abs).trim();
|
|
109
|
+
}
|
|
110
|
+
return profile.systemPrompt ?? "";
|
|
111
|
+
}
|
|
112
|
+
/** 提示词来源(`list`/`validate` 展示用) */
|
|
113
|
+
function profileSystemPromptSource(profile) {
|
|
114
|
+
if (profile?.systemPromptFile?.trim()) return "file";
|
|
115
|
+
if (profile?.systemPrompt?.trim()) return "inline";
|
|
116
|
+
return "none";
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 提示词**读取器**(长期热更新;改文件即生效,无需重启)。
|
|
120
|
+
*
|
|
121
|
+
* 为什么返回函数而不是字符串(R↓,实证代价):旧实现在创建时把提示词**快照**进系统提示词段闭包
|
|
122
|
+
* ⇒ CCC 改了提示词文件必须重启 dsh web 才生效(实测:改完 18KB 提示词,live agent 仍用旧文本)。
|
|
123
|
+
* 现形态由系统提示词段的 `text()` **每轮调用**本读取器:
|
|
124
|
+
* - `systemPromptFile`:按 **mtime + size** 缓存重读(命中缓存零 IO;未变不解析)
|
|
125
|
+
* - 内嵌 `systemPrompt`:静态(改 JSON 仍需重载配置,这与文件热更的差别是**必然**的 —— 文件有 mtime,JSON 没有)
|
|
126
|
+
* - 读取失败:返回**上次成功内容**(⛔ 绝不返回空——空提示词会让角色失去人格与纪律),并按标签去重告警一次
|
|
127
|
+
*
|
|
128
|
+
* @param root CCC 根
|
|
129
|
+
* @param profile 约束面
|
|
130
|
+
*/
|
|
131
|
+
function createProfilePromptReader(root, profile) {
|
|
132
|
+
const file = profile?.systemPromptFile?.trim();
|
|
133
|
+
if (!file) {
|
|
134
|
+
const inline = profile?.systemPrompt ?? "";
|
|
135
|
+
return () => inline;
|
|
136
|
+
}
|
|
137
|
+
let abs = null;
|
|
138
|
+
try {
|
|
139
|
+
abs = resolveInside(root, file);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.warn(`[serenity-hooks] ✗ skiff 提示词路径非法(${file}): ${String(err?.message ?? err)}`);
|
|
142
|
+
}
|
|
143
|
+
if (abs === null) return () => "";
|
|
144
|
+
const absPath = abs;
|
|
145
|
+
let cachedMtime = -1;
|
|
146
|
+
let cachedSize = -1;
|
|
147
|
+
let cachedText = "";
|
|
148
|
+
let warned = false;
|
|
149
|
+
return () => {
|
|
150
|
+
try {
|
|
151
|
+
const st = statSync(absPath);
|
|
152
|
+
if (st.mtimeMs === cachedMtime && st.size === cachedSize) return cachedText;
|
|
153
|
+
const text = readUtf8(absPath).trim();
|
|
154
|
+
const isReload = cachedMtime !== -1;
|
|
155
|
+
cachedMtime = st.mtimeMs;
|
|
156
|
+
cachedSize = st.size;
|
|
157
|
+
cachedText = text;
|
|
158
|
+
warned = false;
|
|
159
|
+
if (isReload) console.info(`[serenity-hooks] ↻ skiff 提示词已热重载(${file},${text.length} 字符)`);
|
|
160
|
+
return text;
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (!warned) {
|
|
163
|
+
warned = true;
|
|
164
|
+
console.warn(`[serenity-hooks] ✗ skiff 提示词读取失败(${file})——沿用上次成功内容: ${String(err?.message ?? err)}`);
|
|
165
|
+
}
|
|
166
|
+
return cachedText;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* skiff 基础提示词(dsp 只给这部分;CCC 的 `systemPrompt` 段由调用方拼接)。
|
|
172
|
+
*
|
|
173
|
+
* ## 🔴 为什么**不再枚举**清单(owner 2026-10-19 令)
|
|
174
|
+
*
|
|
175
|
+
* 旧版在这里抄了两份清单(`MSMs: a, b, c` / `Tools: read, grep, …`)。那是**第二真相源**,
|
|
176
|
+
* 而真相源本来就在模型自己手里:
|
|
177
|
+
* · **工具清单** = 这一轮实际注册给它的 tool schema —— 模型已经看得见,抄一遍只会漂
|
|
178
|
+
* · **MSM 清单** = `msm()` 无参调用返回的目录 —— 已按白名单过滤,且**每次实时读配置**
|
|
179
|
+
*
|
|
180
|
+
* ⚠️ **漂移不是理论风险,是已发生的缺陷**(2026-10-19):角色配置早就长到 8 个工具,
|
|
181
|
+
* 而这个函数是从**创建时冻结的快照**求值的 ⇒ 提示词里一直写着旧的 4 个
|
|
182
|
+
* ⇒ 模型照着提示词办事,**它自己以为没有的能力就不去调** ⇒ 症状是"工具用不了",
|
|
183
|
+
* 而守卫那边(每调用实时读配置)其实是放行的。两个真相源打架,输的总是提示词。
|
|
184
|
+
*
|
|
185
|
+
* ⇒ 现在只声明**边界与发现路径**,不抄内容:清单在哪,怎么取,越界会怎样。
|
|
186
|
+
*
|
|
187
|
+
* @param code 轨迹编码(skiff 的身份 —— 不是"角色名",那个概念已退役)
|
|
188
|
+
*/
|
|
189
|
+
function buildSkiffBasePrompt(code) {
|
|
190
|
+
return [
|
|
191
|
+
"=== Serenity Skiff ===",
|
|
192
|
+
`Cognitive trajectory: ${code} (this is the trajectory you ARE, not a role you play)`,
|
|
193
|
+
"Your capability boundary is enforced mechanically by this CCC — anything outside it is rejected at the call site, not by your judgment.",
|
|
194
|
+
"Discover your surface, do not memorize it (both are generated from the live config on every turn):",
|
|
195
|
+
" · Your tools are exactly the tool schemas you see in this session — if a tool is not in your schema, it is not available.",
|
|
196
|
+
" · Your MSMs are listed by calling msm() with no arguments — that directory is already filtered to this trajectory.",
|
|
197
|
+
" · Usage for any MSM: msm(\"<name>\", [], inspect=true) or pass \"--help\" as the first argument.",
|
|
198
|
+
"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.",
|
|
199
|
+
"",
|
|
200
|
+
"---",
|
|
201
|
+
""
|
|
202
|
+
].join("\n");
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/kernel/ports.ts
|
|
206
|
+
/**
|
|
207
|
+
* 集中端口表。
|
|
208
|
+
* A=宿主主面(插件不监听)/ B=网关(可配,默认关)/ C=微信发送(默认开,仅 loopback)
|
|
209
|
+
* / D=Skiff 调试(默认关)。
|
|
210
|
+
*/
|
|
211
|
+
const FACE_PORTS = {
|
|
212
|
+
main: {
|
|
213
|
+
port: 3080,
|
|
214
|
+
host: null,
|
|
215
|
+
defaultEnabled: true,
|
|
216
|
+
meaning: "宿主 DSH 主 WebUI + 插件 /serenity/* 路由面(**插件不起此 listener**,端口自宿主读)"
|
|
217
|
+
},
|
|
218
|
+
gateway: {
|
|
219
|
+
port: 3081,
|
|
220
|
+
host: "0.0.0.0",
|
|
221
|
+
defaultEnabled: false,
|
|
222
|
+
meaning: "双端口网关(外部登录 + 全量反代宿主主面;host/port 走 plugin 全局配置)"
|
|
223
|
+
},
|
|
224
|
+
weixinSend: {
|
|
225
|
+
port: 3082,
|
|
226
|
+
host: "127.0.0.1",
|
|
227
|
+
defaultEnabled: true,
|
|
228
|
+
meaning: "微信主动发送面(容器外进程 → POST /send;与 3080 显式解耦,不经网关)"
|
|
229
|
+
},
|
|
230
|
+
skiffDebug: {
|
|
231
|
+
port: 3099,
|
|
232
|
+
host: "127.0.0.1",
|
|
233
|
+
defaultEnabled: false,
|
|
234
|
+
meaning: "Skiff 调试问答面(人工调试子角色;无认证,仅靠 loopback)"
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
/** 宿主主 WebUI 端口(面 A:插件**不监听**,只读取;`readWebPort` 的回退值) */
|
|
238
|
+
const MAIN_WEB_PORT = FACE_PORTS.main.port;
|
|
239
|
+
/** 网关端口默认值(面 B) */
|
|
240
|
+
const GATEWAY_PORT = FACE_PORTS.gateway.port;
|
|
241
|
+
/** 微信主动发送端口默认值(面 C) */
|
|
242
|
+
const WEIXIN_SEND_PORT = FACE_PORTS.weixinSend.port;
|
|
243
|
+
/** Skiff 调试页端口默认值(面 D) */
|
|
244
|
+
const SKIFF_DEBUG_PORT = FACE_PORTS.skiffDebug.port;
|
|
245
|
+
/**
|
|
246
|
+
* **机制端口**词表(output-guard 用:agent 在**用户可见输出**里提及内部端口即打回)。
|
|
247
|
+
*
|
|
248
|
+
* 由 {@link FACE_PORTS} 机械派生 ⇒ 与面清单**永不漂移**。
|
|
249
|
+
* ⚠️ 修复记录(C4 块 B,2026-09-15):旧常量硬写 `['3080','3081','3099','3100']`
|
|
250
|
+
* ——**漏 3082**(微信发送面,默认开)⇒ 守卫拦不住"3082"这类提及。现由表派生,四面齐全。
|
|
251
|
+
* ⚠️ 面变更记录(2026-10-15):**面 E(3100 ACP/问答)已整体退役**(owner 裁「砍掉外部问答」)
|
|
252
|
+
* ⇒ 本词表随之少一项,**无需另改**(这正是"派生而非手抄"的收益)。
|
|
253
|
+
*/
|
|
254
|
+
const MECHANISM_PORTS = Object.keys(FACE_PORTS).map((id) => String(FACE_PORTS[id].port));
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/surface/settings.ts
|
|
257
|
+
/**
|
|
258
|
+
* 设置面板命名空间 = **profile 条目 id**(v0.1.7 起;与旧 `settings.yaml` 段名逐字相同)。
|
|
259
|
+
*/
|
|
260
|
+
const SERENITY_SETTINGS_NS = "serenity-hooks";
|
|
261
|
+
/** 进程级默认(无 Config 可读时的兜底;与 Config 各字段的缺省一致) */
|
|
262
|
+
function defaultSimpleSettings() {
|
|
263
|
+
return {
|
|
264
|
+
gatewayEnabled: false,
|
|
265
|
+
rebuildThresholdK: 400,
|
|
266
|
+
skiffEnabled: false,
|
|
267
|
+
skiffDebugPort: SKIFF_DEBUG_PORT,
|
|
268
|
+
croEnabled: true,
|
|
269
|
+
diagramEnabled: false,
|
|
270
|
+
humanChannel: defaultHumanChannelSettings()
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* volatile 字段的**结构化解包**(v1.47.2;本文件读取语义的第一道)。
|
|
275
|
+
*
|
|
276
|
+
* ## 为什么必须有它(两个事实的合成,缺一即错)
|
|
277
|
+
* ① **0.1.7 的面板层字段必须标 `.volatile()`** —— 否则本插件命名空间整个不进宿主的
|
|
278
|
+
* `settings.describe()`(`volatileForm()` 只认 `meta.volatile`)⇒ **设置面板那一节消失**。
|
|
279
|
+
* ② **标了之后,运行期拿到的是引用包装,不是裸值** —— schemastery 的 `Schema.resolve()` 对
|
|
280
|
+
* `meta.volatile` **无条件**包 `createVolatile(value)`,且该分支排在"缺省值回退"那一段
|
|
281
|
+
* **之前** ⇒ **"用户没设过"的字段也是恒真对象**(`.get()` 返回 `undefined`),**不是 `undefined`**。
|
|
282
|
+
*
|
|
283
|
+
* ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 / Skiff 会被
|
|
284
|
+
* **无意打开**(比"面板不显示"更坏:静默改变对外暴露面)。
|
|
285
|
+
*
|
|
286
|
+
* ## 为什么用结构判定
|
|
287
|
+
* 可观察形状就是"有 `get()`"(cosmokit 的 `isVolatile` 也这么判);本模块不引它的运行时依赖。
|
|
288
|
+
*
|
|
289
|
+
* @param value - volatile 引用 | 裸值 | undefined
|
|
290
|
+
* @returns 解包后的裸值(引用 ⇒ `.get()`;裸值原样;缺省 ⇒ `undefined`)
|
|
291
|
+
*/
|
|
292
|
+
function unwrapVolatile(value) {
|
|
293
|
+
if (value === void 0 || value === null) return void 0;
|
|
294
|
+
if (typeof value.get === "function") return value.get();
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
/** human-channel 的内建缺省(与 `Config` 的 schema 缺省**同源**:这里是读取面,那里是解析面) */
|
|
298
|
+
function defaultHumanChannelSettings() {
|
|
299
|
+
return {
|
|
300
|
+
accounts: [],
|
|
301
|
+
subscriptions: [],
|
|
302
|
+
relayCcc: null,
|
|
303
|
+
sendingAllow: ["*"]
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/** 取"有内容的字符串"(首尾空白剥掉;空白串 ⇒ `undefined`)—— 面板空文本框会写 `''`,那是"没填"不是"填了个空名" */
|
|
307
|
+
function nonEmptyText(value) {
|
|
308
|
+
if (typeof value !== "string") return void 0;
|
|
309
|
+
const trimmed = value.trim();
|
|
310
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* `humanChannel` 片段 → 运行时归一形态。**优先级:显式值 > 内建缺省**(与 `simpleSettingsFromConfig` 同族)。
|
|
314
|
+
*
|
|
315
|
+
* 🔴 三处"缺省 vs 显式"的语义差别,别写反:
|
|
316
|
+
* ① `sending.allow: []` ⇒ **`[]`**("谁都不许"),**不是**回退到 `['*']` —— 故用 `??`
|
|
317
|
+
* (只对 `undefined`/`null` 回退)。⚠️ **别拿 `??`→`||` 当变异探针**:空数组在 JS 里是
|
|
318
|
+
* **真值** ⇒ 两种写法在这个输入上**恰好同效**,那是**非变异**(会得出"测试不承重"的假结论)。
|
|
319
|
+
* ② `relay.ccc` 空白 ⇒ `null`("没填"与"没这一项"同义),**不是**把空白当 CCC 名;
|
|
320
|
+
* ③ 账号 `enabled` 缺省 **true**(老账号表里 `enabled` 不写就是开)。
|
|
321
|
+
*
|
|
322
|
+
* 🔴 **旧 CCC 侧 `weixin.*` 不在这里、也不该在这里**:机器级面**只**认 `humanChannel`
|
|
323
|
+
* (硬切无别名,同 D61 先例)⇒ 传进来的旧键**一律不进结构**(回归钉见
|
|
324
|
+
* `tests/human-channel-config.test.ts`;CCC 侧旧读取面的退役在 P0-2~P0-4)。
|
|
325
|
+
* @param fragment - `Config.humanChannel`(可能整个不存在 —— 那是**合法**初始态)
|
|
326
|
+
* @returns 归一形态(永不 `undefined`)
|
|
327
|
+
*/
|
|
328
|
+
function humanChannelSettingsFromConfig(fragment) {
|
|
329
|
+
const d = defaultHumanChannelSettings();
|
|
330
|
+
if (!fragment) return d;
|
|
331
|
+
return {
|
|
332
|
+
accounts: (fragment.accounts ?? []).map((a) => ({
|
|
333
|
+
id: nonEmptyText(a?.id),
|
|
334
|
+
channel: nonEmptyText(a?.channel),
|
|
335
|
+
enabled: a?.enabled !== false,
|
|
336
|
+
token: a?.token,
|
|
337
|
+
userId: a?.userId
|
|
338
|
+
})),
|
|
339
|
+
subscriptions: (fragment.subscriptions ?? []).map((s) => ({
|
|
340
|
+
account: nonEmptyText(s?.account),
|
|
341
|
+
user: nonEmptyText(s?.user),
|
|
342
|
+
ccc: nonEmptyText(s?.ccc),
|
|
343
|
+
role: nonEmptyText(s?.role)
|
|
344
|
+
})),
|
|
345
|
+
relayCcc: nonEmptyText(fragment.relay?.ccc) ?? d.relayCcc,
|
|
346
|
+
sendingAllow: fragment.sending?.allow ?? d.sendingAllow
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Config(宿主 Loader 校验后的解析值)→ 扁平简单配置。**本文件唯一的读取语义**。
|
|
351
|
+
*
|
|
352
|
+
* 优先级:**面板层(扁平键)> 部署层(嵌套段)> 内建缺省**。
|
|
353
|
+
* @param config - 插件 Config(`fiber.config` 或 apply 收到的 config)
|
|
354
|
+
* @returns 扁平开关集合
|
|
355
|
+
*/
|
|
356
|
+
function simpleSettingsFromConfig(config) {
|
|
357
|
+
const d = defaultSimpleSettings();
|
|
358
|
+
return {
|
|
359
|
+
gatewayEnabled: unwrapVolatile(config.gatewayEnabled) ?? config.gateway?.enabled ?? d.gatewayEnabled,
|
|
360
|
+
rebuildThresholdK: unwrapVolatile(config.rebuildThresholdK) ?? config.rebuild?.thresholdK ?? d.rebuildThresholdK,
|
|
361
|
+
skiffEnabled: unwrapVolatile(config.skiffEnabled) ?? config.skiff?.enabled ?? d.skiffEnabled,
|
|
362
|
+
skiffDebugPort: unwrapVolatile(config.skiffDebugPort) ?? config.skiff?.debugPort ?? d.skiffDebugPort,
|
|
363
|
+
croEnabled: unwrapVolatile(config.croEnabled) ?? d.croEnabled,
|
|
364
|
+
diagramEnabled: unwrapVolatile(config.diagramEnabled) ?? config.diagram?.enabled ?? d.diagramEnabled,
|
|
365
|
+
humanChannel: humanChannelSettingsFromConfig(config.humanChannel)
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/** 运行时源(`registerSettingsSection` 装配:读本插件 fiber 的**当前** Config) */
|
|
369
|
+
let simpleSource = null;
|
|
370
|
+
/**
|
|
371
|
+
* 读取当前简单配置(各功能 gateway/rebuild/skiff/… 的启动与运行时判断都经此)。
|
|
372
|
+
*
|
|
373
|
+
* 源 = 本插件 fiber 的 `config`(宿主在配置变更时走 `fiber.update()` ⇒ `config` 始终最新,
|
|
374
|
+
* **不必**依赖 apply 重跑,也不必缓存)。
|
|
375
|
+
* @returns 扁平开关集合
|
|
376
|
+
*/
|
|
377
|
+
function readSimpleSettings() {
|
|
378
|
+
return simpleSource ? simpleSource() : defaultSimpleSettings();
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* 装配简单配置读取面(v1.21 分层;**v0.1.7 起不再注册任何 section**)。
|
|
382
|
+
*
|
|
383
|
+
* 两件事:
|
|
384
|
+
* 1. 把"本插件自己的 Config"接成简单配置源 —— 读 `ctx.fiber.config`(宿主 `fiber.update()`
|
|
385
|
+
* 保证最新),拿不到 fiber 时退回 apply 收到的 `config`。
|
|
386
|
+
* 2. 关掉宿主按 Config schema **自动生成**的通用页(`settings.configure({ auto: false })`)——
|
|
387
|
+
* 门面是我们的自定义页 `client/SettingsSection.tsx`(同一个命名空间,同一份数据)。
|
|
388
|
+
*
|
|
389
|
+
* ⚠️ 两个失败都不阻断插件装载(面板是附属能力,绝不能成为启动单点)。
|
|
390
|
+
* @param ctx - 插件上下文
|
|
391
|
+
* @param config - apply 收到的 Config(fiber.config 不可用时的兜底)
|
|
392
|
+
*/
|
|
393
|
+
function registerSettingsSection(ctx, config) {
|
|
394
|
+
const fiber = ctx.fiber;
|
|
395
|
+
const readConfig = () => fiber?.config ?? config;
|
|
396
|
+
simpleSource = () => simpleSettingsFromConfig(readConfig());
|
|
397
|
+
const settings = hostSettings(ctx);
|
|
398
|
+
try {
|
|
399
|
+
settings?.configure?.({ auto: false }, fiber);
|
|
400
|
+
} catch {}
|
|
401
|
+
}
|
|
402
|
+
//#endregion
|
|
403
|
+
export { resolveProfileSystemPrompt as _, GATEWAY_PORT as a, SKIFF_DEBUG_PORT as c, buildSkiffBasePrompt as d, createProfilePromptReader as f, profileToolWhitelist as g, profileSystemPromptSource as h, FACE_PORTS as i, WEIXIN_SEND_PORT as l, profileMsmWhitelist as m, readSimpleSettings as n, MAIN_WEB_PORT as o, profileCognitiveTrajectory as p, registerSettingsSection as r, MECHANISM_PORTS as s, SERENITY_SETTINGS_NS as t, SKIFF_PROFILE_HINT as u, ACC_VERSION as v, findPackageRoot as y };
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* 六条硬性规则(逐条对应设计 §4.2,改这里先改那里):
|
|
15
15
|
* ① **只枚举,不注入正文** —— 索引里**绝不**出现任何参考文件的正文;
|
|
16
16
|
* ② **确定性排序** = 相对路径的 **UTF-8 字节序**(🔴 **不用 mtime**:mtime 随检出漂移,
|
|
17
|
-
* 会让每请求的注入文本抖动;对照 `src/cro.ts:101` 那份**落盘快照**用 {name,bytes,mtime} ——
|
|
17
|
+
* 会让每请求的注入文本抖动;对照 `src/wake/cro.ts:101` 那份**落盘快照**用 {name,bytes,mtime} ——
|
|
18
18
|
* **同一件事两处的取舍相反,那个差异是判据不是巧合**);
|
|
19
19
|
* ③ **有界 + 响亮** —— 条数/字符各有上限,超出**必须显式写出还剩多少项**,**绝不静默截断**;
|
|
20
20
|
* ④ **摘要可复现** —— 取该文件首个 `#` 标题(剥 markdown 记号);取不到 ⇒ 留空,**不编**;
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
*
|
|
31
31
|
* 🔴 **两个面,两个入口**(2026-10-12 增;owner 裁决 O2)—— 同源、但成本形态不同:
|
|
32
32
|
* · **载入面** `buildResourceIndex()` —— 完整索引(名字 + 首标题 + 字节),带 `.md` 文件头读取;
|
|
33
|
-
* 在三条路径上各附一次:入口段(`system-prompt.ts` order −50)/轨迹段(`
|
|
33
|
+
* 在三条路径上各附一次:入口段(`system-prompt.ts` order −50)/轨迹段(`trajectory/skills.ts`
|
|
34
34
|
* order −45)/目录 provider 的 `get()`(`opencode-skills.ts`)。
|
|
35
35
|
* · **目录面** `buildResourceNameSuffix()` —— **只有名字**,**不读任何正文**;只挂一处:
|
|
36
36
|
* 目录 provider 的 `list()`(`available_skills` 的 `SkillSummary.description`)。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* surface/faces.ts — **面宿主**:插件自起 HTTP listener 的唯一生命周期处(C4 块 A,S142 2026-09-15)
|
|
3
3
|
*
|
|
4
4
|
* ## 现状 → 本模块要收掉什么(取证:`…/acc-c4c5-current-state.md` §①/§③ 3.1)
|
|
5
5
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* ⇒ 仍保有面 B/C/D 三个;本段保留"当年是四个"的记述,因为下方"两套拆卸"的病史由它解释。
|
|
9
9
|
* 此前四处各写一份同款样板:`createServer` + `server.once('error')` +
|
|
10
10
|
* `listen(port, host)` + 模块级 `active` 变量 + `close()`;拆卸也分裂成**两套**——
|
|
11
|
-
* `seams/lifecycle.ts` 有一个聚合 disposer,而 `gateway.ts` 与 `
|
|
11
|
+
* `seams/lifecycle.ts` 有一个聚合 disposer,而 `gateway/server.ts` 与 `im/send.ts`
|
|
12
12
|
* **各自另注册**一个(D/E 干脆没有自己的 disposer)。
|
|
13
13
|
*
|
|
14
14
|
* 本模块把"listener 生命周期"收成一处:**active 表 + 统一 listen/close + 统一错误与端口占用
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* ## 🔴 共享的是「生命周期与样板」,**绝不是**「路由 / 监听器 / 鉴权」
|
|
18
18
|
*
|
|
19
19
|
* 每个面**保留自己的 listener、自己的路由、自己的鉴权**(`handler` 内部原样不动):
|
|
20
|
-
* · **A ↔ C 解耦**(`
|
|
20
|
+
* · **A ↔ C 解耦**(`im/send.ts:4-7`):3080 会被 3081 原样反代(连请求头),
|
|
21
21
|
* 把 3082 的端点挂到 3080 上 = 已登录的外部家庭账号即可冒充 bot 发消息
|
|
22
22
|
* ⇒ 本模块**不提供**"把面挂到别的面"的能力,也不共享路由表;
|
|
23
23
|
* · (原 **E ↔ D** 共用会话核心但各自独立 listener 一条,**随 E 退役删除**);
|
|
@@ -64,7 +64,7 @@ export interface FaceSpec {
|
|
|
64
64
|
*
|
|
65
65
|
* 面 C(微信发送,`FACE_PORTS.weixinSend.defaultEnabled = true`)必须传 `true`:宿主若以
|
|
66
66
|
* **一次性命令**方式跑(`dsh <cmd>` 用完即退),未 unref 的 listener 会拖着进程不退出。
|
|
67
|
-
* 这是 C4 之前 `
|
|
67
|
+
* 这是 C4 之前 `im/send.ts` 的原语义(C4 归一到面宿主时丢失),现逐字恢复。
|
|
68
68
|
*/
|
|
69
69
|
unref?: boolean;
|
|
70
70
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* surface/http.ts — HTTP 状态接口(WebUI 停靠栏数据源/开关通道 + 图片落盘)
|
|
3
3
|
*
|
|
4
4
|
* 路由:
|
|
5
5
|
* GET /serenity/status ?workspace=<路径> → 状态(root/accVersion/safeModeOn/...)
|
|
@@ -43,7 +43,7 @@ interface StatusApiRegistration {
|
|
|
43
43
|
* 它就会**静默返回另一个 CCC 的路径**,于是面板把**别人的轨迹/技能/安全模式**当成你的显示出来。
|
|
44
44
|
* 这比"显示未激活"危险得多 —— 用户会据此做判断(以为自己的 safe-mode 开着、以为那是自己的轨迹)。
|
|
45
45
|
*
|
|
46
|
-
* 修法与既有先例一致(`rebuild.ts` 对"跨轨迹猜测"的裁决:**响亮失败优于静默接错**):
|
|
46
|
+
* 修法与既有先例一致(`trajectory/rebuild.ts` 对"跨轨迹猜测"的裁决:**响亮失败优于静默接错**):
|
|
47
47
|
* **多个候选 ⇒ 不猜**。只有当 live sessions 里的 CCC 根**唯一**时才采用它
|
|
48
48
|
* (那正是 v1.22.2 要救的场景:单 CCC 部署 + 远程访问缺 sessionId);
|
|
49
49
|
* 一旦出现**两个及以上**不同 CCC 根,就退回进程 cwd(→ 面板显示"未激活"),绝不挑一个顶替。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* surface/observation.ts — 容器状态模型的**取数层唯一出口**(C5「观察面归一」,S142 2026-09-15)
|
|
3
3
|
*
|
|
4
4
|
* 为什么存在(R↓):ACC 有多个观察面(`dashboard health` / `acc-diag` / WebUI 面板 /
|
|
5
5
|
* 系统提示词注入),它们读的是同一批宿主事实。此前"这些事实怎么取"写在**每个消费方各自的
|
|
@@ -17,19 +17,19 @@
|
|
|
17
17
|
* 依赖分层(为什么本模块"重"而 `kit-ops` / `status` 保持"轻"):
|
|
18
18
|
* {@link containerClocks} 必须读 `wake-scheduler` 的进程内快照,而该模块带 **@deepseek-ai 值依赖**
|
|
19
19
|
* (`dsh-llm` 的 `createUserMessage`)。故本模块**只能**被本就重型或异步的消费方使用:
|
|
20
|
-
* `diag-ops`(静态 import)、`
|
|
20
|
+
* `diag-ops`(静态 import)、`surface/http.ts` / `kit-ops`(`await import(...)`,与 `surface/http.ts` 既有的
|
|
21
21
|
* "保持静态链纯净"约定一致)。**轻链模块不得静态 import 本模块**。
|
|
22
22
|
*
|
|
23
23
|
* 已有的单一真相源继续指向原处(**不复制**):
|
|
24
|
-
* L0 根解析 `ccc.ts findSerenityRoot`|L1/L2 根与枚举 `ccc-roots.ts`|宿主契约 `host/contract.ts`
|
|
25
|
-
* |CCC 配置 `ccc.ts loadSerenityConfig`|注册表 `msm
|
|
26
|
-
* |唤醒注册表 `wake
|
|
24
|
+
* L0 根解析 `kernel/ccc.ts findSerenityRoot`|L1/L2 根与枚举 `kernel/ccc-roots.ts`|宿主契约 `host/contract.ts`
|
|
25
|
+
* |CCC 配置 `kernel/ccc.ts loadSerenityConfig`|注册表 `tools/msm/service.ts`|时钟 `wake/scheduler.ts`
|
|
26
|
+
* |唤醒注册表 `wake/registry.ts`。
|
|
27
27
|
*/
|
|
28
|
-
import { type HostContractReport } from '
|
|
29
|
-
import { checkRegistryQuality, type RegistryQualityReport } from '
|
|
30
|
-
import { type WakeEntry } from '
|
|
31
|
-
import { wakeSchedulerState } from '
|
|
32
|
-
import type { JsonValue } from '
|
|
28
|
+
import { type HostContractReport } from '../host/contract.js';
|
|
29
|
+
import { checkRegistryQuality, type RegistryQualityReport } from '../tools/msm/service.js';
|
|
30
|
+
import { type WakeEntry } from '../wake/registry.js';
|
|
31
|
+
import { wakeSchedulerState } from '../wake/scheduler.js';
|
|
32
|
+
import type { JsonValue } from '../kernel/json.js';
|
|
33
33
|
export interface ContainerIdentity {
|
|
34
34
|
/** CCC 根(调用方给定;null = 不在任何 CCC 内) */
|
|
35
35
|
root: string | null;
|
|
@@ -58,7 +58,7 @@ export interface ContainerPrinciples {
|
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
60
|
* 三原则取数。
|
|
61
|
-
* ⚠️ 与 `ccc.ts` 的 P1/P2 原语是**同一实现**(`existsSync(.serenity)` / `findGitRoot`),
|
|
61
|
+
* ⚠️ 与 `kernel/ccc.ts` 的 P1/P2 原语是**同一实现**(`existsSync(.serenity)` / `findGitRoot`),
|
|
62
62
|
* 本函数只把它们组装成"一次检查"的形状;判定语义未变(P1 要求文件非空、P2 要求 `.git` 存在)。
|
|
63
63
|
*/
|
|
64
64
|
export declare function containerPrinciples(root: string | null): ContainerPrinciples;
|
|
@@ -71,7 +71,7 @@ export interface RegistryStructureReport {
|
|
|
71
71
|
issues: string[];
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
|
-
* **判据 A —— 注册表「结构完整性」**(原 `kit
|
|
74
|
+
* **判据 A —— 注册表「结构完整性」**(原 `tools/kit/service.ts checkRegistryHealth`,C5 迁入本模块)。
|
|
75
75
|
*
|
|
76
76
|
* 回答的问题:**这张表坏没坏**——文件能不能被安全解析(JSON 合法 / 顶层形状 / entry 字段类型 /
|
|
77
77
|
* name 全局唯一 / path 在根内且脚本存在)。
|
|
@@ -90,7 +90,7 @@ export interface RegistryStructureReport {
|
|
|
90
90
|
*/
|
|
91
91
|
export declare function checkRegistryHealth(root: string): RegistryStructureReport;
|
|
92
92
|
/**
|
|
93
|
-
* **判据 B —— 注册表「质量契约」**(DC-M1~M4;实现在 `msm
|
|
93
|
+
* **判据 B —— 注册表「质量契约」**(DC-M1~M4;实现在 `tools/msm/service.ts checkRegistryQuality`,
|
|
94
94
|
* 执行面 `container_admin msm check` 亦走它)。
|
|
95
95
|
*
|
|
96
96
|
* 与判据 A 的关系见 {@link checkRegistryHealth} 的对照注释——**两条判据、两个名字、两份结论**。
|
|
@@ -99,7 +99,7 @@ export declare function checkRegistryHealth(root: string): RegistryStructureRepo
|
|
|
99
99
|
export { checkRegistryQuality, type RegistryQualityReport };
|
|
100
100
|
/**
|
|
101
101
|
* 进程内时钟快照类型。
|
|
102
|
-
* ⚠️ 形状由**时钟模块自身**定义(`wake
|
|
102
|
+
* ⚠️ 形状由**时钟模块自身**定义(`wake/scheduler.ts` 未导出的 `WakeSchedulerRuntime`)——故此处
|
|
103
103
|
* 直接取其返回类型。2026-09-15:原为「唤醒调度器 ∪ autopilot」联合(两者诊断字段同规格但类型
|
|
104
104
|
* 不同);autopilot 时钟随 ACC 侧 autopilot 退场删除后,**只剩一座钟**(唤醒调度器)。
|
|
105
105
|
*/
|
|
@@ -112,7 +112,7 @@ export interface ContainerClocks {
|
|
|
112
112
|
* 时钟的进程内状态("为什么这轮没被唤起"的第一手判据)。
|
|
113
113
|
*
|
|
114
114
|
* 🔴 **只读快照,不重算**(设计红线):`armed`/`ticks`/`lastTickAt`/`lastSkipReason` 只存在于
|
|
115
|
-
* 调度器自己的模块级运行时对象里(`wake
|
|
115
|
+
* 调度器自己的模块级运行时对象里(`wake/scheduler.ts schedulerRuntime`)。任何"从配置或文件
|
|
116
116
|
* 重新推导"得到的值都会与真实调度器的状态不一致——那比没有可观测量更糟(会给出假的"一切正常")。
|
|
117
117
|
*/
|
|
118
118
|
export declare function containerClocks(): ContainerClocks;
|
|
@@ -146,7 +146,7 @@ export interface ContainerStatus {
|
|
|
146
146
|
*/
|
|
147
147
|
quality: RegistryQualityReport | null;
|
|
148
148
|
};
|
|
149
|
-
/** CCC 配置(`ccc.ts loadSerenityConfig`;无根 → null) */
|
|
149
|
+
/** CCC 配置(`kernel/ccc.ts loadSerenityConfig`;无根 → null) */
|
|
150
150
|
config: JsonValue | null;
|
|
151
151
|
/** 宿主契约(进程内唯一来源,见 `host/contract.ts hostContractReport`) */
|
|
152
152
|
hostContract: HostContractReport | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* settings
|
|
2
|
+
* surface/settings.ts — 简单配置读取(dsh 原生设置面板;v1.21 分层,**v1.47 A 案移植到 0.1.7 表单模型**)
|
|
3
3
|
*
|
|
4
4
|
* 分层决策(S142):**简单配置(开关/阈值)→ dsh 原生设置面板**;
|
|
5
5
|
* **复杂配置(账号列表)→ 宁静号高级面板**(localstore + /serenity/config)。
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* status.ts — 状态与安全模式操作(纯逻辑,零 DSH 依赖,可独立单测)
|
|
2
|
+
* surface/status.ts — 状态与安全模式操作(纯逻辑,零 DSH 依赖,可独立单测)
|
|
3
3
|
*
|
|
4
4
|
* WebUI 停靠栏的数据源:ACC 版本 / CCC 根 / safe-mode 状态 / 黑名单 / keeper 阈值 / cheap-worker 模型。
|
|
5
5
|
* setSafeMode 直接读写 .serenity-safe-on 标记(守卫实时读取,写即生效)。
|
|
6
6
|
*/
|
|
7
|
-
import { type TrajectorySummary } from '
|
|
7
|
+
import { type TrajectorySummary } from '../trajectory/core.js';
|
|
8
8
|
/**
|
|
9
9
|
* 🔴 **布局无关档**:从**正在跑的那个入口脚本**反推宿主包根,读它的 `version`。
|
|
10
10
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* usage
|
|
2
|
+
* surface/usage.ts — ACC 用量统计(**skill 加载次数** + **MSM 执行次数**;落该 CCC 的 `_tmp`)
|
|
3
3
|
*
|
|
4
4
|
* ## 所有者令(本模块的存在理由)
|
|
5
5
|
*
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* ⇒ 本模块让每个 CCC 留下一份**纯计数**账本:**用了哪个、多少次、第一次和最近一次是什么时候**。
|
|
16
16
|
* 用途 = 清理/精简的**依据**(没有人用的东西与天天在用的东西,不再靠猜)。
|
|
17
17
|
*
|
|
18
|
-
* ## 🔴 铁律(与 `cro-log.ts` 同族)
|
|
18
|
+
* ## 🔴 铁律(与 `wake/cro-log.ts` 同族)
|
|
19
19
|
*
|
|
20
20
|
* **本模块每个导出函数都不抛错**:读盘 / 解析 / 写盘失败一律返回结构化失败,
|
|
21
21
|
* 由调用方只记一行日志(或什么都不做)⇒ **统计的问题绝不影响工具执行与既有链路**。
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
* ## 🔴 重启后必须续算(所有者令)
|
|
31
31
|
*
|
|
32
32
|
* 宿主会被重启(本容器实测 24h 内 3 次)⇒ 计数**不能只活在内存里**:
|
|
33
|
-
* 每次写盘 = **读旧文件 → 在旧值上自增 → 原子写**(`tmp + rename`,与 `wake
|
|
34
|
-
* `cro-log.ts` 同款形态)⇒ **进程重启后计数自然延续**,无需额外的 flush/落盘时机。
|
|
33
|
+
* 每次写盘 = **读旧文件 → 在旧值上自增 → 原子写**(`tmp + rename`,与 `wake/registry.ts` /
|
|
34
|
+
* `wake/cro-log.ts` 同款形态)⇒ **进程重启后计数自然延续**,无需额外的 flush/落盘时机。
|
|
35
35
|
*
|
|
36
36
|
* ## 口径(两个字段,勿合并)
|
|
37
37
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* voyage
|
|
2
|
+
* surface/voyage/page.ts — `/serenity/voyage`:宁静号航行动画(**ACC 层静态资产**)
|
|
3
3
|
*
|
|
4
4
|
* 作用:把「宁静号航行动画」作为 **ACC 的一部分**随包分发,并用一条 HTTP 路由把它
|
|
5
5
|
* 交给 WebUI。当前唯一消费者 = 会话头部状态胶囊展开卡片(`SafeModePanel` 的 `sp-pop`)
|
|
@@ -39,7 +39,7 @@ export declare function resetVoyageCache(): void;
|
|
|
39
39
|
/**
|
|
40
40
|
* 注册 `/serenity/voyage`(GET)。
|
|
41
41
|
* @param opts.assetPath 覆盖资产路径(测试用;生产不传)
|
|
42
|
-
* @param opts.resolveRoot 解析本次请求所属 CCC 根(生产由
|
|
42
|
+
* @param opts.resolveRoot 解析本次请求所属 CCC 根(生产由 surface/http.ts 的 resolveWorkspace 提供)。
|
|
43
43
|
* 🔴 **2026-10-18 新增**:词表按请求所属 CCC **运行时注入**(owner 报「在别的 CCC 点开
|
|
44
44
|
* 显示的还是 home-serenity 的信息」)。不传 ⇒ 不注入(保持资产原样 = 旧行为)。
|
|
45
45
|
*/
|