diff --git a/lib/alerts/telegram.mjs b/lib/alerts/telegram.mjs index 36a06cf..1a7f6ae 100644 --- a/lib/alerts/telegram.mjs +++ b/lib/alerts/telegram.mjs @@ -730,7 +730,7 @@ Respond with ONLY valid JSON: if (evaluation.signals?.length) { lines.push('', `*Signals:*`); for (const sig of evaluation.signals) { - lines.push(`• ${sig}`); + lines.push(`• ${escapeMd(sig)}`); } } @@ -742,6 +742,11 @@ Respond with ONLY valid JSON: // ─── Helpers ────────────────────────────────────────────────────────────── +function escapeMd(text) { + if (!text) return ''; + return text.replace(/([_*\[\]()~`>#+\-=|{}.!\\])/g, '\\$1'); +} + function parseJSON(text) { if (!text) return null; let cleaned = text.trim(); diff --git a/lib/llm/ideas.mjs b/lib/llm/ideas.mjs index 12a0fc1..1e78e23 100644 --- a/lib/llm/ideas.mjs +++ b/lib/llm/ideas.mjs @@ -88,10 +88,20 @@ function compactSweepForLLM(data, delta, previousIdeas) { sections.push(`SUPPLY_CHAIN: GSCPI=${data.gscpi.value} (${data.gscpi.interpretation})`); } - // Geopolitical signals + // Geopolitical signals (cap total OSINT text to ~1500 chars to keep prompt compact) const urgentPosts = (data.tg?.urgent || []).slice(0, 5); if (urgentPosts.length) { - sections.push(`URGENT_OSINT:\n${urgentPosts.map(p => `- ${p.text || ''}`).join('\n')}`); + const MAX_OSINT_CHARS = 1500; + let remaining = MAX_OSINT_CHARS; + const lines = []; + for (const p of urgentPosts) { + const text = p.text || ''; + if (remaining <= 0) break; + const trimmed = text.length > remaining ? text.substring(0, remaining) + '…' : text; + lines.push(`- ${trimmed}`); + remaining -= trimmed.length; + } + sections.push(`URGENT_OSINT:\n${lines.join('\n')}`); } // Thermal / fire detections