@shgroup/dsh-serenity-hooks 1.27.12 → 1.28.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 +7 -5
- package/dsh.plugin.json +3 -2
- package/lib/{ccc-CfDrlfA7.js → ccc-CX48YNUL.js} +19 -1
- package/lib/ccc.d.ts +16 -0
- package/lib/client.js +18 -22
- package/lib/config-ops.d.ts +8 -10
- package/lib/index.d.ts +1 -1
- package/lib/index.js +695 -120
- package/lib/invariant.d.ts +1 -1
- package/lib/invariant.js +2 -1
- package/lib/kit-ops.d.ts +27 -0
- package/lib/msm-ops.d.ts +17 -3
- package/lib/rebuild.d.ts +5 -0
- package/lib/seams/guards.d.ts +14 -0
- package/lib/seams/keeper.d.ts +4 -1
- package/lib/seams/system-prompt.d.ts +15 -4
- package/lib/{settings-section-PzeHStWP.js → settings-section-DnX9TJxt.js} +3 -3
- package/lib/settings-section.d.ts +5 -5
- package/lib/{skiff-debug-BRlc17GU.js → skiff-debug-DIf_Uenr.js} +2 -2
- package/lib/{skiff-role-Dw7UKCUm.js → skiff-role-BTdBHyOQ.js} +1 -1
- package/lib/tools/session.d.ts +23 -6
- package/lib/{weixin-api-DAugVJ-f.js → weixin-api-OUCuw0Ws.js} +1 -1
- package/lib/weixin-hook.d.ts +103 -0
- package/lib/{weixin-route-EVu56Ytp.js → weixin-route-DFkRf9ou.js} +2 -1
- package/package.json +1 -1
package/lib/invariant.d.ts
CHANGED
|
@@ -26,4 +26,4 @@ export declare function readPluginManifest(manifestPath: string): PluginManifest
|
|
|
26
26
|
*/
|
|
27
27
|
export declare function verifyToolConsistency(manifestPath: string, registeredTools: readonly string[]): string[];
|
|
28
28
|
/** 包级不变量:本插件注册的工具集合(与 dsh.plugin.json contributes.tools 一致) */
|
|
29
|
-
export declare const REGISTERED_TOOLS: readonly ["cc_fs", "session", "acc_kit", "cc_git", "acc_msm", "eap", "neat", "cce", "handyman", "session_rebuild", "localstore", "skiff_admin"];
|
|
29
|
+
export declare const REGISTERED_TOOLS: readonly ["cc_fs", "session", "acc_kit", "cc_git", "acc_msm", "eap", "neat", "cce", "handyman", "session_rebuild", "localstore", "skiff_admin", "autopilot-trajectory"];
|
package/lib/invariant.js
CHANGED
package/lib/kit-ops.d.ts
CHANGED
|
@@ -19,5 +19,32 @@ export interface KitArgs {
|
|
|
19
19
|
action: KitAction;
|
|
20
20
|
seconds?: number;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* MSM 注册表完整性检查(需求⑤c S142 用户:"要有ACC层检查方法检查注册表没坏,这东西太核心了")。
|
|
24
|
+
* 注册表坏 → loadMsmEntries JSON.parse 抛 → acc_msm/skiff-admin/output-guard/session 全崩 +
|
|
25
|
+
* register 判重也 loadMsmEntries → **自锁无法自救**(R7)——health 必须**不因坏表抛错**,
|
|
26
|
+
* 独立解析(不依赖 loadMsmEntries)逐项检查,坏 = ok:false + issues + 修复指引。
|
|
27
|
+
*
|
|
28
|
+
* 检查项(单级化后唯一聚合档 + root 级遗留):
|
|
29
|
+
* - 文件存在(无注册表 = 空 ok——CCC 尚未注册 MSM 不视为坏)
|
|
30
|
+
* - JSON.parse 合法(剥 BOM;坏 JSON = 结构损坏)
|
|
31
|
+
* - 顶层 v1 wrapper(entries[])或裸数组
|
|
32
|
+
* - 每 entry 字段类型(name/path/skill/category/description)
|
|
33
|
+
* - name 全局唯一(loadMsmEntries byName 去重语义——重复歧义)
|
|
34
|
+
* - path 根内 + 脚本存在(引用完整)
|
|
35
|
+
*
|
|
36
|
+
* 修复指引:register/deregister 每次变更精提交(只 add 注册表文件)→ 坏表可
|
|
37
|
+
* `git checkout -- <registry>` 恢复(bash 可用时 / 用户手动 / cc_git 无 checkout 子命令——
|
|
38
|
+
* 走 bash git restore,safe-mode 下建议用户介入)。工具只输出指引不代劳(用户拍板)。
|
|
39
|
+
*/
|
|
40
|
+
export interface RegistryHealthReport {
|
|
41
|
+
/** 聚合档路径(相对 CCC 根);无 cccName → null */
|
|
42
|
+
path: string | null;
|
|
43
|
+
ok: boolean;
|
|
44
|
+
/** 该注册表文件是否存在(不存在 = 未注册任何 MSM,非坏) */
|
|
45
|
+
present: boolean;
|
|
46
|
+
issues: string[];
|
|
47
|
+
}
|
|
48
|
+
export declare function checkRegistryHealth(root: string): RegistryHealthReport;
|
|
22
49
|
export declare function runKit(root: string | null, args: KitArgs): Promise<JsonValue>;
|
|
23
50
|
export { loadSerenityConfig };
|
package/lib/msm-ops.d.ts
CHANGED
|
@@ -6,13 +6,20 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { JsonValue } from './json.js';
|
|
8
8
|
export declare const MSM_TIMEOUT_MS = 600000;
|
|
9
|
-
/** CCC 名:从 .serenity
|
|
9
|
+
/** CCC 名:从 .serenity 解析(review P2-2 统一收口到 ccc.ts readCccName——跳 # 注释/空行首非空行;此处保留导出名兼容内部调用) */
|
|
10
10
|
export declare function readCccName(root: string): string | null;
|
|
11
|
-
export type MsmAction = 'list' | 'exec' | 'register' | 'deregister' | 'check' | 'guide' | 'ccc-config';
|
|
11
|
+
export type MsmAction = 'list' | 'exec' | 'register' | 'deregister' | 'check' | 'guide' | 'ccc-config' | 'catalog';
|
|
12
12
|
export declare const MSM_ACTIONS: readonly MsmAction[];
|
|
13
|
+
/**
|
|
14
|
+
* ACC 能力目录(需求④ S142 用户拍板:"acc 配置各自做 guide 越来越多,整合成目录式使用指南")。
|
|
15
|
+
* 目录在前、详情各归各——**单一真相源**:本目录只索引"去哪个工具/子命令看详情",不复制详情
|
|
16
|
+
* (避免 skiff_admin/weixin-doctor 等 guide 全文重复 → 多真相源 → 失同步)。
|
|
17
|
+
* 后续新增功能只需在此加一行(低熵可演进)。
|
|
18
|
+
*/
|
|
19
|
+
export declare const ACC_CATALOG = "\u2550\u2550\u2550 ACC Usage Catalog \u2550\u2550\u2550\nDirectory of ACC capabilities \u2014 each area lists where to go for details (guides live with their feature; this catalog only points).\n\n \u2460 \u4F1A\u8BDD\u4E0E\u8F68\u8FF9 \u2192 session tool\uFF08list/show/create/use/close/health/qa/archive/summary/hook-develop-guide\uFF09\n session_rebuild\uFF08\u8D85\u9650\u91CD\u5EFA\uFF1B\u9608\u503C K \u89C1 ccc-config / \u8BBE\u7F6E\u9762\u677F\u91CD\u5EFA\u9608\u503CK\uFF09\n \u2461 \u8BA4\u77E5\u8D28\u91CF\u6846\u67B6 \u2192 eap\uFF08E\u2191/R\u2193/S\u2191\uFF09/ neat\uFF08Neat \u534F\u8BAE\uFF09/ cce\uFF08\u8BA4\u77E5\u8FDE\u7EED\u6027\u5DE5\u7A0B\uFF09\u2014\u2014\u6E10\u8FDB\u62AB\u9732\uFF0C\u65E0\u53C2\u5373\u5168\u6587\n \u2462 \u5DE5\u5177\u4E0E\u6267\u884C \u2192 cc_fs\uFF08\u6587\u4EF6\u7CFB\u7EDF 15 \u5B50\u547D\u4EE4\uFF09/ cc_git\uFF08git\uFF09/ acc_kit\uFF08health \u542B\u6CE8\u518C\u8868\u68C0\u67E5/time/wait\uFF09\n acc_msm\uFF08MSM \u6846\u67B6\uFF1A\u672C\u5DE5\u5177 list/exec/register/deregister/check/guide/ccc-config/catalog\uFF09\n handyman\uFF08\u6742\u5DE5 agent \u7F16\u6392\u2014\u2014guide \u5B50\u547D\u4EE4\uFF1Aacc_msm exec handyman guide\uFF09\n \u2463 \u89D2\u8272\u4E0E\u5BF9\u5916\u9762 \u2192 skiff_admin\uFF08Skiff \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF1Aguide/validate/apply/list\u2014\u2014acc_msm exec skiff_admin guide\uFF09\n ACP\uFF08\u7A0B\u5E8F\u5316 JSON-RPC 3100\uFF09/ Skiff \u95EE\u7B54\u9875\uFF08\u516C\u7F51 ask\uFF09\u2014\u2014\u9762\u677F\u300C\u5916\u90E8\u80FD\u529B\u300D\u7EC4\n \u2464 \u81EA\u4E3B\u4E0E\u63A5\u5165 \u2192 autopilot-trajectory\uFF08Autopilot \u4E00\u7AD9\u5F0F\uFF1Aall/init/random/diag/doc/check/status/guide\uFF09\n weixin \u5FAE\u4FE1\u6865\uFF08\u914D\u7F6E/\u626B\u7801/\u8DEF\u7531/\u6D88\u606F hook\u2014\u2014CCC \u4FA7 weixin-doctor MSM\uFF1Aacc_msm exec weixin-doctor guide\uFF09\n \u2465 CCC \u914D\u7F6E\u603B\u89C8 \u2192 acc_msm ccc-config\uFF088 \u6BB5\uFF1Ahandyman/sessionKeeper/localstore/hooks/safeMode/skiff/autopilotTrajectory/weixin\uFF09\n \u2466 \u6CE8\u518C\u8868\u4E0E\u5B89\u5168 \u2192 mech-registry.json \u7531 acc_msm register/deregister \u7BA1\u7406\uFF08\u5199\u4FDD\u62A4\u2014\u2014\u4E0D\u53EF\u76F4\u63A5\u7F16\u8F91\uFF09\n acc_kit health \u8F93\u51FA registry \u5B8C\u6574\u6027\u68C0\u67E5\uFF1B\u635F\u574F\u6062\u590D\u6307\u5F15\u89C1 health registry.issues\n\n\u6BCF\u533A\uFF1A\u4E00\u53E5\u8BDD\u5B9A\u4F4D + \u8BE6\u7EC6\u5165\u53E3\uFF08\u5DE5\u5177\u540D / guide \u5B50\u547D\u4EE4\uFF09\u3002\u8BE6\u60C5\u6C38\u8FDC\u4EE5\u5BF9\u5E94\u5DE5\u5177\u7684 guide/ccc-config \u4E3A\u5355\u4E00\u771F\u76F8\u6E90\u3002\n";
|
|
13
20
|
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 (acc_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. acc_msm 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 acc_msm register --flags '<json>' (the tool currently parses the name style)\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 (acc_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)\nacc_msm exec <name> --list \u2014 list all MSMs\nacc_msm exec <name> --schema <n> \u2014 view a specific MSM's parameter schema\nacc_msm exec <name> --format=json \u2014 JSON output mode (remaining args passed through losslessly)\n";
|
|
14
21
|
/** CCC 配置参考(对齐 osp ccc-config action) */
|
|
15
|
-
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. handyman.models \u2500\u2500\nhandyman\uFF08\u6742\u5DE5\uFF09\u5DE5\u5177\u53EF\u7528\u6A21\u578B\u767D\u540D\u5355\uFF08provider/model \u5217\u8868\uFF09\uFF1B\u672A\u914D\u7F6E\u65F6 handyman \u62A5\u9519\u8981\u6C42\u914D\u7F6E\u3002\n\u7F3A\u7701\u6A21\u578B = models[0]\uFF08\u53EF\u7528 handyman.defaultModel \u6307\u5B9A\uFF0C\u5FC5\u987B \u2208 models\uFF09\u3002\n\n Config:\n { \"handyman\": { \"models\": [\"provider/model-name\"] } }\n\n Example:\n { \"handyman\": { \"models\": [\"minimax-cn-coding-plan/MiniMax-M3\"], \"defaultModel\": \"minimax-cn-coding-plan/MiniMax-M3\", \"maxRounds\": 100, \"maxParallel\": 10 } }\n\n\u2500\u2500 2. sessionKeeper.threshold \u2500\u2500\nSESSION-KEEPER \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 } }\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\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\uFF0Ccc_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 / blacklist \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 Config:\n { \"safeMode\": { \"blacklist\": [\".secrets/\"] } }\n";
|
|
22
|
+
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. handyman.models \u2500\u2500\nhandyman\uFF08\u6742\u5DE5\uFF09\u5DE5\u5177\u53EF\u7528\u6A21\u578B\u767D\u540D\u5355\uFF08provider/model \u5217\u8868\uFF09\uFF1B\u672A\u914D\u7F6E\u65F6 handyman \u62A5\u9519\u8981\u6C42\u914D\u7F6E\u3002\n\u7F3A\u7701\u6A21\u578B = models[0]\uFF08\u53EF\u7528 handyman.defaultModel \u6307\u5B9A\uFF0C\u5FC5\u987B \u2208 models\uFF09\u3002\n\n Config:\n { \"handyman\": { \"models\": [\"provider/model-name\"] } }\n\n Example:\n { \"handyman\": { \"models\": [\"minimax-cn-coding-plan/MiniMax-M3\"], \"defaultModel\": \"minimax-cn-coding-plan/MiniMax-M3\", \"maxRounds\": 100, \"maxParallel\": 10 } }\n\n\u2500\u2500 2. sessionKeeper.threshold \u2500\u2500\nSESSION-KEEPER \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 } }\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\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\uFF0Ccc_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 / blacklist \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 Config:\n { \"safeMode\": { \"blacklist\": [\".secrets/\"] } }\n\n\u2500\u2500 6. skiff.roles\uFF08F4 \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF09\u2500\u2500\nSkiff \u89D2\u8272 = \u5168\u77E5\u5168\u80FD 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\ntrajectory \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\uFF1Aacc_msm exec skiff_admin <guide|validate|apply|list>\u3002\n\n Config:\n { \"skiff\": { \"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. autopilotTrajectory\uFF08\u81EA\u52A8\u5DE1\u822A\u8F68\u8FF9\uFF09\u2500\u2500\nCCC \u5B9A\u4E49\u7684\u4E00\u6761\u81EA\u4E3B trajectory\u2014\u2014\u65F6\u949F\u5230\u70B9\u81EA\u52A8\u5524\u8D77\uFF08\u524D\u53F0\u6CE8\u5165\uFF0C\u7528\u6237\u53EF\u89C1\u53EF\u4ECB\u5165\uFF09\u3002\n\u672A\u914D\u7F6E\u6216 enabled=false \u2192 \u5B8C\u5168\u4E0D\u542F\u52A8\uFF08\u96F6\u8D44\u6E90\u5360\u7528\uFF09\u3002\u591A CCC \u72EC\u7ACB\uFF1A\u6BCF CCC \u81EA\u5DF1\u7684\u914D\u7F6E\u3002\n\u5524\u8D77\u6D88\u606F\u56DB\u6BB5\u5F0F\uFF1A\u8F68\u8FF9\u7126\u70B9 topPrompt\uFF08\u6700\u5148\u6CE8\u5165\uFF0C\u7A33\u5B9A\u951A\uFF09\u2192 \u8EAB\u4EFD\u951A\u5B9A \u2192 \u5148\u9A8C\u504F\u89C1\n\uFF08CCC \u6839\u811A\u672C biasProvider \u8F93\u51FA\uFF09\u2192 \u4EFB\u52A1\u3002\u76EE\u6807\u4F1A\u8BDD session \u5FC5\u586B\uFF08\u76EE\u5F55\u987B\u5E26 --auto \u540E\u7F00\uFF09\u3002\n\u8BCA\u65AD/\u72B6\u6001/\u7ACB\u5373\u5524\u8D77\uFF1Aacc_msm exec autopilot-trajectory <all|check|status|diag>\u3002\n\n Config:\n { \"autopilotTrajectory\": {\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 skiff.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\uFF1Bacc_msm exec weixin-doctor\n<status|diag|verify> \u6392\u67E5\u3002\u51ED\u636E\u4E3B\u52A8\u67E5\u770B\uFF1Alocalstore get WEIXIN_<ACCOUNT>_TOKEN\u3002\n\n Config:\n { \"weixin\": {\n \"enabled\": true,\n \"hook\": \"scripts/weixin-message-hook.ts\",\n \"accounts\": [{ \"accountId\": \"wechat-1\", \"name\": \"\u5BB6\u5EAD\u52A9\u624B\", \"enabled\": true }],\n \"routes\": [{ \"user\": \"*\", \"role\": \"zhaocai\" }]\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\u56DE\u590D\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\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\": \"zhaocai\",\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\" }\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";
|
|
16
23
|
export interface MsmFlag {
|
|
17
24
|
name: string;
|
|
18
25
|
type?: string;
|
|
@@ -43,6 +50,13 @@ export interface MsmArgs {
|
|
|
43
50
|
usage?: string;
|
|
44
51
|
}
|
|
45
52
|
export declare function parseRegistry(raw: string): MsmEntry[];
|
|
53
|
+
/**
|
|
54
|
+
* 注册表单级化(需求⑤a S142 用户拍板:注册表只有一级——对齐 osp cccName 单聚合档):
|
|
55
|
+
* 所有 entry 集中写入 `.opencode/skills/<cccName>/references/mech-registry.json`
|
|
56
|
+
* (cccName = .serenity 首行,即顶层入口 skill 目录),skill 只作 entry 字段。
|
|
57
|
+
* 不再扫描/写入各 skill 目录的分散注册表(历史形态 v1.14~v1.27,register --skill
|
|
58
|
+
* 曾写入各自 skill 目录 —— 多真相源,废弃)。
|
|
59
|
+
*/
|
|
46
60
|
export declare function findRegistries(root: string): string[];
|
|
47
61
|
export declare function loadMsmEntries(root: string): MsmEntry[];
|
|
48
62
|
export declare function findEntry(root: string, name: string): MsmEntry | null;
|
package/lib/rebuild.d.ts
CHANGED
|
@@ -69,6 +69,10 @@ export declare function resolveSessionMdPath(root: string, scope: string, sessio
|
|
|
69
69
|
interface PendingRebuild {
|
|
70
70
|
/** 锚点消息文本 */
|
|
71
71
|
anchor: string;
|
|
72
|
+
/** 需求②:重建后重命名标题用的内容概括(≤20 字;服务端清洗/截断) */
|
|
73
|
+
summary: string;
|
|
74
|
+
/** 持久轨迹 SESSION.md(重建后重命名标题的编号/日期来源) */
|
|
75
|
+
mdPath: string;
|
|
72
76
|
/** 排队时间(防陈旧队列误清空——超时丢弃) */
|
|
73
77
|
queuedAt: number;
|
|
74
78
|
}
|
|
@@ -84,6 +88,7 @@ export declare function pendingRebuildSnapshot(): ReadonlyMap<string, PendingReb
|
|
|
84
88
|
export declare function queueRebuild(ctx: Context, opts: {
|
|
85
89
|
root: string;
|
|
86
90
|
note?: string;
|
|
91
|
+
summary: string;
|
|
87
92
|
agentCwd: string;
|
|
88
93
|
dshSessionId: string;
|
|
89
94
|
}): Promise<RebuildResult>;
|
package/lib/seams/guards.d.ts
CHANGED
|
@@ -32,6 +32,20 @@ export interface GuardInput {
|
|
|
32
32
|
* 含 API keys/tokens/SSH 密码,即使 prompt 纪律失败也无值可吐(机械保证)。
|
|
33
33
|
*/
|
|
34
34
|
export declare const SENSITIVE_CREDENTIAL_FILES: Set<string>;
|
|
35
|
+
/**
|
|
36
|
+
* MSM 注册表写保护(需求⑤b S142 用户拍板:"msm注册的文件需要写保护起来,避免CCC意外写坏搞崩自己"):
|
|
37
|
+
* mech-registry.json 是 CCC 执行层地基(坏 → loadMsmEntries 抛 → acc_msm/skiff/output-guard 全崩,
|
|
38
|
+
* register 也崩 → 自锁)。**写 deny、读 allow**(与 localstore 读 deny 语义区分 R6——注册表是
|
|
39
|
+
* 结构核心不是秘密,需被 output-guard/skiff-admin 读取建 MSM 词表)。
|
|
40
|
+
*
|
|
41
|
+
* 单级化(需求⑤a)后**唯一合法注册表 = cccName 聚合档**
|
|
42
|
+
* `.opencode/skills/<cccName>/references/mech-registry.json`(cccName = .serenity 首行)。
|
|
43
|
+
* 历史 root 级 `mech-registry.json` 与各 skill 分散注册表已废弃:**不再保护**
|
|
44
|
+
* (review P1:保护一个永不被读的文件 = 死锁——MSM 既不可见又不可删/迁移)。
|
|
45
|
+
* 写工具命中聚合档 → deny(唯一合法写通道 = acc_msm register/deregister 内部 writeRegistry,
|
|
46
|
+
* 走工具实现不经 pre-execute → 天然豁免);读工具放行。
|
|
47
|
+
*/
|
|
48
|
+
export declare function isProtectedRegistryRel(root: string, rel: string): boolean;
|
|
35
49
|
/** 判定工具是否为读类(read/grep/glob + cc_fs 只读子命令):凭据文件对读工具同样 deny */
|
|
36
50
|
export declare function isReadTool(toolName: string, action?: string): boolean;
|
|
37
51
|
/** 判定工具是否为写类:普通工具按名;cc_fs 复合工具按子命令 action */
|
package/lib/seams/keeper.d.ts
CHANGED
|
@@ -37,8 +37,11 @@ export declare function reminderText(code: string, score: number): string;
|
|
|
37
37
|
*
|
|
38
38
|
* escalated=true(v1.23.3):连续多轮超阈值仍未 rebuild → 升级强制语气
|
|
39
39
|
* (STOP and rebuild now,持续注入直到调用 session_rebuild)。
|
|
40
|
+
*
|
|
41
|
+
* 需求①(S142 用户拍板):百分比比例 → K 数值——tokensK = 实际占用(千 token),
|
|
42
|
+
* thresholdK = 配置阈值(千 token);文案 `Context usage at NNNK (threshold NNNK)`。
|
|
40
43
|
*/
|
|
41
|
-
export declare function rebuildReminderText(
|
|
44
|
+
export declare function rebuildReminderText(tokensK: number, thresholdK: number, escalated?: boolean): string;
|
|
42
45
|
/** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
|
|
43
46
|
export declare function readContextPressure(ctx: Context, session: unknown): {
|
|
44
47
|
projectedTokens: number;
|
|
@@ -25,8 +25,16 @@ export declare function readPersonaSettings(): {
|
|
|
25
25
|
mode: string;
|
|
26
26
|
overrideText: string;
|
|
27
27
|
};
|
|
28
|
-
/** 1) ACC
|
|
29
|
-
export declare function
|
|
28
|
+
/** 1) 身份块:ACC 身份 + CCC 名 + 平台工具说明(需求③ S142:工具清单移出为独立 toolsBlock 放装配末尾——身份先行、工具参考殿后) */
|
|
29
|
+
export declare function identityBlock(root: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* 工具清单块(需求③ S142 用户拍板:工具列表移装配末尾 + MSM 调用示例):
|
|
32
|
+
* 原 accBlock 内嵌 13 行工具清单 → 独立成块放装配末尾(SKILL 后、Session 前)。
|
|
33
|
+
* 身份先行(认知轨迹开头不被 13 行清单干扰)、工具参考殿后(需要时再看)。
|
|
34
|
+
* 附带 MSM 调用示例(用户拍板:顶层提示词加调用方式说明,避免偶发调用错误——
|
|
35
|
+
* 模型对 acc_msm 参数面/协议 flag 理解不稳)。
|
|
36
|
+
*/
|
|
37
|
+
export declare function toolsBlock(): string;
|
|
30
38
|
/** 2) CCE 块:逐字对齐 osp(CCE 5 行为约束 + H_op 操作熵) */
|
|
31
39
|
export declare function cceBlock(): string;
|
|
32
40
|
/**
|
|
@@ -85,8 +93,11 @@ export declare function sessionBlock(root: string, scope?: string): string;
|
|
|
85
93
|
/**
|
|
86
94
|
* 完整系统提示词注入文本(v1.19.8 结构精简,S142 重建视角 R↓):
|
|
87
95
|
* 身份(ACC)→ 世界模型(Metaphor)→ 信念/边界(Principles)→ 时间约束(CCE)
|
|
88
|
-
* → 质量(EAP)→ 状态(SafeMode/Localstore)→ CCC 上下文(SKILL)→ 会话(Session)。
|
|
89
|
-
* 认知展开顺序:我是谁 → 我所在的世界 → 为什么 → 如何一致 → 产物标准 → 当前状态 →
|
|
96
|
+
* → 质量(EAP)→ 状态(SafeMode/Localstore)→ CCC 上下文(SKILL)→ 工具参考(Tools)→ 会话(Session)。
|
|
97
|
+
* 认知展开顺序:我是谁 → 我所在的世界 → 为什么 → 如何一致 → 产物标准 → 当前状态 → 上下文 → 工具参考 → 会话。
|
|
98
|
+
*
|
|
99
|
+
* 需求③(S142 用户拍板):工具清单从 accBlock 移出为独立 toolsBlock,
|
|
100
|
+
* 放 SKILL 后 Session 前(工具参考殿后,身份先行不被 13 行清单干扰)。
|
|
90
101
|
*
|
|
91
102
|
* v1.23.1 persona(彩蛋):persona.mode 配置 → EAP 块替换为 Persona 块(输出约束),
|
|
92
103
|
* Principles 剥离 MSM 原则段(指令遵循约束)——用户文本承接两处风格;本体论/关系段/
|
|
@@ -2058,7 +2058,7 @@ const SERENITY_SETTINGS_NS = "serenity-hooks";
|
|
|
2058
2058
|
const simpleSettingsSchema = z.object({
|
|
2059
2059
|
gatewayEnabled: z.boolean().default(false),
|
|
2060
2060
|
rebuildEnabled: z.boolean().default(true),
|
|
2061
|
-
|
|
2061
|
+
rebuildThresholdK: z.number().min(50).max(4e3).default(400),
|
|
2062
2062
|
skiffEnabled: z.boolean().default(false),
|
|
2063
2063
|
skiffDebugPort: z.number().min(1024).max(65535).default(3099),
|
|
2064
2064
|
acpEnabled: z.boolean().default(false),
|
|
@@ -2071,7 +2071,7 @@ function entryDefaults(config) {
|
|
|
2071
2071
|
return {
|
|
2072
2072
|
gatewayEnabled: config.gateway?.enabled ?? false,
|
|
2073
2073
|
rebuildEnabled: config.rebuild?.enabled ?? true,
|
|
2074
|
-
|
|
2074
|
+
rebuildThresholdK: config.rebuild?.thresholdK ?? 400,
|
|
2075
2075
|
skiffEnabled: config.skiff?.enabled ?? false,
|
|
2076
2076
|
skiffDebugPort: config.skiff?.debugPort ?? 3099,
|
|
2077
2077
|
acpEnabled: config.acp?.enabled ?? false,
|
|
@@ -2087,7 +2087,7 @@ function defaultSimpleSettings() {
|
|
|
2087
2087
|
return {
|
|
2088
2088
|
gatewayEnabled: false,
|
|
2089
2089
|
rebuildEnabled: true,
|
|
2090
|
-
|
|
2090
|
+
rebuildThresholdK: 400,
|
|
2091
2091
|
skiffEnabled: false,
|
|
2092
2092
|
skiffDebugPort: 3099,
|
|
2093
2093
|
acpEnabled: false,
|
|
@@ -23,7 +23,7 @@ export interface SimpleConfigFragment {
|
|
|
23
23
|
};
|
|
24
24
|
rebuild?: {
|
|
25
25
|
enabled?: boolean;
|
|
26
|
-
|
|
26
|
+
thresholdK?: number;
|
|
27
27
|
};
|
|
28
28
|
/** F4 Skiff(实验性):调试服务启停(人工) */
|
|
29
29
|
skiff?: {
|
|
@@ -51,8 +51,8 @@ export interface SerenitySimpleSettings {
|
|
|
51
51
|
gatewayEnabled: boolean;
|
|
52
52
|
/** F2 超限重建总开关 */
|
|
53
53
|
rebuildEnabled: boolean;
|
|
54
|
-
/** F2
|
|
55
|
-
|
|
54
|
+
/** F2 触发阈值(需求① S142 用户拍板:百分比比例 → K 数值;projectedTokens ≥ thresholdK*1000 触发,纯绝对无窗口比例保护) */
|
|
55
|
+
rebuildThresholdK: number;
|
|
56
56
|
/** F4 Skiff 调试服务总开关(实验性;默认关——不随插件加载自动启动,人工开启) */
|
|
57
57
|
skiffEnabled: boolean;
|
|
58
58
|
/** F4 Skiff 调试端口(默认 3099,仅 127.0.0.1) */
|
|
@@ -71,7 +71,7 @@ export interface SerenitySimpleSettings {
|
|
|
71
71
|
export declare const simpleSettingsSchema: z<Schemastery.ObjectS<{
|
|
72
72
|
gatewayEnabled: z<boolean, boolean>;
|
|
73
73
|
rebuildEnabled: z<boolean, boolean>;
|
|
74
|
-
|
|
74
|
+
rebuildThresholdK: z<number, number>;
|
|
75
75
|
skiffEnabled: z<boolean, boolean>;
|
|
76
76
|
skiffDebugPort: z<number, number>;
|
|
77
77
|
acpEnabled: z<boolean, boolean>;
|
|
@@ -81,7 +81,7 @@ export declare const simpleSettingsSchema: z<Schemastery.ObjectS<{
|
|
|
81
81
|
}>, Schemastery.ObjectT<{
|
|
82
82
|
gatewayEnabled: z<boolean, boolean>;
|
|
83
83
|
rebuildEnabled: z<boolean, boolean>;
|
|
84
|
-
|
|
84
|
+
rebuildThresholdK: z<number, number>;
|
|
85
85
|
skiffEnabled: z<boolean, boolean>;
|
|
86
86
|
skiffDebugPort: z<number, number>;
|
|
87
87
|
acpEnabled: z<boolean, boolean>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { a as findSerenityRoot,
|
|
3
|
-
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, n as buildSkiffBasePrompt, o as roleMsmWhitelist, r as isSkiffSessionId, u as trajectorySubset } from "./skiff-role-
|
|
2
|
+
import { a as findSerenityRoot, f as readHandymanConfig } from "./ccc-CX48YNUL.js";
|
|
3
|
+
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, n as buildSkiffBasePrompt, o as roleMsmWhitelist, r as isSkiffSessionId, u as trajectorySubset } from "./skiff-role-BTdBHyOQ.js";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, join } from "node:path";
|
|
6
6
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
2
|
+
import { m as resolveInside, p as readUtf8, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-CX48YNUL.js";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
//#region src/skiff-role.ts
|
|
5
5
|
/**
|
package/lib/tools/session.d.ts
CHANGED
|
@@ -22,18 +22,34 @@ export interface RenameOnUseDeps {
|
|
|
22
22
|
sessionTitleAvailable: boolean;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* 清洗 + 截断会话概括(需求② S142 用户拍板:编号日期后加 ≤20 字内容概括)。
|
|
26
|
+
* 规则(服务端统一,不信任 LLM 输入):
|
|
27
|
+
* - 去控制字符/换行/回车/制表(防标题注入/多行污染)
|
|
28
|
+
* - trim(去首尾空白)
|
|
29
|
+
* - 截断 ≤20 字符(按 Unicode 码点——中英混排统一;emoji 等代理对按码点保留)
|
|
30
|
+
* - 去 `/`(防标题被误读为路径分隔)
|
|
31
|
+
* @returns 清洗后的概括(空输入 → 空串;调用方决定是否允许空)
|
|
32
|
+
*/
|
|
33
|
+
export declare function sanitizeSessionSummary(summary: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* 从激活会话派生命名标题(v1.22.9 格式修正 + 需求② 概括):
|
|
26
36
|
* F3 原始需求是 **`S###-日期`**(如 `S143-2026-08-26`)——从 `sessionId` 派生,
|
|
27
37
|
* 而非完整目录名(`2026-08-24--S142--...` 超长 + 中文,不符合用户拍板格式)。
|
|
38
|
+
* 需求②(S142 用户拍板):编号日期后加 ≤20 字内容概括 → `S###-YYYY-MM-DD-<概括>`
|
|
39
|
+
* ——概括来自显式 summary 参数(服务端截断/清洗,可靠不靠猜);编号日期仍固定派生。
|
|
28
40
|
* 无 S### 编号(issue 会话等)→ 回退目录名。
|
|
29
|
-
* @
|
|
41
|
+
* @param active 激活会话信息(sessionId + dirName)
|
|
42
|
+
* @param summary 内容概括(≤20 字,服务端清洗截断;空 → 不带概括的 `S###-日期`)
|
|
43
|
+
* @returns `S143-2026-08-26-概括` / `S143-2026-08-26` / 原目录名
|
|
30
44
|
*/
|
|
31
|
-
export declare function namingTitleFor(active: ActiveSessionInfo): string;
|
|
45
|
+
export declare function namingTitleFor(active: ActiveSessionInfo, summary?: string): string;
|
|
32
46
|
/**
|
|
33
|
-
* use 激活宁静号会话后,把当前 dsh 会话重命名为命名标题(`S
|
|
47
|
+
* use 激活宁静号会话后,把当前 dsh 会话重命名为命名标题(`S###-日期[-概括]`)。
|
|
34
48
|
* v1.27.1:**永远开启**(不再有 naming.enabled 门控)——仅 sessionTitle 服务
|
|
35
49
|
* 存在性守卫;失败不静默——返回结果对象而非 null(v1.22.9),调用方决定可见性。
|
|
36
50
|
*
|
|
51
|
+
* 需求②(S142 用户拍板):summary 参数(≤20 字概括)→ 标题带概括;编号日期固定派生。
|
|
52
|
+
*
|
|
37
53
|
* v1.23.2 修复(this 绑定):第三参从**解构的裸 rename 函数**改为**整个
|
|
38
54
|
* sessionTitle 服务对象**——内部以 `titles.rename(session, title)` **方法调用**
|
|
39
55
|
* (this = titles 服务实例)。旧实现调用点 `const rename = titles.rename` 解构
|
|
@@ -44,7 +60,7 @@ export declare function namingTitleFor(active: ActiveSessionInfo): string;
|
|
|
44
60
|
*/
|
|
45
61
|
export declare function renameDshSessionOnUse(deps: RenameOnUseDeps, session: unknown, titles: {
|
|
46
62
|
rename: (session: unknown, title: string) => unknown;
|
|
47
|
-
} | undefined, active: ActiveSessionInfo): {
|
|
63
|
+
} | undefined, active: ActiveSessionInfo, summary?: string): {
|
|
48
64
|
ok: true;
|
|
49
65
|
title: string;
|
|
50
66
|
} | {
|
|
@@ -59,6 +75,7 @@ export declare function renameDshSessionOnUse(deps: RenameOnUseDeps, session: un
|
|
|
59
75
|
export declare function activeInfoFromCreate(result: CreateSessionResult): ActiveSessionInfo;
|
|
60
76
|
/**
|
|
61
77
|
* 把当前 dsh 会话重命名为指定 SESSION 的命名标题(use/create 共用;v1.25.11)。
|
|
78
|
+
* 需求②:summary 参数(≤20 字概括)透传——标题带概括(编号日期固定派生)。
|
|
62
79
|
* 门控/失败可见性与 renameDshSessionOnUse 一致(不静默:成功 log / 失败 warn)。
|
|
63
80
|
* 调用点(use 分支原内联逻辑提取,create 分支复用):
|
|
64
81
|
* - use:激活后从 activeStore 取 info
|
|
@@ -68,7 +85,7 @@ export declare function renameDshSessionForActive(ctx: Context, exec: {
|
|
|
68
85
|
agent?: {
|
|
69
86
|
session?: unknown;
|
|
70
87
|
};
|
|
71
|
-
}, info: ActiveSessionInfo): void;
|
|
88
|
+
}, info: ActiveSessionInfo, summary?: string): void;
|
|
72
89
|
/**
|
|
73
90
|
* 创建 session 工具(闭包捕获插件 ctx → use 后可调 ctx.sessionTitle.rename)。
|
|
74
91
|
* v1.21 F3:use 激活宁静号会话 → 当前 dsh 会话重命名为该 SESSION 目录名。
|
|
@@ -331,4 +331,4 @@ function markdownToPlainText(text) {
|
|
|
331
331
|
return result.trim();
|
|
332
332
|
}
|
|
333
333
|
//#endregion
|
|
334
|
-
export {
|
|
334
|
+
export { markdownToPlainText as a, sniffImageExt as c, getUpdates as i, weixin_api_exports as l, downloadMedia as n, sendTextMessage as o, getConfig as r, sendTyping as s, TypingStatus as t };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weixin-hook.ts — 微信桥消息记录 hook(F4c-3 扩展,v1.27.13 用户需求)
|
|
3
|
+
*
|
|
4
|
+
* 需求:微信桥发生的所有消息记录,支持 hook——允许 CCC 通过自行编写 hook
|
|
5
|
+
* 脚本进行**持久性保存**(存哪/存成什么/是否入库,全归 CCC 决定——ACC 不绑定存储)。
|
|
6
|
+
*
|
|
7
|
+
* 设计(docs/weixin-message-hook-design.md v0.1,三项拍板 H1~H6):
|
|
8
|
+
* - **双向触发**(H1):incoming(用户→bot,媒体落盘后)+ outgoing(bot→用户,发送成功后)
|
|
9
|
+
* - **脚本 + stdin JSON**(H2):serenity.json weixin.hook = CCC 根相对脚本路径;
|
|
10
|
+
* 每次消息事件 spawn 一次,事件 JSON 单行经 stdin 传入(bun 优先 / node 兜底,biasProvider 先例)
|
|
11
|
+
* - **旁路容忍**(H3):异步 fire-and-forget + 超时 kill + 失败仅日志——微信桥可靠性不受影响
|
|
12
|
+
* - **不含会话凭据**(H5):事件只有记录所需字段(无 context_token / bot_token——最小暴露面)
|
|
13
|
+
*
|
|
14
|
+
* 定位:Mech 纯逻辑(事件构造纯函数 + 执行器确定性),可独立单测。
|
|
15
|
+
*/
|
|
16
|
+
/** hook 执行超时(旁路记录足够;超时 kill 防挂死脚本) */
|
|
17
|
+
export declare const WEIXIN_HOOK_TIMEOUT_MS = 15000;
|
|
18
|
+
/** 媒体引用(hook 事件载荷:kind + 相对 CCC 根路径——CCC 可自行决定是否持久保存媒体) */
|
|
19
|
+
export interface WeixinHookMediaRef {
|
|
20
|
+
kind: 'image' | 'file';
|
|
21
|
+
/** 相对 CCC 根的落盘路径(_tmp/weixin-inbound/...);null = 未落盘 */
|
|
22
|
+
relPath: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** hook 事件公共字段(incoming/outgoing 共享) */
|
|
25
|
+
interface WeixinHookEventBase {
|
|
26
|
+
/** 事件类型:incoming(用户→bot)/ outgoing(bot→用户) */
|
|
27
|
+
event: 'incoming' | 'outgoing';
|
|
28
|
+
/** 事件时间(epoch ms) */
|
|
29
|
+
ts: number;
|
|
30
|
+
/** 触发 CCC 根(多 CCC 一进程可区分) */
|
|
31
|
+
cccRoot: string;
|
|
32
|
+
/** 接收账号(incoming)/ 发送账号(outgoing) */
|
|
33
|
+
accountId: string;
|
|
34
|
+
/** 对端用户(from_user_id,形如 userA@im.wechat) */
|
|
35
|
+
userId: string;
|
|
36
|
+
/** 固定会话 id(同用户长期同一:skiff-weixin-xxx) */
|
|
37
|
+
sessionId: string;
|
|
38
|
+
/** 路由命中的 skiff role */
|
|
39
|
+
role: string;
|
|
40
|
+
}
|
|
41
|
+
/** incoming 事件(用户 → bot):文本(含语音转写)+ 媒体(已落盘 relPath) */
|
|
42
|
+
export interface WeixinHookIncomingEvent extends WeixinHookEventBase {
|
|
43
|
+
event: 'incoming';
|
|
44
|
+
message: {
|
|
45
|
+
/** 文本(含语音服务端转写);无文本 → null */
|
|
46
|
+
text: string | null;
|
|
47
|
+
/** 媒体(图片/文件);无 → [] */
|
|
48
|
+
media: WeixinHookMediaRef[];
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** outgoing 事件(bot → 用户):回复文本(已剥离 think) */
|
|
52
|
+
export interface WeixinHookOutgoingEvent extends WeixinHookEventBase {
|
|
53
|
+
event: 'outgoing';
|
|
54
|
+
reply: string;
|
|
55
|
+
}
|
|
56
|
+
/** 全部 hook 事件(判别联合) */
|
|
57
|
+
export type WeixinHookEvent = WeixinHookIncomingEvent | WeixinHookOutgoingEvent;
|
|
58
|
+
/** 事件载荷(JSON 序列化:不含任何会话凭据——H5) */
|
|
59
|
+
export type WeixinHookEventJson = WeixinHookEvent;
|
|
60
|
+
/** incoming 事件构造输入(bridge 提供实参) */
|
|
61
|
+
export interface IncomingHookInput {
|
|
62
|
+
cccRoot: string;
|
|
63
|
+
accountId: string;
|
|
64
|
+
userId: string;
|
|
65
|
+
sessionId: string;
|
|
66
|
+
role: string;
|
|
67
|
+
text: string | null;
|
|
68
|
+
media: WeixinHookMediaRef[];
|
|
69
|
+
}
|
|
70
|
+
/** outgoing 事件构造输入(bridge 提供实参) */
|
|
71
|
+
export interface OutgoingHookInput {
|
|
72
|
+
cccRoot: string;
|
|
73
|
+
accountId: string;
|
|
74
|
+
userId: string;
|
|
75
|
+
sessionId: string;
|
|
76
|
+
role: string;
|
|
77
|
+
reply: string;
|
|
78
|
+
}
|
|
79
|
+
/** 构造 incoming 事件对象(纯函数,可测) */
|
|
80
|
+
export declare function buildIncomingHookEvent(input: IncomingHookInput): WeixinHookIncomingEvent;
|
|
81
|
+
/** 构造 outgoing 事件对象(纯函数,可测) */
|
|
82
|
+
export declare function buildOutgoingHookEvent(input: OutgoingHookInput): WeixinHookOutgoingEvent;
|
|
83
|
+
/** hook 执行结果(测试/日志用;调用方只关心无异常) */
|
|
84
|
+
export interface WeixinHookRunResult {
|
|
85
|
+
ok: boolean;
|
|
86
|
+
detail?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 执行一次 hook:spawn CCC 根脚本,事件 JSON 单行经 stdin 传入。
|
|
90
|
+
* 旁路容忍(H3):超时 kill / 非 0 退出 / spawn 失败 → 仅日志返回 {ok:false},绝不抛(不阻断微信桥)。
|
|
91
|
+
* 路径逃逸校验:脚本必须解析在 CCC 根内(resolveInside——resolveInside 抛错也吞为 {ok:false})。
|
|
92
|
+
* 脚本缺失 → {ok:false}(配置了但未实现——日志提示;不视为异常)。
|
|
93
|
+
*/
|
|
94
|
+
export declare function runWeixinHook(root: string, hookRel: string, event: WeixinHookEvent, timeoutMs?: number): Promise<WeixinHookRunResult>;
|
|
95
|
+
/** 测试辅助:把 WeixinHookEvent 转单行 JSON(脚本侧读取约定) */
|
|
96
|
+
export declare function hookEventToStdinLine(event: WeixinHookEvent): string;
|
|
97
|
+
/** hook runner 签名(可注入——bridge 集成测试捕获事件防真实 spawn flake) */
|
|
98
|
+
export type WeixinHookRunner = (root: string, hookRel: string, event: WeixinHookEvent) => Promise<WeixinHookRunResult>;
|
|
99
|
+
/** 测试辅助:替换 runner(null → 还原真实 spawn 执行);生产零调用 */
|
|
100
|
+
export declare function setWeixinHookRunnerForTest(runner: WeixinHookRunner | null): void;
|
|
101
|
+
/** bridge 调用入口(转发 activeRunner——默认真实 spawn;测试可注入捕获) */
|
|
102
|
+
export declare function invokeWeixinHook(root: string, hookRel: string, event: WeixinHookEvent): Promise<WeixinHookRunResult>;
|
|
103
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
2
|
+
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-CX48YNUL.js";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -340,6 +340,7 @@ function readWeixinSettings(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
340
340
|
return {
|
|
341
341
|
enabled: w.enabled === true,
|
|
342
342
|
botType: typeof w.botType === "string" && w.botType !== "" ? w.botType : void 0,
|
|
343
|
+
hook: typeof w.hook === "string" && w.hook.trim() !== "" ? w.hook.trim() : void 0,
|
|
343
344
|
accounts: Array.isArray(w.accounts) ? w.accounts.filter((a) => typeof a === "object" && a !== null && typeof a.accountId === "string" && a.accountId !== "").map((a) => ({
|
|
344
345
|
accountId: a.accountId,
|
|
345
346
|
name: typeof a.name === "string" ? a.name : void 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.0",
|
|
4
4
|
"description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|