@ai-setting/roy-agent-core 1.7.77 → 1.7.85
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/dist/config/config-protection.d.ts +89 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +16 -0
- package/dist/env/agent/agent-component.d.ts +42 -21
- package/dist/env/agent/agent-component.js +2 -2
- package/dist/env/agent/index.js +2 -2
- package/dist/env/agent/types.d.ts +32 -12
- package/dist/env/error-reply.d.ts +6 -0
- package/dist/env/error-reply.js +1 -1
- package/dist/env/event-source/bounty-im-handler.d.ts +8 -0
- package/dist/env/event-source/es-command.d.ts +39 -0
- package/dist/env/event-source/es-plugins.d.ts +68 -0
- package/dist/env/event-source/event-source-component.d.ts +15 -0
- package/dist/env/event-source/event-source-handlers.d.ts +68 -0
- package/dist/env/event-source/event-source-handlers.js +20 -8
- package/dist/env/event-source/index.d.ts +5 -1
- package/dist/env/event-source/index.js +57 -8
- package/dist/env/event-source/types.d.ts +101 -1
- package/dist/env/event-source/wecom-card-action-handler.d.ts +141 -0
- package/dist/env/event-source/wecom-card-builder.d.ts +220 -0
- package/dist/env/event-source/wecom-cli-handler.d.ts +266 -0
- package/dist/env/event-source/wecom-cli-sender.d.ts +88 -0
- package/dist/env/event-source/wecom-media.d.ts +130 -0
- package/dist/env/event-source/wecom-respond-registry.d.ts +151 -0
- package/dist/env/event-source/wecom-ws-bridge.d.ts +213 -0
- package/dist/env/index.js +16 -14
- package/dist/env/task/delegate/delegate-tool.d.ts +62 -2
- package/dist/env/task/delegate/delegate-tool.js +3 -1
- package/dist/env/task/delegate/index.js +1 -1
- package/dist/env/task/index.js +2 -2
- package/dist/env/task/plugins/index.d.ts +2 -0
- package/dist/env/task/plugins/index.js +23 -2
- package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +41 -2
- package/dist/env/task/plugins/lark-card-patch-notifier.js +1 -1
- package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +16 -0
- package/dist/env/task/plugins/wecom-card-state-store.d.ts +121 -0
- package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +224 -0
- package/dist/env/tool/built-in/index.d.ts +2 -0
- package/dist/env/tool/built-in/index.js +6 -1
- package/dist/env/tool/index.js +3 -2
- package/dist/env/workflow/index.js +3 -3
- package/dist/env/workflow/plugins/index.js +2 -2
- package/dist/env/workflow/tools/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +68 -17
- package/dist/shared/@ai-setting/{roy-agent-core-v87njmq7.js → roy-agent-core-0dh8npnz.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-2stt4rp5.js +134 -0
- package/dist/shared/@ai-setting/{roy-agent-core-mww0mnd9.js → roy-agent-core-7bpj859q.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-w0qjeq2v.js → roy-agent-core-7dd0s3xb.js} +14 -4
- package/dist/shared/@ai-setting/{roy-agent-core-rsn44n9y.js → roy-agent-core-b31v3qpv.js} +851 -46
- package/dist/shared/@ai-setting/{roy-agent-core-0v27y99n.js → roy-agent-core-begm7ytz.js} +63 -2
- package/dist/shared/@ai-setting/{roy-agent-core-aa2j7bk9.js → roy-agent-core-cha0jc7n.js} +40 -26
- package/dist/shared/@ai-setting/{roy-agent-core-hd3n57zs.js → roy-agent-core-d1n4jbza.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9vhq7fvv.js → roy-agent-core-dkrvmjsh.js} +12 -74
- package/dist/shared/@ai-setting/{roy-agent-core-99nwgj84.js → roy-agent-core-fzw9xzm1.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-m3vjepzz.js +544 -0
- package/dist/shared/@ai-setting/{roy-agent-core-ew6qb5yx.js → roy-agent-core-p6ks90tb.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-rhtdspx6.js → roy-agent-core-qk9hd89p.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-6cy43nss.js → roy-agent-core-rykf7c04.js} +49 -9
- package/dist/shared/@ai-setting/{roy-agent-core-8ez6mccb.js → roy-agent-core-tk98f92b.js} +4 -0
- package/dist/shared/@ai-setting/{roy-agent-core-bxkjk7zx.js → roy-agent-core-vx8zc847.js} +4 -1
- package/dist/shared/@ai-setting/roy-agent-core-vyr6svt6.js +31 -0
- package/dist/shared/@ai-setting/{roy-agent-core-3gpygqcx.js → roy-agent-core-xf6z96qy.js} +2086 -163
- package/package.json +10 -1
- /package/dist/shared/@ai-setting/{roy-agent-core-b4x6x4k2.js → roy-agent-core-m1fgtnrc.js} +0 -0
- /package/dist/shared/@ai-setting/{roy-agent-core-v85n42ve.js → roy-agent-core-ze5cqpf2.js} +0 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getEnvContextOrEmpty,
|
|
3
|
+
init_env_context
|
|
4
|
+
} from "./roy-agent-core-7cbpmk4q.js";
|
|
5
|
+
|
|
6
|
+
// src/env/event-source/wecom-respond-registry.ts
|
|
7
|
+
init_env_context();
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
// src/env/event-source/wecom-cli-sender.ts
|
|
11
|
+
import { spawn } from "child_process";
|
|
12
|
+
var WECOM_CLI_SEND_TIMEOUT_MS = 15000;
|
|
13
|
+
var WECOM_CLI_RESOLVE_TIMEOUT_MS = 15000;
|
|
14
|
+
function resolveWecomCliBinary() {
|
|
15
|
+
const fromEnv = process.env.WECOM_CLI_BIN;
|
|
16
|
+
if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
|
|
17
|
+
return fromEnv.trim();
|
|
18
|
+
}
|
|
19
|
+
return "wecom-cli";
|
|
20
|
+
}
|
|
21
|
+
function buildWecomCliSendArgs(input) {
|
|
22
|
+
return [
|
|
23
|
+
"message",
|
|
24
|
+
"aibot",
|
|
25
|
+
"send",
|
|
26
|
+
"--chat-id",
|
|
27
|
+
input.chatId,
|
|
28
|
+
"--msg-type",
|
|
29
|
+
"markdown",
|
|
30
|
+
"--markdown",
|
|
31
|
+
JSON.stringify({ content: input.content })
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
function buildWecomCliResolveArgs(keywords) {
|
|
35
|
+
return ["contact", "users", "search", "--set", `keywords=${keywords}`];
|
|
36
|
+
}
|
|
37
|
+
function parseWecomCliResult(stdout) {
|
|
38
|
+
const text = (stdout ?? "").trim();
|
|
39
|
+
if (!text)
|
|
40
|
+
return { ok: false, error: "empty stdout" };
|
|
41
|
+
const start = text.indexOf("{");
|
|
42
|
+
const jsonText = start >= 0 ? text.slice(start) : text;
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(jsonText);
|
|
46
|
+
} catch {
|
|
47
|
+
return { ok: false, error: `non-JSON stdout: ${text.slice(0, 200)}` };
|
|
48
|
+
}
|
|
49
|
+
const obj = parsed;
|
|
50
|
+
if (obj?.error) {
|
|
51
|
+
const code = obj.error.code ?? "?";
|
|
52
|
+
const message = obj.error.message ?? obj.error.type ?? "unknown error";
|
|
53
|
+
return { ok: false, error: `[code=${code}] ${message}` };
|
|
54
|
+
}
|
|
55
|
+
if (obj?.success === true)
|
|
56
|
+
return { ok: true };
|
|
57
|
+
return { ok: true };
|
|
58
|
+
}
|
|
59
|
+
function parseWecomCliUserSearch(stdout, fallback) {
|
|
60
|
+
const text = (stdout ?? "").trim();
|
|
61
|
+
const start = text.indexOf("{");
|
|
62
|
+
if (start < 0)
|
|
63
|
+
return fallback;
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(text.slice(start));
|
|
66
|
+
const userid = parsed?.users?.[0]?.userid;
|
|
67
|
+
return typeof userid === "string" && userid.length > 0 ? userid : fallback;
|
|
68
|
+
} catch {
|
|
69
|
+
return fallback;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function runCli(args, timeoutMs) {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
let settled = false;
|
|
75
|
+
let stdout = "";
|
|
76
|
+
let stderr = "";
|
|
77
|
+
const finish = (code) => {
|
|
78
|
+
if (settled)
|
|
79
|
+
return;
|
|
80
|
+
settled = true;
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
resolve({ stdout, stderr, code });
|
|
83
|
+
};
|
|
84
|
+
let proc;
|
|
85
|
+
try {
|
|
86
|
+
proc = spawn(resolveWecomCliBinary(), args, {
|
|
87
|
+
shell: false,
|
|
88
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
89
|
+
});
|
|
90
|
+
} catch (err) {
|
|
91
|
+
resolve({
|
|
92
|
+
stdout: "",
|
|
93
|
+
stderr: err instanceof Error ? err.message : String(err),
|
|
94
|
+
code: null
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const timer = setTimeout(() => {
|
|
99
|
+
try {
|
|
100
|
+
proc.kill("SIGKILL");
|
|
101
|
+
} catch {}
|
|
102
|
+
finish(null);
|
|
103
|
+
}, timeoutMs);
|
|
104
|
+
timer.unref?.();
|
|
105
|
+
proc.stdout?.on("data", (d) => {
|
|
106
|
+
stdout += d.toString();
|
|
107
|
+
});
|
|
108
|
+
proc.stderr?.on("data", (d) => {
|
|
109
|
+
stderr += d.toString();
|
|
110
|
+
});
|
|
111
|
+
proc.on("error", (err) => {
|
|
112
|
+
stderr += err.message;
|
|
113
|
+
finish(null);
|
|
114
|
+
});
|
|
115
|
+
proc.on("close", (code) => finish(code));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
var cliChatIdCache = new Map;
|
|
119
|
+
async function resolveWecomCliChatId(userId) {
|
|
120
|
+
const key = userId.trim();
|
|
121
|
+
if (!key)
|
|
122
|
+
return key;
|
|
123
|
+
const cached = cliChatIdCache.get(key);
|
|
124
|
+
if (cached)
|
|
125
|
+
return cached;
|
|
126
|
+
const { stdout } = await runCli(buildWecomCliResolveArgs(key), WECOM_CLI_RESOLVE_TIMEOUT_MS);
|
|
127
|
+
const resolved = parseWecomCliUserSearch(stdout, key);
|
|
128
|
+
if (resolved !== key)
|
|
129
|
+
cliChatIdCache.set(key, resolved);
|
|
130
|
+
return resolved;
|
|
131
|
+
}
|
|
132
|
+
async function sendWecomCliMarkdown(input) {
|
|
133
|
+
const content = input.content ?? "";
|
|
134
|
+
if (!input.chatId || content.trim().length === 0) {
|
|
135
|
+
return { ok: false, error: "empty chatId or content" };
|
|
136
|
+
}
|
|
137
|
+
const { stdout, stderr, code } = await runCli(buildWecomCliSendArgs({ chatId: input.chatId, content }), input.timeoutMs ?? WECOM_CLI_SEND_TIMEOUT_MS);
|
|
138
|
+
const parsed = parseWecomCliResult(stdout);
|
|
139
|
+
if (parsed.ok)
|
|
140
|
+
return parsed;
|
|
141
|
+
const detail = stderr.trim() ? `${parsed.error} | stderr: ${stderr.trim().slice(0, 200)}` : parsed.error;
|
|
142
|
+
return { ok: false, error: `${detail} (exit=${code ?? "null"})` };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/env/event-source/wecom-card-builder.ts
|
|
146
|
+
var WecomCardActionKey = {
|
|
147
|
+
VIEW: "task_view",
|
|
148
|
+
MARK_DONE: "task_mark_done",
|
|
149
|
+
REOPEN: "task_reopen"
|
|
150
|
+
};
|
|
151
|
+
function wecomCardTaskIdForTask(taskId) {
|
|
152
|
+
return `roy-task-${taskId}`;
|
|
153
|
+
}
|
|
154
|
+
var cardSeq = 0;
|
|
155
|
+
function newWecomCardTaskId(taskId) {
|
|
156
|
+
cardSeq = (cardSeq + 1) % 1e6;
|
|
157
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
158
|
+
return `${wecomCardTaskIdForTask(taskId)}-${cardSeq}-${rand}`;
|
|
159
|
+
}
|
|
160
|
+
function formatTs(iso) {
|
|
161
|
+
try {
|
|
162
|
+
const d = new Date(iso);
|
|
163
|
+
if (isNaN(d.getTime()))
|
|
164
|
+
return iso;
|
|
165
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
166
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` + `${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
167
|
+
} catch {
|
|
168
|
+
return iso;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function clip(text, max) {
|
|
172
|
+
if (!text)
|
|
173
|
+
return "";
|
|
174
|
+
const t = text.trim();
|
|
175
|
+
return t.length > max ? `${t.slice(0, max)}…` : t;
|
|
176
|
+
}
|
|
177
|
+
function shortMilestoneValue(text) {
|
|
178
|
+
if (!text)
|
|
179
|
+
return "";
|
|
180
|
+
const t = text.trim();
|
|
181
|
+
const colonIdx = t.indexOf(":");
|
|
182
|
+
const head = colonIdx > 0 && colonIdx <= 20 ? t.slice(0, colonIdx).trim() : t;
|
|
183
|
+
if (head.length <= MAX_HORIZONTAL_VALUE)
|
|
184
|
+
return head;
|
|
185
|
+
return clipWord(head, MAX_HORIZONTAL_VALUE);
|
|
186
|
+
}
|
|
187
|
+
function clipWord(text, max) {
|
|
188
|
+
if (!text)
|
|
189
|
+
return "";
|
|
190
|
+
const t = text.trim();
|
|
191
|
+
if (t.length <= max)
|
|
192
|
+
return t;
|
|
193
|
+
let cut = t.slice(0, max - 1);
|
|
194
|
+
const lastSpace = cut.lastIndexOf(" ");
|
|
195
|
+
if (lastSpace > max * 0.4)
|
|
196
|
+
return `${cut.slice(0, lastSpace).trimEnd()}…`;
|
|
197
|
+
const cjkBoundary = findCjkAsciiBoundary(cut);
|
|
198
|
+
if (cjkBoundary > max * 0.4)
|
|
199
|
+
return `${cut.slice(0, cjkBoundary).trimEnd()}…`;
|
|
200
|
+
return cut.slice(0, max - 1);
|
|
201
|
+
}
|
|
202
|
+
function findCjkAsciiBoundary(text) {
|
|
203
|
+
const isCjk = (ch) => /[一-鿿-ヿ가-]/.test(ch);
|
|
204
|
+
const isAsciiAlnum = (ch) => /[A-Za-z0-9]/.test(ch);
|
|
205
|
+
for (let i = text.length - 1;i > 0; i--) {
|
|
206
|
+
const prev = text[i - 1];
|
|
207
|
+
const cur = text[i];
|
|
208
|
+
if (isCjk(prev) !== isAsciiAlnum(cur) && isCjk(cur) !== isAsciiAlnum(prev)) {
|
|
209
|
+
if (isCjk(prev) && isAsciiAlnum(cur) || isAsciiAlnum(prev) && isCjk(cur)) {
|
|
210
|
+
return i;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return -1;
|
|
215
|
+
}
|
|
216
|
+
var MAX_TIMELINE_LINES = 8;
|
|
217
|
+
var MAX_HORIZONTAL_ROWS = 6;
|
|
218
|
+
var MAX_HORIZONTAL_VALUE = 26;
|
|
219
|
+
function buildWecomTextNoticeCard(input) {
|
|
220
|
+
const progressPct = `${Math.max(0, Math.min(100, Math.round(input.progress ?? 0)))}%`;
|
|
221
|
+
const sorted = [...input.milestones].slice(-MAX_TIMELINE_LINES).reverse();
|
|
222
|
+
const timelineRows = sorted.slice(0, MAX_HORIZONTAL_ROWS - 1).map((m) => ({
|
|
223
|
+
keyname: formatTs(m.timestamp).slice(11),
|
|
224
|
+
value: shortMilestoneValue(m.title)
|
|
225
|
+
}));
|
|
226
|
+
const horizontal = [
|
|
227
|
+
{ keyname: "状态", value: clip(input.statusLabel, MAX_HORIZONTAL_VALUE) },
|
|
228
|
+
...timelineRows
|
|
229
|
+
];
|
|
230
|
+
const latest = sorted[0];
|
|
231
|
+
const quoteLine = latest ? `${formatTs(latest.timestamp)} — ${clipWord(latest.title, 80)}${latest.description ? `:${clipWord(latest.description, 80)}` : ""}` : "";
|
|
232
|
+
const latestSummary = latest && latest.title ? `${formatTs(latest.timestamp)} ${clip(latest.title, 140)}` : undefined;
|
|
233
|
+
const subTitle = input.terminal ? `任务${input.statusLabel.replace(/^[^\u4e00-\u9fa5A-Za-z]*/, "")} · 详见会话` : latestSummary;
|
|
234
|
+
return {
|
|
235
|
+
card_type: "text_notice",
|
|
236
|
+
main_title: {
|
|
237
|
+
title: `\uD83D\uDCCB Task #${input.taskId} · ${clip(input.title, 60)}`,
|
|
238
|
+
desc: clip(input.description, 80) || undefined
|
|
239
|
+
},
|
|
240
|
+
emphasis_content: {
|
|
241
|
+
title: progressPct,
|
|
242
|
+
desc: "进度"
|
|
243
|
+
},
|
|
244
|
+
sub_title_text: subTitle,
|
|
245
|
+
horizontal_content_list: horizontal,
|
|
246
|
+
quote_area: {
|
|
247
|
+
type: 0,
|
|
248
|
+
title: "最新进展",
|
|
249
|
+
quote_text: quoteLine || "(暂无操作记录)"
|
|
250
|
+
},
|
|
251
|
+
card_action: { type: 0 }
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
var MAX_BUTTONS = 6;
|
|
255
|
+
var MAX_BUTTON_TEXT = 10;
|
|
256
|
+
function buildWecomButtonInteractionCard(input) {
|
|
257
|
+
const notice = buildWecomTextNoticeCard({
|
|
258
|
+
taskId: input.taskId,
|
|
259
|
+
title: input.title,
|
|
260
|
+
statusLabel: input.statusLabel,
|
|
261
|
+
progress: input.progress,
|
|
262
|
+
description: input.description,
|
|
263
|
+
changeSummary: input.changeSummary,
|
|
264
|
+
milestones: input.milestones ?? []
|
|
265
|
+
});
|
|
266
|
+
const buttons = (input.buttons ?? []).slice(0, MAX_BUTTONS).map((b) => ({
|
|
267
|
+
text: clip(b.text, MAX_BUTTON_TEXT),
|
|
268
|
+
style: b.style ?? 1,
|
|
269
|
+
key: b.key
|
|
270
|
+
}));
|
|
271
|
+
return {
|
|
272
|
+
card_type: "button_interaction",
|
|
273
|
+
main_title: notice.main_title,
|
|
274
|
+
sub_title_text: notice.sub_title_text,
|
|
275
|
+
horizontal_content_list: notice.horizontal_content_list,
|
|
276
|
+
quote_area: notice.quote_area,
|
|
277
|
+
button_list: buttons,
|
|
278
|
+
card_action: { type: 0 },
|
|
279
|
+
task_id: input.cardTaskId ?? newWecomCardTaskId(input.taskId)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function buildWecomSendCardFrame(input) {
|
|
283
|
+
const reqId = input.reqId && input.reqId.trim() ? input.reqId.trim() : `aibot_send_msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
284
|
+
return {
|
|
285
|
+
cmd: "aibot_send_msg",
|
|
286
|
+
headers: { req_id: reqId },
|
|
287
|
+
body: {
|
|
288
|
+
chatid: input.chatId,
|
|
289
|
+
chat_type: input.chatType === "group" ? 2 : 1,
|
|
290
|
+
msgtype: "template_card",
|
|
291
|
+
template_card: input.card
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
var MAX_UPDATE_TITLE = 60;
|
|
296
|
+
function buildUpdateMainTitle(statusLabel, taskNumber, taskTitle) {
|
|
297
|
+
const segments = [];
|
|
298
|
+
if (taskNumber !== undefined && Number.isFinite(taskNumber)) {
|
|
299
|
+
segments.push(`Task #${taskNumber}`);
|
|
300
|
+
}
|
|
301
|
+
if (taskTitle) {
|
|
302
|
+
segments.push(clip(taskTitle, MAX_UPDATE_TITLE));
|
|
303
|
+
}
|
|
304
|
+
segments.push(statusLabel);
|
|
305
|
+
return segments.length > 1 ? `\uD83D\uDCCB ${segments.join(" · ")}` : `\uD83D\uDCCB ${statusLabel}`;
|
|
306
|
+
}
|
|
307
|
+
function buildWecomCardUpdateFrame(input) {
|
|
308
|
+
const reqId = (input.reqId ?? "").trim();
|
|
309
|
+
if (!reqId) {
|
|
310
|
+
throw new Error("buildWecomCardUpdateFrame: req_id is required " + "(update must reference the template_card_event frame)");
|
|
311
|
+
}
|
|
312
|
+
const buttons = (input.buttonOverrides ?? []).slice(0, MAX_BUTTONS).map((b) => ({
|
|
313
|
+
text: clip(b.text, MAX_BUTTON_TEXT),
|
|
314
|
+
style: b.style ?? 1,
|
|
315
|
+
key: b.key
|
|
316
|
+
}));
|
|
317
|
+
const sorted = [...input.timeline ?? []].reverse();
|
|
318
|
+
const timelineRows = sorted.slice(0, MAX_HORIZONTAL_ROWS).map((m) => ({
|
|
319
|
+
keyname: formatTs(m.timestamp).slice(11),
|
|
320
|
+
value: shortMilestoneValue(m.title)
|
|
321
|
+
}));
|
|
322
|
+
const total = sorted.length;
|
|
323
|
+
const subTitle = total > 0 ? total > MAX_HORIZONTAL_ROWS ? `共 ${total} 条操作记录,显示最近 ${MAX_HORIZONTAL_ROWS} 条(最新在顶部)` : `共 ${total} 条操作记录(最新在顶部)` : undefined;
|
|
324
|
+
return {
|
|
325
|
+
cmd: "aibot_respond_update_msg",
|
|
326
|
+
headers: { req_id: reqId },
|
|
327
|
+
body: {
|
|
328
|
+
response_type: "update_template_card",
|
|
329
|
+
template_card: {
|
|
330
|
+
card_type: "button_interaction",
|
|
331
|
+
main_title: {
|
|
332
|
+
title: buildUpdateMainTitle(input.statusLabel, input.taskNumber, input.taskTitle),
|
|
333
|
+
desc: input.note ? clip(input.note, 80) : undefined
|
|
334
|
+
},
|
|
335
|
+
sub_title_text: subTitle,
|
|
336
|
+
horizontal_content_list: timelineRows,
|
|
337
|
+
button_list: buttons,
|
|
338
|
+
task_id: String(input.taskId)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/env/event-source/wecom-respond-registry.ts
|
|
345
|
+
var registry = new Map;
|
|
346
|
+
function registerWecomRespondTarget(sourceId, target) {
|
|
347
|
+
registry.set(sourceId, target);
|
|
348
|
+
}
|
|
349
|
+
function unregisterWecomRespondTarget(sourceId) {
|
|
350
|
+
registry.delete(sourceId);
|
|
351
|
+
}
|
|
352
|
+
function getWecomRespondTarget(sourceId) {
|
|
353
|
+
return registry.get(sourceId);
|
|
354
|
+
}
|
|
355
|
+
function __resetWecomRespondRegistryForTest() {
|
|
356
|
+
registry.clear();
|
|
357
|
+
}
|
|
358
|
+
async function sendWecomReply(input) {
|
|
359
|
+
const content = input.content ?? "";
|
|
360
|
+
if (content.trim().length === 0) {
|
|
361
|
+
return { ok: false, error: "empty content" };
|
|
362
|
+
}
|
|
363
|
+
const errors = [];
|
|
364
|
+
if (input.reqId) {
|
|
365
|
+
const target = getWecomRespondTarget(input.sourceId);
|
|
366
|
+
if (target) {
|
|
367
|
+
const wsErr = target.respond(input.reqId, {
|
|
368
|
+
msgtype: "markdown",
|
|
369
|
+
markdown: { content }
|
|
370
|
+
});
|
|
371
|
+
if (!wsErr)
|
|
372
|
+
return { ok: true, channel: "ws" };
|
|
373
|
+
errors.push(`ws: ${wsErr.message}`);
|
|
374
|
+
} else {
|
|
375
|
+
errors.push(`ws: no instance registered for sourceId=${input.sourceId}`);
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
errors.push("ws: no req_id");
|
|
379
|
+
}
|
|
380
|
+
if (input.rawChatId) {
|
|
381
|
+
try {
|
|
382
|
+
const chatId = await resolveWecomCliChatId(input.rawChatId);
|
|
383
|
+
const cliResult = await sendWecomCliMarkdown({ chatId, content });
|
|
384
|
+
if (cliResult.ok)
|
|
385
|
+
return { ok: true, channel: "cli" };
|
|
386
|
+
errors.push(`cli: ${cliResult.error}`);
|
|
387
|
+
} catch (err) {
|
|
388
|
+
errors.push(`cli: ${err instanceof Error ? err.message : String(err)}`);
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
errors.push("cli: no chatId");
|
|
392
|
+
}
|
|
393
|
+
return { ok: false, error: errors.join(" | ") };
|
|
394
|
+
}
|
|
395
|
+
async function sendWecomTemplateCard(input) {
|
|
396
|
+
if (!input.chatId) {
|
|
397
|
+
return { ok: false, error: "no chatId" };
|
|
398
|
+
}
|
|
399
|
+
const target = getWecomRespondTarget(input.sourceId);
|
|
400
|
+
if (!target) {
|
|
401
|
+
return {
|
|
402
|
+
ok: false,
|
|
403
|
+
error: `no instance registered for sourceId=${input.sourceId}`
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
if (typeof target.sendCmd !== "function") {
|
|
407
|
+
return {
|
|
408
|
+
ok: false,
|
|
409
|
+
error: `registered instance has no sendCmd (old bridge?)`
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
const frame = buildWecomSendCardFrame({
|
|
413
|
+
chatId: input.chatId,
|
|
414
|
+
chatType: input.chatType,
|
|
415
|
+
card: input.card
|
|
416
|
+
});
|
|
417
|
+
try {
|
|
418
|
+
const err = target.sendCmd(frame.cmd, frame.body);
|
|
419
|
+
if (err)
|
|
420
|
+
return { ok: false, error: err.message };
|
|
421
|
+
return { ok: true, channel: "ws" };
|
|
422
|
+
} catch (err) {
|
|
423
|
+
return {
|
|
424
|
+
ok: false,
|
|
425
|
+
error: err instanceof Error ? err.message : String(err)
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
var wecomRespondTool = {
|
|
430
|
+
name: "wecom_respond",
|
|
431
|
+
description: "Reply to the WeCom (企业微信) user. Primary path is the official wecom-cli " + "(message aibot send, markdown); WS long-connection frame is used as fallback. " + "This is the ONLY outbound channel for WeCom messages.",
|
|
432
|
+
parameters: z.object({
|
|
433
|
+
content: z.string().describe("Reply content (markdown supported)"),
|
|
434
|
+
chat_id: z.string().optional().describe("Target chat id (single chat = peer userid, group = chatid). " + "Defaults to the current event's chatId/senderId."),
|
|
435
|
+
req_id: z.string().optional().describe("Original message frame req_id (optional; used for WS fallback only). " + "Defaults to the current event metadata.reqId.")
|
|
436
|
+
}),
|
|
437
|
+
metadata: {
|
|
438
|
+
category: "messaging",
|
|
439
|
+
tags: ["wecom", "reply", "im"],
|
|
440
|
+
version: "2.0.0"
|
|
441
|
+
},
|
|
442
|
+
execute: async (args, _ctx) => {
|
|
443
|
+
const { content } = args;
|
|
444
|
+
let sourceId;
|
|
445
|
+
let eventChatId;
|
|
446
|
+
let eventReqId;
|
|
447
|
+
try {
|
|
448
|
+
const ctx = getEnvContextOrEmpty();
|
|
449
|
+
sourceId = typeof ctx.sourceId === "string" ? ctx.sourceId : undefined;
|
|
450
|
+
const meta = ctx.envEvent?.metadata;
|
|
451
|
+
const cid = meta?.chatId;
|
|
452
|
+
if (typeof cid === "string" && cid.length > 0)
|
|
453
|
+
eventChatId = cid;
|
|
454
|
+
const rid = meta?.reqId;
|
|
455
|
+
if (typeof rid === "string" && rid.length > 0)
|
|
456
|
+
eventReqId = rid;
|
|
457
|
+
} catch {}
|
|
458
|
+
const chatId = args.chat_id?.trim() || eventChatId;
|
|
459
|
+
const reqId = args.req_id?.trim() || eventReqId;
|
|
460
|
+
if (!chatId) {
|
|
461
|
+
return {
|
|
462
|
+
success: false,
|
|
463
|
+
output: "",
|
|
464
|
+
error: "wecom_respond: cannot resolve chat_id (no explicit chat_id and no EnvContext event chatId)."
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
if (!sourceId) {
|
|
468
|
+
sourceId = "unknown";
|
|
469
|
+
}
|
|
470
|
+
const startedAt = Date.now();
|
|
471
|
+
const result = await sendWecomReply({
|
|
472
|
+
sourceId,
|
|
473
|
+
rawChatId: chatId,
|
|
474
|
+
reqId,
|
|
475
|
+
content
|
|
476
|
+
});
|
|
477
|
+
if (!result.ok) {
|
|
478
|
+
return {
|
|
479
|
+
success: false,
|
|
480
|
+
output: "",
|
|
481
|
+
error: `wecom_respond: send failed: ${result.error ?? "unknown"}`
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
markWecomReplySent();
|
|
485
|
+
return {
|
|
486
|
+
success: true,
|
|
487
|
+
output: `Reply sent to WeCom via ${result.channel} ` + `(chat_id=${chatId}, ${content.length} chars)`,
|
|
488
|
+
metadata: {
|
|
489
|
+
execution_time_ms: Date.now() - startedAt,
|
|
490
|
+
channel: result.channel,
|
|
491
|
+
chat_id: chatId
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
var REPLY_SENT_KEY = "__wecomReplySent";
|
|
497
|
+
function replySentBag(explicit) {
|
|
498
|
+
if (explicit)
|
|
499
|
+
return explicit;
|
|
500
|
+
try {
|
|
501
|
+
const ctx = getEnvContextOrEmpty();
|
|
502
|
+
return ctx.agentContext ??= {};
|
|
503
|
+
} catch {
|
|
504
|
+
return {};
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function markWecomReplySent(explicitBag) {
|
|
508
|
+
replySentBag(explicitBag)[REPLY_SENT_KEY] = true;
|
|
509
|
+
}
|
|
510
|
+
function wasWecomReplySent(explicitBag) {
|
|
511
|
+
return replySentBag(explicitBag)[REPLY_SENT_KEY] === true;
|
|
512
|
+
}
|
|
513
|
+
function __resetWecomReplySentForTest(explicitBag) {
|
|
514
|
+
delete replySentBag(explicitBag)[REPLY_SENT_KEY];
|
|
515
|
+
}
|
|
516
|
+
var WECOM_EVENT_TYPE = "event-source.event.wecom-cli";
|
|
517
|
+
async function autoReplyWecomTurn(envEvent, finalText) {
|
|
518
|
+
const evt = envEvent;
|
|
519
|
+
if (evt?.type !== WECOM_EVENT_TYPE) {
|
|
520
|
+
return { skipped: true, reason: "not a wecom event" };
|
|
521
|
+
}
|
|
522
|
+
const text = (finalText ?? "").trim();
|
|
523
|
+
if (!text)
|
|
524
|
+
return { skipped: true, reason: "empty final text" };
|
|
525
|
+
if (wasWecomReplySent()) {
|
|
526
|
+
return { skipped: true, reason: "already replied via tool" };
|
|
527
|
+
}
|
|
528
|
+
const meta = evt.metadata ?? {};
|
|
529
|
+
const chatId = typeof meta.chatId === "string" && meta.chatId || typeof meta.senderId === "string" && meta.senderId || "";
|
|
530
|
+
const sourceId = typeof meta.sourceId === "string" && meta.sourceId || "unknown";
|
|
531
|
+
const reqId = typeof meta.reqId === "string" ? meta.reqId : undefined;
|
|
532
|
+
if (!chatId) {
|
|
533
|
+
return { skipped: true, reason: "no chatId in event metadata" };
|
|
534
|
+
}
|
|
535
|
+
const r = await sendWecomReply({
|
|
536
|
+
sourceId,
|
|
537
|
+
rawChatId: chatId,
|
|
538
|
+
reqId,
|
|
539
|
+
content: text
|
|
540
|
+
});
|
|
541
|
+
return { skipped: false, ok: r.ok, channel: r.channel, error: r.error };
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export { WecomCardActionKey, buildWecomTextNoticeCard, buildWecomButtonInteractionCard, buildWecomCardUpdateFrame, registerWecomRespondTarget, unregisterWecomRespondTarget, getWecomRespondTarget, __resetWecomRespondRegistryForTest, sendWecomReply, sendWecomTemplateCard, wecomRespondTool, markWecomReplySent, wasWecomReplySent, __resetWecomReplySentForTest, autoReplyWecomTurn };
|
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
createLogger,
|
|
9
9
|
init_logger
|
|
10
10
|
} from "./roy-agent-core-7f7t9wwp.js";
|
|
11
|
+
import {
|
|
12
|
+
__require
|
|
13
|
+
} from "./roy-agent-core-fs0mn2jk.js";
|
|
11
14
|
|
|
12
15
|
// src/env/error-reply.ts
|
|
13
16
|
init_logger();
|
|
@@ -361,9 +364,10 @@ function isUsableChannel(ch) {
|
|
|
361
364
|
return false;
|
|
362
365
|
if (!ch.chatId && !ch.messageId)
|
|
363
366
|
return false;
|
|
364
|
-
if (
|
|
365
|
-
return
|
|
366
|
-
|
|
367
|
+
if (!ch.type)
|
|
368
|
+
return true;
|
|
369
|
+
const t = ch.type.toLowerCase();
|
|
370
|
+
return t.includes("lark") || t.startsWith("wecom");
|
|
367
371
|
}
|
|
368
372
|
var repliedTriggers = new Map;
|
|
369
373
|
var CHAT_DEDUP_WINDOW_MS = 5 * 60000;
|
|
@@ -501,6 +505,32 @@ function spawnLarkReply(options) {
|
|
|
501
505
|
proc.on("error", (err) => finish(err));
|
|
502
506
|
});
|
|
503
507
|
}
|
|
508
|
+
async function spawnWecomReply(options) {
|
|
509
|
+
const { chatId, content, params } = options;
|
|
510
|
+
if (!chatId) {
|
|
511
|
+
throw new Error("No chatId specified for wecom failure reply");
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
const { sendWecomReply } = await import("./roy-agent-core-vyr6svt6.js");
|
|
515
|
+
const result = await sendWecomReply({
|
|
516
|
+
sourceId: typeof params?.sourceId === "string" && params.sourceId || "unknown",
|
|
517
|
+
rawChatId: chatId,
|
|
518
|
+
reqId: typeof params?.reqId === "string" ? params.reqId : undefined,
|
|
519
|
+
content
|
|
520
|
+
});
|
|
521
|
+
if (!result.ok) {
|
|
522
|
+
throw new Error(result.error ?? "sendWecomReply failed");
|
|
523
|
+
}
|
|
524
|
+
} catch (err) {
|
|
525
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function pickSender(channel) {
|
|
529
|
+
const t = (channel.type ?? "").toLowerCase();
|
|
530
|
+
if (t.startsWith("wecom"))
|
|
531
|
+
return spawnWecomReply;
|
|
532
|
+
return spawnLarkReply;
|
|
533
|
+
}
|
|
504
534
|
async function notifyReactFailure(options) {
|
|
505
535
|
const info = classifyReactFailure(options.error);
|
|
506
536
|
if (!info.notify) {
|
|
@@ -510,7 +540,12 @@ async function notifyReactFailure(options) {
|
|
|
510
540
|
const channel = (isUsableChannel(options.replyChannel) ? options.replyChannel : undefined) ?? extractReplyChannelFromEvent(options.envEvent);
|
|
511
541
|
if (!isUsableChannel(channel)) {
|
|
512
542
|
const hasTrigger = Boolean(options.envEvent);
|
|
513
|
-
const
|
|
543
|
+
const hasChannelObj = !hasTrigger || (() => {
|
|
544
|
+
const e = options.envEvent;
|
|
545
|
+
const rc = e?.metadata?.replyChannel ?? e?.payload?.replyChannel ?? options.replyChannel;
|
|
546
|
+
return Boolean(rc && typeof rc === "object");
|
|
547
|
+
})();
|
|
548
|
+
const noChannelMsg = hasChannelObj ? `[#4418] React 中断(${info.kind}/${info.ruleId})replyChannel.type 不在支持列表(lark*/wecom*),` + `触发方收不到失败说明` + (hasTrigger ? ` —— channel 存在但 type=${String(options.envEvent?.metadata?.replyChannel?.type ?? options.envEvent?.payload?.replyChannel?.type ?? options.replyChannel?.type ?? "unknown")} 未被放行` : "") + (options.sessionId ? ` (session=${options.sessionId})` : "") : `[#4418] React 中断(${info.kind}/${info.ruleId})无法定位 replyChannel,` + `触发方收不到失败说明` + (hasTrigger ? " —— triggerEvent 存在但缺少 metadata.replyChannel" : "(本地执行,无 triggerEvent)") + (options.sessionId ? ` (session=${options.sessionId})` : "");
|
|
514
549
|
if (hasTrigger)
|
|
515
550
|
logger.warn(noChannelMsg);
|
|
516
551
|
else
|
|
@@ -523,21 +558,26 @@ async function notifyReactFailure(options) {
|
|
|
523
558
|
return { sent: false, kind: info.kind, info, skipped: "duplicate" };
|
|
524
559
|
}
|
|
525
560
|
rememberTrigger(dedupKey);
|
|
526
|
-
const
|
|
561
|
+
const isWecom = (channel.type ?? "").toLowerCase().startsWith("wecom");
|
|
562
|
+
const baseContent = buildFailureReplyMarkdown(info, { query: options.query });
|
|
563
|
+
const content = isWecom ? `⚠️ 系统失败通知
|
|
564
|
+
|
|
565
|
+
${baseContent}` : baseContent;
|
|
527
566
|
try {
|
|
528
|
-
await
|
|
567
|
+
await pickSender(channel)({
|
|
529
568
|
chatId: channel.chatId,
|
|
530
569
|
messageId: channel.messageId,
|
|
531
570
|
content,
|
|
532
571
|
profile: channel.profile,
|
|
533
|
-
timeoutMs: options.replyTimeoutMs
|
|
572
|
+
timeoutMs: options.replyTimeoutMs,
|
|
573
|
+
params: channel.params
|
|
534
574
|
});
|
|
535
|
-
logger.info(`[#4418] React 中断(${info.kind}/${info.ruleId}
|
|
575
|
+
logger.info(`[#4418] React 中断(${info.kind}/${info.ruleId})已回${isWecom ? "企微" : "飞书"} (${channel.messageId ?? channel.chatId})`);
|
|
536
576
|
return { sent: true, kind: info.kind, info };
|
|
537
577
|
} catch (err) {
|
|
538
578
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
539
579
|
repliedTriggers.delete(dedupKey);
|
|
540
|
-
logger.warn(`[#4418] React 中断(${info.kind}/${info.ruleId}
|
|
580
|
+
logger.warn(`[#4418] React 中断(${info.kind}/${info.ruleId})回${isWecom ? "企微" : "飞书"}失败(已忽略): ${errorMsg}`);
|
|
541
581
|
return { sent: false, kind: info.kind, info, error: errorMsg };
|
|
542
582
|
}
|
|
543
583
|
}
|
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
getCurrentProcessRegistry,
|
|
10
10
|
killProcessTree
|
|
11
11
|
} from "./roy-agent-core-fttgm52a.js";
|
|
12
|
+
import {
|
|
13
|
+
wecomRespondTool
|
|
14
|
+
} from "./roy-agent-core-m3vjepzz.js";
|
|
12
15
|
import {
|
|
13
16
|
init_decorator,
|
|
14
17
|
init_trace_filters,
|
|
@@ -940,6 +943,7 @@ function getAllBuiltInTools() {
|
|
|
940
943
|
editFileTool,
|
|
941
944
|
grepTool,
|
|
942
945
|
understandImageTool,
|
|
946
|
+
wecomRespondTool,
|
|
943
947
|
queryBackgroundProcessTool,
|
|
944
948
|
compactSessionTool
|
|
945
949
|
];
|
|
@@ -7,7 +7,8 @@ var BUILT_IN_EVENT_SOURCE_TYPES = {
|
|
|
7
7
|
LARK_CLI: "lark-cli",
|
|
8
8
|
TIMER: "timer",
|
|
9
9
|
BOUNTY_IM: "bounty-im",
|
|
10
|
-
CLI_COMMAND: "cli-command"
|
|
10
|
+
CLI_COMMAND: "cli-command",
|
|
11
|
+
WECOM_CLI: "wecom-cli"
|
|
11
12
|
};
|
|
12
13
|
var BUILT_IN_EVENT_SOURCE_TYPE_LIST = Object.values(BUILT_IN_EVENT_SOURCE_TYPES);
|
|
13
14
|
var defaultLarkCliRunner = {
|
|
@@ -60,6 +61,8 @@ function getDefaultConfigForType(type) {
|
|
|
60
61
|
switch (type) {
|
|
61
62
|
case BUILT_IN_EVENT_SOURCE_TYPES.LARK_CLI:
|
|
62
63
|
return { command: "lark-cli event consume im.message.receive_v1 --as bot" };
|
|
64
|
+
case BUILT_IN_EVENT_SOURCE_TYPES.WECOM_CLI:
|
|
65
|
+
return { command: "wecom-cli event consume" };
|
|
63
66
|
default:
|
|
64
67
|
return {};
|
|
65
68
|
}
|