@shgroup/dsh-serenity-hooks 1.73.0 → 1.74.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 +12 -11
- package/dsh.plugin.json +2 -2
- package/lib/accounts-BHQbakUh.js +33 -0
- package/lib/{ccc-CZUOq3NY.js → ccc-CyYJL4F2.js} +9 -9
- package/lib/{ccc-roots-BhoA7G-0.js → ccc-roots-B2t_aM8L.js} +4 -4
- package/lib/client.js +116 -1
- package/lib/cro/core.d.ts +1 -1
- package/lib/guard/seam.d.ts +4 -4
- package/lib/host/effect.d.ts +1 -1
- package/lib/im/bridge.d.ts +3 -3
- package/lib/im/endpoint.d.ts +2 -2
- package/lib/im/guard.d.ts +1 -1
- package/lib/im/hook.d.ts +1 -1
- package/lib/im/inbound-event.d.ts +6 -6
- package/lib/im/route.d.ts +25 -3
- package/lib/index.d.ts +5 -5
- package/lib/index.js +360 -265
- package/lib/kernel/ccc-roots.d.ts +4 -4
- package/lib/kernel/ccc.d.ts +19 -19
- package/lib/kernel/ports.d.ts +1 -1
- package/lib/{message-source-ChyVfev9.js → message-source-DCpoRHP3.js} +2 -2
- package/lib/patches/opencode-provider.d.ts +1 -1
- package/lib/{profile-BIQ2widI.js → profile-BVt84mbi.js} +59 -59
- package/lib/{route-ItCgAsnz.js → route-COOFLTpU.js} +44 -6
- package/lib/seams/guards.d.ts +14 -14
- package/lib/seams/keeper.d.ts +1 -1
- package/lib/seams/lifecycle.d.ts +2 -2
- package/lib/{settings-UcwIPJhM.js → settings-D8hlqxua.js} +40 -26
- package/lib/surface/faces.d.ts +1 -1
- package/lib/surface/http.d.ts +1 -1
- package/lib/surface/settings.d.ts +7 -7
- package/lib/surface/text.d.ts +3 -3
- package/lib/tools/config-docs.d.ts +53 -0
- package/lib/tools/container-admin.d.ts +19 -12
- package/lib/tools/msm/service.d.ts +7 -0
- package/lib/tools/trajectory-constraint-admin.d.ts +30 -0
- package/lib/trajectory/carrier.d.ts +1 -1
- package/lib/trajectory/core.d.ts +1 -1
- package/lib/trajectory/policy/profile.d.ts +36 -36
- package/lib/trajectory/policy/rules.d.ts +33 -20
- package/lib/trajectory/policy/window.d.ts +13 -13
- package/lib/trajectory/prompt.d.ts +12 -12
- package/lib/trajectory/rebuild.d.ts +2 -2
- package/lib/trajectory/session.d.ts +49 -49
- package/package.json +2 -2
- package/lib/tools/skiff-admin.d.ts +0 -30
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { f as hostSettings } from "./ccc-roots-
|
|
2
|
-
import { _ as resolveInside, g as readUtf8 } from "./ccc-
|
|
1
|
+
import { f as hostSettings } from "./ccc-roots-B2t_aM8L.js";
|
|
2
|
+
import { _ as resolveInside, g as readUtf8 } from "./ccc-CyYJL4F2.js";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -56,26 +56,26 @@ const ACC_VERSION = (() => {
|
|
|
56
56
|
*
|
|
57
57
|
* | 模块 | 管什么 |
|
|
58
58
|
* |---|---|
|
|
59
|
-
* | `./profile.ts` | **判据 + 读盘**("这是不是
|
|
59
|
+
* | `./profile.ts` | **判据 + 读盘**("这是不是 轨迹约束"、"约束面是什么") |
|
|
60
60
|
* | `./rules.ts`(本文件) | **从约束面派生出的纯函数**(白名单集合、提示词读取器、基础提示词文本) |
|
|
61
61
|
* | `../session-create.ts` | 会话创建/复用、提问、守门查询 |
|
|
62
62
|
*
|
|
63
|
-
* ⇒ 本文件**不再读 `serenity.json
|
|
64
|
-
* 它只接收一个已解析好的 `
|
|
63
|
+
* ⇒ 本文件**不再读 `serenity.json`**:`轨迹约束.roles` 整段已退役(owner 2026-10-19 重做)。
|
|
64
|
+
* 它只接收一个已解析好的 `TrajectoryConstraint`,因此**不涉及任何 IO**(除提示词文件,那是刻意懒读的)。
|
|
65
65
|
*
|
|
66
66
|
* ## S12(2026-10-19,轨迹中心重构)
|
|
67
67
|
*
|
|
68
|
-
* 原名 `role
|
|
68
|
+
* 原名 `role/轨迹约束/config.ts`。改名理由(框架 §6.3「角色不是一个概念,是一段历史」):
|
|
69
69
|
* 它派生的是**某条轨迹的约束面**,与"配置读取"无关(本文件零配置读取,见上一段)
|
|
70
70
|
* ⇒ `config` 这个名字描述的是**它曾经的样子**,`rules` 描述它**现在做的事**。
|
|
71
71
|
*/
|
|
72
72
|
/** 白名单推导 ────────────────────────────────────────────────────────────── */
|
|
73
73
|
/** 提示词来源提示串(`apply` 回执与面板共用;避免两处措辞漂) */
|
|
74
|
-
const
|
|
74
|
+
const TRAJECTORY_CONSTRAINT_HINT = "systemPromptFile hot-reloads by mtime; the rest of trajectory-constraint.json is re-read every turn";
|
|
75
75
|
/**
|
|
76
|
-
* 该
|
|
76
|
+
* 该 轨迹约束会话**可见**的工具面(tools ∪ `msm`)。
|
|
77
77
|
*
|
|
78
|
-
* 🔴 `msm` 为什么自动加入:`msms` 非空意味着这个
|
|
78
|
+
* 🔴 `msm` 为什么自动加入:`msms` 非空意味着这个 轨迹约束 **有 MSM 通道**,
|
|
79
79
|
* 而 MSM 的唯一合法调用入口是 `msm` 工具(单入口纪律)——不给它 `msm` 就等于
|
|
80
80
|
* 声明了一堆调不动的能力。⚠️ 这是**并集**,不是覆盖:`tools` 里显式写的照旧生效。
|
|
81
81
|
*/
|
|
@@ -85,12 +85,12 @@ function profileToolWhitelist(profile) {
|
|
|
85
85
|
if ((profile?.msms?.length ?? 0) > 0) out.add("msm");
|
|
86
86
|
return out;
|
|
87
87
|
}
|
|
88
|
-
/** 该
|
|
88
|
+
/** 该 轨迹约束会话可 exec 的 MSM 白名单(`msm` 工具内部再过滤;独立于工具白名单) */
|
|
89
89
|
function profileMsmWhitelist(profile) {
|
|
90
90
|
return new Set(profile?.msms ?? []);
|
|
91
91
|
}
|
|
92
|
-
/** 轨迹纪律参与项(`profile` 缺省已在 `
|
|
93
|
-
function
|
|
92
|
+
/** 轨迹纪律参与项(`profile` 缺省已在 `readTrajectoryConstraintAt` 归一为全 true) */
|
|
93
|
+
function constraintCognitiveTrajectory(profile) {
|
|
94
94
|
return {
|
|
95
95
|
session: profile?.cognitiveTrajectory.session === true,
|
|
96
96
|
keeper: profile?.cognitiveTrajectory.keeper === true,
|
|
@@ -105,18 +105,32 @@ function profileCognitiveTrajectory(profile) {
|
|
|
105
105
|
*
|
|
106
106
|
* ⚠️ 热路径**不用**它(那会用每次工具调用去读盘)——热路径用 `createProfilePromptReader`。
|
|
107
107
|
*/
|
|
108
|
-
function
|
|
108
|
+
function resolveConstraintSystemPrompt(root, profile) {
|
|
109
109
|
if (!profile) return "";
|
|
110
110
|
const file = profile.systemPromptFile?.trim();
|
|
111
111
|
if (file) {
|
|
112
112
|
const abs = resolveInside(root, file);
|
|
113
|
-
if (!existsSync(abs)) throw new Error(
|
|
113
|
+
if (!existsSync(abs)) throw new Error(`轨迹约束 "${profile.code}": systemPromptFile "${file}" not found (resolved: ${abs})`);
|
|
114
114
|
return readUtf8(abs).trim();
|
|
115
115
|
}
|
|
116
116
|
return profile.systemPrompt ?? "";
|
|
117
117
|
}
|
|
118
|
-
/**
|
|
119
|
-
|
|
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";
|
|
120
134
|
if (profile?.systemPromptFile?.trim()) return "file";
|
|
121
135
|
if (profile?.systemPrompt?.trim()) return "inline";
|
|
122
136
|
return "none";
|
|
@@ -144,7 +158,7 @@ function createProfilePromptReader(root, profile) {
|
|
|
144
158
|
try {
|
|
145
159
|
abs = resolveInside(root, file);
|
|
146
160
|
} catch (err) {
|
|
147
|
-
console.warn(`[serenity-hooks] ✗
|
|
161
|
+
console.warn(`[serenity-hooks] ✗ 轨迹约束提示词路径非法(${file}): ${String(err?.message ?? err)}`);
|
|
148
162
|
}
|
|
149
163
|
if (abs === null) return () => "";
|
|
150
164
|
const absPath = abs;
|
|
@@ -162,19 +176,19 @@ function createProfilePromptReader(root, profile) {
|
|
|
162
176
|
cachedSize = st.size;
|
|
163
177
|
cachedText = text;
|
|
164
178
|
warned = false;
|
|
165
|
-
if (isReload) console.info(`[serenity-hooks] ↻
|
|
179
|
+
if (isReload) console.info(`[serenity-hooks] ↻ 轨迹约束提示词已热重载(${file},${text.length} 字符)`);
|
|
166
180
|
return text;
|
|
167
181
|
} catch (err) {
|
|
168
182
|
if (!warned) {
|
|
169
183
|
warned = true;
|
|
170
|
-
console.warn(`[serenity-hooks] ✗
|
|
184
|
+
console.warn(`[serenity-hooks] ✗ 轨迹约束提示词读取失败(${file})——沿用上次成功内容: ${String(err?.message ?? err)}`);
|
|
171
185
|
}
|
|
172
186
|
return cachedText;
|
|
173
187
|
}
|
|
174
188
|
};
|
|
175
189
|
}
|
|
176
190
|
/**
|
|
177
|
-
*
|
|
191
|
+
* 轨迹约束 基础提示词(dsp 只给这部分;CCC 的 `systemPrompt` 段由调用方拼接)。
|
|
178
192
|
*
|
|
179
193
|
* ## 🔴 为什么**不再枚举**清单(owner 2026-10-19 令)
|
|
180
194
|
*
|
|
@@ -190,11 +204,11 @@ function createProfilePromptReader(root, profile) {
|
|
|
190
204
|
*
|
|
191
205
|
* ⇒ 现在只声明**边界与发现路径**,不抄内容:清单在哪,怎么取,越界会怎样。
|
|
192
206
|
*
|
|
193
|
-
* @param code
|
|
207
|
+
* @param code 轨迹编码(轨迹约束的身份 —— 不是"角色名",那个概念已退役)
|
|
194
208
|
*/
|
|
195
|
-
function
|
|
209
|
+
function buildTrajectoryBasePrompt(code) {
|
|
196
210
|
return [
|
|
197
|
-
"=== Serenity
|
|
211
|
+
"=== Serenity 轨迹约束 ===",
|
|
198
212
|
`Cognitive trajectory: ${code} (this is the trajectory you ARE, not a role you play)`,
|
|
199
213
|
"Your capability boundary is enforced mechanically by this CCC — anything outside it is rejected at the call site, not by your judgment.",
|
|
200
214
|
"Discover your surface, do not memorize it (both are generated from the live config on every turn):",
|
|
@@ -233,7 +247,7 @@ function defaultSimpleSettings() {
|
|
|
233
247
|
* `meta.volatile` **无条件**包 `createVolatile(value)`,且该分支排在"缺省值回退"那一段
|
|
234
248
|
* **之前** ⇒ **"用户没设过"的字段也是恒真对象**(`.get()` 返回 `undefined`),**不是 `undefined`**。
|
|
235
249
|
*
|
|
236
|
-
* ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 /
|
|
250
|
+
* ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 / 轨迹约束 会被
|
|
237
251
|
* **无意打开**(比"面板不显示"更坏:静默改变对外暴露面)。
|
|
238
252
|
*
|
|
239
253
|
* ## 为什么用结构判定
|
|
@@ -319,7 +333,7 @@ function simpleSettingsFromConfig(config) {
|
|
|
319
333
|
/** 运行时源(`registerSettingsSection` 装配:读本插件 fiber 的**当前** Config) */
|
|
320
334
|
let simpleSource = null;
|
|
321
335
|
/**
|
|
322
|
-
* 读取当前简单配置(各功能 gateway/rebuild
|
|
336
|
+
* 读取当前简单配置(各功能 gateway/rebuild/轨迹约束/… 的启动与运行时判断都经此)。
|
|
323
337
|
*
|
|
324
338
|
* 源 = 本插件 fiber 的 `config`(宿主在配置变更时走 `fiber.update()` ⇒ `config` 始终最新,
|
|
325
339
|
* **不必**依赖 apply 重跑,也不必缓存)。
|
|
@@ -351,4 +365,4 @@ function registerSettingsSection(ctx, config) {
|
|
|
351
365
|
} catch {}
|
|
352
366
|
}
|
|
353
367
|
//#endregion
|
|
354
|
-
export {
|
|
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 };
|
package/lib/surface/faces.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ## 现状 → 本模块要收掉什么(取证:`…/acc-c4c5-current-state.md` §①/§③ 3.1)
|
|
5
5
|
*
|
|
6
|
-
* 插件自己 spawn 了**三个** node:http 监听器(B 3081 网关 / C 3082 微信发送 / D 3099
|
|
6
|
+
* 插件自己 spawn 了**三个** node:http 监听器(B 3081 网关 / C 3082 微信发送 / D 3099 轨迹约束调试)。
|
|
7
7
|
* ⚠️ **2026-10-15 变更**:原为四个(含 **E 3100 ACP+问答**),E 已随 owner「砍掉外部问答」令**整面退役**
|
|
8
8
|
* ⇒ 仍保有面 B/C/D 三个;本段保留"当年是四个"的记述,因为下方"两套拆卸"的病史由它解释。
|
|
9
9
|
* 此前四处各写一份同款样板:`createServer` + `server.once('error')` +
|
package/lib/surface/http.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ interface StatusApiRegistration {
|
|
|
39
39
|
*
|
|
40
40
|
* 🔴🔴 **2026-10-18 修缺陷(owner 报「在别的 CCC 点开显示的还是 home-serenity 的信息」)**:
|
|
41
41
|
* 旧实现第 ③ 步是「**遍历 live sessions 找第一个 CCC 会话的 cwd**」—— 那是**跨工作区猜测**:
|
|
42
|
-
* 只要 sessionId 缺失或那条会话查不到(ACP
|
|
42
|
+
* 只要 sessionId 缺失或那条会话查不到(ACP/轨迹约束会话、尚未 materialize 的会话、远程访问),
|
|
43
43
|
* 它就会**静默返回另一个 CCC 的路径**,于是面板把**别人的轨迹/技能/安全模式**当成你的显示出来。
|
|
44
44
|
* 这比"显示未激活"危险得多 —— 用户会据此做判断(以为自己的 safe-mode 开着、以为那是自己的轨迹)。
|
|
45
45
|
*
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* ## 为什么旧扁平键**原样保留**(这是本文件最容易被后人改错的地方)
|
|
16
16
|
*
|
|
17
17
|
* 旧模型把简单配置存成 `~/.dsh/settings.yaml` 的 `serenity-hooks:` 段,字段名是**扁平**的
|
|
18
|
-
* (`gatewayEnabled` / `
|
|
18
|
+
* (`gatewayEnabled` / `轨迹约束Enabled` / …)。新模型的命名空间**逐字相同**
|
|
19
19
|
* (`serenity-hooks`),而宿主的 `SettingsForms.importLegacyDocument()` 会把该段
|
|
20
20
|
* **按同名条目导入**;schemastery 的 object 解析在非 strict 模式下 `merge(result, data)`
|
|
21
21
|
* ⇒ **未声明的键会被保留而非报错**。⇒ 只要这些扁平键**仍在本插件 Config 里声明**,
|
|
@@ -59,12 +59,12 @@ interface SimpleConfigFragment {
|
|
|
59
59
|
rebuild?: {
|
|
60
60
|
thresholdK?: number;
|
|
61
61
|
};
|
|
62
|
-
/** F4
|
|
63
|
-
|
|
62
|
+
/** F4 轨迹约束(实验性):调试服务启停(人工) */
|
|
63
|
+
轨迹约束?: {
|
|
64
64
|
enabled?: boolean;
|
|
65
65
|
debugPort?: number;
|
|
66
66
|
};
|
|
67
|
-
/** F4
|
|
67
|
+
/** F4 轨迹约束(实验性):调试服务启停(人工) */
|
|
68
68
|
/** diagram(实验性 demo;2026-09-29 owner 具名令 D104):标签源码 → 进程内出图(不借浏览器) */
|
|
69
69
|
diagram?: {
|
|
70
70
|
enabled?: boolean;
|
|
@@ -103,7 +103,7 @@ interface SerenitySimpleSettings {
|
|
|
103
103
|
* `parseDsl → renderDsl(SVG + 四项体检)→ resvg-wasm 光栅化(内置字体)→ PNG`,
|
|
104
104
|
* 产物落 CCC `_tmp/diagram/` 且**图片块进对话**(**不借浏览器、不截图**)。
|
|
105
105
|
* **缺省关**:owner 口中的"demo 功能" ⇒ 关时**不注册**(工具不进模型工具清单:
|
|
106
|
-
* 零 token、不会被误调),同
|
|
106
|
+
* 零 token、不会被误调),同 `轨迹约束` 的"未开启零资源占用"口径。
|
|
107
107
|
* 依赖面:随包分发 3.8 MB 内置字体(`assets/diagram/`,wasm 对系统字体不可见)。 */
|
|
108
108
|
diagramEnabled: boolean;
|
|
109
109
|
/** **human-channel(人机信道)机器级配置**(2026-09-26 owner 定案;P0-1 立 schema + 投影)。
|
|
@@ -128,7 +128,7 @@ interface VolatileRef<T> {
|
|
|
128
128
|
* `meta.volatile` **无条件**包 `createVolatile(value)`,且该分支排在"缺省值回退"那一段
|
|
129
129
|
* **之前** ⇒ **"用户没设过"的字段也是恒真对象**(`.get()` 返回 `undefined`),**不是 `undefined`**。
|
|
130
130
|
*
|
|
131
|
-
* ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 /
|
|
131
|
+
* ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 / 轨迹约束 会被
|
|
132
132
|
* **无意打开**(比"面板不显示"更坏:静默改变对外暴露面)。
|
|
133
133
|
*
|
|
134
134
|
* ## 为什么用结构判定
|
|
@@ -220,7 +220,7 @@ export declare function humanChannelSettingsFromConfig(fragment: HumanChannelCon
|
|
|
220
220
|
*/
|
|
221
221
|
export declare function simpleSettingsFromConfig(config: SimpleConfigFragment): SerenitySimpleSettings;
|
|
222
222
|
/**
|
|
223
|
-
* 读取当前简单配置(各功能 gateway/rebuild
|
|
223
|
+
* 读取当前简单配置(各功能 gateway/rebuild/轨迹约束/… 的启动与运行时判断都经此)。
|
|
224
224
|
*
|
|
225
225
|
* 源 = 本插件 fiber 的 `config`(宿主在配置变更时走 `fiber.update()` ⇒ `config` 始终最新,
|
|
226
226
|
* **不必**依赖 apply 重跑,也不必缓存)。
|
package/lib/surface/text.d.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ## 为什么它从调试面里拆出来(S11,2026-10-19,轨迹中心重构)
|
|
5
5
|
*
|
|
6
|
-
* 拆之前它是 `role
|
|
6
|
+
* 拆之前它是 `role/轨迹约束/debug.ts` 的**后半段**,而那份文件是**混装件**:
|
|
7
7
|
* · 前半段 = **面 D** 的 HTTP 调试页(自起 listener,端口见 `kernel/ports.ts`)
|
|
8
8
|
* · 后半段 = **通用文本清洗**(本文件,已随重构搬到 `surface/text.ts`)
|
|
9
9
|
*
|
|
10
|
-
* 判据(实测,不是猜):本文件的导出被**两个与
|
|
10
|
+
* 判据(实测,不是猜):本文件的导出被**两个与 轨迹约束 无关的模块**直接使用 ——
|
|
11
11
|
* `im/bridge.ts`(微信桥:回复前剥 think)与 `guard/seam.ts`(输出守卫:检测前剥 think)。
|
|
12
12
|
* ⇒ 它不属于"某一条轨迹的调试面",它是**任何输出面都会用的清洗**。
|
|
13
13
|
*
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* **hideThink=true(v1.26.4,public 口)→ 直接移除**(思考过程对普通用户不展示)
|
|
29
29
|
* @param hideThink 为 true 时 `<think>` 内容完全不渲染(public 问答页体验)
|
|
30
30
|
*/
|
|
31
|
-
export declare function
|
|
31
|
+
export declare function renderTrajectoryMarkdown(raw: string, hideThink?: boolean): string;
|
|
32
32
|
/**
|
|
33
33
|
* 剥离 `<think>…</think>` 块(v1.27.1,微信桥用户反馈:"过微信桥回复给用户的消息,
|
|
34
34
|
* 要去掉 think 标签,用户不应看到")。复用 extractThinkBlocks 状态机(v1.26.8,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tools/config-docs.ts — `container_admin config docs` 的**两层菜单**(P3,2026-10-19)
|
|
3
|
+
*
|
|
4
|
+
* ## owner 原话(本文件存在的全部理由,逐字)
|
|
5
|
+
*
|
|
6
|
+
* > 「im 的整体配置方法要在 ACC 级有方法能交代我们的**说明类内容**越来越多了,
|
|
7
|
+
* > 做个**两层菜单**来承载吧,admin 里面搞两个方法,一个是**使用说明目录**,
|
|
8
|
+
* > 一个是**使用说明详情(按章节获得)**」
|
|
9
|
+
*
|
|
10
|
+
* ⇒ 第一层 `docs`(无参)= **目录**:只列"有哪几章、每章一句话、去哪个子命令看详情"。
|
|
11
|
+
* 第二层 `docs <chapter>` = **详情**:该章全文。
|
|
12
|
+
*
|
|
13
|
+
* ## 为什么是"目录 + 按章详情"而不是一坨长文(R↓,这不是排版偏好)
|
|
14
|
+
*
|
|
15
|
+
* 说明类内容已经长到 `ccc-config` 单份约 20 KB。整份塞进一次工具返回有两个真实代价:
|
|
16
|
+
* ① **截断** —— 宿主对工具结果有预算(超了走 head/tail + 落盘)⇒ 用户拿到的**不是全文**,
|
|
17
|
+
* 而是它以为的全文(本轨 A59 记过这一格)。
|
|
18
|
+
* ② **不可发现** —— 一坨长文里"我该看哪一段"要靠通读才知道;目录让模型**先选后读**。
|
|
19
|
+
* ⇒ 判据:**目录只索引,不复制**。每章正文的唯一真相源仍在它自己的产出函数里
|
|
20
|
+
* (本章 = `ccc-config` 的对应段 / `role guide` 全文),本文件只做**切分与路由**。
|
|
21
|
+
*
|
|
22
|
+
* ## 归属(D23)
|
|
23
|
+
* 机制在 ACC(有哪几章、怎么切、怎么路由);**章目内容**部分来自 ACC 自己的 guide
|
|
24
|
+
* (那些是机制说明),部分来自 CCC 的配置现状(运行时读取)。
|
|
25
|
+
*
|
|
26
|
+
* ## 与 `msm catalog` 的分工(两个目录不是冗余)
|
|
27
|
+
* - `msm catalog` = **工具/能力**目录("去哪找功能说明")
|
|
28
|
+
* - `config docs` = **配置**目录("这个键怎么填、填错会怎样")
|
|
29
|
+
* 二者在 ④ 章互指,但**不复制**对方的内容。
|
|
30
|
+
*/
|
|
31
|
+
/** 一个章节 = 菜单项(第一层)+ 正文(第二层) */
|
|
32
|
+
export interface DocsChapter {
|
|
33
|
+
/** 章节 id(稳定标识:`config docs <id>`;**英文短名**,便于逐字敲出来) */
|
|
34
|
+
id: string;
|
|
35
|
+
/** 菜单里的一句话(用户据此决定要不要读下去) */
|
|
36
|
+
summary: string;
|
|
37
|
+
/** 该章正文(第二层才返回;**惰性**——目录不触发它) */
|
|
38
|
+
render: () => string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 渲染**目录**(第一层)。
|
|
42
|
+
*
|
|
43
|
+
* 🔴 只调 `summary`,**绝不调 `render`** —— 目录若触发正文,就退化成"整份长文"。
|
|
44
|
+
* 这条不是性能优化,是**判据**:它保证 `docs` 的输出大小与章数成正比,与正文长度无关。
|
|
45
|
+
*/
|
|
46
|
+
export declare function renderDocsIndex(chapters: readonly DocsChapter[]): string;
|
|
47
|
+
/**
|
|
48
|
+
* 解析一个章节参数(第二层)。
|
|
49
|
+
*
|
|
50
|
+
* 不存在 ⇒ 返回 `null`,由调用方给出**带候选清单**的拒绝 ——
|
|
51
|
+
* "不知道这个章节" 与 "这一章是空的" 必须可区分(否则用户以为功能没有)。
|
|
52
|
+
*/
|
|
53
|
+
export declare function findDocsChapter(chapters: readonly DocsChapter[], id: string): DocsChapter | null;
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* container-admin.ts — container_admin(v1.30:机务舱——容器管理面统一入口)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* · skiff_admin(F4 Skiff 认知子集角色:guide/validate/apply/list)
|
|
6
|
-
* · acc_msm 管理面(register/deregister/check/guide/catalog/ccc-config)
|
|
7
|
-
* —— msm 工具只管执行+发现;一切「管理容器」的操作归本工具
|
|
4
|
+
* ## 域结构(2026-10-19 owner 两条令后:**两域**)
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* | 域 | 管什么 |
|
|
7
|
+
* |---|---|
|
|
8
|
+
* | `config` | **本 CCC 的全部配置面**:说明目录/按章详情 + 轨迹约束(原 `role` 域,已并入) |
|
|
9
|
+
* | `msm` | MSM 注册表管理 + 手册(register/deregister/check/guide/catalog/ccc-config) |
|
|
10
|
+
*
|
|
11
|
+
* 🔴 **`role` 域已并入 `config`**(owner 2026-10-19 裁「1 并入」)。为什么不是三个平行域:
|
|
12
|
+
* `role` 管的本来就是"某条轨迹的约束面"——**一组配置**,与 `config` 是同一抽象层的两半。
|
|
13
|
+
* 分着放的代价是:用户在 `config` 里看不到"轨迹怎么约束",得先知道 `role` 这个词存在。
|
|
14
|
+
* ⚠️ **旧 `role` 域仍被接受**(过渡别名,指向同一实现)——存量 CCC 的文档/脚本里到处是
|
|
15
|
+
* `container_admin role validate`(实测本仓 30 处),硬切会让它们全部变成"未知域"。
|
|
16
|
+
* 别名**只保留读取路径**:schema 的 `enum` 只列新名,文案只教新名。
|
|
12
17
|
*
|
|
13
|
-
*
|
|
18
|
+
* ## 三个动作名为什么是这三个(P3 的实现形态)
|
|
19
|
+
* - `config docs`(无参)→ **使用说明目录**(第一层;只索引不复制)
|
|
20
|
+
* - `config docs <chapter>` → **使用说明详情**(第二层;按章获得)
|
|
21
|
+
* - `config view` → 配置**现值**(原来是唯一动作;现在是"看当前填了什么")
|
|
14
22
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* `ctx` 现**无消费者**,工厂形态保留只是为了让装配点签名不再变动(见 index.ts 注册处)。
|
|
23
|
+
* 归属(D23):**机制在 ACC**(有哪几章、怎么路由、怎么校验),**内容在 CCC**(填什么)。
|
|
24
|
+
* 语义(用户拍板"轨迹约束_admin 及系列 admin 合并成 container_admin"):
|
|
25
|
+
* container = CCC(具体认知容器);admin = 管理操作。
|
|
19
26
|
*/
|
|
20
27
|
import type { Context } from 'cordis';
|
|
21
28
|
export declare function createContainerAdminTool(ctx: Context): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
@@ -8,6 +8,13 @@ import type { JsonValue } from '../../kernel/json.js';
|
|
|
8
8
|
type MsmAction = 'list' | 'exec' | 'register' | 'deregister' | 'check' | 'guide' | 'ccc-config' | 'catalog';
|
|
9
9
|
export declare const MSM_ACTIONS: readonly MsmAction[];
|
|
10
10
|
export declare const MSM_GUIDE = "MSM Development Manual (Mech & Semi-Mech framework)\n\n## What it is\nMSM = the executable-unit layer. Mech is pure TS with zero LLM reasoning; Semi-Mech is a TS framework + LLM decision points.\nMSM is ACC's deterministic executable-unit layer \u2014 all shell/exec operations go through MSM and cannot be bypassed.\n\n## Registering a new MSM (container_admin msm register)\n1. Write the script under <skill>/scripts/ (runnable via tsx; must include a main() CLI guard with an import.meta.url check)\n2. container_admin msm register --domain msm --action register <name> --skill <s> --path <script path relative to root> --category <mech|semi-mech> --description <desc>\n3. Auto-writes mech-registry.json (preserving the original format) + git commit (commits only the registry file)\n4. Validation: path must be inside root, script must exist, name must be globally unique\n\n## flag schema (v1)\nflags is a new-style object array for parameter validation and path-escape guarding:\n [{\"name\":\"output\",\"type\":\"string\",\"description\":\"output path\"},\n {\"name\":\"target\",\"type\":\"path\",\"description\":\"operation target (type:path enables in-root validation + symlink defense)\"},\n {\"name\":\"force\",\"type\":\"boolean\",\"description\":\"force mode\",\"default\":false}]\n- {name, type} format \u2014 new style, type:\"path\" enables the path-escape guard\n- {flag, description} format \u2014 old style, CLI flag description string\n- On registration, flags are passed via container_admin msm register --flags '<json>'\n\n## Script conventions\n- Top-of-file documentation: purpose / usage / exit codes\n- Exit codes: 0 success / 1 user / 2 system / 3 operator (per ACC protocol classification)\n- main() CLI guard (DC-M2): the script must start with\n if (import.meta.url === `file://${process.argv[1]}`) { main() }\n or an equivalent isMain / require.main === check \u2014 so vitest imports never trigger top-level code\n- A paired .test.ts or .spec.ts (DC-M1, vitest)\n- Business subprocess environment: SERENITY_ROOT / SERENITY_CCC / SERENITY_VERSION injected\n\n## Interaction & confirmation conventions (no blocking confirmation)\n- MSMs run in **user-less subprocesses** (spawn/execFile, 600s timeout) \u2014 **never** use\n readline / prompt / process.stdin etc. to block waiting for user input (it would hang until killed by the timeout)\n- When a second confirmation is needed: **directly return the confirmation request** (list the operation to perform and its impact + how to retry with the confirmation flag),\n returning a non-zero exit code (or an explicit hint)\n- After the agent confirms, **re-invoke the MSM with the confirmation flag** (e.g. --confirm / --yes / --force) to retry\n- Standard mode (two-phase):\n 1. First call: destructive/confirmation-requiring operation detected and no confirmation flag present \u2192 output the confirmation request (list operation/impact/rollback),\n exit non-zero (e.g. 1 user), **perform no changes**\n 2. After the agent evaluates, re-invoke: with the confirmation flag \u2192 execute and output the result\n- Applies to: delete/overwrite/push/batch operations and other irreversible or wide-impact operations\n- Anti-example (forbidden): readline waiting for user input, process.stdin.on('data') blocking waits\n (MSMs have no stdin interaction channel \u2014 they hang until the 600s timeout)\n\n## Quality checks (container_admin msm check, DC-M1~M4)\nDC-M1 has .test.ts/.spec.ts; DC-M2 has a main() guard (function main( / isMain / require.main === / import.meta.url);\nDC-M3 bidirectional: scripts unregistered + registry references missing scripts; DC-M4 path-type flags marked type:\"path\"\n\n## Self-description (protocol flags, first argument only)\nmsm(\"<name>\", [\"--list\"]) \u2014 list all MSMs\nmsm(\"<name>\", [\"--schema\", \"<n>\"]) \u2014 view a specific MSM's parameter schema\nmsm(\"<name>\", [\"--format=json\"]) \u2014 JSON output mode (remaining args passed through losslessly)\n";
|
|
11
|
+
/**
|
|
12
|
+
* CCC 配置参考(对齐 osp ccc-config action)。
|
|
13
|
+
*
|
|
14
|
+
* 🔴 **2026-10-19 起导出**:`container_admin config docs config` 的两层菜单把它作为
|
|
15
|
+
* 「config」章正文返回。**不另写一份**——同一段说明有第二份副本就会漂(本轨反复踩过)。
|
|
16
|
+
*/
|
|
17
|
+
export declare const CCC_CONFIG_REFERENCE = "\u2550\u2550\u2550 CCC Configuration Reference \u2550\u2550\u2550\n\nCCC-level features are configured in .opencode/serenity.json.\nBelow are all available configuration sections.\n\n\u2500\u2500 1. cheap-worker.models \u2500\u2500\ncheap-worker\uFF08\u6742\u5DE5\uFF09\u5DE5\u5177\u53EF\u7528\u6A21\u578B\u767D\u540D\u5355\uFF08provider/model \u5217\u8868\uFF09\uFF1B\u672A\u914D\u7F6E\u65F6 cheap-worker \u62A5\u9519\u8981\u6C42\u914D\u7F6E\u3002\n\u7F3A\u7701\u6A21\u578B = models[0]\uFF08\u53EF\u7528 cheap-worker.defaultModel \u6307\u5B9A\uFF0C\u5FC5\u987B \u2208 models\uFF09\u3002\n**CCC \u5E94\u628A\u8BE5\u767D\u540D\u5355\u6307\u5411\u4F4E\u6210\u672C\u6A21\u578B**\u2014\u2014cheap-worker \u662F\u6279\u91CF\u6267\u884C\u901A\u9053\u3002\n\uD83D\uDD34 \u6A21\u578B**\u4E0D\u53EF\u7531\u8C03\u7528\u65B9\u6307\u5B9A**\uFF08\u5DE5\u5177\u65E0 model \u53C2\u6570\uFF09\u21D2 \u672C\u767D\u540D\u5355\u662F\u6A21\u578B\u9009\u62E9\u7684\u552F\u4E00\u6765\u6E90\u3002\n\n Behaviour\uFF082026-10-14\uFF1Abackground \u6A21\u5F0F\u5DF2\u5220\u9664\uFF0C\u53EA\u5269\u524D\u53F0\u59D4\u6D3E\uFF09:\n cheap-worker(task, [label])\uFF1A\u4E00\u6B21\u524D\u53F0\u59D4\u6D3E\uFF08\u5BBF\u4E3B\u59D4\u6D3E\u670D\u52A1 ctx.subagents.start(\"spawn\") +\n agentOptions \u6CE8\u5165\u6A21\u578B\uFF09\u2014\u2014\u4E0D\u5FAA\u73AF\u3001\u4E0D\u6821\u9A8C\u5B8C\u6210\u7801\u3001\u4E0D\u5199\u8FDB\u5EA6\u6587\u4EF6\uFF1B\u8FD4\u56DE\u5B50 agent \u6700\u7EC8\u6587\u672C\u3002\n cheap-worker(jobs=[{task,label?},...])\uFF1A\u5E76\u884C\u6247\u51FA\uFF0C\u4E0A\u9650 10\uFF08\u786C\u7F16\u7801\uFF09\uFF0C\u7ED3\u679C\u4FDD\u6301\u4F20\u5165\u987A\u5E8F\u3002\n \u21D2 **\u65E0 background \u6A21\u5F0F\u3001\u65E0\u8FDB\u5EA6\u6587\u4EF6\u3001\u65E0\u7EED\u8DD1**\uFF08owner 2026-10-14 \u4EE4\u300C\u5220\u9664\u540E\u53F0\u8FD0\u884C\u80FD\u529B\u300D\uFF09\u3002\n\n Old key\uFF08\u517C\u5BB9\uFF09:\n \u8BFB\u4E0D\u5230 \"cheap-worker\" \u65F6**\u56DE\u843D\u5230\u65E7\u952E \"handyman\"**\uFF08\u540C\u5F62\u72B6\uFF1Amodels / defaultModel\uFF09\u3002\n \u65E7\u952E\u91CC\u7684 \"maxRounds\"\uFF0F\"maxParallel\" \u5DF2\u968F background \u9000\u573A\uFF0C\u7559\u7740\u65E0\u5BB3\u4F46\u4E0D\u518D\u751F\u6548\u3002\n\n Config:\n { \"cheap-worker\": { \"models\": [\"provider/model-name\"] } }\n\n Example:\n { \"cheap-worker\": { \"models\": [\"minimax-cn-coding-plan/MiniMax-M3\"], \"defaultModel\": \"minimax-cn-coding-plan/MiniMax-M3\" } }\n\n\u2500\u2500 2. sessionKeeper.threshold / sessionKeeper.sessionMdMaxKB \u2500\u2500\ncognitive-trajectory-assistant\uFF08COGNITIVE-TRAJECTORY-ASSISTANT \u00B7 CHECKPOINT\uFF09\u63D0\u9192\u673A\u5236\u7684\u79EF\u5206\u9608\u503C\uFF08\u975E headless \u4E3B agent\uFF09\u3002\n\u6309\u5DE5\u5177\u8C03\u7528\u52A0\u6743 + \u8017\u65F6\u8BA1\u5206\uFF1B\u8FBE\u5230\u9608\u503C\u6CE8\u5165\u63D0\u9192\uFF0C\u8981\u6C42\u6A21\u578B\u56DE\u590D ACK \u7801\u3002\n\n Config:\n { \"sessionKeeper\": { \"threshold\": 150, \"sessionMdMaxKB\": 200 } }\n\n \u8BA1\u5206\uFF1Awrite/edit = 3\uFF0Ctask = 10\uFF0Cread/grep/glob/msm \u7B49 = 1\uFF0C\u65F6\u95F4 = 1/\u5206\u949F\n \u9ED8\u8BA4\uFF1A150\n\n \u25B8 sessionMdMaxKB\uFF08v1.31.1 \u65B0\u589E\uFF0Cv1.31.2 \u7F3A\u7701 100\u2192200\uFF09\uFF1A**\u6D3B\u8DC3 SESSION.md \u4F53\u79EF\u4E0A\u9650\uFF08KB\uFF0C\u7F3A\u7701 200\uFF1B0 = \u5173\u95ED\uFF09**\u3002\n \u8D85\u8FC7 \u2192 \u6CE8\u5165 [COGNITIVE-TRAJECTORY-ASSISTANT \u00B7 LOGBOOK COMPACTION] \u63D0\u9192\uFF1A\u6682\u505C\u5F53\u524D\u5DE5\u4F5C \u2192\n \u52A0\u8F7D eap\uFF08praxis eap\uFF09\u2192 \u6309 EAP \u5206\u5C42\u9AA8\u67B6\u91CD\u5199 SESSION.md\u3002\u56DB\u6761\u539F\u5219\uFF08ACC \u5185\u5D4C\uFF0C\u968F\u63D0\u9192\u7ED9\u51FA\uFF09\uFF1A\n \u2460 \u4FDD\u7559 EAP \u5206\u5C42\u9AA8\u67B6\uFF08\u4E0D\u538B\u6210\u65F6\u95F4\u6D41\u6C34\u8D26\uFF09\u2461 \u5185\u5BB9\u53EF\u5916\u79FB\u5230 references \u6587\u4EF6\n \uFF08AGENT_SESSIONS/<\u4F1A\u8BDD>/references/*.md\uFF0CSESSION.md \u7559\u94FE\u63A5\uFF09\u2462 \u5141\u8BB8\u6574\u5408/\u5220\u9664\u4E0D\u91CD\u8981\u4E8B\u9879\n \uFF08\u88AB\u53D6\u4EE3\u7684\u51B3\u7B56\u3001\u5DF2\u89E3\u51B3\u95EE\u9898\u3001\u4E2D\u95F4\u6001\uFF09\u2463 \u81EA\u4E3B\u88C1\u91CF\u6743\u5145\u5206\u5141\u8BB8\uFF08\u552F\u4E00\u786C\u8981\u6C42 = \u9AA8\u67B6 +\n \u672A\u51B3\u9879/\u51B3\u7B56\u7406\u7531/\u4E0B\u4E00\u6B65\u4ECD\u53EF\u91CD\u5EFA\uFF09\u3002\u8D85\u9650\u6BCF\u8F6E\u63D0\u9192\uFF1B\u8FDE\u7EED 3 \u8F6E\u672A\u91CD\u5199 \u2192 \u5347\u7EA7\u5F3A\u5236\u8BED\u6C14\uFF1B\n \u6587\u4EF6\u56DE\u5230\u9650\u5185 \u2192 \u81EA\u52A8\u505C\u6B62\uFF08\u81EA\u6108\uFF09\u3002\u8F68\u8FF9\u63D0\u9192**\u4E0D\u673A\u68B0\u963B\u65AD\u5DE5\u4F5C**\uFF08\u4E0E rebuild \u63D0\u9192\u540C\u65CF\uFF09\u3002\n\n\u2500\u2500 3. localstore.gitTrack \u2500\u2500\nlocalstore.json \u7684 git \u7B56\u7565\uFF1Aallow \u53EF\u63D0\u4EA4 / deny \u7981\u63D0\u4EA4\uFF08\u9ED8\u8BA4 deny\uFF09\u3002\ndeny \u65F6\u5199\u5165\u81EA\u52A8\u786E\u4FDD .gitignore \u542B\u8BE5\u6587\u4EF6\uFF08\u7269\u7406\u4FDD\u8BC1\uFF09\uFF0Ccontainer_git commit \u4F1A\u68C0\u67E5\u62D2\u7EDD\u3002\n\n Config:\n { \"localstore\": { \"gitTrack\": \"allow\" } }\n\n\u2500\u2500 4. hooks.autoRestoreSession \u2500\u2500\n\u4F1A\u8BDD\u81EA\u52A8\u6062\u590D\uFF08\u9ED8\u8BA4 true\uFF1B\u53D7 events \u95E8\u63A7\u2014\u2014\u4EC5\u6839\u4F1A\u8BDD + \u5DF2\u6709\u5BF9\u8BDD\u5386\u53F2\u624D\u6062\u590D\uFF09\u3002\n\n Config:\n { \"hooks\": { \"autoRestoreSession\": false } }\n\n\u2500\u2500 5. safeMode\uFF08\u9ED1\u540D\u5355 \uFF0B \u5F00\u542F\u671F\u95F4\u9690\u85CF\u7684\u5DE5\u5177\uFF09\u2500\u2500\nsafe-mode \u7531 WebUI \u5F00\u5173\u63A7\u5236\uFF08\u5199 .serenity-safe-on \u6807\u8BB0\uFF09\uFF1B\u9ED1\u540D\u5355\u8DEF\u5F84\u53D7 guards seam \u62E6\u622A\u3002\n\n \u25B8 blacklist\uFF1A\u5199\u8DEF\u5F84\u9ED1\u540D\u5355\uFF08\u540C\u4E0B Config\uFF09\u3002\n \u25B8 hiddenTools\uFF08v1.51.4\uFF0Cowner 2026-10-09 \u4EE4\uFF09\uFF1A**\u4EC5\u5728\u5B89\u5168\u6A21\u5F0F\u5F00\u542F\u671F\u95F4**\u989D\u5916\u4ECE\u6A21\u578B\u5DE5\u5177\n \u6E05\u5355\u9690\u85CF\u7684\u5DE5\u5177\uFF08ACC \u57FA\u7840\u8868\u6052\u9690 bash\uFF09\u3002\u6253\u5F00 safe-mode \u751F\u6548\u3001**\u5173\u6389\u5373\u5168\u90E8\u6062\u590D**\uFF1B\n \u6539\u672C\u952E**\u70ED\u751F\u6548**\uFF08\u4E0B\u4E00\u4E2A pre-step \u91CD\u7B97\uFF0C\u65E0\u9700\u91CD\u542F\uFF09\u3002\u58F0\u660E\u5728 CCC\uFF08D23\uFF09\u2014\u2014ACC \u4EE3\u7801\u91CC\n \u4E0D\u51FA\u73B0\u4EFB\u4F55\u5177\u4F53\u5DE5\u5177\u540D\u3002\n \u26A0\uFE0F \u5199\u4E00\u4E2A**\u672C CCC \u672A\u6CE8\u518C**\u7684\u540D\u5B57\u4F1A\u8BA9**\u6574\u6B21 restrict \u5931\u8D25**\uFF08DSH \u5951\u7EA6\u9010\u5B57\uFF1A\"unknown\n names \u2026 fail\"\uFF09\u21D2 \u6B64\u65F6**\u9000\u5316\u4E3A\u53EA\u9690\u85CF\u57FA\u7840\u8868**\u5E76\u5728\u65E5\u5FD7\u54CD\u4EAE\u62A5\u9519\uFF08\u7EDD\u4E0D\u56E0\u4E00\u4E2A\u9519\u540D\u4E22\u6389\n safe-mode \u7684\u6838\u5FC3\u9690\u85CF\uFF09\u3002\u8BCA\u65AD\uFF1A`AGENT_SESSIONS/.restrict-diag.json` \u7684 `denyByKey`\u3002\n \u26A0\uFE0F \u9690\u85CF = **\u4ECE schema \u79FB\u9664**\uFF08agent \u6839\u672C\u770B\u4E0D\u5230\u8BE5\u5DE5\u5177\uFF09\u21D2 \u522B\u628A skill/\u6587\u6863\u8FD8\u5728\u6559\u7684\u5DE5\u5177\u5199\u8FDB\u6765\u3002\n\n Config:\n { \"safeMode\": { \"blacklist\": [\".secrets/\"], \"hiddenTools\": [\"workflow\", \"job_kill\"] } }\n\n\u2500\u2500 6. \u8F68\u8FF9\u7EA6\u675F.roles\uFF08F4 \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF09\u2500\u2500\n\u8F68\u8FF9\u7EA6\u675F\u89D2\u8272 = \u5168\u77E5\u5168\u80FD cognitive trajectory \u7684\u4EFB\u610F\u5B50\u96C6\uFF08CCC \u5B9A\u4E49\uFF09\u3002\u6BCF\u89D2\u8272\u72EC\u7ACB\u6A21\u578B + \u53CC\u767D\u540D\u5355\n\uFF08MSM \u767D\u540D\u5355 msms[] \u4E0E\u975E MSM \u5DE5\u5177\u767D\u540D\u5355 tools[]\uFF0C\u767D\u540D\u5355\u5916\u5168\u9690\u85CF\uFF1Bskill \u52A0\u8F7D\u6052\u53EF\u7528\uFF09\u3002\ncognitive trajectory \u7EAA\u5F8B\u5B50\u96C6\uFF08session/keeper/rebuild \u53C2\u4E0E\u9879\uFF09\u9ED8\u8BA4\u5168\u5173 = \u5B8C\u5168\u72EC\u7ACB\u3002\nsystemPrompt \u5185\u8054 \u6216 systemPromptFile\uFF08.md \u5F15\u7528\uFF0C\u63A8\u8350\uFF09\u2014\u2014\u89D2\u8272\u4F1A\u8BDD\u7684\u5B8C\u6574\u4EBA\u683C/\u8FB9\u754C\u63D0\u793A\u8BCD\u3002\nvalidate \u6821\u9A8C / apply \u751F\u6548 / list \u67E5\u770B\uFF1Acontainer_admin config <guide|validate|apply|list>\uFF08\u539F role \u57DF\u5DF2\u5E76\u5165 config\uFF0C\u65E7\u540D\u4F5C\u8FC7\u6E21\u522B\u540D\u4ECD\u53EF\u7528\uFF09\u3002\n\n Config:\n { \"\u8F68\u8FF9\u7EA6\u675F\": { \"roles\": {\n \"qa\": {\n \"model\": \"provider/model\",\n \"msms\": [\"web-search\", \"vlm-describe\"],\n \"tools\": [\"read\", \"grep\", \"glob\"],\n \"systemPromptFile\": \".opencode/skiff/qa.md\"\n } } } }\n\n\u2500\u2500 7. cognitive_trajectory\uFF08\u8F68\u8FF9 + \u5524\u9192\u6CE8\u518C\u8868\uFF0CD58\uFF09\u2500\u2500\nCCC \u7684\u4E00\u6761 cognitive trajectory = \u6301\u4E45\u8EAB\u4F53\uFF08SESSION.md\uFF09+ \u65F6\u95F4\u8F74\uFF08AGENT_SESSIONS/ \u76EE\u5F55 + \u5524\u9192\u6CE8\u518C\u8868\uFF09\u3002\n\u4E00\u4E2A CCC \u5185\u53EF\u591A\u6761\u5E76\u884C\uFF1B\u6761\u76EE\u5168\u90E8\u7531\u4EBA\u7C7B/agent \u663E\u5F0F\u767B\u8BB0\u3002\n\u26A0\uFE0F ACC \u4E0D\u518D\u63D0\u4F9B**\u5468\u671F\u81EA\u5524\u9192**\uFF08\u539F autopilot\uFF0C2026-09-15 \u6574\u6BB5\u9000\u573A\uFF1A\u673A\u5236/\u65F6\u949F/container_admin autopilot\n\u57DF/\u9762\u677F\u533A\u5757\u4E00\u5E76\u5220\u9664\uFF09\u3002\u81EA\u4E3B\u5DE1\u822A\u6539\u7531 CCC \u81EA\u5DF1\u9A71\u52A8\uFF08home-serenity \u4FA7 msm autopilot-round \u6BCF\u8F6E\u81EA\u6392\u4E0B\u4E00\u8F6E\uFF0C\n\u771F\u76F8\u6E90 = S151 SESSION.md \u00A71/\u00A71b/\u00A71c\uFF09\u3002**\u672C\u8F6E\u7684 cognitiveTrajectory.autopilot \u914D\u7F6E\u6BB5\u4FDD\u7559**\uFF08\u89C1\u4E0B\u8868\uFF09\u2014\u2014\n\u5B83\u73B0\u7531 **CCC \u4FA7\u811A\u672C**\u8BFB\u53D6\uFF0CACC \u4E0D\u518D\u6D88\u8D39\u8BE5\u6BB5\u3002\n\u5524\u9192\u6CE8\u518C\u8868\uFF08cognitive_trajectory send-later\uFF09\uFF1A\u5524\u9192 = \u672A\u6765\u65F6\u523B + \u4E00\u6761 message\uFF0C\u53EF\u5524\u9192\u81EA\u5DF1\u6216\u522B\u7684 cognitive trajectory\uFF1B\n\u843D\u70B9 AGENT_SESSIONS/wake-registry.json\uFF0C\u4E2D\u5FC3\u8C03\u5EA6\u5668\u5230\u70B9\u6295\u9012\uFF08fire-and-forget\uFF1B\u65E0\u56DE\u6267\u3001\u65E0\u56DE\u6536\uFF09\u3002\n\u8FD0\u884C\u6001\u8BCA\u65AD\uFF08ACC \u8D1F\u8D23\u4EBA\u4E13\u7528\uFF09\uFF1Aacc-diag\uFF08\u2460 live \u8FD0\u884C\u6001 / \u2460b \u5524\u9192\u65F6\u949F / \u2462 \u5524\u9192\u6CE8\u518C\u8868\uFF09\u3002\n\n\u25B8 cognitiveTrajectory.skills\uFF08v1.44.0\uFF0C**CCC \u7EA7 skill \u4F9B\u7ED9**\uFF1Bowner \u4EE4 2026-09-20\u300C\u8981\u6C42\u5B9E\u73B0\u6211\u7684\u8BC9\u6C42\u2026\u2026\n \u6CE8\u5165\u662F\u52A8\u6001\u6CE8\u5165\u5C31\u884C\uFF1Bcreate \u5148\u4E0D\u89E6\u53D1\u5427\uFF1B**\u8F68\u8FF9\u7EA6\u675F\u4E5F\u652F\u6301**\u300D\uFF09\uFF1A\n **\u672C CCC \u7684\u6BCF\u6761\u8F68\u8FF9\u90FD\u5E26\u8FD9\u4E9B skill**\u2014\u2014\u58F0\u660E\u7684 skill \u5168\u6587\u5728\u8BE5\u8F68\u8FF9**\u88AB\u7ED1\u5B9A\u671F\u95F4**\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u8BCD\n \uFF08**\u52A8\u6001\u6CE8\u5165**\uFF1A\u6BCF\u8BF7\u6C42\u91CD\u65B0\u6C42\u503C\u3001\u4E0D\u7F13\u5B58 \u21D2 \u6539\u914D\u7F6E\u6216\u6539 frontmatter **\u5373\u65F6\u751F\u6548**\uFF0C\u65E0\u9700\u91CD\u65B0 use\u3001\u65E0\u9700\u91CD\u542F\uFF09\u3002\n \u4E24\u6761\u58F0\u660E\u6765\u6E90**\u5E76\u96C6**\uFF08\u4E0D\u662F\u4E8C\u9009\u4E00\uFF09\uFF1A\u672C\u952E **cognitiveTrajectory.skills**\uFF08\u5BB9\u5668\u7ED9\u7684\u5E95\u5EA7\uFF0C**\u5728\u524D**\uFF09\n \uFF0B \u8F68\u8FF9\u81EA\u5DF1 SESSION.md \u9876\u90E8 frontmatter \u7684 skills:\uFF08\u8FD9\u6761\u8F68\u8FF9\u989D\u5916\u7684\uFF0C**\u8FFD\u52A0**\uFF1B\u540C\u540D\u53BB\u91CD\uFF09\u3002\n \u2705 **\u542B \u8F68\u8FF9\u7EA6\u675F\u89D2\u8272\u4F1A\u8BDD**\uFF08\u65C1\u8DEF\u7684\u662F ACC \u8EAB\u4EFD\u4E0E\u7EAA\u5F8B\uFF1Bskill \u662F\"\u5DE5\u4F5C\u8D44\u6599\"\uFF0C\u7167\u7ED9\uFF09\u3002\n \u5B88\u536B\uFF1A\u540D\u5B57\u5148\u8FC7\u5B89\u5168\u6821\u9A8C\u624D\u7528\u4E8E\u62FC\u8DEF\u5F84\uFF08\u9632\u7A7F\u8D8A\uFF09\uFF5C**\u5408\u5E76\u540E**\u7EDF\u4E00 32 KB \u4E0A\u9650\u622A\u65AD\uFF5C\u627E\u4E0D\u5230\u54CD\u4EAE\u5199 [\u7F3A\u5931]\u3002\n \u26A0\uFE0F \u4E0E create **\u65E0\u5173**\uFF08create \u523B\u610F\u4E0D\u593A\u7ED1\u5B9A\uFF0CU6\uFF09\u21D2 \u6CE8\u5165\u7684\u95E8\u662F\"**\u5B58\u5728\u7ED1\u5B9A**\"\uFF08cognitive_trajectory use\uFF09\u3002\n \u5931\u8D25\u8BED\u4E49\uFF1A\u672A\u914D\u7F6E / \u5B57\u6BB5\u7F3A\u5931 / \u914D\u7F6E\u635F\u574F \u21D2 **\u7A7A\u6570\u7EC4**\uFF08= \u65E0 CCC \u7EA7\u4F9B\u7ED9\uFF1B\u8F68\u8FF9\u7EA7\u58F0\u660E\u7167\u65E7\u751F\u6548\uFF09\u3002\n \u5F52\u5C5E\uFF08D23\uFF09\uFF1A**\u673A\u5236\u5728 ACC**\uFF08\u8BFB\u53D6 + \u5408\u5E76 + \u6CE8\u5165 + \u5B88\u536B\uFF09\uFF0C**\u58F0\u660E\u5728 CCC**\uFF08\u672C\u952E\uFF09\u3002\n\n Config:\n { \"cognitiveTrajectory\": {\n \"skills\": [\"acc-session\", \"cce\"], // v1.44.0\uFF1A\u672C CCC \u7684\u6BCF\u6761\u8F68\u8FF9\u90FD\u5E26\uFF08\u542B \u8F68\u8FF9\u7EA6\u675F\u4F1A\u8BDD\uFF09\n \"autopilot\": { // \u539F autopilotTrajectory \u6BB5\uFF0C\u5B57\u6BB5\u4E0D\u53D8\uFF08\u65E7\u952E\u4ECD\u56DE\u9000\u8BFB\uFF09\n \"enabled\": true,\n \"intervalHours\": 2,\n \"session\": \"S151\",\n \"biasProvider\": \"autopilot-bias.ts\",\n \"topPrompt\": \"\u672C\u8F68\u8FF9\u6838\u5FC3\u76EE\u6807/\u7EAA\u5F8B/\u8D28\u91CF\u8981\u6C42\uFF08CCC \u81EA\u586B\uFF0C\u9632\u6F02\u79FB\uFF09\",\n \"avoidWakeHours\": { \"start\": 8, \"end\": 18 }\n } } }\n\n\u2500\u2500 8. weixin\uFF08\u5FAE\u4FE1\u6865 F4c-3\uFF0C\u542B\u6D88\u606F\u8BB0\u5F55 hook\uFF09\u2500\u2500\nCCC \u7EA7\u5FAE\u4FE1\u4E2A\u4EBA\u53F7\u63A5\u5165\uFF08iLink \u534F\u8BAE\uFF09\uFF1Adsh \u4E00\u8FDB\u7A0B\u591A CCC\uFF0C\u6BCF CCC \u72EC\u7ACB\u5BF9\u63A5\u5FAE\u4FE1\u6865\u3002\n\u8D26\u53F7/\u8DEF\u7531/\u5F00\u5173\u5728\u6B64\u6587\u4EF6\uFF1B**bot_token \u51ED\u636E\u5728 CCC localstore credential scope**\n\uFF08\u626B\u7801\u7ED1\u5B9A\u540E\u81EA\u52A8\u5199\u5165\uFF0C\u6C38\u4E0D\u8FDB git \u660E\u6587\u9762\uFF09\u3002\n\u8DEF\u7531 user \u2192 role\uFF1Aexact \u4F18\u5148\uFF0C* \u901A\u914D\u515C\u5E95\uFF1Brole \u5FC5\u987B \u2208 \u8BE5 CCC \u8F68\u8FF9\u7EA6\u675F.roles\u3002\n\u9762\u677F\uFF08WebUI \u8BBE\u7F6E \u2192 \u5FAE\u4FE1\u6865\uFF09\u53EF\u626B\u7801\u7ED1\u5B9A/\u79FB\u9664\u8D26\u53F7/\u7F16\u8F91\u8DEF\u7531\uFF1Bmsm(\"weixin-doctor\", [\"status\"|\"diag\"|\"verify\"|\"guide\"]) \u6392\u67E5 + \u5927\u800C\u5168\u6307\u5357\u3002\u51ED\u636E\u4E3B\u52A8\u67E5\u770B\uFF1Alocalstore get WEIXIN_<ACCOUNT>_TOKEN\u3002\n \u25B8 \u4E3B\u52A8\u53D1\u9001\uFF08v1.31.0\uFF1A**ACC \u5DE5\u5177 im-bridge**\uFF09\uFF1A\u8C03\n im-bridge({channel:\"weixin\", action:\"send\", user:\"<\u522B\u540D|id>\", text:\"<\u6587\u672C>\"}) \u7ED9\u7528\u6237\u53D1\u6D88\u606F\u2014\u2014\n **\u5DE5\u5177\u53EA\u80FD\u64CD\u4F5C\u672C\u4F1A\u8BDD CCC**\uFF08\u65E0 ccc \u53C2\u6570\uFF0C\u9632\u8BEF\u53D1\u4ED6\u5BB9\u5668\uFF09\u2192 weixin-bridge.sendProactiveText \u2192\n \u6210\u529F\u540E\u89E6\u53D1 outgoing hook\uFF08source=\"proactive\"\uFF09\u3002\u6587\u4EF6\u7528 action:\"send-file\" + file:\"<CCC \u5185\u8DEF\u5F84>\"\n \uFF08\u9003\u9038/\u7F3A\u5931/\u8D85 20MB \u62D2\u7EDD\uFF09+ \u53EF\u9009 caption\u3002**\u53EF\u89C1\u6027**\uFF1A\u672C CCC \u914D\u7F6E\u4E86 IM \u901A\u9053\n \uFF08weixin.enabled=true\uFF09\u624D\u51FA\u73B0\u5728\u6A21\u578B\u5DE5\u5177\u6E05\u5355\u91CC\uFF1B\u672A\u914D\u7F6E \u2192 \u7531 guards \u9010 agent\n tools.restrict({deny:['im-bridge']}) \u79FB\u9664\uFF08\u4E0D\u662F\"\u770B\u5F97\u5230\u4F46\u88AB\u62D2\"\uFF09\u3002\u8F68\u8FF9\u7EA6\u675F\u89D2\u8272\u53E6\u9700\u5728\n roles.<role>.tools \u767D\u540D\u5355\u5217\u51FA im-bridge\u3002\n \u5386\u53F2\uFF08v1.30.9~v1.30.x\uFF09\uFF1ACCC \u4FA7 MSM weixin-send\uFF08\u8D70 loopback HTTP \u5165\u53E3 3082\uFF09\u2014\u2014\n v1.31.0 \u5DF2\u9000\u5F79\uFF08\u811A\u672C\u5220\u9664 + \u6CE8\u518C\u8868\u6CE8\u9500\uFF09\uFF1B3082 \u5165\u53E3\u4FDD\u7559\u7ED9**\u975E agent \u8C03\u7528\u8005**\u3002\n\n \u25B8 weixin.autoReplyWithLastMessage\uFF08v1.30.10\uFF0C\u5173\u95ED\u6865\u7684\u81EA\u52A8\u8F93\u51FA\uFF09\uFF1A\n \u7F3A\u7701 true = \u73B0\u72B6\uFF1A\u6865\u5728 agent \u4E00\u8F6E\u7ED3\u675F\u540E\u628A**\u6700\u7EC8 assistant \u6587\u672C**\u81EA\u52A8\u56DE\u53D1\u7528\u6237\uFF08source=\"reply\"\uFF09\u3002\n \u663E\u5F0F false = \u5173\u95ED\uFF1A\u6865**\u4E0D\u518D\u8F6C\u53D1\u6700\u7EC8\u6587\u672C**\uFF0C\u8F93\u51FA\u6743\u4EA4\u7ED9 agent\u2014\u2014\u6BCF\u8F6E\u5728\u7528\u6237\u6D88\u606F**\u672B\u5C3E**\u8FFD\u52A0\n **\u673A\u5236\u6807\u8BB0** [serenity:weixin-manual-output] + \u4E00\u884C\u5DF2\u586B\u597D\u53C2\u6570\u7684 im-bridge \u8C03\u7528\n \uFF08\u8D26\u53F7/\u7528\u6237 id\uFF09\uFF0Cagent \u81EA\u5DF1\u51B3\u5B9A\u53D1\u4EC0\u4E48\u3001\u53D1\u51E0\u6761\uFF08\u5168\u90E8\u8BB0 source=\"proactive\"\uFF09\u3002\n **ACC \u53EA\u7ED9\u673A\u5236\u4E0E\u6570\u636E\uFF0C\u7EAA\u5F8B\u63AA\u8F9E\u5F52 CCC**\uFF08v1.30.16\uFF09\uFF1A\u600E\u4E48\u5199/\u5FC5\u987B\u600E\u4E48\u505A/\u540E\u679C\u662F\u4EC0\u4E48\u5199\u5728\n CCC \u89D2\u8272\u63D0\u793A\u8BCD\u6587\u4EF6\u91CC\uFF08\u5982 .opencode/skiff/<role>.md\uFF09\uFF0C\u53EF\u70ED\u6539\uFF08v1.30.15 \u8D77\u89D2\u8272\u63D0\u793A\u8BCD\u70ED\u91CD\u8F7D\uFF09\u3002\n \u673A\u68B0\u95F8\u95E8\uFF08v1.30.16\uFF09\uFF1A\u672C\u8F6E turn \u7ED3\u675F\u4ECD\u672A\u6210\u529F\u8C03\u7528 im-bridge\uFF08send / send-file\uFF1B\u517C\u5BB9\u65E7\n msm(\"weixin-send\") \u5F62\u6001\uFF09\u2192 agent \u88AB\u6253\u56DE\uFF08\u22642 \u6B21\uFF0C\u63D0\u793A = \u6807\u8BB0 + \u4E8B\u5B9E\uFF09\uFF1B\u8FBE\u4E0A\u9650\u653E\u5F03\u5E76\u54CD\u4EAE\u544A\u8B66\u3002\n \u5931\u8D25\u8C03\u7528\u4E0D\u7B97\u5DF2\u9001\u8FBE\u3002\n \u8BED\u4E49\u8FB9\u754C\uFF08\u7528\u6237\u62CD\u677F\uFF09\uFF1A\u53EA\u5173\u6700\u7EC8\u6587\u672C\uFF1B\u7CFB\u7EDF\u7C7B\u6D88\u606F\uFF08\u65B0\u5BF9\u8BDD\u901A\u77E5 / \u8BED\u97F3\u65E0\u6CD5\u89E3\u6790\u63D0\u793A\uFF09\u4FDD\u7559\uFF1B\n typing \u6307\u793A\u4E0E incoming hook \u4E0D\u53D7\u5F71\u54CD\u3002\n\n \u25B8 weixin.fallbackOnNoSend\uFF08v1.30.17\uFF0C\u624B\u52A8\u6A21\u5F0F\u7684**\u515C\u5E95**\uFF1B\u7F3A\u7701 false\uFF09\uFF1A\n \u4EC5\u5728 autoReplyWithLastMessage=false \u65F6\u751F\u6548\u3002true \u2192 \u4E00\u8F6E\u7ED3\u675F\u65F6\u82E5 agent **\u4E00\u6B21\u90FD\u6CA1\u6210\u529F**\n \u8C03\u7528\u8F93\u51FA\u5DE5\u5177\uFF08\u95F8\u95E8\u6253\u56DE\u7528\u5C3D\uFF09\uFF0C\u6865\u628A\u8BE5\u8F6E\u6700\u7EC8\u6587\u672C\u8F6C\u53D1\u7ED9\u7528\u6237\uFF08\u8BB0\u5F55 source=\"reply-fallback\"\uFF0C\n \u65E5\u5FD7\u7559\u54CD\u4EAE\u544A\u8B66\uFF09\u2014\u2014\u4FDD\u8BC1\"\u4E0D\u4E22\u6D88\u606F\"\u3002agent \u81EA\u5DF1\u53D1\u8FC7 \u2192 \u4E0D\u515C\u5E95\uFF08\u8F93\u51FA\u6743\u4ECD\u5728 agent\uFF09\u3002\n \u4E3A\u4EC0\u4E48\u9700\u8981\uFF08\u5B9E\u8BC1\uFF09\uFF1Av1.30.16 \u95F8\u95E8\u4F1A\u6253\u56DE \u22642 \u6B21\uFF0C\u4F46\u5B9E\u6D4B\u67D0\u6A21\u578B\u5728**\u5BD2\u6684\u7C7B\u6D88\u606F**\u4E0A\u8FDE\u7EED 4 \u8F6E\u3001\n \u8DE8 3 \u7248 CCC \u63D0\u793A\u8BCD\u4ECD\u4E0D\u8C03\u5DE5\u5177 \u2192 \u6253\u56DE\u7528\u5C3D\u540E\u7528\u6237\u4EC0\u4E48\u90FD\u6536\u4E0D\u5230\uFF1B\u63D0\u793A\u8BCD\u4E0E\u95F8\u95E8\u5747\u5DF2\u6392\u9664\u3002\n \u4EE3\u4EF7\uFF08\u663E\u5F0F\uFF09\uFF1A\u89D2\u8272\u5931\u53BB\"\u6545\u610F\u9759\u9ED8\"\u80FD\u529B\uFF08\u5B83\u603B\u4F1A\u4EA7\u51FA\u4E00\u6BB5\u6700\u7EC8\u6587\u672C\uFF09\u2192 \u9700\u4E25\u683C\u9759\u9ED8\u7684\u89D2\u8272\u4FDD\u6301\u7F3A\u7701\u3002\n \u4E09\u6001\uFF1A\u2460 autoReplyWithLastMessage=true\uFF08\u7F3A\u7701\uFF09\u6865\u603B\u662F\u56DE\u53D1\uFF1B\u2461 =false + fallbackOnNoSend=true\n = agent \u4F18\u5148\u3001\u6865\u515C\u5E95\uFF08\u63A8\u8350\uFF09\uFF1B\u2462 \u4E24\u8005\u7686 false\uFF08\u7F3A\u7701\uFF09= \u4E25\u683C\u9759\u9ED8\uFF08v1.30.10 \u8BED\u4E49\uFF09\u3002\n \u26A0\uFE0F \u515C\u5E95\u53EA\u5728**\u95F8\u95E8\u88C5\u914D\u6210\u529F**\u65F6\u751F\u6548\uFF08\u672A\u88C5\u914D\u5219\u65E0\u6CD5\u5224\u5B9A\u662F\u5426\u53D1\u9001\u8FC7 \u2192 \u4E0D\u5192\u91CD\u590D\u53D1\u9001\u98CE\u9669\uFF09\u3002\n\n Config:\n { \"weixin\": {\n \"enabled\": true,\n \"hook\": \"scripts/weixin-message-hook.ts\",\n \"autoReplyWithLastMessage\": true,\n \"fallbackOnNoSend\": false,\n \"accounts\": [{ \"accountId\": \"wechat-1\", \"name\": \"\u5BB6\u5EAD\u52A9\u624B\", \"enabled\": true }],\n \"routes\": [{ \"user\": \"*\", \"role\": \"my-role\" }]\n } }\n\n \u25B8 weixin.hook\uFF08\u6D88\u606F\u8BB0\u5F55 hook\uFF0Cv1.27.13\uFF09\uFF1A\n \u5FAE\u4FE1\u6865\u6BCF\u6536/\u53D1\u4E00\u6761\u6D88\u606F\u89E6\u53D1\u4E00\u6B21 CCC \u81EA\u5199\u811A\u672C\uFF0C\u7531 CCC \u81EA\u884C\u6301\u4E45\u5316\u4FDD\u5B58\uFF08\u5B58\u54EA/\u5B58\u6210\u4EC0\u4E48\n /\u662F\u5426\u5165\u5E93\u5168\u5F52 CCC\u2014\u2014ACC \u4E0D\u7ED1\u5B9A\u5B58\u50A8\uFF09\u3002\u672A\u914D\u7F6E hook \u2192 \u96F6\u53D8\u5316\uFF08\u4E0D\u89E6\u53D1\uFF09\u3002\n\n \u89E6\u53D1\uFF08\u53CC\u5411\uFF09\uFF1A\n incoming = \u7528\u6237 \u2192 bot\uFF1A\u8DEF\u7531\u547D\u4E2D\u540E\u3001\u5A92\u4F53\u843D\u76D8\u540E\u89E6\u53D1\uFF08\u6587\u672C\u542B\u8BED\u97F3\u8F6C\u5199\uFF1B\u5A92\u4F53\u5E26\u843D\u76D8 relPath\uFF09\n outgoing = bot \u2192 \u7528\u6237\uFF1A\u53D1\u9001\u6210\u529F\u540E\u89E6\u53D1\uFF08reply = \u7528\u6237\u5B9E\u9645\u6536\u5230\u7684\u7EAF\u6587\u672C\uFF0C\u5DF2\u5265\u79BB think\uFF09\n \u00B7 source=\"reply\"\uFF08\u7F3A\u7701\uFF09= \u56DE\u590D\u7528\u6237\u6D88\u606F\uFF1Bsource=\"proactive\"\uFF08v1.30.9\uFF09= bot \u4E3B\u52A8\u53D1\u8D77\n \uFF08CCC \u7ECF im-bridge \u5DE5\u5177\uFF08v1.31.0\uFF1B\u65E7 weixin-send MSM \u5DF2\u9000\u5F79\uFF09\u2192 sendProactiveText\uFF09\uFF1B\n source=\"reply-fallback\"\uFF08v1.30.17\uFF09= \u624B\u52A8\u6A21\u5F0F\u4E0B agent \u672C\u8F6E\u672A\u53D1\u9001 \u2192 \u6865\u515C\u5E95\u8F6C\u53D1\u6700\u7EC8\u6587\u672C\n \u00B7 file\uFF08\u4EC5 send-file \u8865\u8BB0\uFF09\uFF1A{ \"name\": \"\u62A5\u544A.pdf\", \"size\": 12345, \"caption\": \"\u53EF\u9009\" }\n\n \u811A\u672C\u7EA6\u5B9A\uFF08\u4E8B\u4EF6 JSON \u5355\u884C\u7ECF stdin \u4F20\u5165\uFF1Bbun \u4F18\u5148 node \u515C\u5E95\uFF09\uFF1A\n #!/usr/bin/env bun \u6216 node script.js \u2014\u2014 \u8BFB process.stdin \u6574\u6BB5 JSON.parse\n const ev = JSON.parse(await new Response(process.stdin).text())\n ev.event === 'incoming' | 'outgoing'\n\n \u4E8B\u4EF6 schema\uFF08**\u4E0D\u542B\u4EFB\u4F55\u4F1A\u8BDD\u51ED\u636E**\u2014\u2014context_token/bot_token/token/aes_key \u5747\u4E0D\u51FA\u73B0\uFF09\uFF1A\n { \"event\": \"incoming\", \"ts\": 1788359941488, \"cccRoot\": \"/path/ccc\",\n \"accountId\": \"wechat-1\", \"userId\": \"u1@im.wechat\",\n \"sessionId\": \"skiff-weixin-xxx\", \"role\": \"my-role\",\n \"message\": { \"text\": \"\u4F60\u597D\", \"media\": [{ \"kind\": \"image\", \"relPath\": \"_tmp/weixin-inbound/<hash>/img_x.jpg\" }] } }\n { \"event\": \"outgoing\", \"ts\": ..., \"cccRoot\": ..., \"accountId\": ...,\n \"userId\": ..., \"sessionId\": ..., \"role\": ...,\n \"reply\": \"\u5DF2\u8BB0\u5F55\uFF08\u7EAF\u6587\u672C\uFF09\", \"source\": \"reply\" | \"proactive\" | \"reply-fallback\" }\n\n \u793A\u4F8B\u811A\u672C\uFF08\u8FFD\u52A0\u5230\u6309\u65E5\u6587\u4EF6\u2014\u2014\u6301\u4E45\u5316\u5F52 CCC \u81EA\u9009\uFF1A\u6587\u4EF6/DB/\u8FDC\u7AEF\u5747\u53EF\uFF09\uFF1A\n const fs = require('node:fs'); const p = '/path/ccc/AGENT_SESSIONS/_weixin-log.jsonl';\n fs.appendFileSync(p, JSON.stringify(ev) + '\\n');\n\n \u6267\u884C\u8BED\u4E49\uFF08\u65C1\u8DEF\u5BB9\u5FCD\uFF09\uFF1A\u5F02\u6B65 fire-and-forget + 15s \u8D85\u65F6 kill + \u5931\u8D25\u4EC5\u65E5\u5FD7\u2014\u2014\n \u5FAE\u4FE1\u6865\u6D88\u606F\u5904\u7406/\u56DE\u590D\u4E0D\u53D7 hook \u5F71\u54CD\uFF1B\u811A\u672C\u987B\u5728 CCC \u6839\u5185\uFF08\u8DEF\u5F84\u9003\u9038\u62D2\u7EDD\uFF09\u3002\n \u5A92\u4F53 relPath \u6307\u5411 _tmp/weixin-inbound/\uFF08\u4E34\u65F6\u76EE\u5F55\uFF09\u2014\u2014\u9700\u6301\u4E45\u4FDD\u5B58\u5A92\u4F53\u6587\u4EF6\u8BF7\u81EA\u884C copy\u3002\n\n\u2500\u2500 9. exclusiveTools\uFF08\u4E13\u5C5E\u5DE5\u5177\u58F0\u660E\uFF0Cv1.33\uFF09\u2500\u2500\n**ACC \u6CE8\u518C\u4F46\u9ED8\u8BA4\u5BF9\u6240\u6709 CCC \u9690\u85CF**\u7684\u5DE5\u5177\uFF0C\u53EA\u6709\u5728\u81EA\u5DF1\u8FD9\u91CC**\u70B9\u540D**\u7684 CCC \u53EF\u89C1\u3002\n\u8FD9\u662F\u901A\u7528\u673A\u5236\uFF08\u4EFB\u4F55\u5DE5\u5177\u540D\u7686\u53EF\uFF09\uFF0C\u5224\u636E\u7531 CCC \u58F0\u660E\u2014\u2014ACC \u4EE3\u7801\u91CC\u4E0D\u51FA\u73B0\u4EFB\u4F55\u5177\u4F53 CCC \u7684\u540D\u5B57\u3002\n\u5F53\u524D\u4E13\u5C5E\u5DE5\u5177\uFF1Aacc-diag\uFF08\u8FD0\u884C\u6001\u8BCA\u65AD\u2014\u2014live \u4F1A\u8BDD / agent \u5B9A\u4F4D / \u5524\u9192\u6CE8\u518C\u8868 / \u5524\u8D77\u6761\u4EF6\u94FE\uFF09\u3002\n\n \u25B8 \u53EF\u89C1\u6027\u8BED\u4E49\uFF1A\u672A\u58F0\u660E \u21D2 \u7531 guards \u9010 agent tools.restrict({deny:[tool]}) \u4ECE**\u6A21\u578B\u5DE5\u5177\u6E05\u5355\u79FB\u9664**\n \uFF08\u4E0D\u662F\"\u770B\u5F97\u5230\u4F46\u88AB\u62D2\"\uFF09\u3002\u914D\u7F6E\u70ED\u66F4\u65B0\uFF1A\u6539\u672C\u6587\u4EF6\u5373\u65F6\u751F\u6548\uFF08\u65E0\u9700\u91CD\u542F\uFF09\u3002\n \u25B8 \u5931\u8D25\u65B9\u5411 **fail-closed**\uFF1A\u672A\u914D\u7F6E / \u5B57\u6BB5\u7F3A\u5931 / \u914D\u7F6E\u635F\u574F\uFF08JSON \u574F\uFF09\u21D2 \u4E00\u5F8B\u6309\"\u672A\u58F0\u660E\"\u5904\u7406\n \uFF08\u4E13\u5C5E\u5DE5\u5177\u4FDD\u6301\u9690\u85CF\uFF1B\u914D\u7F6E\u635F\u574F\u672C\u8EAB\u53E6\u6709\u54CD\u4EAE\u544A\u8B66\uFF09\u3002\u4E0E im-bridge \u76F8\u53CD\u2014\u2014\u9690\u85CF\u4E00\u4E2A**\u5DF2\u5728\u4F7F\u7528**\n \u7684\u901A\u9053\u624D\u4F1A\u9759\u9ED8\u635F\u5BB3\u80FD\u529B\uFF0C\u800C\u4E13\u5C5E\u5DE5\u5177\u7684\u9ED8\u8BA4\u6001\u672C\u5C31\u662F\u9690\u85CF\u3002\n \u25B8 \u914D\u7F6E\u635F\u574F\u7684\u54CD\u4EAE\u544A\u8B66\u53EA\u51FA\u73B0\u4E00\u6B21\uFF08\u6BCF\u8DEF\u5F84\uFF09\uFF0Cdashboard health \u540C\u6837\u80FD\u770B\u5230\u3002\n\n Config:\n { \"exclusiveTools\": [\"acc-diag\"] }\n";
|
|
11
18
|
interface MsmFlag {
|
|
12
19
|
name: string;
|
|
13
20
|
type?: string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trajectory-constraint-admin.ts — 轨迹约束面逻辑层(公开入口 = `container_admin` 的 `role` 域)
|
|
3
|
+
*
|
|
4
|
+
* 三动作分工(用户拍板 2026-08-28,2026-10-19 重做后语义不变):guide 定义教程 /
|
|
5
|
+
* validate 校验 / list 摘要 / apply 显式应用确认。
|
|
6
|
+
*
|
|
7
|
+
* ## 🔴 2026-10-19 重做后,本文件的**数据源变了**
|
|
8
|
+
*
|
|
9
|
+
* 旧:读 `.opencode/serenity.json` 的 `轨迹约束.roles`(容器级配置表)。
|
|
10
|
+
* 新:读**轨迹目录里的 `trajectory-constraint.json`**(owner:「目录下拥有 `trajectory-constraint.json` 的认知轨迹自动是一个 轨迹约束」)。
|
|
11
|
+
* ⇒ `constraintCount` 的语义 = **本 CCC 有几条 轨迹约束轨迹**;"角色名"这个概念没有了,只有**轨迹编码**。
|
|
12
|
+
*
|
|
13
|
+
* 归属(D23)不变:ACC 提供机制(教程 + 校验 + 列表),**约束内容归 CCC**(写进 `trajectory-constraint.json`)。
|
|
14
|
+
*/
|
|
15
|
+
import type { JsonValue } from '../kernel/json.js';
|
|
16
|
+
/** 轨迹约束 定义教程:概念 / `trajectory-constraint.json` schema / 双白名单 / 轨迹纪律 / 微信路由 */
|
|
17
|
+
export declare const TRAJECTORY_CONSTRAINT_GUIDE = "\u2550\u2550\u2550 \u8F68\u8FF9\u7EA6\u675F Guide (2026-10-19 redesigned) \u2550\u2550\u2550\n\n**A \u8F68\u8FF9\u7EA6\u675F IS a cognitive trajectory \u2014 the one whose directory contains `trajectory-constraint.json`.**\nThat is the whole rule. No role table, no name registry: put the file there and the trajectory\nbecomes a \u8F68\u8FF9\u7EA6\u675F; delete it and it goes back to being an ordinary trajectory (takes effect at once).\n\n AGENT_SESSIONS/<YYYY-MM-DD--<code>--<desc>>/\n \u251C\u2500 SESSION.md \u2190 its persistent body (this IS the workspace \u2014 nothing is auto-created)\n \u251C\u2500 trajectory-constraint.json \u2190 its constraints (this file is what makes it a \u8F68\u8FF9\u7EA6\u675F)\n \u2514\u2500 TRAJECTORY-UID \u2190 legacy, read-only (superseded by the code)\n\n\u2500\u2500 trajectory-constraint.json schema \u2500\u2500\n {\n \"model\": \"provider/model\", // optional; falls back to cheap-worker.defaultModel\n \"tools\": [\"read\",\"grep\",\"msm\",...], // non-MSM toolbar whitelist \u2014 anything not listed is REMOVED from the schema\n \"msms\": [\"msm-a\",\"msm-b\"], // MSM whitelist \u2014 msm exec may only run these; register/deregister always refused\n \"cognitiveTrajectory\": { \"session\": true, \"keeper\": true, \"rebuild\": true }, // defaults TRUE (see below)\n \"systemPromptFile\": \".opencode/skiff/<role>.md\", // recommended: relative to CCC root, hot-reloads on save\n \"systemPrompt\": \"...\" // inline fallback; systemPromptFile wins when both are set\n }\n\n\u2500\u2500 Two whitelists, both default-deny \u2500\u2500\n - `tools: []` + non-empty `msms` = pure-MSM \u8F68\u8FF9\u7EA6\u675F (the usual knowledge-QA shape)\n - non-empty `msms` \u21D2 the `msm` tool is added automatically (the only entry point for MSMs)\n - anything outside both lists is invisible in the schema, not merely rejected at call time\n\n\u2500\u2500 cognitiveTrajectory: why the defaults are TRUE \u2500\u2500\n The workspace of a \u8F68\u8FF9\u7EA6\u675F is its own SESSION.md. Setting these to false would cut it off from\n its own body, so `session`/`keeper`/`rebuild` all default to true. Set false explicitly only\n when you deliberately want that participation off.\n\n\u2500\u2500 WeChat routing: user \u2192 trajectory code \u2500\u2500\n `weixin.routes`: `[{ \"user\": \"<wx id or *>\", \"trajectory\": \"<code>\" }]`\n A route pointing at a code with no such trajectory is a hard error (the message is NOT delivered).\n A trajectory WITHOUT `trajectory-constraint.json` is still a legal target \u2014 it is just an ordinary trajectory.\n\n\u2500\u2500 Operating it \u2500\u2500\n Everything is read live: saving `trajectory-constraint.json` takes effect on the next turn, no restart.\n (The prompt FILE hot-reloads by mtime; the JSON itself is re-read every turn.)\n After editing, run container_admin config apply for an explicit validation + confirmation pass.\n Skill loading is always available to \u8F68\u8FF9\u7EA6\u675F sessions (skills are not whitelisted \u2014 they are read-only knowledge).";
|
|
18
|
+
/** 校验当前 CCC 的 轨迹约束面:msms 均已注册 / model ∈ cheap-worker.models / 提示词非空 */
|
|
19
|
+
export declare function validateTrajectoryConstraint(root: string): JsonValue;
|
|
20
|
+
/** 列出当前 CCC 的轨迹约束(编码 / 模型 / msms / tools / 纪律摘要 / 提示词来源) */
|
|
21
|
+
export declare function listTrajectoryConstraintSummary(root: string): JsonValue;
|
|
22
|
+
/**
|
|
23
|
+
* 应用当前 轨迹约束 配置(S142 用户:改了 json 应该有个生效机制)。
|
|
24
|
+
*
|
|
25
|
+
* 语义:约束面是**每轮实时读取**的 ⇒ apply 不是"推送",而是**显式校验 + 应用确认**:
|
|
26
|
+
* ① 校验合法(复用 validate:msms 注册 / model 白名单 / 提示词非空)
|
|
27
|
+
* ② 非法 → 报告问题清单,不应用(提示修复后重跑 apply)
|
|
28
|
+
* ③ 合法 → 应用确认:绑定 CCC + 轨迹清单 + 说明(下一轮即生效)
|
|
29
|
+
*/
|
|
30
|
+
export declare function applyTrajectoryConstraint(root: string): JsonValue;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* 而本模块回答的是**关于载体的问题**(它空闲了/它被销毁了/会话先没了)。
|
|
12
12
|
* 三个结算条件(见下)里有两条与"空闲"无关 —— 名字取窄了会误导调用方以为只需等 idle。
|
|
13
13
|
*
|
|
14
|
-
* 为什么单独成模块:`
|
|
14
|
+
* 为什么单独成模块:`askTrajectory`(问答)与 `cheap-worker`(杂工循环)都要"等这一轮做完",
|
|
15
15
|
* 此前各自复制了一份只监听 `agent/status → idle` 的实现。该实现有一个静默挂死:
|
|
16
16
|
* **agent 先被销毁(会话关闭/进程回收/显式 dispose)就永远不会再发 idle 事件**,
|
|
17
17
|
* 等待方 Promise 永不结算 → 问答/杂工永久卡住(宿主的 disposal 语义:
|
package/lib/trajectory/core.d.ts
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
*
|
|
33
33
|
* ⚠️ 由此产生一条**必须成立**的配套:`readAllSessions` **跳过 `_` 前缀目录**。
|
|
34
34
|
* 否则归档后的轨迹会继续出现在清单里,"归档"就不是归档了(历史 bug:`_weixin-logs` /
|
|
35
|
-
* `
|
|
35
|
+
* `_轨迹约束-logs` / `_corpus` 曾被当成轨迹列出来)。
|
|
36
36
|
*/
|
|
37
37
|
/**
|
|
38
38
|
* `cognitive_trajectory` 工具的动作集合(v1.33,S142 §32 用户裁决后的收敛结果)。
|