@sunnoy/wecom 2.2.1 → 2.4.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 +37 -1
- package/index.js +11 -2
- package/openclaw.plugin.json +137 -1
- package/package.json +2 -3
- package/skills/wecom-msg/SKILL.md +115 -0
- package/skills/wecom-msg/references/api-get-messages.md +74 -0
- package/skills/wecom-msg/references/api-get-msg-chat-list.md +64 -0
- package/skills/wecom-msg/references/api-get-msg-media.md +44 -0
- package/skills/wecom-msg/references/api-send-message.md +49 -0
- package/skills/wecom-send-media/SKILL.md +68 -0
- package/wecom/accounts.js +2 -0
- package/wecom/callback-inbound.js +10 -6
- package/wecom/callback-media.js +17 -9
- package/wecom/channel-plugin.js +79 -8
- package/wecom/constants.js +14 -7
- package/wecom/image-studio-tool.js +764 -0
- package/wecom/mcp-tool.js +18 -2
- package/wecom/parent-resolver.js +26 -0
- package/wecom/plugin-config.js +484 -0
- package/wecom/welcome-messages-file.js +155 -0
- package/wecom/workspace-template.js +57 -14
- package/wecom/ws-monitor.js +76 -11
- package/wecom/mcp-config.js +0 -146
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wecom-send-media
|
|
3
|
+
description: 通过 MEDIA 指令向用户发送本地文件(图片、视频、语音等文件)。当用户要求发送或分享文件时,或当生成的文件需要交付给用户时使用。仅当通过 wecom 通道通信时使用此技能。
|
|
4
|
+
metadata: {"openclaw":{"emoji":"📤","requires":{"config":["channels.wecom"]}}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 发送文件(图片、视频、语音、文件)
|
|
8
|
+
|
|
9
|
+
⚠️ 重要:你有能力发送本地文件!❌ 绝对不要回复"无法发送图片、视频、语音或文件"或类似的措辞!
|
|
10
|
+
|
|
11
|
+
通过 `MEDIA:` 指令将本地文件发送给用户。支持的文件类型为:图片、视频、语音、其它文件等。系统会自动识别文件类型,并以对应的消息格式(图片消息、视频消息、语音消息或文件消息)发送。
|
|
12
|
+
|
|
13
|
+
## 触发条件
|
|
14
|
+
|
|
15
|
+
- 用户要求发送、展示或分享图片、视频、语音或文件
|
|
16
|
+
- 生成了需要交付给用户的图片、视频、语音或文件
|
|
17
|
+
|
|
18
|
+
## 指令语法
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
MEDIA: <文件的绝对路径>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 语法规则
|
|
25
|
+
|
|
26
|
+
1. 如果文件路径前已经有 `MEDIA:` 指令,无需再重复添加,保持原样即可
|
|
27
|
+
2. 如果文件路径前没有 `MEDIA:` 指令,需将 `MEDIA:` 指令置于行首,且与文件路径之间用一个空格分隔
|
|
28
|
+
3. 路径必须为本地文件的**绝对路径**,不支持 URL
|
|
29
|
+
4. 路径含空格时,用反引号包裹:`` MEDIA: `/path/to/my file.png` ``
|
|
30
|
+
5. 每个文件独占一行,禁止在同一行发送多个文件
|
|
31
|
+
6. `MEDIA:` 指令的前后可以附加文字说明,文字与指令各占独立行
|
|
32
|
+
|
|
33
|
+
## 文件存放
|
|
34
|
+
|
|
35
|
+
将生成的文件优先存放至 `~/.openclaw/workspace/` 目录,确保路径可访问。
|
|
36
|
+
|
|
37
|
+
## 文件大小与格式限制
|
|
38
|
+
|
|
39
|
+
| 类型 | 大小上限 | 格式要求 | 备注 |
|
|
40
|
+
|------|----------|----------|------|
|
|
41
|
+
| 图片 | 10 MB | 常见图片格式均可 | openclaw 默认会对图片进行压缩处理 |
|
|
42
|
+
| 视频 | 10 MB | 常见视频格式均可 | |
|
|
43
|
+
| 语音 | 2 MB | **仅支持 AMR 格式**(`.amr`) | |
|
|
44
|
+
| 文件 | 20 MB | 不限 | |
|
|
45
|
+
|
|
46
|
+
⚠️ 重要:请务必**先检查文件大小和格式**是否满足要求。如果不满足要求,请主动告知用户以下“降级与限制处理”措施:
|
|
47
|
+
|
|
48
|
+
- **图片压缩情况**:openclaw 默认会对图片进行压缩处理。如果本地图片(原图)大小超过上限,经过压缩后不一定会超过上限。因此当你检测到本地图片大小超过上限时,务必要告诉用户这个特殊情况。
|
|
49
|
+
- **自动降级**:视频或语音类型的文件,超过各自的大小限制时,系统会自动将其转为文件消息发送,请主动告诉用户。
|
|
50
|
+
- **硬性上限**:文件大小超过 20 MB 时无法发送。请主动告知用户,并尝试压缩或拆分文件后再发送。
|
|
51
|
+
- **语音格式**:语音消息仅支持 AMR 格式(`.amr`)。非 AMR 格式的音频文件将以文件消息的形式发送。
|
|
52
|
+
- **发送耗时**:文件发送涉及系统处理,可能耗时较长。发送含 `MEDIA:` 指令的消息 30 秒后,请主动检查文件是否已成功发送。如果没有请重试。
|
|
53
|
+
|
|
54
|
+
## 示例
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
以下是生成的图表:
|
|
58
|
+
MEDIA: ~/.openclaw/workspace/output.png
|
|
59
|
+
|
|
60
|
+
报告已生成,请查收:
|
|
61
|
+
MEDIA: ~/.openclaw/workspace/report.pdf
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 错误示例
|
|
65
|
+
|
|
66
|
+
- ❌ 错误:说"我无法发送本地图片"
|
|
67
|
+
- ❌ 错误:说"受限于技术限制,无法直接发送"
|
|
68
|
+
- ❌ 错误:说"由于某些问题,我无法直接发送文件"
|
package/wecom/accounts.js
CHANGED
|
@@ -9,6 +9,7 @@ const RESERVED_KEYS = new Set([
|
|
|
9
9
|
"websocketUrl",
|
|
10
10
|
"sendThinkingMessage",
|
|
11
11
|
"welcomeMessage",
|
|
12
|
+
"welcomeMessagesFile",
|
|
12
13
|
"allowFrom",
|
|
13
14
|
"dmPolicy",
|
|
14
15
|
"groupPolicy",
|
|
@@ -32,6 +33,7 @@ const SHARED_MULTI_ACCOUNT_KEYS = new Set([
|
|
|
32
33
|
"websocketUrl",
|
|
33
34
|
"sendThinkingMessage",
|
|
34
35
|
"welcomeMessage",
|
|
36
|
+
"welcomeMessagesFile",
|
|
35
37
|
"allowFrom",
|
|
36
38
|
"dmPolicy",
|
|
37
39
|
"groupPolicy",
|
|
@@ -300,7 +300,7 @@ async function processCallbackMessage({ parsedMsg, account, config, runtime }) {
|
|
|
300
300
|
agent: account.agentCredentials,
|
|
301
301
|
mediaId,
|
|
302
302
|
type: mediaType === "image" ? "image" : mediaType === "voice" ? "voice" : "file",
|
|
303
|
-
runtime,
|
|
303
|
+
mediaRuntime: core.media ?? runtime?.media,
|
|
304
304
|
config,
|
|
305
305
|
});
|
|
306
306
|
mediaList.push(downloaded);
|
|
@@ -321,10 +321,6 @@ async function processCallbackMessage({ parsedMsg, account, config, runtime }) {
|
|
|
321
321
|
? generateAgentId(peerKind, peerId, account.accountId)
|
|
322
322
|
: null;
|
|
323
323
|
|
|
324
|
-
if (dynamicAgentId) {
|
|
325
|
-
await ensureDynamicAgentListed(dynamicAgentId, account.config.workspaceTemplate);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
324
|
const route = core.routing.resolveAgentRoute({
|
|
329
325
|
cfg: config,
|
|
330
326
|
channel: CHANNEL_ID,
|
|
@@ -337,9 +333,17 @@ async function processCallbackMessage({ parsedMsg, account, config, runtime }) {
|
|
|
337
333
|
config.bindings.some(
|
|
338
334
|
(b) => b.match?.channel === CHANNEL_ID && b.match?.accountId === account.accountId,
|
|
339
335
|
);
|
|
336
|
+
|
|
340
337
|
if (dynamicAgentId && !hasExplicitBinding) {
|
|
338
|
+
const routeAgentId = route.agentId;
|
|
339
|
+
// Use the account's configured agentId as the base for property inheritance
|
|
340
|
+
// (model, subagents, tools). route.agentId may resolve to "main" when
|
|
341
|
+
// there is no explicit binding, but the account's agentId points to the
|
|
342
|
+
// actual parent agent whose properties the dynamic agent should inherit.
|
|
343
|
+
const baseAgentId = account.config.agentId || routeAgentId;
|
|
344
|
+
await ensureDynamicAgentListed(dynamicAgentId, account.config.workspaceTemplate, baseAgentId);
|
|
345
|
+
route.sessionKey = route.sessionKey.replace(`agent:${routeAgentId}:`, `agent:${dynamicAgentId}:`);
|
|
341
346
|
route.agentId = dynamicAgentId;
|
|
342
|
-
route.sessionKey = `agent:${dynamicAgentId}:${peerKind}:${peerId}`;
|
|
343
347
|
}
|
|
344
348
|
|
|
345
349
|
// Build a body object that mirrors the WS frame.body structure expected by
|
package/wecom/callback-media.js
CHANGED
|
@@ -12,6 +12,12 @@ import { getAccessToken } from "./agent-api.js";
|
|
|
12
12
|
import { wecomFetch } from "./http.js";
|
|
13
13
|
import { AGENT_API_ENDPOINTS, CALLBACK_MEDIA_DOWNLOAD_TIMEOUT_MS } from "./constants.js";
|
|
14
14
|
|
|
15
|
+
function resolveManagedCallbackMediaDir() {
|
|
16
|
+
const override = process.env.OPENCLAW_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim();
|
|
17
|
+
const stateDir = override || path.join(process.env.HOME || "/tmp", ".openclaw");
|
|
18
|
+
return path.join(stateDir, "media", "wecom");
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Download a WeCom media file (image / voice / file) by MediaId via the
|
|
17
23
|
* self-built app access token and save it through the core media runtime.
|
|
@@ -20,11 +26,11 @@ import { AGENT_API_ENDPOINTS, CALLBACK_MEDIA_DOWNLOAD_TIMEOUT_MS } from "./const
|
|
|
20
26
|
* @param {object} params.agent - { corpId, corpSecret, agentId }
|
|
21
27
|
* @param {string} params.mediaId - WeCom MediaId
|
|
22
28
|
* @param {"image"|"voice"|"file"} params.type - media type hint
|
|
23
|
-
* @param {object} params.
|
|
29
|
+
* @param {object} [params.mediaRuntime] - OpenClaw media runtime (for saveMediaBuffer)
|
|
24
30
|
* @param {object} params.config - OpenClaw config (for mediaMaxMb)
|
|
25
31
|
* @returns {Promise<{ path: string, contentType: string }>}
|
|
26
32
|
*/
|
|
27
|
-
export async function downloadCallbackMedia({ agent, mediaId, type,
|
|
33
|
+
export async function downloadCallbackMedia({ agent, mediaId, type, mediaRuntime, config }) {
|
|
28
34
|
const token = await getAccessToken(agent);
|
|
29
35
|
const url = `${AGENT_API_ENDPOINTS.DOWNLOAD_MEDIA}?access_token=${encodeURIComponent(token)}&media_id=${encodeURIComponent(mediaId)}`;
|
|
30
36
|
|
|
@@ -57,20 +63,22 @@ export async function downloadCallbackMedia({ agent, mediaId, type, runtime, con
|
|
|
57
63
|
(type === "image" ? `${mediaId}.jpg` : type === "voice" ? `${mediaId}.amr` : mediaId);
|
|
58
64
|
|
|
59
65
|
// Save via core media runtime when available
|
|
60
|
-
if (typeof
|
|
61
|
-
const saved = await
|
|
66
|
+
if (typeof mediaRuntime?.saveMediaBuffer === "function") {
|
|
67
|
+
const saved = await mediaRuntime.saveMediaBuffer(buffer, contentType, "inbound", maxBytes, filename);
|
|
62
68
|
return { path: saved.path, contentType: saved.contentType };
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
// Fallback:
|
|
66
|
-
|
|
67
|
-
const { writeFile } = await import("node:fs/promises");
|
|
71
|
+
// Fallback: keep callback media under the managed OpenClaw media root so
|
|
72
|
+
// stageSandboxMedia can safely copy it into the agent sandbox later.
|
|
73
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
68
74
|
const ext = path.extname(filename) || (type === "image" ? ".jpg" : ".bin");
|
|
75
|
+
const mediaDir = resolveManagedCallbackMediaDir();
|
|
69
76
|
const tempPath = path.join(
|
|
70
|
-
|
|
77
|
+
mediaDir,
|
|
71
78
|
`wecom-cb-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`,
|
|
72
79
|
);
|
|
80
|
+
await mkdir(mediaDir, { recursive: true, mode: 0o700 });
|
|
73
81
|
await writeFile(tempPath, buffer);
|
|
74
|
-
logger.debug(`[CB] Media saved to
|
|
82
|
+
logger.debug(`[CB] Media saved to managed path: ${tempPath}`);
|
|
75
83
|
return { path: tempPath, contentType };
|
|
76
84
|
}
|
package/wecom/channel-plugin.js
CHANGED
|
@@ -24,7 +24,7 @@ import { wecomOnboardingAdapter } from "./onboarding.js";
|
|
|
24
24
|
import { getAccountTelemetry, recordOutboundActivity } from "./runtime-telemetry.js";
|
|
25
25
|
import { getOpenclawConfig, getRuntime, setOpenclawConfig } from "./state.js";
|
|
26
26
|
import { resolveWecomTarget } from "./target.js";
|
|
27
|
-
import { webhookSendFile, webhookSendImage, webhookSendMarkdown, webhookUploadFile } from "./webhook-bot.js";
|
|
27
|
+
import { webhookSendFile, webhookSendImage, webhookSendMarkdown, webhookSendText, webhookUploadFile } from "./webhook-bot.js";
|
|
28
28
|
import { loadOutboundMediaFromUrl as loadOutboundMediaFromUrlCompat } from "./openclaw-compat.js";
|
|
29
29
|
import {
|
|
30
30
|
CHANNEL_ID,
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
} from "./constants.js";
|
|
37
37
|
import { uploadAndSendMedia } from "./media-uploader.js";
|
|
38
38
|
import { getExtendedMediaLocalRoots } from "./openclaw-compat.js";
|
|
39
|
+
import { extractParentAgentId } from "./parent-resolver.js";
|
|
39
40
|
import { sendWsMessage, startWsMonitor } from "./ws-monitor.js";
|
|
40
41
|
import { getWsClient } from "./ws-state.js";
|
|
41
42
|
|
|
@@ -141,7 +142,7 @@ function applyNetworkConfig(cfg, accountId) {
|
|
|
141
142
|
return account;
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
async function sendViaWebhook({ cfg, accountId, webhookName, text, mediaUrl, preparedMedia }) {
|
|
145
|
+
async function sendViaWebhook({ cfg, accountId, webhookName, text, mediaUrl, preparedMedia, replyFormat }) {
|
|
145
146
|
const account = resolveAccount(cfg, accountId);
|
|
146
147
|
const raw = account?.config?.webhooks?.[webhookName];
|
|
147
148
|
const url = raw ? (String(raw).startsWith("http") ? String(raw) : `${getWebhookBotSendUrl()}?key=${raw}`) : null;
|
|
@@ -149,8 +150,13 @@ async function sendViaWebhook({ cfg, accountId, webhookName, text, mediaUrl, pre
|
|
|
149
150
|
throw new Error(`unknown webhook target: ${webhookName}`);
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
const effectiveFormat = replyFormat || account?.agentReplyFormat || "markdown";
|
|
154
|
+
const sendWebhookText = effectiveFormat === "text"
|
|
155
|
+
? (opts) => webhookSendText(opts)
|
|
156
|
+
: (opts) => webhookSendMarkdown(opts);
|
|
157
|
+
|
|
152
158
|
if (!mediaUrl) {
|
|
153
|
-
await
|
|
159
|
+
await sendWebhookText({ url, content: text });
|
|
154
160
|
recordOutboundActivity({ accountId });
|
|
155
161
|
return { channel: CHANNEL_ID, messageId: `wecom-webhook-${Date.now()}` };
|
|
156
162
|
}
|
|
@@ -159,7 +165,7 @@ async function sendViaWebhook({ cfg, accountId, webhookName, text, mediaUrl, pre
|
|
|
159
165
|
preparedMedia ?? (await loadResolvedMedia(mediaUrl, { accountConfig: account?.config }));
|
|
160
166
|
|
|
161
167
|
if (text) {
|
|
162
|
-
await
|
|
168
|
+
await sendWebhookText({ url, content: text });
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
if (mediaType === "image") {
|
|
@@ -177,15 +183,17 @@ async function sendViaWebhook({ cfg, accountId, webhookName, text, mediaUrl, pre
|
|
|
177
183
|
return { channel: CHANNEL_ID, messageId: `wecom-webhook-${Date.now()}` };
|
|
178
184
|
}
|
|
179
185
|
|
|
180
|
-
async function sendViaAgent({ cfg, accountId, target, text, mediaUrl, preparedMedia }) {
|
|
181
|
-
const
|
|
186
|
+
async function sendViaAgent({ cfg, accountId, target, text, mediaUrl, preparedMedia, replyFormat }) {
|
|
187
|
+
const account = resolveAccount(cfg, accountId);
|
|
188
|
+
const agent = account?.agentCredentials;
|
|
182
189
|
if (!agent) {
|
|
183
190
|
throw new Error("Agent API is not configured for this account");
|
|
184
191
|
}
|
|
185
192
|
|
|
193
|
+
const effectiveFormat = replyFormat || account?.agentReplyFormat || "markdown";
|
|
186
194
|
if (text) {
|
|
187
195
|
for (const chunk of splitTextByByteLimit(text)) {
|
|
188
|
-
await agentSendText({ agent, ...target, text: chunk });
|
|
196
|
+
await agentSendText({ agent, ...target, text: chunk, format: effectiveFormat });
|
|
189
197
|
}
|
|
190
198
|
}
|
|
191
199
|
|
|
@@ -262,6 +270,7 @@ export const wecomChannelPlugin = {
|
|
|
262
270
|
websocketUrl: { type: "string" },
|
|
263
271
|
sendThinkingMessage: { type: "boolean" },
|
|
264
272
|
welcomeMessage: { type: "string" },
|
|
273
|
+
welcomeMessagesFile: { type: "string" },
|
|
265
274
|
dmPolicy: { enum: ["pairing", "allowlist", "open", "disabled"] },
|
|
266
275
|
allowFrom: { type: "array", items: { type: "string" } },
|
|
267
276
|
groupPolicy: { enum: ["open", "allowlist", "disabled"] },
|
|
@@ -291,6 +300,10 @@ export const wecomChannelPlugin = {
|
|
|
291
300
|
secret: { label: "Secret", sensitive: true },
|
|
292
301
|
websocketUrl: { label: "WebSocket URL", placeholder: DEFAULT_WS_URL },
|
|
293
302
|
welcomeMessage: { label: "Welcome Message" },
|
|
303
|
+
welcomeMessagesFile: {
|
|
304
|
+
label: "Welcome Messages File",
|
|
305
|
+
placeholder: "welcome-messages.json",
|
|
306
|
+
},
|
|
294
307
|
"agent.corpSecret": { sensitive: true, label: "Application Secret" },
|
|
295
308
|
"agent.replyFormat": { label: "Reply Format", placeholder: "text" },
|
|
296
309
|
"agent.callback.token": { label: "Callback Token" },
|
|
@@ -404,6 +417,7 @@ export const wecomChannelPlugin = {
|
|
|
404
417
|
try {
|
|
405
418
|
if (!target.toParty && !target.toTag) {
|
|
406
419
|
const wsTarget = target.chatId || target.toUser || to;
|
|
420
|
+
logger.debug(`[wecom] sendText: trying WS accountId=${resolvedAccountId} target=${wsTarget}`);
|
|
407
421
|
return await sendWsMessage({
|
|
408
422
|
to: wsTarget,
|
|
409
423
|
content: text,
|
|
@@ -411,9 +425,17 @@ export const wecomChannelPlugin = {
|
|
|
411
425
|
});
|
|
412
426
|
}
|
|
413
427
|
} catch (error) {
|
|
414
|
-
logger.warn(`[wecom] WS sendText failed, falling back
|
|
428
|
+
logger.warn(`[wecom] WS sendText failed (accountId=${resolvedAccountId}), falling back: ${error.message}`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Webhook fallback for accounts without Agent API (e.g. WS bot mode)
|
|
432
|
+
const account = resolveAccount(cfg, resolvedAccountId);
|
|
433
|
+
if (!account?.agentCredentials && account?.config?.webhooks?.default) {
|
|
434
|
+
logger.debug(`[wecom] sendText: Agent API unavailable, using webhook fallback accountId=${resolvedAccountId}`);
|
|
435
|
+
return sendViaWebhook({ cfg, accountId: resolvedAccountId, webhookName: "default", text });
|
|
415
436
|
}
|
|
416
437
|
|
|
438
|
+
logger.debug(`[wecom] sendText: trying Agent API accountId=${resolvedAccountId}`);
|
|
417
439
|
return sendViaAgent({
|
|
418
440
|
cfg,
|
|
419
441
|
accountId: resolvedAccountId,
|
|
@@ -631,6 +653,55 @@ export const wecomChannelPlugin = {
|
|
|
631
653
|
};
|
|
632
654
|
},
|
|
633
655
|
},
|
|
656
|
+
hooks: {
|
|
657
|
+
/**
|
|
658
|
+
* Ensure announce delivery uses a valid WeCom channel accountId.
|
|
659
|
+
*
|
|
660
|
+
* When a dynamic agent (e.g. wecom-yoyo-dm-xxx) spawns a sub-agent,
|
|
661
|
+
* the announce delivery may reference the dynamic agent ID as accountId.
|
|
662
|
+
* This hook resolves it to the actual WeCom account (e.g. yoyo) so the
|
|
663
|
+
* outbound sendText can find valid WS/Agent API credentials.
|
|
664
|
+
*/
|
|
665
|
+
subagent_delivery_target: async (event, ctx) => {
|
|
666
|
+
const origin = event.requesterOrigin;
|
|
667
|
+
if (!origin?.channel || origin.channel !== CHANNEL_ID) return;
|
|
668
|
+
|
|
669
|
+
const cfg = ctx?.cfg ?? getOpenclawConfig();
|
|
670
|
+
|
|
671
|
+
// Check whether current accountId already resolves to a valid account
|
|
672
|
+
const currentAccount = resolveAccount(cfg, origin.accountId);
|
|
673
|
+
if (currentAccount?.enabled) return;
|
|
674
|
+
|
|
675
|
+
// Try to extract the base account from a dynamic agent ID
|
|
676
|
+
const baseId = extractParentAgentId(origin.accountId);
|
|
677
|
+
if (baseId && baseId !== origin.accountId) {
|
|
678
|
+
const baseAccount = resolveAccount(cfg, baseId);
|
|
679
|
+
if (baseAccount?.enabled) {
|
|
680
|
+
logger.info(`[wecom] subagent_delivery_target: ${origin.accountId} → ${baseId}`);
|
|
681
|
+
return { origin: { ...origin, accountId: baseId } };
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Fallback to default account
|
|
686
|
+
const defaultId = resolveDefaultAccountId(cfg);
|
|
687
|
+
if (defaultId && defaultId !== origin.accountId) {
|
|
688
|
+
logger.info(`[wecom] subagent_delivery_target: fallback → ${defaultId}`);
|
|
689
|
+
return { origin: { ...origin, accountId: defaultId } };
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
subagent_spawned: async (event) => {
|
|
694
|
+
logger.info(
|
|
695
|
+
`[wecom] subagent spawned: child=${event.childSessionKey} requester=${event.requesterSessionKey}`,
|
|
696
|
+
);
|
|
697
|
+
},
|
|
698
|
+
|
|
699
|
+
subagent_ended: async (event) => {
|
|
700
|
+
logger.info(
|
|
701
|
+
`[wecom] subagent ended: target=${event.targetSessionKey} reason=${event.reason} outcome=${event.outcome}`,
|
|
702
|
+
);
|
|
703
|
+
},
|
|
704
|
+
},
|
|
634
705
|
};
|
|
635
706
|
|
|
636
707
|
export const wecomChannelPluginTesting = {};
|
package/wecom/constants.js
CHANGED
|
@@ -47,7 +47,8 @@ export const DEFAULT_WELCOME_MESSAGES = [
|
|
|
47
47
|
"/compact 压缩对话",
|
|
48
48
|
"/help 帮助",
|
|
49
49
|
"/status 查看状态",
|
|
50
|
-
"
|
|
50
|
+
"你可以让我生成和编辑图片了",
|
|
51
|
+
"你可以用语音跟我对话",
|
|
51
52
|
].join("\n"),
|
|
52
53
|
[
|
|
53
54
|
"终于唤醒我啦,我已经准备就绪!😄",
|
|
@@ -57,7 +58,8 @@ export const DEFAULT_WELCOME_MESSAGES = [
|
|
|
57
58
|
"/compact 压缩对话",
|
|
58
59
|
"/help 帮助",
|
|
59
60
|
"/status 查看状态",
|
|
60
|
-
"
|
|
61
|
+
"你可以让我生成和编辑图片了",
|
|
62
|
+
"你可以用语音跟我对话",
|
|
61
63
|
].join("\n"),
|
|
62
64
|
[
|
|
63
65
|
"欢迎回来,准备开始今天的工作吧!✨",
|
|
@@ -67,7 +69,8 @@ export const DEFAULT_WELCOME_MESSAGES = [
|
|
|
67
69
|
"/compact 压缩对话",
|
|
68
70
|
"/help 帮助",
|
|
69
71
|
"/status 查看状态",
|
|
70
|
-
"
|
|
72
|
+
"你可以让我生成和编辑图片了",
|
|
73
|
+
"你可以用语音跟我对话",
|
|
71
74
|
].join("\n"),
|
|
72
75
|
[
|
|
73
76
|
"嗨,我已经在线!🤖",
|
|
@@ -77,7 +80,8 @@ export const DEFAULT_WELCOME_MESSAGES = [
|
|
|
77
80
|
"/compact 压缩对话",
|
|
78
81
|
"/help 帮助",
|
|
79
82
|
"/status 查看状态",
|
|
80
|
-
"
|
|
83
|
+
"你可以让我生成和编辑图片了",
|
|
84
|
+
"你可以用语音跟我对话",
|
|
81
85
|
].join("\n"),
|
|
82
86
|
[
|
|
83
87
|
"今天也一起高效开工吧!🚀",
|
|
@@ -87,7 +91,8 @@ export const DEFAULT_WELCOME_MESSAGES = [
|
|
|
87
91
|
"/compact 压缩对话",
|
|
88
92
|
"/help 帮助",
|
|
89
93
|
"/status 查看状态",
|
|
90
|
-
"
|
|
94
|
+
"你可以让我生成和编辑图片了",
|
|
95
|
+
"你可以用语音跟我对话",
|
|
91
96
|
].join("\n"),
|
|
92
97
|
[
|
|
93
98
|
"叮咚,你的数字助手已就位!🎉",
|
|
@@ -97,7 +102,8 @@ export const DEFAULT_WELCOME_MESSAGES = [
|
|
|
97
102
|
"/compact 压缩对话",
|
|
98
103
|
"/help 帮助",
|
|
99
104
|
"/status 查看状态",
|
|
100
|
-
"
|
|
105
|
+
"你可以让我生成和编辑图片了",
|
|
106
|
+
"你可以用语音跟我对话",
|
|
101
107
|
].join("\n"),
|
|
102
108
|
[
|
|
103
109
|
"灵感加载完成,随时可以开聊!💡",
|
|
@@ -107,7 +113,8 @@ export const DEFAULT_WELCOME_MESSAGES = [
|
|
|
107
113
|
"/compact 压缩对话",
|
|
108
114
|
"/help 帮助",
|
|
109
115
|
"/status 查看状态",
|
|
110
|
-
"
|
|
116
|
+
"你可以让我生成和编辑图片了",
|
|
117
|
+
"你可以用语音跟我对话",
|
|
111
118
|
].join("\n"),
|
|
112
119
|
];
|
|
113
120
|
export const DEFAULT_WELCOME_MESSAGE = DEFAULT_WELCOME_MESSAGES[0];
|