@core-workspace/infoflow-openclaw-plugin 2026.3.9 → 2026.3.27-beta.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/CHANGELOG.md +91 -0
- package/CLAUDE.md +135 -0
- package/COLLABORATION_REPORT.md +209 -0
- package/PROJECT_GUIDE.md +355 -0
- package/README.md +158 -66
- package/docs/dev-guide.md +63 -50
- package/docs/qa-feature-list.md +452 -0
- package/docs/webhook-guide.md +178 -0
- package/index.ts +28 -2
- package/openclaw.plugin.json +131 -21
- package/package.json +16 -3
- package/scripts/deploy.sh +66 -7
- package/scripts/postinstall.cjs +80 -0
- package/skills/infoflow-dev/SKILL.md +2 -2
- package/skills/infoflow-dev/references/api.md +1 -1
- package/src/adapter/inbound/webhook-parser.ts +27 -5
- package/src/adapter/inbound/ws-receiver.ts +304 -43
- package/src/adapter/outbound/markdown-local-images.ts +80 -0
- package/src/adapter/outbound/reply-dispatcher.ts +146 -65
- package/src/adapter/outbound/target-resolver.ts +4 -3
- package/src/channel/accounts.ts +97 -22
- package/src/channel/channel.ts +456 -12
- package/src/channel/media.ts +20 -6
- package/src/channel/monitor.ts +8 -3
- package/src/channel/outbound.ts +358 -21
- package/src/channel/streaming.ts +740 -0
- package/src/commands/changelog.ts +80 -0
- package/src/commands/doctor.ts +545 -0
- package/src/commands/logs.ts +449 -0
- package/src/commands/version.ts +20 -0
- package/src/compat/openclaw-sdk.ts +218 -0
- package/src/handler/message-handler.ts +673 -166
- package/src/logging.ts +1 -1
- package/src/runtime.ts +1 -1
- package/src/security/dm-policy.ts +1 -4
- package/src/security/group-policy.ts +174 -51
- package/src/tools/actions/index.ts +15 -13
- package/src/tools/cron/relay.ts +1154 -0
- package/src/tools/hooks/index.ts +13 -1
- package/src/tools/index.ts +714 -32
- package/src/types.ts +144 -25
- package/src/utils/audio/g722/dct_tables.ts +381 -0
- package/src/utils/audio/g722/decoder.ts +919 -0
- package/src/utils/audio/g722/defs.ts +105 -0
- package/src/utils/audio/g722/hd-parser.ts +247 -0
- package/src/utils/audio/g722/huff_tables.ts +240 -0
- package/src/utils/audio/g722/index.ts +78 -0
- package/src/utils/audio/g722/output_decoded.pcm +0 -0
- package/src/utils/audio/g722/output_decoded.wav +0 -0
- package/src/utils/audio/g722/tables.ts +173 -0
- package/src/utils/audio/g722/test_api.ts +31 -0
- package/src/utils/audio/g722/test_voice.hd +0 -0
- package/src/utils/bos/im-bos-client.ts +219 -0
- package/src/utils/group-agent-cache.ts +142 -0
- package/src/utils/token-adapter.ts +120 -51
package/src/types.ts
CHANGED
|
@@ -10,6 +10,18 @@ export type InfoflowDmPolicy = "open" | "pairing" | "allowlist";
|
|
|
10
10
|
export type InfoflowGroupPolicy = "open" | "allowlist" | "disabled";
|
|
11
11
|
export type InfoflowChatType = "direct" | "group";
|
|
12
12
|
export type InfoflowConnectionMode = "webhook" | "websocket";
|
|
13
|
+
export type InfoflowMessageFormat = "text" | "markdown" | "streaming-card";
|
|
14
|
+
|
|
15
|
+
export type InfoflowCronRelayConfig = {
|
|
16
|
+
/** Whether to relay cron run results into Infoflow. Default: true */
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
/** Skip runs already delivered by cron delivery config. Default: false */
|
|
19
|
+
includeAlreadyDelivered?: boolean;
|
|
20
|
+
/** Message prefix for relayed cron notifications. Default: "【定时任务】" */
|
|
21
|
+
prefix?: string;
|
|
22
|
+
/** Poll interval for scanning cron run logs (ms). Default: 2000 */
|
|
23
|
+
pollIntervalMs?: number;
|
|
24
|
+
};
|
|
13
25
|
|
|
14
26
|
/** Reply mode controlling bot behavior per group */
|
|
15
27
|
export type InfoflowReplyMode =
|
|
@@ -19,9 +31,14 @@ export type InfoflowReplyMode =
|
|
|
19
31
|
| "mention-and-watch"
|
|
20
32
|
| "proactive";
|
|
21
33
|
|
|
34
|
+
/** Group session mode: whether to split sessions by group or by group+user */
|
|
35
|
+
export type InfoflowGroupSessionMode = "group" | "user";
|
|
36
|
+
|
|
22
37
|
/** Per-group configuration overrides */
|
|
23
38
|
export type InfoflowGroupConfig = {
|
|
24
39
|
replyMode?: InfoflowReplyMode;
|
|
40
|
+
/** Group session mode: "group" = one session per group, "user" = one session per user in group */
|
|
41
|
+
groupSessionMode?: InfoflowGroupSessionMode;
|
|
25
42
|
systemPrompt?: string;
|
|
26
43
|
/** Enable follow-up replies after bot responds (default: true) */
|
|
27
44
|
followUp?: boolean;
|
|
@@ -29,8 +46,8 @@ export type InfoflowGroupConfig = {
|
|
|
29
46
|
followUpWindow?: number;
|
|
30
47
|
/** Names to watch for @mentions */
|
|
31
48
|
watchMentions?: string[];
|
|
32
|
-
/** Regex pattern to watch for in message content */
|
|
33
|
-
watchRegex?: string;
|
|
49
|
+
/** Regex pattern(s) to watch for in message content (string or array; any match triggers) */
|
|
50
|
+
watchRegex?: string | string[];
|
|
34
51
|
};
|
|
35
52
|
|
|
36
53
|
// ---------------------------------------------------------------------------
|
|
@@ -50,6 +67,8 @@ export type InfoflowInboundBodyItem = {
|
|
|
50
67
|
userid?: string;
|
|
51
68
|
/** IMAGE 类型 body item 的图片下载地址 */
|
|
52
69
|
downloadurl?: string;
|
|
70
|
+
/** Message ID in replyData items (always string; converted at inbound boundary) */
|
|
71
|
+
messageid?: string;
|
|
53
72
|
};
|
|
54
73
|
|
|
55
74
|
/** Mention IDs extracted from inbound group AT items (excluding the bot itself) */
|
|
@@ -60,6 +79,12 @@ export type InfoflowMentionIds = {
|
|
|
60
79
|
agentIds: number[];
|
|
61
80
|
};
|
|
62
81
|
|
|
82
|
+
/** Single agent (robot) info from the group member list API */
|
|
83
|
+
export type InfoflowGroupAgentInfo = {
|
|
84
|
+
agentId: number;
|
|
85
|
+
name: string;
|
|
86
|
+
};
|
|
87
|
+
|
|
63
88
|
// ---------------------------------------------------------------------------
|
|
64
89
|
// AT mention types
|
|
65
90
|
// ---------------------------------------------------------------------------
|
|
@@ -76,7 +101,7 @@ export type InfoflowAtOptions = {
|
|
|
76
101
|
export type InfoflowGroupMessageBodyItem =
|
|
77
102
|
| { type: "TEXT"; content: string }
|
|
78
103
|
| { type: "MD"; content: string }
|
|
79
|
-
| { type: "AT"; atall?: boolean; atuserids
|
|
104
|
+
| { type: "AT"; atall?: boolean; atuserids?: string[]; atagentids?: number[] }
|
|
80
105
|
| { type: "LINK"; href: string }
|
|
81
106
|
| { type: "IMAGE"; content: string };
|
|
82
107
|
|
|
@@ -86,7 +111,7 @@ export type InfoflowMessageContentItem = {
|
|
|
86
111
|
content: string;
|
|
87
112
|
};
|
|
88
113
|
|
|
89
|
-
/** Outbound reply/quote context for group messages */
|
|
114
|
+
/** Outbound reply/quote context for group and private messages */
|
|
90
115
|
export type InfoflowOutboundReply = {
|
|
91
116
|
/** Message ID of the message being replied to (string to preserve large integer precision) */
|
|
92
117
|
messageid: string;
|
|
@@ -96,6 +121,8 @@ export type InfoflowOutboundReply = {
|
|
|
96
121
|
replytype?: "1" | "2";
|
|
97
122
|
/** User IMID (optional, if not provided, shows robot name) */
|
|
98
123
|
imid?: string;
|
|
124
|
+
/** Secondary message ID for private message reply (maps to API field "msgid2") */
|
|
125
|
+
msgid2?: string;
|
|
99
126
|
};
|
|
100
127
|
|
|
101
128
|
// ---------------------------------------------------------------------------
|
|
@@ -112,8 +139,10 @@ export type InfoflowAccountConfig = {
|
|
|
112
139
|
appSecret?: string;
|
|
113
140
|
/** 连接方式:webhook(默认)或 websocket */
|
|
114
141
|
connectionMode?: InfoflowConnectionMode;
|
|
115
|
-
/** WebSocket Gateway
|
|
142
|
+
/** WebSocket Gateway 域名,用于 Phase 1 端点分配请求(仅 websocket 模式使用) */
|
|
116
143
|
wsGateway?: string;
|
|
144
|
+
/** WebSocket 连接域名,用于 Phase 2 实际 WS 握手(不填则使用服务端返回的地址) */
|
|
145
|
+
wsConnectDomain?: string;
|
|
117
146
|
dmPolicy?: InfoflowDmPolicy;
|
|
118
147
|
allowFrom?: string[];
|
|
119
148
|
groupPolicy?: InfoflowGroupPolicy;
|
|
@@ -121,19 +150,29 @@ export type InfoflowAccountConfig = {
|
|
|
121
150
|
requireMention?: boolean;
|
|
122
151
|
/** Robot name for matching @mentions in group messages */
|
|
123
152
|
robotName?: string;
|
|
153
|
+
/** Actual robot id from Infoflow (auto-discovered when bot is @mentioned; used to ignore own messages). Not user-configured. */
|
|
154
|
+
robotId?: string;
|
|
124
155
|
/** Names to watch for @mentions; when someone @mentions a person in this list,
|
|
125
156
|
* the bot analyzes the message and replies only if confident. */
|
|
126
157
|
watchMentions?: string[];
|
|
127
|
-
/** Regex pattern to watch for in message content; triggers bot activation when
|
|
128
|
-
watchRegex?: string;
|
|
158
|
+
/** Regex pattern(s) to watch for in message content; triggers bot activation when any pattern matches */
|
|
159
|
+
watchRegex?: string | string[];
|
|
129
160
|
/** Reply mode controlling bot engagement level in groups */
|
|
130
161
|
replyMode?: InfoflowReplyMode;
|
|
162
|
+
/** Group session mode: "group" = one session per group, "user" = one session per user in group (default: "group") */
|
|
163
|
+
groupSessionMode?: InfoflowGroupSessionMode;
|
|
131
164
|
/** Enable follow-up replies after bot responds to a mention (default: true) */
|
|
132
165
|
followUp?: boolean;
|
|
133
166
|
/** Follow-up window in seconds after last bot reply (default: 300) */
|
|
134
167
|
followUpWindow?: number;
|
|
135
168
|
/** 如流企业后台的应用ID(私聊消息撤回依赖此字段) */
|
|
136
169
|
appAgentId?: number;
|
|
170
|
+
/** Default Infoflow target used by cron relay when the run has no explicit target */
|
|
171
|
+
defaultTo?: string;
|
|
172
|
+
/** Private plugin data directory under OpenClaw stateDir */
|
|
173
|
+
privateDataDir?: string;
|
|
174
|
+
/** Cron relay options */
|
|
175
|
+
cronRelay?: InfoflowCronRelayConfig;
|
|
137
176
|
/** Per-group configuration overrides, keyed by group ID */
|
|
138
177
|
groups?: Record<string, InfoflowGroupConfig>;
|
|
139
178
|
accounts?: Record<string, InfoflowAccountConfig>;
|
|
@@ -154,14 +193,20 @@ export type InfoflowAccountConfig = {
|
|
|
154
193
|
* Message format for outbound private (DM) messages: "text" or "markdown".
|
|
155
194
|
* Default: "text"
|
|
156
195
|
*/
|
|
157
|
-
dmMessageFormat?:
|
|
196
|
+
dmMessageFormat?: InfoflowMessageFormat;
|
|
158
197
|
/**
|
|
159
198
|
* Message format for outbound group messages: "text" or "markdown".
|
|
160
199
|
* Note: "markdown" does not support reply-to (quote) context — the bot
|
|
161
200
|
* will still reply but without quoting the user's message.
|
|
162
201
|
* Default: "text"
|
|
163
202
|
*/
|
|
164
|
-
groupMessageFormat?:
|
|
203
|
+
groupMessageFormat?: InfoflowMessageFormat;
|
|
204
|
+
/**
|
|
205
|
+
* Maximum character limit per outbound message chunk.
|
|
206
|
+
* Long messages are automatically split into multiple messages each within this limit.
|
|
207
|
+
* Default: 1800
|
|
208
|
+
*/
|
|
209
|
+
textChunkLimit?: number;
|
|
165
210
|
};
|
|
166
211
|
|
|
167
212
|
export type ResolvedInfoflowAccount = {
|
|
@@ -179,8 +224,10 @@ export type ResolvedInfoflowAccount = {
|
|
|
179
224
|
appSecret: string;
|
|
180
225
|
/** 连接方式:webhook(默认)或 websocket */
|
|
181
226
|
connectionMode?: InfoflowConnectionMode;
|
|
182
|
-
/** WebSocket Gateway
|
|
227
|
+
/** WebSocket Gateway 域名,用于 Phase 1 端点分配请求(仅 websocket 模式使用) */
|
|
183
228
|
wsGateway?: string;
|
|
229
|
+
/** WebSocket 连接域名,用于 Phase 2 实际 WS 握手(不填则使用服务端返回的地址) */
|
|
230
|
+
wsConnectDomain?: string;
|
|
184
231
|
dmPolicy?: InfoflowDmPolicy;
|
|
185
232
|
allowFrom?: string[];
|
|
186
233
|
groupPolicy?: InfoflowGroupPolicy;
|
|
@@ -188,36 +235,62 @@ export type ResolvedInfoflowAccount = {
|
|
|
188
235
|
requireMention?: boolean;
|
|
189
236
|
/** Robot name for matching @mentions in group messages */
|
|
190
237
|
robotName?: string;
|
|
238
|
+
/** Actual robot id from Infoflow (auto-discovered when bot is @mentioned; used to ignore own messages). Not user-configured. */
|
|
239
|
+
robotId?: string;
|
|
191
240
|
/** Names to watch for @mentions; when someone @mentions a person in this list,
|
|
192
241
|
* the bot analyzes the message and replies only if confident. */
|
|
193
242
|
watchMentions?: string[];
|
|
194
|
-
/** Regex pattern to watch for in message content; triggers bot activation when
|
|
195
|
-
watchRegex?: string;
|
|
243
|
+
/** Regex pattern(s) to watch for in message content; triggers bot activation when any pattern matches */
|
|
244
|
+
watchRegex?: string | string[];
|
|
196
245
|
/** Reply mode controlling bot engagement level in groups */
|
|
197
246
|
replyMode?: InfoflowReplyMode;
|
|
247
|
+
/** Group session mode: "group" = one session per group, "user" = one session per user in group (default: "group") */
|
|
248
|
+
groupSessionMode?: InfoflowGroupSessionMode;
|
|
198
249
|
/** Enable follow-up replies after bot responds to a mention (default: true) */
|
|
199
250
|
followUp?: boolean;
|
|
200
251
|
/** Follow-up window in seconds after last bot reply (default: 300) */
|
|
201
252
|
followUpWindow?: number;
|
|
202
253
|
/** 如流企业后台的应用ID(私聊消息撤回依赖此字段) */
|
|
203
254
|
appAgentId?: number;
|
|
255
|
+
/** Default Infoflow target used by cron relay when the run has no explicit target */
|
|
256
|
+
defaultTo?: string;
|
|
257
|
+
/** Private plugin data directory under OpenClaw stateDir */
|
|
258
|
+
privateDataDir: string;
|
|
259
|
+
/** Cron relay options */
|
|
260
|
+
cronRelay: {
|
|
261
|
+
enabled: boolean;
|
|
262
|
+
includeAlreadyDelivered: boolean;
|
|
263
|
+
prefix: string;
|
|
264
|
+
pollIntervalMs: number;
|
|
265
|
+
};
|
|
204
266
|
/** Per-group configuration overrides, keyed by group ID */
|
|
205
267
|
groups?: Record<string, InfoflowGroupConfig>;
|
|
268
|
+
/**
|
|
269
|
+
* Send a "processing" hint message before the real reply when processing is slow.
|
|
270
|
+
* Default: true
|
|
271
|
+
*/
|
|
272
|
+
processingHint?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Delay in seconds before sending the "processing" hint message.
|
|
275
|
+
* Default: 5
|
|
276
|
+
*/
|
|
277
|
+
processingHintDelay?: number;
|
|
206
278
|
/**
|
|
207
279
|
* Message format for outbound private (DM) messages: "text" or "markdown".
|
|
208
280
|
* Default: "text"
|
|
209
281
|
*/
|
|
210
|
-
dmMessageFormat?:
|
|
282
|
+
dmMessageFormat?: InfoflowMessageFormat;
|
|
211
283
|
/**
|
|
212
284
|
* Message format for outbound group messages: "text" or "markdown".
|
|
213
285
|
* Note: "markdown" does not support reply-to (quote) context.
|
|
214
286
|
* Default: "text"
|
|
215
287
|
*/
|
|
216
|
-
groupMessageFormat?:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
288
|
+
groupMessageFormat?: InfoflowMessageFormat;
|
|
289
|
+
/**
|
|
290
|
+
* Maximum character limit per outbound message chunk.
|
|
291
|
+
* Default: 1800
|
|
292
|
+
*/
|
|
293
|
+
textChunkLimit?: number;
|
|
221
294
|
};
|
|
222
295
|
};
|
|
223
296
|
|
|
@@ -225,16 +298,50 @@ export type ResolvedInfoflowAccount = {
|
|
|
225
298
|
// Message types
|
|
226
299
|
// ---------------------------------------------------------------------------
|
|
227
300
|
|
|
301
|
+
/** 如流消息发送者身份(判别联合) */
|
|
302
|
+
export type InfoflowSender =
|
|
303
|
+
| {
|
|
304
|
+
/** 普通账户 */
|
|
305
|
+
kind: "regular";
|
|
306
|
+
/** UUAP 用户名(fromuserid) */
|
|
307
|
+
userid: string;
|
|
308
|
+
/** 显示名称 */
|
|
309
|
+
name?: string;
|
|
310
|
+
/** 数字 IMID(fromid) */
|
|
311
|
+
imid?: string;
|
|
312
|
+
}
|
|
313
|
+
| {
|
|
314
|
+
/** 机器人账户 */
|
|
315
|
+
kind: "robot";
|
|
316
|
+
/** 机器人 agent ID(数字,对应 robotid / fromid);群聊 ALL_MESSAGE_FORWARD 场景暂无法获取,可能为空 */
|
|
317
|
+
agentid?: string;
|
|
318
|
+
/** 显示名称 */
|
|
319
|
+
name?: string;
|
|
320
|
+
/** 数字 IMID(fromid,机器人的 fromid = robotid) */
|
|
321
|
+
imid?: string;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/** 输出 InfoflowSender 的描述字符串,用于日志和调试 */
|
|
325
|
+
export function descSender(s: InfoflowSender): string {
|
|
326
|
+
if (s.kind === "regular") {
|
|
327
|
+
return `regular(${s.userid}/${s.name ?? "-"}/${s.imid ?? "-"})`;
|
|
328
|
+
}
|
|
329
|
+
return `robot(${s.agentid ?? "-"}/${s.name ?? "-"}/${s.imid ?? "-"})`;
|
|
330
|
+
}
|
|
331
|
+
|
|
228
332
|
export type InfoflowMessageEvent = {
|
|
229
|
-
|
|
333
|
+
/** 发送者身份信息(包含 kind/id/name/imid) */
|
|
334
|
+
sender: InfoflowSender;
|
|
230
335
|
mes: string;
|
|
231
336
|
chatType: InfoflowChatType;
|
|
232
337
|
groupId?: number;
|
|
233
|
-
|
|
338
|
+
|
|
234
339
|
/** Whether the bot was @mentioned in the message */
|
|
235
340
|
wasMentioned?: boolean;
|
|
236
341
|
/** Original message ID from Infoflow */
|
|
237
342
|
messageId?: string;
|
|
343
|
+
/** Secondary message ID from Infoflow (used for private message reply as "msgid2") */
|
|
344
|
+
msgid2?: string;
|
|
238
345
|
/** Unix millisecond timestamp of the message */
|
|
239
346
|
timestamp?: number;
|
|
240
347
|
/** Raw message text preserving @mentions (for RawBody) */
|
|
@@ -247,8 +354,20 @@ export type InfoflowMessageEvent = {
|
|
|
247
354
|
replyContext?: string[];
|
|
248
355
|
/** Image download URLs extracted from IMAGE body items (group) or PicUrl (private) */
|
|
249
356
|
imageUrls?: string[];
|
|
250
|
-
/**
|
|
251
|
-
|
|
357
|
+
/** Agent-visible body text with robotid annotations (e.g. @name (robotid:N)) for LLM */
|
|
358
|
+
bodyForAgent?: string;
|
|
359
|
+
/** Whether the message is a quoted reply to one of the bot's own previous messages */
|
|
360
|
+
isReplyToBot?: boolean;
|
|
361
|
+
/** 语音消息的音频下载地址 */
|
|
362
|
+
voiceUrl?: string;
|
|
363
|
+
/** 本地 hd→wav 转换后的 WAV 文件路径 */
|
|
364
|
+
localVoicePath?: string;
|
|
365
|
+
/** 消息来源平台标识 */
|
|
366
|
+
fromPlatform?: string;
|
|
367
|
+
/** 如流应用的 agentId */
|
|
368
|
+
agentId?: string;
|
|
369
|
+
/** 如流开放平台的 OpenCode(加密的用户/应用凭证) */
|
|
370
|
+
openCode?: string;
|
|
252
371
|
};
|
|
253
372
|
|
|
254
373
|
// ---------------------------------------------------------------------------
|
|
@@ -256,21 +375,21 @@ export type InfoflowMessageEvent = {
|
|
|
256
375
|
// ---------------------------------------------------------------------------
|
|
257
376
|
|
|
258
377
|
export type HandleInfoflowMessageParams = {
|
|
259
|
-
cfg: import("openclaw/plugin-sdk").OpenClawConfig;
|
|
378
|
+
cfg: import("openclaw/plugin-sdk/plugin-entry").OpenClawConfig;
|
|
260
379
|
event: InfoflowMessageEvent;
|
|
261
380
|
accountId: string;
|
|
262
381
|
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
|
263
382
|
};
|
|
264
383
|
|
|
265
384
|
export type HandlePrivateChatParams = {
|
|
266
|
-
cfg: import("openclaw/plugin-sdk").OpenClawConfig;
|
|
385
|
+
cfg: import("openclaw/plugin-sdk/plugin-entry").OpenClawConfig;
|
|
267
386
|
msgData: Record<string, unknown>;
|
|
268
387
|
accountId: string;
|
|
269
388
|
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
|
270
389
|
};
|
|
271
390
|
|
|
272
391
|
export type HandleGroupChatParams = {
|
|
273
|
-
cfg: import("openclaw/plugin-sdk").OpenClawConfig;
|
|
392
|
+
cfg: import("openclaw/plugin-sdk/plugin-entry").OpenClawConfig;
|
|
274
393
|
msgData: Record<string, unknown>;
|
|
275
394
|
accountId: string;
|
|
276
395
|
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|