@ebowwa/glm-daemon 0.4.1 → 0.4.3

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": "@ebowwa/glm-daemon",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Autonomous GLM 4.7 daemon with hooks, tools, teammates, and communication channels (Telegram, Discord)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -54,18 +54,17 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@ebowwa/ai": "^0.1.2",
57
+ "@ebowwa/channel-telegram": "^1.13.0",
57
58
  "@ebowwa/channel-types": "^0.1.1",
58
59
  "@ebowwa/codespaces-types": "^1.4.0",
59
60
  "@ebowwa/structured-prompts": "^0.3.2",
60
61
  "@ebowwa/teammates": "^0.1.2",
61
62
  "discord.js": "^14.16.3",
62
- "node-telegram-bot-api": "^0.66.0",
63
63
  "zod": "^4.3.5"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/bun": "catalog:dev",
67
67
  "@types/node": "catalog:dev",
68
- "@types/node-telegram-bot-api": "^0.64.13",
69
68
  "typescript": "catalog:",
70
69
  "bun-types": "catalog:dev"
71
70
  },
@@ -20,7 +20,8 @@ import {
20
20
  RICH_CAPABILITIES,
21
21
  } from "@ebowwa/channel-types";
22
22
  import { GLMClient } from "@ebowwa/ai";
23
- import { GLMDaemon, type GLMDaemonConfig } from "@ebowwa/glm-daemon";
23
+ import { GLMDaemon, type GLMDaemonConfig } from "../daemon.js";
24
+ import { BUILTIN_TOOLS, toGLMFormat, executeBuiltinTool } from "../builtin-tools.js";
24
25
 
25
26
  // ============================================================
26
27
  // CHANNEL CONFIG (extends base config from channel-types)
@@ -331,7 +332,7 @@ export abstract class BaseChannel implements ChannelConnector {
331
332
  // ============================================================
332
333
 
333
334
  /**
334
- * Handle chat messages locally with GLM
335
+ * Handle chat messages locally with GLM + tools
335
336
  */
336
337
  protected async handleChat(content: string, context: MessageContext): Promise<RouteResult> {
337
338
  const start = Date.now();
@@ -340,20 +341,28 @@ export abstract class BaseChannel implements ChannelConnector {
340
341
  const history = this.conversationHistory.get(context.userId) || [];
341
342
 
342
343
  // Build system prompt with context
343
- const systemPrompt = `You are a helpful AI assistant. You have access to conversation history and user context.
344
+ const systemPrompt = `You are a helpful AI assistant with tool access. You have access to conversation history and user context.
344
345
 
345
346
  User: ${context.userName || context.userId}
346
347
  Time: ${context.timestamp.toISOString()}
348
+ Working Directory: ${this.config.daemonWorkdir || process.cwd()}
347
349
 
348
- Be concise, helpful, and friendly. If the user asks about your capabilities, explain that you can:
349
- - Answer questions and have conversations
350
- - Handle coding tasks by delegating to a specialized daemon
351
- - Check status of ongoing work
350
+ You have access to tools for:
351
+ - File operations (read_file, write_file, edit_file, list_dir)
352
+ - Shell commands (run_command) - use for git, system info, etc.
353
+ - Git operations (git_status)
354
+ - System info (system_info)
352
355
 
353
- For complex coding tasks, suggest the user phrase it as a task request.`;
356
+ USE TOOLS when the user asks you to do something (not just chat). Examples:
357
+ - "list files" → use list_dir
358
+ - "read package.json" → use read_file
359
+ - "what's the git status" → use git_status
360
+ - "run npm test" → use run_command
361
+
362
+ Always use tools when appropriate. Be helpful and execute tasks.`;
354
363
 
355
364
  // Build messages array with system prompt and history
356
- const messages: Array<{ role: "system" | "user" | "assistant"; content: string }> = [
365
+ const messages: Array<{ role: "system" | "user" | "assistant"; content: string; tool_calls?: unknown[]; tool_call_id?: string }> = [
357
366
  { role: "system", content: systemPrompt },
358
367
  ...history.slice(-10).map(msg => ({
359
368
  role: msg.role as "user" | "assistant",
@@ -362,22 +371,73 @@ For complex coding tasks, suggest the user phrase it as a task request.`;
362
371
  { role: "user", content }
363
372
  ];
364
373
 
365
- const response = await this.glmClient.chatCompletion(messages, {
374
+ // Get tools in GLM format
375
+ const tools = toGLMFormat(BUILTIN_TOOLS);
376
+
377
+ // First call with tools
378
+ let response = await this.glmClient.chatCompletion(messages, {
366
379
  temperature: 0.7,
367
- maxTokens: 4096
380
+ maxTokens: 4096,
381
+ tools
368
382
  });
369
383
 
370
- // Extract response text from chat completion response
371
- const responseText = response.choices?.[0]?.message?.content || String(response);
384
+ // Handle tool calls in a loop (max 5 iterations to prevent infinite loops)
385
+ let iterations = 0;
386
+ const maxIterations = 5;
387
+ let responseMessage = response.choices?.[0]?.message;
388
+
389
+ while (responseMessage?.tool_calls && iterations < maxIterations) {
390
+ iterations++;
391
+ console.log(`[${this.constructor.name}] Tool call iteration ${iterations}`);
392
+
393
+ // Add assistant message with tool calls to history
394
+ messages.push({
395
+ role: "assistant",
396
+ content: responseMessage.content || "",
397
+ tool_calls: responseMessage.tool_calls
398
+ });
399
+
400
+ // Execute each tool call
401
+ for (const toolCall of responseMessage.tool_calls as Array<{ id: string; function: { name: string; arguments: string } }>) {
402
+ const toolName = toolCall.function.name;
403
+ const toolArgs = JSON.parse(toolCall.function.arguments);
404
+
405
+ console.log(`[${this.constructor.name}] Executing tool: ${toolName}(${JSON.stringify(toolArgs).slice(0, 100)})`);
406
+
407
+ // Execute the tool
408
+ const toolResult = await executeBuiltinTool(toolName, toolArgs);
409
+
410
+ console.log(`[${this.constructor.name}] Tool result: ${toolResult.slice(0, 100)}...`);
411
+
412
+ // Add tool result to messages
413
+ messages.push({
414
+ role: "tool",
415
+ content: toolResult,
416
+ tool_call_id: toolCall.id
417
+ } as any);
418
+ }
419
+
420
+ // Get next response (without tools to get final answer)
421
+ response = await this.glmClient.chatCompletion(messages, {
422
+ temperature: 0.7,
423
+ maxTokens: 4096,
424
+ tools
425
+ });
426
+ responseMessage = response.choices?.[0]?.message;
427
+ }
428
+
429
+ // Extract final response text
430
+ const responseText = responseMessage?.content || "No response generated.";
372
431
 
373
432
  this.addToHistory(context.userId, { role: "assistant", content: responseText });
374
433
 
375
434
  return {
376
435
  source: "butler",
377
436
  response: responseText,
378
- metadata: { executionTime: Date.now() - start },
437
+ metadata: { executionTime: Date.now() - start, toolIterations: iterations },
379
438
  };
380
439
  } catch (error) {
440
+ console.error(`[${this.constructor.name}] handleChat error:`, error);
381
441
  return {
382
442
  source: "error",
383
443
  response: `Sorry, I encountered an error: ${error instanceof Error ? error.message : String(error)}`,
@@ -1,27 +1,21 @@
1
1
  /**
2
- * Telegram Channel Adapter
2
+ * Telegram Channel for GLM Daemon
3
3
  *
4
- * Implements ChannelConnector from @ebowwa/channel-types.
5
- * Telegram bot using node-telegram-bot-api.
4
+ * Wraps @ebowwa/channel-telegram (pure protocol adapter)
5
+ * with GLM Daemon intelligence (GLM client, daemon delegation, routing).
6
6
  */
7
7
 
8
- import TelegramBot from "node-telegram-bot-api";
9
8
  import {
10
9
  type ChannelId,
11
- type ChannelMessage,
12
- type ChannelResponse,
13
10
  type ChannelCapabilities,
14
- type MessageSender,
15
- type MessageContext,
16
- createChannelId,
17
- RICH_CAPABILITIES,
18
11
  } from "@ebowwa/channel-types";
12
+ import {
13
+ TelegramChannel as TelegramProtocolAdapter,
14
+ type TelegramConfig as ProtocolConfig,
15
+ } from "@ebowwa/channel-telegram";
19
16
  import {
20
17
  BaseChannel,
21
18
  type GLMChannelConfig,
22
- type BaseChannelConfig,
23
- type MessageContext as InternalMessageContext,
24
- type RouteResult,
25
19
  } from "./base.js";
26
20
 
27
21
  // ============================================================
@@ -44,7 +38,7 @@ export interface TelegramChannelConfig extends GLMChannelConfig {
44
38
  export type { TelegramChannelConfig as TelegramConfig };
45
39
 
46
40
  // ============================================================
47
- // TELEGRAM CHANNEL
41
+ // TELEGRAM CHANNEL (Wraps @ebowwa/channel-telegram)
48
42
  // ============================================================
49
43
 
50
44
  export class TelegramChannel extends BaseChannel {
@@ -56,7 +50,7 @@ export class TelegramChannel extends BaseChannel {
56
50
  media: true,
57
51
  replies: true,
58
52
  threads: false,
59
- reactions: true, // Now supported via message_reaction
53
+ reactions: true,
60
54
  editing: true,
61
55
  streaming: false,
62
56
  },
@@ -70,20 +64,26 @@ export class TelegramChannel extends BaseChannel {
70
64
  },
71
65
  };
72
66
 
73
- private bot: TelegramBot;
74
- private token: string;
75
- private testChatId?: string;
76
- private allowedUsers?: number[];
77
- private allowedChats?: number[];
67
+ private protocolAdapter: TelegramProtocolAdapter;
68
+ private telegramConfig: {
69
+ botToken: string;
70
+ testChatId?: string;
71
+ allowedUsers?: number[];
72
+ allowedChats?: number[];
73
+ };
78
74
 
79
75
  constructor(config: TelegramChannelConfig) {
80
76
  super(config);
81
- this.token = config.botToken;
82
- this.testChatId = config.testChatId;
83
- this.allowedUsers = config.allowedUsers;
84
- this.allowedChats = config.allowedChats;
77
+
78
+ this.telegramConfig = {
79
+ botToken: config.botToken,
80
+ testChatId: config.testChatId,
81
+ allowedUsers: config.allowedUsers,
82
+ allowedChats: config.allowedChats,
83
+ };
84
+
85
85
  this.id = this.createId();
86
- this.bot = new TelegramBot(this.token, { polling: false }); // Don't start polling yet
86
+ this.protocolAdapter = new TelegramProtocolAdapter(this.telegramConfig);
87
87
  }
88
88
 
89
89
  // ============================================================
@@ -91,339 +91,77 @@ export class TelegramChannel extends BaseChannel {
91
91
  // ============================================================
92
92
 
93
93
  /**
94
- * Send response to Telegram
94
+ * Send response to Telegram via protocol adapter
95
95
  */
96
- async send(response: ChannelResponse): Promise<void> {
97
- const chatId = this.extractChatId(response.replyTo);
98
- if (!chatId) {
99
- console.error("[Telegram] Cannot send: no chat ID in response");
100
- return;
101
- }
102
-
103
- await this.sendResponse(chatId, response.content.text);
96
+ async send(response: Parameters<TelegramProtocolAdapter['send']>[0]): Promise<void> {
97
+ await this.protocolAdapter.send(response);
104
98
  }
105
99
 
106
100
  // ============================================================
107
- // Platform-Specific Implementation
101
+ // Platform-Specific Implementation (BaseChannel abstract methods)
108
102
  // ============================================================
109
103
 
110
104
  /**
111
- * Start Telegram bot
105
+ * Start Telegram bot via protocol adapter
112
106
  */
113
107
  protected async startPlatform(): Promise<void> {
114
- console.log("[Telegram] Starting bot...");
115
-
116
- // Start polling with all required update types
117
- this.bot = new TelegramBot(this.token, {
118
- polling: {
119
- interval: 300,
120
- autoStart: true,
121
- params: {
122
- allowed_updates: ['message', 'edited_message', 'message_reaction', 'callback_query']
123
- }
124
- }
125
- });
126
-
127
- // Handle /start command
128
- this.bot.onText(/\/start/, async (msg: TelegramBot.Message) => {
129
- const chatId = msg.chat.id;
130
- await this.bot.sendMessage(
131
- chatId,
132
- "👋 Hello! I'm the GLM Daemon Telegram Bot.\n\n" +
133
- "I can help you with:\n" +
134
- "- Chat and questions (handled locally)\n" +
135
- "- Coding tasks and features (delegated to daemon)\n\n" +
136
- "Just send me a message!"
137
- );
138
- });
139
-
140
- // Handle all text messages (with reply context)
141
- this.bot.on("message", async (msg: TelegramBot.Message) => {
142
- if (!msg.text) return;
143
- if (msg.text.startsWith("/")) return;
144
-
145
- const chatId = msg.chat.id;
146
- const userId = msg.from?.id;
147
-
148
- // Check allowlist
149
- if (!this.isAllowed(userId, chatId)) {
150
- console.log(`[Telegram] Blocked message from unauthorized user/chat: ${userId}/${chatId}`);
151
- return;
152
- }
153
-
154
- const userName = msg.from?.first_name || msg.from?.username || "User";
155
-
156
- // Check if this is a reply
157
- const replyContext = msg.reply_to_message ? {
158
- originalText: msg.reply_to_message.text,
159
- originalFrom: msg.reply_to_message.from?.first_name || 'User',
160
- originalMessageId: msg.reply_to_message.message_id
161
- } : null;
162
-
163
- if (replyContext) {
164
- console.log(`[Telegram] ${userName} (replying to ${replyContext.originalFrom}): ${msg.text}`);
165
- } else {
166
- console.log(`[Telegram] ${userName}: ${msg.text}`);
167
- }
168
-
169
- // Build message with reply context
170
- let messageText = msg.text;
171
- if (replyContext?.originalText) {
172
- messageText = `[Replying to ${replyContext.originalFrom}: "${replyContext.originalText.slice(0, 100)}"]\n\n${msg.text}`;
173
- }
174
-
175
- // Show typing indicator
176
- await this.bot.sendChatAction(chatId, "typing");
177
-
178
- try {
179
- // Create normalized ChannelMessage
180
- const message = this.createChannelMessage(msg);
181
- if (replyContext) {
182
- message.text = messageText; // Override with context
183
- }
184
-
185
- // Route through base channel
186
- const response = await this.routeChannelMessage(message);
187
-
188
- // Send response - thread to original if reply
189
- if (replyContext) {
190
- await this.bot.sendMessage(chatId, response.content.text, {
191
- reply_to_message_id: msg.message_id,
192
- parse_mode: "Markdown"
193
- });
194
- } else {
195
- await this.send(response);
196
- }
197
- } catch (error) {
198
- console.error("[Telegram] Error handling message:", error);
199
- await this.bot.sendMessage(
200
- chatId,
201
- `Sorry, I encountered an error: ${error instanceof Error ? error.message : String(error)}`
202
- );
203
- }
204
- });
205
-
206
- // Handle message edits
207
- this.bot.on("edited_message", async (msg: TelegramBot.Message) => {
208
- if (!msg.text) return;
209
-
210
- const chatId = msg.chat.id;
211
- const userId = msg.from?.id;
212
- const userName = msg.from?.first_name || "User";
213
-
214
- if (!this.isAllowed(userId, chatId)) return;
215
-
216
- console.log(`[Telegram] EDIT ${userName}: ${msg.text.slice(0, 50)}...`);
217
-
218
- // Notify about edit and re-process
219
- await this.bot.sendChatAction(chatId, "typing");
220
-
221
- try {
222
- const message = this.createChannelMessage(msg);
223
- message.text = `[User edited their message to: "${msg.text}"]`;
108
+ console.log("[TelegramChannel] Starting via protocol adapter...");
224
109
 
225
- const response = await this.routeChannelMessage(message);
226
- await this.bot.sendMessage(chatId, `📝 *Edit noted:*\n\n${response.content.text}`, {
227
- parse_mode: "Markdown"
228
- });
229
- } catch (error) {
230
- console.error("[Telegram] Error handling edit:", error);
231
- }
110
+ // Connect protocol adapter to our routing logic
111
+ this.protocolAdapter.onMessage(async (message) => {
112
+ // Route through BaseChannel's routing logic (GLM + daemon)
113
+ return this.routeChannelMessage(message);
232
114
  });
233
115
 
234
- // Handle reactions (likes/emojis) - Telegram Bot API 5.0+
235
- // Type definitions may not include this yet
236
- (this.bot as any).on("message_reaction", async (reaction: {
237
- chat: { id: number };
238
- message_id: number;
239
- user: { id: number; first_name?: string };
240
- old_reaction: Array<{ type: string; emoji?: string }>;
241
- new_reaction: Array<{ type: string; emoji?: string }>;
242
- }) => {
243
- const chatId = reaction.chat.id;
244
- const userName = reaction.user.first_name || "User";
245
-
246
- const newEmojis = reaction.new_reaction
247
- .filter(r => r.type === 'emoji' && r.emoji)
248
- .map(r => r.emoji!);
249
-
250
- const oldEmojis = reaction.old_reaction
251
- .filter(r => r.type === 'emoji' && r.emoji)
252
- .map(r => r.emoji!);
253
-
254
- const added = newEmojis.filter(e => !oldEmojis.includes(e));
255
- const removed = oldEmojis.filter(e => !newEmojis.includes(e));
256
-
257
- console.log(`[Telegram] REACTION by ${userName}: +${added.join(',')} -${removed.join(',')} on msg ${reaction.message_id}`);
258
-
259
- // Respond to negative feedback
260
- if (added.includes('👎')) {
261
- await this.bot.sendMessage(chatId, `Thanks for the feedback, ${userName}. I'll try to do better!`);
262
- }
263
- });
264
-
265
- // Handle callback queries (inline button reactions)
266
- this.bot.on("callback_query", async (query: TelegramBot.CallbackQuery) => {
267
- const chatId = query.message?.chat.id;
268
- const data = query.data;
269
- const userName = query.from.first_name || "User";
270
-
271
- if (!chatId || !data) {
272
- await this.bot.answerCallbackQuery(query.id);
273
- return;
274
- }
275
-
276
- console.log(`[Telegram] CALLBACK ${userName}: ${data}`);
277
-
278
- const [action, value] = data.split(':');
279
-
280
- switch (action) {
281
- case 'react': {
282
- const count = parseInt(value) || 0;
283
- const newCount = count + 1;
284
- await this.bot.editMessageReplyMarkup(
285
- { inline_keyboard: [[
286
- { text: `👍 ${newCount}`, callback_data: `react:${newCount}` }
287
- ]]},
288
- { chat_id: chatId, message_id: query.message?.message_id }
289
- );
290
- break;
291
- }
292
- case 'approve':
293
- await this.bot.sendMessage(chatId, `✅ Approved by ${userName}`);
294
- break;
295
- case 'reject':
296
- await this.bot.sendMessage(chatId, `❌ Rejected by ${userName}`);
297
- break;
298
- case 'cancel':
299
- await this.bot.sendMessage(chatId, `🛑 Task cancelled by ${userName}`);
300
- break;
301
- }
302
-
303
- await this.bot.answerCallbackQuery(query.id);
304
- });
305
-
306
- // Handle polling errors
307
- this.bot.on("polling_error", (error: Error) => {
308
- console.error("[Telegram] Polling error:", error);
309
- });
310
-
311
- console.log("[Telegram] Bot is running!");
312
-
313
- // Send test message if chat ID provided
314
- if (this.testChatId) {
315
- await this.sendTestMessage(Number(this.testChatId));
316
- }
116
+ await this.protocolAdapter.start();
117
+ console.log("[TelegramChannel] Protocol adapter started");
317
118
  }
318
119
 
319
120
  /**
320
- * Stop Telegram bot
121
+ * Stop Telegram bot via protocol adapter
321
122
  */
322
123
  protected async stopPlatform(): Promise<void> {
323
- console.log("[Telegram] Stopping bot...");
324
- await this.bot.stopPolling();
124
+ console.log("[TelegramChannel] Stopping protocol adapter...");
125
+ await this.protocolAdapter.stop();
325
126
  }
326
127
 
327
128
  // ============================================================
328
- // Telegram-Specific Helpers
129
+ // Telegram-Specific Helpers (delegated to protocol adapter)
329
130
  // ============================================================
330
131
 
331
132
  /**
332
- * Create normalized ChannelMessage from Telegram message
133
+ * Get underlying bot instance for advanced operations
333
134
  */
334
- private createChannelMessage(msg: TelegramBot.Message): ChannelMessage {
335
- const sender: MessageSender = {
336
- id: msg.from?.id?.toString() || msg.chat.id.toString(),
337
- username: msg.from?.username,
338
- displayName: msg.from?.first_name || msg.from?.username,
339
- isBot: msg.from?.is_bot || false,
340
- };
341
-
342
- const context: MessageContext = {
343
- isDM: msg.chat.type === "private",
344
- groupName: msg.chat.type !== "private" ? msg.chat.title : undefined,
345
- };
346
-
347
- return {
348
- messageId: msg.message_id.toString(),
349
- channelId: this.id,
350
- timestamp: new Date(msg.date * 1000),
351
- sender,
352
- text: msg.text || "",
353
- context,
354
- replyTo: msg.reply_to_message
355
- ? {
356
- messageId: msg.reply_to_message.message_id.toString(),
357
- channelId: this.id,
358
- }
359
- : undefined,
360
- };
135
+ getBot() {
136
+ return this.protocolAdapter.getBot();
361
137
  }
362
138
 
363
139
  /**
364
- * Check if user/chat is allowed
140
+ * Send a simple message
365
141
  */
366
- private isAllowed(userId?: number, chatId?: number): boolean {
367
- // No allowlist configured = allow all
368
- if (!this.allowedUsers?.length && !this.allowedChats?.length) {
369
- return true;
370
- }
371
-
372
- // Check user allowlist
373
- if (this.allowedUsers?.length && userId) {
374
- if (this.allowedUsers.includes(userId)) return true;
375
- }
376
-
377
- // Check chat allowlist
378
- if (this.allowedChats?.length && chatId) {
379
- if (this.allowedChats.includes(chatId)) return true;
380
- }
381
-
382
- return false;
142
+ async sendMessage(chatId: number, text: string) {
143
+ await this.protocolAdapter.sendMessage(chatId, text);
383
144
  }
384
145
 
385
146
  /**
386
- * Extract chat ID from MessageRef
147
+ * Start typing indicator
387
148
  */
388
- private extractChatId(replyTo: { messageId: string; channelId: ChannelId }): number | null {
389
- // Chat ID is stored in channelId.accountId for Telegram
390
- const accountId = replyTo.channelId.accountId;
391
- // Try to parse as number, fallback to null
392
- const parsed = parseInt(accountId, 10);
393
- return isNaN(parsed) ? null : parsed;
149
+ startTypingIndicator(chatId: number) {
150
+ this.protocolAdapter.startTypingIndicator(chatId);
394
151
  }
395
152
 
396
153
  /**
397
- * Send response, handling chunking for long messages
154
+ * Stop typing indicator
398
155
  */
399
- private async sendResponse(chatId: number, response: string): Promise<void> {
400
- const maxLength = 4096; // Telegram message limit
401
-
402
- if (response.length > maxLength) {
403
- const chunks = response.match(/[\s\S]{1,4000}/g) || [];
404
- for (const chunk of chunks) {
405
- await this.bot.sendMessage(chatId, chunk, { parse_mode: "Markdown" });
406
- }
407
- } else {
408
- await this.bot.sendMessage(chatId, response, { parse_mode: "Markdown" });
409
- }
156
+ stopTypingIndicator(chatId: number) {
157
+ this.protocolAdapter.stopTypingIndicator(chatId);
410
158
  }
411
159
 
412
160
  /**
413
- * Send test message
161
+ * Check if user/chat is allowed
414
162
  */
415
- async sendTestMessage(chatId: number): Promise<void> {
416
- await this.bot.sendMessage(
417
- chatId,
418
- "✅ GLM Daemon Telegram Bot is NOW ONLINE!\n\n" +
419
- "🎉 The bot is working correctly!\n\n" +
420
- "Commands:\n" +
421
- "/start - Show welcome message\n" +
422
- 'Any message - Chat or task\n' +
423
- '"status" - Check daemon status\n\n' +
424
- "I'll route your messages appropriately!"
425
- );
426
- console.log(`[Telegram] Test message sent to chat ${chatId}`);
163
+ isAllowed(userId?: number, chatId?: number): boolean {
164
+ return this.protocolAdapter.isAllowed(userId, chatId);
427
165
  }
428
166
  }
429
167