@shgroup/dsh-serenity-hooks 1.26.14 → 1.26.16

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.
Files changed (58) hide show
  1. package/dsh.plugin.json +1 -1
  2. package/lib/acp-core.d.ts +100 -0
  3. package/lib/acp-http.d.ts +29 -0
  4. package/lib/api.d.ts +47 -0
  5. package/lib/autotrajectory.d.ts +186 -0
  6. package/lib/ccc-CfDrlfA7.js +141 -0
  7. package/lib/ccc.d.ts +154 -0
  8. package/lib/config-ops.d.ts +166 -0
  9. package/lib/constants.d.ts +8 -0
  10. package/lib/fs-ops.d.ts +36 -0
  11. package/lib/gateway-auth.d.ts +89 -0
  12. package/lib/gateway-proxy.d.ts +39 -0
  13. package/lib/gateway.d.ts +80 -0
  14. package/lib/git-ops.d.ts +28 -0
  15. package/lib/handyman-ops.d.ts +68 -0
  16. package/lib/handyman-preset-inherit.d.ts +34 -0
  17. package/lib/index.d.ts +68 -0
  18. package/lib/index.js +2172 -1992
  19. package/lib/invariant.d.ts +29 -0
  20. package/lib/json.d.ts +9 -0
  21. package/lib/kit-ops.d.ts +23 -0
  22. package/lib/localstore-ops.d.ts +85 -0
  23. package/lib/msm-ops.d.ts +78 -0
  24. package/lib/output-guard-seam.d.ts +21 -0
  25. package/lib/output-guard.d.ts +68 -0
  26. package/lib/rebuild.d.ts +118 -0
  27. package/lib/seams/bootstrap.d.ts +83 -0
  28. package/lib/seams/compact.d.ts +31 -0
  29. package/lib/seams/context.d.ts +48 -0
  30. package/lib/seams/env.d.ts +16 -0
  31. package/lib/seams/guards.d.ts +72 -0
  32. package/lib/seams/keeper.d.ts +61 -0
  33. package/lib/seams/opencode-skills.d.ts +14 -0
  34. package/lib/seams/system-prompt.d.ts +113 -0
  35. package/lib/session-ops.d.ts +127 -0
  36. package/lib/settings-section.d.ts +113 -0
  37. package/lib/skiff-core.d.ts +78 -0
  38. package/lib/skiff-debug.d.ts +86 -0
  39. package/lib/skiff-registry.d.ts +25 -0
  40. package/lib/skiff-role.d.ts +50 -0
  41. package/lib/skills/opencode-scan.d.ts +27 -0
  42. package/lib/skills-discovery.d.ts +25 -0
  43. package/lib/status.d.ts +38 -0
  44. package/lib/tools/autotrajectory-exp.d.ts +24 -0
  45. package/lib/tools/cc-fs.d.ts +7 -0
  46. package/lib/tools/cce.d.ts +9 -0
  47. package/lib/tools/eap.d.ts +7 -0
  48. package/lib/tools/git.d.ts +4 -0
  49. package/lib/tools/handyman.d.ts +43 -0
  50. package/lib/tools/kit.d.ts +4 -0
  51. package/lib/tools/localstore.d.ts +8 -0
  52. package/lib/tools/msm.d.ts +4 -0
  53. package/lib/tools/neat.d.ts +5 -0
  54. package/lib/tools/rebuild.d.ts +19 -0
  55. package/lib/tools/session.d.ts +78 -0
  56. package/lib/tools/skiff-admin.d.ts +25 -0
  57. package/lib/totp.d.ts +40 -0
  58. package/package.json +6 -4
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.26.14",
3
+ "version": "1.26.16",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/handyman/session_rebuild/skiff_admin + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)+ Skiff 认知子集角色(实验性)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
@@ -0,0 +1,100 @@
1
+ /**
2
+ * acp-core.ts — ACP 会话管理层(F4c,v1.26.0 实验性)
3
+ *
4
+ * **传输无关**:方法处理器(initialize/session/new/prompt/cancel/list/close/request_permission)
5
+ * 不依赖任何传输(stdio/HTTP/直调)——企业微信桥同进程直调处理器函数,
6
+ * acp-http 端点(外部程序化面)与未来 stdio 独立进程复用同一处理器。
7
+ *
8
+ * Skiff 映射(S142 用户拍板:指定 认知容器+角色+会话(可选)进行对话):
9
+ * - session/new { ccc, role, sessionId? } → createSkiffAgent / getSkiffAgent 复用
10
+ * (无 sessionId → 新建;有 → 进程内延续——与 v1.25.10 调试页同语义)
11
+ * - session/prompt → askSkiff(agent, question, 0)(全量轨迹)
12
+ * - session/cancel → agent.interrupt(DSH 能力)
13
+ * - session/close → unregisterSkiffSession(释放 agent + 注册表清理)
14
+ * - session/request_permission → 恒 allow(G9:白名单即授权)
15
+ *
16
+ * 实验性质:仅当 ACP 服务装配(settings acpEnabled)且 CCC 配置了角色时才创建 agent;
17
+ * 未启用时本模块零副作用(无监听、无 agent)。
18
+ */
19
+ import type { Context } from 'cordis';
20
+ import type { SessionId } from '@deepseek-ai/dsh-session';
21
+ export interface JsonRpcRequest {
22
+ jsonrpc: '2.0';
23
+ id?: string | number;
24
+ method: string;
25
+ params?: Record<string, unknown>;
26
+ }
27
+ export interface JsonRpcResponse {
28
+ jsonrpc: '2.0';
29
+ id: string | number | null;
30
+ result?: unknown;
31
+ error?: {
32
+ code: number;
33
+ message: string;
34
+ data?: unknown;
35
+ };
36
+ }
37
+ export interface JsonRpcNotification {
38
+ jsonrpc: '2.0';
39
+ method: string;
40
+ params?: Record<string, unknown>;
41
+ }
42
+ /** JSON-RPC 错误码(对齐标准) */
43
+ export declare const RPC_ERROR: {
44
+ readonly PARSE: -32700;
45
+ readonly INVALID_REQUEST: -32600;
46
+ readonly METHOD_NOT_FOUND: -32601;
47
+ readonly INVALID_PARAMS: -32602;
48
+ readonly INTERNAL: -32603;
49
+ };
50
+ export interface AcpSessionHandle {
51
+ sessionId: string;
52
+ role: string;
53
+ ccc: string;
54
+ /** 会话是否可复用(进程内延续) */
55
+ continued: boolean;
56
+ }
57
+ /**
58
+ * ACP 会话管理器:持有 skiff agent 会话(与 skiff-core 注册表同源——
59
+ * 创建走 createSkiffAgent(自动注册),关闭走 unregisterSkiffSession)。
60
+ * 传输无关:HTTP 端点/企业微信桥/未来 stdio 都调 handle(method, params)。
61
+ */
62
+ export declare class AcpServer {
63
+ private readonly ctx;
64
+ constructor(ctx: Context);
65
+ /** 实现级方法调度(返回 result 或抛 JSON-RPC 语义错误) */
66
+ handle(method: string, params: Record<string, unknown> | undefined): Promise<unknown>;
67
+ private initialize;
68
+ /**
69
+ * session/new:{ ccc, role, sessionId? } → 创建或延续 skiff 会话。
70
+ * 无 sessionId → createSkiffAgent(新会话,continued:false);
71
+ * 有 → 进程内延续(getSkiffAgent 命中 + role/ccc 绑定校验;未命中 → 错误)。
72
+ */
73
+ private newSession;
74
+ private listSessions;
75
+ /**
76
+ * session/prompt:{ sessionId, question } → 答案(**v1.26.10:不返回 trajectory**——
77
+ * 3100 对外只提供问答,轨迹含工具结果等内部信息;3099 调试页另走 /ask 保留)
78
+ */
79
+ private prompt;
80
+ /** session/cancel:{ sessionId } → 中断当前 prompt(DSH interrupt) */
81
+ private cancel;
82
+ /** session/close:{ sessionId } → 释放 agent + 注册表清理(进程内) */
83
+ private closeSession;
84
+ }
85
+ /** 方法不存在错误(JSON-RPC -32601) */
86
+ export declare class RpcMethodError extends Error {
87
+ constructor(message: string);
88
+ }
89
+ /** 参数错误(JSON-RPC -32602) */
90
+ export declare class RpcInvalidParams extends Error {
91
+ constructor(message: string);
92
+ }
93
+ /**
94
+ * JSON-RPC 2.0 批处理(单帧):解析请求/通知 → 调 AcpServer.handle → 响应。
95
+ * 传输无关:acp-http 端点 / 未来 stdio 共用。
96
+ * @returns 响应帧数组(请求有响应;通知无;解析失败 → 单错误响应)
97
+ */
98
+ export declare function dispatchRpc(server: AcpServer, frame: unknown): Promise<JsonRpcResponse[]>;
99
+ /** 断言 SessionId 类型(ACP 会话 id 即 skiff sessionId) */
100
+ export type { SessionId };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * acp-http.ts — ACP HTTP JSON-RPC + 建议问答页(F4c/F4d,v1.26.x 实验性)
3
+ *
4
+ * node:http 服务(默认关,仅监听 127.0.0.1;启停 = 人工——设置面板「Serenity」
5
+ * 页 ACP 区块开关 / 问答页开关,不随插件加载自动启动;仿 skiff 调试服务模式)。
6
+ *
7
+ * - POST / → JSON-RPC 2.0 单帧/批处理(acpEnabled 门控;ACP 程序化面)
8
+ * - GET / → 容器列表页(publicAskEnabled 门控;v1.26.2:只列已开放容器)
9
+ * - GET /c/<name> → 单容器问答页(v1.26.2:URL 体现容器名,用户只输 key)
10
+ * - POST /c/<name>/ask → 该容器问答(key 校验 + allowed 白名单校验)
11
+ * - POST /ask → 兼容旧形态 { key, ccc, ... }(key 校验 + allowed 校验)
12
+ *
13
+ * 与 skiff 调试页(人工测试面)共享同一会话核心(acp-core → skiff-core);
14
+ * 企业微信桥(F4c-2)同进程直调 acp-core 处理器函数,不经本端点。
15
+ * stdio 形态(独立进程部署)后续复用同一 acp-core(官方 NDJSON 帧格式)。
16
+ *
17
+ * 实验性质:未开启时零资源占用(无监听、无 agent 创建)。
18
+ */
19
+ import type { Context } from 'cordis';
20
+ export declare function acpHttpActive(): boolean;
21
+ export declare function acpHttpPort(): number | null;
22
+ /**
23
+ * 启动 ACP HTTP JSON-RPC + 建议问答页服务(单实例;重复启动幂等返回既有实例)。
24
+ * @param ctx 插件上下文(AcpServer 内部经 skiff-core 创建 agent)
25
+ * @param port 监听端口(仅 127.0.0.1)
26
+ * @param defaultRoot 默认 CCC 根(问答页候选发现兜底;resolveSkiffRoot 传入)
27
+ */
28
+ export declare function startAcpHttpServer(ctx: Context, port: number, defaultRoot?: string): Promise<void>;
29
+ export declare function stopAcpHttpServer(): void;
package/lib/api.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * api.ts — HTTP 状态接口(WebUI 停靠栏数据源/开关通道 + 图片落盘)
3
+ *
4
+ * 路由:
5
+ * GET /serenity/status ?workspace=<路径> → 状态(root/accVersion/safeModeOn/...)
6
+ * POST /serenity/status { workspace, on } → 切换安全模式(WebUI 专属头)
7
+ * GET /serenity/handymen → handyman 运行状态(v1.24.0:loop → handyman)
8
+ * POST /serenity/image-upload { workspace?, mediaType, data(base64) } → 图片写 _tmp/images_from_user/(client 专属)
9
+ *
10
+ * 经 ctx.webServer(webserver 服务,公开版由 httpServer 改名)注册;客户端与服务器同源,调用无信任围栏问题。
11
+ */
12
+ import type { Context } from 'cordis';
13
+ /** 图片落盘目录(CCC 根相对;S142 图片自动识别基础设施——粘贴图片落盘供 agent 经 CCC vlm MSM 自主处理) */
14
+ export declare const IMAGE_UPLOAD_DIR = "_tmp/images_from_user";
15
+ export declare const IMAGE_MEDIA_TYPES: Set<string>;
16
+ /** 文件落盘目录(CCC 根相对;v1.24.1 粘贴任意文件自动落盘——agent 经 CCC 既有 MSM(pdf-extract/archive-extract 等)自主处理) */
17
+ export declare const FILE_UPLOAD_DIR = "_tmp/files_from_user";
18
+ /** 拒绝的可执行/危险扩展名(安全边界:不落盘可执行文件,防 agent 被诱导执行) */
19
+ export declare const BLOCKED_FILE_EXTS: Set<string>;
20
+ /**
21
+ * 图片落盘核心逻辑(可测):校验 mediaType 白名单 + base64 解码 + 大小上限 →
22
+ * 写 CCC 根 _tmp/images_from_user/<ts>-<rand>.<ext>,返回相对路径。
23
+ * 校验失败抛 Error(handler 转 400)。
24
+ */
25
+ export declare function saveImageToTmp(root: string, mediaType: string, data: string): string;
26
+ /**
27
+ * 文件名脱敏(路径逃逸 + 非法字符):取 basename(去 / 与 \),剥离前导点/空,
28
+ * 非法字符 → '-',限长。返回空则 'file'。
29
+ */
30
+ export declare function sanitizeFileName(name: string): string;
31
+ /**
32
+ * 任意文件落盘核心逻辑(可测):文件名校验 + 可执行扩展名拒绝 + base64 解码 +
33
+ * 大小上限(10MB)→ 写 CCC 根 _tmp/files_from_user/<ts>-<rand>-<safeName>,返回相对路径。
34
+ * 校验失败抛 Error(handler 转 400)。
35
+ */
36
+ export declare function saveFileToTmp(root: string, fileName: string, data: string): string;
37
+ export interface StatusApiRegistration {
38
+ configPaths?: string[];
39
+ }
40
+ /**
41
+ * 从请求参数解析 workspace:优先 sessionId → 会话 header.cwd;其次 workspace 参数;
42
+ * 无参 → 遍历 live sessions 找第一个 CCC 会话的 cwd(远程访问时 sessionId 可能缺失,
43
+ * 回退 process.cwd()=$HOME 会错误显示"未激活"——v1.22.2 修复);最后进程 cwd。
44
+ * 纯逻辑(可单测):resolveWorkspaceCore(sessionCwd, workspaceParam, listCwds)。
45
+ */
46
+ export declare function resolveWorkspaceCore(sessionCwd: string | undefined, workspaceParam: string | undefined, listCwds: () => string[], fallback: string): string;
47
+ export declare function registerStatusApi(ctx: Context, opts?: StatusApiRegistration): void;
@@ -0,0 +1,186 @@
1
+ /**
2
+ * autotrajectory.ts — 自主轨迹(Self-Sustaining Trajectory)验证机制(v1.26.12)
3
+ *
4
+ * 依据:serenity-acc-specs/docs/self-sustaining-trajectory-hypothesis.md(v0.1 实验提案;
5
+ * S142 2026-08-30 用户提出猜想 + 三项裁决:① 时钟驱动 ② 先验偏见自生+随机(随机充分引入)
6
+ * ③ 人类=反馈来源(不直接参与))。
7
+ *
8
+ * 形态(用户审核定稿 v0.5):
9
+ * · **前台运行**:时钟唤起 = 向活跃会话注入唤起消息(agent.steer,v1.22.5 自动继续同款
10
+ * 通道)→ 模型自动继续 → 用户全程可见、随时可介入(人类反馈天然并入——同会话)。
11
+ * · **偏见内容提供者 = CCC 根目录下脚本**(用户拍板:不再让 CCC 注册 MSM;tool 直接运行脚本;
12
+ * 命名修正:它不是"随机脚本",本质是**偏见内容提供者**——biasProvider):
13
+ * 配置 biasProvider(相对 CCC 根,缺省 autotrajectory-bias.ts)——tool 直接运行取
14
+ * stdout 作为偏见内容;脚本缺失 → **报错要求实现**(偏见内容归 CCC,实现 = 根目录放脚本)。
15
+ * · **会话标志 = 目录名后缀 --auto**(用户拍板:后缀,验证用方便):
16
+ * AGENT_SESSIONS/<date>--<desc>--auto/ → 该轨迹为自主形态;无标志会话不受影响。
17
+ * · **唤起窗口避开北京时间 8~18 点**(用户拍板:用量峰谷省钱)——avoidWakeHours 可覆盖。
18
+ * · **默认关**:serenity.json autotrajectory.enabled=false → 定时器不启动,零资源占用。
19
+ * · **零影响**:不修改任何现有工具 / seams / 外部面;新增独立模块 + ccc.ts 纯类型扩展。
20
+ */
21
+ import type { Context } from 'cordis';
22
+ import { type AutoTrajectorySettings } from './ccc.js';
23
+ /** 偏见内容提供者脚本缺省名(CCC 根目录下;用户拍板:tool 直接运行,缺失报错要求实现) */
24
+ export declare const DEFAULT_BIAS_PROVIDER = "autotrajectory-bias.ts";
25
+ /** 自主轨迹会话目录后缀标志(用户拍板:后缀) */
26
+ export declare const AUTO_DIR_SUFFIX = "--auto";
27
+ /** 调度 tick 周期(10min——验证形态"一个就够") */
28
+ export declare const TICK_MS: number;
29
+ /** SESSION.md 内「下一轮动机」段标记(自生偏见载体——轨迹预设自己的未来) */
30
+ export declare const MOTIVATION_MARKER = "\u4E0B\u4E00\u8F6E\u52A8\u673A";
31
+ /** 缺省避开的高峰时段(北京时间 [start, end) 不唤起——用量峰谷省钱) */
32
+ export declare const DEFAULT_AVOID_HOURS: {
33
+ start: number;
34
+ end: number;
35
+ };
36
+ export declare function readAutoTrajectorySettings(root: string): AutoTrajectorySettings | null;
37
+ /**
38
+ * 北京时间(UTC+8)当前小时——不依赖服务器时区(服务器可能 UTC/本地任意)。
39
+ */
40
+ export declare function beijingHour(nowMs: number): number;
41
+ /**
42
+ * 唤起窗口判定:北京时间 [avoidStart, avoidEnd) 内不唤起(缺省 8~18)。
43
+ * 单段避开(start<=end):窗口 = [0,start) ∪ [end,24);跨零点避开(start>end):窗口 = [end,start)。
44
+ */
45
+ export declare function inAllowedWakeWindow(nowMs: number, avoid?: {
46
+ start?: number;
47
+ end?: number;
48
+ }): boolean;
49
+ /** 标志位判定:SESSION.md 所在目录名以 --auto 结尾 → 自主轨迹形态 */
50
+ export declare function isAutoTrajectorySession(mdPath: string): boolean;
51
+ /**
52
+ * 目标会话定位:**必须配置 cfg.session**(S###/目录名)——CCC 日常有多条 trajectory 在跑,
53
+ * 绝不默认唤起(缺省最近活跃会误伤其他正在运行的轨迹;用户拍板:必须配置才生效)。
54
+ * 未配置 session 或未命中 → null(不唤起)。
55
+ */
56
+ export declare function resolveTargetMd(root: string, cfg: AutoTrajectorySettings): string | null;
57
+ /**
58
+ * 唤起条件(纯逻辑,可测):enabled + 未在运行 + 目录标志 + mtime 超间隔 + 窗口允许。
59
+ */
60
+ export declare function shouldWake(settings: AutoTrajectorySettings, mdPath: string | null, nowMs: number, running: boolean): boolean;
61
+ /** 自生动机读取:SESSION.md「下一轮动机」段内容(到下一个二级标题或文件尾;无 → null) */
62
+ export declare function readSelfGeneratedMotivation(mdPath: string): string | null;
63
+ /**
64
+ * 偏见内容:直接运行 CCC 根目录下偏见提供者脚本(biasProvider,缺省 autotrajectory-bias.ts)→ stdout。
65
+ * 脚本缺失 → 返回 { text: null, error: 提示实现 }(唤起侧报错要求实现,不静默跳过)。
66
+ * 路径逃逸校验(resolveInside);bun 优先,node 兜底;600s 超时。
67
+ */
68
+ export declare function fetchBiasContent(root: string, providerRel: string): Promise<{
69
+ text: string | null;
70
+ error: string | null;
71
+ }>;
72
+ /** 唤起消息(三段式:身份锚定 / 先验偏见[自生动机+偏见内容] / 任务)——注入前台会话,用户可见 */
73
+ export declare function buildWakeMessage(opts: {
74
+ sessionName: string;
75
+ mdPath: string;
76
+ intervalHours: number;
77
+ motivation: string | null;
78
+ biasContent: string | null;
79
+ }): string;
80
+ /**
81
+ * 执行一次唤起(时钟 tick 与面板「立即唤起」共用):
82
+ * - force=false(时钟):完整校验——enabled / 目标命中 / --auto 标志 / 窗口 / 间隔 / 偏见脚本
83
+ * - force=true(手动调试,用户在场):跳过窗口与间隔,仍校验 enabled / 目标命中 / --auto / 偏见脚本
84
+ * 成功 → 注入前台会话(agent.steer,用户可见);返回结果供调用方(tick 打日志 / 面板显示)。
85
+ */
86
+ export interface WakeResult {
87
+ ok: boolean;
88
+ detail: string;
89
+ }
90
+ export declare function performAutoTrajectoryWake(ctx: Context, root: string, settings: AutoTrajectorySettings, opts?: {
91
+ force?: boolean;
92
+ }): Promise<WakeResult>;
93
+ /**
94
+ * 装配(index.ts apply 调用)。时钟唤起(v1.26.14 修复——用户"手动唤起正常,时钟唤起不工作"):
95
+ *
96
+ * 根因:旧实现启动时一次性 `resolveAutoTrajectoryRoot(ctx)`——web 进程启动时 live 会话
97
+ * 往往为空(用户尚未打开实验 CCC 会话)→ root=null → settings=null → **定时器根本不启动**;
98
+ * 手动唤起走 HTTP 端点(点击时重新解析 root)→ 正常。
99
+ *
100
+ * 修复:
101
+ * ① **动态解析**:每次 tick 重新解析 root + settings(不绑定启动时值)——live 会话变化即跟上;
102
+ * ② **事件驱动启动**:监听 `session/created`(live 会话出现)→ 启动定时器(进程启动时若
103
+ * 已有实验 CCC 会话则立即启动);
104
+ * ③ **优先实验 CCC**:resolveAutoTrajectoryCcc(配置了 autotrajectory 的 live CCC)优先于
105
+ * 进程 cwd/任一 live(多 CCC 同实例时时钟唤起绑定实验 CCC,而非当前维护会话 CCC)。
106
+ *
107
+ * 零资源占用语义保留:未配置/未启用 → tick 内直接 return(定时器存在但每 10min 一次空检查,
108
+ * unref 不阻塞进程退出)。
109
+ */
110
+ export declare function registerAutoTrajectory(ctx: Context): void;
111
+ /**
112
+ * 面板状态(GET /serenity/autotrajectory 数据源;纯逻辑,可单测)——
113
+ * WebUI 设置面板「自主轨迹」只读区块展示的完整状态:配置摘要 + 目标会话
114
+ * 命中/标志/空闲时长 + 当前窗口/可唤起判定(唤起逻辑同 registerAutoTrajectory)。
115
+ * 不运行偏见脚本(只报脚本是否就绪——运行验证走 autotrajectory-exp random)。
116
+ */
117
+ export interface AutoTrajectoryStatus {
118
+ /** 是否配置了 autotrajectory 段(.opencode/serenity.json) */
119
+ configured: boolean;
120
+ /** 总开关(缺省 false——未开零资源占用) */
121
+ enabled: boolean;
122
+ /** 无人类活动 N 小时后自动唤起(缺省 12) */
123
+ intervalHours: number;
124
+ /** 偏见内容提供者脚本(相对 CCC 根;缺省 autotrajectory-bias.ts) */
125
+ biasProvider: string;
126
+ /** 目标会话(S###/目录名;未配置 = 不唤起) */
127
+ session: string | null;
128
+ /** 避开唤起的高峰时段(北京时间) */
129
+ avoidWakeHours: {
130
+ start: number;
131
+ end: number;
132
+ };
133
+ /** 目标会话状态(未配置/未命中 → null) */
134
+ target: {
135
+ /** 目录名(含 --auto 后缀时为自主形态) */
136
+ dirName: string;
137
+ /** 目录名是否带 --auto 标志 */
138
+ autoFlag: boolean;
139
+ /** 距上次轨迹活动(小时) */
140
+ idleHours: number;
141
+ /** 当前时刻是否满足唤起条件(标志+间隔+窗口,未运行中) */
142
+ wakeable: boolean;
143
+ } | null;
144
+ /** 当前北京时间小时 */
145
+ beijingHour: number;
146
+ /** 当前是否在唤起窗口内(避开高峰之外) */
147
+ windowAllowed: boolean;
148
+ }
149
+ export declare function getAutoTrajectoryStatus(root: string): AutoTrajectoryStatus;
150
+ /** live 会话条目(诊断/面板解析用;标题从 events 读) */
151
+ export interface LiveSessionEntry {
152
+ id: string;
153
+ cwd: string | null;
154
+ cccRoot: string | null;
155
+ title: string | null;
156
+ }
157
+ /** 遍历 live 会话(sessions.list())+ 补标题(events session/title latest-wins)+ ccc 归属 */
158
+ export declare function listLiveSessions(ctx: Context): LiveSessionEntry[];
159
+ /**
160
+ * 面板解析:GET /serenity/autotrajectory 无 workspace 参数时——
161
+ * **优先「配置了 autotrajectory 的 live CCC」**(用户实验状态所在;v1.26.14 修复:
162
+ * 原 resolveWorkspace 解析到第一个 live 会话(如 home-serenity)→ 面板显示"未配置",
163
+ * 用户实验 CCC(pangu)检测不到)。优先级:已启用 → 已配置(未启用)→ null。
164
+ */
165
+ export declare function resolveAutoTrajectoryCcc(ctx: Context): string | null;
166
+ /**
167
+ * 进程内诊断(autotrajectory-exp diag-live 数据源;用户"排查访问不到 pangu 写个 msm")——
168
+ * 输出当前实例 live 会话清单(id/cwd/ccc/标题)+ 每个配置了 autotrajectory 的 CCC
169
+ * 状态(配置摘要/目标命中/可唤起)+ 目标 agent 定位结果。脚本 diag 看不到运行时,
170
+ * 本函数在插件进程内运行(能读 sessions/agents)。
171
+ */
172
+ export interface DiagLiveReport {
173
+ processCwd: string;
174
+ processCcc: string | null;
175
+ liveSessions: LiveSessionEntry[];
176
+ autotrajectoryCccs: Array<{
177
+ root: string;
178
+ enabled: boolean;
179
+ session: string | null;
180
+ target: AutoTrajectoryStatus['target'];
181
+ agentResolved: boolean;
182
+ agentDiagnosis: string | null;
183
+ }>;
184
+ panelResolved: string | null;
185
+ }
186
+ export declare function diagLive(ctx: Context): DiagLiveReport;
@@ -0,0 +1,141 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ //#region src/ccc.ts
4
+ /**
5
+ * ccc.ts — CCC 纯逻辑层(零 DSH 依赖,可独立单测)
6
+ *
7
+ * 职责:CCC 根检测(P1)、git 检测(P2)、serenity.json 配置读取(.opencode 规范位置,.dsh 回退)、
8
+ * 路径守卫(P3 语义)、安全模式黑名单匹配。
9
+ *
10
+ * 由 tools/ 与 seams/ 复用;逻辑移植自 dsh-serenity-plugin v0.1-v0.2
11
+ * runner(本项目自有代码,非 opencode-serenity-plugin 源码)。
12
+ */
13
+ function findSerenityRoot(cwd) {
14
+ let current = resolve(cwd);
15
+ while (true) {
16
+ const marker = resolve(current, ".serenity");
17
+ if (existsSync(marker) && statSync(marker).isFile()) return current;
18
+ const parent = dirname(current);
19
+ if (parent === current) return null;
20
+ current = parent;
21
+ }
22
+ }
23
+ function findGitRoot(cwd) {
24
+ let current = resolve(cwd);
25
+ while (true) {
26
+ if (existsSync(resolve(current, ".git"))) return current;
27
+ const parent = dirname(current);
28
+ if (parent === current) return null;
29
+ current = parent;
30
+ }
31
+ }
32
+ /**
33
+ * 前缀判定:abs 是否位于 rootAbs 之内。
34
+ * caseInsensitive(Windows 盘符/路径大小写不敏感)由调用方按平台传入;
35
+ * 边界必须是路径分隔符(`\` 或 `/` 任一)——不依赖平台 sep,跨平台语义一致:
36
+ * - 跨盘符绝对路径(root 在 D:\、target 在 C:\)前缀不匹配 → outside
37
+ * - 兄弟目录前缀陷阱(home vs home2)边界非分隔符 → outside
38
+ * (旧实现用 path.relative().startsWith('..')——跨盘时 relative 返回绝对路径原文,
39
+ * 不以 `..` 开头 → 漏判放行,见 Windows 兼容审计问题 1。)
40
+ */
41
+ function pathInside(rootAbs, abs, caseInsensitive = process.platform === "win32") {
42
+ const r = caseInsensitive ? rootAbs.toLowerCase() : rootAbs;
43
+ const a = caseInsensitive ? abs.toLowerCase() : abs;
44
+ if (a === r) return true;
45
+ if (!a.startsWith(r)) return false;
46
+ const next = a[r.length];
47
+ return next === "\\" || next === "/";
48
+ }
49
+ function classifyPath(p, root) {
50
+ const rootAbs = resolve(root);
51
+ const abs = resolve(p);
52
+ const ci = process.platform === "win32";
53
+ if (ci ? abs.toLowerCase() === rootAbs.toLowerCase() : abs === rootAbs) return "same";
54
+ return pathInside(rootAbs, abs, ci) ? "inside" : "outside";
55
+ }
56
+ function resolveInside(root, p) {
57
+ const abs = resolve(root, p);
58
+ if (classifyPath(abs, root) === "outside") throw new Error(`Path escape blocked: "${p}" resolves outside "${root}"`);
59
+ return abs;
60
+ }
61
+ /**
62
+ * 解析 handyman 配置(v1.24.0:取代 loop.defaultModel;不兼容旧 loop 配置——用户拍板)。
63
+ * @returns 白名单 + 缺省模型 + 上限;models 未配置返回 null(工具应报错要求配置)
64
+ */
65
+ function readHandymanConfig(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
66
+ const cfg = loadSerenityConfig(root, paths);
67
+ const models = cfg.handyman?.models;
68
+ if (!Array.isArray(models) || models.length === 0) return null;
69
+ const clean = models.map((m) => m.trim()).filter(Boolean);
70
+ if (clean.length === 0) return null;
71
+ const defaultModel = cfg.handyman?.defaultModel?.trim();
72
+ return {
73
+ models: clean,
74
+ defaultModel: defaultModel !== void 0 && clean.includes(defaultModel) ? defaultModel : clean[0],
75
+ maxRounds: cfg.handyman?.maxRounds ?? 100,
76
+ maxParallel: cfg.handyman?.maxParallel ?? 10
77
+ };
78
+ }
79
+ /**
80
+ * ACC 依赖的 CCC 配置路径(S134 修正:兼容历史——历史在 .opencode,规范位置 = .opencode;
81
+ * .dsh 仅作 dsh 运行时回退,不优先)。
82
+ */
83
+ const DEFAULT_SERENITY_CONFIG_PATHS = [".opencode/serenity.json", ".dsh/serenity.json"];
84
+ /**
85
+ * 读取 UTF-8 文件并剥离 BOM(Windows 审计问题 16):PowerShell/Windows 编辑器
86
+ * 写出的 BOM(\uFEFF)会让 JSON.parse 抛错(配置静默变空)或 frontmatter 检测失败(技能被丢弃)。
87
+ */
88
+ function readUtf8(path) {
89
+ return readFileSync(path, "utf-8").replace(/^\uFEFF/, "");
90
+ }
91
+ function loadSerenityConfig(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
92
+ for (const candidate of paths) {
93
+ const p = resolve(root, candidate);
94
+ if (!existsSync(p)) continue;
95
+ try {
96
+ return JSON.parse(readUtf8(p));
97
+ } catch {
98
+ return {};
99
+ }
100
+ }
101
+ return {};
102
+ }
103
+ const SAFE_MODE_MARKER = ".serenity-safe-on";
104
+ function isSafeModeOn(root) {
105
+ return existsSync(resolve(root, SAFE_MODE_MARKER));
106
+ }
107
+ /**
108
+ * 读取黑名单(对齐 osp readBlacklist):支持两种条目格式——
109
+ * string:".secrets/" 或 "regex:^..."(前缀 / 正则)
110
+ * object:{ "pattern": ".secrets/", "message": "自定义提示" }
111
+ * dsp v1.17.4 前只支持 string(对象会被 String() 成 "[object Object]" → 规则失效,不拦截)。
112
+ */
113
+ function readBlacklist(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
114
+ const rules = loadSerenityConfig(root, paths).safeMode?.blacklist;
115
+ if (!Array.isArray(rules)) return [];
116
+ const out = [];
117
+ for (const item of rules) if (typeof item === "string") {
118
+ if (item) out.push({ pattern: item });
119
+ } else if (item && typeof item === "object" && typeof item.pattern === "string") {
120
+ const obj = item;
121
+ const p = obj.pattern;
122
+ if (p) out.push({
123
+ pattern: p,
124
+ message: typeof obj.message === "string" ? obj.message : void 0
125
+ });
126
+ }
127
+ return out;
128
+ }
129
+ /** 匹配黑名单规则;命中返回条目,未命中返回 null。前缀匹配 / regex: 前缀(对齐 osp)。Windows:规则反斜杠归一化为正斜杠与 rel 对齐 */
130
+ function matchBlacklist(relPath, rules) {
131
+ for (const rule of rules) if (rule.pattern.startsWith("regex:")) try {
132
+ if (new RegExp(rule.pattern.slice(6)).test(relPath)) return rule;
133
+ } catch {}
134
+ else {
135
+ const pat = rule.pattern.split("\\").join("/");
136
+ if (relPath.startsWith(pat)) return rule;
137
+ }
138
+ return null;
139
+ }
140
+ //#endregion
141
+ export { findSerenityRoot as a, matchBlacklist as c, readHandymanConfig as d, readUtf8 as f, findGitRoot as i, pathInside as l, SAFE_MODE_MARKER as n, isSafeModeOn as o, resolveInside as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
package/lib/ccc.d.ts ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+ * ccc.ts — CCC 纯逻辑层(零 DSH 依赖,可独立单测)
3
+ *
4
+ * 职责:CCC 根检测(P1)、git 检测(P2)、serenity.json 配置读取(.opencode 规范位置,.dsh 回退)、
5
+ * 路径守卫(P3 语义)、安全模式黑名单匹配。
6
+ *
7
+ * 由 tools/ 与 seams/ 复用;逻辑移植自 dsh-serenity-plugin v0.1-v0.2
8
+ * runner(本项目自有代码,非 opencode-serenity-plugin 源码)。
9
+ */
10
+ export declare function findSerenityRoot(cwd: string): string | null;
11
+ export declare function findGitRoot(cwd: string): string | null;
12
+ export type PathClass = 'inside' | 'outside' | 'same';
13
+ /**
14
+ * 前缀判定:abs 是否位于 rootAbs 之内。
15
+ * caseInsensitive(Windows 盘符/路径大小写不敏感)由调用方按平台传入;
16
+ * 边界必须是路径分隔符(`\` 或 `/` 任一)——不依赖平台 sep,跨平台语义一致:
17
+ * - 跨盘符绝对路径(root 在 D:\、target 在 C:\)前缀不匹配 → outside
18
+ * - 兄弟目录前缀陷阱(home vs home2)边界非分隔符 → outside
19
+ * (旧实现用 path.relative().startsWith('..')——跨盘时 relative 返回绝对路径原文,
20
+ * 不以 `..` 开头 → 漏判放行,见 Windows 兼容审计问题 1。)
21
+ */
22
+ export declare function pathInside(rootAbs: string, abs: string, caseInsensitive?: boolean): boolean;
23
+ export declare function classifyPath(p: string, root: string): PathClass;
24
+ export declare function resolveInside(root: string, p: string): string;
25
+ export interface SerenityConfig {
26
+ /** handyman(杂工)配置:模型白名单(v1.24.0 取代 loop) */
27
+ handyman?: {
28
+ /** 可用模型白名单(provider/model 列表)——handyman 只能使用其中模型(用户拍板:CCC 配置控制) */
29
+ models?: string[];
30
+ /** 缺省模型(必须 ∈ models) */
31
+ defaultModel?: string;
32
+ /** 对话轮次上限(保险阀,缺省 100) */
33
+ maxRounds?: number;
34
+ /** jobs 编排并行上限(缺省 10——便宜模型便宜) */
35
+ maxParallel?: number;
36
+ };
37
+ sessionKeeper?: {
38
+ threshold?: number;
39
+ };
40
+ safeMode?: {
41
+ blacklist?: string[];
42
+ };
43
+ /** localstore git 提交策略(S134 重设计):allow 可提交 / deny 禁提交(缺省 deny) */
44
+ localstore?: {
45
+ gitTrack?: 'allow' | 'deny';
46
+ };
47
+ hooks?: {
48
+ enabled?: boolean;
49
+ injectAccContext?: boolean;
50
+ enforceSafeMode?: boolean;
51
+ sessionKeeper?: boolean;
52
+ /** 重启自动恢复最近激活的宁静号会话(session-start 时,根会话且无自身标记 → 回退最近 use 的标记) */
53
+ autoRestoreSession?: boolean;
54
+ };
55
+ /** Skiff(F4,v1.25.0 实验性)认知子集角色配置——角色归 CCC 定义(S142 用户拍板) */
56
+ skiff?: {
57
+ /** 角色定义(名 → 角色配置);缺省/空 = Skiff 未启用(零影响) */
58
+ roles?: Record<string, SkiffRoleConfig>;
59
+ };
60
+ /**
61
+ * 自主轨迹(v1.26.12 实验提案,specs self-sustaining-trajectory-hypothesis):
62
+ * CCC 定义的一条自主 trajectory——时钟唤起 + 先验偏见(自生+随机)+ 前台运行。
63
+ * 未配置或 enabled=false → 机制完全不启动(零资源占用,零影响现有功能)。
64
+ */
65
+ autotrajectory?: AutoTrajectorySettings;
66
+ }
67
+ /**
68
+ * 自主轨迹配置(CCC 定义;S142 用户拍板 v0.5):
69
+ * - 偏见内容提供者 = CCC 根目录下脚本(biasProvider,缺省 autotrajectory-bias.ts)——
70
+ * tool 直接运行取 stdout 作为偏见内容;脚本缺失 → 报错要求实现(不再经 mech-registry 注册 MSM)
71
+ * - 会话标志 = 目录名后缀 `--auto`(验证用方便):AGENT_SESSIONS/<date>--<desc>--auto/
72
+ * - **session 必填**(用户拍板:自动唤起不默认任何会话——CCC 日常有多条 trajectory 在跑,
73
+ * 未配置明确目标绝不唤起);唤起窗口避开北京时间 8~18 点(用量峰谷省钱——用户拍板)
74
+ */
75
+ export interface AutoTrajectorySettings {
76
+ /** 总开关(缺省 false——默认关,未开零资源占用) */
77
+ enabled?: boolean;
78
+ /** 无人类活动 N 小时后自动唤起(缺省 12) */
79
+ intervalHours?: number;
80
+ /** 偏见内容提供者脚本(相对 CCC 根;缺省 autotrajectory-bias.ts——缺失报错要求实现) */
81
+ biasProvider?: string;
82
+ /** **必填**:目标会话(S### / 目录名)——未配置绝不唤起(CCC 日常多轨迹并行,不默认) */
83
+ session?: string;
84
+ /** 避开唤起的高峰时段(北京时间,[start, end) 内不唤起;缺省 {8, 18}——用量峰谷省钱) */
85
+ avoidWakeHours?: {
86
+ start?: number;
87
+ end?: number;
88
+ };
89
+ }
90
+ /**
91
+ * Skiff 角色配置(CCC 定义:全知全能轨迹的一个子集):
92
+ * 能力面 = tools(非 MSM 工具白名单)+ msms(MSM 白名单)双白名单,白名单外全隐藏;
93
+ * 轨迹纪律面 = trajectory 子集(session/keeper/rebuild 参与项,默认全关)。
94
+ * 实验性质:未配置任何角色时 Skiff 完全零影响(无监听、无 agent 创建)。
95
+ */
96
+ export interface SkiffRoleConfig {
97
+ /** 角色模型(provider/model;CCC 直接指定,无白名单校验——用户拍板) */
98
+ model?: string;
99
+ /** MSM 白名单(独立):该角色可 exec 的 MSM 名列表;缺省空 = 无 MSM 通道 */
100
+ msms?: string[];
101
+ /** 非 MSM 工具白名单(独立):平台/ACC 工具名列表;缺省空 = 仅 MSM 通道 */
102
+ tools?: string[];
103
+ /** 轨迹纪律子集(缺省全 false = 完全独立,不参与 keeper/session/rebuild) */
104
+ trajectory?: {
105
+ session?: boolean;
106
+ keeper?: boolean;
107
+ rebuild?: boolean;
108
+ };
109
+ /** 角色系统提示词(CCC 完整定义:人格/认知边界/回答风格) */
110
+ systemPrompt?: string;
111
+ /** 角色系统提示词文件(推荐:相对 CCC 根引用 md 文件——超长提示词在 JSON 内嵌不可读;
112
+ * 优先于 systemPrompt;文件缺失/逃逸 → resolveRoleSystemPrompt 抛错) */
113
+ systemPromptFile?: string;
114
+ }
115
+ /**
116
+ * 解析 handyman 配置(v1.24.0:取代 loop.defaultModel;不兼容旧 loop 配置——用户拍板)。
117
+ * @returns 白名单 + 缺省模型 + 上限;models 未配置返回 null(工具应报错要求配置)
118
+ */
119
+ export declare function readHandymanConfig(root: string, paths?: string[]): {
120
+ models: string[];
121
+ defaultModel: string;
122
+ maxRounds: number;
123
+ maxParallel: number;
124
+ } | null;
125
+ /**
126
+ * ACC 依赖的 CCC 配置路径(S134 修正:兼容历史——历史在 .opencode,规范位置 = .opencode;
127
+ * .dsh 仅作 dsh 运行时回退,不优先)。
128
+ */
129
+ export declare const DEFAULT_SERENITY_CONFIG_PATHS: string[];
130
+ /**
131
+ * 读取 UTF-8 文件并剥离 BOM(Windows 审计问题 16):PowerShell/Windows 编辑器
132
+ * 写出的 BOM(\uFEFF)会让 JSON.parse 抛错(配置静默变空)或 frontmatter 检测失败(技能被丢弃)。
133
+ */
134
+ export declare function readUtf8(path: string): string;
135
+ export declare function loadSerenityConfig(root: string, paths?: string[]): SerenityConfig;
136
+ export declare const SAFE_MODE_MARKER = ".serenity-safe-on";
137
+ export declare function isSafeModeOn(root: string): boolean;
138
+ /** 黑名单条目(对齐 osp BlacklistEntry):pattern + 可选自定义拦截提示 message */
139
+ export interface BlacklistRule {
140
+ pattern: string;
141
+ message?: string;
142
+ }
143
+ /**
144
+ * 读取黑名单(对齐 osp readBlacklist):支持两种条目格式——
145
+ * string:".secrets/" 或 "regex:^..."(前缀 / 正则)
146
+ * object:{ "pattern": ".secrets/", "message": "自定义提示" }
147
+ * dsp v1.17.4 前只支持 string(对象会被 String() 成 "[object Object]" → 规则失效,不拦截)。
148
+ */
149
+ export declare function readBlacklist(root: string, paths?: string[]): BlacklistRule[];
150
+ /** 匹配黑名单规则;命中返回条目,未命中返回 null。前缀匹配 / regex: 前缀(对齐 osp)。Windows:规则反斜杠归一化为正斜杠与 rel 对齐 */
151
+ export declare function matchBlacklist(relPath: string, rules: BlacklistRule[]): BlacklistRule | null;
152
+ /** 写类工具名(safe-mode 下禁止) */
153
+ export declare const WRITE_TOOLS: Set<string>;
154
+ export declare function isWriteTool(toolName: string): boolean;