



























@@ -82,6 +82,21 @@ type DiscordMessageProcessObserver = {
8282onReplyPlanResolved?: (params: { createdThreadId?: string; sessionKey?: string }) => void;
8383};
848485+type ToolStartPayload = {
86+name?: string;
87+phase?: string;
88+args?: Record<string, unknown>;
89+};
90+91+function readToolStringArg(args: Record<string, unknown>, key: string): string | undefined {
92+const value = args[key];
93+return typeof value === "string" && value.trim() ? value.trim() : undefined;
94+}
95+96+function readToolBooleanArg(args: Record<string, unknown>, key: string): boolean {
97+return args[key] === true;
98+}
99+85100export async function processDiscordMessage(
86101ctx: DiscordMessagePreflightContext,
87102observer?: DiscordMessageProcessObserver,
@@ -203,7 +218,9 @@ export async function processDiscordMessage(
203218messageId: message.id,
204219reactionContext: ackReactionContext,
205220});
206-const statusReactions = createStatusReactionController({
221+let statusReactionTarget = `${messageChannelId}/${message.id}`;
222+let statusReactionsActive = statusReactionsEnabled;
223+let statusReactions = createStatusReactionController({
207224enabled: statusReactionsEnabled,
208225adapter: discordAdapter,
209226initialEmoji: ackReaction,
@@ -213,11 +230,67 @@ export async function processDiscordMessage(
213230logAckFailure({
214231log: logVerbose,
215232channel: "discord",
216-target: `${messageChannelId}/${message.id}`,
233+target: statusReactionTarget,
217234error: err,
218235});
219236},
220237});
238+const maybeBindStatusReactionsToToolReaction = (payload: ToolStartPayload) => {
239+if (
240+sourceRepliesAreToolOnly ||
241+cfg.messages?.statusReactions?.enabled === false ||
242+payload.phase !== "start" ||
243+payload.name !== "message" ||
244+!payload.args
245+) {
246+return;
247+}
248+const args = payload.args;
249+const action = readToolStringArg(args, "action")?.toLowerCase();
250+if (action !== "react") {
251+return;
252+}
253+const shouldTrack =
254+readToolBooleanArg(args, "trackToolCalls") || readToolBooleanArg(args, "track_tool_calls");
255+if (!shouldTrack) {
256+return;
257+}
258+const emoji = readToolStringArg(args, "emoji");
259+const remove = readToolBooleanArg(args, "remove");
260+if (!emoji || remove) {
261+return;
262+}
263+const trackedMessageId =
264+readToolStringArg(args, "messageId") ?? readToolStringArg(args, "message_id") ?? message.id;
265+const trackedChannelId =
266+readToolStringArg(args, "channelId") ?? readToolStringArg(args, "to") ?? messageChannelId;
267+statusReactionTarget = `${trackedChannelId}/${trackedMessageId}`;
268+if (statusReactionsActive) {
269+void statusReactions.clear();
270+}
271+const trackedAdapter = createDiscordAckReactionAdapter({
272+channelId: trackedChannelId,
273+messageId: trackedMessageId,
274+reactionContext: ackReactionContext,
275+});
276+statusReactions = createStatusReactionController({
277+enabled: true,
278+adapter: trackedAdapter,
279+initialEmoji: emoji,
280+emojis: cfg.messages?.statusReactions?.emojis,
281+timing: cfg.messages?.statusReactions?.timing,
282+onError: (err) => {
283+logAckFailure({
284+log: logVerbose,
285+channel: "discord",
286+target: statusReactionTarget,
287+error: err,
288+});
289+},
290+});
291+statusReactionsActive = true;
292+void statusReactions.setQueued();
293+};
221294queueInitialDiscordAckReaction({
222295enabled: statusReactionsEnabled,
223296 shouldSendAckReaction,
@@ -546,6 +619,7 @@ export async function processDiscordMessage(
546619if (isProcessAborted(abortSignal)) {
547620return;
548621}
622+maybeBindStatusReactionsToToolReaction(payload);
549623await statusReactions.setTool(payload.name);
550624draftPreview.pushToolProgress(
551625payload.name ? `tool: ${payload.name}` : "tool running",
@@ -632,7 +706,7 @@ export async function processDiscordMessage(
632706markDispatchIdle();
633707}
634708}
635-if (statusReactionsEnabled) {
709+if (statusReactionsActive) {
636710if (dispatchAborted) {
637711if (removeAckAfterReply) {
638712void statusReactions.clear();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。