Prevent historical Teams chat replay as fresh transcript input.

Add chat warmup guard and key-based deduplication so old thread messages are not re-emitted with new timestamps after join.

Made-with: Cursor
This commit is contained in:
ValueOn AG 2026-02-26 21:41:56 +01:00
parent abf38eada3
commit 5caa5a047e

View file

@ -20,6 +20,8 @@ export class ChatProcedure {
private _onChatMessage: (entry: ChatMessageEntry) => void;
private _isSubscribed: boolean = false;
private _lastMessageText: string = '';
private _chatReadyAtMs: number = 0;
private _recentMessageKeyTimestamps: Map<string, number> = new Map();
constructor(
page: Page,
@ -119,6 +121,7 @@ export class ChatProcedure {
speaker: string;
text: string;
timestamp: string;
messageKey?: string;
}) => {
this._handleChatMessage(msg);
});
@ -204,6 +207,7 @@ export class ChatProcedure {
speaker: author,
text,
timestamp: new Date().toISOString(),
messageKey: `${author}::${text}`,
});
return true;
}
@ -239,6 +243,7 @@ export class ChatProcedure {
speaker: candidateName,
text: candidateBody,
timestamp: new Date().toISOString(),
messageKey: `${candidateName}::${candidateBody}`,
});
return true;
}
@ -320,16 +325,32 @@ export class ChatProcedure {
});
this._logger.info(`Chat MutationObserver set up (target: ${chatObserverTarget})`);
// Guard against Teams replaying historical thread entries as fresh mutations.
this._chatReadyAtMs = Date.now() + 12000;
this._logger.info('Chat monitor warmup active for 12s');
}
/**
* Handle a chat message event from the browser.
*/
private _handleChatMessage(msg: { speaker: string; text: string; timestamp: string }): void {
private _handleChatMessage(msg: { speaker: string; text: string; timestamp: string; messageKey?: string }): void {
if (!this._isSubscribed || !msg.text) return;
const nowMs = Date.now();
if (this._chatReadyAtMs > 0 && nowMs < this._chatReadyAtMs) return;
// Dedup
if (msg.text === this._lastMessageText) return;
const key = msg.messageKey || `${msg.speaker}::${msg.text}`;
const lastSeen = this._recentMessageKeyTimestamps.get(key);
if (lastSeen && (nowMs - lastSeen) < 120000) return;
this._recentMessageKeyTimestamps.set(key, nowMs);
if (this._recentMessageKeyTimestamps.size > 400) {
const cutoff = nowMs - 10 * 60 * 1000;
for (const [k, ts] of this._recentMessageKeyTimestamps.entries()) {
if (ts < cutoff) this._recentMessageKeyTimestamps.delete(k);
}
}
this._lastMessageText = msg.text;
this._logger.info(`Chat: [${msg.speaker}] ${msg.text}`);