@soimy/dingtalk 3.2.0 → 3.3.0

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/src/types.ts CHANGED
@@ -17,6 +17,7 @@ import type {
17
17
  ChannelGatewayContext as SDKChannelGatewayContext,
18
18
  ChannelPlugin as SDKChannelPlugin,
19
19
  } from "openclaw/plugin-sdk";
20
+ import { mergeAccountWithDefaults } from "./config";
20
21
 
21
22
  export interface DingtalkPluginModule {
22
23
  id: string;
@@ -41,7 +42,8 @@ export interface DingTalkConfig extends OpenClawConfig {
41
42
  groupPolicy?: "open" | "allowlist";
42
43
  allowFrom?: string[];
43
44
  mediaUrlAllowlist?: string[];
44
- showThinking?: boolean;
45
+ journalTTLDays?: number;
46
+ ackReaction?: string;
45
47
  debug?: boolean;
46
48
  messageType?: "markdown" | "card";
47
49
  cardTemplateId?: string;
@@ -55,14 +57,36 @@ export interface DingTalkConfig extends OpenClawConfig {
55
57
  reconnectJitter?: number;
56
58
  /** Maximum number of runtime reconnect cycles before giving up (default: 10) */
57
59
  maxReconnectCycles?: number;
60
+ /** Maximum time (ms) for a single reconnect cycle before giving up and starting a new cycle (default: 50000) */
61
+ reconnectDeadlineMs?: number;
58
62
  /** Whether to use ConnectionManager; when false, use DWClient native keepAlive+autoReconnect */
59
63
  useConnectionManager?: boolean;
60
64
  /** Maximum inbound media file size in MB (overrides runtime default when set) */
61
65
  mediaMaxMb?: number;
66
+ /** Whether to enable underlying stream keepAlive heartbeat; defaults to !useConnectionManager when omitted */
67
+ keepAlive?: boolean;
68
+ /** Bypass system/global HTTP(S) proxy for DingTalk outbound send/card/upload APIs */
69
+ bypassProxyForSend?: boolean;
62
70
  proactivePermissionHint?: {
63
71
  enabled?: boolean;
64
72
  cooldownHours?: number;
65
73
  };
74
+ /** Enable real-time card streaming (default false, true = 300ms throttled per-token updates) */
75
+ cardRealTimeStream?: boolean;
76
+ /** AICard degrade duration in milliseconds after trigger errors (default 30m) */
77
+ aicardDegradeMs?: number;
78
+ /** Enable local learning loop (events/reflections/session notes/global rules) */
79
+ learningEnabled?: boolean;
80
+ /** Auto-apply generated reflections into session notes/global rules (default false) */
81
+ learningAutoApply?: boolean;
82
+ /** Session learning note TTL in milliseconds (default 6h) */
83
+ learningNoteTtlMs?: number;
84
+ /** @deprecated Use learningEnabled */
85
+ feedbackLearningEnabled?: boolean;
86
+ /** @deprecated Use learningAutoApply */
87
+ feedbackLearningAutoApply?: boolean;
88
+ /** @deprecated Use learningNoteTtlMs */
89
+ feedbackLearningNoteTtlMs?: number;
66
90
  }
67
91
 
68
92
  /**
@@ -80,7 +104,8 @@ export interface DingTalkChannelConfig {
80
104
  groupPolicy?: "open" | "allowlist";
81
105
  allowFrom?: string[];
82
106
  mediaUrlAllowlist?: string[];
83
- showThinking?: boolean;
107
+ journalTTLDays?: number;
108
+ ackReaction?: string;
84
109
  debug?: boolean;
85
110
  messageType?: "markdown" | "card";
86
111
  cardTemplateId?: string;
@@ -93,14 +118,36 @@ export interface DingTalkChannelConfig {
93
118
  reconnectJitter?: number;
94
119
  /** Maximum number of runtime reconnect cycles before giving up (default: 10) */
95
120
  maxReconnectCycles?: number;
121
+ /** Maximum time (ms) for a single reconnect cycle before giving up and starting a new cycle (default: 50000) */
122
+ reconnectDeadlineMs?: number;
96
123
  /** Whether to use ConnectionManager; when false, use DWClient native keepAlive+autoReconnect */
97
124
  useConnectionManager?: boolean;
98
125
  /** Maximum inbound media file size in MB (overrides runtime default when set) */
99
126
  mediaMaxMb?: number;
127
+ /** Whether to enable underlying stream keepAlive heartbeat; defaults to !useConnectionManager when omitted */
128
+ keepAlive?: boolean;
129
+ /** Bypass system/global HTTP(S) proxy for DingTalk outbound send/card/upload APIs */
130
+ bypassProxyForSend?: boolean;
100
131
  proactivePermissionHint?: {
101
132
  enabled?: boolean;
102
133
  cooldownHours?: number;
103
134
  };
135
+ /** Enable real-time card streaming (default false, true = 300ms throttled per-token updates) */
136
+ cardRealTimeStream?: boolean;
137
+ /** AICard degrade duration in milliseconds after trigger errors (default 30m) */
138
+ aicardDegradeMs?: number;
139
+ /** Enable local learning loop (events/reflections/session notes/global rules) */
140
+ learningEnabled?: boolean;
141
+ /** Auto-apply generated reflections into session notes/global rules (default false) */
142
+ learningAutoApply?: boolean;
143
+ /** Session learning note TTL in milliseconds (default 6h) */
144
+ learningNoteTtlMs?: number;
145
+ /** @deprecated Use learningEnabled */
146
+ feedbackLearningEnabled?: boolean;
147
+ /** @deprecated Use learningAutoApply */
148
+ feedbackLearningAutoApply?: boolean;
149
+ /** @deprecated Use learningNoteTtlMs */
150
+ feedbackLearningNoteTtlMs?: number;
104
151
  }
105
152
 
106
153
  /**
@@ -145,6 +192,14 @@ export interface MediaFile {
145
192
  mimeType: string;
146
193
  }
147
194
 
195
+ export interface DocInfo {
196
+ docId: string;
197
+ title: string;
198
+ docType: string;
199
+ creatorId?: string;
200
+ updatedAt?: number | string;
201
+ }
202
+
148
203
  /**
149
204
  * DingTalk incoming message (Stream mode)
150
205
  */
@@ -156,15 +211,22 @@ export interface DingTalkInboundMessage {
156
211
  content: string;
157
212
  isReplyMsg?: boolean; // 是否是回复消息
158
213
  repliedMsg?: {
159
- // 被回复的消息
214
+ msgType?: string;
215
+ msgId?: string;
216
+ senderId?: string;
217
+ createdAt?: number;
160
218
  content?: {
161
219
  text?: string;
220
+ downloadCode?: string;
221
+ biz_custom_action_url?: string;
162
222
  richText?: Array<{
163
223
  msgType?: string;
164
224
  type?: string;
165
225
  content?: string;
226
+ text?: string;
166
227
  code?: string;
167
228
  atName?: string;
229
+ downloadCode?: string;
168
230
  }>;
169
231
  };
170
232
  };
@@ -173,13 +235,16 @@ export interface DingTalkInboundMessage {
173
235
  downloadCode?: string;
174
236
  fileName?: string;
175
237
  recognition?: string;
238
+ spaceId?: string;
239
+ fileId?: string;
240
+ biz_custom_action_url?: string;
176
241
  richText?: Array<{
177
242
  type: string;
178
243
  text?: string;
179
244
  atName?: string;
180
- downloadCode?: string; // For picture type in richText
245
+ downloadCode?: string;
181
246
  }>;
182
- quoteContent?: string; // 替代引用格式
247
+ quoteContent?: string;
183
248
  };
184
249
  // Legacy 引用格式
185
250
  quoteMessage?: {
@@ -191,6 +256,7 @@ export interface DingTalkInboundMessage {
191
256
  };
192
257
  // 富媒体引用,仅有消息ID的情况(包括手机端和PC端)
193
258
  originalMsgId?: string;
259
+ originalProcessQueryKey?: string;
194
260
  conversationType: string;
195
261
  conversationId: string;
196
262
  conversationTitle?: string;
@@ -201,14 +267,39 @@ export interface DingTalkInboundMessage {
201
267
  sessionWebhook: string;
202
268
  }
203
269
 
270
+ /**
271
+ * Quoted/reply message metadata extracted from repliedMsg.
272
+ * Populated when isReplyMsg is true; downstream handlers use these fields
273
+ * to download quoted media or look up cached card content.
274
+ */
275
+ export interface QuotedInfo {
276
+ prefix: string;
277
+ mediaDownloadCode?: string;
278
+ mediaType?: string;
279
+ isQuotedFile?: boolean;
280
+ isQuotedCard?: boolean;
281
+ isQuotedDocCard?: boolean;
282
+ docSpaceId?: string;
283
+ docFileId?: string;
284
+ cardCreatedAt?: number;
285
+ processQueryKey?: string;
286
+ fileCreatedAt?: number;
287
+ msgId?: string;
288
+ }
289
+
204
290
  /**
205
291
  * Extracted message content for unified processing
206
292
  */
207
293
  export interface MessageContent {
208
294
  text: string;
209
295
  mediaPath?: string;
296
+ mediaPaths?: string[];
210
297
  mediaType?: string;
298
+ mediaTypes?: string[];
211
299
  messageType: string;
300
+ docSpaceId?: string;
301
+ docFileId?: string;
302
+ quoted?: QuotedInfo;
212
303
  }
213
304
 
214
305
  /**
@@ -218,14 +309,21 @@ export interface SendMessageOptions {
218
309
  title?: string;
219
310
  useMarkdown?: boolean;
220
311
  atUserId?: string | null;
221
- log?: any;
312
+ log?: Logger;
313
+ conversationId?: string;
222
314
  mediaPath?: string;
223
315
  filePath?: string;
224
316
  mediaUrl?: string;
225
317
  mediaType?: "image" | "voice" | "video" | "file";
226
318
  accountId?: string;
227
- cardUpdateMode?: "replace" | "append" | "finalize";
228
- cardFinalize?: boolean;
319
+ storePath?: string;
320
+ cardUpdateMode?: "append";
321
+ }
322
+
323
+ export interface DingTalkTrackingMetadata {
324
+ processQueryKey?: string;
325
+ outTrackId?: string;
326
+ cardInstanceId?: string;
229
327
  }
230
328
 
231
329
  /**
@@ -254,7 +352,7 @@ export interface HandleDingTalkMessageParams {
254
352
  accountId: string;
255
353
  data: DingTalkInboundMessage;
256
354
  sessionWebhook: string;
257
- log?: any;
355
+ log?: Logger;
258
356
  dingtalkConfig: DingTalkConfig;
259
357
  }
260
358
 
@@ -295,7 +393,7 @@ export interface ResolvedAccount {
295
393
  export interface AxiosRequestConfig {
296
394
  url?: string;
297
395
  method?: string;
298
- data?: any;
396
+ data?: unknown;
299
397
  headers?: Record<string, string>;
300
398
  responseType?: "arraybuffer" | "json" | "text";
301
399
  }
@@ -303,7 +401,7 @@ export interface AxiosRequestConfig {
303
401
  /**
304
402
  * HTTP response from axios
305
403
  */
306
- export interface AxiosResponse<T = any> {
404
+ export interface AxiosResponse<T = unknown> {
307
405
  data: T;
308
406
  status: number;
309
407
  statusText: string;
@@ -325,15 +423,15 @@ export interface StreamCallbackResponse {
325
423
  */
326
424
  export interface ReplyDispatchContext {
327
425
  responsePrefix?: string;
328
- deliver: (payload: any) => Promise<{ ok: boolean; error?: string }>;
426
+ deliver: (payload: unknown) => Promise<{ ok: boolean; error?: string }>;
329
427
  }
330
428
 
331
429
  /**
332
430
  * Reply dispatcher result
333
431
  */
334
432
  export interface ReplyDispatcherResult {
335
- dispatcher: any;
336
- replyOptions: any;
433
+ dispatcher: unknown;
434
+ replyOptions: unknown;
337
435
  markDispatchIdle: () => void;
338
436
  }
339
437
 
@@ -343,7 +441,7 @@ export interface ReplyDispatcherResult {
343
441
  export interface RetryOptions {
344
442
  maxRetries?: number;
345
443
  baseDelayMs?: number;
346
- log?: any;
444
+ log?: Logger;
347
445
  }
348
446
 
349
447
  /**
@@ -397,7 +495,7 @@ export interface TargetResolutionResult {
397
495
  */
398
496
  export interface ResolveTargetParams {
399
497
  to?: string | null;
400
- [key: string]: any;
498
+ [key: string]: unknown;
401
499
  }
402
500
 
403
501
  /**
@@ -408,7 +506,7 @@ export interface SendTextParams {
408
506
  to: string;
409
507
  text: string;
410
508
  accountId?: string;
411
- [key: string]: any;
509
+ [key: string]: unknown;
412
510
  }
413
511
 
414
512
  /**
@@ -419,7 +517,7 @@ export interface SendMediaParams {
419
517
  to: string;
420
518
  mediaPath: string;
421
519
  accountId?: string;
422
- [key: string]: any;
520
+ [key: string]: unknown;
423
521
  }
424
522
 
425
523
  /**
@@ -428,8 +526,8 @@ export interface SendMediaParams {
428
526
  export interface DingTalkOutboundHandler {
429
527
  deliveryMode: "direct" | "queued" | "batch";
430
528
  resolveTarget: (params: ResolveTargetParams) => TargetResolutionResult;
431
- sendText: (params: SendTextParams) => Promise<{ ok: boolean; data?: any; error?: any }>;
432
- sendMedia?: (params: SendMediaParams) => Promise<{ ok: boolean; data?: any; error?: any }>;
529
+ sendText: (params: SendTextParams) => Promise<{ ok: boolean; data?: unknown; error?: unknown }>;
530
+ sendMedia?: (params: SendMediaParams) => Promise<{ ok: boolean; data?: unknown; error?: unknown }>;
433
531
  }
434
532
 
435
533
  /**
@@ -452,13 +550,17 @@ export type AICardState = (typeof AICardStatus)[keyof typeof AICardStatus];
452
550
  */
453
551
  export interface AICardInstance {
454
552
  cardInstanceId: string;
553
+ processQueryKey?: string;
455
554
  accessToken: string;
456
555
  conversationId: string;
556
+ accountId?: string;
557
+ storePath?: string;
457
558
  createdAt: number;
458
559
  lastUpdated: number;
459
560
  state: AICardState; // Current card state: PROCESSING, INPUTING, FINISHED, FAILED
460
561
  config?: DingTalkConfig; // Store config reference for token refresh
461
562
  lastStreamedContent?: string;
563
+ outTrackId?: string;
462
564
  }
463
565
 
464
566
  /**
@@ -485,6 +587,14 @@ export enum ConnectionState {
485
587
  FAILED = "FAILED",
486
588
  }
487
589
 
590
+ /**
591
+ * Factory function that creates a fresh DWClient with callback listeners
592
+ * already registered. Used by ConnectionManager to create a new client
593
+ * on reconnection so the new WebSocket can start receiving messages
594
+ * while the old zombie socket is still being cleaned up.
595
+ */
596
+ export type StreamClientFactory = () => import("dingtalk-stream").DWClient;
597
+
488
598
  /**
489
599
  * Connection manager configuration
490
600
  */
@@ -495,6 +605,8 @@ export interface ConnectionManagerConfig {
495
605
  jitter: number;
496
606
  /** Maximum number of runtime reconnect cycles before giving up (default: 10) */
497
607
  maxReconnectCycles?: number;
608
+ /** Maximum time (ms) for a single reconnect cycle before giving up and starting a new cycle (default: 50000) */
609
+ reconnectDeadlineMs?: number;
498
610
  /** Callback invoked when connection state changes */
499
611
  onStateChange?: (state: ConnectionState, error?: string) => void;
500
612
  }
@@ -568,7 +680,8 @@ export function resolveDingTalkAccount(
568
680
  dmPolicy: dingtalk?.dmPolicy,
569
681
  groupPolicy: dingtalk?.groupPolicy,
570
682
  allowFrom: dingtalk?.allowFrom,
571
- showThinking: dingtalk?.showThinking,
683
+ journalTTLDays: dingtalk?.journalTTLDays,
684
+ ackReaction: dingtalk?.ackReaction,
572
685
  debug: dingtalk?.debug,
573
686
  messageType: dingtalk?.messageType,
574
687
  cardTemplateId: dingtalk?.cardTemplateId,
@@ -580,9 +693,20 @@ export function resolveDingTalkAccount(
580
693
  maxReconnectDelay: dingtalk?.maxReconnectDelay,
581
694
  reconnectJitter: dingtalk?.reconnectJitter,
582
695
  maxReconnectCycles: dingtalk?.maxReconnectCycles,
696
+ reconnectDeadlineMs: dingtalk?.reconnectDeadlineMs,
583
697
  useConnectionManager: dingtalk?.useConnectionManager,
584
698
  mediaMaxMb: dingtalk?.mediaMaxMb,
699
+ keepAlive: dingtalk?.keepAlive,
700
+ bypassProxyForSend: dingtalk?.bypassProxyForSend,
585
701
  proactivePermissionHint: dingtalk?.proactivePermissionHint,
702
+ cardRealTimeStream: dingtalk?.cardRealTimeStream,
703
+ aicardDegradeMs: dingtalk?.aicardDegradeMs,
704
+ learningEnabled: dingtalk?.learningEnabled ?? dingtalk?.feedbackLearningEnabled,
705
+ learningAutoApply: dingtalk?.learningAutoApply ?? dingtalk?.feedbackLearningAutoApply,
706
+ learningNoteTtlMs: dingtalk?.learningNoteTtlMs ?? dingtalk?.feedbackLearningNoteTtlMs,
707
+ feedbackLearningEnabled: dingtalk?.feedbackLearningEnabled,
708
+ feedbackLearningAutoApply: dingtalk?.feedbackLearningAutoApply,
709
+ feedbackLearningNoteTtlMs: dingtalk?.feedbackLearningNoteTtlMs,
586
710
  };
587
711
  return {
588
712
  ...config,
@@ -591,13 +715,17 @@ export function resolveDingTalkAccount(
591
715
  };
592
716
  }
593
717
 
594
- // If named account, get from accounts object
718
+ // If named account, merge channel-level defaults with account-level overrides
595
719
  const accountConfig = dingtalk?.accounts?.[id];
596
720
  if (accountConfig) {
721
+ const merged = mergeAccountWithDefaults(
722
+ dingtalk as DingTalkConfig,
723
+ accountConfig,
724
+ );
597
725
  return {
598
- ...accountConfig,
726
+ ...merged,
599
727
  accountId: id,
600
- configured: Boolean(accountConfig.clientId && accountConfig.clientSecret),
728
+ configured: Boolean(merged.clientId && merged.clientSecret),
601
729
  };
602
730
  }
603
731