@ai-setting/roy-agent-cli 1.7.57 → 1.7.59
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/bin/roy-agent.js +448 -9
- package/dist/index.js +448 -9
- package/dist/roy-agent-linux-x64/bin/roy-agent +2 -2
- package/package.json +1 -1
package/dist/bin/roy-agent.js
CHANGED
|
@@ -9639,7 +9639,7 @@ class CliQuietModeService {
|
|
|
9639
9639
|
// package.json
|
|
9640
9640
|
var package_default = {
|
|
9641
9641
|
name: "@ai-setting/roy-agent-cli",
|
|
9642
|
-
version: "1.7.
|
|
9642
|
+
version: "1.7.59",
|
|
9643
9643
|
type: "module",
|
|
9644
9644
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9645
9645
|
main: "./dist/index.js",
|
|
@@ -11785,6 +11785,321 @@ import {
|
|
|
11785
11785
|
} from "@ai-setting/roy-agent-core/env";
|
|
11786
11786
|
import { TracedAs as TracedAs5 } from "@ai-setting/roy-agent-core";
|
|
11787
11787
|
import { notifyReactFailure as notifyReactFailure2 } from "@ai-setting/roy-agent-core/env/error-reply";
|
|
11788
|
+
|
|
11789
|
+
// src/commands/shared/telemetry-notice.ts
|
|
11790
|
+
import { spawn } from "node:child_process";
|
|
11791
|
+
import { normalizeLarkAtMentions } from "@ai-setting/roy-agent-core/env/lark-mention-utils";
|
|
11792
|
+
import { deriveIdempotencyKey } from "@ai-setting/roy-agent-core/env/event-source/idempotency-key";
|
|
11793
|
+
var TELEMETRY_EVENT_TYPES = new Set([
|
|
11794
|
+
"task.background.started",
|
|
11795
|
+
"task.background.progress"
|
|
11796
|
+
]);
|
|
11797
|
+
function isTelemetryEvent(type) {
|
|
11798
|
+
if (!type)
|
|
11799
|
+
return false;
|
|
11800
|
+
return TELEMETRY_EVENT_TYPES.has(type);
|
|
11801
|
+
}
|
|
11802
|
+
var RULE_NOTICE_EVENT_TYPES = new Set([
|
|
11803
|
+
"task.background.completed",
|
|
11804
|
+
"task.background.timeout"
|
|
11805
|
+
]);
|
|
11806
|
+
function isRuleNoticeEvent(type) {
|
|
11807
|
+
if (!type)
|
|
11808
|
+
return false;
|
|
11809
|
+
return RULE_NOTICE_EVENT_TYPES.has(type);
|
|
11810
|
+
}
|
|
11811
|
+
var MAX_TITLE_CHARS = 40;
|
|
11812
|
+
function pad2(n) {
|
|
11813
|
+
return n < 10 ? `0${n}` : String(n);
|
|
11814
|
+
}
|
|
11815
|
+
function formatElapsed(ms) {
|
|
11816
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11817
|
+
return "0m";
|
|
11818
|
+
const totalMin = Math.floor(ms / 60000);
|
|
11819
|
+
if (totalMin < 60)
|
|
11820
|
+
return `${totalMin}m`;
|
|
11821
|
+
const totalHour = Math.floor(totalMin / 60);
|
|
11822
|
+
const min = totalMin % 60;
|
|
11823
|
+
if (totalHour < 24) {
|
|
11824
|
+
return min === 0 ? `${totalHour}h` : `${totalHour}h${min}m`;
|
|
11825
|
+
}
|
|
11826
|
+
const day = Math.floor(totalHour / 24);
|
|
11827
|
+
const hour = totalHour % 24;
|
|
11828
|
+
return hour === 0 ? `${day}d` : `${day}d${hour}h`;
|
|
11829
|
+
}
|
|
11830
|
+
function formatClock(ts) {
|
|
11831
|
+
const d = new Date(ts);
|
|
11832
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
11833
|
+
}
|
|
11834
|
+
function formatTimeoutLabel(ms) {
|
|
11835
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11836
|
+
return "0m";
|
|
11837
|
+
return formatElapsed(ms);
|
|
11838
|
+
}
|
|
11839
|
+
function truncateTitle(raw) {
|
|
11840
|
+
const s = raw.trim();
|
|
11841
|
+
if (s.length <= MAX_TITLE_CHARS)
|
|
11842
|
+
return s;
|
|
11843
|
+
return `${s.slice(0, MAX_TITLE_CHARS)}…`;
|
|
11844
|
+
}
|
|
11845
|
+
function pickString(v) {
|
|
11846
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11847
|
+
}
|
|
11848
|
+
function pickNumber(v) {
|
|
11849
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
11850
|
+
}
|
|
11851
|
+
function buildTelemetryNoticeText(type, payload, now = Date.now()) {
|
|
11852
|
+
const p = payload ?? {};
|
|
11853
|
+
const taskId = pickNumber(p.associatedTaskId);
|
|
11854
|
+
const bgId = pickString(p.backgroundTaskId);
|
|
11855
|
+
const desc = truncateTitle(pickString(p.description) ?? "后台任务");
|
|
11856
|
+
let label;
|
|
11857
|
+
if (taskId !== undefined)
|
|
11858
|
+
label = `Task #${taskId}`;
|
|
11859
|
+
else if (bgId)
|
|
11860
|
+
label = bgId;
|
|
11861
|
+
else
|
|
11862
|
+
label = "后台任务";
|
|
11863
|
+
const head = type === "task.background.started" ? `\uD83D\uDD14 ${label}「${desc}」已启动` : `\uD83D\uDD04 ${label}「${desc}」后台运行中`;
|
|
11864
|
+
const parts = [];
|
|
11865
|
+
const startedAt = pickNumber(p.startedAt);
|
|
11866
|
+
const timeoutMs = pickNumber(p.timeoutMs);
|
|
11867
|
+
if (type !== "task.background.started" && startedAt !== undefined) {
|
|
11868
|
+
parts.push(`已跑 ${formatElapsed(now - startedAt)}`);
|
|
11869
|
+
}
|
|
11870
|
+
if (startedAt !== undefined && timeoutMs !== undefined) {
|
|
11871
|
+
parts.push(`预计 ${formatClock(startedAt + timeoutMs)} 超时(timeout ${formatTimeoutLabel(timeoutMs)})`);
|
|
11872
|
+
}
|
|
11873
|
+
let text = head;
|
|
11874
|
+
if (parts.length > 0)
|
|
11875
|
+
text += `
|
|
11876
|
+
${parts.join(",")}`;
|
|
11877
|
+
const progressMessage = pickString(p.progressMessage);
|
|
11878
|
+
if (progressMessage)
|
|
11879
|
+
text += `
|
|
11880
|
+
${progressMessage}`;
|
|
11881
|
+
return text;
|
|
11882
|
+
}
|
|
11883
|
+
function parseTimeoutMsFromError(error) {
|
|
11884
|
+
if (!error)
|
|
11885
|
+
return;
|
|
11886
|
+
const m = error.match(/timeout after\s+(\d+)\s*ms/i);
|
|
11887
|
+
if (!m)
|
|
11888
|
+
return;
|
|
11889
|
+
const n = Number(m[1]);
|
|
11890
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
11891
|
+
}
|
|
11892
|
+
function buildTerminalNoticeText(type, payload) {
|
|
11893
|
+
const p = payload ?? {};
|
|
11894
|
+
const taskId = pickNumber(p.associatedTaskId);
|
|
11895
|
+
const bgId = pickString(p.backgroundTaskId);
|
|
11896
|
+
const desc = truncateTitle(pickString(p.description) ?? "后台任务");
|
|
11897
|
+
let label;
|
|
11898
|
+
if (taskId !== undefined)
|
|
11899
|
+
label = `Task #${taskId}`;
|
|
11900
|
+
else if (bgId)
|
|
11901
|
+
label = bgId;
|
|
11902
|
+
else
|
|
11903
|
+
label = "后台任务";
|
|
11904
|
+
const elapsedMs = pickNumber(p.executionTimeMs);
|
|
11905
|
+
const elapsed = elapsedMs !== undefined ? formatElapsed(elapsedMs) : undefined;
|
|
11906
|
+
if (type === "task.background.timeout") {
|
|
11907
|
+
const timeoutMs = pickNumber(p.timeoutMs) ?? parseTimeoutMsFromError(p.error);
|
|
11908
|
+
const parts = [];
|
|
11909
|
+
if (elapsed)
|
|
11910
|
+
parts.push(`跑了 ${elapsed}`);
|
|
11911
|
+
if (timeoutMs !== undefined)
|
|
11912
|
+
parts.push(`timeout ${formatTimeoutLabel(timeoutMs)}`);
|
|
11913
|
+
const head = `⏱️ ${label}「${desc}」已超时`;
|
|
11914
|
+
return parts.length > 0 ? `${head}(${parts.join(",")})
|
|
11915
|
+
助手随后补充结论与下一步选项。` : `${head}
|
|
11916
|
+
助手随后补充结论与下一步选项。`;
|
|
11917
|
+
}
|
|
11918
|
+
if (type === "task.background.completed") {
|
|
11919
|
+
const head = `✅ ${label}「${desc}」已完成`;
|
|
11920
|
+
return elapsed ? `${head}(耗时 ${elapsed})
|
|
11921
|
+
助手随后补充交付摘要。` : `${head}
|
|
11922
|
+
助手随后补充交付摘要。`;
|
|
11923
|
+
}
|
|
11924
|
+
const bgStatus = type.replace("task.background.", "");
|
|
11925
|
+
return `ℹ️ ${label}「${desc}」${bgStatus}${elapsed ? `(耗时 ${elapsed})` : ""}`;
|
|
11926
|
+
}
|
|
11927
|
+
var DEFAULT_SEND_TIMEOUT_MS = 5000;
|
|
11928
|
+
var MAX_IDEMPOTENCY_KEY_LEN = 50;
|
|
11929
|
+
function deriveTelemetryIdempotencyKey(eventId, chatId) {
|
|
11930
|
+
return deriveNoticeIdempotencyKey("telemetry", eventId, chatId);
|
|
11931
|
+
}
|
|
11932
|
+
function deriveRuleNoticeIdempotencyKey(eventId, chatId) {
|
|
11933
|
+
return deriveNoticeIdempotencyKey("rule", eventId, chatId);
|
|
11934
|
+
}
|
|
11935
|
+
function deriveNoticeIdempotencyKey(namespace, eventId, chatId) {
|
|
11936
|
+
const anchor = pickString(eventId) ?? `ts-${Date.now()}-${Math.random()}`;
|
|
11937
|
+
return deriveIdempotencyKey([namespace, anchor, chatId]);
|
|
11938
|
+
}
|
|
11939
|
+
function buildLarkSendArgs(input) {
|
|
11940
|
+
const { chatId, text, profile, idempotencyKey } = input;
|
|
11941
|
+
const args = [
|
|
11942
|
+
"im",
|
|
11943
|
+
"+messages-send",
|
|
11944
|
+
"--chat-id",
|
|
11945
|
+
chatId,
|
|
11946
|
+
"--markdown",
|
|
11947
|
+
normalizeLarkAtMentions(text),
|
|
11948
|
+
"--as",
|
|
11949
|
+
"bot"
|
|
11950
|
+
];
|
|
11951
|
+
if (idempotencyKey) {
|
|
11952
|
+
args.push("--idempotency-key", idempotencyKey.slice(0, MAX_IDEMPOTENCY_KEY_LEN));
|
|
11953
|
+
}
|
|
11954
|
+
if (profile)
|
|
11955
|
+
args.unshift("--profile", profile);
|
|
11956
|
+
return args;
|
|
11957
|
+
}
|
|
11958
|
+
function sendLarkChatMarkdown(options) {
|
|
11959
|
+
const { chatId, text, profile, idempotencyKey } = options;
|
|
11960
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
11961
|
+
if (!chatId) {
|
|
11962
|
+
return Promise.reject(new Error("sendLarkChatMarkdown: empty chatId"));
|
|
11963
|
+
}
|
|
11964
|
+
return new Promise((resolve, reject) => {
|
|
11965
|
+
let settled = false;
|
|
11966
|
+
let timer;
|
|
11967
|
+
const finish = (err) => {
|
|
11968
|
+
if (settled)
|
|
11969
|
+
return;
|
|
11970
|
+
settled = true;
|
|
11971
|
+
if (timer)
|
|
11972
|
+
clearTimeout(timer);
|
|
11973
|
+
if (err)
|
|
11974
|
+
reject(err);
|
|
11975
|
+
else
|
|
11976
|
+
resolve();
|
|
11977
|
+
};
|
|
11978
|
+
const args = buildLarkSendArgs({ chatId, text, profile, idempotencyKey });
|
|
11979
|
+
let proc;
|
|
11980
|
+
try {
|
|
11981
|
+
proc = spawn("lark-cli", args, {
|
|
11982
|
+
shell: false,
|
|
11983
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
11984
|
+
});
|
|
11985
|
+
} catch (err) {
|
|
11986
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
11987
|
+
return;
|
|
11988
|
+
}
|
|
11989
|
+
let stderr = "";
|
|
11990
|
+
proc.stderr?.on("data", (chunk) => {
|
|
11991
|
+
stderr += chunk.toString();
|
|
11992
|
+
});
|
|
11993
|
+
timer = setTimeout(() => {
|
|
11994
|
+
try {
|
|
11995
|
+
proc.kill("SIGKILL");
|
|
11996
|
+
} catch {}
|
|
11997
|
+
finish(new Error(`lark-cli timed out after ${timeoutMs}ms`));
|
|
11998
|
+
}, timeoutMs);
|
|
11999
|
+
timer.unref?.();
|
|
12000
|
+
proc.on("close", (code) => {
|
|
12001
|
+
if (code === 0)
|
|
12002
|
+
finish();
|
|
12003
|
+
else
|
|
12004
|
+
finish(new Error(`lark-cli exited with code ${code}: ${stderr}`));
|
|
12005
|
+
});
|
|
12006
|
+
proc.on("error", (err) => finish(err));
|
|
12007
|
+
});
|
|
12008
|
+
}
|
|
12009
|
+
function targetFromEventMetadata(metadata) {
|
|
12010
|
+
if (!metadata)
|
|
12011
|
+
return;
|
|
12012
|
+
const replyChannel = metadata.replyChannel;
|
|
12013
|
+
const chatId = pickString(replyChannel?.chatId) ?? pickString(metadata.chatId);
|
|
12014
|
+
if (!chatId)
|
|
12015
|
+
return;
|
|
12016
|
+
const profile = pickString(replyChannel?.profile) ?? pickString(metadata.profile);
|
|
12017
|
+
return profile ? { chatId, profile } : { chatId };
|
|
12018
|
+
}
|
|
12019
|
+
async function targetFromTriggerEvent(eventStore, triggerEventId) {
|
|
12020
|
+
if (!eventStore || !triggerEventId)
|
|
12021
|
+
return;
|
|
12022
|
+
let persisted;
|
|
12023
|
+
try {
|
|
12024
|
+
persisted = await eventStore.getEventById(triggerEventId);
|
|
12025
|
+
} catch {
|
|
12026
|
+
return;
|
|
12027
|
+
}
|
|
12028
|
+
if (!persisted || typeof persisted !== "object")
|
|
12029
|
+
return;
|
|
12030
|
+
const metadata = persisted.metadata;
|
|
12031
|
+
if (!metadata || typeof metadata !== "object")
|
|
12032
|
+
return;
|
|
12033
|
+
return targetFromEventMetadata(metadata);
|
|
12034
|
+
}
|
|
12035
|
+
|
|
12036
|
+
// src/commands/shared/self-echo-filter.ts
|
|
12037
|
+
var BOT_SENDER_TYPES = new Set(["bot", "app"]);
|
|
12038
|
+
function pickString2(v) {
|
|
12039
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
12040
|
+
}
|
|
12041
|
+
function extractSenderIdentity(event) {
|
|
12042
|
+
if (!event)
|
|
12043
|
+
return {};
|
|
12044
|
+
const metadata = event.metadata;
|
|
12045
|
+
const payload = event.payload;
|
|
12046
|
+
const payloadMeta = payload?.metadata;
|
|
12047
|
+
const rawEvent = payload?.rawEvent;
|
|
12048
|
+
const innerEvent = rawEvent?.event;
|
|
12049
|
+
const innerSender = innerEvent?.sender;
|
|
12050
|
+
const topSender = rawEvent?.sender;
|
|
12051
|
+
const senderType = pickString2(metadata?.senderType) ?? pickString2(payloadMeta?.senderType) ?? pickString2(rawEvent?.sender_type) ?? pickString2(innerSender?.sender_type) ?? pickString2(topSender?.sender_type) ?? pickString2(payload?.senderType);
|
|
12052
|
+
const nestedSenderId = innerSender?.sender_id ?? topSender?.sender_id;
|
|
12053
|
+
const senderId = pickString2(metadata?.senderId) ?? pickString2(payloadMeta?.senderId) ?? pickString2(payload?.senderId) ?? pickString2(rawEvent?.sender_id) ?? (typeof nestedSenderId === "string" ? pickString2(nestedSenderId) : pickString2(nestedSenderId?.open_id) ?? pickString2(nestedSenderId?.user_id) ?? pickString2(nestedSenderId?.union_id));
|
|
12054
|
+
return {
|
|
12055
|
+
...senderType ? { senderType } : {},
|
|
12056
|
+
...senderId ? { senderId } : {}
|
|
12057
|
+
};
|
|
12058
|
+
}
|
|
12059
|
+
function isSelfEchoEvent(event, options = {}) {
|
|
12060
|
+
const { senderType, senderId } = extractSenderIdentity(event);
|
|
12061
|
+
if (options.enabled === false) {
|
|
12062
|
+
return {
|
|
12063
|
+
skip: false,
|
|
12064
|
+
reason: "disabled",
|
|
12065
|
+
...senderType ? { senderType } : {},
|
|
12066
|
+
...senderId ? { senderId } : {}
|
|
12067
|
+
};
|
|
12068
|
+
}
|
|
12069
|
+
if (senderType && BOT_SENDER_TYPES.has(senderType.toLowerCase())) {
|
|
12070
|
+
return {
|
|
12071
|
+
skip: true,
|
|
12072
|
+
reason: "bot-sender",
|
|
12073
|
+
...senderType ? { senderType } : {},
|
|
12074
|
+
...senderId ? { senderId } : {}
|
|
12075
|
+
};
|
|
12076
|
+
}
|
|
12077
|
+
if (senderId && options.selfBotIds && options.selfBotIds.length > 0) {
|
|
12078
|
+
if (options.selfBotIds.includes(senderId)) {
|
|
12079
|
+
return {
|
|
12080
|
+
skip: true,
|
|
12081
|
+
reason: "self-bot-id",
|
|
12082
|
+
...senderType ? { senderType } : {},
|
|
12083
|
+
senderId
|
|
12084
|
+
};
|
|
12085
|
+
}
|
|
12086
|
+
}
|
|
12087
|
+
return {
|
|
12088
|
+
skip: false,
|
|
12089
|
+
...senderType ? { senderType } : {},
|
|
12090
|
+
...senderId ? { senderId } : {}
|
|
12091
|
+
};
|
|
12092
|
+
}
|
|
12093
|
+
var SELF_ECHO_FILTER_ENABLED_ENV = "ROY_AGENT_SELF_ECHO_FILTER";
|
|
12094
|
+
function readSelfEchoFilterEnabled(env = process.env) {
|
|
12095
|
+
const raw = env[SELF_ECHO_FILTER_ENABLED_ENV];
|
|
12096
|
+
if (raw === undefined)
|
|
12097
|
+
return true;
|
|
12098
|
+
const v = raw.trim().toLowerCase();
|
|
12099
|
+
return !(v === "0" || v === "false" || v === "off" || v === "no");
|
|
12100
|
+
}
|
|
12101
|
+
|
|
12102
|
+
// src/commands/shared/event-handler.ts
|
|
11788
12103
|
function parseLarkPerTaskChatId(contextJson) {
|
|
11789
12104
|
if (!contextJson)
|
|
11790
12105
|
return;
|
|
@@ -11837,6 +12152,15 @@ class EventHandler {
|
|
|
11837
12152
|
resolvedSessionByTrigger = new Map;
|
|
11838
12153
|
eventStore = null;
|
|
11839
12154
|
getHandlerForEsType = null;
|
|
12155
|
+
telemetrySender = (notice) => sendLarkChatMarkdown(notice);
|
|
12156
|
+
selfEchoFilter = {
|
|
12157
|
+
enabled: true,
|
|
12158
|
+
selfBotIds: []
|
|
12159
|
+
};
|
|
12160
|
+
selfEchoDroppedCount = 0;
|
|
12161
|
+
getSelfEchoDroppedCount() {
|
|
12162
|
+
return this.selfEchoDroppedCount;
|
|
12163
|
+
}
|
|
11840
12164
|
constructor(options) {
|
|
11841
12165
|
this.env = options.env;
|
|
11842
12166
|
this.eventTypes = options.eventTypes;
|
|
@@ -11851,6 +12175,11 @@ class EventHandler {
|
|
|
11851
12175
|
this.sessionComponent = options.sessionComponent ?? null;
|
|
11852
12176
|
this.eventStore = options.eventStore ?? null;
|
|
11853
12177
|
this.getHandlerForEsType = options.getHandlerForEsType ?? null;
|
|
12178
|
+
this.telemetrySender = options.telemetrySender ?? ((notice) => sendLarkChatMarkdown(notice));
|
|
12179
|
+
this.selfEchoFilter = {
|
|
12180
|
+
enabled: options.selfEchoFilter?.enabled ?? readSelfEchoFilterEnabled(),
|
|
12181
|
+
selfBotIds: options.selfEchoFilter?.selfBotIds ?? []
|
|
12182
|
+
};
|
|
11854
12183
|
}
|
|
11855
12184
|
start() {
|
|
11856
12185
|
this.isStopped = false;
|
|
@@ -11897,13 +12226,82 @@ class EventHandler {
|
|
|
11897
12226
|
if (this.isStopped) {
|
|
11898
12227
|
return;
|
|
11899
12228
|
}
|
|
11900
|
-
|
|
12229
|
+
if (this.selfEchoFilter.enabled) {
|
|
12230
|
+
const verdict = isSelfEchoEvent(event, {
|
|
12231
|
+
selfBotIds: this.selfEchoFilter.selfBotIds
|
|
12232
|
+
});
|
|
12233
|
+
if (verdict.skip) {
|
|
12234
|
+
this.selfEchoDroppedCount += 1;
|
|
12235
|
+
console.error(`[EventHandler] self-echo event dropped (reason=${verdict.reason}` + `, senderType=${verdict.senderType ?? "-"}` + `, senderId=${verdict.senderId ?? "-"}` + `, type=${event.type})`);
|
|
12236
|
+
return;
|
|
12237
|
+
}
|
|
12238
|
+
}
|
|
12239
|
+
if (isTelemetryEvent(event.type)) {
|
|
12240
|
+
this.sendTelemetryNotice(event);
|
|
12241
|
+
return;
|
|
12242
|
+
}
|
|
12243
|
+
const ruleNotice = isRuleNoticeEvent(event.type) ? await this.sendRuleNotice(event) : undefined;
|
|
12244
|
+
const formatted = await this.formatEvent(event, { ruleNotice });
|
|
11901
12245
|
this.enqueue(formatted);
|
|
11902
12246
|
}
|
|
11903
|
-
async
|
|
12247
|
+
async resolveNoticeTarget(event) {
|
|
12248
|
+
const metadata = event.metadata;
|
|
12249
|
+
const target = targetFromEventMetadata(metadata);
|
|
12250
|
+
if (target)
|
|
12251
|
+
return target;
|
|
12252
|
+
const triggerEventId = typeof metadata?.triggerEventId === "string" ? metadata.triggerEventId : undefined;
|
|
12253
|
+
return targetFromTriggerEvent(this.eventStore, triggerEventId);
|
|
12254
|
+
}
|
|
12255
|
+
async sendRuleNotice(event) {
|
|
12256
|
+
try {
|
|
12257
|
+
const target = await this.resolveNoticeTarget(event);
|
|
12258
|
+
if (!target) {
|
|
12259
|
+
return { attempted: true, delivered: false, error: "no-target" };
|
|
12260
|
+
}
|
|
12261
|
+
const text = buildTerminalNoticeText(event.type, event.payload);
|
|
12262
|
+
await this.telemetrySender({
|
|
12263
|
+
chatId: target.chatId,
|
|
12264
|
+
...target.profile ? { profile: target.profile } : {},
|
|
12265
|
+
text,
|
|
12266
|
+
idempotencyKey: deriveRuleNoticeIdempotencyKey(event.id, target.chatId)
|
|
12267
|
+
});
|
|
12268
|
+
return {
|
|
12269
|
+
attempted: true,
|
|
12270
|
+
delivered: true,
|
|
12271
|
+
chatId: target.chatId,
|
|
12272
|
+
text,
|
|
12273
|
+
sentAt: Date.now()
|
|
12274
|
+
};
|
|
12275
|
+
} catch (err) {
|
|
12276
|
+
console.error(`[EventHandler] rule notice failed (swallowed) type=${event.type}`, err);
|
|
12277
|
+
return {
|
|
12278
|
+
attempted: true,
|
|
12279
|
+
delivered: false,
|
|
12280
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12281
|
+
};
|
|
12282
|
+
}
|
|
12283
|
+
}
|
|
12284
|
+
async sendTelemetryNotice(event) {
|
|
12285
|
+
try {
|
|
12286
|
+
const target = await this.resolveNoticeTarget(event);
|
|
12287
|
+
if (!target) {
|
|
12288
|
+
return;
|
|
12289
|
+
}
|
|
12290
|
+
const text = buildTelemetryNoticeText(event.type, event.payload);
|
|
12291
|
+
await this.telemetrySender({
|
|
12292
|
+
chatId: target.chatId,
|
|
12293
|
+
...target.profile ? { profile: target.profile } : {},
|
|
12294
|
+
text,
|
|
12295
|
+
idempotencyKey: deriveTelemetryIdempotencyKey(event.id, target.chatId)
|
|
12296
|
+
});
|
|
12297
|
+
} catch (err) {
|
|
12298
|
+
console.error(`[EventHandler] telemetry notice failed (swallowed) type=${event.type}`, err);
|
|
12299
|
+
}
|
|
12300
|
+
}
|
|
12301
|
+
async formatEvent(event, opts = {}) {
|
|
11904
12302
|
if (NOTIFY_V2_TERMINAL_TYPES.has(event.type) && event.metadata?.triggerEventId && this.eventStore && this.getHandlerForEsType) {
|
|
11905
12303
|
try {
|
|
11906
|
-
const v2Formatted = await this.formatEventViaESPluginV2(event);
|
|
12304
|
+
const v2Formatted = await this.formatEventViaESPluginV2(event, opts.ruleNotice);
|
|
11907
12305
|
if (v2Formatted) {
|
|
11908
12306
|
return v2Formatted;
|
|
11909
12307
|
}
|
|
@@ -11913,7 +12311,26 @@ class EventHandler {
|
|
|
11913
12311
|
}
|
|
11914
12312
|
return this.formatter.formatWithEvent(event);
|
|
11915
12313
|
}
|
|
11916
|
-
|
|
12314
|
+
buildNotifyChannelInfo(triggerEvent, handler, profile) {
|
|
12315
|
+
const meta = triggerEvent.metadata ?? {};
|
|
12316
|
+
const ctx = handler.extractReplyContext?.(triggerEvent, profile);
|
|
12317
|
+
const chatId = ctx?.chatId || meta.chatId || undefined;
|
|
12318
|
+
const messageId = ctx?.messageId || meta.messageId || undefined;
|
|
12319
|
+
const senderId = ctx?.senderOpenId || meta.senderId || undefined;
|
|
12320
|
+
const resolvedProfile = ctx?.profile ?? profile ?? meta.profile;
|
|
12321
|
+
const rawChatType = meta.chatType;
|
|
12322
|
+
const chatType = rawChatType === "group" || rawChatType === "p2p" ? rawChatType : "unknown";
|
|
12323
|
+
return {
|
|
12324
|
+
esType: triggerEvent.esType ?? handler.type,
|
|
12325
|
+
...chatId ? { chatId } : {},
|
|
12326
|
+
chatType,
|
|
12327
|
+
...messageId ? { messageId } : {},
|
|
12328
|
+
...senderId ? { senderId } : {},
|
|
12329
|
+
...resolvedProfile ? { profile: resolvedProfile } : {},
|
|
12330
|
+
replyKind: chatId ? messageId ? "reply" : "send" : "none"
|
|
12331
|
+
};
|
|
12332
|
+
}
|
|
12333
|
+
async formatEventViaESPluginV2(event, ruleNotice) {
|
|
11917
12334
|
const triggerEventId = event.metadata.triggerEventId;
|
|
11918
12335
|
const triggerEvent = await this.eventStore.getEventById(triggerEventId);
|
|
11919
12336
|
if (!triggerEvent) {
|
|
@@ -11928,17 +12345,21 @@ class EventHandler {
|
|
|
11928
12345
|
summary: typeof payload?.result === "string" ? payload.result : typeof payload?.error === "string" ? payload.error : "(无结果)"
|
|
11929
12346
|
};
|
|
11930
12347
|
const profile = triggerEvent.metadata?.profile ?? undefined;
|
|
12348
|
+
const channel = this.buildNotifyChannelInfo(triggerEvent, handler, profile);
|
|
11931
12349
|
const prompt = handler.buildNotifyHandlePrompt({
|
|
11932
12350
|
notifyEvent: {
|
|
11933
12351
|
type: event.type,
|
|
11934
12352
|
taskId: typeof payload?.associatedTaskId === "number" ? payload.associatedTaskId : undefined,
|
|
11935
12353
|
description: typeof payload?.description === "string" ? payload.description : undefined,
|
|
11936
12354
|
result: typeof payload?.result === "string" ? payload.result : undefined,
|
|
11937
|
-
error: typeof payload?.error === "string" ? payload.error : undefined
|
|
12355
|
+
error: typeof payload?.error === "string" ? payload.error : undefined,
|
|
12356
|
+
subSessionId: typeof payload?.subSessionId === "string" ? payload.subSessionId : undefined
|
|
11938
12357
|
},
|
|
11939
12358
|
triggerEvent,
|
|
11940
12359
|
subAgentResult,
|
|
11941
|
-
profile
|
|
12360
|
+
profile,
|
|
12361
|
+
channel,
|
|
12362
|
+
...ruleNotice ? { ruleNotice } : {}
|
|
11942
12363
|
});
|
|
11943
12364
|
if (handler.postProcessNotifyEvent) {
|
|
11944
12365
|
const notifyEventForHook = {
|
|
@@ -12388,18 +12809,36 @@ __legacyDecorateClassTS([
|
|
|
12388
12809
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12389
12810
|
], EventHandler.prototype, "handleEvent", null);
|
|
12390
12811
|
__legacyDecorateClassTS([
|
|
12391
|
-
TracedAs5("
|
|
12812
|
+
TracedAs5("event_handler.rule_notice", { recordParams: true, recordResult: false }),
|
|
12392
12813
|
__legacyMetadataTS("design:type", Function),
|
|
12393
12814
|
__legacyMetadataTS("design:paramtypes", [
|
|
12394
12815
|
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12395
12816
|
]),
|
|
12396
12817
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12818
|
+
], EventHandler.prototype, "sendRuleNotice", null);
|
|
12819
|
+
__legacyDecorateClassTS([
|
|
12820
|
+
TracedAs5("event_handler.telemetry_notice", { recordParams: true, recordResult: false }),
|
|
12821
|
+
__legacyMetadataTS("design:type", Function),
|
|
12822
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
12823
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12824
|
+
]),
|
|
12825
|
+
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12826
|
+
], EventHandler.prototype, "sendTelemetryNotice", null);
|
|
12827
|
+
__legacyDecorateClassTS([
|
|
12828
|
+
TracedAs5("cli.event-handler.formatEvent", { recordParams: true, recordResult: false }),
|
|
12829
|
+
__legacyMetadataTS("design:type", Function),
|
|
12830
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
12831
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent,
|
|
12832
|
+
Object
|
|
12833
|
+
]),
|
|
12834
|
+
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12397
12835
|
], EventHandler.prototype, "formatEvent", null);
|
|
12398
12836
|
__legacyDecorateClassTS([
|
|
12399
12837
|
TracedAs5("event_handler.process_notify", { recordParams: true, recordResult: false }),
|
|
12400
12838
|
__legacyMetadataTS("design:type", Function),
|
|
12401
12839
|
__legacyMetadataTS("design:paramtypes", [
|
|
12402
|
-
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12840
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent,
|
|
12841
|
+
typeof RuleNoticeInfo === "undefined" ? Object : RuleNoticeInfo
|
|
12403
12842
|
]),
|
|
12404
12843
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12405
12844
|
], EventHandler.prototype, "formatEventViaESPluginV2", null);
|
package/dist/index.js
CHANGED
|
@@ -9638,7 +9638,7 @@ class CliQuietModeService {
|
|
|
9638
9638
|
// package.json
|
|
9639
9639
|
var package_default = {
|
|
9640
9640
|
name: "@ai-setting/roy-agent-cli",
|
|
9641
|
-
version: "1.7.
|
|
9641
|
+
version: "1.7.59",
|
|
9642
9642
|
type: "module",
|
|
9643
9643
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9644
9644
|
main: "./dist/index.js",
|
|
@@ -11784,6 +11784,321 @@ import {
|
|
|
11784
11784
|
} from "@ai-setting/roy-agent-core/env";
|
|
11785
11785
|
import { TracedAs as TracedAs5 } from "@ai-setting/roy-agent-core";
|
|
11786
11786
|
import { notifyReactFailure as notifyReactFailure2 } from "@ai-setting/roy-agent-core/env/error-reply";
|
|
11787
|
+
|
|
11788
|
+
// src/commands/shared/telemetry-notice.ts
|
|
11789
|
+
import { spawn } from "node:child_process";
|
|
11790
|
+
import { normalizeLarkAtMentions } from "@ai-setting/roy-agent-core/env/lark-mention-utils";
|
|
11791
|
+
import { deriveIdempotencyKey } from "@ai-setting/roy-agent-core/env/event-source/idempotency-key";
|
|
11792
|
+
var TELEMETRY_EVENT_TYPES = new Set([
|
|
11793
|
+
"task.background.started",
|
|
11794
|
+
"task.background.progress"
|
|
11795
|
+
]);
|
|
11796
|
+
function isTelemetryEvent(type) {
|
|
11797
|
+
if (!type)
|
|
11798
|
+
return false;
|
|
11799
|
+
return TELEMETRY_EVENT_TYPES.has(type);
|
|
11800
|
+
}
|
|
11801
|
+
var RULE_NOTICE_EVENT_TYPES = new Set([
|
|
11802
|
+
"task.background.completed",
|
|
11803
|
+
"task.background.timeout"
|
|
11804
|
+
]);
|
|
11805
|
+
function isRuleNoticeEvent(type) {
|
|
11806
|
+
if (!type)
|
|
11807
|
+
return false;
|
|
11808
|
+
return RULE_NOTICE_EVENT_TYPES.has(type);
|
|
11809
|
+
}
|
|
11810
|
+
var MAX_TITLE_CHARS = 40;
|
|
11811
|
+
function pad2(n) {
|
|
11812
|
+
return n < 10 ? `0${n}` : String(n);
|
|
11813
|
+
}
|
|
11814
|
+
function formatElapsed(ms) {
|
|
11815
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11816
|
+
return "0m";
|
|
11817
|
+
const totalMin = Math.floor(ms / 60000);
|
|
11818
|
+
if (totalMin < 60)
|
|
11819
|
+
return `${totalMin}m`;
|
|
11820
|
+
const totalHour = Math.floor(totalMin / 60);
|
|
11821
|
+
const min = totalMin % 60;
|
|
11822
|
+
if (totalHour < 24) {
|
|
11823
|
+
return min === 0 ? `${totalHour}h` : `${totalHour}h${min}m`;
|
|
11824
|
+
}
|
|
11825
|
+
const day = Math.floor(totalHour / 24);
|
|
11826
|
+
const hour = totalHour % 24;
|
|
11827
|
+
return hour === 0 ? `${day}d` : `${day}d${hour}h`;
|
|
11828
|
+
}
|
|
11829
|
+
function formatClock(ts) {
|
|
11830
|
+
const d = new Date(ts);
|
|
11831
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
11832
|
+
}
|
|
11833
|
+
function formatTimeoutLabel(ms) {
|
|
11834
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11835
|
+
return "0m";
|
|
11836
|
+
return formatElapsed(ms);
|
|
11837
|
+
}
|
|
11838
|
+
function truncateTitle(raw) {
|
|
11839
|
+
const s = raw.trim();
|
|
11840
|
+
if (s.length <= MAX_TITLE_CHARS)
|
|
11841
|
+
return s;
|
|
11842
|
+
return `${s.slice(0, MAX_TITLE_CHARS)}…`;
|
|
11843
|
+
}
|
|
11844
|
+
function pickString(v) {
|
|
11845
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11846
|
+
}
|
|
11847
|
+
function pickNumber(v) {
|
|
11848
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
11849
|
+
}
|
|
11850
|
+
function buildTelemetryNoticeText(type, payload, now = Date.now()) {
|
|
11851
|
+
const p = payload ?? {};
|
|
11852
|
+
const taskId = pickNumber(p.associatedTaskId);
|
|
11853
|
+
const bgId = pickString(p.backgroundTaskId);
|
|
11854
|
+
const desc = truncateTitle(pickString(p.description) ?? "后台任务");
|
|
11855
|
+
let label;
|
|
11856
|
+
if (taskId !== undefined)
|
|
11857
|
+
label = `Task #${taskId}`;
|
|
11858
|
+
else if (bgId)
|
|
11859
|
+
label = bgId;
|
|
11860
|
+
else
|
|
11861
|
+
label = "后台任务";
|
|
11862
|
+
const head = type === "task.background.started" ? `\uD83D\uDD14 ${label}「${desc}」已启动` : `\uD83D\uDD04 ${label}「${desc}」后台运行中`;
|
|
11863
|
+
const parts = [];
|
|
11864
|
+
const startedAt = pickNumber(p.startedAt);
|
|
11865
|
+
const timeoutMs = pickNumber(p.timeoutMs);
|
|
11866
|
+
if (type !== "task.background.started" && startedAt !== undefined) {
|
|
11867
|
+
parts.push(`已跑 ${formatElapsed(now - startedAt)}`);
|
|
11868
|
+
}
|
|
11869
|
+
if (startedAt !== undefined && timeoutMs !== undefined) {
|
|
11870
|
+
parts.push(`预计 ${formatClock(startedAt + timeoutMs)} 超时(timeout ${formatTimeoutLabel(timeoutMs)})`);
|
|
11871
|
+
}
|
|
11872
|
+
let text = head;
|
|
11873
|
+
if (parts.length > 0)
|
|
11874
|
+
text += `
|
|
11875
|
+
${parts.join(",")}`;
|
|
11876
|
+
const progressMessage = pickString(p.progressMessage);
|
|
11877
|
+
if (progressMessage)
|
|
11878
|
+
text += `
|
|
11879
|
+
${progressMessage}`;
|
|
11880
|
+
return text;
|
|
11881
|
+
}
|
|
11882
|
+
function parseTimeoutMsFromError(error) {
|
|
11883
|
+
if (!error)
|
|
11884
|
+
return;
|
|
11885
|
+
const m = error.match(/timeout after\s+(\d+)\s*ms/i);
|
|
11886
|
+
if (!m)
|
|
11887
|
+
return;
|
|
11888
|
+
const n = Number(m[1]);
|
|
11889
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
11890
|
+
}
|
|
11891
|
+
function buildTerminalNoticeText(type, payload) {
|
|
11892
|
+
const p = payload ?? {};
|
|
11893
|
+
const taskId = pickNumber(p.associatedTaskId);
|
|
11894
|
+
const bgId = pickString(p.backgroundTaskId);
|
|
11895
|
+
const desc = truncateTitle(pickString(p.description) ?? "后台任务");
|
|
11896
|
+
let label;
|
|
11897
|
+
if (taskId !== undefined)
|
|
11898
|
+
label = `Task #${taskId}`;
|
|
11899
|
+
else if (bgId)
|
|
11900
|
+
label = bgId;
|
|
11901
|
+
else
|
|
11902
|
+
label = "后台任务";
|
|
11903
|
+
const elapsedMs = pickNumber(p.executionTimeMs);
|
|
11904
|
+
const elapsed = elapsedMs !== undefined ? formatElapsed(elapsedMs) : undefined;
|
|
11905
|
+
if (type === "task.background.timeout") {
|
|
11906
|
+
const timeoutMs = pickNumber(p.timeoutMs) ?? parseTimeoutMsFromError(p.error);
|
|
11907
|
+
const parts = [];
|
|
11908
|
+
if (elapsed)
|
|
11909
|
+
parts.push(`跑了 ${elapsed}`);
|
|
11910
|
+
if (timeoutMs !== undefined)
|
|
11911
|
+
parts.push(`timeout ${formatTimeoutLabel(timeoutMs)}`);
|
|
11912
|
+
const head = `⏱️ ${label}「${desc}」已超时`;
|
|
11913
|
+
return parts.length > 0 ? `${head}(${parts.join(",")})
|
|
11914
|
+
助手随后补充结论与下一步选项。` : `${head}
|
|
11915
|
+
助手随后补充结论与下一步选项。`;
|
|
11916
|
+
}
|
|
11917
|
+
if (type === "task.background.completed") {
|
|
11918
|
+
const head = `✅ ${label}「${desc}」已完成`;
|
|
11919
|
+
return elapsed ? `${head}(耗时 ${elapsed})
|
|
11920
|
+
助手随后补充交付摘要。` : `${head}
|
|
11921
|
+
助手随后补充交付摘要。`;
|
|
11922
|
+
}
|
|
11923
|
+
const bgStatus = type.replace("task.background.", "");
|
|
11924
|
+
return `ℹ️ ${label}「${desc}」${bgStatus}${elapsed ? `(耗时 ${elapsed})` : ""}`;
|
|
11925
|
+
}
|
|
11926
|
+
var DEFAULT_SEND_TIMEOUT_MS = 5000;
|
|
11927
|
+
var MAX_IDEMPOTENCY_KEY_LEN = 50;
|
|
11928
|
+
function deriveTelemetryIdempotencyKey(eventId, chatId) {
|
|
11929
|
+
return deriveNoticeIdempotencyKey("telemetry", eventId, chatId);
|
|
11930
|
+
}
|
|
11931
|
+
function deriveRuleNoticeIdempotencyKey(eventId, chatId) {
|
|
11932
|
+
return deriveNoticeIdempotencyKey("rule", eventId, chatId);
|
|
11933
|
+
}
|
|
11934
|
+
function deriveNoticeIdempotencyKey(namespace, eventId, chatId) {
|
|
11935
|
+
const anchor = pickString(eventId) ?? `ts-${Date.now()}-${Math.random()}`;
|
|
11936
|
+
return deriveIdempotencyKey([namespace, anchor, chatId]);
|
|
11937
|
+
}
|
|
11938
|
+
function buildLarkSendArgs(input) {
|
|
11939
|
+
const { chatId, text, profile, idempotencyKey } = input;
|
|
11940
|
+
const args = [
|
|
11941
|
+
"im",
|
|
11942
|
+
"+messages-send",
|
|
11943
|
+
"--chat-id",
|
|
11944
|
+
chatId,
|
|
11945
|
+
"--markdown",
|
|
11946
|
+
normalizeLarkAtMentions(text),
|
|
11947
|
+
"--as",
|
|
11948
|
+
"bot"
|
|
11949
|
+
];
|
|
11950
|
+
if (idempotencyKey) {
|
|
11951
|
+
args.push("--idempotency-key", idempotencyKey.slice(0, MAX_IDEMPOTENCY_KEY_LEN));
|
|
11952
|
+
}
|
|
11953
|
+
if (profile)
|
|
11954
|
+
args.unshift("--profile", profile);
|
|
11955
|
+
return args;
|
|
11956
|
+
}
|
|
11957
|
+
function sendLarkChatMarkdown(options) {
|
|
11958
|
+
const { chatId, text, profile, idempotencyKey } = options;
|
|
11959
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
11960
|
+
if (!chatId) {
|
|
11961
|
+
return Promise.reject(new Error("sendLarkChatMarkdown: empty chatId"));
|
|
11962
|
+
}
|
|
11963
|
+
return new Promise((resolve, reject) => {
|
|
11964
|
+
let settled = false;
|
|
11965
|
+
let timer;
|
|
11966
|
+
const finish = (err) => {
|
|
11967
|
+
if (settled)
|
|
11968
|
+
return;
|
|
11969
|
+
settled = true;
|
|
11970
|
+
if (timer)
|
|
11971
|
+
clearTimeout(timer);
|
|
11972
|
+
if (err)
|
|
11973
|
+
reject(err);
|
|
11974
|
+
else
|
|
11975
|
+
resolve();
|
|
11976
|
+
};
|
|
11977
|
+
const args = buildLarkSendArgs({ chatId, text, profile, idempotencyKey });
|
|
11978
|
+
let proc;
|
|
11979
|
+
try {
|
|
11980
|
+
proc = spawn("lark-cli", args, {
|
|
11981
|
+
shell: false,
|
|
11982
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
11983
|
+
});
|
|
11984
|
+
} catch (err) {
|
|
11985
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
11986
|
+
return;
|
|
11987
|
+
}
|
|
11988
|
+
let stderr = "";
|
|
11989
|
+
proc.stderr?.on("data", (chunk) => {
|
|
11990
|
+
stderr += chunk.toString();
|
|
11991
|
+
});
|
|
11992
|
+
timer = setTimeout(() => {
|
|
11993
|
+
try {
|
|
11994
|
+
proc.kill("SIGKILL");
|
|
11995
|
+
} catch {}
|
|
11996
|
+
finish(new Error(`lark-cli timed out after ${timeoutMs}ms`));
|
|
11997
|
+
}, timeoutMs);
|
|
11998
|
+
timer.unref?.();
|
|
11999
|
+
proc.on("close", (code) => {
|
|
12000
|
+
if (code === 0)
|
|
12001
|
+
finish();
|
|
12002
|
+
else
|
|
12003
|
+
finish(new Error(`lark-cli exited with code ${code}: ${stderr}`));
|
|
12004
|
+
});
|
|
12005
|
+
proc.on("error", (err) => finish(err));
|
|
12006
|
+
});
|
|
12007
|
+
}
|
|
12008
|
+
function targetFromEventMetadata(metadata) {
|
|
12009
|
+
if (!metadata)
|
|
12010
|
+
return;
|
|
12011
|
+
const replyChannel = metadata.replyChannel;
|
|
12012
|
+
const chatId = pickString(replyChannel?.chatId) ?? pickString(metadata.chatId);
|
|
12013
|
+
if (!chatId)
|
|
12014
|
+
return;
|
|
12015
|
+
const profile = pickString(replyChannel?.profile) ?? pickString(metadata.profile);
|
|
12016
|
+
return profile ? { chatId, profile } : { chatId };
|
|
12017
|
+
}
|
|
12018
|
+
async function targetFromTriggerEvent(eventStore, triggerEventId) {
|
|
12019
|
+
if (!eventStore || !triggerEventId)
|
|
12020
|
+
return;
|
|
12021
|
+
let persisted;
|
|
12022
|
+
try {
|
|
12023
|
+
persisted = await eventStore.getEventById(triggerEventId);
|
|
12024
|
+
} catch {
|
|
12025
|
+
return;
|
|
12026
|
+
}
|
|
12027
|
+
if (!persisted || typeof persisted !== "object")
|
|
12028
|
+
return;
|
|
12029
|
+
const metadata = persisted.metadata;
|
|
12030
|
+
if (!metadata || typeof metadata !== "object")
|
|
12031
|
+
return;
|
|
12032
|
+
return targetFromEventMetadata(metadata);
|
|
12033
|
+
}
|
|
12034
|
+
|
|
12035
|
+
// src/commands/shared/self-echo-filter.ts
|
|
12036
|
+
var BOT_SENDER_TYPES = new Set(["bot", "app"]);
|
|
12037
|
+
function pickString2(v) {
|
|
12038
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
12039
|
+
}
|
|
12040
|
+
function extractSenderIdentity(event) {
|
|
12041
|
+
if (!event)
|
|
12042
|
+
return {};
|
|
12043
|
+
const metadata = event.metadata;
|
|
12044
|
+
const payload = event.payload;
|
|
12045
|
+
const payloadMeta = payload?.metadata;
|
|
12046
|
+
const rawEvent = payload?.rawEvent;
|
|
12047
|
+
const innerEvent = rawEvent?.event;
|
|
12048
|
+
const innerSender = innerEvent?.sender;
|
|
12049
|
+
const topSender = rawEvent?.sender;
|
|
12050
|
+
const senderType = pickString2(metadata?.senderType) ?? pickString2(payloadMeta?.senderType) ?? pickString2(rawEvent?.sender_type) ?? pickString2(innerSender?.sender_type) ?? pickString2(topSender?.sender_type) ?? pickString2(payload?.senderType);
|
|
12051
|
+
const nestedSenderId = innerSender?.sender_id ?? topSender?.sender_id;
|
|
12052
|
+
const senderId = pickString2(metadata?.senderId) ?? pickString2(payloadMeta?.senderId) ?? pickString2(payload?.senderId) ?? pickString2(rawEvent?.sender_id) ?? (typeof nestedSenderId === "string" ? pickString2(nestedSenderId) : pickString2(nestedSenderId?.open_id) ?? pickString2(nestedSenderId?.user_id) ?? pickString2(nestedSenderId?.union_id));
|
|
12053
|
+
return {
|
|
12054
|
+
...senderType ? { senderType } : {},
|
|
12055
|
+
...senderId ? { senderId } : {}
|
|
12056
|
+
};
|
|
12057
|
+
}
|
|
12058
|
+
function isSelfEchoEvent(event, options = {}) {
|
|
12059
|
+
const { senderType, senderId } = extractSenderIdentity(event);
|
|
12060
|
+
if (options.enabled === false) {
|
|
12061
|
+
return {
|
|
12062
|
+
skip: false,
|
|
12063
|
+
reason: "disabled",
|
|
12064
|
+
...senderType ? { senderType } : {},
|
|
12065
|
+
...senderId ? { senderId } : {}
|
|
12066
|
+
};
|
|
12067
|
+
}
|
|
12068
|
+
if (senderType && BOT_SENDER_TYPES.has(senderType.toLowerCase())) {
|
|
12069
|
+
return {
|
|
12070
|
+
skip: true,
|
|
12071
|
+
reason: "bot-sender",
|
|
12072
|
+
...senderType ? { senderType } : {},
|
|
12073
|
+
...senderId ? { senderId } : {}
|
|
12074
|
+
};
|
|
12075
|
+
}
|
|
12076
|
+
if (senderId && options.selfBotIds && options.selfBotIds.length > 0) {
|
|
12077
|
+
if (options.selfBotIds.includes(senderId)) {
|
|
12078
|
+
return {
|
|
12079
|
+
skip: true,
|
|
12080
|
+
reason: "self-bot-id",
|
|
12081
|
+
...senderType ? { senderType } : {},
|
|
12082
|
+
senderId
|
|
12083
|
+
};
|
|
12084
|
+
}
|
|
12085
|
+
}
|
|
12086
|
+
return {
|
|
12087
|
+
skip: false,
|
|
12088
|
+
...senderType ? { senderType } : {},
|
|
12089
|
+
...senderId ? { senderId } : {}
|
|
12090
|
+
};
|
|
12091
|
+
}
|
|
12092
|
+
var SELF_ECHO_FILTER_ENABLED_ENV = "ROY_AGENT_SELF_ECHO_FILTER";
|
|
12093
|
+
function readSelfEchoFilterEnabled(env = process.env) {
|
|
12094
|
+
const raw = env[SELF_ECHO_FILTER_ENABLED_ENV];
|
|
12095
|
+
if (raw === undefined)
|
|
12096
|
+
return true;
|
|
12097
|
+
const v = raw.trim().toLowerCase();
|
|
12098
|
+
return !(v === "0" || v === "false" || v === "off" || v === "no");
|
|
12099
|
+
}
|
|
12100
|
+
|
|
12101
|
+
// src/commands/shared/event-handler.ts
|
|
11787
12102
|
function parseLarkPerTaskChatId(contextJson) {
|
|
11788
12103
|
if (!contextJson)
|
|
11789
12104
|
return;
|
|
@@ -11836,6 +12151,15 @@ class EventHandler {
|
|
|
11836
12151
|
resolvedSessionByTrigger = new Map;
|
|
11837
12152
|
eventStore = null;
|
|
11838
12153
|
getHandlerForEsType = null;
|
|
12154
|
+
telemetrySender = (notice) => sendLarkChatMarkdown(notice);
|
|
12155
|
+
selfEchoFilter = {
|
|
12156
|
+
enabled: true,
|
|
12157
|
+
selfBotIds: []
|
|
12158
|
+
};
|
|
12159
|
+
selfEchoDroppedCount = 0;
|
|
12160
|
+
getSelfEchoDroppedCount() {
|
|
12161
|
+
return this.selfEchoDroppedCount;
|
|
12162
|
+
}
|
|
11839
12163
|
constructor(options) {
|
|
11840
12164
|
this.env = options.env;
|
|
11841
12165
|
this.eventTypes = options.eventTypes;
|
|
@@ -11850,6 +12174,11 @@ class EventHandler {
|
|
|
11850
12174
|
this.sessionComponent = options.sessionComponent ?? null;
|
|
11851
12175
|
this.eventStore = options.eventStore ?? null;
|
|
11852
12176
|
this.getHandlerForEsType = options.getHandlerForEsType ?? null;
|
|
12177
|
+
this.telemetrySender = options.telemetrySender ?? ((notice) => sendLarkChatMarkdown(notice));
|
|
12178
|
+
this.selfEchoFilter = {
|
|
12179
|
+
enabled: options.selfEchoFilter?.enabled ?? readSelfEchoFilterEnabled(),
|
|
12180
|
+
selfBotIds: options.selfEchoFilter?.selfBotIds ?? []
|
|
12181
|
+
};
|
|
11853
12182
|
}
|
|
11854
12183
|
start() {
|
|
11855
12184
|
this.isStopped = false;
|
|
@@ -11896,13 +12225,82 @@ class EventHandler {
|
|
|
11896
12225
|
if (this.isStopped) {
|
|
11897
12226
|
return;
|
|
11898
12227
|
}
|
|
11899
|
-
|
|
12228
|
+
if (this.selfEchoFilter.enabled) {
|
|
12229
|
+
const verdict = isSelfEchoEvent(event, {
|
|
12230
|
+
selfBotIds: this.selfEchoFilter.selfBotIds
|
|
12231
|
+
});
|
|
12232
|
+
if (verdict.skip) {
|
|
12233
|
+
this.selfEchoDroppedCount += 1;
|
|
12234
|
+
console.error(`[EventHandler] self-echo event dropped (reason=${verdict.reason}` + `, senderType=${verdict.senderType ?? "-"}` + `, senderId=${verdict.senderId ?? "-"}` + `, type=${event.type})`);
|
|
12235
|
+
return;
|
|
12236
|
+
}
|
|
12237
|
+
}
|
|
12238
|
+
if (isTelemetryEvent(event.type)) {
|
|
12239
|
+
this.sendTelemetryNotice(event);
|
|
12240
|
+
return;
|
|
12241
|
+
}
|
|
12242
|
+
const ruleNotice = isRuleNoticeEvent(event.type) ? await this.sendRuleNotice(event) : undefined;
|
|
12243
|
+
const formatted = await this.formatEvent(event, { ruleNotice });
|
|
11900
12244
|
this.enqueue(formatted);
|
|
11901
12245
|
}
|
|
11902
|
-
async
|
|
12246
|
+
async resolveNoticeTarget(event) {
|
|
12247
|
+
const metadata = event.metadata;
|
|
12248
|
+
const target = targetFromEventMetadata(metadata);
|
|
12249
|
+
if (target)
|
|
12250
|
+
return target;
|
|
12251
|
+
const triggerEventId = typeof metadata?.triggerEventId === "string" ? metadata.triggerEventId : undefined;
|
|
12252
|
+
return targetFromTriggerEvent(this.eventStore, triggerEventId);
|
|
12253
|
+
}
|
|
12254
|
+
async sendRuleNotice(event) {
|
|
12255
|
+
try {
|
|
12256
|
+
const target = await this.resolveNoticeTarget(event);
|
|
12257
|
+
if (!target) {
|
|
12258
|
+
return { attempted: true, delivered: false, error: "no-target" };
|
|
12259
|
+
}
|
|
12260
|
+
const text = buildTerminalNoticeText(event.type, event.payload);
|
|
12261
|
+
await this.telemetrySender({
|
|
12262
|
+
chatId: target.chatId,
|
|
12263
|
+
...target.profile ? { profile: target.profile } : {},
|
|
12264
|
+
text,
|
|
12265
|
+
idempotencyKey: deriveRuleNoticeIdempotencyKey(event.id, target.chatId)
|
|
12266
|
+
});
|
|
12267
|
+
return {
|
|
12268
|
+
attempted: true,
|
|
12269
|
+
delivered: true,
|
|
12270
|
+
chatId: target.chatId,
|
|
12271
|
+
text,
|
|
12272
|
+
sentAt: Date.now()
|
|
12273
|
+
};
|
|
12274
|
+
} catch (err) {
|
|
12275
|
+
console.error(`[EventHandler] rule notice failed (swallowed) type=${event.type}`, err);
|
|
12276
|
+
return {
|
|
12277
|
+
attempted: true,
|
|
12278
|
+
delivered: false,
|
|
12279
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12280
|
+
};
|
|
12281
|
+
}
|
|
12282
|
+
}
|
|
12283
|
+
async sendTelemetryNotice(event) {
|
|
12284
|
+
try {
|
|
12285
|
+
const target = await this.resolveNoticeTarget(event);
|
|
12286
|
+
if (!target) {
|
|
12287
|
+
return;
|
|
12288
|
+
}
|
|
12289
|
+
const text = buildTelemetryNoticeText(event.type, event.payload);
|
|
12290
|
+
await this.telemetrySender({
|
|
12291
|
+
chatId: target.chatId,
|
|
12292
|
+
...target.profile ? { profile: target.profile } : {},
|
|
12293
|
+
text,
|
|
12294
|
+
idempotencyKey: deriveTelemetryIdempotencyKey(event.id, target.chatId)
|
|
12295
|
+
});
|
|
12296
|
+
} catch (err) {
|
|
12297
|
+
console.error(`[EventHandler] telemetry notice failed (swallowed) type=${event.type}`, err);
|
|
12298
|
+
}
|
|
12299
|
+
}
|
|
12300
|
+
async formatEvent(event, opts = {}) {
|
|
11903
12301
|
if (NOTIFY_V2_TERMINAL_TYPES.has(event.type) && event.metadata?.triggerEventId && this.eventStore && this.getHandlerForEsType) {
|
|
11904
12302
|
try {
|
|
11905
|
-
const v2Formatted = await this.formatEventViaESPluginV2(event);
|
|
12303
|
+
const v2Formatted = await this.formatEventViaESPluginV2(event, opts.ruleNotice);
|
|
11906
12304
|
if (v2Formatted) {
|
|
11907
12305
|
return v2Formatted;
|
|
11908
12306
|
}
|
|
@@ -11912,7 +12310,26 @@ class EventHandler {
|
|
|
11912
12310
|
}
|
|
11913
12311
|
return this.formatter.formatWithEvent(event);
|
|
11914
12312
|
}
|
|
11915
|
-
|
|
12313
|
+
buildNotifyChannelInfo(triggerEvent, handler, profile) {
|
|
12314
|
+
const meta = triggerEvent.metadata ?? {};
|
|
12315
|
+
const ctx = handler.extractReplyContext?.(triggerEvent, profile);
|
|
12316
|
+
const chatId = ctx?.chatId || meta.chatId || undefined;
|
|
12317
|
+
const messageId = ctx?.messageId || meta.messageId || undefined;
|
|
12318
|
+
const senderId = ctx?.senderOpenId || meta.senderId || undefined;
|
|
12319
|
+
const resolvedProfile = ctx?.profile ?? profile ?? meta.profile;
|
|
12320
|
+
const rawChatType = meta.chatType;
|
|
12321
|
+
const chatType = rawChatType === "group" || rawChatType === "p2p" ? rawChatType : "unknown";
|
|
12322
|
+
return {
|
|
12323
|
+
esType: triggerEvent.esType ?? handler.type,
|
|
12324
|
+
...chatId ? { chatId } : {},
|
|
12325
|
+
chatType,
|
|
12326
|
+
...messageId ? { messageId } : {},
|
|
12327
|
+
...senderId ? { senderId } : {},
|
|
12328
|
+
...resolvedProfile ? { profile: resolvedProfile } : {},
|
|
12329
|
+
replyKind: chatId ? messageId ? "reply" : "send" : "none"
|
|
12330
|
+
};
|
|
12331
|
+
}
|
|
12332
|
+
async formatEventViaESPluginV2(event, ruleNotice) {
|
|
11916
12333
|
const triggerEventId = event.metadata.triggerEventId;
|
|
11917
12334
|
const triggerEvent = await this.eventStore.getEventById(triggerEventId);
|
|
11918
12335
|
if (!triggerEvent) {
|
|
@@ -11927,17 +12344,21 @@ class EventHandler {
|
|
|
11927
12344
|
summary: typeof payload?.result === "string" ? payload.result : typeof payload?.error === "string" ? payload.error : "(无结果)"
|
|
11928
12345
|
};
|
|
11929
12346
|
const profile = triggerEvent.metadata?.profile ?? undefined;
|
|
12347
|
+
const channel = this.buildNotifyChannelInfo(triggerEvent, handler, profile);
|
|
11930
12348
|
const prompt = handler.buildNotifyHandlePrompt({
|
|
11931
12349
|
notifyEvent: {
|
|
11932
12350
|
type: event.type,
|
|
11933
12351
|
taskId: typeof payload?.associatedTaskId === "number" ? payload.associatedTaskId : undefined,
|
|
11934
12352
|
description: typeof payload?.description === "string" ? payload.description : undefined,
|
|
11935
12353
|
result: typeof payload?.result === "string" ? payload.result : undefined,
|
|
11936
|
-
error: typeof payload?.error === "string" ? payload.error : undefined
|
|
12354
|
+
error: typeof payload?.error === "string" ? payload.error : undefined,
|
|
12355
|
+
subSessionId: typeof payload?.subSessionId === "string" ? payload.subSessionId : undefined
|
|
11937
12356
|
},
|
|
11938
12357
|
triggerEvent,
|
|
11939
12358
|
subAgentResult,
|
|
11940
|
-
profile
|
|
12359
|
+
profile,
|
|
12360
|
+
channel,
|
|
12361
|
+
...ruleNotice ? { ruleNotice } : {}
|
|
11941
12362
|
});
|
|
11942
12363
|
if (handler.postProcessNotifyEvent) {
|
|
11943
12364
|
const notifyEventForHook = {
|
|
@@ -12387,18 +12808,36 @@ __legacyDecorateClassTS([
|
|
|
12387
12808
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12388
12809
|
], EventHandler.prototype, "handleEvent", null);
|
|
12389
12810
|
__legacyDecorateClassTS([
|
|
12390
|
-
TracedAs5("
|
|
12811
|
+
TracedAs5("event_handler.rule_notice", { recordParams: true, recordResult: false }),
|
|
12391
12812
|
__legacyMetadataTS("design:type", Function),
|
|
12392
12813
|
__legacyMetadataTS("design:paramtypes", [
|
|
12393
12814
|
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12394
12815
|
]),
|
|
12395
12816
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12817
|
+
], EventHandler.prototype, "sendRuleNotice", null);
|
|
12818
|
+
__legacyDecorateClassTS([
|
|
12819
|
+
TracedAs5("event_handler.telemetry_notice", { recordParams: true, recordResult: false }),
|
|
12820
|
+
__legacyMetadataTS("design:type", Function),
|
|
12821
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
12822
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12823
|
+
]),
|
|
12824
|
+
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12825
|
+
], EventHandler.prototype, "sendTelemetryNotice", null);
|
|
12826
|
+
__legacyDecorateClassTS([
|
|
12827
|
+
TracedAs5("cli.event-handler.formatEvent", { recordParams: true, recordResult: false }),
|
|
12828
|
+
__legacyMetadataTS("design:type", Function),
|
|
12829
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
12830
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent,
|
|
12831
|
+
Object
|
|
12832
|
+
]),
|
|
12833
|
+
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12396
12834
|
], EventHandler.prototype, "formatEvent", null);
|
|
12397
12835
|
__legacyDecorateClassTS([
|
|
12398
12836
|
TracedAs5("event_handler.process_notify", { recordParams: true, recordResult: false }),
|
|
12399
12837
|
__legacyMetadataTS("design:type", Function),
|
|
12400
12838
|
__legacyMetadataTS("design:paramtypes", [
|
|
12401
|
-
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12839
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent,
|
|
12840
|
+
typeof RuleNoticeInfo === "undefined" ? Object : RuleNoticeInfo
|
|
12402
12841
|
]),
|
|
12403
12842
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12404
12843
|
], EventHandler.prototype, "formatEventViaESPluginV2", null);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/dist/roy-agent-linux-x64/bin/roy-agent
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 109906048 bytes
|
|
4
|
+
sha256: a97336c59bd5ce3cada998cc8b03fec204414ec9ecc0995b494c0a3f6613d1b2
|