@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.
@@ -14344,9 +14344,152 @@ function decideInboundAccess(input) {
14344
14344
  return { kind: "admit" };
14345
14345
  }
14346
14346
 
14347
+ // src/watch-command.ts
14348
+ var WATCH_DEFAULT_DURATION_MS = 2 * 60 * 60 * 1e3;
14349
+ var WATCH_MAX_DURATION_MS = 7 * 24 * 60 * 60 * 1e3;
14350
+ var WATCH_MIN_DURATION_MS = 5 * 60 * 1e3;
14351
+ function extractDriveFileId(s) {
14352
+ const trimmed = s.trim().replace(/^<|>$/g, "");
14353
+ const pathId = trimmed.match(/\/d\/([A-Za-z0-9_-]{10,})/);
14354
+ if (pathId?.[1]) return pathId[1];
14355
+ const idParam = trimmed.match(/[?&]id=([A-Za-z0-9_-]{10,})/);
14356
+ if (idParam?.[1]) return idParam[1];
14357
+ if (!/[/:?]/.test(trimmed) && /^[A-Za-z0-9_-]{20,}$/.test(trimmed)) return trimmed;
14358
+ return null;
14359
+ }
14360
+ function parseDurationToken(token) {
14361
+ const m = token.trim().match(/^(\d+)\s*([smhd])?$/i);
14362
+ if (!m?.[1]) return null;
14363
+ const n = Number.parseInt(m[1], 10);
14364
+ if (!Number.isFinite(n) || n <= 0) return null;
14365
+ const unit = (m[2] ?? "m").toLowerCase();
14366
+ const mult = unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
14367
+ return n * mult;
14368
+ }
14369
+ function parseWatchArgs(args) {
14370
+ const parts = args.trim().split(/\s+/).filter(Boolean);
14371
+ const first = parts[0];
14372
+ if (!first) return { ok: false, error: "usage" };
14373
+ if (parts.length > 2) return { ok: false, error: "usage" };
14374
+ const fileId = extractDriveFileId(first);
14375
+ if (!fileId) return { ok: false, error: "bad-url" };
14376
+ let durationMs = WATCH_DEFAULT_DURATION_MS;
14377
+ const durationToken = parts[1];
14378
+ if (durationToken !== void 0) {
14379
+ const parsed = parseDurationToken(durationToken);
14380
+ if (parsed === null) return { ok: false, error: "bad-duration" };
14381
+ durationMs = Math.min(Math.max(parsed, WATCH_MIN_DURATION_MS), WATCH_MAX_DURATION_MS);
14382
+ }
14383
+ return { ok: true, value: { fileId, durationMs } };
14384
+ }
14385
+ function watchArgsFromText(strippedText, codeName, opts) {
14386
+ const forms = [];
14387
+ if (opts?.allowBare ?? true) forms.push("/watch");
14388
+ if (codeName) forms.push(`/watch-${codeName}`);
14389
+ for (const f of forms) {
14390
+ if (strippedText === f) return "";
14391
+ if (strippedText.startsWith(`${f} `)) return strippedText.slice(f.length + 1).trim();
14392
+ }
14393
+ return null;
14394
+ }
14395
+ async function postWatchTrigger(opts) {
14396
+ const doFetch = opts.fetchImpl ?? fetch;
14397
+ try {
14398
+ const res = await doFetch(`${opts.host}/host/triggers`, {
14399
+ method: "POST",
14400
+ headers: {
14401
+ "Content-Type": "application/json; charset=utf-8",
14402
+ Authorization: `Bearer ${opts.apiKey}`
14403
+ },
14404
+ body: JSON.stringify({
14405
+ agent_id: opts.agentId,
14406
+ provider: "gdrive_comments",
14407
+ config: { fileId: opts.fileId },
14408
+ expires_at: opts.expiresAtIso
14409
+ }),
14410
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
14411
+ });
14412
+ if (!res.ok) {
14413
+ let msg = `HTTP ${res.status}`;
14414
+ try {
14415
+ const j2 = await res.json();
14416
+ if (j2?.error) msg = j2.error;
14417
+ } catch {
14418
+ }
14419
+ return { ok: false, status: res.status, error: msg };
14420
+ }
14421
+ const j = await res.json().catch(() => ({}));
14422
+ return { ok: true, status: res.status, reused: !!j.reused, expiresAt: j.expires_at ?? null };
14423
+ } catch (err) {
14424
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
14425
+ }
14426
+ }
14427
+ function humanDuration(ms) {
14428
+ const mins = Math.round(ms / 6e4);
14429
+ if (mins % 1440 === 0) return `${mins / 1440}d`;
14430
+ if (mins % 60 === 0) return `${mins / 60}h`;
14431
+ return `${mins}m`;
14432
+ }
14433
+ function watchUsageText() {
14434
+ 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).";
14435
+ }
14436
+ function watchBadUrlText() {
14437
+ return "That doesn't look like a Google Doc link. Paste the doc's share URL (the part with `/d/<id>`).";
14438
+ }
14439
+ function watchBadDurationText() {
14440
+ return "I couldn't read that duration. Try `30m`, `2h`, or `1d` (max 7d).";
14441
+ }
14442
+ function watchErrorText() {
14443
+ return "\u274C I couldn't set up that watch just now. Please try again in a moment.";
14444
+ }
14445
+ function watchSuccessTextSlack(fileId, durationMs, reused) {
14446
+ const url = `https://docs.google.com/document/d/${fileId}/edit`;
14447
+ const verb = reused ? "Extended my watch on" : "Watching";
14448
+ return `\u{1F440} ${verb} <${url}|that doc> 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.)`;
14449
+ }
14450
+
14347
14451
  // src/ack-reaction.ts
14348
- import { readdirSync, readFileSync } from "fs";
14452
+ import { readdirSync, readFileSync as readFileSync2 } from "fs";
14453
+ import { join as join2 } from "path";
14454
+
14455
+ // src/flags-cache-read.ts
14456
+ import { existsSync, readFileSync } from "fs";
14457
+ import { homedir } from "os";
14349
14458
  import { join } from "path";
14459
+ function defaultFlagsCachePath() {
14460
+ return join(homedir(), ".augmented", "flags-cache.json");
14461
+ }
14462
+ function envBoolean(raw) {
14463
+ if (raw === void 0) return void 0;
14464
+ const v = raw.trim().toLowerCase();
14465
+ if (v === "") return void 0;
14466
+ if (v === "1" || v === "true" || v === "yes" || v === "on") return true;
14467
+ if (v === "0" || v === "false" || v === "no" || v === "off") return false;
14468
+ return void 0;
14469
+ }
14470
+ function cachedBoolean(key2, path) {
14471
+ try {
14472
+ if (!existsSync(path)) return void 0;
14473
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
14474
+ if (!parsed || typeof parsed !== "object") return void 0;
14475
+ const flags = parsed.flags;
14476
+ if (!flags || typeof flags !== "object") return void 0;
14477
+ const value = flags[key2];
14478
+ return typeof value === "boolean" ? value : void 0;
14479
+ } catch {
14480
+ return void 0;
14481
+ }
14482
+ }
14483
+ function resolveHostBooleanFlag(opts) {
14484
+ const env = opts.env ?? process.env;
14485
+ const envValue = envBoolean(env[opts.envVar]);
14486
+ if (envValue !== void 0) return envValue;
14487
+ const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath());
14488
+ if (cached2 !== void 0) return cached2;
14489
+ return opts.defaultValue;
14490
+ }
14491
+
14492
+ // src/ack-reaction.ts
14350
14493
  var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
14351
14494
  var ACK_STARTUP_GRACE_MS = 6e4;
14352
14495
  var ACK_PANE_FRESH_THRESHOLD_MS = 6e4;
@@ -14381,8 +14524,11 @@ function undeliverableNoticeText() {
14381
14524
  var BUSY_ACK_THRESHOLD_MS = 9e4;
14382
14525
  var BUSY_ACK_NOTICE_THROTTLE_MS = 10 * 60 * 1e3;
14383
14526
  function channelBusyAckEnabled() {
14384
- const v = process.env.AGT_CHANNEL_BUSY_ACK_ENABLED;
14385
- return v === "1" || v === "true";
14527
+ return resolveHostBooleanFlag({
14528
+ key: "channel-busy-ack",
14529
+ envVar: "AGT_CHANNEL_BUSY_ACK_ENABLED",
14530
+ defaultValue: false
14531
+ });
14386
14532
  }
14387
14533
  function channelBusyAckThresholdMs() {
14388
14534
  const raw = parseInt(process.env.AGT_CHANNEL_BUSY_ACK_THRESHOLD_MS ?? "", 10);
@@ -14406,7 +14552,7 @@ var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
14406
14552
  function readGiveUpSignalAtMs(path, now = Date.now()) {
14407
14553
  if (!path) return null;
14408
14554
  try {
14409
- const raw = JSON.parse(readFileSync(path, "utf8"));
14555
+ const raw = JSON.parse(readFileSync2(path, "utf8"));
14410
14556
  if (typeof raw.gave_up_at !== "string") return null;
14411
14557
  const t = Date.parse(raw.gave_up_at);
14412
14558
  if (!Number.isFinite(t) || t > now) return null;
@@ -14438,7 +14584,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
14438
14584
  if (!name.endsWith(".json")) continue;
14439
14585
  let receivedAt;
14440
14586
  try {
14441
- const raw = JSON.parse(readFileSync(join(dir, name), "utf-8"));
14587
+ const raw = JSON.parse(readFileSync2(join2(dir, name), "utf-8"));
14442
14588
  receivedAt = raw.received_at;
14443
14589
  } catch {
14444
14590
  continue;
@@ -14521,15 +14667,15 @@ function probeAgentSessionCached(codeName, ttlMs = SESSION_PROBE_TTL_MS, now = D
14521
14667
  }
14522
14668
 
14523
14669
  // src/agent-config-state.ts
14524
- import { existsSync, readFileSync as readFileSync2 } from "fs";
14525
- import { join as join2 } from "path";
14670
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
14671
+ import { join as join3 } from "path";
14526
14672
  var SESSION_STATE_FILENAME = "session-state.json";
14527
14673
  function readAgentSessionState(stateDir) {
14528
14674
  if (!stateDir) return null;
14529
- const path = join2(stateDir, SESSION_STATE_FILENAME);
14530
- if (!existsSync(path)) return null;
14675
+ const path = join3(stateDir, SESSION_STATE_FILENAME);
14676
+ if (!existsSync2(path)) return null;
14531
14677
  try {
14532
- const parsed = JSON.parse(readFileSync2(path, "utf-8"));
14678
+ const parsed = JSON.parse(readFileSync3(path, "utf-8"));
14533
14679
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
14534
14680
  return parsed;
14535
14681
  } catch {
@@ -14601,10 +14747,10 @@ import { promisify } from "util";
14601
14747
  import { open, stat } from "fs/promises";
14602
14748
 
14603
14749
  // src/channel-attachments.ts
14604
- import { homedir } from "os";
14605
- import { join as join3, resolve, sep } from "path";
14750
+ import { homedir as homedir2 } from "os";
14751
+ import { join as join4, resolve, sep } from "path";
14606
14752
  function resolveChannelInboundDir(codeName, channelSlug) {
14607
- const base = join3(homedir(), ".augmented");
14753
+ const base = join4(homedir2(), ".augmented");
14608
14754
  const allowedSegment = /^[A-Za-z0-9_-]+$/;
14609
14755
  if (!allowedSegment.test(codeName) || !allowedSegment.test(channelSlug)) {
14610
14756
  throw new Error(
@@ -14682,7 +14828,7 @@ function isPathInside(target, root) {
14682
14828
  return normalizedTarget === resolve(root) || normalizedTarget.startsWith(normalizedRoot);
14683
14829
  }
14684
14830
  function redactAugmentedPaths(msg) {
14685
- const homePrefix = homedir().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14831
+ const homePrefix = homedir2().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14686
14832
  return msg.replaceAll(
14687
14833
  new RegExp(`${homePrefix}[\\\\/]\\.augmented(?:[\\\\/][^\\s'"\`]*)*`, "g"),
14688
14834
  "<augmented-path>"
@@ -15030,14 +15176,14 @@ var SLACK_EGRESS_TOOLS = /* @__PURE__ */ new Set([
15030
15176
  ]);
15031
15177
 
15032
15178
  // src/slack-pending-inbound-cleanup.ts
15033
- import { existsSync as existsSync2, readdirSync as readdirSync2, statSync, unlinkSync } from "fs";
15034
- import { join as join4 } from "path";
15179
+ import { existsSync as existsSync3, readdirSync as readdirSync2, statSync, unlinkSync } from "fs";
15180
+ import { join as join5 } from "path";
15035
15181
  function sanitizeMarkerSegment(value) {
15036
15182
  return value.replace(/[^A-Za-z0-9_-]/g, "_");
15037
15183
  }
15038
15184
  var defaultClearMarkerFile = (fullPath) => {
15039
15185
  try {
15040
- if (existsSync2(fullPath)) unlinkSync(fullPath);
15186
+ if (existsSync3(fullPath)) unlinkSync(fullPath);
15041
15187
  } catch {
15042
15188
  }
15043
15189
  };
@@ -15048,7 +15194,7 @@ function clearAllSlackPendingMarkersForThread(dir, channel, threadTs, clear = de
15048
15194
  try {
15049
15195
  for (const f of readdirSync2(dir)) {
15050
15196
  if (!f.startsWith(prefix) || !f.endsWith(".json")) continue;
15051
- clear(join4(dir, f));
15197
+ clear(join5(dir, f));
15052
15198
  cleared += 1;
15053
15199
  }
15054
15200
  } catch {
@@ -15063,7 +15209,7 @@ function clearSlackPendingMarkerByMessageTs(dir, channel, messageTs, clear = def
15063
15209
  try {
15064
15210
  for (const f of readdirSync2(dir)) {
15065
15211
  if (!f.startsWith(channelPrefix) || !f.endsWith(messageSuffix)) continue;
15066
- clear(join4(dir, f));
15212
+ clear(join5(dir, f));
15067
15213
  cleared += 1;
15068
15214
  }
15069
15215
  } catch {
@@ -15075,7 +15221,7 @@ function clearOldestSlackPendingMarkerInChannel(dir, channel, clear = defaultCle
15075
15221
  const channelPrefix = `${sanitizeMarkerSegment(channel)}__`;
15076
15222
  try {
15077
15223
  const entries = readdirSync2(dir).filter((f) => f.startsWith(channelPrefix) && f.endsWith(".json")).map((f) => {
15078
- const full = join4(dir, f);
15224
+ const full = join5(dir, f);
15079
15225
  let mtime = 0;
15080
15226
  try {
15081
15227
  mtime = statSync(full).mtimeMs;
@@ -15103,7 +15249,7 @@ function resolveReplyThreadTs(input) {
15103
15249
  }
15104
15250
 
15105
15251
  // src/restart-confirm.ts
15106
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync as unlinkSync2, writeFileSync } from "fs";
15252
+ import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, renameSync, unlinkSync as unlinkSync2, writeFileSync } from "fs";
15107
15253
  import { dirname } from "path";
15108
15254
  import { randomUUID } from "crypto";
15109
15255
  var RESTART_CONFIRM_MAX_AGE_MS = 10 * 60 * 1e3;
@@ -15121,15 +15267,15 @@ function buildBackOnlineText(name) {
15121
15267
  }
15122
15268
  function writeRestartConfirmMarker(filePath, marker) {
15123
15269
  const dir = dirname(filePath);
15124
- if (!existsSync3(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
15270
+ if (!existsSync4(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
15125
15271
  const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
15126
15272
  writeFileSync(tmpPath, JSON.stringify(marker) + "\n", { encoding: "utf8", mode: 384 });
15127
15273
  renameSync(tmpPath, filePath);
15128
15274
  }
15129
15275
  function readRestartConfirmMarker(filePath) {
15130
15276
  try {
15131
- if (!existsSync3(filePath)) return null;
15132
- const parsed = JSON.parse(readFileSync3(filePath, "utf8"));
15277
+ if (!existsSync4(filePath)) return null;
15278
+ const parsed = JSON.parse(readFileSync4(filePath, "utf8"));
15133
15279
  if (!parsed || typeof parsed !== "object") return null;
15134
15280
  return parsed;
15135
15281
  } catch {
@@ -15138,7 +15284,7 @@ function readRestartConfirmMarker(filePath) {
15138
15284
  }
15139
15285
  function clearRestartConfirmMarker(filePath) {
15140
15286
  try {
15141
- if (existsSync3(filePath)) unlinkSync2(filePath);
15287
+ if (existsSync4(filePath)) unlinkSync2(filePath);
15142
15288
  } catch {
15143
15289
  }
15144
15290
  }
@@ -15239,9 +15385,9 @@ var StdioServerTransport = class {
15239
15385
  import {
15240
15386
  chmodSync,
15241
15387
  createWriteStream,
15242
- existsSync as existsSync6,
15388
+ existsSync as existsSync7,
15243
15389
  mkdirSync as mkdirSync5,
15244
- readFileSync as readFileSync7,
15390
+ readFileSync as readFileSync8,
15245
15391
  readdirSync as readdirSync3,
15246
15392
  renameSync as renameSync3,
15247
15393
  statSync as statSync2,
@@ -15249,12 +15395,12 @@ import {
15249
15395
  watch,
15250
15396
  writeFileSync as writeFileSync5
15251
15397
  } from "fs";
15252
- import { basename, join as join6, resolve as resolve2 } from "path";
15253
- import { homedir as homedir2 } from "os";
15398
+ import { basename, join as join7, resolve as resolve2 } from "path";
15399
+ import { homedir as homedir3 } from "os";
15254
15400
  import { createHash, randomUUID as randomUUID2 } from "crypto";
15255
15401
 
15256
15402
  // src/slack-thread-store.ts
15257
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
15403
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
15258
15404
  import { dirname as dirname2 } from "path";
15259
15405
  var FILE_VERSION = 1;
15260
15406
  var DEFAULT_TTL_DAYS = 30;
@@ -15265,7 +15411,7 @@ function loadThreadStore(filePath, opts = {}) {
15265
15411
  const ttlMs = ttlDays * 24 * 60 * 60 * 1e3;
15266
15412
  let raw;
15267
15413
  try {
15268
- raw = readFileSync4(filePath, "utf-8");
15414
+ raw = readFileSync5(filePath, "utf-8");
15269
15415
  } catch {
15270
15416
  return { threads: /* @__PURE__ */ new Map(), pruned: 0 };
15271
15417
  }
@@ -15379,7 +15525,7 @@ async function runOrRetry(fn, opts) {
15379
15525
  }
15380
15526
 
15381
15527
  // src/slack-bot-photo.ts
15382
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
15528
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
15383
15529
  import { dirname as dirname3 } from "path";
15384
15530
  async function applyBotPhoto(opts) {
15385
15531
  const fetchImpl = opts.fetchImpl ?? fetch;
@@ -15387,9 +15533,9 @@ async function applyBotPhoto(opts) {
15387
15533
  process.stderr.write(m);
15388
15534
  });
15389
15535
  const { token, avatarUrl, markerPath } = opts;
15390
- if (markerPath && existsSync4(markerPath)) {
15536
+ if (markerPath && existsSync5(markerPath)) {
15391
15537
  try {
15392
- if (readFileSync5(markerPath, "utf-8").trim() === avatarUrl) {
15538
+ if (readFileSync6(markerPath, "utf-8").trim() === avatarUrl) {
15393
15539
  return { status: "skipped-unchanged" };
15394
15540
  }
15395
15541
  } catch {
@@ -16182,14 +16328,14 @@ function createSlackBotUserIdClient(args) {
16182
16328
 
16183
16329
  // src/mcp-spawn-lock.ts
16184
16330
  import {
16185
- existsSync as existsSync5,
16331
+ existsSync as existsSync6,
16186
16332
  mkdirSync as mkdirSync4,
16187
- readFileSync as readFileSync6,
16333
+ readFileSync as readFileSync7,
16188
16334
  renameSync as renameSync2,
16189
16335
  unlinkSync as unlinkSync3,
16190
16336
  writeFileSync as writeFileSync4
16191
16337
  } from "fs";
16192
- import { join as join5 } from "path";
16338
+ import { join as join6 } from "path";
16193
16339
  function defaultIsPidAlive(pid) {
16194
16340
  if (!Number.isFinite(pid) || pid <= 0) return false;
16195
16341
  try {
@@ -16207,7 +16353,7 @@ function acquireMcpSpawnLock(args) {
16207
16353
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
16208
16354
  const selfPid = options.selfPid ?? process.pid;
16209
16355
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
16210
- const path = join5(agentDir, basename2);
16356
+ const path = join6(agentDir, basename2);
16211
16357
  const existing = readLockHolder(path);
16212
16358
  if (existing) {
16213
16359
  if (existing.pid === selfPid) {
@@ -16236,9 +16382,9 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
16236
16382
  }
16237
16383
  }
16238
16384
  function readLockHolder(path) {
16239
- if (!existsSync5(path)) return null;
16385
+ if (!existsSync6(path)) return null;
16240
16386
  try {
16241
- const raw = readFileSync6(path, "utf8");
16387
+ const raw = readFileSync7(path, "utf8");
16242
16388
  const parsed = JSON.parse(raw);
16243
16389
  const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
16244
16390
  if (!Number.isFinite(pid) || pid <= 0) return null;
@@ -16414,8 +16560,8 @@ var SLACK_PEER_CLASSIFIER_CONFIG = {
16414
16560
  peers: parsePeersEnv(process.env.SLACK_PEERS, process.env.SLACK_PEERS_GATE),
16415
16561
  peer_disabled_mode: SLACK_PEER_DISABLED_MODE
16416
16562
  };
16417
- var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join6(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
16418
- var SLACK_MCP_CONFIG_PATH = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "project", ".mcp.json") : null;
16563
+ var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join7(homedir3(), ".augmented", AGENT_CODE_NAME) : null;
16564
+ var SLACK_MCP_CONFIG_PATH = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "project", ".mcp.json") : null;
16419
16565
  var liveAllowedUsersCache = null;
16420
16566
  function readLiveAllowedUsers() {
16421
16567
  if (!SLACK_MCP_CONFIG_PATH) return null;
@@ -16425,7 +16571,7 @@ function readLiveAllowedUsers() {
16425
16571
  return liveAllowedUsersCache.value;
16426
16572
  }
16427
16573
  const value = extractAllowedUsersFromMcpJson(
16428
- readFileSync7(SLACK_MCP_CONFIG_PATH, "utf-8")
16574
+ readFileSync8(SLACK_MCP_CONFIG_PATH, "utf-8")
16429
16575
  );
16430
16576
  if (value === null) return null;
16431
16577
  liveAllowedUsersCache = { mtimeMs, value };
@@ -16437,11 +16583,11 @@ function readLiveAllowedUsers() {
16437
16583
  function getEffectiveAllowedUsers() {
16438
16584
  return readLiveAllowedUsers() ?? ALLOWED_USERS;
16439
16585
  }
16440
- var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
16441
- var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
16442
- var SLACK_RESTART_CONFIRM_FILE = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "slack-restart-confirm.json") : null;
16586
+ var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
16587
+ var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
16588
+ var SLACK_RESTART_CONFIRM_FILE = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-restart-confirm.json") : null;
16443
16589
  var SLACK_MAX_RECOVERY_ATTEMPTS = 3;
16444
- var SLACK_AVATAR_MARKER_PATH = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "slack-avatar-applied") : null;
16590
+ var SLACK_AVATAR_MARKER_PATH = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-avatar-applied") : null;
16445
16591
  function redactSlackId(id) {
16446
16592
  if (!id) return "<none>";
16447
16593
  return createHash("sha256").update(id).digest("hex").slice(0, 8);
@@ -16452,7 +16598,7 @@ function safeSlackMarkerName(channel, threadTs, messageTs) {
16452
16598
  }
16453
16599
  function slackPendingInboundPath(channel, threadTs, messageTs) {
16454
16600
  if (!SLACK_PENDING_INBOUND_DIR) return null;
16455
- return join6(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
16601
+ return join7(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
16456
16602
  }
16457
16603
  function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undeliverable = false) {
16458
16604
  const path = slackPendingInboundPath(channel, threadTs, messageTs);
@@ -16477,9 +16623,9 @@ function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undelivera
16477
16623
  }
16478
16624
  function readSlackPendingInboundMarker(channel, threadTs, messageTs) {
16479
16625
  const path = slackPendingInboundPath(channel, threadTs, messageTs);
16480
- if (!path || !existsSync6(path)) return null;
16626
+ if (!path || !existsSync7(path)) return null;
16481
16627
  try {
16482
- return JSON.parse(readFileSync7(path, "utf-8"));
16628
+ return JSON.parse(readFileSync8(path, "utf-8"));
16483
16629
  } catch {
16484
16630
  return null;
16485
16631
  }
@@ -16573,7 +16719,7 @@ function scheduleBusyAck(channel, threadTs, messageTs, isThreadReply) {
16573
16719
  let paneLogFreshAgeMs = null;
16574
16720
  if (SLACK_AGENT_DIR) {
16575
16721
  try {
16576
- const paneMtimeMs = statSync2(join6(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
16722
+ const paneMtimeMs = statSync2(join7(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
16577
16723
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
16578
16724
  } catch {
16579
16725
  }
@@ -16598,7 +16744,7 @@ function __resetSlackBusyAckNoticeThrottle() {
16598
16744
  function clearSlackMarkerFileWithHeal(fullPath) {
16599
16745
  let marker = null;
16600
16746
  try {
16601
- marker = JSON.parse(readFileSync7(fullPath, "utf-8"));
16747
+ marker = JSON.parse(readFileSync8(fullPath, "utf-8"));
16602
16748
  } catch {
16603
16749
  }
16604
16750
  if (marker && decideRecoveryHeal({
@@ -16608,7 +16754,7 @@ function clearSlackMarkerFileWithHeal(fullPath) {
16608
16754
  healSlackUndeliverable(marker.channel, marker.message_ts);
16609
16755
  }
16610
16756
  try {
16611
- if (existsSync6(fullPath)) unlinkSync4(fullPath);
16757
+ if (existsSync7(fullPath)) unlinkSync4(fullPath);
16612
16758
  } catch {
16613
16759
  }
16614
16760
  }
@@ -16649,10 +16795,10 @@ function slackNextRetryName(filename) {
16649
16795
  async function processSlackRecoveryOutboxFile(filename) {
16650
16796
  if (!SLACK_RECOVERY_OUTBOX_DIR) return;
16651
16797
  if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
16652
- const fullPath = join6(SLACK_RECOVERY_OUTBOX_DIR, filename);
16798
+ const fullPath = join7(SLACK_RECOVERY_OUTBOX_DIR, filename);
16653
16799
  let payload;
16654
16800
  try {
16655
- payload = JSON.parse(readFileSync7(fullPath, "utf-8"));
16801
+ payload = JSON.parse(readFileSync8(fullPath, "utf-8"));
16656
16802
  } catch (err) {
16657
16803
  process.stderr.write(
16658
16804
  `slack-channel(${AGENT_CODE_NAME}): recovery outbox parse failed (${filename}): ${err.message}
@@ -16726,7 +16872,7 @@ async function processSlackRecoveryOutboxFile(filename) {
16726
16872
  const next = slackNextRetryName(filename);
16727
16873
  if (next) {
16728
16874
  try {
16729
- renameSync3(fullPath, join6(SLACK_RECOVERY_OUTBOX_DIR, next.next));
16875
+ renameSync3(fullPath, join7(SLACK_RECOVERY_OUTBOX_DIR, next.next));
16730
16876
  if (next.attempt >= SLACK_MAX_RECOVERY_ATTEMPTS) {
16731
16877
  process.stderr.write(
16732
16878
  `slack-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
@@ -16765,7 +16911,7 @@ function scanSlackRecoveryRetries() {
16765
16911
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
16766
16912
  let mtimeMs;
16767
16913
  try {
16768
- mtimeMs = statSync2(join6(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
16914
+ mtimeMs = statSync2(join7(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
16769
16915
  } catch {
16770
16916
  continue;
16771
16917
  }
@@ -16795,7 +16941,7 @@ function startSlackRecoveryOutboxWatcher() {
16795
16941
  const watcher = watch(SLACK_RECOVERY_OUTBOX_DIR, (event, filename) => {
16796
16942
  if (event !== "rename" || !filename) return;
16797
16943
  if (!isFirstAttemptSlackOutboxFile(filename)) return;
16798
- if (existsSync6(join6(SLACK_RECOVERY_OUTBOX_DIR, filename))) {
16944
+ if (existsSync7(join7(SLACK_RECOVERY_OUTBOX_DIR, filename))) {
16799
16945
  void processSlackRecoveryOutboxFile(filename);
16800
16946
  }
16801
16947
  });
@@ -16816,7 +16962,7 @@ function trackPendingMessage(channel, threadTs, messageTs, undeliverable = false
16816
16962
  }
16817
16963
  function sweepSlackStaleMarkers(thresholdMs) {
16818
16964
  if (!SLACK_PENDING_INBOUND_DIR) return;
16819
- if (!existsSync6(SLACK_PENDING_INBOUND_DIR)) return;
16965
+ if (!existsSync7(SLACK_PENDING_INBOUND_DIR)) return;
16820
16966
  let filenames;
16821
16967
  try {
16822
16968
  filenames = readdirSync3(SLACK_PENDING_INBOUND_DIR);
@@ -16832,10 +16978,10 @@ function sweepSlackStaleMarkers(thresholdMs) {
16832
16978
  for (const filename of filenames) {
16833
16979
  if (!filename.endsWith(".json")) continue;
16834
16980
  if (filename.endsWith(".tmp")) continue;
16835
- const fullPath = join6(SLACK_PENDING_INBOUND_DIR, filename);
16981
+ const fullPath = join7(SLACK_PENDING_INBOUND_DIR, filename);
16836
16982
  let marker;
16837
16983
  try {
16838
- marker = JSON.parse(readFileSync7(fullPath, "utf-8"));
16984
+ marker = JSON.parse(readFileSync8(fullPath, "utf-8"));
16839
16985
  } catch (err) {
16840
16986
  process.stderr.write(
16841
16987
  `slack-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactSlackId(filename)}: ${err.message}
@@ -16874,14 +17020,14 @@ var slackOrphanSweepTimer = setInterval(() => {
16874
17020
  slackOrphanSweepTimer.unref?.();
16875
17021
  var lastSlackGiveUpHandledAtMs = null;
16876
17022
  function listPendingSlackConversations() {
16877
- if (!SLACK_PENDING_INBOUND_DIR || !existsSync6(SLACK_PENDING_INBOUND_DIR)) return [];
17023
+ if (!SLACK_PENDING_INBOUND_DIR || !existsSync7(SLACK_PENDING_INBOUND_DIR)) return [];
16878
17024
  const byKey = /* @__PURE__ */ new Map();
16879
17025
  try {
16880
17026
  for (const name of readdirSync3(SLACK_PENDING_INBOUND_DIR)) {
16881
17027
  if (!name.endsWith(".json")) continue;
16882
17028
  try {
16883
17029
  const marker = JSON.parse(
16884
- readFileSync7(join6(SLACK_PENDING_INBOUND_DIR, name), "utf8")
17030
+ readFileSync8(join7(SLACK_PENDING_INBOUND_DIR, name), "utf8")
16885
17031
  );
16886
17032
  if (typeof marker.channel !== "string" || !marker.channel) continue;
16887
17033
  if (typeof marker.thread_ts !== "string" || !marker.thread_ts) continue;
@@ -16931,7 +17077,7 @@ function postSlackWatchdogGiveUpNotice(channel, threadTs, isThreadReply) {
16931
17077
  }
16932
17078
  function checkSlackWatchdogGiveUpNotice() {
16933
17079
  if (!SLACK_AGENT_DIR) return;
16934
- const signalAtMs = readGiveUpSignalAtMs(join6(SLACK_AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
17080
+ const signalAtMs = readGiveUpSignalAtMs(join7(SLACK_AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
16935
17081
  const act = decideGiveUpNotice({
16936
17082
  signalAtMs,
16937
17083
  lastHandledAtMs: lastSlackGiveUpHandledAtMs,
@@ -16958,7 +17104,7 @@ async function notifyStrandedInboundsOnFirstConnect() {
16958
17104
  strandedInboundNoticeInFlight = true;
16959
17105
  let hadFailure = false;
16960
17106
  try {
16961
- if (!SLACK_PENDING_INBOUND_DIR || !existsSync6(SLACK_PENDING_INBOUND_DIR)) return;
17107
+ if (!SLACK_PENDING_INBOUND_DIR || !existsSync7(SLACK_PENDING_INBOUND_DIR)) return;
16962
17108
  let filenames;
16963
17109
  try {
16964
17110
  filenames = readdirSync3(SLACK_PENDING_INBOUND_DIR);
@@ -16971,10 +17117,10 @@ async function notifyStrandedInboundsOnFirstConnect() {
16971
17117
  let notified = 0;
16972
17118
  for (const filename of filenames) {
16973
17119
  if (!filename.endsWith(".json")) continue;
16974
- const fullPath = join6(SLACK_PENDING_INBOUND_DIR, filename);
17120
+ const fullPath = join7(SLACK_PENDING_INBOUND_DIR, filename);
16975
17121
  let marker;
16976
17122
  try {
16977
- marker = JSON.parse(readFileSync7(fullPath, "utf-8"));
17123
+ marker = JSON.parse(readFileSync8(fullPath, "utf-8"));
16978
17124
  } catch {
16979
17125
  continue;
16980
17126
  }
@@ -17101,7 +17247,7 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
17101
17247
  if (!channel || !messageTs) return;
17102
17248
  clearPendingMessage(channel, messageTs);
17103
17249
  if (!SLACK_PENDING_INBOUND_DIR) return;
17104
- if (!existsSync6(SLACK_PENDING_INBOUND_DIR)) return;
17250
+ if (!existsSync7(SLACK_PENDING_INBOUND_DIR)) return;
17105
17251
  let filenames;
17106
17252
  try {
17107
17253
  filenames = readdirSync3(SLACK_PENDING_INBOUND_DIR);
@@ -17115,10 +17261,10 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
17115
17261
  for (const filename of filenames) {
17116
17262
  if (!filename.startsWith(channelPrefix)) continue;
17117
17263
  if (!filename.endsWith(messageSuffix)) continue;
17118
- clearSlackMarkerFileWithHeal(join6(SLACK_PENDING_INBOUND_DIR, filename));
17264
+ clearSlackMarkerFileWithHeal(join7(SLACK_PENDING_INBOUND_DIR, filename));
17119
17265
  }
17120
17266
  }
17121
- var RESTART_FLAGS_DIR = join6(homedir2(), ".augmented", "restart-flags");
17267
+ var RESTART_FLAGS_DIR = join7(homedir3(), ".augmented", "restart-flags");
17122
17268
  function buildAugmentedSlackMetadata() {
17123
17269
  if (!AGT_TEAM_ID) return void 0;
17124
17270
  return {
@@ -17172,6 +17318,7 @@ function buildSlackHelpMessage(codeName) {
17172
17318
  `\u2022 \`${agentSlashCommand("/help")}\` (or type \`/help\`) \u2014 show this help`,
17173
17319
  `\u2022 \`${agentSlashCommand("/restart")}\` \u2014 restart this agent`,
17174
17320
  `\u2022 \`${agentSlashCommand("/status")}\` \u2014 this agent's model, session origin, uptime + connectivity`,
17321
+ "\u2022 `/watch <google-doc-url> [duration]` (type it in chat) \u2014 watch a Google Doc for comments that mention me (default 2h, max 7d; auto-pauses when the window ends). In a shared channel, address me as `/watch-<my-name>`.",
17175
17322
  "\u2022 `/kill` \u2014 silence all agents in this thread for 6h (use as a thread reply)",
17176
17323
  "\u2022 `/unkill` \u2014 clear a kill (use as a thread reply)",
17177
17324
  `\u2022 \`${agentSlashCommand("/investigate")}\` \u2014 live tail of this agent's terminal pane (DM only, allowlisted users; works while the channel process is alive \u2014 a wedged host still needs SSM diagnostics)`
@@ -17504,10 +17651,10 @@ async function handleSlashCommandEnvelope(payload) {
17504
17651
  return;
17505
17652
  }
17506
17653
  try {
17507
- if (!existsSync6(RESTART_FLAGS_DIR)) {
17654
+ if (!existsSync7(RESTART_FLAGS_DIR)) {
17508
17655
  mkdirSync5(RESTART_FLAGS_DIR, { recursive: true });
17509
17656
  }
17510
- const flagPath = join6(RESTART_FLAGS_DIR, `${codeName}.flag`);
17657
+ const flagPath = join7(RESTART_FLAGS_DIR, `${codeName}.flag`);
17511
17658
  writeSlackRestartConfirm(
17512
17659
  {
17513
17660
  channel: payload.channel_id,
@@ -17622,10 +17769,10 @@ async function handleHelpCommand(opts) {
17622
17769
  async function handleRestartCommand(opts) {
17623
17770
  const codeName = AGENT_CODE_NAME ?? "unknown";
17624
17771
  try {
17625
- if (!existsSync6(RESTART_FLAGS_DIR)) {
17772
+ if (!existsSync7(RESTART_FLAGS_DIR)) {
17626
17773
  mkdirSync5(RESTART_FLAGS_DIR, { recursive: true });
17627
17774
  }
17628
- const flagPath = join6(RESTART_FLAGS_DIR, `${codeName}.flag`);
17775
+ const flagPath = join7(RESTART_FLAGS_DIR, `${codeName}.flag`);
17629
17776
  writeSlackRestartConfirm(
17630
17777
  {
17631
17778
  channel: opts.channel,
@@ -17685,13 +17832,68 @@ async function denyUnauthorizedRestart(opts) {
17685
17832
  ...opts.threadTs ? { thread_ts: opts.threadTs } : {}
17686
17833
  });
17687
17834
  }
17835
+ async function handleWatchCommand(opts) {
17836
+ const codeName = AGENT_CODE_NAME ?? "unknown";
17837
+ const replyThread = opts.threadTs ?? opts.ts;
17838
+ const post = (text) => postSlackMessage({
17839
+ channel: opts.channel,
17840
+ text,
17841
+ ...replyThread ? { thread_ts: replyThread } : {}
17842
+ });
17843
+ const parsed = parseWatchArgs(watchArgsFromText(opts.rawText, AGENT_CODE_NAME) ?? "");
17844
+ if (!parsed.ok) {
17845
+ await post(
17846
+ parsed.error === "bad-url" ? watchBadUrlText() : parsed.error === "bad-duration" ? watchBadDurationText() : watchUsageText()
17847
+ );
17848
+ return;
17849
+ }
17850
+ if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) {
17851
+ process.stderr.write(`slack-channel(${codeName}): /watch missing AGT_* env \u2014 cannot create watch
17852
+ `);
17853
+ await post(watchErrorText());
17854
+ return;
17855
+ }
17856
+ const expiresAtIso = new Date(Date.now() + parsed.value.durationMs).toISOString();
17857
+ const res = await postWatchTrigger({
17858
+ host: AGT_HOST,
17859
+ apiKey: AGT_API_KEY,
17860
+ agentId: AGT_AGENT_ID,
17861
+ fileId: parsed.value.fileId,
17862
+ expiresAtIso
17863
+ });
17864
+ if (!res.ok) {
17865
+ process.stderr.write(
17866
+ `slack-channel(${codeName}): /watch create failed (status=${res.status ?? "?"}): ${res.error ?? "unknown"}
17867
+ `
17868
+ );
17869
+ await post(watchErrorText());
17870
+ return;
17871
+ }
17872
+ process.stderr.write(
17873
+ `slack-channel(${codeName}): /watch ${res.reused ? "extended" : "created"} for doc ${parsed.value.fileId.slice(0, 8)}\u2026 channel ${hashChannelId(opts.channel)}
17874
+ `
17875
+ );
17876
+ await post(watchSuccessTextSlack(parsed.value.fileId, parsed.value.durationMs, !!res.reused));
17877
+ }
17878
+ async function denyUnauthorizedWatch(opts) {
17879
+ const codeName = AGENT_CODE_NAME ?? "unknown";
17880
+ process.stderr.write(
17881
+ `slack-channel(${codeName}): /watch denied \u2014 sender not in SLACK_ALLOWED_USERS, channel ${hashChannelId(opts.channel)}
17882
+ `
17883
+ );
17884
+ await postSlackMessage({
17885
+ channel: opts.channel,
17886
+ text: `\u{1F6AB} \`/watch\` denied \u2014 your Slack user is not in the allowlist for \`${codeName}\`.`,
17887
+ ...opts.threadTs ? { thread_ts: opts.threadTs } : {}
17888
+ });
17889
+ }
17688
17890
  var trackedThreads = /* @__PURE__ */ new Map();
17689
17891
  var THREAD_STORE_PATH = resolveThreadStorePath();
17690
17892
  var THREAD_STORE_TTL_DAYS = parseTtlDays(process.env.SLACK_THREAD_FOLLOW_TTL_DAYS);
17691
17893
  var threadPersister = null;
17692
17894
  function resolveThreadStorePath() {
17693
17895
  if (!AGENT_CODE_NAME) return null;
17694
- return join6(homedir2(), ".augmented", AGENT_CODE_NAME, "slack-tracked-threads.json");
17896
+ return join7(homedir3(), ".augmented", AGENT_CODE_NAME, "slack-tracked-threads.json");
17695
17897
  }
17696
17898
  function parseTtlDays(raw) {
17697
17899
  if (!raw) return void 0;
@@ -17726,9 +17928,9 @@ if (!BOT_TOKEN || !APP_TOKEN) {
17726
17928
  var slackStderrLogStream = null;
17727
17929
  if (AGENT_CODE_NAME) {
17728
17930
  try {
17729
- const logDir = join6(homedir2(), ".augmented", AGENT_CODE_NAME);
17931
+ const logDir = join7(homedir3(), ".augmented", AGENT_CODE_NAME);
17730
17932
  mkdirSync5(logDir, { recursive: true });
17731
- slackStderrLogStream = createWriteStream(join6(logDir, "slack-channel-stderr.log"), {
17933
+ slackStderrLogStream = createWriteStream(join7(logDir, "slack-channel-stderr.log"), {
17732
17934
  flags: "a",
17733
17935
  mode: 384
17734
17936
  });
@@ -18285,7 +18487,7 @@ ${result.formatted}` : "Thread is empty or not found."
18285
18487
  isError: true
18286
18488
  };
18287
18489
  }
18288
- const allowedRoot = resolve2(homedir2(), ".augmented", AGENT_CODE_NAME, "project") + "/";
18490
+ const allowedRoot = resolve2(homedir3(), ".augmented", AGENT_CODE_NAME, "project") + "/";
18289
18491
  const resolvedPath = resolve2(path);
18290
18492
  if (!resolvedPath.startsWith(allowedRoot)) {
18291
18493
  return {
@@ -18307,7 +18509,7 @@ ${result.formatted}` : "Thread is empty or not found."
18307
18509
  };
18308
18510
  }
18309
18511
  size = stat2.size;
18310
- bytes = readFileSync7(resolvedPath);
18512
+ bytes = readFileSync8(resolvedPath);
18311
18513
  } catch (err) {
18312
18514
  return {
18313
18515
  content: [{ type: "text", text: `Failed to read file: ${err.message}` }],
@@ -18894,7 +19096,7 @@ function isDownloadableFileId(fileId, channel) {
18894
19096
  }
18895
19097
  function redactAugmentedPaths2(msg) {
18896
19098
  return msg.replaceAll(
18897
- new RegExp(`${homedir2().replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")}/\\.augmented/[^\\s'"\`]*`, "g"),
19099
+ new RegExp(`${homedir3().replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")}/\\.augmented/[^\\s'"\`]*`, "g"),
18898
19100
  "<augmented-path>"
18899
19101
  );
18900
19102
  }
@@ -19146,7 +19348,9 @@ async function connectSocketMode() {
19146
19348
  const helpSuffixed = agentSlashCommand("/help");
19147
19349
  const isRestartCommand = strippedText === "/restart" || strippedText.startsWith("/restart ") || strippedText === restartSuffixed || strippedText.startsWith(`${restartSuffixed} `);
19148
19350
  const isHelpCommand = strippedText === "/help" || strippedText.startsWith("/help ") || strippedText === helpSuffixed || strippedText.startsWith(`${helpSuffixed} `);
19149
- const command = isBot ? void 0 : isHelpCommand ? { command: "help", authorized: true } : isRestartCommand ? { command: "restart", authorized: isRestartSenderAllowed(getEffectiveAllowedUsers(), evt.user) } : void 0;
19351
+ const isWatchDm = evt.channel?.startsWith("D") ?? false;
19352
+ const isWatchCommand = !isBot && watchArgsFromText(strippedText, AGENT_CODE_NAME, { allowBare: isWatchDm }) !== null;
19353
+ const command = isBot ? void 0 : isHelpCommand ? { command: "help", authorized: true } : isWatchCommand ? { command: "watch", authorized: isRestartSenderAllowed(getEffectiveAllowedUsers(), evt.user) } : isRestartCommand ? { command: "restart", authorized: isRestartSenderAllowed(getEffectiveAllowedUsers(), evt.user) } : void 0;
19150
19354
  const slackHomeTeamId = process.env.SLACK_HOME_TEAM_ID;
19151
19355
  const sameOrg = !!slackHomeTeamId && evt.team === slackHomeTeamId;
19152
19356
  const access = decideInboundAccess({
@@ -19205,6 +19409,22 @@ async function connectSocketMode() {
19205
19409
  });
19206
19410
  return;
19207
19411
  }
19412
+ if (access.command === "watch") {
19413
+ if (!access.authorized) {
19414
+ await denyUnauthorizedWatch({
19415
+ channel: evt.channel ?? "",
19416
+ threadTs: evt.thread_ts
19417
+ });
19418
+ return;
19419
+ }
19420
+ await handleWatchCommand({
19421
+ channel: evt.channel ?? "",
19422
+ threadTs: evt.thread_ts,
19423
+ ts: evt.ts ?? "",
19424
+ rawText: strippedText
19425
+ });
19426
+ return;
19427
+ }
19208
19428
  if (access.command === "restart") {
19209
19429
  if (!access.authorized) {
19210
19430
  await denyUnauthorizedRestart({
@@ -19286,7 +19506,7 @@ async function connectSocketMode() {
19286
19506
  let paneLogFreshAgeMs = null;
19287
19507
  if (SLACK_AGENT_DIR) {
19288
19508
  try {
19289
- const paneMtimeMs = statSync2(join6(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
19509
+ const paneMtimeMs = statSync2(join7(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
19290
19510
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
19291
19511
  } catch {
19292
19512
  }