@firstperson/firstperson 2026.1.35 → 2026.1.36
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/.claude/settings.local.json +2 -1
- package/package.json +1 -1
- package/src/channel.ts +38 -0
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -14,6 +14,30 @@ import type { FirstPersonConfig, ResolvedFirstPersonAccount, CoreConfig } from "
|
|
|
14
14
|
|
|
15
15
|
const DEFAULT_RELAY_URL = "wss://chat.firstperson.ai";
|
|
16
16
|
|
|
17
|
+
// Dedupe cache to track recently processed messages (20-minute window matches OpenClaw)
|
|
18
|
+
const DEDUPE_WINDOW_MS = 20 * 60 * 1000; // 20 minutes
|
|
19
|
+
const processedMessages = new Map<string, number>(); // messageId -> timestamp
|
|
20
|
+
|
|
21
|
+
function isDuplicateMessage(messageId: string): boolean {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
|
|
24
|
+
// Clean up old entries
|
|
25
|
+
for (const [id, timestamp] of processedMessages) {
|
|
26
|
+
if (now - timestamp > DEDUPE_WINDOW_MS) {
|
|
27
|
+
processedMessages.delete(id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if this message was already processed
|
|
32
|
+
if (processedMessages.has(messageId)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Mark as processed
|
|
37
|
+
processedMessages.set(messageId, now);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
17
41
|
const meta = {
|
|
18
42
|
id: "firstperson",
|
|
19
43
|
label: "First Person",
|
|
@@ -325,6 +349,20 @@ export const firstPersonPlugin: ChannelPlugin<ResolvedFirstPersonAccount> = {
|
|
|
325
349
|
onMessage: async (message) => {
|
|
326
350
|
log?.info(`[fp] 1. received: ${message.messageId} from ${message.deviceId}`);
|
|
327
351
|
|
|
352
|
+
// Check for duplicate message (dedupe within 20-minute window)
|
|
353
|
+
if (isDuplicateMessage(message.messageId)) {
|
|
354
|
+
log?.info(`[fp] skipping duplicate message: ${message.messageId}`);
|
|
355
|
+
// Send "skipped" status instead of processing
|
|
356
|
+
sendStatusUpdate({
|
|
357
|
+
to: message.deviceId,
|
|
358
|
+
messageId: message.messageId,
|
|
359
|
+
status: "completed", // Use completed but with skip indication in details
|
|
360
|
+
message: "Message already processed (duplicate)",
|
|
361
|
+
details: { skipped: true, reason: "duplicate" },
|
|
362
|
+
});
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
328
366
|
// Track timing for status updates
|
|
329
367
|
const dispatchStartTime = Date.now();
|
|
330
368
|
|