@integrity-labs/agt-cli 0.27.79 → 0.27.81

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.
@@ -14345,6 +14345,15 @@ function decideAckReaction(i) {
14345
14345
  function decideRecoveryHeal(i) {
14346
14346
  return i.wasUndeliverable && i.hasTarget ? "heal" : "none";
14347
14347
  }
14348
+ var SLACK_UNDELIVERABLE_REACTION = "hourglass_flowing_sand";
14349
+ var SLACK_UNDELIVERABLE_REACTION_LEGACY = "x";
14350
+ var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
14351
+ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
14352
+ return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
14353
+ }
14354
+ function undeliverableNoticeText() {
14355
+ return "\u23F3 I can't get to this right now \u2014 I'll follow up as soon as I'm back, but if it's urgent please resend in a few minutes.";
14356
+ }
14348
14357
  function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
14349
14358
  if (!dir) return null;
14350
14359
  let names;
@@ -15859,12 +15868,16 @@ function healSlackUndeliverable(channel, messageTs) {
15859
15868
  "Content-Type": "application/json",
15860
15869
  Authorization: `Bearer ${BOT_TOKEN}`
15861
15870
  };
15862
- fetch("https://slack.com/api/reactions.remove", {
15871
+ const remove = (name) => fetch("https://slack.com/api/reactions.remove", {
15863
15872
  method: "POST",
15864
15873
  headers,
15865
- body: JSON.stringify({ channel, timestamp: messageTs, name: "x" })
15874
+ body: JSON.stringify({ channel, timestamp: messageTs, name })
15866
15875
  }).catch(() => {
15867
- }).finally(() => {
15876
+ });
15877
+ void Promise.allSettled([
15878
+ remove(SLACK_UNDELIVERABLE_REACTION),
15879
+ remove(SLACK_UNDELIVERABLE_REACTION_LEGACY)
15880
+ ]).finally(() => {
15868
15881
  fetch("https://slack.com/api/reactions.add", {
15869
15882
  method: "POST",
15870
15883
  headers,
@@ -15873,6 +15886,32 @@ function healSlackUndeliverable(channel, messageTs) {
15873
15886
  });
15874
15887
  });
15875
15888
  }
15889
+ var lastSlackUndeliverableNoticeAt = /* @__PURE__ */ new Map();
15890
+ function postUndeliverableNotice(channel, threadTs, isThreadReply) {
15891
+ if (!BOT_TOKEN || !channel) return;
15892
+ const now = Date.now();
15893
+ const conversationKey = isThreadReply && threadTs ? `${channel}:${threadTs}` : channel;
15894
+ if (!shouldPostUndeliverableNotice(lastSlackUndeliverableNoticeAt.get(conversationKey), now)) {
15895
+ return;
15896
+ }
15897
+ lastSlackUndeliverableNoticeAt.set(conversationKey, now);
15898
+ fetch("https://slack.com/api/chat.postMessage", {
15899
+ method: "POST",
15900
+ headers: {
15901
+ "Content-Type": "application/json",
15902
+ Authorization: `Bearer ${BOT_TOKEN}`
15903
+ },
15904
+ body: JSON.stringify({
15905
+ channel,
15906
+ text: undeliverableNoticeText(),
15907
+ ...threadTs ? { thread_ts: threadTs } : {}
15908
+ })
15909
+ }).catch(() => {
15910
+ });
15911
+ }
15912
+ function __resetSlackUndeliverableNoticeThrottle() {
15913
+ lastSlackUndeliverableNoticeAt.clear();
15914
+ }
15876
15915
  function clearSlackMarkerFileWithHeal(fullPath) {
15877
15916
  let marker = null;
15878
15917
  try {
@@ -16725,7 +16764,7 @@ var mcp = new Server(
16725
16764
  "Inbound attachments: <channel> `files` is a JSON-serialised array \u2014 JSON.parse it. If an entry has `path`, the image is already downloaded \u2014 Read it directly, do NOT call slack.download_attachment. Use that tool only for entries with `file_id` but NO `path` (PDF, docx, csv): pass file_id + channel verbatim, then Read the returned path. Single-image messages also get a top-level `image_path`. Don't surface internal file-handling errors that don't affect the answer.",
16726
16765
  "Address users by user_name, never by raw user ID. In multi-participant threads the CURRENT speaker is the one on the latest <channel> tag.",
16727
16766
  'Mentioned in a channel \u2192 respond in that thread. DM \u2192 respond directly. auto_followed="true" \u2192 only reply if useful, OR if your own bot user is @-mentioned (counts even in auto_followed).',
16728
- "Reaction taxonomy (use slack.react sparingly \u2014 prefer a reply): \u{1F440} = ack (already auto-added on inbound, do not duplicate); \u2705 = success. NEVER react to signal failure of YOUR work \u2014 users can't tell why something failed from an emoji. On failure, slack.reply with one sentence explaining what went wrong (no stack traces, no secrets). (The \u274C you may see on an inbound is applied by the system, not you \u2014 it marks a message that arrived while the agent couldn't reply; never add \u274C yourself.)",
16767
+ "Reaction taxonomy (use slack.react sparingly \u2014 prefer a reply): \u{1F440} = ack (already auto-added on inbound, do not duplicate); \u2705 = success. NEVER react to signal failure of YOUR work \u2014 users can't tell why something failed from an emoji. On failure, slack.reply with one sentence explaining what went wrong (no stack traces, no secrets). (The \u23F3 you may see on an inbound is applied by the system, not you \u2014 it marks a message that arrived while the agent couldn't reply; never add \u23F3 or \u274C yourself.)",
16729
16768
  `When a thread message is NOT addressed to you (different @-mention, side conversation, auto_followed catch-up): SILENTLY SKIP \u2014 no reaction, no reply, no "this wasn't for me" message.`,
16730
16769
  "To deliver a file: save under your project dir, call slack.upload_file with path + channel + thread_ts."
16731
16770
  ].join(" ")
@@ -16765,7 +16804,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
16765
16804
  },
16766
16805
  {
16767
16806
  name: "slack.react",
16768
- description: "Add an emoji reaction to a Slack message. Use sparingly \u2014 prefer a text reply. Reaction taxonomy: \u2705 = action completed successfully. NEVER react to signal failure of your work \u2014 users can't tell why it failed from a reaction. On failure, call slack.reply with one sentence explaining what went wrong instead. \u{1F440} (eyes) is already auto-applied on inbound; do not duplicate. \u274C (:x:) is reserved for the system to mark an inbound that arrived while the agent couldn't reply \u2014 never add \u274C yourself.",
16807
+ description: "Add an emoji reaction to a Slack message. Use sparingly \u2014 prefer a text reply. Reaction taxonomy: \u2705 = action completed successfully. NEVER react to signal failure of your work \u2014 users can't tell why it failed from a reaction. On failure, call slack.reply with one sentence explaining what went wrong instead. \u{1F440} (eyes) is already auto-applied on inbound; do not duplicate. \u23F3 (:hourglass_flowing_sand:) is reserved for the system to mark an inbound that arrived while the agent couldn't reply \u2014 never add \u23F3 or \u274C yourself.",
16769
16808
  inputSchema: {
16770
16809
  type: "object",
16771
16810
  properties: {
@@ -18281,7 +18320,7 @@ async function connectSocketMode() {
18281
18320
  paneLogFreshAgeMs
18282
18321
  });
18283
18322
  if (ackDecision !== "none") {
18284
- const reactionName = ackDecision === "undeliverable" ? "x" : "eyes";
18323
+ const reactionName = ackDecision === "undeliverable" ? SLACK_UNDELIVERABLE_REACTION : "eyes";
18285
18324
  fetch("https://slack.com/api/reactions.add", {
18286
18325
  method: "POST",
18287
18326
  headers: {
@@ -18291,6 +18330,9 @@ async function connectSocketMode() {
18291
18330
  body: JSON.stringify({ channel, timestamp: ts, name: reactionName })
18292
18331
  }).catch(() => {
18293
18332
  });
18333
+ if (ackDecision === "undeliverable" && channel && shouldEngage) {
18334
+ postUndeliverableNotice(channel, threadTs, isThreadReply);
18335
+ }
18294
18336
  }
18295
18337
  if (channel && ts && shouldEngage) {
18296
18338
  trackPendingMessage(channel, threadTs, ts, ackDecision === "undeliverable");
@@ -18460,3 +18502,6 @@ if (THREAD_STORE_PATH) {
18460
18502
  );
18461
18503
  }
18462
18504
  connectSocketModeSafely();
18505
+ export {
18506
+ __resetSlackUndeliverableNoticeThrottle
18507
+ };
@@ -15526,6 +15526,13 @@ function decideAckReaction(i) {
15526
15526
  function decideRecoveryHeal(i) {
15527
15527
  return i.wasUndeliverable && i.hasTarget ? "heal" : "none";
15528
15528
  }
15529
+ var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
15530
+ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
15531
+ return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
15532
+ }
15533
+ function undeliverableNoticeText() {
15534
+ return "\u23F3 I can't get to this right now \u2014 I'll follow up as soon as I'm back, but if it's urgent please resend in a few minutes.";
15535
+ }
15529
15536
  function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
15530
15537
  if (!dir) return null;
15531
15538
  let names;
@@ -15806,20 +15813,19 @@ async function setMessageReaction(chatId, messageId, emoji2) {
15806
15813
  );
15807
15814
  }
15808
15815
  }
15809
- var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
15810
15816
  var lastUndeliverableNoticeAt = /* @__PURE__ */ new Map();
15811
15817
  async function notifyUndeliverable(chatId, messageId) {
15812
15818
  const key2 = String(chatId);
15813
15819
  const now = Date.now();
15814
- const last = lastUndeliverableNoticeAt.get(key2);
15815
- if (last != null && now - last < UNDELIVERABLE_NOTICE_THROTTLE_MS) return;
15820
+ if (!shouldPostUndeliverableNotice(lastUndeliverableNoticeAt.get(key2), now)) return;
15816
15821
  lastUndeliverableNoticeAt.set(key2, now);
15817
15822
  try {
15818
15823
  const resp = await telegramApiCall(
15819
15824
  "sendMessage",
15820
15825
  {
15821
15826
  chat_id: chatId,
15822
- text: "\u26A0\uFE0F I can't respond right now \u2014 I'll follow up once I'm back.",
15827
+ // ENG-6025: shared copy with the Slack undeliverable notice.
15828
+ text: undeliverableNoticeText(),
15823
15829
  reply_to_message_id: Number(messageId),
15824
15830
  allow_sending_without_reply: true
15825
15831
  },
@@ -15845,8 +15851,7 @@ var lastBackOnlineNoticeAt = /* @__PURE__ */ new Map();
15845
15851
  function notifyBackOnline(chatId) {
15846
15852
  const key2 = String(chatId);
15847
15853
  const now = Date.now();
15848
- const last = lastBackOnlineNoticeAt.get(key2);
15849
- if (last != null && now - last < UNDELIVERABLE_NOTICE_THROTTLE_MS) return;
15854
+ if (!shouldPostUndeliverableNotice(lastBackOnlineNoticeAt.get(key2), now)) return;
15850
15855
  lastBackOnlineNoticeAt.set(key2, now);
15851
15856
  void (async () => {
15852
15857
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.27.79",
3
+ "version": "0.27.81",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {