talon-agent 5.5.0 → 5.5.1

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": "5.5.0",
3
+ "version": "5.5.1",
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",
@@ -108,7 +108,7 @@
108
108
  "@kilocode/sdk": "^7.2.22",
109
109
  "@modelcontextprotocol/sdk": "^1.29.0",
110
110
  "@openai/agents": "^0.18.0",
111
- "@openai/codex-sdk": "^0.154.0",
111
+ "@openai/codex-sdk": "^0.155.0",
112
112
  "@opencode-ai/sdk": "^1.17.4",
113
113
  "@playwright/mcp": "0.0.56",
114
114
  "@types/cross-spawn": "^6.0.6",
@@ -151,6 +151,47 @@ export async function isAccessAllowed(
151
151
  return false;
152
152
  }
153
153
 
154
+ /**
155
+ * True when the update is a /command or an inline-button press.
156
+ *
157
+ * Both are handled by `bot.command` / `bot.callbackQuery` handlers that
158
+ * run before — and independently of — the message handlers, so the
159
+ * message-path access check never sees them.
160
+ */
161
+ export function isCommandOrCallback(ctx: Context): boolean {
162
+ if (ctx.callbackQuery) return true;
163
+ const msg = ctx.message ?? ctx.editedMessage;
164
+ if (!msg) return false;
165
+ const entities = msg.entities ?? msg.caption_entities ?? [];
166
+ return entities.some((e) => e.type === "bot_command" && e.offset === 0);
167
+ }
168
+
169
+ /**
170
+ * Gate every /command and button press behind the same access check as
171
+ * ordinary messages (denylist, DM whitelist, group admin membership).
172
+ *
173
+ * Without this, a sender outside `allowedUsers` who DMs the bot is refused
174
+ * for plain text but can still run /model, /settings, /status, /mesh,
175
+ * /plugins, /memory and press any inline button, because those handlers
176
+ * are registered with grammy directly and never pass through
177
+ * `isAccessAllowed`. Must be installed BEFORE registerCommands and
178
+ * registerCallbacks so it sees the update first.
179
+ */
180
+ export function registerCommandAccessGate(bot: Bot): void {
181
+ bot.use(async (ctx, next) => {
182
+ if (!isCommandOrCallback(ctx)) return next();
183
+ if (await isAccessAllowed(ctx, bot)) return next();
184
+ if (ctx.callbackQuery) {
185
+ try {
186
+ await ctx.answerCallbackQuery();
187
+ } catch {
188
+ /* stale query — ignore */
189
+ }
190
+ }
191
+ // Dropped: not passed to any command or callback handler.
192
+ });
193
+ }
194
+
154
195
  /**
155
196
  * Maximum length of an unauthorized message body to retain in logs.
156
197
  * Keeps abusive payloads (large pastes, attachment captions etc.) bounded
@@ -17,6 +17,7 @@ export {
17
17
  setAccessControl,
18
18
  shouldHandleInGroup,
19
19
  extractUnauthorizedPreview,
20
+ registerCommandAccessGate,
20
21
  } from "./access.js";
21
22
  export {
22
23
  getSenderName,
@@ -21,7 +21,10 @@ import {
21
21
  setAdminUserId,
22
22
  telegramCommandMenu,
23
23
  } from "./commands/index.js";
24
- import { setAccessControl } from "./handlers/index.js";
24
+ import {
25
+ setAccessControl,
26
+ registerCommandAccessGate,
27
+ } from "./handlers/index.js";
25
28
  import { registerMiddleware } from "./middleware.js";
26
29
  import { confirmUpdates } from "./update-offset.js";
27
30
  import { registerCallbacks } from "./callbacks/index.js";
@@ -94,6 +97,9 @@ export function createTelegramFrontend(
94
97
  adminUserId: config.adminUserId,
95
98
  });
96
99
 
100
+ // Gate /commands and button presses behind the DM whitelist and
101
+ // group check BEFORE any command/callback handler is registered.
102
+ registerCommandAccessGate(bot);
97
103
  registerCommands(bot, config, gateway);
98
104
  registerMiddleware(bot, config);
99
105
  registerCallbacks(bot, config, gateway);