@shgroup/dsh-serenity-hooks 1.26.17 → 1.27.1

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.
@@ -69,6 +69,13 @@ export declare function jscSafeJsonText(jsonText: string): string;
69
69
  * @param hideThink 为 true 时 `<think>` 内容完全不渲染(public 问答页体验)
70
70
  */
71
71
  export declare function renderSkiffMarkdown(raw: string, hideThink?: boolean): string;
72
+ /**
73
+ * 剥离 `<think>…</think>` 块(v1.27.1,微信桥用户反馈:"过微信桥回复给用户的消息,
74
+ * 要去掉 think 标签,用户不应看到")。复用 extractThinkBlocks 状态机(v1.26.8,
75
+ * 弃正则)——提取后**只保留正文**(占位符替换为空,think 内容丢弃),
76
+ * 未闭合 think 优雅截断。适用于纯文本输出面(微信等不支持 think 折叠的通道)。
77
+ */
78
+ export declare function stripThink(raw: string): string;
72
79
  /**
73
80
  * 启动调试问答服务(单实例;重复启动幂等返回既有实例)。
74
81
  *
@@ -0,0 +1,126 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { f as readUtf8, p as resolveInside, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-CfDrlfA7.js";
3
+ import { existsSync } from "node:fs";
4
+ //#region src/skiff-role.ts
5
+ /**
6
+ * skiff-role.ts — Skiff(F4,v1.25.0 实验性)角色层纯逻辑(零 DSH 依赖,可独立单测)
7
+ *
8
+ * 概念(S142 用户拍板):Skiff = 完整宁静号 trajectory(在宁静号内全知全能)的
9
+ * **任意子集**——CCC 通过角色配置定义:能力面(tools 非 MSM 工具白名单 + msms
10
+ * MSM 白名单,双白名单独立,白名单外全隐藏)+ 轨迹纪律面(trajectory 子集)+
11
+ * 系统提示词(CCC 完整定义,dsp 只给基础部分)。
12
+ *
13
+ * 实验性质:未配置任何角色 → Skiff 完全零影响(无监听、无 agent 创建、guard 无规则)。
14
+ */
15
+ var skiff_role_exports = /* @__PURE__ */ __exportAll({
16
+ SKIFF_SESSION_PREFIX: () => SKIFF_SESSION_PREFIX,
17
+ buildSkiffBasePrompt: () => buildSkiffBasePrompt,
18
+ isSkiffSessionId: () => isSkiffSessionId,
19
+ readSkiffRoles: () => readSkiffRoles,
20
+ resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
21
+ roleMsmWhitelist: () => roleMsmWhitelist,
22
+ roleToolWhitelist: () => roleToolWhitelist,
23
+ systemPromptSource: () => systemPromptSource,
24
+ trajectorySubset: () => trajectorySubset
25
+ });
26
+ /** Skiff agent 会话 id 前缀(agents.create 生成;seams 旁路/白名单判定用) */
27
+ const SKIFF_SESSION_PREFIX = "skiff-";
28
+ /** 判定 sessionId 是否为 Skiff 会话(仿 handyman- 前缀排除模式) */
29
+ function isSkiffSessionId(sessionId) {
30
+ return typeof sessionId === "string" && sessionId.startsWith("skiff-");
31
+ }
32
+ /**
33
+ * 读取 CCC 的 Skiff 角色配置(.opencode/serenity.json skiff.roles)。
34
+ * @returns 名 → 角色配置 的 Map;未配置(无 skiff 段/空 roles)返回空 Map(Skiff 未启用)
35
+ */
36
+ function readSkiffRoles(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
37
+ const out = /* @__PURE__ */ new Map();
38
+ try {
39
+ const roles = loadSerenityConfig(root, paths).skiff?.roles;
40
+ if (!roles || typeof roles !== "object") return out;
41
+ for (const [name, role] of Object.entries(roles)) {
42
+ if (!role || typeof role !== "object") continue;
43
+ if (name.trim() === "") continue;
44
+ out.set(name.trim(), {
45
+ model: typeof role.model === "string" ? role.model : void 0,
46
+ msms: Array.isArray(role.msms) ? role.msms.filter((m) => typeof m === "string") : void 0,
47
+ tools: Array.isArray(role.tools) ? role.tools.filter((t) => typeof t === "string") : void 0,
48
+ trajectory: role.trajectory && typeof role.trajectory === "object" ? {
49
+ session: role.trajectory.session === true,
50
+ keeper: role.trajectory.keeper === true,
51
+ rebuild: role.trajectory.rebuild === true
52
+ } : void 0,
53
+ systemPrompt: typeof role.systemPrompt === "string" ? role.systemPrompt : void 0,
54
+ systemPromptFile: typeof role.systemPromptFile === "string" ? role.systemPromptFile : void 0
55
+ });
56
+ }
57
+ } catch {}
58
+ return out;
59
+ }
60
+ function trajectorySubset(role) {
61
+ return {
62
+ session: role?.trajectory?.session === true,
63
+ keeper: role?.trajectory?.keeper === true,
64
+ rebuild: role?.trajectory?.rebuild === true
65
+ };
66
+ }
67
+ /** 角色可用工具面(白名单并集):tools + acc_msm(msms 非空时作为 MSM 通道自动可用) */
68
+ function roleToolWhitelist(role) {
69
+ const out = /* @__PURE__ */ new Set();
70
+ for (const t of role?.tools ?? []) out.add(t);
71
+ if ((role?.msms?.length ?? 0) > 0) out.add("acc_msm");
72
+ return out;
73
+ }
74
+ /** 角色允许的 MSM 白名单(acc_msm exec 校验 / msm_list 过滤用;独立于 tools 白名单) */
75
+ function roleMsmWhitelist(role) {
76
+ return new Set(role?.msms ?? []);
77
+ }
78
+ /**
79
+ * 解析角色的系统提示词全文(v1.25.10,S142 用户:超长提示词 JSON 内嵌不可读):
80
+ * ① `systemPromptFile` 存在 → 读取文件内容(**推荐配置方法**;相对 CCC 根,
81
+ * 路径逃逸拒绝(resolveInside)+ BOM 剥除(readUtf8)+ 存在性校验)
82
+ * ② 否则 → 内嵌 `systemPrompt`(兼容旧配置)
83
+ * ③ 都无 → 空字符串
84
+ * 文件缺失/逃逸 → 抛错(调用方 catch 降级 + validate 报 issue)。
85
+ * 懒读取:readSkiffRoles 不读文件(guards/seams 每次工具调用查询的热路径零 IO),
86
+ * 仅在本函数(创建 agent / validate / list 时)读取。
87
+ */
88
+ function resolveRoleSystemPrompt(root, role) {
89
+ if (!role) return "";
90
+ const file = role.systemPromptFile?.trim();
91
+ if (file) {
92
+ const abs = resolveInside(root, file);
93
+ if (!existsSync(abs)) throw new Error(`skiff role "${role.systemPrompt ?? "(unnamed)"}": systemPromptFile "${file}" not found (resolved: ${abs})`);
94
+ return readUtf8(abs).trim();
95
+ }
96
+ return role.systemPrompt ?? "";
97
+ }
98
+ /** 角色系统提示词来源(validate/list 展示用) */
99
+ function systemPromptSource(role) {
100
+ if (role?.systemPromptFile?.trim()) return "file";
101
+ if (role?.systemPrompt?.trim()) return "inline";
102
+ return "none";
103
+ }
104
+ /**
105
+ * Skiff 基础提示词(dsp 只给这部分;CCC 的 systemPrompt 段由调用方拼接):
106
+ * 身份 + 可用 MSM/工具清单 + 调用协议 + 边界声明。动态生成(清单来自角色白名单)。
107
+ */
108
+ function buildSkiffBasePrompt(roleName, role) {
109
+ const msms = role?.msms ?? [];
110
+ const tools = role?.tools ?? [];
111
+ const lines = [
112
+ "=== Serenity Skiff ===",
113
+ `Role: ${roleName} (defined by this CCC)`,
114
+ "You interact with this CCC ONLY through the exposed surface below:"
115
+ ];
116
+ if (msms.length > 0) lines.push(` MSMs: ${msms.join(", ")} (call acc_msm exec <name> [args...]; pass --help as the first arg for usage)`);
117
+ else lines.push(" MSMs: (none)");
118
+ lines.push(` Tools: ${tools.length > 0 ? tools.join(", ") : "(none)"}`);
119
+ lines.push("No other tools are available. Your capability boundary is this surface.");
120
+ lines.push("");
121
+ lines.push("---");
122
+ lines.push("");
123
+ return lines.join("\n");
124
+ }
125
+ //#endregion
126
+ export { resolveRoleSystemPrompt as a, skiff_role_exports as c, readSkiffRoles as i, systemPromptSource as l, buildSkiffBasePrompt as n, roleMsmWhitelist as o, isSkiffSessionId as r, roleToolWhitelist as s, SKIFF_SESSION_PREFIX as t, trajectorySubset as u };
@@ -18,8 +18,6 @@ import { defineTool } from '@deepseek-ai/dsh-tools';
18
18
  import type { Context } from 'cordis';
19
19
  import { type ActiveSessionInfo, type CreateSessionResult } from '../session-ops.js';
20
20
  export interface RenameOnUseDeps {
21
- /** naming.enabled 简单配置(DSH settings) */
22
- namingEnabled: boolean;
23
21
  /** sessionTitle 服务可用性 */
24
22
  sessionTitleAvailable: boolean;
25
23
  }
@@ -33,8 +31,8 @@ export interface RenameOnUseDeps {
33
31
  export declare function namingTitleFor(active: ActiveSessionInfo): string;
34
32
  /**
35
33
  * use 激活宁静号会话后,把当前 dsh 会话重命名为命名标题(`S###-日期`)。
36
- * 门控:naming.enabled + sessionTitle 服务存在;失败不静默——返回结果对象
37
- * 而非 null(v1.22.9),调用方决定可见性。
34
+ * v1.27.1:**永远开启**(不再有 naming.enabled 门控)——仅 sessionTitle 服务
35
+ * 存在性守卫;失败不静默——返回结果对象而非 null(v1.22.9),调用方决定可见性。
38
36
  *
39
37
  * v1.23.2 修复(this 绑定):第三参从**解构的裸 rename 函数**改为**整个
40
38
  * sessionTitle 服务对象**——内部以 `titles.rename(session, title)` **方法调用**
@@ -0,0 +1,211 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { randomBytes } from "node:crypto";
3
+ //#region src/weixin-api.ts
4
+ /**
5
+ * weixin-api.ts — 微信个人号 iLink Bot API 客户端(F4c-3,v1.27.0 实验性)
6
+ *
7
+ * 纯 fetch 客户端(Node ≥ 18 原生 fetch + node:crypto),零第三方依赖——
8
+ * 对齐 openclaw-weixin 插件 api.ts 语义(协议全解见 docs/weixin-bot-api.md)。
9
+ *
10
+ * 定位:**Mech 传输层**——只做 HTTP 编解码与请求头,不含业务逻辑(路由/会话映射
11
+ * 在 weixin-route.ts / weixin-bridge.ts)。所有函数可单测(fetch 注入 mock)。
12
+ *
13
+ * 关键协议事实(实证裸调 2026-08-31):
14
+ * - get_bot_qrcode 无需任何凭据即可取码(无 OpenClaw 账号体系/平台审核)
15
+ * - 鉴权后请求头:AuthorizationType: ilink_bot_token + X-WECHAT-UIN(随机防重放)
16
+ * + Authorization: Bearer <bot_token> + iLink-App-Id/ClientVersion
17
+ * - getupdates 长轮询 35s hold;游标 get_updates_buf 从响应原样带回续传
18
+ * - sendmessage 必须回带 context_token 关联对话
19
+ */
20
+ var weixin_api_exports = /* @__PURE__ */ __exportAll({
21
+ ILINK_CDN_BASE_URL: () => ILINK_CDN_BASE_URL,
22
+ ILINK_DEFAULT_BASE_URL: () => ILINK_DEFAULT_BASE_URL,
23
+ MessageItemType: () => MessageItemType,
24
+ MessageState: () => MessageState,
25
+ MessageType: () => MessageType,
26
+ buildClientVersion: () => buildClientVersion,
27
+ fetchQRCode: () => fetchQRCode,
28
+ getUpdates: () => getUpdates,
29
+ markdownToPlainText: () => markdownToPlainText,
30
+ pollQRStatus: () => pollQRStatus,
31
+ sendTextMessage: () => sendTextMessage
32
+ });
33
+ /** iLink API Base URL(腾讯官方) */
34
+ const ILINK_DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
35
+ /** iLink-App-Id(openclaw-weixin 同款) */
36
+ const ILINK_APP_ID = "bot";
37
+ /** 通道版本(对齐 openclaw-weixin 2.1.1;buildClientVersion 编码) */
38
+ const CHANNEL_VERSION = "2.1.1";
39
+ /** 版本号 → ClientVersion 整数:(major<<16)|(minor<<8)|patch */
40
+ function buildClientVersion(version) {
41
+ const parts = version.split(".").map((p) => parseInt(p, 10));
42
+ const major = parts[0] ?? 0;
43
+ const minor = parts[1] ?? 0;
44
+ const patch = parts[2] ?? 0;
45
+ return (major & 255) << 16 | (minor & 255) << 8 | patch & 255;
46
+ }
47
+ const CLIENT_VERSION = buildClientVersion(CHANNEL_VERSION);
48
+ /** 随机 X-WECHAT-UIN(uint32 → base64;每请求不同,防重放) */
49
+ function randomWechatUin() {
50
+ const uint32 = randomBytes(4).readUInt32BE(0);
51
+ return Buffer.from(String(uint32), "utf-8").toString("base64");
52
+ }
53
+ function ensureTrailingSlash(url) {
54
+ return url.endsWith("/") ? url : `${url}/`;
55
+ }
56
+ function buildCommonHeaders() {
57
+ return {
58
+ "iLink-App-Id": ILINK_APP_ID,
59
+ "iLink-App-ClientVersion": String(CLIENT_VERSION)
60
+ };
61
+ }
62
+ function buildHeaders(token) {
63
+ return {
64
+ "Content-Type": "application/json",
65
+ AuthorizationType: "ilink_bot_token",
66
+ "X-WECHAT-UIN": randomWechatUin(),
67
+ ...token ? { Authorization: `Bearer ${token}` } : {},
68
+ ...buildCommonHeaders()
69
+ };
70
+ }
71
+ let currentFetch = (...args) => fetch(...args);
72
+ async function apiGet(params) {
73
+ const base = ensureTrailingSlash(params.baseUrl);
74
+ const url = new URL(params.endpoint, base);
75
+ const controller = new AbortController();
76
+ const timer = setTimeout(() => controller.abort(), params.timeoutMs);
77
+ try {
78
+ const res = await currentFetch(url.toString(), {
79
+ method: "GET",
80
+ headers: buildCommonHeaders(),
81
+ signal: controller.signal
82
+ });
83
+ clearTimeout(timer);
84
+ const rawText = await res.text();
85
+ if (!res.ok) throw new Error(`${params.endpoint} ${res.status}: ${rawText}`);
86
+ return rawText;
87
+ } catch (err) {
88
+ clearTimeout(timer);
89
+ throw err;
90
+ }
91
+ }
92
+ async function apiPost(params) {
93
+ const base = ensureTrailingSlash(params.baseUrl);
94
+ const url = new URL(params.endpoint, base);
95
+ const controller = new AbortController();
96
+ const timer = setTimeout(() => controller.abort(), params.timeoutMs);
97
+ try {
98
+ const res = await currentFetch(url.toString(), {
99
+ method: "POST",
100
+ headers: buildHeaders(params.token),
101
+ body: params.body,
102
+ signal: controller.signal
103
+ });
104
+ clearTimeout(timer);
105
+ const rawText = await res.text();
106
+ if (!res.ok) throw new Error(`${params.endpoint} ${res.status}: ${rawText}`);
107
+ return rawText;
108
+ } catch (err) {
109
+ clearTimeout(timer);
110
+ throw err;
111
+ }
112
+ }
113
+ /** 取登录二维码(无需凭据;实证裸调可用) */
114
+ async function fetchQRCode(params) {
115
+ const botType = params.botType ?? "3";
116
+ const rawText = await apiGet({
117
+ baseUrl: params.baseUrl ?? "https://ilinkai.weixin.qq.com",
118
+ endpoint: `ilink/bot/get_bot_qrcode?bot_type=${encodeURIComponent(botType)}`,
119
+ timeoutMs: params.timeoutMs ?? 5e3
120
+ });
121
+ return JSON.parse(rawText);
122
+ }
123
+ async function pollQRStatus(params) {
124
+ try {
125
+ const rawText = await apiGet({
126
+ baseUrl: params.baseUrl ?? "https://ilinkai.weixin.qq.com",
127
+ endpoint: `ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(params.qrcode)}`,
128
+ timeoutMs: params.timeoutMs ?? 35e3
129
+ });
130
+ return JSON.parse(rawText);
131
+ } catch {
132
+ return { status: "wait" };
133
+ }
134
+ }
135
+ /** 长轮询收消息(35s hold;超时返回空 msgs 不报错——对齐 openclaw-weixin) */
136
+ async function getUpdates(params) {
137
+ const timeout = params.timeoutMs ?? 35e3;
138
+ try {
139
+ const rawText = await apiPost({
140
+ baseUrl: params.baseUrl,
141
+ endpoint: "ilink/bot/getupdates",
142
+ body: JSON.stringify({ get_updates_buf: params.getUpdatesBuf ?? "" }),
143
+ token: params.token,
144
+ timeoutMs: timeout
145
+ });
146
+ return JSON.parse(rawText);
147
+ } catch (err) {
148
+ if (err instanceof Error && err.name === "AbortError") return {
149
+ ret: 0,
150
+ msgs: [],
151
+ get_updates_buf: params.getUpdatesBuf
152
+ };
153
+ throw err;
154
+ }
155
+ }
156
+ const MessageType = {
157
+ NONE: 0,
158
+ USER: 1,
159
+ BOT: 2
160
+ };
161
+ const MessageState = {
162
+ NEW: 0,
163
+ GENERATING: 1,
164
+ FINISH: 2
165
+ };
166
+ const MessageItemType = {
167
+ NONE: 0,
168
+ TEXT: 1,
169
+ IMAGE: 2,
170
+ VOICE: 3,
171
+ FILE: 4,
172
+ VIDEO: 5
173
+ };
174
+ /** 发文本消息(必须回带 context_token 关联对话;md→plain 内置——微信不支持 Markdown) */
175
+ async function sendTextMessage(params) {
176
+ const clientId = params.clientId ?? `dsp-weixin-${randomBytes(4).toString("hex")}`;
177
+ await apiPost({
178
+ baseUrl: params.baseUrl,
179
+ endpoint: "ilink/bot/sendmessage",
180
+ body: JSON.stringify({ msg: {
181
+ from_user_id: "",
182
+ to_user_id: params.toUserId,
183
+ client_id: clientId,
184
+ message_type: MessageType.BOT,
185
+ message_state: MessageState.FINISH,
186
+ item_list: [{
187
+ type: MessageItemType.TEXT,
188
+ text_item: { text: markdownToPlainText(params.text) }
189
+ }],
190
+ ...params.contextToken ? { context_token: params.contextToken } : {}
191
+ } }),
192
+ token: params.token,
193
+ timeoutMs: params.timeoutMs ?? 15e3
194
+ });
195
+ }
196
+ /** Markdown → 纯文本(代码块/图片/链接/表格/标题/粗斜体/删除线 7 类) */
197
+ function markdownToPlainText(text) {
198
+ let result = text;
199
+ result = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, code) => code.trim());
200
+ result = result.replace(/!\[[^\]]*\]\([^)]*\)/g, "");
201
+ result = result.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
202
+ result = result.replace(/^\|.*\|[ \t]*$/gm, "");
203
+ result = result.replace(/^#+\s*/gm, "");
204
+ result = result.replace(/\*\*([^*]+)\*\*/g, "$1");
205
+ result = result.replace(/\*([^*]+)\*/g, "$1");
206
+ result = result.replace(/`([^`]+)`/g, "$1");
207
+ result = result.replace(/~~([^~]+)~~/g, "$1");
208
+ return result.trim();
209
+ }
210
+ //#endregion
211
+ export { sendTextMessage as n, weixin_api_exports as r, getUpdates as t };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * weixin-api.ts — 微信个人号 iLink Bot API 客户端(F4c-3,v1.27.0 实验性)
3
+ *
4
+ * 纯 fetch 客户端(Node ≥ 18 原生 fetch + node:crypto),零第三方依赖——
5
+ * 对齐 openclaw-weixin 插件 api.ts 语义(协议全解见 docs/weixin-bot-api.md)。
6
+ *
7
+ * 定位:**Mech 传输层**——只做 HTTP 编解码与请求头,不含业务逻辑(路由/会话映射
8
+ * 在 weixin-route.ts / weixin-bridge.ts)。所有函数可单测(fetch 注入 mock)。
9
+ *
10
+ * 关键协议事实(实证裸调 2026-08-31):
11
+ * - get_bot_qrcode 无需任何凭据即可取码(无 OpenClaw 账号体系/平台审核)
12
+ * - 鉴权后请求头:AuthorizationType: ilink_bot_token + X-WECHAT-UIN(随机防重放)
13
+ * + Authorization: Bearer <bot_token> + iLink-App-Id/ClientVersion
14
+ * - getupdates 长轮询 35s hold;游标 get_updates_buf 从响应原样带回续传
15
+ * - sendmessage 必须回带 context_token 关联对话
16
+ */
17
+ /** iLink API Base URL(腾讯官方) */
18
+ export declare const ILINK_DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
19
+ /** CDN Base(媒体上传/下载;P3 媒体期用) */
20
+ export declare const ILINK_CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
21
+ /** 版本号 → ClientVersion 整数:(major<<16)|(minor<<8)|patch */
22
+ export declare function buildClientVersion(version: string): number;
23
+ /** fetch 注入点(测试替换;生产 = 全局 fetch) */
24
+ export type FetchLike = typeof fetch;
25
+ /** 测试注入:替换全局 fetch 实现(生产零调用) */
26
+ export declare function __setWeixinFetchForTest(fn: FetchLike | null): void;
27
+ /** 取登录二维码(无需凭据;实证裸调可用) */
28
+ export declare function fetchQRCode(params: {
29
+ baseUrl?: string;
30
+ botType?: string;
31
+ timeoutMs?: number;
32
+ }): Promise<{
33
+ qrcode: string;
34
+ qrcode_img_content: string;
35
+ }>;
36
+ /** 扫码状态(轮询;1s 间隔建议,5min 有效) */
37
+ export interface QrStatusResult {
38
+ status: string;
39
+ bot_token?: string;
40
+ ilink_bot_id?: string;
41
+ baseurl?: string;
42
+ ilink_user_id?: string;
43
+ redirect_host?: string;
44
+ }
45
+ export declare function pollQRStatus(params: {
46
+ baseUrl?: string;
47
+ qrcode: string;
48
+ timeoutMs?: number;
49
+ }): Promise<QrStatusResult>;
50
+ export interface WeixinMessageItem {
51
+ type?: number;
52
+ msg_id?: string;
53
+ text_item?: {
54
+ text?: string;
55
+ };
56
+ image_item?: Record<string, unknown>;
57
+ voice_item?: Record<string, unknown>;
58
+ file_item?: Record<string, unknown>;
59
+ video_item?: Record<string, unknown>;
60
+ }
61
+ export interface WeixinMessage {
62
+ seq?: number;
63
+ message_id?: number;
64
+ from_user_id?: string;
65
+ to_user_id?: string;
66
+ client_id?: string;
67
+ create_time_ms?: number;
68
+ session_id?: string;
69
+ group_id?: string;
70
+ message_type?: number;
71
+ message_state?: number;
72
+ item_list?: WeixinMessageItem[];
73
+ context_token?: string;
74
+ }
75
+ export interface GetUpdatesResp {
76
+ ret?: number;
77
+ errcode?: number;
78
+ errmsg?: string;
79
+ msgs?: WeixinMessage[];
80
+ get_updates_buf?: string;
81
+ longpolling_timeout_ms?: number;
82
+ }
83
+ /** 长轮询收消息(35s hold;超时返回空 msgs 不报错——对齐 openclaw-weixin) */
84
+ export declare function getUpdates(params: {
85
+ baseUrl: string;
86
+ token: string;
87
+ getUpdatesBuf?: string;
88
+ timeoutMs?: number;
89
+ }): Promise<GetUpdatesResp>;
90
+ export declare const MessageType: {
91
+ readonly NONE: 0;
92
+ readonly USER: 1;
93
+ readonly BOT: 2;
94
+ };
95
+ export declare const MessageState: {
96
+ readonly NEW: 0;
97
+ readonly GENERATING: 1;
98
+ readonly FINISH: 2;
99
+ };
100
+ export declare const MessageItemType: {
101
+ readonly NONE: 0;
102
+ readonly TEXT: 1;
103
+ readonly IMAGE: 2;
104
+ readonly VOICE: 3;
105
+ readonly FILE: 4;
106
+ readonly VIDEO: 5;
107
+ };
108
+ /** 发文本消息(必须回带 context_token 关联对话;md→plain 内置——微信不支持 Markdown) */
109
+ export declare function sendTextMessage(params: {
110
+ baseUrl: string;
111
+ token: string;
112
+ toUserId: string;
113
+ text: string;
114
+ contextToken?: string;
115
+ clientId?: string;
116
+ timeoutMs?: number;
117
+ }): Promise<void>;
118
+ /** Markdown → 纯文本(代码块/图片/链接/表格/标题/粗斜体/删除线 7 类) */
119
+ export declare function markdownToPlainText(text: string): string;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * weixin-bridge.ts — 微信桥装配(F4c-3,v1.27.0 实验性)
3
+ *
4
+ * **CCC 级**:dsh 一个进程含多个 CCC,每个 CCC 独立对接微信桥(S142 用户拍板)——
5
+ * 扫描 live CCC → 每 CCC 读 weixin 配置(enabled + accounts + routes)→
6
+ * 启动该 CCC 该账号的 iLink 轮询循环。消息 → weixin-route 路由 → AcpServer 直调
7
+ * (session/new + session/prompt,同进程不经网络——acp-core 传输无关设计)。
8
+ *
9
+ * 实验性质:未配置/未启用 → 完全不启动(零资源占用)。配置变化(面板写入)
10
+ * → 热重建受影响 CCC 的桥(对齐 gateway 热重建模式)。
11
+ *
12
+ * 外部面纯净(D9/D11 延续):微信桥会话 = 外部面——session/prompt 走
13
+ * includeTrajectory:false(对外不返回轨迹);skiff 角色白名单即授权(G9)。
14
+ */
15
+ import type { Context } from 'cordis';
16
+ import { type WeixinAccountCredential } from './weixin-route.js';
17
+ /**
18
+ * 处理单条微信消息:路由 → skiff 会话(固定 id 创建/延续)→ 提问 → 回复回写。
19
+ *
20
+ * 会话语义:`weixinSessionIdFor(fromUserId)` 固定可重建——同用户长期同一会话;
21
+ * 进程内已有(getSkiffAgent 命中)→ 延续;无(首次/进程重启)→ createSkiffAgent
22
+ * 以固定 id 新建(角色不变,记忆从新开始——重启后自动重建,与 3100 问答页同语义)。
23
+ */
24
+ export declare function handleIncoming(ctx: Context, root: string, _accountId: string, cred: WeixinAccountCredential, msg: {
25
+ from_user_id?: string;
26
+ context_token?: string;
27
+ item_list?: Array<{
28
+ type?: number;
29
+ text_item?: {
30
+ text?: string;
31
+ };
32
+ }>;
33
+ }): Promise<void>;
34
+ /**
35
+ * 启动/重建某 CCC 的桥:读配置 → 对每个 enabled + 有凭据的账号启动轮询循环。
36
+ * 已存在(配置变化热重建)→ 先停旧循环再启动新的。
37
+ */
38
+ export declare function syncCccBridge(ctx: Context, root: string): void;
39
+ /** 停止某 CCC 的桥(移除账号/禁用时) */
40
+ export declare function stopCccBridge(root: string): void;
41
+ /** 停止全部桥(插件 dispose) */
42
+ export declare function stopAllBridges(): void;
43
+ /** 桥状态快照(面板数据源):每 CCC → 每账号 → 轮询健康 */
44
+ export declare function weixinBridgeStatus(): Array<{
45
+ ccc: string;
46
+ accounts: Array<{
47
+ accountId: string;
48
+ lastPollAt: number;
49
+ lastError?: string;
50
+ }>;
51
+ }>;
52
+ /**
53
+ * 装配(index.ts 调用):扫描 live CCC 启动桥 + 监听会话变化。
54
+ * 事件驱动(对齐 autotrajectory v1.26.15):live 会话出现 → 同步该 CCC 桥。
55
+ */
56
+ export declare function registerWeixinBridge(ctx: Context): void;