@integrity-labs/agt-cli 0.27.122 → 0.27.124

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.
@@ -14335,6 +14335,29 @@ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDEL
14335
14335
  function undeliverableNoticeText() {
14336
14336
  return "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
14337
14337
  }
14338
+ var BUSY_ACK_THRESHOLD_MS = 9e4;
14339
+ var BUSY_ACK_NOTICE_THROTTLE_MS = 10 * 60 * 1e3;
14340
+ function channelBusyAckEnabled() {
14341
+ const v = process.env.AGT_CHANNEL_BUSY_ACK_ENABLED;
14342
+ return v === "1" || v === "true";
14343
+ }
14344
+ function channelBusyAckThresholdMs() {
14345
+ const raw = parseInt(process.env.AGT_CHANNEL_BUSY_ACK_THRESHOLD_MS ?? "", 10);
14346
+ return Number.isFinite(raw) && raw > 0 ? raw : BUSY_ACK_THRESHOLD_MS;
14347
+ }
14348
+ function decideBusyAck(i) {
14349
+ if (!i.hasTarget) return false;
14350
+ if (!i.stillPending) return false;
14351
+ if (!Number.isFinite(i.pendingAgeMs)) return false;
14352
+ const threshold = i.thresholdMs ?? channelBusyAckThresholdMs();
14353
+ if (i.pendingAgeMs < threshold) return false;
14354
+ if (!i.sessionAlive) return false;
14355
+ const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
14356
+ return i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
14357
+ }
14358
+ function busyAckNoticeText() {
14359
+ return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
14360
+ }
14338
14361
  var GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
14339
14362
  var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
14340
14363
  function readGiveUpSignalAtMs(path, now = Date.now()) {
@@ -16249,6 +16272,15 @@ function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undelivera
16249
16272
  );
16250
16273
  }
16251
16274
  }
16275
+ function readSlackPendingInboundMarker(channel, threadTs, messageTs) {
16276
+ const path = slackPendingInboundPath(channel, threadTs, messageTs);
16277
+ if (!path || !existsSync4(path)) return null;
16278
+ try {
16279
+ return JSON.parse(readFileSync5(path, "utf-8"));
16280
+ } catch {
16281
+ return null;
16282
+ }
16283
+ }
16252
16284
  function healSlackUndeliverable(channel, messageTs) {
16253
16285
  if (!BOT_TOKEN || !channel || !messageTs) return;
16254
16286
  const headers = {
@@ -16299,6 +16331,67 @@ function postUndeliverableNotice(channel, threadTs, isThreadReply) {
16299
16331
  function __resetSlackUndeliverableNoticeThrottle() {
16300
16332
  lastSlackUndeliverableNoticeAt.clear();
16301
16333
  }
16334
+ var lastSlackBusyAckNoticeAt = /* @__PURE__ */ new Map();
16335
+ function postBusyAckNotice(channel, threadTs, isThreadReply) {
16336
+ if (!BOT_TOKEN || !channel) return;
16337
+ const now = Date.now();
16338
+ const conversationKey = isThreadReply && threadTs ? `${channel}:${threadTs}` : channel;
16339
+ if (!shouldPostUndeliverableNotice(
16340
+ lastSlackBusyAckNoticeAt.get(conversationKey),
16341
+ now,
16342
+ BUSY_ACK_NOTICE_THROTTLE_MS
16343
+ )) {
16344
+ return;
16345
+ }
16346
+ lastSlackBusyAckNoticeAt.set(conversationKey, now);
16347
+ fetch("https://slack.com/api/chat.postMessage", {
16348
+ method: "POST",
16349
+ headers: {
16350
+ "Content-Type": "application/json",
16351
+ Authorization: `Bearer ${BOT_TOKEN}`
16352
+ },
16353
+ body: JSON.stringify({
16354
+ channel,
16355
+ text: busyAckNoticeText(),
16356
+ ...threadTs ? { thread_ts: threadTs } : {}
16357
+ }),
16358
+ // Bound the request so a hung Slack call can't dangle to the platform
16359
+ // timeout (matches the other bounded REST calls in this file). 10s mirrors
16360
+ // Telegram's busy-ack send; fire-and-forget, an abort is swallowed below.
16361
+ signal: AbortSignal.timeout(1e4)
16362
+ }).catch(() => {
16363
+ });
16364
+ }
16365
+ function scheduleBusyAck(channel, threadTs, messageTs, isThreadReply) {
16366
+ if (!channelBusyAckEnabled()) return;
16367
+ const thresholdMs = channelBusyAckThresholdMs();
16368
+ const timer = setTimeout(() => {
16369
+ const probe = process.env.TMUX && AGENT_CODE_NAME ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
16370
+ let paneLogFreshAgeMs = null;
16371
+ if (SLACK_AGENT_DIR) {
16372
+ try {
16373
+ const paneMtimeMs = statSync2(join5(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
16374
+ paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
16375
+ } catch {
16376
+ }
16377
+ }
16378
+ const marker = readSlackPendingInboundMarker(channel, threadTs, messageTs);
16379
+ if (!marker) return;
16380
+ const post = decideBusyAck({
16381
+ hasTarget: Boolean(channel && messageTs),
16382
+ stillPending: true,
16383
+ pendingAgeMs: Math.max(0, Date.now() - Date.parse(marker.received_at)),
16384
+ sessionAlive: probe.tmux === "alive" && probe.claude === "alive",
16385
+ paneLogFreshAgeMs,
16386
+ thresholdMs
16387
+ });
16388
+ if (post) postBusyAckNotice(channel, threadTs, isThreadReply);
16389
+ }, thresholdMs);
16390
+ timer.unref();
16391
+ }
16392
+ function __resetSlackBusyAckNoticeThrottle() {
16393
+ lastSlackBusyAckNoticeAt.clear();
16394
+ }
16302
16395
  function clearSlackMarkerFileWithHeal(fullPath) {
16303
16396
  let marker = null;
16304
16397
  try {
@@ -18917,6 +19010,9 @@ async function connectSocketMode() {
18917
19010
  }
18918
19011
  if (channel && ts && shouldEngage) {
18919
19012
  trackPendingMessage(channel, threadTs, ts, ackDecision === "undeliverable");
19013
+ if (ackDecision === "ack") {
19014
+ scheduleBusyAck(channel, threadTs, ts, isThreadReply);
19015
+ }
18920
19016
  }
18921
19017
  const userName = await resolveUserName(user);
18922
19018
  const fileMeta = await buildInboundFileMeta(evt.files, AGENT_CODE_NAME, channel);
@@ -19091,6 +19187,7 @@ if (THREAD_STORE_PATH) {
19091
19187
  }
19092
19188
  connectSocketModeSafely();
19093
19189
  export {
19190
+ __resetSlackBusyAckNoticeThrottle,
19094
19191
  __resetSlackGiveUpNoticeStateForTests,
19095
19192
  __resetSlackUndeliverableNoticeThrottle
19096
19193
  };
@@ -15832,6 +15832,29 @@ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDEL
15832
15832
  function undeliverableNoticeText() {
15833
15833
  return "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
15834
15834
  }
15835
+ var BUSY_ACK_THRESHOLD_MS = 9e4;
15836
+ var BUSY_ACK_NOTICE_THROTTLE_MS = 10 * 60 * 1e3;
15837
+ function channelBusyAckEnabled() {
15838
+ const v = process.env.AGT_CHANNEL_BUSY_ACK_ENABLED;
15839
+ return v === "1" || v === "true";
15840
+ }
15841
+ function channelBusyAckThresholdMs() {
15842
+ const raw = parseInt(process.env.AGT_CHANNEL_BUSY_ACK_THRESHOLD_MS ?? "", 10);
15843
+ return Number.isFinite(raw) && raw > 0 ? raw : BUSY_ACK_THRESHOLD_MS;
15844
+ }
15845
+ function decideBusyAck(i) {
15846
+ if (!i.hasTarget) return false;
15847
+ if (!i.stillPending) return false;
15848
+ if (!Number.isFinite(i.pendingAgeMs)) return false;
15849
+ const threshold = i.thresholdMs ?? channelBusyAckThresholdMs();
15850
+ if (i.pendingAgeMs < threshold) return false;
15851
+ if (!i.sessionAlive) return false;
15852
+ const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
15853
+ return i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
15854
+ }
15855
+ function busyAckNoticeText() {
15856
+ return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
15857
+ }
15835
15858
  var GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
15836
15859
  var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
15837
15860
  function readGiveUpSignalAtMs(path, now = Date.now()) {
@@ -16161,6 +16184,70 @@ function notifyBackOnline(chatId) {
16161
16184
  function __resetBackOnlineNoticeThrottle() {
16162
16185
  lastBackOnlineNoticeAt.clear();
16163
16186
  }
16187
+ var lastBusyAckNoticeAt = /* @__PURE__ */ new Map();
16188
+ function postBusyAckNotice(chatId, messageId) {
16189
+ const key2 = String(chatId);
16190
+ const now = Date.now();
16191
+ if (!shouldPostUndeliverableNotice(lastBusyAckNoticeAt.get(key2), now, BUSY_ACK_NOTICE_THROTTLE_MS)) {
16192
+ return;
16193
+ }
16194
+ lastBusyAckNoticeAt.set(key2, now);
16195
+ void (async () => {
16196
+ try {
16197
+ const resp = await telegramApiCall(
16198
+ "sendMessage",
16199
+ {
16200
+ chat_id: chatId,
16201
+ text: busyAckNoticeText(),
16202
+ reply_to_message_id: Number(messageId),
16203
+ allow_sending_without_reply: true
16204
+ },
16205
+ 1e4
16206
+ );
16207
+ if (!resp.ok) {
16208
+ process.stderr.write(
16209
+ `telegram-channel(${AGENT_CODE_NAME}): busy-ack notice failed (chat=${redactId(chatId)}): ${resp.description ?? "unknown"}
16210
+ `
16211
+ );
16212
+ }
16213
+ } catch (err) {
16214
+ process.stderr.write(
16215
+ `telegram-channel(${AGENT_CODE_NAME}): busy-ack notice error: ${err.message}
16216
+ `
16217
+ );
16218
+ }
16219
+ })();
16220
+ }
16221
+ function scheduleBusyAck(chatId, messageId) {
16222
+ if (!channelBusyAckEnabled()) return;
16223
+ const thresholdMs = channelBusyAckThresholdMs();
16224
+ const timer = setTimeout(() => {
16225
+ const probe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
16226
+ let paneLogFreshAgeMs = null;
16227
+ if (AGENT_DIR) {
16228
+ try {
16229
+ const paneMtimeMs = statSync(join4(AGENT_DIR, "pane.log")).mtimeMs;
16230
+ paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
16231
+ } catch {
16232
+ }
16233
+ }
16234
+ const marker = readPendingInboundMarker(chatId, messageId);
16235
+ if (!marker) return;
16236
+ const post = decideBusyAck({
16237
+ hasTarget: Boolean(chatId && messageId),
16238
+ stillPending: true,
16239
+ pendingAgeMs: Math.max(0, Date.now() - Date.parse(marker.received_at)),
16240
+ sessionAlive: probe.tmux === "alive" && probe.claude === "alive",
16241
+ paneLogFreshAgeMs,
16242
+ thresholdMs
16243
+ });
16244
+ if (post) postBusyAckNotice(chatId, messageId);
16245
+ }, thresholdMs);
16246
+ timer.unref();
16247
+ }
16248
+ function __resetBusyAckNoticeThrottle() {
16249
+ lastBusyAckNoticeAt.clear();
16250
+ }
16164
16251
  var RESTART_FLAGS_DIR = join4(homedir2(), ".augmented", "restart-flags");
16165
16252
  function buildTelegramHelpMessage(codeName) {
16166
16253
  return [
@@ -16630,6 +16717,15 @@ function clearPendingInboundMarker(chatId, messageId) {
16630
16717
  if (!path) return;
16631
16718
  clearTelegramMarkerFileWithHeal(path);
16632
16719
  }
16720
+ function readPendingInboundMarker(chatId, messageId) {
16721
+ const path = pendingInboundPath(chatId, messageId);
16722
+ if (!path || !existsSync2(path)) return null;
16723
+ try {
16724
+ return JSON.parse(readFileSync3(path, "utf-8"));
16725
+ } catch {
16726
+ return null;
16727
+ }
16728
+ }
16633
16729
  var MAX_RECOVERY_ATTEMPTS = 3;
16634
16730
  function nextRetryName(filename) {
16635
16731
  const match = filename.match(/^(.*?)(?:\.retry-(\d+))?\.json$/);
@@ -18042,6 +18138,9 @@ async function pollLoop() {
18042
18138
  ackDecision === "undeliverable",
18043
18139
  channelPayload
18044
18140
  );
18141
+ if (ackDecision === "ack") {
18142
+ scheduleBusyAck(chatId, messageId);
18143
+ }
18045
18144
  await mcp.notification({
18046
18145
  method: "notifications/claude/channel",
18047
18146
  params: channelPayload
@@ -18139,6 +18238,7 @@ pollLoop().catch((err) => {
18139
18238
  });
18140
18239
  export {
18141
18240
  __resetBackOnlineNoticeThrottle,
18241
+ __resetBusyAckNoticeThrottle,
18142
18242
  __resetGiveUpNoticeStateForTests,
18143
18243
  __resetUndeliverableNoticeThrottle,
18144
18244
  _resetQueuedReplyCountsForTests
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.27.122",
3
+ "version": "0.27.124",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {