@integrity-labs/agt-cli 0.12.8 → 0.12.9

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/bin/agt.js CHANGED
@@ -3717,7 +3717,7 @@ import { execFileSync, execSync } from "child_process";
3717
3717
  import { existsSync as existsSync5, realpathSync } from "fs";
3718
3718
  import chalk20 from "chalk";
3719
3719
  import ora15 from "ora";
3720
- var cliVersion = true ? "0.12.8" : "dev";
3720
+ var cliVersion = true ? "0.12.9" : "dev";
3721
3721
  async function fetchLatestVersion() {
3722
3722
  const host2 = getHost();
3723
3723
  if (!host2) return null;
@@ -4166,7 +4166,7 @@ function handleError(err) {
4166
4166
  }
4167
4167
 
4168
4168
  // src/bin/agt.ts
4169
- var cliVersion2 = true ? "0.12.8" : "dev";
4169
+ var cliVersion2 = true ? "0.12.9" : "dev";
4170
4170
  var program = new Command();
4171
4171
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
4172
4172
  program.hook("preAction", (thisCommand) => {
package/mcp/index.js CHANGED
@@ -21150,13 +21150,10 @@ server.tool(
21150
21150
  }
21151
21151
  ]
21152
21152
  });
21153
+ const first = data.added_items?.[0];
21154
+ const text = !data.ok ? "Failed to add item." : first ? `Added "${first.title}" to board (status: ${params.status ?? "today"}, id: ${first.id}). Board: ${first.url}` : `Added "${params.title}" to board (status: ${params.status ?? "today"}).`;
21153
21155
  return {
21154
- content: [
21155
- {
21156
- type: "text",
21157
- text: data.ok ? `Added "${params.title}" to board (status: ${params.status ?? "today"}).` : "Failed to add item."
21158
- }
21159
- ]
21156
+ content: [{ type: "text", text }]
21160
21157
  };
21161
21158
  }
21162
21159
  );
@@ -13863,6 +13863,10 @@ var StdioServerTransport = class {
13863
13863
 
13864
13864
  // src/telegram-channel.ts
13865
13865
  import https from "https";
13866
+ import { createHash } from "crypto";
13867
+ function redactId(id) {
13868
+ return createHash("sha256").update(String(id)).digest("hex").slice(0, 8);
13869
+ }
13866
13870
  var BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
13867
13871
  var AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME ?? "unknown";
13868
13872
  var ALLOWED_CHATS = new Set(
@@ -13913,6 +13917,67 @@ function telegramApiCall(method, body, timeoutMs) {
13913
13917
  req.end();
13914
13918
  });
13915
13919
  }
13920
+ var ACK_EMOJI = "\u{1F440}";
13921
+ var TIMEOUT_EMOJI = "\u{1F494}";
13922
+ var RESPONSE_TIMEOUT_MS = 12e4;
13923
+ async function setMessageReaction(chatId, messageId, emoji2) {
13924
+ try {
13925
+ const resp = await telegramApiCall(
13926
+ "setMessageReaction",
13927
+ {
13928
+ chat_id: chatId,
13929
+ message_id: Number(messageId),
13930
+ reaction: emoji2 ? [{ type: "emoji", emoji: emoji2 }] : []
13931
+ },
13932
+ 1e4
13933
+ );
13934
+ if (!resp.ok) {
13935
+ process.stderr.write(
13936
+ `telegram-channel(${AGENT_CODE_NAME}): setMessageReaction failed (${emoji2 ?? "clear"}): ${resp.description ?? "unknown"}
13937
+ `
13938
+ );
13939
+ }
13940
+ } catch (err) {
13941
+ process.stderr.write(
13942
+ `telegram-channel(${AGENT_CODE_NAME}): setMessageReaction error: ${err.message}
13943
+ `
13944
+ );
13945
+ }
13946
+ }
13947
+ var pendingMessages = /* @__PURE__ */ new Map();
13948
+ function trackPendingMessage(chatId, messageId, chatType) {
13949
+ const key = `${chatId}:${messageId}`;
13950
+ const existing = pendingMessages.get(key);
13951
+ if (existing) clearTimeout(existing.timer);
13952
+ const timer = setTimeout(() => {
13953
+ pendingMessages.delete(key);
13954
+ void setMessageReaction(chatId, messageId, TIMEOUT_EMOJI);
13955
+ process.stderr.write(
13956
+ `telegram-channel(${AGENT_CODE_NAME}): response timeout for message ${redactId(messageId)} in chat ${redactId(chatId)}
13957
+ `
13958
+ );
13959
+ }, RESPONSE_TIMEOUT_MS);
13960
+ timer.unref?.();
13961
+ pendingMessages.set(key, { timer, chatType });
13962
+ }
13963
+ function clearPendingMessage(chatId, messageId) {
13964
+ if (messageId) {
13965
+ const key = `${chatId}:${messageId}`;
13966
+ const entry = pendingMessages.get(key);
13967
+ if (entry) {
13968
+ clearTimeout(entry.timer);
13969
+ pendingMessages.delete(key);
13970
+ }
13971
+ return;
13972
+ }
13973
+ const prefix = `${chatId}:`;
13974
+ for (const [key, entry] of pendingMessages) {
13975
+ if (!key.startsWith(prefix)) continue;
13976
+ if (entry.chatType !== "private") continue;
13977
+ clearTimeout(entry.timer);
13978
+ pendingMessages.delete(key);
13979
+ }
13980
+ }
13916
13981
  var mcp = new Server(
13917
13982
  { name: "telegram", version: "0.1.0" },
13918
13983
  {
@@ -13922,10 +13987,15 @@ var mcp = new Server(
13922
13987
  },
13923
13988
  instructions: [
13924
13989
  'Messages from Telegram arrive as <channel source="telegram" chat_id="..." user="..." user_name="..." message_id="...">.',
13925
- "Reply using the telegram.reply tool, passing chat_id from the tag.",
13990
+ "The user reads Telegram, not this session. The ONLY way to reach them is the telegram.reply tool \u2014 anything you put in session output is invisible to them.",
13991
+ `EVERY response to a Telegram message goes through telegram.reply, passing the chat_id from the tag. This includes clarifying questions, error messages, partial answers, "I don't understand", and status updates \u2014 no exceptions.`,
13992
+ 'For work that will take more than ~30 seconds, follow the standard kanban flow from CLAUDE.md: call kanban.add to track the task, then IMMEDIATELY reply via telegram.reply with "On it \u2014 tracking here: <kanban URL>" including the created task title, move the task to in_progress, do the work, and reply via telegram.reply with the result.',
13993
+ "Simple lookups, one-line answers, and no-action acknowledgements do NOT need a kanban task \u2014 but still reply via telegram.reply every time.",
13994
+ "When a user mentions a time without a timezone, interpret it in your Timezone from the Identity section of CLAUDE.md. Do NOT ask the user which timezone they meant \u2014 resolve it yourself against your own identity.",
13926
13995
  "Address users by their user_name when possible; user is their numeric Telegram ID.",
13927
- "To reply as a thread quote (reference a specific earlier message), pass reply_to_message_id \u2014 otherwise omit it for a normal reply.",
13928
- "Telegram DMs are 1:1 \u2014 always reply unless the message is obviously not for you."
13996
+ "To quote-reply to a specific earlier message, pass its message_id as reply_to_message_id \u2014 otherwise omit it for a normal reply.",
13997
+ "Telegram DMs are 1:1 \u2014 always reply to every inbound message unless it is obviously not addressed to you.",
13998
+ 'Every inbound message gets an automatic \u{1F440} reaction as a "seen, working on it" ack. Use the telegram.react tool to add your own reactions (e.g. \u270D while clarifying, \u{1FAE1} on task complete). Only free-tier emoji are available to bots \u2014 stick to: \u{1F44D} \u{1F44E} \u2764 \u{1F525} \u{1F389} \u{1F914} \u{1F92F} \u{1F64F} \u{1F44C} \u{1F440} \u{1F4AF} \u270D \u{1FAE1} \u{1F192} \u{1F973} and similar common reactions. Premium-only emoji fail silently.'
13929
13999
  ].join(" ")
13930
14000
  }
13931
14001
  );
@@ -13964,6 +14034,28 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
13964
14034
  },
13965
14035
  required: ["chat_id", "text"]
13966
14036
  }
14037
+ },
14038
+ {
14039
+ name: "telegram.react",
14040
+ description: "Add an emoji reaction to a Telegram message. Only free-tier emoji reactions are available to bots (Premium-only emoji fail silently). Pass an empty string or omit emoji to clear the bot's reaction on that message.",
14041
+ inputSchema: {
14042
+ type: "object",
14043
+ properties: {
14044
+ chat_id: {
14045
+ type: "string",
14046
+ description: "Telegram chat ID (from the chat_id attribute in the <channel> tag)"
14047
+ },
14048
+ message_id: {
14049
+ type: "string",
14050
+ description: "Telegram message_id to react to (from the message_id attribute)"
14051
+ },
14052
+ emoji: {
14053
+ type: "string",
14054
+ description: `A single emoji from Telegram's free-tier reaction set (e.g. "\u{1F440}", "\u{1FAE1}", "\u270D", "\u{1F525}"). Pass "" to clear.`
14055
+ }
14056
+ },
14057
+ required: ["chat_id", "message_id"]
14058
+ }
13967
14059
  }
13968
14060
  ]
13969
14061
  }));
@@ -13987,6 +14079,9 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
13987
14079
  isError: true
13988
14080
  };
13989
14081
  }
14082
+ if (name === "telegram.reply") {
14083
+ clearPendingMessage(chat_id, reply_to_message_id);
14084
+ }
13990
14085
  return { content: [{ type: "text", text: "sent" }] };
13991
14086
  } catch (err) {
13992
14087
  return {
@@ -13995,6 +14090,38 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
13995
14090
  };
13996
14091
  }
13997
14092
  }
14093
+ if (name === "telegram.react") {
14094
+ const { chat_id, message_id, emoji: emoji2 } = args;
14095
+ if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chat_id)) {
14096
+ return {
14097
+ content: [{ type: "text", text: `Chat ${chat_id} is not in TELEGRAM_ALLOWED_CHATS` }],
14098
+ isError: true
14099
+ };
14100
+ }
14101
+ try {
14102
+ const data = await telegramApiCall(
14103
+ "setMessageReaction",
14104
+ {
14105
+ chat_id,
14106
+ message_id: Number(message_id),
14107
+ reaction: emoji2 ? [{ type: "emoji", emoji: emoji2 }] : []
14108
+ },
14109
+ 1e4
14110
+ );
14111
+ if (!data.ok) {
14112
+ return {
14113
+ content: [{ type: "text", text: `Telegram error: ${data.description ?? "unknown"}` }],
14114
+ isError: true
14115
+ };
14116
+ }
14117
+ return { content: [{ type: "text", text: emoji2 ? "reacted" : "cleared" }] };
14118
+ } catch (err) {
14119
+ return {
14120
+ content: [{ type: "text", text: `Failed: ${err.message}` }],
14121
+ isError: true
14122
+ };
14123
+ }
14124
+ }
13998
14125
  throw new Error(`Unknown tool: ${name}`);
13999
14126
  });
14000
14127
  await mcp.connect(new StdioServerTransport());
@@ -14031,6 +14158,9 @@ async function pollLoop() {
14031
14158
  if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chatId)) continue;
14032
14159
  const userId = msg.from?.id != null ? String(msg.from.id) : "unknown";
14033
14160
  const userName = msg.from?.username || [msg.from?.first_name, msg.from?.last_name].filter(Boolean).join(" ").trim() || userId;
14161
+ const messageId = String(msg.message_id);
14162
+ void setMessageReaction(chatId, messageId, ACK_EMOJI);
14163
+ trackPendingMessage(chatId, messageId, msg.chat.type);
14034
14164
  await mcp.notification({
14035
14165
  method: "notifications/claude/channel",
14036
14166
  params: {
@@ -14039,7 +14169,7 @@ async function pollLoop() {
14039
14169
  source: "telegram",
14040
14170
  chat_id: chatId,
14041
14171
  chat_type: msg.chat.type,
14042
- message_id: String(msg.message_id),
14172
+ message_id: messageId,
14043
14173
  user: userId,
14044
14174
  user_name: userName,
14045
14175
  ts: String(msg.date)
@@ -14063,6 +14193,8 @@ function sleep(ms) {
14063
14193
  function shutdown(reason) {
14064
14194
  if (isShuttingDown) return;
14065
14195
  isShuttingDown = true;
14196
+ for (const { timer } of pendingMessages.values()) clearTimeout(timer);
14197
+ pendingMessages.clear();
14066
14198
  process.stderr.write(
14067
14199
  `telegram-channel(${AGENT_CODE_NAME}): ${reason} \u2014 exiting
14068
14200
  `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.12.8",
3
+ "version": "0.12.9",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {