@integrity-labs/agt-cli 0.27.9-test.12 → 0.27.9-test.14

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.
Files changed (35) hide show
  1. package/dist/bin/agt.js +4 -4
  2. package/dist/chunk-354FAVQR.js +173 -0
  3. package/dist/chunk-354FAVQR.js.map +1 -0
  4. package/dist/{chunk-MQJ5DMPT.js → chunk-7GKJZBTB.js} +111 -204
  5. package/dist/chunk-7GKJZBTB.js.map +1 -0
  6. package/dist/{chunk-HR5T2RQF.js → chunk-I3YS5WFV.js} +3 -1
  7. package/dist/chunk-I3YS5WFV.js.map +1 -0
  8. package/dist/{chunk-SXBZDUKN.js → chunk-R7NKCGWN.js} +341 -46
  9. package/dist/chunk-R7NKCGWN.js.map +1 -0
  10. package/dist/{chunk-I2OTQJH3.js → chunk-WOOYOAPG.js} +2644 -2142
  11. package/dist/chunk-WOOYOAPG.js.map +1 -0
  12. package/dist/{claude-pair-runtime-F6USL3EW.js → claude-pair-runtime-GIUCD7IG.js} +2 -2
  13. package/dist/{claude-scheduler-EM24LTGV.js → claude-scheduler-FATCLHDM.js} +2 -2
  14. package/dist/daily-session-PNQX5URX.js +27 -0
  15. package/dist/lib/manager-worker.js +1125 -149
  16. package/dist/lib/manager-worker.js.map +1 -1
  17. package/dist/mcp/augmented-admin.js +21335 -0
  18. package/dist/mcp/index.js +121 -4
  19. package/dist/mcp/slack-channel.js +845 -208
  20. package/dist/mcp/teams-channel.js +73 -29
  21. package/dist/mcp/telegram-channel.js +579 -157
  22. package/dist/{persistent-session-OL5EDYB4.js → persistent-session-35PWSTLO.js} +10 -3
  23. package/dist/persistent-session-35PWSTLO.js.map +1 -0
  24. package/dist/responsiveness-probe-MA4M2QM4.js +158 -0
  25. package/dist/responsiveness-probe-MA4M2QM4.js.map +1 -0
  26. package/package.json +3 -2
  27. package/dist/chunk-HR5T2RQF.js.map +0 -1
  28. package/dist/chunk-I2OTQJH3.js.map +0 -1
  29. package/dist/chunk-MQJ5DMPT.js.map +0 -1
  30. package/dist/chunk-SXBZDUKN.js.map +0 -1
  31. package/dist/responsiveness-probe-B6LJJRUD.js +0 -74
  32. package/dist/responsiveness-probe-B6LJJRUD.js.map +0 -1
  33. /package/dist/{claude-pair-runtime-F6USL3EW.js.map → claude-pair-runtime-GIUCD7IG.js.map} +0 -0
  34. /package/dist/{claude-scheduler-EM24LTGV.js.map → claude-scheduler-FATCLHDM.js.map} +0 -0
  35. /package/dist/{persistent-session-OL5EDYB4.js.map → daily-session-PNQX5URX.js.map} +0 -0
@@ -14180,6 +14180,47 @@ var Server = class extends Protocol {
14180
14180
  }
14181
14181
  };
14182
14182
 
14183
+ // src/restart-confirm.ts
14184
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
14185
+ import { dirname } from "path";
14186
+ import { randomUUID } from "crypto";
14187
+ var RESTART_CONFIRM_MAX_AGE_MS = 10 * 60 * 1e3;
14188
+ function shouldPostRestartConfirm(marker, processBootMs, nowMs) {
14189
+ if (!marker) return false;
14190
+ const requestedAt = marker.requested_at;
14191
+ if (typeof requestedAt !== "number" || !Number.isFinite(requestedAt)) return false;
14192
+ if (requestedAt >= processBootMs) return false;
14193
+ if (nowMs - requestedAt > RESTART_CONFIRM_MAX_AGE_MS) return false;
14194
+ return true;
14195
+ }
14196
+ function buildBackOnlineText(name) {
14197
+ const trimmed = typeof name === "string" ? name.trim() : "";
14198
+ return trimmed ? `Hey ${trimmed}, I'm back online after my restart. \u{1F7E2}` : `I'm back online after my restart. \u{1F7E2}`;
14199
+ }
14200
+ function writeRestartConfirmMarker(filePath, marker) {
14201
+ const dir = dirname(filePath);
14202
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
14203
+ const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
14204
+ writeFileSync(tmpPath, JSON.stringify(marker) + "\n", { encoding: "utf8", mode: 384 });
14205
+ renameSync(tmpPath, filePath);
14206
+ }
14207
+ function readRestartConfirmMarker(filePath) {
14208
+ try {
14209
+ if (!existsSync(filePath)) return null;
14210
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
14211
+ if (!parsed || typeof parsed !== "object") return null;
14212
+ return parsed;
14213
+ } catch {
14214
+ return null;
14215
+ }
14216
+ }
14217
+ function clearRestartConfirmMarker(filePath) {
14218
+ try {
14219
+ if (existsSync(filePath)) unlinkSync(filePath);
14220
+ } catch {
14221
+ }
14222
+ }
14223
+
14183
14224
  // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
14184
14225
  import process2 from "process";
14185
14226
 
@@ -14274,21 +14315,21 @@ var StdioServerTransport = class {
14274
14315
 
14275
14316
  // src/telegram-channel.ts
14276
14317
  import https from "https";
14277
- import { createHash, randomUUID } from "crypto";
14318
+ import { createHash, randomUUID as randomUUID2 } from "crypto";
14278
14319
  import {
14279
14320
  createWriteStream,
14280
- existsSync as existsSync2,
14281
- mkdirSync as mkdirSync3,
14282
- readFileSync as readFileSync3,
14321
+ existsSync as existsSync4,
14322
+ mkdirSync as mkdirSync4,
14323
+ readFileSync as readFileSync5,
14283
14324
  readdirSync as readdirSync2,
14284
- renameSync as renameSync3,
14325
+ renameSync as renameSync4,
14285
14326
  statSync,
14286
- unlinkSync as unlinkSync3,
14327
+ unlinkSync as unlinkSync4,
14287
14328
  watch,
14288
- writeFileSync as writeFileSync3
14329
+ writeFileSync as writeFileSync4
14289
14330
  } from "fs";
14290
14331
  import { homedir as homedir2 } from "os";
14291
- import { join as join4 } from "path";
14332
+ import { join as join5 } from "path";
14292
14333
 
14293
14334
  // src/channel-attachments.ts
14294
14335
  import { homedir } from "os";
@@ -14412,7 +14453,7 @@ function redactAugmentedPaths(msg) {
14412
14453
  }
14413
14454
 
14414
14455
  // src/telegram-attachments.ts
14415
- import { mkdirSync, writeFileSync, chmodSync, renameSync, unlinkSync } from "fs";
14456
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, chmodSync, renameSync as renameSync2, unlinkSync as unlinkSync2 } from "fs";
14416
14457
  function resolveTelegramInboundDir(codeName) {
14417
14458
  return resolveChannelInboundDir(codeName, "telegram-inbound");
14418
14459
  }
@@ -14501,18 +14542,18 @@ async function downloadTelegramFile(fileId, opts) {
14501
14542
  if (!isPathInside(savedPath, dir)) {
14502
14543
  throw new Error(`refusing to write ${savedPath} outside ${dir}`);
14503
14544
  }
14504
- mkdirSync(dir, { recursive: true });
14545
+ mkdirSync2(dir, { recursive: true });
14505
14546
  const tempPath = `${savedPath}.${process.pid}.${Date.now()}.tmp`;
14506
14547
  try {
14507
- writeFileSync(tempPath, bytes, { mode: 384, flag: "wx" });
14548
+ writeFileSync2(tempPath, bytes, { mode: 384, flag: "wx" });
14508
14549
  try {
14509
14550
  chmodSync(tempPath, 384);
14510
14551
  } catch {
14511
14552
  }
14512
- renameSync(tempPath, savedPath);
14553
+ renameSync2(tempPath, savedPath);
14513
14554
  } catch (err) {
14514
14555
  try {
14515
- unlinkSync(tempPath);
14556
+ unlinkSync2(tempPath);
14516
14557
  } catch {
14517
14558
  }
14518
14559
  throw err;
@@ -14772,6 +14813,61 @@ function parsePeerAgentModeEnv(raw) {
14772
14813
  return "off";
14773
14814
  }
14774
14815
 
14816
+ // src/sender-policy-decline.ts
14817
+ function politeDeclineCopy(reason) {
14818
+ switch (reason) {
14819
+ case "internal_only":
14820
+ return "Sorry, I can only respond to people inside our organisation. This conversation appears to be from outside.";
14821
+ case "mode_manager_only":
14822
+ return "Sorry, I only respond to my manager in this channel.";
14823
+ case "mode_team_only":
14824
+ return "Sorry, this agent only replies to its team members.";
14825
+ case "mode_team_agents_only":
14826
+ return "Sorry, I only respond to agents on my team in this channel.";
14827
+ case "mode_agents_only":
14828
+ return "Sorry, I only respond to other agents in this channel.";
14829
+ }
14830
+ }
14831
+
14832
+ // src/inbound-access.ts
14833
+ function decideInboundAccess(input) {
14834
+ if (!input.content.forward) {
14835
+ return { kind: "drop", reason: `content:${input.content.reason}` };
14836
+ }
14837
+ if (input.isSelf) {
14838
+ return { kind: "drop", reason: "self" };
14839
+ }
14840
+ if (input.orgBoundary && !input.orgBoundary.forward) {
14841
+ return { kind: "drop", reason: "org_boundary" };
14842
+ }
14843
+ if (input.senderPolicy && !input.senderPolicy.forward) {
14844
+ const reason = input.senderPolicy.reason;
14845
+ const declineWorthy = input.sameOrg && reason !== "internal_only";
14846
+ return {
14847
+ kind: "drop",
14848
+ reason: `sender_policy:${reason}`,
14849
+ decline: declineWorthy ? politeDeclineCopy(reason) : void 0
14850
+ };
14851
+ }
14852
+ if (input.command && !input.isBot) {
14853
+ return {
14854
+ kind: "command",
14855
+ command: input.command.command,
14856
+ authorized: input.command.authorized
14857
+ };
14858
+ }
14859
+ if (input.isBot) {
14860
+ const peer = input.peer;
14861
+ if (!peer || peer.kind === "self") {
14862
+ return { kind: "drop", reason: peer?.kind === "self" ? "self" : "peer:unclassified" };
14863
+ }
14864
+ if (peer.kind === "drop") {
14865
+ return { kind: "drop", reason: `peer:${peer.reason ?? "unspecified"}` };
14866
+ }
14867
+ }
14868
+ return { kind: "admit" };
14869
+ }
14870
+
14775
14871
  // src/telegram-peer-rate-limiter.ts
14776
14872
  var SECOND_MS = 1e3;
14777
14873
  var MINUTE_MS = 60 * SECOND_MS;
@@ -15269,6 +15365,10 @@ var HELP = {
15269
15365
  command: "help",
15270
15366
  description: "Show available commands"
15271
15367
  };
15368
+ var STATUS = {
15369
+ command: "status",
15370
+ description: "Show this agent\u2019s model, session origin + uptime"
15371
+ };
15272
15372
  var RESTART = {
15273
15373
  command: "restart",
15274
15374
  description: "Restart this agent"
@@ -15279,11 +15379,86 @@ var INVESTIGATE = {
15279
15379
  };
15280
15380
  function buildCommandRegistrations() {
15281
15381
  return [
15282
- { scope: { type: "default" }, commands: [HELP, RESTART] },
15283
- { scope: { type: "all_private_chats" }, commands: [HELP, RESTART, INVESTIGATE] }
15382
+ { scope: { type: "default" }, commands: [HELP, STATUS, RESTART] },
15383
+ { scope: { type: "all_private_chats" }, commands: [HELP, STATUS, RESTART, INVESTIGATE] }
15284
15384
  ];
15285
15385
  }
15286
15386
 
15387
+ // src/agent-config-state.ts
15388
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
15389
+ import { join as join2 } from "path";
15390
+ var SESSION_STATE_FILENAME = "session-state.json";
15391
+ function readAgentSessionState(stateDir) {
15392
+ if (!stateDir) return null;
15393
+ const path = join2(stateDir, SESSION_STATE_FILENAME);
15394
+ if (!existsSync2(path)) return null;
15395
+ try {
15396
+ const parsed = JSON.parse(readFileSync2(path, "utf-8"));
15397
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
15398
+ return parsed;
15399
+ } catch {
15400
+ return null;
15401
+ }
15402
+ }
15403
+ function describeSessionOrigin(source) {
15404
+ switch (source) {
15405
+ case "startup":
15406
+ return "fresh start";
15407
+ case "resume":
15408
+ return "resumed";
15409
+ case "clear":
15410
+ return "cleared (/clear)";
15411
+ case "compact":
15412
+ return "compacted";
15413
+ default:
15414
+ return "unknown";
15415
+ }
15416
+ }
15417
+ function formatModelLabel(model) {
15418
+ const trimmed = model?.trim();
15419
+ return trimmed && trimmed.length > 0 ? trimmed : "unknown";
15420
+ }
15421
+ function shortSessionId(id) {
15422
+ const trimmed = id?.trim();
15423
+ if (!trimmed) return "unknown";
15424
+ return trimmed.length > 8 ? trimmed.slice(0, 8) : trimmed;
15425
+ }
15426
+ function formatRelativeAge(ts, now = Date.now()) {
15427
+ if (typeof ts !== "number" || !Number.isFinite(ts) || ts <= 0) return "unknown";
15428
+ const seconds = Math.floor((now - ts) / 1e3);
15429
+ if (seconds < 0) return "unknown";
15430
+ if (seconds < 5) return "just now";
15431
+ if (seconds < 60) return `${seconds}s ago`;
15432
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
15433
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
15434
+ return `${Math.floor(seconds / 86400)}d ago`;
15435
+ }
15436
+ function formatChannels(channels) {
15437
+ if (!Array.isArray(channels) || channels.length === 0) return "unknown";
15438
+ const cleaned = channels.map((c) => String(c).trim()).filter((c) => c.length > 0);
15439
+ return cleaned.length > 0 ? cleaned.join(", ") : "unknown";
15440
+ }
15441
+ function buildAgentConfigReport(opts) {
15442
+ const { codeName, connectivityLine, state, now = Date.now() } = opts;
15443
+ const lines = [`\u{1F916} *Config for \`${codeName}\`*`, connectivityLine];
15444
+ if (!state) {
15445
+ lines.push("\u2022 *Model:* unknown \u2014 session state not recorded yet");
15446
+ lines.push("\u2022 *Session:* unknown");
15447
+ return lines.join("\n");
15448
+ }
15449
+ lines.push(`\u2022 *Model:* \`${formatModelLabel(state.model)}\``);
15450
+ const origin = describeSessionOrigin(state.source);
15451
+ const age = formatRelativeAge(state.recorded_at, now);
15452
+ const sessionLine = age === "unknown" ? `\u2022 *Session:* ${origin}` : `\u2022 *Session:* ${origin} \xB7 started ${age}`;
15453
+ lines.push(sessionLine);
15454
+ lines.push(`\u2022 *Session ID:* \`${shortSessionId(state.session_id)}\``);
15455
+ if (state.environment && state.environment.trim().length > 0) {
15456
+ lines.push(`\u2022 *Environment:* ${state.environment.trim()}`);
15457
+ }
15458
+ lines.push(`\u2022 *Channels:* ${formatChannels(state.channels)}`);
15459
+ return lines.join("\n");
15460
+ }
15461
+
15287
15462
  // src/pane-tail.ts
15288
15463
  import { execFile } from "child_process";
15289
15464
  import { promisify } from "util";
@@ -15733,14 +15908,14 @@ var TELEGRAM_EGRESS_TOOLS = /* @__PURE__ */ new Set([
15733
15908
 
15734
15909
  // src/mcp-spawn-lock.ts
15735
15910
  import {
15736
- existsSync,
15737
- mkdirSync as mkdirSync2,
15738
- readFileSync,
15739
- renameSync as renameSync2,
15740
- unlinkSync as unlinkSync2,
15741
- writeFileSync as writeFileSync2
15911
+ existsSync as existsSync3,
15912
+ mkdirSync as mkdirSync3,
15913
+ readFileSync as readFileSync3,
15914
+ renameSync as renameSync3,
15915
+ unlinkSync as unlinkSync3,
15916
+ writeFileSync as writeFileSync3
15742
15917
  } from "fs";
15743
- import { join as join2 } from "path";
15918
+ import { join as join3 } from "path";
15744
15919
  function defaultIsPidAlive(pid) {
15745
15920
  if (!Number.isFinite(pid) || pid <= 0) return false;
15746
15921
  try {
@@ -15758,7 +15933,7 @@ function acquireMcpSpawnLock(args) {
15758
15933
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
15759
15934
  const selfPid = options.selfPid ?? process.pid;
15760
15935
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
15761
- const path = join2(agentDir, basename);
15936
+ const path = join3(agentDir, basename);
15762
15937
  const existing = readLockHolder(path);
15763
15938
  if (existing) {
15764
15939
  if (existing.pid === selfPid) {
@@ -15768,11 +15943,11 @@ function acquireMcpSpawnLock(args) {
15768
15943
  return { kind: "blocked", path, holder: existing };
15769
15944
  }
15770
15945
  }
15771
- mkdirSync2(agentDir, { recursive: true, mode: 448 });
15946
+ mkdirSync3(agentDir, { recursive: true, mode: 448 });
15772
15947
  const tmpPath = `${path}.${selfPid}.tmp`;
15773
15948
  const payload = { pid: selfPid, started_at: now() };
15774
- writeFileSync2(tmpPath, JSON.stringify(payload), { mode: 384 });
15775
- renameSync2(tmpPath, path);
15949
+ writeFileSync3(tmpPath, JSON.stringify(payload), { mode: 384 });
15950
+ renameSync3(tmpPath, path);
15776
15951
  return { kind: "acquired", path };
15777
15952
  }
15778
15953
  function releaseMcpSpawnLock(lockPath, opts = {}) {
@@ -15782,14 +15957,14 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
15782
15957
  if (!existing) return;
15783
15958
  if (existing.pid !== selfPid) return;
15784
15959
  try {
15785
- unlinkSync2(lockPath);
15960
+ unlinkSync3(lockPath);
15786
15961
  } catch {
15787
15962
  }
15788
15963
  }
15789
15964
  function readLockHolder(path) {
15790
- if (!existsSync(path)) return null;
15965
+ if (!existsSync3(path)) return null;
15791
15966
  try {
15792
- const raw = readFileSync(path, "utf8");
15967
+ const raw = readFileSync3(path, "utf8");
15793
15968
  const parsed = JSON.parse(raw);
15794
15969
  const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
15795
15970
  if (!Number.isFinite(pid) || pid <= 0) return null;
@@ -15801,8 +15976,8 @@ function readLockHolder(path) {
15801
15976
  }
15802
15977
 
15803
15978
  // src/ack-reaction.ts
15804
- import { readdirSync, readFileSync as readFileSync2 } from "fs";
15805
- import { join as join3 } from "path";
15979
+ import { readdirSync, readFileSync as readFileSync4 } from "fs";
15980
+ import { join as join4 } from "path";
15806
15981
  var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
15807
15982
  var ACK_STARTUP_GRACE_MS = 6e4;
15808
15983
  var ACK_PANE_FRESH_THRESHOLD_MS = 6e4;
@@ -15832,12 +16007,35 @@ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDEL
15832
16007
  function undeliverableNoticeText() {
15833
16008
  return "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
15834
16009
  }
16010
+ var BUSY_ACK_THRESHOLD_MS = 9e4;
16011
+ var BUSY_ACK_NOTICE_THROTTLE_MS = 10 * 60 * 1e3;
16012
+ function channelBusyAckEnabled() {
16013
+ const v = process.env.AGT_CHANNEL_BUSY_ACK_ENABLED;
16014
+ return v === "1" || v === "true";
16015
+ }
16016
+ function channelBusyAckThresholdMs() {
16017
+ const raw = parseInt(process.env.AGT_CHANNEL_BUSY_ACK_THRESHOLD_MS ?? "", 10);
16018
+ return Number.isFinite(raw) && raw > 0 ? raw : BUSY_ACK_THRESHOLD_MS;
16019
+ }
16020
+ function decideBusyAck(i) {
16021
+ if (!i.hasTarget) return false;
16022
+ if (!i.stillPending) return false;
16023
+ if (!Number.isFinite(i.pendingAgeMs)) return false;
16024
+ const threshold = i.thresholdMs ?? channelBusyAckThresholdMs();
16025
+ if (i.pendingAgeMs < threshold) return false;
16026
+ if (!i.sessionAlive) return false;
16027
+ const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
16028
+ return i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
16029
+ }
16030
+ function busyAckNoticeText() {
16031
+ return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
16032
+ }
15835
16033
  var GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
15836
16034
  var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
15837
16035
  function readGiveUpSignalAtMs(path, now = Date.now()) {
15838
16036
  if (!path) return null;
15839
16037
  try {
15840
- const raw = JSON.parse(readFileSync2(path, "utf8"));
16038
+ const raw = JSON.parse(readFileSync4(path, "utf8"));
15841
16039
  if (typeof raw.gave_up_at !== "string") return null;
15842
16040
  const t = Date.parse(raw.gave_up_at);
15843
16041
  if (!Number.isFinite(t) || t > now) return null;
@@ -15869,7 +16067,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
15869
16067
  if (!name.endsWith(".json")) continue;
15870
16068
  let receivedAt;
15871
16069
  try {
15872
- const raw = JSON.parse(readFileSync2(join3(dir, name), "utf-8"));
16070
+ const raw = JSON.parse(readFileSync4(join4(dir, name), "utf-8"));
15873
16071
  receivedAt = raw.received_at;
15874
16072
  } catch {
15875
16073
  continue;
@@ -15917,7 +16115,7 @@ function redactId(id) {
15917
16115
  }
15918
16116
  var BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
15919
16117
  var AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME ?? "unknown";
15920
- var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join4(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
16118
+ var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join5(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
15921
16119
  var AGT_HOST = process.env.AGT_HOST ?? null;
15922
16120
  var AGT_API_KEY = process.env.AGT_API_KEY ?? null;
15923
16121
  var AGT_AGENT_ID = process.env.AGT_AGENT_ID ?? null;
@@ -16011,9 +16209,9 @@ if (!BOT_TOKEN) {
16011
16209
  var stderrLogStream = null;
16012
16210
  if (AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown") {
16013
16211
  try {
16014
- const logDir = join4(homedir2(), ".augmented", AGENT_CODE_NAME);
16015
- mkdirSync3(logDir, { recursive: true });
16016
- stderrLogStream = createWriteStream(join4(logDir, "telegram-channel-stderr.log"), {
16212
+ const logDir = join5(homedir2(), ".augmented", AGENT_CODE_NAME);
16213
+ mkdirSync4(logDir, { recursive: true });
16214
+ stderrLogStream = createWriteStream(join5(logDir, "telegram-channel-stderr.log"), {
16017
16215
  flags: "a",
16018
16216
  mode: 384
16019
16217
  });
@@ -16161,13 +16359,138 @@ function notifyBackOnline(chatId) {
16161
16359
  function __resetBackOnlineNoticeThrottle() {
16162
16360
  lastBackOnlineNoticeAt.clear();
16163
16361
  }
16164
- var RESTART_FLAGS_DIR = join4(homedir2(), ".augmented", "restart-flags");
16362
+ var lastBusyAckNoticeAt = /* @__PURE__ */ new Map();
16363
+ function postBusyAckNotice(chatId, messageId) {
16364
+ const key2 = String(chatId);
16365
+ const now = Date.now();
16366
+ if (!shouldPostUndeliverableNotice(lastBusyAckNoticeAt.get(key2), now, BUSY_ACK_NOTICE_THROTTLE_MS)) {
16367
+ return;
16368
+ }
16369
+ lastBusyAckNoticeAt.set(key2, now);
16370
+ void (async () => {
16371
+ try {
16372
+ const resp = await telegramApiCall(
16373
+ "sendMessage",
16374
+ {
16375
+ chat_id: chatId,
16376
+ text: busyAckNoticeText(),
16377
+ reply_to_message_id: Number(messageId),
16378
+ allow_sending_without_reply: true
16379
+ },
16380
+ 1e4
16381
+ );
16382
+ if (!resp.ok) {
16383
+ process.stderr.write(
16384
+ `telegram-channel(${AGENT_CODE_NAME}): busy-ack notice failed (chat=${redactId(chatId)}): ${resp.description ?? "unknown"}
16385
+ `
16386
+ );
16387
+ }
16388
+ } catch (err) {
16389
+ process.stderr.write(
16390
+ `telegram-channel(${AGENT_CODE_NAME}): busy-ack notice error: ${err.message}
16391
+ `
16392
+ );
16393
+ }
16394
+ })();
16395
+ }
16396
+ function scheduleBusyAck(chatId, messageId) {
16397
+ if (!channelBusyAckEnabled()) return;
16398
+ const thresholdMs = channelBusyAckThresholdMs();
16399
+ const timer = setTimeout(() => {
16400
+ const probe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
16401
+ let paneLogFreshAgeMs = null;
16402
+ if (AGENT_DIR) {
16403
+ try {
16404
+ const paneMtimeMs = statSync(join5(AGENT_DIR, "pane.log")).mtimeMs;
16405
+ paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
16406
+ } catch {
16407
+ }
16408
+ }
16409
+ const marker = readPendingInboundMarker(chatId, messageId);
16410
+ if (!marker) return;
16411
+ const post = decideBusyAck({
16412
+ hasTarget: Boolean(chatId && messageId),
16413
+ stillPending: true,
16414
+ pendingAgeMs: Math.max(0, Date.now() - Date.parse(marker.received_at)),
16415
+ sessionAlive: probe.tmux === "alive" && probe.claude === "alive",
16416
+ paneLogFreshAgeMs,
16417
+ thresholdMs
16418
+ });
16419
+ if (post) postBusyAckNotice(chatId, messageId);
16420
+ }, thresholdMs);
16421
+ timer.unref();
16422
+ }
16423
+ function __resetBusyAckNoticeThrottle() {
16424
+ lastBusyAckNoticeAt.clear();
16425
+ }
16426
+ var RESTART_FLAGS_DIR = join5(homedir2(), ".augmented", "restart-flags");
16427
+ function writeTelegramRestartConfirm(reply, requesterName) {
16428
+ if (!RESTART_CONFIRM_FILE) return;
16429
+ const marker = {
16430
+ source: "telegram",
16431
+ requested_at: Date.now(),
16432
+ ...requesterName ? { requester_name: requesterName } : {},
16433
+ reply
16434
+ };
16435
+ try {
16436
+ writeRestartConfirmMarker(RESTART_CONFIRM_FILE, marker);
16437
+ } catch (err) {
16438
+ process.stderr.write(
16439
+ `telegram-channel(${AGENT_CODE_NAME}): restart-confirm marker write failed: ${redactAugmentedPaths(err.message)}
16440
+ `
16441
+ );
16442
+ }
16443
+ }
16444
+ async function notifyRestartCompleteOnBoot() {
16445
+ if (!RESTART_CONFIRM_FILE) return;
16446
+ const marker = readRestartConfirmMarker(RESTART_CONFIRM_FILE);
16447
+ if (!shouldPostRestartConfirm(marker, TELEGRAM_PROCESS_BOOT_MS, Date.now())) {
16448
+ if (marker) clearRestartConfirmMarker(RESTART_CONFIRM_FILE);
16449
+ return;
16450
+ }
16451
+ const chatId = marker.reply["chat_id"];
16452
+ if (chatId == null) {
16453
+ clearRestartConfirmMarker(RESTART_CONFIRM_FILE);
16454
+ return;
16455
+ }
16456
+ const messageId = marker.reply["message_id"];
16457
+ try {
16458
+ const resp = await telegramApiCall(
16459
+ "sendMessage",
16460
+ {
16461
+ chat_id: chatId,
16462
+ text: buildBackOnlineText(marker.requester_name),
16463
+ // Thread under the original /restart command when we have its id.
16464
+ ...messageId != null ? { reply_to_message_id: Number(messageId), allow_sending_without_reply: true } : {}
16465
+ },
16466
+ 1e4
16467
+ );
16468
+ if (resp.ok) {
16469
+ clearRestartConfirmMarker(RESTART_CONFIRM_FILE);
16470
+ process.stderr.write(
16471
+ `telegram-channel(${AGENT_CODE_NAME}): posted back-online confirmation after restart
16472
+ `
16473
+ );
16474
+ } else {
16475
+ process.stderr.write(
16476
+ `telegram-channel(${AGENT_CODE_NAME}): back-online confirmation rejected (chat=${redactId(chatId)}): ${resp.description ?? "unknown"}
16477
+ `
16478
+ );
16479
+ }
16480
+ } catch (err) {
16481
+ process.stderr.write(
16482
+ `telegram-channel(${AGENT_CODE_NAME}): back-online confirmation send failed: ${redactAugmentedPaths(err.message)}
16483
+ `
16484
+ );
16485
+ }
16486
+ }
16165
16487
  function buildTelegramHelpMessage(codeName) {
16166
16488
  return [
16167
16489
  `\u{1F916} *Available commands for \`${codeName}\`*`,
16168
16490
  "",
16169
16491
  `_Type these in any chat where the bot is present (intercepted by the agent):_`,
16170
16492
  `\u2022 /help \u2014 show this help`,
16493
+ `\u2022 /status \u2014 this agent's model, session origin, uptime + connectivity`,
16171
16494
  `\u2022 /restart \u2014 restart this agent`,
16172
16495
  `\u2022 /investigate-${codeName} \u2014 live tail of this agent's terminal pane (DM only; team owners/admins and the agent's reports-to person)`
16173
16496
  ].join("\n");
@@ -16197,21 +16520,58 @@ async function handleHelpCommand(opts) {
16197
16520
  );
16198
16521
  }
16199
16522
  }
16523
+ function buildTelegramStatusReply() {
16524
+ const state = readAgentSessionState(TELEGRAM_AGENT_DIR);
16525
+ return buildAgentConfigReport({
16526
+ codeName: AGENT_CODE_NAME,
16527
+ connectivityLine: "\u{1F7E2} *online* \u2014 Telegram channel connected.",
16528
+ state
16529
+ });
16530
+ }
16531
+ async function handleStatusCommand(opts) {
16532
+ try {
16533
+ const resp = await telegramApiCall(
16534
+ "sendMessage",
16535
+ {
16536
+ chat_id: opts.chatId,
16537
+ text: buildTelegramStatusReply(),
16538
+ parse_mode: "Markdown",
16539
+ reply_to_message_id: Number(opts.messageId)
16540
+ },
16541
+ 1e4
16542
+ );
16543
+ if (!resp.ok) {
16544
+ process.stderr.write(
16545
+ `telegram-channel(${AGENT_CODE_NAME}): /status reply rejected by Telegram (chat ${redactId(opts.chatId)}): ${resp.description ?? "unknown"}
16546
+ `
16547
+ );
16548
+ }
16549
+ } catch (err) {
16550
+ process.stderr.write(
16551
+ `telegram-channel(${AGENT_CODE_NAME}): /status reply send failed: ${redactAugmentedPaths(err.message)}
16552
+ `
16553
+ );
16554
+ }
16555
+ }
16200
16556
  async function handleRestartCommand(opts) {
16201
16557
  try {
16202
- if (!existsSync2(RESTART_FLAGS_DIR)) {
16203
- mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
16558
+ if (!existsSync4(RESTART_FLAGS_DIR)) {
16559
+ mkdirSync4(RESTART_FLAGS_DIR, { recursive: true });
16204
16560
  }
16205
- const flagPath = join4(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
16561
+ const flagPath = join5(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
16562
+ writeTelegramRestartConfirm(
16563
+ { chat_id: opts.chatId, message_id: opts.messageId },
16564
+ opts.requesterName
16565
+ );
16206
16566
  const flag = {
16207
16567
  codeName: AGENT_CODE_NAME,
16208
16568
  source: "telegram",
16209
16569
  ts: Date.now(),
16210
16570
  reply: { chat_id: opts.chatId, message_id: opts.messageId }
16211
16571
  };
16212
- const tmpPath = `${flagPath}.${process.pid}.${randomUUID()}.tmp`;
16213
- writeFileSync3(tmpPath, JSON.stringify(flag) + "\n", "utf8");
16214
- renameSync3(tmpPath, flagPath);
16572
+ const tmpPath = `${flagPath}.${process.pid}.${randomUUID2()}.tmp`;
16573
+ writeFileSync4(tmpPath, JSON.stringify(flag) + "\n", "utf8");
16574
+ renameSync4(tmpPath, flagPath);
16215
16575
  process.stderr.write(
16216
16576
  `telegram-channel(${AGENT_CODE_NAME}): /restart queued from chat ${redactId(opts.chatId)}
16217
16577
  `
@@ -16561,6 +16921,10 @@ var HELP_SYNTAX_RE = /^\/help(?:@([A-Za-z0-9_]{1,64}))?(?:\s|$)/;
16561
16921
  function isHelpSyntax(text) {
16562
16922
  return HELP_SYNTAX_RE.test(text);
16563
16923
  }
16924
+ var STATUS_SYNTAX_RE = /^\/status(?:@([A-Za-z0-9_]{1,64}))?(?:\s|$)/;
16925
+ function isStatusSyntax(text) {
16926
+ return STATUS_SYNTAX_RE.test(text);
16927
+ }
16564
16928
  function isRestartSyntax(text) {
16565
16929
  return RESTART_SYNTAX_RE.test(text);
16566
16930
  }
@@ -16573,16 +16937,18 @@ async function classifyRestartCommand(text) {
16573
16937
  if (!ours) return "verification_failed";
16574
16938
  return target === ours ? "act" : "ignore";
16575
16939
  }
16576
- var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join4(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
16577
- var PENDING_INBOUND_DIR = AGENT_DIR ? join4(AGENT_DIR, "telegram-pending-inbound") : null;
16578
- var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join4(AGENT_DIR, "telegram-recovery-outbox") : null;
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;
16944
+ var TELEGRAM_PROCESS_BOOT_MS = Date.now();
16579
16945
  function safeMarkerName(chatId, messageId) {
16580
16946
  const safe = (s) => s.replace(/[^A-Za-z0-9_-]/g, "_");
16581
16947
  return `${safe(chatId)}__${safe(messageId)}.json`;
16582
16948
  }
16583
16949
  function pendingInboundPath(chatId, messageId) {
16584
16950
  if (!PENDING_INBOUND_DIR) return null;
16585
- return join4(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
16951
+ return join5(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
16586
16952
  }
16587
16953
  function writePendingInboundMarker(chatId, messageId, chatType, undeliverable = false, payload) {
16588
16954
  const path = pendingInboundPath(chatId, messageId);
@@ -16599,8 +16965,8 @@ function writePendingInboundMarker(chatId, messageId, chatType, undeliverable =
16599
16965
  ...payload ? { payload } : {}
16600
16966
  };
16601
16967
  try {
16602
- mkdirSync3(PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
16603
- writeFileSync3(path, JSON.stringify(marker), { mode: 384 });
16968
+ mkdirSync4(PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
16969
+ writeFileSync4(path, JSON.stringify(marker), { mode: 384 });
16604
16970
  } catch (err) {
16605
16971
  process.stderr.write(
16606
16972
  `telegram-channel(${AGENT_CODE_NAME}): pending-inbound marker write failed: ${err.message}
@@ -16611,7 +16977,7 @@ function writePendingInboundMarker(chatId, messageId, chatType, undeliverable =
16611
16977
  function clearTelegramMarkerFileWithHeal(fullPath) {
16612
16978
  let marker = null;
16613
16979
  try {
16614
- marker = JSON.parse(readFileSync3(fullPath, "utf-8"));
16980
+ marker = JSON.parse(readFileSync5(fullPath, "utf-8"));
16615
16981
  } catch {
16616
16982
  }
16617
16983
  if (marker && decideRecoveryHeal({
@@ -16621,7 +16987,7 @@ function clearTelegramMarkerFileWithHeal(fullPath) {
16621
16987
  notifyBackOnline(marker.chat_id);
16622
16988
  }
16623
16989
  try {
16624
- if (existsSync2(fullPath)) unlinkSync3(fullPath);
16990
+ if (existsSync4(fullPath)) unlinkSync4(fullPath);
16625
16991
  } catch {
16626
16992
  }
16627
16993
  }
@@ -16630,6 +16996,15 @@ function clearPendingInboundMarker(chatId, messageId) {
16630
16996
  if (!path) return;
16631
16997
  clearTelegramMarkerFileWithHeal(path);
16632
16998
  }
16999
+ function readPendingInboundMarker(chatId, messageId) {
17000
+ const path = pendingInboundPath(chatId, messageId);
17001
+ if (!path || !existsSync4(path)) return null;
17002
+ try {
17003
+ return JSON.parse(readFileSync5(path, "utf-8"));
17004
+ } catch {
17005
+ return null;
17006
+ }
17007
+ }
16633
17008
  var MAX_RECOVERY_ATTEMPTS = 3;
16634
17009
  function nextRetryName(filename) {
16635
17010
  const match = filename.match(/^(.*?)(?:\.retry-(\d+))?\.json$/);
@@ -16645,10 +17020,10 @@ function nextRetryName(filename) {
16645
17020
  async function processRecoveryOutboxFile(filename) {
16646
17021
  if (!RECOVERY_OUTBOX_DIR) return;
16647
17022
  if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
16648
- const fullPath = join4(RECOVERY_OUTBOX_DIR, filename);
17023
+ const fullPath = join5(RECOVERY_OUTBOX_DIR, filename);
16649
17024
  let payload;
16650
17025
  try {
16651
- const raw = readFileSync3(fullPath, "utf-8");
17026
+ const raw = readFileSync5(fullPath, "utf-8");
16652
17027
  payload = JSON.parse(raw);
16653
17028
  } catch (err) {
16654
17029
  process.stderr.write(
@@ -16656,7 +17031,7 @@ async function processRecoveryOutboxFile(filename) {
16656
17031
  `
16657
17032
  );
16658
17033
  try {
16659
- renameSync3(fullPath, `${fullPath}.parse-error.poison`);
17034
+ renameSync4(fullPath, `${fullPath}.parse-error.poison`);
16660
17035
  } catch {
16661
17036
  }
16662
17037
  return;
@@ -16667,7 +17042,7 @@ async function processRecoveryOutboxFile(filename) {
16667
17042
  `
16668
17043
  );
16669
17044
  try {
16670
- renameSync3(fullPath, `${fullPath}.malformed.poison`);
17045
+ renameSync4(fullPath, `${fullPath}.malformed.poison`);
16671
17046
  } catch {
16672
17047
  }
16673
17048
  return;
@@ -16703,7 +17078,7 @@ async function processRecoveryOutboxFile(filename) {
16703
17078
  }
16704
17079
  if (sendSucceeded) {
16705
17080
  try {
16706
- unlinkSync3(fullPath);
17081
+ unlinkSync4(fullPath);
16707
17082
  } catch {
16708
17083
  }
16709
17084
  return;
@@ -16711,7 +17086,7 @@ async function processRecoveryOutboxFile(filename) {
16711
17086
  const next = nextRetryName(filename);
16712
17087
  if (next) {
16713
17088
  try {
16714
- renameSync3(fullPath, join4(RECOVERY_OUTBOX_DIR, next.next));
17089
+ renameSync4(fullPath, join5(RECOVERY_OUTBOX_DIR, next.next));
16715
17090
  if (next.attempt >= MAX_RECOVERY_ATTEMPTS) {
16716
17091
  process.stderr.write(
16717
17092
  `telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
@@ -16751,7 +17126,7 @@ function scanRecoveryRetries() {
16751
17126
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
16752
17127
  let mtimeMs;
16753
17128
  try {
16754
- mtimeMs = statSync(join4(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
17129
+ mtimeMs = statSync(join5(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
16755
17130
  } catch {
16756
17131
  continue;
16757
17132
  }
@@ -16763,7 +17138,7 @@ function scanRecoveryRetries() {
16763
17138
  function startRecoveryOutboxWatcher() {
16764
17139
  if (!RECOVERY_OUTBOX_DIR) return;
16765
17140
  try {
16766
- mkdirSync3(RECOVERY_OUTBOX_DIR, { recursive: true, mode: 448 });
17141
+ mkdirSync4(RECOVERY_OUTBOX_DIR, { recursive: true, mode: 448 });
16767
17142
  } catch (err) {
16768
17143
  process.stderr.write(
16769
17144
  `telegram-channel(${AGENT_CODE_NAME}): recovery outbox mkdir failed: ${err.message}
@@ -16781,7 +17156,7 @@ function startRecoveryOutboxWatcher() {
16781
17156
  const watcher = watch(RECOVERY_OUTBOX_DIR, (event, filename) => {
16782
17157
  if (event !== "rename" || !filename) return;
16783
17158
  if (!isFirstAttemptOutboxFile(filename)) return;
16784
- if (existsSync2(join4(RECOVERY_OUTBOX_DIR, filename))) {
17159
+ if (existsSync4(join5(RECOVERY_OUTBOX_DIR, filename))) {
16785
17160
  void processRecoveryOutboxFile(filename);
16786
17161
  }
16787
17162
  });
@@ -16802,7 +17177,7 @@ function trackPendingMessage(chatId, messageId, chatType, undeliverable = false,
16802
17177
  }
16803
17178
  function sweepTelegramStaleMarkers(thresholdMs) {
16804
17179
  if (!PENDING_INBOUND_DIR) return;
16805
- if (!existsSync2(PENDING_INBOUND_DIR)) return;
17180
+ if (!existsSync4(PENDING_INBOUND_DIR)) return;
16806
17181
  let filenames;
16807
17182
  try {
16808
17183
  filenames = readdirSync2(PENDING_INBOUND_DIR);
@@ -16818,17 +17193,17 @@ function sweepTelegramStaleMarkers(thresholdMs) {
16818
17193
  for (const filename of filenames) {
16819
17194
  if (!filename.endsWith(".json")) continue;
16820
17195
  if (filename.endsWith(".tmp")) continue;
16821
- const fullPath = join4(PENDING_INBOUND_DIR, filename);
17196
+ const fullPath = join5(PENDING_INBOUND_DIR, filename);
16822
17197
  let marker;
16823
17198
  try {
16824
- marker = JSON.parse(readFileSync3(fullPath, "utf-8"));
17199
+ marker = JSON.parse(readFileSync5(fullPath, "utf-8"));
16825
17200
  } catch (err) {
16826
17201
  process.stderr.write(
16827
17202
  `telegram-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactId(filename)}: ${err.message}
16828
17203
  `
16829
17204
  );
16830
17205
  try {
16831
- unlinkSync3(fullPath);
17206
+ unlinkSync4(fullPath);
16832
17207
  } catch {
16833
17208
  }
16834
17209
  cleared++;
@@ -16837,7 +17212,7 @@ function sweepTelegramStaleMarkers(thresholdMs) {
16837
17212
  const { chat_id, message_id, received_at } = marker;
16838
17213
  if (!chat_id || !message_id || isPendingMarkerStale(received_at, now, thresholdMs)) {
16839
17214
  try {
16840
- unlinkSync3(fullPath);
17215
+ unlinkSync4(fullPath);
16841
17216
  } catch {
16842
17217
  }
16843
17218
  cleared++;
@@ -16860,14 +17235,14 @@ var orphanSweepTimer = setInterval(() => {
16860
17235
  orphanSweepTimer.unref?.();
16861
17236
  var lastGiveUpHandledAtMs = null;
16862
17237
  function listPendingInboundChatIds() {
16863
- if (!PENDING_INBOUND_DIR || !existsSync2(PENDING_INBOUND_DIR)) return [];
17238
+ if (!PENDING_INBOUND_DIR || !existsSync4(PENDING_INBOUND_DIR)) return [];
16864
17239
  const chats = /* @__PURE__ */ new Set();
16865
17240
  try {
16866
17241
  for (const name of readdirSync2(PENDING_INBOUND_DIR)) {
16867
17242
  if (!name.endsWith(".json")) continue;
16868
17243
  try {
16869
17244
  const marker = JSON.parse(
16870
- readFileSync3(join4(PENDING_INBOUND_DIR, name), "utf8")
17245
+ readFileSync5(join5(PENDING_INBOUND_DIR, name), "utf8")
16871
17246
  );
16872
17247
  if (typeof marker.chat_id === "string" && marker.chat_id) chats.add(marker.chat_id);
16873
17248
  } catch {
@@ -16908,7 +17283,7 @@ async function notifyWatchdogGiveUp(chatId) {
16908
17283
  }
16909
17284
  function checkWatchdogGiveUpNotice() {
16910
17285
  if (!AGENT_DIR) return;
16911
- const signalAtMs = readGiveUpSignalAtMs(join4(AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
17286
+ const signalAtMs = readGiveUpSignalAtMs(join5(AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
16912
17287
  const act = decideGiveUpNotice({
16913
17288
  signalAtMs,
16914
17289
  lastHandledAtMs: lastGiveUpHandledAtMs,
@@ -16986,7 +17361,7 @@ function clearPendingMessage(chatId, messageId) {
16986
17361
  clearPendingInboundMarker(chatId, messageId);
16987
17362
  return;
16988
17363
  }
16989
- if (!PENDING_INBOUND_DIR || !existsSync2(PENDING_INBOUND_DIR)) return;
17364
+ if (!PENDING_INBOUND_DIR || !existsSync4(PENDING_INBOUND_DIR)) return;
16990
17365
  const safeChatId = chatId.replace(/[^A-Za-z0-9_-]/g, "_");
16991
17366
  const prefix = `${safeChatId}__`;
16992
17367
  let filenames;
@@ -16998,7 +17373,7 @@ function clearPendingMessage(chatId, messageId) {
16998
17373
  for (const filename of filenames) {
16999
17374
  if (!filename.startsWith(prefix)) continue;
17000
17375
  if (!filename.endsWith(".json")) continue;
17001
- clearTelegramMarkerFileWithHeal(join4(PENDING_INBOUND_DIR, filename));
17376
+ clearTelegramMarkerFileWithHeal(join5(PENDING_INBOUND_DIR, filename));
17002
17377
  }
17003
17378
  }
17004
17379
  function noteThreadActivity(chatId, messageId) {
@@ -17597,7 +17972,7 @@ await mcp.connect(new StdioServerTransport());
17597
17972
  var REPLAY_SCAN_INTERVAL_MS = 6e4;
17598
17973
  async function replayPendingTelegramMarkers() {
17599
17974
  if (!channelReplayEnabled()) return;
17600
- if (!PENDING_INBOUND_DIR || !existsSync2(PENDING_INBOUND_DIR)) return;
17975
+ if (!PENDING_INBOUND_DIR || !existsSync4(PENDING_INBOUND_DIR)) return;
17601
17976
  const probe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
17602
17977
  const sessionAlive = probe.tmux === "alive" && probe.claude === "alive";
17603
17978
  if (!sessionAlive) return;
@@ -17611,10 +17986,10 @@ async function replayPendingTelegramMarkers() {
17611
17986
  const entries = [];
17612
17987
  for (const name of filenames) {
17613
17988
  if (!name.endsWith(".json") || name.endsWith(".tmp")) continue;
17614
- const fullPath = join4(PENDING_INBOUND_DIR, name);
17989
+ const fullPath = join5(PENDING_INBOUND_DIR, name);
17615
17990
  let marker;
17616
17991
  try {
17617
- marker = JSON.parse(readFileSync3(fullPath, "utf-8"));
17992
+ marker = JSON.parse(readFileSync5(fullPath, "utf-8"));
17618
17993
  } catch {
17619
17994
  continue;
17620
17995
  }
@@ -17646,12 +18021,12 @@ async function replayPendingTelegramMarkers() {
17646
18021
  continue;
17647
18022
  }
17648
18023
  try {
17649
- if (existsSync2(path)) {
18024
+ if (existsSync4(path)) {
17650
18025
  const updated = {
17651
18026
  ...marker,
17652
18027
  replay_count: (marker.replay_count ?? 0) + 1
17653
18028
  };
17654
- writeFileSync3(path, JSON.stringify(updated), { mode: 384 });
18029
+ writeFileSync4(path, JSON.stringify(updated), { mode: 384 });
17655
18030
  }
17656
18031
  } catch {
17657
18032
  }
@@ -17788,14 +18163,68 @@ async function pollLoop() {
17788
18163
  const content = typeof msg.text === "string" && msg.text.length > 0 ? msg.text : typeof msg.caption === "string" && msg.caption.length > 0 ? msg.caption : "";
17789
18164
  if (content.length === 0 && classifiedAttachments.length === 0) continue;
17790
18165
  const chatId = String(msg.chat.id);
17791
- if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chatId)) continue;
18166
+ const trimmedContent = content.trim();
18167
+ const isFromBot = !!msg.from?.is_bot;
18168
+ const nowMs = Date.now();
18169
+ const telegramCommand = isHelpSyntax(trimmedContent) ? "help" : isStatusSyntax(trimmedContent) ? "status" : isRestartSyntax(trimmedContent) ? "restart" : isInvestigateSyntax(trimmedContent) ? "investigate" : void 0;
18170
+ let classification;
18171
+ let peerLeaf;
18172
+ if (isFromBot) {
18173
+ if (PEER_DISABLED_GLOBAL) {
18174
+ peerLeaf = { kind: "drop", reason: "peer_disabled_global" };
18175
+ } else {
18176
+ const classifierMsg = {
18177
+ message_id: msg.message_id,
18178
+ text: msg.text,
18179
+ caption: msg.caption,
18180
+ from: msg.from ? { id: msg.from.id, username: msg.from.username, is_bot: msg.from.is_bot } : void 0,
18181
+ chat: { id: msg.chat.id, type: msg.chat.type },
18182
+ reply_to_message: msg.reply_to_message ? {
18183
+ message_id: msg.reply_to_message.message_id,
18184
+ from: msg.reply_to_message.from ? { id: msg.reply_to_message.from.id, is_bot: msg.reply_to_message.from.is_bot } : void 0
18185
+ } : void 0,
18186
+ entities: msg.entities,
18187
+ caption_entities: msg.caption_entities
18188
+ };
18189
+ const self = await resolveBotIdentity();
18190
+ classification = classifyPeerMessage(classifierMsg, PEER_CLASSIFIER_CONFIG, self);
18191
+ peerLeaf = {
18192
+ kind: classification.kind,
18193
+ reason: classification.kind === "drop" ? classification.reason : void 0
18194
+ };
18195
+ }
18196
+ }
18197
+ const access = decideInboundAccess({
18198
+ content: { forward: true },
18199
+ // empty content already filtered above
18200
+ // CodeRabbit: derive self from the peer classification so an echo of
18201
+ // our own outbound is dropped at the self step — BEFORE command
18202
+ // routing — instead of slipping through as a command. (Slack catches
18203
+ // self at evt.user===botUserId and Teams inside its activity filter;
18204
+ // Telegram's only self signal is the peer classifier.)
18205
+ isSelf: peerLeaf?.kind === "self",
18206
+ isBot: isFromBot,
18207
+ peer: peerLeaf,
18208
+ // TELEGRAM_ALLOWED_CHATS is Telegram's org-boundary axis (the drift
18209
+ // the consolidation names). An unlisted chat is external ⇒ silent.
18210
+ orgBoundary: ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chatId) ? { forward: false } : { forward: true },
18211
+ command: telegramCommand ? { command: telegramCommand, authorized: true } : void 0,
18212
+ // No sender-policy mode system on Telegram, so no same-org decline.
18213
+ sameOrg: false
18214
+ });
18215
+ if (access.kind === "drop" && (access.reason === "org_boundary" || access.reason.startsWith("content:"))) {
18216
+ process.stderr.write(
18217
+ `telegram-channel(${AGENT_CODE_NAME}): inbound drop reason=${access.reason} chat=${redactId(chatId)}
18218
+ `
18219
+ );
18220
+ continue;
18221
+ }
17792
18222
  observedChatClient?.observe({
17793
18223
  chat_id: chatId,
17794
18224
  chat_title: msg.chat.title ?? msg.chat.username ?? msg.chat.first_name ?? null,
17795
18225
  chat_type: msg.chat.type ?? null
17796
18226
  });
17797
- const trimmedContent = content.trim();
17798
- if (isHelpSyntax(trimmedContent)) {
18227
+ if (access.kind === "command" && access.command === "help") {
17799
18228
  const disposition = await classifyRestartCommand(trimmedContent.replace(/^\/help/, "/restart"));
17800
18229
  if (disposition === "act") {
17801
18230
  await handleHelpCommand({
@@ -17805,12 +18234,26 @@ async function pollLoop() {
17805
18234
  }
17806
18235
  continue;
17807
18236
  }
17808
- if (isRestartSyntax(trimmedContent)) {
18237
+ if (access.kind === "command" && access.command === "status") {
18238
+ const disposition = await classifyRestartCommand(
18239
+ trimmedContent.replace(/^\/status/, "/restart")
18240
+ );
18241
+ if (disposition === "act") {
18242
+ await handleStatusCommand({
18243
+ chatId,
18244
+ messageId: String(msg.message_id)
18245
+ });
18246
+ }
18247
+ continue;
18248
+ }
18249
+ if (access.kind === "command" && access.command === "restart") {
17809
18250
  const disposition = await classifyRestartCommand(trimmedContent);
17810
18251
  if (disposition === "act") {
17811
18252
  await handleRestartCommand({
17812
18253
  chatId,
17813
- messageId: String(msg.message_id)
18254
+ messageId: String(msg.message_id),
18255
+ // ENG-6194: first_name personalises the back-online confirmation.
18256
+ requesterName: msg.from?.first_name?.trim() || void 0
17814
18257
  });
17815
18258
  } else if (disposition === "verification_failed") {
17816
18259
  try {
@@ -17832,7 +18275,7 @@ async function pollLoop() {
17832
18275
  }
17833
18276
  continue;
17834
18277
  }
17835
- if (isInvestigateSyntax(trimmedContent)) {
18278
+ if (access.kind === "command" && access.command === "investigate") {
17836
18279
  const disposition = await classifyInvestigateCommand(trimmedContent);
17837
18280
  if (disposition === "act") {
17838
18281
  await handleInvestigateCommand({
@@ -17863,8 +18306,6 @@ async function pollLoop() {
17863
18306
  }
17864
18307
  const userId = msg.from?.id != null ? String(msg.from.id) : "unknown";
17865
18308
  const userName = msg.from?.username || [msg.from?.first_name, msg.from?.last_name].filter(Boolean).join(" ").trim() || userId;
17866
- const isFromBot = !!msg.from?.is_bot;
17867
- const nowMs = Date.now();
17868
18309
  if (chatId && !isFromBot) {
17869
18310
  resetThread(chatId, "");
17870
18311
  }
@@ -17873,38 +18314,14 @@ async function pollLoop() {
17873
18314
  peerRateLimiter.recordInboundHuman(chatId, nowMs);
17874
18315
  }
17875
18316
  let peerAgentMeta = null;
17876
- if (isFromBot) {
17877
- if (PEER_DISABLED_GLOBAL) {
18317
+ if (access.kind === "drop") {
18318
+ if (access.reason !== "self") {
18319
+ const peerReason = access.reason.startsWith("peer:") ? access.reason.slice("peer:".length) : access.reason;
17878
18320
  process.stderr.write(
17879
- `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=peer_disabled_global chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
18321
+ `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${peerReason} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
17880
18322
  `
17881
18323
  );
17882
- continue;
17883
- }
17884
- const classifierMsg = {
17885
- message_id: msg.message_id,
17886
- text: msg.text,
17887
- caption: msg.caption,
17888
- from: msg.from ? { id: msg.from.id, username: msg.from.username, is_bot: msg.from.is_bot } : void 0,
17889
- chat: { id: msg.chat.id, type: msg.chat.type },
17890
- reply_to_message: msg.reply_to_message ? {
17891
- message_id: msg.reply_to_message.message_id,
17892
- from: msg.reply_to_message.from ? { id: msg.reply_to_message.from.id, is_bot: msg.reply_to_message.from.is_bot } : void 0
17893
- } : void 0,
17894
- entities: msg.entities,
17895
- caption_entities: msg.caption_entities
17896
- };
17897
- const self = await resolveBotIdentity();
17898
- const classification = classifyPeerMessage(classifierMsg, PEER_CLASSIFIER_CONFIG, self);
17899
- if (classification.kind === "self") {
17900
- continue;
17901
- }
17902
- if (classification.kind === "drop") {
17903
- process.stderr.write(
17904
- `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${classification.reason} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
17905
- `
17906
- );
17907
- if (classification.reason === "cross_team_grant_missing") {
18324
+ if (classification?.kind === "drop" && classification.reason === "cross_team_grant_missing") {
17908
18325
  crossTeamPeerAuditClient?.emit({
17909
18326
  event_type: "telegram.peer.drop.cross_team_grant_missing",
17910
18327
  peer_agent_id: null,
@@ -17914,52 +18331,52 @@ async function pollLoop() {
17914
18331
  message_id: String(msg.message_id)
17915
18332
  });
17916
18333
  }
17917
- continue;
17918
18334
  }
17919
- if (classification.kind === "peer-ingress") {
17920
- const limit = await peerRateLimiter.recordInboundPeer(
17921
- chatId,
17922
- classification.peer.bot_id,
17923
- nowMs
17924
- );
17925
- if (limit.decision !== "ok") {
17926
- process.stderr.write(
17927
- `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${limit.decision} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
18335
+ continue;
18336
+ }
18337
+ if (isFromBot && classification?.kind === "peer-ingress") {
18338
+ const limit = await peerRateLimiter.recordInboundPeer(
18339
+ chatId,
18340
+ classification.peer.bot_id,
18341
+ nowMs
18342
+ );
18343
+ if (limit.decision !== "ok") {
18344
+ process.stderr.write(
18345
+ `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${limit.decision} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
17928
18346
  `
17929
- );
17930
- continue;
17931
- }
17932
- if (limit.dailyBudgetWarn && !dailyBudgetWarned) {
17933
- dailyBudgetWarned = true;
17934
- process.stderr.write(
17935
- `telegram-channel(${AGENT_CODE_NAME}): peer_warn reason=daily_budget_80pct
18347
+ );
18348
+ continue;
18349
+ }
18350
+ if (limit.dailyBudgetWarn && !dailyBudgetWarned) {
18351
+ dailyBudgetWarned = true;
18352
+ process.stderr.write(
18353
+ `telegram-channel(${AGENT_CODE_NAME}): peer_warn reason=daily_budget_80pct
17936
18354
  `
17937
- );
17938
- }
17939
- peerAgentMeta = {
17940
- code_name: classification.peer.code_name,
17941
- agent_id: classification.peer.agent_id
17942
- };
17943
- const gate = classification.peer.gate_path;
17944
- if (gate && gate !== "same_team" && gate !== "intra_org_unrestricted" && gate.startsWith("grant:")) {
17945
- crossTeamPeerAuditClient?.emit({
17946
- event_type: "telegram.peer.ingress.cross_team",
17947
- peer_agent_id: classification.peer.agent_id || null,
17948
- gate_path: gate,
17949
- grant_id: gate.slice("grant:".length),
17950
- chat_id: String(chatId),
17951
- message_id: String(msg.message_id)
17952
- });
17953
- } else if (gate === "intra_org_unrestricted") {
17954
- crossTeamPeerAuditClient?.emit({
17955
- event_type: "telegram.peer.ingress.cross_team",
17956
- peer_agent_id: classification.peer.agent_id || null,
17957
- gate_path: gate,
17958
- grant_id: null,
17959
- chat_id: String(chatId),
17960
- message_id: String(msg.message_id)
17961
- });
17962
- }
18355
+ );
18356
+ }
18357
+ peerAgentMeta = {
18358
+ code_name: classification.peer.code_name,
18359
+ agent_id: classification.peer.agent_id
18360
+ };
18361
+ const gate = classification.peer.gate_path;
18362
+ if (gate && gate !== "same_team" && gate !== "intra_org_unrestricted" && gate.startsWith("grant:")) {
18363
+ crossTeamPeerAuditClient?.emit({
18364
+ event_type: "telegram.peer.ingress.cross_team",
18365
+ peer_agent_id: classification.peer.agent_id || null,
18366
+ gate_path: gate,
18367
+ grant_id: gate.slice("grant:".length),
18368
+ chat_id: String(chatId),
18369
+ message_id: String(msg.message_id)
18370
+ });
18371
+ } else if (gate === "intra_org_unrestricted") {
18372
+ crossTeamPeerAuditClient?.emit({
18373
+ event_type: "telegram.peer.ingress.cross_team",
18374
+ peer_agent_id: classification.peer.agent_id || null,
18375
+ gate_path: gate,
18376
+ grant_id: null,
18377
+ chat_id: String(chatId),
18378
+ message_id: String(msg.message_id)
18379
+ });
17963
18380
  }
17964
18381
  }
17965
18382
  const messageId = String(msg.message_id);
@@ -17967,7 +18384,7 @@ async function pollLoop() {
17967
18384
  let paneLogFreshAgeMs = null;
17968
18385
  if (AGENT_DIR) {
17969
18386
  try {
17970
- const paneMtimeMs = statSync(join4(AGENT_DIR, "pane.log")).mtimeMs;
18387
+ const paneMtimeMs = statSync(join5(AGENT_DIR, "pane.log")).mtimeMs;
17971
18388
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
17972
18389
  } catch {
17973
18390
  }
@@ -18042,6 +18459,9 @@ async function pollLoop() {
18042
18459
  ackDecision === "undeliverable",
18043
18460
  channelPayload
18044
18461
  );
18462
+ if (ackDecision === "ack") {
18463
+ scheduleBusyAck(chatId, messageId);
18464
+ }
18045
18465
  await mcp.notification({
18046
18466
  method: "notifications/claude/channel",
18047
18467
  params: channelPayload
@@ -18130,6 +18550,7 @@ void (async () => {
18130
18550
  }
18131
18551
  }
18132
18552
  })();
18553
+ void notifyRestartCompleteOnBoot();
18133
18554
  pollLoop().catch((err) => {
18134
18555
  process.stderr.write(
18135
18556
  `telegram-channel(${AGENT_CODE_NAME}): poll loop died: ${err.message}
@@ -18139,6 +18560,7 @@ pollLoop().catch((err) => {
18139
18560
  });
18140
18561
  export {
18141
18562
  __resetBackOnlineNoticeThrottle,
18563
+ __resetBusyAckNoticeThrottle,
18142
18564
  __resetGiveUpNoticeStateForTests,
18143
18565
  __resetUndeliverableNoticeThrottle,
18144
18566
  _resetQueuedReplyCountsForTests