@soimy/dingtalk 3.6.10 → 3.7.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.
Files changed (163) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +11655 -10324
  3. package/dist/src/{ack-reaction-service.d.ts → ack-reaction/ack-reaction-service.d.ts} +1 -1
  4. package/dist/src/ack-reaction/dynamic-ack-reaction-controller.d.ts +3 -3
  5. package/dist/src/card/ask-user-question-context.d.ts +1 -1
  6. package/dist/src/card/ask-user-question-store.d.ts +1 -1
  7. package/dist/src/card/ask-user-question.d.ts +1 -1
  8. package/dist/src/card/card-action-handler.d.ts +2 -2
  9. package/dist/src/{card-draft-controller.d.ts → card/card-draft-controller.d.ts} +5 -1
  10. package/dist/src/card/card-run-registry.d.ts +2 -2
  11. package/dist/src/{card-service.d.ts → card/card-service.d.ts} +1 -1
  12. package/dist/src/card/card-stop-handler.d.ts +1 -1
  13. package/dist/src/card/card-streaming-mode.d.ts +1 -1
  14. package/dist/src/card/card-task-progress.d.ts +60 -0
  15. package/dist/src/channel.d.ts +6 -6
  16. package/dist/src/command/card-stop-command.d.ts +1 -1
  17. package/dist/src/{feedback-learning-service.d.ts → command/feedback-learning-service.d.ts} +1 -1
  18. package/dist/src/command/inbound-command-dispatch-service.d.ts +2 -2
  19. package/dist/src/{learning-command-service.d.ts → command/learning-command-service.d.ts} +1 -1
  20. package/dist/src/gateway/channel-gateway.d.ts +1 -1
  21. package/dist/src/{connection-manager.d.ts → gateway/connection-manager.d.ts} +1 -1
  22. package/dist/src/{docs-service.d.ts → gateway/docs-service.d.ts} +5 -5
  23. package/dist/src/{inbound-handler.d.ts → gateway/inbound-handler.d.ts} +1 -1
  24. package/dist/src/gateway/inbound-session-queue-dispatcher.d.ts +1 -1
  25. package/dist/src/messaging/attachment-text-extractor.d.ts +1 -1
  26. package/dist/src/messaging/btw-deliver.d.ts +1 -1
  27. package/dist/src/messaging/channel-outbound.d.ts +1 -1
  28. package/dist/src/messaging/inline-directives.d.ts +42 -0
  29. package/dist/src/{media-utils.d.ts → messaging/media-utils.d.ts} +1 -1
  30. package/dist/src/{message-context-store.d.ts → messaging/message-context-store.d.ts} +7 -1
  31. package/dist/src/{message-utils.d.ts → messaging/message-utils.d.ts} +1 -1
  32. package/dist/src/messaging/quoted-context.d.ts +2 -2
  33. package/dist/src/messaging/quoted-file-service.d.ts +1 -1
  34. package/dist/src/messaging/quoted-ref.d.ts +2 -2
  35. package/dist/src/{reply-strategy-card.d.ts → messaging/reply-strategy-card.d.ts} +1 -1
  36. package/dist/src/{reply-strategy-types.d.ts → messaging/reply-strategy-types.d.ts} +10 -1
  37. package/dist/src/{reply-strategy-with-reaction.d.ts → messaging/reply-strategy-with-reaction.d.ts} +1 -1
  38. package/dist/src/{reply-strategy.d.ts → messaging/reply-strategy.d.ts} +1 -1
  39. package/dist/src/{send-service.d.ts → messaging/send-service.d.ts} +1 -1
  40. package/dist/src/platform/channel-status.d.ts +1 -1
  41. package/dist/src/platform/config.d.ts +110 -0
  42. package/dist/src/platform/runtime-events.d.ts +67 -0
  43. package/dist/src/{types.d.ts → platform/types.d.ts} +75 -2
  44. package/dist/src/{persistence-store.d.ts → shared/persistence-store.d.ts} +1 -1
  45. package/dist/src/{utils.d.ts → shared/utils.d.ts} +1 -1
  46. package/dist/src/targeting/agent-name-matcher.d.ts +1 -1
  47. package/dist/src/targeting/agent-routing.d.ts +3 -3
  48. package/dist/src/{session-routing.d.ts → targeting/session-routing.d.ts} +1 -1
  49. package/index.ts +193 -69
  50. package/openclaw.plugin.json +158 -2
  51. package/package.json +16 -16
  52. package/src/{ack-reaction-classifier.ts → ack-reaction/ack-reaction-classifier.ts} +85 -13
  53. package/src/{ack-reaction-service.ts → ack-reaction/ack-reaction-service.ts} +13 -7
  54. package/src/ack-reaction/dynamic-ack-reaction-controller.ts +46 -31
  55. package/src/ack-reaction/dynamic-ack-reaction-progress.ts +4 -1
  56. package/src/card/ask-user-question-context.ts +1 -1
  57. package/src/card/ask-user-question-store.ts +2 -2
  58. package/src/card/ask-user-question.ts +11 -7
  59. package/src/card/card-action-handler.ts +2 -2
  60. package/src/{card-callback-service.ts → card/card-callback-service.ts} +7 -9
  61. package/src/card/card-draft-controller.ts +770 -0
  62. package/src/card/card-markdown-image-reroute.ts +8 -2
  63. package/src/card/card-run-registry.ts +17 -7
  64. package/src/{card-service.ts → card/card-service.ts} +91 -61
  65. package/src/card/card-stop-handler.ts +12 -8
  66. package/src/card/card-streaming-mode.ts +1 -1
  67. package/src/card/card-task-progress.ts +330 -0
  68. package/src/card/draft-stream-loop.ts +119 -0
  69. package/src/card/reasoning-answer-split.ts +125 -121
  70. package/src/card/reasoning-block-assembler.ts +122 -123
  71. package/src/{run-usage-store.ts → card/run-usage-store.ts} +18 -6
  72. package/src/card/statusline-renderer.ts +14 -5
  73. package/src/card/task-model-metadata.ts +8 -3
  74. package/src/channel.ts +14 -16
  75. package/src/command/card-stop-command.ts +2 -2
  76. package/src/{feedback-learning-service.ts → command/feedback-learning-service.ts} +36 -16
  77. package/src/{feedback-learning-store.ts → command/feedback-learning-store.ts} +133 -81
  78. package/src/command/inbound-command-dispatch-service.ts +12 -6
  79. package/src/{learning-command-service.ts → command/learning-command-service.ts} +36 -29
  80. package/src/{session-command-service.ts → command/session-command-service.ts} +3 -1
  81. package/src/gateway/channel-gateway.ts +106 -28
  82. package/src/{connection-manager.ts → gateway/connection-manager.ts} +45 -30
  83. package/src/{docs-service.ts → gateway/docs-service.ts} +5 -5
  84. package/src/{inbound-handler.ts → gateway/inbound-handler.ts} +144 -85
  85. package/src/gateway/inbound-session-queue-dispatcher.ts +18 -14
  86. package/src/{session-lock.ts → gateway/session-lock.ts} +14 -14
  87. package/src/messaging/attachment-text-extractor.ts +21 -7
  88. package/src/messaging/btw-deliver.ts +2 -2
  89. package/src/messaging/channel-actions.ts +15 -7
  90. package/src/messaging/channel-outbound.ts +16 -11
  91. package/src/messaging/inline-directives.ts +342 -0
  92. package/src/{media-utils.ts → messaging/media-utils.ts} +276 -72
  93. package/src/{message-context-store.ts → messaging/message-context-store.ts} +11 -2
  94. package/src/{message-utils.ts → messaging/message-utils.ts} +46 -11
  95. package/src/messaging/quoted-context.ts +2 -2
  96. package/src/messaging/quoted-file-service.ts +311 -294
  97. package/src/messaging/quoted-ref.ts +11 -7
  98. package/src/{reply-strategy-card.ts → messaging/reply-strategy-card.ts} +178 -101
  99. package/src/{reply-strategy-markdown.ts → messaging/reply-strategy-markdown.ts} +8 -2
  100. package/src/{reply-strategy-types.ts → messaging/reply-strategy-types.ts} +11 -1
  101. package/src/{reply-strategy-with-reaction.ts → messaging/reply-strategy-with-reaction.ts} +6 -1
  102. package/src/{reply-strategy.ts → messaging/reply-strategy.ts} +2 -2
  103. package/src/{send-service.ts → messaging/send-service.ts} +100 -65
  104. package/src/{auth.ts → platform/auth.ts} +2 -2
  105. package/src/platform/channel-status.ts +3 -3
  106. package/src/{config-schema.ts → platform/config-schema.ts} +81 -4
  107. package/src/{config.ts → platform/config.ts} +167 -19
  108. package/src/{device-registration.ts → platform/device-registration.ts} +5 -4
  109. package/src/{onboarding.ts → platform/onboarding.ts} +1 -4
  110. package/src/{plugin-sdk-channel-actions-augment.ts → platform/plugin-sdk-channel-actions-augment.ts} +6 -5
  111. package/src/platform/runtime-events.ts +202 -0
  112. package/src/{secret-input.ts → platform/secret-input.ts} +117 -11
  113. package/src/{types.ts → platform/types.ts} +77 -2
  114. package/src/{http-client.ts → shared/http-client.ts} +1 -3
  115. package/src/{persistence-store.ts → shared/persistence-store.ts} +6 -6
  116. package/src/{utils.ts → shared/utils.ts} +30 -13
  117. package/src/targeting/agent-name-matcher.ts +1 -1
  118. package/src/targeting/agent-routing.ts +7 -7
  119. package/src/targeting/group-members-store.ts +1 -1
  120. package/src/{session-peer-store.ts → targeting/session-peer-store.ts} +9 -4
  121. package/src/{session-routing.ts → targeting/session-routing.ts} +1 -1
  122. package/src/targeting/target-directory-adapter.ts +3 -3
  123. package/src/targeting/target-directory-store.ts +1 -1
  124. package/src/targeting/target-input.ts +1 -1
  125. package/dist/src/ack-reaction/dynamic-ack-reaction-events.d.ts +0 -35
  126. package/dist/src/config.d.ts +0 -59
  127. package/src/ack-reaction/dynamic-ack-reaction-events.ts +0 -123
  128. package/src/card-draft-controller.ts +0 -637
  129. package/src/draft-stream-loop.ts +0 -119
  130. /package/dist/src/{ack-reaction-classifier.d.ts → ack-reaction/ack-reaction-classifier.d.ts} +0 -0
  131. /package/dist/src/{card-callback-service.d.ts → card/card-callback-service.d.ts} +0 -0
  132. /package/dist/src/{draft-stream-loop.d.ts → card/draft-stream-loop.d.ts} +0 -0
  133. /package/dist/src/{run-usage-store.d.ts → card/run-usage-store.d.ts} +0 -0
  134. /package/dist/src/{feedback-learning-store.d.ts → command/feedback-learning-store.d.ts} +0 -0
  135. /package/dist/src/{session-command-service.d.ts → command/session-command-service.d.ts} +0 -0
  136. /package/dist/src/{session-lock.d.ts → gateway/session-lock.d.ts} +0 -0
  137. /package/dist/src/{proactive-risk-registry.d.ts → messaging/proactive-risk-registry.d.ts} +0 -0
  138. /package/dist/src/{reply-strategy-markdown.d.ts → messaging/reply-strategy-markdown.d.ts} +0 -0
  139. /package/dist/src/{access-control.d.ts → platform/access-control.d.ts} +0 -0
  140. /package/dist/src/{auth.d.ts → platform/auth.d.ts} +0 -0
  141. /package/dist/src/{config-schema.d.ts → platform/config-schema.d.ts} +0 -0
  142. /package/dist/src/{device-registration.d.ts → platform/device-registration.d.ts} +0 -0
  143. /package/dist/src/{logger-context.d.ts → platform/logger-context.d.ts} +0 -0
  144. /package/dist/src/{onboarding.d.ts → platform/onboarding.d.ts} +0 -0
  145. /package/dist/src/{plugin-sdk-channel-actions-augment.d.ts → platform/plugin-sdk-channel-actions-augment.d.ts} +0 -0
  146. /package/dist/src/{runtime.d.ts → platform/runtime.d.ts} +0 -0
  147. /package/dist/src/{secret-input.d.ts → platform/secret-input.d.ts} +0 -0
  148. /package/dist/src/{session-state.d.ts → platform/session-state.d.ts} +0 -0
  149. /package/dist/src/{signature.d.ts → platform/signature.d.ts} +0 -0
  150. /package/dist/src/{dedup.d.ts → shared/dedup.d.ts} +0 -0
  151. /package/dist/src/{http-client.d.ts → shared/http-client.d.ts} +0 -0
  152. /package/dist/src/{path-utils.d.ts → shared/path-utils.d.ts} +0 -0
  153. /package/dist/src/{peer-id-registry.d.ts → targeting/peer-id-registry.d.ts} +0 -0
  154. /package/dist/src/{session-peer-store.d.ts → targeting/session-peer-store.d.ts} +0 -0
  155. /package/src/{proactive-risk-registry.ts → messaging/proactive-risk-registry.ts} +0 -0
  156. /package/src/{access-control.ts → platform/access-control.ts} +0 -0
  157. /package/src/{logger-context.ts → platform/logger-context.ts} +0 -0
  158. /package/src/{runtime.ts → platform/runtime.ts} +0 -0
  159. /package/src/{session-state.ts → platform/session-state.ts} +0 -0
  160. /package/src/{signature.ts → platform/signature.ts} +0 -0
  161. /package/src/{dedup.ts → shared/dedup.ts} +0 -0
  162. /package/src/{path-utils.ts → shared/path-utils.ts} +0 -0
  163. /package/src/{peer-id-registry.ts → targeting/peer-id-registry.ts} +0 -0
@@ -10,14 +10,20 @@ export interface MarkdownImageCandidate {
10
10
  }
11
11
 
12
12
  const MARKDOWN_IMAGE_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
13
- const PRIVATE_HOST_RE = /^(?:localhost|127\.0\.0\.1|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[0-1])\.\d+\.\d+)$/;
13
+ const PRIVATE_HOST_RE =
14
+ /^(?:localhost|127\.0\.0\.1|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[0-1])\.\d+\.\d+)$/;
14
15
 
15
16
  function isLikelyPlainRelativePath(url: string): boolean {
16
17
  return !/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(url) && !url.startsWith("//");
17
18
  }
18
19
 
19
20
  function isLikelyLocalPath(url: string): boolean {
20
- return url.startsWith("./") || url.startsWith("../") || url.startsWith("/") || isLikelyPlainRelativePath(url);
21
+ return (
22
+ url.startsWith("./") ||
23
+ url.startsWith("../") ||
24
+ url.startsWith("/") ||
25
+ isLikelyPlainRelativePath(url)
26
+ );
21
27
  }
22
28
 
23
29
  function safeFileNameFromUrl(url: string): string {
@@ -1,3 +1,4 @@
1
+ import type { AICardInstance } from "../platform/types";
1
2
  /**
2
3
  * In-process card run registry for tracking active AI card runs.
3
4
  *
@@ -7,8 +8,7 @@
7
8
  * the stop button to silently fail. Ensure single-process deployment or sticky
8
9
  * routing per card callback when using the stop button feature.
9
10
  */
10
- import type { CardDraftController } from "../card-draft-controller";
11
- import type { AICardInstance } from "../types";
11
+ import type { CardDraftController } from "./card-draft-controller";
12
12
 
13
13
  export interface CardRunRecord {
14
14
  outTrackId: string;
@@ -110,10 +110,16 @@ export function resolveCardRunByConversation(
110
110
  const targetOwner = options?.ownerUserId;
111
111
  let latest: CardRunRecord | null = null;
112
112
  for (const record of records.values()) {
113
- if (record.accountId !== accountId) { continue; }
114
- if (!record.sessionKey.toLowerCase().includes(lowerCid)) { continue; }
113
+ if (record.accountId !== accountId) {
114
+ continue;
115
+ }
116
+ if (!record.sessionKey.toLowerCase().includes(lowerCid)) {
117
+ continue;
118
+ }
115
119
  // If ownerUserId filter is specified, only match runs owned by that user
116
- if (targetOwner !== undefined && record.ownerUserId !== targetOwner) { continue; }
120
+ if (targetOwner !== undefined && record.ownerUserId !== targetOwner) {
121
+ continue;
122
+ }
117
123
  if (!latest || record.registeredAt > latest.registeredAt) {
118
124
  latest = record;
119
125
  }
@@ -134,8 +140,12 @@ export function resolveCardRunByOwner(
134
140
  ): CardRunRecord | null {
135
141
  let latest: CardRunRecord | null = null;
136
142
  for (const record of records.values()) {
137
- if (record.accountId !== accountId) { continue; }
138
- if (record.ownerUserId !== ownerUserId) { continue; }
143
+ if (record.accountId !== accountId) {
144
+ continue;
145
+ }
146
+ if (record.ownerUserId !== ownerUserId) {
147
+ continue;
148
+ }
139
149
  if (!latest || record.registeredAt > latest.registeredAt) {
140
150
  latest = record;
141
151
  }
@@ -1,12 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import axios from "./http-client";
5
- import { getAccessToken } from "./auth";
6
- import { updateCardVariables } from "./card-callback-service";
7
- import { DINGTALK_CARD_TEMPLATE, STOP_ACTION_VISIBLE, STOP_ACTION_HIDDEN } from "./card/card-template";
8
- import { resolveRobotCode, stripTargetPrefix } from "./config";
9
- import { resolveOriginalPeerId } from "./peer-id-registry";
10
4
  import {
11
5
  createSyntheticOutboundMsgId,
12
6
  clearMessageContextCacheForTest,
@@ -16,12 +10,9 @@ import {
16
10
  inferConversationChatType,
17
11
  resolveByCreatedAtWindow,
18
12
  upsertOutboundMessageContext,
19
- } from "./message-context-store";
20
- import {
21
- readNamespaceJson,
22
- resolveNamespacePath,
23
- writeNamespaceJsonAtomic,
24
- } from "./persistence-store";
13
+ } from "../messaging/message-context-store";
14
+ import { getAccessToken } from "../platform/auth";
15
+ import { resolveRobotCode, stripTargetPrefix } from "../platform/config";
25
16
  import type {
26
17
  AICardInstance,
27
18
  AICardStreamingRequest,
@@ -30,9 +21,18 @@ import type {
30
21
  DingTalkTrackingMetadata,
31
22
  Logger,
32
23
  QuotedRef,
33
- } from "./types";
34
- import { AICardStatus } from "./types";
35
- import { formatDingTalkErrorPayloadLog, getProxyBypassOption } from "./utils";
24
+ } from "../platform/types";
25
+ import { AICardStatus } from "../platform/types";
26
+ import axios from "../shared/http-client";
27
+ import {
28
+ readNamespaceJson,
29
+ resolveNamespacePath,
30
+ writeNamespaceJsonAtomic,
31
+ } from "../shared/persistence-store";
32
+ import { formatDingTalkErrorPayloadLog, getProxyBypassOption } from "../shared/utils";
33
+ import { resolveOriginalPeerId } from "../targeting/peer-id-registry";
34
+ import { updateCardVariables } from "./card-callback-service";
35
+ import { DINGTALK_CARD_TEMPLATE, STOP_ACTION_VISIBLE, STOP_ACTION_HIDDEN } from "./card-template";
36
36
 
37
37
  const DINGTALK_API = "https://api.dingtalk.com";
38
38
  // Thinking/tool stream snippets are truncated to keep card updates compact.
@@ -85,7 +85,10 @@ function pruneInMemoryCardContentEntries(
85
85
  return entries.filter((entry) => nowMs < entry.expiresAt).slice(-CARD_CACHE_MAX_PER_CONVERSATION);
86
86
  }
87
87
 
88
- function touchInMemoryCardContentBucket(scopeKey: string, nowMs: number): {
88
+ function touchInMemoryCardContentBucket(
89
+ scopeKey: string,
90
+ nowMs: number,
91
+ ): {
89
92
  entries: Array<{ content: string; createdAt: number; expiresAt: number }>;
90
93
  lastActiveAt: number;
91
94
  } {
@@ -282,7 +285,11 @@ async function putAICardStreamingField(
282
285
  };
283
286
 
284
287
  try {
285
- const streamResp = await axios.put(`${DINGTALK_API}/v1.0/card/streaming`, streamBody, requestConfig);
288
+ const streamResp = await axios.put(
289
+ `${DINGTALK_API}/v1.0/card/streaming`,
290
+ streamBody,
291
+ requestConfig,
292
+ );
286
293
  log?.debug?.(
287
294
  `[DingTalk][AICard] Streaming response: status=${streamResp.status}, data=${JSON.stringify(streamResp.data)}`,
288
295
  );
@@ -408,8 +415,11 @@ function normalizePendingState(parsed: Partial<PendingCardStateFile>): PendingCa
408
415
  typeof entry.conversationId === "string" &&
409
416
  (entry.lastContent === undefined || typeof entry.lastContent === "string") &&
410
417
  (entry.lastBlockListJson === undefined || typeof entry.lastBlockListJson === "string") &&
411
- (entry.streamLifecycleOpened === undefined || typeof entry.streamLifecycleOpened === "boolean") &&
412
- (entry.recoveryAction === undefined || entry.recoveryAction === "finalize" || entry.recoveryAction === "recall"),
418
+ (entry.streamLifecycleOpened === undefined ||
419
+ typeof entry.streamLifecycleOpened === "boolean") &&
420
+ (entry.recoveryAction === undefined ||
421
+ entry.recoveryAction === "finalize" ||
422
+ entry.recoveryAction === "recall"),
413
423
  ),
414
424
  ),
415
425
  };
@@ -517,9 +527,7 @@ function buildStoppedCardFinalizePayload(params: {
517
527
  const baseContent = params.previousContent?.trim() || "";
518
528
  const blocks = parseStoredBlockList(params.previousBlockListJson);
519
529
  blocks.push({ type: 0, markdown: markerText });
520
- const content = baseContent
521
- ? `${baseContent}\n\n---\n*${markerText}*`
522
- : markerText;
530
+ const content = baseContent ? `${baseContent}\n\n---\n*${markerText}*` : markerText;
523
531
  return {
524
532
  blockListJson: JSON.stringify(blocks),
525
533
  content,
@@ -593,9 +601,9 @@ function normalizeRecoveredState(state: string): AICardInstance["state"] {
593
601
  // Helper to identify card terminal states.
594
602
  export function isCardInTerminalState(state: string): boolean {
595
603
  return (
596
- state === AICardStatus.FINISHED
597
- || state === AICardStatus.STOPPED
598
- || state === AICardStatus.FAILED
604
+ state === AICardStatus.FINISHED ||
605
+ state === AICardStatus.STOPPED ||
606
+ state === AICardStatus.FAILED
599
607
  );
600
608
  }
601
609
 
@@ -619,7 +627,10 @@ async function ensureFreshToken(card: AICardInstance, log?: Logger): Promise<voi
619
627
  }
620
628
  }
621
629
 
622
- export function formatContentForCard(content: string | undefined, type: "thinking" | "tool"): string {
630
+ export function formatContentForCard(
631
+ content: string | undefined,
632
+ type: "thinking" | "tool",
633
+ ): string {
623
634
  if (!content) {
624
635
  return "";
625
636
  }
@@ -693,10 +704,14 @@ export async function sendProactiveCardText(
693
704
  return { ok: false, error: "Failed to create AI card" };
694
705
  }
695
706
  const blockListJson = JSON.stringify([{ type: 0, markdown: content } satisfies CardBlock]);
696
- await commitAICardBlocks(card, {
697
- blockListJson,
698
- content,
699
- }, log);
707
+ await commitAICardBlocks(
708
+ card,
709
+ {
710
+ blockListJson,
711
+ content,
712
+ },
713
+ log,
714
+ );
700
715
  return {
701
716
  ok: true,
702
717
  processQueryKey: card.processQueryKey,
@@ -749,8 +764,9 @@ async function finalizePendingCardsByAccount(
749
764
 
750
765
  const pendingCards = listPendingCardsByAccount(accountId, storePath, log).filter(
751
766
  (item) =>
752
- (item.recoveryAction === "recall" && mode === "recover")
753
- || (item.recoveryAction !== "recall" && (!isCardInTerminalState(item.state) || item.recoveryAction === "finalize")),
767
+ (item.recoveryAction === "recall" && mode === "recover") ||
768
+ (item.recoveryAction !== "recall" &&
769
+ (!isCardInTerminalState(item.state) || item.recoveryAction === "finalize")),
754
770
  );
755
771
  if (pendingCards.length === 0) {
756
772
  return 0;
@@ -794,11 +810,15 @@ async function finalizePendingCardsByAccount(
794
810
  }
795
811
  continue;
796
812
  }
797
- await finalizeStoppedAICard(card, {
798
- reason,
799
- previousContent: entry.lastContent,
800
- previousBlockListJson: entry.lastBlockListJson,
801
- }, log);
813
+ await finalizeStoppedAICard(
814
+ card,
815
+ {
816
+ reason,
817
+ previousContent: entry.lastContent,
818
+ previousBlockListJson: entry.lastBlockListJson,
819
+ },
820
+ log,
821
+ );
802
822
  finalizedCount += 1;
803
823
  } catch (err: unknown) {
804
824
  const action = mode === "recover" ? "recover" : "finalize";
@@ -904,7 +924,11 @@ export async function createAICard(
904
924
  cardInstanceId?: unknown;
905
925
  }
906
926
  | undefined;
907
- const deliverResults = (responseData?.result as { deliverResults?: Array<{ success?: boolean; errorMsg?: string }> } | undefined)?.deliverResults;
927
+ const deliverResults = (
928
+ responseData?.result as
929
+ | { deliverResults?: Array<{ success?: boolean; errorMsg?: string }> }
930
+ | undefined
931
+ )?.deliverResults;
908
932
  if (Array.isArray(deliverResults)) {
909
933
  const failedDelivery = deliverResults.find((item) => item?.success === false);
910
934
  if (failedDelivery) {
@@ -1130,7 +1154,9 @@ async function finalizeAICardStreamingLifecycleIfNeeded(
1130
1154
  });
1131
1155
  } catch (err: unknown) {
1132
1156
  const message = err instanceof Error ? err.message : String(err);
1133
- log?.warn?.(`[DingTalk][AICard] Streaming lifecycle finalize failed; continuing instances finalize: ${message}`);
1157
+ log?.warn?.(
1158
+ `[DingTalk][AICard] Streaming lifecycle finalize failed; continuing instances finalize: ${message}`,
1159
+ );
1134
1160
  }
1135
1161
  }
1136
1162
 
@@ -1189,8 +1215,8 @@ export async function commitAICardBlocks(
1189
1215
 
1190
1216
  log?.debug?.(
1191
1217
  `[DingTalk][AICard] Finalizing via instances API: outTrackId=${card.outTrackId || card.cardInstanceId} ` +
1192
- `blockListLen=${options.blockListJson.length} contentLen=${options.content.length} flowStatus=3` +
1193
- (options.statusLine ? ` statusLine="${options.statusLine}"` : ""),
1218
+ `blockListLen=${options.blockListJson.length} contentLen=${options.content.length} flowStatus=3` +
1219
+ (options.statusLine ? ` statusLine="${options.statusLine}"` : ""),
1194
1220
  );
1195
1221
 
1196
1222
  try {
@@ -1233,10 +1259,11 @@ export async function commitAICardBlocks(
1233
1259
  card.state = AICardStatus.FINISHED;
1234
1260
  card.lastUpdated = Date.now();
1235
1261
  removePendingCard(card, log);
1236
- log?.info?.(`[DingTalk][AICard] Card finalized: outTrackId=${card.outTrackId || card.cardInstanceId} state=FINISHED`);
1262
+ log?.info?.(
1263
+ `[DingTalk][AICard] Card finalized: outTrackId=${card.outTrackId || card.cardInstanceId} state=FINISHED`,
1264
+ );
1237
1265
  }
1238
1266
 
1239
-
1240
1267
  export async function streamAICard(
1241
1268
  card: AICardInstance,
1242
1269
  content: string,
@@ -1270,8 +1297,8 @@ export async function streamAICard(
1270
1297
  retainPendingCardForRecovery(card, options.recoveryAction ?? "finalize", log);
1271
1298
  if (err.response?.status === 500 && err.response?.data?.code === "unknownError") {
1272
1299
  const errorMsg =
1273
- "⚠️ **[DingTalk] AI Card 串流更新失败 (500 unknownError)**\n\n"
1274
- + "这通常表示当前内置模板契约与钉钉侧模板字段不一致,当前及后续消息将自动回退为 Markdown 发送。";
1300
+ "⚠️ **[DingTalk] AI Card 串流更新失败 (500 unknownError)**\n\n" +
1301
+ "这通常表示当前内置模板契约与钉钉侧模板字段不一致,当前及后续消息将自动回退为 Markdown 发送。";
1275
1302
  await sendTemplateMismatchNotification(card, errorMsg, log);
1276
1303
  }
1277
1304
  throw err;
@@ -1300,7 +1327,9 @@ export async function finishAICard(
1300
1327
  const token = await getAccessToken(card.config, log);
1301
1328
  await hideCardStopButton(card.outTrackId, token, card.config);
1302
1329
  } catch (err: any) {
1303
- log?.debug?.(`[DingTalk][AICard] Non-critical: failed to hide stop button on finish: ${err.message}`);
1330
+ log?.debug?.(
1331
+ `[DingTalk][AICard] Non-critical: failed to hide stop button on finish: ${err.message}`,
1332
+ );
1304
1333
  }
1305
1334
  }
1306
1335
  if (card.conversationId && content.trim() && card.accountId && card.processQueryKey) {
@@ -1334,14 +1363,12 @@ function parseRecallFailureEntries(payload: unknown): Array<[string, string]> {
1334
1363
  if (!payload || typeof payload !== "object") {
1335
1364
  return [];
1336
1365
  }
1337
- return Object.entries(payload as Record<string, unknown>)
1338
- .map(([key, value]) => [String(key), String(value ?? "")] as [string, string]);
1366
+ return Object.entries(payload as Record<string, unknown>).map(
1367
+ ([key, value]) => [String(key), String(value ?? "")] as [string, string],
1368
+ );
1339
1369
  }
1340
1370
 
1341
- export async function recallAICardMessage(
1342
- card: AICardInstance,
1343
- log?: Logger,
1344
- ): Promise<boolean> {
1371
+ export async function recallAICardMessage(card: AICardInstance, log?: Logger): Promise<boolean> {
1345
1372
  const config = card.config;
1346
1373
  const processQueryKey = card.processQueryKey?.trim();
1347
1374
  const robotCode = config ? resolveRobotCode(config) : "";
@@ -1349,8 +1376,8 @@ export async function recallAICardMessage(
1349
1376
  if (!config || !processQueryKey || !robotCode) {
1350
1377
  log?.warn?.(
1351
1378
  `[DingTalk][AICard] Skip recall because required metadata is missing: ` +
1352
- `card=${card.cardInstanceId} hasConfig=${Boolean(config)} ` +
1353
- `processQueryKey=${processQueryKey || "(none)"} robotCode=${robotCode || "(none)"}`,
1379
+ `card=${card.cardInstanceId} hasConfig=${Boolean(config)} ` +
1380
+ `processQueryKey=${processQueryKey || "(none)"} robotCode=${robotCode || "(none)"}`,
1354
1381
  );
1355
1382
  return false;
1356
1383
  }
@@ -1383,9 +1410,12 @@ export async function recallAICardMessage(
1383
1410
  },
1384
1411
  ...getProxyBypassOption(config),
1385
1412
  });
1386
- const successResults = Array.isArray((response.data as Record<string, unknown> | undefined)?.successResult)
1387
- ? ((response.data as Record<string, unknown>).successResult as unknown[])
1388
- .map((item) => String(item))
1413
+ const successResults = Array.isArray(
1414
+ (response.data as Record<string, unknown> | undefined)?.successResult,
1415
+ )
1416
+ ? ((response.data as Record<string, unknown>).successResult as unknown[]).map((item) =>
1417
+ String(item),
1418
+ )
1389
1419
  : [];
1390
1420
  const failedEntries = parseRecallFailureEntries(
1391
1421
  (response.data as Record<string, unknown> | undefined)?.failedResult,
@@ -1393,14 +1423,14 @@ export async function recallAICardMessage(
1393
1423
  if (failedEntries.length > 0) {
1394
1424
  log?.warn?.(
1395
1425
  `[DingTalk][AICard] Recall reported failedResult: card=${card.cardInstanceId} ` +
1396
- `processQueryKey=${processQueryKey} failed=${JSON.stringify(failedEntries)}`,
1426
+ `processQueryKey=${processQueryKey} failed=${JSON.stringify(failedEntries)}`,
1397
1427
  );
1398
1428
  return false;
1399
1429
  }
1400
1430
  if (!successResults.includes(processQueryKey)) {
1401
1431
  log?.warn?.(
1402
1432
  `[DingTalk][AICard] Recall response missing successResult for processQueryKey=${processQueryKey} ` +
1403
- `payload=${JSON.stringify(response.data)}`,
1433
+ `payload=${JSON.stringify(response.data)}`,
1404
1434
  );
1405
1435
  return false;
1406
1436
  }
@@ -1410,7 +1440,7 @@ export async function recallAICardMessage(
1410
1440
  removePendingCard(card, log);
1411
1441
  log?.info?.(
1412
1442
  `[DingTalk][AICard] Recalled empty card message: card=${card.cardInstanceId} ` +
1413
- `conversationId=${target.conversationId} processQueryKey=${processQueryKey} mode=${target.isGroup ? "group" : "direct"}`,
1443
+ `conversationId=${target.conversationId} processQueryKey=${processQueryKey} mode=${target.isGroup ? "group" : "direct"}`,
1414
1444
  );
1415
1445
  return true;
1416
1446
  } catch (err: any) {
@@ -1514,7 +1544,7 @@ function cacheCardContentByProcessQueryKey(
1514
1544
  }
1515
1545
  log?.debug?.(
1516
1546
  `[DingTalk][QuotedRef][Persist] direction=outbound scope=${conversationId} messageType=card ` +
1517
- `processQueryKey=${processQueryKey} quotedRef=${quotedRef ? JSON.stringify(quotedRef) : "(none)"}`,
1547
+ `processQueryKey=${processQueryKey} quotedRef=${quotedRef ? JSON.stringify(quotedRef) : "(none)"}`,
1518
1548
  );
1519
1549
  upsertOutboundMessageContext({
1520
1550
  storePath,
@@ -1,9 +1,9 @@
1
1
  import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
2
- import { finalizeStoppedAICard } from "../card-service";
3
2
  import { dispatchDingTalkCardStopCommand } from "../command/card-stop-command";
4
- import type { DingTalkConfig, Logger } from "../types";
5
- import { AICardStatus } from "../types";
3
+ import type { DingTalkConfig, Logger } from "../platform/types";
4
+ import { AICardStatus } from "../platform/types";
6
5
  import { markCardRunStopRequested, resolveCardRun } from "./card-run-registry";
6
+ import { finalizeStoppedAICard } from "./card-service";
7
7
 
8
8
  export interface StopCardRunResult {
9
9
  ok: boolean;
@@ -68,11 +68,15 @@ export async function stopCardRun(params: {
68
68
  // --- Phase 2: Finalize card via instances API so V2 blockList/content stay consistent ---
69
69
  if (record.card) {
70
70
  try {
71
- await finalizeStoppedAICard(record.card, {
72
- reason: "⏹️ 已停止",
73
- previousContent: lastContent,
74
- previousBlockListJson: lastBlockListJson,
75
- }, params.log);
71
+ await finalizeStoppedAICard(
72
+ record.card,
73
+ {
74
+ reason: "⏹️ 已停止",
75
+ previousContent: lastContent,
76
+ previousBlockListJson: lastBlockListJson,
77
+ },
78
+ params.log,
79
+ );
76
80
  } catch (error) {
77
81
  params.log?.warn?.(
78
82
  `[${params.accountId}] [DingTalk][CardStop] failed to finalize stopped card: ${error instanceof Error ? error.message : String(error)}`,
@@ -1,4 +1,4 @@
1
- import type { DingTalkConfig } from "../types";
1
+ import type { DingTalkConfig } from "../platform/types";
2
2
 
3
3
  export type CardStreamingMode = "off" | "answer" | "all";
4
4