@inerrata/channel 0.1.2 → 0.1.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 +25 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -116,11 +116,31 @@ function apiFetch(path, init) {
|
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
118
|
// ---------------------------------------------------------------------------
|
|
119
|
+
// Deduplication — prevents double-delivery when both the poll and a
|
|
120
|
+
// server-side SSE push fire for the same notification.
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
const seenNotifications = new Set();
|
|
123
|
+
const SEEN_MAX = 200;
|
|
124
|
+
function isDuplicate(id) {
|
|
125
|
+
if (seenNotifications.has(id))
|
|
126
|
+
return true;
|
|
127
|
+
seenNotifications.add(id);
|
|
128
|
+
// Prune oldest entry once the cap is hit
|
|
129
|
+
if (seenNotifications.size > SEEN_MAX) {
|
|
130
|
+
seenNotifications.delete(seenNotifications.values().next().value);
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
119
135
|
// Notification pusher — sends channel events into Claude Code
|
|
120
136
|
// ---------------------------------------------------------------------------
|
|
121
137
|
async function pushNotification(data) {
|
|
122
138
|
try {
|
|
123
139
|
const type = data.type ?? 'message';
|
|
140
|
+
// Deduplicate on the most stable ID available
|
|
141
|
+
const notifId = data.requestId ?? data.messageId;
|
|
142
|
+
if (notifId && isDuplicate(notifId))
|
|
143
|
+
return;
|
|
124
144
|
const fromHandle = data.fromHandle ?? data.from?.handle ?? 'unknown';
|
|
125
145
|
const preview = data.preview ?? '';
|
|
126
146
|
let content;
|
|
@@ -168,17 +188,18 @@ async function pollInbox() {
|
|
|
168
188
|
const requests = reqRes.ok
|
|
169
189
|
? (await reqRes.json())
|
|
170
190
|
: [];
|
|
171
|
-
// Push unread messages newer than last check
|
|
191
|
+
// Push unread messages newer than last check, then mark as read so
|
|
192
|
+
// subsequent polls (and other running instances) don't re-deliver.
|
|
172
193
|
for (const msg of msgs) {
|
|
173
194
|
if (!msg.read && msg.createdAt > lastCheckedAt) {
|
|
174
|
-
// Resolve sender handle
|
|
175
|
-
const handleRes = await apiFetch(`/messages/inbox?limit=1`);
|
|
176
195
|
await pushNotification({
|
|
177
196
|
type: 'message.received',
|
|
197
|
+
messageId: msg.id,
|
|
178
198
|
threadId: msg.threadId,
|
|
179
|
-
fromHandle: msg.
|
|
199
|
+
fromHandle: msg.fromHandle ?? msg.fromAgent,
|
|
180
200
|
preview: msg.body.slice(0, 200),
|
|
181
201
|
});
|
|
202
|
+
apiFetch(`/messages/${msg.id}/read`, { method: 'PATCH' }).catch(() => { });
|
|
182
203
|
}
|
|
183
204
|
}
|
|
184
205
|
// Push pending requests
|