@integrity-labs/agt-cli 0.27.96 → 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
  };
@@ -15264,6 +15264,26 @@ function createSingleTailSlot() {
15264
15264
  };
15265
15265
  }
15266
15266
 
15267
+ // src/telegram-command-registry.ts
15268
+ var HELP = {
15269
+ command: "help",
15270
+ description: "Show available commands"
15271
+ };
15272
+ var RESTART = {
15273
+ command: "restart",
15274
+ description: "Restart this agent"
15275
+ };
15276
+ var INVESTIGATE = {
15277
+ command: "investigate",
15278
+ description: "Live tail of this agent's terminal pane (operators only)"
15279
+ };
15280
+ function buildCommandRegistrations() {
15281
+ return [
15282
+ { scope: { type: "default" }, commands: [HELP, RESTART] },
15283
+ { scope: { type: "all_private_chats" }, commands: [HELP, RESTART, INVESTIGATE] }
15284
+ ];
15285
+ }
15286
+
15267
15287
  // src/pane-tail.ts
15268
15288
  import { execFile } from "child_process";
15269
15289
  import { promisify } from "util";
@@ -15812,6 +15832,30 @@ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDEL
15812
15832
  function undeliverableNoticeText() {
15813
15833
  return "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
15814
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
+ }
15815
15859
  function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
15816
15860
  if (!dir) return null;
15817
15861
  let names;
@@ -16811,8 +16855,84 @@ var orphanSweepTimer = setInterval(() => {
16811
16855
  const probe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
16812
16856
  const sessionAlive = probe.tmux === "alive" && probe.claude === "alive";
16813
16857
  sweepTelegramStaleMarkers(sessionAlive ? channelOrphanMarkerMs() : STALE_MARKER_MS);
16858
+ checkWatchdogGiveUpNotice();
16814
16859
  }, orphanSweepIntervalMs());
16815
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
+ }
16816
16936
  var queuedReplyCounts = /* @__PURE__ */ new Map();
16817
16937
  function _resetQueuedReplyCountsForTests() {
16818
16938
  queuedReplyCounts.clear();
@@ -17992,6 +18112,24 @@ process.stderr.write(
17992
18112
  `telegram-channel(${AGENT_CODE_NAME}): started, long-polling getUpdates
17993
18113
  `
17994
18114
  );
18115
+ void (async () => {
18116
+ for (const registration of buildCommandRegistrations()) {
18117
+ try {
18118
+ const resp = await telegramApiCall("setMyCommands", registration, 1e4);
18119
+ if (!resp.ok) {
18120
+ process.stderr.write(
18121
+ `telegram-channel(${AGENT_CODE_NAME}): setMyCommands(${registration.scope.type}) rejected: ${resp.description ?? "unknown"}
18122
+ `
18123
+ );
18124
+ }
18125
+ } catch (err) {
18126
+ process.stderr.write(
18127
+ `telegram-channel(${AGENT_CODE_NAME}): setMyCommands(${registration.scope.type}) failed: ${redactAugmentedPaths(err.message)}
18128
+ `
18129
+ );
18130
+ }
18131
+ }
18132
+ })();
17995
18133
  pollLoop().catch((err) => {
17996
18134
  process.stderr.write(
17997
18135
  `telegram-channel(${AGENT_CODE_NAME}): poll loop died: ${err.message}
@@ -18001,6 +18139,7 @@ pollLoop().catch((err) => {
18001
18139
  });
18002
18140
  export {
18003
18141
  __resetBackOnlineNoticeThrottle,
18142
+ __resetGiveUpNoticeStateForTests,
18004
18143
  __resetUndeliverableNoticeThrottle,
18005
18144
  _resetQueuedReplyCountsForTests
18006
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.96",
3
+ "version": "0.27.98",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {