diff --git a/apps/broker/src/telegram-ai.ts b/apps/broker/src/telegram-ai.ts index cd64d75..8ce7e10 100644 --- a/apps/broker/src/telegram-ai.ts +++ b/apps/broker/src/telegram-ai.ts @@ -58,6 +58,14 @@ const TOOLS: AiTool[] = [ properties: {}, }, }, + { + name: "list_meshes", + description: "List all meshes this Telegram chat is connected to. Use when user asks about their meshes, which meshes are available, or wants to see their workspace list.", + input_schema: { + type: "object", + properties: {}, + }, + }, { name: "remember", description: "Store a memory/note in the mesh's shared knowledge. Use when user wants to save information for later.", @@ -140,6 +148,8 @@ const SYSTEM_PROMPT = `You are the claudemesh Telegram assistant. You help users You have access to tools for mesh operations. When the user's intent maps to a tool, use it. When it's a general question or conversation, respond directly. +IMPORTANT: Always respond in the same language the user writes in. If they write in Spanish, respond in Spanish. If English, respond in English. + Rules: - Be concise — Telegram messages should be short - When sending messages to peers, preserve the user's tone and intent @@ -246,13 +256,21 @@ export function formatResult(toolName: string, result: unknown): string { case "list_peers": { const peers = result as Array<{ displayName: string; status: string; summary?: string }>; - if (!peers || peers.length === 0) return "No peers online."; + if (!peers || peers.length === 0) return "No peers online\\."; return "👥 *Online peers:*\n\n" + peers.map(p => { const icon = p.status === "idle" ? "🟢" : p.status === "working" ? "🟡" : p.status === "dnd" ? "🔴" : "⚪"; return `${icon} *${escMd(p.displayName)}*${p.summary ? ` — ${escMd(p.summary)}` : ""}`; }).join("\n"); } + case "list_meshes": { + const meshes = result as Array<{ slug: string; peers: number }>; + if (!meshes || meshes.length === 0) return "No meshes connected\\. Use /connect to add one\\."; + return "🔗 *Connected meshes:*\n\n" + meshes.map(m => + `• *${escMd(m.slug)}* — ${m.peers} peer${m.peers !== 1 ? "s" : ""} online` + ).join("\n"); + } + case "recall": { const memories = result as Array<{ content: string; tags: string[] }>; if (!memories || memories.length === 0) return "No memories found."; diff --git a/apps/broker/src/telegram-bridge.ts b/apps/broker/src/telegram-bridge.ts index 01940ae..ba73d70 100644 --- a/apps/broker/src/telegram-bridge.ts +++ b/apps/broker/src/telegram-bridge.ts @@ -1592,7 +1592,7 @@ function setupBotCommands( // Gather context for the AI const firstMeshId = meshIds[0]!; const firstConn = meshConnections.get(firstMeshId); - const meshSlug = chatMeshSlugs.get(chatId)?.[0]; + const meshSlug = meshSlugs.get(firstMeshId) ?? firstMeshId.slice(0, 12); let recentPeers: string[] = []; if (firstConn?.isConnected()) { try { @@ -1706,12 +1706,27 @@ async function executeAiToolCall( case "list_peers": return conn.listPeers(); + case "list_meshes": { + const results: Array<{ slug: string; peers: number }> = []; + for (const meshId of meshIds) { + const conn = meshConnections.get(meshId); + const slug = meshSlugs.get(meshId) ?? meshId.slice(0, 12); + let peerCount = 0; + if (conn?.isConnected()) { + try { + const peers = await conn.listPeers(); + peerCount = peers.length; + } catch {} + } + results.push({ slug, peers: peerCount }); + } + return results; + } + case "remember": case "recall": case "get_state": case "set_state": - // These operations require WS request/response patterns not yet - // implemented in MeshConnection. Coming in a future update. throw new Error(`${toolCall.name} not yet available via Telegram. Use the CLI.`); default: