diff --git a/src/telegram/format.ts b/src/telegram/format.ts
index 1a0b9c0a583..aec5f9f2257 100644
--- a/src/telegram/format.ts
+++ b/src/telegram/format.ts
@@ -216,6 +216,20 @@ export function wrapFileReferencesInHtml(html: string): string {
return `${prefix}${escapeHtml(filename)}`;
});
+ // Second pass: catch orphaned single-letter TLD patterns (e.g., 'D.md' in 'R&D.md')
+ // These can be auto-linked by Telegram as domains
+ const orphanedTldPattern = new RegExp(
+ `([^a-zA-Z0-9]|^)([A-Za-z]\\.(?:${extensionsPattern}))(?=[^a-zA-Z0-9/]|$)`,
+ "g",
+ );
+ result = result.replace(orphanedTldPattern, (m, prefix, tld) => {
+ // Skip if already wrapped in a tag (check for < before or > after in context)
+ if (prefix === ">") {
+ return m;
+ }
+ return `${prefix}${escapeHtml(tld)}`;
+ });
+
return result;
}
diff --git a/src/telegram/format.wrap-md.test.ts b/src/telegram/format.wrap-md.test.ts
index 0b2b61ac9fd..5cc41e4c414 100644
--- a/src/telegram/format.wrap-md.test.ts
+++ b/src/telegram/format.wrap-md.test.ts
@@ -278,12 +278,20 @@ describe("edge cases", () => {
expect(wrapFileReferencesInHtml(input)).toBe(input);
});
- it("does not match filenames with special characters", () => {
- // The regex only matches [a-zA-Z0-9_.\\-./] so & breaks the pattern
+ it("wraps orphaned TLD pattern after special character", () => {
+ // R&D.md - the & breaks the main pattern, but D.md could be auto-linked
+ // So we wrap the orphaned D.md part to prevent Telegram linking it
const input = "R&D.md";
const result = wrapFileReferencesInHtml(input);
- // Not wrapped because & is not in the allowed character class
- expect(result).toBe(input);
+ expect(result).toBe("R&D.md");
+ });
+
+ it("wraps orphaned single-letter TLD patterns", () => {
+ const result1 = wrapFileReferencesInHtml("X.ai is cool");
+ expect(result1).toContain("X.ai");
+
+ const result2 = wrapFileReferencesInHtml("Check R.io");
+ expect(result2).toContain("R.io");
});
it("does not match filenames containing angle brackets", () => {