@yaoyuanchao/dingtalk 1.7.5 → 1.7.6
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/package.json +1 -1
- package/src/monitor.ts +52 -6
package/package.json
CHANGED
package/src/monitor.ts
CHANGED
|
@@ -198,6 +198,30 @@ const AGGREGATION_DELAY_MS = 2000; // 2 seconds - balance between UX and catchin
|
|
|
198
198
|
|
|
199
199
|
const sessionQueues = new Map<string, Promise<void>>();
|
|
200
200
|
const sessionQueueLastActivity = new Map<string, number>();
|
|
201
|
+
|
|
202
|
+
// Track delivery activity per queue key to detect when the SDK's dispatch resolves
|
|
203
|
+
// before the agent's full turn completes (e.g. followup turns running in background).
|
|
204
|
+
// This supplements sessionQueues for the "busy" check.
|
|
205
|
+
const DELIVER_ACTIVITY_GRACE_MS = 8000; // 8s after last delivery, consider idle
|
|
206
|
+
const deliverActivityTimestamps = new Map<string, number>();
|
|
207
|
+
|
|
208
|
+
function markDeliverActivity(queueKey: string): void {
|
|
209
|
+
deliverActivityTimestamps.set(queueKey, Date.now());
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function hasActiveDelivery(queueKey: string): boolean {
|
|
213
|
+
const ts = deliverActivityTimestamps.get(queueKey);
|
|
214
|
+
if (!ts) return false;
|
|
215
|
+
if (Date.now() - ts > DELIVER_ACTIVITY_GRACE_MS) {
|
|
216
|
+
deliverActivityTimestamps.delete(queueKey);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function clearDeliverActivity(queueKey: string): void {
|
|
223
|
+
deliverActivityTimestamps.delete(queueKey);
|
|
224
|
+
}
|
|
201
225
|
const SESSION_QUEUE_TTL_MS = 5 * 60 * 1000; // 5 min
|
|
202
226
|
|
|
203
227
|
const QUEUE_BUSY_PHRASES = [
|
|
@@ -1395,7 +1419,11 @@ async function dispatchMessage(params: {
|
|
|
1395
1419
|
const { account, log } = ctx;
|
|
1396
1420
|
|
|
1397
1421
|
const queueKey = `${account.accountId}:${conversationId}`;
|
|
1398
|
-
|
|
1422
|
+
// Check both the explicit queue AND recent delivery activity.
|
|
1423
|
+
// The SDK's dispatchReplyFromConfig may resolve before the agent's full turn
|
|
1424
|
+
// completes (followup turns run in background), clearing the queue entry
|
|
1425
|
+
// while deliveries are still happening.
|
|
1426
|
+
const isQueueBusy = sessionQueues.has(queueKey) || hasActiveDelivery(queueKey);
|
|
1399
1427
|
|
|
1400
1428
|
// If queue is busy, add emotion reaction on user's message to indicate queued
|
|
1401
1429
|
let queueAckCleanup: (() => Promise<void>) | null = null;
|
|
@@ -1437,11 +1465,13 @@ async function dispatchMessage(params: {
|
|
|
1437
1465
|
// Clean up only if this is still the latest task
|
|
1438
1466
|
if (sessionQueues.get(queueKey) === currentTask) {
|
|
1439
1467
|
sessionQueues.delete(queueKey);
|
|
1468
|
+
log?.info?.("[dingtalk] Queue entry removed for " + queueKey + " (deliverActive=" + hasActiveDelivery(queueKey) + ")");
|
|
1440
1469
|
}
|
|
1441
1470
|
});
|
|
1442
1471
|
|
|
1443
1472
|
sessionQueues.set(queueKey, currentTask);
|
|
1444
1473
|
sessionQueueLastActivity.set(queueKey, Date.now());
|
|
1474
|
+
log?.info?.("[dingtalk] Queue entry set for " + queueKey + " (wasQueueBusy=" + isQueueBusy + ")");
|
|
1445
1475
|
|
|
1446
1476
|
// Don't await — fire-and-forget so message buffering and SDK callback stay responsive
|
|
1447
1477
|
}
|
|
@@ -1604,11 +1634,14 @@ async function dispatchMessageInternal(params: {
|
|
|
1604
1634
|
|
|
1605
1635
|
// Await dispatch so per-session queue waits for reply delivery to complete
|
|
1606
1636
|
// before starting the next queued message.
|
|
1637
|
+
const fallbackQueueKey = `${account.accountId}:${conversationId}`;
|
|
1607
1638
|
await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
1608
1639
|
ctx: ctxPayload,
|
|
1609
1640
|
cfg: actualCfg,
|
|
1610
1641
|
dispatcherOptions: {
|
|
1611
1642
|
deliver: async (payload: any) => {
|
|
1643
|
+
// Track delivery activity for queue busy detection
|
|
1644
|
+
markDeliverActivity(fallbackQueueKey);
|
|
1612
1645
|
// Recall typing indicator on first delivery
|
|
1613
1646
|
await cleanupTyping();
|
|
1614
1647
|
|
|
@@ -1757,9 +1790,12 @@ async function dispatchWithFullPipeline(params: {
|
|
|
1757
1790
|
}
|
|
1758
1791
|
|
|
1759
1792
|
// 8. Create typing-aware dispatcher
|
|
1793
|
+
const deliverQueueKey = `${account.accountId}:${conversationId}`;
|
|
1760
1794
|
const { dispatcher, replyOptions, markDispatchIdle } = rt.channel.reply.createReplyDispatcherWithTyping({
|
|
1761
1795
|
responsePrefix: '',
|
|
1762
1796
|
deliver: async (payload: any) => {
|
|
1797
|
+
// Track delivery activity for queue busy detection
|
|
1798
|
+
markDeliverActivity(deliverQueueKey);
|
|
1763
1799
|
// Recall typing indicator on first delivery
|
|
1764
1800
|
if (!firstReplyFired && onFirstReply) {
|
|
1765
1801
|
firstReplyFired = true;
|
|
@@ -1787,14 +1823,24 @@ async function dispatchWithFullPipeline(params: {
|
|
|
1787
1823
|
|
|
1788
1824
|
// 9. Dispatch reply from config
|
|
1789
1825
|
try {
|
|
1826
|
+
log?.info?.("[dingtalk] dispatchReplyFromConfig started for " + deliverQueueKey);
|
|
1790
1827
|
await rt.channel.reply.dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyOptions });
|
|
1828
|
+
log?.info?.("[dingtalk] dispatchReplyFromConfig completed for " + deliverQueueKey);
|
|
1791
1829
|
} finally {
|
|
1830
|
+
// OpenClaw 2026.4+ moved dispatcher.markComplete() + waitForIdle() out of
|
|
1831
|
+
// dispatchReplyFromConfig into the withReplyDispatcher wrapper. Since we call
|
|
1832
|
+
// dispatchReplyFromConfig directly (not through withReplyDispatcher), we must
|
|
1833
|
+
// do this ourselves to ensure all pending deliveries drain before returning.
|
|
1834
|
+
try {
|
|
1835
|
+
if (typeof dispatcher.markComplete === 'function') {
|
|
1836
|
+
dispatcher.markComplete();
|
|
1837
|
+
}
|
|
1838
|
+
await dispatcher.waitForIdle();
|
|
1839
|
+
log?.info?.("[dingtalk] dispatcher.waitForIdle completed for " + deliverQueueKey);
|
|
1840
|
+
} catch (err) {
|
|
1841
|
+
log?.info?.("[dingtalk] dispatcher settle error: " + err);
|
|
1842
|
+
}
|
|
1792
1843
|
markDispatchIdle();
|
|
1793
|
-
// Don't recall typing here — dispatchReplyFromConfig resolves when dispatch
|
|
1794
|
-
// is *initiated*, not when the agent finishes. The agent may still be doing
|
|
1795
|
-
// tool calls for many minutes before producing text. The deliver callback
|
|
1796
|
-
// above handles recall on first delivery. If the agent crashes without
|
|
1797
|
-
// replying, the emoji reaction simply stays — acceptable tradeoff.
|
|
1798
1844
|
}
|
|
1799
1845
|
|
|
1800
1846
|
// 10. Record activity
|