@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
@@ -251,6 +251,15 @@
251
251
  "default": 1000,
252
252
  "description": "Throttle interval in milliseconds between AI card streaming updates."
253
253
  },
254
+ "cardTaskProgress": {
255
+ "type": "boolean",
256
+ "description": "Live task-progress block on AI cards for long-running tasks. true forces it on, false forces it off; when unset it is on unless cardStreamingMode is explicitly set to off."
257
+ },
258
+ "cardTaskProgressRefresh": {
259
+ "type": "string",
260
+ "enum": ["heartbeat", "interval"],
261
+ "description": "How tool-driven progress changes reach the card. heartbeat (effective default) coalesces them into the 30-second refresh; interval pushes on every correlated tool event, throttled by cardStreamInterval."
262
+ },
254
263
  "aicardDegradeMs": {
255
264
  "type": "integer",
256
265
  "minimum": 60000,
@@ -283,7 +292,14 @@
283
292
  "type": "object",
284
293
  "description": "Status line visibility toggles for the AI card footer.",
285
294
  "additionalProperties": false,
286
- "default": { "model": true, "effort": true, "agent": true, "taskTime": false, "tokens": false, "dapiUsage": false },
295
+ "default": {
296
+ "model": true,
297
+ "effort": true,
298
+ "agent": true,
299
+ "taskTime": false,
300
+ "tokens": false,
301
+ "dapiUsage": false
302
+ },
287
303
  "properties": {
288
304
  "model": {
289
305
  "type": "boolean",
@@ -325,6 +341,54 @@
325
341
  "type": "boolean",
326
342
  "description": "Legacy compatibility field from older host or runtime integrations. It is not used by the current runtime and new configs should omit it."
327
343
  },
344
+ "gatewayCapabilities": {
345
+ "type": "object",
346
+ "additionalProperties": false,
347
+ "description": "Capability gates and allowlists for docs Gateway RPCs (dingtalk.docs.* / dingtalk-connector.docs.*) and proactive-send Gateway RPCs (dingtalk-connector.sendToUser/sendToGroup/send). All capabilities default to enabled; these RPCs rely on the host Gateway trust model and do not authenticate the caller.",
348
+ "properties": {
349
+ "tools": {
350
+ "type": "object",
351
+ "additionalProperties": false,
352
+ "description": "Capability switches. Omitted switches default to enabled.",
353
+ "properties": {
354
+ "docs": {
355
+ "type": "boolean",
356
+ "description": "Enable dingtalk.docs.* and dingtalk-connector.docs.* Gateway RPCs. Defaults to true when omitted."
357
+ },
358
+ "proactiveSend": {
359
+ "type": "boolean",
360
+ "description": "Enable dingtalk-connector.sendToUser/sendToGroup/send proactive-send RPCs. Defaults to true when omitted."
361
+ }
362
+ }
363
+ },
364
+ "docs": {
365
+ "type": "object",
366
+ "additionalProperties": false,
367
+ "description": "Docs RPC allowlist. When allowedSpaceIds is configured, requests without a spaceId (for example dingtalk.docs.append) are denied as well.",
368
+ "properties": {
369
+ "allowedSpaceIds": {
370
+ "type": "array",
371
+ "minItems": 1,
372
+ "items": { "type": "string", "minLength": 1 },
373
+ "description": "When set, docs RPCs only accept these spaceId values."
374
+ }
375
+ }
376
+ },
377
+ "send": {
378
+ "type": "object",
379
+ "additionalProperties": false,
380
+ "description": "Proactive-send target allowlist.",
381
+ "properties": {
382
+ "allowedTargets": {
383
+ "type": "array",
384
+ "minItems": 1,
385
+ "items": { "type": "string", "pattern": "^(user|group):\\S+$" },
386
+ "description": "When set, proactive-send RPCs only accept these user:<id> or group:<conversationId> targets."
387
+ }
388
+ }
389
+ }
390
+ }
391
+ },
328
392
  "accounts": {
329
393
  "type": "object",
330
394
  "description": "Named multi-account DingTalk configurations. Each account can override most top-level settings and inherits unspecified values from the top level.",
@@ -557,6 +621,15 @@
557
621
  "default": 1000,
558
622
  "description": "Throttle interval in milliseconds between AI card streaming updates."
559
623
  },
624
+ "cardTaskProgress": {
625
+ "type": "boolean",
626
+ "description": "Live task-progress block on AI cards for long-running tasks. true forces it on, false forces it off; when unset it is on unless cardStreamingMode is explicitly set to off."
627
+ },
628
+ "cardTaskProgressRefresh": {
629
+ "type": "string",
630
+ "enum": ["heartbeat", "interval"],
631
+ "description": "How tool-driven progress changes reach the card. heartbeat (effective default) coalesces them into the 30-second refresh; interval pushes on every correlated tool event, throttled by cardStreamInterval."
632
+ },
560
633
  "aicardDegradeMs": {
561
634
  "type": "integer",
562
635
  "minimum": 60000,
@@ -589,7 +662,14 @@
589
662
  "type": "object",
590
663
  "description": "Status line visibility toggles for the AI card footer.",
591
664
  "additionalProperties": false,
592
- "default": { "model": true, "effort": true, "agent": true, "taskTime": false, "tokens": false, "dapiUsage": false },
665
+ "default": {
666
+ "model": true,
667
+ "effort": true,
668
+ "agent": true,
669
+ "taskTime": false,
670
+ "tokens": false,
671
+ "dapiUsage": false
672
+ },
593
673
  "properties": {
594
674
  "model": {
595
675
  "type": "boolean",
@@ -622,6 +702,54 @@
622
702
  "description": "Show DingTalk API call count in AI card status line footer."
623
703
  }
624
704
  }
705
+ },
706
+ "gatewayCapabilities": {
707
+ "type": "object",
708
+ "additionalProperties": false,
709
+ "description": "Account-level capability gates for docs and proactive-send Gateway RPCs. Merged with the channel-level gatewayCapabilities by sub-key (tools / docs / send), so a channel-level allowlist is never dropped implicitly.",
710
+ "properties": {
711
+ "tools": {
712
+ "type": "object",
713
+ "additionalProperties": false,
714
+ "description": "Capability switches. Omitted switches default to enabled.",
715
+ "properties": {
716
+ "docs": {
717
+ "type": "boolean",
718
+ "description": "Enable dingtalk.docs.* and dingtalk-connector.docs.* Gateway RPCs. Defaults to true when omitted."
719
+ },
720
+ "proactiveSend": {
721
+ "type": "boolean",
722
+ "description": "Enable dingtalk-connector.sendToUser/sendToGroup/send proactive-send RPCs. Defaults to true when omitted."
723
+ }
724
+ }
725
+ },
726
+ "docs": {
727
+ "type": "object",
728
+ "additionalProperties": false,
729
+ "description": "Docs RPC allowlist. When allowedSpaceIds is configured, requests without a spaceId (for example dingtalk.docs.append) are denied as well.",
730
+ "properties": {
731
+ "allowedSpaceIds": {
732
+ "type": "array",
733
+ "minItems": 1,
734
+ "items": { "type": "string", "minLength": 1 },
735
+ "description": "When set, docs RPCs only accept these spaceId values."
736
+ }
737
+ }
738
+ },
739
+ "send": {
740
+ "type": "object",
741
+ "additionalProperties": false,
742
+ "description": "Proactive-send target allowlist.",
743
+ "properties": {
744
+ "allowedTargets": {
745
+ "type": "array",
746
+ "minItems": 1,
747
+ "items": { "type": "string", "pattern": "^(user|group):\\S+$" },
748
+ "description": "When set, proactive-send RPCs only accept these user:<id> or group:<conversationId> targets."
749
+ }
750
+ }
751
+ }
752
+ }
625
753
  }
626
754
  }
627
755
  }
@@ -778,6 +906,14 @@
778
906
  "label": "Card Stream Interval (ms)",
779
907
  "help": "Throttle interval in milliseconds between AI card incremental updates."
780
908
  },
909
+ "cardTaskProgress": {
910
+ "label": "Card Task Progress",
911
+ "help": "Show a live task-progress block on AI cards for long-running tasks. Leave empty to keep the default (on unless cardStreamingMode is explicitly off)."
912
+ },
913
+ "cardTaskProgressRefresh": {
914
+ "label": "Card Task Progress Refresh",
915
+ "help": "heartbeat (default) refreshes the progress block every 30 seconds with a predictable update count; interval also pushes every tool-driven stage change, throttled by cardStreamInterval."
916
+ },
781
917
  "aicardDegradeMs": {
782
918
  "label": "AI Card Degrade Window (ms)",
783
919
  "help": "Cooldown window after AI card trigger errors. Replies fall back to non-card delivery during this period."
@@ -838,6 +974,26 @@
838
974
  "label": "Legacy Async Mode",
839
975
  "help": "Deprecated compatibility field from older host or runtime integrations. The current runtime ignores it."
840
976
  },
977
+ "gatewayCapabilities": {
978
+ "label": "Gateway RPC Capability Gates",
979
+ "help": "Turn docs and proactive-send Gateway RPCs off, or restrict them to specific doc spaces and send targets."
980
+ },
981
+ "gatewayCapabilities.tools.docs": {
982
+ "label": "Docs RPC Enabled",
983
+ "help": "Allow dingtalk.docs.* and dingtalk-connector.docs.* Gateway RPCs. Defaults to enabled."
984
+ },
985
+ "gatewayCapabilities.tools.proactiveSend": {
986
+ "label": "Proactive Send RPC Enabled",
987
+ "help": "Allow dingtalk-connector.sendToUser/sendToGroup/send Gateway RPCs. Defaults to enabled."
988
+ },
989
+ "gatewayCapabilities.docs.allowedSpaceIds": {
990
+ "label": "Allowed Doc Space IDs",
991
+ "help": "When set, docs RPCs only accept these spaceId values; requests without a spaceId, such as dingtalk.docs.append, are denied."
992
+ },
993
+ "gatewayCapabilities.send.allowedTargets": {
994
+ "label": "Allowed Send Targets",
995
+ "help": "When set, proactive-send RPCs only accept these user:<id> or group:<conversationId> targets."
996
+ },
841
997
  "accounts": {
842
998
  "label": "Named Accounts",
843
999
  "help": "Create named DingTalk accounts when one plugin instance must serve multiple apps or tenants."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soimy/dingtalk",
3
- "version": "3.6.10",
3
+ "version": "3.7.0",
4
4
  "description": "DingTalk (钉钉) channel plugin for OpenClaw",
5
5
  "keywords": [
6
6
  "bot",
@@ -29,10 +29,6 @@
29
29
  "LICENSE"
30
30
  ],
31
31
  "type": "module",
32
- "engines": {
33
- "node": ">=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0"
34
- },
35
- "packageManager": "pnpm@10.33.0",
36
32
  "main": "./dist/index.js",
37
33
  "types": "./dist/index.d.ts",
38
34
  "exports": {
@@ -49,14 +45,14 @@
49
45
  "build": "pnpm run build:runtime && pnpm run build:types",
50
46
  "build:runtime": "node scripts/build-runtime.mjs",
51
47
  "build:types": "tsc -p tsconfig.build.json",
52
- "format": "oxfmt --write package.json tsconfig.json index.ts src/*.ts",
53
- "format:check": "oxfmt --check package.json tsconfig.json index.ts src/*.ts",
54
- "lint": "oxlint --type-aware index.ts src",
55
- "lint:fix": "oxlint --type-aware --fix index.ts src && pnpm format",
56
- "docs:dev": "vitepress dev docs --host 127.0.0.1 --port 8000",
57
- "docs:serv": "vitepress dev docs --host 127.0.0.1 --port 8000",
58
48
  "docs:build": "vitepress build docs",
49
+ "docs:dev": "vitepress dev docs --host 127.0.0.1 --port 8000",
59
50
  "docs:preview": "vitepress preview docs --host 127.0.0.1 --port 4173",
51
+ "docs:serv": "vitepress dev docs --host 127.0.0.1 --port 8000",
52
+ "format": "oxfmt --write package.json tsconfig.json openclaw.plugin.json vitest.config.ts index.ts \"src/**/*.ts\" \"scripts/**/*.mjs\"",
53
+ "format:check": "oxfmt --check package.json tsconfig.json openclaw.plugin.json vitest.config.ts index.ts \"src/**/*.ts\" \"scripts/**/*.mjs\"",
54
+ "lint": "oxlint --type-aware index.ts src",
55
+ "lint:fix": "oxlint --type-aware --fix index.ts src && pnpm format",
60
56
  "monitor:stream": "node scripts/dingtalk-stream-monitor.mjs",
61
57
  "pack:check": "node scripts/verify-runtime-package.mjs",
62
58
  "prepack": "pnpm run build",
@@ -76,7 +72,7 @@
76
72
  "@types/node": "^25.2.0",
77
73
  "@vitest/coverage-v8": "^3.2.4",
78
74
  "esbuild": "^0.27.3",
79
- "openclaw": "2026.7.1-2",
75
+ "openclaw": "2026.8.1",
80
76
  "oxfmt": "0.34.0",
81
77
  "oxlint": "1.49.0",
82
78
  "oxlint-tsgolint": "0.18.1",
@@ -85,19 +81,23 @@
85
81
  "vitest": "^3.2.4"
86
82
  },
87
83
  "peerDependencies": {
88
- "openclaw": ">=2026.7.1-2"
84
+ "openclaw": ">=2026.8.1"
89
85
  },
90
86
  "peerDependenciesMeta": {
91
87
  "openclaw": {
92
88
  "optional": true
93
89
  }
94
90
  },
91
+ "engines": {
92
+ "node": ">=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0"
93
+ },
94
+ "packageManager": "pnpm@10.33.0",
95
95
  "openclaw": {
96
96
  "compat": {
97
- "pluginApi": ">=2026.7.1-2"
97
+ "pluginApi": ">=2026.8.1"
98
98
  },
99
99
  "build": {
100
- "openclawVersion": "2026.7.1-2"
100
+ "openclawVersion": "2026.8.1"
101
101
  },
102
102
  "extensions": [
103
103
  "./index.ts"
@@ -123,7 +123,7 @@
123
123
  ]
124
124
  },
125
125
  "install": {
126
- "minHostVersion": ">=2026.7.1-2",
126
+ "minHostVersion": ">=2026.8.1",
127
127
  "npmSpec": "@soimy/dingtalk",
128
128
  "localPath": ".",
129
129
  "defaultChoice": "npm"
@@ -10,10 +10,70 @@ type EmojiMap = Record<SentenceType, readonly string[]>;
10
10
  const CATCHPHRASE = "叽 ";
11
11
 
12
12
  const KEYWORDS = {
13
- praise: ["真棒", "太好了", "厉害", "优秀", "聪明", "好样的", "赞", "牛", "完美", "出色", "真行", "干得漂亮", "天才", "棒极了"],
14
- blame: ["怎么又", "搞砸", "太差了", "烦死了", "讨厌", "笨", "蠢", "马虎", "不负责任", "乱来", "糟糕", "废物", "气死我了", "错了"],
15
- command: ["必须", "立刻", "马上", "赶紧", "不准", "不要", "别动", "别说", "别做", "快去", "去做", "给我", "听着", "站住", "闭嘴"],
16
- request: ["能不能", "可以吗", "好吗", "请", "麻烦", "帮个忙", "帮忙", "劳驾", "能否", "想请你", "能帮我", "借我", "方便吗"],
13
+ praise: [
14
+ "真棒",
15
+ "太好了",
16
+ "厉害",
17
+ "优秀",
18
+ "聪明",
19
+ "好样的",
20
+ "赞",
21
+ "牛",
22
+ "完美",
23
+ "出色",
24
+ "真行",
25
+ "干得漂亮",
26
+ "天才",
27
+ "棒极了",
28
+ ],
29
+ blame: [
30
+ "怎么又",
31
+ "搞砸",
32
+ "太差了",
33
+ "烦死了",
34
+ "讨厌",
35
+ "笨",
36
+ "蠢",
37
+ "马虎",
38
+ "不负责任",
39
+ "乱来",
40
+ "糟糕",
41
+ "废物",
42
+ "气死我了",
43
+ "错了",
44
+ ],
45
+ command: [
46
+ "必须",
47
+ "立刻",
48
+ "马上",
49
+ "赶紧",
50
+ "不准",
51
+ "不要",
52
+ "别动",
53
+ "别说",
54
+ "别做",
55
+ "快去",
56
+ "去做",
57
+ "给我",
58
+ "听着",
59
+ "站住",
60
+ "闭嘴",
61
+ ],
62
+ request: [
63
+ "能不能",
64
+ "可以吗",
65
+ "好吗",
66
+ "请",
67
+ "麻烦",
68
+ "帮个忙",
69
+ "帮忙",
70
+ "劳驾",
71
+ "能否",
72
+ "想请你",
73
+ "能帮我",
74
+ "借我",
75
+ "方便吗",
76
+ ],
17
77
  } as const;
18
78
 
19
79
  const POLITE_EXCLUSIONS = ["别客气", "别介意", "别见怪", "别担心", "别着急"] as const;
@@ -23,25 +83,34 @@ const EMOJIS: EmojiMap = {
23
83
  // Keep only the strings that survived repeated direct API retests against
24
84
  // the live endpoint using a real message target. Removed as unstable/bad:
25
85
  // `٩(๑>◡<๑)۶`.
26
- "夸奖": ["(๑•̀ㅂ•́)و✧", "(ノ≧∀≦)ノ", "(★▽★)", "(⌒▽⌒)☆", "(*≧ω≦)", "(ง •_•)ง", "ヾ(≧▽≦*)o"],
27
- "责怪": ["(╬ Ò﹏Ó)", "(╯°□°)╯", "(▼皿▼#)", "(。•́︿•̀。)", "(╥﹏╥)", "ヽ(`Д´)ノ", "(#><)", "(;′⌒`)"],
86
+ 夸奖: ["(๑•̀ㅂ•́)و✧", "(ノ≧∀≦)ノ", "(★▽★)", "(⌒▽⌒)☆", "(*≧ω≦)", "(ง •_•)ง", "ヾ(≧▽≦*)o"],
87
+ 责怪: [
88
+ "(╬ Ò﹏Ó)",
89
+ "(╯°□°)╯",
90
+ "(▼皿▼#)",
91
+ "(。•́︿•̀。)",
92
+ "(╥﹏╥)",
93
+ "ヽ(`Д´)ノ",
94
+ "(#><)",
95
+ "(;′⌒`)",
96
+ ],
28
97
  // Keep command kaomoji limited to strings that passed local
29
98
  // DingTalk emotion/reply verification. Removed as unstable/bad after
30
99
  // repeated direct API retests: `┌(┌ *`д´)┐`, `(•̀へ •́ ╮ )`.
31
- "命令": ["(¬_¬)", "(`ε´)", "(#`Д´)", "(●`∀´●)", "(`д´)", "( ̄ω ̄;)"],
100
+ 命令: ["(¬_¬)", "(`ε´)", "(#`Д´)", "(●`∀´●)", "(`д´)", "( ̄ω ̄;)"],
32
101
  // Narrative kaomoji must also stay within the subset accepted by
33
102
  // DingTalk emotion/reply. Removed as unstable/bad after repeated direct
34
103
  // API retests: `(´• ω •`)`.
35
- "叙事": ["(。・ω・。)", "( ̄▽ ̄)", "(・・?)", "(。_。)", "( ̄ω ̄)", "(´▽`)", "(=_=)"],
104
+ 叙事: ["(。・ω・。)", "( ̄▽ ̄)", "(・・?)", "(。_。)", "( ̄ω ̄)", "(´▽`)", "(=_=)"],
36
105
  // Request kaomoji must exclude strings that stayed unstable across
37
106
  // repeated direct API retests. Removed as unstable/bad:
38
107
  // `(づ。◕‿‿◕。)づ`, `(⁄ ⁄•⁄ω⁄•⁄ ⁄)`.
39
- "请求": ["(っ´∀`)っ", "(๑•̀ω•́๑)✧", "(p≧w≦q)", "(♡˙︶˙♡)", "(´;ω;`)", "(人•ᴗ•✿)"],
40
- "未知": ["(•̀_•́)", "(;一_一)", "(???)"],
108
+ 请求: ["(っ´∀`)っ", "(๑•̀ω•́๑)✧", "(p≧w≦q)", "(♡˙︶˙♡)", "(´;ω;`)", "(人•ᴗ•✿)"],
109
+ 未知: ["(•̀_•́)", "(;一_一)", "(???)"],
41
110
  };
42
111
 
43
112
  function containsAny(text: string, words: readonly string[]): boolean {
44
- return words.some(word => text.includes(word));
113
+ return words.some((word) => text.includes(word));
45
114
  }
46
115
 
47
116
  export function classifyAckReactionEmoji(sentence: unknown): AckReactionClassifyResult {
@@ -50,7 +119,7 @@ export function classifyAckReactionEmoji(sentence: unknown): AckReactionClassify
50
119
  }
51
120
 
52
121
  const s = sentence.trim();
53
- const isPolitePhrase = POLITE_EXCLUSIONS.some(phrase => s.includes(phrase));
122
+ const isPolitePhrase = POLITE_EXCLUSIONS.some((phrase) => s.includes(phrase));
54
123
  const isQuestion = /吗|呢|?|\?/.test(s);
55
124
  const startsWithPlease = s.startsWith("请");
56
125
  const hasExclamation = /[!!]/.test(s);
@@ -63,7 +132,10 @@ export function classifyAckReactionEmoji(sentence: unknown): AckReactionClassify
63
132
  type = "命令";
64
133
  } else if (containsAny(s, KEYWORDS.praise)) {
65
134
  type = "夸奖";
66
- } else if (containsAny(s, KEYWORDS.blame) || (hasExclamation && /烦|讨厌|笨|蠢|差|气死/.test(s))) {
135
+ } else if (
136
+ containsAny(s, KEYWORDS.blame) ||
137
+ (hasExclamation && /烦|讨厌|笨|蠢|差|气死/.test(s))
138
+ ) {
67
139
  type = "责怪";
68
140
  } else {
69
141
  type = "叙事";
@@ -1,7 +1,11 @@
1
- import axios from "./http-client";
2
- import { getAccessToken } from "./auth";
3
- import type { DingTalkConfig } from "./types";
4
- import { formatDingTalkErrorPayloadLog, getErrorMessage, getProxyBypassOption } from "./utils";
1
+ import { getAccessToken } from "../platform/auth";
2
+ import type { DingTalkConfig } from "../platform/types";
3
+ import axios from "../shared/http-client";
4
+ import {
5
+ formatDingTalkErrorPayloadLog,
6
+ getErrorMessage,
7
+ getProxyBypassOption,
8
+ } from "../shared/utils";
5
9
 
6
10
  // DingTalk currently exposes a dedicated native "thinking" reaction flow rather than
7
11
  // a generic arbitrary-emoji reaction API for this plugin path.
@@ -114,7 +118,9 @@ function isRetryableEmotionApiError(err: unknown): boolean {
114
118
  const response = asRecord(asRecord(err)?.response);
115
119
  const data = asRecord(response?.data);
116
120
  const status = Number(response?.status ?? 0);
117
- const errorCode = String(data?.code || "").trim().toLowerCase();
121
+ const errorCode = String(data?.code || "")
122
+ .trim()
123
+ .toLowerCase();
118
124
  if (!response) {
119
125
  return true;
120
126
  }
@@ -136,7 +142,7 @@ export async function attachNativeAckReaction(
136
142
  for (let index = 0; index < THINKING_REACTION_ATTACH_DELAYS_MS.length; index += 1) {
137
143
  const delayMs = THINKING_REACTION_ATTACH_DELAYS_MS[index];
138
144
  if (delayMs > 0) {
139
- await new Promise(resolve => setTimeout(resolve, delayMs));
145
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
140
146
  }
141
147
  const attempt = index + 1;
142
148
  const attemptLabel = `${attempt}/${THINKING_REACTION_ATTACH_DELAYS_MS.length}`;
@@ -191,7 +197,7 @@ export async function recallNativeAckReactionWithRetry(
191
197
  }
192
198
  for (const delayMs of THINKING_REACTION_RECALL_DELAYS_MS) {
193
199
  if (delayMs > 0) {
194
- await new Promise(resolve => setTimeout(resolve, delayMs));
200
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
195
201
  }
196
202
  if (await recallNativeAckReaction(config, request, log)) {
197
203
  return;
@@ -1,15 +1,17 @@
1
- import { attachNativeAckReaction, recallNativeAckReactionWithRetry } from "../ack-reaction-service";
2
- import type { DingTalkConfig } from "../types";
3
- import { getErrorMessage } from "../utils";
4
1
  import {
5
- createDynamicAckReactionCorrelator,
2
+ createAgentEventCorrelator,
6
3
  describeEvent,
7
- type DynamicAckReactionLogger,
8
4
  type RuntimeAgentEvent,
5
+ type RuntimeEventsLogger,
9
6
  type RuntimeEventsSurface,
10
- } from "./dynamic-ack-reaction-events";
7
+ } from "../platform/runtime-events";
8
+ import type { DingTalkConfig } from "../platform/types";
9
+ import { getErrorMessage } from "../shared/utils";
10
+ import { attachNativeAckReaction, recallNativeAckReactionWithRetry } from "./ack-reaction-service";
11
11
  import { resolveToolProgressReaction } from "./dynamic-ack-reaction-progress";
12
12
 
13
+ const CORRELATION_CONSUMER = "ack-reaction";
14
+
13
15
  type DynamicAckReactionControllerParams = {
14
16
  enabled: boolean;
15
17
  initialReaction: string;
@@ -19,7 +21,7 @@ type DynamicAckReactionControllerParams = {
19
21
  msgId: string;
20
22
  conversationId: string;
21
23
  sessionKey: string;
22
- log?: DynamicAckReactionLogger;
24
+ log?: RuntimeEventsLogger;
23
25
  runtimeEvents?: RuntimeEventsSurface;
24
26
  onReactionDisposed?: () => void;
25
27
  };
@@ -42,7 +44,8 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
42
44
  let lastDynamicReactionSwitchAt = 0;
43
45
  let disposed = false;
44
46
  const createdAt = Date.now();
45
- const isCorrelatedEvent = createDynamicAckReactionCorrelator({
47
+ const isCorrelatedEvent = createAgentEventCorrelator({
48
+ consumer: CORRELATION_CONSUMER,
46
49
  sessionKey: params.sessionKey,
47
50
  enabled: params.enabled,
48
51
  createdAt,
@@ -55,7 +58,7 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
55
58
  if (disposed || !normalizedReaction || !params.enabled || !ackReactionAttached) {
56
59
  params.log?.debug?.(
57
60
  `[DingTalk] Dynamic ack reaction update skipped reaction=${normalizedReaction || "-"} ` +
58
- `enabled=${params.enabled} ackReactionAttached=${ackReactionAttached} disposed=${disposed}`,
61
+ `enabled=${params.enabled} ackReactionAttached=${ackReactionAttached} disposed=${disposed}`,
59
62
  );
60
63
  return;
61
64
  }
@@ -70,12 +73,12 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
70
73
  return;
71
74
  }
72
75
  if (
73
- lastDynamicReactionSwitchAt > 0
74
- && Date.now() - lastDynamicReactionSwitchAt < DYNAMIC_REACTION_MIN_SWITCH_INTERVAL_MS
76
+ lastDynamicReactionSwitchAt > 0 &&
77
+ Date.now() - lastDynamicReactionSwitchAt < DYNAMIC_REACTION_MIN_SWITCH_INTERVAL_MS
75
78
  ) {
76
79
  params.log?.debug?.(
77
80
  `[DingTalk] Dynamic ack reaction update throttled previous=${currentAckReaction} next=${normalizedReaction} ` +
78
- `minIntervalMs=${DYNAMIC_REACTION_MIN_SWITCH_INTERVAL_MS}`,
81
+ `minIntervalMs=${DYNAMIC_REACTION_MIN_SWITCH_INTERVAL_MS}`,
79
82
  );
80
83
  return;
81
84
  }
@@ -150,7 +153,9 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
150
153
  dynamicReactionUpdatePromise = dynamicReactionUpdatePromise
151
154
  .then(() => updateDynamicAckReaction(nextReaction))
152
155
  .catch((err: unknown) => {
153
- params.log?.warn?.(`[DingTalk] Dynamic ack reaction update failed: ${getErrorMessage(err)}`);
156
+ params.log?.warn?.(
157
+ `[DingTalk] Dynamic ack reaction update failed: ${getErrorMessage(err)}`,
158
+ );
154
159
  });
155
160
  return dynamicReactionUpdatePromise;
156
161
  };
@@ -160,7 +165,9 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
160
165
  if (!params.enabled || disposed) {
161
166
  return;
162
167
  }
163
- params.log?.debug?.(`[DingTalk] Dynamic reaction observed agent event ${describeEvent(agentEvent)}`);
168
+ params.log?.debug?.(
169
+ `[DingTalk] Dynamic reaction observed agent event ${describeEvent(agentEvent)}`,
170
+ );
164
171
  if (agentEvent?.stream === "lifecycle" && agentEvent.data?.phase === "start") {
165
172
  void isCorrelatedEvent(agentEvent);
166
173
  return;
@@ -174,7 +181,8 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
174
181
  );
175
182
  return;
176
183
  }
177
- const toolCallId = typeof agentEvent.data?.toolCallId === "string" ? agentEvent.data.toolCallId : "-";
184
+ const toolCallId =
185
+ typeof agentEvent.data?.toolCallId === "string" ? agentEvent.data.toolCallId : "-";
178
186
  params.log?.debug?.(
179
187
  `[DingTalk] Tool event received for dynamic ack reaction: name=${agentEvent.data?.name || "-"} toolCallId=${toolCallId}`,
180
188
  );
@@ -183,26 +191,31 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
183
191
  );
184
192
  };
185
193
 
186
- const unsubscribeAgentEvents = params.enabled && params.runtimeEvents?.onAgentEvent
187
- ? params.runtimeEvents.onAgentEvent((event: unknown) => {
188
- void handleAgentEvent(event).catch((err: unknown) => {
189
- params.log?.warn?.(`[DingTalk] Dynamic ack reaction event handling failed: ${getErrorMessage(err)}`);
190
- });
191
- })
192
- : () => {};
194
+ const unsubscribeAgentEvents =
195
+ params.enabled && params.runtimeEvents?.onAgentEvent
196
+ ? params.runtimeEvents.onAgentEvent((event: unknown) => {
197
+ void handleAgentEvent(event).catch((err: unknown) => {
198
+ params.log?.warn?.(
199
+ `[DingTalk] Dynamic ack reaction event handling failed: ${getErrorMessage(err)}`,
200
+ );
201
+ });
202
+ })
203
+ : () => {};
193
204
 
194
205
  if (params.enabled && !params.runtimeEvents?.onAgentEvent) {
195
- params.log?.debug?.("[DingTalk] onAgentEvent not available, dynamic reaction tracking disabled");
206
+ params.log?.debug?.(
207
+ "[DingTalk] onAgentEvent not available, dynamic reaction tracking disabled",
208
+ );
196
209
  }
197
210
 
198
211
  if (params.enabled) {
199
212
  progressHeartbeatTimer = setInterval(() => {
200
213
  if (
201
- disposed
202
- || !ackReactionAttached
203
- || progressHeartbeatInFlight
204
- || dynamicReactionStartedAt === 0
205
- || lastDynamicReactionAt === 0
214
+ disposed ||
215
+ !ackReactionAttached ||
216
+ progressHeartbeatInFlight ||
217
+ dynamicReactionStartedAt === 0 ||
218
+ lastDynamicReactionAt === 0
206
219
  ) {
207
220
  return;
208
221
  }
@@ -211,7 +224,7 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
211
224
  }
212
225
  params.log?.debug?.(
213
226
  `[DingTalk] Dynamic ack reaction heartbeat triggered currentReaction=${currentAckReaction} ` +
214
- `lastDynamicReactionAt=${lastDynamicReactionAt}`,
227
+ `lastDynamicReactionAt=${lastDynamicReactionAt}`,
215
228
  );
216
229
  progressHeartbeatInFlight = true;
217
230
  void queueDynamicAckReactionUpdate(TOOL_HEARTBEAT_REACTION).finally(() => {
@@ -248,7 +261,7 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
248
261
  const elapsedMs = ackReactionAttachedAt > 0 ? Date.now() - ackReactionAttachedAt : 0;
249
262
  const remainingVisibleMs = shouldRespectMinVisible ? minVisibleMs - elapsedMs : 0;
250
263
  if (remainingVisibleMs > 0) {
251
- await new Promise(resolve => setTimeout(resolve, remainingVisibleMs));
264
+ await new Promise((resolve) => setTimeout(resolve, remainingVisibleMs));
252
265
  }
253
266
  await recallNativeAckReactionWithRetry(
254
267
  params.dingtalkConfig,
@@ -260,7 +273,9 @@ export function createDynamicAckReactionController(params: DynamicAckReactionCon
260
273
  params.log,
261
274
  );
262
275
  } catch (err: unknown) {
263
- params.log?.warn?.(`[DingTalk] Dynamic ack reaction dispose recall failed: ${getErrorMessage(err)}`);
276
+ params.log?.warn?.(
277
+ `[DingTalk] Dynamic ack reaction dispose recall failed: ${getErrorMessage(err)}`,
278
+ );
264
279
  } finally {
265
280
  ackReactionAttached = false;
266
281
  params.onReactionDisposed?.();
@@ -27,7 +27,10 @@ export function resolveToolProgressReaction(toolName: unknown, args: unknown): s
27
27
  if (!command) {
28
28
  return "🛠️";
29
29
  }
30
- if (/\bbrew\s+install\s+/i.test(command) || /\b(?:pnpm|npm|yarn)\s+(?:add|install)\s+/i.test(command)) {
30
+ if (
31
+ /\bbrew\s+install\s+/i.test(command) ||
32
+ /\b(?:pnpm|npm|yarn)\s+(?:add|install)\s+/i.test(command)
33
+ ) {
31
34
  return "📦";
32
35
  }
33
36
  if (/\bwhich\s+/i.test(command)) {