@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.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11655 -10324
- package/dist/src/{ack-reaction-service.d.ts → ack-reaction/ack-reaction-service.d.ts} +1 -1
- package/dist/src/ack-reaction/dynamic-ack-reaction-controller.d.ts +3 -3
- package/dist/src/card/ask-user-question-context.d.ts +1 -1
- package/dist/src/card/ask-user-question-store.d.ts +1 -1
- package/dist/src/card/ask-user-question.d.ts +1 -1
- package/dist/src/card/card-action-handler.d.ts +2 -2
- package/dist/src/{card-draft-controller.d.ts → card/card-draft-controller.d.ts} +5 -1
- package/dist/src/card/card-run-registry.d.ts +2 -2
- package/dist/src/{card-service.d.ts → card/card-service.d.ts} +1 -1
- package/dist/src/card/card-stop-handler.d.ts +1 -1
- package/dist/src/card/card-streaming-mode.d.ts +1 -1
- package/dist/src/card/card-task-progress.d.ts +60 -0
- package/dist/src/channel.d.ts +6 -6
- package/dist/src/command/card-stop-command.d.ts +1 -1
- package/dist/src/{feedback-learning-service.d.ts → command/feedback-learning-service.d.ts} +1 -1
- package/dist/src/command/inbound-command-dispatch-service.d.ts +2 -2
- package/dist/src/{learning-command-service.d.ts → command/learning-command-service.d.ts} +1 -1
- package/dist/src/gateway/channel-gateway.d.ts +1 -1
- package/dist/src/{connection-manager.d.ts → gateway/connection-manager.d.ts} +1 -1
- package/dist/src/{docs-service.d.ts → gateway/docs-service.d.ts} +5 -5
- package/dist/src/{inbound-handler.d.ts → gateway/inbound-handler.d.ts} +1 -1
- package/dist/src/gateway/inbound-session-queue-dispatcher.d.ts +1 -1
- package/dist/src/messaging/attachment-text-extractor.d.ts +1 -1
- package/dist/src/messaging/btw-deliver.d.ts +1 -1
- package/dist/src/messaging/channel-outbound.d.ts +1 -1
- package/dist/src/messaging/inline-directives.d.ts +42 -0
- package/dist/src/{media-utils.d.ts → messaging/media-utils.d.ts} +1 -1
- package/dist/src/{message-context-store.d.ts → messaging/message-context-store.d.ts} +7 -1
- package/dist/src/{message-utils.d.ts → messaging/message-utils.d.ts} +1 -1
- package/dist/src/messaging/quoted-context.d.ts +2 -2
- package/dist/src/messaging/quoted-file-service.d.ts +1 -1
- package/dist/src/messaging/quoted-ref.d.ts +2 -2
- package/dist/src/{reply-strategy-card.d.ts → messaging/reply-strategy-card.d.ts} +1 -1
- package/dist/src/{reply-strategy-types.d.ts → messaging/reply-strategy-types.d.ts} +10 -1
- package/dist/src/{reply-strategy-with-reaction.d.ts → messaging/reply-strategy-with-reaction.d.ts} +1 -1
- package/dist/src/{reply-strategy.d.ts → messaging/reply-strategy.d.ts} +1 -1
- package/dist/src/{send-service.d.ts → messaging/send-service.d.ts} +1 -1
- package/dist/src/platform/channel-status.d.ts +1 -1
- package/dist/src/platform/config.d.ts +110 -0
- package/dist/src/platform/runtime-events.d.ts +67 -0
- package/dist/src/{types.d.ts → platform/types.d.ts} +75 -2
- package/dist/src/{persistence-store.d.ts → shared/persistence-store.d.ts} +1 -1
- package/dist/src/{utils.d.ts → shared/utils.d.ts} +1 -1
- package/dist/src/targeting/agent-name-matcher.d.ts +1 -1
- package/dist/src/targeting/agent-routing.d.ts +3 -3
- package/dist/src/{session-routing.d.ts → targeting/session-routing.d.ts} +1 -1
- package/index.ts +193 -69
- package/openclaw.plugin.json +158 -2
- package/package.json +16 -16
- package/src/{ack-reaction-classifier.ts → ack-reaction/ack-reaction-classifier.ts} +85 -13
- package/src/{ack-reaction-service.ts → ack-reaction/ack-reaction-service.ts} +13 -7
- package/src/ack-reaction/dynamic-ack-reaction-controller.ts +46 -31
- package/src/ack-reaction/dynamic-ack-reaction-progress.ts +4 -1
- package/src/card/ask-user-question-context.ts +1 -1
- package/src/card/ask-user-question-store.ts +2 -2
- package/src/card/ask-user-question.ts +11 -7
- package/src/card/card-action-handler.ts +2 -2
- package/src/{card-callback-service.ts → card/card-callback-service.ts} +7 -9
- package/src/card/card-draft-controller.ts +770 -0
- package/src/card/card-markdown-image-reroute.ts +8 -2
- package/src/card/card-run-registry.ts +17 -7
- package/src/{card-service.ts → card/card-service.ts} +91 -61
- package/src/card/card-stop-handler.ts +12 -8
- package/src/card/card-streaming-mode.ts +1 -1
- package/src/card/card-task-progress.ts +330 -0
- package/src/card/draft-stream-loop.ts +119 -0
- package/src/card/reasoning-answer-split.ts +125 -121
- package/src/card/reasoning-block-assembler.ts +122 -123
- package/src/{run-usage-store.ts → card/run-usage-store.ts} +18 -6
- package/src/card/statusline-renderer.ts +14 -5
- package/src/card/task-model-metadata.ts +8 -3
- package/src/channel.ts +14 -16
- package/src/command/card-stop-command.ts +2 -2
- package/src/{feedback-learning-service.ts → command/feedback-learning-service.ts} +36 -16
- package/src/{feedback-learning-store.ts → command/feedback-learning-store.ts} +133 -81
- package/src/command/inbound-command-dispatch-service.ts +12 -6
- package/src/{learning-command-service.ts → command/learning-command-service.ts} +36 -29
- package/src/{session-command-service.ts → command/session-command-service.ts} +3 -1
- package/src/gateway/channel-gateway.ts +106 -28
- package/src/{connection-manager.ts → gateway/connection-manager.ts} +45 -30
- package/src/{docs-service.ts → gateway/docs-service.ts} +5 -5
- package/src/{inbound-handler.ts → gateway/inbound-handler.ts} +144 -85
- package/src/gateway/inbound-session-queue-dispatcher.ts +18 -14
- package/src/{session-lock.ts → gateway/session-lock.ts} +14 -14
- package/src/messaging/attachment-text-extractor.ts +21 -7
- package/src/messaging/btw-deliver.ts +2 -2
- package/src/messaging/channel-actions.ts +15 -7
- package/src/messaging/channel-outbound.ts +16 -11
- package/src/messaging/inline-directives.ts +342 -0
- package/src/{media-utils.ts → messaging/media-utils.ts} +276 -72
- package/src/{message-context-store.ts → messaging/message-context-store.ts} +11 -2
- package/src/{message-utils.ts → messaging/message-utils.ts} +46 -11
- package/src/messaging/quoted-context.ts +2 -2
- package/src/messaging/quoted-file-service.ts +311 -294
- package/src/messaging/quoted-ref.ts +11 -7
- package/src/{reply-strategy-card.ts → messaging/reply-strategy-card.ts} +178 -101
- package/src/{reply-strategy-markdown.ts → messaging/reply-strategy-markdown.ts} +8 -2
- package/src/{reply-strategy-types.ts → messaging/reply-strategy-types.ts} +11 -1
- package/src/{reply-strategy-with-reaction.ts → messaging/reply-strategy-with-reaction.ts} +6 -1
- package/src/{reply-strategy.ts → messaging/reply-strategy.ts} +2 -2
- package/src/{send-service.ts → messaging/send-service.ts} +100 -65
- package/src/{auth.ts → platform/auth.ts} +2 -2
- package/src/platform/channel-status.ts +3 -3
- package/src/{config-schema.ts → platform/config-schema.ts} +81 -4
- package/src/{config.ts → platform/config.ts} +167 -19
- package/src/{device-registration.ts → platform/device-registration.ts} +5 -4
- package/src/{onboarding.ts → platform/onboarding.ts} +1 -4
- package/src/{plugin-sdk-channel-actions-augment.ts → platform/plugin-sdk-channel-actions-augment.ts} +6 -5
- package/src/platform/runtime-events.ts +202 -0
- package/src/{secret-input.ts → platform/secret-input.ts} +117 -11
- package/src/{types.ts → platform/types.ts} +77 -2
- package/src/{http-client.ts → shared/http-client.ts} +1 -3
- package/src/{persistence-store.ts → shared/persistence-store.ts} +6 -6
- package/src/{utils.ts → shared/utils.ts} +30 -13
- package/src/targeting/agent-name-matcher.ts +1 -1
- package/src/targeting/agent-routing.ts +7 -7
- package/src/targeting/group-members-store.ts +1 -1
- package/src/{session-peer-store.ts → targeting/session-peer-store.ts} +9 -4
- package/src/{session-routing.ts → targeting/session-routing.ts} +1 -1
- package/src/targeting/target-directory-adapter.ts +3 -3
- package/src/targeting/target-directory-store.ts +1 -1
- package/src/targeting/target-input.ts +1 -1
- package/dist/src/ack-reaction/dynamic-ack-reaction-events.d.ts +0 -35
- package/dist/src/config.d.ts +0 -59
- package/src/ack-reaction/dynamic-ack-reaction-events.ts +0 -123
- package/src/card-draft-controller.ts +0 -637
- package/src/draft-stream-loop.ts +0 -119
- /package/dist/src/{ack-reaction-classifier.d.ts → ack-reaction/ack-reaction-classifier.d.ts} +0 -0
- /package/dist/src/{card-callback-service.d.ts → card/card-callback-service.d.ts} +0 -0
- /package/dist/src/{draft-stream-loop.d.ts → card/draft-stream-loop.d.ts} +0 -0
- /package/dist/src/{run-usage-store.d.ts → card/run-usage-store.d.ts} +0 -0
- /package/dist/src/{feedback-learning-store.d.ts → command/feedback-learning-store.d.ts} +0 -0
- /package/dist/src/{session-command-service.d.ts → command/session-command-service.d.ts} +0 -0
- /package/dist/src/{session-lock.d.ts → gateway/session-lock.d.ts} +0 -0
- /package/dist/src/{proactive-risk-registry.d.ts → messaging/proactive-risk-registry.d.ts} +0 -0
- /package/dist/src/{reply-strategy-markdown.d.ts → messaging/reply-strategy-markdown.d.ts} +0 -0
- /package/dist/src/{access-control.d.ts → platform/access-control.d.ts} +0 -0
- /package/dist/src/{auth.d.ts → platform/auth.d.ts} +0 -0
- /package/dist/src/{config-schema.d.ts → platform/config-schema.d.ts} +0 -0
- /package/dist/src/{device-registration.d.ts → platform/device-registration.d.ts} +0 -0
- /package/dist/src/{logger-context.d.ts → platform/logger-context.d.ts} +0 -0
- /package/dist/src/{onboarding.d.ts → platform/onboarding.d.ts} +0 -0
- /package/dist/src/{plugin-sdk-channel-actions-augment.d.ts → platform/plugin-sdk-channel-actions-augment.d.ts} +0 -0
- /package/dist/src/{runtime.d.ts → platform/runtime.d.ts} +0 -0
- /package/dist/src/{secret-input.d.ts → platform/secret-input.d.ts} +0 -0
- /package/dist/src/{session-state.d.ts → platform/session-state.d.ts} +0 -0
- /package/dist/src/{signature.d.ts → platform/signature.d.ts} +0 -0
- /package/dist/src/{dedup.d.ts → shared/dedup.d.ts} +0 -0
- /package/dist/src/{http-client.d.ts → shared/http-client.d.ts} +0 -0
- /package/dist/src/{path-utils.d.ts → shared/path-utils.d.ts} +0 -0
- /package/dist/src/{peer-id-registry.d.ts → targeting/peer-id-registry.d.ts} +0 -0
- /package/dist/src/{session-peer-store.d.ts → targeting/session-peer-store.d.ts} +0 -0
- /package/src/{proactive-risk-registry.ts → messaging/proactive-risk-registry.ts} +0 -0
- /package/src/{access-control.ts → platform/access-control.ts} +0 -0
- /package/src/{logger-context.ts → platform/logger-context.ts} +0 -0
- /package/src/{runtime.ts → platform/runtime.ts} +0 -0
- /package/src/{session-state.ts → platform/session-state.ts} +0 -0
- /package/src/{signature.ts → platform/signature.ts} +0 -0
- /package/src/{dedup.ts → shared/dedup.ts} +0 -0
- /package/src/{path-utils.ts → shared/path-utils.ts} +0 -0
- /package/src/{peer-id-registry.ts → targeting/peer-id-registry.ts} +0 -0
|
@@ -1,46 +1,42 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
import { resolveCardRunByConversation, resolveCardRunByOwner } from "../card/card-run-registry";
|
|
3
|
+
import { isCardInTerminalState, sendProactiveCardText } from "../card/card-service";
|
|
4
|
+
import { getAccessToken } from "../platform/auth";
|
|
5
|
+
import { resolveRobotCode, stripTargetPrefix } from "../platform/config";
|
|
6
|
+
import { getLogger } from "../platform/logger-context";
|
|
7
|
+
import type {
|
|
8
|
+
AICardInstance,
|
|
9
|
+
AxiosResponse,
|
|
10
|
+
DingTalkConfig,
|
|
11
|
+
DingTalkTrackingMetadata,
|
|
12
|
+
Logger,
|
|
13
|
+
ProactiveMessagePayload,
|
|
14
|
+
QuotedRef,
|
|
15
|
+
SendMessageOptions,
|
|
16
|
+
SessionWebhookResponse,
|
|
17
|
+
} from "../platform/types";
|
|
18
|
+
import axios from "../shared/http-client";
|
|
19
|
+
import { formatDingTalkErrorPayloadLog, getProxyBypassOption } from "../shared/utils";
|
|
20
|
+
import { resolveOriginalPeerId } from "../targeting/peer-id-registry";
|
|
12
21
|
import {
|
|
13
22
|
getVoiceDurationMs,
|
|
14
23
|
prepareMediaInput,
|
|
15
24
|
resolveOutboundMediaType,
|
|
16
25
|
uploadMedia as uploadMediaUtil,
|
|
17
26
|
} from "./media-utils";
|
|
18
|
-
import {
|
|
27
|
+
import type { UploadMediaResult } from "./media-utils";
|
|
19
28
|
import {
|
|
20
29
|
DEFAULT_MESSAGE_CONTEXT_TTL_DAYS,
|
|
21
30
|
DEFAULT_OUTBOUND_SENDER,
|
|
22
31
|
inferConversationChatType,
|
|
23
32
|
upsertOutboundMessageContext,
|
|
24
33
|
} from "./message-context-store";
|
|
25
|
-
import {
|
|
34
|
+
import { convertMarkdownTablesToPlainText, detectMarkdownAndExtractTitle } from "./message-utils";
|
|
26
35
|
import {
|
|
27
36
|
deleteProactiveRiskObservation,
|
|
28
37
|
getProactiveRiskObservation,
|
|
29
38
|
recordProactiveRiskObservation,
|
|
30
39
|
} from "./proactive-risk-registry";
|
|
31
|
-
import { formatDingTalkErrorPayloadLog, getProxyBypassOption } from "./utils";
|
|
32
|
-
import type { UploadMediaResult } from "./media-utils";
|
|
33
|
-
import type {
|
|
34
|
-
AICardInstance,
|
|
35
|
-
AxiosResponse,
|
|
36
|
-
DingTalkConfig,
|
|
37
|
-
DingTalkTrackingMetadata,
|
|
38
|
-
Logger,
|
|
39
|
-
ProactiveMessagePayload,
|
|
40
|
-
QuotedRef,
|
|
41
|
-
SendMessageOptions,
|
|
42
|
-
SessionWebhookResponse,
|
|
43
|
-
} from "./types";
|
|
44
40
|
|
|
45
41
|
export { detectMediaTypeFromExtension } from "./media-utils";
|
|
46
42
|
|
|
@@ -93,7 +89,9 @@ async function replaceMarkdownLocalImages(params: {
|
|
|
93
89
|
|
|
94
90
|
type ProactiveTextSendResult = AxiosResponse | { tracking: DingTalkTrackingMetadata };
|
|
95
91
|
|
|
96
|
-
function isTrackingResult(
|
|
92
|
+
function isTrackingResult(
|
|
93
|
+
result: ProactiveTextSendResult,
|
|
94
|
+
): result is { tracking: DingTalkTrackingMetadata } {
|
|
97
95
|
return "tracking" in result;
|
|
98
96
|
}
|
|
99
97
|
|
|
@@ -120,7 +118,12 @@ function extractOutboundDeliveryMetadata(payload: unknown): {
|
|
|
120
118
|
data.tracking && typeof data.tracking === "object"
|
|
121
119
|
? (data.tracking as Record<string, unknown>)
|
|
122
120
|
: undefined;
|
|
123
|
-
const messageId = firstTrimmedString(
|
|
121
|
+
const messageId = firstTrimmedString(
|
|
122
|
+
data.messageId,
|
|
123
|
+
data.msgid,
|
|
124
|
+
tracking?.messageId,
|
|
125
|
+
tracking?.msgid,
|
|
126
|
+
);
|
|
124
127
|
const processQueryKey = firstTrimmedString(data.processQueryKey, tracking?.processQueryKey);
|
|
125
128
|
const outTrackId = firstTrimmedString(data.outTrackId, tracking?.outTrackId);
|
|
126
129
|
const cardInstanceId = firstTrimmedString(data.cardInstanceId, tracking?.cardInstanceId);
|
|
@@ -152,8 +155,8 @@ function persistOutboundMessageContext(params: {
|
|
|
152
155
|
}
|
|
153
156
|
params.log?.debug?.(
|
|
154
157
|
`[DingTalk][QuotedRef][Persist] direction=outbound scope=${params.conversationId} ` +
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
`messageType=${params.messageType || "(none)"} processQueryKey=${params.delivery.processQueryKey || "(none)"} ` +
|
|
159
|
+
`messageId=${params.delivery.messageId || "(none)"} quotedRef=${params.quotedRef ? JSON.stringify(params.quotedRef) : "(none)"}`,
|
|
157
160
|
);
|
|
158
161
|
upsertOutboundMessageContext({
|
|
159
162
|
storePath: params.storePath,
|
|
@@ -182,7 +185,9 @@ function buildPersistedOutboundText(text: string, options: SendMessageOptions):
|
|
|
182
185
|
return text;
|
|
183
186
|
}
|
|
184
187
|
|
|
185
|
-
function shouldRouteSessionMediaViaProactive(
|
|
188
|
+
function shouldRouteSessionMediaViaProactive(
|
|
189
|
+
mediaType?: string | null,
|
|
190
|
+
): mediaType is "voice" | "video" | "file" {
|
|
186
191
|
return mediaType === "voice" || mediaType === "video" || mediaType === "file";
|
|
187
192
|
}
|
|
188
193
|
|
|
@@ -273,12 +278,9 @@ function summarizeSessionWebhookResponse(data: unknown): string {
|
|
|
273
278
|
}
|
|
274
279
|
const payload = data as Record<string, unknown>;
|
|
275
280
|
const code = extractErrorCodeFromResponseData(payload) || "(none)";
|
|
276
|
-
const message =
|
|
277
|
-
payload.message,
|
|
278
|
-
|
|
279
|
-
payload.msg,
|
|
280
|
-
payload.errorMessage,
|
|
281
|
-
) || "(none)";
|
|
281
|
+
const message =
|
|
282
|
+
firstTrimmedString(payload.message, payload.errmsg, payload.msg, payload.errorMessage) ||
|
|
283
|
+
"(none)";
|
|
282
284
|
const success =
|
|
283
285
|
typeof payload.success === "boolean"
|
|
284
286
|
? String(payload.success)
|
|
@@ -294,21 +296,15 @@ function summarizeSessionWebhookResponse(data: unknown): string {
|
|
|
294
296
|
);
|
|
295
297
|
}
|
|
296
298
|
|
|
297
|
-
function ensureSessionWebhookBusinessSuccess(
|
|
298
|
-
data: unknown,
|
|
299
|
-
context: { msgtype: string },
|
|
300
|
-
): void {
|
|
299
|
+
function ensureSessionWebhookBusinessSuccess(data: unknown, context: { msgtype: string }): void {
|
|
301
300
|
if (!data || typeof data !== "object") {
|
|
302
301
|
return;
|
|
303
302
|
}
|
|
304
303
|
const payload = data as Record<string, unknown>;
|
|
305
304
|
const code = extractErrorCodeFromResponseData(payload);
|
|
306
|
-
const message =
|
|
307
|
-
payload.message,
|
|
308
|
-
|
|
309
|
-
payload.msg,
|
|
310
|
-
payload.errorMessage,
|
|
311
|
-
) || "unknown error";
|
|
305
|
+
const message =
|
|
306
|
+
firstTrimmedString(payload.message, payload.errmsg, payload.msg, payload.errorMessage) ||
|
|
307
|
+
"unknown error";
|
|
312
308
|
|
|
313
309
|
const hasFailureSuccessFlag = payload.success === false || payload.result === false;
|
|
314
310
|
const hasFailureCode = typeof code === "string" && code !== "" && code !== "0";
|
|
@@ -319,7 +315,9 @@ function ensureSessionWebhookBusinessSuccess(
|
|
|
319
315
|
const reason = [
|
|
320
316
|
code && code !== "0" ? `code=${code}` : "",
|
|
321
317
|
message !== "unknown error" ? `message=${message}` : "",
|
|
322
|
-
]
|
|
318
|
+
]
|
|
319
|
+
.filter(Boolean)
|
|
320
|
+
.join(" ");
|
|
323
321
|
throw new Error(`Session webhook ${context.msgtype} send failed${reason ? `: ${reason}` : ""}`);
|
|
324
322
|
}
|
|
325
323
|
|
|
@@ -407,7 +405,11 @@ export async function sendProactiveTextOrMarkdown(
|
|
|
407
405
|
config.convertMarkdownTables !== false
|
|
408
406
|
? convertMarkdownTablesToPlainText(textWithUploadedLocalImages)
|
|
409
407
|
: textWithUploadedLocalImages;
|
|
410
|
-
const { useMarkdown, title } = detectMarkdownAndExtractTitle(
|
|
408
|
+
const { useMarkdown, title } = detectMarkdownAndExtractTitle(
|
|
409
|
+
normalizedText,
|
|
410
|
+
options,
|
|
411
|
+
"OpenClaw 提醒",
|
|
412
|
+
);
|
|
411
413
|
|
|
412
414
|
log?.debug?.(
|
|
413
415
|
`[DingTalk] Sending proactive message to ${isGroup ? "group" : "user"} ${resolvedTarget} with title "${title}"${proactiveRiskTag}`,
|
|
@@ -519,8 +521,9 @@ export async function sendProactiveMedia(
|
|
|
519
521
|
msgParam = JSON.stringify({ photoURL: mediaId });
|
|
520
522
|
} else if (mediaType === "voice") {
|
|
521
523
|
msgKey = "sampleAudio";
|
|
522
|
-
const durationMs =
|
|
523
|
-
??
|
|
524
|
+
const durationMs =
|
|
525
|
+
uploadedDurationMs ??
|
|
526
|
+
(await getVoiceDurationMs(mediaPath, mediaType, log, { preReadBuffer: buffer }));
|
|
524
527
|
msgParam = JSON.stringify({ mediaId, duration: String(durationMs) });
|
|
525
528
|
} else {
|
|
526
529
|
// sampleVideo requires picMediaId; fallback to sampleFile for broader compatibility.
|
|
@@ -614,7 +617,10 @@ export async function sendProactiveMedia(
|
|
|
614
617
|
).catch((fallbackErr: any) => ({ __fallbackError: fallbackErr }));
|
|
615
618
|
|
|
616
619
|
if ((fallback as any)?.__fallbackError) {
|
|
617
|
-
return {
|
|
620
|
+
return {
|
|
621
|
+
ok: false,
|
|
622
|
+
error: `${err.message}; fallback failed: ${(fallback as any).__fallbackError?.message || "unknown"}`,
|
|
623
|
+
};
|
|
618
624
|
}
|
|
619
625
|
|
|
620
626
|
const fallbackDelivery = extractOutboundDeliveryMetadata(fallback);
|
|
@@ -632,7 +638,9 @@ export async function sendProactiveMedia(
|
|
|
632
638
|
chatType: inferConversationChatType(options.conversationId || normalizedTarget),
|
|
633
639
|
delivery: {
|
|
634
640
|
...fallbackDelivery,
|
|
635
|
-
kind: isTrackingResult(fallback as ProactiveTextSendResult)
|
|
641
|
+
kind: isTrackingResult(fallback as ProactiveTextSendResult)
|
|
642
|
+
? "proactive-card"
|
|
643
|
+
: "proactive-text",
|
|
636
644
|
},
|
|
637
645
|
});
|
|
638
646
|
return { ok: true, data: fallback, messageId: fallbackMessageId };
|
|
@@ -675,9 +683,9 @@ export async function sendMedia(
|
|
|
675
683
|
if (options.conversationId) {
|
|
676
684
|
// conversationId explicitly provided (parsed from sessionKey)
|
|
677
685
|
activeRun = options.expectedCardOwnerId
|
|
678
|
-
? resolveCardRunByConversation(accountId, options.conversationId, {
|
|
686
|
+
? (resolveCardRunByConversation(accountId, options.conversationId, {
|
|
679
687
|
ownerUserId: options.expectedCardOwnerId,
|
|
680
|
-
}) ?? resolveCardRunByConversation(accountId, options.conversationId, undefined)
|
|
688
|
+
}) ?? resolveCardRunByConversation(accountId, options.conversationId, undefined))
|
|
681
689
|
: resolveCardRunByConversation(accountId, options.conversationId, undefined);
|
|
682
690
|
} else if (options.expectedCardOwnerId) {
|
|
683
691
|
// Fallback: when sessionKey=- causes conversationId to be undefined,
|
|
@@ -686,7 +694,7 @@ export async function sendMedia(
|
|
|
686
694
|
if (activeRun) {
|
|
687
695
|
log?.debug?.(
|
|
688
696
|
`[DingTalk] Matched active card by owner-only lookup (conversationId unavailable) ` +
|
|
689
|
-
|
|
697
|
+
`accountId=${accountId} ownerUserId=${options.expectedCardOwnerId} outTrackId=${activeRun.outTrackId}`,
|
|
690
698
|
);
|
|
691
699
|
}
|
|
692
700
|
}
|
|
@@ -744,13 +752,16 @@ export async function sendBySession(
|
|
|
744
752
|
`[DingTalk] Session webhook image will be delivered as markdown media reference mediaId=${uploadResult.mediaId}`,
|
|
745
753
|
);
|
|
746
754
|
} else {
|
|
747
|
-
const mediaHint =
|
|
755
|
+
const mediaHint =
|
|
756
|
+
options.mediaUrl || options.mediaPath || options.filePath || "(媒体发送失败)";
|
|
748
757
|
text = `${text}\n\n📎 媒体发送失败,兜底链接/路径:${mediaHint}`.trim();
|
|
749
758
|
log?.warn?.("[DingTalk] Media upload failed, falling back to text description");
|
|
750
759
|
}
|
|
751
760
|
} else {
|
|
752
|
-
const mediaHint =
|
|
753
|
-
|
|
761
|
+
const mediaHint =
|
|
762
|
+
options.mediaUrl || options.mediaPath || options.filePath || "(媒体发送失败)";
|
|
763
|
+
text =
|
|
764
|
+
`${text}\n\n📎 当前会话无法直接发送 ${options.mediaType},兜底链接/路径:${mediaHint}`.trim();
|
|
754
765
|
log?.warn?.(
|
|
755
766
|
`[DingTalk] Session webhook does not support native ${options.mediaType} replies; falling back to text description`,
|
|
756
767
|
);
|
|
@@ -768,7 +779,11 @@ export async function sendBySession(
|
|
|
768
779
|
config.convertMarkdownTables !== false
|
|
769
780
|
? convertMarkdownTablesToPlainText(textWithUploadedLocalImages)
|
|
770
781
|
: textWithUploadedLocalImages;
|
|
771
|
-
const { useMarkdown, title } = detectMarkdownAndExtractTitle(
|
|
782
|
+
const { useMarkdown, title } = detectMarkdownAndExtractTitle(
|
|
783
|
+
normalizedText,
|
|
784
|
+
options,
|
|
785
|
+
"Clawdbot 消息",
|
|
786
|
+
);
|
|
772
787
|
const chunks = splitMarkdownChunks(normalizedText, DINGTALK_TEXT_CHUNK_LIMIT);
|
|
773
788
|
|
|
774
789
|
let lastResult: any = null;
|
|
@@ -779,7 +794,13 @@ export async function sendBySession(
|
|
|
779
794
|
if (options.atUserId && idx === chunks.length - 1) {
|
|
780
795
|
finalText = `${finalText} @${options.atUserId}`;
|
|
781
796
|
}
|
|
782
|
-
body = {
|
|
797
|
+
body = {
|
|
798
|
+
msgtype: "markdown",
|
|
799
|
+
markdown: {
|
|
800
|
+
title: chunks.length > 1 ? `${title} (${idx + 1}/${chunks.length})` : title,
|
|
801
|
+
text: finalText,
|
|
802
|
+
},
|
|
803
|
+
};
|
|
783
804
|
} else {
|
|
784
805
|
body = { msgtype: "text", text: { content: chunk } };
|
|
785
806
|
}
|
|
@@ -808,16 +829,30 @@ export async function sendMessage(
|
|
|
808
829
|
config: DingTalkConfig,
|
|
809
830
|
conversationId: string,
|
|
810
831
|
text: string,
|
|
811
|
-
options: SendMessageOptions & {
|
|
812
|
-
|
|
832
|
+
options: SendMessageOptions & {
|
|
833
|
+
sessionWebhook?: string;
|
|
834
|
+
card?: AICardInstance;
|
|
835
|
+
accountId?: string;
|
|
836
|
+
} = {},
|
|
837
|
+
): Promise<{
|
|
838
|
+
ok: boolean;
|
|
839
|
+
error?: string;
|
|
840
|
+
data?: AxiosResponse;
|
|
841
|
+
messageId?: string;
|
|
842
|
+
tracking?: DingTalkTrackingMetadata;
|
|
843
|
+
}> {
|
|
813
844
|
try {
|
|
814
845
|
const messageType = config.messageType || "markdown";
|
|
815
846
|
const log = options.log || getLogger();
|
|
816
847
|
|
|
817
|
-
if (
|
|
848
|
+
if (
|
|
849
|
+
options.sessionWebhook &&
|
|
850
|
+
options.mediaPath &&
|
|
851
|
+
shouldRouteSessionMediaViaProactive(options.mediaType)
|
|
852
|
+
) {
|
|
818
853
|
log?.debug?.(
|
|
819
854
|
`[DingTalk] Session webhook does not support ${options.mediaType} replies reliably; ` +
|
|
820
|
-
|
|
855
|
+
"using proactive media API instead",
|
|
821
856
|
);
|
|
822
857
|
const proactiveMediaResult = await sendProactiveMedia(
|
|
823
858
|
config,
|
|
@@ -829,7 +864,7 @@ export async function sendMessage(
|
|
|
829
864
|
if (!proactiveMediaResult.ok) {
|
|
830
865
|
log?.warn?.(
|
|
831
866
|
`[DingTalk] Proactive ${options.mediaType} reply failed; falling back to session markdown: ` +
|
|
832
|
-
|
|
867
|
+
(proactiveMediaResult.error || "unknown"),
|
|
833
868
|
);
|
|
834
869
|
const data = await sendBySession(config, options.sessionWebhook, text, options);
|
|
835
870
|
const delivery = extractOutboundDeliveryMetadata(data);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import axios from "../shared/http-client";
|
|
2
|
+
import { retryWithBackoff } from "../shared/utils";
|
|
1
3
|
import { resolveRuntimeConfig } from "./config";
|
|
2
|
-
import axios from "./http-client";
|
|
3
4
|
import type { DingTalkConfig, Logger, TokenInfo } from "./types";
|
|
4
|
-
import { retryWithBackoff } from "./utils";
|
|
5
5
|
|
|
6
6
|
interface TokenCache {
|
|
7
7
|
accessToken: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import { getCurrentTimestamp } from "../shared/utils";
|
|
2
|
+
import { getAccessToken } from "./auth";
|
|
3
|
+
import type { DingTalkChannelPlugin } from "./types";
|
|
4
4
|
|
|
5
5
|
export function createDingTalkStatus(): NonNullable<DingTalkChannelPlugin["status"]> {
|
|
6
6
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { DEFAULT_MESSAGE_CONTEXT_TTL_DAYS } from "
|
|
2
|
+
import { DEFAULT_MESSAGE_CONTEXT_TTL_DAYS } from "../messaging/message-context-store";
|
|
3
3
|
import { buildSecretInputSchema } from "./secret-input";
|
|
4
4
|
|
|
5
5
|
const AckReactionSchema = z.union([
|
|
@@ -9,8 +9,51 @@ const AckReactionSchema = z.union([
|
|
|
9
9
|
]);
|
|
10
10
|
|
|
11
11
|
const CardStreamingModeSchema = z.enum(["off", "answer", "all"]);
|
|
12
|
+
const CardTaskProgressRefreshSchema = z.enum(["heartbeat", "interval"]);
|
|
12
13
|
const ContextVisibilitySchema = z.enum(["all", "allowlist", "allowlist_quote"]);
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Gateway RPC capability gates (Issue #608, 问题 3).
|
|
17
|
+
* Shape aligned with the official connector's `tools: { docs, media }` gate;
|
|
18
|
+
* all capabilities default to enabled for backward compatibility.
|
|
19
|
+
* Allowlists must list at least one entry when present: an empty list would be
|
|
20
|
+
* ambiguous, so it is rejected at config-validation time instead of silently
|
|
21
|
+
* disabling the restriction.
|
|
22
|
+
*/
|
|
23
|
+
const GatewayTargetListSchema = z
|
|
24
|
+
.array(z.string().regex(/^(user|group):\S+$/, "must be `user:<id>` or `group:<conversationId>`"))
|
|
25
|
+
.min(1)
|
|
26
|
+
.optional();
|
|
27
|
+
|
|
28
|
+
const DingTalkGatewayCapabilitiesSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
tools: z
|
|
31
|
+
.object({
|
|
32
|
+
/** Enable dingtalk.docs.* and dingtalk-connector.docs.* Gateway RPCs (default: true) */
|
|
33
|
+
docs: z.boolean().optional(),
|
|
34
|
+
/** Enable dingtalk-connector.sendToUser/sendToGroup/send proactive-send RPCs (default: true) */
|
|
35
|
+
proactiveSend: z.boolean().optional(),
|
|
36
|
+
})
|
|
37
|
+
.strict()
|
|
38
|
+
.optional(),
|
|
39
|
+
docs: z
|
|
40
|
+
.object({
|
|
41
|
+
/** When set, docs RPCs only accept these spaceId values (at least one entry) */
|
|
42
|
+
allowedSpaceIds: z.array(z.string().min(1)).min(1).optional(),
|
|
43
|
+
})
|
|
44
|
+
.strict()
|
|
45
|
+
.optional(),
|
|
46
|
+
send: z
|
|
47
|
+
.object({
|
|
48
|
+
/** When set, proactive-send RPCs only accept these `user:*` / `group:*` targets (at least one entry) */
|
|
49
|
+
allowedTargets: GatewayTargetListSchema,
|
|
50
|
+
})
|
|
51
|
+
.strict()
|
|
52
|
+
.optional(),
|
|
53
|
+
})
|
|
54
|
+
.strict()
|
|
55
|
+
.optional();
|
|
56
|
+
|
|
14
57
|
/**
|
|
15
58
|
* Runtime-parsed DingTalk account config.
|
|
16
59
|
*
|
|
@@ -121,7 +164,13 @@ const DingTalkAccountConfigShape = {
|
|
|
121
164
|
/** Show the proactive-send permission hint when the runtime detects missing DingTalk proactive permission. */
|
|
122
165
|
enabled: z.boolean().optional().default(true),
|
|
123
166
|
/** Minimum cooldown in hours before the same proactive permission hint can be shown again. */
|
|
124
|
-
cooldownHours: z
|
|
167
|
+
cooldownHours: z
|
|
168
|
+
.number()
|
|
169
|
+
.int()
|
|
170
|
+
.min(1)
|
|
171
|
+
.max(24 * 30)
|
|
172
|
+
.optional()
|
|
173
|
+
.default(24),
|
|
125
174
|
})
|
|
126
175
|
.optional()
|
|
127
176
|
.default({ enabled: true, cooldownHours: 24 }),
|
|
@@ -138,8 +187,26 @@ const DingTalkAccountConfigShape = {
|
|
|
138
187
|
/** Throttle interval in milliseconds between AI card streaming updates. */
|
|
139
188
|
cardStreamInterval: z.number().int().min(200).optional().default(1000),
|
|
140
189
|
|
|
190
|
+
/** Live "task in progress" block on AI cards for long-running tasks.
|
|
191
|
+
* - true: always enabled
|
|
192
|
+
* - false: always disabled
|
|
193
|
+
* - unset (default): enabled, except when `cardStreamingMode` is explicitly `"off"` */
|
|
194
|
+
cardTaskProgress: z.boolean().optional(),
|
|
195
|
+
|
|
196
|
+
/** How tool-driven stage/step changes reach the progress block:
|
|
197
|
+
* - heartbeat (effective default): coalesced into the 30s refresh, so a task
|
|
198
|
+
* costs one update on appearance plus one per 30s
|
|
199
|
+
* - interval: pushed on every correlated tool event, throttled by
|
|
200
|
+
* `cardStreamInterval` (more updates, fresher stage label) */
|
|
201
|
+
cardTaskProgressRefresh: CardTaskProgressRefreshSchema.optional(),
|
|
202
|
+
|
|
141
203
|
/** Cooldown window in milliseconds after AI card trigger errors. Replies fall back to non-card delivery during this period. */
|
|
142
|
-
aicardDegradeMs: z
|
|
204
|
+
aicardDegradeMs: z
|
|
205
|
+
.number()
|
|
206
|
+
.int()
|
|
207
|
+
.min(60_000)
|
|
208
|
+
.optional()
|
|
209
|
+
.default(30 * 60 * 1000),
|
|
143
210
|
|
|
144
211
|
/** Enable the local feedback-learning loop for notes, reflections, and command-assisted learning. */
|
|
145
212
|
learningEnabled: z.boolean().optional(),
|
|
@@ -175,7 +242,17 @@ const DingTalkAccountConfigShape = {
|
|
|
175
242
|
dapiUsage: z.boolean().optional().default(false),
|
|
176
243
|
})
|
|
177
244
|
.optional()
|
|
178
|
-
.default({
|
|
245
|
+
.default({
|
|
246
|
+
model: true,
|
|
247
|
+
effort: true,
|
|
248
|
+
agent: true,
|
|
249
|
+
taskTime: false,
|
|
250
|
+
tokens: false,
|
|
251
|
+
dapiUsage: false,
|
|
252
|
+
}),
|
|
253
|
+
|
|
254
|
+
/** 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. */
|
|
255
|
+
gatewayCapabilities: DingTalkGatewayCapabilitiesSchema,
|
|
179
256
|
} as const;
|
|
180
257
|
|
|
181
258
|
const DingTalkAccountConfigSchema = z.object(DingTalkAccountConfigShape);
|