clawgram 2.19.1 → 2.19.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/dist/channel.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createChannelPlugin = void 0;
6
+ exports.createChannelPlugin = exports.CORE_ACTION_SYNONYMS = void 0;
7
7
  const core_1 = require("openclaw/plugin-sdk/core");
8
8
  const node_os_1 = __importDefault(require("node:os"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
@@ -177,6 +177,30 @@ function readAccountManageChats(account) {
177
177
  const entries = Array.isArray(raw) ? raw : [raw];
178
178
  return entries.map((entry) => String(entry).trim()).filter(Boolean);
179
179
  }
180
+ /**
181
+ * Core's own name for a clawgram action, and the only thing that makes the
182
+ * action reachable from the agent's `message` tool.
183
+ *
184
+ * Core keys its target policy by `CHANNEL_MESSAGE_ACTION_NAMES`, and an action
185
+ * outside that vocabulary is simultaneously "requires a target" and "does not
186
+ * accept a target" — there is no call that satisfies both. Declaring `chatId`
187
+ * through `messageActionTargetAliases` looks like the fix and is not: core
188
+ * resolves the channel with `getBootstrapChannelPlugin`, which only knows
189
+ * bundled channels, so a plugin channel's declaration is never read. Measured
190
+ * on 2026-08-30 — `thread-list` reached `handleAction` and `topics` did not,
191
+ * from the same caller, on the same chat.
192
+ *
193
+ * Every name on the right maps to core target mode `"none"` except
194
+ * `channel-info`, which is `"to"` and therefore arrives with the chat in
195
+ * `params.to` — a spelling all of these parsers already accept.
196
+ */
197
+ exports.CORE_ACTION_SYNONYMS = {
198
+ "thread-list": "topics",
199
+ "channel-list": "dialogs",
200
+ "channel-info": "chatInfo",
201
+ "member-info": "participants",
202
+ "download-file": "fetch-media",
203
+ };
180
204
  /** Canonical management action for every accepted spelling. */
181
205
  const MANAGE_ACTION_ALIASES = {
182
206
  createGroup: "createGroup",
@@ -405,11 +429,12 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
405
429
  "Explicit targets may be @username, numeric Telegram user id, phone/contact resolvable by Telegram, group chat ids, or clawgram:<target>.",
406
430
  "For Telegram forum topics, send to the group chat id and pass the topic id separately as `threadId`.",
407
431
  "Use the `react` action to acknowledge a message with an emoji instead of sending a reply; pass an empty `emoji` (or `remove: true`) to take the reaction back.",
408
- "Use the `chatInfo` action to learn what a chat is — title, type, member count, description, pinned message — instead of guessing from its id.",
409
- "Use the `topics` action to list a forum's topics by name (optional `query` narrows by title); that is where a `threadId` comes from when someone names a topic instead of quoting a message in it.",
432
+ "Use the `channel-info` action (clawgram also answers to `chatInfo`) to learn what a chat is — title, type, member count, description, pinned message — instead of guessing from its id.",
433
+ "Use the `thread-list` action to list a forum's topics by name (optional `query` narrows by title); that is where a `threadId` comes from when someone names a topic instead of quoting a message in it. Name the chat with `chatId` and do not pass `target` — core refuses it for this action. `topics` is the same call under a name core does not know, and is only reachable through the gateway RPC.",
410
434
  "Pass that `threadId` to `read` as well: without it a forum read returns every topic interleaved rather than the one that was asked about.",
411
435
  "Use the `download-file` action (or its alias `fetch-media`) to fetch the attachment on a message `read` reported. Name the chat with `chatId` and the message with `messageId`; do not pass `target` — core refuses it for this action: `mode: \"read\"` returns a description of an image or a transcript of a voice note, `\"file\"` returns a path to reuse, `\"both\"` (default) returns both. `read` only says an attachment exists; this is what brings it.",
412
- "Use the `dialogs` action to find out which group chats this account is actually in — including ones nobody has configured yet. It reports id, title and type only, never direct chats, and only when the account enables `discoverChats`.",
436
+ "Use the `channel-list` action (clawgram also answers to `dialogs`) to find out which group chats this account is actually in — including ones nobody has configured yet. It reports id, title and type only, never direct chats, and only when the account enables `discoverChats`.",
437
+ "Use `member-info` with a `chatId` to list who is in a chat; `joins` has no name in core's vocabulary and is reachable only through the gateway RPC.",
413
438
  "Use `createGroup` (title, optional about, optional users) to create a new Telegram supergroup; `addMembers`/`removeMember` change who is in a managed chat, `promoteAdmin`/`demoteAdmin` grant or revoke admin rights, `transferOwnership` hands the chat over, `inviteLink` issues an invite link for people Telegram refused to add directly.",
414
439
  ],
415
440
  messageToolCapabilities: () => [
@@ -1389,6 +1414,9 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1389
1414
  // file stays on disk. That is exactly what happened on 2026-08-07.
1390
1415
  actions: [
1391
1416
  "send", "read", "participants", "joins", "react", "chatInfo", "topics", "dialogs", "upload-file",
1417
+ // Core's own names for four of the above. Without them the
1418
+ // action is advertised and unreachable — see CORE_ACTION_SYNONYMS.
1419
+ "thread-list", "channel-list", "channel-info", "member-info",
1392
1420
  // Reading an attachment that is already in a chat. `read` reports
1393
1421
  // that a photo exists; this is what turns it into something the
1394
1422
  // agent can look at or pass on.
@@ -1423,6 +1451,17 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1423
1451
  // reaches `handleAction`. The chat is named by `chatId` rather than
1424
1452
  // `target` because core reserves `target` for actions in its own
1425
1453
  // vocabulary and throws on it for everything else.
1454
+ //
1455
+ // This declaration alone does not rescue an action, and 2.19.1 read too
1456
+ // much into it. Core resolves the channel through
1457
+ // `getBootstrapChannelPlugin`, which only ever returns a *bundled*
1458
+ // channel; for a plugin channel the lookup misses and the declaration is
1459
+ // never consulted. Measured on the live server on 2026-08-30: `topics`
1460
+ // was refused for `target`, `chatId`, `groupId` and the prefixed form
1461
+ // alike even with `chatId` declared here. What actually carried
1462
+ // `fetch-media` through was its second name, `download-file` — see
1463
+ // CORE_ACTION_SYNONYMS. This stays because it costs nothing and is
1464
+ // correct the day core consults plugin channels too.
1426
1465
  messageActionTargetAliases: {
1427
1466
  "fetch-media": { aliases: ["chatId"] },
1428
1467
  "download-file": { aliases: ["chatId"] },
@@ -1639,7 +1678,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1639
1678
  // Membership is a read, so the same `readChats` scope that gates history
1640
1679
  // gates it too: this cannot become a way to enumerate chats the account
1641
1680
  // was never allowed to read.
1642
- if (action === "participants" || action === "members") {
1681
+ if (action === "participants" || action === "members" || action === "member-info") {
1643
1682
  const participantsParams = (0, history_1.parseListParticipantsParams)(params);
1644
1683
  const participantsAccountId = resolveRuntimeAccountId(cfg, accountId);
1645
1684
  if (!participantsAccountId) {
@@ -1679,7 +1718,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1679
1718
  // id could only be lifted off an inbound message — so a topic nobody had
1680
1719
  // written in yet was unreachable, and one named in words was unfindable.
1681
1720
  // Titles say what a chat is working on, so the read scope gates them.
1682
- if (action === "topics" || action === "forumTopics") {
1721
+ if (action === "topics" || action === "forumTopics" || action === "thread-list") {
1683
1722
  const topicsParams = (0, topics_1.parseTopicsParams)(params);
1684
1723
  const topicsAccountId = resolveRuntimeAccountId(cfg, accountId);
1685
1724
  if (!topicsAccountId) {
@@ -1716,7 +1755,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1716
1755
  // Which chats this account is in. Not gated by `readChats` — the whole
1717
1756
  // point is to find chats that are not in it yet — so it has a gate of
1718
1757
  // its own, is metadata only, and never reports direct chats.
1719
- if (action === "dialogs" || action === "chats") {
1758
+ if (action === "dialogs" || action === "chats" || action === "channel-list") {
1720
1759
  const dialogsParams = (0, dialogs_1.parseDialogsParams)(params);
1721
1760
  const dialogsAccountId = resolveRuntimeAccountId(cfg, accountId);
1722
1761
  if (!dialogsAccountId) {
@@ -1776,7 +1815,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1776
1815
  // Describing a chat is a read, so the same `readChats` scope that gates
1777
1816
  // history gates it too — this must not become a way to learn the title
1778
1817
  // and size of a chat the account was never allowed to read.
1779
- if (action === "chatInfo" || action === "getChatInfo"
1818
+ if (action === "chatInfo" || action === "getChatInfo" || action === "channel-info"
1780
1819
  || action === "chatMetadata" || action === "getChatMetadata") {
1781
1820
  const chatInfoParams = (0, chat_info_1.parseChatInfoParams)(params, toolContext);
1782
1821
  const chatInfoAccountId = resolveRuntimeAccountId(cfg, accountId);
@@ -2,7 +2,7 @@
2
2
  "id": "clawgram",
3
3
  "name": "Clawgram",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
- "version": "2.19.1",
5
+ "version": "2.19.3",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawgram",
3
- "version": "2.19.1",
3
+ "version": "2.19.3",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {