@essentialai/cogent-bridge
Version:
Cogent Bridge — cross-agent comms for Claude Code, OpenAI Codex and Slack. Codex users: install with `curl -fsSL https://cogent.tools/install.sh | sh`, then start with `cogent-codex` for real-time peer wake.
109 lines • 3.37 kB
JavaScript
/**
* In-memory message inbox with deduplication and timestamp ordering.
*
* Aggregates messages from both WebSocket push and poll fallback channels.
* Deduplicates by message ID (bounded to last 1000 IDs) and maintains
* messages ordered by server timestamp for consistent display.
*/
export class MessageInbox {
messages = [];
seenIds = new Set();
seenOrder = [];
listeners = [];
lastMessageId = null;
/**
* Add a single message to the inbox.
* Returns true if the message was new (not a duplicate).
*/
addMessage(msg) {
if (this.seenIds.has(msg.id)) {
return false;
}
// Track for dedup with bounded size
this.seenIds.add(msg.id);
this.seenOrder.push(msg.id);
if (this.seenOrder.length > 1000) {
const toRemove = this.seenOrder.shift();
this.seenIds.delete(toRemove);
}
// Insert maintaining timestamp order.
// Messages usually arrive in order, so search from the end.
let insertIdx = this.messages.length;
for (let i = this.messages.length - 1; i >= 0; i--) {
if (this.messages[i].timestamp <= msg.timestamp) {
insertIdx = i + 1;
break;
}
if (i === 0) {
insertIdx = 0;
}
}
this.messages.splice(insertIdx, 0, msg);
// Update lastMessageId to the latest message by timestamp
const last = this.messages[this.messages.length - 1];
this.lastMessageId = last.id;
// Notify listeners
for (const listener of this.listeners) {
listener();
}
return true;
}
/**
* Add multiple messages at once.
* Returns the count of new (non-duplicate) messages added.
*/
addMessages(msgs) {
let count = 0;
for (const msg of msgs) {
if (this.addMessage(msg)) {
count++;
}
}
return count;
}
/**
* Get all unread messages (copy of the internal array).
*/
getUnread() {
return this.messages.slice();
}
/**
* Mark messages as read by removing them from the unread list.
* Does NOT remove from seenIds -- they remain for deduplication.
*/
markRead(messageIds) {
const idsToRemove = new Set(messageIds);
this.messages = this.messages.filter((m) => !idsToRemove.has(m.id));
}
/**
* Get the ID of the last message received (by timestamp order).
* Used for poll sync -- pass as lastMessageId query parameter.
*/
getLastMessageId() {
return this.lastMessageId;
}
/**
* Subscribe to new message notifications.
* Returns an unsubscribe function.
*/
onNewMessage(listener) {
this.listeners.push(listener);
return () => {
const idx = this.listeners.indexOf(listener);
if (idx !== -1) {
this.listeners.splice(idx, 1);
}
};
}
/**
* Clear all state: messages, seen IDs, last message ID, and listeners.
*/
clear() {
this.messages = [];
this.seenIds = new Set();
this.seenOrder = [];
this.lastMessageId = null;
this.listeners = [];
}
}
//# sourceMappingURL=message-inbox.js.map