@unicitylabs/openclaw-unicity 0.5.3 → 0.5.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/package.json +1 -1
- package/src/channel.ts +48 -18
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -257,25 +257,15 @@ export const unicityChannelPlugin = {
|
|
|
257
257
|
const myNostrPubkey = sphere.groupChat?.getMyPublicKey?.() ?? null;
|
|
258
258
|
if (myNostrPubkey) selfPubkeys.add(myNostrPubkey);
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (msg.id) {
|
|
267
|
-
seenDmIds.add(msg.id);
|
|
268
|
-
if (seenDmIds.size > DM_SEEN_MAX) {
|
|
269
|
-
const first = seenDmIds.values().next().value!;
|
|
270
|
-
seenDmIds.delete(first);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
260
|
+
// Per-sender queue: while a reply is being generated for a sender, hold
|
|
261
|
+
// the latest incoming message so it gets processed after the current reply
|
|
262
|
+
// finishes. Prevents duplicate replies while not dropping follow-ups.
|
|
263
|
+
type DmMsg = { id: string; senderPubkey: string; senderNametag?: string; recipientPubkey: string; content: string; timestamp: number; isRead: boolean };
|
|
264
|
+
const sendersInFlight = new Set<string>();
|
|
265
|
+
const pendingPerSender = new Map<string, DmMsg>();
|
|
273
266
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
ctx.log?.debug(`[${ctx.account.accountId}] Skipping historical DM (ts=${msg.timestamp} < start=${dmStartTime})`);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
267
|
+
function dispatchDm(msg: DmMsg): void {
|
|
268
|
+
sendersInFlight.add(msg.senderPubkey);
|
|
279
269
|
|
|
280
270
|
// Immediately signal that we're composing a reply
|
|
281
271
|
sphere.communications.sendComposingIndicator(msg.senderPubkey)
|
|
@@ -354,7 +344,47 @@ export const unicityChannelPlugin = {
|
|
|
354
344
|
})
|
|
355
345
|
.catch((err: unknown) => {
|
|
356
346
|
ctx.log?.error(`[${ctx.account.accountId}] Reply dispatch error: ${err}`);
|
|
347
|
+
})
|
|
348
|
+
.finally(() => {
|
|
349
|
+
sendersInFlight.delete(msg.senderPubkey);
|
|
350
|
+
// If a follow-up message arrived while we were replying, process it now
|
|
351
|
+
const pending = pendingPerSender.get(msg.senderPubkey);
|
|
352
|
+
if (pending) {
|
|
353
|
+
pendingPerSender.delete(msg.senderPubkey);
|
|
354
|
+
ctx.log?.info(`[${ctx.account.accountId}] Processing queued DM from ${peerId}`);
|
|
355
|
+
dispatchDm(pending);
|
|
356
|
+
}
|
|
357
357
|
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const unsub = sphere.communications.onDirectMessage((msg) => {
|
|
361
|
+
// Skip messages from self (own DMs echoed back by the relay)
|
|
362
|
+
if (selfPubkeys.has(msg.senderPubkey)) return;
|
|
363
|
+
|
|
364
|
+
// Deduplicate: skip already-processed messages (relays may deliver dupes)
|
|
365
|
+
if (msg.id && seenDmIds.has(msg.id)) return;
|
|
366
|
+
if (msg.id) {
|
|
367
|
+
seenDmIds.add(msg.id);
|
|
368
|
+
if (seenDmIds.size > DM_SEEN_MAX) {
|
|
369
|
+
const first = seenDmIds.values().next().value!;
|
|
370
|
+
seenDmIds.delete(first);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Skip historical messages replayed on connect
|
|
375
|
+
if (msg.timestamp && msg.timestamp < dmStartTime) {
|
|
376
|
+
ctx.log?.debug(`[${ctx.account.accountId}] Skipping historical DM (ts=${msg.timestamp} < start=${dmStartTime})`);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Per-sender lock: queue if we're already generating a reply for this sender
|
|
381
|
+
if (sendersInFlight.has(msg.senderPubkey)) {
|
|
382
|
+
ctx.log?.debug(`[${ctx.account.accountId}] Queuing DM from ${msg.senderPubkey.slice(0, 16)}… — reply in flight`);
|
|
383
|
+
pendingPerSender.set(msg.senderPubkey, msg);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
dispatchDm(msg);
|
|
358
388
|
});
|
|
359
389
|
|
|
360
390
|
ctx.log?.info(`[${ctx.account.accountId}] Unicity DM listener active`);
|