@openclaw-china/dingtalk 2026.3.5 → 2026.3.7

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/index.d.ts CHANGED
@@ -14,6 +14,7 @@ import { z } from 'zod';
14
14
  * - groupAllowFrom: 群聊白名单会话 ID 列表
15
15
  * - historyLimit: 历史消息数量限制
16
16
  * - textChunkLimit: 文本分块大小限制
17
+ * - longTaskNoticeDelayMs: 长任务提醒延迟(毫秒,0 表示关闭)
17
18
  * - enableAICard: 是否启用 AI Card 流式响应
18
19
  * - maxFileSizeMB: 媒体文件大小限制 (MB)
19
20
  * - inboundMedia: 入站媒体归档与保留策略
@@ -39,6 +40,8 @@ declare const DingtalkConfigSchema: z.ZodObject<{
39
40
  historyLimit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
40
41
  /** 文本分块大小限制 (钉钉单条消息最大 4000 字符) */
41
42
  textChunkLimit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
43
+ /** 长任务提醒延迟(毫秒),0 表示关闭 */
44
+ longTaskNoticeDelayMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
42
45
  /** 是否启用 AI Card 流式响应 */
43
46
  enableAICard: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
44
47
  /** Gateway auth token(Bearer) */
@@ -65,6 +68,7 @@ declare const DingtalkConfigSchema: z.ZodObject<{
65
68
  requireMention: boolean;
66
69
  historyLimit: number;
67
70
  textChunkLimit: number;
71
+ longTaskNoticeDelayMs: number;
68
72
  enableAICard: boolean;
69
73
  maxFileSizeMB: number;
70
74
  clientId?: string | undefined;
@@ -88,6 +92,7 @@ declare const DingtalkConfigSchema: z.ZodObject<{
88
92
  groupAllowFrom?: string[] | undefined;
89
93
  historyLimit?: number | undefined;
90
94
  textChunkLimit?: number | undefined;
95
+ longTaskNoticeDelayMs?: number | undefined;
91
96
  enableAICard?: boolean | undefined;
92
97
  gatewayToken?: string | undefined;
93
98
  gatewayPassword?: string | undefined;
@@ -293,6 +298,10 @@ declare const dingtalkPlugin: {
293
298
  type: string;
294
299
  minimum: number;
295
300
  };
301
+ longTaskNoticeDelayMs: {
302
+ type: string;
303
+ minimum: number;
304
+ };
296
305
  enableAICard: {
297
306
  type: string;
298
307
  };
@@ -751,6 +760,10 @@ declare const plugin: {
751
760
  type: string;
752
761
  minimum: number;
753
762
  };
763
+ longTaskNoticeDelayMs: {
764
+ type: string;
765
+ minimum: number;
766
+ };
754
767
  };
755
768
  };
756
769
  /**
package/dist/index.js CHANGED
@@ -4236,6 +4236,8 @@ var DingtalkConfigSchema = external_exports.object({
4236
4236
  historyLimit: external_exports.number().int().min(0).optional().default(10),
4237
4237
  /** 文本分块大小限制 (钉钉单条消息最大 4000 字符) */
4238
4238
  textChunkLimit: external_exports.number().int().positive().optional().default(4e3),
4239
+ /** 长任务提醒延迟(毫秒),0 表示关闭 */
4240
+ longTaskNoticeDelayMs: external_exports.number().int().min(0).optional().default(3e4),
4239
4241
  /** 是否启用 AI Card 流式响应 */
4240
4242
  enableAICard: external_exports.boolean().optional().default(true),
4241
4243
  /** Gateway auth token(Bearer) */
@@ -7910,6 +7912,76 @@ async function finishAICard(card, content, log) {
7910
7912
  }
7911
7913
 
7912
7914
  // src/bot-handler.ts
7915
+ var LONG_TASK_NOTICE_TEXT = "\u4EFB\u52A1\u5904\u7406\u65F6\u95F4\u8F83\u957F\uFF0C\u8BF7\u7A0D\u7B49\uFF0C\u6211\u8FD8\u5728\u7EE7\u7EED\u5904\u7406\u3002";
7916
+ var DEFAULT_LONG_TASK_NOTICE_DELAY_MS = 3e4;
7917
+ function startLongTaskNoticeTimer(params) {
7918
+ const { delayMs, logger, sendNotice } = params;
7919
+ let completed = false;
7920
+ let timer = null;
7921
+ const clear = () => {
7922
+ if (!timer) return;
7923
+ clearTimeout(timer);
7924
+ timer = null;
7925
+ };
7926
+ if (delayMs > 0) {
7927
+ timer = setTimeout(() => {
7928
+ if (completed) return;
7929
+ completed = true;
7930
+ timer = null;
7931
+ void sendNotice().catch((err) => {
7932
+ logger.warn(`send long-task notice failed: ${String(err)}`);
7933
+ });
7934
+ }, delayMs);
7935
+ timer.unref?.();
7936
+ } else {
7937
+ completed = true;
7938
+ }
7939
+ return {
7940
+ markReplyDelivered: () => {
7941
+ if (completed) return;
7942
+ completed = true;
7943
+ clear();
7944
+ },
7945
+ dispose: () => {
7946
+ completed = true;
7947
+ clear();
7948
+ }
7949
+ };
7950
+ }
7951
+ var pendingLongTaskNotices = /* @__PURE__ */ new Map();
7952
+ function armLongTaskNoticeForSession(params) {
7953
+ const { sessionKey, delayMs, logger, sendNotice } = params;
7954
+ pendingLongTaskNotices.get(sessionKey)?.dispose();
7955
+ logger.debug?.(`[long-task] armed sessionKey=${sessionKey} delayMs=${delayMs}`);
7956
+ const controller = startLongTaskNoticeTimer({
7957
+ delayMs,
7958
+ logger,
7959
+ sendNotice: async () => {
7960
+ pendingLongTaskNotices.delete(sessionKey);
7961
+ logger.debug?.(`[long-task] firing sessionKey=${sessionKey}`);
7962
+ await sendNotice();
7963
+ }
7964
+ });
7965
+ const wrapped = {
7966
+ markReplyDelivered: () => {
7967
+ const active = pendingLongTaskNotices.get(sessionKey);
7968
+ if (active !== wrapped) return;
7969
+ pendingLongTaskNotices.delete(sessionKey);
7970
+ logger.debug?.(`[long-task] cleared sessionKey=${sessionKey} reason=reply-delivered`);
7971
+ controller.markReplyDelivered();
7972
+ },
7973
+ dispose: () => {
7974
+ const active = pendingLongTaskNotices.get(sessionKey);
7975
+ if (active === wrapped) {
7976
+ pendingLongTaskNotices.delete(sessionKey);
7977
+ logger.debug?.(`[long-task] cleared sessionKey=${sessionKey} reason=disposed`);
7978
+ }
7979
+ controller.dispose();
7980
+ }
7981
+ };
7982
+ pendingLongTaskNotices.set(sessionKey, wrapped);
7983
+ return wrapped;
7984
+ }
7913
7985
  function buildGatewayUserContent(inboundCtx, logger) {
7914
7986
  const base = inboundCtx.CommandBody ?? inboundCtx.Body ?? "";
7915
7987
  const { base: baseText, prompt } = splitCronHiddenPrompt(base);
@@ -8653,202 +8725,226 @@ async function handleDingtalkMessage(params) {
8653
8725
  logger.warn("AI Card creation failed, falling back to normal message");
8654
8726
  }
8655
8727
  }
8656
- const textApi = coreChannel?.text;
8657
- const textChunkLimitResolved = textApi?.resolveTextChunkLimit?.(
8658
- {
8659
- cfg,
8660
- channel: "dingtalk",
8661
- defaultLimit: dingtalkCfgResolved.textChunkLimit ?? 4e3
8728
+ const targetId = isGroup ? ctx.conversationId : ctx.senderId;
8729
+ const chatType = isGroup ? "group" : "direct";
8730
+ const routeSessionKeyRaw = route.sessionKey;
8731
+ const routeSessionKey = typeof routeSessionKeyRaw === "string" && routeSessionKeyRaw.trim() ? routeSessionKeyRaw : `dingtalk:${chatType}:${targetId}`;
8732
+ const longTaskNotice = armLongTaskNoticeForSession({
8733
+ sessionKey: routeSessionKey,
8734
+ delayMs: dingtalkCfgResolved.longTaskNoticeDelayMs ?? DEFAULT_LONG_TASK_NOTICE_DELAY_MS,
8735
+ logger,
8736
+ sendNotice: async () => {
8737
+ await sendMessageDingtalk({
8738
+ cfg: dingtalkCfgResolved,
8739
+ to: targetId,
8740
+ text: LONG_TASK_NOTICE_TEXT,
8741
+ chatType
8742
+ });
8662
8743
  }
8663
- ) ?? (dingtalkCfgResolved.textChunkLimit ?? 4e3);
8664
- const chunkMode = textApi?.resolveChunkMode?.(cfg, "dingtalk");
8665
- const tableMode = "bullets";
8666
- const deliver = async (payload, info) => {
8667
- logger.debug(
8668
- `[reply] meta=${JSON.stringify({
8669
- ...resolvedTargetMeta,
8670
- kind: info?.kind ?? "unknown",
8671
- hasText: typeof payload.text === "string",
8672
- mediaCount: Array.isArray(payload.mediaUrls) ? payload.mediaUrls.length : payload.mediaUrl ? 1 : 0
8673
- })}`
8674
- );
8675
- const targetId = isGroup ? ctx.conversationId : ctx.senderId;
8676
- const chatType = isGroup ? "group" : "direct";
8677
- let sent = false;
8678
- const sendMediaWithFallback = async (mediaUrl) => {
8679
- try {
8680
- await sendMediaDingtalk({
8681
- cfg: dingtalkCfgResolved,
8682
- to: targetId,
8683
- mediaUrl,
8684
- chatType
8685
- });
8686
- sent = true;
8687
- } catch (err) {
8688
- logger.error(
8689
- `[reply] sendMediaDingtalk failed (target=${JSON.stringify(resolvedTargetMeta)}): ${String(err)}`
8690
- );
8691
- const fallbackText = `\u{1F4CE} ${mediaUrl}`;
8692
- await sendMessageDingtalk({
8693
- cfg: dingtalkCfgResolved,
8694
- to: targetId,
8695
- text: fallbackText,
8696
- chatType
8697
- });
8698
- sent = true;
8744
+ });
8745
+ try {
8746
+ const textApi = coreChannel?.text;
8747
+ const textChunkLimitResolved = textApi?.resolveTextChunkLimit?.(
8748
+ {
8749
+ cfg,
8750
+ channel: "dingtalk",
8751
+ defaultLimit: dingtalkCfgResolved.textChunkLimit ?? 4e3
8699
8752
  }
8753
+ ) ?? (dingtalkCfgResolved.textChunkLimit ?? 4e3);
8754
+ const chunkMode = textApi?.resolveChunkMode?.(cfg, "dingtalk");
8755
+ const tableMode = "bullets";
8756
+ const deliver = async (payload, info) => {
8757
+ logger.debug(
8758
+ `[reply] meta=${JSON.stringify({
8759
+ ...resolvedTargetMeta,
8760
+ kind: info?.kind ?? "unknown",
8761
+ hasText: typeof payload.text === "string",
8762
+ mediaCount: Array.isArray(payload.mediaUrls) ? payload.mediaUrls.length : payload.mediaUrl ? 1 : 0
8763
+ })}`
8764
+ );
8765
+ let sent = false;
8766
+ const sendMediaWithFallback = async (mediaUrl) => {
8767
+ try {
8768
+ await sendMediaDingtalk({
8769
+ cfg: dingtalkCfgResolved,
8770
+ to: targetId,
8771
+ mediaUrl,
8772
+ chatType
8773
+ });
8774
+ sent = true;
8775
+ longTaskNotice.markReplyDelivered();
8776
+ } catch (err) {
8777
+ logger.error(
8778
+ `[reply] sendMediaDingtalk failed (target=${JSON.stringify(resolvedTargetMeta)}): ${String(err)}`
8779
+ );
8780
+ const fallbackText = `\u{1F4CE} ${mediaUrl}`;
8781
+ await sendMessageDingtalk({
8782
+ cfg: dingtalkCfgResolved,
8783
+ to: targetId,
8784
+ text: fallbackText,
8785
+ chatType
8786
+ });
8787
+ sent = true;
8788
+ longTaskNotice.markReplyDelivered();
8789
+ }
8790
+ };
8791
+ const payloadMediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
8792
+ const rawText = payload.text ?? "";
8793
+ const { mediaUrls: mediaFromLines } = extractMediaLinesFromText({
8794
+ text: rawText,
8795
+ logger
8796
+ });
8797
+ const { mediaUrls: localMediaFromText } = extractLocalMediaFromText({
8798
+ text: rawText,
8799
+ logger
8800
+ });
8801
+ const mediaQueue = [];
8802
+ const seenMedia = /* @__PURE__ */ new Set();
8803
+ const addMedia = (value) => {
8804
+ const trimmed = value?.trim();
8805
+ if (!trimmed) return;
8806
+ if (seenMedia.has(trimmed)) return;
8807
+ seenMedia.add(trimmed);
8808
+ mediaQueue.push(trimmed);
8809
+ };
8810
+ for (const url of payloadMediaUrls) addMedia(url);
8811
+ for (const url of mediaFromLines) addMedia(url);
8812
+ for (const url of localMediaFromText) addMedia(url);
8813
+ const converted = textApi?.convertMarkdownTables?.(
8814
+ rawText,
8815
+ tableMode
8816
+ ) ?? rawText;
8817
+ const hasText = converted.trim().length > 0;
8818
+ if (hasText) {
8819
+ const chunks = textApi?.chunkTextWithMode && typeof textChunkLimitResolved === "number" && textChunkLimitResolved > 0 ? textApi.chunkTextWithMode(converted, textChunkLimitResolved, chunkMode) : [converted];
8820
+ for (const chunk of chunks) {
8821
+ await sendMessageDingtalk({
8822
+ cfg: dingtalkCfgResolved,
8823
+ to: targetId,
8824
+ text: chunk,
8825
+ chatType
8826
+ });
8827
+ sent = true;
8828
+ longTaskNotice.markReplyDelivered();
8829
+ }
8830
+ }
8831
+ for (const mediaUrl of mediaQueue) {
8832
+ await sendMediaWithFallback(mediaUrl);
8833
+ }
8834
+ if (!hasText && mediaQueue.length === 0) {
8835
+ return false;
8836
+ }
8837
+ return sent;
8700
8838
  };
8701
- const payloadMediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
8702
- const rawText = payload.text ?? "";
8703
- const { mediaUrls: mediaFromLines } = extractMediaLinesFromText({
8704
- text: rawText,
8705
- logger
8706
- });
8707
- const { mediaUrls: localMediaFromText } = extractLocalMediaFromText({
8708
- text: rawText,
8709
- logger
8710
- });
8711
- const mediaQueue = [];
8712
- const seenMedia = /* @__PURE__ */ new Set();
8713
- const addMedia = (value) => {
8714
- const trimmed = value?.trim();
8715
- if (!trimmed) return;
8716
- if (seenMedia.has(trimmed)) return;
8717
- seenMedia.add(trimmed);
8718
- mediaQueue.push(trimmed);
8719
- };
8720
- for (const url of payloadMediaUrls) addMedia(url);
8721
- for (const url of mediaFromLines) addMedia(url);
8722
- for (const url of localMediaFromText) addMedia(url);
8723
- const converted = textApi?.convertMarkdownTables?.(
8724
- rawText,
8725
- tableMode
8726
- ) ?? rawText;
8727
- const hasText = converted.trim().length > 0;
8728
- if (hasText) {
8729
- const chunks = textApi?.chunkTextWithMode && typeof textChunkLimitResolved === "number" && textChunkLimitResolved > 0 ? textApi.chunkTextWithMode(converted, textChunkLimitResolved, chunkMode) : [converted];
8730
- for (const chunk of chunks) {
8839
+ const humanDelay = replyApi?.resolveHumanDelayConfig?.(
8840
+ cfg,
8841
+ route?.agentId
8842
+ );
8843
+ const createDispatcherWithTyping = replyApi?.createReplyDispatcherWithTyping;
8844
+ const createDispatcher = replyApi?.createReplyDispatcher;
8845
+ const dispatchReplyWithDispatcher = replyApi?.dispatchReplyWithDispatcher;
8846
+ if (dispatchReplyWithDispatcher) {
8847
+ logger.debug(
8848
+ `[dispatch] direct=${JSON.stringify({
8849
+ sessionKey: route?.sessionKey,
8850
+ ...resolvedTargetMeta
8851
+ })}`
8852
+ );
8853
+ const deliveryState = { delivered: false, skippedNonSilent: 0 };
8854
+ const result2 = await dispatchReplyWithDispatcher({
8855
+ ctx: finalCtx,
8856
+ cfg,
8857
+ dispatcherOptions: {
8858
+ deliver: async (payload, info) => {
8859
+ const didSend = await deliver(
8860
+ payload,
8861
+ info
8862
+ );
8863
+ if (didSend) {
8864
+ deliveryState.delivered = true;
8865
+ }
8866
+ },
8867
+ humanDelay,
8868
+ onSkip: (_payload, info) => {
8869
+ if (info.reason !== "silent") {
8870
+ deliveryState.skippedNonSilent += 1;
8871
+ }
8872
+ },
8873
+ onError: (err, info) => {
8874
+ logger.error(`${info.kind} reply failed: ${String(err)}`);
8875
+ }
8876
+ }
8877
+ });
8878
+ if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) {
8731
8879
  await sendMessageDingtalk({
8732
8880
  cfg: dingtalkCfgResolved,
8733
8881
  to: targetId,
8734
- text: chunk,
8882
+ text: "No response generated. Please try again.",
8735
8883
  chatType
8736
8884
  });
8737
- sent = true;
8885
+ longTaskNotice.markReplyDelivered();
8738
8886
  }
8887
+ const counts2 = result2?.counts;
8888
+ const queuedFinal2 = result2?.queuedFinal;
8889
+ logger.debug(
8890
+ `dispatch complete (queuedFinal=${typeof queuedFinal2 === "boolean" ? queuedFinal2 : "unknown"}, replies=${counts2?.final ?? 0})`
8891
+ );
8892
+ return;
8739
8893
  }
8740
- for (const mediaUrl of mediaQueue) {
8741
- await sendMediaWithFallback(mediaUrl);
8742
- }
8743
- if (!hasText && mediaQueue.length === 0) {
8744
- return false;
8894
+ const dispatcherResult = createDispatcherWithTyping ? createDispatcherWithTyping({
8895
+ deliver: async (payload, info) => {
8896
+ await deliver(payload, info);
8897
+ },
8898
+ humanDelay,
8899
+ onError: (err, info) => {
8900
+ logger.error(`${info.kind} reply failed: ${String(err)}`);
8901
+ }
8902
+ }) : {
8903
+ dispatcher: createDispatcher?.({
8904
+ deliver: async (payload, info) => {
8905
+ await deliver(payload, info);
8906
+ },
8907
+ humanDelay,
8908
+ onError: (err, info) => {
8909
+ logger.error(`${info.kind} reply failed: ${String(err)}`);
8910
+ }
8911
+ }),
8912
+ replyOptions: {},
8913
+ markDispatchIdle: () => void 0
8914
+ };
8915
+ const dispatcher = dispatcherResult?.dispatcher;
8916
+ if (!dispatcher) {
8917
+ logger.debug("dispatcher not available, skipping dispatch");
8918
+ return;
8745
8919
  }
8746
- return sent;
8747
- };
8748
- const humanDelay = replyApi?.resolveHumanDelayConfig?.(
8749
- cfg,
8750
- route?.agentId
8751
- );
8752
- const createDispatcherWithTyping = replyApi?.createReplyDispatcherWithTyping;
8753
- const createDispatcher = replyApi?.createReplyDispatcher;
8754
- const dispatchReplyWithBufferedBlockDispatcher = replyApi?.dispatchReplyWithBufferedBlockDispatcher;
8755
- if (dispatchReplyWithBufferedBlockDispatcher) {
8756
8920
  logger.debug(
8757
- `[dispatch] buffered=${JSON.stringify({
8921
+ `[dispatch] legacy=${JSON.stringify({
8758
8922
  sessionKey: route?.sessionKey,
8759
8923
  ...resolvedTargetMeta
8760
8924
  })}`
8761
8925
  );
8762
- const deliveryState = { delivered: false, skippedNonSilent: 0 };
8763
- const result2 = await dispatchReplyWithBufferedBlockDispatcher({
8926
+ const dispatchReplyFromConfig = replyApi?.dispatchReplyFromConfig;
8927
+ if (!dispatchReplyFromConfig) {
8928
+ logger.debug("dispatchReplyFromConfig not available");
8929
+ return;
8930
+ }
8931
+ const result = await dispatchReplyFromConfig({
8764
8932
  ctx: finalCtx,
8765
8933
  cfg,
8766
- dispatcherOptions: {
8767
- deliver: async (payload, info) => {
8768
- const didSend = await deliver(
8769
- payload,
8770
- info
8771
- );
8772
- if (didSend) {
8773
- deliveryState.delivered = true;
8774
- }
8775
- },
8776
- humanDelay,
8777
- onSkip: (_payload, info) => {
8778
- if (info.reason !== "silent") {
8779
- deliveryState.skippedNonSilent += 1;
8780
- }
8781
- },
8782
- onError: (err, info) => {
8783
- logger.error(`${info.kind} reply failed: ${String(err)}`);
8784
- }
8785
- }
8934
+ dispatcher,
8935
+ replyOptions: dispatcherResult?.replyOptions ?? {}
8786
8936
  });
8787
- if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) {
8788
- await sendMessageDingtalk({
8789
- cfg: dingtalkCfgResolved,
8790
- to: isGroup ? ctx.conversationId : ctx.senderId,
8791
- text: "No response generated. Please try again.",
8792
- chatType: isGroup ? "group" : "direct"
8793
- });
8794
- }
8795
- const counts2 = result2?.counts;
8796
- const queuedFinal2 = result2?.queuedFinal;
8937
+ const markDispatchIdle = dispatcherResult?.markDispatchIdle;
8938
+ markDispatchIdle?.();
8939
+ const counts = result?.counts;
8940
+ const queuedFinal = result?.queuedFinal;
8797
8941
  logger.debug(
8798
- `dispatch complete (queuedFinal=${typeof queuedFinal2 === "boolean" ? queuedFinal2 : "unknown"}, replies=${counts2?.final ?? 0})`
8942
+ `dispatch complete (queuedFinal=${typeof queuedFinal === "boolean" ? queuedFinal : "unknown"}, replies=${counts?.final ?? 0})`
8799
8943
  );
8800
- return;
8801
- }
8802
- const dispatcherResult = createDispatcherWithTyping ? createDispatcherWithTyping({
8803
- deliver: async (payload, info) => {
8804
- await deliver(payload, info);
8805
- },
8806
- humanDelay,
8807
- onError: (err, info) => {
8808
- logger.error(`${info.kind} reply failed: ${String(err)}`);
8809
- }
8810
- }) : {
8811
- dispatcher: createDispatcher?.({
8812
- deliver: async (payload, info) => {
8813
- await deliver(payload, info);
8814
- },
8815
- humanDelay,
8816
- onError: (err, info) => {
8817
- logger.error(`${info.kind} reply failed: ${String(err)}`);
8818
- }
8819
- }),
8820
- replyOptions: {},
8821
- markDispatchIdle: () => void 0
8822
- };
8823
- const dispatcher = dispatcherResult?.dispatcher;
8824
- if (!dispatcher) {
8825
- logger.debug("dispatcher not available, skipping dispatch");
8826
- return;
8827
- }
8828
- logger.debug(
8829
- `[dispatch] standard=${JSON.stringify({
8830
- sessionKey: route?.sessionKey,
8831
- ...resolvedTargetMeta
8832
- })}`
8833
- );
8834
- const dispatchReplyFromConfig = replyApi?.dispatchReplyFromConfig;
8835
- if (!dispatchReplyFromConfig) {
8836
- logger.debug("dispatchReplyFromConfig not available");
8837
- return;
8944
+ } catch (err) {
8945
+ longTaskNotice.dispose();
8946
+ throw err;
8838
8947
  }
8839
- const result = await dispatchReplyFromConfig({
8840
- ctx: finalCtx,
8841
- cfg,
8842
- dispatcher,
8843
- replyOptions: dispatcherResult?.replyOptions ?? {}
8844
- });
8845
- const markDispatchIdle = dispatcherResult?.markDispatchIdle;
8846
- markDispatchIdle?.();
8847
- const counts = result?.counts;
8848
- const queuedFinal = result?.queuedFinal;
8849
- logger.debug(
8850
- `dispatch complete (queuedFinal=${typeof queuedFinal === "boolean" ? queuedFinal : "unknown"}, replies=${counts?.final ?? 0})`
8851
- );
8852
8948
  } catch (err) {
8853
8949
  logger.error(
8854
8950
  `failed to dispatch message (target=${JSON.stringify(inboundTargetMeta)}): ${String(err)}`
@@ -9681,6 +9777,7 @@ var dingtalkPlugin = {
9681
9777
  groupAllowFrom: { type: "array", items: { type: "string" } },
9682
9778
  historyLimit: { type: "integer", minimum: 0 },
9683
9779
  textChunkLimit: { type: "integer", minimum: 1 },
9780
+ longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
9684
9781
  enableAICard: { type: "boolean" },
9685
9782
  gatewayToken: { type: "string" },
9686
9783
  gatewayPassword: { type: "string" },
@@ -9861,7 +9958,8 @@ var plugin = {
9861
9958
  allowFrom: { type: "array", items: { type: "string" } },
9862
9959
  groupAllowFrom: { type: "array", items: { type: "string" } },
9863
9960
  historyLimit: { type: "integer", minimum: 0 },
9864
- textChunkLimit: { type: "integer", minimum: 1 }
9961
+ textChunkLimit: { type: "integer", minimum: 1 },
9962
+ longTaskNoticeDelayMs: { type: "integer", minimum: 0 }
9865
9963
  }
9866
9964
  },
9867
9965
  /**