@integrity-labs/agt-cli 0.27.17 → 0.27.19

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.
@@ -15,7 +15,7 @@ import {
15
15
  provisionOrientHook,
16
16
  provisionStopHook,
17
17
  requireHost
18
- } from "../chunk-QQVNHJ76.js";
18
+ } from "../chunk-OUPCUHJZ.js";
19
19
  import {
20
20
  getProjectDir as getProjectDir2,
21
21
  getReadyTasks,
@@ -3166,7 +3166,7 @@ var cachedFrameworkVersion = null;
3166
3166
  var lastVersionCheckAt = 0;
3167
3167
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
3168
3168
  var lastResponsivenessProbeAt = 0;
3169
- var agtCliVersion = true ? "0.27.17" : "dev";
3169
+ var agtCliVersion = true ? "0.27.19" : "dev";
3170
3170
  function resolveBrewPath(execFileSync4) {
3171
3171
  try {
3172
3172
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -14114,6 +14114,14 @@ var mcp = new Server(
14114
14114
  tools: {}
14115
14115
  },
14116
14116
  instructions: [
14117
+ // ENG-5845: highest-priority guard first. Without this framing,
14118
+ // Opus 4.8 reads the softer "Reply using…" line as a suggestion and
14119
+ // emits plain text — the reply lives in the local Claude session
14120
+ // but never reaches the operator's session_id through the channel
14121
+ // API. Mirrors the slack-channel CRITICAL line shipped in
14122
+ // ENG-5830. Total instructions string is ~850 / 2048 chars so we
14123
+ // have room to be explicit.
14124
+ "CRITICAL: every response to a direct-chat <channel> tag MUST go through direct_chat.reply (passing the session_id verbatim). Text in your session WITHOUT a direct_chat.reply call never reaches the operator \u2014 the reply dies inside the agent process.",
14117
14125
  'Messages from the webapp Direct Chat arrive as <channel source="direct-chat" session_id="..." user="...">.',
14118
14126
  "Reply using the direct_chat.reply tool, passing the session_id from the tag.",
14119
14127
  "Always reply to every direct chat message \u2014 the user is waiting in the webapp.",
@@ -14265,6 +14265,9 @@ function decideAckReaction(i) {
14265
14265
  }
14266
14266
  return "ack";
14267
14267
  }
14268
+ function decideRecoveryHeal(i) {
14269
+ return i.wasUndeliverable && i.hasTarget ? "heal" : "none";
14270
+ }
14268
14271
  function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
14269
14272
  if (!dir) return null;
14270
14273
  let names;
@@ -15589,14 +15592,16 @@ function slackPendingInboundPath(channel, threadTs, messageTs) {
15589
15592
  if (!SLACK_PENDING_INBOUND_DIR) return null;
15590
15593
  return join4(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
15591
15594
  }
15592
- function writeSlackPendingInboundMarker(channel, threadTs, messageTs) {
15595
+ function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undeliverable = false) {
15593
15596
  const path = slackPendingInboundPath(channel, threadTs, messageTs);
15594
15597
  if (!path || !SLACK_PENDING_INBOUND_DIR) return;
15595
15598
  const marker = {
15596
15599
  channel,
15597
15600
  thread_ts: threadTs,
15598
15601
  message_ts: messageTs,
15599
- received_at: (/* @__PURE__ */ new Date()).toISOString()
15602
+ received_at: (/* @__PURE__ */ new Date()).toISOString(),
15603
+ // Only persist the flag when set — keeps healthy-path markers byte-identical.
15604
+ ...undeliverable ? { undeliverable: true } : {}
15600
15605
  };
15601
15606
  try {
15602
15607
  mkdirSync3(SLACK_PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
@@ -15608,6 +15613,43 @@ function writeSlackPendingInboundMarker(channel, threadTs, messageTs) {
15608
15613
  );
15609
15614
  }
15610
15615
  }
15616
+ function healSlackUndeliverable(channel, messageTs) {
15617
+ if (!BOT_TOKEN || !channel || !messageTs) return;
15618
+ const headers = {
15619
+ "Content-Type": "application/json",
15620
+ Authorization: `Bearer ${BOT_TOKEN}`
15621
+ };
15622
+ fetch("https://slack.com/api/reactions.remove", {
15623
+ method: "POST",
15624
+ headers,
15625
+ body: JSON.stringify({ channel, timestamp: messageTs, name: "x" })
15626
+ }).catch(() => {
15627
+ }).finally(() => {
15628
+ fetch("https://slack.com/api/reactions.add", {
15629
+ method: "POST",
15630
+ headers,
15631
+ body: JSON.stringify({ channel, timestamp: messageTs, name: "eyes" })
15632
+ }).catch(() => {
15633
+ });
15634
+ });
15635
+ }
15636
+ function clearSlackMarkerFileWithHeal(fullPath) {
15637
+ let marker = null;
15638
+ try {
15639
+ marker = JSON.parse(readFileSync4(fullPath, "utf-8"));
15640
+ } catch {
15641
+ }
15642
+ if (marker && decideRecoveryHeal({
15643
+ wasUndeliverable: marker.undeliverable === true,
15644
+ hasTarget: Boolean(marker.channel && marker.message_ts)
15645
+ }) === "heal") {
15646
+ healSlackUndeliverable(marker.channel, marker.message_ts);
15647
+ }
15648
+ try {
15649
+ if (existsSync2(fullPath)) unlinkSync2(fullPath);
15650
+ } catch {
15651
+ }
15652
+ }
15611
15653
  function clearAllSlackPendingMarkersForThread(channel, threadTs) {
15612
15654
  if (!SLACK_PENDING_INBOUND_DIR) return;
15613
15655
  const safeChan = channel.replace(/[^A-Za-z0-9_-]/g, "_");
@@ -15616,10 +15658,7 @@ function clearAllSlackPendingMarkersForThread(channel, threadTs) {
15616
15658
  try {
15617
15659
  for (const f of readdirSync2(SLACK_PENDING_INBOUND_DIR)) {
15618
15660
  if (!f.startsWith(prefix) || !f.endsWith(".json")) continue;
15619
- try {
15620
- unlinkSync2(join4(SLACK_PENDING_INBOUND_DIR, f));
15621
- } catch {
15622
- }
15661
+ clearSlackMarkerFileWithHeal(join4(SLACK_PENDING_INBOUND_DIR, f));
15623
15662
  }
15624
15663
  } catch {
15625
15664
  }
@@ -15800,8 +15839,8 @@ function startSlackRecoveryOutboxWatcher() {
15800
15839
  }
15801
15840
  startSlackRecoveryOutboxWatcher();
15802
15841
  var STALE_MARKER_MS = 24 * 60 * 60 * 1e3;
15803
- function trackPendingMessage(channel, threadTs, messageTs) {
15804
- writeSlackPendingInboundMarker(channel, threadTs, messageTs);
15842
+ function trackPendingMessage(channel, threadTs, messageTs, undeliverable = false) {
15843
+ writeSlackPendingInboundMarker(channel, threadTs, messageTs, undeliverable);
15805
15844
  }
15806
15845
  function sweepSlackStaleMarkersOnBoot() {
15807
15846
  if (!SLACK_PENDING_INBOUND_DIR) return;
@@ -15888,10 +15927,7 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
15888
15927
  for (const filename of filenames) {
15889
15928
  if (!filename.startsWith(channelPrefix)) continue;
15890
15929
  if (!filename.endsWith(messageSuffix)) continue;
15891
- try {
15892
- unlinkSync2(join4(SLACK_PENDING_INBOUND_DIR, filename));
15893
- } catch {
15894
- }
15930
+ clearSlackMarkerFileWithHeal(join4(SLACK_PENDING_INBOUND_DIR, filename));
15895
15931
  }
15896
15932
  }
15897
15933
  var RESTART_FLAGS_DIR = join4(homedir2(), ".augmented", "restart-flags");
@@ -17888,7 +17924,7 @@ async function connectSocketMode() {
17888
17924
  });
17889
17925
  }
17890
17926
  if (channel && ts && shouldEngage) {
17891
- trackPendingMessage(channel, threadTs, ts);
17927
+ trackPendingMessage(channel, threadTs, ts, ackDecision === "undeliverable");
17892
17928
  }
17893
17929
  const userName = await resolveUserName(user);
17894
17930
  const fileMeta = await buildInboundFileMeta(evt.files, AGENT_CODE_NAME, channel);
@@ -15517,6 +15517,9 @@ function decideAckReaction(i) {
15517
15517
  }
15518
15518
  return "ack";
15519
15519
  }
15520
+ function decideRecoveryHeal(i) {
15521
+ return i.wasUndeliverable && i.hasTarget ? "heal" : "none";
15522
+ }
15520
15523
  function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
15521
15524
  if (!dir) return null;
15522
15525
  let names;
@@ -15805,6 +15808,40 @@ async function notifyUndeliverable(chatId, messageId) {
15805
15808
  function __resetUndeliverableNoticeThrottle() {
15806
15809
  lastUndeliverableNoticeAt.clear();
15807
15810
  }
15811
+ var lastBackOnlineNoticeAt = /* @__PURE__ */ new Map();
15812
+ function notifyBackOnline(chatId) {
15813
+ const key2 = String(chatId);
15814
+ const now = Date.now();
15815
+ const last = lastBackOnlineNoticeAt.get(key2);
15816
+ if (last != null && now - last < UNDELIVERABLE_NOTICE_THROTTLE_MS) return;
15817
+ lastBackOnlineNoticeAt.set(key2, now);
15818
+ void (async () => {
15819
+ try {
15820
+ const resp = await telegramApiCall(
15821
+ "sendMessage",
15822
+ {
15823
+ chat_id: chatId,
15824
+ text: "\u2705 Back online \u2014 catching up on what I missed."
15825
+ },
15826
+ 1e4
15827
+ );
15828
+ if (!resp.ok) {
15829
+ process.stderr.write(
15830
+ `telegram-channel(${AGENT_CODE_NAME}): back-online notice failed (chat=${redactId(chatId)}): ${resp.description ?? "unknown"}
15831
+ `
15832
+ );
15833
+ }
15834
+ } catch (err) {
15835
+ process.stderr.write(
15836
+ `telegram-channel(${AGENT_CODE_NAME}): back-online notice error: ${err.message}
15837
+ `
15838
+ );
15839
+ }
15840
+ })();
15841
+ }
15842
+ function __resetBackOnlineNoticeThrottle() {
15843
+ lastBackOnlineNoticeAt.clear();
15844
+ }
15808
15845
  var RESTART_FLAGS_DIR = join4(homedir2(), ".augmented", "restart-flags");
15809
15846
  function buildTelegramHelpMessage(codeName) {
15810
15847
  return [
@@ -15972,14 +16009,16 @@ function pendingInboundPath(chatId, messageId) {
15972
16009
  if (!PENDING_INBOUND_DIR) return null;
15973
16010
  return join4(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
15974
16011
  }
15975
- function writePendingInboundMarker(chatId, messageId, chatType) {
16012
+ function writePendingInboundMarker(chatId, messageId, chatType, undeliverable = false) {
15976
16013
  const path = pendingInboundPath(chatId, messageId);
15977
16014
  if (!path || !PENDING_INBOUND_DIR) return;
15978
16015
  const marker = {
15979
16016
  chat_id: chatId,
15980
16017
  message_id: messageId,
15981
16018
  chat_type: chatType,
15982
- received_at: (/* @__PURE__ */ new Date()).toISOString()
16019
+ received_at: (/* @__PURE__ */ new Date()).toISOString(),
16020
+ // Only persist the flag when set — keeps healthy-path markers byte-identical.
16021
+ ...undeliverable ? { undeliverable: true } : {}
15983
16022
  };
15984
16023
  try {
15985
16024
  mkdirSync3(PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
@@ -15991,14 +16030,28 @@ function writePendingInboundMarker(chatId, messageId, chatType) {
15991
16030
  );
15992
16031
  }
15993
16032
  }
15994
- function clearPendingInboundMarker(chatId, messageId) {
15995
- const path = pendingInboundPath(chatId, messageId);
15996
- if (!path) return;
16033
+ function clearTelegramMarkerFileWithHeal(fullPath) {
16034
+ let marker = null;
15997
16035
  try {
15998
- if (existsSync2(path)) unlinkSync3(path);
16036
+ marker = JSON.parse(readFileSync3(fullPath, "utf-8"));
16037
+ } catch {
16038
+ }
16039
+ if (marker && decideRecoveryHeal({
16040
+ wasUndeliverable: marker.undeliverable === true,
16041
+ hasTarget: Boolean(marker.chat_id)
16042
+ }) === "heal") {
16043
+ notifyBackOnline(marker.chat_id);
16044
+ }
16045
+ try {
16046
+ if (existsSync2(fullPath)) unlinkSync3(fullPath);
15999
16047
  } catch {
16000
16048
  }
16001
16049
  }
16050
+ function clearPendingInboundMarker(chatId, messageId) {
16051
+ const path = pendingInboundPath(chatId, messageId);
16052
+ if (!path) return;
16053
+ clearTelegramMarkerFileWithHeal(path);
16054
+ }
16002
16055
  var MAX_RECOVERY_ATTEMPTS = 3;
16003
16056
  function nextRetryName(filename) {
16004
16057
  const match = filename.match(/^(.*?)(?:\.retry-(\d+))?\.json$/);
@@ -16166,8 +16219,8 @@ function startRecoveryOutboxWatcher() {
16166
16219
  }
16167
16220
  startRecoveryOutboxWatcher();
16168
16221
  var STALE_MARKER_MS = 24 * 60 * 60 * 1e3;
16169
- function trackPendingMessage(chatId, messageId, chatType) {
16170
- writePendingInboundMarker(chatId, messageId, chatType);
16222
+ function trackPendingMessage(chatId, messageId, chatType, undeliverable = false) {
16223
+ writePendingInboundMarker(chatId, messageId, chatType, undeliverable);
16171
16224
  }
16172
16225
  function sweepTelegramStaleMarkersOnBoot() {
16173
16226
  if (!PENDING_INBOUND_DIR) return;
@@ -16246,10 +16299,7 @@ function clearPendingMessage(chatId, messageId) {
16246
16299
  for (const filename of filenames) {
16247
16300
  if (!filename.startsWith(prefix)) continue;
16248
16301
  if (!filename.endsWith(".json")) continue;
16249
- try {
16250
- unlinkSync3(join4(PENDING_INBOUND_DIR, filename));
16251
- } catch {
16252
- }
16302
+ clearTelegramMarkerFileWithHeal(join4(PENDING_INBOUND_DIR, filename));
16253
16303
  }
16254
16304
  }
16255
16305
  function noteThreadActivity(chatId, messageId) {
@@ -17104,7 +17154,7 @@ async function pollLoop() {
17104
17154
  } else if (ackDecision === "undeliverable") {
17105
17155
  void notifyUndeliverable(chatId, messageId);
17106
17156
  }
17107
- trackPendingMessage(chatId, messageId, msg.chat.type);
17157
+ trackPendingMessage(chatId, messageId, msg.chat.type, ackDecision === "undeliverable");
17108
17158
  const fileMeta = [];
17109
17159
  for (const attachment of classifiedAttachments) {
17110
17160
  if (attachment.kind === "image") {
@@ -17231,5 +17281,6 @@ pollLoop().catch((err) => {
17231
17281
  process.exit(1);
17232
17282
  });
17233
17283
  export {
17284
+ __resetBackOnlineNoticeThrottle,
17234
17285
  __resetUndeliverableNoticeThrottle
17235
17286
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.27.17",
3
+ "version": "0.27.19",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {