@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
@@ -13,7 +13,7 @@ import type {
13
13
  ChannelAccountSnapshot as SDKChannelAccountSnapshot,
14
14
  ChannelGatewayContext as SDKChannelGatewayContext,
15
15
  ChannelLogSink as SDKChannelLogSink,
16
- } from "openclaw/plugin-sdk/channel-runtime";
16
+ } from "openclaw/plugin-sdk/channel-contract";
17
17
  import type { ChannelPlugin as SDKChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk/core";
18
18
  import type { ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
19
19
  import type { SecretInput } from "./secret-input";
@@ -23,8 +23,34 @@ export type AckReactionMode = "off" | "emoji" | "kaomoji";
23
23
  // explicit modes remain: "off" | "emoji" | "kaomoji".
24
24
  export type AckReactionConfigValue = string;
25
25
  export type CardStreamingMode = "off" | "answer" | "all";
26
+ /** How tool-driven task-progress changes reach the card:
27
+ * - `heartbeat` (default): coalesced into the 30s progress heartbeat
28
+ * - `interval`: pushed on every correlated tool event, throttled by `cardStreamInterval` */
29
+ export type CardTaskProgressRefresh = "heartbeat" | "interval";
26
30
  export type ContextVisibilityMode = "all" | "allowlist" | "allowlist_quote";
27
31
 
32
+ /**
33
+ * Gateway RPC capability configuration (Issue #608, 问题 3).
34
+ * Aligns with the official connector's `tools: { docs, media }` gate shape.
35
+ * All capabilities default to enabled for backward compatibility.
36
+ */
37
+ export interface DingTalkGatewayCapabilitiesConfig {
38
+ tools?: {
39
+ /** Enable `dingtalk.docs.*` and `dingtalk-connector.docs.*` Gateway RPCs (default: true) */
40
+ docs?: boolean;
41
+ /** Enable proactive-send Gateway RPCs: `dingtalk-connector.sendToUser/sendToGroup/send` (default: true) */
42
+ proactiveSend?: boolean;
43
+ };
44
+ /** Optional doc-space allowlist; when set, docs RPCs only accept these `spaceId` values */
45
+ docs?: {
46
+ allowedSpaceIds?: string[];
47
+ };
48
+ /** Optional proactive-send target allowlist; entries must be `user:*` or `group:*` */
49
+ send?: {
50
+ allowedTargets?: string[];
51
+ };
52
+ }
53
+
28
54
  /**
29
55
  * DingTalk channel configuration (extends base OpenClaw config)
30
56
  */
@@ -79,10 +105,23 @@ export interface DingTalkConfig extends OpenClawConfig {
79
105
  * - answer: stream answer text
80
106
  * - all: stream answer + reasoning text */
81
107
  cardStreamingMode?: CardStreamingMode;
108
+ /** Internal derived flag, not a user-facing config key: whether
109
+ * `cardStreamingMode` came from an explicit user setting instead of the
110
+ * runtime default. Written by config normalization only. */
111
+ cardStreamingModeConfigured?: boolean;
82
112
  /** @deprecated Use `cardStreamingMode` instead. */
83
113
  cardRealTimeStream?: boolean;
84
114
  /** Throttle interval in ms for card stream updates (default 1000) */
85
115
  cardStreamInterval?: number;
116
+ /** Live "task in progress" block on AI cards for long-running tasks.
117
+ * `true` forces it on, `false` forces it off; when unset it is on unless
118
+ * `cardStreamingMode` is explicitly `"off"`. */
119
+ cardTaskProgress?: boolean;
120
+ /** How stage/step changes reach the progress block. `heartbeat` (default)
121
+ * keeps the documented "once on appearance, then once per 30s" budget;
122
+ * `interval` pushes on every correlated tool event, throttled by
123
+ * `cardStreamInterval`, trading card updates for a fresher stage label. */
124
+ cardTaskProgressRefresh?: CardTaskProgressRefresh;
86
125
  /** AICard degrade duration in milliseconds after trigger errors (default 30m) */
87
126
  aicardDegradeMs?: number;
88
127
  /** Enable local learning loop (events/reflections/session notes/global rules) */
@@ -104,6 +143,8 @@ export interface DingTalkConfig extends OpenClawConfig {
104
143
  tokens?: boolean;
105
144
  dapiUsage?: boolean;
106
145
  };
146
+ /** Gateway RPC capability gates and allowlists (docs RPCs + proactive-send RPCs). Named `gatewayCapabilities` because host `OpenClawConfig` already owns `gateway` and `tools`, and a bare `capabilities` key would collide with this plugin's channel capabilities metadata. */
147
+ gatewayCapabilities?: DingTalkGatewayCapabilitiesConfig;
107
148
  }
108
149
 
109
150
  /**
@@ -159,10 +200,23 @@ export interface DingTalkChannelConfig {
159
200
  * - answer: stream answer text
160
201
  * - all: stream answer + reasoning text */
161
202
  cardStreamingMode?: CardStreamingMode;
203
+ /** Internal derived flag, not a user-facing config key: whether
204
+ * `cardStreamingMode` came from an explicit user setting instead of the
205
+ * runtime default. Written by config normalization only. */
206
+ cardStreamingModeConfigured?: boolean;
162
207
  /** @deprecated Use `cardStreamingMode` instead. */
163
208
  cardRealTimeStream?: boolean;
164
209
  /** Throttle interval in ms for card stream updates (default 1000) */
165
210
  cardStreamInterval?: number;
211
+ /** Live "task in progress" block on AI cards for long-running tasks.
212
+ * `true` forces it on, `false` forces it off; when unset it is on unless
213
+ * `cardStreamingMode` is explicitly `"off"`. */
214
+ cardTaskProgress?: boolean;
215
+ /** How stage/step changes reach the progress block. `heartbeat` (default)
216
+ * keeps the documented "once on appearance, then once per 30s" budget;
217
+ * `interval` pushes on every correlated tool event, throttled by
218
+ * `cardStreamInterval`, trading card updates for a fresher stage label. */
219
+ cardTaskProgressRefresh?: CardTaskProgressRefresh;
166
220
  /** AICard degrade duration in milliseconds after trigger errors (default 30m) */
167
221
  aicardDegradeMs?: number;
168
222
  /** Enable local learning loop (events/reflections/session notes/global rules) */
@@ -175,6 +229,8 @@ export interface DingTalkChannelConfig {
175
229
  convertMarkdownTables?: boolean;
176
230
  /** @mention the sender after card finalization in group chats; value is the message text */
177
231
  cardAtSender?: string;
232
+ /** Gateway RPC capability gates and allowlists (docs RPCs + proactive-send RPCs). Named `gatewayCapabilities` because host `OpenClawConfig` already owns `gateway` and `tools`, and a bare `capabilities` key would collide with this plugin's channel capabilities metadata. */
233
+ gatewayCapabilities?: DingTalkGatewayCapabilitiesConfig;
178
234
  }
179
235
 
180
236
  /**
@@ -227,6 +283,17 @@ export interface DocInfo {
227
283
  updatedAt?: number | string;
228
284
  }
229
285
 
286
+ /**
287
+ * Recursive element node of an interactiveCard `cardContent` payload.
288
+ * Real DingTalk quote callbacks carry card text as nested element trees
289
+ * (e.g. LIST -> RICHTEXT -> TEXT nodes) rather than a flat `text` field.
290
+ */
291
+ export interface DingTalkCardContentNode {
292
+ elementType?: string;
293
+ value?: string;
294
+ children?: DingTalkCardContentNode[];
295
+ }
296
+
230
297
  /**
231
298
  * DingTalk incoming message (Stream mode)
232
299
  */
@@ -252,9 +319,13 @@ export interface DingTalkInboundMessage {
252
319
  createdAt?: number;
253
320
  content?: {
254
321
  text?: string;
322
+ /** Quoted text message body in real payloads (`msgType: "text"`). */
323
+ content?: string;
255
324
  downloadCode?: string;
256
325
  fileName?: string;
257
326
  biz_custom_action_url?: string;
327
+ /** interactiveCard quoted content as nested element tree. */
328
+ cardContent?: DingTalkCardContentNode[];
258
329
  richText?: Array<{
259
330
  msgType?: string;
260
331
  type?: string;
@@ -415,7 +486,11 @@ export interface SendMessageOptions {
415
486
  /** Force markdown/text delivery even when messageType is "card". Bypasses card
416
487
  * creation while preserving journal writes and other side-effects. */
417
488
  forceMarkdown?: boolean;
418
- /** Allowed local roots for sandbox/container media path resolution. */
489
+ /**
490
+ * Host-provided allowlist of local media roots. When set, caller-supplied
491
+ * host paths whose canonical location is outside these roots are resolved
492
+ * through the runtime media bridge instead of being read directly.
493
+ */
419
494
  mediaLocalRoots?: string[];
420
495
  }
421
496
 
@@ -10,9 +10,7 @@ type AxiosInstanceWithGuards = AxiosInstance & {
10
10
  // Centralize repo-level axios policy without mutating the global axios singleton
11
11
  // that third-party dependencies may also share.
12
12
  const httpClient = (
13
- typeof axios?.create === "function"
14
- ? axios.create({ timeout: DEFAULT_HTTP_TIMEOUT_MS })
15
- : axios
13
+ typeof axios?.create === "function" ? axios.create({ timeout: DEFAULT_HTTP_TIMEOUT_MS }) : axios
16
14
  ) as AxiosInstanceWithGuards;
17
15
 
18
16
  httpClient.isAxiosError = axios.isAxiosError;
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { Logger } from "./types";
3
+ import type { Logger } from "../platform/types";
4
4
 
5
5
  type NamespaceFormat = "json";
6
6
 
@@ -71,7 +71,10 @@ function buildScopeSuffix(scope?: PersistenceScope): string {
71
71
  return `.${segments.join(".")}`;
72
72
  }
73
73
 
74
- export function resolveNamespacePath(namespace: string, options: ResolveNamespacePathOptions): string {
74
+ export function resolveNamespacePath(
75
+ namespace: string,
76
+ options: ResolveNamespacePathOptions,
77
+ ): string {
75
78
  const format = options.format || "json";
76
79
  const baseDir = path.join(path.dirname(options.storePath), NAMESPACE_ROOT_DIR);
77
80
  const safeNamespace = sanitizeSegment(namespace.trim());
@@ -79,10 +82,7 @@ export function resolveNamespacePath(namespace: string, options: ResolveNamespac
79
82
  return path.join(baseDir, `${safeNamespace}${suffix}.${format}`);
80
83
  }
81
84
 
82
- export function readNamespaceJson<T>(
83
- namespace: string,
84
- options: ReadNamespaceJsonOptions<T>,
85
- ): T {
85
+ export function readNamespaceJson<T>(namespace: string, options: ReadNamespaceJsonOptions<T>): T {
86
86
  const filePath = resolveNamespacePath(namespace, options);
87
87
  try {
88
88
  if (!fs.existsSync(filePath)) {
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
3
3
  import * as net from "node:net";
4
4
  import * as os from "node:os";
5
5
  import * as path from "node:path";
6
- import type { Logger, RetryOptions } from "./types";
6
+ import type { Logger, RetryOptions } from "../platform/types";
7
7
 
8
8
  type PluginDebugLogParams = {
9
9
  accountId: string;
@@ -44,7 +44,11 @@ function formatPluginDebugDate(date: Date): string {
44
44
  return `${date.getFullYear()}-${padNumber(date.getMonth() + 1)}-${padNumber(date.getDate())}`;
45
45
  }
46
46
 
47
- function resolvePluginDebugLogFilePath(params: { storePath: string; accountId: string; date: Date }): string {
47
+ function resolvePluginDebugLogFilePath(params: {
48
+ storePath: string;
49
+ accountId: string;
50
+ date: Date;
51
+ }): string {
48
52
  return path.join(
49
53
  path.dirname(params.storePath),
50
54
  "logs",
@@ -58,7 +62,11 @@ function formatPluginDebugLine(params: { accountId: string; date: Date; message:
58
62
  return `[${formatPluginDebugTimestamp(params.date)}] [debug] [dingtalk] [account:${params.accountId}] ${params.message}`;
59
63
  }
60
64
 
61
- function buildPluginDebugWriterKey(params: { storePath: string; accountId: string; date: Date }): string {
65
+ function buildPluginDebugWriterKey(params: {
66
+ storePath: string;
67
+ accountId: string;
68
+ date: Date;
69
+ }): string {
62
70
  return JSON.stringify([params.storePath, params.accountId, formatPluginDebugDate(params.date)]);
63
71
  }
64
72
 
@@ -209,11 +217,7 @@ export function stringifyUnknown(value: unknown): string {
209
217
  if (typeof value === "string") {
210
218
  return value;
211
219
  }
212
- if (
213
- typeof value === "number" ||
214
- typeof value === "boolean" ||
215
- typeof value === "bigint"
216
- ) {
220
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
217
221
  return String(value);
218
222
  }
219
223
  try {
@@ -292,7 +296,11 @@ export function formatDingTalkErrorPayload(payload: unknown): string {
292
296
  } catch {
293
297
  if (typeof payload === "string") {
294
298
  serialized = payload;
295
- } else if (typeof payload === "number" || typeof payload === "boolean" || typeof payload === "bigint") {
299
+ } else if (
300
+ typeof payload === "number" ||
301
+ typeof payload === "boolean" ||
302
+ typeof payload === "bigint"
303
+ ) {
296
304
  serialized = `${payload}`;
297
305
  } else {
298
306
  serialized = "[unserializable-payload]";
@@ -318,7 +326,9 @@ export function formatDingTalkErrorPayloadLog(
318
326
  return `${prefix}[ErrorPayload][${scope}] ${formatDingTalkErrorPayload(payload)}`;
319
327
  }
320
328
 
321
- export function getProxyBypassOption(config?: { bypassProxyForSend?: boolean }): { proxy: false } | Record<string, never> {
329
+ export function getProxyBypassOption(config?: {
330
+ bypassProxyForSend?: boolean;
331
+ }): { proxy: false } | Record<string, never> {
322
332
  return config?.bypassProxyForSend ? { proxy: false } : {};
323
333
  }
324
334
 
@@ -445,7 +455,8 @@ export function formatDingTalkConnectionErrorLog(
445
455
  ? errRecord.dingtalkConnectionEndpoint
446
456
  : undefined;
447
457
 
448
- const hasResponse = "response" in errRecord && errRecord.response !== null && errRecord.response !== undefined;
458
+ const hasResponse =
459
+ "response" in errRecord && errRecord.response !== null && errRecord.response !== undefined;
449
460
  if (!hasResponse && !endpoint && stage === scope) {
450
461
  return null;
451
462
  }
@@ -455,7 +466,8 @@ export function formatDingTalkConnectionErrorLog(
455
466
  parts.push(`endpoint=${endpoint}`);
456
467
  }
457
468
 
458
- const response = (err as { response?: { status?: unknown; data?: unknown; headers?: unknown } }).response;
469
+ const response = (err as { response?: { status?: unknown; data?: unknown; headers?: unknown } })
470
+ .response;
459
471
  if (response) {
460
472
  if (response.status !== undefined && response.status !== null) {
461
473
  const statusText =
@@ -469,7 +481,12 @@ export function formatDingTalkConnectionErrorLog(
469
481
  }
470
482
 
471
483
  let requestId = getHeaderCaseInsensitive(response.headers, "x-acs-dingtalk-request-id");
472
- if (!requestId && response.data && typeof response.data === "object" && !Array.isArray(response.data)) {
484
+ if (
485
+ !requestId &&
486
+ response.data &&
487
+ typeof response.data === "object" &&
488
+ !Array.isArray(response.data)
489
+ ) {
473
490
  const data = response.data as Record<string, unknown>;
474
491
  if (typeof data.requestId === "string") {
475
492
  requestId = data.requestId;
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
8
- import type { AtMention, AgentNameMatch } from "../types";
8
+ import type { AtMention, AgentNameMatch } from "../platform/types";
9
9
 
10
10
  interface AgentConfig {
11
11
  id: string;
@@ -9,10 +9,10 @@
9
9
 
10
10
  import { maybeResolveTextAlias } from "openclaw/plugin-sdk/command-auth";
11
11
  import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
12
- import { resolveRobotCode } from "../config";
13
- import { parseLearnCommand } from "../learning-command-service";
14
- import { getDingTalkRuntime } from "../runtime";
15
- import { sendBySession } from "../send-service";
12
+ import { parseLearnCommand } from "../command/learning-command-service";
13
+ import { sendBySession } from "../messaging/send-service";
14
+ import { resolveRobotCode } from "../platform/config";
15
+ import { getDingTalkRuntime } from "../platform/runtime";
16
16
  import type {
17
17
  AgentNameMatch,
18
18
  DingTalkConfig,
@@ -21,10 +21,10 @@ import type {
21
21
  Logger,
22
22
  MessageContent,
23
23
  ResolvedDingTalkRoute,
24
- } from "../types";
25
- import { getErrorMessage } from "../utils";
24
+ } from "../platform/types";
25
+ import { getErrorMessage } from "../shared/utils";
26
26
  import { resolveAtAgents } from "./agent-name-matcher";
27
- import type { ResolvedDingTalkSessionPeer } from "../session-routing";
27
+ import type { ResolvedDingTalkSessionPeer } from "./session-routing";
28
28
 
29
29
  export class HostRoutingHelperUnavailableError extends Error {
30
30
  constructor(
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { readNamespaceJson, writeNamespaceJsonAtomic } from "../persistence-store";
3
+ import { readNamespaceJson, writeNamespaceJsonAtomic } from "../shared/persistence-store";
4
4
 
5
5
  const GROUP_MEMBERS_NAMESPACE = "members.group-roster";
6
6
 
@@ -1,4 +1,4 @@
1
- import { readNamespaceJson, writeNamespaceJsonAtomic } from "./persistence-store";
1
+ import { readNamespaceJson, writeNamespaceJsonAtomic } from "../shared/persistence-store";
2
2
 
3
3
  const SESSION_PEER_OVERRIDE_NAMESPACE = "session-peer-overrides";
4
4
 
@@ -20,7 +20,11 @@ function readBucket(storePath: string, accountId: string): SessionPeerOverrideBu
20
20
  });
21
21
  }
22
22
 
23
- function writeBucket(storePath: string, accountId: string, bucket: SessionPeerOverrideBucket): void {
23
+ function writeBucket(
24
+ storePath: string,
25
+ accountId: string,
26
+ bucket: SessionPeerOverrideBucket,
27
+ ): void {
24
28
  writeNamespaceJsonAtomic(SESSION_PEER_OVERRIDE_NAMESPACE, {
25
29
  storePath,
26
30
  scope: { accountId },
@@ -63,8 +67,9 @@ export function clearSessionPeerOverride(params: {
63
67
  const bucket = readBucket(params.storePath, params.accountId);
64
68
  const sourceKey = buildSourceKey(params.sourceKind, params.sourceId);
65
69
  const legacyKey = params.sourceKind === "group" ? params.sourceId : undefined;
66
- const existed = Object.prototype.hasOwnProperty.call(bucket.peers, sourceKey)
67
- || (legacyKey ? Object.prototype.hasOwnProperty.call(bucket.peers, legacyKey) : false);
70
+ const existed =
71
+ Object.prototype.hasOwnProperty.call(bucket.peers, sourceKey) ||
72
+ (legacyKey ? Object.prototype.hasOwnProperty.call(bucket.peers, legacyKey) : false);
68
73
  if (!existed) {
69
74
  return false;
70
75
  }
@@ -1,4 +1,4 @@
1
- import type { DingTalkConfig } from "./types";
1
+ import type { DingTalkConfig } from "../platform/types";
2
2
 
3
3
  export interface ResolveDingTalkSessionPeerParams {
4
4
  isDirect: boolean;
@@ -1,8 +1,8 @@
1
1
  import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
2
2
  import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk/directory-runtime";
3
- import { getConfig, stripTargetPrefix } from "../config";
4
- import { resolveOriginalPeerId } from "../peer-id-registry";
5
- import { getDingTalkRuntime } from "../runtime";
3
+ import { getConfig, stripTargetPrefix } from "../platform/config";
4
+ import { getDingTalkRuntime } from "../platform/runtime";
5
+ import { resolveOriginalPeerId } from "./peer-id-registry";
6
6
  import { listKnownGroupTargets, listKnownUserTargets } from "./target-directory-store";
7
7
 
8
8
  export type DirectoryListParams = {
@@ -1,4 +1,4 @@
1
- import { readNamespaceJson, writeNamespaceJsonAtomic } from "../persistence-store";
1
+ import { readNamespaceJson, writeNamespaceJsonAtomic } from "../shared/persistence-store";
2
2
 
3
3
  const TARGET_DIRECTORY_NAMESPACE = "targets.directory";
4
4
  const MAX_HISTORICAL_NAMES = 20;
@@ -1,4 +1,4 @@
1
- import { stripTargetPrefix } from "../config";
1
+ import { stripTargetPrefix } from "../platform/config";
2
2
 
3
3
  function stripProviderPrefix(raw: string): string {
4
4
  return raw.replace(/^(dingtalk|dd|ding)\s*:\s*/i, "");
@@ -1,35 +0,0 @@
1
- export type DynamicAckReactionLogger = {
2
- debug?: (msg: string) => void;
3
- info?: (msg: string) => void;
4
- warn?: (msg: string) => void;
5
- };
6
- export type RuntimeAgentEvent = {
7
- stream?: string;
8
- runId?: string;
9
- sessionKey?: string;
10
- data?: {
11
- phase?: string;
12
- name?: string;
13
- args?: unknown;
14
- runId?: string;
15
- sessionKey?: string;
16
- toolCallId?: string;
17
- meta?: {
18
- runId?: string;
19
- sessionKey?: string;
20
- } | null;
21
- };
22
- };
23
- export type RuntimeEventsSurface = {
24
- onAgentEvent?: (listener: (event: unknown) => void) => (() => void);
25
- };
26
- export declare function getEventRunId(event: RuntimeAgentEvent | undefined): string | undefined;
27
- export declare function getEventSessionKey(event: RuntimeAgentEvent | undefined): string | undefined;
28
- export declare function describeEvent(event: RuntimeAgentEvent | undefined): string;
29
- export declare function createDynamicAckReactionCorrelator(params: {
30
- sessionKey: string;
31
- enabled: boolean;
32
- createdAt: number;
33
- optimisticCaptureWindowMs: number;
34
- log?: DynamicAckReactionLogger;
35
- }): (event: RuntimeAgentEvent | undefined) => boolean;
@@ -1,59 +0,0 @@
1
- import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
2
- import type { DingTalkConfig } from "./types";
3
- export { resolveRelativePath, resolveUserPath } from "./path-utils";
4
- export type RuntimeDingTalkConfig = Omit<DingTalkConfig, "clientSecret"> & {
5
- clientSecret: string;
6
- };
7
- /**
8
- * Merge channel-level defaults into an account-specific config.
9
- * Account-level values take precedence; `accounts` key is excluded to avoid recursion.
10
- */
11
- export declare function mergeAccountWithDefaults(channelCfg: DingTalkConfig, accountCfg: DingTalkConfig): DingTalkConfig;
12
- /**
13
- * Resolve DingTalk config for an account.
14
- * Named accounts inherit channel-level defaults with account-level overrides.
15
- * Falls back to top-level config for single-account setups.
16
- */
17
- export declare function getConfig(cfg: OpenClawConfig, accountId?: string): DingTalkConfig;
18
- export declare function isConfigured(cfg: OpenClawConfig, accountId?: string): boolean;
19
- export declare function resolveRuntimeConfig(config: DingTalkConfig, log?: {
20
- warn?: (message: string, data?: unknown) => void;
21
- }): Promise<RuntimeDingTalkConfig>;
22
- /**
23
- * Resolve the robot code used by DingTalk APIs.
24
- * DingTalk robotCode is always equal to clientId; this helper trims whitespace.
25
- */
26
- export declare function resolveRobotCode(config: Pick<DingTalkConfig, "clientId">): string;
27
- export declare function resolveGroupConfig(cfg: DingTalkConfig, groupId: string): {
28
- systemPrompt?: string;
29
- requireMention?: boolean;
30
- groupAllowFrom?: string[];
31
- } | undefined;
32
- export declare function resolveAckReactionSetting(params: {
33
- cfg: OpenClawConfig;
34
- accountId?: string | null;
35
- agentId?: string | null;
36
- }): string | undefined;
37
- /**
38
- * Strip group/user prefixes used by CLI targeting.
39
- * Returns raw DingTalk target ID and whether caller explicitly requested a user target.
40
- */
41
- export declare function stripTargetPrefix(target: string): {
42
- targetId: string;
43
- isExplicitUser: boolean;
44
- };
45
- /**
46
- * List all DingTalk account IDs from config
47
- */
48
- export declare function listDingTalkAccountIds(cfg: OpenClawConfig): string[];
49
- /**
50
- * Resolved DingTalk account with configuration status
51
- */
52
- export interface ResolvedDingTalkAccount extends DingTalkConfig {
53
- accountId: string;
54
- configured: boolean;
55
- }
56
- /**
57
- * Resolve a specific DingTalk account configuration
58
- */
59
- export declare function resolveDingTalkAccount(cfg: OpenClawConfig, accountId?: string | null): ResolvedDingTalkAccount;
@@ -1,123 +0,0 @@
1
- import { getErrorMessage } from "../utils";
2
-
3
- export type DynamicAckReactionLogger = {
4
- debug?: (msg: string) => void;
5
- info?: (msg: string) => void;
6
- warn?: (msg: string) => void;
7
- };
8
-
9
- export type RuntimeAgentEvent = {
10
- stream?: string;
11
- runId?: string;
12
- sessionKey?: string;
13
- data?: {
14
- phase?: string;
15
- name?: string;
16
- args?: unknown;
17
- runId?: string;
18
- sessionKey?: string;
19
- toolCallId?: string;
20
- meta?: {
21
- runId?: string;
22
- sessionKey?: string;
23
- } | null;
24
- };
25
- };
26
-
27
- export type RuntimeEventsSurface = {
28
- onAgentEvent?: (listener: (event: unknown) => void) => (() => void);
29
- };
30
-
31
- function firstTrimmedString(...values: unknown[]): string | undefined {
32
- for (const value of values) {
33
- if (typeof value === "string" && value.trim()) {
34
- return value.trim();
35
- }
36
- }
37
- return undefined;
38
- }
39
-
40
- export function getEventRunId(event: RuntimeAgentEvent | undefined): string | undefined {
41
- return firstTrimmedString(event?.runId, event?.data?.runId, event?.data?.meta?.runId);
42
- }
43
-
44
- export function getEventSessionKey(event: RuntimeAgentEvent | undefined): string | undefined {
45
- return firstTrimmedString(event?.sessionKey, event?.data?.sessionKey, event?.data?.meta?.sessionKey);
46
- }
47
-
48
- export function describeEvent(event: RuntimeAgentEvent | undefined): string {
49
- const stream = firstTrimmedString(event?.stream) || "-";
50
- const phase = firstTrimmedString(event?.data?.phase) || "-";
51
- const toolName = firstTrimmedString(event?.data?.name) || "-";
52
- const toolCallId = firstTrimmedString(event?.data?.toolCallId) || "-";
53
- return `stream=${stream} phase=${phase} runId=${getEventRunId(event) || "-"} ` +
54
- `sessionKey=${getEventSessionKey(event) || "-"} toolCallId=${toolCallId} toolName=${toolName}`;
55
- }
56
-
57
- export function createDynamicAckReactionCorrelator(params: {
58
- sessionKey: string;
59
- enabled: boolean;
60
- createdAt: number;
61
- optimisticCaptureWindowMs: number;
62
- log?: DynamicAckReactionLogger;
63
- }) {
64
- let activeRunId: string | undefined;
65
- let correlationUnavailableLogged = false;
66
- let optimisticCaptureCount = 0;
67
-
68
- return (event: RuntimeAgentEvent | undefined): boolean => {
69
- const eventRunId = getEventRunId(event);
70
- const eventSessionKey = getEventSessionKey(event);
71
- const eventStream = firstTrimmedString(event?.stream) || "";
72
- const eventPhase = firstTrimmedString(event?.data?.phase) || "";
73
-
74
- if (activeRunId) {
75
- const matched = eventRunId === activeRunId;
76
- params.log?.debug?.(
77
- `[DingTalk] Dynamic reaction correlation by runId matched=${matched} activeRunId=${activeRunId} ` +
78
- `eventRunId=${eventRunId || "-"} eventSessionKey=${eventSessionKey || "-"}`,
79
- );
80
- return matched;
81
- }
82
-
83
- if (eventSessionKey === params.sessionKey) {
84
- if (eventRunId) {
85
- activeRunId = eventRunId;
86
- params.log?.debug?.(
87
- `[DingTalk] Dynamic reaction captured active runId=${activeRunId} from sessionKey=${params.sessionKey}`,
88
- );
89
- } else {
90
- params.log?.debug?.(
91
- `[DingTalk] Dynamic reaction correlated by sessionKey=${params.sessionKey} without runId`,
92
- );
93
- }
94
- return true;
95
- }
96
-
97
- if (
98
- optimisticCaptureCount === 0
99
- && eventStream === "lifecycle"
100
- && eventPhase === "start"
101
- && eventRunId
102
- && !eventSessionKey
103
- && Date.now() - params.createdAt <= params.optimisticCaptureWindowMs
104
- ) {
105
- optimisticCaptureCount += 1;
106
- activeRunId = eventRunId;
107
- params.log?.debug?.(
108
- `[DingTalk] Dynamic reaction optimistically captured active runId=${activeRunId} ` +
109
- `from first lifecycle event without sessionKey windowMs=${params.optimisticCaptureWindowMs}`,
110
- );
111
- return true;
112
- }
113
-
114
- if (!correlationUnavailableLogged && params.enabled) {
115
- correlationUnavailableLogged = true;
116
- params.log?.debug?.(
117
- `[DingTalk] Dynamic reaction ignored uncorrelated agent events; ` +
118
- `reason=${getErrorMessage(eventRunId || eventSessionKey || "waiting for sessionKey/runId match")}`,
119
- );
120
- }
121
- return false;
122
- };
123
- }