switchroom 0.19.29 → 0.19.31

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 (43) hide show
  1. package/dist/cli/switchroom.js +2044 -900
  2. package/dist/host-control/main.js +162 -80
  3. package/package.json +1 -1
  4. package/profiles/_base/cron-session.sh.hbs +5 -1
  5. package/profiles/_base/start.sh.hbs +15 -1
  6. package/telegram-plugin/dist/gateway/gateway.js +1435 -551
  7. package/telegram-plugin/edit-flood-fuse.ts +70 -20
  8. package/telegram-plugin/gateway/boot-beacon.ts +364 -0
  9. package/telegram-plugin/gateway/boot-sweep-gate.ts +20 -15
  10. package/telegram-plugin/gateway/gateway.ts +87 -88
  11. package/telegram-plugin/gateway/inbound-spool.ts +39 -0
  12. package/telegram-plugin/gateway/narrative-lane.ts +12 -0
  13. package/telegram-plugin/gateway/obligation-store.ts +28 -0
  14. package/telegram-plugin/gateway/stale-pin-sweep-store.ts +221 -0
  15. package/telegram-plugin/gateway/stale-pin-sweep-wiring.ts +211 -0
  16. package/telegram-plugin/gateway/stale-pin-sweep.test.ts +804 -0
  17. package/telegram-plugin/gateway/stale-pin-sweep.ts +1146 -0
  18. package/telegram-plugin/gateway/status-pin-retarget.ts +15 -2
  19. package/telegram-plugin/gateway/status-pin-store.ts +33 -11
  20. package/telegram-plugin/registry/turns-schema.ts +21 -1
  21. package/telegram-plugin/retry-api-call.ts +46 -21
  22. package/telegram-plugin/shared/bot-runtime.ts +61 -17
  23. package/telegram-plugin/shared/gw-trace-gate.ts +18 -2
  24. package/telegram-plugin/tests/activity-card-wiring.test.ts +7 -7
  25. package/telegram-plugin/tests/activity-drain-fuse-drop-not-failure.test.ts +324 -0
  26. package/telegram-plugin/tests/agent-card-result-footer.test.ts +193 -0
  27. package/telegram-plugin/tests/boot-beacon.test.ts +462 -0
  28. package/telegram-plugin/tests/boot-pin-sweep-wiring.test.ts +6 -6
  29. package/telegram-plugin/tests/boot-sweep-gate.test.ts +42 -31
  30. package/telegram-plugin/tests/inbound-delivery-machine-dispatch.test.ts +2 -0
  31. package/telegram-plugin/tests/inbound-spool-progress.test.ts +2 -0
  32. package/telegram-plugin/tests/inbound-spool.test.ts +134 -5
  33. package/telegram-plugin/tests/narrative-lane-golden.test.ts +28 -0
  34. package/telegram-plugin/tests/obligation-determinism.test.ts +2 -0
  35. package/telegram-plugin/tests/obligation-store.test.ts +67 -1
  36. package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +3 -3
  37. package/telegram-plugin/tests/status-pin-store.test.ts +26 -5
  38. package/telegram-plugin/tests/tg-post-logger-error-shape.test.ts +161 -0
  39. package/telegram-plugin/tests/worker-feed-pin-persistence.test.ts +30 -0
  40. package/telegram-plugin/tool-activity-summary.ts +104 -38
  41. package/telegram-plugin/worker-activity-feed.ts +33 -16
  42. package/telegram-plugin/gateway/dm-pin-sweep.test.ts +0 -251
  43. package/telegram-plugin/gateway/dm-pin-sweep.ts +0 -178
@@ -13658,7 +13658,7 @@ import {
13658
13658
  mkdirSync as mkdirSync6,
13659
13659
  chmodSync as chmodSync2,
13660
13660
  unlinkSync as unlinkSync3,
13661
- renameSync as renameSync5
13661
+ renameSync as renameSync6
13662
13662
  } from "node:fs";
13663
13663
  import { dirname as dirname5, join as join5 } from "node:path";
13664
13664
  function floodStatePath(stateDir) {
@@ -23318,17 +23318,17 @@ var init_loader = __esm(() => {
23318
23318
  var init_flock = () => {};
23319
23319
 
23320
23320
  // ../src/vault/vault.ts
23321
- import { randomBytes as randomBytes3, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
23321
+ import { randomBytes as randomBytes4, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
23322
23322
  import {
23323
23323
  readFileSync as readFileSync8,
23324
- writeSync,
23324
+ writeSync as writeSync2,
23325
23325
  existsSync as existsSync7,
23326
- renameSync as renameSync6,
23326
+ renameSync as renameSync7,
23327
23327
  mkdirSync as mkdirSync9,
23328
23328
  unlinkSync as unlinkSync5,
23329
- fsyncSync,
23330
- openSync,
23331
- closeSync,
23329
+ fsyncSync as fsyncSync2,
23330
+ openSync as openSync2,
23331
+ closeSync as closeSync2,
23332
23332
  lstatSync,
23333
23333
  realpathSync
23334
23334
  } from "node:fs";
@@ -23414,13 +23414,13 @@ var init_vault = __esm(() => {
23414
23414
  // ../src/vault/resolver.ts
23415
23415
  import {
23416
23416
  chmodSync as chmodSync4,
23417
- closeSync as closeSync2,
23417
+ closeSync as closeSync3,
23418
23418
  mkdirSync as mkdirSync10,
23419
23419
  mkdtempSync,
23420
- openSync as openSync2,
23421
- rmSync,
23420
+ openSync as openSync3,
23421
+ rmSync as rmSync2,
23422
23422
  statSync as statSync6,
23423
- writeSync as writeSync2
23423
+ writeSync as writeSync3
23424
23424
  } from "node:fs";
23425
23425
  import { join as join8 } from "node:path";
23426
23426
  import { tmpdir } from "node:os";
@@ -23448,13 +23448,13 @@ function registerCleanupHook() {
23448
23448
  const cleanup = () => {
23449
23449
  for (const dir of materializedDirs) {
23450
23450
  try {
23451
- rmSync(dir, { recursive: true, force: true });
23451
+ rmSync2(dir, { recursive: true, force: true });
23452
23452
  } catch {}
23453
23453
  }
23454
23454
  materializedDirs.clear();
23455
23455
  if (cachedRoot) {
23456
23456
  try {
23457
- rmSync(cachedRoot, { recursive: true, force: true });
23457
+ rmSync2(cachedRoot, { recursive: true, force: true });
23458
23458
  } catch {}
23459
23459
  cachedRoot = null;
23460
23460
  }
@@ -23485,18 +23485,18 @@ function materializationRoot() {
23485
23485
  }
23486
23486
  function writeFileExclusive(filePath, content) {
23487
23487
  const buf = typeof content === "string" ? Buffer.from(content, "utf8") : content;
23488
- const fd = openSync2(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, 384);
23488
+ const fd = openSync3(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, 384);
23489
23489
  try {
23490
- writeSync2(fd, buf);
23490
+ writeSync3(fd, buf);
23491
23491
  } finally {
23492
- closeSync2(fd);
23492
+ closeSync3(fd);
23493
23493
  }
23494
23494
  }
23495
23495
  function materializeFilesEntry(key, files) {
23496
23496
  const dir = join8(materializationRoot(), key);
23497
23497
  if (materializedDirs.has(dir)) {
23498
23498
  try {
23499
- rmSync(dir, { recursive: true, force: true });
23499
+ rmSync2(dir, { recursive: true, force: true });
23500
23500
  } catch {}
23501
23501
  }
23502
23502
  mkdirSync10(dir, { recursive: true, mode: 448 });
@@ -23637,21 +23637,21 @@ var init_resolver = __esm(() => {
23637
23637
  });
23638
23638
 
23639
23639
  // ../src/util/atomic.ts
23640
- import { randomBytes as randomBytes4 } from "node:crypto";
23641
- import { closeSync as closeSync3, constants, fchmodSync, fchownSync, fsyncSync as fsyncSync2, openSync as openSync3, renameSync as renameSync7, rmSync as rmSync2, writeSync as writeSync3 } from "node:fs";
23642
- function atomicWriteFileSync(destPath, contents, modeOrOpts = 384) {
23640
+ import { randomBytes as randomBytes5 } from "node:crypto";
23641
+ import { closeSync as closeSync4, constants as constants2, fchmodSync as fchmodSync2, fchownSync as fchownSync2, fsyncSync as fsyncSync3, openSync as openSync4, renameSync as renameSync8, rmSync as rmSync3, writeSync as writeSync4 } from "node:fs";
23642
+ function atomicWriteFileSync2(destPath, contents, modeOrOpts = 384) {
23643
23643
  const opts = typeof modeOrOpts === "number" ? { mode: modeOrOpts } : modeOrOpts;
23644
23644
  const mode = opts.mode ?? 384;
23645
- const tmp = `${destPath}.tmp-${process.pid}-${randomBytes4(4).toString("hex")}`;
23645
+ const tmp = `${destPath}.tmp-${process.pid}-${randomBytes5(4).toString("hex")}`;
23646
23646
  const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
23647
23647
  let fd = null;
23648
23648
  try {
23649
- fd = openSync3(tmp, TMP_OPEN_FLAGS, mode);
23650
- writeSync3(fd, buf, 0, buf.length, 0);
23651
- fchmodSync(fd, mode);
23649
+ fd = openSync4(tmp, TMP_OPEN_FLAGS2, mode);
23650
+ writeSync4(fd, buf, 0, buf.length, 0);
23651
+ fchmodSync2(fd, mode);
23652
23652
  if (opts.uid !== undefined) {
23653
23653
  try {
23654
- fchownSync(fd, opts.uid, opts.gid ?? opts.uid);
23654
+ fchownSync2(fd, opts.uid, opts.gid ?? opts.uid);
23655
23655
  } catch (chownErr) {
23656
23656
  if (opts.onChownError)
23657
23657
  opts.onChownError(chownErr);
@@ -23659,25 +23659,25 @@ function atomicWriteFileSync(destPath, contents, modeOrOpts = 384) {
23659
23659
  throw chownErr;
23660
23660
  }
23661
23661
  }
23662
- fsyncSync2(fd);
23663
- closeSync3(fd);
23662
+ fsyncSync3(fd);
23663
+ closeSync4(fd);
23664
23664
  fd = null;
23665
- renameSync7(tmp, destPath);
23665
+ renameSync8(tmp, destPath);
23666
23666
  } catch (err) {
23667
23667
  if (fd !== null) {
23668
23668
  try {
23669
- closeSync3(fd);
23669
+ closeSync4(fd);
23670
23670
  } catch {}
23671
23671
  }
23672
23672
  try {
23673
- rmSync2(tmp, { force: true });
23673
+ rmSync3(tmp, { force: true });
23674
23674
  } catch {}
23675
23675
  throw err;
23676
23676
  }
23677
23677
  }
23678
- var TMP_OPEN_FLAGS;
23678
+ var TMP_OPEN_FLAGS2;
23679
23679
  var init_atomic = __esm(() => {
23680
- TMP_OPEN_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
23680
+ TMP_OPEN_FLAGS2 = constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | (constants2.O_NOFOLLOW ?? 0);
23681
23681
  });
23682
23682
 
23683
23683
  // gateway/approval-card.ts
@@ -35701,7 +35701,7 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
35701
35701
 
35702
35702
  // gateway/quota-cache.ts
35703
35703
  import { existsSync as existsSync44, readFileSync as readFileSync44, writeFileSync as writeFileSync37, mkdirSync as mkdirSync39 } from "fs";
35704
- import { join as join53, dirname as dirname20 } from "path";
35704
+ import { join as join53, dirname as dirname22 } from "path";
35705
35705
  function defaultCachePath() {
35706
35706
  return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join53(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
35707
35707
  }
@@ -35738,7 +35738,7 @@ function writeQuotaCache(result, opts = {}) {
35738
35738
  result
35739
35739
  };
35740
35740
  try {
35741
- mkdirSync39(dirname20(path3), { recursive: true });
35741
+ mkdirSync39(dirname22(path3), { recursive: true });
35742
35742
  writeFileSync37(path3, JSON.stringify(entry, null, 2), { mode: 384 });
35743
35743
  } catch {}
35744
35744
  }
@@ -36610,8 +36610,8 @@ var init_boot_probes = __esm(() => {
36610
36610
  });
36611
36611
 
36612
36612
  // gateway/boot-issue-cache.ts
36613
- import { existsSync as existsSync46, readFileSync as readFileSync46, writeFileSync as writeFileSync38, mkdirSync as mkdirSync40, renameSync as renameSync21 } from "fs";
36614
- import { dirname as dirname21 } from "path";
36613
+ import { existsSync as existsSync46, readFileSync as readFileSync46, writeFileSync as writeFileSync38, mkdirSync as mkdirSync40, renameSync as renameSync22 } from "fs";
36614
+ import { dirname as dirname23 } from "path";
36615
36615
  function fingerprintProbe(key, r) {
36616
36616
  if (r.status === "ok")
36617
36617
  return `${key}:ok`;
@@ -36703,7 +36703,7 @@ function loadCache(path3, now = Date.now) {
36703
36703
  parsed = JSON.parse(raw);
36704
36704
  } catch {
36705
36705
  try {
36706
- renameSync21(path3, `${path3}.corrupt-${now()}`);
36706
+ renameSync22(path3, `${path3}.corrupt-${now()}`);
36707
36707
  } catch {}
36708
36708
  return { ...EMPTY_CACHE, probes: {} };
36709
36709
  }
@@ -36737,10 +36737,10 @@ function applyAndSave(path3, cache, diff) {
36737
36737
  }
36738
36738
  }
36739
36739
  try {
36740
- mkdirSync40(dirname21(path3), { recursive: true });
36740
+ mkdirSync40(dirname23(path3), { recursive: true });
36741
36741
  const tmp = `${path3}.tmp`;
36742
36742
  writeFileSync38(tmp, JSON.stringify(next), { mode: 384 });
36743
- renameSync21(tmp, path3);
36743
+ renameSync22(tmp, path3);
36744
36744
  } catch {}
36745
36745
  return next;
36746
36746
  }
@@ -36753,8 +36753,8 @@ var init_boot_issue_cache = __esm(() => {
36753
36753
 
36754
36754
  // gateway/config-snapshot.ts
36755
36755
  import { createHash as createHash7 } from "crypto";
36756
- import { existsSync as existsSync47, readFileSync as readFileSync47, writeFileSync as writeFileSync39, mkdirSync as mkdirSync41, renameSync as renameSync22 } from "fs";
36757
- import { dirname as dirname22 } from "path";
36756
+ import { existsSync as existsSync47, readFileSync as readFileSync47, writeFileSync as writeFileSync39, mkdirSync as mkdirSync41, renameSync as renameSync23 } from "fs";
36757
+ import { dirname as dirname24 } from "path";
36758
36758
  function hashStringArray(items) {
36759
36759
  if (!items || items.length === 0)
36760
36760
  return null;
@@ -36831,7 +36831,7 @@ function loadSnapshot(path3, now = Date.now) {
36831
36831
  parsed = JSON.parse(raw);
36832
36832
  } catch {
36833
36833
  try {
36834
- renameSync22(path3, `${path3}.corrupt-${now()}`);
36834
+ renameSync23(path3, `${path3}.corrupt-${now()}`);
36835
36835
  } catch {}
36836
36836
  return null;
36837
36837
  }
@@ -36852,10 +36852,10 @@ function loadSnapshot(path3, now = Date.now) {
36852
36852
  }
36853
36853
  function persistSnapshot(path3, snapshot) {
36854
36854
  try {
36855
- mkdirSync41(dirname22(path3), { recursive: true });
36855
+ mkdirSync41(dirname24(path3), { recursive: true });
36856
36856
  const tmp = `${path3}.tmp`;
36857
36857
  writeFileSync39(tmp, JSON.stringify(snapshot), { mode: 384 });
36858
- renameSync22(tmp, path3);
36858
+ renameSync23(tmp, path3);
36859
36859
  } catch {}
36860
36860
  }
36861
36861
  var init_config_snapshot = __esm(() => {
@@ -37920,7 +37920,7 @@ __export(exports_tmux2, {
37920
37920
  captureAgentPane: () => captureAgentPane2
37921
37921
  });
37922
37922
  import { execFileSync as execFileSync8 } from "node:child_process";
37923
- import { chmodSync as chmodSync13, mkdirSync as mkdirSync49, readdirSync as readdirSync15, statSync as statSync21, unlinkSync as unlinkSync28, writeFileSync as writeFileSync48 } from "node:fs";
37923
+ import { chmodSync as chmodSync13, mkdirSync as mkdirSync50, readdirSync as readdirSync15, statSync as statSync21, unlinkSync as unlinkSync29, writeFileSync as writeFileSync49 } from "node:fs";
37924
37924
  import { resolve as resolve12 } from "node:path";
37925
37925
  function captureAgentPane2(opts) {
37926
37926
  const { agentName: agentName3, agentDir, reason } = opts;
@@ -37932,7 +37932,7 @@ function captureAgentPane2(opts) {
37932
37932
  const reasonSlug = sanitizeReason2(reason);
37933
37933
  const outPath = resolve12(outDir, `${ts}-${reasonSlug}.txt`);
37934
37934
  try {
37935
- mkdirSync49(outDir, { recursive: true, mode: 448 });
37935
+ mkdirSync50(outDir, { recursive: true, mode: 448 });
37936
37936
  } catch (err) {
37937
37937
  const msg = `mkdir crash-reports failed: ${err.message}`;
37938
37938
  console.error(`[tmux-capture] ${agentName3}: ${msg}`);
@@ -37971,7 +37971,7 @@ function captureAgentPane2(opts) {
37971
37971
  ` + `
37972
37972
  `;
37973
37973
  try {
37974
- writeFileSync48(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
37974
+ writeFileSync49(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
37975
37975
  mode: 384
37976
37976
  });
37977
37977
  } catch (err) {
@@ -38055,7 +38055,7 @@ function pruneOldReports2(dir, retain) {
38055
38055
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
38056
38056
  for (const stale of files.slice(retain)) {
38057
38057
  try {
38058
- unlinkSync28(stale.full);
38058
+ unlinkSync29(stale.full);
38059
38059
  } catch {}
38060
38060
  }
38061
38061
  }
@@ -38078,9 +38078,9 @@ __export(exports_config_approval_handler, {
38078
38078
  _peekPendingConfigApprovalForTest: () => _peekPendingConfigApprovalForTest,
38079
38079
  DIFF_SENTINEL: () => DIFF_SENTINEL
38080
38080
  });
38081
- import { randomBytes as randomBytes11 } from "node:crypto";
38081
+ import { randomBytes as randomBytes12 } from "node:crypto";
38082
38082
  function defaultEpoch() {
38083
- return randomBytes11(4).toString("hex");
38083
+ return randomBytes12(4).toString("hex");
38084
38084
  }
38085
38085
  function truncateDiffForCard(unifiedDiff, maxLines = 50, maxChars = 3000) {
38086
38086
  const sentinel = `
@@ -39021,29 +39021,80 @@ var init_approvals_commands = __esm(() => {
39021
39021
  // gateway/gateway.ts
39022
39022
  var import_grammy16 = __toESM(require_mod(), 1);
39023
39023
  var import_runner3 = __toESM(require_mod3(), 1);
39024
- import { randomBytes as randomBytes12, createHash as createHash8 } from "crypto";
39024
+ import { randomBytes as randomBytes13, createHash as createHash8 } from "crypto";
39025
39025
  import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
39026
39026
  import {
39027
- readFileSync as readFileSync59,
39028
- writeFileSync as writeFileSync49,
39029
- mkdirSync as mkdirSync50,
39027
+ readFileSync as readFileSync60,
39028
+ writeFileSync as writeFileSync50,
39029
+ mkdirSync as mkdirSync51,
39030
39030
  readdirSync as readdirSync16,
39031
- rmSync as rmSync7,
39031
+ rmSync as rmSync8,
39032
39032
  statSync as statSync22,
39033
- renameSync as renameSync25,
39033
+ renameSync as renameSync27,
39034
39034
  realpathSync as realpathSync4,
39035
39035
  chmodSync as chmodSync14,
39036
- openSync as openSync13,
39037
- closeSync as closeSync13,
39036
+ openSync as openSync15,
39037
+ closeSync as closeSync15,
39038
39038
  existsSync as existsSync57,
39039
- unlinkSync as unlinkSync29,
39039
+ unlinkSync as unlinkSync30,
39040
39040
  appendFileSync as appendFileSync9
39041
39041
  } from "fs";
39042
+
39043
+ // ../src/util/atomic.ts
39044
+ import { randomBytes } from "node:crypto";
39045
+ import { closeSync, constants, fchmodSync, fchownSync, fsyncSync, openSync, renameSync, rmSync, writeSync } from "node:fs";
39046
+ var TMP_OPEN_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
39047
+ function atomicWriteFileSync(destPath, contents, modeOrOpts = 384) {
39048
+ const opts = typeof modeOrOpts === "number" ? { mode: modeOrOpts } : modeOrOpts;
39049
+ const mode = opts.mode ?? 384;
39050
+ const tmp = `${destPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
39051
+ const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
39052
+ let fd = null;
39053
+ try {
39054
+ fd = openSync(tmp, TMP_OPEN_FLAGS, mode);
39055
+ writeSync(fd, buf, 0, buf.length, 0);
39056
+ fchmodSync(fd, mode);
39057
+ if (opts.uid !== undefined) {
39058
+ try {
39059
+ fchownSync(fd, opts.uid, opts.gid ?? opts.uid);
39060
+ } catch (chownErr) {
39061
+ if (opts.onChownError)
39062
+ opts.onChownError(chownErr);
39063
+ else
39064
+ throw chownErr;
39065
+ }
39066
+ }
39067
+ fsyncSync(fd);
39068
+ closeSync(fd);
39069
+ fd = null;
39070
+ renameSync(tmp, destPath);
39071
+ } catch (err) {
39072
+ if (fd !== null) {
39073
+ try {
39074
+ closeSync(fd);
39075
+ } catch {}
39076
+ }
39077
+ try {
39078
+ rmSync(tmp, { force: true });
39079
+ } catch {}
39080
+ throw err;
39081
+ }
39082
+ }
39083
+ function fsyncPathSync(path) {
39084
+ const fd = openSync(path, constants.O_RDONLY);
39085
+ try {
39086
+ fsyncSync(fd);
39087
+ } finally {
39088
+ closeSync(fd);
39089
+ }
39090
+ }
39091
+
39092
+ // gateway/gateway.ts
39042
39093
  import { homedir as homedir19 } from "os";
39043
- import { join as join67, sep as sep4, basename as basename17 } from "path";
39094
+ import { join as join68, sep as sep4, basename as basename17 } from "path";
39044
39095
 
39045
39096
  // plugin-logger.ts
39046
- import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
39097
+ import { appendFileSync, mkdirSync, renameSync as renameSync2, statSync, existsSync } from "fs";
39047
39098
  import { homedir } from "os";
39048
39099
  import { dirname, join } from "path";
39049
39100
  var DEFAULT_LOG_PATH = join(homedir(), ".switchroom", "logs", "telegram-plugin.log");
@@ -39071,11 +39122,11 @@ function rotateIfNeeded(path) {
39071
39122
  const dst = `${path}.${i + 1}`;
39072
39123
  try {
39073
39124
  if (existsSync(src))
39074
- renameSync(src, dst);
39125
+ renameSync2(src, dst);
39075
39126
  } catch {}
39076
39127
  }
39077
39128
  const backup = `${path}.1`;
39078
- renameSync(path, backup);
39129
+ renameSync2(path, backup);
39079
39130
  } catch {}
39080
39131
  }
39081
39132
  function installPluginLogger(env = process.env) {
@@ -39182,7 +39233,7 @@ function redactAuthCodeMessage(api, chatId, messageId, log) {
39182
39233
  }
39183
39234
 
39184
39235
  // ask-user.ts
39185
- import { randomBytes } from "crypto";
39236
+ import { randomBytes as randomBytes2 } from "crypto";
39186
39237
  var ASK_USER_DEFAULT_TIMEOUT_MS = 300000;
39187
39238
  var ASK_USER_MAX_TIMEOUT_MS = 1800000;
39188
39239
  var ASK_USER_MIN_TIMEOUT_MS = 5000;
@@ -39244,7 +39295,7 @@ function validateAskUserArgs(args) {
39244
39295
  };
39245
39296
  }
39246
39297
  function generateAskId() {
39247
- return randomBytes(4).toString("hex");
39298
+ return randomBytes2(4).toString("hex");
39248
39299
  }
39249
39300
  function encodeAskCallback(askId, idx) {
39250
39301
  if (!/^[0-9a-f]{8}$/.test(askId)) {
@@ -39791,7 +39842,7 @@ async function synthesizeViaSidecar(args) {
39791
39842
  }
39792
39843
 
39793
39844
  // voice-ondemand.ts
39794
- import { readFileSync, writeFileSync, renameSync as renameSync2, mkdirSync as mkdirSync2 } from "fs";
39845
+ import { readFileSync, writeFileSync, renameSync as renameSync3, mkdirSync as mkdirSync2 } from "fs";
39795
39846
  import { dirname as dirname2 } from "path";
39796
39847
  var VOICE_ONDEMAND_TTL_MS = 7 * 24 * 60 * 60 * 1000;
39797
39848
  var VOICE_ONDEMAND_MAX_ENTRIES = 500;
@@ -39915,7 +39966,7 @@ class VoiceOnDemandCache {
39915
39966
  try {
39916
39967
  mkdirSync2(dirname2(this.persistPath), { recursive: true });
39917
39968
  writeFileSync(tmp, JSON.stringify(doc), "utf8");
39918
- renameSync2(tmp, this.persistPath);
39969
+ renameSync3(tmp, this.persistPath);
39919
39970
  } catch (err) {
39920
39971
  process.stderr.write(`voice-ondemand: failed to persist cache ${this.persistPath}: ${String(err)}
39921
39972
  `);
@@ -39924,8 +39975,8 @@ class VoiceOnDemandCache {
39924
39975
  }
39925
39976
 
39926
39977
  // voice-ondemand.ts
39927
- import { randomBytes as randomBytes2 } from "crypto";
39928
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync as renameSync3, mkdirSync as mkdirSync3 } from "fs";
39978
+ import { randomBytes as randomBytes3 } from "crypto";
39979
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync as renameSync4, mkdirSync as mkdirSync3 } from "fs";
39929
39980
  import { dirname as dirname3 } from "path";
39930
39981
  var VOICE_ONDEMAND_CALLBACK_PREFIX = "voice:";
39931
39982
  var VOICE_ONDEMAND_TTL_MS2 = 7 * 24 * 60 * 60 * 1000;
@@ -40050,7 +40101,7 @@ class VoiceOnDemandCache2 {
40050
40101
  try {
40051
40102
  mkdirSync3(dirname3(this.persistPath), { recursive: true });
40052
40103
  writeFileSync2(tmp, JSON.stringify(doc), "utf8");
40053
- renameSync3(tmp, this.persistPath);
40104
+ renameSync4(tmp, this.persistPath);
40054
40105
  } catch (err) {
40055
40106
  process.stderr.write(`voice-ondemand: failed to persist cache ${this.persistPath}: ${String(err)}
40056
40107
  `);
@@ -40058,7 +40109,7 @@ class VoiceOnDemandCache2 {
40058
40109
  }
40059
40110
  }
40060
40111
  function mintVoiceOnDemandToken() {
40061
- return randomBytes2(4).toString("hex");
40112
+ return randomBytes3(4).toString("hex");
40062
40113
  }
40063
40114
  function isVoiceOnDemandCallback(data) {
40064
40115
  return data.startsWith(VOICE_ONDEMAND_CALLBACK_PREFIX);
@@ -40124,7 +40175,7 @@ function makeOutboxListenMarkupResolver(deps) {
40124
40175
  }
40125
40176
 
40126
40177
  // voice-presynth.ts
40127
- import { readdirSync, statSync as statSync2, unlinkSync, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, renameSync as renameSync4 } from "fs";
40178
+ import { readdirSync, statSync as statSync2, unlinkSync, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, renameSync as renameSync5 } from "fs";
40128
40179
  import { join as join2 } from "path";
40129
40180
  var VOICE_FILE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
40130
40181
  var VOICE_CACHE_MAX_BYTES = 500 * 1024 * 1024;
@@ -40190,7 +40241,7 @@ function writeVoiceCacheFile(dir, token, audio) {
40190
40241
  const final = voiceCacheFilePath(dir, token);
40191
40242
  const tmp = `${final}.tmp`;
40192
40243
  writeFileSync3(tmp, audio);
40193
- renameSync4(tmp, final);
40244
+ renameSync5(tmp, final);
40194
40245
  return final;
40195
40246
  }
40196
40247
  function sweepVoiceCacheDir(options) {
@@ -42779,6 +42830,18 @@ import { AsyncLocalStorage } from "async_hooks";
42779
42830
 
42780
42831
  // retry-api-call.ts
42781
42832
  var import_grammy2 = __toESM(require_mod2(), 1);
42833
+ function classifyBenignTelegram400(errorCode, description) {
42834
+ if (errorCode !== 400)
42835
+ return null;
42836
+ const desc = (description ?? "").toLowerCase();
42837
+ if (desc.includes("not modified"))
42838
+ return "not_modified";
42839
+ if (desc.includes("message to edit not found"))
42840
+ return "message_not_found";
42841
+ if (desc.includes("message to delete not found"))
42842
+ return "delete_not_found";
42843
+ return null;
42844
+ }
42782
42845
  function isLocalResourceError(err) {
42783
42846
  const code = err?.code;
42784
42847
  if (typeof code === "string" && ["ENOSPC", "EDQUOT", "EIO", "ENOMEM"].includes(code)) {
@@ -42863,14 +42926,9 @@ function createRetryApiCall(config = {}) {
42863
42926
  await sleep(delayMs);
42864
42927
  continue;
42865
42928
  }
42866
- if (isGrammyErr && err.error_code === 400 && desc.includes("not modified")) {
42867
- observer?.onBenign?.({ kind: "not_modified" });
42868
- return;
42869
- }
42870
- if (isGrammyErr && err.error_code === 400 && (desc.includes("message to edit not found") || desc.includes("message to delete not found"))) {
42871
- observer?.onBenign?.({
42872
- kind: desc.includes("edit") ? "message_not_found" : "delete_not_found"
42873
- });
42929
+ const benignKind = isGrammyErr ? classifyBenignTelegram400(err.error_code, desc) : null;
42930
+ if (benignKind !== null) {
42931
+ observer?.onBenign?.({ kind: benignKind });
42874
42932
  return;
42875
42933
  }
42876
42934
  if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
@@ -44674,12 +44732,12 @@ async function handleVoiceMessage(ctx, deps) {
44674
44732
  }
44675
44733
 
44676
44734
  // gateway/status-pin-store.ts
44677
- var SNAPSHOT_VERSIONS = new Set([1, 2, 3]);
44735
+ var SNAPSHOT_VERSIONS = new Set([1, 2, 3, 4]);
44678
44736
  function isPinRow(x) {
44679
44737
  if (x == null || typeof x !== "object")
44680
44738
  return false;
44681
44739
  const o = x;
44682
- return typeof o.pinKey === "string" && o.pinKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && typeof o.messageId === "number" && (o.pending === undefined || typeof o.pending === "boolean") && (o.expiresAt === undefined || typeof o.expiresAt === "number") && (o.attempts === undefined || typeof o.attempts === "number") && (o.pinnedAt === undefined || typeof o.pinnedAt === "number");
44740
+ return typeof o.pinKey === "string" && o.pinKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && (o.threadId === undefined || typeof o.threadId === "number") && typeof o.messageId === "number" && (o.pending === undefined || typeof o.pending === "boolean") && (o.expiresAt === undefined || typeof o.expiresAt === "number") && (o.attempts === undefined || typeof o.attempts === "number") && (o.pinnedAt === undefined || typeof o.pinnedAt === "number");
44683
44741
  }
44684
44742
  function loadStatusPins(path, fs2) {
44685
44743
  if (!fs2.existsSync(path))
@@ -44704,7 +44762,7 @@ function loadStatusPins(path, fs2) {
44704
44762
  return env.pins.filter(isPinRow);
44705
44763
  }
44706
44764
  function persistStatusPins(path, fs2, snapshot, log = (l) => process.stderr.write(l)) {
44707
- const env = { v: 3, pins: [...snapshot] };
44765
+ const env = { v: 4, pins: [...snapshot] };
44708
44766
  const tmp = path + ".tmp";
44709
44767
  try {
44710
44768
  fs2.writeFileSync(tmp, JSON.stringify(env));
@@ -44740,7 +44798,7 @@ function applyStatusPinRow(path, fs2, pinKey, row, log) {
44740
44798
  persistStatusPins(path, fs2, next, log);
44741
44799
  }
44742
44800
  function reconcileAndPersistStatusPin(args) {
44743
- const { path, fs: fs2, pinKey, chatId, op } = args;
44801
+ const { path, fs: fs2, pinKey, chatId, threadId, op } = args;
44744
44802
  const log = args.log ?? ((l) => process.stderr.write(l));
44745
44803
  const now = args.now ?? Date.now();
44746
44804
  const claimAge = (messageId) => {
@@ -44750,20 +44808,20 @@ function reconcileAndPersistStatusPin(args) {
44750
44808
  return withStoreLock(path, async () => {
44751
44809
  if (op.kind === "pin") {
44752
44810
  const pinnedAt = claimAge(op.messageId);
44753
- applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, messageId: op.messageId, pending: true, pinnedAt }, log);
44811
+ applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, threadId, messageId: op.messageId, pending: true, pinnedAt }, log);
44754
44812
  const next2 = await args.applyPin();
44755
44813
  if (next2 == null) {
44756
44814
  applyStatusPinRow(path, fs2, pinKey, null, log);
44757
44815
  return null;
44758
44816
  }
44759
- applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, messageId: next2.messageId, pinnedAt: claimAge(next2.messageId) }, log);
44817
+ applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, threadId, messageId: next2.messageId, pinnedAt: claimAge(next2.messageId) }, log);
44760
44818
  return next2;
44761
44819
  }
44762
44820
  const next = await args.applyPin();
44763
44821
  if (next == null) {
44764
44822
  applyStatusPinRow(path, fs2, pinKey, null, log);
44765
44823
  } else {
44766
- applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, messageId: next.messageId, pinnedAt: claimAge(next.messageId) }, log);
44824
+ applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, threadId, messageId: next.messageId, pinnedAt: claimAge(next.messageId) }, log);
44767
44825
  }
44768
44826
  return next;
44769
44827
  });
@@ -47361,6 +47419,7 @@ if (isMain)
47361
47419
 
47362
47420
  // tool-activity-summary.ts
47363
47421
  init_card_format();
47422
+ init_redact();
47364
47423
 
47365
47424
  // tool-names.ts
47366
47425
  var TELEGRAM_TOOL_PREFIX_RE = /^mcp__[^_].*?telegram__/;
@@ -47491,12 +47550,21 @@ function renderStatusCard(opts) {
47491
47550
  }
47492
47551
  var WORKER_RESULT_RULE = "\u2500\u2500\u2500\u2500\u2500";
47493
47552
  var WORKER_RESULT_MAX = 320;
47494
- function renderActivityFeed(lines, final = false, liveSuffix = "", stepCount, header) {
47495
- if (lines.length === 0 && header == null)
47553
+ function deriveCardResult(state, summary) {
47554
+ if (state !== "done" && state !== "failed")
47555
+ return;
47556
+ const text = cleanWorkerResultParagraph(redact(summary ?? ""));
47557
+ if (text.length === 0)
47558
+ return;
47559
+ return { emoji: state === "done" ? "\u2705" : "\u26a0\ufe0f", text };
47560
+ }
47561
+ var AGENT_CARD_EMOJI = "\uD83E\uDD16";
47562
+ function renderAgentCard(lines, children, final, liveSuffix, stepCount, header) {
47563
+ if (lines.length === 0 && children.length === 0 && header == null)
47496
47564
  return null;
47497
47565
  return renderStatusCard({
47498
47566
  header: header != null ? {
47499
- emoji: "\uD83E\uDD16",
47567
+ emoji: AGENT_CARD_EMOJI,
47500
47568
  label: header.label,
47501
47569
  elapsedMs: header.elapsedMs,
47502
47570
  toolCount: header.toolCount,
@@ -47505,31 +47573,19 @@ function renderActivityFeed(lines, final = false, liveSuffix = "", stepCount, he
47505
47573
  totalTokens: header.totalTokens
47506
47574
  } : undefined,
47507
47575
  steps: lines,
47576
+ ...children.length > 0 ? { childSteps: children } : {},
47508
47577
  final,
47509
47578
  liveSuffix,
47510
- stepCount
47579
+ stepCount,
47580
+ result: final && header != null ? deriveCardResult(header.state, header.resultText) : undefined
47511
47581
  });
47512
47582
  }
47583
+ function renderActivityFeed(lines, final = false, liveSuffix = "", stepCount, header) {
47584
+ return renderAgentCard(lines, [], final, liveSuffix, stepCount, header);
47585
+ }
47513
47586
  function renderActivityFeedWithNested(lines, childLines, final = false, liveSuffix = "", stepCount, header) {
47514
47587
  const children = childLines.map((s) => s.trim()).filter((s) => s.length > 0);
47515
- if (children.length === 0)
47516
- return renderActivityFeed(lines, final, liveSuffix, stepCount, header);
47517
- return renderStatusCard({
47518
- header: header != null ? {
47519
- emoji: "\uD83E\uDD16",
47520
- label: header.label,
47521
- elapsedMs: header.elapsedMs,
47522
- toolCount: header.toolCount,
47523
- state: header.state,
47524
- model: header.model,
47525
- totalTokens: header.totalTokens
47526
- } : undefined,
47527
- steps: lines,
47528
- childSteps: children,
47529
- final,
47530
- liveSuffix,
47531
- stepCount
47532
- });
47588
+ return renderAgentCard(lines, children, final, liveSuffix, stepCount, header);
47533
47589
  }
47534
47590
  var COMBINED_ROW_DESC_MAX = 72;
47535
47591
  var PER_WORKER_HEADER_COST = 1;
@@ -47651,12 +47707,7 @@ function renderWorkerActivity(v, liveSuffix = "") {
47651
47707
  model: v.model,
47652
47708
  totalTokens: v.totalTokens
47653
47709
  };
47654
- let result;
47655
- if (finished && v.state !== "incomplete") {
47656
- const text = cleanWorkerResultParagraph(v.latestSummary);
47657
- if (text.length > 0)
47658
- result = { emoji: v.state === "done" ? "\u2705" : "\u26a0\ufe0f", text };
47659
- }
47710
+ const result = deriveCardResult(v.state, v.latestSummary);
47660
47711
  const card = renderStatusCard({
47661
47712
  header,
47662
47713
  steps,
@@ -47734,7 +47785,7 @@ function createWorkerActivityFeed(opts) {
47734
47785
  }
47735
47786
  let heartbeatTimer = null;
47736
47787
  function feedKeyOf(chatId, threadId) {
47737
- return `${chatId} ${threadId ?? ""}`;
47788
+ return `${chatId}:${threadId ?? "-"}`;
47738
47789
  }
47739
47790
  function groupOfAgent(agentId) {
47740
47791
  const key = agentIndex.get(agentId);
@@ -48768,8 +48819,8 @@ import { unlinkSync as unlinkSync7 } from "node:fs";
48768
48819
  import { join as join12 } from "node:path";
48769
48820
 
48770
48821
  // gateway/store-file.ts
48771
- import { randomBytes as randomBytes5 } from "node:crypto";
48772
- import { readFileSync as readFileSync12, readdirSync as readdirSync4, renameSync as renameSync8, rmSync as rmSync3, statSync as statSync7 } from "node:fs";
48822
+ import { randomBytes as randomBytes6 } from "node:crypto";
48823
+ import { readFileSync as readFileSync12, readdirSync as readdirSync4, renameSync as renameSync9, rmSync as rmSync4, statSync as statSync7 } from "node:fs";
48773
48824
  import { basename as basename6, dirname as dirname6, join as join11 } from "node:path";
48774
48825
  var defaultLog = (line) => {
48775
48826
  process.stderr.write(line);
@@ -48778,7 +48829,7 @@ var MAX_QUARANTINED_COPIES = 5;
48778
48829
  var quarantineSeq = 0;
48779
48830
  function quarantinePath(filePath) {
48780
48831
  const seq = String(quarantineSeq++).padStart(6, "0");
48781
- return `${filePath}.corrupt-${Date.now()}-${seq}-${randomBytes5(4).toString("hex")}`;
48832
+ return `${filePath}.corrupt-${Date.now()}-${seq}-${randomBytes6(4).toString("hex")}`;
48782
48833
  }
48783
48834
  function reapOldQuarantines(filePath, log) {
48784
48835
  const dir = dirname6(filePath);
@@ -48792,7 +48843,7 @@ function reapOldQuarantines(filePath, log) {
48792
48843
  return { name, mtimeNs };
48793
48844
  }).sort((a, b) => a.mtimeNs === b.mtimeNs ? a.name.localeCompare(b.name) : a.mtimeNs < b.mtimeNs ? -1 : 1);
48794
48845
  for (const stale of copies.slice(0, Math.max(0, copies.length - MAX_QUARANTINED_COPIES))) {
48795
- rmSync3(join11(dir, stale.name), { force: true });
48846
+ rmSync4(join11(dir, stale.name), { force: true });
48796
48847
  }
48797
48848
  } catch (err) {
48798
48849
  log(`telegram gateway: quarantine reap failed dir=${dir}: ${err.message}
@@ -48803,7 +48854,7 @@ function quarantineCorruptStoreFile(filePath, store2, reason, log = defaultLog)
48803
48854
  const target = quarantinePath(filePath);
48804
48855
  let preserved = target;
48805
48856
  try {
48806
- renameSync8(filePath, target);
48857
+ renameSync9(filePath, target);
48807
48858
  } catch (err) {
48808
48859
  preserved = `NOT preserved (${err.message})`;
48809
48860
  }
@@ -48814,7 +48865,7 @@ function quarantineCorruptStoreFile(filePath, store2, reason, log = defaultLog)
48814
48865
  function preserveUnreadableStoreFile(filePath, store2, log = defaultLog) {
48815
48866
  const target = quarantinePath(filePath);
48816
48867
  try {
48817
- renameSync8(filePath, target);
48868
+ renameSync9(filePath, target);
48818
48869
  } catch (err) {
48819
48870
  if (err.code === "ENOENT") {
48820
48871
  return;
@@ -48867,7 +48918,7 @@ function createPendingCardStore(stateDir, log = (l) => process.stderr.write(l))
48867
48918
  preserveUnreadableStoreFile(filePath, "pending-card-store", log);
48868
48919
  unreadable = false;
48869
48920
  }
48870
- atomicWriteFileSync(filePath, JSON.stringify(entries), 384);
48921
+ atomicWriteFileSync2(filePath, JSON.stringify(entries), 384);
48871
48922
  } catch (err) {
48872
48923
  log(`telegram gateway: pending-card-store write failed: ${err.message}
48873
48924
  `);
@@ -49517,12 +49568,12 @@ function buildSkillProposalApplyInbound(opts) {
49517
49568
 
49518
49569
  // ../src/self-improve/skill-proposals.ts
49519
49570
  import {
49520
- closeSync as closeSync4,
49571
+ closeSync as closeSync5,
49521
49572
  existsSync as existsSync10,
49522
49573
  mkdirSync as mkdirSync13,
49523
- openSync as openSync4,
49574
+ openSync as openSync5,
49524
49575
  readFileSync as readFileSync13,
49525
- writeSync as writeSync4
49576
+ writeSync as writeSync5
49526
49577
  } from "node:fs";
49527
49578
  import { join as join14 } from "node:path";
49528
49579
  var PROPOSALS_FILE = "skill-proposals.jsonl";
@@ -49540,12 +49591,12 @@ function ensureDir2(stateDir) {
49540
49591
  }
49541
49592
  }
49542
49593
  function appendLine(path, obj) {
49543
- const fd = openSync4(path, "a");
49594
+ const fd = openSync5(path, "a");
49544
49595
  try {
49545
- writeSync4(fd, JSON.stringify(obj) + `
49596
+ writeSync5(fd, JSON.stringify(obj) + `
49546
49597
  `);
49547
49598
  } finally {
49548
- closeSync4(fd);
49599
+ closeSync5(fd);
49549
49600
  }
49550
49601
  }
49551
49602
  function readLines(path, isValid2) {
@@ -49696,7 +49747,7 @@ async function addAccountViaBroker(label, credentials, opts = {}) {
49696
49747
 
49697
49748
  // gateway/hostd-dispatch.ts
49698
49749
  import { existsSync as existsSync11 } from "node:fs";
49699
- import { randomBytes as randomBytes6 } from "node:crypto";
49750
+ import { randomBytes as randomBytes7 } from "node:crypto";
49700
49751
 
49701
49752
  // ../src/host-control/client.ts
49702
49753
  import { connect } from "node:net";
@@ -50042,7 +50093,7 @@ async function tryHostdDispatch(agentName, req, timeoutMs = 5000) {
50042
50093
  }
50043
50094
  }
50044
50095
  function hostdRequestId(prefix) {
50045
- return `${prefix}-${Date.now()}-${randomBytes6(4).toString("hex")}`;
50096
+ return `${prefix}-${Date.now()}-${randomBytes7(4).toString("hex")}`;
50046
50097
  }
50047
50098
  async function hostdGetStatusOnce(agentName, targetRequestId) {
50048
50099
  if (!isHostdEnabled())
@@ -51684,7 +51735,7 @@ ${trimmed.replace(/```/g, "`\u200b``")}
51684
51735
 
51685
51736
  // gateway/card-tool-handlers.ts
51686
51737
  init_rich_send();
51687
- import { randomBytes as randomBytes7 } from "crypto";
51738
+ import { randomBytes as randomBytes8 } from "crypto";
51688
51739
 
51689
51740
  // gateway/vault-request-access-card.ts
51690
51741
  init_approval_card();
@@ -51819,7 +51870,7 @@ function createCardToolHandlers(deps) {
51819
51870
  throw new Error(`vault_request_save: key must match ${VAULT_KEY_REGEX_LABEL}`);
51820
51871
  }
51821
51872
  const agentSlug = process.env.SWITCHROOM_AGENT_NAME || "agent";
51822
- const stageId = randomBytes7(4).toString("hex");
51873
+ const stageId = randomBytes8(4).toString("hex");
51823
51874
  const pending = {
51824
51875
  agent: agentSlug,
51825
51876
  chat_id,
@@ -51909,7 +51960,7 @@ function createCardToolHandlers(deps) {
51909
51960
  }
51910
51961
  } catch {}
51911
51962
  }
51912
- const stageId = randomBytes7(4).toString("hex");
51963
+ const stageId = randomBytes8(4).toString("hex");
51913
51964
  const pending = {
51914
51965
  agent: agentSlug,
51915
51966
  chat_id,
@@ -51971,7 +52022,7 @@ function createCardToolHandlers(deps) {
51971
52022
  pendingCardStore.remove(sid);
51972
52023
  }
51973
52024
  }
51974
- const stageId = randomBytes7(4).toString("hex");
52025
+ const stageId = randomBytes8(4).toString("hex");
51975
52026
  const pending = { agent: agentSlug, chat_id, key, reason, staged_at: Date.now() };
51976
52027
  pendingSecretRequests.set(stageId, pending);
51977
52028
  sweepSecretRequests();
@@ -52070,7 +52121,7 @@ function createCardToolHandlers(deps) {
52070
52121
  process.stderr.write(`telegram gateway: mental_model_propose dup pre-check read failed: ${err.message}
52071
52122
  `);
52072
52123
  }
52073
- const stageId = randomBytes7(4).toString("hex");
52124
+ const stageId = randomBytes8(4).toString("hex");
52074
52125
  const pending = {
52075
52126
  agent: agentSlug,
52076
52127
  chat_id,
@@ -52271,7 +52322,7 @@ function createMissedApprovalsStore(stateDir, log = (l) => process.stderr.write(
52271
52322
  preserveUnreadableStoreFile(filePath, "missed-approvals-store", log);
52272
52323
  unreadable = false;
52273
52324
  }
52274
- atomicWriteFileSync(filePath, JSON.stringify(f), 384);
52325
+ atomicWriteFileSync2(filePath, JSON.stringify(f), 384);
52275
52326
  } catch (err) {
52276
52327
  log(`telegram gateway: missed-approvals-store write failed: ${err.message}
52277
52328
  `);
@@ -52351,7 +52402,7 @@ function computeBackoffMs(attempts, retryAfterMs) {
52351
52402
  }
52352
52403
  var atomicWriteSeam = (path, data, opts) => {
52353
52404
  const mode = typeof opts === "object" && opts !== null && typeof opts.mode === "number" ? opts.mode : 384;
52354
- atomicWriteFileSync(path, data, mode);
52405
+ atomicWriteFileSync2(path, data, mode);
52355
52406
  };
52356
52407
  function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn = atomicWriteSeam, log = (l) => process.stderr.write(l)) {
52357
52408
  const filePath = join17(stateDir, "always-allow-persist-queue.json");
@@ -52802,6 +52853,7 @@ function isTelegramSurfaceTool2(toolName) {
52802
52853
 
52803
52854
  // tool-activity-summary.ts
52804
52855
  init_card_format();
52856
+ init_redact();
52805
52857
  function clipNarrative2(s) {
52806
52858
  return s.split(`
52807
52859
  `)[0].trim().slice(0, STATUS_LINE_MAX);
@@ -55860,7 +55912,7 @@ var codes = {
55860
55912
  replacementCharacter: 65533
55861
55913
  };
55862
55914
  // ../node_modules/.bun/micromark-util-symbol@2.0.1/node_modules/micromark-util-symbol/lib/constants.js
55863
- var constants2 = {
55915
+ var constants3 = {
55864
55916
  attentionSideAfter: 2,
55865
55917
  attentionSideBefore: 1,
55866
55918
  atxHeadingOpeningFenceSizeMax: 6,
@@ -56116,7 +56168,7 @@ function splice(list, start, remove, items) {
56116
56168
  start = start > end ? end : start;
56117
56169
  }
56118
56170
  remove = remove > 0 ? remove : 0;
56119
- if (items.length < constants2.v8MaxSafeChunkSize) {
56171
+ if (items.length < constants3.v8MaxSafeChunkSize) {
56120
56172
  parameters = Array.from(items);
56121
56173
  parameters.unshift(start, remove);
56122
56174
  list.splice(...parameters);
@@ -56124,11 +56176,11 @@ function splice(list, start, remove, items) {
56124
56176
  if (remove)
56125
56177
  list.splice(start, remove);
56126
56178
  while (chunkStart < items.length) {
56127
- parameters = items.slice(chunkStart, chunkStart + constants2.v8MaxSafeChunkSize);
56179
+ parameters = items.slice(chunkStart, chunkStart + constants3.v8MaxSafeChunkSize);
56128
56180
  parameters.unshift(start, 0);
56129
56181
  list.splice(...parameters);
56130
- chunkStart += constants2.v8MaxSafeChunkSize;
56131
- start += constants2.v8MaxSafeChunkSize;
56182
+ chunkStart += constants3.v8MaxSafeChunkSize;
56183
+ start += constants3.v8MaxSafeChunkSize;
56132
56184
  }
56133
56185
  }
56134
56186
  }
@@ -56264,7 +56316,7 @@ function initializeContent(effects) {
56264
56316
  }
56265
56317
  function lineStart(code) {
56266
56318
  const token = effects.enter(types2.chunkText, {
56267
- contentType: constants2.contentTypeText,
56319
+ contentType: constants3.contentTypeText,
56268
56320
  previous
56269
56321
  });
56270
56322
  if (previous) {
@@ -56386,7 +56438,7 @@ function initializeDocument(effects) {
56386
56438
  childFlow = childFlow || self.parser.flow(self.now());
56387
56439
  effects.enter(types2.chunkFlow, {
56388
56440
  _tokenizer: childFlow,
56389
- contentType: constants2.contentTypeFlow,
56441
+ contentType: constants3.contentTypeFlow,
56390
56442
  previous: childToken
56391
56443
  });
56392
56444
  return flowContinue(code);
@@ -56471,16 +56523,16 @@ function initializeDocument(effects) {
56471
56523
  }
56472
56524
  function tokenizeContainer(effects, ok2, nok) {
56473
56525
  ok(this.parser.constructs.disable.null, "expected `disable.null` to be populated");
56474
- return factorySpace(effects, effects.attempt(this.parser.constructs.document, ok2, nok), types2.linePrefix, this.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants2.tabSize);
56526
+ return factorySpace(effects, effects.attempt(this.parser.constructs.document, ok2, nok), types2.linePrefix, this.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants3.tabSize);
56475
56527
  }
56476
56528
 
56477
56529
  // ../node_modules/.bun/micromark-util-classify-character@2.0.1/node_modules/micromark-util-classify-character/dev/index.js
56478
56530
  function classifyCharacter(code) {
56479
56531
  if (code === codes.eof || markdownLineEndingOrSpace(code) || unicodeWhitespace(code)) {
56480
- return constants2.characterGroupWhitespace;
56532
+ return constants3.characterGroupWhitespace;
56481
56533
  }
56482
56534
  if (unicodePunctuation(code)) {
56483
- return constants2.characterGroupPunctuation;
56535
+ return constants3.characterGroupPunctuation;
56484
56536
  }
56485
56537
  }
56486
56538
 
@@ -56614,8 +56666,8 @@ function tokenizeAttention(effects, ok2) {
56614
56666
  const token = effects.exit("attentionSequence");
56615
56667
  const after = classifyCharacter(code);
56616
56668
  ok(attentionMarkers, "expected `attentionMarkers` to be populated");
56617
- const open = !after || after === constants2.characterGroupPunctuation && before || attentionMarkers.includes(code);
56618
- const close = !before || before === constants2.characterGroupPunctuation && after || attentionMarkers.includes(previous);
56669
+ const open = !after || after === constants3.characterGroupPunctuation && before || attentionMarkers.includes(code);
56670
+ const close = !before || before === constants3.characterGroupPunctuation && after || attentionMarkers.includes(previous);
56619
56671
  token._open = Boolean(marker === codes.asterisk ? open : open && (before || !close));
56620
56672
  token._close = Boolean(marker === codes.asterisk ? close : close && (after || !open));
56621
56673
  return ok2(code);
@@ -56663,7 +56715,7 @@ function tokenizeAutolink(effects, ok2, nok) {
56663
56715
  size = 0;
56664
56716
  return urlInside;
56665
56717
  }
56666
- if ((code === codes.plusSign || code === codes.dash || code === codes.dot || asciiAlphanumeric(code)) && size++ < constants2.autolinkSchemeSizeMax) {
56718
+ if ((code === codes.plusSign || code === codes.dash || code === codes.dot || asciiAlphanumeric(code)) && size++ < constants3.autolinkSchemeSizeMax) {
56667
56719
  effects.consume(code);
56668
56720
  return schemeInsideOrEmailAtext;
56669
56721
  }
@@ -56716,7 +56768,7 @@ function tokenizeAutolink(effects, ok2, nok) {
56716
56768
  return emailValue(code);
56717
56769
  }
56718
56770
  function emailValue(code) {
56719
- if ((code === codes.dash || asciiAlphanumeric(code)) && size++ < constants2.autolinkDomainSizeMax) {
56771
+ if ((code === codes.dash || asciiAlphanumeric(code)) && size++ < constants3.autolinkDomainSizeMax) {
56720
56772
  const next = code === codes.dash ? emailValue : emailLabel;
56721
56773
  effects.consume(code);
56722
56774
  return next;
@@ -56779,7 +56831,7 @@ function tokenizeBlockQuoteContinuation(effects, ok2, nok) {
56779
56831
  function contStart(code) {
56780
56832
  if (markdownSpace(code)) {
56781
56833
  ok(self.parser.constructs.disable.null, "expected `disable.null` to be populated");
56782
- return factorySpace(effects, contBefore, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants2.tabSize)(code);
56834
+ return factorySpace(effects, contBefore, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants3.tabSize)(code);
56783
56835
  }
56784
56836
  return contBefore(code);
56785
56837
  }
@@ -56843,7 +56895,7 @@ function tokenizeCharacterReference(effects, ok2, nok) {
56843
56895
  return numeric;
56844
56896
  }
56845
56897
  effects.enter(types2.characterReferenceValue);
56846
- max = constants2.characterReferenceNamedSizeMax;
56898
+ max = constants3.characterReferenceNamedSizeMax;
56847
56899
  test = asciiAlphanumeric;
56848
56900
  return value(code);
56849
56901
  }
@@ -56853,12 +56905,12 @@ function tokenizeCharacterReference(effects, ok2, nok) {
56853
56905
  effects.consume(code);
56854
56906
  effects.exit(types2.characterReferenceMarkerHexadecimal);
56855
56907
  effects.enter(types2.characterReferenceValue);
56856
- max = constants2.characterReferenceHexadecimalSizeMax;
56908
+ max = constants3.characterReferenceHexadecimalSizeMax;
56857
56909
  test = asciiHexDigit;
56858
56910
  return value;
56859
56911
  }
56860
56912
  effects.enter(types2.characterReferenceValue);
56861
- max = constants2.characterReferenceDecimalSizeMax;
56913
+ max = constants3.characterReferenceDecimalSizeMax;
56862
56914
  test = asciiDigit;
56863
56915
  return value(code);
56864
56916
  }
@@ -56917,7 +56969,7 @@ function tokenizeCodeFenced(effects, ok2, nok) {
56917
56969
  effects.consume(code);
56918
56970
  return sequenceOpen;
56919
56971
  }
56920
- if (sizeOpen < constants2.codeFencedSequenceSizeMin) {
56972
+ if (sizeOpen < constants3.codeFencedSequenceSizeMin) {
56921
56973
  return nok(code);
56922
56974
  }
56923
56975
  effects.exit(types2.codeFencedFenceSequence);
@@ -56929,7 +56981,7 @@ function tokenizeCodeFenced(effects, ok2, nok) {
56929
56981
  return self.interrupt ? ok2(code) : effects.check(nonLazyContinuation, atNonLazyBreak, after)(code);
56930
56982
  }
56931
56983
  effects.enter(types2.codeFencedFenceInfo);
56932
- effects.enter(types2.chunkString, { contentType: constants2.contentTypeString });
56984
+ effects.enter(types2.chunkString, { contentType: constants3.contentTypeString });
56933
56985
  return info(code);
56934
56986
  }
56935
56987
  function info(code) {
@@ -56954,7 +57006,7 @@ function tokenizeCodeFenced(effects, ok2, nok) {
56954
57006
  return infoBefore(code);
56955
57007
  }
56956
57008
  effects.enter(types2.codeFencedFenceMeta);
56957
- effects.enter(types2.chunkString, { contentType: constants2.contentTypeString });
57009
+ effects.enter(types2.chunkString, { contentType: constants3.contentTypeString });
56958
57010
  return meta(code);
56959
57011
  }
56960
57012
  function meta(code) {
@@ -57015,7 +57067,7 @@ function tokenizeCodeFenced(effects, ok2, nok) {
57015
57067
  function start2(code) {
57016
57068
  ok(self.parser.constructs.disable.null, "expected `disable.null` to be populated");
57017
57069
  effects2.enter(types2.codeFencedFence);
57018
- return markdownSpace(code) ? factorySpace(effects2, beforeSequenceClose, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants2.tabSize)(code) : beforeSequenceClose(code);
57070
+ return markdownSpace(code) ? factorySpace(effects2, beforeSequenceClose, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants3.tabSize)(code) : beforeSequenceClose(code);
57019
57071
  }
57020
57072
  function beforeSequenceClose(code) {
57021
57073
  if (code === marker) {
@@ -57074,11 +57126,11 @@ function tokenizeCodeIndented(effects, ok2, nok) {
57074
57126
  function start(code) {
57075
57127
  ok(markdownSpace(code));
57076
57128
  effects.enter(types2.codeIndented);
57077
- return factorySpace(effects, afterPrefix, types2.linePrefix, constants2.tabSize + 1)(code);
57129
+ return factorySpace(effects, afterPrefix, types2.linePrefix, constants3.tabSize + 1)(code);
57078
57130
  }
57079
57131
  function afterPrefix(code) {
57080
57132
  const tail = self.events[self.events.length - 1];
57081
- return tail && tail[1].type === types2.linePrefix && tail[2].sliceSerialize(tail[1], true).length >= constants2.tabSize ? atBreak(code) : nok(code);
57133
+ return tail && tail[1].type === types2.linePrefix && tail[2].sliceSerialize(tail[1], true).length >= constants3.tabSize ? atBreak(code) : nok(code);
57082
57134
  }
57083
57135
  function atBreak(code) {
57084
57136
  if (code === codes.eof) {
@@ -57116,11 +57168,11 @@ function tokenizeFurtherStart(effects, ok2, nok) {
57116
57168
  effects.exit(types2.lineEnding);
57117
57169
  return furtherStart2;
57118
57170
  }
57119
- return factorySpace(effects, afterPrefix, types2.linePrefix, constants2.tabSize + 1)(code);
57171
+ return factorySpace(effects, afterPrefix, types2.linePrefix, constants3.tabSize + 1)(code);
57120
57172
  }
57121
57173
  function afterPrefix(code) {
57122
57174
  const tail = self.events[self.events.length - 1];
57123
- return tail && tail[1].type === types2.linePrefix && tail[2].sliceSerialize(tail[1], true).length >= constants2.tabSize ? ok2(code) : markdownLineEnding(code) ? furtherStart2(code) : nok(code);
57175
+ return tail && tail[1].type === types2.linePrefix && tail[2].sliceSerialize(tail[1], true).length >= constants3.tabSize ? ok2(code) : markdownLineEnding(code) ? furtherStart2(code) : nok(code);
57124
57176
  }
57125
57177
  }
57126
57178
  // ../node_modules/.bun/micromark-core-commonmark@2.0.3/node_modules/micromark-core-commonmark/dev/lib/code-text.js
@@ -57312,12 +57364,12 @@ class SpliceBuffer {
57312
57364
  }
57313
57365
  function chunkedPush(list, right) {
57314
57366
  let chunkStart = 0;
57315
- if (right.length < constants2.v8MaxSafeChunkSize) {
57367
+ if (right.length < constants3.v8MaxSafeChunkSize) {
57316
57368
  list.push(...right);
57317
57369
  } else {
57318
57370
  while (chunkStart < right.length) {
57319
- list.push(...right.slice(chunkStart, chunkStart + constants2.v8MaxSafeChunkSize));
57320
- chunkStart += constants2.v8MaxSafeChunkSize;
57371
+ list.push(...right.slice(chunkStart, chunkStart + constants3.v8MaxSafeChunkSize));
57372
+ chunkStart += constants3.v8MaxSafeChunkSize;
57321
57373
  }
57322
57374
  }
57323
57375
  }
@@ -57489,7 +57541,7 @@ function tokenizeContent(effects, ok2) {
57489
57541
  ok(code !== codes.eof && !markdownLineEnding(code), "expected no eof or eol");
57490
57542
  effects.enter(types2.content);
57491
57543
  previous2 = effects.enter(types2.chunkContent, {
57492
- contentType: constants2.contentTypeContent
57544
+ contentType: constants3.contentTypeContent
57493
57545
  });
57494
57546
  return chunkInside(code);
57495
57547
  }
@@ -57514,7 +57566,7 @@ function tokenizeContent(effects, ok2) {
57514
57566
  effects.exit(types2.chunkContent);
57515
57567
  ok(previous2, "expected previous token");
57516
57568
  previous2.next = effects.enter(types2.chunkContent, {
57517
- contentType: constants2.contentTypeContent,
57569
+ contentType: constants3.contentTypeContent,
57518
57570
  previous: previous2
57519
57571
  });
57520
57572
  previous2 = previous2.next;
@@ -57538,7 +57590,7 @@ function tokenizeContinuation(effects, ok2, nok) {
57538
57590
  }
57539
57591
  ok(self.parser.constructs.disable.null, "expected `disable.null` to be populated");
57540
57592
  const tail = self.events[self.events.length - 1];
57541
- if (!self.parser.constructs.disable.null.includes("codeIndented") && tail && tail[1].type === types2.linePrefix && tail[2].sliceSerialize(tail[1], true).length >= constants2.tabSize) {
57593
+ if (!self.parser.constructs.disable.null.includes("codeIndented") && tail && tail[1].type === types2.linePrefix && tail[2].sliceSerialize(tail[1], true).length >= constants3.tabSize) {
57542
57594
  return ok2(code);
57543
57595
  }
57544
57596
  return effects.interrupt(self.parser.constructs.flow, nok, ok2)(code);
@@ -57564,7 +57616,7 @@ function factoryDestination(effects, ok2, nok, type, literalType2, literalMarker
57564
57616
  effects.enter(type);
57565
57617
  effects.enter(rawType);
57566
57618
  effects.enter(stringType2);
57567
- effects.enter(types2.chunkString, { contentType: constants2.contentTypeString });
57619
+ effects.enter(types2.chunkString, { contentType: constants3.contentTypeString });
57568
57620
  return raw(code);
57569
57621
  }
57570
57622
  function enclosedBefore(code) {
@@ -57577,7 +57629,7 @@ function factoryDestination(effects, ok2, nok, type, literalType2, literalMarker
57577
57629
  return ok2;
57578
57630
  }
57579
57631
  effects.enter(stringType2);
57580
- effects.enter(types2.chunkString, { contentType: constants2.contentTypeString });
57632
+ effects.enter(types2.chunkString, { contentType: constants3.contentTypeString });
57581
57633
  return enclosed(code);
57582
57634
  }
57583
57635
  function enclosed(code) {
@@ -57648,7 +57700,7 @@ function factoryLabel(effects, ok2, nok, type, markerType, stringType2) {
57648
57700
  return atBreak;
57649
57701
  }
57650
57702
  function atBreak(code) {
57651
- if (size > constants2.linkReferenceSizeMax || code === codes.eof || code === codes.leftSquareBracket || code === codes.rightSquareBracket && !seen || code === codes.caret && !size && "_hiddenFootnoteSupport" in self.parser.constructs) {
57703
+ if (size > constants3.linkReferenceSizeMax || code === codes.eof || code === codes.leftSquareBracket || code === codes.rightSquareBracket && !seen || code === codes.caret && !size && "_hiddenFootnoteSupport" in self.parser.constructs) {
57652
57704
  return nok(code);
57653
57705
  }
57654
57706
  if (code === codes.rightSquareBracket) {
@@ -57665,11 +57717,11 @@ function factoryLabel(effects, ok2, nok, type, markerType, stringType2) {
57665
57717
  effects.exit(types2.lineEnding);
57666
57718
  return atBreak;
57667
57719
  }
57668
- effects.enter(types2.chunkString, { contentType: constants2.contentTypeString });
57720
+ effects.enter(types2.chunkString, { contentType: constants3.contentTypeString });
57669
57721
  return labelInside(code);
57670
57722
  }
57671
57723
  function labelInside(code) {
57672
- if (code === codes.eof || code === codes.leftSquareBracket || code === codes.rightSquareBracket || markdownLineEnding(code) || size++ > constants2.linkReferenceSizeMax) {
57724
+ if (code === codes.eof || code === codes.leftSquareBracket || code === codes.rightSquareBracket || markdownLineEnding(code) || size++ > constants3.linkReferenceSizeMax) {
57673
57725
  effects.exit(types2.chunkString);
57674
57726
  return atBreak(code);
57675
57727
  }
@@ -57728,7 +57780,7 @@ function factoryTitle(effects, ok2, nok, type, markerType, stringType2) {
57728
57780
  effects.exit(types2.lineEnding);
57729
57781
  return factorySpace(effects, atBreak, types2.linePrefix);
57730
57782
  }
57731
- effects.enter(types2.chunkString, { contentType: constants2.contentTypeString });
57783
+ effects.enter(types2.chunkString, { contentType: constants3.contentTypeString });
57732
57784
  return inside(code);
57733
57785
  }
57734
57786
  function inside(code) {
@@ -57879,7 +57931,7 @@ function resolveHeadingAtx(events, context) {
57879
57931
  type: types2.chunkText,
57880
57932
  start: events[contentStart][1].start,
57881
57933
  end: events[contentEnd][1].end,
57882
- contentType: constants2.contentTypeText
57934
+ contentType: constants3.contentTypeText
57883
57935
  };
57884
57936
  splice(events, contentStart, contentEnd - contentStart + 1, [
57885
57937
  ["enter", content3, context],
@@ -57903,7 +57955,7 @@ function tokenizeHeadingAtx(effects, ok2, nok) {
57903
57955
  return sequenceOpen(code);
57904
57956
  }
57905
57957
  function sequenceOpen(code) {
57906
- if (code === codes.numberSign && size++ < constants2.atxHeadingOpeningFenceSizeMax) {
57958
+ if (code === codes.numberSign && size++ < constants3.atxHeadingOpeningFenceSizeMax) {
57907
57959
  effects.consume(code);
57908
57960
  return sequenceOpen;
57909
57961
  }
@@ -58068,7 +58120,7 @@ function tokenizeHtmlFlow(effects, ok2, nok) {
58068
58120
  }
58069
58121
  if (code === codes.questionMark) {
58070
58122
  effects.consume(code);
58071
- marker = constants2.htmlInstruction;
58123
+ marker = constants3.htmlInstruction;
58072
58124
  return self.interrupt ? ok2 : continuationDeclarationInside;
58073
58125
  }
58074
58126
  if (asciiAlpha(code)) {
@@ -58082,18 +58134,18 @@ function tokenizeHtmlFlow(effects, ok2, nok) {
58082
58134
  function declarationOpen(code) {
58083
58135
  if (code === codes.dash) {
58084
58136
  effects.consume(code);
58085
- marker = constants2.htmlComment;
58137
+ marker = constants3.htmlComment;
58086
58138
  return commentOpenInside;
58087
58139
  }
58088
58140
  if (code === codes.leftSquareBracket) {
58089
58141
  effects.consume(code);
58090
- marker = constants2.htmlCdata;
58142
+ marker = constants3.htmlCdata;
58091
58143
  index = 0;
58092
58144
  return cdataOpenInside;
58093
58145
  }
58094
58146
  if (asciiAlpha(code)) {
58095
58147
  effects.consume(code);
58096
- marker = constants2.htmlDeclaration;
58148
+ marker = constants3.htmlDeclaration;
58097
58149
  return self.interrupt ? ok2 : continuationDeclarationInside;
58098
58150
  }
58099
58151
  return nok(code);
@@ -58106,7 +58158,7 @@ function tokenizeHtmlFlow(effects, ok2, nok) {
58106
58158
  return nok(code);
58107
58159
  }
58108
58160
  function cdataOpenInside(code) {
58109
- const value = constants2.cdataOpeningString;
58161
+ const value = constants3.cdataOpeningString;
58110
58162
  if (code === value.charCodeAt(index++)) {
58111
58163
  effects.consume(code);
58112
58164
  if (index === value.length) {
@@ -58130,18 +58182,18 @@ function tokenizeHtmlFlow(effects, ok2, nok) {
58130
58182
  const slash = code === codes.slash;
58131
58183
  const name = buffer.toLowerCase();
58132
58184
  if (!slash && !closingTag && htmlRawNames.includes(name)) {
58133
- marker = constants2.htmlRaw;
58185
+ marker = constants3.htmlRaw;
58134
58186
  return self.interrupt ? ok2(code) : continuation(code);
58135
58187
  }
58136
58188
  if (htmlBlockNames.includes(buffer.toLowerCase())) {
58137
- marker = constants2.htmlBasic;
58189
+ marker = constants3.htmlBasic;
58138
58190
  if (slash) {
58139
58191
  effects.consume(code);
58140
58192
  return basicSelfClosing;
58141
58193
  }
58142
58194
  return self.interrupt ? ok2(code) : continuation(code);
58143
58195
  }
58144
- marker = constants2.htmlComplete;
58196
+ marker = constants3.htmlComplete;
58145
58197
  return self.interrupt && !self.parser.lazy[self.now().line] ? nok(code) : closingTag ? completeClosingTagAfter(code) : completeAttributeNameBefore(code);
58146
58198
  }
58147
58199
  if (code === codes.dash || asciiAlphanumeric(code)) {
@@ -58256,27 +58308,27 @@ function tokenizeHtmlFlow(effects, ok2, nok) {
58256
58308
  return nok(code);
58257
58309
  }
58258
58310
  function continuation(code) {
58259
- if (code === codes.dash && marker === constants2.htmlComment) {
58311
+ if (code === codes.dash && marker === constants3.htmlComment) {
58260
58312
  effects.consume(code);
58261
58313
  return continuationCommentInside;
58262
58314
  }
58263
- if (code === codes.lessThan && marker === constants2.htmlRaw) {
58315
+ if (code === codes.lessThan && marker === constants3.htmlRaw) {
58264
58316
  effects.consume(code);
58265
58317
  return continuationRawTagOpen;
58266
58318
  }
58267
- if (code === codes.greaterThan && marker === constants2.htmlDeclaration) {
58319
+ if (code === codes.greaterThan && marker === constants3.htmlDeclaration) {
58268
58320
  effects.consume(code);
58269
58321
  return continuationClose;
58270
58322
  }
58271
- if (code === codes.questionMark && marker === constants2.htmlInstruction) {
58323
+ if (code === codes.questionMark && marker === constants3.htmlInstruction) {
58272
58324
  effects.consume(code);
58273
58325
  return continuationDeclarationInside;
58274
58326
  }
58275
- if (code === codes.rightSquareBracket && marker === constants2.htmlCdata) {
58327
+ if (code === codes.rightSquareBracket && marker === constants3.htmlCdata) {
58276
58328
  effects.consume(code);
58277
58329
  return continuationCdataInside;
58278
58330
  }
58279
- if (markdownLineEnding(code) && (marker === constants2.htmlBasic || marker === constants2.htmlComplete)) {
58331
+ if (markdownLineEnding(code) && (marker === constants3.htmlBasic || marker === constants3.htmlComplete)) {
58280
58332
  effects.exit(types2.htmlFlowData);
58281
58333
  return effects.check(blankLineBefore, continuationAfter, continuationStart)(code);
58282
58334
  }
@@ -58328,7 +58380,7 @@ function tokenizeHtmlFlow(effects, ok2, nok) {
58328
58380
  }
58329
58381
  return continuation(code);
58330
58382
  }
58331
- if (asciiAlpha(code) && buffer.length < constants2.htmlRawSizeMax) {
58383
+ if (asciiAlpha(code) && buffer.length < constants3.htmlRawSizeMax) {
58332
58384
  ok(code !== null);
58333
58385
  effects.consume(code);
58334
58386
  buffer += String.fromCharCode(code);
@@ -58348,7 +58400,7 @@ function tokenizeHtmlFlow(effects, ok2, nok) {
58348
58400
  effects.consume(code);
58349
58401
  return continuationClose;
58350
58402
  }
58351
- if (code === codes.dash && marker === constants2.htmlComment) {
58403
+ if (code === codes.dash && marker === constants3.htmlComment) {
58352
58404
  effects.consume(code);
58353
58405
  return continuationDeclarationInside;
58354
58406
  }
@@ -58476,7 +58528,7 @@ function tokenizeHtmlText(effects, ok2, nok) {
58476
58528
  return code === codes.greaterThan ? end(code) : code === codes.dash ? commentClose(code) : comment(code);
58477
58529
  }
58478
58530
  function cdataOpenInside(code) {
58479
- const value = constants2.cdataOpeningString;
58531
+ const value = constants3.cdataOpeningString;
58480
58532
  if (code === value.charCodeAt(index++)) {
58481
58533
  effects.consume(code);
58482
58534
  return index === value.length ? cdata : cdataOpenInside;
@@ -58692,7 +58744,7 @@ function tokenizeHtmlText(effects, ok2, nok) {
58692
58744
  }
58693
58745
  function lineEndingAfter(code) {
58694
58746
  ok(self.parser.constructs.disable.null, "expected `disable.null` to be populated");
58695
- return markdownSpace(code) ? factorySpace(effects, lineEndingAfterPrefix, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants2.tabSize)(code) : lineEndingAfterPrefix(code);
58747
+ return markdownSpace(code) ? factorySpace(effects, lineEndingAfterPrefix, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants3.tabSize)(code) : lineEndingAfterPrefix(code);
58696
58748
  }
58697
58749
  function lineEndingAfterPrefix(code) {
58698
58750
  effects.enter(types2.htmlTextData);
@@ -58855,7 +58907,7 @@ function tokenizeResource(effects, ok2, nok) {
58855
58907
  if (code === codes.rightParenthesis) {
58856
58908
  return resourceEnd(code);
58857
58909
  }
58858
- return factoryDestination(effects, resourceDestinationAfter, resourceDestinationMissing, types2.resourceDestination, types2.resourceDestinationLiteral, types2.resourceDestinationLiteralMarker, types2.resourceDestinationRaw, types2.resourceDestinationString, constants2.linkResourceDestinationBalanceMax)(code);
58910
+ return factoryDestination(effects, resourceDestinationAfter, resourceDestinationMissing, types2.resourceDestination, types2.resourceDestinationLiteral, types2.resourceDestinationLiteralMarker, types2.resourceDestinationRaw, types2.resourceDestinationString, constants3.linkResourceDestinationBalanceMax)(code);
58859
58911
  }
58860
58912
  function resourceDestinationAfter(code) {
58861
58913
  return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceBetween)(code) : resourceEnd(code);
@@ -59006,7 +59058,7 @@ function tokenizeThematicBreak(effects, ok2, nok) {
59006
59058
  effects.enter(types2.thematicBreakSequence);
59007
59059
  return sequence(code);
59008
59060
  }
59009
- if (size >= constants2.thematicBreakMarkerCountMin && (code === codes.eof || markdownLineEnding(code))) {
59061
+ if (size >= constants3.thematicBreakMarkerCountMin && (code === codes.eof || markdownLineEnding(code))) {
59010
59062
  effects.exit(types2.thematicBreak);
59011
59063
  return ok2(code);
59012
59064
  }
@@ -59063,7 +59115,7 @@ function tokenizeListStart(effects, ok2, nok) {
59063
59115
  }
59064
59116
  function inside(code) {
59065
59117
  ok(self.containerState, "expected state");
59066
- if (asciiDigit(code) && ++size < constants2.listItemValueSizeMax) {
59118
+ if (asciiDigit(code) && ++size < constants3.listItemValueSizeMax) {
59067
59119
  effects.consume(code);
59068
59120
  return inside;
59069
59121
  }
@@ -59130,7 +59182,7 @@ function tokenizeListContinuation(effects, ok2, nok) {
59130
59182
  self.containerState._closeFlow = true;
59131
59183
  self.interrupt = undefined;
59132
59184
  ok(self.parser.constructs.disable.null, "expected `disable.null` to be populated");
59133
- return factorySpace(effects, effects.attempt(list, ok2, nok), types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants2.tabSize)(code);
59185
+ return factorySpace(effects, effects.attempt(list, ok2, nok), types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants3.tabSize)(code);
59134
59186
  }
59135
59187
  }
59136
59188
  function tokenizeIndent(effects, ok2, nok) {
@@ -59152,7 +59204,7 @@ function tokenizeListEnd(effects) {
59152
59204
  function tokenizeListItemPrefixWhitespace(effects, ok2, nok) {
59153
59205
  const self = this;
59154
59206
  ok(self.parser.constructs.disable.null, "expected `disable.null` to be populated");
59155
- return factorySpace(effects, afterPrefix, types2.listItemPrefixWhitespace, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants2.tabSize + 1);
59207
+ return factorySpace(effects, afterPrefix, types2.listItemPrefixWhitespace, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants3.tabSize + 1);
59156
59208
  function afterPrefix(code) {
59157
59209
  const tail = self.events[self.events.length - 1];
59158
59210
  return !markdownSpace(code) && tail && tail[1].type === types2.listItemPrefixWhitespace ? ok2(code) : nok(code);
@@ -59390,7 +59442,7 @@ function resolveAllLineSuffixes(events, context) {
59390
59442
  }
59391
59443
  if (size) {
59392
59444
  const token = {
59393
- type: eventIndex === events.length || tabs || size < constants2.hardBreakPrefixSizeMin ? types2.lineSuffix : types2.hardBreakTrailing,
59445
+ type: eventIndex === events.length || tabs || size < constants3.hardBreakPrefixSizeMin ? types2.lineSuffix : types2.hardBreakTrailing,
59394
59446
  start: {
59395
59447
  _bufferIndex: index ? bufferIndex : data.start._bufferIndex + bufferIndex,
59396
59448
  _index: data.start._index + index,
@@ -59880,7 +59932,7 @@ function preprocess() {
59880
59932
  break;
59881
59933
  }
59882
59934
  case codes.ht: {
59883
- next = Math.ceil(column / constants2.tabSize) * constants2.tabSize;
59935
+ next = Math.ceil(column / constants3.tabSize) * constants3.tabSize;
59884
59936
  chunks.push(codes.horizontalTab);
59885
59937
  while (column++ < next)
59886
59938
  chunks.push(codes.virtualSpace);
@@ -59922,7 +59974,7 @@ function decode($0, $1, $2) {
59922
59974
  if (head === codes.numberSign) {
59923
59975
  const head2 = $2.charCodeAt(1);
59924
59976
  const hex = head2 === codes.lowercaseX || head2 === codes.uppercaseX;
59925
- return decodeNumericCharacterReference($2.slice(hex ? 2 : 1), hex ? constants2.numericBaseHexadecimal : constants2.numericBaseDecimal);
59977
+ return decodeNumericCharacterReference($2.slice(hex ? 2 : 1), hex ? constants3.numericBaseHexadecimal : constants3.numericBaseDecimal);
59926
59978
  }
59927
59979
  return decodeNamedCharacterReference($2) || $0;
59928
59980
  }
@@ -60259,7 +60311,7 @@ function compiler(options) {
60259
60311
  const ancestor = this.stack[this.stack.length - 2];
60260
60312
  ok(ancestor, "expected nodes on stack");
60261
60313
  ok(ancestor.type === "list", "expected list on stack");
60262
- ancestor.start = Number.parseInt(this.sliceSerialize(token), constants2.numericBaseDecimal);
60314
+ ancestor.start = Number.parseInt(this.sliceSerialize(token), constants3.numericBaseDecimal);
60263
60315
  this.data.expectingFirstListItemValue = undefined;
60264
60316
  }
60265
60317
  }
@@ -60500,7 +60552,7 @@ function compiler(options) {
60500
60552
  const type = this.data.characterReferenceType;
60501
60553
  let value;
60502
60554
  if (type) {
60503
- value = decodeNumericCharacterReference(data2, type === types2.characterReferenceMarkerNumeric ? constants2.numericBaseDecimal : constants2.numericBaseHexadecimal);
60555
+ value = decodeNumericCharacterReference(data2, type === types2.characterReferenceMarkerNumeric ? constants3.numericBaseDecimal : constants3.numericBaseHexadecimal);
60504
60556
  this.data.characterReferenceType = undefined;
60505
60557
  } else {
60506
60558
  const result = decodeNamedCharacterReference(data2);
@@ -61116,7 +61168,7 @@ function tokenizeGfmFootnoteCall(effects, ok2, nok) {
61116
61168
  return callData;
61117
61169
  }
61118
61170
  function callData(code2) {
61119
- if (size > constants2.linkReferenceSizeMax || code2 === codes.rightSquareBracket && !data || code2 === codes.eof || code2 === codes.leftSquareBracket || markdownLineEndingOrSpace(code2)) {
61171
+ if (size > constants3.linkReferenceSizeMax || code2 === codes.rightSquareBracket && !data || code2 === codes.eof || code2 === codes.leftSquareBracket || markdownLineEndingOrSpace(code2)) {
61120
61172
  return nok(code2);
61121
61173
  }
61122
61174
  if (code2 === codes.rightSquareBracket) {
@@ -61175,7 +61227,7 @@ function tokenizeDefinitionStart(effects, ok2, nok) {
61175
61227
  return nok(code2);
61176
61228
  }
61177
61229
  function labelInside(code2) {
61178
- if (size > constants2.linkReferenceSizeMax || code2 === codes.rightSquareBracket && !data || code2 === codes.eof || code2 === codes.leftSquareBracket || markdownLineEndingOrSpace(code2)) {
61230
+ if (size > constants3.linkReferenceSizeMax || code2 === codes.rightSquareBracket && !data || code2 === codes.eof || code2 === codes.leftSquareBracket || markdownLineEndingOrSpace(code2)) {
61179
61231
  return nok(code2);
61180
61232
  }
61181
61233
  if (code2 === codes.rightSquareBracket) {
@@ -61227,10 +61279,10 @@ function gfmFootnoteDefinitionEnd(effects) {
61227
61279
  }
61228
61280
  function tokenizeIndent2(effects, ok2, nok) {
61229
61281
  const self = this;
61230
- return factorySpace(effects, afterPrefix, "gfmFootnoteDefinitionIndent", constants2.tabSize + 1);
61282
+ return factorySpace(effects, afterPrefix, "gfmFootnoteDefinitionIndent", constants3.tabSize + 1);
61231
61283
  function afterPrefix(code2) {
61232
61284
  const tail = self.events[self.events.length - 1];
61233
- return tail && tail[1].type === "gfmFootnoteDefinitionIndent" && tail[2].sliceSerialize(tail[1], true).length === constants2.tabSize ? ok2(code2) : nok(code2);
61285
+ return tail && tail[1].type === "gfmFootnoteDefinitionIndent" && tail[2].sliceSerialize(tail[1], true).length === constants3.tabSize ? ok2(code2) : nok(code2);
61234
61286
  }
61235
61287
  }
61236
61288
  // ../node_modules/.bun/micromark-extension-gfm-strikethrough@2.1.0/node_modules/micromark-extension-gfm-strikethrough/dev/lib/syntax.js
@@ -61326,8 +61378,8 @@ function gfmStrikethrough(options) {
61326
61378
  return nok(code2);
61327
61379
  const token = effects.exit("strikethroughSequenceTemporary");
61328
61380
  const after = classifyCharacter(code2);
61329
- token._open = !after || after === constants2.attentionSideAfter && Boolean(before);
61330
- token._close = !before || before === constants2.attentionSideAfter && Boolean(after);
61381
+ token._open = !after || after === constants3.attentionSideAfter && Boolean(before);
61382
+ token._close = !before || before === constants3.attentionSideAfter && Boolean(after);
61331
61383
  return ok2(code2);
61332
61384
  }
61333
61385
  }
@@ -61511,7 +61563,7 @@ function tokenizeTable(effects, ok2, nok) {
61511
61563
  seen = false;
61512
61564
  if (markdownSpace(code2)) {
61513
61565
  ok(self.parser.constructs.disable.null, "expected `disabled.null`");
61514
- return factorySpace(effects, headDelimiterBefore, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants2.tabSize)(code2);
61566
+ return factorySpace(effects, headDelimiterBefore, types2.linePrefix, self.parser.constructs.disable.null.includes("codeIndented") ? undefined : constants3.tabSize)(code2);
61515
61567
  }
61516
61568
  return headDelimiterBefore(code2);
61517
61569
  }
@@ -61762,7 +61814,7 @@ function flushCell(map, context, range, rowKind, rowEnd, previousCell) {
61762
61814
  const end = context.events[range[3]];
61763
61815
  start[1].end = Object.assign({}, end[1].end);
61764
61816
  start[1].type = types2.chunkText;
61765
- start[1].contentType = constants2.contentTypeText;
61817
+ start[1].contentType = constants3.contentTypeText;
61766
61818
  if (range[3] > range[2] + 1) {
61767
61819
  const a = range[2] + 1;
61768
61820
  const b = range[3] - range[2] - 1;
@@ -63186,6 +63238,18 @@ function createChatLock() {
63186
63238
 
63187
63239
  // retry-api-call.ts
63188
63240
  var import_grammy6 = __toESM(require_mod2(), 1);
63241
+ function classifyBenignTelegram4002(errorCode, description) {
63242
+ if (errorCode !== 400)
63243
+ return null;
63244
+ const desc = (description ?? "").toLowerCase();
63245
+ if (desc.includes("not modified"))
63246
+ return "not_modified";
63247
+ if (desc.includes("message to edit not found"))
63248
+ return "message_not_found";
63249
+ if (desc.includes("message to delete not found"))
63250
+ return "delete_not_found";
63251
+ return null;
63252
+ }
63189
63253
  function isLocalResourceError2(err) {
63190
63254
  const code2 = err?.code;
63191
63255
  if (typeof code2 === "string" && ["ENOSPC", "EDQUOT", "EIO", "ENOMEM"].includes(code2)) {
@@ -63270,14 +63334,9 @@ function createRetryApiCall2(config = {}) {
63270
63334
  await sleep(delayMs);
63271
63335
  continue;
63272
63336
  }
63273
- if (isGrammyErr && err.error_code === 400 && desc.includes("not modified")) {
63274
- observer?.onBenign?.({ kind: "not_modified" });
63275
- return;
63276
- }
63277
- if (isGrammyErr && err.error_code === 400 && (desc.includes("message to edit not found") || desc.includes("message to delete not found"))) {
63278
- observer?.onBenign?.({
63279
- kind: desc.includes("edit") ? "message_not_found" : "delete_not_found"
63280
- });
63337
+ const benignKind = isGrammyErr ? classifyBenignTelegram4002(err.error_code, desc) : null;
63338
+ if (benignKind !== null) {
63339
+ observer?.onBenign?.({ kind: benignKind });
63281
63340
  return;
63282
63341
  }
63283
63342
  if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
@@ -63425,7 +63484,7 @@ function editFloodFuseConfigFromEnv(env) {
63425
63484
  }
63426
63485
  return cfg;
63427
63486
  }
63428
- var DROPPED_RESULT = true;
63487
+ var DROPPED_RESULT = { ok: true, result: true };
63429
63488
  function createEditFloodFuse(config = {}) {
63430
63489
  const enabled2 = config.enabled ?? true;
63431
63490
  const clock = config.clock ?? systemClock;
@@ -63683,12 +63742,12 @@ function createEditFloodFuse(config = {}) {
63683
63742
  const msgKey = `m:${chat}:${msg}`;
63684
63743
  const mw = win(msgKey);
63685
63744
  mw.inflight++;
63745
+ const dropGuard = () => mw.inflight > 1;
63746
+ const lateKey = cls === "cosmetic" ? `lr:${chat}` : undefined;
63686
63747
  try {
63687
- const msgSlot = await awaitRoom(msgKey, perMessageWindowMs, cls === "cosmetic" ? (t) => ceiling(cosmeticPerMessageMax, t) : (t) => classCeiling(perMessageMax, cls, t), method, "supersede", cls, undefined, deadline);
63748
+ const msgSlot = await awaitRoom(msgKey, perMessageWindowMs, cls === "cosmetic" ? (t) => ceiling(cosmeticPerMessageMax, t) : (t) => classCeiling(perMessageMax, cls, t), method, "supersede", cls, dropGuard, deadline, lateKey);
63688
63749
  if (msgSlot === null)
63689
63750
  return DROPPED_RESULT;
63690
- const dropGuard = () => mw.inflight > 1;
63691
- const lateKey = cls === "cosmetic" ? `lr:${chat}` : undefined;
63692
63751
  const reserved = [[msgKey, msgSlot]];
63693
63752
  const giveBack = () => {
63694
63753
  for (const [k, at] of reserved)
@@ -64607,6 +64666,11 @@ function formatTgPostTags(tags) {
64607
64666
  }
64608
64667
  return parts.length > 0 ? " " + parts.join(" ") : "";
64609
64668
  }
64669
+ function shortDesc(raw) {
64670
+ if (!raw)
64671
+ return "-";
64672
+ return raw.replace(/\s+/g, " ").slice(0, 80).replace(/[\r\n]/g, " ") || "-";
64673
+ }
64610
64674
  function installTgPostLogger(bot) {
64611
64675
  bot.api.config.use(async (prev, method, payload, signal) => {
64612
64676
  const p = payload ?? {};
@@ -64617,20 +64681,28 @@ function installTgPostLogger(bot) {
64617
64681
  const bytes = text4.length;
64618
64682
  const hash = bytes > 0 ? createHash3("sha1").update(text4).digest("hex").slice(0, 12) : "-";
64619
64683
  const tagSuffix = formatTgPostTags(_getTgPostTags());
64684
+ const emit = (status, errClass, code2, desc) => {
64685
+ if (!shouldEmitTgPost(method, status))
64686
+ return;
64687
+ process.stderr.write(`tg-post method=${method} chat=${chat} thread=${thread} parse_mode=${parseMode} bytes=${bytes} hash=${hash} status=${status} err=${errClass} code=${code2} desc=${desc}${tagSuffix}
64688
+ `);
64689
+ };
64620
64690
  try {
64621
64691
  const res = await prev(method, payload, signal);
64622
- if (shouldEmitTgPost(method, "ok")) {
64623
- process.stderr.write(`tg-post method=${method} chat=${chat} thread=${thread} parse_mode=${parseMode} bytes=${bytes} hash=${hash} status=ok err=- code=- desc=-${tagSuffix}
64624
- `);
64692
+ const r = res;
64693
+ if (r != null && typeof r === "object" && r.ok === false) {
64694
+ const code2 = r.error_code;
64695
+ const benign = classifyBenignTelegram400(code2, r.description);
64696
+ emit(benign !== null ? "benign" : "err", `telegram_${code2 ?? "unknown"}`, code2 != null ? String(code2) : "-", shortDesc(r.description));
64697
+ return res;
64625
64698
  }
64699
+ emit("ok", "-", "-", "-");
64626
64700
  return res;
64627
64701
  } catch (err) {
64628
64702
  const errClass = err instanceof import_grammy7.GrammyError ? `grammy_${err.error_code}` : err?.constructor?.name ?? "Error";
64629
64703
  const code2 = err instanceof import_grammy7.GrammyError ? String(err.error_code) : "-";
64630
64704
  const rawDesc = err instanceof import_grammy7.GrammyError ? err.description : err instanceof Error ? err.message : "";
64631
- const desc = rawDesc ? rawDesc.replace(/\s+/g, " ").slice(0, 80).replace(/[\r\n]/g, " ") || "-" : "-";
64632
- process.stderr.write(`tg-post method=${method} chat=${chat} thread=${thread} parse_mode=${parseMode} bytes=${bytes} hash=${hash} status=err err=${errClass} code=${code2} desc=${desc}${tagSuffix}
64633
- `);
64705
+ emit("err", errClass, code2, shortDesc(rawDesc));
64634
64706
  throw err;
64635
64707
  }
64636
64708
  });
@@ -64660,7 +64732,7 @@ import {
64660
64732
  mkdirSync as mkdirSync16,
64661
64733
  chmodSync as chmodSync6,
64662
64734
  unlinkSync as unlinkSync11,
64663
- renameSync as renameSync9
64735
+ renameSync as renameSync10
64664
64736
  } from "node:fs";
64665
64737
  import { dirname as dirname9, join as join19 } from "node:path";
64666
64738
  var FLOOD_STATE_FILE2 = "flood-wait.json";
@@ -64867,7 +64939,7 @@ function writeFloodWindow(path2, record, now) {
64867
64939
  const arr = [...byScope.values()];
64868
64940
  const tmp = `${path2}.tmp-${process.pid}`;
64869
64941
  writeFileSync13(tmp, JSON.stringify(arr), { mode: FLOOD_STATE_MODE2 });
64870
- renameSync9(tmp, path2);
64942
+ renameSync10(tmp, path2);
64871
64943
  try {
64872
64944
  chmodSync6(path2, FLOOD_STATE_MODE2);
64873
64945
  } catch {}
@@ -72946,10 +73018,10 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
72946
73018
 
72947
73019
  // gateway/auth-add-flow.ts
72948
73020
  import { execFileSync as execFileSync4 } from "node:child_process";
72949
- import { existsSync as existsSync21, mkdirSync as mkdirSync24, readFileSync as readFileSync22, readdirSync as readdirSync6, rmSync as rmSync5, statSync as statSync9, writeFileSync as writeFileSync20 } from "node:fs";
73021
+ import { existsSync as existsSync21, mkdirSync as mkdirSync24, readFileSync as readFileSync22, readdirSync as readdirSync6, rmSync as rmSync6, statSync as statSync9, writeFileSync as writeFileSync20 } from "node:fs";
72950
73022
  import { homedir as homedir8 } from "node:os";
72951
73023
  import { join as join29 } from "node:path";
72952
- import { randomBytes as randomBytes8 } from "node:crypto";
73024
+ import { randomBytes as randomBytes9 } from "node:crypto";
72953
73025
 
72954
73026
  // ../src/auth/manager.ts
72955
73027
  import {
@@ -72959,7 +73031,7 @@ import {
72959
73031
  writeFileSync as writeFileSync19,
72960
73032
  mkdirSync as mkdirSync23,
72961
73033
  mkdtempSync as mkdtempSync2,
72962
- rmSync as rmSync4,
73034
+ rmSync as rmSync5,
72963
73035
  chmodSync as chmodSync7,
72964
73036
  statSync as statSync8
72965
73037
  } from "node:fs";
@@ -73054,12 +73126,12 @@ function makeAuthAddTmuxOps(tmuxBin = "tmux") {
73054
73126
  }
73055
73127
  var pendingAuthAddFlows = new Map;
73056
73128
  function pickScratchDir(label, home2 = homedir8()) {
73057
- const suffix = randomBytes8(8).toString("hex");
73129
+ const suffix = randomBytes9(8).toString("hex");
73058
73130
  return join29(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
73059
73131
  }
73060
73132
  function cleanScratchDir(scratchDir) {
73061
73133
  try {
73062
- rmSync5(scratchDir, { recursive: true, force: true });
73134
+ rmSync6(scratchDir, { recursive: true, force: true });
73063
73135
  } catch {}
73064
73136
  }
73065
73137
  var AUTH_TMUX_SESSION_FILE = ".auth-tmux-session";
@@ -76377,7 +76449,7 @@ import {
76377
76449
  mkdirSync as mkdirSync26,
76378
76450
  readFileSync as readFileSync23,
76379
76451
  readdirSync as readdirSync7,
76380
- renameSync as renameSync10,
76452
+ renameSync as renameSync11,
76381
76453
  statSync as statSync10,
76382
76454
  unlinkSync as unlinkSync13,
76383
76455
  writeFileSync as writeFileSync21,
@@ -76413,7 +76485,7 @@ function writeOutboxRecordAtomic(record, stateDir) {
76413
76485
  return true;
76414
76486
  const tmpPath = join31(dir, `.${record.turnNonce}.${process.pid}.tmp`);
76415
76487
  writeFileSync21(tmpPath, JSON.stringify(record), { mode: 384 });
76416
- renameSync10(tmpPath, finalPath);
76488
+ renameSync11(tmpPath, finalPath);
76417
76489
  return true;
76418
76490
  } catch {
76419
76491
  return false;
@@ -76440,7 +76512,7 @@ function claimRecord(nonce, stateDir) {
76440
76512
  const from = join31(dir, `${nonce}.json`);
76441
76513
  const to = join31(dir, `${nonce}.sending`);
76442
76514
  try {
76443
- renameSync10(from, to);
76515
+ renameSync11(from, to);
76444
76516
  return to;
76445
76517
  } catch {
76446
76518
  return null;
@@ -76449,7 +76521,7 @@ function claimRecord(nonce, stateDir) {
76449
76521
  function releaseClaim(nonce, stateDir) {
76450
76522
  const dir = resolveOutboxDir(stateDir);
76451
76523
  try {
76452
- renameSync10(join31(dir, `${nonce}.sending`), join31(dir, `${nonce}.json`));
76524
+ renameSync11(join31(dir, `${nonce}.sending`), join31(dir, `${nonce}.json`));
76453
76525
  } catch {}
76454
76526
  }
76455
76527
  function removeClaimed(nonce, stateDir) {
@@ -76469,7 +76541,7 @@ function reclaimStaleSending(stateDir, now = Date.now()) {
76469
76541
  try {
76470
76542
  const st = statSync10(join31(dir, f));
76471
76543
  if (now - st.mtimeMs > OUTBOX_SENDING_TIMEOUT_MS) {
76472
- renameSync10(join31(dir, f), join31(dir, f.replace(/\.sending$/, ".json")));
76544
+ renameSync11(join31(dir, f), join31(dir, f.replace(/\.sending$/, ".json")));
76473
76545
  }
76474
76546
  } catch {}
76475
76547
  }
@@ -76543,7 +76615,7 @@ function compactJournalIfLarge(path2) {
76543
76615
  writeFileSync21(tmp, kept.join(`
76544
76616
  `) + `
76545
76617
  `, { mode: 384 });
76546
- renameSync10(tmp, path2);
76618
+ renameSync11(tmp, path2);
76547
76619
  } catch {}
76548
76620
  }
76549
76621
  function clearOutboxRecord(nonce, stateDir) {
@@ -76607,7 +76679,7 @@ import {
76607
76679
  existsSync as existsSync24,
76608
76680
  mkdirSync as mkdirSync27,
76609
76681
  readFileSync as readFileSync24,
76610
- renameSync as renameSync11,
76682
+ renameSync as renameSync12,
76611
76683
  writeFileSync as writeFileSync22,
76612
76684
  appendFileSync as appendFileSync5
76613
76685
  } from "node:fs";
@@ -76741,7 +76813,7 @@ function compactIfLarge(path2) {
76741
76813
  writeFileSync22(tmp, kept.join(`
76742
76814
  `) + `
76743
76815
  `, { mode: 384 });
76744
- renameSync11(tmp, path2);
76816
+ renameSync12(tmp, path2);
76745
76817
  } catch {}
76746
76818
  }
76747
76819
 
@@ -77198,7 +77270,7 @@ function eagerVoiceEnabled2() {
77198
77270
  }
77199
77271
 
77200
77272
  // photo-precheck.ts
77201
- import { closeSync as closeSync5, fstatSync, openSync as openSync5, readSync } from "node:fs";
77273
+ import { closeSync as closeSync6, fstatSync, openSync as openSync6, readSync } from "node:fs";
77202
77274
  var PHOTO_MAX_DIMENSION_SUM = 1e4;
77203
77275
  var PHOTO_MAX_ASPECT_RATIO = 10;
77204
77276
  var PHOTO_MAX_BYTES = 10 * 1024 * 1024;
@@ -77279,7 +77351,7 @@ var JPEG_PROBE_BYTES = 64 * 1024;
77279
77351
  function probeImageFile(path2) {
77280
77352
  let fd;
77281
77353
  try {
77282
- fd = openSync5(path2, "r");
77354
+ fd = openSync6(path2, "r");
77283
77355
  const bytes = fstatSync(fd).size;
77284
77356
  const headBuf = Buffer.alloc(HEADER_PROBE_BYTES);
77285
77357
  const headRead = readSync(fd, headBuf, 0, HEADER_PROBE_BYTES, 0);
@@ -77298,7 +77370,7 @@ function probeImageFile(path2) {
77298
77370
  } finally {
77299
77371
  if (fd !== undefined) {
77300
77372
  try {
77301
- closeSync5(fd);
77373
+ closeSync6(fd);
77302
77374
  } catch {}
77303
77375
  }
77304
77376
  }
@@ -79526,10 +79598,10 @@ function detectStatusSurfaceDegraded(t) {
79526
79598
 
79527
79599
  // gateway/turn-active-marker.ts
79528
79600
  import {
79529
- closeSync as closeSync6,
79601
+ closeSync as closeSync7,
79530
79602
  existsSync as existsSync28,
79531
79603
  mkdirSync as mkdirSync31,
79532
- openSync as openSync6,
79604
+ openSync as openSync7,
79533
79605
  readFileSync as readFileSync26,
79534
79606
  statSync as statSync12,
79535
79607
  unlinkSync as unlinkSync15,
@@ -79555,8 +79627,8 @@ function touchTurnActiveMarker(stateDir) {
79555
79627
  utimesSync(path2, now, now);
79556
79628
  } catch {
79557
79629
  try {
79558
- const fd = openSync6(path2, "r+");
79559
- closeSync6(fd);
79630
+ const fd = openSync7(path2, "r+");
79631
+ closeSync7(fd);
79560
79632
  } catch {}
79561
79633
  }
79562
79634
  }
@@ -80882,7 +80954,8 @@ function createNarrativeLane(deps) {
80882
80954
  toolCount: turn.labeledToolCount,
80883
80955
  state: final ? "done" : "running",
80884
80956
  model: turn.currentModel,
80885
- totalTokens: turn.totalTokens
80957
+ totalTokens: turn.totalTokens,
80958
+ resultText: final ? turn.lastReplyText : undefined
80886
80959
  };
80887
80960
  return renderActivityFeedWithNested(turn.mirrorLines, childLines, final, liveSuffix, stepCount, header);
80888
80961
  }
@@ -81199,7 +81272,8 @@ function createNarrativeLane(deps) {
81199
81272
  elapsedMs: livenessElapsed,
81200
81273
  toolCount: turn.labeledToolCount,
81201
81274
  state: "done",
81202
- model: turn.currentModel
81275
+ model: turn.currentModel,
81276
+ resultText: turn.lastReplyText
81203
81277
  };
81204
81278
  finalHtml = renderActivityFeedWithNested(["Working\u2026"], [], true, "", undefined, livenessHeader);
81205
81279
  }
@@ -81569,7 +81643,7 @@ function resolveAgentDirFromEnv() {
81569
81643
  }
81570
81644
 
81571
81645
  // active-reactions.ts
81572
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync25, renameSync as renameSync12, existsSync as existsSync29, unlinkSync as unlinkSync16 } from "node:fs";
81646
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync25, renameSync as renameSync13, existsSync as existsSync29, unlinkSync as unlinkSync16 } from "node:fs";
81573
81647
  import { join as join37 } from "node:path";
81574
81648
  var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
81575
81649
  function reactionsPath(agentDir) {
@@ -81615,7 +81689,7 @@ function writeActiveReactions(agentDir, reactions) {
81615
81689
  try {
81616
81690
  writeFileSync25(tmp, JSON.stringify(reactions) + `
81617
81691
  `, "utf-8");
81618
- renameSync12(tmp, p);
81692
+ renameSync13(tmp, p);
81619
81693
  } catch {}
81620
81694
  }
81621
81695
  function addActiveReaction(agentDir, reaction) {
@@ -81637,7 +81711,7 @@ function clearActiveReactions(agentDir) {
81637
81711
  }
81638
81712
 
81639
81713
  // active-reactions.ts
81640
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync26, renameSync as renameSync13, existsSync as existsSync30, unlinkSync as unlinkSync17 } from "node:fs";
81714
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync26, renameSync as renameSync14, existsSync as existsSync30, unlinkSync as unlinkSync17 } from "node:fs";
81641
81715
  import { join as join38 } from "node:path";
81642
81716
  var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
81643
81717
  function reactionsPath2(agentDir) {
@@ -83866,7 +83940,7 @@ async function menuWithBannerStatic(deps, banner) {
83866
83940
  }
83867
83941
 
83868
83942
  // gateway/session-model-file.ts
83869
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync27, renameSync as renameSync14, rmSync as rmSync6 } from "node:fs";
83943
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync27, renameSync as renameSync15, rmSync as rmSync7 } from "node:fs";
83870
83944
  import { join as join40 } from "node:path";
83871
83945
  var SESSION_MODEL_FILE = ".session-model";
83872
83946
  var CONFIGURED_DEFAULT_MODEL_FILE = ".configured-default-model";
@@ -83874,7 +83948,7 @@ var SESSION_MODEL_BOOT_ATTEMPTS_FILE = ".session-model-boot-attempts";
83874
83948
  function atomicWrite(path2, content3) {
83875
83949
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
83876
83950
  writeFileSync27(tmp, content3, "utf8");
83877
- renameSync14(tmp, path2);
83951
+ renameSync15(tmp, path2);
83878
83952
  }
83879
83953
  function serializeSessionModel(rec) {
83880
83954
  return `${JSON.stringify({
@@ -83889,7 +83963,7 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
83889
83963
  throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
83890
83964
  }
83891
83965
  try {
83892
- rmSync6(join40(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
83966
+ rmSync7(join40(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
83893
83967
  } catch {}
83894
83968
  atomicWrite(join40(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
83895
83969
  }
@@ -83902,15 +83976,15 @@ function readSessionModelFileRaw(agentDir) {
83902
83976
  }
83903
83977
  function clearSessionModelFile(agentDir) {
83904
83978
  try {
83905
- rmSync6(join40(agentDir, SESSION_MODEL_FILE), { force: true });
83979
+ rmSync7(join40(agentDir, SESSION_MODEL_FILE), { force: true });
83906
83980
  } catch {}
83907
83981
  }
83908
83982
  function consumeSessionModelCarrierOnHealthyBoot(agentDir) {
83909
83983
  try {
83910
- rmSync6(join40(agentDir, SESSION_MODEL_FILE), { force: true });
83984
+ rmSync7(join40(agentDir, SESSION_MODEL_FILE), { force: true });
83911
83985
  } catch {}
83912
83986
  try {
83913
- rmSync6(join40(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
83987
+ rmSync7(join40(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
83914
83988
  } catch {}
83915
83989
  }
83916
83990
  function restoreSessionModelFileRaw(agentDir, raw) {
@@ -83941,7 +84015,7 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
83941
84015
  }
83942
84016
  function clearSessionEffortFile(agentDir) {
83943
84017
  try {
83944
- rmSync6(join40(agentDir, SESSION_EFFORT_FILE), { force: true });
84018
+ rmSync7(join40(agentDir, SESSION_EFFORT_FILE), { force: true });
83945
84019
  } catch {}
83946
84020
  }
83947
84021
  var PREMIUM_RECOVERY_FILE = ".premium-recovery";
@@ -83983,7 +84057,7 @@ function readPremiumRecoveryFile(agentDir) {
83983
84057
  }
83984
84058
  function clearPremiumRecoveryFile(agentDir) {
83985
84059
  try {
83986
- rmSync6(join40(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
84060
+ rmSync7(join40(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
83987
84061
  } catch {}
83988
84062
  }
83989
84063
 
@@ -86239,7 +86313,7 @@ function nextCompactNotify(state6, ev) {
86239
86313
 
86240
86314
  // gateway/hostd-dispatch.ts
86241
86315
  import { existsSync as existsSync36 } from "node:fs";
86242
- import { randomBytes as randomBytes9 } from "node:crypto";
86316
+ import { randomBytes as randomBytes10 } from "node:crypto";
86243
86317
  init_loader();
86244
86318
  var _hostdEnabled2;
86245
86319
  function isHostdEnabled2() {
@@ -86286,7 +86360,7 @@ async function tryHostdDispatch2(agentName3, req, timeoutMs = 5000) {
86286
86360
  }
86287
86361
  }
86288
86362
  function hostdRequestId2(prefix) {
86289
- return `${prefix}-${Date.now()}-${randomBytes9(4).toString("hex")}`;
86363
+ return `${prefix}-${Date.now()}-${randomBytes10(4).toString("hex")}`;
86290
86364
  }
86291
86365
  function withOperatorAttestation(req, passphrase) {
86292
86366
  if (!passphrase)
@@ -86348,6 +86422,56 @@ function shouldSweepChatAtBoot(chatId) {
86348
86422
  return n < 0;
86349
86423
  }
86350
86424
 
86425
+ // gateway/stale-pin-sweep-store.ts
86426
+ var SWEEP_MAX_ATTEMPTS = 8;
86427
+ var SWEEP_ENVELOPE_VERSIONS = new Set([1]);
86428
+ function sweepTargetKey(chatId, threadId) {
86429
+ return `${chatId}:${threadId ?? "-"}`;
86430
+ }
86431
+ function isCursorRow(x) {
86432
+ if (x == null || typeof x !== "object")
86433
+ return false;
86434
+ const o = x;
86435
+ return typeof o.chatId === "string" && o.chatId.length > 0 && (o.threadId === undefined || typeof o.threadId === "number") && (o.kind === "dm" || o.kind === "forum-topic" || o.kind === "supergroup") && typeof o.popped === "number" && typeof o.done === "boolean" && typeof o.attempts === "number" && (o.doneIds === undefined || Array.isArray(o.doneIds) && o.doneIds.every((n) => typeof n === "number")) && (o.lastStatus === undefined || typeof o.lastStatus === "string") && typeof o.updatedAt === "number";
86436
+ }
86437
+ function loadSweepCursors(path2, fs2) {
86438
+ if (!fs2.existsSync(path2))
86439
+ return [];
86440
+ let raw = "";
86441
+ try {
86442
+ raw = fs2.readFileSync(path2);
86443
+ } catch {
86444
+ return [];
86445
+ }
86446
+ let parsed;
86447
+ try {
86448
+ parsed = JSON.parse(raw);
86449
+ } catch {
86450
+ return [];
86451
+ }
86452
+ if (parsed == null || typeof parsed !== "object")
86453
+ return [];
86454
+ const env = parsed;
86455
+ if (typeof env.v !== "number" || !SWEEP_ENVELOPE_VERSIONS.has(env.v) || !Array.isArray(env.cursors)) {
86456
+ return [];
86457
+ }
86458
+ return env.cursors.filter(isCursorRow);
86459
+ }
86460
+ function persistSweepCursors(path2, fs2, cursors, log = (l) => process.stderr.write(l)) {
86461
+ const env = { v: 1, cursors: [...cursors] };
86462
+ try {
86463
+ fs2.writeFileSync(path2, JSON.stringify(env));
86464
+ } catch (err) {
86465
+ log(`stale-pin-sweep-store: persist FAILED path=${path2}: ${err.message} \u2014 ` + `durability degraded; a restart mid-sweep will re-seed instead of resume
86466
+ `);
86467
+ }
86468
+ }
86469
+ function upsertSweepCursor(path2, fs2, cursor, log = (l) => process.stderr.write(l)) {
86470
+ const key = sweepTargetKey(cursor.chatId, cursor.threadId);
86471
+ const others = loadSweepCursors(path2, fs2).filter((c) => sweepTargetKey(c.chatId, c.threadId) !== key);
86472
+ persistSweepCursors(path2, fs2, [...others, cursor], log);
86473
+ }
86474
+
86351
86475
  // gateway/boot-sweep-gate.ts
86352
86476
  function createBootSweepGate(args) {
86353
86477
  let armed = false;
@@ -86392,21 +86516,22 @@ async function step(name, fn, log) {
86392
86516
  }
86393
86517
  async function runBootPinSweepSteps(deps) {
86394
86518
  const log = deps.log ?? ((l) => process.stderr.write(l));
86395
- let dmChatIds = [];
86396
- await step("dm-chat-scan", async () => {
86397
- dmChatIds = deps.scanDmChatIds();
86519
+ let targets = [];
86520
+ await step("sweep-target-scan", async () => {
86521
+ targets = deps.scanSweepTargets();
86398
86522
  }, log);
86399
86523
  await step("status-pin-cleanup", deps.statusPinCleanup, log);
86400
86524
  await step("activity-card-reaper", deps.activityCardReaper, log);
86401
86525
  await step("queued-card-reaper", deps.queuedCardReaper, log);
86402
- await step("enable-dm-sweep", async () => deps.enableDmSweep(), log);
86403
- for (const id of dmChatIds)
86404
- await step(`dm-sweep:${id}`, () => deps.sweepDm(id), log);
86526
+ await step("enable-sweep", async () => deps.enableSweep(), log);
86527
+ for (const t of targets) {
86528
+ await step(`stale-pin-sweep:${sweepTargetKey(t.chatId, t.threadId)}`, () => deps.sweepTarget(t), log);
86529
+ }
86405
86530
  }
86406
86531
 
86407
86532
  // gateway/status-pin-retarget.ts
86408
86533
  async function runStatusPinReconcile(args) {
86409
- const { pinKey, chatId, prev, desired, persist, runPin, claims } = args;
86534
+ const { pinKey, chatId, threadId, prev, desired, persist, runPin, claims } = args;
86410
86535
  const now = args.now ?? Date.now;
86411
86536
  const commit = (next) => {
86412
86537
  if (next == null) {
@@ -86414,7 +86539,7 @@ async function runStatusPinReconcile(args) {
86414
86539
  return;
86415
86540
  }
86416
86541
  const pinnedAt = claims.get(pinKey)?.pinnedAt ?? now();
86417
- claims.set(pinKey, { messageId: next.messageId, chatId, pinnedAt });
86542
+ claims.set(pinKey, { messageId: next.messageId, chatId, threadId, pinnedAt });
86418
86543
  };
86419
86544
  const runLeg = (legAction, from) => {
86420
86545
  if (persist == null)
@@ -86425,6 +86550,7 @@ async function runStatusPinReconcile(args) {
86425
86550
  fs: persist.fs,
86426
86551
  pinKey,
86427
86552
  chatId,
86553
+ threadId,
86428
86554
  op,
86429
86555
  applyPin: () => runPin(legAction, from),
86430
86556
  now: now()
@@ -86524,24 +86650,77 @@ function createStatusPinApi(getBot, robust) {
86524
86650
  };
86525
86651
  }
86526
86652
 
86527
- // gateway/dm-pin-sweep.ts
86528
- function isDmChatId(chatId) {
86529
- const n = Number(chatId);
86530
- return Number.isFinite(n) && Number.isInteger(n) && n > 0;
86531
- }
86532
- function collectDmChatIdsFromStores(input) {
86533
- const out = new Set;
86653
+ // gateway/stale-pin-sweep.ts
86654
+ var GROUP_SWEEP_GATE = {
86655
+ minCallDelayMs: 6000,
86656
+ maxPopsPerChatPerSweep: Number.POSITIVE_INFINITY,
86657
+ maxPinOpsPerMinute: 10
86658
+ };
86659
+ function collectSweepTargets(input) {
86660
+ const out = new Map;
86534
86661
  const add = (rows) => {
86535
86662
  if (rows == null)
86536
86663
  return;
86537
- for (const r of rows)
86538
- if (isDmChatId(r.chatId))
86539
- out.add(r.chatId);
86664
+ for (const r of rows) {
86665
+ if (typeof r.chatId !== "string" || r.chatId.length === 0)
86666
+ continue;
86667
+ const threadId = r.threadId ?? undefined;
86668
+ const key = sweepTargetKey(r.chatId, threadId);
86669
+ let target = out.get(key);
86670
+ if (target == null) {
86671
+ target = { chatId: r.chatId, threadId, messageIds: [] };
86672
+ out.set(key, target);
86673
+ }
86674
+ const id = r.messageId ?? (r.pinned === true ? r.activityMessageId : undefined);
86675
+ if (typeof id === "number" && !target.messageIds.includes(id))
86676
+ target.messageIds.push(id);
86677
+ }
86540
86678
  };
86541
86679
  add(input.statusPins);
86542
86680
  add(input.activityCards);
86543
86681
  add(input.queuedCards);
86544
- return [...out];
86682
+ return [...out.values()];
86683
+ }
86684
+
86685
+ // gateway/stale-pin-sweep.ts
86686
+ var DM_SWEEP_GATE = {
86687
+ minCallDelayMs: 1500,
86688
+ maxPopsPerChatPerSweep: 40,
86689
+ maxPinOpsPerMinute: 30
86690
+ };
86691
+ var GROUP_SWEEP_GATE2 = {
86692
+ minCallDelayMs: 6000,
86693
+ maxPopsPerChatPerSweep: Number.POSITIVE_INFINITY,
86694
+ maxPinOpsPerMinute: 10
86695
+ };
86696
+ var FLOOD_BACKOFF_FACTOR = 2;
86697
+ var MAX_CONSECUTIVE_FLOOD_WAITS = 3;
86698
+ var MAX_IDENTICAL_RETRY_AFTER_NO_PROGRESS = 2;
86699
+ var CIRCUIT_BREAKER_RETRY_AFTER_SEC = 60;
86700
+ var VERIFY_READ_GAP_MS = 1800;
86701
+ var VERIFY_READ_MAX_PAIRS = 3;
86702
+ var UNPIN_ALL_FORUM_TOPIC_ENABLED = false;
86703
+ function mayUnpinAllForumTopic(kind, override) {
86704
+ if (kind !== "forum-topic")
86705
+ return false;
86706
+ return override ?? UNPIN_ALL_FORUM_TOPIC_ENABLED;
86707
+ }
86708
+ function gateFor(kind) {
86709
+ return kind === "dm" ? DM_SWEEP_GATE : GROUP_SWEEP_GATE2;
86710
+ }
86711
+ function budgetClassFor(kind) {
86712
+ return kind === "dm" ? "dm" : "group";
86713
+ }
86714
+ function isDmChatId(chatId) {
86715
+ const n = Number(chatId);
86716
+ return Number.isFinite(n) && Number.isInteger(n) && n > 0;
86717
+ }
86718
+ function classifyChatForSweep(args) {
86719
+ if (isDmChatId(args.chatId))
86720
+ return "dm";
86721
+ if (args.isForum === true && args.threadId != null)
86722
+ return "forum-topic";
86723
+ return "supergroup";
86545
86724
  }
86546
86725
  function unexpiredStoreRepinIds(rows, chatId, now) {
86547
86726
  const out = [];
@@ -86553,41 +86732,571 @@ function unexpiredStoreRepinIds(rows, chatId, now) {
86553
86732
  }
86554
86733
  return out;
86555
86734
  }
86556
- function createDmPinSweeper(deps) {
86557
- const swept = new Set;
86735
+ function description(err) {
86736
+ if (err != null && typeof err === "object") {
86737
+ const o = err;
86738
+ if (typeof o.description === "string" && o.description.length > 0) {
86739
+ return o.description.toLowerCase();
86740
+ }
86741
+ if (typeof o.message === "string" && o.message.length > 0)
86742
+ return o.message.toLowerCase();
86743
+ }
86744
+ return String(err).toLowerCase();
86745
+ }
86746
+ function retryAfterSeconds(err) {
86747
+ if (err == null || typeof err !== "object")
86748
+ return null;
86749
+ const o = err;
86750
+ const fromParams = o.parameters?.retry_after;
86751
+ if (typeof fromParams === "number" && fromParams >= 0)
86752
+ return fromParams;
86753
+ if (o.error_code === 429) {
86754
+ const m = /retry after (\d+)/.exec(description(err));
86755
+ if (m != null)
86756
+ return Number(m[1]);
86757
+ return 0;
86758
+ }
86759
+ return null;
86760
+ }
86761
+ function isPeerFloodError(err) {
86762
+ return description(err).includes("peer_flood");
86763
+ }
86764
+ function isPinRightsError2(err) {
86765
+ return description(err).includes("not enough rights");
86766
+ }
86767
+ function isNothingToUnpinError(err) {
86768
+ return description(err).includes("message to unpin not found");
86769
+ }
86770
+ function createPinOpBudget(now) {
86771
+ const windows = { dm: [], group: [] };
86772
+ const limits = {
86773
+ dm: DM_SWEEP_GATE.maxPinOpsPerMinute,
86774
+ group: GROUP_SWEEP_GATE2.maxPinOpsPerMinute
86775
+ };
86776
+ const prune = (cls, t) => {
86777
+ const w = windows[cls];
86778
+ while (w.length > 0 && t - w[0] >= 60000)
86779
+ w.shift();
86780
+ };
86558
86781
  return {
86559
- hasSwept: (chatId) => swept.has(chatId),
86560
- async sweep(chatId) {
86561
- if (!isDmChatId(chatId))
86562
- return;
86563
- if (!deps.eligible())
86564
- return;
86565
- if (swept.has(chatId))
86566
- return;
86567
- swept.add(chatId);
86568
- const liveIds = [...new Set(deps.liveTrackedMessageIds(chatId))];
86782
+ waitMs(cls) {
86783
+ const t = now();
86784
+ prune(cls, t);
86785
+ const w = windows[cls];
86786
+ if (w.length < limits[cls])
86787
+ return 0;
86788
+ return Math.max(0, 60000 - (t - w[0]));
86789
+ },
86790
+ record(cls) {
86791
+ windows[cls].push(now());
86792
+ }
86793
+ };
86794
+ }
86795
+ function createStalePinSweeper(deps) {
86796
+ const log = deps.log ?? ((l) => process.stderr.write(l));
86797
+ const budget = createPinOpBudget(deps.now);
86798
+ let circuitOpen = false;
86799
+ let lastWriteAt = 0;
86800
+ const commit = (cursor) => {
86801
+ try {
86802
+ upsertSweepCursor(deps.store.path, deps.store.fs, cursor, log);
86803
+ } catch (err) {
86804
+ log(`stale-pin-sweep: cursor persist failed: ${err.message}
86805
+ `);
86806
+ }
86807
+ };
86808
+ const loadCursor = (t, kind) => {
86809
+ const key = sweepTargetKey(t.chatId, t.threadId);
86810
+ let rows = [];
86811
+ try {
86812
+ rows = loadSweepCursors(deps.store.path, deps.store.fs);
86813
+ } catch {
86814
+ rows = [];
86815
+ }
86816
+ const existing = rows.find((c) => sweepTargetKey(c.chatId, c.threadId) === key);
86817
+ if (existing != null)
86818
+ return { ...existing, kind };
86819
+ return {
86820
+ chatId: t.chatId,
86821
+ threadId: t.threadId,
86822
+ kind,
86823
+ popped: 0,
86824
+ done: false,
86825
+ attempts: 0,
86826
+ updatedAt: deps.now()
86827
+ };
86828
+ };
86829
+ const awaitWriteSlot = async (kind) => {
86830
+ const cls = budgetClassFor(kind);
86831
+ const wait = budget.waitMs(cls);
86832
+ if (wait > 0)
86833
+ return false;
86834
+ const gate = gateFor(kind);
86835
+ const since = deps.now() - lastWriteAt;
86836
+ if (lastWriteAt > 0 && since < gate.minCallDelayMs) {
86837
+ await deps.sleep(gate.minCallDelayMs - since);
86838
+ }
86839
+ lastWriteAt = deps.now();
86840
+ budget.record(cls);
86841
+ return true;
86842
+ };
86843
+ const readTopVerified = async (chatId) => {
86844
+ const read = async () => {
86569
86845
  try {
86570
- await deps.unpinAll(chatId);
86846
+ return { v: await deps.getTopPinnedMessageId(chatId) };
86847
+ } catch {
86848
+ return null;
86849
+ }
86850
+ };
86851
+ let prev = await read();
86852
+ if (prev == null)
86853
+ return null;
86854
+ for (let i = 0;i < VERIFY_READ_MAX_PAIRS; i++) {
86855
+ await deps.sleep(VERIFY_READ_GAP_MS);
86856
+ const next = await read();
86857
+ if (next == null)
86858
+ return null;
86859
+ if (next.v === prev.v)
86860
+ return { top: next.v };
86861
+ prev = next;
86862
+ }
86863
+ return null;
86864
+ };
86865
+ const classify = (err) => {
86866
+ if (isPeerFloodError(err))
86867
+ return { kind: "breaker", detail: "PEER_FLOOD" };
86868
+ const retry = retryAfterSeconds(err);
86869
+ if (retry != null) {
86870
+ if (retry > CIRCUIT_BREAKER_RETRY_AFTER_SEC) {
86871
+ return { kind: "breaker", detail: `retry_after=${retry}s > ${CIRCUIT_BREAKER_RETRY_AFTER_SEC}s` };
86872
+ }
86873
+ return { kind: "flood", seconds: retry };
86874
+ }
86875
+ if (isPinRightsError2(err))
86876
+ return { kind: "rights" };
86877
+ return { kind: "other", detail: err?.message ?? String(err) };
86878
+ };
86879
+ const repinUnpinLoop = async (target, kind, cursor) => {
86880
+ const gate = gateFor(kind);
86881
+ let popped = 0;
86882
+ let consecutiveFloods = 0;
86883
+ let lastRetryAfter = null;
86884
+ let noProgressAtSameRetryAfter = 0;
86885
+ let lastObservedTop = null;
86886
+ let unverifiedPop = false;
86887
+ const creditPop = () => {
86888
+ popped++;
86889
+ cursor.popped++;
86890
+ cursor.updatedAt = deps.now();
86891
+ commit(cursor);
86892
+ };
86893
+ for (let i = 0;i < gate.maxPopsPerChatPerSweep; i++) {
86894
+ if (circuitOpen) {
86895
+ return { status: "aborted-circuit-breaker", popped, detail: "breaker open" };
86896
+ }
86897
+ const observed = await readTopVerified(target.chatId);
86898
+ if (observed == null) {
86899
+ return { status: "incomplete", popped, detail: "pin-stack read unverifiable" };
86900
+ }
86901
+ if (unverifiedPop) {
86902
+ if (observed.top != null && observed.top === lastObservedTop) {
86903
+ return {
86904
+ status: "incomplete",
86905
+ popped,
86906
+ detail: `no progress: message ${observed.top} still on top after a repin+unpin`
86907
+ };
86908
+ }
86909
+ creditPop();
86910
+ unverifiedPop = false;
86911
+ }
86912
+ if (observed.top == null) {
86913
+ return { status: "drained", popped };
86914
+ }
86915
+ lastObservedTop = observed.top;
86916
+ if (!await awaitWriteSlot(kind)) {
86917
+ return { status: "deferred-budget", popped, detail: "per-minute pin-op budget spent" };
86918
+ }
86919
+ try {
86920
+ await deps.pinSilent(target.chatId, observed.top);
86571
86921
  } catch (err) {
86572
- const msg = err instanceof Error ? err.message : String(err);
86573
- deps.log?.(`telegram gateway: dm-pin-sweep: unpin-all failed (chat=${chatId}): ${msg}
86574
- `);
86922
+ const d = classify(err);
86923
+ if (d.kind === "breaker") {
86924
+ circuitOpen = true;
86925
+ return { status: "aborted-circuit-breaker", popped, detail: d.detail };
86926
+ }
86927
+ if (d.kind === "rights")
86928
+ return { status: "skipped-no-rights", popped };
86929
+ if (d.kind === "flood") {
86930
+ consecutiveFloods++;
86931
+ if (lastRetryAfter === d.seconds)
86932
+ noProgressAtSameRetryAfter++;
86933
+ else
86934
+ noProgressAtSameRetryAfter = 1;
86935
+ lastRetryAfter = d.seconds;
86936
+ if (consecutiveFloods >= MAX_CONSECUTIVE_FLOOD_WAITS || noProgressAtSameRetryAfter >= MAX_IDENTICAL_RETRY_AFTER_NO_PROGRESS) {
86937
+ return {
86938
+ status: "deferred-flood",
86939
+ popped,
86940
+ detail: `flood: ${consecutiveFloods} consecutive 429s, retry_after=${d.seconds}s`
86941
+ };
86942
+ }
86943
+ await deps.sleep(d.seconds * 1000 * Math.pow(FLOOD_BACKOFF_FACTOR, consecutiveFloods - 1));
86944
+ continue;
86945
+ }
86946
+ return { status: "error", popped, detail: d.detail };
86947
+ }
86948
+ if (!await awaitWriteSlot(kind)) {
86949
+ return { status: "deferred-budget", popped, detail: "per-minute pin-op budget spent" };
86950
+ }
86951
+ try {
86952
+ await deps.unpin(target.chatId, observed.top);
86953
+ } catch (err) {
86954
+ const d = classify(err);
86955
+ if (d.kind === "breaker") {
86956
+ circuitOpen = true;
86957
+ return { status: "aborted-circuit-breaker", popped, detail: d.detail };
86958
+ }
86959
+ if (d.kind === "rights")
86960
+ return { status: "skipped-no-rights", popped };
86961
+ if (isNothingToUnpinError(err))
86962
+ return { status: "drained", popped };
86963
+ if (d.kind === "flood") {
86964
+ consecutiveFloods++;
86965
+ if (lastRetryAfter === d.seconds)
86966
+ noProgressAtSameRetryAfter++;
86967
+ else
86968
+ noProgressAtSameRetryAfter = 1;
86969
+ lastRetryAfter = d.seconds;
86970
+ if (consecutiveFloods >= MAX_CONSECUTIVE_FLOOD_WAITS || noProgressAtSameRetryAfter >= MAX_IDENTICAL_RETRY_AFTER_NO_PROGRESS) {
86971
+ return {
86972
+ status: "deferred-flood",
86973
+ popped,
86974
+ detail: `flood: ${consecutiveFloods} consecutive 429s, retry_after=${d.seconds}s`
86975
+ };
86976
+ }
86977
+ await deps.sleep(d.seconds * 1000 * Math.pow(FLOOD_BACKOFF_FACTOR, consecutiveFloods - 1));
86978
+ continue;
86979
+ }
86980
+ return { status: "error", popped, detail: d.detail };
86981
+ }
86982
+ consecutiveFloods = 0;
86983
+ unverifiedPop = true;
86984
+ }
86985
+ const finalRead = await readTopVerified(target.chatId);
86986
+ if (unverifiedPop && finalRead != null && finalRead.top !== lastObservedTop)
86987
+ creditPop();
86988
+ if (finalRead != null && finalRead.top == null)
86989
+ return { status: "drained", popped };
86990
+ return {
86991
+ status: "deferred-budget",
86992
+ popped,
86993
+ detail: `hit the ${gate.maxPopsPerChatPerSweep}-pop cap for this chat; yielding to next boot`
86994
+ };
86995
+ };
86996
+ const targetedUnpinDrain = async (target, kind, cursor, ids) => {
86997
+ const alreadyDone = new Set(cursor.doneIds ?? []);
86998
+ const pending = ids.filter((id) => !alreadyDone.has(id));
86999
+ if (pending.length === 0) {
87000
+ return { status: "drained", popped: 0, issued: 0, detail: "every recorded id already unpinned" };
87001
+ }
87002
+ const before = await readTopVerified(target.chatId);
87003
+ let issued = 0;
87004
+ let deferred = null;
87005
+ for (const id of pending) {
87006
+ if (circuitOpen) {
87007
+ deferred = { status: "aborted-circuit-breaker", popped: 0, issued, detail: "breaker open" };
87008
+ break;
87009
+ }
87010
+ if (!await awaitWriteSlot(kind)) {
87011
+ deferred = {
87012
+ status: "deferred-budget",
87013
+ popped: 0,
87014
+ issued,
87015
+ detail: `per-minute pin-op budget spent with ${pending.length - issued} ids left`
87016
+ };
87017
+ break;
87018
+ }
87019
+ try {
87020
+ await deps.unpin(target.chatId, id);
87021
+ } catch (err) {
87022
+ const d = classify(err);
87023
+ if (d.kind === "breaker") {
87024
+ circuitOpen = true;
87025
+ deferred = { status: "aborted-circuit-breaker", popped: 0, issued, detail: d.detail };
87026
+ break;
87027
+ }
87028
+ if (d.kind === "rights") {
87029
+ deferred = { status: "skipped-no-rights", popped: 0, issued };
87030
+ break;
87031
+ }
87032
+ if (d.kind === "flood") {
87033
+ deferred = {
87034
+ status: "deferred-flood",
87035
+ popped: 0,
87036
+ issued,
87037
+ detail: `flood: retry_after=${d.seconds}s`
87038
+ };
87039
+ break;
87040
+ }
87041
+ if (!isNothingToUnpinError(err)) {
87042
+ deferred = { status: "error", popped: 0, issued, detail: d.detail };
87043
+ break;
87044
+ }
87045
+ }
87046
+ issued++;
87047
+ alreadyDone.add(id);
87048
+ cursor.doneIds = [...alreadyDone];
87049
+ cursor.updatedAt = deps.now();
87050
+ commit(cursor);
87051
+ }
87052
+ const after = await readTopVerified(target.chatId);
87053
+ if (after == null) {
87054
+ return {
87055
+ status: deferred?.status ?? "incomplete",
87056
+ popped: 0,
87057
+ issued,
87058
+ detail: "pin-stack read unverifiable after the unpin pass"
87059
+ };
87060
+ }
87061
+ if (after.top == null) {
87062
+ const popped2 = issued;
87063
+ cursor.popped += popped2;
87064
+ commit(cursor);
87065
+ return { status: "drained", popped: popped2, issued };
87066
+ }
87067
+ if (deferred != null)
87068
+ return { ...deferred, popped: 0 };
87069
+ const ours = new Set(ids);
87070
+ if (ours.has(after.top)) {
87071
+ return {
87072
+ status: "incomplete",
87073
+ popped: 0,
87074
+ issued,
87075
+ detail: `recorded pin ${after.top} is still on top after a targeted unpin`
87076
+ };
87077
+ }
87078
+ const popped = before != null && before.top !== after.top ? 1 : 0;
87079
+ cursor.popped += popped;
87080
+ commit(cursor);
87081
+ return {
87082
+ status: "drained",
87083
+ popped,
87084
+ issued,
87085
+ detail: `unpinned ${issued} recorded id(s); top ${after.top} is not ours and was left alone`
87086
+ };
87087
+ };
87088
+ const forumTopicDrain = async (target, cursor) => {
87089
+ const threadId = target.threadId;
87090
+ if (threadId == null) {
87091
+ return { status: "skipped-nothing-recorded", popped: 0, detail: "no message_thread_id" };
87092
+ }
87093
+ if (!await awaitWriteSlot("forum-topic")) {
87094
+ return { status: "deferred-budget", popped: 0, detail: "per-minute pin-op budget spent" };
87095
+ }
87096
+ try {
87097
+ await deps.unpinAllForumTopicMessages(target.chatId, threadId);
87098
+ } catch (err) {
87099
+ const d = classify(err);
87100
+ if (d.kind === "breaker") {
87101
+ circuitOpen = true;
87102
+ return { status: "aborted-circuit-breaker", popped: 0, detail: d.detail };
87103
+ }
87104
+ if (d.kind === "rights")
87105
+ return { status: "skipped-no-rights", popped: 0 };
87106
+ if (d.kind === "flood") {
87107
+ return {
87108
+ status: "deferred-flood",
87109
+ popped: 0,
87110
+ detail: `unpinAllForumTopicMessages pegged at retry_after=${d.seconds}s; not looping`
87111
+ };
87112
+ }
87113
+ return { status: "error", popped: 0, detail: d.detail };
87114
+ }
87115
+ cursor.popped++;
87116
+ cursor.updatedAt = deps.now();
87117
+ commit(cursor);
87118
+ const observed = await readTopVerified(target.chatId);
87119
+ if (observed != null && observed.top == null)
87120
+ return { status: "drained", popped: 1 };
87121
+ return { status: "drained", popped: 1, detail: "topic drained; chat-wide stack may hold other topics" };
87122
+ };
87123
+ const sweepOnce = async (target) => {
87124
+ if (!deps.eligible())
87125
+ return { status: "skipped-not-eligible", popped: 0 };
87126
+ if (circuitOpen) {
87127
+ return { status: "aborted-circuit-breaker", popped: 0, detail: "breaker already open" };
87128
+ }
87129
+ const kind = classifyChatForSweep(target);
87130
+ const cursor = loadCursor(target, kind);
87131
+ if (cursor.done)
87132
+ return { status: "already-drained", popped: 0 };
87133
+ if (cursor.attempts >= SWEEP_MAX_ATTEMPTS) {
87134
+ return { status: "forfeited", popped: 0, detail: `${cursor.attempts} attempts exhausted` };
87135
+ }
87136
+ if (kind !== "dm") {
87137
+ let allowed = false;
87138
+ try {
87139
+ allowed = await deps.canPinInChat(target.chatId);
87140
+ } catch {
87141
+ allowed = false;
86575
87142
  }
86576
- for (const messageId of liveIds) {
87143
+ if (!allowed) {
87144
+ cursor.attempts++;
87145
+ cursor.lastStatus = "skipped-no-rights";
87146
+ cursor.updatedAt = deps.now();
87147
+ commit(cursor);
87148
+ return { status: "skipped-no-rights", popped: 0 };
87149
+ }
87150
+ }
87151
+ cursor.attempts++;
87152
+ cursor.updatedAt = deps.now();
87153
+ commit(cursor);
87154
+ const protectedIds = [...new Set(deps.protectedMessageIds(target.chatId))];
87155
+ let result;
87156
+ try {
87157
+ if (kind === "dm") {
87158
+ result = await repinUnpinLoop(target, kind, cursor);
87159
+ } else if (mayUnpinAllForumTopic(kind, deps.allowUnpinAllForumTopic)) {
87160
+ result = await forumTopicDrain(target, cursor);
87161
+ } else {
87162
+ const keep = new Set(protectedIds);
87163
+ const ids = [
87164
+ ...new Set([
87165
+ ...target.messageIds ?? [],
87166
+ ...deps.recordedPinIds(target.chatId, target.threadId)
87167
+ ])
87168
+ ].filter((id) => !keep.has(id));
87169
+ result = ids.length === 0 ? {
87170
+ status: "skipped-nothing-recorded",
87171
+ popped: 0,
87172
+ issued: 0,
87173
+ detail: "no recorded pin ids for this target \u2014 nothing this gateway may remove"
87174
+ } : await targetedUnpinDrain(target, kind, cursor, ids);
87175
+ }
87176
+ } catch (err) {
87177
+ result = { status: "error", popped: 0, detail: err.message };
87178
+ }
87179
+ if (result.status === "drained" || result.status === "skipped-nothing-recorded") {
87180
+ cursor.done = true;
87181
+ }
87182
+ cursor.lastStatus = result.status;
87183
+ cursor.updatedAt = deps.now();
87184
+ commit(cursor);
87185
+ if (kind === "dm" && (result.status === "drained" || result.popped > 0)) {
87186
+ for (const messageId of protectedIds) {
87187
+ if (circuitOpen)
87188
+ break;
87189
+ if (!await awaitWriteSlot(kind))
87190
+ break;
86577
87191
  try {
86578
- await deps.pinSilent(chatId, messageId);
87192
+ await deps.pinSilent(target.chatId, messageId);
86579
87193
  } catch (err) {
86580
- const msg = err instanceof Error ? err.message : String(err);
86581
- deps.log?.(`telegram gateway: dm-pin-sweep: re-pin failed ` + `(chat=${chatId} msg=${messageId}): ${msg}
87194
+ log(`telegram gateway: stale-pin-sweep: re-pin of protected message failed ` + `(chat=${target.chatId} msg=${messageId}): ${err.message}
86582
87195
  `);
86583
87196
  }
86584
87197
  }
86585
- if (liveIds.length > 0) {
86586
- deps.log?.(`telegram gateway: dm-pin-sweep: cleared stacked pins in DM ${chatId}, ` + `re-pinned ${liveIds.length} live card(s)
86587
- `);
86588
- }
86589
87198
  }
87199
+ log(`telegram gateway: stale-pin-sweep: chat=${target.chatId} ` + `thread=${target.threadId ?? "-"} kind=${kind} status=${result.status} ` + `popped=${result.popped} issued=${result.issued ?? 0} total=${cursor.popped}` + (result.detail != null ? ` (${result.detail})` : "") + `
87200
+ `);
87201
+ return result;
87202
+ };
87203
+ const attempted = new Set;
87204
+ const inFlight = new Map;
87205
+ const sweepTarget = (target) => {
87206
+ const key = sweepTargetKey(target.chatId, target.threadId);
87207
+ const running = inFlight.get(key);
87208
+ if (running != null)
87209
+ return running;
87210
+ if (!deps.eligible())
87211
+ return Promise.resolve({ status: "skipped-not-eligible", popped: 0 });
87212
+ if (attempted.has(key))
87213
+ return Promise.resolve({ status: "already-attempted", popped: 0 });
87214
+ attempted.add(key);
87215
+ const p = sweepOnce(target).finally(() => inFlight.delete(key));
87216
+ inFlight.set(key, p);
87217
+ return p;
86590
87218
  };
87219
+ return { sweepTarget, isCircuitOpen: () => circuitOpen };
87220
+ }
87221
+
87222
+ // gateway/stale-pin-sweep-wiring.ts
87223
+ function protectedPinIds(args) {
87224
+ const ids = [];
87225
+ for (const claim of args.claims) {
87226
+ if (claim.chatId === args.chatId)
87227
+ ids.push(claim.messageId);
87228
+ }
87229
+ try {
87230
+ ids.push(...unexpiredStoreRepinIds(args.loadPinRows(), args.chatId, args.now));
87231
+ } catch (err) {
87232
+ args.log(`telegram gateway: stale-pin-sweep: store repin scan failed ` + `(chat=${args.chatId}): ${err.message}
87233
+ `);
87234
+ }
87235
+ return ids;
87236
+ }
87237
+ function recordedPinIdsFor(rows, chatId, threadId) {
87238
+ const out = [];
87239
+ for (const r of rows) {
87240
+ if (r.chatId !== chatId)
87241
+ continue;
87242
+ if ((r.threadId ?? undefined) !== threadId)
87243
+ continue;
87244
+ if (typeof r.messageId === "number" && !out.includes(r.messageId))
87245
+ out.push(r.messageId);
87246
+ }
87247
+ return out;
87248
+ }
87249
+ function createGatewayStalePinSweeper(w) {
87250
+ const log = w.log ?? ((line) => process.stderr.write(line));
87251
+ const now = w.now ?? Date.now;
87252
+ const { handle: bot, call } = w.telegram;
87253
+ return createStalePinSweeper({
87254
+ getTopPinnedMessageId: async (chatId) => {
87255
+ const chat = await call(() => bot().api.getChat(chatId), {
87256
+ chat_id: chatId,
87257
+ verb: "stale-pin-sweep.get-chat"
87258
+ });
87259
+ return chat?.pinned_message?.message_id ?? null;
87260
+ },
87261
+ pinSilent: (chatId, messageId) => call(() => bot().api.pinChatMessage(chatId, messageId, { disable_notification: true }), { chat_id: chatId, verb: "stale-pin-sweep.repin" }),
87262
+ unpin: (chatId, messageId) => call(() => bot().api.unpinChatMessage(chatId, messageId), { chat_id: chatId, verb: "stale-pin-sweep.unpin" }),
87263
+ unpinAllForumTopicMessages: (chatId, threadId) => call(() => bot().api.unpinAllForumTopicMessages(chatId, threadId), {
87264
+ chat_id: chatId,
87265
+ verb: "stale-pin-sweep.unpin-all-topic"
87266
+ }),
87267
+ canPinInChat: async (chatId) => {
87268
+ const self = bot().botInfo?.id;
87269
+ if (self == null)
87270
+ return false;
87271
+ const member = await call(() => bot().api.getChatMember(chatId, self), {
87272
+ chat_id: chatId,
87273
+ verb: "stale-pin-sweep.get-chat-member"
87274
+ });
87275
+ return member?.status === "administrator" && member.can_pin_messages === true;
87276
+ },
87277
+ recordedPinIds: (chatId, threadId) => {
87278
+ try {
87279
+ return recordedPinIdsFor(w.loadPinRows(), chatId, threadId);
87280
+ } catch (err) {
87281
+ log(`telegram gateway: stale-pin-sweep: recorded-pin scan failed ` + `(chat=${chatId}): ${err.message}
87282
+ `);
87283
+ return [];
87284
+ }
87285
+ },
87286
+ protectedMessageIds: (chatId) => protectedPinIds({
87287
+ chatId,
87288
+ claims: w.claims(),
87289
+ loadPinRows: w.loadPinRows,
87290
+ now: now(),
87291
+ log
87292
+ }),
87293
+ eligible: w.eligible,
87294
+ sleep: w.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))),
87295
+ now,
87296
+ store: w.store,
87297
+ allowUnpinAllForumTopic: w.allowUnpinAllForumTopic,
87298
+ log
87299
+ });
86591
87300
  }
86592
87301
 
86593
87302
  // gateway/webhook-ingest-server.ts
@@ -87751,7 +88460,7 @@ function recordWebhookEvent(rec, deps = {}) {
87751
88460
 
87752
88461
  // gateway/ipc-server.ts
87753
88462
  init_format();
87754
- import { renameSync as renameSync16, unlinkSync as unlinkSync20, chmodSync as chmodSync11 } from "fs";
88463
+ import { renameSync as renameSync17, unlinkSync as unlinkSync20, chmodSync as chmodSync11 } from "fs";
87755
88464
  var MAX_BUFFER_SIZE = 1024 * 1024;
87756
88465
  var VALID_OPERATOR_KINDS = new Set([
87757
88466
  "credentials-expired",
@@ -87962,7 +88671,7 @@ function createIpcServer(options) {
87962
88671
  heartbeatTimeoutMs = 30000
87963
88672
  } = options;
87964
88673
  try {
87965
- renameSync16(socketPath, socketPath + ".bak");
88674
+ renameSync17(socketPath, socketPath + ".bak");
87966
88675
  } catch {}
87967
88676
  try {
87968
88677
  unlinkSync20(socketPath + ".bak");
@@ -88352,7 +89061,7 @@ function createIpcServer(options) {
88352
89061
  clientBySocketId.clear();
88353
89062
  server.stop(true);
88354
89063
  try {
88355
- renameSync16(socketPath, socketPath + ".bak");
89064
+ renameSync17(socketPath, socketPath + ".bak");
88356
89065
  } catch {}
88357
89066
  }
88358
89067
  };
@@ -89256,6 +89965,7 @@ function obligationEscalationText(o) {
89256
89965
  }
89257
89966
 
89258
89967
  // gateway/obligation-store.ts
89968
+ import { dirname as dirname20 } from "node:path";
89259
89969
  function isObligationRow(x) {
89260
89970
  if (x == null || typeof x !== "object")
89261
89971
  return false;
@@ -89311,9 +90021,17 @@ function persistObligations(path3, fs3, snapshot, log = (l) => process.stderr.wr
89311
90021
  const tmp = path3 + ".tmp";
89312
90022
  try {
89313
90023
  fs3.writeFileSync(tmp, JSON.stringify(env));
90024
+ fs3.fsyncFileSync(tmp);
89314
90025
  fs3.renameSync(tmp, path3);
89315
90026
  } catch (err) {
89316
90027
  log(`obligation-store: persist FAILED path=${path3}: ${err.message} \u2014 ` + `durability degraded to in-memory
90028
+ `);
90029
+ return;
90030
+ }
90031
+ try {
90032
+ fs3.fsyncDirSync(dirname20(path3));
90033
+ } catch (err) {
90034
+ log(`obligation-store: directory fsync FAILED path=${path3}: ${err.message} \u2014 ` + `snapshot written but the rename may not survive a power cut
89317
90035
  `);
89318
90036
  }
89319
90037
  }
@@ -89397,12 +90115,12 @@ function createCapturedResumeDispatcher(ports) {
89397
90115
 
89398
90116
  // gateway/status-pin-store.ts
89399
90117
  var BOOT_UNPIN_MAX_ATTEMPTS = 5;
89400
- var SNAPSHOT_VERSIONS2 = new Set([1, 2, 3]);
90118
+ var SNAPSHOT_VERSIONS2 = new Set([1, 2, 3, 4]);
89401
90119
  function isPinRow2(x) {
89402
90120
  if (x == null || typeof x !== "object")
89403
90121
  return false;
89404
90122
  const o = x;
89405
- return typeof o.pinKey === "string" && o.pinKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && typeof o.messageId === "number" && (o.pending === undefined || typeof o.pending === "boolean") && (o.expiresAt === undefined || typeof o.expiresAt === "number") && (o.attempts === undefined || typeof o.attempts === "number") && (o.pinnedAt === undefined || typeof o.pinnedAt === "number");
90123
+ return typeof o.pinKey === "string" && o.pinKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && (o.threadId === undefined || typeof o.threadId === "number") && typeof o.messageId === "number" && (o.pending === undefined || typeof o.pending === "boolean") && (o.expiresAt === undefined || typeof o.expiresAt === "number") && (o.attempts === undefined || typeof o.attempts === "number") && (o.pinnedAt === undefined || typeof o.pinnedAt === "number");
89406
90124
  }
89407
90125
  function loadStatusPins2(path3, fs3) {
89408
90126
  if (!fs3.existsSync(path3))
@@ -89427,7 +90145,7 @@ function loadStatusPins2(path3, fs3) {
89427
90145
  return env.pins.filter(isPinRow2);
89428
90146
  }
89429
90147
  function persistStatusPins2(path3, fs3, snapshot, log = (l) => process.stderr.write(l)) {
89430
- const env = { v: 3, pins: [...snapshot] };
90148
+ const env = { v: 4, pins: [...snapshot] };
89431
90149
  const tmp = path3 + ".tmp";
89432
90150
  try {
89433
90151
  fs3.writeFileSync(tmp, JSON.stringify(env));
@@ -89775,6 +90493,7 @@ function decideWorkerPinReaps(args) {
89775
90493
  }
89776
90494
 
89777
90495
  // gateway/inbound-spool.ts
90496
+ import { dirname as dirname21 } from "node:path";
89778
90497
  function spoolId(msg) {
89779
90498
  if (msg.meta?.source === "subagent_handback" && typeof msg.meta?.subagent_jsonl_id === "string" && msg.meta.subagent_jsonl_id.length > 0) {
89780
90499
  return `s:handback:${msg.meta.subagent_jsonl_id}`;
@@ -89878,6 +90597,7 @@ function createInboundSpool(opts) {
89878
90597
  try {
89879
90598
  fs3.appendFileSync(path3, JSON.stringify(rec) + `
89880
90599
  `);
90600
+ fs3.fsyncFileSync(path3);
89881
90601
  if (consecutiveAppendFailures > 0) {
89882
90602
  log(`inbound-spool: append recovered path=${path3} after ` + `${consecutiveAppendFailures} failure(s) \u2014 durability restored
89883
90603
  `);
@@ -89936,11 +90656,19 @@ function createInboundSpool(opts) {
89936
90656
  fs3.writeFileSync(tmp, lines.length ? lines.join(`
89937
90657
  `) + `
89938
90658
  ` : "");
90659
+ fs3.fsyncFileSync(tmp);
89939
90660
  fs3.renameSync(tmp, path3);
89940
90661
  log(`inbound-spool: compacted path=${path3} live=${live.size}
89941
90662
  `);
89942
90663
  } catch (err) {
89943
90664
  log(`inbound-spool: compact FAILED path=${path3}: ${err.message}
90665
+ `);
90666
+ return;
90667
+ }
90668
+ try {
90669
+ fs3.fsyncDirSync(dirname21(path3));
90670
+ } catch (err) {
90671
+ log(`inbound-spool: compact directory fsync FAILED path=${path3}: ` + `${err.message} \u2014 compacted log written but the rename ` + `may not survive a power cut
89944
90672
  `);
89945
90673
  }
89946
90674
  }
@@ -92163,9 +92891,9 @@ function toolLabel(tool, input, preamble, precomputedLabel) {
92163
92891
  }
92164
92892
  case "Bash":
92165
92893
  case "BashOutput": {
92166
- const description = str("description");
92167
- if (description)
92168
- return truncate4(firstLine(description), MAX_DESCRIPTION_CHARS);
92894
+ const description2 = str("description");
92895
+ if (description2)
92896
+ return truncate4(firstLine(description2), MAX_DESCRIPTION_CHARS);
92169
92897
  const pre = preambleLabel();
92170
92898
  if (pre)
92171
92899
  return pre;
@@ -92227,9 +92955,9 @@ function toolLabel(tool, input, preamble, precomputedLabel) {
92227
92955
  }
92228
92956
  default:
92229
92957
  if (tool.startsWith("mcp__")) {
92230
- const description = str("description");
92231
- if (description)
92232
- return truncate4(firstLine(stripHtml(description)), MAX_DESCRIPTION_CHARS);
92958
+ const description2 = str("description");
92959
+ if (description2)
92960
+ return truncate4(firstLine(stripHtml(description2)), MAX_DESCRIPTION_CHARS);
92233
92961
  const label = mcpBaseLabel(tool);
92234
92962
  const query3 = str("query") ?? str("text") ?? str("name");
92235
92963
  if (label && query3) {
@@ -92596,12 +93324,12 @@ function skillProposalKeyboard(id) {
92596
93324
 
92597
93325
  // ../src/self-improve/skill-proposals.ts
92598
93326
  import {
92599
- closeSync as closeSync8,
93327
+ closeSync as closeSync9,
92600
93328
  existsSync as existsSync41,
92601
93329
  mkdirSync as mkdirSync37,
92602
- openSync as openSync8,
93330
+ openSync as openSync9,
92603
93331
  readFileSync as readFileSync37,
92604
- writeSync as writeSync5
93332
+ writeSync as writeSync6
92605
93333
  } from "node:fs";
92606
93334
  import { join as join50 } from "node:path";
92607
93335
  import { randomUUID as randomUUID5 } from "node:crypto";
@@ -92621,12 +93349,12 @@ function ensureDir3(stateDir) {
92621
93349
  }
92622
93350
  }
92623
93351
  function appendLine2(path3, obj) {
92624
- const fd = openSync8(path3, "a");
93352
+ const fd = openSync9(path3, "a");
92625
93353
  try {
92626
- writeSync5(fd, JSON.stringify(obj) + `
93354
+ writeSync6(fd, JSON.stringify(obj) + `
92627
93355
  `);
92628
93356
  } finally {
92629
- closeSync8(fd);
93357
+ closeSync9(fd);
92630
93358
  }
92631
93359
  }
92632
93360
  function readLines2(path3, isValid2) {
@@ -93231,11 +93959,11 @@ function escapeBody2(s) {
93231
93959
  }
93232
93960
 
93233
93961
  // gateway/pid-file.ts
93234
- import { writeFileSync as writeFileSync33, readFileSync as readFileSync38, unlinkSync as unlinkSync21, renameSync as renameSync17 } from "node:fs";
93962
+ import { writeFileSync as writeFileSync33, readFileSync as readFileSync38, unlinkSync as unlinkSync21, renameSync as renameSync18 } from "node:fs";
93235
93963
  function writePidFile(path3, record) {
93236
93964
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
93237
93965
  writeFileSync33(tmp, JSON.stringify(record), "utf-8");
93238
- renameSync17(tmp, path3);
93966
+ renameSync18(tmp, path3);
93239
93967
  }
93240
93968
  function clearPidFile(path3) {
93241
93969
  try {
@@ -93456,11 +94184,11 @@ function safeCount(fn) {
93456
94184
  }
93457
94185
 
93458
94186
  // gateway/session-marker.ts
93459
- import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync18, unlinkSync as unlinkSync22 } from "node:fs";
94187
+ import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync19, unlinkSync as unlinkSync22 } from "node:fs";
93460
94188
  function writeSessionMarker(path3, marker) {
93461
94189
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
93462
94190
  writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
93463
- renameSync18(tmp, path3);
94191
+ renameSync19(tmp, path3);
93464
94192
  }
93465
94193
  function readSessionMarker(path3) {
93466
94194
  try {
@@ -93486,12 +94214,12 @@ function shouldFireRestartBanner(input) {
93486
94214
  }
93487
94215
 
93488
94216
  // gateway/clean-shutdown-marker.ts
93489
- import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync19, unlinkSync as unlinkSync23 } from "node:fs";
94217
+ import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync20, unlinkSync as unlinkSync23 } from "node:fs";
93490
94218
  var DEFAULT_MAX_AGE_MS = 60000;
93491
94219
  function writeCleanShutdownMarker(path3, marker) {
93492
94220
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
93493
94221
  writeFileSync35(tmp, JSON.stringify(marker), "utf-8");
93494
- renameSync19(tmp, path3);
94222
+ renameSync20(tmp, path3);
93495
94223
  }
93496
94224
  function readCleanShutdownMarker(path3) {
93497
94225
  try {
@@ -93868,10 +94596,10 @@ function classifyAdminGate(text4, myAgentName) {
93868
94596
  // subagent-watcher.ts
93869
94597
  import {
93870
94598
  existsSync as existsSync42,
93871
- openSync as openSync9,
94599
+ openSync as openSync10,
93872
94600
  readSync as readSync2,
93873
94601
  statSync as statSync15,
93874
- closeSync as closeSync9,
94602
+ closeSync as closeSync10,
93875
94603
  watch,
93876
94604
  readdirSync as readdirSync10,
93877
94605
  readFileSync as readFileSync42
@@ -94684,8 +95412,8 @@ function startSubagentWatcher(config) {
94684
95412
  existsSync: existsSync42,
94685
95413
  readdirSync: readdirSync10,
94686
95414
  statSync: statSync15,
94687
- openSync: openSync9,
94688
- closeSync: closeSync9,
95415
+ openSync: openSync10,
95416
+ closeSync: closeSync10,
94689
95417
  readSync: readSync2,
94690
95418
  watch
94691
95419
  };
@@ -95060,13 +95788,13 @@ function startSubagentWatcher(config) {
95060
95788
  if (size <= rec.sizeAtSynthesis)
95061
95789
  continue;
95062
95790
  const entry = registry.get(agentId);
95063
- const description = entry?.description ?? "sub-agent";
95791
+ const description2 = entry?.description ?? "sub-agent";
95064
95792
  if (rec.resurrectionCount >= MAX_RESURRECTIONS) {
95065
95793
  rec.lost = true;
95066
95794
  log?.(`subagent-watcher: worker ${agentId} FALSELY finalised again after a prior resurrection (resurrectionCount=${rec.resurrectionCount} >= ${MAX_RESURRECTIONS}) \u2014 NAMED AS LOST, not resurrecting again (bounded resurrection chain, issue #3023)`);
95067
95795
  if (config.onWorkerLost != null) {
95068
95796
  try {
95069
- config.onWorkerLost(agentId, description);
95797
+ config.onWorkerLost(agentId, description2);
95070
95798
  } catch (cbErr) {
95071
95799
  log?.(`subagent-watcher: onWorkerLost callback error ${agentId}: ${cbErr.message}`);
95072
95800
  }
@@ -95078,7 +95806,7 @@ function startSubagentWatcher(config) {
95078
95806
  log?.(`subagent-watcher: RESURRECTING worker ${agentId} \u2014 JSONL resumed growing (${rec.sizeAtSynthesis} \u2192 ${size} bytes) after a false terminal synthesis; the worker is still alive, reviving its card (resurrection #${rec.resurrectionCount}, issue #3023)`);
95079
95807
  if (config.onResurrect != null) {
95080
95808
  try {
95081
- config.onResurrect(agentId, description);
95809
+ config.onResurrect(agentId, description2);
95082
95810
  } catch (cbErr) {
95083
95811
  log?.(`subagent-watcher: onResurrect callback error ${agentId}: ${cbErr.message}`);
95084
95812
  }
@@ -95431,7 +96159,7 @@ import {
95431
96159
  readdirSync as readdirSync11,
95432
96160
  unlinkSync as unlinkSync24,
95433
96161
  existsSync as existsSync43,
95434
- renameSync as renameSync20
96162
+ renameSync as renameSync21
95435
96163
  } from "node:fs";
95436
96164
  import { join as join52, resolve as resolve10 } from "node:path";
95437
96165
  import { homedir as homedir15 } from "node:os";
@@ -95450,7 +96178,7 @@ function writeRecord(record) {
95450
96178
  const tmp = `${target}.tmp${process.pid}`;
95451
96179
  writeFileSync36(tmp, JSON.stringify(record, null, 2) + `
95452
96180
  `, { mode: 384 });
95453
- renameSync20(tmp, target);
96181
+ renameSync21(tmp, target);
95454
96182
  }
95455
96183
  function readRecord(id) {
95456
96184
  const path3 = recordPath(id);
@@ -95613,11 +96341,8 @@ function determineRestartReason(opts) {
95613
96341
  return "fresh";
95614
96342
  }
95615
96343
 
95616
- // gateway/gateway.ts
95617
- init_boot_card();
95618
-
95619
96344
  // gateway/update-announce.ts
95620
- import { existsSync as existsSync48, mkdirSync as mkdirSync42, openSync as openSync10, closeSync as closeSync10, readFileSync as readFileSync50 } from "node:fs";
96345
+ import { existsSync as existsSync48, mkdirSync as mkdirSync42, openSync as openSync11, closeSync as closeSync11, readFileSync as readFileSync50 } from "node:fs";
95621
96346
  import { join as join57 } from "node:path";
95622
96347
  import { homedir as homedir17 } from "node:os";
95623
96348
 
@@ -95710,6 +96435,9 @@ function parseAuditLine(line) {
95710
96435
  entry.failed_step = o.failed_step;
95711
96436
  if (typeof o.failed_agent === "string")
95712
96437
  entry.failed_agent = o.failed_agent;
96438
+ if (Array.isArray(o.drifted) && o.drifted.every((x) => typeof x === "string")) {
96439
+ entry.drifted = o.drifted;
96440
+ }
95713
96441
  if (typeof o.agent === "string")
95714
96442
  entry.agent = o.agent;
95715
96443
  if (typeof o.n === "number")
@@ -95826,8 +96554,8 @@ function claimUpdateAnnouncement(requestId, opts = {}) {
95826
96554
  const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
95827
96555
  const path3 = join57(dir, safeId);
95828
96556
  try {
95829
- const fd = openSync10(path3, "wx");
95830
- closeSync10(fd);
96557
+ const fd = openSync11(path3, "wx");
96558
+ closeSync11(fd);
95831
96559
  return true;
95832
96560
  } catch {
95833
96561
  return false;
@@ -96050,20 +96778,20 @@ import { join as join59 } from "node:path";
96050
96778
 
96051
96779
  // ../src/issues/store.ts
96052
96780
  import {
96053
- closeSync as closeSync11,
96781
+ closeSync as closeSync12,
96054
96782
  existsSync as existsSync49,
96055
96783
  mkdirSync as mkdirSync43,
96056
- openSync as openSync11,
96784
+ openSync as openSync12,
96057
96785
  readdirSync as readdirSync13,
96058
96786
  readFileSync as readFileSync52,
96059
- renameSync as renameSync23,
96787
+ renameSync as renameSync24,
96060
96788
  statSync as statSync17,
96061
96789
  unlinkSync as unlinkSync25,
96062
96790
  writeFileSync as writeFileSync42,
96063
- writeSync as writeSync6
96791
+ writeSync as writeSync7
96064
96792
  } from "node:fs";
96065
96793
  import { join as join58 } from "node:path";
96066
- import { randomBytes as randomBytes10 } from "node:crypto";
96794
+ import { randomBytes as randomBytes11 } from "node:crypto";
96067
96795
  import { execSync } from "node:child_process";
96068
96796
 
96069
96797
  // ../src/issues/types.ts
@@ -96138,12 +96866,12 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
96138
96866
  function writeAll(stateDir, events) {
96139
96867
  const path3 = join58(stateDir, ISSUES_FILE);
96140
96868
  sweepOrphanTmpFiles(stateDir);
96141
- const tmp = `${path3}.tmp-${process.pid}-${randomBytes10(4).toString("hex")}`;
96869
+ const tmp = `${path3}.tmp-${process.pid}-${randomBytes11(4).toString("hex")}`;
96142
96870
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
96143
96871
  `) + `
96144
96872
  `;
96145
96873
  writeFileSync42(tmp, body, "utf-8");
96146
- renameSync23(tmp, path3);
96874
+ renameSync24(tmp, path3);
96147
96875
  }
96148
96876
  var ORPHAN_TMP_TTL_MS = 60000;
96149
96877
  var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
@@ -96175,9 +96903,9 @@ function withLock(stateDir, fn) {
96175
96903
  let fd = null;
96176
96904
  while (fd === null) {
96177
96905
  try {
96178
- fd = openSync11(lockPath, "wx");
96906
+ fd = openSync12(lockPath, "wx");
96179
96907
  try {
96180
- writeSync6(fd, String(process.pid));
96908
+ writeSync7(fd, String(process.pid));
96181
96909
  } catch {}
96182
96910
  } catch (err) {
96183
96911
  const e = err;
@@ -96195,7 +96923,7 @@ function withLock(stateDir, fn) {
96195
96923
  return fn();
96196
96924
  } finally {
96197
96925
  try {
96198
- closeSync11(fd);
96926
+ closeSync12(fd);
96199
96927
  } catch {}
96200
96928
  try {
96201
96929
  unlinkSync25(lockPath);
@@ -97087,7 +97815,7 @@ function createScopedGrantStore(stateDir, env = process.env, log = (l) => proces
97087
97815
  preserveUnreadableStoreFile(filePath, "scoped-grant-store", log);
97088
97816
  unreadable = false;
97089
97817
  }
97090
- atomicWriteFileSync(filePath, JSON.stringify(serializeScopedGrants(store3)), 384);
97818
+ atomicWriteFileSync2(filePath, JSON.stringify(serializeScopedGrants(store3)), 384);
97091
97819
  } catch (err) {
97092
97820
  log(`telegram gateway: scoped-grant-store write failed: ${err.message}
97093
97821
  `);
@@ -97875,10 +98603,10 @@ init_auth_snapshot_format2();
97875
98603
 
97876
98604
  // gateway/turn-active-marker.ts
97877
98605
  import {
97878
- closeSync as closeSync12,
98606
+ closeSync as closeSync13,
97879
98607
  existsSync as existsSync53,
97880
98608
  mkdirSync as mkdirSync46,
97881
- openSync as openSync12,
98609
+ openSync as openSync13,
97882
98610
  readFileSync as readFileSync55,
97883
98611
  statSync as statSync19,
97884
98612
  unlinkSync as unlinkSync26,
@@ -97963,6 +98691,163 @@ function startGatewayHeartbeat(stateDir, intervalMs = GATEWAY_HEARTBEAT_INTERVAL
97963
98691
  return timer3;
97964
98692
  }
97965
98693
 
98694
+ // gateway/boot-beacon.ts
98695
+ import { randomUUID as randomUUID6 } from "node:crypto";
98696
+ import {
98697
+ closeSync as closeSync14,
98698
+ fsyncSync as fsyncSync5,
98699
+ mkdirSync as mkdirSync48,
98700
+ openSync as openSync14,
98701
+ readFileSync as readFileSync56,
98702
+ renameSync as renameSync25,
98703
+ unlinkSync as unlinkSync27,
98704
+ writeFileSync as writeFileSync47
98705
+ } from "node:fs";
98706
+ import { join as join65 } from "node:path";
98707
+ var BOOT_BEACON_FILE = "gateway-beacon.json";
98708
+ var HOST_BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id";
98709
+ var CGROUP_MEMORY_EVENTS_PATH = "/sys/fs/cgroup/memory.events";
98710
+ var CGROUP_MEMORY_CURRENT_PATH = "/sys/fs/cgroup/memory.current";
98711
+ var CGROUP_MEMORY_MAX_PATH = "/sys/fs/cgroup/memory.max";
98712
+ var MAX_FIELD_CHARS = 256;
98713
+ function parseHostBootId(raw) {
98714
+ if (typeof raw !== "string")
98715
+ return;
98716
+ const trimmed = raw.trim();
98717
+ if (trimmed.length === 0 || trimmed.length > MAX_FIELD_CHARS)
98718
+ return;
98719
+ if (/\s/.test(trimmed))
98720
+ return;
98721
+ return trimmed;
98722
+ }
98723
+ function parseOomKillCount(raw) {
98724
+ if (typeof raw !== "string")
98725
+ return;
98726
+ for (const line of raw.split(`
98727
+ `)) {
98728
+ const parts = line.trim().split(/\s+/);
98729
+ if (parts.length !== 2 || parts[0] !== "oom_kill")
98730
+ continue;
98731
+ return parseNonNegativeInt2(parts[1]);
98732
+ }
98733
+ return;
98734
+ }
98735
+ function parseMemoryBytes(raw) {
98736
+ if (typeof raw !== "string")
98737
+ return;
98738
+ return parseNonNegativeInt2(raw.trim());
98739
+ }
98740
+ function parseMemoryMax(raw) {
98741
+ if (typeof raw !== "string")
98742
+ return;
98743
+ const trimmed = raw.trim();
98744
+ if (trimmed === "max")
98745
+ return "max";
98746
+ return parseNonNegativeInt2(trimmed);
98747
+ }
98748
+ function parseNonNegativeInt2(text4) {
98749
+ if (typeof text4 !== "string")
98750
+ return;
98751
+ const trimmed = text4.trim();
98752
+ if (trimmed.length === 0 || trimmed.length > MAX_FIELD_CHARS)
98753
+ return;
98754
+ if (!/^\d+$/.test(trimmed))
98755
+ return;
98756
+ const value = Number(trimmed);
98757
+ return Number.isSafeInteger(value) ? value : undefined;
98758
+ }
98759
+ function buildBootBeacon(input) {
98760
+ const beacon = {
98761
+ bootId: input.bootId,
98762
+ pid: input.pid,
98763
+ wallMs: input.wallMs,
98764
+ monotonicMs: input.monotonicMs
98765
+ };
98766
+ const sample3 = input.sample ?? {};
98767
+ if (sample3.hostBootId !== undefined)
98768
+ beacon.hostBootId = sample3.hostBootId;
98769
+ if (sample3.oomKillCount !== undefined)
98770
+ beacon.oomKillCount = sample3.oomKillCount;
98771
+ if (sample3.memCurrent !== undefined)
98772
+ beacon.memCurrent = sample3.memCurrent;
98773
+ if (sample3.memMax !== undefined)
98774
+ beacon.memMax = sample3.memMax;
98775
+ return beacon;
98776
+ }
98777
+ function serializeBootBeacon(beacon) {
98778
+ return `${JSON.stringify(beacon)}
98779
+ `;
98780
+ }
98781
+ function readTextOrNull(path3) {
98782
+ try {
98783
+ return readFileSync56(path3, "utf-8");
98784
+ } catch {
98785
+ return null;
98786
+ }
98787
+ }
98788
+ function sampleBeaconHost(paths = {}) {
98789
+ const sample3 = {};
98790
+ const hostBootId = parseHostBootId(readTextOrNull(paths.hostBootId ?? HOST_BOOT_ID_PATH));
98791
+ if (hostBootId !== undefined)
98792
+ sample3.hostBootId = hostBootId;
98793
+ const oomKillCount = parseOomKillCount(readTextOrNull(paths.memoryEvents ?? CGROUP_MEMORY_EVENTS_PATH));
98794
+ if (oomKillCount !== undefined)
98795
+ sample3.oomKillCount = oomKillCount;
98796
+ const memCurrent = parseMemoryBytes(readTextOrNull(paths.memoryCurrent ?? CGROUP_MEMORY_CURRENT_PATH));
98797
+ if (memCurrent !== undefined)
98798
+ sample3.memCurrent = memCurrent;
98799
+ const memMax = parseMemoryMax(readTextOrNull(paths.memoryMax ?? CGROUP_MEMORY_MAX_PATH));
98800
+ if (memMax !== undefined)
98801
+ sample3.memMax = memMax;
98802
+ return sample3;
98803
+ }
98804
+ function writeBootBeaconFile(stateDir, beacon) {
98805
+ const path3 = join65(stateDir, BOOT_BEACON_FILE);
98806
+ const tmp = `${path3}.tmp-${process.pid}`;
98807
+ let fd;
98808
+ try {
98809
+ mkdirSync48(stateDir, { recursive: true, mode: 448 });
98810
+ fd = openSync14(tmp, "w", 384);
98811
+ writeFileSync47(fd, serializeBootBeacon(beacon));
98812
+ fsyncSync5(fd);
98813
+ closeSync14(fd);
98814
+ fd = undefined;
98815
+ renameSync25(tmp, path3);
98816
+ return true;
98817
+ } catch {
98818
+ if (fd !== undefined) {
98819
+ try {
98820
+ closeSync14(fd);
98821
+ } catch {}
98822
+ }
98823
+ try {
98824
+ unlinkSync27(tmp);
98825
+ } catch {}
98826
+ return false;
98827
+ }
98828
+ }
98829
+ var GATEWAY_BOOT_ID = randomUUID6();
98830
+ function tickBootBeacon(stateDir, bootId = GATEWAY_BOOT_ID) {
98831
+ try {
98832
+ return writeBootBeaconFile(stateDir, buildBootBeacon({
98833
+ bootId,
98834
+ pid: process.pid,
98835
+ wallMs: Date.now(),
98836
+ monotonicMs: Math.round(performance.now()),
98837
+ sample: sampleBeaconHost()
98838
+ }));
98839
+ } catch {
98840
+ return false;
98841
+ }
98842
+ }
98843
+ function attachBootBeacon(stateDir, next) {
98844
+ tickBootBeacon(stateDir);
98845
+ return () => {
98846
+ tickBootBeacon(stateDir);
98847
+ next();
98848
+ };
98849
+ }
98850
+
97966
98851
  // gateway/outbox-sweep.ts
97967
98852
  init_flood_circuit_breaker();
97968
98853
  async function sweepOutbox(deps) {
@@ -98185,10 +99070,10 @@ function startOutboxSweep(deps) {
98185
99070
  }
98186
99071
 
98187
99072
  // ../src/build-info.ts
98188
- var VERSION2 = "0.19.29";
98189
- var COMMIT_SHA = "c618451c";
98190
- var COMMIT_DATE = "2026-07-28T17:39:27Z";
98191
- var LATEST_PR = 3918;
99073
+ var VERSION2 = "0.19.31";
99074
+ var COMMIT_SHA = "d24ee3cc";
99075
+ var COMMIT_DATE = "2026-07-28T23:06:42Z";
99076
+ var LATEST_PR = 3961;
98192
99077
  var COMMITS_AHEAD_OF_TAG = 0;
98193
99078
 
98194
99079
  // gateway/boot-version.ts
@@ -98266,11 +99151,11 @@ init_peercred();
98266
99151
  import * as net5 from "node:net";
98267
99152
  import * as fs3 from "node:fs";
98268
99153
  import { homedir as homedir18 } from "node:os";
98269
- import { join as join65 } from "node:path";
99154
+ import { join as join66 } from "node:path";
98270
99155
  var DEFAULT_TIMEOUT_MS4 = 2000;
98271
99156
  var UNLOCK_TIMEOUT_MS = 30000;
98272
- var LEGACY_SOCKET_PATH2 = join65(homedir18(), ".switchroom", "vault-broker.sock");
98273
- var OPERATOR_SOCKET_PATH2 = join65(homedir18(), ".switchroom", "broker-operator", "sock");
99157
+ var LEGACY_SOCKET_PATH2 = join66(homedir18(), ".switchroom", "vault-broker.sock");
99158
+ var OPERATOR_SOCKET_PATH2 = join66(homedir18(), ".switchroom", "broker-operator", "sock");
98274
99159
  function defaultBrokerSocketPath2() {
98275
99160
  if (fs3.existsSync(OPERATOR_SOCKET_PATH2))
98276
99161
  return OPERATOR_SOCKET_PATH2;
@@ -98871,8 +99756,8 @@ async function createLinearIssue(args, deps = {}) {
98871
99756
  }
98872
99757
  teamId = nodes[0].id;
98873
99758
  }
98874
- const description = dedupKey ? `${body}${captureDedupMarker(dedupKey)}` : body;
98875
- const input = { teamId, title, description };
99759
+ const description2 = dedupKey ? `${body}${captureDedupMarker(dedupKey)}` : body;
99760
+ const input = { teamId, title, description: description2 };
98876
99761
  if (priority !== undefined)
98877
99762
  input.priority = priority;
98878
99763
  const create = await gql("mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url } } }", { input });
@@ -99152,8 +100037,8 @@ function resolveVaultApprovalPosture(broker) {
99152
100037
  }
99153
100038
 
99154
100039
  // registry/turns-schema.ts
99155
- import { chmodSync as chmodSync12, mkdirSync as mkdirSync48 } from "fs";
99156
- import { join as join66 } from "path";
100040
+ import { chmodSync as chmodSync12, mkdirSync as mkdirSync49 } from "fs";
100041
+ import { join as join67 } from "path";
99157
100042
  var DatabaseClass3 = null;
99158
100043
  function loadDatabaseClass3() {
99159
100044
  if (DatabaseClass3 != null)
@@ -99210,7 +100095,7 @@ var PHASE4_MIGRATIONS = [
99210
100095
  ];
99211
100096
  function applySchema(db3) {
99212
100097
  db3.exec("PRAGMA journal_mode = WAL");
99213
- db3.exec("PRAGMA synchronous = NORMAL");
100098
+ db3.exec("PRAGMA synchronous = FULL");
99214
100099
  db3.exec("PRAGMA busy_timeout = 5000");
99215
100100
  db3.exec(SCHEMA_SQL);
99216
100101
  for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS, ...PHASE4_MIGRATIONS]) {
@@ -99225,9 +100110,9 @@ function applySchema(db3) {
99225
100110
  }
99226
100111
  function openTurnsDb(agentDir) {
99227
100112
  const Database = loadDatabaseClass3();
99228
- const dir = join66(agentDir, "telegram");
99229
- mkdirSync48(dir, { recursive: true, mode: 448 });
99230
- const path3 = join66(dir, "registry.db");
100113
+ const dir = join67(agentDir, "telegram");
100114
+ mkdirSync49(dir, { recursive: true, mode: 448 });
100115
+ const path3 = join67(dir, "registry.db");
99231
100116
  const db3 = new Database(path3, { create: true });
99232
100117
  applySchema(db3);
99233
100118
  try {
@@ -99572,7 +100457,7 @@ function selectResumeBuilder(endedVia, opts) {
99572
100457
  }
99573
100458
 
99574
100459
  // gateway/bridge-dead-watchdog.ts
99575
- import { readFileSync as readFileSync57, writeFileSync as writeFileSync47, renameSync as renameSync24, unlinkSync as unlinkSync27 } from "node:fs";
100460
+ import { readFileSync as readFileSync58, writeFileSync as writeFileSync48, renameSync as renameSync26, unlinkSync as unlinkSync28 } from "node:fs";
99576
100461
 
99577
100462
  // gateway/cron-session.ts
99578
100463
  var CRON_IDENTITY_SUFFIX2 = "-cron";
@@ -99605,7 +100490,7 @@ function readFreshCrashLogTail(path3, opts = {}) {
99605
100490
  const nowMs3 = opts.nowMs ?? Date.now();
99606
100491
  const freshWindowMs = opts.freshWindowMs ?? CRASH_LOG_FRESH_WINDOW_MS;
99607
100492
  const maxLines = opts.maxLines ?? CRASH_LOG_TAIL_LINES;
99608
- const readFile = opts.readFile ?? ((p) => readFileSync57(p, "utf8"));
100493
+ const readFile = opts.readFile ?? ((p) => readFileSync58(p, "utf8"));
99609
100494
  let raw;
99610
100495
  try {
99611
100496
  raw = readFile(path3);
@@ -99626,13 +100511,13 @@ function readFreshCrashLogTail(path3, opts = {}) {
99626
100511
  }
99627
100512
  function writeBridgeDeadEscalationMarker(path3, marker) {
99628
100513
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
99629
- writeFileSync47(tmp, JSON.stringify(marker), "utf8");
99630
- renameSync24(tmp, path3);
100514
+ writeFileSync48(tmp, JSON.stringify(marker), "utf8");
100515
+ renameSync26(tmp, path3);
99631
100516
  }
99632
100517
  function consumeBridgeDeadEscalationMarker(path3, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
99633
100518
  let marker = null;
99634
100519
  try {
99635
- const parsed = JSON.parse(readFileSync57(path3, "utf8"));
100520
+ const parsed = JSON.parse(readFileSync58(path3, "utf8"));
99636
100521
  if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.reason === "string") {
99637
100522
  const age = nowMs3 - parsed.ts;
99638
100523
  if (age >= 0 && age < maxAgeMs) {
@@ -99644,7 +100529,7 @@ function consumeBridgeDeadEscalationMarker(path3, nowMs3 = Date.now(), maxAgeMs
99644
100529
  }
99645
100530
  } catch {}
99646
100531
  try {
99647
- unlinkSync27(path3);
100532
+ unlinkSync28(path3);
99648
100533
  } catch {}
99649
100534
  return marker;
99650
100535
  }
@@ -99785,7 +100670,7 @@ function createBridgeDeadWatchdog(opts) {
99785
100670
  }
99786
100671
 
99787
100672
  // gateway/boot-probes.ts
99788
- import { readFileSync as readFileSync58, readdirSync as readdirSync14, existsSync as existsSync55 } from "fs";
100673
+ import { readFileSync as readFileSync59, readdirSync as readdirSync14, existsSync as existsSync55 } from "fs";
99789
100674
  init_quota_cache();
99790
100675
  init_generation_stamp();
99791
100676
  init_quota_check();
@@ -99794,7 +100679,7 @@ import { promisify as promisify2 } from "util";
99794
100679
  var execFile2 = promisify2(execFileCb2);
99795
100680
  var realProcFs2 = {
99796
100681
  readdir: (p) => readdirSync14(p),
99797
- readFile: (p) => readFileSync58(p, "utf-8")
100682
+ readFile: (p) => readFileSync59(p, "utf-8")
99798
100683
  };
99799
100684
  function findAgentProcessInContainer2(fs4 = realProcFs2) {
99800
100685
  let entries;
@@ -100044,7 +100929,7 @@ if (isGatewayMain) {
100044
100929
  shutdownAnalytics();
100045
100930
  });
100046
100931
  }
100047
- var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join67(homedir19(), ".claude", "channels", "telegram");
100932
+ var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join68(homedir19(), ".claude", "channels", "telegram");
100048
100933
  var permCardStore = createPermissionCardStore(STATE_DIR);
100049
100934
  var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
100050
100935
  var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
@@ -100083,7 +100968,7 @@ function alwaysAllowDrainDeps() {
100083
100968
  return {
100084
100969
  readConfigText: () => {
100085
100970
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
100086
- return readFileSync59(cfgPath, "utf8");
100971
+ return readFileSync60(cfgPath, "utf8");
100087
100972
  },
100088
100973
  resolveAllowList: (_configText, agentName3) => {
100089
100974
  const cfg = loadConfig2();
@@ -100144,11 +101029,11 @@ function scheduleAlwaysAllowPersistDrain() {
100144
101029
  }, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
100145
101030
  timer3.unref?.();
100146
101031
  }
100147
- var ACCESS_FILE = join67(STATE_DIR, "access.json");
100148
- var APPROVED_DIR = join67(STATE_DIR, "approved");
100149
- var ENV_FILE = join67(STATE_DIR, ".env");
100150
- var INBOX_DIR = join67(STATE_DIR, "inbox");
100151
- var PEOPLE_FILE = join67(STATE_DIR, "people.json");
101032
+ var ACCESS_FILE = join68(STATE_DIR, "access.json");
101033
+ var APPROVED_DIR = join68(STATE_DIR, "approved");
101034
+ var ENV_FILE = join68(STATE_DIR, ".env");
101035
+ var INBOX_DIR = join68(STATE_DIR, "inbox");
101036
+ var PEOPLE_FILE = join68(STATE_DIR, "people.json");
100152
101037
  function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
100153
101038
  const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
100154
101039
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
@@ -100213,7 +101098,7 @@ function formatBootVersion() {
100213
101098
  }
100214
101099
  try {
100215
101100
  chmodSync14(ENV_FILE, 384);
100216
- for (const line of readFileSync59(ENV_FILE, "utf8").split(`
101101
+ for (const line of readFileSync60(ENV_FILE, "utf8").split(`
100217
101102
  `)) {
100218
101103
  const m = line.match(/^(\w+)=(.*)$/);
100219
101104
  if (m && process.env[m[1]] === undefined)
@@ -100234,7 +101119,7 @@ var bot;
100234
101119
  var lastGetUpdatesHeartbeatMs = Date.now();
100235
101120
  var GRAMMY_VERSION = (() => {
100236
101121
  try {
100237
- const raw = readFileSync59(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
101122
+ const raw = readFileSync60(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
100238
101123
  return JSON.parse(raw).version ?? "unknown";
100239
101124
  } catch {
100240
101125
  return "unknown";
@@ -100297,7 +101182,7 @@ function assertSendable(f) {
100297
101182
  } catch {
100298
101183
  throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
100299
101184
  }
100300
- const inbox = join67(stateReal, "inbox");
101185
+ const inbox = join68(stateReal, "inbox");
100301
101186
  if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
100302
101187
  throw new Error(`refusing to send channel state: ${f}`);
100303
101188
  }
@@ -100316,7 +101201,7 @@ function assertSendable(f) {
100316
101201
  }
100317
101202
  function readAccessFile() {
100318
101203
  try {
100319
- const raw = readFileSync59(ACCESS_FILE, "utf8");
101204
+ const raw = readFileSync60(ACCESS_FILE, "utf8");
100320
101205
  const parsed = JSON.parse(raw);
100321
101206
  const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
100322
101207
  const groups = {};
@@ -100356,7 +101241,7 @@ function readAccessFile() {
100356
101241
  if (err.code === "ENOENT")
100357
101242
  return defaultAccess();
100358
101243
  try {
100359
- renameSync25(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
101244
+ renameSync27(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
100360
101245
  } catch {}
100361
101246
  process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
100362
101247
  `);
@@ -100378,7 +101263,7 @@ function loadAccess() {
100378
101263
  }
100379
101264
  function readPeopleFile() {
100380
101265
  try {
100381
- const raw = readFileSync59(PEOPLE_FILE, "utf8");
101266
+ const raw = readFileSync60(PEOPLE_FILE, "utf8");
100382
101267
  const parsed = JSON.parse(raw);
100383
101268
  if (!Array.isArray(parsed.entries))
100384
101269
  return [];
@@ -100400,11 +101285,11 @@ function assertAllowedChat(chat_id) {
100400
101285
  function saveAccess(a) {
100401
101286
  if (STATIC)
100402
101287
  return;
100403
- mkdirSync50(STATE_DIR, { recursive: true, mode: 448 });
101288
+ mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
100404
101289
  const tmp = ACCESS_FILE + ".tmp";
100405
- writeFileSync49(tmp, JSON.stringify(a, null, 2) + `
101290
+ writeFileSync50(tmp, JSON.stringify(a, null, 2) + `
100406
101291
  `, { mode: 384 });
100407
- renameSync25(tmp, ACCESS_FILE);
101292
+ renameSync27(tmp, ACCESS_FILE);
100408
101293
  }
100409
101294
  function pruneExpired(a) {
100410
101295
  const now = Date.now();
@@ -100422,7 +101307,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
100422
101307
  if (isGatewayMain && HISTORY_ENABLED) {
100423
101308
  try {
100424
101309
  initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
100425
- process.stderr.write(`telegram gateway: history capture enabled at ${join67(STATE_DIR, "history.db")}
101310
+ process.stderr.write(`telegram gateway: history capture enabled at ${join68(STATE_DIR, "history.db")}
100426
101311
  `);
100427
101312
  } catch (err) {
100428
101313
  process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
@@ -100441,12 +101326,12 @@ if (isGatewayMain)
100441
101326
  let markerTurnKey = null;
100442
101327
  let markerAgeMs = null;
100443
101328
  try {
100444
- const markerPath = join67(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
101329
+ const markerPath = join68(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
100445
101330
  if (existsSync57(markerPath)) {
100446
101331
  const st = statSync22(markerPath);
100447
101332
  markerAgeMs = Date.now() - st.mtimeMs;
100448
101333
  try {
100449
- const payload = JSON.parse(readFileSync59(markerPath, "utf8"));
101334
+ const payload = JSON.parse(readFileSync60(markerPath, "utf8"));
100450
101335
  if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
100451
101336
  markerTurnKey = payload.turnKey;
100452
101337
  }
@@ -100466,10 +101351,10 @@ if (isGatewayMain)
100466
101351
  process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
100467
101352
  `);
100468
101353
  } else {
100469
- process.stderr.write(`telegram gateway: turn-registry initialized at ${join67(agentDir, "telegram", "registry.db")}
101354
+ process.stderr.write(`telegram gateway: turn-registry initialized at ${join68(agentDir, "telegram", "registry.db")}
100470
101355
  `);
100471
101356
  }
100472
- const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join67(STATE_DIR, "bridge-dead-escalation.json"));
101357
+ const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join68(STATE_DIR, "bridge-dead-escalation.json"));
100473
101358
  if (bridgeDeadMarker != null) {
100474
101359
  bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
100475
101360
  process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
@@ -100482,7 +101367,7 @@ if (isGatewayMain)
100482
101367
  const pending2 = findLatestTurnIfInterrupted(turnsDb);
100483
101368
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
100484
101369
  if (pending2 != null && selfAgent) {
100485
- const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join67(STATE_DIR, "clean-shutdown.json");
101370
+ const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join68(STATE_DIR, "clean-shutdown.json");
100486
101371
  const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
100487
101372
  const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
100488
101373
  const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
@@ -100595,7 +101480,7 @@ if (isGatewayMain)
100595
101480
  `);
100596
101481
  }
100597
101482
  }
100598
- const pendingEnvPath = join67(agentDir, ".pending-turn.env");
101483
+ const pendingEnvPath = join68(agentDir, ".pending-turn.env");
100599
101484
  try {
100600
101485
  if (pending2 != null) {
100601
101486
  const lines = [
@@ -100609,14 +101494,14 @@ if (isGatewayMain)
100609
101494
  pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
100610
101495
  ];
100611
101496
  const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
100612
- writeFileSync49(pendingEnvTmp, lines.join(`
101497
+ writeFileSync50(pendingEnvTmp, lines.join(`
100613
101498
  `) + `
100614
101499
  `, { mode: 384 });
100615
- renameSync25(pendingEnvTmp, pendingEnvPath);
101500
+ renameSync27(pendingEnvTmp, pendingEnvPath);
100616
101501
  process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
100617
101502
  `);
100618
101503
  } else if (existsSync57(pendingEnvPath)) {
100619
- rmSync7(pendingEnvPath, { force: true });
101504
+ rmSync8(pendingEnvPath, { force: true });
100620
101505
  process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
100621
101506
  `);
100622
101507
  }
@@ -100723,16 +101608,16 @@ function checkApprovals() {
100723
101608
  return;
100724
101609
  }
100725
101610
  for (const senderId of files) {
100726
- const file = join67(APPROVED_DIR, senderId);
100727
- bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync7(file, { force: true }), (err) => {
101611
+ const file = join68(APPROVED_DIR, senderId);
101612
+ bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync8(file, { force: true }), (err) => {
100728
101613
  process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
100729
101614
  `);
100730
- rmSync7(file, { force: true });
101615
+ rmSync8(file, { force: true });
100731
101616
  });
100732
101617
  }
100733
101618
  }
100734
101619
  if (isGatewayMain && !STATIC)
100735
- setInterval(checkApprovals, 5000).unref();
101620
+ setInterval(attachBootBeacon(STATE_DIR, checkApprovals), 5000).unref();
100736
101621
  if (isGatewayMain && !STATIC)
100737
101622
  startGatewayHeartbeat(STATE_DIR);
100738
101623
  var IDLE_CLEAR_CHECK_MS = Number(process.env.SWITCHROOM_IDLE_CLEAR_CHECK_MS ?? 60000);
@@ -100897,12 +101782,14 @@ function noteAgentOutputAt(key, ts) {
100897
101782
  lastAgentOutputAt.delete(oldest);
100898
101783
  }
100899
101784
  }
100900
- var OBLIGATION_STORE_PATH = join67(STATE_DIR, "obligations.json");
101785
+ var OBLIGATION_STORE_PATH = join68(STATE_DIR, "obligations.json");
100901
101786
  var obligationStoreFs = {
100902
- readFileSync: (p) => readFileSync59(p, "utf8"),
100903
- writeFileSync: (p, d) => writeFileSync49(p, d),
100904
- renameSync: (a, b) => renameSync25(a, b),
100905
- existsSync: (p) => existsSync57(p)
101787
+ readFileSync: (p) => readFileSync60(p, "utf8"),
101788
+ writeFileSync: (p, d) => writeFileSync50(p, d),
101789
+ renameSync: (a, b) => renameSync27(a, b),
101790
+ existsSync: (p) => existsSync57(p),
101791
+ fsyncFileSync: fsyncPathSync,
101792
+ fsyncDirSync: fsyncPathSync
100906
101793
  };
100907
101794
  var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
100908
101795
  onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
@@ -101677,7 +102564,7 @@ function emitTurnRecord(turn, endedAt) {
101677
102564
  return;
101678
102565
  }
101679
102566
  },
101680
- rename: (from, to) => renameSync25(from, to)
102567
+ rename: (from, to) => renameSync27(from, to)
101681
102568
  });
101682
102569
  appendFileSync9(turnsPath, rec);
101683
102570
  } catch {}
@@ -102225,7 +103112,7 @@ function maybePostMissedApprovalDigest(reason) {
102225
103112
  const first = entries[0];
102226
103113
  const chatId = first.chatId;
102227
103114
  const threadId = first.threadId ?? undefined;
102228
- const digestId = randomBytes12(4).toString("hex");
103115
+ const digestId = randomBytes13(4).toString("hex");
102229
103116
  const text5 = renderMissedApprovalsDigest(entries, {
102230
103117
  agentName: process.env.SWITCHROOM_AGENT_NAME ?? null
102231
103118
  });
@@ -103240,27 +104127,27 @@ var PIN_STATUS_WHILE_WORKING = (() => {
103240
104127
  })();
103241
104128
  var statusPinClaims = new Map;
103242
104129
  var statusPinRightsCache = new PinRightsCache2;
103243
- var STATUS_PIN_STORE_PATH = join67(STATE_DIR, "status-pins.json");
104130
+ var STATUS_PIN_STORE_PATH = join68(STATE_DIR, "status-pins.json");
103244
104131
  var statusPinStoreFs = {
103245
- readFileSync: (p) => readFileSync59(p, "utf8"),
103246
- writeFileSync: (p, d) => writeFileSync49(p, d),
103247
- renameSync: (a, b) => renameSync25(a, b),
104132
+ readFileSync: (p) => readFileSync60(p, "utf8"),
104133
+ writeFileSync: (p, d) => writeFileSync50(p, d),
104134
+ renameSync: (a, b) => renameSync27(a, b),
103248
104135
  existsSync: (p) => existsSync57(p)
103249
104136
  };
103250
104137
  var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
103251
- var ACTIVITY_CARD_STORE_PATH = join67(STATE_DIR, "activity-cards-pending.json");
104138
+ var ACTIVITY_CARD_STORE_PATH = join68(STATE_DIR, "activity-cards-pending.json");
103252
104139
  var activityCardStoreFs = {
103253
- readFileSync: (p) => readFileSync59(p, "utf8"),
103254
- writeFileSync: (p, d) => writeFileSync49(p, d),
103255
- renameSync: (a, b) => renameSync25(a, b),
104140
+ readFileSync: (p) => readFileSync60(p, "utf8"),
104141
+ writeFileSync: (p, d) => writeFileSync50(p, d),
104142
+ renameSync: (a, b) => renameSync27(a, b),
103256
104143
  existsSync: (p) => existsSync57(p)
103257
104144
  };
103258
104145
  var activityCardPersistEnabled = !STATIC;
103259
- var QUEUED_CARD_STORE_PATH = join67(STATE_DIR, "queued-cards-pending.json");
104146
+ var QUEUED_CARD_STORE_PATH = join68(STATE_DIR, "queued-cards-pending.json");
103260
104147
  var queuedCardStoreFs = {
103261
- readFileSync: (p) => readFileSync59(p, "utf8"),
103262
- writeFileSync: (p, d) => writeFileSync49(p, d),
103263
- renameSync: (a, b) => renameSync25(a, b),
104148
+ readFileSync: (p) => readFileSync60(p, "utf8"),
104149
+ writeFileSync: (p, d) => writeFileSync50(p, d),
104150
+ renameSync: (a, b) => renameSync27(a, b),
103264
104151
  existsSync: (p) => existsSync57(p)
103265
104152
  };
103266
104153
  var queuedCardPersistEnabled = !STATIC;
@@ -103499,16 +104386,16 @@ var midSessionCardReaper = isGatewayMain ? setInterval(() => {
103499
104386
  midSessionReaperGuard.tick();
103500
104387
  }, MID_SESSION_CARD_REAPER_INTERVAL_MS) : undefined;
103501
104388
  midSessionCardReaper?.unref();
103502
- async function reconcileStatusPin(pinKey, chatId, desired) {
104389
+ async function reconcileStatusPin(pinKey, chatId, desired, threadId) {
103503
104390
  try {
103504
- await withPinReconcileLock(pinKey, () => reconcileStatusPinInner(pinKey, chatId, desired));
104391
+ await withPinReconcileLock(pinKey, () => reconcileStatusPinInner(pinKey, chatId, desired, threadId));
103505
104392
  } catch (err) {
103506
104393
  const msg = err instanceof Error ? err.message : String(err);
103507
104394
  process.stderr.write(`telegram gateway: status-pin reconcile absorbed error (key=${pinKey} chat=${chatId}): ${msg}
103508
104395
  `);
103509
104396
  }
103510
104397
  }
103511
- async function reconcileStatusPinInner(pinKey, chatId, desired) {
104398
+ async function reconcileStatusPinInner(pinKey, chatId, desired, threadId) {
103512
104399
  if (!PIN_STATUS_WHILE_WORKING)
103513
104400
  return;
103514
104401
  if (chatId.length === 0)
@@ -103534,6 +104421,7 @@ async function reconcileStatusPinInner(pinKey, chatId, desired) {
103534
104421
  await runStatusPinReconcile({
103535
104422
  pinKey,
103536
104423
  chatId,
104424
+ threadId,
103537
104425
  prev,
103538
104426
  desired,
103539
104427
  persist: statusPinPersistEnabled ? { path: STATUS_PIN_STORE_PATH, fs: statusPinStoreFs } : null,
@@ -103546,37 +104434,26 @@ async function unpinAllStatusPins() {
103546
104434
  await reconcileStatusPin(key, claim.chatId, { pinned: false });
103547
104435
  }
103548
104436
  }
103549
- var dmPinSweepEligible = false;
103550
- var dmPinSweeper = createDmPinSweeper({
103551
- unpinAll: (chatId) => robustApiCall(() => lockedBot.api.unpinAllChatMessages(chatId), {
103552
- chat_id: chatId,
103553
- verb: "dm-pin-sweep.unpin-all"
103554
- }),
103555
- pinSilent: (chatId, messageId) => robustApiCall(() => lockedBot.api.pinChatMessage(chatId, messageId, {
103556
- disable_notification: true
103557
- }), { chat_id: chatId, verb: "dm-pin-sweep.repin" }),
103558
- liveTrackedMessageIds: (chatId) => {
103559
- const ids = [];
103560
- for (const claim of statusPinClaims.values()) {
103561
- if (claim.chatId === chatId)
103562
- ids.push(claim.messageId);
103563
- }
103564
- if (statusPinPersistEnabled || bannerPinPersistEnabled || toolPinPersistEnabled) {
103565
- try {
103566
- ids.push(...unexpiredStoreRepinIds(loadStatusPins2(STATUS_PIN_STORE_PATH, statusPinStoreFs), chatId, Date.now()));
103567
- } catch (err) {
103568
- process.stderr.write(`telegram gateway: dm-pin-sweep: store repin scan failed (chat=${chatId}): ${err.message}
103569
- `);
103570
- }
104437
+ var stalePinSweepEligible = false;
104438
+ var STALE_PIN_SWEEP_STORE_PATH = join68(STATE_DIR, "stale-pin-sweep.json");
104439
+ var stalePinSweeper = createGatewayStalePinSweeper({
104440
+ telegram: { handle: () => lockedBot, call: robustApiCall },
104441
+ claims: () => statusPinClaims.values(),
104442
+ loadPinRows: () => statusPinPersistEnabled || bannerPinPersistEnabled || toolPinPersistEnabled ? loadStatusPins2(STATUS_PIN_STORE_PATH, statusPinStoreFs) : [],
104443
+ eligible: () => stalePinSweepEligible,
104444
+ store: {
104445
+ path: STALE_PIN_SWEEP_STORE_PATH,
104446
+ fs: {
104447
+ readFileSync: (p) => readFileSync60(p, "utf-8"),
104448
+ writeFileSync: (p, data) => atomicWriteFileSync(p, data, 384),
104449
+ existsSync: (p) => existsSync57(p)
103571
104450
  }
103572
- return ids;
103573
104451
  },
103574
- eligible: () => dmPinSweepEligible,
103575
- log: (line) => process.stderr.write(line)
104452
+ allowUnpinAllForumTopic: process.env.SWITCHROOM_PIN_SWEEP_UNPIN_ALL_TOPIC == null ? undefined : process.env.SWITCHROOM_PIN_SWEEP_UNPIN_ALL_TOPIC === "1"
103576
104453
  });
103577
- function runBootPinCleanupAndDmSweep() {
104454
+ function runBootPinCleanupAndStalePinSweep() {
103578
104455
  return runBootPinSweepSteps({
103579
- scanDmChatIds: () => collectDmChatIdsFromStores({
104456
+ scanSweepTargets: () => collectSweepTargets({
103580
104457
  statusPins: statusPinPersistEnabled || bannerPinPersistEnabled || toolPinPersistEnabled ? loadStatusPins2(STATUS_PIN_STORE_PATH, statusPinStoreFs) : [],
103581
104458
  activityCards: activityCardPersistEnabled ? loadActivityCards2(ACTIVITY_CARD_STORE_PATH, activityCardStoreFs) : [],
103582
104459
  queuedCards: queuedCardPersistEnabled ? loadQueuedCards(QUEUED_CARD_STORE_PATH, queuedCardStoreFs) : []
@@ -103584,14 +104461,14 @@ function runBootPinCleanupAndDmSweep() {
103584
104461
  statusPinCleanup: statusPinBootCleanup,
103585
104462
  activityCardReaper: activityCardBootReaper,
103586
104463
  queuedCardReaper: queuedCardBootReaper,
103587
- enableDmSweep: () => {
103588
- dmPinSweepEligible = true;
104464
+ enableSweep: () => {
104465
+ stalePinSweepEligible = true;
103589
104466
  },
103590
- sweepDm: (id) => dmPinSweeper.sweep(id),
104467
+ sweepTarget: (t) => stalePinSweeper.sweepTarget(t),
103591
104468
  log: (line) => process.stderr.write(line)
103592
104469
  });
103593
104470
  }
103594
- var bootPinSweepGate = createBootSweepGate({ run: runBootPinCleanupAndDmSweep, onError: (err) => process.stderr.write(`telegram gateway: boot pin cleanup / DM sweep failed: ${err.message}
104471
+ var bootPinSweepGate = createBootSweepGate({ run: runBootPinCleanupAndStalePinSweep, onError: (err) => process.stderr.write(`telegram gateway: boot pin cleanup / stale-pin sweep failed: ${err.message}
103595
104472
  `) });
103596
104473
  var progressDriver = null;
103597
104474
  var unpinProgressCardForChat = null;
@@ -103599,12 +104476,12 @@ var getPinnedProgressCardMessageId = null;
103599
104476
  var completeProgressCardTurn = null;
103600
104477
  var subagentWatcher = null;
103601
104478
  var workerActivityFeed = null;
103602
- var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join67(STATE_DIR, "gateway.sock");
104479
+ var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join68(STATE_DIR, "gateway.sock");
103603
104480
  if (isGatewayMain)
103604
- mkdirSync50(STATE_DIR, { recursive: true, mode: 448 });
103605
- var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join67(STATE_DIR, "gateway.pid.json");
103606
- var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join67(STATE_DIR, "gateway-session.json");
103607
- var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join67(STATE_DIR, "clean-shutdown.json");
104481
+ mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
104482
+ var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join68(STATE_DIR, "gateway.pid.json");
104483
+ var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join68(STATE_DIR, "gateway-session.json");
104484
+ var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join68(STATE_DIR, "clean-shutdown.json");
103608
104485
  var GATEWAY_STARTED_AT_MS = Date.now();
103609
104486
  var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
103610
104487
  var activeBootCard = null;
@@ -103633,7 +104510,7 @@ function ensureIssuesCard(chatId, threadId) {
103633
104510
  bot: botApi,
103634
104511
  log: (msg) => process.stderr.write(`telegram gateway: ${msg}
103635
104512
  `),
103636
- persistPath: join67(stateDir, "issues-card.json")
104513
+ persistPath: join68(stateDir, "issues-card.json")
103637
104514
  });
103638
104515
  activeIssuesWatcher = startIssuesWatcher({
103639
104516
  stateDir,
@@ -103833,14 +104710,16 @@ if (isGatewayMain)
103833
104710
  var inboundSpool;
103834
104711
  if (isGatewayMain)
103835
104712
  inboundSpool = STATIC ? undefined : createInboundSpool({
103836
- path: join67(STATE_DIR, "inbound-spool.jsonl"),
104713
+ path: join68(STATE_DIR, "inbound-spool.jsonl"),
103837
104714
  fs: {
103838
104715
  appendFileSync: (p, d) => appendFileSync9(p, d),
103839
- readFileSync: (p) => readFileSync59(p, "utf8"),
103840
- writeFileSync: (p, d) => writeFileSync49(p, d),
103841
- renameSync: (a, b) => renameSync25(a, b),
104716
+ readFileSync: (p) => readFileSync60(p, "utf8"),
104717
+ writeFileSync: (p, d) => writeFileSync50(p, d),
104718
+ renameSync: (a, b) => renameSync27(a, b),
103842
104719
  existsSync: (p) => existsSync57(p),
103843
- statSizeSync: (p) => statSync22(p).size
104720
+ statSizeSync: (p) => statSync22(p).size,
104721
+ fsyncFileSync: fsyncPathSync,
104722
+ fsyncDirSync: fsyncPathSync
103844
104723
  },
103845
104724
  onDegraded: (info) => {
103846
104725
  process.stderr.write(`telegram gateway: inbound-spool durability ${info.degraded ? "DEGRADED to in-memory-only" : "RECOVERED"} path=${info.path} consecutiveFailures=${info.consecutiveFailures}${info.error != null ? ` error=${info.error}` : ""}
@@ -103901,13 +104780,13 @@ async function maybeRedeliverUndeliveredAnswer() {
103901
104780
  let transcriptText;
103902
104781
  try {
103903
104782
  const projectsDir = getProjectsDirForCwd();
103904
- const path3 = join67(projectsDir, `${sessionId}.jsonl`);
104783
+ const path3 = join68(projectsDir, `${sessionId}.jsonl`);
103905
104784
  if (!existsSync57(path3)) {
103906
104785
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path3}); skipping
103907
104786
  `);
103908
104787
  return;
103909
104788
  }
103910
- transcriptText = readFileSync59(path3, "utf8");
104789
+ transcriptText = readFileSync60(path3, "utf8");
103911
104790
  } catch (err) {
103912
104791
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
103913
104792
  `);
@@ -104024,13 +104903,13 @@ function dispatchPermissionVerdict(ev) {
104024
104903
  }
104025
104904
  }
104026
104905
  function rearmPermissionFromStore(msg, persisted) {
104027
- const { requestId, toolName, description, inputPreview } = msg;
104906
+ const { requestId, toolName, description: description2, inputPreview } = msg;
104028
104907
  const startedAt = persisted.reduce((min, c) => c.startedAt < min ? c.startedAt : min, persisted[0].startedAt);
104029
104908
  const cardText = persisted[0].cardText;
104030
104909
  const cards = persisted.map((c) => ({ chatId: c.chatId, messageId: c.messageId }));
104031
104910
  pendingPermissions.set(requestId, {
104032
104911
  tool_name: toolName,
104033
- description,
104912
+ description: description2,
104034
104913
  input_preview: inputPreview,
104035
104914
  startedAt,
104036
104915
  card_text: cardText,
@@ -104066,8 +104945,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
104066
104945
  isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
104067
104946
  isShuttingDown: () => shuttingDown,
104068
104947
  escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
104069
- crashLogPath: join67(STATE_DIR, "bridge-crash.log"),
104070
- markerPath: join67(STATE_DIR, "bridge-dead-escalation.json"),
104948
+ crashLogPath: join68(STATE_DIR, "bridge-crash.log"),
104949
+ markerPath: join68(STATE_DIR, "bridge-dead-escalation.json"),
104071
104950
  log: (line) => process.stderr.write(`${line}
104072
104951
  `),
104073
104952
  priorStreak: bridgeDeadPriorStreak,
@@ -104173,8 +105052,8 @@ if (isGatewayMain)
104173
105052
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
104174
105053
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
104175
105054
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
104176
- configSnapshotPath: join67(resolvedAgentDirForCard, ".config-snapshot.json"),
104177
- bootCardStatePath: join67(resolvedAgentDirForCard, ".boot-card-msgid.json"),
105055
+ configSnapshotPath: join68(resolvedAgentDirForCard, ".config-snapshot.json"),
105056
+ bootCardStatePath: join68(resolvedAgentDirForCard, ".boot-card-msgid.json"),
104178
105057
  floodStatePath: FLOOD_STATE_PATH,
104179
105058
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
104180
105059
  }, ackMsgId).then((handle) => {
@@ -104289,7 +105168,7 @@ if (isGatewayMain)
104289
105168
  }
104290
105169
  },
104291
105170
  onPermissionRequest(_client, msg) {
104292
- const { requestId, toolName, description, inputPreview } = msg;
105171
+ const { requestId, toolName, description: description2, inputPreview } = msg;
104293
105172
  if (isPermissionRearmEnabled()) {
104294
105173
  const persisted = pendingPermissions.has(requestId) ? [] : permCardStore.loadAll().filter((e) => e.requestId === requestId);
104295
105174
  const disposition = classifyPermissionRequest({
@@ -104333,10 +105212,10 @@ if (isGatewayMain)
104333
105212
  const text5 = formatPermissionCardBody({
104334
105213
  toolName,
104335
105214
  inputPreview,
104336
- description,
105215
+ description: description2,
104337
105216
  agentName: _client.agentName
104338
105217
  });
104339
- const pendEntry = { tool_name: toolName, description, input_preview: inputPreview, startedAt: Date.now(), card_text: text5, cards: [] };
105218
+ const pendEntry = { tool_name: toolName, description: description2, input_preview: inputPreview, startedAt: Date.now(), card_text: text5, cards: [] };
104340
105219
  pendingPermissions.set(requestId, pendEntry);
104341
105220
  const activeTurn = currentTurn;
104342
105221
  postPermissionCard(requestId, pendEntry);
@@ -104842,7 +105721,7 @@ if (isGatewayMain)
104842
105721
  const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
104843
105722
  if (Number.isInteger(receiverUid))
104844
105723
  allowedUids.push(receiverUid);
104845
- const socketPath = join67(STATE_DIR, "webhook.sock");
105724
+ const socketPath = join68(STATE_DIR, "webhook.sock");
104846
105725
  const webhookInject = (agentName3, inbound) => {
104847
105726
  const msg = inbound;
104848
105727
  const delivered = ipcServer.sendToAgent(agentName3, msg);
@@ -105070,9 +105949,9 @@ function redactOutboundText(text5, site) {
105070
105949
  var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
105071
105950
  var VOICE_OUT_HARD_CHUNK_CAP = 4096;
105072
105951
  var voiceOnDemandCache = new VoiceOnDemandCache({
105073
- persistPath: join67(STATE_DIR, "voice-ondemand.json")
105952
+ persistPath: join68(STATE_DIR, "voice-ondemand.json")
105074
105953
  });
105075
- var VOICE_CACHE_DIR = join67(STATE_DIR, "voice-cache");
105954
+ var VOICE_CACHE_DIR = join68(STATE_DIR, "voice-cache");
105076
105955
  var voicePreSynthQueue = new PreSynthQueue({
105077
105956
  runJob: async (job) => {
105078
105957
  const sidecarToken = await materializeSidecarToken2();
@@ -105474,11 +106353,11 @@ async function executeSendGif(rawArgs) {
105474
106353
  };
105475
106354
  }
105476
106355
  async function publishToTelegraph(text5, shortName, authorName) {
105477
- const accountPath = join67(STATE_DIR, "telegraph-account.json");
106356
+ const accountPath = join68(STATE_DIR, "telegraph-account.json");
105478
106357
  let account = null;
105479
106358
  try {
105480
106359
  if (existsSync57(accountPath)) {
105481
- const raw = readFileSync59(accountPath, "utf-8");
106360
+ const raw = readFileSync60(accountPath, "utf-8");
105482
106361
  const parsed = JSON.parse(raw);
105483
106362
  if (parsed.shortName && parsed.accessToken) {
105484
106363
  account = parsed;
@@ -105497,8 +106376,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
105497
106376
  }
105498
106377
  account = created.value;
105499
106378
  try {
105500
- mkdirSync50(STATE_DIR, { recursive: true, mode: 448 });
105501
- writeFileSync49(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
106379
+ mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
106380
+ writeFileSync50(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
105502
106381
  } catch (err) {
105503
106382
  process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
105504
106383
  `);
@@ -105614,7 +106493,7 @@ _The secret was NOT saved. The agent can re-request with \`request_secret\`._`,
105614
106493
  }
105615
106494
  function readLiveSwitchroomConfigText() {
105616
106495
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
105617
- return readFileSync59(cfgPath, "utf8");
106496
+ return readFileSync60(cfgPath, "utf8");
105618
106497
  }
105619
106498
  async function executeReact(args) {
105620
106499
  if (!args.chat_id)
@@ -105653,9 +106532,9 @@ async function executeDownloadAttachment(args) {
105653
106532
  fileUniqueId: file.file_unique_id,
105654
106533
  now: Date.now()
105655
106534
  });
105656
- mkdirSync50(INBOX_DIR, { recursive: true, mode: 448 });
106535
+ mkdirSync51(INBOX_DIR, { recursive: true, mode: 448 });
105657
106536
  assertInsideInbox2(INBOX_DIR, dlPath);
105658
- writeFileSync49(dlPath, buf, { mode: 384 });
106537
+ writeFileSync50(dlPath, buf, { mode: 384 });
105659
106538
  return { content: [{ type: "text", text: dlPath }] };
105660
106539
  }
105661
106540
  async function executeEditMessage(args) {
@@ -106299,7 +107178,7 @@ function gate(ctx) {
106299
107178
  }
106300
107179
  if (Object.keys(access.pending).length >= 3)
106301
107180
  return { action: "drop", reason: "pairing_capacity" };
106302
- const code2 = randomBytes12(3).toString("hex");
107181
+ const code2 = randomBytes13(3).toString("hex");
106303
107182
  const now = Date.now();
106304
107183
  access.pending[code2] = {
106305
107184
  senderId,
@@ -106539,8 +107418,13 @@ async function handleInbound(ctx, text5, downloadImage, attachment, extraAttachm
106539
107418
  maybePostMissedApprovalDigest("operator inbound");
106540
107419
  {
106541
107420
  const inboundChatId = ctx.chat?.id;
106542
- if (inboundChatId != null)
106543
- dmPinSweeper.sweep(String(inboundChatId));
107421
+ if (inboundChatId != null) {
107422
+ stalePinSweeper.sweepTarget({
107423
+ chatId: String(inboundChatId),
107424
+ threadId: ctx.message?.message_thread_id,
107425
+ isForum: ctx.chat?.is_forum === true
107426
+ });
107427
+ }
106544
107428
  }
106545
107429
  const inboundReceivedAt = Date.now();
106546
107430
  const machineInTurnAtReceipt = isMachineInTurn();
@@ -107023,14 +107907,14 @@ function restartMarkerPath() {
107023
107907
  const agentDir = resolveAgentDirFromEnv();
107024
107908
  if (!agentDir)
107025
107909
  return null;
107026
- return join67(agentDir, "restart-pending.json");
107910
+ return join68(agentDir, "restart-pending.json");
107027
107911
  }
107028
107912
  function writeRestartMarker(marker) {
107029
107913
  const p = restartMarkerPath();
107030
107914
  if (!p)
107031
107915
  return;
107032
107916
  try {
107033
- writeFileSync49(p, JSON.stringify(marker));
107917
+ writeFileSync50(p, JSON.stringify(marker));
107034
107918
  lastPlannedRestartAt = Date.now();
107035
107919
  process.stderr.write(`telegram gateway: restart-marker: write chat_id=${marker.chat_id} thread_id=${marker.thread_id ?? "-"} ack=${marker.ack_message_id ?? "-"} path=${p}
107036
107920
  `);
@@ -107049,7 +107933,7 @@ function readRestartMarker() {
107049
107933
  if (!p)
107050
107934
  return null;
107051
107935
  try {
107052
- return JSON.parse(readFileSync59(p, "utf8"));
107936
+ return JSON.parse(readFileSync60(p, "utf8"));
107053
107937
  } catch {
107054
107938
  return null;
107055
107939
  }
@@ -107059,7 +107943,7 @@ function clearRestartMarker() {
107059
107943
  if (!p)
107060
107944
  return;
107061
107945
  try {
107062
- rmSync7(p, { force: true });
107946
+ rmSync8(p, { force: true });
107063
107947
  process.stderr.write(`telegram gateway: restart-marker: cleared path=${p}
107064
107948
  `);
107065
107949
  } catch {}
@@ -107215,12 +108099,12 @@ function _resetDockerReachableCache() {
107215
108099
  }
107216
108100
  function spawnSwitchroomDetached(args, onFailure) {
107217
108101
  const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
107218
- const logPath = join67(STATE_DIR, "detached-spawn.log");
108102
+ const logPath = join68(STATE_DIR, "detached-spawn.log");
107219
108103
  let outFd = null;
107220
108104
  try {
107221
- mkdirSync50(STATE_DIR, { recursive: true });
107222
- outFd = openSync13(logPath, "a");
107223
- writeFileSync49(logPath, `
108105
+ mkdirSync51(STATE_DIR, { recursive: true });
108106
+ outFd = openSync15(logPath, "a");
108107
+ writeFileSync50(logPath, `
107224
108108
  [${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
107225
108109
  `, { flag: "a" });
107226
108110
  } catch {}
@@ -107234,7 +108118,7 @@ function spawnSwitchroomDetached(args, onFailure) {
107234
108118
  });
107235
108119
  if (outFd != null) {
107236
108120
  try {
107237
- closeSync13(outFd);
108121
+ closeSync15(outFd);
107238
108122
  } catch {}
107239
108123
  }
107240
108124
  if (onFailure) {
@@ -107246,7 +108130,7 @@ function spawnSwitchroomDetached(args, onFailure) {
107246
108130
  return;
107247
108131
  let tail = "";
107248
108132
  try {
107249
- const full = readFileSync59(logPath, "utf8");
108133
+ const full = readFileSync60(logPath, "utf8");
107250
108134
  tail = full.split(`
107251
108135
  `).slice(-30).join(`
107252
108136
  `).trim();
@@ -107508,10 +108392,10 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
107508
108392
  }
107509
108393
  function readRecentDenialsForAgent(agentName3, windowMs, limit) {
107510
108394
  try {
107511
- const auditPath = join67(homedir19(), ".switchroom", "vault-audit.log");
108395
+ const auditPath = join68(homedir19(), ".switchroom", "vault-audit.log");
107512
108396
  if (!existsSync57(auditPath))
107513
108397
  return [];
107514
- const raw = readFileSync59(auditPath, "utf8");
108398
+ const raw = readFileSync60(auditPath, "utf8");
107515
108399
  return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
107516
108400
  } catch {
107517
108401
  return [];
@@ -107562,7 +108446,7 @@ async function buildAgentMetadata(agentName3) {
107562
108446
  try {
107563
108447
  const agentDir = resolveAgentDirFromEnv();
107564
108448
  if (agentDir) {
107565
- const raw = readFileSync59(join67(agentDir, ".claude", ".claude.json"), "utf8");
108449
+ const raw = readFileSync60(join68(agentDir, ".claude", ".claude.json"), "utf8");
107566
108450
  claudeJson = JSON.parse(raw);
107567
108451
  }
107568
108452
  } catch {}
@@ -107691,7 +108575,7 @@ function buildModelDeps(restartCtx) {
107691
108575
  try {
107692
108576
  const agentDir = resolveAgentDirFromEnv();
107693
108577
  if (agentDir) {
107694
- const local = await fetchQuota2({ claudeConfigDir: join67(agentDir, ".claude") });
108578
+ const local = await fetchQuota2({ claudeConfigDir: join68(agentDir, ".claude") });
107695
108579
  if (local.ok)
107696
108580
  return formatQuotaLine2(local.data);
107697
108581
  }
@@ -107937,10 +108821,10 @@ function effortMenuReplyMarkup(reply) {
107937
108821
  function flushAgentHandoff(agentDir) {
107938
108822
  let removed = 0;
107939
108823
  for (const fname of [".handoff.md", ".handoff-topic"]) {
107940
- const p = join67(agentDir, fname);
108824
+ const p = join68(agentDir, fname);
107941
108825
  try {
107942
108826
  if (existsSync57(p)) {
107943
- unlinkSync29(p);
108827
+ unlinkSync30(p);
107944
108828
  removed++;
107945
108829
  }
107946
108830
  } catch (err) {
@@ -107995,7 +108879,7 @@ async function handleNewCommand(ctx) {
107995
108879
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
107996
108880
  if (agentDir != null) {
107997
108881
  try {
107998
- writeFileSync49(join67(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
108882
+ writeFileSync50(join68(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
107999
108883
  `, "utf8");
108000
108884
  } catch (err) {
108001
108885
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -108108,11 +108992,11 @@ function buildFolderPickerDeps() {
108108
108992
  };
108109
108993
  }
108110
108994
  var lockoutOps = {
108111
- readFileSync: (p, enc) => readFileSync59(p, enc),
108112
- writeFileSync: (p, data, opts) => writeFileSync49(p, data, opts),
108995
+ readFileSync: (p, enc) => readFileSync60(p, enc),
108996
+ writeFileSync: (p, data, opts) => writeFileSync50(p, data, opts),
108113
108997
  existsSync: (p) => existsSync57(p),
108114
- mkdirSync: (p, opts) => mkdirSync50(p, opts),
108115
- joinPath: (...parts) => join67(...parts)
108998
+ mkdirSync: (p, opts) => mkdirSync51(p, opts),
108999
+ joinPath: (...parts) => join68(...parts)
108116
109000
  };
108117
109001
  var FLEET_FALLBACK_DEDUP_MS = 30000;
108118
109002
  function isAuthBrokerSocketReachable() {
@@ -108372,7 +109256,7 @@ async function runCreditWatch() {
108372
109256
  if (!agentDir)
108373
109257
  return;
108374
109258
  const agentName3 = getMyAgentName();
108375
- const claudeConfigDir = join67(agentDir, ".claude");
109259
+ const claudeConfigDir = join68(agentDir, ".claude");
108376
109260
  const stateDir = STATE_DIR;
108377
109261
  const reason = readClaudeJsonOverage(claudeConfigDir);
108378
109262
  const prev = loadCreditState(stateDir);
@@ -110128,7 +111012,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
110128
111012
  await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
110129
111013
  return;
110130
111014
  }
110131
- const result = await fetchQuota2({ claudeConfigDir: join67(agentDir, ".claude") });
111015
+ const result = await fetchQuota2({ claudeConfigDir: join68(agentDir, ".claude") });
110132
111016
  if (!result.ok) {
110133
111017
  await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
110134
111018
  return;
@@ -110515,7 +111399,7 @@ ${interimLabel}` : interimLabel
110515
111399
  const unifiedDiff = (() => {
110516
111400
  try {
110517
111401
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
110518
- const raw = readFileSync59(cfgPath, "utf8");
111402
+ const raw = readFileSync60(cfgPath, "utf8");
110519
111403
  return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
110520
111404
  } catch (err) {
110521
111405
  process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
@@ -111384,7 +112268,7 @@ async function startGateway() {
111384
112268
  return;
111385
112269
  }
111386
112270
  })();
111387
- const resolvedAgentDirForBootCard = agentDir ?? join67(homedir19(), ".switchroom", "agents", agentSlug);
112271
+ const resolvedAgentDirForBootCard = agentDir ?? join68(homedir19(), ".switchroom", "agents", agentSlug);
111388
112272
  const handle = await startBootCard(chatId, threadId, botApiForCard, {
111389
112273
  agentName: agentDisplayName,
111390
112274
  agentSlug,
@@ -111398,8 +112282,8 @@ async function startGateway() {
111398
112282
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
111399
112283
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
111400
112284
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
111401
- configSnapshotPath: join67(resolvedAgentDirForBootCard, ".config-snapshot.json"),
111402
- bootCardStatePath: join67(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
112285
+ configSnapshotPath: join68(resolvedAgentDirForBootCard, ".config-snapshot.json"),
112286
+ bootCardStatePath: join68(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
111403
112287
  floodStatePath: FLOOD_STATE_PATH,
111404
112288
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
111405
112289
  }, ackMsgId);
@@ -111430,10 +112314,10 @@ async function startGateway() {
111430
112314
  try {
111431
112315
  const smAgentDir = resolveAgentDirFromEnv();
111432
112316
  if (smAgentDir) {
111433
- const activePath = join67(smAgentDir, ".active-session-model");
112317
+ const activePath = join68(smAgentDir, ".active-session-model");
111434
112318
  if (existsSync57(activePath)) {
111435
112319
  try {
111436
- const launched = readFileSync59(activePath, "utf8").trim();
112320
+ const launched = readFileSync60(activePath, "utf8").trim();
111437
112321
  const configured = (() => {
111438
112322
  const d = switchroomExecJson(["agent", "list"]);
111439
112323
  const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
@@ -111466,29 +112350,29 @@ async function startGateway() {
111466
112350
  deliverModelSwitchBootNotice({
111467
112351
  ...modelBootCardDeps,
111468
112352
  confirmation,
111469
- hasSessionModelAlert: existsSync57(join67(smAgentDir, ".session-model-alert"))
112353
+ hasSessionModelAlert: existsSync57(join68(smAgentDir, ".session-model-alert"))
111470
112354
  });
111471
112355
  }
111472
112356
  } catch {}
111473
112357
  }
111474
- const activeEffortPath = join67(smAgentDir, ".active-session-effort");
112358
+ const activeEffortPath = join68(smAgentDir, ".active-session-effort");
111475
112359
  if (existsSync57(activeEffortPath)) {
111476
112360
  try {
111477
- const launchedEffort = readFileSync59(activeEffortPath, "utf8").trim();
112361
+ const launchedEffort = readFileSync60(activeEffortPath, "utf8").trim();
111478
112362
  const configuredEffort = getConfiguredEffortForPersist();
111479
112363
  sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
111480
112364
  } catch {}
111481
112365
  }
111482
- const alertPath = join67(smAgentDir, ".session-model-alert");
112366
+ const alertPath = join68(smAgentDir, ".session-model-alert");
111483
112367
  if (existsSync57(alertPath)) {
111484
112368
  let alertText = null;
111485
112369
  try {
111486
- alertText = readFileSync59(alertPath, "utf8").trim();
112370
+ alertText = readFileSync60(alertPath, "utf8").trim();
111487
112371
  } catch {
111488
112372
  alertText = null;
111489
112373
  }
111490
112374
  try {
111491
- unlinkSync29(alertPath);
112375
+ unlinkSync30(alertPath);
111492
112376
  } catch {}
111493
112377
  if (alertText && alertText.length > 0) {
111494
112378
  const operators = loadAccess().allowFrom;
@@ -111609,12 +112493,12 @@ async function startGateway() {
111609
112493
  staleWorkerTtlMs: resolveInflightTerminalCapMs() + WORKER_FEED_STALE_TTL_MARGIN_MS,
111610
112494
  absoluteRowLifetimeCapMs: resolveInflightTerminalCapMs() * WORKER_FEED_ABSOLUTE_ROW_LIFETIME_CAP_MULTIPLE,
111611
112495
  groupMessageLifetimeCapMs: WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS,
111612
- reconcilePin: ({ feedKey, chatId, messageId }) => {
112496
+ reconcilePin: ({ feedKey, chatId, threadId, messageId }) => {
111613
112497
  if (!PIN_STATUS_WHILE_WORKING)
111614
112498
  return;
111615
112499
  const key = `wk:group:${feedKey}`;
111616
112500
  if (messageId != null) {
111617
- reconcileStatusPin(key, chatId, { pinned: true, messageId });
112501
+ reconcileStatusPin(key, chatId, { pinned: true, messageId }, threadId);
111618
112502
  } else {
111619
112503
  const unpinChat = chatId || statusPinClaims.get(key)?.chatId;
111620
112504
  if (unpinChat != null && unpinChat.length > 0) {
@@ -111667,7 +112551,7 @@ async function startGateway() {
111667
112551
  `);
111668
112552
  }
111669
112553
  },
111670
- onFinish: ({ agentId, outcome, description, resultText, toolCount, totalTokens, durationMs, background: entryBackground }) => {
112554
+ onFinish: ({ agentId, outcome, description: description2, resultText, toolCount, totalTokens, durationMs, background: entryBackground }) => {
111671
112555
  workerFeedOriginDeferrals.delete(agentId);
111672
112556
  deferredDoneReactions.promote();
111673
112557
  let fleetChatId = "";
@@ -111680,10 +112564,10 @@ async function startGateway() {
111680
112564
  }
111681
112565
  }
111682
112566
  } catch {}
111683
- let dispatch = resolveWorkerFeedDispatch(null, description, entryBackground);
112567
+ let dispatch = resolveWorkerFeedDispatch(null, description2, entryBackground);
111684
112568
  if (turnsDb != null) {
111685
112569
  try {
111686
- dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description, entryBackground);
112570
+ dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description2, entryBackground);
111687
112571
  } catch {}
111688
112572
  }
111689
112573
  let isBackground = dispatch.isBackground;
@@ -111773,7 +112657,7 @@ async function startGateway() {
111773
112657
  fleetChatId: handbackOrigin?.chatId || fleetChatId,
111774
112658
  ...handbackOrigin?.threadId != null ? { originThreadId: handbackOrigin.threadId } : {},
111775
112659
  ownerChatId: loadAccess().allowFrom[0] ?? "",
111776
- taskDescription: description,
112660
+ taskDescription: description2,
111777
112661
  resultText,
111778
112662
  jsonlAgentId: agentId
111779
112663
  });
@@ -111799,7 +112683,7 @@ async function startGateway() {
111799
112683
  process.stderr.write(`telegram gateway: subagent-handback queued agent=${agentId} outcome=${outcome} chat=${decision.chatId} resultChars=${resultText.length}
111800
112684
  `);
111801
112685
  },
111802
- onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount, totalTokens, progressLine, model, skeleton }) => {
112686
+ onProgress: ({ agentId, description: description2, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount, totalTokens, progressLine, model, skeleton }) => {
111803
112687
  let fleetChatId = "";
111804
112688
  try {
111805
112689
  const fleets = progressDriver?.peekAllFleets() ?? [];
@@ -111810,10 +112694,10 @@ async function startGateway() {
111810
112694
  }
111811
112695
  }
111812
112696
  } catch {}
111813
- let dispatch = resolveWorkerFeedDispatch(null, description);
112697
+ let dispatch = resolveWorkerFeedDispatch(null, description2);
111814
112698
  if (turnsDb != null) {
111815
112699
  try {
111816
- dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description);
112700
+ dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description2);
111817
112701
  } catch {}
111818
112702
  }
111819
112703
  const isBackground = dispatch.isBackground || dispatch.isNested;
@@ -111931,7 +112815,7 @@ async function startGateway() {
111931
112815
  ...progressOrigin?.threadId != null ? { originThreadId: progressOrigin.threadId } : {},
111932
112816
  ownerChatId: loadAccess().allowFrom[0] ?? "",
111933
112817
  subagentJsonlId: agentId,
111934
- taskDescription: description,
112818
+ taskDescription: description2,
111935
112819
  latestSummary,
111936
112820
  elapsedMs,
111937
112821
  progressIntervalMs: DEFAULT_PROGRESS_INTERVAL_MS,