Fix Telegram Markdown formatting and clarify tool/memory distinctions

- Add escape_telegram_markdown() to handle underscores in tool names
  (e.g., build_software was breaking Telegram's Markdown parser)
- Use Telegram-compatible *bold* syntax instead of **bold**
- Clarify workspace memory vs filesystem tool descriptions to prevent
  LLM from using read_file on memory_tree paths
- Update build_software to strongly prefer Rust WASM for agent tools
- Rewrite WASM tool template to use Component Model with wit_bindgen
  instead of outdated extern "C" approach

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-05 00:37:19 -08:00
co-authored by Claude Opus 4.5
parent 9edc666ee3
commit aec42aceda
5 changed files with 191 additions and 85 deletions
+35 -7
View File
@@ -2,6 +2,29 @@
use std::sync::Arc;
/// Escape special characters for Telegram's legacy Markdown.
///
/// In Telegram's Markdown mode, these characters have special meaning:
/// - `_` starts/ends italic
/// - `*` starts/ends bold
/// - `` ` `` starts/ends code
/// - `[` starts a link
///
/// We escape them with backslash so dynamic content doesn't break formatting.
fn escape_telegram_markdown(s: &str) -> String {
let mut result = String::with_capacity(s.len());
for c in s.chars() {
match c {
'_' | '*' | '`' | '[' => {
result.push('\\');
result.push(c);
}
_ => result.push(c),
}
}
result
}
use futures::StreamExt;
use tokio::sync::Mutex;
use uuid::Uuid;
@@ -341,17 +364,22 @@ impl Agent {
} else {
params_preview
};
// Escape Markdown special chars in dynamic values to avoid breaking
// Telegram's Markdown parser (underscores, asterisks, backticks, brackets)
let tool_name_escaped = escape_telegram_markdown(&tool_name);
let description_escaped = escape_telegram_markdown(&description);
// Params go inside a code block, so no escaping needed there
Ok(Some(format!(
"🔒 Tool requires approval:\n\n\
**Tool:** {}\n\
**Description:** {}\n\
**Parameters:** ```\n{}\n```\n\n\
*Tool:* {}\n\
*Description:* {}\n\
*Parameters:*\n```\n{}\n```\n\n\
Reply with:\n\
- `yes` or `approve` to allow this tool\n\
- `always` to always allow this tool in this session\n\
- `no` or `deny` to reject\n\n\
yes or approve to allow this tool\n\
always to always allow this tool in this session\n\
no or deny to reject\n\n\
Request ID: {}",
tool_name, description, params_truncated, request_id
tool_name_escaped, description_escaped, params_truncated, request_id
)))
}
}
+1 -1
View File
@@ -244,7 +244,7 @@ impl HeartbeatRunner {
};
let response = OutgoingResponse {
content: format!("🔔 **Heartbeat Alert**\n\n{}", message),
content: format!("🔔 *Heartbeat Alert*\n\n{}", message),
thread_id: None,
metadata: serde_json::json!({
"source": "heartbeat",