talon-agent 3.0.4 → 3.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "talon-agent",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "description": "Multi-frontend AI agent with full tool access, streaming, cron jobs, and plugin system",
5
5
  "author": "Dylan Neve",
6
6
  "license": "MIT",
@@ -69,24 +69,29 @@ export function markdownToTelegramHtml(text: string): string {
69
69
  );
70
70
  // Italic: _text_ (surrounded by non-word or start/end)
71
71
  processed = processed.replace(/(?<!\w)_(.+?)_(?!\w)/g, "<i>$1</i>");
72
- // Links: [text](url) — only allow safe URL schemes; escape the URL to
73
- // prevent HTML attribute injection (e.g. href="url" onmouseover="x")
72
+ // Links: [text](url) — only safe URL schemes become anchors. Both text
73
+ // and url were already HTML-escaped by step 3 (quotes included, so the
74
+ // href attribute can't be broken out of); escaping again here corrupted
75
+ // every & in a query string into &amp;amp;.
74
76
  processed = processed.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, text, url) =>
75
- /^https?:\/\//i.test(url)
76
- ? `<a href="${escapeHtml(url)}">${escapeHtml(text)}</a>`
77
- : text,
77
+ /^https?:\/\//i.test(url) ? `<a href="${url}">${text}</a>` : text,
78
78
  );
79
79
  // Strikethrough: ~~text~~
80
80
  processed = processed.replace(/~~(.+?)~~/g, "<s>$1</s>");
81
81
 
82
- // Step 5: Restore inline code spans.
82
+ // Steps 5+6: Restore code spans and fenced blocks. The replacement MUST
83
+ // go through a function: with a string, String.replace interprets $-
84
+ // substitution patterns in the *code content* ($& re-inserts the
85
+ // placeholder, $\` splices the whole preceding message), which is how
86
+ // \`$&\` in a code span used to leak a stranded INLINECODEn into chat.
83
87
  for (let i = 0; i < inlineCode.length; i++) {
84
- processed = processed.replace(`\x00INLINECODE${i}\x00`, inlineCode[i]);
88
+ processed = processed.replace(
89
+ `\x00INLINECODE${i}\x00`,
90
+ () => inlineCode[i],
91
+ );
85
92
  }
86
-
87
- // Step 6: Restore fenced code blocks.
88
93
  for (let i = 0; i < codeBlocks.length; i++) {
89
- processed = processed.replace(`\x00CODEBLOCK${i}\x00`, codeBlocks[i]);
94
+ processed = processed.replace(`\x00CODEBLOCK${i}\x00`, () => codeBlocks[i]);
90
95
  }
91
96
 
92
97
  return processed;