@integrity-labs/agt-cli 0.27.97 → 0.27.98

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,30 @@ 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 GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
14339
+ var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
14340
+ function readGiveUpSignalAtMs(path, now = Date.now()) {
14341
+ if (!path) return null;
14342
+ try {
14343
+ const raw = JSON.parse(readFileSync(path, "utf8"));
14344
+ if (typeof raw.gave_up_at !== "string") return null;
14345
+ const t = Date.parse(raw.gave_up_at);
14346
+ if (!Number.isFinite(t) || t > now) return null;
14347
+ return t;
14348
+ } catch {
14349
+ return null;
14350
+ }
14351
+ }
14352
+ function decideGiveUpNotice(input) {
14353
+ const maxAge = input.maxAgeMs ?? GIVE_UP_SIGNAL_MAX_AGE_MS;
14354
+ if (input.signalAtMs == null) return false;
14355
+ if (input.nowMs - input.signalAtMs > maxAge) return false;
14356
+ if (input.lastHandledAtMs != null && input.signalAtMs <= input.lastHandledAtMs) return false;
14357
+ return true;
14358
+ }
14359
+ function giveUpNoticeText() {
14360
+ return "\u26A0\uFE0F I couldn't read your last message \u2014 please resend it.";
14361
+ }
14338
14362
  function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
14339
14363
  if (!dir) return null;
14340
14364
  let names;
@@ -16322,8 +16346,85 @@ var slackOrphanSweepTimer = setInterval(() => {
16322
16346
  const probe = process.env.TMUX && AGENT_CODE_NAME ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
16323
16347
  const sessionAlive = probe.tmux === "alive" && probe.claude === "alive";
16324
16348
  sweepSlackStaleMarkers(sessionAlive ? channelOrphanMarkerMs() : STALE_MARKER_MS);
16349
+ checkSlackWatchdogGiveUpNotice();
16325
16350
  }, orphanSweepIntervalMs());
16326
16351
  slackOrphanSweepTimer.unref?.();
16352
+ var lastSlackGiveUpHandledAtMs = null;
16353
+ function listPendingSlackConversations() {
16354
+ if (!SLACK_PENDING_INBOUND_DIR || !existsSync3(SLACK_PENDING_INBOUND_DIR)) return [];
16355
+ const byKey = /* @__PURE__ */ new Map();
16356
+ try {
16357
+ for (const name of readdirSync3(SLACK_PENDING_INBOUND_DIR)) {
16358
+ if (!name.endsWith(".json")) continue;
16359
+ try {
16360
+ const marker = JSON.parse(
16361
+ readFileSync4(join5(SLACK_PENDING_INBOUND_DIR, name), "utf8")
16362
+ );
16363
+ if (typeof marker.channel !== "string" || !marker.channel) continue;
16364
+ if (typeof marker.thread_ts !== "string" || !marker.thread_ts) continue;
16365
+ const isThreadReply = typeof marker.message_ts === "string" && marker.message_ts !== marker.thread_ts;
16366
+ const key2 = isThreadReply ? `${marker.channel}:${marker.thread_ts}` : marker.channel;
16367
+ if (!byKey.has(key2)) {
16368
+ byKey.set(key2, { channel: marker.channel, threadTs: marker.thread_ts, isThreadReply });
16369
+ }
16370
+ } catch {
16371
+ }
16372
+ }
16373
+ } catch {
16374
+ return [];
16375
+ }
16376
+ return [...byKey.values()];
16377
+ }
16378
+ function postSlackWatchdogGiveUpNotice(channel, threadTs, isThreadReply) {
16379
+ if (!BOT_TOKEN || !channel) return;
16380
+ const now = Date.now();
16381
+ const conversationKey = isThreadReply ? `${channel}:${threadTs}` : channel;
16382
+ if (!shouldPostUndeliverableNotice(lastSlackUndeliverableNoticeAt.get(conversationKey), now)) {
16383
+ return;
16384
+ }
16385
+ lastSlackUndeliverableNoticeAt.set(conversationKey, now);
16386
+ process.stderr.write(
16387
+ `slack-channel(${AGENT_CODE_NAME}): give-up notice posted channel=${redactSlackId(channel)}
16388
+ `
16389
+ );
16390
+ fetch("https://slack.com/api/chat.postMessage", {
16391
+ method: "POST",
16392
+ headers: {
16393
+ "Content-Type": "application/json",
16394
+ Authorization: `Bearer ${BOT_TOKEN}`
16395
+ },
16396
+ body: JSON.stringify({
16397
+ channel,
16398
+ text: giveUpNoticeText(),
16399
+ // CR on PR #1824: anchor to the originating message even for root
16400
+ // messages (threadTs === messageTs is still the conversation target) —
16401
+ // a channel-root notice diverges from postUndeliverableNotice and
16402
+ // loses the link to the message the user should resend. isThreadReply
16403
+ // only shapes the THROTTLE key, not the delivery target.
16404
+ ...threadTs ? { thread_ts: threadTs } : {}
16405
+ })
16406
+ }).catch(() => {
16407
+ });
16408
+ }
16409
+ function checkSlackWatchdogGiveUpNotice() {
16410
+ if (!SLACK_AGENT_DIR) return;
16411
+ const signalAtMs = readGiveUpSignalAtMs(join5(SLACK_AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
16412
+ const act = decideGiveUpNotice({
16413
+ signalAtMs,
16414
+ lastHandledAtMs: lastSlackGiveUpHandledAtMs,
16415
+ nowMs: Date.now()
16416
+ });
16417
+ if (signalAtMs != null) {
16418
+ lastSlackGiveUpHandledAtMs = Math.max(lastSlackGiveUpHandledAtMs ?? 0, signalAtMs);
16419
+ }
16420
+ if (!act) return;
16421
+ for (const convo of listPendingSlackConversations()) {
16422
+ postSlackWatchdogGiveUpNotice(convo.channel, convo.threadTs, convo.isThreadReply);
16423
+ }
16424
+ }
16425
+ function __resetSlackGiveUpNoticeStateForTests() {
16426
+ lastSlackGiveUpHandledAtMs = null;
16427
+ }
16327
16428
  var SLACK_PROCESS_BOOT_MS = Date.now();
16328
16429
  var STRANDED_INBOUND_MIN_AGE_FROM_BOOT_MS = 6e4;
16329
16430
  var STRANDED_INBOUND_MAX_AGE_MS = 5 * 60 * 1e3;
@@ -18742,5 +18843,6 @@ if (THREAD_STORE_PATH) {
18742
18843
  }
18743
18844
  connectSocketModeSafely();
18744
18845
  export {
18846
+ __resetSlackGiveUpNoticeStateForTests,
18745
18847
  __resetSlackUndeliverableNoticeThrottle
18746
18848
  };
@@ -15832,6 +15832,30 @@ 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 GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
15836
+ var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
15837
+ function readGiveUpSignalAtMs(path, now = Date.now()) {
15838
+ if (!path) return null;
15839
+ try {
15840
+ const raw = JSON.parse(readFileSync2(path, "utf8"));
15841
+ if (typeof raw.gave_up_at !== "string") return null;
15842
+ const t = Date.parse(raw.gave_up_at);
15843
+ if (!Number.isFinite(t) || t > now) return null;
15844
+ return t;
15845
+ } catch {
15846
+ return null;
15847
+ }
15848
+ }
15849
+ function decideGiveUpNotice(input) {
15850
+ const maxAge = input.maxAgeMs ?? GIVE_UP_SIGNAL_MAX_AGE_MS;
15851
+ if (input.signalAtMs == null) return false;
15852
+ if (input.nowMs - input.signalAtMs > maxAge) return false;
15853
+ if (input.lastHandledAtMs != null && input.signalAtMs <= input.lastHandledAtMs) return false;
15854
+ return true;
15855
+ }
15856
+ function giveUpNoticeText() {
15857
+ return "\u26A0\uFE0F I couldn't read your last message \u2014 please resend it.";
15858
+ }
15835
15859
  function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
15836
15860
  if (!dir) return null;
15837
15861
  let names;
@@ -16831,8 +16855,84 @@ var orphanSweepTimer = setInterval(() => {
16831
16855
  const probe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
16832
16856
  const sessionAlive = probe.tmux === "alive" && probe.claude === "alive";
16833
16857
  sweepTelegramStaleMarkers(sessionAlive ? channelOrphanMarkerMs() : STALE_MARKER_MS);
16858
+ checkWatchdogGiveUpNotice();
16834
16859
  }, orphanSweepIntervalMs());
16835
16860
  orphanSweepTimer.unref?.();
16861
+ var lastGiveUpHandledAtMs = null;
16862
+ function listPendingInboundChatIds() {
16863
+ if (!PENDING_INBOUND_DIR || !existsSync2(PENDING_INBOUND_DIR)) return [];
16864
+ const chats = /* @__PURE__ */ new Set();
16865
+ try {
16866
+ for (const name of readdirSync2(PENDING_INBOUND_DIR)) {
16867
+ if (!name.endsWith(".json")) continue;
16868
+ try {
16869
+ const marker = JSON.parse(
16870
+ readFileSync3(join4(PENDING_INBOUND_DIR, name), "utf8")
16871
+ );
16872
+ if (typeof marker.chat_id === "string" && marker.chat_id) chats.add(marker.chat_id);
16873
+ } catch {
16874
+ }
16875
+ }
16876
+ } catch {
16877
+ return [];
16878
+ }
16879
+ return [...chats];
16880
+ }
16881
+ async function notifyWatchdogGiveUp(chatId) {
16882
+ const now = Date.now();
16883
+ if (!shouldPostUndeliverableNotice(lastUndeliverableNoticeAt.get(chatId), now)) return;
16884
+ lastUndeliverableNoticeAt.set(chatId, now);
16885
+ try {
16886
+ const resp = await telegramApiCall(
16887
+ "sendMessage",
16888
+ { chat_id: chatId, text: giveUpNoticeText() },
16889
+ 1e4
16890
+ );
16891
+ if (!resp.ok) {
16892
+ process.stderr.write(
16893
+ `telegram-channel(${AGENT_CODE_NAME}): give-up notice failed (chat=${redactId(chatId)}): ${resp.description ?? "unknown"}
16894
+ `
16895
+ );
16896
+ return;
16897
+ }
16898
+ process.stderr.write(
16899
+ `telegram-channel(${AGENT_CODE_NAME}): give-up notice posted chat=${redactId(chatId)}
16900
+ `
16901
+ );
16902
+ } catch (err) {
16903
+ process.stderr.write(
16904
+ `telegram-channel(${AGENT_CODE_NAME}): give-up notice error: ${err.message}
16905
+ `
16906
+ );
16907
+ }
16908
+ }
16909
+ function checkWatchdogGiveUpNotice() {
16910
+ if (!AGENT_DIR) return;
16911
+ const signalAtMs = readGiveUpSignalAtMs(join4(AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
16912
+ const act = decideGiveUpNotice({
16913
+ signalAtMs,
16914
+ lastHandledAtMs: lastGiveUpHandledAtMs,
16915
+ nowMs: Date.now()
16916
+ });
16917
+ if (signalAtMs != null) {
16918
+ lastGiveUpHandledAtMs = Math.max(lastGiveUpHandledAtMs ?? 0, signalAtMs);
16919
+ }
16920
+ if (!act) return;
16921
+ const chats = listPendingInboundChatIds();
16922
+ if (chats.length === 0) {
16923
+ process.stderr.write(
16924
+ `telegram-channel(${AGENT_CODE_NAME}): give-up signal seen but no pending markers \u2014 nothing to notice
16925
+ `
16926
+ );
16927
+ return;
16928
+ }
16929
+ for (const chatId of chats) {
16930
+ void notifyWatchdogGiveUp(chatId);
16931
+ }
16932
+ }
16933
+ function __resetGiveUpNoticeStateForTests() {
16934
+ lastGiveUpHandledAtMs = null;
16935
+ }
16836
16936
  var queuedReplyCounts = /* @__PURE__ */ new Map();
16837
16937
  function _resetQueuedReplyCountsForTests() {
16838
16938
  queuedReplyCounts.clear();
@@ -18039,6 +18139,7 @@ pollLoop().catch((err) => {
18039
18139
  });
18040
18140
  export {
18041
18141
  __resetBackOnlineNoticeThrottle,
18142
+ __resetGiveUpNoticeStateForTests,
18042
18143
  __resetUndeliverableNoticeThrottle,
18043
18144
  _resetQueuedReplyCountsForTests
18044
18145
  };
@@ -22,7 +22,7 @@ import {
22
22
  stopPersistentSession,
23
23
  takeZombieDetection,
24
24
  writePersistentClaudeWrapper
25
- } from "./chunk-MBHA6PEN.js";
25
+ } from "./chunk-3TAVWBOA.js";
26
26
  import "./chunk-T2UTQH6W.js";
27
27
  import "./chunk-XWVM4KPK.js";
28
28
  export {
@@ -50,4 +50,4 @@ export {
50
50
  takeZombieDetection,
51
51
  writePersistentClaudeWrapper
52
52
  };
53
- //# sourceMappingURL=persistent-session-2XLQOMXH.js.map
53
+ //# sourceMappingURL=persistent-session-HAPAZHOA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-MBHA6PEN.js";
3
+ } from "./chunk-3TAVWBOA.js";
4
4
  import "./chunk-T2UTQH6W.js";
5
5
  import "./chunk-XWVM4KPK.js";
6
6
 
@@ -70,4 +70,4 @@ export {
70
70
  collectResponsivenessProbes,
71
71
  getResponsivenessIntervalMs
72
72
  };
73
- //# sourceMappingURL=responsiveness-probe-ABUPNDR7.js.map
73
+ //# sourceMappingURL=responsiveness-probe-ZFYY5XOU.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.27.97",
3
+ "version": "0.27.98",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {