@integrity-labs/agt-cli 0.28.329 → 0.28.331

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.
@@ -14522,7 +14522,7 @@ import { createHash, randomUUID as randomUUID3 } from "crypto";
14522
14522
  import {
14523
14523
  closeSync,
14524
14524
  createWriteStream,
14525
- existsSync as existsSync7,
14525
+ existsSync as existsSync8,
14526
14526
  ftruncateSync,
14527
14527
  mkdirSync as mkdirSync8,
14528
14528
  openSync,
@@ -14531,13 +14531,13 @@ import {
14531
14531
  realpathSync,
14532
14532
  renameSync as renameSync6,
14533
14533
  statSync as statSync2,
14534
- unlinkSync as unlinkSync6,
14534
+ unlinkSync as unlinkSync7,
14535
14535
  watch,
14536
14536
  writeFileSync as writeFileSync10,
14537
14537
  writeSync
14538
14538
  } from "fs";
14539
14539
  import { homedir as homedir4 } from "os";
14540
- import { basename, extname, join as join10, resolve as resolve2 } from "path";
14540
+ import { basename, extname, join as join11, resolve as resolve2 } from "path";
14541
14541
 
14542
14542
  // src/channel-attachments.ts
14543
14543
  import { homedir } from "os";
@@ -17024,6 +17024,29 @@ function applyToChatMarkers(pendingDir, chatId, op, cutoffMs) {
17024
17024
  return applied;
17025
17025
  }
17026
17026
 
17027
+ // src/recovery-ledger.ts
17028
+ import { existsSync as existsSync6, unlinkSync as unlinkSync5 } from "fs";
17029
+ import { join as join8 } from "path";
17030
+ function recoveryLedgerEntryExists(ledgerDir, markerName, exists = (p) => existsSync6(p)) {
17031
+ if (!ledgerDir || !markerName) return false;
17032
+ if (markerName.includes("/") || markerName.includes("\\") || markerName.includes("..")) return false;
17033
+ try {
17034
+ return exists(join8(ledgerDir, markerName));
17035
+ } catch {
17036
+ return false;
17037
+ }
17038
+ }
17039
+ function removeRecoveryLedgerEntry(ledgerDir, markerName, unlink = (p) => {
17040
+ if (existsSync6(p)) unlinkSync5(p);
17041
+ }) {
17042
+ if (!ledgerDir || !markerName) return;
17043
+ if (markerName.includes("/") || markerName.includes("\\") || markerName.includes("..")) return;
17044
+ try {
17045
+ unlink(join8(ledgerDir, markerName));
17046
+ } catch {
17047
+ }
17048
+ }
17049
+
17027
17050
  // src/channel-progress.ts
17028
17051
  function channelLiveProgressEnabled() {
17029
17052
  return resolveHostBooleanFlag({
@@ -17208,14 +17231,14 @@ function createKanbanCardActiveClient(args) {
17208
17231
 
17209
17232
  // src/mcp-spawn-lock.ts
17210
17233
  import {
17211
- existsSync as existsSync6,
17234
+ existsSync as existsSync7,
17212
17235
  mkdirSync as mkdirSync7,
17213
17236
  readFileSync as readFileSync9,
17214
17237
  renameSync as renameSync5,
17215
- unlinkSync as unlinkSync5,
17238
+ unlinkSync as unlinkSync6,
17216
17239
  writeFileSync as writeFileSync8
17217
17240
  } from "fs";
17218
- import { join as join8 } from "path";
17241
+ import { join as join9 } from "path";
17219
17242
  function defaultIsPidAlive(pid) {
17220
17243
  if (!Number.isFinite(pid) || pid <= 0) return false;
17221
17244
  try {
@@ -17233,7 +17256,7 @@ function acquireMcpSpawnLock(args) {
17233
17256
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
17234
17257
  const selfPid = options.selfPid ?? process.pid;
17235
17258
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
17236
- const path = join8(agentDir, basename2);
17259
+ const path = join9(agentDir, basename2);
17237
17260
  const existing = readLockHolder(path);
17238
17261
  if (existing) {
17239
17262
  if (existing.pid === selfPid) {
@@ -17257,12 +17280,12 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
17257
17280
  if (!existing) return;
17258
17281
  if (existing.pid !== selfPid) return;
17259
17282
  try {
17260
- unlinkSync5(lockPath);
17283
+ unlinkSync6(lockPath);
17261
17284
  } catch {
17262
17285
  }
17263
17286
  }
17264
17287
  function readLockHolder(path) {
17265
- if (!existsSync6(path)) return null;
17288
+ if (!existsSync7(path)) return null;
17266
17289
  try {
17267
17290
  const raw = readFileSync9(path, "utf8");
17268
17291
  const parsed = JSON.parse(raw);
@@ -17277,7 +17300,7 @@ function readLockHolder(path) {
17277
17300
 
17278
17301
  // src/ack-reaction.ts
17279
17302
  import { readdirSync as readdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
17280
- import { join as join9 } from "path";
17303
+ import { join as join10 } from "path";
17281
17304
  var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
17282
17305
  var ACK_STARTUP_GRACE_MS = 6e4;
17283
17306
  var ACK_PANE_FRESH_THRESHOLD_MS = 6e4;
@@ -17385,7 +17408,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now(), opts) {
17385
17408
  if (!name.endsWith(".json")) continue;
17386
17409
  let receivedAt;
17387
17410
  try {
17388
- const raw = JSON.parse(readFileSync10(join9(dir, name), "utf-8"));
17411
+ const raw = JSON.parse(readFileSync10(join10(dir, name), "utf-8"));
17389
17412
  if (raw.discretionary === true) continue;
17390
17413
  if (!opts?.includeSeen && typeof raw.seen_at === "string" && raw.seen_at) continue;
17391
17414
  receivedAt = raw.received_at;
@@ -17453,7 +17476,7 @@ function isMarkerGenuinelyAged(receivedAt, nowMs, thresholdMs) {
17453
17476
  }
17454
17477
  var DEFLECTION_COUNTER_SUFFIX = "-deflections.json";
17455
17478
  function deflectionCounterPath(agentDir, channel) {
17456
- return join9(agentDir, `${channel}${DEFLECTION_COUNTER_SUFFIX}`);
17479
+ return join10(agentDir, `${channel}${DEFLECTION_COUNTER_SUFFIX}`);
17457
17480
  }
17458
17481
  function recordChannelDeflection(agentDir, channel, cause) {
17459
17482
  if (!agentDir) return;
@@ -17541,7 +17564,7 @@ function redactId(id) {
17541
17564
  }
17542
17565
  var BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
17543
17566
  var AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME ?? "unknown";
17544
- var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join10(homedir4(), ".augmented", AGENT_CODE_NAME) : null;
17567
+ var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join11(homedir4(), ".augmented", AGENT_CODE_NAME) : null;
17545
17568
  var AGT_HOST = process.env.AGT_HOST ?? null;
17546
17569
  var AGT_API_KEY = process.env.AGT_API_KEY ?? null;
17547
17570
  var AGT_AGENT_ID = process.env.AGT_AGENT_ID ?? null;
@@ -17645,9 +17668,9 @@ if (!BOT_TOKEN) {
17645
17668
  var stderrLogStream = null;
17646
17669
  if (AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown") {
17647
17670
  try {
17648
- const logDir = join10(homedir4(), ".augmented", AGENT_CODE_NAME);
17671
+ const logDir = join11(homedir4(), ".augmented", AGENT_CODE_NAME);
17649
17672
  mkdirSync8(logDir, { recursive: true });
17650
- stderrLogStream = createWriteStream(join10(logDir, "telegram-channel-stderr.log"), {
17673
+ stderrLogStream = createWriteStream(join11(logDir, "telegram-channel-stderr.log"), {
17651
17674
  flags: "a",
17652
17675
  mode: 384
17653
17676
  });
@@ -17926,7 +17949,7 @@ function scheduleBusyAck(chatId, messageId, arrivedWhileBusy) {
17926
17949
  let paneLogFreshAgeMs = null;
17927
17950
  if (AGENT_DIR) {
17928
17951
  try {
17929
- const paneMtimeMs = statSync2(join10(AGENT_DIR, "pane.log")).mtimeMs;
17952
+ const paneMtimeMs = statSync2(join11(AGENT_DIR, "pane.log")).mtimeMs;
17930
17953
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
17931
17954
  } catch {
17932
17955
  }
@@ -17956,7 +17979,7 @@ function scheduleBusyAck(chatId, messageId, arrivedWhileBusy) {
17956
17979
  function __resetBusyAckNoticeThrottle() {
17957
17980
  lastBusyAckNoticeAt.clear();
17958
17981
  }
17959
- var RESTART_FLAGS_DIR = join10(homedir4(), ".augmented", "restart-flags");
17982
+ var RESTART_FLAGS_DIR = join11(homedir4(), ".augmented", "restart-flags");
17960
17983
  function actuateHostRestartTelegram() {
17961
17984
  return actuateHostRestart({
17962
17985
  agtHost: AGT_HOST,
@@ -18338,10 +18361,10 @@ async function handleRestartCommand(opts) {
18338
18361
  writeTelegramRestartConfirm({ chat_id: opts.chatId, message_id: opts.messageId }, opts.requesterName);
18339
18362
  } else {
18340
18363
  writeTelegramRestartConfirm({ chat_id: opts.chatId, message_id: opts.messageId }, opts.requesterName);
18341
- if (!existsSync7(RESTART_FLAGS_DIR)) {
18364
+ if (!existsSync8(RESTART_FLAGS_DIR)) {
18342
18365
  mkdirSync8(RESTART_FLAGS_DIR, { recursive: true });
18343
18366
  }
18344
- const flagPath = join10(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
18367
+ const flagPath = join11(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
18345
18368
  const flag = {
18346
18369
  codeName: AGENT_CODE_NAME,
18347
18370
  source: "telegram",
@@ -18747,14 +18770,15 @@ async function classifyRestartCommand(text) {
18747
18770
  if (!ours) return "verification_failed";
18748
18771
  return target === ours ? "act" : "ignore";
18749
18772
  }
18750
- var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join10(homedir4(), ".augmented", AGENT_CODE_NAME) : null;
18751
- var PENDING_INBOUND_DIR = AGENT_DIR ? join10(AGENT_DIR, "telegram-pending-inbound") : null;
18752
- var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join10(AGENT_DIR, "telegram-recovery-outbox") : null;
18753
- var RESTART_CONFIRM_FILE = AGENT_DIR ? join10(AGENT_DIR, "telegram-restart-confirm.json") : null;
18773
+ var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join11(homedir4(), ".augmented", AGENT_CODE_NAME) : null;
18774
+ var PENDING_INBOUND_DIR = AGENT_DIR ? join11(AGENT_DIR, "telegram-pending-inbound") : null;
18775
+ var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join11(AGENT_DIR, "telegram-recovery-outbox") : null;
18776
+ var RECOVERY_LEDGER_DIR = AGENT_DIR ? join11(AGENT_DIR, ".agt-telegram-recovery-ledger") : null;
18777
+ var RESTART_CONFIRM_FILE = AGENT_DIR ? join11(AGENT_DIR, "telegram-restart-confirm.json") : null;
18754
18778
  var TELEGRAM_PROCESS_BOOT_MS = Date.now();
18755
- var TELEGRAM_RECENT_DMS_FILE = AGENT_DIR ? join10(AGENT_DIR, "telegram-recent-dms.json") : null;
18756
- var TELEGRAM_CHANNEL_ADD_RESTART_FILE = AGENT_DIR ? join10(AGENT_DIR, "telegram-channel-add-restart.json") : null;
18757
- var TELEGRAM_OFFSET_FILE = AGENT_DIR ? join10(AGENT_DIR, "telegram-getupdates-offset.json") : null;
18779
+ var TELEGRAM_RECENT_DMS_FILE = AGENT_DIR ? join11(AGENT_DIR, "telegram-recent-dms.json") : null;
18780
+ var TELEGRAM_CHANNEL_ADD_RESTART_FILE = AGENT_DIR ? join11(AGENT_DIR, "telegram-channel-add-restart.json") : null;
18781
+ var TELEGRAM_OFFSET_FILE = AGENT_DIR ? join11(AGENT_DIR, "telegram-getupdates-offset.json") : null;
18758
18782
  var recentDms = /* @__PURE__ */ new Map();
18759
18783
  var recentDmPersister = TELEGRAM_RECENT_DMS_FILE ? createRecentDmPersister({
18760
18784
  filePath: TELEGRAM_RECENT_DMS_FILE,
@@ -18809,7 +18833,7 @@ function safeMarkerName(chatId, messageId) {
18809
18833
  }
18810
18834
  function pendingInboundPath(chatId, messageId) {
18811
18835
  if (!PENDING_INBOUND_DIR) return null;
18812
- return join10(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
18836
+ return join11(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
18813
18837
  }
18814
18838
  function writePendingInboundMarker(chatId, messageId, chatType, undeliverable = false, payload) {
18815
18839
  const path = pendingInboundPath(chatId, messageId);
@@ -18865,7 +18889,7 @@ function clearTelegramMarkerFileWithHeal(fullPath) {
18865
18889
  notifyBackOnline(marker.chat_id);
18866
18890
  }
18867
18891
  try {
18868
- if (existsSync7(fullPath)) unlinkSync6(fullPath);
18892
+ if (existsSync8(fullPath)) unlinkSync7(fullPath);
18869
18893
  } catch {
18870
18894
  }
18871
18895
  }
@@ -18898,7 +18922,7 @@ function markTelegramMarkerSeenWithHeal(fullPath) {
18898
18922
  }
18899
18923
  function readPendingInboundMarker(chatId, messageId) {
18900
18924
  const path = pendingInboundPath(chatId, messageId);
18901
- if (!path || !existsSync7(path)) return null;
18925
+ if (!path || !existsSync8(path)) return null;
18902
18926
  try {
18903
18927
  return JSON.parse(readFileSync11(path, "utf-8"));
18904
18928
  } catch {
@@ -18920,7 +18944,7 @@ function nextRetryName(filename) {
18920
18944
  async function processRecoveryOutboxFile(filename) {
18921
18945
  if (!RECOVERY_OUTBOX_DIR) return;
18922
18946
  if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
18923
- const fullPath = join10(RECOVERY_OUTBOX_DIR, filename);
18947
+ const fullPath = join11(RECOVERY_OUTBOX_DIR, filename);
18924
18948
  let payload;
18925
18949
  try {
18926
18950
  const raw = readFileSync11(fullPath, "utf-8");
@@ -18941,6 +18965,7 @@ async function processRecoveryOutboxFile(filename) {
18941
18965
  `telegram-channel(${AGENT_CODE_NAME}): recovery outbox malformed (${filename}): missing chat_id or text
18942
18966
  `
18943
18967
  );
18968
+ removeRecoveryLedgerEntry(RECOVERY_LEDGER_DIR, payload.marker_name);
18944
18969
  try {
18945
18970
  renameSync6(fullPath, `${fullPath}.malformed.poison`);
18946
18971
  } catch {
@@ -18969,8 +18994,9 @@ async function processRecoveryOutboxFile(filename) {
18969
18994
  );
18970
18995
  if (decision.action === "suppress") {
18971
18996
  clearPendingMessage(payload.chat_id, recoveryDrainCutoffMs);
18997
+ removeRecoveryLedgerEntry(RECOVERY_LEDGER_DIR, payload.marker_name);
18972
18998
  try {
18973
- unlinkSync6(fullPath);
18999
+ unlinkSync7(fullPath);
18974
19000
  } catch {
18975
19001
  }
18976
19002
  return;
@@ -19005,8 +19031,9 @@ async function processRecoveryOutboxFile(filename) {
19005
19031
  );
19006
19032
  }
19007
19033
  if (sendSucceeded) {
19034
+ removeRecoveryLedgerEntry(RECOVERY_LEDGER_DIR, payload.marker_name);
19008
19035
  try {
19009
- unlinkSync6(fullPath);
19036
+ unlinkSync7(fullPath);
19010
19037
  } catch {
19011
19038
  }
19012
19039
  return;
@@ -19014,12 +19041,13 @@ async function processRecoveryOutboxFile(filename) {
19014
19041
  const next = nextRetryName(filename);
19015
19042
  if (next) {
19016
19043
  try {
19017
- renameSync6(fullPath, join10(RECOVERY_OUTBOX_DIR, next.next));
19044
+ renameSync6(fullPath, join11(RECOVERY_OUTBOX_DIR, next.next));
19018
19045
  if (next.attempt >= MAX_RECOVERY_ATTEMPTS) {
19019
19046
  process.stderr.write(
19020
19047
  `telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
19021
19048
  `
19022
19049
  );
19050
+ removeRecoveryLedgerEntry(RECOVERY_LEDGER_DIR, payload.marker_name);
19023
19051
  }
19024
19052
  } catch {
19025
19053
  }
@@ -19054,7 +19082,7 @@ function scanRecoveryRetries() {
19054
19082
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
19055
19083
  let mtimeMs;
19056
19084
  try {
19057
- mtimeMs = statSync2(join10(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
19085
+ mtimeMs = statSync2(join11(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
19058
19086
  } catch {
19059
19087
  continue;
19060
19088
  }
@@ -19084,7 +19112,7 @@ function startRecoveryOutboxWatcher() {
19084
19112
  const watcher = watch(RECOVERY_OUTBOX_DIR, (event, filename) => {
19085
19113
  if (event !== "rename" || !filename) return;
19086
19114
  if (!isFirstAttemptOutboxFile(filename)) return;
19087
- if (existsSync7(join10(RECOVERY_OUTBOX_DIR, filename))) {
19115
+ if (existsSync8(join11(RECOVERY_OUTBOX_DIR, filename))) {
19088
19116
  void processRecoveryOutboxFile(filename);
19089
19117
  }
19090
19118
  });
@@ -19099,7 +19127,7 @@ function startRecoveryOutboxWatcher() {
19099
19127
  retryTimer.unref?.();
19100
19128
  }
19101
19129
  startRecoveryOutboxWatcher();
19102
- var NOTICE_OUTBOX_DIR = AGENT_DIR ? join10(AGENT_DIR, "telegram-notice-outbox") : null;
19130
+ var NOTICE_OUTBOX_DIR = AGENT_DIR ? join11(AGENT_DIR, "telegram-notice-outbox") : null;
19103
19131
  var NOTICE_MAX_AGE_MS = 9e4;
19104
19132
  var NOTICE_INFLIGHT = /* @__PURE__ */ new Set();
19105
19133
  async function processNoticeOutboxFile(filename) {
@@ -19107,7 +19135,7 @@ async function processNoticeOutboxFile(filename) {
19107
19135
  if (filename.startsWith(".") || filename.endsWith(".tmp") || !filename.endsWith(".json")) return;
19108
19136
  if (NOTICE_INFLIGHT.has(filename)) return;
19109
19137
  NOTICE_INFLIGHT.add(filename);
19110
- const fullPath = join10(NOTICE_OUTBOX_DIR, filename);
19138
+ const fullPath = join11(NOTICE_OUTBOX_DIR, filename);
19111
19139
  try {
19112
19140
  let mtimeMs;
19113
19141
  try {
@@ -19117,7 +19145,7 @@ async function processNoticeOutboxFile(filename) {
19117
19145
  }
19118
19146
  if (Date.now() - mtimeMs > NOTICE_MAX_AGE_MS) {
19119
19147
  try {
19120
- unlinkSync6(fullPath);
19148
+ unlinkSync7(fullPath);
19121
19149
  } catch {
19122
19150
  }
19123
19151
  return;
@@ -19129,14 +19157,14 @@ async function processNoticeOutboxFile(filename) {
19129
19157
  payload = parsed;
19130
19158
  } catch {
19131
19159
  try {
19132
- unlinkSync6(fullPath);
19160
+ unlinkSync7(fullPath);
19133
19161
  } catch {
19134
19162
  }
19135
19163
  return;
19136
19164
  }
19137
19165
  if (!payload.chat_id || !payload.text) {
19138
19166
  try {
19139
- unlinkSync6(fullPath);
19167
+ unlinkSync7(fullPath);
19140
19168
  } catch {
19141
19169
  }
19142
19170
  return;
@@ -19147,7 +19175,7 @@ async function processNoticeOutboxFile(filename) {
19147
19175
  defaultValue: false
19148
19176
  })) {
19149
19177
  try {
19150
- unlinkSync6(fullPath);
19178
+ unlinkSync7(fullPath);
19151
19179
  } catch {
19152
19180
  }
19153
19181
  return;
@@ -19176,7 +19204,7 @@ async function processNoticeOutboxFile(filename) {
19176
19204
  );
19177
19205
  }
19178
19206
  try {
19179
- unlinkSync6(fullPath);
19207
+ unlinkSync7(fullPath);
19180
19208
  } catch {
19181
19209
  }
19182
19210
  } finally {
@@ -19201,7 +19229,7 @@ function startNoticeOutboxWatcher() {
19201
19229
  try {
19202
19230
  const watcher = watch(NOTICE_OUTBOX_DIR, (event, filename) => {
19203
19231
  if (event !== "rename" || !filename) return;
19204
- if (existsSync7(join10(NOTICE_OUTBOX_DIR, filename))) void processNoticeOutboxFile(filename);
19232
+ if (existsSync8(join11(NOTICE_OUTBOX_DIR, filename))) void processNoticeOutboxFile(filename);
19205
19233
  });
19206
19234
  watcher.unref?.();
19207
19235
  } catch (err) {
@@ -19218,7 +19246,7 @@ function trackPendingMessage(chatId, messageId, chatType, undeliverable = false,
19218
19246
  }
19219
19247
  function sweepTelegramStaleMarkers(thresholdMs) {
19220
19248
  if (!PENDING_INBOUND_DIR) return;
19221
- if (!existsSync7(PENDING_INBOUND_DIR)) return;
19249
+ if (!existsSync8(PENDING_INBOUND_DIR)) return;
19222
19250
  let filenames;
19223
19251
  try {
19224
19252
  filenames = readdirSync3(PENDING_INBOUND_DIR);
@@ -19234,7 +19262,7 @@ function sweepTelegramStaleMarkers(thresholdMs) {
19234
19262
  for (const filename of filenames) {
19235
19263
  if (!filename.endsWith(".json")) continue;
19236
19264
  if (filename.endsWith(".tmp")) continue;
19237
- const fullPath = join10(PENDING_INBOUND_DIR, filename);
19265
+ const fullPath = join11(PENDING_INBOUND_DIR, filename);
19238
19266
  let marker;
19239
19267
  try {
19240
19268
  marker = JSON.parse(readFileSync11(fullPath, "utf-8"));
@@ -19244,7 +19272,7 @@ function sweepTelegramStaleMarkers(thresholdMs) {
19244
19272
  `
19245
19273
  );
19246
19274
  try {
19247
- unlinkSync6(fullPath);
19275
+ unlinkSync7(fullPath);
19248
19276
  } catch {
19249
19277
  }
19250
19278
  cleared++;
@@ -19262,7 +19290,7 @@ function sweepTelegramStaleMarkers(thresholdMs) {
19262
19290
  );
19263
19291
  }
19264
19292
  try {
19265
- unlinkSync6(fullPath);
19293
+ unlinkSync7(fullPath);
19266
19294
  } catch {
19267
19295
  }
19268
19296
  cleared++;
@@ -19283,11 +19311,11 @@ var orphanSweepTimer = setInterval(() => {
19283
19311
  checkWatchdogGiveUpNotice();
19284
19312
  }, orphanSweepIntervalMs());
19285
19313
  orphanSweepTimer.unref?.();
19286
- var TELEGRAM_PROGRESS_HEARTBEAT_PATH = AGENT_DIR ? join10(AGENT_DIR, "channel-progress-heartbeat.json") : null;
19314
+ var TELEGRAM_PROGRESS_HEARTBEAT_PATH = AGENT_DIR ? join11(AGENT_DIR, "channel-progress-heartbeat.json") : null;
19287
19315
  var telegramTrackedProgress = null;
19288
19316
  var telegramProgressTickRunning = false;
19289
19317
  function readTelegramProgressHeartbeat() {
19290
- if (!TELEGRAM_PROGRESS_HEARTBEAT_PATH || !existsSync7(TELEGRAM_PROGRESS_HEARTBEAT_PATH)) return null;
19318
+ if (!TELEGRAM_PROGRESS_HEARTBEAT_PATH || !existsSync8(TELEGRAM_PROGRESS_HEARTBEAT_PATH)) return null;
19291
19319
  try {
19292
19320
  return parseProgressHeartbeat(readFileSync11(TELEGRAM_PROGRESS_HEARTBEAT_PATH, "utf-8"));
19293
19321
  } catch {
@@ -19302,13 +19330,13 @@ function seedTelegramProgressHeartbeat() {
19302
19330
  renameSync6(tmp, TELEGRAM_PROGRESS_HEARTBEAT_PATH);
19303
19331
  } catch {
19304
19332
  try {
19305
- unlinkSync6(tmp);
19333
+ unlinkSync7(tmp);
19306
19334
  } catch {
19307
19335
  }
19308
19336
  }
19309
19337
  }
19310
19338
  function findTelegramProgressTarget() {
19311
- if (!PENDING_INBOUND_DIR || !existsSync7(PENDING_INBOUND_DIR)) return null;
19339
+ if (!PENDING_INBOUND_DIR || !existsSync8(PENDING_INBOUND_DIR)) return null;
19312
19340
  let best = null;
19313
19341
  let bestMs = Infinity;
19314
19342
  try {
@@ -19316,7 +19344,7 @@ function findTelegramProgressTarget() {
19316
19344
  if (!name.endsWith(".json")) continue;
19317
19345
  let m;
19318
19346
  try {
19319
- m = JSON.parse(readFileSync11(join10(PENDING_INBOUND_DIR, name), "utf-8"));
19347
+ m = JSON.parse(readFileSync11(join11(PENDING_INBOUND_DIR, name), "utf-8"));
19320
19348
  } catch {
19321
19349
  continue;
19322
19350
  }
@@ -19440,14 +19468,14 @@ if (BOT_TOKEN && PENDING_INBOUND_DIR) {
19440
19468
  }
19441
19469
  var lastGiveUpHandledAtMs = null;
19442
19470
  function listPendingInboundChatIds() {
19443
- if (!PENDING_INBOUND_DIR || !existsSync7(PENDING_INBOUND_DIR)) return [];
19471
+ if (!PENDING_INBOUND_DIR || !existsSync8(PENDING_INBOUND_DIR)) return [];
19444
19472
  const chats = /* @__PURE__ */ new Set();
19445
19473
  try {
19446
19474
  for (const name of readdirSync3(PENDING_INBOUND_DIR)) {
19447
19475
  if (!name.endsWith(".json")) continue;
19448
19476
  try {
19449
19477
  const marker = JSON.parse(
19450
- readFileSync11(join10(PENDING_INBOUND_DIR, name), "utf8")
19478
+ readFileSync11(join11(PENDING_INBOUND_DIR, name), "utf8")
19451
19479
  );
19452
19480
  if (typeof marker.seen_at === "string" && marker.seen_at) continue;
19453
19481
  if (typeof marker.chat_id === "string" && marker.chat_id) chats.add(marker.chat_id);
@@ -19489,7 +19517,7 @@ async function notifyWatchdogGiveUp(chatId, reason) {
19489
19517
  }
19490
19518
  function checkWatchdogGiveUpNotice() {
19491
19519
  if (!AGENT_DIR) return;
19492
- const signal = readGiveUpSignal(join10(AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
19520
+ const signal = readGiveUpSignal(join11(AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
19493
19521
  const signalAtMs = signal?.atMs ?? null;
19494
19522
  const act = decideGiveUpNotice({
19495
19523
  signalAtMs,
@@ -19551,7 +19579,7 @@ async function deliverQueuedReply(p) {
19551
19579
  }
19552
19580
  }
19553
19581
  function clearPendingMessage(chatId, cutoffMs = Number.POSITIVE_INFINITY) {
19554
- if (!PENDING_INBOUND_DIR || !existsSync7(PENDING_INBOUND_DIR)) return;
19582
+ if (!PENDING_INBOUND_DIR || !existsSync8(PENDING_INBOUND_DIR)) return;
19555
19583
  clearAllTelegramPendingMarkersForChat(
19556
19584
  PENDING_INBOUND_DIR,
19557
19585
  chatId,
@@ -19560,7 +19588,7 @@ function clearPendingMessage(chatId, cutoffMs = Number.POSITIVE_INFINITY) {
19560
19588
  );
19561
19589
  }
19562
19590
  function markSeenPendingMessage(chatId, cutoffMs = Number.POSITIVE_INFINITY) {
19563
- if (!PENDING_INBOUND_DIR || !existsSync7(PENDING_INBOUND_DIR)) return;
19591
+ if (!PENDING_INBOUND_DIR || !existsSync8(PENDING_INBOUND_DIR)) return;
19564
19592
  markSeenAllTelegramPendingMarkersForChat(
19565
19593
  PENDING_INBOUND_DIR,
19566
19594
  chatId,
@@ -20386,7 +20414,7 @@ await mcp.connect(new StdioServerTransport());
20386
20414
  var REPLAY_SCAN_INTERVAL_MS = 6e4;
20387
20415
  async function replayPendingTelegramMarkers() {
20388
20416
  if (!channelReplayEnabled()) return;
20389
- if (!PENDING_INBOUND_DIR || !existsSync7(PENDING_INBOUND_DIR)) return;
20417
+ if (!PENDING_INBOUND_DIR || !existsSync8(PENDING_INBOUND_DIR)) return;
20390
20418
  const probe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
20391
20419
  const sessionAlive = probe.tmux === "alive" && probe.claude === "alive";
20392
20420
  if (!sessionAlive) return;
@@ -20400,7 +20428,7 @@ async function replayPendingTelegramMarkers() {
20400
20428
  let paneFreshAgeMs = null;
20401
20429
  if (AGENT_DIR) {
20402
20430
  try {
20403
- paneFreshAgeMs = Math.max(0, now - statSync2(join10(AGENT_DIR, "pane.log")).mtimeMs);
20431
+ paneFreshAgeMs = Math.max(0, now - statSync2(join11(AGENT_DIR, "pane.log")).mtimeMs);
20404
20432
  } catch {
20405
20433
  }
20406
20434
  }
@@ -20408,7 +20436,7 @@ async function replayPendingTelegramMarkers() {
20408
20436
  const entries = [];
20409
20437
  for (const name of filenames) {
20410
20438
  if (!name.endsWith(".json") || name.endsWith(".tmp")) continue;
20411
- const fullPath = join10(PENDING_INBOUND_DIR, name);
20439
+ const fullPath = join11(PENDING_INBOUND_DIR, name);
20412
20440
  let marker;
20413
20441
  try {
20414
20442
  marker = JSON.parse(readFileSync11(fullPath, "utf-8"));
@@ -20421,6 +20449,7 @@ async function replayPendingTelegramMarkers() {
20421
20449
  }
20422
20450
  entries.sort((a, b) => b.ageMs - a.ageMs);
20423
20451
  for (const { path, marker, ageMs } of entries) {
20452
+ if (recoveryLedgerEntryExists(RECOVERY_LEDGER_DIR, basename(path))) continue;
20424
20453
  if (!shouldReplayMarker({
20425
20454
  enabled: true,
20426
20455
  hasPayload: Boolean(marker.payload),
@@ -20930,7 +20959,7 @@ async function pollLoop() {
20930
20959
  let paneLogFreshAgeMs = null;
20931
20960
  if (AGENT_DIR) {
20932
20961
  try {
20933
- const paneMtimeMs = statSync2(join10(AGENT_DIR, "pane.log")).mtimeMs;
20962
+ const paneMtimeMs = statSync2(join11(AGENT_DIR, "pane.log")).mtimeMs;
20934
20963
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
20935
20964
  } catch {
20936
20965
  }
package/dist/mcp/xero.js CHANGED
@@ -114409,7 +114409,8 @@ var BrokerCredentialClient = class {
114409
114409
  accessToken: res.data.access_token,
114410
114410
  tenantId: res.data.tenant_id ?? null,
114411
114411
  expiresAt: res.data.expires_at ?? null,
114412
- grantedScopes: res.data.granted_scopes ?? null
114412
+ grantedScopes: res.data.granted_scopes ?? null,
114413
+ effectiveScopes: res.data.effective_scopes ?? null
114413
114414
  };
114414
114415
  } catch (err) {
114415
114416
  if (err.status !== void 0) throw err;
@@ -120511,38 +120512,80 @@ function writesGoViaBroker() {
120511
120512
  const raw = (process.env.XERO_WRITES_VIA_BROKER ?? "").trim().toLowerCase();
120512
120513
  return raw === "true" || raw === "1" || raw === "yes";
120513
120514
  }
120514
- function ToolFactory(server) {
120515
+ var SCOPE_GATES = [
120516
+ { family: /payroll/i, requiresScope: "payroll" }
120517
+ ];
120518
+ function makeToolGate(effectiveScopes) {
120519
+ if (effectiveScopes == null) return () => true;
120520
+ const scopes = effectiveScopes.toLowerCase();
120521
+ return (toolName) => {
120522
+ for (const gate of SCOPE_GATES) {
120523
+ if (gate.family.test(toolName) && !scopes.includes(gate.requiresScope)) {
120524
+ return false;
120525
+ }
120526
+ }
120527
+ return true;
120528
+ };
120529
+ }
120530
+ function ToolFactory(server, effectiveScopes = null) {
120515
120531
  const readOnly = writesGoViaBroker();
120532
+ const allow = makeToolGate(effectiveScopes);
120516
120533
  if (readOnly) {
120517
120534
  console.error(
120518
120535
  "xero-mcp-server: XERO_WRITES_VIA_BROKER is set \u2014 registering read-only tools only (Get/List). Create/Update/Delete are gated through the Augmented xero-broker (HITL)."
120519
120536
  );
120520
120537
  }
120521
- GetTools.map((tool) => tool()).forEach(
120522
- (tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler)
120523
- );
120524
- ListTools.map((tool) => tool()).forEach(
120525
- (tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler)
120526
- );
120538
+ if (effectiveScopes != null && !effectiveScopes.toLowerCase().includes("payroll")) {
120539
+ console.error(
120540
+ "xero-mcp-server: agent policy excludes payroll scopes \u2014 payroll tools not registered (ENG-7824)."
120541
+ );
120542
+ }
120543
+ GetTools.map((tool) => tool()).filter((tool) => allow(tool.name)).forEach((tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler));
120544
+ ListTools.map((tool) => tool()).filter((tool) => allow(tool.name)).forEach((tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler));
120527
120545
  AttachmentTools.map((tool) => tool()).forEach(
120528
120546
  (tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler)
120529
120547
  );
120530
120548
  if (readOnly) return;
120531
- DeleteTools.map((tool) => tool()).forEach(
120532
- (tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler)
120533
- );
120534
- CreateTools.map((tool) => tool()).forEach(
120535
- (tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler)
120536
- );
120537
- UpdateTools.map((tool) => tool()).forEach(
120538
- (tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler)
120539
- );
120549
+ DeleteTools.map((tool) => tool()).filter((tool) => allow(tool.name)).forEach((tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler));
120550
+ CreateTools.map((tool) => tool()).filter((tool) => allow(tool.name)).forEach((tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler));
120551
+ UpdateTools.map((tool) => tool()).filter((tool) => allow(tool.name)).forEach((tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler));
120540
120552
  }
120541
120553
 
120542
120554
  // src/index.ts
120555
+ async function resolveEffectiveScopes() {
120556
+ const host = process.env.AGT_HOST;
120557
+ const agentId = process.env.AGT_AGENT_ID;
120558
+ const integrationId = process.env.AGT_INTEGRATION_ID;
120559
+ const initialToken = process.env.AGT_TOKEN;
120560
+ const apiKey = process.env.AGT_API_KEY;
120561
+ const brokerMode = Boolean(host && agentId && integrationId && (initialToken || apiKey));
120562
+ if (!brokerMode) return null;
120563
+ try {
120564
+ const client = new BrokerCredentialClient({
120565
+ host,
120566
+ initialToken,
120567
+ apiKey,
120568
+ agentId,
120569
+ integrationId
120570
+ });
120571
+ const cred = await Promise.race([
120572
+ client.fetchCredential(),
120573
+ new Promise(
120574
+ (_, reject) => setTimeout(() => reject(new Error("effective-scope resolution timed out")), 8e3)
120575
+ )
120576
+ ]);
120577
+ return cred.effectiveScopes;
120578
+ } catch (err) {
120579
+ console.error(
120580
+ `xero-mcp-server: effective-scope resolution failed, registering all tools (fail-open): ${err.message}`
120581
+ );
120582
+ return null;
120583
+ }
120584
+ }
120543
120585
  var main = async () => {
120544
120586
  const server = XeroMcpServer.GetServer();
120545
- ToolFactory(server);
120587
+ const effectiveScopes = await resolveEffectiveScopes();
120588
+ ToolFactory(server, effectiveScopes);
120546
120589
  const transport = new StdioServerTransport();
120547
120590
  await server.connect(transport);
120548
120591
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.329",
3
+ "version": "0.28.331",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {