@integrity-labs/agt-cli 0.27.162 → 0.27.164

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.
@@ -14318,9 +14318,9 @@ import https from "https";
14318
14318
  import { createHash, randomUUID as randomUUID2 } from "crypto";
14319
14319
  import {
14320
14320
  createWriteStream,
14321
- existsSync as existsSync4,
14321
+ existsSync as existsSync5,
14322
14322
  mkdirSync as mkdirSync4,
14323
- readFileSync as readFileSync5,
14323
+ readFileSync as readFileSync6,
14324
14324
  readdirSync as readdirSync2,
14325
14325
  renameSync as renameSync4,
14326
14326
  statSync,
@@ -14328,8 +14328,8 @@ import {
14328
14328
  watch,
14329
14329
  writeFileSync as writeFileSync4
14330
14330
  } from "fs";
14331
- import { homedir as homedir2 } from "os";
14332
- import { join as join5 } from "path";
14331
+ import { homedir as homedir3 } from "os";
14332
+ import { join as join6 } from "path";
14333
14333
 
14334
14334
  // src/channel-attachments.ts
14335
14335
  import { homedir } from "os";
@@ -14868,6 +14868,100 @@ function decideInboundAccess(input) {
14868
14868
  return { kind: "admit" };
14869
14869
  }
14870
14870
 
14871
+ // src/watch-command.ts
14872
+ var WATCH_DEFAULT_DURATION_MS = 2 * 60 * 60 * 1e3;
14873
+ var WATCH_MAX_DURATION_MS = 7 * 24 * 60 * 60 * 1e3;
14874
+ var WATCH_MIN_DURATION_MS = 5 * 60 * 1e3;
14875
+ function extractDriveFileId(s) {
14876
+ const trimmed = s.trim().replace(/^<|>$/g, "");
14877
+ const pathId = trimmed.match(/\/d\/([A-Za-z0-9_-]{10,})/);
14878
+ if (pathId?.[1]) return pathId[1];
14879
+ const idParam = trimmed.match(/[?&]id=([A-Za-z0-9_-]{10,})/);
14880
+ if (idParam?.[1]) return idParam[1];
14881
+ if (!/[/:?]/.test(trimmed) && /^[A-Za-z0-9_-]{20,}$/.test(trimmed)) return trimmed;
14882
+ return null;
14883
+ }
14884
+ function parseDurationToken(token) {
14885
+ const m = token.trim().match(/^(\d+)\s*([smhd])?$/i);
14886
+ if (!m?.[1]) return null;
14887
+ const n = Number.parseInt(m[1], 10);
14888
+ if (!Number.isFinite(n) || n <= 0) return null;
14889
+ const unit = (m[2] ?? "m").toLowerCase();
14890
+ const mult = unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
14891
+ return n * mult;
14892
+ }
14893
+ function parseWatchArgs(args) {
14894
+ const parts = args.trim().split(/\s+/).filter(Boolean);
14895
+ const first = parts[0];
14896
+ if (!first) return { ok: false, error: "usage" };
14897
+ if (parts.length > 2) return { ok: false, error: "usage" };
14898
+ const fileId = extractDriveFileId(first);
14899
+ if (!fileId) return { ok: false, error: "bad-url" };
14900
+ let durationMs = WATCH_DEFAULT_DURATION_MS;
14901
+ const durationToken = parts[1];
14902
+ if (durationToken !== void 0) {
14903
+ const parsed = parseDurationToken(durationToken);
14904
+ if (parsed === null) return { ok: false, error: "bad-duration" };
14905
+ durationMs = Math.min(Math.max(parsed, WATCH_MIN_DURATION_MS), WATCH_MAX_DURATION_MS);
14906
+ }
14907
+ return { ok: true, value: { fileId, durationMs } };
14908
+ }
14909
+ async function postWatchTrigger(opts) {
14910
+ const doFetch = opts.fetchImpl ?? fetch;
14911
+ try {
14912
+ const res = await doFetch(`${opts.host}/host/triggers`, {
14913
+ method: "POST",
14914
+ headers: {
14915
+ "Content-Type": "application/json; charset=utf-8",
14916
+ Authorization: `Bearer ${opts.apiKey}`
14917
+ },
14918
+ body: JSON.stringify({
14919
+ agent_id: opts.agentId,
14920
+ provider: "gdrive_comments",
14921
+ config: { fileId: opts.fileId },
14922
+ expires_at: opts.expiresAtIso
14923
+ }),
14924
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
14925
+ });
14926
+ if (!res.ok) {
14927
+ let msg = `HTTP ${res.status}`;
14928
+ try {
14929
+ const j2 = await res.json();
14930
+ if (j2?.error) msg = j2.error;
14931
+ } catch {
14932
+ }
14933
+ return { ok: false, status: res.status, error: msg };
14934
+ }
14935
+ const j = await res.json().catch(() => ({}));
14936
+ return { ok: true, status: res.status, reused: !!j.reused, expiresAt: j.expires_at ?? null };
14937
+ } catch (err) {
14938
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
14939
+ }
14940
+ }
14941
+ function humanDuration(ms) {
14942
+ const mins = Math.round(ms / 6e4);
14943
+ if (mins % 1440 === 0) return `${mins / 1440}d`;
14944
+ if (mins % 60 === 0) return `${mins / 60}h`;
14945
+ return `${mins}m`;
14946
+ }
14947
+ function watchUsageText() {
14948
+ return "Usage: `/watch <google-doc-url> [duration]` \u2014 e.g. `/watch https://docs.google.com/document/d/\u2026/edit 2h`. Duration defaults to 2h (max 7d).";
14949
+ }
14950
+ function watchBadUrlText() {
14951
+ return "That doesn't look like a Google Doc link. Paste the doc's share URL (the part with `/d/<id>`).";
14952
+ }
14953
+ function watchBadDurationText() {
14954
+ return "I couldn't read that duration. Try `30m`, `2h`, or `1d` (max 7d).";
14955
+ }
14956
+ function watchErrorText() {
14957
+ return "\u274C I couldn't set up that watch just now. Please try again in a moment.";
14958
+ }
14959
+ function watchSuccessTextPlain(fileId, durationMs, reused) {
14960
+ const url = `https://docs.google.com/document/d/${fileId}/edit`;
14961
+ const verb = reused ? "Extended my watch on" : "Watching";
14962
+ return `\u{1F440} ${verb} ${url} for new comments that mention me, for the next ${humanDuration(durationMs)}. I'll pause automatically when the window's up. (Make sure Google Drive is connected with comment access, or I won't see them.)`;
14963
+ }
14964
+
14871
14965
  // src/telegram-peer-rate-limiter.ts
14872
14966
  var SECOND_MS = 1e3;
14873
14967
  var MINUTE_MS = 60 * SECOND_MS;
@@ -15976,8 +16070,47 @@ function readLockHolder(path) {
15976
16070
  }
15977
16071
 
15978
16072
  // src/ack-reaction.ts
15979
- import { readdirSync, readFileSync as readFileSync4 } from "fs";
16073
+ import { readdirSync, readFileSync as readFileSync5 } from "fs";
16074
+ import { join as join5 } from "path";
16075
+
16076
+ // src/flags-cache-read.ts
16077
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
16078
+ import { homedir as homedir2 } from "os";
15980
16079
  import { join as join4 } from "path";
16080
+ function defaultFlagsCachePath() {
16081
+ return join4(homedir2(), ".augmented", "flags-cache.json");
16082
+ }
16083
+ function envBoolean(raw) {
16084
+ if (raw === void 0) return void 0;
16085
+ const v = raw.trim().toLowerCase();
16086
+ if (v === "") return void 0;
16087
+ if (v === "1" || v === "true" || v === "yes" || v === "on") return true;
16088
+ if (v === "0" || v === "false" || v === "no" || v === "off") return false;
16089
+ return void 0;
16090
+ }
16091
+ function cachedBoolean(key2, path) {
16092
+ try {
16093
+ if (!existsSync4(path)) return void 0;
16094
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
16095
+ if (!parsed || typeof parsed !== "object") return void 0;
16096
+ const flags = parsed.flags;
16097
+ if (!flags || typeof flags !== "object") return void 0;
16098
+ const value = flags[key2];
16099
+ return typeof value === "boolean" ? value : void 0;
16100
+ } catch {
16101
+ return void 0;
16102
+ }
16103
+ }
16104
+ function resolveHostBooleanFlag(opts) {
16105
+ const env = opts.env ?? process.env;
16106
+ const envValue = envBoolean(env[opts.envVar]);
16107
+ if (envValue !== void 0) return envValue;
16108
+ const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath());
16109
+ if (cached2 !== void 0) return cached2;
16110
+ return opts.defaultValue;
16111
+ }
16112
+
16113
+ // src/ack-reaction.ts
15981
16114
  var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
15982
16115
  var ACK_STARTUP_GRACE_MS = 6e4;
15983
16116
  var ACK_PANE_FRESH_THRESHOLD_MS = 6e4;
@@ -16010,8 +16143,11 @@ function undeliverableNoticeText() {
16010
16143
  var BUSY_ACK_THRESHOLD_MS = 9e4;
16011
16144
  var BUSY_ACK_NOTICE_THROTTLE_MS = 10 * 60 * 1e3;
16012
16145
  function channelBusyAckEnabled() {
16013
- const v = process.env.AGT_CHANNEL_BUSY_ACK_ENABLED;
16014
- return v === "1" || v === "true";
16146
+ return resolveHostBooleanFlag({
16147
+ key: "channel-busy-ack",
16148
+ envVar: "AGT_CHANNEL_BUSY_ACK_ENABLED",
16149
+ defaultValue: false
16150
+ });
16015
16151
  }
16016
16152
  function channelBusyAckThresholdMs() {
16017
16153
  const raw = parseInt(process.env.AGT_CHANNEL_BUSY_ACK_THRESHOLD_MS ?? "", 10);
@@ -16035,7 +16171,7 @@ var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
16035
16171
  function readGiveUpSignalAtMs(path, now = Date.now()) {
16036
16172
  if (!path) return null;
16037
16173
  try {
16038
- const raw = JSON.parse(readFileSync4(path, "utf8"));
16174
+ const raw = JSON.parse(readFileSync5(path, "utf8"));
16039
16175
  if (typeof raw.gave_up_at !== "string") return null;
16040
16176
  const t = Date.parse(raw.gave_up_at);
16041
16177
  if (!Number.isFinite(t) || t > now) return null;
@@ -16067,7 +16203,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
16067
16203
  if (!name.endsWith(".json")) continue;
16068
16204
  let receivedAt;
16069
16205
  try {
16070
- const raw = JSON.parse(readFileSync4(join4(dir, name), "utf-8"));
16206
+ const raw = JSON.parse(readFileSync5(join5(dir, name), "utf-8"));
16071
16207
  receivedAt = raw.received_at;
16072
16208
  } catch {
16073
16209
  continue;
@@ -16115,7 +16251,7 @@ function redactId(id) {
16115
16251
  }
16116
16252
  var BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
16117
16253
  var AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME ?? "unknown";
16118
- var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join5(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
16254
+ var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join6(homedir3(), ".augmented", AGENT_CODE_NAME) : null;
16119
16255
  var AGT_HOST = process.env.AGT_HOST ?? null;
16120
16256
  var AGT_API_KEY = process.env.AGT_API_KEY ?? null;
16121
16257
  var AGT_AGENT_ID = process.env.AGT_AGENT_ID ?? null;
@@ -16209,9 +16345,9 @@ if (!BOT_TOKEN) {
16209
16345
  var stderrLogStream = null;
16210
16346
  if (AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown") {
16211
16347
  try {
16212
- const logDir = join5(homedir2(), ".augmented", AGENT_CODE_NAME);
16348
+ const logDir = join6(homedir3(), ".augmented", AGENT_CODE_NAME);
16213
16349
  mkdirSync4(logDir, { recursive: true });
16214
- stderrLogStream = createWriteStream(join5(logDir, "telegram-channel-stderr.log"), {
16350
+ stderrLogStream = createWriteStream(join6(logDir, "telegram-channel-stderr.log"), {
16215
16351
  flags: "a",
16216
16352
  mode: 384
16217
16353
  });
@@ -16401,7 +16537,7 @@ function scheduleBusyAck(chatId, messageId) {
16401
16537
  let paneLogFreshAgeMs = null;
16402
16538
  if (AGENT_DIR) {
16403
16539
  try {
16404
- const paneMtimeMs = statSync(join5(AGENT_DIR, "pane.log")).mtimeMs;
16540
+ const paneMtimeMs = statSync(join6(AGENT_DIR, "pane.log")).mtimeMs;
16405
16541
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
16406
16542
  } catch {
16407
16543
  }
@@ -16423,7 +16559,7 @@ function scheduleBusyAck(chatId, messageId) {
16423
16559
  function __resetBusyAckNoticeThrottle() {
16424
16560
  lastBusyAckNoticeAt.clear();
16425
16561
  }
16426
- var RESTART_FLAGS_DIR = join5(homedir2(), ".augmented", "restart-flags");
16562
+ var RESTART_FLAGS_DIR = join6(homedir3(), ".augmented", "restart-flags");
16427
16563
  function writeTelegramRestartConfirm(reply, requesterName) {
16428
16564
  if (!RESTART_CONFIRM_FILE) return;
16429
16565
  const marker = {
@@ -16491,6 +16627,7 @@ function buildTelegramHelpMessage(codeName) {
16491
16627
  `_Type these in any chat where the bot is present (intercepted by the agent):_`,
16492
16628
  `\u2022 /help \u2014 show this help`,
16493
16629
  `\u2022 /status \u2014 this agent's model, session origin, uptime + connectivity`,
16630
+ `\u2022 /watch <google-doc-url> [duration] \u2014 watch a Google Doc for comments that mention me (default 2h, max 7d; auto-pauses when the window ends)`,
16494
16631
  `\u2022 /restart \u2014 restart this agent`,
16495
16632
  `\u2022 /investigate-${codeName} \u2014 live tail of this agent's terminal pane (DM only; team owners/admins and the agent's reports-to person)`
16496
16633
  ].join("\n");
@@ -16520,6 +16657,63 @@ async function handleHelpCommand(opts) {
16520
16657
  );
16521
16658
  }
16522
16659
  }
16660
+ async function handleWatchCommand(opts) {
16661
+ const reply = async (text) => {
16662
+ try {
16663
+ const resp = await telegramApiCall(
16664
+ "sendMessage",
16665
+ { chat_id: opts.chatId, text, reply_to_message_id: Number(opts.messageId) },
16666
+ 1e4
16667
+ );
16668
+ if (!resp.ok) {
16669
+ process.stderr.write(
16670
+ `telegram-channel(${AGENT_CODE_NAME}): /watch reply rejected (chat ${redactId(opts.chatId)}): ${resp.description ?? "unknown"}
16671
+ `
16672
+ );
16673
+ }
16674
+ } catch (err) {
16675
+ process.stderr.write(
16676
+ `telegram-channel(${AGENT_CODE_NAME}): /watch reply send failed: ${redactAugmentedPaths(err.message)}
16677
+ `
16678
+ );
16679
+ }
16680
+ };
16681
+ const args = opts.text.replace(/^\/watch(?:@[A-Za-z0-9_]{1,64})?\s*/i, "");
16682
+ const parsed = parseWatchArgs(args);
16683
+ if (!parsed.ok) {
16684
+ await reply(
16685
+ parsed.error === "bad-url" ? watchBadUrlText() : parsed.error === "bad-duration" ? watchBadDurationText() : watchUsageText()
16686
+ );
16687
+ return;
16688
+ }
16689
+ if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) {
16690
+ process.stderr.write(`telegram-channel(${AGENT_CODE_NAME}): /watch missing AGT_* env \u2014 cannot create watch
16691
+ `);
16692
+ await reply(watchErrorText());
16693
+ return;
16694
+ }
16695
+ const expiresAtIso = new Date(Date.now() + parsed.value.durationMs).toISOString();
16696
+ const res = await postWatchTrigger({
16697
+ host: AGT_HOST,
16698
+ apiKey: AGT_API_KEY,
16699
+ agentId: AGT_AGENT_ID,
16700
+ fileId: parsed.value.fileId,
16701
+ expiresAtIso
16702
+ });
16703
+ if (!res.ok) {
16704
+ process.stderr.write(
16705
+ `telegram-channel(${AGENT_CODE_NAME}): /watch create failed (status=${res.status ?? "?"}): ${res.error ?? "unknown"}
16706
+ `
16707
+ );
16708
+ await reply(watchErrorText());
16709
+ return;
16710
+ }
16711
+ process.stderr.write(
16712
+ `telegram-channel(${AGENT_CODE_NAME}): /watch ${res.reused ? "extended" : "created"} for doc ${parsed.value.fileId.slice(0, 8)}\u2026 chat ${redactId(opts.chatId)}
16713
+ `
16714
+ );
16715
+ await reply(watchSuccessTextPlain(parsed.value.fileId, parsed.value.durationMs, !!res.reused));
16716
+ }
16523
16717
  function buildTelegramStatusReply() {
16524
16718
  const state = readAgentSessionState(TELEGRAM_AGENT_DIR);
16525
16719
  return buildAgentConfigReport({
@@ -16555,10 +16749,10 @@ async function handleStatusCommand(opts) {
16555
16749
  }
16556
16750
  async function handleRestartCommand(opts) {
16557
16751
  try {
16558
- if (!existsSync4(RESTART_FLAGS_DIR)) {
16752
+ if (!existsSync5(RESTART_FLAGS_DIR)) {
16559
16753
  mkdirSync4(RESTART_FLAGS_DIR, { recursive: true });
16560
16754
  }
16561
- const flagPath = join5(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
16755
+ const flagPath = join6(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
16562
16756
  writeTelegramRestartConfirm(
16563
16757
  { chat_id: opts.chatId, message_id: opts.messageId },
16564
16758
  opts.requesterName
@@ -16925,6 +17119,10 @@ var STATUS_SYNTAX_RE = /^\/status(?:@([A-Za-z0-9_]{1,64}))?(?:\s|$)/;
16925
17119
  function isStatusSyntax(text) {
16926
17120
  return STATUS_SYNTAX_RE.test(text);
16927
17121
  }
17122
+ var WATCH_SYNTAX_RE = /^\/watch(?:@([A-Za-z0-9_]{1,64}))?(?:\s|$)/;
17123
+ function isWatchSyntax(text) {
17124
+ return WATCH_SYNTAX_RE.test(text);
17125
+ }
16928
17126
  function isRestartSyntax(text) {
16929
17127
  return RESTART_SYNTAX_RE.test(text);
16930
17128
  }
@@ -16937,10 +17135,10 @@ async function classifyRestartCommand(text) {
16937
17135
  if (!ours) return "verification_failed";
16938
17136
  return target === ours ? "act" : "ignore";
16939
17137
  }
16940
- var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join5(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
16941
- var PENDING_INBOUND_DIR = AGENT_DIR ? join5(AGENT_DIR, "telegram-pending-inbound") : null;
16942
- var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join5(AGENT_DIR, "telegram-recovery-outbox") : null;
16943
- var RESTART_CONFIRM_FILE = AGENT_DIR ? join5(AGENT_DIR, "telegram-restart-confirm.json") : null;
17138
+ var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join6(homedir3(), ".augmented", AGENT_CODE_NAME) : null;
17139
+ var PENDING_INBOUND_DIR = AGENT_DIR ? join6(AGENT_DIR, "telegram-pending-inbound") : null;
17140
+ var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join6(AGENT_DIR, "telegram-recovery-outbox") : null;
17141
+ var RESTART_CONFIRM_FILE = AGENT_DIR ? join6(AGENT_DIR, "telegram-restart-confirm.json") : null;
16944
17142
  var TELEGRAM_PROCESS_BOOT_MS = Date.now();
16945
17143
  function safeMarkerName(chatId, messageId) {
16946
17144
  const safe = (s) => s.replace(/[^A-Za-z0-9_-]/g, "_");
@@ -16948,7 +17146,7 @@ function safeMarkerName(chatId, messageId) {
16948
17146
  }
16949
17147
  function pendingInboundPath(chatId, messageId) {
16950
17148
  if (!PENDING_INBOUND_DIR) return null;
16951
- return join5(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
17149
+ return join6(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
16952
17150
  }
16953
17151
  function writePendingInboundMarker(chatId, messageId, chatType, undeliverable = false, payload) {
16954
17152
  const path = pendingInboundPath(chatId, messageId);
@@ -16977,7 +17175,7 @@ function writePendingInboundMarker(chatId, messageId, chatType, undeliverable =
16977
17175
  function clearTelegramMarkerFileWithHeal(fullPath) {
16978
17176
  let marker = null;
16979
17177
  try {
16980
- marker = JSON.parse(readFileSync5(fullPath, "utf-8"));
17178
+ marker = JSON.parse(readFileSync6(fullPath, "utf-8"));
16981
17179
  } catch {
16982
17180
  }
16983
17181
  if (marker && decideRecoveryHeal({
@@ -16987,7 +17185,7 @@ function clearTelegramMarkerFileWithHeal(fullPath) {
16987
17185
  notifyBackOnline(marker.chat_id);
16988
17186
  }
16989
17187
  try {
16990
- if (existsSync4(fullPath)) unlinkSync4(fullPath);
17188
+ if (existsSync5(fullPath)) unlinkSync4(fullPath);
16991
17189
  } catch {
16992
17190
  }
16993
17191
  }
@@ -16998,9 +17196,9 @@ function clearPendingInboundMarker(chatId, messageId) {
16998
17196
  }
16999
17197
  function readPendingInboundMarker(chatId, messageId) {
17000
17198
  const path = pendingInboundPath(chatId, messageId);
17001
- if (!path || !existsSync4(path)) return null;
17199
+ if (!path || !existsSync5(path)) return null;
17002
17200
  try {
17003
- return JSON.parse(readFileSync5(path, "utf-8"));
17201
+ return JSON.parse(readFileSync6(path, "utf-8"));
17004
17202
  } catch {
17005
17203
  return null;
17006
17204
  }
@@ -17020,10 +17218,10 @@ function nextRetryName(filename) {
17020
17218
  async function processRecoveryOutboxFile(filename) {
17021
17219
  if (!RECOVERY_OUTBOX_DIR) return;
17022
17220
  if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
17023
- const fullPath = join5(RECOVERY_OUTBOX_DIR, filename);
17221
+ const fullPath = join6(RECOVERY_OUTBOX_DIR, filename);
17024
17222
  let payload;
17025
17223
  try {
17026
- const raw = readFileSync5(fullPath, "utf-8");
17224
+ const raw = readFileSync6(fullPath, "utf-8");
17027
17225
  payload = JSON.parse(raw);
17028
17226
  } catch (err) {
17029
17227
  process.stderr.write(
@@ -17086,7 +17284,7 @@ async function processRecoveryOutboxFile(filename) {
17086
17284
  const next = nextRetryName(filename);
17087
17285
  if (next) {
17088
17286
  try {
17089
- renameSync4(fullPath, join5(RECOVERY_OUTBOX_DIR, next.next));
17287
+ renameSync4(fullPath, join6(RECOVERY_OUTBOX_DIR, next.next));
17090
17288
  if (next.attempt >= MAX_RECOVERY_ATTEMPTS) {
17091
17289
  process.stderr.write(
17092
17290
  `telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
@@ -17126,7 +17324,7 @@ function scanRecoveryRetries() {
17126
17324
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
17127
17325
  let mtimeMs;
17128
17326
  try {
17129
- mtimeMs = statSync(join5(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
17327
+ mtimeMs = statSync(join6(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
17130
17328
  } catch {
17131
17329
  continue;
17132
17330
  }
@@ -17156,7 +17354,7 @@ function startRecoveryOutboxWatcher() {
17156
17354
  const watcher = watch(RECOVERY_OUTBOX_DIR, (event, filename) => {
17157
17355
  if (event !== "rename" || !filename) return;
17158
17356
  if (!isFirstAttemptOutboxFile(filename)) return;
17159
- if (existsSync4(join5(RECOVERY_OUTBOX_DIR, filename))) {
17357
+ if (existsSync5(join6(RECOVERY_OUTBOX_DIR, filename))) {
17160
17358
  void processRecoveryOutboxFile(filename);
17161
17359
  }
17162
17360
  });
@@ -17177,7 +17375,7 @@ function trackPendingMessage(chatId, messageId, chatType, undeliverable = false,
17177
17375
  }
17178
17376
  function sweepTelegramStaleMarkers(thresholdMs) {
17179
17377
  if (!PENDING_INBOUND_DIR) return;
17180
- if (!existsSync4(PENDING_INBOUND_DIR)) return;
17378
+ if (!existsSync5(PENDING_INBOUND_DIR)) return;
17181
17379
  let filenames;
17182
17380
  try {
17183
17381
  filenames = readdirSync2(PENDING_INBOUND_DIR);
@@ -17193,10 +17391,10 @@ function sweepTelegramStaleMarkers(thresholdMs) {
17193
17391
  for (const filename of filenames) {
17194
17392
  if (!filename.endsWith(".json")) continue;
17195
17393
  if (filename.endsWith(".tmp")) continue;
17196
- const fullPath = join5(PENDING_INBOUND_DIR, filename);
17394
+ const fullPath = join6(PENDING_INBOUND_DIR, filename);
17197
17395
  let marker;
17198
17396
  try {
17199
- marker = JSON.parse(readFileSync5(fullPath, "utf-8"));
17397
+ marker = JSON.parse(readFileSync6(fullPath, "utf-8"));
17200
17398
  } catch (err) {
17201
17399
  process.stderr.write(
17202
17400
  `telegram-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactId(filename)}: ${err.message}
@@ -17235,14 +17433,14 @@ var orphanSweepTimer = setInterval(() => {
17235
17433
  orphanSweepTimer.unref?.();
17236
17434
  var lastGiveUpHandledAtMs = null;
17237
17435
  function listPendingInboundChatIds() {
17238
- if (!PENDING_INBOUND_DIR || !existsSync4(PENDING_INBOUND_DIR)) return [];
17436
+ if (!PENDING_INBOUND_DIR || !existsSync5(PENDING_INBOUND_DIR)) return [];
17239
17437
  const chats = /* @__PURE__ */ new Set();
17240
17438
  try {
17241
17439
  for (const name of readdirSync2(PENDING_INBOUND_DIR)) {
17242
17440
  if (!name.endsWith(".json")) continue;
17243
17441
  try {
17244
17442
  const marker = JSON.parse(
17245
- readFileSync5(join5(PENDING_INBOUND_DIR, name), "utf8")
17443
+ readFileSync6(join6(PENDING_INBOUND_DIR, name), "utf8")
17246
17444
  );
17247
17445
  if (typeof marker.chat_id === "string" && marker.chat_id) chats.add(marker.chat_id);
17248
17446
  } catch {
@@ -17283,7 +17481,7 @@ async function notifyWatchdogGiveUp(chatId) {
17283
17481
  }
17284
17482
  function checkWatchdogGiveUpNotice() {
17285
17483
  if (!AGENT_DIR) return;
17286
- const signalAtMs = readGiveUpSignalAtMs(join5(AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
17484
+ const signalAtMs = readGiveUpSignalAtMs(join6(AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
17287
17485
  const act = decideGiveUpNotice({
17288
17486
  signalAtMs,
17289
17487
  lastHandledAtMs: lastGiveUpHandledAtMs,
@@ -17361,7 +17559,7 @@ function clearPendingMessage(chatId, messageId) {
17361
17559
  clearPendingInboundMarker(chatId, messageId);
17362
17560
  return;
17363
17561
  }
17364
- if (!PENDING_INBOUND_DIR || !existsSync4(PENDING_INBOUND_DIR)) return;
17562
+ if (!PENDING_INBOUND_DIR || !existsSync5(PENDING_INBOUND_DIR)) return;
17365
17563
  const safeChatId = chatId.replace(/[^A-Za-z0-9_-]/g, "_");
17366
17564
  const prefix = `${safeChatId}__`;
17367
17565
  let filenames;
@@ -17373,7 +17571,7 @@ function clearPendingMessage(chatId, messageId) {
17373
17571
  for (const filename of filenames) {
17374
17572
  if (!filename.startsWith(prefix)) continue;
17375
17573
  if (!filename.endsWith(".json")) continue;
17376
- clearTelegramMarkerFileWithHeal(join5(PENDING_INBOUND_DIR, filename));
17574
+ clearTelegramMarkerFileWithHeal(join6(PENDING_INBOUND_DIR, filename));
17377
17575
  }
17378
17576
  }
17379
17577
  function noteThreadActivity(chatId, messageId) {
@@ -17972,7 +18170,7 @@ await mcp.connect(new StdioServerTransport());
17972
18170
  var REPLAY_SCAN_INTERVAL_MS = 6e4;
17973
18171
  async function replayPendingTelegramMarkers() {
17974
18172
  if (!channelReplayEnabled()) return;
17975
- if (!PENDING_INBOUND_DIR || !existsSync4(PENDING_INBOUND_DIR)) return;
18173
+ if (!PENDING_INBOUND_DIR || !existsSync5(PENDING_INBOUND_DIR)) return;
17976
18174
  const probe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
17977
18175
  const sessionAlive = probe.tmux === "alive" && probe.claude === "alive";
17978
18176
  if (!sessionAlive) return;
@@ -17986,10 +18184,10 @@ async function replayPendingTelegramMarkers() {
17986
18184
  const entries = [];
17987
18185
  for (const name of filenames) {
17988
18186
  if (!name.endsWith(".json") || name.endsWith(".tmp")) continue;
17989
- const fullPath = join5(PENDING_INBOUND_DIR, name);
18187
+ const fullPath = join6(PENDING_INBOUND_DIR, name);
17990
18188
  let marker;
17991
18189
  try {
17992
- marker = JSON.parse(readFileSync5(fullPath, "utf-8"));
18190
+ marker = JSON.parse(readFileSync6(fullPath, "utf-8"));
17993
18191
  } catch {
17994
18192
  continue;
17995
18193
  }
@@ -18021,7 +18219,7 @@ async function replayPendingTelegramMarkers() {
18021
18219
  continue;
18022
18220
  }
18023
18221
  try {
18024
- if (existsSync4(path)) {
18222
+ if (existsSync5(path)) {
18025
18223
  const updated = {
18026
18224
  ...marker,
18027
18225
  replay_count: (marker.replay_count ?? 0) + 1
@@ -18166,7 +18364,7 @@ async function pollLoop() {
18166
18364
  const trimmedContent = content.trim();
18167
18365
  const isFromBot = !!msg.from?.is_bot;
18168
18366
  const nowMs = Date.now();
18169
- const telegramCommand = isHelpSyntax(trimmedContent) ? "help" : isStatusSyntax(trimmedContent) ? "status" : isRestartSyntax(trimmedContent) ? "restart" : isInvestigateSyntax(trimmedContent) ? "investigate" : void 0;
18367
+ const telegramCommand = isHelpSyntax(trimmedContent) ? "help" : isStatusSyntax(trimmedContent) ? "status" : isWatchSyntax(trimmedContent) ? "watch" : isRestartSyntax(trimmedContent) ? "restart" : isInvestigateSyntax(trimmedContent) ? "investigate" : void 0;
18170
18368
  let classification;
18171
18369
  let peerLeaf;
18172
18370
  if (isFromBot) {
@@ -18246,6 +18444,19 @@ async function pollLoop() {
18246
18444
  }
18247
18445
  continue;
18248
18446
  }
18447
+ if (access.kind === "command" && access.command === "watch") {
18448
+ const disposition = await classifyRestartCommand(
18449
+ trimmedContent.replace(/^\/watch(@[A-Za-z0-9_]{1,64})?/, "/restart$1")
18450
+ );
18451
+ if (disposition === "act") {
18452
+ await handleWatchCommand({
18453
+ chatId,
18454
+ messageId: String(msg.message_id),
18455
+ text: trimmedContent
18456
+ });
18457
+ }
18458
+ continue;
18459
+ }
18249
18460
  if (access.kind === "command" && access.command === "restart") {
18250
18461
  const disposition = await classifyRestartCommand(trimmedContent);
18251
18462
  if (disposition === "act") {
@@ -18384,7 +18595,7 @@ async function pollLoop() {
18384
18595
  let paneLogFreshAgeMs = null;
18385
18596
  if (AGENT_DIR) {
18386
18597
  try {
18387
- const paneMtimeMs = statSync(join5(AGENT_DIR, "pane.log")).mtimeMs;
18598
+ const paneMtimeMs = statSync(join6(AGENT_DIR, "pane.log")).mtimeMs;
18388
18599
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
18389
18600
  } catch {
18390
18601
  }
@@ -25,8 +25,8 @@ import {
25
25
  takeAcpxExecFailureCount,
26
26
  takeZombieDetection,
27
27
  writePersistentClaudeWrapper
28
- } from "./chunk-AEECYKHW.js";
29
- import "./chunk-LK7R6HLJ.js";
28
+ } from "./chunk-5TBIEU36.js";
29
+ import "./chunk-3A2H4ZLD.js";
30
30
  import "./chunk-XWVM4KPK.js";
31
31
  export {
32
32
  SEND_KEYS_ENTER_DELAY_MS,
@@ -56,4 +56,4 @@ export {
56
56
  takeZombieDetection,
57
57
  writePersistentClaudeWrapper
58
58
  };
59
- //# sourceMappingURL=persistent-session-WL22MKBS.js.map
59
+ //# sourceMappingURL=persistent-session-7BLPRGWR.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-AEECYKHW.js";
4
- import "./chunk-LK7R6HLJ.js";
3
+ } from "./chunk-5TBIEU36.js";
4
+ import "./chunk-3A2H4ZLD.js";
5
5
  import "./chunk-XWVM4KPK.js";
6
6
 
7
7
  // src/lib/responsiveness-probe.ts
@@ -192,4 +192,4 @@ export {
192
192
  oldestLivePendingInboundMtimeMs,
193
193
  parkPendingInbound
194
194
  };
195
- //# sourceMappingURL=responsiveness-probe-N3Q3O6X7.js.map
195
+ //# sourceMappingURL=responsiveness-probe-AL3O7SYZ.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.27.162",
3
+ "version": "0.27.164",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {