feat(telegram): wire replyToMode config, add forum topic support, fix messaging tool duplicates
Changes: - Default replyToMode from "off" to "first" for better threading UX - Add messageThreadId and replyToMessageId params for forum topic support - Add messaging tool duplicate detection to suppress redundant block replies - Add sendMessage action to telegram tool schema - Add @grammyjs/types devDependency for proper TypeScript typing - Remove @ts-nocheck and fix all type errors in send.ts - Add comprehensive docs/telegram.md documentation - Add PR-326-REVIEW.md with John Carmack-level code review Test coverage: - normalizeTextForComparison: 5 cases - isMessagingToolDuplicate: 7 cases - sendMessageTelegram thread params: 5 cases - handleTelegramAction sendMessage: 4 cases - Forum topic isolation: 4 cases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Peter Steinberger
parent
6cd32ec7f6
commit
33e2d53be3
@@ -346,3 +346,50 @@ export function validateGeminiTurns(messages: AgentMessage[]): AgentMessage[] {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Messaging tool duplicate detection ──────────────────────────────────────
|
||||
// When the agent uses a messaging tool (telegram, discord, slack, sessions_send)
|
||||
// to send a message, we track the text so we can suppress duplicate block replies.
|
||||
// The LLM sometimes elaborates or wraps the same content, so we use substring matching.
|
||||
|
||||
const MIN_DUPLICATE_TEXT_LENGTH = 10;
|
||||
|
||||
/**
|
||||
* Normalize text for duplicate comparison.
|
||||
* - Trims whitespace
|
||||
* - Lowercases
|
||||
* - Strips emoji (Emoji_Presentation and Extended_Pictographic)
|
||||
* - Collapses multiple spaces to single space
|
||||
*/
|
||||
export function normalizeTextForComparison(text: string): string {
|
||||
return text
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\p{Emoji_Presentation}|\p{Extended_Pictographic}/gu, "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a text is a duplicate of any previously sent messaging tool text.
|
||||
* Uses substring matching to handle LLM elaboration (e.g., wrapping in quotes,
|
||||
* adding context, or slight rephrasing that includes the original).
|
||||
*/
|
||||
export function isMessagingToolDuplicate(
|
||||
text: string,
|
||||
sentTexts: string[],
|
||||
): boolean {
|
||||
if (sentTexts.length === 0) return false;
|
||||
const normalized = normalizeTextForComparison(text);
|
||||
if (!normalized || normalized.length < MIN_DUPLICATE_TEXT_LENGTH)
|
||||
return false;
|
||||
return sentTexts.some((sent) => {
|
||||
const normalizedSent = normalizeTextForComparison(sent);
|
||||
if (!normalizedSent || normalizedSent.length < MIN_DUPLICATE_TEXT_LENGTH)
|
||||
return false;
|
||||
// Substring match: either text contains the other
|
||||
return (
|
||||
normalized.includes(normalizedSent) || normalizedSent.includes(normalized)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user