@soimy/dingtalk 3.6.3 → 3.6.4
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.js +120 -62
- package/dist/index.js.map +2 -2
- package/dist/src/inbound-handler.d.ts.map +1 -1
- package/dist/src/reply-strategy-card.d.ts.map +1 -1
- package/dist/src/targeting/agent-routing.d.ts +55 -16
- package/dist/src/targeting/agent-routing.d.ts.map +1 -1
- package/dist/src/types.d.ts +7 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/inbound-handler.ts +82 -33
- package/src/reply-strategy-card.ts +19 -0
- package/src/targeting/agent-routing.ts +151 -85
- package/src/types.ts +7 -0
package/dist/index.js
CHANGED
|
@@ -9584,6 +9584,7 @@ function createCardReplyStrategy(ctx) {
|
|
|
9584
9584
|
let sawFinalDelivery = false;
|
|
9585
9585
|
let latestReasoningSnapshot = "";
|
|
9586
9586
|
let pendingNonImageMedia = [];
|
|
9587
|
+
const processedMediaUrls = /* @__PURE__ */ new Set();
|
|
9587
9588
|
const getRenderedTimeline = (options = {}) => {
|
|
9588
9589
|
const fallbackAnswer = finalTextForFallback || (sawFinalDelivery ? EMPTY_FINAL_REPLY : void 0);
|
|
9589
9590
|
return controller.getRenderedContent({
|
|
@@ -9752,6 +9753,11 @@ function createCardReplyStrategy(ctx) {
|
|
|
9752
9753
|
if (candidate.classification !== "local") {
|
|
9753
9754
|
continue;
|
|
9754
9755
|
}
|
|
9756
|
+
if (processedMediaUrls.has(candidate.url.trim())) {
|
|
9757
|
+
const placeholder = buildImagePlaceholderText({ alt: candidate.alt, url: candidate.url });
|
|
9758
|
+
nextText = `${nextText.slice(0, candidate.start)}${placeholder}${nextText.slice(candidate.end)}`;
|
|
9759
|
+
continue;
|
|
9760
|
+
}
|
|
9755
9761
|
let prepared;
|
|
9756
9762
|
try {
|
|
9757
9763
|
prepared = await prepareMediaInput(candidate.url, log, config.mediaUrlAllowlist);
|
|
@@ -9764,6 +9770,7 @@ function createCardReplyStrategy(ctx) {
|
|
|
9764
9770
|
if (!result?.mediaId) {
|
|
9765
9771
|
continue;
|
|
9766
9772
|
}
|
|
9773
|
+
processedMediaUrls.add(candidate.url.trim());
|
|
9767
9774
|
const placeholder = buildImagePlaceholderText({ alt: candidate.alt, url: candidate.url });
|
|
9768
9775
|
const blockText = candidate.alt.trim() || placeholder.replace(/^见下图/, "").trim() || "\u56FE\u7247";
|
|
9769
9776
|
successfulReroutes.push({
|
|
@@ -9863,6 +9870,10 @@ function createCardReplyStrategy(ctx) {
|
|
|
9863
9870
|
);
|
|
9864
9871
|
if (payload.mediaUrls.length > 0) {
|
|
9865
9872
|
for (const url of payload.mediaUrls) {
|
|
9873
|
+
const normalizedUrl = url.trim();
|
|
9874
|
+
if (processedMediaUrls.has(normalizedUrl)) {
|
|
9875
|
+
continue;
|
|
9876
|
+
}
|
|
9866
9877
|
try {
|
|
9867
9878
|
const prepared = await prepareMediaInput(url, log, config.mediaUrlAllowlist);
|
|
9868
9879
|
const mediaType = resolveOutboundMediaType({ mediaPath: prepared.path, asVoice: false });
|
|
@@ -9877,6 +9888,7 @@ function createCardReplyStrategy(ctx) {
|
|
|
9877
9888
|
const result = await uploadMedia2(config, prepared.path, "image", log);
|
|
9878
9889
|
await prepared.cleanup?.();
|
|
9879
9890
|
if (result?.mediaId) {
|
|
9891
|
+
processedMediaUrls.add(normalizedUrl);
|
|
9880
9892
|
await controller.appendImageBlock(result.mediaId);
|
|
9881
9893
|
}
|
|
9882
9894
|
} catch (err) {
|
|
@@ -9935,6 +9947,10 @@ function createCardReplyStrategy(ctx) {
|
|
|
9935
9947
|
}
|
|
9936
9948
|
if (payload.mediaUrls.length > 0) {
|
|
9937
9949
|
for (const url of payload.mediaUrls) {
|
|
9950
|
+
const normalizedUrl = url.trim();
|
|
9951
|
+
if (processedMediaUrls.has(normalizedUrl)) {
|
|
9952
|
+
continue;
|
|
9953
|
+
}
|
|
9938
9954
|
try {
|
|
9939
9955
|
const prepared = await prepareMediaInput(url, log, config.mediaUrlAllowlist);
|
|
9940
9956
|
const mediaType = resolveOutboundMediaType({ mediaPath: prepared.path, asVoice: false });
|
|
@@ -9947,6 +9963,7 @@ function createCardReplyStrategy(ctx) {
|
|
|
9947
9963
|
const result = await uploadMedia2(config, prepared.path, "image", log);
|
|
9948
9964
|
await prepared.cleanup?.();
|
|
9949
9965
|
if (result?.mediaId) {
|
|
9966
|
+
processedMediaUrls.add(normalizedUrl);
|
|
9950
9967
|
await controller.appendImageBlock(result.mediaId);
|
|
9951
9968
|
}
|
|
9952
9969
|
} catch (err) {
|
|
@@ -10556,46 +10573,61 @@ function buildAgentSessionKey(params) {
|
|
|
10556
10573
|
identityLinks: cfg.session?.identityLinks
|
|
10557
10574
|
}).toLowerCase();
|
|
10558
10575
|
}
|
|
10559
|
-
function
|
|
10560
|
-
|
|
10561
|
-
}
|
|
10562
|
-
async function resolveSubAgentRoute(params) {
|
|
10563
|
-
const { extractedContent, cfg, isGroup, dingtalkConfig, sessionWebhook, senderId, log } = params;
|
|
10576
|
+
function resolveMessageTarget(params) {
|
|
10577
|
+
const { extractedContent, cfg, isGroup } = params;
|
|
10564
10578
|
const atMentions = extractedContent.atMentions || [];
|
|
10565
|
-
|
|
10579
|
+
if (atMentions.length === 0 || !cfg.agents?.list || cfg.agents.list.length === 0) {
|
|
10580
|
+
return { kind: "default" };
|
|
10581
|
+
}
|
|
10566
10582
|
const textForCommandCheck = extractedContent.text.replace(/^\[引用[^\]]*\]\s*/, "");
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
const isSlashCommand = maybeResolveTextAlias(textWithoutMentions, cfg) !== null;
|
|
10570
|
-
if (atMentions.length === 0 || !cfg.agents?.list || cfg.agents.list.length === 0 || isLearnCommand || isSlashCommand) {
|
|
10571
|
-
return null;
|
|
10583
|
+
if (parseLearnCommand(textForCommandCheck).scope !== "unknown") {
|
|
10584
|
+
return { kind: "default" };
|
|
10572
10585
|
}
|
|
10573
|
-
const
|
|
10586
|
+
const atUserDingtalkIds = isGroup ? extractedContent.atUserDingtalkIds : void 0;
|
|
10587
|
+
const { matchedAgents, unmatchedNames, hasInvalidAgentNames } = resolveAtAgents(
|
|
10574
10588
|
atMentions,
|
|
10575
10589
|
cfg,
|
|
10576
10590
|
atUserDingtalkIds
|
|
10577
10591
|
);
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
try {
|
|
10584
|
-
const sendOptions = isGroup ? { atUserId: senderId, log } : { log };
|
|
10585
|
-
await sendBySession(dingtalkConfig, sessionWebhook, `\u26A0\uFE0F ${fallbackReason}`, {
|
|
10586
|
-
...sendOptions
|
|
10587
|
-
});
|
|
10588
|
-
} catch (err) {
|
|
10589
|
-
log?.debug?.(`[DingTalk] Failed to send fallback notice: ${getErrorMessage(err)}`);
|
|
10592
|
+
const commandText = textForCommandCheck.replace(/^(?:@\S+\s+)*/u, "").trim();
|
|
10593
|
+
if (maybeResolveTextAlias(commandText, cfg) !== null) {
|
|
10594
|
+
const firstMatch = matchedAgents[0];
|
|
10595
|
+
if (firstMatch) {
|
|
10596
|
+
return { kind: "subagent-command", agent: firstMatch, commandText };
|
|
10590
10597
|
}
|
|
10591
10598
|
}
|
|
10592
|
-
if (matchedAgents.length === 0) {
|
|
10593
|
-
return
|
|
10599
|
+
if (matchedAgents.length === 0 && !hasInvalidAgentNames) {
|
|
10600
|
+
return { kind: "default" };
|
|
10601
|
+
}
|
|
10602
|
+
return { kind: "subagent-content", matchedAgents, unmatchedNames, hasInvalidAgentNames };
|
|
10603
|
+
}
|
|
10604
|
+
function sanitizeAgentName(name) {
|
|
10605
|
+
return name.replace(/[[\]\r\n]/g, "").trim();
|
|
10606
|
+
}
|
|
10607
|
+
async function sendUnmatchedAgentNotice(params) {
|
|
10608
|
+
const { unmatchedNames, isGroup, senderId, dingtalkConfig, sessionWebhook, log } = params;
|
|
10609
|
+
const fallbackReason = `\u672A\u627E\u5230\u540D\u4E3A"${unmatchedNames.join("\u3001")}"\u7684\u52A9\u624B`;
|
|
10610
|
+
try {
|
|
10611
|
+
const sendOptions = isGroup ? { atUserId: senderId, log } : { log };
|
|
10612
|
+
await sendBySession(dingtalkConfig, sessionWebhook, `\u26A0\uFE0F ${fallbackReason}`, sendOptions);
|
|
10613
|
+
} catch (err) {
|
|
10614
|
+
log?.debug?.(`[DingTalk] Failed to send fallback notice: ${getErrorMessage(err)}`);
|
|
10594
10615
|
}
|
|
10595
|
-
return { matchedAgents };
|
|
10596
10616
|
}
|
|
10597
10617
|
async function dispatchSubAgents(params) {
|
|
10598
|
-
const {
|
|
10618
|
+
const {
|
|
10619
|
+
matchedAgents,
|
|
10620
|
+
commandText,
|
|
10621
|
+
cfg,
|
|
10622
|
+
accountId,
|
|
10623
|
+
data,
|
|
10624
|
+
dingtalkConfig,
|
|
10625
|
+
sessionWebhook,
|
|
10626
|
+
extractedContent,
|
|
10627
|
+
handleMessage,
|
|
10628
|
+
downloadMedia: download,
|
|
10629
|
+
log
|
|
10630
|
+
} = params;
|
|
10599
10631
|
let preDownloadedMedia;
|
|
10600
10632
|
const robotCode = resolveRobotCode(dingtalkConfig);
|
|
10601
10633
|
if (robotCode) {
|
|
@@ -10630,18 +10662,17 @@ async function dispatchSubAgents(params) {
|
|
|
10630
10662
|
dingtalkConfig,
|
|
10631
10663
|
subAgentOptions: {
|
|
10632
10664
|
agentId: agentMatch.agentId,
|
|
10633
|
-
responsePrefix: `> \u{1F916} **${sanitizeAgentName(agentMatch.matchedName)}**:
|
|
10665
|
+
responsePrefix: commandText ? "" : `> \u{1F916} **${sanitizeAgentName(agentMatch.matchedName)}**:
|
|
10634
10666
|
|
|
10635
10667
|
`,
|
|
10636
|
-
matchedName: agentMatch.matchedName
|
|
10668
|
+
matchedName: agentMatch.matchedName,
|
|
10669
|
+
commandText
|
|
10637
10670
|
},
|
|
10638
10671
|
preDownloadedMedia
|
|
10639
10672
|
});
|
|
10640
10673
|
} catch (error) {
|
|
10641
10674
|
const message = getErrorMessage(error);
|
|
10642
|
-
log?.error?.(
|
|
10643
|
-
`[DingTalk] Sub-agent ${agentMatch.agentId} failed: ${message}`
|
|
10644
|
-
);
|
|
10675
|
+
log?.error?.(`[DingTalk] Sub-agent ${agentMatch.agentId} failed: ${message}`);
|
|
10645
10676
|
if (error instanceof HostRoutingHelperUnavailableError && !helperMissingWarningSent) {
|
|
10646
10677
|
helperMissingWarningSent = true;
|
|
10647
10678
|
try {
|
|
@@ -11361,7 +11392,7 @@ async function handleDingTalkMessage(params) {
|
|
|
11361
11392
|
return;
|
|
11362
11393
|
}
|
|
11363
11394
|
const rawInboundText = extractedContent.text.trim();
|
|
11364
|
-
if (subAgentOptions) {
|
|
11395
|
+
if (subAgentOptions && !subAgentOptions.commandText) {
|
|
11365
11396
|
const cleanText = extractedContent.text.replace(/^\[引用[^\]]*\]\s*/, "");
|
|
11366
11397
|
const contextHint = `[\u4F60\u88AB @ \u4E3A"${subAgentOptions.matchedName}"]
|
|
11367
11398
|
|
|
@@ -11539,36 +11570,62 @@ async function handleDingTalkMessage(params) {
|
|
|
11539
11570
|
peerIdOverride,
|
|
11540
11571
|
config: dingtalkConfig
|
|
11541
11572
|
});
|
|
11542
|
-
const
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
cfg,
|
|
11547
|
-
accountId,
|
|
11573
|
+
const messageTarget = subAgentOptions ? null : resolveMessageTarget({ extractedContent, cfg, isGroup });
|
|
11574
|
+
let route;
|
|
11575
|
+
if (subAgentOptions) {
|
|
11576
|
+
route = {
|
|
11548
11577
|
agentId: subAgentOptions.agentId,
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
extractedContent,
|
|
11578
|
+
sessionKey: buildAgentSessionKey({
|
|
11579
|
+
rt,
|
|
11580
|
+
cfg,
|
|
11581
|
+
accountId,
|
|
11582
|
+
agentId: subAgentOptions.agentId,
|
|
11583
|
+
peerKind: sessionPeer.kind,
|
|
11584
|
+
peerId: sessionPeer.peerId
|
|
11585
|
+
}),
|
|
11586
|
+
mainSessionKey: ""
|
|
11587
|
+
};
|
|
11588
|
+
} else {
|
|
11589
|
+
route = rt.channel.routing.resolveAgentRoute({
|
|
11562
11590
|
cfg,
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
senderId,
|
|
11567
|
-
log
|
|
11591
|
+
channel: "dingtalk",
|
|
11592
|
+
accountId,
|
|
11593
|
+
peer: { kind: sessionPeer.kind, id: sessionPeer.peerId }
|
|
11568
11594
|
});
|
|
11569
|
-
|
|
11595
|
+
}
|
|
11596
|
+
if (messageTarget && messageTarget.kind !== "default") {
|
|
11597
|
+
if (messageTarget.kind === "subagent-command") {
|
|
11598
|
+
await dispatchSubAgents({
|
|
11599
|
+
matchedAgents: [messageTarget.agent],
|
|
11600
|
+
commandText: messageTarget.commandText,
|
|
11601
|
+
cfg,
|
|
11602
|
+
accountId,
|
|
11603
|
+
data,
|
|
11604
|
+
dingtalkConfig,
|
|
11605
|
+
sessionWebhook,
|
|
11606
|
+
extractedContent,
|
|
11607
|
+
handleMessage: handleDingTalkMessage,
|
|
11608
|
+
downloadMedia,
|
|
11609
|
+
log
|
|
11610
|
+
});
|
|
11611
|
+
return;
|
|
11612
|
+
}
|
|
11613
|
+
if (messageTarget.hasInvalidAgentNames) {
|
|
11614
|
+
await sendUnmatchedAgentNotice({
|
|
11615
|
+
unmatchedNames: messageTarget.unmatchedNames,
|
|
11616
|
+
isGroup,
|
|
11617
|
+
senderId,
|
|
11618
|
+
dingtalkConfig,
|
|
11619
|
+
sessionWebhook,
|
|
11620
|
+
log
|
|
11621
|
+
});
|
|
11622
|
+
}
|
|
11623
|
+
if (messageTarget.matchedAgents.length > 0) {
|
|
11624
|
+
log?.info?.(
|
|
11625
|
+
`[DingTalk] Sub-agent resolve: matched=${messageTarget.matchedAgents.map((a) => a.agentId).join(",")} unmatched=${messageTarget.unmatchedNames.join(",")}`
|
|
11626
|
+
);
|
|
11570
11627
|
await dispatchSubAgents({
|
|
11571
|
-
|
|
11628
|
+
matchedAgents: messageTarget.matchedAgents,
|
|
11572
11629
|
cfg,
|
|
11573
11630
|
accountId,
|
|
11574
11631
|
data,
|
|
@@ -11592,7 +11649,7 @@ async function handleDingTalkMessage(params) {
|
|
|
11592
11649
|
dingtalkConfig,
|
|
11593
11650
|
senderId,
|
|
11594
11651
|
isDirect,
|
|
11595
|
-
extractedText: extractedContent.text,
|
|
11652
|
+
extractedText: subAgentOptions?.commandText ?? extractedContent.text,
|
|
11596
11653
|
messageType: extractedContent.messageType,
|
|
11597
11654
|
data: {
|
|
11598
11655
|
conversationId: data.conversationId,
|
|
@@ -12215,6 +12272,7 @@ ${extracted.text}`;
|
|
|
12215
12272
|
const inboundText = attachmentExtractedText ? `${inboundBody.trimEnd()}
|
|
12216
12273
|
|
|
12217
12274
|
${attachmentExtractedText}` : inboundBody;
|
|
12275
|
+
const commandBody = subAgentOptions?.commandText ?? inboundText;
|
|
12218
12276
|
const learningEnabled = isLearningEnabled(dingtalkConfig);
|
|
12219
12277
|
const learningContextBlock = buildLearningContextBlock({
|
|
12220
12278
|
enabled: learningEnabled,
|
|
@@ -12256,7 +12314,7 @@ ${attachmentExtractedText}` : inboundBody;
|
|
|
12256
12314
|
const ctx = rt.channel.reply.finalizeInboundContext({
|
|
12257
12315
|
Body: body,
|
|
12258
12316
|
RawBody: inboundText,
|
|
12259
|
-
CommandBody:
|
|
12317
|
+
CommandBody: commandBody,
|
|
12260
12318
|
QuotedRef: quotedRef,
|
|
12261
12319
|
QuotedRefJson: quotedRef ? JSON.stringify(quotedRef) : void 0,
|
|
12262
12320
|
ReplyToId: quotedRuntimeContext?.replyToId,
|