@soimy/dingtalk 3.0.1 → 3.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soimy/dingtalk",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "DingTalk (钉钉) channel plugin for OpenClaw",
5
5
  "keywords": [
6
6
  "bot",
@@ -221,6 +221,13 @@ export async function streamAICard(
221
221
  finished: boolean = false,
222
222
  log?: Logger,
223
223
  ): Promise<void> {
224
+ if (card.state === AICardStatus.FINISHED) {
225
+ log?.debug?.(
226
+ `[DingTalk][AICard] Skip stream update because card already finalized: outTrackId=${card.cardInstanceId}`,
227
+ );
228
+ return;
229
+ }
230
+
224
231
  // Refresh token defensively before DingTalk 2h token horizon.
225
232
  const tokenAge = Date.now() - card.createdAt;
226
233
  const tokenRefreshThreshold = 90 * 60 * 1000;
@@ -372,6 +372,13 @@ export async function handleDingTalkMessage(params: HandleDingTalkMessageParams)
372
372
 
373
373
  // Tool outputs are rendered into card stream as a separate formatted block.
374
374
  if (useCardMode && currentAICard && info?.kind === "tool") {
375
+ if (isCardInTerminalState(currentAICard.state)) {
376
+ log?.debug?.(
377
+ `[DingTalk] Skipping tool stream update because card is terminal: state=${currentAICard.state}`,
378
+ );
379
+ return;
380
+ }
381
+
375
382
  log?.info?.(
376
383
  `[DingTalk] Tool result received, streaming to AI Card: ${textToSend.slice(0, 100)}`,
377
384
  );
@@ -401,6 +408,12 @@ export async function handleDingTalkMessage(params: HandleDingTalkMessageParams)
401
408
  if (!useCardMode || !currentAICard) {
402
409
  return;
403
410
  }
411
+ if (isCardInTerminalState(currentAICard.state)) {
412
+ log?.debug?.(
413
+ `[DingTalk] Skipping thinking stream update because card is terminal: state=${currentAICard.state}`,
414
+ );
415
+ return;
416
+ }
404
417
  const thinkingText = formatContentForCard(payload.text, "thinking");
405
418
  if (!thinkingText) {
406
419
  return;
@@ -417,6 +430,13 @@ export async function handleDingTalkMessage(params: HandleDingTalkMessageParams)
417
430
  // 5) Finalize card stream if card mode is active.
418
431
  if (useCardMode && currentAICard) {
419
432
  try {
433
+ if (isCardInTerminalState(currentAICard.state)) {
434
+ log?.debug?.(
435
+ `[DingTalk] Skipping AI Card finalization because card is terminal: state=${currentAICard.state}`,
436
+ );
437
+ return;
438
+ }
439
+
420
440
  const isNonEmptyString = (value: any): boolean =>
421
441
  typeof value === "string" && value.trim().length > 0;
422
442