fix(slack): clear assistant thread status after replies
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { stripHeartbeatToken } from "../heartbeat.js";
|
import { stripHeartbeatToken } from "../heartbeat.js";
|
||||||
import { HEARTBEAT_TOKEN, SILENT_REPLY_TOKEN } from "../tokens.js";
|
import { HEARTBEAT_TOKEN, SILENT_REPLY_TOKEN } from "../tokens.js";
|
||||||
import type { ReplyPayload } from "../types.js";
|
import type { GetReplyOptions, ReplyPayload } from "../types.js";
|
||||||
|
import type { TypingController } from "./typing.js";
|
||||||
|
|
||||||
export type ReplyDispatchKind = "tool" | "block" | "final";
|
export type ReplyDispatchKind = "tool" | "block" | "final";
|
||||||
|
|
||||||
@@ -22,6 +23,20 @@ export type ReplyDispatcherOptions = {
|
|||||||
onError?: ReplyDispatchErrorHandler;
|
onError?: ReplyDispatchErrorHandler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ReplyDispatcherWithTypingOptions = Omit<
|
||||||
|
ReplyDispatcherOptions,
|
||||||
|
"onIdle"
|
||||||
|
> & {
|
||||||
|
onReplyStart?: () => Promise<void> | void;
|
||||||
|
onIdle?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ReplyDispatcherWithTypingResult = {
|
||||||
|
dispatcher: ReplyDispatcher;
|
||||||
|
replyOptions: Pick<GetReplyOptions, "onReplyStart" | "onTypingController">;
|
||||||
|
markDispatchIdle: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
export type ReplyDispatcher = {
|
export type ReplyDispatcher = {
|
||||||
sendToolResult: (payload: ReplyPayload) => boolean;
|
sendToolResult: (payload: ReplyPayload) => boolean;
|
||||||
sendBlockReply: (payload: ReplyPayload) => boolean;
|
sendBlockReply: (payload: ReplyPayload) => boolean;
|
||||||
@@ -107,3 +122,31 @@ export function createReplyDispatcher(
|
|||||||
getQueuedCounts: () => ({ ...queuedCounts }),
|
getQueuedCounts: () => ({ ...queuedCounts }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createReplyDispatcherWithTyping(
|
||||||
|
options: ReplyDispatcherWithTypingOptions,
|
||||||
|
): ReplyDispatcherWithTypingResult {
|
||||||
|
const { onReplyStart, onIdle, ...dispatcherOptions } = options;
|
||||||
|
let typingController: TypingController | undefined;
|
||||||
|
const dispatcher = createReplyDispatcher({
|
||||||
|
...dispatcherOptions,
|
||||||
|
onIdle: () => {
|
||||||
|
typingController?.markDispatchIdle();
|
||||||
|
onIdle?.();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
dispatcher,
|
||||||
|
replyOptions: {
|
||||||
|
onReplyStart,
|
||||||
|
onTypingController: (typing) => {
|
||||||
|
typingController = typing;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
markDispatchIdle: () => {
|
||||||
|
typingController?.markDispatchIdle();
|
||||||
|
onIdle?.();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ import {
|
|||||||
buildMentionRegexes,
|
buildMentionRegexes,
|
||||||
matchesMentionPatterns,
|
matchesMentionPatterns,
|
||||||
} from "../auto-reply/reply/mentions.js";
|
} from "../auto-reply/reply/mentions.js";
|
||||||
import { createReplyDispatcher } from "../auto-reply/reply/reply-dispatcher.js";
|
import {
|
||||||
import type { TypingController } from "../auto-reply/reply/typing.js";
|
createReplyDispatcher,
|
||||||
|
createReplyDispatcherWithTyping,
|
||||||
|
} from "../auto-reply/reply/reply-dispatcher.js";
|
||||||
import { getReplyFromConfig } from "../auto-reply/reply.js";
|
import { getReplyFromConfig } from "../auto-reply/reply.js";
|
||||||
import type { ReplyPayload } from "../auto-reply/types.js";
|
import type { ReplyPayload } from "../auto-reply/types.js";
|
||||||
import type { ReplyToMode } from "../config/config.js";
|
import type { ReplyToMode } from "../config/config.js";
|
||||||
@@ -797,43 +799,36 @@ export function createDiscordMessageHandler(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let didSendReply = false;
|
let didSendReply = false;
|
||||||
let typingController: TypingController | undefined;
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
||||||
const dispatcher = createReplyDispatcher({
|
createReplyDispatcherWithTyping({
|
||||||
responsePrefix: cfg.messages?.responsePrefix,
|
responsePrefix: cfg.messages?.responsePrefix,
|
||||||
deliver: async (payload) => {
|
deliver: async (payload) => {
|
||||||
await deliverDiscordReply({
|
await deliverDiscordReply({
|
||||||
replies: [payload],
|
replies: [payload],
|
||||||
target: replyTarget,
|
target: replyTarget,
|
||||||
token,
|
token,
|
||||||
rest: client.rest,
|
rest: client.rest,
|
||||||
runtime,
|
runtime,
|
||||||
replyToMode,
|
replyToMode,
|
||||||
textLimit,
|
textLimit,
|
||||||
});
|
});
|
||||||
didSendReply = true;
|
didSendReply = true;
|
||||||
},
|
},
|
||||||
onIdle: () => {
|
onError: (err, info) => {
|
||||||
typingController?.markDispatchIdle();
|
runtime.error?.(
|
||||||
},
|
danger(`discord ${info.kind} reply failed: ${String(err)}`),
|
||||||
onError: (err, info) => {
|
);
|
||||||
runtime.error?.(
|
},
|
||||||
danger(`discord ${info.kind} reply failed: ${String(err)}`),
|
onReplyStart: () => sendTyping(message),
|
||||||
);
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { queuedFinal, counts } = await dispatchReplyFromConfig({
|
const { queuedFinal, counts } = await dispatchReplyFromConfig({
|
||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
cfg,
|
cfg,
|
||||||
dispatcher,
|
dispatcher,
|
||||||
replyOptions: {
|
replyOptions,
|
||||||
onReplyStart: () => sendTyping(message),
|
|
||||||
onTypingController: (typing) => {
|
|
||||||
typingController = typing;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
typingController?.markDispatchIdle();
|
markDispatchIdle();
|
||||||
if (!queuedFinal) {
|
if (!queuedFinal) {
|
||||||
if (
|
if (
|
||||||
isGuildMessage &&
|
isGuildMessage &&
|
||||||
|
|||||||
@@ -189,12 +189,20 @@ describe("monitorSlackProvider tool results", () => {
|
|||||||
const client = getSlackClient() as {
|
const client = getSlackClient() as {
|
||||||
assistant?: { threads?: { setStatus?: ReturnType<typeof vi.fn> } };
|
assistant?: { threads?: { setStatus?: ReturnType<typeof vi.fn> } };
|
||||||
};
|
};
|
||||||
expect(client.assistant?.threads?.setStatus).toHaveBeenCalledWith({
|
const setStatus = client.assistant?.threads?.setStatus;
|
||||||
|
expect(setStatus).toHaveBeenCalledTimes(2);
|
||||||
|
expect(setStatus).toHaveBeenNthCalledWith(1, {
|
||||||
token: "bot-token",
|
token: "bot-token",
|
||||||
channel_id: "C1",
|
channel_id: "C1",
|
||||||
thread_ts: "123",
|
thread_ts: "123",
|
||||||
status: "is typing...",
|
status: "is typing...",
|
||||||
});
|
});
|
||||||
|
expect(setStatus).toHaveBeenNthCalledWith(2, {
|
||||||
|
token: "bot-token",
|
||||||
|
channel_id: "C1",
|
||||||
|
thread_ts: "123",
|
||||||
|
status: "",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("accepts channel messages when mentionPatterns match", async () => {
|
it("accepts channel messages when mentionPatterns match", async () => {
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ import {
|
|||||||
buildMentionRegexes,
|
buildMentionRegexes,
|
||||||
matchesMentionPatterns,
|
matchesMentionPatterns,
|
||||||
} from "../auto-reply/reply/mentions.js";
|
} from "../auto-reply/reply/mentions.js";
|
||||||
import { createReplyDispatcher } from "../auto-reply/reply/reply-dispatcher.js";
|
import { createReplyDispatcherWithTyping } from "../auto-reply/reply/reply-dispatcher.js";
|
||||||
import type { TypingController } from "../auto-reply/reply/typing.js";
|
|
||||||
import { getReplyFromConfig } from "../auto-reply/reply.js";
|
import { getReplyFromConfig } from "../auto-reply/reply.js";
|
||||||
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
||||||
import type { ReplyPayload } from "../auto-reply/types.js";
|
import type { ReplyPayload } from "../auto-reply/types.js";
|
||||||
@@ -860,61 +859,58 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
// Only thread replies if the incoming message was in a thread.
|
// Only thread replies if the incoming message was in a thread.
|
||||||
const incomingThreadTs = message.thread_ts;
|
const incomingThreadTs = message.thread_ts;
|
||||||
const statusThreadTs = message.thread_ts ?? message.ts;
|
const statusThreadTs = message.thread_ts ?? message.ts;
|
||||||
|
let didSetStatus = false;
|
||||||
const onReplyStart = async () => {
|
const onReplyStart = async () => {
|
||||||
|
didSetStatus = true;
|
||||||
await setSlackThreadStatus({
|
await setSlackThreadStatus({
|
||||||
channelId: message.channel,
|
channelId: message.channel,
|
||||||
threadTs: statusThreadTs,
|
threadTs: statusThreadTs,
|
||||||
status: "is typing...",
|
status: "is typing...",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let typingController: TypingController | undefined;
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
||||||
const dispatcher = createReplyDispatcher({
|
createReplyDispatcherWithTyping({
|
||||||
responsePrefix: cfg.messages?.responsePrefix,
|
responsePrefix: cfg.messages?.responsePrefix,
|
||||||
deliver: async (payload) => {
|
deliver: async (payload) => {
|
||||||
await deliverReplies({
|
await deliverReplies({
|
||||||
replies: [payload],
|
replies: [payload],
|
||||||
target: replyTarget,
|
target: replyTarget,
|
||||||
token: botToken,
|
token: botToken,
|
||||||
runtime,
|
runtime,
|
||||||
textLimit,
|
textLimit,
|
||||||
threadTs: incomingThreadTs,
|
threadTs: incomingThreadTs,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onIdle: () => {
|
onError: (err, info) => {
|
||||||
typingController?.markDispatchIdle();
|
runtime.error?.(
|
||||||
},
|
danger(`slack ${info.kind} reply failed: ${String(err)}`),
|
||||||
onError: (err, info) => {
|
);
|
||||||
runtime.error?.(
|
if (didSetStatus) {
|
||||||
danger(`slack ${info.kind} reply failed: ${String(err)}`),
|
void setSlackThreadStatus({
|
||||||
);
|
channelId: message.channel,
|
||||||
void setSlackThreadStatus({
|
threadTs: statusThreadTs,
|
||||||
channelId: message.channel,
|
status: "",
|
||||||
threadTs: statusThreadTs,
|
});
|
||||||
status: "",
|
}
|
||||||
});
|
},
|
||||||
},
|
onReplyStart,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { queuedFinal, counts } = await dispatchReplyFromConfig({
|
const { queuedFinal, counts } = await dispatchReplyFromConfig({
|
||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
cfg,
|
cfg,
|
||||||
dispatcher,
|
dispatcher,
|
||||||
replyOptions: {
|
replyOptions,
|
||||||
onReplyStart,
|
|
||||||
onTypingController: (typing) => {
|
|
||||||
typingController = typing;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
typingController?.markDispatchIdle();
|
markDispatchIdle();
|
||||||
if (!queuedFinal) {
|
if (didSetStatus) {
|
||||||
await setSlackThreadStatus({
|
await setSlackThreadStatus({
|
||||||
channelId: message.channel,
|
channelId: message.channel,
|
||||||
threadTs: statusThreadTs,
|
threadTs: statusThreadTs,
|
||||||
status: "",
|
status: "",
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
if (!queuedFinal) return;
|
||||||
if (shouldLogVerbose()) {
|
if (shouldLogVerbose()) {
|
||||||
const finalCount = counts.final;
|
const finalCount = counts.final;
|
||||||
logVerbose(
|
logVerbose(
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ import {
|
|||||||
buildMentionRegexes,
|
buildMentionRegexes,
|
||||||
matchesMentionPatterns,
|
matchesMentionPatterns,
|
||||||
} from "../auto-reply/reply/mentions.js";
|
} from "../auto-reply/reply/mentions.js";
|
||||||
import { createReplyDispatcher } from "../auto-reply/reply/reply-dispatcher.js";
|
import { createReplyDispatcherWithTyping } from "../auto-reply/reply/reply-dispatcher.js";
|
||||||
import type { TypingController } from "../auto-reply/reply/typing.js";
|
|
||||||
import type { ReplyPayload } from "../auto-reply/types.js";
|
import type { ReplyPayload } from "../auto-reply/types.js";
|
||||||
import type { ReplyToMode } from "../config/config.js";
|
import type { ReplyToMode } from "../config/config.js";
|
||||||
import { loadConfig } from "../config/config.js";
|
import { loadConfig } from "../config/config.js";
|
||||||
@@ -451,42 +450,35 @@ export function createTelegramBot(opts: TelegramBotOptions) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let typingController: TypingController | undefined;
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
||||||
const dispatcher = createReplyDispatcher({
|
createReplyDispatcherWithTyping({
|
||||||
responsePrefix: cfg.messages?.responsePrefix,
|
responsePrefix: cfg.messages?.responsePrefix,
|
||||||
deliver: async (payload) => {
|
deliver: async (payload) => {
|
||||||
await deliverReplies({
|
await deliverReplies({
|
||||||
replies: [payload],
|
replies: [payload],
|
||||||
chatId: String(chatId),
|
chatId: String(chatId),
|
||||||
token: opts.token,
|
token: opts.token,
|
||||||
runtime,
|
runtime,
|
||||||
bot,
|
bot,
|
||||||
replyToMode,
|
replyToMode,
|
||||||
textLimit,
|
textLimit,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onIdle: () => {
|
onError: (err, info) => {
|
||||||
typingController?.markDispatchIdle();
|
runtime.error?.(
|
||||||
},
|
danger(`telegram ${info.kind} reply failed: ${String(err)}`),
|
||||||
onError: (err, info) => {
|
);
|
||||||
runtime.error?.(
|
},
|
||||||
danger(`telegram ${info.kind} reply failed: ${String(err)}`),
|
onReplyStart: sendTyping,
|
||||||
);
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { queuedFinal } = await dispatchReplyFromConfig({
|
const { queuedFinal } = await dispatchReplyFromConfig({
|
||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
cfg,
|
cfg,
|
||||||
dispatcher,
|
dispatcher,
|
||||||
replyOptions: {
|
replyOptions,
|
||||||
onReplyStart: sendTyping,
|
|
||||||
onTypingController: (typing) => {
|
|
||||||
typingController = typing;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
typingController?.markDispatchIdle();
|
markDispatchIdle();
|
||||||
if (!queuedFinal) return;
|
if (!queuedFinal) return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ import {
|
|||||||
buildMentionRegexes,
|
buildMentionRegexes,
|
||||||
normalizeMentionText,
|
normalizeMentionText,
|
||||||
} from "../auto-reply/reply/mentions.js";
|
} from "../auto-reply/reply/mentions.js";
|
||||||
import { createReplyDispatcher } from "../auto-reply/reply/reply-dispatcher.js";
|
import { createReplyDispatcherWithTyping } from "../auto-reply/reply/reply-dispatcher.js";
|
||||||
import type { TypingController } from "../auto-reply/reply/typing.js";
|
|
||||||
import { getReplyFromConfig } from "../auto-reply/reply.js";
|
import { getReplyFromConfig } from "../auto-reply/reply.js";
|
||||||
import { HEARTBEAT_TOKEN, SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
import { HEARTBEAT_TOKEN, SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
||||||
import type { ReplyPayload } from "../auto-reply/types.js";
|
import type { ReplyPayload } from "../auto-reply/types.js";
|
||||||
@@ -1161,72 +1160,70 @@ export async function monitorWebProvider(
|
|||||||
const textLimit = resolveTextChunkLimit(cfg, "whatsapp");
|
const textLimit = resolveTextChunkLimit(cfg, "whatsapp");
|
||||||
let didLogHeartbeatStrip = false;
|
let didLogHeartbeatStrip = false;
|
||||||
let didSendReply = false;
|
let didSendReply = false;
|
||||||
let typingController: TypingController | undefined;
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
||||||
const dispatcher = createReplyDispatcher({
|
createReplyDispatcherWithTyping({
|
||||||
responsePrefix: cfg.messages?.responsePrefix,
|
responsePrefix: cfg.messages?.responsePrefix,
|
||||||
onHeartbeatStrip: () => {
|
onHeartbeatStrip: () => {
|
||||||
if (!didLogHeartbeatStrip) {
|
if (!didLogHeartbeatStrip) {
|
||||||
didLogHeartbeatStrip = true;
|
didLogHeartbeatStrip = true;
|
||||||
logVerbose("Stripped stray HEARTBEAT_OK token from web reply");
|
logVerbose("Stripped stray HEARTBEAT_OK token from web reply");
|
||||||
}
|
|
||||||
},
|
|
||||||
deliver: async (payload, info) => {
|
|
||||||
await deliverWebReply({
|
|
||||||
replyResult: payload,
|
|
||||||
msg,
|
|
||||||
maxMediaBytes,
|
|
||||||
textLimit,
|
|
||||||
replyLogger,
|
|
||||||
connectionId,
|
|
||||||
// Tool + block updates are noisy; skip their log lines.
|
|
||||||
skipLog: info.kind !== "final",
|
|
||||||
});
|
|
||||||
didSendReply = true;
|
|
||||||
if (info.kind === "tool") {
|
|
||||||
rememberSentText(payload.text, { combinedBody: "" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const shouldLog =
|
|
||||||
info.kind === "final" && payload.text ? true : undefined;
|
|
||||||
rememberSentText(payload.text, {
|
|
||||||
combinedBody,
|
|
||||||
logVerboseMessage: shouldLog,
|
|
||||||
});
|
|
||||||
if (info.kind === "final") {
|
|
||||||
const fromDisplay =
|
|
||||||
msg.chatType === "group"
|
|
||||||
? conversationId
|
|
||||||
: (msg.from ?? "unknown");
|
|
||||||
const hasMedia = Boolean(
|
|
||||||
payload.mediaUrl || payload.mediaUrls?.length,
|
|
||||||
);
|
|
||||||
whatsappOutboundLog.info(
|
|
||||||
`Auto-replied to ${fromDisplay}${hasMedia ? " (media)" : ""}`,
|
|
||||||
);
|
|
||||||
if (shouldLogVerbose()) {
|
|
||||||
const preview =
|
|
||||||
payload.text != null ? elide(payload.text, 400) : "<media>";
|
|
||||||
whatsappOutboundLog.debug(
|
|
||||||
`Reply body: ${preview}${hasMedia ? " (media)" : ""}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
deliver: async (payload, info) => {
|
||||||
onIdle: () => {
|
await deliverWebReply({
|
||||||
typingController?.markDispatchIdle();
|
replyResult: payload,
|
||||||
},
|
msg,
|
||||||
onError: (err, info) => {
|
maxMediaBytes,
|
||||||
const label =
|
textLimit,
|
||||||
info.kind === "tool"
|
replyLogger,
|
||||||
? "tool update"
|
connectionId,
|
||||||
: info.kind === "block"
|
// Tool + block updates are noisy; skip their log lines.
|
||||||
? "block update"
|
skipLog: info.kind !== "final",
|
||||||
: "auto-reply";
|
});
|
||||||
whatsappOutboundLog.error(
|
didSendReply = true;
|
||||||
`Failed sending web ${label} to ${msg.from ?? conversationId}: ${formatError(err)}`,
|
if (info.kind === "tool") {
|
||||||
);
|
rememberSentText(payload.text, { combinedBody: "" });
|
||||||
},
|
return;
|
||||||
});
|
}
|
||||||
|
const shouldLog =
|
||||||
|
info.kind === "final" && payload.text ? true : undefined;
|
||||||
|
rememberSentText(payload.text, {
|
||||||
|
combinedBody,
|
||||||
|
logVerboseMessage: shouldLog,
|
||||||
|
});
|
||||||
|
if (info.kind === "final") {
|
||||||
|
const fromDisplay =
|
||||||
|
msg.chatType === "group"
|
||||||
|
? conversationId
|
||||||
|
: (msg.from ?? "unknown");
|
||||||
|
const hasMedia = Boolean(
|
||||||
|
payload.mediaUrl || payload.mediaUrls?.length,
|
||||||
|
);
|
||||||
|
whatsappOutboundLog.info(
|
||||||
|
`Auto-replied to ${fromDisplay}${hasMedia ? " (media)" : ""}`,
|
||||||
|
);
|
||||||
|
if (shouldLogVerbose()) {
|
||||||
|
const preview =
|
||||||
|
payload.text != null ? elide(payload.text, 400) : "<media>";
|
||||||
|
whatsappOutboundLog.debug(
|
||||||
|
`Reply body: ${preview}${hasMedia ? " (media)" : ""}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (err, info) => {
|
||||||
|
const label =
|
||||||
|
info.kind === "tool"
|
||||||
|
? "tool update"
|
||||||
|
: info.kind === "block"
|
||||||
|
? "block update"
|
||||||
|
: "auto-reply";
|
||||||
|
whatsappOutboundLog.error(
|
||||||
|
`Failed sending web ${label} to ${msg.from ?? conversationId}: ${formatError(err)}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onReplyStart: msg.sendComposing,
|
||||||
|
});
|
||||||
|
|
||||||
const { queuedFinal } = await dispatchReplyFromConfig({
|
const { queuedFinal } = await dispatchReplyFromConfig({
|
||||||
ctx: {
|
ctx: {
|
||||||
@@ -1258,14 +1255,9 @@ export async function monitorWebProvider(
|
|||||||
cfg,
|
cfg,
|
||||||
dispatcher,
|
dispatcher,
|
||||||
replyResolver,
|
replyResolver,
|
||||||
replyOptions: {
|
replyOptions,
|
||||||
onReplyStart: msg.sendComposing,
|
|
||||||
onTypingController: (typing) => {
|
|
||||||
typingController = typing;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
typingController?.markDispatchIdle();
|
markDispatchIdle();
|
||||||
if (!queuedFinal) {
|
if (!queuedFinal) {
|
||||||
if (shouldClearGroupHistory && didSendReply) {
|
if (shouldClearGroupHistory && didSendReply) {
|
||||||
groupHistories.set(route.sessionKey, []);
|
groupHistories.set(route.sessionKey, []);
|
||||||
|
|||||||
Reference in New Issue
Block a user